summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2012-04-01 00:00:00 -0700
committerThe Android Open Source Project <initial-contribution@android.com>2012-06-21 11:19:58 -0700
commitb178bea7d8af0d7e65faad94586356d52ef3e409 (patch)
tree7259e615d1996e190ad080611feb2fd2bd93354c
downloaddalvik-snapshot-gingerbread.tar.gz
Snapshot d0d351b2ef726ff1b76a3efb4aad91e4d1436f6agingerbread
-rw-r--r--Android.mk65
-rw-r--r--CleanSpec.mk54
-rw-r--r--MODULE_LICENSE_APACHE20
-rw-r--r--NOTICE190
-rw-r--r--README.txt60
-rw-r--r--dalvikvm/Android.mk75
-rw-r--r--dalvikvm/Main.c301
-rw-r--r--dexdump/Android.mk74
-rw-r--r--dexdump/DexDump.c1891
-rw-r--r--dexlist/Android.mk54
-rw-r--r--dexlist/DexList.c293
-rw-r--r--dexopt/Android.mk61
-rw-r--r--dexopt/OptMain.c593
-rw-r--r--docs/dalvik-bytecode.css165
-rw-r--r--docs/dalvik-bytecode.html1503
-rw-r--r--docs/dalvik-constraints.css59
-rw-r--r--docs/dalvik-constraints.html897
-rw-r--r--docs/debugger.html243
-rw-r--r--docs/debugmon.html736
-rw-r--r--docs/dex-format.css387
-rw-r--r--docs/dex-format.html3043
-rw-r--r--docs/dexopt.html326
-rw-r--r--docs/embedded-vm-control.html310
-rw-r--r--docs/heap-profiling.html199
-rw-r--r--docs/hello-world.html216
-rw-r--r--docs/instruction-formats.css129
-rw-r--r--docs/instruction-formats.html430
-rw-r--r--docs/java-bytecode.css54
-rw-r--r--docs/java-bytecode.html228
-rw-r--r--docs/java-constraints.css59
-rw-r--r--docs/java-constraints.html1080
-rw-r--r--docs/jni-tips.html770
-rw-r--r--docs/libraries.html165
-rw-r--r--docs/opcodes/opcode-00-nop.html59
-rw-r--r--docs/opcodes/opcode-01-move.html92
-rw-r--r--docs/opcodes/opcode-04-move-wide.html106
-rw-r--r--docs/opcodes/opcode-07-move-object.html90
-rw-r--r--docs/opcodes/opcode-0a-move-result.html96
-rw-r--r--docs/opcodes/opcode-0b-move-result-wide.html101
-rw-r--r--docs/opcodes/opcode-0c-move-result-object.html97
-rw-r--r--docs/opcodes/opcode-0d-move-exception.html79
-rw-r--r--docs/opcodes/opcode-0e-return-void.html84
-rw-r--r--docs/opcodes/opcode-0f-return.html100
-rw-r--r--docs/opcodes/opcode-10-return-wide.html99
-rw-r--r--docs/opcodes/opcode-11-return-object.html96
-rw-r--r--docs/opcodes/opcode-12-const.html102
-rw-r--r--docs/opcodes/opcode-16-const-wide.html110
-rw-r--r--docs/opcodes/opcode-1a-const-string.html85
-rw-r--r--docs/opcodes/opcode-1b-const-class.html92
-rw-r--r--docs/opcodes/opcode-1d-monitor-enter.html93
-rw-r--r--docs/opcodes/opcode-1e-monitor-exit.html101
-rw-r--r--docs/opcodes/opcode-1f-check-cast.html95
-rw-r--r--docs/opcodes/opcode-20-instance-of.html100
-rw-r--r--docs/opcodes/opcode-21-array-length.html78
-rw-r--r--docs/opcodes/opcode-22-new-instance.html95
-rw-r--r--docs/opcodes/opcode-23-new-array.html108
-rw-r--r--docs/opcodes/opcode-24-filled-new-array.html144
-rw-r--r--docs/opcodes/opcode-25-filled-new-array-range.html125
-rw-r--r--docs/opcodes/opcode-26-fill-array-data.html96
-rw-r--r--docs/opcodes/opcode-27-throw.html80
-rw-r--r--docs/opcodes/opcode-28-goto.html73
-rw-r--r--docs/opcodes/opcode-29-goto-16.html73
-rw-r--r--docs/opcodes/opcode-2a-goto-32.html66
-rw-r--r--docs/opcodes/opcode-2b-packed-switch.html103
-rw-r--r--docs/opcodes/opcode-2c-sparse-switch.html110
-rw-r--r--docs/opcodes/opcode-2d-cmp-kind.html121
-rw-r--r--docs/opcodes/opcode-32-if-test.html101
-rw-r--r--docs/opcodes/opcode-38-if-testz.html100
-rw-r--r--docs/opcodes/opcode-44-aget.html113
-rw-r--r--docs/opcodes/opcode-4b-aput.html98
-rw-r--r--docs/opcodes/opcode-52-iget.html109
-rw-r--r--docs/opcodes/opcode-59-iput.html112
-rw-r--r--docs/opcodes/opcode-60-sget.html106
-rw-r--r--docs/opcodes/opcode-67-sput.html108
-rw-r--r--docs/opcodes/opcode-7b-unop.html108
-rw-r--r--docs/opcodes/opcode-90-binop.html120
-rw-r--r--docs/opcodes/opcode-b0-binop-2addr.html120
-rw-r--r--docs/opcodes/opcode-d0-binop-lit16.html94
-rw-r--r--docs/opcodes/opcode-d8-binop-lit8.html97
-rw-r--r--docs/opcodes/opcode.css166
-rw-r--r--docs/porting-guide.html356
-rw-r--r--docs/porting-proto.c.txt244
-rw-r--r--docs/prettify.css27
-rw-r--r--docs/prettify.js1280
-rw-r--r--docs/verifier.html179
-rw-r--r--dvz/Android.mk19
-rw-r--r--dvz/dvz.c109
-rw-r--r--dx/.classpath7
-rw-r--r--dx/.project17
-rw-r--r--dx/Android.mk73
-rw-r--r--dx/README.txt3
-rw-r--r--dx/etc/bytecode.txt278
-rw-r--r--dx/etc/dx89
-rwxr-xr-xdx/etc/dx.bat85
-rw-r--r--dx/etc/jasmin39
-rw-r--r--dx/etc/jasmin.jarbin0 -> 128775 bytes
-rw-r--r--dx/etc/manifest.txt1
-rwxr-xr-xdx/etc/opcode-gen141
-rwxr-xr-xdx/etc/run-opcode-gen20
-rw-r--r--dx/src/Android.mk34
-rw-r--r--dx/src/com/android/dx/Version.java25
-rw-r--r--dx/src/com/android/dx/cf/attrib/AttAnnotationDefault.java67
-rw-r--r--dx/src/com/android/dx/cf/attrib/AttCode.java145
-rw-r--r--dx/src/com/android/dx/cf/attrib/AttConstantValue.java77
-rw-r--r--dx/src/com/android/dx/cf/attrib/AttDeprecated.java37
-rw-r--r--dx/src/com/android/dx/cf/attrib/AttEnclosingMethod.java78
-rw-r--r--dx/src/com/android/dx/cf/attrib/AttExceptions.java68
-rw-r--r--dx/src/com/android/dx/cf/attrib/AttInnerClasses.java64
-rw-r--r--dx/src/com/android/dx/cf/attrib/AttLineNumberTable.java65
-rw-r--r--dx/src/com/android/dx/cf/attrib/AttLocalVariableTable.java36
-rw-r--r--dx/src/com/android/dx/cf/attrib/AttLocalVariableTypeTable.java36
-rw-r--r--dx/src/com/android/dx/cf/attrib/AttRuntimeInvisibleAnnotations.java40
-rw-r--r--dx/src/com/android/dx/cf/attrib/AttRuntimeInvisibleParameterAnnotations.java42
-rw-r--r--dx/src/com/android/dx/cf/attrib/AttRuntimeVisibleAnnotations.java40
-rw-r--r--dx/src/com/android/dx/cf/attrib/AttRuntimeVisibleParameterAnnotations.java42
-rw-r--r--dx/src/com/android/dx/cf/attrib/AttSignature.java59
-rw-r--r--dx/src/com/android/dx/cf/attrib/AttSourceFile.java59
-rw-r--r--dx/src/com/android/dx/cf/attrib/AttSynthetic.java37
-rw-r--r--dx/src/com/android/dx/cf/attrib/BaseAnnotations.java72
-rw-r--r--dx/src/com/android/dx/cf/attrib/BaseAttribute.java46
-rw-r--r--dx/src/com/android/dx/cf/attrib/BaseLocalVariables.java65
-rw-r--r--dx/src/com/android/dx/cf/attrib/BaseParameterAnnotations.java73
-rw-r--r--dx/src/com/android/dx/cf/attrib/InnerClassList.java137
-rw-r--r--dx/src/com/android/dx/cf/attrib/RawAttribute.java91
-rw-r--r--dx/src/com/android/dx/cf/attrib/package.html11
-rw-r--r--dx/src/com/android/dx/cf/code/BaseMachine.java545
-rw-r--r--dx/src/com/android/dx/cf/code/BasicBlocker.java452
-rw-r--r--dx/src/com/android/dx/cf/code/ByteBlock.java145
-rw-r--r--dx/src/com/android/dx/cf/code/ByteBlockList.java75
-rw-r--r--dx/src/com/android/dx/cf/code/ByteCatchList.java317
-rw-r--r--dx/src/com/android/dx/cf/code/ByteOps.java649
-rw-r--r--dx/src/com/android/dx/cf/code/BytecodeArray.java1393
-rw-r--r--dx/src/com/android/dx/cf/code/ConcreteMethod.java254
-rw-r--r--dx/src/com/android/dx/cf/code/ExecutionStack.java305
-rw-r--r--dx/src/com/android/dx/cf/code/Frame.java415
-rw-r--r--dx/src/com/android/dx/cf/code/LineNumberList.java184
-rw-r--r--dx/src/com/android/dx/cf/code/LocalVariableList.java373
-rw-r--r--dx/src/com/android/dx/cf/code/LocalsArray.java182
-rw-r--r--dx/src/com/android/dx/cf/code/LocalsArraySet.java462
-rw-r--r--dx/src/com/android/dx/cf/code/Machine.java208
-rw-r--r--dx/src/com/android/dx/cf/code/Merger.java305
-rw-r--r--dx/src/com/android/dx/cf/code/OneLocalsArray.java246
-rw-r--r--dx/src/com/android/dx/cf/code/ReturnAddress.java108
-rw-r--r--dx/src/com/android/dx/cf/code/Ropper.java1675
-rw-r--r--dx/src/com/android/dx/cf/code/RopperMachine.java941
-rw-r--r--dx/src/com/android/dx/cf/code/SimException.java37
-rw-r--r--dx/src/com/android/dx/cf/code/Simulator.java720
-rw-r--r--dx/src/com/android/dx/cf/code/SwitchList.java193
-rw-r--r--dx/src/com/android/dx/cf/code/ValueAwareMachine.java199
-rw-r--r--dx/src/com/android/dx/cf/code/package.html10
-rw-r--r--dx/src/com/android/dx/cf/cst/ConstantPoolParser.java335
-rw-r--r--dx/src/com/android/dx/cf/cst/ConstantTags.java55
-rw-r--r--dx/src/com/android/dx/cf/direct/AnnotationParser.java474
-rw-r--r--dx/src/com/android/dx/cf/direct/AttributeFactory.java134
-rw-r--r--dx/src/com/android/dx/cf/direct/AttributeListParser.java164
-rw-r--r--dx/src/com/android/dx/cf/direct/ClassPathOpener.java250
-rw-r--r--dx/src/com/android/dx/cf/direct/CodeObserver.java306
-rw-r--r--dx/src/com/android/dx/cf/direct/DirectClassFile.java633
-rw-r--r--dx/src/com/android/dx/cf/direct/FieldListParser.java86
-rw-r--r--dx/src/com/android/dx/cf/direct/MemberListParser.java240
-rw-r--r--dx/src/com/android/dx/cf/direct/MethodListParser.java86
-rw-r--r--dx/src/com/android/dx/cf/direct/StdAttributeFactory.java764
-rw-r--r--dx/src/com/android/dx/cf/direct/package.html12
-rw-r--r--dx/src/com/android/dx/cf/iface/Attribute.java38
-rw-r--r--dx/src/com/android/dx/cf/iface/AttributeList.java75
-rw-r--r--dx/src/com/android/dx/cf/iface/ClassFile.java123
-rw-r--r--dx/src/com/android/dx/cf/iface/Field.java35
-rw-r--r--dx/src/com/android/dx/cf/iface/FieldList.java48
-rw-r--r--dx/src/com/android/dx/cf/iface/Member.java74
-rw-r--r--dx/src/com/android/dx/cf/iface/Method.java34
-rw-r--r--dx/src/com/android/dx/cf/iface/MethodList.java47
-rw-r--r--dx/src/com/android/dx/cf/iface/ParseException.java37
-rw-r--r--dx/src/com/android/dx/cf/iface/ParseObserver.java68
-rw-r--r--dx/src/com/android/dx/cf/iface/StdAttributeList.java104
-rw-r--r--dx/src/com/android/dx/cf/iface/StdField.java54
-rw-r--r--dx/src/com/android/dx/cf/iface/StdFieldList.java49
-rw-r--r--dx/src/com/android/dx/cf/iface/StdMember.java110
-rw-r--r--dx/src/com/android/dx/cf/iface/StdMethod.java55
-rw-r--r--dx/src/com/android/dx/cf/iface/StdMethodList.java49
-rw-r--r--dx/src/com/android/dx/cf/iface/package.html10
-rw-r--r--dx/src/com/android/dx/command/DxConsole.java37
-rw-r--r--dx/src/com/android/dx/command/Main.java172
-rw-r--r--dx/src/com/android/dx/command/UsageException.java25
-rw-r--r--dx/src/com/android/dx/command/annotool/AnnotationLister.java283
-rw-r--r--dx/src/com/android/dx/command/annotool/Main.java160
-rw-r--r--dx/src/com/android/dx/command/dexer/Main.java930
-rw-r--r--dx/src/com/android/dx/command/dump/Args.java56
-rw-r--r--dx/src/com/android/dx/command/dump/BaseDumper.java299
-rw-r--r--dx/src/com/android/dx/command/dump/BlockDumper.java355
-rw-r--r--dx/src/com/android/dx/command/dump/ClassDumper.java74
-rw-r--r--dx/src/com/android/dx/command/dump/DotDumper.java167
-rw-r--r--dx/src/com/android/dx/command/dump/Main.java131
-rw-r--r--dx/src/com/android/dx/command/dump/SsaDumper.java182
-rw-r--r--dx/src/com/android/dx/dex/cf/AttributeTranslator.java428
-rw-r--r--dx/src/com/android/dx/dex/cf/CfOptions.java50
-rw-r--r--dx/src/com/android/dx/dex/cf/CfTranslator.java383
-rw-r--r--dx/src/com/android/dx/dex/cf/CodeStatistics.java172
-rw-r--r--dx/src/com/android/dx/dex/cf/OptimizerOptions.java185
-rw-r--r--dx/src/com/android/dx/dex/cf/package.html15
-rw-r--r--dx/src/com/android/dx/dex/code/ArrayData.java198
-rw-r--r--dx/src/com/android/dx/dex/code/BlockAddresses.java143
-rw-r--r--dx/src/com/android/dx/dex/code/CatchBuilder.java48
-rw-r--r--dx/src/com/android/dx/dex/code/CatchHandlerList.java238
-rw-r--r--dx/src/com/android/dx/dex/code/CatchTable.java192
-rw-r--r--dx/src/com/android/dx/dex/code/CodeAddress.java57
-rw-r--r--dx/src/com/android/dx/dex/code/CstInsn.java205
-rw-r--r--dx/src/com/android/dx/dex/code/DalvCode.java232
-rw-r--r--dx/src/com/android/dx/dex/code/DalvInsn.java422
-rw-r--r--dx/src/com/android/dx/dex/code/DalvInsnList.java268
-rw-r--r--dx/src/com/android/dx/dex/code/DalvOps.java298
-rw-r--r--dx/src/com/android/dx/dex/code/Dop.java150
-rw-r--r--dx/src/com/android/dx/dex/code/Dops.java1231
-rw-r--r--dx/src/com/android/dx/dex/code/FixedSizeInsn.java73
-rw-r--r--dx/src/com/android/dx/dex/code/HighRegisterPrefix.java147
-rw-r--r--dx/src/com/android/dx/dex/code/InsnFormat.java578
-rw-r--r--dx/src/com/android/dx/dex/code/LocalEnd.java90
-rw-r--r--dx/src/com/android/dx/dex/code/LocalList.java948
-rw-r--r--dx/src/com/android/dx/dex/code/LocalSnapshot.java96
-rw-r--r--dx/src/com/android/dx/dex/code/LocalStart.java98
-rw-r--r--dx/src/com/android/dx/dex/code/OddSpacer.java75
-rw-r--r--dx/src/com/android/dx/dex/code/OutputCollector.java118
-rw-r--r--dx/src/com/android/dx/dex/code/OutputFinisher.java737
-rw-r--r--dx/src/com/android/dx/dex/code/PositionList.java192
-rw-r--r--dx/src/com/android/dx/dex/code/RopToDop.java415
-rw-r--r--dx/src/com/android/dx/dex/code/RopTranslator.java872
-rw-r--r--dx/src/com/android/dx/dex/code/SimpleInsn.java59
-rw-r--r--dx/src/com/android/dx/dex/code/StdCatchBuilder.java316
-rw-r--r--dx/src/com/android/dx/dex/code/SwitchData.java257
-rw-r--r--dx/src/com/android/dx/dex/code/TargetInsn.java132
-rw-r--r--dx/src/com/android/dx/dex/code/VariableSizeInsn.java49
-rw-r--r--dx/src/com/android/dx/dex/code/ZeroSizeInsn.java62
-rw-r--r--dx/src/com/android/dx/dex/code/form/Form10t.java92
-rw-r--r--dx/src/com/android/dx/dex/code/form/Form10x.java78
-rw-r--r--dx/src/com/android/dx/dex/code/form/Form11n.java104
-rw-r--r--dx/src/com/android/dx/dex/code/form/Form11x.java82
-rw-r--r--dx/src/com/android/dx/dex/code/form/Form12x.java132
-rw-r--r--dx/src/com/android/dx/dex/code/form/Form20t.java92
-rw-r--r--dx/src/com/android/dx/dex/code/form/Form21c.java133
-rw-r--r--dx/src/com/android/dx/dex/code/form/Form21h.java120
-rw-r--r--dx/src/com/android/dx/dex/code/form/Form21s.java104
-rw-r--r--dx/src/com/android/dx/dex/code/form/Form21t.java100
-rw-r--r--dx/src/com/android/dx/dex/code/form/Form22b.java106
-rw-r--r--dx/src/com/android/dx/dex/code/form/Form22c.java109
-rw-r--r--dx/src/com/android/dx/dex/code/form/Form22s.java107
-rw-r--r--dx/src/com/android/dx/dex/code/form/Form22t.java103
-rw-r--r--dx/src/com/android/dx/dex/code/form/Form22x.java86
-rw-r--r--dx/src/com/android/dx/dex/code/form/Form23x.java88
-rw-r--r--dx/src/com/android/dx/dex/code/form/Form30t.java90
-rw-r--r--dx/src/com/android/dx/dex/code/form/Form31c.java129
-rw-r--r--dx/src/com/android/dx/dex/code/form/Form31i.java103
-rw-r--r--dx/src/com/android/dx/dex/code/form/Form31t.java96
-rw-r--r--dx/src/com/android/dx/dex/code/form/Form32x.java87
-rw-r--r--dx/src/com/android/dx/dex/code/form/Form35c.java193
-rw-r--r--dx/src/com/android/dx/dex/code/form/Form3rc.java175
-rw-r--r--dx/src/com/android/dx/dex/code/form/Form51l.java102
-rw-r--r--dx/src/com/android/dx/dex/code/form/SpecialFormat.java79
-rw-r--r--dx/src/com/android/dx/dex/file/AnnotationItem.java220
-rw-r--r--dx/src/com/android/dx/dex/file/AnnotationSetItem.java157
-rw-r--r--dx/src/com/android/dx/dex/file/AnnotationSetRefItem.java80
-rw-r--r--dx/src/com/android/dx/dex/file/AnnotationUtils.java254
-rw-r--r--dx/src/com/android/dx/dex/file/AnnotationsDirectoryItem.java385
-rw-r--r--dx/src/com/android/dx/dex/file/CatchStructs.java317
-rw-r--r--dx/src/com/android/dx/dex/file/ClassDataItem.java429
-rw-r--r--dx/src/com/android/dx/dex/file/ClassDefItem.java410
-rw-r--r--dx/src/com/android/dx/dex/file/ClassDefsSection.java187
-rw-r--r--dx/src/com/android/dx/dex/file/CodeItem.java334
-rw-r--r--dx/src/com/android/dx/dex/file/DebugInfoConstants.java154
-rw-r--r--dx/src/com/android/dx/dex/file/DebugInfoDecoder.java653
-rw-r--r--dx/src/com/android/dx/dex/file/DebugInfoEncoder.java920
-rw-r--r--dx/src/com/android/dx/dex/file/DebugInfoItem.java196
-rw-r--r--dx/src/com/android/dx/dex/file/DexFile.java647
-rw-r--r--dx/src/com/android/dx/dex/file/EncodedArrayItem.java131
-rw-r--r--dx/src/com/android/dx/dex/file/EncodedField.java154
-rw-r--r--dx/src/com/android/dx/dex/file/EncodedMember.java86
-rw-r--r--dx/src/com/android/dx/dex/file/EncodedMethod.java196
-rw-r--r--dx/src/com/android/dx/dex/file/FieldAnnotationStruct.java122
-rw-r--r--dx/src/com/android/dx/dex/file/FieldIdItem.java70
-rw-r--r--dx/src/com/android/dx/dex/file/FieldIdsSection.java137
-rw-r--r--dx/src/com/android/dx/dex/file/HeaderItem.java123
-rw-r--r--dx/src/com/android/dx/dex/file/HeaderSection.java63
-rw-r--r--dx/src/com/android/dx/dex/file/IdItem.java61
-rw-r--r--dx/src/com/android/dx/dex/file/IndexedItem.java81
-rw-r--r--dx/src/com/android/dx/dex/file/Item.java80
-rw-r--r--dx/src/com/android/dx/dex/file/ItemType.java97
-rw-r--r--dx/src/com/android/dx/dex/file/MapItem.java235
-rw-r--r--dx/src/com/android/dx/dex/file/MemberIdItem.java111
-rw-r--r--dx/src/com/android/dx/dex/file/MemberIdsSection.java44
-rw-r--r--dx/src/com/android/dx/dex/file/MethodAnnotationStruct.java122
-rw-r--r--dx/src/com/android/dx/dex/file/MethodIdItem.java70
-rw-r--r--dx/src/com/android/dx/dex/file/MethodIdsSection.java137
-rw-r--r--dx/src/com/android/dx/dex/file/MixedItemSection.java362
-rw-r--r--dx/src/com/android/dx/dex/file/OffsettedItem.java314
-rw-r--r--dx/src/com/android/dx/dex/file/ParameterAnnotationStruct.java161
-rw-r--r--dx/src/com/android/dx/dex/file/ProtoIdItem.java162
-rw-r--r--dx/src/com/android/dx/dex/file/ProtoIdsSection.java140
-rw-r--r--dx/src/com/android/dx/dex/file/Section.java287
-rw-r--r--dx/src/com/android/dx/dex/file/Statistics.java195
-rw-r--r--dx/src/com/android/dx/dex/file/StringDataItem.java99
-rw-r--r--dx/src/com/android/dx/dex/file/StringIdItem.java128
-rw-r--r--dx/src/com/android/dx/dex/file/StringIdsSection.java210
-rw-r--r--dx/src/com/android/dx/dex/file/TypeIdItem.java72
-rw-r--r--dx/src/com/android/dx/dex/file/TypeIdsSection.java192
-rw-r--r--dx/src/com/android/dx/dex/file/TypeListItem.java122
-rw-r--r--dx/src/com/android/dx/dex/file/UniformItemSection.java112
-rw-r--r--dx/src/com/android/dx/dex/file/UniformListItem.java216
-rw-r--r--dx/src/com/android/dx/dex/file/ValueEncoder.java529
-rw-r--r--dx/src/com/android/dx/rop/annotation/Annotation.java232
-rw-r--r--dx/src/com/android/dx/rop/annotation/AnnotationVisibility.java46
-rw-r--r--dx/src/com/android/dx/rop/annotation/Annotations.java213
-rw-r--r--dx/src/com/android/dx/rop/annotation/AnnotationsList.java91
-rw-r--r--dx/src/com/android/dx/rop/annotation/NameValuePair.java112
-rw-r--r--dx/src/com/android/dx/rop/code/AccessFlags.java374
-rw-r--r--dx/src/com/android/dx/rop/code/BasicBlock.java281
-rw-r--r--dx/src/com/android/dx/rop/code/BasicBlockList.java398
-rw-r--r--dx/src/com/android/dx/rop/code/ConservativeTranslationAdvice.java52
-rw-r--r--dx/src/com/android/dx/rop/code/CstInsn.java74
-rw-r--r--dx/src/com/android/dx/rop/code/DexTranslationAdvice.java119
-rw-r--r--dx/src/com/android/dx/rop/code/Exceptions.java133
-rw-r--r--dx/src/com/android/dx/rop/code/FillArrayDataInsn.java116
-rw-r--r--dx/src/com/android/dx/rop/code/Insn.java458
-rw-r--r--dx/src/com/android/dx/rop/code/InsnList.java130
-rw-r--r--dx/src/com/android/dx/rop/code/LocalItem.java143
-rw-r--r--dx/src/com/android/dx/rop/code/LocalVariableExtractor.java191
-rw-r--r--dx/src/com/android/dx/rop/code/LocalVariableInfo.java250
-rw-r--r--dx/src/com/android/dx/rop/code/PlainCstInsn.java87
-rw-r--r--dx/src/com/android/dx/rop/code/PlainInsn.java140
-rw-r--r--dx/src/com/android/dx/rop/code/RegOps.java399
-rw-r--r--dx/src/com/android/dx/rop/code/RegisterSpec.java650
-rw-r--r--dx/src/com/android/dx/rop/code/RegisterSpecList.java362
-rw-r--r--dx/src/com/android/dx/rop/code/RegisterSpecSet.java397
-rw-r--r--dx/src/com/android/dx/rop/code/Rop.java407
-rw-r--r--dx/src/com/android/dx/rop/code/RopMethod.java207
-rw-r--r--dx/src/com/android/dx/rop/code/Rops.java2086
-rw-r--r--dx/src/com/android/dx/rop/code/SourcePosition.java168
-rw-r--r--dx/src/com/android/dx/rop/code/SwitchInsn.java119
-rw-r--r--dx/src/com/android/dx/rop/code/ThrowingCstInsn.java105
-rw-r--r--dx/src/com/android/dx/rop/code/ThrowingInsn.java120
-rw-r--r--dx/src/com/android/dx/rop/code/TranslationAdvice.java62
-rw-r--r--dx/src/com/android/dx/rop/code/package.html8
-rw-r--r--dx/src/com/android/dx/rop/cst/Constant.java68
-rw-r--r--dx/src/com/android/dx/rop/cst/ConstantPool.java70
-rw-r--r--dx/src/com/android/dx/rop/cst/CstAnnotation.java96
-rw-r--r--dx/src/com/android/dx/rop/cst/CstArray.java164
-rw-r--r--dx/src/com/android/dx/rop/cst/CstBaseMethodRef.java151
-rw-r--r--dx/src/com/android/dx/rop/cst/CstBoolean.java99
-rw-r--r--dx/src/com/android/dx/rop/cst/CstByte.java99
-rw-r--r--dx/src/com/android/dx/rop/cst/CstChar.java99
-rw-r--r--dx/src/com/android/dx/rop/cst/CstDouble.java90
-rw-r--r--dx/src/com/android/dx/rop/cst/CstEnumRef.java68
-rw-r--r--dx/src/com/android/dx/rop/cst/CstFieldRef.java79
-rw-r--r--dx/src/com/android/dx/rop/cst/CstFloat.java91
-rw-r--r--dx/src/com/android/dx/rop/cst/CstInteger.java116
-rw-r--r--dx/src/com/android/dx/rop/cst/CstInterfaceMethodRef.java60
-rw-r--r--dx/src/com/android/dx/rop/cst/CstKnownNull.java110
-rw-r--r--dx/src/com/android/dx/rop/cst/CstLiteral32.java87
-rw-r--r--dx/src/com/android/dx/rop/cst/CstLiteral64.java87
-rw-r--r--dx/src/com/android/dx/rop/cst/CstLiteralBits.java82
-rw-r--r--dx/src/com/android/dx/rop/cst/CstLong.java87
-rw-r--r--dx/src/com/android/dx/rop/cst/CstMemberRef.java122
-rw-r--r--dx/src/com/android/dx/rop/cst/CstMethodRef.java39
-rw-r--r--dx/src/com/android/dx/rop/cst/CstNat.java170
-rw-r--r--dx/src/com/android/dx/rop/cst/CstShort.java100
-rw-r--r--dx/src/com/android/dx/rop/cst/CstString.java109
-rw-r--r--dx/src/com/android/dx/rop/cst/CstType.java230
-rw-r--r--dx/src/com/android/dx/rop/cst/CstUtf8.java371
-rw-r--r--dx/src/com/android/dx/rop/cst/StdConstantPool.java139
-rw-r--r--dx/src/com/android/dx/rop/cst/TypedConstant.java49
-rw-r--r--dx/src/com/android/dx/rop/cst/Zeroes.java55
-rw-r--r--dx/src/com/android/dx/rop/cst/package.html9
-rw-r--r--dx/src/com/android/dx/rop/package-info.java200
-rw-r--r--dx/src/com/android/dx/rop/type/Prototype.java397
-rw-r--r--dx/src/com/android/dx/rop/type/StdTypeList.java407
-rw-r--r--dx/src/com/android/dx/rop/type/Type.java855
-rw-r--r--dx/src/com/android/dx/rop/type/TypeBearer.java74
-rw-r--r--dx/src/com/android/dx/rop/type/TypeList.java69
-rw-r--r--dx/src/com/android/dx/rop/type/package.html8
-rw-r--r--dx/src/com/android/dx/ssa/BasicRegisterMapper.java129
-rw-r--r--dx/src/com/android/dx/ssa/ConstCollector.java384
-rw-r--r--dx/src/com/android/dx/ssa/DeadCodeRemover.java227
-rw-r--r--dx/src/com/android/dx/ssa/DomFront.java204
-rw-r--r--dx/src/com/android/dx/ssa/Dominators.java285
-rw-r--r--dx/src/com/android/dx/ssa/EscapeAnalysis.java843
-rw-r--r--dx/src/com/android/dx/ssa/InterferenceRegisterMapper.java164
-rw-r--r--dx/src/com/android/dx/ssa/LiteralOpUpgrader.java160
-rw-r--r--dx/src/com/android/dx/ssa/LocalVariableExtractor.java208
-rw-r--r--dx/src/com/android/dx/ssa/LocalVariableInfo.java251
-rw-r--r--dx/src/com/android/dx/ssa/MoveParamCombiner.java156
-rw-r--r--dx/src/com/android/dx/ssa/NormalSsaInsn.java234
-rw-r--r--dx/src/com/android/dx/ssa/Optimizer.java255
-rw-r--r--dx/src/com/android/dx/ssa/PhiInsn.java377
-rw-r--r--dx/src/com/android/dx/ssa/PhiTypeResolver.java200
-rw-r--r--dx/src/com/android/dx/ssa/RegisterMapper.java61
-rw-r--r--dx/src/com/android/dx/ssa/SCCP.java483
-rw-r--r--dx/src/com/android/dx/ssa/SetFactory.java95
-rw-r--r--dx/src/com/android/dx/ssa/SsaBasicBlock.java1032
-rw-r--r--dx/src/com/android/dx/ssa/SsaConverter.java391
-rw-r--r--dx/src/com/android/dx/ssa/SsaInsn.java289
-rw-r--r--dx/src/com/android/dx/ssa/SsaMethod.java873
-rw-r--r--dx/src/com/android/dx/ssa/SsaRenamer.java662
-rw-r--r--dx/src/com/android/dx/ssa/_tests/_DomFront.java32
-rw-r--r--dx/src/com/android/dx/ssa/back/FirstFitAllocator.java151
-rw-r--r--dx/src/com/android/dx/ssa/back/FirstFitLocalCombiningAllocator.java958
-rw-r--r--dx/src/com/android/dx/ssa/back/IdenticalBlockCombiner.java182
-rw-r--r--dx/src/com/android/dx/ssa/back/InterferenceGraph.java113
-rw-r--r--dx/src/com/android/dx/ssa/back/LivenessAnalyzer.java277
-rw-r--r--dx/src/com/android/dx/ssa/back/NullRegisterAllocator.java58
-rw-r--r--dx/src/com/android/dx/ssa/back/RegisterAllocator.java197
-rw-r--r--dx/src/com/android/dx/ssa/back/SsaToRop.java386
-rw-r--r--dx/src/com/android/dx/ssa/package-info.java103
-rw-r--r--dx/src/com/android/dx/util/AnnotatedOutput.java79
-rw-r--r--dx/src/com/android/dx/util/BitIntSet.java145
-rw-r--r--dx/src/com/android/dx/util/Bits.java236
-rw-r--r--dx/src/com/android/dx/util/ByteArray.java361
-rw-r--r--dx/src/com/android/dx/util/ByteArrayAnnotatedOutput.java639
-rw-r--r--dx/src/com/android/dx/util/ExceptionWithContext.java149
-rw-r--r--dx/src/com/android/dx/util/FileUtils.java92
-rw-r--r--dx/src/com/android/dx/util/FixedSizeList.java276
-rw-r--r--dx/src/com/android/dx/util/Hex.java303
-rw-r--r--dx/src/com/android/dx/util/HexParser.java145
-rw-r--r--dx/src/com/android/dx/util/IndentingWriter.java169
-rw-r--r--dx/src/com/android/dx/util/IntIterator.java38
-rw-r--r--dx/src/com/android/dx/util/IntList.java452
-rw-r--r--dx/src/com/android/dx/util/IntSet.java67
-rw-r--r--dx/src/com/android/dx/util/LabeledItem.java30
-rw-r--r--dx/src/com/android/dx/util/LabeledList.java161
-rw-r--r--dx/src/com/android/dx/util/Leb128Utils.java77
-rw-r--r--dx/src/com/android/dx/util/ListIntSet.java132
-rw-r--r--dx/src/com/android/dx/util/MutabilityControl.java89
-rw-r--r--dx/src/com/android/dx/util/MutabilityException.java35
-rw-r--r--dx/src/com/android/dx/util/Output.java129
-rw-r--r--dx/src/com/android/dx/util/ToHuman.java31
-rw-r--r--dx/src/com/android/dx/util/TwoColumnOutput.java254
-rw-r--r--dx/src/com/android/dx/util/Warning.java31
-rw-r--r--dx/src/com/android/dx/util/Writers.java48
-rw-r--r--dx/src/com/android/dx/util/_tests/_BitIntSet.java211
-rw-r--r--dx/src/com/android/dx/util/_tests/_Bits.java351
-rw-r--r--dx/src/com/android/dx/util/_tests/_IntList.java72
-rw-r--r--dx/src/com/android/dx/util/_tests/_ListIntSet.java203
-rw-r--r--dx/src/com/android/dx/util/package.html3
-rw-r--r--dx/src/junit/extensions/ActiveTestSuite.java64
-rw-r--r--dx/src/junit/extensions/ExceptionTestCase.java46
-rw-r--r--dx/src/junit/extensions/RepeatedTest.java31
-rw-r--r--dx/src/junit/extensions/TestDecorator.java38
-rw-r--r--dx/src/junit/extensions/TestSetup.java37
-rw-r--r--dx/src/junit/framework/Assert.java291
-rw-r--r--dx/src/junit/framework/AssertionFailedError.java13
-rw-r--r--dx/src/junit/framework/ComparisonFailure.java68
-rw-r--r--dx/src/junit/framework/Protectable.java14
-rw-r--r--dx/src/junit/framework/Test.java17
-rw-r--r--dx/src/junit/framework/TestCase.java197
-rw-r--r--dx/src/junit/framework/TestFailure.java57
-rw-r--r--dx/src/junit/framework/TestListener.java23
-rw-r--r--dx/src/junit/framework/TestResult.java166
-rw-r--r--dx/src/junit/framework/TestSuite.java265
-rw-r--r--dx/src/junit/runner/BaseTestRunner.java323
-rw-r--r--dx/src/junit/runner/ClassPathTestCollector.java80
-rw-r--r--dx/src/junit/runner/FailureDetailView.java23
-rw-r--r--dx/src/junit/runner/LoadingTestCollector.java69
-rw-r--r--dx/src/junit/runner/ReloadingTestSuiteLoader.java19
-rw-r--r--dx/src/junit/runner/SimpleTestCollector.java20
-rw-r--r--dx/src/junit/runner/Sorter.java38
-rw-r--r--dx/src/junit/runner/StandardTestSuiteLoader.java19
-rw-r--r--dx/src/junit/runner/TestCaseClassLoader.java226
-rw-r--r--dx/src/junit/runner/TestCollector.java16
-rw-r--r--dx/src/junit/runner/TestRunListener.java19
-rw-r--r--dx/src/junit/runner/TestSuiteLoader.java9
-rw-r--r--dx/src/junit/runner/Version.java14
-rw-r--r--dx/src/junit/runner/excluded.properties12
-rw-r--r--dx/src/junit/runner/logo.gifbin0 -> 964 bytes
-rw-r--r--dx/src/junit/runner/smalllogo.gifbin0 -> 883 bytes
-rw-r--r--dx/src/junit/textui/ResultPrinter.java139
-rw-r--r--dx/src/junit/textui/TestRunner.java189
-rw-r--r--dx/tests/001-nop/expected.txt1
-rw-r--r--dx/tests/001-nop/info.txt2
-rw-r--r--dx/tests/001-nop/run17
-rw-r--r--dx/tests/002-minimal-valid/expected.txt46
-rw-r--r--dx/tests/002-minimal-valid/info.txt1
-rw-r--r--dx/tests/002-minimal-valid/run17
-rw-r--r--dx/tests/002-minimal-valid/small-class.txt49
-rw-r--r--dx/tests/003-magic-version-access/class-bad-magic.txt25
-rw-r--r--dx/tests/003-magic-version-access/class-version-44.0.txt25
-rw-r--r--dx/tests/003-magic-version-access/class-version-44.65535.txt25
-rw-r--r--dx/tests/003-magic-version-access/class-version-45.0.txt25
-rw-r--r--dx/tests/003-magic-version-access/class-version-45.65535.txt25
-rw-r--r--dx/tests/003-magic-version-access/class-version-48.0.txt25
-rw-r--r--dx/tests/003-magic-version-access/class-version-48.65535.txt25
-rw-r--r--dx/tests/003-magic-version-access/class-version-49.0.txt25
-rw-r--r--dx/tests/003-magic-version-access/class-version-49.1.txt26
-rw-r--r--dx/tests/003-magic-version-access/class-version-49.65535.txt26
-rw-r--r--dx/tests/003-magic-version-access/class-version-50.0.txt26
-rw-r--r--dx/tests/003-magic-version-access/class-version-50.1.txt26
-rw-r--r--dx/tests/003-magic-version-access/class-version-50.65535.txt26
-rw-r--r--dx/tests/003-magic-version-access/class-version-51.0.txt26
-rw-r--r--dx/tests/003-magic-version-access/expected.txt243
-rw-r--r--dx/tests/003-magic-version-access/info.txt9
-rw-r--r--dx/tests/003-magic-version-access/run46
-rw-r--r--dx/tests/003-magic-version-access/small-class.txt25
-rw-r--r--dx/tests/004-cp-bottom-up/expected.txt34
-rw-r--r--dx/tests/004-cp-bottom-up/info.txt8
-rw-r--r--dx/tests/004-cp-bottom-up/run17
-rw-r--r--dx/tests/004-cp-bottom-up/small-class.txt38
-rw-r--r--dx/tests/005-cp-top-down/expected.txt34
-rw-r--r--dx/tests/005-cp-top-down/info.txt8
-rw-r--r--dx/tests/005-cp-top-down/run17
-rw-r--r--dx/tests/005-cp-top-down/small-class.txt38
-rw-r--r--dx/tests/006-interfaces/expected.txt31
-rw-r--r--dx/tests/006-interfaces/info.txt6
-rw-r--r--dx/tests/006-interfaces/run17
-rw-r--r--dx/tests/006-interfaces/small-class.txt33
-rw-r--r--dx/tests/007-no-superclass/expected.txt19
-rw-r--r--dx/tests/007-no-superclass/info.txt6
-rw-r--r--dx/tests/007-no-superclass/run17
-rw-r--r--dx/tests/007-no-superclass/small-class.txt24
-rw-r--r--dx/tests/008-field/expected.txt30
-rw-r--r--dx/tests/008-field/info.txt7
-rw-r--r--dx/tests/008-field/run17
-rw-r--r--dx/tests/008-field/small-class.txt34
-rw-r--r--dx/tests/009-method/expected.txt30
-rw-r--r--dx/tests/009-method/info.txt7
-rw-r--r--dx/tests/009-method/run17
-rw-r--r--dx/tests/009-method/small-class.txt34
-rw-r--r--dx/tests/010-class-attrib-InnerClasses/expected.txt46
-rw-r--r--dx/tests/010-class-attrib-InnerClasses/info.txt7
-rw-r--r--dx/tests/010-class-attrib-InnerClasses/run17
-rw-r--r--dx/tests/010-class-attrib-InnerClasses/small-class.txt37
-rw-r--r--dx/tests/011-class-attrib-Synthetic/expected.txt27
-rw-r--r--dx/tests/011-class-attrib-Synthetic/info.txt6
-rw-r--r--dx/tests/011-class-attrib-Synthetic/run17
-rw-r--r--dx/tests/011-class-attrib-Synthetic/small-class.txt30
-rw-r--r--dx/tests/012-class-attrib-SourceFile/expected.txt29
-rw-r--r--dx/tests/012-class-attrib-SourceFile/info.txt6
-rw-r--r--dx/tests/012-class-attrib-SourceFile/run17
-rw-r--r--dx/tests/012-class-attrib-SourceFile/small-class.txt32
-rw-r--r--dx/tests/013-class-attrib-Deprecated/expected.txt27
-rw-r--r--dx/tests/013-class-attrib-Deprecated/info.txt6
-rw-r--r--dx/tests/013-class-attrib-Deprecated/run17
-rw-r--r--dx/tests/013-class-attrib-Deprecated/small-class.txt30
-rw-r--r--dx/tests/014-field-attrib-ConstantValue/expected.txt162
-rw-r--r--dx/tests/014-field-attrib-ConstantValue/info.txt7
-rw-r--r--dx/tests/014-field-attrib-ConstantValue/run17
-rw-r--r--dx/tests/014-field-attrib-ConstantValue/small-class.txt140
-rw-r--r--dx/tests/015-field-attrib-Synthetic/expected.txt36
-rw-r--r--dx/tests/015-field-attrib-Synthetic/info.txt6
-rw-r--r--dx/tests/015-field-attrib-Synthetic/run17
-rw-r--r--dx/tests/015-field-attrib-Synthetic/small-class.txt38
-rw-r--r--dx/tests/016-field-attrib-Deprecated/expected.txt36
-rw-r--r--dx/tests/016-field-attrib-Deprecated/info.txt6
-rw-r--r--dx/tests/016-field-attrib-Deprecated/run17
-rw-r--r--dx/tests/016-field-attrib-Deprecated/small-class.txt38
-rw-r--r--dx/tests/017-method-attrib-Code/expected.txt42
-rw-r--r--dx/tests/017-method-attrib-Code/info.txt6
-rw-r--r--dx/tests/017-method-attrib-Code/run17
-rw-r--r--dx/tests/017-method-attrib-Code/small-class.txt43
-rw-r--r--dx/tests/018-method-attrib-Exceptions/expected.txt40
-rw-r--r--dx/tests/018-method-attrib-Exceptions/info.txt6
-rw-r--r--dx/tests/018-method-attrib-Exceptions/run17
-rw-r--r--dx/tests/018-method-attrib-Exceptions/small-class.txt41
-rw-r--r--dx/tests/019-method-attrib-Synthetic/expected.txt36
-rw-r--r--dx/tests/019-method-attrib-Synthetic/info.txt6
-rw-r--r--dx/tests/019-method-attrib-Synthetic/run17
-rw-r--r--dx/tests/019-method-attrib-Synthetic/small-class.txt37
-rw-r--r--dx/tests/020-method-attrib-Deprecated/expected.txt36
-rw-r--r--dx/tests/020-method-attrib-Deprecated/info.txt6
-rw-r--r--dx/tests/020-method-attrib-Deprecated/run17
-rw-r--r--dx/tests/020-method-attrib-Deprecated/small-class.txt37
-rw-r--r--dx/tests/021-code-attrib-LineNumberTable/expected.txt52
-rw-r--r--dx/tests/021-code-attrib-LineNumberTable/info.txt7
-rw-r--r--dx/tests/021-code-attrib-LineNumberTable/run17
-rw-r--r--dx/tests/021-code-attrib-LineNumberTable/small-class.txt51
-rw-r--r--dx/tests/022-code-attrib-LocalVariableTable/expected.txt57
-rw-r--r--dx/tests/022-code-attrib-LocalVariableTable/info.txt7
-rw-r--r--dx/tests/022-code-attrib-LocalVariableTable/run17
-rw-r--r--dx/tests/022-code-attrib-LocalVariableTable/small-class.txt56
-rw-r--r--dx/tests/023-code-exception-table/expected.txt51
-rw-r--r--dx/tests/023-code-exception-table/info.txt7
-rw-r--r--dx/tests/023-code-exception-table/run17
-rw-r--r--dx/tests/023-code-exception-table/small-class.txt52
-rw-r--r--dx/tests/024-code-bytecode/expected.txt294
-rw-r--r--dx/tests/024-code-bytecode/info.txt7
-rw-r--r--dx/tests/024-code-bytecode/run17
-rw-r--r--dx/tests/024-code-bytecode/small-class.txt304
-rw-r--r--dx/tests/025-class-attrib-Signature/expected.txt29
-rw-r--r--dx/tests/025-class-attrib-Signature/info.txt6
-rw-r--r--dx/tests/025-class-attrib-Signature/run17
-rw-r--r--dx/tests/025-class-attrib-Signature/small-class.txt32
-rw-r--r--dx/tests/026-field-attrib-Signature/expected.txt38
-rw-r--r--dx/tests/026-field-attrib-Signature/info.txt6
-rw-r--r--dx/tests/026-field-attrib-Signature/run17
-rw-r--r--dx/tests/026-field-attrib-Signature/small-class.txt40
-rw-r--r--dx/tests/027-method-attrib-Signature/expected.txt38
-rw-r--r--dx/tests/027-method-attrib-Signature/info.txt6
-rw-r--r--dx/tests/027-method-attrib-Signature/run17
-rw-r--r--dx/tests/027-method-attrib-Signature/small-class.txt39
-rw-r--r--dx/tests/028-class-attrib-EnclosingMethod/expected.txt61
-rw-r--r--dx/tests/028-class-attrib-EnclosingMethod/info.txt8
-rw-r--r--dx/tests/028-class-attrib-EnclosingMethod/run17
-rw-r--r--dx/tests/028-class-attrib-EnclosingMethod/small-class-1.txt32
-rw-r--r--dx/tests/028-class-attrib-EnclosingMethod/small-class-2.txt35
-rw-r--r--dx/tests/029-unit-Bits/expected.txt1
-rw-r--r--dx/tests/029-unit-Bits/info.txt1
-rw-r--r--dx/tests/029-unit-Bits/run23
-rw-r--r--dx/tests/030-minimal-jasmin/blort.j28
-rw-r--r--dx/tests/030-minimal-jasmin/expected.txt7
-rw-r--r--dx/tests/030-minimal-jasmin/info.txt2
-rw-r--r--dx/tests/030-minimal-jasmin/run18
-rw-r--r--dx/tests/031-bb-dead-code/blort.j182
-rw-r--r--dx/tests/031-bb-dead-code/expected.txt190
-rw-r--r--dx/tests/031-bb-dead-code/info.txt3
-rw-r--r--dx/tests/031-bb-dead-code/run18
-rw-r--r--dx/tests/032-bb-live-code/blort.j342
-rw-r--r--dx/tests/032-bb-live-code/expected.txt497
-rw-r--r--dx/tests/032-bb-live-code/info.txt5
-rw-r--r--dx/tests/032-bb-live-code/run18
-rw-r--r--dx/tests/033-unit-IntList/expected.txt1
-rw-r--r--dx/tests/033-unit-IntList/info.txt1
-rw-r--r--dx/tests/033-unit-IntList/run23
-rw-r--r--dx/tests/034-dex-minimal/blort.j16
-rw-r--r--dx/tests/034-dex-minimal/expected.txt70
-rw-r--r--dx/tests/034-dex-minimal/info.txt9
-rw-r--r--dx/tests/034-dex-minimal/run24
-rw-r--r--dx/tests/035-dex-instance-var/blort.j18
-rw-r--r--dx/tests/035-dex-instance-var/expected.txt1
-rw-r--r--dx/tests/035-dex-instance-var/info.txt4
-rw-r--r--dx/tests/035-dex-instance-var/run21
-rw-r--r--dx/tests/036-dex-static-var/blort.j18
-rw-r--r--dx/tests/036-dex-static-var/expected.txt1
-rw-r--r--dx/tests/036-dex-static-var/info.txt4
-rw-r--r--dx/tests/036-dex-static-var/run21
-rw-r--r--dx/tests/037-dex-static-final-var/blort.j18
-rw-r--r--dx/tests/037-dex-static-final-var/expected.txt1
-rw-r--r--dx/tests/037-dex-static-final-var/info.txt4
-rw-r--r--dx/tests/037-dex-static-final-var/run21
-rw-r--r--dx/tests/038-dex-instance-method/blort.j21
-rw-r--r--dx/tests/038-dex-instance-method/expected.txt1
-rw-r--r--dx/tests/038-dex-instance-method/info.txt4
-rw-r--r--dx/tests/038-dex-instance-method/run21
-rw-r--r--dx/tests/039-dex-static-method/blort.j21
-rw-r--r--dx/tests/039-dex-static-method/expected.txt1
-rw-r--r--dx/tests/039-dex-static-method/info.txt4
-rw-r--r--dx/tests/039-dex-static-method/run21
-rw-r--r--dx/tests/040-dex-constructor/blort.j21
-rw-r--r--dx/tests/040-dex-constructor/expected.txt1
-rw-r--r--dx/tests/040-dex-constructor/info.txt4
-rw-r--r--dx/tests/040-dex-constructor/run21
-rw-r--r--dx/tests/041-dex-abstract-method/blort.j19
-rw-r--r--dx/tests/041-dex-abstract-method/expected.txt1
-rw-r--r--dx/tests/041-dex-abstract-method/info.txt4
-rw-r--r--dx/tests/041-dex-abstract-method/run21
-rw-r--r--dx/tests/042-dex-ignore-result/Blort.java27
-rw-r--r--dx/tests/042-dex-ignore-result/expected.txt1
-rw-r--r--dx/tests/042-dex-ignore-result/info.txt5
-rw-r--r--dx/tests/042-dex-ignore-result/run21
-rw-r--r--dx/tests/043-dex-two-classes/Blort.java20
-rw-r--r--dx/tests/043-dex-two-classes/Zorch.java20
-rw-r--r--dx/tests/043-dex-two-classes/expected.txt3
-rw-r--r--dx/tests/043-dex-two-classes/info.txt4
-rw-r--r--dx/tests/043-dex-two-classes/run21
-rw-r--r--dx/tests/044-dex-math-ops/Blort.java73
-rw-r--r--dx/tests/044-dex-math-ops/expected.txt213
-rw-r--r--dx/tests/044-dex-math-ops/info.txt6
-rw-r--r--dx/tests/044-dex-math-ops/run19
-rw-r--r--dx/tests/045-dex-switch-ops/Blort.java56
-rw-r--r--dx/tests/045-dex-switch-ops/expected.txt53
-rw-r--r--dx/tests/045-dex-switch-ops/info.txt6
-rw-r--r--dx/tests/045-dex-switch-ops/run19
-rw-r--r--dx/tests/046-dex-exceptions/Blort.java58
-rw-r--r--dx/tests/046-dex-exceptions/expected.txt48
-rw-r--r--dx/tests/046-dex-exceptions/info.txt6
-rw-r--r--dx/tests/046-dex-exceptions/run18
-rw-r--r--dx/tests/047-dex-wide-args/Blort.java26
-rw-r--r--dx/tests/047-dex-wide-args/expected.txt34
-rw-r--r--dx/tests/047-dex-wide-args/info.txt6
-rw-r--r--dx/tests/047-dex-wide-args/run19
-rw-r--r--dx/tests/048-dex-new-array/Blort.java34
-rw-r--r--dx/tests/048-dex-new-array/expected.txt29
-rw-r--r--dx/tests/048-dex-new-array/info.txt6
-rw-r--r--dx/tests/048-dex-new-array/run18
-rw-r--r--dx/tests/049-dex-instanceof/Blort.java22
-rw-r--r--dx/tests/049-dex-instanceof/expected.txt7
-rw-r--r--dx/tests/049-dex-instanceof/info.txt6
-rw-r--r--dx/tests/049-dex-instanceof/run19
-rw-r--r--dx/tests/050-dex-checkcast/Blort.java22
-rw-r--r--dx/tests/050-dex-checkcast/expected.txt7
-rw-r--r--dx/tests/050-dex-checkcast/info.txt6
-rw-r--r--dx/tests/050-dex-checkcast/run19
-rw-r--r--dx/tests/051-dex-explicit-null/Blort.java26
-rw-r--r--dx/tests/051-dex-explicit-null/expected.txt10
-rw-r--r--dx/tests/051-dex-explicit-null/info.txt7
-rw-r--r--dx/tests/051-dex-explicit-null/run19
-rw-r--r--dx/tests/052-dex-static-var-access/Blort.java51
-rw-r--r--dx/tests/052-dex-static-var-access/expected.txt59
-rw-r--r--dx/tests/052-dex-static-var-access/info.txt6
-rw-r--r--dx/tests/052-dex-static-var-access/run19
-rw-r--r--dx/tests/053-dex-instance-var-access/Blort.java51
-rw-r--r--dx/tests/053-dex-instance-var-access/expected.txt79
-rw-r--r--dx/tests/053-dex-instance-var-access/info.txt6
-rw-r--r--dx/tests/053-dex-instance-var-access/run19
-rw-r--r--dx/tests/054-dex-high16/Blort.java74
-rw-r--r--dx/tests/054-dex-high16/expected.txt68
-rw-r--r--dx/tests/054-dex-high16/info.txt6
-rw-r--r--dx/tests/054-dex-high16/run19
-rw-r--r--dx/tests/055-dex-explicit-throw/Blort.java32
-rw-r--r--dx/tests/055-dex-explicit-throw/expected.txt17
-rw-r--r--dx/tests/055-dex-explicit-throw/info.txt6
-rw-r--r--dx/tests/055-dex-explicit-throw/run19
-rw-r--r--dx/tests/056-dex-call-interface/Blort.java26
-rw-r--r--dx/tests/056-dex-call-interface/Zorch.java23
-rw-r--r--dx/tests/056-dex-call-interface/expected.txt24
-rw-r--r--dx/tests/056-dex-call-interface/info.txt6
-rw-r--r--dx/tests/056-dex-call-interface/run19
-rw-r--r--dx/tests/057-dex-call-virtual/Blort.java23
-rw-r--r--dx/tests/057-dex-call-virtual/Zorch.java26
-rw-r--r--dx/tests/057-dex-call-virtual/expected.txt11
-rw-r--r--dx/tests/057-dex-call-virtual/info.txt6
-rw-r--r--dx/tests/057-dex-call-virtual/run19
-rw-r--r--dx/tests/058-dex-call-direct/Blort.java31
-rw-r--r--dx/tests/058-dex-call-direct/expected.txt11
-rw-r--r--dx/tests/058-dex-call-direct/info.txt6
-rw-r--r--dx/tests/058-dex-call-direct/run19
-rw-r--r--dx/tests/059-dex-call-super/Blort.java28
-rw-r--r--dx/tests/059-dex-call-super/Zorch.java30
-rw-r--r--dx/tests/059-dex-call-super/expected.txt17
-rw-r--r--dx/tests/059-dex-call-super/info.txt6
-rw-r--r--dx/tests/059-dex-call-super/run19
-rw-r--r--dx/tests/060-dex-call-static/Blort.java23
-rw-r--r--dx/tests/060-dex-call-static/Zorch.java26
-rw-r--r--dx/tests/060-dex-call-static/expected.txt7
-rw-r--r--dx/tests/060-dex-call-static/info.txt6
-rw-r--r--dx/tests/060-dex-call-static/run19
-rw-r--r--dx/tests/061-dex-try-catch/Blort.java66
-rw-r--r--dx/tests/061-dex-try-catch/expected.txt49
-rw-r--r--dx/tests/061-dex-try-catch/info.txt6
-rw-r--r--dx/tests/061-dex-try-catch/run19
-rw-r--r--dx/tests/062-dex-synch-method/Blort.java74
-rw-r--r--dx/tests/062-dex-synch-method/expected.txt146
-rw-r--r--dx/tests/062-dex-synch-method/info.txt6
-rw-r--r--dx/tests/062-dex-synch-method/run19
-rw-r--r--dx/tests/063-dex-empty-switch/Blort.java32
-rw-r--r--dx/tests/063-dex-empty-switch/expected.txt18
-rw-r--r--dx/tests/063-dex-empty-switch/info.txt7
-rw-r--r--dx/tests/063-dex-empty-switch/run19
-rw-r--r--dx/tests/064-dex-array-access/Blort.java74
-rw-r--r--dx/tests/064-dex-array-access/expected.txt157
-rw-r--r--dx/tests/064-dex-array-access/info.txt6
-rw-r--r--dx/tests/064-dex-array-access/run19
-rw-r--r--dx/tests/065-dex-new-array/Blort.java54
-rw-r--r--dx/tests/065-dex-new-array/expected.txt63
-rw-r--r--dx/tests/065-dex-new-array/info.txt6
-rw-r--r--dx/tests/065-dex-new-array/run19
-rw-r--r--dx/tests/066-dex-try-catch-rethrow/Blort.java78
-rw-r--r--dx/tests/066-dex-try-catch-rethrow/expected.txt95
-rw-r--r--dx/tests/066-dex-try-catch-rethrow/info.txt6
-rw-r--r--dx/tests/066-dex-try-catch-rethrow/run19
-rw-r--r--dx/tests/067-dex-switch-and-try/Blort.java114
-rw-r--r--dx/tests/067-dex-switch-and-try/expected.txt100
-rw-r--r--dx/tests/067-dex-switch-and-try/info.txt7
-rw-r--r--dx/tests/067-dex-switch-and-try/run19
-rw-r--r--dx/tests/068-dex-infinite-loop/Blort.java52
-rw-r--r--dx/tests/068-dex-infinite-loop/expected.txt28
-rw-r--r--dx/tests/068-dex-infinite-loop/info.txt6
-rw-r--r--dx/tests/068-dex-infinite-loop/run19
-rw-r--r--dx/tests/069-dex-source-position/Blort.java32
-rw-r--r--dx/tests/069-dex-source-position/expected.txt134
-rw-r--r--dx/tests/069-dex-source-position/info.txt6
-rw-r--r--dx/tests/069-dex-source-position/run23
-rw-r--r--dx/tests/070-dex-multianewarray/Blort.java98
-rw-r--r--dx/tests/070-dex-multianewarray/expected.txt246
-rw-r--r--dx/tests/070-dex-multianewarray/info.txt6
-rw-r--r--dx/tests/070-dex-multianewarray/run19
-rw-r--r--dx/tests/071-dex-java-stack-ops/blort.j319
-rw-r--r--dx/tests/071-dex-java-stack-ops/expected.txt210
-rw-r--r--dx/tests/071-dex-java-stack-ops/info.txt7
-rw-r--r--dx/tests/071-dex-java-stack-ops/run19
-rw-r--r--dx/tests/072-dex-switch-edge-cases/Blort.java97
-rw-r--r--dx/tests/072-dex-switch-edge-cases/expected.txt126
-rw-r--r--dx/tests/072-dex-switch-edge-cases/info.txt6
-rw-r--r--dx/tests/072-dex-switch-edge-cases/run19
-rw-r--r--dx/tests/073-dex-null-array-refs/Blort.java73
-rw-r--r--dx/tests/073-dex-null-array-refs/expected.txt85
-rw-r--r--dx/tests/073-dex-null-array-refs/info.txt7
-rw-r--r--dx/tests/073-dex-null-array-refs/run19
-rw-r--r--dx/tests/074-dex-form35c-edge-case/Blort.java41
-rw-r--r--dx/tests/074-dex-form35c-edge-case/expected.txt33
-rw-r--r--dx/tests/074-dex-form35c-edge-case/info.txt8
-rw-r--r--dx/tests/074-dex-form35c-edge-case/run19
-rw-r--r--dx/tests/075-dex-cat2-value-merge/Blort.java27
-rw-r--r--dx/tests/075-dex-cat2-value-merge/expected.txt12
-rw-r--r--dx/tests/075-dex-cat2-value-merge/info.txt7
-rw-r--r--dx/tests/075-dex-cat2-value-merge/run19
-rw-r--r--dx/tests/076-dex-synch-and-stack/Blort.java22
-rw-r--r--dx/tests/076-dex-synch-and-stack/expected.txt19
-rw-r--r--dx/tests/076-dex-synch-and-stack/info.txt7
-rw-r--r--dx/tests/076-dex-synch-and-stack/run19
-rw-r--r--dx/tests/077-dex-code-alignment/Blort.java26
-rw-r--r--dx/tests/077-dex-code-alignment/expected.txt0
-rw-r--r--dx/tests/077-dex-code-alignment/info.txt6
-rw-r--r--dx/tests/077-dex-code-alignment/run30
-rw-r--r--dx/tests/078-dex-local-variable-table/Blort.java112
-rw-r--r--dx/tests/078-dex-local-variable-table/expected.txt314
-rw-r--r--dx/tests/078-dex-local-variable-table/info.txt6
-rw-r--r--dx/tests/078-dex-local-variable-table/run19
-rw-r--r--dx/tests/079-dex-local-variable-renumbering/Blort.java39
-rw-r--r--dx/tests/079-dex-local-variable-renumbering/expected.txt87
-rw-r--r--dx/tests/079-dex-local-variable-renumbering/info.txt7
-rw-r--r--dx/tests/079-dex-local-variable-renumbering/run19
-rw-r--r--dx/tests/080-dex-exception-tables/Blort.java199
-rw-r--r--dx/tests/080-dex-exception-tables/expected.txt286
-rw-r--r--dx/tests/080-dex-exception-tables/info.txt8
-rw-r--r--dx/tests/080-dex-exception-tables/run19
-rw-r--r--dx/tests/081-dex-throws-list/Blort.java28
-rw-r--r--dx/tests/081-dex-throws-list/expected.txt4
-rw-r--r--dx/tests/081-dex-throws-list/info.txt7
-rw-r--r--dx/tests/081-dex-throws-list/run19
-rw-r--r--dx/tests/082-dex-throws-list-sharing/Blort.java38
-rw-r--r--dx/tests/082-dex-throws-list-sharing/expected.txt2
-rw-r--r--dx/tests/082-dex-throws-list-sharing/info.txt7
-rw-r--r--dx/tests/082-dex-throws-list-sharing/run20
-rw-r--r--dx/tests/083-ssa-phi-placement/Blort.java66
-rw-r--r--dx/tests/083-ssa-phi-placement/expected.txt345
-rw-r--r--dx/tests/083-ssa-phi-placement/info.txt5
-rw-r--r--dx/tests/083-ssa-phi-placement/run18
-rw-r--r--dx/tests/084-dex-high-register-moves/Blort.java49
-rw-r--r--dx/tests/084-dex-high-register-moves/expected.txt60
-rw-r--r--dx/tests/084-dex-high-register-moves/info.txt7
-rw-r--r--dx/tests/084-dex-high-register-moves/run19
-rw-r--r--dx/tests/085-dex-jsr-ret/blort.j69
-rw-r--r--dx/tests/085-dex-jsr-ret/expected.txt171
-rw-r--r--dx/tests/085-dex-jsr-ret/info.txt1
-rw-r--r--dx/tests/085-dex-jsr-ret/run18
-rw-r--r--dx/tests/086-ssa-edge-split/Blort.java74
-rw-r--r--dx/tests/086-ssa-edge-split/expected.txt343
-rw-r--r--dx/tests/086-ssa-edge-split/info.txt5
-rw-r--r--dx/tests/086-ssa-edge-split/run18
-rw-r--r--dx/tests/087-ssa-local-vars/Blort.java94
-rw-r--r--dx/tests/087-ssa-local-vars/expected.txt1266
-rw-r--r--dx/tests/087-ssa-local-vars/info.txt5
-rw-r--r--dx/tests/087-ssa-local-vars/run18
-rw-r--r--dx/tests/088-ssa-combine-blocks/Blort.java44
-rw-r--r--dx/tests/088-ssa-combine-blocks/expected.txt82
-rw-r--r--dx/tests/088-ssa-combine-blocks/info.txt5
-rw-r--r--dx/tests/088-ssa-combine-blocks/run18
-rw-r--r--dx/tests/089-dex-define-object/Class.java21
-rw-r--r--dx/tests/089-dex-define-object/Object.java50
-rw-r--r--dx/tests/089-dex-define-object/String.java21
-rw-r--r--dx/tests/089-dex-define-object/expected.txt1
-rw-r--r--dx/tests/089-dex-define-object/info.txt4
-rw-r--r--dx/tests/089-dex-define-object/run21
-rw-r--r--dx/tests/090-dex-unify-arrays/Blort.java60
-rw-r--r--dx/tests/090-dex-unify-arrays/expected.txt122
-rw-r--r--dx/tests/090-dex-unify-arrays/info.txt6
-rw-r--r--dx/tests/090-dex-unify-arrays/run19
-rw-r--r--dx/tests/091-ssa-const-collector/Blort.java64
-rw-r--r--dx/tests/091-ssa-const-collector/expected.txt475
-rw-r--r--dx/tests/091-ssa-const-collector/info.txt5
-rw-r--r--dx/tests/091-ssa-const-collector/run18
-rw-r--r--dx/tests/092-ssa-cfg-edge-cases/Blort.java20
-rw-r--r--dx/tests/092-ssa-cfg-edge-cases/expected.txt120
-rw-r--r--dx/tests/092-ssa-cfg-edge-cases/info.txt5
-rw-r--r--dx/tests/092-ssa-cfg-edge-cases/run18
-rw-r--r--dx/tests/093-ssa-invoke-range/Blort.java69
-rw-r--r--dx/tests/093-ssa-invoke-range/expected.txt301
-rw-r--r--dx/tests/093-ssa-invoke-range/info.txt6
-rw-r--r--dx/tests/093-ssa-invoke-range/run18
-rw-r--r--dx/tests/094-scala-locals/blort.j44
-rw-r--r--dx/tests/094-scala-locals/expected.txt85
-rw-r--r--dx/tests/094-scala-locals/info.txt8
-rw-r--r--dx/tests/094-scala-locals/run18
-rw-r--r--dx/tests/095-dex-const-string-jumbo/Blort.java25
-rw-r--r--dx/tests/095-dex-const-string-jumbo/expected.txt6
-rw-r--r--dx/tests/095-dex-const-string-jumbo/info.txt6
-rw-r--r--dx/tests/095-dex-const-string-jumbo/run41
-rw-r--r--dx/tests/096-dex-giant-catch/Blort.java22
-rw-r--r--dx/tests/096-dex-giant-catch/expected.txt5
-rw-r--r--dx/tests/096-dex-giant-catch/info.txt7
-rw-r--r--dx/tests/096-dex-giant-catch/run40
-rw-r--r--dx/tests/097-dex-branch-offset-zero/Blort.java30
-rw-r--r--dx/tests/097-dex-branch-offset-zero/expected.txt1
-rw-r--r--dx/tests/097-dex-branch-offset-zero/info.txt6
-rw-r--r--dx/tests/097-dex-branch-offset-zero/run27
-rw-r--r--dx/tests/098-dex-jsr-ret-throw/ViewDebug$ViewServer.classbin0 -> 5381 bytes
-rw-r--r--dx/tests/098-dex-jsr-ret-throw/expected.txt652
-rw-r--r--dx/tests/098-dex-jsr-ret-throw/info.txt4
-rwxr-xr-xdx/tests/098-dex-jsr-ret-throw/run17
-rw-r--r--dx/tests/099-dex-core-library-error/Blort.java5
-rw-r--r--dx/tests/099-dex-core-library-error/Muffins.java5
-rw-r--r--dx/tests/099-dex-core-library-error/Zorch.java5
-rw-r--r--dx/tests/099-dex-core-library-error/expected.txt5
-rw-r--r--dx/tests/099-dex-core-library-error/info.txt3
-rw-r--r--dx/tests/099-dex-core-library-error/run37
-rw-r--r--dx/tests/100-local-mismatch/blort1.j28
-rw-r--r--dx/tests/100-local-mismatch/blort2.j28
-rw-r--r--dx/tests/100-local-mismatch/blort3.j28
-rw-r--r--dx/tests/100-local-mismatch/blort4.j28
-rw-r--r--dx/tests/100-local-mismatch/expected.txt9
-rw-r--r--dx/tests/100-local-mismatch/info.txt3
-rw-r--r--dx/tests/100-local-mismatch/run34
-rw-r--r--dx/tests/101-verify-wide-math/expected.txt54
-rw-r--r--dx/tests/101-verify-wide-math/info.txt3
-rw-r--r--dx/tests/101-verify-wide-math/op_d2f.j25
-rw-r--r--dx/tests/101-verify-wide-math/op_d2i.j25
-rw-r--r--dx/tests/101-verify-wide-math/op_d2l.j25
-rw-r--r--dx/tests/101-verify-wide-math/op_dadd.j26
-rw-r--r--dx/tests/101-verify-wide-math/op_dcmpg.j26
-rw-r--r--dx/tests/101-verify-wide-math/op_dcmpl.j26
-rw-r--r--dx/tests/101-verify-wide-math/op_ddiv.j26
-rw-r--r--dx/tests/101-verify-wide-math/op_dmul.j26
-rw-r--r--dx/tests/101-verify-wide-math/op_dneg.j25
-rw-r--r--dx/tests/101-verify-wide-math/op_drem.j26
-rw-r--r--dx/tests/101-verify-wide-math/op_dsub.j26
-rw-r--r--dx/tests/101-verify-wide-math/op_l2d.j25
-rw-r--r--dx/tests/101-verify-wide-math/op_l2f.j25
-rw-r--r--dx/tests/101-verify-wide-math/op_l2i.j25
-rw-r--r--dx/tests/101-verify-wide-math/op_ladd.j26
-rw-r--r--dx/tests/101-verify-wide-math/op_land.j26
-rw-r--r--dx/tests/101-verify-wide-math/op_lcmp.j26
-rw-r--r--dx/tests/101-verify-wide-math/op_ldiv.j26
-rw-r--r--dx/tests/101-verify-wide-math/op_lmul.j26
-rw-r--r--dx/tests/101-verify-wide-math/op_lneg.j25
-rw-r--r--dx/tests/101-verify-wide-math/op_lor.j26
-rw-r--r--dx/tests/101-verify-wide-math/op_lrem.j26
-rw-r--r--dx/tests/101-verify-wide-math/op_lshl.j26
-rw-r--r--dx/tests/101-verify-wide-math/op_lshr.j26
-rw-r--r--dx/tests/101-verify-wide-math/op_lsub.j26
-rw-r--r--dx/tests/101-verify-wide-math/op_lushr.j26
-rw-r--r--dx/tests/101-verify-wide-math/op_lxor.j26
-rw-r--r--dx/tests/101-verify-wide-math/run54
-rw-r--r--dx/tests/102-verify-nonwide-math/expected.txt48
-rw-r--r--dx/tests/102-verify-nonwide-math/info.txt3
-rw-r--r--dx/tests/102-verify-nonwide-math/op_f2d.j25
-rw-r--r--dx/tests/102-verify-nonwide-math/op_f2i.j25
-rw-r--r--dx/tests/102-verify-nonwide-math/op_f2l.j25
-rw-r--r--dx/tests/102-verify-nonwide-math/op_fadd.j26
-rw-r--r--dx/tests/102-verify-nonwide-math/op_fdiv.j26
-rw-r--r--dx/tests/102-verify-nonwide-math/op_fmul.j26
-rw-r--r--dx/tests/102-verify-nonwide-math/op_fneg.j25
-rw-r--r--dx/tests/102-verify-nonwide-math/op_frem.j26
-rw-r--r--dx/tests/102-verify-nonwide-math/op_fsub.j26
-rw-r--r--dx/tests/102-verify-nonwide-math/op_i2d.j25
-rw-r--r--dx/tests/102-verify-nonwide-math/op_i2f.j25
-rw-r--r--dx/tests/102-verify-nonwide-math/op_i2l.j25
-rw-r--r--dx/tests/102-verify-nonwide-math/op_iadd.j26
-rw-r--r--dx/tests/102-verify-nonwide-math/op_iand.j26
-rw-r--r--dx/tests/102-verify-nonwide-math/op_idiv.j26
-rw-r--r--dx/tests/102-verify-nonwide-math/op_imul.j26
-rw-r--r--dx/tests/102-verify-nonwide-math/op_ineg.j25
-rw-r--r--dx/tests/102-verify-nonwide-math/op_ior.j26
-rw-r--r--dx/tests/102-verify-nonwide-math/op_irem.j26
-rw-r--r--dx/tests/102-verify-nonwide-math/op_ishl.j26
-rw-r--r--dx/tests/102-verify-nonwide-math/op_ishr.j26
-rw-r--r--dx/tests/102-verify-nonwide-math/op_isub.j26
-rw-r--r--dx/tests/102-verify-nonwide-math/op_iushr.j26
-rw-r--r--dx/tests/102-verify-nonwide-math/op_ixor.j26
-rw-r--r--dx/tests/102-verify-nonwide-math/run51
-rw-r--r--dx/tests/103-verify-branch-ops/expected.txt36
-rw-r--r--dx/tests/103-verify-branch-ops/info.txt2
-rw-r--r--dx/tests/103-verify-branch-ops/op_if_acmpeq.j28
-rw-r--r--dx/tests/103-verify-branch-ops/op_if_acmpne.j28
-rw-r--r--dx/tests/103-verify-branch-ops/op_if_icmpeq.j28
-rw-r--r--dx/tests/103-verify-branch-ops/op_if_icmpge.j28
-rw-r--r--dx/tests/103-verify-branch-ops/op_if_icmpgt.j28
-rw-r--r--dx/tests/103-verify-branch-ops/op_if_icmple.j28
-rw-r--r--dx/tests/103-verify-branch-ops/op_if_icmplt.j28
-rw-r--r--dx/tests/103-verify-branch-ops/op_if_icmpne.j28
-rw-r--r--dx/tests/103-verify-branch-ops/op_ifeq.j27
-rw-r--r--dx/tests/103-verify-branch-ops/op_ifge.j27
-rw-r--r--dx/tests/103-verify-branch-ops/op_ifgt.j27
-rw-r--r--dx/tests/103-verify-branch-ops/op_ifle.j27
-rw-r--r--dx/tests/103-verify-branch-ops/op_iflt.j27
-rw-r--r--dx/tests/103-verify-branch-ops/op_ifne.j27
-rw-r--r--dx/tests/103-verify-branch-ops/op_ifnonnull.j27
-rw-r--r--dx/tests/103-verify-branch-ops/op_ifnull.j27
-rw-r--r--dx/tests/103-verify-branch-ops/op_lookupswitch.j33
-rw-r--r--dx/tests/103-verify-branch-ops/op_tableswitch.j33
-rw-r--r--dx/tests/103-verify-branch-ops/run45
-rw-r--r--dx/tests/104-verify-return-ops/expected.txt22
-rw-r--r--dx/tests/104-verify-return-ops/info.txt2
-rw-r--r--dx/tests/104-verify-return-ops/op_areturn.j24
-rw-r--r--dx/tests/104-verify-return-ops/op_dreturn.j24
-rw-r--r--dx/tests/104-verify-return-ops/op_freturn.j24
-rw-r--r--dx/tests/104-verify-return-ops/op_ireturn.j24
-rw-r--r--dx/tests/104-verify-return-ops/op_lreturn.j24
-rw-r--r--dx/tests/104-verify-return-ops/op_sig_areturn.j24
-rw-r--r--dx/tests/104-verify-return-ops/op_sig_dreturn.j24
-rw-r--r--dx/tests/104-verify-return-ops/op_sig_freturn.j24
-rw-r--r--dx/tests/104-verify-return-ops/op_sig_ireturn.j24
-rw-r--r--dx/tests/104-verify-return-ops/op_sig_lreturn.j24
-rw-r--r--dx/tests/104-verify-return-ops/op_sig_return.j23
-rw-r--r--dx/tests/104-verify-return-ops/run38
-rw-r--r--dx/tests/105-verify-load-store-ops/expected.txt82
-rw-r--r--dx/tests/105-verify-load-store-ops/info.txt2
-rw-r--r--dx/tests/105-verify-load-store-ops/op_aaload.j26
-rw-r--r--dx/tests/105-verify-load-store-ops/op_aastore.j27
-rw-r--r--dx/tests/105-verify-load-store-ops/op_astore.j25
-rw-r--r--dx/tests/105-verify-load-store-ops/op_astore_0.j25
-rw-r--r--dx/tests/105-verify-load-store-ops/op_astore_1.j25
-rw-r--r--dx/tests/105-verify-load-store-ops/op_astore_2.j25
-rw-r--r--dx/tests/105-verify-load-store-ops/op_astore_3.j25
-rw-r--r--dx/tests/105-verify-load-store-ops/op_baload.j26
-rw-r--r--dx/tests/105-verify-load-store-ops/op_bastore.j27
-rw-r--r--dx/tests/105-verify-load-store-ops/op_caload.j26
-rw-r--r--dx/tests/105-verify-load-store-ops/op_castore.j27
-rw-r--r--dx/tests/105-verify-load-store-ops/op_daload.j26
-rw-r--r--dx/tests/105-verify-load-store-ops/op_dastore.j27
-rw-r--r--dx/tests/105-verify-load-store-ops/op_dstore.j25
-rw-r--r--dx/tests/105-verify-load-store-ops/op_dstore_0.j25
-rw-r--r--dx/tests/105-verify-load-store-ops/op_dstore_1.j25
-rw-r--r--dx/tests/105-verify-load-store-ops/op_dstore_2.j25
-rw-r--r--dx/tests/105-verify-load-store-ops/op_dstore_3.j25
-rw-r--r--dx/tests/105-verify-load-store-ops/op_faload.j26
-rw-r--r--dx/tests/105-verify-load-store-ops/op_fastore.j27
-rw-r--r--dx/tests/105-verify-load-store-ops/op_fstore.j25
-rw-r--r--dx/tests/105-verify-load-store-ops/op_fstore_0.j25
-rw-r--r--dx/tests/105-verify-load-store-ops/op_fstore_1.j25
-rw-r--r--dx/tests/105-verify-load-store-ops/op_fstore_2.j25
-rw-r--r--dx/tests/105-verify-load-store-ops/op_fstore_3.j25
-rw-r--r--dx/tests/105-verify-load-store-ops/op_iaload.j26
-rw-r--r--dx/tests/105-verify-load-store-ops/op_iastore.j27
-rw-r--r--dx/tests/105-verify-load-store-ops/op_istore.j25
-rw-r--r--dx/tests/105-verify-load-store-ops/op_istore_0.j25
-rw-r--r--dx/tests/105-verify-load-store-ops/op_istore_1.j25
-rw-r--r--dx/tests/105-verify-load-store-ops/op_istore_2.j25
-rw-r--r--dx/tests/105-verify-load-store-ops/op_istore_3.j25
-rw-r--r--dx/tests/105-verify-load-store-ops/op_laload.j26
-rw-r--r--dx/tests/105-verify-load-store-ops/op_lastore.j27
-rw-r--r--dx/tests/105-verify-load-store-ops/op_lstore.j25
-rw-r--r--dx/tests/105-verify-load-store-ops/op_lstore_0.j25
-rw-r--r--dx/tests/105-verify-load-store-ops/op_lstore_1.j25
-rw-r--r--dx/tests/105-verify-load-store-ops/op_lstore_2.j25
-rw-r--r--dx/tests/105-verify-load-store-ops/op_lstore_3.j25
-rw-r--r--dx/tests/105-verify-load-store-ops/op_saload.j26
-rw-r--r--dx/tests/105-verify-load-store-ops/op_sastore.j27
-rw-r--r--dx/tests/105-verify-load-store-ops/run68
-rw-r--r--dx/tests/106-verify-object-ops/expected.txt32
-rw-r--r--dx/tests/106-verify-object-ops/info.txt2
-rw-r--r--dx/tests/106-verify-object-ops/op_anewarray.j25
-rw-r--r--dx/tests/106-verify-object-ops/op_arraylength.j25
-rw-r--r--dx/tests/106-verify-object-ops/op_athrow.j25
-rw-r--r--dx/tests/106-verify-object-ops/op_checkcast.j25
-rw-r--r--dx/tests/106-verify-object-ops/op_getfield.j25
-rw-r--r--dx/tests/106-verify-object-ops/op_instanceof.j25
-rw-r--r--dx/tests/106-verify-object-ops/op_invokeinterface.j25
-rw-r--r--dx/tests/106-verify-object-ops/op_invokespecial.j25
-rw-r--r--dx/tests/106-verify-object-ops/op_invokestatic.j25
-rw-r--r--dx/tests/106-verify-object-ops/op_invokevirtual.j25
-rw-r--r--dx/tests/106-verify-object-ops/op_monitorenter.j25
-rw-r--r--dx/tests/106-verify-object-ops/op_monitorexit.j25
-rw-r--r--dx/tests/106-verify-object-ops/op_multianewarray.j26
-rw-r--r--dx/tests/106-verify-object-ops/op_newarray.j25
-rw-r--r--dx/tests/106-verify-object-ops/op_putfield.j26
-rw-r--r--dx/tests/106-verify-object-ops/op_putstatic.j25
-rw-r--r--dx/tests/106-verify-object-ops/run43
-rw-r--r--dx/tests/107-verify-stack-ops/expected.txt34
-rw-r--r--dx/tests/107-verify-stack-ops/info.txt2
-rw-r--r--dx/tests/107-verify-stack-ops/op_dup.j25
-rw-r--r--dx/tests/107-verify-stack-ops/op_dup2.j26
-rw-r--r--dx/tests/107-verify-stack-ops/op_dup2_case1.j26
-rw-r--r--dx/tests/107-verify-stack-ops/op_dup2_x1_case1.j27
-rw-r--r--dx/tests/107-verify-stack-ops/op_dup2_x1_case2.j27
-rw-r--r--dx/tests/107-verify-stack-ops/op_dup2_x1_case3.j27
-rw-r--r--dx/tests/107-verify-stack-ops/op_dup2_x2_case1.j28
-rw-r--r--dx/tests/107-verify-stack-ops/op_dup2_x2_case2.j28
-rw-r--r--dx/tests/107-verify-stack-ops/op_dup2_x2_case3.j27
-rw-r--r--dx/tests/107-verify-stack-ops/op_dup_x1_case1.j26
-rw-r--r--dx/tests/107-verify-stack-ops/op_dup_x1_case2.j26
-rw-r--r--dx/tests/107-verify-stack-ops/op_dup_x2_case1.j27
-rw-r--r--dx/tests/107-verify-stack-ops/op_dup_x2_case2.j27
-rw-r--r--dx/tests/107-verify-stack-ops/op_dup_x2_case3.j27
-rw-r--r--dx/tests/107-verify-stack-ops/op_pop.j25
-rw-r--r--dx/tests/107-verify-stack-ops/op_pop2.j26
-rw-r--r--dx/tests/107-verify-stack-ops/op_pop2_case2.j26
-rw-r--r--dx/tests/107-verify-stack-ops/op_swap_case1.j26
-rw-r--r--dx/tests/107-verify-stack-ops/op_swap_case2.j26
-rw-r--r--dx/tests/107-verify-stack-ops/run44
-rw-r--r--dx/tests/108-string-annotation/Blort.java32
-rw-r--r--dx/tests/108-string-annotation/Fizmo.java19
-rw-r--r--dx/tests/108-string-annotation/Frotz.java19
-rw-r--r--dx/tests/108-string-annotation/expected.txt12
-rw-r--r--dx/tests/108-string-annotation/info.txt6
-rw-r--r--dx/tests/108-string-annotation/run47
-rw-r--r--dx/tests/109-int-branch/blort.j99
-rw-r--r--dx/tests/109-int-branch/expected.txt67
-rw-r--r--dx/tests/109-int-branch/info.txt6
-rw-r--r--dx/tests/109-int-branch/run18
-rw-r--r--dx/tests/110-dex-preserve-this/Blort.java26
-rw-r--r--dx/tests/110-dex-preserve-this/expected.txt1
-rw-r--r--dx/tests/110-dex-preserve-this/info.txt10
-rw-r--r--dx/tests/110-dex-preserve-this/run41
-rw-r--r--dx/tests/111-use-null-as-array/Blort.java107
-rw-r--r--dx/tests/111-use-null-as-array/expected.txt116
-rw-r--r--dx/tests/111-use-null-as-array/info.txt18
-rw-r--r--dx/tests/111-use-null-as-array/run19
-rw-r--r--dx/tests/112-dex-return-jsr-result/blort.j41
-rw-r--r--dx/tests/112-dex-return-jsr-result/expected.txt2
-rw-r--r--dx/tests/112-dex-return-jsr-result/info.txt6
-rw-r--r--dx/tests/112-dex-return-jsr-result/run63
-rw-r--r--dx/tests/113-old-style-inner-class/Blort.java27
-rw-r--r--dx/tests/113-old-style-inner-class/expected.txt2
-rw-r--r--dx/tests/113-old-style-inner-class/info.txt3
-rw-r--r--dx/tests/113-old-style-inner-class/run22
-rwxr-xr-xdx/tests/run-all-tests57
-rwxr-xr-xdx/tests/run-test149
-rw-r--r--hit/samples/android.hprofbin0 -> 8569517 bytes
-rw-r--r--hit/src/com/android/hit/ArrayInstance.java188
-rw-r--r--hit/src/com/android/hit/ClassInstance.java208
-rw-r--r--hit/src/com/android/hit/ClassObj.java241
-rw-r--r--hit/src/com/android/hit/Heap.java185
-rw-r--r--hit/src/com/android/hit/HprofParser.java611
-rw-r--r--hit/src/com/android/hit/Instance.java117
-rw-r--r--hit/src/com/android/hit/Main.java97
-rw-r--r--hit/src/com/android/hit/Queries.java239
-rw-r--r--hit/src/com/android/hit/RootObj.java111
-rw-r--r--hit/src/com/android/hit/RootType.java53
-rw-r--r--hit/src/com/android/hit/StackFrame.java60
-rw-r--r--hit/src/com/android/hit/StackTrace.java64
-rw-r--r--hit/src/com/android/hit/State.java179
-rw-r--r--hit/src/com/android/hit/ThreadObj.java27
-rw-r--r--hit/src/com/android/hit/Types.java89
-rwxr-xr-xhit/test/testparser8
-rw-r--r--libdex/Android.mk68
-rw-r--r--libdex/CmdUtils.c217
-rw-r--r--libdex/CmdUtils.h73
-rw-r--r--libdex/DexCatch.c90
-rw-r--r--libdex/DexCatch.h162
-rw-r--r--libdex/DexClass.c192
-rw-r--r--libdex/DexClass.h162
-rw-r--r--libdex/DexDataMap.c141
-rw-r--r--libdex/DexDataMap.h73
-rw-r--r--libdex/DexFile.c1032
-rw-r--r--libdex/DexFile.h1056
-rw-r--r--libdex/DexInlines.c30
-rw-r--r--libdex/DexOptData.c128
-rw-r--r--libdex/DexOptData.h42
-rw-r--r--libdex/DexProto.c533
-rw-r--r--libdex/DexProto.h216
-rw-r--r--libdex/DexSwapVerify.c2944
-rw-r--r--libdex/InstrUtils.c1261
-rw-r--r--libdex/InstrUtils.h186
-rw-r--r--libdex/Leb128.c65
-rw-r--r--libdex/Leb128.h164
-rw-r--r--libdex/OpCode.h664
-rw-r--r--libdex/OpCodeNames.c333
-rw-r--r--libdex/OpCodeNames.h27
-rw-r--r--libdex/OptInvocation.c142
-rw-r--r--libdex/OptInvocation.h38
-rw-r--r--libdex/SysUtil.c408
-rw-r--r--libdex/SysUtil.h116
-rw-r--r--libdex/ZipArchive.c750
-rw-r--r--libdex/ZipArchive.h182
-rw-r--r--libdex/sha1.c514
-rw-r--r--libdex/sha1.h20
-rw-r--r--libnativehelper/Android.mk88
-rw-r--r--libnativehelper/JNIHelp.c339
-rw-r--r--libnativehelper/MODULE_LICENSE_APACHE20
-rw-r--r--libnativehelper/NOTICE190
-rw-r--r--libnativehelper/README11
-rw-r--r--libnativehelper/Register.c33
-rw-r--r--libnativehelper/include/nativehelper/JNIHelp.h181
-rw-r--r--libnativehelper/include/nativehelper/jni.h1155
-rw-r--r--tests/001-nop/build3
-rw-r--r--tests/001-nop/expected.txt1
-rw-r--r--tests/001-nop/info.txt2
-rw-r--r--tests/001-nop/run3
-rw-r--r--tests/002-sleep/expected.txt2
-rw-r--r--tests/002-sleep/info.txt3
-rw-r--r--tests/002-sleep/src/Main.java22
-rw-r--r--tests/003-omnibus-opcodes/build26
-rw-r--r--tests/003-omnibus-opcodes/expected.txt74
-rw-r--r--tests/003-omnibus-opcodes/info.txt1
-rw-r--r--tests/003-omnibus-opcodes/src/Array.java224
-rw-r--r--tests/003-omnibus-opcodes/src/Classes.java219
-rw-r--r--tests/003-omnibus-opcodes/src/Compare.java171
-rw-r--r--tests/003-omnibus-opcodes/src/FloatMath.java337
-rw-r--r--tests/003-omnibus-opcodes/src/Goto.java2408
-rw-r--r--tests/003-omnibus-opcodes/src/InstField.java108
-rw-r--r--tests/003-omnibus-opcodes/src/IntMath.java492
-rw-r--r--tests/003-omnibus-opcodes/src/InternedString.java56
-rw-r--r--tests/003-omnibus-opcodes/src/Main.java82
-rw-r--r--tests/003-omnibus-opcodes/src/MethodCall.java82
-rw-r--r--tests/003-omnibus-opcodes/src/Monitor.java44
-rw-r--r--tests/003-omnibus-opcodes/src/StaticField.java76
-rw-r--r--tests/003-omnibus-opcodes/src/Switch.java62
-rw-r--r--tests/003-omnibus-opcodes/src/Throw.java124
-rw-r--r--tests/003-omnibus-opcodes/src/UnresClass.java9
-rw-r--r--tests/003-omnibus-opcodes/src/UnresStuff.java22
-rw-r--r--tests/003-omnibus-opcodes/src/UnresTest1.java80
-rw-r--r--tests/003-omnibus-opcodes/src/UnresTest2.java49
-rw-r--r--tests/003-omnibus-opcodes/src2/UnresStuff.java9
-rw-r--r--tests/004-annotations/expected.txt96
-rw-r--r--tests/004-annotations/info.txt1
-rw-r--r--tests/004-annotations/src/Main.java7
-rw-r--r--tests/004-annotations/src/android/test/anno/AnnoArrayField.java19
-rw-r--r--tests/004-annotations/src/android/test/anno/AnnoFancyConstructor.java10
-rw-r--r--tests/004-annotations/src/android/test/anno/AnnoFancyField.java12
-rw-r--r--tests/004-annotations/src/android/test/anno/AnnoFancyMethod.java14
-rw-r--r--tests/004-annotations/src/android/test/anno/AnnoFancyParameter.java10
-rw-r--r--tests/004-annotations/src/android/test/anno/AnnoFancyType.java11
-rw-r--r--tests/004-annotations/src/android/test/anno/AnnoSimpleConstructor.java8
-rw-r--r--tests/004-annotations/src/android/test/anno/AnnoSimpleField.java8
-rw-r--r--tests/004-annotations/src/android/test/anno/AnnoSimpleLocalVariable.java8
-rw-r--r--tests/004-annotations/src/android/test/anno/AnnoSimpleMethod.java8
-rw-r--r--tests/004-annotations/src/android/test/anno/AnnoSimplePackage.java8
-rw-r--r--tests/004-annotations/src/android/test/anno/AnnoSimpleParameter.java8
-rw-r--r--tests/004-annotations/src/android/test/anno/AnnoSimpleType.java9
-rw-r--r--tests/004-annotations/src/android/test/anno/AnnoSimpleType2.java8
-rw-r--r--tests/004-annotations/src/android/test/anno/AnnoSimpleTypeInvis.java8
-rw-r--r--tests/004-annotations/src/android/test/anno/ExportedProperty.java12
-rw-r--r--tests/004-annotations/src/android/test/anno/FullyNoted.java39
-rw-r--r--tests/004-annotations/src/android/test/anno/INoted.java7
-rw-r--r--tests/004-annotations/src/android/test/anno/IntToString.java12
-rw-r--r--tests/004-annotations/src/android/test/anno/SimplyNoted.java30
-rw-r--r--tests/004-annotations/src/android/test/anno/SomeClass.java4
-rw-r--r--tests/004-annotations/src/android/test/anno/SubNoted.java12
-rw-r--r--tests/004-annotations/src/android/test/anno/TestAnnotations.java177
-rw-r--r--tests/004-annotations/src/android/test/anno/package-info.java2
-rw-r--r--tests/005-args/expected.txt5
-rw-r--r--tests/005-args/info.txt6
-rw-r--r--tests/005-args/src/ArgsTest.java44
-rw-r--r--tests/005-args/src/Main.java25
-rw-r--r--tests/006-count10/expected.txt10
-rw-r--r--tests/006-count10/info.txt6
-rw-r--r--tests/006-count10/src/Main.java26
-rw-r--r--tests/007-exceptions/expected.txt9
-rw-r--r--tests/007-exceptions/info.txt6
-rw-r--r--tests/007-exceptions/src/Main.java45
-rw-r--r--tests/008-instanceof/expected.txt6
-rw-r--r--tests/008-instanceof/info.txt6
-rw-r--r--tests/008-instanceof/src/Iface1.java13
-rw-r--r--tests/008-instanceof/src/Iface2.java9
-rw-r--r--tests/008-instanceof/src/Iface2Sub1.java9
-rw-r--r--tests/008-instanceof/src/ImplA.java14
-rw-r--r--tests/008-instanceof/src/ImplB.java16
-rw-r--r--tests/008-instanceof/src/ImplBSub.java14
-rw-r--r--tests/008-instanceof/src/Main.java49
-rw-r--r--tests/009-instanceof2/expected.txt5
-rw-r--r--tests/009-instanceof2/info.txt6
-rw-r--r--tests/009-instanceof2/src/Iface1.java13
-rw-r--r--tests/009-instanceof2/src/Iface2.java9
-rw-r--r--tests/009-instanceof2/src/Iface2Sub1.java9
-rw-r--r--tests/009-instanceof2/src/ImplA.java14
-rw-r--r--tests/009-instanceof2/src/ImplB.java16
-rw-r--r--tests/009-instanceof2/src/ImplBSub.java14
-rw-r--r--tests/009-instanceof2/src/Main.java44
-rw-r--r--tests/010-instance/expected.txt30
-rw-r--r--tests/010-instance/info.txt6
-rw-r--r--tests/010-instance/src/InstanceTest.java93
-rw-r--r--tests/010-instance/src/Main.java24
-rw-r--r--tests/010-instance/src/X.java8
-rw-r--r--tests/010-instance/src/Y.java8
-rw-r--r--tests/011-array-copy/expected.txt4
-rw-r--r--tests/011-array-copy/info.txt6
-rw-r--r--tests/011-array-copy/src/Iface1.java13
-rw-r--r--tests/011-array-copy/src/Iface2.java9
-rw-r--r--tests/011-array-copy/src/ImplA.java14
-rw-r--r--tests/011-array-copy/src/Main.java41
-rw-r--r--tests/012-math/expected.txt32
-rw-r--r--tests/012-math/info.txt6
-rw-r--r--tests/012-math/src/Main.java102
-rw-r--r--tests/013-math2/expected.txt1
-rw-r--r--tests/013-math2/info.txt6
-rw-r--r--tests/013-math2/src/Main.java31
-rw-r--r--tests/014-math3/expected.txt1
-rw-r--r--tests/014-math3/info.txt6
-rw-r--r--tests/014-math3/src/Main.java57
-rw-r--r--tests/015-switch/expected.txt10
-rw-r--r--tests/015-switch/info.txt6
-rw-r--r--tests/015-switch/src/Main.java105
-rw-r--r--tests/016-intern/expected.txt1
-rw-r--r--tests/016-intern/info.txt6
-rw-r--r--tests/016-intern/src/Main.java34
-rw-r--r--tests/017-float/expected.txt3
-rw-r--r--tests/017-float/info.txt6
-rw-r--r--tests/017-float/src/Main.java33
-rw-r--r--tests/018-stack-overflow/expected.txt2
-rw-r--r--tests/018-stack-overflow/info.txt6
-rw-r--r--tests/018-stack-overflow/src/Main.java35
-rw-r--r--tests/019-wrong-array-type/expected.txt1
-rw-r--r--tests/019-wrong-array-type/info.txt6
-rw-r--r--tests/019-wrong-array-type/src/Main.java33
-rw-r--r--tests/020-string/expected.txt7
-rw-r--r--tests/020-string/info.txt6
-rw-r--r--tests/020-string/src/Main.java84
-rw-r--r--tests/021-string2/expected.txt1
-rw-r--r--tests/021-string2/info.txt6
-rw-r--r--tests/021-string2/src/Main.java83
-rw-r--r--tests/021-string2/src/junit/framework/Assert.java291
-rw-r--r--tests/021-string2/src/junit/framework/AssertionFailedError.java13
-rw-r--r--tests/021-string2/src/junit/framework/ComparisonFailure.java68
-rw-r--r--tests/022-interface/expected.txt2
-rw-r--r--tests/022-interface/info.txt6
-rw-r--r--tests/022-interface/src/Iface1.java13
-rw-r--r--tests/022-interface/src/Iface2.java9
-rw-r--r--tests/022-interface/src/Iface2Sub1.java9
-rw-r--r--tests/022-interface/src/ImplA.java14
-rw-r--r--tests/022-interface/src/ImplB.java16
-rw-r--r--tests/022-interface/src/ImplBSub.java14
-rw-r--r--tests/022-interface/src/Main.java37
-rw-r--r--tests/023-many-interfaces/build28
-rw-r--r--tests/023-many-interfaces/expected.txt9
-rw-r--r--tests/023-many-interfaces/iface-gen.c54
-rw-r--r--tests/023-many-interfaces/info.txt6
-rw-r--r--tests/023-many-interfaces/src/Main.java6
-rw-r--r--tests/023-many-interfaces/src/ManyInterfaces.java413
-rw-r--r--tests/024-illegal-access/expected.txt2
-rw-r--r--tests/024-illegal-access/info.txt3
-rw-r--r--tests/024-illegal-access/src/CheckInstanceof.java27
-rw-r--r--tests/024-illegal-access/src/Main.java41
-rw-r--r--tests/024-illegal-access/src/PublicAccess.java11
-rw-r--r--tests/024-illegal-access/src/SemiPrivate.java8
-rw-r--r--tests/024-illegal-access/src/otherpkg/Package.java23
-rw-r--r--tests/024-illegal-access/src2/SemiPrivate.java8
-rw-r--r--tests/024-illegal-access/src2/otherpkg/Package.java23
-rw-r--r--tests/025-access-controller/expected.txt1
-rw-r--r--tests/025-access-controller/info.txt6
-rw-r--r--tests/025-access-controller/src/Main.java14
-rw-r--r--tests/025-access-controller/src/Privvy.java18
-rw-r--r--tests/026-access/expected.txt2
-rw-r--r--tests/026-access/info.txt6
-rw-r--r--tests/026-access/src/Main.java12
-rw-r--r--tests/026-access/src/otherpackage/PublicAccess.java7
-rw-r--r--tests/027-arithmetic/expected.txt18
-rw-r--r--tests/027-arithmetic/info.txt6
-rw-r--r--tests/027-arithmetic/src/Main.java141
-rw-r--r--tests/028-array-write/expected.txt3
-rw-r--r--tests/028-array-write/info.txt6
-rw-r--r--tests/028-array-write/src/Main.java68
-rw-r--r--tests/029-assert/expected.txt1
-rw-r--r--tests/029-assert/info.txt6
-rw-r--r--tests/029-assert/src/Main.java16
-rw-r--r--tests/030-bad-finalizer/expected.txt8
-rw-r--r--tests/030-bad-finalizer/info.txt3
-rw-r--r--tests/030-bad-finalizer/run27
-rw-r--r--tests/030-bad-finalizer/src/BadFinalizer.java32
-rw-r--r--tests/030-bad-finalizer/src/Main.java25
-rw-r--r--tests/031-class-attributes/expected.txt164
-rw-r--r--tests/031-class-attributes/info.txt6
-rw-r--r--tests/031-class-attributes/src/ClassAttrs.java201
-rw-r--r--tests/031-class-attributes/src/Main.java5
-rw-r--r--tests/031-class-attributes/src/OtherClass.java2
-rw-r--r--tests/031-class-attributes/src/otherpackage/OtherPackageClass.java4
-rw-r--r--tests/032-concrete-sub/expected.txt6
-rw-r--r--tests/032-concrete-sub/info.txt3
-rw-r--r--tests/032-concrete-sub/src/AbstractBase.java26
-rw-r--r--tests/032-concrete-sub/src/ConcreteSub.java53
-rw-r--r--tests/032-concrete-sub/src/ConcreteSub2.java26
-rw-r--r--tests/032-concrete-sub/src/Main.java36
-rw-r--r--tests/032-concrete-sub/src2/AbstractBase.java29
-rw-r--r--tests/033-class-init-deadlock/expected.txt7
-rw-r--r--tests/033-class-init-deadlock/info.txt6
-rw-r--r--tests/033-class-init-deadlock/src/Main.java51
-rw-r--r--tests/034-call-null/expected.txt3
-rw-r--r--tests/034-call-null/info.txt6
-rw-r--r--tests/034-call-null/src/Main.java14
-rw-r--r--tests/035-enum/expected.txt3
-rw-r--r--tests/035-enum/info.txt6
-rw-r--r--tests/035-enum/src/Main.java23
-rw-r--r--tests/036-finalizer/expected.txt14
-rw-r--r--tests/036-finalizer/info.txt6
-rw-r--r--tests/036-finalizer/src/FinalizerTest.java23
-rw-r--r--tests/036-finalizer/src/Main.java107
-rw-r--r--tests/037-inherit/expected.txt3
-rw-r--r--tests/037-inherit/info.txt6
-rw-r--r--tests/037-inherit/src/Main.java37
-rw-r--r--tests/038-inner-null/expected.txt5
-rw-r--r--tests/038-inner-null/info.txt6
-rw-r--r--tests/038-inner-null/src/Main.java27
-rw-r--r--tests/039-join-main/expected.txt5
-rw-r--r--tests/039-join-main/info.txt6
-rw-r--r--tests/039-join-main/src/Main.java41
-rw-r--r--tests/040-miranda/expected.txt12
-rw-r--r--tests/040-miranda/info.txt6
-rw-r--r--tests/040-miranda/src/Main.java27
-rw-r--r--tests/040-miranda/src/MirandaAbstract.java16
-rw-r--r--tests/040-miranda/src/MirandaClass.java24
-rw-r--r--tests/040-miranda/src/MirandaClass2.java9
-rw-r--r--tests/040-miranda/src/MirandaInterface.java10
-rw-r--r--tests/040-miranda/src/MirandaInterface2.java12
-rw-r--r--tests/041-narrowing/expected.txt38
-rw-r--r--tests/041-narrowing/info.txt6
-rw-r--r--tests/041-narrowing/src/Main.java99
-rw-r--r--tests/042-new-instance/expected.txt8
-rw-r--r--tests/042-new-instance/info.txt2
-rw-r--r--tests/042-new-instance/src/Main.java156
-rw-r--r--tests/042-new-instance/src/MaybeAbstract.java20
-rw-r--r--tests/042-new-instance/src/otherpackage/PackageAccess.java6
-rw-r--r--tests/042-new-instance/src2/MaybeAbstract.java20
-rw-r--r--tests/043-privates/expected.txt6
-rw-r--r--tests/043-privates/info.txt6
-rw-r--r--tests/043-privates/src/Main.java45
-rw-r--r--tests/044-proxy/expected.txt80
-rw-r--r--tests/044-proxy/info.txt6
-rw-r--r--tests/044-proxy/src/BasicTest.java263
-rw-r--r--tests/044-proxy/src/Clash.java70
-rw-r--r--tests/044-proxy/src/Clash2.java60
-rw-r--r--tests/044-proxy/src/Clash3.java75
-rw-r--r--tests/044-proxy/src/Clash4.java77
-rw-r--r--tests/044-proxy/src/Main.java29
-rw-r--r--tests/044-proxy/src/WrappedThrow.java244
-rw-r--r--tests/045-reflect-array/expected.txt6
-rw-r--r--tests/045-reflect-array/info.txt6
-rw-r--r--tests/045-reflect-array/src/Main.java147
-rw-r--r--tests/046-reflect/expected.txt97
-rw-r--r--tests/046-reflect/info.txt6
-rw-r--r--tests/046-reflect/src/Main.java435
-rw-r--r--tests/047-returns/expected.txt10
-rw-r--r--tests/047-returns/info.txt6
-rw-r--r--tests/047-returns/src/Main.java65
-rw-r--r--tests/048-server-socket/expected.txt4
-rw-r--r--tests/048-server-socket/info.txt6
-rw-r--r--tests/048-server-socket/src/Main.java52
-rw-r--r--tests/049-show-object/expected.txt11
-rw-r--r--tests/049-show-object/info.txt6
-rw-r--r--tests/049-show-object/src/Main.java34
-rw-r--r--tests/050-sync-test/expected.txt34
-rw-r--r--tests/050-sync-test/info.txt6
-rw-r--r--tests/050-sync-test/src/Main.java179
-rw-r--r--tests/050-sync-test/src/ThreadDeathHandler.java19
-rw-r--r--tests/051-thread/expected.txt518
-rw-r--r--tests/051-thread/info.txt6
-rw-r--r--tests/051-thread/src/Main.java73
-rw-r--r--tests/052-verifier-fun/expected.txt2
-rw-r--r--tests/052-verifier-fun/info.txt6
-rw-r--r--tests/052-verifier-fun/src/Blah.java4
-rw-r--r--tests/052-verifier-fun/src/BlahFeature.java3
-rw-r--r--tests/052-verifier-fun/src/BlahOne.java5
-rw-r--r--tests/052-verifier-fun/src/BlahTwo.java5
-rw-r--r--tests/052-verifier-fun/src/Main.java109
-rw-r--r--tests/053-wait-some/expected.txt7
-rw-r--r--tests/053-wait-some/info.txt6
-rw-r--r--tests/053-wait-some/src/Main.java71
-rw-r--r--tests/054-uncaught/expected.txt21
-rw-r--r--tests/054-uncaught/info.txt6
-rw-r--r--tests/054-uncaught/src/Main.java63
-rw-r--r--tests/054-uncaught/src/ThreadDeathHandler.java19
-rw-r--r--tests/055-enum-performance/expected.txt12
-rw-r--r--tests/055-enum-performance/info.txt2
-rw-r--r--tests/055-enum-performance/src/Main.java199
-rw-r--r--tests/055-enum-performance/src/SamePackagePrivateEnum.java5
-rw-r--r--tests/055-enum-performance/src/SamePackagePublicEnum.java5
-rw-r--r--tests/055-enum-performance/src/otherpackage/OtherPackagePublicEnum.java7
-rw-r--r--tests/056-const-string-jumbo/build47
-rw-r--r--tests/056-const-string-jumbo/expected.txt1
-rw-r--r--tests/056-const-string-jumbo/info.txt1
-rw-r--r--tests/056-const-string-jumbo/src/Main.java21
-rw-r--r--tests/057-iteration-performance/expected.txt11
-rw-r--r--tests/057-iteration-performance/info.txt2
-rw-r--r--tests/057-iteration-performance/src/Main.java1108
-rw-r--r--tests/058-enum-order/expected.txt5
-rw-r--r--tests/058-enum-order/info.txt1
-rw-r--r--tests/058-enum-order/src/Main.java31
-rw-r--r--tests/059-finalizer-throw/expected.txt2
-rw-r--r--tests/059-finalizer-throw/info.txt1
-rw-r--r--tests/059-finalizer-throw/src/Main.java56
-rw-r--r--tests/061-out-of-memory/expected.txt6
-rw-r--r--tests/061-out-of-memory/info.txt1
-rw-r--r--tests/061-out-of-memory/src/Main.java100
-rw-r--r--tests/062-character-encodings/expected.txt1
-rw-r--r--tests/062-character-encodings/info.txt1
-rw-r--r--tests/062-character-encodings/src/Main.java25
-rw-r--r--tests/063-process-manager/expected.txt15
-rw-r--r--tests/063-process-manager/info.txt2
-rw-r--r--tests/063-process-manager/src/Main.java43
-rw-r--r--tests/064-field-access/expected.txt2
-rw-r--r--tests/064-field-access/info.txt10
-rw-r--r--tests/064-field-access/src/GetNonexistent.java21
-rw-r--r--tests/064-field-access/src/Holder.java19
-rw-r--r--tests/064-field-access/src/Main.java345
-rw-r--r--tests/064-field-access/src/other/OtherPackage.java15
-rw-r--r--tests/064-field-access/src2/Holder.java19
-rw-r--r--tests/065-mismatched-implements/expected.txt1
-rw-r--r--tests/065-mismatched-implements/info.txt2
-rw-r--r--tests/065-mismatched-implements/src/Base.java7
-rw-r--r--tests/065-mismatched-implements/src/Defs.java7
-rw-r--r--tests/065-mismatched-implements/src/Indirect.java27
-rw-r--r--tests/065-mismatched-implements/src/Main.java29
-rw-r--r--tests/065-mismatched-implements/src2/Defs.java11
-rw-r--r--tests/066-mismatched-super/expected.txt1
-rw-r--r--tests/066-mismatched-super/info.txt2
-rw-r--r--tests/066-mismatched-super/src/Base.java5
-rw-r--r--tests/066-mismatched-super/src/Defs.java11
-rw-r--r--tests/066-mismatched-super/src/Indirect.java27
-rw-r--r--tests/066-mismatched-super/src/Main.java29
-rw-r--r--tests/066-mismatched-super/src2/Defs.java7
-rw-r--r--tests/067-preemptive-unpark/expected.txt5
-rw-r--r--tests/067-preemptive-unpark/info.txt1
-rw-r--r--tests/067-preemptive-unpark/src/Main.java107
-rw-r--r--tests/068-classloader/expected.txt13
-rw-r--r--tests/068-classloader/info.txt8
-rw-r--r--tests/068-classloader/src-ex/AbstractGet.java32
-rw-r--r--tests/068-classloader/src-ex/DoubledExtend.java20
-rw-r--r--tests/068-classloader/src-ex/DoubledExtendOkay.java36
-rw-r--r--tests/068-classloader/src-ex/DoubledImplement.java18
-rw-r--r--tests/068-classloader/src-ex/DoubledImplement2.java32
-rw-r--r--tests/068-classloader/src-ex/GetDoubled.java26
-rw-r--r--tests/068-classloader/src-ex/IfaceImpl.java21
-rw-r--r--tests/068-classloader/src-ex/IfaceSub.java19
-rw-r--r--tests/068-classloader/src-ex/Inaccessible1.java11
-rw-r--r--tests/068-classloader/src-ex/Inaccessible2.java10
-rw-r--r--tests/068-classloader/src-ex/Inaccessible3.java10
-rw-r--r--tests/068-classloader/src/Base.java16
-rw-r--r--tests/068-classloader/src/BaseOkay.java38
-rw-r--r--tests/068-classloader/src/DoubledExtend.java20
-rw-r--r--tests/068-classloader/src/DoubledExtendOkay.java36
-rw-r--r--tests/068-classloader/src/DoubledImplement.java18
-rw-r--r--tests/068-classloader/src/DoubledImplement2.java32
-rw-r--r--tests/068-classloader/src/FancyLoader.java228
-rw-r--r--tests/068-classloader/src/ICommon.java8
-rw-r--r--tests/068-classloader/src/ICommon2.java22
-rw-r--r--tests/068-classloader/src/IGetDoubled.java22
-rw-r--r--tests/068-classloader/src/IfaceSuper.java19
-rw-r--r--tests/068-classloader/src/InaccessibleBase.java7
-rw-r--r--tests/068-classloader/src/InaccessibleInterface.java7
-rw-r--r--tests/068-classloader/src/Main.java425
-rw-r--r--tests/068-classloader/src/SimpleBase.java8
-rw-r--r--tests/068-classloader/src/Useless.java4
-rw-r--r--tests/069-field-type/expected.txt4
-rw-r--r--tests/069-field-type/info.txt4
-rw-r--r--tests/069-field-type/src/Blah.java9
-rw-r--r--tests/069-field-type/src/Holder.java7
-rw-r--r--tests/069-field-type/src/Main.java34
-rw-r--r--tests/069-field-type/src2/Blah.java10
-rw-r--r--tests/070-nio-buffer/expected.txt3
-rw-r--r--tests/070-nio-buffer/info.txt1
-rw-r--r--tests/070-nio-buffer/src/Main.java97
-rw-r--r--tests/071-dexfile/expected.txt3
-rw-r--r--tests/071-dexfile/info.txt4
-rw-r--r--tests/071-dexfile/src-ex/Another.java28
-rw-r--r--tests/071-dexfile/src/Main.java145
-rw-r--r--tests/072-precise-gc/expected.txt2
-rw-r--r--tests/072-precise-gc/info.txt1
-rw-r--r--tests/072-precise-gc/src/Main.java113
-rw-r--r--tests/073-mismatched-field/expected.txt1
-rw-r--r--tests/073-mismatched-field/info.txt3
-rw-r--r--tests/073-mismatched-field/src/IMain.java19
-rw-r--r--tests/073-mismatched-field/src/Main.java31
-rw-r--r--tests/073-mismatched-field/src/SuperMain.java19
-rw-r--r--tests/073-mismatched-field/src2/IMain.java19
-rw-r--r--tests/074-gc-thrash/expected.txt2
-rw-r--r--tests/074-gc-thrash/info.txt1
-rw-r--r--tests/074-gc-thrash/src/Main.java337
-rw-r--r--tests/075-verification-error/expected.txt12
-rw-r--r--tests/075-verification-error/info.txt1
-rw-r--r--tests/075-verification-error/src/Main.java148
-rw-r--r--tests/075-verification-error/src/MaybeAbstract.java20
-rw-r--r--tests/075-verification-error/src/other/InaccessibleClass.java23
-rw-r--r--tests/075-verification-error/src/other/InaccessibleMethod.java21
-rw-r--r--tests/075-verification-error/src/other/Mutant.java43
-rw-r--r--tests/075-verification-error/src2/MaybeAbstract.java20
-rw-r--r--tests/075-verification-error/src2/other/InaccessibleClass.java23
-rw-r--r--tests/075-verification-error/src2/other/InaccessibleMethod.java21
-rw-r--r--tests/075-verification-error/src2/other/Mutant.java43
-rw-r--r--tests/076-boolean-put/expected.txt1
-rw-r--r--tests/076-boolean-put/info.txt3
-rw-r--r--tests/076-boolean-put/src/Main.java48
-rw-r--r--tests/077-method-override/expected.txt15
-rw-r--r--tests/077-method-override/info.txt2
-rw-r--r--tests/077-method-override/src/Base.java83
-rw-r--r--tests/077-method-override/src/Derived.java59
-rw-r--r--tests/077-method-override/src/Main.java53
-rw-r--r--tests/077-method-override/src2/Base.java82
-rw-r--r--tests/078-polymorphic-virtual/expected.txt3
-rw-r--r--tests/078-polymorphic-virtual/info.txt2
-rw-r--r--tests/078-polymorphic-virtual/src/Base.java32
-rw-r--r--tests/078-polymorphic-virtual/src/Derived1.java21
-rw-r--r--tests/078-polymorphic-virtual/src/Derived2.java21
-rw-r--r--tests/078-polymorphic-virtual/src/Derived3.java21
-rw-r--r--tests/078-polymorphic-virtual/src/Main.java40
-rw-r--r--tests/079-phantom/expected.txt14
-rw-r--r--tests/079-phantom/info.txt1
-rw-r--r--tests/079-phantom/src/Bitmap.java152
-rw-r--r--tests/079-phantom/src/Main.java85
-rw-r--r--tests/080-oom-throw/expected.txt2
-rw-r--r--tests/080-oom-throw/info.txt3
-rw-r--r--tests/080-oom-throw/src/Main.java79
-rw-r--r--tests/081-hot-exceptions/expected.txt2
-rw-r--r--tests/081-hot-exceptions/info.txt3
-rw-r--r--tests/081-hot-exceptions/src/Main.java42
-rw-r--r--tests/082-inline-execute/expected.txt8
-rw-r--r--tests/082-inline-execute/info.txt8
-rw-r--r--tests/082-inline-execute/src/Main.java205
-rw-r--r--tests/082-inline-execute/src/junit/framework/Assert.java291
-rw-r--r--tests/082-inline-execute/src/junit/framework/AssertionFailedError.java13
-rw-r--r--tests/082-inline-execute/src/junit/framework/ComparisonFailure.java68
-rw-r--r--tests/083-jit-regressions/expected.txt3
-rw-r--r--tests/083-jit-regressions/info.txt10
-rw-r--r--tests/083-jit-regressions/src/Main.java122
-rw-r--r--tests/084-class-init/expected.txt8
-rw-r--r--tests/084-class-init/info.txt1
-rw-r--r--tests/084-class-init/src/IntHolder.java41
-rw-r--r--tests/084-class-init/src/Main.java93
-rw-r--r--tests/084-class-init/src/PartialInit.java24
-rw-r--r--tests/084-class-init/src/SlowInit.java40
-rw-r--r--tests/084-old-style-inner-class/build29
-rw-r--r--tests/084-old-style-inner-class/expected.txt8
-rw-r--r--tests/084-old-style-inner-class/info.txt2
-rw-r--r--tests/084-old-style-inner-class/src/Main.java55
-rw-r--r--tests/086-null-super/expected.txt1
-rw-r--r--tests/086-null-super/info.txt7
-rw-r--r--tests/086-null-super/src/Main.java165
-rw-r--r--tests/087-gc-after-link/expected.txt2
-rw-r--r--tests/087-gc-after-link/info.txt8
-rw-r--r--tests/087-gc-after-link/src/Main.java167
-rw-r--r--tests/README.txt13
-rwxr-xr-xtests/etc/default-build43
-rwxr-xr-xtests/etc/default-run17
-rwxr-xr-xtests/etc/local-run-test-jar153
-rwxr-xr-xtests/etc/push-and-run-test-jar130
-rwxr-xr-xtests/etc/reference-run-test-classes60
-rwxr-xr-xtests/run-all-tests124
-rwxr-xr-xtests/run-test254
-rw-r--r--tools/Android.mk1
-rwxr-xr-xtools/deadcode.py128
-rwxr-xr-xtools/dex-preopt320
-rwxr-xr-xtools/dexcheck67
-rw-r--r--tools/dexdeps/Android.mk45
-rw-r--r--tools/dexdeps/README.txt27
-rw-r--r--tools/dexdeps/etc/dexdeps69
-rw-r--r--tools/dexdeps/etc/manifest.txt1
-rw-r--r--tools/dexdeps/src/Android.mk31
-rw-r--r--tools/dexdeps/src/com/android/dexdeps/ClassRef.java69
-rw-r--r--tools/dexdeps/src/com/android/dexdeps/DexData.java593
-rw-r--r--tools/dexdeps/src/com/android/dexdeps/DexDataException.java23
-rw-r--r--tools/dexdeps/src/com/android/dexdeps/FieldRef.java51
-rw-r--r--tools/dexdeps/src/com/android/dexdeps/Main.java202
-rw-r--r--tools/dexdeps/src/com/android/dexdeps/MethodRef.java87
-rw-r--r--tools/dexdeps/src/com/android/dexdeps/Output.java300
-rw-r--r--tools/dexdeps/src/com/android/dexdeps/UsageException.java23
-rw-r--r--tools/dmtracedump/Android.mk24
-rw-r--r--tools/dmtracedump/CreateTestTrace.c493
-rw-r--r--tools/dmtracedump/TraceDump.c3631
-rwxr-xr-xtools/dmtracedump/dmtracedump.pl18
-rw-r--r--tools/dmtracedump/dumpdir.sh11
-rw-r--r--tools/dmtracedump/filters42
-rwxr-xr-xtools/dmtracedump/tests/filters/run_tests.sh36
-rw-r--r--tools/dmtracedump/tests/filters/testFilters9
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterDiffKeys19
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterDiffKeysExpected232
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterDiffKeysTracebin0 -> 270 bytes
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterSameKeys19
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterSameKeysExpected210
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterSameKeysTracebin0 -> 246 bytes
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterDiffKeys19
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterDiffKeysExpected232
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterDiffKeysTracebin0 -> 270 bytes
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterSameKeys19
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterSameKeysExpected203
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterSameKeysTracebin0 -> 246 bytes
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterDiffKeys17
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterDiffKeysExpected232
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterDiffKeysTracebin0 -> 290 bytes
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterSameKeys17
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterSameKeysExpected210
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterSameKeysTracebin0 -> 266 bytes
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterDiffKeys16
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterDiffKeysExpected216
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterDiffKeysTracebin0 -> 258 bytes
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterSameKeys17
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterSameKeysExpected203
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterSameKeysTracebin0 -> 266 bytes
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterDiffKeys19
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterDiffKeysExpected232
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterDiffKeysTracebin0 -> 270 bytes
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterSameKeys19
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterSameKeysExpected210
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterSameKeysTracebin0 -> 246 bytes
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterDiffKeys19
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterDiffKeysExpected232
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterDiffKeysTracebin0 -> 270 bytes
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterSameKeys19
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterSameKeysExpected203
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterSameKeysTracebin0 -> 246 bytes
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterDiffKeys13
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterDiffKeysExpected232
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterDiffKeysTracebin0 -> 250 bytes
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterSameKeys13
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterSameKeysExpected210
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterSameKeysTracebin0 -> 226 bytes
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterDiffKeys11
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterDiffKeysExpected214
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterDiffKeysTracebin0 -> 218 bytes
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterSameKeys13
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterSameKeysExpected203
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterSameKeysTracebin0 -> 226 bytes
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingPairCrossThread14
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingPairCrossThreadExpected203
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingPairCrossThreadTracebin0 -> 206 bytes
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingPairSingleThread11
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingPairSingleThreadExpected203
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingPairSingleThreadTracebin0 -> 206 bytes
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterDiffKeys23
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterDiffKeysExpected232
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterDiffKeysTracebin0 -> 310 bytes
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterSameKeys23
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterSameKeysExpected210
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterSameKeysTracebin0 -> 286 bytes
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterDiffKeys23
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterDiffKeysExpected232
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterDiffKeysTracebin0 -> 310 bytes
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterSameKeys23
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterSameKeysExpected203
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterSameKeysTracebin0 -> 286 bytes
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterDiffKeys13
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterDiffKeysExpected232
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterDiffKeysTracebin0 -> 250 bytes
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterSameKeys13
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterSameKeysExpected210
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterSameKeysTracebin0 -> 226 bytes
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterDiffKeys12
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterDiffKeysExpected214
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterDiffKeysTracebin0 -> 218 bytes
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterSameKeys13
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterSameKeysExpected203
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterSameKeysTracebin0 -> 226 bytes
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingSoloCrossThread12
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingSoloCrossThreadExpected192
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingSoloCrossThreadTracebin0 -> 175 bytes
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingSoloSingleThread10
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingSoloSingleThreadExpected194
-rw-r--r--tools/dmtracedump/tests/filters/testWaitingSoloSingleThreadTracebin0 -> 175 bytes
-rwxr-xr-xtools/gclog.py234
-rw-r--r--tools/gdbjithelper/Android.mk22
-rw-r--r--tools/gdbjithelper/README.txt65
-rw-r--r--tools/gdbjithelper/gdbjithelper.c108
-rwxr-xr-xtools/get-hprof41
-rw-r--r--tools/hprof-conv/Android.mk21
-rw-r--r--tools/hprof-conv/HprofConv.c718
-rw-r--r--vm/AllocTracker.c653
-rw-r--r--vm/AllocTracker.h63
-rw-r--r--vm/Android.mk137
-rw-r--r--vm/Atomic.c282
-rw-r--r--vm/Atomic.h55
-rw-r--r--vm/AtomicCache.c180
-rw-r--r--vm/AtomicCache.h173
-rw-r--r--vm/Bits.h358
-rw-r--r--vm/CheckJni.c2810
-rw-r--r--vm/Common.h155
-rw-r--r--vm/Dalvik.h89
-rw-r--r--vm/DalvikVersion.h37
-rw-r--r--vm/Ddm.c606
-rw-r--r--vm/Ddm.h87
-rw-r--r--vm/Debugger.c3071
-rw-r--r--vm/Debugger.h311
-rw-r--r--vm/Dvm.mk336
-rw-r--r--vm/DvmDex.c296
-rw-r--r--vm/DvmDex.h160
-rw-r--r--vm/Exception.c1304
-rw-r--r--vm/Exception.h203
-rw-r--r--vm/Globals.h877
-rw-r--r--vm/Hash.c418
-rw-r--r--vm/Hash.h221
-rw-r--r--vm/IndirectRefTable.c501
-rw-r--r--vm/IndirectRefTable.h382
-rw-r--r--vm/Init.c1748
-rw-r--r--vm/Init.h54
-rw-r--r--vm/InlineNative.c857
-rw-r--r--vm/InlineNative.h111
-rw-r--r--vm/Inlines.c28
-rw-r--r--vm/Inlines.h32
-rw-r--r--vm/Intern.c206
-rw-r--r--vm/Intern.h28
-rw-r--r--vm/JarFile.c370
-rw-r--r--vm/JarFile.h71
-rw-r--r--vm/Jni.c4509
-rw-r--r--vm/JniInternal.h248
-rw-r--r--vm/LinearAlloc.c704
-rw-r--r--vm/LinearAlloc.h120
-rw-r--r--vm/Misc.c738
-rw-r--r--vm/Misc.h328
-rw-r--r--vm/Native.c874
-rw-r--r--vm/Native.h122
-rw-r--r--vm/PointerSet.c273
-rw-r--r--vm/PointerSet.h95
-rw-r--r--vm/Profile.c895
-rw-r--r--vm/Profile.h196
-rw-r--r--vm/Properties.c284
-rw-r--r--vm/Properties.h37
-rw-r--r--vm/README.txt19
-rw-r--r--vm/RawDexFile.c43
-rw-r--r--vm/RawDexFile.h59
-rw-r--r--vm/ReconfigureDvm.mk36
-rw-r--r--vm/ReferenceTable.c293
-rw-r--r--vm/ReferenceTable.h117
-rw-r--r--vm/SignalCatcher.c317
-rw-r--r--vm/SignalCatcher.h25
-rw-r--r--vm/StdioConverter.c286
-rw-r--r--vm/StdioConverter.h25
-rw-r--r--vm/Sync.c2129
-rw-r--r--vm/Sync.h167
-rw-r--r--vm/TestCompability.c26
-rw-r--r--vm/Thread.c4219
-rw-r--r--vm/Thread.h563
-rw-r--r--vm/UtfString.c523
-rw-r--r--vm/UtfString.h138
-rw-r--r--vm/alloc/Alloc.c350
-rw-r--r--vm/alloc/Alloc.h192
-rw-r--r--vm/alloc/CardTable.c271
-rw-r--r--vm/alloc/CardTable.h68
-rw-r--r--vm/alloc/Copying.c2360
-rw-r--r--vm/alloc/DdmHeap.c497
-rw-r--r--vm/alloc/DdmHeap.h41
-rw-r--r--vm/alloc/Float12.h130
-rw-r--r--vm/alloc/GC.h155
-rw-r--r--vm/alloc/Heap.c1007
-rw-r--r--vm/alloc/Heap.h92
-rw-r--r--vm/alloc/HeapBitmap.c200
-rw-r--r--vm/alloc/HeapBitmap.h307
-rw-r--r--vm/alloc/HeapDebug.c401
-rw-r--r--vm/alloc/HeapDebug.h31
-rw-r--r--vm/alloc/HeapInternal.h162
-rw-r--r--vm/alloc/HeapSource.c1891
-rw-r--r--vm/alloc/HeapSource.h202
-rw-r--r--vm/alloc/HeapTable.c198
-rw-r--r--vm/alloc/HeapTable.h49
-rw-r--r--vm/alloc/HeapWorker.c542
-rw-r--r--vm/alloc/HeapWorker.h92
-rw-r--r--vm/alloc/MarkSweep.c986
-rw-r--r--vm/alloc/MarkSweep.h60
-rw-r--r--vm/alloc/TEST/HeapBitmapTest/Makefile28
-rw-r--r--vm/alloc/TEST/HeapBitmapTest/include/Dalvik.h18
-rw-r--r--vm/alloc/TEST/HeapBitmapTest/include/cutils/ashmem.h14
-rw-r--r--vm/alloc/TEST/HeapBitmapTest/main.c496
-rw-r--r--vm/alloc/Verify.c129
-rw-r--r--vm/alloc/Verify.h36
-rw-r--r--vm/alloc/Visit.c205
-rw-r--r--vm/alloc/Visit.h38
-rw-r--r--vm/alloc/VisitInlines.h180
-rw-r--r--vm/alloc/WriteBarrier.h53
-rw-r--r--vm/alloc/clz.c49
-rw-r--r--vm/alloc/clz.h44
-rw-r--r--vm/analysis/CodeVerify.c5782
-rw-r--r--vm/analysis/CodeVerify.h308
-rw-r--r--vm/analysis/DexPrepare.c1446
-rw-r--r--vm/analysis/DexPrepare.h122
-rw-r--r--vm/analysis/DexVerify.c697
-rw-r--r--vm/analysis/DexVerify.h54
-rw-r--r--vm/analysis/Optimize.c1108
-rw-r--r--vm/analysis/Optimize.h48
-rw-r--r--vm/analysis/RegisterMap.c3272
-rw-r--r--vm/analysis/RegisterMap.h266
-rw-r--r--vm/analysis/VerifySubs.c457
-rw-r--r--vm/analysis/VerifySubs.h79
-rw-r--r--vm/arch/arm/CallEABI.S424
-rw-r--r--vm/arch/arm/CallOldABI.S173
-rw-r--r--vm/arch/arm/HintsEABI.c105
-rw-r--r--vm/arch/generic/Call.c104
-rw-r--r--vm/arch/generic/Hints.c56
-rw-r--r--vm/arch/sh/CallSH4ABI.S400
-rw-r--r--vm/arch/x86-atom/Call386ABI.S189
-rw-r--r--vm/arch/x86-atom/Hints386ABI.c79
-rw-r--r--vm/arch/x86/Call386ABI.S174
-rw-r--r--vm/arch/x86/Hints386ABI.c89
-rw-r--r--vm/compiler/Compiler.c755
-rw-r--r--vm/compiler/Compiler.h296
-rw-r--r--vm/compiler/CompilerIR.h243
-rw-r--r--vm/compiler/CompilerInternals.h25
-rw-r--r--vm/compiler/CompilerUtility.h57
-rw-r--r--vm/compiler/Dataflow.c1459
-rw-r--r--vm/compiler/Dataflow.h124
-rw-r--r--vm/compiler/Frontend.c1313
-rw-r--r--vm/compiler/InlineTransformation.c366
-rw-r--r--vm/compiler/IntermediateRep.c121
-rw-r--r--vm/compiler/Loop.c526
-rw-r--r--vm/compiler/Loop.h37
-rw-r--r--vm/compiler/Ralloc.c159
-rw-r--r--vm/compiler/Utility.c316
-rw-r--r--vm/compiler/codegen/CompilerCodegen.h67
-rw-r--r--vm/compiler/codegen/Optimizer.h44
-rw-r--r--vm/compiler/codegen/arm/ArchUtility.c387
-rw-r--r--vm/compiler/codegen/arm/ArmLIR.h789
-rw-r--r--vm/compiler/codegen/arm/Assemble.c2651
-rw-r--r--vm/compiler/codegen/arm/CalloutHelper.h135
-rw-r--r--vm/compiler/codegen/arm/Codegen.h88
-rw-r--r--vm/compiler/codegen/arm/CodegenCommon.c384
-rw-r--r--vm/compiler/codegen/arm/CodegenDriver.c4482
-rw-r--r--vm/compiler/codegen/arm/CodegenFactory.c333
-rw-r--r--vm/compiler/codegen/arm/FP/Thumb2VFP.c261
-rw-r--r--vm/compiler/codegen/arm/FP/ThumbPortableFP.c89
-rw-r--r--vm/compiler/codegen/arm/FP/ThumbVFP.c261
-rw-r--r--vm/compiler/codegen/arm/GlobalOptimizations.c61
-rw-r--r--vm/compiler/codegen/arm/LocalOptimizations.c512
-rw-r--r--vm/compiler/codegen/arm/README.txt48
-rw-r--r--vm/compiler/codegen/arm/Ralloc.h206
-rw-r--r--vm/compiler/codegen/arm/RallocUtil.c1010
-rw-r--r--vm/compiler/codegen/arm/Thumb/Factory.c879
-rw-r--r--vm/compiler/codegen/arm/Thumb/Gen.c226
-rw-r--r--vm/compiler/codegen/arm/Thumb/Ralloc.c44
-rw-r--r--vm/compiler/codegen/arm/Thumb2/Factory.c1210
-rw-r--r--vm/compiler/codegen/arm/Thumb2/Gen.c415
-rw-r--r--vm/compiler/codegen/arm/Thumb2/Ralloc.c63
-rw-r--r--vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.c101
-rw-r--r--vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.h34
-rw-r--r--vm/compiler/codegen/arm/armv5te-vfp/CallingConvention.S34
-rw-r--r--vm/compiler/codegen/arm/armv5te-vfp/Codegen.c51
-rw-r--r--vm/compiler/codegen/arm/armv5te/ArchVariant.c101
-rw-r--r--vm/compiler/codegen/arm/armv5te/ArchVariant.h34
-rw-r--r--vm/compiler/codegen/arm/armv5te/CallingConvention.S32
-rw-r--r--vm/compiler/codegen/arm/armv5te/Codegen.c51
-rw-r--r--vm/compiler/codegen/arm/armv7-a-neon/ArchVariant.c97
-rw-r--r--vm/compiler/codegen/arm/armv7-a-neon/ArchVariant.h34
-rw-r--r--vm/compiler/codegen/arm/armv7-a-neon/CallingConvention.S34
-rw-r--r--vm/compiler/codegen/arm/armv7-a-neon/Codegen.c51
-rw-r--r--vm/compiler/codegen/arm/armv7-a/ArchVariant.c97
-rw-r--r--vm/compiler/codegen/arm/armv7-a/ArchVariant.h34
-rw-r--r--vm/compiler/codegen/arm/armv7-a/CallingConvention.S34
-rw-r--r--vm/compiler/codegen/arm/armv7-a/Codegen.c51
-rw-r--r--vm/compiler/template/Makefile-template49
-rw-r--r--vm/compiler/template/README.txt1
-rw-r--r--vm/compiler/template/armv5te-vfp/TEMPLATE_ADD_DOUBLE_VFP.S2
-rw-r--r--vm/compiler/template/armv5te-vfp/TEMPLATE_ADD_FLOAT_VFP.S2
-rw-r--r--vm/compiler/template/armv5te-vfp/TEMPLATE_CMPG_DOUBLE_VFP.S33
-rw-r--r--vm/compiler/template/armv5te-vfp/TEMPLATE_CMPG_FLOAT_VFP.S32
-rw-r--r--vm/compiler/template/armv5te-vfp/TEMPLATE_CMPL_DOUBLE_VFP.S32
-rw-r--r--vm/compiler/template/armv5te-vfp/TEMPLATE_CMPL_FLOAT_VFP.S32
-rw-r--r--vm/compiler/template/armv5te-vfp/TEMPLATE_DIV_DOUBLE_VFP.S2
-rw-r--r--vm/compiler/template/armv5te-vfp/TEMPLATE_DIV_FLOAT_VFP.S2
-rw-r--r--vm/compiler/template/armv5te-vfp/TEMPLATE_DOUBLE_TO_FLOAT_VFP.S2
-rw-r--r--vm/compiler/template/armv5te-vfp/TEMPLATE_DOUBLE_TO_INT_VFP.S2
-rw-r--r--vm/compiler/template/armv5te-vfp/TEMPLATE_FLOAT_TO_DOUBLE_VFP.S2
-rw-r--r--vm/compiler/template/armv5te-vfp/TEMPLATE_FLOAT_TO_INT_VFP.S2
-rw-r--r--vm/compiler/template/armv5te-vfp/TEMPLATE_INT_TO_DOUBLE_VFP.S2
-rw-r--r--vm/compiler/template/armv5te-vfp/TEMPLATE_INT_TO_FLOAT_VFP.S2
-rw-r--r--vm/compiler/template/armv5te-vfp/TEMPLATE_MEM_OP_DECODE.S19
-rw-r--r--vm/compiler/template/armv5te-vfp/TEMPLATE_MUL_DOUBLE_VFP.S2
-rw-r--r--vm/compiler/template/armv5te-vfp/TEMPLATE_MUL_FLOAT_VFP.S2
-rw-r--r--vm/compiler/template/armv5te-vfp/TEMPLATE_RESTORE_STATE.S11
-rw-r--r--vm/compiler/template/armv5te-vfp/TEMPLATE_SAVE_STATE.S23
-rw-r--r--vm/compiler/template/armv5te-vfp/TEMPLATE_SQRT_DOUBLE_VFP.S23
-rw-r--r--vm/compiler/template/armv5te-vfp/TEMPLATE_SUB_DOUBLE_VFP.S2
-rw-r--r--vm/compiler/template/armv5te-vfp/TEMPLATE_SUB_FLOAT_VFP.S2
-rw-r--r--vm/compiler/template/armv5te-vfp/TemplateOpList.h59
-rw-r--r--vm/compiler/template/armv5te-vfp/fbinop.S14
-rw-r--r--vm/compiler/template/armv5te-vfp/fbinopWide.S14
-rw-r--r--vm/compiler/template/armv5te-vfp/funop.S15
-rw-r--r--vm/compiler/template/armv5te-vfp/funopNarrower.S15
-rw-r--r--vm/compiler/template/armv5te-vfp/funopWider.S15
-rw-r--r--vm/compiler/template/armv5te-vfp/platform.S16
-rw-r--r--vm/compiler/template/armv5te/TEMPLATE_CMPG_DOUBLE.S1
-rw-r--r--vm/compiler/template/armv5te/TEMPLATE_CMPG_FLOAT.S1
-rw-r--r--vm/compiler/template/armv5te/TEMPLATE_CMPL_DOUBLE.S36
-rw-r--r--vm/compiler/template/armv5te/TEMPLATE_CMPL_FLOAT.S54
-rw-r--r--vm/compiler/template/armv5te/TEMPLATE_CMP_LONG.S33
-rw-r--r--vm/compiler/template/armv5te/TEMPLATE_INTERPRET.S23
-rw-r--r--vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S45
-rw-r--r--vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S70
-rw-r--r--vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S54
-rw-r--r--vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S50
-rw-r--r--vm/compiler/template/armv5te/TEMPLATE_MEM_OP_DECODE.S17
-rw-r--r--vm/compiler/template/armv5te/TEMPLATE_MONITOR_ENTER.S25
-rw-r--r--vm/compiler/template/armv5te/TEMPLATE_MONITOR_ENTER_DEBUG.S32
-rw-r--r--vm/compiler/template/armv5te/TEMPLATE_MUL_LONG.S28
-rw-r--r--vm/compiler/template/armv5te/TEMPLATE_RESTORE_STATE.S8
-rw-r--r--vm/compiler/template/armv5te/TEMPLATE_RETURN.S50
-rw-r--r--vm/compiler/template/armv5te/TEMPLATE_SAVE_STATE.S21
-rw-r--r--vm/compiler/template/armv5te/TEMPLATE_SHL_LONG.S15
-rw-r--r--vm/compiler/template/armv5te/TEMPLATE_SHR_LONG.S15
-rw-r--r--vm/compiler/template/armv5te/TEMPLATE_STRING_COMPARETO.S133
-rw-r--r--vm/compiler/template/armv5te/TEMPLATE_STRING_INDEXOF.S112
-rw-r--r--vm/compiler/template/armv5te/TEMPLATE_THROW_EXCEPTION_COMMON.S6
-rw-r--r--vm/compiler/template/armv5te/TEMPLATE_USHR_LONG.S15
-rw-r--r--vm/compiler/template/armv5te/TemplateOpList.h44
-rw-r--r--vm/compiler/template/armv5te/footer.S107
-rw-r--r--vm/compiler/template/armv5te/header.S95
-rw-r--r--vm/compiler/template/armv5te/platform.S16
-rw-r--r--vm/compiler/template/armv7-a-neon/TemplateOpList.h59
-rw-r--r--vm/compiler/template/armv7-a/TemplateOpList.h59
-rw-r--r--vm/compiler/template/config-armv5te45
-rw-r--r--vm/compiler/template/config-armv5te-vfp62
-rw-r--r--vm/compiler/template/config-armv7-a61
-rw-r--r--vm/compiler/template/config-armv7-a-neon61
-rwxr-xr-xvm/compiler/template/gen-template.py422
-rw-r--r--vm/compiler/template/out/CompilerTemplateAsm-armv5te-vfp.S1544
-rw-r--r--vm/compiler/template/out/CompilerTemplateAsm-armv5te.S1267
-rw-r--r--vm/compiler/template/out/CompilerTemplateAsm-armv7-a-neon.S1544
-rw-r--r--vm/compiler/template/out/CompilerTemplateAsm-armv7-a.S1544
-rwxr-xr-xvm/compiler/template/rebuild.sh22
-rw-r--r--vm/hprof/Hprof.c185
-rw-r--r--vm/hprof/Hprof.h263
-rw-r--r--vm/hprof/HprofClass.c221
-rw-r--r--vm/hprof/HprofHeap.c494
-rw-r--r--vm/hprof/HprofOutput.c339
-rw-r--r--vm/hprof/HprofStack.c266
-rw-r--r--vm/hprof/HprofStack.h42
-rw-r--r--vm/hprof/HprofStackFrame.c242
-rw-r--r--vm/hprof/HprofString.c118
-rw-r--r--vm/interp/Interp.c1360
-rw-r--r--vm/interp/Interp.h62
-rw-r--r--vm/interp/InterpDefs.h277
-rw-r--r--vm/interp/Jit.c1352
-rw-r--r--vm/interp/Jit.h126
-rw-r--r--vm/interp/README.txt3
-rw-r--r--vm/interp/Stack.c1381
-rw-r--r--vm/interp/Stack.h286
-rw-r--r--vm/jdwp/ExpandBuf.c175
-rw-r--r--vm/jdwp/ExpandBuf.h57
-rw-r--r--vm/jdwp/Jdwp.h238
-rw-r--r--vm/jdwp/JdwpAdb.c761
-rw-r--r--vm/jdwp/JdwpConstants.c239
-rw-r--r--vm/jdwp/JdwpConstants.h229
-rw-r--r--vm/jdwp/JdwpEvent.c1292
-rw-r--r--vm/jdwp/JdwpEvent.h129
-rw-r--r--vm/jdwp/JdwpHandler.c2223
-rw-r--r--vm/jdwp/JdwpHandler.h47
-rw-r--r--vm/jdwp/JdwpMain.c415
-rw-r--r--vm/jdwp/JdwpPriv.h179
-rw-r--r--vm/jdwp/JdwpSocket.c924
-rw-r--r--vm/jdwp/README.txt12
-rw-r--r--vm/mterp/Makefile-mterp51
-rw-r--r--vm/mterp/Mterp.c125
-rw-r--r--vm/mterp/Mterp.h55
-rw-r--r--vm/mterp/NOTES.txt71
-rw-r--r--vm/mterp/README.txt215
-rw-r--r--vm/mterp/arm-vfp/OP_ADD_DOUBLE.S2
-rw-r--r--vm/mterp/arm-vfp/OP_ADD_DOUBLE_2ADDR.S2
-rw-r--r--vm/mterp/arm-vfp/OP_ADD_FLOAT.S2
-rw-r--r--vm/mterp/arm-vfp/OP_ADD_FLOAT_2ADDR.S2
-rw-r--r--vm/mterp/arm-vfp/OP_CMPG_DOUBLE.S42
-rw-r--r--vm/mterp/arm-vfp/OP_CMPG_FLOAT.S42
-rw-r--r--vm/mterp/arm-vfp/OP_CMPL_DOUBLE.S42
-rw-r--r--vm/mterp/arm-vfp/OP_CMPL_FLOAT.S42
-rw-r--r--vm/mterp/arm-vfp/OP_DIV_DOUBLE.S2
-rw-r--r--vm/mterp/arm-vfp/OP_DIV_DOUBLE_2ADDR.S2
-rw-r--r--vm/mterp/arm-vfp/OP_DIV_FLOAT.S2
-rw-r--r--vm/mterp/arm-vfp/OP_DIV_FLOAT_2ADDR.S2
-rw-r--r--vm/mterp/arm-vfp/OP_DOUBLE_TO_FLOAT.S2
-rw-r--r--vm/mterp/arm-vfp/OP_DOUBLE_TO_INT.S2
-rw-r--r--vm/mterp/arm-vfp/OP_FLOAT_TO_DOUBLE.S2
-rw-r--r--vm/mterp/arm-vfp/OP_FLOAT_TO_INT.S2
-rw-r--r--vm/mterp/arm-vfp/OP_INT_TO_DOUBLE.S2
-rw-r--r--vm/mterp/arm-vfp/OP_INT_TO_FLOAT.S2
-rw-r--r--vm/mterp/arm-vfp/OP_MUL_DOUBLE.S2
-rw-r--r--vm/mterp/arm-vfp/OP_MUL_DOUBLE_2ADDR.S2
-rw-r--r--vm/mterp/arm-vfp/OP_MUL_FLOAT.S2
-rw-r--r--vm/mterp/arm-vfp/OP_MUL_FLOAT_2ADDR.S2
-rw-r--r--vm/mterp/arm-vfp/OP_SUB_DOUBLE.S2
-rw-r--r--vm/mterp/arm-vfp/OP_SUB_DOUBLE_2ADDR.S2
-rw-r--r--vm/mterp/arm-vfp/OP_SUB_FLOAT.S2
-rw-r--r--vm/mterp/arm-vfp/OP_SUB_FLOAT_2ADDR.S2
-rw-r--r--vm/mterp/arm-vfp/README.txt7
-rw-r--r--vm/mterp/arm-vfp/fbinop.S23
-rw-r--r--vm/mterp/arm-vfp/fbinop2addr.S21
-rw-r--r--vm/mterp/arm-vfp/fbinopWide.S23
-rw-r--r--vm/mterp/arm-vfp/fbinopWide2addr.S22
-rw-r--r--vm/mterp/arm-vfp/funop.S18
-rw-r--r--vm/mterp/arm-vfp/funopNarrower.S18
-rw-r--r--vm/mterp/arm-vfp/funopWider.S18
-rw-r--r--vm/mterp/armv4t/OP_AGET_WIDE.S33
-rw-r--r--vm/mterp/armv4t/OP_APUT_WIDE.S31
-rw-r--r--vm/mterp/armv4t/OP_IGET_WIDE.S50
-rw-r--r--vm/mterp/armv4t/OP_IGET_WIDE_QUICK.S17
-rw-r--r--vm/mterp/armv4t/OP_IGET_WIDE_VOLATILE.S2
-rw-r--r--vm/mterp/armv4t/OP_IPUT_WIDE.S46
-rw-r--r--vm/mterp/armv4t/OP_IPUT_WIDE_QUICK.S17
-rw-r--r--vm/mterp/armv4t/OP_IPUT_WIDE_VOLATILE.S2
-rw-r--r--vm/mterp/armv4t/OP_SGET_WIDE.S44
-rw-r--r--vm/mterp/armv4t/OP_SGET_WIDE_VOLATILE.S2
-rw-r--r--vm/mterp/armv4t/OP_SPUT_WIDE.S46
-rw-r--r--vm/mterp/armv4t/OP_SPUT_WIDE_VOLATILE.S2
-rw-r--r--vm/mterp/armv4t/platform.S44
-rw-r--r--vm/mterp/armv5te/OP_ADD_DOUBLE.S2
-rw-r--r--vm/mterp/armv5te/OP_ADD_DOUBLE_2ADDR.S2
-rw-r--r--vm/mterp/armv5te/OP_ADD_FLOAT.S2
-rw-r--r--vm/mterp/armv5te/OP_ADD_FLOAT_2ADDR.S2
-rw-r--r--vm/mterp/armv5te/OP_ADD_INT.S2
-rw-r--r--vm/mterp/armv5te/OP_ADD_INT_2ADDR.S2
-rw-r--r--vm/mterp/armv5te/OP_ADD_INT_LIT16.S2
-rw-r--r--vm/mterp/armv5te/OP_ADD_INT_LIT8.S2
-rw-r--r--vm/mterp/armv5te/OP_ADD_LONG.S2
-rw-r--r--vm/mterp/armv5te/OP_ADD_LONG_2ADDR.S2
-rw-r--r--vm/mterp/armv5te/OP_AGET.S27
-rw-r--r--vm/mterp/armv5te/OP_AGET_BOOLEAN.S2
-rw-r--r--vm/mterp/armv5te/OP_AGET_BYTE.S2
-rw-r--r--vm/mterp/armv5te/OP_AGET_CHAR.S2
-rw-r--r--vm/mterp/armv5te/OP_AGET_OBJECT.S2
-rw-r--r--vm/mterp/armv5te/OP_AGET_SHORT.S2
-rw-r--r--vm/mterp/armv5te/OP_AGET_WIDE.S32
-rw-r--r--vm/mterp/armv5te/OP_AND_INT.S2
-rw-r--r--vm/mterp/armv5te/OP_AND_INT_2ADDR.S2
-rw-r--r--vm/mterp/armv5te/OP_AND_INT_LIT16.S2
-rw-r--r--vm/mterp/armv5te/OP_AND_INT_LIT8.S2
-rw-r--r--vm/mterp/armv5te/OP_AND_LONG.S2
-rw-r--r--vm/mterp/armv5te/OP_AND_LONG_2ADDR.S2
-rw-r--r--vm/mterp/armv5te/OP_APUT.S27
-rw-r--r--vm/mterp/armv5te/OP_APUT_BOOLEAN.S2
-rw-r--r--vm/mterp/armv5te/OP_APUT_BYTE.S2
-rw-r--r--vm/mterp/armv5te/OP_APUT_CHAR.S2
-rw-r--r--vm/mterp/armv5te/OP_APUT_OBJECT.S51
-rw-r--r--vm/mterp/armv5te/OP_APUT_SHORT.S2
-rw-r--r--vm/mterp/armv5te/OP_APUT_WIDE.S32
-rw-r--r--vm/mterp/armv5te/OP_ARRAY_LENGTH.S15
-rw-r--r--vm/mterp/armv5te/OP_BREAKPOINT.S1
-rw-r--r--vm/mterp/armv5te/OP_CHECK_CAST.S72
-rw-r--r--vm/mterp/armv5te/OP_CMPG_DOUBLE.S2
-rw-r--r--vm/mterp/armv5te/OP_CMPG_FLOAT.S2
-rw-r--r--vm/mterp/armv5te/OP_CMPL_DOUBLE.S48
-rw-r--r--vm/mterp/armv5te/OP_CMPL_FLOAT.S115
-rw-r--r--vm/mterp/armv5te/OP_CMP_LONG.S60
-rw-r--r--vm/mterp/armv5te/OP_CONST.S10
-rw-r--r--vm/mterp/armv5te/OP_CONST_16.S8
-rw-r--r--vm/mterp/armv5te/OP_CONST_4.S10
-rw-r--r--vm/mterp/armv5te/OP_CONST_CLASS.S35
-rw-r--r--vm/mterp/armv5te/OP_CONST_HIGH16.S9
-rw-r--r--vm/mterp/armv5te/OP_CONST_STRING.S34
-rw-r--r--vm/mterp/armv5te/OP_CONST_STRING_JUMBO.S36
-rw-r--r--vm/mterp/armv5te/OP_CONST_WIDE.S14
-rw-r--r--vm/mterp/armv5te/OP_CONST_WIDE_16.S10
-rw-r--r--vm/mterp/armv5te/OP_CONST_WIDE_32.S12
-rw-r--r--vm/mterp/armv5te/OP_CONST_WIDE_HIGH16.S11
-rw-r--r--vm/mterp/armv5te/OP_DIV_DOUBLE.S2
-rw-r--r--vm/mterp/armv5te/OP_DIV_DOUBLE_2ADDR.S2
-rw-r--r--vm/mterp/armv5te/OP_DIV_FLOAT.S2
-rw-r--r--vm/mterp/armv5te/OP_DIV_FLOAT_2ADDR.S2
-rw-r--r--vm/mterp/armv5te/OP_DIV_INT.S2
-rw-r--r--vm/mterp/armv5te/OP_DIV_INT_2ADDR.S2
-rw-r--r--vm/mterp/armv5te/OP_DIV_INT_LIT16.S2
-rw-r--r--vm/mterp/armv5te/OP_DIV_INT_LIT8.S2
-rw-r--r--vm/mterp/armv5te/OP_DIV_LONG.S2
-rw-r--r--vm/mterp/armv5te/OP_DIV_LONG_2ADDR.S2
-rw-r--r--vm/mterp/armv5te/OP_DOUBLE_TO_FLOAT.S2
-rw-r--r--vm/mterp/armv5te/OP_DOUBLE_TO_INT.S54
-rw-r--r--vm/mterp/armv5te/OP_DOUBLE_TO_LONG.S53
-rw-r--r--vm/mterp/armv5te/OP_EXECUTE_INLINE.S60
-rw-r--r--vm/mterp/armv5te/OP_EXECUTE_INLINE_RANGE.S55
-rw-r--r--vm/mterp/armv5te/OP_FILLED_NEW_ARRAY.S108
-rw-r--r--vm/mterp/armv5te/OP_FILLED_NEW_ARRAY_RANGE.S2
-rw-r--r--vm/mterp/armv5te/OP_FILL_ARRAY_DATA.S15
-rw-r--r--vm/mterp/armv5te/OP_FLOAT_TO_DOUBLE.S2
-rw-r--r--vm/mterp/armv5te/OP_FLOAT_TO_INT.S40
-rw-r--r--vm/mterp/armv5te/OP_FLOAT_TO_LONG.S40
-rw-r--r--vm/mterp/armv5te/OP_GOTO.S25
-rw-r--r--vm/mterp/armv5te/OP_GOTO_16.S24
-rw-r--r--vm/mterp/armv5te/OP_GOTO_32.S32
-rw-r--r--vm/mterp/armv5te/OP_IF_EQ.S2
-rw-r--r--vm/mterp/armv5te/OP_IF_EQZ.S2
-rw-r--r--vm/mterp/armv5te/OP_IF_GE.S2
-rw-r--r--vm/mterp/armv5te/OP_IF_GEZ.S2
-rw-r--r--vm/mterp/armv5te/OP_IF_GT.S2
-rw-r--r--vm/mterp/armv5te/OP_IF_GTZ.S2
-rw-r--r--vm/mterp/armv5te/OP_IF_LE.S2
-rw-r--r--vm/mterp/armv5te/OP_IF_LEZ.S2
-rw-r--r--vm/mterp/armv5te/OP_IF_LT.S2
-rw-r--r--vm/mterp/armv5te/OP_IF_LTZ.S2
-rw-r--r--vm/mterp/armv5te/OP_IF_NE.S2
-rw-r--r--vm/mterp/armv5te/OP_IF_NEZ.S2
-rw-r--r--vm/mterp/armv5te/OP_IGET.S47
-rw-r--r--vm/mterp/armv5te/OP_IGET_BOOLEAN.S3
-rw-r--r--vm/mterp/armv5te/OP_IGET_BYTE.S4
-rw-r--r--vm/mterp/armv5te/OP_IGET_CHAR.S4
-rw-r--r--vm/mterp/armv5te/OP_IGET_OBJECT.S2
-rw-r--r--vm/mterp/armv5te/OP_IGET_OBJECT_QUICK.S2
-rw-r--r--vm/mterp/armv5te/OP_IGET_OBJECT_VOLATILE.S2
-rw-r--r--vm/mterp/armv5te/OP_IGET_QUICK.S16
-rw-r--r--vm/mterp/armv5te/OP_IGET_SHORT.S4
-rw-r--r--vm/mterp/armv5te/OP_IGET_VOLATILE.S2
-rw-r--r--vm/mterp/armv5te/OP_IGET_WIDE.S49
-rw-r--r--vm/mterp/armv5te/OP_IGET_WIDE_QUICK.S16
-rw-r--r--vm/mterp/armv5te/OP_IGET_WIDE_VOLATILE.S2
-rw-r--r--vm/mterp/armv5te/OP_INSTANCE_OF.S85
-rw-r--r--vm/mterp/armv5te/OP_INT_TO_BYTE.S2
-rw-r--r--vm/mterp/armv5te/OP_INT_TO_CHAR.S2
-rw-r--r--vm/mterp/armv5te/OP_INT_TO_DOUBLE.S2
-rw-r--r--vm/mterp/armv5te/OP_INT_TO_FLOAT.S2
-rw-r--r--vm/mterp/armv5te/OP_INT_TO_LONG.S2
-rw-r--r--vm/mterp/armv5te/OP_INT_TO_SHORT.S2
-rw-r--r--vm/mterp/armv5te/OP_INVOKE_DIRECT.S47
-rw-r--r--vm/mterp/armv5te/OP_INVOKE_DIRECT_EMPTY.S7
-rw-r--r--vm/mterp/armv5te/OP_INVOKE_DIRECT_RANGE.S2
-rw-r--r--vm/mterp/armv5te/OP_INVOKE_INTERFACE.S27
-rw-r--r--vm/mterp/armv5te/OP_INVOKE_INTERFACE_RANGE.S2
-rw-r--r--vm/mterp/armv5te/OP_INVOKE_STATIC.S24
-rw-r--r--vm/mterp/armv5te/OP_INVOKE_STATIC_RANGE.S2
-rw-r--r--vm/mterp/armv5te/OP_INVOKE_SUPER.S60
-rw-r--r--vm/mterp/armv5te/OP_INVOKE_SUPER_QUICK.S25
-rw-r--r--vm/mterp/armv5te/OP_INVOKE_SUPER_QUICK_RANGE.S2
-rw-r--r--vm/mterp/armv5te/OP_INVOKE_SUPER_RANGE.S2
-rw-r--r--vm/mterp/armv5te/OP_INVOKE_VIRTUAL.S45
-rw-r--r--vm/mterp/armv5te/OP_INVOKE_VIRTUAL_QUICK.S23
-rw-r--r--vm/mterp/armv5te/OP_INVOKE_VIRTUAL_QUICK_RANGE.S2
-rw-r--r--vm/mterp/armv5te/OP_INVOKE_VIRTUAL_RANGE.S2
-rw-r--r--vm/mterp/armv5te/OP_IPUT.S47
-rw-r--r--vm/mterp/armv5te/OP_IPUT_BOOLEAN.S3
-rw-r--r--vm/mterp/armv5te/OP_IPUT_BYTE.S3
-rw-r--r--vm/mterp/armv5te/OP_IPUT_CHAR.S3
-rw-r--r--vm/mterp/armv5te/OP_IPUT_OBJECT.S50
-rw-r--r--vm/mterp/armv5te/OP_IPUT_OBJECT_QUICK.S19
-rw-r--r--vm/mterp/armv5te/OP_IPUT_OBJECT_VOLATILE.S2
-rw-r--r--vm/mterp/armv5te/OP_IPUT_QUICK.S16
-rw-r--r--vm/mterp/armv5te/OP_IPUT_SHORT.S3
-rw-r--r--vm/mterp/armv5te/OP_IPUT_VOLATILE.S2
-rw-r--r--vm/mterp/armv5te/OP_IPUT_WIDE.S46
-rw-r--r--vm/mterp/armv5te/OP_IPUT_WIDE_QUICK.S16
-rw-r--r--vm/mterp/armv5te/OP_IPUT_WIDE_VOLATILE.S2
-rw-r--r--vm/mterp/armv5te/OP_LONG_TO_DOUBLE.S2
-rw-r--r--vm/mterp/armv5te/OP_LONG_TO_FLOAT.S2
-rw-r--r--vm/mterp/armv5te/OP_LONG_TO_INT.S3
-rw-r--r--vm/mterp/armv5te/OP_MONITOR_ENTER.S22
-rw-r--r--vm/mterp/armv5te/OP_MONITOR_EXIT.S26
-rw-r--r--vm/mterp/armv5te/OP_MOVE.S11
-rw-r--r--vm/mterp/armv5te/OP_MOVE_16.S10
-rw-r--r--vm/mterp/armv5te/OP_MOVE_EXCEPTION.S11
-rw-r--r--vm/mterp/armv5te/OP_MOVE_FROM16.S10
-rw-r--r--vm/mterp/armv5te/OP_MOVE_OBJECT.S2
-rw-r--r--vm/mterp/armv5te/OP_MOVE_OBJECT_16.S2
-rw-r--r--vm/mterp/armv5te/OP_MOVE_OBJECT_FROM16.S2
-rw-r--r--vm/mterp/armv5te/OP_MOVE_RESULT.S9
-rw-r--r--vm/mterp/armv5te/OP_MOVE_RESULT_OBJECT.S2
-rw-r--r--vm/mterp/armv5te/OP_MOVE_RESULT_WIDE.S10
-rw-r--r--vm/mterp/armv5te/OP_MOVE_WIDE.S13
-rw-r--r--vm/mterp/armv5te/OP_MOVE_WIDE_16.S12
-rw-r--r--vm/mterp/armv5te/OP_MOVE_WIDE_FROM16.S12
-rw-r--r--vm/mterp/armv5te/OP_MUL_DOUBLE.S2
-rw-r--r--vm/mterp/armv5te/OP_MUL_DOUBLE_2ADDR.S2
-rw-r--r--vm/mterp/armv5te/OP_MUL_FLOAT.S2
-rw-r--r--vm/mterp/armv5te/OP_MUL_FLOAT_2ADDR.S2
-rw-r--r--vm/mterp/armv5te/OP_MUL_INT.S3
-rw-r--r--vm/mterp/armv5te/OP_MUL_INT_2ADDR.S3
-rw-r--r--vm/mterp/armv5te/OP_MUL_INT_LIT16.S3
-rw-r--r--vm/mterp/armv5te/OP_MUL_INT_LIT8.S3
-rw-r--r--vm/mterp/armv5te/OP_MUL_LONG.S41
-rw-r--r--vm/mterp/armv5te/OP_MUL_LONG_2ADDR.S26
-rw-r--r--vm/mterp/armv5te/OP_NEG_DOUBLE.S2
-rw-r--r--vm/mterp/armv5te/OP_NEG_FLOAT.S2
-rw-r--r--vm/mterp/armv5te/OP_NEG_INT.S2
-rw-r--r--vm/mterp/armv5te/OP_NEG_LONG.S2
-rw-r--r--vm/mterp/armv5te/OP_NEW_ARRAY.S61
-rw-r--r--vm/mterp/armv5te/OP_NEW_INSTANCE.S68
-rw-r--r--vm/mterp/armv5te/OP_NOP.S14
-rw-r--r--vm/mterp/armv5te/OP_NOT_INT.S2
-rw-r--r--vm/mterp/armv5te/OP_NOT_LONG.S2
-rw-r--r--vm/mterp/armv5te/OP_OR_INT.S2
-rw-r--r--vm/mterp/armv5te/OP_OR_INT_2ADDR.S2
-rw-r--r--vm/mterp/armv5te/OP_OR_INT_LIT16.S2
-rw-r--r--vm/mterp/armv5te/OP_OR_INT_LIT8.S2
-rw-r--r--vm/mterp/armv5te/OP_OR_LONG.S2
-rw-r--r--vm/mterp/armv5te/OP_OR_LONG_2ADDR.S2
-rw-r--r--vm/mterp/armv5te/OP_PACKED_SWITCH.S34
-rw-r--r--vm/mterp/armv5te/OP_REM_DOUBLE.S3
-rw-r--r--vm/mterp/armv5te/OP_REM_DOUBLE_2ADDR.S3
-rw-r--r--vm/mterp/armv5te/OP_REM_FLOAT.S3
-rw-r--r--vm/mterp/armv5te/OP_REM_FLOAT_2ADDR.S3
-rw-r--r--vm/mterp/armv5te/OP_REM_INT.S3
-rw-r--r--vm/mterp/armv5te/OP_REM_INT_2ADDR.S3
-rw-r--r--vm/mterp/armv5te/OP_REM_INT_LIT16.S3
-rw-r--r--vm/mterp/armv5te/OP_REM_INT_LIT8.S3
-rw-r--r--vm/mterp/armv5te/OP_REM_LONG.S3
-rw-r--r--vm/mterp/armv5te/OP_REM_LONG_2ADDR.S3
-rw-r--r--vm/mterp/armv5te/OP_RETURN.S12
-rw-r--r--vm/mterp/armv5te/OP_RETURN_OBJECT.S2
-rw-r--r--vm/mterp/armv5te/OP_RETURN_VOID.S2
-rw-r--r--vm/mterp/armv5te/OP_RETURN_WIDE.S12
-rw-r--r--vm/mterp/armv5te/OP_RSUB_INT.S3
-rw-r--r--vm/mterp/armv5te/OP_RSUB_INT_LIT8.S2
-rw-r--r--vm/mterp/armv5te/OP_SGET.S39
-rw-r--r--vm/mterp/armv5te/OP_SGET_BOOLEAN.S2
-rw-r--r--vm/mterp/armv5te/OP_SGET_BYTE.S2
-rw-r--r--vm/mterp/armv5te/OP_SGET_CHAR.S2
-rw-r--r--vm/mterp/armv5te/OP_SGET_OBJECT.S2
-rw-r--r--vm/mterp/armv5te/OP_SGET_OBJECT_VOLATILE.S2
-rw-r--r--vm/mterp/armv5te/OP_SGET_SHORT.S2
-rw-r--r--vm/mterp/armv5te/OP_SGET_VOLATILE.S2
-rw-r--r--vm/mterp/armv5te/OP_SGET_WIDE.S44
-rw-r--r--vm/mterp/armv5te/OP_SGET_WIDE_VOLATILE.S2
-rw-r--r--vm/mterp/armv5te/OP_SHL_INT.S2
-rw-r--r--vm/mterp/armv5te/OP_SHL_INT_2ADDR.S2
-rw-r--r--vm/mterp/armv5te/OP_SHL_INT_LIT8.S2
-rw-r--r--vm/mterp/armv5te/OP_SHL_LONG.S32
-rw-r--r--vm/mterp/armv5te/OP_SHL_LONG_2ADDR.S28
-rw-r--r--vm/mterp/armv5te/OP_SHR_INT.S2
-rw-r--r--vm/mterp/armv5te/OP_SHR_INT_2ADDR.S2
-rw-r--r--vm/mterp/armv5te/OP_SHR_INT_LIT8.S2
-rw-r--r--vm/mterp/armv5te/OP_SHR_LONG.S32
-rw-r--r--vm/mterp/armv5te/OP_SHR_LONG_2ADDR.S28
-rw-r--r--vm/mterp/armv5te/OP_SPARSE_SWITCH.S2
-rw-r--r--vm/mterp/armv5te/OP_SPUT.S39
-rw-r--r--vm/mterp/armv5te/OP_SPUT_BOOLEAN.S2
-rw-r--r--vm/mterp/armv5te/OP_SPUT_BYTE.S2
-rw-r--r--vm/mterp/armv5te/OP_SPUT_CHAR.S2
-rw-r--r--vm/mterp/armv5te/OP_SPUT_OBJECT.S38
-rw-r--r--vm/mterp/armv5te/OP_SPUT_OBJECT_VOLATILE.S2
-rw-r--r--vm/mterp/armv5te/OP_SPUT_SHORT.S2
-rw-r--r--vm/mterp/armv5te/OP_SPUT_VOLATILE.S2
-rw-r--r--vm/mterp/armv5te/OP_SPUT_WIDE.S46
-rw-r--r--vm/mterp/armv5te/OP_SPUT_WIDE_VOLATILE.S2
-rw-r--r--vm/mterp/armv5te/OP_SUB_DOUBLE.S2
-rw-r--r--vm/mterp/armv5te/OP_SUB_DOUBLE_2ADDR.S2
-rw-r--r--vm/mterp/armv5te/OP_SUB_FLOAT.S2
-rw-r--r--vm/mterp/armv5te/OP_SUB_FLOAT_2ADDR.S2
-rw-r--r--vm/mterp/armv5te/OP_SUB_INT.S2
-rw-r--r--vm/mterp/armv5te/OP_SUB_INT_2ADDR.S2
-rw-r--r--vm/mterp/armv5te/OP_SUB_LONG.S2
-rw-r--r--vm/mterp/armv5te/OP_SUB_LONG_2ADDR.S2
-rw-r--r--vm/mterp/armv5te/OP_THROW.S15
-rw-r--r--vm/mterp/armv5te/OP_THROW_VERIFICATION_ERROR.S13
-rw-r--r--vm/mterp/armv5te/OP_UNUSED_3E.S1
-rw-r--r--vm/mterp/armv5te/OP_UNUSED_3F.S1
-rw-r--r--vm/mterp/armv5te/OP_UNUSED_40.S1
-rw-r--r--vm/mterp/armv5te/OP_UNUSED_41.S1
-rw-r--r--vm/mterp/armv5te/OP_UNUSED_42.S1
-rw-r--r--vm/mterp/armv5te/OP_UNUSED_43.S1
-rw-r--r--vm/mterp/armv5te/OP_UNUSED_73.S1
-rw-r--r--vm/mterp/armv5te/OP_UNUSED_79.S1
-rw-r--r--vm/mterp/armv5te/OP_UNUSED_7A.S1
-rw-r--r--vm/mterp/armv5te/OP_UNUSED_F1.S1
-rw-r--r--vm/mterp/armv5te/OP_UNUSED_FF.S1
-rw-r--r--vm/mterp/armv5te/OP_USHR_INT.S2
-rw-r--r--vm/mterp/armv5te/OP_USHR_INT_2ADDR.S2
-rw-r--r--vm/mterp/armv5te/OP_USHR_INT_LIT8.S2
-rw-r--r--vm/mterp/armv5te/OP_USHR_LONG.S32
-rw-r--r--vm/mterp/armv5te/OP_USHR_LONG_2ADDR.S28
-rw-r--r--vm/mterp/armv5te/OP_XOR_INT.S2
-rw-r--r--vm/mterp/armv5te/OP_XOR_INT_2ADDR.S2
-rw-r--r--vm/mterp/armv5te/OP_XOR_INT_LIT16.S2
-rw-r--r--vm/mterp/armv5te/OP_XOR_INT_LIT8.S2
-rw-r--r--vm/mterp/armv5te/OP_XOR_LONG.S2
-rw-r--r--vm/mterp/armv5te/OP_XOR_LONG_2ADDR.S2
-rw-r--r--vm/mterp/armv5te/bincmp.S31
-rw-r--r--vm/mterp/armv5te/binop.S35
-rw-r--r--vm/mterp/armv5te/binop2addr.S33
-rw-r--r--vm/mterp/armv5te/binopLit16.S30
-rw-r--r--vm/mterp/armv5te/binopLit8.S32
-rw-r--r--vm/mterp/armv5te/binopWide.S38
-rw-r--r--vm/mterp/armv5te/binopWide2addr.S35
-rw-r--r--vm/mterp/armv5te/debug.c79
-rw-r--r--vm/mterp/armv5te/entry.S163
-rw-r--r--vm/mterp/armv5te/footer.S1229
-rw-r--r--vm/mterp/armv5te/header.S200
-rw-r--r--vm/mterp/armv5te/platform.S41
-rw-r--r--vm/mterp/armv5te/stub.S8
-rw-r--r--vm/mterp/armv5te/unop.S21
-rw-r--r--vm/mterp/armv5te/unopNarrower.S24
-rw-r--r--vm/mterp/armv5te/unopWide.S22
-rw-r--r--vm/mterp/armv5te/unopWider.S21
-rw-r--r--vm/mterp/armv5te/unused.S1
-rw-r--r--vm/mterp/armv5te/zcmp.S31
-rw-r--r--vm/mterp/armv6/OP_INT_TO_BYTE.S2
-rw-r--r--vm/mterp/armv6/OP_INT_TO_CHAR.S2
-rw-r--r--vm/mterp/armv6/OP_INT_TO_SHORT.S2
-rw-r--r--vm/mterp/armv6t2/OP_ADD_DOUBLE_2ADDR.S2
-rw-r--r--vm/mterp/armv6t2/OP_ADD_FLOAT_2ADDR.S2
-rw-r--r--vm/mterp/armv6t2/OP_ADD_INT_2ADDR.S2
-rw-r--r--vm/mterp/armv6t2/OP_ADD_INT_LIT16.S2
-rw-r--r--vm/mterp/armv6t2/OP_ADD_LONG_2ADDR.S2
-rw-r--r--vm/mterp/armv6t2/OP_AND_INT_2ADDR.S2
-rw-r--r--vm/mterp/armv6t2/OP_AND_INT_LIT16.S2
-rw-r--r--vm/mterp/armv6t2/OP_AND_LONG_2ADDR.S2
-rw-r--r--vm/mterp/armv6t2/OP_ARRAY_LENGTH.S14
-rw-r--r--vm/mterp/armv6t2/OP_CONST_4.S9
-rw-r--r--vm/mterp/armv6t2/OP_DIV_DOUBLE_2ADDR.S2
-rw-r--r--vm/mterp/armv6t2/OP_DIV_FLOAT_2ADDR.S2
-rw-r--r--vm/mterp/armv6t2/OP_DIV_INT_2ADDR.S2
-rw-r--r--vm/mterp/armv6t2/OP_DIV_INT_LIT16.S2
-rw-r--r--vm/mterp/armv6t2/OP_DIV_LONG_2ADDR.S2
-rw-r--r--vm/mterp/armv6t2/OP_DOUBLE_TO_FLOAT.S2
-rw-r--r--vm/mterp/armv6t2/OP_DOUBLE_TO_INT.S54
-rw-r--r--vm/mterp/armv6t2/OP_DOUBLE_TO_LONG.S53
-rw-r--r--vm/mterp/armv6t2/OP_FLOAT_TO_DOUBLE.S2
-rw-r--r--vm/mterp/armv6t2/OP_FLOAT_TO_INT.S40
-rw-r--r--vm/mterp/armv6t2/OP_FLOAT_TO_LONG.S40
-rw-r--r--vm/mterp/armv6t2/OP_IF_EQ.S2
-rw-r--r--vm/mterp/armv6t2/OP_IF_GE.S2
-rw-r--r--vm/mterp/armv6t2/OP_IF_GT.S2
-rw-r--r--vm/mterp/armv6t2/OP_IF_LE.S2
-rw-r--r--vm/mterp/armv6t2/OP_IF_LT.S2
-rw-r--r--vm/mterp/armv6t2/OP_IF_NE.S2
-rw-r--r--vm/mterp/armv6t2/OP_IGET.S45
-rw-r--r--vm/mterp/armv6t2/OP_IGET_QUICK.S15
-rw-r--r--vm/mterp/armv6t2/OP_IGET_WIDE.S42
-rw-r--r--vm/mterp/armv6t2/OP_IGET_WIDE_QUICK.S15
-rw-r--r--vm/mterp/armv6t2/OP_INT_TO_BYTE.S2
-rw-r--r--vm/mterp/armv6t2/OP_INT_TO_CHAR.S2
-rw-r--r--vm/mterp/armv6t2/OP_INT_TO_DOUBLE.S2
-rw-r--r--vm/mterp/armv6t2/OP_INT_TO_FLOAT.S2
-rw-r--r--vm/mterp/armv6t2/OP_INT_TO_LONG.S2
-rw-r--r--vm/mterp/armv6t2/OP_INT_TO_SHORT.S2
-rw-r--r--vm/mterp/armv6t2/OP_IPUT.S45
-rw-r--r--vm/mterp/armv6t2/OP_IPUT_QUICK.S15
-rw-r--r--vm/mterp/armv6t2/OP_IPUT_WIDE.S39
-rw-r--r--vm/mterp/armv6t2/OP_IPUT_WIDE_QUICK.S15
-rw-r--r--vm/mterp/armv6t2/OP_LONG_TO_DOUBLE.S2
-rw-r--r--vm/mterp/armv6t2/OP_LONG_TO_FLOAT.S2
-rw-r--r--vm/mterp/armv6t2/OP_MOVE.S10
-rw-r--r--vm/mterp/armv6t2/OP_MOVE_WIDE.S12
-rw-r--r--vm/mterp/armv6t2/OP_MUL_DOUBLE_2ADDR.S2
-rw-r--r--vm/mterp/armv6t2/OP_MUL_FLOAT_2ADDR.S2
-rw-r--r--vm/mterp/armv6t2/OP_MUL_INT_2ADDR.S3
-rw-r--r--vm/mterp/armv6t2/OP_MUL_INT_LIT16.S3
-rw-r--r--vm/mterp/armv6t2/OP_MUL_LONG_2ADDR.S25
-rw-r--r--vm/mterp/armv6t2/OP_NEG_DOUBLE.S2
-rw-r--r--vm/mterp/armv6t2/OP_NEG_FLOAT.S2
-rw-r--r--vm/mterp/armv6t2/OP_NEG_INT.S2
-rw-r--r--vm/mterp/armv6t2/OP_NEG_LONG.S2
-rw-r--r--vm/mterp/armv6t2/OP_NOT_INT.S2
-rw-r--r--vm/mterp/armv6t2/OP_NOT_LONG.S2
-rw-r--r--vm/mterp/armv6t2/OP_OR_INT_2ADDR.S2
-rw-r--r--vm/mterp/armv6t2/OP_OR_INT_LIT16.S2
-rw-r--r--vm/mterp/armv6t2/OP_OR_LONG_2ADDR.S2
-rw-r--r--vm/mterp/armv6t2/OP_REM_DOUBLE_2ADDR.S3
-rw-r--r--vm/mterp/armv6t2/OP_REM_FLOAT_2ADDR.S3
-rw-r--r--vm/mterp/armv6t2/OP_REM_INT_2ADDR.S3
-rw-r--r--vm/mterp/armv6t2/OP_REM_INT_LIT16.S3
-rw-r--r--vm/mterp/armv6t2/OP_REM_LONG_2ADDR.S3
-rw-r--r--vm/mterp/armv6t2/OP_RSUB_INT.S3
-rw-r--r--vm/mterp/armv6t2/OP_SHL_INT_2ADDR.S2
-rw-r--r--vm/mterp/armv6t2/OP_SHL_LONG_2ADDR.S27
-rw-r--r--vm/mterp/armv6t2/OP_SHR_INT_2ADDR.S2
-rw-r--r--vm/mterp/armv6t2/OP_SHR_LONG_2ADDR.S27
-rw-r--r--vm/mterp/armv6t2/OP_SUB_DOUBLE_2ADDR.S2
-rw-r--r--vm/mterp/armv6t2/OP_SUB_FLOAT_2ADDR.S2
-rw-r--r--vm/mterp/armv6t2/OP_SUB_INT_2ADDR.S2
-rw-r--r--vm/mterp/armv6t2/OP_SUB_LONG_2ADDR.S2
-rw-r--r--vm/mterp/armv6t2/OP_USHR_INT_2ADDR.S2
-rw-r--r--vm/mterp/armv6t2/OP_USHR_LONG_2ADDR.S27
-rw-r--r--vm/mterp/armv6t2/OP_XOR_INT_2ADDR.S2
-rw-r--r--vm/mterp/armv6t2/OP_XOR_INT_LIT16.S2
-rw-r--r--vm/mterp/armv6t2/OP_XOR_LONG_2ADDR.S2
-rw-r--r--vm/mterp/armv6t2/bincmp.S30
-rw-r--r--vm/mterp/armv6t2/binop2addr.S32
-rw-r--r--vm/mterp/armv6t2/binopLit16.S29
-rw-r--r--vm/mterp/armv6t2/binopWide2addr.S34
-rw-r--r--vm/mterp/armv6t2/unop.S20
-rw-r--r--vm/mterp/armv6t2/unopNarrower.S23
-rw-r--r--vm/mterp/armv6t2/unopWide.S21
-rw-r--r--vm/mterp/armv6t2/unopWider.S20
-rw-r--r--vm/mterp/armv7-a/platform.S51
-rw-r--r--vm/mterp/c/OP_ADD_DOUBLE.c2
-rw-r--r--vm/mterp/c/OP_ADD_DOUBLE_2ADDR.c2
-rw-r--r--vm/mterp/c/OP_ADD_FLOAT.c2
-rw-r--r--vm/mterp/c/OP_ADD_FLOAT_2ADDR.c2
-rw-r--r--vm/mterp/c/OP_ADD_INT.c2
-rw-r--r--vm/mterp/c/OP_ADD_INT_2ADDR.c2
-rw-r--r--vm/mterp/c/OP_ADD_INT_LIT16.c2
-rw-r--r--vm/mterp/c/OP_ADD_INT_LIT8.c2
-rw-r--r--vm/mterp/c/OP_ADD_LONG.c2
-rw-r--r--vm/mterp/c/OP_ADD_LONG_2ADDR.c2
-rw-r--r--vm/mterp/c/OP_AGET.c2
-rw-r--r--vm/mterp/c/OP_AGET_BOOLEAN.c2
-rw-r--r--vm/mterp/c/OP_AGET_BYTE.c2
-rw-r--r--vm/mterp/c/OP_AGET_CHAR.c2
-rw-r--r--vm/mterp/c/OP_AGET_OBJECT.c2
-rw-r--r--vm/mterp/c/OP_AGET_SHORT.c2
-rw-r--r--vm/mterp/c/OP_AGET_WIDE.c2
-rw-r--r--vm/mterp/c/OP_AND_INT.c2
-rw-r--r--vm/mterp/c/OP_AND_INT_2ADDR.c2
-rw-r--r--vm/mterp/c/OP_AND_INT_LIT16.c2
-rw-r--r--vm/mterp/c/OP_AND_INT_LIT8.c2
-rw-r--r--vm/mterp/c/OP_AND_LONG.c2
-rw-r--r--vm/mterp/c/OP_AND_LONG_2ADDR.c2
-rw-r--r--vm/mterp/c/OP_APUT.c2
-rw-r--r--vm/mterp/c/OP_APUT_BOOLEAN.c2
-rw-r--r--vm/mterp/c/OP_APUT_BYTE.c2
-rw-r--r--vm/mterp/c/OP_APUT_CHAR.c2
-rw-r--r--vm/mterp/c/OP_APUT_OBJECT.c40
-rw-r--r--vm/mterp/c/OP_APUT_SHORT.c2
-rw-r--r--vm/mterp/c/OP_APUT_WIDE.c2
-rw-r--r--vm/mterp/c/OP_ARRAY_LENGTH.c15
-rw-r--r--vm/mterp/c/OP_BREAKPOINT.c29
-rw-r--r--vm/mterp/c/OP_CHECK_CAST.c32
-rw-r--r--vm/mterp/c/OP_CMPG_DOUBLE.c2
-rw-r--r--vm/mterp/c/OP_CMPG_FLOAT.c2
-rw-r--r--vm/mterp/c/OP_CMPL_DOUBLE.c2
-rw-r--r--vm/mterp/c/OP_CMPL_FLOAT.c2
-rw-r--r--vm/mterp/c/OP_CMP_LONG.c2
-rw-r--r--vm/mterp/c/OP_CONST.c12
-rw-r--r--vm/mterp/c/OP_CONST_16.c7
-rw-r--r--vm/mterp/c/OP_CONST_4.c11
-rw-r--r--vm/mterp/c/OP_CONST_CLASS.c18
-rw-r--r--vm/mterp/c/OP_CONST_HIGH16.c7
-rw-r--r--vm/mterp/c/OP_CONST_STRING.c18
-rw-r--r--vm/mterp/c/OP_CONST_STRING_JUMBO.c20
-rw-r--r--vm/mterp/c/OP_CONST_WIDE.c14
-rw-r--r--vm/mterp/c/OP_CONST_WIDE_16.c7
-rw-r--r--vm/mterp/c/OP_CONST_WIDE_32.c12
-rw-r--r--vm/mterp/c/OP_CONST_WIDE_HIGH16.c7
-rw-r--r--vm/mterp/c/OP_DIV_DOUBLE.c2
-rw-r--r--vm/mterp/c/OP_DIV_DOUBLE_2ADDR.c2
-rw-r--r--vm/mterp/c/OP_DIV_FLOAT.c2
-rw-r--r--vm/mterp/c/OP_DIV_FLOAT_2ADDR.c2
-rw-r--r--vm/mterp/c/OP_DIV_INT.c2
-rw-r--r--vm/mterp/c/OP_DIV_INT_2ADDR.c2
-rw-r--r--vm/mterp/c/OP_DIV_INT_LIT16.c2
-rw-r--r--vm/mterp/c/OP_DIV_INT_LIT8.c2
-rw-r--r--vm/mterp/c/OP_DIV_LONG.c2
-rw-r--r--vm/mterp/c/OP_DIV_LONG_2ADDR.c2
-rw-r--r--vm/mterp/c/OP_DOUBLE_TO_FLOAT.c2
-rw-r--r--vm/mterp/c/OP_DOUBLE_TO_INT.c3
-rw-r--r--vm/mterp/c/OP_DOUBLE_TO_LONG.c3
-rw-r--r--vm/mterp/c/OP_EXECUTE_INLINE.c59
-rw-r--r--vm/mterp/c/OP_EXECUTE_INLINE_RANGE.c43
-rw-r--r--vm/mterp/c/OP_FILLED_NEW_ARRAY.c3
-rw-r--r--vm/mterp/c/OP_FILLED_NEW_ARRAY_RANGE.c3
-rw-r--r--vm/mterp/c/OP_FILL_ARRAY_DATA.c28
-rw-r--r--vm/mterp/c/OP_FLOAT_TO_DOUBLE.c2
-rw-r--r--vm/mterp/c/OP_FLOAT_TO_INT.c3
-rw-r--r--vm/mterp/c/OP_FLOAT_TO_LONG.c3
-rw-r--r--vm/mterp/c/OP_GOTO.c11
-rw-r--r--vm/mterp/c/OP_GOTO_16.c14
-rw-r--r--vm/mterp/c/OP_GOTO_32.c15
-rw-r--r--vm/mterp/c/OP_IF_EQ.c2
-rw-r--r--vm/mterp/c/OP_IF_EQZ.c2
-rw-r--r--vm/mterp/c/OP_IF_GE.c2
-rw-r--r--vm/mterp/c/OP_IF_GEZ.c2
-rw-r--r--vm/mterp/c/OP_IF_GT.c2
-rw-r--r--vm/mterp/c/OP_IF_GTZ.c2
-rw-r--r--vm/mterp/c/OP_IF_LE.c2
-rw-r--r--vm/mterp/c/OP_IF_LEZ.c2
-rw-r--r--vm/mterp/c/OP_IF_LT.c2
-rw-r--r--vm/mterp/c/OP_IF_LTZ.c2
-rw-r--r--vm/mterp/c/OP_IF_NE.c2
-rw-r--r--vm/mterp/c/OP_IF_NEZ.c2
-rw-r--r--vm/mterp/c/OP_IGET.c2
-rw-r--r--vm/mterp/c/OP_IGET_BOOLEAN.c2
-rw-r--r--vm/mterp/c/OP_IGET_BYTE.c2
-rw-r--r--vm/mterp/c/OP_IGET_CHAR.c2
-rw-r--r--vm/mterp/c/OP_IGET_OBJECT.c2
-rw-r--r--vm/mterp/c/OP_IGET_OBJECT_QUICK.c2
-rw-r--r--vm/mterp/c/OP_IGET_OBJECT_VOLATILE.c2
-rw-r--r--vm/mterp/c/OP_IGET_QUICK.c2
-rw-r--r--vm/mterp/c/OP_IGET_SHORT.c2
-rw-r--r--vm/mterp/c/OP_IGET_VOLATILE.c2
-rw-r--r--vm/mterp/c/OP_IGET_WIDE.c2
-rw-r--r--vm/mterp/c/OP_IGET_WIDE_QUICK.c2
-rw-r--r--vm/mterp/c/OP_IGET_WIDE_VOLATILE.c2
-rw-r--r--vm/mterp/c/OP_INSTANCE_OF.c30
-rw-r--r--vm/mterp/c/OP_INT_TO_BYTE.c2
-rw-r--r--vm/mterp/c/OP_INT_TO_CHAR.c2
-rw-r--r--vm/mterp/c/OP_INT_TO_DOUBLE.c2
-rw-r--r--vm/mterp/c/OP_INT_TO_FLOAT.c2
-rw-r--r--vm/mterp/c/OP_INT_TO_LONG.c2
-rw-r--r--vm/mterp/c/OP_INT_TO_SHORT.c2
-rw-r--r--vm/mterp/c/OP_INVOKE_DIRECT.c3
-rw-r--r--vm/mterp/c/OP_INVOKE_DIRECT_EMPTY.c15
-rw-r--r--vm/mterp/c/OP_INVOKE_DIRECT_RANGE.c3
-rw-r--r--vm/mterp/c/OP_INVOKE_INTERFACE.c3
-rw-r--r--vm/mterp/c/OP_INVOKE_INTERFACE_RANGE.c3
-rw-r--r--vm/mterp/c/OP_INVOKE_STATIC.c3
-rw-r--r--vm/mterp/c/OP_INVOKE_STATIC_RANGE.c3
-rw-r--r--vm/mterp/c/OP_INVOKE_SUPER.c3
-rw-r--r--vm/mterp/c/OP_INVOKE_SUPER_QUICK.c3
-rw-r--r--vm/mterp/c/OP_INVOKE_SUPER_QUICK_RANGE.c3
-rw-r--r--vm/mterp/c/OP_INVOKE_SUPER_RANGE.c3
-rw-r--r--vm/mterp/c/OP_INVOKE_VIRTUAL.c3
-rw-r--r--vm/mterp/c/OP_INVOKE_VIRTUAL_QUICK.c3
-rw-r--r--vm/mterp/c/OP_INVOKE_VIRTUAL_QUICK_RANGE.c3
-rw-r--r--vm/mterp/c/OP_INVOKE_VIRTUAL_RANGE.c3
-rw-r--r--vm/mterp/c/OP_IPUT.c2
-rw-r--r--vm/mterp/c/OP_IPUT_BOOLEAN.c2
-rw-r--r--vm/mterp/c/OP_IPUT_BYTE.c2
-rw-r--r--vm/mterp/c/OP_IPUT_CHAR.c2
-rw-r--r--vm/mterp/c/OP_IPUT_OBJECT.c13
-rw-r--r--vm/mterp/c/OP_IPUT_OBJECT_QUICK.c2
-rw-r--r--vm/mterp/c/OP_IPUT_OBJECT_VOLATILE.c2
-rw-r--r--vm/mterp/c/OP_IPUT_QUICK.c2
-rw-r--r--vm/mterp/c/OP_IPUT_SHORT.c2
-rw-r--r--vm/mterp/c/OP_IPUT_VOLATILE.c2
-rw-r--r--vm/mterp/c/OP_IPUT_WIDE.c2
-rw-r--r--vm/mterp/c/OP_IPUT_WIDE_QUICK.c2
-rw-r--r--vm/mterp/c/OP_IPUT_WIDE_VOLATILE.c2
-rw-r--r--vm/mterp/c/OP_LONG_TO_DOUBLE.c2
-rw-r--r--vm/mterp/c/OP_LONG_TO_FLOAT.c2
-rw-r--r--vm/mterp/c/OP_LONG_TO_INT.c2
-rw-r--r--vm/mterp/c/OP_MONITOR_ENTER.c20
-rw-r--r--vm/mterp/c/OP_MONITOR_EXIT.c30
-rw-r--r--vm/mterp/c/OP_MOVE.c9
-rw-r--r--vm/mterp/c/OP_MOVE_16.c9
-rw-r--r--vm/mterp/c/OP_MOVE_EXCEPTION.c8
-rw-r--r--vm/mterp/c/OP_MOVE_FROM16.c9
-rw-r--r--vm/mterp/c/OP_MOVE_OBJECT.c1
-rw-r--r--vm/mterp/c/OP_MOVE_OBJECT_16.c1
-rw-r--r--vm/mterp/c/OP_MOVE_OBJECT_FROM16.c1
-rw-r--r--vm/mterp/c/OP_MOVE_RESULT.c8
-rw-r--r--vm/mterp/c/OP_MOVE_RESULT_OBJECT.c1
-rw-r--r--vm/mterp/c/OP_MOVE_RESULT_WIDE.c6
-rw-r--r--vm/mterp/c/OP_MOVE_WIDE.c10
-rw-r--r--vm/mterp/c/OP_MOVE_WIDE_16.c8
-rw-r--r--vm/mterp/c/OP_MOVE_WIDE_FROM16.c8
-rw-r--r--vm/mterp/c/OP_MUL_DOUBLE.c2
-rw-r--r--vm/mterp/c/OP_MUL_DOUBLE_2ADDR.c2
-rw-r--r--vm/mterp/c/OP_MUL_FLOAT.c2
-rw-r--r--vm/mterp/c/OP_MUL_FLOAT_2ADDR.c2
-rw-r--r--vm/mterp/c/OP_MUL_INT.c2
-rw-r--r--vm/mterp/c/OP_MUL_INT_2ADDR.c2
-rw-r--r--vm/mterp/c/OP_MUL_INT_LIT16.c2
-rw-r--r--vm/mterp/c/OP_MUL_INT_LIT8.c2
-rw-r--r--vm/mterp/c/OP_MUL_LONG.c2
-rw-r--r--vm/mterp/c/OP_MUL_LONG_2ADDR.c2
-rw-r--r--vm/mterp/c/OP_NEG_DOUBLE.c2
-rw-r--r--vm/mterp/c/OP_NEG_FLOAT.c2
-rw-r--r--vm/mterp/c/OP_NEG_INT.c2
-rw-r--r--vm/mterp/c/OP_NEG_LONG.c2
-rw-r--r--vm/mterp/c/OP_NEW_ARRAY.c35
-rw-r--r--vm/mterp/c/OP_NEW_INSTANCE.c45
-rw-r--r--vm/mterp/c/OP_NOP.c3
-rw-r--r--vm/mterp/c/OP_NOT_INT.c2
-rw-r--r--vm/mterp/c/OP_NOT_LONG.c2
-rw-r--r--vm/mterp/c/OP_OR_INT.c2
-rw-r--r--vm/mterp/c/OP_OR_INT_2ADDR.c2
-rw-r--r--vm/mterp/c/OP_OR_INT_LIT16.c2
-rw-r--r--vm/mterp/c/OP_OR_INT_LIT8.c2
-rw-r--r--vm/mterp/c/OP_OR_LONG.c2
-rw-r--r--vm/mterp/c/OP_OR_LONG_2ADDR.c2
-rw-r--r--vm/mterp/c/OP_PACKED_SWITCH.c29
-rw-r--r--vm/mterp/c/OP_REM_DOUBLE.c13
-rw-r--r--vm/mterp/c/OP_REM_DOUBLE_2ADDR.c8
-rw-r--r--vm/mterp/c/OP_REM_FLOAT.c13
-rw-r--r--vm/mterp/c/OP_REM_FLOAT_2ADDR.c8
-rw-r--r--vm/mterp/c/OP_REM_INT.c2
-rw-r--r--vm/mterp/c/OP_REM_INT_2ADDR.c2
-rw-r--r--vm/mterp/c/OP_REM_INT_LIT16.c2
-rw-r--r--vm/mterp/c/OP_REM_INT_LIT8.c2
-rw-r--r--vm/mterp/c/OP_REM_LONG.c2
-rw-r--r--vm/mterp/c/OP_REM_LONG_2ADDR.c2
-rw-r--r--vm/mterp/c/OP_RETURN.c7
-rw-r--r--vm/mterp/c/OP_RETURN_OBJECT.c1
-rw-r--r--vm/mterp/c/OP_RETURN_VOID.c7
-rw-r--r--vm/mterp/c/OP_RETURN_WIDE.c6
-rw-r--r--vm/mterp/c/OP_RSUB_INT.c10
-rw-r--r--vm/mterp/c/OP_RSUB_INT_LIT8.c12
-rw-r--r--vm/mterp/c/OP_SGET.c2
-rw-r--r--vm/mterp/c/OP_SGET_BOOLEAN.c2
-rw-r--r--vm/mterp/c/OP_SGET_BYTE.c2
-rw-r--r--vm/mterp/c/OP_SGET_CHAR.c2
-rw-r--r--vm/mterp/c/OP_SGET_OBJECT.c2
-rw-r--r--vm/mterp/c/OP_SGET_OBJECT_VOLATILE.c2
-rw-r--r--vm/mterp/c/OP_SGET_SHORT.c2
-rw-r--r--vm/mterp/c/OP_SGET_VOLATILE.c2
-rw-r--r--vm/mterp/c/OP_SGET_WIDE.c2
-rw-r--r--vm/mterp/c/OP_SGET_WIDE_VOLATILE.c2
-rw-r--r--vm/mterp/c/OP_SHL_INT.c2
-rw-r--r--vm/mterp/c/OP_SHL_INT_2ADDR.c2
-rw-r--r--vm/mterp/c/OP_SHL_INT_LIT8.c2
-rw-r--r--vm/mterp/c/OP_SHL_LONG.c2
-rw-r--r--vm/mterp/c/OP_SHL_LONG_2ADDR.c2
-rw-r--r--vm/mterp/c/OP_SHR_INT.c2
-rw-r--r--vm/mterp/c/OP_SHR_INT_2ADDR.c2
-rw-r--r--vm/mterp/c/OP_SHR_INT_LIT8.c2
-rw-r--r--vm/mterp/c/OP_SHR_LONG.c2
-rw-r--r--vm/mterp/c/OP_SHR_LONG_2ADDR.c2
-rw-r--r--vm/mterp/c/OP_SPARSE_SWITCH.c29
-rw-r--r--vm/mterp/c/OP_SPUT.c2
-rw-r--r--vm/mterp/c/OP_SPUT_BOOLEAN.c2
-rw-r--r--vm/mterp/c/OP_SPUT_BYTE.c2
-rw-r--r--vm/mterp/c/OP_SPUT_CHAR.c2
-rw-r--r--vm/mterp/c/OP_SPUT_OBJECT.c2
-rw-r--r--vm/mterp/c/OP_SPUT_OBJECT_VOLATILE.c2
-rw-r--r--vm/mterp/c/OP_SPUT_SHORT.c2
-rw-r--r--vm/mterp/c/OP_SPUT_VOLATILE.c2
-rw-r--r--vm/mterp/c/OP_SPUT_WIDE.c2
-rw-r--r--vm/mterp/c/OP_SPUT_WIDE_VOLATILE.c2
-rw-r--r--vm/mterp/c/OP_SUB_DOUBLE.c2
-rw-r--r--vm/mterp/c/OP_SUB_DOUBLE_2ADDR.c2
-rw-r--r--vm/mterp/c/OP_SUB_FLOAT.c2
-rw-r--r--vm/mterp/c/OP_SUB_FLOAT_2ADDR.c2
-rw-r--r--vm/mterp/c/OP_SUB_INT.c2
-rw-r--r--vm/mterp/c/OP_SUB_INT_2ADDR.c2
-rw-r--r--vm/mterp/c/OP_SUB_LONG.c2
-rw-r--r--vm/mterp/c/OP_SUB_LONG_2ADDR.c2
-rw-r--r--vm/mterp/c/OP_THROW.c24
-rw-r--r--vm/mterp/c/OP_THROW_VERIFICATION_ERROR.c7
-rw-r--r--vm/mterp/c/OP_UNUSED_3E.c2
-rw-r--r--vm/mterp/c/OP_UNUSED_3F.c2
-rw-r--r--vm/mterp/c/OP_UNUSED_40.c2
-rw-r--r--vm/mterp/c/OP_UNUSED_41.c2
-rw-r--r--vm/mterp/c/OP_UNUSED_42.c2
-rw-r--r--vm/mterp/c/OP_UNUSED_43.c2
-rw-r--r--vm/mterp/c/OP_UNUSED_73.c2
-rw-r--r--vm/mterp/c/OP_UNUSED_79.c2
-rw-r--r--vm/mterp/c/OP_UNUSED_7A.c2
-rw-r--r--vm/mterp/c/OP_UNUSED_F1.c2
-rw-r--r--vm/mterp/c/OP_UNUSED_FF.c8
-rw-r--r--vm/mterp/c/OP_USHR_INT.c2
-rw-r--r--vm/mterp/c/OP_USHR_INT_2ADDR.c2
-rw-r--r--vm/mterp/c/OP_USHR_INT_LIT8.c2
-rw-r--r--vm/mterp/c/OP_USHR_LONG.c2
-rw-r--r--vm/mterp/c/OP_USHR_LONG_2ADDR.c2
-rw-r--r--vm/mterp/c/OP_XOR_INT.c2
-rw-r--r--vm/mterp/c/OP_XOR_INT_2ADDR.c2
-rw-r--r--vm/mterp/c/OP_XOR_INT_LIT16.c2
-rw-r--r--vm/mterp/c/OP_XOR_INT_LIT8.c2
-rw-r--r--vm/mterp/c/OP_XOR_LONG.c2
-rw-r--r--vm/mterp/c/OP_XOR_LONG_2ADDR.c2
-rw-r--r--vm/mterp/c/gotoTargets.c992
-rw-r--r--vm/mterp/c/header.c405
-rw-r--r--vm/mterp/c/opcommon.c656
-rw-r--r--vm/mterp/common/FindInterface.h37
-rw-r--r--vm/mterp/common/asm-constants.h326
-rw-r--r--vm/mterp/common/jit-config.h23
-rw-r--r--vm/mterp/config-allstubs44
-rw-r--r--vm/mterp/config-armv4t68
-rw-r--r--vm/mterp/config-armv5te54
-rw-r--r--vm/mterp/config-armv5te-vfp104
-rw-r--r--vm/mterp/config-armv7-a166
-rw-r--r--vm/mterp/config-armv7-a-neon166
-rw-r--r--vm/mterp/config-portdbg49
-rw-r--r--vm/mterp/config-portstd48
-rw-r--r--vm/mterp/config-x8656
-rw-r--r--vm/mterp/config-x86-atom300
-rw-r--r--vm/mterp/cstubs/enddefs.c9
-rw-r--r--vm/mterp/cstubs/entry.c80
-rw-r--r--vm/mterp/cstubs/stubdefs.c124
-rwxr-xr-xvm/mterp/gen-mterp.py479
-rw-r--r--vm/mterp/out/InterpAsm-allstubs.S35
-rw-r--r--vm/mterp/out/InterpAsm-armv4t.S11077
-rw-r--r--vm/mterp/out/InterpAsm-armv5te-vfp.S10615
-rw-r--r--vm/mterp/out/InterpAsm-armv5te.S11073
-rw-r--r--vm/mterp/out/InterpAsm-armv7-a-neon.S10549
-rw-r--r--vm/mterp/out/InterpAsm-armv7-a.S10549
-rw-r--r--vm/mterp/out/InterpAsm-x86-atom.S18547
-rw-r--r--vm/mterp/out/InterpAsm-x86.S9425
-rw-r--r--vm/mterp/out/InterpC-allstubs.c4132
-rw-r--r--vm/mterp/out/InterpC-armv4t.c1289
-rw-r--r--vm/mterp/out/InterpC-armv5te-vfp.c1289
-rw-r--r--vm/mterp/out/InterpC-armv5te.c1289
-rw-r--r--vm/mterp/out/InterpC-armv7-a-neon.c1289
-rw-r--r--vm/mterp/out/InterpC-armv7-a.c1289
-rw-r--r--vm/mterp/out/InterpC-portdbg.c4442
-rw-r--r--vm/mterp/out/InterpC-portstd.c4192
-rw-r--r--vm/mterp/out/InterpC-x86-atom.c2326
-rw-r--r--vm/mterp/out/InterpC-x86.c2263
-rw-r--r--vm/mterp/portable/debug.c239
-rw-r--r--vm/mterp/portable/enddefs.c40
-rw-r--r--vm/mterp/portable/entry.c127
-rw-r--r--vm/mterp/portable/portdbg.c17
-rw-r--r--vm/mterp/portable/portstd.c8
-rw-r--r--vm/mterp/portable/stubdefs.c96
-rwxr-xr-xvm/mterp/rebuild.sh29
-rw-r--r--vm/mterp/x86-atom/OP_ADD_DOUBLE.S20
-rw-r--r--vm/mterp/x86-atom/OP_ADD_DOUBLE_2ADDR.S20
-rw-r--r--vm/mterp/x86-atom/OP_ADD_FLOAT.S20
-rw-r--r--vm/mterp/x86-atom/OP_ADD_FLOAT_2ADDR.S20
-rw-r--r--vm/mterp/x86-atom/OP_ADD_INT.S20
-rw-r--r--vm/mterp/x86-atom/OP_ADD_INT_2ADDR.S20
-rw-r--r--vm/mterp/x86-atom/OP_ADD_INT_LIT16.S20
-rw-r--r--vm/mterp/x86-atom/OP_ADD_INT_LIT8.S20
-rw-r--r--vm/mterp/x86-atom/OP_ADD_LONG.S20
-rw-r--r--vm/mterp/x86-atom/OP_ADD_LONG_2ADDR.S20
-rw-r--r--vm/mterp/x86-atom/OP_AGET.S53
-rw-r--r--vm/mterp/x86-atom/OP_AGET_BOOLEAN.S20
-rw-r--r--vm/mterp/x86-atom/OP_AGET_BYTE.S20
-rw-r--r--vm/mterp/x86-atom/OP_AGET_CHAR.S20
-rw-r--r--vm/mterp/x86-atom/OP_AGET_OBJECT.S20
-rw-r--r--vm/mterp/x86-atom/OP_AGET_SHORT.S20
-rw-r--r--vm/mterp/x86-atom/OP_AGET_WIDE.S43
-rw-r--r--vm/mterp/x86-atom/OP_AND_INT.S20
-rw-r--r--vm/mterp/x86-atom/OP_AND_INT_2ADDR.S20
-rw-r--r--vm/mterp/x86-atom/OP_AND_INT_LIT16.S20
-rw-r--r--vm/mterp/x86-atom/OP_AND_INT_LIT8.S20
-rw-r--r--vm/mterp/x86-atom/OP_AND_LONG.S20
-rw-r--r--vm/mterp/x86-atom/OP_AND_LONG_2ADDR.S20
-rw-r--r--vm/mterp/x86-atom/OP_APUT.S50
-rw-r--r--vm/mterp/x86-atom/OP_APUT_BOOLEAN.S20
-rw-r--r--vm/mterp/x86-atom/OP_APUT_BYTE.S20
-rw-r--r--vm/mterp/x86-atom/OP_APUT_CHAR.S20
-rw-r--r--vm/mterp/x86-atom/OP_APUT_OBJECT.S77
-rw-r--r--vm/mterp/x86-atom/OP_APUT_SHORT.S20
-rw-r--r--vm/mterp/x86-atom/OP_APUT_WIDE.S43
-rw-r--r--vm/mterp/x86-atom/OP_ARRAY_LENGTH.S40
-rw-r--r--vm/mterp/x86-atom/OP_BREAKPOINT.S0
-rw-r--r--vm/mterp/x86-atom/OP_CHECK_CAST.S113
-rw-r--r--vm/mterp/x86-atom/OP_CMPG_DOUBLE.S20
-rw-r--r--vm/mterp/x86-atom/OP_CMPG_FLOAT.S20
-rw-r--r--vm/mterp/x86-atom/OP_CMPL_DOUBLE.S20
-rw-r--r--vm/mterp/x86-atom/OP_CMPL_FLOAT.S63
-rw-r--r--vm/mterp/x86-atom/OP_CMP_LONG.S55
-rw-r--r--vm/mterp/x86-atom/OP_CONST.S36
-rw-r--r--vm/mterp/x86-atom/OP_CONST_16.S34
-rw-r--r--vm/mterp/x86-atom/OP_CONST_4.S37
-rw-r--r--vm/mterp/x86-atom/OP_CONST_CLASS.S67
-rw-r--r--vm/mterp/x86-atom/OP_CONST_HIGH16.S35
-rw-r--r--vm/mterp/x86-atom/OP_CONST_STRING.S65
-rw-r--r--vm/mterp/x86-atom/OP_CONST_STRING_JUMBO.S66
-rw-r--r--vm/mterp/x86-atom/OP_CONST_WIDE.S42
-rw-r--r--vm/mterp/x86-atom/OP_CONST_WIDE_16.S37
-rw-r--r--vm/mterp/x86-atom/OP_CONST_WIDE_32.S39
-rw-r--r--vm/mterp/x86-atom/OP_CONST_WIDE_HIGH16.S35
-rw-r--r--vm/mterp/x86-atom/OP_DIV_DOUBLE.S37
-rw-r--r--vm/mterp/x86-atom/OP_DIV_DOUBLE_2ADDR.S37
-rw-r--r--vm/mterp/x86-atom/OP_DIV_FLOAT.S37
-rw-r--r--vm/mterp/x86-atom/OP_DIV_FLOAT_2ADDR.S37
-rw-r--r--vm/mterp/x86-atom/OP_DIV_INT.S20
-rw-r--r--vm/mterp/x86-atom/OP_DIV_INT_2ADDR.S20
-rw-r--r--vm/mterp/x86-atom/OP_DIV_INT_LIT16.S20
-rw-r--r--vm/mterp/x86-atom/OP_DIV_INT_LIT8.S20
-rw-r--r--vm/mterp/x86-atom/OP_DIV_LONG.S20
-rw-r--r--vm/mterp/x86-atom/OP_DIV_LONG_2ADDR.S20
-rw-r--r--vm/mterp/x86-atom/OP_DOUBLE_TO_FLOAT.S36
-rw-r--r--vm/mterp/x86-atom/OP_DOUBLE_TO_INT.S68
-rw-r--r--vm/mterp/x86-atom/OP_DOUBLE_TO_LONG.S71
-rw-r--r--vm/mterp/x86-atom/OP_EXECUTE_INLINE.S86
-rw-r--r--vm/mterp/x86-atom/OP_EXECUTE_INLINE_RANGE.S75
-rw-r--r--vm/mterp/x86-atom/OP_FILLED_NEW_ARRAY.S173
-rw-r--r--vm/mterp/x86-atom/OP_FILLED_NEW_ARRAY_RANGE.S20
-rw-r--r--vm/mterp/x86-atom/OP_FILL_ARRAY_DATA.S46
-rw-r--r--vm/mterp/x86-atom/OP_FLOAT_TO_DOUBLE.S36
-rw-r--r--vm/mterp/x86-atom/OP_FLOAT_TO_INT.S68
-rw-r--r--vm/mterp/x86-atom/OP_FLOAT_TO_LONG.S71
-rw-r--r--vm/mterp/x86-atom/OP_GOTO.S36
-rw-r--r--vm/mterp/x86-atom/OP_GOTO_16.S34
-rw-r--r--vm/mterp/x86-atom/OP_GOTO_32.S37
-rw-r--r--vm/mterp/x86-atom/OP_IF_EQ.S20
-rw-r--r--vm/mterp/x86-atom/OP_IF_EQZ.S20
-rw-r--r--vm/mterp/x86-atom/OP_IF_GE.S20
-rw-r--r--vm/mterp/x86-atom/OP_IF_GEZ.S20
-rw-r--r--vm/mterp/x86-atom/OP_IF_GT.S20
-rw-r--r--vm/mterp/x86-atom/OP_IF_GTZ.S20
-rw-r--r--vm/mterp/x86-atom/OP_IF_LE.S20
-rw-r--r--vm/mterp/x86-atom/OP_IF_LEZ.S20
-rw-r--r--vm/mterp/x86-atom/OP_IF_LT.S20
-rw-r--r--vm/mterp/x86-atom/OP_IF_LTZ.S20
-rw-r--r--vm/mterp/x86-atom/OP_IF_NE.S20
-rw-r--r--vm/mterp/x86-atom/OP_IF_NEZ.S20
-rw-r--r--vm/mterp/x86-atom/OP_IGET.S80
-rw-r--r--vm/mterp/x86-atom/OP_IGET_BOOLEAN.S20
-rw-r--r--vm/mterp/x86-atom/OP_IGET_BYTE.S20
-rw-r--r--vm/mterp/x86-atom/OP_IGET_CHAR.S20
-rw-r--r--vm/mterp/x86-atom/OP_IGET_OBJECT.S20
-rw-r--r--vm/mterp/x86-atom/OP_IGET_OBJECT_QUICK.S20
-rw-r--r--vm/mterp/x86-atom/OP_IGET_OBJECT_VOLATILE.S1
-rw-r--r--vm/mterp/x86-atom/OP_IGET_QUICK.S38
-rw-r--r--vm/mterp/x86-atom/OP_IGET_SHORT.S20
-rw-r--r--vm/mterp/x86-atom/OP_IGET_VOLATILE.S1
-rw-r--r--vm/mterp/x86-atom/OP_IGET_WIDE.S74
-rw-r--r--vm/mterp/x86-atom/OP_IGET_WIDE_QUICK.S38
-rw-r--r--vm/mterp/x86-atom/OP_INSTANCE_OF.S121
-rw-r--r--vm/mterp/x86-atom/OP_INT_TO_BYTE.S20
-rw-r--r--vm/mterp/x86-atom/OP_INT_TO_CHAR.S20
-rw-r--r--vm/mterp/x86-atom/OP_INT_TO_DOUBLE.S37
-rw-r--r--vm/mterp/x86-atom/OP_INT_TO_FLOAT.S36
-rw-r--r--vm/mterp/x86-atom/OP_INT_TO_LONG.S40
-rw-r--r--vm/mterp/x86-atom/OP_INT_TO_SHORT.S20
-rw-r--r--vm/mterp/x86-atom/OP_INVOKE_DIRECT.S92
-rw-r--r--vm/mterp/x86-atom/OP_INVOKE_DIRECT_EMPTY.S26
-rw-r--r--vm/mterp/x86-atom/OP_INVOKE_DIRECT_RANGE.S20
-rw-r--r--vm/mterp/x86-atom/OP_INVOKE_INTERFACE.S76
-rw-r--r--vm/mterp/x86-atom/OP_INVOKE_INTERFACE_RANGE.S20
-rw-r--r--vm/mterp/x86-atom/OP_INVOKE_STATIC.S70
-rw-r--r--vm/mterp/x86-atom/OP_INVOKE_STATIC_RANGE.S20
-rw-r--r--vm/mterp/x86-atom/OP_INVOKE_SUPER.S105
-rw-r--r--vm/mterp/x86-atom/OP_INVOKE_SUPER_QUICK.S40
-rw-r--r--vm/mterp/x86-atom/OP_INVOKE_SUPER_QUICK_RANGE.S20
-rw-r--r--vm/mterp/x86-atom/OP_INVOKE_SUPER_RANGE.S20
-rw-r--r--vm/mterp/x86-atom/OP_INVOKE_VIRTUAL.S93
-rw-r--r--vm/mterp/x86-atom/OP_INVOKE_VIRTUAL_QUICK.S38
-rw-r--r--vm/mterp/x86-atom/OP_INVOKE_VIRTUAL_QUICK_RANGE.S20
-rw-r--r--vm/mterp/x86-atom/OP_INVOKE_VIRTUAL_RANGE.S20
-rw-r--r--vm/mterp/x86-atom/OP_IPUT.S76
-rw-r--r--vm/mterp/x86-atom/OP_IPUT_BOOLEAN.S20
-rw-r--r--vm/mterp/x86-atom/OP_IPUT_BYTE.S20
-rw-r--r--vm/mterp/x86-atom/OP_IPUT_CHAR.S20
-rw-r--r--vm/mterp/x86-atom/OP_IPUT_OBJECT.S81
-rw-r--r--vm/mterp/x86-atom/OP_IPUT_OBJECT_QUICK.S44
-rw-r--r--vm/mterp/x86-atom/OP_IPUT_OBJECT_VOLATILE.S1
-rw-r--r--vm/mterp/x86-atom/OP_IPUT_QUICK.S37
-rw-r--r--vm/mterp/x86-atom/OP_IPUT_SHORT.S20
-rw-r--r--vm/mterp/x86-atom/OP_IPUT_VOLATILE.S1
-rw-r--r--vm/mterp/x86-atom/OP_IPUT_WIDE.S74
-rw-r--r--vm/mterp/x86-atom/OP_IPUT_WIDE_QUICK.S38
-rw-r--r--vm/mterp/x86-atom/OP_LONG_TO_DOUBLE.S37
-rw-r--r--vm/mterp/x86-atom/OP_LONG_TO_FLOAT.S37
-rw-r--r--vm/mterp/x86-atom/OP_LONG_TO_INT.S20
-rw-r--r--vm/mterp/x86-atom/OP_MONITOR_ENTER.S58
-rw-r--r--vm/mterp/x86-atom/OP_MONITOR_EXIT.S46
-rw-r--r--vm/mterp/x86-atom/OP_MOVE.S38
-rw-r--r--vm/mterp/x86-atom/OP_MOVE_16.S36
-rw-r--r--vm/mterp/x86-atom/OP_MOVE_EXCEPTION.S38
-rw-r--r--vm/mterp/x86-atom/OP_MOVE_FROM16.S35
-rw-r--r--vm/mterp/x86-atom/OP_MOVE_OBJECT.S20
-rw-r--r--vm/mterp/x86-atom/OP_MOVE_OBJECT_16.S20
-rw-r--r--vm/mterp/x86-atom/OP_MOVE_OBJECT_FROM16.S20
-rw-r--r--vm/mterp/x86-atom/OP_MOVE_RESULT.S38
-rw-r--r--vm/mterp/x86-atom/OP_MOVE_RESULT_OBJECT.S20
-rw-r--r--vm/mterp/x86-atom/OP_MOVE_RESULT_WIDE.S38
-rw-r--r--vm/mterp/x86-atom/OP_MOVE_WIDE.S37
-rw-r--r--vm/mterp/x86-atom/OP_MOVE_WIDE_16.S36
-rw-r--r--vm/mterp/x86-atom/OP_MOVE_WIDE_FROM16.S34
-rw-r--r--vm/mterp/x86-atom/OP_MUL_DOUBLE.S20
-rw-r--r--vm/mterp/x86-atom/OP_MUL_DOUBLE_2ADDR.S20
-rw-r--r--vm/mterp/x86-atom/OP_MUL_FLOAT.S20
-rw-r--r--vm/mterp/x86-atom/OP_MUL_FLOAT_2ADDR.S20
-rw-r--r--vm/mterp/x86-atom/OP_MUL_INT.S20
-rw-r--r--vm/mterp/x86-atom/OP_MUL_INT_2ADDR.S20
-rw-r--r--vm/mterp/x86-atom/OP_MUL_INT_LIT16.S20
-rw-r--r--vm/mterp/x86-atom/OP_MUL_INT_LIT8.S20
-rw-r--r--vm/mterp/x86-atom/OP_MUL_LONG.S71
-rw-r--r--vm/mterp/x86-atom/OP_MUL_LONG_2ADDR.S72
-rw-r--r--vm/mterp/x86-atom/OP_NEG_DOUBLE.S20
-rw-r--r--vm/mterp/x86-atom/OP_NEG_FLOAT.S20
-rw-r--r--vm/mterp/x86-atom/OP_NEG_INT.S20
-rw-r--r--vm/mterp/x86-atom/OP_NEG_LONG.S20
-rw-r--r--vm/mterp/x86-atom/OP_NEW_ARRAY.S92
-rw-r--r--vm/mterp/x86-atom/OP_NEW_INSTANCE.S147
-rw-r--r--vm/mterp/x86-atom/OP_NOP.S41
-rw-r--r--vm/mterp/x86-atom/OP_NOT_INT.S20
-rw-r--r--vm/mterp/x86-atom/OP_NOT_LONG.S20
-rw-r--r--vm/mterp/x86-atom/OP_OR_INT.S20
-rw-r--r--vm/mterp/x86-atom/OP_OR_INT_2ADDR.S20
-rw-r--r--vm/mterp/x86-atom/OP_OR_INT_LIT16.S20
-rw-r--r--vm/mterp/x86-atom/OP_OR_INT_LIT8.S20
-rw-r--r--vm/mterp/x86-atom/OP_OR_LONG.S20
-rw-r--r--vm/mterp/x86-atom/OP_OR_LONG_2ADDR.S20
-rw-r--r--vm/mterp/x86-atom/OP_PACKED_SWITCH.S52
-rw-r--r--vm/mterp/x86-atom/OP_REM_DOUBLE.S51
-rw-r--r--vm/mterp/x86-atom/OP_REM_DOUBLE_2ADDR.S52
-rw-r--r--vm/mterp/x86-atom/OP_REM_FLOAT.S43
-rw-r--r--vm/mterp/x86-atom/OP_REM_FLOAT_2ADDR.S44
-rw-r--r--vm/mterp/x86-atom/OP_REM_INT.S20
-rw-r--r--vm/mterp/x86-atom/OP_REM_INT_2ADDR.S20
-rw-r--r--vm/mterp/x86-atom/OP_REM_INT_LIT16.S20
-rw-r--r--vm/mterp/x86-atom/OP_REM_INT_LIT8.S20
-rw-r--r--vm/mterp/x86-atom/OP_REM_LONG.S20
-rw-r--r--vm/mterp/x86-atom/OP_REM_LONG_2ADDR.S20
-rw-r--r--vm/mterp/x86-atom/OP_RETURN.S20
-rw-r--r--vm/mterp/x86-atom/OP_RETURN_COMMON.S34
-rw-r--r--vm/mterp/x86-atom/OP_RETURN_OBJECT.S20
-rw-r--r--vm/mterp/x86-atom/OP_RETURN_VOID.S20
-rw-r--r--vm/mterp/x86-atom/OP_RETURN_WIDE.S34
-rw-r--r--vm/mterp/x86-atom/OP_RSUB_INT.S39
-rw-r--r--vm/mterp/x86-atom/OP_RSUB_INT_LIT8.S36
-rw-r--r--vm/mterp/x86-atom/OP_SGET.S60
-rw-r--r--vm/mterp/x86-atom/OP_SGET_BOOLEAN.S20
-rw-r--r--vm/mterp/x86-atom/OP_SGET_BYTE.S20
-rw-r--r--vm/mterp/x86-atom/OP_SGET_CHAR.S20
-rw-r--r--vm/mterp/x86-atom/OP_SGET_OBJECT.S20
-rw-r--r--vm/mterp/x86-atom/OP_SGET_OBJECT_VOLATILE.S1
-rw-r--r--vm/mterp/x86-atom/OP_SGET_SHORT.S20
-rw-r--r--vm/mterp/x86-atom/OP_SGET_VOLATILE.S1
-rw-r--r--vm/mterp/x86-atom/OP_SGET_WIDE.S65
-rw-r--r--vm/mterp/x86-atom/OP_SHL_INT.S20
-rw-r--r--vm/mterp/x86-atom/OP_SHL_INT_2ADDR.S20
-rw-r--r--vm/mterp/x86-atom/OP_SHL_INT_LIT8.S20
-rw-r--r--vm/mterp/x86-atom/OP_SHL_LONG.S40
-rw-r--r--vm/mterp/x86-atom/OP_SHL_LONG_2ADDR.S41
-rw-r--r--vm/mterp/x86-atom/OP_SHR_INT.S20
-rw-r--r--vm/mterp/x86-atom/OP_SHR_INT_2ADDR.S20
-rw-r--r--vm/mterp/x86-atom/OP_SHR_INT_LIT8.S20
-rw-r--r--vm/mterp/x86-atom/OP_SHR_LONG.S53
-rw-r--r--vm/mterp/x86-atom/OP_SHR_LONG_2ADDR.S54
-rw-r--r--vm/mterp/x86-atom/OP_SPARSE_SWITCH.S20
-rw-r--r--vm/mterp/x86-atom/OP_SPUT.S60
-rw-r--r--vm/mterp/x86-atom/OP_SPUT_BOOLEAN.S20
-rw-r--r--vm/mterp/x86-atom/OP_SPUT_BYTE.S20
-rw-r--r--vm/mterp/x86-atom/OP_SPUT_CHAR.S20
-rw-r--r--vm/mterp/x86-atom/OP_SPUT_OBJECT.S70
-rw-r--r--vm/mterp/x86-atom/OP_SPUT_OBJECT_VOLATILE.S1
-rw-r--r--vm/mterp/x86-atom/OP_SPUT_SHORT.S20
-rw-r--r--vm/mterp/x86-atom/OP_SPUT_VOLATILE.S1
-rw-r--r--vm/mterp/x86-atom/OP_SPUT_WIDE.S65
-rw-r--r--vm/mterp/x86-atom/OP_SUB_DOUBLE.S20
-rw-r--r--vm/mterp/x86-atom/OP_SUB_DOUBLE_2ADDR.S20
-rw-r--r--vm/mterp/x86-atom/OP_SUB_FLOAT.S20
-rw-r--r--vm/mterp/x86-atom/OP_SUB_FLOAT_2ADDR.S20
-rw-r--r--vm/mterp/x86-atom/OP_SUB_INT.S20
-rw-r--r--vm/mterp/x86-atom/OP_SUB_INT_2ADDR.S20
-rw-r--r--vm/mterp/x86-atom/OP_SUB_INT_LIT8.S20
-rw-r--r--vm/mterp/x86-atom/OP_SUB_LONG.S20
-rw-r--r--vm/mterp/x86-atom/OP_SUB_LONG_2ADDR.S20
-rw-r--r--vm/mterp/x86-atom/OP_THROW.S37
-rw-r--r--vm/mterp/x86-atom/OP_THROW_VERIFICATION_ERROR.S40
-rw-r--r--vm/mterp/x86-atom/OP_UNUSED_3E.S20
-rw-r--r--vm/mterp/x86-atom/OP_UNUSED_3F.S20
-rw-r--r--vm/mterp/x86-atom/OP_UNUSED_40.S20
-rw-r--r--vm/mterp/x86-atom/OP_UNUSED_41.S20
-rw-r--r--vm/mterp/x86-atom/OP_UNUSED_42.S20
-rw-r--r--vm/mterp/x86-atom/OP_UNUSED_43.S20
-rw-r--r--vm/mterp/x86-atom/OP_UNUSED_73.S20
-rw-r--r--vm/mterp/x86-atom/OP_UNUSED_79.S20
-rw-r--r--vm/mterp/x86-atom/OP_UNUSED_7A.S20
-rw-r--r--vm/mterp/x86-atom/OP_UNUSED_E3.S20
-rw-r--r--vm/mterp/x86-atom/OP_UNUSED_E4.S20
-rw-r--r--vm/mterp/x86-atom/OP_UNUSED_E5.S20
-rw-r--r--vm/mterp/x86-atom/OP_UNUSED_E6.S20
-rw-r--r--vm/mterp/x86-atom/OP_UNUSED_E7.S20
-rw-r--r--vm/mterp/x86-atom/OP_UNUSED_F1.S20
-rw-r--r--vm/mterp/x86-atom/OP_UNUSED_FC.S20
-rw-r--r--vm/mterp/x86-atom/OP_UNUSED_FD.S20
-rw-r--r--vm/mterp/x86-atom/OP_UNUSED_FE.S20
-rw-r--r--vm/mterp/x86-atom/OP_UNUSED_FF.S20
-rw-r--r--vm/mterp/x86-atom/OP_USHR_INT.S20
-rw-r--r--vm/mterp/x86-atom/OP_USHR_INT_2ADDR.S20
-rw-r--r--vm/mterp/x86-atom/OP_USHR_INT_LIT8.S20
-rw-r--r--vm/mterp/x86-atom/OP_USHR_LONG.S39
-rw-r--r--vm/mterp/x86-atom/OP_USHR_LONG_2ADDR.S41
-rw-r--r--vm/mterp/x86-atom/OP_XOR_INT.S20
-rw-r--r--vm/mterp/x86-atom/OP_XOR_INT_2ADDR.S20
-rw-r--r--vm/mterp/x86-atom/OP_XOR_INT_LIT16.S20
-rw-r--r--vm/mterp/x86-atom/OP_XOR_INT_LIT8.S20
-rw-r--r--vm/mterp/x86-atom/OP_XOR_LONG.S20
-rw-r--r--vm/mterp/x86-atom/OP_XOR_LONG_2ADDR.S20
-rw-r--r--vm/mterp/x86-atom/TODO.txt4
-rw-r--r--vm/mterp/x86-atom/bincmp.S44
-rw-r--r--vm/mterp/x86-atom/binop.S39
-rw-r--r--vm/mterp/x86-atom/binop2addr.S45
-rw-r--r--vm/mterp/x86-atom/binopD.S61
-rw-r--r--vm/mterp/x86-atom/binopD2addr.S68
-rw-r--r--vm/mterp/x86-atom/binopDLit16.S66
-rw-r--r--vm/mterp/x86-atom/binopDLit8.S65
-rw-r--r--vm/mterp/x86-atom/binopDivRemLong.S52
-rw-r--r--vm/mterp/x86-atom/binopDivRemLong2Addr.S54
-rw-r--r--vm/mterp/x86-atom/binopF.S39
-rw-r--r--vm/mterp/x86-atom/binopF2addr.S41
-rw-r--r--vm/mterp/x86-atom/binopLit16.S43
-rw-r--r--vm/mterp/x86-atom/binopLit8.S40
-rw-r--r--vm/mterp/x86-atom/binopLit8S.S40
-rw-r--r--vm/mterp/x86-atom/binopS.S39
-rw-r--r--vm/mterp/x86-atom/binopS2addr.S42
-rw-r--r--vm/mterp/x86-atom/binopWide.S40
-rw-r--r--vm/mterp/x86-atom/binopWide2addr.S41
-rw-r--r--vm/mterp/x86-atom/entry.S384
-rw-r--r--vm/mterp/x86-atom/footer.S662
-rw-r--r--vm/mterp/x86-atom/header.S433
-rw-r--r--vm/mterp/x86-atom/stub.S25
-rw-r--r--vm/mterp/x86-atom/unop.S43
-rw-r--r--vm/mterp/x86-atom/unopWide.S43
-rw-r--r--vm/mterp/x86-atom/unused.S30
-rw-r--r--vm/mterp/x86-atom/zcmp.S53
-rw-r--r--vm/mterp/x86/OP_ADD_DOUBLE.S2
-rw-r--r--vm/mterp/x86/OP_ADD_DOUBLE_2ADDR.S2
-rw-r--r--vm/mterp/x86/OP_ADD_FLOAT.S2
-rw-r--r--vm/mterp/x86/OP_ADD_FLOAT_2ADDR.S2
-rw-r--r--vm/mterp/x86/OP_ADD_INT.S2
-rw-r--r--vm/mterp/x86/OP_ADD_INT_2ADDR.S2
-rw-r--r--vm/mterp/x86/OP_ADD_INT_LIT16.S2
-rw-r--r--vm/mterp/x86/OP_ADD_INT_LIT8.S2
-rw-r--r--vm/mterp/x86/OP_ADD_LONG.S2
-rw-r--r--vm/mterp/x86/OP_ADD_LONG_2ADDR.S2
-rw-r--r--vm/mterp/x86/OP_AGET.S23
-rw-r--r--vm/mterp/x86/OP_AGET_BOOLEAN.S2
-rw-r--r--vm/mterp/x86/OP_AGET_BYTE.S2
-rw-r--r--vm/mterp/x86/OP_AGET_CHAR.S2
-rw-r--r--vm/mterp/x86/OP_AGET_OBJECT.S2
-rw-r--r--vm/mterp/x86/OP_AGET_SHORT.S2
-rw-r--r--vm/mterp/x86/OP_AGET_WIDE.S27
-rw-r--r--vm/mterp/x86/OP_AND_INT.S2
-rw-r--r--vm/mterp/x86/OP_AND_INT_2ADDR.S2
-rw-r--r--vm/mterp/x86/OP_AND_INT_LIT16.S2
-rw-r--r--vm/mterp/x86/OP_AND_INT_LIT8.S2
-rw-r--r--vm/mterp/x86/OP_AND_LONG.S2
-rw-r--r--vm/mterp/x86/OP_AND_LONG_2ADDR.S2
-rw-r--r--vm/mterp/x86/OP_APUT.S23
-rw-r--r--vm/mterp/x86/OP_APUT_BOOLEAN.S2
-rw-r--r--vm/mterp/x86/OP_APUT_BYTE.S2
-rw-r--r--vm/mterp/x86/OP_APUT_CHAR.S2
-rw-r--r--vm/mterp/x86/OP_APUT_OBJECT.S56
-rw-r--r--vm/mterp/x86/OP_APUT_SHORT.S2
-rw-r--r--vm/mterp/x86/OP_APUT_WIDE.S27
-rw-r--r--vm/mterp/x86/OP_ARRAY_LENGTH.S15
-rw-r--r--vm/mterp/x86/OP_BREAKPOINT.S1
-rw-r--r--vm/mterp/x86/OP_CHECK_CAST.S82
-rw-r--r--vm/mterp/x86/OP_CMPG_DOUBLE.S36
-rw-r--r--vm/mterp/x86/OP_CMPG_FLOAT.S2
-rw-r--r--vm/mterp/x86/OP_CMPL_DOUBLE.S2
-rw-r--r--vm/mterp/x86/OP_CMPL_FLOAT.S2
-rw-r--r--vm/mterp/x86/OP_CMP_LONG.S37
-rw-r--r--vm/mterp/x86/OP_CONST.S8
-rw-r--r--vm/mterp/x86/OP_CONST_16.S8
-rw-r--r--vm/mterp/x86/OP_CONST_4.S10
-rw-r--r--vm/mterp/x86/OP_CONST_CLASS.S41
-rw-r--r--vm/mterp/x86/OP_CONST_HIGH16.S9
-rw-r--r--vm/mterp/x86/OP_CONST_STRING.S40
-rw-r--r--vm/mterp/x86/OP_CONST_STRING_JUMBO.S40
-rw-r--r--vm/mterp/x86/OP_CONST_WIDE.S11
-rw-r--r--vm/mterp/x86/OP_CONST_WIDE_16.S12
-rw-r--r--vm/mterp/x86/OP_CONST_WIDE_32.S12
-rw-r--r--vm/mterp/x86/OP_CONST_WIDE_HIGH16.S11
-rw-r--r--vm/mterp/x86/OP_DIV_DOUBLE.S2
-rw-r--r--vm/mterp/x86/OP_DIV_DOUBLE_2ADDR.S2
-rw-r--r--vm/mterp/x86/OP_DIV_FLOAT.S2
-rw-r--r--vm/mterp/x86/OP_DIV_FLOAT_2ADDR.S2
-rw-r--r--vm/mterp/x86/OP_DIV_INT.S2
-rw-r--r--vm/mterp/x86/OP_DIV_INT_2ADDR.S2
-rw-r--r--vm/mterp/x86/OP_DIV_INT_LIT16.S2
-rw-r--r--vm/mterp/x86/OP_DIV_INT_LIT8.S2
-rw-r--r--vm/mterp/x86/OP_DIV_LONG.S52
-rw-r--r--vm/mterp/x86/OP_DIV_LONG_2ADDR.S54
-rw-r--r--vm/mterp/x86/OP_DOUBLE_TO_FLOAT.S2
-rw-r--r--vm/mterp/x86/OP_DOUBLE_TO_INT.S2
-rw-r--r--vm/mterp/x86/OP_DOUBLE_TO_LONG.S2
-rw-r--r--vm/mterp/x86/OP_EXECUTE_INLINE.S66
-rw-r--r--vm/mterp/x86/OP_EXECUTE_INLINE_RANGE.S1
-rw-r--r--vm/mterp/x86/OP_FILLED_NEW_ARRAY.S142
-rw-r--r--vm/mterp/x86/OP_FILLED_NEW_ARRAY_RANGE.S2
-rw-r--r--vm/mterp/x86/OP_FILL_ARRAY_DATA.S17
-rw-r--r--vm/mterp/x86/OP_FLOAT_TO_DOUBLE.S2
-rw-r--r--vm/mterp/x86/OP_FLOAT_TO_INT.S2
-rw-r--r--vm/mterp/x86/OP_FLOAT_TO_LONG.S2
-rw-r--r--vm/mterp/x86/OP_GOTO.S16
-rw-r--r--vm/mterp/x86/OP_GOTO_16.S15
-rw-r--r--vm/mterp/x86/OP_GOTO_32.S18
-rw-r--r--vm/mterp/x86/OP_IF_EQ.S2
-rw-r--r--vm/mterp/x86/OP_IF_EQZ.S2
-rw-r--r--vm/mterp/x86/OP_IF_GE.S2
-rw-r--r--vm/mterp/x86/OP_IF_GEZ.S2
-rw-r--r--vm/mterp/x86/OP_IF_GT.S2
-rw-r--r--vm/mterp/x86/OP_IF_GTZ.S2
-rw-r--r--vm/mterp/x86/OP_IF_LE.S2
-rw-r--r--vm/mterp/x86/OP_IF_LEZ.S2
-rw-r--r--vm/mterp/x86/OP_IF_LT.S2
-rw-r--r--vm/mterp/x86/OP_IF_LTZ.S2
-rw-r--r--vm/mterp/x86/OP_IF_NE.S2
-rw-r--r--vm/mterp/x86/OP_IF_NEZ.S2
-rw-r--r--vm/mterp/x86/OP_IGET.S64
-rw-r--r--vm/mterp/x86/OP_IGET_BOOLEAN.S2
-rw-r--r--vm/mterp/x86/OP_IGET_BYTE.S3
-rw-r--r--vm/mterp/x86/OP_IGET_CHAR.S3
-rw-r--r--vm/mterp/x86/OP_IGET_OBJECT.S2
-rw-r--r--vm/mterp/x86/OP_IGET_OBJECT_QUICK.S2
-rw-r--r--vm/mterp/x86/OP_IGET_OBJECT_VOLATILE.S3
-rw-r--r--vm/mterp/x86/OP_IGET_QUICK.S17
-rw-r--r--vm/mterp/x86/OP_IGET_SHORT.S3
-rw-r--r--vm/mterp/x86/OP_IGET_VOLATILE.S3
-rw-r--r--vm/mterp/x86/OP_IGET_WIDE.S64
-rw-r--r--vm/mterp/x86/OP_IGET_WIDE_QUICK.S20
-rw-r--r--vm/mterp/x86/OP_INSTANCE_OF.S102
-rw-r--r--vm/mterp/x86/OP_INT_TO_BYTE.S2
-rw-r--r--vm/mterp/x86/OP_INT_TO_CHAR.S2
-rw-r--r--vm/mterp/x86/OP_INT_TO_DOUBLE.S2
-rw-r--r--vm/mterp/x86/OP_INT_TO_FLOAT.S2
-rw-r--r--vm/mterp/x86/OP_INT_TO_LONG.S14
-rw-r--r--vm/mterp/x86/OP_INT_TO_SHORT.S2
-rw-r--r--vm/mterp/x86/OP_INVOKE_DIRECT.S58
-rw-r--r--vm/mterp/x86/OP_INVOKE_DIRECT_EMPTY.S7
-rw-r--r--vm/mterp/x86/OP_INVOKE_DIRECT_RANGE.S2
-rw-r--r--vm/mterp/x86/OP_INVOKE_INTERFACE.S38
-rw-r--r--vm/mterp/x86/OP_INVOKE_INTERFACE_RANGE.S2
-rw-r--r--vm/mterp/x86/OP_INVOKE_STATIC.S36
-rw-r--r--vm/mterp/x86/OP_INVOKE_STATIC_RANGE.S2
-rw-r--r--vm/mterp/x86/OP_INVOKE_SUPER.S72
-rw-r--r--vm/mterp/x86/OP_INVOKE_SUPER_QUICK.S26
-rw-r--r--vm/mterp/x86/OP_INVOKE_SUPER_QUICK_RANGE.S2
-rw-r--r--vm/mterp/x86/OP_INVOKE_SUPER_RANGE.S2
-rw-r--r--vm/mterp/x86/OP_INVOKE_VIRTUAL.S55
-rw-r--r--vm/mterp/x86/OP_INVOKE_VIRTUAL_QUICK.S23
-rw-r--r--vm/mterp/x86/OP_INVOKE_VIRTUAL_QUICK_RANGE.S2
-rw-r--r--vm/mterp/x86/OP_INVOKE_VIRTUAL_RANGE.S2
-rw-r--r--vm/mterp/x86/OP_IPUT.S64
-rw-r--r--vm/mterp/x86/OP_IPUT_BOOLEAN.S2
-rw-r--r--vm/mterp/x86/OP_IPUT_BYTE.S2
-rw-r--r--vm/mterp/x86/OP_IPUT_CHAR.S2
-rw-r--r--vm/mterp/x86/OP_IPUT_OBJECT.S70
-rw-r--r--vm/mterp/x86/OP_IPUT_OBJECT_QUICK.S28
-rw-r--r--vm/mterp/x86/OP_IPUT_OBJECT_VOLATILE.S2
-rw-r--r--vm/mterp/x86/OP_IPUT_QUICK.S17
-rw-r--r--vm/mterp/x86/OP_IPUT_SHORT.S2
-rw-r--r--vm/mterp/x86/OP_IPUT_VOLATILE.S2
-rw-r--r--vm/mterp/x86/OP_IPUT_WIDE.S64
-rw-r--r--vm/mterp/x86/OP_IPUT_WIDE_QUICK.S20
-rw-r--r--vm/mterp/x86/OP_LONG_TO_DOUBLE.S2
-rw-r--r--vm/mterp/x86/OP_LONG_TO_FLOAT.S2
-rw-r--r--vm/mterp/x86/OP_LONG_TO_INT.S3
-rw-r--r--vm/mterp/x86/OP_MONITOR_ENTER.S32
-rw-r--r--vm/mterp/x86/OP_MONITOR_EXIT.S35
-rw-r--r--vm/mterp/x86/OP_MOVE.S11
-rw-r--r--vm/mterp/x86/OP_MOVE_16.S10
-rw-r--r--vm/mterp/x86/OP_MOVE_EXCEPTION.S11
-rw-r--r--vm/mterp/x86/OP_MOVE_FROM16.S10
-rw-r--r--vm/mterp/x86/OP_MOVE_OBJECT.S2
-rw-r--r--vm/mterp/x86/OP_MOVE_OBJECT_16.S2
-rw-r--r--vm/mterp/x86/OP_MOVE_OBJECT_FROM16.S2
-rw-r--r--vm/mterp/x86/OP_MOVE_RESULT.S10
-rw-r--r--vm/mterp/x86/OP_MOVE_RESULT_OBJECT.S2
-rw-r--r--vm/mterp/x86/OP_MOVE_RESULT_WIDE.S11
-rw-r--r--vm/mterp/x86/OP_MOVE_WIDE.S13
-rw-r--r--vm/mterp/x86/OP_MOVE_WIDE_16.S12
-rw-r--r--vm/mterp/x86/OP_MOVE_WIDE_FROM16.S12
-rw-r--r--vm/mterp/x86/OP_MUL_DOUBLE.S2
-rw-r--r--vm/mterp/x86/OP_MUL_DOUBLE_2ADDR.S2
-rw-r--r--vm/mterp/x86/OP_MUL_FLOAT.S2
-rw-r--r--vm/mterp/x86/OP_MUL_FLOAT_2ADDR.S2
-rw-r--r--vm/mterp/x86/OP_MUL_INT.S16
-rw-r--r--vm/mterp/x86/OP_MUL_INT_2ADDR.S13
-rw-r--r--vm/mterp/x86/OP_MUL_INT_LIT16.S16
-rw-r--r--vm/mterp/x86/OP_MUL_INT_LIT8.S13
-rw-r--r--vm/mterp/x86/OP_MUL_LONG.S43
-rw-r--r--vm/mterp/x86/OP_MUL_LONG_2ADDR.S41
-rw-r--r--vm/mterp/x86/OP_NEG_DOUBLE.S2
-rw-r--r--vm/mterp/x86/OP_NEG_FLOAT.S2
-rw-r--r--vm/mterp/x86/OP_NEG_INT.S2
-rw-r--r--vm/mterp/x86/OP_NEG_LONG.S16
-rw-r--r--vm/mterp/x86/OP_NEW_ARRAY.S72
-rw-r--r--vm/mterp/x86/OP_NEW_INSTANCE.S93
-rw-r--r--vm/mterp/x86/OP_NOP.S4
-rw-r--r--vm/mterp/x86/OP_NOT_INT.S2
-rw-r--r--vm/mterp/x86/OP_NOT_LONG.S15
-rw-r--r--vm/mterp/x86/OP_OR_INT.S2
-rw-r--r--vm/mterp/x86/OP_OR_INT_2ADDR.S2
-rw-r--r--vm/mterp/x86/OP_OR_INT_LIT16.S2
-rw-r--r--vm/mterp/x86/OP_OR_INT_LIT8.S2
-rw-r--r--vm/mterp/x86/OP_OR_LONG.S2
-rw-r--r--vm/mterp/x86/OP_OR_LONG_2ADDR.S2
-rw-r--r--vm/mterp/x86/OP_PACKED_SWITCH.S27
-rw-r--r--vm/mterp/x86/OP_REM_DOUBLE.S17
-rw-r--r--vm/mterp/x86/OP_REM_DOUBLE_2ADDR.S17
-rw-r--r--vm/mterp/x86/OP_REM_FLOAT.S17
-rw-r--r--vm/mterp/x86/OP_REM_FLOAT_2ADDR.S17
-rw-r--r--vm/mterp/x86/OP_REM_INT.S2
-rw-r--r--vm/mterp/x86/OP_REM_INT_2ADDR.S2
-rw-r--r--vm/mterp/x86/OP_REM_INT_LIT16.S2
-rw-r--r--vm/mterp/x86/OP_REM_INT_LIT8.S2
-rw-r--r--vm/mterp/x86/OP_REM_LONG.S2
-rw-r--r--vm/mterp/x86/OP_REM_LONG_2ADDR.S2
-rw-r--r--vm/mterp/x86/OP_RETURN.S13
-rw-r--r--vm/mterp/x86/OP_RETURN_OBJECT.S2
-rw-r--r--vm/mterp/x86/OP_RETURN_VOID.S2
-rw-r--r--vm/mterp/x86/OP_RETURN_WIDE.S13
-rw-r--r--vm/mterp/x86/OP_RSUB_INT.S2
-rw-r--r--vm/mterp/x86/OP_RSUB_INT_LIT8.S2
-rw-r--r--vm/mterp/x86/OP_SGET.S43
-rw-r--r--vm/mterp/x86/OP_SGET_BOOLEAN.S2
-rw-r--r--vm/mterp/x86/OP_SGET_BYTE.S2
-rw-r--r--vm/mterp/x86/OP_SGET_CHAR.S2
-rw-r--r--vm/mterp/x86/OP_SGET_OBJECT.S2
-rw-r--r--vm/mterp/x86/OP_SGET_OBJECT_VOLATILE.S2
-rw-r--r--vm/mterp/x86/OP_SGET_SHORT.S2
-rw-r--r--vm/mterp/x86/OP_SGET_VOLATILE.S2
-rw-r--r--vm/mterp/x86/OP_SGET_WIDE.S44
-rw-r--r--vm/mterp/x86/OP_SHL_INT.S2
-rw-r--r--vm/mterp/x86/OP_SHL_INT_2ADDR.S2
-rw-r--r--vm/mterp/x86/OP_SHL_INT_LIT8.S2
-rw-r--r--vm/mterp/x86/OP_SHL_LONG.S37
-rw-r--r--vm/mterp/x86/OP_SHL_LONG_2ADDR.S35
-rw-r--r--vm/mterp/x86/OP_SHR_INT.S2
-rw-r--r--vm/mterp/x86/OP_SHR_INT_2ADDR.S2
-rw-r--r--vm/mterp/x86/OP_SHR_INT_LIT8.S2
-rw-r--r--vm/mterp/x86/OP_SHR_LONG.S38
-rw-r--r--vm/mterp/x86/OP_SHR_LONG_2ADDR.S35
-rw-r--r--vm/mterp/x86/OP_SPARSE_SWITCH.S2
-rw-r--r--vm/mterp/x86/OP_SPUT.S43
-rw-r--r--vm/mterp/x86/OP_SPUT_BOOLEAN.S2
-rw-r--r--vm/mterp/x86/OP_SPUT_BYTE.S2
-rw-r--r--vm/mterp/x86/OP_SPUT_CHAR.S2
-rw-r--r--vm/mterp/x86/OP_SPUT_OBJECT.S50
-rw-r--r--vm/mterp/x86/OP_SPUT_OBJECT_VOLATILE.S2
-rw-r--r--vm/mterp/x86/OP_SPUT_SHORT.S2
-rw-r--r--vm/mterp/x86/OP_SPUT_VOLATILE.S2
-rw-r--r--vm/mterp/x86/OP_SPUT_WIDE.S45
-rw-r--r--vm/mterp/x86/OP_SUB_DOUBLE.S2
-rw-r--r--vm/mterp/x86/OP_SUB_DOUBLE_2ADDR.S2
-rw-r--r--vm/mterp/x86/OP_SUB_FLOAT.S2
-rw-r--r--vm/mterp/x86/OP_SUB_FLOAT_2ADDR.S2
-rw-r--r--vm/mterp/x86/OP_SUB_INT.S2
-rw-r--r--vm/mterp/x86/OP_SUB_INT_2ADDR.S2
-rw-r--r--vm/mterp/x86/OP_SUB_LONG.S2
-rw-r--r--vm/mterp/x86/OP_SUB_LONG_2ADDR.S2
-rw-r--r--vm/mterp/x86/OP_THROW.S15
-rw-r--r--vm/mterp/x86/OP_THROW_VERIFICATION_ERROR.S19
-rw-r--r--vm/mterp/x86/OP_UNUSED_3E.S1
-rw-r--r--vm/mterp/x86/OP_UNUSED_3F.S1
-rw-r--r--vm/mterp/x86/OP_UNUSED_40.S1
-rw-r--r--vm/mterp/x86/OP_UNUSED_41.S1
-rw-r--r--vm/mterp/x86/OP_UNUSED_42.S1
-rw-r--r--vm/mterp/x86/OP_UNUSED_43.S1
-rw-r--r--vm/mterp/x86/OP_UNUSED_73.S1
-rw-r--r--vm/mterp/x86/OP_UNUSED_79.S1
-rw-r--r--vm/mterp/x86/OP_UNUSED_7A.S1
-rw-r--r--vm/mterp/x86/OP_UNUSED_E3.S1
-rw-r--r--vm/mterp/x86/OP_UNUSED_E4.S1
-rw-r--r--vm/mterp/x86/OP_UNUSED_E5.S1
-rw-r--r--vm/mterp/x86/OP_UNUSED_E6.S1
-rw-r--r--vm/mterp/x86/OP_UNUSED_E7.S1
-rw-r--r--vm/mterp/x86/OP_UNUSED_F1.S1
-rw-r--r--vm/mterp/x86/OP_UNUSED_FC.S1
-rw-r--r--vm/mterp/x86/OP_UNUSED_FD.S1
-rw-r--r--vm/mterp/x86/OP_UNUSED_FE.S1
-rw-r--r--vm/mterp/x86/OP_UNUSED_FF.S1
-rw-r--r--vm/mterp/x86/OP_USHR_INT.S2
-rw-r--r--vm/mterp/x86/OP_USHR_INT_2ADDR.S2
-rw-r--r--vm/mterp/x86/OP_USHR_INT_LIT8.S2
-rw-r--r--vm/mterp/x86/OP_USHR_LONG.S38
-rw-r--r--vm/mterp/x86/OP_USHR_LONG_2ADDR.S35
-rw-r--r--vm/mterp/x86/OP_XOR_INT.S2
-rw-r--r--vm/mterp/x86/OP_XOR_INT_2ADDR.S2
-rw-r--r--vm/mterp/x86/OP_XOR_INT_LIT16.S2
-rw-r--r--vm/mterp/x86/OP_XOR_INT_LIT8.S2
-rw-r--r--vm/mterp/x86/OP_XOR_LONG.S2
-rw-r--r--vm/mterp/x86/OP_XOR_LONG_2ADDR.S2
-rw-r--r--vm/mterp/x86/bincmp.S26
-rw-r--r--vm/mterp/x86/bindiv.S32
-rw-r--r--vm/mterp/x86/bindiv2addr.S32
-rw-r--r--vm/mterp/x86/bindivLit16.S33
-rw-r--r--vm/mterp/x86/bindivLit8.S30
-rw-r--r--vm/mterp/x86/binflop.S15
-rw-r--r--vm/mterp/x86/binflop2addr.S16
-rw-r--r--vm/mterp/x86/binop.S20
-rw-r--r--vm/mterp/x86/binop1.S16
-rw-r--r--vm/mterp/x86/binop2addr.S24
-rw-r--r--vm/mterp/x86/binopLit16.S22
-rw-r--r--vm/mterp/x86/binopLit8.S21
-rw-r--r--vm/mterp/x86/binopWide.S19
-rw-r--r--vm/mterp/x86/binopWide2addr.S15
-rw-r--r--vm/mterp/x86/cvtfp_int.S63
-rw-r--r--vm/mterp/x86/entry.S111
-rw-r--r--vm/mterp/x86/footer.S535
-rw-r--r--vm/mterp/x86/fpcvt.S14
-rw-r--r--vm/mterp/x86/header.S208
-rw-r--r--vm/mterp/x86/shop2addr.S16
-rw-r--r--vm/mterp/x86/stub.S11
-rw-r--r--vm/mterp/x86/unop.S17
-rw-r--r--vm/mterp/x86/unopWide.S22
-rw-r--r--vm/mterp/x86/unused.S1
-rw-r--r--vm/mterp/x86/zcmp.S22
-rw-r--r--vm/native/InternalNative.c347
-rw-r--r--vm/native/InternalNative.h32
-rw-r--r--vm/native/InternalNativePriv.h112
-rw-r--r--vm/native/README.txt23
-rw-r--r--vm/native/dalvik_system_DexFile.c454
-rw-r--r--vm/native/dalvik_system_VMDebug.c965
-rw-r--r--vm/native/dalvik_system_VMRuntime.c242
-rw-r--r--vm/native/dalvik_system_VMStack.c235
-rw-r--r--vm/native/dalvik_system_Zygote.c523
-rw-r--r--vm/native/java_lang_Class.c814
-rw-r--r--vm/native/java_lang_Object.c111
-rw-r--r--vm/native/java_lang_Runtime.c197
-rw-r--r--vm/native/java_lang_String.c42
-rw-r--r--vm/native/java_lang_System.c329
-rw-r--r--vm/native/java_lang_SystemProperties.c64
-rw-r--r--vm/native/java_lang_Throwable.c65
-rw-r--r--vm/native/java_lang_VMClassLoader.c201
-rw-r--r--vm/native/java_lang_VMThread.c262
-rw-r--r--vm/native/java_lang_reflect_AccessibleObject.c44
-rw-r--r--vm/native/java_lang_reflect_Array.c146
-rw-r--r--vm/native/java_lang_reflect_Constructor.c164
-rw-r--r--vm/native/java_lang_reflect_Field.c458
-rw-r--r--vm/native/java_lang_reflect_Method.c219
-rw-r--r--vm/native/java_lang_reflect_Proxy.c47
-rw-r--r--vm/native/java_security_AccessController.c138
-rw-r--r--vm/native/java_util_concurrent_atomic_AtomicLong.c38
-rw-r--r--vm/native/org_apache_harmony_dalvik_NativeTestTarget.c42
-rw-r--r--vm/native/org_apache_harmony_dalvik_ddmc_DdmServer.c48
-rw-r--r--vm/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.c169
-rw-r--r--vm/native/sun_misc_Unsafe.c339
-rw-r--r--vm/oo/AccessCheck.c150
-rw-r--r--vm/oo/AccessCheck.h43
-rw-r--r--vm/oo/Array.c814
-rw-r--r--vm/oo/Array.h176
-rw-r--r--vm/oo/Class.c4967
-rw-r--r--vm/oo/Class.h281
-rw-r--r--vm/oo/Object.c776
-rw-r--r--vm/oo/Object.h794
-rw-r--r--vm/oo/ObjectInlines.h342
-rw-r--r--vm/oo/Resolve.c588
-rw-r--r--vm/oo/Resolve.h95
-rw-r--r--vm/oo/TypeCheck.c248
-rw-r--r--vm/oo/TypeCheck.h78
-rw-r--r--vm/reflect/Annotation.c2181
-rw-r--r--vm/reflect/Proxy.c1103
-rw-r--r--vm/reflect/Reflect.c1253
-rw-r--r--vm/reflect/Reflect.h239
-rw-r--r--vm/test/AtomicTest.c383
-rw-r--r--vm/test/Test.h27
-rw-r--r--vm/test/TestHash.c191
-rw-r--r--vm/test/TestIndirectRefTable.c488
3348 files changed, 434817 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..e70e5f5
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,65 @@
+# Copyright (C) 2006 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+subdirs := $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk, \
+ libdex \
+ vm \
+ dalvikvm \
+ dexlist \
+ dexopt \
+ dexdump \
+ dvz \
+ dx \
+ libnativehelper \
+ tools \
+ ))
+
+include $(subdirs)
+
+
+.PHONY: dex dex-debug
+ifeq ($(DONT_INSTALL_DEX_FILES),true)
+dex:
+ @echo "Forcing a remake with DONT_INSTALL_DEX_FILES=false"
+ $(hide) $(MAKE) DONT_INSTALL_DEX_FILES=false
+else
+# DONT_INSTALL_DEX_FILES is already false, so a normal make takes care of it.
+dex: $(DEFAULT_GOAL)
+endif
+
+d :=
+ifneq ($(GENERATE_DEX_DEBUG),)
+d := debug
+endif
+ifneq ($(DONT_INSTALL_DEX_FILES),true)
+d := $(d)-install
+endif
+ifneq ($(d),debug-install)
+# generate the debug .dex files, with a copy in ./dalvik/DEBUG-FILES.
+# We need to rebuild the .dex files for the debug output to be generated.
+# The "touch -c $(DX)" is a hack that we know will force
+# a rebuild of the .dex files. If $(DX) doesn't exist yet,
+# we won't touch it (-c) and the normal build will create
+# the .dex files naturally.
+dex-debug:
+ @echo "Forcing an app rebuild with GENERATE_DEX_DEBUG=true"
+ @touch -c $(DX)
+ $(hide) $(MAKE) DONT_INSTALL_DEX_FILES=false GENERATE_DEX_DEBUG=true
+else
+# GENERATE_DEX_DEBUG and DONT_INSTALL_DEX_FILES are already set properly,
+# so a normal make takes care of it.
+dex-debug: $(DEFAULT_GOAL)
+endif
diff --git a/CleanSpec.mk b/CleanSpec.mk
new file mode 100644
index 0000000..1bc6e95
--- /dev/null
+++ b/CleanSpec.mk
@@ -0,0 +1,54 @@
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list. These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+# $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+# $(call add-clean-step, rm -rf $(OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list. E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+#$(call add-clean-step, rm -rf $(OUT)/obj/SHARED_LIBRARIES/libdvm*)
+$(call add-clean-step, rm -rf $(OUT)/obj/SHARED_LIBRARIES/libdvm*)
+$(call add-clean-step, rm -rf $(OUT)/obj/SHARED_LIBRARIES/libdvm*)
+$(call add-clean-step, rm -rf $(OUT)/obj/SHARED_LIBRARIES/libdvm*)
+$(call add-clean-step, rm -rf $(OUT)/obj/SHARED_LIBRARIES/libdvm*)
+$(call add-clean-step, rm -rf $(OUT)/obj/SHARED_LIBRARIES/libdvm*)
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_APACHE2
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-2008, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..2969180
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,60 @@
+This directory contains the Dalvik virtual machine and core class library,
+as well as related tools, libraries, and tests.
+
+A note about the licenses and header comments
+---------------------------------------------
+
+Much of the code under this directory originally came from the Apache
+Harmony project, and as such contains the standard Apache header
+comment. Some of the code was written originally for the Android
+project, and as such contains the standard Android header comment.
+Some files contain code from both projects. In these cases, the header
+comment is a combination of the other two, and the portions of the
+code from Harmony are identified as indicated in the comment.
+
+Here is the combined header comment:
+
+/*
+ * Copyright (C) <year> The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * ----------
+ *
+ * Portions of the code surrounded by "// BEGIN Harmony code" and
+ * "// END Harmony code" are copyrighted and licensed separately, as
+ * follows:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+Native SH call bridge
+---------------------
+
+Native SH call bridge is written by
+Shin-ichiro KAWASAKI <shinichiro.kawasaki.mg@hitachi.com>
+and Contributed to Android by Hitachi, Ltd. and Renesas Solutions Corp.
diff --git a/dalvikvm/Android.mk b/dalvikvm/Android.mk
new file mode 100644
index 0000000..b7dda0f
--- /dev/null
+++ b/dalvikvm/Android.mk
@@ -0,0 +1,75 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+LOCAL_PATH:= $(call my-dir)
+
+#
+# Common definitions.
+#
+
+dalvikvm_src_files := \
+ Main.c
+
+dalvikvm_c_includes := \
+ $(JNI_H_INCLUDE) \
+ dalvik/include
+
+
+#
+# Build for the target (device).
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(dalvikvm_src_files)
+LOCAL_C_INCLUDES := $(dalvikvm_c_includes)
+
+LOCAL_SHARED_LIBRARIES := \
+ libdvm \
+ libssl \
+ libz
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := dalvikvm
+
+include $(BUILD_EXECUTABLE)
+
+
+#
+# Build for the host.
+#
+
+ifeq ($(WITH_HOST_DALVIK),true)
+
+ include $(CLEAR_VARS)
+ LOCAL_SRC_FILES := $(dalvikvm_src_files)
+ LOCAL_C_INCLUDES := $(dalvikvm_c_includes)
+
+ ifeq ($(HOST_OS)-$(HOST_ARCH),darwin-x86)
+ # OS X comes with all these libraries, so there is no need
+ # to build any of them. Note: OpenSSL consists of libssl
+ # and libcrypto.
+ LOCAL_LDLIBS := -lffi -lssl -lcrypto -lz -lsqlite3
+ else
+ LOCAL_LDLIBS += -ldl -lpthread
+ LOCAL_SHARED_LIBRARIES += libdvm libcrypto libicuuc libicui18n libssl
+ endif
+
+ LOCAL_MODULE_TAGS := optional
+ LOCAL_MODULE := dalvikvm
+
+ include $(BUILD_HOST_EXECUTABLE)
+
+endif
diff --git a/dalvikvm/Main.c b/dalvikvm/Main.c
new file mode 100644
index 0000000..666317c
--- /dev/null
+++ b/dalvikvm/Main.c
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Command-line invocation of the Dalvik VM.
+ */
+#include "jni.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <assert.h>
+
+
+/*
+ * We want failed write() calls to just return with an error.
+ */
+static void blockSigpipe()
+{
+ sigset_t mask;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGPIPE);
+ if (sigprocmask(SIG_BLOCK, &mask, NULL) != 0)
+ fprintf(stderr, "WARNING: SIGPIPE not blocked\n");
+}
+
+/*
+ * Create a String[] and populate it with the contents of argv.
+ */
+static jobjectArray createStringArray(JNIEnv* env, char* const argv[], int argc)
+{
+ jclass stringClass = NULL;
+ jobjectArray strArray = NULL;
+ jobjectArray result = NULL;
+ int i;
+
+ stringClass = (*env)->FindClass(env, "java/lang/String");
+ if ((*env)->ExceptionCheck(env)) {
+ fprintf(stderr, "Got exception while finding class String\n");
+ goto bail;
+ }
+ assert(stringClass != NULL);
+ strArray = (*env)->NewObjectArray(env, argc, stringClass, NULL);
+ if ((*env)->ExceptionCheck(env)) {
+ fprintf(stderr, "Got exception while creating String array\n");
+ goto bail;
+ }
+ assert(strArray != NULL);
+
+ for (i = 0; i < argc; i++) {
+ jstring argStr;
+
+ argStr = (*env)->NewStringUTF(env, argv[i]);
+ if ((*env)->ExceptionCheck(env)) {
+ fprintf(stderr, "Got exception while allocating Strings\n");
+ goto bail;
+ }
+ assert(argStr != NULL);
+ (*env)->SetObjectArrayElement(env, strArray, i, argStr);
+ (*env)->DeleteLocalRef(env, argStr);
+ }
+
+ /* return the array, and ensure we don't delete the local ref to it */
+ result = strArray;
+ strArray = NULL;
+
+bail:
+ (*env)->DeleteLocalRef(env, stringClass);
+ (*env)->DeleteLocalRef(env, strArray);
+ return result;
+}
+
+/*
+ * Determine whether or not the specified method is public.
+ *
+ * Returns JNI_TRUE on success, JNI_FALSE on failure.
+ */
+static int methodIsPublic(JNIEnv* env, jclass clazz, jmethodID methodId)
+{
+ static const int PUBLIC = 0x0001; // java.lang.reflect.Modifiers.PUBLIC
+ jobject refMethod = NULL;
+ jclass methodClass = NULL;
+ jmethodID getModifiersId;
+ int modifiers;
+ int result = JNI_FALSE;
+
+ refMethod = (*env)->ToReflectedMethod(env, clazz, methodId, JNI_FALSE);
+ if (refMethod == NULL) {
+ fprintf(stderr, "Dalvik VM unable to get reflected method\n");
+ goto bail;
+ }
+
+ /*
+ * We now have a Method instance. We need to call
+ * its getModifiers() method.
+ */
+ methodClass = (*env)->FindClass(env, "java/lang/reflect/Method");
+ if (methodClass == NULL) {
+ fprintf(stderr, "Dalvik VM unable to find class Method\n");
+ goto bail;
+ }
+ getModifiersId = (*env)->GetMethodID(env, methodClass,
+ "getModifiers", "()I");
+ if (getModifiersId == NULL) {
+ fprintf(stderr, "Dalvik VM unable to find reflect.Method.getModifiers\n");
+ goto bail;
+ }
+
+ modifiers = (*env)->CallIntMethod(env, refMethod, getModifiersId);
+ if ((modifiers & PUBLIC) == 0) {
+ fprintf(stderr, "Dalvik VM: main() is not public\n");
+ goto bail;
+ }
+
+ result = JNI_TRUE;
+
+bail:
+ (*env)->DeleteLocalRef(env, refMethod);
+ (*env)->DeleteLocalRef(env, methodClass);
+ return result;
+}
+
+/*
+ * Parse arguments. Most of it just gets passed through to the VM. The
+ * JNI spec defines a handful of standard arguments.
+ */
+int main(int argc, char* const argv[])
+{
+ JavaVM* vm = NULL;
+ JNIEnv* env = NULL;
+ JavaVMInitArgs initArgs;
+ JavaVMOption* options = NULL;
+ char* slashClass = NULL;
+ int optionCount, curOpt, i, argIdx;
+ int needExtra = JNI_FALSE;
+ int result = 1;
+
+ setvbuf(stdout, NULL, _IONBF, 0);
+
+ /* ignore argv[0] */
+ argv++;
+ argc--;
+
+ /*
+ * If we're adding any additional stuff, e.g. function hook specifiers,
+ * add them to the count here.
+ *
+ * We're over-allocating, because this includes the options to the VM
+ * plus the options to the program.
+ */
+ optionCount = argc;
+
+ options = (JavaVMOption*) malloc(sizeof(JavaVMOption) * optionCount);
+ memset(options, 0, sizeof(JavaVMOption) * optionCount);
+
+ /*
+ * Copy options over. Everything up to the name of the class starts
+ * with a '-' (the function hook stuff is strictly internal).
+ *
+ * [Do we need to catch & handle "-jar" here?]
+ */
+ for (curOpt = argIdx = 0; argIdx < argc; argIdx++) {
+ if (argv[argIdx][0] != '-' && !needExtra)
+ break;
+ options[curOpt++].optionString = strdup(argv[argIdx]);
+
+ /* some options require an additional arg */
+ needExtra = JNI_FALSE;
+ if (strcmp(argv[argIdx], "-classpath") == 0 ||
+ strcmp(argv[argIdx], "-cp") == 0)
+ /* others? */
+ {
+ needExtra = JNI_TRUE;
+ }
+ }
+
+ if (needExtra) {
+ fprintf(stderr, "Dalvik VM requires value after last option flag\n");
+ goto bail;
+ }
+
+ /* insert additional internal options here */
+
+ assert(curOpt <= optionCount);
+
+ initArgs.version = JNI_VERSION_1_4;
+ initArgs.options = options;
+ initArgs.nOptions = curOpt;
+ initArgs.ignoreUnrecognized = JNI_FALSE;
+
+ //printf("nOptions = %d\n", initArgs.nOptions);
+
+ blockSigpipe();
+
+ /*
+ * Start VM. The current thread becomes the main thread of the VM.
+ */
+ if (JNI_CreateJavaVM(&vm, &env, &initArgs) < 0) {
+ fprintf(stderr, "Dalvik VM init failed (check log file)\n");
+ goto bail;
+ }
+
+ /*
+ * Make sure they provided a class name. We do this after VM init
+ * so that things like "-Xrunjdwp:help" have the opportunity to emit
+ * a usage statement.
+ */
+ if (argIdx == argc) {
+ fprintf(stderr, "Dalvik VM requires a class name\n");
+ goto bail;
+ }
+
+ /*
+ * We want to call main() with a String array with our arguments in it.
+ * Create an array and populate it. Note argv[0] is not included.
+ */
+ jobjectArray strArray;
+ strArray = createStringArray(env, &argv[argIdx+1], argc-argIdx-1);
+ if (strArray == NULL)
+ goto bail;
+
+ /*
+ * Find [class].main(String[]).
+ */
+ jclass startClass;
+ jmethodID startMeth;
+ char* cp;
+
+ /* convert "com.android.Blah" to "com/android/Blah" */
+ slashClass = strdup(argv[argIdx]);
+ for (cp = slashClass; *cp != '\0'; cp++)
+ if (*cp == '.')
+ *cp = '/';
+
+ startClass = (*env)->FindClass(env, slashClass);
+ if (startClass == NULL) {
+ fprintf(stderr, "Dalvik VM unable to locate class '%s'\n", slashClass);
+ goto bail;
+ }
+
+ startMeth = (*env)->GetStaticMethodID(env, startClass,
+ "main", "([Ljava/lang/String;)V");
+ if (startMeth == NULL) {
+ fprintf(stderr, "Dalvik VM unable to find static main(String[]) in '%s'\n",
+ slashClass);
+ goto bail;
+ }
+
+ /*
+ * Make sure the method is public. JNI doesn't prevent us from calling
+ * a private method, so we have to check it explicitly.
+ */
+ if (!methodIsPublic(env, startClass, startMeth))
+ goto bail;
+
+ /*
+ * Invoke main().
+ */
+ (*env)->CallStaticVoidMethod(env, startClass, startMeth, strArray);
+
+ if (!(*env)->ExceptionCheck(env))
+ result = 0;
+
+bail:
+ /*printf("Shutting down Dalvik VM\n");*/
+ if (vm != NULL) {
+ /*
+ * This allows join() and isAlive() on the main thread to work
+ * correctly, and also provides uncaught exception handling.
+ */
+ if ((*vm)->DetachCurrentThread(vm) != JNI_OK) {
+ fprintf(stderr, "Warning: unable to detach main thread\n");
+ result = 1;
+ }
+
+ if ((*vm)->DestroyJavaVM(vm) != 0)
+ fprintf(stderr, "Warning: Dalvik VM did not shut down cleanly\n");
+ /*printf("\nDalvik VM has exited\n");*/
+ }
+
+ for (i = 0; i < optionCount; i++)
+ free((char*) options[i].optionString);
+ free(options);
+ free(slashClass);
+ /*printf("--- VM is down, process exiting\n");*/
+ return result;
+}
diff --git a/dexdump/Android.mk b/dexdump/Android.mk
new file mode 100644
index 0000000..89749d6
--- /dev/null
+++ b/dexdump/Android.mk
@@ -0,0 +1,74 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# dexdump, similar in purpose to objdump.
+#
+LOCAL_PATH:= $(call my-dir)
+
+dexdump_src_files := \
+ DexDump.c
+
+dexdump_c_includes := \
+ dalvik \
+ $(JNI_H_INCLUDE)
+
+dexdump_shared_libraries :=
+
+dexdump_static_libraries := \
+ libdex
+
+##
+##
+## Build the device command line tool dexdump
+##
+##
+ifneq ($(SDK_ONLY),true) # SDK_only doesn't need device version
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := dexdump
+LOCAL_SRC_FILES := $(dexdump_src_files)
+LOCAL_C_INCLUDES := $(dexdump_c_includes)
+LOCAL_SHARED_LIBRARIES := $(dexdump_shared_libraries) libz liblog
+LOCAL_STATIC_LIBRARIES := $(dexdump_static_libraries)
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := optional
+LOCAL_LDLIBS +=
+include $(BUILD_EXECUTABLE)
+
+endif # !SDK_ONLY
+
+
+##
+##
+## Build the host command line tool dexdump
+##
+##
+ifneq ($(TARGET_SIMULATOR),true) # not 64 bit clean
+include $(CLEAR_VARS)
+LOCAL_MODULE := dexdump
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := $(dexdump_src_files)
+LOCAL_C_INCLUDES := $(dexdump_c_includes)
+LOCAL_SHARED_LIBRARIES := $(dexdump_shared_libraries)
+LOCAL_STATIC_LIBRARIES := $(dexdump_static_libraries) liblog
+
+ifneq ($(strip $(USE_MINGW)),)
+LOCAL_STATIC_LIBRARIES += libz
+else
+LOCAL_LDLIBS += -lpthread -lz
+endif
+
+include $(BUILD_HOST_EXECUTABLE)
+endif # !TARGET_SIMULATOR
diff --git a/dexdump/DexDump.c b/dexdump/DexDump.c
new file mode 100644
index 0000000..636408a
--- /dev/null
+++ b/dexdump/DexDump.c
@@ -0,0 +1,1891 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * The "dexdump" tool is intended to mimic "objdump". When possible, use
+ * similar command-line arguments.
+ *
+ * TODO: rework the "plain" output format to be more regexp-friendly
+ *
+ * Differences between XML output and the "current.xml" file:
+ * - classes in same package are not all grouped together; generally speaking
+ * nothing is sorted
+ * - no "deprecated" on fields and methods
+ * - no "value" on fields
+ * - no parameter names
+ * - no generic signatures on parameters, e.g. type="java.lang.Class&lt;?&gt;"
+ * - class shows declared fields and methods; does not show inherited fields
+ */
+#include "libdex/DexFile.h"
+#include "libdex/DexCatch.h"
+#include "libdex/DexClass.h"
+#include "libdex/DexProto.h"
+#include "libdex/InstrUtils.h"
+#include "libdex/OpCodeNames.h"
+#include "libdex/SysUtil.h"
+#include "libdex/CmdUtils.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <assert.h>
+
+static const char* gProgName = "dexdump";
+
+static InstructionWidth* gInstrWidth;
+static InstructionFormat* gInstrFormat;
+
+typedef enum OutputFormat {
+ OUTPUT_PLAIN = 0, /* default */
+ OUTPUT_XML, /* fancy */
+} OutputFormat;
+
+/* command-line options */
+struct {
+ bool checksumOnly;
+ bool disassemble;
+ bool showFileHeaders;
+ bool showSectionHeaders;
+ bool ignoreBadChecksum;
+ bool dumpRegisterMaps;
+ OutputFormat outputFormat;
+ const char* tempFileName;
+ bool exportsOnly;
+ bool verbose;
+} gOptions;
+
+/* basic info about a field or method */
+typedef struct FieldMethodInfo {
+ const char* classDescriptor;
+ const char* name;
+ const char* signature;
+} FieldMethodInfo;
+
+/*
+ * Get 2 little-endian bytes.
+ */
+static inline u2 get2LE(unsigned char const* pSrc)
+{
+ return pSrc[0] | (pSrc[1] << 8);
+}
+
+/*
+ * Get 4 little-endian bytes.
+ */
+static inline u4 get4LE(unsigned char const* pSrc)
+{
+ return pSrc[0] | (pSrc[1] << 8) | (pSrc[2] << 16) | (pSrc[3] << 24);
+}
+
+/*
+ * Converts a single-character primitive type into its human-readable
+ * equivalent.
+ */
+static const char* primitiveTypeLabel(char typeChar)
+{
+ switch (typeChar) {
+ case 'B': return "byte";
+ case 'C': return "char";
+ case 'D': return "double";
+ case 'F': return "float";
+ case 'I': return "int";
+ case 'J': return "long";
+ case 'S': return "short";
+ case 'V': return "void";
+ case 'Z': return "boolean";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+/*
+ * Converts a type descriptor to human-readable "dotted" form. For
+ * example, "Ljava/lang/String;" becomes "java.lang.String", and
+ * "[I" becomes "int[]". Also converts '$' to '.', which means this
+ * form can't be converted back to a descriptor.
+ */
+static char* descriptorToDot(const char* str)
+{
+ int targetLen = strlen(str);
+ int offset = 0;
+ int arrayDepth = 0;
+ char* newStr;
+
+ /* strip leading [s; will be added to end */
+ while (targetLen > 1 && str[offset] == '[') {
+ offset++;
+ targetLen--;
+ }
+ arrayDepth = offset;
+
+ if (targetLen == 1) {
+ /* primitive type */
+ str = primitiveTypeLabel(str[offset]);
+ offset = 0;
+ targetLen = strlen(str);
+ } else {
+ /* account for leading 'L' and trailing ';' */
+ if (targetLen >= 2 && str[offset] == 'L' &&
+ str[offset+targetLen-1] == ';')
+ {
+ targetLen -= 2;
+ offset++;
+ }
+ }
+
+ newStr = malloc(targetLen + arrayDepth * 2 +1);
+
+ /* copy class name over */
+ int i;
+ for (i = 0; i < targetLen; i++) {
+ char ch = str[offset + i];
+ newStr[i] = (ch == '/' || ch == '$') ? '.' : ch;
+ }
+
+ /* add the appropriate number of brackets for arrays */
+ while (arrayDepth-- > 0) {
+ newStr[i++] = '[';
+ newStr[i++] = ']';
+ }
+ newStr[i] = '\0';
+ assert(i == targetLen + arrayDepth * 2);
+
+ return newStr;
+}
+
+/*
+ * Converts the class name portion of a type descriptor to human-readable
+ * "dotted" form.
+ *
+ * Returns a newly-allocated string.
+ */
+static char* descriptorClassToDot(const char* str)
+{
+ const char* lastSlash;
+ char* newStr;
+ char* cp;
+
+ /* reduce to just the class name, trimming trailing ';' */
+ lastSlash = strrchr(str, '/');
+ if (lastSlash == NULL)
+ lastSlash = str + 1; /* start past 'L' */
+ else
+ lastSlash++; /* start past '/' */
+
+ newStr = strdup(lastSlash);
+ newStr[strlen(lastSlash)-1] = '\0';
+ for (cp = newStr; *cp != '\0'; cp++) {
+ if (*cp == '$')
+ *cp = '.';
+ }
+
+ return newStr;
+}
+
+/*
+ * Returns a quoted string representing the boolean value.
+ */
+static const char* quotedBool(bool val)
+{
+ if (val)
+ return "\"true\"";
+ else
+ return "\"false\"";
+}
+
+static const char* quotedVisibility(u4 accessFlags)
+{
+ if ((accessFlags & ACC_PUBLIC) != 0)
+ return "\"public\"";
+ else if ((accessFlags & ACC_PROTECTED) != 0)
+ return "\"protected\"";
+ else if ((accessFlags & ACC_PRIVATE) != 0)
+ return "\"private\"";
+ else
+ return "\"package\"";
+}
+
+/*
+ * Count the number of '1' bits in a word.
+ */
+static int countOnes(u4 val)
+{
+ int count = 0;
+
+ val = val - ((val >> 1) & 0x55555555);
+ val = (val & 0x33333333) + ((val >> 2) & 0x33333333);
+ count = (((val + (val >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
+
+ return count;
+}
+
+/*
+ * Flag for use with createAccessFlagStr().
+ */
+typedef enum AccessFor {
+ kAccessForClass = 0, kAccessForMethod = 1, kAccessForField = 2,
+ kAccessForMAX
+} AccessFor;
+
+/*
+ * Create a new string with human-readable access flags.
+ *
+ * In the base language the access_flags fields are type u2; in Dalvik
+ * they're u4.
+ */
+static char* createAccessFlagStr(u4 flags, AccessFor forWhat)
+{
+#define NUM_FLAGS 18
+ static const char* kAccessStrings[kAccessForMAX][NUM_FLAGS] = {
+ {
+ /* class, inner class */
+ "PUBLIC", /* 0x0001 */
+ "PRIVATE", /* 0x0002 */
+ "PROTECTED", /* 0x0004 */
+ "STATIC", /* 0x0008 */
+ "FINAL", /* 0x0010 */
+ "?", /* 0x0020 */
+ "?", /* 0x0040 */
+ "?", /* 0x0080 */
+ "?", /* 0x0100 */
+ "INTERFACE", /* 0x0200 */
+ "ABSTRACT", /* 0x0400 */
+ "?", /* 0x0800 */
+ "SYNTHETIC", /* 0x1000 */
+ "ANNOTATION", /* 0x2000 */
+ "ENUM", /* 0x4000 */
+ "?", /* 0x8000 */
+ "VERIFIED", /* 0x10000 */
+ "OPTIMIZED", /* 0x20000 */
+ },
+ {
+ /* method */
+ "PUBLIC", /* 0x0001 */
+ "PRIVATE", /* 0x0002 */
+ "PROTECTED", /* 0x0004 */
+ "STATIC", /* 0x0008 */
+ "FINAL", /* 0x0010 */
+ "SYNCHRONIZED", /* 0x0020 */
+ "BRIDGE", /* 0x0040 */
+ "VARARGS", /* 0x0080 */
+ "NATIVE", /* 0x0100 */
+ "?", /* 0x0200 */
+ "ABSTRACT", /* 0x0400 */
+ "STRICT", /* 0x0800 */
+ "SYNTHETIC", /* 0x1000 */
+ "?", /* 0x2000 */
+ "?", /* 0x4000 */
+ "MIRANDA", /* 0x8000 */
+ "CONSTRUCTOR", /* 0x10000 */
+ "DECLARED_SYNCHRONIZED", /* 0x20000 */
+ },
+ {
+ /* field */
+ "PUBLIC", /* 0x0001 */
+ "PRIVATE", /* 0x0002 */
+ "PROTECTED", /* 0x0004 */
+ "STATIC", /* 0x0008 */
+ "FINAL", /* 0x0010 */
+ "?", /* 0x0020 */
+ "VOLATILE", /* 0x0040 */
+ "TRANSIENT", /* 0x0080 */
+ "?", /* 0x0100 */
+ "?", /* 0x0200 */
+ "?", /* 0x0400 */
+ "?", /* 0x0800 */
+ "SYNTHETIC", /* 0x1000 */
+ "?", /* 0x2000 */
+ "ENUM", /* 0x4000 */
+ "?", /* 0x8000 */
+ "?", /* 0x10000 */
+ "?", /* 0x20000 */
+ },
+ };
+ const int kLongest = 21; /* strlen of longest string above */
+ int i, count;
+ char* str;
+ char* cp;
+
+ /*
+ * Allocate enough storage to hold the expected number of strings,
+ * plus a space between each. We over-allocate, using the longest
+ * string above as the base metric.
+ */
+ count = countOnes(flags);
+ cp = str = (char*) malloc(count * (kLongest+1) +1);
+
+ for (i = 0; i < NUM_FLAGS; i++) {
+ if (flags & 0x01) {
+ const char* accessStr = kAccessStrings[forWhat][i];
+ int len = strlen(accessStr);
+ if (cp != str)
+ *cp++ = ' ';
+
+ memcpy(cp, accessStr, len);
+ cp += len;
+ }
+ flags >>= 1;
+ }
+ *cp = '\0';
+
+ return str;
+}
+
+
+/*
+ * Copy character data from "data" to "out", converting non-ASCII values
+ * to printf format chars or an ASCII filler ('.' or '?').
+ *
+ * The output buffer must be able to hold (2*len)+1 bytes. The result is
+ * NUL-terminated.
+ */
+static void asciify(char* out, const unsigned char* data, size_t len)
+{
+ while (len--) {
+ if (*data < 0x20) {
+ /* could do more here, but we don't need them yet */
+ switch (*data) {
+ case '\0':
+ *out++ = '\\';
+ *out++ = '0';
+ break;
+ case '\n':
+ *out++ = '\\';
+ *out++ = 'n';
+ break;
+ default:
+ *out++ = '.';
+ break;
+ }
+ } else if (*data >= 0x80) {
+ *out++ = '?';
+ } else {
+ *out++ = *data;
+ }
+ data++;
+ }
+ *out = '\0';
+}
+
+/*
+ * Dump the file header.
+ */
+void dumpFileHeader(const DexFile* pDexFile)
+{
+ const DexOptHeader* pOptHeader = pDexFile->pOptHeader;
+ const DexHeader* pHeader = pDexFile->pHeader;
+ char sanitized[sizeof(pHeader->magic)*2 +1];
+
+ assert(sizeof(pHeader->magic) == sizeof(pOptHeader->magic));
+
+ if (pOptHeader != NULL) {
+ printf("Optimized DEX file header:\n");
+
+ asciify(sanitized, pOptHeader->magic, sizeof(pOptHeader->magic));
+ printf("magic : '%s'\n", sanitized);
+ printf("dex_offset : %d (0x%06x)\n",
+ pOptHeader->dexOffset, pOptHeader->dexOffset);
+ printf("dex_length : %d\n", pOptHeader->dexLength);
+ printf("deps_offset : %d (0x%06x)\n",
+ pOptHeader->depsOffset, pOptHeader->depsOffset);
+ printf("deps_length : %d\n", pOptHeader->depsLength);
+ printf("opt_offset : %d (0x%06x)\n",
+ pOptHeader->optOffset, pOptHeader->optOffset);
+ printf("opt_length : %d\n", pOptHeader->optLength);
+ printf("flags : %08x\n", pOptHeader->flags);
+ printf("checksum : %08x\n", pOptHeader->checksum);
+ printf("\n");
+ }
+
+ printf("DEX file header:\n");
+ asciify(sanitized, pHeader->magic, sizeof(pHeader->magic));
+ printf("magic : '%s'\n", sanitized);
+ printf("checksum : %08x\n", pHeader->checksum);
+ printf("signature : %02x%02x...%02x%02x\n",
+ pHeader->signature[0], pHeader->signature[1],
+ pHeader->signature[kSHA1DigestLen-2],
+ pHeader->signature[kSHA1DigestLen-1]);
+ printf("file_size : %d\n", pHeader->fileSize);
+ printf("header_size : %d\n", pHeader->headerSize);
+ printf("link_size : %d\n", pHeader->linkSize);
+ printf("link_off : %d (0x%06x)\n",
+ pHeader->linkOff, pHeader->linkOff);
+ printf("string_ids_size : %d\n", pHeader->stringIdsSize);
+ printf("string_ids_off : %d (0x%06x)\n",
+ pHeader->stringIdsOff, pHeader->stringIdsOff);
+ printf("type_ids_size : %d\n", pHeader->typeIdsSize);
+ printf("type_ids_off : %d (0x%06x)\n",
+ pHeader->typeIdsOff, pHeader->typeIdsOff);
+ printf("field_ids_size : %d\n", pHeader->fieldIdsSize);
+ printf("field_ids_off : %d (0x%06x)\n",
+ pHeader->fieldIdsOff, pHeader->fieldIdsOff);
+ printf("method_ids_size : %d\n", pHeader->methodIdsSize);
+ printf("method_ids_off : %d (0x%06x)\n",
+ pHeader->methodIdsOff, pHeader->methodIdsOff);
+ printf("class_defs_size : %d\n", pHeader->classDefsSize);
+ printf("class_defs_off : %d (0x%06x)\n",
+ pHeader->classDefsOff, pHeader->classDefsOff);
+ printf("data_size : %d\n", pHeader->dataSize);
+ printf("data_off : %d (0x%06x)\n",
+ pHeader->dataOff, pHeader->dataOff);
+ printf("\n");
+}
+
+/*
+ * Dump the "table of contents" for the opt area.
+ */
+void dumpOptDirectory(const DexFile* pDexFile)
+{
+ const DexOptHeader* pOptHeader = pDexFile->pOptHeader;
+ if (pOptHeader == NULL)
+ return;
+
+ printf("OPT section contents:\n");
+
+ const u4* pOpt = (const u4*) ((u1*) pOptHeader + pOptHeader->optOffset);
+
+ if (*pOpt == 0) {
+ printf("(1.0 format, only class lookup table is present)\n\n");
+ return;
+ }
+
+ /*
+ * The "opt" section is in "chunk" format: a 32-bit identifier, a 32-bit
+ * length, then the data. Chunks start on 64-bit boundaries.
+ */
+ while (*pOpt != kDexChunkEnd) {
+ const char* verboseStr;
+
+ u4 size = *(pOpt+1);
+
+ switch (*pOpt) {
+ case kDexChunkClassLookup:
+ verboseStr = "class lookup hash table";
+ break;
+ case kDexChunkRegisterMaps:
+ verboseStr = "register maps";
+ break;
+ default:
+ verboseStr = "(unknown chunk type)";
+ break;
+ }
+
+ printf("Chunk %08x (%c%c%c%c) - %s (%d bytes)\n", *pOpt,
+ *pOpt >> 24, (char)(*pOpt >> 16), (char)(*pOpt >> 8), (char)*pOpt,
+ verboseStr, size);
+
+ size = (size + 8 + 7) & ~7;
+ pOpt += size / sizeof(u4);
+ }
+ printf("\n");
+}
+
+/*
+ * Dump a class_def_item.
+ */
+void dumpClassDef(DexFile* pDexFile, int idx)
+{
+ const DexClassDef* pClassDef;
+ const u1* pEncodedData;
+ DexClassData* pClassData;
+
+ pClassDef = dexGetClassDef(pDexFile, idx);
+ pEncodedData = dexGetClassData(pDexFile, pClassDef);
+ pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL);
+
+ if (pClassData == NULL) {
+ fprintf(stderr, "Trouble reading class data\n");
+ return;
+ }
+
+ printf("Class #%d header:\n", idx);
+ printf("class_idx : %d\n", pClassDef->classIdx);
+ printf("access_flags : %d (0x%04x)\n",
+ pClassDef->accessFlags, pClassDef->accessFlags);
+ printf("superclass_idx : %d\n", pClassDef->superclassIdx);
+ printf("interfaces_off : %d (0x%06x)\n",
+ pClassDef->interfacesOff, pClassDef->interfacesOff);
+ printf("source_file_idx : %d\n", pClassDef->sourceFileIdx);
+ printf("annotations_off : %d (0x%06x)\n",
+ pClassDef->annotationsOff, pClassDef->annotationsOff);
+ printf("class_data_off : %d (0x%06x)\n",
+ pClassDef->classDataOff, pClassDef->classDataOff);
+ printf("static_fields_size : %d\n", pClassData->header.staticFieldsSize);
+ printf("instance_fields_size: %d\n",
+ pClassData->header.instanceFieldsSize);
+ printf("direct_methods_size : %d\n", pClassData->header.directMethodsSize);
+ printf("virtual_methods_size: %d\n",
+ pClassData->header.virtualMethodsSize);
+ printf("\n");
+
+ free(pClassData);
+}
+
+/*
+ * Dump an interface that a class declares to implement.
+ */
+void dumpInterface(const DexFile* pDexFile, const DexTypeItem* pTypeItem,
+ int i)
+{
+ const char* interfaceName =
+ dexStringByTypeIdx(pDexFile, pTypeItem->typeIdx);
+
+ if (gOptions.outputFormat == OUTPUT_PLAIN) {
+ printf(" #%d : '%s'\n", i, interfaceName);
+ } else {
+ char* dotted = descriptorToDot(interfaceName);
+ printf("<implements name=\"%s\">\n</implements>\n", dotted);
+ free(dotted);
+ }
+}
+
+/*
+ * Dump the catches table associated with the code.
+ */
+void dumpCatches(DexFile* pDexFile, const DexCode* pCode)
+{
+ u4 triesSize = pCode->triesSize;
+
+ if (triesSize == 0) {
+ printf(" catches : (none)\n");
+ return;
+ }
+
+ printf(" catches : %d\n", triesSize);
+
+ const DexTry* pTries = dexGetTries(pCode);
+ u4 i;
+
+ for (i = 0; i < triesSize; i++) {
+ const DexTry* pTry = &pTries[i];
+ u4 start = pTry->startAddr;
+ u4 end = start + pTry->insnCount;
+ DexCatchIterator iterator;
+
+ printf(" 0x%04x - 0x%04x\n", start, end);
+
+ dexCatchIteratorInit(&iterator, pCode, pTry->handlerOff);
+
+ for (;;) {
+ DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
+ const char* descriptor;
+
+ if (handler == NULL) {
+ break;
+ }
+
+ descriptor = (handler->typeIdx == kDexNoIndex) ? "<any>" :
+ dexStringByTypeIdx(pDexFile, handler->typeIdx);
+
+ printf(" %s -> 0x%04x\n", descriptor,
+ handler->address);
+ }
+ }
+}
+
+static int dumpPositionsCb(void *cnxt, u4 address, u4 lineNum)
+{
+ printf(" 0x%04x line=%d\n", address, lineNum);
+ return 0;
+}
+
+/*
+ * Dump the positions list.
+ */
+void dumpPositions(DexFile* pDexFile, const DexCode* pCode,
+ const DexMethod *pDexMethod)
+{
+ printf(" positions : \n");
+ const DexMethodId *pMethodId
+ = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
+ const char *classDescriptor
+ = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
+
+ dexDecodeDebugInfo(pDexFile, pCode, classDescriptor, pMethodId->protoIdx,
+ pDexMethod->accessFlags, dumpPositionsCb, NULL, NULL);
+}
+
+static void dumpLocalsCb(void *cnxt, u2 reg, u4 startAddress,
+ u4 endAddress, const char *name, const char *descriptor,
+ const char *signature)
+{
+ printf(" 0x%04x - 0x%04x reg=%d %s %s %s\n",
+ startAddress, endAddress, reg, name, descriptor,
+ signature);
+}
+
+/*
+ * Dump the locals list.
+ */
+void dumpLocals(DexFile* pDexFile, const DexCode* pCode,
+ const DexMethod *pDexMethod)
+{
+ printf(" locals : \n");
+
+ const DexMethodId *pMethodId
+ = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
+ const char *classDescriptor
+ = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
+
+ dexDecodeDebugInfo(pDexFile, pCode, classDescriptor, pMethodId->protoIdx,
+ pDexMethod->accessFlags, NULL, dumpLocalsCb, NULL);
+}
+
+/*
+ * Get information about a method.
+ */
+bool getMethodInfo(DexFile* pDexFile, u4 methodIdx, FieldMethodInfo* pMethInfo)
+{
+ const DexMethodId* pMethodId;
+
+ if (methodIdx >= pDexFile->pHeader->methodIdsSize)
+ return false;
+
+ pMethodId = dexGetMethodId(pDexFile, methodIdx);
+ pMethInfo->name = dexStringById(pDexFile, pMethodId->nameIdx);
+ pMethInfo->signature = dexCopyDescriptorFromMethodId(pDexFile, pMethodId);
+
+ pMethInfo->classDescriptor =
+ dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
+ return true;
+}
+
+/*
+ * Get information about a field.
+ */
+bool getFieldInfo(DexFile* pDexFile, u4 fieldIdx, FieldMethodInfo* pFieldInfo)
+{
+ const DexFieldId* pFieldId;
+
+ if (fieldIdx >= pDexFile->pHeader->fieldIdsSize)
+ return false;
+
+ pFieldId = dexGetFieldId(pDexFile, fieldIdx);
+ pFieldInfo->name = dexStringById(pDexFile, pFieldId->nameIdx);
+ pFieldInfo->signature = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
+ pFieldInfo->classDescriptor =
+ dexStringByTypeIdx(pDexFile, pFieldId->classIdx);
+ return true;
+}
+
+
+/*
+ * Look up a class' descriptor.
+ */
+const char* getClassDescriptor(DexFile* pDexFile, u4 classIdx)
+{
+ return dexStringByTypeIdx(pDexFile, classIdx);
+}
+
+/*
+ * Dump a single instruction.
+ */
+void dumpInstruction(DexFile* pDexFile, const DexCode* pCode, int insnIdx,
+ int insnWidth, const DecodedInstruction* pDecInsn)
+{
+ const u2* insns = pCode->insns;
+ int i;
+
+ printf("%06x:", ((u1*)insns - pDexFile->baseAddr) + insnIdx*2);
+ for (i = 0; i < 8; i++) {
+ if (i < insnWidth) {
+ if (i == 7) {
+ printf(" ... ");
+ } else {
+ /* print 16-bit value in little-endian order */
+ const u1* bytePtr = (const u1*) &insns[insnIdx+i];
+ printf(" %02x%02x", bytePtr[0], bytePtr[1]);
+ }
+ } else {
+ fputs(" ", stdout);
+ }
+ }
+
+ if (pDecInsn->opCode == OP_NOP) {
+ u2 instr = get2LE((const u1*) &insns[insnIdx]);
+ if (instr == kPackedSwitchSignature) {
+ printf("|%04x: packed-switch-data (%d units)",
+ insnIdx, insnWidth);
+ } else if (instr == kSparseSwitchSignature) {
+ printf("|%04x: sparse-switch-data (%d units)",
+ insnIdx, insnWidth);
+ } else if (instr == kArrayDataSignature) {
+ printf("|%04x: array-data (%d units)",
+ insnIdx, insnWidth);
+ } else {
+ printf("|%04x: nop // spacer", insnIdx);
+ }
+ } else {
+ printf("|%04x: %s", insnIdx, dexGetOpcodeName(pDecInsn->opCode));
+ }
+
+ switch (dexGetInstrFormat(gInstrFormat, pDecInsn->opCode)) {
+ case kFmt10x: // op
+ break;
+ case kFmt12x: // op vA, vB
+ printf(" v%d, v%d", pDecInsn->vA, pDecInsn->vB);
+ break;
+ case kFmt11n: // op vA, #+B
+ printf(" v%d, #int %d // #%x",
+ pDecInsn->vA, (s4)pDecInsn->vB, (u1)pDecInsn->vB);
+ break;
+ case kFmt11x: // op vAA
+ printf(" v%d", pDecInsn->vA);
+ break;
+ case kFmt10t: // op +AA
+ case kFmt20t: // op +AAAA
+ {
+ s4 targ = (s4) pDecInsn->vA;
+ printf(" %04x // %c%04x",
+ insnIdx + targ,
+ (targ < 0) ? '-' : '+',
+ (targ < 0) ? -targ : targ);
+ }
+ break;
+ case kFmt22x: // op vAA, vBBBB
+ printf(" v%d, v%d", pDecInsn->vA, pDecInsn->vB);
+ break;
+ case kFmt21t: // op vAA, +BBBB
+ {
+ s4 targ = (s4) pDecInsn->vB;
+ printf(" v%d, %04x // %c%04x", pDecInsn->vA,
+ insnIdx + targ,
+ (targ < 0) ? '-' : '+',
+ (targ < 0) ? -targ : targ);
+ }
+ break;
+ case kFmt21s: // op vAA, #+BBBB
+ printf(" v%d, #int %d // #%x",
+ pDecInsn->vA, (s4)pDecInsn->vB, (u2)pDecInsn->vB);
+ break;
+ case kFmt21h: // op vAA, #+BBBB0000[00000000]
+ // The printed format varies a bit based on the actual opcode.
+ if (pDecInsn->opCode == OP_CONST_HIGH16) {
+ s4 value = pDecInsn->vB << 16;
+ printf(" v%d, #int %d // #%x",
+ pDecInsn->vA, value, (u2)pDecInsn->vB);
+ } else {
+ s8 value = ((s8) pDecInsn->vB) << 48;
+ printf(" v%d, #long %lld // #%x",
+ pDecInsn->vA, value, (u2)pDecInsn->vB);
+ }
+ break;
+ case kFmt21c: // op vAA, thing@BBBB
+ if (pDecInsn->opCode == OP_CONST_STRING) {
+ printf(" v%d, \"%s\" // string@%04x", pDecInsn->vA,
+ dexStringById(pDexFile, pDecInsn->vB), pDecInsn->vB);
+ } else if (pDecInsn->opCode == OP_CHECK_CAST ||
+ pDecInsn->opCode == OP_NEW_INSTANCE ||
+ pDecInsn->opCode == OP_CONST_CLASS)
+ {
+ printf(" v%d, %s // class@%04x", pDecInsn->vA,
+ getClassDescriptor(pDexFile, pDecInsn->vB), pDecInsn->vB);
+ } else /* OP_SGET* */ {
+ FieldMethodInfo fieldInfo;
+ if (getFieldInfo(pDexFile, pDecInsn->vB, &fieldInfo)) {
+ printf(" v%d, %s.%s:%s // field@%04x", pDecInsn->vA,
+ fieldInfo.classDescriptor, fieldInfo.name,
+ fieldInfo.signature, pDecInsn->vB);
+ } else {
+ printf(" v%d, ??? // field@%04x", pDecInsn->vA, pDecInsn->vB);
+ }
+ }
+ break;
+ case kFmt23x: // op vAA, vBB, vCC
+ printf(" v%d, v%d, v%d", pDecInsn->vA, pDecInsn->vB, pDecInsn->vC);
+ break;
+ case kFmt22b: // op vAA, vBB, #+CC
+ printf(" v%d, v%d, #int %d // #%02x",
+ pDecInsn->vA, pDecInsn->vB, (s4)pDecInsn->vC, (u1)pDecInsn->vC);
+ break;
+ case kFmt22t: // op vA, vB, +CCCC
+ {
+ s4 targ = (s4) pDecInsn->vC;
+ printf(" v%d, v%d, %04x // %c%04x", pDecInsn->vA, pDecInsn->vB,
+ insnIdx + targ,
+ (targ < 0) ? '-' : '+',
+ (targ < 0) ? -targ : targ);
+ }
+ break;
+ case kFmt22s: // op vA, vB, #+CCCC
+ printf(" v%d, v%d, #int %d // #%04x",
+ pDecInsn->vA, pDecInsn->vB, (s4)pDecInsn->vC, (u2)pDecInsn->vC);
+ break;
+ case kFmt22c: // op vA, vB, thing@CCCC
+ if (pDecInsn->opCode == OP_INSTANCE_OF ||
+ pDecInsn->opCode == OP_NEW_ARRAY)
+ {
+ printf(" v%d, v%d, %s // class@%04x",
+ pDecInsn->vA, pDecInsn->vB,
+ getClassDescriptor(pDexFile, pDecInsn->vC), pDecInsn->vC);
+ } else {
+ /* iget* and iput*, including dexopt-generated -volatile */
+ FieldMethodInfo fieldInfo;
+ if (getFieldInfo(pDexFile, pDecInsn->vC, &fieldInfo)) {
+ printf(" v%d, v%d, %s.%s:%s // field@%04x", pDecInsn->vA,
+ pDecInsn->vB, fieldInfo.classDescriptor, fieldInfo.name,
+ fieldInfo.signature, pDecInsn->vC);
+ } else {
+ printf(" v%d, v%d, ??? // field@%04x", pDecInsn->vA,
+ pDecInsn->vB, pDecInsn->vC);
+ }
+ }
+ break;
+ case kFmt22cs: // [opt] op vA, vB, field offset CCCC
+ printf(" v%d, v%d, [obj+%04x]",
+ pDecInsn->vA, pDecInsn->vB, pDecInsn->vC);
+ break;
+ case kFmt30t:
+ printf(" #%08x", pDecInsn->vA);
+ break;
+ case kFmt31i: // op vAA, #+BBBBBBBB
+ {
+ /* this is often, but not always, a float */
+ union {
+ float f;
+ u4 i;
+ } conv;
+ conv.i = pDecInsn->vB;
+ printf(" v%d, #float %f // #%08x",
+ pDecInsn->vA, conv.f, pDecInsn->vB);
+ }
+ break;
+ case kFmt31c: // op vAA, thing@BBBBBBBB
+ printf(" v%d, \"%s\" // string@%08x", pDecInsn->vA,
+ dexStringById(pDexFile, pDecInsn->vB), pDecInsn->vB);
+ break;
+ case kFmt31t: // op vAA, offset +BBBBBBBB
+ printf(" v%d, %08x // +%08x",
+ pDecInsn->vA, insnIdx + pDecInsn->vB, pDecInsn->vB);
+ break;
+ case kFmt32x: // op vAAAA, vBBBB
+ printf(" v%d, v%d", pDecInsn->vA, pDecInsn->vB);
+ break;
+ case kFmt35c: // op vB, {vD, vE, vF, vG, vA}, thing@CCCC
+ {
+ /* NOTE: decoding of 35c doesn't quite match spec */
+ fputs(" {", stdout);
+ for (i = 0; i < (int) pDecInsn->vA; i++) {
+ if (i == 0)
+ printf("v%d", pDecInsn->arg[i]);
+ else
+ printf(", v%d", pDecInsn->arg[i]);
+ }
+ if (pDecInsn->opCode == OP_FILLED_NEW_ARRAY) {
+ printf("}, %s // class@%04x",
+ getClassDescriptor(pDexFile, pDecInsn->vB), pDecInsn->vB);
+ } else {
+ FieldMethodInfo methInfo;
+ if (getMethodInfo(pDexFile, pDecInsn->vB, &methInfo)) {
+ printf("}, %s.%s:%s // method@%04x",
+ methInfo.classDescriptor, methInfo.name,
+ methInfo.signature, pDecInsn->vB);
+ } else {
+ printf("}, ??? // method@%04x", pDecInsn->vB);
+ }
+ }
+ }
+ break;
+ case kFmt35ms: // [opt] invoke-virtual+super
+ case kFmt35fs: // [opt] invoke-interface
+ {
+ fputs(" {", stdout);
+ for (i = 0; i < (int) pDecInsn->vA; i++) {
+ if (i == 0)
+ printf("v%d", pDecInsn->arg[i]);
+ else
+ printf(", v%d", pDecInsn->arg[i]);
+ }
+ printf("}, [%04x] // vtable #%04x", pDecInsn->vB, pDecInsn->vB);
+ }
+ break;
+ case kFmt3rc: // op {vCCCC .. v(CCCC+AA-1)}, meth@BBBB
+ {
+ /*
+ * This doesn't match the "dx" output when some of the args are
+ * 64-bit values -- dx only shows the first register.
+ */
+ fputs(" {", stdout);
+ for (i = 0; i < (int) pDecInsn->vA; i++) {
+ if (i == 0)
+ printf("v%d", pDecInsn->vC + i);
+ else
+ printf(", v%d", pDecInsn->vC + i);
+ }
+ if (pDecInsn->opCode == OP_FILLED_NEW_ARRAY_RANGE) {
+ printf("}, %s // class@%04x",
+ getClassDescriptor(pDexFile, pDecInsn->vB), pDecInsn->vB);
+ } else {
+ FieldMethodInfo methInfo;
+ if (getMethodInfo(pDexFile, pDecInsn->vB, &methInfo)) {
+ printf("}, %s.%s:%s // method@%04x",
+ methInfo.classDescriptor, methInfo.name,
+ methInfo.signature, pDecInsn->vB);
+ } else {
+ printf("}, ??? // method@%04x", pDecInsn->vB);
+ }
+ }
+ }
+ break;
+ case kFmt3rms: // [opt] invoke-virtual+super/range
+ case kFmt3rfs: // [opt] invoke-interface/range
+ {
+ /*
+ * This doesn't match the "dx" output when some of the args are
+ * 64-bit values -- dx only shows the first register.
+ */
+ fputs(" {", stdout);
+ for (i = 0; i < (int) pDecInsn->vA; i++) {
+ if (i == 0)
+ printf("v%d", pDecInsn->vC + i);
+ else
+ printf(", v%d", pDecInsn->vC + i);
+ }
+ printf("}, [%04x] // vtable #%04x", pDecInsn->vB, pDecInsn->vB);
+ }
+ break;
+ case kFmt3rinline: // [opt] execute-inline/range
+ {
+ fputs(" {", stdout);
+ for (i = 0; i < (int) pDecInsn->vA; i++) {
+ if (i == 0)
+ printf("v%d", pDecInsn->vC + i);
+ else
+ printf(", v%d", pDecInsn->vC + i);
+ }
+ printf("}, [%04x] // inline #%04x", pDecInsn->vB, pDecInsn->vB);
+ }
+ break;
+ case kFmt3inline: // [opt] inline invoke
+ {
+#if 0
+ const InlineOperation* inlineOpsTable = dvmGetInlineOpsTable();
+ u4 tableLen = dvmGetInlineOpsTableLength();
+#endif
+
+ fputs(" {", stdout);
+ for (i = 0; i < (int) pDecInsn->vA; i++) {
+ if (i == 0)
+ printf("v%d", pDecInsn->arg[i]);
+ else
+ printf(", v%d", pDecInsn->arg[i]);
+ }
+#if 0
+ if (pDecInsn->vB < tableLen) {
+ printf("}, %s.%s:%s // inline #%04x",
+ inlineOpsTable[pDecInsn->vB].classDescriptor,
+ inlineOpsTable[pDecInsn->vB].methodName,
+ inlineOpsTable[pDecInsn->vB].methodSignature,
+ pDecInsn->vB);
+ } else {
+#endif
+ printf("}, [%04x] // inline #%04x", pDecInsn->vB, pDecInsn->vB);
+#if 0
+ }
+#endif
+ }
+ break;
+ case kFmt51l: // op vAA, #+BBBBBBBBBBBBBBBB
+ {
+ /* this is often, but not always, a double */
+ union {
+ double d;
+ u8 j;
+ } conv;
+ conv.j = pDecInsn->vB_wide;
+ printf(" v%d, #double %f // #%016llx",
+ pDecInsn->vA, conv.d, pDecInsn->vB_wide);
+ }
+ break;
+ case kFmtUnknown:
+ break;
+ default:
+ printf(" ???");
+ break;
+ }
+
+
+ putchar('\n');
+
+}
+
+/*
+ * Dump a bytecode disassembly.
+ */
+void dumpBytecodes(DexFile* pDexFile, const DexMethod* pDexMethod)
+{
+ const DexCode* pCode = dexGetCode(pDexFile, pDexMethod);
+ const u2* insns;
+ int insnIdx;
+ FieldMethodInfo methInfo;
+ int startAddr;
+ char* className = NULL;
+
+ assert(pCode->insnsSize > 0);
+ insns = pCode->insns;
+
+ getMethodInfo(pDexFile, pDexMethod->methodIdx, &methInfo);
+ startAddr = ((u1*)pCode - pDexFile->baseAddr);
+ className = descriptorToDot(methInfo.classDescriptor);
+
+ printf("%06x: |[%06x] %s.%s:%s\n",
+ startAddr, startAddr,
+ className, methInfo.name, methInfo.signature);
+
+ insnIdx = 0;
+ while (insnIdx < (int) pCode->insnsSize) {
+ int insnWidth;
+ OpCode opCode;
+ DecodedInstruction decInsn;
+ u2 instr;
+
+ instr = get2LE((const u1*)insns);
+ if (instr == kPackedSwitchSignature) {
+ insnWidth = 4 + get2LE((const u1*)(insns+1)) * 2;
+ } else if (instr == kSparseSwitchSignature) {
+ insnWidth = 2 + get2LE((const u1*)(insns+1)) * 4;
+ } else if (instr == kArrayDataSignature) {
+ int width = get2LE((const u1*)(insns+1));
+ int size = get2LE((const u1*)(insns+2)) |
+ (get2LE((const u1*)(insns+3))<<16);
+ // The plus 1 is to round up for odd size and width
+ insnWidth = 4 + ((size * width) + 1) / 2;
+ } else {
+ opCode = instr & 0xff;
+ insnWidth = dexGetInstrWidthAbs(gInstrWidth, opCode);
+ if (insnWidth == 0) {
+ fprintf(stderr,
+ "GLITCH: zero-width instruction at idx=0x%04x\n", insnIdx);
+ break;
+ }
+ }
+
+ dexDecodeInstruction(gInstrFormat, insns, &decInsn);
+ dumpInstruction(pDexFile, pCode, insnIdx, insnWidth, &decInsn);
+
+ insns += insnWidth;
+ insnIdx += insnWidth;
+ }
+
+ free(className);
+}
+
+/*
+ * Dump a "code" struct.
+ */
+void dumpCode(DexFile* pDexFile, const DexMethod* pDexMethod)
+{
+ const DexCode* pCode = dexGetCode(pDexFile, pDexMethod);
+
+ printf(" registers : %d\n", pCode->registersSize);
+ printf(" ins : %d\n", pCode->insSize);
+ printf(" outs : %d\n", pCode->outsSize);
+ printf(" insns size : %d 16-bit code units\n", pCode->insnsSize);
+
+ if (gOptions.disassemble)
+ dumpBytecodes(pDexFile, pDexMethod);
+
+ dumpCatches(pDexFile, pCode);
+ /* both of these are encoded in debug info */
+ dumpPositions(pDexFile, pCode, pDexMethod);
+ dumpLocals(pDexFile, pCode, pDexMethod);
+}
+
+/*
+ * Dump a method.
+ */
+void dumpMethod(DexFile* pDexFile, const DexMethod* pDexMethod, int i)
+{
+ const DexMethodId* pMethodId;
+ const char* backDescriptor;
+ const char* name;
+ char* typeDescriptor = NULL;
+ char* accessStr = NULL;
+
+ if (gOptions.exportsOnly &&
+ (pDexMethod->accessFlags & (ACC_PUBLIC | ACC_PROTECTED)) == 0)
+ {
+ return;
+ }
+
+ pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
+ name = dexStringById(pDexFile, pMethodId->nameIdx);
+ typeDescriptor = dexCopyDescriptorFromMethodId(pDexFile, pMethodId);
+
+ backDescriptor = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
+
+ accessStr = createAccessFlagStr(pDexMethod->accessFlags,
+ kAccessForMethod);
+
+ if (gOptions.outputFormat == OUTPUT_PLAIN) {
+ printf(" #%d : (in %s)\n", i, backDescriptor);
+ printf(" name : '%s'\n", name);
+ printf(" type : '%s'\n", typeDescriptor);
+ printf(" access : 0x%04x (%s)\n",
+ pDexMethod->accessFlags, accessStr);
+
+ if (pDexMethod->codeOff == 0) {
+ printf(" code : (none)\n");
+ } else {
+ printf(" code -\n");
+ dumpCode(pDexFile, pDexMethod);
+ }
+
+ if (gOptions.disassemble)
+ putchar('\n');
+ } else if (gOptions.outputFormat == OUTPUT_XML) {
+ bool constructor = (name[0] == '<');
+
+ if (constructor) {
+ char* tmp;
+
+ tmp = descriptorClassToDot(backDescriptor);
+ printf("<constructor name=\"%s\"\n", tmp);
+ free(tmp);
+
+ tmp = descriptorToDot(backDescriptor);
+ printf(" type=\"%s\"\n", tmp);
+ free(tmp);
+ } else {
+ printf("<method name=\"%s\"\n", name);
+
+ const char* returnType = strrchr(typeDescriptor, ')');
+ if (returnType == NULL) {
+ fprintf(stderr, "bad method type descriptor '%s'\n",
+ typeDescriptor);
+ goto bail;
+ }
+
+ char* tmp = descriptorToDot(returnType+1);
+ printf(" return=\"%s\"\n", tmp);
+ free(tmp);
+
+ printf(" abstract=%s\n",
+ quotedBool((pDexMethod->accessFlags & ACC_ABSTRACT) != 0));
+ printf(" native=%s\n",
+ quotedBool((pDexMethod->accessFlags & ACC_NATIVE) != 0));
+
+ bool isSync =
+ (pDexMethod->accessFlags & ACC_SYNCHRONIZED) != 0 ||
+ (pDexMethod->accessFlags & ACC_DECLARED_SYNCHRONIZED) != 0;
+ printf(" synchronized=%s\n", quotedBool(isSync));
+ }
+
+ printf(" static=%s\n",
+ quotedBool((pDexMethod->accessFlags & ACC_STATIC) != 0));
+ printf(" final=%s\n",
+ quotedBool((pDexMethod->accessFlags & ACC_FINAL) != 0));
+ // "deprecated=" not knowable w/o parsing annotations
+ printf(" visibility=%s\n",
+ quotedVisibility(pDexMethod->accessFlags));
+
+ printf(">\n");
+
+ /*
+ * Parameters.
+ */
+ if (typeDescriptor[0] != '(') {
+ fprintf(stderr, "ERROR: bad descriptor '%s'\n", typeDescriptor);
+ goto bail;
+ }
+
+ char tmpBuf[strlen(typeDescriptor)+1]; /* more than big enough */
+ int argNum = 0;
+
+ const char* base = typeDescriptor+1;
+
+ while (*base != ')') {
+ char* cp = tmpBuf;
+
+ while (*base == '[')
+ *cp++ = *base++;
+
+ if (*base == 'L') {
+ /* copy through ';' */
+ do {
+ *cp = *base++;
+ } while (*cp++ != ';');
+ } else {
+ /* primitive char, copy it */
+ if (strchr("ZBCSIFJD", *base) == NULL) {
+ fprintf(stderr, "ERROR: bad method signature '%s'\n", base);
+ goto bail;
+ }
+ *cp++ = *base++;
+ }
+
+ /* null terminate and display */
+ *cp++ = '\0';
+
+ char* tmp = descriptorToDot(tmpBuf);
+ printf("<parameter name=\"arg%d\" type=\"%s\">\n</parameter>\n",
+ argNum++, tmp);
+ free(tmp);
+ }
+
+ if (constructor)
+ printf("</constructor>\n");
+ else
+ printf("</method>\n");
+ }
+
+bail:
+ free(typeDescriptor);
+ free(accessStr);
+}
+
+/*
+ * Dump a static (class) field.
+ */
+void dumpSField(const DexFile* pDexFile, const DexField* pSField, int i)
+{
+ const DexFieldId* pFieldId;
+ const char* backDescriptor;
+ const char* name;
+ const char* typeDescriptor;
+ char* accessStr;
+
+ if (gOptions.exportsOnly &&
+ (pSField->accessFlags & (ACC_PUBLIC | ACC_PROTECTED)) == 0)
+ {
+ return;
+ }
+
+ pFieldId = dexGetFieldId(pDexFile, pSField->fieldIdx);
+ name = dexStringById(pDexFile, pFieldId->nameIdx);
+ typeDescriptor = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
+ backDescriptor = dexStringByTypeIdx(pDexFile, pFieldId->classIdx);
+
+ accessStr = createAccessFlagStr(pSField->accessFlags, kAccessForField);
+
+ if (gOptions.outputFormat == OUTPUT_PLAIN) {
+ printf(" #%d : (in %s)\n", i, backDescriptor);
+ printf(" name : '%s'\n", name);
+ printf(" type : '%s'\n", typeDescriptor);
+ printf(" access : 0x%04x (%s)\n",
+ pSField->accessFlags, accessStr);
+ } else if (gOptions.outputFormat == OUTPUT_XML) {
+ char* tmp;
+
+ printf("<field name=\"%s\"\n", name);
+
+ tmp = descriptorToDot(typeDescriptor);
+ printf(" type=\"%s\"\n", tmp);
+ free(tmp);
+
+ printf(" transient=%s\n",
+ quotedBool((pSField->accessFlags & ACC_TRANSIENT) != 0));
+ printf(" volatile=%s\n",
+ quotedBool((pSField->accessFlags & ACC_VOLATILE) != 0));
+ // "value=" not knowable w/o parsing annotations
+ printf(" static=%s\n",
+ quotedBool((pSField->accessFlags & ACC_STATIC) != 0));
+ printf(" final=%s\n",
+ quotedBool((pSField->accessFlags & ACC_FINAL) != 0));
+ // "deprecated=" not knowable w/o parsing annotations
+ printf(" visibility=%s\n",
+ quotedVisibility(pSField->accessFlags));
+ printf(">\n</field>\n");
+ }
+
+ free(accessStr);
+}
+
+/*
+ * Dump an instance field.
+ */
+void dumpIField(const DexFile* pDexFile, const DexField* pIField, int i)
+{
+ dumpSField(pDexFile, pIField, i);
+}
+
+/*
+ * Dump the class.
+ *
+ * Note "idx" is a DexClassDef index, not a DexTypeId index.
+ *
+ * If "*pLastPackage" is NULL or does not match the current class' package,
+ * the value will be replaced with a newly-allocated string.
+ */
+void dumpClass(DexFile* pDexFile, int idx, char** pLastPackage)
+{
+ const DexTypeList* pInterfaces;
+ const DexClassDef* pClassDef;
+ DexClassData* pClassData = NULL;
+ const u1* pEncodedData;
+ const char* fileName;
+ const char* classDescriptor;
+ const char* superclassDescriptor;
+ char* accessStr = NULL;
+ int i;
+
+ pClassDef = dexGetClassDef(pDexFile, idx);
+
+ if (gOptions.exportsOnly && (pClassDef->accessFlags & ACC_PUBLIC) == 0) {
+ //printf("<!-- omitting non-public class %s -->\n",
+ // classDescriptor);
+ goto bail;
+ }
+
+ pEncodedData = dexGetClassData(pDexFile, pClassDef);
+ pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL);
+
+ if (pClassData == NULL) {
+ printf("Trouble reading class data (#%d)\n", idx);
+ goto bail;
+ }
+
+ classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
+
+ /*
+ * For the XML output, show the package name. Ideally we'd gather
+ * up the classes, sort them, and dump them alphabetically so the
+ * package name wouldn't jump around, but that's not a great plan
+ * for something that needs to run on the device.
+ */
+ if (!(classDescriptor[0] == 'L' &&
+ classDescriptor[strlen(classDescriptor)-1] == ';'))
+ {
+ /* arrays and primitives should not be defined explicitly */
+ fprintf(stderr, "Malformed class name '%s'\n", classDescriptor);
+ /* keep going? */
+ } else if (gOptions.outputFormat == OUTPUT_XML) {
+ char* mangle;
+ char* lastSlash;
+ char* cp;
+
+ mangle = strdup(classDescriptor + 1);
+ mangle[strlen(mangle)-1] = '\0';
+
+ /* reduce to just the package name */
+ lastSlash = strrchr(mangle, '/');
+ if (lastSlash != NULL) {
+ *lastSlash = '\0';
+ } else {
+ *mangle = '\0';
+ }
+
+ for (cp = mangle; *cp != '\0'; cp++) {
+ if (*cp == '/')
+ *cp = '.';
+ }
+
+ if (*pLastPackage == NULL || strcmp(mangle, *pLastPackage) != 0) {
+ /* start of a new package */
+ if (*pLastPackage != NULL)
+ printf("</package>\n");
+ printf("<package name=\"%s\"\n>\n", mangle);
+ free(*pLastPackage);
+ *pLastPackage = mangle;
+ } else {
+ free(mangle);
+ }
+ }
+
+ accessStr = createAccessFlagStr(pClassDef->accessFlags, kAccessForClass);
+
+ if (pClassDef->superclassIdx == kDexNoIndex) {
+ superclassDescriptor = NULL;
+ } else {
+ superclassDescriptor =
+ dexStringByTypeIdx(pDexFile, pClassDef->superclassIdx);
+ }
+
+ if (gOptions.outputFormat == OUTPUT_PLAIN) {
+ printf("Class #%d -\n", idx);
+ printf(" Class descriptor : '%s'\n", classDescriptor);
+ printf(" Access flags : 0x%04x (%s)\n",
+ pClassDef->accessFlags, accessStr);
+
+ if (superclassDescriptor != NULL)
+ printf(" Superclass : '%s'\n", superclassDescriptor);
+
+ printf(" Interfaces -\n");
+ } else {
+ char* tmp;
+
+ tmp = descriptorClassToDot(classDescriptor);
+ printf("<class name=\"%s\"\n", tmp);
+ free(tmp);
+
+ if (superclassDescriptor != NULL) {
+ tmp = descriptorToDot(superclassDescriptor);
+ printf(" extends=\"%s\"\n", tmp);
+ free(tmp);
+ }
+ printf(" abstract=%s\n",
+ quotedBool((pClassDef->accessFlags & ACC_ABSTRACT) != 0));
+ printf(" static=%s\n",
+ quotedBool((pClassDef->accessFlags & ACC_STATIC) != 0));
+ printf(" final=%s\n",
+ quotedBool((pClassDef->accessFlags & ACC_FINAL) != 0));
+ // "deprecated=" not knowable w/o parsing annotations
+ printf(" visibility=%s\n",
+ quotedVisibility(pClassDef->accessFlags));
+ printf(">\n");
+ }
+ pInterfaces = dexGetInterfacesList(pDexFile, pClassDef);
+ if (pInterfaces != NULL) {
+ for (i = 0; i < (int) pInterfaces->size; i++)
+ dumpInterface(pDexFile, dexGetTypeItem(pInterfaces, i), i);
+ }
+
+ if (gOptions.outputFormat == OUTPUT_PLAIN)
+ printf(" Static fields -\n");
+ for (i = 0; i < (int) pClassData->header.staticFieldsSize; i++) {
+ dumpSField(pDexFile, &pClassData->staticFields[i], i);
+ }
+
+ if (gOptions.outputFormat == OUTPUT_PLAIN)
+ printf(" Instance fields -\n");
+ for (i = 0; i < (int) pClassData->header.instanceFieldsSize; i++) {
+ dumpIField(pDexFile, &pClassData->instanceFields[i], i);
+ }
+
+ if (gOptions.outputFormat == OUTPUT_PLAIN)
+ printf(" Direct methods -\n");
+ for (i = 0; i < (int) pClassData->header.directMethodsSize; i++) {
+ dumpMethod(pDexFile, &pClassData->directMethods[i], i);
+ }
+
+ if (gOptions.outputFormat == OUTPUT_PLAIN)
+ printf(" Virtual methods -\n");
+ for (i = 0; i < (int) pClassData->header.virtualMethodsSize; i++) {
+ dumpMethod(pDexFile, &pClassData->virtualMethods[i], i);
+ }
+
+ // TODO: Annotations.
+
+ if (pClassDef->sourceFileIdx != kDexNoIndex)
+ fileName = dexStringById(pDexFile, pClassDef->sourceFileIdx);
+ else
+ fileName = "unknown";
+
+ if (gOptions.outputFormat == OUTPUT_PLAIN) {
+ printf(" source_file_idx : %d (%s)\n",
+ pClassDef->sourceFileIdx, fileName);
+ printf("\n");
+ }
+
+ if (gOptions.outputFormat == OUTPUT_XML) {
+ printf("</class>\n");
+ }
+
+bail:
+ free(pClassData);
+ free(accessStr);
+}
+
+
+/*
+ * Advance "ptr" to ensure 32-bit alignment.
+ */
+static inline const u1* align32(const u1* ptr)
+{
+ return (u1*) (((int) ptr + 3) & ~0x03);
+}
+
+
+/*
+ * Dump a map in the "differential" format.
+ *
+ * TODO: show a hex dump of the compressed data. (We can show the
+ * uncompressed data if we move the compression code to libdex; otherwise
+ * it's too complex to merit a fast & fragile implementation here.)
+ */
+void dumpDifferentialCompressedMap(const u1** pData)
+{
+ const u1* data = *pData;
+ const u1* dataStart = data -1; // format byte already removed
+ u1 regWidth;
+ u2 numEntries;
+
+ /* standard header */
+ regWidth = *data++;
+ numEntries = *data++;
+ numEntries |= (*data++) << 8;
+
+ /* compressed data begins with the compressed data length */
+ int compressedLen = readUnsignedLeb128(&data);
+ int addrWidth = 1;
+ if ((*data & 0x80) != 0)
+ addrWidth++;
+
+ int origLen = 4 + (addrWidth + regWidth) * numEntries;
+ int compLen = (data - dataStart) + compressedLen;
+
+ printf(" (differential compression %d -> %d [%d -> %d])\n",
+ origLen, compLen,
+ (addrWidth + regWidth) * numEntries, compressedLen);
+
+ /* skip past end of entry */
+ data += compressedLen;
+
+ *pData = data;
+}
+
+/*
+ * Dump register map contents of the current method.
+ *
+ * "*pData" should point to the start of the register map data. Advances
+ * "*pData" to the start of the next map.
+ */
+void dumpMethodMap(DexFile* pDexFile, const DexMethod* pDexMethod, int idx,
+ const u1** pData)
+{
+ const u1* data = *pData;
+ const DexMethodId* pMethodId;
+ const char* name;
+ int offset = data - (u1*) pDexFile->pOptHeader;
+
+ pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
+ name = dexStringById(pDexFile, pMethodId->nameIdx);
+ printf(" #%d: 0x%08x %s\n", idx, offset, name);
+
+ u1 format;
+ int addrWidth;
+
+ format = *data++;
+ if (format == 1) { /* kRegMapFormatNone */
+ /* no map */
+ printf(" (no map)\n");
+ addrWidth = 0;
+ } else if (format == 2) { /* kRegMapFormatCompact8 */
+ addrWidth = 1;
+ } else if (format == 3) { /* kRegMapFormatCompact16 */
+ addrWidth = 2;
+ } else if (format == 4) { /* kRegMapFormatDifferential */
+ dumpDifferentialCompressedMap(&data);
+ goto bail;
+ } else {
+ printf(" (unknown format %d!)\n", format);
+ /* don't know how to skip data; failure will cascade to end of class */
+ goto bail;
+ }
+
+ if (addrWidth > 0) {
+ u1 regWidth;
+ u2 numEntries;
+ int idx, addr, byte;
+
+ regWidth = *data++;
+ numEntries = *data++;
+ numEntries |= (*data++) << 8;
+
+ for (idx = 0; idx < numEntries; idx++) {
+ addr = *data++;
+ if (addrWidth > 1)
+ addr |= (*data++) << 8;
+
+ printf(" %4x:", addr);
+ for (byte = 0; byte < regWidth; byte++) {
+ printf(" %02x", *data++);
+ }
+ printf("\n");
+ }
+ }
+
+bail:
+ //if (addrWidth >= 0)
+ // *pData = align32(data);
+ *pData = data;
+}
+
+/*
+ * Dump the contents of the register map area.
+ *
+ * These are only present in optimized DEX files, and the structure is
+ * not really exposed to other parts of the VM itself. We're going to
+ * dig through them here, but this is pretty fragile. DO NOT rely on
+ * this or derive other code from it.
+ */
+void dumpRegisterMaps(DexFile* pDexFile)
+{
+ const u1* pClassPool = pDexFile->pRegisterMapPool;
+ const u4* classOffsets;
+ const u1* ptr;
+ u4 numClasses;
+ int baseFileOffset = (u1*) pClassPool - (u1*) pDexFile->pOptHeader;
+ int idx;
+
+ if (pClassPool == NULL) {
+ printf("No register maps found\n");
+ return;
+ }
+
+ ptr = pClassPool;
+ numClasses = get4LE(ptr);
+ ptr += sizeof(u4);
+ classOffsets = (const u4*) ptr;
+
+ printf("RMAP begins at offset 0x%07x\n", baseFileOffset);
+ printf("Maps for %d classes\n", numClasses);
+ for (idx = 0; idx < (int) numClasses; idx++) {
+ const DexClassDef* pClassDef;
+ const char* classDescriptor;
+
+ pClassDef = dexGetClassDef(pDexFile, idx);
+ classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
+
+ printf("%4d: +%d (0x%08x) %s\n", idx, classOffsets[idx],
+ baseFileOffset + classOffsets[idx], classDescriptor);
+
+ if (classOffsets[idx] == 0)
+ continue;
+
+ /*
+ * What follows is a series of RegisterMap entries, one for every
+ * direct method, then one for every virtual method.
+ */
+ DexClassData* pClassData;
+ const u1* pEncodedData;
+ const u1* data = (u1*) pClassPool + classOffsets[idx];
+ u2 methodCount;
+ int i;
+
+ pEncodedData = dexGetClassData(pDexFile, pClassDef);
+ pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL);
+ if (pClassData == NULL) {
+ fprintf(stderr, "Trouble reading class data\n");
+ continue;
+ }
+
+ methodCount = *data++;
+ methodCount |= (*data++) << 8;
+ data += 2; /* two pad bytes follow methodCount */
+ if (methodCount != pClassData->header.directMethodsSize
+ + pClassData->header.virtualMethodsSize)
+ {
+ printf("NOTE: method count discrepancy (%d != %d + %d)\n",
+ methodCount, pClassData->header.directMethodsSize,
+ pClassData->header.virtualMethodsSize);
+ /* this is bad, but keep going anyway */
+ }
+
+ printf(" direct methods: %d\n",
+ pClassData->header.directMethodsSize);
+ for (i = 0; i < (int) pClassData->header.directMethodsSize; i++) {
+ dumpMethodMap(pDexFile, &pClassData->directMethods[i], i, &data);
+ }
+
+ printf(" virtual methods: %d\n",
+ pClassData->header.virtualMethodsSize);
+ for (i = 0; i < (int) pClassData->header.virtualMethodsSize; i++) {
+ dumpMethodMap(pDexFile, &pClassData->virtualMethods[i], i, &data);
+ }
+
+ free(pClassData);
+ }
+}
+
+/*
+ * Dump the requested sections of the file.
+ */
+void processDexFile(const char* fileName, DexFile* pDexFile)
+{
+ char* package = NULL;
+ int i;
+
+ if (gOptions.verbose) {
+ printf("Opened '%s', DEX version '%.3s'\n", fileName,
+ pDexFile->pHeader->magic +4);
+ }
+
+ if (gOptions.dumpRegisterMaps) {
+ dumpRegisterMaps(pDexFile);
+ return;
+ }
+
+ if (gOptions.showFileHeaders) {
+ dumpFileHeader(pDexFile);
+ dumpOptDirectory(pDexFile);
+ }
+
+ if (gOptions.outputFormat == OUTPUT_XML)
+ printf("<api>\n");
+
+ for (i = 0; i < (int) pDexFile->pHeader->classDefsSize; i++) {
+ if (gOptions.showSectionHeaders)
+ dumpClassDef(pDexFile, i);
+
+ dumpClass(pDexFile, i, &package);
+ }
+
+ /* free the last one allocated */
+ if (package != NULL) {
+ printf("</package>\n");
+ free(package);
+ }
+
+ if (gOptions.outputFormat == OUTPUT_XML)
+ printf("</api>\n");
+}
+
+
+/*
+ * Process one file.
+ */
+int process(const char* fileName)
+{
+ DexFile* pDexFile = NULL;
+ MemMapping map;
+ bool mapped = false;
+ int result = -1;
+
+ if (gOptions.verbose)
+ printf("Processing '%s'...\n", fileName);
+
+ if (dexOpenAndMap(fileName, gOptions.tempFileName, &map, false) != 0)
+ goto bail;
+ mapped = true;
+
+ int flags = kDexParseVerifyChecksum;
+ if (gOptions.ignoreBadChecksum)
+ flags |= kDexParseContinueOnError;
+
+ pDexFile = dexFileParse(map.addr, map.length, flags);
+ if (pDexFile == NULL) {
+ fprintf(stderr, "ERROR: DEX parse failed\n");
+ goto bail;
+ }
+
+ if (gOptions.checksumOnly) {
+ printf("Checksum verified\n");
+ } else {
+ processDexFile(fileName, pDexFile);
+ }
+
+ result = 0;
+
+bail:
+ if (mapped)
+ sysReleaseShmem(&map);
+ if (pDexFile != NULL)
+ dexFileFree(pDexFile);
+ return result;
+}
+
+
+/*
+ * Show usage.
+ */
+void usage(void)
+{
+ fprintf(stderr, "Copyright (C) 2007 The Android Open Source Project\n\n");
+ fprintf(stderr,
+ "%s: [-c] [-d] [-f] [-h] [-i] [-l layout] [-m] [-t tempfile] dexfile...\n",
+ gProgName);
+ fprintf(stderr, "\n");
+ fprintf(stderr, " -c : verify checksum and exit\n");
+ fprintf(stderr, " -d : disassemble code sections\n");
+ fprintf(stderr, " -f : display summary information from file header\n");
+ fprintf(stderr, " -h : display file header details\n");
+ fprintf(stderr, " -i : ignore checksum failures\n");
+ fprintf(stderr, " -l : output layout, either 'plain' or 'xml'\n");
+ fprintf(stderr, " -m : dump register maps (and nothing else)\n");
+ fprintf(stderr, " -t : temp file name (defaults to /sdcard/dex-temp-*)\n");
+}
+
+/*
+ * Parse args.
+ *
+ * I'm not using getopt_long() because we may not have it in libc.
+ */
+int main(int argc, char* const argv[])
+{
+ bool wantUsage = false;
+ int ic;
+
+ memset(&gOptions, 0, sizeof(gOptions));
+ gOptions.verbose = true;
+
+ while (1) {
+ ic = getopt(argc, argv, "cdfhil:mt:");
+ if (ic < 0)
+ break;
+
+ switch (ic) {
+ case 'c': // verify the checksum then exit
+ gOptions.checksumOnly = true;
+ break;
+ case 'd': // disassemble Dalvik instructions
+ gOptions.disassemble = true;
+ break;
+ case 'f': // dump outer file header
+ gOptions.showFileHeaders = true;
+ break;
+ case 'h': // dump section headers, i.e. all meta-data
+ gOptions.showSectionHeaders = true;
+ break;
+ case 'i': // continue even if checksum is bad
+ gOptions.ignoreBadChecksum = true;
+ break;
+ case 'l': // layout
+ if (strcmp(optarg, "plain") == 0) {
+ gOptions.outputFormat = OUTPUT_PLAIN;
+ } else if (strcmp(optarg, "xml") == 0) {
+ gOptions.outputFormat = OUTPUT_XML;
+ gOptions.verbose = false;
+ gOptions.exportsOnly = true;
+ } else {
+ wantUsage = true;
+ }
+ break;
+ case 'm': // dump register maps only
+ gOptions.dumpRegisterMaps = true;
+ break;
+ case 't': // temp file, used when opening compressed Jar
+ gOptions.tempFileName = optarg;
+ break;
+ default:
+ wantUsage = true;
+ break;
+ }
+ }
+
+ if (optind == argc) {
+ fprintf(stderr, "%s: no file specified\n", gProgName);
+ wantUsage = true;
+ }
+
+ if (gOptions.checksumOnly && gOptions.ignoreBadChecksum) {
+ fprintf(stderr, "Can't specify both -c and -i\n");
+ wantUsage = true;
+ }
+
+ /* initialize some VM tables */
+ gInstrWidth = dexCreateInstrWidthTable();
+ gInstrFormat = dexCreateInstrFormatTable();
+
+ if (wantUsage) {
+ usage();
+ return 2;
+ }
+
+ int result = 0;
+ while (optind < argc) {
+ result |= process(argv[optind++]);
+ }
+
+ free(gInstrWidth);
+ free(gInstrFormat);
+
+ return (result != 0);
+}
diff --git a/dexlist/Android.mk b/dexlist/Android.mk
new file mode 100644
index 0000000..aacdfb5
--- /dev/null
+++ b/dexlist/Android.mk
@@ -0,0 +1,54 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# dexlist -- list all concrete methods found in a DEX file
+#
+LOCAL_PATH:= $(call my-dir)
+
+dexdump_src_files := \
+ DexList.c
+
+dexdump_c_includes := \
+ dalvik \
+ $(JNI_H_INCLUDE)
+
+dexdump_shared_libraries :=
+
+dexdump_static_libraries := \
+ libdex
+
+ifneq ($(TARGET_SIMULATOR),true) # not 64 bit clean
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := dexlist
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := $(dexdump_src_files)
+LOCAL_C_INCLUDES := $(dexdump_c_includes)
+LOCAL_SHARED_LIBRARIES := $(dexdump_shared_libraries) libcutils libz
+LOCAL_STATIC_LIBRARIES := $(dexdump_static_libraries)
+LOCAL_LDLIBS +=
+#include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := dexlist
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := $(dexdump_src_files)
+LOCAL_C_INCLUDES := $(dexdump_c_includes)
+LOCAL_SHARED_LIBRARIES := $(dexdump_shared_libraries)
+LOCAL_STATIC_LIBRARIES := $(dexdump_static_libraries) libcutils
+LOCAL_LDLIBS += -lpthread -lz
+include $(BUILD_HOST_EXECUTABLE)
+
+endif # TARGET_SIMULATOR
diff --git a/dexlist/DexList.c b/dexlist/DexList.c
new file mode 100644
index 0000000..3552d2e
--- /dev/null
+++ b/dexlist/DexList.c
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * List all methods in all concrete classes in one or more DEX files.
+ */
+#include "libdex/DexFile.h"
+#include "libdex/DexClass.h"
+#include "libdex/DexProto.h"
+#include "libdex/SysUtil.h"
+#include "libdex/CmdUtils.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <assert.h>
+
+static const char* gProgName = "dexlist";
+
+/* command-line args */
+static struct {
+ char* argCopy;
+ const char* classToFind;
+ const char* methodToFind;
+} gParms;
+
+
+/*
+ * Return a newly-allocated string for the "dot version" of the class
+ * name for the given type descriptor. That is, The initial "L" and
+ * final ";" (if any) have been removed and all occurrences of '/'
+ * have been changed to '.'.
+ */
+static char* descriptorToDot(const char* str)
+{
+ size_t at = strlen(str);
+ char* newStr;
+
+ if (str[0] == 'L') {
+ assert(str[at - 1] == ';');
+ at -= 2; /* Two fewer chars to copy. */
+ str++; /* Skip the 'L'. */
+ }
+
+ newStr = malloc(at + 1); /* Add one for the '\0'. */
+ newStr[at] = '\0';
+
+ while (at > 0) {
+ at--;
+ newStr[at] = (str[at] == '/') ? '.' : str[at];
+ }
+
+ return newStr;
+}
+
+/*
+ * Position table callback; we just want to catch the number of the
+ * first line in the method, which *should* correspond to the first
+ * entry from the table. (Could also use "min" here.)
+ */
+static int positionsCallback(void* cnxt, u4 address, u4 lineNum)
+{
+ int* pFirstLine = (int*) cnxt;
+ if (*pFirstLine == -1)
+ *pFirstLine = lineNum;
+ return 0;
+}
+
+
+/*
+ * Dump a method.
+ */
+void dumpMethod(DexFile* pDexFile, const char* fileName,
+ const DexMethod* pDexMethod, int i)
+{
+ const DexMethodId* pMethodId;
+ const DexCode* pCode;
+ const char* classDescriptor;
+ const char* methodName;
+ int firstLine;
+
+ /* abstract and native methods don't get listed */
+ if (pDexMethod->codeOff == 0)
+ return;
+
+ pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
+ methodName = dexStringById(pDexFile, pMethodId->nameIdx);
+
+ classDescriptor = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
+
+ pCode = dexGetCode(pDexFile, pDexMethod);
+ assert(pCode != NULL);
+
+ /*
+ * If the filename is empty, then set it to something printable
+ * so that it is easier to parse.
+ *
+ * TODO: A method may override its class's default source file by
+ * specifying a different one in its debug info. This possibility
+ * should be handled here.
+ */
+ if (fileName == NULL || fileName[0] == 0) {
+ fileName = "(none)";
+ }
+
+ firstLine = -1;
+ dexDecodeDebugInfo(pDexFile, pCode, classDescriptor, pMethodId->protoIdx,
+ pDexMethod->accessFlags, positionsCallback, NULL, &firstLine);
+
+ char* className = descriptorToDot(classDescriptor);
+ char* desc = dexCopyDescriptorFromMethodId(pDexFile, pMethodId);
+ u4 insnsOff = pDexMethod->codeOff + offsetof(DexCode, insns);
+
+ if (gParms.methodToFind != NULL &&
+ (strcmp(gParms.classToFind, className) != 0 ||
+ strcmp(gParms.methodToFind, methodName) != 0))
+ {
+ goto skip;
+ }
+
+ printf("0x%08x %d %s %s %s %s %d\n",
+ insnsOff, pCode->insnsSize * 2,
+ className, methodName, desc,
+ fileName, firstLine);
+
+skip:
+ free(desc);
+ free(className);
+}
+
+/*
+ * Run through all direct and virtual methods in the class.
+ */
+void dumpClass(DexFile* pDexFile, int idx)
+{
+ const DexClassDef* pClassDef;
+ DexClassData* pClassData;
+ const u1* pEncodedData;
+ const char* fileName;
+ int i;
+
+ pClassDef = dexGetClassDef(pDexFile, idx);
+ pEncodedData = dexGetClassData(pDexFile, pClassDef);
+ pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL);
+
+ if (pClassData == NULL) {
+ fprintf(stderr, "Trouble reading class data\n");
+ return;
+ }
+
+ if (pClassDef->sourceFileIdx == 0xffffffff) {
+ fileName = NULL;
+ } else {
+ fileName = dexStringById(pDexFile, pClassDef->sourceFileIdx);
+ }
+
+ /*
+ * TODO: Each class def points at a sourceFile, so maybe that
+ * should be printed out. However, this needs to be coordinated
+ * with the tools that parse this output.
+ */
+
+ for (i = 0; i < (int) pClassData->header.directMethodsSize; i++) {
+ dumpMethod(pDexFile, fileName, &pClassData->directMethods[i], i);
+ }
+
+ for (i = 0; i < (int) pClassData->header.virtualMethodsSize; i++) {
+ dumpMethod(pDexFile, fileName, &pClassData->virtualMethods[i], i);
+ }
+
+ free(pClassData);
+}
+
+/*
+ * Process a file.
+ *
+ * Returns 0 on success.
+ */
+int process(const char* fileName)
+{
+ DexFile* pDexFile = NULL;
+ MemMapping map;
+ bool mapped = false;
+ int result = -1;
+ UnzipToFileResult utfr;
+
+ utfr = dexOpenAndMap(fileName, NULL, &map, true);
+ if (utfr != kUTFRSuccess) {
+ if (utfr == kUTFRNoClassesDex) {
+ /* no classes.dex in the APK; pretend we succeeded */
+ result = 0;
+ goto bail;
+ }
+ fprintf(stderr, "Unable to process '%s'\n", fileName);
+ goto bail;
+ }
+ mapped = true;
+
+ pDexFile = dexFileParse(map.addr, map.length, kDexParseDefault);
+ if (pDexFile == NULL) {
+ fprintf(stderr, "Warning: DEX parse failed for '%s'\n", fileName);
+ goto bail;
+ }
+
+ printf("#%s\n", fileName);
+
+ int i;
+ for (i = 0; i < (int) pDexFile->pHeader->classDefsSize; i++) {
+ dumpClass(pDexFile, i);
+ }
+
+ result = 0;
+
+bail:
+ if (mapped)
+ sysReleaseShmem(&map);
+ if (pDexFile != NULL)
+ dexFileFree(pDexFile);
+ return result;
+}
+
+
+/*
+ * Show usage.
+ */
+void usage(void)
+{
+ fprintf(stderr, "Copyright (C) 2007 The Android Open Source Project\n\n");
+ fprintf(stderr, "%s: dexfile [dexfile2 ...]\n", gProgName);
+ fprintf(stderr, "\n");
+}
+
+/*
+ * Parse args.
+ */
+int main(int argc, char* const argv[])
+{
+ int result = 0;
+ int i;
+
+ /*
+ * Find all instances of the fully-qualified method name. This isn't
+ * really what dexlist is for, but it's easy to do it here.
+ */
+ if (argc > 3 && strcmp(argv[1], "--method") == 0) {
+ gParms.argCopy = strdup(argv[2]);
+ char* meth = strrchr(gParms.argCopy, '.');
+ if (meth == NULL) {
+ fprintf(stderr, "Expected package.Class.method\n");
+ free(gParms.argCopy);
+ return 2;
+ }
+ *meth = '\0';
+ gParms.classToFind = gParms.argCopy;
+ gParms.methodToFind = meth+1;
+ argv += 2;
+ argc -= 2;
+ }
+
+ if (argc < 2) {
+ fprintf(stderr, "%s: no file specified\n", gProgName);
+ usage();
+ return 2;
+ }
+
+ /*
+ * Run through the list of files. If one of them fails we contine on,
+ * only returning a failure at the end.
+ */
+ for (i = 1; i < argc; i++)
+ result |= process(argv[i]);
+
+ free(gParms.argCopy);
+ return result;
+}
diff --git a/dexopt/Android.mk b/dexopt/Android.mk
new file mode 100644
index 0000000..3bb98a5
--- /dev/null
+++ b/dexopt/Android.mk
@@ -0,0 +1,61 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# dexopt, the DEX file optimizer. This is fully integrated with the VM,
+# so it must be linked against the full VM shared library.
+#
+LOCAL_PATH:= $(call my-dir)
+
+local_src_files := \
+ OptMain.c
+
+local_c_includes := \
+ dalvik \
+ dalvik/libdex \
+ dalvik/vm \
+ $(JNI_H_INCLUDE)
+
+local_shared_libraries := \
+ libssl \
+ libdvm \
+ libcrypto \
+ libicuuc \
+ libicui18n
+
+include $(CLEAR_VARS)
+ifeq ($(TARGET_CPU_SMP),true)
+ LOCAL_CFLAGS += -DANDROID_SMP=1
+else
+ LOCAL_CFLAGS += -DANDROID_SMP=0
+endif
+LOCAL_SRC_FILES := $(local_src_files)
+LOCAL_C_INCLUDES := $(local_c_includes)
+LOCAL_SHARED_LIBRARIES := $(local_shared_libraries) libcutils libexpat liblog libnativehelper libutils libz
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := dexopt
+include $(BUILD_EXECUTABLE)
+
+ifeq ($(WITH_HOST_DALVIK),true)
+ include $(CLEAR_VARS)
+ LOCAL_SRC_FILES := $(local_src_files)
+ LOCAL_C_INCLUDES := $(local_c_includes)
+ LOCAL_SHARED_LIBRARIES := $(local_shared_libraries)
+ LOCAL_STATIC_LIBRARIES := libcutils libexpat liblog libnativehelper libutils libz
+ LOCAL_LDLIBS += -ldl -lpthread
+ LOCAL_CFLAGS += -DANDROID_SMP=1
+ LOCAL_MODULE_TAGS := optional
+ LOCAL_MODULE := dexopt
+ include $(BUILD_HOST_EXECUTABLE)
+endif
diff --git a/dexopt/OptMain.c b/dexopt/OptMain.c
new file mode 100644
index 0000000..b8e5889
--- /dev/null
+++ b/dexopt/OptMain.c
@@ -0,0 +1,593 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Command-line DEX optimization and verification entry point.
+ *
+ * There are three ways to launch this:
+ * (1) From the VM. This takes a dozen args, one of which is a file
+ * descriptor that acts as both input and output. This allows us to
+ * remain ignorant of where the DEX data originally came from.
+ * (2) From installd or another native application. Pass in a file
+ * descriptor for a zip file, a file descriptor for the output, and
+ * a filename for debug messages. Many assumptions are made about
+ * what's going on (verification + optimization are enabled, boot
+ * class path is in BOOTCLASSPATH, etc).
+ * (3) On the host during a build for preoptimization. This behaves
+ * almost the same as (2), except it takes file names instead of
+ * file descriptors.
+ *
+ * There are some fragile aspects around bootclasspath entries, owing
+ * largely to the VM's history of working on whenever it thought it needed
+ * instead of strictly doing what it was told. If optimizing bootclasspath
+ * entries, always do them in the order in which they appear in the path.
+ */
+#include "Dalvik.h"
+#include "libdex/OptInvocation.h"
+
+#include "utils/Log.h"
+#include "cutils/process_name.h"
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+static const char* kClassesDex = "classes.dex";
+
+
+/*
+ * Extract "classes.dex" from zipFd into "cacheFd", leaving a little space
+ * up front for the DEX optimization header.
+ */
+static int extractAndProcessZip(int zipFd, int cacheFd,
+ const char* debugFileName, bool isBootstrap, const char* bootClassPath,
+ const char* dexoptFlagStr)
+{
+ ZipArchive zippy;
+ ZipEntry zipEntry;
+ size_t uncompLen;
+ long modWhen, crc32;
+ off_t dexOffset;
+ int err;
+ int result = -1;
+
+ memset(&zippy, 0, sizeof(zippy));
+
+ /* make sure we're still at the start of an empty file */
+ if (lseek(cacheFd, 0, SEEK_END) != 0) {
+ LOGE("DexOptZ: new cache file '%s' is not empty\n", debugFileName);
+ goto bail;
+ }
+
+ /*
+ * Write a skeletal DEX optimization header. We want the classes.dex
+ * to come just after it.
+ */
+ err = dexOptCreateEmptyHeader(cacheFd);
+ if (err != 0)
+ goto bail;
+
+ /* record the file position so we can get back here later */
+ dexOffset = lseek(cacheFd, 0, SEEK_CUR);
+ if (dexOffset < 0)
+ goto bail;
+
+ /*
+ * Open the zip archive, find the DEX entry.
+ */
+ if (dexZipPrepArchive(zipFd, debugFileName, &zippy) != 0) {
+ LOGW("DexOptZ: unable to open zip archive '%s'\n", debugFileName);
+ goto bail;
+ }
+
+ zipEntry = dexZipFindEntry(&zippy, kClassesDex);
+ if (zipEntry == NULL) {
+ LOGW("DexOptZ: zip archive '%s' does not include %s\n",
+ debugFileName, kClassesDex);
+ goto bail;
+ }
+
+ /*
+ * Extract some info about the zip entry.
+ */
+ if (dexZipGetEntryInfo(&zippy, zipEntry, NULL, &uncompLen, NULL, NULL,
+ &modWhen, &crc32) != 0)
+ {
+ LOGW("DexOptZ: zip archive GetEntryInfo failed on %s\n", debugFileName);
+ goto bail;
+ }
+
+ uncompLen = uncompLen;
+ modWhen = modWhen;
+ crc32 = crc32;
+
+ /*
+ * Extract the DEX data into the cache file at the current offset.
+ */
+ if (dexZipExtractEntryToFile(&zippy, zipEntry, cacheFd) != 0) {
+ LOGW("DexOptZ: extraction of %s from %s failed\n",
+ kClassesDex, debugFileName);
+ goto bail;
+ }
+
+ /* Parse the options. */
+
+ DexClassVerifyMode verifyMode = VERIFY_MODE_ALL;
+ DexOptimizerMode dexOptMode = OPTIMIZE_MODE_VERIFIED;
+ int dexoptFlags = 0; /* bit flags, from enum DexoptFlags */
+
+ if (dexoptFlagStr[0] != '\0') {
+ const char* opc;
+ const char* val;
+
+ opc = strstr(dexoptFlagStr, "v="); /* verification */
+ if (opc != NULL) {
+ switch (*(opc+2)) {
+ case 'n': verifyMode = VERIFY_MODE_NONE; break;
+ case 'r': verifyMode = VERIFY_MODE_REMOTE; break;
+ case 'a': verifyMode = VERIFY_MODE_ALL; break;
+ default: break;
+ }
+ }
+
+ opc = strstr(dexoptFlagStr, "o="); /* optimization */
+ if (opc != NULL) {
+ switch (*(opc+2)) {
+ case 'n': dexOptMode = OPTIMIZE_MODE_NONE; break;
+ case 'v': dexOptMode = OPTIMIZE_MODE_VERIFIED; break;
+ case 'a': dexOptMode = OPTIMIZE_MODE_ALL; break;
+ default: break;
+ }
+ }
+
+ opc = strstr(dexoptFlagStr, "m=y"); /* register map */
+ if (opc != NULL) {
+ dexoptFlags |= DEXOPT_GEN_REGISTER_MAPS;
+ }
+
+ opc = strstr(dexoptFlagStr, "u="); /* uniprocessor target */
+ if (opc != NULL) {
+ switch (*(opc+2)) {
+ case 'y': dexoptFlags |= DEXOPT_UNIPROCESSOR; break;
+ case 'n': dexoptFlags |= DEXOPT_SMP; break;
+ default: break;
+ }
+ }
+ }
+
+ /*
+ * Prep the VM and perform the optimization.
+ */
+
+ if (dvmPrepForDexOpt(bootClassPath, dexOptMode, verifyMode,
+ dexoptFlags) != 0)
+ {
+ LOGE("DexOptZ: VM init failed\n");
+ goto bail;
+ }
+
+ //vmStarted = 1;
+
+ /* do the optimization */
+ if (!dvmContinueOptimization(cacheFd, dexOffset, uncompLen, debugFileName,
+ modWhen, crc32, isBootstrap))
+ {
+ LOGE("Optimization failed\n");
+ goto bail;
+ }
+
+ /* we don't shut the VM down -- process is about to exit */
+
+ result = 0;
+
+bail:
+ dexZipCloseArchive(&zippy);
+ return result;
+}
+
+/*
+ * Common functionality for normal device-side processing as well as
+ * preoptimization.
+ */
+static int processZipFile(int zipFd, int cacheFd, const char* zipName,
+ const char *dexoptFlags)
+{
+ int result = -1;
+ char* bcpCopy = NULL;
+
+ /*
+ * Check to see if this is a bootstrap class entry. If so, truncate
+ * the path.
+ */
+ const char* bcp = getenv("BOOTCLASSPATH");
+ if (bcp == NULL) {
+ LOGE("DexOptZ: BOOTCLASSPATH not set\n");
+ goto bail;
+ }
+
+ bool isBootstrap = false;
+ const char* match = strstr(bcp, zipName);
+ if (match != NULL) {
+ /*
+ * TODO: we have a partial string match, but that doesn't mean
+ * we've matched an entire path component. We should make sure
+ * that we're matching on the full zipName, and if not we
+ * should re-do the strstr starting at (match+1).
+ *
+ * The scenario would be a bootclasspath with something like
+ * "/system/framework/core.jar" while we're trying to optimize
+ * "/framework/core.jar". Not very likely since all paths are
+ * absolute and end with ".jar", but not impossible.
+ */
+ int matchOffset = match - bcp;
+ if (matchOffset > 0 && bcp[matchOffset-1] == ':')
+ matchOffset--;
+ LOGV("DexOptZ: found '%s' in bootclasspath, cutting off at %d\n",
+ inputFileName, matchOffset);
+ bcpCopy = strdup(bcp);
+ bcpCopy[matchOffset] = '\0';
+
+ bcp = bcpCopy;
+ LOGD("DexOptZ: truncated BOOTCLASSPATH to '%s'\n", bcp);
+ isBootstrap = true;
+ }
+
+ result = extractAndProcessZip(zipFd, cacheFd, zipName, isBootstrap,
+ bcp, dexoptFlags);
+
+bail:
+ free(bcpCopy);
+ return result;
+}
+
+/* advance to the next arg and extract it */
+#define GET_ARG(_var, _func, _msg) \
+ { \
+ char* endp; \
+ (_var) = _func(*++argv, &endp, 0); \
+ if (*endp != '\0') { \
+ LOGE("%s '%s'", _msg, *argv); \
+ goto bail; \
+ } \
+ --argc; \
+ }
+
+/*
+ * Parse arguments. We want:
+ * 0. (name of dexopt command -- ignored)
+ * 1. "--zip"
+ * 2. zip fd (input, read-only)
+ * 3. cache fd (output, read-write, locked with flock)
+ * 4. filename of zipfile being optimized (used for debug messages and
+ * for comparing against BOOTCLASSPATH; does not need to be
+ * accessible or even exist)
+ * 5. dexopt flags
+ *
+ * The BOOTCLASSPATH environment variable is assumed to hold the correct
+ * boot class path. If the filename provided appears in the boot class
+ * path, the path will be truncated just before that entry (so that, if
+ * you were to dexopt "core.jar", your bootclasspath would be empty).
+ *
+ * This does not try to normalize the boot class path name, so the
+ * filename test won't catch you if you get creative.
+ */
+static int fromZip(int argc, char* const argv[])
+{
+ int result = -1;
+ int zipFd, cacheFd;
+ const char* zipName;
+ char* bcpCopy = NULL;
+ const char* dexoptFlags;
+
+ if (argc != 6) {
+ LOGE("Wrong number of args for --zip (found %d)\n", argc);
+ goto bail;
+ }
+
+ /* skip "--zip" */
+ argc--;
+ argv++;
+
+ GET_ARG(zipFd, strtol, "bad zip fd");
+ GET_ARG(cacheFd, strtol, "bad cache fd");
+ zipName = *++argv;
+ --argc;
+ dexoptFlags = *++argv;
+ --argc;
+
+ result = processZipFile(zipFd, cacheFd, zipName, dexoptFlags);
+
+bail:
+ return result;
+}
+
+/*
+ * Parse arguments for a preoptimization run. This is when dalvikvm is run
+ * on a host to optimize dex files for eventual running on a (different)
+ * device. We want:
+ * 0. (name of dexopt command -- ignored)
+ * 1. "--preopt"
+ * 2. zipfile name
+ * 3. output file name
+ * 4. dexopt flags
+ *
+ * The BOOTCLASSPATH environment variable is assumed to hold the correct
+ * boot class path. If the filename provided appears in the boot class
+ * path, the path will be truncated just before that entry (so that, if
+ * you were to dexopt "core.jar", your bootclasspath would be empty).
+ *
+ * This does not try to normalize the boot class path name, so the
+ * filename test won't catch you if you get creative.
+ */
+static int preopt(int argc, char* const argv[])
+{
+ int zipFd = -1;
+ int outFd = -1;
+ int result = -1;
+
+ if (argc != 5) {
+ /*
+ * Use stderr here, since this variant is meant to be called on
+ * the host side.
+ */
+ fprintf(stderr, "Wrong number of args for --preopt (found %d)\n",
+ argc);
+ goto bail;
+ }
+
+ const char* zipName = argv[2];
+ const char* outName = argv[3];
+ const char* dexoptFlags = argv[4];
+
+ if (strstr(dexoptFlags, "u=y") == NULL &&
+ strstr(dexoptFlags, "u=n") == NULL)
+ {
+ fprintf(stderr, "Either 'u=y' or 'u=n' must be specified\n");
+ goto bail;
+ }
+
+ zipFd = open(zipName, O_RDONLY);
+ if (zipFd < 0) {
+ perror(argv[0]);
+ goto bail;
+ }
+
+ outFd = open(outName, O_RDWR | O_EXCL | O_CREAT, 0666);
+ if (outFd < 0) {
+ perror(argv[0]);
+ goto bail;
+ }
+
+ result = processZipFile(zipFd, outFd, zipName, dexoptFlags);
+
+bail:
+ if (zipFd >= 0) {
+ close(zipFd);
+ }
+
+ if (outFd >= 0) {
+ close(outFd);
+ }
+
+ return result;
+}
+
+/*
+ * Parse arguments for an "old-style" invocation directly from the VM.
+ *
+ * Here's what we want:
+ * 0. (name of dexopt command -- ignored)
+ * 1. "--dex"
+ * 2. DALVIK_VM_BUILD value, as a sanity check
+ * 3. file descriptor, locked with flock, for DEX file being optimized
+ * 4. DEX offset within file
+ * 5. DEX length
+ * 6. filename of file being optimized (for debug messages only)
+ * 7. modification date of source (goes into dependency section)
+ * 8. CRC of source (goes into dependency section)
+ * 9. flags (optimization level, isBootstrap)
+ * 10. bootclasspath entry #1
+ * 11. bootclasspath entry #2
+ * ...
+ *
+ * dvmOptimizeDexFile() in dalvik/vm/analysis/DexOptimize.c builds the
+ * argument list and calls this executable.
+ *
+ * The bootclasspath entries become the dependencies for this DEX file.
+ *
+ * The open file descriptor MUST NOT be for one of the bootclasspath files.
+ * The parent has the descriptor locked, and we'll try to lock it again as
+ * part of processing the bootclasspath. (We can catch this and return
+ * an error by comparing filenames or by opening the bootclasspath files
+ * and stat()ing them for inode numbers).
+ */
+static int fromDex(int argc, char* const argv[])
+{
+ int result = -1;
+ bool vmStarted = false;
+ char* bootClassPath = NULL;
+ int fd, flags, vmBuildVersion;
+ long offset, length;
+ const char* debugFileName;
+ u4 crc, modWhen;
+ char* endp;
+
+ if (argc < 10) {
+ /* don't have all mandatory args */
+ LOGE("Not enough arguments for --dex (found %d)\n", argc);
+ goto bail;
+ }
+
+ /* skip "--dex" */
+ argc--;
+ argv++;
+
+ /*
+ * Extract the args.
+ */
+ GET_ARG(vmBuildVersion, strtol, "bad vm build");
+ if (vmBuildVersion != DALVIK_VM_BUILD) {
+ LOGE("DexOpt: build rev does not match VM: %d vs %d\n",
+ vmBuildVersion, DALVIK_VM_BUILD);
+ goto bail;
+ }
+ GET_ARG(fd, strtol, "bad fd");
+ GET_ARG(offset, strtol, "bad offset");
+ GET_ARG(length, strtol, "bad length");
+ debugFileName = *++argv;
+ --argc;
+ GET_ARG(modWhen, strtoul, "bad modWhen");
+ GET_ARG(crc, strtoul, "bad crc");
+ GET_ARG(flags, strtol, "bad flags");
+
+ LOGV("Args: fd=%d off=%ld len=%ld name='%s' mod=0x%x crc=0x%x flg=%d (argc=%d)\n",
+ fd, offset, length, debugFileName, modWhen, crc, flags, argc);
+ assert(argc > 0);
+
+ if (--argc == 0) {
+ bootClassPath = strdup("");
+ } else {
+ int i, bcpLen;
+ char* const* argp;
+ char* cp;
+
+ bcpLen = 0;
+ for (i = 0, argp = argv; i < argc; i++) {
+ ++argp;
+ LOGV("DEP: '%s'\n", *argp);
+ bcpLen += strlen(*argp) + 1;
+ }
+
+ cp = bootClassPath = (char*) malloc(bcpLen +1);
+ for (i = 0, argp = argv; i < argc; i++) {
+ int strLen;
+
+ ++argp;
+ strLen = strlen(*argp);
+ if (i != 0)
+ *cp++ = ':';
+ memcpy(cp, *argp, strLen);
+ cp += strLen;
+ }
+ *cp = '\0';
+
+ assert((int) strlen(bootClassPath) == bcpLen-1);
+ }
+ LOGV(" bootclasspath is '%s'\n", bootClassPath);
+
+ /* start the VM partway */
+ bool onlyOptVerifiedDex = false;
+ DexClassVerifyMode verifyMode;
+ DexOptimizerMode dexOptMode;
+
+ /* ugh -- upgrade these to a bit field if they get any more complex */
+ if ((flags & DEXOPT_VERIFY_ENABLED) != 0) {
+ if ((flags & DEXOPT_VERIFY_ALL) != 0)
+ verifyMode = VERIFY_MODE_ALL;
+ else
+ verifyMode = VERIFY_MODE_REMOTE;
+ } else {
+ verifyMode = VERIFY_MODE_NONE;
+ }
+ if ((flags & DEXOPT_OPT_ENABLED) != 0) {
+ if ((flags & DEXOPT_OPT_ALL) != 0)
+ dexOptMode = OPTIMIZE_MODE_ALL;
+ else
+ dexOptMode = OPTIMIZE_MODE_VERIFIED;
+ } else {
+ dexOptMode = OPTIMIZE_MODE_NONE;
+ }
+
+ if (dvmPrepForDexOpt(bootClassPath, dexOptMode, verifyMode, flags) != 0) {
+ LOGE("VM init failed\n");
+ goto bail;
+ }
+
+ vmStarted = true;
+
+ /* do the optimization */
+ if (!dvmContinueOptimization(fd, offset, length, debugFileName,
+ modWhen, crc, (flags & DEXOPT_IS_BOOTSTRAP) != 0))
+ {
+ LOGE("Optimization failed\n");
+ goto bail;
+ }
+
+ result = 0;
+
+bail:
+ /*
+ * In theory we should gracefully shut the VM down at this point. In
+ * practice that only matters if we're checking for memory leaks with
+ * valgrind -- simply exiting is much faster.
+ *
+ * As it turns out, the DEX optimizer plays a little fast and loose
+ * with class loading. We load all of the classes from a partially-
+ * formed DEX file, which is unmapped when we're done. If we want to
+ * do clean shutdown here, perhaps for testing with valgrind, we need
+ * to skip the munmap call there.
+ */
+#if 0
+ if (vmStarted) {
+ LOGI("DexOpt shutting down, result=%d\n", result);
+ dvmShutdown();
+ }
+#endif
+
+ //dvmLinearAllocDump(NULL);
+
+#if 0
+ {
+ extern int gDvm__totalInstr, gDvm__gcInstr, gDvm__gcData,
+ gDvm__gcSimpleData;
+ LOGI("GC DATA: totinst=%d, gcinst=%d, gcdata=%d simpled=%d\n",
+ gDvm__totalInstr, gDvm__gcInstr, gDvm__gcData, gDvm__gcSimpleData);
+ }
+#endif
+
+ free(bootClassPath);
+ LOGV("DexOpt command complete (result=%d)\n", result);
+ return result;
+}
+
+/*
+ * Main entry point. Decide where to go.
+ */
+int main(int argc, char* const argv[])
+{
+ set_process_name("dexopt");
+
+ setvbuf(stdout, NULL, _IONBF, 0);
+
+ if (argc > 1) {
+ if (strcmp(argv[1], "--zip") == 0)
+ return fromZip(argc, argv);
+ else if (strcmp(argv[1], "--dex") == 0)
+ return fromDex(argc, argv);
+ else if (strcmp(argv[1], "--preopt") == 0)
+ return preopt(argc, argv);
+ }
+
+ fprintf(stderr,
+ "Usage:\n\n"
+ "Short version: Don't use this.\n\n"
+ "Slightly longer version: This system-internal tool is used to\n"
+ "produce optimized dex files. See the source code for details.\n");
+
+ return 1;
+}
diff --git a/docs/dalvik-bytecode.css b/docs/dalvik-bytecode.css
new file mode 100644
index 0000000..e4a5caa
--- /dev/null
+++ b/docs/dalvik-bytecode.css
@@ -0,0 +1,165 @@
+h1 {
+ font-family: serif;
+ color: #222266;
+}
+
+h2 {
+ font-family: serif;
+ border-top-style: solid;
+ border-top-width: 2px;
+ border-color: #ccccdd;
+ padding-top: 12px;
+ margin-top: 48px;
+ margin-bottom: 2px;
+ color: #222266;
+}
+
+@media print {
+ table {
+ font-size: 8pt;
+ }
+}
+
+@media screen {
+ table {
+ font-size: 10pt;
+ }
+}
+
+
+/* general for all tables */
+
+table {
+ border-collapse: collapse;
+ margin-top: 12px;
+}
+
+table th {
+ font-family: sans-serif;
+ background: #aabbff;
+}
+
+table td {
+ font-family: sans-serif;
+ border-top-style: solid;
+ border-bottom-style: solid;
+ border-width: 1px;
+ border-color: #aaaaff;
+ padding-top: 4px;
+ padding-bottom: 4px;
+ padding-left: 4px;
+ padding-right: 6px;
+ background: #eeeeff;
+}
+
+table td p {
+ margin-top: 4pt;
+ margin-bottom: 0pt;
+}
+
+
+
+/* opcodes table */
+
+table.instruc {
+ margin-top: 24px;
+ margin-bottom: 24px;
+ margin-left: 48px;
+ margin-right: 48px;
+}
+
+table.instruc td {
+ font-family: sans-serif;
+ border-top-style: solid;
+ border-bottom-style: solid;
+ border-width: 1px;
+ padding-top: 4px;
+ padding-bottom: 4px;
+ padding-left: 2px;
+ padding-right: 2px;
+}
+
+table.instruc td:first-child {
+ font-family: monospace;
+ font-size: 90%;
+ vertical-align: top;
+ width: 12%;
+}
+
+table.instruc td:first-child + td {
+ font-family: monospace;
+ font-size: 90%;
+ vertical-align: top;
+ width: 23%;
+}
+
+table.instruc td:first-child + td i {
+ font-family: sans-serif;
+ font-size: 90%;
+}
+
+table.instruc td:first-child + td + td {
+ vertical-align: top;
+ width: 28%;
+}
+
+table.instruc td:first-child + td + td + td {
+ vertical-align: top;
+ width: 37%;
+}
+
+
+/* supplemental opcode format table */
+
+table.supplement {
+ margin-top: 24px;
+ margin-bottom: 24px;
+ margin-left: 48px;
+ margin-right: 48px;
+}
+
+table.supplement td:first-child {
+ font-family: monospace;
+ vertical-align: top;
+ width: 20%;
+}
+
+table.supplement td:first-child + td {
+ font-family: monospace;
+ vertical-align: top;
+ width: 20%;
+}
+
+table.supplement td:first-child + td + td {
+ font-family: sans-serif;
+ vertical-align: top;
+ width: 60%;
+}
+
+
+/* math details table */
+
+table.math {
+ margin-top: 24px;
+ margin-bottom: 24px;
+ margin-left: 48px;
+ margin-right: 48px;
+}
+
+table.math td:first-child {
+ font-family: monospace;
+ vertical-align: top;
+ width: 10%;
+}
+
+table.math td:first-child + td {
+ font-family: monospace;
+ vertical-align: top;
+ width: 30%;
+}
+
+table.math td:first-child + td + td {
+ font-family: sans-serif;
+ vertical-align: top;
+ width: 60%;
+}
diff --git a/docs/dalvik-bytecode.html b/docs/dalvik-bytecode.html
new file mode 100644
index 0000000..35fa64b
--- /dev/null
+++ b/docs/dalvik-bytecode.html
@@ -0,0 +1,1503 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>Bytecode for the Dalvik VM</title>
+<link rel=stylesheet href="dalvik-bytecode.css">
+</head>
+
+<body>
+
+<h1>Bytecode for the Dalvik VM</h1>
+<p>Copyright &copy; 2007 The Android Open Source Project
+
+<h2>General Design</h2>
+
+<ul>
+<li>The machine model and calling conventions are meant to approximately
+ imitate common real architectures and C-style calling conventions:
+ <ul>
+ <li>The VM is register-based, and frames are fixed in size upon creation.
+ Each frame consists of a particular number of registers (specified by
+ the method) as well as any adjunct data needed to execute the method,
+ such as (but not limited to) the program counter and a reference to the
+ <code>.dex</code> file that contains the method.
+ </li>
+ <li>Registers are 32 bits wide. Adjacent register pairs are used for 64-bit
+ values.
+ </li>
+ <li>In terms of bitwise representation, <code>(Object) null == (int)
+ 0</code>.
+ </li>
+ <li>The <i>N</i> arguments to a method land in the last <i>N</i> registers
+ of the method's invocation frame, in order. Wide arguments consume
+ two registers. Instance methods are passed a <code>this</code> reference
+ as their first argument.
+ </li>
+ </ul>
+<li>The storage unit in the instruction stream is a 16-bit unsigned quantity.
+ Some bits in some instructions are ignored / must-be-zero.
+</li>
+<li>Instructions aren't gratuitously limited to a particular type. For
+ example, instructions that move 32-bit register values without interpretation
+ don't have to specify whether they are moving ints or floats.
+</li>
+<li>There are separately enumerated and indexed constant pools for
+ references to strings, types, fields, and methods.
+</li>
+<li>Bitwise literal data is represented in-line in the instruction stream.</li>
+<li>Because, in practice, it is uncommon for a method to need more than
+ 16 registers, and because needing more than eight registers <i>is</i>
+ reasonably common, many instructions are limited to only addressing
+ the first 16
+ registers. When reasonably possible, instructions allow references to
+ up to the first 256 registers. In cases where an instruction variant isn't
+ available to address a desired register, it is expected that the register
+ contents get moved from the original register to a low register (before the
+ operation) and/or moved from a low result register to a high register
+ (after the operation).
+</li>
+<li>There are several "pseudo-instructions" that are used to hold
+ variable-length data referred to by regular instructions (for example,
+ <code>fill-array-data</code>). Such instructions must never be
+ encountered during the normal flow of execution. In addition, the
+ instructions must be located on even-numbered bytecode offsets (that is,
+ 4-byte aligned). In order to meet this requirement, dex generation tools
+ should emit an extra <code>nop</code> instruction as a spacer if such an
+ instruction would otherwise be unaligned. Finally, though not required,
+ it is expected that most tools will choose to emit these instructions at
+ the ends of methods, since otherwise it would likely be the case that
+ additional instructions would be needed to branch around them.
+</li>
+<li>When installed on a running system, some instructions may be altered,
+ changing their format, as an install-time static linking optimization.
+ This is to allow for faster execution once linkage is known.
+ See the associated
+ <a href="instruction-formats.html">instruction formats document</a>
+ for the suggested variants. The word "suggested" is used advisedly;
+ it is not mandatory to implement these.
+</li>
+<li>Human-syntax and mnemonics:
+ <ul>
+ <li>Dest-then-source ordering for arguments.</li>
+ <li>Some opcodes have a disambiguating suffix with respect to the type(s)
+ they operate on: Type-general 64-bit opcodes
+ are suffixed with <code>-wide</code>.
+ Type-specific opcodes are suffixed with their type (or a
+ straightforward abbreviation), one of: <code>-boolean</code>
+ <code>-byte</code> <code>-char</code> <code>-short</code>
+ <code>-int</code> <code>-long</code> <code>-float</code>
+ <code>-double</code> <code>-object</code> <code>-string</code>
+ <code>-class</code> <code>-void</code>. Type-general 32-bit opcodes
+ are unmarked.
+ </li>
+ <li>Some opcodes have a disambiguating suffix to distinguish
+ otherwise-identical operations that have different instruction layouts
+ or options. These suffixes are separated from the main names with a slash
+ ("<code>/</code>") and mainly exist at all to make there be a one-to-one
+ mapping with static constants in the code that generates and interprets
+ executables (that is, to reduce ambiguity for humans).
+ </li>
+ </ul>
+</li>
+<li>See the <a href="instruction-formats.html">instruction formats
+ document</a> for more details about the various instruction formats
+ (listed under "Op &amp; Format") as well as details about the opcode
+ syntax.
+</li>
+</ul>
+
+<h2>Summary of Instruction Set</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>00 10x</td>
+ <td>nop</td>
+ <td>&nbsp;</td>
+ <td>Waste cycles.</td>
+</tr>
+<tr>
+ <td>01 12x</td>
+ <td>move vA, vB</td>
+ <td><code>A:</code> destination register (4 bits)<br/>
+ <code>B:</code> source register (4 bits)</td>
+ <td>Move the contents of one non-object register to another.</td>
+</tr>
+<tr>
+ <td>02 22x</td>
+ <td>move/from16 vAA, vBBBB</td>
+ <td><code>A:</code> destination register (8 bits)<br/>
+ <code>B:</code> source register (16 bits)</td>
+ <td>Move the contents of one non-object register to another.</td>
+</tr>
+<tr>
+ <td>03 32x</td>
+ <td>move/16 vAAAA, vBBBB</td>
+ <td><code>A:</code> destination register (16 bits)<br/>
+ <code>B:</code> source register (16 bits)</td>
+ <td>Move the contents of one non-object register to another.</td>
+</tr>
+<tr>
+ <td>04 12x</td>
+ <td>move-wide vA, vB</td>
+ <td><code>A:</code> destination register pair (4 bits)<br/>
+ <code>B:</code> source register pair (4 bits)</td>
+ <td>Move the contents of one register-pair to another.
+ <p><b>Note:</b>
+ It is legal to move from <code>v<i>N</i></code> to either
+ <code>v<i>N-1</i></code> or <code>v<i>N+1</i></code>, so implementations
+ must arrange for both halves of a register pair to be read before
+ anything is written.</p>
+ </td>
+</tr>
+<tr>
+ <td>05 22x</td>
+ <td>move-wide/from16 vAA, vBBBB</td>
+ <td><code>A:</code> destination register pair (8 bits)<br/>
+ <code>B:</code> source register pair (16 bits)</td>
+ <td>Move the contents of one register-pair to another.
+ <p><b>Note:</b>
+ Implementation considerations are the same as <code>move-wide</code>,
+ above.</p>
+ </td>
+</tr>
+<tr>
+ <td>06 32x</td>
+ <td>move-wide/16 vAAAA, vBBBB</td>
+ <td><code>A:</code> destination register pair (16 bits)<br/>
+ <code>B:</code> source register pair (16 bits)</td>
+ <td>Move the contents of one register-pair to another.
+ <p><b>Note:</b>
+ Implementation considerations are the same as <code>move-wide</code>,
+ above.</p>
+ </td>
+</tr>
+<tr>
+ <td>07 12x</td>
+ <td>move-object vA, vB</td>
+ <td><code>A:</code> destination register (4 bits)<br/>
+ <code>B:</code> source register (4 bits)</td>
+ <td>Move the contents of one object-bearing register to another.</td>
+</tr>
+<tr>
+ <td>08 22x</td>
+ <td>move-object/from16 vAA, vBBBB</td>
+ <td><code>A:</code> destination register (8 bits)<br/>
+ <code>B:</code> source register (16 bits)</td>
+ <td>Move the contents of one object-bearing register to another.</td>
+</tr>
+<tr>
+ <td>09 32x</td>
+ <td>move-object/16 vAAAA, vBBBB</td>
+ <td><code>A:</code> destination register (16 bits)<br/>
+ <code>B:</code> source register (16 bits)</td>
+ <td>Move the contents of one object-bearing register to another.</td>
+</tr>
+<tr>
+ <td>0a 11x</td>
+ <td>move-result vAA</td>
+ <td><code>A:</code> destination register (8 bits)</td>
+ <td>Move the single-word non-object result of the most recent
+ <code>invoke-<i>kind</i></code> into the indicated register.
+ This must be done as the instruction immediately after an
+ <code>invoke-<i>kind</i></code> whose (single-word, non-object) result
+ is not to be ignored; anywhere else is invalid.</td>
+</tr>
+<tr>
+ <td>0b 11x</td>
+ <td>move-result-wide vAA</td>
+ <td><code>A:</code> destination register pair (8 bits)</td>
+ <td>Move the double-word result of the most recent
+ <code>invoke-<i>kind</i></code> into the indicated register pair.
+ This must be done as the instruction immediately after an
+ <code>invoke-<i>kind</i></code> whose (double-word) result
+ is not to be ignored; anywhere else is invalid.</td>
+</tr>
+<tr>
+ <td>0c 11x</td>
+ <td>move-result-object vAA</td>
+ <td><code>A:</code> destination register (8 bits)</td>
+ <td>Move the object result of the most recent <code>invoke-<i>kind</i></code>
+ into the indicated register. This must be done as the instruction
+ immediately after an <code>invoke-<i>kind</i></code> or
+ <code>filled-new-array</code>
+ whose (object) result is not to be ignored; anywhere else is invalid.</td>
+</tr>
+<tr>
+ <td>0d 11x</td>
+ <td>move-exception vAA</td>
+ <td><code>A:</code> destination register (8 bits)</td>
+ <td>Save a just-caught exception into the given register. This should
+ be the first instruction of any exception handler whose caught
+ exception is not to be ignored, and this instruction must <i>only</i>
+ ever occur as the first instruction of an exception handler; anywhere
+ else is invalid.</td>
+</tr>
+<tr>
+ <td>0e 10x</td>
+ <td>return-void</td>
+ <td>&nbsp;</td>
+ <td>Return from a <code>void</code> method.</td>
+</tr>
+<tr>
+ <td>0f 11x</td>
+ <td>return vAA</td>
+ <td><code>A:</code> return value register (8 bits)</td>
+ <td>Return from a single-width (32-bit) non-object value-returning
+ method.
+ </td>
+</tr>
+<tr>
+ <td>10 11x</td>
+ <td>return-wide vAA</td>
+ <td><code>A:</code> return value register-pair (8 bits)</td>
+ <td>Return from a double-width (64-bit) value-returning method.</td>
+</tr>
+<tr>
+ <td>11 11x</td>
+ <td>return-object vAA</td>
+ <td><code>A:</code> return value register (8 bits)</td>
+ <td>Return from an object-returning method.</td>
+</tr>
+<tr>
+ <td>12 11n</td>
+ <td>const/4 vA, #+B</td>
+ <td><code>A:</code> destination register (4 bits)<br/>
+ <code>B:</code> signed int (4 bits)</td>
+ <td>Move the given literal value (sign-extended to 32 bits) into
+ the specified register.</td>
+</tr>
+<tr>
+ <td>13 21s</td>
+ <td>const/16 vAA, #+BBBB</td>
+ <td><code>A:</code> destination register (8 bits)<br/>
+ <code>B:</code> signed int (16 bits)</td>
+ <td>Move the given literal value (sign-extended to 32 bits) into
+ the specified register.</td>
+</tr>
+<tr>
+ <td>14 31i</td>
+ <td>const vAA, #+BBBBBBBB</td>
+ <td><code>A:</code> destination register (8 bits)<br/>
+ <code>B:</code> arbitrary 32-bit constant</td>
+ <td>Move the given literal value into the specified register.</td>
+</tr>
+<tr>
+ <td>15 21h</td>
+ <td>const/high16 vAA, #+BBBB0000</td>
+ <td><code>A:</code> destination register (8 bits)<br/>
+ <code>B:</code> signed int (16 bits)</td>
+ <td>Move the given literal value (right-zero-extended to 32 bits) into
+ the specified register.</td>
+</tr>
+<tr>
+ <td>16 21s</td>
+ <td>const-wide/16 vAA, #+BBBB</td>
+ <td><code>A:</code> destination register (8 bits)<br/>
+ <code>B:</code> signed int (16 bits)</td>
+ <td>Move the given literal value (sign-extended to 64 bits) into
+ the specified register-pair.</td>
+</tr>
+<tr>
+ <td>17 31i</td>
+ <td>const-wide/32 vAA, #+BBBBBBBB</td>
+ <td><code>A:</code> destination register (8 bits)<br/>
+ <code>B:</code> signed int (32 bits)</td>
+ <td>Move the given literal value (sign-extended to 64 bits) into
+ the specified register-pair.</td>
+</tr>
+<tr>
+ <td>18 51l</td>
+ <td>const-wide vAA, #+BBBBBBBBBBBBBBBB</td>
+ <td><code>A:</code> destination register (8 bits)<br/>
+ <code>B:</code> arbitrary double-width (64-bit) constant</td>
+ <td>Move the given literal value into
+ the specified register-pair.</td>
+</tr>
+<tr>
+ <td>19 21h</td>
+ <td>const-wide/high16 vAA, #+BBBB000000000000</td>
+ <td><code>A:</code> destination register (8 bits)<br/>
+ <code>B:</code> signed int (16 bits)</td>
+ <td>Move the given literal value (right-zero-extended to 64 bits) into
+ the specified register-pair.</td>
+</tr>
+<tr>
+ <td>1a 21c</td>
+ <td>const-string vAA, string@BBBB</td>
+ <td><code>A:</code> destination register (8 bits)<br/>
+ <code>B:</code> string index</td>
+ <td>Move a reference to the string specified by the given index into the
+ specified register.</td>
+</tr>
+<tr>
+ <td>1b 31c</td>
+ <td>const-string/jumbo vAA, string@BBBBBBBB</td>
+ <td><code>A:</code> destination register (8 bits)<br/>
+ <code>B:</code> string index</td>
+ <td>Move a reference to the string specified by the given index into the
+ specified register.</td>
+</tr>
+<tr>
+ <td>1c 21c</td>
+ <td>const-class vAA, type@BBBB</td>
+ <td><code>A:</code> destination register (8 bits)<br/>
+ <code>B:</code> type index</td>
+ <td>Move a reference to the class specified by the given index into the
+ specified register. In the case where the indicated type is primitive,
+ this will store a reference to the primitive type's degenerate
+ class.</td>
+</tr>
+<tr>
+ <td>1d 11x</td>
+ <td>monitor-enter vAA</td>
+ <td><code>A:</code> reference-bearing register (8 bits)</td>
+ <td>Acquire the monitor for the indicated object.</td>
+</tr>
+<tr>
+ <td>1e 11x</td>
+ <td>monitor-exit vAA</td>
+ <td><code>A:</code> reference-bearing register (8 bits)</td>
+ <td>Release the monitor for the indicated object.
+ <p><b>Note:</b>
+ If this instruction needs to throw an exception, it must do
+ so as if the pc has already advanced past the instruction.
+ It may be useful to think of this as the instruction successfully
+ executing (in a sense), and the exception getting thrown <i>after</i>
+ the instruction but <i>before</i> the next one gets a chance to
+ run. This definition makes it possible for a method to use
+ a monitor cleanup catch-all (e.g., <code>finally</code>) block as
+ the monitor cleanup for that block itself, as a way to handle the
+ arbitrary exceptions that might get thrown due to the historical
+ implementation of <code>Thread.stop()</code>, while still managing
+ to have proper monitor hygiene.</p>
+ </td>
+</tr>
+<tr>
+ <td>1f 21c</td>
+ <td>check-cast vAA, type@BBBB</td>
+ <td><code>A:</code> reference-bearing register (8 bits)<br/>
+ <code>B:</code> type index (16 bits)</td>
+ <td>Throw a <code>ClassCastException</code> if the reference in the
+ given register cannot be cast to the indicated type.
+ <p><b>Note:</b> Since <code>A</code> must always be a reference
+ (and not a primitive value), this will necessarily fail at runtime
+ (that is, it will throw an exception) if <code>B</code> refers to a
+ primitive type.</p>
+ </td>
+</tr>
+<tr>
+ <td>20 22c</td>
+ <td>instance-of vA, vB, type@CCCC</td>
+ <td><code>A:</code> destination register (4 bits)<br/>
+ <code>B:</code> reference-bearing register (4 bits)<br/>
+ <code>C:</code> type index (16 bits)</td>
+ <td>Store in the given destination register <code>1</code>
+ if the indicated reference is an instance of the given type,
+ or <code>0</code> if not.
+ <p><b>Note:</b> Since <code>B</code> must always be a reference
+ (and not a primitive value), this will always result
+ in <code>0</code> being stored if <code>C</code> refers to a primitive
+ type.</td>
+</tr>
+<tr>
+ <td>21 12x</td>
+ <td>array-length vA, vB</td>
+ <td><code>A:</code> destination register (4 bits)<br/>
+ <code>B:</code> array reference-bearing register (4 bits)</td>
+ <td>Store in the given destination register the length of the indicated
+ array, in entries</td>
+</tr>
+<tr>
+ <td>22 21c</td>
+ <td>new-instance vAA, type@BBBB</td>
+ <td><code>A:</code> destination register (8 bits)<br/>
+ <code>B:</code> type index</td>
+ <td>Construct a new instance of the indicated type, storing a
+ reference to it in the destination. The type must refer to a
+ non-array class.</td>
+</tr>
+<tr>
+ <td>23 22c</td>
+ <td>new-array vA, vB, type@CCCC</td>
+ <td><code>A:</code> destination register (8 bits)<br/>
+ <code>B:</code> size register<br/>
+ <code>C:</code> type index</td>
+ <td>Construct a new array of the indicated type and size. The type
+ must be an array type.</td>
+</tr>
+<tr>
+ <td>24 35c</td>
+ <td>filled-new-array {vD, vE, vF, vG, vA}, type@CCCC</td>
+ <td><code>B:</code> array size and argument word count (4 bits)<br/>
+ <code>C:</code> type index (16 bits)<br/>
+ <code>D..G, A:</code> argument registers (4 bits each)</td>
+ <td>Construct an array of the given type and size, filling it with the
+ supplied contents. The type must be an array type. The array's
+ contents must be single-word (that is,
+ no arrays of <code>long</code> or <code>double</code>, but reference
+ types are acceptable). The constructed
+ instance is stored as a "result" in the same way that the method invocation
+ instructions store their results, so the constructed instance must
+ be moved to a register with an immediately subsequent
+ <code>move-result-object</code> instruction (if it is to be used).</td>
+</tr>
+<tr>
+ <td>25 3rc</td>
+ <td>filled-new-array/range {vCCCC .. vNNNN}, type@BBBB</td>
+ <td><code>A:</code> array size and argument word count (8 bits)<br/>
+ <code>B:</code> type index (16 bits)<br/>
+ <code>C:</code> first argument register (16 bits)<br/>
+ <code>N = A + C - 1</code></td>
+ <td>Construct an array of the given type and size, filling it with
+ the supplied contents. Clarifications and restrictions are the same
+ as <code>filled-new-array</code>, described above.</td>
+</tr>
+<tr>
+ <td>26 31t</td>
+ <td>fill-array-data vAA, +BBBBBBBB <i>(with supplemental data as specified
+ below in "<code>fill-array-data</code> Format")</i></td>
+ <td><code>A:</code> array reference (8 bits)<br/>
+ <code>B:</code> signed "branch" offset to table data pseudo-instruction
+ (32 bits)
+ </td>
+ <td>Fill the given array with the indicated data. The reference must be
+ to an array of primitives, and the data table must match it in type and
+ must contain no more elements than will fit in the array. That is,
+ the array may be larger than the table, and if so, only the initial
+ elements of the array are set, leaving the remainder alone.
+ </td>
+</tr>
+<tr>
+ <td>27 11x</td>
+ <td>throw vAA</td>
+ <td><code>A:</code> exception-bearing register (8 bits)<br/></td>
+ <td>Throw the indicated exception.</td>
+</tr>
+<tr>
+ <td>28 10t</td>
+ <td>goto +AA</td>
+ <td><code>A:</code> signed branch offset (8 bits)</td>
+ <td>Unconditionally jump to the indicated instruction.
+ <p><b>Note:</b>
+ The branch offset must not be <code>0</code>. (A spin
+ loop may be legally constructed either with <code>goto/32</code> or
+ by including a <code>nop</code> as a target before the branch.)</p>
+ </td>
+</tr>
+<tr>
+ <td>29 20t</td>
+ <td>goto/16 +AAAA</td>
+ <td><code>A:</code> signed branch offset (16 bits)<br/></td>
+ <td>Unconditionally jump to the indicated instruction.
+ <p><b>Note:</b>
+ The branch offset must not be <code>0</code>. (A spin
+ loop may be legally constructed either with <code>goto/32</code> or
+ by including a <code>nop</code> as a target before the branch.)</p>
+ </td>
+</tr>
+<tr>
+ <td>2a 30t</td>
+ <td>goto/32 +AAAAAAAA</td>
+ <td><code>A:</code> signed branch offset (32 bits)<br/></td>
+ <td>Unconditionally jump to the indicated instruction.</td>
+</tr>
+<tr>
+ <td>2b 31t</td>
+ <td>packed-switch vAA, +BBBBBBBB <i>(with supplemental data as
+ specified below in "<code>packed-switch</code> Format")</i></td>
+ <td><code>A:</code> register to test<br/>
+ <code>B:</code> signed "branch" offset to table data pseudo-instruction
+ (32 bits)
+ </td>
+ <td>Jump to a new instruction based on the value in the
+ given register, using a table of offsets corresponding to each value
+ in a particular integral range, or fall through to the next
+ instruction if there is no match.
+ </td>
+</tr>
+<tr>
+ <td>2c 31t</td>
+ <td>sparse-switch vAA, +BBBBBBBB <i>(with supplemental data as
+ specified below in "<code>sparse-switch</code> Format")</i></td>
+ <td><code>A:</code> register to test<br/>
+ <code>B:</code> signed "branch" offset to table data pseudo-instruction
+ (32 bits)
+ </td>
+ <td>Jump to a new instruction based on the value in the given
+ register, using an ordered table of value-offset pairs, or fall
+ through to the next instruction if there is no match.
+ </td>
+</tr>
+<tr>
+ <td>2d..31 23x</td>
+ <td>cmp<i>kind</i> vAA, vBB, vCC<br/>
+ 2d: cmpl-float <i>(lt bias)</i><br/>
+ 2e: cmpg-float <i>(gt bias)</i><br/>
+ 2f: cmpl-double <i>(lt bias)</i><br/>
+ 30: cmpg-double <i>(gt bias)</i><br/>
+ 31: cmp-long
+ </td>
+ <td><code>A:</code> destination register (8 bits)<br/>
+ <code>B:</code> first source register or pair<br/>
+ <code>C:</code> second source register or pair</td>
+ <td>Perform the indicated floating point or <code>long</code> comparison,
+ storing <code>0</code> if the two arguments are equal, <code>1</code>
+ if the second argument is larger, or <code>-1</code> if the first
+ argument is larger. The "bias" listed for the floating point operations
+ indicates how <code>NaN</code> comparisons are treated: "Gt bias"
+ instructions return <code>1</code> for <code>NaN</code> comparisons,
+ and "lt bias" instructions return
+ <code>-1</code>.
+ <p>For example, to check to see if floating point
+ <code>a &lt; b</code>, then it is advisable to use
+ <code>cmpg-float</code>; a result of <code>-1</code> indicates that
+ the test was true, and the other values indicate it was false either
+ due to a valid comparison or because one or the other values was
+ <code>NaN</code>.</p>
+ </td>
+</tr>
+<tr>
+ <td>32..37 22t</td>
+ <td>if-<i>test</i> vA, vB, +CCCC<br/>
+ 32: if-eq<br/>
+ 33: if-ne<br/>
+ 34: if-lt<br/>
+ 35: if-ge<br/>
+ 36: if-gt<br/>
+ 37: if-le<br/>
+ </td>
+ <td><code>A:</code> first register to test (4 bits)<br/>
+ <code>B:</code> second register to test (4 bits)<br/>
+ <code>C:</code> signed branch offset (16 bits)</td>
+ <td>Branch to the given destination if the given two registers' values
+ compare as specified.
+ <p><b>Note:</b>
+ The branch offset must not be <code>0</code>. (A spin
+ loop may be legally constructed either by branching around a
+ backward <code>goto</code> or by including a <code>nop</code> as
+ a target before the branch.)</p>
+ </td>
+</tr>
+<tr>
+ <td>38..3d 21t</td>
+ <td>if-<i>test</i>z vAA, +BBBB<br/>
+ 38: if-eqz<br/>
+ 39: if-nez<br/>
+ 3a: if-ltz<br/>
+ 3b: if-gez<br/>
+ 3c: if-gtz<br/>
+ 3d: if-lez<br/>
+ </td>
+ <td><code>A:</code> register to test (8 bits)<br/>
+ <code>B:</code> signed branch offset (16 bits)</td>
+ <td>Branch to the given destination if the given register's value compares
+ with 0 as specified.
+ <p><b>Note:</b>
+ The branch offset must not be <code>0</code>. (A spin
+ loop may be legally constructed either by branching around a
+ backward <code>goto</code> or by including a <code>nop</code> as
+ a target before the branch.)</p>
+ </td>
+</tr>
+<tr>
+ <td>3e..43 10x</td>
+ <td><i>(unused)</i></td>
+ <td>&nbsp;</td>
+ <td><i>(unused)</i></td>
+</tr>
+<tr>
+ <td>44..51 23x</td>
+ <td><i>arrayop</i> vAA, vBB, vCC<br/>
+ 44: aget<br/>
+ 45: aget-wide<br/>
+ 46: aget-object<br/>
+ 47: aget-boolean<br/>
+ 48: aget-byte<br/>
+ 49: aget-char<br/>
+ 4a: aget-short<br/>
+ 4b: aput<br/>
+ 4c: aput-wide<br/>
+ 4d: aput-object<br/>
+ 4e: aput-boolean<br/>
+ 4f: aput-byte<br/>
+ 50: aput-char<br/>
+ 51: aput-short
+ </td>
+ <td><code>A:</code> value register or pair; may be source or dest
+ (8 bits)<br/>
+ <code>B:</code> array register (8 bits)<br/>
+ <code>C:</code> index register (8 bits)</td>
+ <td>Perform the identified array operation at the identified index of
+ the given array, loading or storing into the value register.</td>
+</tr>
+<tr>
+ <td>52..5f 22c</td>
+ <td>i<i>instanceop</i> vA, vB, field@CCCC<br/>
+ 52: iget<br/>
+ 53: iget-wide<br/>
+ 54: iget-object<br/>
+ 55: iget-boolean<br/>
+ 56: iget-byte<br/>
+ 57: iget-char<br/>
+ 58: iget-short<br/>
+ 59: iput<br/>
+ 5a: iput-wide<br/>
+ 5b: iput-object<br/>
+ 5c: iput-boolean<br/>
+ 5d: iput-byte<br/>
+ 5e: iput-char<br/>
+ 5f: iput-short
+ </td>
+ <td><code>A:</code> value register or pair; may be source or dest
+ (4 bits)<br/>
+ <code>B:</code> object register (4 bits)<br/>
+ <code>C:</code> instance field reference index (16 bits)</td>
+ <td>Perform the identified object instance field operation with
+ the identified field, loading or storing into the value register.
+ <p><b>Note:</b> These opcodes are reasonable candidates for static linking,
+ altering the field argument to be a more direct offset.</p>
+ </td>
+</tr>
+<tr>
+ <td>60..6d 21c</td>
+ <td>s<i>staticop</i> vAA, field@BBBB<br/>
+ 60: sget<br/>
+ 61: sget-wide<br/>
+ 62: sget-object<br/>
+ 63: sget-boolean<br/>
+ 64: sget-byte<br/>
+ 65: sget-char<br/>
+ 66: sget-short<br/>
+ 67: sput<br/>
+ 68: sput-wide<br/>
+ 69: sput-object<br/>
+ 6a: sput-boolean<br/>
+ 6b: sput-byte<br/>
+ 6c: sput-char<br/>
+ 6d: sput-short
+ </td>
+ <td><code>A:</code> value register or pair; may be source or dest
+ (8 bits)<br/>
+ <code>B:</code> static field reference index (16 bits)</td>
+ <td>Perform the identified object static field operation with the identified
+ static field, loading or storing into the value register.
+ <p><b>Note:</b> These opcodes are reasonable candidates for static linking,
+ altering the field argument to be a more direct offset.</p>
+ </td>
+</tr>
+<tr>
+ <td>6e..72 35c</td>
+ <td>invoke-<i>kind</i> {vD, vE, vF, vG, vA}, meth@CCCC<br/>
+ 6e: invoke-virtual<br/>
+ 6f: invoke-super<br/>
+ 70: invoke-direct<br/>
+ 71: invoke-static<br/>
+ 72: invoke-interface
+ </td>
+ <td><code>B:</code> argument word count (4 bits)<br/>
+ <code>C:</code> method index (16 bits)<br/>
+ <code>D..G, A:</code> argument registers (4 bits each)</td>
+ <td>Call the indicated method. The result (if any) may be stored
+ with an appropriate <code>move-result*</code> variant as the immediately
+ subsequent instruction.
+ <p><code>invoke-virtual</code> is used to invoke a normal virtual
+ method (a method that is not <code>private</code>, <code>static</code>,
+ or <code>final</code>, and is also not a constructor).</p>
+ <p><code>invoke-super</code> is used to invoke the closest superclass's
+ virtual method (as opposed to the one with the same <code>method_id</code>
+ in the calling class). The same method restrictions hold as for
+ <code>invoke-virtual</code>.</p>
+ <p><code>invoke-direct</code> is used to invoke a non-<code>static</code>
+ direct method (that is, an instance method that is by its nature
+ non-overridable, namely either a <code>private</code> instance method
+ or a constructor).</p>
+ <p><code>invoke-static</code> is used to invoke a <code>static</code>
+ method (which is always considered a direct method).</p>
+ <p><code>invoke-interface</code> is used to invoke an
+ <code>interface</code> method, that is, on an object whose concrete
+ class isn't known, using a <code>method_id</code> that refers to
+ an <code>interface</code>.</p>
+ <p><b>Note:</b> These opcodes are reasonable candidates for static linking,
+ altering the method argument to be a more direct offset
+ (or pair thereof).</p>
+ </td>
+</tr>
+<tr>
+ <td>73 10x</td>
+ <td><i>(unused)</i></td>
+ <td>&nbsp;</td>
+ <td><i>(unused)</i></td>
+</tr>
+<tr>
+ <td>74..78 3rc</td>
+ <td>invoke-<i>kind</i>/range {vCCCC .. vNNNN}, meth@BBBB<br/>
+ 74: invoke-virtual/range<br/>
+ 75: invoke-super/range<br/>
+ 76: invoke-direct/range<br/>
+ 77: invoke-static/range<br/>
+ 78: invoke-interface/range
+ </td>
+ <td><code>A:</code> argument word count (8 bits)<br/>
+ <code>B:</code> method index (16 bits)<br/>
+ <code>C:</code> first argument register (16 bits)<br/>
+ <code>N = A + C - 1</code></td>
+ <td>Call the indicated method. See first <code>invoke-<i>kind</i></code>
+ description above for details, caveats, and suggestions.
+ </td>
+</tr>
+<tr>
+ <td>79..7a 10x</td>
+ <td><i>(unused)</i></td>
+ <td>&nbsp;</td>
+ <td><i>(unused)</i></td>
+</tr>
+<tr>
+ <td>7b..8f 12x</td>
+ <td><i>unop</i> vA, vB<br/>
+ 7b: neg-int<br/>
+ 7c: not-int<br/>
+ 7d: neg-long<br/>
+ 7e: not-long<br/>
+ 7f: neg-float<br/>
+ 80: neg-double<br/>
+ 81: int-to-long<br/>
+ 82: int-to-float<br/>
+ 83: int-to-double<br/>
+ 84: long-to-int<br/>
+ 85: long-to-float<br/>
+ 86: long-to-double<br/>
+ 87: float-to-int<br/>
+ 88: float-to-long<br/>
+ 89: float-to-double<br/>
+ 8a: double-to-int<br/>
+ 8b: double-to-long<br/>
+ 8c: double-to-float<br/>
+ 8d: int-to-byte<br/>
+ 8e: int-to-char<br/>
+ 8f: int-to-short
+ </td>
+ <td><code>A:</code> destination register or pair (4 bits)<br/>
+ <code>B:</code> source register or pair (4 bits)</td>
+ <td>Perform the identified unary operation on the source register,
+ storing the result in the destination register.</td>
+</tr>
+
+<tr>
+ <td>90..af 23x</td>
+ <td><i>binop</i> vAA, vBB, vCC<br/>
+ 90: add-int<br/>
+ 91: sub-int<br/>
+ 92: mul-int<br/>
+ 93: div-int<br/>
+ 94: rem-int<br/>
+ 95: and-int<br/>
+ 96: or-int<br/>
+ 97: xor-int<br/>
+ 98: shl-int<br/>
+ 99: shr-int<br/>
+ 9a: ushr-int<br/>
+ 9b: add-long<br/>
+ 9c: sub-long<br/>
+ 9d: mul-long<br/>
+ 9e: div-long<br/>
+ 9f: rem-long<br/>
+ a0: and-long<br/>
+ a1: or-long<br/>
+ a2: xor-long<br/>
+ a3: shl-long<br/>
+ a4: shr-long<br/>
+ a5: ushr-long<br/>
+ a6: add-float<br/>
+ a7: sub-float<br/>
+ a8: mul-float<br/>
+ a9: div-float<br/>
+ aa: rem-float<br/>
+ ab: add-double<br/>
+ ac: sub-double<br/>
+ ad: mul-double<br/>
+ ae: div-double<br/>
+ af: rem-double
+ </td>
+ <td><code>A:</code> destination register or pair (8 bits)<br/>
+ <code>B:</code> first source register or pair (8 bits)<br/>
+ <code>C:</code> second source register or pair (8 bits)</td>
+ <td>Perform the identified binary operation on the two source registers,
+ storing the result in the first source register.</td>
+</tr>
+<tr>
+ <td>b0..cf 12x</td>
+ <td><i>binop</i>/2addr vA, vB<br/>
+ b0: add-int/2addr<br/>
+ b1: sub-int/2addr<br/>
+ b2: mul-int/2addr<br/>
+ b3: div-int/2addr<br/>
+ b4: rem-int/2addr<br/>
+ b5: and-int/2addr<br/>
+ b6: or-int/2addr<br/>
+ b7: xor-int/2addr<br/>
+ b8: shl-int/2addr<br/>
+ b9: shr-int/2addr<br/>
+ ba: ushr-int/2addr<br/>
+ bb: add-long/2addr<br/>
+ bc: sub-long/2addr<br/>
+ bd: mul-long/2addr<br/>
+ be: div-long/2addr<br/>
+ bf: rem-long/2addr<br/>
+ c0: and-long/2addr<br/>
+ c1: or-long/2addr<br/>
+ c2: xor-long/2addr<br/>
+ c3: shl-long/2addr<br/>
+ c4: shr-long/2addr<br/>
+ c5: ushr-long/2addr<br/>
+ c6: add-float/2addr<br/>
+ c7: sub-float/2addr<br/>
+ c8: mul-float/2addr<br/>
+ c9: div-float/2addr<br/>
+ ca: rem-float/2addr<br/>
+ cb: add-double/2addr<br/>
+ cc: sub-double/2addr<br/>
+ cd: mul-double/2addr<br/>
+ ce: div-double/2addr<br/>
+ cf: rem-double/2addr
+ </td>
+ <td><code>A:</code> destination and first source register or pair
+ (4 bits)<br/>
+ <code>B:</code> second source register or pair (4 bits)</td>
+ <td>Perform the identified binary operation on the two source registers,
+ storing the result in the first source register.</td>
+</tr>
+<tr>
+ <td>d0..d7 22s</td>
+ <td><i>binop</i>/lit16 vA, vB, #+CCCC<br/>
+ d0: add-int/lit16<br/>
+ d1: rsub-int (reverse subtract)<br/>
+ d2: mul-int/lit16<br/>
+ d3: div-int/lit16<br/>
+ d4: rem-int/lit16<br/>
+ d5: and-int/lit16<br/>
+ d6: or-int/lit16<br/>
+ d7: xor-int/lit16
+ </td>
+ <td><code>A:</code> destination register (4 bits)<br/>
+ <code>B:</code> source register (4 bits)<br/>
+ <code>C:</code> signed int constant (16 bits)</td>
+ <td>Perform the indicated binary op on the indicated register (first
+ argument) and literal value (second argument), storing the result in
+ the destination register.
+ <p><b>Note:</b>
+ <code>rsub-int</code> does not have a suffix since this version is the
+ main opcode of its family. Also, see below for details on its semantics.
+ </p>
+ </td>
+</tr>
+<tr>
+ <td>d8..e2 22b</td>
+ <td><i>binop</i>/lit8 vAA, vBB, #+CC<br/>
+ d8: add-int/lit8<br/>
+ d9: rsub-int/lit8<br/>
+ da: mul-int/lit8<br/>
+ db: div-int/lit8<br/>
+ dc: rem-int/lit8<br/>
+ dd: and-int/lit8<br/>
+ de: or-int/lit8<br/>
+ df: xor-int/lit8<br/>
+ e0: shl-int/lit8<br/>
+ e1: shr-int/lit8<br/>
+ e2: ushr-int/lit8
+ </td>
+ <td><code>A:</code> destination register (8 bits)<br/>
+ <code>B:</code> source register (8 bits)<br/>
+ <code>C:</code> signed int constant (8 bits)</td>
+ <td>Perform the indicated binary op on the indicated register (first
+ argument) and literal value (second argument), storing the result
+ in the destination register.
+ <p><b>Note:</b> See below for details on the semantics of
+ <code>rsub-int</code>.</p>
+ </td>
+</tr>
+<tr>
+ <td>e3..ff 10x</td>
+ <td><i>(unused)</i></td>
+ <td>&nbsp;</td>
+ <td><i>(unused)</i></td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>packed-switch</code> Format</h2>
+
+<table class="supplement">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>ident</td>
+ <td>ushort = 0x0100</td>
+ <td>identifying pseudo-opcode</td>
+</tr>
+<tr>
+ <td>size</td>
+ <td>ushort</td>
+ <td>number of entries in the table</td>
+</tr>
+<tr>
+ <td>first_key</td>
+ <td>int</td>
+ <td>first (and lowest) switch case value</td>
+</tr>
+<tr>
+ <td>targets</td>
+ <td>int[]</td>
+ <td>list of <code>size</code> relative branch targets. The targets are
+ relative to the address of the switch opcode, not of this table.
+ </td>
+</tr>
+</tbody>
+</table>
+
+<p><b>Note:</b> The total number of code units for an instance of this
+table is <code>(size * 2) + 4</code>.</p>
+
+<h2><code>sparse-switch</code> Format</h2>
+
+<table class="supplement">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>ident</td>
+ <td>ushort = 0x0200</td>
+ <td>identifying pseudo-opcode</td>
+</tr>
+<tr>
+ <td>size</td>
+ <td>ushort</td>
+ <td>number of entries in the table</td>
+</tr>
+<tr>
+ <td>keys</td>
+ <td>int[]</td>
+ <td>list of <code>size</code> key values, sorted low-to-high</td>
+</tr>
+<tr>
+ <td>targets</td>
+ <td>int[]</td>
+ <td>list of <code>size</code> relative branch targets, each corresponding
+ to the key value at the same index. The targets are
+ relative to the address of the switch opcode, not of this table.
+ </td>
+</tr>
+</tbody>
+</table>
+
+<p><b>Note:</b> The total number of code units for an instance of this
+table is <code>(size * 4) + 2</code>.</p>
+
+<h2><code>fill-array-data</code> Format</h2>
+
+<table class="supplement">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>ident</td>
+ <td>ushort = 0x0300</td>
+ <td>identifying pseudo-opcode</td>
+</tr>
+<tr>
+ <td>element_width</td>
+ <td>ushort</td>
+ <td>number of bytes in each element</td>
+</tr>
+<tr>
+ <td>size</td>
+ <td>uint</td>
+ <td>number of elements in the table</td>
+</tr>
+<tr>
+ <td>data</td>
+ <td>ubyte[]</td>
+ <td>data values</td>
+</tr>
+</tbody>
+</table>
+
+<p><b>Note:</b> The total number of code units for an instance of this
+table is <code>(size * element_width + 1) / 2 + 4</code>.</p>
+
+
+<h2>Mathematical Operation Details</h2>
+
+<p><b>Note:</b> Floating point operations must follow IEEE 754 rules, using
+round-to-nearest and gradual underflow, except where stated otherwise.</p>
+
+<table class="math">
+<thead>
+<tr>
+ <th>Opcode</th>
+ <th>C Semantics</th>
+ <th>Notes</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>neg-int</td>
+ <td>int32 a;<br/>
+ int32 result = -a;
+ </td>
+ <td>Unary twos-complement.</td>
+</tr>
+<tr>
+ <td>not-int</td>
+ <td>int32 a;<br/>
+ int32 result = ~a;
+ </td>
+ <td>Unary ones-complement.</td>
+</tr>
+<tr>
+ <td>neg-long</td>
+ <td>int64 a;<br/>
+ int64 result = -a;
+ </td>
+ <td>Unary twos-complement.</td>
+</tr>
+<tr>
+ <td>not-long</td>
+ <td>int64 a;<br/>
+ int64 result = ~a;
+ </td>
+ <td>Unary ones-complement.</td>
+</tr>
+<tr>
+ <td>neg-float</td>
+ <td>float a;<br/>
+ float result = -a;
+ </td>
+ <td>Floating point negation.</td>
+</tr>
+<tr>
+ <td>neg-double</td>
+ <td>double a;<br/>
+ double result = -a;
+ </td>
+ <td>Floating point negation.</td>
+</tr>
+<tr>
+ <td>int-to-long</td>
+ <td>int32 a;<br/>
+ int64 result = (int64) a;
+ </td>
+ <td>Sign extension of <code>int32</code> into <code>int64</code>.</td>
+</tr>
+<tr>
+ <td>int-to-float</td>
+ <td>int32 a;<br/>
+ float result = (float) a;
+ </td>
+ <td>Conversion of <code>int32</code> to <code>float</code>, using
+ round-to-nearest. This loses precision for some values.
+ </td>
+</tr>
+<tr>
+ <td>int-to-double</td>
+ <td>int32 a;<br/>
+ double result = (double) a;
+ </td>
+ <td>Conversion of <code>int32</code> to <code>double</code>.</td>
+</tr>
+<tr>
+ <td>long-to-int</td>
+ <td>int64 a;<br/>
+ int32 result = (int32) a;
+ </td>
+ <td>Truncation of <code>int64</code> into <code>int32</code>.</td>
+</tr>
+<tr>
+ <td>long-to-float</td>
+ <td>int64 a;<br/>
+ float result = (float) a;
+ </td>
+ <td>Conversion of <code>int64</code> to <code>float</code>, using
+ round-to-nearest. This loses precision for some values.
+ </td>
+</tr>
+<tr>
+ <td>long-to-double</td>
+ <td>int64 a;<br/>
+ double result = (double) a;
+ </td>
+ <td>Conversion of <code>int64</code> to <code>double</code>, using
+ round-to-nearest. This loses precision for some values.
+ </td>
+</tr>
+<tr>
+ <td>float-to-int</td>
+ <td>float a;<br/>
+ int32 result = (int32) a;
+ </td>
+ <td>Conversion of <code>float</code> to <code>int32</code>, using
+ round-toward-zero. <code>NaN</code> and <code>-0.0</code> (negative zero)
+ convert to the integer <code>0</code>. Infinities and values with
+ too large a magnitude to be represented get converted to either
+ <code>0x7fffffff</code> or <code>-0x80000000</code> depending on sign.
+ </td>
+</tr>
+<tr>
+ <td>float-to-long</td>
+ <td>float a;<br/>
+ int64 result = (int64) a;
+ </td>
+ <td>Conversion of <code>float</code> to <code>int64</code>, using
+ round-toward-zero. The same special case rules as for
+ <code>float-to-int</code> apply here, except that out-of-range values
+ get converted to either <code>0x7fffffffffffffff</code> or
+ <code>-0x8000000000000000</code> depending on sign.
+ </td>
+</tr>
+<tr>
+ <td>float-to-double</td>
+ <td>float a;<br/>
+ double result = (double) a;
+ </td>
+ <td>Conversion of <code>float</code> to <code>double</code>, preserving
+ the value exactly.
+ </td>
+</tr>
+<tr>
+ <td>double-to-int</td>
+ <td>double a;<br/>
+ int32 result = (int32) a;
+ </td>
+ <td>Conversion of <code>double</code> to <code>int32</code>, using
+ round-toward-zero. The same special case rules as for
+ <code>float-to-int</code> apply here.
+ </td>
+</tr>
+<tr>
+ <td>double-to-long</td>
+ <td>double a;<br/>
+ int64 result = (int64) a;
+ </td>
+ <td>Conversion of <code>double</code> to <code>int64</code>, using
+ round-toward-zero. The same special case rules as for
+ <code>float-to-long</code> apply here.
+ </td>
+</tr>
+<tr>
+ <td>double-to-float</td>
+ <td>double a;<br/>
+ float result = (float) a;
+ </td>
+ <td>Conversion of <code>double</code> to <code>float</code>, using
+ round-to-nearest. This loses precision for some values.
+ </td>
+</tr>
+<tr>
+ <td>int-to-byte</td>
+ <td>int32 a;<br/>
+ int32 result = (a &lt;&lt; 24) &gt;&gt; 24;
+ </td>
+ <td>Truncation of <code>int32</code> to <code>int8</code>, sign
+ extending the result.
+ </td>
+</tr>
+<tr>
+ <td>int-to-char</td>
+ <td>int32 a;<br/>
+ int32 result = a &amp; 0xffff;
+ </td>
+ <td>Truncation of <code>int32</code> to <code>uint16</code>, without
+ sign extension.
+ </td>
+</tr>
+<tr>
+ <td>int-to-short</td>
+ <td>int32 a;<br/>
+ int32 result = (a &lt;&lt; 16) &gt;&gt; 16;
+ </td>
+ <td>Truncation of <code>int32</code> to <code>int16</code>, sign
+ extending the result.
+ </td>
+</tr>
+<tr>
+ <td>add-int</td>
+ <td>int32 a, b;<br/>
+ int32 result = a + b;
+ </td>
+ <td>Twos-complement addition.</td>
+</tr>
+<tr>
+ <td>sub-int</td>
+ <td>int32 a, b;<br/>
+ int32 result = a - b;
+ </td>
+ <td>Twos-complement subtraction.</td>
+</tr>
+<tr>
+ <td>rsub-int</td>
+ <td>int32 a, b;<br/>
+ int32 result = b - a;
+ </td>
+ <td>Twos-complement reverse subtraction.</td>
+</tr>
+<tr>
+ <td>mul-int</td>
+ <td>int32 a, b;<br/>
+ int32 result = a * b;
+ </td>
+ <td>Twos-complement multiplication.</td>
+</tr>
+<tr>
+ <td>div-int</td>
+ <td>int32 a, b;<br/>
+ int32 result = a / b;
+ </td>
+ <td>Twos-complement division, rounded towards zero (that is, truncated to
+ integer). This throws <code>ArithmeticException</code> if
+ <code>b == 0</code>.
+ </td>
+</tr>
+<tr>
+ <td>rem-int</td>
+ <td>int32 a, b;<br/>
+ int32 result = a % b;
+ </td>
+ <td>Twos-complement remainder after division. The sign of the result
+ is the same as that of <code>a</code>, and it is more precisely
+ defined as <code>result == a - (a / b) * b</code>. This throws
+ <code>ArithmeticException</code> if <code>b == 0</code>.
+ </td>
+</tr>
+<tr>
+ <td>and-int</td>
+ <td>int32 a, b;<br/>
+ int32 result = a &amp; b;
+ </td>
+ <td>Bitwise AND.</td>
+</tr>
+<tr>
+ <td>or-int</td>
+ <td>int32 a, b;<br/>
+ int32 result = a | b;
+ </td>
+ <td>Bitwise OR.</td>
+</tr>
+<tr>
+ <td>xor-int</td>
+ <td>int32 a, b;<br/>
+ int32 result = a ^ b;
+ </td>
+ <td>Bitwise XOR.</td>
+</tr>
+<tr>
+ <td>shl-int</td>
+ <td>int32 a, b;<br/>
+ int32 result = a &lt;&lt; (b &amp; 0x1f);
+ </td>
+ <td>Bitwise shift left (with masked argument).</td>
+</tr>
+<tr>
+ <td>shr-int</td>
+ <td>int32 a, b;<br/>
+ int32 result = a &gt;&gt; (b &amp; 0x1f);
+ </td>
+ <td>Bitwise signed shift right (with masked argument).</td>
+</tr>
+<tr>
+ <td>ushr-int</td>
+ <td>uint32 a, b;<br/>
+ int32 result = a &gt;&gt; (b &amp; 0x1f);
+ </td>
+ <td>Bitwise unsigned shift right (with masked argument).</td>
+</tr>
+<tr>
+ <td>add-long</td>
+ <td>int64 a, b;<br/>
+ int64 result = a + b;
+ </td>
+ <td>Twos-complement addition.</td>
+</tr>
+<tr>
+ <td>sub-long</td>
+ <td>int64 a, b;<br/>
+ int64 result = a - b;
+ </td>
+ <td>Twos-complement subtraction.</td>
+</tr>
+<tr>
+ <td>mul-long</td>
+ <td>int64 a, b;<br/>
+ int64 result = a * b;
+ </td>
+ <td>Twos-complement multiplication.</td>
+</tr>
+<tr>
+ <td>div-long</td>
+ <td>int64 a, b;<br/>
+ int64 result = a / b;
+ </td>
+ <td>Twos-complement division, rounded towards zero (that is, truncated to
+ integer). This throws <code>ArithmeticException</code> if
+ <code>b == 0</code>.
+ </td>
+</tr>
+<tr>
+ <td>rem-long</td>
+ <td>int64 a, b;<br/>
+ int64 result = a % b;
+ </td>
+ <td>Twos-complement remainder after division. The sign of the result
+ is the same as that of <code>a</code>, and it is more precisely
+ defined as <code>result == a - (a / b) * b</code>. This throws
+ <code>ArithmeticException</code> if <code>b == 0</code>.
+ </td>
+</tr>
+<tr>
+ <td>and-long</td>
+ <td>int64 a, b;<br/>
+ int64 result = a &amp; b;
+ </td>
+ <td>Bitwise AND.</td>
+</tr>
+<tr>
+ <td>or-long</td>
+ <td>int64 a, b;<br/>
+ int64 result = a | b;
+ </td>
+ <td>Bitwise OR.</td>
+</tr>
+<tr>
+ <td>xor-long</td>
+ <td>int64 a, b;<br/>
+ int64 result = a ^ b;
+ </td>
+ <td>Bitwise XOR.</td>
+</tr>
+<tr>
+ <td>shl-long</td>
+ <td>int64 a, b;<br/>
+ int64 result = a &lt;&lt; (b &amp; 0x3f);
+ </td>
+ <td>Bitwise shift left (with masked argument).</td>
+</tr>
+<tr>
+ <td>shr-long</td>
+ <td>int64 a, b;<br/>
+ int64 result = a &gt;&gt; (b &amp; 0x3f);
+ </td>
+ <td>Bitwise signed shift right (with masked argument).</td>
+</tr>
+<tr>
+ <td>ushr-long</td>
+ <td>uint64 a, b;<br/>
+ int64 result = a &gt;&gt; (b &amp; 0x3f);
+ </td>
+ <td>Bitwise unsigned shift right (with masked argument).</td>
+</tr>
+<tr>
+ <td>add-float</td>
+ <td>float a, b;<br/>
+ float result = a + b;
+ </td>
+ <td>Floating point addition.</td>
+</tr>
+<tr>
+ <td>sub-float</td>
+ <td>float a, b;<br/>
+ float result = a - b;
+ </td>
+ <td>Floating point subtraction.</td>
+</tr>
+<tr>
+ <td>mul-float</td>
+ <td>float a, b;<br/>
+ float result = a * b;
+ </td>
+ <td>Floating point multiplication.</td>
+</tr>
+<tr>
+ <td>div-float</td>
+ <td>float a, b;<br/>
+ float result = a / b;
+ </td>
+ <td>Floating point division.</td>
+</tr>
+<tr>
+ <td>rem-float</td>
+ <td>float a, b;<br/>
+ float result = a % b;
+ </td>
+ <td>Floating point remainder after division. This function is different
+ than IEEE 754 remainder and is defined as
+ <code>result == a - roundTowardZero(a / b) * b</code>.
+ </td>
+</tr>
+<tr>
+ <td>add-double</td>
+ <td>double a, b;<br/>
+ double result = a + b;
+ </td>
+ <td>Floating point addition.</td>
+</tr>
+<tr>
+ <td>sub-double</td>
+ <td>double a, b;<br/>
+ double result = a - b;
+ </td>
+ <td>Floating point subtraction.</td>
+</tr>
+<tr>
+ <td>mul-double</td>
+ <td>double a, b;<br/>
+ double result = a * b;
+ </td>
+ <td>Floating point multiplication.</td>
+</tr>
+<tr>
+ <td>div-double</td>
+ <td>double a, b;<br/>
+ double result = a / b;
+ </td>
+ <td>Floating point division.</td>
+</tr>
+<tr>
+ <td>rem-double</td>
+ <td>double a, b;<br/>
+ double result = a % b;
+ </td>
+ <td>Floating point remainder after division. This function is different
+ than IEEE 754 remainder and is defined as
+ <code>result == a - roundTowardZero(a / b) * b</code>.
+ </td>
+</tr>
+</tbody>
+</table>
+
+</body>
+</html>
diff --git a/docs/dalvik-constraints.css b/docs/dalvik-constraints.css
new file mode 100644
index 0000000..a315a73
--- /dev/null
+++ b/docs/dalvik-constraints.css
@@ -0,0 +1,59 @@
+h1 {
+ font-family: serif;
+ color: #222266;
+}
+
+h2 {
+ font-family: serif;
+ border-top-style: solid;
+ border-top-width: 2px;
+ border-color: #ccccdd;
+ padding-top: 12px;
+ margin-top: 48px;
+ margin-bottom: 2px;
+ color: #222266;
+}
+
+@media print {
+ table {
+ font-size: 8pt;
+ }
+}
+
+@media screen {
+ table {
+ font-size: 10pt;
+ }
+}
+
+
+/* general for all tables */
+
+table {
+ border-collapse: collapse;
+ margin-top: 24px;
+ margin-bottom: 24px;
+ margin-left: 48px;
+ margin-right: 48px;
+}
+
+table th {
+ font-family: sans-serif;
+ background: #aabbff;
+ text-align: left;
+}
+
+table td {
+ font-family: sans-serif;
+ border-top-style: solid;
+ border-bottom-style: solid;
+ border-width: 1px;
+ border-color: #aaaaff;
+ padding-top: 4px;
+ padding-bottom: 4px;
+ padding-left: 4px;
+ padding-right: 6px;
+ background: #eeeeff;
+ margin-top: 4pt;
+ margin-bottom: 0pt;
+}
diff --git a/docs/dalvik-constraints.html b/docs/dalvik-constraints.html
new file mode 100644
index 0000000..69abf3a
--- /dev/null
+++ b/docs/dalvik-constraints.html
@@ -0,0 +1,897 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+ <head>
+ <title>Dalvik bytecode constraints</title>
+ <link rel=stylesheet href="dalvik-constraints.css">
+ </head>
+
+ <body>
+
+ <h1>Dalvik bytecode constraints</h1>
+
+<!--
+ <h1>General integrity constraints</h1>
+
+ <table>
+ <tr>
+ <th>
+ Identifier
+ </th>
+
+ <th>
+ Description
+ </th>
+ </tr>
+
+ <tr>
+ <td>
+ A1
+ </td>
+
+ <td>
+ The magic number of the DEX file must be "dex\n035\0".
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A1
+ </td>
+
+ <td>
+ The checksum must be an Adler-32 checksum of the whole file contents
+ except magic and checksum field.
+ </td>
+ </tr>
+
+
+The signature must be a SHA-1 hash of the whole file contents except magic,
+checksum, and signature.
+
+The file_size must match the actual file size in bytes.
+
+The header_size must have the value 0x70.
+
+The endian_tag must have either the value ENDIAN_CONSTANT or
+REVERSE_ENDIAN_CONSTANT.
+
+For each of the link, string_ids, type_ids, proto_ids, field_ids, method_ids, class_defs
+and data sections, the offset and size fields must be either both zero or both
+non-zero. In the latter case, the offset must be four-byte-aligned.
+
+All offset fields in the header except map_off must be four-byte-aligned.
+
+The map_off field must be either zero or point into the data section. In the
+latter case, the data section must exist.
+
+None of the link, string_ids, type_ids, proto_ids, field_ids, method_ids, class_defs
+and data sections must overlap each other or the header.
+
+If a map exists, then each map entry must have a valid type. Each type may
+appear at most once.
+
+If a map exists, then each map entry must have a nonzero offset and size. The
+offset must point into the corresponding section of the file (i.e. a
+string_id_item must point into the string_ids section) and the explicit or
+implicit size of the item must match the actual contents and size of the
+section.
+
+If a map exists, then the offset of map entry n+1 must be greater or equal to
+the offset of map entry n plus then size of map entry n. This implies
+non-overlapping entries and low-to-high ordering.
+
+The following types of entries must have an offset that is
+four-byte-aligned: string_id_item, type_id_item, proto_id_item, field_id_item,
+method_id_item, class_def_item, type_list, code_item,
+annotations_directory_item.
+
+For each string_id_item, the string_data_off field must contain a valid
+reference into the data section. For the referenced string_data_item, the data
+field must contain a valid MUTF-8 string, and the utf16_size must match the
+decoded length of the string.
+
+For each type_id_item, the desciptor_idx field must contain a valid reference
+into the string_ids list. The referenced string must be a valid type descriptor.
+
+For each proto_id_item, the shorty_idx field must contain a valid reference
+into the string_ids list. The referenced string must be a valid shorty descriptor.
+Also, the return_type_idx field must be a valid index into the type_ids section,
+and the parameters_off field must be either zero or a valid offset pointing
+into the data section. If nonzero, the parameter list must not contain any void
+entries.
+
+For each field_id_item, both the class_idx and type_idx fields must be a valid
+ indices into the
+type_ids list. The entry referenced by class_idx must be a non-array reference type.
+In addition, the name_idx field must be a valid reference into the string_ids
+section, and the contents of the referenced entry must conform to the MemberName
+specification.
+
+For each method_id_item, the class_idx field must be a valid index into the
+type_ids section, and the
+referenced entry must be a non-array reference type. The proto_id field must
+be a valid reference into the proto_ids list. The name_idx field must be a
+valid reference into the string_ids
+section, and the contents of the referenced entry must conform to the MemberName
+specification.
+
+For each class_def_item, ...
+
+For each field_id_item, the class_idx field must be a valid index into the
+type_ids list. The referenced entry must be a non-array reference type.
+
+...
+
+-->
+
+ <h2>
+ Static constraints
+ </h2>
+
+ <p>
+ Static constraints are constraints on individual elements of the bytecode.
+ They usually can be checked without employing control or data-flow analysis
+ techniques.
+ </p>
+
+ <table>
+ <tr>
+ <th>
+ Identifier
+ </th>
+
+ <th>
+ Description
+ </th>
+
+ <th>
+ Spec equivalent
+ </th>
+ </tr>
+
+ <tr>
+ <td>
+ A1
+ </td>
+
+ <td>
+ The <code>insns</code> array must not be empty.
+ </td>
+
+ <td>
+ 4.8.1.1
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A2
+ </td>
+
+ <td>
+ The first opcode in the <code>insns</code> array must have index zero.
+ </td>
+
+ <td>
+ 4.8.1.3
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A3
+ </td>
+
+ <td>
+ The <code>insns</code> array must only contain valid Dalvik opcodes.
+ </td>
+
+ <td>
+ 4.8.1.4
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A4
+ </td>
+
+ <td>
+ The index of instruction <code>n+1</code> must equal the index of
+ instruction <code>n</code> plus the length of instruction
+ <code>n</code>, taking into account possible operands.
+ </td>
+
+ <td>
+ 4.8.1.5
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A5
+ </td>
+
+ <td>
+ The last instruction in the <code>insns</code> array must end at index
+ <code>insns_size-1</code>.
+ </td>
+
+ <td>
+ 4.8.1.6
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A6
+ </td>
+
+ <td>
+ All <code>goto</code> and <code>if-&lt;kind&gt;</code> targets must
+ be opcodes within in the same method.
+ </td>
+
+ <td>
+ 4.8.1.7
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A7
+ </td>
+
+ <td>
+ All targets of a <code>packed-switch</code> instruction must be
+ opcodes within in the same method. The size and the list of targets
+ must be consistent.
+ </td>
+
+ <td>
+ 4.8.1.8
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A8
+ </td>
+
+ <td>
+ All targets of a <code>sparse-switch</code> instruction must be
+ opcodes within in the same method. The corresponding table must be
+ consistent and sorted low-to-high.
+ </td>
+
+ <td>
+ 4.8.1.9
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A9
+ </td>
+
+ <td>
+ The <code>B</code> operand of the <code>const-string</code> and
+ <code>const-string/jumbo</code> instructions must be a valid index
+ into the string constant pool.
+ </td>
+
+ <td>
+ 4.8.1.10
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A10
+ </td>
+
+ <td>
+ The <code>C</code> operand of the <code>iget&lt;kind&gt;</code> and
+ <code>iput&lt;kind&gt;</code> instructions must be a valid index into
+ the field constant pool. The referenced entry must represent an
+ instance field.
+ </td>
+
+ <td>
+ 4.8.1.12
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A11
+ </td>
+
+ <td>
+ The <code>C</code> operand of the <code>sget&lt;kind&gt;</code> and
+ <code>sput&lt;kind&gt;</code> instructions must be a valid index into
+ the field constant pool. The referenced entry must represent a static
+ field.
+ </td>
+
+ <td>
+ 4.8.1.12
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A12
+ </td>
+
+ <td>
+ The <code>C</code> operand of the <code>invoke-virtual</code>,
+ <code>invoke-super</code>, <code<invoke-direct</code> and
+ <code>invoke-static</code> instructions must be a valid index into the
+ method constant pool. In all cases, the referenced
+ <code>method_id</code> must belong to a class (not an interface).
+ </td>
+
+ <td>
+ 4.8.1.13
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A13
+ </td>
+
+ <td>
+ The <code>B</code> operand of the <code>invoke-virtual/range</code>,
+ <code>invoke-super/range</code>, <code>invoke-direct/range</code>, and
+ <code>invoke-static/range</code> instructions must be a valid index
+ into the method constant pool. In all cases, the referenced
+ <code>method_id</code> must belong to a class (not an interface).
+ </td>
+
+ <td>
+ 4.8.1.13
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A14
+ </td>
+
+ <td>
+ A method the name of which starts with a '<' must only be invoked
+ implicitly by the VM, not by code originating from a Dex file. The
+ only exception is the instance initializer, which may be invoked by
+ <code>invoke-direct</code>.
+ </td>
+
+ <td>
+ 4.8.1.14
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A15
+ </td>
+
+ <td>
+ The <code>C</code> operand of the <code>invoke-interface</code>
+ instruction must be a valid index into the method constant pool. The
+ referenced <code>method_id</code> must belong to an interface (not a
+ class).
+ </td>
+
+ <td>
+ 4.8.1.15
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A16
+ </td>
+
+ <td>
+ The <code>B</code> operand of the <code>invoke-interface/range</code>
+ instruction must be a valid index into the method constant pool.
+ The referenced <code>method_id</code> must belong to an interface (not
+ a class).
+ </td>
+
+ <td>
+ 4.8.1.15
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A17
+ </td>
+
+ <td>
+ The <code>B</code> operand of the <code>const-class</code>,
+ <code>check-cast</code>, <code>new-instance</code>, and
+ <code>filled-new-array/range</code> instructions must be a valid index
+ into the type constant pool.
+ </td>
+
+ <td>
+ 4.8.1.16
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A18
+ </td>
+
+ <td>
+ The <code>C</code> operand of the <code>instance-of</code>,
+ <code>new-array</code>, and <code>filled-new-array</code>
+ instructions must be a valid index into the type constant pool.
+ </td>
+
+ <td>
+ 4.8.1.16
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A19
+ </td>
+
+ <td>
+ The dimensions of an array created by a <code>new-array</code>
+ instruction must be less than <code>256</code>.
+ </td>
+
+ <td>
+ 4.8.1.17
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A20
+ </td>
+
+ <td>
+ The <code>new</code> instruction must not refer to array classes,
+ interfaces, or abstract classes.
+ </td>
+
+ <td>
+ 4.8.1.18
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A21
+ </td>
+
+ <td>
+ The type referred to by a <code>new-array</code> instruction must be
+ a valid, non-reference type.
+ </td>
+
+ <td>
+ 4.8.1.20
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A22
+ </td>
+
+ <td>
+ All registers referred to by an instruction in a single-width
+ (non-pair) fashion must be valid for the current method. That is,
+ their indices must be non-negative and smaller than
+ <code>registers_size</code>.
+ </td>
+
+ <td>
+ 4.8.1.21
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A23
+ </td>
+
+ <td>
+ All registers referred to by an instruction in a double-width (pair)
+ fashion must be valid for the current method. That is, their indices
+ must be non-negative and smaller than <code>registers_size-1</code>.
+ </td>
+
+ <td>
+ 4.8.1.23
+ </td>
+ </tr>
+ </table>
+
+ <h2>
+ Structural constraints
+ </h2>
+
+ <p>
+ Structural constraints are constraints on relationships between several
+ elements of the bytecode. They usually can't be checked without employing
+ control or data-flow analysis techniques.
+ </p>
+
+ <table>
+ <tr>
+ <th>
+ Identifier
+ </th>
+
+ <th>
+ Description
+ </th>
+
+ <th>
+ Spec equivalent
+ </th>
+ </tr>
+
+ <tr>
+ <td>
+ B1
+ </td>
+
+ <td>
+ The number and types of arguments (registers and immediate values)
+ must always match the instruction.
+ </td>
+
+ <td>
+ 4.8.2.1
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B2
+ </td>
+
+ <td>
+ Register pairs must never be broken up.
+ </td>
+
+ <td>
+ 4.8.2.3
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B3
+ </td>
+
+ <td>
+ A register (or pair) has to be assigned first before it can be
+ read.
+ </td>
+
+ <td>
+ 4.8.2.4
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B4
+ </td>
+
+ <td>
+ An <code>invoke-direct</code> instruction must only invoke an instance
+ initializer or a method in the current class or one of its
+ superclasses.
+ </td>
+
+ <td>
+ 4.8.2.7
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B5
+ </td>
+
+ <td>
+ An instance initializer must only be invoked on an uninitialized
+ instance.
+ </td>
+
+ <td>
+ 4.8.2.8
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B6
+ </td>
+
+ <td>
+ Instance methods may only be invoked on and instance fields may only
+ be accessed on already initialized instances.
+ </td>
+
+ <td>
+ 4.8.2.9
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B7
+ </td>
+
+ <td>
+ A register which holds the result of a <code>new-instance</code>code>
+ instruction must not be used if the same
+ <code>new-instance</code>code> instruction is again executed before
+ the instance is initialized.
+ </td>
+
+ <td>
+ 4.8.2.10
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B8
+ </td>
+
+ <td>
+ An instance initializer must call another instance initializer (same
+ class or superclass) before any instance members can be accessed.
+ Exceptions are non-inherited instance fields, which can be assigned
+ before calling another initializer, and the <code>Object</code> class
+ in general.
+ </td>
+
+ <td>
+ 4.8.2.11
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B9
+ </td>
+
+ <td>
+ All actual method arguments must be assignment-compatible with their
+ respective formal arguments.
+ </td>
+
+ <td>
+ 4.8.2.12
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B10
+ </td>
+
+ <td>
+ For each instance method invocation, the actual instance must be
+ assignment-compatible with the class or interface specified in the
+ instruction.
+ </td>
+
+ <td>
+ 4.8.2.13
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B11
+ </td>
+
+ <td>
+ A <code>return&lt;kind&gt;</code> instruction must match its
+ method's return type.
+ </td>
+
+ <td>
+ 4.8.2.14
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B12
+ </td>
+
+ <td>
+ When accessing protected members of a superclass, the actual type of
+ the instance being accessed must be either the current class or one
+ of its subclasses.
+ </td>
+
+ <td>
+ 4.8.2.15
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B13
+ </td>
+
+ <td>
+ The type of a value stored into a static field must be
+ assignment-compatible with or convertible to the field's type.
+ </td>
+
+ <td>
+ 4.8.2.16
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B14
+ </td>
+
+ <td>
+ The type of a value stored into a field must be assignment-compatible
+ with or convertible to the field's type.
+ </td>
+
+ <td>
+ 4.8.2.17
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B15
+ </td>
+
+ <td>
+ The type of every value stored into an array must be
+ assignment-compatible with the array's component type.
+ </td>
+
+ <td>
+ 4.8.2.18
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B16
+ </td>
+
+ <td>
+ The <code>A</code> operand of a <code>throw</code> instruction must
+ be assignment-compatible with <code>java.lang.Throwable</code>.
+ </td>
+
+ <td>
+ 4.8.2.19
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B17
+ </td>
+
+ <td>
+ The last reachable instruction of a method must either be a backwards
+ <code>goto</code> or branch, a <code>return</code>, or a
+ <code>throw</code> instruction. It must not be possible to leave the
+ <code>insns</code> array at the bottom.
+ </td>
+
+ <td>
+ 4.8.2.20
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B18
+ </td>
+
+ <td>
+ The unassigned half of a former register pair may not be read (is
+ considered invalid) until it has been re-assigned by some other
+ instruction.
+ </td>
+
+ <td>
+ 4.8.2.3, 4.8.2.4
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B19
+ </td>
+
+ <td>
+ A <code>move-result&lt;kind&gt;</code> instruction must be immediately
+ preceded (in the <code>insns</code> array) by an
+ <code>&lt;invoke-kind&gt;</code> instruction. The only exception is
+ the <code>move-result-object</code> instruction, which may also be
+ preceded by a <code>filled-new-array</code> instruction.
+ </td>
+
+ <td>
+ -
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B20
+ </td>
+
+ <td>
+ A <code>move-result&lt;kind&gt;</code> instruction must be immediately
+ preceded (in actual control flow) by a matching
+ <code>return-&lt;kind&gt;</code> instruction (it must not be jumped
+ to). The only exception is the <code>move-result-object</code>
+ instruction, which may also be preceded by a
+ <code>filled-new-array</code> instruction.
+ </td>
+
+ <td>
+ -
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B21
+ </td>
+
+ <td>
+ A <code>move-exception</code> instruction must only appear as the
+ first instruction in an exception handler.
+ </td>
+
+ <td>
+ -
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B22
+ </td>
+
+ <td>
+ The <code>packed-switch-data</code>, <code>sparse-switch-data</code>,
+ and <code>fill-array-data</code> pseudo-instructions must not be
+ reachable by control flow.
+ </td>
+
+ <td>
+ -
+ </td>
+ </tr>
+ </table>
+
+ </body>
+</html>
diff --git a/docs/debugger.html b/docs/debugger.html
new file mode 100644
index 0000000..1c47c7a
--- /dev/null
+++ b/docs/debugger.html
@@ -0,0 +1,243 @@
+<html>
+<head>
+<title>Dalvik Debugger Support</title>
+</head>
+
+<body>
+<h1>Dalvik Debugger Support</h1>
+
+<p>
+The Dalvik virtual machine supports source-level debugging with many popular
+development environments. Any tool that allows remote debugging over JDWP
+(the
+<a href="http://java.sun.com/javase/6/docs/technotes/guides/jpda/jdwp-spec.html">
+Java Debug Wire Protocol</a>) is expected work. Supported debuggers
+include jdb, Eclipse, IntelliJ, and JSwat.
+</p><p>
+The VM does not support tools based on JVMTI (Java Virtual
+Machine Tool Interface). This is a relatively intrusive approach that
+relies on bytecode insertion, something the Dalvik VM does not currently
+support.
+</p><p>
+Dalvik's implementation of JDWP also includes hooks for supporting
+DDM (Dalvik Debug Monitor) features, notably as implemented by DDMS
+(Dalvik Debug Monitor Server) and the Eclipse ADT plugin. The protocol
+and VM interaction is described in some detail
+<a href="debugmon.html">here</a>.
+</p><p>
+All of the debugger support in the VM lives in the <code>dalvik/vm/jdwp</code>
+directory, and is almost entirely isolated from the rest of the VM sources.
+<code>dalvik/vm/Debugger.c</code> bridges the gap. The goal in doing so
+was to make it easier to re-use the JDWP code in other projects.
+</p><p>
+
+
+<h2>Implementation</h2>
+
+<p>
+Every VM that has debugging enabled starts a "JDWP" thread. The thread
+typically sits idle until DDMS or a debugger connects. The thread is
+only responsible for handling requests from the debugger; VM-initated
+communication, such as notifying the debugger when the VM has stopped at
+a breakpoint, are sent from the affected thread.
+</p><p>
+When the VM is started from the Android app framework, debugging is enabled
+for all applications when the system property <code>ro.debuggable</code>
+is set to </code>1</code> (use <code>adb shell getprop ro.debuggable</code>
+to check it). If it's zero, debugging can be enabled via the application's
+manifest, which must include <code>android:debuggable="true"</code> in the
+<code>&lt;application&gt;</code> element.
+
+</p><p>
+The VM recognizes the difference between a connection from DDMS and a
+connection from a debugger (either directly or in concert with DDMS).
+A connection from DDMS alone doesn't result in a change in VM behavior,
+but when the VM sees debugger packets it allocates additional data
+structures and may switch to a different implementation of the interpreter.
+</p><p>
+Pre-Froyo implementations of the Dalvik VM used read-only memory mappings
+for all bytecode, which made it necessary to scan for breakpoints by
+comparing the program counter to a set of addresses. In Froyo this was
+changed to allow insertion of breakpoint opcodes. This allows the VM
+to execute code more quickly, and does away with the hardcoded limit
+of 20 breakpoints. Even with this change, however, the debug-enabled
+interpreter is much slower than the regular interpreter (perhaps 5x).
+</p><p>
+The JDWP protocol is stateless, so the VM handles individual debugger
+requests as they arrive, and posts events to the debugger as they happen.
+</p><p>
+
+
+<h2>Debug Data</h2>
+<p> Source code debug data, which includes mappings of source code to
+bytecode and lists describing which registers are used to hold method
+arguments and local variables, are optionally emitted by the Java compiler.
+When <code>dx</code> converts Java bytecode to Dalvik bytecode, it must
+also convert this debug data.
+</p><p>
+<code>dx</code> must also ensure that it doesn't perform operations
+that confuse the debugger. For example, re-using registers that hold
+method arguments and the "<code>this</code>" pointer is allowed in
+Dalvik bytecode if the values are never used or no longer needed.
+This can be very confusing for the debugger (and the programmer)
+since the values have method scope and aren't expected to disappear. For
+this reason, <code>dx</code> generates sub-optimal code in some situations
+when debugging support is enabled.
+</p><p>
+Some of the debug data is used for other purposes; in particular, having
+filename and line number data is necessary for generating useful exception
+stack traces. This data can be omitted by <code>dx</code> to make the DEX
+file smaller.
+</p><p>
+
+
+<h2>Usage</h2>
+
+<p>
+The Dalvik VM supports many of the same command-line flags that other popular
+desktop VMs do. To start a VM with debugging enabled, you add a command-line
+flag with some basic options. The basic incantation looks something
+like this:
+
+<pre>-Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=y</pre>
+or
+<pre>-agentlib:jdwp=transport=dt_socket,address=8000,server=y,suspend=y</pre>
+
+</p><p>
+After the initial prefix, options are provided as name=value pairs. The
+options currently supported by the Dalvik VM are:
+<dl>
+ <dt>transport (no default)</dt>
+ <dd>Communication transport mechanism to use. Dalvik supports
+ TCP/IP sockets (<code>dt_socket</code>) and connection over USB
+ through ADB (<code>dt_android_adb</code>).
+ </dd>
+
+ <dt>server (default='n')</dt>
+ <dd>Determines whether the VM acts as a client or a server. When
+ acting as a server, the VM waits for a debugger to connect to it.
+ When acting as a client, the VM attempts to connect to a waiting
+ debugger.
+ </dd>
+
+ <dt>suspend (default='n')</dt>
+ <dd>If set to 'y', the VM will wait for a debugger connection
+ before executing application code. When the debugger connects (or
+ when the VM finishes connecting to the debugger), the VM tells the
+ debugger that it has suspended, and will not proceed until told
+ to resume. If set to 'n', the VM just plows ahead.
+ </dd>
+
+ <dt>address (default="")</dt>
+ <dd>This must be <code>hostname:port</code> when <code>server=n</code>,
+ but can be just <code>port</code> when <code>server=y</code>. This
+ specifies the IP address and port number to connect or listen to.
+ <br>
+ Listening on port 0 has a special meaning: try to
+ listen on port 8000; if that fails, try 8001, 8002, and so on. (This
+ behavior is non-standard and may be removed from a future release.)
+ <br>This option has no meaning for <code>transport=dt_android_adb</code>.
+ </dd>
+
+ <dt>help (no arguments)</dt>
+ <dd>If this is the only option, a brief usage message is displayed.
+ </dd>
+
+ <dt>launch, onthrow, oncaught, timeout</dt>
+ <dd>These options are accepted but ignored.
+ </dd>
+</dl>
+
+</p><p>
+To debug a program on an Android device using DDMS over USB, you could
+use a command like this:
+<pre>% dalvikvm -agentlib:jdwp=transport=dt_android_adb,suspend=y,server=y -cp /data/foo.jar Foo</pre>
+
+This tells the Dalvik VM to run the program with debugging enabled, listening
+for a connection from DDMS, and waiting for a debugger. The program will show
+up with an app name of "?" in the process list, because it wasn't started
+from the Android application framework. From here you would connect your
+debugger to the appropriate DDMS listen port (e.g.
+<code>jdb -attach localhost:8700</code> after selecting it in the app list).
+
+</p><p>
+To debug a program on an Android device using TCP/IP bridged across ADB,
+you would first need to set up forwarding:
+<pre>% adb forward tcp:8000 tcp:8000
+% adb shell dalvikvm -agentlib:jdwp=transport=dt_socket,address=8000,suspend=y,server=y -cp /data/foo.jar Foo</pre>
+and then <code>jdb -attach localhost:8000</code>.
+</p><p>
+(In the above examples, the VM will be suspended when you attach. In jdb,
+type <code>cont</code> to continue.)
+</p><p>
+The DDMS integration makes the <code>dt_android_adb</code> transport much
+more convenient when debugging on an Android device, but when working with
+Dalvik on the desktop it makes sense to use the TCP/IP transport.
+</p><p>
+
+
+<h2>Known Issues and Limitations</h2>
+
+</p><p>
+Most of the optional features JDWP allows are not implemented. These
+include field access watchpoints and better tracking of monitors.
+</p><p>
+Not all JDWP requests are implemented. In particular, anything that
+never gets emitted by the debuggers we've used is not supported and will
+result in error messages being logged. Support will be added when a
+use case is uncovered.
+</p><p>
+&nbsp;
+</p><p>
+The debugger and garbage collector are somewhat loosely
+integrated at present. The VM currently guarantees that any object the
+debugger is aware of will not be garbage collected until after the
+debugger disconnects. This can result in a build-up over time while the
+debugger is connected. For example, if the debugger sees a running
+thread, the associated Thread object will not be collected, even after
+the thread terminates.
+</p><p>
+The only way to "unlock" the references is to detach and reattach the
+debugger.
+</p><p>
+&nbsp;
+</p><p>
+The translation from Java bytecode to Dalvik bytecode may result in
+identical sequences of instructions being combined. This can make it
+look like the wrong bit of code is being executed. For example:
+<pre> int test(int i) {
+ if (i == 1) {
+ return 0;
+ }
+ return 1;
+ }</pre>
+The Dalvik bytecode uses a common <code>return</code> instruction for both
+<code>return</code> statements, so when <code>i</code> is 1 the debugger
+will single-step through <code>return 0</code> and then <code>return 1</code>.
+</p><p>
+&nbsp;
+</p><p>
+Dalvik handles synchronized methods differently from other VMs.
+Instead of marking a method as <code>synchronized</code> and expecting
+the VM to handle the locks, <code>dx</code> inserts a "lock"
+instruction at the top of the method and an "unlock" instruction in a
+synthetic <code>finally</code> block. As a result, when single-stepping
+a <code>return</code> statement, the "current line" cursor may jump to
+the last line in the method.
+</p><p>
+This can also affect the way the debugger processes exceptions. The
+debugger may decide to break on an
+exception based on whether that exception is "caught" or "uncaught". To
+be considered uncaught, there must be no matching <code>catch</code> block
+or <code>finally</code> clause between the current point of execution and
+the top of the thread. An exception thrown within or below a synchronized
+method will always be considered "caught", so the debugger won't stop
+until the exception is re-thrown from the synthetic <code>finally</code> block.
+</p><p>
+
+
+<address>Copyright &copy; 2009 The Android Open Source Project</address>
+</p>
+
+</body>
+</html>
diff --git a/docs/debugmon.html b/docs/debugmon.html
new file mode 100644
index 0000000..cf56ef5
--- /dev/null
+++ b/docs/debugmon.html
@@ -0,0 +1,736 @@
+<HTML>
+
+
+<head>
+ <title>Dalvik VM Debug Monitor</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <link href="http://www.google.com/favicon.ico" type="image/x-icon"
+ rel="shortcut icon">
+ <link href="../android.css" type="text/css" rel="stylesheet">
+ <script language="JavaScript1.2" type="text/javascript">
+function highlight(name) {
+ if (document.getElementById) {
+ tags = [ 'span', 'div', 'tr', 'td' ];
+ for (i in tags) {
+ elements = document.getElementsByTagName(tags[i]);
+ if (elements) {
+ for (j = 0; j < elements.length; j++) {
+ elementName = elements[j].getAttribute("id");
+ if (elementName == name) {
+ elements[j].style.backgroundColor = "#C0F0C0";
+ } else if (elementName && elementName.indexOf("rev") == 0) {
+ elements[j].style.backgroundColor = "#FFFFFF";
+ }
+ }
+ }
+ }
+ }
+}
+ </script>
+</head>
+<body onload="prettyPrint()">
+
+<h1><a name="My_Project_"></a>Dalvik VM<br>Debug Monitor</h1>
+
+<!-- Status is one of: Draft, Current, Needs Update, Obsolete -->
+<p style="text-align:center"><strong>Status:</strong><em>Draft</em> &nbsp;
+<small>(as of March 6, 2007)</small></p>
+<address>
+[authors]
+<address>
+
+<!-- last modified date can be different to the "Status date." It automatically
+updates
+whenever the file is modified. -->
+<i>Modified:</i>
+ <!-- this script automatically sets the modified date,you don't need to modify
+it -->
+ <script type=text/javascript>
+ <!--
+ var lm = new Date(document.lastModified);
+ document.write(lm.toDateString());
+ //-->
+ </script>
+</address>
+
+<p><br>
+<HR>
+
+<h2>Introduction</h2>
+
+<p>It's extremely useful to be able to monitor the live state of the
+VM. For Android, we need to monitor multiple VMs running on a device
+connected through USB or a wireless network connection. This document
+describes a debug monitor server that interacts with multiple VMs, and
+an API that VMs and applications can use to provide information
+to the monitor.
+
+<p>Some things we can monitor with the Dalvik Debug Monitor ("DDM"):
+<ul>
+ <li> Thread states. Track thread creation/exit, busy/idle status.
+ <li> Overall heap status, useful for a heap bitmap display or
+ fragmentation analysis.
+</ul>
+
+<p>It is possible for something other than a VM to act as a DDM client, but
+that is a secondary goal. Examples include "logcat" log extraction
+and system monitors for virtual memory usage and load average.
+
+<p>It's also possible for the DDM server to be run on the device, with
+the information presented through the device UI. However, the initial goal
+is to provide a display tool that takes advantage of desktop tools and
+screen real estate.
+
+<p>This work is necessary because we are unable to use standard JVMTI-based
+tools with Dalvik. JVMTI relies on bytecode insertion, which is not
+currently possible because Dalvik doesn't support Java bytecode.
+
+<p>The DDM server is written in the Java programming language
+for portability. It uses a desktop
+UI toolkit (SWT) for its interface.
+
+
+<h2>Protocol</h2>
+
+<p>To take advantage of existing infrastructure we are piggy-backing the
+DDM protocol on top of JDWP (the Java Debug Wire Protocol, normally spoken
+between a VM and a debugger). To a
+non-DDM client, the DDM server just looks like a debugger.
+
+<p>The JDWP protocol is very close to what we want to use. In particular:
+<ul>
+ <li>It explicitly allows for vendor-defined packets, so there is no
+ need to "bend" the JDWP spec.
+ <li>Events may be posted from the VM at arbitrary points. Such
+ events do not elicit a response from the debugger, meaning the client
+ can post data and immediately resume work without worrying about the
+ eventual response.
+ <li>The basic protocol is stateless and asynchronous. Request packets
+ from the debugger side include a serial number, which the VM includes
+ in the response packet. This allows multiple simultaneous
+ conversations, which means the DDM traffic can be interleaved with
+ debugger traffic.
+</ul>
+
+<p>There are a few issues with using JDWP for our purposes:
+<ul>
+ <li>The VM only expects one connection from a debugger, so you couldn't
+ attach the monitor and a debugger at the same time. This will be
+ worked around by connecting the debugger to the monitor and passing the
+ traffic through. (We're already doing the pass-through with "jdwpspy";
+ requires some management of our request IDs though.) This should
+ be more convenient than the current "guess the port
+ number" system when we're attached to a device.
+ <li>The VM behaves differently when a debugger is attached. It will
+ run more slowly, and any objects passed to the monitor or debugger are
+ immune to GC. We can work around this by not enabling the slow path
+ until non-DDM traffic is observed. We also want to have a "debugger
+ has connected/disconnected" message that allows the VM to release
+ debugger-related resources without dropping the net connection.
+ <li>Non-DDM VMs should not freak out when DDM connects. There are
+ no guarantees here for 3rd-party VMs (e.g. a certain mainstream VM,
+ which crashes instantly), but our older JamVM can be
+ configured to reject the "hello" packet.
+</ul>
+
+
+<h3>Connection Establishment</h3>
+
+<p>There are two basic approaches: have the server contact the VMs, and
+have the VMs contact the server. The former is less "precise" than the
+latter, because you have to scan for the clients, but it has some
+advantages.
+
+<p>There are three interesting scenarios:
+<ol>
+ <li>The DDM server is started, then the USB-attached device is booted
+ or the simulator is launched.
+ <li>The device or simulator is already running when the DDM server
+ is started.
+ <li>The DDM server is running when an already-started device is
+ attached to USB.
+</ol>
+<p>If we have the VMs connect to the DDM server on startup, we only handle
+case #1. If the DDM server scans for VMs when it starts, we only handle
+case #2. Neither handles case #3, which is probably the most important
+of the bunch as the device matures.
+<p>The plan is to have a drop-down menu with two entries,
+"scan workstation" and "scan device".
+The former causes the DDM server to search for VMs on "localhost", the
+latter causes it to search for VMs on the other side of an ADB connection.
+The DDM server will scan for VMs every few seconds, either checking a
+range of known VM ports (e.g. 8000-8040) or interacting with some sort
+of process database on the device. Changing modes causes all existing
+connections to be dropped.
+<p>When the DDM server first starts, it will try to execute "adb usb"
+to ensure that the ADB server is running. (Note it will be necessary
+to launch the DDM server from a shell with "adb" in the path.) If this
+fails, talking to the device will still be possible so long as the ADB
+daemon is already running.
+
+<h4>Connecting a Debugger</h4>
+
+<p>With the DDM server sitting on the JDWP port of all VMs, it will be
+necessary to connect the debugger through the DDM server. Each VM being
+debugged will have a separate port being listened to by the DDM server,
+allowing you to connect a debugger to one or more VMs simultaneously.
+
+<p>In the common case, however, the developer will only want to debug
+a single VM. One port (say 8700) will be listened to by the DDM server,
+and anything connecting to it will be connected to the "current VM"
+(selected in the UI). This should allow developers to focus on a
+single application, which may otherwise shift around in the ordering, without
+having to adjust their IDE settings to a different port every time they
+restart the device.
+
+
+<h3>Packet Format</h3>
+
+<p>Information is sent in chunks. Each chunk starts with:
+<pre>
+u4 type
+u4 length
+</pre>
+and contains a variable amount of type-specific data.
+Unrecognized types cause an empty response from the client and
+are quietly ignored by the server. [Should probably return an error;
+need an "error" chunk type and a handler on the server side.]
+
+<p>The same chunk type may have different meanings when sent in different
+directions. For example, the same type may be used for both a query and
+a response to the query. For sanity the type must always be used in
+related transactions.
+
+<p>This is somewhat redundant with the JDWP framing, which includes a
+4-byte length and a two-byte type code ("command set" and "command"; a
+range of command set values is designated for "vendor-defined commands
+and extensions"). Using the chunk format allows us to remain independent
+of the underlying transport, avoids intrusive integration
+with JDWP client code, and provides a way to send multiple chunks in a
+single transmission unit. [I'm taking the multi-chunk packets into
+account in the design, but do not plan to implement them unless the need
+arises.]
+
+<p>Because we may be sending data over a slow USB link, the chunks may be
+compressed. Compressed chunks are written as a chunk type that
+indicates the compression, followed by the compressed length, followed
+by the original chunk type and the uncompressed length. For zlib's deflate
+algorithm, the chunk type is "ZLIB".
+
+<p>Following the JDWP model, packets sent from the server to the client
+are always acknowledged, but packets sent from client to server never are.
+The JDWP error code field is always set to "no error"; failure responses
+from specific requests must be encoded into the DDM messages.
+
+<p>In what follows "u4" is an unsigned 32-bit value and "u1" is an
+unsigned 8-bit value. Values are written in big-endian order to match
+JDWP.
+
+
+<h3>Initial Handshake</h3>
+
+<p>After the JDWP handshake, the server sends a HELO chunk to the client.
+If the client's JDWP layer rejects it, the server assumes that the client
+is not a DDM-aware VM, and does not send it any further DDM queries.
+<p>On the client side, upon seeing a HELO it can know that a DDM server
+is attached and prepare accordingly. The VM should not assume that a
+debugger is attached until a non-DDM packet arrives.
+
+<h4>Chunk HELO (server --&gt; client)</h4>
+<p>Basic "hello" message.
+<pre>
+u4 DDM server protocol version
+</pre>
+
+
+<h4>Chunk HELO (client --&gt; server, reply only)</h4>
+Information about the client. Must be sent in response to the HELO message.
+<pre>
+u4 DDM client protocol version
+u4 pid
+u4 VM ident string len (in 16-bit units)
+u4 application name len (in 16-bit units)
+var VM ident string (UTF-16)
+var application name (UTF-16)
+</pre>
+
+<p>If the client does not wish to speak to the DDM server, it should respond
+with a JDWP error packet. This is the same behavior you'd get from a VM
+that doesn't support DDM.
+
+
+<h3>Debugger Management</h3>
+<p>VMs usually prepare for debugging when a JDWP connection is established,
+and release debugger-related resources when the connection drops. We want
+to open the JDWP connection early and hold it open after the debugger
+disconnects.
+<p>The VM can tell when a debugger attaches, because it will start seeing
+non-DDM JDWP traffic, but it can't identify the disconnect. For this reason,
+we need to send a packet to the client when the debugger disconnects.
+<p>If the DDM server is talking to a non-DDM-aware client, it will be
+necessary to drop and re-establish the connection when the debugger goes away.
+(This also works with DDM-aware clients; this packet is an optimization.)
+
+<h4>Chunk DBGD (server --&gt; client)</h4>
+<p>Debugger has disconnected. The client responds with a DBGD to acknowledge
+receipt. No data in request, no response required.
+
+
+<h3>VM Info</h3>
+<p>Update the server's info about the client.
+
+<h4>Chunk APNM (client --&gt; server)</h4>
+
+<p>If a VM's application name changes -- possible in our environment because
+of the "pre-initialized" app processes -- it must send up one of these.
+<pre>
+u4 application name len (in 16-bit chars)
+var application name (UTF-16)
+</pre>
+
+<h4>Chunk WAIT (client --&gt; server)</h4>
+
+<p>This tells DDMS that one or more threads are waiting on an external
+event. The simplest use is to tell DDMS that the VM is waiting for a
+debugger to attach.
+<pre>
+u1 reason (0 = wait for debugger)
+</pre>
+If DDMS is attached, the client VM sends this up when waitForDebugger()
+is called. If waitForDebugger() is called before DDMS attaches, the WAIT
+chunk will be sent up at about the same time as the HELO response.
+
+
+<h3>Thread Status</h3>
+
+<p>The client can send updates when their status changes, or periodically
+send thread state info, e.g. 2x per
+second to allow a "blinkenlights" display of thread activity.
+
+<h4>Chunk THEN (server --&gt; client)</h4>
+
+<p>Enable thread creation/death notification.
+<pre>
+u1 boolean (true=enable, false=disable)
+</pre>
+<p>The response is empty. The client generates THCR packets for all
+known threads. (Note the THCR packets may arrive before the THEN
+response.)
+
+<h4>Chunk THCR (client --&gt; server)</h4>
+<p>Thread Creation notification.
+<pre>
+u4 VM-local thread ID (usually a small int)
+u4 thread name len (in 16-bit chars)
+var thread name (UTF-16)
+</pre>
+
+<h4>Chunk THDE (client --&gt; server)</h4>
+<p>Thread Death notification.
+<pre>
+u4 VM-local thread ID
+</pre>
+
+<h4>Chunk THST (server --&gt; client)</h4>
+
+<p>Enable periodic thread activity updates.
+Threads in THCR messages are assumed to be in the "initializing" state. A
+THST message should follow closely on the heels of THCR.
+<pre>
+u4 interval, in msec
+</pre>
+<p>An interval of 0 disables the updates. This is done periodically,
+rather than every time the thread state changes, to reduce the amount
+of data that must be sent for an actively running VM.
+
+<h4>Chunk THST (client --&gt; server)</h4>
+<p>Thread Status, describing the state of one or more threads. This is
+most useful when creation/death notifications are enabled first. The
+overall layout is:
+<pre>
+u4 count
+var thread data
+</pre>
+Then, for every thread:
+<pre>
+u4 VM-local thread ID
+u1 thread state
+u1 suspended
+</pre>
+<p>"thread state" must be one of:
+<ul> <!-- don't use ol, we may need (-1) or sparse -->
+ <li> 1 - running (now executing or ready to do so)
+ <li> 2 - sleeping (in Thread.sleep())
+ <li> 3 - monitor (blocked on a monitor lock)
+ <li> 4 - waiting (in Object.wait())
+ <li> 5 - initializing
+ <li> 6 - starting
+ <li> 7 - native (executing native code)
+ <li> 8 - vmwait (waiting on a VM resource)
+</ul>
+<p>"suspended" will be 0 if the thread is running, 1 if not.
+<p>[Any reason not to make "suspended" be the high bit of "thread state"?
+Do we need to differentiate suspend-by-GC from suspend-by-debugger?]
+<p>[We might be able to send the currently-executing method. This is a
+little risky in a running VM, and increases the size of the messages
+considerably, but might be handy.]
+
+
+<h3>Heap Status</h3>
+
+<p>The client sends what amounts to a color-coded bitmap to the server,
+indicating which stretches of memory are free and which are in use. For
+compactness the bitmap is run-length encoded, and based on multi-byte
+"allocation units" rather than byte counts.
+
+<p>In the future the server will be able to correlate the bitmap with more
+detailed object data, so enough information is provided to associate the
+bitmap data with virtual addresses.
+
+<p>Heaps may be broken into segments within the VM, and due to memory
+constraints it may be desirable to send the bitmap in smaller pieces,
+so the protocol allows the heap data to be sent in several chunks.
+To avoid ambiguity, the client is required
+to send explicit "start" and "end" messages during an update.
+
+<p>All messages include a "heap ID" that can be used to differentiate
+between multiple independent virtual heaps or perhaps a native heap. The
+client is allowed to send information about different heaps simultaneously,
+so all heap-specific information is tagged with a "heap ID".
+
+<h4>Chunk HPIF (server --&gt; client)</h4>
+<p>Request heap info.
+<pre>
+u1 when to send
+</pre>
+<p>The "when" values are:
+<pre>
+0: never
+1: immediately
+2: at the next GC
+3: at every GC
+</pre>
+
+<h4>Chunk HPIF (client --&gt; server, reply only)</h4>
+<p>Heap Info. General information about the heap, suitable for a summary
+display.
+<pre>
+u4 number of heaps
+</pre>
+For each heap:
+<pre>
+u4 heap ID
+u8 timestamp in ms since Unix epoch
+u1 capture reason (same as 'when' value from server)
+u4 max heap size in bytes (-Xmx)
+u4 current heap size in bytes
+u4 current number of bytes allocated
+u4 current number of objects allocated
+</pre>
+<p>[We can get some of this from HPSG, more from HPSO.]
+<p>[Do we need a "heap overhead" stat here, indicating how much goes to
+waste? e.g. (8 bytes per object * number of objects)]
+
+<h4>Chunk HPSG (server --&gt; client)</h4>
+<p>Request transmission of heap segment data.
+<pre>
+u1 when to send
+u1 what to send
+</pre>
+<p>The "when" to send will be zero to disable transmission, 1 to send
+during a GC. Other values are currently undefined. (Could use to pick
+which part of the GC to send it, or cause periodic transmissions.)
+<p>The "what" field is currently 0 for HPSG and 1 for HPSO.
+<p>No reply is expected.
+
+<h4>Chunk NHSG (server --&gt; client)</h4>
+<p>Request transmission of native heap segment data.
+<pre>
+u1 when to send
+u1 what to send
+</pre>
+<p>The "when" to send will be zero to disable transmission, 1 to send
+during a GC. Other values are currently undefined.
+<p>The "what" field is currently ignored.
+<p>No reply is expected.
+
+<h4>Chunk HPST/NHST (client --&gt; server)</h4>
+<p>This is a Heap Start message. It tells the server to discard any
+existing notion of what the client's heap looks like, and prepare for
+new information. HPST indicates a virtual heap dump and must be followed
+by zero or more HPSG/HPSO messages and an HPEN. NHST indicates a native
+heap dump and must be followed by zero or more NHSG messages and an NHEN.
+
+<p>The only data item is:
+<pre>
+u4 heap ID
+</pre>
+
+<h4>Chunk HPEN/NHEN (client --&gt; server)</h4>
+<p>Heap End, indicating that all information about the heap has been sent.
+A HPST will be paired with an HPEN and an NHST will be paired with an NHEN.
+
+<p>The only data item is:
+<pre>
+u4 heap ID
+</pre>
+
+<h4>Chunk HPSG (client --&gt; server)</h4>
+<p>Heap segment data. Each chunk describes all or part of a contiguous
+stretch of heap memory.
+<pre>
+u4 heap ID
+u1 size of allocation unit, in bytes (e.g. 8 bytes)
+u4 virtual address of segment start
+u4 offset of this piece (relative to the virtual address)
+u4 length of piece, in allocation units
+var usage data
+</pre>
+<p>The "usage data" indicates the status of each allocation unit. The data
+is a stream of pairs of bytes, where the first byte indicates the state
+of the allocation unit, and the second byte indicates the number of
+consecutive allocation units with the same state.
+<p>The bits in the "state" byte have the following meaning:
+<pre>
++---------------------------------------+
+| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
++---------------------------------------+
+| P | U0 | K2 | K1 | K0 | S2 | S1 | S0 |
++---------------------------------------+
+</pre>
+<ul>
+ <li>'S': solidity
+ <ul>
+ <li>0=free
+ <li>1=has hard reference
+ <li>2=has soft reference
+ <li>3=has weak reference
+ <li>4=has phantom reference
+ <li>5=pending finalization
+ <li>6=marked, about to be swept
+ </ul>
+ <li>'K': kind
+ <ul>
+ <li>0=object
+ <li>1=class object
+ <li>2=array of byte/boolean
+ <li>3=array of char/short
+ <li>4=array of Object/int/float
+ <li>5=array of long/double
+ </ul>
+ <li>'P': partial flag (not used for HPSG)
+ <li>'U': unused, must be zero
+</ul>
+
+<p>The use of the various 'S' types depends on when the information is
+sent. The current plan is to send it either immediately after a GC,
+or between the "mark" and "sweep" phases of the GC. For a fancy generational
+collector, we may just want to send it up periodically.
+
+<p>The run-length byte indicates the number of allocation units minus one, so a
+length of 255 means there are 256 consecutive units with this state. In
+some cases, e.g. arrays of bytes, the actual size of the data is rounded
+up the nearest allocation unit.
+<p>For HPSG, the runs do not end at object boundaries. It is not possible
+to tell from this bitmap whether a run contains one or several objects.
+(But see HPSO, below.)
+<p>[If we find that we have many long runs, we can overload the 'P' flag
+or dedicate the 'U' flag to indicate that we have a 16-bit length instead
+of 8-bit. We can also use a variable-width integer scheme for the length,
+encoding 1-128 in one byte, 1-16384 in two bytes, etc.]
+<p>[Alternate plan for 'K': array of byte, array of char, array of Object,
+array of miscellaneous primitive type]
+<p>To parse the data, the server runs through the usage data until either
+(a) the end of the chunk is reached, or (b) all allocation units have been
+accounted for. (If these two things don't happen at the same time, the
+chunk is rejected.)
+<p>Example: suppose a VM has a heap at 0x10000 that is 0x2000 bytes long
+(with an 8-byte allocation unit size, that's 0x0400 units long).
+The client could send one chunk (allocSize=8, virtAddr=0x10000, offset=0,
+length=0x0400) or two (allocSize=8, virtAddr=0x10000, offset=0, length=0x300;
+then allocSize=8, virtAddr=0x10000, offset=0x300, length=0x100).
+<p>The client must encode the entire heap, including all free space at
+the end, or the server will not have an accurate impression of the amount
+of memory in the heap. This refers to the current heap size, not the
+maximum heap size.
+
+<h4>Chunk HPSO (client --&gt; server)</h4>
+<p>This is essentially identical to HPSG, but the runs are terminated at
+object boundaries. If an object is larger than 256 allocation units, the
+"partial" flag is set in all runs except the last.
+<p>The resulting unpacked bitmap is identical, but the object boundary
+information can be used to gain insights into heap layout.
+<p>[Do we want to have a separate message for this? Maybe just include
+a "variant" flag in the HPST packet. Another possible form of output
+would be one that indicates the age, in generations, of each block of
+memory. That would provide a quick visual indication of "permanent vs.
+transient residents", perhaps with a 16-level grey scale.]
+
+<h4>Chunk NHSG (client --&gt; server)</h4>
+<p>Native heap segment data. Each chunk describes all or part of a
+contiguous stretch of native heap memory. The format is the same as
+for HPSG, except that only solidity values 0 (= free) and 1 (= hard
+reference) are used, and the kind value is always 0 for free chunks
+and 7 for allocated chunks, indicating a non-VM object.
+<pre>
+u4 heap ID
+u1 size of allocation unit, in bytes (e.g. 8 bytes)
+u4 virtual address of segment start
+u4 offset of this piece (relative to the virtual address)
+u4 length of piece, in allocation units
+var usage data
+</pre>
+
+<h3>Generic Replies</h3>
+
+The client-side chunk handlers need a common way to report simple success
+or failure. By convention, an empty reply packet indicates success.
+
+<h4>Chunk FAIL (client --&gt; server, reply only)</h4>
+<p>The chunk includes a machine-readable error code and a
+human-readable error message. Server code can associate the failure
+with the original request by comparing the JDWP packet ID.
+<p>This allows a standard way of, for example, rejecting badly-formed
+request packets.
+<pre>
+u4 error code
+u4 error message len (in 16-bit chars)
+var error message (UTF-16)
+</pre>
+
+<h3>Miscellaneous</h3>
+
+<h4>Chunk EXIT (server --&gt; client)</h4>
+<p>Cause the client to exit with the specified status, using System.exit().
+Useful for certain kinds of testing.
+<pre>
+u4 exit status
+</pre>
+
+<h4>Chunk DTRC (server --&gt; client)</h4>
+<p>[TBD] start/stop dmtrace; can send the results back over the wire. For
+size reasons we probably need "sending", "data", "key", "finished" as
+4 separate chunks/packets rather than one glob.
+
+
+<h2>Client API</h2>
+
+<p>The API is written in the Java programming language
+for convenience. The code is free to call native methods if appropriate.
+
+<h3>Chunk Handler API</h3>
+
+<p>The basic idea is that arbitrary code can register handlers for
+specific chunk types. When a DDM chunk with that type arrives, the
+appropriate handler is invoked. The handler's return value provides the
+response to the server.
+
+<p>There are two packages. android.ddm lives in the "framework" library,
+and has all of the chunk handlers and registration code. It can freely
+use Android classes. org.apache.harmony.dalvik.ddmc lives in the "core"
+library, and has
+some base classes and features that interact with the VM. Nothing should
+need to modify the org.apache.harmony.dalvik.ddmc classes.
+
+<p>The DDM classes pass chunks of data around with a simple class:
+
+<pre class=prettyprint>
+class Chunk {
+ int type;
+ byte[] data;
+ int offset, length;
+};
+</pre>
+
+<p>The chunk handlers accept and return them:
+<pre class=prettyprint>
+public Chunk handleChunk(Chunk request)
+</pre>
+<p>The code is free to parse the chunk and generate a response in any
+way it chooses. Big-endian byte ordering is recommended but not mandatory.
+<p>Chunk handlers will be notified when a DDM server connects or disconnects,
+so that they can perform setup and cleanup operations:
+<pre class=prettyprint>
+public void connected()
+public void disconnected()
+</pre>
+
+<p>The method processes the request, formulates a response, and returns it.
+If the method returns null, an empty JDWP success message will be returned.
+<p>The request/response interaction is essentially asynchronous in the
+protocol. The packets are linked together with the JDWP message ID.
+<p>[We could use ByteBuffer here instead of byte[], but it doesn't gain
+us much. Wrapping a ByteBuffer around an array is easy. We don't want
+to pass the full packet in because we could have multiple chunks in one
+request packet. The DDM code needs to collect and aggregate the responses
+to all chunks into a single JDWP response packet. Parties wanting to
+write multiple chunks in response to a single chunk should send a null
+response back and use "sendChunk()" to send the data independently.]
+
+<h3>Unsolicited event API</h3>
+
+<p>If a piece of code wants to send a chunk of data to the server at some
+arbitrary time, it may do so with a method provided by
+org.apache.harmony.dalvik.DdmServer:
+
+<pre class=prettyprint>
+public static void sendChunk(Chunk chunk)
+</pre>
+
+<p>There is no response or status code. No exceptions are thrown.
+
+
+<h2>Server API</h2>
+
+<p>This is similar to the client side in many ways, but makes extensive
+use of ByteBuffer in a perhaps misguided attempt to use java.nio.channels
+and avoid excessive thread creation and unnecessary data copying.
+
+<p>Upon receipt of a packet, the server will identify it as one of:
+<ol>
+ <li>Message to be passed through to the debugger
+ <li>Response to an earlier request
+ <li>Unsolicited event packet
+</ol>
+<p>To handle (2), when messages are sent from the server to the client,
+the message must be paired with a callback method. The response might be
+delayed for a while -- or might never arrive -- so the server can't block
+waiting for responses from the client.
+<p>The chunk handlers look like this:
+<pre class=prettyprint>
+public void handleChunk(Client client, int type,
+ ByteBuffer data, boolean isReply, int msgId)
+</pre>
+<p>The arguments are:
+<dl>
+ <dt>client
+ <dd>An object representing the client VM that send us the packet.
+ <dt>type
+ <dd>The 32-bit chunk type.
+ <dt>data
+ <dd>The data. The data's length can be determined by calling data.limit().
+ <dt>isReply
+ <dd>Set to "true" if this was a reply to a message we sent earlier,
+ "false" if the client sent this unsolicited.
+ <dt>msgId
+ <dd>The JDWP message ID. Useful for connecting replies with requests.
+</dl>
+<p>If a handler doesn't like the contents of a packet, it should log an
+error message and return. If the handler doesn't recognize the packet at
+all, it can call the superclass' handleUnknownChunk() method.
+
+<p>As with the client, the server code can be notified when clients
+connect or disconnect. This allows the handler to send initialization
+code immediately after a connect, or clean up after a disconnect.
+<p>Data associated with a client can be stored in a ClientData object,
+which acts as a general per-client dumping around for VM and UI state.
+
+
+<P><BR>
+
+<HR>
+
+<address>Copyright &copy; 2007 The Android Open Source Project</address>
+
+</body>
+</HTML>
diff --git a/docs/dex-format.css b/docs/dex-format.css
new file mode 100644
index 0000000..153dd4e
--- /dev/null
+++ b/docs/dex-format.css
@@ -0,0 +1,387 @@
+h1 {
+ font-family: serif;
+ border-top-style: solid;
+ border-top-width: 5px;
+ padding-top: 9pt;
+ margin-top: 40pt;
+ color: #222266;
+}
+
+h1.title {
+ border: none;
+}
+
+h2 {
+ font-family: serif;
+ border-top-style: solid;
+ border-top-width: 2px;
+ border-color: #ccccdd;
+ padding-top: 9pt;
+ margin-top: 40pt;
+ margin-bottom: 2pt;
+ color: #222266;
+}
+
+h3 {
+ font-family: serif;
+ font-style: bold;
+ margin-top: 20pt;
+ margin-bottom: 2pt;
+ color: #222266;
+}
+
+h4 {
+ font-family: serif;
+ font-style: italic;
+ margin-top: 2pt;
+ margin-bottom: 2pt;
+ color: #666688;
+}
+
+@media print {
+ table {
+ font-size: 8pt;
+ }
+}
+
+@media screen {
+ table {
+ font-size: 10pt;
+ }
+}
+
+pre {
+ background: #eeeeff;
+ border-color: #aaaaff;
+ border-style: solid;
+ border-width: 1px;
+ margin-left: 40pt;
+ margin-right: 40pt;
+ padding: 6pt;
+}
+
+table {
+ border-collapse: collapse;
+ margin-top: 10pt;
+ margin-left: 40pt;
+ margin-right: 40pt;
+}
+
+table th {
+ font-family: sans-serif;
+ background: #aabbff;
+}
+
+table td {
+ font-family: sans-serif;
+ border-top-style: solid;
+ border-bottom-style: solid;
+ border-width: 1px;
+ border-color: #aaaaff;
+ padding-top: 3pt;
+ padding-bottom: 3pt;
+ padding-left: 3pt;
+ padding-right: 4pt;
+ background: #eeeeff;
+}
+
+table p {
+ margin-bottom: 0pt;
+}
+
+/* for the bnf syntax sections */
+
+table.bnf {
+ background: #eeeeff;
+ border-color: #aaaaff;
+ border-style: solid;
+ border-width: 1px;
+ margin-top: 3pt;
+ margin-bottom: 3pt;
+ padding-top: 2pt;
+ padding-bottom: 6pt;
+ padding-left: 6pt;
+ padding-right: 6pt;
+}
+
+table.bnf td {
+ border: none;
+ padding-left: 6pt;
+ padding-right: 6pt;
+ padding-top: 1pt;
+ padding-bottom: 1pt;
+}
+
+table.bnf td:first-child {
+ padding-right: 0pt;
+ width: 8pt;
+}
+
+table.bnf td:first-child td {
+ padding-left: 0pt;
+}
+
+table.bnf td.def {
+ padding-top: 6pt;
+}
+
+table.bnf td.bar {
+ padding-left: 15pt;
+}
+
+table.bnf code {
+ font-weight: bold;
+}
+
+
+/* for the type name guide */
+
+table.guide {
+ margin-top: 20pt;
+ margin-bottom: 20pt;
+}
+
+table.guide td:first-child {
+ font-family: monospace;
+ width: 15%;
+}
+
+table.guide td:first-child + td {
+ font-family: sans-serif;
+ width: 85%;
+}
+
+
+/* for the LEB128 example tables */
+
+table.leb128Bits {
+ margin-top: 20pt;
+ margin-bottom: 20pt;
+}
+
+table.leb128Bits td {
+ border-left: solid #aaaaff 1px;
+ border-right: solid #aaaaff 1px;
+}
+
+table.leb128Bits td.start1 {
+ border-left: none;
+}
+
+table.leb128Bits td.start2 {
+ border-left: solid #000 2px;
+}
+
+table.leb128Bits td.end2 {
+ border-right: none;
+}
+
+table.leb128 {
+ margin-top: 20pt;
+ margin-bottom: 20pt;
+}
+
+table.leb128 td:first-child {
+ font-family: monospace;
+ text-align: center;
+ width: 31%;
+}
+
+table.leb128 td:first-child + td {
+ font-family: monospace;
+ text-align: center;
+ width: 23%;
+}
+
+table.leb128 td:first-child + td + td {
+ font-family: monospace;
+ text-align: center;
+ width: 23%;
+}
+
+table.leb128 td:first-child + td + td + td {
+ font-family: monospace;
+ text-align: center;
+ width: 23%;
+}
+
+
+/* for the general format tables */
+
+table.format {
+ margin-top: 20pt;
+ margin-bottom: 20pt;
+}
+
+table.format td:first-child {
+ font-family: monospace;
+ width: 20%;
+}
+
+table.format td:first-child + td {
+ font-family: monospace;
+ width: 20%;
+}
+
+table.format td:first-child + td + td {
+ width: 60%;
+}
+
+table.format td i {
+ font-family: sans-serif;
+}
+
+
+/* for the type code table */
+
+table.typeCodes {
+ margin-top: 20pt;
+ margin-bottom: 20pt;
+}
+
+table.typeCodes td:first-child {
+ font-family: monospace;
+ width: 30%;
+}
+
+table.typeCodes td:first-child + td {
+ font-family: monospace;
+ width: 30%;
+}
+
+table.typeCodes td:first-child + td + td {
+ font-family: monospace;
+ width: 10%;
+}
+
+table.typeCodes td:first-child + td + td + td {
+ font-family: monospace;
+ width: 30%;
+}
+
+table.typeCodes td i {
+ font-family: sans-serif;
+}
+
+
+/* for the access flags table */
+
+table.accessFlags {
+ margin-top: 20pt;
+ margin-bottom: 20pt;
+}
+
+table.accessFlags td:first-child {
+ font-family: monospace;
+ width: 10%;
+}
+
+table.accessFlags td:first-child + td {
+ font-family: monospace;
+ width: 6%;
+}
+
+table.accessFlags td:first-child + td + td {
+ width: 28%;
+}
+
+table.accessFlags td:first-child + td + td + td {
+ width: 28%;
+}
+
+table.accessFlags td:first-child + td + td + td + td {
+ width: 28%;
+}
+
+table.accessFlags i {
+ font-family: sans-serif;
+}
+
+
+/* for the descriptor table */
+
+table.descriptor {
+ margin-top: 20pt;
+ margin-bottom: 20pt;
+}
+
+table.descriptor td:first-child {
+ font-family: monospace;
+ width: 25%;
+}
+
+table.descriptor td:first-child + td {
+ font-family: sans-serif;
+ width: 75%;
+}
+
+
+/* for the debug bytecode table */
+
+table.debugByteCode {
+ margin-top: 20pt;
+ margin-bottom: 20pt;
+}
+
+table.debugByteCode td:first-child {
+ font-family: monospace;
+ width: 20%;
+}
+
+table.debugByteCode td:first-child + td {
+ font-family: monospace;
+ width: 5%;
+}
+
+table.debugByteCode td:first-child + td + td{
+ font-family: monospace;
+ width: 15%;
+}
+
+table.debugByteCode td:first-child + td + td + td {
+ width: 25%;
+}
+
+table.debugByteCode td:first-child + td + td + td + td {
+ width: 35%;
+}
+
+table.debugByteCode i {
+ font-family: sans-serif;
+}
+
+
+/* for the encoded value table */
+
+table.encodedValue {
+ margin-top: 20pt;
+ margin-bottom: 20pt;
+}
+
+table.encodedValue td:first-child {
+ font-family: monospace;
+ width: 12%;
+}
+
+table.encodedValue td:first-child + td {
+ font-family: monospace;
+ width: 10%;
+}
+
+table.encodedValue td:first-child + td + td {
+ font-family: monospace;
+ width: 15%;
+}
+
+table.encodedValue td:first-child + td + td + td {
+ font-family: monospace;
+ width: 15%;
+}
+
+table.encodedValue td:first-child + td + td + td + td {
+ width: 48%;
+}
+
+table.encodedValue td i {
+ font-family: sans-serif;
+}
diff --git a/docs/dex-format.html b/docs/dex-format.html
new file mode 100644
index 0000000..cab9d4c
--- /dev/null
+++ b/docs/dex-format.html
@@ -0,0 +1,3043 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>.dex &mdash; Dalvik Executable Format</title>
+<link rel=stylesheet href="dex-format.css">
+</head>
+
+<body>
+
+<h1 class="title"><code>.dex</code> &mdash; Dalvik Executable Format</h1>
+<p>Copyright &copy; 2007 The Android Open Source Project
+
+<p>This document describes the layout and contents of <code>.dex</code>
+files, which are used to hold a set of class definitions and their associated
+adjunct data.</p>
+
+<h1>Guide To Types</h1>
+
+<table class="guide">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>byte</td>
+ <td>8-bit signed int</td>
+</tr>
+<tr>
+ <td>ubyte</td>
+ <td>8-bit unsigned int</td>
+</tr>
+<tr>
+ <td>short</td>
+ <td>16-bit signed int, little-endian</td>
+</tr>
+<tr>
+ <td>ushort</td>
+ <td>16-bit unsigned int, little-endian</td>
+</tr>
+<tr>
+ <td>int</td>
+ <td>32-bit signed int, little-endian</td>
+</tr>
+<tr>
+ <td>uint</td>
+ <td>32-bit unsigned int, little-endian</td>
+</tr>
+<tr>
+ <td>long</td>
+ <td>64-bit signed int, little-endian</td>
+</tr>
+<tr>
+ <td>ulong</td>
+ <td>64-bit unsigned int, little-endian</td>
+</tr>
+<tr>
+ <td>sleb128</td>
+ <td>signed LEB128, variable-length (see below)</td>
+</tr>
+<tr>
+ <td>uleb128</td>
+ <td>unsigned LEB128, variable-length (see below)</td>
+</tr>
+<tr>
+ <td>uleb128p1</td>
+ <td>unsigned LEB128 plus <code>1</code>, variable-length (see below)</td>
+</tr>
+</tbody>
+</table>
+
+<h3>LEB128</h3>
+
+<p>LEB128 ("<b>L</b>ittle-<b>E</b>ndian <b>B</b>ase <b>128</b>") is a
+variable-length encoding for
+arbitrary signed or unsigned integer quantities. The format was
+borrowed from the <a href="http://dwarfstd.org/Dwarf3Std.php">DWARF3</a>
+specification. In a <code>.dex</code> file, LEB128 is only ever used to
+encode 32-bit quantities.</p>
+
+<p>Each LEB128 encoded value consists of one to five
+bytes, which together represent a single 32-bit value. Each
+byte has its most significant bit set except for the final byte in the
+sequence, which has its most significant bit clear. The remaining
+seven bits of each byte are payload, with the least significant seven
+bits of the quantity in the first byte, the next seven in the second
+byte and so on. In the case of a signed LEB128 (<code>sleb128</code>),
+the most significant payload bit of the final byte in the sequence is
+sign-extended to produce the final value. In the unsigned case
+(<code>uleb128</code>), any bits not explicitly represented are
+interpreted as <code>0</code>.
+
+<table class="leb128Bits">
+<thead>
+<tr><th colspan="16">Bitwise diagram of a two-byte LEB128 value</th></tr>
+<tr>
+ <th colspan="8">First byte</td>
+ <th colspan="8">Second byte</td>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td class="start1"><code>1</code></td>
+ <td>bit<sub>6</sub></td>
+ <td>bit<sub>5</sub></td>
+ <td>bit<sub>4</sub></td>
+ <td>bit<sub>3</sub></td>
+ <td>bit<sub>2</sub></td>
+ <td>bit<sub>1</sub></td>
+ <td>bit<sub>0</sub></td>
+ <td class="start2"><code>0</code></td>
+ <td>bit<sub>13</sub></td>
+ <td>bit<sub>12</sub></td>
+ <td>bit<sub>11</sub></td>
+ <td>bit<sub>10</sub></td>
+ <td>bit<sub>9</sub></td>
+ <td>bit<sub>8</sub></td>
+ <td class="end2">bit<sub>7</sub></td>
+</tr>
+</tbody>
+</table>
+
+<p>The variant <code>uleb128p1</code> is used to represent a signed
+value, where the representation is of the value <i>plus one</i> encoded
+as a <code>uleb128</code>. This makes the encoding of <code>-1</code>
+(alternatively thought of as the unsigned value <code>0xffffffff</code>)
+&mdash; but no other negative number &mdash; a single byte, and is
+useful in exactly those cases where the represented number must either
+be non-negative or <code>-1</code> (or <code>0xffffffff</code>),
+and where no other negative values are allowed (or where large unsigned
+values are unlikely to be needed).</p>
+
+<p>Here are some examples of the formats:</p>
+
+<table class="leb128">
+<thead>
+<tr>
+ <th>Encoded Sequence</th>
+ <th>As <code>sleb128</code></th>
+ <th>As <code>uleb128</code></th>
+ <th>As <code>uleb128p1</code></th>
+</tr>
+</thead>
+<tbody>
+ <tr><td>00</td><td>0</td><td>0</td><td>-1</td></tr>
+ <tr><td>01</td><td>1</td><td>1</td><td>0</td></tr>
+ <tr><td>7f</td><td>-1</td><td>127</td><td>126</td></tr>
+ <tr><td>80 7f</td><td>-128</td><td>16256</td><td>16255</td></tr>
+</tbody>
+</table>
+
+<h1>Overall File Layout</h1>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>header</td>
+ <td>header_item</td>
+ <td>the header</td>
+</tr>
+<tr>
+ <td>string_ids</td>
+ <td>string_id_item[]</td>
+ <td>string identifiers list. These are identifiers for all the strings
+ used by this file, either for internal naming (e.g., type descriptors)
+ or as constant objects referred to by code. This list must be sorted
+ by string contents, using UTF-16 code point values (not in a
+ locale-sensitive manner).
+ </td>
+</tr>
+<tr>
+ <td>type_ids</td>
+ <td>type_id_item[]</td>
+ <td>type identifiers list. These are identifiers for all types (classes,
+ arrays, or primitive types) referred to by this file, whether defined
+ in the file or not. This list must be sorted by <code>string_id</code>
+ index.
+ </td>
+</tr>
+<tr>
+ <td>proto_ids</td>
+ <td>proto_id_item[]</td>
+ <td>method prototype identifiers list. These are identifiers for all
+ prototypes referred to by this file. This list must be sorted in
+ return-type (by <code>type_id</code> index) major order, and then
+ by arguments (also by <code>type_id</code> index).
+ </td>
+</tr>
+<tr>
+ <td>field_ids</td>
+ <td>field_id_item[]</td>
+ <td>field identifiers list. These are identifiers for all fields
+ referred to by this file, whether defined in the file or not. This
+ list must be sorted, where the defining type (by <code>type_id</code>
+ index) is the major order, field name (by <code>string_id</code> index)
+ is the intermediate order, and type (by <code>type_id</code> index)
+ is the minor order.
+ </td>
+</tr>
+<tr>
+ <td>method_ids</td>
+ <td>method_id_item[]</td>
+ <td>method identifiers list. These are identifiers for all methods
+ referred to by this file, whether defined in the file or not. This
+ list must be sorted, where the defining type (by <code>type_id</code>
+ index) is the major order, method name (by <code>string_id</code>
+ index) is the intermediate order, and method
+ prototype (by <code>proto_id</code> index) is the minor order.
+ </td>
+</tr>
+<tr>
+ <td>class_defs</td>
+ <td>class_def_item[]</td>
+ <td>class definitions list. The classes must be ordered such that a given
+ class's superclass and implemented interfaces appear in the
+ list earlier than the referring class.
+ </td>
+</tr>
+<tr>
+ <td>data</td>
+ <td>ubyte[]</td>
+ <td>data area, containing all the support data for the tables listed above.
+ Different items have different alignment requirements, and
+ padding bytes are inserted before each item if necessary to achieve
+ proper alignment.
+ </td>
+</tr>
+<tr>
+ <td>link_data</td>
+ <td>ubyte[]</td>
+ <td>data used in statically linked files. The format of the data in
+ this section is left unspecified by this document;
+ this section is empty in unlinked files, and runtime implementations
+ may use it as they see fit.
+ </td>
+</tr>
+</tbody>
+</table>
+
+<h1>Bitfield, String, and Constant Definitions</h1>
+
+<h2><code>DEX_FILE_MAGIC</code></h2>
+<h4>embedded in <code>header_item</code></h4>
+
+<p>The constant array/string <code>DEX_FILE_MAGIC</code> is the list of
+bytes that must appear at the beginning of a <code>.dex</code> file
+in order for it to be recognized as such. The value intentionally
+contains a newline (<code>"\n"</code> or <code>0x0a</code>) and a
+null byte (<code>"\0"</code> or <code>0x00</code>) in order to help
+in the detection of certain forms of corruption. The value also
+encodes a format version number as three decimal digits, which is
+expected to increase monotonically over time as the format evolves.</p>
+
+<pre>
+ubyte[8] DEX_FILE_MAGIC = { 0x64 0x65 0x78 0x0a 0x30 0x33 0x35 0x00 }
+ = "dex\n035\0"
+</pre>
+
+<p><b>Note:</b> At least a couple earlier versions of the format have
+been used in widely-available public software releases. For example,
+version <code>009</code> was used for the M3 releases of the
+Android platform (November-December 2007),
+and version <code>013</code> was used for the M5 releases of the Android
+platform (February-March 2008). In several respects, these earlier versions
+of the format differ significantly from the version described in this
+document.</p>
+
+<h2><code>ENDIAN_CONSTANT</code> and <code>REVERSE_ENDIAN_CONSTANT</code></h2>
+<h4>embedded in <code>header_item</code></h4>
+
+<p>The constant <code>ENDIAN_CONSTANT</code> is used to indicate the
+endianness of the file in which it is found. Although the standard
+<code>.dex</code> format is little-endian, implementations may choose
+to perform byte-swapping. Should an implementation come across a
+header whose <code>endian_tag</code> is <code>REVERSE_ENDIAN_CONSTANT</code>
+instead of <code>ENDIAN_CONSTANT</code>, it would know that the file
+has been byte-swapped from the expected form.</p>
+
+<pre>
+uint ENDIAN_CONSTANT = 0x12345678;
+uint REVERSE_ENDIAN_CONSTANT = 0x78563412;
+</pre>
+
+<h2><code>NO_INDEX</code></h2>
+<h4>embedded in <code>class_def_item</code> and
+<code>debug_info_item</code></h4>
+
+<p>The constant <code>NO_INDEX</code> is used to indicate that
+an index value is absent.</p>
+
+<p><b>Note:</b> This value isn't defined to be
+<code>0</code>, because that is in fact typically a valid index.</p>
+
+<p><b>Also Note:</b> The chosen value for <code>NO_INDEX</code> is
+representable as a single byte in the <code>uleb128p1</code> encoding.</p>
+
+<pre>
+uint NO_INDEX = 0xffffffff; // == -1 if treated as a signed int
+</pre>
+
+<h2><code>access_flags</code> Definitions</h2>
+<h4>embedded in <code>class_def_item</code>,
+<code>field_item</code>, <code>method_item</code>, and
+<code>InnerClass</code></h4>
+
+<p>Bitfields of these flags are used to indicate the accessibility and
+overall properties of classes and class members.</p>
+
+<table class="accessFlags">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Value</th>
+ <th>For Classes (and <code>InnerClass</code> annotations)</th>
+ <th>For Fields</th>
+ <th>For Methods</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>ACC_PUBLIC</td>
+ <td>0x1</td>
+ <td><code>public</code>: visible everywhere</td>
+ <td><code>public</code>: visible everywhere</td>
+ <td><code>public</code>: visible everywhere</td>
+</tr>
+<tr>
+ <td>ACC_PRIVATE</td>
+ <td>0x2</td>
+ <td><super>*</super>
+ <code>private</code>: only visible to defining class
+ </td>
+ <td><code>private</code>: only visible to defining class</td>
+ <td><code>private</code>: only visible to defining class</td>
+</tr>
+<tr>
+ <td>ACC_PROTECTED</td>
+ <td>0x4</td>
+ <td><super>*</super>
+ <code>protected</code>: visible to package and subclasses
+ </td>
+ <td><code>protected</code>: visible to package and subclasses</td>
+ <td><code>protected</code>: visible to package and subclasses</td>
+</tr>
+<tr>
+ <td>ACC_STATIC</td>
+ <td>0x8</td>
+ <td><super>*</super>
+ <code>static</code>: is not constructed with an outer
+ <code>this</code> reference</td>
+ <td><code>static</code>: global to defining class</td>
+ <td><code>static</code>: does not take a <code>this</code> argument</td>
+</tr>
+<tr>
+ <td>ACC_FINAL</td>
+ <td>0x10</td>
+ <td><code>final</code>: not subclassable</td>
+ <td><code>final</code>: immutable after construction</td>
+ <td><code>final</code>: not overridable</td>
+</tr>
+<tr>
+ <td>ACC_SYNCHRONIZED</td>
+ <td>0x20</td>
+ <td>&nbsp;</td>
+ <td>&nbsp;</td>
+ <td><code>synchronized</code>: associated lock automatically acquired
+ around call to this method. <b>Note:</b> This is only valid to set when
+ <code>ACC_NATIVE</code> is also set.</td>
+</tr>
+<tr>
+ <td>ACC_VOLATILE</td>
+ <td>0x40</td>
+ <td>&nbsp;</td>
+ <td><code>volatile</code>: special access rules to help with thread
+ safety</td>
+ <td>&nbsp;</td>
+</tr>
+<tr>
+ <td>ACC_BRIDGE</td>
+ <td>0x40</td>
+ <td>&nbsp;</td>
+ <td>&nbsp;</td>
+ <td>bridge method, added automatically by compiler as a type-safe
+ bridge</td>
+</tr>
+<tr>
+ <td>ACC_TRANSIENT</td>
+ <td>0x80</td>
+ <td>&nbsp;</td>
+ <td><code>transient</code>: not to be saved by default serialization</td>
+ <td>&nbsp;</td>
+</tr>
+<tr>
+ <td>ACC_VARARGS</td>
+ <td>0x80</td>
+ <td>&nbsp;</td>
+ <td>&nbsp;</td>
+ <td>last argument should be treated as a "rest" argument by compiler</td>
+</tr>
+<tr>
+ <td>ACC_NATIVE</td>
+ <td>0x100</td>
+ <td>&nbsp;</td>
+ <td>&nbsp;</td>
+ <td><code>native</code>: implemented in native code</td>
+</tr>
+<tr>
+ <td>ACC_INTERFACE</td>
+ <td>0x200</td>
+ <td><code>interface</code>: multiply-implementable abstract class</td>
+ <td>&nbsp;</td>
+ <td>&nbsp;</td>
+</tr>
+<tr>
+ <td>ACC_ABSTRACT</td>
+ <td>0x400</td>
+ <td><code>abstract</code>: not directly instantiable</td>
+ <td>&nbsp;</td>
+ <td><code>abstract</code>: unimplemented by this class</td>
+</tr>
+<tr>
+ <td>ACC_STRICT</td>
+ <td>0x800</td>
+ <td>&nbsp;</td>
+ <td>&nbsp;</td>
+ <td><code>strictfp</code>: strict rules for floating-point arithmetic</td>
+</tr>
+<tr>
+ <td>ACC_SYNTHETIC</td>
+ <td>0x1000</td>
+ <td>not directly defined in source code</td>
+ <td>not directly defined in source code</td>
+ <td>not directly defined in source code</td>
+</tr>
+<tr>
+ <td>ACC_ANNOTATION</td>
+ <td>0x2000</td>
+ <td>declared as an annotation class</td>
+ <td>&nbsp;</td>
+ <td>&nbsp;</td>
+</tr>
+<tr>
+ <td>ACC_ENUM</td>
+ <td>0x4000</td>
+ <td>declared as an enumerated type</td>
+ <td>declared as an enumerated value</td>
+ <td>&nbsp;</td>
+</tr>
+<tr>
+ <td><i>(unused)</i></td>
+ <td>0x8000</td>
+ <td>&nbsp;</td>
+ <td>&nbsp;</td>
+ <td>&nbsp;</td>
+</tr>
+<tr>
+ <td>ACC_CONSTRUCTOR</td>
+ <td>0x10000</td>
+ <td>&nbsp;</td>
+ <td>&nbsp;</td>
+ <td>constructor method (class or instance initializer)</td>
+</tr>
+<tr>
+ <td>ACC_DECLARED_<br/>SYNCHRONIZED</td>
+ <td>0x20000</td>
+ <td>&nbsp;</td>
+ <td>&nbsp;</td>
+ <td>declared <code>synchronized</code>. <b>Note:</b> This has no effect on
+ execution (other than in reflection of this flag, per se).
+ </td>
+</tr>
+</tbody>
+</table>
+
+<p><super>*</super> Only allowed on for <code>InnerClass</code> annotations,
+and must not ever be on in a <code>class_def_item</code>.</p>
+
+<h2>MUTF-8 (Modified UTF-8) Encoding</h2>
+
+<p>As a concession to easier legacy support, the <code>.dex</code> format
+encodes its string data in a de facto standard modified UTF-8 form, hereafter
+referred to as MUTF-8. This form is identical to standard UTF-8, except:</p>
+
+<ul>
+ <li>Only the one-, two-, and three-byte encodings are used.</li>
+ <li>Code points in the range <code>U+10000</code> &hellip;
+ <code>U+10ffff</code> are encoded as a surrogate pair, each of
+ which is represented as a three-byte encoded value.</li>
+ <li>The code point <code>U+0000</code> is encoded in two-byte form.</li>
+ <li>A plain null byte (value <code>0</code>) indicates the end of
+ a string, as is the standard C language interpretation.</li>
+</ul>
+
+<p>The first two items above can be summarized as: MUTF-8
+is an encoding format for UTF-16, instead of being a more direct
+encoding format for Unicode characters.</p>
+
+<p>The final two items above make it simultaneously possible to include
+the code point <code>U+0000</code> in a string <i>and</i> still manipulate
+it as a C-style null-terminated string.</p>
+
+<p>However, the special encoding of <code>U+0000</code> means that, unlike
+normal UTF-8, the result of calling the standard C function
+<code>strcmp()</code> on a pair of MUTF-8 strings does not always
+indicate the properly signed result of comparison of <i>unequal</i> strings.
+When ordering (not just equality) is a concern, the most straightforward
+way to compare MUTF-8 strings is to decode them character by character,
+and compare the decoded values. (However, more clever implementations are
+also possible.)</p>
+
+<p>Please refer to <a href="http://unicode.org">The Unicode
+Standard</a> for further information about character encoding.
+MUTF-8 is actually closer to the (relatively less well-known) encoding
+<a href="http://www.unicode.org/reports/tr26/">CESU-8</a> than to UTF-8
+per se.</p>
+
+<h2><code>encoded_value</code> Encoding</h2>
+<h4>embedded in <code>annotation_element</code> and
+<code>encoded_array_item</code></h4>
+
+<p>An <code>encoded_value</code> is an encoded piece of (nearly)
+arbitrary hierarchically structured data. The encoding is meant to
+be both compact and straightforward to parse.</p>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>(value_arg &lt;&lt; 5) | value_type</td>
+ <td>ubyte</td>
+ <td>byte indicating the type of the immediately subsequent
+ <code>value</code> along
+ with an optional clarifying argument in the high-order three bits.
+ See below for the various <code>value</code> definitions.
+ In most cases, <code>value_arg</code> encodes the length of
+ the immediately-subsequent <code>value</code> in bytes, as
+ <code>(size - 1)</code>, e.g., <code>0</code> means that
+ the value requires one byte, and <code>7</code> means it requires
+ eight bytes; however, there are exceptions as noted below.
+ </td>
+</tr>
+<tr>
+ <td>value</td>
+ <td>ubyte[]</td>
+ <td>bytes representing the value, variable in length and interpreted
+ differently for different <code>value_type</code> bytes, though
+ always little-endian. See the various value definitions below for
+ details.
+ </td>
+</tr>
+</tbody>
+</table>
+
+<h3>Value Formats</h3>
+
+<table class="encodedValue">
+<thead>
+<tr>
+ <th>Type Name</th>
+ <th><code>value_type</code></th>
+ <th><code>value_arg</code> Format</th>
+ <th><code>value</code> Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>VALUE_BYTE</td>
+ <td>0x00</td>
+ <td><i>(none; must be <code>0</code>)</i></td>
+ <td>ubyte[1]</td>
+ <td>signed one-byte integer value</td>
+</tr>
+<tr>
+ <td>VALUE_SHORT</td>
+ <td>0x02</td>
+ <td>size - 1 (0&hellip;1)</td>
+ <td>ubyte[size]</td>
+ <td>signed two-byte integer value, sign-extended</td>
+</tr>
+<tr>
+ <td>VALUE_CHAR</td>
+ <td>0x03</td>
+ <td>size - 1 (0&hellip;1)</td>
+ <td>ubyte[size]</td>
+ <td>unsigned two-byte integer value, zero-extended</td>
+</tr>
+<tr>
+ <td>VALUE_INT</td>
+ <td>0x04</td>
+ <td>size - 1 (0&hellip;3)</td>
+ <td>ubyte[size]</td>
+ <td>signed four-byte integer value, sign-extended</td>
+</tr>
+<tr>
+ <td>VALUE_LONG</td>
+ <td>0x06</td>
+ <td>size - 1 (0&hellip;7)</td>
+ <td>ubyte[size]</td>
+ <td>signed eight-byte integer value, sign-extended</td>
+</tr>
+<tr>
+ <td>VALUE_FLOAT</td>
+ <td>0x10</td>
+ <td>size - 1 (0&hellip;3)</td>
+ <td>ubyte[size]</td>
+ <td>four-byte bit pattern, zero-extended <i>to the right</i>, and
+ interpreted as an IEEE754 32-bit floating point value
+ </td>
+</tr>
+<tr>
+ <td>VALUE_DOUBLE</td>
+ <td>0x11</td>
+ <td>size - 1 (0&hellip;7)</td>
+ <td>ubyte[size]</td>
+ <td>eight-byte bit pattern, zero-extended <i>to the right</i>, and
+ interpreted as an IEEE754 64-bit floating point value
+ </td>
+</tr>
+<tr>
+ <td>VALUE_STRING</td>
+ <td>0x17</td>
+ <td>size - 1 (0&hellip;3)</td>
+ <td>ubyte[size]</td>
+ <td>unsigned (zero-extended) four-byte integer value,
+ interpreted as an index into
+ the <code>string_ids</code> section and representing a string value
+ </td>
+</tr>
+<tr>
+ <td>VALUE_TYPE</td>
+ <td>0x18</td>
+ <td>size - 1 (0&hellip;3)</td>
+ <td>ubyte[size]</td>
+ <td>unsigned (zero-extended) four-byte integer value,
+ interpreted as an index into
+ the <code>type_ids</code> section and representing a reflective
+ type/class value
+ </td>
+</tr>
+<tr>
+ <td>VALUE_FIELD</td>
+ <td>0x19</td>
+ <td>size - 1 (0&hellip;3)</td>
+ <td>ubyte[size]</td>
+ <td>unsigned (zero-extended) four-byte integer value,
+ interpreted as an index into
+ the <code>field_ids</code> section and representing a reflective
+ field value
+ </td>
+</tr>
+<tr>
+ <td>VALUE_METHOD</td>
+ <td>0x1a</td>
+ <td>size - 1 (0&hellip;3)</td>
+ <td>ubyte[size]</td>
+ <td>unsigned (zero-extended) four-byte integer value,
+ interpreted as an index into
+ the <code>method_ids</code> section and representing a reflective
+ method value
+ </td>
+</tr>
+<tr>
+ <td>VALUE_ENUM</td>
+ <td>0x1b</td>
+ <td>size - 1 (0&hellip;3)</td>
+ <td>ubyte[size]</td>
+ <td>unsigned (zero-extended) four-byte integer value,
+ interpreted as an index into
+ the <code>field_ids</code> section and representing the value of
+ an enumerated type constant
+ </td>
+</tr>
+<tr>
+ <td>VALUE_ARRAY</td>
+ <td>0x1c</td>
+ <td><i>(none; must be <code>0</code>)</i></td>
+ <td>encoded_array</td>
+ <td>an array of values, in the format specified by
+ "<code>encoded_array</code> Format" below. The size
+ of the <code>value</code> is implicit in the encoding.
+ </td>
+</tr>
+<tr>
+ <td>VALUE_ANNOTATION</td>
+ <td>0x1d</td>
+ <td><i>(none; must be <code>0</code>)</i></td>
+ <td>encoded_annotation</td>
+ <td>a sub-annotation, in the format specified by
+ "<code>encoded_annotation</code> Format" below. The size
+ of the <code>value</code> is implicit in the encoding.
+ </td>
+</tr>
+<tr>
+ <td>VALUE_NULL</td>
+ <td>0x1e</td>
+ <td><i>(none; must be <code>0</code>)</i></td>
+ <td><i>(none)</i></td>
+ <td><code>null</code> reference value</td>
+</tr>
+<tr>
+ <td>VALUE_BOOLEAN</td>
+ <td>0x1f</td>
+ <td>boolean (0&hellip;1)</td>
+ <td><i>(none)</i></td>
+ <td>one-bit value; <code>0</code> for <code>false</code> and
+ <code>1</code> for <code>true</code>. The bit is represented in the
+ <code>value_arg</code>.
+ </td>
+</tr>
+</tbody>
+</table>
+
+<h3><code>encoded_array</code> Format</h3>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>size</td>
+ <td>uleb128</td>
+ <td>number of elements in the array</td>
+</tr>
+<tr>
+ <td>values</td>
+ <td>encoded_value[size]</td>
+ <td>a series of <code>size</code> <code>encoded_value</code> byte
+ sequences in the format specified by this section, concatenated
+ sequentially.
+ </td>
+</tr>
+</tbody>
+</table>
+
+<h3><code>encoded_annotation</code> Format</h3>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>type_idx</td>
+ <td>uleb128</td>
+ <td>type of the annotation. This must be a class (not array or primitive)
+ type.
+ </td>
+</tr>
+<tr>
+ <td>size</td>
+ <td>uleb128</td>
+ <td>number of name-value mappings in this annotation</td>
+</tr>
+<tr>
+ <td>elements</td>
+ <td>annotation_element[size]</td>
+ <td>elements of the annotataion, represented directly in-line (not as
+ offsets). Elements must be sorted in increasing order by
+ <code>string_id</code> index.
+ </td>
+</tr>
+</tbody>
+</table>
+
+<h3><code>annotation_element</code> Format</h3>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>name_idx</td>
+ <td>uleb128</td>
+ <td>element name, represented as an index into the
+ <code>string_ids</code> section. The string must conform to the
+ syntax for <i>MemberName</i>, defined above.
+ </td>
+</tr>
+<tr>
+ <td>value</td>
+ <td>encoded_value</td>
+ <td>element value</td>
+</tr>
+</tbody>
+</table>
+
+<h2>String Syntax</h2>
+
+<p>There are several kinds of item in a <code>.dex</code> file which
+ultimately refer to a string. The following BNF-style definitions
+indicate the acceptable syntax for these strings.</p>
+
+<h3><i>SimpleName</i></h3>
+
+<p>A <i>SimpleName</i> is the basis for the syntax of the names of other
+things. The <code>.dex</code> format allows a fair amount of latitude
+here (much more than most common source languages). In brief, a simple
+name may consist of any low-ASCII alphabetic character or digit, a few
+specific low-ASCII symbols, and most non-ASCII code points that are not
+control, space, or special characters. Note that surrogate code points
+(in the range <code>U+d800</code> &hellip; <code>U+dfff</code>) are not
+considered valid name characters, per se, but Unicode supplemental
+characters <i>are</i> valid (which are represented by the final
+alternative of the rule for <i>SimpleNameChar</i>), and they should be
+represented in a file as pairs of surrogate code points in the MUTF-8
+encoding.</p>
+
+<table class="bnf">
+ <tr><td colspan="2" class="def"><i>SimpleName</i> &rarr;</td></tr>
+ <tr>
+ <td/>
+ <td><i>SimpleNameChar</i> (<i>SimpleNameChar</i>)*</td>
+ </tr>
+
+ <tr><td colspan="2" class="def"><i>SimpleNameChar</i> &rarr;</td></tr>
+ <tr>
+ <td/>
+ <td><code>'A'</code> &hellip; <code>'Z'</code></td>
+ </tr>
+ <tr>
+ <td class="bar">|</td>
+ <td><code>'a'</code> &hellip; <code>'z'</code></td>
+ </tr>
+ <tr>
+ <td class="bar">|</td>
+ <td><code>'0'</code> &hellip; <code>'9'</code></td>
+ </tr>
+ <tr>
+ <td class="bar">|</td>
+ <td><code>'$'</code></td>
+ </tr>
+ <tr>
+ <td class="bar">|</td>
+ <td><code>'-'</code></td>
+ </tr>
+ <tr>
+ <td class="bar">|</td>
+ <td><code>'_'</code></td>
+ </tr>
+ <tr>
+ <td class="bar">|</td>
+ <td><code>U+00a1</code> &hellip; <code>U+1fff</code></td>
+ </tr>
+ <tr>
+ <td class="bar">|</td>
+ <td><code>U+2010</code> &hellip; <code>U+2027</code></td>
+ </tr>
+ <tr>
+ <td class="bar">|</td>
+ <td><code>U+2030</code> &hellip; <code>U+d7ff</code></td>
+ </tr>
+ <tr>
+ <td class="bar">|</td>
+ <td><code>U+e000</code> &hellip; <code>U+ffef</code></td>
+ </tr>
+ <tr>
+ <td class="bar">|</td>
+ <td><code>U+10000</code> &hellip; <code>U+10ffff</code></td>
+ </tr>
+</table>
+
+<h3><i>MemberName</i></h3>
+<h4>used by <code>field_id_item</code> and <code>method_id_item</code></h4>
+
+<p>A <i>MemberName</i> is the name of a member of a class, members being
+fields, methods, and inner classes.</p>
+
+<table class="bnf">
+ <tr><td colspan="2" class="def"><i>MemberName</i> &rarr;</td></tr>
+ <tr>
+ <td/>
+ <td><i>SimpleName</i></td>
+ </tr>
+ <tr>
+ <td class="bar">|</td>
+ <td><code>'&lt;'</code> <i>SimpleName</i> <code>'&gt;'</code></td>
+ </tr>
+</table>
+
+<h3><i>FullClassName</i></h3>
+
+<p>A <i>FullClassName</i> is a fully-qualified class name, including an
+optional package specifier followed by a required name.</p>
+
+<table class="bnf">
+ <tr><td colspan="2" class="def"><i>FullClassName</i> &rarr;</td></tr>
+ <tr>
+ <td/>
+ <td><i>OptionalPackagePrefix</i> <i>SimpleName</i></td>
+ </tr>
+
+ <tr><td colspan="2" class="def"><i>OptionalPackagePrefix</i> &rarr;</td></tr>
+ <tr>
+ <td/>
+ <td>(<i>SimpleName</i> <code>'/'</code>)*</td>
+ </tr>
+</table>
+
+<h3><i>TypeDescriptor</i></h3>
+<h4>used by <code>type_id_item</code></h4>
+
+<p>A <i>TypeDescriptor</i> is the representation of any type, including
+primitives, classes, arrays, and <code>void</code>. See below for
+the meaning of the various versions.</p>
+
+<table class="bnf">
+ <tr><td colspan="2" class="def"><i>TypeDescriptor</i> &rarr;</td></tr>
+ <tr>
+ <td/>
+ <td><code>'V'</code></td>
+ </tr>
+ <tr>
+ <td class="bar">|</td>
+ <td><i>FieldTypeDescriptor</i></td>
+ </tr>
+
+ <tr><td colspan="2" class="def"><i>FieldTypeDescriptor</i> &rarr;</td></tr>
+ <tr>
+ <td/>
+ <td><i>NonArrayFieldTypeDescriptor</i></td>
+ </tr>
+ <tr>
+ <td class="bar">|</td>
+ <td>(<code>'['</code> * 1&hellip;255)
+ <i>NonArrayFieldTypeDescriptor</i></td>
+ </tr>
+
+ <tr>
+ <td colspan="2" class="def"><i>NonArrayFieldTypeDescriptor</i>&rarr;</td>
+ </tr>
+ <tr>
+ <td/>
+ <td><code>'Z'</code></td>
+ </tr>
+ <tr>
+ <td class="bar">|</td>
+ <td><code>'B'</code></td>
+ </tr>
+ <tr>
+ <td class="bar">|</td>
+ <td><code>'S'</code></td>
+ </tr>
+ <tr>
+ <td class="bar">|</td>
+ <td><code>'C'</code></td>
+ </tr>
+ <tr>
+ <td class="bar">|</td>
+ <td><code>'I'</code></td>
+ </tr>
+ <tr>
+ <td class="bar">|</td>
+ <td><code>'J'</code></td>
+ </tr>
+ <tr>
+ <td class="bar">|</td>
+ <td><code>'F'</code></td>
+ </tr>
+ <tr>
+ <td class="bar">|</td>
+ <td><code>'D'</code></td>
+ </tr>
+ <tr>
+ <td class="bar">|</td>
+ <td><code>'L'</code> <i>FullClassName</i> <code>';'</code></td>
+ </tr>
+</table>
+
+<h3><i>ShortyDescriptor</i></h3>
+<h4>used by <code>proto_id_item</code></h4>
+
+<p>A <i>ShortyDescriptor</i> is the short form representation of a method
+prototype, including return and parameter types, except that there is
+no distinction between various reference (class or array) types. Instead,
+all reference types are represented by a single <code>'L'</code> character.</p>
+
+<table class="bnf">
+ <tr><td colspan="2" class="def"><i>ShortyDescriptor</i> &rarr;</td></tr>
+ <tr>
+ <td/>
+ <td><i>ShortyReturnType</i> (<i>ShortyFieldType</i>)*</td>
+ </tr>
+
+ <tr><td colspan="2" class="def"><i>ShortyReturnType</i> &rarr;</td></tr>
+ <tr>
+ <td/>
+ <td><code>'V'</code></td>
+ </tr>
+ <tr>
+ <td class="bar">|</td>
+ <td><i>ShortyFieldType</i></td>
+ </tr>
+
+ <tr><td colspan="2" class="def"><i>ShortyFieldType</i> &rarr;</td></tr>
+ <tr>
+ <td/>
+ <td><code>'Z'</code></td>
+ </tr>
+ <tr>
+ <td class="bar">|</td>
+ <td><code>'B'</code></td>
+ </tr>
+ <tr>
+ <td class="bar">|</td>
+ <td><code>'S'</code></td>
+ </tr>
+ <tr>
+ <td class="bar">|</td>
+ <td><code>'C'</code></td>
+ </tr>
+ <tr>
+ <td class="bar">|</td>
+ <td><code>'I'</code></td>
+ </tr>
+ <tr>
+ <td class="bar">|</td>
+ <td><code>'J'</code></td>
+ </tr>
+ <tr>
+ <td class="bar">|</td>
+ <td><code>'F'</code></td>
+ </tr>
+ <tr>
+ <td class="bar">|</td>
+ <td><code>'D'</code></td>
+ </tr>
+ <tr>
+ <td class="bar">|</td>
+ <td><code>'L'</code></td>
+ </tr>
+</table>
+
+<h2><i>TypeDescriptor</i> Semantics</h2>
+
+<p>This is the meaning of each of the variants of <i>TypeDescriptor</i>.</p>
+
+<table class="descriptor">
+<thead>
+<tr>
+ <th>Syntax</th>
+ <th>Meaning</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>V</td>
+ <td><code>void</code>; only valid for return types</td>
+</tr>
+<tr>
+ <td>Z</td>
+ <td><code>boolean</code></td>
+</tr>
+<tr>
+ <td>B</td>
+ <td><code>byte</code></td>
+</tr>
+<tr>
+ <td>S</td>
+ <td><code>short</code></td>
+</tr>
+<tr>
+ <td>C</td>
+ <td><code>char</code></td>
+</tr>
+<tr>
+ <td>I</td>
+ <td><code>int</code></td>
+</tr>
+<tr>
+ <td>J</td>
+ <td><code>long</code></td>
+</tr>
+<tr>
+ <td>F</td>
+ <td><code>float</code></td>
+</tr>
+<tr>
+ <td>D</td>
+ <td><code>double</code></td>
+</tr>
+<tr>
+ <td>L<i>fully/qualified/Name</i>;</td>
+ <td>the class <code><i>fully.qualified.Name</i></code></td>
+</tr>
+<tr>
+ <td>[<i>descriptor</i></td>
+ <td>array of <code><i>descriptor</i></code>, usable recursively for
+ arrays-of-arrays, though it is invalid to have more than 255
+ dimensions.
+ </td>
+</tr>
+</tbody>
+</table>
+
+<h1>Items and Related Structures</h1>
+
+<p>This section includes definitions for each of the top-level items that
+may appear in a <code>.dex</code> file.
+
+<h2><code>header_item</code></h2>
+<h4>appears in the <code>header</code> section</h4>
+<h4>alignment: 4 bytes</h4>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>magic</td>
+ <td>ubyte[8] = DEX_FILE_MAGIC</td>
+ <td>magic value. See discussion above under "<code>DEX_FILE_MAGIC</code>"
+ for more details.
+ </td>
+</tr>
+<tr>
+ <td>checksum</td>
+ <td>uint</td>
+ <td>adler32 checksum of the rest of the file (everything but
+ <code>magic</code> and this field); used to detect file corruption
+ </td>
+</tr>
+<tr>
+ <td>signature</td>
+ <td>ubyte[20]</td>
+ <td>SHA-1 signature (hash) of the rest of the file (everything but
+ <code>magic</code>, <code>checksum</code>, and this field); used
+ to uniquely identify files
+ </td>
+</tr>
+<tr>
+ <td>file_size</td>
+ <td>uint</td>
+ <td>size of the entire file (including the header), in bytes
+</tr>
+<tr>
+ <td>header_size</td>
+ <td>uint = 0x70</td>
+ <td>size of the header (this entire section), in bytes. This allows for at
+ least a limited amount of backwards/forwards compatibility without
+ invalidating the format.
+ </td>
+</tr>
+<tr>
+ <td>endian_tag</td>
+ <td>uint = ENDIAN_CONSTANT</td>
+ <td>endianness tag. See discussion above under "<code>ENDIAN_CONSTANT</code>
+ and <code>REVERSE_ENDIAN_CONSTANT</code>" for more details.
+ </td>
+</tr>
+<tr>
+ <td>link_size</td>
+ <td>uint</td>
+ <td>size of the link section, or <code>0</code> if this file isn't
+ statically linked</td>
+</tr>
+<tr>
+ <td>link_off</td>
+ <td>uint</td>
+ <td>offset from the start of the file to the link section, or
+ <code>0</code> if <code>link_size == 0</code>. The offset, if non-zero,
+ should be to an offset into the <code>link_data</code> section. The
+ format of the data pointed at is left unspecified by this document;
+ this header field (and the previous) are left as hooks for use by
+ runtime implementations.
+ </td>
+</tr>
+<tr>
+ <td>map_off</td>
+ <td>uint</td>
+ <td>offset from the start of the file to the map item, or
+ <code>0</code> if this file has no map. The offset, if non-zero,
+ should be to an offset into the <code>data</code> section,
+ and the data should be in the format specified by "<code>map_list</code>"
+ below.
+ </td>
+</tr>
+<tr>
+ <td>string_ids_size</td>
+ <td>uint</td>
+ <td>count of strings in the string identifiers list</td>
+</tr>
+<tr>
+ <td>string_ids_off</td>
+ <td>uint</td>
+ <td>offset from the start of the file to the string identifiers list, or
+ <code>0</code> if <code>string_ids_size == 0</code> (admittedly a
+ strange edge case). The offset, if non-zero,
+ should be to the start of the <code>string_ids</code> section.
+ </td>
+</tr>
+<tr>
+ <td>type_ids_size</td>
+ <td>uint</td>
+ <td>count of elements in the type identifiers list</td>
+</tr>
+<tr>
+ <td>type_ids_off</td>
+ <td>uint</td>
+ <td>offset from the start of the file to the type identifiers list, or
+ <code>0</code> if <code>type_ids_size == 0</code> (admittedly a
+ strange edge case). The offset, if non-zero,
+ should be to the start of the <code>type_ids</code>
+ section.
+ </td>
+</tr>
+<tr>
+ <td>proto_ids_size</td>
+ <td>uint</td>
+ <td>count of elements in the prototype identifiers list</td>
+</tr>
+<tr>
+ <td>proto_ids_off</td>
+ <td>uint</td>
+ <td>offset from the start of the file to the prototype identifiers list, or
+ <code>0</code> if <code>proto_ids_size == 0</code> (admittedly a
+ strange edge case). The offset, if non-zero,
+ should be to the start of the <code>proto_ids</code>
+ section.
+ </td>
+</tr>
+<tr>
+ <td>field_ids_size</td>
+ <td>uint</td>
+ <td>count of elements in the field identifiers list</td>
+</tr>
+<tr>
+ <td>field_ids_off</td>
+ <td>uint</td>
+ <td>offset from the start of the file to the field identifiers list, or
+ <code>0</code> if <code>field_ids_size == 0</code>. The offset, if
+ non-zero, should be to the start of the <code>field_ids</code>
+ section.</td>
+</td>
+</tr>
+<tr>
+ <td>method_ids_size</td>
+ <td>uint</td>
+ <td>count of elements in the method identifiers list</td>
+</tr>
+<tr>
+ <td>method_ids_off</td>
+ <td>uint</td>
+ <td>offset from the start of the file to the method identifiers list, or
+ <code>0</code> if <code>method_ids_size == 0</code>. The offset, if
+ non-zero, should be to the start of the <code>method_ids</code>
+ section.</td>
+</tr>
+<tr>
+ <td>class_defs_size</td>
+ <td>uint</td>
+ <td>count of elements in the class definitions list</td>
+</tr>
+<tr>
+ <td>class_defs_off</td>
+ <td>uint</td>
+ <td>offset from the start of the file to the class definitions list, or
+ <code>0</code> if <code>class_defs_size == 0</code> (admittedly a
+ strange edge case). The offset, if non-zero,
+ should be to the start of the <code>class_defs</code> section.
+ </td>
+</tr>
+<tr>
+ <td>data_size</td>
+ <td>uint</td>
+ <td>Size of <code>data</code> section in bytes. Must be an even
+ multiple of sizeof(uint).</td>
+</tr>
+<tr>
+ <td>data_off</td>
+ <td>uint</td>
+ <td>offset from the start of the file to the start of the
+ <code>data</code> section.
+ </td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>map_list</code></h2>
+<h4>appears in the <code>data</code> section</h4>
+<h4>referenced from <code>header_item</code></h4>
+<h4>alignment: 4 bytes</h4>
+
+<p>This is a list of the entire contents of a file, in order. It
+contains some redundancy with respect to the <code>header_item</code>
+but is intended to be an easy form to use to iterate over an entire
+file. A given type may appear at most once in a map, but there is no
+restriction on what order types may appear in, other than the
+restrictions implied by the rest of the format (e.g., a
+<code>header</code> section must appear first, followed by a
+<code>string_ids</code> section, etc.). Additionally, the map entries must
+be ordered by initial offset and must not overlap.</p>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>size</td>
+ <td>uint</td>
+ <td>size of the list, in entries</td>
+</tr>
+<tr>
+ <td>list</td>
+ <td>map_item[size]</td>
+ <td>elements of the list</td>
+</tr>
+</tbody>
+</table>
+
+<h3><code>map_item</code> Format</h3>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>type</td>
+ <td>ushort</td>
+ <td>type of the items; see table below</td>
+</tr>
+<tr>
+ <td>unused</td>
+ <td>ushort</td>
+ <td><i>(unused)</i></td>
+</tr>
+<tr>
+ <td>size</td>
+ <td>uint</td>
+ <td>count of the number of items to be found at the indicated offset</td>
+</tr>
+<tr>
+ <td>offset</td>
+ <td>uint</td>
+ <td>offset from the start of the file to the items in question</td>
+</tr>
+</tbody>
+</table>
+
+
+<h3>Type Codes</h3>
+
+<table class="typeCodes">
+<thead>
+<tr>
+ <th>Item Type</th>
+ <th>Constant</th>
+ <th>Value</th>
+ <th>Item Size In Bytes</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>header_item</td>
+ <td>TYPE_HEADER_ITEM</td>
+ <td>0x0000</td>
+ <td>0x70</td>
+</tr>
+<tr>
+ <td>string_id_item</td>
+ <td>TYPE_STRING_ID_ITEM</td>
+ <td>0x0001</td>
+ <td>0x04</td>
+</tr>
+<tr>
+ <td>type_id_item</td>
+ <td>TYPE_TYPE_ID_ITEM</td>
+ <td>0x0002</td>
+ <td>0x04</td>
+</tr>
+<tr>
+ <td>proto_id_item</td>
+ <td>TYPE_PROTO_ID_ITEM</td>
+ <td>0x0003</td>
+ <td>0x0c</td>
+</tr>
+<tr>
+ <td>field_id_item</td>
+ <td>TYPE_FIELD_ID_ITEM</td>
+ <td>0x0004</td>
+ <td>0x08</td>
+</tr>
+<tr>
+ <td>method_id_item</td>
+ <td>TYPE_METHOD_ID_ITEM</td>
+ <td>0x0005</td>
+ <td>0x08</td>
+</tr>
+<tr>
+ <td>class_def_item</td>
+ <td>TYPE_CLASS_DEF_ITEM</td>
+ <td>0x0006</td>
+ <td>0x20</td>
+</tr>
+<tr>
+ <td>map_list</td>
+ <td>TYPE_MAP_LIST</td>
+ <td>0x1000</td>
+ <td>4 + (item.size * 12)</td>
+</tr>
+<tr>
+ <td>type_list</td>
+ <td>TYPE_TYPE_LIST</td>
+ <td>0x1001</td>
+ <td>4 + (item.size * 2)</td>
+</tr>
+<tr>
+ <td>annotation_set_ref_list</td>
+ <td>TYPE_ANNOTATION_SET_REF_LIST</td>
+ <td>0x1002</td>
+ <td>4 + (item.size * 4)</td>
+</tr>
+<tr>
+ <td>annotation_set_item</td>
+ <td>TYPE_ANNOTATION_SET_ITEM</td>
+ <td>0x1003</td>
+ <td>4 + (item.size * 4)</td>
+</tr>
+<tr>
+ <td>class_data_item</td>
+ <td>TYPE_CLASS_DATA_ITEM</td>
+ <td>0x2000</td>
+ <td><i>implicit; must parse</i></td>
+</tr>
+<tr>
+ <td>code_item</td>
+ <td>TYPE_CODE_ITEM</td>
+ <td>0x2001</td>
+ <td><i>implicit; must parse</i></td>
+</tr>
+<tr>
+ <td>string_data_item</td>
+ <td>TYPE_STRING_DATA_ITEM</td>
+ <td>0x2002</td>
+ <td><i>implicit; must parse</i></td>
+</tr>
+<tr>
+ <td>debug_info_item</td>
+ <td>TYPE_DEBUG_INFO_ITEM</td>
+ <td>0x2003</td>
+ <td><i>implicit; must parse</i></td>
+</tr>
+<tr>
+ <td>annotation_item</td>
+ <td>TYPE_ANNOTATION_ITEM</td>
+ <td>0x2004</td>
+ <td><i>implicit; must parse</i></td>
+</tr>
+<tr>
+ <td>encoded_array_item</td>
+ <td>TYPE_ENCODED_ARRAY_ITEM</td>
+ <td>0x2005</td>
+ <td><i>implicit; must parse</i></td>
+</tr>
+<tr>
+ <td>annotations_directory_item</td>
+ <td>TYPE_ANNOTATIONS_DIRECTORY_ITEM</td>
+ <td>0x2006</td>
+ <td><i>implicit; must parse</i></td>
+</tr>
+</tbody>
+</table>
+
+
+<h2><code>string_id_item</code></h2>
+<h4>appears in the <code>string_ids</code> section</h4>
+<h4>alignment: 4 bytes</h4>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>string_data_off</td>
+ <td>uint</td>
+ <td>offset from the start of the file to the string data for this
+ item. The offset should be to a location
+ in the <code>data</code> section, and the data should be in the
+ format specified by "<code>string_data_item</code>" below.
+ There is no alignment requirement for the offset.
+ </td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>string_data_item</code></h2>
+<h4>appears in the <code>data</code> section</h4>
+<h4>alignment: none (byte-aligned)</h4>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>utf16_size</td>
+ <td>uleb128</td>
+ <td>size of this string, in UTF-16 code units (which is the "string
+ length" in many systems). That is, this is the decoded length of
+ the string. (The encoded length is implied by the position of
+ the <code>0</code> byte.)</td>
+</tr>
+<tr>
+ <td>data</td>
+ <td>ubyte[]</td>
+ <td>a series of MUTF-8 code units (a.k.a. octets, a.k.a. bytes)
+ followed by a byte of value <code>0</code>. See
+ "MUTF-8 (Modified UTF-8) Encoding" above for details and
+ discussion about the data format.
+ <p><b>Note:</b> It is acceptable to have a string which includes
+ (the encoded form of) UTF-16 surrogate code units (that is,
+ <code>U+d800</code> &hellip; <code>U+dfff</code>)
+ either in isolation or out-of-order with respect to the usual
+ encoding of Unicode into UTF-16. It is up to higher-level uses of
+ strings to reject such invalid encodings, if appropriate.</p>
+ </td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>type_id_item</code></h2>
+<h4>appears in the <code>type_ids</code> section</h4>
+<h4>alignment: 4 bytes</h4>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>descriptor_idx</td>
+ <td>uint</td>
+ <td>index into the <code>string_ids</code> list for the descriptor
+ string of this type. The string must conform to the syntax for
+ <i>TypeDescriptor</i>, defined above.
+ </td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>proto_id_item</code></h2>
+<h4>appears in the <code>proto_ids</code> section</h4>
+<h4>alignment: 4 bytes</h4>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>shorty_idx</td>
+ <td>uint</td>
+ <td>index into the <code>string_ids</code> list for the short-form
+ descriptor string of this prototype. The string must conform to the
+ syntax for <i>ShortyDescriptor</i>, defined above, and must correspond
+ to the return type and parameters of this item.
+ </td>
+</tr>
+<tr>
+ <td>return_type_idx</td>
+ <td>uint</td>
+ <td>index into the <code>type_ids</code> list for the return type
+ of this prototype
+ </td>
+</tr>
+<tr>
+ <td>parameters_off</td>
+ <td>uint</td>
+ <td>offset from the start of the file to the list of parameter types
+ for this prototype, or <code>0</code> if this prototype has no
+ parameters. This offset, if non-zero, should be in the
+ <code>data</code> section, and the data there should be in the
+ format specified by <code>"type_list"</code> below. Additionally, there
+ should be no reference to the type <code>void</code> in the list.
+ </td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>field_id_item</code></h2>
+<h4>appears in the <code>field_ids</code> section</h4>
+<h4>alignment: 4 bytes</h4>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>class_idx</td>
+ <td>ushort</td>
+ <td>index into the <code>type_ids</code> list for the definer of this
+ field. This must be a class type, and not an array or primitive type.
+ </td>
+</tr>
+<tr>
+ <td>type_idx</td>
+ <td>ushort</td>
+ <td>index into the <code>type_ids</code> list for the type of
+ this field
+ </td>
+</tr>
+<tr>
+ <td>name_idx</td>
+ <td>uint</td>
+ <td>index into the <code>string_ids</code> list for the name of this
+ field. The string must conform to the syntax for <i>MemberName</i>,
+ defined above.
+ </td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>method_id_item</code></h2>
+<h4>appears in the <code>method_ids</code> section</h4>
+<h4>alignment: 4 bytes</h4>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>class_idx</td>
+ <td>ushort</td>
+ <td>index into the <code>type_ids</code> list for the definer of this
+ method. This must be a class or array type, and not a primitive type.
+ </td>
+</tr>
+<tr>
+ <td>proto_idx</td>
+ <td>ushort</td>
+ <td>index into the <code>proto_ids</code> list for the prototype of
+ this method
+ </td>
+</tr>
+<tr>
+ <td>name_idx</td>
+ <td>uint</td>
+ <td>index into the <code>string_ids</code> list for the name of this
+ method. The string must conform to the syntax for <i>MemberName</i>,
+ defined above.
+ </td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>class_def_item</code></h2>
+<h4>appears in the <code>class_defs</code> section</h4>
+<h4>alignment: 4 bytes</h4>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>class_idx</td>
+ <td>uint</td>
+ <td>index into the <code>type_ids</code> list for this class.
+ This must be a class type, and not an array or primitive type.
+ </td>
+</tr>
+<tr>
+ <td>access_flags</td>
+ <td>uint</td>
+ <td>access flags for the class (<code>public</code>, <code>final</code>,
+ etc.). See "<code>access_flags</code> Definitions" for details.
+ </td>
+</tr>
+<tr>
+ <td>superclass_idx</td>
+ <td>uint</td>
+ <td>index into the <code>type_ids</code> list for the superclass, or
+ the constant value <code>NO_INDEX</code> if this class has no
+ superclass (i.e., it is a root class such as <code>Object</code>).
+ If present, this must be a class type, and not an array or primitive type.
+ </td>
+</tr>
+<tr>
+ <td>interfaces_off</td>
+ <td>uint</td>
+ <td>offset from the start of the file to the list of interfaces, or
+ <code>0</code> if there are none. This offset
+ should be in the <code>data</code> section, and the data
+ there should be in the format specified by
+ "<code>type_list</code>" below. Each of the elements of the list
+ must be a class type (not an array or primitive type), and there
+ must not be any duplicates.
+ </td>
+</tr>
+<tr>
+ <td>source_file_idx</td>
+ <td>uint</td>
+ <td>index into the <code>string_ids</code> list for the name of the
+ file containing the original source for (at least most of) this class,
+ or the special value <code>NO_INDEX</code> to represent a lack of
+ this information. The <code>debug_info_item</code> of any given method
+ may override this source file, but the expectation is that most classes
+ will only come from one source file.
+ </td>
+</tr>
+<tr>
+ <td>annotations_off</td>
+ <td>uint</td>
+ <td>offset from the start of the file to the annotations structure
+ for this class, or <code>0</code> if there are no annotations on
+ this class. This offset, if non-zero, should be in the
+ <code>data</code> section, and the data there should be in
+ the format specified by "<code>annotations_directory_item</code>" below,
+ with all items referring to this class as the definer.
+ </td>
+</tr>
+<tr>
+ <td>class_data_off</td>
+ <td>uint</td>
+ <td>offset from the start of the file to the associated
+ class data for this item, or <code>0</code> if there is no class
+ data for this class. (This may be the case, for example, if this class
+ is a marker interface.) The offset, if non-zero, should be in the
+ <code>data</code> section, and the data there should be in the
+ format specified by "<code>class_data_item</code>" below, with all
+ items referring to this class as the definer.
+ </td>
+</tr>
+<tr>
+ <td>static_values_off</td>
+ <td>uint</td>
+ <td>offset from the start of the file to the list of initial
+ values for <code>static</code> fields, or <code>0</code> if there
+ are none (and all <code>static</code> fields are to be initialized with
+ <code>0</code> or <code>null</code>). This offset should be in the
+ <code>data</code> section, and the data there should be in the
+ format specified by "<code>encoded_array_item</code>" below. The size
+ of the array must be no larger than the number of <code>static</code>
+ fields declared by this class, and the elements correspond to the
+ <code>static</code> fields in the same order as declared in the
+ corresponding <code>field_list</code>. The type of each array
+ element must match the declared type of its corresponding field.
+ If there are fewer elements in the array than there are
+ <code>static</code> fields, then the leftover fields are initialized
+ with a type-appropriate <code>0</code> or <code>null</code>.
+ </td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>class_data_item</code></h2>
+<h4>referenced from <code>class_def_item</code></h4>
+<h4>appears in the <code>data</code> section</h4>
+<h4>alignment: none (byte-aligned)</h4>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>static_fields_size</td>
+ <td>uleb128</td>
+ <td>the number of static fields defined in this item</td>
+</tr>
+<tr>
+ <td>instance_fields_size</td>
+ <td>uleb128</td>
+ <td>the number of instance fields defined in this item</td>
+</tr>
+<tr>
+ <td>direct_methods_size</td>
+ <td>uleb128</td>
+ <td>the number of direct methods defined in this item</td>
+</tr>
+<tr>
+ <td>virtual_methods_size</td>
+ <td>uleb128</td>
+ <td>the number of virtual methods defined in this item</td>
+</tr>
+<tr>
+ <td>static_fields</td>
+ <td>encoded_field[static_fields_size]</td>
+ <td>the defined static fields, represented as a sequence of
+ encoded elements. The fields must be sorted by
+ <code>field_idx</code> in increasing order.
+ </td>
+</tr>
+<tr>
+ <td>instance_fields</td>
+ <td>encoded_field[instance_fields_size]</td>
+ <td>the defined instance fields, represented as a sequence of
+ encoded elements. The fields must be sorted by
+ <code>field_idx</code> in increasing order.
+ </td>
+</tr>
+<tr>
+ <td>direct_methods</td>
+ <td>encoded_method[direct_methods_size]</td>
+ <td>the defined direct (any of <code>static</code>, <code>private</code>,
+ or constructor) methods, represented as a sequence of
+ encoded elements. The methods must be sorted by
+ <code>method_idx</code> in increasing order.
+ </td>
+</tr>
+<tr>
+ <td>virtual_methods</td>
+ <td>encoded_method[virtual_methods_size]</td>
+ <td>the defined virtual (none of <code>static</code>, <code>private</code>,
+ or constructor) methods, represented as a sequence of
+ encoded elements. This list should <i>not</i> include inherited
+ methods unless overridden by the class that this item represents. The
+ methods must be sorted by <code>method_idx</code> in increasing order.
+ </td>
+</tr>
+</tbody>
+</table>
+
+<p><b>Note:</b> All elements' <code>field_id</code>s and
+<code>method_id</code>s must refer to the same defining class.</p>
+
+<h3><code>encoded_field</code> Format</h3>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>field_idx_diff</td>
+ <td>uleb128</td>
+ <td>index into the <code>field_ids</code> list for the identity of this
+ field (includes the name and descriptor), represented as a difference
+ from the index of previous element in the list. The index of the
+ first element in a list is represented directly.
+ </td>
+</tr>
+<tr>
+ <td>access_flags</td>
+ <td>uleb128</td>
+ <td>access flags for the field (<code>public</code>, <code>final</code>,
+ etc.). See "<code>access_flags</code> Definitions" for details.
+ </td>
+</tr>
+</tbody>
+</table>
+
+<h3><code>encoded_method</code> Format</h3>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>method_idx_diff</td>
+ <td>uleb128</td>
+ <td>index into the <code>method_ids</code> list for the identity of this
+ method (includes the name and descriptor), represented as a difference
+ from the index of previous element in the list. The index of the
+ first element in a list is represented directly.
+ </td>
+</tr>
+<tr>
+ <td>access_flags</td>
+ <td>uleb128</td>
+ <td>access flags for the method (<code>public</code>, <code>final</code>,
+ etc.). See "<code>access_flags</code> Definitions" for details.
+ </td>
+</tr>
+<tr>
+ <td>code_off</td>
+ <td>uleb128</td>
+ <td>offset from the start of the file to the code structure for this
+ method, or <code>0</code> if this method is either <code>abstract</code>
+ or <code>native</code>. The offset should be to a location in the
+ <code>data</code> section. The format of the data is specified by
+ "<code>code_item</code>" below.
+ </td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>type_list</code></h2>
+<h4>referenced from <code>class_def_item</code> and
+<code>proto_id_item</code></h4>
+<h4>appears in the <code>data</code> section</h4>
+<h4>alignment: 4 bytes</h4>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>size</td>
+ <td>uint</td>
+ <td>size of the list, in entries</td>
+</tr>
+<tr>
+ <td>list</td>
+ <td>type_item[size]</td>
+ <td>elements of the list</td>
+</tr>
+</tbody>
+</table>
+
+<h3><code>type_item</code> Format</h3>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>type_idx</td>
+ <td>ushort</td>
+ <td>index into the <code>type_ids</code> list</td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>code_item</code></h2>
+<h4>referenced from <code>method_item</code></h4>
+<h4>appears in the <code>data</code> section</h4>
+<h4>alignment: 4 bytes</h4>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>registers_size</td>
+ <td>ushort</td>
+ <td>the number of registers used by this code</td>
+</tr>
+<tr>
+ <td>ins_size</td>
+ <td>ushort</td>
+ <td>the number of words of incoming arguments to the method that this
+ code is for</td>
+</tr>
+<tr>
+ <td>outs_size</td>
+ <td>ushort</td>
+ <td>the number of words of outgoing argument space required by this
+ code for method invocation
+ </td>
+</tr>
+<tr>
+ <td>tries_size</td>
+ <td>ushort</td>
+ <td>the number of <code>try_item</code>s for this instance. If non-zero,
+ then these appear as the <code>tries</code> array just after the
+ <code>insns</code> in this instance.
+ </td>
+</tr>
+<tr>
+ <td>debug_info_off</td>
+ <td>uint</td>
+ <td>offset from the start of the file to the debug info (line numbers +
+ local variable info) sequence for this code, or <code>0</code> if
+ there simply is no information. The offset, if non-zero, should be
+ to a location in the <code>data</code> section. The format of
+ the data is specified by "<code>debug_info_item</code>" below.
+ </td>
+</tr>
+<tr>
+ <td>insns_size</td>
+ <td>uint</td>
+ <td>size of the instructions list, in 16-bit code units</td>
+</tr>
+<tr>
+ <td>insns</td>
+ <td>ushort[insns_size]</td>
+ <td>actual array of bytecode. The format of code in an <code>insns</code>
+ array is specified by the companion document
+ <a href="dalvik-bytecode.html">"Bytecode for the Dalvik VM"</a>. Note
+ that though this is defined as an array of <code>ushort</code>, there
+ are some internal structures that prefer four-byte alignment. Also,
+ if this happens to be in an endian-swapped file, then the swapping is
+ <i>only</i> done on individual <code>ushort</code>s and not on the
+ larger internal structures.
+ </td>
+</tr>
+<tr>
+ <td>padding</td>
+ <td>ushort <i>(optional)</i> = 0</td>
+ <td>two bytes of padding to make <code>tries</code> four-byte aligned.
+ This element is only present if <code>tries_size</code> is non-zero
+ and <code>insns_size</code> is odd.
+ </td>
+</tr>
+<tr>
+ <td>tries</td>
+ <td>try_item[tries_size] <i>(optional)</i></td>
+ <td>array indicating where in the code exceptions may be caught and
+ how to handle them. Elements of the array must be non-overlapping in
+ range and in order from low to high address. This element is only
+ present if <code>tries_size</code> is non-zero.
+ </td>
+</tr>
+<tr>
+ <td>handlers</td>
+ <td>encoded_catch_handler_list <i>(optional)</i></td>
+ <td>bytes representing a list of lists of catch types and associated
+ handler addresses. Each <code>try_item</code> has a byte-wise offset
+ into this structure. This element is only present if
+ <code>tries_size</code> is non-zero.
+ </td>
+</tr>
+</tbody>
+</table>
+
+<h3><code>try_item</code> Format </h3>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>start_addr</td>
+ <td>uint</td>
+ <td>start address of the block of code covered by this entry. The address
+ is a count of 16-bit code units to the start of the first covered
+ instruction.
+ </td>
+</tr>
+<tr>
+ <td>insn_count</td>
+ <td>ushort</td>
+ <td>number of 16-bit code units covered by this entry. The last code
+ unit covered (inclusive) is <code>start_addr + insn_count - 1</code>.
+ </td>
+</tr>
+<tr>
+ <td>handler_off</td>
+ <td>ushort</td>
+ <td>offset in bytes from the start of the associated encoded handler data
+ to the <code>catch_handler_item</code> for this entry
+ </td>
+</tr>
+</tbody>
+</table>
+
+<h3><code>encoded_catch_handler_list</code> Format</h3>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>size</td>
+ <td>uleb128</td>
+ <td>size of this list, in entries</td>
+</tr>
+<tr>
+ <td>list</td>
+ <td>encoded_catch_handler[handlers_size]</td>
+ <td>actual list of handler lists, represented directly (not as offsets),
+ and concatenated sequentially</td>
+</tr>
+</tbody>
+</table>
+
+<h3><code>encoded_catch_handler</code> Format</h3>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>size</td>
+ <td>sleb128</td>
+ <td>number of catch types in this list. If non-positive, then this is
+ the negative of the number of catch types, and the catches are followed
+ by a catch-all handler. For example: A <code>size</code> of <code>0</code>
+ means that there is a catch-all but no explicitly typed catches.
+ A <code>size</code> of <code>2</code> means that there are two explicitly
+ typed catches and no catch-all. And a <code>size</code> of <code>-1</code>
+ means that there is one typed catch along with a catch-all.
+ </td>
+</tr>
+<tr>
+ <td>handlers</td>
+ <td>encoded_type_addr_pair[abs(size)]</td>
+ <td>stream of <code>abs(size)</code> encoded items, one for each caught
+ type, in the order that the types should be tested.
+ </td>
+</tr>
+<tr>
+ <td>catch_all_addr</td>
+ <td>uleb128 <i>(optional)</i></td>
+ <td>bytecode address of the catch-all handler. This element is only
+ present if <code>size</code> is non-positive.
+ </td>
+</tr>
+</tbody>
+</table>
+
+<h3><code>encoded_type_addr_pair</code> Format</h3>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>type_idx</td>
+ <td>uleb128</td>
+ <td>index into the <code>type_ids</code> list for the type of the
+ exception to catch
+ </td>
+</tr>
+<tr>
+ <td>addr</td>
+ <td>uleb128</td>
+ <td>bytecode address of the associated exception handler</td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>debug_info_item</code></h2>
+<h4>referenced from <code>code_item</code></h4>
+<h4>appears in the <code>data</code> section</h4>
+<h4>alignment: none (byte-aligned)</h4>
+
+<p>Each <code>debug_info_item</code> defines a DWARF3-inspired byte-coded
+state machine that, when interpreted, emits the positions
+table and (potentially) the local variable information for a
+<code>code_item</code>. The sequence begins with a variable-length
+header (the length of which depends on the number of method
+parameters), is followed by the state machine bytecodes, and ends
+with an <code>DBG_END_SEQUENCE</code> byte.</p>
+
+<p>The state machine consists of five registers. The
+<code>address</code> register represents the instruction offset in the
+associated <code>insns_item</code> in 16-bit code units. The
+<code>address</code> register starts at <code>0</code> at the beginning of each
+<code>debug_info</code> sequence and may only monotonically increase.
+The <code>line</code> register represents what source line number
+should be associated with the next positions table entry emitted by
+the state machine. It is initialized in the sequence header, and may
+change in positive or negative directions but must never be less than
+<code>1</code>. The <code>source_file</code> register represents the
+source file that the line number entries refer to. It is initialized to
+the value of <code>source_file_idx</code> in <code>class_def_item</code>.
+The other two variables, <code>prologue_end</code> and
+<code>epilogue_begin</code>, are boolean flags (initialized to
+<code>false</code>) that indicate whether the next position emitted
+should be considered a method prologue or epilogue. The state machine
+must also track the name and type of the last local variable live in
+each register for the <code>DBG_RESTART_LOCAL</code> code.</p>
+
+<p>The header is as follows:</p>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>line_start</td>
+ <td>uleb128</td>
+ <td>the initial value for the state machine's <code>line</code> register.
+ Does not represent an actual positions entry.
+ </td>
+</tr>
+<tr>
+ <td>parameters_size</td>
+ <td>uleb128</td>
+ <td>the number of parameter names that are encoded. There should be
+ one per method parameter, excluding an instance method's <code>this</code>,
+ if any.
+ </td>
+</tr>
+<tr>
+ <td>parameter_names</td>
+ <td>uleb128p1[parameters_size]</td>
+ <td>string index of the method parameter name. An encoded value of
+ <code>NO_INDEX</code> indicates that no name
+ is available for the associated parameter. The type descriptor
+ and signature are implied from the method descriptor and signature.
+ </td>
+</tr>
+</tbody>
+</table>
+
+<p>The byte code values are as follows:</p>
+
+<table class="debugByteCode">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Value</th>
+ <th>Format</th>
+ <th>Arguments</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>DBG_END_SEQUENCE</td>
+ <td>0x00</td>
+ <td></td>
+ <td><i>(none)</i></td>
+ <td>terminates a debug info sequence for a <code>code_item</code></td>
+</tr>
+<tr>
+ <td>DBG_ADVANCE_PC</td>
+ <td>0x01</td>
+ <td>uleb128&nbsp;addr_diff</td>
+ <td><code>addr_diff</code>: amount to add to address register</td>
+ <td>advances the address register without emitting a positions entry</td>
+</tr>
+<tr>
+ <td>DBG_ADVANCE_LINE</td>
+ <td>0x02</td>
+ <td>sleb128&nbsp;line_diff</td>
+ <td><code>line_diff</code>: amount to change line register by</td>
+ <td>advances the line register without emitting a positions entry</td>
+</tr>
+<tr>
+ <td>DBG_START_LOCAL</td>
+ <td>0x03</td>
+ <td>uleb128&nbsp;register_num<br/>
+ uleb128p1&nbsp;name_idx<br/>
+ uleb128p1&nbsp;type_idx
+ </td>
+ <td><code>register_num</code>: register that will contain local<br/>
+ <code>name_idx</code>: string index of the name<br/>
+ <code>type_idx</code>: type index of the type
+ </td>
+ <td>introduces a local variable at the current address. Either
+ <code>name_idx</code> or <code>type_idx</code> may be
+ <code>NO_INDEX</code> to indicate that that value is unknown.
+ </td>
+</tr>
+<tr>
+ <td>DBG_START_LOCAL_EXTENDED</td>
+ <td>0x04</td>
+ <td>uleb128&nbsp;register_num<br/>
+ uleb128p1&nbsp;name_idx<br/>
+ uleb128p1&nbsp;type_idx<br/>
+ uleb128p1&nbsp;sig_idx
+ </td>
+ <td><code>register_num</code>: register that will contain local<br/>
+ <code>name_idx</code>: string index of the name<br/>
+ <code>type_idx</code>: type index of the type<br/>
+ <code>sig_idx</code>: string index of the type signature
+ </td>
+ <td>introduces a local with a type signature at the current address.
+ Any of <code>name_idx</code>, <code>type_idx</code>, or
+ <code>sig_idx</code> may be <code>NO_INDEX</code>
+ to indicate that that value is unknown. (If <code>sig_idx</code> is
+ <code>-1</code>, though, the same data could be represented more
+ efficiently using the opcode <code>DBG_START_LOCAL</code>.)
+ <p><b>Note:</b> See the discussion under
+ "<code>dalvik.annotation.Signature</code>" below for caveats about
+ handling signatures.</p>
+ </td>
+</tr>
+<tr>
+ <td>DBG_END_LOCAL</td>
+ <td>0x05</td>
+ <td>uleb128&nbsp;register_num</td>
+ <td><code>register_num</code>: register that contained local</td>
+ <td>marks a currently-live local variable as out of scope at the current
+ address
+ </td>
+</tr>
+<tr>
+ <td>DBG_RESTART_LOCAL</td>
+ <td>0x06</td>
+ <td>uleb128&nbsp;register_num</td>
+ <td><code>register_num</code>: register to restart</td>
+ <td>re-introduces a local variable at the current address. The name
+ and type are the same as the last local that was live in the specified
+ register.
+ </td>
+</tr>
+<tr>
+ <td>DBG_SET_PROLOGUE_END</td>
+ <td>0x07</td>
+ <td></td>
+ <td><i>(none)</i></td>
+ <td>sets the <code>prologue_end</code> state machine register,
+ indicating that the next position entry that is added should be
+ considered the end of a method prologue (an appropriate place for
+ a method breakpoint). The <code>prologue_end</code> register is
+ cleared by any special (<code>&gt;= 0x0a</code>) opcode.
+ </td>
+</tr>
+<tr>
+ <td>DBG_SET_EPILOGUE_BEGIN</td>
+ <td>0x08</td>
+ <td></td>
+ <td><i>(none)</i></td>
+ <td>sets the <code>epilogue_begin</code> state machine register,
+ indicating that the next position entry that is added should be
+ considered the beginning of a method epilogue (an appropriate place
+ to suspend execution before method exit).
+ The <code>epilogue_begin</code> register is cleared by any special
+ (<code>&gt;= 0x0a</code>) opcode.
+ </td>
+</tr>
+<tr>
+ <td>DBG_SET_FILE</td>
+ <td>0x09</td>
+ <td>uleb128p1&nbsp;name_idx</td>
+ <td><code>name_idx</code>: string index of source file name;
+ <code>NO_INDEX</code> if unknown
+ </td>
+ <td>indicates that all subsequent line number entries make reference to this
+ source file name, instead of the default name specified in
+ <code>code_item</code>
+ </td>
+</tr>
+<tr>
+ <td><i>Special Opcodes</i></td>
+ <!-- When updating the range below, make sure to search for other
+ instances of 0x0a in this section. -->
+ <td>0x0a&hellip;0xff</td>
+ <td></td>
+ <td><i>(none)</i></td>
+ <td>advances the <code>line</code> and <code>address</code> registers,
+ emits a position entry, and clears <code>prologue_end</code> and
+ <code>epilogue_begin</code>. See below for description.
+ </td>
+</tr>
+</tbody>
+</table>
+
+<h3>Special Opcodes</h3>
+
+<p>Opcodes with values between <code>0x0a</code> and <code>0xff</code>
+(inclusive) move both the <code>line</code> and <code>address</code>
+registers by a small amount and then emit a new position table entry.
+The formula for the increments are as follows:</p>
+
+<pre>
+DBG_FIRST_SPECIAL = 0x0a // the smallest special opcode
+DBG_LINE_BASE = -4 // the smallest line number increment
+DBG_LINE_RANGE = 15 // the number of line increments represented
+
+adjusted_opcode = opcode - DBG_FIRST_SPECIAL
+
+line += DBG_LINE_BASE + (adjusted_opcode % DBG_LINE_RANGE)
+address += (adjusted_opcode / DBG_LINE_RANGE)
+</pre>
+
+<h2><code>annotations_directory_item</code></h2>
+<h4>referenced from <code>class_def_item</code></h4>
+<h4>appears in the <code>data</code> section</h4>
+<h4>alignment: 4 bytes</h4>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>class_annotations_off</td>
+ <td>uint</td>
+ <td>offset from the start of the file to the annotations made directly
+ on the class, or <code>0</code> if the class has no direct annotations.
+ The offset, if non-zero, should be to a location in the
+ <code>data</code> section. The format of the data is specified
+ by "<code>annotation_set_item</code>" below.
+ </td>
+</tr>
+<tr>
+ <td>fields_size</td>
+ <td>uint</td>
+ <td>count of fields annotated by this item</td>
+</tr>
+<tr>
+ <td>annotated_methods_size</td>
+ <td>uint</td>
+ <td>count of methods annotated by this item</td>
+</tr>
+<tr>
+ <td>annotated_parameters_size</td>
+ <td>uint</td>
+ <td>count of method parameter lists annotated by this item</td>
+</tr>
+<tr>
+ <td>field_annotations</td>
+ <td>field_annotation[fields_size] <i>(optional)</i></td>
+ <td>list of associated field annotations. The elements of the list must
+ be sorted in increasing order, by <code>field_idx</code>.
+ </td>
+</tr>
+<tr>
+ <td>method_annotations</td>
+ <td>method_annotation[methods_size] <i>(optional)</i></td>
+ <td>list of associated method annotations. The elements of the list must
+ be sorted in increasing order, by <code>method_idx</code>.
+ </td>
+</tr>
+<tr>
+ <td>parameter_annotations</td>
+ <td>parameter_annotation[parameters_size] <i>(optional)</i></td>
+ <td>list of associated method parameter annotations. The elements of the
+ list must be sorted in increasing order, by <code>method_idx</code>.
+ </td>
+</tr>
+</tbody>
+</table>
+
+<p><b>Note:</b> All elements' <code>field_id</code>s and
+<code>method_id</code>s must refer to the same defining class.</p>
+
+<h3><code>field_annotation</code> Format</h3>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>field_idx</td>
+ <td>uint</td>
+ <td>index into the <code>field_ids</code> list for the identity of the
+ field being annotated
+ </td>
+</tr>
+<tr>
+ <td>annotations_off</td>
+ <td>uint</td>
+ <td>offset from the start of the file to the list of annotations for
+ the field. The offset should be to a location in the <code>data</code>
+ section. The format of the data is specified by
+ "<code>annotation_set_item</code>" below.
+ </td>
+</tr>
+</tbody>
+</table>
+
+<h3><code>method_annotation</code> Format</h3>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>method_idx</td>
+ <td>uint</td>
+ <td>index into the <code>method_ids</code> list for the identity of the
+ method being annotated
+ </td>
+</tr>
+<tr>
+ <td>annotations_off</td>
+ <td>uint</td>
+ <td>offset from the start of the file to the list of annotations for
+ the method. The offset should be to a location in the
+ <code>data</code> section. The format of the data is specified by
+ "<code>annotation_set_item</code>" below.
+ </td>
+</tr>
+</tbody>
+</table>
+
+<h3><code>parameter_annotation</code> Format</h2>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>method_idx</td>
+ <td>uint</td>
+ <td>index into the <code>method_ids</code> list for the identity of the
+ method whose parameters are being annotated
+ </td>
+</tr>
+<tr>
+ <td>annotations_off</td>
+ <td>uint</td>
+ <td>offset from the start of the file to the list of annotations for
+ the method parameters. The offset should be to a location in the
+ <code>data</code> section. The format of the data is specified by
+ "<code>annotation_set_ref_list</code>" below.
+ </td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>annotation_set_ref_list</code></h2>
+<h4>referenced from <code>parameter_annotations_item</code></h4>
+<h4>appears in the <code>data</code> section</h4>
+<h4>alignment: 4 bytes</h4>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>size</td>
+ <td>uint</td>
+ <td>size of the list, in entries</td>
+</tr>
+<tr>
+ <td>list</td>
+ <td>annotation_set_ref_item[size]</td>
+ <td>elements of the list</td>
+</tr>
+</tbody>
+</table>
+
+<h3><code>annotation_set_ref_item</code> Format</h3>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>annotations_off</td>
+ <td>uint</td>
+ <td>offset from the start of the file to the referenced annotation set
+ or <code>0</code> if there are no annotations for this element.
+ The offset, if non-zero, should be to a location in the <code>data</code>
+ section. The format of the data is specified by
+ "<code>annotation_set_item</code>" below.
+ </td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>annotation_set_item</code></h2>
+<h4>referenced from <code>annotations_directory_item</code>,
+<code>field_annotations_item</code>,
+<code>method_annotations_item</code>, and
+<code>annotation_set_ref_item</code></h4>
+<h4>appears in the <code>data</code> section</h4>
+<h4>alignment: 4 bytes</h4>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>size</td>
+ <td>uint</td>
+ <td>size of the set, in entries</td>
+</tr>
+<tr>
+ <td>entries</td>
+ <td>annotation_off_item[size]</td>
+ <td>elements of the set. The elements must be sorted in increasing order,
+ by <code>type_idx</code>.
+ </td>
+</tr>
+</tbody>
+</table>
+
+<h3><code>annotation_off_item</code> Format</h3>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>annotation_off</td>
+ <td>uint</td>
+ <td>offset from the start of the file to an annotation.
+ The offset should be to a location in the <code>data</code> section,
+ and the format of the data at that location is specified by
+ "<code>annotation_item</code>" below.
+ </td>
+</tr>
+</tbody>
+</table>
+
+
+<h2><code>annotation_item</code></h2>
+<h4>referenced from <code>annotation_set_item</code></h4>
+<h4>appears in the <code>data</code> section</h4>
+<h4>alignment: none (byte-aligned)</h4>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>visibility</td>
+ <td>ubyte</td>
+ <td>intended visibility of this annotation (see below)</td>
+</tr>
+<tr>
+ <td>annotation</td>
+ <td>encoded_annotation</td>
+ <td>encoded annotation contents, in the format described by
+ "<code>encoded_annotation</code> Format" under
+ "<code>encoded_value</code> Encoding" above.
+ </td>
+</tr>
+</tbody>
+</table>
+
+<h3>Visibility values</h3>
+
+<p>These are the options for the <code>visibility</code> field in an
+<code>annotation_item</code>:</p>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Value</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>VISIBILITY_BUILD</td>
+ <td>0x00</td>
+ <td>intended only to be visible at build time (e.g., during compilation
+ of other code)
+ </td>
+</tr>
+<tr>
+ <td>VISIBILITY_RUNTIME</td>
+ <td>0x01</td>
+ <td>intended to visible at runtime</td>
+</tr>
+<tr>
+ <td>VISIBILITY_SYSTEM</td>
+ <td>0x02</td>
+ <td>intended to visible at runtime, but only to the underlying system
+ (and not to regular user code)
+ </td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>encoded_array_item</code></h2>
+<h4>referenced from <code>class_def_item</code></h4>
+<h4>appears in the <code>data</code> section</h4>
+<h4>alignment: none (byte-aligned)</h4>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>value</td>
+ <td>encoded_array</td>
+ <td>bytes representing the encoded array value, in the format specified
+ by "<code>encoded_array</code> Format" under "<code>encoded_value</code>
+ Encoding" above.
+ </td>
+</tr>
+</tbody>
+</table>
+
+<h1>System Annotations</h1>
+
+<p>System annotations are used to represent various pieces of reflective
+information about classes (and methods and fields). This information is
+generally only accessed indirectly by client (non-system) code.</p>
+
+<p>System annotations are represented in <code>.dex</code> files as
+annotations with visibility set to <code>VISIBILITY_SYSTEM</code>.
+
+<h2><code>dalvik.annotation.AnnotationDefault</code></h2>
+<h4>appears on methods in annotation interfaces</h4>
+
+<p>An <code>AnnotationDefault</code> annotation is attached to each
+annotation interface which wishes to indicate default bindings.</p>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>value</td>
+ <td>Annotation</td>
+ <td>the default bindings for this annotation, represented as an annotation
+ of this type. The annotation need not include all names defined by the
+ annotation; missing names simply do not have defaults.
+ </td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>dalvik.annotation.EnclosingClass</code></h2>
+<h4>appears on classes</h4>
+
+<p>An <code>EnclosingClass</code> annotation is attached to each class
+which is either defined as a member of another class, per se, or is
+anonymous but not defined within a method body (e.g., a synthetic
+inner class). Every class that has this annotation must also have an
+<code>InnerClass</code> annotation. Additionally, a class may not have
+both an <code>EnclosingClass</code> and an
+<code>EnclosingMethod</code> annotation.</p>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>value</td>
+ <td>Class</td>
+ <td>the class which most closely lexically scopes this class</td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>dalvik.annotation.EnclosingMethod</code></h2>
+<h4>appears on classes</h4>
+
+<p>An <code>EnclosingMethod</code> annotation is attached to each class
+which is defined inside a method body. Every class that has this
+annotation must also have an <code>InnerClass</code> annotation.
+Additionally, a class may not have both an <code>EnclosingClass</code>
+and an <code>EnclosingMethod</code> annotation.</p>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>value</td>
+ <td>Method</td>
+ <td>the method which most closely lexically scopes this class</td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>dalvik.annotation.InnerClass</code></h2>
+<h4>appears on classes</h4>
+
+<p>An <code>InnerClass</code> annotation is attached to each class
+which is defined in the lexical scope of another class's definition.
+Any class which has this annotation must also have <i>either</i> an
+<code>EnclosingClass</code> annotation <i>or</i> an
+<code>EnclosingMethod</code> annotation.</p>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>name</td>
+ <td>String</td>
+ <td>the originally declared simple name of this class (not including any
+ package prefix). If this class is anonymous, then the name is
+ <code>null</code>.
+ </td>
+</tr>
+<tr>
+ <td>accessFlags</td>
+ <td>int</td>
+ <td>the originally declared access flags of the class (which may differ
+ from the effective flags because of a mismatch between the execution
+ models of the source language and target virtual machine)
+ </td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>dalvik.annotation.MemberClasses</code></h2>
+<h4>appears on classes</h4>
+
+<p>A <code>MemberClasses</code> annotation is attached to each class
+which declares member classes. (A member class is a direct inner class
+that has a name.)</p>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>value</td>
+ <td>Class[]</td>
+ <td>array of the member classes</td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>dalvik.annotation.Signature</code></h2>
+<h4>appears on classes, fields, and methods</h4>
+
+<p>A <code>Signature</code> annotation is attached to each class,
+field, or method which is defined in terms of a more complicated type
+than is representable by a <code>type_id_item</code>. The
+<code>.dex</code> format does not define the format for signatures; it
+is merely meant to be able to represent whatever signatures a source
+language requires for successful implementation of that language's
+semantics. As such, signatures are not generally parsed (or verified)
+by virtual machine implementations. The signatures simply get handed
+off to higher-level APIs and tools (such as debuggers). Any use of a
+signature, therefore, should be written so as not to make any
+assumptions about only receiving valid signatures, explicitly guarding
+itself against the possibility of coming across a syntactically
+invalid signature.</p>
+
+<p>Because signature strings tend to have a lot of duplicated content,
+a <code>Signature</code> annotation is defined as an <i>array</i> of
+strings, where duplicated elements naturally refer to the same
+underlying data, and the signature is taken to be the concatenation of
+all the strings in the array. There are no rules about how to pull
+apart a signature into separate strings; that is entirely up to the
+tools that generate <code>.dex</code> files.</p>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>value</td>
+ <td>String[]</td>
+ <td>the signature of this class or member, as an array of strings that
+ is to be concatenated together</td>
+</tr>
+</tbody>
+</table>
+
+<h2><code>dalvik.annotation.Throws</code></h2>
+<h4>appears on methods</h4>
+
+<p>A <code>Throws</code> annotation is attached to each method which is
+declared to throw one or more exception types.</p>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>value</td>
+ <td>Class[]</td>
+ <td>the array of exception types thrown</td>
+</tr>
+</tbody>
+</table>
+
+</body>
+</html>
diff --git a/docs/dexopt.html b/docs/dexopt.html
new file mode 100644
index 0000000..7f0b4bc
--- /dev/null
+++ b/docs/dexopt.html
@@ -0,0 +1,326 @@
+<html>
+<head>
+ <title>Dalvik Optimization and Verification</title>
+</head>
+
+<body>
+<h1>Dalvik Optimization and Verification With <i>dexopt</i></h1>
+
+<p>
+The Dalvik virtual machine was designed specifically for the Android
+mobile platform. The target systems have little RAM, store data on slow
+internal flash memory, and generally have the performance characteristics
+of decade-old desktop systems. They also run Linux, which provides
+virtual memory, processes and threads, and UID-based security mechanisms.
+<p>
+The features and limitations caused us to focus on certain goals:
+
+<ul>
+ <li>Class data, notably bytecode, must be shared between multiple
+ processes to minimize total system memory usage.
+ <li>The overhead in launching a new app must be minimized to keep
+ the device responsive.
+ <li>Storing class data in individual files results in a lot of
+ redundancy, especially with respect to strings. To conserve disk
+ space we need to factor this out.
+ <li>Parsing class data fields adds unnecessary overhead during
+ class loading. Accessing data values (e.g. integers and strings)
+ directly as C types is better.
+ <li>Bytecode verification is necessary, but slow, so we want to verify
+ as much as possible outside app execution.
+ <li>Bytecode optimization (quickened instructions, method pruning) is
+ important for speed and battery life.
+ <li>For security reasons, processes may not edit shared code.
+</ul>
+
+<p>
+The typical VM implementation uncompresses individual classes from a
+compressed archive and stores them on the heap. This implies a separate
+copy of each class in every process, and slows application startup because
+the code must be uncompressed (or at least read off disk in many small
+pieces). On the other hand, having the bytecode on the local heap makes
+it easy to rewrite instructions on first use, facilitating a number of
+different optimizations.
+<p>
+The goals led us to make some fundamental decisions:
+
+<ul>
+ <li>Multiple classes are aggregated into a single "DEX" file.
+ <li>DEX files are mapped read-only and shared between processes.
+ <li>Byte ordering and word alignment are adjusted to suit the local
+ system.
+ <li>Bytecode verification is mandatory for all classes, but we want
+ to "pre-verify" whatever we can.
+ <li>Optimizations that require rewriting bytecode must be done ahead
+ of time.
+</ul>
+
+<p>
+The consequences of these decisions are explained in the following sections.
+
+
+<h2>VM Operation</h2>
+
+<p>
+Application code is delivered to the system in a <code>.jar</code>
+or <code>.apk</code> file. These are really just <code>.zip</code>
+archives with some meta-data files added. The Dalvik DEX data file
+is always called <code>classes.dex</code>.
+<p>
+The bytecode cannot be memory-mapped and executed directly from the zip
+file, because the data is compressed and the start of the file is not
+guaranteed to be word-aligned. These problems could be addressed by
+storing <code>classes.dex</code> without compression and padding out the zip
+file, but that would increase the size of the package sent across the
+data network.
+<p>
+We need to extract <code>classes.dex</code> from the zip archive before
+we can use it. While we have the file available, we might as well perform
+some of the other actions (realignment, optimization, verification) described
+earlier. This raises a new question however: who is responsible for doing
+this, and where do we keep the output?
+
+<h3>Preparation</h3>
+
+<p>
+There are at least three different ways to create a "prepared" DEX file,
+sometimes known as "ODEX" (for Optimized DEX):
+<ol>
+ <li>The VM does it "just in time". The output goes into a special
+ <code>dalvik-cache</code> directory. This works on the desktop and
+ engineering-only device builds where the permissions on the
+ <code>dalvik-cache</code> directory are not restricted. On production
+ devices, this is not allowed.
+ <li>The system installer does it when an application is first added.
+ It has the privileges required to write to <code>dalvik-cache</code>.
+ <li>The build system does it ahead of time. The relevant <code>jar</code>
+ / <code>apk</code> files are present, but the <code>classes.dex</code>
+ is stripped out. The optimized DEX is stored next to the original
+ zip archive, not in <code>dalvik-cache</code>, and is part of the
+ system image.
+</ol>
+<p>
+The <code>dalvik-cache</code> directory is more accurately
+<code>$ANDROID_DATA/data/dalvik-cache</code>. The files inside it have
+names derived from the full path of the source DEX. On the device the
+directory is owned by <code>system</code> / <code>system</code>
+and has 0771 permissions, and the optimized DEX files stored there are
+owned by <code>system</code> and the
+application's group, with 0644 permissions. DRM-locked applications will
+use 640 permissions to prevent other user applications from examining them.
+The bottom line is that you can read your own DEX file and those of most
+other applications, but you cannot create, modify, or remove them.
+<p>
+Preparation of the DEX file for the "just in time" and "system installer"
+approaches proceeds in three steps:
+<p>
+First, the dalvik-cache file is created. This must be done in a process
+with appropriate privileges, so for the "system installer" case this is
+done within <code>installd</code>, which runs as root.
+<p>
+Second, the <code>classes.dex</code> entry is extracted from the the zip
+archive. A small amount of space is left at the start of the file for
+the ODEX header.
+<p>
+Third, the file is memory-mapped for easy access and tweaked for use on
+the current system. This includes byte-swapping and structure realigning,
+but no meaningful changes to the DEX file. We also do some basic
+structure checks, such as ensuring that file offsets and data indices
+fall within valid ranges.
+<p>
+The build system uses a hairy process that involves starting the
+emulator, forcing just-in-time optimization of all relevant DEX files,
+and then extracting the results from <code>dalvik-cache</code>. The
+reasons for doing this, rather than using a tool that runs on the desktop,
+will become more apparent when the optimizations are explained.
+<p>
+Once the code is byte-swapped and aligned, we're ready to go. We append
+some pre-computed data, fill in the ODEX header at the start of the file,
+and start executing. (The header is filled in last, so that we don't
+try to use a partial file.) If we're interested in verification and
+optimization, however, we need to insert a step after the initial prep.
+
+<h3>dexopt</h3>
+
+<p>
+We want to verify and optimize all of the classes in the DEX file. The
+easiest and safest way to do this is to load all of the classes into
+the VM and run through them. Anything that fails to load is simply not
+verified or optimized. Unfortunately, this can cause allocation of some
+resources that are difficult to release (e.g. loading of native shared
+libraries), so we don't want to do it in the same virtual machine that
+we're running applications in.
+<p>
+The solution is to invoke a program called <code>dexopt</code>, which
+is really just a back door into the VM. It performs an abbreviated VM
+initialization, loads zero or more DEX files from the bootstrap class
+path, and then sets about verifying and optimizing whatever it can from
+the target DEX. On completion, the process exits, freeing all resources.
+<p>
+It is possible for multiple VMs to want the same DEX file at the same
+time. File locking is used to ensure that dexopt is only run once.
+
+
+<h2>Verification</h2>
+
+<p>
+The bytecode verification process involves scanning through the instructions
+in every method in every class in a DEX file. The goal is to identify
+illegal instruction sequences so that we don't have to check for them at
+run time. Many of the computations involved are also necessary for "exact"
+garbage collection. See
+<a href="verifier.html">Dalvik Bytecode Verifier Notes</a> for more
+information.
+<p>
+For performance reasons, the optimizer (described in the next section)
+assumes that the verifier has run successfully, and makes some potentially
+unsafe assumptions. By default, Dalvik insists upon verifying all classes,
+and only optimizes classes that have been verified. If you want to
+disable the verifier, you can use command-line flags to do so. See also
+<a href="embedded-vm-control.html"> Controlling the Embedded VM</a>
+for instructions on controlling these
+features within the Android application framework.
+<p>
+Reporting of verification failures is a tricky issue. For example,
+calling a package-scope method on a class in a different package is
+illegal and will be caught by the verifier. We don't necessarily want
+to report it during verification though -- we actually want to throw
+an exception when the method call is attempted. Checking the access
+flags on every method call is expensive though. The
+<a href="verifier.html">Dalvik Bytecode Verifier Notes</a> document
+addresses this issue.
+<p>
+Classes that have been verified successfully have a flag set in the ODEX.
+They will not be re-verified when loaded. The Linux access permissions
+are expected to prevent tampering; if you can get around those, installing
+faulty bytecode is far from the easiest line of attack. The ODEX file has
+a 32-bit checksum, but that's chiefly present as a quick check for
+corrupted data.
+
+
+<h2>Optimization</h2>
+
+<p>
+Virtual machine interpreters typically perform certain optimizations the
+first time a piece of code is used. Constant pool references are replaced
+with pointers to internal data structures, operations that always succeed
+or always work a certain way are replaced with simpler forms. Some of
+these require information only available at runtime, others can be inferred
+statically when certain assumptions are made.
+<p>
+The Dalvik optimizer does the following:
+<ul>
+ <li>For virtual method calls, replace the method index with a
+ vtable index.
+ <li>For instance field get/put, replace the field index with
+ a byte offset. Also, merge the boolean / byte / char / short
+ variants into a single 32-bit form (less code in the interpreter
+ means more room in the CPU I-cache).
+ <li>Replace a handful of high-volume calls, like String.length(),
+ with "inline" replacements. This skips the usual method call
+ overhead, directly switching from the interpreter to a native
+ implementation.
+ <li>Prune empty methods. The simplest example is
+ <code>Object.&lt;init&gt;</code>, which does nothing, but must be
+ called whenever any object is allocated. The instruction is
+ replaced with a new version that acts as a no-op unless a debugger
+ is attached.
+ <li>Append pre-computed data. For example, the VM wants to have a
+ hash table for lookups on class name. Instead of computing this
+ when the DEX file is loaded, we can compute it now, saving heap
+ space and computation time in every VM where the DEX is loaded.
+</ul>
+
+<p>
+All of the instruction modifications involve replacing the opcode with
+one not defined by the Dalvik specification. This allows us to freely
+mix optimized and unoptimized instructions. The set of optimized
+instructions, and their exact representation, is tied closely to the VM
+version.
+<p>
+Most of the optimizations are obvious "wins". The use of raw indices
+and offsets not only allows us to execute more quickly, we can also
+skip the initial symbolic resolution. Pre-computation eats up
+disk space, and so must be done in moderation.
+<p>
+There are a couple of potential sources of trouble with these
+optimizations. First, vtable indices and byte offsets are subject to
+change if the VM is updated. Second, if a superclass is in a different
+DEX, and that other DEX is updated, we need to ensure that our optimized
+indices and offsets are updated as well. A similar but more subtle
+problem emerges when user-defined class loaders are employed: the class
+we actually call may not be the one we expected to call.
+<p>These problems are addressed with dependency lists and some limitations
+on what can be optimized.
+
+
+<h2>Dependencies and Limitations</h2>
+
+<p>
+The optimized DEX file includes a list of dependencies on other DEX files,
+plus the CRC-32 and modification date from the originating
+<code>classes.dex</code> zip file entry. The dependency list includes the
+full path to the <code>dalvik-cache</code> file, and the file's SHA-1
+signature. The timestamps of files on the device are unreliable and
+not used. The dependency area also includes the VM version number.
+<p>
+An optimized DEX is dependent upon all of the DEX files in the bootstrap
+class path. DEX files that are part of the bootstrap class path depend
+upon the DEX files that appeared earlier. To ensure that nothing outside
+the dependent DEX files is available, <code>dexopt</code> only loads the
+bootstrap classes. References to classes in other DEX files fail, which
+causes class loading and/or verification to fail, and classes with
+external dependencies are simply not optimized.
+<p>
+This means that splitting code out into many separate DEX files has a
+disadvantage: virtual method calls and instance field lookups between
+non-boot DEX files can't be optimized. Because verification is pass/fail
+with class granularity, no method in a class that has any reliance on
+classes in external DEX files can be optimized. This may be a bit
+heavy-handed, but it's the only way to guarantee that nothing breaks
+when individual pieces are updated.
+<p>
+Another negative consequence: any change to a bootstrap DEX will result
+in rejection of all optimized DEX files. This makes it hard to keep
+system updates small.
+<p>
+Despite our caution, there is still a possibility that a class in a DEX
+file loaded by a user-defined class loader could ask for a bootstrap class
+(say, String) and be given a different class with the same name. If a
+class in the DEX file being processed has the same name as a class in the
+bootstrap DEX files, the class will be flagged as ambiguous and references
+to it will not be resolved during verification / optimization. The class
+linking code in the VM does additional checks to plug another hole;
+see the verbose description in the VM sources for details (vm/oo/Class.c).
+<p>
+If one of the dependencies is updated, we need to re-verify and
+re-optimize the DEX file. If we can do a just-in-time <code>dexopt</code>
+invocation, this is easy. If we have to rely on the installer daemon, or
+the DEX was shipped only in ODEX, then the VM has to reject the DEX.
+<p>
+The output of <code>dexopt</code> is byte-swapped and struct-aligned
+for the host, and contains indices and offsets that are highly VM-specific
+(both version-wise and platform-wise). For this reason it's tricky to
+write a version of <code>dexopt</code> that runs on the desktop but
+generates output suitable for a particular device. The safest way to
+invoke it is on the target device, or on an emulator for that device.
+
+
+<h2>Generated DEX</h2>
+
+<p>
+Some languages and frameworks rely on the ability to generate bytecode
+and execute it. The rather heavy <code>dexopt</code> verification and
+optimization model doesn't work well with that.
+<p>
+We intend to support this in a future release, but the exact method is
+to be determined. We may allow individual classes to be added or whole
+DEX files; may allow Java bytecode or Dalvik bytecode in instructions;
+may perform the usual set of optimizations, or use a separate interpreter
+that performs on-first-use optimizations directly on the bytecode (which
+won't be mapped read-only, since it's locally defined).
+
+<address>Copyright &copy; 2008 The Android Open Source Project</address>
+
+</body>
+</html>
diff --git a/docs/embedded-vm-control.html b/docs/embedded-vm-control.html
new file mode 100644
index 0000000..ec2b694
--- /dev/null
+++ b/docs/embedded-vm-control.html
@@ -0,0 +1,310 @@
+<html>
+<head>
+ <title>Controlling the Embedded VM</title>
+ <link rel=stylesheet href="android.css">
+</head>
+
+<body>
+<h1>Controlling the Embedded VM</h1>
+
+<ul>
+ <li><a href="#introduction">Introduction</a> (read this first!)
+ <li><a href="#checkjni">Extended JNI Checks</a>
+ <li><a href="#assertions">Assertions</a>
+ <li><a href="#verifier">Bytecode Verification and Optimization</a>
+ <li><a href="#execmode">Execution Mode</a>
+ <li><a href="#dp">Deadlock Prediction</a>
+ <li><a href="#stackdump">Stack Dumps</a>
+ <li><a href="#dexcheck">DEX File Checksums</a>
+ <li><a href="#general">General Flags</a>
+</ul>
+
+<h2><a name="introduction">Introduction (read this first!)</a></h2>
+
+<p>The Dalvik VM supports a variety of command-line arguments
+(use <code>adb shell dalvikvm -help</code> to get a summary), but
+it's not possible to pass arbitrary arguments through the
+Android application runtime. It is, however, possible to affect the
+VM behavior through certain system properties.
+
+<p>For all of the features described below, you would set the system property
+with <code>setprop</code>,
+issuing a shell command on the device like this:
+<pre>adb shell setprop &lt;name&gt; &lt;value&gt;</pre>
+
+<p><strong>The Android runtime must be restarted before the changes will take
+effect</strong> (<code>adb shell stop; adb shell start</code>). This is because the
+settings are processed in the "zygote" process, which starts early and stays
+around "forever".
+
+<p>You may not be able to set <code>dalvik.*</code> properties or restart
+the system as an unprivileged user. You can use
+<code>adb root</code> or run the <code>su</code> command from the device
+shell on "userdebug" builds to become root first. When in doubt,
+<pre>adb shell getprop &lt;name&gt;</pre>
+will tell you if the <code>setprop</code> took.
+
+<p>If you don't want the property to evaporate when the device reboots,
+add a line to <code>/data/local.prop</code> that looks like:
+<pre>&lt;name&gt; = &lt;value&gt;</pre>
+
+<p>Such changes will survive reboots, but will be lost if the data
+partition is wiped. (Hint: create a <code>local.prop</code>
+on your workstation, then <code>adb push local.prop /data</code>. Or,
+use one-liners like
+<code>adb shell "echo name = value &gt;&gt; /data/local.prop"</code> -- note
+the quotes are important.)
+
+
+<h2><a name="checkjni">Extended JNI Checks</a></h2>
+
+<p>JNI, the Java Native Interface, provides a way for code written in the
+Java programming language
+interact with native (C/C++) code. The extended JNI checks will cause
+the system to run more slowly, but they can spot a variety of nasty bugs
+before they have a chance to cause problems.
+
+<p>There are two system properties that affect this feature, which is
+enabled with the <code>-Xcheck:jni</code> command-line argument. The
+first is <code>ro.kernel.android.checkjni</code>. This is set by the
+Android build system for development builds. (It may also be set by
+the Android emulator unless the <code>-nojni</code> flag is provided on the
+emulator command line.) Because this is an "ro." property, the value cannot
+be changed once the device has started.
+
+<p>To allow toggling of the CheckJNI flag, a second
+property, <code>dalvik.vm.checkjni</code>, is also checked. The value
+of this overrides the value from <code>ro.kernel.android.checkjni</code>.
+
+<p>If neither property is defined, or <code>dalvik.vm.checkjni</code>
+is set to <code>false</code>, the <code>-Xcheck:jni</code> flag is
+not passed in, and JNI checks will be disabled.
+
+<p>To enable JNI checking:
+<pre>adb shell setprop dalvik.vm.checkjni true</pre>
+
+<p>You can also pass JNI-checking options into the VM through a system
+property. The value set for <code>dalvik.vm.jniopts</code> will
+be passed in as the <code>-Xjniopts</code> argument. For example:
+<pre>adb shell setprop dalvik.vm.jniopts forcecopy</pre>
+
+<p>For more information about JNI checks, see
+<a href="jni-tips.html">JNI Tips</a>.
+
+
+<h2><a name="assertions">Assertions</a></h2>
+
+<p>Dalvik VM supports the Java programming language "assert" statement.
+By default they are off, but the <code>dalvik.vm.enableassertions</code>
+property provides a way to set the value for a <code>-ea</code> argument.
+
+<p>The argument behaves the same as it does in other desktop VMs. You
+can provide a class name, a package name (followed by "..."), or the
+special value "all".
+
+<p>For example, this:
+<pre>adb shell setprop dalvik.vm.enableassertions all</pre>
+enables assertions in all non-system classes.
+
+<p>The system property is much more limited than the full command line.
+It is not possible to specify more than one <code>-ea</code> entry, and there
+is no way to specify a <code>-da</code> entry. There is presently no
+equivalent for <code>-esa</code>/<code>-dsa</code>.
+
+
+<h2><a name="verifier">Bytecode Verification and Optimization</a></h2>
+
+<p>The system tries to pre-verify all classes in a DEX file to reduce
+class load overhead, and performs a series of optimizations to improve
+runtime performance. Both of these are done by the <code>dexopt</code>
+command, either in the build system or by the installer. On a development
+device, <code>dexopt</code> may be run the first time a DEX file is used
+and whenever it or one of its dependencies is updated ("just-in-time"
+optimization and verification).
+
+<p>There are two command-line flags that control the just-in-time
+verification and optimization,
+<code>-Xverify</code> and <code>-Xdexopt</code>. The Android framework
+configures these based on the <code>dalvik.vm.dexopt-flags</code>
+property.
+
+<p>If you set:
+<pre>adb shell setprop dalvik.vm.dexopt-flags v=a,o=v</pre>
+then the framework will pass <code>-Xverify:all -Xdexopt:verified</code>
+to the VM. This enables verification, and only optimizes classes that
+successfully verified. This is the safest setting, and is the default.
+<p>You could also set <code>dalvik.vm.dexopt-flags</code> to <code>v=n</code>
+to have the framework pass <code>-Xverify:none -Xdexopt:verified</code>
+to disable verification. (We could pass in <code>-Xdexopt:all</code> to
+allow optimization, but that wouldn't necessarily optimize more of the
+code, since classes that fail verification may well be skipped by the
+optimizer for the same reasons.) Classes will not be verified by
+<code>dexopt</code>, and unverified code will be loaded and executed.
+
+<p>Enabling verification will make the <code>dexopt</code> command
+take significantly longer, because the verification process is fairly slow.
+Once the verified and optimized DEX files have been prepared, verification
+incurs no additional overhead except when loading classes that failed
+to pre-verify.
+
+<p>If your DEX files are processed with verification disabled, and you
+later turn the verifier on, application loading will be noticeably
+slower (perhaps 40% or more) as classes are verified on first use.
+
+<p>For best results you should force a re-dexopt of all DEX files when
+this property changes. You can do this with:
+<pre>adb shell "rm /data/dalvik-cache/*"</pre>
+This removes the cached versions of the DEX files. Remember to
+stop and restart the runtime (<code>adb shell stop; adb shell start</code>).
+
+<p>(Previous version of the runtime supported the boolean
+<code>dalvik.vm.verify-bytecode</code> property, but that has been
+superceded by <code>dalvik.vm.dexopt-flags</code>.)</p>
+
+
+<h2><a name="execmode">Execution Mode</a></h2>
+
+<p>The current implementation of the Dalvik VM includes three distinct
+interpreter cores. These are referred to as "fast", "portable", and
+"debug". The "fast" interpreter is optimized for the current
+platform, and might consist of hand-optimized assembly routines. In
+constrast, the "portable" interpreter is written in C and expected to
+run on a broad range of platforms. The "debug" interpreter is a variant
+of "portable" that includes support for profiling and single-stepping.
+
+<p>The VM may also support just-in-time compilation. While not strictly
+a different interpreter, the JIT compiler may be enabled or disabled
+with the same flag. (Check the output of <code>dalvikvm -help</code> to
+see if JIT compilation is enabled in your VM.)
+
+<p>The VM allows you to choose between "fast", "portable", and "jit" with an
+extended form of the <code>-Xint</code> argument. The value of this
+argument can be set through the <code>dalvik.vm.execution-mode</code>
+system property.
+
+<p>To select the "portable" interpreter, you would use:
+<pre>adb shell setprop dalvik.vm.execution-mode int:portable</pre>
+If the property is not specified, the most appropriate interpreter
+will be selected automatically. At some point this mechanism may allow
+selection of other modes, such as JIT compilation.
+
+<p>Not all platforms have an optimized implementation. In such cases,
+the "fast" interpreter is generated as a series of C stubs, and the
+result will be slower than the
+"portable" version. (When we have optimized versions for all popular
+architectures the naming convention will be more accurate.)
+
+<p>If profiling is enabled or a debugger is attached, the VM
+switches to the "debug" interpreter. When profiling ends or the debugger
+disconnects, the original interpreter is resumed. (The "debug" interpreter
+is substantially slower, something to keep in mind when evaluating
+profiling data.)
+
+<p>The JIT compiler can be disabled on a per-application basis by adding
+<code>android:vmSafeMode="true"</code> in the <code>application</code>
+tag in <code>AndroidManifest.xml</code>. This can be useful if you
+suspect that JIT compilation is causing your application to behave
+incorrectly.
+
+
+<h2><a name="dp">Deadlock Prediction</a></h2>
+
+<p>If the VM is built with <code>WITH_DEADLOCK_PREDICTION</code>, the deadlock
+predictor can be enabled with the <code>-Xdeadlockpredict</code> argument.
+(The output from <code>dalvikvm -help</code> will tell you if the VM was
+built appropriately -- look for <code>deadlock_prediction</code> on the
+<code>Configured with:</code> line.)
+This feature tells the VM to keep track of the order in which object
+monitor locks are acquired. If the program attempts to acquire a set
+of locks in a different order from what was seen earlier, the VM logs
+a warning and optionally throws an exception.
+
+<p>The command-line argument is set based on the
+<code>dalvik.vm.deadlock-predict</code> property. Valid values are
+<code>off</code> to disable it (default), <code>warn</code> to log the
+problem but continue executing, <code>err</code> to cause a
+<code>dalvik.system.PotentialDeadlockError</code> to be thrown from the
+<code>monitor-enter</code> instruction, and <code>abort</code> to have
+the entire VM abort.
+
+<p>You will usually want to use:
+<pre>adb shell setprop dalvik.vm.deadlock-predict err</pre>
+unless you are keeping an eye on the logs as they scroll by.
+
+<p>Please note that this feature is deadlock prediction, not deadlock
+detection -- in the current implementation, the computations are performed
+after the lock is acquired (this simplifies the code, reducing the
+overhead added to every mutex operation). You can spot a deadlock in a
+hung process by sending a <code>kill -3</code> and examining the stack
+trace written to the log.
+
+<p>This only takes monitors into account. Native mutexes and other resources
+can also be the cause of deadlocks, but will not be detected by this.
+
+
+<h2><a name="stackdump">Stack Dumps</a></h2>
+
+<p>Like other desktop VMs, when the Dalvik VM receives a SIGQUIT
+(Ctrl-\ or <code>kill -3</code>), it dumps stack traces for all threads.
+By default this goes to the Android log, but it can also be written to a file.
+
+<p>The <code>dalvik.vm.stack-trace-file</code> property allows you to
+specify the name of the file where the thread stack traces will be written.
+The file will be created (world writable) if it doesn't exist, and the
+new information will be appended to the end of the file. The filename
+is passed into the VM via the <code>-Xstacktracefile</code> argument.
+
+<p>For example:
+<pre>adb shell setprop dalvik.vm.stack-trace-file /tmp/stack-traces.txt</pre>
+
+<p>If the property is not defined, the VM will write the stack traces to
+the Android log when the signal arrives.
+
+
+<h2><a name="dexcheck">DEX File Checksums</a></h2>
+
+<p>For performance reasons, the checksum on "optimized" DEX files is
+ignored. This is usually safe, because the files are generated on the
+device, and have access permissions that prevent modification.
+
+<p>If the storage on a device becomes unreliable, however, data corruption
+can occur. This usually manifests itself as a repeatable virtual machine
+crash. To speed diagnosis of such failures, the VM provides the
+<code>-Xcheckdexsum</code> argument. When set, the checksums on all DEX
+files are verified before the contents are used.
+
+<p>The application framework will provide this argument during VM
+creation if the <code>dalvik.vm.check-dex-sum</code> property is enabled.
+
+<p>To enable extended DEX checksum verification:
+<pre>adb shell setprop dalvik.vm.check-dex-sum true</pre>
+
+<p>Incorrect checksums will prevent the DEX data from being used, and will
+cause errors to be written to the log file. If a device has a history of
+problems it may be useful to add the property to
+<code>/data/local.prop</code>.
+
+<p>Note also that the
+<code>dexdump</code> tool always verifies DEX checksums, and can be used
+to check for corruption in a large set of files.
+
+
+<h2><a name="general">General Flags</a></h2>
+
+<p>In the "Honeycomb" release, a general mechanism for passing flags to
+the VM was introduced:
+
+<pre>adb shell setprop dalvik.vm.extra-opts "flag1 flag2 ... flagN"</pre>
+
+<p>The flags are separated by spaces. You can specify as many as you want
+so long as they all fit within the system property value length limit
+(currently 92 characters).
+
+<p>The extra-opts flags will be added at the end of the command line,
+which means they will override earlier settings. This can be used, for
+example, to experiment with different values for <code>-Xmx</code> even
+though the Android framework is setting it explicitly.
+
+<address>Copyright &copy; 2008 The Android Open Source Project</address>
+
+</body></html>
diff --git a/docs/heap-profiling.html b/docs/heap-profiling.html
new file mode 100644
index 0000000..9b9a58f
--- /dev/null
+++ b/docs/heap-profiling.html
@@ -0,0 +1,199 @@
+<html>
+<head>
+ <title>Dalvik Heap Profiling</title>
+</head>
+
+<body>
+<h1>Dalvik Heap Profiling</h1>
+
+<p>
+The Dalvik virtual machine can produce a complete dump of the contents
+of the virtual heap. This is very useful for debugging memory usage
+and looking for memory leaks. Getting at the information can be tricky,
+but has become easier in recent releases.
+</p><p>
+In what follows, the version number refers to the software release
+running on the phone. To take advantage of the DDMS integration, you will
+also need a sufficiently recent version of DDMS.
+
+
+<h2>Getting the data</h2>
+<p>
+The first step is to cause the VM to dump its status, and then pull the hprof
+data off. The exact manner for doing so has changed over time.
+</p><p>
+There is a <code>runhat</code> shell function, added by
+<code>build/envsetup.sh</code>, that partially automates these steps. The
+function changes in each release to accommodate newer behavior, so you have
+to be careful that you don't use the wrong version.
+</p><p>
+
+<h3>Early releases (1.0/1.1)</h3>
+<p>
+You can only generate heap data on the emulator or a device with root
+access, because of the way the dump is initiated and where the output
+files go.
+</p><p>
+Get a command shell on the device:
+<blockquote><pre>
+$ adb shell
+</pre></blockquote>
+</p><p>
+You can verify that you're running as root with the <code>id</code> command.
+The response should look like <code>uid=0(root) gid=0(root)</code>. If not,
+type <code>su</code> and try again. If <code>su</code> fails, you're out
+of luck.
+
+</p><p>
+Next, ensure the target directory exists:
+<blockquote><pre>
+# mkdir /data/misc
+# chmod 777 /data/misc
+</pre></blockquote>
+
+</p><p>
+Use <code>ps</code> or DDMS to determine the process ID of your application,
+then send a <code>SIGUSR1</code> to the target process:
+
+<blockquote><pre>
+# kill -10 &lt;pid&gt;
+</pre></blockquote>
+
+</p><p>
+The signal causes a GC, followed by the heap dump (to be completely
+accurate, they actually happen concurrently, but the results in the heap
+dump reflect the post-GC state). This can take a couple of seconds,
+so you have to watch for the GC log message to know when it's complete.
+</p><p>
+Next:
+
+<blockquote><pre>
+# ls /data/misc/heap-dump*
+# exit
+</pre></blockquote>
+
+</p><p>
+Use <code>ls</code> to check the file names, then <code>exit</code> to quit
+the device command shell.
+
+</p><p>
+You should see two output files, named
+<code>/data/misc/heap-dump-BLAH-BLAH.hprof</code> and
+<code>.hprof-head</code>, where BLAH is a runtime-generated value
+that ensures the filename is unique. Pull them off of the device and
+remove the device-side copy:
+
+<blockquote><pre>
+$ adb pull /data/misc/heap-dump-BLAH-BLAH.hprof tail.hprof
+$ adb pull /data/misc/heap-dump-BLAH-BLAH.hprof-head head.hprof
+$ adb shell rm /data/misc/heap-dump-BLAH-BLAH.hprof /data/misc/heap-dump-BLAH-BLAH.hprof-head
+</pre></blockquote>
+
+</p><p>
+Merge them together and remove the intermediates:
+
+<blockquote><pre>
+$ cat head.hprof tail.hprof &gt; dump.hprof
+$ rm head.hprof tail.hprof
+</pre></blockquote>
+
+</p><p>
+You now have the hprof dump in <code>dump.hprof</code>.
+</p><p>
+
+
+<h3>Android 1.5 ("Cupcake")</h3>
+<p>
+Some steps were taken to make this simpler. Notably, the two output
+files are now combined for you, and a new API call was added that allows
+a program to write the dump at will to a specific file. If you're not
+using the API call, you still need to be on an emulator or running as root.
+(For some builds, you can use <code>adb root</code> to restart the adb
+daemon as root.)
+</p><p>
+The basic procedure is the same as for 1.0/1.1, but only one file will
+appear in <code>/data/misc</code> (no <code>-head</code>), and upon
+completion you will see a log message that says "hprof: heap dump completed".
+It looks like this in the log:
+
+<blockquote><pre>
+I/dalvikvm( 289): threadid=7: reacting to signal 10
+I/dalvikvm( 289): SIGUSR1 forcing GC and HPROF dump
+I/dalvikvm( 289): hprof: dumping VM heap to "/data/misc/heap-dump-tm1240861355-pid289.hprof-hptemp".
+I/dalvikvm( 289): hprof: dumping heap strings to "/data/misc/heap-dump-tm1240861355-pid289.hprof".
+I/dalvikvm( 289): hprof: heap dump completed, temp file removed
+</pre></blockquote>
+
+</p><p>
+Summary: as above, use <code>mkdir</code> and <code>chmod</code>
+to ensure the directory exists and is writable by your application.
+Send the <code>SIGUSR1</code> or use the API call to initiate a dump.
+Use <code>adb pull &lt;dump-file&gt;</code> and <code>adb shell rm
+&lt;dump-file&gt;</code> to retrieve the file and remove it from the
+device. The concatenation step is not needed.
+
+</p><p>
+The new API is in the <code>android.os.Debug</code> class:
+<blockquote><pre>
+public static void dumpHprofData(String fileName) throws IOException
+</pre></blockquote>
+When called, the VM will go through the same series of steps (GC and
+generate a .hprof file), but the output will be written to a file of
+your choice, e.g. <code>/sdcard/myapp.hprof</code>. Because you're
+initiating the action from within the app, and can write the file to
+removable storage or the app's private data area, you can do this on a
+device without root access.
+
+
+<h3>Android 1.6 ("Donut")</h3>
+<p>
+No real change to the way profiling works.
+However, 1.6 introduced the <code>WRITE_EXTERNAL_STORAGE</code>
+permission, which is required to write data to the SD card. If you're
+accustomed to writing profile data to <code>/sdcard</code>, you will
+need to enable the permission in your application's manifest.
+</p>
+
+
+<h3>Android 2.0 ("Eclair")</h3>
+<p>
+In 2.0, features were added that allow DDMS to request a heap dump on
+demand, and automatically pull the result across. Select your application
+and click the "dump HPROF file" button in the top left. This always
+writes files to the SD card, so
+you must have a card inserted and the permission enabled in your application.
+</p>
+
+
+<h3>Android 2.2 ("Froyo")</h3>
+<p>
+DDMS heap dump requests are now streamed directly out of the VM, removing
+the external storage requirement.
+</p>
+
+<h3>Android 2.3 ("Gingerbread")</h3>
+<p>
+The <code>kill -10</code> (<code>SIGUSR1</code>) method of generating heap
+dumps has been removed from the VM.
+</p>
+
+<h2>Examining the data</h2>
+<p>
+The data file format was augmented slightly from the common hprof format,
+and due to licensing restrictions the modified <code>hat</code> tool cannot
+be distributed. A conversion tool, <code>hprof-conv</code>, can be used
+to strip the Android-specific portions from the output. This tool was
+first included in 1.5, but will work with older versions of Android.
+</p><p>
+The converted output should work with any hprof data analyzer, including
+<code>jhat</code>, which is available for free in the Sun JDK, and
+Eclipse MAT.
+
+<!-- say something about how to track down common problems, interesting
+ things to look for, ...? -->
+
+</p><p>
+<address>Copyright &copy; 2009 The Android Open Source Project</address>
+
+</body>
+</html>
diff --git a/docs/hello-world.html b/docs/hello-world.html
new file mode 100644
index 0000000..7491a28
--- /dev/null
+++ b/docs/hello-world.html
@@ -0,0 +1,216 @@
+<html>
+<head>
+ <title>Basic Dalvik VM Invocation</title>
+</head>
+
+<body>
+<h1>Basic Dalvik VM Invocation</h1>
+
+<p>
+On an Android device, the Dalvik virtual machine usually executes embedded
+in the Android application framework. It's also possible to run it directly,
+just as you would a virtual machine on your desktop system.
+</p><p>
+After compiling your Java language sources, convert and combine the .class
+files into a DEX file, and push that to the device. Here's a simple example:
+
+</p><p><code>
+% <font color="green">echo 'class Foo {'\</font><br>
+&gt; <font color="green">'public static void main(String[] args) {'\</font><br>
+&gt; <font color="green">'System.out.println("Hello, world"); }}' &gt; Foo.java</font><br>
+% <font color="green">javac Foo.java</font><br>
+% <font color="green">dx --dex --output=foo.jar Foo.class</font><br>
+% <font color="green">adb push foo.jar /sdcard</font><br>
+% <font color="green">adb shell dalvikvm -cp /sdcard/foo.jar Foo</font><br>
+Hello, world
+</code>
+</p><p>
+The <code>-cp</code> option sets the classpath. The initial directory
+for <code>adb shell</code> may not be what you expect it to be, so it's
+usually best to specify absolute pathnames.
+
+</p><p>
+The <code>dx</code> command accepts lists of individual class files,
+directories, or Jar archives. When the <code>--output</code> filename
+ends with <code>.jar</code>, <code>.zip</code>, or <code>.apk</code>,
+a file called <code>classes.dex</code> is created and stored inside the
+archive.
+</p><p>
+Run <code>adb shell dalvikvm -help</code> to see a list of command-line
+options.
+</p><p>
+
+
+
+<h2>Using a debugger</h2>
+
+<p>
+You can debug stand-alone applications with any JDWP-compliant debugger.
+There are two basic approaches.
+</p><p>
+The first way is to connect directly through TCP. Add, to the "dalvikvm"
+invocation line above, an argument like:
+</p><p>
+<code>&nbsp;&nbsp;-agentlib:jdwp=transport=dt_socket,address=8000,server=y,suspend=y</code>
+</p><p>
+This tells the VM to wait for a debugger to connect to it on TCP port 8000.
+You need to tell adb to forward local port 8000 to device port 8000:
+</p><p>
+<code>% <font color="green">adb forward tcp:8000 tcp:8000</font></code>
+</p><p>
+and then connect to it with your favorite debugger (using <code>jdb</code>
+as an example here):
+</p><p>
+<code>% <font color="green">jdb -attach localhost:8000</font></code>
+</p><p>
+When the debugger attaches, the VM will be in a suspended state. You can
+set breakpoints and then tell it to continue.
+
+
+</p><p>
+You can also connect through DDMS, like you would for an Android application.
+Add, to the "dalvikvm" command line:
+</p><p>
+<code>&nbsp;&nbsp;-agentlib:jdwp=transport=dt_android_adb,suspend=y,server=y</code>
+</p><p>
+Note the <code>transport</code> has changed, and you no longer need to
+specify a TCP port number. When your application starts, it will appear
+in DDMS, with "?" as the application name. Select it in DDMS, and connect
+to it as usual, e.g.:
+</p><p>
+<code>% <font color="green">jdb -attach localhost:8700</font></code>
+</p><p>
+Because command-line applications don't include the client-side
+DDM setup, features like thread monitoring and allocation tracking will not
+be available in DDMS. It's strictly a debugger pass-through in this mode.
+</p><p>
+See <a href="debugger.html">Dalvik Debugger Support</a> for more information
+about using debuggers with Dalvik.
+
+
+
+<h2>Working with the desktop build</h2>
+
+<!-- largely lifted from
+http://groups.google.com/group/android-porting/browse_thread/thread/ab553116dbc960da/29167c58b3b49051#29167c58b3b49051
+-->
+
+<p>
+The Dalvik VM can also be used directly on the desktop. This is somewhat
+more complicated however, because you won't have certain things set up in
+your environment, and several native code libraries are required to support
+the core Dalvik libs.
+</p><p>
+Start with:
+
+<pre>
+ . build/envsetup.sh
+ lunch sim-eng
+</pre>
+
+You should see something like:
+
+<pre>
+ ============================================
+ TARGET_PRODUCT=sim
+ TARGET_BUILD_VARIANT=eng
+ TARGET_SIMULATOR=true
+ TARGET_BUILD_TYPE=debug
+ TARGET_ARCH=x86
+ HOST_ARCH=x86
+ HOST_OS=linux
+ HOST_BUILD_TYPE=release
+ BUILD_ID=
+ ============================================
+</pre>
+
+</p></p>
+This configures you to build for the desktop, linking against glibc.
+This mode is NOT recommended for anything but experimental use. It
+may go away in the future.
+</p></p>
+You may see <code>TARGET_BUILD_TYPE=release</code> or <code>=debug</code>
+or possibly nothing there at all. You may want to replace the
+<code>lunch</code> command with
+<code>choosecombo Simulator debug sim eng</code>.
+</p></p>
+Build the world (add a <code>-j4</code> if you have multiple cores):
+
+<pre>
+ make
+</pre>
+
+</p></p>
+When that completes, you have a working dalvikm on your desktop
+machine:
+
+<pre>
+ % dalvikvm
+ E/dalvikvm(19521): ERROR: must specify non-'.' bootclasspath
+ W/dalvikvm(19521): JNI_CreateJavaVM failed
+ Dalvik VM init failed (check log file)
+</pre>
+
+</p></p>
+To actually do something, you need to specify the bootstrap class path
+and give it a place to put DEX data that it uncompresses from jar
+files. You can do that with a script like this:
+
+<blockquote><pre>
+#!/bin/sh
+
+# base directory, at top of source tree; replace with absolute path
+base=`pwd`
+
+# configure root dir of interesting stuff
+root=$base/out/debug/host/linux-x86/product/sim/system
+export ANDROID_ROOT=$root
+
+# configure bootclasspath
+bootpath=$root/framework
+export BOOTCLASSPATH=$bootpath/core.jar:$bootpath/ext.jar:$bootpath/framework.jar:$bootpath/android.policy.jar:$bootpath/services.jar
+
+# this is where we create the dalvik-cache directory; make sure it exists
+export ANDROID_DATA=/tmp/dalvik_$USER
+mkdir -p $ANDROID_DATA/dalvik-cache
+
+exec dalvikvm $@
+</pre></blockquote>
+
+</p></p>
+The preparation with <code>dx</code> is the same as before:
+
+<pre>
+ % cat &gt; Foo.java
+ class Foo { public static void main(String[] args) {
+ System.out.println("Hello, world");
+ } }
+ (ctrl-D)
+ % javac Foo.java
+ % dx --dex --output=foo.jar Foo.class
+ % ./rund -cp foo.jar Foo
+ Hello, world
+</pre>
+
+As above, you can get some info about valid arguments like this:
+
+<pre>
+ % ./rund -help
+</pre>
+
+</p></p>
+This also shows what options the VM was configured with. The sim "debug"
+build has all sorts of additional assertions and checks enabled,
+which slows the VM down, but since this is just for experiments it
+doesn't matter.
+
+</p></p>
+All of the above applies to x86 Linux. Anything else will likely
+require a porting effort. If libffi supports your system, the amount of
+work required should be minor.
+
+</p></p>
+<address>Copyright &copy; 2009 The Android Open Source Project</address>
+
+</body>
+</html>
diff --git a/docs/instruction-formats.css b/docs/instruction-formats.css
new file mode 100644
index 0000000..a2dc42f
--- /dev/null
+++ b/docs/instruction-formats.css
@@ -0,0 +1,129 @@
+h1 {
+ font-family: serif;
+ color: #222266;
+}
+
+h2 {
+ font-family: serif;
+ border-top-style: solid;
+ border-top-width: 2px;
+ border-color: #ccccdd;
+ padding-top: 12px;
+ margin-top: 48px;
+ margin-bottom: 2px;
+ color: #222266;
+}
+
+h3 {
+ font-family: serif;
+ color: #222266;
+}
+
+@media print {
+ table {
+ font-size: 8pt;
+ }
+}
+
+@media screen {
+ table {
+ font-size: 10pt;
+ }
+}
+
+table th {
+ font-family: sans-serif;
+ background: #aaaaff;
+}
+
+table {
+ border-collapse: collapse;
+}
+
+table td {
+ font-family: sans-serif;
+ border-top-style: solid;
+ border-bottom-style: solid;
+ border-width: 1px;
+ border-color: #aaaaff;
+ padding-top: 4px;
+ padding-bottom: 4px;
+ padding-left: 2px;
+ padding-right: 2px;
+ background: #eeeeff;
+}
+
+
+/* the mnemonic guide */
+
+table.letters {
+ margin-top: 24px;
+ margin-bottom: 24px;
+ margin-left: 48px;
+ margin-right: 48px;
+}
+
+table.letters td:first-child {
+ font-family: monospace;
+ width: 10%;
+ text-align: center;
+}
+
+table.letters td:first-child + td {
+ width: 10%;
+ text-align: center;
+}
+
+table.letters td:first-child + td + td {
+ width: 80%;
+}
+
+
+/* the formats, per se */
+
+table.format {
+ background: #aaaaaa;
+ border-collapse: collapse;
+ margin-top: 24px;
+ margin-bottom: 24px;
+ margin-left: 48px;
+ margin-right: 48px;
+}
+
+table.format td {
+ font-family: monospace;
+}
+
+table.format td + td i {
+ font-family: sans-serif;
+}
+
+table.format td sub {
+ font-family: sans-serif;
+}
+
+table.format td sub {
+ font-family: sans-serif;
+ font-style: italic;
+ font-size: 70%
+}
+
+table.format th:first-child {
+ width: 28%;
+}
+
+table.format th:first-child + th {
+ width: 5%;
+}
+
+table.format th:first-child + th + th {
+ width: 45%;
+}
+
+table.format th:first-child + th + th + th {
+ width: 22%;
+}
+
+table.format p {
+ margin-bottom: 0pt;
+} \ No newline at end of file
diff --git a/docs/instruction-formats.html b/docs/instruction-formats.html
new file mode 100644
index 0000000..d7bf690
--- /dev/null
+++ b/docs/instruction-formats.html
@@ -0,0 +1,430 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>Dalvik VM Instruction Formats</title>
+<link rel=stylesheet href="instruction-formats.css">
+</head>
+
+<body>
+
+<h1>Dalvik VM Instruction Formats</h1>
+<p>Copyright &copy; 2007 The Android Open Source Project
+
+<h2>Introduction and Overview</h2>
+
+<p>This document lists the instruction formats used by Dalvik bytecode
+and is meant to be used in conjunction with the
+<a href="dalvik-bytecode.html">bytecode reference document</a>.</p>
+
+<h3>Bitwise descriptions</h3>
+
+<p>The first column in the format table lists the bitwise layout of
+the format. It consists of one or more space-separated "words" each of
+which describes a 16-bit code unit. Each character in a word
+represents four bits, read from high bits to low, with vertical bars
+("<code>|</code>") interspersed to aid in reading. Uppercase letters
+in sequence from "<code>A</code>" are used to indicate fields within
+the format (which then get defined further by the syntax column). The term
+"<code>op</code>" is used to indicate the position of the eight-bit
+opcode within the format. A slashed zero ("<code>&Oslash;</code>") is
+used to indicate that all bits should be zero in the indicated
+position.</p>
+
+<p>For example, the format "<code>B|A|<i>op</i> CCCC</code>" indicates
+that the format consists of two 16-bit code units. The first word
+consists of the opcode in the low eight bits and a pair of four-bit
+values in the high eight bits; and the second word consists of a single
+16-bit value.</p>
+
+<h3>Format IDs</h3>
+
+<p>The second column in the format table indicates the short identifier
+for the format, which is used in other documents and in code to identify
+the format.</p>
+
+<p>Format IDs consist of three characters, two digits followed by a
+letter. The first digit indicates the number of 16-bit code units in the
+format. The second digit indicates the maximum number of registers that the
+format contains (maximum, since some formats can accomodate a variable
+number of registers), with the special designation "<code>r</code>" indicating
+that a range of registers is encoded. The final letter semi-mnemonically
+indicates the type of any extra data encoded by the format. For example,
+format "<code>21t</code>" is of length two, contains one register reference,
+and additionally contains a branch target.</p>
+
+<p>Suggested static linking formats have an additional "<code>s</code>" suffix,
+making them four characters total.</p>
+
+<p>The full list of typecode letters are as follows. Note that some
+forms have different sizes, depending on the format:</p>
+
+<table class="letters">
+<thead>
+<tr>
+ <th>Mnemonic</th>
+ <th>Bit Sizes</th>
+ <th>Meaning</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>b</td>
+ <td>8</td>
+ <td>immediate signed <b>b</b>yte</td>
+</tr>
+<tr>
+ <td>c</td>
+ <td>16, 32</td>
+ <td><b>c</b>onstant pool index</td>
+</tr>
+<tr>
+ <td>f</td>
+ <td>16</td>
+ <td>inter<b>f</b>ace constants (only used in statically linked formats)
+ </td>
+</tr>
+<tr>
+ <td>h</td>
+ <td>16</td>
+ <td>immediate signed <b>h</b>at (high-order bits of a 32- or 64-bit
+ value; low-order bits are all <code>0</code>)
+ </td>
+</tr>
+<tr>
+ <td>i</td>
+ <td>32</td>
+ <td>immediate signed <b>i</b>nt, or 32-bit float</td>
+</tr>
+<tr>
+ <td>l</td>
+ <td>64</td>
+ <td>immediate signed <b>l</b>ong, or 64-bit double</td>
+</tr>
+<tr>
+ <td>m</td>
+ <td>16</td>
+ <td><b>m</b>ethod constants (only used in statically linked formats)</td>
+</tr>
+<tr>
+ <td>n</td>
+ <td>4</td>
+ <td>immediate signed <b>n</b>ibble</td>
+</tr>
+<tr>
+ <td>s</td>
+ <td>16</td>
+ <td>immediate signed <b>s</b>hort</td>
+</tr>
+<tr>
+ <td>t</td>
+ <td>8, 16, 32</td>
+ <td>branch <b>t</b>arget</td>
+</tr>
+<tr>
+ <td>x</td>
+ <td>0</td>
+ <td>no additional data</td>
+</tr>
+</tbody>
+</table>
+
+<h3>Syntax</h3>
+
+<p>The third column of the format table indicates the human-oriented
+syntax for instructions which use the indicated format. Each instruction
+starts with the named opcode and is optionally followed by one or
+more arguments, themselves separated with commas.</p>
+
+<p>Wherever an argument refers to a field from the first column, the
+letter for that field is indicated in the syntax, repeated once for
+each four bits of the field. For example, an eight-bit field labeled
+"<code>BB</code>" in the first column would also be labeled
+"<code>BB</code>" in the syntax column.</p>
+
+<p>Arguments which name a register have the form "<code>v<i>X</i></code>".
+The prefix "<code>v</code>" was chosen instead of the more common
+"<code>r</code>" exactly to avoid conflicting with (non-virtual) architectures
+on which a Dalvik virtual machine might be implemented which themselves
+use the prefix "<code>r</code>" for their registers. (That is, this
+decision makes it possible to talk about both virtual and real registers
+together without the need for circumlocution.)</p>
+
+<p>Arguments which indicate a literal value have the form
+"<code>#+<i>X</i></code>". Some formats indicate literals that only
+have non-zero bits in their high-order bits; for these, the zeroes
+are represented explicitly in the syntax, even though they do not
+appear in the bitwise representation.</p>
+
+<p>Arguments which indicate a relative instruction address offset have the
+form "<code>+<i>X</i></code>".</p>
+
+<p>Arguments which indicate a literal constant pool index have the form
+"<code><i>kind</i>@<i>X</i></code>", where "<code><i>kind</i></code>"
+indicates which constant pool is being referred to. Each opcode that
+uses such a format explicitly allows only one kind of constant; see
+the opcode reference to figure out the correspondence. The four
+kinds of constant pool are "<code>string</code>" (string pool index),
+"<code>type</code>" (type pool index), "<code>field</code>" (field
+pool index), and "<code>meth</code>" (method pool index).</p>
+
+<p>Similar to the representation of constant pool indices, there are
+also suggested (optional) forms that indicate prelinked offsets or
+indices. These prelinked values include "<code>vtaboff</code>"
+(vtable offset), "<code>fieldoff</code>" (field offset), and
+"<code>iface</code>" (interface pool index).</p>
+
+<p>In the cases where a format value isn't explictly part of the syntax
+but instead picks a variant, each variant is listed with the prefix
+"<code>[<i>X</i>=<i>N</i>]</code>" (e.g., "<code>[B=2]</code>") to indicate
+the correspondence.</p>
+
+<h2>The Formats</h2>
+
+<table class="format">
+<thead>
+<tr>
+ <th>Format</th>
+ <th>ID</th>
+ <th>Syntax</th>
+ <th>Notable Opcodes Covered</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>&Oslash;&Oslash;|<i>op</i></td>
+ <td>10x</td>
+ <td><i><code>op</code></i></td>
+ <td>&nbsp;</td>
+</tr>
+<tr>
+ <td rowspan="2">B|A|<i>op</i></td>
+ <td>12x</td>
+ <td><i><code>op</code></i> vA, vB</td>
+ <td>&nbsp;</td>
+</tr>
+<tr>
+ <td>11n</td>
+ <td><i><code>op</code></i> vA, #+B</td>
+ <td>&nbsp;</td>
+</tr>
+<tr>
+ <td rowspan="2">AA|<i>op</i></td>
+ <td>11x</td>
+ <td><i><code>op</code></i> vAA</td>
+ <td>&nbsp;</td>
+</tr>
+<tr>
+ <td>10t</td>
+ <td><i><code>op</code></i> +AA</td>
+ <td>goto</td>
+</tr>
+<tr>
+ <td>&Oslash;&Oslash;|<i>op</i> AAAA</td></td>
+ <td>20t</td>
+ <td><i><code>op</code></i> +AAAA</td>
+ <td>goto/16</td>
+</tr>
+<tr>
+ <td rowspan="5">AA|<i>op</i> BBBB</td>
+ <td>22x</td>
+ <td><i><code>op</code></i> vAA, vBBBB</td>
+ <td>&nbsp;</td>
+</tr>
+<tr>
+ <td>21t</td>
+ <td><i><code>op</code></i> vAA, +BBBB</td>
+ <td>&nbsp;</td>
+</tr>
+<tr>
+ <td>21s</td>
+ <td><i><code>op</code></i> vAA, #+BBBB</td>
+ <td>&nbsp;</td>
+</tr>
+<tr>
+ <td>21h</td>
+ <td><i><code>op</code></i> vAA, #+BBBB0000<br/>
+ <i><code>op</code></i> vAA, #+BBBB000000000000
+ </td>
+ <td>&nbsp;</td>
+</tr>
+<tr>
+ <td>21c</td>
+ <td><i><code>op</code></i> vAA, type@BBBB<br/>
+ <i><code>op</code></i> vAA, field@BBBB<br/>
+ <i><code>op</code></i> vAA, string@BBBB
+ </td>
+ <td>check-cast<br/>
+ const-class<br/>
+ const-string
+ </td>
+</tr>
+<tr>
+ <td rowspan="2">AA|<i>op</i> CC|BB</td>
+ <td>23x</td>
+ <td><i><code>op</code></i> vAA, vBB, vCC</td>
+ <td>&nbsp;</td>
+</tr>
+<tr>
+ <td>22b</td>
+ <td><i><code>op</code></i> vAA, vBB, #+CC</td>
+ <td>&nbsp;</td>
+</tr>
+<tr>
+ <td rowspan="4">B|A|<i>op</i> CCCC</td>
+ <td>22t</td>
+ <td><i><code>op</code></i> vA, vB, +CCCC</td>
+ <td>&nbsp;</td>
+</tr>
+<tr>
+ <td>22s</td>
+ <td><i><code>op</code></i> vA, vB, #+CCCC</td>
+ <td>&nbsp;</td>
+</tr>
+<tr>
+ <td>22c</td>
+ <td><i><code>op</code></i> vA, vB, type@CCCC<br/>
+ <i><code>op</code></i> vA, vB, field@CCCC
+ </td>
+ <td>instance-of</td>
+</tr>
+<tr>
+ <td>22cs</td>
+ <td><i><code>op</code></i> vA, vB, fieldoff@CCCC</td>
+ <td><i>(suggested format for statically linked field access instructions of
+ format 22c)</i>
+ </td>
+</tr>
+<tr>
+ <td>&Oslash;&Oslash;|<i>op</i> AAAA<sub>lo</sub> AAAA<sub>hi</sub></td></td>
+ <td>30t</td>
+ <td><i><code>op</code></i> +AAAAAAAA</td>
+ <td>goto/32</td>
+</tr>
+<tr>
+ <td>&Oslash;&Oslash;|<i>op</i> AAAA BBBB</td>
+ <td>32x</td>
+ <td><i><code>op</code></i> vAAAA, vBBBB</td>
+ <td>&nbsp;</td>
+</tr>
+<tr>
+ <td rowspan="3">AA|<i>op</i> BBBB<sub>lo</sub> BBBB<sub>hi</sub></td>
+ <td>31i</td>
+ <td><i><code>op</code></i> vAA, #+BBBBBBBB</td>
+ <td>&nbsp;</td>
+</tr>
+<tr>
+ <td>31t</td>
+ <td><i><code>op</code></i> vAA, +BBBBBBBB</td>
+ <td>&nbsp;</td>
+</tr>
+<tr>
+ <td>31c</td>
+ <td><i><code>op</code></i> vAA, string@BBBBBBBB</td>
+ <td>const-string/jumbo</td>
+</tr>
+<tr>
+ <td>B|A|<i>op</i> CCCC G|F|E|D</td>
+ <td>35c</td>
+ <td><i>[<code>B=5</code>] <code>op</code></i> {vD, vE, vF, vG, vA},
+ meth@CCCC<br/>
+ <i>[<code>B=5</code>] <code>op</code></i> {vD, vE, vF, vG, vA},
+ type@CCCC<br/>
+ <i>[<code>B=4</code>] <code>op</code></i> {vD, vE, vF, vG},
+ <i><code>kind</code></i>@CCCC<br/>
+ <i>[<code>B=3</code>] <code>op</code></i> {vD, vE, vF},
+ <i><code>kind</code></i>@CCCC<br/>
+ <i>[<code>B=2</code>] <code>op</code></i> {vD, vE},
+ <i><code>kind</code></i>@CCCC<br/>
+ <i>[<code>B=1</code>] <code>op</code></i> {vD},
+ <i><code>kind</code></i>@CCCC<br/>
+ <i>[<code>B=0</code>] <code>op</code></i> {},
+ <i><code>kind</code></i>@CCCC
+ </td>
+ <td>&nbsp;</td>
+</tr>
+<tr>
+ <td>B|A|<i>op</i> CCCC G|F|E|D</td>
+ <td>35ms</td>
+
+ <td><i>[<code>B=5</code>] <code>op</code></i> {vD, vE, vF, vG, vA},
+ vtaboff@CCCC<br/>
+ <i>[<code>B=4</code>] <code>op</code></i> {vD, vE, vF, vG},
+ vtaboff@CCCC<br/>
+ <i>[<code>B=3</code>] <code>op</code></i> {vD, vE, vF},
+ vtaboff@CCCC<br/>
+ <i>[<code>B=2</code>] <code>op</code></i> {vD, vE},
+ vtaboff@CCCC<br/>
+ <i>[<code>B=1</code>] <code>op</code></i> {vD},
+ vtaboff@CCCC<br/>
+ </td>
+ <td><i>(suggested format for statically linked <code>invoke-virtual</code>
+ and <code>invoke-super</code> instructions of format 35c)</i>
+ </td>
+</tr>
+<tr>
+ <td>B|A|<i>op</i> DDCC H|G|F|E</td>
+ <td>35fs</td>
+ <td><i>[<code>B=5</code>] <code>op</code></i> {vE, vF, vG, vH, vA},
+ vtaboff@CC, iface@DD<br/>
+ <i>[<code>B=4</code>] <code>op</code></i> {vE, vF, vG, vH},
+ vtaboff@CC, iface@DD<br/>
+ <i>[<code>B=3</code>] <code>op</code></i> {vE, vF, vG},
+ vtaboff@CC, iface@DD<br/>
+ <i>[<code>B=2</code>] <code>op</code></i> {vE, vF},
+ vtaboff@CC, iface@DD<br/>
+ <i>[<code>B=1</code>] <code>op</code></i> {vE},
+ vtaboff@CC, iface@DD<br/>
+ </td>
+ <td><i>(suggested format for statically linked <code>invoke-interface</code>
+ instructions of format 35c)</i>
+ </td>
+</tr>
+<tr>
+ <td>AA|<i>op</i> BBBB CCCC</td>
+ <td>3rc</td>
+ <td><i><code>op</code></i> {vCCCC .. vNNNN}, meth@BBBB<br/>
+ <i><code>op</code></i> {vCCCC .. vNNNN}, type@BBBB<br/>
+ <p><i>(where <code>NNNN = CCCC+AA-1</code>, that is <code>A</code>
+ determines the count <code>0..255</code>, and <code>C</code>
+ determines the first register)</i></p>
+ </td>
+ <td>&nbsp;</td>
+</tr>
+<tr>
+ <td>AA|<i>op</i> BBBB CCCC</td>
+ <td>3rms</td>
+ <td><i><code>op</code></i> {vCCCC .. vNNNN}, vtaboff@BBBB<br/>
+ <p><i>(where <code>NNNN = CCCC+AA-1</code>, that is <code>A</code>
+ determines the count <code>0..255</code>, and <code>C</code>
+ determines the first register)</i></p>
+ </td>
+ <td><i>(suggested format for statically linked <code>invoke-virtual</code>
+ and <code>invoke-super</code> instructions of format <code>3rc</code>)</i>
+ </td>
+</tr>
+<tr>
+ <td>AA|<i>op</i> CCBB DDDD</td>
+ <td>3rfs</td>
+ <td><i><code>op</code></i> {vDDDD .. vNNNN}, vtaboff@BB,
+ iface@CC<br/>
+ <p><i>(where <code>NNNN = DDDD+AA-1</code>, that is <code>A</code>
+ determines the count <code>0..255</code>, and <code>D</code>
+ determines the first register)</i></p>
+ </td>
+ <td><i>(suggested format for statically linked <code>invoke-interface</code>
+ instructions of format <code>3rc</code>)</i>
+ </td>
+</tr>
+<tr>
+ <td>AA|<i>op</i> BBBB<sub>lo</sub> BBBB BBBB BBBB<sub>hi</sub></td>
+ <td>51l</td>
+ <td><i><code>op</code></i> vAA, #+BBBBBBBBBBBBBBBB</td>
+ <td>const-wide</td>
+</tr>
+</tbody>
+</table>
+
+</body>
+</html>
diff --git a/docs/java-bytecode.css b/docs/java-bytecode.css
new file mode 100644
index 0000000..48984b2
--- /dev/null
+++ b/docs/java-bytecode.css
@@ -0,0 +1,54 @@
+@media print {
+ table {
+ font-size: 8pt;
+ }
+}
+
+@media screen {
+ table {
+ font-size: 10pt;
+ }
+}
+
+h1 {
+ text-align: center;
+}
+
+table {
+ vertical-align: top;
+ border-collapse: collapse;
+ font-family: sans-serif;
+}
+
+td {
+ vertical-align: top;
+ background: #f8f8f8;
+ border-width: 0;
+}
+
+td.outer {
+ width: 25%;
+ padding: 0;
+}
+
+td.outer table {
+ width: 100%;
+}
+
+td.outer td {
+ border-width: 0;
+ background: #f8f8f8;
+ padding: 1pt;
+ padding-left: 10pt;
+ padding-right: 2pt;
+}
+
+tr.d td {
+ background: #dddddd;
+}
+
+td.outer td + td + td {
+ font-family: monospace;
+ font-weight: bold;
+ padding-right: 5pt;
+} \ No newline at end of file
diff --git a/docs/java-bytecode.html b/docs/java-bytecode.html
new file mode 100644
index 0000000..691ae54
--- /dev/null
+++ b/docs/java-bytecode.html
@@ -0,0 +1,228 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>Java Bytecode At A Glance</title>
+<link rel="stylesheet" href="java-bytecode.css">
+</head>
+
+<body>
+
+<h1>Java Bytecode At A Glance</h1>
+
+<table align="center">
+<tr><td class="outer"><table>
+<tr><td>0x00</td><td>0</td><td>nop</td></tr>
+<tr><td>0x01</td><td>1</td><td>aconst_null</td></tr>
+<tr class="d"><td>0x02</td><td>2</td><td>iconst_m1</td></tr>
+<tr class="d"><td>0x03</td><td>3</td><td>iconst_0</td></tr>
+<tr><td>0x04</td><td>4</td><td>iconst_1</td></tr>
+<tr><td>0x05</td><td>5</td><td>iconst_2</td></tr>
+<tr class="d"><td>0x06</td><td>6</td><td>iconst_3</td></tr>
+<tr class="d"><td>0x07</td><td>7</td><td>iconst_4</td></tr>
+<tr><td>0x08</td><td>8</td><td>iconst_5</td></tr>
+<tr><td>0x09</td><td>9</td><td>lconst_0</td></tr>
+<tr class="d"><td>0x0a</td><td>10</td><td>lconst_1</td></tr>
+<tr class="d"><td>0x0b</td><td>11</td><td>fconst_0</td></tr>
+<tr><td>0x0c</td><td>12</td><td>fconst_1</td></tr>
+<tr><td>0x0d</td><td>13</td><td>fconst_2</td></tr>
+<tr class="d"><td>0x0e</td><td>14</td><td>dconst_0</td></tr>
+<tr class="d"><td>0x0f</td><td>15</td><td>dconst_1</td></tr>
+<tr><td>0x10</td><td>16</td><td>bipush</td></tr>
+<tr><td>0x11</td><td>17</td><td>sipush</td></tr>
+<tr class="d"><td>0x12</td><td>18</td><td>ldc</td></tr>
+<tr class="d"><td>0x13</td><td>19</td><td>ldc_w</td></tr>
+<tr><td>0x14</td><td>20</td><td>ldc2_w</td></tr>
+<tr><td>0x15</td><td>21</td><td>iload</td></tr>
+<tr class="d"><td>0x16</td><td>22</td><td>lload</td></tr>
+<tr class="d"><td>0x17</td><td>23</td><td>fload</td></tr>
+<tr><td>0x18</td><td>24</td><td>dload</td></tr>
+<tr><td>0x19</td><td>25</td><td>aload</td></tr>
+<tr class="d"><td>0x1a</td><td>26</td><td>iload_0</td></tr>
+<tr class="d"><td>0x1b</td><td>27</td><td>iload_1</td></tr>
+<tr><td>0x1c</td><td>28</td><td>iload_2</td></tr>
+<tr><td>0x1d</td><td>29</td><td>iload_3</td></tr>
+<tr class="d"><td>0x1e</td><td>30</td><td>lload_0</td></tr>
+<tr class="d"><td>0x1f</td><td>31</td><td>lload_1</td></tr>
+<tr><td>0x20</td><td>32</td><td>lload_2</td></tr>
+<tr><td>0x21</td><td>33</td><td>lload_3</td></tr>
+<tr class="d"><td>0x22</td><td>34</td><td>fload_0</td></tr>
+<tr class="d"><td>0x23</td><td>35</td><td>fload_1</td></tr>
+<tr><td>0x24</td><td>36</td><td>fload_2</td></tr>
+<tr><td>0x25</td><td>37</td><td>fload_3</td></tr>
+<tr class="d"><td>0x26</td><td>38</td><td>dload_0</td></tr>
+<tr class="d"><td>0x27</td><td>39</td><td>dload_1</td></tr>
+<tr><td>0x28</td><td>40</td><td>dload_2</td></tr>
+<tr><td>0x29</td><td>41</td><td>dload_3</td></tr>
+<tr class="d"><td>0x2a</td><td>42</td><td>aload_0</td></tr>
+<tr class="d"><td>0x2b</td><td>43</td><td>aload_1</td></tr>
+<tr><td>0x2c</td><td>44</td><td>aload_2</td></tr>
+<tr><td>0x2d</td><td>45</td><td>aload_3</td></tr>
+<tr class="d"><td>0x2e</td><td>46</td><td>iaload</td></tr>
+<tr class="d"><td>0x2f</td><td>47</td><td>laload</td></tr>
+<tr><td>0x30</td><td>48</td><td>faload</td></tr>
+<tr><td>0x31</td><td>49</td><td>daload</td></tr>
+<tr class="d"><td>0x32</td><td>50</td><td>aaload</td></tr>
+</table></td>
+<td class="outer"><table>
+<tr><td>0x33</td><td>51</td><td>baload</td></tr>
+<tr><td>0x34</td><td>52</td><td>caload</td></tr>
+<tr class="d"><td>0x35</td><td>53</td><td>saload</td></tr>
+<tr class="d"><td>0x36</td><td>54</td><td>istore</td></tr>
+<tr><td>0x37</td><td>55</td><td>lstore</td></tr>
+<tr><td>0x38</td><td>56</td><td>fstore</td></tr>
+<tr class="d"><td>0x39</td><td>57</td><td>dstore</td></tr>
+<tr class="d"><td>0x3a</td><td>58</td><td>astore</td></tr>
+<tr><td>0x3b</td><td>59</td><td>istore_0</td></tr>
+<tr><td>0x3c</td><td>60</td><td>istore_1</td></tr>
+<tr class="d"><td>0x3d</td><td>61</td><td>istore_2</td></tr>
+<tr class="d"><td>0x3e</td><td>62</td><td>istore_3</td></tr>
+<tr><td>0x3f</td><td>63</td><td>lstore_0</td></tr>
+<tr><td>0x40</td><td>64</td><td>lstore_1</td></tr>
+<tr class="d"><td>0x41</td><td>65</td><td>lstore_2</td></tr>
+<tr class="d"><td>0x42</td><td>66</td><td>lstore_3</td></tr>
+<tr><td>0x43</td><td>67</td><td>fstore_0</td></tr>
+<tr><td>0x44</td><td>68</td><td>fstore_1</td></tr>
+<tr class="d"><td>0x45</td><td>69</td><td>fstore_2</td></tr>
+<tr class="d"><td>0x46</td><td>70</td><td>fstore_3</td></tr>
+<tr><td>0x47</td><td>71</td><td>dstore_0</td></tr>
+<tr><td>0x48</td><td>72</td><td>dstore_1</td></tr>
+<tr class="d"><td>0x49</td><td>73</td><td>dstore_2</td></tr>
+<tr class="d"><td>0x4a</td><td>74</td><td>dstore_3</td></tr>
+<tr><td>0x4b</td><td>75</td><td>astore_0</td></tr>
+<tr><td>0x4c</td><td>76</td><td>astore_1</td></tr>
+<tr class="d"><td>0x4d</td><td>77</td><td>astore_2</td></tr>
+<tr class="d"><td>0x4e</td><td>78</td><td>astore_3</td></tr>
+<tr><td>0x4f</td><td>79</td><td>iastore</td></tr>
+<tr><td>0x50</td><td>80</td><td>lastore</td></tr>
+<tr class="d"><td>0x51</td><td>81</td><td>fastore</td></tr>
+<tr class="d"><td>0x52</td><td>82</td><td>dastore</td></tr>
+<tr><td>0x53</td><td>83</td><td>aastore</td></tr>
+<tr><td>0x54</td><td>84</td><td>bastore</td></tr>
+<tr class="d"><td>0x55</td><td>85</td><td>castore</td></tr>
+<tr class="d"><td>0x56</td><td>86</td><td>sastore</td></tr>
+<tr><td>0x57</td><td>87</td><td>pop</td></tr>
+<tr><td>0x58</td><td>88</td><td>pop2</td></tr>
+<tr class="d"><td>0x59</td><td>89</td><td>dup</td></tr>
+<tr class="d"><td>0x5a</td><td>90</td><td>dup_x1</td></tr>
+<tr><td>0x5b</td><td>91</td><td>dup_x2</td></tr>
+<tr><td>0x5c</td><td>92</td><td>dup2</td></tr>
+<tr class="d"><td>0x5d</td><td>93</td><td>dup2_x1</td></tr>
+<tr class="d"><td>0x5e</td><td>94</td><td>dup2_x2</td></tr>
+<tr><td>0x5f</td><td>95</td><td>swap</td></tr>
+<tr><td>0x60</td><td>96</td><td>iadd</td></tr>
+<tr class="d"><td>0x61</td><td>97</td><td>ladd</td></tr>
+<tr class="d"><td>0x62</td><td>98</td><td>fadd</td></tr>
+<tr><td>0x63</td><td>99</td><td>dadd</td></tr>
+<tr><td>0x64</td><td>100</td><td>isub</td></tr>
+<tr class="d"><td>0x65</td><td>101</td><td>lsub</td></tr>
+</table></td>
+<td class="outer"><table>
+<tr><td>0x66</td><td>102</td><td>fsub</td></tr>
+<tr><td>0x67</td><td>103</td><td>dsub</td></tr>
+<tr class="d"><td>0x68</td><td>104</td><td>imul</td></tr>
+<tr class="d"><td>0x69</td><td>105</td><td>lmul</td></tr>
+<tr><td>0x6a</td><td>106</td><td>fmul</td></tr>
+<tr><td>0x6b</td><td>107</td><td>dmul</td></tr>
+<tr class="d"><td>0x6c</td><td>108</td><td>idiv</td></tr>
+<tr class="d"><td>0x6d</td><td>109</td><td>ldiv</td></tr>
+<tr><td>0x6e</td><td>110</td><td>fdiv</td></tr>
+<tr><td>0x6f</td><td>111</td><td>ddiv</td></tr>
+<tr class="d"><td>0x70</td><td>112</td><td>irem</td></tr>
+<tr class="d"><td>0x71</td><td>113</td><td>lrem</td></tr>
+<tr><td>0x72</td><td>114</td><td>frem</td></tr>
+<tr><td>0x73</td><td>115</td><td>drem</td></tr>
+<tr class="d"><td>0x74</td><td>116</td><td>ineg</td></tr>
+<tr class="d"><td>0x75</td><td>117</td><td>lneg</td></tr>
+<tr><td>0x76</td><td>118</td><td>fneg</td></tr>
+<tr><td>0x77</td><td>119</td><td>dneg</td></tr>
+<tr class="d"><td>0x78</td><td>120</td><td>ishl</td></tr>
+<tr class="d"><td>0x79</td><td>121</td><td>lshl</td></tr>
+<tr><td>0x7a</td><td>122</td><td>ishr</td></tr>
+<tr><td>0x7b</td><td>123</td><td>lshr</td></tr>
+<tr class="d"><td>0x7c</td><td>124</td><td>iushr</td></tr>
+<tr class="d"><td>0x7d</td><td>125</td><td>lushr</td></tr>
+<tr><td>0x7e</td><td>126</td><td>iand</td></tr>
+<tr><td>0x7f</td><td>127</td><td>land</td></tr>
+<tr class="d"><td>0x80</td><td>128</td><td>ior</td></tr>
+<tr class="d"><td>0x81</td><td>129</td><td>lor</td></tr>
+<tr><td>0x82</td><td>130</td><td>ixor</td></tr>
+<tr><td>0x83</td><td>131</td><td>lxor</td></tr>
+<tr class="d"><td>0x84</td><td>132</td><td>iinc</td></tr>
+<tr class="d"><td>0x85</td><td>133</td><td>i2l</td></tr>
+<tr><td>0x86</td><td>134</td><td>i2f</td></tr>
+<tr><td>0x87</td><td>135</td><td>i2d</td></tr>
+<tr class="d"><td>0x88</td><td>136</td><td>l2i</td></tr>
+<tr class="d"><td>0x89</td><td>137</td><td>l2f</td></tr>
+<tr><td>0x8a</td><td>138</td><td>l2d</td></tr>
+<tr><td>0x8b</td><td>139</td><td>f2i</td></tr>
+<tr class="d"><td>0x8c</td><td>140</td><td>f2l</td></tr>
+<tr class="d"><td>0x8d</td><td>141</td><td>f2d</td></tr>
+<tr><td>0x8e</td><td>142</td><td>d2i</td></tr>
+<tr><td>0x8f</td><td>143</td><td>d2l</td></tr>
+<tr class="d"><td>0x90</td><td>144</td><td>d2f</td></tr>
+<tr class="d"><td>0x91</td><td>145</td><td>i2b</td></tr>
+<tr><td>0x92</td><td>146</td><td>i2c</td></tr>
+<tr><td>0x93</td><td>147</td><td>i2s</td></tr>
+<tr class="d"><td>0x94</td><td>148</td><td>lcmp</td></tr>
+<tr class="d"><td>0x95</td><td>149</td><td>fcmpl</td></tr>
+<tr><td>0x96</td><td>150</td><td>fcmpg</td></tr>
+<tr><td>0x97</td><td>151</td><td>dcmpl</td></tr>
+<tr class="d"><td>0x98</td><td>152</td><td>dcmpg</td></tr>
+</table></td>
+<td class="outer"><table>
+<tr><td>0x99</td><td>153</td><td>ifeq</td></tr>
+<tr><td>0x9a</td><td>154</td><td>ifne</td></tr>
+<tr class="d"><td>0x9b</td><td>155</td><td>iflt</td></tr>
+<tr class="d"><td>0x9c</td><td>156</td><td>ifge</td></tr>
+<tr><td>0x9d</td><td>157</td><td>ifgt</td></tr>
+<tr><td>0x9e</td><td>158</td><td>ifle</td></tr>
+<tr class="d"><td>0x9f</td><td>159</td><td>if_icmpeq</td></tr>
+<tr class="d"><td>0xa0</td><td>160</td><td>if_icmpne</td></tr>
+<tr><td>0xa1</td><td>161</td><td>if_icmplt</td></tr>
+<tr><td>0xa2</td><td>162</td><td>if_icmpge</td></tr>
+<tr class="d"><td>0xa3</td><td>163</td><td>if_icmpgt</td></tr>
+<tr class="d"><td>0xa4</td><td>164</td><td>if_icmple</td></tr>
+<tr><td>0xa5</td><td>165</td><td>if_acmpeq</td></tr>
+<tr><td>0xa6</td><td>166</td><td>if_acmpne</td></tr>
+<tr class="d"><td>0xa7</td><td>167</td><td>goto</td></tr>
+<tr class="d"><td>0xa8</td><td>168</td><td>jsr</td></tr>
+<tr><td>0xa9</td><td>169</td><td>ret</td></tr>
+<tr><td>0xaa</td><td>170</td><td>tableswitch</td></tr>
+<tr class="d"><td>0xab</td><td>171</td><td>lookupswitch</td></tr>
+<tr class="d"><td>0xac</td><td>172</td><td>ireturn</td></tr>
+<tr><td>0xad</td><td>173</td><td>lreturn</td></tr>
+<tr><td>0xae</td><td>174</td><td>freturn</td></tr>
+<tr class="d"><td>0xaf</td><td>175</td><td>dreturn</td></tr>
+<tr class="d"><td>0xb0</td><td>176</td><td>areturn</td></tr>
+<tr><td>0xb1</td><td>177</td><td>return</td></tr>
+<tr><td>0xb2</td><td>178</td><td>getstatic</td></tr>
+<tr class="d"><td>0xb3</td><td>179</td><td>putstatic</td></tr>
+<tr class="d"><td>0xb4</td><td>180</td><td>getfield</td></tr>
+<tr><td>0xb5</td><td>181</td><td>putfield</td></tr>
+<tr><td>0xb6</td><td>182</td><td>invokevirtual</td></tr>
+<tr class="d"><td>0xb7</td><td>183</td><td>invokespecial</td></tr>
+<tr class="d"><td>0xb8</td><td>184</td><td>invokestatic</td></tr>
+<tr><td>0xb9</td><td>185</td><td>invokeinterface</td></tr>
+<tr><td>0xba</td><td>186</td><td><i>(unused)</i></td></tr>
+<tr class="d"><td>0xbb</td><td>187</td><td>new</td></tr>
+<tr class="d"><td>0xbc</td><td>188</td><td>newarray</td></tr>
+<tr><td>0xbd</td><td>189</td><td>anewarray</td></tr>
+<tr><td>0xbe</td><td>190</td><td>arraylength</td></tr>
+<tr class="d"><td>0xbf</td><td>191</td><td>athrow</td></tr>
+<tr class="d"><td>0xc0</td><td>192</td><td>checkcast</td></tr>
+<tr><td>0xc1</td><td>193</td><td>instanceof</td></tr>
+<tr><td>0xc2</td><td>194</td><td>monitorenter</td></tr>
+<tr class="d"><td>0xc3</td><td>195</td><td>monitorexit</td></tr>
+<tr class="d"><td>0xc4</td><td>196</td><td>wide</td></tr>
+<tr><td>0xc5</td><td>197</td><td>multianewarray</td></tr>
+<tr><td>0xc6</td><td>198</td><td>ifnull</td></tr>
+<tr class="d"><td>0xc7</td><td>199</td><td>ifnonnull</td></tr>
+<tr class="d"><td>0xc8</td><td>200</td><td>goto_w</td></tr>
+<tr><td>0xc9</td><td>201</td><td>jsr_w</td></tr>
+</table></td></tr>
+</table>
+
+</body>
+</html>
diff --git a/docs/java-constraints.css b/docs/java-constraints.css
new file mode 100644
index 0000000..a315a73
--- /dev/null
+++ b/docs/java-constraints.css
@@ -0,0 +1,59 @@
+h1 {
+ font-family: serif;
+ color: #222266;
+}
+
+h2 {
+ font-family: serif;
+ border-top-style: solid;
+ border-top-width: 2px;
+ border-color: #ccccdd;
+ padding-top: 12px;
+ margin-top: 48px;
+ margin-bottom: 2px;
+ color: #222266;
+}
+
+@media print {
+ table {
+ font-size: 8pt;
+ }
+}
+
+@media screen {
+ table {
+ font-size: 10pt;
+ }
+}
+
+
+/* general for all tables */
+
+table {
+ border-collapse: collapse;
+ margin-top: 24px;
+ margin-bottom: 24px;
+ margin-left: 48px;
+ margin-right: 48px;
+}
+
+table th {
+ font-family: sans-serif;
+ background: #aabbff;
+ text-align: left;
+}
+
+table td {
+ font-family: sans-serif;
+ border-top-style: solid;
+ border-bottom-style: solid;
+ border-width: 1px;
+ border-color: #aaaaff;
+ padding-top: 4px;
+ padding-bottom: 4px;
+ padding-left: 4px;
+ padding-right: 6px;
+ background: #eeeeff;
+ margin-top: 4pt;
+ margin-bottom: 0pt;
+}
diff --git a/docs/java-constraints.html b/docs/java-constraints.html
new file mode 100644
index 0000000..9d3c434
--- /dev/null
+++ b/docs/java-constraints.html
@@ -0,0 +1,1080 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+ <head>
+ <title>Java bytecode constraints</title>
+ <link rel=stylesheet href="java-constraints.css">
+ </head>
+
+ <body>
+ <h1>
+ Bytecode constraints
+ </h1>
+
+ <p>
+ From the point of view of a piece of code written in the Java
+ programming language or targeted in the same way to <code>.class</code>
+ files, the Dalvik VM aims to behave in a way
+ that is fully consistent with the language's definition.
+ That is, the code running in Dalvik will behave the same as it
+ would have running in any other virtual machine. This includes
+ verification failures.
+ The Dx/Dalvik system will check roughly the same
+ constraints that any other VM would, except as noted in the file
+ <a href="verifier.html">verifier.html</a>. The following table briefly
+ lists all Dx/Dalvik verification constraints together their analogs
+ from the book <i>The Java<super>TM</super> Language Specification</i>,
+ second edition. In the numbering scheme, the first three
+ elements refer to the specification chapter, the fourth one to the
+ bullet inside that chapter. The failure mode specifies whether the
+ constraint will fail during the Dx conversion or during verification in
+ the VM itself.
+ </p>
+
+ <h2>
+ Static constraints
+ </h2>
+
+ <p>
+ Static constraints are constraints on individual elements of the bytecode.
+ They usually can be checked without employing control or data-flow analysis
+ techniques.
+ </p>
+
+ <table>
+ <tr>
+ <th>
+ Identifier
+ </th>
+
+ <th>
+ Description
+ </th>
+
+ <th>
+ Spec equivalent
+ </th>
+
+ <th>
+ Failure mode
+ </th>
+ </tr>
+
+ <tr>
+ <td>
+ A1
+ </td>
+
+ <td>
+ The <code>code</code> array must not be empty.
+ </td>
+
+ <td>
+ 4.8.1.1
+ </td>
+
+ <td>
+ DX
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A2
+ </td>
+
+ <td>
+ The <code>code</code> array must not be larger than 65535 bytes.
+ </td>
+
+ <td>
+ 4.8.1.2
+ </td>
+
+ <td>
+ DX
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A3
+ </td>
+
+ <td>
+ The first opcode in <code>code</code> array must have index
+ <code>0</code>.
+ </td>
+
+ <td>
+ 4.8.1.3
+ </td>
+
+ <td>
+ DX
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A4
+ </td>
+
+ <td>
+ The <code>code</code> array must only contain valid opcodes.
+ </td>
+
+ <td>
+ 4.8.1.4
+ </td>
+
+ <td>
+ DX
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A5
+ </td>
+
+ <td>
+ The index of instruction <code>n+1</code> must equal the index of
+ instruction <code>n</code> plus the length of instruction
+ <code>n</code>, taking into account a possible <code>wide</code>
+ instruction. Opcodes modified by a <code>wide</code> instruction must
+ not be directly reachable.
+ </td>
+
+ <td>
+ 4.8.1.5
+ </td>
+
+ <td>
+ DX
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A6
+ </td>
+
+ <td>
+ The last instruction in <code>code</code> array must end at index
+ <code>code_length-1</code>.
+ </td>
+
+ <td>
+ 4.8.1.6
+ </td>
+
+ <td>
+ DX
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A7
+ </td>
+
+ <td>
+ All jump and branch targets must be opcodes within the same method.
+ Opcodes modified by a <code>wide</code> instruction must not be
+ directly reachable via a jump or branch instruction.
+ </td>
+
+ <td>
+ 4.8.1.7
+ </td>
+
+ <td>
+ DX
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A8
+ </td>
+
+ <td>
+ All targets of a <code>tableswitch</code> instruction must be opcodes
+ within the same method. Upper and lower bounds must be consistent.
+ Opcodes modified by a <code>wide</code> instruction must not be
+ directly reachable via a <code>tableswitch</code> instruction.
+ </td>
+
+ <td>
+ 4.8.1.8
+ </td>
+
+ <td>
+ DX
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A9
+ </td>
+
+ <td>
+ All targets of a <code>lookupswitch</code> instruction must be opcodes
+ within the same method. Its table must be consistent and sorted
+ low-to-high. Opcodes modified by a <code>wide</code> instruction must
+ not be directly reachable via a <code>lookupswitch</code> instruction.
+ </td>
+
+ <td>
+ 4.8.1.9
+ </td>
+
+ <td>
+ DX
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A10
+ </td>
+
+ <td>
+ The operands of <code>ldc</code> and <code>ldc_w</code> instructions
+ must be valid indices into the constant pool. The respective entries
+ must be of type <code>CONSTANT_Integer</code>,
+ <code>CONSTANT_Float</code>, or <code>CONSTANT_String</code>.
+ </td>
+
+ <td>
+ 4.8.1.10
+ </td>
+
+ <td>
+ DX
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A11
+ </td>
+
+ <td>
+ The operands of <code>ldc2_w</code> instructions must be valid indices
+ into the constant pool. The respective entries must be of type
+ <code>CONSTANT_Long</code> or <code>CONSTANT_Double</code>. The
+ subsequent constant pool entry must be valid and remain unused.
+ </td>
+
+ <td>
+ 4.8.1.11
+ </td>
+
+ <td>
+ DX
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A12
+ </td>
+
+ <td>
+ The Operands of <code>get&lt;kind&gt;</code> and
+ <code>put&lt;kind&gt;</code> instructions must be valid indices into
+ constant pool. The respective entries must be of type
+ <code>CONSTANT_Fieldref</code>.
+ </td>
+
+ <td>
+ 4.8.1.12
+ </td>
+
+ <td>
+ DX
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A13
+ </td>
+
+ <td>
+ The first two operands of <code>invokevirtual</code>,
+ <code>invokespecial</code>, and <code>invokestatic</code> must form a
+ valid 16-bit index into the constant pool. The respective entries must
+ be of type <code>CONSTANT_Methodref</code>.
+ </td>
+
+ <td>
+ 4.8.1.13
+ </td>
+
+ <td>
+ DX
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A14
+ </td>
+
+ <td>
+ Methods whose names start with '<' must only be invoked implicitly by
+ the VM, not by class file code. The only exception is the instance
+ initializer, which may be invoked by <code>invokespecial</code>.
+ </td>
+
+ <td>
+ 4.8.1.14
+ </td>
+
+ <td>
+ DX
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A15
+ </td>
+
+ <td>
+ The first two operands of <code>invokeinterface</code> must form a
+ valid 16-bit index into the constant pool. The entry must be of type
+ <code>CONSTANT_Interface_Methodref</code>. The third operand must
+ specify number of local variables and the fourth operand must always
+ be zero.
+ </td>
+
+ <td>
+ 4.8.1.15
+ </td>
+
+ <td>
+ DX
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A16
+ </td>
+
+ <td>
+ The operands of <code>instanceof</code>, <code>checkcast</code>,
+ <code>new</code>, and <code>anewarray</code> instructions must
+ be a valid index into the constant pool. The first two operands of
+ <code>multianewarray</code> instruction must form a valid 16-bit index
+ into the constant pool. All respective entries must be of type
+ <code>CONSTANT_Class</code>.
+ </td>
+
+ <td>
+ 4.8.1.16
+ </td>
+
+ <td>
+ DX
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A17
+ </td>
+
+ <td>
+ The dimensions of an array created by <code>anewarray</code>
+ instructions must be less than <code>256</code>.
+ </td>
+
+ <td>
+ 4.8.1.17
+ </td>
+
+ <td>
+ DX
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A18
+ </td>
+
+ <td>
+ The <code>new</code> instruction must not reference array classes,
+ interfaces, or abstract classes.
+ </td>
+
+ <td>
+ 4.8.1.18
+ </td>
+
+ <td>
+ DX
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A19
+ </td>
+
+ <td>
+ The type referenced by a <code>multinewarray</code> instruction must
+ have at least as many dimensions as specified in the instruction. The
+ dimensions operand must not be <code>0</code>
+ </td>
+
+ <td>
+ 4.8.1.19
+ </td>
+
+ <td>
+ DX
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A20
+ </td>
+
+ <td>
+ The type referenced by a <code>newarray</code> instruction must be a
+ valid, non-reference type.
+ </td>
+
+ <td>
+ 4.8.1.20
+ </td>
+
+ <td>
+ DX
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A21
+ </td>
+
+ <td>
+ The index operand of instructions explicitly referencing single-width
+ local variables must be non-negative and smaller than
+ <code>max_locals</code>.
+ </td>
+
+ <td>
+ 4.8.1.21
+ </td>
+
+ <td>
+ DX
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A22
+ </td>
+
+ <td>
+ The index operand of instructions implicitly referencing single-width
+ local variables must be non-negative and smaller than
+ <code>max_locals</code>.
+ </td>
+
+ <td>
+ 4.8.1.22
+ </td>
+
+ <td>
+ DX
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A23
+ </td>
+
+ <td>
+ The index operand of instructions explicitly referencing double-width
+ local variables must be non-negative and smaller than
+ <code>max_locals-1</code>.
+ </td>
+
+ <td>
+ 4.8.1.23
+ </td>
+
+ <td>
+ DX
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A24
+ </td>
+
+ <td>
+ The index operand of instructions implicitly referencing double-width
+ local variables must be non-negative and smaller than
+ <code>max_locals-1</code>.
+ </td>
+
+ <td>
+ 4.8.1.24
+ </td>
+
+ <td>
+ DX
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A25
+ </td>
+
+ <td>
+ The index operand of <code>wide</code> instructions explicitly
+ referencing single-width local variables must be non-negative and
+ smaller than <code>max_locals</code>.
+ </td>
+
+ <td>
+ 4.8.1.25
+ </td>
+
+ <td>
+ DX
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ A26
+ </td>
+
+ <td>
+ The index operand of <code>wide</code> instructions explicitly
+ referencing double-width local variables must be non-negative and
+ smaller than <code>max_locals-1</code>.
+ </td>
+
+ <td>
+ 4.8.1.25
+ </td>
+
+ <td>
+ DX
+ </td>
+ </tr>
+ </table>
+
+ <h2>
+ Structural constraints
+ </h2>
+
+ <p>
+ Structural constraints are constraints on relationships between several
+ elements of the bytecode. They usually can't be checked without employing
+ control or data-flow analysis techniques.
+ </p>
+
+ <table>
+ <tr>
+ <th>
+ Identifier
+ </th>
+
+ <th>
+ Description
+ </th>
+
+ <th>
+ Spec equivalent
+ </th>
+
+ <th>
+ Failure mode
+ </th>
+ </tr>
+
+ <tr>
+ <td>
+ B1
+ </td>
+
+ <td>
+ The number and types of arguments (operands and local variables) must
+ always match the instruction.
+ </td>
+
+ <td>
+ 4.8.2.1
+ </td>
+
+ <td>
+ DX
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B2
+ </td>
+
+ <td>
+ The operand stack must have the same depth for all executions paths
+ leading to an instruction.
+ </td>
+
+ <td>
+ 4.8.2.2
+ </td>
+
+ <td>
+ DX
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B3
+ </td>
+
+ <td>
+ Local variable pairs must never be broken up.
+ </td>
+
+ <td>
+ 4.8.2.3
+ </td>
+
+ <td>
+ DX
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B4
+ </td>
+
+ <td>
+ A local variable (or pair) has to be assigned first before it can be
+ read.
+ </td>
+
+ <td>
+ 4.8.2.4
+ </td>
+
+ <td>
+ DX
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B5
+ </td>
+
+ <td>
+ The operand stack must never grow beyond <code>max_stack</code>.
+ </td>
+
+ <td>
+ 4.8.2.5
+ </td>
+
+ <td>
+ DX
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B6
+ </td>
+
+ <td>
+ The operand stack must never underflow.
+ </td>
+
+ <td>
+ 4.8.2.6
+ </td>
+
+ <td>
+ DX
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B7
+ </td>
+
+ <td>
+ An <code>invokespecial</code> instruction must only invoke an instance
+ initializer or a method in the current class or one of its
+ superclasses.
+ </td>
+
+ <td>
+ 4.8.2.7
+ </td>
+
+ <td>
+ VM
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B8
+ </td>
+
+ <td>
+ An instance initializer must only be invoked on an uninitialized
+ instance residing on the operand stack.
+ </td>
+
+ <td>
+ 4.8.2.8
+ </td>
+
+ <td>
+ VM
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B9
+ </td>
+
+ <td>
+ Instance methods may only be invoked on and instance fields may only
+ be accessed on already initialized instances.
+ </td>
+
+ <td>
+ 4.8.2.9
+ </td>
+
+ <td>
+ VM
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B10
+ </td>
+
+ <td>
+ The must be no backwards branches with uninitialized instances on the
+ operand stack or in local variables. There must be no code protected
+ by an exception handler that contains local variables with
+ uninitialized instances.
+ </td>
+
+ <td>
+ 4.8.2.10
+ </td>
+
+ <td>
+ DX
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B11
+ </td>
+
+ <td>
+ An instance initializer must call another instance initializer (same
+ class or superclass) before any instance members can be accessed.
+ Exceptions are non-inherited instance fields, which can be assigned
+ before calling another initializer, and the <code>Object</code> class
+ in general.
+ </td>
+
+ <td>
+ 4.8.2.11
+ </td>
+
+ <td>
+ VM
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B12
+ </td>
+
+ <td>
+ All actual method arguments must be assignment-compatible with formal
+ arguments.
+ </td>
+
+ <td>
+ 4.8.2.12
+ </td>
+
+ <td>
+ VM
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B13
+ </td>
+
+ <td>
+ For each instance method invocation, the actual instance must be
+ assignment-compatible with the class or interface specified in the
+ instruction.
+ </td>
+
+ <td>
+ 4.8.2.13
+ </td>
+
+ <td>
+ VM
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B14
+ </td>
+
+ <td>
+ A returns instruction must match its method's return type.
+ </td>
+
+ <td>
+ 4.8.2.14
+ </td>
+
+ <td>
+ VM
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B15
+ </td>
+
+ <td>
+ When accessing protected members of a superclass, the actual type of
+ the instance being accessed must be either the current class or one
+ of its subclasses.
+ </td>
+
+ <td>
+ 4.8.2.15
+ </td>
+
+ <td>
+ VM
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B16
+ </td>
+
+ <td>
+ The type of a value stored into a static field must be
+ assignment-compatible with or convertible to the field's type.
+ </td>
+
+ <td>
+ 4.8.2.16
+ </td>
+
+ <td>
+ VM
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B17
+ </td>
+
+ <td>
+ The type of a value stored into a field must be assignment-compatible
+ with or convertible to the field's type.
+ </td>
+
+ <td>
+ 4.8.2.17
+ </td>
+
+ <td>
+ VM
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B18
+ </td>
+
+ <td>
+ The type of every value stored into an array must be
+ assignment-compatible with the array's component type.
+ </td>
+
+ <td>
+ 4.8.2.18
+ </td>
+
+ <td>
+ VM
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B19
+ </td>
+
+ <td>
+ The operand of an <code>athrow</code> instruction must be
+ assignment-compatible with <code>java.lang.Throwable</code>.
+ </td>
+
+ <td>
+ 4.8.2.19
+ </td>
+
+ <td>
+ VM
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B20
+ </td>
+
+ <td>
+ The last reachable instruction of a method must either be a backwards
+ jump or branch, a return, or an <code>athrow</code> instruction. It
+ must not be possible to leave the <code>code</code> array at the
+ bottom.
+ </td>
+
+ <td>
+ 4.8.2.20
+ </td>
+
+ <td>
+ VM
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B21
+ </td>
+
+ <td>
+ Local variable values must not be used as return addresses.
+ </td>
+
+ <td>
+ 4.8.2.21
+ </td>
+
+ <td>
+ VM
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B22
+ </td>
+
+ <td>
+ There must be a single, uniquely determined return instruction per
+ subroutine call.
+ </td>
+
+ <td>
+ 4.8.2.22
+ </td>
+
+ <td>
+ VM
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B23
+ </td>
+
+ <td>
+ Subroutine calls must not be directly or indirectly self-recursive.
+ </td>
+
+ <td>
+ 4.8.2.23
+ </td>
+
+ <td>
+ DX
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ B24
+ </td>
+
+ <td>
+ <code>ReturnAddress</code> instances must not be reused. If a
+ subroutine returns to a <code>ReturnAddress</code> further up the
+ stack than where its original call instruction is located, then all
+ <code>ReturnAddress</code> instances further down the stack must
+ never be used.
+ </td>
+
+ <td>
+ 4.8.2.24
+ </td>
+
+ <td>
+ DX
+ </td>
+ </tr>
+
+ </table>
+ </body>
+</html>
diff --git a/docs/jni-tips.html b/docs/jni-tips.html
new file mode 100644
index 0000000..c01c107
--- /dev/null
+++ b/docs/jni-tips.html
@@ -0,0 +1,770 @@
+<html>
+ <head>
+ <title>Android JNI Tips</title>
+ <link rel=stylesheet href="android.css">
+ </head>
+
+ <body>
+ <h1><a name="JNI_Tips"></a>Android JNI Tips</h1>
+<p>
+</p><p>
+</p><ul>
+<li> <a href="#What_s_JNI_">What's JNI?</a>
+</li>
+<li> <a href="#JavaVM_and_JNIEnv">JavaVM and JNIEnv</a>
+</li>
+<li> <a href="#Threads">Threads</a>
+</li>
+<li> <a href="#jclass_jmethodID_and_jfieldID">jclass, jmethodID, and jfieldID</a>
+</li>
+<li> <a href="#local_vs_global_references">Local vs. Global References</a>
+</li>
+<li> <a href="#UTF_8_and_UTF_16_strings">UTF-8 and UTF-16 Strings</a>
+</li>
+<li> <a href="#Arrays">Primitive Arrays</a>
+</li>
+<li> <a href="#RegionCalls">Region Calls</a>
+</li>
+<li> <a href="#Exceptions">Exceptions</a>
+</li>
+
+<li> <a href="#Extended_checking">Extended Checking</a>
+</li>
+<li> <a href="#Native_Libraries">Native Libraries</a>
+</li>
+<li> <a href="#64bit">64-bit Considerations</a>
+</li>
+
+<li> <a href="#Unsupported">Unsupported Features</a>
+</li>
+
+<li> <a href="#FAQUnsatisfied">FAQ: UnsatisfiedLinkError</a>
+</li>
+<li> <a href="#FAQFindClass">FAQ: FindClass didn't find my class</a>
+</li>
+<li> <a href="#FAQSharing">FAQ: Sharing raw data with native code</a>
+</li>
+
+</ul>
+<p>
+<noautolink>
+</noautolink></p><p>
+</p><h2><a name="What_s_JNI_"> </a> What's JNI? </h2>
+<p>
+
+JNI is the Java Native Interface. It defines a way for code written in the
+Java programming language to interact with native
+code, e.g. functions written in C/C++. It's VM-neutral, has support for loading code from
+dynamic shared libraries, and while cumbersome at times is reasonably efficient.
+</p><p>
+You really should read through the
+<a href="http://java.sun.com/javase/6/docs/technotes/guides/jni/spec/jniTOC.html">JNI spec for J2SE 1.6</a>
+to get a sense for how JNI works and what features are available. Some
+aspects of the interface aren't immediately obvious on
+first reading, so you may find the next few sections handy.
+The more detailed <i>JNI Programmer's Guide and Specification</i> can be found
+<a href="http://java.sun.com/docs/books/jni/html/jniTOC.html">here</a>.
+</p><p>
+</p><p>
+</p><h2><a name="JavaVM_and_JNIEnv"> </a> JavaVM and JNIEnv </h2>
+<p>
+JNI defines two key data structures, "JavaVM" and "JNIEnv". Both of these are essentially
+pointers to pointers to function tables. (In the C++ version, it's a class whose sole member
+is a pointer to a function table.) The JavaVM provides the "invocation interface" functions,
+which allow you to create and destroy the VM. In theory you can have multiple VMs per process,
+but Android's VM only allows one.
+</p><p>
+The JNIEnv provides most of the JNI functions. Your native functions all receive a JNIEnv as
+the first argument.
+</p><p>
+
+On some VMs, the JNIEnv is used for thread-local storage. For this reason, <strong>you cannot share a JNIEnv between threads</strong>.
+If a piece of code has no other way to get its JNIEnv, you should share
+the JavaVM, and use JavaVM-&gt;GetEnv to discover the thread's JNIEnv.
+</p><p>
+The C declarations of JNIEnv and JavaVM are different from the C++
+declarations. "jni.h" provides different typedefs
+depending on whether it's included into ".c" or ".cpp". For this reason it's a bad idea to
+include JNIEnv arguments in header files included by both languages. (Put another way: if your
+header file requires "#ifdef __cplusplus", you may have to do some extra work if anything in
+that header refers to JNIEnv.)
+
+</p><p>
+</p><h2><a name="Threads"> Threads </a></h2>
+<p>
+All VM threads are Linux threads, scheduled by the kernel. They're usually
+started using Java language features (notably <code>Thread.start()</code>),
+but they can also be created elsewhere and then attached to the VM. For
+example, a thread started with <code>pthread_create</code> can be attached
+with the JNI <code>AttachCurrentThread</code> or
+<code>AttachCurrentThreadAsDaemon</code> functions. Until a thread is
+attached to the VM, it has no JNIEnv, and
+<strong>cannot make JNI calls</strong>.
+</p><p>
+Attaching a natively-created thread causes the VM to allocate and initialize
+a <code>Thread</code> object, add it to the "main" <code>ThreadGroup</code>,
+and add the thread to the set that is visible to the debugger. Calling
+<code>AttachCurrentThread</code> on an already-attached thread is a no-op.
+</p><p>
+The Dalvik VM does not suspend threads executing native code. If
+garbage collection is in progress, or the debugger has issued a suspend
+request, the VM will pause the thread the next time it makes a JNI call.
+</p><p>
+Threads attached through JNI <strong>must call
+<code>DetachCurrentThread</code> before they exit</strong>.
+If coding this directly is awkward, in Android &gt;= 2.0 you
+can use <code>pthread_key_create</code> to define a destructor
+function that will be called before the thread exits, and
+call <code>DetachCurrentThread</code> from there. (Use that
+key with <code>pthread_setspecific</code> to store the JNIEnv in
+thread-local-storage; that way it'll be passed into your destructor as
+the argument.)
+
+
+</p><h2><a name="jclass_jmethodID_and_jfieldID"> jclass, jmethodID, and jfieldID </a></h2>
+<p>
+If you want to access an object's field from native code, you would do the following:
+</p><p>
+</p><ul>
+<li> Get the class object reference for the class with <code>FindClass</code>
+</li>
+<li> Get the field ID for the field with <code>GetFieldID</code>
+</li>
+<li> Get the contents of the field with something appropriate, e.g.
+<code>GetIntField</code>
+</li>
+</ul>
+<p>
+Similarly, to call a method, you'd first get a class object reference and then a method ID. The IDs are often just
+pointers to internal VM data structures. Looking them up may require several string
+comparisons, but once you have them the actual call to get the field or invoke the method
+is very quick.
+</p><p>
+If performance is important, it's useful to look the values up once and cache the results
+in your native code. Because we are limiting ourselves to one VM per process, it's reasonable
+to store this data in a static local structure.
+</p><p>
+The class references, field IDs, and method IDs are guaranteed valid until the class is unloaded. Classes
+are only unloaded if all classes associated with a ClassLoader can be garbage collected,
+which is rare but will not be impossible in our system. Note however that
+the <code>jclass</code>
+is a class reference and <strong>must be protected</strong> with a call
+to <code>NewGlobalRef</code> (see the next section).
+</p><p>
+If you would like to cache the IDs when a class is loaded, and automatically re-cache them
+if the class is ever unloaded and reloaded, the correct way to initialize
+the IDs is to add a piece of code that looks like this to the appropriate class:
+</p><p>
+
+</p><pre> /*
+ * We use a class initializer to allow the native code to cache some
+ * field offsets.
+ */
+
+ /*
+ * A native function that looks up and caches interesting
+ * class/field/method IDs for this class. Returns false on failure.
+ */
+ native private static boolean nativeClassInit();
+
+ /*
+ * Invoke the native initializer when the class is loaded.
+ */
+ static {
+ if (!nativeClassInit())
+ throw new RuntimeException("native init failed");
+ }
+</pre>
+<p>
+Create a nativeClassInit method in your C/C++ code that performs the ID lookups. The code
+will be executed once, when the class is initialized. If the class is ever unloaded and
+then reloaded, it will be executed again. (See the implementation of java.io.FileDescriptor
+for an example in our source tree.)
+</p><p>
+</p><p>
+</p><p>
+</p><h2><a name="local_vs_global_references"> Local vs. Global References </a></h2>
+<p>
+Every object that JNI returns is a "local reference". This means that it's valid for the
+duration of the current native method in the current thread.
+<strong>Even if the object itself continues to live on after the native method returns, the reference is not valid.</strong>
+This applies to all sub-classes of <code>jobject</code>, including
+<code>jclass</code>, <code>jstring</code>, and <code>jarray</code>.
+(Dalvik VM will warn you about most reference mis-uses when extended JNI
+checks are enabled.)
+</p><p>
+
+If you want to hold on to a reference for a longer period, you must use
+a "global" reference. The <code>NewGlobalRef</code> function takes the
+local reference as an argument and returns a global one.
+The global reference is guaranteed to be valid until you call
+<code>DeleteGlobalRef</code>.
+
+</p><p>
+This pattern is commonly used when caching copies of class objects obtained
+from <code>FindClass</code>, e.g.:
+<p><pre>jclass* localClass = env-&gt;FindClass("MyClass");
+jclass* globalClass = (jclass*) env-&gt;NewGlobalRef(localClass);
+</pre>
+
+</p><p>
+All JNI methods accept both local and global references as arguments.
+It's possible for references to the same object to have different values;
+for example, the return values from consecutive calls to
+<code>NewGlobalRef</code> on the same object may be different.
+<strong>To see if two references refer to the same object,
+you must use the <code>IsSameObject</code> function.</strong> Never compare
+references with "==" in native code.
+</p><p>
+One consequence of this is that you
+<strong>must not assume object references are constant or unique</strong>
+in native code. The 32-bit value representing an object may be different
+from one invocation of a method to the next, and it's possible that two
+different objects could have the same 32-bit value on consecutive calls. Do
+not use <code>jobject</code> values as keys.
+</p><p>
+Programmers are required to "not excessively allocate" local references. In practical terms this means
+that if you're creating large numbers of local references, perhaps while running through an array of
+Objects, you should free them manually with
+<code>DeleteLocalRef</code> instead of letting JNI do it for you. The
+VM is only required to reserve slots for
+16 local references, so if you need more than that you should either delete as you go or use
+<code>EnsureLocalCapacity</code> to reserve more.
+</p><p>
+Note: method and field IDs are just 32-bit identifiers, not object
+references, and should not be passed to <code>NewGlobalRef</code>. The raw data
+pointers returned by functions like <code>GetStringUTFChars</code>
+and <code>GetByteArrayElements</code> are also not objects.
+</p><p>
+One unusual case deserves separate mention. If you attach a native
+thread to the VM with AttachCurrentThread, the code you are running will
+never "return" to the VM until the thread detaches from the VM. Any local
+references you create will have to be deleted manually unless you're going
+to detach the thread soon.
+</p><p>
+</p><p>
+</p><p>
+</p><h2><a name="UTF_8_and_UTF_16_strings"> </a> UTF-8 and UTF-16 Strings </h2>
+<p>
+The Java programming language uses UTF-16. For convenience, JNI provides methods that work with "modified UTF-8" encoding
+as well. (Some VMs use the modified UTF-8 internally to store strings; ours do not.) The
+modified encoding only supports the 8- and 16-bit forms, and stores ASCII NUL values in a 16-bit encoding.
+The nice thing about it is that you can count on having C-style zero-terminated strings,
+suitable for use with standard libc string functions. The down side is that you cannot pass
+arbitrary UTF-8 data into the VM and expect it to work correctly.
+</p><p>
+It's usually best to operate with UTF-16 strings. With our current VMs, the
+<code>GetStringChars</code> method
+does not require a copy, whereas <code>GetStringUTFChars</code> requires a malloc and a UTF conversion. Note that
+<strong>UTF-16 strings are not zero-terminated</strong>, and \u0000 is allowed,
+so you need to hang on to the string length as well as
+the string pointer.
+
+</p><p>
+<strong>Don't forget to Release the strings you Get</strong>. The
+string functions return <code>jchar*</code> or <code>jbyte*</code>, which
+are C-style pointers to primitive data rather than local references. They
+are guaranteed valid until Release is called, which means they are not
+released when the native method returns.
+</p><p>
+<strong>Data passed to NewStringUTF must be in "modified" UTF-8 format</strong>. A
+common mistake is reading character data from a file or network stream
+and handing it to <code>NewStringUTF</code> without filtering it.
+Unless you know the data is 7-bit ASCII, you need to strip out high-ASCII
+characters or convert them to proper "modified" UTF-8 form. If you don't,
+the UTF-16 conversion will likely not be what you expect. The extended
+JNI checks will scan strings and warn you about invalid data, but they
+won't catch everything.
+</p><p>
+</p><p>
+
+
+</p><h2><a name="Arrays"> </a> Primitive Arrays </h2>
+<p>
+JNI provides functions for accessing the contents of array objects.
+While arrays of objects must be accessed one entry at a time, arrays of
+primitives can be read and written directly as if they were declared in C.
+</p><p>
+To make the interface as efficient as possible without constraining
+the VM implementation,
+the <code>Get&lt;PrimitiveType&gt;ArrayElements</code> family of calls
+allows the VM to either return a pointer to the actual elements, or
+allocate some memory and make a copy. Either way, the raw pointer returned
+is guaranteed to be valid until the corresponding <code>Release</code> call
+is issued (which implies that, if the data wasn't copied, the array object
+will be pinned down and can't be relocated as part of compacting the heap).
+<strong>You must Release every array you Get.</strong> Also, if the Get
+call fails, you must ensure that your code doesn't try to Release a NULL
+pointer later.
+</p><p>
+You can determine whether or not the data was copied by passing in a
+non-NULL pointer for the <code>isCopy</code> argument. This is rarely
+useful.
+</p><p>
+The <code>Release</code> call takes a <code>mode</code> argument that can
+have one of three values. The actions performed by the VM depend upon
+whether it returned a pointer to the actual data or a copy of it:
+<ul>
+ <li><code>0</code>
+ <ul>
+ <li>Actual: the array object is un-pinned.
+ <li>Copy: data is copied back. The buffer with the copy is freed.
+ </ul>
+ <li><code>JNI_COMMIT</code>
+ <ul>
+ <li>Actual: does nothing.
+ <li>Copy: data is copied back. The buffer with the copy
+ <strong>is not freed</strong>.
+ </ul>
+ <li><code>JNI_ABORT</code>
+ <ul>
+ <li>Actual: the array object is un-pinned. Earlier
+ writes are <strong>not</strong> aborted.
+ <li>Copy: the buffer with the copy is freed; any changes to it are lost.
+ </ul>
+</ul>
+</p><p>
+One reason for checking the <code>isCopy</code> flag is to know if
+you need to call <code>Release</code> with <code>JNI_COMMIT</code>
+after making changes to an array &mdash; if you're alternating between making
+changes and executing code that uses the contents of the array, you may be
+able to
+skip the no-op commit. Another possible reason for checking the flag is for
+efficient handling of <code>JNI_ABORT</code>. For example, you might want
+to get an array, modify it in place, pass pieces to other functions, and
+then discard the changes. If you know that JNI is making a new copy for
+you, there's no need to create another "editable" copy. If JNI is passing
+you the original, then you do need to make your own copy.
+</p><p>
+Some have asserted that you can skip the <code>Release</code> call if
+<code>*isCopy</code> is false. This is not the case. If no copy buffer was
+allocated, then the original memory must be pinned down and can't be moved by
+the garbage collector.
+</p><p>
+Also note that the <code>JNI_COMMIT</code> flag does NOT release the array,
+and you will need to call <code>Release</code> again with a different flag
+eventually.
+</p><p>
+</p><p>
+
+
+</p><h2><a name="RegionCalls"> Region Calls </a></h2>
+
+<p>
+There is an alternative to calls like <code>Get&lt;Type&gt;ArrayElements</code>
+and <code>GetStringChars</code> that may be very helpful when all you want
+to do is copy data in or out. Consider the following:
+<pre>
+ jbyte* data = env->GetByteArrayElements(array, NULL);
+ if (data != NULL) {
+ memcpy(buffer, data, len);
+ env->ReleaseByteArrayElements(array, data, JNI_ABORT);
+ }
+</pre>
+<p>
+This grabs the array, copies the first <code>len</code> byte
+elements out of it, and then releases the array. Depending upon the VM
+policies the <code>Get</code> call will either pin or copy the array contents.
+We copy the data (for perhaps a second time), then call Release; in this case
+we use <code>JNI_ABORT</code> so there's no chance of a third copy.
+</p><p>
+We can accomplish the same thing with this:
+<pre>
+ env->GetByteArrayRegion(array, 0, len, buffer);
+</pre>
+</p><p>
+This has several advantages:
+<ul>
+ <li>Requires one JNI call instead of 2, reducing overhead.
+ <li>Doesn't require pinning or extra data copies.
+ <li>Reduces the risk of programmer error &mdash; no risk of forgetting
+ to call <code>Release</code> after something fails.
+</ul>
+</p><p>
+Similarly, you can use the <code>Set&lt;Type&gt;ArrayRegion</code> call
+to copy data into an array, and <code>GetStringRegion</code> or
+<code>GetStringUTFRegion</code> to copy characters out of a
+<code>String</code>.
+
+
+</p><h2><a name="Exceptions"> Exceptions </a></h2>
+<p>
+<strong>You may not call most JNI functions while an exception is pending.</strong>
+Your code is expected to notice the exception (via the function's return value,
+<code>ExceptionCheck()</code>, or <code>ExceptionOccurred()</code>) and return,
+or clear the exception and handle it.
+</p><p>
+The only JNI functions that you are allowed to call while an exception is
+pending are:
+<font size="-1"><ul>
+ <li>DeleteGlobalRef
+ <li>DeleteLocalRef
+ <li>DeleteWeakGlobalRef
+ <li>ExceptionCheck
+ <li>ExceptionClear
+ <li>ExceptionDescribe
+ <li>ExceptionOccurred
+ <li>MonitorExit
+ <li>PopLocalFrame
+ <li>PushLocalFrame
+ <li>Release&lt;PrimitiveType&gt;ArrayElements
+ <li>ReleasePrimitiveArrayCritical
+ <li>ReleaseStringChars
+ <li>ReleaseStringCritical
+ <li>ReleaseStringUTFChars
+</ul></font>
+</p><p>
+Many JNI calls can throw an exception, but often provide a simpler way
+of checking for failure. For example, if <code>NewString</code> returns
+a non-NULL value, you don't need to check for an exception. However, if
+you call a method (using a function like <code>CallObjectMethod</code>),
+you must always check for an exception, because the return value is not
+going to be valid if an exception was thrown.
+</p><p>
+Note that exceptions thrown by interpreted code do not "leap over" native code,
+and C++ exceptions thrown by native code are not handled by Dalvik.
+The JNI <code>Throw</code> and <code>ThrowNew</code> instructions just
+set an exception pointer in the current thread. Upon returning to the VM from
+native code, the exception will be noted and handled appropriately.
+</p><p>
+Native code can "catch" an exception by calling <code>ExceptionCheck</code> or
+<code>ExceptionOccurred</code>, and clear it with
+<code>ExceptionClear</code>. As usual,
+discarding exceptions without handling them can lead to problems.
+</p><p>
+There are no built-in functions for manipulating the Throwable object
+itself, so if you want to (say) get the exception string you will need to
+find the Throwable class, look up the method ID for
+<code>getMessage "()Ljava/lang/String;"</code>, invoke it, and if the result
+is non-NULL use <code>GetStringUTFChars</code> to get something you can
+hand to printf or a LOG macro.
+
+</p><p>
+</p><p>
+</p><h2><a name="Extended_checking"> Extended Checking </a></h2>
+<p>
+JNI does very little error checking. Calling <code>SetIntField</code>
+on an Object field will succeed, even if the field is marked
+<code>private</code> and <code>final</code>. The
+goal is to minimize the overhead on the assumption that, if you've written it in native code,
+you probably did it for performance reasons.
+</p><p>
+In Dalvik, you can enable additional checks by setting the
+"<code>-Xcheck:jni</code>" flag. If the flag is set, the VM directs
+the JavaVM and JNIEnv pointers to a different table of functions.
+These functions perform an extended series of checks before calling the
+standard implementation.
+
+</p><p>
+The additional tests include:
+</p><p>
+</p>
+<ul>
+<li> Check for null pointers where not allowed.
+</li>
+<li> Verify argument type correctness (jclass is a class object,
+jfieldID points to field data, jstring is a java.lang.String).
+</li>
+<li> Field type correctness, e.g. don't store a HashMap in a String field.
+</li>
+<li> Ensure jmethodID is appropriate when making a static or virtual
+method call.
+</li>
+<li> Check to see if an exception is pending on calls where pending exceptions are not legal.
+</li>
+<li> Check for calls to inappropriate functions between Critical get/release calls.
+</li>
+<li> Check that JNIEnv structs aren't being shared between threads.
+
+</li>
+<li> Make sure local references aren't used outside their allowed lifespan.
+</li>
+<li> UTF-8 strings contain only valid "modified UTF-8" data.
+</li>
+</ul>
+<p>Accessibility of methods and fields (i.e. public vs. private) is not
+checked.
+<p>
+For a description of how to enable CheckJNI for Android apps, see
+<a href="embedded-vm-control.html">Controlling the Embedded VM</a>.
+It's currently enabled by default in the Android emulator and on
+"engineering" device builds.
+
+</p><p>
+JNI checks can be modified with the <code>-Xjniopts</code> command-line
+flag. Currently supported values include:
+</p>
+<blockquote><dl>
+<dt>forcecopy
+<dd>When set, any function that can return a copy of the original data
+(array of primitive values, UTF-16 chars) will always do so. The buffers
+are over-allocated and surrounded with a guard pattern to help identify
+code writing outside the buffer, and the contents are erased before the
+storage is freed to trip up code that uses the data after calling Release.
+This will have a noticeable performance impact on some applications.
+<dt>warnonly
+<dd>By default, JNI "warnings" cause the VM to abort. With this flag
+it continues on.
+</dl></blockquote>
+
+
+</p><p>
+</p><h2><a name="Native_Libraries"> Native Libraries </a></h2>
+<p>
+You can load native code from shared libraries with the standard
+<code>System.loadLibrary()</code> call. The
+preferred way to get at your native code is:
+</p><p>
+</p><ul>
+<li> Call <code>System.loadLibrary()</code> from a static class
+initializer. (See the earlier example, where one is used to call
+<code>nativeClassInit()</code>.) The argument is the "undecorated"
+library name, e.g. to load "libfubar.so" you would pass in "fubar".
+
+</li>
+<li> Provide a native function: <code><strong>jint JNI_OnLoad(JavaVM* vm, void* reserved)</strong></code>
+</li>
+<li>In <code>JNI_OnLoad</code>, register all of your native methods. You
+should declare
+the methods "static" so the names don't take up space in the symbol table
+on the device.
+</li>
+</ul>
+<p>
+The <code>JNI_OnLoad</code> function should look something like this if
+written in C:
+</p><blockquote><pre>jint JNI_OnLoad(JavaVM* vm, void* reserved)
+{
+ JNIEnv* env;
+ if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_6) != JNI_OK)
+ return -1;
+
+ /* get class with (*env)->FindClass */
+ /* register methods with (*env)->RegisterNatives */
+
+ return JNI_VERSION_1_6;
+}
+</pre></blockquote>
+</p><p>
+You can also call <code>System.load()</code> with the full path name of the
+shared library. For Android apps, you may find it useful to get the full
+path to the application's private data storage area from the context object.
+</p><p>
+This is the recommended approach, but not the only approach. The VM does
+not require explicit registration, nor that you provide a
+<code>JNI_OnLoad</code> function.
+You can instead use "discovery" of native methods that are named in a
+specific way (see <a href="http://java.sun.com/javase/6/docs/technotes/guides/jni/spec/design.html#wp615">
+ the JNI spec</a> for details), though this is less desirable.
+It requires more space in the shared object symbol table,
+loading is slower because it requires string searches through all of the
+loaded shared libraries, and if a method signature is wrong you won't know
+about it until the first time the method is actually used.
+</p><p>
+One other note about <code>JNI_OnLoad</code>: any <code>FindClass</code>
+calls you make from there will happen in the context of the class loader
+that was used to load the shared library. Normally <code>FindClass</code>
+uses the loader associated with the method at the top of the interpreted
+stack, or if there isn't one (because the thread was just attached to
+the VM) it uses the "system" class loader. This makes
+<code>JNI_OnLoad</code> a convenient place to look up and cache class
+object references.
+</p><p>
+
+
+</p><h2><a name="64bit"> 64-bit Considerations </a></h2>
+
+<p>
+Android is currently expected to run on 32-bit platforms. In theory it
+could be built for a 64-bit system, but that is not a goal at this time.
+For the most part this isn't something that you will need to worry about
+when interacting with native code,
+but it becomes significant if you plan to store pointers to native
+structures in integer fields in an object. To support architectures
+that use 64-bit pointers, <strong>you need to stash your native pointers in a
+<code>long</code> field rather than an <code>int</code></strong>.
+
+
+</p><h2><a name="Unsupported"> Unsupported Features </a></h2>
+<p>All JNI 1.6 features are supported, with the following exceptions:
+<ul>
+ <li><code>DefineClass</code> is not implemented. Dalvik does not use
+ Java bytecodes or class files, so passing in binary class data
+ doesn't work. Translation facilities may be added in a future
+ version of the VM.</li>
+ <li>"Weak global" references are implemented, but may only be passed
+ to <code>NewLocalRef</code>, <code>NewGlobalRef</code>, and
+ <code>DeleteWeakGlobalRef</code>. (The spec strongly encourages
+ programmers to create hard references to weak globals before doing
+ anything with them, so this should not be at all limiting.)</li>
+ <li><code>GetObjectRefType</code> (new in 1.6) is implemented but not fully
+ functional &mdash; it can't always tell the difference between "local" and
+ "global" references.</li>
+</ul>
+
+<p>For backward compatibility, you may need to be aware of:
+<ul>
+ <li>Until 2.0 ("Eclair"), the '$' character was not properly
+ converted to "_00024" during searches for method names. Working
+ around this requires using explicit registration or moving the
+ native methods out of inner classes.
+ <li>Until 2.0, it was not possible to use a <code>pthread_key_create</code>
+ destructor function to avoid the VM's "thread must be detached before
+ exit" check. (The VM also uses a pthread key destructor function,
+ so it'd be a race to see which gets called first.)
+ <li>"Weak global" references were not implemented until 2.2 ("Froyo").
+ Older VMs will vigorously reject attempts to use them. You can use
+ the Android platform version constants to test for support.
+</ul>
+
+
+</p><h2><a name="FAQUnsatisfied"> FAQ: UnsatisfiedLinkError </a></h2>
+<p>
+When working on native code it's not uncommon to see a failure like this:
+<pre>java.lang.UnsatisfiedLinkError: Library foo not found</pre>
+<p>
+In some cases it means what it says &mdash; the library wasn't found. In
+other cases the library exists but couldn't be opened by dlopen(), and
+the details of the failure can be found in the exception's detail message.
+<p>
+Common reasons why you might encounter "library not found" exceptions:
+<ul>
+ <li>The library doesn't exist or isn't accessible to the app. Use
+ <code>adb shell ls -l &lt;path&gt;</code> to check its presence
+ and permissions.
+ <li>The library wasn't built with the NDK. This can result in
+ dependencies on functions or libraries that don't exist on the device.
+</ul>
+</p><p>
+Another class of <code>UnsatisfiedLinkError</code> failures looks like:
+<pre>java.lang.UnsatisfiedLinkError: myfunc
+ at Foo.myfunc(Native Method)
+ at Foo.main(Foo.java:10)</pre>
+<p>
+In logcat, you'll see:
+<pre>W/dalvikvm( 880): No implementation found for native LFoo;.myfunc ()V</pre>
+<p>
+This means that the VM tried to find a matching method but was unsuccessful.
+Some common reasons for this are:
+<ul>
+ <li>The library isn't getting loaded. Check the logcat output for
+ messages about library loading.
+ <li>The method isn't being found due to a name or signature mismatch. This
+ is commonly caused by:
+ <ul>
+ <li>For lazy method lookup, failing to declare C++ functions
+ with <code>extern C</code>. You can use <code>arm-eabi-nm</code>
+ to see the symbols as they appear in the library; if they look
+ mangled (e.g. <code>_Z15Java_Foo_myfuncP7_JNIEnvP7_jclass</code>
+ rather than <code>Java_Foo_myfunc</code>) then you need to
+ adjust the declaration.
+ <li>For explicit registration, minor errors when entering the
+ method signature. Make sure that what you're passing to the
+ registration call matches the signature in the log file.
+ Remember that 'B' is <code>byte</code> and 'Z' is <code>boolean</code>.
+ Class name components in signatures start with 'L', end with ';',
+ use '/' to separate package/class names, and use '$' to separate
+ inner-class names
+ (e.g. <code>Ljava/util/Map$Entry;</code>).
+ </ul>
+</ul>
+<p>
+Using <code>javah</code> to automatically generate JNI headers may help
+avoid some problems.
+
+
+</p><h2><a name="FAQFindClass"> FAQ: FindClass didn't find my class </a></h2>
+<p>
+Make sure that the class name string has the correct format. JNI class
+names start with the package name and are separated with slashes,
+e.g. <code>java/lang/String</code>. If you're looking up an array class,
+you need to start with the appropriate number of square brackets and
+must also wrap the class with 'L' and ';', so a one-dimensional array of
+<code>String</code> would be <code>[Ljava/lang/String;</code>.
+</p><p>
+If the class name looks right, you could be running into a class loader
+issue. <code>FindClass</code> wants to start the class search in the
+class loader associated with your code. It examines the VM call stack,
+which will look something like:
+<pre> Foo.myfunc(Native Method)
+ Foo.main(Foo.java:10)
+ dalvik.system.NativeStart.main(Native Method)</pre>
+<p>
+The topmost method is <code>Foo.myfunc</code>. <code>FindClass</code>
+finds the <code>ClassLoader</code> object associated with the <code>Foo</code>
+class and uses that.
+</p><p>
+This usually does what you want. You can get into trouble if you
+create a thread outside the VM (perhaps by calling <code>pthread_create</code>
+and then attaching it to the VM with <code>AttachCurrentThread</code>).
+Now the stack trace looks like this:
+<pre> dalvik.system.NativeStart.run(Native Method)</pre>
+<p>
+The topmost method is <code>NativeStart.run</code>, which isn't part of
+your application. If you call <code>FindClass</code> from this thread, the
+VM will start in the "system" class loader instead of the one associated
+with your application, so attempts to find app-specific classes will fail.
+</p><p>
+There are a few ways to work around this:
+<ul>
+ <li>Do your <code>FindClass</code> lookups once, in
+ <code>JNI_OnLoad</code>, and cache the class references for later
+ use. Any <code>FindClass</code> calls made as part of executing
+ <code>JNI_OnLoad</code> will use the class loader associated with
+ the function that called <code>System.loadLibrary</code> (this is a
+ special rule, provided to make library initialization more convenient).
+ If your app code is loading the library, <code>FindClass</code>
+ will use the correct class loader.
+ <li>Pass an instance of the class into the functions that need
+ it, e.g. declare your native method to take a Class argument and
+ then pass <code>Foo.class</code> in.
+ <li>Cache a reference to the <code>ClassLoader</code> object somewhere
+ handy, and issue <code>loadClass</code> calls directly. This requires
+ some effort.
+</ul>
+
+</p><p>
+
+
+</p><h2><a name="FAQSharing"> FAQ: Sharing raw data with native code </a></h2>
+<p>
+You may find yourself in a situation where you need to access a large
+buffer of raw data from code written in Java and C/C++. Common examples
+include manipulation of bitmaps or sound samples. There are two
+basic approaches.
+</p><p>
+You can store the data in a <code>byte[]</code>. This allows very fast
+access from code written in Java. On the native side, however, you're
+not guaranteed to be able to access the data without having to copy it. In
+some implementations, <code>GetByteArrayElements</code> and
+<code>GetPrimitiveArrayCritical</code> will return actual pointers to the
+raw data in the managed heap, but in others it will allocate a buffer
+on the native heap and copy the data over.
+</p><p>
+The alternative is to store the data in a direct byte buffer. These
+can be created with <code>java.nio.ByteBuffer.allocateDirect</code>, or
+the JNI <code>NewDirectByteBuffer</code> function. Unlike regular
+byte buffers, the storage is not allocated on the managed heap, and can
+always be accessed directly from native code (get the address
+with <code>GetDirectBufferAddress</code>). Depending on how direct
+byte buffer access is implemented in the VM, accessing the data from code
+written in Java can be very slow.
+</p><p>
+The choice of which to use depends on two factors:
+<ol>
+ <li>Will most of the data accesses happen from code written in Java
+ or in C/C++?
+ <li>If the data is eventually being passed to a system API, what form
+ must it be in? (For example, if the data is eventually passed to a
+ function that takes a byte[], doing processing in a direct
+ <code>ByteBuffer</code> might be unwise.)
+</ol>
+If there's no clear winner, use a direct byte buffer. Support for them
+is built directly into JNI, and access to them from code written in
+Java can be made faster with VM improvements.
+</p>
+
+<address>Copyright &copy; 2008 The Android Open Source Project</address>
+
+ </body>
+</html>
diff --git a/docs/libraries.html b/docs/libraries.html
new file mode 100644
index 0000000..ed2fa72
--- /dev/null
+++ b/docs/libraries.html
@@ -0,0 +1,165 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+
+<title>Dalvik Libraries</title>
+
+<link rel=stylesheet href="dex-format.css">
+<link href="prettify.css" type="text/css" rel="stylesheet" />
+<script type="text/javascript" src="prettify.js"></script>
+
+<style>
+ul.code li {
+ font-family: monospace;
+}
+</style>
+
+</head>
+
+<body onload="prettyPrint()">
+
+<h1 class="title">Dalvik Libraries</h1>
+
+<p>The Dalvik Libraries, also known as the <i>Android core libraries</i>,
+implement general purpose APIs used by code written in the Java programming
+language. While the libraries themselves don't depend on Android, they do form
+the foundation of the Android framework. Android applications use the Dalvik
+libraries both directly and indirectly for data structures, networking,
+concurrency, I/O, and more.</p>
+
+<p>The Dalvik libraries break down into two categories:</p>
+
+<ul>
+ <li><a href="#vm-specific">Dalvik VM-specific libraries</a></li>
+ <li><a href="#interop">Java programming language interoperability
+ libraries</a></li>
+</ul>
+
+<p>Any system claiming to be Android-compatible must implement these libraries.
+Unless otherwise noted, both the signatures and the behavior of such a system
+need to conform to the Android 1.0 reference implementation. Both types of
+conformance will be checked by the upcoming Android Compatibility Test Suite
+(CTS).</p>
+
+<a name="vm-specific"/><h2>Dalvik VM-specific libraries</h2>
+
+<p>The VM-specific libraries enable requesting or modifying VM-specific
+information. Code that uses these classes is only portable across Dalvik-based
+systems. The VM-specific Dalvik packages include:</p>
+
+<ul class="code">
+ <li>dalvik.annotation</li>
+ <li>dalvik.bytecode</li>
+ <li>dalvik.system</li>
+</ul>
+
+<a name="interop"/><h2>Java programming language interoperability libraries</h2>
+
+<p>This category of library provides a familiar environment for programmers
+writing code in the Java programming language. Much of the implementation of
+this code comes from <a href="http://harmony.apache.org/">Apache Harmony</a>.
+Sometimes, we have to change the Harmony code to make it more suitable for the
+memory and CPU-constrained environments targeted by Dalvik. We delineate
+Dalvik-specific changes like so:
+
+<pre class="prettyprint">
+ private static final long serialVersionUID = 8683452581122892189L;
+
+// BEGIN android-added
+ /** zero-element array */
+ private static final Object[] emptyArray = new Object[0];
+// END android-added
+
+ private transient int firstIndex;
+</pre>
+
+<p>If you change existing Harmony code instead of just inserting new code, use
+<code>android-changed</code> instead of <code>android-added</code>. These
+markers help us keep track of our own changes when we pull down updates from
+Harmony.</p>
+
+<p>Packages in this category include:</p>
+
+<ul class="code">
+ <li>java.io</li>
+ <li>java.lang</li>
+ <li>java.lang.annotation</li>
+ <li>java.lang.ref</li>
+ <li>java.lang.reflect</li>
+ <li>java.math</li>
+ <li>java.net</li>
+ <li>java.nio</li>
+ <li>java.nio.channels</li>
+ <li>java.nio.channels.spi</li>
+ <li>java.nio.charset</li>
+ <li>java.nio.charset.spi</li>
+ <li>java.security</li>
+ <li>java.security.acl</li>
+ <li>java.security.cert</li>
+ <li>java.security.interfaces</li>
+ <li>java.security.spec</li>
+ <li>java.sql</li>
+ <li>java.text</li>
+ <li>java.util</li>
+ <li>java.util.concurrent</li>
+ <li>java.util.concurrent.atomic</li>
+ <li>java.util.concurrent.locks</li>
+ <li>java.util.jar</li>
+ <li>java.util.logging</li>
+ <li>java.util.prefs</li>
+ <li>java.util.regex</li>
+ <li>java.util.zip</li>
+ <li>javax.crypto</li>
+ <li>javax.crypto.interfaces</li>
+ <li>javax.crypto.spec</li>
+ <li>javax.net</li>
+ <li>javax.net.ssl</li>
+ <li>javax.security.auth</li>
+ <li>javax.security.auth.callback</li>
+ <li>javax.security.auth.login</li>
+ <li>javax.security.auth.x500</li>
+ <li>javax.security.cert</li>
+ <li>javax.sql</li>
+ <li>javax.xml</li>
+ <li>javax.xml.parsers</li>
+ <li>org.w3c.dom</li>
+ <li>org.xml.sax</li>
+ <li>org.xml.sax.ext</li>
+ <li>org.xml.sax.helpers</li>
+</ul>
+
+<p>We only provide the core functionality of <code>XMLParser</code> and
+<code>DocumentBuilder</code> in the XML packages. Some methods dealing with XML
+schema were left out because we don't provide the corresponding packages.</p>
+
+<p>In addition to the aforementioned packages, we plan to support the following
+packages some time in the future. We currently have an unfinished
+implementation of 2D drawing and image processing.</p>
+
+<ul class="code">
+ <li>java.awt</li>
+ <li>java.awt.color</li>
+ <li>java.awt.event</li>
+ <li>java.awt.font</li>
+ <li>java.awt.geom</li>
+ <li>java.awt.im</li>
+ <li>java.awt.im.spi</li>
+ <li>java.awt.image</li>
+ <li>java.awt.image.renderable</li>
+ <li>javax.imageio</li>
+ <li>javax.imageio.event</li>
+ <li>javax.imageio.metadata</li>
+ <li>javax.imageio.plugins.bmp</li>
+ <li>javax.imageio.plugins.jpeg</li>
+ <li>javax.imageio.spi</li>
+ <li>javax.imageio.stream</li>
+</ul>
+
+<p style="margin-top: 50px">Copyright &copy; 2008 The Android Open Source
+Project</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-00-nop.html b/docs/opcodes/opcode-00-nop.html
new file mode 100644
index 0000000..726f560
--- /dev/null
+++ b/docs/opcodes/opcode-00-nop.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>nop</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>nop</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Waste cycles.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>00 10x</td>
+ <td>nop</td>
+ <td>&nbsp;</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<p>
+None.
+</p>
+
+<h2>Behavior</h2>
+
+<p>
+No externally observable effects, that is, all registers and object state(s)
+stay the same. The program counter silently advances to the next instruction.
+</p>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-01-move.html b/docs/opcodes/opcode-01-move.html
new file mode 100644
index 0000000..13c1150
--- /dev/null
+++ b/docs/opcodes/opcode-01-move.html
@@ -0,0 +1,92 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>move</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>move</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Move the contents of one non-object register to another.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>01 12x</td>
+ <td>move vA, vB</td>
+ <td><code>A:</code> destination register (4 bits)<br/>
+ <code>B:</code> source register (4 bits)</td>
+</tr>
+<tr>
+ <td>02 22x</td>
+ <td>move/from16 vAA, vBBBB</td>
+ <td><code>A:</code> destination register (8 bits)<br/>
+ <code>B:</code> source register (16 bits)</td>
+</tr>
+<tr>
+ <td>03 32x</td>
+ <td>move/16 vAAAA, vBBBB</td>
+ <td><code>A:</code> destination register (16 bits)<br/>
+ <code>B:</code> source register (16 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ Both A and B must be valid register indices in the current stack frame.
+ </li>
+ <li>
+ Register vB must be defined.
+ </li>
+ <li>
+ Register vB must not contain a reference value.
+ </li>
+ <li>
+ Register vB must not be part of a register pair.
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ The value of register vB is moved to register vA, that is, vA' = vB.
+ </li>
+ <li>
+ If register v(A-1) is the first half of a register pair, register v(A-1)'
+ becomes undefined.
+ </li>
+ <li>
+ If register v(A+1) is the second half of a register pair, register v(A+1)'
+ becomes undefined.
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-04-move-wide.html b/docs/opcodes/opcode-04-move-wide.html
new file mode 100644
index 0000000..8a3bd00
--- /dev/null
+++ b/docs/opcodes/opcode-04-move-wide.html
@@ -0,0 +1,106 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>move-wide</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>move-wide</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Move the contents of one register-pair to another.
+</p>
+<p>
+Note: It is legal to move from vN to either vN-1 or vN+1, so implementations
+must arrange for both halves of a register pair to be read before anything is
+written.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>04 12x</td>
+ <td>move-wide vA, vB</td>
+ <td><code>A:</code> destination register pair (4 bits)<br/>
+ <code>B:</code> source register pair (4 bits)</td>
+</tr>
+<tr>
+ <td>05 22x</td>
+ <td>move-wide/from16 vAA, vBBBB</td>
+ <td><code>A:</code> destination register pair (8 bits)<br/>
+ <code>B:</code> source register pair (16 bits)</td>
+</tr>
+<tr>
+ <td>06 32x</td>
+ <td>move-wide/16 vAAAA, vBBBB</td>
+ <td><code>A:</code> destination register pair (16 bits)<br/>
+ <code>B:</code> source register pair (16 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ Both A+1 and B+1 must be valid register indices in the current stackframe
+ (which includes A and B being valid).
+ </li>
+ <li>
+ Register vB must be the lower half of a register pair (which excludes the
+ case of it containing a reference).
+ </li>
+ <li>
+ Both register vB and v(B+1) must be defined.
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ The value of register vB is moved to register vA, that is, vA' = vB.
+ </li>
+ <li>
+ The value of register v(B+1) is moved to register v(A+1), that is, v(A+1)'
+ = v(B+1).
+ </li>
+ <li>
+ If register v(A-1) is the lower half of a register pair, then v(A-1)'
+ becomes undefined.
+ </li>
+ <li>
+ If register v(A+2) is the upper half of a register pair, then v(A+2)'
+ becomes undefined.
+ </li>
+ <li>
+ If A = B-1, then v(B+1)' becomes undefined.
+ </li>
+ <li>
+ If A = B+1, then v(B)' becomes undefined.
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-07-move-object.html b/docs/opcodes/opcode-07-move-object.html
new file mode 100644
index 0000000..f290277
--- /dev/null
+++ b/docs/opcodes/opcode-07-move-object.html
@@ -0,0 +1,90 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>move-object</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>move-object</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Move the contents of one object-bearing register to another.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>07 12x</td>
+ <td>move-object vA, vB</td>
+ <td><code>A:</code> destination register (4 bits)<br/>
+ <code>B:</code> source register (4 bits)</td>
+</tr>
+<tr>
+ <td>08 22x</td>
+ <td>move-object/from16 vAA, vBBBB</td>
+ <td><code>A:</code> destination register (8 bits)<br/>
+ <code>B:</code> source register (16 bits)</td>
+</tr>
+<tr>
+ <td>09 32x</td>
+ <td>move-object/16 vAAAA, vBBBB</td>
+ <td><code>A:</code> destination register (16 bits)<br/>
+ <code>B:</code> source register (16 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ Both A and B must be legal register indices in the current stack frame.
+ </li>
+ <li>
+ Register vB must be defined.
+ </li>
+ <li>
+ Register vB must contain a reference value (which excludes the case of it
+ being part of a register pair).
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ The value of register vB is moved to register vA, that is, vA' = vB.
+ </li>
+ <li>
+ If register v(A-1) is the lower half of a register pair, register v(A-1)'
+ becomes undefined.
+ </li>
+ <li>
+ If register v(A+1) is the upper half of a register pair, register v(A+1)'
+ becomes undefined.
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-0a-move-result.html b/docs/opcodes/opcode-0a-move-result.html
new file mode 100644
index 0000000..616087f
--- /dev/null
+++ b/docs/opcodes/opcode-0a-move-result.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>move-result</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>move-result</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Move the single-word non-object result of the most recent invoke-kind into the
+indicated register. This must be done as the instruction immediately after an
+invoke-kind whose (single-word, non-object) result is not to be ignored;
+anywhere else is invalid.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>0a 11x</td>
+ <td>move-result vAA</td>
+ <td><code>A:</code> destination register (8 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ A must be a valid register index in the current stack frame.
+ </li>
+ <li>
+ The instruction must be immediately preceded (in the code array) by an
+ invoke-kind instruction.
+ </li>
+ <li>
+ The instruction must be immediately reached (in the actual control flow)
+ through returning from this invoke-kind instruction (it must not be jumped
+ to).
+ </li>
+ <li>
+ The result delivered by the invoke-kind instruction must not be a reference
+ value or require a register pair.
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ The result delivered by the invoke-kind instruction is moved to register
+ vA, that is, vA' = result.
+ </li>
+ <li>
+ If register v(A-1) is the lower half of a register pair, register v(A-1)'
+ becomes undefined.
+ </li>
+ <li>
+ If register v(A+1) is the upper half of a register pair, register v(A+1)'
+ becomes undefined.
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+<h2>Notes</h2>
+
+<p>
+This instruction can also be thought of as reading the contents of a special
+"result" register that is made valid and defined by executing a non-void return
+instruction or a filled-new-array instruction. The execution of any other
+instruction (including this one) renders this special register invalid.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-0b-move-result-wide.html b/docs/opcodes/opcode-0b-move-result-wide.html
new file mode 100644
index 0000000..c53517a
--- /dev/null
+++ b/docs/opcodes/opcode-0b-move-result-wide.html
@@ -0,0 +1,101 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>move-result-wide</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>move-result-wide</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Move the double-word result of the most recent invoke-kind into the indicated
+register pair. This must be done as the instruction immediately after an
+invoke-kind whose (double-word) result is not to be ignored; anywhere else is
+invalid.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>0b 11x</td>
+ <td>move-result-wide vAA</td>
+ <td><code>A:</code> destination register pair (8 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ A+1 must be a valid register index in the current stack frame (which
+ includes A itself being valid).
+ </li>
+ <li>
+ The instruction must be immediately preceded (in the code array) by an
+ invoke-kind instruction.
+ </li>
+ <li>
+ The instruction must be immediately reached (in the actual control flow)
+ through returning from this invoke-kind instruction (it must not be jumped
+ to).
+ </li>
+ <li>
+ The result delivered by the invoke-kind instruction must be either a long
+ or a double value.
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ The upper 32 bits of the result delivered by the invoke-kind instruction are
+ moved to register vA, that is, vA' = result >> 0x20.
+ </li>
+ <li>
+ The lower 32 bits of the result delivered by the invoke-kind instruction are
+ moved to register v(A+1), that is, v(A+1)' = result & 0xffffffff.
+ </li>
+ <li>
+ If register v(A-1) is the lower half of a register pair, register v(A-1)'
+ becomes undefined.
+ </li>
+ <li>
+ If register v(A+2) is the upper half of a register pair, register v(A+2)'
+ becomes undefined.
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+<h2>Notes</h2>
+
+<p>
+This instruction can also be thought of as reading the contents of a special
+"result" register that is made valid and defined by executing a non-void return
+instruction or a filled-new-array instruction. The execution of any other
+instruction (including this one) renders this special register invalid.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-0c-move-result-object.html b/docs/opcodes/opcode-0c-move-result-object.html
new file mode 100644
index 0000000..1538735
--- /dev/null
+++ b/docs/opcodes/opcode-0c-move-result-object.html
@@ -0,0 +1,97 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>move-result-object</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>move-result-object</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Move the object result of the most recent invoke-kind into the indicated
+register. This must be done as the instruction immediately after an invoke-kind
+or filled-new-array whose (object) result is not to be ignored; anywhere else
+is invalid.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>0c 11x</td>
+ <td>move-result-object vAA</td>
+ <td><code>A:</code> destination register (8 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ A must be a valid register index in the current stack frame.
+ </li>
+ <li>
+ The instruction must be immediately preceded (in the code array) by an
+ invoke-kind, filled-new-array, or filled-new-array/range instruction.
+ </li>
+ <li>
+ The instruction must be immediately reached (in the actual control flow)
+ through returning from this invoke-kind instruction or by passing a
+ filled-new-array or filled-new-array/range instruction (it must not be
+ jumped to).
+ </li>
+ <li>
+ The result delivered by the invoke-kind instruction must be a reference
+ value (which excludes the case of a long and double values).
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ The result delivered by the invoke-kind instruction is moved to register
+ vA, that, is vA' = result.
+ </li>
+ <li>
+ If register v(A-1) is the lower half of a register pair, register v(A-1)'
+ becomes undefined.
+ </li>
+ <li>
+ If register v(A+1) is the upper half of a register pair, register v(A+1)'
+ becomes undefined.
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+<h2>Notes</h2>
+
+<p>
+This instruction can also be thought of as reading the contents of a special
+"result" register that is made valid and defined by executing a non-void return
+instruction or a filled-new-array instruction. The execution of any other
+instruction (including this one) renders this special register invalid.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-0d-move-exception.html b/docs/opcodes/opcode-0d-move-exception.html
new file mode 100644
index 0000000..0f756d0
--- /dev/null
+++ b/docs/opcodes/opcode-0d-move-exception.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>move-exception</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>move-exception</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Save a just-caught exception into the given register. This should be the first
+instruction of any exception handler whose caught exception is not to be
+ignored, and this instruction may only ever occur as the first instruction of an
+exception handler; anywhere else is invalid.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>0d 11x</td>
+ <td>move-exception vAA</td>
+ <td><code>A:</code> destination register (8 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ A must be a valid register index in the current stack frame.
+ </li>
+ <li>
+ The instruction must be the first instruction (in the code array) of an
+ instruction handler, that is, its offset in the code array must match one of
+ the handlers defined for the method in the Dex file.
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ The active exception of the current thread is moved to register vA, that is,
+ vA' = exception.
+ </li>
+ <li>
+ If register v(A-1) is the lower half of a register pair, register v(A-1)'
+ becomes undefined.
+ </li>
+ <li>
+ If register v(A+1) is the upper half of a register pair, register v(A+1)'
+ becomes undefined.
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-0e-return-void.html b/docs/opcodes/opcode-0e-return-void.html
new file mode 100644
index 0000000..0498f81
--- /dev/null
+++ b/docs/opcodes/opcode-0e-return-void.html
@@ -0,0 +1,84 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>return-void</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>return-void</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Return from a void method.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>0e 10x</td>
+ <td>return-void</td>
+ <td>&nbsp;</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ The return type of the current method must be void.
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ If the method is synchronized, the object's monitor is released in a way
+ similar to the monitor-exit instruction.
+ </li>
+ <li>
+ The stack frame of the current method invocation is removed from the stack.
+ This includes all its registers becoming invalid.
+ </li>
+ <li>
+ If the stack is now empty, the current thread terminates.
+ </li>
+ <li>
+ Otherwise, the following happens:
+ <ul>
+ <li>
+ The stack frame that caused this method invocation becomes valid. This
+ includes all its registers and their old values.
+ </li>
+ <li>
+ Execution continues at the bytecode instruction immediately following
+ the invoke-kind or invoke-kind/range instruction that caused this method
+ invocation.
+ </li>
+ </ul>
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-0f-return.html b/docs/opcodes/opcode-0f-return.html
new file mode 100644
index 0000000..4de55ea
--- /dev/null
+++ b/docs/opcodes/opcode-0f-return.html
@@ -0,0 +1,100 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>return</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>return</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Return from a single-width (32-bit) non-object value-returning method.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>0f 11x</td>
+ <td>return vAA</td>
+ <td><code>A:</code> return value register (8 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ The return type of the current method must not be double, long, or a
+ reference.
+ </li>
+ <li>
+ A must be a valid register index in the current stack frame.
+ </li>
+ <li>
+ Register vA must not be part of a register pair.
+ </li>
+ <li>
+ The type of vA must match the return type of the method.
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ If the method is synchronized, the object's monitor is released in a way
+ similar to the monitor-exit instruction.
+ </li>
+ <li>
+ The stack frame of the current method invocation is removed from the stack.
+ This includes all its registers becoming invalid.
+ </li>
+ <li>
+ If the stack is now empty, the current thread terminates.
+ </li>
+ <li>
+ Otherwise, the following happens:
+ <ul>
+ <li>
+ The stack frame that caused this method invocation becomes valid. This
+ includes all its registers and their old values.
+ </li>
+ <li>
+ Execution continues at the bytecode instruction immediately following
+ the invoke-kind or invoke-kind/range instruction that caused this
+ method invocation.
+ </li>
+ <li>
+ The return value can be consumed by (exactly) the first instruction
+ following the invoke-kind or invoke-kind/range instruction that caused
+ this method invocation, and this instructions needs to be a move-result
+ instruction.
+ </li>
+ </ul>
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-10-return-wide.html b/docs/opcodes/opcode-10-return-wide.html
new file mode 100644
index 0000000..4ccfce4
--- /dev/null
+++ b/docs/opcodes/opcode-10-return-wide.html
@@ -0,0 +1,99 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>return-wide</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>return-wide</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Return from a double-width (64-bit) value-returning method.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>10 11x</td>
+ <td>return-wide vAA</td>
+ <td><code>A:</code> return value register-pair (8 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ The return type of the current method must be double or long.
+ </li>
+ <li>
+ A+1 must be a valid register index in the current stack frame (which
+ includes A being valid).
+ </li>
+ <li>
+ Register vA must be the lower half of a register pair.
+ </li>
+ <li>
+ The type of vA must match the return type of the method.
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ If the method is synchronized, the object's monitor is released in a way
+ similar to the monitor-exit instruction.
+ </li>
+ <li>
+ The stack frame of the current method invocation is removed from the stack.
+ This includes all its registers becoming invalid.
+ </li>
+ <li>
+ If the stack is now empty, the current thread terminates.
+ </li>
+ <li>
+ Otherwise, the following happens:
+ <ul>
+ <li>
+ The stack frame that caused this method invocation becomes valid. This
+ includes all its registers and their old values.
+ </li>
+ <li>
+ Execution continues at the bytecode instruction immediately following
+ the invoke instruction that caused this method invocation.
+ </li>
+ <li>
+ The return value can be consumed by (exactly) the first instruction
+ following the invoke-kind or invoke-kind/range instruction that caused
+ this method invocation, and this instructions needs to be a
+ move-result-wide instruction.
+ </li>
+ </ul>
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-11-return-object.html b/docs/opcodes/opcode-11-return-object.html
new file mode 100644
index 0000000..b4866ed
--- /dev/null
+++ b/docs/opcodes/opcode-11-return-object.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>return-object</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>return-object</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Return from an object-returning method.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>11 11x</td>
+ <td>return-object vAA</td>
+ <td><code>A:</code> return value register (8 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ The return type of the current method must be a reference.
+ </li>
+ <li>
+ A must be a valid register index in the current stack frame.
+ </li>
+ <li>
+ Register vA must be known to be reference-bearing.
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ If the method is synchronized, the object's monitor is released in a way
+ similar to the monitor-exit instruction.
+ </li>
+ <li>
+ The stack frame of the current method invocation is removed from the stack.
+ This includes all its registers becoming invalid.
+ </li>
+ <li>
+ If the stack is now empty, the current thread terminates.
+ </li>
+ <li>
+ Otherwise, the following happens:
+ </li>
+ <ul>
+ <li>
+ The stack frame that caused this method invocation becomes valid. This
+ includes all its registers and their old values.
+ </li>
+ <li>
+ Execution continues at the bytecode instruction immediately following
+ the invoke instruction that caused this method invocation.
+ </li>
+ <li>
+ The return value can be consumed by (exactly) the first instruction
+ following the invoke-kind or invoke-kind/range instruction that caused
+ this method invocation, and this instructions needs to be a
+ move-result-object instruction.
+ </li>
+ </ul>
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-12-const.html b/docs/opcodes/opcode-12-const.html
new file mode 100644
index 0000000..d2b6ef9
--- /dev/null
+++ b/docs/opcodes/opcode-12-const.html
@@ -0,0 +1,102 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>const</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>const</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Move the given literal value (sign-extended to 32 bits, if necessary) into the
+specified register.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>12 11n</td>
+ <td>const/4 vA, #+B</td>
+ <td><code>A:</code> destination register (4 bits)<br/>
+ <code>B:</code> signed int (4 bits)</td>
+</tr>
+<tr>
+ <td>13 21s</td>
+ <td>const/16 vAA, #+BBBB</td>
+ <td><code>A:</code> destination register (8 bits)<br/>
+ <code>B:</code> signed int (16 bits)</td>
+</tr>
+<tr>
+ <td>14 31i</td>
+ <td>const vAA, #+BBBBBBBB</td>
+ <td><code>A:</code> destination register (8 bits)<br/>
+ <code>B:</code> arbitrary 32-bit constant</td>
+</tr>
+<tr>
+ <td>15 21h</td>
+ <td>const/high16 vAA, #+BBBB0000</td>
+ <td><code>A:</code> destination register (8 bits)<br/>
+ <code>B:</code> signed int (16 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ A must be a valid register index in the current stackframe.
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ First, an adjusted value B' is determined as follows:
+ <ul>
+ <li>
+ If we are executing the /high16 variant, then B is left-shifted by 16
+ bits, that is, B'=B << 0x10
+ <li>
+ Otherwise, if B is a 4 bit or 16 bit constant, it is sign-extended to 32
+ bits, that is, B'=sign-extended(B).
+ </li>
+ <li>
+ Otherwise, B'=B.
+ </li>
+ </ul>
+ <li>
+ Then, the adjusted value B' is moved into the register A, that is, vA'=B'
+ </li>
+ <li>
+ If v(A-1) is the lower half of a register pair, v(A-1)' becomes undefined.
+ </li>
+ <li>
+ If v(A+1) is the upper half of a register pair, v(A+1)' becomes undefined.
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-16-const-wide.html b/docs/opcodes/opcode-16-const-wide.html
new file mode 100644
index 0000000..6197e35
--- /dev/null
+++ b/docs/opcodes/opcode-16-const-wide.html
@@ -0,0 +1,110 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>const-wide</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>const-wide</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Move the given literal value (sign-extended to 64 bits) into the specified
+register-pair.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>16 21s</td>
+ <td>const-wide/16 vAA, #+BBBB</td>
+ <td><code>A:</code> destination register (8 bits)<br/>
+ <code>B:</code> signed int (16 bits)</td>
+</tr>
+<tr>
+ <td>17 31i</td>
+ <td>const-wide/32 vAA, #+BBBBBBBB</td>
+ <td><code>A:</code> destination register (8 bits)<br/>
+ <code>B:</code> signed int (32 bits)</td>
+</tr>
+<tr>
+ <td>18 51l</td>
+ <td>const-wide vAA, #+BBBBBBBBBBBBBBBB</td>
+ <td><code>A:</code> destination register (8 bits)<br/>
+ <code>B:</code> arbitrary double-width (64-bit) constant</td>
+</tr>
+<tr>
+ <td>19 21h</td>
+ <td>const-wide/high16 vAA, #+BBBB000000000000</td>
+ <td><code>A:</code> destination register (8 bits)<br/>
+ <code>B:</code> signed int (16 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ A must be a valid register index in the current stack frame.
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ First, an adjusted value B' is determined as follows:
+ <ul>
+ <li>
+ If we are executing the /high16 variant, then B is left-shifted by 40
+ bits, that is, B'=B << 0x28
+ <li>
+ Otherwise, if B is a 16 bit or 32 bit constant, it is sign-extended to
+ 64 bits, that is, B'=sign-extended(B).
+ </li>
+ <li>
+ Otherwise, B'=B.
+ </li>
+ </ul>
+ <li>
+ The immediate value B is moved into the register pair (vA, v(A+1)), that is,
+ <ul>
+ <li>
+ vA' = B << 0x20
+ </li>
+ <li>
+ v(A+1)' = B & 0xffffffff
+ </li>
+ </ul>
+ </li>
+ <li>
+ If v(A-1) is the lower half of a register pair, v(A-1)' becomes undefined.
+ </li>
+ <li>
+ If v(A+2) is the upper half of a register pair, v(A+2)' becomes undefined.
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-1a-const-string.html b/docs/opcodes/opcode-1a-const-string.html
new file mode 100644
index 0000000..d10c115
--- /dev/null
+++ b/docs/opcodes/opcode-1a-const-string.html
@@ -0,0 +1,85 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>const-string</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>const-string</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Move a reference to the string specified by the given index into the specified
+register.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>1a 21c</td>
+ <td>const-string vAA, string@BBBB</td>
+ <td><code>A:</code> destination register (8 bits)<br/>
+ <code>B:</code> string index</td>
+</tr>
+<tr>
+ <td>1b 31c</td>
+ <td>const-string/jumbo vAA, string@BBBBBBBB</td>
+ <td><code>A:</code> destination register (8 bits)<br/>
+ <code>B:</code> string index</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ A must be a valid register index in the current stack frame.
+ </li>
+ <li>
+ B must be a valid index into the string constant pool.
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ A new java.lang.String object S is allocated on the heap and filled with the
+ contents of string pool entry B.
+ </li>
+ <li>
+ A reference to an internalized version of the new object is moved into
+ register vA, that is, the instruction behaves as if vA' = S.intern() was
+ called.
+ </li>
+ <li>
+ If v(A-1) is the lower half of a register pair, v(A-1)' becomes undefined.
+ </li>
+ <li>
+ If v(A+1) is the upper half of a register pair, v(A+1)' becomes undefined.
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-1b-const-class.html b/docs/opcodes/opcode-1b-const-class.html
new file mode 100644
index 0000000..f40b986
--- /dev/null
+++ b/docs/opcodes/opcode-1b-const-class.html
@@ -0,0 +1,92 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>const-class</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>const-class</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Move a reference to the class specified by the given index into the specified
+register. In the case where the indicated type is primitive, this will store a
+reference to the primitive type's degenerate class.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>1c 21c</td>
+ <td>const-class vAA, type@BBBB</td>
+ <td><code>A:</code> destination register (8 bits)<br/>
+ <code>B:</code> type index</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ A must be a valid register index in the current stack frame.
+ </li>
+ <li>
+ B must be a valid index into the type constant pool.
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ An attempt is made to get a reference to the class C the name of which is
+ contained in type pool entry B.
+ </li>
+ <li>
+ If B refers to a primitive type, the corresponding degenerate class is used
+ instead.
+ </li>
+ <li>
+ If C has not been loaded and resolved before, it is being loaded and
+ resolved. All exceptions that are possible during class loading can occur at
+ this point.
+ </li>
+ <li>
+ A reference to C is moved into register vA, that is, vA' = C.
+ </li>
+ <li>
+ If v(A-1) is the lower half of a register pair, v(A-1)' becomes undefined.
+ </li>
+ <li>
+ If v(A+1) is the upper half of a register pair, v(A+1)' becomes undefined.
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+ <li>
+ ClassNotFoundException is thrown if the class does not exist at all.
+ </li>
+ <li>
+ VerifyError is thrown if the class does exist, but could not be verified.
+ </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-1d-monitor-enter.html b/docs/opcodes/opcode-1d-monitor-enter.html
new file mode 100644
index 0000000..28c10f4
--- /dev/null
+++ b/docs/opcodes/opcode-1d-monitor-enter.html
@@ -0,0 +1,93 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>monitor-enter</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>monitor-enter</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Acquire the monitor for the indicated object.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>1d 11x</td>
+ <td>monitor-enter vAA</td>
+ <td><code>A:</code> reference-bearing register (8 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ A must be a valid register index for the current stack frame.
+ </li>
+ <li>
+ Register vA must contain a reference to an object.
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ An attempt is made for the current thread to acquire the monitor of the
+ indicated object. Various results are possible:
+ <ul>
+ <li>
+ If the monitor is not owned by any thread at this point, then the
+ current thread becomes owner of the monitor. The entry count of the
+ indicated object is set to 1.
+ </li>
+ <li>
+ Otherwise, if the monitor is owned by the same thread that attempts the
+ acquiration, then the entry count of the indicated object is increased
+ by 1.
+ </li>
+ <li>
+ Otherwise the monitor is owned by a different thread. The current thread
+ sleeps until the monitor of the object is released. Once that happens, a
+ new attempt to acquire the monitor is made, as described here. There is
+ no guarantee that the second attempt (or any subsequent attempt) will be
+ successful.
+ </li>
+ </ul>
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+ <li>
+ NullPointerException if vA is null.
+ </li>
+ <li>
+ IllegalMonitorStateException if the entry count exceeds an
+ (implementation-dependent) upper bound for recursive monitor entries. Note
+ that it is unlikely this bound is ever hit, since for most implementations
+ the call stack will overflow before.
+ </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-1e-monitor-exit.html b/docs/opcodes/opcode-1e-monitor-exit.html
new file mode 100644
index 0000000..cd7b165
--- /dev/null
+++ b/docs/opcodes/opcode-1e-monitor-exit.html
@@ -0,0 +1,101 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>monitor-exit</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>monitor-exit</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Release the monitor for the indicated object.
+</p>
+<p>
+Note: If this instruction needs to throw an exception, it must do so as if the
+pc has already advanced past the instruction. It may be useful to think of this
+as the instruction successfully executing (in a sense), and the exception
+getting thrown after the instruction but before the next one gets a chance to
+run. This definition makes it possible for a method to use a monitor cleanup
+catch-all (e.g., finally) block as the monitor cleanup for that block itself,
+as a way to handle the arbitrary exceptions that might get thrown due to the
+historical implementation of Thread.stop(), while still managing to have proper
+monitor hygiene.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>1e 11x</td>
+ <td>monitor-exit vAA</td>
+ <td><code>A:</code> reference-bearing register (8 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ A must be a valid register index for the current stack frame.
+ </li>
+ <li>
+ Register vA must contain a reference to an object.
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ An attempt is made for the current thread to release the monitor of the
+ indicated object.
+ </li>
+ <li>
+ If the current thread is the owner, the following happens:
+ <ul>
+ <li>
+ The monitor's entry count is decreased by one.
+ </li>
+ <li>
+ If the entry count has reached zero, the monitor is released. Other
+ threads waiting for the same monitor have a chance to acquire it.
+ </li>
+ </ul>
+ </li>
+ <li>
+ Any exception that gets thrown by this instruction bears the PC of the
+ instruction following the monitor-exit. That is, from the point of view of
+ an exception handler it cannot be distinguished from the same type of
+ exception being thrown immediately after the monitor-exit instruction.
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+ <li>
+ NullPointerException is thrown if vA is null.
+ </li>
+ <li>
+ IllegalMonitorStateException is thrown if the current thread is not the
+ owner of that monitor.
+ </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-1f-check-cast.html b/docs/opcodes/opcode-1f-check-cast.html
new file mode 100644
index 0000000..8eedd2d
--- /dev/null
+++ b/docs/opcodes/opcode-1f-check-cast.html
@@ -0,0 +1,95 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>check-cast</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>check-cast</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Throw if the reference in the given register cannot be cast to the indicated
+type. The type must be a reference type (not a primitive type).
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>1f 21c</td>
+ <td>check-cast vAA, type@BBBB</td>
+ <td><code>A:</code> reference-bearing register (8 bits)<br/>
+ <code>B:</code> type index (16 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ A must be a valid register index in the current stack frame.
+ </li>
+ <li>
+ Register vA must contain a reference value.
+ </li>
+ <li>
+ B must be a valid index into the type pool.
+ </li>
+ <li>
+ Type pool entry B must contain a valid type descriptor for a reference type.
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ An attempt is made to get a reference to the class C the name of which is
+ contained in type pool entry B.
+ </li>
+ <li>
+ If C has not been loaded and resolved before, it is being loaded and
+ resolved. All exceptions that are possible during class loading can occur at
+ this point.
+ </li>
+ <li>
+ The run-time type of the object reference vA is compared against C.
+ <ul>
+ <li>
+ If vA is null, the instruction succeeds (without further effects).
+ </li>
+ <li>
+ If vA is assignment compatible with C according to the usual rules of
+ the Java programming language, the instruction succeeds (without further
+ effects).
+ </li>
+ </ul>
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+ <li>
+ ClassCastException is thrown if vA is either not null or not assignment
+ compatible with C.
+ </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-20-instance-of.html b/docs/opcodes/opcode-20-instance-of.html
new file mode 100644
index 0000000..88076d8
--- /dev/null
+++ b/docs/opcodes/opcode-20-instance-of.html
@@ -0,0 +1,100 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>instance-of</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>instance-of</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Store in the given destination register 1 if the indicated reference is an
+instance of the given type, or 0 if not. The type must be a reference type (not
+a primitive type).
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>20 22c</td>
+ <td>instance-of vA, vB, type@CCCC</td>
+ <td><code>A:</code> destination register (4 bits)<br/>
+ <code>B:</code> reference-bearing register (4 bits)<br/>
+ <code>C:</code> type index (16 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ Both A and B must be a valid register indices for the current stack frame.
+ </li>
+ <li>
+ Register vB must contain a reference value.
+ </li>
+ <li>
+ C must be a valid index into the type constant pool.
+ </li>
+ <li>
+ Type constant pool entry C must contain a valid type descriptor for a
+ reference type.
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ An attempt is made to get a reference to the class K the name of which is
+ contained in type pool entry C.
+ </li>
+ <li>
+ If K has not been loaded and resolved before, it is being loaded and
+ resolved. All exceptions that are possible during class loading can occur at
+ this point.
+ </li>
+ <li>
+ The run-time type of the object reference vB is compared against K. The
+ register vA reflects the result:
+ <ul>
+ <li>
+ vA' = 1 if (and only if) vB is not null and vB is assignment compatible
+ with K according to the usual rules of the Java programming language.
+ </li>
+ <li>
+ vA' = 0 otherwise
+ </li>
+ </ul>
+ </li>
+ <li>
+ If v(A-1) is the lower half of a register pair, v(A-1)' becomes undefined.
+ </li>
+ <li>
+ If v(A+1) is the upper half of a register pair, v(A+1)' becomes undefined.
+ </li>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-21-array-length.html b/docs/opcodes/opcode-21-array-length.html
new file mode 100644
index 0000000..8072a7c
--- /dev/null
+++ b/docs/opcodes/opcode-21-array-length.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>array-length</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>array-length</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Store in the given destination register the length of the indicated array,
+in entries.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>21 12x</td>
+ <td>array-length vA, vB</td>
+ <td><code>A:</code> destination register (4 bits)<br/>
+ <code>B:</code> array reference-bearing register (4 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ Both A and B must be valid register indices for the current stack frame.
+ </li>
+ <li>
+ Register vB must contain a reference to an array.
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ The length of the array referenced by vB is stored in vA, that is
+ vA' = length(vB).
+ </li>
+ <li>
+ If register v(A-1) is the lower half of a register pair, register v(A-1)'
+ becomes undefined.
+ </li>
+ <li>
+ If register v(A+1) is the upper half of a register pair, register v(A+1)'
+ becomes undefined.
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+ <li>
+ NullPointerException is thrown if the value of register vB is null.
+ </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-22-new-instance.html b/docs/opcodes/opcode-22-new-instance.html
new file mode 100644
index 0000000..bdcfc3e
--- /dev/null
+++ b/docs/opcodes/opcode-22-new-instance.html
@@ -0,0 +1,95 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>new-instance</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>new-instance</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Construct a new instance of the indicated type, storing a reference to it in the
+destination. The type must refer to a non-array class.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>22 21c</td>
+ <td>new-instance vAA, type@BBBB</td>
+ <td><code>A:</code> destination register (8 bits)<br/>
+ <code>B:</code> type index</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ A must be a valid register index for the current stack frame.
+ </li>
+ <li>
+ B must be a valid index into the type pool.
+ </li>
+ <li>
+ Type constant pool entry B must contain a valid type descriptor for a
+ non-array class.
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ An attempt is made to get a reference to the class C the name of which is
+ contained in type pool entry B.
+ </li>
+ <li>
+ If C has not been loaded and resolved before, it is being loaded and
+ resolved. All exceptions that are possible during class loading can occur at
+ this point.
+ </li>
+ <li>
+ An attempt is made to create a new instance I of C. All exceptions that are
+ possible during instantiation can occur at this point.
+ </li>
+ <li>
+ A reference to the new instance is stored in register vA, that is vA' = I.
+ </li>
+ <li>
+ If v(A-1) is the lower part of a register pair, v(A-1)' becomes undefined.
+ </li>
+ <li>
+ If v(A+1) is the upper part of a register pair, v(A+1)' becomes undefined.
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+ <li>
+ All exceptions that are possible during class loading can occur.
+ </li>
+ <li>
+ All exceptions that are possible during instantiation can occur.
+ </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-23-new-array.html b/docs/opcodes/opcode-23-new-array.html
new file mode 100644
index 0000000..29327e9
--- /dev/null
+++ b/docs/opcodes/opcode-23-new-array.html
@@ -0,0 +1,108 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>new-array</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>new-array</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Construct a new array of the indicated type and size. The type must be an array
+type.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>23 22c</td>
+ <td>new-array vA, vB, type@CCCC</td>
+ <td><code>A:</code> destination register (8 bits)<br/>
+ <code>B:</code> size register<br/>
+ <code>C:</code> type index</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ A and B must be valid register indices for the current stack frame.
+ </li>
+ <li>
+ Register vB must not contain a reference value.
+ </li>
+ <li>
+ Register vB must not be part of a register pair.
+ </li>
+ <li>
+ C must be a valid index into the type pool.
+ </li>
+ <li>
+ Type constant pool entry C must contain a valid array type descriptor.
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ An attempt is made to get a reference to the class K the name of which is
+ contained in type pool entry C.
+ </li>
+ <li>
+ If K has not been loaded and resolved before, it is being loaded and
+ resolved. All exceptions that are possible during class loading can occur at
+ this point.
+ </li>
+ <li>
+ An attempt is made to create a new instance I of K and length B. All
+ exceptions that are possible during instantiation can occur at this point.
+ </li>
+ <li>
+ All elements of the new array are initialized to null (for object arrays) or
+ 0 (for numeric arrays) or false (for boolean arrays).
+ </li>
+ <li>
+ A reference to the new array is moved to register vA, that is, vA' = I.
+ </li>
+ <li>
+ If v(A-1) is the lower half of a register pair, v(A-1)' becomes undefined.
+ </li>
+ <li>
+ If v(A+1) is the upper half of a register pair, v(A+1)' becomes undefined.
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+ <li>
+ NegativeArraySizeException if vB < 0
+ </li>
+ <li>
+ All exceptions that are possible during class loading can occur.
+ </li>
+ <li>
+ All exceptions that are possible during instantiation can occur.
+ </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-24-filled-new-array.html b/docs/opcodes/opcode-24-filled-new-array.html
new file mode 100644
index 0000000..1dfa089
--- /dev/null
+++ b/docs/opcodes/opcode-24-filled-new-array.html
@@ -0,0 +1,144 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>filled-new-array</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>filled-new-array</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Construct an array of the given type and size, filling it with the supplied
+contents. The type must be an array type. The array's contents must be
+single-word (that is, no arrays of long or double). The constructed instance is
+stored as a "result" in the same way that the method invocation instructions
+store their results, so the constructed instance must be moved to a register
+with a subsequent move-result-object instruction (if it is to be used).
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>24 35c</td>
+ <td>filled-new-array {vD, vE, vF, vG, vA}, type@CCCC</td>
+ <td><code>B:</code> array size and argument word count (4 bits)<br/>
+ <code>C:</code> type index (16 bits)<br/>
+ <code>D..G, A:</code> argument registers (4 bits each)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ The value B must not be greater than 5.
+ </li>
+ <li>
+ If B > 0, then D must be a valid register index in the current stack frame.
+ </li>
+ <li>
+ If B > 1, then E must be a valid register index in the current stack frame.
+ </li>
+ <li>
+ If B > 2, then F must be a valid register index in the current stack frame.
+ </li>
+ <li>
+ If B > 3, then G must be a valid register index in the current stack frame.
+ </li>
+ <li>
+ If B > 4, then A must be a valid register index in the current stack frame.
+ </li>
+ <li>
+ C must be a valid index into the type pool.
+ </li>
+ <li>
+ The type denoted by C must be a valid array type descriptor.
+ </li>
+ <li>
+ The element size of the type denoted by C must be no larger than 32 bits.
+ </li>
+ <li>
+ If the element type is a primitive type, then all actual arguments
+ (vD .. vA, depending on B) must be primitive, too.
+ </li>
+ <li>
+ If the element type is a reference type, then all actual arguments
+ (vD .. vA, depending on B) must be references, too.
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ An attempt is made to get a reference to the type T the name of which is
+ contained in type pool entry C.
+ </li>
+ <li>
+ If T is a reference type and it has not been loaded and resolved before, it
+ is being loaded and resolved. All exceptions that are possible during class
+ loading can occur at this point.
+ </li>
+ <li>
+ An attempt is made to create a new array R of type T and length B. All
+ exceptions that are possible during instantiation can occur at this point.
+ </li>
+ <li>
+ The elements of R are filled according to the following rules:
+ <ul>
+ <li>
+ If B > 0 then R[0] = vD
+ </li>
+ <li>
+ If B > 1 then R[1] = vE
+ </li>
+ <li>
+ If B > 2 then R[2] = vF
+ </li>
+ <li>
+ If B > 3 then R[3] = vG
+ </li>
+ <li>
+ If B > 4 then R[4] = vA
+ </li>
+ </ul>
+ </li>
+ <li>
+ No reference to R is stored in any register. Instead, R can be accessed by a
+ move-result-object instruction immediately following this filled-new-array
+ instruction.
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+ <li>
+ NegativeArraySizeException if vB < 0
+ </li>
+ <li>
+ All exceptions that are possible during class loading can occur.
+ </li>
+ <li>
+ All exceptions that are possible during instantiation can occur.
+ </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-25-filled-new-array-range.html b/docs/opcodes/opcode-25-filled-new-array-range.html
new file mode 100644
index 0000000..2ee7505
--- /dev/null
+++ b/docs/opcodes/opcode-25-filled-new-array-range.html
@@ -0,0 +1,125 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>filled-new-array/range</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>filled-new-array/range</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Construct an array of the given type and size, filling it with the supplied
+contents. Clarifications and restrictions are the same as filled-new-array,
+described above.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>25 3rc</td>
+ <td>filled-new-array/range {vCCCC .. vNNNN}, type@BBBB</td>
+ <td><code>A:</code> array size and argument word count (8 bits)<br/>
+ <code>B:</code> type index (16 bits)<br/>
+ <code>C:</code> first argument register (16 bits)<br/>
+ <code>N = A + C - 1</code></td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ vN must be a valid register index in the current stack frame (this means
+ everything below vN is valid, too).
+ </li>
+ <li>
+ For all values I in the interval [C .. N] the following must hold:
+ <ul>
+ <li>
+ vI must not be part of a register pair
+ </li>
+ <li>
+ If the array type is a simple type, vI must be a simple type, too.
+ </li>
+ <li>
+ If the array type is a reference type, vI must be a reference type, too.
+ </li>
+ </ul>
+ </li>
+ <li>
+ B must be a valid index into the type pool.
+ </li>
+ <li>
+ The type denoted by B must be an array type.
+ </li>
+ <li>
+ The element size of the type denoted by B must be no larger than 32 bits.
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ An attempt is made to get a reference to the type T the name of which is
+ contained in type pool entry B.
+ </li>
+ <li>
+ If T has not been loaded and resolved before, it is being loaded and
+ resolved. All exceptions that are possible during class loading can occur at
+ this point.
+ </li>
+ <li>
+ An attempt is made to create a new instance J of type T and length vA. All
+ exceptions that are possible during instantiation can occur at this point.
+ </li>
+ <li>
+ The elements of R are filled according to the following rules:
+ <ul>
+ <li>
+ J[0] = vC
+ </li>
+ <li>
+ J[1] = v(C+1)
+ </li>
+ <li>
+ ...
+ </li>
+ <li>
+ J[vA] = vN
+ </li>
+ </ul>
+ </li>
+ <li>
+ No reference to J is stored in any register. Instead, J can be accessed by a
+ move-result-object instruction immediately following this filled-new-array
+ instruction.
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+ <li>
+ NegativeArraySizeException if vA < 0
+ </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-26-fill-array-data.html b/docs/opcodes/opcode-26-fill-array-data.html
new file mode 100644
index 0000000..77b45ae
--- /dev/null
+++ b/docs/opcodes/opcode-26-fill-array-data.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>fill-array-data</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>fill-array-data</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Fill the given array with the indicated data. The reference must be to an array
+of primitives, and the data table must match it in type and size.
+</p>
+<p>
+Note: The address of the table is guaranteed to be even (that is, 4-byte
+aligned). If the code size of the method is otherwise odd, then an extra code
+unit is inserted between the main code and the table whose value is the same as
+a nop.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+ <td>26 31t</td>
+ <td>fill-array-data vAA, +BBBBBBBB <i>(with supplemental data as specified
+ below in "<code>fill-array-data</code> Format")</i></td>
+ <td><code>A:</code> array reference (8 bits)<br/>
+ <code>B:</code> signed "branch" offset to table data (32 bits)</td>
+ </td>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ A must be a valid register index in the current stack frame.
+ </li>
+ <li>
+ vA must be a reference-bearing register (according to data flow) and contain
+ an array-reference.
+ </li>
+ <li>
+ B must be branch offset in the same method.
+ </li>
+ <li>
+ The target address (PC+B) must be 4-byte aligned.
+ </li>
+ <li>
+ The target address must hold the pseudo-opcode 0x300.
+ </li>
+ <li>
+ The table entry size must match the size of the data type of the array.
+ </li>
+ <li>
+ The table size must be equal or smaller than the array length.
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ The contents of the array referenced by vA are filled with the table data,
+ starting from array index 0 and in the given order.
+ </li>
+ <li>
+ If there are less elements in the table than the array provides space for,
+ the remaining array elements stay untouched.
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+ <li>
+ NullPointerException if vA is null.
+ </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-27-throw.html b/docs/opcodes/opcode-27-throw.html
new file mode 100644
index 0000000..1a0eb09
--- /dev/null
+++ b/docs/opcodes/opcode-27-throw.html
@@ -0,0 +1,80 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>throw</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>throw</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Throw the indicated exception.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>27 11x</td>
+ <td>throw vAA</td>
+ <td><code>A:</code> exception-bearing register (8 bits)<br/></td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ A must be a valid register index in the current stack frame.
+ </li>
+ <li>
+ Register vA must be a reference-bearing register
+ </li>
+ <li>
+ Register vA must be assignment-compatible with java.lang.Throwable according
+ to the usual rules of the Java programming language.
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ Throws the given exception vA, resulting in a search for a matching handler
+ according to the usual rules of the Java programming language.
+ </li>
+ <li>
+ If no matching handler is found for the current thread, the thread
+ terminates, possibly notifying its uncaught exception handler or thread
+ group before.
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+ <li>
+ NullPointerException if vA is null.
+ </li>
+ <li>
+ Otherwise, the indicated exception.
+ </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-28-goto.html b/docs/opcodes/opcode-28-goto.html
new file mode 100644
index 0000000..fec294c
--- /dev/null
+++ b/docs/opcodes/opcode-28-goto.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>goto</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>goto</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Unconditionally jump to the indicated instruction.
+</p>
+<p>
+Note: The branch offset may not be 0. (A spin loop may be legally constructed
+either with goto/32 or by including a nop as a target before the branch.)
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>28 10t</td>
+ <td>goto +AA</td>
+ <td><code>A:</code> signed branch offset (8 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ A must point to a valid bytecode instruction inside the current method.
+ </li>
+ <li>
+ A must not be 0.
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ The (otherwise invisible) program counter PC is set to the address of the
+ instruction plus the given offset, that is, PC' = PC(goto) + A.
+ </li>
+ <li>
+ Executions resumes at PC'.
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-29-goto-16.html b/docs/opcodes/opcode-29-goto-16.html
new file mode 100644
index 0000000..791456b
--- /dev/null
+++ b/docs/opcodes/opcode-29-goto-16.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>goto/16</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>goto/16</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Unconditionally jump to the indicated instruction.
+</p>
+<p>
+Note: The branch offset may not be 0. (A spin loop may be legally constructed
+either with goto/32 or by including a nop as a target before the branch.)
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>29 20t</td>
+ <td>goto/16 +AAAA</td>
+ <td><code>A:</code> signed branch offset (16 bits)<br/></td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ A must point to a valid bytecode instruction inside the current method.
+ </li>
+ <li>
+ A must not be 0.
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ The (otherwise invisible) program counter PC is set to the address of the
+ instruction plus the given offset, that is, PC' = PC(goto) + A.
+ </li>
+ <li>
+ Executions resumes at PC'.
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-2a-goto-32.html b/docs/opcodes/opcode-2a-goto-32.html
new file mode 100644
index 0000000..b98dd85
--- /dev/null
+++ b/docs/opcodes/opcode-2a-goto-32.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>goto/32</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>goto/32</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Unconditionally jump to the indicated instruction.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>2a 30t</td>
+ <td>goto/32 +AAAAAAAA</td>
+ <td><code>A:</code> signed branch offset (32 bits)<br/></td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ A must point to a valid bytecode instruction inside the current method.
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ The (otherwise invisible) program counter PC is set to the address of the
+ instruction plus the given offset, that is, PC' = PC(goto) + A.
+ </li>
+ <li>
+ Executions resumes at PC'.
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-2b-packed-switch.html b/docs/opcodes/opcode-2b-packed-switch.html
new file mode 100644
index 0000000..b2d5251
--- /dev/null
+++ b/docs/opcodes/opcode-2b-packed-switch.html
@@ -0,0 +1,103 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>packed-switch</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>packed-switch</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Jump to a new instruction based on the value in the given register, using a
+table of offsets corresponding to each value in a particular integral range, or
+fall through to the next instruction if there is no match.
+</p>
+<p>
+Note: The address of the table is guaranteed to be even (that is, 4-byte
+aligned). If the code size of the method is otherwise odd, then an extra code
+unit is inserted between the main code and the table whose value is the same as
+a nop.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>2b 31t</td>
+ <td>packed-switch vAA, +BBBBBBBB <i>(with supplemental data as
+ specified below in "<code>packed-switch</code> Format")</i></td>
+ <td><code>A:</code> register to test<br/>
+ <code>B:</code> signed "branch" offset to table data (32 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ A must be a valid register index in the current stack frame.
+ </li>
+ <li>
+ Let PC be the address of the packed-switch instruction in the code array of
+ the current method. Then T = PC + B with the following properties:
+ <ul>
+ <li>
+ T must be 4-byte-aligned.
+ </li
+ <li>
+ T must be in the same method.
+ </li>
+ <li>
+ T must point to a packed-switch data table.
+ </li>
+ </ul>
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ The value of vA is used as an index into the given table data.
+ </li>
+ <li>
+ If vA is in the range of the table, that is, if vA >= table.first_key and
+ vA < first_key + size, then the jump target is determined as follows:
+ <ul>
+ <li>
+ PC' = PC + table.targets[vA - table.firstKey].
+ </li>
+ <li>
+ Execution resumes at this address.
+ </li>
+ </ul>
+ </li>
+ <li>
+ Otherwise execution continues at the instruction following the packed-switch
+ statement.
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-2c-sparse-switch.html b/docs/opcodes/opcode-2c-sparse-switch.html
new file mode 100644
index 0000000..9d81eda
--- /dev/null
+++ b/docs/opcodes/opcode-2c-sparse-switch.html
@@ -0,0 +1,110 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>sparse-switch</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>sparse-switch</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Jump to a new instruction based on the value in the given register, using an
+ordered table of value-offset pairs, or fall through to the next instruction if
+there is no match.
+</p>
+<p>
+Note: The address of the table is guaranteed to be even (that is, 4-byte
+aligned). If the code size of the method is otherwise odd, then an extra code
+unit is inserted between the main code and the table whose value is the same as
+a nop.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>2c 31t</td>
+ <td>sparse-switch vAA, +BBBBBBBB <i>(with supplemental data as
+ specified below in "<code>sparse-switch</code> Format")</i></td>
+ <td><code>A:</code> register to test<br/>
+ <code>B:</code> signed "branch" offset to table data (32 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ A must be a valid register index in the current stack frame.
+ </li>
+ <li>
+ Let PC be the address of the packed-switch instruction in the code array of
+ the current method. Then T = PC + B with the following properties:
+ <ul>
+ <li>
+ T must be 4-byte-aligned.
+ </li>
+ <li>
+ T must be in the same method.
+ </li>
+ <li>
+ T must point to a sparse-switch data table.
+ </li>
+ </ul>
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ The value of vA is used as a lookup key inside the sparse table data.
+ </li>
+ <li>
+ If there exists an I with 0 <= I < table.size such that table.keys[I] = vA,
+ then the jump target is determined as follows:
+ <ul>
+ <li>
+ PC' = PC + table.targets[I].
+ </li>
+ <li>
+ Execution will resume at this address.
+ </li>
+ </ul>
+ </li>
+ <li>
+ Otherwise execution continues at the instruction following the sparse-switch
+ statement.
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+<h2>Notes</h2>
+
+<p>
+The low-to-high ordering of the keys allows the VM to employ binary search for
+the lookup, resulting in O(log table.size) comparisons.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-2d-cmp-kind.html b/docs/opcodes/opcode-2d-cmp-kind.html
new file mode 100644
index 0000000..f55a006
--- /dev/null
+++ b/docs/opcodes/opcode-2d-cmp-kind.html
@@ -0,0 +1,121 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>cmp&lt;kind&gt;</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>cmp&lt;kind&gt;</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Perform the indicated floating point or long comparison, storing 0 if the two
+arguments are equal, 1 if the second argument is larger, or -1 if the first
+argument is larger. The "bias" listed for the floating point operations
+indicates how NaN comparisons are treated: "Gt bias" instructions return 1 for
+NaN comparisons, and "lt bias" instructions return -1.
+</p>
+<p>
+For example, to check to see if floating point a < b, then it is advisable to
+use cmpg-float; a result of -1 indicates that the test was true, and the other
+values indicate it was false either due to a valid comparison or because one
+or the other values was NaN.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>2d..31 23x</td>
+ <td>cmp<i>kind</i> vAA, vBB, vCC<br/>
+ 2d: cmpl-float <i>(lt bias)</i><br/>
+ 2e: cmpg-float <i>(gt bias)</i><br/>
+ 2f: cmpl-double <i>(lt bias)</i><br/>
+ 30: cmpg-double <i>(gt bias)</i><br/>
+ 31: cmp-long
+ </td>
+ <td><code>A:</code> destination register (8 bits)<br/>
+ <code>B:</code> first source register or pair<br/>
+ <code>C:</code> second source register or pair</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ A, B and C must be valid register indices in the current stack frame.
+ </li>
+ <li>
+ For the two -float variants, both vB and vC must be of type float.
+ </li>
+ <li>
+ For the two -double variants, both vB and vC must be the lower part of a
+ register pair holding a double value.
+ </li>
+ <li>
+ For the -long variant, both both vB and vC must be the lower part of a
+ register pair holding a long value.
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ The values of registers vB and vC are compared. The result, which is stored
+ in vA, is one of the following three:
+ <ul>
+ <li>
+ If vB < vC, then vA'=-1.
+ </li>
+ <li>
+ If vB == vC, then vA'=0.
+ </li>
+ <li>
+ If vC > vC, then vA'=1.
+ </li>
+ </ul>
+ </li>
+ <li>
+ For the -float and -double variants, an addition "bias" specifies what
+ happens if one or both of the arguments are NaN:
+ <ul>
+ <li>
+ A "lt bias" results in vA'=-1.
+ </li>
+ <li>
+ A "gt bias" results in vA'=1.
+ </li>
+ </ul>
+ </li>
+ <li>
+ If v(A-1) is the lower half of a register pair, v(A-1)' becomes undefined.
+ </li>
+ <li>
+ If v(A+1) is the upper half of a register pair, v(A+1)' becomes undefined.
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-32-if-test.html b/docs/opcodes/opcode-32-if-test.html
new file mode 100644
index 0000000..ee394f6
--- /dev/null
+++ b/docs/opcodes/opcode-32-if-test.html
@@ -0,0 +1,101 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>if-test</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>if-test</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Branch to the given destination if the given two registers' values compare as
+specified.
+</p>
+<p>
+Note: The branch offset may not be 0. (A spin loop may be legally constructed
+either by branching around a backward goto or by including a nop as a target
+before the branch.)
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>32..37 22t</td>
+ <td>if-<i>test</i> vA, vB, +CCCC<br/>
+ 32: if-eq<br/>
+ 33: if-ne<br/>
+ 34: if-lt<br/>
+ 35: if-ge<br/>
+ 36: if-gt<br/>
+ 37: if-le<br/>
+ </td>
+ <td><code>A:</code> first register to test (4 bits)<br/>
+ <code>B:</code> second register to test (4 bits)<br/>
+ <code>C:</code> signed branch offset (16 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ A and B must be valid register indices for the current stack frame.
+ </li>
+ <li>
+ Registers vA and vB must not contain a reference value.
+ </li>
+ <li>
+ Registers vA and vB must not be part of a register pair.
+ </li>
+ <li>
+ Registers vA and vB must not contain a floating point value (???).
+ </li>
+ C must of a signed offset that, when added to the PC of the instruction,
+ points to a valid bytecode instruction inside the same method.
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ The values of registers vA and vB are compared according to the &lt;test&gt;
+ condition. Two results are possible:
+ <ul>
+ <li>
+ The condition holds. The value of C is used as a signed offset to the
+ address of the if-&lt;test&gt; instruction. Execution continues at the
+ resulting address.
+ </li>
+ <li>
+ The condition does not hold. Execution continues at the instruction
+ following the if-&lt;test&gt; instruction.
+ </li>
+ </ul>
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-38-if-testz.html b/docs/opcodes/opcode-38-if-testz.html
new file mode 100644
index 0000000..060bbdb
--- /dev/null
+++ b/docs/opcodes/opcode-38-if-testz.html
@@ -0,0 +1,100 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>if-&lt;test&gt;z</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>if-&lt;test&gt;z</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Branch to the given destination if the given register's value compares with 0
+as specified.
+</p>
+<p>
+ Note: The branch offset may not be 0. (A spin loop may be legally constructed
+ either by branching around a backward goto or by including a nop as a target
+ before the branch.)
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>38..3d 21t</td>
+ <td>if-<i>test</i>z vAA, +BBBB<br/>
+ 38: if-eqz<br/>
+ 39: if-nez<br/>
+ 3a: if-ltz<br/>
+ 3b: if-gez<br/>
+ 3c: if-gtz<br/>
+ 3d: if-lez<br/>
+ </td>
+ <td><code>A:</code> register to test (8 bits)<br/>
+ <code>B:</code> signed branch offset (16 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ A must be a valid register index for the current stackframe.
+ </li>
+ <li>
+ Register vA must not contain a reference value.
+ </li>
+ <li>
+ Register vA must not be part of a register pair.
+ </li>
+ <li>
+ Register vA must not contain a floating point value (???).
+ </li>
+ <li>
+ B must not be 0.
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ The value of register vA is compared to zero according to the &lt;test&gt;
+ condition. Two results are possible:
+ <ul>
+ <li>
+ The condition holds. The value of B is used as a signed offset to the
+ address of the if-&lt;test&gt;z instruction. Execution continues at the
+ resulting address.
+ </li>
+ <li>
+ The condition does not hold. Execution continues at the instruction
+ following the if-&lt;test&gt;z instruction.
+ </li>
+ </ul>
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<p>
+None.
+</p>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-44-aget.html b/docs/opcodes/opcode-44-aget.html
new file mode 100644
index 0000000..6e8836f
--- /dev/null
+++ b/docs/opcodes/opcode-44-aget.html
@@ -0,0 +1,113 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>aget&lt;kind&gt;</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>aget&lt;kind&gt;</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Perform the identified array operation at the identified index of the given
+array, storing into the value register.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>44..51 23x</td>
+ <td><i>arrayop</i> vAA, vBB, vCC<br/>
+ 44: aget<br/>
+ 45: aget-wide<br/>
+ 46: aget-object<br/>
+ 47: aget-boolean<br/>
+ 48: aget-byte<br/>
+ 49: aget-char<br/>
+ 4a: aget-short<br/>
+ </td>
+ <td><code>A:</code> dest value register or pair; (8 bits)<br/>
+ <code>B:</code> array register (8 bits)<br/>
+ <code>C:</code> index register (8 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ A, B and C must be valid register indices in the current stackframe.
+ </li>
+ <li>
+ For the aget-wide variant, also A+1 must be a valid register index in the
+ current stackframe.
+ </li>
+ <li>
+ Register vB must contain an array reference. The component type of the
+ array must match the variant of the instruction.
+ </li>
+ <li>
+ Register vC must contain an integer value.
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ For all but the -wide variant, the array element at the given index is moved
+ into register vA, that is, vA'=array[index].
+ </li>
+ <li>
+ For the -wide variant, the array element at the given index is moved into
+ registers vA and v(A+1) as follows:
+ <ul>
+ <li>
+ vA'=array[index] >> 0x20
+ </li>
+ <li>
+ v(A+1)'=array[index] & 0xffffffff;
+ </li>
+ </ul>
+ </li>
+ <li>
+ If v(A-1) is the lower half of a register pair, v(A-1)' becomes undefined.
+ </li>
+ <li>
+ For all but the -wide variant, if v(A+1) is the upper half of a register
+ pair, v(A+1)' becomes undefined.
+ </li>
+ <li>
+ For the -wide variant, if v(A+2) is the upper half of a register pair,
+ v(A+2)' becomes undefined.
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+ <li>
+ NullPointerException if vB=null.
+ </li>
+ <li>
+ ArrayIndexOutOfBoundsException if vC < 0 or vC >= array.length.
+ </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-4b-aput.html b/docs/opcodes/opcode-4b-aput.html
new file mode 100644
index 0000000..089c1ca
--- /dev/null
+++ b/docs/opcodes/opcode-4b-aput.html
@@ -0,0 +1,98 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>aput&lt;kind&gt;</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>aput&lt;kind&gt;</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Waste cycles.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>4b..51 23x</td>
+ <td><i>arrayop</i> vAA, vBB, vCC<br/>
+ 4b: aput<br/>
+ 4c: aput-wide<br/>
+ 4d: aput-object<br/>
+ 4e: aput-boolean<br/>
+ 4f: aput-byte<br/>
+ 50: aput-char<br/>
+ 51: aput-short
+ </td>
+ <td><code>A:</code> source value register or pair; (8 bits)<br/>
+ <code>B:</code> array register (8 bits)<br/>
+ <code>C:</code> index register (8 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ A, B and C must be valid register indices in the current stack frame.
+ </li>
+ <li>
+ For the aget-wide variant, also A+1 must be a valid register index in the
+ current stack frame.
+ </li>
+ <li>
+ Register vB must contain an array reference. The component type of the array
+ must match the variant of the instruction.
+ </li>
+ <li>
+ Register vC must contain an integer value.
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ For all but the -wide variant, the value of register vA is move into the
+ array element at the given index, that is, array[index]'=vA.
+ </li>
+ <li>
+ For the -wide variant, the registers vA and v(A+1) are moved into the array
+ element at the given index as follows:
+ <ul>
+ <li>
+ array[index]' = vA &lt;&lt; 0x20 | v(A+1)
+ </li>
+ </ul>
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+ <li>
+ NullPointerException if vB=null.
+ </li>
+ <li>
+ ArrayIndexOutOfBoundsException if vC &lt; 0 or vC &gt;= array.length.
+ </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-52-iget.html b/docs/opcodes/opcode-52-iget.html
new file mode 100644
index 0000000..837b511
--- /dev/null
+++ b/docs/opcodes/opcode-52-iget.html
@@ -0,0 +1,109 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>iget&lt;kind&gt;</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>iget&lt;kind&gt;</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Perform the identified object instance field operation with the identified
+field, loading or storing into the value register.
+</p>
+<p>
+Note: These opcodes are reasonable candidates for static linking, altering the
+field argument to be a more direct offset.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>52..58 22c</td>
+ <td>i<i>instanceop</i> vA, vB, field@CCCC<br/>
+ 52: iget<br/>
+ 53: iget-wide<br/>
+ 54: iget-object<br/>
+ 55: iget-boolean<br/>
+ 56: iget-byte<br/>
+ 57: iget-char<br/>
+ 58: iget-short<br/>
+ </td>
+ <td><code>A:</code> dest value register or pair; (4 bits)<br/>
+ <code>B:</code> object register (4 bits)<br/>
+ <code>C:</code> instance field reference index (16 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ A and B must be valid register indices in the current stackframe.
+ </li>
+ <li>
+ For the -wide variant, also A+1 must be a valid register index in the
+ current stackframe.
+ </li>
+ <li>
+ Register vB must contain an object reference.
+ </li>
+ <li>
+ C must be a valid index into the field reference pool.
+ </li>
+ <li>
+ The field must be an instance field. The type of the field denoted by C must
+ match the variant of the instruction.
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ The value of the given instance field is read from the given object and
+ moved into the given register vA, that is, vA'=&lt;object&gt;.&lt;field&gt;.
+ </li>
+ <li>
+ If v(A-1) is the lower half of a register pair, v(A-1)' becomes undefined.
+ </li>
+ <li>For all but the -wide variant, if v(A+1) is the upper half of a register
+ pair, v(A+1)' becomes undefined.
+ </li>
+ <li>
+ For the -wide variant, if v(A+2) is the upper half of a register pair,
+ v(A+2)' becomes undefined.
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+ <li>
+ NullPointerException if object is null.
+ </li>
+ <li>
+ IllegalAccessException if &lt;object&gt;.&lt;field&gt; is not visible from
+ the current context according to the usual visibility and access rules of
+ the Java programming language.
+ </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-59-iput.html b/docs/opcodes/opcode-59-iput.html
new file mode 100644
index 0000000..22a3479
--- /dev/null
+++ b/docs/opcodes/opcode-59-iput.html
@@ -0,0 +1,112 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>iget&lt;kind&gt;</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>iget&lt;kind&gt;</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Perform the identified object instance field operation with the identified
+field, loading or storing into the value register.
+</p>
+<p>
+Note: These opcodes are reasonable candidates for static linking, altering the
+field argument to be a more direct offset.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>59..5f 22c</td>
+ <td>i<i>instanceop</i> vA, vB, field@CCCC<br/>
+ 59: iput<br/>
+ 5a: iput-wide<br/>
+ 5b: iput-object<br/>
+ 5c: iput-boolean<br/>
+ 5d: iput-byte<br/>
+ 5e: iput-char<br/>
+ 5f: iput-short
+ </td>
+ <td><code>A:</code> source value register or pair; (4 bits)<br/>
+ <code>B:</code> object register (4 bits)<br/>
+ <code>C:</code> instance field reference index (16 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ A and B must be valid register indices in the current stack frame.
+ </li>
+ <li>
+ For the -wide variant, also A+1 must be a valid register index in the
+ current stack frame.
+ </li>
+ <li>
+ Register vB must contain an object reference.
+ </li>
+ <li>
+ C must be a valid index into the field reference pool.
+ </li>
+ <li>
+ The field must be an instance field. The type of the field denoted by C must
+ match the variant of the instruction.
+ </li>
+ <li>
+ For the -object variant, the instance referenced by register vA must be
+ assignment-compatible to the type of the field.
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ For all but the -wide variant, the value of register vA is move into the
+ field, that is, &lt;object&gt;.&lt;field&gt;'=vA.
+ </li>
+ <li>
+ For the -wide variant, the registers vA and v(A+1) are moved into the
+ field as follows:
+ <ul>
+ <li>
+ &lt;object&gt;.&lt;field&gt;' = vA &lt;&lt; 0x20 | v(A+1)
+ </li>
+ </ul>
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+ <li>
+ NullPointerException if vB=null.
+ </li>
+ <li>
+ IllegalAccessException if &lt;object&gt;.&lt;field&gt; is not visible from
+ the current context according to the usual visibility and access rules of
+ the Java programming language, or final.
+ </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-60-sget.html b/docs/opcodes/opcode-60-sget.html
new file mode 100644
index 0000000..820886e
--- /dev/null
+++ b/docs/opcodes/opcode-60-sget.html
@@ -0,0 +1,106 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>sget&lt;kind&gt;</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>sget&lt;kind&gt;</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Perform the identified object static field operation with the identified static
+field, loading or storing into the value register.
+</p>
+<p>
+Note: These opcodes are reasonable candidates for static linking, altering the
+field argument to be a more direct offset.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>60..6d 21c</td>
+ <td>s<i>staticop</i> vAA, field@BBBB<br/>
+ 60: sget<br/>
+ 61: sget-wide<br/>
+ 62: sget-object<br/>
+ 63: sget-boolean<br/>
+ 64: sget-byte<br/>
+ 65: sget-char<br/>
+ 66: sget-short<br/>
+ </td>
+ <td><code>A:</code> dest value register or pair; (8 bits)<br/>
+ <code>B:</code> static field reference index (16 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ A must be a valid register index in the current stackframe.
+ </li>
+ <li>
+ For the -wide variant, also A+1 must be a valid register index in the
+ current stackframe.
+ </li>
+ <li>
+ B must be a valid index into the field reference pool.
+ </li>
+ <li>
+ The field denoted by B must be static. The type of the field denoted by B
+ must match the variant of the instruction.
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ The value of the given instance field is read from the given object and
+ moved into the given register vA, that is, vA'=&lt;class&gt;.&lt;field&gt;.
+ </li>
+ <li>
+ If v(A-1) is the lower half of a register pair, v(A-1)' becomes undefined.
+ </li>
+ <li>
+ For all but the -wide variant, if v(A+1) is the upper half of a register
+ pair, v(A+1)' becomes undefined.
+ </li>
+ <li>
+ For the -wide variant, if v(A+2) is the upper half of a register pair,
+ v(A+2)' becomes undefined.
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+ <li>
+ NullPointerException if object is null.
+ </li>
+ <li>
+ IllegalAccessException if &lt;object&gt;.&lt;field&gt; is not visible from
+ the current context according to the usual visibility and access rules of
+ the Java programming language.
+ </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-67-sput.html b/docs/opcodes/opcode-67-sput.html
new file mode 100644
index 0000000..b4d88bb
--- /dev/null
+++ b/docs/opcodes/opcode-67-sput.html
@@ -0,0 +1,108 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>sput&lt;kind&gt;</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>sput&lt;kind&gt;</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Perform the identified object static field operation with the identified static
+field, loading or storing into the value register.
+</p>
+<p>
+Note: These opcodes are reasonable candidates for static linking, altering the
+field argument to be a more direct offset.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>67..6d 21c</td>
+ <td>s<i>staticop</i> vAA, field@BBBB<br/>
+ 67: sput<br/>
+ 68: sput-wide<br/>
+ 69: sput-object<br/>
+ 6a: sput-boolean<br/>
+ 6b: sput-byte<br/>
+ 6c: sput-char<br/>
+ 6d: sput-short
+ </td>
+ <td><code>A:</code> source value register or pair; (8 bits)<br/>
+ <code>B:</code> static field reference index (16 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ A must be a valid register index in the current stackframe.
+ </li>
+ <li>
+ For the -wide variant, also A+1 must be a valid register index in the
+ current stackframe.
+ </li>
+ <li>
+ B must be a valid index into the field reference pool.
+ </li>
+ <li>
+ The field must be static. The type of the field denoted by C must match the
+ variant of the instruction.
+ </li>
+ <li>
+ For the -object variant, the instance referenced by register vA must be
+ assignment-compatible to the type of the field.
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ For all but the -wide variant, the value of register vA is move into the
+ field, that is, &lt;class&gt;.&lt;field&gt;'=vA.
+ </li>
+ <li>
+ For the -wide variant, the registers vA and v(A+1) are moved into the field
+ as follows:
+ <ul>
+ <li>
+ &lt;class&gt;.&lt;field&gt;' = vA &lt;&lt; 0x20 | v(A+1)
+ </li>
+ </ul>
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+ <li>
+ NullPointerException if vB=null.
+ </li>
+ <li>
+ IllegalAccessException if &lt;object&gt;.&lt;field&gt; is not visible from
+ the current context according to the usual visibility and access rules of
+ the Java programming language, or final.
+ </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-7b-unop.html b/docs/opcodes/opcode-7b-unop.html
new file mode 100644
index 0000000..8b06092
--- /dev/null
+++ b/docs/opcodes/opcode-7b-unop.html
@@ -0,0 +1,108 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>unop</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>unop</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Perform the identified unary operation on the source register, storing the
+result in the destination register.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>7b..8f 12x</td>
+ <td><i>unop</i> vA, vB<br/>
+ 7b: neg-int<br/>
+ 7c: not-int<br/>
+ 7d: neg-long<br/>
+ 7e: not-long<br/>
+ 7f: neg-float<br/>
+ 80: neg-double<br/>
+ 81: int-to-long<br/>
+ 82: int-to-float<br/>
+ 83: int-to-double<br/>
+ 84: long-to-int<br/>
+ 85: long-to-float<br/>
+ 86: long-to-double<br/>
+ 87: float-to-int<br/>
+ 88: float-to-long<br/>
+ 89: float-to-double<br/>
+ 8a: double-to-int<br/>
+ 8b: double-to-long<br/>
+ 8c: double-to-float<br/>
+ 8d: int-to-byte<br/>
+ 8e: int-to-char<br/>
+ 8f: int-to-short
+ </td>
+ <td><code>A:</code> destination register or pair (4 bits)<br/>
+ <code>B:</code> source register or pair (4 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ Both A and B must be valid register indices for the current stackframe.
+ </li>
+ <li>
+ If the input type of &lt;unop&gt; is double or long, also B+1 must be a
+ valid register index in the current stackframe.
+ </li>
+ <li>
+ If the output type of &lt;unop&gt; is double or long, also A+1 must be a
+ valid register index in the current stackframe.
+ </li>
+ <li>
+ The type of register vB must match the source type of the instruction (this
+ probably needs more detail).
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ The given operation &lt;unop&gt; is performed according to the semantics
+ specified in table XXX.
+ </li>
+ <li>
+ The result is stored in register vA, that is, vA'=&lt;unop&gt; vB.
+ </li>
+ <li>
+ It gets a bit messy if we want to describe all the combinations of input and
+ output with and without pairs here. Probably it's better to split it up.
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+ <li>
+ ArithmeticException if an arithmetic error occurs during the instruction.
+ </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-90-binop.html b/docs/opcodes/opcode-90-binop.html
new file mode 100644
index 0000000..cdc08a8
--- /dev/null
+++ b/docs/opcodes/opcode-90-binop.html
@@ -0,0 +1,120 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>binop</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>binop</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Perform the identified binary operation on the two source registers, storing
+the result in the first source register.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>90..af 23x</td>
+ <td><i>binop</i> vAA, vBB, vCC<br/>
+ 90: add-int<br/>
+ 91: sub-int<br/>
+ 92: mul-int<br/>
+ 93: div-int<br/>
+ 94: rem-int<br/>
+ 95: and-int<br/>
+ 96: or-int<br/>
+ 97: xor-int<br/>
+ 98: shl-int<br/>
+ 99: shr-int<br/>
+ 9a: ushr-int<br/>
+ 9b: add-long<br/>
+ 9c: sub-long<br/>
+ 9d: mul-long<br/>
+ 9e: div-long<br/>
+ 9f: rem-long<br/>
+ a0: and-long<br/>
+ a1: or-long<br/>
+ a2: xor-long<br/>
+ a3: shl-long<br/>
+ a4: shr-long<br/>
+ a5: ushr-long<br/>
+ a6: add-float<br/>
+ a7: sub-float<br/>
+ a8: mul-float<br/>
+ a9: div-float<br/>
+ aa: rem-float<br/>
+ ab: add-double<br/>
+ ac: sub-double<br/>
+ ad: mul-double<br/>
+ ae: div-double<br/>
+ af: rem-double
+ </td>
+ <td><code>A:</code> destination register or pair (8 bits)<br/>
+ <code>B:</code> first source register or pair (8 bits)<br/>
+ <code>C:</code> second source register or pair (8 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ All A, B and C must be valid register indices in the current stackframe.
+ </li>
+ <li>
+ For the -long and -double variants, also A+1, B+1 and C+1 must be valid
+ register indices.
+ </li>
+ <li>
+ Registers vB and vC must be defined. They must both contain values that
+ match the variant of the instruction (it's probably better to split this up
+ into multiple pages again).
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ The given operation &lt;binop&gt; is performed according to the semantics
+ specified in table XXX.
+ </li>
+ <li>
+ The result is stored in register vA, that is, vA'=&lt;biop&gt; vB.
+ </li>
+ <li>
+ For the -double and -long variants, (vA+1) is also affected.
+ </li>
+ <li>
+ As usual, neighboring registers might get undefined, if vA (and vA+1) were
+ part of a register pair originally.
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+ <li>
+ ArithmeticException if an error occurs during the instruction.
+ </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-b0-binop-2addr.html b/docs/opcodes/opcode-b0-binop-2addr.html
new file mode 100644
index 0000000..b3374f4
--- /dev/null
+++ b/docs/opcodes/opcode-b0-binop-2addr.html
@@ -0,0 +1,120 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>binop/2addr</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>binop/2addr</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Perform the identified binary operation on the two source registers, storing the
+result in the first source register.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>b0..cf 12x</td>
+ <td><i>binop</i>/2addr vA, vB<br/>
+ b0: add-int/2addr<br/>
+ b1: sub-int/2addr<br/>
+ b2: mul-int/2addr<br/>
+ b3: div-int/2addr<br/>
+ b4: rem-int/2addr<br/>
+ b5: and-int/2addr<br/>
+ b6: or-int/2addr<br/>
+ b7: xor-int/2addr<br/>
+ b8: shl-int/2addr<br/>
+ b9: shr-int/2addr<br/>
+ ba: ushr-int/2addr<br/>
+ bb: add-long/2addr<br/>
+ bc: sub-long/2addr<br/>
+ bd: mul-long/2addr<br/>
+ be: div-long/2addr<br/>
+ bf: rem-long/2addr<br/>
+ c0: and-long/2addr<br/>
+ c1: or-long/2addr<br/>
+ c2: xor-long/2addr<br/>
+ c3: shl-long/2addr<br/>
+ c4: shr-long/2addr<br/>
+ c5: ushr-long/2addr<br/>
+ c6: add-float/2addr<br/>
+ c7: sub-float/2addr<br/>
+ c8: mul-float/2addr<br/>
+ c9: div-float/2addr<br/>
+ ca: rem-float/2addr<br/>
+ cb: add-double/2addr<br/>
+ cc: sub-double/2addr<br/>
+ cd: mul-double/2addr<br/>
+ ce: div-double/2addr<br/>
+ cf: rem-double/2addr
+ </td>
+ <td><code>A:</code> destination and first source register or pair
+ (4 bits)<br/>
+ <code>B:</code> second source register or pair (4 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ Both A and B must be valid register indices in the current stackframe.
+ </li>
+ <li>
+ For the -long and -double variants, also A+1 and B+1 must be valid register
+ indices.
+ </li>
+ <li>
+ Registers vA and vB must be defined. They must both contain values that
+ match the variant of the instruction (it's probably better to split this up
+ into multiple pages again).
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ The given operation &lt;binop&gt; is performed according to the semantics
+ specified in table XXX.
+ </li>
+ <li>
+ The result is stored in register vA, that is, vA'=vA &lt;binop&gt; vB.
+ </li>
+ <li>
+ For the -double and -long variants, (vA+1) is also affected.
+ </li>
+ <li>
+ As usual, neighboring registers might get undefined, if vA (and vA+1) were
+ part of a register pair originally.
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+ <li>
+ ArithmeticException if an error occurs during the instruction.
+ </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-d0-binop-lit16.html b/docs/opcodes/opcode-d0-binop-lit16.html
new file mode 100644
index 0000000..f9d3327
--- /dev/null
+++ b/docs/opcodes/opcode-d0-binop-lit16.html
@@ -0,0 +1,94 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>binop/lit16</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>binop/lit16</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Perform the indicated binary op on the indicated register (first argument) and
+literal value (second argument), storing the result in the destination register.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>d0..d7 22s</td>
+ <td><i>binop</i>/lit16 vA, vB, #+CCCC<br/>
+ d0: add-int/lit16<br/>
+ d1: rsub-int (reverse subtract)<br/>
+ d2: mul-int/lit16<br/>
+ d3: div-int/lit16<br/>
+ d4: rem-int/lit16<br/>
+ d5: and-int/lit16<br/>
+ d6: or-int/lit16<br/>
+ d7: xor-int/lit16
+ </td>
+ <td><code>A:</code> destination register (4 bits)<br/>
+ <code>B:</code> source register (4 bits)<br/>
+ <code>C:</code> signed int constant (16 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ Both A and B must be valid register indices in the current stackframe.
+ </li>
+ <li>
+ Registers vA and vB must be defined. They must both contain integer values.
+ </li>
+ <li>
+ C is an immediate, signed integer constant taken from the instruction stream
+ (actually this means there are no special requirements for C at all).
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ The given operation &lt;binop&gt; is performed according to the semantics
+ specified in table XXX.
+ </li>
+ <li>
+ Argument C is sign-extended to 32 bits before.
+ </li>
+ <li>
+ The result is stored in register vA, that is, vA'=vB &lt;binop&gt; vC.
+ </li>
+ <li>
+ As usual, neighboring registers might get undefined, if vA was part of a
+ register pair originally.
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+ <li>
+ ArithmeticException if an error occurs during the instruction.
+ </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode-d8-binop-lit8.html b/docs/opcodes/opcode-d8-binop-lit8.html
new file mode 100644
index 0000000..26005e9
--- /dev/null
+++ b/docs/opcodes/opcode-d8-binop-lit8.html
@@ -0,0 +1,97 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+
+<head>
+<title>binop/lit8</title>
+<link rel=stylesheet href="opcode.css">
+</head>
+
+<body>
+
+<h1>binop/lit8</h1>
+
+<h2>Purpose</h2>
+
+<p>
+Perform the indicated binary op on the indicated register (first argument) and
+literal value (second argument), storing the result in the destination register.
+</p>
+
+<h2>Details</h2>
+
+<table class="instruc">
+<thead>
+<tr>
+ <th>Op &amp; Format</th>
+ <th>Mnemonic / Syntax</th>
+ <th>Arguments</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>d8..e2 22b</td>
+ <td><i>binop</i>/lit8 vAA, vBB, #+CC<br/>
+ d8: add-int/lit8<br/>
+ d9: rsub-int/lit8<br/>
+ da: mul-int/lit8<br/>
+ db: div-int/lit8<br/>
+ dc: rem-int/lit8<br/>
+ dd: and-int/lit8<br/>
+ de: or-int/lit8<br/>
+ df: xor-int/lit8<br/>
+ e0: shl-int/lit8<br/>
+ e1: shr-int/lit8<br/>
+ e2: ushr-int/lit8
+ </td>
+ <td><code>A:</code> destination register (8 bits)<br/>
+ <code>B:</code> source register (8 bits)<br/>
+ <code>C:</code> signed int constant (8 bits)</td>
+</tr>
+</tbody>
+</table>
+
+<h2>Constraints</h2>
+
+<ul>
+ <li>
+ Both A and B must be valid register indices in the current stackframe.
+ </li>
+ <li>
+ Registers vA and vB must be defined. They must both contain integer values.
+ </li>
+ <li>
+ C is an immediate, signed integer constant taken from the instruction stream
+ (actually this means there are no special requirements for C at all).
+ </li>
+</ul>
+
+<h2>Behavior</h2>
+
+<ul>
+ <li>
+ The given operation &lt;binop&gt; is performed according to the semantics
+ specified in table XXX.
+ </li>
+ <li>
+ Argument C is sign-extended to 32 bits before.
+ </li>
+ <li>
+ The result is stored in register vA, that is, vA'=vB &lt;binop&gt; vC.
+ </li>
+ <li>
+ As usual, neighboring registers might get undefined, if vA was part of a
+ register pair originally.
+ </li>
+</ul>
+
+<h2>Exceptions</h2>
+
+<ul>
+ <li>
+ ArithmeticException if an error occurs during the instruction.
+ </li>
+</ul>
+
+</body>
+</html>
diff --git a/docs/opcodes/opcode.css b/docs/opcodes/opcode.css
new file mode 100644
index 0000000..c3c1304
--- /dev/null
+++ b/docs/opcodes/opcode.css
@@ -0,0 +1,166 @@
+h1 {
+ font-family: serif;
+ color: #222266;
+}
+
+h2 {
+ font-family: serif;
+ border-top-style: solid;
+ border-top-width: 2px;
+ border-color: #ccccdd;
+ padding-top: 12px;
+ margin-top: 48px;
+ margin-bottom: 2px;
+ color: #222266;
+}
+
+@media print {
+ table {
+ font-size: 8pt;
+ }
+}
+
+@media screen {
+ table {
+ font-size: 10pt;
+ }
+}
+
+
+/* general for all tables */
+
+table {
+ border-collapse: collapse;
+ margin-top: 12px;
+}
+
+table th {
+ font-family: sans-serif;
+ background: #aabbff;
+ text-align: left;
+}
+
+table td {
+ font-family: sans-serif;
+ border-top-style: solid;
+ border-bottom-style: solid;
+ border-width: 1px;
+ border-color: #aaaaff;
+ padding-top: 4px;
+ padding-bottom: 4px;
+ padding-left: 4px;
+ padding-right: 6px;
+ background: #eeeeff;
+}
+
+table td p {
+ margin-top: 4pt;
+ margin-bottom: 0pt;
+}
+
+
+
+/* opcodes table */
+
+table.instruc {
+ margin-top: 24px;
+ margin-bottom: 24px;
+ margin-left: 48px;
+ margin-right: 48px;
+}
+
+table.instruc td {
+ font-family: sans-serif;
+ border-top-style: solid;
+ border-bottom-style: solid;
+ border-width: 1px;
+ padding-top: 4px;
+ padding-bottom: 4px;
+ padding-left: 2px;
+ padding-right: 2px;
+}
+
+table.instruc td:first-child {
+ font-family: monospace;
+ font-size: 90%;
+ vertical-align: top;
+ width: 12%;
+}
+
+table.instruc td:first-child + td {
+ font-family: monospace;
+ font-size: 90%;
+ vertical-align: top;
+ width: 23%;
+}
+
+table.instruc td:first-child + td i {
+ font-family: sans-serif;
+ font-size: 90%;
+}
+
+table.instruc td:first-child + td + td {
+ vertical-align: top;
+ width: 28%;
+}
+
+table.instruc td:first-child + td + td + td {
+ vertical-align: top;
+ width: 37%;
+}
+
+
+/* supplemental opcode format table */
+
+table.supplement {
+ margin-top: 24px;
+ margin-bottom: 24px;
+ margin-left: 48px;
+ margin-right: 48px;
+}
+
+table.supplement td:first-child {
+ font-family: monospace;
+ vertical-align: top;
+ width: 20%;
+}
+
+table.supplement td:first-child + td {
+ font-family: monospace;
+ vertical-align: top;
+ width: 20%;
+}
+
+table.supplement td:first-child + td + td {
+ font-family: sans-serif;
+ vertical-align: top;
+ width: 60%;
+}
+
+
+/* math details table */
+
+table.math {
+ margin-top: 24px;
+ margin-bottom: 24px;
+ margin-left: 48px;
+ margin-right: 48px;
+}
+
+table.math td:first-child {
+ font-family: monospace;
+ vertical-align: top;
+ width: 10%;
+}
+
+table.math td:first-child + td {
+ font-family: monospace;
+ vertical-align: top;
+ width: 30%;
+}
+
+table.math td:first-child + td + td {
+ font-family: sans-serif;
+ vertical-align: top;
+ width: 60%;
+}
diff --git a/docs/porting-guide.html b/docs/porting-guide.html
new file mode 100644
index 0000000..d23b903
--- /dev/null
+++ b/docs/porting-guide.html
@@ -0,0 +1,356 @@
+<html>
+<head>
+ <title>Dalvik Porting Guide</title>
+</head>
+
+<body>
+<h1>Dalvik Porting Guide</h1>
+
+<p>
+The Dalvik virtual machine is intended to run on a variety of platforms.
+The baseline system is expected to be a variant of UNIX (Linux, BSD, Mac
+OS X) running the GNU C compiler. Little-endian CPUs have been exercised
+the most heavily, but big-endian systems are explicitly supported.
+</p><p>
+There are two general categories of work: porting to a Linux system
+with a previously unseen CPU architecture, and porting to a different
+operating system. This document covers the former.
+</p><p>
+Basic familiarity with the Android platform, source code structure, and
+build system is assumed.
+</p>
+
+
+<h2>Core Libraries</h2>
+
+<p>
+The native code in the core libraries (chiefly <code>libcore</code>,
+but also <code>dalvik/vm/native</code>) is written in C/C++ and is expected
+to work without modification in a Linux environment. Much of the code
+comes directly from the Apache Harmony project.
+</p><p>
+The core libraries pull in code from many other projects, including
+OpenSSL, zlib, and ICU. These will also need to be ported before the VM
+can be used.
+</p>
+
+
+<h2>JNI Call Bridge</h2>
+
+<p>
+Most of the Dalvik VM runtime is written in portable C. The one
+non-portable component of the runtime is the JNI call bridge. Simply put,
+this converts an array of integers into function arguments of various
+types, and calls a function. This must be done according to the C calling
+conventions for the platform. The task could be as simple as pushing all
+of the arguments onto the stack, or involve complex rules for register
+assignment and stack alignment.
+</p><p>
+To ease porting to new platforms, the <a href="http://sourceware.org/libffi/">
+open-source FFI library</a> (Foreign Function Interface) is used when a
+custom bridge is unavailable. FFI is not as fast as a native implementation,
+and the optional performance improvements it does offer are not used, so
+writing a replacement is a good first step.
+</p><p>
+The code lives in <code>dalvik/vm/arch/*</code>, with the FFI-based version
+in the "generic" directory. There are two source files for each architecture.
+One defines the call bridge itself:
+</p><p><blockquote>
+<code>void dvmPlatformInvoke(void* pEnv, ClassObject* clazz, int argInfo,
+int argc, const u4* argv, const char* signature, void* func,
+JValue* pReturn)</code>
+</blockquote></p><p>
+This will invoke a C/C++ function declared:
+</p><p><blockquote>
+ <code>return_type func(JNIEnv* pEnv, Object* this [, <i>args</i>])<br></code>
+</blockquote>or (for a "static" method):<blockquote>
+ <code>return_type func(JNIEnv* pEnv, ClassObject* clazz [, <i>args</i>])</code>
+</blockquote></p><p>
+The role of <code>dvmPlatformInvoke</code> is to convert the values in
+<code>argv</code> into C-style calling conventions, call the method, and
+then place the return type into <code>pReturn</code> (a union that holds
+all of the basic JNI types). The code may use the method signature
+(a DEX "shorty" signature, with one character for the return type and one
+per argument) to determine how to handle the values.
+</p><p>
+The other source file involved here defines a 32-bit "hint". The hint
+is computed when the method's class is loaded, and passed in as the
+"argInfo" argument. The hint can be used to avoid scanning the ASCII
+method signature for things like the return value, total argument size,
+or inter-argument 64-bit alignment restrictions.
+
+
+<h2>Interpreter</h2>
+
+<p>
+The Dalvik runtime includes two interpreters, labeled "portable" and "fast".
+The portable interpreter is largely contained within a single C function,
+and should compile on any system that supports gcc. (If you don't have gcc,
+you may need to disable the "threaded" execution model, which relies on
+gcc's "goto table" implementation; look for the THREADED_INTERP define.)
+</p><p>
+The fast interpreter uses hand-coded assembly fragments. If none are
+available for the current architecture, the build system will create an
+interpreter out of C "stubs". The resulting "all stubs" interpreter is
+quite a bit slower than the portable interpreter, making "fast" something
+of a misnomer.
+</p><p>
+The fast interpreter is enabled by default. On platforms without native
+support, you may want to switch to the portable interpreter. This can
+be controlled with the <code>dalvik.vm.execution-mode</code> system
+property. For example, if you:
+</p><p><blockquote>
+<code>adb shell "echo dalvik.vm.execution-mode = int:portable >> /data/local.prop"</code>
+</blockquote></p><p>
+and reboot, the Android app framework will start the VM with the portable
+interpreter enabled.
+</p>
+
+
+<h3>Mterp Interpreter Structure</h3>
+
+<p>
+There may be significant performance advantages to rewriting the
+interpreter core in assembly language, using architecture-specific
+optimizations. In Dalvik this can be done one instruction at a time.
+</p><p>
+The simplest way to implement an interpreter is to have a large "switch"
+statement. After each instruction is handled, the interpreter returns to
+the top of the loop, fetches the next instruction, and jumps to the
+appropriate label.
+</p><p>
+An improvement on this is called "threaded" execution. The instruction
+fetch and dispatch are included at the end of every instruction handler.
+This makes the interpreter a little larger overall, but you get to avoid
+the (potentially expensive) branch back to the top of the switch statement.
+</p><p>
+Dalvik mterp goes one step further, using a computed goto instead of a goto
+table. Instead of looking up the address in a table, which requires an
+extra memory fetch on every instruction, mterp multiplies the opcode number
+by a fixed value. By default, each handler is allowed 64 bytes of space.
+</p><p>
+Not all handlers fit in 64 bytes. Those that don't can have subroutines
+or simply continue on to additional code outside the basic space. Some of
+this is handled automatically by Dalvik, but there's no portable way to detect
+overflow of a 64-byte handler until the VM starts executing.
+</p><p>
+The choice of 64 bytes is somewhat arbitrary, but has worked out well for
+ARM and x86.
+</p><p>
+In the course of development it's useful to have C and assembly
+implementations of each handler, and be able to flip back and forth
+between them when hunting problems down. In mterp this is relatively
+straightforward. You can always see the files being fed to the compiler
+and assembler for your platform by looking in the
+<code>dalvik/vm/mterp/out</code> directory.
+</p><p>
+The interpreter sources live in <code>dalvik/vm/mterp</code>. If you
+haven't yet, you should read <code>dalvik/vm/mterp/README.txt</code> now.
+</p>
+
+
+<h3>Getting Started With Mterp</h3>
+
+</p><p>
+Getting started:
+<ol>
+<li>Decide on the name of your architecture. For the sake of discussion,
+let's call it <code>myarch</code>.
+<li>Make a copy of <code>dalvik/vm/mterp/config-allstubs</code> to
+<code>dalvik/vm/mterp/config-myarch</code>.
+<li>Create a <code>dalvik/vm/mterp/myarch</code> directory to hold your
+source files.
+<li>Add <code>myarch</code> to the list in
+<code>dalvik/vm/mterp/rebuild.sh</code>.
+<li>Make sure <code>dalvik/vm/Android.mk</code> will find the files for
+your architecture. If <code>$(TARGET_ARCH)</code> is configured this
+will happen automatically.
+</ol>
+</p><p>
+You now have the basic framework in place. Whenever you make a change, you
+need to perform two steps: regenerate the mterp output, and build the
+core VM library. (It's two steps because we didn't want the build system
+to require Python 2.5. Which, incidentally, you need to have.)
+<ol>
+<li>In the <code>dalvik/vm/mterp</code> directory, regenerate the contents
+of the files in <code>dalvik/vm/mterp/out</code> by executing
+<code>./rebuild.sh</code>. Note there are two files, one in C and one
+in assembly.
+<li>In the <code>dalvik</code> directory, regenerate the
+<code>libdvm.so</code> library with <code>mm</code>. You can also use
+<code>make libdvm</code> from the top of the tree.
+</ol>
+</p><p>
+This will leave you with an updated libdvm.so, which can be pushed out to
+a device with <code>adb sync</code> or <code>adb push</code>. If you're
+using the emulator, you need to add <code>make snod</code> (System image,
+NO Dependency check) to rebuild the system image file. You should not
+need to do a top-level "make" and rebuild the dependent binaries.
+</p><p>
+At this point you have an "all stubs" interpreter. You can see how it
+works by examining <code>dalvik/vm/mterp/cstubs/entry.c</code>. The
+code runs in a loop, pulling out the next opcode, and invoking the
+handler through a function pointer. Each handler takes a "glue" argument
+that contains all of the useful state.
+</p><p>
+Your goal is to replace the entry method, exit method, and each individual
+instruction with custom implementations. The first thing you need to do
+is create an entry function that calls the handler for the first instruction.
+After that, the instructions chain together, so you don't need a loop.
+(Look at the ARM or x86 implementation to see how they work.)
+</p><p>
+Once you have that, you need something to jump to. You can't branch
+directly to the C stub because it's expecting to be called with a "glue"
+argument and then return. We need a C stub "wrapper" that does the
+setup and jumps directly to the next handler. We write this in assembly
+and then add it to the config file definition.
+</p><p>
+To see how this works, create a file called
+<code>dalvik/vm/mterp/myarch/stub.S</code> that contains one line:
+<pre>
+/* stub for ${opcode} */
+</pre>
+Then, in <code>dalvik/vm/mterp/config-myarch</code>, add this below the
+<code>handler-size</code> directive:
+<pre>
+# source for the instruction table stub
+asm-stub myarch/stub.S
+</pre>
+</p><p>
+Regenerate the sources with <code>./rebuild.sh</code>, and take a look
+inside <code>dalvik/vm/mterp/out/InterpAsm-myarch.S</code>. You should
+see 256 copies of the stub function in a single large block after the
+<code>dvmAsmInstructionStart</code> label. The <code>stub.S</code>
+code will be used anywhere you don't provide an assembly implementation.
+</p><p>
+Note that each block begins with a <code>.balign 64</code> directive.
+This is what pads each handler out to 64 bytes. Note also that the
+<code>${opcode}</code> text changed into an opcode name, which should
+be used to call the C implementation (<code>dvmMterp_${opcode}</code>).
+</p><p>
+The actual contents of <code>stub.S</code> are up to you to define.
+See <code>entry.S</code> and <code>stub.S</code> in the <code>armv5te</code>
+or <code>x86</code> directories for working examples.
+</p><p>
+If you're working on a variation of an existing architecture, you may be
+able to use most of the existing code and just provide replacements for
+a few instructions. Look at the <code>armv4t</code> implementation as
+an example.
+</p>
+
+
+<h3>Replacing Stubs</h3>
+
+<p>
+There are roughly 230 Dalvik opcodes, including some that are inserted by
+<a href="dexopt.html">dexopt</a> and aren't described in the
+<a href="dalvik-bytecode.html">Dalvik bytecode</a> documentation. Each
+one must perform the appropriate actions, fetch the next opcode, and
+branch to the next handler. The actions performed by the assembly version
+must exactly match those performed by the C version (in
+<code>dalvik/vm/mterp/c/OP_*</code>).
+</p><p>
+It is possible to customize the set of "optimized" instructions for your
+platform. This is possible because optimized DEX files are not expected
+to work on multiple devices. Adding, removing, or redefining instructions
+is beyond the scope of this document, and for simplicity it's best to stick
+with the basic set defined by the portable interpreter.
+</p><p>
+Once you have written a handler that looks like it should work, add
+it to the config file. For example, suppose we have a working version
+of <code>OP_NOP</code>. For demonstration purposes, fake it for now by
+putting this into <code>dalvik/vm/mterp/myarch/OP_NOP.S</code>:
+<pre>
+/* This is my NOP handler */
+</pre>
+</p><p>
+Then, in the <code>op-start</code> section of <code>config-myarch</code>, add:
+<pre>
+ op OP_NOP myarch
+</pre>
+</p><p>
+This tells the generation script to use the assembly version from the
+<code>myarch</code> directory instead of the C version from the <code>c</code>
+directory.
+</p><p>
+Execute <code>./rebuild.sh</code>. Look at <code>InterpAsm-myarch.S</code>
+and <code>InterpC-myarch.c</code> in the <code>out</code> directory. You
+will see that the <code>OP_NOP</code> stub wrapper has been replaced with our
+new code in the assembly file, and the C stub implementation is no longer
+included.
+</p><p>
+As you implement instructions, the C version and corresponding stub wrapper
+will disappear from the output files. Eventually you will have a 100%
+assembly interpreter. You may find it saves a little time to examine
+the output of your compiler for some of the operations. The
+<a href="porting-proto.c.txt">porting-proto.c</a> sample code can be
+helpful here.
+</p>
+
+
+<h3>Interpreter Switching</h3>
+
+<p>
+The Dalvik VM actually includes a third interpreter implementation: the debug
+interpreter. This is a variation of the portable interpreter that includes
+support for debugging and profiling.
+</p><p>
+When a debugger attaches, or a profiling feature is enabled, the VM
+will switch interpreters at a convenient point. This is done at the
+same time as the GC safe point check: on a backward branch, a method
+return, or an exception throw. Similarly, when the debugger detaches
+or profiling is discontinued, execution transfers back to the "fast" or
+"portable" interpreter.
+</p><p>
+Your entry function needs to test the "entryPoint" value in the "glue"
+pointer to determine where execution should begin. Your exit function
+will need to return a boolean that indicates whether the interpreter is
+exiting (because we reached the "bottom" of a thread stack) or wants to
+switch to the other implementation.
+</p><p>
+See the <code>entry.S</code> file in <code>x86</code> or <code>armv5te</code>
+for examples.
+</p>
+
+
+<h3>Testing</h3>
+
+<p>
+A number of VM tests can be found in <code>dalvik/tests</code>. The most
+useful during interpreter development is <code>003-omnibus-opcodes</code>,
+which tests many different instructions.
+</p><p>
+The basic invocation is:
+<pre>
+$ cd dalvik/tests
+$ ./run-test 003
+</pre>
+</p><p>
+This will run test 003 on an attached device or emulator. You can run
+the test against your desktop VM by specifying <code>--reference</code>
+if you suspect the test may be faulty. You can also use
+<code>--portable</code> and <code>--fast</code> to explictly specify
+one Dalvik interpreter or the other.
+</p><p>
+Some instructions are replaced by <code>dexopt</code>, notably when
+"quickening" field accesses and method invocations. To ensure
+that you are testing the basic form of the instruction, add the
+<code>--no-optimize</code> option.
+</p><p>
+There is no in-built instruction tracing mechanism. If you want
+to know for sure that your implementation of an opcode handler
+is being used, the easiest approach is to insert a "printf"
+call. For an example, look at <code>common_squeak</code> in
+<code>dalvik/vm/mterp/armv5te/footer.S</code>.
+</p><p>
+At some point you need to ensure that debuggers and profiling work with
+your interpreter. The easiest way to do this is to simply connect a
+debugger or toggle profiling. (A future test suite may include some
+tests for this.)
+</p>
+
+<p>
+<address>Copyright &copy; 2009 The Android Open Source Project</address>
+
+</body>
+</html>
diff --git a/docs/porting-proto.c.txt b/docs/porting-proto.c.txt
new file mode 100644
index 0000000..98c6fd3
--- /dev/null
+++ b/docs/porting-proto.c.txt
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik instruction fragments, useful when porting mterp.
+ *
+ * Compile this and examine the output to see what your compiler generates.
+ * This can give you a head start on some of the more complicated operations.
+ *
+ * Example:
+ * % gcc -c -O2 -save-temps -fverbose-asm porting-proto.c
+ * % less porting-proto.s
+ */
+#include <stdint.h>
+
+typedef int8_t s1;
+typedef uint8_t u1;
+typedef int16_t s2;
+typedef uint16_t u2;
+typedef int32_t s4;
+typedef uint32_t u4;
+typedef int64_t s8;
+typedef uint64_t u8;
+
+s4 iadd32(s4 x, s4 y) { return x + y; }
+s8 iadd64(s8 x, s8 y) { return x + y; }
+float fadd32(float x, float y) { return x + y; }
+double fadd64(double x, double y) { return x + y; }
+
+s4 isub32(s4 x, s4 y) { return x - y; }
+s8 isub64(s8 x, s8 y) { return x - y; }
+float fsub32(float x, float y) { return x - y; }
+double fsub64(double x, double y) { return x - y; }
+
+s4 irsub32lit8(s4 x) { return 25 - x; }
+
+s4 imul32(s4 x, s4 y) { return x * y; }
+s8 imul64(s8 x, s8 y) { return x * y; }
+float fmul32(float x, float y) { return x * y; }
+double fmul64(double x, double y) { return x * y; }
+
+s4 idiv32(s4 x, s4 y) { return x / y; }
+s8 idiv64(s8 x, s8 y) { return x / y; }
+float fdiv32(float x, float y) { return x / y; }
+double fdiv64(double x, double y) { return x / y; }
+
+s4 irem32(s4 x, s4 y) { return x % y; }
+s8 irem64(s8 x, s8 y) { return x % y; }
+
+s4 iand32(s4 x, s4 y) { return x & y; }
+s8 iand64(s8 x, s8 y) { return x & y; }
+
+s4 ior32(s4 x, s4 y) { return x | y; }
+s8 ior64(s8 x, s8 y) { return x | y; }
+
+s4 ixor32(s4 x, s4 y) { return x ^ y; }
+s8 ixor64(s8 x, s8 y) { return x ^ y; }
+
+s4 iasl32(s4 x, s4 count) { return x << (count & 0x1f); }
+s8 iasl64(s8 x, s4 count) { return x << (count & 0x3f); }
+
+s4 iasr32(s4 x, s4 count) { return x >> (count & 0x1f); }
+s8 iasr64(s8 x, s4 count) { return x >> (count & 0x3f); }
+
+s4 ilsr32(s4 x, s4 count) { return ((u4)x) >> (count & 0x1f); } // unsigned
+s8 ilsr64(s8 x, s4 count) { return ((u8)x) >> (count & 0x3f); } // unsigned
+
+s4 ineg32(s4 x) { return -x; }
+s8 ineg64(s8 x) { return -x; }
+float fneg32(float x) { return -x; }
+double fneg64(double x) { return -x; }
+
+s4 inot32(s4 x) { return x ^ -1; }
+s8 inot64(s8 x) { return x ^ -1LL; }
+
+s4 float2int(float x) { return (s4) x; }
+double float2double(float x) { return (double) x; }
+s4 double2int(double x) { return (s4) x; }
+float double2float(double x) { return (float) x; }
+
+/*
+ * ARM lib doesn't clamp large values or NaN the way we want on these two.
+ * If the simple version isn't correct, use the long version. (You can use
+ * dalvik/tests/041-narrowing to verify.)
+ */
+s8 float2long(float x) { return (s8) x; }
+s8 float2long_clamp(float x)
+{
+ static const float kMaxLong = (float)0x7fffffffffffffffULL;
+ static const float kMinLong = (float)0x8000000000000000ULL;
+
+ if (x >= kMaxLong) {
+ return 0x7fffffffffffffffULL;
+ } else if (x <= kMinLong) {
+ return 0x8000000000000000ULL;
+ } else if (x != x) {
+ return 0;
+ } else {
+ return (s8) x;
+ }
+}
+s8 double2long(double x) { return (s8) x; }
+s8 double2long_clamp(double x)
+{
+ static const double kMaxLong = (double)0x7fffffffffffffffULL;
+ static const double kMinLong = (double)0x8000000000000000ULL;
+
+ if (x >= kMaxLong) {
+ return 0x7fffffffffffffffULL;
+ } else if (x <= kMinLong) {
+ return 0x8000000000000000ULL;
+ } else if (x != x) {
+ return 0;
+ } else {
+ return (s8) x;
+ }
+}
+
+s1 int2byte(s4 x) { return (s1) x; }
+s2 int2short(s4 x) { return (s2) x; }
+u2 int2char(s4 x) { return (u2) x; }
+s8 int2long(s4 x) { return (s8) x; }
+float int2float(s4 x) { return (float) x; }
+double int2double(s4 x) { return (double) x; }
+
+s4 long2int(s8 x) { return (s4) x; }
+float long2float(s8 x) { return (float) x; }
+double long2double(s8 x) { return (double) x; }
+
+int cmpl_float(float x, float y)
+{
+ int result;
+
+ if (x == y)
+ result = 0;
+ else if (x > y)
+ result = 1;
+ else /* (x < y) or NaN */
+ result = -1;
+ return result;
+}
+
+int cmpg_float(float x, float y)
+{
+ int result;
+
+ if (x == y)
+ result = 0;
+ else if (x < y)
+ result = -1;
+ else /* (x > y) or NaN */
+ result = 1;
+ return result;
+}
+
+int cmpl_double(double x, double y)
+{
+ int result;
+
+ if (x == y)
+ result = 0;
+ else if (x > y)
+ result = 1;
+ else /* (x < y) or NaN */
+ result = -1;
+ return result;
+}
+
+int cmpg_double(double x, double y)
+{
+ int result;
+
+ if (x == y)
+ result = 0;
+ else if (x < y)
+ result = -1;
+ else /* (x > y) or NaN */
+ result = 1;
+ return result;
+}
+
+int cmp_long(s8 x, s8 y)
+{
+ int result;
+
+ if (x == y)
+ result = 0;
+ else if (x < y)
+ result = -1;
+ else /* (x > y) */
+ result = 1;
+ return result;
+}
+
+/* instruction decoding fragments */
+u1 unsignedAA(u2 x) { return x >> 8; }
+s1 signedAA(u2 x) { return (s4)(x << 16) >> 24; }
+s2 signedBB(u2 x) { return (s2) x; }
+u1 unsignedA(u2 x) { return (x >> 8) & 0x0f; }
+u1 unsignedB(u2 x) { return x >> 12; }
+
+/* some handy immediate constants when working with float/double */
+u4 const_43e00000(u4 highword) { return 0x43e00000; }
+u4 const_c3e00000(u4 highword) { return 0xc3e00000; }
+u4 const_ffc00000(u4 highword) { return 0xffc00000; }
+u4 const_41dfffff(u4 highword) { return 0x41dfffff; }
+u4 const_c1e00000(u4 highword) { return 0xc1e00000; }
+
+/*
+ * Test for some gcc-defined symbols. If you're frequently switching
+ * between different cross-compiler architectures or CPU feature sets,
+ * this can help you keep track of which one you're compiling for.
+ */
+#ifdef __arm__
+# warning "found __arm__"
+#endif
+#ifdef __ARM_EABI__
+# warning "found __ARM_EABI__"
+#endif
+#ifdef __VFP_FP__
+# warning "found __VFP_FP__" /* VFP-format doubles used; may not have VFP */
+#endif
+#if defined(__VFP_FP__) && !defined(__SOFTFP__)
+# warning "VFP in use"
+#endif
+#ifdef __ARM_ARCH_5TE__
+# warning "found __ARM_ARCH_5TE__"
+#endif
+#ifdef __ARM_ARCH_7A__
+# warning "found __ARM_ARCH_7A__"
+#endif
diff --git a/docs/prettify.css b/docs/prettify.css
new file mode 100644
index 0000000..351152b
--- /dev/null
+++ b/docs/prettify.css
@@ -0,0 +1,27 @@
+/* Pretty printing styles. Used with prettify.js. */
+
+.str { color: #080; }
+.kwd { color: #008; }
+.com { color: #800; }
+.typ { color: #606; }
+.lit { color: #066; }
+.pun { color: #660; }
+.pln { color: #000; }
+.tag { color: #008; }
+.atn { color: #606; }
+.atv { color: #080; }
+.dec { color: #606; }
+pre.prettyprint { padding: 2px; border: 1px solid #888; }
+
+@media print {
+ .str { color: #060; }
+ .kwd { color: #006; font-weight: bold; }
+ .com { color: #600; font-style: italic; }
+ .typ { color: #404; font-weight: bold; }
+ .lit { color: #044; }
+ .pun { color: #440; }
+ .pln { color: #000; }
+ .tag { color: #006; font-weight: bold; }
+ .atn { color: #404; }
+ .atv { color: #060; }
+}
diff --git a/docs/prettify.js b/docs/prettify.js
new file mode 100644
index 0000000..9e99fc6
--- /dev/null
+++ b/docs/prettify.js
@@ -0,0 +1,1280 @@
+// Copyright (C) 2006 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+/**
+ * @fileoverview
+ * some functions for browser-side pretty printing of code contained in html.
+ *
+ * The lexer should work on a number of languages including C and friends,
+ * Java, Python, Bash, SQL, HTML, XML, CSS, Javascript, and Makefiles.
+ * It works passably on Ruby, PHP and Awk and a decent subset of Perl, but,
+ * because of commenting conventions, doesn't work on Smalltalk, Lisp-like, or
+ * CAML-like languages.
+ *
+ * If there's a language not mentioned here, then I don't know it, and don't
+ * know whether it works. If it has a C-like, Bash-like, or XML-like syntax
+ * then it should work passably.
+ *
+ * Usage:
+ * 1) include this source file in an html page via
+ * <script type="text/javascript" src="/path/to/prettify.js"></script>
+ * 2) define style rules. See the example page for examples.
+ * 3) mark the <pre> and <code> tags in your source with class=prettyprint.
+ * You can also use the (html deprecated) <xmp> tag, but the pretty printer
+ * needs to do more substantial DOM manipulations to support that, so some
+ * css styles may not be preserved.
+ * That's it. I wanted to keep the API as simple as possible, so there's no
+ * need to specify which language the code is in.
+ *
+ * Change log:
+ * cbeust, 2006/08/22
+ * Java annotations (start with "@") are now captured as literals ("lit")
+ */
+
+// JSLint declarations
+/*global console, document, navigator, setTimeout, window */
+
+/**
+ * Split {@code prettyPrint} into multiple timeouts so as not to interfere with
+ * UI events.
+ * If set to {@code false}, {@code prettyPrint()} is synchronous.
+ */
+var PR_SHOULD_USE_CONTINUATION = true;
+
+/** the number of characters between tab columns */
+var PR_TAB_WIDTH = 8;
+
+/** Walks the DOM returning a properly escaped version of innerHTML.
+ * @param {Node} node
+ * @param {Array.<string>} out output buffer that receives chunks of HTML.
+ */
+var PR_normalizedHtml;
+
+/** Contains functions for creating and registering new language handlers.
+ * @type {Object}
+ */
+var PR;
+
+/** Pretty print a chunk of code.
+ *
+ * @param {string} sourceCodeHtml code as html
+ * @return {string} code as html, but prettier
+ */
+var prettyPrintOne;
+/** find all the < pre > and < code > tags in the DOM with class=prettyprint
+ * and prettify them.
+ * @param {Function} opt_whenDone if specified, called when the last entry
+ * has been finished.
+ */
+var prettyPrint;
+
+/** browser detection. @extern */
+function _pr_isIE6() {
+ var isIE6 = navigator && navigator.userAgent &&
+ /\bMSIE 6\./.test(navigator.userAgent);
+ _pr_isIE6 = function () { return isIE6; };
+ return isIE6;
+}
+
+
+(function () {
+ /** Splits input on space and returns an Object mapping each non-empty part to
+ * true.
+ */
+ function wordSet(words) {
+ words = words.split(/ /g);
+ var set = {};
+ for (var i = words.length; --i >= 0;) {
+ var w = words[i];
+ if (w) { set[w] = null; }
+ }
+ return set;
+ }
+
+ // Keyword lists for various languages.
+ var FLOW_CONTROL_KEYWORDS =
+ "break continue do else for if return while ";
+ var C_KEYWORDS = FLOW_CONTROL_KEYWORDS + "auto case char const default " +
+ "double enum extern float goto int long register short signed sizeof " +
+ "static struct switch typedef union unsigned void volatile ";
+ var COMMON_KEYWORDS = C_KEYWORDS + "catch class delete false import " +
+ "new operator private protected public this throw true try ";
+ var CPP_KEYWORDS = COMMON_KEYWORDS + "alignof align_union asm axiom bool " +
+ "concept concept_map const_cast constexpr decltype " +
+ "dynamic_cast explicit export friend inline late_check " +
+ "mutable namespace nullptr reinterpret_cast static_assert static_cast " +
+ "template typeid typename typeof using virtual wchar_t where ";
+ var JAVA_KEYWORDS = COMMON_KEYWORDS +
+ "boolean byte extends final finally implements import instanceof null " +
+ "native package strictfp super synchronized throws transient ";
+ var CSHARP_KEYWORDS = JAVA_KEYWORDS +
+ "as base by checked decimal delegate descending event " +
+ "fixed foreach from group implicit in interface internal into is lock " +
+ "object out override orderby params readonly ref sbyte sealed " +
+ "stackalloc string select uint ulong unchecked unsafe ushort var ";
+ var JSCRIPT_KEYWORDS = COMMON_KEYWORDS +
+ "debugger eval export function get null set undefined var with " +
+ "Infinity NaN ";
+ var PERL_KEYWORDS = "caller delete die do dump elsif eval exit foreach for " +
+ "goto if import last local my next no our print package redo require " +
+ "sub undef unless until use wantarray while BEGIN END ";
+ var PYTHON_KEYWORDS = FLOW_CONTROL_KEYWORDS + "and as assert class def del " +
+ "elif except exec finally from global import in is lambda " +
+ "nonlocal not or pass print raise try with yield " +
+ "False True None ";
+ var RUBY_KEYWORDS = FLOW_CONTROL_KEYWORDS + "alias and begin case class def" +
+ " defined elsif end ensure false in module next nil not or redo rescue " +
+ "retry self super then true undef unless until when yield BEGIN END ";
+ var SH_KEYWORDS = FLOW_CONTROL_KEYWORDS + "case done elif esac eval fi " +
+ "function in local set then until ";
+ var ALL_KEYWORDS = (
+ CPP_KEYWORDS + CSHARP_KEYWORDS + JSCRIPT_KEYWORDS + PERL_KEYWORDS +
+ PYTHON_KEYWORDS + RUBY_KEYWORDS + SH_KEYWORDS);
+
+ // token style names. correspond to css classes
+ /** token style for a string literal */
+ var PR_STRING = 'str';
+ /** token style for a keyword */
+ var PR_KEYWORD = 'kwd';
+ /** token style for a comment */
+ var PR_COMMENT = 'com';
+ /** token style for a type */
+ var PR_TYPE = 'typ';
+ /** token style for a literal value. e.g. 1, null, true. */
+ var PR_LITERAL = 'lit';
+ /** token style for a punctuation string. */
+ var PR_PUNCTUATION = 'pun';
+ /** token style for a punctuation string. */
+ var PR_PLAIN = 'pln';
+
+ /** token style for an sgml tag. */
+ var PR_TAG = 'tag';
+ /** token style for a markup declaration such as a DOCTYPE. */
+ var PR_DECLARATION = 'dec';
+ /** token style for embedded source. */
+ var PR_SOURCE = 'src';
+ /** token style for an sgml attribute name. */
+ var PR_ATTRIB_NAME = 'atn';
+ /** token style for an sgml attribute value. */
+ var PR_ATTRIB_VALUE = 'atv';
+
+ /**
+ * A class that indicates a section of markup that is not code, e.g. to allow
+ * embedding of line numbers within code listings.
+ */
+ var PR_NOCODE = 'nocode';
+
+ function isWordChar(ch) {
+ return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z');
+ }
+
+ /** Splice one array into another.
+ * Like the python <code>
+ * container[containerPosition:containerPosition + countReplaced] = inserted
+ * </code>
+ * @param {Array} inserted
+ * @param {Array} container modified in place
+ * @param {Number} containerPosition
+ * @param {Number} countReplaced
+ */
+ function spliceArrayInto(
+ inserted, container, containerPosition, countReplaced) {
+ inserted.unshift(containerPosition, countReplaced || 0);
+ try {
+ container.splice.apply(container, inserted);
+ } finally {
+ inserted.splice(0, 2);
+ }
+ }
+
+ /** A set of tokens that can precede a regular expression literal in
+ * javascript.
+ * http://www.mozilla.org/js/language/js20/rationale/syntax.html has the full
+ * list, but I've removed ones that might be problematic when seen in
+ * languages that don't support regular expression literals.
+ *
+ * <p>Specifically, I've removed any keywords that can't precede a regexp
+ * literal in a syntactically legal javascript program, and I've removed the
+ * "in" keyword since it's not a keyword in many languages, and might be used
+ * as a count of inches.
+ * @private
+ */
+ var REGEXP_PRECEDER_PATTERN = function () {
+ var preceders = [
+ "!", "!=", "!==", "#", "%", "%=", "&", "&&", "&&=",
+ "&=", "(", "*", "*=", /* "+", */ "+=", ",", /* "-", */ "-=",
+ "->", /*".", "..", "...", handled below */ "/", "/=", ":", "::", ";",
+ "<", "<<", "<<=", "<=", "=", "==", "===", ">",
+ ">=", ">>", ">>=", ">>>", ">>>=", "?", "@", "[",
+ "^", "^=", "^^", "^^=", "{", "|", "|=", "||",
+ "||=", "~" /* handles =~ and !~ */,
+ "break", "case", "continue", "delete",
+ "do", "else", "finally", "instanceof",
+ "return", "throw", "try", "typeof"
+ ];
+ var pattern = '(?:' +
+ '(?:(?:^|[^0-9.])\\.{1,3})|' + // a dot that's not part of a number
+ '(?:(?:^|[^\\+])\\+)|' + // allow + but not ++
+ '(?:(?:^|[^\\-])-)'; // allow - but not --
+ for (var i = 0; i < preceders.length; ++i) {
+ var preceder = preceders[i];
+ if (isWordChar(preceder.charAt(0))) {
+ pattern += '|\\b' + preceder;
+ } else {
+ pattern += '|' + preceder.replace(/([^=<>:&])/g, '\\$1');
+ }
+ }
+ pattern += '|^)\\s*$'; // matches at end, and matches empty string
+ return new RegExp(pattern);
+ // CAVEAT: this does not properly handle the case where a regular
+ // expression immediately follows another since a regular expression may
+ // have flags for case-sensitivity and the like. Having regexp tokens
+ // adjacent is not
+ // valid in any language I'm aware of, so I'm punting.
+ // TODO: maybe style special characters inside a regexp as punctuation.
+ }();
+
+ // Define regexps here so that the interpreter doesn't have to create an
+ // object each time the function containing them is called.
+ // The language spec requires a new object created even if you don't access
+ // the $1 members.
+ var pr_amp = /&/g;
+ var pr_lt = /</g;
+ var pr_gt = />/g;
+ var pr_quot = /\"/g;
+ /** like textToHtml but escapes double quotes to be attribute safe. */
+ function attribToHtml(str) {
+ return str.replace(pr_amp, '&amp;')
+ .replace(pr_lt, '&lt;')
+ .replace(pr_gt, '&gt;')
+ .replace(pr_quot, '&quot;');
+ }
+
+ /** escapest html special characters to html. */
+ function textToHtml(str) {
+ return str.replace(pr_amp, '&amp;')
+ .replace(pr_lt, '&lt;')
+ .replace(pr_gt, '&gt;');
+ }
+
+
+ var pr_ltEnt = /&lt;/g;
+ var pr_gtEnt = /&gt;/g;
+ var pr_aposEnt = /&apos;/g;
+ var pr_quotEnt = /&quot;/g;
+ var pr_ampEnt = /&amp;/g;
+ var pr_nbspEnt = /&nbsp;/g;
+ /** unescapes html to plain text. */
+ function htmlToText(html) {
+ var pos = html.indexOf('&');
+ if (pos < 0) { return html; }
+ // Handle numeric entities specially. We can't use functional substitution
+ // since that doesn't work in older versions of Safari.
+ // These should be rare since most browsers convert them to normal chars.
+ for (--pos; (pos = html.indexOf('&#', pos + 1)) >= 0;) {
+ var end = html.indexOf(';', pos);
+ if (end >= 0) {
+ var num = html.substring(pos + 3, end);
+ var radix = 10;
+ if (num && num.charAt(0) === 'x') {
+ num = num.substring(1);
+ radix = 16;
+ }
+ var codePoint = parseInt(num, radix);
+ if (!isNaN(codePoint)) {
+ html = (html.substring(0, pos) + String.fromCharCode(codePoint) +
+ html.substring(end + 1));
+ }
+ }
+ }
+
+ return html.replace(pr_ltEnt, '<')
+ .replace(pr_gtEnt, '>')
+ .replace(pr_aposEnt, "'")
+ .replace(pr_quotEnt, '"')
+ .replace(pr_ampEnt, '&')
+ .replace(pr_nbspEnt, ' ');
+ }
+
+ /** is the given node's innerHTML normally unescaped? */
+ function isRawContent(node) {
+ return 'XMP' === node.tagName;
+ }
+
+ function normalizedHtml(node, out) {
+ switch (node.nodeType) {
+ case 1: // an element
+ var name = node.tagName.toLowerCase();
+ out.push('<', name);
+ for (var i = 0; i < node.attributes.length; ++i) {
+ var attr = node.attributes[i];
+ if (!attr.specified) { continue; }
+ out.push(' ');
+ normalizedHtml(attr, out);
+ }
+ out.push('>');
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ normalizedHtml(child, out);
+ }
+ if (node.firstChild || !/^(?:br|link|img)$/.test(name)) {
+ out.push('<\/', name, '>');
+ }
+ break;
+ case 2: // an attribute
+ out.push(node.name.toLowerCase(), '="', attribToHtml(node.value), '"');
+ break;
+ case 3: case 4: // text
+ out.push(textToHtml(node.nodeValue));
+ break;
+ }
+ }
+
+ var PR_innerHtmlWorks = null;
+ function getInnerHtml(node) {
+ // inner html is hopelessly broken in Safari 2.0.4 when the content is
+ // an html description of well formed XML and the containing tag is a PRE
+ // tag, so we detect that case and emulate innerHTML.
+ if (null === PR_innerHtmlWorks) {
+ var testNode = document.createElement('PRE');
+ testNode.appendChild(
+ document.createTextNode('<!DOCTYPE foo PUBLIC "foo bar">\n<foo />'));
+ PR_innerHtmlWorks = !/</.test(testNode.innerHTML);
+ }
+
+ if (PR_innerHtmlWorks) {
+ var content = node.innerHTML;
+ // XMP tags contain unescaped entities so require special handling.
+ if (isRawContent(node)) {
+ content = textToHtml(content);
+ }
+ return content;
+ }
+
+ var out = [];
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ normalizedHtml(child, out);
+ }
+ return out.join('');
+ }
+
+ /** returns a function that expand tabs to spaces. This function can be fed
+ * successive chunks of text, and will maintain its own internal state to
+ * keep track of how tabs are expanded.
+ * @return {function (string) : string} a function that takes
+ * plain text and return the text with tabs expanded.
+ * @private
+ */
+ function makeTabExpander(tabWidth) {
+ var SPACES = ' ';
+ var charInLine = 0;
+
+ return function (plainText) {
+ // walk over each character looking for tabs and newlines.
+ // On tabs, expand them. On newlines, reset charInLine.
+ // Otherwise increment charInLine
+ var out = null;
+ var pos = 0;
+ for (var i = 0, n = plainText.length; i < n; ++i) {
+ var ch = plainText.charAt(i);
+
+ switch (ch) {
+ case '\t':
+ if (!out) { out = []; }
+ out.push(plainText.substring(pos, i));
+ // calculate how much space we need in front of this part
+ // nSpaces is the amount of padding -- the number of spaces needed
+ // to move us to the next column, where columns occur at factors of
+ // tabWidth.
+ var nSpaces = tabWidth - (charInLine % tabWidth);
+ charInLine += nSpaces;
+ for (; nSpaces >= 0; nSpaces -= SPACES.length) {
+ out.push(SPACES.substring(0, nSpaces));
+ }
+ pos = i + 1;
+ break;
+ case '\n':
+ charInLine = 0;
+ break;
+ default:
+ ++charInLine;
+ }
+ }
+ if (!out) { return plainText; }
+ out.push(plainText.substring(pos));
+ return out.join('');
+ };
+ }
+
+ // The below pattern matches one of the following
+ // (1) /[^<]+/ : A run of characters other than '<'
+ // (2) /<!--.*?-->/: an HTML comment
+ // (3) /<!\[CDATA\[.*?\]\]>/: a cdata section
+ // (3) /<\/?[a-zA-Z][^>]*>/ : A probably tag that should not be highlighted
+ // (4) /</ : A '<' that does not begin a larger chunk. Treated as 1
+ var pr_chunkPattern =
+ /(?:[^<]+|<!--[\s\S]*?-->|<!\[CDATA\[([\s\S]*?)\]\]>|<\/?[a-zA-Z][^>]*>|<)/g;
+ var pr_commentPrefix = /^<!--/;
+ var pr_cdataPrefix = /^<\[CDATA\[/;
+ var pr_brPrefix = /^<br\b/i;
+ var pr_tagNameRe = /^<(\/?)([a-zA-Z]+)/;
+
+ /** split markup into chunks of html tags (style null) and
+ * plain text (style {@link #PR_PLAIN}), converting tags which are
+ * significant for tokenization (<br>) into their textual equivalent.
+ *
+ * @param {string} s html where whitespace is considered significant.
+ * @return {Object} source code and extracted tags.
+ * @private
+ */
+ function extractTags(s) {
+ // since the pattern has the 'g' modifier and defines no capturing groups,
+ // this will return a list of all chunks which we then classify and wrap as
+ // PR_Tokens
+ var matches = s.match(pr_chunkPattern);
+ var sourceBuf = [];
+ var sourceBufLen = 0;
+ var extractedTags = [];
+ if (matches) {
+ for (var i = 0, n = matches.length; i < n; ++i) {
+ var match = matches[i];
+ if (match.length > 1 && match.charAt(0) === '<') {
+ if (pr_commentPrefix.test(match)) { continue; }
+ if (pr_cdataPrefix.test(match)) {
+ // strip CDATA prefix and suffix. Don't unescape since it's CDATA
+ sourceBuf.push(match.substring(9, match.length - 3));
+ sourceBufLen += match.length - 12;
+ } else if (pr_brPrefix.test(match)) {
+ // <br> tags are lexically significant so convert them to text.
+ // This is undone later.
+ sourceBuf.push('\n');
+ ++sourceBufLen;
+ } else {
+ if (match.indexOf(PR_NOCODE) >= 0 && isNoCodeTag(match)) {
+ // A <span class="nocode"> will start a section that should be
+ // ignored. Continue walking the list until we see a matching end
+ // tag.
+ var name = match.match(pr_tagNameRe)[2];
+ var depth = 1;
+ end_tag_loop:
+ for (var j = i + 1; j < n; ++j) {
+ var name2 = matches[j].match(pr_tagNameRe);
+ if (name2 && name2[2] === name) {
+ if (name2[1] === '/') {
+ if (--depth === 0) { break end_tag_loop; }
+ } else {
+ ++depth;
+ }
+ }
+ }
+ if (j < n) {
+ extractedTags.push(
+ sourceBufLen, matches.slice(i, j + 1).join(''));
+ i = j;
+ } else { // Ignore unclosed sections.
+ extractedTags.push(sourceBufLen, match);
+ }
+ } else {
+ extractedTags.push(sourceBufLen, match);
+ }
+ }
+ } else {
+ var literalText = htmlToText(match);
+ sourceBuf.push(literalText);
+ sourceBufLen += literalText.length;
+ }
+ }
+ }
+ return { source: sourceBuf.join(''), tags: extractedTags };
+ }
+
+ /** True if the given tag contains a class attribute with the nocode class. */
+ function isNoCodeTag(tag) {
+ return !!tag
+ // First canonicalize the representation of attributes
+ .replace(/\s(\w+)\s*=\s*(?:\"([^\"]*)\"|'([^\']*)'|(\S+))/g,
+ ' $1="$2$3$4"')
+ // Then look for the attribute we want.
+ .match(/[cC][lL][aA][sS][sS]=\"[^\"]*\bnocode\b/);
+ }
+
+ /** Given triples of [style, pattern, context] returns a lexing function,
+ * The lexing function interprets the patterns to find token boundaries and
+ * returns a decoration list of the form
+ * [index_0, style_0, index_1, style_1, ..., index_n, style_n]
+ * where index_n is an index into the sourceCode, and style_n is a style
+ * constant like PR_PLAIN. index_n-1 <= index_n, and style_n-1 applies to
+ * all characters in sourceCode[index_n-1:index_n].
+ *
+ * The stylePatterns is a list whose elements have the form
+ * [style : string, pattern : RegExp, context : RegExp, shortcut : string].
+ &
+ * Style is a style constant like PR_PLAIN.
+ *
+ * Pattern must only match prefixes, and if it matches a prefix and context
+ * is null or matches the last non-comment token parsed, then that match is
+ * considered a token with the same style.
+ *
+ * Context is applied to the last non-whitespace, non-comment token
+ * recognized.
+ *
+ * Shortcut is an optional string of characters, any of which, if the first
+ * character, gurantee that this pattern and only this pattern matches.
+ *
+ * @param {Array} shortcutStylePatterns patterns that always start with
+ * a known character. Must have a shortcut string.
+ * @param {Array} fallthroughStylePatterns patterns that will be tried in
+ * order if the shortcut ones fail. May have shortcuts.
+ *
+ * @return {function (string, number?) : Array.<number|string>} a
+ * function that takes source code and returns a list of decorations.
+ */
+ function createSimpleLexer(shortcutStylePatterns,
+ fallthroughStylePatterns) {
+ var shortcuts = {};
+ (function () {
+ var allPatterns = shortcutStylePatterns.concat(fallthroughStylePatterns);
+ for (var i = allPatterns.length; --i >= 0;) {
+ var patternParts = allPatterns[i];
+ var shortcutChars = patternParts[3];
+ if (shortcutChars) {
+ for (var c = shortcutChars.length; --c >= 0;) {
+ shortcuts[shortcutChars.charAt(c)] = patternParts;
+ }
+ }
+ }
+ })();
+
+ var nPatterns = fallthroughStylePatterns.length;
+ var notWs = /\S/;
+
+ return function (sourceCode, opt_basePos) {
+ opt_basePos = opt_basePos || 0;
+ var decorations = [opt_basePos, PR_PLAIN];
+ var lastToken = '';
+ var pos = 0; // index into sourceCode
+ var tail = sourceCode;
+
+ while (tail.length) {
+ var style;
+ var token = null;
+ var match;
+
+ var patternParts = shortcuts[tail.charAt(0)];
+ if (patternParts) {
+ match = tail.match(patternParts[1]);
+ token = match[0];
+ style = patternParts[0];
+ } else {
+ for (var i = 0; i < nPatterns; ++i) {
+ patternParts = fallthroughStylePatterns[i];
+ var contextPattern = patternParts[2];
+ if (contextPattern && !contextPattern.test(lastToken)) {
+ // rule can't be used
+ continue;
+ }
+ match = tail.match(patternParts[1]);
+ if (match) {
+ token = match[0];
+ style = patternParts[0];
+ break;
+ }
+ }
+
+ if (!token) { // make sure that we make progress
+ style = PR_PLAIN;
+ token = tail.substring(0, 1);
+ }
+ }
+
+ decorations.push(opt_basePos + pos, style);
+ pos += token.length;
+ tail = tail.substring(token.length);
+ if (style !== PR_COMMENT && notWs.test(token)) { lastToken = token; }
+ }
+ return decorations;
+ };
+ }
+
+ var PR_MARKUP_LEXER = createSimpleLexer([], [
+ [PR_PLAIN, /^[^<]+/, null],
+ [PR_DECLARATION, /^<!\w[^>]*(?:>|$)/, null],
+ [PR_COMMENT, /^<!--[\s\S]*?(?:-->|$)/, null],
+ [PR_SOURCE, /^<\?[\s\S]*?(?:\?>|$)/, null],
+ [PR_SOURCE, /^<%[\s\S]*?(?:%>|$)/, null],
+ [PR_SOURCE,
+ // Tags whose content is not escaped, and which contain source code.
+ /^<(script|style|xmp)\b[^>]*>[\s\S]*?<\/\1\b[^>]*>/i, null],
+ [PR_TAG, /^<\/?\w[^<>]*>/, null]
+ ]);
+ // Splits any of the source|style|xmp entries above into a start tag,
+ // source content, and end tag.
+ var PR_SOURCE_CHUNK_PARTS = /^(<[^>]*>)([\s\S]*)(<\/[^>]*>)$/;
+ /** split markup on tags, comments, application directives, and other top
+ * level constructs. Tags are returned as a single token - attributes are
+ * not yet broken out.
+ * @private
+ */
+ function tokenizeMarkup(source) {
+ var decorations = PR_MARKUP_LEXER(source);
+ for (var i = 0; i < decorations.length; i += 2) {
+ if (decorations[i + 1] === PR_SOURCE) {
+ var start, end;
+ start = decorations[i];
+ end = i + 2 < decorations.length ? decorations[i + 2] : source.length;
+ // Split out start and end script tags as actual tags, and leave the
+ // body with style SCRIPT.
+ var sourceChunk = source.substring(start, end);
+ var match = sourceChunk.match(PR_SOURCE_CHUNK_PARTS);
+ if (match) {
+ decorations.splice(
+ i, 2,
+ start, PR_TAG, // the open chunk
+ start + match[1].length, PR_SOURCE,
+ start + match[1].length + (match[2] || '').length, PR_TAG);
+ }
+ }
+ }
+ return decorations;
+ }
+
+ var PR_TAG_LEXER = createSimpleLexer([
+ [PR_ATTRIB_VALUE, /^\'[^\']*(?:\'|$)/, null, "'"],
+ [PR_ATTRIB_VALUE, /^\"[^\"]*(?:\"|$)/, null, '"'],
+ [PR_PUNCTUATION, /^[<>\/=]+/, null, '<>/=']
+ ], [
+ [PR_TAG, /^[\w:\-]+/, /^</],
+ [PR_ATTRIB_VALUE, /^[\w\-]+/, /^=/],
+ [PR_ATTRIB_NAME, /^[\w:\-]+/, null],
+ [PR_PLAIN, /^\s+/, null, ' \t\r\n']
+ ]);
+ /** split tags attributes and their values out from the tag name, and
+ * recursively lex source chunks.
+ * @private
+ */
+ function splitTagAttributes(source, decorations) {
+ for (var i = 0; i < decorations.length; i += 2) {
+ var style = decorations[i + 1];
+ if (style === PR_TAG) {
+ var start, end;
+ start = decorations[i];
+ end = i + 2 < decorations.length ? decorations[i + 2] : source.length;
+ var chunk = source.substring(start, end);
+ var subDecorations = PR_TAG_LEXER(chunk, start);
+ spliceArrayInto(subDecorations, decorations, i, 2);
+ i += subDecorations.length - 2;
+ }
+ }
+ return decorations;
+ }
+
+ /** returns a function that produces a list of decorations from source text.
+ *
+ * This code treats ", ', and ` as string delimiters, and \ as a string
+ * escape. It does not recognize perl's qq() style strings.
+ * It has no special handling for double delimiter escapes as in basic, or
+ * the tripled delimiters used in python, but should work on those regardless
+ * although in those cases a single string literal may be broken up into
+ * multiple adjacent string literals.
+ *
+ * It recognizes C, C++, and shell style comments.
+ *
+ * @param {Object} options a set of optional parameters.
+ * @return {function (string) : Array.<string|number>} a
+ * decorator that takes sourceCode as plain text and that returns a
+ * decoration list
+ */
+ function sourceDecorator(options) {
+ var shortcutStylePatterns = [], fallthroughStylePatterns = [];
+ if (options.tripleQuotedStrings) {
+ // '''multi-line-string''', 'single-line-string', and double-quoted
+ shortcutStylePatterns.push(
+ [PR_STRING, /^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,
+ null, '\'"']);
+ } else if (options.multiLineStrings) {
+ // 'multi-line-string', "multi-line-string"
+ shortcutStylePatterns.push(
+ [PR_STRING, /^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,
+ null, '\'"`']);
+ } else {
+ // 'single-line-string', "single-line-string"
+ shortcutStylePatterns.push(
+ [PR_STRING,
+ /^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,
+ null, '"\'']);
+ }
+ fallthroughStylePatterns.push(
+ [PR_PLAIN, /^(?:[^\'\"\`\/\#]+)/, null, ' \r\n']);
+ if (options.hashComments) {
+ shortcutStylePatterns.push([PR_COMMENT, /^#[^\r\n]*/, null, '#']);
+ }
+ if (options.cStyleComments) {
+ fallthroughStylePatterns.push([PR_COMMENT, /^\/\/[^\r\n]*/, null]);
+ fallthroughStylePatterns.push(
+ [PR_COMMENT, /^\/\*[\s\S]*?(?:\*\/|$)/, null]);
+ }
+ if (options.regexLiterals) {
+ var REGEX_LITERAL = (
+ // A regular expression literal starts with a slash that is
+ // not followed by * or / so that it is not confused with
+ // comments.
+ '^/(?=[^/*])'
+ // and then contains any number of raw characters,
+ + '(?:[^/\\x5B\\x5C]'
+ // escape sequences (\x5C),
+ + '|\\x5C[\\s\\S]'
+ // or non-nesting character sets (\x5B\x5D);
+ + '|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+'
+ // finally closed by a /.
+ + '(?:/|$)');
+ fallthroughStylePatterns.push(
+ [PR_STRING, new RegExp(REGEX_LITERAL), REGEXP_PRECEDER_PATTERN]);
+ }
+
+ var keywords = wordSet(options.keywords);
+
+ options = null;
+
+ /** splits the given string into comment, string, and "other" tokens.
+ * @param {string} sourceCode as plain text
+ * @return {Array.<number|string>} a decoration list.
+ * @private
+ */
+ var splitStringAndCommentTokens = createSimpleLexer(
+ shortcutStylePatterns, fallthroughStylePatterns);
+
+ var styleLiteralIdentifierPuncRecognizer = createSimpleLexer([], [
+ [PR_PLAIN, /^\s+/, null, ' \r\n'],
+ // TODO(mikesamuel): recognize non-latin letters and numerals in idents
+ [PR_PLAIN, /^[a-z_$@][a-z_$@0-9]*/i, null],
+ // A hex number
+ [PR_LITERAL, /^0x[a-f0-9]+[a-z]/i, null],
+ // An octal or decimal number, possibly in scientific notation
+ [PR_LITERAL,
+ /^(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d+)(?:e[+\-]?\d+)?[a-z]*/i,
+ null, '123456789'],
+ [PR_PUNCTUATION, /^[^\s\w\.$@]+/, null]
+ // Fallback will handle decimal points not adjacent to a digit
+ ]);
+
+ /** splits plain text tokens into more specific tokens, and then tries to
+ * recognize keywords, and types.
+ * @private
+ */
+ function splitNonStringNonCommentTokens(source, decorations) {
+ for (var i = 0; i < decorations.length; i += 2) {
+ var style = decorations[i + 1];
+ if (style === PR_PLAIN) {
+ var start, end, chunk, subDecs;
+ start = decorations[i];
+ end = i + 2 < decorations.length ? decorations[i + 2] : source.length;
+ chunk = source.substring(start, end);
+ subDecs = styleLiteralIdentifierPuncRecognizer(chunk, start);
+ for (var j = 0, m = subDecs.length; j < m; j += 2) {
+ var subStyle = subDecs[j + 1];
+ if (subStyle === PR_PLAIN) {
+ var subStart = subDecs[j];
+ var subEnd = j + 2 < m ? subDecs[j + 2] : chunk.length;
+ var token = source.substring(subStart, subEnd);
+ if (token === '.') {
+ subDecs[j + 1] = PR_PUNCTUATION;
+ } else if (token in keywords) {
+ subDecs[j + 1] = PR_KEYWORD;
+ } else if (/^@?[A-Z][A-Z$]*[a-z][A-Za-z$]*$/.test(token)) {
+ // classify types and annotations using Java's style conventions
+ subDecs[j + 1] = token.charAt(0) === '@' ? PR_LITERAL : PR_TYPE;
+ }
+ }
+ }
+ spliceArrayInto(subDecs, decorations, i, 2);
+ i += subDecs.length - 2;
+ }
+ }
+ return decorations;
+ }
+
+ return function (sourceCode) {
+ // Split into strings, comments, and other.
+ // We do this because strings and comments are easily recognizable and can
+ // contain stuff that looks like other tokens, so we want to mark those
+ // early so we don't recurse into them.
+ var decorations = splitStringAndCommentTokens(sourceCode);
+
+ // Split non comment|string tokens on whitespace and word boundaries
+ decorations = splitNonStringNonCommentTokens(sourceCode, decorations);
+
+ return decorations;
+ };
+ }
+
+ var decorateSource = sourceDecorator({
+ keywords: ALL_KEYWORDS,
+ hashComments: true,
+ cStyleComments: true,
+ multiLineStrings: true,
+ regexLiterals: true
+ });
+
+ /** identify regions of markup that are really source code, and recursivley
+ * lex them.
+ * @private
+ */
+ function splitSourceNodes(source, decorations) {
+ for (var i = 0; i < decorations.length; i += 2) {
+ var style = decorations[i + 1];
+ if (style === PR_SOURCE) {
+ // Recurse using the non-markup lexer
+ var start, end;
+ start = decorations[i];
+ end = i + 2 < decorations.length ? decorations[i + 2] : source.length;
+ var subDecorations = decorateSource(source.substring(start, end));
+ for (var j = 0, m = subDecorations.length; j < m; j += 2) {
+ subDecorations[j] += start;
+ }
+ spliceArrayInto(subDecorations, decorations, i, 2);
+ i += subDecorations.length - 2;
+ }
+ }
+ return decorations;
+ }
+
+ /** identify attribute values that really contain source code and recursively
+ * lex them.
+ * @private
+ */
+ function splitSourceAttributes(source, decorations) {
+ var nextValueIsSource = false;
+ for (var i = 0; i < decorations.length; i += 2) {
+ var style = decorations[i + 1];
+ var start, end;
+ if (style === PR_ATTRIB_NAME) {
+ start = decorations[i];
+ end = i + 2 < decorations.length ? decorations[i + 2] : source.length;
+ nextValueIsSource = /^on|^style$/i.test(source.substring(start, end));
+ } else if (style === PR_ATTRIB_VALUE) {
+ if (nextValueIsSource) {
+ start = decorations[i];
+ end = i + 2 < decorations.length ? decorations[i + 2] : source.length;
+ var attribValue = source.substring(start, end);
+ var attribLen = attribValue.length;
+ var quoted =
+ (attribLen >= 2 && /^[\"\']/.test(attribValue) &&
+ attribValue.charAt(0) === attribValue.charAt(attribLen - 1));
+
+ var attribSource;
+ var attribSourceStart;
+ var attribSourceEnd;
+ if (quoted) {
+ attribSourceStart = start + 1;
+ attribSourceEnd = end - 1;
+ attribSource = attribValue;
+ } else {
+ attribSourceStart = start + 1;
+ attribSourceEnd = end - 1;
+ attribSource = attribValue.substring(1, attribValue.length - 1);
+ }
+
+ var attribSourceDecorations = decorateSource(attribSource);
+ for (var j = 0, m = attribSourceDecorations.length; j < m; j += 2) {
+ attribSourceDecorations[j] += attribSourceStart;
+ }
+
+ if (quoted) {
+ attribSourceDecorations.push(attribSourceEnd, PR_ATTRIB_VALUE);
+ spliceArrayInto(attribSourceDecorations, decorations, i + 2, 0);
+ } else {
+ spliceArrayInto(attribSourceDecorations, decorations, i, 2);
+ }
+ }
+ nextValueIsSource = false;
+ }
+ }
+ return decorations;
+ }
+
+ /** returns a decoration list given a string of markup.
+ *
+ * This code recognizes a number of constructs.
+ * <!-- ... --> comment
+ * <!\w ... > declaration
+ * <\w ... > tag
+ * </\w ... > tag
+ * <?...?> embedded source
+ * <%...%> embedded source
+ * &[#\w]...; entity
+ *
+ * It does not recognizes %foo; doctype entities from .
+ *
+ * It will recurse into any <style>, <script>, and on* attributes using
+ * PR_lexSource.
+ */
+ function decorateMarkup(sourceCode) {
+ // This function works as follows:
+ // 1) Start by splitting the markup into text and tag chunks
+ // Input: string s
+ // Output: List<PR_Token> where style in (PR_PLAIN, null)
+ // 2) Then split the text chunks further into comments, declarations,
+ // tags, etc.
+ // After each split, consider whether the token is the start of an
+ // embedded source section, i.e. is an open <script> tag. If it is, find
+ // the corresponding close token, and don't bother to lex in between.
+ // Input: List<string>
+ // Output: List<PR_Token> with style in
+ // (PR_TAG, PR_PLAIN, PR_SOURCE, null)
+ // 3) Finally go over each tag token and split out attribute names and
+ // values.
+ // Input: List<PR_Token>
+ // Output: List<PR_Token> where style in
+ // (PR_TAG, PR_PLAIN, PR_SOURCE, NAME, VALUE, null)
+ var decorations = tokenizeMarkup(sourceCode);
+ decorations = splitTagAttributes(sourceCode, decorations);
+ decorations = splitSourceNodes(sourceCode, decorations);
+ decorations = splitSourceAttributes(sourceCode, decorations);
+ return decorations;
+ }
+
+ /**
+ * @param {string} sourceText plain text
+ * @param {Array.<number|string>} extractedTags chunks of raw html preceded
+ * by their position in sourceText in order.
+ * @param {Array.<number|string>} decorations style classes preceded by their
+ * position in sourceText in order.
+ * @return {string} html
+ * @private
+ */
+ function recombineTagsAndDecorations(sourceText, extractedTags, decorations) {
+ var html = [];
+ // index past the last char in sourceText written to html
+ var outputIdx = 0;
+
+ var openDecoration = null;
+ var currentDecoration = null;
+ var tagPos = 0; // index into extractedTags
+ var decPos = 0; // index into decorations
+ var tabExpander = makeTabExpander(PR_TAB_WIDTH);
+
+ var adjacentSpaceRe = /([\r\n ]) /g;
+ var startOrSpaceRe = /(^| ) /gm;
+ var newlineRe = /\r\n?|\n/g;
+ var trailingSpaceRe = /[ \r\n]$/;
+ var lastWasSpace = true; // the last text chunk emitted ended with a space.
+
+ // A helper function that is responsible for opening sections of decoration
+ // and outputing properly escaped chunks of source
+ function emitTextUpTo(sourceIdx) {
+ if (sourceIdx > outputIdx) {
+ if (openDecoration && openDecoration !== currentDecoration) {
+ // Close the current decoration
+ html.push('</span>');
+ openDecoration = null;
+ }
+ if (!openDecoration && currentDecoration) {
+ openDecoration = currentDecoration;
+ html.push('<span class="', openDecoration, '">');
+ }
+ // This interacts badly with some wikis which introduces paragraph tags
+ // into pre blocks for some strange reason.
+ // It's necessary for IE though which seems to lose the preformattedness
+ // of <pre> tags when their innerHTML is assigned.
+ // http://stud3.tuwien.ac.at/~e0226430/innerHtmlQuirk.html
+ // and it serves to undo the conversion of <br>s to newlines done in
+ // chunkify.
+ var htmlChunk = textToHtml(
+ tabExpander(sourceText.substring(outputIdx, sourceIdx)))
+ .replace(lastWasSpace
+ ? startOrSpaceRe
+ : adjacentSpaceRe, '$1&nbsp;');
+ // Keep track of whether we need to escape space at the beginning of the
+ // next chunk.
+ lastWasSpace = trailingSpaceRe.test(htmlChunk);
+ html.push(htmlChunk.replace(newlineRe, '<br />'));
+ outputIdx = sourceIdx;
+ }
+ }
+
+ while (true) {
+ // Determine if we're going to consume a tag this time around. Otherwise
+ // we consume a decoration or exit.
+ var outputTag;
+ if (tagPos < extractedTags.length) {
+ if (decPos < decorations.length) {
+ // Pick one giving preference to extractedTags since we shouldn't open
+ // a new style that we're going to have to immediately close in order
+ // to output a tag.
+ outputTag = extractedTags[tagPos] <= decorations[decPos];
+ } else {
+ outputTag = true;
+ }
+ } else {
+ outputTag = false;
+ }
+ // Consume either a decoration or a tag or exit.
+ if (outputTag) {
+ emitTextUpTo(extractedTags[tagPos]);
+ if (openDecoration) {
+ // Close the current decoration
+ html.push('</span>');
+ openDecoration = null;
+ }
+ html.push(extractedTags[tagPos + 1]);
+ tagPos += 2;
+ } else if (decPos < decorations.length) {
+ emitTextUpTo(decorations[decPos]);
+ currentDecoration = decorations[decPos + 1];
+ decPos += 2;
+ } else {
+ break;
+ }
+ }
+ emitTextUpTo(sourceText.length);
+ if (openDecoration) {
+ html.push('</span>');
+ }
+
+ return html.join('');
+ }
+
+ /** Maps language-specific file extensions to handlers. */
+ var langHandlerRegistry = {};
+ /** Register a language handler for the given file extensions.
+ * @param {function (string) : Array.<number|string>} handler
+ * a function from source code to a list of decorations.
+ * @param {Array.<string>} fileExtensions
+ */
+ function registerLangHandler(handler, fileExtensions) {
+ for (var i = fileExtensions.length; --i >= 0;) {
+ var ext = fileExtensions[i];
+ if (!langHandlerRegistry.hasOwnProperty(ext)) {
+ langHandlerRegistry[ext] = handler;
+ } else if ('console' in window) {
+ console.log('cannot override language handler %s', ext);
+ }
+ }
+ }
+ registerLangHandler(decorateSource, ['default-code']);
+ registerLangHandler(decorateMarkup,
+ ['default-markup', 'html', 'htm', 'xhtml', 'xml', 'xsl']);
+ registerLangHandler(sourceDecorator({
+ keywords: CPP_KEYWORDS,
+ hashComments: true,
+ cStyleComments: true
+ }), ['c', 'cc', 'cpp', 'cxx', 'cyc']);
+ registerLangHandler(sourceDecorator({
+ keywords: CSHARP_KEYWORDS,
+ hashComments: true,
+ cStyleComments: true
+ }), ['cs']);
+ registerLangHandler(sourceDecorator({
+ keywords: JAVA_KEYWORDS,
+ cStyleComments: true
+ }), ['java']);
+ registerLangHandler(sourceDecorator({
+ keywords: SH_KEYWORDS,
+ hashComments: true,
+ multiLineStrings: true
+ }), ['bsh', 'csh', 'sh']);
+ registerLangHandler(sourceDecorator({
+ keywords: PYTHON_KEYWORDS,
+ hashComments: true,
+ multiLineStrings: true,
+ tripleQuotedStrings: true
+ }), ['cv', 'py']);
+ registerLangHandler(sourceDecorator({
+ keywords: PERL_KEYWORDS,
+ hashComments: true,
+ multiLineStrings: true,
+ regexLiterals: true
+ }), ['perl', 'pl', 'pm']);
+ registerLangHandler(sourceDecorator({
+ keywords: RUBY_KEYWORDS,
+ hashComments: true,
+ multiLineStrings: true,
+ regexLiterals: true
+ }), ['rb']);
+ registerLangHandler(sourceDecorator({
+ keywords: JSCRIPT_KEYWORDS,
+ cStyleComments: true,
+ regexLiterals: true
+ }), ['js']);
+
+ function prettyPrintOne(sourceCodeHtml, opt_langExtension) {
+ try {
+ // Extract tags, and convert the source code to plain text.
+ var sourceAndExtractedTags = extractTags(sourceCodeHtml);
+ /** Plain text. @type {string} */
+ var source = sourceAndExtractedTags.source;
+
+ /** Even entries are positions in source in ascending order. Odd entries
+ * are tags that were extracted at that position.
+ * @type {Array.<number|string>}
+ */
+ var extractedTags = sourceAndExtractedTags.tags;
+
+ // Pick a lexer and apply it.
+ if (!langHandlerRegistry.hasOwnProperty(opt_langExtension)) {
+ // Treat it as markup if the first non whitespace character is a < and
+ // the last non-whitespace character is a >.
+ opt_langExtension =
+ /^\s*</.test(source) ? 'default-markup' : 'default-code';
+ }
+
+ /** Even entries are positions in source in ascending order. Odd enties
+ * are style markers (e.g., PR_COMMENT) that run from that position until
+ * the end.
+ * @type {Array.<number|string>}
+ */
+ var decorations = langHandlerRegistry[opt_langExtension].call({}, source);
+
+ // Integrate the decorations and tags back into the source code to produce
+ // a decorated html string.
+ return recombineTagsAndDecorations(source, extractedTags, decorations);
+ } catch (e) {
+ if ('console' in window) {
+ console.log(e);
+ console.trace();
+ }
+ return sourceCodeHtml;
+ }
+ }
+
+ function prettyPrint(opt_whenDone) {
+ var isIE6 = _pr_isIE6();
+
+ // fetch a list of nodes to rewrite
+ var codeSegments = [
+ document.getElementsByTagName('pre'),
+ document.getElementsByTagName('code'),
+ document.getElementsByTagName('xmp') ];
+ var elements = [];
+ for (var i = 0; i < codeSegments.length; ++i) {
+ for (var j = 0; j < codeSegments[i].length; ++j) {
+ elements.push(codeSegments[i][j]);
+ }
+ }
+ codeSegments = null;
+
+ // the loop is broken into a series of continuations to make sure that we
+ // don't make the browser unresponsive when rewriting a large page.
+ var k = 0;
+
+ function doWork() {
+ var endTime = (PR_SHOULD_USE_CONTINUATION ?
+ new Date().getTime() + 250 /* ms */ :
+ Infinity);
+ for (; k < elements.length && new Date().getTime() < endTime; k++) {
+ var cs = elements[k];
+ if (cs.className && cs.className.indexOf('prettyprint') >= 0) {
+ // If the classes includes a language extensions, use it.
+ // Language extensions can be specified like
+ // <pre class="prettyprint lang-cpp">
+ // the language extension "cpp" is used to find a language handler as
+ // passed to PR_registerLangHandler.
+ var langExtension = cs.className.match(/\blang-(\w+)\b/);
+ if (langExtension) { langExtension = langExtension[1]; }
+
+ // make sure this is not nested in an already prettified element
+ var nested = false;
+ for (var p = cs.parentNode; p; p = p.parentNode) {
+ if ((p.tagName === 'pre' || p.tagName === 'code' ||
+ p.tagName === 'xmp') &&
+ p.className && p.className.indexOf('prettyprint') >= 0) {
+ nested = true;
+ break;
+ }
+ }
+ if (!nested) {
+ // fetch the content as a snippet of properly escaped HTML.
+ // Firefox adds newlines at the end.
+ var content = getInnerHtml(cs);
+ content = content.replace(/(?:\r\n?|\n)$/, '');
+
+ // do the pretty printing
+ var newContent = prettyPrintOne(content, langExtension);
+
+ // push the prettified html back into the tag.
+ if (!isRawContent(cs)) {
+ // just replace the old html with the new
+ cs.innerHTML = newContent;
+ } else {
+ // we need to change the tag to a <pre> since <xmp>s do not allow
+ // embedded tags such as the span tags used to attach styles to
+ // sections of source code.
+ var pre = document.createElement('PRE');
+ for (var i = 0; i < cs.attributes.length; ++i) {
+ var a = cs.attributes[i];
+ if (a.specified) {
+ var aname = a.name.toLowerCase();
+ if (aname === 'class') {
+ pre.className = a.value; // For IE 6
+ } else {
+ pre.setAttribute(a.name, a.value);
+ }
+ }
+ }
+ pre.innerHTML = newContent;
+
+ // remove the old
+ cs.parentNode.replaceChild(pre, cs);
+ cs = pre;
+ }
+
+ // Replace <br>s with line-feeds so that copying and pasting works
+ // on IE 6.
+ // Doing this on other browsers breaks lots of stuff since \r\n is
+ // treated as two newlines on Firefox, and doing this also slows
+ // down rendering.
+ if (isIE6 && cs.tagName === 'PRE') {
+ var lineBreaks = cs.getElementsByTagName('br');
+ for (var j = lineBreaks.length; --j >= 0;) {
+ var lineBreak = lineBreaks[j];
+ lineBreak.parentNode.replaceChild(
+ document.createTextNode('\r\n'), lineBreak);
+ }
+ }
+ }
+ }
+ }
+ if (k < elements.length) {
+ // finish up in a continuation
+ setTimeout(doWork, 250);
+ } else if (opt_whenDone) {
+ opt_whenDone();
+ }
+ }
+
+ doWork();
+ }
+
+ window['PR_normalizedHtml'] = normalizedHtml;
+ window['prettyPrintOne'] = prettyPrintOne;
+ window['prettyPrint'] = prettyPrint;
+ window['PR'] = {
+ 'createSimpleLexer': createSimpleLexer,
+ 'registerLangHandler': registerLangHandler,
+ 'sourceDecorator': sourceDecorator,
+ 'PR_ATTRIB_NAME': PR_ATTRIB_NAME,
+ 'PR_ATTRIB_VALUE': PR_ATTRIB_VALUE,
+ 'PR_COMMENT': PR_COMMENT,
+ 'PR_DECLARATION': PR_DECLARATION,
+ 'PR_KEYWORD': PR_KEYWORD,
+ 'PR_LITERAL': PR_LITERAL,
+ 'PR_NOCODE': PR_NOCODE,
+ 'PR_PLAIN': PR_PLAIN,
+ 'PR_PUNCTUATION': PR_PUNCTUATION,
+ 'PR_SOURCE': PR_SOURCE,
+ 'PR_STRING': PR_STRING,
+ 'PR_TAG': PR_TAG,
+ 'PR_TYPE': PR_TYPE
+ };
+})();
diff --git a/docs/verifier.html b/docs/verifier.html
new file mode 100644
index 0000000..21bbdf0
--- /dev/null
+++ b/docs/verifier.html
@@ -0,0 +1,179 @@
+<html>
+<head>
+<title>Dalvik Bytecode Verifier Notes</title>
+</head>
+
+<body>
+<h1>Dalvik Bytecode Verifier Notes</h1>
+
+<p>
+The bytecode verifier in the Dalvik VM attempts to provide the same sorts
+of checks and guarantees that other popular virtual machines do. We
+perform generally the same set of checks as are described in _The Java
+Virtual Machine Specification, Second Edition_, including the updates
+planned for the Third Edition.
+
+<p>
+Verification can be enabled for all classes, disabled for all, or enabled
+only for "remote" (non-bootstrap) classes. It should be performed for any
+class that will be processed with the DEX optimizer, and in fact the
+default VM behavior is to only optimize verified classes.
+
+
+<h2>Why Verify?</h2>
+
+<p>
+The verification process adds additional time to the build and to
+the installation of new applications. It's fairly quick for app-sized
+DEX files, but rather slow for the big "core" and "framework" files.
+Why do it all, when our system relies on UNIX processes for security?
+<p>
+<ol>
+ <li>Optimizations. The interpreter can ignore a lot of potential
+ error cases because the verifier guarantees that they are impossible.
+ Also, we can optimize the DEX file more aggressively if we start
+ with a stronger set of assumptions about the bytecode.
+ <li>"Precise" GC. The work peformed during verification has significant
+ overlap with the work required to compute register use maps for
+ type-precise GC.
+ <li>Intra-application security. If an app wants to download bits
+ of interpreted code over the network and execute them, it can safely
+ do so using well-established security mechanisms.
+ <li>3rd party app failure analysis. We have no way to control the
+ tools and post-processing utilities that external developers employ,
+ so when we get bug reports with a weird exception or native crash
+ it's very helpful to start with the assumption that the bytecode
+ is valid.
+</ol>
+<p>
+It's also a convenient framework to deal with certain situations, notably
+replacement of instructions that access volatile 64-bit fields with
+more rigorous versions that guarantee atomicity.
+
+
+<h2>Verifier Differences</h2>
+
+<p>
+There are a few checks that the Dalvik bytecode verifier does not perform,
+because they're not relevant. For example:
+<ul>
+ <li>Type restrictions on constant pool references are not enforced,
+ because Dalvik does not have a pool of typed constants. (Dalvik
+ uses a simple index into type-specific pools.)
+ <li>Verification of the operand stack size is not performed, because
+ Dalvik does not have an operand stack.
+ <li>Limitations on <code>jsr</code> and <code>ret</code> do not apply,
+ because Dalvik doesn't support subroutines.
+</ul>
+
+In some cases they are implemented differently, e.g.:
+<ul>
+ <li>In a conventional VM, backward branches and exceptions are
+ forbidden when a local variable holds an uninitialized reference. The
+ restriction was changed to mark registers as invalid when they hold
+ references to the uninitialized result of a previous invocation of the
+ same <code>new-instance</code> instruction.
+ This solves the same problem -- trickery potentially allowing
+ uninitialized objects to slip past the verifier -- without unduly
+ limiting branches.
+</ul>
+
+There are also some new ones, such as:
+<ul>
+ <li>The <code>move-exception</code> instruction can only appear as
+ the first instruction in an exception handler.
+ <li>The <code>move-result*</code> instructions can only appear
+ immediately after an appropriate <code>invoke-*</code>
+ or <code>filled-new-array</code> instruction.
+</ul>
+
+<p>
+The VM is permitted but not required to enforce "structured locking"
+constraints, which are designed to ensure that, when a method returns, all
+monitors locked by the method have been unlocked an equal number of times.
+This is not currently implemented.
+
+<p>
+The Dalvik verifier is more restrictive than other VMs in one area:
+type safety on sub-32-bit integer widths. These additional restrictions
+should make it impossible to, say, pass a value outside the range
+[-128, 127] to a function that takes a <code>byte</code> as an argument.
+
+
+<h2>Verification Failures</h2>
+
+<p>
+The verifier may reject a class immediately, or it may defer throwing
+an exception until the code is actually used. For example, if a class
+attempts to perform an illegal access on a field, the VM should throw
+an IllegalAccessError the first time the instruction is encountered.
+On the other hand, if a class contains an invalid bytecode, it should be
+rejected immediately with a VerifyError.
+
+<p>
+Immediate VerifyErrors are accompanied by detailed, if somewhat cryptic,
+information in the log file. From this it's possible to determine the
+exact instruction that failed, and the reason for the failure.
+
+<p>
+It's a bit tricky to implement deferred verification errors in Dalvik.
+A few approaches were considered:
+
+<ol>
+<li>We could replace the invalid field access instruction with a special
+instruction that generates an illegal access error, and allow class
+verification to complete successfully. This type of verification must
+be deferred to first class load, rather than be performed ahead of time
+during DEX optimization, because some failures will depend on the current
+execution environment (e.g. not all classes are available at dexopt time).
+At that point the bytecode instructions are mapped read-only during
+verification, so rewriting them isn't possible.
+</li>
+
+<li>We can perform the access checks when the field/method/class is
+resolved. In a typical VM implementation we would do the check when the
+entry is resolved in the context of the current classfile, but our DEX
+files combine multiple classfiles together, merging the field/method/class
+resolution results into a single large table. Once one class successfully
+resolves the field, every other class in the same DEX file would be able
+to access the field. This is incorrect.
+</li>
+
+<li>Perform the access checks on every field/method/class access.
+This adds significant overhead. This is mitigated somewhat by the DEX
+optimizer, which will convert many field/method/class accesses into a
+simpler form after performing the access check. However, not all accesses
+can be optimized (e.g. accesses to classes unknown at dexopt time),
+and we don't currently have an optimized form of certain instructions
+(notably static field operations).
+</li>
+</ol>
+
+<p>
+In early versions of Dalvik (as found in Android 1.6 and earlier), the verifier
+simply regarded all problems as immediately fatal. This generally worked,
+but in some cases the VM was rejecting classes because of bits of code
+that were never used. The VerifyError itself was sometimes difficult to
+decipher, because it was thrown during verification rather than at the
+point where the problem was first noticed during execution.
+<p>
+The current version uses a variation of approach #1. The dexopt
+command works the way it did before, leaving the code untouched and
+flagging fully-correct classes as "pre-verified". When the VM loads a
+class that didn't pass pre-verification, the verifier is invoked. If a
+"deferrable" problem is detected, a modifiable copy of the instructions
+in the problematic method is made. In that copy, the troubled instruction
+is replaced with an "always throw" opcode, and verification continues.
+
+<p>
+In the example used earlier, an attempt to read from an inaccessible
+field would result in the "field get" instruction being replaced by
+"always throw IllegalAccessError on field X". Creating copies of method
+bodies requires additional heap space, but since this affects very few
+methods overall the memory impact should be minor.
+
+<p>
+<address>Copyright &copy; 2008 The Android Open Source Project</address>
+
+</body>
+</html>
diff --git a/dvz/Android.mk b/dvz/Android.mk
new file mode 100644
index 0000000..4e4387e
--- /dev/null
+++ b/dvz/Android.mk
@@ -0,0 +1,19 @@
+# Copyright 2006 The Android Open Source Project
+
+LOCAL_PATH := $(my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ dvz.c
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils
+
+LOCAL_C_INCLUDES :=
+
+LOCAL_CFLAGS :=
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := dvz
+
+include $(BUILD_EXECUTABLE)
diff --git a/dvz/dvz.c b/dvz/dvz.c
new file mode 100644
index 0000000..88fe086
--- /dev/null
+++ b/dvz/dvz.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cutils/zygote.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+
+#ifndef NELEM
+# define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
+#endif
+
+// pid of child process
+static pid_t g_pid = -1;
+
+static void signal_forwarder (int signal, siginfo_t *si, void *context)
+{
+ if (g_pid >= 0) {
+ kill(g_pid, signal);
+ }
+}
+
+static void post_run_func (int pid) {
+ int my_pgid;
+ int spawned_pgid;
+ int i;
+ int err;
+
+ g_pid = pid;
+
+ my_pgid = getpgid(0);
+ if (my_pgid < 0) {
+ perror ("error with getpgid()");
+ exit (-1);
+ }
+
+ spawned_pgid = getpgid(pid);
+ if (spawned_pgid < 0) {
+ perror ("error with getpgid()");
+ exit (-1);
+ }
+
+ if (my_pgid != spawned_pgid) {
+ // The zygote was unable to move this process into our pgid
+ // We have to forward signals
+
+ int forward_signals[]
+ = {SIGHUP, SIGINT, SIGTERM, SIGWINCH,
+ SIGTSTP, SIGTTIN, SIGTTOU, SIGCONT};
+
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+
+ sa.sa_sigaction = signal_forwarder;
+ sa.sa_flags = SA_SIGINFO;
+
+ for (i = 0; i < NELEM(forward_signals); i++) {
+ err = sigaction(forward_signals[i], &sa, NULL);
+ if (err < 0) {
+ perror ("unexpected error");
+ exit (-1);
+ }
+ }
+ }
+}
+
+static void usage(const char *argv0) {
+ fprintf(stderr,"Usage: %s [--help] [-classpath <classpath>] \n"
+ "\t[additional zygote args] fully.qualified.java.ClassName [args]\n", argv0);
+ fprintf(stderr, "\nRequests a new Dalvik VM instance to be spawned from the zygote\n"
+ "process. stdin, stdout, and stderr are hooked up. This process remains\n"
+ "while the spawned VM instance is alive and forwards some signals.\n"
+ "The exit code of the spawned VM instance is dropped.\n");
+}
+
+int main (int argc, const char **argv) {
+ int err;
+
+ if (argc > 1 && 0 == strcmp(argv[1], "--help")) {
+ usage(argv[0]);
+ exit(0);
+ }
+
+ err = zygote_run_wait(argc - 1, argv + 1, post_run_func);
+
+ if (err < 0) {
+ fprintf(stderr, "%s error: no zygote process found\n", argv[0]);
+ exit(-1);
+ }
+ exit(0);
+}
diff --git a/dx/.classpath b/dx/.classpath
new file mode 100644
index 0000000..5b6d9c7
--- /dev/null
+++ b/dx/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3.8.1"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/dx/.project b/dx/.project
new file mode 100644
index 0000000..bcae232
--- /dev/null
+++ b/dx/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>dx</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/dx/Android.mk b/dx/Android.mk
new file mode 100644
index 0000000..3abf21a
--- /dev/null
+++ b/dx/Android.mk
@@ -0,0 +1,73 @@
+# Copyright 2006 The Android Open Source Project
+#
+LOCAL_PATH := $(call my-dir)
+
+# We use copy-file-to-new-target so that the installed
+# script files' timestamps are at least as new as the
+# .jar files they wrap.
+
+# This tool is prebuilt if we're doing an app-only build.
+ifeq ($(TARGET_BUILD_APPS),)
+
+# the dx script
+# ============================================================
+include $(CLEAR_VARS)
+LOCAL_IS_HOST_MODULE := true
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := dx
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(HOST_OUT_JAVA_LIBRARIES)/dx$(COMMON_JAVA_PACKAGE_SUFFIX)
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/etc/dx | $(ACP)
+ @echo "Copy: $(PRIVATE_MODULE) ($@)"
+ $(copy-file-to-new-target)
+ $(hide) chmod 755 $@
+
+INTERNAL_DALVIK_MODULES += $(LOCAL_INSTALLED_MODULE)
+
+endif # TARGET_BUILD_APPS
+
+# the jasmin script
+# ============================================================
+include $(CLEAR_VARS)
+LOCAL_IS_HOST_MODULE := true
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := jasmin
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(HOST_OUT_JAVA_LIBRARIES)/jasmin.jar
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/etc/jasmin | $(ACP)
+ @echo "Copy: $(PRIVATE_MODULE) ($@)"
+ $(copy-file-to-new-target)
+ $(hide) chmod 755 $@
+
+INTERNAL_DALVIK_MODULES += $(LOCAL_INSTALLED_MODULE)
+
+# the jasmin lib
+# ============================================================
+include $(CLEAR_VARS)
+LOCAL_IS_HOST_MODULE := true
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := jasmin.jar
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/etc/jasmin.jar | $(ACP)
+ @echo "Copy: $(PRIVATE_MODULE) ($@)"
+ $(copy-file-to-target)
+ $(hide) chmod 644 $@
+
+INTERNAL_DALVIK_MODULES += $(LOCAL_INSTALLED_MODULE)
+
+# the other stuff
+# ============================================================
+subdirs := $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk, \
+ src \
+ ))
+
+include $(subdirs)
diff --git a/dx/README.txt b/dx/README.txt
new file mode 100644
index 0000000..6a20c82
--- /dev/null
+++ b/dx/README.txt
@@ -0,0 +1,3 @@
+Home of Dalvik eXchange, the thing that takes in class files and
+reformulates them for consumption in the VM. It also does a few other
+things; use "dx --help" to see a modicum of self-documentation.
diff --git a/dx/etc/bytecode.txt b/dx/etc/bytecode.txt
new file mode 100644
index 0000000..f1df5bf
--- /dev/null
+++ b/dx/etc/bytecode.txt
@@ -0,0 +1,278 @@
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Bytecode definition file
+#
+# Columns are:
+# hex for opcode
+# format
+# has result register (y/n)
+# opcode name
+
+00 10x n nop
+01 12x y move
+02 22x y move/from16
+03 32x y move/16
+04 12x y move-wide
+05 22x y move-wide/from16
+06 32x y move-wide/16
+07 12x y move-object
+08 22x y move-object/from16
+09 32x y move-object/16
+0a 11x y move-result
+0b 11x y move-result-wide
+0c 11x y move-result-object
+0d 11x y move-exception
+0e 10x n return-void
+0f 11x n return
+10 11x n return-wide
+11 11x n return-object
+12 11n y const/4
+13 21s y const/16
+14 31i y const
+15 21h y const/high16
+16 21s y const-wide/16
+17 31i y const-wide/32
+18 51l y const-wide
+19 21h y const-wide/high16
+1a 21c y const-string
+1b 31c y const-string/jumbo
+1c 21c y const-class
+1d 11x n monitor-enter
+1e 11x n monitor-exit
+1f 21c y check-cast
+20 22c y instance-of
+21 12x y array-length
+22 21c y new-instance
+23 22c y new-array
+24 35c n filled-new-array
+25 3rc n filled-new-array/range
+26 31t n fill-array-data
+27 11x n throw
+28 10t n goto
+29 20t n goto/16
+2a 30t n goto/32
+2b 31t n packed-switch
+2c 31t n sparse-switch
+2d 23x y cmpl-float
+2e 23x y cmpg-float
+2f 23x y cmpl-double
+30 23x y cmpg-double
+31 23x y cmp-long
+32 22t n if-eq
+33 22t n if-ne
+34 22t n if-lt
+35 22t n if-ge
+36 22t n if-gt
+37 22t n if-le
+38 21t n if-eqz
+39 21t n if-nez
+3a 21t n if-ltz
+3b 21t n if-gez
+3c 21t n if-gtz
+3d 21t n if-lez
+3e 10x n unused-3e
+3f 10x n unused-3f
+40 10x n unused-40
+41 10x n unused-41
+42 10x n unused-42
+43 10x n unused-43
+44 23x y aget
+45 23x y aget-wide
+46 23x y aget-object
+47 23x y aget-boolean
+48 23x y aget-byte
+49 23x y aget-char
+4a 23x y aget-short
+4b 23x n aput
+4c 23x n aput-wide
+4d 23x n aput-object
+4e 23x n aput-boolean
+4f 23x n aput-byte
+50 23x n aput-char
+51 23x n aput-short
+52 22c y iget
+53 22c y iget-wide
+54 22c y iget-object
+55 22c y iget-boolean
+56 22c y iget-byte
+57 22c y iget-char
+58 22c y iget-short
+59 22c n iput
+5a 22c n iput-wide
+5b 22c n iput-object
+5c 22c n iput-boolean
+5d 22c n iput-byte
+5e 22c n iput-char
+5f 22c n iput-short
+60 21c y sget
+61 21c y sget-wide
+62 21c y sget-object
+63 21c y sget-boolean
+64 21c y sget-byte
+65 21c y sget-char
+66 21c y sget-short
+67 21c n sput
+68 21c n sput-wide
+69 21c n sput-object
+6a 21c n sput-boolean
+6b 21c n sput-byte
+6c 21c n sput-char
+6d 21c n sput-short
+6e 35c n invoke-virtual
+6f 35c n invoke-super
+70 35c n invoke-direct
+71 35c n invoke-static
+72 35c n invoke-interface
+73 10x n unused-73
+74 3rc n invoke-virtual/range
+75 3rc n invoke-super/range
+76 3rc n invoke-direct/range
+77 3rc n invoke-static/range
+78 3rc n invoke-interface/range
+79 10x n unused-79
+7a 10x n unused-7a
+7b 12x y neg-int
+7c 12x y not-int
+7d 12x y neg-long
+7e 12x y not-long
+7f 12x y neg-float
+80 12x y neg-double
+81 12x y int-to-long
+82 12x y int-to-float
+83 12x y int-to-double
+84 12x y long-to-int
+85 12x y long-to-float
+86 12x y long-to-double
+87 12x y float-to-int
+88 12x y float-to-long
+89 12x y float-to-double
+8a 12x y double-to-int
+8b 12x y double-to-long
+8c 12x y double-to-float
+8d 12x y int-to-byte
+8e 12x y int-to-char
+8f 12x y int-to-short
+90 23x y add-int
+91 23x y sub-int
+92 23x y mul-int
+93 23x y div-int
+94 23x y rem-int
+95 23x y and-int
+96 23x y or-int
+97 23x y xor-int
+98 23x y shl-int
+99 23x y shr-int
+9a 23x y ushr-int
+9b 23x y add-long
+9c 23x y sub-long
+9d 23x y mul-long
+9e 23x y div-long
+9f 23x y rem-long
+a0 23x y and-long
+a1 23x y or-long
+a2 23x y xor-long
+a3 23x y shl-long
+a4 23x y shr-long
+a5 23x y ushr-long
+a6 23x y add-float
+a7 23x y sub-float
+a8 23x y mul-float
+a9 23x y div-float
+aa 23x y rem-float
+ab 23x y add-double
+ac 23x y sub-double
+ad 23x y mul-double
+ae 23x y div-double
+af 23x y rem-double
+b0 12x y add-int/2addr
+b1 12x y sub-int/2addr
+b2 12x y mul-int/2addr
+b3 12x y div-int/2addr
+b4 12x y rem-int/2addr
+b5 12x y and-int/2addr
+b6 12x y or-int/2addr
+b7 12x y xor-int/2addr
+b8 12x y shl-int/2addr
+b9 12x y shr-int/2addr
+ba 12x y ushr-int/2addr
+bb 12x y add-long/2addr
+bc 12x y sub-long/2addr
+bd 12x y mul-long/2addr
+be 12x y div-long/2addr
+bf 12x y rem-long/2addr
+c0 12x y and-long/2addr
+c1 12x y or-long/2addr
+c2 12x y xor-long/2addr
+c3 12x y shl-long/2addr
+c4 12x y shr-long/2addr
+c5 12x y ushr-long/2addr
+c6 12x y add-float/2addr
+c7 12x y sub-float/2addr
+c8 12x y mul-float/2addr
+c9 12x y div-float/2addr
+ca 12x y rem-float/2addr
+cb 12x y add-double/2addr
+cc 12x y sub-double/2addr
+cd 12x y mul-double/2addr
+ce 12x y div-double/2addr
+cf 12x y rem-double/2addr
+d0 22s y add-int/lit16
+d1 22s y rsub-int
+d2 22s y mul-int/lit16
+d3 22s y div-int/lit16
+d4 22s y rem-int/lit16
+d5 22s y and-int/lit16
+d6 22s y or-int/lit16
+d7 22s y xor-int/lit16
+d8 22b y add-int/lit8
+d9 22b y rsub-int/lit8
+da 22b y mul-int/lit8
+db 22b y div-int/lit8
+dc 22b y rem-int/lit8
+dd 22b y and-int/lit8
+de 22b y or-int/lit8
+df 22b y xor-int/lit8
+e0 22b y shl-int/lit8
+e1 22b y shr-int/lit8
+e2 22b y ushr-int/lit8
+e3 10x n unused-e3
+e4 10x n unused-e4
+e5 10x n unused-e5
+e6 10x n unused-e6
+e7 10x n unused-e7
+e8 10x n unused-e8
+e9 10x n unused-e9
+ea 10x n unused-ea
+eb 10x n unused-eb
+ec 10x n unused-ec
+ed 10x n unused-ed
+ee 10x n unused-ee
+ef 10x n unused-ef
+f0 10x n unused-f0
+f1 10x n unused-f1
+f2 10x n unused-f2
+f3 10x n unused-f3
+f4 10x n unused-f4
+f5 10x n unused-f5
+f6 10x n unused-f6
+f7 10x n unused-f7
+f8 10x n unused-f8
+f9 10x n unused-f9
+fa 10x n unused-fa
+fb 10x n unused-fb
+fc 10x n unused-fc
+fd 10x n unused-fd
+fe 10x n unused-fe
+ff 10x n unused-ff
diff --git a/dx/etc/dx b/dx/etc/dx
new file mode 100644
index 0000000..e5cedff
--- /dev/null
+++ b/dx/etc/dx
@@ -0,0 +1,89 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+while [ -h "${prog}" ]; do
+ newProg=`/bin/ls -ld "${prog}"`
+ newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+ if expr "x${newProg}" : 'x/' >/dev/null; then
+ prog="${newProg}"
+ else
+ progdir=`dirname "${prog}"`
+ prog="${progdir}/${newProg}"
+ fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+cd "${oldwd}"
+
+jarfile=dx.jar
+libdir="$progdir"
+
+if [ ! -r "$libdir/$jarfile" ]; then
+ # set dx.jar location for the SDK case
+ libdir=`dirname "$progdir"`/platform-tools/lib
+fi
+
+
+if [ ! -r "$libdir/$jarfile" ]; then
+ # set dx.jar location for the Android tree case
+ libdir=`dirname "$progdir"`/framework
+fi
+
+if [ ! -r "$libdir/$jarfile" ]; then
+ echo `basename "$prog"`": can't find $jarfile"
+ exit 1
+fi
+
+# By default, give dx a max heap size of 1 gig. This can be overridden
+# by using a "-J" option (see below).
+defaultMx="-Xmx1024M"
+
+# The following will extract any initial parameters of the form
+# "-J<stuff>" from the command line and pass them to the Java
+# invocation (instead of to dx). This makes it possible for you to add
+# a command-line parameter such as "-JXmx256M" in your scripts, for
+# example. "java" (with no args) and "java -X" give a summary of
+# available options.
+
+javaOpts=""
+
+while expr "x$1" : 'x-J' >/dev/null; do
+ opt=`expr "x$1" : 'x-J\(.*\)'`
+ javaOpts="${javaOpts} -${opt}"
+ if expr "x${opt}" : "xXmx[0-9]" >/dev/null; then
+ defaultMx="no"
+ fi
+ shift
+done
+
+if [ "${defaultMx}" != "no" ]; then
+ javaOpts="${javaOpts} ${defaultMx}"
+fi
+
+if [ "$OSTYPE" = "cygwin" ]; then
+ # For Cygwin, convert the jarfile path into native Windows style.
+ jarpath=`cygpath -w "$libdir/$jarfile"`
+else
+ jarpath="$libdir/$jarfile"
+fi
+
+exec java $javaOpts -jar "$jarpath" "$@"
diff --git a/dx/etc/dx.bat b/dx/etc/dx.bat
new file mode 100755
index 0000000..36b6206
--- /dev/null
+++ b/dx/etc/dx.bat
@@ -0,0 +1,85 @@
+@echo off
+REM Copyright (C) 2007 The Android Open Source Project
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM Unless required by applicable law or agreed to in writing, software
+REM distributed under the License is distributed on an "AS IS" BASIS,
+REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+REM See the License for the specific language governing permissions and
+REM limitations under the License.
+
+REM don't modify the caller's environment
+setlocal
+
+REM Locate dx.jar in the directory where dx.bat was found and start it.
+
+REM Set up prog to be the path of this script, including following symlinks,
+REM and set up progdir to be the fully-qualified pathname of its directory.
+set prog=%~f0
+
+REM Change current directory to where dx is, to avoid issues with directories
+REM containing whitespaces.
+cd /d %~dp0
+
+set jarfile=dx.jar
+set frameworkdir=
+
+if exist %frameworkdir%%jarfile% goto JarFileOk
+ set frameworkdir=lib\
+
+if exist %frameworkdir%%jarfile% goto JarFileOk
+ set frameworkdir=..\framework\
+
+:JarFileOk
+
+set jarpath=%frameworkdir%%jarfile%
+
+set javaOpts=
+set args=
+
+REM By default, give dx a max heap size of 1 gig. This can be overridden
+REM by using a "-JXmx..." option (see below).
+set defaultMx=-Xmx1024M
+
+REM capture all arguments to process them below
+set params=%*
+
+:nextArg
+if "%params%"=="" goto endArgs
+ REM Note: advanced substitions don't work on %1..%N. We need to assign to
+ REM a variable first.
+ REM We also can't use %1..%N directly because an option such as --output=name
+ REM gets automagically converted into %1=--output and %2=name (yes, really!)
+ REM Instead we manually extract the first token from the params variable.
+ for /F "tokens=1*" %%a in ("%params%") do call :getArg "%%a" "%%b"
+
+ if "%defaultMx%"=="" goto notXmx
+ if "%A:~0,5%" NEQ "-JXmx" goto notXmx
+ set defaultMx=
+ :notXmx
+
+ if "%A:~0,2%" NEQ "-J" goto notJ
+ set javaOpts=%javaOpts% -%A:~2%
+ goto nextArg
+
+ :notJ
+ set args=%args% %A%
+ goto nextArg
+
+:getArg
+ REM this subroutine is called by the for /F with the first argument of params
+ REM and the rest of the line. The "goto :eof" actually exits the subroutine.
+ set A=%~1
+ set params=%~2
+ goto :eof
+
+:endArgs
+
+set javaOpts=%javaOpts% %defaultMx%
+
+call java %javaOpts% -Djava.ext.dirs=%frameworkdir% -jar %jarpath% %args%
diff --git a/dx/etc/jasmin b/dx/etc/jasmin
new file mode 100644
index 0000000..f44c16f
--- /dev/null
+++ b/dx/etc/jasmin
@@ -0,0 +1,39 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+while [ -h "${prog}" ]; do
+ newProg=`/bin/ls -ld "${prog}"`
+ newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+ if expr "x${newProg}" : 'x/' >/dev/null; then
+ prog="${newProg}"
+ else
+ progdir=`dirname "${prog}"`
+ prog="${progdir}/${newProg}"
+ fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+cd "${oldwd}"
+
+libdir=`dirname $progdir`/framework
+
+exec java -jar $libdir/jasmin.jar "$@"
diff --git a/dx/etc/jasmin.jar b/dx/etc/jasmin.jar
new file mode 100644
index 0000000..87db0d0
--- /dev/null
+++ b/dx/etc/jasmin.jar
Binary files differ
diff --git a/dx/etc/manifest.txt b/dx/etc/manifest.txt
new file mode 100644
index 0000000..46bbe63
--- /dev/null
+++ b/dx/etc/manifest.txt
@@ -0,0 +1 @@
+Main-Class: com.android.dx.command.Main
diff --git a/dx/etc/opcode-gen b/dx/etc/opcode-gen
new file mode 100755
index 0000000..390a6c3
--- /dev/null
+++ b/dx/etc/opcode-gen
@@ -0,0 +1,141 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# opcode-gen <file>
+#
+# Use the file bytecodes.txt to generate code inside <file>, based on
+# the directives found in that file:
+#
+# opcodes: static final ints for each opcode
+# dops: static final objects for each opcode
+# dops-init: initialization code for the "dops"
+
+file="$1"
+tmpfile="/tmp/$$.txt"
+
+if [ "x$1" = "x" ]; then
+ echo "must specify a file"
+ exit 1
+fi
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+while [ -h "${prog}" ]; do
+ newProg=`/bin/ls -ld "${prog}"`
+ newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+ if expr "x${newProg}" : 'x/' >/dev/null; then
+ prog="${newProg}"
+ else
+ progdir=`dirname "${prog}"`
+ prog="${progdir}/${newProg}"
+ fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+cd "${oldwd}"
+
+bytecodeFile="$progdir/bytecode.txt"
+
+awk -v "bytecodeFile=$bytecodeFile" '
+
+BEGIN {
+ readBytecodes();
+ consumeUntil = "";
+}
+
+consumeUntil != "" {
+ if (index($0, consumeUntil) != 0) {
+ consumeUntil = "";
+ } else {
+ next;
+ }
+}
+
+/BEGIN\(opcodes\)/ {
+ consumeUntil = "END(opcodes)";
+ print;
+
+ for (i = 0; i < 256; i++) {
+ printf(" public static final int %s = 0x%s;\n",
+ uppername[i], hex[i]);
+ }
+
+ next;
+}
+
+/BEGIN\(dops\)/ {
+ consumeUntil = "END(dops)";
+ print;
+
+ for (i = 0; i < 256; i++) {
+ if (index(name[i], "unused") != 0) {
+ continue;
+ }
+ printf(" public static final Dop %s =\n" \
+ " new Dop(DalvOps.%s, DalvOps.%s,\n" \
+ " Form%s.THE_ONE, %s, \"%s\");\n\n",
+ uppername[i], uppername[i], family[i], format[i], hasres[i],
+ name[i]);
+ }
+
+ next;
+}
+
+/BEGIN\(dops-init\)/ {
+ consumeUntil = "END(dops-init)";
+ print;
+
+ for (i = 0; i < 256; i++) {
+ if (index(name[i], "unused") != 0) {
+ continue;
+ }
+ printf(" set(%s);\n", uppername[i]);
+ }
+
+ next;
+}
+
+{ print; }
+
+function readBytecodes(i, parts) {
+ for (i = 0; i < 256; i++) {
+ $0 = "";
+ while (($0 == "") || (index($0, "#") != 0)) {
+ if ((getline <bytecodeFile) != 1) {
+ print "trouble reading bytecode file";
+ exit 1;
+ }
+ }
+ split($0, parts);
+ hex[i] = parts[1];
+ format[i] = parts[2];
+ hasres[i] = (parts[3] == "n") ? "false" : "true";
+ name[i] = parts[4];
+ uppername[i] = toupper(parts[4]);
+ gsub("[---/]", "_", uppername[i]);
+ split(name[i], parts, "/");
+ family[i] = toupper(parts[1]);
+ gsub("-", "_", family[i]);
+ }
+}
+' "$file" > "$tmpfile"
+
+cp "$tmpfile" "$file"
+rm "$tmpfile"
diff --git a/dx/etc/run-opcode-gen b/dx/etc/run-opcode-gen
new file mode 100755
index 0000000..061c048
--- /dev/null
+++ b/dx/etc/run-opcode-gen
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Run this from this directory.
+
+./opcode-gen ../src/com/android/dx/dex/code/DalvOps.java
+./opcode-gen ../src/com/android/dx/dex/code/Dops.java
diff --git a/dx/src/Android.mk b/dx/src/Android.mk
new file mode 100644
index 0000000..80d1b85
--- /dev/null
+++ b/dx/src/Android.mk
@@ -0,0 +1,34 @@
+# Copyright 2006 The Android Open Source Project
+#
+LOCAL_PATH := $(call my-dir)
+
+# This tool is prebuilt if we're doing an app-only build.
+ifeq ($(TARGET_BUILD_APPS),)
+
+# dx java library
+# ============================================================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_JAR_MANIFEST := ../etc/manifest.txt
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE:= dx
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+INTERNAL_DALVIK_MODULES += $(LOCAL_INSTALLED_MODULE)
+
+endif # TARGET_BUILD_APPS
+
+# the documentation
+# ============================================================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files) $(call all-subdir-html-files)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE:= dx
+LOCAL_DROIDDOC_OPTIONS := -hidden
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+LOCAL_IS_HOST_MODULE := true
+
+include $(BUILD_DROIDDOC)
diff --git a/dx/src/com/android/dx/Version.java b/dx/src/com/android/dx/Version.java
new file mode 100644
index 0000000..b22173d
--- /dev/null
+++ b/dx/src/com/android/dx/Version.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx;
+
+/**
+ * Version number for dx.
+ */
+public class Version {
+ /** {@code non-null;} version string */
+ public static final String VERSION = "1.5";
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttAnnotationDefault.java b/dx/src/com/android/dx/cf/attrib/AttAnnotationDefault.java
new file mode 100644
index 0000000..fe0b3ab
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttAnnotationDefault.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.cst.Constant;
+
+/**
+ * Attribute class for {@code AnnotationDefault} attributes.
+ */
+public final class AttAnnotationDefault extends BaseAttribute {
+ /** {@code non-null;} attribute name for attributes of this type */
+ public static final String ATTRIBUTE_NAME = "AnnotationDefault";
+
+ /** {@code non-null;} the annotation default value */
+ private final Constant value;
+
+ /** {@code >= 0;} attribute data length in the original classfile (not
+ * including the attribute header) */
+ private final int byteLength;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param value {@code non-null;} the annotation default value
+ * @param byteLength {@code >= 0;} attribute data length in the original
+ * classfile (not including the attribute header)
+ */
+ public AttAnnotationDefault(Constant value, int byteLength) {
+ super(ATTRIBUTE_NAME);
+
+ if (value == null) {
+ throw new NullPointerException("value == null");
+ }
+
+ this.value = value;
+ this.byteLength = byteLength;
+ }
+
+ /** {@inheritDoc} */
+ public int byteLength() {
+ // Add six for the standard attribute header.
+ return byteLength + 6;
+ }
+
+ /**
+ * Gets the annotation default value.
+ *
+ * @return {@code non-null;} the value
+ */
+ public Constant getValue() {
+ return value;
+ }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttCode.java b/dx/src/com/android/dx/cf/attrib/AttCode.java
new file mode 100644
index 0000000..8d34c69
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttCode.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.cf.code.ByteCatchList;
+import com.android.dx.cf.code.BytecodeArray;
+import com.android.dx.cf.iface.AttributeList;
+import com.android.dx.util.MutabilityException;
+
+/**
+ * Attribute class for standard {@code Code} attributes.
+ */
+public final class AttCode extends BaseAttribute {
+ /** {@code non-null;} attribute name for attributes of this type */
+ public static final String ATTRIBUTE_NAME = "Code";
+
+ /** {@code >= 0;} the stack size */
+ private final int maxStack;
+
+ /** {@code >= 0;} the number of locals */
+ private final int maxLocals;
+
+ /** {@code non-null;} array containing the bytecode per se */
+ private final BytecodeArray code;
+
+ /** {@code non-null;} the exception table */
+ private final ByteCatchList catches;
+
+ /** {@code non-null;} the associated list of attributes */
+ private final AttributeList attributes;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param maxStack {@code >= 0;} the stack size
+ * @param maxLocals {@code >= 0;} the number of locals
+ * @param code {@code non-null;} array containing the bytecode per se
+ * @param catches {@code non-null;} the exception table
+ * @param attributes {@code non-null;} the associated list of attributes
+ */
+ public AttCode(int maxStack, int maxLocals, BytecodeArray code,
+ ByteCatchList catches, AttributeList attributes) {
+ super(ATTRIBUTE_NAME);
+
+ if (maxStack < 0) {
+ throw new IllegalArgumentException("maxStack < 0");
+ }
+
+ if (maxLocals < 0) {
+ throw new IllegalArgumentException("maxLocals < 0");
+ }
+
+ if (code == null) {
+ throw new NullPointerException("code == null");
+ }
+
+ try {
+ if (catches.isMutable()) {
+ throw new MutabilityException("catches.isMutable()");
+ }
+ } catch (NullPointerException ex) {
+ // Translate the exception.
+ throw new NullPointerException("catches == null");
+ }
+
+ try {
+ if (attributes.isMutable()) {
+ throw new MutabilityException("attributes.isMutable()");
+ }
+ } catch (NullPointerException ex) {
+ // Translate the exception.
+ throw new NullPointerException("attributes == null");
+ }
+
+ this.maxStack = maxStack;
+ this.maxLocals = maxLocals;
+ this.code = code;
+ this.catches = catches;
+ this.attributes = attributes;
+ }
+
+ public int byteLength() {
+ return 10 + code.byteLength() + catches.byteLength() +
+ attributes.byteLength();
+ }
+
+ /**
+ * Gets the maximum stack size.
+ *
+ * @return {@code >= 0;} the maximum stack size
+ */
+ public int getMaxStack() {
+ return maxStack;
+ }
+
+ /**
+ * Gets the number of locals.
+ *
+ * @return {@code >= 0;} the number of locals
+ */
+ public int getMaxLocals() {
+ return maxLocals;
+ }
+
+ /**
+ * Gets the bytecode array.
+ *
+ * @return {@code non-null;} the bytecode array
+ */
+ public BytecodeArray getCode() {
+ return code;
+ }
+
+ /**
+ * Gets the exception table.
+ *
+ * @return {@code non-null;} the exception table
+ */
+ public ByteCatchList getCatches() {
+ return catches;
+ }
+
+ /**
+ * Gets the associated attribute list.
+ *
+ * @return {@code non-null;} the attribute list
+ */
+ public AttributeList getAttributes() {
+ return attributes;
+ }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttConstantValue.java b/dx/src/com/android/dx/cf/attrib/AttConstantValue.java
new file mode 100644
index 0000000..aa6d1b3
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttConstantValue.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.cst.CstDouble;
+import com.android.dx.rop.cst.CstFloat;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.CstLong;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.TypedConstant;
+
+/**
+ * Attribute class for standard {@code ConstantValue} attributes.
+ */
+public final class AttConstantValue extends BaseAttribute {
+ /** {@code non-null;} attribute name for attributes of this type */
+ public static final String ATTRIBUTE_NAME = "ConstantValue";
+
+ /** {@code non-null;} the constant value */
+ private final TypedConstant constantValue;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param constantValue {@code non-null;} the constant value, which must
+ * be an instance of one of: {@code CstString},
+ * {@code CstInteger}, {@code CstLong},
+ * {@code CstFloat}, or {@code CstDouble}
+ */
+ public AttConstantValue(TypedConstant constantValue) {
+ super(ATTRIBUTE_NAME);
+
+ if (!((constantValue instanceof CstString) ||
+ (constantValue instanceof CstInteger) ||
+ (constantValue instanceof CstLong) ||
+ (constantValue instanceof CstFloat) ||
+ (constantValue instanceof CstDouble))) {
+ if (constantValue == null) {
+ throw new NullPointerException("constantValue == null");
+ }
+ throw new IllegalArgumentException("bad type for constantValue");
+ }
+
+ this.constantValue = constantValue;
+ }
+
+ /** {@inheritDoc} */
+ public int byteLength() {
+ return 8;
+ }
+
+ /**
+ * Gets the constant value of this instance. The returned value
+ * is an instance of one of: {@code CstString},
+ * {@code CstInteger}, {@code CstLong},
+ * {@code CstFloat}, or {@code CstDouble}.
+ *
+ * @return {@code non-null;} the constant value
+ */
+ public TypedConstant getConstantValue() {
+ return constantValue;
+ }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttDeprecated.java b/dx/src/com/android/dx/cf/attrib/AttDeprecated.java
new file mode 100644
index 0000000..d440aae
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttDeprecated.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+/**
+ * Attribute class for standard {@code Deprecated} attributes.
+ */
+public final class AttDeprecated extends BaseAttribute {
+ /** {@code non-null;} attribute name for attributes of this type */
+ public static final String ATTRIBUTE_NAME = "Deprecated";
+
+ /**
+ * Constructs an instance.
+ */
+ public AttDeprecated() {
+ super(ATTRIBUTE_NAME);
+ }
+
+ /** {@inheritDoc} */
+ public int byteLength() {
+ return 6;
+ }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttEnclosingMethod.java b/dx/src/com/android/dx/cf/attrib/AttEnclosingMethod.java
new file mode 100644
index 0000000..6717e15
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttEnclosingMethod.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+
+/**
+ * Attribute class for standards-track {@code EnclosingMethod}
+ * attributes.
+ */
+public final class AttEnclosingMethod extends BaseAttribute {
+ /** {@code non-null;} attribute name for attributes of this type */
+ public static final String ATTRIBUTE_NAME = "EnclosingMethod";
+
+ /** {@code non-null;} the innermost enclosing class */
+ private final CstType type;
+
+ /** {@code null-ok;} the name-and-type of the innermost enclosing method, if any */
+ private final CstNat method;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param type {@code non-null;} the innermost enclosing class
+ * @param method {@code null-ok;} the name-and-type of the innermost enclosing
+ * method, if any
+ */
+ public AttEnclosingMethod(CstType type, CstNat method) {
+ super(ATTRIBUTE_NAME);
+
+ if (type == null) {
+ throw new NullPointerException("type == null");
+ }
+
+ this.type = type;
+ this.method = method;
+ }
+
+ /** {@inheritDoc} */
+ public int byteLength() {
+ return 10;
+ }
+
+ /**
+ * Gets the innermost enclosing class.
+ *
+ * @return {@code non-null;} the innermost enclosing class
+ */
+ public CstType getEnclosingClass() {
+ return type;
+ }
+
+ /**
+ * Gets the name-and-type of the innermost enclosing method, if
+ * any.
+ *
+ * @return {@code null-ok;} the name-and-type of the innermost enclosing
+ * method, if any
+ */
+ public CstNat getMethod() {
+ return method;
+ }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttExceptions.java b/dx/src/com/android/dx/cf/attrib/AttExceptions.java
new file mode 100644
index 0000000..a17e009
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttExceptions.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.MutabilityException;
+
+/**
+ * Attribute class for standard {@code Exceptions} attributes.
+ */
+public final class AttExceptions extends BaseAttribute {
+ /** {@code non-null;} attribute name for attributes of this type */
+ public static final String ATTRIBUTE_NAME = "Exceptions";
+
+ /** {@code non-null;} list of exception classes */
+ private final TypeList exceptions;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param exceptions {@code non-null;} list of classes, presumed but not
+ * verified to be subclasses of {@code Throwable}
+ */
+ public AttExceptions(TypeList exceptions) {
+ super(ATTRIBUTE_NAME);
+
+ try {
+ if (exceptions.isMutable()) {
+ throw new MutabilityException("exceptions.isMutable()");
+ }
+ } catch (NullPointerException ex) {
+ // Translate the exception.
+ throw new NullPointerException("exceptions == null");
+ }
+
+ this.exceptions = exceptions;
+ }
+
+ /** {@inheritDoc} */
+ public int byteLength() {
+ return 8 + exceptions.size() * 2;
+ }
+
+ /**
+ * Gets the list of classes associated with this instance. In
+ * general, these classes are not pre-verified to be subclasses of
+ * {@code Throwable}.
+ *
+ * @return {@code non-null;} the list of classes
+ */
+ public TypeList getExceptions() {
+ return exceptions;
+ }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttInnerClasses.java b/dx/src/com/android/dx/cf/attrib/AttInnerClasses.java
new file mode 100644
index 0000000..77a4b08
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttInnerClasses.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.util.MutabilityException;
+
+/**
+ * Attribute class for standard {@code InnerClasses} attributes.
+ */
+public final class AttInnerClasses extends BaseAttribute {
+ /** {@code non-null;} attribute name for attributes of this type */
+ public static final String ATTRIBUTE_NAME = "InnerClasses";
+
+ /** {@code non-null;} list of inner class entries */
+ private final InnerClassList innerClasses;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param innerClasses {@code non-null;} list of inner class entries
+ */
+ public AttInnerClasses(InnerClassList innerClasses) {
+ super(ATTRIBUTE_NAME);
+
+ try {
+ if (innerClasses.isMutable()) {
+ throw new MutabilityException("innerClasses.isMutable()");
+ }
+ } catch (NullPointerException ex) {
+ // Translate the exception.
+ throw new NullPointerException("innerClasses == null");
+ }
+
+ this.innerClasses = innerClasses;
+ }
+
+ /** {@inheritDoc} */
+ public int byteLength() {
+ return 8 + innerClasses.size() * 8;
+ }
+
+ /**
+ * Gets the list of "inner class" entries associated with this instance.
+ *
+ * @return {@code non-null;} the list
+ */
+ public InnerClassList getInnerClasses() {
+ return innerClasses;
+ }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttLineNumberTable.java b/dx/src/com/android/dx/cf/attrib/AttLineNumberTable.java
new file mode 100644
index 0000000..5eac8cb
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttLineNumberTable.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.cf.code.LineNumberList;
+import com.android.dx.util.MutabilityException;
+
+/**
+ * Attribute class for standard {@code LineNumberTable} attributes.
+ */
+public final class AttLineNumberTable extends BaseAttribute {
+ /** {@code non-null;} attribute name for attributes of this type */
+ public static final String ATTRIBUTE_NAME = "LineNumberTable";
+
+ /** {@code non-null;} list of line number entries */
+ private final LineNumberList lineNumbers;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param lineNumbers {@code non-null;} list of line number entries
+ */
+ public AttLineNumberTable(LineNumberList lineNumbers) {
+ super(ATTRIBUTE_NAME);
+
+ try {
+ if (lineNumbers.isMutable()) {
+ throw new MutabilityException("lineNumbers.isMutable()");
+ }
+ } catch (NullPointerException ex) {
+ // Translate the exception.
+ throw new NullPointerException("lineNumbers == null");
+ }
+
+ this.lineNumbers = lineNumbers;
+ }
+
+ /** {@inheritDoc} */
+ public int byteLength() {
+ return 8 + 4 * lineNumbers.size();
+ }
+
+ /**
+ * Gets the list of "line number" entries associated with this instance.
+ *
+ * @return {@code non-null;} the list
+ */
+ public LineNumberList getLineNumbers() {
+ return lineNumbers;
+ }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttLocalVariableTable.java b/dx/src/com/android/dx/cf/attrib/AttLocalVariableTable.java
new file mode 100644
index 0000000..1d2b4aa
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttLocalVariableTable.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.cf.code.LocalVariableList;
+
+/**
+ * Attribute class for standard {@code LocalVariableTable} attributes.
+ */
+public final class AttLocalVariableTable extends BaseLocalVariables {
+ /** {@code non-null;} attribute name for attributes of this type */
+ public static final String ATTRIBUTE_NAME = "LocalVariableTable";
+
+ /**
+ * Constructs an instance.
+ *
+ * @param localVariables {@code non-null;} list of local variable entries
+ */
+ public AttLocalVariableTable(LocalVariableList localVariables) {
+ super(ATTRIBUTE_NAME, localVariables);
+ }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttLocalVariableTypeTable.java b/dx/src/com/android/dx/cf/attrib/AttLocalVariableTypeTable.java
new file mode 100644
index 0000000..2520bf6
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttLocalVariableTypeTable.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.cf.code.LocalVariableList;
+
+/**
+ * Attribute class for standard {@code LocalVariableTypeTable} attributes.
+ */
+public final class AttLocalVariableTypeTable extends BaseLocalVariables {
+ /** {@code non-null;} attribute name for attributes of this type */
+ public static final String ATTRIBUTE_NAME = "LocalVariableTypeTable";
+
+ /**
+ * Constructs an instance.
+ *
+ * @param localVariables {@code non-null;} list of local variable entries
+ */
+ public AttLocalVariableTypeTable(LocalVariableList localVariables) {
+ super(ATTRIBUTE_NAME, localVariables);
+ }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttRuntimeInvisibleAnnotations.java b/dx/src/com/android/dx/cf/attrib/AttRuntimeInvisibleAnnotations.java
new file mode 100644
index 0000000..d3afe27
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttRuntimeInvisibleAnnotations.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.annotation.Annotations;
+
+/**
+ * Attribute class for standard {@code RuntimeInvisibleAnnotations}
+ * attributes.
+ */
+public final class AttRuntimeInvisibleAnnotations extends BaseAnnotations {
+ /** {@code non-null;} attribute name for attributes of this type */
+ public static final String ATTRIBUTE_NAME = "RuntimeInvisibleAnnotations";
+
+ /**
+ * Constructs an instance.
+ *
+ * @param annotations {@code non-null;} the list of annotations
+ * @param byteLength {@code >= 0;} attribute data length in the original
+ * classfile (not including the attribute header)
+ */
+ public AttRuntimeInvisibleAnnotations(Annotations annotations,
+ int byteLength) {
+ super(ATTRIBUTE_NAME, annotations, byteLength);
+ }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttRuntimeInvisibleParameterAnnotations.java b/dx/src/com/android/dx/cf/attrib/AttRuntimeInvisibleParameterAnnotations.java
new file mode 100644
index 0000000..c9c5136
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttRuntimeInvisibleParameterAnnotations.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.annotation.AnnotationsList;
+
+/**
+ * Attribute class for standard
+ * {@code RuntimeInvisibleParameterAnnotations} attributes.
+ */
+public final class AttRuntimeInvisibleParameterAnnotations
+ extends BaseParameterAnnotations {
+ /** {@code non-null;} attribute name for attributes of this type */
+ public static final String ATTRIBUTE_NAME =
+ "RuntimeInvisibleParameterAnnotations";
+
+ /**
+ * Constructs an instance.
+ *
+ * @param parameterAnnotations {@code non-null;} the parameter annotations
+ * @param byteLength {@code >= 0;} attribute data length in the original
+ * classfile (not including the attribute header)
+ */
+ public AttRuntimeInvisibleParameterAnnotations(
+ AnnotationsList parameterAnnotations, int byteLength) {
+ super(ATTRIBUTE_NAME, parameterAnnotations, byteLength);
+ }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttRuntimeVisibleAnnotations.java b/dx/src/com/android/dx/cf/attrib/AttRuntimeVisibleAnnotations.java
new file mode 100644
index 0000000..a6a640d
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttRuntimeVisibleAnnotations.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.annotation.Annotations;
+
+/**
+ * Attribute class for standard {@code RuntimeVisibleAnnotations}
+ * attributes.
+ */
+public final class AttRuntimeVisibleAnnotations extends BaseAnnotations {
+ /** {@code non-null;} attribute name for attributes of this type */
+ public static final String ATTRIBUTE_NAME = "RuntimeVisibleAnnotations";
+
+ /**
+ * Constructs an instance.
+ *
+ * @param annotations {@code non-null;} the list of annotations
+ * @param byteLength {@code >= 0;} attribute data length in the original
+ * classfile (not including the attribute header)
+ */
+ public AttRuntimeVisibleAnnotations(Annotations annotations,
+ int byteLength) {
+ super(ATTRIBUTE_NAME, annotations, byteLength);
+ }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttRuntimeVisibleParameterAnnotations.java b/dx/src/com/android/dx/cf/attrib/AttRuntimeVisibleParameterAnnotations.java
new file mode 100644
index 0000000..177eb4c
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttRuntimeVisibleParameterAnnotations.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.annotation.AnnotationsList;
+
+/**
+ * Attribute class for standard {@code RuntimeVisibleParameterAnnotations}
+ * attributes.
+ */
+public final class AttRuntimeVisibleParameterAnnotations
+ extends BaseParameterAnnotations {
+ /** {@code non-null;} attribute name for attributes of this type */
+ public static final String ATTRIBUTE_NAME =
+ "RuntimeVisibleParameterAnnotations";
+
+ /**
+ * Constructs an instance.
+ *
+ * @param annotations {@code non-null;} the parameter annotations
+ * @param byteLength {@code >= 0;} attribute data length in the original
+ * classfile (not including the attribute header)
+ */
+ public AttRuntimeVisibleParameterAnnotations(
+ AnnotationsList annotations, int byteLength) {
+ super(ATTRIBUTE_NAME, annotations, byteLength);
+ }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttSignature.java b/dx/src/com/android/dx/cf/attrib/AttSignature.java
new file mode 100644
index 0000000..f6023f3
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttSignature.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.cst.CstUtf8;
+
+/**
+ * Attribute class for standards-track {@code Signature} attributes.
+ */
+public final class AttSignature extends BaseAttribute {
+ /** {@code non-null;} attribute name for attributes of this type */
+ public static final String ATTRIBUTE_NAME = "Signature";
+
+ /** {@code non-null;} the signature string */
+ private final CstUtf8 signature;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param signature {@code non-null;} the signature string
+ */
+ public AttSignature(CstUtf8 signature) {
+ super(ATTRIBUTE_NAME);
+
+ if (signature == null) {
+ throw new NullPointerException("signature == null");
+ }
+
+ this.signature = signature;
+ }
+
+ /** {@inheritDoc} */
+ public int byteLength() {
+ return 8;
+ }
+
+ /**
+ * Gets the signature string.
+ *
+ * @return {@code non-null;} the signature string
+ */
+ public CstUtf8 getSignature() {
+ return signature;
+ }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttSourceFile.java b/dx/src/com/android/dx/cf/attrib/AttSourceFile.java
new file mode 100644
index 0000000..b84ff4d
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttSourceFile.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.cst.CstUtf8;
+
+/**
+ * Attribute class for standard {@code SourceFile} attributes.
+ */
+public final class AttSourceFile extends BaseAttribute {
+ /** {@code non-null;} attribute name for attributes of this type */
+ public static final String ATTRIBUTE_NAME = "SourceFile";
+
+ /** {@code non-null;} name of the source file */
+ private final CstUtf8 sourceFile;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param sourceFile {@code non-null;} the name of the source file
+ */
+ public AttSourceFile(CstUtf8 sourceFile) {
+ super(ATTRIBUTE_NAME);
+
+ if (sourceFile == null) {
+ throw new NullPointerException("sourceFile == null");
+ }
+
+ this.sourceFile = sourceFile;
+ }
+
+ /** {@inheritDoc} */
+ public int byteLength() {
+ return 8;
+ }
+
+ /**
+ * Gets the source file name of this instance.
+ *
+ * @return {@code non-null;} the source file
+ */
+ public CstUtf8 getSourceFile() {
+ return sourceFile;
+ }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/AttSynthetic.java b/dx/src/com/android/dx/cf/attrib/AttSynthetic.java
new file mode 100644
index 0000000..e3841eb
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/AttSynthetic.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+/**
+ * Attribute class for standard {@code Synthetic} attributes.
+ */
+public final class AttSynthetic extends BaseAttribute {
+ /** {@code non-null;} attribute name for attributes of this type */
+ public static final String ATTRIBUTE_NAME = "Synthetic";
+
+ /**
+ * Constructs an instance.
+ */
+ public AttSynthetic() {
+ super(ATTRIBUTE_NAME);
+ }
+
+ /** {@inheritDoc} */
+ public int byteLength() {
+ return 6;
+ }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/BaseAnnotations.java b/dx/src/com/android/dx/cf/attrib/BaseAnnotations.java
new file mode 100644
index 0000000..bc138af
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/BaseAnnotations.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.annotation.Annotations;
+import com.android.dx.util.MutabilityException;
+
+/**
+ * Base class for annotations attributes.
+ */
+public abstract class BaseAnnotations extends BaseAttribute {
+ /** {@code non-null;} list of annotations */
+ private final Annotations annotations;
+
+ /** {@code >= 0;} attribute data length in the original classfile (not
+ * including the attribute header) */
+ private final int byteLength;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param attributeName {@code non-null;} the name of the attribute
+ * @param annotations {@code non-null;} the list of annotations
+ * @param byteLength {@code >= 0;} attribute data length in the original
+ * classfile (not including the attribute header)
+ */
+ public BaseAnnotations(String attributeName, Annotations annotations,
+ int byteLength) {
+ super(attributeName);
+
+ try {
+ if (annotations.isMutable()) {
+ throw new MutabilityException("annotations.isMutable()");
+ }
+ } catch (NullPointerException ex) {
+ // Translate the exception.
+ throw new NullPointerException("annotations == null");
+ }
+
+ this.annotations = annotations;
+ this.byteLength = byteLength;
+ }
+
+ /** {@inheritDoc} */
+ public final int byteLength() {
+ // Add six for the standard attribute header.
+ return byteLength + 6;
+ }
+
+ /**
+ * Gets the list of annotations associated with this instance.
+ *
+ * @return {@code non-null;} the list
+ */
+ public final Annotations getAnnotations() {
+ return annotations;
+ }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/BaseAttribute.java b/dx/src/com/android/dx/cf/attrib/BaseAttribute.java
new file mode 100644
index 0000000..9961725
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/BaseAttribute.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.cf.iface.Attribute;
+
+/**
+ * Base implementation of {@link Attribute}, which directly stores
+ * the attribute name but leaves the rest up to subclasses.
+ */
+public abstract class BaseAttribute implements Attribute {
+ /** {@code non-null;} attribute name */
+ private final String name;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param name {@code non-null;} attribute name
+ */
+ public BaseAttribute(String name) {
+ if (name == null) {
+ throw new NullPointerException("name == null");
+ }
+
+ this.name = name;
+ }
+
+ /** {@inheritDoc} */
+ public String getName() {
+ return name;
+ }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/BaseLocalVariables.java b/dx/src/com/android/dx/cf/attrib/BaseLocalVariables.java
new file mode 100644
index 0000000..27cd6fb
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/BaseLocalVariables.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.cf.code.LocalVariableList;
+import com.android.dx.util.MutabilityException;
+
+/**
+ * Base attribute class for standard {@code LocalVariableTable}
+ * and {@code LocalVariableTypeTable} attributes.
+ */
+public abstract class BaseLocalVariables extends BaseAttribute {
+ /** {@code non-null;} list of local variable entries */
+ private final LocalVariableList localVariables;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param name {@code non-null;} attribute name
+ * @param localVariables {@code non-null;} list of local variable entries
+ */
+ public BaseLocalVariables(String name,
+ LocalVariableList localVariables) {
+ super(name);
+
+ try {
+ if (localVariables.isMutable()) {
+ throw new MutabilityException("localVariables.isMutable()");
+ }
+ } catch (NullPointerException ex) {
+ // Translate the exception.
+ throw new NullPointerException("localVariables == null");
+ }
+
+ this.localVariables = localVariables;
+ }
+
+ /** {@inheritDoc} */
+ public final int byteLength() {
+ return 8 + localVariables.size() * 10;
+ }
+
+ /**
+ * Gets the list of "local variable" entries associated with this instance.
+ *
+ * @return {@code non-null;} the list
+ */
+ public final LocalVariableList getLocalVariables() {
+ return localVariables;
+ }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/BaseParameterAnnotations.java b/dx/src/com/android/dx/cf/attrib/BaseParameterAnnotations.java
new file mode 100644
index 0000000..791f8cd
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/BaseParameterAnnotations.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.annotation.AnnotationsList;
+import com.android.dx.util.MutabilityException;
+
+/**
+ * Base class for parameter annotation list attributes.
+ */
+public abstract class BaseParameterAnnotations extends BaseAttribute {
+ /** {@code non-null;} list of annotations */
+ private final AnnotationsList parameterAnnotations;
+
+ /** {@code >= 0;} attribute data length in the original classfile (not
+ * including the attribute header) */
+ private final int byteLength;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param attributeName {@code non-null;} the name of the attribute
+ * @param parameterAnnotations {@code non-null;} the annotations
+ * @param byteLength {@code >= 0;} attribute data length in the original
+ * classfile (not including the attribute header)
+ */
+ public BaseParameterAnnotations(String attributeName,
+ AnnotationsList parameterAnnotations, int byteLength) {
+ super(attributeName);
+
+ try {
+ if (parameterAnnotations.isMutable()) {
+ throw new MutabilityException(
+ "parameterAnnotations.isMutable()");
+ }
+ } catch (NullPointerException ex) {
+ // Translate the exception.
+ throw new NullPointerException("parameterAnnotations == null");
+ }
+
+ this.parameterAnnotations = parameterAnnotations;
+ this.byteLength = byteLength;
+ }
+
+ /** {@inheritDoc} */
+ public final int byteLength() {
+ // Add six for the standard attribute header.
+ return byteLength + 6;
+ }
+
+ /**
+ * Gets the list of annotation lists associated with this instance.
+ *
+ * @return {@code non-null;} the list
+ */
+ public final AnnotationsList getParameterAnnotations() {
+ return parameterAnnotations;
+ }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/InnerClassList.java b/dx/src/com/android/dx/cf/attrib/InnerClassList.java
new file mode 100644
index 0000000..96e1b60
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/InnerClassList.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * List of "inner class" entries, which are the contents of
+ * {@code InnerClasses} attributes.
+ */
+public final class InnerClassList extends FixedSizeList {
+ /**
+ * Constructs an instance.
+ *
+ * @param count the number of elements to be in the list of inner classes
+ */
+ public InnerClassList(int count) {
+ super(count);
+ }
+
+ /**
+ * Gets the indicated item.
+ *
+ * @param n {@code >= 0;} which item
+ * @return {@code null-ok;} the indicated item
+ */
+ public Item get(int n) {
+ return (Item) get0(n);
+ }
+
+ /**
+ * Sets the item at the given index.
+ *
+ * @param n {@code >= 0, < size();} which class
+ * @param innerClass {@code non-null;} class this item refers to
+ * @param outerClass {@code null-ok;} outer class that this class is a
+ * member of, if any
+ * @param innerName {@code null-ok;} original simple name of this class,
+ * if not anonymous
+ * @param accessFlags original declared access flags
+ */
+ public void set(int n, CstType innerClass, CstType outerClass,
+ CstUtf8 innerName, int accessFlags) {
+ set0(n, new Item(innerClass, outerClass, innerName, accessFlags));
+ }
+
+ /**
+ * Item in an inner classes list.
+ */
+ public static class Item {
+ /** {@code non-null;} class this item refers to */
+ private final CstType innerClass;
+
+ /** {@code null-ok;} outer class that this class is a member of, if any */
+ private final CstType outerClass;
+
+ /** {@code null-ok;} original simple name of this class, if not anonymous */
+ private final CstUtf8 innerName;
+
+ /** original declared access flags */
+ private final int accessFlags;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param innerClass {@code non-null;} class this item refers to
+ * @param outerClass {@code null-ok;} outer class that this class is a
+ * member of, if any
+ * @param innerName {@code null-ok;} original simple name of this
+ * class, if not anonymous
+ * @param accessFlags original declared access flags
+ */
+ public Item(CstType innerClass, CstType outerClass,
+ CstUtf8 innerName, int accessFlags) {
+ if (innerClass == null) {
+ throw new NullPointerException("innerClass == null");
+ }
+
+ this.innerClass = innerClass;
+ this.outerClass = outerClass;
+ this.innerName = innerName;
+ this.accessFlags = accessFlags;
+ }
+
+ /**
+ * Gets the class this item refers to.
+ *
+ * @return {@code non-null;} the class
+ */
+ public CstType getInnerClass() {
+ return innerClass;
+ }
+
+ /**
+ * Gets the outer class that this item's class is a member of, if any.
+ *
+ * @return {@code null-ok;} the class
+ */
+ public CstType getOuterClass() {
+ return outerClass;
+ }
+
+ /**
+ * Gets the original name of this item's class, if not anonymous.
+ *
+ * @return {@code null-ok;} the name
+ */
+ public CstUtf8 getInnerName() {
+ return innerName;
+ }
+
+ /**
+ * Gets the original declared access flags.
+ *
+ * @return the access flags
+ */
+ public int getAccessFlags() {
+ return accessFlags;
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/RawAttribute.java b/dx/src/com/android/dx/cf/attrib/RawAttribute.java
new file mode 100644
index 0000000..e905dd1
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/RawAttribute.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.attrib;
+
+import com.android.dx.rop.cst.ConstantPool;
+import com.android.dx.util.ByteArray;
+
+/**
+ * Raw attribute, for holding onto attributes that are unrecognized.
+ */
+public final class RawAttribute extends BaseAttribute {
+ /** {@code non-null;} attribute data */
+ private final ByteArray data;
+
+ /**
+ * {@code null-ok;} constant pool to use for resolution of cpis in {@link
+ * #data}
+ */
+ private final ConstantPool pool;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param name {@code non-null;} attribute name
+ * @param data {@code non-null;} attribute data
+ * @param pool {@code null-ok;} constant pool to use for cpi resolution
+ */
+ public RawAttribute(String name, ByteArray data, ConstantPool pool) {
+ super(name);
+
+ if (data == null) {
+ throw new NullPointerException("data == null");
+ }
+
+ this.data = data;
+ this.pool = pool;
+ }
+
+ /**
+ * Constructs an instance from a sub-array of a {@link ByteArray}.
+ *
+ * @param name {@code non-null;} attribute name
+ * @param data {@code non-null;} array containing the attribute data
+ * @param offset offset in {@code data} to the attribute data
+ * @param length length of the attribute data, in bytes
+ * @param pool {@code null-ok;} constant pool to use for cpi resolution
+ */
+ public RawAttribute(String name, ByteArray data, int offset,
+ int length, ConstantPool pool) {
+ this(name, data.slice(offset, offset + length), pool);
+ }
+
+ /**
+ * Get the raw data of the attribute.
+ *
+ * @return {@code non-null;} the data
+ */
+ public ByteArray getData() {
+ return data;
+ }
+
+ /** {@inheritDoc} */
+ public int byteLength() {
+ return data.size() + 6;
+ }
+
+ /**
+ * Gets the constant pool to use for cpi resolution, if any. It
+ * presumably came from the class file that this attribute came
+ * from.
+ *
+ * @return {@code null-ok;} the constant pool
+ */
+ public ConstantPool getPool() {
+ return pool;
+ }
+}
diff --git a/dx/src/com/android/dx/cf/attrib/package.html b/dx/src/com/android/dx/cf/attrib/package.html
new file mode 100644
index 0000000..8125079
--- /dev/null
+++ b/dx/src/com/android/dx/cf/attrib/package.html
@@ -0,0 +1,11 @@
+<body>
+<p>Implementation of containers and utilities for all the standard Java
+attribute types.</p>
+
+<p><b>PACKAGES USED:</b>
+<ul>
+<li><code>com.android.dx.cf.iface</code></li>
+<li><code>com.android.dx.rop.pool</code></li>
+<li><code>com.android.dx.util</code></li>
+</ul>
+</body>
diff --git a/dx/src/com/android/dx/cf/code/BaseMachine.java b/dx/src/com/android/dx/cf/code/BaseMachine.java
new file mode 100644
index 0000000..aae6056
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/BaseMachine.java
@@ -0,0 +1,545 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.LocalItem;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.type.Prototype;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import java.util.ArrayList;
+
+/**
+ * Base implementation of {@link Machine}.
+ *
+ * <p><b>Note:</b> For the most part, the documentation for this class
+ * ignores the distinction between {@link Type} and {@link
+ * TypeBearer}.</p>
+ */
+public abstract class BaseMachine implements Machine {
+ /* {@code non-null;} the prototype for the associated method */
+ private final Prototype prototype;
+
+ /** {@code non-null;} primary arguments */
+ private TypeBearer[] args;
+
+ /** {@code >= 0;} number of primary arguments */
+ private int argCount;
+
+ /** {@code null-ok;} type of the operation, if salient */
+ private Type auxType;
+
+ /** auxiliary {@code int} argument */
+ private int auxInt;
+
+ /** {@code null-ok;} auxiliary constant argument */
+ private Constant auxCst;
+
+ /** auxiliary branch target argument */
+ private int auxTarget;
+
+ /** {@code null-ok;} auxiliary switch cases argument */
+ private SwitchList auxCases;
+
+ /** {@code null-ok;} auxiliary initial value list for newarray */
+ private ArrayList<Constant> auxInitValues;
+
+ /** {@code >= -1;} last local accessed */
+ private int localIndex;
+
+ /** {@code null-ok;} local target spec, if salient and calculated */
+ private RegisterSpec localTarget;
+
+ /** {@code non-null;} results */
+ private TypeBearer[] results;
+
+ /**
+ * {@code >= -1;} count of the results, or {@code -1} if no results
+ * have been set
+ */
+ private int resultCount;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param prototype {@code non-null;} the prototype for the associated method
+ */
+ public BaseMachine(Prototype prototype) {
+ if (prototype == null) {
+ throw new NullPointerException("prototype == null");
+ }
+
+ this.prototype = prototype;
+ args = new TypeBearer[10];
+ results = new TypeBearer[6];
+ clearArgs();
+ }
+
+ /** {@inheritDoc} */
+ public Prototype getPrototype() {
+ return prototype;
+ }
+
+ /** {@inheritDoc} */
+ public final void clearArgs() {
+ argCount = 0;
+ auxType = null;
+ auxInt = 0;
+ auxCst = null;
+ auxTarget = 0;
+ auxCases = null;
+ auxInitValues = null;
+ localIndex = -1;
+ localTarget = null;
+ resultCount = -1;
+ }
+
+ /** {@inheritDoc} */
+ public final void popArgs(Frame frame, int count) {
+ ExecutionStack stack = frame.getStack();
+
+ clearArgs();
+
+ if (count > args.length) {
+ // Grow args, and add a little extra room to grow even more.
+ args = new TypeBearer[count + 10];
+ }
+
+ for (int i = count - 1; i >= 0; i--) {
+ args[i] = stack.pop();
+ }
+
+ argCount = count;
+ }
+
+ /** {@inheritDoc} */
+ public void popArgs(Frame frame, Prototype prototype) {
+ StdTypeList types = prototype.getParameterTypes();
+ int size = types.size();
+
+ // Use the above method to do the actual popping...
+ popArgs(frame, size);
+
+ // ...and then verify the popped types.
+
+ for (int i = 0; i < size; i++) {
+ if (! Merger.isPossiblyAssignableFrom(types.getType(i), args[i])) {
+ throw new SimException("at stack depth " + (size - 1 - i) +
+ ", expected type " + types.getType(i).toHuman() +
+ " but found " + args[i].getType().toHuman());
+ }
+ }
+ }
+
+ public final void popArgs(Frame frame, Type type) {
+ // Use the above method to do the actual popping...
+ popArgs(frame, 1);
+
+ // ...and then verify the popped type.
+ if (! Merger.isPossiblyAssignableFrom(type, args[0])) {
+ throw new SimException("expected type " + type.toHuman() +
+ " but found " + args[0].getType().toHuman());
+ }
+ }
+
+ /** {@inheritDoc} */
+ public final void popArgs(Frame frame, Type type1, Type type2) {
+ // Use the above method to do the actual popping...
+ popArgs(frame, 2);
+
+ // ...and then verify the popped types.
+
+ if (! Merger.isPossiblyAssignableFrom(type1, args[0])) {
+ throw new SimException("expected type " + type1.toHuman() +
+ " but found " + args[0].getType().toHuman());
+ }
+
+ if (! Merger.isPossiblyAssignableFrom(type2, args[1])) {
+ throw new SimException("expected type " + type2.toHuman() +
+ " but found " + args[1].getType().toHuman());
+ }
+ }
+
+ /** {@inheritDoc} */
+ public final void popArgs(Frame frame, Type type1, Type type2,
+ Type type3) {
+ // Use the above method to do the actual popping...
+ popArgs(frame, 3);
+
+ // ...and then verify the popped types.
+
+ if (! Merger.isPossiblyAssignableFrom(type1, args[0])) {
+ throw new SimException("expected type " + type1.toHuman() +
+ " but found " + args[0].getType().toHuman());
+ }
+
+ if (! Merger.isPossiblyAssignableFrom(type2, args[1])) {
+ throw new SimException("expected type " + type2.toHuman() +
+ " but found " + args[1].getType().toHuman());
+ }
+
+ if (! Merger.isPossiblyAssignableFrom(type3, args[2])) {
+ throw new SimException("expected type " + type2.toHuman() +
+ " but found " + args[2].getType().toHuman());
+ }
+ }
+
+ /** {@inheritDoc} */
+ public final void localArg(Frame frame, int idx) {
+ clearArgs();
+ args[0] = frame.getLocals().get(idx);
+ argCount = 1;
+ localIndex = idx;
+ }
+
+ /** {@inheritDoc} */
+ public final void auxType(Type type) {
+ auxType = type;
+ }
+
+ /** {@inheritDoc} */
+ public final void auxIntArg(int value) {
+ auxInt = value;
+ }
+
+ /** {@inheritDoc} */
+ public final void auxCstArg(Constant cst) {
+ if (cst == null) {
+ throw new NullPointerException("cst == null");
+ }
+
+ auxCst = cst;
+ }
+
+ /** {@inheritDoc} */
+ public final void auxTargetArg(int target) {
+ auxTarget = target;
+ }
+
+ /** {@inheritDoc} */
+ public final void auxSwitchArg(SwitchList cases) {
+ if (cases == null) {
+ throw new NullPointerException("cases == null");
+ }
+
+ auxCases = cases;
+ }
+
+ /** {@inheritDoc} */
+ public final void auxInitValues(ArrayList<Constant> initValues) {
+ auxInitValues = initValues;
+ }
+
+ /** {@inheritDoc} */
+ public final void localTarget(int idx, Type type, LocalItem local) {
+ localTarget = RegisterSpec.makeLocalOptional(idx, type, local);
+ }
+
+ /**
+ * Gets the number of primary arguments.
+ *
+ * @return {@code >= 0;} the number of primary arguments
+ */
+ protected final int argCount() {
+ return argCount;
+ }
+
+ /**
+ * Gets the width of the arguments (where a category-2 value counts as
+ * two).
+ *
+ * @return {@code >= 0;} the argument width
+ */
+ protected final int argWidth() {
+ int result = 0;
+
+ for (int i = 0; i < argCount; i++) {
+ result += args[i].getType().getCategory();
+ }
+
+ return result;
+ }
+
+ /**
+ * Gets the {@code n}th primary argument.
+ *
+ * @param n {@code >= 0, < argCount();} which argument
+ * @return {@code non-null;} the indicated argument
+ */
+ protected final TypeBearer arg(int n) {
+ if (n >= argCount) {
+ throw new IllegalArgumentException("n >= argCount");
+ }
+
+ try {
+ return args[n];
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ // Translate the exception.
+ throw new IllegalArgumentException("n < 0");
+ }
+ }
+
+ /**
+ * Gets the type auxiliary argument.
+ *
+ * @return {@code null-ok;} the salient type
+ */
+ protected final Type getAuxType() {
+ return auxType;
+ }
+
+ /**
+ * Gets the {@code int} auxiliary argument.
+ *
+ * @return the argument value
+ */
+ protected final int getAuxInt() {
+ return auxInt;
+ }
+
+ /**
+ * Gets the constant auxiliary argument.
+ *
+ * @return {@code null-ok;} the argument value
+ */
+ protected final Constant getAuxCst() {
+ return auxCst;
+ }
+
+ /**
+ * Gets the branch target auxiliary argument.
+ *
+ * @return the argument value
+ */
+ protected final int getAuxTarget() {
+ return auxTarget;
+ }
+
+ /**
+ * Gets the switch cases auxiliary argument.
+ *
+ * @return {@code null-ok;} the argument value
+ */
+ protected final SwitchList getAuxCases() {
+ return auxCases;
+ }
+
+ /**
+ * Gets the init values auxiliary argument.
+ *
+ * @return {@code null-ok;} the argument value
+ */
+ protected final ArrayList<Constant> getInitValues() {
+ return auxInitValues;
+ }
+ /**
+ * Gets the last local index accessed.
+ *
+ * @return {@code >= -1;} the salient local index or {@code -1} if none
+ * was set since the last time {@link #clearArgs} was called
+ */
+ protected final int getLocalIndex() {
+ return localIndex;
+ }
+
+ /**
+ * Gets the target local register spec of the current operation, if any.
+ * The local target spec is the combination of the values indicated
+ * by a previous call to {@link #localTarget} with the type of what
+ * should be the sole result set by a call to {@link #setResult} (or
+ * the combination {@link #clearResult} then {@link #addResult}.
+ *
+ * @return {@code null-ok;} the salient register spec or {@code null} if no
+ * local target was set since the last time {@link #clearArgs} was
+ * called
+ */
+ protected final RegisterSpec getLocalTarget() {
+ if (localTarget == null) {
+ return null;
+ }
+
+ if (resultCount != 1) {
+ throw new SimException("local target with " +
+ ((resultCount == 0) ? "no" : "multiple") + " results");
+ }
+
+ TypeBearer result = results[0];
+ Type resultType = result.getType();
+ Type localType = localTarget.getType();
+
+ if (resultType == localType) {
+ return localTarget;
+ }
+
+ if (! Merger.isPossiblyAssignableFrom(localType, resultType)) {
+ // The result and local types are inconsistent. Complain!
+ throwLocalMismatch(resultType, localType);
+ return null;
+ }
+
+ if (localType == Type.OBJECT) {
+ /*
+ * The result type is more specific than the local type,
+ * so use that instead.
+ */
+ localTarget = localTarget.withType(result);
+ }
+
+ return localTarget;
+ }
+
+ /**
+ * Clears the results.
+ */
+ protected final void clearResult() {
+ resultCount = 0;
+ }
+
+ /**
+ * Sets the results list to be the given single value.
+ *
+ * <p><b>Note:</b> If there is more than one result value, the
+ * others may be added by using {@link #addResult}.</p>
+ *
+ * @param result {@code non-null;} result value
+ */
+ protected final void setResult(TypeBearer result) {
+ if (result == null) {
+ throw new NullPointerException("result == null");
+ }
+
+ results[0] = result;
+ resultCount = 1;
+ }
+
+ /**
+ * Adds an additional element to the list of results.
+ *
+ * @see #setResult
+ *
+ * @param result {@code non-null;} result value
+ */
+ protected final void addResult(TypeBearer result) {
+ if (result == null) {
+ throw new NullPointerException("result == null");
+ }
+
+ results[resultCount] = result;
+ resultCount++;
+ }
+
+ /**
+ * Gets the count of results. This throws an exception if results were
+ * never set. (Explicitly clearing the results counts as setting them.)
+ *
+ * @return {@code >= 0;} the count
+ */
+ protected final int resultCount() {
+ if (resultCount < 0) {
+ throw new SimException("results never set");
+ }
+
+ return resultCount;
+ }
+
+ /**
+ * Gets the width of the results (where a category-2 value counts as
+ * two).
+ *
+ * @return {@code >= 0;} the result width
+ */
+ protected final int resultWidth() {
+ int width = 0;
+
+ for (int i = 0; i < resultCount; i++) {
+ width += results[i].getType().getCategory();
+ }
+
+ return width;
+ }
+
+ /**
+ * Gets the {@code n}th result value.
+ *
+ * @param n {@code >= 0, < resultCount();} which result
+ * @return {@code non-null;} the indicated result value
+ */
+ protected final TypeBearer result(int n) {
+ if (n >= resultCount) {
+ throw new IllegalArgumentException("n >= resultCount");
+ }
+
+ try {
+ return results[n];
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ // Translate the exception.
+ throw new IllegalArgumentException("n < 0");
+ }
+ }
+
+ /**
+ * Stores the results of the latest operation into the given frame. If
+ * there is a local target (see {@link #localTarget}), then the sole
+ * result is stored to that target; otherwise any results are pushed
+ * onto the stack.
+ *
+ * @param frame {@code non-null;} frame to operate on
+ */
+ protected final void storeResults(Frame frame) {
+ if (resultCount < 0) {
+ throw new SimException("results never set");
+ }
+
+ if (resultCount == 0) {
+ // Nothing to do.
+ return;
+ }
+
+ if (localTarget != null) {
+ /*
+ * Note: getLocalTarget() doesn't necessarily return
+ * localTarget directly.
+ */
+ frame.getLocals().set(getLocalTarget());
+ } else {
+ ExecutionStack stack = frame.getStack();
+ for (int i = 0; i < resultCount; i++) {
+ stack.push(results[i]);
+ }
+ }
+ }
+
+ /**
+ * Throws an exception that indicates a mismatch in local variable
+ * types.
+ *
+ * @param found {@code non-null;} the encountered type
+ * @param local {@code non-null;} the local variable's claimed type
+ */
+ public static void throwLocalMismatch(TypeBearer found,
+ TypeBearer local) {
+ throw new SimException("local variable type mismatch: " +
+ "attempt to set or access a value of type " +
+ found.toHuman() +
+ " using a local variable of type " +
+ local.toHuman() +
+ ". This is symptomatic of .class transformation tools " +
+ "that ignore local variable information.");
+ }
+}
diff --git a/dx/src/com/android/dx/cf/code/BasicBlocker.java b/dx/src/com/android/dx/cf/code/BasicBlocker.java
new file mode 100644
index 0000000..8fb9560
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/BasicBlocker.java
@@ -0,0 +1,452 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstMemberRef;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.Bits;
+import com.android.dx.util.IntList;
+import java.util.ArrayList;
+
+/**
+ * Utility that identifies basic blocks in bytecode.
+ */
+public final class BasicBlocker implements BytecodeArray.Visitor {
+ /** {@code non-null;} method being converted */
+ private final ConcreteMethod method;
+
+ /**
+ * {@code non-null;} work set; bits indicate offsets in need of
+ * examination
+ */
+ private final int[] workSet;
+
+ /**
+ * {@code non-null;} live set; bits indicate potentially-live
+ * opcodes; contrawise, a bit that isn't on is either in the
+ * middle of an instruction or is a definitely-dead opcode
+ */
+ private final int[] liveSet;
+
+ /**
+ * {@code non-null;} block start set; bits indicate the starts of
+ * basic blocks, including the opcodes that start blocks of
+ * definitely-dead code
+ */
+ private final int[] blockSet;
+
+ /**
+ * {@code non-null, sparse;} for each instruction offset to a branch of
+ * some sort, the list of targets for that instruction
+ */
+ private final IntList[] targetLists;
+
+ /**
+ * {@code non-null, sparse;} for each instruction offset to a throwing
+ * instruction, the list of exception handlers for that instruction
+ */
+ private final ByteCatchList[] catchLists;
+
+ /** offset of the previously parsed bytecode */
+ private int previousOffset;
+
+ /**
+ * Identifies and enumerates the basic blocks in the given method,
+ * returning a list of them. The returned list notably omits any
+ * definitely-dead code that is identified in the process.
+ *
+ * @param method {@code non-null;} method to convert
+ * @return {@code non-null;} list of basic blocks
+ */
+ public static ByteBlockList identifyBlocks(ConcreteMethod method) {
+ BasicBlocker bb = new BasicBlocker(method);
+
+ bb.doit();
+ return bb.getBlockList();
+ }
+
+ /**
+ * Constructs an instance. This class is not publicly instantiable; use
+ * {@link #identifyBlocks}.
+ *
+ * @param method {@code non-null;} method to convert
+ */
+ private BasicBlocker(ConcreteMethod method) {
+ if (method == null) {
+ throw new NullPointerException("method == null");
+ }
+
+ this.method = method;
+
+ /*
+ * The "+1" below is so the idx-past-end is also valid,
+ * avoiding a special case, but without preventing
+ * flow-of-control falling past the end of the method from
+ * getting properly reported.
+ */
+ int sz = method.getCode().size() + 1;
+
+ workSet = Bits.makeBitSet(sz);
+ liveSet = Bits.makeBitSet(sz);
+ blockSet = Bits.makeBitSet(sz);
+ targetLists = new IntList[sz];
+ catchLists = new ByteCatchList[sz];
+ previousOffset = -1;
+ }
+
+ /*
+ * Note: These methods are defined implementation of the interface
+ * BytecodeArray.Visitor; since the class isn't publicly
+ * instantiable, no external code ever gets a chance to actually
+ * call these methods.
+ */
+
+ /** {@inheritDoc} */
+ public void visitInvalid(int opcode, int offset, int length) {
+ visitCommon(offset, length, true);
+ }
+
+ /** {@inheritDoc} */
+ public void visitNoArgs(int opcode, int offset, int length, Type type) {
+ switch (opcode) {
+ case ByteOps.IRETURN:
+ case ByteOps.RETURN: {
+ visitCommon(offset, length, false);
+ targetLists[offset] = IntList.EMPTY;
+ break;
+ }
+ case ByteOps.ATHROW: {
+ visitCommon(offset, length, false);
+ visitThrowing(offset, length, false);
+ break;
+ }
+ case ByteOps.IALOAD:
+ case ByteOps.LALOAD:
+ case ByteOps.FALOAD:
+ case ByteOps.DALOAD:
+ case ByteOps.AALOAD:
+ case ByteOps.BALOAD:
+ case ByteOps.CALOAD:
+ case ByteOps.SALOAD:
+ case ByteOps.IASTORE:
+ case ByteOps.LASTORE:
+ case ByteOps.FASTORE:
+ case ByteOps.DASTORE:
+ case ByteOps.AASTORE:
+ case ByteOps.BASTORE:
+ case ByteOps.CASTORE:
+ case ByteOps.SASTORE:
+ case ByteOps.ARRAYLENGTH:
+ case ByteOps.MONITORENTER:
+ case ByteOps.MONITOREXIT: {
+ /*
+ * These instructions can all throw, so they have to end
+ * the block they appear in (since throws are branches).
+ */
+ visitCommon(offset, length, true);
+ visitThrowing(offset, length, true);
+ break;
+ }
+ case ByteOps.IDIV:
+ case ByteOps.IREM: {
+ /*
+ * The int and long versions of division and remainder may
+ * throw, but not the other types.
+ */
+ visitCommon(offset, length, true);
+ if ((type == Type.INT) || (type == Type.LONG)) {
+ visitThrowing(offset, length, true);
+ }
+ break;
+ }
+ default: {
+ visitCommon(offset, length, true);
+ break;
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void visitLocal(int opcode, int offset, int length,
+ int idx, Type type, int value) {
+ if (opcode == ByteOps.RET) {
+ visitCommon(offset, length, false);
+ targetLists[offset] = IntList.EMPTY;
+ } else {
+ visitCommon(offset, length, true);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void visitConstant(int opcode, int offset, int length,
+ Constant cst, int value) {
+ visitCommon(offset, length, true);
+
+ if ((cst instanceof CstMemberRef) || (cst instanceof CstType) ||
+ (cst instanceof CstString)) {
+ /*
+ * Instructions with these sorts of constants have the
+ * possibility of throwing, so this instruction needs to
+ * end its block (since it can throw, and possible-throws
+ * are branch points).
+ */
+ visitThrowing(offset, length, true);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void visitBranch(int opcode, int offset, int length,
+ int target) {
+ switch (opcode) {
+ case ByteOps.GOTO: {
+ visitCommon(offset, length, false);
+ targetLists[offset] = IntList.makeImmutable(target);
+ break;
+ }
+ case ByteOps.JSR: {
+ /*
+ * Each jsr is quarantined into a separate block (containing
+ * only the jsr instruction) but is otherwise treated
+ * as a conditional branch. (That is to say, both its
+ * target and next instruction begin new blocks.)
+ */
+ addWorkIfNecessary(offset, true);
+ // Fall through to next case...
+ }
+ default: {
+ int next = offset + length;
+ visitCommon(offset, length, true);
+ addWorkIfNecessary(next, true);
+ targetLists[offset] = IntList.makeImmutable(next, target);
+ break;
+ }
+ }
+
+ addWorkIfNecessary(target, true);
+ }
+
+ /** {@inheritDoc} */
+ public void visitSwitch(int opcode, int offset, int length,
+ SwitchList cases, int padding) {
+ visitCommon(offset, length, false);
+ addWorkIfNecessary(cases.getDefaultTarget(), true);
+
+ int sz = cases.size();
+ for (int i = 0; i < sz; i++) {
+ addWorkIfNecessary(cases.getTarget(i), true);
+ }
+
+ targetLists[offset] = cases.getTargets();
+ }
+
+ /** {@inheritDoc} */
+ public void visitNewarray(int offset, int length, CstType type,
+ ArrayList<Constant> intVals) {
+ visitCommon(offset, length, true);
+ visitThrowing(offset, length, true);
+ }
+
+ /**
+ * Extracts the list of basic blocks from the bit sets.
+ *
+ * @return {@code non-null;} the list of basic blocks
+ */
+ private ByteBlockList getBlockList() {
+ BytecodeArray bytes = method.getCode();
+ ByteBlock[] bbs = new ByteBlock[bytes.size()];
+ int count = 0;
+
+ for (int at = 0, next; /*at*/; at = next) {
+ next = Bits.findFirst(blockSet, at + 1);
+ if (next < 0) {
+ break;
+ }
+
+ if (Bits.get(liveSet, at)) {
+ /*
+ * Search backward for the branch or throwing
+ * instruction at the end of this block, if any. If
+ * there isn't any, then "next" is the sole target.
+ */
+ IntList targets = null;
+ int targetsAt = -1;
+ ByteCatchList blockCatches;
+
+ for (int i = next - 1; i >= at; i--) {
+ targets = targetLists[i];
+ if (targets != null) {
+ targetsAt = i;
+ break;
+ }
+ }
+
+ if (targets == null) {
+ targets = IntList.makeImmutable(next);
+ blockCatches = ByteCatchList.EMPTY;
+ } else {
+ blockCatches = catchLists[targetsAt];
+ if (blockCatches == null) {
+ blockCatches = ByteCatchList.EMPTY;
+ }
+ }
+
+ bbs[count] =
+ new ByteBlock(at, at, next, targets, blockCatches);
+ count++;
+ }
+ }
+
+ ByteBlockList result = new ByteBlockList(count);
+ for (int i = 0; i < count; i++) {
+ result.set(i, bbs[i]);
+ }
+
+ return result;
+ }
+
+ /**
+ * Does basic block identification.
+ */
+ private void doit() {
+ BytecodeArray bytes = method.getCode();
+ ByteCatchList catches = method.getCatches();
+ int catchSz = catches.size();
+
+ /*
+ * Start by setting offset 0 as the start of a block and in need
+ * of work...
+ */
+ Bits.set(workSet, 0);
+ Bits.set(blockSet, 0);
+
+ /*
+ * And then process the work set, add new work based on
+ * exception ranges that are active, and iterate until there's
+ * nothing left to work on.
+ */
+ while (!Bits.isEmpty(workSet)) {
+ try {
+ bytes.processWorkSet(workSet, this);
+ } catch (IllegalArgumentException ex) {
+ // Translate the exception.
+ throw new SimException("flow of control falls off " +
+ "end of method",
+ ex);
+ }
+
+ for (int i = 0; i < catchSz; i++) {
+ ByteCatchList.Item item = catches.get(i);
+ int start = item.getStartPc();
+ int end = item.getEndPc();
+ if (Bits.anyInRange(liveSet, start, end)) {
+ Bits.set(blockSet, start);
+ Bits.set(blockSet, end);
+ addWorkIfNecessary(item.getHandlerPc(), true);
+ }
+ }
+ }
+ }
+
+ /**
+ * Sets a bit in the work set, but only if the instruction in question
+ * isn't yet known to be possibly-live.
+ *
+ * @param offset offset to the instruction in question
+ * @param blockStart {@code true} iff this instruction starts a
+ * basic block
+ */
+ private void addWorkIfNecessary(int offset, boolean blockStart) {
+ if (!Bits.get(liveSet, offset)) {
+ Bits.set(workSet, offset);
+ }
+
+ if (blockStart) {
+ Bits.set(blockSet, offset);
+ }
+ }
+
+ /**
+ * Helper method used by all the visitor methods.
+ *
+ * @param offset offset to the instruction
+ * @param length length of the instruction, in bytes
+ * @param nextIsLive {@code true} iff the instruction after
+ * the indicated one is possibly-live (because this one isn't an
+ * unconditional branch, a return, or a switch)
+ */
+ private void visitCommon(int offset, int length, boolean nextIsLive) {
+ Bits.set(liveSet, offset);
+
+ if (nextIsLive) {
+ /*
+ * If the next instruction is flowed to by this one, just
+ * add it to the work set, and then a subsequent visit*()
+ * will deal with it as appropriate.
+ */
+ addWorkIfNecessary(offset + length, false);
+ } else {
+ /*
+ * If the next instruction isn't flowed to by this one,
+ * then mark it as a start of a block but *don't* add it
+ * to the work set, so that in the final phase we can know
+ * dead code blocks as those marked as blocks but not also marked
+ * live.
+ */
+ Bits.set(blockSet, offset + length);
+ }
+ }
+
+ /**
+ * Helper method used by all the visitor methods that deal with
+ * opcodes that possibly throw. This method should be called after calling
+ * {@link #visitCommon}.
+ *
+ * @param offset offset to the instruction
+ * @param length length of the instruction, in bytes
+ * @param nextIsLive {@code true} iff the instruction after
+ * the indicated one is possibly-live (because this one isn't an
+ * unconditional throw)
+ */
+ private void visitThrowing(int offset, int length, boolean nextIsLive) {
+ int next = offset + length;
+
+ if (nextIsLive) {
+ addWorkIfNecessary(next, true);
+ }
+
+ ByteCatchList catches = method.getCatches().listFor(offset);
+ catchLists[offset] = catches;
+ targetLists[offset] = catches.toTargetList(nextIsLive ? next : -1);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setPreviousOffset(int offset) {
+ previousOffset = offset;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int getPreviousOffset() {
+ return previousOffset;
+ }
+}
diff --git a/dx/src/com/android/dx/cf/code/ByteBlock.java b/dx/src/com/android/dx/cf/code/ByteBlock.java
new file mode 100644
index 0000000..73bbbab
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/ByteBlock.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.util.Hex;
+import com.android.dx.util.IntList;
+import com.android.dx.util.LabeledItem;
+
+/**
+ * Representation of a basic block in a bytecode array.
+ */
+public final class ByteBlock implements LabeledItem {
+ /** {@code >= 0;} label for this block */
+ private final int label;
+
+ /** {@code >= 0;} bytecode offset (inclusive) of the start of the block */
+ private final int start;
+
+ /** {@code > start;} bytecode offset (exclusive) of the end of the block */
+ private final int end;
+
+ /** {@code non-null;} list of successors that this block may branch to */
+ private final IntList successors;
+
+ /** {@code non-null;} list of exceptions caught and their handler targets */
+ private final ByteCatchList catches;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param label {@code >= 0;} target label for this block
+ * @param start {@code >= 0;} bytecode offset (inclusive) of the start
+ * of the block
+ * @param end {@code > start;} bytecode offset (exclusive) of the end
+ * of the block
+ * @param successors {@code non-null;} list of successors that this block may
+ * branch to
+ * @param catches {@code non-null;} list of exceptions caught and their
+ * handler targets
+ */
+ public ByteBlock(int label, int start, int end, IntList successors,
+ ByteCatchList catches) {
+ if (label < 0) {
+ throw new IllegalArgumentException("label < 0");
+ }
+
+ if (start < 0) {
+ throw new IllegalArgumentException("start < 0");
+ }
+
+ if (end <= start) {
+ throw new IllegalArgumentException("end <= start");
+ }
+
+ if (successors == null) {
+ throw new NullPointerException("targets == null");
+ }
+
+ int sz = successors.size();
+ for (int i = 0; i < sz; i++) {
+ if (successors.get(i) < 0) {
+ throw new IllegalArgumentException("successors[" + i +
+ "] == " +
+ successors.get(i));
+ }
+ }
+
+ if (catches == null) {
+ throw new NullPointerException("catches == null");
+ }
+
+ this.label = label;
+ this.start = start;
+ this.end = end;
+ this.successors = successors;
+ this.catches = catches;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return '{' + Hex.u2(label) + ": " + Hex.u2(start) + ".." +
+ Hex.u2(end) + '}';
+ }
+
+ /**
+ * Gets the label of this block.
+ *
+ * @return {@code >= 0;} the label
+ */
+ public int getLabel() {
+ return label;
+ }
+
+ /**
+ * Gets the bytecode offset (inclusive) of the start of this block.
+ *
+ * @return {@code >= 0;} the start offset
+ */
+ public int getStart() {
+ return start;
+ }
+
+ /**
+ * Gets the bytecode offset (exclusive) of the end of this block.
+ *
+ * @return {@code > getStart();} the end offset
+ */
+ public int getEnd() {
+ return end;
+ }
+
+ /**
+ * Gets the list of successors that this block may branch to
+ * non-exceptionally.
+ *
+ * @return {@code non-null;} the successor list
+ */
+ public IntList getSuccessors() {
+ return successors;
+ }
+
+ /**
+ * Gets the list of exceptions caught and their handler targets.
+ *
+ * @return {@code non-null;} the catch list
+ */
+ public ByteCatchList getCatches() {
+ return catches;
+ }
+}
diff --git a/dx/src/com/android/dx/cf/code/ByteBlockList.java b/dx/src/com/android/dx/cf/code/ByteBlockList.java
new file mode 100644
index 0000000..412dfc3
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/ByteBlockList.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.util.FixedSizeList;
+import com.android.dx.util.Hex;
+import com.android.dx.util.LabeledList;
+
+/**
+ * List of {@link ByteBlock} instances.
+ */
+public final class ByteBlockList extends LabeledList {
+
+ /**
+ * Constructs an instance.
+ *
+ * @param size {@code >= 0;} the number of elements to be in the list
+ */
+ public ByteBlockList(int size) {
+ super(size);
+ }
+
+ /**
+ * Gets the indicated element. It is an error to call this with the
+ * index for an element which was never set; if you do that, this
+ * will throw {@code NullPointerException}.
+ *
+ * @param n {@code >= 0, < size();} which element
+ * @return {@code non-null;} the indicated element
+ */
+ public ByteBlock get(int n) {
+ return (ByteBlock) get0(n);
+ }
+
+ /**
+ * Gets the block with the given label.
+ *
+ * @param label the label to look for
+ * @return {@code non-null;} the block with the given label
+ */
+ public ByteBlock labelToBlock(int label) {
+ int idx = indexOfLabel(label);
+
+ if (idx < 0) {
+ throw new IllegalArgumentException("no such label: "
+ + Hex.u2(label));
+ }
+
+ return get(idx);
+ }
+
+ /**
+ * Sets the element at the given index.
+ *
+ * @param n {@code >= 0, < size();} which element
+ * @param bb {@code null-ok;} the value to store
+ */
+ public void set(int n, ByteBlock bb) {
+ super.set(n, bb);
+ }
+}
diff --git a/dx/src/com/android/dx/cf/code/ByteCatchList.java b/dx/src/com/android/dx/cf/code/ByteCatchList.java
new file mode 100644
index 0000000..36c37af
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/ByteCatchList.java
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.FixedSizeList;
+import com.android.dx.util.IntList;
+
+/**
+ * List of catch entries, that is, the elements of an "exception table,"
+ * which is part of a standard {@code Code} attribute.
+ */
+public final class ByteCatchList extends FixedSizeList {
+ /** {@code non-null;} convenient zero-entry instance */
+ public static final ByteCatchList EMPTY = new ByteCatchList(0);
+
+ /**
+ * Constructs an instance.
+ *
+ * @param count the number of elements to be in the table
+ */
+ public ByteCatchList(int count) {
+ super(count);
+ }
+
+ /**
+ * Gets the total length of this structure in bytes, when included in
+ * a {@code Code} attribute. The returned value includes the
+ * two bytes for {@code exception_table_length}.
+ *
+ * @return {@code >= 2;} the total length, in bytes
+ */
+ public int byteLength() {
+ return 2 + size() * 8;
+ }
+
+ /**
+ * Gets the indicated item.
+ *
+ * @param n {@code >= 0;} which item
+ * @return {@code null-ok;} the indicated item
+ */
+ public Item get(int n) {
+ return (Item) get0(n);
+ }
+
+ /**
+ * Sets the item at the given index.
+ *
+ * @param n {@code >= 0, < size();} which entry to set
+ * @param item {@code non-null;} the item
+ */
+ public void set(int n, Item item) {
+ if (item == null) {
+ throw new NullPointerException("item == null");
+ }
+
+ set0(n, item);
+ }
+
+ /**
+ * Sets the item at the given index.
+ *
+ * @param n {@code >= 0, < size();} which entry to set
+ * @param startPc {@code >= 0;} the start pc (inclusive) of the handler's range
+ * @param endPc {@code >= startPc;} the end pc (exclusive) of the
+ * handler's range
+ * @param handlerPc {@code >= 0;} the pc of the exception handler
+ * @param exceptionClass {@code null-ok;} the exception class or
+ * {@code null} to catch all exceptions with this handler
+ */
+ public void set(int n, int startPc, int endPc, int handlerPc,
+ CstType exceptionClass) {
+ set0(n, new Item(startPc, endPc, handlerPc, exceptionClass));
+ }
+
+ /**
+ * Gets the list of items active at the given address. The result is
+ * automatically made immutable.
+ *
+ * @param pc which address
+ * @return {@code non-null;} list of exception handlers active at
+ * {@code pc}
+ */
+ public ByteCatchList listFor(int pc) {
+ int sz = size();
+ Item[] resultArr = new Item[sz];
+ int resultSz = 0;
+
+ for (int i = 0; i < sz; i++) {
+ Item one = get(i);
+ if (one.covers(pc) && typeNotFound(one, resultArr, resultSz)) {
+ resultArr[resultSz] = one;
+ resultSz++;
+ }
+ }
+
+ if (resultSz == 0) {
+ return EMPTY;
+ }
+
+ ByteCatchList result = new ByteCatchList(resultSz);
+ for (int i = 0; i < resultSz; i++) {
+ result.set(i, resultArr[i]);
+ }
+
+ result.setImmutable();
+ return result;
+ }
+
+ /**
+ * Helper method for {@link #listFor}, which tells whether a match
+ * is <i>not</i> found for the exception type of the given item in
+ * the given array. A match is considered to be either an exact type
+ * match or the class {@code Object} which represents a catch-all.
+ *
+ * @param item {@code non-null;} item with the exception type to look for
+ * @param arr {@code non-null;} array to search in
+ * @param count {@code non-null;} maximum number of elements in the array to check
+ * @return {@code true} iff the exception type is <i>not</i> found
+ */
+ private static boolean typeNotFound(Item item, Item[] arr, int count) {
+ CstType type = item.getExceptionClass();
+
+ for (int i = 0; i < count; i++) {
+ CstType one = arr[i].getExceptionClass();
+ if ((one == type) || (one == CstType.OBJECT)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns a target list corresponding to this instance. The result
+ * is a list of all the exception handler addresses, with the given
+ * {@code noException} address appended if appropriate. The
+ * result is automatically made immutable.
+ *
+ * @param noException {@code >= -1;} the no-exception address to append, or
+ * {@code -1} not to append anything
+ * @return {@code non-null;} list of exception targets, with
+ * {@code noException} appended if necessary
+ */
+ public IntList toTargetList(int noException) {
+ if (noException < -1) {
+ throw new IllegalArgumentException("noException < -1");
+ }
+
+ boolean hasDefault = (noException >= 0);
+ int sz = size();
+
+ if (sz == 0) {
+ if (hasDefault) {
+ /*
+ * The list is empty, but there is a no-exception
+ * address; so, the result is just that address.
+ */
+ return IntList.makeImmutable(noException);
+ }
+ /*
+ * The list is empty and there isn't even a no-exception
+ * address.
+ */
+ return IntList.EMPTY;
+ }
+
+ IntList result = new IntList(sz + (hasDefault ? 1 : 0));
+
+ for (int i = 0; i < sz; i++) {
+ result.add(get(i).getHandlerPc());
+ }
+
+ if (hasDefault) {
+ result.add(noException);
+ }
+
+ result.setImmutable();
+ return result;
+ }
+
+ /**
+ * Returns a rop-style catches list equivalent to this one.
+ *
+ * @return {@code non-null;} the converted instance
+ */
+ public TypeList toRopCatchList() {
+ int sz = size();
+ if (sz == 0) {
+ return StdTypeList.EMPTY;
+ }
+
+ StdTypeList result = new StdTypeList(sz);
+
+ for (int i = 0; i < sz; i++) {
+ result.set(i, get(i).getExceptionClass().getClassType());
+ }
+
+ result.setImmutable();
+ return result;
+ }
+
+ /**
+ * Item in an exception handler list.
+ */
+ public static class Item {
+ /** {@code >= 0;} the start pc (inclusive) of the handler's range */
+ private final int startPc;
+
+ /** {@code >= startPc;} the end pc (exclusive) of the handler's range */
+ private final int endPc;
+
+ /** {@code >= 0;} the pc of the exception handler */
+ private final int handlerPc;
+
+ /** {@code null-ok;} the exception class or {@code null} to catch all
+ * exceptions with this handler */
+ private final CstType exceptionClass;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param startPc {@code >= 0;} the start pc (inclusive) of the
+ * handler's range
+ * @param endPc {@code >= startPc;} the end pc (exclusive) of the
+ * handler's range
+ * @param handlerPc {@code >= 0;} the pc of the exception handler
+ * @param exceptionClass {@code null-ok;} the exception class or
+ * {@code null} to catch all exceptions with this handler
+ */
+ public Item(int startPc, int endPc, int handlerPc,
+ CstType exceptionClass) {
+ if (startPc < 0) {
+ throw new IllegalArgumentException("startPc < 0");
+ }
+
+ if (endPc < startPc) {
+ throw new IllegalArgumentException("endPc < startPc");
+ }
+
+ if (handlerPc < 0) {
+ throw new IllegalArgumentException("handlerPc < 0");
+ }
+
+ this.startPc = startPc;
+ this.endPc = endPc;
+ this.handlerPc = handlerPc;
+ this.exceptionClass = exceptionClass;
+ }
+
+ /**
+ * Gets the start pc (inclusive) of the handler's range.
+ *
+ * @return {@code >= 0;} the start pc (inclusive) of the handler's range.
+ */
+ public int getStartPc() {
+ return startPc;
+ }
+
+ /**
+ * Gets the end pc (exclusive) of the handler's range.
+ *
+ * @return {@code >= startPc;} the end pc (exclusive) of the
+ * handler's range.
+ */
+ public int getEndPc() {
+ return endPc;
+ }
+
+ /**
+ * Gets the pc of the exception handler.
+ *
+ * @return {@code >= 0;} the pc of the exception handler
+ */
+ public int getHandlerPc() {
+ return handlerPc;
+ }
+
+ /**
+ * Gets the class of exception handled.
+ *
+ * @return {@code non-null;} the exception class; {@link CstType#OBJECT}
+ * if this entry handles all possible exceptions
+ */
+ public CstType getExceptionClass() {
+ return (exceptionClass != null) ?
+ exceptionClass : CstType.OBJECT;
+ }
+
+ /**
+ * Returns whether the given address is in the range of this item.
+ *
+ * @param pc the address
+ * @return {@code true} iff this item covers {@code pc}
+ */
+ public boolean covers(int pc) {
+ return (pc >= startPc) && (pc < endPc);
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/cf/code/ByteOps.java b/dx/src/com/android/dx/cf/code/ByteOps.java
new file mode 100644
index 0000000..1376008
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/ByteOps.java
@@ -0,0 +1,649 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.util.Hex;
+
+/**
+ * Constants and utility methods for dealing with bytecode arrays at an
+ * opcode level.
+ */
+public class ByteOps {
+ // one constant per opcode
+ public static final int NOP = 0x00;
+ public static final int ACONST_NULL = 0x01;
+ public static final int ICONST_M1 = 0x02;
+ public static final int ICONST_0 = 0x03;
+ public static final int ICONST_1 = 0x04;
+ public static final int ICONST_2 = 0x05;
+ public static final int ICONST_3 = 0x06;
+ public static final int ICONST_4 = 0x07;
+ public static final int ICONST_5 = 0x08;
+ public static final int LCONST_0 = 0x09;
+ public static final int LCONST_1 = 0x0a;
+ public static final int FCONST_0 = 0x0b;
+ public static final int FCONST_1 = 0x0c;
+ public static final int FCONST_2 = 0x0d;
+ public static final int DCONST_0 = 0x0e;
+ public static final int DCONST_1 = 0x0f;
+ public static final int BIPUSH = 0x10;
+ public static final int SIPUSH = 0x11;
+ public static final int LDC = 0x12;
+ public static final int LDC_W = 0x13;
+ public static final int LDC2_W = 0x14;
+ public static final int ILOAD = 0x15;
+ public static final int LLOAD = 0x16;
+ public static final int FLOAD = 0x17;
+ public static final int DLOAD = 0x18;
+ public static final int ALOAD = 0x19;
+ public static final int ILOAD_0 = 0x1a;
+ public static final int ILOAD_1 = 0x1b;
+ public static final int ILOAD_2 = 0x1c;
+ public static final int ILOAD_3 = 0x1d;
+ public static final int LLOAD_0 = 0x1e;
+ public static final int LLOAD_1 = 0x1f;
+ public static final int LLOAD_2 = 0x20;
+ public static final int LLOAD_3 = 0x21;
+ public static final int FLOAD_0 = 0x22;
+ public static final int FLOAD_1 = 0x23;
+ public static final int FLOAD_2 = 0x24;
+ public static final int FLOAD_3 = 0x25;
+ public static final int DLOAD_0 = 0x26;
+ public static final int DLOAD_1 = 0x27;
+ public static final int DLOAD_2 = 0x28;
+ public static final int DLOAD_3 = 0x29;
+ public static final int ALOAD_0 = 0x2a;
+ public static final int ALOAD_1 = 0x2b;
+ public static final int ALOAD_2 = 0x2c;
+ public static final int ALOAD_3 = 0x2d;
+ public static final int IALOAD = 0x2e;
+ public static final int LALOAD = 0x2f;
+ public static final int FALOAD = 0x30;
+ public static final int DALOAD = 0x31;
+ public static final int AALOAD = 0x32;
+ public static final int BALOAD = 0x33;
+ public static final int CALOAD = 0x34;
+ public static final int SALOAD = 0x35;
+ public static final int ISTORE = 0x36;
+ public static final int LSTORE = 0x37;
+ public static final int FSTORE = 0x38;
+ public static final int DSTORE = 0x39;
+ public static final int ASTORE = 0x3a;
+ public static final int ISTORE_0 = 0x3b;
+ public static final int ISTORE_1 = 0x3c;
+ public static final int ISTORE_2 = 0x3d;
+ public static final int ISTORE_3 = 0x3e;
+ public static final int LSTORE_0 = 0x3f;
+ public static final int LSTORE_1 = 0x40;
+ public static final int LSTORE_2 = 0x41;
+ public static final int LSTORE_3 = 0x42;
+ public static final int FSTORE_0 = 0x43;
+ public static final int FSTORE_1 = 0x44;
+ public static final int FSTORE_2 = 0x45;
+ public static final int FSTORE_3 = 0x46;
+ public static final int DSTORE_0 = 0x47;
+ public static final int DSTORE_1 = 0x48;
+ public static final int DSTORE_2 = 0x49;
+ public static final int DSTORE_3 = 0x4a;
+ public static final int ASTORE_0 = 0x4b;
+ public static final int ASTORE_1 = 0x4c;
+ public static final int ASTORE_2 = 0x4d;
+ public static final int ASTORE_3 = 0x4e;
+ public static final int IASTORE = 0x4f;
+ public static final int LASTORE = 0x50;
+ public static final int FASTORE = 0x51;
+ public static final int DASTORE = 0x52;
+ public static final int AASTORE = 0x53;
+ public static final int BASTORE = 0x54;
+ public static final int CASTORE = 0x55;
+ public static final int SASTORE = 0x56;
+ public static final int POP = 0x57;
+ public static final int POP2 = 0x58;
+ public static final int DUP = 0x59;
+ public static final int DUP_X1 = 0x5a;
+ public static final int DUP_X2 = 0x5b;
+ public static final int DUP2 = 0x5c;
+ public static final int DUP2_X1 = 0x5d;
+ public static final int DUP2_X2 = 0x5e;
+ public static final int SWAP = 0x5f;
+ public static final int IADD = 0x60;
+ public static final int LADD = 0x61;
+ public static final int FADD = 0x62;
+ public static final int DADD = 0x63;
+ public static final int ISUB = 0x64;
+ public static final int LSUB = 0x65;
+ public static final int FSUB = 0x66;
+ public static final int DSUB = 0x67;
+ public static final int IMUL = 0x68;
+ public static final int LMUL = 0x69;
+ public static final int FMUL = 0x6a;
+ public static final int DMUL = 0x6b;
+ public static final int IDIV = 0x6c;
+ public static final int LDIV = 0x6d;
+ public static final int FDIV = 0x6e;
+ public static final int DDIV = 0x6f;
+ public static final int IREM = 0x70;
+ public static final int LREM = 0x71;
+ public static final int FREM = 0x72;
+ public static final int DREM = 0x73;
+ public static final int INEG = 0x74;
+ public static final int LNEG = 0x75;
+ public static final int FNEG = 0x76;
+ public static final int DNEG = 0x77;
+ public static final int ISHL = 0x78;
+ public static final int LSHL = 0x79;
+ public static final int ISHR = 0x7a;
+ public static final int LSHR = 0x7b;
+ public static final int IUSHR = 0x7c;
+ public static final int LUSHR = 0x7d;
+ public static final int IAND = 0x7e;
+ public static final int LAND = 0x7f;
+ public static final int IOR = 0x80;
+ public static final int LOR = 0x81;
+ public static final int IXOR = 0x82;
+ public static final int LXOR = 0x83;
+ public static final int IINC = 0x84;
+ public static final int I2L = 0x85;
+ public static final int I2F = 0x86;
+ public static final int I2D = 0x87;
+ public static final int L2I = 0x88;
+ public static final int L2F = 0x89;
+ public static final int L2D = 0x8a;
+ public static final int F2I = 0x8b;
+ public static final int F2L = 0x8c;
+ public static final int F2D = 0x8d;
+ public static final int D2I = 0x8e;
+ public static final int D2L = 0x8f;
+ public static final int D2F = 0x90;
+ public static final int I2B = 0x91;
+ public static final int I2C = 0x92;
+ public static final int I2S = 0x93;
+ public static final int LCMP = 0x94;
+ public static final int FCMPL = 0x95;
+ public static final int FCMPG = 0x96;
+ public static final int DCMPL = 0x97;
+ public static final int DCMPG = 0x98;
+ public static final int IFEQ = 0x99;
+ public static final int IFNE = 0x9a;
+ public static final int IFLT = 0x9b;
+ public static final int IFGE = 0x9c;
+ public static final int IFGT = 0x9d;
+ public static final int IFLE = 0x9e;
+ public static final int IF_ICMPEQ = 0x9f;
+ public static final int IF_ICMPNE = 0xa0;
+ public static final int IF_ICMPLT = 0xa1;
+ public static final int IF_ICMPGE = 0xa2;
+ public static final int IF_ICMPGT = 0xa3;
+ public static final int IF_ICMPLE = 0xa4;
+ public static final int IF_ACMPEQ = 0xa5;
+ public static final int IF_ACMPNE = 0xa6;
+ public static final int GOTO = 0xa7;
+ public static final int JSR = 0xa8;
+ public static final int RET = 0xa9;
+ public static final int TABLESWITCH = 0xaa;
+ public static final int LOOKUPSWITCH = 0xab;
+ public static final int IRETURN = 0xac;
+ public static final int LRETURN = 0xad;
+ public static final int FRETURN = 0xae;
+ public static final int DRETURN = 0xaf;
+ public static final int ARETURN = 0xb0;
+ public static final int RETURN = 0xb1;
+ public static final int GETSTATIC = 0xb2;
+ public static final int PUTSTATIC = 0xb3;
+ public static final int GETFIELD = 0xb4;
+ public static final int PUTFIELD = 0xb5;
+ public static final int INVOKEVIRTUAL = 0xb6;
+ public static final int INVOKESPECIAL = 0xb7;
+ public static final int INVOKESTATIC = 0xb8;
+ public static final int INVOKEINTERFACE = 0xb9;
+ public static final int NEW = 0xbb;
+ public static final int NEWARRAY = 0xbc;
+ public static final int ANEWARRAY = 0xbd;
+ public static final int ARRAYLENGTH = 0xbe;
+ public static final int ATHROW = 0xbf;
+ public static final int CHECKCAST = 0xc0;
+ public static final int INSTANCEOF = 0xc1;
+ public static final int MONITORENTER = 0xc2;
+ public static final int MONITOREXIT = 0xc3;
+ public static final int WIDE = 0xc4;
+ public static final int MULTIANEWARRAY = 0xc5;
+ public static final int IFNULL = 0xc6;
+ public static final int IFNONNULL = 0xc7;
+ public static final int GOTO_W = 0xc8;
+ public static final int JSR_W = 0xc9;
+
+ // a constant for each valid argument to "newarray"
+
+ public static final int NEWARRAY_BOOLEAN = 4;
+ public static final int NEWARRAY_CHAR = 5;
+ public static final int NEWARRAY_FLOAT = 6;
+ public static final int NEWARRAY_DOUBLE = 7;
+ public static final int NEWARRAY_BYTE = 8;
+ public static final int NEWARRAY_SHORT = 9;
+ public static final int NEWARRAY_INT = 10;
+ public static final int NEWARRAY_LONG = 11;
+
+ // a constant for each possible instruction format
+
+ /** invalid */
+ public static final int FMT_INVALID = 0;
+
+ /** "-": {@code op} */
+ public static final int FMT_NO_ARGS = 1;
+
+ /** "0": {@code op}; implies {@code max_locals >= 1} */
+ public static final int FMT_NO_ARGS_LOCALS_1 = 2;
+
+ /** "1": {@code op}; implies {@code max_locals >= 2} */
+ public static final int FMT_NO_ARGS_LOCALS_2 = 3;
+
+ /** "2": {@code op}; implies {@code max_locals >= 3} */
+ public static final int FMT_NO_ARGS_LOCALS_3 = 4;
+
+ /** "3": {@code op}; implies {@code max_locals >= 4} */
+ public static final int FMT_NO_ARGS_LOCALS_4 = 5;
+
+ /** "4": {@code op}; implies {@code max_locals >= 5} */
+ public static final int FMT_NO_ARGS_LOCALS_5 = 6;
+
+ /** "b": {@code op target target} */
+ public static final int FMT_BRANCH = 7;
+
+ /** "c": {@code op target target target target} */
+ public static final int FMT_WIDE_BRANCH = 8;
+
+ /** "p": {@code op #cpi #cpi}; constant restricted as specified */
+ public static final int FMT_CPI = 9;
+
+ /**
+ * "l": {@code op local}; category-1 local; implies
+ * {@code max_locals} is at least two more than the given
+ * local number
+ */
+ public static final int FMT_LOCAL_1 = 10;
+
+ /**
+ * "m": {@code op local}; category-2 local; implies
+ * {@code max_locals} is at least two more than the given
+ * local number
+ */
+ public static final int FMT_LOCAL_2 = 11;
+
+ /**
+ * "y": {@code op #byte} ({@code bipush} and
+ * {@code newarray})
+ */
+ public static final int FMT_LITERAL_BYTE = 12;
+
+ /** "I": {@code invokeinterface cpi cpi count 0} */
+ public static final int FMT_INVOKEINTERFACE = 13;
+
+ /** "L": {@code ldc #cpi}; constant restricted as specified */
+ public static final int FMT_LDC = 14;
+
+ /** "S": {@code sipush #byte #byte} */
+ public static final int FMT_SIPUSH = 15;
+
+ /** "T": {@code tableswitch ...} */
+ public static final int FMT_TABLESWITCH = 16;
+
+ /** "U": {@code lookupswitch ...} */
+ public static final int FMT_LOOKUPSWITCH = 17;
+
+ /** "M": {@code multianewarray cpi cpi dims} */
+ public static final int FMT_MULTIANEWARRAY = 18;
+
+ /** "W": {@code wide ...} */
+ public static final int FMT_WIDE = 19;
+
+ /** mask for the bits representing the opcode format */
+ public static final int FMT_MASK = 0x1f;
+
+ /** "I": flag bit for valid cp type for {@code Integer} */
+ public static final int CPOK_Integer = 0x20;
+
+ /** "F": flag bit for valid cp type for {@code Float} */
+ public static final int CPOK_Float = 0x40;
+
+ /** "J": flag bit for valid cp type for {@code Long} */
+ public static final int CPOK_Long = 0x80;
+
+ /** "D": flag bit for valid cp type for {@code Double} */
+ public static final int CPOK_Double = 0x100;
+
+ /** "c": flag bit for valid cp type for {@code Class} */
+ public static final int CPOK_Class = 0x200;
+
+ /** "s": flag bit for valid cp type for {@code String} */
+ public static final int CPOK_String = 0x400;
+
+ /** "f": flag bit for valid cp type for {@code Fieldref} */
+ public static final int CPOK_Fieldref = 0x800;
+
+ /** "m": flag bit for valid cp type for {@code Methodref} */
+ public static final int CPOK_Methodref = 0x1000;
+
+ /** "i": flag bit for valid cp type for {@code InterfaceMethodref} */
+ public static final int CPOK_InterfaceMethodref = 0x2000;
+
+ /**
+ * {@code non-null;} map from opcodes to format or'ed with allowed constant
+ * pool types
+ */
+ private static final int[] OPCODE_INFO = new int[256];
+
+ /** {@code non-null;} map from opcodes to their names */
+ private static final String[] OPCODE_NAMES = new String[256];
+
+ /** {@code non-null;} bigass string describing all the opcodes */
+ private static final String OPCODE_DETAILS =
+ "00 - nop;" +
+ "01 - aconst_null;" +
+ "02 - iconst_m1;" +
+ "03 - iconst_0;" +
+ "04 - iconst_1;" +
+ "05 - iconst_2;" +
+ "06 - iconst_3;" +
+ "07 - iconst_4;" +
+ "08 - iconst_5;" +
+ "09 - lconst_0;" +
+ "0a - lconst_1;" +
+ "0b - fconst_0;" +
+ "0c - fconst_1;" +
+ "0d - fconst_2;" +
+ "0e - dconst_0;" +
+ "0f - dconst_1;" +
+ "10 y bipush;" +
+ "11 S sipush;" +
+ "12 L:IFcs ldc;" +
+ "13 p:IFcs ldc_w;" +
+ "14 p:DJ ldc2_w;" +
+ "15 l iload;" +
+ "16 m lload;" +
+ "17 l fload;" +
+ "18 m dload;" +
+ "19 l aload;" +
+ "1a 0 iload_0;" +
+ "1b 1 iload_1;" +
+ "1c 2 iload_2;" +
+ "1d 3 iload_3;" +
+ "1e 1 lload_0;" +
+ "1f 2 lload_1;" +
+ "20 3 lload_2;" +
+ "21 4 lload_3;" +
+ "22 0 fload_0;" +
+ "23 1 fload_1;" +
+ "24 2 fload_2;" +
+ "25 3 fload_3;" +
+ "26 1 dload_0;" +
+ "27 2 dload_1;" +
+ "28 3 dload_2;" +
+ "29 4 dload_3;" +
+ "2a 0 aload_0;" +
+ "2b 1 aload_1;" +
+ "2c 2 aload_2;" +
+ "2d 3 aload_3;" +
+ "2e - iaload;" +
+ "2f - laload;" +
+ "30 - faload;" +
+ "31 - daload;" +
+ "32 - aaload;" +
+ "33 - baload;" +
+ "34 - caload;" +
+ "35 - saload;" +
+ "36 - istore;" +
+ "37 - lstore;" +
+ "38 - fstore;" +
+ "39 - dstore;" +
+ "3a - astore;" +
+ "3b 0 istore_0;" +
+ "3c 1 istore_1;" +
+ "3d 2 istore_2;" +
+ "3e 3 istore_3;" +
+ "3f 1 lstore_0;" +
+ "40 2 lstore_1;" +
+ "41 3 lstore_2;" +
+ "42 4 lstore_3;" +
+ "43 0 fstore_0;" +
+ "44 1 fstore_1;" +
+ "45 2 fstore_2;" +
+ "46 3 fstore_3;" +
+ "47 1 dstore_0;" +
+ "48 2 dstore_1;" +
+ "49 3 dstore_2;" +
+ "4a 4 dstore_3;" +
+ "4b 0 astore_0;" +
+ "4c 1 astore_1;" +
+ "4d 2 astore_2;" +
+ "4e 3 astore_3;" +
+ "4f - iastore;" +
+ "50 - lastore;" +
+ "51 - fastore;" +
+ "52 - dastore;" +
+ "53 - aastore;" +
+ "54 - bastore;" +
+ "55 - castore;" +
+ "56 - sastore;" +
+ "57 - pop;" +
+ "58 - pop2;" +
+ "59 - dup;" +
+ "5a - dup_x1;" +
+ "5b - dup_x2;" +
+ "5c - dup2;" +
+ "5d - dup2_x1;" +
+ "5e - dup2_x2;" +
+ "5f - swap;" +
+ "60 - iadd;" +
+ "61 - ladd;" +
+ "62 - fadd;" +
+ "63 - dadd;" +
+ "64 - isub;" +
+ "65 - lsub;" +
+ "66 - fsub;" +
+ "67 - dsub;" +
+ "68 - imul;" +
+ "69 - lmul;" +
+ "6a - fmul;" +
+ "6b - dmul;" +
+ "6c - idiv;" +
+ "6d - ldiv;" +
+ "6e - fdiv;" +
+ "6f - ddiv;" +
+ "70 - irem;" +
+ "71 - lrem;" +
+ "72 - frem;" +
+ "73 - drem;" +
+ "74 - ineg;" +
+ "75 - lneg;" +
+ "76 - fneg;" +
+ "77 - dneg;" +
+ "78 - ishl;" +
+ "79 - lshl;" +
+ "7a - ishr;" +
+ "7b - lshr;" +
+ "7c - iushr;" +
+ "7d - lushr;" +
+ "7e - iand;" +
+ "7f - land;" +
+ "80 - ior;" +
+ "81 - lor;" +
+ "82 - ixor;" +
+ "83 - lxor;" +
+ "84 l iinc;" +
+ "85 - i2l;" +
+ "86 - i2f;" +
+ "87 - i2d;" +
+ "88 - l2i;" +
+ "89 - l2f;" +
+ "8a - l2d;" +
+ "8b - f2i;" +
+ "8c - f2l;" +
+ "8d - f2d;" +
+ "8e - d2i;" +
+ "8f - d2l;" +
+ "90 - d2f;" +
+ "91 - i2b;" +
+ "92 - i2c;" +
+ "93 - i2s;" +
+ "94 - lcmp;" +
+ "95 - fcmpl;" +
+ "96 - fcmpg;" +
+ "97 - dcmpl;" +
+ "98 - dcmpg;" +
+ "99 b ifeq;" +
+ "9a b ifne;" +
+ "9b b iflt;" +
+ "9c b ifge;" +
+ "9d b ifgt;" +
+ "9e b ifle;" +
+ "9f b if_icmpeq;" +
+ "a0 b if_icmpne;" +
+ "a1 b if_icmplt;" +
+ "a2 b if_icmpge;" +
+ "a3 b if_icmpgt;" +
+ "a4 b if_icmple;" +
+ "a5 b if_acmpeq;" +
+ "a6 b if_acmpne;" +
+ "a7 b goto;" +
+ "a8 b jsr;" +
+ "a9 l ret;" +
+ "aa T tableswitch;" +
+ "ab U lookupswitch;" +
+ "ac - ireturn;" +
+ "ad - lreturn;" +
+ "ae - freturn;" +
+ "af - dreturn;" +
+ "b0 - areturn;" +
+ "b1 - return;" +
+ "b2 p:f getstatic;" +
+ "b3 p:f putstatic;" +
+ "b4 p:f getfield;" +
+ "b5 p:f putfield;" +
+ "b6 p:m invokevirtual;" +
+ "b7 p:m invokespecial;" +
+ "b8 p:m invokestatic;" +
+ "b9 I:i invokeinterface;" +
+ "bb p:c new;" +
+ "bc y newarray;" +
+ "bd p:c anewarray;" +
+ "be - arraylength;" +
+ "bf - athrow;" +
+ "c0 p:c checkcast;" +
+ "c1 p:c instanceof;" +
+ "c2 - monitorenter;" +
+ "c3 - monitorexit;" +
+ "c4 W wide;" +
+ "c5 M:c multianewarray;" +
+ "c6 b ifnull;" +
+ "c7 b ifnonnull;" +
+ "c8 c goto_w;" +
+ "c9 c jsr_w;";
+
+ static {
+ // Set up OPCODE_INFO and OPCODE_NAMES.
+ String s = OPCODE_DETAILS;
+ int len = s.length();
+
+ for (int i = 0; i < len; /*i*/) {
+ int idx = (Character.digit(s.charAt(i), 16) << 4) |
+ Character.digit(s.charAt(i + 1), 16);
+ int info;
+ switch (s.charAt(i + 3)) {
+ case '-': info = FMT_NO_ARGS; break;
+ case '0': info = FMT_NO_ARGS_LOCALS_1; break;
+ case '1': info = FMT_NO_ARGS_LOCALS_2; break;
+ case '2': info = FMT_NO_ARGS_LOCALS_3; break;
+ case '3': info = FMT_NO_ARGS_LOCALS_4; break;
+ case '4': info = FMT_NO_ARGS_LOCALS_5; break;
+ case 'b': info = FMT_BRANCH; break;
+ case 'c': info = FMT_WIDE_BRANCH; break;
+ case 'p': info = FMT_CPI; break;
+ case 'l': info = FMT_LOCAL_1; break;
+ case 'm': info = FMT_LOCAL_2; break;
+ case 'y': info = FMT_LITERAL_BYTE; break;
+ case 'I': info = FMT_INVOKEINTERFACE; break;
+ case 'L': info = FMT_LDC; break;
+ case 'S': info = FMT_SIPUSH; break;
+ case 'T': info = FMT_TABLESWITCH; break;
+ case 'U': info = FMT_LOOKUPSWITCH; break;
+ case 'M': info = FMT_MULTIANEWARRAY; break;
+ case 'W': info = FMT_WIDE; break;
+ default: info = FMT_INVALID; break;
+ }
+
+ i += 5;
+ if (s.charAt(i - 1) == ':') {
+ inner:
+ for (;;) {
+ switch (s.charAt(i)) {
+ case 'I': info |= CPOK_Integer; break;
+ case 'F': info |= CPOK_Float; break;
+ case 'J': info |= CPOK_Long; break;
+ case 'D': info |= CPOK_Double; break;
+ case 'c': info |= CPOK_Class; break;
+ case 's': info |= CPOK_String; break;
+ case 'f': info |= CPOK_Fieldref; break;
+ case 'm': info |= CPOK_Methodref; break;
+ case 'i': info |= CPOK_InterfaceMethodref; break;
+ default: break inner;
+ }
+ i++;
+ }
+ i++;
+ }
+
+ int endAt = s.indexOf(';', i);
+ OPCODE_INFO[idx] = info;
+ OPCODE_NAMES[idx] = s.substring(i, endAt);
+ i = endAt + 1;
+ }
+ }
+
+ /**
+ * This class is uninstantiable.
+ */
+ private ByteOps() {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Gets the name of the given opcode.
+ *
+ * @param opcode {@code >= 0, <= 255;} the opcode
+ * @return {@code non-null;} its name
+ */
+ public static String opName(int opcode) {
+ String result = OPCODE_NAMES[opcode];
+
+ if (result == null) {
+ result = "unused_" + Hex.u1(opcode);
+ OPCODE_NAMES[opcode] = result;
+ }
+
+ return result;
+ }
+
+ /**
+ * Gets the format and allowed cp types of the given opcode.
+ *
+ * @param opcode {@code >= 0, <= 255;} the opcode
+ * @return its format and allowed cp types
+ */
+ public static int opInfo(int opcode) {
+ return OPCODE_INFO[opcode];
+ }
+}
diff --git a/dx/src/com/android/dx/cf/code/BytecodeArray.java b/dx/src/com/android/dx/cf/code/BytecodeArray.java
new file mode 100644
index 0000000..80e94bf
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/BytecodeArray.java
@@ -0,0 +1,1393 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.ConstantPool;
+import com.android.dx.rop.cst.CstDouble;
+import com.android.dx.rop.cst.CstFloat;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.CstKnownNull;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.rop.cst.CstLong;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.Bits;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+import java.util.ArrayList;
+
+/**
+ * Bytecode array, which is part of a standard {@code Code} attribute.
+ */
+public final class BytecodeArray {
+ /** convenient no-op implementation of {@link Visitor} */
+ public static final Visitor EMPTY_VISITOR = new BaseVisitor();
+
+ /** {@code non-null;} underlying bytes */
+ private final ByteArray bytes;
+
+ /** {@code non-null;} constant pool to use when resolving constant pool indices */
+ private final ConstantPool pool;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param bytes {@code non-null;} underlying bytes
+ * @param pool {@code non-null;} constant pool to use when resolving constant
+ * pool indices
+ */
+ public BytecodeArray(ByteArray bytes, ConstantPool pool) {
+ if (bytes == null) {
+ throw new NullPointerException("bytes == null");
+ }
+
+ if (pool == null) {
+ throw new NullPointerException("pool == null");
+ }
+
+ this.bytes = bytes;
+ this.pool = pool;
+ }
+
+ /**
+ * Gets the underlying byte array.
+ *
+ * @return {@code non-null;} the byte array
+ */
+ public ByteArray getBytes() {
+ return bytes;
+ }
+
+ /**
+ * Gets the size of the bytecode array, per se.
+ *
+ * @return {@code >= 0;} the length of the bytecode array
+ */
+ public int size() {
+ return bytes.size();
+ }
+
+ /**
+ * Gets the total length of this structure in bytes, when included in
+ * a {@code Code} attribute. The returned value includes the
+ * array size plus four bytes for {@code code_length}.
+ *
+ * @return {@code >= 4;} the total length, in bytes
+ */
+ public int byteLength() {
+ return 4 + bytes.size();
+ }
+
+ /**
+ * Parses each instruction in the array, in order.
+ *
+ * @param visitor {@code null-ok;} visitor to call back to for each instruction
+ */
+ public void forEach(Visitor visitor) {
+ int sz = bytes.size();
+ int at = 0;
+
+ while (at < sz) {
+ /*
+ * Don't record the previous offset here, so that we get to see the
+ * raw code that initializes the array
+ */
+ at += parseInstruction(at, visitor);
+ }
+ }
+
+ /**
+ * Finds the offset to each instruction in the bytecode array. The
+ * result is a bit set with the offset of each opcode-per-se flipped on.
+ *
+ * @see Bits
+ * @return {@code non-null;} appropriately constructed bit set
+ */
+ public int[] getInstructionOffsets() {
+ int sz = bytes.size();
+ int[] result = Bits.makeBitSet(sz);
+ int at = 0;
+
+ while (at < sz) {
+ Bits.set(result, at, true);
+ int length = parseInstruction(at, null);
+ at += length;
+ }
+
+ return result;
+ }
+
+ /**
+ * Processes the given "work set" by repeatedly finding the lowest bit
+ * in the set, clearing it, and parsing and visiting the instruction at
+ * the indicated offset (that is, the bit index), repeating until the
+ * work set is empty. It is expected that the visitor will regularly
+ * set new bits in the work set during the process.
+ *
+ * @param workSet {@code non-null;} the work set to process
+ * @param visitor {@code non-null;} visitor to call back to for each instruction
+ */
+ public void processWorkSet(int[] workSet, Visitor visitor) {
+ if (visitor == null) {
+ throw new NullPointerException("visitor == null");
+ }
+
+ for (;;) {
+ int offset = Bits.findFirst(workSet, 0);
+ if (offset < 0) {
+ break;
+ }
+ Bits.clear(workSet, offset);
+ parseInstruction(offset, visitor);
+ visitor.setPreviousOffset(offset);
+ }
+ }
+
+ /**
+ * Parses the instruction at the indicated offset. Indicate the
+ * result by calling the visitor if supplied and by returning the
+ * number of bytes consumed by the instruction.
+ *
+ * <p>In order to simplify further processing, the opcodes passed
+ * to the visitor are canonicalized, altering the opcode to a more
+ * universal one and making formerly implicit arguments
+ * explicit. In particular:</p>
+ *
+ * <ul>
+ * <li>The opcodes to push literal constants of primitive types all become
+ * {@code ldc}.
+ * E.g., {@code fconst_0}, {@code sipush}, and
+ * {@code lconst_0} qualify for this treatment.</li>
+ * <li>{@code aconst_null} becomes {@code ldc} of a
+ * "known null."</li>
+ * <li>Shorthand local variable accessors become the corresponding
+ * longhand. E.g. {@code aload_2} becomes {@code aload}.</li>
+ * <li>{@code goto_w} and {@code jsr_w} become {@code goto}
+ * and {@code jsr} (respectively).</li>
+ * <li>{@code ldc_w} becomes {@code ldc}.</li>
+ * <li>{@code tableswitch} becomes {@code lookupswitch}.
+ * <li>Arithmetic, array, and value-returning ops are collapsed
+ * to the {@code int} variant opcode, with the {@code type}
+ * argument set to indicate the actual type. E.g.,
+ * {@code fadd} becomes {@code iadd}, but
+ * {@code type} is passed as {@code Type.FLOAT} in that
+ * case. Similarly, {@code areturn} becomes
+ * {@code ireturn}. (However, {@code return} remains
+ * unchanged.</li>
+ * <li>Local variable access ops are collapsed to the {@code int}
+ * variant opcode, with the {@code type} argument set to indicate
+ * the actual type. E.g., {@code aload} becomes {@code iload},
+ * but {@code type} is passed as {@code Type.OBJECT} in
+ * that case.</li>
+ * <li>Numeric conversion ops ({@code i2l}, etc.) are left alone
+ * to avoid too much confustion, but their {@code type} is
+ * the pushed type. E.g., {@code i2b} gets type
+ * {@code Type.INT}, and {@code f2d} gets type
+ * {@code Type.DOUBLE}. Other unaltered opcodes also get
+ * their pushed type. E.g., {@code arraylength} gets type
+ * {@code Type.INT}.</li>
+ * </ul>
+ *
+ * @param offset {@code >= 0, < bytes.size();} offset to the start of the
+ * instruction
+ * @param visitor {@code null-ok;} visitor to call back to
+ * @return the length of the instruction, in bytes
+ */
+ public int parseInstruction(int offset, Visitor visitor) {
+ if (visitor == null) {
+ visitor = EMPTY_VISITOR;
+ }
+
+ try {
+ int opcode = bytes.getUnsignedByte(offset);
+ int info = ByteOps.opInfo(opcode);
+ int fmt = info & ByteOps.FMT_MASK;
+
+ switch (opcode) {
+ case ByteOps.NOP: {
+ visitor.visitNoArgs(opcode, offset, 1, Type.VOID);
+ return 1;
+ }
+ case ByteOps.ACONST_NULL: {
+ visitor.visitConstant(ByteOps.LDC, offset, 1,
+ CstKnownNull.THE_ONE, 0);
+ return 1;
+ }
+ case ByteOps.ICONST_M1: {
+ visitor.visitConstant(ByteOps.LDC, offset, 1,
+ CstInteger.VALUE_M1, -1);
+ return 1;
+ }
+ case ByteOps.ICONST_0: {
+ visitor.visitConstant(ByteOps.LDC, offset, 1,
+ CstInteger.VALUE_0, 0);
+ return 1;
+ }
+ case ByteOps.ICONST_1: {
+ visitor.visitConstant(ByteOps.LDC, offset, 1,
+ CstInteger.VALUE_1, 1);
+ return 1;
+ }
+ case ByteOps.ICONST_2: {
+ visitor.visitConstant(ByteOps.LDC, offset, 1,
+ CstInteger.VALUE_2, 2);
+ return 1;
+ }
+ case ByteOps.ICONST_3: {
+ visitor.visitConstant(ByteOps.LDC, offset, 1,
+ CstInteger.VALUE_3, 3);
+ return 1;
+ }
+ case ByteOps.ICONST_4: {
+ visitor.visitConstant(ByteOps.LDC, offset, 1,
+ CstInteger.VALUE_4, 4);
+ return 1;
+ }
+ case ByteOps.ICONST_5: {
+ visitor.visitConstant(ByteOps.LDC, offset, 1,
+ CstInteger.VALUE_5, 5);
+ return 1;
+ }
+ case ByteOps.LCONST_0: {
+ visitor.visitConstant(ByteOps.LDC, offset, 1,
+ CstLong.VALUE_0, 0);
+ return 1;
+ }
+ case ByteOps.LCONST_1: {
+ visitor.visitConstant(ByteOps.LDC, offset, 1,
+ CstLong.VALUE_1, 0);
+ return 1;
+ }
+ case ByteOps.FCONST_0: {
+ visitor.visitConstant(ByteOps.LDC, offset, 1,
+ CstFloat.VALUE_0, 0);
+ return 1;
+ }
+ case ByteOps.FCONST_1: {
+ visitor.visitConstant(ByteOps.LDC, offset, 1,
+ CstFloat.VALUE_1, 0);
+ return 1;
+ }
+ case ByteOps.FCONST_2: {
+ visitor.visitConstant(ByteOps.LDC, offset, 1,
+ CstFloat.VALUE_2, 0);
+ return 1;
+ }
+ case ByteOps.DCONST_0: {
+ visitor.visitConstant(ByteOps.LDC, offset, 1,
+ CstDouble.VALUE_0, 0);
+ return 1;
+ }
+ case ByteOps.DCONST_1: {
+ visitor.visitConstant(ByteOps.LDC, offset, 1,
+ CstDouble.VALUE_1, 0);
+ return 1;
+ }
+ case ByteOps.BIPUSH: {
+ int value = bytes.getByte(offset + 1);
+ visitor.visitConstant(ByteOps.LDC, offset, 2,
+ CstInteger.make(value), value);
+ return 2;
+ }
+ case ByteOps.SIPUSH: {
+ int value = bytes.getShort(offset + 1);
+ visitor.visitConstant(ByteOps.LDC, offset, 3,
+ CstInteger.make(value), value);
+ return 3;
+ }
+ case ByteOps.LDC: {
+ int idx = bytes.getUnsignedByte(offset + 1);
+ Constant cst = pool.get(idx);
+ int value = (cst instanceof CstInteger) ?
+ ((CstInteger) cst).getValue() : 0;
+ visitor.visitConstant(ByteOps.LDC, offset, 2, cst, value);
+ return 2;
+ }
+ case ByteOps.LDC_W: {
+ int idx = bytes.getUnsignedShort(offset + 1);
+ Constant cst = pool.get(idx);
+ int value = (cst instanceof CstInteger) ?
+ ((CstInteger) cst).getValue() : 0;
+ visitor.visitConstant(ByteOps.LDC, offset, 3, cst, value);
+ return 3;
+ }
+ case ByteOps.LDC2_W: {
+ int idx = bytes.getUnsignedShort(offset + 1);
+ Constant cst = pool.get(idx);
+ visitor.visitConstant(ByteOps.LDC2_W, offset, 3, cst, 0);
+ return 3;
+ }
+ case ByteOps.ILOAD: {
+ int idx = bytes.getUnsignedByte(offset + 1);
+ visitor.visitLocal(ByteOps.ILOAD, offset, 2, idx,
+ Type.INT, 0);
+ return 2;
+ }
+ case ByteOps.LLOAD: {
+ int idx = bytes.getUnsignedByte(offset + 1);
+ visitor.visitLocal(ByteOps.ILOAD, offset, 2, idx,
+ Type.LONG, 0);
+ return 2;
+ }
+ case ByteOps.FLOAD: {
+ int idx = bytes.getUnsignedByte(offset + 1);
+ visitor.visitLocal(ByteOps.ILOAD, offset, 2, idx,
+ Type.FLOAT, 0);
+ return 2;
+ }
+ case ByteOps.DLOAD: {
+ int idx = bytes.getUnsignedByte(offset + 1);
+ visitor.visitLocal(ByteOps.ILOAD, offset, 2, idx,
+ Type.DOUBLE, 0);
+ return 2;
+ }
+ case ByteOps.ALOAD: {
+ int idx = bytes.getUnsignedByte(offset + 1);
+ visitor.visitLocal(ByteOps.ILOAD, offset, 2, idx,
+ Type.OBJECT, 0);
+ return 2;
+ }
+ case ByteOps.ILOAD_0:
+ case ByteOps.ILOAD_1:
+ case ByteOps.ILOAD_2:
+ case ByteOps.ILOAD_3: {
+ int idx = opcode - ByteOps.ILOAD_0;
+ visitor.visitLocal(ByteOps.ILOAD, offset, 1, idx,
+ Type.INT, 0);
+ return 1;
+ }
+ case ByteOps.LLOAD_0:
+ case ByteOps.LLOAD_1:
+ case ByteOps.LLOAD_2:
+ case ByteOps.LLOAD_3: {
+ int idx = opcode - ByteOps.LLOAD_0;
+ visitor.visitLocal(ByteOps.ILOAD, offset, 1, idx,
+ Type.LONG, 0);
+ return 1;
+ }
+ case ByteOps.FLOAD_0:
+ case ByteOps.FLOAD_1:
+ case ByteOps.FLOAD_2:
+ case ByteOps.FLOAD_3: {
+ int idx = opcode - ByteOps.FLOAD_0;
+ visitor.visitLocal(ByteOps.ILOAD, offset, 1, idx,
+ Type.FLOAT, 0);
+ return 1;
+ }
+ case ByteOps.DLOAD_0:
+ case ByteOps.DLOAD_1:
+ case ByteOps.DLOAD_2:
+ case ByteOps.DLOAD_3: {
+ int idx = opcode - ByteOps.DLOAD_0;
+ visitor.visitLocal(ByteOps.ILOAD, offset, 1, idx,
+ Type.DOUBLE, 0);
+ return 1;
+ }
+ case ByteOps.ALOAD_0:
+ case ByteOps.ALOAD_1:
+ case ByteOps.ALOAD_2:
+ case ByteOps.ALOAD_3: {
+ int idx = opcode - ByteOps.ALOAD_0;
+ visitor.visitLocal(ByteOps.ILOAD, offset, 1, idx,
+ Type.OBJECT, 0);
+ return 1;
+ }
+ case ByteOps.IALOAD: {
+ visitor.visitNoArgs(ByteOps.IALOAD, offset, 1, Type.INT);
+ return 1;
+ }
+ case ByteOps.LALOAD: {
+ visitor.visitNoArgs(ByteOps.IALOAD, offset, 1, Type.LONG);
+ return 1;
+ }
+ case ByteOps.FALOAD: {
+ visitor.visitNoArgs(ByteOps.IALOAD, offset, 1,
+ Type.FLOAT);
+ return 1;
+ }
+ case ByteOps.DALOAD: {
+ visitor.visitNoArgs(ByteOps.IALOAD, offset, 1,
+ Type.DOUBLE);
+ return 1;
+ }
+ case ByteOps.AALOAD: {
+ visitor.visitNoArgs(ByteOps.IALOAD, offset, 1,
+ Type.OBJECT);
+ return 1;
+ }
+ case ByteOps.BALOAD: {
+ visitor.visitNoArgs(ByteOps.IALOAD, offset, 1, Type.BYTE);
+ return 1;
+ }
+ case ByteOps.CALOAD: {
+ visitor.visitNoArgs(ByteOps.IALOAD, offset, 1, Type.CHAR);
+ return 1;
+ }
+ case ByteOps.SALOAD: {
+ visitor.visitNoArgs(ByteOps.IALOAD, offset, 1,
+ Type.SHORT);
+ return 1;
+ }
+ case ByteOps.ISTORE: {
+ int idx = bytes.getUnsignedByte(offset + 1);
+ visitor.visitLocal(ByteOps.ISTORE, offset, 2, idx,
+ Type.INT, 0);
+ return 2;
+ }
+ case ByteOps.LSTORE: {
+ int idx = bytes.getUnsignedByte(offset + 1);
+ visitor.visitLocal(ByteOps.ISTORE, offset, 2, idx,
+ Type.LONG, 0);
+ return 2;
+ }
+ case ByteOps.FSTORE: {
+ int idx = bytes.getUnsignedByte(offset + 1);
+ visitor.visitLocal(ByteOps.ISTORE, offset, 2, idx,
+ Type.FLOAT, 0);
+ return 2;
+ }
+ case ByteOps.DSTORE: {
+ int idx = bytes.getUnsignedByte(offset + 1);
+ visitor.visitLocal(ByteOps.ISTORE, offset, 2, idx,
+ Type.DOUBLE, 0);
+ return 2;
+ }
+ case ByteOps.ASTORE: {
+ int idx = bytes.getUnsignedByte(offset + 1);
+ visitor.visitLocal(ByteOps.ISTORE, offset, 2, idx,
+ Type.OBJECT, 0);
+ return 2;
+ }
+ case ByteOps.ISTORE_0:
+ case ByteOps.ISTORE_1:
+ case ByteOps.ISTORE_2:
+ case ByteOps.ISTORE_3: {
+ int idx = opcode - ByteOps.ISTORE_0;
+ visitor.visitLocal(ByteOps.ISTORE, offset, 1, idx,
+ Type.INT, 0);
+ return 1;
+ }
+ case ByteOps.LSTORE_0:
+ case ByteOps.LSTORE_1:
+ case ByteOps.LSTORE_2:
+ case ByteOps.LSTORE_3: {
+ int idx = opcode - ByteOps.LSTORE_0;
+ visitor.visitLocal(ByteOps.ISTORE, offset, 1, idx,
+ Type.LONG, 0);
+ return 1;
+ }
+ case ByteOps.FSTORE_0:
+ case ByteOps.FSTORE_1:
+ case ByteOps.FSTORE_2:
+ case ByteOps.FSTORE_3: {
+ int idx = opcode - ByteOps.FSTORE_0;
+ visitor.visitLocal(ByteOps.ISTORE, offset, 1, idx,
+ Type.FLOAT, 0);
+ return 1;
+ }
+ case ByteOps.DSTORE_0:
+ case ByteOps.DSTORE_1:
+ case ByteOps.DSTORE_2:
+ case ByteOps.DSTORE_3: {
+ int idx = opcode - ByteOps.DSTORE_0;
+ visitor.visitLocal(ByteOps.ISTORE, offset, 1, idx,
+ Type.DOUBLE, 0);
+ return 1;
+ }
+ case ByteOps.ASTORE_0:
+ case ByteOps.ASTORE_1:
+ case ByteOps.ASTORE_2:
+ case ByteOps.ASTORE_3: {
+ int idx = opcode - ByteOps.ASTORE_0;
+ visitor.visitLocal(ByteOps.ISTORE, offset, 1, idx,
+ Type.OBJECT, 0);
+ return 1;
+ }
+ case ByteOps.IASTORE: {
+ visitor.visitNoArgs(ByteOps.IASTORE, offset, 1, Type.INT);
+ return 1;
+ }
+ case ByteOps.LASTORE: {
+ visitor.visitNoArgs(ByteOps.IASTORE, offset, 1,
+ Type.LONG);
+ return 1;
+ }
+ case ByteOps.FASTORE: {
+ visitor.visitNoArgs(ByteOps.IASTORE, offset, 1,
+ Type.FLOAT);
+ return 1;
+ }
+ case ByteOps.DASTORE: {
+ visitor.visitNoArgs(ByteOps.IASTORE, offset, 1,
+ Type.DOUBLE);
+ return 1;
+ }
+ case ByteOps.AASTORE: {
+ visitor.visitNoArgs(ByteOps.IASTORE, offset, 1,
+ Type.OBJECT);
+ return 1;
+ }
+ case ByteOps.BASTORE: {
+ visitor.visitNoArgs(ByteOps.IASTORE, offset, 1,
+ Type.BYTE);
+ return 1;
+ }
+ case ByteOps.CASTORE: {
+ visitor.visitNoArgs(ByteOps.IASTORE, offset, 1,
+ Type.CHAR);
+ return 1;
+ }
+ case ByteOps.SASTORE: {
+ visitor.visitNoArgs(ByteOps.IASTORE, offset, 1,
+ Type.SHORT);
+ return 1;
+ }
+ case ByteOps.POP:
+ case ByteOps.POP2:
+ case ByteOps.DUP:
+ case ByteOps.DUP_X1:
+ case ByteOps.DUP_X2:
+ case ByteOps.DUP2:
+ case ByteOps.DUP2_X1:
+ case ByteOps.DUP2_X2:
+ case ByteOps.SWAP: {
+ visitor.visitNoArgs(opcode, offset, 1, Type.VOID);
+ return 1;
+ }
+ case ByteOps.IADD:
+ case ByteOps.ISUB:
+ case ByteOps.IMUL:
+ case ByteOps.IDIV:
+ case ByteOps.IREM:
+ case ByteOps.INEG:
+ case ByteOps.ISHL:
+ case ByteOps.ISHR:
+ case ByteOps.IUSHR:
+ case ByteOps.IAND:
+ case ByteOps.IOR:
+ case ByteOps.IXOR: {
+ visitor.visitNoArgs(opcode, offset, 1, Type.INT);
+ return 1;
+ }
+ case ByteOps.LADD:
+ case ByteOps.LSUB:
+ case ByteOps.LMUL:
+ case ByteOps.LDIV:
+ case ByteOps.LREM:
+ case ByteOps.LNEG:
+ case ByteOps.LSHL:
+ case ByteOps.LSHR:
+ case ByteOps.LUSHR:
+ case ByteOps.LAND:
+ case ByteOps.LOR:
+ case ByteOps.LXOR: {
+ /*
+ * It's "opcode - 1" because, conveniently enough, all
+ * these long ops are one past the int variants.
+ */
+ visitor.visitNoArgs(opcode - 1, offset, 1, Type.LONG);
+ return 1;
+ }
+ case ByteOps.FADD:
+ case ByteOps.FSUB:
+ case ByteOps.FMUL:
+ case ByteOps.FDIV:
+ case ByteOps.FREM:
+ case ByteOps.FNEG: {
+ /*
+ * It's "opcode - 2" because, conveniently enough, all
+ * these float ops are two past the int variants.
+ */
+ visitor.visitNoArgs(opcode - 2, offset, 1, Type.FLOAT);
+ return 1;
+ }
+ case ByteOps.DADD:
+ case ByteOps.DSUB:
+ case ByteOps.DMUL:
+ case ByteOps.DDIV:
+ case ByteOps.DREM:
+ case ByteOps.DNEG: {
+ /*
+ * It's "opcode - 3" because, conveniently enough, all
+ * these double ops are three past the int variants.
+ */
+ visitor.visitNoArgs(opcode - 3, offset, 1, Type.DOUBLE);
+ return 1;
+ }
+ case ByteOps.IINC: {
+ int idx = bytes.getUnsignedByte(offset + 1);
+ int value = bytes.getByte(offset + 2);
+ visitor.visitLocal(opcode, offset, 3, idx,
+ Type.INT, value);
+ return 3;
+ }
+ case ByteOps.I2L:
+ case ByteOps.F2L:
+ case ByteOps.D2L: {
+ visitor.visitNoArgs(opcode, offset, 1, Type.LONG);
+ return 1;
+ }
+ case ByteOps.I2F:
+ case ByteOps.L2F:
+ case ByteOps.D2F: {
+ visitor.visitNoArgs(opcode, offset, 1, Type.FLOAT);
+ return 1;
+ }
+ case ByteOps.I2D:
+ case ByteOps.L2D:
+ case ByteOps.F2D: {
+ visitor.visitNoArgs(opcode, offset, 1, Type.DOUBLE);
+ return 1;
+ }
+ case ByteOps.L2I:
+ case ByteOps.F2I:
+ case ByteOps.D2I:
+ case ByteOps.I2B:
+ case ByteOps.I2C:
+ case ByteOps.I2S:
+ case ByteOps.LCMP:
+ case ByteOps.FCMPL:
+ case ByteOps.FCMPG:
+ case ByteOps.DCMPL:
+ case ByteOps.DCMPG:
+ case ByteOps.ARRAYLENGTH: {
+ visitor.visitNoArgs(opcode, offset, 1, Type.INT);
+ return 1;
+ }
+ case ByteOps.IFEQ:
+ case ByteOps.IFNE:
+ case ByteOps.IFLT:
+ case ByteOps.IFGE:
+ case ByteOps.IFGT:
+ case ByteOps.IFLE:
+ case ByteOps.IF_ICMPEQ:
+ case ByteOps.IF_ICMPNE:
+ case ByteOps.IF_ICMPLT:
+ case ByteOps.IF_ICMPGE:
+ case ByteOps.IF_ICMPGT:
+ case ByteOps.IF_ICMPLE:
+ case ByteOps.IF_ACMPEQ:
+ case ByteOps.IF_ACMPNE:
+ case ByteOps.GOTO:
+ case ByteOps.JSR:
+ case ByteOps.IFNULL:
+ case ByteOps.IFNONNULL: {
+ int target = offset + bytes.getShort(offset + 1);
+ visitor.visitBranch(opcode, offset, 3, target);
+ return 3;
+ }
+ case ByteOps.RET: {
+ int idx = bytes.getUnsignedByte(offset + 1);
+ visitor.visitLocal(opcode, offset, 2, idx,
+ Type.RETURN_ADDRESS, 0);
+ return 2;
+ }
+ case ByteOps.TABLESWITCH: {
+ return parseTableswitch(offset, visitor);
+ }
+ case ByteOps.LOOKUPSWITCH: {
+ return parseLookupswitch(offset, visitor);
+ }
+ case ByteOps.IRETURN: {
+ visitor.visitNoArgs(ByteOps.IRETURN, offset, 1, Type.INT);
+ return 1;
+ }
+ case ByteOps.LRETURN: {
+ visitor.visitNoArgs(ByteOps.IRETURN, offset, 1,
+ Type.LONG);
+ return 1;
+ }
+ case ByteOps.FRETURN: {
+ visitor.visitNoArgs(ByteOps.IRETURN, offset, 1,
+ Type.FLOAT);
+ return 1;
+ }
+ case ByteOps.DRETURN: {
+ visitor.visitNoArgs(ByteOps.IRETURN, offset, 1,
+ Type.DOUBLE);
+ return 1;
+ }
+ case ByteOps.ARETURN: {
+ visitor.visitNoArgs(ByteOps.IRETURN, offset, 1,
+ Type.OBJECT);
+ return 1;
+ }
+ case ByteOps.RETURN:
+ case ByteOps.ATHROW:
+ case ByteOps.MONITORENTER:
+ case ByteOps.MONITOREXIT: {
+ visitor.visitNoArgs(opcode, offset, 1, Type.VOID);
+ return 1;
+ }
+ case ByteOps.GETSTATIC:
+ case ByteOps.PUTSTATIC:
+ case ByteOps.GETFIELD:
+ case ByteOps.PUTFIELD:
+ case ByteOps.INVOKEVIRTUAL:
+ case ByteOps.INVOKESPECIAL:
+ case ByteOps.INVOKESTATIC:
+ case ByteOps.NEW:
+ case ByteOps.ANEWARRAY:
+ case ByteOps.CHECKCAST:
+ case ByteOps.INSTANCEOF: {
+ int idx = bytes.getUnsignedShort(offset + 1);
+ Constant cst = pool.get(idx);
+ visitor.visitConstant(opcode, offset, 3, cst, 0);
+ return 3;
+ }
+ case ByteOps.INVOKEINTERFACE: {
+ int idx = bytes.getUnsignedShort(offset + 1);
+ int count = bytes.getUnsignedByte(offset + 3);
+ int expectZero = bytes.getUnsignedByte(offset + 4);
+ Constant cst = pool.get(idx);
+ visitor.visitConstant(opcode, offset, 5, cst,
+ count | (expectZero << 8));
+ return 5;
+ }
+ case ByteOps.NEWARRAY: {
+ return parseNewarray(offset, visitor);
+ }
+ case ByteOps.WIDE: {
+ return parseWide(offset, visitor);
+ }
+ case ByteOps.MULTIANEWARRAY: {
+ int idx = bytes.getUnsignedShort(offset + 1);
+ int dimensions = bytes.getUnsignedByte(offset + 3);
+ Constant cst = pool.get(idx);
+ visitor.visitConstant(opcode, offset, 4, cst, dimensions);
+ return 4;
+ }
+ case ByteOps.GOTO_W:
+ case ByteOps.JSR_W: {
+ int target = offset + bytes.getInt(offset + 1);
+ int newop =
+ (opcode == ByteOps.GOTO_W) ? ByteOps.GOTO :
+ ByteOps.JSR;
+ visitor.visitBranch(newop, offset, 5, target);
+ return 5;
+ }
+ default: {
+ visitor.visitInvalid(opcode, offset, 1);
+ return 1;
+ }
+ }
+ } catch (SimException ex) {
+ ex.addContext("...at bytecode offset " + Hex.u4(offset));
+ throw ex;
+ } catch (RuntimeException ex) {
+ SimException se = new SimException(ex);
+ se.addContext("...at bytecode offset " + Hex.u4(offset));
+ throw se;
+ }
+ }
+
+ /**
+ * Helper to deal with {@code tableswitch}.
+ *
+ * @param offset the offset to the {@code tableswitch} opcode itself
+ * @param visitor {@code non-null;} visitor to use
+ * @return instruction length, in bytes
+ */
+ private int parseTableswitch(int offset, Visitor visitor) {
+ int at = (offset + 4) & ~3; // "at" skips the padding.
+
+ // Collect the padding.
+ int padding = 0;
+ for (int i = offset + 1; i < at; i++) {
+ padding = (padding << 8) | bytes.getUnsignedByte(i);
+ }
+
+ int defaultTarget = offset + bytes.getInt(at);
+ int low = bytes.getInt(at + 4);
+ int high = bytes.getInt(at + 8);
+ int count = high - low + 1;
+ at += 12;
+
+ if (low > high) {
+ throw new SimException("low / high inversion");
+ }
+
+ SwitchList cases = new SwitchList(count);
+ for (int i = 0; i < count; i++) {
+ int target = offset + bytes.getInt(at);
+ at += 4;
+ cases.add(low + i, target);
+ }
+ cases.setDefaultTarget(defaultTarget);
+ cases.removeSuperfluousDefaults();
+ cases.setImmutable();
+
+ int length = at - offset;
+ visitor.visitSwitch(ByteOps.LOOKUPSWITCH, offset, length, cases,
+ padding);
+
+ return length;
+ }
+
+ /**
+ * Helper to deal with {@code lookupswitch}.
+ *
+ * @param offset the offset to the {@code lookupswitch} opcode itself
+ * @param visitor {@code non-null;} visitor to use
+ * @return instruction length, in bytes
+ */
+ private int parseLookupswitch(int offset, Visitor visitor) {
+ int at = (offset + 4) & ~3; // "at" skips the padding.
+
+ // Collect the padding.
+ int padding = 0;
+ for (int i = offset + 1; i < at; i++) {
+ padding = (padding << 8) | bytes.getUnsignedByte(i);
+ }
+
+ int defaultTarget = offset + bytes.getInt(at);
+ int npairs = bytes.getInt(at + 4);
+ at += 8;
+
+ SwitchList cases = new SwitchList(npairs);
+ for (int i = 0; i < npairs; i++) {
+ int match = bytes.getInt(at);
+ int target = offset + bytes.getInt(at + 4);
+ at += 8;
+ cases.add(match, target);
+ }
+ cases.setDefaultTarget(defaultTarget);
+ cases.removeSuperfluousDefaults();
+ cases.setImmutable();
+
+ int length = at - offset;
+ visitor.visitSwitch(ByteOps.LOOKUPSWITCH, offset, length, cases,
+ padding);
+
+ return length;
+ }
+
+ /**
+ * Helper to deal with {@code newarray}.
+ *
+ * @param offset the offset to the {@code newarray} opcode itself
+ * @param visitor {@code non-null;} visitor to use
+ * @return instruction length, in bytes
+ */
+ private int parseNewarray(int offset, Visitor visitor) {
+ int value = bytes.getUnsignedByte(offset + 1);
+ CstType type;
+ switch (value) {
+ case ByteOps.NEWARRAY_BOOLEAN: {
+ type = CstType.BOOLEAN_ARRAY;
+ break;
+ }
+ case ByteOps.NEWARRAY_CHAR: {
+ type = CstType.CHAR_ARRAY;
+ break;
+ }
+ case ByteOps.NEWARRAY_DOUBLE: {
+ type = CstType.DOUBLE_ARRAY;
+ break;
+ }
+ case ByteOps.NEWARRAY_FLOAT: {
+ type = CstType.FLOAT_ARRAY;
+ break;
+ }
+ case ByteOps.NEWARRAY_BYTE: {
+ type = CstType.BYTE_ARRAY;
+ break;
+ }
+ case ByteOps.NEWARRAY_SHORT: {
+ type = CstType.SHORT_ARRAY;
+ break;
+ }
+ case ByteOps.NEWARRAY_INT: {
+ type = CstType.INT_ARRAY;
+ break;
+ }
+ case ByteOps.NEWARRAY_LONG: {
+ type = CstType.LONG_ARRAY;
+ break;
+ }
+ default: {
+ throw new SimException("bad newarray code " +
+ Hex.u1(value));
+ }
+ }
+
+ // Revisit the previous bytecode to find out the length of the array
+ int previousOffset = visitor.getPreviousOffset();
+ ConstantParserVisitor constantVisitor = new ConstantParserVisitor();
+ int arrayLength = 0;
+
+ /*
+ * For visitors that don't record the previous offset, -1 will be
+ * seen here
+ */
+ if (previousOffset >= 0) {
+ parseInstruction(previousOffset, constantVisitor);
+ if (constantVisitor.cst instanceof CstInteger &&
+ constantVisitor.length + previousOffset == offset) {
+ arrayLength = constantVisitor.value;
+
+ }
+ }
+
+ /*
+ * Try to match the array initialization idiom. For example, if the
+ * subsequent code is initializing an int array, we are expecting the
+ * following pattern repeatedly:
+ * dup
+ * push index
+ * push value
+ * *astore
+ *
+ * where the index value will be incrimented sequentially from 0 up.
+ */
+ int nInit = 0;
+ int curOffset = offset+2;
+ int lastOffset = curOffset;
+ ArrayList<Constant> initVals = new ArrayList<Constant>();
+
+ if (arrayLength != 0) {
+ while (true) {
+ boolean punt = false;
+
+ // First check if the next bytecode is dup
+ int nextByte = bytes.getUnsignedByte(curOffset++);
+ if (nextByte != ByteOps.DUP)
+ break;
+
+ // Next check if the expected array index is pushed to the stack
+ parseInstruction(curOffset, constantVisitor);
+ if (constantVisitor.length == 0 ||
+ !(constantVisitor.cst instanceof CstInteger) ||
+ constantVisitor.value != nInit)
+ break;
+
+ // Next, fetch the init value and record it
+ curOffset += constantVisitor.length;
+
+ // Next find out what kind of constant is pushed onto the stack
+ parseInstruction(curOffset, constantVisitor);
+ if (constantVisitor.length == 0 ||
+ !(constantVisitor.cst instanceof CstLiteralBits))
+ break;
+
+ curOffset += constantVisitor.length;
+ initVals.add(constantVisitor.cst);
+
+ nextByte = bytes.getUnsignedByte(curOffset++);
+ // Now, check if the value is stored to the array properly
+ switch (value) {
+ case ByteOps.NEWARRAY_BYTE:
+ case ByteOps.NEWARRAY_BOOLEAN: {
+ if (nextByte != ByteOps.BASTORE) {
+ punt = true;
+ }
+ break;
+ }
+ case ByteOps.NEWARRAY_CHAR: {
+ if (nextByte != ByteOps.CASTORE) {
+ punt = true;
+ }
+ break;
+ }
+ case ByteOps.NEWARRAY_DOUBLE: {
+ if (nextByte != ByteOps.DASTORE) {
+ punt = true;
+ }
+ break;
+ }
+ case ByteOps.NEWARRAY_FLOAT: {
+ if (nextByte != ByteOps.FASTORE) {
+ punt = true;
+ }
+ break;
+ }
+ case ByteOps.NEWARRAY_SHORT: {
+ if (nextByte != ByteOps.SASTORE) {
+ punt = true;
+ }
+ break;
+ }
+ case ByteOps.NEWARRAY_INT: {
+ if (nextByte != ByteOps.IASTORE) {
+ punt = true;
+ }
+ break;
+ }
+ case ByteOps.NEWARRAY_LONG: {
+ if (nextByte != ByteOps.LASTORE) {
+ punt = true;
+ }
+ break;
+ }
+ default:
+ punt = true;
+ break;
+ }
+ if (punt) {
+ break;
+ }
+ lastOffset = curOffset;
+ nInit++;
+ }
+ }
+
+ /*
+ * For singleton arrays it is still more economical to
+ * generate the aput.
+ */
+ if (nInit < 2 || nInit != arrayLength) {
+ visitor.visitNewarray(offset, 2, type, null);
+ return 2;
+ } else {
+ visitor.visitNewarray(offset, lastOffset - offset, type, initVals);
+ return lastOffset - offset;
+ }
+ }
+
+
+ /**
+ * Helper to deal with {@code wide}.
+ *
+ * @param offset the offset to the {@code wide} opcode itself
+ * @param visitor {@code non-null;} visitor to use
+ * @return instruction length, in bytes
+ */
+ private int parseWide(int offset, Visitor visitor) {
+ int opcode = bytes.getUnsignedByte(offset + 1);
+ int idx = bytes.getUnsignedShort(offset + 2);
+ switch (opcode) {
+ case ByteOps.ILOAD: {
+ visitor.visitLocal(ByteOps.ILOAD, offset, 4, idx,
+ Type.INT, 0);
+ return 4;
+ }
+ case ByteOps.LLOAD: {
+ visitor.visitLocal(ByteOps.ILOAD, offset, 4, idx,
+ Type.LONG, 0);
+ return 4;
+ }
+ case ByteOps.FLOAD: {
+ visitor.visitLocal(ByteOps.ILOAD, offset, 4, idx,
+ Type.FLOAT, 0);
+ return 4;
+ }
+ case ByteOps.DLOAD: {
+ visitor.visitLocal(ByteOps.ILOAD, offset, 4, idx,
+ Type.DOUBLE, 0);
+ return 4;
+ }
+ case ByteOps.ALOAD: {
+ visitor.visitLocal(ByteOps.ILOAD, offset, 4, idx,
+ Type.OBJECT, 0);
+ return 4;
+ }
+ case ByteOps.ISTORE: {
+ visitor.visitLocal(ByteOps.ISTORE, offset, 4, idx,
+ Type.INT, 0);
+ return 4;
+ }
+ case ByteOps.LSTORE: {
+ visitor.visitLocal(ByteOps.ISTORE, offset, 4, idx,
+ Type.LONG, 0);
+ return 4;
+ }
+ case ByteOps.FSTORE: {
+ visitor.visitLocal(ByteOps.ISTORE, offset, 4, idx,
+ Type.FLOAT, 0);
+ return 4;
+ }
+ case ByteOps.DSTORE: {
+ visitor.visitLocal(ByteOps.ISTORE, offset, 4, idx,
+ Type.DOUBLE, 0);
+ return 4;
+ }
+ case ByteOps.ASTORE: {
+ visitor.visitLocal(ByteOps.ISTORE, offset, 4, idx,
+ Type.OBJECT, 0);
+ return 4;
+ }
+ case ByteOps.RET: {
+ visitor.visitLocal(opcode, offset, 4, idx,
+ Type.RETURN_ADDRESS, 0);
+ return 4;
+ }
+ case ByteOps.IINC: {
+ int value = bytes.getShort(offset + 4);
+ visitor.visitLocal(opcode, offset, 6, idx,
+ Type.INT, value);
+ return 6;
+ }
+ default: {
+ visitor.visitInvalid(ByteOps.WIDE, offset, 1);
+ return 1;
+ }
+ }
+ }
+
+ /**
+ * Instruction visitor interface.
+ */
+ public interface Visitor {
+ /**
+ * Visits an invalid instruction.
+ *
+ * @param opcode the opcode
+ * @param offset offset to the instruction
+ * @param length length of the instruction, in bytes
+ */
+ public void visitInvalid(int opcode, int offset, int length);
+
+ /**
+ * Visits an instruction which has no inline arguments
+ * (implicit or explicit).
+ *
+ * @param opcode the opcode
+ * @param offset offset to the instruction
+ * @param length length of the instruction, in bytes
+ * @param type {@code non-null;} type the instruction operates on
+ */
+ public void visitNoArgs(int opcode, int offset, int length,
+ Type type);
+
+ /**
+ * Visits an instruction which has a local variable index argument.
+ *
+ * @param opcode the opcode
+ * @param offset offset to the instruction
+ * @param length length of the instruction, in bytes
+ * @param idx the local variable index
+ * @param type {@code non-null;} the type of the accessed value
+ * @param value additional literal integer argument, if salient (i.e.,
+ * for {@code iinc})
+ */
+ public void visitLocal(int opcode, int offset, int length,
+ int idx, Type type, int value);
+
+ /**
+ * Visits an instruction which has a (possibly synthetic)
+ * constant argument, and possibly also an
+ * additional literal integer argument. In the case of
+ * {@code multianewarray}, the argument is the count of
+ * dimensions. In the case of {@code invokeinterface},
+ * the argument is the parameter count or'ed with the
+ * should-be-zero value left-shifted by 8. In the case of entries
+ * of type {@code int}, the {@code value} field always
+ * holds the raw value (for convenience of clients).
+ *
+ * <p><b>Note:</b> In order to avoid giving it a barely-useful
+ * visitor all its own, {@code newarray} also uses this
+ * form, passing {@code value} as the array type code and
+ * {@code cst} as a {@link CstType} instance
+ * corresponding to the array type.</p>
+ *
+ * @param opcode the opcode
+ * @param offset offset to the instruction
+ * @param length length of the instruction, in bytes
+ * @param cst {@code non-null;} the constant
+ * @param value additional literal integer argument, if salient
+ * (ignore if not)
+ */
+ public void visitConstant(int opcode, int offset, int length,
+ Constant cst, int value);
+
+ /**
+ * Visits an instruction which has a branch target argument.
+ *
+ * @param opcode the opcode
+ * @param offset offset to the instruction
+ * @param length length of the instruction, in bytes
+ * @param target the absolute (not relative) branch target
+ */
+ public void visitBranch(int opcode, int offset, int length,
+ int target);
+
+ /**
+ * Visits a switch instruction.
+ *
+ * @param opcode the opcode
+ * @param offset offset to the instruction
+ * @param length length of the instruction, in bytes
+ * @param cases {@code non-null;} list of (value, target) pairs, plus the
+ * default target
+ * @param padding the bytes found in the padding area (if any),
+ * packed
+ */
+ public void visitSwitch(int opcode, int offset, int length,
+ SwitchList cases, int padding);
+
+ /**
+ * Visits a newarray instruction.
+ *
+ * @param offset offset to the instruction
+ * @param length length of the instruction, in bytes
+ * @param type {@code non-null;} the type of the array
+ * @param initVals {@code non-null;} list of bytecode offsets for init values
+ */
+ public void visitNewarray(int offset, int length, CstType type,
+ ArrayList<Constant> initVals);
+
+ /**
+ * Set previous bytecode offset
+ * @param offset offset of the previous fully parsed bytecode
+ */
+ public void setPreviousOffset(int offset);
+
+ /**
+ * Get previous bytecode offset
+ * @return return the recored offset of the previous bytecode
+ */
+ public int getPreviousOffset();
+ }
+
+ /**
+ * Base implementation of {@link Visitor}, which has empty method
+ * bodies for all methods.
+ */
+ public static class BaseVisitor implements Visitor {
+
+ /** offset of the previously parsed bytecode */
+ private int previousOffset;
+
+ BaseVisitor() {
+ previousOffset = -1;
+ }
+
+ /** {@inheritDoc} */
+ public void visitInvalid(int opcode, int offset, int length) {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ public void visitNoArgs(int opcode, int offset, int length,
+ Type type) {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ public void visitLocal(int opcode, int offset, int length,
+ int idx, Type type, int value) {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ public void visitConstant(int opcode, int offset, int length,
+ Constant cst, int value) {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ public void visitBranch(int opcode, int offset, int length,
+ int target) {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ public void visitSwitch(int opcode, int offset, int length,
+ SwitchList cases, int padding) {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ public void visitNewarray(int offset, int length, CstType type,
+ ArrayList<Constant> initValues) {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ public void setPreviousOffset(int offset) {
+ previousOffset = offset;
+ }
+
+ /** {@inheritDoc} */
+ public int getPreviousOffset() {
+ return previousOffset;
+ }
+ }
+
+ /**
+ * Base implementation of {@link Visitor}, which has empty method
+ * bodies for all methods.
+ */
+ class ConstantParserVisitor extends BaseVisitor {
+ Constant cst;
+ int length;
+ int value;
+
+ /** Empty constructor */
+ ConstantParserVisitor() {
+ }
+
+ private void clear() {
+ length = 0;
+ }
+
+ /** {@inheritDoc} */
+ public void visitInvalid(int opcode, int offset, int length) {
+ clear();
+ }
+
+ /** {@inheritDoc} */
+ public void visitNoArgs(int opcode, int offset, int length,
+ Type type) {
+ clear();
+ }
+
+ /** {@inheritDoc} */
+ public void visitLocal(int opcode, int offset, int length,
+ int idx, Type type, int value) {
+ clear();
+ }
+
+ /** {@inheritDoc} */
+ public void visitConstant(int opcode, int offset, int length,
+ Constant cst, int value) {
+ this.cst = cst;
+ this.length = length;
+ this.value = value;
+ }
+
+ /** {@inheritDoc} */
+ public void visitBranch(int opcode, int offset, int length,
+ int target) {
+ clear();
+ }
+
+ /** {@inheritDoc} */
+ public void visitSwitch(int opcode, int offset, int length,
+ SwitchList cases, int padding) {
+ clear();
+ }
+
+ /** {@inheritDoc} */
+ public void visitNewarray(int offset, int length, CstType type,
+ ArrayList<Constant> initVals) {
+ clear();
+ }
+
+ /** {@inheritDoc} */
+ public void setPreviousOffset(int offset) {
+ // Intentionally left empty
+ }
+
+ /** {@inheritDoc} */
+ public int getPreviousOffset() {
+ // Intentionally left empty
+ return -1;
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/cf/code/ConcreteMethod.java b/dx/src/com/android/dx/cf/code/ConcreteMethod.java
new file mode 100644
index 0000000..da6cff7
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/ConcreteMethod.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.cf.attrib.AttCode;
+import com.android.dx.cf.attrib.AttLineNumberTable;
+import com.android.dx.cf.attrib.AttLocalVariableTable;
+import com.android.dx.cf.attrib.AttLocalVariableTypeTable;
+import com.android.dx.cf.attrib.AttSourceFile;
+import com.android.dx.cf.iface.AttributeList;
+import com.android.dx.cf.iface.ClassFile;
+import com.android.dx.cf.iface.Method;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Prototype;
+
+/**
+ * Container for all the giblets that make up a concrete Java bytecode method.
+ * It implements {@link Method}, so it provides all the original access
+ * (by delegation), but it also constructs and keeps useful versions of
+ * stuff extracted from the method's {@code Code} attribute.
+ */
+public final class ConcreteMethod implements Method {
+ /** {@code non-null;} method being wrapped */
+ private final Method method;
+
+ /**
+ * {@code null-ok;} the class's {@code SourceFile} attribute value,
+ * if any
+ */
+ private final CstUtf8 sourceFile;
+
+ /**
+ * whether the class that this method is part of is defined with
+ * {@code ACC_SUPER}
+ */
+ private final boolean accSuper;
+
+ /** {@code non-null;} the code attribute */
+ private final AttCode attCode;
+
+ /** {@code non-null;} line number list */
+ private final LineNumberList lineNumbers;
+
+ /** {@code non-null;} local variable list */
+ private final LocalVariableList localVariables;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param method {@code non-null;} the method to be based on
+ * @param cf {@code non-null;} the class file that contains this method
+ * @param keepLines whether to keep the line number information
+ * (if any)
+ * @param keepLocals whether to keep the local variable
+ * information (if any)
+ */
+ public ConcreteMethod(Method method, ClassFile cf, boolean keepLines,
+ boolean keepLocals) {
+ this.method = method;
+ this.accSuper = (cf.getAccessFlags() & AccessFlags.ACC_SUPER) != 0;
+ this.sourceFile = cf.getSourceFile();
+
+ AttributeList attribs = method.getAttributes();
+ this.attCode = (AttCode) attribs.findFirst(AttCode.ATTRIBUTE_NAME);
+
+ AttributeList codeAttribs = attCode.getAttributes();
+
+ /*
+ * Combine all LineNumberTable attributes into one, with the
+ * combined result saved into the instance. The following code
+ * isn't particularly efficient for doing merges, but as far
+ * as I know, this situation rarely occurs "in the
+ * wild," so there's not much point in optimizing for it.
+ */
+ LineNumberList lineNumbers = LineNumberList.EMPTY;
+ if (keepLines) {
+ for (AttLineNumberTable lnt = (AttLineNumberTable)
+ codeAttribs.findFirst(AttLineNumberTable.ATTRIBUTE_NAME);
+ lnt != null;
+ lnt = (AttLineNumberTable) codeAttribs.findNext(lnt)) {
+ lineNumbers = LineNumberList.concat(lineNumbers,
+ lnt.getLineNumbers());
+ }
+ }
+ this.lineNumbers = lineNumbers;
+
+ LocalVariableList localVariables = LocalVariableList.EMPTY;
+ if (keepLocals) {
+ /*
+ * Do likewise (and with the same caveat) for
+ * LocalVariableTable and LocalVariableTypeTable attributes.
+ * This combines both of these kinds of attribute into a
+ * single LocalVariableList.
+ */
+ for (AttLocalVariableTable lvt = (AttLocalVariableTable)
+ codeAttribs.findFirst(
+ AttLocalVariableTable.ATTRIBUTE_NAME);
+ lvt != null;
+ lvt = (AttLocalVariableTable) codeAttribs.findNext(lvt)) {
+ localVariables =
+ LocalVariableList.concat(localVariables,
+ lvt.getLocalVariables());
+ }
+
+ LocalVariableList typeList = LocalVariableList.EMPTY;
+ for (AttLocalVariableTypeTable lvtt = (AttLocalVariableTypeTable)
+ codeAttribs.findFirst(
+ AttLocalVariableTypeTable.ATTRIBUTE_NAME);
+ lvtt != null;
+ lvtt =
+ (AttLocalVariableTypeTable) codeAttribs.findNext(lvtt)) {
+ typeList =
+ LocalVariableList.concat(typeList,
+ lvtt.getLocalVariables());
+ }
+
+ if (typeList.size() != 0) {
+ localVariables =
+ LocalVariableList.mergeDescriptorsAndSignatures(
+ localVariables, typeList);
+ }
+ }
+ this.localVariables = localVariables;
+ }
+
+ /** {@inheritDoc} */
+ public CstNat getNat() {
+ return method.getNat();
+ }
+
+ /** {@inheritDoc} */
+ public CstUtf8 getName() {
+ return method.getName();
+ }
+
+ /** {@inheritDoc} */
+ public CstUtf8 getDescriptor() {
+ return method.getDescriptor();
+ }
+
+ /** {@inheritDoc} */
+ public int getAccessFlags() {
+ return method.getAccessFlags();
+ }
+
+ /** {@inheritDoc} */
+ public AttributeList getAttributes() {
+ return method.getAttributes();
+ }
+
+ /** {@inheritDoc} */
+ public CstType getDefiningClass() {
+ return method.getDefiningClass();
+ }
+
+ /** {@inheritDoc} */
+ public Prototype getEffectiveDescriptor() {
+ return method.getEffectiveDescriptor();
+ }
+
+ /**
+ * Gets whether the class that this method is part of is defined with
+ * {@code ACC_SUPER}.
+ *
+ * @return the {@code ACC_SUPER} value
+ */
+ public boolean getAccSuper() {
+ return accSuper;
+ }
+
+ /**
+ * Gets the maximum stack size.
+ *
+ * @return {@code >= 0;} the maximum stack size
+ */
+ public int getMaxStack() {
+ return attCode.getMaxStack();
+ }
+
+ /**
+ * Gets the number of locals.
+ *
+ * @return {@code >= 0;} the number of locals
+ */
+ public int getMaxLocals() {
+ return attCode.getMaxLocals();
+ }
+
+ /**
+ * Gets the bytecode array.
+ *
+ * @return {@code non-null;} the bytecode array
+ */
+ public BytecodeArray getCode() {
+ return attCode.getCode();
+ }
+
+ /**
+ * Gets the exception table.
+ *
+ * @return {@code non-null;} the exception table
+ */
+ public ByteCatchList getCatches() {
+ return attCode.getCatches();
+ }
+
+ /**
+ * Gets the line number list.
+ *
+ * @return {@code non-null;} the line number list
+ */
+ public LineNumberList getLineNumbers() {
+ return lineNumbers;
+ }
+
+ /**
+ * Gets the local variable list.
+ *
+ * @return {@code non-null;} the local variable list
+ */
+ public LocalVariableList getLocalVariables() {
+ return localVariables;
+ }
+
+ /**
+ * Returns a {@link SourcePosition} instance corresponding to the
+ * given bytecode offset.
+ *
+ * @param offset {@code >= 0;} the bytecode offset
+ * @return {@code non-null;} an appropriate instance
+ */
+ public SourcePosition makeSourcePosistion(int offset) {
+ return new SourcePosition(sourceFile, offset,
+ lineNumbers.pcToLine(offset));
+ }
+}
diff --git a/dx/src/com/android/dx/cf/code/ExecutionStack.java b/dx/src/com/android/dx/cf/code/ExecutionStack.java
new file mode 100644
index 0000000..8f5b528
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/ExecutionStack.java
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.util.ExceptionWithContext;
+import com.android.dx.util.Hex;
+import com.android.dx.util.MutabilityControl;
+
+/**
+ * Representation of a Java method execution stack.
+ *
+ * <p><b>Note:</b> For the most part, the documentation for this class
+ * ignores the distinction between {@link Type} and {@link
+ * TypeBearer}.</p>
+ */
+public final class ExecutionStack extends MutabilityControl {
+ /** {@code non-null;} array of stack contents */
+ private final TypeBearer[] stack;
+
+ /**
+ * {@code >= 0;} stack pointer (points one past the end) / current stack
+ * size
+ */
+ private int stackPtr;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param maxStack {@code >= 0;} the maximum size of the stack for this
+ * instance
+ */
+ public ExecutionStack(int maxStack) {
+ super(maxStack != 0);
+ stack = new TypeBearer[maxStack];
+ stackPtr = 0;
+ }
+
+ /**
+ * Makes and returns a mutable copy of this instance.
+ *
+ * @return {@code non-null;} the copy
+ */
+ public ExecutionStack copy() {
+ ExecutionStack result = new ExecutionStack(stack.length);
+
+ System.arraycopy(stack, 0, result.stack, 0, stack.length);
+ result.stackPtr = stackPtr;
+
+ return result;
+ }
+
+ /**
+ * Annotates (adds context to) the given exception with information
+ * about this instance.
+ *
+ * @param ex {@code non-null;} the exception to annotate
+ */
+ public void annotate(ExceptionWithContext ex) {
+ int limit = stackPtr - 1;
+
+ for (int i = 0; i <= limit; i++) {
+ String idx = (i == limit) ? "top0" : Hex.u2(limit - i);
+
+ ex.addContext("stack[" + idx + "]: " +
+ stackElementString(stack[i]));
+ }
+ }
+
+ /**
+ * Replaces all the occurrences of the given uninitialized type in
+ * this stack with its initialized equivalent.
+ *
+ * @param type {@code non-null;} type to replace
+ */
+ public void makeInitialized(Type type) {
+ if (stackPtr == 0) {
+ // We have to check for this before checking for immutability.
+ return;
+ }
+
+ throwIfImmutable();
+
+ Type initializedType = type.getInitializedType();
+
+ for (int i = 0; i < stackPtr; i++) {
+ if (stack[i] == type) {
+ stack[i] = initializedType;
+ }
+ }
+ }
+
+ /**
+ * Gets the maximum stack size for this instance.
+ *
+ * @return {@code >= 0;} the max stack size
+ */
+ public int getMaxStack() {
+ return stack.length;
+ }
+
+ /**
+ * Gets the current stack size.
+ *
+ * @return {@code >= 0, < getMaxStack();} the current stack size
+ */
+ public int size() {
+ return stackPtr;
+ }
+
+ /**
+ * Clears the stack. (That is, this method pops everything off.)
+ */
+ public void clear() {
+ throwIfImmutable();
+
+ for (int i = 0; i < stackPtr; i++) {
+ stack[i] = null;
+ }
+
+ stackPtr = 0;
+ }
+
+ /**
+ * Pushes a value of the given type onto the stack.
+ *
+ * @param type {@code non-null;} type of the value
+ * @throws SimException thrown if there is insufficient room on the
+ * stack for the value
+ */
+ public void push(TypeBearer type) {
+ throwIfImmutable();
+
+ int category;
+
+ try {
+ type = type.getFrameType();
+ category = type.getType().getCategory();
+ } catch (NullPointerException ex) {
+ // Elucidate the exception.
+ throw new NullPointerException("type == null");
+ }
+
+ if ((stackPtr + category) > stack.length) {
+ throwSimException("overflow");
+ return;
+ }
+
+ if (category == 2) {
+ stack[stackPtr] = null;
+ stackPtr++;
+ }
+
+ stack[stackPtr] = type;
+ stackPtr++;
+ }
+
+ /**
+ * Peeks at the {@code n}th element down from the top of the stack.
+ * {@code n == 0} means to peek at the top of the stack. Note that
+ * this will return {@code null} if the indicated element is the
+ * deeper half of a category-2 value.
+ *
+ * @param n {@code >= 0;} which element to peek at
+ * @return {@code null-ok;} the type of value stored at that element
+ * @throws SimException thrown if {@code n >= size()}
+ */
+ public TypeBearer peek(int n) {
+ if (n < 0) {
+ throw new IllegalArgumentException("n < 0");
+ }
+
+ if (n >= stackPtr) {
+ return throwSimException("underflow");
+ }
+
+ return stack[stackPtr - n - 1];
+ }
+
+ /**
+ * Peeks at the {@code n}th element down from the top of the
+ * stack, returning the type per se, as opposed to the
+ * <i>type-bearer</i>. This method is just a convenient shorthand
+ * for {@code peek(n).getType()}.
+ *
+ * @see #peek
+ */
+ public Type peekType(int n) {
+ return peek(n).getType();
+ }
+
+ /**
+ * Pops the top element off of the stack.
+ *
+ * @return {@code non-null;} the type formerly on the top of the stack
+ * @throws SimException thrown if the stack is empty
+ */
+ public TypeBearer pop() {
+ throwIfImmutable();
+
+ TypeBearer result = peek(0);
+
+ stack[stackPtr - 1] = null;
+ stackPtr -= result.getType().getCategory();
+
+ return result;
+ }
+
+ /**
+ * Changes an element already on a stack. This method is useful in limited
+ * contexts, particularly when merging two instances. As such, it places
+ * the following restriction on its behavior: You may only replace
+ * values with other values of the same category.
+ *
+ * @param n {@code >= 0;} which element to change, where {@code 0} is
+ * the top element of the stack
+ * @param type {@code non-null;} type of the new value
+ * @throws SimException thrown if {@code n >= size()} or
+ * the action is otherwise prohibited
+ */
+ public void change(int n, TypeBearer type) {
+ throwIfImmutable();
+
+ try {
+ type = type.getFrameType();
+ } catch (NullPointerException ex) {
+ // Elucidate the exception.
+ throw new NullPointerException("type == null");
+ }
+
+ int idx = stackPtr - n - 1;
+ TypeBearer orig = stack[idx];
+
+ if ((orig == null) ||
+ (orig.getType().getCategory() != type.getType().getCategory())) {
+ throwSimException("incompatible substitution: " +
+ stackElementString(orig) + " -> " +
+ stackElementString(type));
+ }
+
+ stack[idx] = type;
+ }
+
+ /**
+ * Merges this stack with another stack. A new instance is returned if
+ * this merge results in a change. If no change results, this instance is
+ * returned. See {@link Merger#mergeStack(ExecutionStack,ExecutionStack)
+ * Merger.mergeStack()}
+ *
+ * @param other {@code non-null;} a stack to merge with
+ * @return {@code non-null;} the result of the merge
+ */
+ public ExecutionStack merge(ExecutionStack other) {
+ try {
+ return Merger.mergeStack(this, other);
+ } catch (SimException ex) {
+ ex.addContext("underlay stack:");
+ this.annotate(ex);
+ ex.addContext("overlay stack:");
+ other.annotate(ex);
+ throw ex;
+ }
+ }
+
+ /**
+ * Gets the string form for a stack element. This is the same as
+ * {@code toString()} except that {@code null} is converted
+ * to {@code "<invalid>"}.
+ *
+ * @param type {@code null-ok;} the stack element
+ * @return {@code non-null;} the string form
+ */
+ private static String stackElementString(TypeBearer type) {
+ if (type == null) {
+ return "<invalid>";
+ }
+
+ return type.toString();
+ }
+
+ /**
+ * Throws a properly-formatted exception.
+ *
+ * @param msg {@code non-null;} useful message
+ * @return never (keeps compiler happy)
+ */
+ private static TypeBearer throwSimException(String msg) {
+ throw new SimException("stack: " + msg);
+ }
+}
diff --git a/dx/src/com/android/dx/cf/code/Frame.java b/dx/src/com/android/dx/cf/code/Frame.java
new file mode 100644
index 0000000..002a4fb
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/Frame.java
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.ExceptionWithContext;
+import com.android.dx.util.IntList;
+
+/**
+ * Representation of a Java method execution frame. A frame consists
+ * of a set of locals and a value stack, and it can be told to act on
+ * them to load and store values between them and an "arguments /
+ * results" area.
+ */
+public final class Frame {
+ /** {@code non-null;} the locals */
+ private final LocalsArray locals;
+
+ /** {@code non-null;} the stack */
+ private final ExecutionStack stack;
+
+ /** {@code null-ok;} stack of labels of subroutines that this block is nested in */
+ private final IntList subroutines;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param locals {@code non-null;} the locals array to use
+ * @param stack {@code non-null;} the execution stack to use
+ */
+ private Frame(LocalsArray locals, ExecutionStack stack) {
+ this(locals, stack, IntList.EMPTY);
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param locals {@code non-null;} the locals array to use
+ * @param stack {@code non-null;} the execution stack to use
+ * @param subroutines {@code non-null;} list of subroutine start labels for
+ * subroutines this frame is nested in
+ */
+ private Frame(LocalsArray locals,
+ ExecutionStack stack, IntList subroutines) {
+ if (locals == null) {
+ throw new NullPointerException("locals == null");
+ }
+
+ if (stack == null) {
+ throw new NullPointerException("stack == null");
+ }
+
+ subroutines.throwIfMutable();
+
+ this.locals = locals;
+ this.stack = stack;
+ this.subroutines = subroutines;
+ }
+
+ /**
+ * Constructs an instance. The locals array initially consists of
+ * all-uninitialized values (represented as {@code null}s) and
+ * the stack starts out empty.
+ *
+ * @param maxLocals {@code >= 0;} the maximum number of locals this instance
+ * can refer to
+ * @param maxStack {@code >= 0;} the maximum size of the stack for this
+ * instance
+ */
+ public Frame(int maxLocals, int maxStack) {
+ this(new OneLocalsArray(maxLocals), new ExecutionStack(maxStack));
+ }
+
+ /**
+ * Makes and returns a mutable copy of this instance. The copy
+ * contains copies of the locals and stack (that is, it doesn't
+ * share them with the original).
+ *
+ * @return {@code non-null;} the copy
+ */
+ public Frame copy() {
+ return new Frame(locals.copy(), stack.copy(), subroutines);
+ }
+
+ /**
+ * Makes this instance immutable.
+ */
+ public void setImmutable() {
+ locals.setImmutable();
+ stack.setImmutable();
+ // "subroutines" is always immutable
+ }
+
+ /**
+ * Replaces all the occurrences of the given uninitialized type in
+ * this frame with its initialized equivalent.
+ *
+ * @param type {@code non-null;} type to replace
+ */
+ public void makeInitialized(Type type) {
+ locals.makeInitialized(type);
+ stack.makeInitialized(type);
+ }
+
+ /**
+ * Gets the locals array for this instance.
+ *
+ * @return {@code non-null;} the locals array
+ */
+ public LocalsArray getLocals() {
+ return locals;
+ }
+
+ /**
+ * Gets the execution stack for this instance.
+ *
+ * @return {@code non-null;} the execution stack
+ */
+ public ExecutionStack getStack() {
+ return stack;
+ }
+
+ /**
+ * Returns the largest subroutine nesting this block may be in. An
+ * empty list is returned if this block is not in any subroutine.
+ * Subroutines are identified by the label of their start block. The
+ * list is ordered such that the deepest nesting (the actual subroutine
+ * this block is in) is the last label in the list.
+ *
+ * @return {@code non-null;} list as noted above
+ */
+ public IntList getSubroutines() {
+ return subroutines;
+ }
+
+ /**
+ * Initialize this frame with the method's parameters. Used for the first
+ * frame.
+ *
+ * @param params Type list of method parameters.
+ */
+ public void initializeWithParameters(StdTypeList params) {
+ int at = 0;
+ int sz = params.size();
+
+ for (int i = 0; i < sz; i++) {
+ Type one = params.get(i);
+ locals.set(at, one);
+ at += one.getCategory();
+ }
+ }
+
+ /**
+ * Returns a Frame instance representing the frame state that should
+ * be used when returning from a subroutine. The stack state of all
+ * subroutine invocations is identical, but the locals state may differ.
+ *
+ * @param startLabel {@code >=0;} The label of the returning subroutine's
+ * start block
+ * @param subLabel {@code >=0;} A calling label of a subroutine
+ * @return {@code null-ok;} an appropriatly-constructed instance, or null
+ * if label is not in the set
+ */
+ public Frame subFrameForLabel(int startLabel, int subLabel) {
+ LocalsArray subLocals = null;
+
+ if (locals instanceof LocalsArraySet) {
+ subLocals = ((LocalsArraySet)locals).subArrayForLabel(subLabel);
+ }
+
+ IntList newSubroutines;
+ try {
+ newSubroutines = subroutines.mutableCopy();
+
+ if (newSubroutines.pop() != startLabel) {
+ throw new RuntimeException("returning from invalid subroutine");
+ }
+ newSubroutines.setImmutable();
+ } catch (IndexOutOfBoundsException ex) {
+ throw new RuntimeException("returning from invalid subroutine");
+ } catch (NullPointerException ex) {
+ throw new NullPointerException("can't return from non-subroutine");
+ }
+
+ return (subLocals == null) ? null
+ : new Frame(subLocals, stack, newSubroutines);
+ }
+
+ /**
+ * Merges two frames. If the merged result is the same as this frame,
+ * then this instance is returned.
+ *
+ * @param other {@code non-null;} another frame
+ * @return {@code non-null;} the result of merging the two frames
+ */
+ public Frame mergeWith(Frame other) {
+ LocalsArray resultLocals;
+ ExecutionStack resultStack;
+ IntList resultSubroutines;
+
+ resultLocals = getLocals().merge(other.getLocals());
+ resultStack = getStack().merge(other.getStack());
+ resultSubroutines = mergeSubroutineLists(other.subroutines);
+
+ resultLocals = adjustLocalsForSubroutines(
+ resultLocals, resultSubroutines);
+
+ if ((resultLocals == getLocals())
+ && (resultStack == getStack())
+ && subroutines == resultSubroutines) {
+ return this;
+ }
+
+ return new Frame(resultLocals, resultStack, resultSubroutines);
+ }
+
+ /**
+ * Merges this frame's subroutine lists with another. The result
+ * is the deepest common nesting (effectively, the common prefix of the
+ * two lists).
+ *
+ * @param otherSubroutines label list of subroutine start blocks, from
+ * least-nested to most-nested.
+ * @return {@code non-null;} merged subroutine nest list as described above
+ */
+ private IntList mergeSubroutineLists(IntList otherSubroutines) {
+ if (subroutines.equals(otherSubroutines)) {
+ return subroutines;
+ }
+
+ IntList resultSubroutines = new IntList();
+
+ int szSubroutines = subroutines.size();
+ int szOthers = otherSubroutines.size();
+ for (int i = 0; i < szSubroutines && i < szOthers
+ && (subroutines.get(i) == otherSubroutines.get(i)); i++) {
+ resultSubroutines.add(i);
+ }
+
+ resultSubroutines.setImmutable();
+
+ return resultSubroutines;
+ }
+
+ /**
+ * Adjusts a locals array to account for a merged subroutines list.
+ * If a frame merge results in, effectively, a subroutine return through
+ * a throw then the current locals will be a LocalsArraySet that will
+ * need to be trimmed of all OneLocalsArray elements that relevent to
+ * the subroutine that is returning.
+ *
+ * @param locals {@code non-null;} LocalsArray from before a merge
+ * @param subroutines {@code non-null;} a label list of subroutine start blocks
+ * representing the subroutine nesting of the block being merged into.
+ * @return {@code non-null;} locals set appropriate for merge
+ */
+ private static LocalsArray adjustLocalsForSubroutines(
+ LocalsArray locals, IntList subroutines) {
+ if (! (locals instanceof LocalsArraySet)) {
+ // nothing to see here
+ return locals;
+ }
+
+ LocalsArraySet laSet = (LocalsArraySet)locals;
+
+ if (subroutines.size() == 0) {
+ /*
+ * We've merged from a subroutine context to a non-subroutine
+ * context, likely via a throw. Our successor will only need
+ * to consider the primary locals state, not the state of
+ * all possible subroutine paths.
+ */
+
+ return laSet.getPrimary();
+ }
+
+ /*
+ * It's unclear to me if the locals set needs to be trimmed here.
+ * If it does, then I believe it is all of the calling blocks
+ * in the subroutine at the end of "subroutines" passed into
+ * this method that should be removed.
+ */
+ return laSet;
+ }
+
+ /**
+ * Merges this frame with the frame of a subroutine caller at
+ * {@code predLabel}. Only called on the frame at the first
+ * block of a subroutine.
+ *
+ * @param other {@code non-null;} another frame
+ * @param subLabel label of subroutine start block
+ * @param predLabel label of calling block
+ * @return {@code non-null;} the result of merging the two frames
+ */
+ public Frame mergeWithSubroutineCaller(Frame other, int subLabel,
+ int predLabel) {
+ LocalsArray resultLocals;
+ ExecutionStack resultStack;
+
+ resultLocals = getLocals().mergeWithSubroutineCaller(
+ other.getLocals(), predLabel);
+ resultStack = getStack().merge(other.getStack());
+
+ IntList newOtherSubroutines = other.subroutines.mutableCopy();
+ newOtherSubroutines.add(subLabel);
+ newOtherSubroutines.setImmutable();
+
+ if ((resultLocals == getLocals())
+ && (resultStack == getStack())
+ && subroutines.equals(newOtherSubroutines)) {
+ return this;
+ }
+
+ IntList resultSubroutines;
+
+ if (subroutines.equals(newOtherSubroutines)) {
+ resultSubroutines = subroutines;
+ } else {
+ /*
+ * The new subroutines list should be the deepest of the two
+ * lists being merged, but the postfix of the resultant list
+ * must be equal to the shorter list.
+ */
+ IntList nonResultSubroutines;
+
+ if (subroutines.size() > newOtherSubroutines.size()) {
+ resultSubroutines = subroutines;
+ nonResultSubroutines = newOtherSubroutines;
+ } else {
+ resultSubroutines = newOtherSubroutines;
+ nonResultSubroutines = subroutines;
+ }
+
+ int szResult = resultSubroutines.size();
+ int szNonResult = nonResultSubroutines.size();
+
+ for (int i = szNonResult - 1; i >=0; i-- ) {
+ if (nonResultSubroutines.get(i)
+ != resultSubroutines.get(
+ i + (szResult - szNonResult))) {
+ throw new
+ RuntimeException("Incompatible merged subroutines");
+ }
+ }
+
+ }
+
+ return new Frame(resultLocals, resultStack, resultSubroutines);
+ }
+
+ /**
+ * Makes a frame for a subroutine start block, given that this is the
+ * ending frame of one of the subroutine's calling blocks. Subroutine
+ * calls may be nested and thus may have nested locals state, so we
+ * start with an initial state as seen by the subroutine, but keep track
+ * of the individual locals states that will be expected when the individual
+ * subroutine calls return.
+ *
+ * @param subLabel label of subroutine start block
+ * @param callerLabel {@code >=0;} label of the caller block where this frame
+ * came from.
+ * @return a new instance to begin a called subroutine.
+ */
+ public Frame makeNewSubroutineStartFrame(int subLabel, int callerLabel) {
+ IntList newSubroutines = subroutines.mutableCopy();
+ newSubroutines.add(subLabel);
+ Frame newFrame = new Frame(locals.getPrimary(), stack,
+ IntList.makeImmutable(subLabel));
+ return newFrame.mergeWithSubroutineCaller(this, subLabel, callerLabel);
+ }
+
+ /**
+ * Makes a new frame for an exception handler block invoked from this
+ * frame.
+ *
+ * @param exceptionClass exception that the handler block will handle
+ * @return new frame
+ */
+ public Frame makeExceptionHandlerStartFrame(CstType exceptionClass) {
+ ExecutionStack newStack = getStack().copy();
+
+ newStack.clear();
+ newStack.push(exceptionClass);
+
+ return new Frame(getLocals(), newStack, subroutines);
+ }
+
+ /**
+ * Annotates (adds context to) the given exception with information
+ * about this frame.
+ *
+ * @param ex {@code non-null;} the exception to annotate
+ */
+ public void annotate(ExceptionWithContext ex) {
+ locals.annotate(ex);
+ stack.annotate(ex);
+ }
+}
diff --git a/dx/src/com/android/dx/cf/code/LineNumberList.java b/dx/src/com/android/dx/cf/code/LineNumberList.java
new file mode 100644
index 0000000..f54f8b5
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/LineNumberList.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * List of "line number" entries, which are the contents of
+ * {@code LineNumberTable} attributes.
+ */
+public final class LineNumberList extends FixedSizeList {
+ /** {@code non-null;} zero-size instance */
+ public static final LineNumberList EMPTY = new LineNumberList(0);
+
+ /**
+ * Returns an instance which is the concatenation of the two given
+ * instances.
+ *
+ * @param list1 {@code non-null;} first instance
+ * @param list2 {@code non-null;} second instance
+ * @return {@code non-null;} combined instance
+ */
+ public static LineNumberList concat(LineNumberList list1,
+ LineNumberList list2) {
+ if (list1 == EMPTY) {
+ // easy case
+ return list2;
+ }
+
+ int sz1 = list1.size();
+ int sz2 = list2.size();
+ LineNumberList result = new LineNumberList(sz1 + sz2);
+
+ for (int i = 0; i < sz1; i++) {
+ result.set(i, list1.get(i));
+ }
+
+ for (int i = 0; i < sz2; i++) {
+ result.set(sz1 + i, list2.get(i));
+ }
+
+ return result;
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param count the number of elements to be in the list
+ */
+ public LineNumberList(int count) {
+ super(count);
+ }
+
+ /**
+ * Gets the indicated item.
+ *
+ * @param n {@code >= 0;} which item
+ * @return {@code null-ok;} the indicated item
+ */
+ public Item get(int n) {
+ return (Item) get0(n);
+ }
+
+ /**
+ * Sets the item at the given index.
+ *
+ * @param n {@code >= 0, < size();} which element
+ * @param item {@code non-null;} the item
+ */
+ public void set(int n, Item item) {
+ if (item == null) {
+ throw new NullPointerException("item == null");
+ }
+
+ set0(n, item);
+ }
+
+ /**
+ * Sets the item at the given index.
+ *
+ * @param n {@code >= 0, < size();} which element
+ * @param startPc {@code >= 0;} start pc of this item
+ * @param lineNumber {@code >= 0;} corresponding line number
+ */
+ public void set(int n, int startPc, int lineNumber) {
+ set0(n, new Item(startPc, lineNumber));
+ }
+
+ /**
+ * Gets the line number associated with the given address.
+ *
+ * @param pc {@code >= 0;} the address to look up
+ * @return {@code >= -1;} the associated line number, or {@code -1} if
+ * none is known
+ */
+ public int pcToLine(int pc) {
+ /*
+ * Line number entries don't have to appear in any particular
+ * order, so we have to do a linear search. TODO: If
+ * this turns out to be a bottleneck, consider sorting the
+ * list prior to use.
+ */
+ int sz = size();
+ int bestPc = -1;
+ int bestLine = -1;
+
+ for (int i = 0; i < sz; i++) {
+ Item one = get(i);
+ int onePc = one.getStartPc();
+ if ((onePc <= pc) && (onePc > bestPc)) {
+ bestPc = onePc;
+ bestLine = one.getLineNumber();
+ if (bestPc == pc) {
+ // We can't do better than this
+ break;
+ }
+ }
+ }
+
+ return bestLine;
+ }
+
+ /**
+ * Item in a line number table.
+ */
+ public static class Item {
+ /** {@code >= 0;} start pc of this item */
+ private final int startPc;
+
+ /** {@code >= 0;} corresponding line number */
+ private final int lineNumber;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param startPc {@code >= 0;} start pc of this item
+ * @param lineNumber {@code >= 0;} corresponding line number
+ */
+ public Item(int startPc, int lineNumber) {
+ if (startPc < 0) {
+ throw new IllegalArgumentException("startPc < 0");
+ }
+
+ if (lineNumber < 0) {
+ throw new IllegalArgumentException("lineNumber < 0");
+ }
+
+ this.startPc = startPc;
+ this.lineNumber = lineNumber;
+ }
+
+ /**
+ * Gets the start pc of this item.
+ *
+ * @return the start pc
+ */
+ public int getStartPc() {
+ return startPc;
+ }
+
+ /**
+ * Gets the line number of this item.
+ *
+ * @return the line number
+ */
+ public int getLineNumber() {
+ return lineNumber;
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/cf/code/LocalVariableList.java b/dx/src/com/android/dx/cf/code/LocalVariableList.java
new file mode 100644
index 0000000..dbf8ba2
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/LocalVariableList.java
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.code.LocalItem;
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * List of "local variable" entries, which are the contents of
+ * {@code LocalVariableTable} and {@code LocalVariableTypeTable}
+ * attributes, as well as combinations of the two.
+ */
+public final class LocalVariableList extends FixedSizeList {
+ /** {@code non-null;} zero-size instance */
+ public static final LocalVariableList EMPTY = new LocalVariableList(0);
+
+ /**
+ * Returns an instance which is the concatenation of the two given
+ * instances. The result is immutable.
+ *
+ * @param list1 {@code non-null;} first instance
+ * @param list2 {@code non-null;} second instance
+ * @return {@code non-null;} combined instance
+ */
+ public static LocalVariableList concat(LocalVariableList list1,
+ LocalVariableList list2) {
+ if (list1 == EMPTY) {
+ // easy case
+ return list2;
+ }
+
+ int sz1 = list1.size();
+ int sz2 = list2.size();
+ LocalVariableList result = new LocalVariableList(sz1 + sz2);
+
+ for (int i = 0; i < sz1; i++) {
+ result.set(i, list1.get(i));
+ }
+
+ for (int i = 0; i < sz2; i++) {
+ result.set(sz1 + i, list2.get(i));
+ }
+
+ result.setImmutable();
+ return result;
+ }
+
+ /**
+ * Returns an instance which is the result of merging the two
+ * given instances, where one instance should have only type
+ * descriptors and the other only type signatures. The merged
+ * result is identical to the one with descriptors, except that
+ * any element whose {name, index, start, length} matches an
+ * element in the signature list gets augmented with the
+ * corresponding signature. The result is immutable.
+ *
+ * @param descriptorList {@code non-null;} list with descriptors
+ * @param signatureList {@code non-null;} list with signatures
+ * @return {@code non-null;} the merged result
+ */
+ public static LocalVariableList mergeDescriptorsAndSignatures(
+ LocalVariableList descriptorList,
+ LocalVariableList signatureList) {
+ int descriptorSize = descriptorList.size();
+ LocalVariableList result = new LocalVariableList(descriptorSize);
+
+ for (int i = 0; i < descriptorSize; i++) {
+ Item item = descriptorList.get(i);
+ Item signatureItem = signatureList.itemToLocal(item);
+ if (signatureItem != null) {
+ CstUtf8 signature = signatureItem.getSignature();
+ item = item.withSignature(signature);
+ }
+ result.set(i, item);
+ }
+
+ result.setImmutable();
+ return result;
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param count the number of elements to be in the list
+ */
+ public LocalVariableList(int count) {
+ super(count);
+ }
+
+ /**
+ * Gets the indicated item.
+ *
+ * @param n {@code >= 0;} which item
+ * @return {@code null-ok;} the indicated item
+ */
+ public Item get(int n) {
+ return (Item) get0(n);
+ }
+
+ /**
+ * Sets the item at the given index.
+ *
+ * @param n {@code >= 0, < size();} which element
+ * @param item {@code non-null;} the item
+ */
+ public void set(int n, Item item) {
+ if (item == null) {
+ throw new NullPointerException("item == null");
+ }
+
+ set0(n, item);
+ }
+
+ /**
+ * Sets the item at the given index.
+ *
+ * <p><b>Note:</b> At least one of {@code descriptor} or
+ * {@code signature} must be passed as non-null.</p>
+ *
+ * @param n {@code >= 0, < size();} which element
+ * @param startPc {@code >= 0;} the start pc of this variable's scope
+ * @param length {@code >= 0;} the length (in bytecodes) of this variable's
+ * scope
+ * @param name {@code non-null;} the variable's name
+ * @param descriptor {@code null-ok;} the variable's type descriptor
+ * @param signature {@code null-ok;} the variable's type signature
+ * @param index {@code >= 0;} the variable's local index
+ */
+ public void set(int n, int startPc, int length, CstUtf8 name,
+ CstUtf8 descriptor, CstUtf8 signature, int index) {
+ set0(n, new Item(startPc, length, name, descriptor, signature, index));
+ }
+
+ /**
+ * Gets the local variable information in this instance which matches
+ * the given {@link com.android.dx.cf.code.LocalVariableList.Item}
+ * in all respects but the type descriptor and signature, if any.
+ *
+ * @param item {@code non-null;} local variable information to match
+ * @return {@code null-ok;} the corresponding local variable information stored
+ * in this instance, or {@code null} if there is no matching
+ * information
+ */
+ public Item itemToLocal(Item item) {
+ int sz = size();
+
+ for (int i = 0; i < sz; i++) {
+ Item one = (Item) get0(i);
+
+ if ((one != null) && one.matchesAllButType(item)) {
+ return one;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Gets the local variable information associated with a given address
+ * and local index, if any. <b>Note:</b> In standard classfiles, a
+ * variable's start point is listed as the address of the instruction
+ * <i>just past</i> the one that sets the variable.
+ *
+ * @param pc {@code >= 0;} the address to look up
+ * @param index {@code >= 0;} the local variable index
+ * @return {@code null-ok;} the associated local variable information, or
+ * {@code null} if none is known
+ */
+ public Item pcAndIndexToLocal(int pc, int index) {
+ int sz = size();
+
+ for (int i = 0; i < sz; i++) {
+ Item one = (Item) get0(i);
+
+ if ((one != null) && one.matchesPcAndIndex(pc, index)) {
+ return one;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Item in a local variable table.
+ */
+ public static class Item {
+ /** {@code >= 0;} the start pc of this variable's scope */
+ private final int startPc;
+
+ /** {@code >= 0;} the length (in bytecodes) of this variable's scope */
+ private final int length;
+
+ /** {@code non-null;} the variable's name */
+ private final CstUtf8 name;
+
+ /** {@code null-ok;} the variable's type descriptor */
+ private final CstUtf8 descriptor;
+
+ /** {@code null-ok;} the variable's type signature */
+ private final CstUtf8 signature;
+
+ /** {@code >= 0;} the variable's local index */
+ private final int index;
+
+ /**
+ * Constructs an instance.
+ *
+ * <p><b>Note:</b> At least one of {@code descriptor} or
+ * {@code signature} must be passed as non-null.</p>
+ *
+ * @param startPc {@code >= 0;} the start pc of this variable's scope
+ * @param length {@code >= 0;} the length (in bytecodes) of this variable's
+ * scope
+ * @param name {@code non-null;} the variable's name
+ * @param descriptor {@code null-ok;} the variable's type descriptor
+ * @param signature {@code null-ok;} the variable's type signature
+ * @param index {@code >= 0;} the variable's local index
+ */
+ public Item(int startPc, int length, CstUtf8 name,
+ CstUtf8 descriptor, CstUtf8 signature, int index) {
+ if (startPc < 0) {
+ throw new IllegalArgumentException("startPc < 0");
+ }
+
+ if (length < 0) {
+ throw new IllegalArgumentException("length < 0");
+ }
+
+ if (name == null) {
+ throw new NullPointerException("name == null");
+ }
+
+ if ((descriptor == null) && (signature == null)) {
+ throw new NullPointerException(
+ "(descriptor == null) && (signature == null)");
+ }
+
+ if (index < 0) {
+ throw new IllegalArgumentException("index < 0");
+ }
+
+ this.startPc = startPc;
+ this.length = length;
+ this.name = name;
+ this.descriptor = descriptor;
+ this.signature = signature;
+ this.index = index;
+ }
+
+ /**
+ * Gets the start pc of this variable's scope.
+ *
+ * @return {@code >= 0;} the start pc of this variable's scope
+ */
+ public int getStartPc() {
+ return startPc;
+ }
+
+ /**
+ * Gets the length (in bytecodes) of this variable's scope.
+ *
+ * @return {@code >= 0;} the length (in bytecodes) of this variable's scope
+ */
+ public int getLength() {
+ return length;
+ }
+
+ /**
+ * Gets the variable's type descriptor.
+ *
+ * @return {@code null-ok;} the variable's type descriptor
+ */
+ public CstUtf8 getDescriptor() {
+ return descriptor;
+ }
+
+ /**
+ * Gets the variable's LocalItem, a (name, signature) tuple
+ *
+ * @return {@code null-ok;} the variable's type descriptor
+ */
+ public LocalItem getLocalItem() {
+ return LocalItem.make(name, signature);
+ }
+
+ /**
+ * Gets the variable's type signature. Private because if you need this,
+ * you want getLocalItem() instead.
+ *
+ * @return {@code null-ok;} the variable's type signature
+ */
+ private CstUtf8 getSignature() {
+ return signature;
+ }
+
+ /**
+ * Gets the variable's local index.
+ *
+ * @return {@code >= 0;} the variable's local index
+ */
+ public int getIndex() {
+ return index;
+ }
+
+ /**
+ * Gets the variable's type descriptor. This is a convenient shorthand
+ * for {@code Type.intern(getDescriptor().getString())}.
+ *
+ * @return {@code non-null;} the variable's type
+ */
+ public Type getType() {
+ return Type.intern(descriptor.getString());
+ }
+
+ /**
+ * Constructs and returns an instance which is identical to this
+ * one, except that the signature is changed to the given value.
+ *
+ * @param newSignature {@code non-null;} the new signature
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public Item withSignature(CstUtf8 newSignature) {
+ return new Item(startPc, length, name, descriptor, newSignature,
+ index);
+ }
+
+ /**
+ * Gets whether this instance matches (describes) the given
+ * address and index.
+ *
+ * @param pc {@code >= 0;} the address in question
+ * @param index {@code >= 0;} the local variable index in question
+ * @return {@code true} iff this instance matches {@code pc}
+ * and {@code index}
+ */
+ public boolean matchesPcAndIndex(int pc, int index) {
+ return (index == this.index) &&
+ (pc >= startPc) &&
+ (pc < (startPc + length));
+ }
+
+ /**
+ * Gets whether this instance matches (describes) the given
+ * other instance exactly in all fields except type descriptor and
+ * type signature.
+ *
+ * @param other {@code non-null;} the instance to compare to
+ * @return {@code true} iff this instance matches
+ */
+ public boolean matchesAllButType(Item other) {
+ return (startPc == other.startPc)
+ && (length == other.length)
+ && (index == other.index)
+ && name.equals(other.name);
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/cf/code/LocalsArray.java b/dx/src/com/android/dx/cf/code/LocalsArray.java
new file mode 100644
index 0000000..75af047
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/LocalsArray.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.util.ExceptionWithContext;
+import com.android.dx.util.Hex;
+import com.android.dx.util.MutabilityControl;
+import com.android.dx.util.ToHuman;
+
+/**
+ * Representation of an array of local variables, with Java semantics.
+ *
+ * <p><b>Note:</b> For the most part, the documentation for this class
+ * ignores the distinction between {@link Type} and {@link
+ * TypeBearer}.</p>
+ */
+public abstract class LocalsArray extends MutabilityControl implements ToHuman {
+
+ /**
+ * Constructs an instance, explicitly indicating the mutability.
+ *
+ * @param mutable {@code true} if this instance is mutable
+ */
+ protected LocalsArray(boolean mutable) {
+ super(mutable);
+ }
+
+ /**
+ * Makes and returns a mutable copy of this instance.
+ *
+ * @return {@code non-null;} the copy
+ */
+ public abstract LocalsArray copy();
+
+ /**
+ * Annotates (adds context to) the given exception with information
+ * about this instance.
+ *
+ * @param ex {@code non-null;} the exception to annotate
+ */
+ public abstract void annotate(ExceptionWithContext ex);
+
+ /**
+ * Replaces all the occurrences of the given uninitialized type in
+ * this array with its initialized equivalent.
+ *
+ * @param type {@code non-null;} type to replace
+ */
+ public abstract void makeInitialized(Type type);
+
+ /**
+ * Gets the maximum number of locals this instance can refer to.
+ *
+ * @return the max locals
+ */
+ public abstract int getMaxLocals();
+
+ /**
+ * Sets the type stored at the given local index. If the given type
+ * is category-2, then (a) the index must be at least two less than
+ * {@link #getMaxLocals} and (b) the next index gets invalidated
+ * by the operation. In case of either category, if the <i>previous</i>
+ * local contains a category-2 value, then it too is invalidated by
+ * this operation.
+ *
+ * @param idx {@code >= 0, < getMaxLocals();} which local
+ * @param type {@code non-null;} new type for the local at {@code idx}
+ */
+ public abstract void set(int idx, TypeBearer type);
+
+ /**
+ * Sets the type for the local indicated by the given register spec
+ * to that register spec (which includes type and optional name
+ * information). This is identical to calling
+ * {@code set(spec.getReg(), spec)}.
+ *
+ * @param spec {@code non-null;} register spec to use as the basis for the update
+ */
+ public abstract void set(RegisterSpec spec);
+
+ /**
+ * Invalidates the local at the given index.
+ *
+ * @param idx {@code >= 0, < getMaxLocals();} which local
+ */
+ public abstract void invalidate(int idx);
+
+ /**
+ * Gets the type stored at the given local index, or {@code null}
+ * if the given local is uninitialized / invalid.
+ *
+ * @param idx {@code >= 0, < getMaxLocals();} which local
+ * @return {@code null-ok;} the type of value stored in that local
+ */
+ public abstract TypeBearer getOrNull(int idx);
+
+ /**
+ * Gets the type stored at the given local index, only succeeding if
+ * the given local contains a valid type (though it is allowed to
+ * be an uninitialized instance).
+ *
+ * @param idx {@code >= 0, < getMaxLocals();} which local
+ * @return {@code non-null;} the type of value stored in that local
+ * @throws SimException thrown if {@code idx} is valid, but
+ * the contents are invalid
+ */
+ public abstract TypeBearer get(int idx);
+
+ /**
+ * Gets the type stored at the given local index, which is expected
+ * to be an initialized category-1 value.
+ *
+ * @param idx {@code >= 0, < getMaxLocals();} which local
+ * @return {@code non-null;} the type of value stored in that local
+ * @throws SimException thrown if {@code idx} is valid, but
+ * one of the following holds: (a) the local is invalid; (b) the local
+ * contains an uninitialized instance; (c) the local contains a
+ * category-2 value
+ */
+ public abstract TypeBearer getCategory1(int idx);
+
+ /**
+ * Gets the type stored at the given local index, which is expected
+ * to be a category-2 value.
+ *
+ * @param idx {@code >= 0, < getMaxLocals();} which local
+ * @return {@code non-null;} the type of value stored in that local
+ * @throws SimException thrown if {@code idx} is valid, but
+ * one of the following holds: (a) the local is invalid; (b) the local
+ * contains a category-1 value
+ */
+ public abstract TypeBearer getCategory2(int idx);
+
+ /**
+ * Merges this instance with {@code other}. If the merged result is
+ * the same as this instance, then this is returned (not a copy).
+ *
+ * @param other {@code non-null;} another LocalsArray
+ * @return {@code non-null;} the merge result, a new instance or this
+ */
+ public abstract LocalsArray merge(LocalsArray other);
+
+ /**
+ * Merges this instance with a {@code LocalsSet} from a subroutine
+ * caller. To be used when merging in the first block of a subroutine.
+ *
+ * @param other {@code other non-null;} another LocalsArray. The final locals
+ * state of a subroutine caller.
+ * @param predLabel the label of the subroutine caller block.
+ * @return {@code non-null;} the merge result, a new instance or this
+ */
+ public abstract LocalsArraySet mergeWithSubroutineCaller
+ (LocalsArray other, int predLabel);
+
+ /**
+ * Gets the locals set appropriate for the current execution context.
+ * That is, if this is a {@code OneLocalsArray} instance, then return
+ * {@code this}, otherwise return {@code LocalsArraySet}'s
+ * primary.
+ *
+ * @return locals for this execution context.
+ */
+ protected abstract OneLocalsArray getPrimary();
+
+}
diff --git a/dx/src/com/android/dx/cf/code/LocalsArraySet.java b/dx/src/com/android/dx/cf/code/LocalsArraySet.java
new file mode 100644
index 0000000..5d03055
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/LocalsArraySet.java
@@ -0,0 +1,462 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.util.ExceptionWithContext;
+import com.android.dx.util.Hex;
+import com.android.dx.util.MutabilityControl;
+
+import java.util.ArrayList;
+
+/**
+ * Representation of a set of local variable arrays, with Java semantics.
+ * This peculiar case is to support in-method subroutines, which can
+ * have different locals sets for each caller.
+ *
+ * <p><b>Note:</b> For the most part, the documentation for this class
+ * ignores the distinction between {@link com.android.dx.rop.type.Type} and {@link
+ * com.android.dx.rop.type.TypeBearer}.</p>
+ */
+public class LocalsArraySet extends LocalsArray {
+
+ /**
+ * The primary LocalsArray represents the locals as seen from
+ * the subroutine itself, which is the merged representation of all the
+ * individual locals states.
+ */
+ private final OneLocalsArray primary;
+
+ /**
+ * Indexed by label of caller block: the locals specific to each caller's
+ * invocation of the subroutine.
+ */
+ private final ArrayList<LocalsArray> secondaries;
+
+ /**
+ * Constructs an instance. The locals array initially consists of
+ * all-uninitialized values (represented as {@code null}s).
+ *
+ * @param maxLocals {@code >= 0;} the maximum number of locals this instance
+ * can refer to
+ */
+ public LocalsArraySet(int maxLocals) {
+ super(maxLocals != 0);
+ primary = new OneLocalsArray(maxLocals);
+ secondaries = new ArrayList();
+ }
+
+ /**
+ * Constructs an instance with the specified primary and secondaries set.
+ *
+ * @param primary {@code non-null;} primary locals to use
+ * @param secondaries {@code non-null;} secondaries set, indexed by subroutine
+ * caller label.
+ */
+ public LocalsArraySet(OneLocalsArray primary,
+ ArrayList<LocalsArray> secondaries) {
+ super(primary.getMaxLocals() > 0);
+
+ this.primary = primary;
+ this.secondaries = secondaries;
+ }
+
+ /**
+ * Constructs an instance which is a copy of another.
+ *
+ * @param toCopy {@code non-null;} instance to copy.
+ */
+ private LocalsArraySet(LocalsArraySet toCopy) {
+ super(toCopy.getMaxLocals() > 0);
+
+ primary = toCopy.primary.copy();
+ secondaries = new ArrayList(toCopy.secondaries.size());
+
+ int sz = toCopy.secondaries.size();
+ for (int i = 0; i < sz; i++) {
+ LocalsArray la = toCopy.secondaries.get(i);
+
+ if (la == null) {
+ secondaries.add(null);
+ } else {
+ secondaries.add(la.copy());
+ }
+ }
+ }
+
+
+ /** @inheritDoc */
+ @Override
+ public void setImmutable() {
+ primary.setImmutable();
+
+ for (LocalsArray la : secondaries) {
+ if (la != null) {
+ la.setImmutable();
+ }
+ }
+ super.setImmutable();
+ }
+
+ /** @inheritDoc */
+ @Override
+ public LocalsArray copy() {
+ return new LocalsArraySet(this);
+ }
+
+ /** @inheritDoc */
+ @Override
+ public void annotate(ExceptionWithContext ex) {
+ ex.addContext("(locals array set; primary)");
+ primary.annotate(ex);
+
+ int sz = secondaries.size();
+ for (int label = 0; label < sz; label++) {
+ LocalsArray la = secondaries.get(label);
+
+ if (la != null) {
+ ex.addContext("(locals array set: primary for caller "
+ + Hex.u2(label) + ')');
+
+ la.getPrimary().annotate(ex);
+ }
+ }
+ }
+
+ /** {@inheritDoc*/
+ public String toHuman() {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append("(locals array set; primary)\n");
+
+ sb.append(getPrimary().toHuman());
+ sb.append('\n');
+
+ int sz = secondaries.size();
+ for (int label = 0; label < sz; label++) {
+ LocalsArray la = secondaries.get(label);
+
+ if (la != null) {
+ sb.append("(locals array set: primary for caller "
+ + Hex.u2(label) + ")\n");
+
+ sb.append(la.getPrimary().toHuman());
+ sb.append('\n');
+ }
+ }
+
+ return sb.toString();
+ }
+
+ /** @inheritDoc */
+ @Override
+ public void makeInitialized(Type type) {
+ int len = primary.getMaxLocals();
+
+ if (len == 0) {
+ // We have to check for this before checking for immutability.
+ return;
+ }
+
+ throwIfImmutable();
+
+ primary.makeInitialized(type);
+
+ for (LocalsArray la : secondaries) {
+ if (la != null) {
+ la.makeInitialized(type);
+ }
+ }
+ }
+
+ /** @inheritDoc */
+ @Override
+ public int getMaxLocals() {
+ return primary.getMaxLocals();
+ }
+
+ /** @inheritDoc */
+ @Override
+ public void set(int idx, TypeBearer type) {
+ throwIfImmutable();
+
+ primary.set(idx, type);
+
+ for (LocalsArray la : secondaries) {
+ if (la != null) {
+ la.set(idx, type);
+ }
+ }
+ }
+
+ /** @inheritDoc */
+ @Override
+ public void set(RegisterSpec spec) {
+ set(spec.getReg(), spec);
+ }
+
+ /** @inheritDoc */
+ @Override
+ public void invalidate(int idx) {
+ throwIfImmutable();
+
+ primary.invalidate(idx);
+
+ for (LocalsArray la : secondaries) {
+ if (la != null) {
+ la.invalidate(idx);
+ }
+ }
+ }
+
+ /** @inheritDoc */
+ @Override
+ public TypeBearer getOrNull(int idx) {
+ return primary.getOrNull(idx);
+ }
+
+ /** @inheritDoc */
+ @Override
+ public TypeBearer get(int idx) {
+ return primary.get(idx);
+ }
+
+ /** @inheritDoc */
+ @Override
+ public TypeBearer getCategory1(int idx) {
+ return primary.getCategory1(idx);
+ }
+
+ /** @inheritDoc */
+ @Override
+ public TypeBearer getCategory2(int idx) {
+ return primary.getCategory2(idx);
+ }
+
+ /**
+ * Merges this set with another {@code LocalsArraySet} instance.
+ *
+ * @param other {@code non-null;} to merge
+ * @return {@code non-null;} this instance if merge was a no-op, or
+ * new merged instance.
+ */
+ private LocalsArraySet mergeWithSet(LocalsArraySet other) {
+ OneLocalsArray newPrimary;
+ ArrayList<LocalsArray> newSecondaries;
+ boolean secondariesChanged = false;
+
+ newPrimary = primary.merge(other.getPrimary());
+
+ int sz1 = secondaries.size();
+ int sz2 = other.secondaries.size();
+ int sz = Math.max(sz1, sz2);
+ newSecondaries = new ArrayList(sz);
+
+ for (int i = 0; i < sz; i++) {
+ LocalsArray la1 = (i < sz1 ? secondaries.get(i) : null);
+ LocalsArray la2 = (i < sz2 ? other.secondaries.get(i) : null);
+ LocalsArray resultla = null;
+
+ if (la1 == la2) {
+ resultla = la1;
+ } else if (la1 == null) {
+ resultla = la2;
+ } else if (la2 == null) {
+ resultla = la1;
+ } else {
+ try {
+ resultla = la1.merge(la2);
+ } catch (SimException ex) {
+ ex.addContext(
+ "Merging locals set for caller block " + Hex.u2(i));
+ }
+ }
+
+ secondariesChanged = secondariesChanged || (la1 != resultla);
+
+ newSecondaries.add(resultla);
+ }
+
+ if ((primary == newPrimary) && ! secondariesChanged ) {
+ return this;
+ }
+
+ return new LocalsArraySet(newPrimary, newSecondaries);
+ }
+
+ /**
+ * Merges this set with a {@code OneLocalsArray} instance.
+ *
+ * @param other {@code non-null;} to merge
+ * @return {@code non-null;} this instance if merge was a no-op, or
+ * new merged instance.
+ */
+ private LocalsArraySet mergeWithOne(OneLocalsArray other) {
+ OneLocalsArray newPrimary;
+ ArrayList<LocalsArray> newSecondaries;
+ boolean secondariesChanged = false;
+
+ newPrimary = primary.merge(other.getPrimary());
+ newSecondaries = new ArrayList(secondaries.size());
+
+ int sz = secondaries.size();
+ for (int i = 0; i < sz; i++) {
+ LocalsArray la = secondaries.get(i);
+ LocalsArray resultla = null;
+
+ if (la != null) {
+ try {
+ resultla = la.merge(other);
+ } catch (SimException ex) {
+ ex.addContext("Merging one locals against caller block "
+ + Hex.u2(i));
+ }
+ }
+
+ secondariesChanged = secondariesChanged || (la != resultla);
+
+ newSecondaries.add(resultla);
+ }
+
+ if ((primary == newPrimary) && ! secondariesChanged ) {
+ return this;
+ }
+
+ return new LocalsArraySet(newPrimary, newSecondaries);
+ }
+
+ /** @inheritDoc */
+ @Override
+ public LocalsArraySet merge(LocalsArray other) {
+ LocalsArraySet result;
+
+ try {
+ if (other instanceof LocalsArraySet) {
+ result = mergeWithSet((LocalsArraySet) other);
+ } else {
+ result = mergeWithOne((OneLocalsArray) other);
+ }
+ } catch (SimException ex) {
+ ex.addContext("underlay locals:");
+ annotate(ex);
+ ex.addContext("overlay locals:");
+ other.annotate(ex);
+ throw ex;
+ }
+
+ result.setImmutable();
+ return result;
+ }
+
+ /**
+ * Gets the {@code LocalsArray} instance for a specified subroutine
+ * caller label, or null if label has no locals associated with it.
+ *
+ * @param label {@code >= 0;} subroutine caller label
+ * @return {@code null-ok;} locals if available.
+ */
+ private LocalsArray getSecondaryForLabel(int label) {
+ if (label >= secondaries.size()) {
+ return null;
+ }
+
+ return secondaries.get(label);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public LocalsArraySet mergeWithSubroutineCaller
+ (LocalsArray other, int predLabel) {
+
+ LocalsArray mine = getSecondaryForLabel(predLabel);
+ LocalsArray newSecondary;
+ OneLocalsArray newPrimary;
+
+ newPrimary = primary.merge(other.getPrimary());
+
+ if (mine == other) {
+ newSecondary = mine;
+ } else if (mine == null) {
+ newSecondary = other;
+ } else {
+ newSecondary = mine.merge(other);
+ }
+
+ if ((newSecondary == mine) && (newPrimary == primary)) {
+ return this;
+ } else {
+ /*
+ * We're going to re-build a primary as a merge of all the
+ * secondaries.
+ */
+ newPrimary = null;
+
+ int szSecondaries = secondaries.size();
+ int sz = Math.max(predLabel + 1, szSecondaries);
+ ArrayList<LocalsArray> newSecondaries = new ArrayList(sz);
+ for (int i = 0; i < sz; i++) {
+ LocalsArray la = null;
+
+ if (i == predLabel) {
+ /*
+ * This LocalsArray always replaces any existing one,
+ * since this is the result of a refined iteration.
+ */
+ la = newSecondary;
+ } else if (i < szSecondaries) {
+ la = secondaries.get(i);
+ }
+
+ if (la != null) {
+ if (newPrimary == null) {
+ newPrimary = la.getPrimary();
+ } else {
+ newPrimary = newPrimary.merge(la.getPrimary());
+ }
+ }
+
+ newSecondaries.add(la);
+ }
+
+ LocalsArraySet result
+ = new LocalsArraySet(newPrimary, newSecondaries);
+ result.setImmutable();
+ return result;
+ }
+ }
+
+ /**
+ * Returns a LocalsArray instance representing the locals state that should
+ * be used when returning to a subroutine caller.
+ *
+ * @param subLabel {@code >= 0;} A calling label of a subroutine
+ * @return {@code null-ok;} an instance for this subroutine, or null if subroutine
+ * is not in this set.
+ */
+ public LocalsArray subArrayForLabel(int subLabel) {
+ LocalsArray result = getSecondaryForLabel(subLabel);
+ return result;
+ }
+
+ /**{@inheritDoc}*/
+ @Override
+ protected OneLocalsArray getPrimary() {
+ return primary;
+ }
+}
diff --git a/dx/src/com/android/dx/cf/code/Machine.java b/dx/src/com/android/dx/cf/code/Machine.java
new file mode 100644
index 0000000..72ba3b4
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/Machine.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.type.Prototype;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.code.LocalItem;
+import java.util.ArrayList;
+
+/**
+ * Interface for machines capable of executing bytecode by acting
+ * upon a {@link Frame}. A machine conceptually contains four arbitrary-value
+ * argument slots, slots for several literal-value arguments, and slots for
+ * branch target information.
+ */
+public interface Machine {
+ /**
+ * Gets the effective prototype of the method that this instance is
+ * being used for. The <i>effective</i> prototype includes an initial
+ * {@code this} argument for instance methods.
+ *
+ * @return {@code non-null;} the method prototype
+ */
+ public Prototype getPrototype();
+
+ /**
+ * Clears the regular and auxiliary arguments area.
+ */
+ public void clearArgs();
+
+ /**
+ * Pops the given number of values from the stack (of either category),
+ * and store them in the arguments area, indicating that there are now
+ * that many arguments. Also, clear the auxiliary arguments.
+ *
+ * @param frame {@code non-null;} frame to operate on
+ * @param count {@code >= 0;} number of values to pop
+ */
+ public void popArgs(Frame frame, int count);
+
+ /**
+ * Pops values from the stack of the types indicated by the given
+ * {@code Prototype} (popped in reverse of the argument
+ * order, so the first prototype argument type is for the deepest
+ * element of the stack), and store them in the arguments area,
+ * indicating that there are now that many arguments. Also, clear
+ * the auxiliary arguments.
+ *
+ * @param frame {@code non-null;} frame to operate on
+ * @param prototype {@code non-null;} prototype indicating arguments to pop
+ */
+ public void popArgs(Frame frame, Prototype prototype);
+
+ /**
+ * Pops a value from the stack of the indicated type, and store it
+ * in the arguments area, indicating that there are now that many
+ * arguments. Also, clear the auxiliary arguments.
+ *
+ * @param frame {@code non-null;} frame to operate on
+ * @param type {@code non-null;} type of the argument
+ */
+ public void popArgs(Frame frame, Type type);
+
+ /**
+ * Pops values from the stack of the indicated types (popped in
+ * reverse argument order, so the first indicated type is for the
+ * deepest element of the stack), and store them in the arguments
+ * area, indicating that there are now that many arguments. Also,
+ * clear the auxiliary arguments.
+ *
+ * @param frame {@code non-null;} frame to operate on
+ * @param type1 {@code non-null;} type of the first argument
+ * @param type2 {@code non-null;} type of the second argument
+ */
+ public void popArgs(Frame frame, Type type1, Type type2);
+
+ /**
+ * Pops values from the stack of the indicated types (popped in
+ * reverse argument order, so the first indicated type is for the
+ * deepest element of the stack), and store them in the arguments
+ * area, indicating that there are now that many arguments. Also,
+ * clear the auxiliary arguments.
+ *
+ * @param frame {@code non-null;} frame to operate on
+ * @param type1 {@code non-null;} type of the first argument
+ * @param type2 {@code non-null;} type of the second argument
+ * @param type3 {@code non-null;} type of the third argument
+ */
+ public void popArgs(Frame frame, Type type1, Type type2, Type type3);
+
+ /**
+ * Loads the local variable with the given index as the sole argument in
+ * the arguments area. Also, clear the auxiliary arguments.
+ *
+ * @param frame {@code non-null;} frame to operate on
+ * @param idx {@code >= 0;} the local variable index
+ */
+ public void localArg(Frame frame, int idx);
+
+ /**
+ * Indicates that the salient type of this operation is as
+ * given. This differentiates between, for example, the various
+ * arithmetic opcodes, which, by the time they hit a
+ * {@code Machine} are collapsed to the {@code int}
+ * variant. (See {@link BytecodeArray#parseInstruction} for
+ * details.)
+ *
+ * @param type {@code non-null;} the salient type of the upcoming operation
+ */
+ public void auxType(Type type);
+
+ /**
+ * Indicates that there is an auxiliary (inline, not stack)
+ * argument of type {@code int}, with the given value.
+ *
+ * <p><b>Note:</b> Perhaps unintuitively, the stack manipulation
+ * ops (e.g., {@code dup} and {@code swap}) use this to
+ * indicate the result stack pattern with a straightforward hex
+ * encoding of the push order starting with least-significant
+ * nibbles getting pushed first). For example, an all-category-1
+ * {@code dup2_x1} sets this to {@code 0x12312}, and the
+ * other form of that op sets this to
+ * {@code 0x121}.</p>
+ *
+ * <p><b>Also Note:</b> For {@code switch*} instructions, this is
+ * used to indicate the padding value (which is only useful for
+ * verification).</p>
+ *
+ * @param value the argument value
+ */
+ public void auxIntArg(int value);
+
+ /**
+ * Indicates that there is an auxiliary (inline, not stack) object
+ * argument, with the value based on the given constant.
+ *
+ * <p><b>Note:</b> Some opcodes use both {@code int} and
+ * constant auxiliary arguments.</p>
+ *
+ * @param cst {@code non-null;} the constant containing / referencing
+ * the value
+ */
+ public void auxCstArg(Constant cst);
+
+ /**
+ * Indicates that there is an auxiliary (inline, not stack) argument
+ * indicating a branch target.
+ *
+ * @param target the argument value
+ */
+ public void auxTargetArg(int target);
+
+ /**
+ * Indicates that there is an auxiliary (inline, not stack) argument
+ * consisting of a {@code switch*} table.
+ *
+ * <p><b>Note:</b> This is generally used in conjunction with
+ * {@link #auxIntArg} (which holds the padding).</p>
+ *
+ * @param cases {@code non-null;} the list of key-target pairs, plus the default
+ * target
+ */
+ public void auxSwitchArg(SwitchList cases);
+
+ /**
+ * Indicates that there is an auxiliary (inline, not stack) argument
+ * consisting of a list of initial values for a newly created array.
+ *
+ * @param initValues {@code non-null;} the list of constant values to initialize
+ * the array
+ */
+ public void auxInitValues(ArrayList<Constant> initValues);
+
+ /**
+ * Indicates that the target of this operation is the given local.
+ *
+ * @param idx {@code >= 0;} the local variable index
+ * @param type {@code non-null;} the type of the local
+ * @param local {@code null-ok;} the name and signature of the local, if known
+ */
+ public void localTarget(int idx, Type type, LocalItem local);
+
+ /**
+ * "Runs" the indicated opcode in an appropriate way, using the arguments
+ * area as appropriate, and modifying the given frame in response.
+ *
+ * @param frame {@code non-null;} frame to operate on
+ * @param offset {@code >= 0;} byte offset in the method to the opcode being
+ * run
+ * @param opcode {@code >= 0;} the opcode to run
+ */
+ public void run(Frame frame, int offset, int opcode);
+}
diff --git a/dx/src/com/android/dx/cf/code/Merger.java b/dx/src/com/android/dx/cf/code/Merger.java
new file mode 100644
index 0000000..51c31c3
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/Merger.java
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.util.Hex;
+
+/**
+ * Utility methods to merge various frame information.
+ */
+public final class Merger {
+ /**
+ * This class is uninstantiable.
+ */
+ private Merger() {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Merges two locals arrays. If the merged result is the same as the first
+ * argument, then return the first argument (not a copy).
+ *
+ * @param locals1 {@code non-null;} a locals array
+ * @param locals2 {@code non-null;} another locals array
+ * @return {@code non-null;} the result of merging the two locals arrays
+ */
+ public static OneLocalsArray mergeLocals(OneLocalsArray locals1,
+ OneLocalsArray locals2) {
+ if (locals1 == locals2) {
+ // Easy out.
+ return locals1;
+ }
+
+ int sz = locals1.getMaxLocals();
+ OneLocalsArray result = null;
+
+ if (locals2.getMaxLocals() != sz) {
+ throw new SimException("mismatched maxLocals values");
+ }
+
+ for (int i = 0; i < sz; i++) {
+ TypeBearer tb1 = locals1.getOrNull(i);
+ TypeBearer tb2 = locals2.getOrNull(i);
+ TypeBearer resultType = mergeType(tb1, tb2);
+ if (resultType != tb1) {
+ /*
+ * We only need to do anything when the result differs
+ * from what is in the first array, since that's what the
+ * result gets initialized to.
+ */
+ if (result == null) {
+ result = locals1.copy();
+ }
+
+ if (resultType == null) {
+ result.invalidate(i);
+ } else {
+ result.set(i, resultType);
+ }
+ }
+ }
+
+ if (result == null) {
+ return locals1;
+ }
+
+ result.setImmutable();
+ return result;
+ }
+
+ /**
+ * Merges two stacks. If the merged result is the same as the first
+ * argument, then return the first argument (not a copy).
+ *
+ * @param stack1 {@code non-null;} a stack
+ * @param stack2 {@code non-null;} another stack
+ * @return {@code non-null;} the result of merging the two stacks
+ */
+ public static ExecutionStack mergeStack(ExecutionStack stack1,
+ ExecutionStack stack2) {
+ if (stack1 == stack2) {
+ // Easy out.
+ return stack1;
+ }
+
+ int sz = stack1.size();
+ ExecutionStack result = null;
+
+ if (stack2.size() != sz) {
+ throw new SimException("mismatched stack depths");
+ }
+
+ for (int i = 0; i < sz; i++) {
+ TypeBearer tb1 = stack1.peek(i);
+ TypeBearer tb2 = stack2.peek(i);
+ TypeBearer resultType = mergeType(tb1, tb2);
+ if (resultType != tb1) {
+ /*
+ * We only need to do anything when the result differs
+ * from what is in the first stack, since that's what the
+ * result gets initialized to.
+ */
+ if (result == null) {
+ result = stack1.copy();
+ }
+
+ try {
+ if (resultType == null) {
+ throw new SimException("incompatible: " + tb1 + ", " +
+ tb2);
+ } else {
+ result.change(i, resultType);
+ }
+ } catch (SimException ex) {
+ ex.addContext("...while merging stack[" + Hex.u2(i) + "]");
+ throw ex;
+ }
+ }
+ }
+
+ if (result == null) {
+ return stack1;
+ }
+
+ result.setImmutable();
+ return result;
+ }
+
+ /**
+ * Merges two frame types.
+ *
+ * @param ft1 {@code non-null;} a frame type
+ * @param ft2 {@code non-null;} another frame type
+ * @return {@code non-null;} the result of merging the two types
+ */
+ public static TypeBearer mergeType(TypeBearer ft1, TypeBearer ft2) {
+ if ((ft1 == null) || ft1.equals(ft2)) {
+ return ft1;
+ } else if (ft2 == null) {
+ return null;
+ } else {
+ Type type1 = ft1.getType();
+ Type type2 = ft2.getType();
+
+ if (type1 == type2) {
+ return type1;
+ } else if (type1.isReference() && type2.isReference()) {
+ if (type1 == Type.KNOWN_NULL) {
+ /*
+ * A known-null merges with any other reference type to
+ * be that reference type.
+ */
+ return type2;
+ } else if (type2 == Type.KNOWN_NULL) {
+ /*
+ * The same as above, but this time it's type2 that's
+ * the known-null.
+ */
+ return type1;
+ } else if (type1.isArray() && type2.isArray()) {
+ TypeBearer componentUnion =
+ mergeType(type1.getComponentType(),
+ type2.getComponentType());
+ if (componentUnion == null) {
+ /*
+ * At least one of the types is a primitive type,
+ * so the merged result is just Object.
+ */
+ return Type.OBJECT;
+ }
+ return ((Type) componentUnion).getArrayType();
+ } else {
+ /*
+ * All other unequal reference types get merged to be
+ * Object in this phase. This is fine here, but it
+ * won't be the right thing to do in the verifier.
+ */
+ return Type.OBJECT;
+ }
+ } else if (type1.isIntlike() && type2.isIntlike()) {
+ /*
+ * Merging two non-identical int-like types results in
+ * the type int.
+ */
+ return Type.INT;
+ } else {
+ return null;
+ }
+ }
+ }
+
+ /**
+ * Returns whether the given supertype is possibly assignable from
+ * the given subtype. This takes into account primitiveness,
+ * int-likeness, known-nullness, and array dimensions, but does
+ * not assume anything about class hierarchy other than that the
+ * type {@code Object} is the supertype of all reference
+ * types and all arrays are assignable to
+ * {@code Serializable} and {@code Cloneable}.
+ *
+ * @param supertypeBearer {@code non-null;} the supertype
+ * @param subtypeBearer {@code non-null;} the subtype
+ */
+ public static boolean isPossiblyAssignableFrom(TypeBearer supertypeBearer,
+ TypeBearer subtypeBearer) {
+ Type supertype = supertypeBearer.getType();
+ Type subtype = subtypeBearer.getType();
+
+ if (supertype.equals(subtype)) {
+ // Easy out.
+ return true;
+ }
+
+ int superBt = supertype.getBasicType();
+ int subBt = subtype.getBasicType();
+
+ // Treat return types as Object for the purposes of this method.
+
+ if (superBt == Type.BT_ADDR) {
+ supertype = Type.OBJECT;
+ superBt = Type.BT_OBJECT;
+ }
+
+ if (subBt == Type.BT_ADDR) {
+ subtype = Type.OBJECT;
+ subBt = Type.BT_OBJECT;
+ }
+
+ if ((superBt != Type.BT_OBJECT) || (subBt != Type.BT_OBJECT)) {
+ /*
+ * No two distinct primitive types are assignable in this sense,
+ * unless they are both int-like.
+ */
+ return supertype.isIntlike() && subtype.isIntlike();
+ }
+
+ // At this point, we know both types are reference types.
+
+ if (supertype == Type.KNOWN_NULL) {
+ /*
+ * A known-null supertype is only assignable from another
+ * known-null (handled in the easy out at the top of the
+ * method).
+ */
+ return false;
+ } else if (subtype == Type.KNOWN_NULL) {
+ /*
+ * A known-null subtype is in fact assignable to any
+ * reference type.
+ */
+ return true;
+ } else if (supertype == Type.OBJECT) {
+ /*
+ * Object is assignable from any reference type.
+ */
+ return true;
+ } else if (supertype.isArray()) {
+ // The supertype is an array type.
+ if (! subtype.isArray()) {
+ // The subtype isn't an array, and so can't be assignable.
+ return false;
+ }
+
+ /*
+ * Strip off as many matched component types from both
+ * types as possible, and check the assignability of the
+ * results.
+ */
+ do {
+ supertype = supertype.getComponentType();
+ subtype = subtype.getComponentType();
+ } while (supertype.isArray() && subtype.isArray());
+
+ return isPossiblyAssignableFrom(supertype, subtype);
+ } else if (subtype.isArray()) {
+ /*
+ * Other than Object (handled above), array types are
+ * assignable only to Serializable and Cloneable.
+ */
+ return (supertype == Type.SERIALIZABLE) ||
+ (supertype == Type.CLONEABLE);
+ } else {
+ /*
+ * All other unequal reference types are considered at
+ * least possibly assignable.
+ */
+ return true;
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/cf/code/OneLocalsArray.java b/dx/src/com/android/dx/cf/code/OneLocalsArray.java
new file mode 100644
index 0000000..cafd177
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/OneLocalsArray.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.util.ExceptionWithContext;
+import com.android.dx.util.Hex;
+import com.android.dx.util.MutabilityControl;
+
+/**
+ * Representation of an array of local variables, with Java semantics.
+ *
+ * <p><b>Note:</b> For the most part, the documentation for this class
+ * ignores the distinction between {@link com.android.dx.rop.type.Type} and {@link
+ * com.android.dx.rop.type.TypeBearer}.</p>
+ */
+public class OneLocalsArray extends LocalsArray {
+ /** {@code non-null;} actual array */
+ private final TypeBearer[] locals;
+
+ /**
+ * Constructs an instance. The locals array initially consists of
+ * all-uninitialized values (represented as {@code null}s).
+ *
+ * @param maxLocals {@code >= 0;} the maximum number of locals this instance
+ * can refer to
+ */
+ public OneLocalsArray(int maxLocals) {
+ super(maxLocals != 0);
+ locals = new TypeBearer[maxLocals];
+ }
+
+ /** @inheritDoc */
+ public OneLocalsArray copy() {
+ OneLocalsArray result = new OneLocalsArray(locals.length);
+
+ System.arraycopy(locals, 0, result.locals, 0, locals.length);
+
+ return result;
+ }
+
+ /** @inheritDoc */
+ public void annotate(ExceptionWithContext ex) {
+ for (int i = 0; i < locals.length; i++) {
+ TypeBearer type = locals[i];
+ String s = (type == null) ? "<invalid>" : type.toString();
+ ex.addContext("locals[" + Hex.u2(i) + "]: " + s);
+ }
+ }
+
+ /** {@inheritDoc*/
+ public String toHuman() {
+ StringBuilder sb = new StringBuilder();
+
+ for (int i = 0; i < locals.length; i++) {
+ TypeBearer type = locals[i];
+ String s = (type == null) ? "<invalid>" : type.toString();
+ sb.append("locals[" + Hex.u2(i) + "]: " + s + "\n");
+ }
+
+ return sb.toString();
+ }
+
+ /** @inheritDoc */
+ public void makeInitialized(Type type) {
+ int len = locals.length;
+
+ if (len == 0) {
+ // We have to check for this before checking for immutability.
+ return;
+ }
+
+ throwIfImmutable();
+
+ Type initializedType = type.getInitializedType();
+
+ for (int i = 0; i < len; i++) {
+ if (locals[i] == type) {
+ locals[i] = initializedType;
+ }
+ }
+ }
+
+ /** @inheritDoc */
+ public int getMaxLocals() {
+ return locals.length;
+ }
+
+ /** @inheritDoc */
+ public void set(int idx, TypeBearer type) {
+ throwIfImmutable();
+
+ try {
+ type = type.getFrameType();
+ } catch (NullPointerException ex) {
+ // Elucidate the exception
+ throw new NullPointerException("type == null");
+ }
+
+ if (idx < 0) {
+ throw new IndexOutOfBoundsException("idx < 0");
+ }
+
+ // Make highest possible out-of-bounds check happen first.
+ if (type.getType().isCategory2()) {
+ locals[idx + 1] = null;
+ }
+
+ locals[idx] = type;
+
+ if (idx != 0) {
+ TypeBearer prev = locals[idx - 1];
+ if ((prev != null) && prev.getType().isCategory2()) {
+ locals[idx - 1] = null;
+ }
+ }
+ }
+
+ /** @inheritDoc */
+ public void set(RegisterSpec spec) {
+ set(spec.getReg(), spec);
+ }
+
+ /** @inheritDoc */
+ public void invalidate(int idx) {
+ throwIfImmutable();
+ locals[idx] = null;
+ }
+
+ /** @inheritDoc */
+ public TypeBearer getOrNull(int idx) {
+ return locals[idx];
+ }
+
+ /** @inheritDoc */
+ public TypeBearer get(int idx) {
+ TypeBearer result = locals[idx];
+
+ if (result == null) {
+ return throwSimException(idx, "invalid");
+ }
+
+ return result;
+ }
+
+ /** @inheritDoc */
+ public TypeBearer getCategory1(int idx) {
+ TypeBearer result = get(idx);
+ Type type = result.getType();
+
+ if (type.isUninitialized()) {
+ return throwSimException(idx, "uninitialized instance");
+ }
+
+ if (type.isCategory2()) {
+ return throwSimException(idx, "category-2");
+ }
+
+ return result;
+ }
+
+ /** @inheritDoc */
+ public TypeBearer getCategory2(int idx) {
+ TypeBearer result = get(idx);
+
+ if (result.getType().isCategory1()) {
+ return throwSimException(idx, "category-1");
+ }
+
+ return result;
+ }
+
+ /** @inheritDoc */
+ @Override
+ public LocalsArray merge(LocalsArray other) {
+ if (other instanceof OneLocalsArray) {
+ return merge((OneLocalsArray)other);
+ } else { //LocalsArraySet
+ // LocalsArraySet knows how to merge me.
+ return other.merge(this);
+ }
+ }
+
+ /**
+ * Merges this OneLocalsArray instance with another OneLocalsArray
+ * instance. A more-refined version of {@link #merge(LocalsArray) merge}
+ * which is called by that method when appropriate.
+ *
+ * @param other locals array with which to merge
+ * @return this instance if merge was a no-op, or a new instance if
+ * the merge resulted in a change.
+ */
+ public OneLocalsArray merge(OneLocalsArray other) {
+ try {
+ return Merger.mergeLocals(this, other);
+ } catch (SimException ex) {
+ ex.addContext("underlay locals:");
+ annotate(ex);
+ ex.addContext("overlay locals:");
+ other.annotate(ex);
+ throw ex;
+ }
+ }
+
+ /** @inheritDoc */
+ @Override
+ public LocalsArraySet mergeWithSubroutineCaller
+ (LocalsArray other, int predLabel) {
+
+ LocalsArraySet result = new LocalsArraySet(getMaxLocals());
+ return result.mergeWithSubroutineCaller(other, predLabel);
+ }
+
+ /**{@inheritDoc}*/
+ @Override
+ protected OneLocalsArray getPrimary() {
+ return this;
+ }
+
+ /**
+ * Throws a properly-formatted exception.
+ *
+ * @param idx the salient local index
+ * @param msg {@code non-null;} useful message
+ * @return never (keeps compiler happy)
+ */
+ private static TypeBearer throwSimException(int idx, String msg) {
+ throw new SimException("local " + Hex.u2(idx) + ": " + msg);
+ }
+}
diff --git a/dx/src/com/android/dx/cf/code/ReturnAddress.java b/dx/src/com/android/dx/cf/code/ReturnAddress.java
new file mode 100644
index 0000000..ee36450
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/ReturnAddress.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.util.Hex;
+
+/**
+ * Representation of a subroutine return address. In Java verification,
+ * somewhat counterintuitively, the salient bit of information you need to
+ * know about a return address is the <i>start address</i> of the subroutine
+ * being returned from, not the address being returned <i>to</i>, so that's
+ * what instances of this class hang onto.
+ */
+public final class ReturnAddress implements TypeBearer {
+ /** {@code >= 0;} the start address of the subroutine being returned from */
+ private final int subroutineAddress;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param subroutineAddress {@code >= 0;} the start address of the
+ * subroutine being returned from
+ */
+ public ReturnAddress(int subroutineAddress) {
+ if (subroutineAddress < 0) {
+ throw new IllegalArgumentException("subroutineAddress < 0");
+ }
+
+ this.subroutineAddress = subroutineAddress;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return ("<addr:" + Hex.u2(subroutineAddress) + ">");
+ }
+
+ /** {@inheritDoc} */
+ public String toHuman() {
+ return toString();
+ }
+
+ /** {@inheritDoc} */
+ public Type getType() {
+ return Type.RETURN_ADDRESS;
+ }
+
+ /** {@inheritDoc} */
+ public TypeBearer getFrameType() {
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ public int getBasicType() {
+ return Type.RETURN_ADDRESS.getBasicType();
+ }
+
+ /** {@inheritDoc} */
+ public int getBasicFrameType() {
+ return Type.RETURN_ADDRESS.getBasicFrameType();
+ }
+
+ /** {@inheritDoc} */
+ public boolean isConstant() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof ReturnAddress)) {
+ return false;
+ }
+
+ return subroutineAddress == ((ReturnAddress) other).subroutineAddress;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return subroutineAddress;
+ }
+
+ /**
+ * Gets the subroutine address.
+ *
+ * @return {@code >= 0;} the subroutine address
+ */
+ public int getSubroutineAddress() {
+ return subroutineAddress;
+ }
+}
diff --git a/dx/src/com/android/dx/cf/code/Ropper.java b/dx/src/com/android/dx/cf/code/Ropper.java
new file mode 100644
index 0000000..8217166
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/Ropper.java
@@ -0,0 +1,1675 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.code.*;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Prototype;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.Bits;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IntList;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.HashMap;
+
+/**
+ * Utility that converts a basic block list into a list of register-oriented
+ * blocks.
+ */
+public final class Ropper {
+ /** label offset for the parameter assignment block */
+ private static final int PARAM_ASSIGNMENT = -1;
+
+ /** label offset for the return block */
+ private static final int RETURN = -2;
+
+ /** label offset for the synchronized method final return block */
+ private static final int SYNCH_RETURN = -3;
+
+ /** label offset for the first synchronized method setup block */
+ private static final int SYNCH_SETUP_1 = -4;
+
+ /** label offset for the second synchronized method setup block */
+ private static final int SYNCH_SETUP_2 = -5;
+
+ /**
+ * label offset for the first synchronized method exception
+ * handler block
+ */
+ private static final int SYNCH_CATCH_1 = -6;
+
+ /**
+ * label offset for the second synchronized method exception
+ * handler block
+ */
+ private static final int SYNCH_CATCH_2 = -7;
+
+ /** number of special label offsets */
+ private static final int SPECIAL_LABEL_COUNT = 7;
+
+ /** {@code non-null;} method being converted */
+ private final ConcreteMethod method;
+
+ /** {@code non-null;} original block list */
+ private final ByteBlockList blocks;
+
+ /** max locals of the method */
+ private final int maxLocals;
+
+ /** max label (exclusive) of any original bytecode block */
+ private final int maxLabel;
+
+ /** {@code non-null;} simulation machine to use */
+ private final RopperMachine machine;
+
+ /** {@code non-null;} simulator to use */
+ private final Simulator sim;
+
+ /**
+ * {@code non-null;} sparse array mapping block labels to initial frame
+ * contents, if known
+ */
+ private final Frame[] startFrames;
+
+ /** {@code non-null;} output block list in-progress */
+ private final ArrayList<BasicBlock> result;
+
+ /**
+ * {@code non-null;} list of subroutine-nest labels
+ * (See {@link Frame#getSubroutines} associated with each result block.
+ * Parallel to {@link Ropper#result}.
+ */
+ private final ArrayList<IntList> resultSubroutines;
+
+ /**
+ * {@code non-null;} for each block (by label) that is used as an exception
+ * handler, the type of exception it catches
+ */
+ private final Type[] catchTypes;
+
+ /**
+ * whether an exception-handler block for a synchronized method was
+ * ever required
+ */
+ private boolean synchNeedsExceptionHandler;
+
+ /**
+ * {@code non-null;} list of subroutines indexed by label of start
+ * address */
+ private final Subroutine[] subroutines;
+
+ /** true if {@code subroutines} is non-empty */
+ private boolean hasSubroutines;
+
+ /**
+ * Keeps track of subroutines that exist in java form and are inlined in
+ * Rop form.
+ */
+ private class Subroutine {
+ /** list of all blocks that jsr to this subroutine */
+ private BitSet callerBlocks;
+ /** List of all blocks that return from this subroutine */
+ private BitSet retBlocks;
+ /** first block in this subroutine */
+ private int startBlock;
+
+ /**
+ * Constructs instance.
+ *
+ * @param startBlock First block of the subroutine.
+ */
+ Subroutine(int startBlock) {
+ this.startBlock = startBlock;
+ retBlocks = new BitSet(maxLabel);
+ callerBlocks = new BitSet(maxLabel);
+ hasSubroutines = true;
+ }
+
+ /**
+ * Constructs instance.
+ *
+ * @param startBlock First block of the subroutine.
+ * @param retBlock one of the ret blocks (final blocks) of this
+ * subroutine.
+ */
+ Subroutine(int startBlock, int retBlock) {
+ this(startBlock);
+ addRetBlock(retBlock);
+ }
+
+ /**
+ * @return {@code >= 0;} the label of the subroutine's start block.
+ */
+ int getStartBlock() {
+ return startBlock;
+ }
+
+ /**
+ * Adds a label to the list of ret blocks (final blocks) for this
+ * subroutine.
+ *
+ * @param retBlock ret block label
+ */
+ void addRetBlock(int retBlock) {
+ retBlocks.set(retBlock);
+ }
+
+ /**
+ * Adds a label to the list of caller blocks for this subroutine.
+ *
+ * @param label a block that invokes this subroutine.
+ */
+ void addCallerBlock(int label) {
+ callerBlocks.set(label);
+ }
+
+ /**
+ * Generates a list of subroutine successors. Note: successor blocks
+ * could be listed more than once. This is ok, because this successor
+ * list (and the block it's associated with) will be copied and inlined
+ * before we leave the ropper. Redundent successors will result in
+ * redundent (no-op) merges.
+ *
+ * @return all currently known successors
+ * (return destinations) for that subroutine
+ */
+ IntList getSuccessors() {
+ IntList successors = new IntList(callerBlocks.size());
+
+ /*
+ * For each subroutine caller, get it's target. If the
+ * target is us, add the ret target (subroutine successor)
+ * to our list
+ */
+
+ for (int label = callerBlocks.nextSetBit(0); label >= 0;
+ label = callerBlocks.nextSetBit(label+1)) {
+ BasicBlock subCaller = labelToBlock(label);
+ successors.add(subCaller.getSuccessors().get(0));
+ }
+
+ successors.setImmutable();
+
+ return successors;
+ }
+
+ /**
+ * Merges the specified frame into this subroutine's successors,
+ * setting {@code workSet} as appropriate. To be called with
+ * the frame of a subroutine ret block.
+ *
+ * @param frame {@code non-null;} frame from ret block to merge
+ * @param workSet {@code non-null;} workset to update
+ */
+ void mergeToSuccessors(Frame frame, int[] workSet) {
+ for (int label = callerBlocks.nextSetBit(0); label >= 0;
+ label = callerBlocks.nextSetBit(label+1)) {
+ BasicBlock subCaller = labelToBlock(label);
+ int succLabel = subCaller.getSuccessors().get(0);
+
+ Frame subFrame = frame.subFrameForLabel(startBlock, label);
+
+ if (subFrame != null) {
+ mergeAndWorkAsNecessary(succLabel, -1, null,
+ subFrame, workSet);
+ } else {
+ Bits.set(workSet, label);
+ }
+ }
+ }
+ }
+
+ /**
+ * Converts a {@link ConcreteMethod} to a {@link RopMethod}.
+ *
+ * @param method {@code non-null;} method to convert
+ * @param advice {@code non-null;} translation advice to use
+ * @return {@code non-null;} the converted instance
+ */
+ public static RopMethod convert(ConcreteMethod method,
+ TranslationAdvice advice) {
+ try {
+ Ropper r = new Ropper(method, advice);
+ r.doit();
+ return r.getRopMethod();
+ } catch (SimException ex) {
+ ex.addContext("...while working on method " +
+ method.getNat().toHuman());
+ throw ex;
+ }
+ }
+
+ /**
+ * Constructs an instance. This class is not publicly instantiable; use
+ * {@link #convert}.
+ *
+ * @param method {@code non-null;} method to convert
+ * @param advice {@code non-null;} translation advice to use
+ */
+ private Ropper(ConcreteMethod method, TranslationAdvice advice) {
+ if (method == null) {
+ throw new NullPointerException("method == null");
+ }
+
+ if (advice == null) {
+ throw new NullPointerException("advice == null");
+ }
+
+ this.method = method;
+ this.blocks = BasicBlocker.identifyBlocks(method);
+ this.maxLabel = blocks.getMaxLabel();
+ this.maxLocals = method.getMaxLocals();
+ this.machine = new RopperMachine(this, method, advice);
+ this.sim = new Simulator(machine, method);
+ this.startFrames = new Frame[maxLabel];
+ this.subroutines = new Subroutine[maxLabel];
+
+ /*
+ * The "* 2 + 10" below is to conservatively believe that every
+ * block is an exception handler target and should also
+ * take care of enough other possible extra overhead such that
+ * the underlying array is unlikely to need resizing.
+ */
+ this.result = new ArrayList<BasicBlock>(blocks.size() * 2 + 10);
+ this.resultSubroutines =
+ new ArrayList<IntList>(blocks.size() * 2 + 10);
+
+ this.catchTypes = new Type[maxLabel];
+ this.synchNeedsExceptionHandler = false;
+
+ /*
+ * Set up the first stack frame with the right limits, but leave it
+ * empty here (to be filled in outside of the constructor).
+ */
+ startFrames[0] = new Frame(maxLocals, method.getMaxStack());
+ }
+
+ /**
+ * Gets the first (lowest) register number to use as the temporary
+ * area when unwinding stack manipulation ops.
+ *
+ * @return {@code >= 0;} the first register to use
+ */
+ /*package*/ int getFirstTempStackReg() {
+ /*
+ * We use the register that is just past the deepest possible
+ * stack element, plus one if the method is synchronized to
+ * avoid overlapping with the synch register. We don't need to
+ * do anything else special at this level, since later passes
+ * will merely notice the highest register used by explicit
+ * inspection.
+ */
+ int regCount = getNormalRegCount();
+ return isSynchronized() ? regCount + 1 : regCount;
+ }
+
+ /**
+ * Gets the label for the exception handler setup block corresponding
+ * to the given label.
+ *
+ * @param label {@code >= 0;} the original label
+ * @return {@code >= 0;} the corresponding exception handler setup label
+ */
+ private int getExceptionSetupLabel(int label) {
+ return maxLabel + label;
+ }
+
+ /**
+ * Gets the label for the given special-purpose block. The given label
+ * should be one of the static constants defined by this class.
+ *
+ * @param label {@code < 0;} the special label constant
+ * @return {@code >= 0;} the actual label value to use
+ */
+ private int getSpecialLabel(int label) {
+ /*
+ * The label is bitwise-complemented so that mistakes where
+ * LABEL is used instead of getSpecialLabel(LABEL) cause a
+ * failure at block construction time, since negative labels
+ * are illegal. We multiply maxLabel by 2 since 0..maxLabel
+ * (exclusive) are the original blocks and
+ * maxLabel..(maxLabel*2) are reserved for exception handler
+ * setup blocks (see getExceptionSetupLabel(), above).
+ */
+ return (maxLabel * 2) + ~label;
+ }
+
+ /**
+ * Gets the minimum label for unreserved use.
+ *
+ * @return {@code >= 0;} the minimum label
+ */
+ private int getMinimumUnreservedLabel() {
+ /*
+ * The labels below ((maxLabel * 2) + SPECIAL_LABEL_COUNT) are
+ * reserved for particular uses.
+ */
+
+ return (maxLabel * 2) + SPECIAL_LABEL_COUNT;
+ }
+
+ /**
+ * Gets an arbitrary unreserved and available label.
+ *
+ * @return {@code >= 0;} the label
+ */
+ private int getAvailableLabel() {
+ int candidate = getMinimumUnreservedLabel();
+
+ for (BasicBlock bb : result) {
+ int label = bb.getLabel();
+ if (label >= candidate) {
+ candidate = label + 1;
+ }
+ }
+
+ return candidate;
+ }
+
+ /**
+ * Gets whether the method being translated is synchronized.
+ *
+ * @return whether the method being translated is synchronized
+ */
+ private boolean isSynchronized() {
+ int accessFlags = method.getAccessFlags();
+ return (accessFlags & AccessFlags.ACC_SYNCHRONIZED) != 0;
+ }
+
+ /**
+ * Gets whether the method being translated is static.
+ *
+ * @return whether the method being translated is static
+ */
+ private boolean isStatic() {
+ int accessFlags = method.getAccessFlags();
+ return (accessFlags & AccessFlags.ACC_STATIC) != 0;
+ }
+
+ /**
+ * Gets the total number of registers used for "normal" purposes (i.e.,
+ * for the straightforward translation from the original Java).
+ *
+ * @return {@code >= 0;} the total number of registers used
+ */
+ private int getNormalRegCount() {
+ return maxLocals + method.getMaxStack();
+ }
+
+ /**
+ * Gets the register spec to use to hold the object to synchronize on,
+ * for a synchronized method.
+ *
+ * @return {@code non-null;} the register spec
+ */
+ private RegisterSpec getSynchReg() {
+ /*
+ * We use the register that is just past the deepest possible
+ * stack element, with a minimum of v1 since v0 is what's
+ * always used to hold the caught exception when unwinding. We
+ * don't need to do anything else special at this level, since
+ * later passes will merely notice the highest register used
+ * by explicit inspection.
+ */
+ int reg = getNormalRegCount();
+ return RegisterSpec.make((reg < 1) ? 1 : reg, Type.OBJECT);
+ }
+
+ /**
+ * Searches {@link #result} for a block with the given label. Returns its
+ * index if found, or returns {@code -1} if there is no such block.
+ *
+ * @param label the label to look for
+ * @return {@code >= -1;} the index for the block with the given label or
+ * {@code -1} if there is no such block
+ */
+ private int labelToResultIndex(int label) {
+ int sz = result.size();
+ for (int i = 0; i < sz; i++) {
+ BasicBlock one = result.get(i);
+ if (one.getLabel() == label) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * Searches {@link #result} for a block with the given label. Returns it if
+ * found, or throws an exception if there is no such block.
+ *
+ * @param label the label to look for
+ * @return {@code non-null;} the block with the given label
+ */
+ private BasicBlock labelToBlock(int label) {
+ int idx = labelToResultIndex(label);
+
+ if (idx < 0) {
+ throw new IllegalArgumentException("no such label " +
+ Hex.u2(label));
+ }
+
+ return result.get(idx);
+ }
+
+ /**
+ * Adds a block to the output result.
+ *
+ * @param block {@code non-null;} the block to add
+ * @param subroutines {@code non-null;} subroutine label list
+ * as described in {@link Frame#getSubroutines}
+ */
+ private void addBlock(BasicBlock block, IntList subroutines) {
+ if (block == null) {
+ throw new NullPointerException("block == null");
+ }
+
+ result.add(block);
+ subroutines.throwIfMutable();
+ resultSubroutines.add(subroutines);
+ }
+
+ /**
+ * Adds or replace a block in the output result. If this is a
+ * replacement, then any extra blocks that got added with the
+ * original get removed as a result of calling this method.
+ *
+ * @param block {@code non-null;} the block to add or replace
+ * @param subroutines {@code non-null;} subroutine label list
+ * as described in {@link Frame#getSubroutines}
+ * @return {@code true} if the block was replaced or
+ * {@code false} if it was added for the first time
+ */
+ private boolean addOrReplaceBlock(BasicBlock block, IntList subroutines) {
+ if (block == null) {
+ throw new NullPointerException("block == null");
+ }
+
+ int idx = labelToResultIndex(block.getLabel());
+ boolean ret;
+
+ if (idx < 0) {
+ ret = false;
+ } else {
+ /*
+ * We are replacing a pre-existing block, so find any
+ * blocks that got added as part of the original and
+ * remove those too. Such blocks are (possibly indirect)
+ * successors of this block which are out of the range of
+ * normally-translated blocks.
+ */
+ removeBlockAndSpecialSuccessors(idx);
+ ret = true;
+ }
+
+ result.add(block);
+ subroutines.throwIfMutable();
+ resultSubroutines.add(subroutines);
+ return ret;
+ }
+
+ /**
+ * Adds or replaces a block in the output result. Do not delete
+ * any successors.
+ *
+ * @param block {@code non-null;} the block to add or replace
+ * @param subroutines {@code non-null;} subroutine label list
+ * as described in {@link Frame#getSubroutines}
+ * @return {@code true} if the block was replaced or
+ * {@code false} if it was added for the first time
+ */
+ private boolean addOrReplaceBlockNoDelete(BasicBlock block,
+ IntList subroutines) {
+ if (block == null) {
+ throw new NullPointerException("block == null");
+ }
+
+ int idx = labelToResultIndex(block.getLabel());
+ boolean ret;
+
+ if (idx < 0) {
+ ret = false;
+ } else {
+ result.remove(idx);
+ resultSubroutines.remove(idx);
+ ret = true;
+ }
+
+ result.add(block);
+ subroutines.throwIfMutable();
+ resultSubroutines.add(subroutines);
+ return ret;
+ }
+
+ /**
+ * Helper for {@link #addOrReplaceBlock} which recursively removes
+ * the given block and all blocks that are (direct and indirect)
+ * successors of it whose labels indicate that they are not in the
+ * normally-translated range.
+ *
+ * @param idx {@code non-null;} block to remove (etc.)
+ */
+ private void removeBlockAndSpecialSuccessors(int idx) {
+ int minLabel = getMinimumUnreservedLabel();
+ BasicBlock block = result.get(idx);
+ IntList successors = block.getSuccessors();
+ int sz = successors.size();
+
+ result.remove(idx);
+ resultSubroutines.remove(idx);
+
+ for (int i = 0; i < sz; i++) {
+ int label = successors.get(i);
+ if (label >= minLabel) {
+ idx = labelToResultIndex(label);
+ if (idx < 0) {
+ throw new RuntimeException("Invalid label "
+ + Hex.u2(label));
+ }
+ removeBlockAndSpecialSuccessors(idx);
+ }
+ }
+ }
+
+ /**
+ * Extracts the resulting {@link RopMethod} from the instance.
+ *
+ * @return {@code non-null;} the method object
+ */
+ private RopMethod getRopMethod() {
+
+ // Construct the final list of blocks.
+
+ int sz = result.size();
+ BasicBlockList bbl = new BasicBlockList(sz);
+ for (int i = 0; i < sz; i++) {
+ bbl.set(i, result.get(i));
+ }
+ bbl.setImmutable();
+
+ // Construct the method object to wrap it all up.
+
+ /*
+ * Note: The parameter assignment block is always the first
+ * that should be executed, hence the second argument to the
+ * constructor.
+ */
+ return new RopMethod(bbl, getSpecialLabel(PARAM_ASSIGNMENT));
+ }
+
+ /**
+ * Does the conversion.
+ */
+ private void doit() {
+ int[] workSet = Bits.makeBitSet(maxLabel);
+
+ Bits.set(workSet, 0);
+ addSetupBlocks();
+ setFirstFrame();
+
+ for (;;) {
+ int offset = Bits.findFirst(workSet, 0);
+ if (offset < 0) {
+ break;
+ }
+ Bits.clear(workSet, offset);
+ ByteBlock block = blocks.labelToBlock(offset);
+ Frame frame = startFrames[offset];
+ try {
+ processBlock(block, frame, workSet);
+ } catch (SimException ex) {
+ ex.addContext("...while working on block " + Hex.u2(offset));
+ throw ex;
+ }
+ }
+
+ addReturnBlock();
+ addSynchExceptionHandlerBlock();
+ addExceptionSetupBlocks();
+
+ if (hasSubroutines) {
+ // Subroutines are very rare, so skip this step if it's n/a
+ inlineSubroutines();
+ }
+ }
+
+ /**
+ * Sets up the first frame to contain all the incoming parameters in
+ * locals.
+ */
+ private void setFirstFrame() {
+ Prototype desc = method.getEffectiveDescriptor();
+ startFrames[0].initializeWithParameters(desc.getParameterTypes());
+ startFrames[0].setImmutable();
+ }
+
+ /**
+ * Processes the given block.
+ *
+ * @param block {@code non-null;} block to process
+ * @param frame {@code non-null;} start frame for the block
+ * @param workSet {@code non-null;} bits representing work to do,
+ * which this method may add to
+ */
+ private void processBlock(ByteBlock block, Frame frame, int[] workSet) {
+ // Prepare the list of caught exceptions for this block.
+ ByteCatchList catches = block.getCatches();
+ machine.startBlock(catches.toRopCatchList());
+
+ /*
+ * Using a copy of the given frame, simulate each instruction,
+ * calling into machine for each.
+ */
+ frame = frame.copy();
+ sim.simulate(block, frame);
+ frame.setImmutable();
+
+ int extraBlockCount = machine.getExtraBlockCount();
+ ArrayList<Insn> insns = machine.getInsns();
+ int insnSz = insns.size();
+
+ /*
+ * Merge the frame into each possible non-exceptional
+ * successor.
+ */
+
+ int catchSz = catches.size();
+ IntList successors = block.getSuccessors();
+
+ int startSuccessorIndex;
+
+ Subroutine calledSubroutine = null;
+ if (machine.hasJsr()) {
+ /*
+ * If this frame ends in a JSR, only merge our frame with
+ * the subroutine start, not the subroutine's return target.
+ */
+ startSuccessorIndex = 1;
+
+ int subroutineLabel = successors.get(1);
+
+ if (subroutines[subroutineLabel] == null) {
+ subroutines[subroutineLabel] =
+ new Subroutine (subroutineLabel);
+ }
+
+ subroutines[subroutineLabel].addCallerBlock(block.getLabel());
+
+ calledSubroutine = subroutines[subroutineLabel];
+ } else if (machine.hasRet()) {
+ /*
+ * This block ends in a ret, which means it's the final block
+ * in some subroutine. Ultimately, this block will be copied
+ * and inlined for each call and then disposed of.
+ */
+
+ ReturnAddress ra = machine.getReturnAddress();
+ int subroutineLabel = ra.getSubroutineAddress();
+
+ if (subroutines[subroutineLabel] == null) {
+ subroutines[subroutineLabel]
+ = new Subroutine (subroutineLabel, block.getLabel());
+ } else {
+ subroutines[subroutineLabel].addRetBlock(block.getLabel());
+ }
+
+ successors = subroutines[subroutineLabel].getSuccessors();
+ subroutines[subroutineLabel]
+ .mergeToSuccessors(frame, workSet);
+ // Skip processing below since we just did it.
+ startSuccessorIndex = successors.size();
+ } else if (machine.wereCatchesUsed()) {
+ /*
+ * If there are catches, then the first successors
+ * (which will either be all of them or all but the last one)
+ * are catch targets.
+ */
+ startSuccessorIndex = catchSz;
+ } else {
+ startSuccessorIndex = 0;
+ }
+
+ int succSz = successors.size();
+ for (int i = startSuccessorIndex; i < succSz;
+ i++) {
+ int succ = successors.get(i);
+ try {
+ mergeAndWorkAsNecessary(succ, block.getLabel(),
+ calledSubroutine, frame, workSet);
+ } catch (SimException ex) {
+ ex.addContext("...while merging to block " + Hex.u2(succ));
+ throw ex;
+ }
+ }
+
+ if ((succSz == 0) && machine.returns()) {
+ /*
+ * The block originally contained a return, but it has
+ * been made to instead end with a goto, and we need to
+ * tell it at this point that its sole successor is the
+ * return block. This has to happen after the merge loop
+ * above, since, at this point, the return block doesn't
+ * actually exist; it gets synthesized at the end of
+ * processing the original blocks.
+ */
+ successors = IntList.makeImmutable(getSpecialLabel(RETURN));
+ succSz = 1;
+ }
+
+ int primarySucc;
+
+ if (succSz == 0) {
+ primarySucc = -1;
+ } else {
+ primarySucc = machine.getPrimarySuccessorIndex();
+ if (primarySucc >= 0) {
+ primarySucc = successors.get(primarySucc);
+ }
+ }
+
+ /*
+ * This variable is true only when the method is synchronized and
+ * the block being processed can possibly throw an exception.
+ */
+ boolean synch = isSynchronized() && machine.canThrow();
+
+ if (synch || (catchSz != 0)) {
+ /*
+ * Deal with exception handlers: Merge an exception-catch
+ * frame into each possible exception handler, and
+ * construct a new set of successors to point at the
+ * exception handler setup blocks (which get synthesized
+ * at the very end of processing).
+ */
+ boolean catchesAny = false;
+ IntList newSucc = new IntList(succSz);
+ for (int i = 0; i < catchSz; i++) {
+ ByteCatchList.Item one = catches.get(i);
+ CstType exceptionClass = one.getExceptionClass();
+ int targ = one.getHandlerPc();
+
+ catchesAny |= (exceptionClass == CstType.OBJECT);
+
+ Frame f = frame.makeExceptionHandlerStartFrame(exceptionClass);
+
+ try {
+ mergeAndWorkAsNecessary(targ, block.getLabel(),
+ null, f, workSet);
+ } catch (SimException ex) {
+ ex.addContext("...while merging exception to block " +
+ Hex.u2(targ));
+ throw ex;
+ }
+
+ /*
+ * Set up the exception handler type, by setting it if
+ * the given handler has yet to be encountered, or by
+ * conservatively unioning if it has.
+ */
+ Type already = catchTypes[targ];
+ if (already == null) {
+ catchTypes[targ] = exceptionClass.getClassType();
+ } else if (already != exceptionClass.getClassType()) {
+ catchTypes[targ] = Type.OBJECT;
+ }
+
+ /*
+ * The synthesized exception setup block will have the
+ * label getExceptionSetupLabel(targ).
+ */
+ newSucc.add(getExceptionSetupLabel(targ));
+ }
+
+ if (synch && !catchesAny) {
+ /*
+ * The method is synchronized and this block doesn't
+ * already have a catch-all handler, so add one to the
+ * end, both in the successors and in the throwing
+ * instruction(s) at the end of the block (which is where
+ * the caught classes live).
+ */
+ newSucc.add(getSpecialLabel(SYNCH_CATCH_1));
+ synchNeedsExceptionHandler = true;
+
+ for (int i = insnSz - extraBlockCount - 1; i < insnSz; i++) {
+ Insn insn = insns.get(i);
+ if (insn.canThrow()) {
+ insn = insn.withAddedCatch(Type.OBJECT);
+ insns.set(i, insn);
+ }
+ }
+ }
+
+ if (primarySucc >= 0) {
+ newSucc.add(primarySucc);
+ }
+
+ newSucc.setImmutable();
+ successors = newSucc;
+ }
+
+ // Construct the final resulting block(s), and store it (them).
+
+ int primarySuccListIndex = successors.indexOf(primarySucc);
+
+ /*
+ * If there are any extra blocks, work backwards through the
+ * list of instructions, adding single-instruction blocks, and
+ * resetting the successors variables as appropriate.
+ */
+ for (/*extraBlockCount*/; extraBlockCount > 0; extraBlockCount--) {
+ /*
+ * Some of the blocks that the RopperMachine wants added
+ * are for move-result insns, and these need goto insns as well.
+ */
+ Insn extraInsn = insns.get(--insnSz);
+ boolean needsGoto
+ = extraInsn.getOpcode().getBranchingness()
+ == Rop.BRANCH_NONE;
+ InsnList il = new InsnList(needsGoto ? 2 : 1);
+ IntList extraBlockSuccessors = successors;
+
+ il.set(0, extraInsn);
+
+ if (needsGoto) {
+ il.set(1, new PlainInsn(Rops.GOTO,
+ extraInsn.getPosition(), null,
+ RegisterSpecList.EMPTY));
+ /*
+ * Obviously, this block won't be throwing an exception
+ * so it should only have one successor.
+ */
+ extraBlockSuccessors = IntList.makeImmutable(primarySucc);
+ }
+ il.setImmutable();
+
+ int label = getAvailableLabel();
+ BasicBlock bb = new BasicBlock(label, il, extraBlockSuccessors,
+ primarySucc);
+ // All of these extra blocks will be in the same subroutine
+ addBlock(bb, frame.getSubroutines());
+
+ successors = successors.mutableCopy();
+ successors.set(primarySuccListIndex, label);
+ successors.setImmutable();
+ primarySucc = label;
+ }
+
+ Insn lastInsn = (insnSz == 0) ? null : insns.get(insnSz - 1);
+
+ /*
+ * Add a goto to the end of the block if it doesn't already
+ * end with a branch, to maintain the invariant that all
+ * blocks end with a branch of some sort or other. Note that
+ * it is possible for there to be blocks for which no
+ * instructions were ever output (e.g., only consist of pop*
+ * in the original Java bytecode).
+ */
+ if ((lastInsn == null) ||
+ (lastInsn.getOpcode().getBranchingness() == Rop.BRANCH_NONE)) {
+ SourcePosition pos = (lastInsn == null) ? SourcePosition.NO_INFO :
+ lastInsn.getPosition();
+ insns.add(new PlainInsn(Rops.GOTO, pos, null,
+ RegisterSpecList.EMPTY));
+ insnSz++;
+ }
+
+ /*
+ * Construct a block for the remaining instructions (which in
+ * the usual case is all of them).
+ */
+
+ InsnList il = new InsnList(insnSz);
+ for (int i = 0; i < insnSz; i++) {
+ il.set(i, insns.get(i));
+ }
+ il.setImmutable();
+
+ BasicBlock bb =
+ new BasicBlock(block.getLabel(), il, successors, primarySucc);
+ addOrReplaceBlock(bb, frame.getSubroutines());
+ }
+
+ /**
+ * Helper for {@link #processBlock}, which merges frames and
+ * adds to the work set, as necessary.
+ *
+ * @param label {@code >= 0;} label to work on
+ * @param pred predecessor label; must be {@code >= 0} when
+ * {@code label} is a subroutine start block and calledSubroutine
+ * is non-null. Otherwise, may be -1.
+ * @param calledSubroutine {@code null-ok;} a Subroutine instance if
+ * {@code label} is the first block in a subroutine.
+ * @param frame {@code non-null;} new frame for the labelled block
+ * @param workSet {@code non-null;} bits representing work to do,
+ * which this method may add to
+ */
+ private void mergeAndWorkAsNecessary(int label, int pred,
+ Subroutine calledSubroutine, Frame frame, int[] workSet) {
+ Frame existing = startFrames[label];
+ Frame merged;
+
+ if (existing != null) {
+ /*
+ * Some other block also continues at this label. Merge
+ * the frames, and re-set the bit in the work set if there
+ * was a change.
+ */
+ if (calledSubroutine != null) {
+ merged = existing.mergeWithSubroutineCaller(frame,
+ calledSubroutine.getStartBlock(), pred);
+ } else {
+ merged = existing.mergeWith(frame);
+ }
+ if (merged != existing) {
+ startFrames[label] = merged;
+ Bits.set(workSet, label);
+ }
+ } else {
+ // This is the first time this label has been encountered.
+ if (calledSubroutine != null) {
+ startFrames[label]
+ = frame.makeNewSubroutineStartFrame(label, pred);
+ } else {
+ startFrames[label] = frame;
+ }
+ Bits.set(workSet, label);
+ }
+ }
+
+ /**
+ * Constructs and adds the blocks that perform setup for the rest of
+ * the method. This includes a first block which merely contains
+ * assignments from parameters to the same-numbered registers and
+ * a possible second block which deals with synchronization.
+ */
+ private void addSetupBlocks() {
+ LocalVariableList localVariables = method.getLocalVariables();
+ SourcePosition pos = method.makeSourcePosistion(0);
+ Prototype desc = method.getEffectiveDescriptor();
+ StdTypeList params = desc.getParameterTypes();
+ int sz = params.size();
+ InsnList insns = new InsnList(sz + 1);
+ int at = 0;
+
+ for (int i = 0; i < sz; i++) {
+ Type one = params.get(i);
+ LocalVariableList.Item local =
+ localVariables.pcAndIndexToLocal(0, at);
+ RegisterSpec result = (local == null) ?
+ RegisterSpec.make(at, one) :
+ RegisterSpec.makeLocalOptional(at, one, local.getLocalItem());
+
+ Insn insn = new PlainCstInsn(Rops.opMoveParam(one), pos, result,
+ RegisterSpecList.EMPTY,
+ CstInteger.make(at));
+ insns.set(i, insn);
+ at += one.getCategory();
+ }
+
+ insns.set(sz, new PlainInsn(Rops.GOTO, pos, null,
+ RegisterSpecList.EMPTY));
+ insns.setImmutable();
+
+ boolean synch = isSynchronized();
+ int label = synch ? getSpecialLabel(SYNCH_SETUP_1) : 0;
+ BasicBlock bb =
+ new BasicBlock(getSpecialLabel(PARAM_ASSIGNMENT), insns,
+ IntList.makeImmutable(label), label);
+ addBlock(bb, IntList.EMPTY);
+
+ if (synch) {
+ RegisterSpec synchReg = getSynchReg();
+ Insn insn;
+ if (isStatic()) {
+ insn = new ThrowingCstInsn(Rops.CONST_OBJECT, pos,
+ RegisterSpecList.EMPTY,
+ StdTypeList.EMPTY,
+ method.getDefiningClass());
+ insns = new InsnList(1);
+ insns.set(0, insn);
+ } else {
+ insns = new InsnList(2);
+ insn = new PlainCstInsn(Rops.MOVE_PARAM_OBJECT, pos,
+ synchReg, RegisterSpecList.EMPTY,
+ CstInteger.VALUE_0);
+ insns.set(0, insn);
+ insns.set(1, new PlainInsn(Rops.GOTO, pos, null,
+ RegisterSpecList.EMPTY));
+ }
+
+ int label2 = getSpecialLabel(SYNCH_SETUP_2);
+ insns.setImmutable();
+ bb = new BasicBlock(label, insns,
+ IntList.makeImmutable(label2), label2);
+ addBlock(bb, IntList.EMPTY);
+
+ insns = new InsnList(isStatic() ? 2 : 1);
+
+ if (isStatic()) {
+ insns.set(0, new PlainInsn(Rops.opMoveResultPseudo(synchReg),
+ pos, synchReg, RegisterSpecList.EMPTY));
+ }
+
+ insn = new ThrowingInsn(Rops.MONITOR_ENTER, pos,
+ RegisterSpecList.make(synchReg),
+ StdTypeList.EMPTY);
+ insns.set(isStatic() ? 1 :0, insn);
+ insns.setImmutable();
+ bb = new BasicBlock(label2, insns, IntList.makeImmutable(0), 0);
+ addBlock(bb, IntList.EMPTY);
+ }
+ }
+
+ /**
+ * Constructs and adds the return block, if necessary. The return
+ * block merely contains an appropriate {@code return}
+ * instruction.
+ */
+ private void addReturnBlock() {
+ Rop returnOp = machine.getReturnOp();
+
+ if (returnOp == null) {
+ /*
+ * The method being converted never returns normally, so there's
+ * no need for a return block.
+ */
+ return;
+ }
+
+ SourcePosition returnPos = machine.getReturnPosition();
+ int label = getSpecialLabel(RETURN);
+
+ if (isSynchronized()) {
+ InsnList insns = new InsnList(1);
+ Insn insn = new ThrowingInsn(Rops.MONITOR_EXIT, returnPos,
+ RegisterSpecList.make(getSynchReg()),
+ StdTypeList.EMPTY);
+ insns.set(0, insn);
+ insns.setImmutable();
+
+ int nextLabel = getSpecialLabel(SYNCH_RETURN);
+ BasicBlock bb =
+ new BasicBlock(label, insns,
+ IntList.makeImmutable(nextLabel), nextLabel);
+ addBlock(bb, IntList.EMPTY);
+
+ label = nextLabel;
+ }
+
+ InsnList insns = new InsnList(1);
+ TypeList sourceTypes = returnOp.getSources();
+ RegisterSpecList sources;
+
+ if (sourceTypes.size() == 0) {
+ sources = RegisterSpecList.EMPTY;
+ } else {
+ RegisterSpec source = RegisterSpec.make(0, sourceTypes.getType(0));
+ sources = RegisterSpecList.make(source);
+ }
+
+ Insn insn = new PlainInsn(returnOp, returnPos, null, sources);
+ insns.set(0, insn);
+ insns.setImmutable();
+
+ BasicBlock bb = new BasicBlock(label, insns, IntList.EMPTY, -1);
+ addBlock(bb, IntList.EMPTY);
+ }
+
+ /**
+ * Constructs and adds, if necessary, the catch-all exception handler
+ * block to deal with unwinding the lock taken on entry to a synchronized
+ * method.
+ */
+ private void addSynchExceptionHandlerBlock() {
+ if (!synchNeedsExceptionHandler) {
+ /*
+ * The method being converted either isn't synchronized or
+ * can't possibly throw exceptions in its main body, so
+ * there's no need for a synchronized method exception
+ * handler.
+ */
+ return;
+ }
+
+ SourcePosition pos = method.makeSourcePosistion(0);
+ RegisterSpec exReg = RegisterSpec.make(0, Type.THROWABLE);
+ BasicBlock bb;
+ Insn insn;
+
+ InsnList insns = new InsnList(2);
+ insn = new PlainInsn(Rops.opMoveException(Type.THROWABLE), pos,
+ exReg, RegisterSpecList.EMPTY);
+ insns.set(0, insn);
+ insn = new ThrowingInsn(Rops.MONITOR_EXIT, pos,
+ RegisterSpecList.make(getSynchReg()),
+ StdTypeList.EMPTY);
+ insns.set(1, insn);
+ insns.setImmutable();
+
+ int label2 = getSpecialLabel(SYNCH_CATCH_2);
+ bb = new BasicBlock(getSpecialLabel(SYNCH_CATCH_1), insns,
+ IntList.makeImmutable(label2), label2);
+ addBlock(bb, IntList.EMPTY);
+
+ insns = new InsnList(1);
+ insn = new ThrowingInsn(Rops.THROW, pos,
+ RegisterSpecList.make(exReg),
+ StdTypeList.EMPTY);
+ insns.set(0, insn);
+ insns.setImmutable();
+
+ bb = new BasicBlock(label2, insns, IntList.EMPTY, -1);
+ addBlock(bb, IntList.EMPTY);
+ }
+
+ /**
+ * Creates the exception handler setup blocks. "maxLocals"
+ * below is because that's the register number corresponding
+ * to the sole element on a one-deep stack (which is the
+ * situation at the start of an exception handler block).
+ */
+ private void addExceptionSetupBlocks() {
+
+ int len = catchTypes.length;
+ for (int i = 0; i < len; i++) {
+ Type one = catchTypes[i];
+ if (one != null) {
+ Insn proto = labelToBlock(i).getFirstInsn();
+ SourcePosition pos = proto.getPosition();
+ InsnList il = new InsnList(2);
+
+ Insn insn = new PlainInsn(Rops.opMoveException(one),
+ pos,
+ RegisterSpec.make(maxLocals, one),
+ RegisterSpecList.EMPTY);
+ il.set(0, insn);
+
+ insn = new PlainInsn(Rops.GOTO, pos, null,
+ RegisterSpecList.EMPTY);
+ il.set(1, insn);
+ il.setImmutable();
+
+ BasicBlock bb = new BasicBlock(getExceptionSetupLabel(i),
+ il,
+ IntList.makeImmutable(i),
+ i);
+ addBlock(bb, startFrames[i].getSubroutines());
+ }
+ }
+ }
+
+ /**
+ * Checks to see if the basic block is a subroutine caller block.
+ *
+ * @param bb {@code non-null;} the basic block in question
+ * @return true if this block calls a subroutine
+ */
+ private boolean isSubroutineCaller(BasicBlock bb) {
+ IntList successors = bb.getSuccessors();
+ if (successors.size() < 2) return false;
+
+ int subLabel = successors.get(1);
+
+ return (subLabel < subroutines.length)
+ && (subroutines[subLabel] != null);
+ }
+
+ /**
+ * Inlines any subroutine calls.
+ */
+ private void inlineSubroutines() {
+ final IntList reachableSubroutineCallerLabels = new IntList(4);
+
+ /*
+ * Compile a list of all subroutine calls reachable
+ * through the normal (non-subroutine) flow. We do this first, since
+ * we'll be affecting the call flow as we go.
+ *
+ * Start at label 0 -- the param assignment block has nothing for us
+ */
+ forEachNonSubBlockDepthFirst(0, new BasicBlock.Visitor() {
+ public void visitBlock(BasicBlock b) {
+ if (isSubroutineCaller(b)) {
+ reachableSubroutineCallerLabels.add(b.getLabel());
+ }
+ }
+ });
+
+ /*
+ * Convert the resultSubroutines list, indexed by block index,
+ * to a label-to-subroutines mapping used by the inliner.
+ */
+ int largestAllocedLabel = getAvailableLabel();
+ ArrayList<IntList> labelToSubroutines
+ = new ArrayList<IntList>(largestAllocedLabel);
+ for (int i = 0; i < largestAllocedLabel; i++) {
+ labelToSubroutines.add(null);
+ }
+
+ for (int i = 0; i < result.size(); i++) {
+ BasicBlock b = result.get(i);
+ if (b == null) {
+ continue;
+ }
+ IntList subroutineList = resultSubroutines.get(i);
+ labelToSubroutines.set(b.getLabel(), subroutineList);
+ }
+
+ /*
+ * Inline all reachable subroutines.
+ * Inner subroutines will be inlined as they are encountered.
+ */
+ int sz = reachableSubroutineCallerLabels.size();
+ for (int i = 0 ; i < sz ; i++) {
+ int label = reachableSubroutineCallerLabels.get(i);
+ new SubroutineInliner(
+ new LabelAllocator(getAvailableLabel()),
+ labelToSubroutines)
+ .inlineSubroutineCalledFrom(labelToBlock(label));
+ }
+
+ // Now find the blocks that aren't reachable and remove them
+ deleteUnreachableBlocks();
+ }
+
+ /**
+ * Deletes all blocks that cannot be reached. This is run to delete
+ * original subroutine blocks after subroutine inlining.
+ */
+ private void deleteUnreachableBlocks() {
+ final IntList reachableLabels = new IntList(result.size());
+
+ // subroutine inlining is done now and we won't update this list here
+ resultSubroutines.clear();
+
+ forEachNonSubBlockDepthFirst(getSpecialLabel(PARAM_ASSIGNMENT),
+ new BasicBlock.Visitor() {
+
+ public void visitBlock(BasicBlock b) {
+ reachableLabels.add(b.getLabel());
+ }
+ });
+
+ reachableLabels.sort();
+
+ for (int i = result.size() - 1 ; i >= 0 ; i--) {
+ if (reachableLabels.indexOf(result.get(i).getLabel()) < 0) {
+ result.remove(i);
+ // unnecessary here really, since subroutine inlining is done
+ //resultSubroutines.remove(i);
+ }
+ }
+ }
+
+ /**
+ * Allocates labels, without requiring previously allocated labels
+ * to have been added to the blocks list.
+ */
+ private static class LabelAllocator {
+ int nextAvailableLabel;
+
+ /**
+ * @param startLabel available label to start allocating from
+ */
+ LabelAllocator(int startLabel) {
+ nextAvailableLabel = startLabel;
+ }
+
+ /**
+ * @return next available label
+ */
+ int getNextLabel() {
+ return nextAvailableLabel++;
+ }
+ }
+
+ /**
+ * Inlines a subroutine. Start by calling
+ * {@link #inlineSubroutineCalledFrom}.
+ */
+ private class SubroutineInliner {
+ /**
+ * maps original label to the label that will be used by the
+ * inlined version
+ */
+ private final HashMap<Integer, Integer> origLabelToCopiedLabel;
+
+ /** set of original labels that need to be copied */
+ private final BitSet workList;
+
+ /** the label of the original start block for this subroutine */
+ private int subroutineStart;
+
+ /** the label of the ultimate return block */
+ private int subroutineSuccessor;
+
+ /** used for generating new labels for copied blocks */
+ private final LabelAllocator labelAllocator;
+
+ /**
+ * A mapping, indexed by label, to subroutine nesting list.
+ * The subroutine nest list is as returned by
+ * {@link Frame#getSubroutines}.
+ */
+ private final ArrayList<IntList> labelToSubroutines;
+
+ SubroutineInliner(final LabelAllocator labelAllocator,
+ ArrayList<IntList> labelToSubroutines) {
+ origLabelToCopiedLabel = new HashMap<Integer, Integer>();
+
+ workList = new BitSet(maxLabel);
+
+ this.labelAllocator = labelAllocator;
+ this.labelToSubroutines = labelToSubroutines;
+ }
+
+ /**
+ * Inlines a subroutine.
+ *
+ * @param b block where {@code jsr} occurred in the original bytecode
+ */
+ void inlineSubroutineCalledFrom(final BasicBlock b) {
+ /*
+ * The 0th successor of a subroutine caller block is where
+ * the subroutine should return to. The 1st successor is
+ * the start block of the subroutine.
+ */
+ subroutineSuccessor = b.getSuccessors().get(0);
+ subroutineStart = b.getSuccessors().get(1);
+
+ /*
+ * This allocates an initial label and adds the first
+ * block to the worklist.
+ */
+ int newSubStartLabel = mapOrAllocateLabel(subroutineStart);
+
+ for (int label = workList.nextSetBit(0); label >= 0;
+ label = workList.nextSetBit(0)) {
+ workList.clear(label);
+ int newLabel = origLabelToCopiedLabel.get(label);
+
+ copyBlock(label, newLabel);
+
+ if (isSubroutineCaller(labelToBlock(label))) {
+ new SubroutineInliner(labelAllocator, labelToSubroutines)
+ .inlineSubroutineCalledFrom(labelToBlock(newLabel));
+ }
+ }
+
+ /*
+ * Replace the original caller block, since we now have a
+ * new successor
+ */
+
+ addOrReplaceBlockNoDelete(
+ new BasicBlock(b.getLabel(), b.getInsns(),
+ IntList.makeImmutable (newSubStartLabel),
+ newSubStartLabel),
+ labelToSubroutines.get(b.getLabel()));
+ }
+
+ /**
+ * Copies a basic block, mapping its successors along the way.
+ *
+ * @param origLabel original block label
+ * @param newLabel label that the new block should have
+ */
+ private void copyBlock(int origLabel, int newLabel) {
+
+ BasicBlock origBlock = labelToBlock(origLabel);
+
+ final IntList origSuccessors = origBlock.getSuccessors();
+ IntList successors;
+ int primarySuccessor = -1;
+ Subroutine subroutine;
+
+ if (isSubroutineCaller(origBlock)) {
+ /*
+ * A subroutine call inside a subroutine call.
+ * Set up so we can recurse. The caller block should have
+ * it's first successor be a copied block that will be
+ * the subroutine's return point. It's second successor will
+ * be copied when we recurse, and remains as the original
+ * label of the start of the inner subroutine.
+ */
+
+ successors = IntList.makeImmutable(
+ mapOrAllocateLabel(origSuccessors.get(0)),
+ origSuccessors.get(1));
+ // primary successor will be set when this block is replaced
+ } else if (null
+ != (subroutine = subroutineFromRetBlock(origLabel))) {
+ /*
+ * this is a ret block -- its successor
+ * should be subroutineSuccessor
+ */
+
+ // Sanity check
+ if (subroutine.startBlock != subroutineStart) {
+ throw new RuntimeException (
+ "ret instruction returns to label "
+ + Hex.u2 (subroutine.startBlock)
+ + " expected: " + Hex.u2(subroutineStart));
+ }
+
+ successors = IntList.makeImmutable(subroutineSuccessor);
+ primarySuccessor = subroutineSuccessor;
+ } else {
+ // Map all the successor labels
+
+ int origPrimary = origBlock.getPrimarySuccessor();
+ int sz = origSuccessors.size();
+
+ successors = new IntList(sz);
+
+ for (int i = 0 ; i < sz ; i++) {
+ int origSuccLabel = origSuccessors.get(i);
+ int newSuccLabel = mapOrAllocateLabel(origSuccLabel);
+
+ successors.add(newSuccLabel);
+
+ if (origPrimary == origSuccLabel) {
+ primarySuccessor = newSuccLabel;
+ }
+ }
+
+ successors.setImmutable();
+ }
+
+ addBlock (
+ new BasicBlock(newLabel,
+ filterMoveReturnAddressInsns(origBlock.getInsns()),
+ successors, primarySuccessor),
+ labelToSubroutines.get(newLabel));
+ }
+
+ /**
+ * Checks to see if a specified label is involved in a specified
+ * subroutine.
+ *
+ * @param label {@code >= 0;} a basic block label
+ * @param subroutineStart {@code >= 0;} a subroutine as identified
+ * by the label of its start block
+ * @return true if the block is dominated by the subroutine call
+ */
+ private boolean involvedInSubroutine(int label, int subroutineStart) {
+ IntList subroutinesList = labelToSubroutines.get(label);
+ return (subroutinesList.size() > 0
+ && subroutinesList.top() == subroutineStart);
+ }
+
+ /**
+ * Maps the label of a pre-copied block to the label of the inlined
+ * block, allocating a new label and adding it to the worklist
+ * if necessary. If the origLabel is a "special" label, it
+ * is returned exactly and not scheduled for duplication: copying
+ * never proceeds past a special label, which likely is the function
+ * return block or an immediate predecessor.
+ *
+ * @param origLabel label of original, pre-copied block
+ * @return label for new, inlined block
+ */
+ private int mapOrAllocateLabel(int origLabel) {
+ int resultLabel;
+ Integer mappedLabel = origLabelToCopiedLabel.get(origLabel);
+
+ if (mappedLabel != null) {
+ resultLabel = mappedLabel;
+ } else if (!involvedInSubroutine(origLabel,subroutineStart)) {
+ /*
+ * A subroutine has ended by some means other than a "ret"
+ * (which really means a throw caught later).
+ */
+ resultLabel = origLabel;
+ } else {
+ resultLabel = labelAllocator.getNextLabel();
+ workList.set(origLabel);
+ origLabelToCopiedLabel.put(origLabel, resultLabel);
+
+ // The new label has the same frame as the original label
+ while (labelToSubroutines.size() <= resultLabel) {
+ labelToSubroutines.add(null);
+ }
+ labelToSubroutines.set(resultLabel,
+ labelToSubroutines.get(origLabel));
+ }
+
+ return resultLabel;
+ }
+ }
+
+ /**
+ * Finds a {@code Subroutine} that is returned from by a {@code ret} in
+ * a given block.
+ *
+ * @param label A block that originally contained a {@code ret} instruction
+ * @return {@code null-ok;} found subroutine or {@code null} if none
+ * was found
+ */
+ private Subroutine subroutineFromRetBlock(int label) {
+ for (int i = subroutines.length - 1 ; i >= 0 ; i--) {
+ if (subroutines[i] != null) {
+ Subroutine subroutine = subroutines[i];
+
+ if (subroutine.retBlocks.get(label)) {
+ return subroutine;
+ }
+ }
+ }
+
+ return null;
+ }
+
+
+ /**
+ * Removes all {@code move-return-address} instructions, returning a new
+ * {@code InsnList} if necessary. The {@code move-return-address}
+ * insns are dead code after subroutines have been inlined.
+ *
+ * @param insns {@code InsnList} that may contain
+ * {@code move-return-address} insns
+ * @return {@code InsnList} with {@code move-return-address} removed
+ */
+ private InsnList filterMoveReturnAddressInsns(InsnList insns) {
+ int sz;
+ int newSz = 0;
+
+ // First see if we need to filter, and if so what the new size will be
+ sz = insns.size();
+ for (int i = 0; i < sz; i++) {
+ if (insns.get(i).getOpcode() != Rops.MOVE_RETURN_ADDRESS) {
+ newSz++;
+ }
+ }
+
+ if (newSz == sz) {
+ return insns;
+ }
+
+ // Make a new list without the MOVE_RETURN_ADDRESS insns
+ InsnList newInsns = new InsnList(newSz);
+
+ int newIndex = 0;
+ for (int i = 0; i < sz; i++) {
+ Insn insn = insns.get(i);
+ if (insn.getOpcode() != Rops.MOVE_RETURN_ADDRESS) {
+ newInsns.set(newIndex++, insn);
+ }
+ }
+
+ newInsns.setImmutable();
+ return newInsns;
+ }
+
+ /**
+ * Visits each non-subroutine block once in depth-first successor order.
+ *
+ * @param firstLabel label of start block
+ * @param v callback interface
+ */
+ private void forEachNonSubBlockDepthFirst(int firstLabel,
+ BasicBlock.Visitor v) {
+ forEachNonSubBlockDepthFirst0(labelToBlock(firstLabel),
+ v, new BitSet(maxLabel));
+ }
+
+ /**
+ * Visits each block once in depth-first successor order, ignoring
+ * {@code jsr} targets. Worker for {@link #forEachNonSubBlockDepthFirst}.
+ *
+ * @param next next block to visit
+ * @param v callback interface
+ * @param visited set of blocks already visited
+ */
+ private void forEachNonSubBlockDepthFirst0(
+ BasicBlock next, BasicBlock.Visitor v, BitSet visited) {
+ v.visitBlock(next);
+ visited.set(next.getLabel());
+
+ IntList successors = next.getSuccessors();
+ int sz = successors.size();
+
+ for (int i = 0; i < sz; i++) {
+ int succ = successors.get(i);
+
+ if (visited.get(succ)) {
+ continue;
+ }
+
+ if (isSubroutineCaller(next) && i > 0) {
+ // ignore jsr targets
+ continue;
+ }
+
+ /*
+ * Ignore missing labels: they're successors of
+ * subroutines that never invoke a ret.
+ */
+ int idx = labelToResultIndex(succ);
+ if (idx >= 0) {
+ forEachNonSubBlockDepthFirst0(result.get(idx), v, visited);
+ }
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/cf/code/RopperMachine.java b/dx/src/com/android/dx/cf/code/RopperMachine.java
new file mode 100644
index 0000000..ebff24b
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/RopperMachine.java
@@ -0,0 +1,941 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.code.FillArrayDataInsn;
+import com.android.dx.rop.code.Insn;
+import com.android.dx.rop.code.PlainCstInsn;
+import com.android.dx.rop.code.PlainInsn;
+import com.android.dx.rop.code.RegOps;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.Rop;
+import com.android.dx.rop.code.Rops;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.rop.code.SwitchInsn;
+import com.android.dx.rop.code.ThrowingCstInsn;
+import com.android.dx.rop.code.ThrowingInsn;
+import com.android.dx.rop.code.TranslationAdvice;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.IntList;
+
+import java.util.ArrayList;
+
+/**
+ * Machine implementation for use by {@link Ropper}.
+ */
+/*package*/ final class RopperMachine extends ValueAwareMachine {
+ /** {@code non-null;} array reflection class */
+ private static final CstType ARRAY_REFLECT_TYPE =
+ new CstType(Type.internClassName("java/lang/reflect/Array"));
+
+ /**
+ * {@code non-null;} method constant for use in converting
+ * {@code multianewarray} instructions
+ */
+ private static final CstMethodRef MULTIANEWARRAY_METHOD =
+ new CstMethodRef(ARRAY_REFLECT_TYPE,
+ new CstNat(new CstUtf8("newInstance"),
+ new CstUtf8("(Ljava/lang/Class;[I)" +
+ "Ljava/lang/Object;")));
+
+ /** {@code non-null;} {@link Ropper} controlling this instance */
+ private final Ropper ropper;
+
+ /** {@code non-null;} method being converted */
+ private final ConcreteMethod method;
+
+ /** {@code non-null;} translation advice */
+ private final TranslationAdvice advice;
+
+ /** max locals of the method */
+ private final int maxLocals;
+
+ /** {@code non-null;} instructions for the rop basic block in-progress */
+ private final ArrayList<Insn> insns;
+
+ /** {@code non-null;} catches for the block currently being processed */
+ private TypeList catches;
+
+ /** whether the catches have been used in an instruction */
+ private boolean catchesUsed;
+
+ /** whether the block contains a {@code return} */
+ private boolean returns;
+
+ /** primary successor index */
+ private int primarySuccessorIndex;
+
+ /** {@code >= 0;} number of extra basic blocks required */
+ private int extraBlockCount;
+
+ /** true if last processed block ends with a jsr or jsr_W*/
+ private boolean hasJsr;
+
+ /** true if an exception can be thrown by the last block processed */
+ private boolean blockCanThrow;
+
+ /**
+ * If non-null, the ReturnAddress that was used by the terminating ret
+ * instruction. If null, there was no ret instruction encountered.
+ */
+
+ private ReturnAddress returnAddress;
+
+ /**
+ * {@code null-ok;} the appropriate {@code return} op or {@code null}
+ * if it is not yet known
+ */
+ private Rop returnOp;
+
+ /**
+ * {@code null-ok;} the source position for the return block or {@code null}
+ * if it is not yet known
+ */
+ private SourcePosition returnPosition;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param ropper {@code non-null;} ropper controlling this instance
+ * @param method {@code non-null;} method being converted
+ * @param advice {@code non-null;} translation advice to use
+ */
+ public RopperMachine(Ropper ropper, ConcreteMethod method,
+ TranslationAdvice advice) {
+ super(method.getEffectiveDescriptor());
+
+ if (ropper == null) {
+ throw new NullPointerException("ropper == null");
+ }
+
+ if (advice == null) {
+ throw new NullPointerException("advice == null");
+ }
+
+ this.ropper = ropper;
+ this.method = method;
+ this.advice = advice;
+ this.maxLocals = method.getMaxLocals();
+ this.insns = new ArrayList<Insn>(25);
+ this.catches = null;
+ this.catchesUsed = false;
+ this.returns = false;
+ this.primarySuccessorIndex = -1;
+ this.extraBlockCount = 0;
+ this.blockCanThrow = false;
+ this.returnOp = null;
+ this.returnPosition = null;
+ }
+
+ /**
+ * Gets the instructions array. It is shared and gets modified by
+ * subsequent calls to this instance.
+ *
+ * @return {@code non-null;} the instructions array
+ */
+ public ArrayList<Insn> getInsns() {
+ return insns;
+ }
+
+ /**
+ * Gets the return opcode encountered, if any.
+ *
+ * @return {@code null-ok;} the return opcode
+ */
+ public Rop getReturnOp() {
+ return returnOp;
+ }
+
+ /**
+ * Gets the return position, if known.
+ *
+ * @return {@code null-ok;} the return position
+ */
+ public SourcePosition getReturnPosition() {
+ return returnPosition;
+ }
+
+ /**
+ * Gets ready to start working on a new block. This will clear the
+ * {@link #insns} list, set {@link #catches}, reset whether it has
+ * been used, reset whether the block contains a
+ * {@code return}, and reset {@link #primarySuccessorIndex}.
+ */
+ public void startBlock(TypeList catches) {
+ this.catches = catches;
+
+ insns.clear();
+ catchesUsed = false;
+ returns = false;
+ primarySuccessorIndex = 0;
+ extraBlockCount = 0;
+ blockCanThrow = false;
+ hasJsr = false;
+ returnAddress = null;
+ }
+
+ /**
+ * Gets whether {@link #catches} was used. This indicates that the
+ * last instruction in the block is one of the ones that can throw.
+ *
+ * @return whether {@code catches} has been used
+ */
+ public boolean wereCatchesUsed() {
+ return catchesUsed;
+ }
+
+ /**
+ * Gets whether the block just processed ended with a
+ * {@code return}.
+ *
+ * @return whether the block returns
+ */
+ public boolean returns() {
+ return returns;
+ }
+
+ /**
+ * Gets the primary successor index. This is the index into the
+ * successors list where the primary may be found or
+ * {@code -1} if there are successors but no primary
+ * successor. This may return something other than
+ * {@code -1} in the case of an instruction with no
+ * successors at all (primary or otherwise).
+ *
+ * @return {@code >= -1;} the primary successor index
+ */
+ public int getPrimarySuccessorIndex() {
+ return primarySuccessorIndex;
+ }
+
+ /**
+ * Gets how many extra blocks will be needed to represent the
+ * block currently being translated. Each extra block should consist
+ * of one instruction from the end of the original block.
+ *
+ * @return {@code >= 0;} the number of extra blocks needed
+ */
+ public int getExtraBlockCount() {
+ return extraBlockCount;
+ }
+
+ /**
+ * @return true if at least one of the insn processed since the last
+ * call to startBlock() can throw.
+ */
+ public boolean canThrow() {
+ return blockCanThrow;
+ }
+
+ /**
+ * @return true if a JSR has ben encountered since the last call to
+ * startBlock()
+ */
+ public boolean hasJsr() {
+ return hasJsr;
+ }
+
+ /**
+ * @return {@code true} if a {@code ret} has ben encountered since
+ * the last call to {@code startBlock()}
+ */
+ public boolean hasRet() {
+ return returnAddress != null;
+ }
+
+ /**
+ * @return {@code null-ok;} return address of a {@code ret}
+ * instruction if encountered since last call to startBlock().
+ * {@code null} if no ret instruction encountered.
+ */
+ public ReturnAddress getReturnAddress() {
+ return returnAddress;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void run(Frame frame, int offset, int opcode) {
+ /*
+ * This is the stack pointer after the opcode's arguments have been
+ * popped.
+ */
+ int stackPointer = maxLocals + frame.getStack().size();
+
+ // The sources have to be retrieved before super.run() gets called.
+ RegisterSpecList sources = getSources(opcode, stackPointer);
+ int sourceCount = sources.size();
+
+ super.run(frame, offset, opcode);
+
+ SourcePosition pos = method.makeSourcePosistion(offset);
+ RegisterSpec localTarget = getLocalTarget();
+ int destCount = resultCount();
+ RegisterSpec dest;
+
+ if (destCount == 0) {
+ dest = null;
+ switch (opcode) {
+ case ByteOps.POP:
+ case ByteOps.POP2: {
+ // These simply don't appear in the rop form.
+ return;
+ }
+ }
+ } else if (localTarget != null) {
+ dest = localTarget;
+ } else if (destCount == 1) {
+ dest = RegisterSpec.make(stackPointer, result(0));
+ } else {
+ /*
+ * This clause only ever applies to the stack manipulation
+ * ops that have results (that is, dup* and swap but not
+ * pop*).
+ *
+ * What we do is first move all the source registers into
+ * the "temporary stack" area defined for the method, and
+ * then move stuff back down onto the main "stack" in the
+ * arrangement specified by the stack op pattern.
+ *
+ * Note: This code ends up emitting a lot of what will
+ * turn out to be superfluous moves (e.g., moving back and
+ * forth to the same local when doing a dup); however,
+ * that makes this code a bit easier (and goodness knows
+ * it doesn't need any extra complexity), and all the SSA
+ * stuff is going to want to deal with this sort of
+ * superfluous assignment anyway, so it should be a wash
+ * in the end.
+ */
+ int scratchAt = ropper.getFirstTempStackReg();
+ RegisterSpec[] scratchRegs = new RegisterSpec[sourceCount];
+
+ for (int i = 0; i < sourceCount; i++) {
+ RegisterSpec src = sources.get(i);
+ TypeBearer type = src.getTypeBearer();
+ RegisterSpec scratch = src.withReg(scratchAt);
+ insns.add(new PlainInsn(Rops.opMove(type), pos, scratch, src));
+ scratchRegs[i] = scratch;
+ scratchAt += src.getCategory();
+ }
+
+ for (int pattern = getAuxInt(); pattern != 0; pattern >>= 4) {
+ int which = (pattern & 0x0f) - 1;
+ RegisterSpec scratch = scratchRegs[which];
+ TypeBearer type = scratch.getTypeBearer();
+ insns.add(new PlainInsn(Rops.opMove(type), pos,
+ scratch.withReg(stackPointer),
+ scratch));
+ stackPointer += type.getType().getCategory();
+ }
+ return;
+ }
+
+ TypeBearer destType = (dest != null) ? dest : Type.VOID;
+ Constant cst = getAuxCst();
+ int ropOpcode;
+ Rop rop;
+ Insn insn;
+
+ if (opcode == ByteOps.MULTIANEWARRAY) {
+ blockCanThrow = true;
+
+ // Add the extra instructions for handling multianewarray.
+
+ extraBlockCount = 6;
+
+ /*
+ * Add an array constructor for the int[] containing all the
+ * dimensions.
+ */
+ RegisterSpec dimsReg =
+ RegisterSpec.make(dest.getNextReg(), Type.INT_ARRAY);
+ rop = Rops.opFilledNewArray(Type.INT_ARRAY, sourceCount);
+ insn = new ThrowingCstInsn(rop, pos, sources, catches,
+ CstType.INT_ARRAY);
+ insns.add(insn);
+
+ // Add a move-result for the new-filled-array
+ rop = Rops.opMoveResult(Type.INT_ARRAY);
+ insn = new PlainInsn(rop, pos, dimsReg, RegisterSpecList.EMPTY);
+ insns.add(insn);
+
+ /*
+ * Add a const-class instruction for the specified array
+ * class.
+ */
+
+ /*
+ * Remove as many dimensions from the originally specified
+ * class as are given in the explicit list of dimensions,
+ * so as to pass the right component class to the standard
+ * Java library array constructor.
+ */
+ Type componentType = ((CstType) cst).getClassType();
+ for (int i = 0; i < sourceCount; i++) {
+ componentType = componentType.getComponentType();
+ }
+
+ RegisterSpec classReg =
+ RegisterSpec.make(dest.getReg(), Type.CLASS);
+
+ if (componentType.isPrimitive()) {
+ /*
+ * The component type is primitive (e.g., int as opposed
+ * to Integer), so we have to fetch the corresponding
+ * TYPE class.
+ */
+ CstFieldRef typeField =
+ CstFieldRef.forPrimitiveType(componentType);
+ insn = new ThrowingCstInsn(Rops.GET_STATIC_OBJECT, pos,
+ RegisterSpecList.EMPTY,
+ catches, typeField);
+ } else {
+ /*
+ * The component type is an object type, so just make a
+ * normal class reference.
+ */
+ insn = new ThrowingCstInsn(Rops.CONST_OBJECT, pos,
+ RegisterSpecList.EMPTY, catches,
+ new CstType(componentType));
+ }
+
+ insns.add(insn);
+
+ // Add a move-result-pseudo for the get-static or const
+ rop = Rops.opMoveResultPseudo(classReg.getType());
+ insn = new PlainInsn(rop, pos, classReg, RegisterSpecList.EMPTY);
+ insns.add(insn);
+
+ /*
+ * Add a call to the "multianewarray method," that is,
+ * Array.newInstance(class, dims). Note: The result type
+ * of newInstance() is Object, which is why the last
+ * instruction in this sequence is a cast to the right
+ * type for the original instruction.
+ */
+
+ RegisterSpec objectReg =
+ RegisterSpec.make(dest.getReg(), Type.OBJECT);
+
+ insn = new ThrowingCstInsn(
+ Rops.opInvokeStatic(MULTIANEWARRAY_METHOD.getPrototype()),
+ pos, RegisterSpecList.make(classReg, dimsReg),
+ catches, MULTIANEWARRAY_METHOD);
+ insns.add(insn);
+
+ // Add a move-result.
+ rop = Rops.opMoveResult(MULTIANEWARRAY_METHOD.getPrototype()
+ .getReturnType());
+ insn = new PlainInsn(rop, pos, objectReg, RegisterSpecList.EMPTY);
+ insns.add(insn);
+
+ /*
+ * And finally, set up for the remainder of this method to
+ * add an appropriate cast.
+ */
+
+ opcode = ByteOps.CHECKCAST;
+ sources = RegisterSpecList.make(objectReg);
+ } else if (opcode == ByteOps.JSR) {
+ // JSR has no Rop instruction
+ hasJsr = true;
+ return;
+ } else if (opcode == ByteOps.RET) {
+ try {
+ returnAddress = (ReturnAddress)arg(0);
+ } catch (ClassCastException ex) {
+ throw new RuntimeException(
+ "Argument to RET was not a ReturnAddress", ex);
+ }
+ // RET has no Rop instruction.
+ return;
+ }
+
+ ropOpcode = jopToRopOpcode(opcode, cst);
+ rop = Rops.ropFor(ropOpcode, destType, sources, cst);
+
+ Insn moveResult = null;
+ if (dest != null && rop.isCallLike()) {
+ /*
+ * We're going to want to have a move-result in the next
+ * basic block.
+ */
+ extraBlockCount++;
+
+ moveResult = new PlainInsn(
+ Rops.opMoveResult(((CstMethodRef) cst).getPrototype()
+ .getReturnType()), pos, dest, RegisterSpecList.EMPTY);
+
+ dest = null;
+ } else if (dest != null && rop.canThrow()) {
+ /*
+ * We're going to want to have a move-result-pseudo in the
+ * next basic block.
+ */
+ extraBlockCount++;
+
+ moveResult = new PlainInsn(
+ Rops.opMoveResultPseudo(dest.getTypeBearer()),
+ pos, dest, RegisterSpecList.EMPTY);
+
+ dest = null;
+ }
+ if (ropOpcode == RegOps.NEW_ARRAY) {
+ /*
+ * In the original bytecode, this was either a primitive
+ * array constructor "newarray" or an object array
+ * constructor "anewarray". In the former case, there is
+ * no explicit constant, and in the latter, the constant
+ * is for the element type and not the array type. The rop
+ * instruction form for both of these is supposed to be
+ * the resulting array type, so we initialize / alter
+ * "cst" here, accordingly. Conveniently enough, the rop
+ * opcode already gets constructed with the proper array
+ * type.
+ */
+ cst = CstType.intern(rop.getResult());
+ } else if ((cst == null) && (sourceCount == 2)) {
+ TypeBearer lastType = sources.get(1).getTypeBearer();
+
+ if (lastType.isConstant()
+ && advice.hasConstantOperation(rop,
+ sources.get(0), sources.get(1))) {
+ /*
+ * The target architecture has an instruction that can
+ * build in the constant found in the second argument,
+ * so pull it out of the sources and just use it as a
+ * constant here.
+ */
+ cst = (Constant) lastType;
+ sources = sources.withoutLast();
+ rop = Rops.ropFor(ropOpcode, destType, sources, cst);
+ }
+ }
+
+ SwitchList cases = getAuxCases();
+ ArrayList<Constant> initValues = getInitValues();
+ boolean canThrow = rop.canThrow();
+
+ blockCanThrow |= canThrow;
+
+ if (cases != null) {
+ if (cases.size() == 0) {
+ // It's a default-only switch statement. It can happen!
+ insn = new PlainInsn(Rops.GOTO, pos, null,
+ RegisterSpecList.EMPTY);
+ primarySuccessorIndex = 0;
+ } else {
+ IntList values = cases.getValues();
+ insn = new SwitchInsn(rop, pos, dest, sources, values);
+ primarySuccessorIndex = values.size();
+ }
+ } else if (ropOpcode == RegOps.RETURN) {
+ /*
+ * Returns get turned into the combination of a move (if
+ * non-void and if the return doesn't already mention
+ * register 0) and a goto (to the return block).
+ */
+ if (sources.size() != 0) {
+ RegisterSpec source = sources.get(0);
+ TypeBearer type = source.getTypeBearer();
+ if (source.getReg() != 0) {
+ insns.add(new PlainInsn(Rops.opMove(type), pos,
+ RegisterSpec.make(0, type),
+ source));
+ }
+ }
+ insn = new PlainInsn(Rops.GOTO, pos, null, RegisterSpecList.EMPTY);
+ primarySuccessorIndex = 0;
+ updateReturnOp(rop, pos);
+ returns = true;
+ } else if (cst != null) {
+ if (canThrow) {
+ insn =
+ new ThrowingCstInsn(rop, pos, sources, catches, cst);
+ catchesUsed = true;
+ primarySuccessorIndex = catches.size();
+ } else {
+ insn = new PlainCstInsn(rop, pos, dest, sources, cst);
+ }
+ } else if (canThrow) {
+ insn = new ThrowingInsn(rop, pos, sources, catches);
+ catchesUsed = true;
+ if (opcode == ByteOps.ATHROW) {
+ /*
+ * The op athrow is the only one where it's possible
+ * to have non-empty successors and yet not have a
+ * primary successor.
+ */
+ primarySuccessorIndex = -1;
+ } else {
+ primarySuccessorIndex = catches.size();
+ }
+ } else {
+ insn = new PlainInsn(rop, pos, dest, sources);
+ }
+
+ insns.add(insn);
+
+ if (moveResult != null) {
+ insns.add(moveResult);
+ }
+
+ /*
+ * If initValues is non-null, it means that the parser has
+ * seen a group of compatible constant initialization
+ * bytecodes that are applied to the current newarray. The
+ * action we take here is to convert these initialization
+ * bytecodes into a single fill-array-data ROP which lays out
+ * all the constant values in a table.
+ */
+ if (initValues != null) {
+ extraBlockCount++;
+ insn = new FillArrayDataInsn(Rops.FILL_ARRAY_DATA, pos,
+ RegisterSpecList.make(moveResult.getResult()), initValues,
+ cst);
+ insns.add(insn);
+ }
+ }
+
+ /**
+ * Helper for {@link #run}, which gets the list of sources for the.
+ * instruction.
+ *
+ * @param opcode the opcode being translated
+ * @param stackPointer {@code >= 0;} the stack pointer after the
+ * instruction's arguments have been popped
+ * @return {@code non-null;} the sources
+ */
+ private RegisterSpecList getSources(int opcode, int stackPointer) {
+ int count = argCount();
+
+ if (count == 0) {
+ // We get an easy out if there aren't any sources.
+ return RegisterSpecList.EMPTY;
+ }
+
+ int localIndex = getLocalIndex();
+ RegisterSpecList sources;
+
+ if (localIndex >= 0) {
+ // The instruction is operating on a local variable.
+ sources = new RegisterSpecList(1);
+ sources.set(0, RegisterSpec.make(localIndex, arg(0)));
+ } else {
+ sources = new RegisterSpecList(count);
+ int regAt = stackPointer;
+ for (int i = 0; i < count; i++) {
+ RegisterSpec spec = RegisterSpec.make(regAt, arg(i));
+ sources.set(i, spec);
+ regAt += spec.getCategory();
+ }
+
+ switch (opcode) {
+ case ByteOps.IASTORE: {
+ /*
+ * The Java argument order for array stores is
+ * (array, index, value), but the rop argument
+ * order is (value, array, index). The following
+ * code gets the right arguments in the right
+ * places.
+ */
+ if (count != 3) {
+ throw new RuntimeException("shouldn't happen");
+ }
+ RegisterSpec array = sources.get(0);
+ RegisterSpec index = sources.get(1);
+ RegisterSpec value = sources.get(2);
+ sources.set(0, value);
+ sources.set(1, array);
+ sources.set(2, index);
+ break;
+ }
+ case ByteOps.PUTFIELD: {
+ /*
+ * Similar to above: The Java argument order for
+ * putfield is (object, value), but the rop
+ * argument order is (value, object).
+ */
+ if (count != 2) {
+ throw new RuntimeException("shouldn't happen");
+ }
+ RegisterSpec obj = sources.get(0);
+ RegisterSpec value = sources.get(1);
+ sources.set(0, value);
+ sources.set(1, obj);
+ break;
+ }
+ }
+ }
+
+ sources.setImmutable();
+ return sources;
+ }
+
+ /**
+ * Sets or updates the information about the return block.
+ *
+ * @param op {@code non-null;} the opcode to use
+ * @param pos {@code non-null;} the position to use
+ */
+ private void updateReturnOp(Rop op, SourcePosition pos) {
+ if (op == null) {
+ throw new NullPointerException("op == null");
+ }
+
+ if (pos == null) {
+ throw new NullPointerException("pos == null");
+ }
+
+ if (returnOp == null) {
+ returnOp = op;
+ returnPosition = pos;
+ } else {
+ if (returnOp != op) {
+ throw new SimException("return op mismatch: " + op + ", " +
+ returnOp);
+ }
+
+ if (pos.getLine() > returnPosition.getLine()) {
+ // Pick the largest line number to be the "canonical" return.
+ returnPosition = pos;
+ }
+ }
+ }
+
+ /**
+ * Gets the register opcode for the given Java opcode.
+ *
+ * @param jop {@code >= 0;} the Java opcode
+ * @param cst {@code null-ok;} the constant argument, if any
+ * @return {@code >= 0;} the corresponding register opcode
+ */
+ private int jopToRopOpcode(int jop, Constant cst) {
+ switch (jop) {
+ case ByteOps.POP:
+ case ByteOps.POP2:
+ case ByteOps.DUP:
+ case ByteOps.DUP_X1:
+ case ByteOps.DUP_X2:
+ case ByteOps.DUP2:
+ case ByteOps.DUP2_X1:
+ case ByteOps.DUP2_X2:
+ case ByteOps.SWAP:
+ case ByteOps.JSR:
+ case ByteOps.RET:
+ case ByteOps.MULTIANEWARRAY: {
+ // These need to be taken care of specially.
+ break;
+ }
+ case ByteOps.NOP: {
+ return RegOps.NOP;
+ }
+ case ByteOps.LDC:
+ case ByteOps.LDC2_W: {
+ return RegOps.CONST;
+ }
+ case ByteOps.ILOAD:
+ case ByteOps.ISTORE: {
+ return RegOps.MOVE;
+ }
+ case ByteOps.IALOAD: {
+ return RegOps.AGET;
+ }
+ case ByteOps.IASTORE: {
+ return RegOps.APUT;
+ }
+ case ByteOps.IADD:
+ case ByteOps.IINC: {
+ return RegOps.ADD;
+ }
+ case ByteOps.ISUB: {
+ return RegOps.SUB;
+ }
+ case ByteOps.IMUL: {
+ return RegOps.MUL;
+ }
+ case ByteOps.IDIV: {
+ return RegOps.DIV;
+ }
+ case ByteOps.IREM: {
+ return RegOps.REM;
+ }
+ case ByteOps.INEG: {
+ return RegOps.NEG;
+ }
+ case ByteOps.ISHL: {
+ return RegOps.SHL;
+ }
+ case ByteOps.ISHR: {
+ return RegOps.SHR;
+ }
+ case ByteOps.IUSHR: {
+ return RegOps.USHR;
+ }
+ case ByteOps.IAND: {
+ return RegOps.AND;
+ }
+ case ByteOps.IOR: {
+ return RegOps.OR;
+ }
+ case ByteOps.IXOR: {
+ return RegOps.XOR;
+ }
+ case ByteOps.I2L:
+ case ByteOps.I2F:
+ case ByteOps.I2D:
+ case ByteOps.L2I:
+ case ByteOps.L2F:
+ case ByteOps.L2D:
+ case ByteOps.F2I:
+ case ByteOps.F2L:
+ case ByteOps.F2D:
+ case ByteOps.D2I:
+ case ByteOps.D2L:
+ case ByteOps.D2F: {
+ return RegOps.CONV;
+ }
+ case ByteOps.I2B: {
+ return RegOps.TO_BYTE;
+ }
+ case ByteOps.I2C: {
+ return RegOps.TO_CHAR;
+ }
+ case ByteOps.I2S: {
+ return RegOps.TO_SHORT;
+ }
+ case ByteOps.LCMP:
+ case ByteOps.FCMPL:
+ case ByteOps.DCMPL: {
+ return RegOps.CMPL;
+ }
+ case ByteOps.FCMPG:
+ case ByteOps.DCMPG: {
+ return RegOps.CMPG;
+ }
+ case ByteOps.IFEQ:
+ case ByteOps.IF_ICMPEQ:
+ case ByteOps.IF_ACMPEQ:
+ case ByteOps.IFNULL: {
+ return RegOps.IF_EQ;
+ }
+ case ByteOps.IFNE:
+ case ByteOps.IF_ICMPNE:
+ case ByteOps.IF_ACMPNE:
+ case ByteOps.IFNONNULL: {
+ return RegOps.IF_NE;
+ }
+ case ByteOps.IFLT:
+ case ByteOps.IF_ICMPLT: {
+ return RegOps.IF_LT;
+ }
+ case ByteOps.IFGE:
+ case ByteOps.IF_ICMPGE: {
+ return RegOps.IF_GE;
+ }
+ case ByteOps.IFGT:
+ case ByteOps.IF_ICMPGT: {
+ return RegOps.IF_GT;
+ }
+ case ByteOps.IFLE:
+ case ByteOps.IF_ICMPLE: {
+ return RegOps.IF_LE;
+ }
+ case ByteOps.GOTO: {
+ return RegOps.GOTO;
+ }
+ case ByteOps.LOOKUPSWITCH: {
+ return RegOps.SWITCH;
+ }
+ case ByteOps.IRETURN:
+ case ByteOps.RETURN: {
+ return RegOps.RETURN;
+ }
+ case ByteOps.GETSTATIC: {
+ return RegOps.GET_STATIC;
+ }
+ case ByteOps.PUTSTATIC: {
+ return RegOps.PUT_STATIC;
+ }
+ case ByteOps.GETFIELD: {
+ return RegOps.GET_FIELD;
+ }
+ case ByteOps.PUTFIELD: {
+ return RegOps.PUT_FIELD;
+ }
+ case ByteOps.INVOKEVIRTUAL: {
+ return RegOps.INVOKE_VIRTUAL;
+ }
+ case ByteOps.INVOKESPECIAL: {
+ /*
+ * Determine whether the opcode should be
+ * INVOKE_DIRECT or INVOKE_SUPER. See vmspec-2 section 6
+ * on "invokespecial" as well as section 4.8.2 (7th
+ * bullet point) for the gory details.
+ */
+ CstMethodRef ref = (CstMethodRef) cst;
+ if (ref.isInstanceInit() ||
+ (ref.getDefiningClass() == method.getDefiningClass()) ||
+ !method.getAccSuper()) {
+ return RegOps.INVOKE_DIRECT;
+ }
+ return RegOps.INVOKE_SUPER;
+ }
+ case ByteOps.INVOKESTATIC: {
+ return RegOps.INVOKE_STATIC;
+ }
+ case ByteOps.INVOKEINTERFACE: {
+ return RegOps.INVOKE_INTERFACE;
+ }
+ case ByteOps.NEW: {
+ return RegOps.NEW_INSTANCE;
+ }
+ case ByteOps.NEWARRAY:
+ case ByteOps.ANEWARRAY: {
+ return RegOps.NEW_ARRAY;
+ }
+ case ByteOps.ARRAYLENGTH: {
+ return RegOps.ARRAY_LENGTH;
+ }
+ case ByteOps.ATHROW: {
+ return RegOps.THROW;
+ }
+ case ByteOps.CHECKCAST: {
+ return RegOps.CHECK_CAST;
+ }
+ case ByteOps.INSTANCEOF: {
+ return RegOps.INSTANCE_OF;
+ }
+ case ByteOps.MONITORENTER: {
+ return RegOps.MONITOR_ENTER;
+ }
+ case ByteOps.MONITOREXIT: {
+ return RegOps.MONITOR_EXIT;
+ }
+ }
+
+ throw new RuntimeException("shouldn't happen");
+ }
+}
diff --git a/dx/src/com/android/dx/cf/code/SimException.java b/dx/src/com/android/dx/cf/code/SimException.java
new file mode 100644
index 0000000..220f281
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/SimException.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.util.ExceptionWithContext;
+
+/**
+ * Exception from simulation.
+ */
+public class SimException
+ extends ExceptionWithContext {
+ public SimException(String message) {
+ super(message);
+ }
+
+ public SimException(Throwable cause) {
+ super(cause);
+ }
+
+ public SimException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/dx/src/com/android/dx/cf/code/Simulator.java b/dx/src/com/android/dx/cf/code/Simulator.java
new file mode 100644
index 0000000..f96699e
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/Simulator.java
@@ -0,0 +1,720 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.CstInterfaceMethodRef;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Prototype;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.rop.code.LocalItem;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IntList;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Class which knows how to simulate the effects of executing bytecode.
+ *
+ * <p><b>Note:</b> This class is not thread-safe. If multiple threads
+ * need to use a single instance, they must synchronize access explicitly
+ * between themselves.</p>
+ */
+public class Simulator {
+ /**
+ * {@code non-null;} canned error message for local variable
+ * table mismatches
+ */
+ private static final String LOCAL_MISMATCH_ERROR =
+ "This is symptomatic of .class transformation tools that ignore " +
+ "local variable information.";
+
+ /** {@code non-null;} machine to use when simulating */
+ private final Machine machine;
+
+ /** {@code non-null;} array of bytecode */
+ private final BytecodeArray code;
+
+ /** {@code non-null;} local variable information */
+ private final LocalVariableList localVariables;
+
+ /** {@code non-null;} visitor instance to use */
+ private final SimVisitor visitor;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param machine {@code non-null;} machine to use when simulating
+ * @param method {@code non-null;} method data to use
+ */
+ public Simulator(Machine machine, ConcreteMethod method) {
+ if (machine == null) {
+ throw new NullPointerException("machine == null");
+ }
+
+ if (method == null) {
+ throw new NullPointerException("method == null");
+ }
+
+ this.machine = machine;
+ this.code = method.getCode();
+ this.localVariables = method.getLocalVariables();
+ this.visitor = new SimVisitor();
+ }
+
+ /**
+ * Simulates the effect of executing the given basic block. This modifies
+ * the passed-in frame to represent the end result.
+ *
+ * @param bb {@code non-null;} the basic block
+ * @param frame {@code non-null;} frame to operate on
+ */
+ public void simulate(ByteBlock bb, Frame frame) {
+ int end = bb.getEnd();
+
+ visitor.setFrame(frame);
+
+ try {
+ for (int off = bb.getStart(); off < end; /*off*/) {
+ int length = code.parseInstruction(off, visitor);
+ visitor.setPreviousOffset(off);
+ off += length;
+ }
+ } catch (SimException ex) {
+ frame.annotate(ex);
+ throw ex;
+ }
+ }
+
+ /**
+ * Simulates the effect of the instruction at the given offset, by
+ * making appropriate calls on the given frame.
+ *
+ * @param offset {@code >= 0;} offset of the instruction to simulate
+ * @param frame {@code non-null;} frame to operate on
+ * @return the length of the instruction, in bytes
+ */
+ public int simulate(int offset, Frame frame) {
+ visitor.setFrame(frame);
+ return code.parseInstruction(offset, visitor);
+ }
+
+ /**
+ * Constructs an "illegal top-of-stack" exception, for the stack
+ * manipulation opcodes.
+ */
+ private static SimException illegalTos() {
+ return new SimException("stack mismatch: illegal " +
+ "top-of-stack for opcode");
+ }
+
+ /**
+ * Bytecode visitor used during simulation.
+ */
+ private class SimVisitor implements BytecodeArray.Visitor {
+ /**
+ * {@code non-null;} machine instance to use (just to avoid excessive
+ * cross-object field access)
+ */
+ private final Machine machine;
+
+ /**
+ * {@code null-ok;} frame to use; set with each call to
+ * {@link Simulator#simulate}
+ */
+ private Frame frame;
+
+ /** offset of the previous bytecode */
+ private int previousOffset;
+
+ /**
+ * Constructs an instance.
+ */
+ public SimVisitor() {
+ this.machine = Simulator.this.machine;
+ this.frame = null;
+ }
+
+ /**
+ * Sets the frame to act on.
+ *
+ * @param frame {@code non-null;} the frame
+ */
+ public void setFrame(Frame frame) {
+ if (frame == null) {
+ throw new NullPointerException("frame == null");
+ }
+
+ this.frame = frame;
+ }
+
+ /** {@inheritDoc} */
+ public void visitInvalid(int opcode, int offset, int length) {
+ throw new SimException("invalid opcode " + Hex.u1(opcode));
+ }
+
+ /** {@inheritDoc} */
+ public void visitNoArgs(int opcode, int offset, int length,
+ Type type) {
+ switch (opcode) {
+ case ByteOps.NOP: {
+ machine.clearArgs();
+ break;
+ }
+ case ByteOps.INEG: {
+ machine.popArgs(frame, type);
+ break;
+ }
+ case ByteOps.I2L:
+ case ByteOps.I2F:
+ case ByteOps.I2D:
+ case ByteOps.I2B:
+ case ByteOps.I2C:
+ case ByteOps.I2S: {
+ machine.popArgs(frame, Type.INT);
+ break;
+ }
+ case ByteOps.L2I:
+ case ByteOps.L2F:
+ case ByteOps.L2D: {
+ machine.popArgs(frame, Type.LONG);
+ break;
+ }
+ case ByteOps.F2I:
+ case ByteOps.F2L:
+ case ByteOps.F2D: {
+ machine.popArgs(frame, Type.FLOAT);
+ break;
+ }
+ case ByteOps.D2I:
+ case ByteOps.D2L:
+ case ByteOps.D2F: {
+ machine.popArgs(frame, Type.DOUBLE);
+ break;
+ }
+ case ByteOps.RETURN: {
+ machine.clearArgs();
+ checkReturnType(Type.VOID);
+ break;
+ }
+ case ByteOps.IRETURN: {
+ Type checkType = type;
+ if (type == Type.OBJECT) {
+ /*
+ * For an object return, use the best-known
+ * type of the popped value.
+ */
+ checkType = frame.getStack().peekType(0);
+ }
+ machine.popArgs(frame, type);
+ checkReturnType(checkType);
+ break;
+ }
+ case ByteOps.POP: {
+ Type peekType = frame.getStack().peekType(0);
+ if (peekType.isCategory2()) {
+ throw illegalTos();
+ }
+ machine.popArgs(frame, 1);
+ break;
+ }
+ case ByteOps.ARRAYLENGTH: {
+ Type arrayType = frame.getStack().peekType(0);
+ if (!arrayType.isArrayOrKnownNull()) {
+ throw new SimException("type mismatch: expected " +
+ "array type but encountered " +
+ arrayType.toHuman());
+ }
+ machine.popArgs(frame, Type.OBJECT);
+ break;
+ }
+ case ByteOps.ATHROW:
+ case ByteOps.MONITORENTER:
+ case ByteOps.MONITOREXIT: {
+ machine.popArgs(frame, Type.OBJECT);
+ break;
+ }
+ case ByteOps.IALOAD: {
+ /*
+ * Change the type (which is to be pushed) to
+ * reflect the actual component type of the array
+ * being popped, unless it turns out to be a
+ * known-null, in which case we just use the type
+ * implied by the original instruction.
+ */
+ Type foundArrayType = frame.getStack().peekType(1);
+ Type requireArrayType;
+
+ if (foundArrayType != Type.KNOWN_NULL) {
+ requireArrayType = foundArrayType;
+ type = foundArrayType.getComponentType();
+ } else {
+ requireArrayType = type.getArrayType();
+ }
+
+ machine.popArgs(frame, requireArrayType, Type.INT);
+ break;
+ }
+ case ByteOps.IADD:
+ case ByteOps.ISUB:
+ case ByteOps.IMUL:
+ case ByteOps.IDIV:
+ case ByteOps.IREM:
+ case ByteOps.IAND:
+ case ByteOps.IOR:
+ case ByteOps.IXOR: {
+ machine.popArgs(frame, type, type);
+ break;
+ }
+ case ByteOps.ISHL:
+ case ByteOps.ISHR:
+ case ByteOps.IUSHR: {
+ machine.popArgs(frame, type, Type.INT);
+ break;
+ }
+ case ByteOps.LCMP: {
+ machine.popArgs(frame, Type.LONG, Type.LONG);
+ break;
+ }
+ case ByteOps.FCMPL:
+ case ByteOps.FCMPG: {
+ machine.popArgs(frame, Type.FLOAT, Type.FLOAT);
+ break;
+ }
+ case ByteOps.DCMPL:
+ case ByteOps.DCMPG: {
+ machine.popArgs(frame, Type.DOUBLE, Type.DOUBLE);
+ break;
+ }
+ case ByteOps.IASTORE: {
+ /*
+ * Change the type (which is the type of the
+ * element) to reflect the actual component type
+ * of the array being popped, unless it turns out
+ * to be a known-null, in which case we just use
+ * the type implied by the original instruction.
+ * The category 1 vs. 2 thing here is that, if the
+ * element type is category 2, we have to skip over
+ * one extra stack slot to find the array.
+ */
+ Type foundArrayType =
+ frame.getStack().peekType(type.isCategory1() ? 2 : 3);
+ Type requireArrayType;
+
+ if (foundArrayType != Type.KNOWN_NULL) {
+ requireArrayType = foundArrayType;
+ type = foundArrayType.getComponentType();
+ } else {
+ requireArrayType = type.getArrayType();
+ }
+
+ machine.popArgs(frame, requireArrayType, Type.INT, type);
+ break;
+ }
+ case ByteOps.POP2:
+ case ByteOps.DUP2: {
+ ExecutionStack stack = frame.getStack();
+ int pattern;
+
+ if (stack.peekType(0).isCategory2()) {
+ // "form 2" in vmspec-2
+ machine.popArgs(frame, 1);
+ pattern = 0x11;
+ } else if (stack.peekType(1).isCategory1()) {
+ // "form 1"
+ machine.popArgs(frame, 2);
+ pattern = 0x2121;
+ } else {
+ throw illegalTos();
+ }
+
+ if (opcode == ByteOps.DUP2) {
+ machine.auxIntArg(pattern);
+ }
+ break;
+ }
+ case ByteOps.DUP: {
+ Type peekType = frame.getStack().peekType(0);
+
+ if (peekType.isCategory2()) {
+ throw illegalTos();
+ }
+
+ machine.popArgs(frame, 1);
+ machine.auxIntArg(0x11);
+ break;
+ }
+ case ByteOps.DUP_X1: {
+ ExecutionStack stack = frame.getStack();
+
+ if (! (stack.peekType(0).isCategory1() &&
+ stack.peekType(1).isCategory1())) {
+ throw illegalTos();
+ }
+
+ machine.popArgs(frame, 2);
+ machine.auxIntArg(0x212);
+ break;
+ }
+ case ByteOps.DUP_X2: {
+ ExecutionStack stack = frame.getStack();
+
+ if (stack.peekType(0).isCategory2()) {
+ throw illegalTos();
+ }
+
+ if (stack.peekType(1).isCategory2()) {
+ // "form 2" in vmspec-2
+ machine.popArgs(frame, 2);
+ machine.auxIntArg(0x212);
+ } else if (stack.peekType(2).isCategory1()) {
+ // "form 1"
+ machine.popArgs(frame, 3);
+ machine.auxIntArg(0x3213);
+ } else {
+ throw illegalTos();
+ }
+ break;
+ }
+ case ByteOps.DUP2_X1: {
+ ExecutionStack stack = frame.getStack();
+
+ if (stack.peekType(0).isCategory2()) {
+ // "form 2" in vmspec-2
+ if (stack.peekType(2).isCategory2()) {
+ throw illegalTos();
+ }
+ machine.popArgs(frame, 2);
+ machine.auxIntArg(0x212);
+ } else {
+ // "form 1"
+ if (stack.peekType(1).isCategory2() ||
+ stack.peekType(2).isCategory2()) {
+ throw illegalTos();
+ }
+ machine.popArgs(frame, 3);
+ machine.auxIntArg(0x32132);
+ }
+ break;
+ }
+ case ByteOps.DUP2_X2: {
+ ExecutionStack stack = frame.getStack();
+
+ if (stack.peekType(0).isCategory2()) {
+ if (stack.peekType(2).isCategory2()) {
+ // "form 4" in vmspec-2
+ machine.popArgs(frame, 2);
+ machine.auxIntArg(0x212);
+ } else if (stack.peekType(3).isCategory1()) {
+ // "form 2"
+ machine.popArgs(frame, 3);
+ machine.auxIntArg(0x3213);
+ } else {
+ throw illegalTos();
+ }
+ } else if (stack.peekType(1).isCategory1()) {
+ if (stack.peekType(2).isCategory2()) {
+ // "form 3"
+ machine.popArgs(frame, 3);
+ machine.auxIntArg(0x32132);
+ } else if (stack.peekType(3).isCategory1()) {
+ // "form 1"
+ machine.popArgs(frame, 4);
+ machine.auxIntArg(0x432143);
+ } else {
+ throw illegalTos();
+ }
+ } else {
+ throw illegalTos();
+ }
+ break;
+ }
+ case ByteOps.SWAP: {
+ ExecutionStack stack = frame.getStack();
+
+ if (! (stack.peekType(0).isCategory1() &&
+ stack.peekType(1).isCategory1())) {
+ throw illegalTos();
+ }
+
+ machine.popArgs(frame, 2);
+ machine.auxIntArg(0x12);
+ break;
+ }
+ default: {
+ visitInvalid(opcode, offset, length);
+ return;
+ }
+ }
+
+ machine.auxType(type);
+ machine.run(frame, offset, opcode);
+ }
+
+ /**
+ * Checks whether the prototype is compatible with returning the
+ * given type, and throws if not.
+ *
+ * @param encountered {@code non-null;} the encountered return type
+ */
+ private void checkReturnType(Type encountered) {
+ Type returnType = machine.getPrototype().getReturnType();
+
+ /*
+ * Check to see if the prototype's return type is
+ * possibly assignable from the type we encountered. This
+ * takes care of all the salient cases (types are the same,
+ * they're compatible primitive types, etc.).
+ */
+ if (! Merger.isPossiblyAssignableFrom(returnType, encountered)) {
+ throw new SimException("return type mismatch: prototype " +
+ "indicates " + returnType.toHuman() +
+ ", but encountered type " + encountered.toHuman());
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void visitLocal(int opcode, int offset, int length,
+ int idx, Type type, int value) {
+ /*
+ * Note that the "type" parameter is always the simplest
+ * type based on the original opcode, e.g., "int" for
+ * "iload" (per se) and "Object" for "aload". So, when
+ * possible, we replace the type with the one indicated in
+ * the local variable table, though we still need to check
+ * to make sure it's valid for the opcode.
+ *
+ * The reason we use (offset + length) for the localOffset
+ * for a store is because it is only after the store that
+ * the local type becomes valid. On the other hand, the
+ * type associated with a load is valid at the start of
+ * the instruction.
+ */
+ int localOffset =
+ (opcode == ByteOps.ISTORE) ? (offset + length) : offset;
+ LocalVariableList.Item local =
+ localVariables.pcAndIndexToLocal(localOffset, idx);
+ Type localType;
+
+ if (local != null) {
+ localType = local.getType();
+ if (localType.getBasicFrameType() !=
+ type.getBasicFrameType()) {
+ BaseMachine.throwLocalMismatch(type, localType);
+ return;
+ }
+ } else {
+ localType = type;
+ }
+
+ switch (opcode) {
+ case ByteOps.ILOAD:
+ case ByteOps.RET: {
+ machine.localArg(frame, idx);
+ machine.auxType(type);
+ break;
+ }
+ case ByteOps.ISTORE: {
+ LocalItem item
+ = (local == null) ? null : local.getLocalItem();
+ machine.popArgs(frame, type);
+ machine.auxType(type);
+ machine.localTarget(idx, localType, item);
+ break;
+ }
+ case ByteOps.IINC: {
+ LocalItem item
+ = (local == null) ? null : local.getLocalItem();
+ machine.localArg(frame, idx);
+ machine.localTarget(idx, localType, item);
+ machine.auxType(type);
+ machine.auxIntArg(value);
+ machine.auxCstArg(CstInteger.make(value));
+ break;
+ }
+ default: {
+ visitInvalid(opcode, offset, length);
+ return;
+ }
+ }
+
+ machine.run(frame, offset, opcode);
+ }
+
+ /** {@inheritDoc} */
+ public void visitConstant(int opcode, int offset, int length,
+ Constant cst, int value) {
+ switch (opcode) {
+ case ByteOps.ANEWARRAY: {
+ machine.popArgs(frame, Type.INT);
+ break;
+ }
+ case ByteOps.PUTSTATIC: {
+ Type fieldType = ((CstFieldRef) cst).getType();
+ machine.popArgs(frame, fieldType);
+ break;
+ }
+ case ByteOps.GETFIELD:
+ case ByteOps.CHECKCAST:
+ case ByteOps.INSTANCEOF: {
+ machine.popArgs(frame, Type.OBJECT);
+ break;
+ }
+ case ByteOps.PUTFIELD: {
+ Type fieldType = ((CstFieldRef) cst).getType();
+ machine.popArgs(frame, Type.OBJECT, fieldType);
+ break;
+ }
+ case ByteOps.INVOKEINTERFACE: {
+ /*
+ * Convert the interface method ref into a normal
+ * method ref.
+ */
+ cst = ((CstInterfaceMethodRef) cst).toMethodRef();
+ // and fall through...
+ }
+ case ByteOps.INVOKEVIRTUAL:
+ case ByteOps.INVOKESPECIAL: {
+ /*
+ * Get the instance prototype, and use it to direct
+ * the machine.
+ */
+ Prototype prototype =
+ ((CstMethodRef) cst).getPrototype(false);
+ machine.popArgs(frame, prototype);
+ break;
+ }
+ case ByteOps.INVOKESTATIC: {
+ /*
+ * Get the static prototype, and use it to direct
+ * the machine.
+ */
+ Prototype prototype =
+ ((CstMethodRef) cst).getPrototype(true);
+ machine.popArgs(frame, prototype);
+ break;
+ }
+ case ByteOps.MULTIANEWARRAY: {
+ /*
+ * The "value" here is the count of dimensions to
+ * create. Make a prototype of that many "int"
+ * types, and tell the machine to pop them. This
+ * isn't the most efficient way in the world to do
+ * this, but then again, multianewarray is pretty
+ * darn rare and so not worth much effort
+ * optimizing for.
+ */
+ Prototype prototype =
+ Prototype.internInts(Type.VOID, value);
+ machine.popArgs(frame, prototype);
+ break;
+ }
+ default: {
+ machine.clearArgs();
+ break;
+ }
+ }
+
+ machine.auxIntArg(value);
+ machine.auxCstArg(cst);
+ machine.run(frame, offset, opcode);
+ }
+
+ /** {@inheritDoc} */
+ public void visitBranch(int opcode, int offset, int length,
+ int target) {
+ switch (opcode) {
+ case ByteOps.IFEQ:
+ case ByteOps.IFNE:
+ case ByteOps.IFLT:
+ case ByteOps.IFGE:
+ case ByteOps.IFGT:
+ case ByteOps.IFLE: {
+ machine.popArgs(frame, Type.INT);
+ break;
+ }
+ case ByteOps.IFNULL:
+ case ByteOps.IFNONNULL: {
+ machine.popArgs(frame, Type.OBJECT);
+ break;
+ }
+ case ByteOps.IF_ICMPEQ:
+ case ByteOps.IF_ICMPNE:
+ case ByteOps.IF_ICMPLT:
+ case ByteOps.IF_ICMPGE:
+ case ByteOps.IF_ICMPGT:
+ case ByteOps.IF_ICMPLE: {
+ machine.popArgs(frame, Type.INT, Type.INT);
+ break;
+ }
+ case ByteOps.IF_ACMPEQ:
+ case ByteOps.IF_ACMPNE: {
+ machine.popArgs(frame, Type.OBJECT, Type.OBJECT);
+ break;
+ }
+ case ByteOps.GOTO:
+ case ByteOps.JSR:
+ case ByteOps.GOTO_W:
+ case ByteOps.JSR_W: {
+ machine.clearArgs();
+ break;
+ }
+ default: {
+ visitInvalid(opcode, offset, length);
+ return;
+ }
+ }
+
+ machine.auxTargetArg(target);
+ machine.run(frame, offset, opcode);
+ }
+
+ /** {@inheritDoc} */
+ public void visitSwitch(int opcode, int offset, int length,
+ SwitchList cases, int padding) {
+ machine.popArgs(frame, Type.INT);
+ machine.auxIntArg(padding);
+ machine.auxSwitchArg(cases);
+ machine.run(frame, offset, opcode);
+ }
+
+ /** {@inheritDoc} */
+ public void visitNewarray(int offset, int length, CstType type,
+ ArrayList<Constant> initValues) {
+ machine.popArgs(frame, Type.INT);
+ machine.auxInitValues(initValues);
+ machine.auxCstArg(type);
+ machine.run(frame, offset, ByteOps.NEWARRAY);
+ }
+
+ /** {@inheritDoc} */
+ public void setPreviousOffset(int offset) {
+ previousOffset = offset;
+ }
+
+ /** {@inheritDoc} */
+ public int getPreviousOffset() {
+ return previousOffset;
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/cf/code/SwitchList.java b/dx/src/com/android/dx/cf/code/SwitchList.java
new file mode 100644
index 0000000..621d728
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/SwitchList.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.util.IntList;
+import com.android.dx.util.MutabilityControl;
+
+/**
+ * List of (value, target) mappings representing the choices of a
+ * {@code tableswitch} or {@code lookupswitch} instruction. It
+ * also holds the default target for the switch.
+ */
+public final class SwitchList extends MutabilityControl {
+ /** {@code non-null;} list of test values */
+ private final IntList values;
+
+ /**
+ * {@code non-null;} list of targets corresponding to the test values; there
+ * is always one extra element in the target list, to hold the
+ * default target
+ */
+ private final IntList targets;
+
+ /** ultimate size of the list */
+ private int size;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param size {@code >= 0;} the number of elements to be in the table
+ */
+ public SwitchList(int size) {
+ super(true);
+ this.values = new IntList(size);
+ this.targets = new IntList(size + 1);
+ this.size = size;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setImmutable() {
+ values.setImmutable();
+ targets.setImmutable();
+ super.setImmutable();
+ }
+
+ /**
+ * Gets the size of the list.
+ *
+ * @return {@code >= 0;} the list size
+ */
+ public int size() {
+ return size;
+ }
+
+ /**
+ * Gets the indicated test value.
+ *
+ * @param n {@code >= 0;}, &lt; size(); which index
+ * @return the test value
+ */
+ public int getValue(int n) {
+ return values.get(n);
+ }
+
+ /**
+ * Gets the indicated target. Asking for the target at {@code size()}
+ * returns the default target.
+ *
+ * @param n {@code >= 0, <= size();} which index
+ * @return {@code >= 0;} the target
+ */
+ public int getTarget(int n) {
+ return targets.get(n);
+ }
+
+ /**
+ * Gets the default target. This is just a shorthand for
+ * {@code getTarget(size())}.
+ *
+ * @return {@code >= 0;} the default target
+ */
+ public int getDefaultTarget() {
+ return targets.get(size);
+ }
+
+ /**
+ * Gets the list of all targets. This includes one extra element at the
+ * end of the list, which holds the default target.
+ *
+ * @return {@code non-null;} the target list
+ */
+ public IntList getTargets() {
+ return targets;
+ }
+
+ /**
+ * Gets the list of all case values.
+ *
+ * @return {@code non-null;} the case value list
+ */
+ public IntList getValues() {
+ return values;
+ }
+
+ /**
+ * Sets the default target. It is only valid to call this method
+ * when all the non-default elements have been set.
+ *
+ * @param target {@code >= 0;} the absolute (not relative) default target
+ * address
+ */
+ public void setDefaultTarget(int target) {
+ throwIfImmutable();
+
+ if (target < 0) {
+ throw new IllegalArgumentException("target < 0");
+ }
+
+ if (targets.size() != size) {
+ throw new RuntimeException("non-default elements not all set");
+ }
+
+ targets.add(target);
+ }
+
+ /**
+ * Adds the given item.
+ *
+ * @param value the test value
+ * @param target {@code >= 0;} the absolute (not relative) target address
+ */
+ public void add(int value, int target) {
+ throwIfImmutable();
+
+ if (target < 0) {
+ throw new IllegalArgumentException("target < 0");
+ }
+
+ values.add(value);
+ targets.add(target);
+ }
+
+ /**
+ * Shrinks this instance if possible, removing test elements that
+ * refer to the default target. This is only valid after the instance
+ * is fully populated, including the default target (naturally).
+ */
+ public void removeSuperfluousDefaults() {
+ throwIfImmutable();
+
+ int sz = size;
+
+ if (sz != (targets.size() - 1)) {
+ throw new IllegalArgumentException("incomplete instance");
+ }
+
+ int defaultTarget = targets.get(sz);
+ int at = 0;
+
+ for (int i = 0; i < sz; i++) {
+ int target = targets.get(i);
+ if (target != defaultTarget) {
+ if (i != at) {
+ targets.set(at, target);
+ values.set(at, values.get(i));
+ }
+ at++;
+ }
+ }
+
+ if (at != sz) {
+ values.shrink(at);
+ targets.set(at, defaultTarget);
+ targets.shrink(at + 1);
+ size = at;
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/cf/code/ValueAwareMachine.java b/dx/src/com/android/dx/cf/code/ValueAwareMachine.java
new file mode 100644
index 0000000..de75db5
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/ValueAwareMachine.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.code;
+
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Prototype;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.util.Hex;
+
+/**
+ * {@link Machine} which keeps track of known values but does not do
+ * smart/realistic reference type calculations.
+ */
+public class ValueAwareMachine extends BaseMachine {
+ /**
+ * Constructs an instance.
+ *
+ * @param prototype {@code non-null;} the prototype for the associated
+ * method
+ */
+ public ValueAwareMachine(Prototype prototype) {
+ super(prototype);
+ }
+
+ /** {@inheritDoc} */
+ public void run(Frame frame, int offset, int opcode) {
+ switch (opcode) {
+ case ByteOps.NOP:
+ case ByteOps.IASTORE:
+ case ByteOps.POP:
+ case ByteOps.POP2:
+ case ByteOps.IFEQ:
+ case ByteOps.IFNE:
+ case ByteOps.IFLT:
+ case ByteOps.IFGE:
+ case ByteOps.IFGT:
+ case ByteOps.IFLE:
+ case ByteOps.IF_ICMPEQ:
+ case ByteOps.IF_ICMPNE:
+ case ByteOps.IF_ICMPLT:
+ case ByteOps.IF_ICMPGE:
+ case ByteOps.IF_ICMPGT:
+ case ByteOps.IF_ICMPLE:
+ case ByteOps.IF_ACMPEQ:
+ case ByteOps.IF_ACMPNE:
+ case ByteOps.GOTO:
+ case ByteOps.RET:
+ case ByteOps.LOOKUPSWITCH:
+ case ByteOps.IRETURN:
+ case ByteOps.RETURN:
+ case ByteOps.PUTSTATIC:
+ case ByteOps.PUTFIELD:
+ case ByteOps.ATHROW:
+ case ByteOps.MONITORENTER:
+ case ByteOps.MONITOREXIT:
+ case ByteOps.IFNULL:
+ case ByteOps.IFNONNULL: {
+ // Nothing to do for these ops in this class.
+ clearResult();
+ break;
+ }
+ case ByteOps.LDC:
+ case ByteOps.LDC2_W: {
+ setResult((TypeBearer) getAuxCst());
+ break;
+ }
+ case ByteOps.ILOAD:
+ case ByteOps.ISTORE: {
+ setResult(arg(0));
+ break;
+ }
+ case ByteOps.IALOAD:
+ case ByteOps.IADD:
+ case ByteOps.ISUB:
+ case ByteOps.IMUL:
+ case ByteOps.IDIV:
+ case ByteOps.IREM:
+ case ByteOps.INEG:
+ case ByteOps.ISHL:
+ case ByteOps.ISHR:
+ case ByteOps.IUSHR:
+ case ByteOps.IAND:
+ case ByteOps.IOR:
+ case ByteOps.IXOR:
+ case ByteOps.IINC:
+ case ByteOps.I2L:
+ case ByteOps.I2F:
+ case ByteOps.I2D:
+ case ByteOps.L2I:
+ case ByteOps.L2F:
+ case ByteOps.L2D:
+ case ByteOps.F2I:
+ case ByteOps.F2L:
+ case ByteOps.F2D:
+ case ByteOps.D2I:
+ case ByteOps.D2L:
+ case ByteOps.D2F:
+ case ByteOps.I2B:
+ case ByteOps.I2C:
+ case ByteOps.I2S:
+ case ByteOps.LCMP:
+ case ByteOps.FCMPL:
+ case ByteOps.FCMPG:
+ case ByteOps.DCMPL:
+ case ByteOps.DCMPG:
+ case ByteOps.ARRAYLENGTH: {
+ setResult(getAuxType());
+ break;
+ }
+ case ByteOps.DUP:
+ case ByteOps.DUP_X1:
+ case ByteOps.DUP_X2:
+ case ByteOps.DUP2:
+ case ByteOps.DUP2_X1:
+ case ByteOps.DUP2_X2:
+ case ByteOps.SWAP: {
+ clearResult();
+ for (int pattern = getAuxInt(); pattern != 0; pattern >>= 4) {
+ int which = (pattern & 0x0f) - 1;
+ addResult(arg(which));
+ }
+ break;
+ }
+
+ case ByteOps.JSR: {
+ setResult(new ReturnAddress(getAuxTarget()));
+ break;
+ }
+ case ByteOps.GETSTATIC:
+ case ByteOps.GETFIELD:
+ case ByteOps.INVOKEVIRTUAL:
+ case ByteOps.INVOKESTATIC:
+ case ByteOps.INVOKEINTERFACE: {
+ Type type = ((TypeBearer) getAuxCst()).getType();
+ if (type == Type.VOID) {
+ clearResult();
+ } else {
+ setResult(type);
+ }
+ break;
+ }
+ case ByteOps.INVOKESPECIAL: {
+ Type thisType = arg(0).getType();
+ if (thisType.isUninitialized()) {
+ frame.makeInitialized(thisType);
+ }
+ Type type = ((TypeBearer) getAuxCst()).getType();
+ if (type == Type.VOID) {
+ clearResult();
+ } else {
+ setResult(type);
+ }
+ break;
+ }
+ case ByteOps.NEW: {
+ Type type = ((CstType) getAuxCst()).getClassType();
+ setResult(type.asUninitialized(offset));
+ break;
+ }
+ case ByteOps.NEWARRAY:
+ case ByteOps.CHECKCAST:
+ case ByteOps.MULTIANEWARRAY: {
+ Type type = ((CstType) getAuxCst()).getClassType();
+ setResult(type);
+ break;
+ }
+ case ByteOps.ANEWARRAY: {
+ Type type = ((CstType) getAuxCst()).getClassType();
+ setResult(type.getArrayType());
+ break;
+ }
+ case ByteOps.INSTANCEOF: {
+ setResult(Type.INT);
+ break;
+ }
+ default: {
+ throw new RuntimeException("shouldn't happen: " +
+ Hex.u1(opcode));
+ }
+ }
+
+ storeResults(frame);
+ }
+}
diff --git a/dx/src/com/android/dx/cf/code/package.html b/dx/src/com/android/dx/cf/code/package.html
new file mode 100644
index 0000000..abd4e9b
--- /dev/null
+++ b/dx/src/com/android/dx/cf/code/package.html
@@ -0,0 +1,10 @@
+<body>
+<p>Implementation of classes having to do with Java simulation, such as
+is needed for verification or stack-to-register conversion.</p>
+
+<p><b>PACKAGES USED:</b>
+<ul>
+<li><code>com.android.dx.rop.pool</code></li>
+<li><code>com.android.dx.util</code></li>
+</ul>
+</body>
diff --git a/dx/src/com/android/dx/cf/cst/ConstantPoolParser.java b/dx/src/com/android/dx/cf/cst/ConstantPoolParser.java
new file mode 100644
index 0000000..5eecfa6
--- /dev/null
+++ b/dx/src/com/android/dx/cf/cst/ConstantPoolParser.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.cst;
+
+import com.android.dx.cf.iface.ParseException;
+import com.android.dx.cf.iface.ParseObserver;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstDouble;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstFloat;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.CstInterfaceMethodRef;
+import com.android.dx.rop.cst.CstLong;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.cst.StdConstantPool;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+
+import static com.android.dx.cf.cst.ConstantTags.*;
+
+/**
+ * Parser for a constant pool embedded in a class file.
+ */
+public final class ConstantPoolParser {
+ /** {@code non-null;} the bytes of the constant pool */
+ private final ByteArray bytes;
+
+ /** {@code non-null;} actual parsed constant pool contents */
+ private final StdConstantPool pool;
+
+ /** {@code non-null;} byte offsets to each cst */
+ private final int[] offsets;
+
+ /**
+ * -1 || &gt;= 10; the end offset of this constant pool in the
+ * {@code byte[]} which it came from or {@code -1} if not
+ * yet parsed
+ */
+ private int endOffset;
+
+ /** {@code null-ok;} parse observer, if any */
+ private ParseObserver observer;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param bytes {@code non-null;} the bytes of the file
+ */
+ public ConstantPoolParser(ByteArray bytes) {
+ int size = bytes.getUnsignedShort(8); // constant_pool_count
+
+ this.bytes = bytes;
+ this.pool = new StdConstantPool(size);
+ this.offsets = new int[size];
+ this.endOffset = -1;
+ }
+
+ /**
+ * Sets the parse observer for this instance.
+ *
+ * @param observer {@code null-ok;} the observer
+ */
+ public void setObserver(ParseObserver observer) {
+ this.observer = observer;
+ }
+
+ /**
+ * Gets the end offset of this constant pool in the {@code byte[]}
+ * which it came from.
+ *
+ * @return {@code >= 10;} the end offset
+ */
+ public int getEndOffset() {
+ parseIfNecessary();
+ return endOffset;
+ }
+
+ /**
+ * Gets the actual constant pool.
+ *
+ * @return {@code non-null;} the constant pool
+ */
+ public StdConstantPool getPool() {
+ parseIfNecessary();
+ return pool;
+ }
+
+ /**
+ * Runs {@link #parse} if it has not yet been run successfully.
+ */
+ private void parseIfNecessary() {
+ if (endOffset < 0) {
+ parse();
+ }
+ }
+
+ /**
+ * Does the actual parsing.
+ */
+ private void parse() {
+ determineOffsets();
+
+ if (observer != null) {
+ observer.parsed(bytes, 8, 2,
+ "constant_pool_count: " + Hex.u2(offsets.length));
+ observer.parsed(bytes, 10, 0, "\nconstant_pool:");
+ observer.changeIndent(1);
+ }
+
+ for (int i = 1; i < offsets.length; i++) {
+ int offset = offsets[i];
+ if ((offset != 0) && (pool.getOrNull(i) == null)) {
+ parse0(i);
+ }
+ }
+
+ if (observer != null) {
+ for (int i = 1; i < offsets.length; i++) {
+ Constant cst = pool.getOrNull(i);
+ if (cst == null) {
+ continue;
+ }
+ int offset = offsets[i];
+ int nextOffset = endOffset;
+ for (int j = i + 1; j < offsets.length; j++) {
+ int off = offsets[j];
+ if (off != 0) {
+ nextOffset = off;
+ break;
+ }
+ }
+ observer.parsed(bytes, offset, nextOffset - offset,
+ Hex.u2(i) + ": " + cst.toString());
+ }
+
+ observer.changeIndent(-1);
+ observer.parsed(bytes, endOffset, 0, "end constant_pool");
+ }
+ }
+
+ /**
+ * Populates {@link #offsets} and also completely parse utf8 constants.
+ */
+ private void determineOffsets() {
+ int at = 10; // offset from the start of the file to the first cst
+ int lastCategory;
+
+ for (int i = 1; i < offsets.length; i += lastCategory) {
+ offsets[i] = at;
+ int tag = bytes.getUnsignedByte(at);
+ switch (tag) {
+ case CONSTANT_Integer:
+ case CONSTANT_Float:
+ case CONSTANT_Fieldref:
+ case CONSTANT_Methodref:
+ case CONSTANT_InterfaceMethodref:
+ case CONSTANT_NameAndType: {
+ lastCategory = 1;
+ at += 5;
+ break;
+ }
+ case CONSTANT_Long:
+ case CONSTANT_Double: {
+ lastCategory = 2;
+ at += 9;
+ break;
+ }
+ case CONSTANT_Class:
+ case CONSTANT_String: {
+ lastCategory = 1;
+ at += 3;
+ break;
+ }
+ case CONSTANT_Utf8: {
+ lastCategory = 1;
+ at += bytes.getUnsignedShort(at + 1) + 3;
+ break;
+ }
+ default: {
+ ParseException ex =
+ new ParseException("unknown tag byte: " + Hex.u1(tag));
+ ex.addContext("...while preparsing cst " + Hex.u2(i) +
+ " at offset " + Hex.u4(at));
+ throw ex;
+ }
+ }
+ }
+
+ endOffset = at;
+ }
+
+ /**
+ * Parses the constant for the given index if it hasn't already been
+ * parsed, also storing it in the constant pool. This will also
+ * have the side effect of parsing any entries the indicated one
+ * depends on.
+ *
+ * @param idx which constant
+ * @return {@code non-null;} the parsed constant
+ */
+ private Constant parse0(int idx) {
+ Constant cst = pool.getOrNull(idx);
+ if (cst != null) {
+ return cst;
+ }
+
+ int at = offsets[idx];
+
+ try {
+ int tag = bytes.getUnsignedByte(at);
+ switch (tag) {
+ case CONSTANT_Utf8: {
+ cst = parseUtf8(at);
+ break;
+ }
+ case CONSTANT_Integer: {
+ int value = bytes.getInt(at + 1);
+ cst = CstInteger.make(value);
+ break;
+ }
+ case CONSTANT_Float: {
+ int bits = bytes.getInt(at + 1);
+ cst = CstFloat.make(bits);
+ break;
+ }
+ case CONSTANT_Long: {
+ long value = bytes.getLong(at + 1);
+ cst = CstLong.make(value);
+ break;
+ }
+ case CONSTANT_Double: {
+ long bits = bytes.getLong(at + 1);
+ cst = CstDouble.make(bits);
+ break;
+ }
+ case CONSTANT_Class: {
+ int nameIndex = bytes.getUnsignedShort(at + 1);
+ CstUtf8 name = (CstUtf8) parse0(nameIndex);
+ cst = new CstType(Type.internClassName(name.getString()));
+ break;
+ }
+ case CONSTANT_String: {
+ int stringIndex = bytes.getUnsignedShort(at + 1);
+ CstUtf8 string = (CstUtf8) parse0(stringIndex);
+ cst = new CstString(string);
+ break;
+ }
+ case CONSTANT_Fieldref: {
+ int classIndex = bytes.getUnsignedShort(at + 1);
+ CstType type = (CstType) parse0(classIndex);
+ int natIndex = bytes.getUnsignedShort(at + 3);
+ CstNat nat = (CstNat) parse0(natIndex);
+ cst = new CstFieldRef(type, nat);
+ break;
+ }
+ case CONSTANT_Methodref: {
+ int classIndex = bytes.getUnsignedShort(at + 1);
+ CstType type = (CstType) parse0(classIndex);
+ int natIndex = bytes.getUnsignedShort(at + 3);
+ CstNat nat = (CstNat) parse0(natIndex);
+ cst = new CstMethodRef(type, nat);
+ break;
+ }
+ case CONSTANT_InterfaceMethodref: {
+ int classIndex = bytes.getUnsignedShort(at + 1);
+ CstType type = (CstType) parse0(classIndex);
+ int natIndex = bytes.getUnsignedShort(at + 3);
+ CstNat nat = (CstNat) parse0(natIndex);
+ cst = new CstInterfaceMethodRef(type, nat);
+ break;
+ }
+ case CONSTANT_NameAndType: {
+ int nameIndex = bytes.getUnsignedShort(at + 1);
+ CstUtf8 name = (CstUtf8) parse0(nameIndex);
+ int descriptorIndex = bytes.getUnsignedShort(at + 3);
+ CstUtf8 descriptor = (CstUtf8) parse0(descriptorIndex);
+ cst = new CstNat(name, descriptor);
+ break;
+ }
+ }
+ } catch (ParseException ex) {
+ ex.addContext("...while parsing cst " + Hex.u2(idx) +
+ " at offset " + Hex.u4(at));
+ throw ex;
+ } catch (RuntimeException ex) {
+ ParseException pe = new ParseException(ex);
+ pe.addContext("...while parsing cst " + Hex.u2(idx) +
+ " at offset " + Hex.u4(at));
+ throw pe;
+ }
+
+ pool.set(idx, cst);
+ return cst;
+ }
+
+ /**
+ * Parses a utf8 constant.
+ *
+ * @param at offset to the start of the constant (where the tag byte is)
+ * @return {@code non-null;} the parsed value
+ */
+ private CstUtf8 parseUtf8(int at) {
+ int length = bytes.getUnsignedShort(at + 1);
+
+ at += 3; // Skip to the data.
+
+ ByteArray ubytes = bytes.slice(at, at + length);
+
+ try {
+ return new CstUtf8(ubytes);
+ } catch (IllegalArgumentException ex) {
+ // Translate the exception
+ throw new ParseException(ex);
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/cf/cst/ConstantTags.java b/dx/src/com/android/dx/cf/cst/ConstantTags.java
new file mode 100644
index 0000000..9febbdf
--- /dev/null
+++ b/dx/src/com/android/dx/cf/cst/ConstantTags.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.cst;
+
+/**
+ * Tags for constant pool constants.
+ */
+public interface ConstantTags {
+ /** tag for a {@code CONSTANT_Utf8_info} */
+ int CONSTANT_Utf8 = 1;
+
+ /** tag for a {@code CONSTANT_Integer_info} */
+ int CONSTANT_Integer = 3;
+
+ /** tag for a {@code CONSTANT_Float_info} */
+ int CONSTANT_Float = 4;
+
+ /** tag for a {@code CONSTANT_Long_info} */
+ int CONSTANT_Long = 5;
+
+ /** tag for a {@code CONSTANT_Double_info} */
+ int CONSTANT_Double = 6;
+
+ /** tag for a {@code CONSTANT_Class_info} */
+ int CONSTANT_Class = 7;
+
+ /** tag for a {@code CONSTANT_String_info} */
+ int CONSTANT_String = 8;
+
+ /** tag for a {@code CONSTANT_Fieldref_info} */
+ int CONSTANT_Fieldref = 9;
+
+ /** tag for a {@code CONSTANT_Methodref_info} */
+ int CONSTANT_Methodref = 10;
+
+ /** tag for a {@code CONSTANT_InterfaceMethodref_info} */
+ int CONSTANT_InterfaceMethodref = 11;
+
+ /** tag for a {@code CONSTANT_NameAndType_info} */
+ int CONSTANT_NameAndType = 12;
+}
diff --git a/dx/src/com/android/dx/cf/direct/AnnotationParser.java b/dx/src/com/android/dx/cf/direct/AnnotationParser.java
new file mode 100644
index 0000000..ca38fc5
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/AnnotationParser.java
@@ -0,0 +1,474 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.direct;
+
+import com.android.dx.cf.iface.ParseException;
+import com.android.dx.cf.iface.ParseObserver;
+import com.android.dx.rop.annotation.Annotation;
+import com.android.dx.rop.annotation.AnnotationVisibility;
+import com.android.dx.rop.annotation.Annotations;
+import com.android.dx.rop.annotation.AnnotationsList;
+import com.android.dx.rop.annotation.NameValuePair;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.ConstantPool;
+import com.android.dx.rop.cst.CstAnnotation;
+import com.android.dx.rop.cst.CstArray;
+import com.android.dx.rop.cst.CstBoolean;
+import com.android.dx.rop.cst.CstByte;
+import com.android.dx.rop.cst.CstChar;
+import com.android.dx.rop.cst.CstDouble;
+import com.android.dx.rop.cst.CstEnumRef;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstFloat;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.CstLong;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstShort;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+
+import java.io.IOException;
+
+/**
+ * Parser for annotations.
+ */
+public final class AnnotationParser {
+ /** {@code non-null;} class file being parsed */
+ private final DirectClassFile cf;
+
+ /** {@code non-null;} constant pool to use */
+ private final ConstantPool pool;
+
+ /** {@code non-null;} bytes of the attribute data */
+ private final ByteArray bytes;
+
+ /** {@code null-ok;} parse observer, if any */
+ private final ParseObserver observer;
+
+ /** {@code non-null;} input stream to parse from */
+ private final ByteArray.MyDataInputStream input;
+
+ /**
+ * {@code non-null;} cursor for use when informing the observer of what
+ * was parsed
+ */
+ private int parseCursor;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param cf {@code non-null;} class file to parse from
+ * @param offset {@code >= 0;} offset into the class file data to parse at
+ * @param length {@code >= 0;} number of bytes left in the attribute data
+ * @param observer {@code null-ok;} parse observer to notify, if any
+ */
+ public AnnotationParser(DirectClassFile cf, int offset, int length,
+ ParseObserver observer) {
+ if (cf == null) {
+ throw new NullPointerException("cf == null");
+ }
+
+ this.cf = cf;
+ this.pool = cf.getConstantPool();
+ this.observer = observer;
+ this.bytes = cf.getBytes().slice(offset, offset + length);
+ this.input = bytes.makeDataInputStream();
+ this.parseCursor = 0;
+ }
+
+ /**
+ * Parses an annotation value ({@code element_value}) attribute.
+ *
+ * @return {@code non-null;} the parsed constant value
+ */
+ public Constant parseValueAttribute() {
+ Constant result;
+
+ try {
+ result = parseValue();
+
+ if (input.available() != 0) {
+ throw new ParseException("extra data in attribute");
+ }
+ } catch (IOException ex) {
+ // ByteArray.MyDataInputStream should never throw.
+ throw new RuntimeException("shouldn't happen", ex);
+ }
+
+ return result;
+ }
+
+ /**
+ * Parses a parameter annotation attribute.
+ *
+ * @param visibility {@code non-null;} visibility of the parsed annotations
+ * @return {@code non-null;} the parsed list of lists of annotations
+ */
+ public AnnotationsList parseParameterAttribute(
+ AnnotationVisibility visibility) {
+ AnnotationsList result;
+
+ try {
+ result = parseAnnotationsList(visibility);
+
+ if (input.available() != 0) {
+ throw new ParseException("extra data in attribute");
+ }
+ } catch (IOException ex) {
+ // ByteArray.MyDataInputStream should never throw.
+ throw new RuntimeException("shouldn't happen", ex);
+ }
+
+ return result;
+ }
+
+ /**
+ * Parses an annotation attribute, per se.
+ *
+ * @param visibility {@code non-null;} visibility of the parsed annotations
+ * @return {@code non-null;} the list of annotations read from the attribute
+ * data
+ */
+ public Annotations parseAnnotationAttribute(
+ AnnotationVisibility visibility) {
+ Annotations result;
+
+ try {
+ result = parseAnnotations(visibility);
+
+ if (input.available() != 0) {
+ throw new ParseException("extra data in attribute");
+ }
+ } catch (IOException ex) {
+ // ByteArray.MyDataInputStream should never throw.
+ throw new RuntimeException("shouldn't happen", ex);
+ }
+
+ return result;
+ }
+
+ /**
+ * Parses a list of annotation lists.
+ *
+ * @param visibility {@code non-null;} visibility of the parsed annotations
+ * @return {@code non-null;} the list of annotation lists read from the attribute
+ * data
+ */
+ private AnnotationsList parseAnnotationsList(
+ AnnotationVisibility visibility) throws IOException {
+ int count = input.readUnsignedByte();
+
+ if (observer != null) {
+ parsed(1, "num_parameters: " + Hex.u1(count));
+ }
+
+ AnnotationsList outerList = new AnnotationsList(count);
+
+ for (int i = 0; i < count; i++) {
+ if (observer != null) {
+ parsed(0, "parameter_annotations[" + i + "]:");
+ changeIndent(1);
+ }
+
+ Annotations annotations = parseAnnotations(visibility);
+ outerList.set(i, annotations);
+
+ if (observer != null) {
+ observer.changeIndent(-1);
+ }
+ }
+
+ outerList.setImmutable();
+ return outerList;
+ }
+
+ /**
+ * Parses an annotation list.
+ *
+ * @param visibility {@code non-null;} visibility of the parsed annotations
+ * @return {@code non-null;} the list of annotations read from the attribute
+ * data
+ */
+ private Annotations parseAnnotations(AnnotationVisibility visibility)
+ throws IOException {
+ int count = input.readUnsignedShort();
+
+ if (observer != null) {
+ parsed(2, "num_annotations: " + Hex.u2(count));
+ }
+
+ Annotations annotations = new Annotations();
+
+ for (int i = 0; i < count; i++) {
+ if (observer != null) {
+ parsed(0, "annotations[" + i + "]:");
+ changeIndent(1);
+ }
+
+ Annotation annotation = parseAnnotation(visibility);
+ annotations.add(annotation);
+
+ if (observer != null) {
+ observer.changeIndent(-1);
+ }
+ }
+
+ annotations.setImmutable();
+ return annotations;
+ }
+
+ /**
+ * Parses a single annotation.
+ *
+ * @param visibility {@code non-null;} visibility of the parsed annotation
+ * @return {@code non-null;} the parsed annotation
+ */
+ private Annotation parseAnnotation(AnnotationVisibility visibility)
+ throws IOException {
+ requireLength(4);
+
+ int typeIndex = input.readUnsignedShort();
+ int numElements = input.readUnsignedShort();
+ CstUtf8 typeUtf8 = (CstUtf8) pool.get(typeIndex);
+ CstType type = new CstType(Type.intern(typeUtf8.getString()));
+
+ if (observer != null) {
+ parsed(2, "type: " + type.toHuman());
+ parsed(2, "num_elements: " + numElements);
+ }
+
+ Annotation annotation = new Annotation(type, visibility);
+
+ for (int i = 0; i < numElements; i++) {
+ if (observer != null) {
+ parsed(0, "elements[" + i + "]:");
+ changeIndent(1);
+ }
+
+ NameValuePair element = parseElement();
+ annotation.add(element);
+
+ if (observer != null) {
+ changeIndent(-1);
+ }
+ }
+
+ annotation.setImmutable();
+ return annotation;
+ }
+
+ /**
+ * Parses a {@link NameValuePair}.
+ *
+ * @return {@code non-null;} the parsed element
+ */
+ private NameValuePair parseElement() throws IOException {
+ requireLength(5);
+
+ int elementNameIndex = input.readUnsignedShort();
+ CstUtf8 elementName = (CstUtf8) pool.get(elementNameIndex);
+
+ if (observer != null) {
+ parsed(2, "element_name: " + elementName.toHuman());
+ parsed(0, "value: ");
+ changeIndent(1);
+ }
+
+ Constant value = parseValue();
+
+ if (observer != null) {
+ changeIndent(-1);
+ }
+
+ return new NameValuePair(elementName, value);
+ }
+
+ /**
+ * Parses an annotation value.
+ *
+ * @return {@code non-null;} the parsed value
+ */
+ private Constant parseValue() throws IOException {
+ int tag = input.readUnsignedByte();
+
+ if (observer != null) {
+ CstUtf8 humanTag = new CstUtf8(Character.toString((char) tag));
+ parsed(1, "tag: " + humanTag.toQuoted());
+ }
+
+ switch (tag) {
+ case 'B': {
+ CstInteger value = (CstInteger) parseConstant();
+ return CstByte.make(value.getValue());
+ }
+ case 'C': {
+ CstInteger value = (CstInteger) parseConstant();
+ int intValue = value.getValue();
+ return CstChar.make(value.getValue());
+ }
+ case 'D': {
+ CstDouble value = (CstDouble) parseConstant();
+ return value;
+ }
+ case 'F': {
+ CstFloat value = (CstFloat) parseConstant();
+ return value;
+ }
+ case 'I': {
+ CstInteger value = (CstInteger) parseConstant();
+ return value;
+ }
+ case 'J': {
+ CstLong value = (CstLong) parseConstant();
+ return value;
+ }
+ case 'S': {
+ CstInteger value = (CstInteger) parseConstant();
+ return CstShort.make(value.getValue());
+ }
+ case 'Z': {
+ CstInteger value = (CstInteger) parseConstant();
+ return CstBoolean.make(value.getValue());
+ }
+ case 'c': {
+ int classInfoIndex = input.readUnsignedShort();
+ CstUtf8 value = (CstUtf8) pool.get(classInfoIndex);
+ Type type = Type.internReturnType(value.getString());
+
+ if (observer != null) {
+ parsed(2, "class_info: " + type.toHuman());
+ }
+
+ return new CstType(type);
+ }
+ case 's': {
+ CstString value = new CstString((CstUtf8) parseConstant());
+ return value;
+ }
+ case 'e': {
+ requireLength(4);
+
+ int typeNameIndex = input.readUnsignedShort();
+ int constNameIndex = input.readUnsignedShort();
+ CstUtf8 typeName = (CstUtf8) pool.get(typeNameIndex);
+ CstUtf8 constName = (CstUtf8) pool.get(constNameIndex);
+
+ if (observer != null) {
+ parsed(2, "type_name: " + typeName.toHuman());
+ parsed(2, "const_name: " + constName.toHuman());
+ }
+
+ return new CstEnumRef(new CstNat(constName, typeName));
+ }
+ case '@': {
+ Annotation annotation =
+ parseAnnotation(AnnotationVisibility.EMBEDDED);
+ return new CstAnnotation(annotation);
+ }
+ case '[': {
+ requireLength(2);
+
+ int numValues = input.readUnsignedShort();
+ CstArray.List list = new CstArray.List(numValues);
+
+ if (observer != null) {
+ parsed(2, "num_values: " + numValues);
+ changeIndent(1);
+ }
+
+ for (int i = 0; i < numValues; i++) {
+ if (observer != null) {
+ changeIndent(-1);
+ parsed(0, "element_value[" + i + "]:");
+ changeIndent(1);
+ }
+ list.set(i, parseValue());
+ }
+
+ if (observer != null) {
+ changeIndent(-1);
+ }
+
+ list.setImmutable();
+ return new CstArray(list);
+ }
+ default: {
+ throw new ParseException("unknown annotation tag: " +
+ Hex.u1(tag));
+ }
+ }
+ }
+
+ /**
+ * Helper for {@link #parseValue}, which parses a constant reference
+ * and returns the referred-to constant value.
+ *
+ * @return {@code non-null;} the parsed value
+ */
+ private Constant parseConstant() throws IOException {
+ int constValueIndex = input.readUnsignedShort();
+ Constant value = (Constant) pool.get(constValueIndex);
+
+ if (observer != null) {
+ String human = (value instanceof CstUtf8)
+ ? ((CstUtf8) value).toQuoted()
+ : value.toHuman();
+ parsed(2, "constant_value: " + human);
+ }
+
+ return value;
+ }
+
+ /**
+ * Helper which will throw an exception if the given number of bytes
+ * is not available to be read.
+ *
+ * @param requiredLength the number of required bytes
+ */
+ private void requireLength(int requiredLength) throws IOException {
+ if (input.available() < requiredLength) {
+ throw new ParseException("truncated annotation attribute");
+ }
+ }
+
+ /**
+ * Helper which indicates that some bytes were just parsed. This should
+ * only be used (for efficiency sake) if the parse is known to be
+ * observed.
+ *
+ * @param length {@code >= 0;} number of bytes parsed
+ * @param message {@code non-null;} associated message
+ */
+ private void parsed(int length, String message) {
+ observer.parsed(bytes, parseCursor, length, message);
+ parseCursor += length;
+ }
+
+ /**
+ * Convenience wrapper that simply calls through to
+ * {@code observer.changeIndent()}.
+ *
+ * @param indent the amount to change the indent by
+ */
+ private void changeIndent(int indent) {
+ observer.changeIndent(indent);
+ }
+}
diff --git a/dx/src/com/android/dx/cf/direct/AttributeFactory.java b/dx/src/com/android/dx/cf/direct/AttributeFactory.java
new file mode 100644
index 0000000..f98372c
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/AttributeFactory.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.direct;
+
+import com.android.dx.cf.attrib.RawAttribute;
+import com.android.dx.cf.iface.Attribute;
+import com.android.dx.cf.iface.ParseException;
+import com.android.dx.cf.iface.ParseObserver;
+import com.android.dx.rop.cst.ConstantPool;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+
+/**
+ * Factory capable of instantiating various {@link Attribute} subclasses
+ * depending on the context and name.
+ */
+public class AttributeFactory {
+ /** context for attributes on class files */
+ public static final int CTX_CLASS = 0;
+
+ /** context for attributes on fields */
+ public static final int CTX_FIELD = 1;
+
+ /** context for attributes on methods */
+ public static final int CTX_METHOD = 2;
+
+ /** context for attributes on code attributes */
+ public static final int CTX_CODE = 3;
+
+ /** number of contexts */
+ public static final int CTX_COUNT = 4;
+
+ /**
+ * Constructs an instance.
+ */
+ public AttributeFactory() {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Parses and makes an attribute based on the bytes at the
+ * indicated position in the given array. This method figures out
+ * the name, and then does all the setup to call on to {@link #parse0},
+ * which does the actual construction.
+ *
+ * @param cf {@code non-null;} class file to parse from
+ * @param context context to parse in; one of the {@code CTX_*}
+ * constants
+ * @param offset offset into {@code dcf}'s {@code bytes}
+ * to start parsing at
+ * @param observer {@code null-ok;} parse observer to report to, if any
+ * @return {@code non-null;} an appropriately-constructed {@link Attribute}
+ */
+ public final Attribute parse(DirectClassFile cf, int context, int offset,
+ ParseObserver observer) {
+ if (cf == null) {
+ throw new NullPointerException("cf == null");
+ }
+
+ if ((context < 0) || (context >= CTX_COUNT)) {
+ throw new IllegalArgumentException("bad context");
+ }
+
+ CstUtf8 name = null;
+
+ try {
+ ByteArray bytes = cf.getBytes();
+ ConstantPool pool = cf.getConstantPool();
+ int nameIdx = bytes.getUnsignedShort(offset);
+ int length = bytes.getInt(offset + 2);
+
+ name = (CstUtf8) pool.get(nameIdx);
+
+ if (observer != null) {
+ observer.parsed(bytes, offset, 2,
+ "name: " + name.toHuman());
+ observer.parsed(bytes, offset + 2, 4,
+ "length: " + Hex.u4(length));
+ }
+
+ return parse0(cf, context, name.getString(), offset + 6, length,
+ observer);
+ } catch (ParseException ex) {
+ ex.addContext("...while parsing " +
+ ((name != null) ? (name.toHuman() + " ") : "") +
+ "attribute at offset " + Hex.u4(offset));
+ throw ex;
+ }
+ }
+
+ /**
+ * Parses attribute content. The base class implements this by constructing
+ * an instance of {@link RawAttribute}. Subclasses are expected to
+ * override this to do something better in most cases.
+ *
+ * @param cf {@code non-null;} class file to parse from
+ * @param context context to parse in; one of the {@code CTX_*}
+ * constants
+ * @param name {@code non-null;} the attribute name
+ * @param offset offset into {@code bytes} to start parsing at; this
+ * is the offset to the start of attribute data, not to the header
+ * @param length the length of the attribute data
+ * @param observer {@code null-ok;} parse observer to report to, if any
+ * @return {@code non-null;} an appropriately-constructed {@link Attribute}
+ */
+ protected Attribute parse0(DirectClassFile cf, int context, String name,
+ int offset, int length,
+ ParseObserver observer) {
+ ByteArray bytes = cf.getBytes();
+ ConstantPool pool = cf.getConstantPool();
+ Attribute result = new RawAttribute(name, bytes, offset, length, pool);
+
+ if (observer != null) {
+ observer.parsed(bytes, offset, length, "attribute data");
+ }
+
+ return result;
+ }
+}
diff --git a/dx/src/com/android/dx/cf/direct/AttributeListParser.java b/dx/src/com/android/dx/cf/direct/AttributeListParser.java
new file mode 100644
index 0000000..2715e6a
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/AttributeListParser.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.direct;
+
+import com.android.dx.cf.iface.Attribute;
+import com.android.dx.cf.iface.ParseException;
+import com.android.dx.cf.iface.ParseObserver;
+import com.android.dx.cf.iface.StdAttributeList;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+
+/**
+ * Parser for lists of attributes.
+ */
+final /*package*/ class AttributeListParser {
+ /** {@code non-null;} the class file to parse from */
+ private final DirectClassFile cf;
+
+ /** attribute parsing context */
+ private final int context;
+
+ /** offset in the byte array of the classfile to the start of the list */
+ private final int offset;
+
+ /** {@code non-null;} attribute factory to use */
+ private final AttributeFactory attributeFactory;
+
+ /** {@code non-null;} list of parsed attributes */
+ private final StdAttributeList list;
+
+ /** {@code >= -1;} the end offset of this list in the byte array of the
+ * classfile, or {@code -1} if not yet parsed */
+ private int endOffset;
+
+ /** {@code null-ok;} parse observer, if any */
+ private ParseObserver observer;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param cf {@code non-null;} class file to parse from
+ * @param context attribute parsing context (see {@link AttributeFactory})
+ * @param offset offset in {@code bytes} to the start of the list
+ * @param attributeFactory {@code non-null;} attribute factory to use
+ */
+ public AttributeListParser(DirectClassFile cf, int context, int offset,
+ AttributeFactory attributeFactory) {
+ if (cf == null) {
+ throw new NullPointerException("cf == null");
+ }
+
+ if (attributeFactory == null) {
+ throw new NullPointerException("attributeFactory == null");
+ }
+
+ int size = cf.getBytes().getUnsignedShort(offset);
+
+ this.cf = cf;
+ this.context = context;
+ this.offset = offset;
+ this.attributeFactory = attributeFactory;
+ this.list = new StdAttributeList(size);
+ this.endOffset = -1;
+ }
+
+ /**
+ * Sets the parse observer for this instance.
+ *
+ * @param observer {@code null-ok;} the observer
+ */
+ public void setObserver(ParseObserver observer) {
+ this.observer = observer;
+ }
+
+ /**
+ * Gets the end offset of this constant pool in the {@code byte[]}
+ * which it came from.
+ *
+ * @return {@code >= 0;} the end offset
+ */
+ public int getEndOffset() {
+ parseIfNecessary();
+ return endOffset;
+ }
+
+ /**
+ * Gets the parsed list.
+ *
+ * @return {@code non-null;} the list
+ */
+ public StdAttributeList getList() {
+ parseIfNecessary();
+ return list;
+ }
+
+ /**
+ * Runs {@link #parse} if it has not yet been run successfully.
+ */
+ private void parseIfNecessary() {
+ if (endOffset < 0) {
+ parse();
+ }
+ }
+
+ /**
+ * Does the actual parsing.
+ */
+ private void parse() {
+ int sz = list.size();
+ int at = offset + 2; // Skip the count.
+
+ ByteArray bytes = cf.getBytes();
+
+ if (observer != null) {
+ observer.parsed(bytes, offset, 2,
+ "attributes_count: " + Hex.u2(sz));
+ }
+
+ for (int i = 0; i < sz; i++) {
+ try {
+ if (observer != null) {
+ observer.parsed(bytes, at, 0,
+ "\nattributes[" + i + "]:\n");
+ observer.changeIndent(1);
+ }
+
+ Attribute attrib =
+ attributeFactory.parse(cf, context, at, observer);
+
+ at += attrib.byteLength();
+ list.set(i, attrib);
+
+ if (observer != null) {
+ observer.changeIndent(-1);
+ observer.parsed(bytes, at, 0,
+ "end attributes[" + i + "]\n");
+ }
+ } catch (ParseException ex) {
+ ex.addContext("...while parsing attributes[" + i + "]");
+ throw ex;
+ } catch (RuntimeException ex) {
+ ParseException pe = new ParseException(ex);
+ pe.addContext("...while parsing attributes[" + i + "]");
+ throw pe;
+ }
+ }
+
+ endOffset = at;
+ }
+}
diff --git a/dx/src/com/android/dx/cf/direct/ClassPathOpener.java b/dx/src/com/android/dx/cf/direct/ClassPathOpener.java
new file mode 100644
index 0000000..4e8c435
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/ClassPathOpener.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.direct;
+
+import com.android.dx.util.FileUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipEntry;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * Opens all the class files found in a class path element. Path elements
+ * can point to class files, {jar,zip,apk} files, or directories containing
+ * class files.
+ */
+public class ClassPathOpener {
+
+ /** {@code non-null;} pathname to start with */
+ private final String pathname;
+ /** {@code non-null;} callback interface */
+ private final Consumer consumer;
+ /**
+ * If true, sort such that classes appear before their inner
+ * classes and "package-info" occurs before all other classes in that
+ * package.
+ */
+ private final boolean sort;
+
+ /**
+ * Callback interface for {@code ClassOpener}.
+ */
+ public interface Consumer {
+
+ /**
+ * Provides the file name and byte array for a class path element.
+ *
+ * @param name {@code non-null;} filename of element. May not be a valid
+ * filesystem path.
+ *
+ * @param bytes {@code non-null;} file data
+ * @return true on success. Result is or'd with all other results
+ * from {@code processFileBytes} and returned to the caller
+ * of {@code process()}.
+ */
+ boolean processFileBytes(String name, byte[] bytes);
+
+ /**
+ * Informs consumer that an exception occurred while processing
+ * this path element. Processing will continue if possible.
+ *
+ * @param ex {@code non-null;} exception
+ */
+ void onException(Exception ex);
+
+ /**
+ * Informs consumer that processing of an archive file has begun.
+ *
+ * @param file {@code non-null;} archive file being processed
+ */
+ void onProcessArchiveStart(File file);
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param pathname {@code non-null;} path element to process
+ * @param sort if true, sort such that classes appear before their inner
+ * classes and "package-info" occurs before all other classes in that
+ * package.
+ * @param consumer {@code non-null;} callback interface
+ */
+ public ClassPathOpener(String pathname, boolean sort, Consumer consumer) {
+ this.pathname = pathname;
+ this.sort = sort;
+ this.consumer = consumer;
+ }
+
+ /**
+ * Processes a path element.
+ *
+ * @return the OR of all return values
+ * from {@code Consumer.processFileBytes()}.
+ */
+ public boolean process() {
+ File file = new File(pathname);
+
+ return processOne(file, true);
+ }
+
+ /**
+ * Processes one file.
+ *
+ * @param file {@code non-null;} the file to process
+ * @param topLevel whether this is a top-level file (that is,
+ * specified directly on the commandline)
+ * @return whether any processing actually happened
+ */
+ private boolean processOne(File file, boolean topLevel) {
+ try {
+ if (file.isDirectory()) {
+ return processDirectory(file, topLevel);
+ }
+
+ String path = file.getPath();
+
+ if (path.endsWith(".zip") ||
+ path.endsWith(".jar") ||
+ path.endsWith(".apk")) {
+ return processArchive(file);
+ }
+
+ byte[] bytes = FileUtils.readFile(file);
+ return consumer.processFileBytes(path, bytes);
+ } catch (Exception ex) {
+ consumer.onException(ex);
+ return false;
+ }
+ }
+
+ /**
+ * Sorts java class names such that outer classes preceed their inner
+ * classes and "package-info" preceeds all other classes in its package.
+ *
+ * @param a {@code non-null;} first class name
+ * @param b {@code non-null;} second class name
+ * @return {@code compareTo()}-style result
+ */
+ private static int compareClassNames(String a, String b) {
+ // Ensure inner classes sort second
+ a = a.replace('$','0');
+ b = b.replace('$','0');
+
+ /*
+ * Assuming "package-info" only occurs at the end, ensures package-info
+ * sorts first.
+ */
+ a = a.replace("package-info", "");
+ b = b.replace("package-info", "");
+
+ return a.compareTo(b);
+ }
+
+ /**
+ * Processes a directory recursively.
+ *
+ * @param dir {@code non-null;} file representing the directory
+ * @param topLevel whether this is a top-level directory (that is,
+ * specified directly on the commandline)
+ * @return whether any processing actually happened
+ */
+ private boolean processDirectory(File dir, boolean topLevel) {
+ if (topLevel) {
+ dir = new File(dir, ".");
+ }
+
+ File[] files = dir.listFiles();
+ int len = files.length;
+ boolean any = false;
+
+ if (sort) {
+ Arrays.sort(files, new Comparator<File>() {
+ public int compare(File a, File b) {
+ return compareClassNames(a.getName(), b.getName());
+ }
+ });
+ }
+
+ for (int i = 0; i < len; i++) {
+ any |= processOne(files[i], false);
+ }
+
+ return any;
+ }
+
+ /**
+ * Processes the contents of an archive ({@code .zip},
+ * {@code .jar}, or {@code .apk}).
+ *
+ * @param file {@code non-null;} archive file to process
+ * @return whether any processing actually happened
+ * @throws IOException on i/o problem
+ */
+ private boolean processArchive(File file) throws IOException {
+ ZipFile zip = new ZipFile(file);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(40000);
+ byte[] buf = new byte[20000];
+ boolean any = false;
+
+ ArrayList<? extends java.util.zip.ZipEntry> entriesList
+ = Collections.list(zip.entries());
+
+ if (sort) {
+ Collections.sort(entriesList, new Comparator<ZipEntry>() {
+ public int compare (ZipEntry a, ZipEntry b) {
+ return compareClassNames(a.getName(), b.getName());
+ }
+ });
+ }
+
+ consumer.onProcessArchiveStart(file);
+
+ for (ZipEntry one : entriesList) {
+ if (one.isDirectory()) {
+ continue;
+ }
+
+ String path = one.getName();
+ InputStream in = zip.getInputStream(one);
+
+ baos.reset();
+ for (;;) {
+ int amt = in.read(buf);
+ if (amt < 0) {
+ break;
+ }
+
+ baos.write(buf, 0, amt);
+ }
+
+ in.close();
+
+ byte[] bytes = baos.toByteArray();
+ any |= consumer.processFileBytes(path, bytes);
+ }
+
+ zip.close();
+ return any;
+ }
+}
diff --git a/dx/src/com/android/dx/cf/direct/CodeObserver.java b/dx/src/com/android/dx/cf/direct/CodeObserver.java
new file mode 100644
index 0000000..efcc80b
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/CodeObserver.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.direct;
+
+import com.android.dx.cf.code.ByteOps;
+import com.android.dx.cf.code.BytecodeArray;
+import com.android.dx.cf.code.SwitchList;
+import com.android.dx.cf.iface.ParseObserver;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstDouble;
+import com.android.dx.rop.cst.CstFloat;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.CstKnownNull;
+import com.android.dx.rop.cst.CstLong;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IntList;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Bytecode visitor to use when "observing" bytecode getting parsed.
+ */
+public class CodeObserver implements BytecodeArray.Visitor {
+ /** {@code non-null;} actual array of bytecode */
+ private final ByteArray bytes;
+
+ /** {@code non-null;} observer to inform of parsing */
+ private final ParseObserver observer;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param bytes {@code non-null;} actual array of bytecode
+ * @param observer {@code non-null;} observer to inform of parsing
+ */
+ public CodeObserver(ByteArray bytes, ParseObserver observer) {
+ if (bytes == null) {
+ throw new NullPointerException("bytes == null");
+ }
+
+ if (observer == null) {
+ throw new NullPointerException("observer == null");
+ }
+
+ this.bytes = bytes;
+ this.observer = observer;
+ }
+
+ /** {@inheritDoc} */
+ public void visitInvalid(int opcode, int offset, int length) {
+ observer.parsed(bytes, offset, length, header(offset));
+ }
+
+ /** {@inheritDoc} */
+ public void visitNoArgs(int opcode, int offset, int length, Type type) {
+ observer.parsed(bytes, offset, length, header(offset));
+ }
+
+ /** {@inheritDoc} */
+ public void visitLocal(int opcode, int offset, int length,
+ int idx, Type type, int value) {
+ String idxStr = (length <= 3) ? Hex.u1(idx) : Hex.u2(idx);
+ boolean argComment = (length == 1);
+ String valueStr = "";
+
+ if (opcode == ByteOps.IINC) {
+ valueStr = ", #" +
+ ((length <= 3) ? Hex.s1(value) : Hex.s2(value));
+ }
+
+ String catStr = "";
+ if (type.isCategory2()) {
+ catStr = (argComment ? "," : " //") + " category-2";
+ }
+
+ observer.parsed(bytes, offset, length,
+ header(offset) + (argComment ? " // " : " ") +
+ idxStr + valueStr + catStr);
+ }
+
+ /** {@inheritDoc} */
+ public void visitConstant(int opcode, int offset, int length,
+ Constant cst, int value) {
+ if (cst instanceof CstKnownNull) {
+ // This is aconst_null.
+ visitNoArgs(opcode, offset, length, null);
+ return;
+ }
+
+ if (cst instanceof CstInteger) {
+ visitLiteralInt(opcode, offset, length, value);
+ return;
+ }
+
+ if (cst instanceof CstLong) {
+ visitLiteralLong(opcode, offset, length,
+ ((CstLong) cst).getValue());
+ return;
+ }
+
+ if (cst instanceof CstFloat) {
+ visitLiteralFloat(opcode, offset, length,
+ ((CstFloat) cst).getIntBits());
+ return;
+ }
+
+ if (cst instanceof CstDouble) {
+ visitLiteralDouble(opcode, offset, length,
+ ((CstDouble) cst).getLongBits());
+ return;
+ }
+
+ String valueStr = "";
+ if (value != 0) {
+ valueStr = ", ";
+ if (opcode == ByteOps.MULTIANEWARRAY) {
+ valueStr += Hex.u1(value);
+ } else {
+ valueStr += Hex.u2(value);
+ }
+ }
+
+ observer.parsed(bytes, offset, length,
+ header(offset) + " " + cst + valueStr);
+ }
+
+ /** {@inheritDoc} */
+ public void visitBranch(int opcode, int offset, int length,
+ int target) {
+ String targetStr = (length <= 3) ? Hex.u2(target) : Hex.u4(target);
+ observer.parsed(bytes, offset, length,
+ header(offset) + " " + targetStr);
+ }
+
+ /** {@inheritDoc} */
+ public void visitSwitch(int opcode, int offset, int length,
+ SwitchList cases, int padding) {
+ int sz = cases.size();
+ StringBuffer sb = new StringBuffer(sz * 20 + 100);
+
+ sb.append(header(offset));
+ if (padding != 0) {
+ sb.append(" // padding: " + Hex.u4(padding));
+ }
+ sb.append('\n');
+
+ for (int i = 0; i < sz; i++) {
+ sb.append(" ");
+ sb.append(Hex.s4(cases.getValue(i)));
+ sb.append(": ");
+ sb.append(Hex.u2(cases.getTarget(i)));
+ sb.append('\n');
+ }
+
+ sb.append(" default: ");
+ sb.append(Hex.u2(cases.getDefaultTarget()));
+
+ observer.parsed(bytes, offset, length, sb.toString());
+ }
+
+ /** {@inheritDoc} */
+ public void visitNewarray(int offset, int length, CstType cst,
+ ArrayList<Constant> intVals) {
+ String commentOrSpace = (length == 1) ? " // " : " ";
+ String typeName = cst.getClassType().getComponentType().toHuman();
+
+ observer.parsed(bytes, offset, length,
+ header(offset) + commentOrSpace + typeName);
+ }
+
+ /** {@inheritDoc} */
+ public void setPreviousOffset(int offset) {
+ // Do nothing
+ }
+
+ /** {@inheritDoc} */
+ public int getPreviousOffset() {
+ return -1;
+ }
+
+ /**
+ * Helper to produce the first bit of output for each instruction.
+ *
+ * @param offset the offset to the start of the instruction
+ */
+ private String header(int offset) {
+ /*
+ * Note: This uses the original bytecode, not the
+ * possibly-transformed one.
+ */
+ int opcode = bytes.getUnsignedByte(offset);
+ String name = ByteOps.opName(opcode);
+
+ if (opcode == ByteOps.WIDE) {
+ opcode = bytes.getUnsignedByte(offset + 1);
+ name += " " + ByteOps.opName(opcode);
+ }
+
+ return Hex.u2(offset) + ": " + name;
+ }
+
+ /**
+ * Helper for {@link #visitConstant} where the constant is an
+ * {@code int}.
+ *
+ * @param opcode the opcode
+ * @param offset offset to the instruction
+ * @param length instruction length
+ * @param value constant value
+ */
+ private void visitLiteralInt(int opcode, int offset, int length,
+ int value) {
+ String commentOrSpace = (length == 1) ? " // " : " ";
+ String valueStr;
+
+ opcode = bytes.getUnsignedByte(offset); // Compare with orig op below.
+ if ((length == 1) || (opcode == ByteOps.BIPUSH)) {
+ valueStr = "#" + Hex.s1(value);
+ } else if (opcode == ByteOps.SIPUSH) {
+ valueStr = "#" + Hex.s2(value);
+ } else {
+ valueStr = "#" + Hex.s4(value);
+ }
+
+ observer.parsed(bytes, offset, length,
+ header(offset) + commentOrSpace + valueStr);
+ }
+
+ /**
+ * Helper for {@link #visitConstant} where the constant is a
+ * {@code long}.
+ *
+ * @param opcode the opcode
+ * @param offset offset to the instruction
+ * @param length instruction length
+ * @param value constant value
+ */
+ private void visitLiteralLong(int opcode, int offset, int length,
+ long value) {
+ String commentOrLit = (length == 1) ? " // " : " #";
+ String valueStr;
+
+ if (length == 1) {
+ valueStr = Hex.s1((int) value);
+ } else {
+ valueStr = Hex.s8(value);
+ }
+
+ observer.parsed(bytes, offset, length,
+ header(offset) + commentOrLit + valueStr);
+ }
+
+ /**
+ * Helper for {@link #visitConstant} where the constant is a
+ * {@code float}.
+ *
+ * @param opcode the opcode
+ * @param offset offset to the instruction
+ * @param length instruction length
+ * @param bits constant value, as float-bits
+ */
+ private void visitLiteralFloat(int opcode, int offset, int length,
+ int bits) {
+ String optArg = (length != 1) ? " #" + Hex.u4(bits) : "";
+
+ observer.parsed(bytes, offset, length,
+ header(offset) + optArg + " // " +
+ Float.intBitsToFloat(bits));
+ }
+
+ /**
+ * Helper for {@link #visitConstant} where the constant is a
+ * {@code double}.
+ *
+ * @param opcode the opcode
+ * @param offset offset to the instruction
+ * @param length instruction length
+ * @param bits constant value, as double-bits
+ */
+ private void visitLiteralDouble(int opcode, int offset, int length,
+ long bits) {
+ String optArg = (length != 1) ? " #" + Hex.u8(bits) : "";
+
+ observer.parsed(bytes, offset, length,
+ header(offset) + optArg + " // " +
+ Double.longBitsToDouble(bits));
+ }
+}
diff --git a/dx/src/com/android/dx/cf/direct/DirectClassFile.java b/dx/src/com/android/dx/cf/direct/DirectClassFile.java
new file mode 100644
index 0000000..b194572
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/DirectClassFile.java
@@ -0,0 +1,633 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.direct;
+
+import com.android.dx.cf.attrib.AttSourceFile;
+import com.android.dx.cf.cst.ConstantPoolParser;
+import com.android.dx.cf.iface.Attribute;
+import com.android.dx.cf.iface.AttributeList;
+import com.android.dx.cf.iface.ClassFile;
+import com.android.dx.cf.iface.FieldList;
+import com.android.dx.cf.iface.MethodList;
+import com.android.dx.cf.iface.ParseException;
+import com.android.dx.cf.iface.ParseObserver;
+import com.android.dx.cf.iface.StdAttributeList;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.rop.cst.ConstantPool;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.cst.StdConstantPool;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+
+/**
+ * Class file with info taken from a {@code byte[]} or slice thereof.
+ */
+public class DirectClassFile implements ClassFile {
+ /** the expected value of the ClassFile.magic field */
+ private static final int CLASS_FILE_MAGIC = 0xcafebabe;
+
+ /**
+ * minimum {@code .class} file major version
+ *
+ * The class file definition (vmspec/2nd-edition) says:
+ *
+ * "Implementations of version 1.2 of the
+ * Java 2 platform can support class file
+ * formats of versions in the range 45.0
+ * through 46.0 inclusive."
+ *
+ * The class files generated by the build are currently
+ * (as of 11/2006) reporting version 49.0 (0x31.0x00),
+ * however, so we use that as our upper bound.
+ *
+ * Valid ranges are typically of the form
+ * "A.0 through B.C inclusive" where A <= B and C >= 0,
+ * which is why we don't have a CLASS_FILE_MIN_MINOR_VERSION.
+ */
+ private static final int CLASS_FILE_MIN_MAJOR_VERSION = 45;
+
+ /** maximum {@code .class} file major version */
+ private static final int CLASS_FILE_MAX_MAJOR_VERSION = 50;
+
+ /** maximum {@code .class} file minor version */
+ private static final int CLASS_FILE_MAX_MINOR_VERSION = 0;
+
+ /**
+ * {@code non-null;} the file path for the class, excluding any base directory
+ * specification
+ */
+ private final String filePath;
+
+ /** {@code non-null;} the bytes of the file */
+ private final ByteArray bytes;
+
+ /**
+ * whether to be strict about parsing; if
+ * {@code false}, this avoids doing checks that only exist
+ * for purposes of verification (such as magic number matching and
+ * path-package consistency checking)
+ */
+ private final boolean strictParse;
+
+ /**
+ * {@code null-ok;} the constant pool; only ever {@code null}
+ * before the constant pool is successfully parsed
+ */
+ private StdConstantPool pool;
+
+ /**
+ * the class file field {@code access_flags}; will be {@code -1}
+ * before the file is successfully parsed
+ */
+ private int accessFlags;
+
+ /**
+ * {@code null-ok;} the class file field {@code this_class},
+ * interpreted as a type constant; only ever {@code null}
+ * before the file is successfully parsed
+ */
+ private CstType thisClass;
+
+ /**
+ * {@code null-ok;} the class file field {@code super_class}, interpreted
+ * as a type constant if non-zero
+ */
+ private CstType superClass;
+
+ /**
+ * {@code null-ok;} the class file field {@code interfaces}; only
+ * ever {@code null} before the file is successfully
+ * parsed
+ */
+ private TypeList interfaces;
+
+ /**
+ * {@code null-ok;} the class file field {@code fields}; only ever
+ * {@code null} before the file is successfully parsed
+ */
+ private FieldList fields;
+
+ /**
+ * {@code null-ok;} the class file field {@code methods}; only ever
+ * {@code null} before the file is successfully parsed
+ */
+ private MethodList methods;
+
+ /**
+ * {@code null-ok;} the class file field {@code attributes}; only
+ * ever {@code null} before the file is successfully
+ * parsed
+ */
+ private StdAttributeList attributes;
+
+ /** {@code null-ok;} attribute factory, if any */
+ private AttributeFactory attributeFactory;
+
+ /** {@code null-ok;} parse observer, if any */
+ private ParseObserver observer;
+
+ /**
+ * Returns the string form of an object or {@code "(none)"}
+ * (rather than {@code "null"}) for {@code null}.
+ *
+ * @param obj {@code null-ok;} the object to stringify
+ * @return {@code non-null;} the appropriate string form
+ */
+ public static String stringOrNone(Object obj) {
+ if (obj == null) {
+ return "(none)";
+ }
+
+ return obj.toString();
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param bytes {@code non-null;} the bytes of the file
+ * @param filePath {@code non-null;} the file path for the class,
+ * excluding any base directory specification
+ * @param strictParse whether to be strict about parsing; if
+ * {@code false}, this avoids doing checks that only exist
+ * for purposes of verification (such as magic number matching and
+ * path-package consistency checking)
+ */
+ public DirectClassFile(ByteArray bytes, String filePath,
+ boolean strictParse) {
+ if (bytes == null) {
+ throw new NullPointerException("bytes == null");
+ }
+
+ if (filePath == null) {
+ throw new NullPointerException("filePath == null");
+ }
+
+ this.filePath = filePath;
+ this.bytes = bytes;
+ this.strictParse = strictParse;
+ this.accessFlags = -1;
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param bytes {@code non-null;} the bytes of the file
+ * @param filePath {@code non-null;} the file path for the class,
+ * excluding any base directory specification
+ * @param strictParse whether to be strict about parsing; if
+ * {@code false}, this avoids doing checks that only exist
+ * for purposes of verification (such as magic number matching and
+ * path-package consistency checking)
+ */
+ public DirectClassFile(byte[] bytes, String filePath,
+ boolean strictParse) {
+ this(new ByteArray(bytes), filePath, strictParse);
+ }
+
+ /**
+ * Sets the parse observer for this instance.
+ *
+ * @param observer {@code null-ok;} the observer
+ */
+ public void setObserver(ParseObserver observer) {
+ this.observer = observer;
+ }
+
+ /**
+ * Sets the attribute factory to use.
+ *
+ * @param attributeFactory {@code non-null;} the attribute factory
+ */
+ public void setAttributeFactory(AttributeFactory attributeFactory) {
+ if (attributeFactory == null) {
+ throw new NullPointerException("attributeFactory == null");
+ }
+
+ this.attributeFactory = attributeFactory;
+ }
+
+ /**
+ * Gets the {@link ByteArray} that this instance's data comes from.
+ *
+ * @return {@code non-null;} the bytes
+ */
+ public ByteArray getBytes() {
+ return bytes;
+ }
+
+ /** {@inheritDoc} */
+ public int getMagic() {
+ parseToInterfacesIfNecessary();
+ return getMagic0();
+ }
+
+ /** {@inheritDoc} */
+ public int getMinorVersion() {
+ parseToInterfacesIfNecessary();
+ return getMinorVersion0();
+ }
+
+ /** {@inheritDoc} */
+ public int getMajorVersion() {
+ parseToInterfacesIfNecessary();
+ return getMajorVersion0();
+ }
+
+ /** {@inheritDoc} */
+ public int getAccessFlags() {
+ parseToInterfacesIfNecessary();
+ return accessFlags;
+ }
+
+ /** {@inheritDoc} */
+ public CstType getThisClass() {
+ parseToInterfacesIfNecessary();
+ return thisClass;
+ }
+
+ /** {@inheritDoc} */
+ public CstType getSuperclass() {
+ parseToInterfacesIfNecessary();
+ return superClass;
+ }
+
+ /** {@inheritDoc} */
+ public ConstantPool getConstantPool() {
+ parseToInterfacesIfNecessary();
+ return pool;
+ }
+
+ /** {@inheritDoc} */
+ public TypeList getInterfaces() {
+ parseToInterfacesIfNecessary();
+ return interfaces;
+ }
+
+ /** {@inheritDoc} */
+ public FieldList getFields() {
+ parseToEndIfNecessary();
+ return fields;
+ }
+
+ /** {@inheritDoc} */
+ public MethodList getMethods() {
+ parseToEndIfNecessary();
+ return methods;
+ }
+
+ /** {@inheritDoc} */
+ public AttributeList getAttributes() {
+ parseToEndIfNecessary();
+ return attributes;
+ }
+
+ /** {@inheritDoc} */
+ public CstUtf8 getSourceFile() {
+ AttributeList attribs = getAttributes();
+ Attribute attSf = attribs.findFirst(AttSourceFile.ATTRIBUTE_NAME);
+
+ if (attSf instanceof AttSourceFile) {
+ return ((AttSourceFile) attSf).getSourceFile();
+ }
+
+ return null;
+ }
+
+ /**
+ * Constructs and returns an instance of {@link TypeList} whose
+ * data comes from the bytes of this instance, interpreted as a
+ * list of constant pool indices for classes, which are in turn
+ * translated to type constants. Instance construction will fail
+ * if any of the (alleged) indices turn out not to refer to
+ * constant pool entries of type {@code Class}.
+ *
+ * @param offset offset into {@link #bytes} for the start of the
+ * data
+ * @param size number of elements in the list (not number of bytes)
+ * @return {@code non-null;} an appropriately-constructed class list
+ */
+ public TypeList makeTypeList(int offset, int size) {
+ if (size == 0) {
+ return StdTypeList.EMPTY;
+ }
+
+ if (pool == null) {
+ throw new IllegalStateException("pool not yet initialized");
+ }
+
+ return new DcfTypeList(bytes, offset, size, pool, observer);
+ }
+
+ /**
+ * Gets the class file field {@code magic}, but without doing any
+ * checks or parsing first.
+ *
+ * @return the magic value
+ */
+ public int getMagic0() {
+ return bytes.getInt(0);
+ }
+
+ /**
+ * Gets the class file field {@code minor_version}, but
+ * without doing any checks or parsing first.
+ *
+ * @return the minor version
+ */
+ public int getMinorVersion0() {
+ return bytes.getUnsignedShort(4);
+ }
+
+ /**
+ * Gets the class file field {@code major_version}, but
+ * without doing any checks or parsing first.
+ *
+ * @return the major version
+ */
+ public int getMajorVersion0() {
+ return bytes.getUnsignedShort(6);
+ }
+
+ /**
+ * Runs {@link #parse} if it has not yet been run to cover up to
+ * the interfaces list.
+ */
+ private void parseToInterfacesIfNecessary() {
+ if (accessFlags == -1) {
+ parse();
+ }
+ }
+
+ /**
+ * Runs {@link #parse} if it has not yet been run successfully.
+ */
+ private void parseToEndIfNecessary() {
+ if (attributes == null) {
+ parse();
+ }
+ }
+
+ /**
+ * Does the parsing, handing exceptions.
+ */
+ private void parse() {
+ try {
+ parse0();
+ } catch (ParseException ex) {
+ ex.addContext("...while parsing " + filePath);
+ throw ex;
+ } catch (RuntimeException ex) {
+ ParseException pe = new ParseException(ex);
+ pe.addContext("...while parsing " + filePath);
+ throw pe;
+ }
+ }
+
+ /**
+ * Sees if the .class file header magic/version are within
+ * range.
+ *
+ * @param magic the value of a classfile "magic" field
+ * @param minorVersion the value of a classfile "minor_version" field
+ * @param majorVersion the value of a classfile "major_version" field
+ * @return true iff the parameters are valid and within range
+ */
+ private boolean isGoodVersion(int magic, int minorVersion,
+ int majorVersion) {
+ /* Valid version ranges are typically of the form
+ * "A.0 through B.C inclusive" where A <= B and C >= 0,
+ * which is why we don't have a CLASS_FILE_MIN_MINOR_VERSION.
+ */
+ if (magic == CLASS_FILE_MAGIC && minorVersion >= 0) {
+ /* Check against max first to handle the case where
+ * MIN_MAJOR == MAX_MAJOR.
+ */
+ if (majorVersion == CLASS_FILE_MAX_MAJOR_VERSION) {
+ if (minorVersion <= CLASS_FILE_MAX_MINOR_VERSION) {
+ return true;
+ }
+ } else if (majorVersion < CLASS_FILE_MAX_MAJOR_VERSION &&
+ majorVersion >= CLASS_FILE_MIN_MAJOR_VERSION) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Does the actual parsing.
+ */
+ private void parse0() {
+ if (bytes.size() < 10) {
+ throw new ParseException("severely truncated class file");
+ }
+
+ if (observer != null) {
+ observer.parsed(bytes, 0, 0, "begin classfile");
+ observer.parsed(bytes, 0, 4, "magic: " + Hex.u4(getMagic0()));
+ observer.parsed(bytes, 4, 2,
+ "minor_version: " + Hex.u2(getMinorVersion0()));
+ observer.parsed(bytes, 6, 2,
+ "major_version: " + Hex.u2(getMajorVersion0()));
+ }
+
+ if (strictParse) {
+ /* Make sure that this looks like a valid class file with a
+ * version that we can handle.
+ */
+ if (!isGoodVersion(getMagic0(), getMinorVersion0(),
+ getMajorVersion0())) {
+ throw new ParseException("bad class file magic (" +
+ Hex.u4(getMagic0()) +
+ ") or version (" +
+ Hex.u2(getMajorVersion0()) + "." +
+ Hex.u2(getMinorVersion0()) + ")");
+ }
+ }
+
+ ConstantPoolParser cpParser = new ConstantPoolParser(bytes);
+ cpParser.setObserver(observer);
+ pool = cpParser.getPool();
+ pool.setImmutable();
+
+ int at = cpParser.getEndOffset();
+ int accessFlags = bytes.getUnsignedShort(at); // u2 access_flags;
+ int cpi = bytes.getUnsignedShort(at + 2); // u2 this_class;
+ thisClass = (CstType) pool.get(cpi);
+ cpi = bytes.getUnsignedShort(at + 4); // u2 super_class;
+ superClass = (CstType) pool.get0Ok(cpi);
+ int count = bytes.getUnsignedShort(at + 6); // u2 interfaces_count
+
+ if (observer != null) {
+ observer.parsed(bytes, at, 2,
+ "access_flags: " +
+ AccessFlags.classString(accessFlags));
+ observer.parsed(bytes, at + 2, 2, "this_class: " + thisClass);
+ observer.parsed(bytes, at + 4, 2, "super_class: " +
+ stringOrNone(superClass));
+ observer.parsed(bytes, at + 6, 2,
+ "interfaces_count: " + Hex.u2(count));
+ if (count != 0) {
+ observer.parsed(bytes, at + 8, 0, "interfaces:");
+ }
+ }
+
+ at += 8;
+ interfaces = makeTypeList(at, count);
+ at += count * 2;
+
+ if (strictParse) {
+ /*
+ * Make sure that the file/jar path matches the declared
+ * package/class name.
+ */
+ String thisClassName = thisClass.getClassType().getClassName();
+ if (!(filePath.endsWith(".class") &&
+ filePath.startsWith(thisClassName) &&
+ (filePath.length() == (thisClassName.length() + 6)))) {
+ throw new ParseException("class name (" + thisClassName +
+ ") does not match path (" +
+ filePath + ")");
+ }
+ }
+
+ /*
+ * Only set the instance variable accessFlags here, since
+ * that's what signals a successful parse of the first part of
+ * the file (through the interfaces list).
+ */
+ this.accessFlags = accessFlags;
+
+ FieldListParser flParser =
+ new FieldListParser(this, thisClass, at, attributeFactory);
+ flParser.setObserver(observer);
+ fields = flParser.getList();
+ at = flParser.getEndOffset();
+
+ MethodListParser mlParser =
+ new MethodListParser(this, thisClass, at, attributeFactory);
+ mlParser.setObserver(observer);
+ methods = mlParser.getList();
+ at = mlParser.getEndOffset();
+
+ AttributeListParser alParser =
+ new AttributeListParser(this, AttributeFactory.CTX_CLASS, at,
+ attributeFactory);
+ alParser.setObserver(observer);
+ attributes = alParser.getList();
+ attributes.setImmutable();
+ at = alParser.getEndOffset();
+
+ if (at != bytes.size()) {
+ throw new ParseException("extra bytes at end of class file, " +
+ "at offset " + Hex.u4(at));
+ }
+
+ if (observer != null) {
+ observer.parsed(bytes, at, 0, "end classfile");
+ }
+ }
+
+ /**
+ * Implementation of {@link TypeList} whose data comes directly
+ * from the bytes of an instance of this (outer) class,
+ * interpreted as a list of constant pool indices for classes
+ * which are in turn returned as type constants. Instance
+ * construction will fail if any of the (alleged) indices turn out
+ * not to refer to constant pool entries of type
+ * {@code Class}.
+ */
+ private static class DcfTypeList implements TypeList {
+ /** {@code non-null;} array containing the data */
+ private final ByteArray bytes;
+
+ /** number of elements in the list (not number of bytes) */
+ private final int size;
+
+ /** {@code non-null;} the constant pool */
+ private final StdConstantPool pool;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param bytes {@code non-null;} original classfile's bytes
+ * @param offset offset into {@link #bytes} for the start of the
+ * data
+ * @param size number of elements in the list (not number of bytes)
+ * @param pool {@code non-null;} the constant pool to use
+ * @param observer {@code null-ok;} parse observer to use, if any
+ */
+ public DcfTypeList(ByteArray bytes, int offset, int size,
+ StdConstantPool pool, ParseObserver observer) {
+ if (size < 0) {
+ throw new IllegalArgumentException("size < 0");
+ }
+
+ bytes = bytes.slice(offset, offset + size * 2);
+ this.bytes = bytes;
+ this.size = size;
+ this.pool = pool;
+
+ for (int i = 0; i < size; i++) {
+ offset = i * 2;
+ int idx = bytes.getUnsignedShort(offset);
+ CstType type;
+ try {
+ type = (CstType) pool.get(idx);
+ } catch (ClassCastException ex) {
+ // Translate the exception.
+ throw new RuntimeException("bogus class cpi", ex);
+ }
+ if (observer != null) {
+ observer.parsed(bytes, offset, 2, " " + type);
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ public boolean isMutable() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public int size() {
+ return size;
+ }
+
+ /** {@inheritDoc} */
+ public int getWordCount() {
+ // It is the same as size because all elements are classes.
+ return size;
+ }
+
+ /** {@inheritDoc} */
+ public Type getType(int n) {
+ int idx = bytes.getUnsignedShort(n * 2);
+ return ((CstType) pool.get(idx)).getClassType();
+ }
+
+ /** {@inheritDoc} */
+ public TypeList withAddedType(Type type) {
+ throw new UnsupportedOperationException("unsupported");
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/cf/direct/FieldListParser.java b/dx/src/com/android/dx/cf/direct/FieldListParser.java
new file mode 100644
index 0000000..2d8280d
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/FieldListParser.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.direct;
+
+import com.android.dx.cf.iface.AttributeList;
+import com.android.dx.cf.iface.Member;
+import com.android.dx.cf.iface.StdField;
+import com.android.dx.cf.iface.StdFieldList;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+
+/**
+ * Parser for lists of fields in a class file.
+ */
+final /*package*/ class FieldListParser extends MemberListParser {
+ /** {@code non-null;} list in progress */
+ private final StdFieldList fields;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param cf {@code non-null;} the class file to parse from
+ * @param definer {@code non-null;} class being defined
+ * @param offset offset in {@code bytes} to the start of the list
+ * @param attributeFactory {@code non-null;} attribute factory to use
+ */
+ public FieldListParser(DirectClassFile cf, CstType definer, int offset,
+ AttributeFactory attributeFactory) {
+ super(cf, definer, offset, attributeFactory);
+ fields = new StdFieldList(getCount());
+ }
+
+ /**
+ * Gets the parsed list.
+ *
+ * @return {@code non-null;} the parsed list
+ */
+ public StdFieldList getList() {
+ parseIfNecessary();
+ return fields;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected String humanName() {
+ return "field";
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected String humanAccessFlags(int accessFlags) {
+ return AccessFlags.fieldString(accessFlags);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected int getAttributeContext() {
+ return AttributeFactory.CTX_FIELD;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected Member set(int n, int accessFlags, CstNat nat,
+ AttributeList attributes) {
+ StdField field =
+ new StdField(getDefiner(), accessFlags, nat, attributes);
+
+ fields.set(n, field);
+ return field;
+ }
+}
diff --git a/dx/src/com/android/dx/cf/direct/MemberListParser.java b/dx/src/com/android/dx/cf/direct/MemberListParser.java
new file mode 100644
index 0000000..91a5e1d
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/MemberListParser.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.direct;
+
+import com.android.dx.cf.iface.AttributeList;
+import com.android.dx.cf.iface.Member;
+import com.android.dx.cf.iface.ParseException;
+import com.android.dx.cf.iface.ParseObserver;
+import com.android.dx.cf.iface.StdAttributeList;
+import com.android.dx.rop.cst.ConstantPool;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+
+/**
+ * Parser for lists of class file members (that is, fields and methods).
+ */
+abstract /*package*/ class MemberListParser {
+ /** {@code non-null;} the class file to parse from */
+ private final DirectClassFile cf;
+
+ /** {@code non-null;} class being defined */
+ private final CstType definer;
+
+ /** offset in the byte array of the classfile to the start of the list */
+ private final int offset;
+
+ /** {@code non-null;} attribute factory to use */
+ private final AttributeFactory attributeFactory;
+
+ /** {@code >= -1;} the end offset of this list in the byte array of the
+ * classfile, or {@code -1} if not yet parsed */
+ private int endOffset;
+
+ /** {@code null-ok;} parse observer, if any */
+ private ParseObserver observer;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param cf {@code non-null;} the class file to parse from
+ * @param definer {@code non-null;} class being defined
+ * @param offset offset in {@code bytes} to the start of the list
+ * @param attributeFactory {@code non-null;} attribute factory to use
+ */
+ public MemberListParser(DirectClassFile cf, CstType definer,
+ int offset, AttributeFactory attributeFactory) {
+ if (cf == null) {
+ throw new NullPointerException("cf == null");
+ }
+
+ if (offset < 0) {
+ throw new IllegalArgumentException("offset < 0");
+ }
+
+ if (attributeFactory == null) {
+ throw new NullPointerException("attributeFactory == null");
+ }
+
+ this.cf = cf;
+ this.definer = definer;
+ this.offset = offset;
+ this.attributeFactory = attributeFactory;
+ this.endOffset = -1;
+ }
+
+ /**
+ * Gets the end offset of this constant pool in the {@code byte[]}
+ * which it came from.
+ *
+ * @return {@code >= 0;} the end offset
+ */
+ public int getEndOffset() {
+ parseIfNecessary();
+ return endOffset;
+ }
+
+ /**
+ * Sets the parse observer for this instance.
+ *
+ * @param observer {@code null-ok;} the observer
+ */
+ public final void setObserver(ParseObserver observer) {
+ this.observer = observer;
+ }
+
+ /**
+ * Runs {@link #parse} if it has not yet been run successfully.
+ */
+ protected final void parseIfNecessary() {
+ if (endOffset < 0) {
+ parse();
+ }
+ }
+
+ /**
+ * Gets the count of elements in the list.
+ *
+ * @return the count
+ */
+ protected final int getCount() {
+ ByteArray bytes = cf.getBytes();
+ return bytes.getUnsignedShort(offset);
+ }
+
+ /**
+ * Gets the class file being defined.
+ *
+ * @return {@code non-null;} the class
+ */
+ protected final CstType getDefiner() {
+ return definer;
+ }
+
+ /**
+ * Gets the human-oriented name for what this instance is parsing.
+ * Subclasses must override this method.
+ *
+ * @return {@code non-null;} the human oriented name
+ */
+ protected abstract String humanName();
+
+ /**
+ * Gets the human-oriented string for the given access flags.
+ * Subclasses must override this method.
+ *
+ * @param accessFlags the flags
+ * @return {@code non-null;} the string form
+ */
+ protected abstract String humanAccessFlags(int accessFlags);
+
+ /**
+ * Gets the {@code CTX_*} constant to use when parsing attributes.
+ * Subclasses must override this method.
+ *
+ * @return {@code non-null;} the human oriented name
+ */
+ protected abstract int getAttributeContext();
+
+ /**
+ * Sets an element in the list. Subclasses must override this method.
+ *
+ * @param n which element
+ * @param accessFlags the {@code access_flags}
+ * @param nat the interpreted name and type (based on the two
+ * {@code *_index} fields)
+ * @param attributes list of parsed attributes
+ * @return {@code non-null;} the constructed member
+ */
+ protected abstract Member set(int n, int accessFlags, CstNat nat,
+ AttributeList attributes);
+
+ /**
+ * Does the actual parsing.
+ */
+ private void parse() {
+ int attributeContext = getAttributeContext();
+ int count = getCount();
+ int at = offset + 2; // Skip the count.
+
+ ByteArray bytes = cf.getBytes();
+ ConstantPool pool = cf.getConstantPool();
+
+ if (observer != null) {
+ observer.parsed(bytes, offset, 2,
+ humanName() + "s_count: " + Hex.u2(count));
+ }
+
+ for (int i = 0; i < count; i++) {
+ try {
+ int accessFlags = bytes.getUnsignedShort(at);
+ int nameIdx = bytes.getUnsignedShort(at + 2);
+ int descIdx = bytes.getUnsignedShort(at + 4);
+ CstUtf8 name = (CstUtf8) pool.get(nameIdx);
+ CstUtf8 desc = (CstUtf8) pool.get(descIdx);
+
+ if (observer != null) {
+ observer.startParsingMember(bytes, at, name.getString(),
+ desc.getString());
+ observer.parsed(bytes, at, 0, "\n" + humanName() +
+ "s[" + i + "]:\n");
+ observer.changeIndent(1);
+ observer.parsed(bytes, at, 2,
+ "access_flags: " +
+ humanAccessFlags(accessFlags));
+ observer.parsed(bytes, at + 2, 2,
+ "name: " + name.toHuman());
+ observer.parsed(bytes, at + 4, 2,
+ "descriptor: " + desc.toHuman());
+ }
+
+ at += 6;
+ AttributeListParser parser =
+ new AttributeListParser(cf, attributeContext, at,
+ attributeFactory);
+ parser.setObserver(observer);
+ at = parser.getEndOffset();
+ StdAttributeList attributes = parser.getList();
+ attributes.setImmutable();
+ CstNat nat = new CstNat(name, desc);
+ Member member = set(i, accessFlags, nat, attributes);
+
+ if (observer != null) {
+ observer.changeIndent(-1);
+ observer.parsed(bytes, at, 0, "end " + humanName() +
+ "s[" + i + "]\n");
+ observer.endParsingMember(bytes, at, name.getString(),
+ desc.getString(), member);
+ }
+ } catch (ParseException ex) {
+ ex.addContext("...while parsing " + humanName() + "s[" + i +
+ "]");
+ throw ex;
+ } catch (RuntimeException ex) {
+ ParseException pe = new ParseException(ex);
+ pe.addContext("...while parsing " + humanName() + "s[" + i +
+ "]");
+ throw pe;
+ }
+ }
+
+ endOffset = at;
+ }
+}
diff --git a/dx/src/com/android/dx/cf/direct/MethodListParser.java b/dx/src/com/android/dx/cf/direct/MethodListParser.java
new file mode 100644
index 0000000..9e3494e
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/MethodListParser.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.direct;
+
+import com.android.dx.cf.iface.AttributeList;
+import com.android.dx.cf.iface.Member;
+import com.android.dx.cf.iface.StdMethod;
+import com.android.dx.cf.iface.StdMethodList;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+
+/**
+ * Parser for lists of methods in a class file.
+ */
+final /*package*/ class MethodListParser extends MemberListParser {
+ /** {@code non-null;} list in progress */
+ final private StdMethodList methods;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param cf {@code non-null;} the class file to parse from
+ * @param definer {@code non-null;} class being defined
+ * @param offset offset in {@code bytes} to the start of the list
+ * @param attributeFactory {@code non-null;} attribute factory to use
+ */
+ public MethodListParser(DirectClassFile cf, CstType definer,
+ int offset, AttributeFactory attributeFactory) {
+ super(cf, definer, offset, attributeFactory);
+ methods = new StdMethodList(getCount());
+ }
+
+ /**
+ * Gets the parsed list.
+ *
+ * @return {@code non-null;} the parsed list
+ */
+ public StdMethodList getList() {
+ parseIfNecessary();
+ return methods;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected String humanName() {
+ return "method";
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected String humanAccessFlags(int accessFlags) {
+ return AccessFlags.methodString(accessFlags);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected int getAttributeContext() {
+ return AttributeFactory.CTX_METHOD;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected Member set(int n, int accessFlags, CstNat nat,
+ AttributeList attributes) {
+ StdMethod meth =
+ new StdMethod(getDefiner(), accessFlags, nat, attributes);
+
+ methods.set(n, meth);
+ return meth;
+ }
+}
diff --git a/dx/src/com/android/dx/cf/direct/StdAttributeFactory.java b/dx/src/com/android/dx/cf/direct/StdAttributeFactory.java
new file mode 100644
index 0000000..a6a97af
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/StdAttributeFactory.java
@@ -0,0 +1,764 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.direct;
+
+import com.android.dx.cf.attrib.AttAnnotationDefault;
+import com.android.dx.cf.attrib.AttCode;
+import com.android.dx.cf.attrib.AttConstantValue;
+import com.android.dx.cf.attrib.AttDeprecated;
+import com.android.dx.cf.attrib.AttEnclosingMethod;
+import com.android.dx.cf.attrib.AttExceptions;
+import com.android.dx.cf.attrib.AttInnerClasses;
+import com.android.dx.cf.attrib.AttLineNumberTable;
+import com.android.dx.cf.attrib.AttLocalVariableTable;
+import com.android.dx.cf.attrib.AttLocalVariableTypeTable;
+import com.android.dx.cf.attrib.AttRuntimeInvisibleAnnotations;
+import com.android.dx.cf.attrib.AttRuntimeInvisibleParameterAnnotations;
+import com.android.dx.cf.attrib.AttRuntimeVisibleAnnotations;
+import com.android.dx.cf.attrib.AttRuntimeVisibleParameterAnnotations;
+import com.android.dx.cf.attrib.AttSignature;
+import com.android.dx.cf.attrib.AttSourceFile;
+import com.android.dx.cf.attrib.AttSynthetic;
+import com.android.dx.cf.attrib.InnerClassList;
+import com.android.dx.cf.code.ByteCatchList;
+import com.android.dx.cf.code.BytecodeArray;
+import com.android.dx.cf.code.LineNumberList;
+import com.android.dx.cf.code.LocalVariableList;
+import com.android.dx.cf.iface.Attribute;
+import com.android.dx.cf.iface.ParseException;
+import com.android.dx.cf.iface.ParseObserver;
+import com.android.dx.cf.iface.StdAttributeList;
+import com.android.dx.rop.annotation.Annotation;
+import com.android.dx.rop.annotation.AnnotationVisibility;
+import com.android.dx.rop.annotation.Annotations;
+import com.android.dx.rop.annotation.AnnotationsList;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.ConstantPool;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.cst.TypedConstant;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+
+import java.io.IOException;
+
+/**
+ * Standard subclass of {@link AttributeFactory}, which knows how to parse
+ * all the standard attribute types.
+ */
+public class StdAttributeFactory
+ extends AttributeFactory {
+ /** {@code non-null;} shared instance of this class */
+ public static final StdAttributeFactory THE_ONE =
+ new StdAttributeFactory();
+
+ /**
+ * Constructs an instance.
+ */
+ public StdAttributeFactory() {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected Attribute parse0(DirectClassFile cf, int context, String name,
+ int offset, int length, ParseObserver observer) {
+ switch (context) {
+ case CTX_CLASS: {
+ if (name == AttDeprecated.ATTRIBUTE_NAME) {
+ return deprecated(cf, offset, length, observer);
+ }
+ if (name == AttEnclosingMethod.ATTRIBUTE_NAME) {
+ return enclosingMethod(cf, offset, length, observer);
+ }
+ if (name == AttInnerClasses.ATTRIBUTE_NAME) {
+ return innerClasses(cf, offset, length, observer);
+ }
+ if (name == AttRuntimeInvisibleAnnotations.ATTRIBUTE_NAME) {
+ return runtimeInvisibleAnnotations(cf, offset, length,
+ observer);
+ }
+ if (name == AttRuntimeVisibleAnnotations.ATTRIBUTE_NAME) {
+ return runtimeVisibleAnnotations(cf, offset, length,
+ observer);
+ }
+ if (name == AttSynthetic.ATTRIBUTE_NAME) {
+ return synthetic(cf, offset, length, observer);
+ }
+ if (name == AttSignature.ATTRIBUTE_NAME) {
+ return signature(cf, offset, length, observer);
+ }
+ if (name == AttSourceFile.ATTRIBUTE_NAME) {
+ return sourceFile(cf, offset, length, observer);
+ }
+ break;
+ }
+ case CTX_FIELD: {
+ if (name == AttConstantValue.ATTRIBUTE_NAME) {
+ return constantValue(cf, offset, length, observer);
+ }
+ if (name == AttDeprecated.ATTRIBUTE_NAME) {
+ return deprecated(cf, offset, length, observer);
+ }
+ if (name == AttRuntimeInvisibleAnnotations.ATTRIBUTE_NAME) {
+ return runtimeInvisibleAnnotations(cf, offset, length,
+ observer);
+ }
+ if (name == AttRuntimeVisibleAnnotations.ATTRIBUTE_NAME) {
+ return runtimeVisibleAnnotations(cf, offset, length,
+ observer);
+ }
+ if (name == AttSignature.ATTRIBUTE_NAME) {
+ return signature(cf, offset, length, observer);
+ }
+ if (name == AttSynthetic.ATTRIBUTE_NAME) {
+ return synthetic(cf, offset, length, observer);
+ }
+ break;
+ }
+ case CTX_METHOD: {
+ if (name == AttAnnotationDefault.ATTRIBUTE_NAME) {
+ return annotationDefault(cf, offset, length, observer);
+ }
+ if (name == AttCode.ATTRIBUTE_NAME) {
+ return code(cf, offset, length, observer);
+ }
+ if (name == AttDeprecated.ATTRIBUTE_NAME) {
+ return deprecated(cf, offset, length, observer);
+ }
+ if (name == AttExceptions.ATTRIBUTE_NAME) {
+ return exceptions(cf, offset, length, observer);
+ }
+ if (name == AttRuntimeInvisibleAnnotations.ATTRIBUTE_NAME) {
+ return runtimeInvisibleAnnotations(cf, offset, length,
+ observer);
+ }
+ if (name == AttRuntimeVisibleAnnotations.ATTRIBUTE_NAME) {
+ return runtimeVisibleAnnotations(cf, offset, length,
+ observer);
+ }
+ if (name == AttRuntimeInvisibleParameterAnnotations.
+ ATTRIBUTE_NAME) {
+ return runtimeInvisibleParameterAnnotations(
+ cf, offset, length, observer);
+ }
+ if (name == AttRuntimeVisibleParameterAnnotations.
+ ATTRIBUTE_NAME) {
+ return runtimeVisibleParameterAnnotations(
+ cf, offset, length, observer);
+ }
+ if (name == AttSignature.ATTRIBUTE_NAME) {
+ return signature(cf, offset, length, observer);
+ }
+ if (name == AttSynthetic.ATTRIBUTE_NAME) {
+ return synthetic(cf, offset, length, observer);
+ }
+ break;
+ }
+ case CTX_CODE: {
+ if (name == AttLineNumberTable.ATTRIBUTE_NAME) {
+ return lineNumberTable(cf, offset, length, observer);
+ }
+ if (name == AttLocalVariableTable.ATTRIBUTE_NAME) {
+ return localVariableTable(cf, offset, length, observer);
+ }
+ if (name == AttLocalVariableTypeTable.ATTRIBUTE_NAME) {
+ return localVariableTypeTable(cf, offset, length,
+ observer);
+ }
+ break;
+ }
+ }
+
+ return super.parse0(cf, context, name, offset, length, observer);
+ }
+
+ /**
+ * Parses an {@code AnnotationDefault} attribute.
+ */
+ private Attribute annotationDefault(DirectClassFile cf,
+ int offset, int length, ParseObserver observer) {
+ if (length < 2) {
+ throwSeverelyTruncated();
+ }
+
+ AnnotationParser ap =
+ new AnnotationParser(cf, offset, length, observer);
+ Constant cst = ap.parseValueAttribute();
+
+ return new AttAnnotationDefault(cst, length);
+ }
+
+ /**
+ * Parses a {@code Code} attribute.
+ */
+ private Attribute code(DirectClassFile cf, int offset, int length,
+ ParseObserver observer) {
+ if (length < 12) {
+ return throwSeverelyTruncated();
+ }
+
+ ByteArray bytes = cf.getBytes();
+ ConstantPool pool = cf.getConstantPool();
+ int maxStack = bytes.getUnsignedShort(offset); // u2 max_stack
+ int maxLocals = bytes.getUnsignedShort(offset + 2); // u2 max_locals
+ int codeLength = bytes.getInt(offset + 4); // u4 code_length
+ int origOffset = offset;
+
+ if (observer != null) {
+ observer.parsed(bytes, offset, 2,
+ "max_stack: " + Hex.u2(maxStack));
+ observer.parsed(bytes, offset + 2, 2,
+ "max_locals: " + Hex.u2(maxLocals));
+ observer.parsed(bytes, offset + 4, 4,
+ "code_length: " + Hex.u4(codeLength));
+ }
+
+ offset += 8;
+ length -= 8;
+
+ if (length < (codeLength + 4)) {
+ return throwTruncated();
+ }
+
+ int codeOffset = offset;
+ offset += codeLength;
+ length -= codeLength;
+ BytecodeArray code =
+ new BytecodeArray(bytes.slice(codeOffset, codeOffset + codeLength),
+ pool);
+ if (observer != null) {
+ code.forEach(new CodeObserver(code.getBytes(), observer));
+ }
+
+ // u2 exception_table_length
+ int exceptionTableLength = bytes.getUnsignedShort(offset);
+ ByteCatchList catches = (exceptionTableLength == 0) ?
+ ByteCatchList.EMPTY :
+ new ByteCatchList(exceptionTableLength);
+
+ if (observer != null) {
+ observer.parsed(bytes, offset, 2,
+ "exception_table_length: " +
+ Hex.u2(exceptionTableLength));
+ }
+
+ offset += 2;
+ length -= 2;
+
+ if (length < (exceptionTableLength * 8 + 2)) {
+ return throwTruncated();
+ }
+
+ for (int i = 0; i < exceptionTableLength; i++) {
+ if (observer != null) {
+ observer.changeIndent(1);
+ }
+
+ int startPc = bytes.getUnsignedShort(offset);
+ int endPc = bytes.getUnsignedShort(offset + 2);
+ int handlerPc = bytes.getUnsignedShort(offset + 4);
+ int catchTypeIdx = bytes.getUnsignedShort(offset + 6);
+ CstType catchType = (CstType) pool.get0Ok(catchTypeIdx);
+ catches.set(i, startPc, endPc, handlerPc, catchType);
+ if (observer != null) {
+ observer.parsed(bytes, offset, 8,
+ Hex.u2(startPc) + ".." + Hex.u2(endPc) +
+ " -> " + Hex.u2(handlerPc) + " " +
+ ((catchType == null) ? "<any>" :
+ catchType.toHuman()));
+ }
+ offset += 8;
+ length -= 8;
+
+ if (observer != null) {
+ observer.changeIndent(-1);
+ }
+ }
+
+ catches.setImmutable();
+
+ AttributeListParser parser =
+ new AttributeListParser(cf, CTX_CODE, offset, this);
+ parser.setObserver(observer);
+
+ StdAttributeList attributes = parser.getList();
+ attributes.setImmutable();
+
+ int attributeByteCount = parser.getEndOffset() - offset;
+ if (attributeByteCount != length) {
+ return throwBadLength(attributeByteCount + (offset - origOffset));
+ }
+
+ return new AttCode(maxStack, maxLocals, code, catches, attributes);
+ }
+
+ /**
+ * Parses a {@code ConstantValue} attribute.
+ */
+ private Attribute constantValue(DirectClassFile cf, int offset, int length,
+ ParseObserver observer) {
+ if (length != 2) {
+ return throwBadLength(2);
+ }
+
+ ByteArray bytes = cf.getBytes();
+ ConstantPool pool = cf.getConstantPool();
+ int idx = bytes.getUnsignedShort(offset);
+ TypedConstant cst = (TypedConstant) pool.get(idx);
+ Attribute result = new AttConstantValue(cst);
+
+ if (observer != null) {
+ observer.parsed(bytes, offset, 2, "value: " + cst);
+ }
+
+ return result;
+ }
+
+ /**
+ * Parses a {@code Deprecated} attribute.
+ */
+ private Attribute deprecated(DirectClassFile cf, int offset, int length,
+ ParseObserver observer) {
+ if (length != 0) {
+ return throwBadLength(0);
+ }
+
+ return new AttDeprecated();
+ }
+
+ /**
+ * Parses an {@code EnclosingMethod} attribute.
+ */
+ private Attribute enclosingMethod(DirectClassFile cf, int offset,
+ int length, ParseObserver observer) {
+ if (length != 4) {
+ throwBadLength(4);
+ }
+
+ ByteArray bytes = cf.getBytes();
+ ConstantPool pool = cf.getConstantPool();
+
+ int idx = bytes.getUnsignedShort(offset);
+ CstType type = (CstType) pool.get(idx);
+
+ idx = bytes.getUnsignedShort(offset + 2);
+ CstNat method = (CstNat) pool.get0Ok(idx);
+
+ Attribute result = new AttEnclosingMethod(type, method);
+
+ if (observer != null) {
+ observer.parsed(bytes, offset, 2, "class: " + type);
+ observer.parsed(bytes, offset + 2, 2, "method: " +
+ DirectClassFile.stringOrNone(method));
+ }
+
+ return result;
+ }
+
+ /**
+ * Parses an {@code Exceptions} attribute.
+ */
+ private Attribute exceptions(DirectClassFile cf, int offset, int length,
+ ParseObserver observer) {
+ if (length < 2) {
+ return throwSeverelyTruncated();
+ }
+
+ ByteArray bytes = cf.getBytes();
+ int count = bytes.getUnsignedShort(offset); // number_of_exceptions
+
+ if (observer != null) {
+ observer.parsed(bytes, offset, 2,
+ "number_of_exceptions: " + Hex.u2(count));
+ }
+
+ offset += 2;
+ length -= 2;
+
+ if (length != (count * 2)) {
+ throwBadLength((count * 2) + 2);
+ }
+
+ TypeList list = cf.makeTypeList(offset, count);
+ return new AttExceptions(list);
+ }
+
+ /**
+ * Parses an {@code InnerClasses} attribute.
+ */
+ private Attribute innerClasses(DirectClassFile cf, int offset, int length,
+ ParseObserver observer) {
+ if (length < 2) {
+ return throwSeverelyTruncated();
+ }
+
+ ByteArray bytes = cf.getBytes();
+ ConstantPool pool = cf.getConstantPool();
+ int count = bytes.getUnsignedShort(offset); // number_of_classes
+
+ if (observer != null) {
+ observer.parsed(bytes, offset, 2,
+ "number_of_classes: " + Hex.u2(count));
+ }
+
+ offset += 2;
+ length -= 2;
+
+ if (length != (count * 8)) {
+ throwBadLength((count * 8) + 2);
+ }
+
+ InnerClassList list = new InnerClassList(count);
+
+ for (int i = 0; i < count; i++) {
+ int innerClassIdx = bytes.getUnsignedShort(offset);
+ int outerClassIdx = bytes.getUnsignedShort(offset + 2);
+ int nameIdx = bytes.getUnsignedShort(offset + 4);
+ int accessFlags = bytes.getUnsignedShort(offset + 6);
+ CstType innerClass = (CstType) pool.get(innerClassIdx);
+ CstType outerClass = (CstType) pool.get0Ok(outerClassIdx);
+ CstUtf8 name = (CstUtf8) pool.get0Ok(nameIdx);
+ list.set(i, innerClass, outerClass, name, accessFlags);
+ if (observer != null) {
+ observer.parsed(bytes, offset, 2,
+ "inner_class: " +
+ DirectClassFile.stringOrNone(innerClass));
+ observer.parsed(bytes, offset + 2, 2,
+ " outer_class: " +
+ DirectClassFile.stringOrNone(outerClass));
+ observer.parsed(bytes, offset + 4, 2,
+ " name: " +
+ DirectClassFile.stringOrNone(name));
+ observer.parsed(bytes, offset + 6, 2,
+ " access_flags: " +
+ AccessFlags.innerClassString(accessFlags));
+ }
+ offset += 8;
+ }
+
+ list.setImmutable();
+ return new AttInnerClasses(list);
+ }
+
+ /**
+ * Parses a {@code LineNumberTable} attribute.
+ */
+ private Attribute lineNumberTable(DirectClassFile cf, int offset,
+ int length, ParseObserver observer) {
+ if (length < 2) {
+ return throwSeverelyTruncated();
+ }
+
+ ByteArray bytes = cf.getBytes();
+ int count = bytes.getUnsignedShort(offset); // line_number_table_length
+
+ if (observer != null) {
+ observer.parsed(bytes, offset, 2,
+ "line_number_table_length: " + Hex.u2(count));
+ }
+
+ offset += 2;
+ length -= 2;
+
+ if (length != (count * 4)) {
+ throwBadLength((count * 4) + 2);
+ }
+
+ LineNumberList list = new LineNumberList(count);
+
+ for (int i = 0; i < count; i++) {
+ int startPc = bytes.getUnsignedShort(offset);
+ int lineNumber = bytes.getUnsignedShort(offset + 2);
+ list.set(i, startPc, lineNumber);
+ if (observer != null) {
+ observer.parsed(bytes, offset, 4,
+ Hex.u2(startPc) + " " + lineNumber);
+ }
+ offset += 4;
+ }
+
+ list.setImmutable();
+ return new AttLineNumberTable(list);
+ }
+
+ /**
+ * Parses a {@code LocalVariableTable} attribute.
+ */
+ private Attribute localVariableTable(DirectClassFile cf, int offset,
+ int length, ParseObserver observer) {
+ if (length < 2) {
+ return throwSeverelyTruncated();
+ }
+
+ ByteArray bytes = cf.getBytes();
+ int count = bytes.getUnsignedShort(offset);
+
+ if (observer != null) {
+ observer.parsed(bytes, offset, 2,
+ "local_variable_table_length: " + Hex.u2(count));
+ }
+
+ LocalVariableList list = parseLocalVariables(
+ bytes.slice(offset + 2, offset + length), cf.getConstantPool(),
+ observer, count, false);
+ return new AttLocalVariableTable(list);
+ }
+
+ /**
+ * Parses a {@code LocalVariableTypeTable} attribute.
+ */
+ private Attribute localVariableTypeTable(DirectClassFile cf, int offset,
+ int length, ParseObserver observer) {
+ if (length < 2) {
+ return throwSeverelyTruncated();
+ }
+
+ ByteArray bytes = cf.getBytes();
+ int count = bytes.getUnsignedShort(offset);
+
+ if (observer != null) {
+ observer.parsed(bytes, offset, 2,
+ "local_variable_type_table_length: " + Hex.u2(count));
+ }
+
+ LocalVariableList list = parseLocalVariables(
+ bytes.slice(offset + 2, offset + length), cf.getConstantPool(),
+ observer, count, true);
+ return new AttLocalVariableTypeTable(list);
+ }
+
+ /**
+ * Parse the table part of either a {@code LocalVariableTable}
+ * or a {@code LocalVariableTypeTable}.
+ *
+ * @param bytes {@code non-null;} bytes to parse, which should <i>only</i>
+ * contain the table data (no header)
+ * @param pool {@code non-null;} constant pool to use
+ * @param count {@code >= 0;} the number of entries
+ * @param typeTable {@code true} iff this is for a type table
+ * @return {@code non-null;} the constructed list
+ */
+ private LocalVariableList parseLocalVariables(ByteArray bytes,
+ ConstantPool pool, ParseObserver observer, int count,
+ boolean typeTable) {
+ if (bytes.size() != (count * 10)) {
+ // "+ 2" is for the count.
+ throwBadLength((count * 10) + 2);
+ }
+
+ ByteArray.MyDataInputStream in = bytes.makeDataInputStream();
+ LocalVariableList list = new LocalVariableList(count);
+
+ try {
+ for (int i = 0; i < count; i++) {
+ int startPc = in.readUnsignedShort();
+ int length = in.readUnsignedShort();
+ int nameIdx = in.readUnsignedShort();
+ int typeIdx = in.readUnsignedShort();
+ int index = in.readUnsignedShort();
+ CstUtf8 name = (CstUtf8) pool.get(nameIdx);
+ CstUtf8 type = (CstUtf8) pool.get(typeIdx);
+ CstUtf8 descriptor = null;
+ CstUtf8 signature = null;
+
+ if (typeTable) {
+ signature = type;
+ } else {
+ descriptor = type;
+ }
+
+ list.set(i, startPc, length, name,
+ descriptor, signature, index);
+
+ if (observer != null) {
+ observer.parsed(bytes, i * 10, 10, Hex.u2(startPc) +
+ ".." + Hex.u2(startPc + length) + " " +
+ Hex.u2(index) + " " + name.toHuman() + " " +
+ type.toHuman());
+ }
+ }
+ } catch (IOException ex) {
+ throw new RuntimeException("shouldn't happen", ex);
+ }
+
+ list.setImmutable();
+ return list;
+ }
+
+ /**
+ * Parses a {@code RuntimeInvisibleAnnotations} attribute.
+ */
+ private Attribute runtimeInvisibleAnnotations(DirectClassFile cf,
+ int offset, int length, ParseObserver observer) {
+ if (length < 2) {
+ throwSeverelyTruncated();
+ }
+
+ AnnotationParser ap =
+ new AnnotationParser(cf, offset, length, observer);
+ Annotations annotations =
+ ap.parseAnnotationAttribute(AnnotationVisibility.BUILD);
+
+ return new AttRuntimeInvisibleAnnotations(annotations, length);
+ }
+
+ /**
+ * Parses a {@code RuntimeVisibleAnnotations} attribute.
+ */
+ private Attribute runtimeVisibleAnnotations(DirectClassFile cf,
+ int offset, int length, ParseObserver observer) {
+ if (length < 2) {
+ throwSeverelyTruncated();
+ }
+
+ AnnotationParser ap =
+ new AnnotationParser(cf, offset, length, observer);
+ Annotations annotations =
+ ap.parseAnnotationAttribute(AnnotationVisibility.RUNTIME);
+
+ return new AttRuntimeVisibleAnnotations(annotations, length);
+ }
+
+ /**
+ * Parses a {@code RuntimeInvisibleParameterAnnotations} attribute.
+ */
+ private Attribute runtimeInvisibleParameterAnnotations(DirectClassFile cf,
+ int offset, int length, ParseObserver observer) {
+ if (length < 2) {
+ throwSeverelyTruncated();
+ }
+
+ AnnotationParser ap =
+ new AnnotationParser(cf, offset, length, observer);
+ AnnotationsList list =
+ ap.parseParameterAttribute(AnnotationVisibility.BUILD);
+
+ return new AttRuntimeInvisibleParameterAnnotations(list, length);
+ }
+
+ /**
+ * Parses a {@code RuntimeVisibleParameterAnnotations} attribute.
+ */
+ private Attribute runtimeVisibleParameterAnnotations(DirectClassFile cf,
+ int offset, int length, ParseObserver observer) {
+ if (length < 2) {
+ throwSeverelyTruncated();
+ }
+
+ AnnotationParser ap =
+ new AnnotationParser(cf, offset, length, observer);
+ AnnotationsList list =
+ ap.parseParameterAttribute(AnnotationVisibility.RUNTIME);
+
+ return new AttRuntimeVisibleParameterAnnotations(list, length);
+ }
+
+ /**
+ * Parses a {@code Signature} attribute.
+ */
+ private Attribute signature(DirectClassFile cf, int offset, int length,
+ ParseObserver observer) {
+ if (length != 2) {
+ throwBadLength(2);
+ }
+
+ ByteArray bytes = cf.getBytes();
+ ConstantPool pool = cf.getConstantPool();
+ int idx = bytes.getUnsignedShort(offset);
+ CstUtf8 cst = (CstUtf8) pool.get(idx);
+ Attribute result = new AttSignature(cst);
+
+ if (observer != null) {
+ observer.parsed(bytes, offset, 2, "signature: " + cst);
+ }
+
+ return result;
+ }
+
+ /**
+ * Parses a {@code SourceFile} attribute.
+ */
+ private Attribute sourceFile(DirectClassFile cf, int offset, int length,
+ ParseObserver observer) {
+ if (length != 2) {
+ throwBadLength(2);
+ }
+
+ ByteArray bytes = cf.getBytes();
+ ConstantPool pool = cf.getConstantPool();
+ int idx = bytes.getUnsignedShort(offset);
+ CstUtf8 cst = (CstUtf8) pool.get(idx);
+ Attribute result = new AttSourceFile(cst);
+
+ if (observer != null) {
+ observer.parsed(bytes, offset, 2, "source: " + cst);
+ }
+
+ return result;
+ }
+
+ /**
+ * Parses a {@code Synthetic} attribute.
+ */
+ private Attribute synthetic(DirectClassFile cf, int offset, int length,
+ ParseObserver observer) {
+ if (length != 0) {
+ return throwBadLength(0);
+ }
+
+ return new AttSynthetic();
+ }
+
+ /**
+ * Throws the right exception when a known attribute has a way too short
+ * length.
+ *
+ * @return never
+ * @throws ParseException always thrown
+ */
+ private static Attribute throwSeverelyTruncated() {
+ throw new ParseException("severely truncated attribute");
+ }
+
+ /**
+ * Throws the right exception when a known attribute has a too short
+ * length.
+ *
+ * @return never
+ * @throws ParseException always thrown
+ */
+ private static Attribute throwTruncated() {
+ throw new ParseException("truncated attribute");
+ }
+
+ /**
+ * Throws the right exception when an attribute has an unexpected length
+ * (given its contents).
+ *
+ * @param expected expected length
+ * @return never
+ * @throws ParseException always thrown
+ */
+ private static Attribute throwBadLength(int expected) {
+ throw new ParseException("bad attribute length; expected length " +
+ Hex.u4(expected));
+ }
+}
diff --git a/dx/src/com/android/dx/cf/direct/package.html b/dx/src/com/android/dx/cf/direct/package.html
new file mode 100644
index 0000000..2a46198
--- /dev/null
+++ b/dx/src/com/android/dx/cf/direct/package.html
@@ -0,0 +1,12 @@
+<body>
+<p>Implementation of <code>cf.iface.*</code> based on a direct representation
+of class files as <code>byte[]</code>s.</p>
+
+<p><b>PACKAGES USED:</b>
+<ul>
+<li><code>com.android.dx.cf.attrib</code></li>
+<li><code>com.android.dx.cf.iface</code></li>
+<li><code>com.android.dx.rop.pool</code></li>
+<li><code>com.android.dx.util</code></li>
+</ul>
+</body>
diff --git a/dx/src/com/android/dx/cf/iface/Attribute.java b/dx/src/com/android/dx/cf/iface/Attribute.java
new file mode 100644
index 0000000..b075251
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/Attribute.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.iface;
+
+/**
+ * Interface representing attributes of class files (directly or indirectly).
+ */
+public interface Attribute {
+ /**
+ * Get the name of the attribute.
+ *
+ * @return {@code non-null;} the name
+ */
+ public String getName();
+
+ /**
+ * Get the total length of the attribute in bytes, including the
+ * header. Since the header is always six bytes, the result of
+ * this method is always at least {@code 6}.
+ *
+ * @return {@code >= 6;} the total length, in bytes
+ */
+ public int byteLength();
+}
diff --git a/dx/src/com/android/dx/cf/iface/AttributeList.java b/dx/src/com/android/dx/cf/iface/AttributeList.java
new file mode 100644
index 0000000..f7a1d27
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/AttributeList.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.iface;
+
+/**
+ * Interface for lists of attributes.
+ */
+public interface AttributeList {
+ /**
+ * Get whether this instance is mutable. Note that the
+ * {@code AttributeList} interface itself doesn't provide any means
+ * of mutation, but that doesn't mean that there isn't a non-interface
+ * way of mutating an instance.
+ *
+ * @return {@code true} iff this instance is somehow mutable
+ */
+ public boolean isMutable();
+
+ /**
+ * Get the number of attributes in the list.
+ *
+ * @return the size
+ */
+ public int size();
+
+ /**
+ * Get the {@code n}th attribute.
+ *
+ * @param n {@code n >= 0, n < size();} which attribute
+ * @return {@code non-null;} the attribute in question
+ */
+ public Attribute get(int n);
+
+ /**
+ * Get the total length of this list in bytes, when part of a
+ * class file. The returned value includes the two bytes for the
+ * {@code attributes_count} length indicator.
+ *
+ * @return {@code >= 2;} the total length, in bytes
+ */
+ public int byteLength();
+
+ /**
+ * Get the first attribute in the list with the given name, if any.
+ *
+ * @param name {@code non-null;} attribute name
+ * @return {@code null-ok;} first attribute in the list with the given name,
+ * or {@code null} if there is none
+ */
+ public Attribute findFirst(String name);
+
+ /**
+ * Get the next attribute in the list after the given one, with the same
+ * name, if any.
+ *
+ * @param attrib {@code non-null;} attribute to start looking after
+ * @return {@code null-ok;} next attribute after {@code attrib} with the
+ * same name as {@code attrib}
+ */
+ public Attribute findNext(Attribute attrib);
+}
diff --git a/dx/src/com/android/dx/cf/iface/ClassFile.java b/dx/src/com/android/dx/cf/iface/ClassFile.java
new file mode 100644
index 0000000..0f584ba
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/ClassFile.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.iface;
+
+import com.android.dx.rop.cst.ConstantPool;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.TypeList;
+
+/**
+ * Interface for things which purport to be class files or reasonable
+ * facsimiles thereof.
+ *
+ * <p><b>Note:</b> The fields referred to in this documentation are of the
+ * {@code ClassFile} structure defined in vmspec-2 sec4.1.
+ */
+public interface ClassFile {
+ /**
+ * Gets the field {@code magic}.
+ *
+ * @return the value in question
+ */
+ public int getMagic();
+
+ /**
+ * Gets the field {@code minor_version}.
+ *
+ * @return the value in question
+ */
+ public int getMinorVersion();
+
+ /**
+ * Gets the field {@code major_version}.
+ *
+ * @return the value in question
+ */
+ public int getMajorVersion();
+
+ /**
+ * Gets the field {@code access_flags}.
+ *
+ * @return the value in question
+ */
+ public int getAccessFlags();
+
+ /**
+ * Gets the field {@code this_class}, interpreted as a type constant.
+ *
+ * @return {@code non-null;} the value in question
+ */
+ public CstType getThisClass();
+
+ /**
+ * Gets the field {@code super_class}, interpreted as a type constant
+ * if non-zero.
+ *
+ * @return {@code null-ok;} the value in question
+ */
+ public CstType getSuperclass();
+
+ /**
+ * Gets the field {@code constant_pool} (along with
+ * {@code constant_pool_count}).
+ *
+ * @return {@code non-null;} the constant pool
+ */
+ public ConstantPool getConstantPool();
+
+ /**
+ * Gets the field {@code interfaces} (along with
+ * {@code interfaces_count}).
+ *
+ * @return {@code non-null;} the list of interfaces
+ */
+ public TypeList getInterfaces();
+
+ /**
+ * Gets the field {@code fields} (along with
+ * {@code fields_count}).
+ *
+ * @return {@code non-null;} the list of fields
+ */
+ public FieldList getFields();
+
+ /**
+ * Gets the field {@code methods} (along with
+ * {@code methods_count}).
+ *
+ * @return {@code non-null;} the list of fields
+ */
+ public MethodList getMethods();
+
+ /**
+ * Gets the field {@code attributes} (along with
+ * {@code attributes_count}).
+ *
+ * @return {@code non-null;} the list of attributes
+ */
+ public AttributeList getAttributes();
+
+ /**
+ * Gets the name out of the {@code SourceFile} attribute of this
+ * file, if any. This is a convenient shorthand for scrounging around
+ * the class's attributes.
+ *
+ * @return {@code non-null;} the constant pool
+ */
+ public CstUtf8 getSourceFile();
+}
diff --git a/dx/src/com/android/dx/cf/iface/Field.java b/dx/src/com/android/dx/cf/iface/Field.java
new file mode 100644
index 0000000..e3002bc
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/Field.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.iface;
+
+import com.android.dx.rop.cst.TypedConstant;
+
+/**
+ * Interface representing fields of class files.
+ */
+public interface Field
+ extends Member {
+ /**
+ * Get the constant value for this field, if any. This only returns
+ * non-{@code null} for a {@code static final} field which
+ * includes a {@code ConstantValue} attribute.
+ *
+ * @return {@code null-ok;} the constant value, or {@code null} if this
+ * field isn't a constant
+ */
+ public TypedConstant getConstantValue();
+}
diff --git a/dx/src/com/android/dx/cf/iface/FieldList.java b/dx/src/com/android/dx/cf/iface/FieldList.java
new file mode 100644
index 0000000..9cd27a3
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/FieldList.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.iface;
+
+/**
+ * Interface for lists of fields.
+ */
+public interface FieldList
+{
+ /**
+ * Get whether this instance is mutable. Note that the
+ * {@code FieldList} interface itself doesn't provide any means
+ * of mutation, but that doesn't mean that there isn't a non-interface
+ * way of mutating an instance.
+ *
+ * @return {@code true} iff this instance is somehow mutable
+ */
+ public boolean isMutable();
+
+ /**
+ * Get the number of fields in the list.
+ *
+ * @return the size
+ */
+ public int size();
+
+ /**
+ * Get the {@code n}th field.
+ *
+ * @param n {@code n >= 0, n < size();} which field
+ * @return {@code non-null;} the field in question
+ */
+ public Field get(int n);
+}
diff --git a/dx/src/com/android/dx/cf/iface/Member.java b/dx/src/com/android/dx/cf/iface/Member.java
new file mode 100644
index 0000000..0453a6f
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/Member.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.iface;
+
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+
+/**
+ * Interface representing members of class files (that is, fields and methods).
+ */
+public interface Member {
+ /**
+ * Get the defining class.
+ *
+ * @return {@code non-null;} the defining class
+ */
+ public CstType getDefiningClass();
+
+ /**
+ * Get the field {@code access_flags}.
+ *
+ * @return the access flags
+ */
+ public int getAccessFlags();
+
+ /**
+ * Get the field {@code name_index} of the member. This is
+ * just a convenient shorthand for {@code getNat().getName()}.
+ *
+ * @return {@code non-null;} the name
+ */
+ public CstUtf8 getName();
+
+ /**
+ * Get the field {@code descriptor_index} of the member. This is
+ * just a convenient shorthand for {@code getNat().getDescriptor()}.
+ *
+ * @return {@code non-null;} the descriptor
+ */
+ public CstUtf8 getDescriptor();
+
+ /**
+ * Get the name and type associated with this member. This is a
+ * combination of the fields {@code name_index} and
+ * {@code descriptor_index} in the original classfile, interpreted
+ * via the constant pool.
+ *
+ * @return {@code non-null;} the name and type
+ */
+ public CstNat getNat();
+
+ /**
+ * Get the field {@code attributes} (along with
+ * {@code attributes_count}).
+ *
+ * @return {@code non-null;} the constant pool
+ */
+ public AttributeList getAttributes();
+}
diff --git a/dx/src/com/android/dx/cf/iface/Method.java b/dx/src/com/android/dx/cf/iface/Method.java
new file mode 100644
index 0000000..18b9af6
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/Method.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.iface;
+
+import com.android.dx.rop.type.Prototype;
+
+/**
+ * Interface representing methods of class files.
+ */
+public interface Method
+ extends Member
+{
+ /**
+ * Get the <i>effective</i> method descriptor, which includes, if
+ * necessary, a first {@code this} parameter.
+ *
+ * @return {@code non-null;} the effective method descriptor
+ */
+ public Prototype getEffectiveDescriptor();
+}
diff --git a/dx/src/com/android/dx/cf/iface/MethodList.java b/dx/src/com/android/dx/cf/iface/MethodList.java
new file mode 100644
index 0000000..dfa6528
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/MethodList.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.iface;
+
+/**
+ * Interface for lists of methods.
+ */
+public interface MethodList {
+ /**
+ * Get whether this instance is mutable. Note that the
+ * {@code MethodList} interface itself doesn't provide any means
+ * of mutation, but that doesn't mean that there isn't a non-interface
+ * way of mutating an instance.
+ *
+ * @return {@code true} iff this instance is somehow mutable
+ */
+ public boolean isMutable();
+
+ /**
+ * Get the number of methods in the list.
+ *
+ * @return the size
+ */
+ public int size();
+
+ /**
+ * Get the {@code n}th method.
+ *
+ * @param n {@code n >= 0, n < size();} which method
+ * @return {@code non-null;} the method in question
+ */
+ public Method get(int n);
+}
diff --git a/dx/src/com/android/dx/cf/iface/ParseException.java b/dx/src/com/android/dx/cf/iface/ParseException.java
new file mode 100644
index 0000000..18a9d0e
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/ParseException.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.iface;
+
+import com.android.dx.util.ExceptionWithContext;
+
+/**
+ * Exception from parsing.
+ */
+public class ParseException
+ extends ExceptionWithContext {
+ public ParseException(String message) {
+ super(message);
+ }
+
+ public ParseException(Throwable cause) {
+ super(cause);
+ }
+
+ public ParseException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/dx/src/com/android/dx/cf/iface/ParseObserver.java b/dx/src/com/android/dx/cf/iface/ParseObserver.java
new file mode 100644
index 0000000..98d5a75
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/ParseObserver.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.iface;
+
+import com.android.dx.util.ByteArray;
+
+/**
+ * Observer of parsing in action. This is used to supply feedback from
+ * the various things that parse particularly to the dumping utilities.
+ */
+public interface ParseObserver {
+ /**
+ * Indicate that the level of indentation for a dump should increase
+ * or decrease (positive or negative argument, respectively).
+ *
+ * @param indentDelta the amount to change indentation
+ */
+ public void changeIndent(int indentDelta);
+
+ /**
+ * Indicate that a particular member is now being parsed.
+ *
+ * @param bytes {@code non-null;} the source that is being parsed
+ * @param offset offset into {@code bytes} for the start of the
+ * member
+ * @param name {@code non-null;} name of the member
+ * @param descriptor {@code non-null;} descriptor of the member
+ */
+ public void startParsingMember(ByteArray bytes, int offset, String name,
+ String descriptor);
+
+ /**
+ * Indicate that a particular member is no longer being parsed.
+ *
+ * @param bytes {@code non-null;} the source that was parsed
+ * @param offset offset into {@code bytes} for the end of the
+ * member
+ * @param name {@code non-null;} name of the member
+ * @param descriptor {@code non-null;} descriptor of the member
+ * @param member {@code non-null;} the actual member that was parsed
+ */
+ public void endParsingMember(ByteArray bytes, int offset, String name,
+ String descriptor, Member member);
+
+ /**
+ * Indicate that some parsing happened.
+ *
+ * @param bytes {@code non-null;} the source that was parsed
+ * @param offset offset into {@code bytes} for what was parsed
+ * @param len number of bytes parsed
+ * @param human {@code non-null;} human form for what was parsed
+ */
+ public void parsed(ByteArray bytes, int offset, int len, String human);
+}
diff --git a/dx/src/com/android/dx/cf/iface/StdAttributeList.java b/dx/src/com/android/dx/cf/iface/StdAttributeList.java
new file mode 100644
index 0000000..287b8c7
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/StdAttributeList.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.iface;
+
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * Standard implementation of {@link AttributeList}, which directly stores
+ * an array of {@link Attribute} objects and can be made immutable.
+ */
+public final class StdAttributeList extends FixedSizeList
+ implements AttributeList {
+ /**
+ * Constructs an instance. All indices initially contain {@code null}.
+ *
+ * @param size the size of the list
+ */
+ public StdAttributeList(int size) {
+ super(size);
+ }
+
+ /** {@inheritDoc} */
+ public Attribute get(int n) {
+ return (Attribute) get0(n);
+ }
+
+ /** {@inheritDoc} */
+ public int byteLength() {
+ int sz = size();
+ int result = 2; // u2 attributes_count
+
+ for (int i = 0; i < sz; i++) {
+ result += get(i).byteLength();
+ }
+
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ public Attribute findFirst(String name) {
+ int sz = size();
+
+ for (int i = 0; i < sz; i++) {
+ Attribute att = get(i);
+ if (att.getName().equals(name)) {
+ return att;
+ }
+ }
+
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ public Attribute findNext(Attribute attrib) {
+ int sz = size();
+ int at;
+
+ outer: {
+ for (at = 0; at < sz; at++) {
+ Attribute att = get(at);
+ if (att == attrib) {
+ break outer;
+ }
+ }
+
+ return null;
+ }
+
+ String name = attrib.getName();
+
+ for (at++; at < sz; at++) {
+ Attribute att = get(at);
+ if (att.getName().equals(name)) {
+ return att;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Sets the attribute at the given index.
+ *
+ * @param n {@code >= 0, < size();} which attribute
+ * @param attribute {@code null-ok;} the attribute object
+ */
+ public void set(int n, Attribute attribute) {
+ set0(n, attribute);
+ }
+}
diff --git a/dx/src/com/android/dx/cf/iface/StdField.java b/dx/src/com/android/dx/cf/iface/StdField.java
new file mode 100644
index 0000000..ef9873d
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/StdField.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.iface;
+
+import com.android.dx.cf.attrib.AttConstantValue;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.TypedConstant;
+
+/**
+ * Standard implementation of {@link Field}, which directly stores
+ * all the associated data.
+ */
+public final class StdField extends StdMember implements Field {
+ /**
+ * Constructs an instance.
+ *
+ * @param definingClass {@code non-null;} the defining class
+ * @param accessFlags access flags
+ * @param nat {@code non-null;} member name and type (descriptor)
+ * @param attributes {@code non-null;} list of associated attributes
+ */
+ public StdField(CstType definingClass, int accessFlags, CstNat nat,
+ AttributeList attributes) {
+ super(definingClass, accessFlags, nat, attributes);
+ }
+
+ /** {@inheritDoc} */
+ public TypedConstant getConstantValue() {
+ AttributeList attribs = getAttributes();
+ AttConstantValue cval = (AttConstantValue)
+ attribs.findFirst(AttConstantValue.ATTRIBUTE_NAME);
+
+ if (cval == null) {
+ return null;
+ }
+
+ return cval.getConstantValue();
+ }
+}
diff --git a/dx/src/com/android/dx/cf/iface/StdFieldList.java b/dx/src/com/android/dx/cf/iface/StdFieldList.java
new file mode 100644
index 0000000..f27bd22
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/StdFieldList.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.iface;
+
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * Standard implementation of {@link FieldList}, which directly stores
+ * an array of {@link Field} objects and can be made immutable.
+ */
+public final class StdFieldList extends FixedSizeList implements FieldList {
+ /**
+ * Constructs an instance. All indices initially contain {@code null}.
+ *
+ * @param size the size of the list
+ */
+ public StdFieldList(int size) {
+ super(size);
+ }
+
+ /** {@inheritDoc} */
+ public Field get(int n) {
+ return (Field) get0(n);
+ }
+
+ /**
+ * Sets the field at the given index.
+ *
+ * @param n {@code >= 0, < size();} which field
+ * @param field {@code null-ok;} the field object
+ */
+ public void set(int n, Field field) {
+ set0(n, field);
+ }
+}
diff --git a/dx/src/com/android/dx/cf/iface/StdMember.java b/dx/src/com/android/dx/cf/iface/StdMember.java
new file mode 100644
index 0000000..9f15585
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/StdMember.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.iface;
+
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+
+/**
+ * Standard implementation of {@link Member}, which directly stores
+ * all the associated data.
+ */
+public abstract class StdMember implements Member {
+ /** {@code non-null;} the defining class */
+ private final CstType definingClass;
+
+ /** access flags */
+ private final int accessFlags;
+
+ /** {@code non-null;} member name and type */
+ private final CstNat nat;
+
+ /** {@code non-null;} list of associated attributes */
+ private final AttributeList attributes;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param definingClass {@code non-null;} the defining class
+ * @param accessFlags access flags
+ * @param nat {@code non-null;} member name and type (descriptor)
+ * @param attributes {@code non-null;} list of associated attributes
+ */
+ public StdMember(CstType definingClass, int accessFlags, CstNat nat,
+ AttributeList attributes) {
+ if (definingClass == null) {
+ throw new NullPointerException("definingClass == null");
+ }
+
+ if (nat == null) {
+ throw new NullPointerException("nat == null");
+ }
+
+ if (attributes == null) {
+ throw new NullPointerException("attributes == null");
+ }
+
+ this.definingClass = definingClass;
+ this.accessFlags = accessFlags;
+ this.nat = nat;
+ this.attributes = attributes;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer(100);
+
+ sb.append(getClass().getName());
+ sb.append('{');
+ sb.append(nat.toHuman());
+ sb.append('}');
+
+ return sb.toString();
+ }
+
+ /** {@inheritDoc} */
+ public final CstType getDefiningClass() {
+ return definingClass;
+ }
+
+ /** {@inheritDoc} */
+ public final int getAccessFlags() {
+ return accessFlags;
+ }
+
+ /** {@inheritDoc} */
+ public final CstNat getNat() {
+ return nat;
+ }
+
+ /** {@inheritDoc} */
+ public final CstUtf8 getName() {
+ return nat.getName();
+ }
+
+ /** {@inheritDoc} */
+ public final CstUtf8 getDescriptor() {
+ return nat.getDescriptor();
+ }
+
+ /** {@inheritDoc} */
+ public final AttributeList getAttributes() {
+ return attributes;
+ }
+}
diff --git a/dx/src/com/android/dx/cf/iface/StdMethod.java b/dx/src/com/android/dx/cf/iface/StdMethod.java
new file mode 100644
index 0000000..c511d7d
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/StdMethod.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.iface;
+
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Prototype;
+
+/**
+ * Standard implementation of {@link Method}, which directly stores
+ * all the associated data.
+ */
+public final class StdMethod extends StdMember implements Method {
+ /** {@code non-null;} the effective method descriptor */
+ private final Prototype effectiveDescriptor;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param definingClass {@code non-null;} the defining class
+ * @param accessFlags access flags
+ * @param nat {@code non-null;} member name and type (descriptor)
+ * @param attributes {@code non-null;} list of associated attributes
+ */
+ public StdMethod(CstType definingClass, int accessFlags, CstNat nat,
+ AttributeList attributes) {
+ super(definingClass, accessFlags, nat, attributes);
+
+ String descStr = getDescriptor().getString();
+ effectiveDescriptor =
+ Prototype.intern(descStr, definingClass.getClassType(),
+ AccessFlags.isStatic(accessFlags),
+ nat.isInstanceInit());
+ }
+
+ /** {@inheritDoc} */
+ public Prototype getEffectiveDescriptor() {
+ return effectiveDescriptor;
+ }
+}
diff --git a/dx/src/com/android/dx/cf/iface/StdMethodList.java b/dx/src/com/android/dx/cf/iface/StdMethodList.java
new file mode 100644
index 0000000..417cdee
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/StdMethodList.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.cf.iface;
+
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * Standard implementation of {@link MethodList}, which directly stores
+ * an array of {@link Method} objects and can be made immutable.
+ */
+public final class StdMethodList extends FixedSizeList implements MethodList {
+ /**
+ * Constructs an instance. All indices initially contain {@code null}.
+ *
+ * @param size the size of the list
+ */
+ public StdMethodList(int size) {
+ super(size);
+ }
+
+ /** {@inheritDoc} */
+ public Method get(int n) {
+ return (Method) get0(n);
+ }
+
+ /**
+ * Sets the method at the given index.
+ *
+ * @param n {@code >= 0, < size();} which method
+ * @param method {@code null-ok;} the method object
+ */
+ public void set(int n, Method method) {
+ set0(n, method);
+ }
+}
diff --git a/dx/src/com/android/dx/cf/iface/package.html b/dx/src/com/android/dx/cf/iface/package.html
new file mode 100644
index 0000000..c734552
--- /dev/null
+++ b/dx/src/com/android/dx/cf/iface/package.html
@@ -0,0 +1,10 @@
+<body>
+<p>Interfaces and base classes for dealing with class files. This package
+doesn't have any parsing but does have basic container implementations.</p>
+
+<p><b>PACKAGES USED:</b>
+<ul>
+<li><code>com.android.dx.rop.pool</code></li>
+<li><code>com.android.dx.util</code></li>
+</ul>
+</body>
diff --git a/dx/src/com/android/dx/command/DxConsole.java b/dx/src/com/android/dx/command/DxConsole.java
new file mode 100644
index 0000000..9ce9836
--- /dev/null
+++ b/dx/src/com/android/dx/command/DxConsole.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.command;
+
+import java.io.PrintStream;
+
+/**
+ * Provides standard and error PrintStream object to output information.<br>
+ * By default the PrintStream objects link to {@code System.out} and
+ * {@code System.err} but they can be changed to link to other
+ * PrintStream.
+ */
+public class DxConsole {
+ /**
+ * Standard output stream. Links to {@code System.out} by default.
+ */
+ public static PrintStream out = System.out;
+
+ /**
+ * Error output stream. Links to {@code System.err} by default.
+ */
+ public static PrintStream err = System.err;
+}
diff --git a/dx/src/com/android/dx/command/Main.java b/dx/src/com/android/dx/command/Main.java
new file mode 100644
index 0000000..626cf7a
--- /dev/null
+++ b/dx/src/com/android/dx/command/Main.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.command;
+
+import com.android.dx.Version;
+
+import junit.textui.TestRunner;
+
+/**
+ * Main class for dx. It recognizes enough options to be able to dispatch
+ * to the right "actual" main.
+ */
+public class Main {
+ private static String USAGE_MESSAGE =
+ "usage:\n" +
+ " dx --dex [--debug] [--verbose] [--positions=<style>] " +
+ "[--no-locals]\n" +
+ " [--no-optimize] [--statistics] [--[no-]optimize-list=<file>] " +
+ "[--no-strict]\n" +
+ " [--keep-classes] [--output=<file>] [--dump-to=<file>] " +
+ "[--dump-width=<n>]\n" +
+ " [--dump-method=<name>[*]] [--verbose-dump] [--no-files] " +
+ "[--core-library]\n" +
+ " [<file>.class | <file>.{zip,jar,apk} | <directory>] ...\n" +
+ " Convert a set of classfiles into a dex file, optionally " +
+ "embedded in a\n" +
+ " jar/zip. Output name must end with one of: .dex .jar " +
+ ".zip .apk. Positions\n" +
+ " options: none, important, lines.\n" +
+ " dx --annotool --annotation=<class> [--element=<element types>]\n" +
+ " [--print=<print types>]\n" +
+ " dx --dump [--debug] [--strict] [--bytes] [--optimize]\n" +
+ " [--basic-blocks | --rop-blocks | --ssa-blocks | --dot] " +
+ "[--ssa-step=<step>]\n" +
+ " [--width=<n>] [<file>.class | <file>.txt] ...\n" +
+ " Dump classfiles, or transformations thereof, in a " +
+ "human-oriented format.\n" +
+ " dx --junit [-wait] <TestClass>\n" +
+ " Run the indicated unit test.\n" +
+ " dx -J<option> ... <arguments, in one of the above " +
+ "forms>\n" +
+ " Pass VM-specific options to the virtual machine that " +
+ "runs dx.\n" +
+ " dx --version\n" +
+ " Print the version of this tool (" + Version.VERSION +
+ ").\n" +
+ " dx --help\n" +
+ " Print this message.";
+
+ /**
+ * This class is uninstantiable.
+ */
+ private Main() {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Run!
+ */
+ public static void main(String[] args) {
+ boolean gotCmd = false;
+ boolean showUsage = false;
+
+ try {
+ for (int i = 0; i < args.length; i++) {
+ String arg = args[i];
+ if (arg.equals("--") || !arg.startsWith("--")) {
+ gotCmd = false;
+ showUsage = true;
+ break;
+ }
+
+ gotCmd = true;
+ if (arg.equals("--dex")) {
+ com.android.dx.command.dexer.Main.main(without(args, i));
+ break;
+ } else if (arg.equals("--dump")) {
+ com.android.dx.command.dump.Main.main(without(args, i));
+ break;
+ } else if (arg.equals("--annotool")) {
+ com.android.dx.command.annotool.Main.main(
+ without(args, i));
+ break;
+ } else if (arg.equals("--junit")) {
+ TestRunner.main(without(args, i));
+ break;
+ } else if (arg.equals("--version")) {
+ version();
+ break;
+ } else if (arg.equals("--help")) {
+ showUsage = true;
+ break;
+ } else {
+ gotCmd = false;
+ }
+ }
+ } catch (UsageException ex) {
+ showUsage = true;
+ } catch (RuntimeException ex) {
+ System.err.println("\nUNEXPECTED TOP-LEVEL EXCEPTION:");
+ ex.printStackTrace();
+ System.exit(2);
+ } catch (Throwable ex) {
+ System.err.println("\nUNEXPECTED TOP-LEVEL ERROR:");
+ ex.printStackTrace();
+ if ((ex instanceof NoClassDefFoundError)
+ || (ex instanceof NoSuchMethodError)) {
+ System.err.println(
+ "Note: You may be using an incompatible " +
+ "virtual machine or class library.\n" +
+ "(This program is known to be incompatible " +
+ "with recent releases of GCJ.)");
+ }
+ System.exit(3);
+ }
+
+ if (!gotCmd) {
+ System.err.println("error: no command specified");
+ showUsage = true;
+ }
+
+ if (showUsage) {
+ usage();
+ System.exit(1);
+ }
+ }
+
+ /**
+ * Prints the version message.
+ */
+ private static void version() {
+ System.err.println("dx version " + Version.VERSION);
+ System.exit(0);
+ }
+
+ /**
+ * Prints the usage message.
+ */
+ private static void usage() {
+ System.err.println(USAGE_MESSAGE);
+ }
+
+ /**
+ * Returns a copy of the given args array, but without the indicated
+ * element.
+ *
+ * @param orig {@code non-null;} original array
+ * @param n which element to omit
+ * @return {@code non-null;} new array
+ */
+ private static String[] without(String[] orig, int n) {
+ int len = orig.length - 1;
+ String[] newa = new String[len];
+ System.arraycopy(orig, 0, newa, 0, n);
+ System.arraycopy(orig, n + 1, newa, n, len - n);
+ return newa;
+ }
+}
diff --git a/dx/src/com/android/dx/command/UsageException.java b/dx/src/com/android/dx/command/UsageException.java
new file mode 100644
index 0000000..6809bf4
--- /dev/null
+++ b/dx/src/com/android/dx/command/UsageException.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.command;
+
+/**
+ * Simple exception class used to communicate that the command-line tool
+ * should print the usage message.
+ */
+public class UsageException extends RuntimeException {
+ // This space intentionally left blank.
+}
diff --git a/dx/src/com/android/dx/command/annotool/AnnotationLister.java b/dx/src/com/android/dx/command/annotool/AnnotationLister.java
new file mode 100644
index 0000000..a29e5ba
--- /dev/null
+++ b/dx/src/com/android/dx/command/annotool/AnnotationLister.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.command.annotool;
+
+import com.android.dx.cf.direct.ClassPathOpener;
+import com.android.dx.cf.direct.DirectClassFile;
+import com.android.dx.cf.direct.StdAttributeFactory;
+import com.android.dx.cf.iface.AttributeList;
+import com.android.dx.cf.iface.Attribute;
+import com.android.dx.cf.attrib.AttRuntimeInvisibleAnnotations;
+import com.android.dx.cf.attrib.BaseAnnotations;
+import com.android.dx.cf.attrib.AttRuntimeVisibleAnnotations;
+import com.android.dx.util.ByteArray;
+import com.android.dx.rop.annotation.Annotation;
+
+import java.io.File;
+import java.lang.annotation.ElementType;
+import java.util.HashSet;
+
+/**
+ * Greps annotations on a set of class files and prints matching elements
+ * to stdout. What counts as a match and what should be printed is controlled
+ * by the {@code Main.Arguments} instance.
+ */
+class AnnotationLister {
+ /**
+ * The string name of the pseudo-class that
+ * contains package-wide annotations
+ */
+ private static final String PACKAGE_INFO = "package-info";
+
+ /** current match configuration */
+ private final Main.Arguments args;
+
+ /** Set of classes whose inner classes should be considered matched */
+ HashSet<String> matchInnerClassesOf = new HashSet<String>();
+
+ /** set of packages whose classes should be considered matched */
+ HashSet<String> matchPackages = new HashSet<String>();
+
+ AnnotationLister (Main.Arguments args) {
+ this.args = args;
+ }
+
+ /** Processes based on configuration specified in constructor. */
+ void process() {
+ for (String path : args.files) {
+ ClassPathOpener opener;
+
+ opener = new ClassPathOpener(path, true,
+ new ClassPathOpener.Consumer() {
+ public boolean processFileBytes(String name, byte[] bytes) {
+ if (!name.endsWith(".class")) {
+ return true;
+ }
+
+ ByteArray ba = new ByteArray(bytes);
+ DirectClassFile cf
+ = new DirectClassFile(ba, name, true);
+
+ cf.setAttributeFactory(StdAttributeFactory.THE_ONE);
+ AttributeList attributes = cf.getAttributes();
+ Attribute att;
+
+ String cfClassName
+ = cf.getThisClass().getClassType().getClassName();
+
+ if (cfClassName.endsWith(PACKAGE_INFO)) {
+ att = attributes.findFirst(
+ AttRuntimeInvisibleAnnotations.ATTRIBUTE_NAME);
+
+ for (;att != null; att = attributes.findNext(att)) {
+ BaseAnnotations ann = (BaseAnnotations)att;
+ visitPackageAnnotation(cf, ann);
+ }
+
+ att = attributes.findFirst(
+ AttRuntimeVisibleAnnotations.ATTRIBUTE_NAME);
+
+ for (;att != null; att = attributes.findNext(att)) {
+ BaseAnnotations ann = (BaseAnnotations)att;
+ visitPackageAnnotation(cf, ann);
+ }
+ } else if (isMatchingInnerClass(cfClassName)
+ || isMatchingPackage(cfClassName)) {
+ printMatch(cf);
+ } else {
+ att = attributes.findFirst(
+ AttRuntimeInvisibleAnnotations.ATTRIBUTE_NAME);
+
+ for (;att != null; att = attributes.findNext(att)) {
+ BaseAnnotations ann = (BaseAnnotations)att;
+ visitClassAnnotation(cf, ann);
+ }
+
+ att = attributes.findFirst(
+ AttRuntimeVisibleAnnotations.ATTRIBUTE_NAME);
+
+ for (;att != null; att = attributes.findNext(att)) {
+ BaseAnnotations ann = (BaseAnnotations)att;
+ visitClassAnnotation(cf, ann);
+ }
+ }
+
+ return true;
+ }
+
+ public void onException(Exception ex) {
+ throw new RuntimeException(ex);
+ }
+
+ public void onProcessArchiveStart(File file) {
+
+ }
+
+ });
+
+ opener.process();
+ }
+ }
+
+ /**
+ * Inspects a class annotation.
+ *
+ * @param cf {@code non-null;} class file
+ * @param ann {@code non-null;} annotation
+ */
+ private void visitClassAnnotation(DirectClassFile cf,
+ BaseAnnotations ann) {
+
+ if (!args.eTypes.contains(ElementType.TYPE)) {
+ return;
+ }
+
+ for (Annotation anAnn : ann.getAnnotations().getAnnotations()) {
+ String annClassName
+ = anAnn.getType().getClassType().getClassName();
+ if (args.aclass.equals(annClassName)) {
+ printMatch(cf);
+ }
+ }
+ }
+
+ /**
+ * Inspects a package annotation
+ *
+ * @param cf {@code non-null;} class file of "package-info" pseudo-class
+ * @param ann {@code non-null;} annotation
+ */
+ private void visitPackageAnnotation(
+ DirectClassFile cf, BaseAnnotations ann) {
+
+ if (!args.eTypes.contains(ElementType.PACKAGE)) {
+ return;
+ }
+
+ String packageName = cf.getThisClass().getClassType().getClassName();
+
+ int slashIndex = packageName.lastIndexOf('/');
+
+ if (slashIndex == -1) {
+ packageName = "";
+ } else {
+ packageName
+ = packageName.substring(0, slashIndex);
+ }
+
+
+ for (Annotation anAnn : ann.getAnnotations().getAnnotations()) {
+ String annClassName
+ = anAnn.getType().getClassType().getClassName();
+ if (args.aclass.equals(annClassName)) {
+ printMatchPackage(packageName);
+ }
+ }
+ }
+
+
+ /**
+ * Prints, or schedules for printing, elements related to a
+ * matching package.
+ *
+ * @param packageName {@code non-null;} name of package
+ */
+ private void printMatchPackage(String packageName) {
+ for (Main.PrintType pt : args.printTypes) {
+ switch (pt) {
+ case CLASS:
+ case INNERCLASS:
+ case METHOD:
+ matchPackages.add(packageName);
+ break;
+ case PACKAGE:
+ System.out.println(packageName.replace('/','.'));
+ break;
+ }
+ }
+ }
+
+ /**
+ * Prints, or schedules for printing, elements related to a matching
+ * class.
+ *
+ * @param cf {@code non-null;} matching class
+ */
+ private void printMatch(DirectClassFile cf) {
+ for (Main.PrintType pt : args.printTypes) {
+ switch (pt) {
+ case CLASS:
+ String classname;
+ classname =
+ cf.getThisClass().getClassType().getClassName();
+ classname = classname.replace('/','.');
+ System.out.println(classname);
+ break;
+ case INNERCLASS:
+ matchInnerClassesOf.add(
+ cf.getThisClass().getClassType().getClassName());
+ break;
+ case METHOD:
+ //TODO
+ break;
+ case PACKAGE:
+ break;
+ }
+ }
+ }
+
+ /**
+ * Checks to see if a specified class name should be considered a match
+ * due to previous matches.
+ *
+ * @param s {@code non-null;} class name
+ * @return true if this class should be considered a match
+ */
+ private boolean isMatchingInnerClass(String s) {
+ int i;
+
+ while (0 < (i = s.lastIndexOf('$'))) {
+ s = s.substring(0, i);
+ if (matchInnerClassesOf.contains(s)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks to see if a specified package should be considered a match due
+ * to previous matches.
+ *
+ * @param s {@code non-null;} package name
+ * @return true if this package should be considered a match
+ */
+ private boolean isMatchingPackage(String s) {
+ int slashIndex = s.lastIndexOf('/');
+
+ String packageName;
+ if (slashIndex == -1) {
+ packageName = "";
+ } else {
+ packageName
+ = s.substring(0, slashIndex);
+ }
+
+ return matchPackages.contains(packageName);
+ }
+}
diff --git a/dx/src/com/android/dx/command/annotool/Main.java b/dx/src/com/android/dx/command/annotool/Main.java
new file mode 100644
index 0000000..7661c3d
--- /dev/null
+++ b/dx/src/com/android/dx/command/annotool/Main.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.command.annotool;
+
+import com.android.dx.cf.direct.ClassPathOpener;
+import com.android.dx.cf.direct.DirectClassFile;
+import com.android.dx.cf.direct.StdAttributeFactory;
+import com.android.dx.cf.iface.AttributeList;
+import com.android.dx.cf.iface.Attribute;
+import com.android.dx.cf.attrib.AttRuntimeInvisibleAnnotations;
+import com.android.dx.cf.attrib.BaseAnnotations;
+import com.android.dx.cf.attrib.AttRuntimeVisibleAnnotations;
+import com.android.dx.util.ByteArray;
+import com.android.dx.rop.annotation.Annotation;
+
+import java.io.File;
+import java.lang.annotation.ElementType;
+import java.util.EnumSet;
+import java.util.Arrays;
+
+
+public class Main {
+
+ private static class InvalidArgumentException extends Exception {
+ InvalidArgumentException() {
+ super();
+ }
+
+ InvalidArgumentException(String s) {
+ super(s);
+ }
+ }
+
+ enum PrintType {
+ CLASS,
+ INNERCLASS,
+ METHOD,
+ PACKAGE
+ }
+
+
+ static class Arguments {
+ /**
+ * from --annotation, dot-seperated classname
+ * of annotation to look for
+ */
+ String aclass;
+
+ /** from --eTypes */
+ EnumSet<ElementType> eTypes = EnumSet.noneOf(ElementType.class);
+
+ /** from --print */
+ EnumSet<PrintType> printTypes = EnumSet.noneOf(PrintType.class);
+
+ /** remaining positional arguments */
+ String[] files;
+
+ Arguments() {
+ }
+
+ void parse (String[] argArray) throws InvalidArgumentException {
+ for (int i = 0; i < argArray.length; i++) {
+ String arg = argArray[i];
+
+ if (arg.startsWith("--annotation=")) {
+ String argParam = arg.substring(arg.indexOf('=') + 1);
+ if (aclass != null) {
+ throw new InvalidArgumentException(
+ "--annotation can only be specified once.");
+ }
+ aclass = argParam.replace('.','/');
+ } else if (arg.startsWith("--element=")) {
+ String argParam = arg.substring(arg.indexOf('=') + 1);
+
+ try {
+ for (String p : argParam.split(",")) {
+ eTypes.add(ElementType.valueOf(p.toUpperCase()));
+ }
+ } catch (IllegalArgumentException ex) {
+ throw new InvalidArgumentException(
+ "invalid --element");
+ }
+ } else if (arg.startsWith("--print=")) {
+ String argParam = arg.substring(arg.indexOf('=') + 1);
+
+ try {
+ for (String p : argParam.split(",")) {
+ printTypes.add(PrintType.valueOf(p.toUpperCase()));
+ }
+ } catch (IllegalArgumentException ex) {
+ throw new InvalidArgumentException("invalid --print");
+ }
+ } else {
+ files = new String[argArray.length - i];
+ System.arraycopy(argArray, i, files, 0, files.length);
+ break;
+ }
+ }
+
+ if (aclass == null) {
+ throw new InvalidArgumentException(
+ "--annotation must be specified");
+ }
+
+ if (printTypes.isEmpty()) {
+ printTypes.add(PrintType.CLASS);
+ }
+
+ if (eTypes.isEmpty()) {
+ eTypes.add(ElementType.TYPE);
+ }
+
+ EnumSet<ElementType> set = eTypes.clone();
+
+ set.remove(ElementType.TYPE);
+ set.remove(ElementType.PACKAGE);
+ if (!set.isEmpty()) {
+ throw new InvalidArgumentException(
+ "only --element parameters 'type' and 'package' "
+ + "supported");
+ }
+ }
+ }
+
+ /**
+ * This class is uninstantiable.
+ */
+ private Main() {
+ // This space intentionally left blank.
+ }
+
+ public static void main(String[] argArray) {
+
+ final Arguments args = new Arguments();
+
+ try {
+ args.parse(argArray);
+ } catch (InvalidArgumentException ex) {
+ System.err.println(ex.getMessage());
+
+ throw new RuntimeException("usage");
+ }
+
+ new AnnotationLister(args).process();
+ }
+}
diff --git a/dx/src/com/android/dx/command/dexer/Main.java b/dx/src/com/android/dx/command/dexer/Main.java
new file mode 100644
index 0000000..363741a
--- /dev/null
+++ b/dx/src/com/android/dx/command/dexer/Main.java
@@ -0,0 +1,930 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.command.dexer;
+
+import com.android.dx.Version;
+import com.android.dx.cf.iface.ParseException;
+import com.android.dx.cf.direct.ClassPathOpener;
+import com.android.dx.command.DxConsole;
+import com.android.dx.command.UsageException;
+import com.android.dx.dex.cf.CfOptions;
+import com.android.dx.dex.cf.CfTranslator;
+import com.android.dx.dex.cf.CodeStatistics;
+import com.android.dx.dex.code.PositionList;
+import com.android.dx.dex.file.ClassDefItem;
+import com.android.dx.dex.file.DexFile;
+import com.android.dx.dex.file.EncodedMethod;
+import com.android.dx.rop.annotation.Annotation;
+import com.android.dx.rop.annotation.Annotations;
+import com.android.dx.rop.annotation.AnnotationsList;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstUtf8;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+
+/**
+ * Main class for the class file translator.
+ */
+public class Main {
+ /**
+ * {@code non-null;} the lengthy message that tries to discourage
+ * people from defining core classes in applications
+ */
+ private static final String IN_RE_CORE_CLASSES =
+ "Ill-advised or mistaken usage of a core class (java.* or javax.*)\n" +
+ "when not building a core library.\n\n" +
+ "This is often due to inadvertently including a core library file\n" +
+ "in your application's project, when using an IDE (such as\n" +
+ "Eclipse). If you are sure you're not intentionally defining a\n" +
+ "core class, then this is the most likely explanation of what's\n" +
+ "going on.\n\n" +
+ "However, you might actually be trying to define a class in a core\n" +
+ "namespace, the source of which you may have taken, for example,\n" +
+ "from a non-Android virtual machine project. This will most\n" +
+ "assuredly not work. At a minimum, it jeopardizes the\n" +
+ "compatibility of your app with future versions of the platform.\n" +
+ "It is also often of questionable legality.\n\n" +
+ "If you really intend to build a core library -- which is only\n" +
+ "appropriate as part of creating a full virtual machine\n" +
+ "distribution, as opposed to compiling an application -- then use\n" +
+ "the \"--core-library\" option to suppress this error message.\n\n" +
+ "If you go ahead and use \"--core-library\" but are in fact\n" +
+ "building an application, then be forewarned that your application\n" +
+ "will still fail to build or run, at some point. Please be\n" +
+ "prepared for angry customers who find, for example, that your\n" +
+ "application ceases to function once they upgrade their operating\n" +
+ "system. You will be to blame for this problem.\n\n" +
+ "If you are legitimately using some code that happens to be in a\n" +
+ "core package, then the easiest safe alternative you have is to\n" +
+ "repackage that code. That is, move the classes in question into\n" +
+ "your own package namespace. This means that they will never be in\n" +
+ "conflict with core system classes. JarJar is a tool that may help\n" +
+ "you in this endeavor. If you find that you cannot do this, then\n" +
+ "that is an indication that the path you are on will ultimately\n" +
+ "lead to pain, suffering, grief, and lamentation.\n";
+
+ /**
+ * {@code non-null;} name for the {@code .dex} file that goes into
+ * {@code .jar} files
+ */
+ private static final String DEX_IN_JAR_NAME = "classes.dex";
+
+ /**
+ * {@code non-null;} name of the standard manifest file in {@code .jar}
+ * files
+ */
+ private static final String MANIFEST_NAME = "META-INF/MANIFEST.MF";
+
+ /**
+ * {@code non-null;} attribute name for the (quasi-standard?)
+ * {@code Created-By} attribute
+ */
+ private static final Attributes.Name CREATED_BY =
+ new Attributes.Name("Created-By");
+
+ /**
+ * {@code non-null;} list of {@code javax} subpackages that are considered
+ * to be "core". <b>Note:</b>: This list must be sorted, since it
+ * is binary-searched.
+ */
+ private static final String[] JAVAX_CORE = {
+ "accessibility", "crypto", "imageio", "management", "naming", "net",
+ "print", "rmi", "security", "sound", "sql", "swing", "transaction",
+ "xml"
+ };
+
+ /** number of warnings during processing */
+ private static int warnings = 0;
+
+ /** number of errors during processing */
+ private static int errors = 0;
+
+ /** {@code non-null;} parsed command-line arguments */
+ private static Arguments args;
+
+ /** {@code non-null;} output file in-progress */
+ private static DexFile outputDex;
+
+ /**
+ * {@code null-ok;} map of resources to include in the output, or
+ * {@code null} if resources are being ignored
+ */
+ private static TreeMap<String, byte[]> outputResources;
+
+ /**
+ * This class is uninstantiable.
+ */
+ private Main() {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Run and exit if something unexpected happened.
+ * @param argArray the command line arguments
+ */
+ public static void main(String[] argArray) {
+ Arguments arguments = new Arguments();
+ arguments.parse(argArray);
+
+ int result = run(arguments);
+ if (result != 0) {
+ System.exit(result);
+ }
+ }
+
+ /**
+ * Run and return a result code.
+ * @param arguments the data + parameters for the conversion
+ * @return 0 if success > 0 otherwise.
+ */
+ public static int run(Arguments arguments) {
+ // Reset the error/warning count to start fresh.
+ warnings = 0;
+ errors = 0;
+
+ args = arguments;
+ args.makeCfOptions();
+
+ if (!processAllFiles()) {
+ return 1;
+ }
+
+ byte[] outArray = writeDex();
+
+ if (outArray == null) {
+ return 2;
+ }
+
+ if (args.jarOutput) {
+ // Effectively free up the (often massive) DexFile memory.
+ outputDex = null;
+
+ if (!createJar(args.outName, outArray)) {
+ return 3;
+ }
+ }
+
+ return 0;
+ }
+
+ /**
+ * Constructs the output {@link DexFile}, fill it in with all the
+ * specified classes, and populate the resources map if required.
+ *
+ * @return whether processing was successful
+ */
+ private static boolean processAllFiles() {
+ outputDex = new DexFile();
+
+ if (args.jarOutput) {
+ outputResources = new TreeMap<String, byte[]>();
+ }
+
+ if (args.dumpWidth != 0) {
+ outputDex.setDumpWidth(args.dumpWidth);
+ }
+
+ boolean any = false;
+ String[] fileNames = args.fileNames;
+
+ try {
+ for (int i = 0; i < fileNames.length; i++) {
+ any |= processOne(fileNames[i]);
+ }
+ } catch (StopProcessing ex) {
+ /*
+ * Ignore it and just let the warning/error reporting do
+ * their things.
+ */
+ }
+
+ if (warnings != 0) {
+ DxConsole.err.println(warnings + " warning" +
+ ((warnings == 1) ? "" : "s"));
+ }
+
+ if (errors != 0) {
+ DxConsole.err.println(errors + " error" +
+ ((errors == 1) ? "" : "s") + "; aborting");
+ return false;
+ }
+
+ if (!(any || args.emptyOk)) {
+ DxConsole.err.println("no classfiles specified");
+ return false;
+ }
+
+ if (args.optimize && args.statistics) {
+ CodeStatistics.dumpStatistics(DxConsole.out);
+ }
+
+ return true;
+ }
+
+ /**
+ * Processes one pathname element.
+ *
+ * @param pathname {@code non-null;} the pathname to process. May
+ * be the path of a class file, a jar file, or a directory
+ * containing class files.
+ * @return whether any processing actually happened
+ */
+ private static boolean processOne(String pathname) {
+ ClassPathOpener opener;
+
+ opener = new ClassPathOpener(pathname, false,
+ new ClassPathOpener.Consumer() {
+ public boolean processFileBytes(String name, byte[] bytes) {
+ return Main.processFileBytes(name, bytes);
+ }
+ public void onException(Exception ex) {
+ if (ex instanceof StopProcessing) {
+ throw (StopProcessing) ex;
+ }
+ DxConsole.err.println("\nUNEXPECTED TOP-LEVEL EXCEPTION:");
+ ex.printStackTrace(DxConsole.err);
+ errors++;
+ }
+ public void onProcessArchiveStart(File file) {
+ if (args.verbose) {
+ DxConsole.out.println("processing archive " + file +
+ "...");
+ }
+ }
+ });
+
+ return opener.process();
+ }
+
+ /**
+ * Processes one file, which may be either a class or a resource.
+ *
+ * @param name {@code non-null;} name of the file
+ * @param bytes {@code non-null;} contents of the file
+ * @return whether processing was successful
+ */
+ private static boolean processFileBytes(String name, byte[] bytes) {
+ boolean isClass = name.endsWith(".class");
+ boolean keepResources = (outputResources != null);
+
+ if (!isClass && !keepResources) {
+ if (args.verbose) {
+ DxConsole.out.println("ignored resource " + name);
+ }
+ return false;
+ }
+
+ if (args.verbose) {
+ DxConsole.out.println("processing " + name + "...");
+ }
+
+ String fixedName = fixPath(name);
+
+ if (isClass) {
+ if (keepResources && args.keepClassesInJar) {
+ outputResources.put(fixedName, bytes);
+ }
+ return processClass(fixedName, bytes);
+ } else {
+ outputResources.put(fixedName, bytes);
+ return true;
+ }
+ }
+
+ /**
+ * Processes one classfile.
+ *
+ * @param name {@code non-null;} name of the file, clipped such that it
+ * <i>should</i> correspond to the name of the class it contains
+ * @param bytes {@code non-null;} contents of the file
+ * @return whether processing was successful
+ */
+ private static boolean processClass(String name, byte[] bytes) {
+ if (! args.coreLibrary) {
+ checkClassName(name);
+ }
+
+ try {
+ ClassDefItem clazz =
+ CfTranslator.translate(name, bytes, args.cfOptions);
+ outputDex.add(clazz);
+ return true;
+ } catch (ParseException ex) {
+ DxConsole.err.println("\ntrouble processing:");
+ if (args.debug) {
+ ex.printStackTrace(DxConsole.err);
+ } else {
+ ex.printContext(DxConsole.err);
+ }
+ }
+
+ warnings++;
+ return false;
+ }
+
+ /**
+ * Check the class name to make sure it's not a "core library"
+ * class. If there is a problem, this updates the error count and
+ * throws an exception to stop processing.
+ *
+ * @param name {@code non-null;} the fully-qualified internal-form
+ * class name
+ */
+ private static void checkClassName(String name) {
+ boolean bogus = false;
+
+ if (name.startsWith("java/")) {
+ bogus = true;
+ } else if (name.startsWith("javax/")) {
+ int slashAt = name.indexOf('/', 6);
+ if (slashAt == -1) {
+ // Top-level javax classes are verboten.
+ bogus = true;
+ } else {
+ String pkg = name.substring(6, slashAt);
+ bogus = (Arrays.binarySearch(JAVAX_CORE, pkg) >= 0);
+ }
+ }
+
+ if (! bogus) {
+ return;
+ }
+
+ /*
+ * The user is probably trying to include an entire desktop
+ * core library in a misguided attempt to get their application
+ * working. Try to help them understand what's happening.
+ */
+
+ DxConsole.err.println("\ntrouble processing \"" + name + "\":\n\n" +
+ IN_RE_CORE_CLASSES);
+ errors++;
+ throw new StopProcessing();
+ }
+
+ /**
+ * Converts {@link #outputDex} into a {@code byte[]}, write
+ * it out to the proper file (if any), and also do whatever human-oriented
+ * dumping is required.
+ *
+ * @return {@code null-ok;} the converted {@code byte[]} or {@code null}
+ * if there was a problem
+ */
+ private static byte[] writeDex() {
+ byte[] outArray = null;
+
+ try {
+ OutputStream out = null;
+ OutputStream humanOutRaw = null;
+ OutputStreamWriter humanOut = null;
+ try {
+ if (args.humanOutName != null) {
+ humanOutRaw = openOutput(args.humanOutName);
+ humanOut = new OutputStreamWriter(humanOutRaw);
+ }
+
+ if (args.methodToDump != null) {
+ /*
+ * Simply dump the requested method. Note: The call
+ * to toDex() is required just to get the underlying
+ * structures ready.
+ */
+ outputDex.toDex(null, false);
+ dumpMethod(outputDex, args.methodToDump, humanOut);
+ } else {
+ /*
+ * This is the usual case: Create an output .dex file,
+ * and write it, dump it, etc.
+ */
+ outArray = outputDex.toDex(humanOut, args.verboseDump);
+
+ if ((args.outName != null) && !args.jarOutput) {
+ out = openOutput(args.outName);
+ out.write(outArray);
+ }
+ }
+
+ if (args.statistics) {
+ DxConsole.out.println(outputDex.getStatistics().toHuman());
+ }
+ } finally {
+ if (humanOut != null) {
+ humanOut.flush();
+ }
+ closeOutput(out);
+ closeOutput(humanOutRaw);
+ }
+ } catch (Exception ex) {
+ if (args.debug) {
+ DxConsole.err.println("\ntrouble writing output:");
+ ex.printStackTrace(DxConsole.err);
+ } else {
+ DxConsole.err.println("\ntrouble writing output: " +
+ ex.getMessage());
+ }
+ return null;
+ }
+
+ return outArray;
+ }
+
+ /**
+ * Creates a jar file from the resources and given dex file array.
+ *
+ * @param fileName {@code non-null;} name of the file
+ * @param dexArray {@code non-null;} array containing the dex file
+ * to include
+ * @return whether the creation was successful
+ */
+ private static boolean createJar(String fileName, byte[] dexArray) {
+ /*
+ * Make or modify the manifest (as appropriate), put the dex
+ * array into the resources map, and then process the entire
+ * resources map in a uniform manner.
+ */
+
+ try {
+ Manifest manifest = makeManifest();
+ OutputStream out = openOutput(fileName);
+ JarOutputStream jarOut = new JarOutputStream(out, manifest);
+
+ outputResources.put(DEX_IN_JAR_NAME, dexArray);
+
+ try {
+ for (Map.Entry<String, byte[]> e :
+ outputResources.entrySet()) {
+ String name = e.getKey();
+ byte[] contents = e.getValue();
+ JarEntry entry = new JarEntry(name);
+
+ if (args.verbose) {
+ DxConsole.out.println("writing " + name + "; size " +
+ contents.length + "...");
+ }
+
+ entry.setSize(contents.length);
+ jarOut.putNextEntry(entry);
+ jarOut.write(contents);
+ jarOut.closeEntry();
+ }
+ } finally {
+ jarOut.finish();
+ jarOut.flush();
+ closeOutput(out);
+ }
+ } catch (Exception ex) {
+ if (args.debug) {
+ DxConsole.err.println("\ntrouble writing output:");
+ ex.printStackTrace(DxConsole.err);
+ } else {
+ DxConsole.err.println("\ntrouble writing output: " +
+ ex.getMessage());
+ }
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Creates and returns the manifest to use for the output. This may
+ * modify {@link #outputResources} (removing the pre-existing manifest).
+ *
+ * @return {@code non-null;} the manifest
+ */
+ private static Manifest makeManifest() throws IOException {
+ byte[] manifestBytes = outputResources.get(MANIFEST_NAME);
+ Manifest manifest;
+ Attributes attribs;
+
+ if (manifestBytes == null) {
+ // We need to construct an entirely new manifest.
+ manifest = new Manifest();
+ attribs = manifest.getMainAttributes();
+ attribs.put(Attributes.Name.MANIFEST_VERSION, "1.0");
+ } else {
+ manifest = new Manifest(new ByteArrayInputStream(manifestBytes));
+ attribs = manifest.getMainAttributes();
+ outputResources.remove(MANIFEST_NAME);
+ }
+
+ String createdBy = attribs.getValue(CREATED_BY);
+ if (createdBy == null) {
+ createdBy = "";
+ } else {
+ createdBy += " + ";
+ }
+ createdBy += "dx " + Version.VERSION;
+
+ attribs.put(CREATED_BY, createdBy);
+ attribs.putValue("Dex-Location", DEX_IN_JAR_NAME);
+
+ return manifest;
+ }
+
+ /**
+ * Opens and returns the named file for writing, treating "-" specially.
+ *
+ * @param name {@code non-null;} the file name
+ * @return {@code non-null;} the opened file
+ */
+ private static OutputStream openOutput(String name) throws IOException {
+ if (name.equals("-") ||
+ name.startsWith("-.")) {
+ return System.out;
+ }
+
+ return new FileOutputStream(name);
+ }
+
+ /**
+ * Flushes and closes the given output stream, except if it happens to be
+ * {@link System#out} in which case this method does the flush but not
+ * the close. This method will also silently do nothing if given a
+ * {@code null} argument.
+ *
+ * @param stream {@code null-ok;} what to close
+ */
+ private static void closeOutput(OutputStream stream) throws IOException {
+ if (stream == null) {
+ return;
+ }
+
+ stream.flush();
+
+ if (stream != System.out) {
+ stream.close();
+ }
+ }
+
+ /**
+ * Returns the "fixed" version of a given file path, suitable for
+ * use as a path within a {@code .jar} file and for checking
+ * against a classfile-internal "this class" name. This looks for
+ * the last instance of the substring {@code "/./"} within
+ * the path, and if it finds it, it takes the portion after to be
+ * the fixed path. If that isn't found but the path starts with
+ * {@code "./"}, then that prefix is removed and the rest is
+ * return. If neither of these is the case, this method returns
+ * its argument.
+ *
+ * @param path {@code non-null;} the path to "fix"
+ * @return {@code non-null;} the fixed version (which might be the same as
+ * the given {@code path})
+ */
+ private static String fixPath(String path) {
+ /*
+ * If the path separator is \ (like on windows), we convert the
+ * path to a standard '/' separated path.
+ */
+ if (File.separatorChar == '\\') {
+ path = path.replace('\\', '/');
+ }
+
+ int index = path.lastIndexOf("/./");
+
+ if (index != -1) {
+ return path.substring(index + 3);
+ }
+
+ if (path.startsWith("./")) {
+ return path.substring(2);
+ }
+
+ return path;
+ }
+
+ /**
+ * Dumps any method with the given name in the given file.
+ *
+ * @param dex {@code non-null;} the dex file
+ * @param fqName {@code non-null;} the fully-qualified name of the
+ * method(s)
+ * @param out {@code non-null;} where to dump to
+ */
+ private static void dumpMethod(DexFile dex, String fqName,
+ OutputStreamWriter out) {
+ boolean wildcard = fqName.endsWith("*");
+ int lastDot = fqName.lastIndexOf('.');
+
+ if ((lastDot <= 0) || (lastDot == (fqName.length() - 1))) {
+ DxConsole.err.println("bogus fully-qualified method name: " +
+ fqName);
+ return;
+ }
+
+ String className = fqName.substring(0, lastDot).replace('.', '/');
+ String methodName = fqName.substring(lastDot + 1);
+ ClassDefItem clazz = dex.getClassOrNull(className);
+
+ if (clazz == null) {
+ DxConsole.err.println("no such class: " + className);
+ return;
+ }
+
+ if (wildcard) {
+ methodName = methodName.substring(0, methodName.length() - 1);
+ }
+
+ ArrayList<EncodedMethod> allMeths = clazz.getMethods();
+ TreeMap<CstNat, EncodedMethod> meths =
+ new TreeMap<CstNat, EncodedMethod>();
+
+ /*
+ * Figure out which methods to include in the output, and get them
+ * all sorted, so that the printout code is robust with respect to
+ * changes in the underlying order.
+ */
+ for (EncodedMethod meth : allMeths) {
+ String methName = meth.getName().getString();
+ if ((wildcard && methName.startsWith(methodName)) ||
+ (!wildcard && methName.equals(methodName))) {
+ meths.put(meth.getRef().getNat(), meth);
+ }
+ }
+
+ if (meths.size() == 0) {
+ DxConsole.err.println("no such method: " + fqName);
+ return;
+ }
+
+ PrintWriter pw = new PrintWriter(out);
+
+ for (EncodedMethod meth : meths.values()) {
+ // TODO: Better stuff goes here, perhaps.
+ meth.debugPrint(pw, args.verboseDump);
+
+ /*
+ * The (default) source file is an attribute of the class, but
+ * it's useful to see it in method dumps.
+ */
+ CstUtf8 sourceFile = clazz.getSourceFile();
+ if (sourceFile != null) {
+ pw.println(" source file: " + sourceFile.toQuoted());
+ }
+
+ Annotations methodAnnotations =
+ clazz.getMethodAnnotations(meth.getRef());
+ AnnotationsList parameterAnnotations =
+ clazz.getParameterAnnotations(meth.getRef());
+
+ if (methodAnnotations != null) {
+ pw.println(" method annotations:");
+ for (Annotation a : methodAnnotations.getAnnotations()) {
+ pw.println(" " + a);
+ }
+ }
+
+ if (parameterAnnotations != null) {
+ pw.println(" parameter annotations:");
+ int sz = parameterAnnotations.size();
+ for (int i = 0; i < sz; i++) {
+ pw.println(" parameter " + i);
+ Annotations annotations = parameterAnnotations.get(i);
+ for (Annotation a : annotations.getAnnotations()) {
+ pw.println(" " + a);
+ }
+ }
+ }
+ }
+
+ pw.flush();
+ }
+
+ /**
+ * Exception class used to halt processing prematurely.
+ */
+ private static class StopProcessing extends RuntimeException {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Command-line argument parser and access.
+ */
+ public static class Arguments {
+ /** whether to run in debug mode */
+ public boolean debug = false;
+
+ /** whether to emit high-level verbose human-oriented output */
+ public boolean verbose = false;
+
+ /** whether to emit verbose human-oriented output in the dump file */
+ public boolean verboseDump = false;
+
+ /** whether we are constructing a core library */
+ public boolean coreLibrary = false;
+
+ /** {@code null-ok;} particular method to dump */
+ public String methodToDump = null;
+
+ /** max width for columnar output */
+ public int dumpWidth = 0;
+
+ /** {@code null-ok;} output file name for binary file */
+ public String outName = null;
+
+ /** {@code null-ok;} output file name for human-oriented dump */
+ public String humanOutName = null;
+
+ /** whether strict file-name-vs-class-name checking should be done */
+ public boolean strictNameCheck = true;
+
+ /**
+ * whether it is okay for there to be no {@code .class} files
+ * to process
+ */
+ public boolean emptyOk = false;
+
+ /**
+ * whether the binary output is to be a {@code .jar} file
+ * instead of a plain {@code .dex}
+ */
+ public boolean jarOutput = false;
+
+ /**
+ * when writing a {@code .jar} file, whether to still
+ * keep the {@code .class} files
+ */
+ public boolean keepClassesInJar = false;
+
+ /** how much source position info to preserve */
+ public int positionInfo = PositionList.LINES;
+
+ /** whether to keep local variable information */
+ public boolean localInfo = true;
+
+ /** {@code non-null after {@link #parse};} file name arguments */
+ public String[] fileNames;
+
+ /** whether to do SSA/register optimization */
+ public boolean optimize = true;
+
+ /** Filename containg list of methods to optimize */
+ public String optimizeListFile = null;
+
+ /** Filename containing list of methods to NOT optimize */
+ public String dontOptimizeListFile = null;
+
+ /** Whether to print statistics to stdout at end of compile cycle */
+ public boolean statistics;
+
+ /** Options for dex.cf.* */
+ public CfOptions cfOptions;
+
+ /**
+ * Parses the given command-line arguments.
+ *
+ * @param args {@code non-null;} the arguments
+ */
+ public void parse(String[] args) {
+ int at = 0;
+
+ for (/*at*/; at < args.length; at++) {
+ String arg = args[at];
+ if (arg.equals("--") || !arg.startsWith("--")) {
+ break;
+ } else if (arg.equals("--debug")) {
+ debug = true;
+ } else if (arg.equals("--verbose")) {
+ verbose = true;
+ } else if (arg.equals("--verbose-dump")) {
+ verboseDump = true;
+ } else if (arg.equals("--no-files")) {
+ emptyOk = true;
+ } else if (arg.equals("--no-optimize")) {
+ optimize = false;
+ } else if (arg.equals("--no-strict")) {
+ strictNameCheck = false;
+ } else if (arg.equals("--core-library")) {
+ coreLibrary = true;
+ } else if (arg.equals("--statistics")) {
+ statistics = true;
+ } else if (arg.startsWith("--optimize-list=")) {
+ if (dontOptimizeListFile != null) {
+ System.err.println("--optimize-list and "
+ + "--no-optimize-list are incompatible.");
+ throw new UsageException();
+ }
+ optimize = true;
+ optimizeListFile = arg.substring(arg.indexOf('=') + 1);
+ } else if (arg.startsWith("--no-optimize-list=")) {
+ if (dontOptimizeListFile != null) {
+ System.err.println("--optimize-list and "
+ + "--no-optimize-list are incompatible.");
+ throw new UsageException();
+ }
+ optimize = true;
+ dontOptimizeListFile = arg.substring(arg.indexOf('=') + 1);
+ } else if (arg.equals("--keep-classes")) {
+ keepClassesInJar = true;
+ } else if (arg.startsWith("--output=")) {
+ outName = arg.substring(arg.indexOf('=') + 1);
+ if (outName.endsWith(".zip") ||
+ outName.endsWith(".jar") ||
+ outName.endsWith(".apk")) {
+ jarOutput = true;
+ } else if (outName.endsWith(".dex") ||
+ outName.equals("-")) {
+ jarOutput = false;
+ } else {
+ System.err.println("unknown output extension: " +
+ outName);
+ throw new UsageException();
+ }
+ } else if (arg.startsWith("--dump-to=")) {
+ humanOutName = arg.substring(arg.indexOf('=') + 1);
+ } else if (arg.startsWith("--dump-width=")) {
+ arg = arg.substring(arg.indexOf('=') + 1);
+ dumpWidth = Integer.parseInt(arg);
+ } else if (arg.startsWith("--dump-method=")) {
+ methodToDump = arg.substring(arg.indexOf('=') + 1);
+ jarOutput = false;
+ } else if (arg.startsWith("--positions=")) {
+ String pstr = arg.substring(arg.indexOf('=') + 1).intern();
+ if (pstr == "none") {
+ positionInfo = PositionList.NONE;
+ } else if (pstr == "important") {
+ positionInfo = PositionList.IMPORTANT;
+ } else if (pstr == "lines") {
+ positionInfo = PositionList.LINES;
+ } else {
+ System.err.println("unknown positions option: " +
+ pstr);
+ throw new UsageException();
+ }
+ } else if (arg.equals("--no-locals")) {
+ localInfo = false;
+ } else {
+ System.err.println("unknown option: " + arg);
+ throw new UsageException();
+ }
+ }
+
+ int fileCount = args.length - at;
+
+ if (fileCount == 0) {
+ if (!emptyOk) {
+ System.err.println("no input files specified");
+ throw new UsageException();
+ }
+ } else if (emptyOk) {
+ System.out.println("ignoring input files");
+ at = 0;
+ fileCount = 0;
+ }
+
+ fileNames = new String[fileCount];
+ System.arraycopy(args, at, fileNames, 0, fileCount);
+
+ if ((humanOutName == null) && (methodToDump != null)) {
+ humanOutName = "-";
+ }
+
+ makeCfOptions();
+ }
+
+ /**
+ * Copies relevent arguments over into a CfOptions instance.
+ */
+ private void makeCfOptions() {
+ cfOptions = new CfOptions();
+
+ cfOptions.positionInfo = positionInfo;
+ cfOptions.localInfo = localInfo;
+ cfOptions.strictNameCheck = strictNameCheck;
+ cfOptions.optimize = optimize;
+ cfOptions.optimizeListFile = optimizeListFile;
+ cfOptions.dontOptimizeListFile = dontOptimizeListFile;
+ cfOptions.statistics = statistics;
+ cfOptions.warn = DxConsole.err;
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/command/dump/Args.java b/dx/src/com/android/dx/command/dump/Args.java
new file mode 100644
index 0000000..042fae2
--- /dev/null
+++ b/dx/src/com/android/dx/command/dump/Args.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.command.dump;
+
+/**
+ * contains command line parsedArgs values
+ */
+class Args {
+ /** whether to run in debug mode */
+ boolean debug = false;
+
+ /** whether to dump raw bytes where salient */
+ boolean rawBytes = false;
+
+ /** whether to dump information about basic blocks */
+ boolean basicBlocks = false;
+
+ /** whether to dump regiserized blocks */
+ boolean ropBlocks = false;
+
+ /** whether to dump SSA-form blocks */
+ boolean ssaBlocks = false;
+
+ /** Step in SSA processing to stop at, or null for all */
+ String ssaStep = null;
+
+ /** whether to run SSA optimizations */
+ boolean optimize = false;
+
+ /** whether to be strict about parsing classfiles*/
+ boolean strictParse = false;
+
+ /** max width for columnar output */
+ int width = 0;
+
+ /** whether to dump flow-graph in "dot" format */
+ boolean dotDump = false;
+
+ /** if non-null, an explicit method to dump */
+ String method;
+
+}
diff --git a/dx/src/com/android/dx/command/dump/BaseDumper.java b/dx/src/com/android/dx/command/dump/BaseDumper.java
new file mode 100644
index 0000000..ad6540b
--- /dev/null
+++ b/dx/src/com/android/dx/command/dump/BaseDumper.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.command.dump;
+
+import com.android.dx.cf.code.ConcreteMethod;
+import com.android.dx.cf.iface.Member;
+import com.android.dx.cf.iface.ParseObserver;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IndentingWriter;
+import com.android.dx.util.TwoColumnOutput;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.StringWriter;
+
+/**
+ * Base class for the various human-friendly dumpers.
+ */
+public abstract class BaseDumper
+ implements ParseObserver {
+ /** {@code non-null;} array of data being dumped */
+ private final byte[] bytes;
+
+ /** whether or not to include the raw bytes (in a column on the left) */
+ private final boolean rawBytes;
+
+ /** {@code non-null;} where to dump to */
+ private final PrintStream out;
+
+ /** width of the output in columns */
+ private final int width;
+
+ /**
+ * {@code non-null;} the file path for the class, excluding any base
+ * directory specification
+ */
+ private final String filePath;
+
+ /** whether to be strict about parsing */
+ private final boolean strictParse;
+
+ /** number of bytes per line in hex dumps */
+ private final int hexCols;
+
+ /** the current level of indentation */
+ private int indent;
+
+ /** {@code non-null;} the current column separator string */
+ private String separator;
+
+ /** the offset of the next byte to dump */
+ private int at;
+
+ /** commandline parsedArgs */
+ protected Args args;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param bytes {@code non-null;} bytes of the (alleged) class file
+ * on the left)
+ * @param out {@code non-null;} where to dump to
+ * @param filePath the file path for the class, excluding any base
+ * directory specification
+ */
+ public BaseDumper(byte[] bytes, PrintStream out,
+ String filePath, Args args) {
+ this.bytes = bytes;
+ this.rawBytes = args.rawBytes;
+ this.out = out;
+ this.width = (args.width <= 0) ? 79 : args.width;
+ this.filePath = filePath;
+ this.strictParse = args.strictParse;
+ this.indent = 0;
+ this.separator = rawBytes ? "|" : "";
+ this.at = 0;
+ this.args = args;
+
+ int hexCols = (((width - 5) / 15) + 1) & ~1;
+ if (hexCols < 6) {
+ hexCols = 6;
+ } else if (hexCols > 10) {
+ hexCols = 10;
+ }
+ this.hexCols = hexCols;
+ }
+
+ /**
+ * Computes the total width, in register-units, of the parameters for
+ * this method.
+ * @param meth method to process
+ * @return width in register-units
+ */
+ static int computeParamWidth(ConcreteMethod meth, boolean isStatic) {
+ return meth.getEffectiveDescriptor().getParameterTypes().
+ getWordCount();
+ }
+
+ /** {@inheritDoc} */
+ public void changeIndent(int indentDelta) {
+ indent += indentDelta;
+
+ separator = rawBytes ? "|" : "";
+ for (int i = 0; i < indent; i++) {
+ separator += " ";
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void parsed(ByteArray bytes, int offset, int len, String human) {
+ offset = bytes.underlyingOffset(offset, getBytes());
+
+ boolean rawBytes = getRawBytes();
+
+ if (offset < at) {
+ println("<dump skipped backwards to " + Hex.u4(offset) + ">");
+ at = offset;
+ } else if (offset > at) {
+ String hex = rawBytes ? hexDump(at, offset - at) : "";
+ print(twoColumns(hex, "<skipped to " + Hex.u4(offset) + ">"));
+ at = offset;
+ }
+
+ String hex = rawBytes ? hexDump(offset, len) : "";
+ print(twoColumns(hex, human));
+ at += len;
+ }
+
+ /** {@inheritDoc} */
+ public void startParsingMember(ByteArray bytes, int offset, String name,
+ String descriptor) {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ public void endParsingMember(ByteArray bytes, int offset, String name,
+ String descriptor, Member member) {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Gets the current dump cursor (that is, the offset of the expected
+ * next byte to dump).
+ *
+ * @return {@code >= 0;} the dump cursor
+ */
+ protected final int getAt() {
+ return at;
+ }
+
+ /**
+ * Sets the dump cursor to the indicated offset in the given array.
+ *
+ * @param arr {@code non-null;} array in question
+ * @param offset {@code >= 0;} offset into the array
+ */
+ protected final void setAt(ByteArray arr, int offset) {
+ at = arr.underlyingOffset(offset, bytes);
+ }
+
+ /**
+ * Gets the array of {@code byte}s to process.
+ *
+ * @return {@code non-null;} the bytes
+ */
+ protected final byte[] getBytes() {
+ return bytes;
+ }
+
+ /**
+ * Gets the filesystem/jar path of the file being dumped.
+ *
+ * @return {@code non-null;} the path
+ */
+ protected final String getFilePath() {
+ return filePath;
+ }
+
+ /**
+ * Gets whether to be strict about parsing.
+ *
+ * @return whether to be strict about parsing
+ */
+ protected final boolean getStrictParse() {
+ return strictParse;
+ }
+
+ /**
+ * Prints the given string to this instance's output stream.
+ *
+ * @param s {@code null-ok;} string to print
+ */
+ protected final void print(String s) {
+ out.print(s);
+ }
+
+ /**
+ * Prints the given string to this instance's output stream, followed
+ * by a newline.
+ *
+ * @param s {@code null-ok;} string to print
+ */
+ protected final void println(String s) {
+ out.println(s);
+ }
+
+ /**
+ * Gets whether this dump is to include raw bytes.
+ *
+ * @return the raw bytes flag
+ */
+ protected final boolean getRawBytes() {
+ return rawBytes;
+ }
+
+ /**
+ * Gets the width of the first column of output. This is {@code 0}
+ * unless raw bytes are being included in the output.
+ *
+ * @return {@code >= 0;} the width of the first column
+ */
+ protected final int getWidth1() {
+ if (rawBytes) {
+ return 5 + (hexCols * 2) + (hexCols / 2);
+ }
+
+ return 0;
+ }
+
+ /**
+ * Gets the width of the second column of output.
+ *
+ * @return {@code >= 0;} the width of the second column
+ */
+ protected final int getWidth2() {
+ int w1 = rawBytes ? (getWidth1() + 1) : 0;
+ return width - w1 - (indent * 2);
+ }
+
+ /**
+ * Constructs a hex data dump of the given portion of {@link #bytes}.
+ *
+ * @param offset offset to start dumping at
+ * @param len length to dump
+ * @return {@code non-null;} the dump
+ */
+ protected final String hexDump(int offset, int len) {
+ return Hex.dump(bytes, offset, len, offset, hexCols, 4);
+ }
+
+ /**
+ * Combines a pair of strings as two columns, or if this is one-column
+ * output, format the otherwise-second column.
+ *
+ * @param s1 {@code non-null;} the first column's string
+ * @param s2 {@code non-null;} the second column's string
+ * @return {@code non-null;} the combined output
+ */
+ protected final String twoColumns(String s1, String s2) {
+ int w1 = getWidth1();
+ int w2 = getWidth2();
+
+ try {
+ if (w1 == 0) {
+ int len2 = s2.length();
+ StringWriter sw = new StringWriter(len2 * 2);
+ IndentingWriter iw = new IndentingWriter(sw, w2, separator);
+
+ iw.write(s2);
+ if ((len2 == 0) || (s2.charAt(len2 - 1) != '\n')) {
+ iw.write('\n');
+ }
+ iw.flush();
+
+ return sw.toString();
+ } else {
+ return TwoColumnOutput.toString(s1, w1, separator, s2, w2);
+ }
+ } catch (IOException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/command/dump/BlockDumper.java b/dx/src/com/android/dx/command/dump/BlockDumper.java
new file mode 100644
index 0000000..7a11888
--- /dev/null
+++ b/dx/src/com/android/dx/command/dump/BlockDumper.java
@@ -0,0 +1,355 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.command.dump;
+
+import com.android.dx.cf.attrib.AttCode;
+import com.android.dx.cf.code.BasicBlocker;
+import com.android.dx.cf.code.ByteBlock;
+import com.android.dx.cf.code.ByteBlockList;
+import com.android.dx.cf.code.ByteCatchList;
+import com.android.dx.cf.code.BytecodeArray;
+import com.android.dx.cf.code.ConcreteMethod;
+import com.android.dx.cf.code.Ropper;
+import com.android.dx.cf.direct.CodeObserver;
+import com.android.dx.cf.direct.DirectClassFile;
+import com.android.dx.cf.direct.StdAttributeFactory;
+import com.android.dx.cf.iface.Member;
+import com.android.dx.cf.iface.Method;
+import com.android.dx.rop.code.BasicBlock;
+import com.android.dx.rop.code.BasicBlockList;
+import com.android.dx.rop.code.Insn;
+import com.android.dx.rop.code.InsnList;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.code.DexTranslationAdvice;
+import com.android.dx.rop.code.TranslationAdvice;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.ssa.Optimizer;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IntList;
+
+import java.io.PrintStream;
+
+/**
+ * Utility to dump basic block info from methods in a human-friendly form.
+ */
+public class BlockDumper
+ extends BaseDumper {
+ /** whether or not to registerize (make rop blocks) */
+ private boolean rop;
+
+ /**
+ * {@code null-ok;} the class file object being constructed;
+ * becomes non-null during {@link #dump}
+ */
+ protected DirectClassFile classFile;
+
+ /** whether or not to suppress dumping */
+ protected boolean suppressDump;
+
+ /** whether this is the first method being dumped */
+ private boolean first;
+
+ /** whether or not to run the ssa optimziations */
+ private boolean optimize;
+
+ /**
+ * Dumps the given array, interpreting it as a class file and dumping
+ * methods with indications of block-level stuff.
+ *
+ * @param bytes {@code non-null;} bytes of the (alleged) class file
+ * @param out {@code non-null;} where to dump to
+ * @param filePath the file path for the class, excluding any base
+ * directory specification
+ * @param rop whether or not to registerize (make rop blocks)
+ * @param args commandline parsedArgs
+ */
+ public static void dump(byte[] bytes, PrintStream out,
+ String filePath, boolean rop, Args args) {
+ BlockDumper bd =
+ new BlockDumper(bytes, out, filePath,
+ rop, args);
+ bd.dump();
+ }
+
+ /**
+ * Constructs an instance. This class is not publicly instantiable.
+ * Use {@link #dump}.
+ */
+ BlockDumper(byte[] bytes, PrintStream out,
+ String filePath,
+ boolean rop, Args args) {
+ super(bytes, out, filePath, args);
+
+ this.rop = rop;
+ this.classFile = null;
+ this.suppressDump = true;
+ this.first = true;
+ this.optimize = args.optimize;
+ }
+
+ /**
+ * Does the dumping.
+ */
+ public void dump() {
+ byte[] bytes = getBytes();
+ ByteArray ba = new ByteArray(bytes);
+
+ /*
+ * First, parse the file completely, so we can safely refer to
+ * attributes, etc.
+ */
+ classFile = new DirectClassFile(ba, getFilePath(), getStrictParse());
+ classFile.setAttributeFactory(StdAttributeFactory.THE_ONE);
+ classFile.getMagic(); // Force parsing to happen.
+
+ // Next, reparse it and observe the process.
+ DirectClassFile liveCf =
+ new DirectClassFile(ba, getFilePath(), getStrictParse());
+ liveCf.setAttributeFactory(StdAttributeFactory.THE_ONE);
+ liveCf.setObserver(this);
+ liveCf.getMagic(); // Force parsing to happen.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void changeIndent(int indentDelta) {
+ if (!suppressDump) {
+ super.changeIndent(indentDelta);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void parsed(ByteArray bytes, int offset, int len, String human) {
+ if (!suppressDump) {
+ super.parsed(bytes, offset, len, human);
+ }
+ }
+
+ /**
+ * @param name method name
+ * @return true if this method should be dumped
+ */
+ protected boolean shouldDumpMethod(String name) {
+ return args.method == null || args.method.equals(name);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void startParsingMember(ByteArray bytes, int offset, String name,
+ String descriptor) {
+ if (descriptor.indexOf('(') < 0) {
+ // It's a field, not a method
+ return;
+ }
+
+ if (!shouldDumpMethod(name)) {
+ return;
+ }
+
+ // Reset the dump cursor to the start of the method.
+ setAt(bytes, offset);
+
+ suppressDump = false;
+
+ if (first) {
+ first = false;
+ } else {
+ parsed(bytes, offset, 0, "\n");
+ }
+
+ parsed(bytes, offset, 0, "method " + name + " " + descriptor);
+ suppressDump = true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void endParsingMember(ByteArray bytes, int offset, String name,
+ String descriptor, Member member) {
+ if (!(member instanceof Method)) {
+ return;
+ }
+
+ if (!shouldDumpMethod(name)) {
+ return;
+ }
+
+ ConcreteMethod meth = new ConcreteMethod((Method) member, classFile,
+ true, true);
+
+ if (rop) {
+ ropDump(meth);
+ } else {
+ regularDump(meth);
+ }
+ }
+
+ /**
+ * Does a regular basic block dump.
+ *
+ * @param meth {@code non-null;} method data to dump
+ */
+ private void regularDump(ConcreteMethod meth) {
+ BytecodeArray code = meth.getCode();
+ ByteArray bytes = code.getBytes();
+ ByteBlockList list = BasicBlocker.identifyBlocks(meth);
+ int sz = list.size();
+ CodeObserver codeObserver = new CodeObserver(bytes, BlockDumper.this);
+
+ // Reset the dump cursor to the start of the bytecode
+ setAt(bytes, 0);
+
+ suppressDump = false;
+
+ int byteAt = 0;
+ for (int i = 0; i < sz; i++) {
+ ByteBlock bb = list.get(i);
+ int start = bb.getStart();
+ int end = bb.getEnd();
+
+ if (byteAt < start) {
+ parsed(bytes, byteAt, start - byteAt,
+ "dead code " + Hex.u2(byteAt) + ".." + Hex.u2(start));
+ }
+
+ parsed(bytes, start, 0,
+ "block " + Hex.u2(bb.getLabel()) + ": " +
+ Hex.u2(start) + ".." + Hex.u2(end));
+ changeIndent(1);
+
+ int len;
+ for (int j = start; j < end; j += len) {
+ len = code.parseInstruction(j, codeObserver);
+ codeObserver.setPreviousOffset(j);
+ }
+
+ IntList successors = bb.getSuccessors();
+ int ssz = successors.size();
+ if (ssz == 0) {
+ parsed(bytes, end, 0, "returns");
+ } else {
+ for (int j = 0; j < ssz; j++) {
+ int succ = successors.get(j);
+ parsed(bytes, end, 0, "next " + Hex.u2(succ));
+ }
+ }
+
+ ByteCatchList catches = bb.getCatches();
+ int csz = catches.size();
+ for (int j = 0; j < csz; j++) {
+ ByteCatchList.Item one = catches.get(j);
+ CstType exceptionClass = one.getExceptionClass();
+ parsed(bytes, end, 0,
+ "catch " +
+ ((exceptionClass == CstType.OBJECT) ? "<any>" :
+ exceptionClass.toHuman()) + " -> " +
+ Hex.u2(one.getHandlerPc()));
+ }
+
+ changeIndent(-1);
+ byteAt = end;
+ }
+
+ int end = bytes.size();
+ if (byteAt < end) {
+ parsed(bytes, byteAt, end - byteAt,
+ "dead code " + Hex.u2(byteAt) + ".." + Hex.u2(end));
+ }
+
+ suppressDump = true;
+ }
+
+ /**
+ * Does a registerizing dump.
+ *
+ * @param meth {@code non-null;} method data to dump
+ */
+ private void ropDump(ConcreteMethod meth) {
+ BytecodeArray code = meth.getCode();
+ ByteArray bytes = code.getBytes();
+
+ TranslationAdvice advice = DexTranslationAdvice.THE_ONE;
+
+ RopMethod rmeth =
+ Ropper.convert(meth, advice);
+ StringBuffer sb = new StringBuffer(2000);
+
+ if (optimize) {
+ boolean isStatic = AccessFlags.isStatic(meth.getAccessFlags());
+
+ int paramWidth = computeParamWidth(meth, isStatic);
+ rmeth = Optimizer.optimize(rmeth, paramWidth, isStatic, true,
+ advice);
+ }
+
+ BasicBlockList blocks = rmeth.getBlocks();
+
+ sb.append("first " + Hex.u2(rmeth.getFirstLabel()) + "\n");
+
+ int sz = blocks.size();
+ for (int i = 0; i < sz; i++) {
+ BasicBlock bb = blocks.get(i);
+ int label = bb.getLabel();
+ sb.append("block ");
+ sb.append(Hex.u2(label));
+ sb.append("\n");
+
+ IntList preds = rmeth.labelToPredecessors(label);
+ int psz = preds.size();
+ for (int j = 0; j < psz; j++) {
+ sb.append(" pred ");
+ sb.append(Hex.u2(preds.get(j)));
+ sb.append("\n");
+ }
+
+ InsnList il = bb.getInsns();
+ int ilsz = il.size();
+ for (int j = 0; j < ilsz; j++) {
+ Insn one = il.get(j);
+ sb.append(" ");
+ sb.append(il.get(j).toHuman());
+ sb.append("\n");
+ }
+
+ IntList successors = bb.getSuccessors();
+ int ssz = successors.size();
+ if (ssz == 0) {
+ sb.append(" returns\n");
+ } else {
+ int primary = bb.getPrimarySuccessor();
+ for (int j = 0; j < ssz; j++) {
+ int succ = successors.get(j);
+ sb.append(" next ");
+ sb.append(Hex.u2(succ));
+
+ if ((ssz != 1) && (succ == primary)) {
+ sb.append(" *");
+ }
+
+ sb.append("\n");
+ }
+ }
+ }
+
+ suppressDump = false;
+ setAt(bytes, 0);
+ parsed(bytes, 0, bytes.size(), sb.toString());
+ suppressDump = true;
+ }
+}
diff --git a/dx/src/com/android/dx/command/dump/ClassDumper.java b/dx/src/com/android/dx/command/dump/ClassDumper.java
new file mode 100644
index 0000000..e98b6d6
--- /dev/null
+++ b/dx/src/com/android/dx/command/dump/ClassDumper.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.command.dump;
+
+import com.android.dx.cf.direct.DirectClassFile;
+import com.android.dx.cf.direct.StdAttributeFactory;
+import com.android.dx.util.ByteArray;
+
+import java.io.PrintStream;
+
+/**
+ * Utility to dump the contents of class files in a human-friendly form.
+ */
+public final class ClassDumper
+ extends BaseDumper {
+ /**
+ * Dumps the given array, interpreting it as a class file.
+ *
+ * @param bytes {@code non-null;} bytes of the (alleged) class file
+ * @param out {@code non-null;} where to dump to
+ * passed in as &lt;= 0
+ * @param filePath the file path for the class, excluding any base
+ * directory specification
+ * @param args bag of commandline arguments
+ */
+ public static void dump(byte[] bytes, PrintStream out,
+ String filePath, Args args) {
+ ClassDumper cd =
+ new ClassDumper(bytes, out, filePath, args);
+ cd.dump();
+ }
+
+ /**
+ * Constructs an instance. This class is not publicly instantiable.
+ * Use {@link #dump}.
+ */
+ private ClassDumper(byte[] bytes, PrintStream out,
+ String filePath, Args args) {
+ super(bytes, out, filePath, args);
+ }
+
+ /**
+ * Does the dumping.
+ */
+ public void dump() {
+ byte[] bytes = getBytes();
+ ByteArray ba = new ByteArray(bytes);
+ DirectClassFile cf =
+ new DirectClassFile(ba, getFilePath(), getStrictParse());
+
+ cf.setAttributeFactory(StdAttributeFactory.THE_ONE);
+ cf.setObserver(this);
+ cf.getMagic(); // Force parsing to happen.
+
+ int at = getAt();
+ if (at != bytes.length) {
+ parsed(ba, at, bytes.length - at, "<extra data at end of file>");
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/command/dump/DotDumper.java b/dx/src/com/android/dx/command/dump/DotDumper.java
new file mode 100644
index 0000000..9de48fc
--- /dev/null
+++ b/dx/src/com/android/dx/command/dump/DotDumper.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.command.dump;
+
+import com.android.dx.cf.code.ConcreteMethod;
+import com.android.dx.cf.code.Ropper;
+import com.android.dx.cf.direct.DirectClassFile;
+import com.android.dx.cf.direct.StdAttributeFactory;
+import com.android.dx.cf.iface.Member;
+import com.android.dx.cf.iface.Method;
+import com.android.dx.cf.iface.ParseObserver;
+import com.android.dx.rop.code.BasicBlock;
+import com.android.dx.rop.code.BasicBlockList;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.code.DexTranslationAdvice;
+import com.android.dx.rop.code.TranslationAdvice;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.ssa.Optimizer;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IntList;
+
+/**
+ * Dumps the pred/succ graph of methods into a format compatible
+ * with the popular graph utility "dot".
+ */
+public class DotDumper implements ParseObserver {
+ private DirectClassFile classFile;
+
+ private final byte[] bytes;
+ private final String filePath;
+ private final boolean strictParse;
+ private final boolean optimize;
+ private final Args args;
+
+ static void dump(byte[] bytes, String filePath, Args args) {
+ new DotDumper(bytes, filePath, args).run();
+ }
+
+ DotDumper(byte[] bytes, String filePath, Args args) {
+ this.bytes = bytes;
+ this.filePath = filePath;
+ this.strictParse = args.strictParse;
+ this.optimize = args.optimize;
+ this.args = args;
+ }
+
+ private void run() {
+ ByteArray ba = new ByteArray(bytes);
+
+ /*
+ * First, parse the file completely, so we can safely refer to
+ * attributes, etc.
+ */
+ classFile = new DirectClassFile(ba, filePath, strictParse);
+ classFile.setAttributeFactory(StdAttributeFactory.THE_ONE);
+ classFile.getMagic(); // Force parsing to happen.
+
+ // Next, reparse it and observe the process.
+ DirectClassFile liveCf =
+ new DirectClassFile(ba, filePath, strictParse);
+ liveCf.setAttributeFactory(StdAttributeFactory.THE_ONE);
+ liveCf.setObserver(this);
+ liveCf.getMagic(); // Force parsing to happen.
+ }
+
+ /**
+ * @param name method name
+ * @return true if this method should be dumped
+ */
+ protected boolean shouldDumpMethod(String name) {
+ return args.method == null || args.method.equals(name);
+ }
+
+ public void changeIndent(int indentDelta) {
+ // This space intentionally left blank.
+ }
+
+ public void parsed(ByteArray bytes, int offset, int len, String human) {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ public void startParsingMember(ByteArray bytes, int offset, String name,
+ String descriptor) {
+ // This space intentionally left blank.
+ }
+
+ public void endParsingMember(ByteArray bytes, int offset, String name,
+ String descriptor, Member member) {
+ if (!(member instanceof Method)) {
+ return;
+ }
+
+ if (!shouldDumpMethod(name)) {
+ return;
+ }
+
+ ConcreteMethod meth = new ConcreteMethod((Method) member, classFile,
+ true, true);
+
+ TranslationAdvice advice = DexTranslationAdvice.THE_ONE;
+ RopMethod rmeth =
+ Ropper.convert(meth, advice);
+
+ if (optimize) {
+ boolean isStatic = AccessFlags.isStatic(meth.getAccessFlags());
+ rmeth = Optimizer.optimize(rmeth,
+ BaseDumper.computeParamWidth(meth, isStatic), isStatic,
+ true, advice);
+ }
+
+ System.out.println("digraph " + name + "{");
+
+ System.out.println("\tfirst -> n"
+ + Hex.u2(rmeth.getFirstLabel()) + ";");
+
+ BasicBlockList blocks = rmeth.getBlocks();
+
+ int sz = blocks.size();
+ for (int i = 0; i < sz; i++) {
+ BasicBlock bb = blocks.get(i);
+ int label = bb.getLabel();
+ IntList successors = bb.getSuccessors();
+
+ if (successors.size() == 0) {
+ System.out.println("\tn" + Hex.u2(label) + " -> returns;");
+ } else if (successors.size() == 1) {
+ System.out.println("\tn" + Hex.u2(label) + " -> n"
+ + Hex.u2(successors.get(0)) + ";");
+ } else {
+ System.out.print("\tn" + Hex.u2(label) + " -> {");
+ for (int j = 0; j < successors.size(); j++ ) {
+ int successor = successors.get(j);
+
+ if (successor != bb.getPrimarySuccessor()) {
+ System.out.print(" n" + Hex.u2(successor) + " ");
+ }
+
+ }
+ System.out.println("};");
+
+ System.out.println("\tn" + Hex.u2(label) + " -> n"
+ + Hex.u2(bb.getPrimarySuccessor())
+ + " [label=\"primary\"];");
+
+
+ }
+ }
+
+ System.out.println("}");
+ }
+}
diff --git a/dx/src/com/android/dx/command/dump/Main.java b/dx/src/com/android/dx/command/dump/Main.java
new file mode 100644
index 0000000..d6ba374
--- /dev/null
+++ b/dx/src/com/android/dx/command/dump/Main.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.command.dump;
+
+import com.android.dx.cf.iface.ParseException;
+import com.android.dx.util.FileUtils;
+import com.android.dx.util.HexParser;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Main class for the class file dumper.
+ */
+public class Main {
+
+ static Args parsedArgs = new Args();
+
+ /**
+ * This class is uninstantiable.
+ */
+ private Main() {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Run!
+ */
+ public static void main(String[] args) {
+ int at = 0;
+
+ for (/*at*/; at < args.length; at++) {
+ String arg = args[at];
+ if (arg.equals("--") || !arg.startsWith("--")) {
+ break;
+ } else if (arg.equals("--bytes")) {
+ parsedArgs.rawBytes = true;
+ } else if (arg.equals("--basic-blocks")) {
+ parsedArgs.basicBlocks = true;
+ } else if (arg.equals("--rop-blocks")) {
+ parsedArgs.ropBlocks = true;
+ } else if (arg.equals("--optimize")) {
+ parsedArgs.optimize = true;
+ } else if (arg.equals("--ssa-blocks")) {
+ parsedArgs.ssaBlocks = true;
+ } else if (arg.startsWith("--ssa-step=")) {
+ parsedArgs.ssaStep = arg.substring(arg.indexOf('=') + 1);
+ } else if (arg.equals("--debug")) {
+ parsedArgs.debug = true;
+ } else if (arg.equals("--dot")) {
+ parsedArgs.dotDump = true;
+ } else if (arg.equals("--strict")) {
+ parsedArgs.strictParse = true;
+ } else if (arg.startsWith("--width=")) {
+ arg = arg.substring(arg.indexOf('=') + 1);
+ parsedArgs.width = Integer.parseInt(arg);
+ } else if (arg.startsWith("--method=")) {
+ arg = arg.substring(arg.indexOf('=') + 1);
+ parsedArgs.method = arg;
+ } else {
+ System.err.println("unknown option: " + arg);
+ throw new RuntimeException("usage");
+ }
+ }
+
+ if (at == args.length) {
+ System.err.println("no input files specified");
+ throw new RuntimeException("usage");
+ }
+
+ for (/*at*/; at < args.length; at++) {
+ try {
+ String name = args[at];
+ System.out.println("reading " + name + "...");
+ byte[] bytes = FileUtils.readFile(name);
+ if (!name.endsWith(".class")) {
+ String src;
+ try {
+ src = new String(bytes, "utf-8");
+ } catch (UnsupportedEncodingException ex) {
+ throw new RuntimeException("shouldn't happen", ex);
+ }
+ bytes = HexParser.parse(src);
+ }
+ processOne(name, bytes);
+ } catch (ParseException ex) {
+ System.err.println("\ntrouble parsing:");
+ if (parsedArgs.debug) {
+ ex.printStackTrace();
+ } else {
+ ex.printContext(System.err);
+ }
+ }
+ }
+ }
+
+ /**
+ * Processes one file.
+ *
+ * @param name {@code non-null;} name of the file
+ * @param bytes {@code non-null;} contents of the file
+ */
+ private static void processOne(String name, byte[] bytes) {
+ if (parsedArgs.dotDump) {
+ DotDumper.dump(bytes, name, parsedArgs);
+ } else if (parsedArgs.basicBlocks) {
+ BlockDumper.dump(bytes, System.out, name, false, parsedArgs);
+ } else if (parsedArgs.ropBlocks) {
+ BlockDumper.dump(bytes, System.out, name, true, parsedArgs);
+ } else if (parsedArgs.ssaBlocks) {
+ // --optimize ignored with --ssa-blocks
+ parsedArgs.optimize = false;
+ SsaDumper.dump(bytes, System.out, name, parsedArgs);
+ } else {
+ ClassDumper.dump(bytes, System.out, name, parsedArgs);
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/command/dump/SsaDumper.java b/dx/src/com/android/dx/command/dump/SsaDumper.java
new file mode 100644
index 0000000..de44c83
--- /dev/null
+++ b/dx/src/com/android/dx/command/dump/SsaDumper.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.command.dump;
+
+import com.android.dx.cf.code.ConcreteMethod;
+import com.android.dx.cf.code.Ropper;
+import com.android.dx.cf.iface.Member;
+import com.android.dx.cf.iface.Method;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.code.TranslationAdvice;
+import com.android.dx.rop.code.DexTranslationAdvice;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.ssa.DeadCodeRemover;
+import com.android.dx.ssa.PhiTypeResolver;
+import com.android.dx.ssa.SsaBasicBlock;
+import com.android.dx.ssa.SsaConverter;
+import com.android.dx.ssa.SsaInsn;
+import com.android.dx.ssa.SsaMethod;
+import com.android.dx.ssa.Optimizer;
+import com.android.dx.ssa.ConstCollector;
+import com.android.dx.ssa.SCCP;
+import com.android.dx.ssa.LiteralOpUpgrader;
+import com.android.dx.ssa.back.SsaToRop;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IntList;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Collections;
+import java.util.EnumSet;
+
+/**
+ * Dumper for the SSA-translated blocks of a method.
+ */
+public class SsaDumper extends BlockDumper {
+ /**
+ * Does the dump.
+ *
+ * @param bytes {@code non-null;} bytes of the original class file
+ * @param out {@code non-null;} where to dump to
+ * @param filePath the file path for the class, excluding any base
+ * directory specification
+ * @param args commandline parsedArgs
+ */
+ public static void dump(byte[] bytes, PrintStream out,
+ String filePath, Args args) {
+ SsaDumper sd = new SsaDumper(bytes, out, filePath, args);
+ sd.dump();
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param bytes {@code non-null;} bytes of the original class file
+ * @param out {@code non-null;} where to dump to
+ * @param filePath the file path for the class, excluding any base
+ * directory specification
+ * @param args commandline parsedArgs
+ */
+ private SsaDumper(byte[] bytes, PrintStream out, String filePath,
+ Args args) {
+ super(bytes, out, filePath, true, args);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void endParsingMember(ByteArray bytes, int offset, String name,
+ String descriptor, Member member) {
+ if (!(member instanceof Method)) {
+ return;
+ }
+
+ if (!shouldDumpMethod(name)) {
+ return;
+ }
+
+ ConcreteMethod meth =
+ new ConcreteMethod((Method) member, classFile, true, true);
+ TranslationAdvice advice = DexTranslationAdvice.THE_ONE;
+ RopMethod rmeth = Ropper.convert(meth, advice);
+ SsaMethod ssaMeth = null;
+ boolean isStatic = AccessFlags.isStatic(meth.getAccessFlags());
+ int paramWidth = computeParamWidth(meth, isStatic);
+
+ if (args.ssaStep == null) {
+ ssaMeth = Optimizer.debugNoRegisterAllocation(rmeth,
+ paramWidth, isStatic, true, advice,
+ EnumSet.allOf(Optimizer.OptionalStep.class));
+ } else if ("edge-split".equals(args.ssaStep)) {
+ ssaMeth = Optimizer.debugEdgeSplit(rmeth, paramWidth,
+ isStatic, true, advice);
+ } else if ("phi-placement".equals(args.ssaStep)) {
+ ssaMeth = Optimizer.debugPhiPlacement(
+ rmeth, paramWidth, isStatic, true, advice);
+ } else if ("renaming".equals(args.ssaStep)) {
+ ssaMeth = Optimizer.debugRenaming(
+ rmeth, paramWidth, isStatic, true, advice);
+ } else if ("dead-code".equals(args.ssaStep)) {
+ ssaMeth = Optimizer.debugDeadCodeRemover(
+ rmeth, paramWidth, isStatic,true, advice);
+ }
+
+ StringBuffer sb = new StringBuffer(2000);
+
+ sb.append("first ");
+ sb.append(Hex.u2(
+ ssaMeth.blockIndexToRopLabel(ssaMeth.getEntryBlockIndex())));
+ sb.append('\n');
+
+ ArrayList<SsaBasicBlock> blocks = ssaMeth.getBlocks();
+ ArrayList<SsaBasicBlock> sortedBlocks =
+ (ArrayList<SsaBasicBlock>) blocks.clone();
+ Collections.sort(sortedBlocks, SsaBasicBlock.LABEL_COMPARATOR);
+
+ for (SsaBasicBlock block : sortedBlocks) {
+ sb.append("block ")
+ .append(Hex.u2(block.getRopLabel())).append('\n');
+
+ BitSet preds = block.getPredecessors();
+
+ for (int i = preds.nextSetBit(0); i >= 0;
+ i = preds.nextSetBit(i+1)) {
+ sb.append(" pred ");
+ sb.append(Hex.u2(ssaMeth.blockIndexToRopLabel(i)));
+ sb.append('\n');
+ }
+
+ sb.append(" live in:" + block.getLiveInRegs());
+ sb.append("\n");
+
+ for (SsaInsn insn : block.getInsns()) {
+ sb.append(" ");
+ sb.append(insn.toHuman());
+ sb.append('\n');
+ }
+
+ if (block.getSuccessors().cardinality() == 0) {
+ sb.append(" returns\n");
+ } else {
+ int primary = block.getPrimarySuccessorRopLabel();
+
+ IntList succLabelList = block.getRopLabelSuccessorList();
+
+ int szSuccLabels = succLabelList.size();
+
+ for (int i = 0; i < szSuccLabels; i++) {
+ sb.append(" next ");
+ sb.append(Hex.u2(succLabelList.get(i)));
+
+ if (szSuccLabels != 1 && primary == succLabelList.get(i)) {
+ sb.append(" *");
+ }
+ sb.append('\n');
+ }
+ }
+
+ sb.append(" live out:" + block.getLiveOutRegs());
+ sb.append("\n");
+ }
+
+ suppressDump = false;
+ setAt(bytes, 0);
+ parsed(bytes, 0, bytes.size(), sb.toString());
+ suppressDump = true;
+ }
+}
diff --git a/dx/src/com/android/dx/dex/cf/AttributeTranslator.java b/dx/src/com/android/dx/dex/cf/AttributeTranslator.java
new file mode 100644
index 0000000..2508a59
--- /dev/null
+++ b/dx/src/com/android/dx/dex/cf/AttributeTranslator.java
@@ -0,0 +1,428 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.cf;
+
+import com.android.dx.cf.attrib.AttAnnotationDefault;
+import com.android.dx.cf.attrib.AttEnclosingMethod;
+import com.android.dx.cf.attrib.AttExceptions;
+import com.android.dx.cf.attrib.AttInnerClasses;
+import com.android.dx.cf.attrib.AttRuntimeInvisibleAnnotations;
+import com.android.dx.cf.attrib.AttRuntimeInvisibleParameterAnnotations;
+import com.android.dx.cf.attrib.AttRuntimeVisibleAnnotations;
+import com.android.dx.cf.attrib.AttRuntimeVisibleParameterAnnotations;
+import com.android.dx.cf.attrib.AttSignature;
+import com.android.dx.cf.attrib.InnerClassList;
+import com.android.dx.cf.direct.DirectClassFile;
+import com.android.dx.cf.direct.StdAttributeFactory;
+import com.android.dx.cf.iface.AttributeList;
+import com.android.dx.cf.iface.Method;
+import com.android.dx.cf.iface.MethodList;
+import com.android.dx.dex.file.AnnotationUtils;
+import com.android.dx.rop.annotation.Annotation;
+import com.android.dx.rop.annotation.AnnotationVisibility;
+import com.android.dx.rop.annotation.Annotations;
+import com.android.dx.rop.annotation.AnnotationsList;
+import com.android.dx.rop.annotation.NameValuePair;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.Warning;
+
+import java.util.ArrayList;
+
+/**
+ * Utility methods that translate various classfile attributes
+ * into forms suitable for use in creating {@code dex} files.
+ */
+/*package*/ class AttributeTranslator {
+ /**
+ * This class is uninstantiable.
+ */
+ private AttributeTranslator() {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Gets the list of thrown exceptions for a given method.
+ *
+ * @param method {@code non-null;} the method in question
+ * @return {@code non-null;} the list of thrown exceptions
+ */
+ public static TypeList getExceptions(Method method) {
+ AttributeList attribs = method.getAttributes();
+ AttExceptions exceptions = (AttExceptions)
+ attribs.findFirst(AttExceptions.ATTRIBUTE_NAME);
+
+ if (exceptions == null) {
+ return StdTypeList.EMPTY;
+ }
+
+ return exceptions.getExceptions();
+ }
+
+ /**
+ * Gets the annotations out of a given {@link AttributeList}. This
+ * combines both visible and invisible annotations into a single
+ * result set and also adds in a system annotation for the
+ * {@code Signature} attribute if present.
+ *
+ * @param attribs {@code non-null;} the attributes list to search in
+ * @return {@code non-null;} the set of annotations, which may be empty
+ */
+ public static Annotations getAnnotations(AttributeList attribs) {
+ Annotations result = getAnnotations0(attribs);
+ Annotation signature = getSignature(attribs);
+
+ if (signature != null) {
+ result = Annotations.combine(result, signature);
+ }
+
+ return result;
+ }
+
+ /**
+ * Gets the annotations out of a given class, similar to {@link
+ * #getAnnotations}, also including annotations for translations
+ * of class-level attributes {@code EnclosingMethod} and
+ * {@code InnerClasses}, if present. Additionally, if the
+ * class is an annotation class, then this also includes a
+ * representation of all the {@code AnnotationDefault}
+ * values.
+ *
+ * @param cf {@code non-null;} the class in question
+ * @param args {@code non-null;} the high-level options
+ * @return {@code non-null;} the set of annotations, which may be empty
+ */
+ public static Annotations getClassAnnotations(DirectClassFile cf,
+ CfOptions args) {
+ CstType thisClass = cf.getThisClass();
+ AttributeList attribs = cf.getAttributes();
+ Annotations result = getAnnotations(attribs);
+ Annotation enclosingMethod = translateEnclosingMethod(attribs);
+
+ try {
+ Annotations innerClassAnnotations =
+ translateInnerClasses(thisClass, attribs,
+ enclosingMethod == null);
+ if (innerClassAnnotations != null) {
+ result = Annotations.combine(result, innerClassAnnotations);
+ }
+ } catch (Warning warn) {
+ args.warn.println("warning: " + warn.getMessage());
+ }
+
+ if (enclosingMethod != null) {
+ result = Annotations.combine(result, enclosingMethod);
+ }
+
+ if (AccessFlags.isAnnotation(cf.getAccessFlags())) {
+ Annotation annotationDefault =
+ translateAnnotationDefaults(cf);
+ if (annotationDefault != null) {
+ result = Annotations.combine(result, annotationDefault);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Gets the annotations out of a given method, similar to {@link
+ * #getAnnotations}, also including an annotation for the translation
+ * of the method-specific attribute {@code Exceptions}.
+ *
+ * @param method {@code non-null;} the method in question
+ * @return {@code non-null;} the set of annotations, which may be empty
+ */
+ public static Annotations getMethodAnnotations(Method method) {
+ Annotations result = getAnnotations(method.getAttributes());
+ TypeList exceptions = getExceptions(method);
+
+ if (exceptions.size() != 0) {
+ Annotation throwsAnnotation =
+ AnnotationUtils.makeThrows(exceptions);
+ result = Annotations.combine(result, throwsAnnotation);
+ }
+
+ return result;
+ }
+
+ /**
+ * Helper method for {@link #getAnnotations} which just gets the
+ * existing annotations, per se.
+ *
+ * @param attribs {@code non-null;} the attributes list to search in
+ * @return {@code non-null;} the set of annotations, which may be empty
+ */
+ private static Annotations getAnnotations0(AttributeList attribs) {
+ AttRuntimeVisibleAnnotations visible =
+ (AttRuntimeVisibleAnnotations)
+ attribs.findFirst(AttRuntimeVisibleAnnotations.ATTRIBUTE_NAME);
+ AttRuntimeInvisibleAnnotations invisible =
+ (AttRuntimeInvisibleAnnotations)
+ attribs.findFirst(AttRuntimeInvisibleAnnotations.ATTRIBUTE_NAME);
+
+ if (visible == null) {
+ if (invisible == null) {
+ return Annotations.EMPTY;
+ }
+ return invisible.getAnnotations();
+ }
+
+ if (invisible == null) {
+ return visible.getAnnotations();
+ }
+
+ // Both are non-null, so combine them.
+
+ return Annotations.combine(visible.getAnnotations(),
+ invisible.getAnnotations());
+ }
+
+ /**
+ * Gets the {@code Signature} attribute out of a given
+ * {@link AttributeList}, if any, translating it to an annotation.
+ *
+ * @param attribs {@code non-null;} the attributes list to search in
+ * @return {@code null-ok;} the converted {@code Signature} annotation,
+ * if there was an attribute to translate
+ */
+ private static Annotation getSignature(AttributeList attribs) {
+ AttSignature signature = (AttSignature)
+ attribs.findFirst(AttSignature.ATTRIBUTE_NAME);
+
+ if (signature == null) {
+ return null;
+ }
+
+ return AnnotationUtils.makeSignature(signature.getSignature());
+ }
+
+ /**
+ * Gets the {@code EnclosingMethod} attribute out of a given
+ * {@link AttributeList}, if any, translating it to an annotation.
+ * If the class really has an enclosing method, this returns an
+ * {@code EnclosingMethod} annotation; if not, this returns
+ * an {@code EnclosingClass} annotation.
+ *
+ * @param attribs {@code non-null;} the attributes list to search in
+ * @return {@code null-ok;} the converted {@code EnclosingMethod} or
+ * {@code EnclosingClass} annotation, if there was an
+ * attribute to translate
+ */
+ private static Annotation translateEnclosingMethod(AttributeList attribs) {
+ AttEnclosingMethod enclosingMethod = (AttEnclosingMethod)
+ attribs.findFirst(AttEnclosingMethod.ATTRIBUTE_NAME);
+
+ if (enclosingMethod == null) {
+ return null;
+ }
+
+ CstType enclosingClass = enclosingMethod.getEnclosingClass();
+ CstNat nat = enclosingMethod.getMethod();
+
+ if (nat == null) {
+ /*
+ * Dalvik doesn't use EnclosingMethod annotations unless
+ * there really is an enclosing method. Anonymous classes
+ * are unambiguously identified by having an InnerClass
+ * annotation with an empty name along with an appropriate
+ * EnclosingClass.
+ */
+ return AnnotationUtils.makeEnclosingClass(enclosingClass);
+ }
+
+ return AnnotationUtils.makeEnclosingMethod(
+ new CstMethodRef(enclosingClass, nat));
+ }
+
+ /**
+ * Gets the {@code InnerClasses} attribute out of a given
+ * {@link AttributeList}, if any, translating it to one or more of an
+ * {@code InnerClass}, {@code EnclosingClass}, or
+ * {@code MemberClasses} annotation.
+ *
+ * @param thisClass {@code non-null;} type representing the class being
+ * processed
+ * @param attribs {@code non-null;} the attributes list to search in
+ * @param needEnclosingClass whether to include an
+ * {@code EnclosingClass} annotation
+ * @return {@code null-ok;} the converted list of annotations, if there
+ * was an attribute to translate
+ */
+ private static Annotations translateInnerClasses(CstType thisClass,
+ AttributeList attribs, boolean needEnclosingClass) {
+ AttInnerClasses innerClasses = (AttInnerClasses)
+ attribs.findFirst(AttInnerClasses.ATTRIBUTE_NAME);
+
+ if (innerClasses == null) {
+ return null;
+ }
+
+ /*
+ * Search the list for the element representing the current class
+ * as well as for any named member classes.
+ */
+
+ InnerClassList list = innerClasses.getInnerClasses();
+ int size = list.size();
+ InnerClassList.Item foundThisClass = null;
+ ArrayList<Type> membersList = new ArrayList<Type>();
+
+ for (int i = 0; i < size; i++) {
+ InnerClassList.Item item = list.get(i);
+ CstType innerClass = item.getInnerClass();
+ if (innerClass.equals(thisClass)) {
+ foundThisClass = item;
+ } else if (thisClass.equals(item.getOuterClass())) {
+ membersList.add(innerClass.getClassType());
+ }
+ }
+
+ int membersSize = membersList.size();
+
+ if ((foundThisClass == null) && (membersSize == 0)) {
+ return null;
+ }
+
+ Annotations result = new Annotations();
+
+ if (foundThisClass != null) {
+ result.add(AnnotationUtils.makeInnerClass(
+ foundThisClass.getInnerName(),
+ foundThisClass.getAccessFlags()));
+ if (needEnclosingClass) {
+ CstType outer = foundThisClass.getOuterClass();
+ if (outer == null) {
+ throw new Warning(
+ "Ignoring InnerClasses attribute for an " +
+ "anonymous inner class\n" +
+ "(" + thisClass.toHuman() +
+ ") that doesn't come with an\n" +
+ "associated EnclosingMethod attribute. " +
+ "This class was probably produced by a\n" +
+ "compiler that did not target the modern " +
+ ".class file format. The recommended\n" +
+ "solution is to recompile the class from " +
+ "source, using an up-to-date compiler\n" +
+ "and without specifying any \"-target\" type " +
+ "options. The consequence of ignoring\n" +
+ "this warning is that reflective operations " +
+ "on this class will incorrectly\n" +
+ "indicate that it is *not* an inner class.");
+ }
+ result.add(AnnotationUtils.makeEnclosingClass(
+ foundThisClass.getOuterClass()));
+ }
+ }
+
+ if (membersSize != 0) {
+ StdTypeList typeList = new StdTypeList(membersSize);
+ for (int i = 0; i < membersSize; i++) {
+ typeList.set(i, membersList.get(i));
+ }
+ typeList.setImmutable();
+ result.add(AnnotationUtils.makeMemberClasses(typeList));
+ }
+
+ result.setImmutable();
+ return result;
+ }
+
+ /**
+ * Gets the parameter annotations out of a given method. This
+ * combines both visible and invisible annotations into a single
+ * result set.
+ *
+ * @param method {@code non-null;} the method in question
+ * @return {@code non-null;} the list of annotation sets, which may be
+ * empty
+ */
+ public static AnnotationsList getParameterAnnotations(Method method) {
+ AttributeList attribs = method.getAttributes();
+ AttRuntimeVisibleParameterAnnotations visible =
+ (AttRuntimeVisibleParameterAnnotations)
+ attribs.findFirst(
+ AttRuntimeVisibleParameterAnnotations.ATTRIBUTE_NAME);
+ AttRuntimeInvisibleParameterAnnotations invisible =
+ (AttRuntimeInvisibleParameterAnnotations)
+ attribs.findFirst(
+ AttRuntimeInvisibleParameterAnnotations.ATTRIBUTE_NAME);
+
+ if (visible == null) {
+ if (invisible == null) {
+ return AnnotationsList.EMPTY;
+ }
+ return invisible.getParameterAnnotations();
+ }
+
+ if (invisible == null) {
+ return visible.getParameterAnnotations();
+ }
+
+ // Both are non-null, so combine them.
+
+ return AnnotationsList.combine(visible.getParameterAnnotations(),
+ invisible.getParameterAnnotations());
+ }
+
+ /**
+ * Gets the {@code AnnotationDefault} attributes out of a
+ * given class, if any, reforming them as an
+ * {@code AnnotationDefault} annotation.
+ *
+ * @param cf {@code non-null;} the class in question
+ * @return {@code null-ok;} an appropriately-constructed
+ * {@code AnnotationDefault} annotation, if there were any
+ * annotation defaults in the class, or {@code null} if not
+ */
+ private static Annotation translateAnnotationDefaults(DirectClassFile cf) {
+ CstType thisClass = cf.getThisClass();
+ MethodList methods = cf.getMethods();
+ int sz = methods.size();
+ Annotation result =
+ new Annotation(thisClass, AnnotationVisibility.EMBEDDED);
+ boolean any = false;
+
+ for (int i = 0; i < sz; i++) {
+ Method one = methods.get(i);
+ AttributeList attribs = one.getAttributes();
+ AttAnnotationDefault oneDefault = (AttAnnotationDefault)
+ attribs.findFirst(AttAnnotationDefault.ATTRIBUTE_NAME);
+
+ if (oneDefault != null) {
+ NameValuePair pair = new NameValuePair(
+ one.getNat().getName(),
+ oneDefault.getValue());
+ result.add(pair);
+ any = true;
+ }
+ }
+
+ if (! any) {
+ return null;
+ }
+
+ result.setImmutable();
+ return AnnotationUtils.makeAnnotationDefault(result);
+ }
+}
diff --git a/dx/src/com/android/dx/dex/cf/CfOptions.java b/dx/src/com/android/dx/dex/cf/CfOptions.java
new file mode 100644
index 0000000..468f0be
--- /dev/null
+++ b/dx/src/com/android/dx/dex/cf/CfOptions.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.cf;
+
+import com.android.dx.dex.code.PositionList;
+
+import java.io.PrintStream;
+
+/**
+ * A class to contain options passed into dex.cf
+ */
+public class CfOptions {
+ /** how much source position info to preserve */
+ public int positionInfo = PositionList.LINES;
+
+ /** whether to keep local variable information */
+ public boolean localInfo = false;
+
+ /** whether strict file-name-vs-class-name checking should be done */
+ public boolean strictNameCheck = true;
+
+ /** whether to do SSA/register optimization */
+ public boolean optimize = false;
+
+ /** filename containing list of methods to optimize */
+ public String optimizeListFile = null;
+
+ /** filename containing list of methods <i>not</i> to optimize */
+ public String dontOptimizeListFile = null;
+
+ /** whether to print statistics to stdout at end of compile cycle */
+ public boolean statistics;
+
+ /** where to issue warnings to */
+ public PrintStream warn = System.err;
+}
diff --git a/dx/src/com/android/dx/dex/cf/CfTranslator.java b/dx/src/com/android/dx/dex/cf/CfTranslator.java
new file mode 100644
index 0000000..1a9aa47
--- /dev/null
+++ b/dx/src/com/android/dx/dex/cf/CfTranslator.java
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.cf;
+
+import com.android.dx.cf.code.ConcreteMethod;
+import com.android.dx.cf.code.Ropper;
+import com.android.dx.cf.direct.DirectClassFile;
+import com.android.dx.cf.direct.StdAttributeFactory;
+import com.android.dx.cf.iface.Field;
+import com.android.dx.cf.iface.FieldList;
+import com.android.dx.cf.iface.Method;
+import com.android.dx.cf.iface.MethodList;
+import com.android.dx.dex.code.DalvCode;
+import com.android.dx.dex.code.PositionList;
+import com.android.dx.dex.code.RopTranslator;
+import com.android.dx.dex.file.ClassDefItem;
+import com.android.dx.dex.file.EncodedField;
+import com.android.dx.dex.file.EncodedMethod;
+import com.android.dx.rop.annotation.Annotations;
+import com.android.dx.rop.annotation.AnnotationsList;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.rop.code.LocalVariableExtractor;
+import com.android.dx.rop.code.LocalVariableInfo;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.code.DexTranslationAdvice;
+import com.android.dx.rop.code.TranslationAdvice;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstBoolean;
+import com.android.dx.rop.cst.CstByte;
+import com.android.dx.rop.cst.CstChar;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstShort;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.cst.TypedConstant;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.ssa.Optimizer;
+import com.android.dx.util.ExceptionWithContext;
+
+/**
+ * Static method that turns {@code byte[]}s containing Java
+ * classfiles into {@link ClassDefItem} instances.
+ */
+public class CfTranslator {
+ /** set to {@code true} to enable development-time debugging code */
+ private static final boolean DEBUG = false;
+
+ /**
+ * This class is uninstantiable.
+ */
+ private CfTranslator() {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Takes a {@code byte[]}, interprets it as a Java classfile, and
+ * translates it into a {@link ClassDefItem}.
+ *
+ * @param filePath {@code non-null;} the file path for the class,
+ * excluding any base directory specification
+ * @param bytes {@code non-null;} contents of the file
+ * @param args command-line arguments
+ * @return {@code non-null;} the translated class
+ */
+ public static ClassDefItem translate(String filePath, byte[] bytes,
+ CfOptions args) {
+ try {
+ return translate0(filePath, bytes, args);
+ } catch (RuntimeException ex) {
+ String msg = "...while processing " + filePath;
+ throw ExceptionWithContext.withContext(ex, msg);
+ }
+ }
+
+ /**
+ * Performs the main act of translation. This method is separated
+ * from {@link #translate} just to keep things a bit simpler in
+ * terms of exception handling.
+ *
+ * @param filePath {@code non-null;} the file path for the class,
+ * excluding any base directory specification
+ * @param bytes {@code non-null;} contents of the file
+ * @param args command-line arguments
+ * @return {@code non-null;} the translated class
+ */
+ private static ClassDefItem translate0(String filePath, byte[] bytes,
+ CfOptions args) {
+ DirectClassFile cf =
+ new DirectClassFile(bytes, filePath, args.strictNameCheck);
+
+ cf.setAttributeFactory(StdAttributeFactory.THE_ONE);
+ cf.getMagic();
+
+ OptimizerOptions.loadOptimizeLists(args.optimizeListFile,
+ args.dontOptimizeListFile);
+
+ // Build up a class to output.
+
+ CstType thisClass = cf.getThisClass();
+ int classAccessFlags = cf.getAccessFlags() & ~AccessFlags.ACC_SUPER;
+ CstUtf8 sourceFile = (args.positionInfo == PositionList.NONE) ? null :
+ cf.getSourceFile();
+ ClassDefItem out =
+ new ClassDefItem(thisClass, classAccessFlags,
+ cf.getSuperclass(), cf.getInterfaces(), sourceFile);
+
+ Annotations classAnnotations =
+ AttributeTranslator.getClassAnnotations(cf, args);
+ if (classAnnotations.size() != 0) {
+ out.setClassAnnotations(classAnnotations);
+ }
+
+ processFields(cf, out);
+ processMethods(cf, args, out);
+
+ return out;
+ }
+
+ /**
+ * Processes the fields of the given class.
+ *
+ * @param cf {@code non-null;} class being translated
+ * @param out {@code non-null;} output class
+ */
+ private static void processFields(DirectClassFile cf, ClassDefItem out) {
+ CstType thisClass = cf.getThisClass();
+ FieldList fields = cf.getFields();
+ int sz = fields.size();
+
+ for (int i = 0; i < sz; i++) {
+ Field one = fields.get(i);
+ try {
+ CstFieldRef field = new CstFieldRef(thisClass, one.getNat());
+ int accessFlags = one.getAccessFlags();
+
+ if (AccessFlags.isStatic(accessFlags)) {
+ TypedConstant constVal = one.getConstantValue();
+ EncodedField fi = new EncodedField(field, accessFlags);
+ if (constVal != null) {
+ constVal = coerceConstant(constVal, field.getType());
+ }
+ out.addStaticField(fi, constVal);
+ } else {
+ EncodedField fi = new EncodedField(field, accessFlags);
+ out.addInstanceField(fi);
+ }
+
+ Annotations annotations =
+ AttributeTranslator.getAnnotations(one.getAttributes());
+ if (annotations.size() != 0) {
+ out.addFieldAnnotations(field, annotations);
+ }
+ } catch (RuntimeException ex) {
+ String msg = "...while processing " + one.getName().toHuman() +
+ " " + one.getDescriptor().toHuman();
+ throw ExceptionWithContext.withContext(ex, msg);
+ }
+ }
+ }
+
+ /**
+ * Helper for {@link #processFields}, which translates constants into
+ * more specific types if necessary.
+ *
+ * @param constant {@code non-null;} the constant in question
+ * @param type {@code non-null;} the desired type
+ */
+ private static TypedConstant coerceConstant(TypedConstant constant,
+ Type type) {
+ Type constantType = constant.getType();
+
+ if (constantType.equals(type)) {
+ return constant;
+ }
+
+ switch (type.getBasicType()) {
+ case Type.BT_BOOLEAN: {
+ return CstBoolean.make(((CstInteger) constant).getValue());
+ }
+ case Type.BT_BYTE: {
+ return CstByte.make(((CstInteger) constant).getValue());
+ }
+ case Type.BT_CHAR: {
+ return CstChar.make(((CstInteger) constant).getValue());
+ }
+ case Type.BT_SHORT: {
+ return CstShort.make(((CstInteger) constant).getValue());
+ }
+ default: {
+ throw new UnsupportedOperationException("can't coerce " +
+ constant + " to " + type);
+ }
+ }
+ }
+
+ /**
+ * Processes the methods of the given class.
+ *
+ * @param cf {@code non-null;} class being translated
+ * @param args {@code non-null;} command-line args
+ * @param out {@code non-null;} output class
+ */
+ private static void processMethods(DirectClassFile cf,
+ CfOptions args, ClassDefItem out) {
+ CstType thisClass = cf.getThisClass();
+ MethodList methods = cf.getMethods();
+ int sz = methods.size();
+
+ for (int i = 0; i < sz; i++) {
+ Method one = methods.get(i);
+ try {
+ CstMethodRef meth = new CstMethodRef(thisClass, one.getNat());
+ int accessFlags = one.getAccessFlags();
+ boolean isStatic = AccessFlags.isStatic(accessFlags);
+ boolean isPrivate = AccessFlags.isPrivate(accessFlags);
+ boolean isNative = AccessFlags.isNative(accessFlags);
+ boolean isAbstract = AccessFlags.isAbstract(accessFlags);
+ boolean isConstructor = meth.isInstanceInit() ||
+ meth.isClassInit();
+ DalvCode code;
+
+ if (isNative || isAbstract) {
+ // There's no code for native or abstract methods.
+ code = null;
+ } else {
+ ConcreteMethod concrete =
+ new ConcreteMethod(one, cf,
+ (args.positionInfo != PositionList.NONE),
+ args.localInfo);
+
+ TranslationAdvice advice;
+
+ advice = DexTranslationAdvice.THE_ONE;
+
+ RopMethod rmeth = Ropper.convert(concrete, advice);
+ RopMethod nonOptRmeth = null;
+ int paramSize;
+
+ paramSize = meth.getParameterWordCount(isStatic);
+
+ String canonicalName
+ = thisClass.getClassType().getDescriptor()
+ + "." + one.getName().getString();
+
+ if (args.optimize &&
+ OptimizerOptions.shouldOptimize(canonicalName)) {
+ if (DEBUG) {
+ System.err.println("Optimizing " + canonicalName);
+ }
+
+ nonOptRmeth = rmeth;
+ rmeth = Optimizer.optimize(rmeth,
+ paramSize, isStatic, args.localInfo, advice);
+
+ if (DEBUG) {
+ OptimizerOptions.compareOptimizerStep(nonOptRmeth,
+ paramSize, isStatic, args, advice, rmeth);
+ }
+
+ if (args.statistics) {
+ CodeStatistics.updateRopStatistics(
+ nonOptRmeth, rmeth);
+ }
+ }
+
+ LocalVariableInfo locals = null;
+
+ if (args.localInfo) {
+ locals = LocalVariableExtractor.extract(rmeth);
+ }
+
+ code = RopTranslator.translate(rmeth, args.positionInfo,
+ locals, paramSize);
+
+ if (args.statistics && nonOptRmeth != null) {
+ updateDexStatistics(args, rmeth, nonOptRmeth, locals,
+ paramSize, concrete.getCode().size());
+ }
+ }
+
+ // Preserve the synchronized flag as its "declared" variant...
+ if (AccessFlags.isSynchronized(accessFlags)) {
+ accessFlags |= AccessFlags.ACC_DECLARED_SYNCHRONIZED;
+
+ /*
+ * ...but only native methods are actually allowed to be
+ * synchronized.
+ */
+ if (!isNative) {
+ accessFlags &= ~AccessFlags.ACC_SYNCHRONIZED;
+ }
+ }
+
+ if (isConstructor) {
+ accessFlags |= AccessFlags.ACC_CONSTRUCTOR;
+ }
+
+ TypeList exceptions = AttributeTranslator.getExceptions(one);
+ EncodedMethod mi =
+ new EncodedMethod(meth, accessFlags, code, exceptions);
+
+ if (meth.isInstanceInit() || meth.isClassInit() ||
+ isStatic || isPrivate) {
+ out.addDirectMethod(mi);
+ } else {
+ out.addVirtualMethod(mi);
+ }
+
+ Annotations annotations =
+ AttributeTranslator.getMethodAnnotations(one);
+ if (annotations.size() != 0) {
+ out.addMethodAnnotations(meth, annotations);
+ }
+
+ AnnotationsList list =
+ AttributeTranslator.getParameterAnnotations(one);
+ if (list.size() != 0) {
+ out.addParameterAnnotations(meth, list);
+ }
+ } catch (RuntimeException ex) {
+ String msg = "...while processing " + one.getName().toHuman() +
+ " " + one.getDescriptor().toHuman();
+ throw ExceptionWithContext.withContext(ex, msg);
+ }
+ }
+ }
+
+ /**
+ * Helper that updates the dex statistics.
+ */
+ private static void updateDexStatistics(CfOptions args,
+ RopMethod optRmeth, RopMethod nonOptRmeth,
+ LocalVariableInfo locals, int paramSize, int originalByteCount) {
+ /*
+ * Run rop->dex again on optimized vs. non-optimized method to
+ * collect statistics. We have to totally convert both ways,
+ * since converting the "real" method getting added to the
+ * file would corrupt it (by messing with its constant pool
+ * indices).
+ */
+
+ DalvCode optCode = RopTranslator.translate(optRmeth,
+ args.positionInfo, locals, paramSize);
+ DalvCode nonOptCode = RopTranslator.translate(nonOptRmeth,
+ args.positionInfo, locals, paramSize);
+
+ /*
+ * Fake out the indices, so code.getInsns() can work well enough
+ * for the current purpose.
+ */
+
+ DalvCode.AssignIndicesCallback callback =
+ new DalvCode.AssignIndicesCallback() {
+ public int getIndex(Constant cst) {
+ // Everything is at index 0!
+ return 0;
+ }
+ };
+
+ optCode.assignIndices(callback);
+ nonOptCode.assignIndices(callback);
+
+ CodeStatistics.updateDexStatistics(nonOptCode, optCode);
+ CodeStatistics.updateOriginalByteCount(originalByteCount);
+ }
+}
diff --git a/dx/src/com/android/dx/dex/cf/CodeStatistics.java b/dx/src/com/android/dx/dex/cf/CodeStatistics.java
new file mode 100644
index 0000000..33d03fd
--- /dev/null
+++ b/dx/src/com/android/dx/dex/cf/CodeStatistics.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.cf;
+
+import com.android.dx.dex.code.DalvCode;
+import com.android.dx.rop.code.RopMethod;
+
+import java.io.PrintStream;
+
+/**
+ * Static methods and variables for collecting statistics on generated
+ * code.
+ */
+public final class CodeStatistics {
+ /** set to {@code true} to enable development-time debugging code */
+ private static final boolean DEBUG = false;
+
+ /**
+ * running sum of the number of registers added/removed in
+ * SSA form by the optimizer
+ */
+ public static int runningDeltaRegisters = 0;
+
+ /**
+ * running sum of the number of insns added/removed in
+ * SSA form by the optimizer
+ */
+ public static int runningDeltaInsns = 0;
+
+ /** running sum of the total number of Rop insns processed */
+ public static int runningTotalInsns = 0;
+
+ /**
+ * running sum of the number of dex-form registers added/removed in
+ * SSA form by the optimizer. Only valid if args.statistics is true.
+ */
+ public static int dexRunningDeltaRegisters = 0;
+
+ /**
+ * running sum of the number of dex-form insns (actually code
+ * units) added/removed in SSA form by the optimizer. Only valid
+ * if args.statistics is true.
+ */
+ public static int dexRunningDeltaInsns = 0;
+
+ /**
+ * running sum of the total number of dex insns (actually code
+ * units) processed
+ */
+ public static int dexRunningTotalInsns = 0;
+
+ /** running sum of original class bytecode bytes */
+ public static int runningOriginalBytes = 0;
+
+ /**
+ * This class is uninstantiable.
+ */
+ private CodeStatistics() {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Updates the number of original bytecode bytes processed.
+ *
+ * @param count {@code >= 0;} the number of bytes to add
+ */
+ public static void updateOriginalByteCount(int count) {
+ runningOriginalBytes += count;
+ }
+
+ /**
+ * Updates the dex statistics.
+ *
+ * @param nonOptCode non-optimized code block
+ * @param code optimized code block
+ */
+ public static void updateDexStatistics(DalvCode nonOptCode,
+ DalvCode code) {
+ if (DEBUG) {
+ System.err.println("dex insns (old/new) "
+ + nonOptCode.getInsns().codeSize()
+ + "/" + code.getInsns().codeSize()
+ + " regs (o/n) "
+ + nonOptCode.getInsns().getRegistersSize()
+ + "/" + code.getInsns().getRegistersSize()
+ );
+ }
+
+ dexRunningDeltaInsns
+ += (code.getInsns().codeSize()
+ - nonOptCode.getInsns().codeSize());
+
+ dexRunningDeltaRegisters
+ += (code.getInsns().getRegistersSize()
+ - nonOptCode.getInsns().getRegistersSize());
+
+ dexRunningTotalInsns += code.getInsns().codeSize();
+ }
+
+ /**
+ * Updates the ROP statistics.
+ *
+ * @param nonOptRmeth non-optimized method
+ * @param rmeth optimized method
+ */
+ public static void updateRopStatistics(RopMethod nonOptRmeth,
+ RopMethod rmeth) {
+ int oldCountInsns
+ = nonOptRmeth.getBlocks().getEffectiveInstructionCount();
+ int oldCountRegs = nonOptRmeth.getBlocks().getRegCount();
+
+ if (DEBUG) {
+ System.err.println("insns (old/new): "
+ + oldCountInsns + "/"
+ + rmeth.getBlocks().getEffectiveInstructionCount()
+ + " regs (o/n):" + oldCountRegs
+ + "/" + rmeth.getBlocks().getRegCount());
+ }
+
+ int newCountInsns
+ = rmeth.getBlocks().getEffectiveInstructionCount();
+
+ runningDeltaInsns
+ += (newCountInsns - oldCountInsns);
+
+ runningDeltaRegisters
+ += (rmeth.getBlocks().getRegCount() - oldCountRegs);
+
+ runningTotalInsns += newCountInsns;
+ }
+
+ /**
+ * Prints out the collected statistics.
+ *
+ * @param out {@code non-null;} where to output to
+ */
+ public static void dumpStatistics(PrintStream out) {
+ out.printf("Optimizer Delta Rop Insns: %d total: %d "
+ + "(%.2f%%) Delta Registers: %d\n",
+ runningDeltaInsns,
+ runningTotalInsns,
+ (100.0 * (((float) runningDeltaInsns)
+ / (runningTotalInsns + Math.abs(runningDeltaInsns)))),
+ runningDeltaRegisters);
+
+ out.printf("Optimizer Delta Dex Insns: Insns: %d total: %d "
+ + "(%.2f%%) Delta Registers: %d\n",
+ dexRunningDeltaInsns,
+ dexRunningTotalInsns,
+ (100.0 * (((float) dexRunningDeltaInsns)
+ / (dexRunningTotalInsns
+ + Math.abs(dexRunningDeltaInsns)))),
+ dexRunningDeltaRegisters);
+
+ out.printf("Original bytecode byte count: %d\n",
+ runningOriginalBytes);
+ }
+}
diff --git a/dx/src/com/android/dx/dex/cf/OptimizerOptions.java b/dx/src/com/android/dx/dex/cf/OptimizerOptions.java
new file mode 100644
index 0000000..a66421e
--- /dev/null
+++ b/dx/src/com/android/dx/dex/cf/OptimizerOptions.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.cf;
+
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.code.TranslationAdvice;
+import com.android.dx.ssa.Optimizer;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.EnumSet;
+import java.util.HashSet;
+
+/**
+ * Settings for optimization of code.
+ */
+public class OptimizerOptions {
+ /**
+ * {@code null-ok;} hash set of class name + method names that
+ * should be optimized. {@code null} if this constraint was not
+ * specified on the command line
+ */
+ private static HashSet<String> optimizeList;
+
+ /**
+ * {@code null-ok;} hash set of class name + method names that should NOT
+ * be optimized. null if this constraint was not specified on the
+ * command line
+ */
+ private static HashSet<String> dontOptimizeList;
+
+ /** true if the above lists have been loaded */
+ private static boolean optimizeListsLoaded;
+
+ /**
+ * This class is uninstantiable.
+ */
+ private OptimizerOptions() {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Loads the optimize/don't optimize lists from files.
+ *
+ * @param optimizeListFile Pathname
+ * @param dontOptimizeListFile Pathname
+ */
+ public static void loadOptimizeLists(String optimizeListFile,
+ String dontOptimizeListFile) {
+ if (optimizeListsLoaded) {
+ return;
+ }
+
+ if (optimizeListFile != null && dontOptimizeListFile != null) {
+ /*
+ * We shouldn't get this far. The condition should have
+ * been caught in the arg processor.
+ */
+ throw new RuntimeException("optimize and don't optimize lists "
+ + " are mutually exclusive.");
+ }
+
+ if (optimizeListFile != null) {
+ optimizeList = loadStringsFromFile(optimizeListFile);
+ }
+
+ if (dontOptimizeListFile != null) {
+ dontOptimizeList = loadStringsFromFile(dontOptimizeListFile);
+ }
+
+ optimizeListsLoaded = true;
+ }
+
+ /**
+ * Loads a list of newline-separated strings into a new HashSet and returns
+ * the HashSet.
+ *
+ * @param filename filename to process
+ * @return set of all unique lines in the file
+ */
+ private static HashSet<String> loadStringsFromFile(String filename) {
+ HashSet<String> result = new HashSet<String>();
+
+ try {
+ FileReader fr = new FileReader(filename);
+ BufferedReader bfr = new BufferedReader(fr);
+
+ String line;
+
+ while (null != (line = bfr.readLine())) {
+ result.add(line);
+ }
+
+ fr.close();
+ } catch (IOException ex) {
+ // Let the exception percolate up as a RuntimeException.
+ throw new RuntimeException("Error with optimize list: " +
+ filename, ex);
+ }
+
+ return result;
+ }
+
+ /**
+ * Compares the output of the optimizer run normally with a run skipping
+ * some optional steps. Results are printed to stderr.
+ *
+ * @param nonOptRmeth {@code non-null;} origional rop method
+ * @param paramSize {@code >= 0;} parameter size of method
+ * @param isStatic true if this method has no 'this' pointer argument.
+ * @param args {@code non-null;} translator arguments
+ * @param advice {@code non-null;} translation advice
+ * @param rmeth {@code non-null;} method with all optimization steps run.
+ */
+ public static void compareOptimizerStep(RopMethod nonOptRmeth,
+ int paramSize, boolean isStatic, CfOptions args,
+ TranslationAdvice advice, RopMethod rmeth) {
+ EnumSet<Optimizer.OptionalStep> steps;
+
+ steps = EnumSet.allOf(Optimizer.OptionalStep.class);
+
+ // This is the step to skip.
+ steps.remove(Optimizer.OptionalStep.CONST_COLLECTOR);
+
+ RopMethod skipRopMethod
+ = Optimizer.optimize(nonOptRmeth,
+ paramSize, isStatic, args.localInfo, advice, steps);
+
+ int normalInsns
+ = rmeth.getBlocks().getEffectiveInstructionCount();
+ int skipInsns
+ = skipRopMethod.getBlocks().getEffectiveInstructionCount();
+
+ System.err.printf(
+ "optimize step regs:(%d/%d/%.2f%%)"
+ + " insns:(%d/%d/%.2f%%)\n",
+ rmeth.getBlocks().getRegCount(),
+ skipRopMethod.getBlocks().getRegCount(),
+ 100.0 * ((skipRopMethod.getBlocks().getRegCount()
+ - rmeth.getBlocks().getRegCount())
+ / (float) skipRopMethod.getBlocks().getRegCount()),
+ normalInsns, skipInsns,
+ 100.0 * ((skipInsns - normalInsns) / (float) skipInsns));
+ }
+
+ /**
+ * Checks whether the specified method should be optimized
+ *
+ * @param canonicalMethodName name of method being considered
+ * @return true if it should be optimized
+ */
+ public static boolean shouldOptimize(String canonicalMethodName) {
+ // Optimize only what's in the optimize list.
+ if (optimizeList != null) {
+ return optimizeList.contains(canonicalMethodName);
+ }
+
+ /*
+ * Or don't optimize what's listed here. (The two lists are
+ * mutually exclusive.
+ */
+
+ if (dontOptimizeList != null) {
+ return !dontOptimizeList.contains(canonicalMethodName);
+ }
+
+ // If neither list has been specified, then optimize everything.
+ return true;
+ }
+}
diff --git a/dx/src/com/android/dx/dex/cf/package.html b/dx/src/com/android/dx/dex/cf/package.html
new file mode 100644
index 0000000..d56e8a7
--- /dev/null
+++ b/dx/src/com/android/dx/dex/cf/package.html
@@ -0,0 +1,15 @@
+<body>
+<p>Classes for translating Java classfiles into Dalvik classes.</p>
+
+<p><b>PACKAGES USED:</b>
+<ul>
+<li><code>com.android.dx.cf.code</code></li>
+<li><code>com.android.dx.cf.direct</code></li>
+<li><code>com.android.dx.cf.iface</code></li>
+<li><code>com.android.dx.dex.code</code></li>
+<li><code>com.android.dx.dex.file</code></li>
+<li><code>com.android.dx.rop.code</code></li>
+<li><code>com.android.dx.rop.cst</code></li>
+<li><code>com.android.dx.util</code></li>
+</ul>
+</body>
diff --git a/dx/src/com/android/dx/dex/code/ArrayData.java b/dx/src/com/android/dx/dex/code/ArrayData.java
new file mode 100644
index 0000000..145f2c2
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/ArrayData.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.rop.cst.*;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+import com.android.dx.rop.type.Type;
+import java.util.ArrayList;
+
+/**
+ * Pseudo-instruction which holds fill array data.
+ */
+public final class ArrayData extends VariableSizeInsn {
+ /**
+ * {@code non-null;} address representing the instruction that uses this
+ * instance
+ */
+ private final CodeAddress user;
+
+ /** {@code non-null;} initial values to be filled into an array */
+ private final ArrayList<Constant> values;
+
+ /** non-null: type of constant that initializes the array */
+ private final Constant arrayType;
+
+ /** Width of the init value element */
+ private final int elemWidth;
+
+ /** Length of the init list */
+ private final int initLength;
+
+ /**
+ * Constructs an instance. The output address of this instance is initially
+ * unknown ({@code -1}).
+ *
+ * @param position {@code non-null;} source position
+ * @param user {@code non-null;} address representing the instruction that
+ * uses this instance
+ * @param values {@code non-null;} initial values to be filled into an array
+ */
+ public ArrayData(SourcePosition position, CodeAddress user,
+ ArrayList<Constant> values,
+ Constant arrayType) {
+ super(position, RegisterSpecList.EMPTY);
+
+ if (user == null) {
+ throw new NullPointerException("user == null");
+ }
+
+ if (values == null) {
+ throw new NullPointerException("values == null");
+ }
+
+ int sz = values.size();
+
+ if (sz <= 0) {
+ throw new IllegalArgumentException("Illegal number of init values");
+ }
+
+ this.arrayType = arrayType;
+
+ if (arrayType == CstType.BYTE_ARRAY ||
+ arrayType == CstType.BOOLEAN_ARRAY) {
+ elemWidth = 1;
+ } else if (arrayType == CstType.SHORT_ARRAY ||
+ arrayType == CstType.CHAR_ARRAY) {
+ elemWidth = 2;
+ } else if (arrayType == CstType.INT_ARRAY ||
+ arrayType == CstType.FLOAT_ARRAY) {
+ elemWidth = 4;
+ } else if (arrayType == CstType.LONG_ARRAY ||
+ arrayType == CstType.DOUBLE_ARRAY) {
+ elemWidth = 8;
+ } else {
+ throw new IllegalArgumentException("Unexpected constant type");
+ }
+ this.user = user;
+ this.values = values;
+ initLength = values.size();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int codeSize() {
+ int sz = initLength;
+ // Note: the unit here is 16-bit
+ return 4 + ((sz * elemWidth) + 1) / 2;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(AnnotatedOutput out) {
+ int sz = values.size();
+
+ out.writeShort(0x300 | DalvOps.NOP);
+ out.writeShort(elemWidth);
+ out.writeInt(initLength);
+
+
+ // For speed reasons, replicate the for loop in each case
+ switch (elemWidth) {
+ case 1: {
+ for (int i = 0; i < sz; i++) {
+ Constant cst = values.get(i);
+ out.writeByte((byte) ((CstLiteral32) cst).getIntBits());
+ }
+ break;
+ }
+ case 2: {
+ for (int i = 0; i < sz; i++) {
+ Constant cst = values.get(i);
+ out.writeShort((short) ((CstLiteral32) cst).getIntBits());
+ }
+ break;
+ }
+ case 4: {
+ for (int i = 0; i < sz; i++) {
+ Constant cst = values.get(i);
+ out.writeInt(((CstLiteral32) cst).getIntBits());
+ }
+ break;
+ }
+ case 8: {
+ for (int i = 0; i < sz; i++) {
+ Constant cst = values.get(i);
+ out.writeLong(((CstLiteral64) cst).getLongBits());
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ // Pad one byte to make the size of data table multiples of 16-bits
+ if (elemWidth == 1 && (sz % 2 != 0)) {
+ out.writeByte(0x00);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public DalvInsn withRegisters(RegisterSpecList registers) {
+ return new ArrayData(getPosition(), user, values, arrayType);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected String argString() {
+ StringBuffer sb = new StringBuffer(100);
+
+ int sz = values.size();
+ for (int i = 0; i < sz; i++) {
+ sb.append("\n ");
+ sb.append(i);
+ sb.append(": ");
+ sb.append(values.get(i).toHuman());
+ }
+
+ return sb.toString();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected String listingString0(boolean noteIndices) {
+ int baseAddress = user.getAddress();
+ StringBuffer sb = new StringBuffer(100);
+ int sz = values.size();
+
+ sb.append("array-data // for fill-array-data @ ");
+ sb.append(Hex.u2(baseAddress));
+
+ for (int i = 0; i < sz; i++) {
+ sb.append("\n ");
+ sb.append(i);
+ sb.append(": ");
+ sb.append(values.get(i).toHuman());
+ }
+
+ return sb.toString();
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/BlockAddresses.java b/dx/src/com/android/dx/dex/code/BlockAddresses.java
new file mode 100644
index 0000000..1a1d184
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/BlockAddresses.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.BasicBlock;
+import com.android.dx.rop.code.BasicBlockList;
+import com.android.dx.rop.code.Insn;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.code.SourcePosition;
+
+/**
+ * Container for the set of {@link CodeAddress} instances associated with
+ * the blocks of a particular method. Each block has a corresponding
+ * start address, end address, and last instruction address.
+ */
+public final class BlockAddresses {
+ /** {@code non-null;} array containing addresses for the start of each basic
+ * block (indexed by basic block label) */
+ private final CodeAddress[] starts;
+
+ /** {@code non-null;} array containing addresses for the final instruction
+ * of each basic block (indexed by basic block label) */
+ private final CodeAddress[] lasts;
+
+ /** {@code non-null;} array containing addresses for the end (just past the
+ * final instruction) of each basic block (indexed by basic block
+ * label) */
+ private final CodeAddress[] ends;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param method {@code non-null;} the method to have block addresses for
+ */
+ public BlockAddresses(RopMethod method) {
+ BasicBlockList blocks = method.getBlocks();
+ int maxLabel = blocks.getMaxLabel();
+
+ this.starts = new CodeAddress[maxLabel];
+ this.lasts = new CodeAddress[maxLabel];
+ this.ends = new CodeAddress[maxLabel];
+
+ setupArrays(method);
+ }
+
+ /**
+ * Gets the instance for the start of the given block.
+ *
+ * @param block {@code non-null;} the block in question
+ * @return {@code non-null;} the appropriate instance
+ */
+ public CodeAddress getStart(BasicBlock block) {
+ return starts[block.getLabel()];
+ }
+
+ /**
+ * Gets the instance for the start of the block with the given label.
+ *
+ * @param label {@code non-null;} the label of the block in question
+ * @return {@code non-null;} the appropriate instance
+ */
+ public CodeAddress getStart(int label) {
+ return starts[label];
+ }
+
+ /**
+ * Gets the instance for the final instruction of the given block.
+ *
+ * @param block {@code non-null;} the block in question
+ * @return {@code non-null;} the appropriate instance
+ */
+ public CodeAddress getLast(BasicBlock block) {
+ return lasts[block.getLabel()];
+ }
+
+ /**
+ * Gets the instance for the final instruction of the block with
+ * the given label.
+ *
+ * @param label {@code non-null;} the label of the block in question
+ * @return {@code non-null;} the appropriate instance
+ */
+ public CodeAddress getLast(int label) {
+ return lasts[label];
+ }
+
+ /**
+ * Gets the instance for the end (address after the final instruction)
+ * of the given block.
+ *
+ * @param block {@code non-null;} the block in question
+ * @return {@code non-null;} the appropriate instance
+ */
+ public CodeAddress getEnd(BasicBlock block) {
+ return ends[block.getLabel()];
+ }
+
+ /**
+ * Gets the instance for the end (address after the final instruction)
+ * of the block with the given label.
+ *
+ * @param label {@code non-null;} the label of the block in question
+ * @return {@code non-null;} the appropriate instance
+ */
+ public CodeAddress getEnd(int label) {
+ return ends[label];
+ }
+
+ /**
+ * Sets up the address arrays.
+ */
+ private void setupArrays(RopMethod method) {
+ BasicBlockList blocks = method.getBlocks();
+ int sz = blocks.size();
+
+ for (int i = 0; i < sz; i++) {
+ BasicBlock one = blocks.get(i);
+ int label = one.getLabel();
+ Insn insn = one.getInsns().get(0);
+
+ starts[label] = new CodeAddress(insn.getPosition());
+
+ SourcePosition pos = one.getLastInsn().getPosition();
+
+ lasts[label] = new CodeAddress(pos);
+ ends[label] = new CodeAddress(pos);
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/CatchBuilder.java b/dx/src/com/android/dx/dex/code/CatchBuilder.java
new file mode 100644
index 0000000..90d2e8d
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/CatchBuilder.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.type.Type;
+
+import java.util.HashSet;
+
+/**
+ * Interface for the construction of {@link CatchTable} instances.
+ */
+public interface CatchBuilder {
+ /**
+ * Builds and returns the catch table for this instance.
+ *
+ * @return {@code non-null;} the constructed table
+ */
+ public CatchTable build();
+
+ /**
+ * Gets whether this instance has any catches at all (either typed
+ * or catch-all).
+ *
+ * @return whether this instance has any catches at all
+ */
+ public boolean hasAnyCatches();
+
+ /**
+ * Gets the set of catch types associated with this instance.
+ *
+ * @return {@code non-null;} the set of catch types
+ */
+ public HashSet<Type> getCatchTypes();
+}
diff --git a/dx/src/com/android/dx/dex/code/CatchHandlerList.java b/dx/src/com/android/dx/dex/code/CatchHandlerList.java
new file mode 100644
index 0000000..8472584
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/CatchHandlerList.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.util.FixedSizeList;
+import com.android.dx.util.Hex;
+
+/**
+ * Ordered list of (exception type, handler address) entries.
+ */
+public final class CatchHandlerList extends FixedSizeList
+ implements Comparable<CatchHandlerList> {
+ /** {@code non-null;} empty instance */
+ public static final CatchHandlerList EMPTY = new CatchHandlerList(0);
+
+ /**
+ * Constructs an instance. All indices initially contain {@code null}.
+ *
+ * @param size {@code >= 0;} the size of the list
+ */
+ public CatchHandlerList(int size) {
+ super(size);
+ }
+
+ /**
+ * Gets the element at the given index. It is an error to call
+ * this with the index for an element which was never set; if you
+ * do that, this will throw {@code NullPointerException}.
+ *
+ * @param n {@code >= 0, < size();} which index
+ * @return {@code non-null;} element at that index
+ */
+ public Entry get(int n) {
+ return (Entry) get0(n);
+ }
+
+ /** {@inheritDoc} */
+ public String toHuman() {
+ return toHuman("", "");
+ }
+
+ /**
+ * Get the human form of this instance, prefixed on each line
+ * with the string.
+ *
+ * @param prefix {@code non-null;} the prefix for every line
+ * @param header {@code non-null;} the header for the first line (after the
+ * first prefix)
+ * @return {@code non-null;} the human form
+ */
+ public String toHuman(String prefix, String header) {
+ StringBuilder sb = new StringBuilder(100);
+ int size = size();
+
+ sb.append(prefix);
+ sb.append(header);
+ sb.append("catch ");
+
+ for (int i = 0; i < size; i++) {
+ Entry entry = get(i);
+
+ if (i != 0) {
+ sb.append(",\n");
+ sb.append(prefix);
+ sb.append(" ");
+ }
+
+ if ((i == (size - 1)) && catchesAll()) {
+ sb.append("<any>");
+ } else {
+ sb.append(entry.getExceptionType().toHuman());
+ }
+
+ sb.append(" -> ");
+ sb.append(Hex.u2or4(entry.getHandler()));
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Returns whether or not this instance ends with a "catch-all"
+ * handler.
+ *
+ * @return {@code true} if this instance ends with a "catch-all"
+ * handler or {@code false} if not
+ */
+ public boolean catchesAll() {
+ int size = size();
+
+ if (size == 0) {
+ return false;
+ }
+
+ Entry last = get(size - 1);
+ return last.getExceptionType().equals(CstType.OBJECT);
+ }
+
+ /**
+ * Sets the entry at the given index.
+ *
+ * @param n {@code >= 0, < size();} which index
+ * @param exceptionType {@code non-null;} type of exception handled
+ * @param handler {@code >= 0;} exception handler address
+ */
+ public void set(int n, CstType exceptionType, int handler) {
+ set0(n, new Entry(exceptionType, handler));
+ }
+
+ /**
+ * Sets the entry at the given index.
+ *
+ * @param n {@code >= 0, < size();} which index
+ * @param entry {@code non-null;} the entry to set at {@code n}
+ */
+ public void set(int n, Entry entry) {
+ set0(n, entry);
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(CatchHandlerList other) {
+ if (this == other) {
+ // Easy out.
+ return 0;
+ }
+
+ int thisSize = size();
+ int otherSize = other.size();
+ int checkSize = Math.min(thisSize, otherSize);
+
+ for (int i = 0; i < checkSize; i++) {
+ Entry thisEntry = get(i);
+ Entry otherEntry = other.get(i);
+ int compare = thisEntry.compareTo(otherEntry);
+ if (compare != 0) {
+ return compare;
+ }
+ }
+
+ if (thisSize < otherSize) {
+ return -1;
+ } else if (thisSize > otherSize) {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ /**
+ * Entry in the list.
+ */
+ public static class Entry implements Comparable<Entry> {
+ /** {@code non-null;} type of exception handled */
+ private final CstType exceptionType;
+
+ /** {@code >= 0;} exception handler address */
+ private final int handler;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param exceptionType {@code non-null;} type of exception handled
+ * @param handler {@code >= 0;} exception handler address
+ */
+ public Entry(CstType exceptionType, int handler) {
+ if (handler < 0) {
+ throw new IllegalArgumentException("handler < 0");
+ }
+
+ if (exceptionType == null) {
+ throw new NullPointerException("exceptionType == null");
+ }
+
+ this.handler = handler;
+ this.exceptionType = exceptionType;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return (handler * 31) + exceptionType.hashCode();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof Entry) {
+ return (compareTo((Entry) other) == 0);
+ }
+
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(Entry other) {
+ if (handler < other.handler) {
+ return -1;
+ } else if (handler > other.handler) {
+ return 1;
+ }
+
+ return exceptionType.compareTo(other.exceptionType);
+ }
+
+ /**
+ * Gets the exception type handled.
+ *
+ * @return {@code non-null;} the exception type
+ */
+ public CstType getExceptionType() {
+ return exceptionType;
+ }
+
+ /**
+ * Gets the handler address.
+ *
+ * @return {@code >= 0;} the handler address
+ */
+ public int getHandler() {
+ return handler;
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/CatchTable.java b/dx/src/com/android/dx/dex/code/CatchTable.java
new file mode 100644
index 0000000..0ee890f
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/CatchTable.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * Table of catch entries. Each entry includes a range of code
+ * addresses for which it is valid and an associated {@link
+ * CatchHandlerList}.
+ */
+public final class CatchTable extends FixedSizeList
+ implements Comparable<CatchTable> {
+ /** {@code non-null;} empty instance */
+ public static final CatchTable EMPTY = new CatchTable(0);
+
+ /**
+ * Constructs an instance. All indices initially contain {@code null}.
+ *
+ * @param size {@code >= 0;} the size of the table
+ */
+ public CatchTable(int size) {
+ super(size);
+ }
+
+ /**
+ * Gets the element at the given index. It is an error to call
+ * this with the index for an element which was never set; if you
+ * do that, this will throw {@code NullPointerException}.
+ *
+ * @param n {@code >= 0, < size();} which index
+ * @return {@code non-null;} element at that index
+ */
+ public Entry get(int n) {
+ return (Entry) get0(n);
+ }
+
+ /**
+ * Sets the entry at the given index.
+ *
+ * @param n {@code >= 0, < size();} which index
+ * @param entry {@code non-null;} the entry to set at {@code n}
+ */
+ public void set(int n, Entry entry) {
+ set0(n, entry);
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(CatchTable other) {
+ if (this == other) {
+ // Easy out.
+ return 0;
+ }
+
+ int thisSize = size();
+ int otherSize = other.size();
+ int checkSize = Math.min(thisSize, otherSize);
+
+ for (int i = 0; i < checkSize; i++) {
+ Entry thisEntry = get(i);
+ Entry otherEntry = other.get(i);
+ int compare = thisEntry.compareTo(otherEntry);
+ if (compare != 0) {
+ return compare;
+ }
+ }
+
+ if (thisSize < otherSize) {
+ return -1;
+ } else if (thisSize > otherSize) {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ /**
+ * Entry in a catch list.
+ */
+ public static class Entry implements Comparable<Entry> {
+ /** {@code >= 0;} start address */
+ private final int start;
+
+ /** {@code > start;} end address (exclusive) */
+ private final int end;
+
+ /** {@code non-null;} list of catch handlers */
+ private final CatchHandlerList handlers;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param start {@code >= 0;} start address
+ * @param end {@code > start;} end address (exclusive)
+ * @param handlers {@code non-null;} list of catch handlers
+ */
+ public Entry(int start, int end, CatchHandlerList handlers) {
+ if (start < 0) {
+ throw new IllegalArgumentException("start < 0");
+ }
+
+ if (end <= start) {
+ throw new IllegalArgumentException("end <= start");
+ }
+
+ if (handlers.isMutable()) {
+ throw new IllegalArgumentException("handlers.isMutable()");
+ }
+
+ this.start = start;
+ this.end = end;
+ this.handlers = handlers;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ int hash = (start * 31) + end;
+ hash = (hash * 31) + handlers.hashCode();
+ return hash;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof Entry) {
+ return (compareTo((Entry) other) == 0);
+ }
+
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(Entry other) {
+ if (start < other.start) {
+ return -1;
+ } else if (start > other.start) {
+ return 1;
+ }
+
+ if (end < other.end) {
+ return -1;
+ } else if (end > other.end) {
+ return 1;
+ }
+
+ return handlers.compareTo(other.handlers);
+ }
+
+ /**
+ * Gets the start address.
+ *
+ * @return {@code >= 0;} the start address
+ */
+ public int getStart() {
+ return start;
+ }
+
+ /**
+ * Gets the end address (exclusive).
+ *
+ * @return {@code > start;} the end address (exclusive)
+ */
+ public int getEnd() {
+ return end;
+ }
+
+ /**
+ * Gets the handlers.
+ *
+ * @return {@code non-null;} the handlers
+ */
+ public CatchHandlerList getHandlers() {
+ return handlers;
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/CodeAddress.java b/dx/src/com/android/dx/dex/code/CodeAddress.java
new file mode 100644
index 0000000..5d26bd1
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/CodeAddress.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+
+/**
+ * Pseudo-instruction which is used to track an address within a code
+ * array. Instances are used for such things as branch targets and
+ * exception handler ranges. Its code size is zero, and so instances
+ * do not in general directly wind up in any output (either
+ * human-oriented or binary file).
+ */
+public final class CodeAddress extends ZeroSizeInsn {
+ /**
+ * Constructs an instance. The output address of this instance is initially
+ * unknown ({@code -1}).
+ *
+ * @param position {@code non-null;} source position
+ */
+ public CodeAddress(SourcePosition position) {
+ super(position);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final DalvInsn withRegisters(RegisterSpecList registers) {
+ return new CodeAddress(getPosition());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected String argString() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected String listingString0(boolean noteIndices) {
+ return "code-address";
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/CstInsn.java b/dx/src/com/android/dx/dex/code/CstInsn.java
new file mode 100644
index 0000000..3f848c0
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/CstInsn.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.rop.cst.Constant;
+
+/**
+ * Instruction which has a single constant argument in addition
+ * to all the normal instruction information.
+ */
+public final class CstInsn extends FixedSizeInsn {
+ /** {@code non-null;} the constant argument for this instruction */
+ private final Constant constant;
+
+ /**
+ * {@code >= -1;} the constant pool index for {@link #constant}, or
+ * {@code -1} if not yet set
+ */
+ private int index;
+
+ /**
+ * {@code >= -1;} the constant pool index for the class reference in
+ * {@link #constant} if any, or {@code -1} if not yet set
+ */
+ private int classIndex;
+
+ /**
+ * Constructs an instance. The output address of this instance is
+ * initially unknown ({@code -1}) as is the constant pool index.
+ *
+ * @param opcode the opcode; one of the constants from {@link Dops}
+ * @param position {@code non-null;} source position
+ * @param registers {@code non-null;} register list, including a
+ * result register if appropriate (that is, registers may be either
+ * ins or outs)
+ * @param constant {@code non-null;} constant argument
+ */
+ public CstInsn(Dop opcode, SourcePosition position,
+ RegisterSpecList registers, Constant constant) {
+ super(opcode, position, registers);
+
+ if (constant == null) {
+ throw new NullPointerException("constant == null");
+ }
+
+ this.constant = constant;
+ this.index = -1;
+ this.classIndex = -1;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public DalvInsn withOpcode(Dop opcode) {
+ CstInsn result =
+ new CstInsn(opcode, getPosition(), getRegisters(), constant);
+
+ if (index >= 0) {
+ result.setIndex(index);
+ }
+
+ if (classIndex >= 0) {
+ result.setClassIndex(classIndex);
+ }
+
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public DalvInsn withRegisters(RegisterSpecList registers) {
+ CstInsn result =
+ new CstInsn(getOpcode(), getPosition(), registers, constant);
+
+ if (index >= 0) {
+ result.setIndex(index);
+ }
+
+ if (classIndex >= 0) {
+ result.setClassIndex(classIndex);
+ }
+
+ return result;
+ }
+
+ /**
+ * Gets the constant argument.
+ *
+ * @return {@code non-null;} the constant argument
+ */
+ public Constant getConstant() {
+ return constant;
+ }
+
+ /**
+ * Gets the constant's index. It is only valid to call this after
+ * {@link #setIndex} has been called.
+ *
+ * @return {@code >= 0;} the constant pool index
+ */
+ public int getIndex() {
+ if (index < 0) {
+ throw new RuntimeException("index not yet set for " + constant);
+ }
+
+ return index;
+ }
+
+ /**
+ * Returns whether the constant's index has been set for this instance.
+ *
+ * @see #setIndex
+ *
+ * @return {@code true} iff the index has been set
+ */
+ public boolean hasIndex() {
+ return (index >= 0);
+ }
+
+ /**
+ * Sets the constant's index. It is only valid to call this method once
+ * per instance.
+ *
+ * @param index {@code >= 0;} the constant pool index
+ */
+ public void setIndex(int index) {
+ if (index < 0) {
+ throw new IllegalArgumentException("index < 0");
+ }
+
+ if (this.index >= 0) {
+ throw new RuntimeException("index already set");
+ }
+
+ this.index = index;
+ }
+
+ /**
+ * Gets the constant's class index. It is only valid to call this after
+ * {@link #setClassIndex} has been called.
+ *
+ * @return {@code >= 0;} the constant's class's constant pool index
+ */
+ public int getClassIndex() {
+ if (classIndex < 0) {
+ throw new RuntimeException("class index not yet set");
+ }
+
+ return classIndex;
+ }
+
+ /**
+ * Returns whether the constant's class index has been set for this
+ * instance.
+ *
+ * @see #setClassIndex
+ *
+ * @return {@code true} iff the index has been set
+ */
+ public boolean hasClassIndex() {
+ return (classIndex >= 0);
+ }
+
+ /**
+ * Sets the constant's class index. This is the constant pool index
+ * for the class referred to by this instance's constant. Only
+ * reference constants have a class, so it is only on instances
+ * with reference constants that this method should ever be
+ * called. It is only valid to call this method once per instance.
+ *
+ * @param index {@code >= 0;} the constant's class's constant pool index
+ */
+ public void setClassIndex(int index) {
+ if (index < 0) {
+ throw new IllegalArgumentException("index < 0");
+ }
+
+ if (this.classIndex >= 0) {
+ throw new RuntimeException("class index already set");
+ }
+
+ this.classIndex = index;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected String argString() {
+ return constant.toHuman();
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/DalvCode.java b/dx/src/com/android/dx/dex/code/DalvCode.java
new file mode 100644
index 0000000..58f191b
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/DalvCode.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.type.Type;
+
+import java.util.HashSet;
+
+/**
+ * Container for all the pieces of a concrete method. Each instance
+ * corresponds to a {@code code} structure in a {@code .dex} file.
+ */
+public final class DalvCode {
+ /**
+ * how much position info to preserve; one of the static
+ * constants in {@link PositionList}
+ */
+ private final int positionInfo;
+
+ /**
+ * {@code null-ok;} the instruction list, ready for final processing;
+ * nulled out in {@link #finishProcessingIfNecessary}
+ */
+ private OutputFinisher unprocessedInsns;
+
+ /**
+ * {@code non-null;} unprocessed catch table;
+ * nulled out in {@link #finishProcessingIfNecessary}
+ */
+ private CatchBuilder unprocessedCatches;
+
+ /**
+ * {@code null-ok;} catch table; set in
+ * {@link #finishProcessingIfNecessary}
+ */
+ private CatchTable catches;
+
+ /**
+ * {@code null-ok;} source positions list; set in
+ * {@link #finishProcessingIfNecessary}
+ */
+ private PositionList positions;
+
+ /**
+ * {@code null-ok;} local variable list; set in
+ * {@link #finishProcessingIfNecessary}
+ */
+ private LocalList locals;
+
+ /**
+ * {@code null-ok;} the processed instruction list; set in
+ * {@link #finishProcessingIfNecessary}
+ */
+ private DalvInsnList insns;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param positionInfo how much position info to preserve; one of the
+ * static constants in {@link PositionList}
+ * @param unprocessedInsns {@code non-null;} the instruction list, ready
+ * for final processing
+ * @param unprocessedCatches {@code non-null;} unprocessed catch
+ * (exception handler) table
+ */
+ public DalvCode(int positionInfo, OutputFinisher unprocessedInsns,
+ CatchBuilder unprocessedCatches) {
+ if (unprocessedInsns == null) {
+ throw new NullPointerException("unprocessedInsns == null");
+ }
+
+ if (unprocessedCatches == null) {
+ throw new NullPointerException("unprocessedCatches == null");
+ }
+
+ this.positionInfo = positionInfo;
+ this.unprocessedInsns = unprocessedInsns;
+ this.unprocessedCatches = unprocessedCatches;
+ this.catches = null;
+ this.positions = null;
+ this.locals = null;
+ this.insns = null;
+ }
+
+ /**
+ * Finish up processing of the method.
+ */
+ private void finishProcessingIfNecessary() {
+ if (insns != null) {
+ return;
+ }
+
+ insns = unprocessedInsns.finishProcessingAndGetList();
+ positions = PositionList.make(insns, positionInfo);
+ locals = LocalList.make(insns);
+ catches = unprocessedCatches.build();
+
+ // Let them be gc'ed.
+ unprocessedInsns = null;
+ unprocessedCatches = null;
+ }
+
+ /**
+ * Assign indices in all instructions that need them, using the
+ * given callback to perform lookups. This must be called before
+ * {@link #getInsns}.
+ *
+ * @param callback {@code non-null;} callback object
+ */
+ public void assignIndices(AssignIndicesCallback callback) {
+ unprocessedInsns.assignIndices(callback);
+ }
+
+ /**
+ * Gets whether this instance has any position data to represent.
+ *
+ * @return {@code true} iff this instance has any position
+ * data to represent
+ */
+ public boolean hasPositions() {
+ return (positionInfo != PositionList.NONE)
+ && unprocessedInsns.hasAnyPositionInfo();
+ }
+
+ /**
+ * Gets whether this instance has any local variable data to represent.
+ *
+ * @return {@code true} iff this instance has any local variable
+ * data to represent
+ */
+ public boolean hasLocals() {
+ return unprocessedInsns.hasAnyLocalInfo();
+ }
+
+ /**
+ * Gets whether this instance has any catches at all (either typed
+ * or catch-all).
+ *
+ * @return whether this instance has any catches at all
+ */
+ public boolean hasAnyCatches() {
+ return unprocessedCatches.hasAnyCatches();
+ }
+
+ /**
+ * Gets the set of catch types handled anywhere in the code.
+ *
+ * @return {@code non-null;} the set of catch types
+ */
+ public HashSet<Type> getCatchTypes() {
+ return unprocessedCatches.getCatchTypes();
+ }
+
+ /**
+ * Gets the set of all constants referred to by instructions in
+ * the code.
+ *
+ * @return {@code non-null;} the set of constants
+ */
+ public HashSet<Constant> getInsnConstants() {
+ return unprocessedInsns.getAllConstants();
+ }
+
+ /**
+ * Gets the list of instructions.
+ *
+ * @return {@code non-null;} the instruction list
+ */
+ public DalvInsnList getInsns() {
+ finishProcessingIfNecessary();
+ return insns;
+ }
+
+ /**
+ * Gets the catch (exception handler) table.
+ *
+ * @return {@code non-null;} the catch table
+ */
+ public CatchTable getCatches() {
+ finishProcessingIfNecessary();
+ return catches;
+ }
+
+ /**
+ * Gets the source positions list.
+ *
+ * @return {@code non-null;} the source positions list
+ */
+ public PositionList getPositions() {
+ finishProcessingIfNecessary();
+ return positions;
+ }
+
+ /**
+ * Gets the source positions list.
+ *
+ * @return {@code non-null;} the source positions list
+ */
+ public LocalList getLocals() {
+ finishProcessingIfNecessary();
+ return locals;
+ }
+
+ /**
+ * Class used as a callback for {@link #assignIndices}.
+ */
+ public static interface AssignIndicesCallback {
+ /**
+ * Gets the index for the given constant.
+ *
+ * @param cst {@code non-null;} the constant
+ * @return {@code >= -1;} the index or {@code -1} if the constant
+ * shouldn't actually be reified with an index
+ */
+ public int getIndex(Constant cst);
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/DalvInsn.java b/dx/src/com/android/dx/dex/code/DalvInsn.java
new file mode 100644
index 0000000..f203817
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/DalvInsn.java
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+import com.android.dx.util.TwoColumnOutput;
+
+/**
+ * Base class for Dalvik instructions.
+ */
+public abstract class DalvInsn {
+ /**
+ * the actual output address of this instance, if known, or
+ * {@code -1} if not
+ */
+ private int address;
+
+ /** the opcode; one of the constants from {@link Dops} */
+ private final Dop opcode;
+
+ /** {@code non-null;} source position */
+ private final SourcePosition position;
+
+ /** {@code non-null;} list of register arguments */
+ private final RegisterSpecList registers;
+
+ /**
+ * Makes a move instruction, appropriate and ideal for the given arguments.
+ *
+ * @param position {@code non-null;} source position information
+ * @param dest {@code non-null;} destination register
+ * @param src {@code non-null;} source register
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public static SimpleInsn makeMove(SourcePosition position,
+ RegisterSpec dest, RegisterSpec src) {
+ boolean category1 = dest.getCategory() == 1;
+ boolean reference = dest.getType().isReference();
+ int destReg = dest.getReg();
+ int srcReg = src.getReg();
+ Dop opcode;
+
+ if ((srcReg | destReg) < 16) {
+ opcode = reference ? Dops.MOVE_OBJECT :
+ (category1 ? Dops.MOVE : Dops.MOVE_WIDE);
+ } else if (destReg < 256) {
+ opcode = reference ? Dops.MOVE_OBJECT_FROM16 :
+ (category1 ? Dops.MOVE_FROM16 : Dops.MOVE_WIDE_FROM16);
+ } else {
+ opcode = reference ? Dops.MOVE_OBJECT_16 :
+ (category1 ? Dops.MOVE_16 : Dops.MOVE_WIDE_16);
+ }
+
+ return new SimpleInsn(opcode, position,
+ RegisterSpecList.make(dest, src));
+ }
+
+ /**
+ * Constructs an instance. The output address of this instance is initially
+ * unknown ({@code -1}).
+ *
+ * <p><b>Note:</b> In the unlikely event that an instruction takes
+ * absolutely no registers (e.g., a {@code nop} or a
+ * no-argument no-result static method call), then the given
+ * register list may be passed as {@link
+ * RegisterSpecList#EMPTY}.</p>
+ *
+ * @param opcode the opcode; one of the constants from {@link Dops}
+ * @param position {@code non-null;} source position
+ * @param registers {@code non-null;} register list, including a
+ * result register if appropriate (that is, registers may be either
+ * ins and outs)
+ */
+ public DalvInsn(Dop opcode, SourcePosition position,
+ RegisterSpecList registers) {
+ if (opcode == null) {
+ throw new NullPointerException("opcode == null");
+ }
+
+ if (position == null) {
+ throw new NullPointerException("position == null");
+ }
+
+ if (registers == null) {
+ throw new NullPointerException("registers == null");
+ }
+
+ this.address = -1;
+ this.opcode = opcode;
+ this.position = position;
+ this.registers = registers;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final String toString() {
+ StringBuffer sb = new StringBuffer(100);
+
+ sb.append(identifierString());
+ sb.append(' ');
+ sb.append(position);
+
+ sb.append(": ");
+ sb.append(opcode.getName());
+
+ boolean needComma = false;
+ if (registers.size() != 0) {
+ sb.append(registers.toHuman(" ", ", ", null));
+ needComma = true;
+ }
+
+ String extra = argString();
+ if (extra != null) {
+ if (needComma) {
+ sb.append(',');
+ }
+ sb.append(' ');
+ sb.append(extra);
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Gets whether the address of this instruction is known.
+ *
+ * @see #getAddress
+ * @see #setAddress
+ */
+ public final boolean hasAddress() {
+ return (address >= 0);
+ }
+
+ /**
+ * Gets the output address of this instruction, if it is known. This throws
+ * a {@code RuntimeException} if it has not yet been set.
+ *
+ * @see #setAddress
+ *
+ * @return {@code >= 0;} the output address
+ */
+ public final int getAddress() {
+ if (address < 0) {
+ throw new RuntimeException("address not yet known");
+ }
+
+ return address;
+ }
+
+ /**
+ * Gets the opcode.
+ *
+ * @return {@code non-null;} the opcode
+ */
+ public final Dop getOpcode() {
+ return opcode;
+ }
+
+ /**
+ * Gets the source position.
+ *
+ * @return {@code non-null;} the source position
+ */
+ public final SourcePosition getPosition() {
+ return position;
+ }
+
+ /**
+ * Gets the register list for this instruction.
+ *
+ * @return {@code non-null;} the registers
+ */
+ public final RegisterSpecList getRegisters() {
+ return registers;
+ }
+
+ /**
+ * Returns whether this instance's opcode uses a result register.
+ * This method is a convenient shorthand for
+ * {@code getOpcode().hasResult()}.
+ *
+ * @return {@code true} iff this opcode uses a result register
+ */
+ public final boolean hasResult() {
+ return opcode.hasResult();
+ }
+
+ /**
+ * Gets the minimum distinct registers required for this instruction.
+ * This assumes that the result (if any) can share registers with the
+ * sources (if any), that each source register is unique, and that
+ * (to be explicit here) category-2 values take up two consecutive
+ * registers.
+ *
+ * @return {@code >= 0;} the minimum distinct register requirement
+ */
+ public final int getMinimumRegisterRequirement() {
+ boolean hasResult = hasResult();
+ int regSz = registers.size();
+ int resultRequirement = hasResult ? registers.get(0).getCategory() : 0;
+ int sourceRequirement = 0;
+
+ for (int i = hasResult ? 1 : 0; i < regSz; i++) {
+ sourceRequirement += registers.get(i).getCategory();
+ }
+
+ return Math.max(sourceRequirement, resultRequirement);
+ }
+
+ /**
+ * Gets the instruction prefix required, if any, to use in a high
+ * register transformed version of this instance.
+ *
+ * @see #hrVersion
+ *
+ * @return {@code null-ok;} the prefix, if any
+ */
+ public DalvInsn hrPrefix() {
+ RegisterSpecList regs = registers;
+ int sz = regs.size();
+
+ if (hasResult()) {
+ if (sz == 1) {
+ return null;
+ }
+ regs = regs.withoutFirst();
+ } else if (sz == 0) {
+ return null;
+ }
+
+ return new HighRegisterPrefix(position, regs);
+ }
+
+ /**
+ * Gets the instruction suffix required, if any, to use in a high
+ * register transformed version of this instance.
+ *
+ * @see #hrVersion
+ *
+ * @return {@code null-ok;} the suffix, if any
+ */
+ public DalvInsn hrSuffix() {
+ if (hasResult()) {
+ RegisterSpec r = registers.get(0);
+ return makeMove(position, r, r.withReg(0));
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Gets the instruction that is equivalent to this one, except that
+ * uses sequential registers starting at {@code 0} (storing
+ * the result, if any, in register {@code 0} as well). The
+ * sequence of instructions from {@link #hrPrefix} and {@link
+ * #hrSuffix} (if non-null) surrounding the result of a call to
+ * this method are the high register transformation of this
+ * instance, and it is guaranteed that the number of low registers
+ * used will be the number returned by {@link
+ * #getMinimumRegisterRequirement}.
+ *
+ * @return {@code non-null;} the replacement
+ */
+ public DalvInsn hrVersion() {
+ RegisterSpecList regs =
+ registers.withSequentialRegisters(0, hasResult());
+ return withRegisters(regs);
+ }
+
+ /**
+ * Gets the short identifier for this instruction. This is its
+ * address, if assigned, or its identity hashcode if not.
+ *
+ * @return {@code non-null;} the identifier
+ */
+ public final String identifierString() {
+ if (address != -1) {
+ return String.format("%04x", address);
+ }
+
+ return Hex.u4(System.identityHashCode(this));
+ }
+
+ /**
+ * Returns the string form of this instance suitable for inclusion in
+ * a human-oriented listing dump. This method will return {@code null}
+ * if this instance should not appear in a listing.
+ *
+ * @param prefix {@code non-null;} prefix before the address; each follow-on
+ * line will be indented to match as well
+ * @param width {@code >= 0;} the width of the output or {@code 0} for
+ * unlimited width
+ * @param noteIndices whether to include an explicit notation of
+ * constant pool indices
+ * @return {@code null-ok;} the string form or {@code null} if this
+ * instance should not appear in a listing
+ */
+ public final String listingString(String prefix, int width,
+ boolean noteIndices) {
+ String insnPerSe = listingString0(noteIndices);
+
+ if (insnPerSe == null) {
+ return null;
+ }
+
+ String addr = prefix + identifierString() + ": ";
+ int w1 = addr.length();
+ int w2 = (width == 0) ? insnPerSe.length() : (width - w1);
+
+ return TwoColumnOutput.toString(addr, w1, "", insnPerSe, w2);
+ }
+
+ /**
+ * Sets the output address.
+ *
+ * @param address {@code >= 0;} the output address
+ */
+ public final void setAddress(int address) {
+ if (address < 0) {
+ throw new IllegalArgumentException("address < 0");
+ }
+
+ this.address = address;
+ }
+
+ /**
+ * Gets the address immediately after this instance. This is only
+ * calculable if this instance's address is known, and it is equal
+ * to the address plus the length of the instruction format of this
+ * instance's opcode.
+ *
+ * @return {@code >= 0;} the next address
+ */
+ public final int getNextAddress() {
+ return getAddress() + codeSize();
+ }
+
+ /**
+ * Gets the size of this instruction, in 16-bit code units.
+ *
+ * @return {@code >= 0;} the code size of this instruction
+ */
+ public abstract int codeSize();
+
+ /**
+ * Writes this instance to the given output. This method should
+ * never annotate the output.
+ *
+ * @param out {@code non-null;} where to write to
+ */
+ public abstract void writeTo(AnnotatedOutput out);
+
+ /**
+ * Returns an instance that is just like this one, except that its
+ * opcode is replaced by the one given, and its address is reset.
+ *
+ * @param opcode {@code non-null;} the new opcode
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public abstract DalvInsn withOpcode(Dop opcode);
+
+ /**
+ * Returns an instance that is just like this one, except that all
+ * register references have been offset by the given delta, and its
+ * address is reset.
+ *
+ * @param delta the amount to offset register references by
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public abstract DalvInsn withRegisterOffset(int delta);
+
+ /**
+ * Returns an instance that is just like this one, except that the
+ * register list is replaced by the given one, and its address is
+ * reset.
+ *
+ * @param registers {@code non-null;} new register list
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public abstract DalvInsn withRegisters(RegisterSpecList registers);
+
+ /**
+ * Gets the string form for any arguments to this instance. Subclasses
+ * must override this.
+ *
+ * @return {@code null-ok;} the string version of any arguments or
+ * {@code null} if there are none
+ */
+ protected abstract String argString();
+
+ /**
+ * Helper for {@link #listingString}, which returns the string
+ * form of this instance suitable for inclusion in a
+ * human-oriented listing dump, not including the instruction
+ * address and without respect for any output formatting. This
+ * method should return {@code null} if this instance should
+ * not appear in a listing.
+ *
+ * @param noteIndices whether to include an explicit notation of
+ * constant pool indices
+ * @return {@code null-ok;} the listing string
+ */
+ protected abstract String listingString0(boolean noteIndices);
+}
diff --git a/dx/src/com/android/dx/dex/code/DalvInsnList.java b/dx/src/com/android/dx/dex/code/DalvInsnList.java
new file mode 100644
index 0000000..0f8c23d
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/DalvInsnList.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstBaseMethodRef;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.ExceptionWithContext;
+import com.android.dx.util.FixedSizeList;
+import com.android.dx.util.IndentingWriter;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+
+/**
+ * List of {@link DalvInsn} instances.
+ */
+public final class DalvInsnList extends FixedSizeList {
+
+ /**
+ * The amount of register space, in register units, required for this
+ * code block. This may be greater than the largest observed register+
+ * category because the method this code block exists in may
+ * specify arguments that are unused by the method.
+ */
+ private final int regCount;
+
+ /**
+ * Constructs and returns an immutable instance whose elements are
+ * identical to the ones in the given list, in the same order.
+ *
+ * @param list {@code non-null;} the list to use for elements
+ * @param regCount count, in register-units, of the number of registers
+ * this code block requires.
+ * @return {@code non-null;} an appropriately-constructed instance of this
+ * class
+ */
+ public static DalvInsnList makeImmutable(ArrayList<DalvInsn> list,
+ int regCount) {
+ int size = list.size();
+ DalvInsnList result = new DalvInsnList(size, regCount);
+
+ for (int i = 0; i < size; i++) {
+ result.set(i, list.get(i));
+ }
+
+ result.setImmutable();
+ return result;
+ }
+
+ /**
+ * Constructs an instance. All indices initially contain {@code null}.
+ *
+ * @param size the size of the list
+ */
+ public DalvInsnList(int size, int regCount) {
+ super(size);
+ this.regCount = regCount;
+ }
+
+ /**
+ * Gets the element at the given index. It is an error to call
+ * this with the index for an element which was never set; if you
+ * do that, this will throw {@code NullPointerException}.
+ *
+ * @param n {@code >= 0, < size();} which index
+ * @return {@code non-null;} element at that index
+ */
+ public DalvInsn get(int n) {
+ return (DalvInsn) get0(n);
+ }
+
+ /**
+ * Sets the instruction at the given index.
+ *
+ * @param n {@code >= 0, < size();} which index
+ * @param insn {@code non-null;} the instruction to set at {@code n}
+ */
+ public void set(int n, DalvInsn insn) {
+ set0(n, insn);
+ }
+
+ /**
+ * Gets the size of this instance, in 16-bit code units. This will only
+ * return a meaningful result if the instructions in this instance all
+ * have valid addresses.
+ *
+ * @return {@code >= 0;} the size
+ */
+ public int codeSize() {
+ int sz = size();
+
+ if (sz == 0) {
+ return 0;
+ }
+
+ DalvInsn last = get(sz - 1);
+ return last.getNextAddress();
+ }
+
+ /**
+ * Writes all the instructions in this instance to the given output
+ * destination.
+ *
+ * @param out {@code non-null;} where to write to
+ */
+ public void writeTo(AnnotatedOutput out) {
+ int startCursor = out.getCursor();
+ int sz = size();
+
+ if (out.annotates()) {
+ boolean verbose = out.isVerbose();
+
+ for (int i = 0; i < sz; i++) {
+ DalvInsn insn = (DalvInsn) get0(i);
+ int codeBytes = insn.codeSize() * 2;
+ String s;
+
+ if ((codeBytes != 0) || verbose) {
+ s = insn.listingString(" ", out.getAnnotationWidth(),
+ true);
+ } else {
+ s = null;
+ }
+
+ if (s != null) {
+ out.annotate(codeBytes, s);
+ } else if (codeBytes != 0) {
+ out.annotate(codeBytes, "");
+ }
+ }
+ }
+
+ for (int i = 0; i < sz; i++) {
+ DalvInsn insn = (DalvInsn) get0(i);
+ try {
+ insn.writeTo(out);
+ } catch (RuntimeException ex) {
+ throw ExceptionWithContext.withContext(ex,
+ "...while writing " + insn);
+ }
+ }
+
+ // Sanity check of the amount written.
+ int written = (out.getCursor() - startCursor) / 2;
+ if (written != codeSize()) {
+ throw new RuntimeException("write length mismatch; expected " +
+ codeSize() + " but actually wrote " + written);
+ }
+ }
+
+ /**
+ * Gets the minimum required register count implied by this
+ * instance. This includes any unused parameters that could
+ * potentially be at the top of the register space.
+ * @return {@code >= 0;} the required registers size
+ */
+ public int getRegistersSize() {
+ return regCount;
+ }
+
+ /**
+ * Gets the size of the outgoing arguments area required by this
+ * method. This is equal to the largest argument word count of any
+ * method referred to by this instance.
+ *
+ * @return {@code >= 0;} the required outgoing arguments size
+ */
+ public int getOutsSize() {
+ int sz = size();
+ int result = 0;
+
+ for (int i = 0; i < sz; i++) {
+ DalvInsn insn = (DalvInsn) get0(i);
+
+ if (!(insn instanceof CstInsn)) {
+ continue;
+ }
+
+ Constant cst = ((CstInsn) insn).getConstant();
+
+ if (!(cst instanceof CstBaseMethodRef)) {
+ continue;
+ }
+
+ boolean isStatic =
+ (insn.getOpcode().getFamily() == DalvOps.INVOKE_STATIC);
+ int count =
+ ((CstBaseMethodRef) cst).getParameterWordCount(isStatic);
+
+ if (count > result) {
+ result = count;
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Does a human-friendly dump of this instance.
+ *
+ * @param out {@code non-null;} where to dump
+ * @param prefix {@code non-null;} prefix to attach to each line of output
+ * @param verbose whether to be verbose; verbose output includes
+ * lines for zero-size instructions and explicit constant pool indices
+ */
+ public void debugPrint(Writer out, String prefix, boolean verbose) {
+ IndentingWriter iw = new IndentingWriter(out, 0, prefix);
+ int sz = size();
+
+ try {
+ for (int i = 0; i < sz; i++) {
+ DalvInsn insn = (DalvInsn) get0(i);
+ String s;
+
+ if ((insn.codeSize() != 0) || verbose) {
+ s = insn.listingString("", 0, verbose);
+ } else {
+ s = null;
+ }
+
+ if (s != null) {
+ iw.write(s);
+ }
+ }
+
+ iw.flush();
+ } catch (IOException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ /**
+ * Does a human-friendly dump of this instance.
+ *
+ * @param out {@code non-null;} where to dump
+ * @param prefix {@code non-null;} prefix to attach to each line of output
+ * @param verbose whether to be verbose; verbose output includes
+ * lines for zero-size instructions
+ */
+ public void debugPrint(OutputStream out, String prefix, boolean verbose) {
+ Writer w = new OutputStreamWriter(out);
+ debugPrint(w, prefix, verbose);
+
+ try {
+ w.flush();
+ } catch (IOException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/DalvOps.java b/dx/src/com/android/dx/dex/code/DalvOps.java
new file mode 100644
index 0000000..7dc648e
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/DalvOps.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+/**
+ * All the Dalvik opcode value constants. See the related spec
+ * document for the meaning and instruction format of each opcode.
+ */
+public final class DalvOps {
+ /** pseudo-opcode used for nonstandard format "instructions" */
+ public static final int SPECIAL_FORMAT = -1;
+
+ /** minimum valid opcode value */
+ public static final int MIN_VALUE = -1;
+
+ /** maximum valid opcode value */
+ public static final int MAX_VALUE = 0xff;
+
+ // BEGIN(opcodes); GENERATED AUTOMATICALLY BY opcode-gen
+ public static final int NOP = 0x00;
+ public static final int MOVE = 0x01;
+ public static final int MOVE_FROM16 = 0x02;
+ public static final int MOVE_16 = 0x03;
+ public static final int MOVE_WIDE = 0x04;
+ public static final int MOVE_WIDE_FROM16 = 0x05;
+ public static final int MOVE_WIDE_16 = 0x06;
+ public static final int MOVE_OBJECT = 0x07;
+ public static final int MOVE_OBJECT_FROM16 = 0x08;
+ public static final int MOVE_OBJECT_16 = 0x09;
+ public static final int MOVE_RESULT = 0x0a;
+ public static final int MOVE_RESULT_WIDE = 0x0b;
+ public static final int MOVE_RESULT_OBJECT = 0x0c;
+ public static final int MOVE_EXCEPTION = 0x0d;
+ public static final int RETURN_VOID = 0x0e;
+ public static final int RETURN = 0x0f;
+ public static final int RETURN_WIDE = 0x10;
+ public static final int RETURN_OBJECT = 0x11;
+ public static final int CONST_4 = 0x12;
+ public static final int CONST_16 = 0x13;
+ public static final int CONST = 0x14;
+ public static final int CONST_HIGH16 = 0x15;
+ public static final int CONST_WIDE_16 = 0x16;
+ public static final int CONST_WIDE_32 = 0x17;
+ public static final int CONST_WIDE = 0x18;
+ public static final int CONST_WIDE_HIGH16 = 0x19;
+ public static final int CONST_STRING = 0x1a;
+ public static final int CONST_STRING_JUMBO = 0x1b;
+ public static final int CONST_CLASS = 0x1c;
+ public static final int MONITOR_ENTER = 0x1d;
+ public static final int MONITOR_EXIT = 0x1e;
+ public static final int CHECK_CAST = 0x1f;
+ public static final int INSTANCE_OF = 0x20;
+ public static final int ARRAY_LENGTH = 0x21;
+ public static final int NEW_INSTANCE = 0x22;
+ public static final int NEW_ARRAY = 0x23;
+ public static final int FILLED_NEW_ARRAY = 0x24;
+ public static final int FILLED_NEW_ARRAY_RANGE = 0x25;
+ public static final int FILL_ARRAY_DATA = 0x26;
+ public static final int THROW = 0x27;
+ public static final int GOTO = 0x28;
+ public static final int GOTO_16 = 0x29;
+ public static final int GOTO_32 = 0x2a;
+ public static final int PACKED_SWITCH = 0x2b;
+ public static final int SPARSE_SWITCH = 0x2c;
+ public static final int CMPL_FLOAT = 0x2d;
+ public static final int CMPG_FLOAT = 0x2e;
+ public static final int CMPL_DOUBLE = 0x2f;
+ public static final int CMPG_DOUBLE = 0x30;
+ public static final int CMP_LONG = 0x31;
+ public static final int IF_EQ = 0x32;
+ public static final int IF_NE = 0x33;
+ public static final int IF_LT = 0x34;
+ public static final int IF_GE = 0x35;
+ public static final int IF_GT = 0x36;
+ public static final int IF_LE = 0x37;
+ public static final int IF_EQZ = 0x38;
+ public static final int IF_NEZ = 0x39;
+ public static final int IF_LTZ = 0x3a;
+ public static final int IF_GEZ = 0x3b;
+ public static final int IF_GTZ = 0x3c;
+ public static final int IF_LEZ = 0x3d;
+ public static final int UNUSED_3E = 0x3e;
+ public static final int UNUSED_3F = 0x3f;
+ public static final int UNUSED_40 = 0x40;
+ public static final int UNUSED_41 = 0x41;
+ public static final int UNUSED_42 = 0x42;
+ public static final int UNUSED_43 = 0x43;
+ public static final int AGET = 0x44;
+ public static final int AGET_WIDE = 0x45;
+ public static final int AGET_OBJECT = 0x46;
+ public static final int AGET_BOOLEAN = 0x47;
+ public static final int AGET_BYTE = 0x48;
+ public static final int AGET_CHAR = 0x49;
+ public static final int AGET_SHORT = 0x4a;
+ public static final int APUT = 0x4b;
+ public static final int APUT_WIDE = 0x4c;
+ public static final int APUT_OBJECT = 0x4d;
+ public static final int APUT_BOOLEAN = 0x4e;
+ public static final int APUT_BYTE = 0x4f;
+ public static final int APUT_CHAR = 0x50;
+ public static final int APUT_SHORT = 0x51;
+ public static final int IGET = 0x52;
+ public static final int IGET_WIDE = 0x53;
+ public static final int IGET_OBJECT = 0x54;
+ public static final int IGET_BOOLEAN = 0x55;
+ public static final int IGET_BYTE = 0x56;
+ public static final int IGET_CHAR = 0x57;
+ public static final int IGET_SHORT = 0x58;
+ public static final int IPUT = 0x59;
+ public static final int IPUT_WIDE = 0x5a;
+ public static final int IPUT_OBJECT = 0x5b;
+ public static final int IPUT_BOOLEAN = 0x5c;
+ public static final int IPUT_BYTE = 0x5d;
+ public static final int IPUT_CHAR = 0x5e;
+ public static final int IPUT_SHORT = 0x5f;
+ public static final int SGET = 0x60;
+ public static final int SGET_WIDE = 0x61;
+ public static final int SGET_OBJECT = 0x62;
+ public static final int SGET_BOOLEAN = 0x63;
+ public static final int SGET_BYTE = 0x64;
+ public static final int SGET_CHAR = 0x65;
+ public static final int SGET_SHORT = 0x66;
+ public static final int SPUT = 0x67;
+ public static final int SPUT_WIDE = 0x68;
+ public static final int SPUT_OBJECT = 0x69;
+ public static final int SPUT_BOOLEAN = 0x6a;
+ public static final int SPUT_BYTE = 0x6b;
+ public static final int SPUT_CHAR = 0x6c;
+ public static final int SPUT_SHORT = 0x6d;
+ public static final int INVOKE_VIRTUAL = 0x6e;
+ public static final int INVOKE_SUPER = 0x6f;
+ public static final int INVOKE_DIRECT = 0x70;
+ public static final int INVOKE_STATIC = 0x71;
+ public static final int INVOKE_INTERFACE = 0x72;
+ public static final int UNUSED_73 = 0x73;
+ public static final int INVOKE_VIRTUAL_RANGE = 0x74;
+ public static final int INVOKE_SUPER_RANGE = 0x75;
+ public static final int INVOKE_DIRECT_RANGE = 0x76;
+ public static final int INVOKE_STATIC_RANGE = 0x77;
+ public static final int INVOKE_INTERFACE_RANGE = 0x78;
+ public static final int UNUSED_79 = 0x79;
+ public static final int UNUSED_7A = 0x7a;
+ public static final int NEG_INT = 0x7b;
+ public static final int NOT_INT = 0x7c;
+ public static final int NEG_LONG = 0x7d;
+ public static final int NOT_LONG = 0x7e;
+ public static final int NEG_FLOAT = 0x7f;
+ public static final int NEG_DOUBLE = 0x80;
+ public static final int INT_TO_LONG = 0x81;
+ public static final int INT_TO_FLOAT = 0x82;
+ public static final int INT_TO_DOUBLE = 0x83;
+ public static final int LONG_TO_INT = 0x84;
+ public static final int LONG_TO_FLOAT = 0x85;
+ public static final int LONG_TO_DOUBLE = 0x86;
+ public static final int FLOAT_TO_INT = 0x87;
+ public static final int FLOAT_TO_LONG = 0x88;
+ public static final int FLOAT_TO_DOUBLE = 0x89;
+ public static final int DOUBLE_TO_INT = 0x8a;
+ public static final int DOUBLE_TO_LONG = 0x8b;
+ public static final int DOUBLE_TO_FLOAT = 0x8c;
+ public static final int INT_TO_BYTE = 0x8d;
+ public static final int INT_TO_CHAR = 0x8e;
+ public static final int INT_TO_SHORT = 0x8f;
+ public static final int ADD_INT = 0x90;
+ public static final int SUB_INT = 0x91;
+ public static final int MUL_INT = 0x92;
+ public static final int DIV_INT = 0x93;
+ public static final int REM_INT = 0x94;
+ public static final int AND_INT = 0x95;
+ public static final int OR_INT = 0x96;
+ public static final int XOR_INT = 0x97;
+ public static final int SHL_INT = 0x98;
+ public static final int SHR_INT = 0x99;
+ public static final int USHR_INT = 0x9a;
+ public static final int ADD_LONG = 0x9b;
+ public static final int SUB_LONG = 0x9c;
+ public static final int MUL_LONG = 0x9d;
+ public static final int DIV_LONG = 0x9e;
+ public static final int REM_LONG = 0x9f;
+ public static final int AND_LONG = 0xa0;
+ public static final int OR_LONG = 0xa1;
+ public static final int XOR_LONG = 0xa2;
+ public static final int SHL_LONG = 0xa3;
+ public static final int SHR_LONG = 0xa4;
+ public static final int USHR_LONG = 0xa5;
+ public static final int ADD_FLOAT = 0xa6;
+ public static final int SUB_FLOAT = 0xa7;
+ public static final int MUL_FLOAT = 0xa8;
+ public static final int DIV_FLOAT = 0xa9;
+ public static final int REM_FLOAT = 0xaa;
+ public static final int ADD_DOUBLE = 0xab;
+ public static final int SUB_DOUBLE = 0xac;
+ public static final int MUL_DOUBLE = 0xad;
+ public static final int DIV_DOUBLE = 0xae;
+ public static final int REM_DOUBLE = 0xaf;
+ public static final int ADD_INT_2ADDR = 0xb0;
+ public static final int SUB_INT_2ADDR = 0xb1;
+ public static final int MUL_INT_2ADDR = 0xb2;
+ public static final int DIV_INT_2ADDR = 0xb3;
+ public static final int REM_INT_2ADDR = 0xb4;
+ public static final int AND_INT_2ADDR = 0xb5;
+ public static final int OR_INT_2ADDR = 0xb6;
+ public static final int XOR_INT_2ADDR = 0xb7;
+ public static final int SHL_INT_2ADDR = 0xb8;
+ public static final int SHR_INT_2ADDR = 0xb9;
+ public static final int USHR_INT_2ADDR = 0xba;
+ public static final int ADD_LONG_2ADDR = 0xbb;
+ public static final int SUB_LONG_2ADDR = 0xbc;
+ public static final int MUL_LONG_2ADDR = 0xbd;
+ public static final int DIV_LONG_2ADDR = 0xbe;
+ public static final int REM_LONG_2ADDR = 0xbf;
+ public static final int AND_LONG_2ADDR = 0xc0;
+ public static final int OR_LONG_2ADDR = 0xc1;
+ public static final int XOR_LONG_2ADDR = 0xc2;
+ public static final int SHL_LONG_2ADDR = 0xc3;
+ public static final int SHR_LONG_2ADDR = 0xc4;
+ public static final int USHR_LONG_2ADDR = 0xc5;
+ public static final int ADD_FLOAT_2ADDR = 0xc6;
+ public static final int SUB_FLOAT_2ADDR = 0xc7;
+ public static final int MUL_FLOAT_2ADDR = 0xc8;
+ public static final int DIV_FLOAT_2ADDR = 0xc9;
+ public static final int REM_FLOAT_2ADDR = 0xca;
+ public static final int ADD_DOUBLE_2ADDR = 0xcb;
+ public static final int SUB_DOUBLE_2ADDR = 0xcc;
+ public static final int MUL_DOUBLE_2ADDR = 0xcd;
+ public static final int DIV_DOUBLE_2ADDR = 0xce;
+ public static final int REM_DOUBLE_2ADDR = 0xcf;
+ public static final int ADD_INT_LIT16 = 0xd0;
+ public static final int RSUB_INT = 0xd1;
+ public static final int MUL_INT_LIT16 = 0xd2;
+ public static final int DIV_INT_LIT16 = 0xd3;
+ public static final int REM_INT_LIT16 = 0xd4;
+ public static final int AND_INT_LIT16 = 0xd5;
+ public static final int OR_INT_LIT16 = 0xd6;
+ public static final int XOR_INT_LIT16 = 0xd7;
+ public static final int ADD_INT_LIT8 = 0xd8;
+ public static final int RSUB_INT_LIT8 = 0xd9;
+ public static final int MUL_INT_LIT8 = 0xda;
+ public static final int DIV_INT_LIT8 = 0xdb;
+ public static final int REM_INT_LIT8 = 0xdc;
+ public static final int AND_INT_LIT8 = 0xdd;
+ public static final int OR_INT_LIT8 = 0xde;
+ public static final int XOR_INT_LIT8 = 0xdf;
+ public static final int SHL_INT_LIT8 = 0xe0;
+ public static final int SHR_INT_LIT8 = 0xe1;
+ public static final int USHR_INT_LIT8 = 0xe2;
+ public static final int UNUSED_E3 = 0xe3;
+ public static final int UNUSED_E4 = 0xe4;
+ public static final int UNUSED_E5 = 0xe5;
+ public static final int UNUSED_E6 = 0xe6;
+ public static final int UNUSED_E7 = 0xe7;
+ public static final int UNUSED_E8 = 0xe8;
+ public static final int UNUSED_E9 = 0xe9;
+ public static final int UNUSED_EA = 0xea;
+ public static final int UNUSED_EB = 0xeb;
+ public static final int UNUSED_EC = 0xec;
+ public static final int UNUSED_ED = 0xed;
+ public static final int UNUSED_EE = 0xee;
+ public static final int UNUSED_EF = 0xef;
+ public static final int UNUSED_F0 = 0xf0;
+ public static final int UNUSED_F1 = 0xf1;
+ public static final int UNUSED_F2 = 0xf2;
+ public static final int UNUSED_F3 = 0xf3;
+ public static final int UNUSED_F4 = 0xf4;
+ public static final int UNUSED_F5 = 0xf5;
+ public static final int UNUSED_F6 = 0xf6;
+ public static final int UNUSED_F7 = 0xf7;
+ public static final int UNUSED_F8 = 0xf8;
+ public static final int UNUSED_F9 = 0xf9;
+ public static final int UNUSED_FA = 0xfa;
+ public static final int UNUSED_FB = 0xfb;
+ public static final int UNUSED_FC = 0xfc;
+ public static final int UNUSED_FD = 0xfd;
+ public static final int UNUSED_FE = 0xfe;
+ public static final int UNUSED_FF = 0xff;
+ // END(opcodes)
+
+ /**
+ * This class is uninstantiable.
+ */
+ private DalvOps() {
+ // This space intentionally left blank.
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/Dop.java b/dx/src/com/android/dx/dex/code/Dop.java
new file mode 100644
index 0000000..2b2a08f
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/Dop.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+/**
+ * Representation of an opcode.
+ */
+public final class Dop {
+ /** DalvOps.MIN_VALUE..DalvOps.MAX_VALUE; the opcode value itself */
+ private final int opcode;
+
+ /** DalvOps.MIN_VALUE..DalvOps.MAX_VALUE; the opcode family */
+ private final int family;
+
+ /** {@code non-null;} the instruction format */
+ private final InsnFormat format;
+
+ /** whether this opcode uses a result register */
+ private final boolean hasResult;
+
+ /** {@code non-null;} the name */
+ private final String name;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param opcode {@code DalvOps.MIN_VALUE..DalvOps.MAX_VALUE;} the opcode
+ * value itself
+ * @param family {@code DalvOps.MIN_VALUE..DalvOps.MAX_VALUE;} the opcode family
+ * @param format {@code non-null;} the instruction format
+ * @param hasResult whether the opcode has a result register; if so it
+ * is always the first register
+ * @param name {@code non-null;} the name
+ */
+ public Dop(int opcode, int family, InsnFormat format,
+ boolean hasResult, String name) {
+ if ((opcode < DalvOps.MIN_VALUE) || (opcode > DalvOps.MAX_VALUE)) {
+ throw new IllegalArgumentException("bogus opcode");
+ }
+
+ if ((family < DalvOps.MIN_VALUE) || (family > DalvOps.MAX_VALUE)) {
+ throw new IllegalArgumentException("bogus family");
+ }
+
+ if (format == null) {
+ throw new NullPointerException("format == null");
+ }
+
+ if (name == null) {
+ throw new NullPointerException("name == null");
+ }
+
+ this.opcode = opcode;
+ this.family = family;
+ this.format = format;
+ this.hasResult = hasResult;
+ this.name = name;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return name;
+ }
+
+ /**
+ * Gets the opcode value.
+ *
+ * @return {@code DalvOps.MIN_VALUE..DalvOps.MAX_VALUE;} the opcode value
+ */
+ public int getOpcode() {
+ return opcode;
+ }
+
+ /**
+ * Gets the opcode family. The opcode family is the unmarked (no
+ * "/...") opcode that has equivalent semantics to this one.
+ *
+ * @return {@code DalvOps.MIN_VALUE..DalvOps.MAX_VALUE;} the opcode family
+ */
+ public int getFamily() {
+ return family;
+ }
+
+ /**
+ * Gets the instruction format.
+ *
+ * @return {@code non-null;} the instruction format
+ */
+ public InsnFormat getFormat() {
+ return format;
+ }
+
+ /**
+ * Returns whether this opcode uses a result register.
+ *
+ * @return {@code true} iff this opcode uses a result register
+ */
+ public boolean hasResult() {
+ return hasResult;
+ }
+
+ /**
+ * Gets the opcode name.
+ *
+ * @return {@code non-null;} the opcode name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Gets the opcode for the opposite test of this instance. This is only
+ * valid for opcodes which are in fact tests.
+ *
+ * @return {@code non-null;} the opposite test
+ */
+ public Dop getOppositeTest() {
+ switch (opcode) {
+ case DalvOps.IF_EQ: return Dops.IF_NE;
+ case DalvOps.IF_NE: return Dops.IF_EQ;
+ case DalvOps.IF_LT: return Dops.IF_GE;
+ case DalvOps.IF_GE: return Dops.IF_LT;
+ case DalvOps.IF_GT: return Dops.IF_LE;
+ case DalvOps.IF_LE: return Dops.IF_GT;
+ case DalvOps.IF_EQZ: return Dops.IF_NEZ;
+ case DalvOps.IF_NEZ: return Dops.IF_EQZ;
+ case DalvOps.IF_LTZ: return Dops.IF_GEZ;
+ case DalvOps.IF_GEZ: return Dops.IF_LTZ;
+ case DalvOps.IF_GTZ: return Dops.IF_LEZ;
+ case DalvOps.IF_LEZ: return Dops.IF_GTZ;
+ }
+
+ throw new IllegalArgumentException("bogus opcode: " + this);
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/Dops.java b/dx/src/com/android/dx/dex/code/Dops.java
new file mode 100644
index 0000000..0211a40
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/Dops.java
@@ -0,0 +1,1231 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.dex.code.form.Form10t;
+import com.android.dx.dex.code.form.Form10x;
+import com.android.dx.dex.code.form.Form11n;
+import com.android.dx.dex.code.form.Form11x;
+import com.android.dx.dex.code.form.Form12x;
+import com.android.dx.dex.code.form.Form20t;
+import com.android.dx.dex.code.form.Form21c;
+import com.android.dx.dex.code.form.Form21h;
+import com.android.dx.dex.code.form.Form21s;
+import com.android.dx.dex.code.form.Form21t;
+import com.android.dx.dex.code.form.Form22b;
+import com.android.dx.dex.code.form.Form22c;
+import com.android.dx.dex.code.form.Form22s;
+import com.android.dx.dex.code.form.Form22t;
+import com.android.dx.dex.code.form.Form22x;
+import com.android.dx.dex.code.form.Form23x;
+import com.android.dx.dex.code.form.Form30t;
+import com.android.dx.dex.code.form.Form31c;
+import com.android.dx.dex.code.form.Form31i;
+import com.android.dx.dex.code.form.Form31t;
+import com.android.dx.dex.code.form.Form32x;
+import com.android.dx.dex.code.form.Form35c;
+import com.android.dx.dex.code.form.Form3rc;
+import com.android.dx.dex.code.form.Form51l;
+import com.android.dx.dex.code.form.SpecialFormat;
+
+/**
+ * Standard instances of {@link Dop} and utility methods for getting
+ * them.
+ */
+public final class Dops {
+ /** {@code non-null;} array containing all the standard instances */
+ private static final Dop[] DOPS;
+
+ /**
+ * pseudo-opcode used for nonstandard formatted "instructions"
+ * (which are mostly not actually instructions, though they do
+ * appear in instruction lists)
+ */
+ public static final Dop SPECIAL_FORMAT =
+ new Dop(DalvOps.SPECIAL_FORMAT, DalvOps.SPECIAL_FORMAT,
+ SpecialFormat.THE_ONE, false, "<special>");
+
+ // BEGIN(dops); GENERATED AUTOMATICALLY BY opcode-gen
+ public static final Dop NOP =
+ new Dop(DalvOps.NOP, DalvOps.NOP,
+ Form10x.THE_ONE, false, "nop");
+
+ public static final Dop MOVE =
+ new Dop(DalvOps.MOVE, DalvOps.MOVE,
+ Form12x.THE_ONE, true, "move");
+
+ public static final Dop MOVE_FROM16 =
+ new Dop(DalvOps.MOVE_FROM16, DalvOps.MOVE,
+ Form22x.THE_ONE, true, "move/from16");
+
+ public static final Dop MOVE_16 =
+ new Dop(DalvOps.MOVE_16, DalvOps.MOVE,
+ Form32x.THE_ONE, true, "move/16");
+
+ public static final Dop MOVE_WIDE =
+ new Dop(DalvOps.MOVE_WIDE, DalvOps.MOVE_WIDE,
+ Form12x.THE_ONE, true, "move-wide");
+
+ public static final Dop MOVE_WIDE_FROM16 =
+ new Dop(DalvOps.MOVE_WIDE_FROM16, DalvOps.MOVE_WIDE,
+ Form22x.THE_ONE, true, "move-wide/from16");
+
+ public static final Dop MOVE_WIDE_16 =
+ new Dop(DalvOps.MOVE_WIDE_16, DalvOps.MOVE_WIDE,
+ Form32x.THE_ONE, true, "move-wide/16");
+
+ public static final Dop MOVE_OBJECT =
+ new Dop(DalvOps.MOVE_OBJECT, DalvOps.MOVE_OBJECT,
+ Form12x.THE_ONE, true, "move-object");
+
+ public static final Dop MOVE_OBJECT_FROM16 =
+ new Dop(DalvOps.MOVE_OBJECT_FROM16, DalvOps.MOVE_OBJECT,
+ Form22x.THE_ONE, true, "move-object/from16");
+
+ public static final Dop MOVE_OBJECT_16 =
+ new Dop(DalvOps.MOVE_OBJECT_16, DalvOps.MOVE_OBJECT,
+ Form32x.THE_ONE, true, "move-object/16");
+
+ public static final Dop MOVE_RESULT =
+ new Dop(DalvOps.MOVE_RESULT, DalvOps.MOVE_RESULT,
+ Form11x.THE_ONE, true, "move-result");
+
+ public static final Dop MOVE_RESULT_WIDE =
+ new Dop(DalvOps.MOVE_RESULT_WIDE, DalvOps.MOVE_RESULT_WIDE,
+ Form11x.THE_ONE, true, "move-result-wide");
+
+ public static final Dop MOVE_RESULT_OBJECT =
+ new Dop(DalvOps.MOVE_RESULT_OBJECT, DalvOps.MOVE_RESULT_OBJECT,
+ Form11x.THE_ONE, true, "move-result-object");
+
+ public static final Dop MOVE_EXCEPTION =
+ new Dop(DalvOps.MOVE_EXCEPTION, DalvOps.MOVE_EXCEPTION,
+ Form11x.THE_ONE, true, "move-exception");
+
+ public static final Dop RETURN_VOID =
+ new Dop(DalvOps.RETURN_VOID, DalvOps.RETURN_VOID,
+ Form10x.THE_ONE, false, "return-void");
+
+ public static final Dop RETURN =
+ new Dop(DalvOps.RETURN, DalvOps.RETURN,
+ Form11x.THE_ONE, false, "return");
+
+ public static final Dop RETURN_WIDE =
+ new Dop(DalvOps.RETURN_WIDE, DalvOps.RETURN_WIDE,
+ Form11x.THE_ONE, false, "return-wide");
+
+ public static final Dop RETURN_OBJECT =
+ new Dop(DalvOps.RETURN_OBJECT, DalvOps.RETURN_OBJECT,
+ Form11x.THE_ONE, false, "return-object");
+
+ public static final Dop CONST_4 =
+ new Dop(DalvOps.CONST_4, DalvOps.CONST,
+ Form11n.THE_ONE, true, "const/4");
+
+ public static final Dop CONST_16 =
+ new Dop(DalvOps.CONST_16, DalvOps.CONST,
+ Form21s.THE_ONE, true, "const/16");
+
+ public static final Dop CONST =
+ new Dop(DalvOps.CONST, DalvOps.CONST,
+ Form31i.THE_ONE, true, "const");
+
+ public static final Dop CONST_HIGH16 =
+ new Dop(DalvOps.CONST_HIGH16, DalvOps.CONST,
+ Form21h.THE_ONE, true, "const/high16");
+
+ public static final Dop CONST_WIDE_16 =
+ new Dop(DalvOps.CONST_WIDE_16, DalvOps.CONST_WIDE,
+ Form21s.THE_ONE, true, "const-wide/16");
+
+ public static final Dop CONST_WIDE_32 =
+ new Dop(DalvOps.CONST_WIDE_32, DalvOps.CONST_WIDE,
+ Form31i.THE_ONE, true, "const-wide/32");
+
+ public static final Dop CONST_WIDE =
+ new Dop(DalvOps.CONST_WIDE, DalvOps.CONST_WIDE,
+ Form51l.THE_ONE, true, "const-wide");
+
+ public static final Dop CONST_WIDE_HIGH16 =
+ new Dop(DalvOps.CONST_WIDE_HIGH16, DalvOps.CONST_WIDE,
+ Form21h.THE_ONE, true, "const-wide/high16");
+
+ public static final Dop CONST_STRING =
+ new Dop(DalvOps.CONST_STRING, DalvOps.CONST_STRING,
+ Form21c.THE_ONE, true, "const-string");
+
+ public static final Dop CONST_STRING_JUMBO =
+ new Dop(DalvOps.CONST_STRING_JUMBO, DalvOps.CONST_STRING,
+ Form31c.THE_ONE, true, "const-string/jumbo");
+
+ public static final Dop CONST_CLASS =
+ new Dop(DalvOps.CONST_CLASS, DalvOps.CONST_CLASS,
+ Form21c.THE_ONE, true, "const-class");
+
+ public static final Dop MONITOR_ENTER =
+ new Dop(DalvOps.MONITOR_ENTER, DalvOps.MONITOR_ENTER,
+ Form11x.THE_ONE, false, "monitor-enter");
+
+ public static final Dop MONITOR_EXIT =
+ new Dop(DalvOps.MONITOR_EXIT, DalvOps.MONITOR_EXIT,
+ Form11x.THE_ONE, false, "monitor-exit");
+
+ public static final Dop CHECK_CAST =
+ new Dop(DalvOps.CHECK_CAST, DalvOps.CHECK_CAST,
+ Form21c.THE_ONE, true, "check-cast");
+
+ public static final Dop INSTANCE_OF =
+ new Dop(DalvOps.INSTANCE_OF, DalvOps.INSTANCE_OF,
+ Form22c.THE_ONE, true, "instance-of");
+
+ public static final Dop ARRAY_LENGTH =
+ new Dop(DalvOps.ARRAY_LENGTH, DalvOps.ARRAY_LENGTH,
+ Form12x.THE_ONE, true, "array-length");
+
+ public static final Dop NEW_INSTANCE =
+ new Dop(DalvOps.NEW_INSTANCE, DalvOps.NEW_INSTANCE,
+ Form21c.THE_ONE, true, "new-instance");
+
+ public static final Dop NEW_ARRAY =
+ new Dop(DalvOps.NEW_ARRAY, DalvOps.NEW_ARRAY,
+ Form22c.THE_ONE, true, "new-array");
+
+ public static final Dop FILLED_NEW_ARRAY =
+ new Dop(DalvOps.FILLED_NEW_ARRAY, DalvOps.FILLED_NEW_ARRAY,
+ Form35c.THE_ONE, false, "filled-new-array");
+
+ public static final Dop FILLED_NEW_ARRAY_RANGE =
+ new Dop(DalvOps.FILLED_NEW_ARRAY_RANGE, DalvOps.FILLED_NEW_ARRAY,
+ Form3rc.THE_ONE, false, "filled-new-array/range");
+
+ public static final Dop FILL_ARRAY_DATA =
+ new Dop(DalvOps.FILL_ARRAY_DATA, DalvOps.FILL_ARRAY_DATA,
+ Form31t.THE_ONE, false, "fill-array-data");
+
+ public static final Dop THROW =
+ new Dop(DalvOps.THROW, DalvOps.THROW,
+ Form11x.THE_ONE, false, "throw");
+
+ public static final Dop GOTO =
+ new Dop(DalvOps.GOTO, DalvOps.GOTO,
+ Form10t.THE_ONE, false, "goto");
+
+ public static final Dop GOTO_16 =
+ new Dop(DalvOps.GOTO_16, DalvOps.GOTO,
+ Form20t.THE_ONE, false, "goto/16");
+
+ public static final Dop GOTO_32 =
+ new Dop(DalvOps.GOTO_32, DalvOps.GOTO,
+ Form30t.THE_ONE, false, "goto/32");
+
+ public static final Dop PACKED_SWITCH =
+ new Dop(DalvOps.PACKED_SWITCH, DalvOps.PACKED_SWITCH,
+ Form31t.THE_ONE, false, "packed-switch");
+
+ public static final Dop SPARSE_SWITCH =
+ new Dop(DalvOps.SPARSE_SWITCH, DalvOps.SPARSE_SWITCH,
+ Form31t.THE_ONE, false, "sparse-switch");
+
+ public static final Dop CMPL_FLOAT =
+ new Dop(DalvOps.CMPL_FLOAT, DalvOps.CMPL_FLOAT,
+ Form23x.THE_ONE, true, "cmpl-float");
+
+ public static final Dop CMPG_FLOAT =
+ new Dop(DalvOps.CMPG_FLOAT, DalvOps.CMPG_FLOAT,
+ Form23x.THE_ONE, true, "cmpg-float");
+
+ public static final Dop CMPL_DOUBLE =
+ new Dop(DalvOps.CMPL_DOUBLE, DalvOps.CMPL_DOUBLE,
+ Form23x.THE_ONE, true, "cmpl-double");
+
+ public static final Dop CMPG_DOUBLE =
+ new Dop(DalvOps.CMPG_DOUBLE, DalvOps.CMPG_DOUBLE,
+ Form23x.THE_ONE, true, "cmpg-double");
+
+ public static final Dop CMP_LONG =
+ new Dop(DalvOps.CMP_LONG, DalvOps.CMP_LONG,
+ Form23x.THE_ONE, true, "cmp-long");
+
+ public static final Dop IF_EQ =
+ new Dop(DalvOps.IF_EQ, DalvOps.IF_EQ,
+ Form22t.THE_ONE, false, "if-eq");
+
+ public static final Dop IF_NE =
+ new Dop(DalvOps.IF_NE, DalvOps.IF_NE,
+ Form22t.THE_ONE, false, "if-ne");
+
+ public static final Dop IF_LT =
+ new Dop(DalvOps.IF_LT, DalvOps.IF_LT,
+ Form22t.THE_ONE, false, "if-lt");
+
+ public static final Dop IF_GE =
+ new Dop(DalvOps.IF_GE, DalvOps.IF_GE,
+ Form22t.THE_ONE, false, "if-ge");
+
+ public static final Dop IF_GT =
+ new Dop(DalvOps.IF_GT, DalvOps.IF_GT,
+ Form22t.THE_ONE, false, "if-gt");
+
+ public static final Dop IF_LE =
+ new Dop(DalvOps.IF_LE, DalvOps.IF_LE,
+ Form22t.THE_ONE, false, "if-le");
+
+ public static final Dop IF_EQZ =
+ new Dop(DalvOps.IF_EQZ, DalvOps.IF_EQZ,
+ Form21t.THE_ONE, false, "if-eqz");
+
+ public static final Dop IF_NEZ =
+ new Dop(DalvOps.IF_NEZ, DalvOps.IF_NEZ,
+ Form21t.THE_ONE, false, "if-nez");
+
+ public static final Dop IF_LTZ =
+ new Dop(DalvOps.IF_LTZ, DalvOps.IF_LTZ,
+ Form21t.THE_ONE, false, "if-ltz");
+
+ public static final Dop IF_GEZ =
+ new Dop(DalvOps.IF_GEZ, DalvOps.IF_GEZ,
+ Form21t.THE_ONE, false, "if-gez");
+
+ public static final Dop IF_GTZ =
+ new Dop(DalvOps.IF_GTZ, DalvOps.IF_GTZ,
+ Form21t.THE_ONE, false, "if-gtz");
+
+ public static final Dop IF_LEZ =
+ new Dop(DalvOps.IF_LEZ, DalvOps.IF_LEZ,
+ Form21t.THE_ONE, false, "if-lez");
+
+ public static final Dop AGET =
+ new Dop(DalvOps.AGET, DalvOps.AGET,
+ Form23x.THE_ONE, true, "aget");
+
+ public static final Dop AGET_WIDE =
+ new Dop(DalvOps.AGET_WIDE, DalvOps.AGET_WIDE,
+ Form23x.THE_ONE, true, "aget-wide");
+
+ public static final Dop AGET_OBJECT =
+ new Dop(DalvOps.AGET_OBJECT, DalvOps.AGET_OBJECT,
+ Form23x.THE_ONE, true, "aget-object");
+
+ public static final Dop AGET_BOOLEAN =
+ new Dop(DalvOps.AGET_BOOLEAN, DalvOps.AGET_BOOLEAN,
+ Form23x.THE_ONE, true, "aget-boolean");
+
+ public static final Dop AGET_BYTE =
+ new Dop(DalvOps.AGET_BYTE, DalvOps.AGET_BYTE,
+ Form23x.THE_ONE, true, "aget-byte");
+
+ public static final Dop AGET_CHAR =
+ new Dop(DalvOps.AGET_CHAR, DalvOps.AGET_CHAR,
+ Form23x.THE_ONE, true, "aget-char");
+
+ public static final Dop AGET_SHORT =
+ new Dop(DalvOps.AGET_SHORT, DalvOps.AGET_SHORT,
+ Form23x.THE_ONE, true, "aget-short");
+
+ public static final Dop APUT =
+ new Dop(DalvOps.APUT, DalvOps.APUT,
+ Form23x.THE_ONE, false, "aput");
+
+ public static final Dop APUT_WIDE =
+ new Dop(DalvOps.APUT_WIDE, DalvOps.APUT_WIDE,
+ Form23x.THE_ONE, false, "aput-wide");
+
+ public static final Dop APUT_OBJECT =
+ new Dop(DalvOps.APUT_OBJECT, DalvOps.APUT_OBJECT,
+ Form23x.THE_ONE, false, "aput-object");
+
+ public static final Dop APUT_BOOLEAN =
+ new Dop(DalvOps.APUT_BOOLEAN, DalvOps.APUT_BOOLEAN,
+ Form23x.THE_ONE, false, "aput-boolean");
+
+ public static final Dop APUT_BYTE =
+ new Dop(DalvOps.APUT_BYTE, DalvOps.APUT_BYTE,
+ Form23x.THE_ONE, false, "aput-byte");
+
+ public static final Dop APUT_CHAR =
+ new Dop(DalvOps.APUT_CHAR, DalvOps.APUT_CHAR,
+ Form23x.THE_ONE, false, "aput-char");
+
+ public static final Dop APUT_SHORT =
+ new Dop(DalvOps.APUT_SHORT, DalvOps.APUT_SHORT,
+ Form23x.THE_ONE, false, "aput-short");
+
+ public static final Dop IGET =
+ new Dop(DalvOps.IGET, DalvOps.IGET,
+ Form22c.THE_ONE, true, "iget");
+
+ public static final Dop IGET_WIDE =
+ new Dop(DalvOps.IGET_WIDE, DalvOps.IGET_WIDE,
+ Form22c.THE_ONE, true, "iget-wide");
+
+ public static final Dop IGET_OBJECT =
+ new Dop(DalvOps.IGET_OBJECT, DalvOps.IGET_OBJECT,
+ Form22c.THE_ONE, true, "iget-object");
+
+ public static final Dop IGET_BOOLEAN =
+ new Dop(DalvOps.IGET_BOOLEAN, DalvOps.IGET_BOOLEAN,
+ Form22c.THE_ONE, true, "iget-boolean");
+
+ public static final Dop IGET_BYTE =
+ new Dop(DalvOps.IGET_BYTE, DalvOps.IGET_BYTE,
+ Form22c.THE_ONE, true, "iget-byte");
+
+ public static final Dop IGET_CHAR =
+ new Dop(DalvOps.IGET_CHAR, DalvOps.IGET_CHAR,
+ Form22c.THE_ONE, true, "iget-char");
+
+ public static final Dop IGET_SHORT =
+ new Dop(DalvOps.IGET_SHORT, DalvOps.IGET_SHORT,
+ Form22c.THE_ONE, true, "iget-short");
+
+ public static final Dop IPUT =
+ new Dop(DalvOps.IPUT, DalvOps.IPUT,
+ Form22c.THE_ONE, false, "iput");
+
+ public static final Dop IPUT_WIDE =
+ new Dop(DalvOps.IPUT_WIDE, DalvOps.IPUT_WIDE,
+ Form22c.THE_ONE, false, "iput-wide");
+
+ public static final Dop IPUT_OBJECT =
+ new Dop(DalvOps.IPUT_OBJECT, DalvOps.IPUT_OBJECT,
+ Form22c.THE_ONE, false, "iput-object");
+
+ public static final Dop IPUT_BOOLEAN =
+ new Dop(DalvOps.IPUT_BOOLEAN, DalvOps.IPUT_BOOLEAN,
+ Form22c.THE_ONE, false, "iput-boolean");
+
+ public static final Dop IPUT_BYTE =
+ new Dop(DalvOps.IPUT_BYTE, DalvOps.IPUT_BYTE,
+ Form22c.THE_ONE, false, "iput-byte");
+
+ public static final Dop IPUT_CHAR =
+ new Dop(DalvOps.IPUT_CHAR, DalvOps.IPUT_CHAR,
+ Form22c.THE_ONE, false, "iput-char");
+
+ public static final Dop IPUT_SHORT =
+ new Dop(DalvOps.IPUT_SHORT, DalvOps.IPUT_SHORT,
+ Form22c.THE_ONE, false, "iput-short");
+
+ public static final Dop SGET =
+ new Dop(DalvOps.SGET, DalvOps.SGET,
+ Form21c.THE_ONE, true, "sget");
+
+ public static final Dop SGET_WIDE =
+ new Dop(DalvOps.SGET_WIDE, DalvOps.SGET_WIDE,
+ Form21c.THE_ONE, true, "sget-wide");
+
+ public static final Dop SGET_OBJECT =
+ new Dop(DalvOps.SGET_OBJECT, DalvOps.SGET_OBJECT,
+ Form21c.THE_ONE, true, "sget-object");
+
+ public static final Dop SGET_BOOLEAN =
+ new Dop(DalvOps.SGET_BOOLEAN, DalvOps.SGET_BOOLEAN,
+ Form21c.THE_ONE, true, "sget-boolean");
+
+ public static final Dop SGET_BYTE =
+ new Dop(DalvOps.SGET_BYTE, DalvOps.SGET_BYTE,
+ Form21c.THE_ONE, true, "sget-byte");
+
+ public static final Dop SGET_CHAR =
+ new Dop(DalvOps.SGET_CHAR, DalvOps.SGET_CHAR,
+ Form21c.THE_ONE, true, "sget-char");
+
+ public static final Dop SGET_SHORT =
+ new Dop(DalvOps.SGET_SHORT, DalvOps.SGET_SHORT,
+ Form21c.THE_ONE, true, "sget-short");
+
+ public static final Dop SPUT =
+ new Dop(DalvOps.SPUT, DalvOps.SPUT,
+ Form21c.THE_ONE, false, "sput");
+
+ public static final Dop SPUT_WIDE =
+ new Dop(DalvOps.SPUT_WIDE, DalvOps.SPUT_WIDE,
+ Form21c.THE_ONE, false, "sput-wide");
+
+ public static final Dop SPUT_OBJECT =
+ new Dop(DalvOps.SPUT_OBJECT, DalvOps.SPUT_OBJECT,
+ Form21c.THE_ONE, false, "sput-object");
+
+ public static final Dop SPUT_BOOLEAN =
+ new Dop(DalvOps.SPUT_BOOLEAN, DalvOps.SPUT_BOOLEAN,
+ Form21c.THE_ONE, false, "sput-boolean");
+
+ public static final Dop SPUT_BYTE =
+ new Dop(DalvOps.SPUT_BYTE, DalvOps.SPUT_BYTE,
+ Form21c.THE_ONE, false, "sput-byte");
+
+ public static final Dop SPUT_CHAR =
+ new Dop(DalvOps.SPUT_CHAR, DalvOps.SPUT_CHAR,
+ Form21c.THE_ONE, false, "sput-char");
+
+ public static final Dop SPUT_SHORT =
+ new Dop(DalvOps.SPUT_SHORT, DalvOps.SPUT_SHORT,
+ Form21c.THE_ONE, false, "sput-short");
+
+ public static final Dop INVOKE_VIRTUAL =
+ new Dop(DalvOps.INVOKE_VIRTUAL, DalvOps.INVOKE_VIRTUAL,
+ Form35c.THE_ONE, false, "invoke-virtual");
+
+ public static final Dop INVOKE_SUPER =
+ new Dop(DalvOps.INVOKE_SUPER, DalvOps.INVOKE_SUPER,
+ Form35c.THE_ONE, false, "invoke-super");
+
+ public static final Dop INVOKE_DIRECT =
+ new Dop(DalvOps.INVOKE_DIRECT, DalvOps.INVOKE_DIRECT,
+ Form35c.THE_ONE, false, "invoke-direct");
+
+ public static final Dop INVOKE_STATIC =
+ new Dop(DalvOps.INVOKE_STATIC, DalvOps.INVOKE_STATIC,
+ Form35c.THE_ONE, false, "invoke-static");
+
+ public static final Dop INVOKE_INTERFACE =
+ new Dop(DalvOps.INVOKE_INTERFACE, DalvOps.INVOKE_INTERFACE,
+ Form35c.THE_ONE, false, "invoke-interface");
+
+ public static final Dop INVOKE_VIRTUAL_RANGE =
+ new Dop(DalvOps.INVOKE_VIRTUAL_RANGE, DalvOps.INVOKE_VIRTUAL,
+ Form3rc.THE_ONE, false, "invoke-virtual/range");
+
+ public static final Dop INVOKE_SUPER_RANGE =
+ new Dop(DalvOps.INVOKE_SUPER_RANGE, DalvOps.INVOKE_SUPER,
+ Form3rc.THE_ONE, false, "invoke-super/range");
+
+ public static final Dop INVOKE_DIRECT_RANGE =
+ new Dop(DalvOps.INVOKE_DIRECT_RANGE, DalvOps.INVOKE_DIRECT,
+ Form3rc.THE_ONE, false, "invoke-direct/range");
+
+ public static final Dop INVOKE_STATIC_RANGE =
+ new Dop(DalvOps.INVOKE_STATIC_RANGE, DalvOps.INVOKE_STATIC,
+ Form3rc.THE_ONE, false, "invoke-static/range");
+
+ public static final Dop INVOKE_INTERFACE_RANGE =
+ new Dop(DalvOps.INVOKE_INTERFACE_RANGE, DalvOps.INVOKE_INTERFACE,
+ Form3rc.THE_ONE, false, "invoke-interface/range");
+
+ public static final Dop NEG_INT =
+ new Dop(DalvOps.NEG_INT, DalvOps.NEG_INT,
+ Form12x.THE_ONE, true, "neg-int");
+
+ public static final Dop NOT_INT =
+ new Dop(DalvOps.NOT_INT, DalvOps.NOT_INT,
+ Form12x.THE_ONE, true, "not-int");
+
+ public static final Dop NEG_LONG =
+ new Dop(DalvOps.NEG_LONG, DalvOps.NEG_LONG,
+ Form12x.THE_ONE, true, "neg-long");
+
+ public static final Dop NOT_LONG =
+ new Dop(DalvOps.NOT_LONG, DalvOps.NOT_LONG,
+ Form12x.THE_ONE, true, "not-long");
+
+ public static final Dop NEG_FLOAT =
+ new Dop(DalvOps.NEG_FLOAT, DalvOps.NEG_FLOAT,
+ Form12x.THE_ONE, true, "neg-float");
+
+ public static final Dop NEG_DOUBLE =
+ new Dop(DalvOps.NEG_DOUBLE, DalvOps.NEG_DOUBLE,
+ Form12x.THE_ONE, true, "neg-double");
+
+ public static final Dop INT_TO_LONG =
+ new Dop(DalvOps.INT_TO_LONG, DalvOps.INT_TO_LONG,
+ Form12x.THE_ONE, true, "int-to-long");
+
+ public static final Dop INT_TO_FLOAT =
+ new Dop(DalvOps.INT_TO_FLOAT, DalvOps.INT_TO_FLOAT,
+ Form12x.THE_ONE, true, "int-to-float");
+
+ public static final Dop INT_TO_DOUBLE =
+ new Dop(DalvOps.INT_TO_DOUBLE, DalvOps.INT_TO_DOUBLE,
+ Form12x.THE_ONE, true, "int-to-double");
+
+ public static final Dop LONG_TO_INT =
+ new Dop(DalvOps.LONG_TO_INT, DalvOps.LONG_TO_INT,
+ Form12x.THE_ONE, true, "long-to-int");
+
+ public static final Dop LONG_TO_FLOAT =
+ new Dop(DalvOps.LONG_TO_FLOAT, DalvOps.LONG_TO_FLOAT,
+ Form12x.THE_ONE, true, "long-to-float");
+
+ public static final Dop LONG_TO_DOUBLE =
+ new Dop(DalvOps.LONG_TO_DOUBLE, DalvOps.LONG_TO_DOUBLE,
+ Form12x.THE_ONE, true, "long-to-double");
+
+ public static final Dop FLOAT_TO_INT =
+ new Dop(DalvOps.FLOAT_TO_INT, DalvOps.FLOAT_TO_INT,
+ Form12x.THE_ONE, true, "float-to-int");
+
+ public static final Dop FLOAT_TO_LONG =
+ new Dop(DalvOps.FLOAT_TO_LONG, DalvOps.FLOAT_TO_LONG,
+ Form12x.THE_ONE, true, "float-to-long");
+
+ public static final Dop FLOAT_TO_DOUBLE =
+ new Dop(DalvOps.FLOAT_TO_DOUBLE, DalvOps.FLOAT_TO_DOUBLE,
+ Form12x.THE_ONE, true, "float-to-double");
+
+ public static final Dop DOUBLE_TO_INT =
+ new Dop(DalvOps.DOUBLE_TO_INT, DalvOps.DOUBLE_TO_INT,
+ Form12x.THE_ONE, true, "double-to-int");
+
+ public static final Dop DOUBLE_TO_LONG =
+ new Dop(DalvOps.DOUBLE_TO_LONG, DalvOps.DOUBLE_TO_LONG,
+ Form12x.THE_ONE, true, "double-to-long");
+
+ public static final Dop DOUBLE_TO_FLOAT =
+ new Dop(DalvOps.DOUBLE_TO_FLOAT, DalvOps.DOUBLE_TO_FLOAT,
+ Form12x.THE_ONE, true, "double-to-float");
+
+ public static final Dop INT_TO_BYTE =
+ new Dop(DalvOps.INT_TO_BYTE, DalvOps.INT_TO_BYTE,
+ Form12x.THE_ONE, true, "int-to-byte");
+
+ public static final Dop INT_TO_CHAR =
+ new Dop(DalvOps.INT_TO_CHAR, DalvOps.INT_TO_CHAR,
+ Form12x.THE_ONE, true, "int-to-char");
+
+ public static final Dop INT_TO_SHORT =
+ new Dop(DalvOps.INT_TO_SHORT, DalvOps.INT_TO_SHORT,
+ Form12x.THE_ONE, true, "int-to-short");
+
+ public static final Dop ADD_INT =
+ new Dop(DalvOps.ADD_INT, DalvOps.ADD_INT,
+ Form23x.THE_ONE, true, "add-int");
+
+ public static final Dop SUB_INT =
+ new Dop(DalvOps.SUB_INT, DalvOps.SUB_INT,
+ Form23x.THE_ONE, true, "sub-int");
+
+ public static final Dop MUL_INT =
+ new Dop(DalvOps.MUL_INT, DalvOps.MUL_INT,
+ Form23x.THE_ONE, true, "mul-int");
+
+ public static final Dop DIV_INT =
+ new Dop(DalvOps.DIV_INT, DalvOps.DIV_INT,
+ Form23x.THE_ONE, true, "div-int");
+
+ public static final Dop REM_INT =
+ new Dop(DalvOps.REM_INT, DalvOps.REM_INT,
+ Form23x.THE_ONE, true, "rem-int");
+
+ public static final Dop AND_INT =
+ new Dop(DalvOps.AND_INT, DalvOps.AND_INT,
+ Form23x.THE_ONE, true, "and-int");
+
+ public static final Dop OR_INT =
+ new Dop(DalvOps.OR_INT, DalvOps.OR_INT,
+ Form23x.THE_ONE, true, "or-int");
+
+ public static final Dop XOR_INT =
+ new Dop(DalvOps.XOR_INT, DalvOps.XOR_INT,
+ Form23x.THE_ONE, true, "xor-int");
+
+ public static final Dop SHL_INT =
+ new Dop(DalvOps.SHL_INT, DalvOps.SHL_INT,
+ Form23x.THE_ONE, true, "shl-int");
+
+ public static final Dop SHR_INT =
+ new Dop(DalvOps.SHR_INT, DalvOps.SHR_INT,
+ Form23x.THE_ONE, true, "shr-int");
+
+ public static final Dop USHR_INT =
+ new Dop(DalvOps.USHR_INT, DalvOps.USHR_INT,
+ Form23x.THE_ONE, true, "ushr-int");
+
+ public static final Dop ADD_LONG =
+ new Dop(DalvOps.ADD_LONG, DalvOps.ADD_LONG,
+ Form23x.THE_ONE, true, "add-long");
+
+ public static final Dop SUB_LONG =
+ new Dop(DalvOps.SUB_LONG, DalvOps.SUB_LONG,
+ Form23x.THE_ONE, true, "sub-long");
+
+ public static final Dop MUL_LONG =
+ new Dop(DalvOps.MUL_LONG, DalvOps.MUL_LONG,
+ Form23x.THE_ONE, true, "mul-long");
+
+ public static final Dop DIV_LONG =
+ new Dop(DalvOps.DIV_LONG, DalvOps.DIV_LONG,
+ Form23x.THE_ONE, true, "div-long");
+
+ public static final Dop REM_LONG =
+ new Dop(DalvOps.REM_LONG, DalvOps.REM_LONG,
+ Form23x.THE_ONE, true, "rem-long");
+
+ public static final Dop AND_LONG =
+ new Dop(DalvOps.AND_LONG, DalvOps.AND_LONG,
+ Form23x.THE_ONE, true, "and-long");
+
+ public static final Dop OR_LONG =
+ new Dop(DalvOps.OR_LONG, DalvOps.OR_LONG,
+ Form23x.THE_ONE, true, "or-long");
+
+ public static final Dop XOR_LONG =
+ new Dop(DalvOps.XOR_LONG, DalvOps.XOR_LONG,
+ Form23x.THE_ONE, true, "xor-long");
+
+ public static final Dop SHL_LONG =
+ new Dop(DalvOps.SHL_LONG, DalvOps.SHL_LONG,
+ Form23x.THE_ONE, true, "shl-long");
+
+ public static final Dop SHR_LONG =
+ new Dop(DalvOps.SHR_LONG, DalvOps.SHR_LONG,
+ Form23x.THE_ONE, true, "shr-long");
+
+ public static final Dop USHR_LONG =
+ new Dop(DalvOps.USHR_LONG, DalvOps.USHR_LONG,
+ Form23x.THE_ONE, true, "ushr-long");
+
+ public static final Dop ADD_FLOAT =
+ new Dop(DalvOps.ADD_FLOAT, DalvOps.ADD_FLOAT,
+ Form23x.THE_ONE, true, "add-float");
+
+ public static final Dop SUB_FLOAT =
+ new Dop(DalvOps.SUB_FLOAT, DalvOps.SUB_FLOAT,
+ Form23x.THE_ONE, true, "sub-float");
+
+ public static final Dop MUL_FLOAT =
+ new Dop(DalvOps.MUL_FLOAT, DalvOps.MUL_FLOAT,
+ Form23x.THE_ONE, true, "mul-float");
+
+ public static final Dop DIV_FLOAT =
+ new Dop(DalvOps.DIV_FLOAT, DalvOps.DIV_FLOAT,
+ Form23x.THE_ONE, true, "div-float");
+
+ public static final Dop REM_FLOAT =
+ new Dop(DalvOps.REM_FLOAT, DalvOps.REM_FLOAT,
+ Form23x.THE_ONE, true, "rem-float");
+
+ public static final Dop ADD_DOUBLE =
+ new Dop(DalvOps.ADD_DOUBLE, DalvOps.ADD_DOUBLE,
+ Form23x.THE_ONE, true, "add-double");
+
+ public static final Dop SUB_DOUBLE =
+ new Dop(DalvOps.SUB_DOUBLE, DalvOps.SUB_DOUBLE,
+ Form23x.THE_ONE, true, "sub-double");
+
+ public static final Dop MUL_DOUBLE =
+ new Dop(DalvOps.MUL_DOUBLE, DalvOps.MUL_DOUBLE,
+ Form23x.THE_ONE, true, "mul-double");
+
+ public static final Dop DIV_DOUBLE =
+ new Dop(DalvOps.DIV_DOUBLE, DalvOps.DIV_DOUBLE,
+ Form23x.THE_ONE, true, "div-double");
+
+ public static final Dop REM_DOUBLE =
+ new Dop(DalvOps.REM_DOUBLE, DalvOps.REM_DOUBLE,
+ Form23x.THE_ONE, true, "rem-double");
+
+ public static final Dop ADD_INT_2ADDR =
+ new Dop(DalvOps.ADD_INT_2ADDR, DalvOps.ADD_INT,
+ Form12x.THE_ONE, true, "add-int/2addr");
+
+ public static final Dop SUB_INT_2ADDR =
+ new Dop(DalvOps.SUB_INT_2ADDR, DalvOps.SUB_INT,
+ Form12x.THE_ONE, true, "sub-int/2addr");
+
+ public static final Dop MUL_INT_2ADDR =
+ new Dop(DalvOps.MUL_INT_2ADDR, DalvOps.MUL_INT,
+ Form12x.THE_ONE, true, "mul-int/2addr");
+
+ public static final Dop DIV_INT_2ADDR =
+ new Dop(DalvOps.DIV_INT_2ADDR, DalvOps.DIV_INT,
+ Form12x.THE_ONE, true, "div-int/2addr");
+
+ public static final Dop REM_INT_2ADDR =
+ new Dop(DalvOps.REM_INT_2ADDR, DalvOps.REM_INT,
+ Form12x.THE_ONE, true, "rem-int/2addr");
+
+ public static final Dop AND_INT_2ADDR =
+ new Dop(DalvOps.AND_INT_2ADDR, DalvOps.AND_INT,
+ Form12x.THE_ONE, true, "and-int/2addr");
+
+ public static final Dop OR_INT_2ADDR =
+ new Dop(DalvOps.OR_INT_2ADDR, DalvOps.OR_INT,
+ Form12x.THE_ONE, true, "or-int/2addr");
+
+ public static final Dop XOR_INT_2ADDR =
+ new Dop(DalvOps.XOR_INT_2ADDR, DalvOps.XOR_INT,
+ Form12x.THE_ONE, true, "xor-int/2addr");
+
+ public static final Dop SHL_INT_2ADDR =
+ new Dop(DalvOps.SHL_INT_2ADDR, DalvOps.SHL_INT,
+ Form12x.THE_ONE, true, "shl-int/2addr");
+
+ public static final Dop SHR_INT_2ADDR =
+ new Dop(DalvOps.SHR_INT_2ADDR, DalvOps.SHR_INT,
+ Form12x.THE_ONE, true, "shr-int/2addr");
+
+ public static final Dop USHR_INT_2ADDR =
+ new Dop(DalvOps.USHR_INT_2ADDR, DalvOps.USHR_INT,
+ Form12x.THE_ONE, true, "ushr-int/2addr");
+
+ public static final Dop ADD_LONG_2ADDR =
+ new Dop(DalvOps.ADD_LONG_2ADDR, DalvOps.ADD_LONG,
+ Form12x.THE_ONE, true, "add-long/2addr");
+
+ public static final Dop SUB_LONG_2ADDR =
+ new Dop(DalvOps.SUB_LONG_2ADDR, DalvOps.SUB_LONG,
+ Form12x.THE_ONE, true, "sub-long/2addr");
+
+ public static final Dop MUL_LONG_2ADDR =
+ new Dop(DalvOps.MUL_LONG_2ADDR, DalvOps.MUL_LONG,
+ Form12x.THE_ONE, true, "mul-long/2addr");
+
+ public static final Dop DIV_LONG_2ADDR =
+ new Dop(DalvOps.DIV_LONG_2ADDR, DalvOps.DIV_LONG,
+ Form12x.THE_ONE, true, "div-long/2addr");
+
+ public static final Dop REM_LONG_2ADDR =
+ new Dop(DalvOps.REM_LONG_2ADDR, DalvOps.REM_LONG,
+ Form12x.THE_ONE, true, "rem-long/2addr");
+
+ public static final Dop AND_LONG_2ADDR =
+ new Dop(DalvOps.AND_LONG_2ADDR, DalvOps.AND_LONG,
+ Form12x.THE_ONE, true, "and-long/2addr");
+
+ public static final Dop OR_LONG_2ADDR =
+ new Dop(DalvOps.OR_LONG_2ADDR, DalvOps.OR_LONG,
+ Form12x.THE_ONE, true, "or-long/2addr");
+
+ public static final Dop XOR_LONG_2ADDR =
+ new Dop(DalvOps.XOR_LONG_2ADDR, DalvOps.XOR_LONG,
+ Form12x.THE_ONE, true, "xor-long/2addr");
+
+ public static final Dop SHL_LONG_2ADDR =
+ new Dop(DalvOps.SHL_LONG_2ADDR, DalvOps.SHL_LONG,
+ Form12x.THE_ONE, true, "shl-long/2addr");
+
+ public static final Dop SHR_LONG_2ADDR =
+ new Dop(DalvOps.SHR_LONG_2ADDR, DalvOps.SHR_LONG,
+ Form12x.THE_ONE, true, "shr-long/2addr");
+
+ public static final Dop USHR_LONG_2ADDR =
+ new Dop(DalvOps.USHR_LONG_2ADDR, DalvOps.USHR_LONG,
+ Form12x.THE_ONE, true, "ushr-long/2addr");
+
+ public static final Dop ADD_FLOAT_2ADDR =
+ new Dop(DalvOps.ADD_FLOAT_2ADDR, DalvOps.ADD_FLOAT,
+ Form12x.THE_ONE, true, "add-float/2addr");
+
+ public static final Dop SUB_FLOAT_2ADDR =
+ new Dop(DalvOps.SUB_FLOAT_2ADDR, DalvOps.SUB_FLOAT,
+ Form12x.THE_ONE, true, "sub-float/2addr");
+
+ public static final Dop MUL_FLOAT_2ADDR =
+ new Dop(DalvOps.MUL_FLOAT_2ADDR, DalvOps.MUL_FLOAT,
+ Form12x.THE_ONE, true, "mul-float/2addr");
+
+ public static final Dop DIV_FLOAT_2ADDR =
+ new Dop(DalvOps.DIV_FLOAT_2ADDR, DalvOps.DIV_FLOAT,
+ Form12x.THE_ONE, true, "div-float/2addr");
+
+ public static final Dop REM_FLOAT_2ADDR =
+ new Dop(DalvOps.REM_FLOAT_2ADDR, DalvOps.REM_FLOAT,
+ Form12x.THE_ONE, true, "rem-float/2addr");
+
+ public static final Dop ADD_DOUBLE_2ADDR =
+ new Dop(DalvOps.ADD_DOUBLE_2ADDR, DalvOps.ADD_DOUBLE,
+ Form12x.THE_ONE, true, "add-double/2addr");
+
+ public static final Dop SUB_DOUBLE_2ADDR =
+ new Dop(DalvOps.SUB_DOUBLE_2ADDR, DalvOps.SUB_DOUBLE,
+ Form12x.THE_ONE, true, "sub-double/2addr");
+
+ public static final Dop MUL_DOUBLE_2ADDR =
+ new Dop(DalvOps.MUL_DOUBLE_2ADDR, DalvOps.MUL_DOUBLE,
+ Form12x.THE_ONE, true, "mul-double/2addr");
+
+ public static final Dop DIV_DOUBLE_2ADDR =
+ new Dop(DalvOps.DIV_DOUBLE_2ADDR, DalvOps.DIV_DOUBLE,
+ Form12x.THE_ONE, true, "div-double/2addr");
+
+ public static final Dop REM_DOUBLE_2ADDR =
+ new Dop(DalvOps.REM_DOUBLE_2ADDR, DalvOps.REM_DOUBLE,
+ Form12x.THE_ONE, true, "rem-double/2addr");
+
+ public static final Dop ADD_INT_LIT16 =
+ new Dop(DalvOps.ADD_INT_LIT16, DalvOps.ADD_INT,
+ Form22s.THE_ONE, true, "add-int/lit16");
+
+ public static final Dop RSUB_INT =
+ new Dop(DalvOps.RSUB_INT, DalvOps.RSUB_INT,
+ Form22s.THE_ONE, true, "rsub-int");
+
+ public static final Dop MUL_INT_LIT16 =
+ new Dop(DalvOps.MUL_INT_LIT16, DalvOps.MUL_INT,
+ Form22s.THE_ONE, true, "mul-int/lit16");
+
+ public static final Dop DIV_INT_LIT16 =
+ new Dop(DalvOps.DIV_INT_LIT16, DalvOps.DIV_INT,
+ Form22s.THE_ONE, true, "div-int/lit16");
+
+ public static final Dop REM_INT_LIT16 =
+ new Dop(DalvOps.REM_INT_LIT16, DalvOps.REM_INT,
+ Form22s.THE_ONE, true, "rem-int/lit16");
+
+ public static final Dop AND_INT_LIT16 =
+ new Dop(DalvOps.AND_INT_LIT16, DalvOps.AND_INT,
+ Form22s.THE_ONE, true, "and-int/lit16");
+
+ public static final Dop OR_INT_LIT16 =
+ new Dop(DalvOps.OR_INT_LIT16, DalvOps.OR_INT,
+ Form22s.THE_ONE, true, "or-int/lit16");
+
+ public static final Dop XOR_INT_LIT16 =
+ new Dop(DalvOps.XOR_INT_LIT16, DalvOps.XOR_INT,
+ Form22s.THE_ONE, true, "xor-int/lit16");
+
+ public static final Dop ADD_INT_LIT8 =
+ new Dop(DalvOps.ADD_INT_LIT8, DalvOps.ADD_INT,
+ Form22b.THE_ONE, true, "add-int/lit8");
+
+ public static final Dop RSUB_INT_LIT8 =
+ new Dop(DalvOps.RSUB_INT_LIT8, DalvOps.RSUB_INT,
+ Form22b.THE_ONE, true, "rsub-int/lit8");
+
+ public static final Dop MUL_INT_LIT8 =
+ new Dop(DalvOps.MUL_INT_LIT8, DalvOps.MUL_INT,
+ Form22b.THE_ONE, true, "mul-int/lit8");
+
+ public static final Dop DIV_INT_LIT8 =
+ new Dop(DalvOps.DIV_INT_LIT8, DalvOps.DIV_INT,
+ Form22b.THE_ONE, true, "div-int/lit8");
+
+ public static final Dop REM_INT_LIT8 =
+ new Dop(DalvOps.REM_INT_LIT8, DalvOps.REM_INT,
+ Form22b.THE_ONE, true, "rem-int/lit8");
+
+ public static final Dop AND_INT_LIT8 =
+ new Dop(DalvOps.AND_INT_LIT8, DalvOps.AND_INT,
+ Form22b.THE_ONE, true, "and-int/lit8");
+
+ public static final Dop OR_INT_LIT8 =
+ new Dop(DalvOps.OR_INT_LIT8, DalvOps.OR_INT,
+ Form22b.THE_ONE, true, "or-int/lit8");
+
+ public static final Dop XOR_INT_LIT8 =
+ new Dop(DalvOps.XOR_INT_LIT8, DalvOps.XOR_INT,
+ Form22b.THE_ONE, true, "xor-int/lit8");
+
+ public static final Dop SHL_INT_LIT8 =
+ new Dop(DalvOps.SHL_INT_LIT8, DalvOps.SHL_INT,
+ Form22b.THE_ONE, true, "shl-int/lit8");
+
+ public static final Dop SHR_INT_LIT8 =
+ new Dop(DalvOps.SHR_INT_LIT8, DalvOps.SHR_INT,
+ Form22b.THE_ONE, true, "shr-int/lit8");
+
+ public static final Dop USHR_INT_LIT8 =
+ new Dop(DalvOps.USHR_INT_LIT8, DalvOps.USHR_INT,
+ Form22b.THE_ONE, true, "ushr-int/lit8");
+
+ // END(dops)
+
+ // Static initialization.
+ static {
+ DOPS = new Dop[DalvOps.MAX_VALUE - DalvOps.MIN_VALUE + 1];
+
+ set(SPECIAL_FORMAT);
+
+ // BEGIN(dops-init); GENERATED AUTOMATICALLY BY opcode-gen
+ set(NOP);
+ set(MOVE);
+ set(MOVE_FROM16);
+ set(MOVE_16);
+ set(MOVE_WIDE);
+ set(MOVE_WIDE_FROM16);
+ set(MOVE_WIDE_16);
+ set(MOVE_OBJECT);
+ set(MOVE_OBJECT_FROM16);
+ set(MOVE_OBJECT_16);
+ set(MOVE_RESULT);
+ set(MOVE_RESULT_WIDE);
+ set(MOVE_RESULT_OBJECT);
+ set(MOVE_EXCEPTION);
+ set(RETURN_VOID);
+ set(RETURN);
+ set(RETURN_WIDE);
+ set(RETURN_OBJECT);
+ set(CONST_4);
+ set(CONST_16);
+ set(CONST);
+ set(CONST_HIGH16);
+ set(CONST_WIDE_16);
+ set(CONST_WIDE_32);
+ set(CONST_WIDE);
+ set(CONST_WIDE_HIGH16);
+ set(CONST_STRING);
+ set(CONST_STRING_JUMBO);
+ set(CONST_CLASS);
+ set(MONITOR_ENTER);
+ set(MONITOR_EXIT);
+ set(CHECK_CAST);
+ set(INSTANCE_OF);
+ set(ARRAY_LENGTH);
+ set(NEW_INSTANCE);
+ set(NEW_ARRAY);
+ set(FILLED_NEW_ARRAY);
+ set(FILLED_NEW_ARRAY_RANGE);
+ set(FILL_ARRAY_DATA);
+ set(THROW);
+ set(GOTO);
+ set(GOTO_16);
+ set(GOTO_32);
+ set(PACKED_SWITCH);
+ set(SPARSE_SWITCH);
+ set(CMPL_FLOAT);
+ set(CMPG_FLOAT);
+ set(CMPL_DOUBLE);
+ set(CMPG_DOUBLE);
+ set(CMP_LONG);
+ set(IF_EQ);
+ set(IF_NE);
+ set(IF_LT);
+ set(IF_GE);
+ set(IF_GT);
+ set(IF_LE);
+ set(IF_EQZ);
+ set(IF_NEZ);
+ set(IF_LTZ);
+ set(IF_GEZ);
+ set(IF_GTZ);
+ set(IF_LEZ);
+ set(AGET);
+ set(AGET_WIDE);
+ set(AGET_OBJECT);
+ set(AGET_BOOLEAN);
+ set(AGET_BYTE);
+ set(AGET_CHAR);
+ set(AGET_SHORT);
+ set(APUT);
+ set(APUT_WIDE);
+ set(APUT_OBJECT);
+ set(APUT_BOOLEAN);
+ set(APUT_BYTE);
+ set(APUT_CHAR);
+ set(APUT_SHORT);
+ set(IGET);
+ set(IGET_WIDE);
+ set(IGET_OBJECT);
+ set(IGET_BOOLEAN);
+ set(IGET_BYTE);
+ set(IGET_CHAR);
+ set(IGET_SHORT);
+ set(IPUT);
+ set(IPUT_WIDE);
+ set(IPUT_OBJECT);
+ set(IPUT_BOOLEAN);
+ set(IPUT_BYTE);
+ set(IPUT_CHAR);
+ set(IPUT_SHORT);
+ set(SGET);
+ set(SGET_WIDE);
+ set(SGET_OBJECT);
+ set(SGET_BOOLEAN);
+ set(SGET_BYTE);
+ set(SGET_CHAR);
+ set(SGET_SHORT);
+ set(SPUT);
+ set(SPUT_WIDE);
+ set(SPUT_OBJECT);
+ set(SPUT_BOOLEAN);
+ set(SPUT_BYTE);
+ set(SPUT_CHAR);
+ set(SPUT_SHORT);
+ set(INVOKE_VIRTUAL);
+ set(INVOKE_SUPER);
+ set(INVOKE_DIRECT);
+ set(INVOKE_STATIC);
+ set(INVOKE_INTERFACE);
+ set(INVOKE_VIRTUAL_RANGE);
+ set(INVOKE_SUPER_RANGE);
+ set(INVOKE_DIRECT_RANGE);
+ set(INVOKE_STATIC_RANGE);
+ set(INVOKE_INTERFACE_RANGE);
+ set(NEG_INT);
+ set(NOT_INT);
+ set(NEG_LONG);
+ set(NOT_LONG);
+ set(NEG_FLOAT);
+ set(NEG_DOUBLE);
+ set(INT_TO_LONG);
+ set(INT_TO_FLOAT);
+ set(INT_TO_DOUBLE);
+ set(LONG_TO_INT);
+ set(LONG_TO_FLOAT);
+ set(LONG_TO_DOUBLE);
+ set(FLOAT_TO_INT);
+ set(FLOAT_TO_LONG);
+ set(FLOAT_TO_DOUBLE);
+ set(DOUBLE_TO_INT);
+ set(DOUBLE_TO_LONG);
+ set(DOUBLE_TO_FLOAT);
+ set(INT_TO_BYTE);
+ set(INT_TO_CHAR);
+ set(INT_TO_SHORT);
+ set(ADD_INT);
+ set(SUB_INT);
+ set(MUL_INT);
+ set(DIV_INT);
+ set(REM_INT);
+ set(AND_INT);
+ set(OR_INT);
+ set(XOR_INT);
+ set(SHL_INT);
+ set(SHR_INT);
+ set(USHR_INT);
+ set(ADD_LONG);
+ set(SUB_LONG);
+ set(MUL_LONG);
+ set(DIV_LONG);
+ set(REM_LONG);
+ set(AND_LONG);
+ set(OR_LONG);
+ set(XOR_LONG);
+ set(SHL_LONG);
+ set(SHR_LONG);
+ set(USHR_LONG);
+ set(ADD_FLOAT);
+ set(SUB_FLOAT);
+ set(MUL_FLOAT);
+ set(DIV_FLOAT);
+ set(REM_FLOAT);
+ set(ADD_DOUBLE);
+ set(SUB_DOUBLE);
+ set(MUL_DOUBLE);
+ set(DIV_DOUBLE);
+ set(REM_DOUBLE);
+ set(ADD_INT_2ADDR);
+ set(SUB_INT_2ADDR);
+ set(MUL_INT_2ADDR);
+ set(DIV_INT_2ADDR);
+ set(REM_INT_2ADDR);
+ set(AND_INT_2ADDR);
+ set(OR_INT_2ADDR);
+ set(XOR_INT_2ADDR);
+ set(SHL_INT_2ADDR);
+ set(SHR_INT_2ADDR);
+ set(USHR_INT_2ADDR);
+ set(ADD_LONG_2ADDR);
+ set(SUB_LONG_2ADDR);
+ set(MUL_LONG_2ADDR);
+ set(DIV_LONG_2ADDR);
+ set(REM_LONG_2ADDR);
+ set(AND_LONG_2ADDR);
+ set(OR_LONG_2ADDR);
+ set(XOR_LONG_2ADDR);
+ set(SHL_LONG_2ADDR);
+ set(SHR_LONG_2ADDR);
+ set(USHR_LONG_2ADDR);
+ set(ADD_FLOAT_2ADDR);
+ set(SUB_FLOAT_2ADDR);
+ set(MUL_FLOAT_2ADDR);
+ set(DIV_FLOAT_2ADDR);
+ set(REM_FLOAT_2ADDR);
+ set(ADD_DOUBLE_2ADDR);
+ set(SUB_DOUBLE_2ADDR);
+ set(MUL_DOUBLE_2ADDR);
+ set(DIV_DOUBLE_2ADDR);
+ set(REM_DOUBLE_2ADDR);
+ set(ADD_INT_LIT16);
+ set(RSUB_INT);
+ set(MUL_INT_LIT16);
+ set(DIV_INT_LIT16);
+ set(REM_INT_LIT16);
+ set(AND_INT_LIT16);
+ set(OR_INT_LIT16);
+ set(XOR_INT_LIT16);
+ set(ADD_INT_LIT8);
+ set(RSUB_INT_LIT8);
+ set(MUL_INT_LIT8);
+ set(DIV_INT_LIT8);
+ set(REM_INT_LIT8);
+ set(AND_INT_LIT8);
+ set(OR_INT_LIT8);
+ set(XOR_INT_LIT8);
+ set(SHL_INT_LIT8);
+ set(SHR_INT_LIT8);
+ set(USHR_INT_LIT8);
+ // END(dops-init)
+ }
+
+ /**
+ * This class is uninstantiable.
+ */
+ private Dops() {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Gets the {@link Dop} for the given opcode value.
+ *
+ * @param opcode {@code DalvOps.MIN_VALUE..DalvOps.MAX_VALUE;} the opcode value
+ * @return {@code non-null;} the associated opcode instance
+ */
+ public static Dop get(int opcode) {
+ int idx = opcode - DalvOps.MIN_VALUE;
+
+ try {
+ Dop result = DOPS[idx];
+ if (result != null) {
+ return result;
+ }
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ // Fall through.
+ }
+
+ throw new IllegalArgumentException("bogus opcode");
+ }
+
+ /**
+ * Gets the {@link Dop} with the given family/format combination, if
+ * any.
+ *
+ * @param family {@code DalvOps.MIN_VALUE..DalvOps.MAX_VALUE;} the opcode family
+ * @param format {@code non-null;} the opcode's instruction format
+ * @return {@code null-ok;} the corresponding opcode, or {@code null} if
+ * there is none
+ */
+ public static Dop getOrNull(int family, InsnFormat format) {
+ if (format == null) {
+ throw new NullPointerException("format == null");
+ }
+
+ int len = DOPS.length;
+
+ // TODO: Linear search is bad.
+ for (int i = 0; i < len; i++) {
+ Dop dop = DOPS[i];
+ if ((dop != null) &&
+ (dop.getFamily() == family) &&
+ (dop.getFormat() == format)) {
+ return dop;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Puts the given opcode into the table of all ops.
+ *
+ * @param opcode {@code non-null;} the opcode
+ */
+ private static void set(Dop opcode) {
+ int idx = opcode.getOpcode() - DalvOps.MIN_VALUE;
+ DOPS[idx] = opcode;
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/FixedSizeInsn.java b/dx/src/com/android/dx/dex/code/FixedSizeInsn.java
new file mode 100644
index 0000000..faed530
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/FixedSizeInsn.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Base class for instructions which are of a fixed code size and
+ * which use {@link InsnFormat} methods to write themselves. This
+ * includes most &mdash; but not all &mdash; instructions.
+ */
+public abstract class FixedSizeInsn extends DalvInsn {
+ /**
+ * Constructs an instance. The output address of this instance is initially
+ * unknown ({@code -1}).
+ *
+ * <p><b>Note:</b> In the unlikely event that an instruction takes
+ * absolutely no registers (e.g., a {@code nop} or a
+ * no-argument no-result * static method call), then the given
+ * register list may be passed as {@link
+ * RegisterSpecList#EMPTY}.</p>
+ *
+ * @param opcode the opcode; one of the constants from {@link Dops}
+ * @param position {@code non-null;} source position
+ * @param registers {@code non-null;} register list, including a
+ * result register if appropriate (that is, registers may be either
+ * ins or outs)
+ */
+ public FixedSizeInsn(Dop opcode, SourcePosition position,
+ RegisterSpecList registers) {
+ super(opcode, position, registers);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final int codeSize() {
+ return getOpcode().getFormat().codeSize();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final void writeTo(AnnotatedOutput out) {
+ getOpcode().getFormat().writeTo(out, this);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final DalvInsn withRegisterOffset(int delta) {
+ return withRegisters(getRegisters().withOffset(delta));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected final String listingString0(boolean noteIndices) {
+ return getOpcode().getFormat().listingString(this, noteIndices);
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/HighRegisterPrefix.java b/dx/src/com/android/dx/dex/code/HighRegisterPrefix.java
new file mode 100644
index 0000000..6fab094
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/HighRegisterPrefix.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Combination instruction which turns into a variable number of
+ * {@code move*} instructions to move a set of registers into
+ * registers starting at {@code 0} sequentially. This is used
+ * in translating an instruction whose register requirements cannot
+ * be met using a straightforward choice of a single opcode.
+ */
+public final class HighRegisterPrefix extends VariableSizeInsn {
+ /** {@code null-ok;} cached instructions, if constructed */
+ private SimpleInsn[] insns;
+
+ /**
+ * Constructs an instance. The output address of this instance is initially
+ * unknown ({@code -1}).
+ *
+ * @param position {@code non-null;} source position
+ * @param registers {@code non-null;} source registers
+ */
+ public HighRegisterPrefix(SourcePosition position,
+ RegisterSpecList registers) {
+ super(position, registers);
+
+ if (registers.size() == 0) {
+ throw new IllegalArgumentException("registers.size() == 0");
+ }
+
+ insns = null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int codeSize() {
+ int result = 0;
+
+ calculateInsnsIfNecessary();
+
+ for (SimpleInsn insn : insns) {
+ result += insn.codeSize();
+ }
+
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(AnnotatedOutput out) {
+ calculateInsnsIfNecessary();
+
+ for (SimpleInsn insn : insns) {
+ insn.writeTo(out);
+ }
+ }
+
+ /**
+ * Helper for {@link #codeSize} and {@link #writeTo} which sets up
+ * {@link #insns} if not already done.
+ */
+ private void calculateInsnsIfNecessary() {
+ if (insns != null) {
+ return;
+ }
+
+ RegisterSpecList registers = getRegisters();
+ int sz = registers.size();
+
+ insns = new SimpleInsn[sz];
+
+ for (int i = 0, outAt = 0; i < sz; i++) {
+ RegisterSpec src = registers.get(i);
+ insns[i] = moveInsnFor(src, outAt);
+ outAt += src.getCategory();
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public DalvInsn withRegisters(RegisterSpecList registers) {
+ return new HighRegisterPrefix(getPosition(), registers);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected String argString() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected String listingString0(boolean noteIndices) {
+ RegisterSpecList registers = getRegisters();
+ int sz = registers.size();
+ StringBuffer sb = new StringBuffer(100);
+
+ for (int i = 0, outAt = 0; i < sz; i++) {
+ RegisterSpec src = registers.get(i);
+ SimpleInsn insn = moveInsnFor(src, outAt);
+
+ if (i != 0) {
+ sb.append('\n');
+ }
+
+ sb.append(insn.listingString0(noteIndices));
+
+ outAt += src.getCategory();
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Returns the proper move instruction for the given source spec
+ * and destination index.
+ *
+ * @param src {@code non-null;} the source register spec
+ * @param destIndex {@code >= 0;} the destination register index
+ * @return {@code non-null;} the appropriate move instruction
+ */
+ private static SimpleInsn moveInsnFor(RegisterSpec src, int destIndex) {
+ return DalvInsn.makeMove(SourcePosition.NO_INFO,
+ RegisterSpec.make(destIndex, src.getType()),
+ src);
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/InsnFormat.java b/dx/src/com/android/dx/dex/code/InsnFormat.java
new file mode 100644
index 0000000..17c127f
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/InsnFormat.java
@@ -0,0 +1,578 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.CstKnownNull;
+import com.android.dx.rop.cst.CstLiteral64;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+/**
+ * Base class for all instruction format handlers. Instruction format
+ * handlers know how to translate {@link DalvInsn} instances into
+ * streams of code words, as well as human-oriented listing strings
+ * representing such translations.
+ */
+public abstract class InsnFormat {
+ /**
+ * Returns the string form, suitable for inclusion in a listing
+ * dump, of the given instruction. The instruction must be of this
+ * instance's format for proper operation.
+ *
+ * @param insn {@code non-null;} the instruction
+ * @param noteIndices whether to include an explicit notation of
+ * constant pool indices
+ * @return {@code non-null;} the string form
+ */
+ public final String listingString(DalvInsn insn, boolean noteIndices) {
+ String op = insn.getOpcode().getName();
+ String arg = insnArgString(insn);
+ String comment = insnCommentString(insn, noteIndices);
+ StringBuilder sb = new StringBuilder(100);
+
+ sb.append(op);
+
+ if (arg.length() != 0) {
+ sb.append(' ');
+ sb.append(arg);
+ }
+
+ if (comment.length() != 0) {
+ sb.append(" // ");
+ sb.append(comment);
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Returns the string form of the arguments to the given instruction.
+ * The instruction must be of this instance's format. If the instruction
+ * has no arguments, then the result should be {@code ""}, not
+ * {@code null}.
+ *
+ * <p>Subclasses must override this method.</p>
+ *
+ * @param insn {@code non-null;} the instruction
+ * @return {@code non-null;} the string form
+ */
+ public abstract String insnArgString(DalvInsn insn);
+
+ /**
+ * Returns the associated comment for the given instruction, if any.
+ * The instruction must be of this instance's format. If the instruction
+ * has no comment, then the result should be {@code ""}, not
+ * {@code null}.
+ *
+ * <p>Subclasses must override this method.</p>
+ *
+ * @param insn {@code non-null;} the instruction
+ * @param noteIndices whether to include an explicit notation of
+ * constant pool indices
+ * @return {@code non-null;} the string form
+ */
+ public abstract String insnCommentString(DalvInsn insn,
+ boolean noteIndices);
+
+ /**
+ * Gets the code size of instructions that use this format. The
+ * size is a number of 16-bit code units, not bytes. This should
+ * throw an exception if this format is of variable size.
+ *
+ * @return {@code >= 0;} the instruction length in 16-bit code units
+ */
+ public abstract int codeSize();
+
+ /**
+ * Returns whether or not the given instruction's arguments will
+ * fit in this instance's format. This includes such things as
+ * counting register arguments, checking register ranges, and
+ * making sure that additional arguments are of appropriate types
+ * and are in-range. If this format has a branch target but the
+ * instruction's branch offset is unknown, this method will simply
+ * not check the offset.
+ *
+ * <p>Subclasses must override this method.</p>
+ *
+ * @param insn {@code non-null;} the instruction to check
+ * @return {@code true} iff the instruction's arguments are
+ * appropriate for this instance, or {@code false} if not
+ */
+ public abstract boolean isCompatible(DalvInsn insn);
+
+ /**
+ * Returns whether or not the given instruction's branch offset will
+ * fit in this instance's format. This always returns {@code false}
+ * for formats that don't include a branch offset.
+ *
+ * <p>The default implementation of this method always returns
+ * {@code false}. Subclasses must override this method if they
+ * include branch offsets.</p>
+ *
+ * @param insn {@code non-null;} the instruction to check
+ * @return {@code true} iff the instruction's branch offset is
+ * appropriate for this instance, or {@code false} if not
+ */
+ public boolean branchFits(TargetInsn insn) {
+ return false;
+ }
+
+ /**
+ * Returns the next instruction format to try to match an instruction
+ * with, presuming that this instance isn't compatible, if any.
+ *
+ * <p>Subclasses must override this method.</p>
+ *
+ * @return {@code null-ok;} the next format to try, or {@code null} if
+ * there are no suitable alternatives
+ */
+ public abstract InsnFormat nextUp();
+
+ /**
+ * Writes the code units for the given instruction to the given
+ * output destination. The instruction must be of this instance's format.
+ *
+ * <p>Subclasses must override this method.</p>
+ *
+ * @param out {@code non-null;} the output destination to write to
+ * @param insn {@code non-null;} the instruction to write
+ */
+ public abstract void writeTo(AnnotatedOutput out, DalvInsn insn);
+
+ /**
+ * Helper method to return a register list string.
+ *
+ * @param list {@code non-null;} the list of registers
+ * @return {@code non-null;} the string form
+ */
+ protected static String regListString(RegisterSpecList list) {
+ int sz = list.size();
+ StringBuffer sb = new StringBuffer(sz * 5 + 2);
+
+ sb.append('{');
+
+ for (int i = 0; i < sz; i++) {
+ if (i != 0) {
+ sb.append(", ");
+ }
+ sb.append(list.get(i).regString());
+ }
+
+ sb.append('}');
+
+ return sb.toString();
+ }
+
+ /**
+ * Helper method to return a literal bits argument string.
+ *
+ * @param value the value
+ * @return {@code non-null;} the string form
+ */
+ protected static String literalBitsString(CstLiteralBits value) {
+ StringBuffer sb = new StringBuffer(100);
+
+ sb.append('#');
+
+ if (value instanceof CstKnownNull) {
+ sb.append("null");
+ } else {
+ sb.append(value.typeName());
+ sb.append(' ');
+ sb.append(value.toHuman());
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Helper method to return a literal bits comment string.
+ *
+ * @param value the value
+ * @param width the width of the constant, in bits (used for displaying
+ * the uninterpreted bits; one of: {@code 4 8 16 32 64}
+ * @return {@code non-null;} the comment
+ */
+ protected static String literalBitsComment(CstLiteralBits value,
+ int width) {
+ StringBuffer sb = new StringBuffer(20);
+
+ sb.append("#");
+
+ long bits;
+
+ if (value instanceof CstLiteral64) {
+ bits = ((CstLiteral64) value).getLongBits();
+ } else {
+ bits = value.getIntBits();
+ }
+
+ switch (width) {
+ case 4: sb.append(Hex.uNibble((int) bits)); break;
+ case 8: sb.append(Hex.u1((int) bits)); break;
+ case 16: sb.append(Hex.u2((int) bits)); break;
+ case 32: sb.append(Hex.u4((int) bits)); break;
+ case 64: sb.append(Hex.u8(bits)); break;
+ default: {
+ throw new RuntimeException("shouldn't happen");
+ }
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Helper method to return a branch address string.
+ *
+ * @param insn {@code non-null;} the instruction in question
+ * @return {@code non-null;} the string form of the instruction's branch target
+ */
+ protected static String branchString(DalvInsn insn) {
+ TargetInsn ti = (TargetInsn) insn;
+ int address = ti.getTargetAddress();
+
+ return (address == (char) address) ? Hex.u2(address) : Hex.u4(address);
+ }
+
+ /**
+ * Helper method to return the comment for a branch.
+ *
+ * @param insn {@code non-null;} the instruction in question
+ * @return {@code non-null;} the comment
+ */
+ protected static String branchComment(DalvInsn insn) {
+ TargetInsn ti = (TargetInsn) insn;
+ int offset = ti.getTargetOffset();
+
+ return (offset == (short) offset) ? Hex.s2(offset) : Hex.s4(offset);
+ }
+
+ /**
+ * Helper method to return a constant string.
+ *
+ * @param insn {@code non-null;} a constant-bearing instruction
+ * @return {@code non-null;} the string form of the contained constant
+ */
+ protected static String cstString(DalvInsn insn) {
+ CstInsn ci = (CstInsn) insn;
+ Constant cst = ci.getConstant();
+
+ return cst.toHuman();
+ }
+
+ /**
+ * Helper method to return an instruction comment for a constant.
+ *
+ * @param insn {@code non-null;} a constant-bearing instruction
+ * @return {@code non-null;} comment string representing the constant
+ */
+ protected static String cstComment(DalvInsn insn) {
+ CstInsn ci = (CstInsn) insn;
+
+ if (! ci.hasIndex()) {
+ return "";
+ }
+
+ StringBuilder sb = new StringBuilder(20);
+ int index = ci.getIndex();
+
+ sb.append(ci.getConstant().typeName());
+ sb.append('@');
+
+ if (index < 65536) {
+ sb.append(Hex.u2(index));
+ } else {
+ sb.append(Hex.u4(index));
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Helper method to determine if a signed int value fits in a nibble.
+ *
+ * @param value the value in question
+ * @return {@code true} iff it's in the range -8..+7
+ */
+ protected static boolean signedFitsInNibble(int value) {
+ return (value >= -8) && (value <= 7);
+ }
+
+ /**
+ * Helper method to determine if an unsigned int value fits in a nibble.
+ *
+ * @param value the value in question
+ * @return {@code true} iff it's in the range 0..0xf
+ */
+ protected static boolean unsignedFitsInNibble(int value) {
+ return value == (value & 0xf);
+ }
+
+ /**
+ * Helper method to determine if a signed int value fits in a byte.
+ *
+ * @param value the value in question
+ * @return {@code true} iff it's in the range -0x80..+0x7f
+ */
+ protected static boolean signedFitsInByte(int value) {
+ return (byte) value == value;
+ }
+
+ /**
+ * Helper method to determine if an unsigned int value fits in a byte.
+ *
+ * @param value the value in question
+ * @return {@code true} iff it's in the range 0..0xff
+ */
+ protected static boolean unsignedFitsInByte(int value) {
+ return value == (value & 0xff);
+ }
+
+ /**
+ * Helper method to determine if a signed int value fits in a short.
+ *
+ * @param value the value in question
+ * @return {@code true} iff it's in the range -0x8000..+0x7fff
+ */
+ protected static boolean signedFitsInShort(int value) {
+ return (short) value == value;
+ }
+
+ /**
+ * Helper method to determine if an unsigned int value fits in a short.
+ *
+ * @param value the value in question
+ * @return {@code true} iff it's in the range 0..0xffff
+ */
+ protected static boolean unsignedFitsInShort(int value) {
+ return value == (value & 0xffff);
+ }
+
+ /**
+ * Helper method to determine if a signed int value fits in three bytes.
+ *
+ * @param value the value in question
+ * @return {@code true} iff it's in the range -0x800000..+0x7fffff
+ */
+ protected static boolean signedFitsIn3Bytes(int value) {
+ return value == ((value << 8) >> 8);
+ }
+
+ /**
+ * Helper method to extract the callout-argument index from an
+ * appropriate instruction.
+ *
+ * @param insn {@code non-null;} the instruction
+ * @return {@code >= 0;} the callout argument index
+ */
+ protected static int argIndex(DalvInsn insn) {
+ int arg = ((CstInteger) ((CstInsn) insn).getConstant()).getValue();
+
+ if (arg < 0) {
+ throw new IllegalArgumentException("bogus insn");
+ }
+
+ return arg;
+ }
+
+ /**
+ * Helper method to combine an opcode and a second byte of data into
+ * the appropriate form for emitting into a code buffer.
+ *
+ * @param insn {@code non-null;} the instruction containing the opcode
+ * @param arg {@code 0..255;} arbitrary other byte value
+ * @return combined value
+ */
+ protected static short opcodeUnit(DalvInsn insn, int arg) {
+ if ((arg & 0xff) != arg) {
+ throw new IllegalArgumentException("arg out of range 0..255");
+ }
+
+ int opcode = insn.getOpcode().getOpcode();
+
+ if ((opcode & 0xff) != opcode) {
+ throw new IllegalArgumentException("opcode out of range 0..255");
+ }
+
+ return (short) (opcode | (arg << 8));
+ }
+
+ /**
+ * Helper method to combine two bytes into a code unit.
+ *
+ * @param low {@code 0..255;} low byte
+ * @param high {@code 0..255;} high byte
+ * @return combined value
+ */
+ protected static short codeUnit(int low, int high) {
+ if ((low & 0xff) != low) {
+ throw new IllegalArgumentException("low out of range 0..255");
+ }
+
+ if ((high & 0xff) != high) {
+ throw new IllegalArgumentException("high out of range 0..255");
+ }
+
+ return (short) (low | (high << 8));
+ }
+
+ /**
+ * Helper method to combine four nibbles into a code unit.
+ *
+ * @param n0 {@code 0..15;} low nibble
+ * @param n1 {@code 0..15;} medium-low nibble
+ * @param n2 {@code 0..15;} medium-high nibble
+ * @param n3 {@code 0..15;} high nibble
+ * @return combined value
+ */
+ protected static short codeUnit(int n0, int n1, int n2, int n3) {
+ if ((n0 & 0xf) != n0) {
+ throw new IllegalArgumentException("n0 out of range 0..15");
+ }
+
+ if ((n1 & 0xf) != n1) {
+ throw new IllegalArgumentException("n1 out of range 0..15");
+ }
+
+ if ((n2 & 0xf) != n2) {
+ throw new IllegalArgumentException("n2 out of range 0..15");
+ }
+
+ if ((n3 & 0xf) != n3) {
+ throw new IllegalArgumentException("n3 out of range 0..15");
+ }
+
+ return (short) (n0 | (n1 << 4) | (n2 << 8) | (n3 << 12));
+ }
+
+ /**
+ * Helper method to combine two nibbles into a byte.
+ *
+ * @param low {@code 0..15;} low nibble
+ * @param high {@code 0..15;} high nibble
+ * @return {@code 0..255;} combined value
+ */
+ protected static int makeByte(int low, int high) {
+ if ((low & 0xf) != low) {
+ throw new IllegalArgumentException("low out of range 0..15");
+ }
+
+ if ((high & 0xf) != high) {
+ throw new IllegalArgumentException("high out of range 0..15");
+ }
+
+ return low | (high << 4);
+ }
+
+ /**
+ * Writes one code unit to the given output destination.
+ *
+ * @param out {@code non-null;} where to write to
+ * @param c0 code unit to write
+ */
+ protected static void write(AnnotatedOutput out, short c0) {
+ out.writeShort(c0);
+ }
+
+ /**
+ * Writes two code units to the given output destination.
+ *
+ * @param out {@code non-null;} where to write to
+ * @param c0 code unit to write
+ * @param c1 code unit to write
+ */
+ protected static void write(AnnotatedOutput out, short c0, short c1) {
+ out.writeShort(c0);
+ out.writeShort(c1);
+ }
+
+ /**
+ * Writes three code units to the given output destination.
+ *
+ * @param out {@code non-null;} where to write to
+ * @param c0 code unit to write
+ * @param c1 code unit to write
+ * @param c2 code unit to write
+ */
+ protected static void write(AnnotatedOutput out, short c0, short c1,
+ short c2) {
+ out.writeShort(c0);
+ out.writeShort(c1);
+ out.writeShort(c2);
+ }
+
+ /**
+ * Writes four code units to the given output destination.
+ *
+ * @param out {@code non-null;} where to write to
+ * @param c0 code unit to write
+ * @param c1 code unit to write
+ * @param c2 code unit to write
+ * @param c3 code unit to write
+ */
+ protected static void write(AnnotatedOutput out, short c0, short c1,
+ short c2, short c3) {
+ out.writeShort(c0);
+ out.writeShort(c1);
+ out.writeShort(c2);
+ out.writeShort(c3);
+ }
+
+ /**
+ * Writes five code units to the given output destination.
+ *
+ * @param out {@code non-null;} where to write to
+ * @param c0 code unit to write
+ * @param c1 code unit to write
+ * @param c2 code unit to write
+ * @param c3 code unit to write
+ * @param c4 code unit to write
+ */
+ protected static void write(AnnotatedOutput out, short c0, short c1,
+ short c2, short c3, short c4) {
+ out.writeShort(c0);
+ out.writeShort(c1);
+ out.writeShort(c2);
+ out.writeShort(c3);
+ out.writeShort(c4);
+ }
+
+ /**
+ * Writes six code units to the given output destination.
+ *
+ * @param out {@code non-null;} where to write to
+ * @param c0 code unit to write
+ * @param c1 code unit to write
+ * @param c2 code unit to write
+ * @param c3 code unit to write
+ * @param c4 code unit to write
+ * @param c5 code unit to write
+ */
+ protected static void write(AnnotatedOutput out, short c0, short c1,
+ short c2, short c3, short c4, short c5) {
+ out.writeShort(c0);
+ out.writeShort(c1);
+ out.writeShort(c2);
+ out.writeShort(c3);
+ out.writeShort(c4);
+ out.writeShort(c5);
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/LocalEnd.java b/dx/src/com/android/dx/dex/code/LocalEnd.java
new file mode 100644
index 0000000..1c2bf89
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/LocalEnd.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+
+/**
+ * Pseudo-instruction which is used to explicitly end the mapping of a
+ * register to a named local variable. That is, an instance of this
+ * class in an instruction stream indicates that starting with the
+ * subsequent instruction, the indicated variable is no longer valid.
+ */
+public final class LocalEnd extends ZeroSizeInsn {
+ /**
+ * {@code non-null;} register spec representing the local variable ended
+ * by this instance. <b>Note:</b> Technically, only the register
+ * number needs to be recorded here as the rest of the information
+ * is implicit in the ambient local variable state, but other code
+ * will check the other info for consistency.
+ */
+ private final RegisterSpec local;
+
+ /**
+ * Constructs an instance. The output address of this instance is initially
+ * unknown ({@code -1}).
+ *
+ * @param position {@code non-null;} source position
+ * @param local {@code non-null;} register spec representing the local
+ * variable introduced by this instance
+ */
+ public LocalEnd(SourcePosition position, RegisterSpec local) {
+ super(position);
+
+ if (local == null) {
+ throw new NullPointerException("local == null");
+ }
+
+ this.local = local;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public DalvInsn withRegisterOffset(int delta) {
+ return new LocalEnd(getPosition(), local.withOffset(delta));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public DalvInsn withRegisters(RegisterSpecList registers) {
+ return new LocalEnd(getPosition(), local);
+ }
+
+ /**
+ * Gets the register spec representing the local variable ended
+ * by this instance.
+ *
+ * @return {@code non-null;} the register spec
+ */
+ public RegisterSpec getLocal() {
+ return local;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected String argString() {
+ return local.toString();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected String listingString0(boolean noteIndices) {
+ return "local-end " + LocalStart.localString(local);
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/LocalList.java b/dx/src/com/android/dx/dex/code/LocalList.java
new file mode 100644
index 0000000..94ca663
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/LocalList.java
@@ -0,0 +1,948 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecSet;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.FixedSizeList;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * List of local variables. Each local variable entry indicates a
+ * range of code which it is valid for, a register number, a name,
+ * and a type.
+ */
+public final class LocalList extends FixedSizeList {
+ /** {@code non-null;} empty instance */
+ public static final LocalList EMPTY = new LocalList(0);
+
+ /** whether to run the self-check code */
+ private static final boolean DEBUG = false;
+
+ /**
+ * Constructs an instance. All indices initially contain {@code null}.
+ *
+ * @param size {@code >= 0;} the size of the list
+ */
+ public LocalList(int size) {
+ super(size);
+ }
+
+ /**
+ * Gets the element at the given index. It is an error to call
+ * this with the index for an element which was never set; if you
+ * do that, this will throw {@code NullPointerException}.
+ *
+ * @param n {@code >= 0, < size();} which index
+ * @return {@code non-null;} element at that index
+ */
+ public Entry get(int n) {
+ return (Entry) get0(n);
+ }
+
+ /**
+ * Sets the entry at the given index.
+ *
+ * @param n {@code >= 0, < size();} which index
+ * @param entry {@code non-null;} the entry to set at {@code n}
+ */
+ public void set(int n, Entry entry) {
+ set0(n, entry);
+ }
+
+ /**
+ * Does a human-friendly dump of this instance.
+ *
+ * @param out {@code non-null;} where to dump
+ * @param prefix {@code non-null;} prefix to attach to each line of output
+ */
+ public void debugPrint(PrintStream out, String prefix) {
+ int sz = size();
+
+ for (int i = 0; i < sz; i++) {
+ out.print(prefix);
+ out.println(get(i));
+ }
+ }
+
+ /**
+ * Disposition of a local entry.
+ */
+ public static enum Disposition {
+ /** local started (introduced) */
+ START,
+
+ /** local ended without being replaced */
+ END_SIMPLY,
+
+ /** local ended because it was directly replaced */
+ END_REPLACED,
+
+ /** local ended because it was moved to a different register */
+ END_MOVED,
+
+ /**
+ * local ended because the previous local clobbered this one
+ * (because it is category-2)
+ */
+ END_CLOBBERED_BY_PREV,
+
+ /**
+ * local ended because the next local clobbered this one
+ * (because this one is a category-2)
+ */
+ END_CLOBBERED_BY_NEXT;
+ }
+
+ /**
+ * Entry in a local list.
+ */
+ public static class Entry implements Comparable<Entry> {
+ /** {@code >= 0;} address */
+ private final int address;
+
+ /** {@code non-null;} disposition of the local */
+ private final Disposition disposition;
+
+ /** {@code non-null;} register spec representing the variable */
+ private final RegisterSpec spec;
+
+ /** {@code non-null;} variable type (derived from {@code spec}) */
+ private final CstType type;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param address {@code >= 0;} address
+ * @param disposition {@code non-null;} disposition of the local
+ * @param spec {@code non-null;} register spec representing
+ * the variable
+ */
+ public Entry(int address, Disposition disposition, RegisterSpec spec) {
+ if (address < 0) {
+ throw new IllegalArgumentException("address < 0");
+ }
+
+ if (disposition == null) {
+ throw new NullPointerException("disposition == null");
+ }
+
+ try {
+ if (spec.getLocalItem() == null) {
+ throw new NullPointerException(
+ "spec.getLocalItem() == null");
+ }
+ } catch (NullPointerException ex) {
+ // Elucidate the exception.
+ throw new NullPointerException("spec == null");
+ }
+
+ this.address = address;
+ this.disposition = disposition;
+ this.spec = spec;
+ this.type = CstType.intern(spec.getType());
+ }
+
+ /** {@inheritDoc} */
+ public String toString() {
+ return Integer.toHexString(address) + " " + disposition + " " +
+ spec;
+ }
+
+ /** {@inheritDoc} */
+ public boolean equals(Object other) {
+ if (!(other instanceof Entry)) {
+ return false;
+ }
+
+ return (compareTo((Entry) other) == 0);
+ }
+
+ /**
+ * Compares by (in priority order) address, end then start
+ * disposition (variants of end are all consistered
+ * equivalent), and spec.
+ *
+ * @param other {@code non-null;} entry to compare to
+ * @return {@code -1..1;} standard result of comparison
+ */
+ public int compareTo(Entry other) {
+ if (address < other.address) {
+ return -1;
+ } else if (address > other.address) {
+ return 1;
+ }
+
+ boolean thisIsStart = isStart();
+ boolean otherIsStart = other.isStart();
+
+ if (thisIsStart != otherIsStart) {
+ return thisIsStart ? 1 : -1;
+ }
+
+ return spec.compareTo(other.spec);
+ }
+
+ /**
+ * Gets the address.
+ *
+ * @return {@code >= 0;} the address
+ */
+ public int getAddress() {
+ return address;
+ }
+
+ /**
+ * Gets the disposition.
+ *
+ * @return {@code non-null;} the disposition
+ */
+ public Disposition getDisposition() {
+ return disposition;
+ }
+
+ /**
+ * Gets whether this is a local start. This is just shorthand for
+ * {@code getDisposition() == Disposition.START}.
+ *
+ * @return {@code true} iff this is a start
+ */
+ public boolean isStart() {
+ return disposition == Disposition.START;
+ }
+
+ /**
+ * Gets the variable name.
+ *
+ * @return {@code null-ok;} the variable name
+ */
+ public CstUtf8 getName() {
+ return spec.getLocalItem().getName();
+ }
+
+ /**
+ * Gets the variable signature.
+ *
+ * @return {@code null-ok;} the variable signature
+ */
+ public CstUtf8 getSignature() {
+ return spec.getLocalItem().getSignature();
+ }
+
+ /**
+ * Gets the variable's type.
+ *
+ * @return {@code non-null;} the type
+ */
+ public CstType getType() {
+ return type;
+ }
+
+ /**
+ * Gets the number of the register holding the variable.
+ *
+ * @return {@code >= 0;} the number of the register holding
+ * the variable
+ */
+ public int getRegister() {
+ return spec.getReg();
+ }
+
+ /**
+ * Gets the RegisterSpec of the register holding the variable.
+ *
+ * @return {@code non-null;} RegisterSpec of the holding register.
+ */
+ public RegisterSpec getRegisterSpec() {
+ return spec;
+ }
+
+ /**
+ * Returns whether or not this instance matches the given spec.
+ *
+ * @param otherSpec {@code non-null;} the spec in question
+ * @return {@code true} iff this instance matches
+ * {@code spec}
+ */
+ public boolean matches(RegisterSpec otherSpec) {
+ return spec.equalsUsingSimpleType(otherSpec);
+ }
+
+ /**
+ * Returns whether or not this instance matches the spec in
+ * the given instance.
+ *
+ * @param other {@code non-null;} another entry
+ * @return {@code true} iff this instance's spec matches
+ * {@code other}
+ */
+ public boolean matches(Entry other) {
+ return matches(other.spec);
+ }
+
+ /**
+ * Returns an instance just like this one but with the disposition
+ * set as given.
+ *
+ * @param disposition {@code non-null;} the new disposition
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public Entry withDisposition(Disposition disposition) {
+ if (disposition == this.disposition) {
+ return this;
+ }
+
+ return new Entry(address, disposition, spec);
+ }
+ }
+
+ /**
+ * Constructs an instance for the given method, based on the given
+ * block order and intermediate local information.
+ *
+ * @param insns {@code non-null;} instructions to convert
+ * @return {@code non-null;} the constructed list
+ */
+ public static LocalList make(DalvInsnList insns) {
+ int sz = insns.size();
+
+ /*
+ * Go through the insn list, looking for all the local
+ * variable pseudoinstructions, splitting out LocalSnapshots
+ * into separate per-variable starts, adding explicit ends
+ * wherever a variable is replaced or moved, and collecting
+ * these and all the other local variable "activity"
+ * together into an output list (without the other insns).
+ *
+ * Note: As of this writing, this method won't be handed any
+ * insn lists that contain local ends, but I (danfuzz) expect
+ * that to change at some point, when we start feeding that
+ * info explicitly into the rop layer rather than only trying
+ * to infer it. So, given that expectation, this code is
+ * written to deal with them.
+ */
+
+ MakeState state = new MakeState(sz);
+
+ for (int i = 0; i < sz; i++) {
+ DalvInsn insn = insns.get(i);
+
+ if (insn instanceof LocalSnapshot) {
+ RegisterSpecSet snapshot =
+ ((LocalSnapshot) insn).getLocals();
+ state.snapshot(insn.getAddress(), snapshot);
+ } else if (insn instanceof LocalStart) {
+ RegisterSpec local = ((LocalStart) insn).getLocal();
+ state.startLocal(insn.getAddress(), local);
+ } else if (insn instanceof LocalEnd) {
+ RegisterSpec local = ((LocalEnd) insn).getLocal();
+ state.endLocal(insn.getAddress(), local);
+ }
+ }
+
+ LocalList result = state.finish();
+
+ if (DEBUG) {
+ debugVerify(result);
+ }
+
+ return result;
+ }
+
+ /**
+ * Debugging helper that verifies the constraint that a list doesn't
+ * contain any redundant local starts and that local ends that are
+ * due to replacements are properly annotated.
+ */
+ private static void debugVerify(LocalList locals) {
+ try {
+ debugVerify0(locals);
+ } catch (RuntimeException ex) {
+ int sz = locals.size();
+ for (int i = 0; i < sz; i++) {
+ System.err.println(locals.get(i));
+ }
+ throw ex;
+ }
+
+ }
+
+ /**
+ * Helper for {@link #debugVerify} which does most of the work.
+ */
+ private static void debugVerify0(LocalList locals) {
+ int sz = locals.size();
+ Entry[] active = new Entry[65536];
+
+ for (int i = 0; i < sz; i++) {
+ Entry e = locals.get(i);
+ int reg = e.getRegister();
+
+ if (e.isStart()) {
+ Entry already = active[reg];
+
+ if ((already != null) && e.matches(already)) {
+ throw new RuntimeException("redundant start at " +
+ Integer.toHexString(e.getAddress()) + ": got " +
+ e + "; had " + already);
+ }
+
+ active[reg] = e;
+ } else {
+ if (active[reg] == null) {
+ throw new RuntimeException("redundant end at " +
+ Integer.toHexString(e.getAddress()));
+ }
+
+ int addr = e.getAddress();
+ boolean foundStart = false;
+
+ for (int j = i + 1; j < sz; j++) {
+ Entry test = locals.get(j);
+ if (test.getAddress() != addr) {
+ break;
+ }
+ if (test.getRegisterSpec().getReg() == reg) {
+ if (test.isStart()) {
+ if (e.getDisposition()
+ != Disposition.END_REPLACED) {
+ throw new RuntimeException(
+ "improperly marked end at " +
+ Integer.toHexString(addr));
+ }
+ foundStart = true;
+ } else {
+ throw new RuntimeException(
+ "redundant end at " +
+ Integer.toHexString(addr));
+ }
+ }
+ }
+
+ if (!foundStart &&
+ (e.getDisposition() == Disposition.END_REPLACED)) {
+ throw new RuntimeException(
+ "improper end replacement claim at " +
+ Integer.toHexString(addr));
+ }
+
+ active[reg] = null;
+ }
+ }
+ }
+
+ /**
+ * Intermediate state when constructing a local list.
+ */
+ public static class MakeState {
+ /** {@code non-null;} result being collected */
+ private final ArrayList<Entry> result;
+
+ /**
+ * {@code >= 0;} running count of nulled result entries, to help with
+ * sizing the final list
+ */
+ private int nullResultCount;
+
+ /** {@code null-ok;} current register mappings */
+ private RegisterSpecSet regs;
+
+ /** {@code null-ok;} result indices where local ends are stored */
+ private int[] endIndices;
+
+ /** {@code >= 0;} last address seen */
+ private int lastAddress;
+
+ /**
+ * Constructs an instance.
+ */
+ public MakeState(int initialSize) {
+ result = new ArrayList<Entry>(initialSize);
+ nullResultCount = 0;
+ regs = null;
+ endIndices = null;
+ lastAddress = 0;
+ }
+
+ /**
+ * Checks the address and other vitals as a prerequisite to
+ * further processing.
+ *
+ * @param address {@code >= 0;} address about to be processed
+ * @param reg {@code >= 0;} register number about to be processed
+ */
+ private void aboutToProcess(int address, int reg) {
+ boolean first = (endIndices == null);
+
+ if ((address == lastAddress) && !first) {
+ return;
+ }
+
+ if (address < lastAddress) {
+ throw new RuntimeException("shouldn't happen");
+ }
+
+ if (first || (reg >= endIndices.length)) {
+ /*
+ * This is the first allocation of the state set and
+ * index array, or we need to grow. (The latter doesn't
+ * happen much; in fact, we have only ever observed
+ * it happening in test cases, never in "real" code.)
+ */
+ int newSz = reg + 1;
+ RegisterSpecSet newRegs = new RegisterSpecSet(newSz);
+ int[] newEnds = new int[newSz];
+ Arrays.fill(newEnds, -1);
+
+ if (!first) {
+ newRegs.putAll(regs);
+ System.arraycopy(endIndices, 0, newEnds, 0,
+ endIndices.length);
+ }
+
+ regs = newRegs;
+ endIndices = newEnds;
+ }
+ }
+
+ /**
+ * Sets the local state at the given address to the given snapshot.
+ * The first call on this instance must be to this method, so that
+ * the register state can be properly sized.
+ *
+ * @param address {@code >= 0;} the address
+ * @param specs {@code non-null;} spec set representing the locals
+ */
+ public void snapshot(int address, RegisterSpecSet specs) {
+ if (DEBUG) {
+ System.err.printf("%04x snapshot %s\n", address, specs);
+ }
+
+ int sz = specs.getMaxSize();
+ aboutToProcess(address, sz - 1);
+
+ for (int i = 0; i < sz; i++) {
+ RegisterSpec oldSpec = regs.get(i);
+ RegisterSpec newSpec = filterSpec(specs.get(i));
+
+ if (oldSpec == null) {
+ if (newSpec != null) {
+ startLocal(address, newSpec);
+ }
+ } else if (newSpec == null) {
+ endLocal(address, oldSpec);
+ } else if (! newSpec.equalsUsingSimpleType(oldSpec)) {
+ endLocal(address, oldSpec);
+ startLocal(address, newSpec);
+ }
+ }
+
+ if (DEBUG) {
+ System.err.printf("%04x snapshot done\n", address);
+ }
+ }
+
+ /**
+ * Starts a local at the given address.
+ *
+ * @param address {@code >= 0;} the address
+ * @param startedLocal {@code non-null;} spec representing the
+ * started local
+ */
+ public void startLocal(int address, RegisterSpec startedLocal) {
+ if (DEBUG) {
+ System.err.printf("%04x start %s\n", address, startedLocal);
+ }
+
+ int regNum = startedLocal.getReg();
+
+ startedLocal = filterSpec(startedLocal);
+ aboutToProcess(address, regNum);
+
+ RegisterSpec existingLocal = regs.get(regNum);
+
+ if (startedLocal.equalsUsingSimpleType(existingLocal)) {
+ // Silently ignore a redundant start.
+ return;
+ }
+
+ RegisterSpec movedLocal = regs.findMatchingLocal(startedLocal);
+ if (movedLocal != null) {
+ /*
+ * The same variable was moved from one register to another.
+ * So add an end for its old location.
+ */
+ addOrUpdateEnd(address, Disposition.END_MOVED, movedLocal);
+ }
+
+ int endAt = endIndices[regNum];
+
+ if (existingLocal != null) {
+ /*
+ * There is an existing (but non-matching) local.
+ * Add an explicit end for it.
+ */
+ add(address, Disposition.END_REPLACED, existingLocal);
+ } else if (endAt >= 0) {
+ /*
+ * Look for an end local for the same register at the
+ * same address. If found, then update it or delete
+ * it, depending on whether or not it represents the
+ * same variable as the one being started.
+ */
+ Entry endEntry = result.get(endAt);
+ if (endEntry.getAddress() == address) {
+ if (endEntry.matches(startedLocal)) {
+ /*
+ * There was already an end local for the same
+ * variable at the same address. This turns
+ * out to be superfluous, as we are starting
+ * up the exact same local. This situation can
+ * happen when a single local variable got
+ * somehow "split up" during intermediate
+ * processing. In any case, rather than represent
+ * the end-then-start, just remove the old end.
+ */
+ result.set(endAt, null);
+ nullResultCount++;
+ regs.put(startedLocal);
+ endIndices[regNum] = -1;
+ return;
+ } else {
+ /*
+ * There was a different variable ended at the
+ * same address. Update it to indicate that
+ * it was ended due to a replacement (rather than
+ * ending for no particular reason).
+ */
+ endEntry = endEntry.withDisposition(
+ Disposition.END_REPLACED);
+ result.set(endAt, endEntry);
+ }
+ }
+ }
+
+ /*
+ * The code above didn't find and remove an unnecessary
+ * local end, so we now have to add one or more entries to
+ * the output to capture the transition.
+ */
+
+ /*
+ * If the local just below (in the register set at reg-1)
+ * is of category-2, then it is ended by this new start.
+ */
+ if (regNum > 0) {
+ RegisterSpec justBelow = regs.get(regNum - 1);
+ if ((justBelow != null) && justBelow.isCategory2()) {
+ addOrUpdateEnd(address,
+ Disposition.END_CLOBBERED_BY_NEXT,
+ justBelow);
+ }
+ }
+
+ /*
+ * Similarly, if this local is category-2, then the local
+ * just above (if any) is ended by the start now being
+ * emitted.
+ */
+ if (startedLocal.isCategory2()) {
+ RegisterSpec justAbove = regs.get(regNum + 1);
+ if (justAbove != null) {
+ addOrUpdateEnd(address,
+ Disposition.END_CLOBBERED_BY_PREV,
+ justAbove);
+ }
+ }
+
+ /*
+ * TODO: Add an end for the same local in a different reg,
+ * if any (that is, if the local migrates from vX to vY,
+ * we should note that as a local end in vX).
+ */
+
+ add(address, Disposition.START, startedLocal);
+ }
+
+ /**
+ * Ends a local at the given address, using the disposition
+ * {@code END_SIMPLY}.
+ *
+ * @param address {@code >= 0;} the address
+ * @param endedLocal {@code non-null;} spec representing the
+ * local being ended
+ */
+ public void endLocal(int address, RegisterSpec endedLocal) {
+ endLocal(address, endedLocal, Disposition.END_SIMPLY);
+ }
+
+ /**
+ * Ends a local at the given address.
+ *
+ * @param address {@code >= 0;} the address
+ * @param endedLocal {@code non-null;} spec representing the
+ * local being ended
+ * @param disposition reason for the end
+ */
+ public void endLocal(int address, RegisterSpec endedLocal,
+ Disposition disposition) {
+ if (DEBUG) {
+ System.err.printf("%04x end %s\n", address, endedLocal);
+ }
+
+ int regNum = endedLocal.getReg();
+
+ endedLocal = filterSpec(endedLocal);
+ aboutToProcess(address, regNum);
+
+ int endAt = endIndices[regNum];
+
+ if (endAt >= 0) {
+ /*
+ * The local in the given register is already ended.
+ * Silently return without adding anything to the result.
+ */
+ return;
+ }
+
+ // Check for start and end at the same address.
+ if (checkForEmptyRange(address, endedLocal)) {
+ return;
+ }
+
+ add(address, disposition, endedLocal);
+ }
+
+ /**
+ * Helper for {@link #endLocal}, which handles the cases where
+ * and end local is issued at the same address as a start local
+ * for the same register. If this case is found, then this
+ * method will remove the start (as the local was never actually
+ * active), update the {@link #endIndices} to be accurate, and
+ * if needed update the newly-active end to reflect an altered
+ * disposition.
+ *
+ * @param address {@code >= 0;} the address
+ * @param endedLocal {@code non-null;} spec representing the
+ * local being ended
+ * @return {@code true} iff this method found the case in question
+ * and adjusted things accordingly
+ */
+ private boolean checkForEmptyRange(int address,
+ RegisterSpec endedLocal) {
+ int at = result.size() - 1;
+ Entry entry;
+
+ // Look for a previous entry at the same address.
+ for (/*at*/; at >= 0; at--) {
+ entry = result.get(at);
+
+ if (entry == null) {
+ continue;
+ }
+
+ if (entry.getAddress() != address) {
+ // We didn't find any match at the same address.
+ return false;
+ }
+
+ if (entry.matches(endedLocal)) {
+ break;
+ }
+ }
+
+ /*
+ * In fact, we found that the endedLocal had started at the
+ * same address, so do all the requisite cleanup.
+ */
+
+ regs.remove(endedLocal);
+ result.set(at, null);
+ nullResultCount++;
+
+ int regNum = endedLocal.getReg();
+ boolean found = false;
+ entry = null;
+
+ // Now look back further to update where the register ended.
+ for (at--; at >= 0; at--) {
+ entry = result.get(at);
+
+ if (entry == null) {
+ continue;
+ }
+
+ if (entry.getRegisterSpec().getReg() == regNum) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ // We found an end for the same register.
+ endIndices[regNum] = at;
+
+ if (entry.getAddress() == address) {
+ /*
+ * It's still the same address, so update the
+ * disposition.
+ */
+ result.set(at,
+ entry.withDisposition(Disposition.END_SIMPLY));
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Converts a given spec into the form acceptable for use in a
+ * local list. This, in particular, transforms the "known
+ * null" type into simply {@code Object}. This method needs to
+ * be called for any spec that is on its way into a locals
+ * list.
+ *
+ * <p>This isn't necessarily the cleanest way to achieve the
+ * goal of not representing known nulls in a locals list, but
+ * it gets the job done.</p>
+ *
+ * @param orig {@code null-ok;} the original spec
+ * @return {@code null-ok;} an appropriately modified spec, or the
+ * original if nothing needs to be done
+ */
+ private static RegisterSpec filterSpec(RegisterSpec orig) {
+ if ((orig != null) && (orig.getType() == Type.KNOWN_NULL)) {
+ return orig.withType(Type.OBJECT);
+ }
+
+ return orig;
+ }
+
+ /**
+ * Adds an entry to the result, updating the adjunct tables
+ * accordingly.
+ *
+ * @param address {@code >= 0;} the address
+ * @param disposition {@code non-null;} the disposition
+ * @param spec {@code non-null;} spec representing the local
+ */
+ private void add(int address, Disposition disposition,
+ RegisterSpec spec) {
+ int regNum = spec.getReg();
+
+ result.add(new Entry(address, disposition, spec));
+
+ if (disposition == Disposition.START) {
+ regs.put(spec);
+ endIndices[regNum] = -1;
+ } else {
+ regs.remove(spec);
+ endIndices[regNum] = result.size() - 1;
+ }
+ }
+
+ /**
+ * Adds or updates an end local (changing its disposition). If
+ * this would cause an empty range for a local, this instead
+ * removes the local entirely.
+ *
+ * @param address {@code >= 0;} the address
+ * @param disposition {@code non-null;} the disposition
+ * @param spec {@code non-null;} spec representing the local
+ */
+ private void addOrUpdateEnd(int address, Disposition disposition,
+ RegisterSpec spec) {
+ if (disposition == Disposition.START) {
+ throw new RuntimeException("shouldn't happen");
+ }
+
+ int regNum = spec.getReg();
+ int endAt = endIndices[regNum];
+
+ if (endAt >= 0) {
+ // There is a previous end.
+ Entry endEntry = result.get(endAt);
+ if ((endEntry.getAddress() == address) &&
+ endEntry.getRegisterSpec().equals(spec)) {
+ /*
+ * The end is for the right address and variable, so
+ * update it.
+ */
+ result.set(endAt, endEntry.withDisposition(disposition));
+ regs.remove(spec); // TODO: Is this line superfluous?
+ return;
+ }
+ }
+
+ endLocal(address, spec, disposition);
+ }
+
+ /**
+ * Finishes processing altogether and gets the result.
+ *
+ * @return {@code non-null;} the result list
+ */
+ public LocalList finish() {
+ aboutToProcess(Integer.MAX_VALUE, 0);
+
+ int resultSz = result.size();
+ int finalSz = resultSz - nullResultCount;
+
+ if (finalSz == 0) {
+ return EMPTY;
+ }
+
+ /*
+ * Collect an array of only the non-null entries, and then
+ * sort it to get a consistent order for everything: Local
+ * ends and starts for a given address could come in any
+ * order, but we want ends before starts as well as
+ * registers in order (within ends or starts).
+ */
+
+ Entry[] resultArr = new Entry[finalSz];
+
+ if (resultSz == finalSz) {
+ result.toArray(resultArr);
+ } else {
+ int at = 0;
+ for (Entry e : result) {
+ if (e != null) {
+ resultArr[at++] = e;
+ }
+ }
+ }
+
+ Arrays.sort(resultArr);
+
+ LocalList resultList = new LocalList(finalSz);
+
+ for (int i = 0; i < finalSz; i++) {
+ resultList.set(i, resultArr[i]);
+ }
+
+ resultList.setImmutable();
+ return resultList;
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/LocalSnapshot.java b/dx/src/com/android/dx/dex/code/LocalSnapshot.java
new file mode 100644
index 0000000..baeab4c
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/LocalSnapshot.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.RegisterSpecSet;
+import com.android.dx.rop.code.SourcePosition;
+
+/**
+ * Pseudo-instruction which is used to hold a snapshot of the
+ * state of local variable name mappings that exists immediately after
+ * the instance in an instruction array.
+ */
+public final class LocalSnapshot extends ZeroSizeInsn {
+ /** {@code non-null;} local state associated with this instance */
+ private final RegisterSpecSet locals;
+
+ /**
+ * Constructs an instance. The output address of this instance is initially
+ * unknown ({@code -1}).
+ *
+ * @param position {@code non-null;} source position
+ * @param locals {@code non-null;} associated local variable state
+ */
+ public LocalSnapshot(SourcePosition position, RegisterSpecSet locals) {
+ super(position);
+
+ if (locals == null) {
+ throw new NullPointerException("locals == null");
+ }
+
+ this.locals = locals;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public DalvInsn withRegisterOffset(int delta) {
+ return new LocalSnapshot(getPosition(), locals.withOffset(delta));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public DalvInsn withRegisters(RegisterSpecList registers) {
+ return new LocalSnapshot(getPosition(), locals);
+ }
+
+ /**
+ * Gets the local state associated with this instance.
+ *
+ * @return {@code non-null;} the state
+ */
+ public RegisterSpecSet getLocals() {
+ return locals;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected String argString() {
+ return locals.toString();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected String listingString0(boolean noteIndices) {
+ int sz = locals.size();
+ int max = locals.getMaxSize();
+ StringBuffer sb = new StringBuffer(100 + sz * 40);
+
+ sb.append("local-snapshot");
+
+ for (int i = 0; i < max; i++) {
+ RegisterSpec spec = locals.get(i);
+ if (spec != null) {
+ sb.append("\n ");
+ sb.append(LocalStart.localString(spec));
+ }
+ }
+
+ return sb.toString();
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/LocalStart.java b/dx/src/com/android/dx/dex/code/LocalStart.java
new file mode 100644
index 0000000..9a17c5b
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/LocalStart.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+
+/**
+ * Pseudo-instruction which is used to introduce a new local variable. That
+ * is, an instance of this class in an instruction stream indicates that
+ * starting with the subsequent instruction, the indicated variable
+ * is bound.
+ */
+public final class LocalStart extends ZeroSizeInsn {
+ /**
+ * {@code non-null;} register spec representing the local variable introduced
+ * by this instance
+ */
+ private final RegisterSpec local;
+
+ /**
+ * Returns the local variable listing string for a single register spec.
+ *
+ * @param spec {@code non-null;} the spec to convert
+ * @return {@code non-null;} the string form
+ */
+ public static String localString(RegisterSpec spec) {
+ return spec.regString() + ' ' + spec.getLocalItem().toString() + ": " +
+ spec.getTypeBearer().toHuman();
+ }
+
+ /**
+ * Constructs an instance. The output address of this instance is initially
+ * unknown ({@code -1}).
+ *
+ * @param position {@code non-null;} source position
+ * @param local {@code non-null;} register spec representing the local
+ * variable introduced by this instance
+ */
+ public LocalStart(SourcePosition position, RegisterSpec local) {
+ super(position);
+
+ if (local == null) {
+ throw new NullPointerException("local == null");
+ }
+
+ this.local = local;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public DalvInsn withRegisterOffset(int delta) {
+ return new LocalStart(getPosition(), local.withOffset(delta));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public DalvInsn withRegisters(RegisterSpecList registers) {
+ return new LocalStart(getPosition(), local);
+ }
+
+ /**
+ * Gets the register spec representing the local variable introduced
+ * by this instance.
+ *
+ * @return {@code non-null;} the register spec
+ */
+ public RegisterSpec getLocal() {
+ return local;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected String argString() {
+ return local.toString();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected String listingString0(boolean noteIndices) {
+ return "local-start " + localString(local);
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/OddSpacer.java b/dx/src/com/android/dx/dex/code/OddSpacer.java
new file mode 100644
index 0000000..756a0e2
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/OddSpacer.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Pseudo-instruction which either turns into a {@code nop} or
+ * nothingness, in order to make the subsequent instruction have an
+ * even address. This is used to align (subsequent) instructions that
+ * require it.
+ */
+public final class OddSpacer extends VariableSizeInsn {
+ /**
+ * Constructs an instance. The output address of this instance is initially
+ * unknown ({@code -1}).
+ *
+ * @param position {@code non-null;} source position
+ */
+ public OddSpacer(SourcePosition position) {
+ super(position, RegisterSpecList.EMPTY);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int codeSize() {
+ return (getAddress() & 1);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(AnnotatedOutput out) {
+ if (codeSize() != 0) {
+ out.writeShort(InsnFormat.codeUnit(DalvOps.NOP, 0));
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public DalvInsn withRegisters(RegisterSpecList registers) {
+ return new OddSpacer(getPosition());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected String argString() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected String listingString0(boolean noteIndices) {
+ if (codeSize() == 0) {
+ return null;
+ }
+
+ return "nop // spacer";
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/OutputCollector.java b/dx/src/com/android/dx/dex/code/OutputCollector.java
new file mode 100644
index 0000000..d78e5fc
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/OutputCollector.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import java.util.ArrayList;
+
+/**
+ * Destination for {@link DalvInsn} instances being output. This class
+ * receives and collects instructions in two pieces &mdash; a primary
+ * list and a suffix (generally consisting of adjunct data referred to
+ * by the primary list, such as switch case tables) &mdash; which it
+ * merges and emits back out in the form of a {@link DalvInsnList}
+ * instance.
+ */
+public final class OutputCollector {
+ /**
+ * {@code non-null;} the associated finisher (which holds the instruction
+ * list in-progress)
+ */
+ private final OutputFinisher finisher;
+
+ /**
+ * {@code null-ok;} suffix for the output, or {@code null} if the suffix
+ * has been appended to the main output (by {@link #appendSuffixToOutput})
+ */
+ private ArrayList<DalvInsn> suffix;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param initialCapacity {@code >= 0;} initial capacity of the output list
+ * @param suffixInitialCapacity {@code >= 0;} initial capacity of the output
+ * suffix
+ * @param regCount {@code >= 0;} register count for the method
+ */
+ public OutputCollector(int initialCapacity, int suffixInitialCapacity,
+ int regCount) {
+ this.finisher = new OutputFinisher(initialCapacity, regCount);
+ this.suffix = new ArrayList<DalvInsn>(suffixInitialCapacity);
+ }
+
+ /**
+ * Adds an instruction to the output.
+ *
+ * @param insn {@code non-null;} the instruction to add
+ */
+ public void add(DalvInsn insn) {
+ finisher.add(insn);
+ }
+
+ /**
+ * Reverses a branch which is buried a given number of instructions
+ * backward in the output. It is illegal to call this unless the
+ * indicated instruction really is a reversible branch.
+ *
+ * @param which how many instructions back to find the branch;
+ * {@code 0} is the most recently added instruction,
+ * {@code 1} is the instruction before that, etc.
+ * @param newTarget {@code non-null;} the new target for the reversed branch
+ */
+ public void reverseBranch(int which, CodeAddress newTarget) {
+ finisher.reverseBranch(which, newTarget);
+ }
+
+ /**
+ * Adds an instruction to the output suffix.
+ *
+ * @param insn {@code non-null;} the instruction to add
+ */
+ public void addSuffix(DalvInsn insn) {
+ suffix.add(insn);
+ }
+
+ /**
+ * Gets the results of all the calls on this instance, in the form of
+ * an {@link OutputFinisher}.
+ *
+ * @return {@code non-null;} the output finisher
+ * @throws UnsupportedOperationException if this method has
+ * already been called
+ */
+ public OutputFinisher getFinisher() {
+ if (suffix == null) {
+ throw new UnsupportedOperationException("already processed");
+ }
+
+ appendSuffixToOutput();
+ return finisher;
+ }
+
+ /**
+ * Helper for {@link #getFinisher}, which appends the suffix to
+ * the primary output.
+ */
+ private void appendSuffixToOutput() {
+ int size = suffix.size();
+
+ for (int i = 0; i < size; i++) {
+ finisher.add(suffix.get(i));
+ }
+
+ suffix = null;
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/OutputFinisher.java b/dx/src/com/android/dx/dex/code/OutputFinisher.java
new file mode 100644
index 0000000..4a51198
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/OutputFinisher.java
@@ -0,0 +1,737 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.LocalItem;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.RegisterSpecSet;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstMemberRef;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Type;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+
+/**
+ * Processor for instruction lists, which takes a "first cut" of
+ * instruction selection as a basis and produces a "final cut" in the
+ * form of a {@link DalvInsnList} instance.
+ */
+public final class OutputFinisher {
+ /**
+ * {@code >= 0;} register count for the method, not including any extra
+ * "reserved" registers needed to translate "difficult" instructions
+ */
+ private final int unreservedRegCount;
+
+ /** {@code non-null;} the list of instructions, per se */
+ private ArrayList<DalvInsn> insns;
+
+ /** whether any instruction has position info */
+ private boolean hasAnyPositionInfo;
+
+ /** whether any instruction has local variable info */
+ private boolean hasAnyLocalInfo;
+
+ /**
+ * {@code >= 0;} the count of reserved registers (low-numbered
+ * registers used when expanding instructions that can't be
+ * represented simply); becomes valid after a call to {@link
+ * #massageInstructions}
+ */
+ private int reservedCount;
+
+ /**
+ * Constructs an instance. It initially contains no instructions.
+ *
+ * @param regCount {@code >= 0;} register count for the method
+ * @param initialCapacity {@code >= 0;} initial capacity of the instructions
+ * list
+ */
+ public OutputFinisher(int initialCapacity, int regCount) {
+ this.unreservedRegCount = regCount;
+ this.insns = new ArrayList<DalvInsn>(initialCapacity);
+ this.reservedCount = -1;
+ this.hasAnyPositionInfo = false;
+ this.hasAnyLocalInfo = false;
+ }
+
+ /**
+ * Returns whether any of the instructions added to this instance
+ * come with position info.
+ *
+ * @return whether any of the instructions added to this instance
+ * come with position info
+ */
+ public boolean hasAnyPositionInfo() {
+ return hasAnyPositionInfo;
+ }
+
+ /**
+ * Returns whether this instance has any local variable information.
+ *
+ * @return whether this instance has any local variable information
+ */
+ public boolean hasAnyLocalInfo() {
+ return hasAnyLocalInfo;
+ }
+
+ /**
+ * Helper for {@link #add} which scrutinizes a single
+ * instruction for local variable information.
+ *
+ * @param insn {@code non-null;} instruction to scrutinize
+ * @return {@code true} iff the instruction refers to any
+ * named locals
+ */
+ private static boolean hasLocalInfo(DalvInsn insn) {
+ if (insn instanceof LocalSnapshot) {
+ RegisterSpecSet specs = ((LocalSnapshot) insn).getLocals();
+ int size = specs.size();
+ for (int i = 0; i < size; i++) {
+ if (hasLocalInfo(specs.get(i))) {
+ return true;
+ }
+ }
+ } else if (insn instanceof LocalStart) {
+ RegisterSpec spec = ((LocalStart) insn).getLocal();
+ if (hasLocalInfo(spec)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Helper for {@link #hasAnyLocalInfo} which scrutinizes a single
+ * register spec.
+ *
+ * @param spec {@code non-null;} spec to scrutinize
+ * @return {@code true} iff the spec refers to any
+ * named locals
+ */
+ private static boolean hasLocalInfo(RegisterSpec spec) {
+ return (spec != null)
+ && (spec.getLocalItem().getName() != null);
+ }
+
+ /**
+ * Returns the set of all constants referred to by instructions added
+ * to this instance.
+ *
+ * @return {@code non-null;} the set of constants
+ */
+ public HashSet<Constant> getAllConstants() {
+ HashSet<Constant> result = new HashSet<Constant>(20);
+
+ for (DalvInsn insn : insns) {
+ addConstants(result, insn);
+ }
+
+ return result;
+ }
+
+ /**
+ * Helper for {@link #getAllConstants} which adds all the info for
+ * a single instruction.
+ *
+ * @param result {@code non-null;} result set to add to
+ * @param insn {@code non-null;} instruction to scrutinize
+ */
+ private static void addConstants(HashSet<Constant> result,
+ DalvInsn insn) {
+ if (insn instanceof CstInsn) {
+ Constant cst = ((CstInsn) insn).getConstant();
+ result.add(cst);
+ } else if (insn instanceof LocalSnapshot) {
+ RegisterSpecSet specs = ((LocalSnapshot) insn).getLocals();
+ int size = specs.size();
+ for (int i = 0; i < size; i++) {
+ addConstants(result, specs.get(i));
+ }
+ } else if (insn instanceof LocalStart) {
+ RegisterSpec spec = ((LocalStart) insn).getLocal();
+ addConstants(result, spec);
+ }
+ }
+
+ /**
+ * Helper for {@link #getAllConstants} which adds all the info for
+ * a single {@code RegisterSpec}.
+ *
+ * @param result {@code non-null;} result set to add to
+ * @param spec {@code null-ok;} register spec to add
+ */
+ private static void addConstants(HashSet<Constant> result,
+ RegisterSpec spec) {
+ if (spec == null) {
+ return;
+ }
+
+ LocalItem local = spec.getLocalItem();
+ CstUtf8 name = local.getName();
+ CstUtf8 signature = local.getSignature();
+ Type type = spec.getType();
+
+ if (type != Type.KNOWN_NULL) {
+ result.add(CstType.intern(type));
+ }
+
+ if (name != null) {
+ result.add(name);
+ }
+
+ if (signature != null) {
+ result.add(signature);
+ }
+ }
+
+ /**
+ * Adds an instruction to the output.
+ *
+ * @param insn {@code non-null;} the instruction to add
+ */
+ public void add(DalvInsn insn) {
+ insns.add(insn);
+ updateInfo(insn);
+ }
+
+ /**
+ * Inserts an instruction in the output at the given offset.
+ *
+ * @param at {@code >= 0;} what index to insert at
+ * @param insn {@code non-null;} the instruction to insert
+ */
+ public void insert(int at, DalvInsn insn) {
+ insns.add(at, insn);
+ updateInfo(insn);
+ }
+
+ /**
+ * Helper for {@link #add} and {@link #insert},
+ * which updates the position and local info flags.
+ *
+ * @param insn {@code non-null;} an instruction that was just introduced
+ */
+ private void updateInfo(DalvInsn insn) {
+ if (! hasAnyPositionInfo) {
+ SourcePosition pos = insn.getPosition();
+ if (pos.getLine() >= 0) {
+ hasAnyPositionInfo = true;
+ }
+ }
+
+ if (! hasAnyLocalInfo) {
+ if (hasLocalInfo(insn)) {
+ hasAnyLocalInfo = true;
+ }
+ }
+ }
+
+ /**
+ * Reverses a branch which is buried a given number of instructions
+ * backward in the output. It is illegal to call this unless the
+ * indicated instruction really is a reversible branch.
+ *
+ * @param which how many instructions back to find the branch;
+ * {@code 0} is the most recently added instruction,
+ * {@code 1} is the instruction before that, etc.
+ * @param newTarget {@code non-null;} the new target for the reversed branch
+ */
+ public void reverseBranch(int which, CodeAddress newTarget) {
+ int size = insns.size();
+ int index = size - which - 1;
+ TargetInsn targetInsn;
+
+ try {
+ targetInsn = (TargetInsn) insns.get(index);
+ } catch (IndexOutOfBoundsException ex) {
+ // Translate the exception.
+ throw new IllegalArgumentException("too few instructions");
+ } catch (ClassCastException ex) {
+ // Translate the exception.
+ throw new IllegalArgumentException("non-reversible instruction");
+ }
+
+ /*
+ * No need to call this.set(), since the format and other info
+ * are the same.
+ */
+ insns.set(index, targetInsn.withNewTargetAndReversed(newTarget));
+ }
+
+ /**
+ * Assigns indices in all instructions that need them, using the
+ * given callback to perform lookups. This should be called before
+ * calling {@link #finishProcessingAndGetList}.
+ *
+ * @param callback {@code non-null;} callback object
+ */
+ public void assignIndices(DalvCode.AssignIndicesCallback callback) {
+ for (DalvInsn insn : insns) {
+ if (insn instanceof CstInsn) {
+ assignIndices((CstInsn) insn, callback);
+ }
+ }
+ }
+
+ /**
+ * Helper for {@link #assignIndices} which does assignment for one
+ * instruction.
+ *
+ * @param insn {@code non-null;} the instruction
+ * @param callback {@code non-null;} the callback
+ */
+ private static void assignIndices(CstInsn insn,
+ DalvCode.AssignIndicesCallback callback) {
+ Constant cst = insn.getConstant();
+ int index = callback.getIndex(cst);
+
+ if (index >= 0) {
+ insn.setIndex(index);
+ }
+
+ if (cst instanceof CstMemberRef) {
+ CstMemberRef member = (CstMemberRef) cst;
+ CstType definer = member.getDefiningClass();
+ index = callback.getIndex(definer);
+ if (index >= 0) {
+ insn.setClassIndex(index);
+ }
+ }
+ }
+
+ /**
+ * Does final processing on this instance and gets the output as
+ * a {@link DalvInsnList}. Final processing consists of:
+ *
+ * <ul>
+ * <li>optionally renumbering registers (to make room as needed for
+ * expanded instructions)</li>
+ * <li>picking a final opcode for each instruction</li>
+ * <li>rewriting instructions, because of register number,
+ * constant pool index, or branch target size issues</li>
+ * <li>assigning final addresses</li>
+ * </ul>
+ *
+ * <p><b>Note:</b> This method may only be called once per instance
+ * of this class.</p>
+ *
+ * @return {@code non-null;} the output list
+ * @throws UnsupportedOperationException if this method has
+ * already been called
+ */
+ public DalvInsnList finishProcessingAndGetList() {
+ if (reservedCount >= 0) {
+ throw new UnsupportedOperationException("already processed");
+ }
+
+ InsnFormat[] formats = makeFormatsArray();
+ reserveRegisters(formats);
+ massageInstructions(formats);
+ assignAddressesAndFixBranches();
+
+ return DalvInsnList.makeImmutable(insns,
+ reservedCount + unreservedRegCount);
+ }
+
+ /**
+ * Helper for {@link #finishProcessingAndGetList}, which extracts
+ * the format out of each instruction into a separate array, to be
+ * further manipulated as things progress.
+ *
+ * @return {@code non-null;} the array of formats
+ */
+ private InsnFormat[] makeFormatsArray() {
+ int size = insns.size();
+ InsnFormat[] result = new InsnFormat[size];
+
+ for (int i = 0; i < size; i++) {
+ result[i] = insns.get(i).getOpcode().getFormat();
+ }
+
+ return result;
+ }
+
+ /**
+ * Helper for {@link #finishProcessingAndGetList}, which figures
+ * out how many reserved registers are required and then reserving
+ * them. It also updates the given {@code formats} array so
+ * as to avoid extra work when constructing the massaged
+ * instruction list.
+ *
+ * @param formats {@code non-null;} array of per-instruction format selections
+ */
+ private void reserveRegisters(InsnFormat[] formats) {
+ int oldReservedCount = (reservedCount < 0) ? 0 : reservedCount;
+
+ /*
+ * Call calculateReservedCount() and then perform register
+ * reservation, repeatedly until no new reservations happen.
+ */
+ for (;;) {
+ int newReservedCount = calculateReservedCount(formats);
+ if (oldReservedCount >= newReservedCount) {
+ break;
+ }
+
+ int reservedDifference = newReservedCount - oldReservedCount;
+ int size = insns.size();
+
+ for (int i = 0; i < size; i++) {
+ /*
+ * CodeAddress instance identity is used to link
+ * TargetInsns to their targets, so it is
+ * inappropriate to make replacements, and they don't
+ * have registers in any case. Hence, the instanceof
+ * test below.
+ */
+ DalvInsn insn = insns.get(i);
+ if (!(insn instanceof CodeAddress)) {
+ /*
+ * No need to call this.set() since the format and
+ * other info are the same.
+ */
+ insns.set(i, insn.withRegisterOffset(reservedDifference));
+ }
+ }
+
+ oldReservedCount = newReservedCount;
+ }
+
+ reservedCount = oldReservedCount;
+ }
+
+ /**
+ * Helper for {@link #reserveRegisters}, which does one
+ * pass over the instructions, calculating the number of
+ * registers that need to be reserved. It also updates the
+ * {@code formats} list to help avoid extra work in future
+ * register reservation passes.
+ *
+ * @param formats {@code non-null;} array of per-instruction format selections
+ * @return {@code >= 0;} the count of reserved registers
+ */
+ private int calculateReservedCount(InsnFormat[] formats) {
+ int size = insns.size();
+
+ /*
+ * Potential new value of reservedCount, which gets updated in the
+ * following loop. It starts out with the existing reservedCount
+ * and gets increased if it turns out that additional registers
+ * need to be reserved.
+ */
+ int newReservedCount = reservedCount;
+
+ for (int i = 0; i < size; i++) {
+ DalvInsn insn = insns.get(i);
+ InsnFormat originalFormat = formats[i];
+ InsnFormat newFormat = findFormatForInsn(insn, originalFormat);
+
+ if (originalFormat == newFormat) {
+ continue;
+ }
+
+ if (newFormat == null) {
+ /*
+ * The instruction will need to be expanded, so reserve
+ * registers for it.
+ */
+ int reserve = insn.getMinimumRegisterRequirement();
+ if (reserve > newReservedCount) {
+ newReservedCount = reserve;
+ }
+ }
+
+ formats[i] = newFormat;
+ }
+
+ return newReservedCount;
+ }
+
+ /**
+ * Attempts to fit the given instruction into a format, returning
+ * either a format that the instruction fits into or {@code null}
+ * to indicate that the instruction will need to be expanded. This
+ * fitting process starts with the given format as a first "best
+ * guess" and then pessimizes from there if necessary.
+ *
+ * @param insn {@code non-null;} the instruction in question
+ * @param format {@code null-ok;} the current guess as to the best instruction
+ * format to use; {@code null} means that no simple format fits
+ * @return {@code null-ok;} a possibly-different format, which is either a
+ * good fit or {@code null} to indicate that no simple format
+ * fits
+ */
+ private InsnFormat findFormatForInsn(DalvInsn insn, InsnFormat format) {
+ if (format == null) {
+ // The instruction is already known not to fit any simple format.
+ return format;
+ }
+
+ if (format.isCompatible(insn)) {
+ // The instruction already fits in the current best-known format.
+ return format;
+ }
+
+ Dop dop = insn.getOpcode();
+ int family = dop.getFamily();
+
+ for (;;) {
+ format = format.nextUp();
+ if ((format == null) ||
+ (format.isCompatible(insn) &&
+ (Dops.getOrNull(family, format) != null))) {
+ break;
+ }
+ }
+
+ return format;
+ }
+
+ /**
+ * Helper for {@link #finishProcessingAndGetList}, which goes
+ * through each instruction in the output, making sure its opcode
+ * can accomodate its arguments. In cases where the opcode is
+ * unable to do so, this replaces the instruction with a larger
+ * instruction with identical semantics that <i>will</i> work.
+ *
+ * <p>This method may also reserve a number of low-numbered
+ * registers, renumbering the instructions' original registers, in
+ * order to have register space available in which to move
+ * very-high registers when expanding instructions into
+ * multi-instruction sequences. This expansion is done when no
+ * simple instruction format can be found for a given instruction that
+ * is able to accomodate that instruction's registers.</p>
+ *
+ * <p>This method ignores issues of branch target size, since
+ * final addresses aren't known at the point that this method is
+ * called.</p>
+ *
+ * @param formats {@code non-null;} array of per-instruction format selections
+ */
+ private void massageInstructions(InsnFormat[] formats) {
+ if (reservedCount == 0) {
+ /*
+ * The easy common case: No registers were reserved, so we
+ * merely need to replace any instructions whose format changed
+ * during the reservation pass, but all instructions will stay
+ * at their original indices, and the instruction list doesn't
+ * grow.
+ */
+ int size = insns.size();
+
+ for (int i = 0; i < size; i++) {
+ DalvInsn insn = insns.get(i);
+ Dop dop = insn.getOpcode();
+ InsnFormat format = formats[i];
+
+ if (format != dop.getFormat()) {
+ dop = Dops.getOrNull(dop.getFamily(), format);
+ insns.set(i, insn.withOpcode(dop));
+ }
+ }
+ } else {
+ /*
+ * The difficult uncommon case: Some instructions have to be
+ * expanded to deal with high registers.
+ */
+ insns = performExpansion(formats);
+ }
+ }
+
+ /**
+ * Helper for {@link #massageInstructions}, which constructs a
+ * replacement list, where each {link DalvInsn} instance that
+ * couldn't be represented simply (due to register representation
+ * problems) is expanded into a series of instances that together
+ * perform the proper function.
+ *
+ * @param formats {@code non-null;} array of per-instruction format selections
+ * @return {@code non-null;} the replacement list
+ */
+ private ArrayList<DalvInsn> performExpansion(InsnFormat[] formats) {
+ int size = insns.size();
+ ArrayList<DalvInsn> result = new ArrayList<DalvInsn>(size * 2);
+
+ for (int i = 0; i < size; i++) {
+ DalvInsn insn = insns.get(i);
+ Dop dop = insn.getOpcode();
+ InsnFormat originalFormat = dop.getFormat();
+ InsnFormat currentFormat = formats[i];
+ DalvInsn prefix;
+ DalvInsn suffix;
+
+ if (currentFormat != null) {
+ // No expansion is necessary.
+ prefix = null;
+ suffix = null;
+ } else {
+ // Expansion is required.
+ prefix = insn.hrPrefix();
+ suffix = insn.hrSuffix();
+
+ /*
+ * Get the initial guess as to the hr version, but then
+ * let findFormatForInsn() pick a better format, if any.
+ */
+ insn = insn.hrVersion();
+ originalFormat = insn.getOpcode().getFormat();
+ currentFormat = findFormatForInsn(insn, originalFormat);
+ }
+
+ if (prefix != null) {
+ result.add(prefix);
+ }
+
+ if (currentFormat != originalFormat) {
+ dop = Dops.getOrNull(dop.getFamily(), currentFormat);
+ insn = insn.withOpcode(dop);
+ }
+ result.add(insn);
+
+ if (suffix != null) {
+ result.add(suffix);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Helper for {@link #finishProcessingAndGetList}, which assigns
+ * addresses to each instruction, possibly rewriting branches to
+ * fix ones that wouldn't otherwise be able to reach their
+ * targets.
+ */
+ private void assignAddressesAndFixBranches() {
+ for (;;) {
+ assignAddresses();
+ if (!fixBranches()) {
+ break;
+ }
+ }
+ }
+
+ /**
+ * Helper for {@link #assignAddressesAndFixBranches}, which
+ * assigns an address to each instruction, in order.
+ */
+ private void assignAddresses() {
+ int address = 0;
+ int size = insns.size();
+
+ for (int i = 0; i < size; i++) {
+ DalvInsn insn = insns.get(i);
+ insn.setAddress(address);
+ address += insn.codeSize();
+ }
+ }
+
+ /**
+ * Helper for {@link #assignAddressesAndFixBranches}, which checks
+ * the branch target size requirement of each branch instruction
+ * to make sure it fits. For instructions that don't fit, this
+ * rewrites them to use a {@code goto} of some sort. In the
+ * case of a conditional branch that doesn't fit, the sense of the
+ * test is reversed in order to branch around a {@code goto}
+ * to the original target.
+ *
+ * @return whether any branches had to be fixed
+ */
+ private boolean fixBranches() {
+ int size = insns.size();
+ boolean anyFixed = false;
+
+ for (int i = 0; i < size; i++) {
+ DalvInsn insn = insns.get(i);
+ if (!(insn instanceof TargetInsn)) {
+ // This loop only needs to inspect TargetInsns.
+ continue;
+ }
+
+ Dop dop = insn.getOpcode();
+ InsnFormat format = dop.getFormat();
+ TargetInsn target = (TargetInsn) insn;
+
+ if (format.branchFits(target)) {
+ continue;
+ }
+
+ if (dop.getFamily() == DalvOps.GOTO) {
+ // It is a goto; widen it if possible.
+ InsnFormat newFormat = findFormatForInsn(insn, format);
+ if (newFormat == null) {
+ /*
+ * The branch is already maximally large. This should
+ * only be possible if a method somehow manages to have
+ * more than 2^31 code units.
+ */
+ throw new UnsupportedOperationException("method too long");
+ }
+ dop = Dops.getOrNull(dop.getFamily(), newFormat);
+ insn = insn.withOpcode(dop);
+ insns.set(i, insn);
+ } else {
+ /*
+ * It is a conditional: Reverse its sense, and arrange for
+ * it to branch around an absolute goto to the original
+ * branch target.
+ *
+ * Note: An invariant of the list being processed is
+ * that every TargetInsn is followed by a CodeAddress.
+ * Hence, it is always safe to get the next element
+ * after a TargetInsn and cast it to CodeAddress, as
+ * is happening a few lines down.
+ *
+ * Also note: Size gets incremented by one here, as we
+ * have -- in the net -- added one additional element
+ * to the list, so we increment i to match. The added
+ * and changed elements will be inspected by a repeat
+ * call to this method after this invocation returns.
+ */
+ CodeAddress newTarget;
+ try {
+ newTarget = (CodeAddress) insns.get(i + 1);
+ } catch (IndexOutOfBoundsException ex) {
+ // The TargetInsn / CodeAddress invariant was violated.
+ throw new IllegalStateException(
+ "unpaired TargetInsn (dangling)");
+ } catch (ClassCastException ex) {
+ // The TargetInsn / CodeAddress invariant was violated.
+ throw new IllegalStateException("unpaired TargetInsn");
+ }
+ TargetInsn gotoInsn =
+ new TargetInsn(Dops.GOTO, target.getPosition(),
+ RegisterSpecList.EMPTY, target.getTarget());
+ insns.set(i, gotoInsn);
+ insns.add(i, target.withNewTargetAndReversed(newTarget));
+ size++;
+ i++;
+ }
+
+ anyFixed = true;
+ }
+
+ return anyFixed;
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/PositionList.java b/dx/src/com/android/dx/dex/code/PositionList.java
new file mode 100644
index 0000000..1e07e46
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/PositionList.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * List of source position entries. This class includes a utility
+ * method to extract an instance out of a {@link DalvInsnList}.
+ */
+public final class PositionList extends FixedSizeList {
+ /** {@code non-null;} empty instance */
+ public static final PositionList EMPTY = new PositionList(0);
+
+ /**
+ * constant for {@link #make} to indicate that no actual position
+ * information should be returned
+ */
+ public static final int NONE = 1;
+
+ /**
+ * constant for {@link #make} to indicate that only line number
+ * transitions should be returned
+ */
+ public static final int LINES = 2;
+
+ /**
+ * constant for {@link #make} to indicate that only "important" position
+ * information should be returned. This includes block starts and
+ * instructions that might throw.
+ */
+ public static final int IMPORTANT = 3;
+
+ /**
+ * Extracts and returns the source position information out of an
+ * instruction list.
+ *
+ * @param insns {@code non-null;} instructions to convert
+ * @param howMuch how much information should be included; one of the
+ * static constants defined by this class
+ * @return {@code non-null;} the positions list
+ */
+ public static PositionList make(DalvInsnList insns, int howMuch) {
+ switch (howMuch) {
+ case NONE: {
+ return EMPTY;
+ }
+ case LINES:
+ case IMPORTANT: {
+ // Valid.
+ break;
+ }
+ default: {
+ throw new IllegalArgumentException("bogus howMuch");
+ }
+ }
+
+ SourcePosition noInfo = SourcePosition.NO_INFO;
+ SourcePosition cur = noInfo;
+ int sz = insns.size();
+ PositionList.Entry[] arr = new PositionList.Entry[sz];
+ boolean lastWasTarget = false;
+ int at = 0;
+
+ for (int i = 0; i < sz; i++) {
+ DalvInsn insn = insns.get(i);
+
+ if (insn instanceof CodeAddress) {
+ lastWasTarget = true;;
+ continue;
+ }
+
+ SourcePosition pos = insn.getPosition();
+
+ if (pos.equals(noInfo) || pos.sameLine(cur)) {
+ continue;
+ }
+
+ if ((howMuch == IMPORTANT) && !lastWasTarget) {
+ continue;
+ }
+
+ cur = pos;
+ arr[at] = new PositionList.Entry(insn.getAddress(), pos);
+ at++;
+
+ lastWasTarget = false;
+ }
+
+ PositionList result = new PositionList(at);
+ for (int i = 0; i < at; i++) {
+ result.set(i, arr[i]);
+ }
+
+ result.setImmutable();
+ return result;
+ }
+
+ /**
+ * Constructs an instance. All indices initially contain {@code null}.
+ *
+ * @param size {@code >= 0;} the size of the list
+ */
+ public PositionList(int size) {
+ super(size);
+ }
+
+ /**
+ * Gets the element at the given index. It is an error to call
+ * this with the index for an element which was never set; if you
+ * do that, this will throw {@code NullPointerException}.
+ *
+ * @param n {@code >= 0, < size();} which index
+ * @return {@code non-null;} element at that index
+ */
+ public Entry get(int n) {
+ return (Entry) get0(n);
+ }
+
+ /**
+ * Sets the entry at the given index.
+ *
+ * @param n {@code >= 0, < size();} which index
+ * @param entry {@code non-null;} the entry to set at {@code n}
+ */
+ public void set(int n, Entry entry) {
+ set0(n, entry);
+ }
+
+ /**
+ * Entry in a position list.
+ */
+ public static class Entry {
+ /** {@code >= 0;} address of this entry */
+ private final int address;
+
+ /** {@code non-null;} corresponding source position information */
+ private final SourcePosition position;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param address {@code >= 0;} address of this entry
+ * @param position {@code non-null;} corresponding source position information
+ */
+ public Entry (int address, SourcePosition position) {
+ if (address < 0) {
+ throw new IllegalArgumentException("address < 0");
+ }
+
+ if (position == null) {
+ throw new NullPointerException("position == null");
+ }
+
+ this.address = address;
+ this.position = position;
+ }
+
+ /**
+ * Gets the address.
+ *
+ * @return {@code >= 0;} the address
+ */
+ public int getAddress() {
+ return address;
+ }
+
+ /**
+ * Gets the source position information.
+ *
+ * @return {@code non-null;} the position information
+ */
+ public SourcePosition getPosition() {
+ return position;
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/RopToDop.java b/dx/src/com/android/dx/dex/code/RopToDop.java
new file mode 100644
index 0000000..d8fa1cc
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/RopToDop.java
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.Insn;
+import com.android.dx.rop.code.RegOps;
+import com.android.dx.rop.code.Rop;
+import com.android.dx.rop.code.Rops;
+import com.android.dx.rop.code.ThrowingCstInsn;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Type;
+
+import java.util.HashMap;
+
+/**
+ * Translator from rop-level {@link Insn} instances to corresponding
+ * {@link Dop} instances.
+ */
+public final class RopToDop {
+ /** {@code non-null;} map from all the common rops to dalvik opcodes */
+ private static final HashMap<Rop, Dop> MAP;
+
+ /**
+ * This class is uninstantiable.
+ */
+ private RopToDop() {
+ // This space intentionally left blank.
+ }
+
+ static {
+ /*
+ * Note: The choices made here are to pick the optimistically
+ * smallest Dalvik opcode, and leave it to later processing to
+ * pessimize.
+ */
+ MAP = new HashMap<Rop, Dop>(400);
+ MAP.put(Rops.NOP, Dops.NOP);
+ MAP.put(Rops.MOVE_INT, Dops.MOVE);
+ MAP.put(Rops.MOVE_LONG, Dops.MOVE_WIDE);
+ MAP.put(Rops.MOVE_FLOAT, Dops.MOVE);
+ MAP.put(Rops.MOVE_DOUBLE, Dops.MOVE_WIDE);
+ MAP.put(Rops.MOVE_OBJECT, Dops.MOVE_OBJECT);
+ MAP.put(Rops.MOVE_PARAM_INT, Dops.MOVE);
+ MAP.put(Rops.MOVE_PARAM_LONG, Dops.MOVE_WIDE);
+ MAP.put(Rops.MOVE_PARAM_FLOAT, Dops.MOVE);
+ MAP.put(Rops.MOVE_PARAM_DOUBLE, Dops.MOVE_WIDE);
+ MAP.put(Rops.MOVE_PARAM_OBJECT, Dops.MOVE_OBJECT);
+
+ /*
+ * Note: No entry for MOVE_EXCEPTION, since it varies by
+ * exception type. (That is, there is no unique instance to
+ * add to the map.)
+ */
+
+ MAP.put(Rops.CONST_INT, Dops.CONST_4);
+ MAP.put(Rops.CONST_LONG, Dops.CONST_WIDE_16);
+ MAP.put(Rops.CONST_FLOAT, Dops.CONST_4);
+ MAP.put(Rops.CONST_DOUBLE, Dops.CONST_WIDE_16);
+
+ /*
+ * Note: No entry for CONST_OBJECT, since it needs to turn
+ * into either CONST_STRING or CONST_CLASS.
+ */
+
+ /*
+ * TODO: I think the only case of this is for null, and
+ * const/4 should cover that.
+ */
+ MAP.put(Rops.CONST_OBJECT_NOTHROW, Dops.CONST_4);
+
+ MAP.put(Rops.GOTO, Dops.GOTO);
+ MAP.put(Rops.IF_EQZ_INT, Dops.IF_EQZ);
+ MAP.put(Rops.IF_NEZ_INT, Dops.IF_NEZ);
+ MAP.put(Rops.IF_LTZ_INT, Dops.IF_LTZ);
+ MAP.put(Rops.IF_GEZ_INT, Dops.IF_GEZ);
+ MAP.put(Rops.IF_LEZ_INT, Dops.IF_LEZ);
+ MAP.put(Rops.IF_GTZ_INT, Dops.IF_GTZ);
+ MAP.put(Rops.IF_EQZ_OBJECT, Dops.IF_EQZ);
+ MAP.put(Rops.IF_NEZ_OBJECT, Dops.IF_NEZ);
+ MAP.put(Rops.IF_EQ_INT, Dops.IF_EQ);
+ MAP.put(Rops.IF_NE_INT, Dops.IF_NE);
+ MAP.put(Rops.IF_LT_INT, Dops.IF_LT);
+ MAP.put(Rops.IF_GE_INT, Dops.IF_GE);
+ MAP.put(Rops.IF_LE_INT, Dops.IF_LE);
+ MAP.put(Rops.IF_GT_INT, Dops.IF_GT);
+ MAP.put(Rops.IF_EQ_OBJECT, Dops.IF_EQ);
+ MAP.put(Rops.IF_NE_OBJECT, Dops.IF_NE);
+ MAP.put(Rops.SWITCH, Dops.SPARSE_SWITCH);
+ MAP.put(Rops.ADD_INT, Dops.ADD_INT_2ADDR);
+ MAP.put(Rops.ADD_LONG, Dops.ADD_LONG_2ADDR);
+ MAP.put(Rops.ADD_FLOAT, Dops.ADD_FLOAT_2ADDR);
+ MAP.put(Rops.ADD_DOUBLE, Dops.ADD_DOUBLE_2ADDR);
+ MAP.put(Rops.SUB_INT, Dops.SUB_INT_2ADDR);
+ MAP.put(Rops.SUB_LONG, Dops.SUB_LONG_2ADDR);
+ MAP.put(Rops.SUB_FLOAT, Dops.SUB_FLOAT_2ADDR);
+ MAP.put(Rops.SUB_DOUBLE, Dops.SUB_DOUBLE_2ADDR);
+ MAP.put(Rops.MUL_INT, Dops.MUL_INT_2ADDR);
+ MAP.put(Rops.MUL_LONG, Dops.MUL_LONG_2ADDR);
+ MAP.put(Rops.MUL_FLOAT, Dops.MUL_FLOAT_2ADDR);
+ MAP.put(Rops.MUL_DOUBLE, Dops.MUL_DOUBLE_2ADDR);
+ MAP.put(Rops.DIV_INT, Dops.DIV_INT_2ADDR);
+ MAP.put(Rops.DIV_LONG, Dops.DIV_LONG_2ADDR);
+ MAP.put(Rops.DIV_FLOAT, Dops.DIV_FLOAT_2ADDR);
+ MAP.put(Rops.DIV_DOUBLE, Dops.DIV_DOUBLE_2ADDR);
+ MAP.put(Rops.REM_INT, Dops.REM_INT_2ADDR);
+ MAP.put(Rops.REM_LONG, Dops.REM_LONG_2ADDR);
+ MAP.put(Rops.REM_FLOAT, Dops.REM_FLOAT_2ADDR);
+ MAP.put(Rops.REM_DOUBLE, Dops.REM_DOUBLE_2ADDR);
+ MAP.put(Rops.NEG_INT, Dops.NEG_INT);
+ MAP.put(Rops.NEG_LONG, Dops.NEG_LONG);
+ MAP.put(Rops.NEG_FLOAT, Dops.NEG_FLOAT);
+ MAP.put(Rops.NEG_DOUBLE, Dops.NEG_DOUBLE);
+ MAP.put(Rops.AND_INT, Dops.AND_INT_2ADDR);
+ MAP.put(Rops.AND_LONG, Dops.AND_LONG_2ADDR);
+ MAP.put(Rops.OR_INT, Dops.OR_INT_2ADDR);
+ MAP.put(Rops.OR_LONG, Dops.OR_LONG_2ADDR);
+ MAP.put(Rops.XOR_INT, Dops.XOR_INT_2ADDR);
+ MAP.put(Rops.XOR_LONG, Dops.XOR_LONG_2ADDR);
+ MAP.put(Rops.SHL_INT, Dops.SHL_INT_2ADDR);
+ MAP.put(Rops.SHL_LONG, Dops.SHL_LONG_2ADDR);
+ MAP.put(Rops.SHR_INT, Dops.SHR_INT_2ADDR);
+ MAP.put(Rops.SHR_LONG, Dops.SHR_LONG_2ADDR);
+ MAP.put(Rops.USHR_INT, Dops.USHR_INT_2ADDR);
+ MAP.put(Rops.USHR_LONG, Dops.USHR_LONG_2ADDR);
+ MAP.put(Rops.NOT_INT, Dops.NOT_INT);
+ MAP.put(Rops.NOT_LONG, Dops.NOT_LONG);
+
+ MAP.put(Rops.ADD_CONST_INT, Dops.ADD_INT_LIT8);
+ // Note: No dalvik ops for other types of add_const.
+
+ /*
+ * Note: No dalvik ops for any type of sub_const; there's a
+ * *reverse* sub (constant - reg) for ints, though, but that
+ * should end up getting handled at optimization time.
+ */
+
+ MAP.put(Rops.MUL_CONST_INT, Dops.MUL_INT_LIT8);
+ // Note: No dalvik ops for other types of mul_const.
+
+ MAP.put(Rops.DIV_CONST_INT, Dops.DIV_INT_LIT8);
+ // Note: No dalvik ops for other types of div_const.
+
+ MAP.put(Rops.REM_CONST_INT, Dops.REM_INT_LIT8);
+ // Note: No dalvik ops for other types of rem_const.
+
+ MAP.put(Rops.AND_CONST_INT, Dops.AND_INT_LIT8);
+ // Note: No dalvik op for and_const_long.
+
+ MAP.put(Rops.OR_CONST_INT, Dops.OR_INT_LIT8);
+ // Note: No dalvik op for or_const_long.
+
+ MAP.put(Rops.XOR_CONST_INT, Dops.XOR_INT_LIT8);
+ // Note: No dalvik op for xor_const_long.
+
+ MAP.put(Rops.SHL_CONST_INT, Dops.SHL_INT_LIT8);
+ // Note: No dalvik op for shl_const_long.
+
+ MAP.put(Rops.SHR_CONST_INT, Dops.SHR_INT_LIT8);
+ // Note: No dalvik op for shr_const_long.
+
+ MAP.put(Rops.USHR_CONST_INT, Dops.USHR_INT_LIT8);
+ // Note: No dalvik op for shr_const_long.
+
+ MAP.put(Rops.CMPL_LONG, Dops.CMP_LONG);
+ MAP.put(Rops.CMPL_FLOAT, Dops.CMPL_FLOAT);
+ MAP.put(Rops.CMPL_DOUBLE, Dops.CMPL_DOUBLE);
+ MAP.put(Rops.CMPG_FLOAT, Dops.CMPG_FLOAT);
+ MAP.put(Rops.CMPG_DOUBLE, Dops.CMPG_DOUBLE);
+ MAP.put(Rops.CONV_L2I, Dops.LONG_TO_INT);
+ MAP.put(Rops.CONV_F2I, Dops.FLOAT_TO_INT);
+ MAP.put(Rops.CONV_D2I, Dops.DOUBLE_TO_INT);
+ MAP.put(Rops.CONV_I2L, Dops.INT_TO_LONG);
+ MAP.put(Rops.CONV_F2L, Dops.FLOAT_TO_LONG);
+ MAP.put(Rops.CONV_D2L, Dops.DOUBLE_TO_LONG);
+ MAP.put(Rops.CONV_I2F, Dops.INT_TO_FLOAT);
+ MAP.put(Rops.CONV_L2F, Dops.LONG_TO_FLOAT);
+ MAP.put(Rops.CONV_D2F, Dops.DOUBLE_TO_FLOAT);
+ MAP.put(Rops.CONV_I2D, Dops.INT_TO_DOUBLE);
+ MAP.put(Rops.CONV_L2D, Dops.LONG_TO_DOUBLE);
+ MAP.put(Rops.CONV_F2D, Dops.FLOAT_TO_DOUBLE);
+ MAP.put(Rops.TO_BYTE, Dops.INT_TO_BYTE);
+ MAP.put(Rops.TO_CHAR, Dops.INT_TO_CHAR);
+ MAP.put(Rops.TO_SHORT, Dops.INT_TO_SHORT);
+ MAP.put(Rops.RETURN_VOID, Dops.RETURN_VOID);
+ MAP.put(Rops.RETURN_INT, Dops.RETURN);
+ MAP.put(Rops.RETURN_LONG, Dops.RETURN_WIDE);
+ MAP.put(Rops.RETURN_FLOAT, Dops.RETURN);
+ MAP.put(Rops.RETURN_DOUBLE, Dops.RETURN_WIDE);
+ MAP.put(Rops.RETURN_OBJECT, Dops.RETURN_OBJECT);
+ MAP.put(Rops.ARRAY_LENGTH, Dops.ARRAY_LENGTH);
+ MAP.put(Rops.THROW, Dops.THROW);
+ MAP.put(Rops.MONITOR_ENTER, Dops.MONITOR_ENTER);
+ MAP.put(Rops.MONITOR_EXIT, Dops.MONITOR_EXIT);
+ MAP.put(Rops.AGET_INT, Dops.AGET);
+ MAP.put(Rops.AGET_LONG, Dops.AGET_WIDE);
+ MAP.put(Rops.AGET_FLOAT, Dops.AGET);
+ MAP.put(Rops.AGET_DOUBLE, Dops.AGET_WIDE);
+ MAP.put(Rops.AGET_OBJECT, Dops.AGET_OBJECT);
+ MAP.put(Rops.AGET_BOOLEAN, Dops.AGET_BOOLEAN);
+ MAP.put(Rops.AGET_BYTE, Dops.AGET_BYTE);
+ MAP.put(Rops.AGET_CHAR, Dops.AGET_CHAR);
+ MAP.put(Rops.AGET_SHORT, Dops.AGET_SHORT);
+ MAP.put(Rops.APUT_INT, Dops.APUT);
+ MAP.put(Rops.APUT_LONG, Dops.APUT_WIDE);
+ MAP.put(Rops.APUT_FLOAT, Dops.APUT);
+ MAP.put(Rops.APUT_DOUBLE, Dops.APUT_WIDE);
+ MAP.put(Rops.APUT_OBJECT, Dops.APUT_OBJECT);
+ MAP.put(Rops.APUT_BOOLEAN, Dops.APUT_BOOLEAN);
+ MAP.put(Rops.APUT_BYTE, Dops.APUT_BYTE);
+ MAP.put(Rops.APUT_CHAR, Dops.APUT_CHAR);
+ MAP.put(Rops.APUT_SHORT, Dops.APUT_SHORT);
+ MAP.put(Rops.NEW_INSTANCE, Dops.NEW_INSTANCE);
+ MAP.put(Rops.CHECK_CAST, Dops.CHECK_CAST);
+ MAP.put(Rops.INSTANCE_OF, Dops.INSTANCE_OF);
+
+ MAP.put(Rops.GET_FIELD_LONG, Dops.IGET_WIDE);
+ MAP.put(Rops.GET_FIELD_FLOAT, Dops.IGET);
+ MAP.put(Rops.GET_FIELD_DOUBLE, Dops.IGET_WIDE);
+ MAP.put(Rops.GET_FIELD_OBJECT, Dops.IGET_OBJECT);
+ /*
+ * Note: No map entries for get_field_* for non-long integral types,
+ * since they need to be handled specially (see dopFor() below).
+ */
+
+ MAP.put(Rops.GET_STATIC_LONG, Dops.SGET_WIDE);
+ MAP.put(Rops.GET_STATIC_FLOAT, Dops.SGET);
+ MAP.put(Rops.GET_STATIC_DOUBLE, Dops.SGET_WIDE);
+ MAP.put(Rops.GET_STATIC_OBJECT, Dops.SGET_OBJECT);
+ /*
+ * Note: No map entries for get_static* for non-long integral types,
+ * since they need to be handled specially (see dopFor() below).
+ */
+
+ MAP.put(Rops.PUT_FIELD_LONG, Dops.IPUT_WIDE);
+ MAP.put(Rops.PUT_FIELD_FLOAT, Dops.IPUT);
+ MAP.put(Rops.PUT_FIELD_DOUBLE, Dops.IPUT_WIDE);
+ MAP.put(Rops.PUT_FIELD_OBJECT, Dops.IPUT_OBJECT);
+ /*
+ * Note: No map entries for put_field_* for non-long integral types,
+ * since they need to be handled specially (see dopFor() below).
+ */
+
+ MAP.put(Rops.PUT_STATIC_LONG, Dops.SPUT_WIDE);
+ MAP.put(Rops.PUT_STATIC_FLOAT, Dops.SPUT);
+ MAP.put(Rops.PUT_STATIC_DOUBLE, Dops.SPUT_WIDE);
+ MAP.put(Rops.PUT_STATIC_OBJECT, Dops.SPUT_OBJECT);
+ /*
+ * Note: No map entries for put_static* for non-long integral types,
+ * since they need to be handled specially (see dopFor() below).
+ */
+
+ /*
+ * Note: No map entries for invoke*, new_array, and
+ * filled_new_array, since they need to be handled specially
+ * (see dopFor() below).
+ */
+ }
+
+ /**
+ * Returns the dalvik opcode appropriate for the given register-based
+ * instruction.
+ *
+ * @param insn {@code non-null;} the original instruction
+ * @return the corresponding dalvik opcode; one of the constants in
+ * {@link Dops}
+ */
+ public static Dop dopFor(Insn insn) {
+ Rop rop = insn.getOpcode();
+
+ /*
+ * First, just try looking up the rop in the MAP of easy
+ * cases.
+ */
+ Dop result = MAP.get(rop);
+ if (result != null) {
+ return result;
+ }
+
+ /*
+ * There was no easy case for the rop, so look up the opcode, and
+ * do something special for each:
+ *
+ * The move_exception, new_array, filled_new_array, and
+ * invoke* opcodes won't be found in MAP, since they'll each
+ * have different source and/or result register types / lists.
+ *
+ * The get* and put* opcodes for (non-long) integral types
+ * aren't in the map, since the type signatures aren't
+ * sufficient to distinguish between the types (the salient
+ * source or result will always be just "int").
+ *
+ * And const instruction need to distinguish between strings and
+ * classes.
+ */
+
+ switch (rop.getOpcode()) {
+ case RegOps.MOVE_EXCEPTION: return Dops.MOVE_EXCEPTION;
+ case RegOps.INVOKE_STATIC: return Dops.INVOKE_STATIC;
+ case RegOps.INVOKE_VIRTUAL: return Dops.INVOKE_VIRTUAL;
+ case RegOps.INVOKE_SUPER: return Dops.INVOKE_SUPER;
+ case RegOps.INVOKE_DIRECT: return Dops.INVOKE_DIRECT;
+ case RegOps.INVOKE_INTERFACE: return Dops.INVOKE_INTERFACE;
+ case RegOps.NEW_ARRAY: return Dops.NEW_ARRAY;
+ case RegOps.FILLED_NEW_ARRAY: return Dops.FILLED_NEW_ARRAY;
+ case RegOps.FILL_ARRAY_DATA: return Dops.FILL_ARRAY_DATA;
+ case RegOps.MOVE_RESULT: {
+ RegisterSpec resultReg = insn.getResult();
+
+ if (resultReg == null) {
+ return Dops.NOP;
+ } else {
+ switch (resultReg.getBasicType()) {
+ case Type.BT_INT:
+ case Type.BT_FLOAT:
+ case Type.BT_BOOLEAN:
+ case Type.BT_BYTE:
+ case Type.BT_CHAR:
+ case Type.BT_SHORT:
+ return Dops.MOVE_RESULT;
+ case Type.BT_LONG:
+ case Type.BT_DOUBLE:
+ return Dops.MOVE_RESULT_WIDE;
+ case Type.BT_OBJECT:
+ return Dops.MOVE_RESULT_OBJECT;
+ default: {
+ throw new RuntimeException("Unexpected basic type");
+ }
+ }
+ }
+ }
+
+ case RegOps.GET_FIELD: {
+ CstFieldRef ref =
+ (CstFieldRef) ((ThrowingCstInsn) insn).getConstant();
+ int basicType = ref.getBasicType();
+ switch (basicType) {
+ case Type.BT_BOOLEAN: return Dops.IGET_BOOLEAN;
+ case Type.BT_BYTE: return Dops.IGET_BYTE;
+ case Type.BT_CHAR: return Dops.IGET_CHAR;
+ case Type.BT_SHORT: return Dops.IGET_SHORT;
+ case Type.BT_INT: return Dops.IGET;
+ }
+ break;
+ }
+ case RegOps.PUT_FIELD: {
+ CstFieldRef ref =
+ (CstFieldRef) ((ThrowingCstInsn) insn).getConstant();
+ int basicType = ref.getBasicType();
+ switch (basicType) {
+ case Type.BT_BOOLEAN: return Dops.IPUT_BOOLEAN;
+ case Type.BT_BYTE: return Dops.IPUT_BYTE;
+ case Type.BT_CHAR: return Dops.IPUT_CHAR;
+ case Type.BT_SHORT: return Dops.IPUT_SHORT;
+ case Type.BT_INT: return Dops.IPUT;
+ }
+ break;
+ }
+ case RegOps.GET_STATIC: {
+ CstFieldRef ref =
+ (CstFieldRef) ((ThrowingCstInsn) insn).getConstant();
+ int basicType = ref.getBasicType();
+ switch (basicType) {
+ case Type.BT_BOOLEAN: return Dops.SGET_BOOLEAN;
+ case Type.BT_BYTE: return Dops.SGET_BYTE;
+ case Type.BT_CHAR: return Dops.SGET_CHAR;
+ case Type.BT_SHORT: return Dops.SGET_SHORT;
+ case Type.BT_INT: return Dops.SGET;
+ }
+ break;
+ }
+ case RegOps.PUT_STATIC: {
+ CstFieldRef ref =
+ (CstFieldRef) ((ThrowingCstInsn) insn).getConstant();
+ int basicType = ref.getBasicType();
+ switch (basicType) {
+ case Type.BT_BOOLEAN: return Dops.SPUT_BOOLEAN;
+ case Type.BT_BYTE: return Dops.SPUT_BYTE;
+ case Type.BT_CHAR: return Dops.SPUT_CHAR;
+ case Type.BT_SHORT: return Dops.SPUT_SHORT;
+ case Type.BT_INT: return Dops.SPUT;
+ }
+ break;
+ }
+ case RegOps.CONST: {
+ Constant cst = ((ThrowingCstInsn) insn).getConstant();
+ if (cst instanceof CstType) {
+ return Dops.CONST_CLASS;
+ } else if (cst instanceof CstString) {
+ return Dops.CONST_STRING;
+ }
+ break;
+ }
+ }
+
+ throw new RuntimeException("unknown rop: " + rop);
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/RopTranslator.java b/dx/src/com/android/dx/dex/code/RopTranslator.java
new file mode 100644
index 0000000..a38ea11
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/RopTranslator.java
@@ -0,0 +1,872 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.BasicBlock;
+import com.android.dx.rop.code.BasicBlockList;
+import com.android.dx.rop.code.FillArrayDataInsn;
+import com.android.dx.rop.code.Insn;
+import com.android.dx.rop.code.LocalVariableInfo;
+import com.android.dx.rop.code.PlainCstInsn;
+import com.android.dx.rop.code.PlainInsn;
+import com.android.dx.rop.code.RegOps;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.RegisterSpecSet;
+import com.android.dx.rop.code.Rop;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.rop.code.SwitchInsn;
+import com.android.dx.rop.code.ThrowingCstInsn;
+import com.android.dx.rop.code.ThrowingInsn;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.Bits;
+import com.android.dx.util.IntList;
+
+import java.util.ArrayList;
+
+/**
+ * Translator from {@link RopMethod} to {@link DalvCode}. The {@link
+ * #translate} method is the thing to call on this class.
+ */
+public final class RopTranslator {
+ /** {@code non-null;} method to translate */
+ private final RopMethod method;
+
+ /**
+ * how much position info to preserve; one of the static
+ * constants in {@link PositionList}
+ */
+ private final int positionInfo;
+
+ /** {@code null-ok;} local variable info to use */
+ private final LocalVariableInfo locals;
+
+ /** {@code non-null;} container for all the address objects for the method */
+ private final BlockAddresses addresses;
+
+ /** {@code non-null;} list of output instructions in-progress */
+ private final OutputCollector output;
+
+ /** {@code non-null;} visitor to use during translation */
+ private final TranslationVisitor translationVisitor;
+
+ /** {@code >= 0;} register count for the method */
+ private final int regCount;
+
+ /** {@code null-ok;} block output order; becomes non-null in {@link #pickOrder} */
+ private int[] order;
+
+ /** size, in register units, of all the parameters to this method */
+ private final int paramSize;
+
+ /**
+ * true if the parameters to this method happen to be in proper order
+ * at the end of the frame (as the optimizer emits them)
+ */
+ private boolean paramsAreInOrder;
+
+ /**
+ * Translates a {@link RopMethod}. This may modify the given
+ * input.
+ *
+ * @param method {@code non-null;} the original method
+ * @param positionInfo how much position info to preserve; one of the
+ * static constants in {@link PositionList}
+ * @param locals {@code null-ok;} local variable information to use
+ * @param paramSize size, in register units, of all the parameters to
+ * this method
+ * @return {@code non-null;} the translated version
+ */
+ public static DalvCode translate(RopMethod method, int positionInfo,
+ LocalVariableInfo locals, int paramSize) {
+ RopTranslator translator =
+ new RopTranslator(method, positionInfo, locals,
+ paramSize);
+ return translator.translateAndGetResult();
+ }
+
+ /**
+ * Constructs an instance. This method is private. Use {@link #translate}.
+ *
+ * @param method {@code non-null;} the original method
+ * @param positionInfo how much position info to preserve; one of the
+ * static constants in {@link PositionList}
+ * @param locals {@code null-ok;} local variable information to use
+ * @param paramSize size, in register units, of all the parameters to
+ * this method
+ */
+ private RopTranslator(RopMethod method, int positionInfo,
+ LocalVariableInfo locals, int paramSize) {
+ this.method = method;
+ this.positionInfo = positionInfo;
+ this.locals = locals;
+ this.addresses = new BlockAddresses(method);
+ this.paramSize = paramSize;
+ this.order = null;
+ this.paramsAreInOrder = calculateParamsAreInOrder(method, paramSize);
+
+ BasicBlockList blocks = method.getBlocks();
+ int bsz = blocks.size();
+
+ /*
+ * Max possible instructions includes three code address
+ * objects per basic block (to the first and last instruction,
+ * and just past the end of the block), and the possibility of
+ * an extra goto at the end of each basic block.
+ */
+ int maxInsns = (bsz * 3) + blocks.getInstructionCount();
+
+ if (locals != null) {
+ /*
+ * If we're tracking locals, then there's could be another
+ * extra instruction per block (for the locals state at the
+ * start of the block) as well as one for each interblock
+ * local introduction.
+ */
+ maxInsns += bsz + locals.getAssignmentCount();
+ }
+
+ /*
+ * If params are not in order, we will need register space
+ * for them before this is all over...
+ */
+ this.regCount = blocks.getRegCount()
+ + (paramsAreInOrder ? 0 : this.paramSize);
+
+ this.output = new OutputCollector(maxInsns, bsz * 3, regCount);
+
+ if (locals != null) {
+ this.translationVisitor =
+ new LocalVariableAwareTranslationVisitor(output, locals);
+ } else {
+ this.translationVisitor = new TranslationVisitor(output);
+ }
+ }
+
+ /**
+ * Checks to see if the move-param instructions that occur in this
+ * method happen to slot the params in an order at the top of the
+ * stack frame that matches dalvik's calling conventions. This will
+ * alway result in "true" for methods that have run through the
+ * SSA optimizer.
+ *
+ * @param paramSize size, in register units, of all the parameters
+ * to this method
+ */
+ private static boolean calculateParamsAreInOrder(RopMethod method,
+ final int paramSize) {
+ final boolean[] paramsAreInOrder = { true };
+ final int initialRegCount = method.getBlocks().getRegCount();
+
+ /*
+ * We almost could just check the first block here, but the
+ * {@code cf} layer will put in a second move-param in a
+ * subsequent block in the case of synchronized methods.
+ */
+ method.getBlocks().forEachInsn(new Insn.BaseVisitor() {
+ public void visitPlainCstInsn(PlainCstInsn insn) {
+ if (insn.getOpcode().getOpcode()== RegOps.MOVE_PARAM) {
+ int param =
+ ((CstInteger) insn.getConstant()).getValue();
+
+ paramsAreInOrder[0] = paramsAreInOrder[0]
+ && ((initialRegCount - paramSize + param)
+ == insn.getResult().getReg());
+ }
+ }
+ });
+
+ return paramsAreInOrder[0];
+ }
+
+ /**
+ * Does the translation and returns the result.
+ *
+ * @return {@code non-null;} the result
+ */
+ private DalvCode translateAndGetResult() {
+ pickOrder();
+ outputInstructions();
+
+ StdCatchBuilder catches =
+ new StdCatchBuilder(method, order, addresses);
+
+ return new DalvCode(positionInfo, output.getFinisher(), catches);
+ }
+
+ /**
+ * Performs initial creation of output instructions based on the
+ * original blocks.
+ */
+ private void outputInstructions() {
+ BasicBlockList blocks = method.getBlocks();
+ int[] order = this.order;
+ int len = order.length;
+
+ // Process the blocks in output order.
+ for (int i = 0; i < len; i++) {
+ int nextI = i + 1;
+ int nextLabel = (nextI == order.length) ? -1 : order[nextI];
+ outputBlock(blocks.labelToBlock(order[i]), nextLabel);
+ }
+ }
+
+ /**
+ * Helper for {@link #outputInstructions}, which does the processing
+ * and output of one block.
+ *
+ * @param block {@code non-null;} the block to process and output
+ * @param nextLabel {@code >= -1;} the next block that will be processed, or
+ * {@code -1} if there is no next block
+ */
+ private void outputBlock(BasicBlock block, int nextLabel) {
+ // Append the code address for this block.
+ CodeAddress startAddress = addresses.getStart(block);
+ output.add(startAddress);
+
+ // Append the local variable state for the block.
+ if (locals != null) {
+ RegisterSpecSet starts = locals.getStarts(block);
+ output.add(new LocalSnapshot(startAddress.getPosition(),
+ starts));
+ }
+
+ /*
+ * Choose and append an output instruction for each original
+ * instruction.
+ */
+ translationVisitor.setBlock(block, addresses.getLast(block));
+ block.getInsns().forEach(translationVisitor);
+
+ // Insert the block end code address.
+ output.add(addresses.getEnd(block));
+
+ // Set up for end-of-block activities.
+
+ int succ = block.getPrimarySuccessor();
+ Insn lastInsn = block.getLastInsn();
+
+ /*
+ * Check for (and possibly correct for) a non-optimal choice of
+ * which block will get output next.
+ */
+
+ if ((succ >= 0) && (succ != nextLabel)) {
+ /*
+ * The block has a "primary successor" and that primary
+ * successor isn't the next block to be output.
+ */
+ Rop lastRop = lastInsn.getOpcode();
+ if ((lastRop.getBranchingness() == Rop.BRANCH_IF) &&
+ (block.getSecondarySuccessor() == nextLabel)) {
+ /*
+ * The block ends with an "if" of some sort, and its
+ * secondary successor (the "then") is in fact the
+ * next block to output. So, reverse the sense of
+ * the test, so that we can just emit the next block
+ * without an interstitial goto.
+ */
+ output.reverseBranch(1, addresses.getStart(succ));
+ } else {
+ /*
+ * Our only recourse is to add a goto here to get the
+ * flow to be correct.
+ */
+ TargetInsn insn =
+ new TargetInsn(Dops.GOTO, lastInsn.getPosition(),
+ RegisterSpecList.EMPTY,
+ addresses.getStart(succ));
+ output.add(insn);
+ }
+ }
+ }
+
+ /**
+ * Picks an order for the blocks by doing "trace" analysis.
+ */
+ private void pickOrder() {
+ BasicBlockList blocks = method.getBlocks();
+ int sz = blocks.size();
+ int maxLabel = blocks.getMaxLabel();
+ int[] workSet = Bits.makeBitSet(maxLabel);
+ int[] tracebackSet = Bits.makeBitSet(maxLabel);
+
+ for (int i = 0; i < sz; i++) {
+ BasicBlock one = blocks.get(i);
+ Bits.set(workSet, one.getLabel());
+ }
+
+ int[] order = new int[sz];
+ int at = 0;
+
+ /*
+ * Starting with the designated "first label" (that is, the
+ * first block of the method), add that label to the order,
+ * and then pick its first as-yet unordered successor to
+ * immediately follow it, giving top priority to the primary
+ * (aka default) successor (if any). Keep following successors
+ * until the trace runs out of possibilities. Then, continue
+ * by finding an unordered chain containing the first as-yet
+ * unordered block, and adding it to the order, and so on.
+ */
+ for (int label = method.getFirstLabel();
+ label != -1;
+ label = Bits.findFirst(workSet, 0)) {
+
+ /*
+ * Attempt to trace backward from the chosen block to an
+ * as-yet unordered predecessor which lists the chosen
+ * block as its primary successor, and so on, until we
+ * fail to find such an unordered predecessor. Start the
+ * trace with that block. Note that the first block in the
+ * method has no predecessors, so in that case this loop
+ * will simply terminate with zero iterations and without
+ * picking a new starter block.
+ */
+ traceBack:
+ for (;;) {
+ IntList preds = method.labelToPredecessors(label);
+ int psz = preds.size();
+
+ for (int i = 0; i < psz; i++) {
+ int predLabel = preds.get(i);
+
+ if (Bits.get(tracebackSet, predLabel)) {
+ /*
+ * We found a predecessor loop; stop tracing back
+ * from here.
+ */
+ break;
+ }
+
+ if (!Bits.get(workSet, predLabel)) {
+ // This one's already ordered.
+ continue;
+ }
+
+ BasicBlock pred = blocks.labelToBlock(predLabel);
+ if (pred.getPrimarySuccessor() == label) {
+ // Found one!
+ label = predLabel;
+ Bits.set(tracebackSet, label);
+ continue traceBack;
+ }
+ }
+
+ // Failed to find a better block to start the trace.
+ break;
+ }
+
+ /*
+ * Trace a path from the chosen block to one of its
+ * unordered successors (hopefully the primary), and so
+ * on, until we run out of unordered successors.
+ */
+ while (label != -1) {
+ Bits.clear(workSet, label);
+ Bits.clear(tracebackSet, label);
+ order[at] = label;
+ at++;
+
+ BasicBlock one = blocks.labelToBlock(label);
+ BasicBlock preferredBlock = blocks.preferredSuccessorOf(one);
+
+ if (preferredBlock == null) {
+ break;
+ }
+
+ int preferred = preferredBlock.getLabel();
+ int primary = one.getPrimarySuccessor();
+
+ if (Bits.get(workSet, preferred)) {
+ /*
+ * Order the current block's preferred successor
+ * next, as it has yet to be scheduled.
+ */
+ label = preferred;
+ } else if ((primary != preferred) && (primary >= 0)
+ && Bits.get(workSet, primary)) {
+ /*
+ * The primary is available, so use that.
+ */
+ label = primary;
+ } else {
+ /*
+ * There's no obvious candidate, so pick the first
+ * one that's available, if any.
+ */
+ IntList successors = one.getSuccessors();
+ int ssz = successors.size();
+ label = -1;
+ for (int i = 0; i < ssz; i++) {
+ int candidate = successors.get(i);
+ if (Bits.get(workSet, candidate)) {
+ label = candidate;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (at != sz) {
+ // There was a duplicate block label.
+ throw new RuntimeException("shouldn't happen");
+ }
+
+ this.order = order;
+ }
+
+ /**
+ * Gets the complete register list (result and sources) out of a
+ * given rop instruction. For insns that are commutative, have
+ * two register sources, and have a source equal to the result,
+ * place that source first.
+ *
+ * @param insn {@code non-null;} instruction in question
+ * @return {@code non-null;} the instruction's complete register list
+ */
+ private static RegisterSpecList getRegs(Insn insn) {
+ return getRegs(insn, insn.getResult());
+ }
+
+ /**
+ * Gets the complete register list (result and sources) out of a
+ * given rop instruction. For insns that are commutative, have
+ * two register sources, and have a source equal to the result,
+ * place that source first.
+ *
+ * @param insn {@code non-null;} instruction in question
+ * @param resultReg {@code null-ok;} the real result to use (ignore the insn's)
+ * @return {@code non-null;} the instruction's complete register list
+ */
+ private static RegisterSpecList getRegs(Insn insn,
+ RegisterSpec resultReg) {
+ RegisterSpecList regs = insn.getSources();
+
+ if (insn.getOpcode().isCommutative()
+ && (regs.size() == 2)
+ && (resultReg.getReg() == regs.get(1).getReg())) {
+
+ /*
+ * For commutative ops which have two register sources,
+ * if the second source is the same register as the result,
+ * swap the sources so that an opcode of form 12x can be selected
+ * instead of one of form 23x
+ */
+
+ regs = RegisterSpecList.make(regs.get(1), regs.get(0));
+ }
+
+ if (resultReg == null) {
+ return regs;
+ }
+
+ return regs.withFirst(resultReg);
+ }
+
+ /**
+ * Instruction visitor class for doing the instruction translation per se.
+ */
+ private class TranslationVisitor implements Insn.Visitor {
+ /** {@code non-null;} list of output instructions in-progress */
+ private final OutputCollector output;
+
+ /** {@code non-null;} basic block being worked on */
+ private BasicBlock block;
+
+ /**
+ * {@code null-ok;} code address for the salient last instruction of the
+ * block (used before switches and throwing instructions)
+ */
+ private CodeAddress lastAddress;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param output {@code non-null;} destination for instruction output
+ */
+ public TranslationVisitor(OutputCollector output) {
+ this.output = output;
+ }
+
+ /**
+ * Sets the block currently being worked on.
+ *
+ * @param block {@code non-null;} the block
+ * @param lastAddress {@code non-null;} code address for the salient
+ * last instruction of the block
+ */
+ public void setBlock(BasicBlock block, CodeAddress lastAddress) {
+ this.block = block;
+ this.lastAddress = lastAddress;
+ }
+
+ /** {@inheritDoc} */
+ public void visitPlainInsn(PlainInsn insn) {
+ Rop rop = insn.getOpcode();
+ if (rop.getOpcode() == RegOps.MARK_LOCAL) {
+ /*
+ * Ignore these. They're dealt with by
+ * the LocalVariableAwareTranslationVisitor
+ */
+ return;
+ }
+ if (rop.getOpcode() == RegOps.MOVE_RESULT_PSEUDO) {
+ // These get skipped
+ return;
+ }
+
+ SourcePosition pos = insn.getPosition();
+ Dop opcode = RopToDop.dopFor(insn);
+ DalvInsn di;
+
+ switch (rop.getBranchingness()) {
+ case Rop.BRANCH_NONE:
+ case Rop.BRANCH_RETURN:
+ case Rop.BRANCH_THROW: {
+ di = new SimpleInsn(opcode, pos, getRegs(insn));
+ break;
+ }
+ case Rop.BRANCH_GOTO: {
+ /*
+ * Code in the main translation loop will emit a
+ * goto if necessary (if the branch isn't to the
+ * immediately subsequent block).
+ */
+ return;
+ }
+ case Rop.BRANCH_IF: {
+ int target = block.getSuccessors().get(1);
+ di = new TargetInsn(opcode, pos, getRegs(insn),
+ addresses.getStart(target));
+ break;
+ }
+ default: {
+ throw new RuntimeException("shouldn't happen");
+ }
+ }
+
+ addOutput(di);
+ }
+
+ /** {@inheritDoc} */
+ public void visitPlainCstInsn(PlainCstInsn insn) {
+ SourcePosition pos = insn.getPosition();
+ Dop opcode = RopToDop.dopFor(insn);
+ Rop rop = insn.getOpcode();
+ int ropOpcode = rop.getOpcode();
+ DalvInsn di;
+
+ if (rop.getBranchingness() != Rop.BRANCH_NONE) {
+ throw new RuntimeException("shouldn't happen");
+ }
+
+ if (ropOpcode == RegOps.MOVE_PARAM) {
+ if (!paramsAreInOrder) {
+ /*
+ * Parameters are not in order at the top of the reg space.
+ * We need to add moves.
+ */
+
+ RegisterSpec dest = insn.getResult();
+ int param =
+ ((CstInteger) insn.getConstant()).getValue();
+ RegisterSpec source =
+ RegisterSpec.make(regCount - paramSize + param,
+ dest.getType());
+ di = new SimpleInsn(opcode, pos,
+ RegisterSpecList.make(dest, source));
+ addOutput(di);
+ }
+ } else {
+ // No moves required for the parameters
+ RegisterSpecList regs = getRegs(insn);
+ di = new CstInsn(opcode, pos, regs, insn.getConstant());
+ addOutput(di);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void visitSwitchInsn(SwitchInsn insn) {
+ SourcePosition pos = insn.getPosition();
+ IntList cases = insn.getCases();
+ IntList successors = block.getSuccessors();
+ int casesSz = cases.size();
+ int succSz = successors.size();
+ int primarySuccessor = block.getPrimarySuccessor();
+
+ /*
+ * Check the assumptions that the number of cases is one
+ * less than the number of successors and that the last
+ * successor in the list is the primary (in this case, the
+ * default). This test is here to guard against forgetting
+ * to change this code if the way switch instructions are
+ * constructed also gets changed.
+ */
+ if ((casesSz != (succSz - 1)) ||
+ (primarySuccessor != successors.get(casesSz))) {
+ throw new RuntimeException("shouldn't happen");
+ }
+
+ CodeAddress[] switchTargets = new CodeAddress[casesSz];
+
+ for (int i = 0; i < casesSz; i++) {
+ int label = successors.get(i);
+ switchTargets[i] = addresses.getStart(label);
+ }
+
+ CodeAddress dataAddress = new CodeAddress(pos);
+ SwitchData dataInsn =
+ new SwitchData(pos, lastAddress, cases, switchTargets);
+ Dop opcode = dataInsn.isPacked() ?
+ Dops.PACKED_SWITCH : Dops.SPARSE_SWITCH;
+ TargetInsn switchInsn =
+ new TargetInsn(opcode, pos, getRegs(insn), dataAddress);
+
+ addOutput(lastAddress);
+ addOutput(switchInsn);
+
+ addOutputSuffix(new OddSpacer(pos));
+ addOutputSuffix(dataAddress);
+ addOutputSuffix(dataInsn);
+ }
+
+ /**
+ * Looks forward to the current block's primary successor, returning
+ * the RegisterSpec of the result of the move-result-pseudo at the
+ * top of that block or null if none.
+ *
+ * @return {@code null-ok;} result of move-result-pseudo at the beginning of
+ * primary successor
+ */
+ private RegisterSpec getNextMoveResultPseudo()
+ {
+ int label = block.getPrimarySuccessor();
+
+ if (label < 0) {
+ return null;
+ }
+
+ Insn insn
+ = method.getBlocks().labelToBlock(label).getInsns().get(0);
+
+ if (insn.getOpcode().getOpcode() != RegOps.MOVE_RESULT_PSEUDO) {
+ return null;
+ } else {
+ return insn.getResult();
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void visitThrowingCstInsn(ThrowingCstInsn insn) {
+ SourcePosition pos = insn.getPosition();
+ Dop opcode = RopToDop.dopFor(insn);
+ Rop rop = insn.getOpcode();
+ Constant cst = insn.getConstant();
+
+ if (rop.getBranchingness() != Rop.BRANCH_THROW) {
+ throw new RuntimeException("shouldn't happen");
+ }
+
+ addOutput(lastAddress);
+
+ if (rop.isCallLike()) {
+ RegisterSpecList regs = insn.getSources();
+ DalvInsn di = new CstInsn(opcode, pos, regs, cst);
+
+ addOutput(di);
+ } else {
+ RegisterSpec realResult = getNextMoveResultPseudo();
+
+ RegisterSpecList regs = getRegs(insn, realResult);
+ DalvInsn di;
+
+ boolean hasResult = opcode.hasResult()
+ || (rop.getOpcode() == RegOps.CHECK_CAST);
+
+ if (hasResult != (realResult != null)) {
+ throw new RuntimeException(
+ "Insn with result/move-result-pseudo mismatch " +
+ insn);
+ }
+
+ if ((rop.getOpcode() == RegOps.NEW_ARRAY) &&
+ (opcode.getOpcode() != DalvOps.NEW_ARRAY)) {
+ /*
+ * It's a type-specific new-array-<primitive>, and
+ * so it should be turned into a SimpleInsn (no
+ * constant ref as it's implicit).
+ */
+ di = new SimpleInsn(opcode, pos, regs);
+ } else {
+ /*
+ * This is the general case for constant-bearing
+ * instructions.
+ */
+ di = new CstInsn(opcode, pos, regs, cst);
+ }
+
+ addOutput(di);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void visitThrowingInsn(ThrowingInsn insn) {
+ SourcePosition pos = insn.getPosition();
+ Dop opcode = RopToDop.dopFor(insn);
+ Rop rop = insn.getOpcode();
+ RegisterSpec realResult;
+
+ if (rop.getBranchingness() != Rop.BRANCH_THROW) {
+ throw new RuntimeException("shouldn't happen");
+ }
+
+ realResult = getNextMoveResultPseudo();
+
+ if (opcode.hasResult() != (realResult != null)) {
+ throw new RuntimeException(
+ "Insn with result/move-result-pseudo mismatch" + insn);
+ }
+
+ addOutput(lastAddress);
+
+ DalvInsn di = new SimpleInsn(opcode, pos,
+ getRegs(insn, realResult));
+
+ addOutput(di);
+ }
+
+ /** {@inheritDoc} */
+ public void visitFillArrayDataInsn(FillArrayDataInsn insn) {
+ SourcePosition pos = insn.getPosition();
+ Constant cst = insn.getConstant();
+ ArrayList<Constant> values = insn.getInitValues();
+ Rop rop = insn.getOpcode();
+
+ if (rop.getBranchingness() != Rop.BRANCH_NONE) {
+ throw new RuntimeException("shouldn't happen");
+ }
+ CodeAddress dataAddress = new CodeAddress(pos);
+ ArrayData dataInsn =
+ new ArrayData(pos, lastAddress, values, cst);
+
+ TargetInsn fillArrayDataInsn =
+ new TargetInsn(Dops.FILL_ARRAY_DATA, pos, getRegs(insn),
+ dataAddress);
+
+ addOutput(lastAddress);
+ addOutput(fillArrayDataInsn);
+
+ addOutputSuffix(new OddSpacer(pos));
+ addOutputSuffix(dataAddress);
+ addOutputSuffix(dataInsn);
+ }
+
+ /**
+ * Adds to the output.
+ *
+ * @param insn {@code non-null;} instruction to add
+ */
+ protected void addOutput(DalvInsn insn) {
+ output.add(insn);
+ }
+
+ /**
+ * Adds to the output suffix.
+ *
+ * @param insn {@code non-null;} instruction to add
+ */
+ protected void addOutputSuffix(DalvInsn insn) {
+ output.addSuffix(insn);
+ }
+ }
+
+ /**
+ * Instruction visitor class for doing instruction translation with
+ * local variable tracking
+ */
+ private class LocalVariableAwareTranslationVisitor
+ extends TranslationVisitor {
+ /** {@code non-null;} local variable info */
+ private LocalVariableInfo locals;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param output {@code non-null;} destination for instruction output
+ * @param locals {@code non-null;} the local variable info
+ */
+ public LocalVariableAwareTranslationVisitor(OutputCollector output,
+ LocalVariableInfo locals) {
+ super(output);
+ this.locals = locals;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void visitPlainInsn(PlainInsn insn) {
+ super.visitPlainInsn(insn);
+ addIntroductionIfNecessary(insn);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void visitPlainCstInsn(PlainCstInsn insn) {
+ super.visitPlainCstInsn(insn);
+ addIntroductionIfNecessary(insn);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void visitSwitchInsn(SwitchInsn insn) {
+ super.visitSwitchInsn(insn);
+ addIntroductionIfNecessary(insn);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void visitThrowingCstInsn(ThrowingCstInsn insn) {
+ super.visitThrowingCstInsn(insn);
+ addIntroductionIfNecessary(insn);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void visitThrowingInsn(ThrowingInsn insn) {
+ super.visitThrowingInsn(insn);
+ addIntroductionIfNecessary(insn);
+ }
+
+ /**
+ * Adds a {@link LocalStart} to the output if the given
+ * instruction in fact introduces a local variable.
+ *
+ * @param insn {@code non-null;} instruction in question
+ */
+ public void addIntroductionIfNecessary(Insn insn) {
+ RegisterSpec spec = locals.getAssignment(insn);
+
+ if (spec != null) {
+ addOutput(new LocalStart(insn.getPosition(), spec));
+ }
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/SimpleInsn.java b/dx/src/com/android/dx/dex/code/SimpleInsn.java
new file mode 100644
index 0000000..8cdcc55
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/SimpleInsn.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+
+/**
+ * Instruction which has no extra info beyond the basics provided for in
+ * the base class.
+ */
+public final class SimpleInsn extends FixedSizeInsn {
+ /**
+ * Constructs an instance. The output address of this instance is initially
+ * unknown ({@code -1}).
+ *
+ * @param opcode the opcode; one of the constants from {@link Dops}
+ * @param position {@code non-null;} source position
+ * @param registers {@code non-null;} register list, including a
+ * result register if appropriate (that is, registers may be either
+ * ins or outs)
+ */
+ public SimpleInsn(Dop opcode, SourcePosition position,
+ RegisterSpecList registers) {
+ super(opcode, position, registers);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public DalvInsn withOpcode(Dop opcode) {
+ return new SimpleInsn(opcode, getPosition(), getRegisters());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public DalvInsn withRegisters(RegisterSpecList registers) {
+ return new SimpleInsn(getOpcode(), getPosition(), registers);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected String argString() {
+ return null;
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/StdCatchBuilder.java b/dx/src/com/android/dx/dex/code/StdCatchBuilder.java
new file mode 100644
index 0000000..1e7612e
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/StdCatchBuilder.java
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.BasicBlock;
+import com.android.dx.rop.code.BasicBlockList;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.IntList;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+
+/**
+ * Constructor of {@link CatchTable} instances from {@link RopMethod}
+ * and associated data.
+ */
+public final class StdCatchBuilder implements CatchBuilder {
+ /** the maximum range of a single catch handler, in code units */
+ private static final int MAX_CATCH_RANGE = 65535;
+
+ /** {@code non-null;} method to build the list for */
+ private final RopMethod method;
+
+ /** {@code non-null;} block output order */
+ private final int[] order;
+
+ /** {@code non-null;} address objects for each block */
+ private final BlockAddresses addresses;
+
+ /**
+ * Constructs an instance. It merely holds onto its parameters for
+ * a subsequent call to {@link #build}.
+ *
+ * @param method {@code non-null;} method to build the list for
+ * @param order {@code non-null;} block output order
+ * @param addresses {@code non-null;} address objects for each block
+ */
+ public StdCatchBuilder(RopMethod method, int[] order,
+ BlockAddresses addresses) {
+ if (method == null) {
+ throw new NullPointerException("method == null");
+ }
+
+ if (order == null) {
+ throw new NullPointerException("order == null");
+ }
+
+ if (addresses == null) {
+ throw new NullPointerException("addresses == null");
+ }
+
+ this.method = method;
+ this.order = order;
+ this.addresses = addresses;
+ }
+
+ /** {@inheritDoc} */
+ public CatchTable build() {
+ return build(method, order, addresses);
+ }
+
+ /** {@inheritDoc} */
+ public boolean hasAnyCatches() {
+ BasicBlockList blocks = method.getBlocks();
+ int size = blocks.size();
+
+ for (int i = 0; i < size; i++) {
+ BasicBlock block = blocks.get(i);
+ TypeList catches = block.getLastInsn().getCatches();
+ if (catches.size() != 0) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public HashSet<Type> getCatchTypes() {
+ HashSet<Type> result = new HashSet<Type>(20);
+ BasicBlockList blocks = method.getBlocks();
+ int size = blocks.size();
+
+ for (int i = 0; i < size; i++) {
+ BasicBlock block = blocks.get(i);
+ TypeList catches = block.getLastInsn().getCatches();
+ int catchSize = catches.size();
+
+ for (int j = 0; j < catchSize; j++) {
+ result.add(catches.getType(j));
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Builds and returns the catch table for a given method.
+ *
+ * @param method {@code non-null;} method to build the list for
+ * @param order {@code non-null;} block output order
+ * @param addresses {@code non-null;} address objects for each block
+ * @return {@code non-null;} the constructed table
+ */
+ public static CatchTable build(RopMethod method, int[] order,
+ BlockAddresses addresses) {
+ int len = order.length;
+ BasicBlockList blocks = method.getBlocks();
+ ArrayList<CatchTable.Entry> resultList =
+ new ArrayList<CatchTable.Entry>(len);
+ CatchHandlerList currentHandlers = CatchHandlerList.EMPTY;
+ BasicBlock currentStartBlock = null;
+ BasicBlock currentEndBlock = null;
+
+ for (int i = 0; i < len; i++) {
+ BasicBlock block = blocks.labelToBlock(order[i]);
+
+ if (!block.canThrow()) {
+ /*
+ * There is no need to concern ourselves with the
+ * placement of blocks that can't throw with respect
+ * to the blocks that *can* throw.
+ */
+ continue;
+ }
+
+ CatchHandlerList handlers = handlersFor(block, addresses);
+
+ if (currentHandlers.size() == 0) {
+ // This is the start of a new catch range.
+ currentStartBlock = block;
+ currentEndBlock = block;
+ currentHandlers = handlers;
+ continue;
+ }
+
+ if (currentHandlers.equals(handlers)
+ && rangeIsValid(currentStartBlock, block, addresses)) {
+ /*
+ * The block we are looking at now has the same handlers
+ * as the block that started the currently open catch
+ * range, and adding it to the currently open range won't
+ * cause it to be too long.
+ */
+ currentEndBlock = block;
+ continue;
+ }
+
+ /*
+ * The block we are looking at now has incompatible handlers,
+ * so we need to finish off the last entry and start a new
+ * one. Note: We only emit an entry if it has associated handlers.
+ */
+ if (currentHandlers.size() != 0) {
+ CatchTable.Entry entry =
+ makeEntry(currentStartBlock, currentEndBlock,
+ currentHandlers, addresses);
+ resultList.add(entry);
+ }
+
+ currentStartBlock = block;
+ currentEndBlock = block;
+ currentHandlers = handlers;
+ }
+
+ if (currentHandlers.size() != 0) {
+ // Emit an entry for the range that was left hanging.
+ CatchTable.Entry entry =
+ makeEntry(currentStartBlock, currentEndBlock,
+ currentHandlers, addresses);
+ resultList.add(entry);
+ }
+
+ // Construct the final result.
+
+ int resultSz = resultList.size();
+
+ if (resultSz == 0) {
+ return CatchTable.EMPTY;
+ }
+
+ CatchTable result = new CatchTable(resultSz);
+
+ for (int i = 0; i < resultSz; i++) {
+ result.set(i, resultList.get(i));
+ }
+
+ result.setImmutable();
+ return result;
+ }
+
+ /**
+ * Makes the {@link CatchHandlerList} for the given basic block.
+ *
+ * @param block {@code non-null;} block to get entries for
+ * @param addresses {@code non-null;} address objects for each block
+ * @return {@code non-null;} array of entries
+ */
+ private static CatchHandlerList handlersFor(BasicBlock block,
+ BlockAddresses addresses) {
+ IntList successors = block.getSuccessors();
+ int succSize = successors.size();
+ int primary = block.getPrimarySuccessor();
+ TypeList catches = block.getLastInsn().getCatches();
+ int catchSize = catches.size();
+
+ if (catchSize == 0) {
+ return CatchHandlerList.EMPTY;
+ }
+
+ if (((primary == -1) && (succSize != catchSize))
+ || ((primary != -1) &&
+ ((succSize != (catchSize + 1))
+ || (primary != successors.get(catchSize))))) {
+ /*
+ * Blocks that throw are supposed to list their primary
+ * successor -- if any -- last in the successors list, but
+ * that constraint appears to be violated here.
+ */
+ throw new RuntimeException(
+ "shouldn't happen: weird successors list");
+ }
+
+ /*
+ * Reduce the effective catchSize if we spot a catch-all that
+ * isn't at the end.
+ */
+ for (int i = 0; i < catchSize; i++) {
+ Type type = catches.getType(i);
+ if (type.equals(Type.OBJECT)) {
+ catchSize = i + 1;
+ break;
+ }
+ }
+
+ CatchHandlerList result = new CatchHandlerList(catchSize);
+
+ for (int i = 0; i < catchSize; i++) {
+ CstType oneType = new CstType(catches.getType(i));
+ CodeAddress oneHandler = addresses.getStart(successors.get(i));
+ result.set(i, oneType, oneHandler.getAddress());
+ }
+
+ result.setImmutable();
+ return result;
+ }
+
+ /**
+ * Makes a {@link CatchTable#Entry} for the given block range and
+ * handlers.
+ *
+ * @param start {@code non-null;} the start block for the range (inclusive)
+ * @param end {@code non-null;} the start block for the range (also inclusive)
+ * @param handlers {@code non-null;} the handlers for the range
+ * @param addresses {@code non-null;} address objects for each block
+ */
+ private static CatchTable.Entry makeEntry(BasicBlock start,
+ BasicBlock end, CatchHandlerList handlers,
+ BlockAddresses addresses) {
+ /*
+ * We start at the *last* instruction of the start block, since
+ * that's the instruction that can throw...
+ */
+ CodeAddress startAddress = addresses.getLast(start);
+
+ // ...And we end *after* the last instruction of the end block.
+ CodeAddress endAddress = addresses.getEnd(end);
+
+ return new CatchTable.Entry(startAddress.getAddress(),
+ endAddress.getAddress(), handlers);
+ }
+
+ /**
+ * Gets whether the address range for the given two blocks is valid
+ * for a catch handler. This is true as long as the covered range is
+ * under 65536 code units.
+ *
+ * @param start {@code non-null;} the start block for the range (inclusive)
+ * @param end {@code non-null;} the start block for the range (also inclusive)
+ * @param addresses {@code non-null;} address objects for each block
+ * @return {@code true} if the range is valid as a catch range
+ */
+ private static boolean rangeIsValid(BasicBlock start, BasicBlock end,
+ BlockAddresses addresses) {
+ if (start == null) {
+ throw new NullPointerException("start == null");
+ }
+
+ if (end == null) {
+ throw new NullPointerException("end == null");
+ }
+
+ // See above about selection of instructions.
+ int startAddress = addresses.getLast(start).getAddress();
+ int endAddress = addresses.getEnd(end).getAddress();
+
+ return (endAddress - startAddress) <= MAX_CATCH_RANGE;
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/SwitchData.java b/dx/src/com/android/dx/dex/code/SwitchData.java
new file mode 100644
index 0000000..27a6342
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/SwitchData.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IntList;
+
+/**
+ * Pseudo-instruction which holds switch data. The switch data is
+ * a map of values to target addresses, and this class writes the data
+ * in either a "packed" or "sparse" form.
+ */
+public final class SwitchData extends VariableSizeInsn {
+ /**
+ * {@code non-null;} address representing the instruction that uses this
+ * instance
+ */
+ private final CodeAddress user;
+
+ /** {@code non-null;} sorted list of switch cases (keys) */
+ private final IntList cases;
+
+ /**
+ * {@code non-null;} corresponding list of code addresses; the branch
+ * target for each case
+ */
+ private final CodeAddress[] targets;
+
+ /** whether the output table will be packed (vs. sparse) */
+ private final boolean packed;
+
+ /**
+ * Constructs an instance. The output address of this instance is initially
+ * unknown ({@code -1}).
+ *
+ * @param position {@code non-null;} source position
+ * @param user {@code non-null;} address representing the instruction that
+ * uses this instance
+ * @param cases {@code non-null;} sorted list of switch cases (keys)
+ * @param targets {@code non-null;} corresponding list of code addresses; the
+ * branch target for each case
+ */
+ public SwitchData(SourcePosition position, CodeAddress user,
+ IntList cases, CodeAddress[] targets) {
+ super(position, RegisterSpecList.EMPTY);
+
+ if (user == null) {
+ throw new NullPointerException("user == null");
+ }
+
+ if (cases == null) {
+ throw new NullPointerException("cases == null");
+ }
+
+ if (targets == null) {
+ throw new NullPointerException("targets == null");
+ }
+
+ int sz = cases.size();
+
+ if (sz != targets.length) {
+ throw new IllegalArgumentException("cases / targets mismatch");
+ }
+
+ if (sz > 65535) {
+ throw new IllegalArgumentException("too many cases");
+ }
+
+ this.user = user;
+ this.cases = cases;
+ this.targets = targets;
+ this.packed = shouldPack(cases);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int codeSize() {
+ return packed ? (int) packedCodeSize(cases) :
+ (int) sparseCodeSize(cases);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(AnnotatedOutput out) {
+ int baseAddress = user.getAddress();
+ int defaultTarget = Dops.PACKED_SWITCH.getFormat().codeSize();
+ int sz = targets.length;
+
+ if (packed) {
+ int firstCase = (sz == 0) ? 0 : cases.get(0);
+ int lastCase = (sz == 0) ? 0 : cases.get(sz - 1);
+ int outSz = lastCase - firstCase + 1;
+
+ out.writeShort(0x100 | DalvOps.NOP);
+ out.writeShort(outSz);
+ out.writeInt(firstCase);
+
+ int caseAt = 0;
+ for (int i = 0; i < outSz; i++) {
+ int outCase = firstCase + i;
+ int oneCase = cases.get(caseAt);
+ int relTarget;
+
+ if (oneCase > outCase) {
+ relTarget = defaultTarget;
+ } else {
+ relTarget = targets[caseAt].getAddress() - baseAddress;
+ caseAt++;
+ }
+
+ out.writeInt(relTarget);
+ }
+ } else {
+ out.writeShort(0x200 | DalvOps.NOP);
+ out.writeShort(sz);
+
+ for (int i = 0; i < sz; i++) {
+ out.writeInt(cases.get(i));
+ }
+
+ for (int i = 0; i < sz; i++) {
+ int relTarget = targets[i].getAddress() - baseAddress;
+ out.writeInt(relTarget);
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public DalvInsn withRegisters(RegisterSpecList registers) {
+ return new SwitchData(getPosition(), user, cases, targets);
+ }
+
+ /**
+ * Returns whether or not this instance's data will be output as packed.
+ *
+ * @return {@code true} iff the data is to be packed
+ */
+ public boolean isPacked() {
+ return packed;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected String argString() {
+ StringBuffer sb = new StringBuffer(100);
+
+ int sz = targets.length;
+ for (int i = 0; i < sz; i++) {
+ sb.append("\n ");
+ sb.append(cases.get(i));
+ sb.append(": ");
+ sb.append(targets[i]);
+ }
+
+ return sb.toString();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected String listingString0(boolean noteIndices) {
+ int baseAddress = user.getAddress();
+ StringBuffer sb = new StringBuffer(100);
+ int sz = targets.length;
+
+ sb.append(packed ? "packed" : "sparse");
+ sb.append("-switch-data // for switch @ ");
+ sb.append(Hex.u2(baseAddress));
+
+ for (int i = 0; i < sz; i++) {
+ int absTarget = targets[i].getAddress();
+ int relTarget = absTarget - baseAddress;
+ sb.append("\n ");
+ sb.append(cases.get(i));
+ sb.append(": ");
+ sb.append(Hex.u4(absTarget));
+ sb.append(" // ");
+ sb.append(Hex.s4(relTarget));
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Gets the size of a packed table for the given cases, in 16-bit code
+ * units.
+ *
+ * @param cases {@code non-null;} sorted list of cases
+ * @return {@code >= -1;} the packed table size or {@code -1} if the
+ * cases couldn't possibly be represented as a packed table
+ */
+ private static long packedCodeSize(IntList cases) {
+ int sz = cases.size();
+ long low = cases.get(0);
+ long high = cases.get(sz - 1);
+ long result = ((high - low + 1)) * 2 + 4;
+
+ return (result <= 0x7fffffff) ? result : -1;
+ }
+
+ /**
+ * Gets the size of a sparse table for the given cases, in 16-bit code
+ * units.
+ *
+ * @param cases {@code non-null;} sorted list of cases
+ * @return {@code > 0;} the sparse table size
+ */
+ private static long sparseCodeSize(IntList cases) {
+ int sz = cases.size();
+
+ return (sz * 4L) + 2;
+ }
+
+ /**
+ * Determines whether the given list of cases warrant being packed.
+ *
+ * @param cases {@code non-null;} sorted list of cases
+ * @return {@code true} iff the table encoding the cases
+ * should be packed
+ */
+ private static boolean shouldPack(IntList cases) {
+ int sz = cases.size();
+
+ if (sz < 2) {
+ return true;
+ }
+
+ long packedSize = packedCodeSize(cases);
+ long sparseSize = sparseCodeSize(cases);
+
+ /*
+ * We pick the packed representation if it is possible and
+ * would be as small or smaller than 5/4 of the sparse
+ * representation. That is, we accept some size overhead on
+ * the packed representation, since that format is faster to
+ * execute at runtime.
+ */
+ return (packedSize >= 0) && (packedSize <= ((sparseSize * 5) / 4));
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/TargetInsn.java b/dx/src/com/android/dx/dex/code/TargetInsn.java
new file mode 100644
index 0000000..cbb5ff9
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/TargetInsn.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+
+/**
+ * Instruction which has a single branch target.
+ */
+public final class TargetInsn extends FixedSizeInsn {
+ /** {@code non-null;} the branch target */
+ private CodeAddress target;
+
+ /**
+ * Constructs an instance. The output address of this instance is initially
+ * unknown ({@code -1}), and the target is initially
+ * {@code null}.
+ *
+ * @param opcode the opcode; one of the constants from {@link Dops}
+ * @param position {@code non-null;} source position
+ * @param registers {@code non-null;} register list, including a
+ * result register if appropriate (that is, registers may be either
+ * ins or outs)
+ * @param target {@code non-null;} the branch target
+ */
+ public TargetInsn(Dop opcode, SourcePosition position,
+ RegisterSpecList registers, CodeAddress target) {
+ super(opcode, position, registers);
+
+ if (target == null) {
+ throw new NullPointerException("target == null");
+ }
+
+ this.target = target;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public DalvInsn withOpcode(Dop opcode) {
+ return new TargetInsn(opcode, getPosition(), getRegisters(), target);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public DalvInsn withRegisters(RegisterSpecList registers) {
+ return new TargetInsn(getOpcode(), getPosition(), registers, target);
+ }
+
+ /**
+ * Returns an instance that is just like this one, except that its
+ * opcode has the opposite sense (as a test; e.g. a
+ * {@code lt} test becomes a {@code ge}), and its branch
+ * target is replaced by the one given, and all set-once values
+ * associated with the class (such as its address) are reset.
+ *
+ * @param target {@code non-null;} the new branch target
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public TargetInsn withNewTargetAndReversed(CodeAddress target) {
+ Dop opcode = getOpcode().getOppositeTest();
+
+ return new TargetInsn(opcode, getPosition(), getRegisters(), target);
+ }
+
+ /**
+ * Gets the unique branch target of this instruction.
+ *
+ * @return {@code non-null;} the branch target
+ */
+ public CodeAddress getTarget() {
+ return target;
+ }
+
+ /**
+ * Gets the target address of this instruction. This is only valid
+ * to call if the target instruction has been assigned an address,
+ * and it is merely a convenient shorthand for
+ * {@code getTarget().getAddress()}.
+ *
+ * @return {@code >= 0;} the target address
+ */
+ public int getTargetAddress() {
+ return target.getAddress();
+ }
+
+ /**
+ * Gets the branch offset of this instruction. This is only valid to
+ * call if both this and the target instruction each has been assigned
+ * an address, and it is merely a convenient shorthand for
+ * {@code getTargetAddress() - getAddress()}.
+ *
+ * @return the branch offset
+ */
+ public int getTargetOffset() {
+ return target.getAddress() - getAddress();
+ }
+
+ /**
+ * Returns whether the target offset is known.
+ *
+ * @return {@code true} if the target offset is known or
+ * {@code false} if not
+ */
+ public boolean hasTargetOffset() {
+ return hasAddress() && target.hasAddress();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected String argString() {
+ if (target == null) {
+ return "????";
+ }
+
+ return target.identifierString();
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/VariableSizeInsn.java b/dx/src/com/android/dx/dex/code/VariableSizeInsn.java
new file mode 100644
index 0000000..06b40f7
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/VariableSizeInsn.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+
+/**
+ * Pseudo-instruction base class for variable-sized instructions.
+ */
+public abstract class VariableSizeInsn extends DalvInsn {
+ /**
+ * Constructs an instance. The output address of this instance is initially
+ * unknown ({@code -1}).
+ *
+ * @param position {@code non-null;} source position
+ * @param registers {@code non-null;} source registers
+ */
+ public VariableSizeInsn(SourcePosition position,
+ RegisterSpecList registers) {
+ super(Dops.SPECIAL_FORMAT, position, registers);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final DalvInsn withOpcode(Dop opcode) {
+ throw new RuntimeException("unsupported");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final DalvInsn withRegisterOffset(int delta) {
+ return withRegisters(getRegisters().withOffset(delta));
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/ZeroSizeInsn.java b/dx/src/com/android/dx/dex/code/ZeroSizeInsn.java
new file mode 100644
index 0000000..2cc157b
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/ZeroSizeInsn.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code;
+
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Pseudo-instruction base class for zero-size (no code emitted)
+ * instructions, which are generally used for tracking metainformation
+ * about the code they are adjacent to.
+ */
+public abstract class ZeroSizeInsn extends DalvInsn {
+ /**
+ * Constructs an instance. The output address of this instance is initially
+ * unknown ({@code -1}).
+ *
+ * @param position {@code non-null;} source position
+ */
+ public ZeroSizeInsn(SourcePosition position) {
+ super(Dops.SPECIAL_FORMAT, position, RegisterSpecList.EMPTY);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final int codeSize() {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final void writeTo(AnnotatedOutput out) {
+ // Nothing to do here, for this class.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final DalvInsn withOpcode(Dop opcode) {
+ throw new RuntimeException("unsupported");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public DalvInsn withRegisterOffset(int delta) {
+ return withRegisters(getRegisters().withOffset(delta));
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form10t.java b/dx/src/com/android/dx/dex/code/form/Form10t.java
new file mode 100644
index 0000000..82b731d
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form10t.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.dex.code.TargetInsn;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 10t}. See the instruction format spec
+ * for details.
+ */
+public final class Form10t extends InsnFormat {
+ /** {@code non-null;} unique instance of this class */
+ public static final InsnFormat THE_ONE = new Form10t();
+
+ /**
+ * Constructs an instance. This class is not publicly
+ * instantiable. Use {@link #THE_ONE}.
+ */
+ private Form10t() {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnArgString(DalvInsn insn) {
+ return branchString(insn);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+ return branchComment(insn);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int codeSize() {
+ return 1;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCompatible(DalvInsn insn) {
+ if (!((insn instanceof TargetInsn) &&
+ (insn.getRegisters().size() == 0))) {
+ return false;
+ }
+
+ TargetInsn ti = (TargetInsn) insn;
+ return ti.hasTargetOffset() ? branchFits(ti) : true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean branchFits(TargetInsn insn) {
+ int offset = insn.getTargetOffset();
+
+ // Note: A zero offset would fit, but it is prohibited by the spec.
+ return (offset != 0) && signedFitsInByte(offset);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public InsnFormat nextUp() {
+ return Form20t.THE_ONE;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+ int offset = ((TargetInsn) insn).getTargetOffset();
+
+ write(out, opcodeUnit(insn, (offset & 0xff)));
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form10x.java b/dx/src/com/android/dx/dex/code/form/Form10x.java
new file mode 100644
index 0000000..c7a22a6
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form10x.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.dex.code.SimpleInsn;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 10x}. See the instruction format spec
+ * for details.
+ */
+public final class Form10x extends InsnFormat {
+ /** {@code non-null;} unique instance of this class */
+ public static final InsnFormat THE_ONE = new Form10x();
+
+ /**
+ * Constructs an instance. This class is not publicly
+ * instantiable. Use {@link #THE_ONE}.
+ */
+ private Form10x() {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnArgString(DalvInsn insn) {
+ // This format has no arguments.
+ return "";
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+ // This format has no comment.
+ return "";
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int codeSize() {
+ return 1;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCompatible(DalvInsn insn) {
+ return (insn instanceof SimpleInsn) &&
+ (insn.getRegisters().size() == 0);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public InsnFormat nextUp() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+ write(out, opcodeUnit(insn, 0));
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form11n.java b/dx/src/com/android/dx/dex/code/form/Form11n.java
new file mode 100644
index 0000000..d63fc6f
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form11n.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 11n}. See the instruction format spec
+ * for details.
+ */
+public final class Form11n extends InsnFormat {
+ /** {@code non-null;} unique instance of this class */
+ public static final InsnFormat THE_ONE = new Form11n();
+
+ /**
+ * Constructs an instance. This class is not publicly
+ * instantiable. Use {@link #THE_ONE}.
+ */
+ private Form11n() {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnArgString(DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+
+ return regs.get(0).regString() + ", " + literalBitsString(value);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+ CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+ return literalBitsComment(value, 4);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int codeSize() {
+ return 1;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCompatible(DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+
+ if (!((insn instanceof CstInsn) &&
+ (regs.size() == 1) &&
+ unsignedFitsInNibble(regs.get(0).getReg()))) {
+ return false;
+ }
+
+ CstInsn ci = (CstInsn) insn;
+ Constant cst = ci.getConstant();
+
+ if (!(cst instanceof CstLiteralBits)) {
+ return false;
+ }
+
+ CstLiteralBits cb = (CstLiteralBits) cst;
+
+ return cb.fitsInInt() && signedFitsInNibble(cb.getIntBits());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public InsnFormat nextUp() {
+ return Form21s.THE_ONE;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ int value =
+ ((CstLiteralBits) ((CstInsn) insn).getConstant()).getIntBits();
+
+ write(out,
+ opcodeUnit(insn, makeByte(regs.get(0).getReg(), value & 0xf)));
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form11x.java b/dx/src/com/android/dx/dex/code/form/Form11x.java
new file mode 100644
index 0000000..b4acc1a
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form11x.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.dex.code.SimpleInsn;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 11x}. See the instruction format spec
+ * for details.
+ */
+public final class Form11x extends InsnFormat {
+ /** {@code non-null;} unique instance of this class */
+ public static final InsnFormat THE_ONE = new Form11x();
+
+ /**
+ * Constructs an instance. This class is not publicly
+ * instantiable. Use {@link #THE_ONE}.
+ */
+ private Form11x() {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnArgString(DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ return regs.get(0).regString();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+ // This format has no comment.
+ return "";
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int codeSize() {
+ return 1;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCompatible(DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ return (insn instanceof SimpleInsn) &&
+ (regs.size() == 1) &&
+ unsignedFitsInByte(regs.get(0).getReg());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public InsnFormat nextUp() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ write(out, opcodeUnit(insn, regs.get(0).getReg()));
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form12x.java b/dx/src/com/android/dx/dex/code/form/Form12x.java
new file mode 100644
index 0000000..c7754be
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form12x.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.HighRegisterPrefix;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.dex.code.SimpleInsn;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 12x}. See the instruction format spec
+ * for details.
+ */
+public final class Form12x extends InsnFormat {
+ /** {@code non-null;} unique instance of this class */
+ public static final InsnFormat THE_ONE = new Form12x();
+
+ /**
+ * Constructs an instance. This class is not publicly
+ * instantiable. Use {@link #THE_ONE}.
+ */
+ private Form12x() {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnArgString(DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ int sz = regs.size();
+
+ /*
+ * The (sz - 2) and (sz - 1) below makes this code work for
+ * both the two- and three-register ops. (See "case 3" in
+ * isCompatible(), below.)
+ */
+
+ return regs.get(sz - 2).regString() + ", " +
+ regs.get(sz - 1).regString();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+ // This format has no comment.
+ return "";
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int codeSize() {
+ return 1;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCompatible(DalvInsn insn) {
+ if (!(insn instanceof SimpleInsn)) {
+ return false;
+ }
+
+ RegisterSpecList regs = insn.getRegisters();
+ RegisterSpec rs1;
+ RegisterSpec rs2;
+
+ switch (regs.size()) {
+ case 2: {
+ rs1 = regs.get(0);
+ rs2 = regs.get(1);
+ break;
+ }
+ case 3: {
+ /*
+ * This format is allowed for ops that are effectively
+ * 3-arg but where the first two args are identical.
+ */
+ rs1 = regs.get(1);
+ rs2 = regs.get(2);
+ if (rs1.getReg() != regs.get(0).getReg()) {
+ return false;
+ }
+ break;
+ }
+ default: {
+ return false;
+ }
+ }
+
+ return unsignedFitsInNibble(rs1.getReg()) &&
+ unsignedFitsInNibble(rs2.getReg());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public InsnFormat nextUp() {
+ return Form22x.THE_ONE;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ int sz = regs.size();
+
+ /*
+ * The (sz - 2) and (sz - 1) below makes this code work for
+ * both the two- and three-register ops. (See "case 3" in
+ * isCompatible(), above.)
+ */
+
+ write(out, opcodeUnit(insn,
+ makeByte(regs.get(sz - 2).getReg(),
+ regs.get(sz - 1).getReg())));
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form20t.java b/dx/src/com/android/dx/dex/code/form/Form20t.java
new file mode 100644
index 0000000..0b5a3b2
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form20t.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.dex.code.TargetInsn;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 20t}. See the instruction format spec
+ * for details.
+ */
+public final class Form20t extends InsnFormat {
+ /** {@code non-null;} unique instance of this class */
+ public static final InsnFormat THE_ONE = new Form20t();
+
+ /**
+ * Constructs an instance. This class is not publicly
+ * instantiable. Use {@link #THE_ONE}.
+ */
+ private Form20t() {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnArgString(DalvInsn insn) {
+ return branchString(insn);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+ return branchComment(insn);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int codeSize() {
+ return 2;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCompatible(DalvInsn insn) {
+ if (!((insn instanceof TargetInsn) &&
+ (insn.getRegisters().size() == 0))) {
+ return false;
+ }
+
+ TargetInsn ti = (TargetInsn) insn;
+ return ti.hasTargetOffset() ? branchFits(ti) : true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean branchFits(TargetInsn insn) {
+ int offset = insn.getTargetOffset();
+
+ // Note: A zero offset would fit, but it is prohibited by the spec.
+ return (offset != 0) && signedFitsInShort(offset);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public InsnFormat nextUp() {
+ return Form30t.THE_ONE;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+ int offset = ((TargetInsn) insn).getTargetOffset();
+
+ write(out, opcodeUnit(insn, 0), (short) offset);
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form21c.java b/dx/src/com/android/dx/dex/code/form/Form21c.java
new file mode 100644
index 0000000..ed1ec3c
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form21c.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 21c}. See the instruction format spec
+ * for details.
+ */
+public final class Form21c extends InsnFormat {
+ /** {@code non-null;} unique instance of this class */
+ public static final InsnFormat THE_ONE = new Form21c();
+
+ /**
+ * Constructs an instance. This class is not publicly
+ * instantiable. Use {@link #THE_ONE}.
+ */
+ private Form21c() {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnArgString(DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ return regs.get(0).regString() + ", " + cstString(insn);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+ if (noteIndices) {
+ return cstComment(insn);
+ } else {
+ return "";
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int codeSize() {
+ return 2;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCompatible(DalvInsn insn) {
+ if (!(insn instanceof CstInsn)) {
+ return false;
+ }
+
+ RegisterSpecList regs = insn.getRegisters();
+ RegisterSpec reg;
+
+ switch (regs.size()) {
+ case 1: {
+ reg = regs.get(0);
+ break;
+ }
+ case 2: {
+ /*
+ * This format is allowed for ops that are effectively
+ * 2-arg but where the two args are identical.
+ */
+ reg = regs.get(0);
+ if (reg.getReg() != regs.get(1).getReg()) {
+ return false;
+ }
+ break;
+ }
+ default: {
+ return false;
+ }
+ }
+
+ if (!unsignedFitsInByte(reg.getReg())) {
+ return false;
+ }
+
+ CstInsn ci = (CstInsn) insn;
+ int cpi = ci.getIndex();
+
+ if (! unsignedFitsInShort(cpi)) {
+ return false;
+ }
+
+ Constant cst = ci.getConstant();
+ return (cst instanceof CstType) ||
+ (cst instanceof CstFieldRef) ||
+ (cst instanceof CstString);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public InsnFormat nextUp() {
+ return Form31c.THE_ONE;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ int cpi = ((CstInsn) insn).getIndex();
+
+ write(out,
+ opcodeUnit(insn, regs.get(0).getReg()),
+ (short) cpi);
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form21h.java b/dx/src/com/android/dx/dex/code/form/Form21h.java
new file mode 100644
index 0000000..e0bd751
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form21h.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 21h}. See the instruction format spec
+ * for details.
+ */
+public final class Form21h extends InsnFormat {
+ /** {@code non-null;} unique instance of this class */
+ public static final InsnFormat THE_ONE = new Form21h();
+
+ /**
+ * Constructs an instance. This class is not publicly
+ * instantiable. Use {@link #THE_ONE}.
+ */
+ private Form21h() {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnArgString(DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+
+ return regs.get(0).regString() + ", " + literalBitsString(value);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+ RegisterSpecList regs = insn.getRegisters();
+ CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+
+ return
+ literalBitsComment(value,
+ (regs.get(0).getCategory() == 1) ? 32 : 64);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int codeSize() {
+ return 2;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCompatible(DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ if (!((insn instanceof CstInsn) &&
+ (regs.size() == 1) &&
+ unsignedFitsInByte(regs.get(0).getReg()))) {
+ return false;
+ }
+
+ CstInsn ci = (CstInsn) insn;
+ Constant cst = ci.getConstant();
+
+ if (!(cst instanceof CstLiteralBits)) {
+ return false;
+ }
+
+ CstLiteralBits cb = (CstLiteralBits) cst;
+
+ // Where the high bits are depends on the category of the target.
+ if (regs.get(0).getCategory() == 1) {
+ int bits = cb.getIntBits();
+ return ((bits & 0xffff) == 0);
+ } else {
+ long bits = cb.getLongBits();
+ return ((bits & 0xffffffffffffL) == 0);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public InsnFormat nextUp() {
+ return Form31i.THE_ONE;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ CstLiteralBits cb = (CstLiteralBits) ((CstInsn) insn).getConstant();
+ short bits;
+
+ // Where the high bits are depends on the category of the target.
+ if (regs.get(0).getCategory() == 1) {
+ bits = (short) (cb.getIntBits() >>> 16);
+ } else {
+ bits = (short) (cb.getLongBits() >>> 48);
+ }
+
+ write(out, opcodeUnit(insn, regs.get(0).getReg()), bits);
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form21s.java b/dx/src/com/android/dx/dex/code/form/Form21s.java
new file mode 100644
index 0000000..a03ee43
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form21s.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 21s}. See the instruction format spec
+ * for details.
+ */
+public final class Form21s extends InsnFormat {
+ /** {@code non-null;} unique instance of this class */
+ public static final InsnFormat THE_ONE = new Form21s();
+
+ /**
+ * Constructs an instance. This class is not publicly
+ * instantiable. Use {@link #THE_ONE}.
+ */
+ private Form21s() {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnArgString(DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+
+ return regs.get(0).regString() + ", " + literalBitsString(value);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+ CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+ return literalBitsComment(value, 16);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int codeSize() {
+ return 2;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCompatible(DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ if (!((insn instanceof CstInsn) &&
+ (regs.size() == 1) &&
+ unsignedFitsInByte(regs.get(0).getReg()))) {
+ return false;
+ }
+
+ CstInsn ci = (CstInsn) insn;
+ Constant cst = ci.getConstant();
+
+ if (!(cst instanceof CstLiteralBits)) {
+ return false;
+ }
+
+ CstLiteralBits cb = (CstLiteralBits) cst;
+
+ return cb.fitsInInt() && signedFitsInShort(cb.getIntBits());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public InsnFormat nextUp() {
+ return Form21h.THE_ONE;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ int value =
+ ((CstLiteralBits) ((CstInsn) insn).getConstant()).getIntBits();
+
+ write(out,
+ opcodeUnit(insn, regs.get(0).getReg()),
+ (short) value);
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form21t.java b/dx/src/com/android/dx/dex/code/form/Form21t.java
new file mode 100644
index 0000000..f0ce644
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form21t.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.dex.code.TargetInsn;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 21t}. See the instruction format spec
+ * for details.
+ */
+public final class Form21t extends InsnFormat {
+ /** {@code non-null;} unique instance of this class */
+ public static final InsnFormat THE_ONE = new Form21t();
+
+ /**
+ * Constructs an instance. This class is not publicly
+ * instantiable. Use {@link #THE_ONE}.
+ */
+ private Form21t() {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnArgString(DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ return regs.get(0).regString() + ", " + branchString(insn);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+ return branchComment(insn);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int codeSize() {
+ return 2;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCompatible(DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+
+ if (!((insn instanceof TargetInsn) &&
+ (regs.size() == 1) &&
+ unsignedFitsInByte(regs.get(0).getReg()))) {
+ return false;
+ }
+
+ TargetInsn ti = (TargetInsn) insn;
+ return ti.hasTargetOffset() ? branchFits(ti) : true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean branchFits(TargetInsn insn) {
+ int offset = insn.getTargetOffset();
+
+ // Note: A zero offset would fit, but it is prohibited by the spec.
+ return (offset != 0) && signedFitsInShort(offset);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public InsnFormat nextUp() {
+ return Form31t.THE_ONE;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ int offset = ((TargetInsn) insn).getTargetOffset();
+
+ write(out,
+ opcodeUnit(insn, regs.get(0).getReg()),
+ (short) offset);
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form22b.java b/dx/src/com/android/dx/dex/code/form/Form22b.java
new file mode 100644
index 0000000..2884fbb
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form22b.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 22b}. See the instruction format spec
+ * for details.
+ */
+public final class Form22b extends InsnFormat {
+ /** {@code non-null;} unique instance of this class */
+ public static final InsnFormat THE_ONE = new Form22b();
+
+ /**
+ * Constructs an instance. This class is not publicly
+ * instantiable. Use {@link #THE_ONE}.
+ */
+ private Form22b() {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnArgString(DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+
+ return regs.get(0).regString() + ", " + regs.get(1).regString() +
+ ", " + literalBitsString(value);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+ CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+ return literalBitsComment(value, 8);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int codeSize() {
+ return 2;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCompatible(DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ if (!((insn instanceof CstInsn) &&
+ (regs.size() == 2) &&
+ unsignedFitsInByte(regs.get(0).getReg()) &&
+ unsignedFitsInByte(regs.get(1).getReg()))) {
+ return false;
+ }
+
+ CstInsn ci = (CstInsn) insn;
+ Constant cst = ci.getConstant();
+
+ if (!(cst instanceof CstLiteralBits)) {
+ return false;
+ }
+
+ CstLiteralBits cb = (CstLiteralBits) cst;
+
+ return cb.fitsInInt() && signedFitsInByte(cb.getIntBits());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public InsnFormat nextUp() {
+ return Form22s.THE_ONE;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ int value =
+ ((CstLiteralBits) ((CstInsn) insn).getConstant()).getIntBits();
+
+ write(out,
+ opcodeUnit(insn, regs.get(0).getReg()),
+ codeUnit(regs.get(1).getReg(), value & 0xff));
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form22c.java b/dx/src/com/android/dx/dex/code/form/Form22c.java
new file mode 100644
index 0000000..423ccc8
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form22c.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 22c}. See the instruction format spec
+ * for details.
+ */
+public final class Form22c extends InsnFormat {
+ /** {@code non-null;} unique instance of this class */
+ public static final InsnFormat THE_ONE = new Form22c();
+
+ /**
+ * Constructs an instance. This class is not publicly
+ * instantiable. Use {@link #THE_ONE}.
+ */
+ private Form22c() {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnArgString(DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ return regs.get(0).regString() + ", " + regs.get(1).regString() +
+ ", " + cstString(insn);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+ if (noteIndices) {
+ return cstComment(insn);
+ } else {
+ return "";
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int codeSize() {
+ return 2;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCompatible(DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ if (!((insn instanceof CstInsn) &&
+ (regs.size() == 2) &&
+ unsignedFitsInNibble(regs.get(0).getReg()) &&
+ unsignedFitsInNibble(regs.get(1).getReg()))) {
+ return false;
+ }
+
+ CstInsn ci = (CstInsn) insn;
+ int cpi = ci.getIndex();
+
+ if (! unsignedFitsInShort(cpi)) {
+ return false;
+ }
+
+ Constant cst = ci.getConstant();
+ return (cst instanceof CstType) ||
+ (cst instanceof CstFieldRef);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public InsnFormat nextUp() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ int cpi = ((CstInsn) insn).getIndex();
+
+ write(out,
+ opcodeUnit(insn,
+ makeByte(regs.get(0).getReg(), regs.get(1).getReg())),
+ (short) cpi);
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form22s.java b/dx/src/com/android/dx/dex/code/form/Form22s.java
new file mode 100644
index 0000000..5964217
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form22s.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 22s}. See the instruction format spec
+ * for details.
+ */
+public final class Form22s extends InsnFormat {
+ /** {@code non-null;} unique instance of this class */
+ public static final InsnFormat THE_ONE = new Form22s();
+
+ /**
+ * Constructs an instance. This class is not publicly
+ * instantiable. Use {@link #THE_ONE}.
+ */
+ private Form22s() {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnArgString(DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+
+ return regs.get(0).regString() + ", " + regs.get(1).regString()
+ + ", " + literalBitsString(value);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+ CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+ return literalBitsComment(value, 16);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int codeSize() {
+ return 2;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCompatible(DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ if (!((insn instanceof CstInsn) &&
+ (regs.size() == 2) &&
+ unsignedFitsInNibble(regs.get(0).getReg()) &&
+ unsignedFitsInNibble(regs.get(1).getReg()))) {
+ return false;
+ }
+
+ CstInsn ci = (CstInsn) insn;
+ Constant cst = ci.getConstant();
+
+ if (!(cst instanceof CstLiteralBits)) {
+ return false;
+ }
+
+ CstLiteralBits cb = (CstLiteralBits) cst;
+
+ return cb.fitsInInt() && signedFitsInShort(cb.getIntBits());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public InsnFormat nextUp() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ int value =
+ ((CstLiteralBits) ((CstInsn) insn).getConstant()).getIntBits();
+
+ write(out,
+ opcodeUnit(insn,
+ makeByte(regs.get(0).getReg(), regs.get(1).getReg())),
+ (short) value);
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form22t.java b/dx/src/com/android/dx/dex/code/form/Form22t.java
new file mode 100644
index 0000000..1577803
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form22t.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.dex.code.TargetInsn;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 22t}. See the instruction format spec
+ * for details.
+ */
+public final class Form22t extends InsnFormat {
+ /** {@code non-null;} unique instance of this class */
+ public static final InsnFormat THE_ONE = new Form22t();
+
+ /**
+ * Constructs an instance. This class is not publicly
+ * instantiable. Use {@link #THE_ONE}.
+ */
+ private Form22t() {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnArgString(DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ return regs.get(0).regString() + ", " + regs.get(1).regString() +
+ ", " + branchString(insn);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+ return branchComment(insn);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int codeSize() {
+ return 2;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCompatible(DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+
+ if (!((insn instanceof TargetInsn) &&
+ (regs.size() == 2) &&
+ unsignedFitsInNibble(regs.get(0).getReg()) &&
+ unsignedFitsInNibble(regs.get(1).getReg()))) {
+ return false;
+ }
+
+ TargetInsn ti = (TargetInsn) insn;
+ return ti.hasTargetOffset() ? branchFits(ti) : true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean branchFits(TargetInsn insn) {
+ int offset = insn.getTargetOffset();
+
+ // Note: A zero offset would fit, but it is prohibited by the spec.
+ return (offset != 0) && signedFitsInShort(offset);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public InsnFormat nextUp() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ int offset = ((TargetInsn) insn).getTargetOffset();
+
+ write(out,
+ opcodeUnit(insn,
+ makeByte(regs.get(0).getReg(), regs.get(1).getReg())),
+ (short) offset);
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form22x.java b/dx/src/com/android/dx/dex/code/form/Form22x.java
new file mode 100644
index 0000000..b7ce4e7
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form22x.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.dex.code.SimpleInsn;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 22x}. See the instruction format spec
+ * for details.
+ */
+public final class Form22x extends InsnFormat {
+ /** {@code non-null;} unique instance of this class */
+ public static final InsnFormat THE_ONE = new Form22x();
+
+ /**
+ * Constructs an instance. This class is not publicly
+ * instantiable. Use {@link #THE_ONE}.
+ */
+ private Form22x() {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnArgString(DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ return regs.get(0).regString() + ", " + regs.get(1).regString();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+ // This format has no comment.
+ return "";
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int codeSize() {
+ return 2;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCompatible(DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+
+ return (insn instanceof SimpleInsn) &&
+ (regs.size() == 2) &&
+ unsignedFitsInByte(regs.get(0).getReg()) &&
+ unsignedFitsInShort(regs.get(1).getReg());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public InsnFormat nextUp() {
+ return Form23x.THE_ONE;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ write(out,
+ opcodeUnit(insn, regs.get(0).getReg()),
+ (short) regs.get(1).getReg());
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form23x.java b/dx/src/com/android/dx/dex/code/form/Form23x.java
new file mode 100644
index 0000000..64dd6b0
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form23x.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.dex.code.SimpleInsn;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 23x}. See the instruction format spec
+ * for details.
+ */
+public final class Form23x extends InsnFormat {
+ /** {@code non-null;} unique instance of this class */
+ public static final InsnFormat THE_ONE = new Form23x();
+
+ /**
+ * Constructs an instance. This class is not publicly
+ * instantiable. Use {@link #THE_ONE}.
+ */
+ private Form23x() {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnArgString(DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ return regs.get(0).regString() + ", " + regs.get(1).regString() +
+ ", " + regs.get(2).regString();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+ // This format has no comment.
+ return "";
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int codeSize() {
+ return 2;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCompatible(DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+
+ return (insn instanceof SimpleInsn) &&
+ (regs.size() == 3) &&
+ unsignedFitsInByte(regs.get(0).getReg()) &&
+ unsignedFitsInByte(regs.get(1).getReg()) &&
+ unsignedFitsInByte(regs.get(2).getReg());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public InsnFormat nextUp() {
+ return Form32x.THE_ONE;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ write(out,
+ opcodeUnit(insn, regs.get(0).getReg()),
+ codeUnit(regs.get(1).getReg(), regs.get(2).getReg()));
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form30t.java b/dx/src/com/android/dx/dex/code/form/Form30t.java
new file mode 100644
index 0000000..af0a699
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form30t.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.dex.code.TargetInsn;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 30t}. See the instruction format spec
+ * for details.
+ */
+public final class Form30t extends InsnFormat {
+ /** {@code non-null;} unique instance of this class */
+ public static final InsnFormat THE_ONE = new Form30t();
+
+ /**
+ * Constructs an instance. This class is not publicly
+ * instantiable. Use {@link #THE_ONE}.
+ */
+ private Form30t() {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnArgString(DalvInsn insn) {
+ return branchString(insn);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+ return branchComment(insn);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int codeSize() {
+ return 3;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCompatible(DalvInsn insn) {
+ if (!((insn instanceof TargetInsn) &&
+ (insn.getRegisters().size() == 0))) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean branchFits(TargetInsn insn) {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public InsnFormat nextUp() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+ int offset = ((TargetInsn) insn).getTargetOffset();
+
+ write(out, opcodeUnit(insn, 0),
+ (short) offset,
+ (short) (offset >> 16));
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form31c.java b/dx/src/com/android/dx/dex/code/form/Form31c.java
new file mode 100644
index 0000000..0c983c5
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form31c.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 31c}. See the instruction format spec
+ * for details.
+ */
+public final class Form31c extends InsnFormat {
+ /** {@code non-null;} unique instance of this class */
+ public static final InsnFormat THE_ONE = new Form31c();
+
+ /**
+ * Constructs an instance. This class is not publicly
+ * instantiable. Use {@link #THE_ONE}.
+ */
+ private Form31c() {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnArgString(DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ return regs.get(0).regString() + ", " + cstString(insn);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+ if (noteIndices) {
+ return cstComment(insn);
+ } else {
+ return "";
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int codeSize() {
+ return 3;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCompatible(DalvInsn insn) {
+ if (!(insn instanceof CstInsn)) {
+ return false;
+ }
+
+ RegisterSpecList regs = insn.getRegisters();
+ RegisterSpec reg;
+
+ switch (regs.size()) {
+ case 1: {
+ reg = regs.get(0);
+ break;
+ }
+ case 2: {
+ /*
+ * This format is allowed for ops that are effectively
+ * 2-arg but where the two args are identical.
+ */
+ reg = regs.get(0);
+ if (reg.getReg() != regs.get(1).getReg()) {
+ return false;
+ }
+ break;
+ }
+ default: {
+ return false;
+ }
+ }
+
+ if (!unsignedFitsInByte(reg.getReg())) {
+ return false;
+ }
+
+ CstInsn ci = (CstInsn) insn;
+ Constant cst = ci.getConstant();
+
+ return ((cst instanceof CstType) ||
+ (cst instanceof CstFieldRef) ||
+ (cst instanceof CstString));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public InsnFormat nextUp() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ int cpi = ((CstInsn) insn).getIndex();
+
+ write(out,
+ opcodeUnit(insn, regs.get(0).getReg()),
+ (short) cpi,
+ (short) (cpi >> 16));
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form31i.java b/dx/src/com/android/dx/dex/code/form/Form31i.java
new file mode 100644
index 0000000..c893a12
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form31i.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 31i}. See the instruction format spec
+ * for details.
+ */
+public final class Form31i extends InsnFormat {
+ /** {@code non-null;} unique instance of this class */
+ public static final InsnFormat THE_ONE = new Form31i();
+
+ /**
+ * Constructs an instance. This class is not publicly
+ * instantiable. Use {@link #THE_ONE}.
+ */
+ private Form31i() {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnArgString(DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+
+ return regs.get(0).regString() + ", " + literalBitsString(value);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+ CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+ return literalBitsComment(value, 32);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int codeSize() {
+ return 3;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCompatible(DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ if (!((insn instanceof CstInsn) &&
+ (regs.size() == 1) &&
+ unsignedFitsInByte(regs.get(0).getReg()))) {
+ return false;
+ }
+
+ CstInsn ci = (CstInsn) insn;
+ Constant cst = ci.getConstant();
+
+ if (!(cst instanceof CstLiteralBits)) {
+ return false;
+ }
+
+ return ((CstLiteralBits) cst).fitsInInt();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public InsnFormat nextUp() {
+ return Form51l.THE_ONE;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ int value =
+ ((CstLiteralBits) ((CstInsn) insn).getConstant()).getIntBits();
+
+ write(out,
+ opcodeUnit(insn, regs.get(0).getReg()),
+ (short) value,
+ (short) (value >> 16));
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form31t.java b/dx/src/com/android/dx/dex/code/form/Form31t.java
new file mode 100644
index 0000000..682408c
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form31t.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.dex.code.TargetInsn;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 31t}. See the instruction format spec
+ * for details.
+ */
+public final class Form31t extends InsnFormat {
+ /** {@code non-null;} unique instance of this class */
+ public static final InsnFormat THE_ONE = new Form31t();
+
+ /**
+ * Constructs an instance. This class is not publicly
+ * instantiable. Use {@link #THE_ONE}.
+ */
+ private Form31t() {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnArgString(DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ return regs.get(0).regString() + ", " + branchString(insn);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+ return branchComment(insn);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int codeSize() {
+ return 3;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCompatible(DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+
+ if (!((insn instanceof TargetInsn) &&
+ (regs.size() == 1) &&
+ unsignedFitsInByte(regs.get(0).getReg()))) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean branchFits(TargetInsn insn) {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public InsnFormat nextUp() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ int offset = ((TargetInsn) insn).getTargetOffset();
+
+ write(out, opcodeUnit(insn, regs.get(0).getReg()),
+ (short) offset,
+ (short) (offset >> 16));
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form32x.java b/dx/src/com/android/dx/dex/code/form/Form32x.java
new file mode 100644
index 0000000..4a981ee
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form32x.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.dex.code.SimpleInsn;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 32x}. See the instruction format spec
+ * for details.
+ */
+public final class Form32x extends InsnFormat {
+ /** {@code non-null;} unique instance of this class */
+ public static final InsnFormat THE_ONE = new Form32x();
+
+ /**
+ * Constructs an instance. This class is not publicly
+ * instantiable. Use {@link #THE_ONE}.
+ */
+ private Form32x() {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnArgString(DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ return regs.get(0).regString() + ", " + regs.get(1).regString();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+ // This format has no comment.
+ return "";
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int codeSize() {
+ return 3;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCompatible(DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ return (insn instanceof SimpleInsn) &&
+ (regs.size() == 2) &&
+ unsignedFitsInShort(regs.get(0).getReg()) &&
+ unsignedFitsInShort(regs.get(1).getReg());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public InsnFormat nextUp() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+
+ write(out,
+ opcodeUnit(insn, 0),
+ (short) regs.get(0).getReg(),
+ (short) regs.get(1).getReg());
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form35c.java b/dx/src/com/android/dx/dex/code/form/Form35c.java
new file mode 100644
index 0000000..31b127d
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form35c.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 35c}. See the instruction format spec
+ * for details.
+ */
+public final class Form35c extends InsnFormat {
+ /** {@code non-null;} unique instance of this class */
+ public static final InsnFormat THE_ONE = new Form35c();
+
+ /** Maximal number of operands */
+ private static final int MAX_NUM_OPS = 5;
+
+ /**
+ * Constructs an instance. This class is not publicly
+ * instantiable. Use {@link #THE_ONE}.
+ */
+ private Form35c() {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnArgString(DalvInsn insn) {
+ RegisterSpecList regs = explicitize(insn.getRegisters());
+ return regListString(regs) + ", " + cstString(insn);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+ if (noteIndices) {
+ return cstComment(insn);
+ } else {
+ return "";
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int codeSize() {
+ return 3;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCompatible(DalvInsn insn) {
+ if (!(insn instanceof CstInsn)) {
+ return false;
+ }
+
+ CstInsn ci = (CstInsn) insn;
+ int cpi = ci.getIndex();
+
+ if (! unsignedFitsInShort(cpi)) {
+ return false;
+ }
+
+ Constant cst = ci.getConstant();
+ if (!((cst instanceof CstMethodRef) ||
+ (cst instanceof CstType))) {
+ return false;
+ }
+
+ RegisterSpecList regs = ci.getRegisters();
+ return (wordCount(regs) >= 0);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public InsnFormat nextUp() {
+ return Form3rc.THE_ONE;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+ int cpi = ((CstInsn) insn).getIndex();
+ RegisterSpecList regs = explicitize(insn.getRegisters());
+ int sz = regs.size();
+ int r0 = (sz > 0) ? regs.get(0).getReg() : 0;
+ int r1 = (sz > 1) ? regs.get(1).getReg() : 0;
+ int r2 = (sz > 2) ? regs.get(2).getReg() : 0;
+ int r3 = (sz > 3) ? regs.get(3).getReg() : 0;
+ int r4 = (sz > 4) ? regs.get(4).getReg() : 0;
+
+ write(out,
+ opcodeUnit(insn,
+ makeByte(r4, sz)), // encode the fifth operand here
+ (short) cpi,
+ codeUnit(r0, r1, r2, r3));
+ }
+
+ /**
+ * Gets the number of words required for the given register list, where
+ * category-2 values count as two words. Return {@code -1} if the
+ * list requires more than five words or contains registers that need
+ * more than a nibble to identify them.
+ *
+ * @param regs {@code non-null;} the register list in question
+ * @return {@code >= -1;} the number of words required, or {@code -1}
+ * if the list couldn't possibly fit in this format
+ */
+ private static int wordCount(RegisterSpecList regs) {
+ int sz = regs.size();
+
+ if (sz > MAX_NUM_OPS) {
+ // It can't possibly fit.
+ return -1;
+ }
+
+ int result = 0;
+
+ for (int i = 0; i < sz; i++) {
+ RegisterSpec one = regs.get(i);
+ result += one.getCategory();
+ /*
+ * The check below adds (category - 1) to the register, to
+ * account for the fact that the second half of a
+ * category-2 register has to be represented explicitly in
+ * the result.
+ */
+ if (!unsignedFitsInNibble(one.getReg() + one.getCategory() - 1)) {
+ return -1;
+ }
+ }
+
+ return (result <= MAX_NUM_OPS) ? result : -1;
+ }
+
+ /**
+ * Returns a register list which is equivalent to the given one,
+ * except that it splits category-2 registers into two explicit
+ * entries. This returns the original list if no modification is
+ * required
+ *
+ * @param orig {@code non-null;} the original list
+ * @return {@code non-null;} the list with the described transformation
+ */
+ private static RegisterSpecList explicitize(RegisterSpecList orig) {
+ int wordCount = wordCount(orig);
+ int sz = orig.size();
+
+ if (wordCount == sz) {
+ return orig;
+ }
+
+ RegisterSpecList result = new RegisterSpecList(wordCount);
+ int wordAt = 0;
+
+ for (int i = 0; i < sz; i++) {
+ RegisterSpec one = orig.get(i);
+ result.set(wordAt, one);
+ if (one.getCategory() == 2) {
+ result.set(wordAt + 1,
+ RegisterSpec.make(one.getReg() + 1, Type.VOID));
+ wordAt += 2;
+ } else {
+ wordAt++;
+ }
+ }
+
+ result.setImmutable();
+ return result;
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form3rc.java b/dx/src/com/android/dx/dex/code/form/Form3rc.java
new file mode 100644
index 0000000..755ad76
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form3rc.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 3rc}. See the instruction format spec
+ * for details.
+ */
+public final class Form3rc extends InsnFormat {
+ /** {@code non-null;} unique instance of this class */
+ public static final InsnFormat THE_ONE = new Form3rc();
+
+ /**
+ * Constructs an instance. This class is not publicly
+ * instantiable. Use {@link #THE_ONE}.
+ */
+ private Form3rc() {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnArgString(DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ int size = regs.size();
+ StringBuilder sb = new StringBuilder(30);
+
+ sb.append("{");
+
+ switch (size) {
+ case 0: {
+ // Nothing to do.
+ break;
+ }
+ case 1: {
+ sb.append(regs.get(0).regString());
+ break;
+ }
+ default: {
+ RegisterSpec lastReg = regs.get(size - 1);
+ if (lastReg.getCategory() == 2) {
+ /*
+ * Add one to properly represent a list-final
+ * category-2 register.
+ */
+ lastReg = lastReg.withOffset(1);
+ }
+
+ sb.append(regs.get(0).regString());
+ sb.append("..");
+ sb.append(lastReg.regString());
+ }
+ }
+
+ sb.append("}, ");
+ sb.append(cstString(insn));
+
+ return sb.toString();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+ if (noteIndices) {
+ return cstComment(insn);
+ } else {
+ return "";
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int codeSize() {
+ return 3;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCompatible(DalvInsn insn) {
+ if (!(insn instanceof CstInsn)) {
+ return false;
+ }
+
+ CstInsn ci = (CstInsn) insn;
+ int cpi = ci.getIndex();
+
+ if (! unsignedFitsInShort(cpi)) {
+ return false;
+ }
+
+ Constant cst = ci.getConstant();
+ if (!((cst instanceof CstMethodRef) ||
+ (cst instanceof CstType))) {
+ return false;
+ }
+
+ RegisterSpecList regs = ci.getRegisters();
+ int sz = regs.size();
+
+ if (sz == 0) {
+ return true;
+ }
+
+ int first = regs.get(0).getReg();
+ int next = first;
+
+ if (!unsignedFitsInShort(first)) {
+ return false;
+ }
+
+ for (int i = 0; i < sz; i++) {
+ RegisterSpec one = regs.get(i);
+ if (one.getReg() != next) {
+ return false;
+ }
+ next += one.getCategory();
+ }
+
+ return unsignedFitsInByte(next - first);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public InsnFormat nextUp() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ int sz = regs.size();
+ int cpi = ((CstInsn) insn).getIndex();
+ int firstReg;
+ int count;
+
+ if (sz == 0) {
+ firstReg = 0;
+ count = 0;
+ } else {
+ int lastReg = regs.get(sz - 1).getNextReg();
+ firstReg = regs.get(0).getReg();
+ count = lastReg - firstReg;
+ }
+
+ write(out,
+ opcodeUnit(insn, count),
+ (short) cpi,
+ (short) firstReg);
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/Form51l.java b/dx/src/com/android/dx/dex/code/form/Form51l.java
new file mode 100644
index 0000000..9e3ab6a
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/Form51l.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstLiteral64;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format {@code 51l}. See the instruction format spec
+ * for details.
+ */
+public final class Form51l extends InsnFormat {
+ /** {@code non-null;} unique instance of this class */
+ public static final InsnFormat THE_ONE = new Form51l();
+
+ /**
+ * Constructs an instance. This class is not publicly
+ * instantiable. Use {@link #THE_ONE}.
+ */
+ private Form51l() {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnArgString(DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+
+ return regs.get(0).regString() + ", " + literalBitsString(value);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+ CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
+ return literalBitsComment(value, 64);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int codeSize() {
+ return 5;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCompatible(DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ if (!((insn instanceof CstInsn) &&
+ (regs.size() == 1) &&
+ unsignedFitsInByte(regs.get(0).getReg()))) {
+ return false;
+ }
+
+ CstInsn ci = (CstInsn) insn;
+ Constant cst = ci.getConstant();
+
+ return (cst instanceof CstLiteral64);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public InsnFormat nextUp() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+ RegisterSpecList regs = insn.getRegisters();
+ long value =
+ ((CstLiteral64) ((CstInsn) insn).getConstant()).getLongBits();
+
+ write(out,
+ opcodeUnit(insn, regs.get(0).getReg()),
+ (short) value,
+ (short) (value >> 16),
+ (short) (value >> 32),
+ (short) (value >> 48));
+ }
+}
diff --git a/dx/src/com/android/dx/dex/code/form/SpecialFormat.java b/dx/src/com/android/dx/dex/code/form/SpecialFormat.java
new file mode 100644
index 0000000..8a2e5ed
--- /dev/null
+++ b/dx/src/com/android/dx/dex/code/form/SpecialFormat.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.code.form;
+
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.DalvOps;
+import com.android.dx.dex.code.InsnFormat;
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Instruction format for nonstandard format instructions, which aren't
+ * generally real instructions but do end up appearing in instruction
+ * lists. Most of the overridden methods on this class end up throwing
+ * exceptions, as code should know (implicitly or explicitly) to avoid
+ * using this class. The one exception is {@link #isCompatible}, which
+ * always returns {@code true}.
+ */
+public final class SpecialFormat extends InsnFormat {
+ /** {@code non-null;} unique instance of this class */
+ public static final InsnFormat THE_ONE = new SpecialFormat();
+
+ /**
+ * Constructs an instance. This class is not publicly
+ * instantiable. Use {@link #THE_ONE}.
+ */
+ private SpecialFormat() {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnArgString(DalvInsn insn) {
+ throw new RuntimeException("unsupported");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String insnCommentString(DalvInsn insn, boolean noteIndices) {
+ throw new RuntimeException("unsupported");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int codeSize() {
+ throw new RuntimeException("unsupported");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCompatible(DalvInsn insn) {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public InsnFormat nextUp() {
+ throw new RuntimeException("unsupported");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(AnnotatedOutput out, DalvInsn insn) {
+ throw new RuntimeException("unsupported");
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/AnnotationItem.java b/dx/src/com/android/dx/dex/file/AnnotationItem.java
new file mode 100644
index 0000000..1febd9e
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/AnnotationItem.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.annotation.Annotation;
+import com.android.dx.rop.annotation.AnnotationVisibility;
+import com.android.dx.rop.annotation.NameValuePair;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstAnnotation;
+import com.android.dx.rop.cst.CstArray;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.ByteArrayAnnotatedOutput;
+import com.android.dx.util.AnnotatedOutput;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+/**
+ * Single annotation, which consists of a type and a set of name-value
+ * element pairs.
+ */
+public final class AnnotationItem extends OffsettedItem {
+ /** annotation visibility constant: visible at build time only */
+ private static final int VISIBILITY_BUILD = 0;
+
+ /** annotation visibility constant: visible at runtime */
+ private static final int VISIBILITY_RUNTIME = 1;
+
+ /** annotation visibility constant: visible at runtime only to system */
+ private static final int VISIBILITY_SYSTEM = 2;
+
+ /** the required alignment for instances of this class */
+ private static final int ALIGNMENT = 1;
+
+ /** {@code non-null;} unique instance of {@link #TypeIdSorter} */
+ private static final TypeIdSorter TYPE_ID_SORTER = new TypeIdSorter();
+
+ /** {@code non-null;} the annotation to represent */
+ private final Annotation annotation;
+
+ /**
+ * {@code null-ok;} type reference for the annotation type; set during
+ * {@link #addContents}
+ */
+ private TypeIdItem type;
+
+ /**
+ * {@code null-ok;} encoded form, ready for writing to a file; set during
+ * {@link #place0}
+ */
+ private byte[] encodedForm;
+
+ /**
+ * Comparator that sorts (outer) instances by type id index.
+ */
+ private static class TypeIdSorter implements Comparator<AnnotationItem> {
+ /** {@inheritDoc} */
+ public int compare(AnnotationItem item1, AnnotationItem item2) {
+ int index1 = item1.type.getIndex();
+ int index2 = item2.type.getIndex();
+
+ if (index1 < index2) {
+ return -1;
+ } else if (index1 > index2) {
+ return 1;
+ }
+
+ return 0;
+ }
+ }
+
+ /**
+ * Sorts an array of instances, in place, by type id index,
+ * ignoring all other aspects of the elements. This is only valid
+ * to use after type id indices are known.
+ *
+ * @param array {@code non-null;} array to sort
+ */
+ public static void sortByTypeIdIndex(AnnotationItem[] array) {
+ Arrays.sort(array, TYPE_ID_SORTER);
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param annotation {@code non-null;} annotation to represent
+ */
+ public AnnotationItem(Annotation annotation) {
+ /*
+ * The write size isn't known up-front because (the variable-lengthed)
+ * leb128 type is used to represent some things.
+ */
+ super(ALIGNMENT, -1);
+
+ if (annotation == null) {
+ throw new NullPointerException("annotation == null");
+ }
+
+ this.annotation = annotation;
+ this.type = null;
+ this.encodedForm = null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ItemType itemType() {
+ return ItemType.TYPE_ANNOTATION_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return annotation.hashCode();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected int compareTo0(OffsettedItem other) {
+ AnnotationItem otherAnnotation = (AnnotationItem) other;
+
+ return annotation.compareTo(otherAnnotation.annotation);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toHuman() {
+ return annotation.toHuman();
+ }
+
+ /** {@inheritDoc} */
+ public void addContents(DexFile file) {
+ type = file.getTypeIds().intern(annotation.getType());
+ ValueEncoder.addContents(file, annotation);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void place0(Section addedTo, int offset) {
+ // Encode the data and note the size.
+
+ ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput();
+ ValueEncoder encoder = new ValueEncoder(addedTo.getFile(), out);
+
+ encoder.writeAnnotation(annotation, false);
+ encodedForm = out.toByteArray();
+
+ // Add one for the visibility byte in front of the encoded annotation.
+ setWriteSize(encodedForm.length + 1);
+ }
+
+ /**
+ * Write a (listing file) annotation for this instance to the given
+ * output, that consumes no bytes of output. This is for annotating
+ * a reference to this instance at the point of the reference.
+ *
+ * @param out {@code non-null;} where to output to
+ * @param prefix {@code non-null;} prefix for each line of output
+ */
+ public void annotateTo(AnnotatedOutput out, String prefix) {
+ out.annotate(0, prefix + "visibility: " +
+ annotation.getVisibility().toHuman());
+ out.annotate(0, prefix + "type: " + annotation.getType().toHuman());
+
+ for (NameValuePair pair : annotation.getNameValuePairs()) {
+ CstUtf8 name = pair.getName();
+ Constant value = pair.getValue();
+
+ out.annotate(0, prefix + name.toHuman() + ": " +
+ ValueEncoder.constantToHuman(value));
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void writeTo0(DexFile file, AnnotatedOutput out) {
+ boolean annotates = out.annotates();
+ AnnotationVisibility visibility = annotation.getVisibility();
+
+ if (annotates) {
+ out.annotate(0, offsetString() + " annotation");
+ out.annotate(1, " visibility: VISBILITY_" + visibility);
+ }
+
+ switch (visibility) {
+ case BUILD: out.writeByte(VISIBILITY_BUILD); break;
+ case RUNTIME: out.writeByte(VISIBILITY_RUNTIME); break;
+ case SYSTEM: out.writeByte(VISIBILITY_SYSTEM); break;
+ default: {
+ // EMBEDDED shouldn't appear at the top level.
+ throw new RuntimeException("shouldn't happen");
+ }
+ }
+
+ if (annotates) {
+ /*
+ * The output is to be annotated, so redo the work previously
+ * done by place0(), except this time annotations will actually
+ * get emitted.
+ */
+ ValueEncoder encoder = new ValueEncoder(file, out);
+ encoder.writeAnnotation(annotation, true);
+ } else {
+ out.write(encodedForm);
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/AnnotationSetItem.java b/dx/src/com/android/dx/dex/file/AnnotationSetItem.java
new file mode 100644
index 0000000..2187700
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/AnnotationSetItem.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.annotation.Annotations;
+import com.android.dx.rop.annotation.Annotation;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+/**
+ * Set of annotations, where no annotation type appears more than once.
+ */
+public final class AnnotationSetItem extends OffsettedItem {
+ /** the required alignment for instances of this class */
+ private static final int ALIGNMENT = 4;
+
+ /** the size of an entry int the set: one {@code uint} */
+ private static final int ENTRY_WRITE_SIZE = 4;
+
+ /** {@code non-null;} the set of annotations */
+ private final Annotations annotations;
+
+ /**
+ * {@code non-null;} set of annotations as individual items in an array.
+ * <b>Note:</b> The contents have to get sorted by type id before
+ * writing.
+ */
+ private final AnnotationItem[] items;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param annotations {@code non-null;} set of annotations
+ */
+ public AnnotationSetItem(Annotations annotations) {
+ super(ALIGNMENT, writeSize(annotations));
+
+ this.annotations = annotations;
+ this.items = new AnnotationItem[annotations.size()];
+
+ int at = 0;
+ for (Annotation a : annotations.getAnnotations()) {
+ items[at] = new AnnotationItem(a);
+ at++;
+ }
+ }
+
+ /**
+ * Gets the write size for the given set.
+ *
+ * @param annotations {@code non-null;} the set
+ * @return {@code > 0;} the write size
+ */
+ private static int writeSize(Annotations annotations) {
+ // This includes an int size at the start of the list.
+
+ try {
+ return (annotations.size() * ENTRY_WRITE_SIZE) + 4;
+ } catch (NullPointerException ex) {
+ // Elucidate the exception.
+ throw new NullPointerException("list == null");
+ }
+ }
+
+ /**
+ * Gets the underlying annotations of this instance
+ *
+ * @return {@code non-null;} the annotations
+ */
+ public Annotations getAnnotations() {
+ return annotations;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return annotations.hashCode();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected int compareTo0(OffsettedItem other) {
+ AnnotationSetItem otherSet = (AnnotationSetItem) other;
+
+ return annotations.compareTo(otherSet.annotations);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ItemType itemType() {
+ return ItemType.TYPE_ANNOTATION_SET_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toHuman() {
+ return annotations.toString();
+ }
+
+ /** {@inheritDoc} */
+ public void addContents(DexFile file) {
+ MixedItemSection byteData = file.getByteData();
+ int size = items.length;
+
+ for (int i = 0; i < size; i++) {
+ items[i] = byteData.intern(items[i]);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void place0(Section addedTo, int offset) {
+ // Sort the array to be in type id index order.
+ AnnotationItem.sortByTypeIdIndex(items);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void writeTo0(DexFile file, AnnotatedOutput out) {
+ boolean annotates = out.annotates();
+ int size = items.length;
+
+ if (annotates) {
+ out.annotate(0, offsetString() + " annotation set");
+ out.annotate(4, " size: " + Hex.u4(size));
+ }
+
+ out.writeInt(size);
+
+ for (int i = 0; i < size; i++) {
+ AnnotationItem item = items[i];
+ int offset = item.getAbsoluteOffset();
+
+ if (annotates) {
+ out.annotate(4, " entries[" + Integer.toHexString(i) + "]: " +
+ Hex.u4(offset));
+ items[i].annotateTo(out, " ");
+ }
+
+ out.writeInt(offset);
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/AnnotationSetRefItem.java b/dx/src/com/android/dx/dex/file/AnnotationSetRefItem.java
new file mode 100644
index 0000000..53072d8
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/AnnotationSetRefItem.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+/**
+ * Indirect reference to an {@link AnnotationSetItem}.
+ */
+public final class AnnotationSetRefItem extends OffsettedItem {
+ /** the required alignment for instances of this class */
+ private static final int ALIGNMENT = 4;
+
+ /** write size of this class, in bytes */
+ private static final int WRITE_SIZE = 4;
+
+ /** {@code non-null;} the annotation set to refer to */
+ private AnnotationSetItem annotations;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param annotations {@code non-null;} the annotation set to refer to
+ */
+ public AnnotationSetRefItem(AnnotationSetItem annotations) {
+ super(ALIGNMENT, WRITE_SIZE);
+
+ if (annotations == null) {
+ throw new NullPointerException("annotations == null");
+ }
+
+ this.annotations = annotations;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ItemType itemType() {
+ return ItemType.TYPE_ANNOTATION_SET_REF_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ public void addContents(DexFile file) {
+ MixedItemSection wordData = file.getWordData();
+
+ annotations = wordData.intern(annotations);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toHuman() {
+ return annotations.toHuman();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void writeTo0(DexFile file, AnnotatedOutput out) {
+ int annotationsOff = annotations.getAbsoluteOffset();
+
+ if (out.annotates()) {
+ out.annotate(4, " annotations_off: " + Hex.u4(annotationsOff));
+ }
+
+ out.writeInt(annotationsOff);
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/AnnotationUtils.java b/dx/src/com/android/dx/dex/file/AnnotationUtils.java
new file mode 100644
index 0000000..d500ec4
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/AnnotationUtils.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.annotation.Annotation;
+import com.android.dx.rop.annotation.NameValuePair;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstAnnotation;
+import com.android.dx.rop.cst.CstArray;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.CstKnownNull;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+
+import java.util.ArrayList;
+
+import static com.android.dx.rop.annotation.AnnotationVisibility.*;
+
+/**
+ * Utility class for dealing with annotations.
+ */
+public final class AnnotationUtils {
+ /** {@code non-null;} type for {@code AnnotationDefault} annotations */
+ private static final CstType ANNOTATION_DEFAULT_TYPE =
+ CstType.intern(Type.intern("Ldalvik/annotation/AnnotationDefault;"));
+
+ /** {@code non-null;} type for {@code EnclosingClass} annotations */
+ private static final CstType ENCLOSING_CLASS_TYPE =
+ CstType.intern(Type.intern("Ldalvik/annotation/EnclosingClass;"));
+
+ /** {@code non-null;} type for {@code EnclosingMethod} annotations */
+ private static final CstType ENCLOSING_METHOD_TYPE =
+ CstType.intern(Type.intern("Ldalvik/annotation/EnclosingMethod;"));
+
+ /** {@code non-null;} type for {@code InnerClass} annotations */
+ private static final CstType INNER_CLASS_TYPE =
+ CstType.intern(Type.intern("Ldalvik/annotation/InnerClass;"));
+
+ /** {@code non-null;} type for {@code MemberClasses} annotations */
+ private static final CstType MEMBER_CLASSES_TYPE =
+ CstType.intern(Type.intern("Ldalvik/annotation/MemberClasses;"));
+
+ /** {@code non-null;} type for {@code Signature} annotations */
+ private static final CstType SIGNATURE_TYPE =
+ CstType.intern(Type.intern("Ldalvik/annotation/Signature;"));
+
+ /** {@code non-null;} type for {@code Throws} annotations */
+ private static final CstType THROWS_TYPE =
+ CstType.intern(Type.intern("Ldalvik/annotation/Throws;"));
+
+ /** {@code non-null;} the UTF-8 constant {@code "accessFlags"} */
+ private static final CstUtf8 ACCESS_FLAGS_UTF = new CstUtf8("accessFlags");
+
+ /** {@code non-null;} the UTF-8 constant {@code "name"} */
+ private static final CstUtf8 NAME_UTF = new CstUtf8("name");
+
+ /** {@code non-null;} the UTF-8 constant {@code "value"} */
+ private static final CstUtf8 VALUE_UTF = new CstUtf8("value");
+
+ /**
+ * This class is uninstantiable.
+ */
+ private AnnotationUtils() {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Constructs a standard {@code AnnotationDefault} annotation.
+ *
+ * @param defaults {@code non-null;} the defaults, itself as an annotation
+ * @return {@code non-null;} the constructed annotation
+ */
+ public static Annotation makeAnnotationDefault(Annotation defaults) {
+ Annotation result = new Annotation(ANNOTATION_DEFAULT_TYPE, SYSTEM);
+
+ result.put(new NameValuePair(VALUE_UTF, new CstAnnotation(defaults)));
+ result.setImmutable();
+ return result;
+ }
+
+ /**
+ * Constructs a standard {@code EnclosingClass} annotation.
+ *
+ * @param clazz {@code non-null;} the enclosing class
+ * @return {@code non-null;} the annotation
+ */
+ public static Annotation makeEnclosingClass(CstType clazz) {
+ Annotation result = new Annotation(ENCLOSING_CLASS_TYPE, SYSTEM);
+
+ result.put(new NameValuePair(VALUE_UTF, clazz));
+ result.setImmutable();
+ return result;
+ }
+
+ /**
+ * Constructs a standard {@code EnclosingMethod} annotation.
+ *
+ * @param method {@code non-null;} the enclosing method
+ * @return {@code non-null;} the annotation
+ */
+ public static Annotation makeEnclosingMethod(CstMethodRef method) {
+ Annotation result = new Annotation(ENCLOSING_METHOD_TYPE, SYSTEM);
+
+ result.put(new NameValuePair(VALUE_UTF, method));
+ result.setImmutable();
+ return result;
+ }
+
+ /**
+ * Constructs a standard {@code InnerClass} annotation.
+ *
+ * @param name {@code null-ok;} the original name of the class, or
+ * {@code null} to represent an anonymous class
+ * @param accessFlags the original access flags
+ * @return {@code non-null;} the annotation
+ */
+ public static Annotation makeInnerClass(CstUtf8 name, int accessFlags) {
+ Annotation result = new Annotation(INNER_CLASS_TYPE, SYSTEM);
+ Constant nameCst =
+ (name != null) ? new CstString(name) : CstKnownNull.THE_ONE;
+
+ result.put(new NameValuePair(NAME_UTF, nameCst));
+ result.put(new NameValuePair(ACCESS_FLAGS_UTF,
+ CstInteger.make(accessFlags)));
+ result.setImmutable();
+ return result;
+ }
+
+ /**
+ * Constructs a standard {@code MemberClasses} annotation.
+ *
+ * @param types {@code non-null;} the list of (the types of) the member classes
+ * @return {@code non-null;} the annotation
+ */
+ public static Annotation makeMemberClasses(TypeList types) {
+ CstArray array = makeCstArray(types);
+ Annotation result = new Annotation(MEMBER_CLASSES_TYPE, SYSTEM);
+ result.put(new NameValuePair(VALUE_UTF, array));
+ result.setImmutable();
+ return result;
+ }
+
+ /**
+ * Constructs a standard {@code Signature} annotation.
+ *
+ * @param signature {@code non-null;} the signature string
+ * @return {@code non-null;} the annotation
+ */
+ public static Annotation makeSignature(CstUtf8 signature) {
+ Annotation result = new Annotation(SIGNATURE_TYPE, SYSTEM);
+
+ /*
+ * Split the string into pieces that are likely to be common
+ * across many signatures and the rest of the file.
+ */
+
+ String raw = signature.getString();
+ int rawLength = raw.length();
+ ArrayList<String> pieces = new ArrayList<String>(20);
+
+ for (int at = 0; at < rawLength; /*at*/) {
+ char c = raw.charAt(at);
+ int endAt = at + 1;
+ if (c == 'L') {
+ // Scan to ';' or '<'. Consume ';' but not '<'.
+ while (endAt < rawLength) {
+ c = raw.charAt(endAt);
+ if (c == ';') {
+ endAt++;
+ break;
+ } else if (c == '<') {
+ break;
+ }
+ endAt++;
+ }
+ } else {
+ // Scan to 'L' without consuming it.
+ while (endAt < rawLength) {
+ c = raw.charAt(endAt);
+ if (c == 'L') {
+ break;
+ }
+ endAt++;
+ }
+ }
+
+ pieces.add(raw.substring(at, endAt));
+ at = endAt;
+ }
+
+ int size = pieces.size();
+ CstArray.List list = new CstArray.List(size);
+
+ for (int i = 0; i < size; i++) {
+ list.set(i, new CstString(pieces.get(i)));
+ }
+
+ list.setImmutable();
+
+ result.put(new NameValuePair(VALUE_UTF, new CstArray(list)));
+ result.setImmutable();
+ return result;
+ }
+
+ /**
+ * Constructs a standard {@code Throws} annotation.
+ *
+ * @param types {@code non-null;} the list of thrown types
+ * @return {@code non-null;} the annotation
+ */
+ public static Annotation makeThrows(TypeList types) {
+ CstArray array = makeCstArray(types);
+ Annotation result = new Annotation(THROWS_TYPE, SYSTEM);
+ result.put(new NameValuePair(VALUE_UTF, array));
+ result.setImmutable();
+ return result;
+ }
+
+ /**
+ * Converts a {@link TypeList} to a {@link CstArray}.
+ *
+ * @param types {@code non-null;} the type list
+ * @return {@code non-null;} the corresponding array constant
+ */
+ private static CstArray makeCstArray(TypeList types) {
+ int size = types.size();
+ CstArray.List list = new CstArray.List(size);
+
+ for (int i = 0; i < size; i++) {
+ list.set(i, CstType.intern(types.getType(i)));
+ }
+
+ list.setImmutable();
+ return new CstArray(list);
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/AnnotationsDirectoryItem.java b/dx/src/com/android/dx/dex/file/AnnotationsDirectoryItem.java
new file mode 100644
index 0000000..972d4e6
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/AnnotationsDirectoryItem.java
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.annotation.Annotations;
+import com.android.dx.rop.annotation.AnnotationsList;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * Per-class directory of annotations.
+ */
+public final class AnnotationsDirectoryItem extends OffsettedItem {
+ /** the required alignment for instances of this class */
+ private static final int ALIGNMENT = 4;
+
+ /** write size of this class's header, in bytes */
+ private static final int HEADER_SIZE = 16;
+
+ /** write size of a list element, in bytes */
+ private static final int ELEMENT_SIZE = 8;
+
+ /** {@code null-ok;} the class-level annotations, if any */
+ private AnnotationSetItem classAnnotations;
+
+ /** {@code null-ok;} the annotated fields, if any */
+ private ArrayList<FieldAnnotationStruct> fieldAnnotations;
+
+ /** {@code null-ok;} the annotated methods, if any */
+ private ArrayList<MethodAnnotationStruct> methodAnnotations;
+
+ /** {@code null-ok;} the annotated parameters, if any */
+ private ArrayList<ParameterAnnotationStruct> parameterAnnotations;
+
+ /**
+ * Constructs an empty instance.
+ */
+ public AnnotationsDirectoryItem() {
+ super(ALIGNMENT, -1);
+
+ classAnnotations = null;
+ fieldAnnotations = null;
+ methodAnnotations = null;
+ parameterAnnotations = null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ItemType itemType() {
+ return ItemType.TYPE_ANNOTATIONS_DIRECTORY_ITEM;
+ }
+
+ /**
+ * Returns whether this item is empty (has no contents).
+ *
+ * @return {@code true} if this item is empty, or {@code false}
+ * if not
+ */
+ public boolean isEmpty() {
+ return (classAnnotations == null) &&
+ (fieldAnnotations == null) &&
+ (methodAnnotations == null) &&
+ (parameterAnnotations == null);
+ }
+
+ /**
+ * Returns whether this item is a candidate for interning. The only
+ * interning candidates are ones that <i>only</i> have a non-null
+ * set of class annotations, with no other lists.
+ *
+ * @return {@code true} if this is an interning candidate, or
+ * {@code false} if not
+ */
+ public boolean isInternable() {
+ return (classAnnotations != null) &&
+ (fieldAnnotations == null) &&
+ (methodAnnotations == null) &&
+ (parameterAnnotations == null);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ if (classAnnotations == null) {
+ return 0;
+ }
+
+ return classAnnotations.hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p><b>Note:</b>: This throws an exception if this item is not
+ * internable.</p>
+ *
+ * @see #isInternable
+ */
+ @Override
+ public int compareTo0(OffsettedItem other) {
+ if (! isInternable()) {
+ throw new UnsupportedOperationException("uninternable instance");
+ }
+
+ AnnotationsDirectoryItem otherDirectory =
+ (AnnotationsDirectoryItem) other;
+ return classAnnotations.compareTo(otherDirectory.classAnnotations);
+ }
+
+ /**
+ * Sets the direct annotations on this instance. These are annotations
+ * made on the class, per se, as opposed to on one of its members.
+ * It is only valid to call this method at most once per instance.
+ *
+ * @param annotations {@code non-null;} annotations to set for this class
+ */
+ public void setClassAnnotations(Annotations annotations) {
+ if (annotations == null) {
+ throw new NullPointerException("annotations == null");
+ }
+
+ if (classAnnotations != null) {
+ throw new UnsupportedOperationException(
+ "class annotations already set");
+ }
+
+ classAnnotations = new AnnotationSetItem(annotations);
+ }
+
+ /**
+ * Adds a field annotations item to this instance.
+ *
+ * @param field {@code non-null;} field in question
+ * @param annotations {@code non-null;} associated annotations to add
+ */
+ public void addFieldAnnotations(CstFieldRef field,
+ Annotations annotations) {
+ if (fieldAnnotations == null) {
+ fieldAnnotations = new ArrayList<FieldAnnotationStruct>();
+ }
+
+ fieldAnnotations.add(new FieldAnnotationStruct(field,
+ new AnnotationSetItem(annotations)));
+ }
+
+ /**
+ * Adds a method annotations item to this instance.
+ *
+ * @param method {@code non-null;} method in question
+ * @param annotations {@code non-null;} associated annotations to add
+ */
+ public void addMethodAnnotations(CstMethodRef method,
+ Annotations annotations) {
+ if (methodAnnotations == null) {
+ methodAnnotations = new ArrayList<MethodAnnotationStruct>();
+ }
+
+ methodAnnotations.add(new MethodAnnotationStruct(method,
+ new AnnotationSetItem(annotations)));
+ }
+
+ /**
+ * Adds a parameter annotations item to this instance.
+ *
+ * @param method {@code non-null;} method in question
+ * @param list {@code non-null;} associated list of annotation sets to add
+ */
+ public void addParameterAnnotations(CstMethodRef method,
+ AnnotationsList list) {
+ if (parameterAnnotations == null) {
+ parameterAnnotations = new ArrayList<ParameterAnnotationStruct>();
+ }
+
+ parameterAnnotations.add(new ParameterAnnotationStruct(method, list));
+ }
+
+ /**
+ * Gets the method annotations for a given method, if any. This is
+ * meant for use by debugging / dumping code.
+ *
+ * @param method {@code non-null;} the method
+ * @return {@code null-ok;} the method annotations, if any
+ */
+ public Annotations getMethodAnnotations(CstMethodRef method) {
+ if (methodAnnotations == null) {
+ return null;
+ }
+
+ for (MethodAnnotationStruct item : methodAnnotations) {
+ if (item.getMethod().equals(method)) {
+ return item.getAnnotations();
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Gets the parameter annotations for a given method, if any. This is
+ * meant for use by debugging / dumping code.
+ *
+ * @param method {@code non-null;} the method
+ * @return {@code null-ok;} the parameter annotations, if any
+ */
+ public AnnotationsList getParameterAnnotations(CstMethodRef method) {
+ if (parameterAnnotations == null) {
+ return null;
+ }
+
+ for (ParameterAnnotationStruct item : parameterAnnotations) {
+ if (item.getMethod().equals(method)) {
+ return item.getAnnotationsList();
+ }
+ }
+
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ public void addContents(DexFile file) {
+ MixedItemSection wordData = file.getWordData();
+
+ if (classAnnotations != null) {
+ classAnnotations = wordData.intern(classAnnotations);
+ }
+
+ if (fieldAnnotations != null) {
+ for (FieldAnnotationStruct item : fieldAnnotations) {
+ item.addContents(file);
+ }
+ }
+
+ if (methodAnnotations != null) {
+ for (MethodAnnotationStruct item : methodAnnotations) {
+ item.addContents(file);
+ }
+ }
+
+ if (parameterAnnotations != null) {
+ for (ParameterAnnotationStruct item : parameterAnnotations) {
+ item.addContents(file);
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toHuman() {
+ throw new RuntimeException("unsupported");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void place0(Section addedTo, int offset) {
+ // We just need to set the write size here.
+
+ int elementCount = listSize(fieldAnnotations)
+ + listSize(methodAnnotations) + listSize(parameterAnnotations);
+ setWriteSize(HEADER_SIZE + (elementCount * ELEMENT_SIZE));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void writeTo0(DexFile file, AnnotatedOutput out) {
+ boolean annotates = out.annotates();
+ int classOff = OffsettedItem.getAbsoluteOffsetOr0(classAnnotations);
+ int fieldsSize = listSize(fieldAnnotations);
+ int methodsSize = listSize(methodAnnotations);
+ int parametersSize = listSize(parameterAnnotations);
+
+ if (annotates) {
+ out.annotate(0, offsetString() + " annotations directory");
+ out.annotate(4, " class_annotations_off: " + Hex.u4(classOff));
+ out.annotate(4, " fields_size: " +
+ Hex.u4(fieldsSize));
+ out.annotate(4, " methods_size: " +
+ Hex.u4(methodsSize));
+ out.annotate(4, " parameters_size: " +
+ Hex.u4(parametersSize));
+ }
+
+ out.writeInt(classOff);
+ out.writeInt(fieldsSize);
+ out.writeInt(methodsSize);
+ out.writeInt(parametersSize);
+
+ if (fieldsSize != 0) {
+ Collections.sort(fieldAnnotations);
+ if (annotates) {
+ out.annotate(0, " fields:");
+ }
+ for (FieldAnnotationStruct item : fieldAnnotations) {
+ item.writeTo(file, out);
+ }
+ }
+
+ if (methodsSize != 0) {
+ Collections.sort(methodAnnotations);
+ if (annotates) {
+ out.annotate(0, " methods:");
+ }
+ for (MethodAnnotationStruct item : methodAnnotations) {
+ item.writeTo(file, out);
+ }
+ }
+
+ if (parametersSize != 0) {
+ Collections.sort(parameterAnnotations);
+ if (annotates) {
+ out.annotate(0, " parameters:");
+ }
+ for (ParameterAnnotationStruct item : parameterAnnotations) {
+ item.writeTo(file, out);
+ }
+ }
+ }
+
+ /**
+ * Gets the list size of the given list, or {@code 0} if given
+ * {@code null}.
+ *
+ * @param list {@code null-ok;} the list in question
+ * @return {@code >= 0;} its size
+ */
+ private static int listSize(ArrayList<?> list) {
+ if (list == null) {
+ return 0;
+ }
+
+ return list.size();
+ }
+
+ /**
+ * Prints out the contents of this instance, in a debugging-friendly
+ * way. This is meant to be called from {@link ClassDefItem#debugPrint}.
+ *
+ * @param out {@code non-null;} where to output to
+ */
+ /*package*/ void debugPrint(PrintWriter out) {
+ if (classAnnotations != null) {
+ out.println(" class annotations: " + classAnnotations);
+ }
+
+ if (fieldAnnotations != null) {
+ out.println(" field annotations:");
+ for (FieldAnnotationStruct item : fieldAnnotations) {
+ out.println(" " + item.toHuman());
+ }
+ }
+
+ if (methodAnnotations != null) {
+ out.println(" method annotations:");
+ for (MethodAnnotationStruct item : methodAnnotations) {
+ out.println(" " + item.toHuman());
+ }
+ }
+
+ if (parameterAnnotations != null) {
+ out.println(" parameter annotations:");
+ for (ParameterAnnotationStruct item : parameterAnnotations) {
+ out.println(" " + item.toHuman());
+ }
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/CatchStructs.java b/dx/src/com/android/dx/dex/file/CatchStructs.java
new file mode 100644
index 0000000..e07ec29
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/CatchStructs.java
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.dex.code.CatchHandlerList;
+import com.android.dx.dex.code.CatchTable;
+import com.android.dx.dex.code.DalvCode;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.ByteArrayAnnotatedOutput;
+import com.android.dx.util.Hex;
+
+import java.io.PrintWriter;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * List of exception handlers (tuples of covered range, catch type,
+ * handler address) for a particular piece of code. Instances of this
+ * class correspond to a {@code try_item[]} and a
+ * {@code catch_handler_item[]}.
+ */
+public final class CatchStructs {
+ /**
+ * the size of a {@code try_item}: a {@code uint}
+ * and two {@code ushort}s
+ */
+ private static final int TRY_ITEM_WRITE_SIZE = 4 + (2 * 2);
+
+ /** {@code non-null;} code that contains the catches */
+ private final DalvCode code;
+
+ /**
+ * {@code null-ok;} the underlying table; set in
+ * {@link #finishProcessingIfNecessary}
+ */
+ private CatchTable table;
+
+ /**
+ * {@code null-ok;} the encoded handler list, if calculated; set in
+ * {@link #encode}
+ */
+ private byte[] encodedHandlers;
+
+ /**
+ * length of the handlers header (encoded size), if known; used for
+ * annotation
+ */
+ private int encodedHandlerHeaderSize;
+
+ /**
+ * {@code null-ok;} map from handler lists to byte offsets, if calculated; set in
+ * {@link #encode}
+ */
+ private TreeMap<CatchHandlerList, Integer> handlerOffsets;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param code {@code non-null;} code that contains the catches
+ */
+ public CatchStructs(DalvCode code) {
+ this.code = code;
+ this.table = null;
+ this.encodedHandlers = null;
+ this.encodedHandlerHeaderSize = 0;
+ this.handlerOffsets = null;
+ }
+
+ /**
+ * Finish processing the catches, if necessary.
+ */
+ private void finishProcessingIfNecessary() {
+ if (table == null) {
+ table = code.getCatches();
+ }
+ }
+
+ /**
+ * Gets the size of the tries list, in entries.
+ *
+ * @return {@code >= 0;} the tries list size
+ */
+ public int triesSize() {
+ finishProcessingIfNecessary();
+ return table.size();
+ }
+
+ /**
+ * Does a human-friendly dump of this instance.
+ *
+ * @param out {@code non-null;} where to dump
+ * @param prefix {@code non-null;} prefix to attach to each line of output
+ */
+ public void debugPrint(PrintWriter out, String prefix) {
+ annotateEntries(prefix, out, null);
+ }
+
+ /**
+ * Encodes the handler lists.
+ *
+ * @param file {@code non-null;} file this instance is part of
+ */
+ public void encode(DexFile file) {
+ finishProcessingIfNecessary();
+
+ TypeIdsSection typeIds = file.getTypeIds();
+ int size = table.size();
+
+ handlerOffsets = new TreeMap<CatchHandlerList, Integer>();
+
+ /*
+ * First add a map entry for each unique list. The tree structure
+ * will ensure they are sorted when we reiterate later.
+ */
+ for (int i = 0; i < size; i++) {
+ handlerOffsets.put(table.get(i).getHandlers(), null);
+ }
+
+ if (handlerOffsets.size() > 65535) {
+ throw new UnsupportedOperationException(
+ "too many catch handlers");
+ }
+
+ ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput();
+
+ // Write out the handlers "header" consisting of its size in entries.
+ encodedHandlerHeaderSize =
+ out.writeUnsignedLeb128(handlerOffsets.size());
+
+ // Now write the lists out in order, noting the offset of each.
+ for (Map.Entry<CatchHandlerList, Integer> mapping :
+ handlerOffsets.entrySet()) {
+ CatchHandlerList list = mapping.getKey();
+ int listSize = list.size();
+ boolean catchesAll = list.catchesAll();
+
+ // Set the offset before we do any writing.
+ mapping.setValue(out.getCursor());
+
+ if (catchesAll) {
+ // A size <= 0 means that the list ends with a catch-all.
+ out.writeSignedLeb128(-(listSize - 1));
+ listSize--;
+ } else {
+ out.writeSignedLeb128(listSize);
+ }
+
+ for (int i = 0; i < listSize; i++) {
+ CatchHandlerList.Entry entry = list.get(i);
+ out.writeUnsignedLeb128(
+ typeIds.indexOf(entry.getExceptionType()));
+ out.writeUnsignedLeb128(entry.getHandler());
+ }
+
+ if (catchesAll) {
+ out.writeUnsignedLeb128(list.get(listSize).getHandler());
+ }
+ }
+
+ encodedHandlers = out.toByteArray();
+ }
+
+ /**
+ * Gets the write size of this instance, in bytes.
+ *
+ * @return {@code >= 0;} the write size
+ */
+ public int writeSize() {
+ return (triesSize() * TRY_ITEM_WRITE_SIZE) +
+ + encodedHandlers.length;
+ }
+
+ /**
+ * Writes this instance to the given stream.
+ *
+ * @param file {@code non-null;} file this instance is part of
+ * @param out {@code non-null;} where to write to
+ */
+ public void writeTo(DexFile file, AnnotatedOutput out) {
+ finishProcessingIfNecessary();
+
+ if (out.annotates()) {
+ annotateEntries(" ", null, out);
+ }
+
+ int tableSize = table.size();
+ for (int i = 0; i < tableSize; i++) {
+ CatchTable.Entry one = table.get(i);
+ int start = one.getStart();
+ int end = one.getEnd();
+ int insnCount = end - start;
+
+ if (insnCount >= 65536) {
+ throw new UnsupportedOperationException(
+ "bogus exception range: " + Hex.u4(start) + ".." +
+ Hex.u4(end));
+ }
+
+ out.writeInt(start);
+ out.writeShort(insnCount);
+ out.writeShort(handlerOffsets.get(one.getHandlers()));
+ }
+
+ out.write(encodedHandlers);
+ }
+
+ /**
+ * Helper method to annotate or simply print the exception handlers.
+ * Only one of {@code printTo} or {@code annotateTo} should
+ * be non-null.
+ *
+ * @param prefix {@code non-null;} prefix for each line
+ * @param printTo {@code null-ok;} where to print to
+ * @param annotateTo {@code null-ok;} where to consume bytes and annotate to
+ */
+ private void annotateEntries(String prefix, PrintWriter printTo,
+ AnnotatedOutput annotateTo) {
+ finishProcessingIfNecessary();
+
+ boolean consume = (annotateTo != null);
+ int amt1 = consume ? 6 : 0;
+ int amt2 = consume ? 2 : 0;
+ int size = table.size();
+ String subPrefix = prefix + " ";
+
+ if (consume) {
+ annotateTo.annotate(0, prefix + "tries:");
+ } else {
+ printTo.println(prefix + "tries:");
+ }
+
+ for (int i = 0; i < size; i++) {
+ CatchTable.Entry entry = table.get(i);
+ CatchHandlerList handlers = entry.getHandlers();
+ String s1 = subPrefix + "try " + Hex.u2or4(entry.getStart())
+ + ".." + Hex.u2or4(entry.getEnd());
+ String s2 = handlers.toHuman(subPrefix, "");
+
+ if (consume) {
+ annotateTo.annotate(amt1, s1);
+ annotateTo.annotate(amt2, s2);
+ } else {
+ printTo.println(s1);
+ printTo.println(s2);
+ }
+ }
+
+ if (! consume) {
+ // Only emit the handler lists if we are consuming bytes.
+ return;
+ }
+
+ annotateTo.annotate(0, prefix + "handlers:");
+ annotateTo.annotate(encodedHandlerHeaderSize,
+ subPrefix + "size: " + Hex.u2(handlerOffsets.size()));
+
+ int lastOffset = 0;
+ CatchHandlerList lastList = null;
+
+ for (Map.Entry<CatchHandlerList, Integer> mapping :
+ handlerOffsets.entrySet()) {
+ CatchHandlerList list = mapping.getKey();
+ int offset = mapping.getValue();
+
+ if (lastList != null) {
+ annotateAndConsumeHandlers(lastList, lastOffset,
+ offset - lastOffset, subPrefix, printTo, annotateTo);
+ }
+
+ lastList = list;
+ lastOffset = offset;
+ }
+
+ annotateAndConsumeHandlers(lastList, lastOffset,
+ encodedHandlers.length - lastOffset,
+ subPrefix, printTo, annotateTo);
+ }
+
+ /**
+ * Helper for {@link #annotateEntries} to annotate a catch handler list
+ * while consuming it.
+ *
+ * @param handlers {@code non-null;} handlers to annotate
+ * @param offset {@code >= 0;} the offset of this handler
+ * @param size {@code >= 1;} the number of bytes the handlers consume
+ * @param prefix {@code non-null;} prefix for each line
+ * @param printTo {@code null-ok;} where to print to
+ * @param annotateTo {@code non-null;} where to annotate to
+ */
+ private static void annotateAndConsumeHandlers(CatchHandlerList handlers,
+ int offset, int size, String prefix, PrintWriter printTo,
+ AnnotatedOutput annotateTo) {
+ String s = handlers.toHuman(prefix, Hex.u2(offset) + ": ");
+
+ if (printTo != null) {
+ printTo.println(s);
+ }
+
+ annotateTo.annotate(size, s);
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/ClassDataItem.java b/dx/src/com/android/dx/dex/file/ClassDataItem.java
new file mode 100644
index 0000000..275ae99
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/ClassDataItem.java
@@ -0,0 +1,429 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstArray;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.Zeroes;
+import com.android.dx.util.ByteArrayAnnotatedOutput;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+import com.android.dx.util.Writers;
+
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.HashMap;
+
+/**
+ * Representation of all the parts of a Dalvik class that are generally
+ * "inflated" into an in-memory representation at runtime. Instances of
+ * this class are represented in a compact streamable form in a
+ * {@code dex} file, as opposed to a random-access form.
+ */
+public final class ClassDataItem extends OffsettedItem {
+ /** {@code non-null;} what class this data is for, just for listing generation */
+ private final CstType thisClass;
+
+ /** {@code non-null;} list of static fields */
+ private final ArrayList<EncodedField> staticFields;
+
+ /** {@code non-null;} list of initial values for static fields */
+ private final HashMap<EncodedField, Constant> staticValues;
+
+ /** {@code non-null;} list of instance fields */
+ private final ArrayList<EncodedField> instanceFields;
+
+ /** {@code non-null;} list of direct methods */
+ private final ArrayList<EncodedMethod> directMethods;
+
+ /** {@code non-null;} list of virtual methods */
+ private final ArrayList<EncodedMethod> virtualMethods;
+
+ /** {@code null-ok;} static initializer list; set in {@link #addContents} */
+ private CstArray staticValuesConstant;
+
+ /**
+ * {@code null-ok;} encoded form, ready for writing to a file; set during
+ * {@link #place0}
+ */
+ private byte[] encodedForm;
+
+ /**
+ * Constructs an instance. Its sets of members are initially
+ * empty.
+ *
+ * @param thisClass {@code non-null;} what class this data is for, just
+ * for listing generation
+ */
+ public ClassDataItem(CstType thisClass) {
+ super(1, -1);
+
+ if (thisClass == null) {
+ throw new NullPointerException("thisClass == null");
+ }
+
+ this.thisClass = thisClass;
+ this.staticFields = new ArrayList<EncodedField>(20);
+ this.staticValues = new HashMap<EncodedField, Constant>(40);
+ this.instanceFields = new ArrayList<EncodedField>(20);
+ this.directMethods = new ArrayList<EncodedMethod>(20);
+ this.virtualMethods = new ArrayList<EncodedMethod>(20);
+ this.staticValuesConstant = null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ItemType itemType() {
+ return ItemType.TYPE_CLASS_DATA_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toHuman() {
+ return toString();
+ }
+
+ /**
+ * Returns whether this instance is empty.
+ *
+ * @return {@code true} if this instance is empty or
+ * {@code false} if at least one element has been added to it
+ */
+ public boolean isEmpty() {
+ return staticFields.isEmpty() && instanceFields.isEmpty()
+ && directMethods.isEmpty() && virtualMethods.isEmpty();
+ }
+
+ /**
+ * Adds a static field.
+ *
+ * @param field {@code non-null;} the field to add
+ * @param value {@code null-ok;} initial value for the field, if any
+ */
+ public void addStaticField(EncodedField field, Constant value) {
+ if (field == null) {
+ throw new NullPointerException("field == null");
+ }
+
+ if (staticValuesConstant != null) {
+ throw new UnsupportedOperationException(
+ "static fields already sorted");
+ }
+
+ staticFields.add(field);
+ staticValues.put(field, value);
+ }
+
+ /**
+ * Adds an instance field.
+ *
+ * @param field {@code non-null;} the field to add
+ */
+ public void addInstanceField(EncodedField field) {
+ if (field == null) {
+ throw new NullPointerException("field == null");
+ }
+
+ instanceFields.add(field);
+ }
+
+ /**
+ * Adds a direct ({@code static} and/or {@code private}) method.
+ *
+ * @param method {@code non-null;} the method to add
+ */
+ public void addDirectMethod(EncodedMethod method) {
+ if (method == null) {
+ throw new NullPointerException("method == null");
+ }
+
+ directMethods.add(method);
+ }
+
+ /**
+ * Adds a virtual method.
+ *
+ * @param method {@code non-null;} the method to add
+ */
+ public void addVirtualMethod(EncodedMethod method) {
+ if (method == null) {
+ throw new NullPointerException("method == null");
+ }
+
+ virtualMethods.add(method);
+ }
+
+ /**
+ * Gets all the methods in this class. The returned list is not linked
+ * in any way to the underlying lists contained in this instance, but
+ * the objects contained in the list are shared.
+ *
+ * @return {@code non-null;} list of all methods
+ */
+ public ArrayList<EncodedMethod> getMethods() {
+ int sz = directMethods.size() + virtualMethods.size();
+ ArrayList<EncodedMethod> result = new ArrayList<EncodedMethod>(sz);
+
+ result.addAll(directMethods);
+ result.addAll(virtualMethods);
+
+ return result;
+ }
+
+
+ /**
+ * Prints out the contents of this instance, in a debugging-friendly
+ * way.
+ *
+ * @param out {@code non-null;} where to output to
+ * @param verbose whether to be verbose with the output
+ */
+ public void debugPrint(Writer out, boolean verbose) {
+ PrintWriter pw = Writers.printWriterFor(out);
+
+ int sz = staticFields.size();
+ for (int i = 0; i < sz; i++) {
+ pw.println(" sfields[" + i + "]: " + staticFields.get(i));
+ }
+
+ sz = instanceFields.size();
+ for (int i = 0; i < sz; i++) {
+ pw.println(" ifields[" + i + "]: " + instanceFields.get(i));
+ }
+
+ sz = directMethods.size();
+ for (int i = 0; i < sz; i++) {
+ pw.println(" dmeths[" + i + "]:");
+ directMethods.get(i).debugPrint(pw, verbose);
+ }
+
+ sz = virtualMethods.size();
+ for (int i = 0; i < sz; i++) {
+ pw.println(" vmeths[" + i + "]:");
+ virtualMethods.get(i).debugPrint(pw, verbose);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addContents(DexFile file) {
+ if (!staticFields.isEmpty()) {
+ getStaticValuesConstant(); // Force the fields to be sorted.
+ for (EncodedField field : staticFields) {
+ field.addContents(file);
+ }
+ }
+
+ if (!instanceFields.isEmpty()) {
+ Collections.sort(instanceFields);
+ for (EncodedField field : instanceFields) {
+ field.addContents(file);
+ }
+ }
+
+ if (!directMethods.isEmpty()) {
+ Collections.sort(directMethods);
+ for (EncodedMethod method : directMethods) {
+ method.addContents(file);
+ }
+ }
+
+ if (!virtualMethods.isEmpty()) {
+ Collections.sort(virtualMethods);
+ for (EncodedMethod method : virtualMethods) {
+ method.addContents(file);
+ }
+ }
+ }
+
+ /**
+ * Gets a {@link CstArray} corresponding to {@link #staticValues} if
+ * it contains any non-zero non-{@code null} values.
+ *
+ * @return {@code null-ok;} the corresponding constant or {@code null} if
+ * there are no values to encode
+ */
+ public CstArray getStaticValuesConstant() {
+ if ((staticValuesConstant == null) && (staticFields.size() != 0)) {
+ staticValuesConstant = makeStaticValuesConstant();
+ }
+
+ return staticValuesConstant;
+ }
+
+ /**
+ * Gets a {@link CstArray} corresponding to {@link #staticValues} if
+ * it contains any non-zero non-{@code null} values.
+ *
+ * @return {@code null-ok;} the corresponding constant or {@code null} if
+ * there are no values to encode
+ */
+ private CstArray makeStaticValuesConstant() {
+ // First sort the statics into their final order.
+ Collections.sort(staticFields);
+
+ /*
+ * Get the size of staticValues minus any trailing zeros/nulls (both
+ * nulls per se as well as instances of CstKnownNull).
+ */
+
+ int size = staticFields.size();
+ while (size > 0) {
+ EncodedField field = staticFields.get(size - 1);
+ Constant cst = staticValues.get(field);
+ if (cst instanceof CstLiteralBits) {
+ // Note: CstKnownNull extends CstLiteralBits.
+ if (((CstLiteralBits) cst).getLongBits() != 0) {
+ break;
+ }
+ } else if (cst != null) {
+ break;
+ }
+ size--;
+ }
+
+ if (size == 0) {
+ return null;
+ }
+
+ // There is something worth encoding, so build up a result.
+
+ CstArray.List list = new CstArray.List(size);
+ for (int i = 0; i < size; i++) {
+ EncodedField field = staticFields.get(i);
+ Constant cst = staticValues.get(field);
+ if (cst == null) {
+ cst = Zeroes.zeroFor(field.getRef().getType());
+ }
+ list.set(i, cst);
+ }
+ list.setImmutable();
+
+ return new CstArray(list);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void place0(Section addedTo, int offset) {
+ // Encode the data and note the size.
+
+ ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput();
+
+ encodeOutput(addedTo.getFile(), out);
+ encodedForm = out.toByteArray();
+ setWriteSize(encodedForm.length);
+ }
+
+ /**
+ * Writes out the encoded form of this instance.
+ *
+ * @param file {@code non-null;} file this instance is part of
+ * @param out {@code non-null;} where to write to
+ */
+ private void encodeOutput(DexFile file, AnnotatedOutput out) {
+ boolean annotates = out.annotates();
+
+ if (annotates) {
+ out.annotate(0, offsetString() + " class data for " +
+ thisClass.toHuman());
+ }
+
+ encodeSize(file, out, "static_fields", staticFields.size());
+ encodeSize(file, out, "instance_fields", instanceFields.size());
+ encodeSize(file, out, "direct_methods", directMethods.size());
+ encodeSize(file, out, "virtual_methods", virtualMethods.size());
+
+ encodeList(file, out, "static_fields", staticFields);
+ encodeList(file, out, "instance_fields", instanceFields);
+ encodeList(file, out, "direct_methods", directMethods);
+ encodeList(file, out, "virtual_methods", virtualMethods);
+
+ if (annotates) {
+ out.endAnnotation();
+ }
+ }
+
+ /**
+ * Helper for {@link #encodeOutput}, which writes out the given
+ * size value, annotating it as well (if annotations are enabled).
+ *
+ * @param file {@code non-null;} file this instance is part of
+ * @param out {@code non-null;} where to write to
+ * @param label {@code non-null;} the label for the purposes of annotation
+ * @param size {@code >= 0;} the size to write
+ */
+ private static void encodeSize(DexFile file, AnnotatedOutput out,
+ String label, int size) {
+ if (out.annotates()) {
+ out.annotate(String.format(" %-21s %08x", label + "_size:",
+ size));
+ }
+
+ out.writeUnsignedLeb128(size);
+ }
+
+ /**
+ * Helper for {@link #encodeOutput}, which writes out the given
+ * list. It also annotates the items (if any and if annotations
+ * are enabled).
+ *
+ * @param file {@code non-null;} file this instance is part of
+ * @param out {@code non-null;} where to write to
+ * @param label {@code non-null;} the label for the purposes of annotation
+ * @param list {@code non-null;} the list in question
+ */
+ private static void encodeList(DexFile file, AnnotatedOutput out,
+ String label, ArrayList<? extends EncodedMember> list) {
+ int size = list.size();
+ int lastIndex = 0;
+
+ if (size == 0) {
+ return;
+ }
+
+ if (out.annotates()) {
+ out.annotate(0, " " + label + ":");
+ }
+
+ for (int i = 0; i < size; i++) {
+ lastIndex = list.get(i).encode(file, out, lastIndex, i);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo0(DexFile file, AnnotatedOutput out) {
+ boolean annotates = out.annotates();
+
+ if (annotates) {
+ /*
+ * The output is to be annotated, so redo the work previously
+ * done by place0(), except this time annotations will actually
+ * get emitted.
+ */
+ encodeOutput(file, out);
+ } else {
+ out.write(encodedForm);
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/ClassDefItem.java b/dx/src/com/android/dx/dex/file/ClassDefItem.java
new file mode 100644
index 0000000..4d1719b
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/ClassDefItem.java
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.annotation.Annotations;
+import com.android.dx.rop.annotation.AnnotationsList;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstArray;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+import com.android.dx.util.Writers;
+
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.TreeSet;
+
+/**
+ * Representation of a Dalvik class, which is basically a set of
+ * members (fields or methods) along with a few more pieces of
+ * information.
+ */
+public final class ClassDefItem extends IndexedItem {
+ /** size of instances when written out to a file, in bytes */
+ public static final int WRITE_SIZE = 32;
+
+ /** {@code non-null;} type constant for this class */
+ private final CstType thisClass;
+
+ /** access flags */
+ private final int accessFlags;
+
+ /**
+ * {@code null-ok;} superclass or {@code null} if this class is a/the
+ * root class
+ */
+ private final CstType superclass;
+
+ /** {@code null-ok;} list of implemented interfaces */
+ private TypeListItem interfaces;
+
+ /** {@code null-ok;} source file name or {@code null} if unknown */
+ private final CstUtf8 sourceFile;
+
+ /** {@code non-null;} associated class data object */
+ private final ClassDataItem classData;
+
+ /**
+ * {@code null-ok;} item wrapper for the static values, initialized
+ * in {@link #addContents}
+ */
+ private EncodedArrayItem staticValuesItem;
+
+ /** {@code non-null;} annotations directory */
+ private AnnotationsDirectoryItem annotationsDirectory;
+
+ /**
+ * Constructs an instance. Its sets of members and annotations are
+ * initially empty.
+ *
+ * @param thisClass {@code non-null;} type constant for this class
+ * @param accessFlags access flags
+ * @param superclass {@code null-ok;} superclass or {@code null} if
+ * this class is a/the root class
+ * @param interfaces {@code non-null;} list of implemented interfaces
+ * @param sourceFile {@code null-ok;} source file name or
+ * {@code null} if unknown
+ */
+ public ClassDefItem(CstType thisClass, int accessFlags,
+ CstType superclass, TypeList interfaces, CstUtf8 sourceFile) {
+ if (thisClass == null) {
+ throw new NullPointerException("thisClass == null");
+ }
+
+ /*
+ * TODO: Maybe check accessFlags and superclass, at
+ * least for easily-checked stuff?
+ */
+
+ if (interfaces == null) {
+ throw new NullPointerException("interfaces == null");
+ }
+
+ this.thisClass = thisClass;
+ this.accessFlags = accessFlags;
+ this.superclass = superclass;
+ this.interfaces =
+ (interfaces.size() == 0) ? null : new TypeListItem(interfaces);
+ this.sourceFile = sourceFile;
+ this.classData = new ClassDataItem(thisClass);
+ this.staticValuesItem = null;
+ this.annotationsDirectory = new AnnotationsDirectoryItem();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ItemType itemType() {
+ return ItemType.TYPE_CLASS_DEF_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int writeSize() {
+ return WRITE_SIZE;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addContents(DexFile file) {
+ TypeIdsSection typeIds = file.getTypeIds();
+ MixedItemSection byteData = file.getByteData();
+ MixedItemSection wordData = file.getWordData();
+ MixedItemSection typeLists = file.getTypeLists();
+ StringIdsSection stringIds = file.getStringIds();
+
+ typeIds.intern(thisClass);
+
+ if (!classData.isEmpty()) {
+ MixedItemSection classDataSection = file.getClassData();
+ classDataSection.add(classData);
+
+ CstArray staticValues = classData.getStaticValuesConstant();
+ if (staticValues != null) {
+ staticValuesItem =
+ byteData.intern(new EncodedArrayItem(staticValues));
+ }
+ }
+
+ if (superclass != null) {
+ typeIds.intern(superclass);
+ }
+
+ if (interfaces != null) {
+ interfaces = typeLists.intern(interfaces);
+ }
+
+ if (sourceFile != null) {
+ stringIds.intern(sourceFile);
+ }
+
+ if (! annotationsDirectory.isEmpty()) {
+ if (annotationsDirectory.isInternable()) {
+ annotationsDirectory = wordData.intern(annotationsDirectory);
+ } else {
+ wordData.add(annotationsDirectory);
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(DexFile file, AnnotatedOutput out) {
+ boolean annotates = out.annotates();
+ TypeIdsSection typeIds = file.getTypeIds();
+ int classIdx = typeIds.indexOf(thisClass);
+ int superIdx = (superclass == null) ? -1 :
+ typeIds.indexOf(superclass);
+ int interOff = OffsettedItem.getAbsoluteOffsetOr0(interfaces);
+ int annoOff = annotationsDirectory.isEmpty() ? 0 :
+ annotationsDirectory.getAbsoluteOffset();
+ int sourceFileIdx = (sourceFile == null) ? -1 :
+ file.getStringIds().indexOf(sourceFile);
+ int dataOff = classData.isEmpty()? 0 : classData.getAbsoluteOffset();
+ int staticValuesOff =
+ OffsettedItem.getAbsoluteOffsetOr0(staticValuesItem);
+
+ if (annotates) {
+ out.annotate(0, indexString() + ' ' + thisClass.toHuman());
+ out.annotate(4, " class_idx: " + Hex.u4(classIdx));
+ out.annotate(4, " access_flags: " +
+ AccessFlags.classString(accessFlags));
+ out.annotate(4, " superclass_idx: " + Hex.u4(superIdx) +
+ " // " + ((superclass == null) ? "<none>" :
+ superclass.toHuman()));
+ out.annotate(4, " interfaces_off: " + Hex.u4(interOff));
+ if (interOff != 0) {
+ TypeList list = interfaces.getList();
+ int sz = list.size();
+ for (int i = 0; i < sz; i++) {
+ out.annotate(0, " " + list.getType(i).toHuman());
+ }
+ }
+ out.annotate(4, " source_file_idx: " + Hex.u4(sourceFileIdx) +
+ " // " + ((sourceFile == null) ? "<none>" :
+ sourceFile.toHuman()));
+ out.annotate(4, " annotations_off: " + Hex.u4(annoOff));
+ out.annotate(4, " class_data_off: " + Hex.u4(dataOff));
+ out.annotate(4, " static_values_off: " +
+ Hex.u4(staticValuesOff));
+ }
+
+ out.writeInt(classIdx);
+ out.writeInt(accessFlags);
+ out.writeInt(superIdx);
+ out.writeInt(interOff);
+ out.writeInt(sourceFileIdx);
+ out.writeInt(annoOff);
+ out.writeInt(dataOff);
+ out.writeInt(staticValuesOff);
+ }
+
+ /**
+ * Gets the constant corresponding to this class.
+ *
+ * @return {@code non-null;} the constant
+ */
+ public CstType getThisClass() {
+ return thisClass;
+ }
+
+ /**
+ * Gets the access flags.
+ *
+ * @return the access flags
+ */
+ public int getAccessFlags() {
+ return accessFlags;
+ }
+
+ /**
+ * Gets the superclass.
+ *
+ * @return {@code null-ok;} the superclass or {@code null} if
+ * this class is a/the root class
+ */
+ public CstType getSuperclass() {
+ return superclass;
+ }
+
+ /**
+ * Gets the list of interfaces implemented.
+ *
+ * @return {@code non-null;} the interfaces list
+ */
+ public TypeList getInterfaces() {
+ if (interfaces == null) {
+ return StdTypeList.EMPTY;
+ }
+
+ return interfaces.getList();
+ }
+
+ /**
+ * Gets the source file name.
+ *
+ * @return {@code null-ok;} the source file name or {@code null} if unknown
+ */
+ public CstUtf8 getSourceFile() {
+ return sourceFile;
+ }
+
+ /**
+ * Adds a static field.
+ *
+ * @param field {@code non-null;} the field to add
+ * @param value {@code null-ok;} initial value for the field, if any
+ */
+ public void addStaticField(EncodedField field, Constant value) {
+ classData.addStaticField(field, value);
+ }
+
+ /**
+ * Adds an instance field.
+ *
+ * @param field {@code non-null;} the field to add
+ */
+ public void addInstanceField(EncodedField field) {
+ classData.addInstanceField(field);
+ }
+
+ /**
+ * Adds a direct ({@code static} and/or {@code private}) method.
+ *
+ * @param method {@code non-null;} the method to add
+ */
+ public void addDirectMethod(EncodedMethod method) {
+ classData.addDirectMethod(method);
+ }
+
+ /**
+ * Adds a virtual method.
+ *
+ * @param method {@code non-null;} the method to add
+ */
+ public void addVirtualMethod(EncodedMethod method) {
+ classData.addVirtualMethod(method);
+ }
+
+ /**
+ * Gets all the methods in this class. The returned list is not linked
+ * in any way to the underlying lists contained in this instance, but
+ * the objects contained in the list are shared.
+ *
+ * @return {@code non-null;} list of all methods
+ */
+ public ArrayList<EncodedMethod> getMethods() {
+ return classData.getMethods();
+ }
+
+ /**
+ * Sets the direct annotations on this class. These are annotations
+ * made on the class, per se, as opposed to on one of its members.
+ * It is only valid to call this method at most once per instance.
+ *
+ * @param annotations {@code non-null;} annotations to set for this class
+ */
+ public void setClassAnnotations(Annotations annotations) {
+ annotationsDirectory.setClassAnnotations(annotations);
+ }
+
+ /**
+ * Adds a field annotations item to this class.
+ *
+ * @param field {@code non-null;} field in question
+ * @param annotations {@code non-null;} associated annotations to add
+ */
+ public void addFieldAnnotations(CstFieldRef field,
+ Annotations annotations) {
+ annotationsDirectory.addFieldAnnotations(field, annotations);
+ }
+
+ /**
+ * Adds a method annotations item to this class.
+ *
+ * @param method {@code non-null;} method in question
+ * @param annotations {@code non-null;} associated annotations to add
+ */
+ public void addMethodAnnotations(CstMethodRef method,
+ Annotations annotations) {
+ annotationsDirectory.addMethodAnnotations(method, annotations);
+ }
+
+ /**
+ * Adds a parameter annotations item to this class.
+ *
+ * @param method {@code non-null;} method in question
+ * @param list {@code non-null;} associated list of annotation sets to add
+ */
+ public void addParameterAnnotations(CstMethodRef method,
+ AnnotationsList list) {
+ annotationsDirectory.addParameterAnnotations(method, list);
+ }
+
+ /**
+ * Gets the method annotations for a given method, if any. This is
+ * meant for use by debugging / dumping code.
+ *
+ * @param method {@code non-null;} the method
+ * @return {@code null-ok;} the method annotations, if any
+ */
+ public Annotations getMethodAnnotations(CstMethodRef method) {
+ return annotationsDirectory.getMethodAnnotations(method);
+ }
+
+ /**
+ * Gets the parameter annotations for a given method, if any. This is
+ * meant for use by debugging / dumping code.
+ *
+ * @param method {@code non-null;} the method
+ * @return {@code null-ok;} the parameter annotations, if any
+ */
+ public AnnotationsList getParameterAnnotations(CstMethodRef method) {
+ return annotationsDirectory.getParameterAnnotations(method);
+ }
+
+ /**
+ * Prints out the contents of this instance, in a debugging-friendly
+ * way.
+ *
+ * @param out {@code non-null;} where to output to
+ * @param verbose whether to be verbose with the output
+ */
+ public void debugPrint(Writer out, boolean verbose) {
+ PrintWriter pw = Writers.printWriterFor(out);
+
+ pw.println(getClass().getName() + " {");
+ pw.println(" accessFlags: " + Hex.u2(accessFlags));
+ pw.println(" superclass: " + superclass);
+ pw.println(" interfaces: " +
+ ((interfaces == null) ? "<none>" : interfaces));
+ pw.println(" sourceFile: " +
+ ((sourceFile == null) ? "<none>" : sourceFile.toQuoted()));
+
+ classData.debugPrint(out, verbose);
+ annotationsDirectory.debugPrint(pw);
+
+ pw.println("}");
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/ClassDefsSection.java b/dx/src/com/android/dx/dex/file/ClassDefsSection.java
new file mode 100644
index 0000000..1ca391f
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/ClassDefsSection.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.TreeMap;
+
+/**
+ * Class definitions list section of a {@code .dex} file.
+ */
+public final class ClassDefsSection extends UniformItemSection {
+ /**
+ * {@code non-null;} map from type constants for classes to {@link
+ * ClassDefItem} instances that define those classes
+ */
+ private final TreeMap<Type, ClassDefItem> classDefs;
+
+ /** {@code null-ok;} ordered list of classes; set in {@link #orderItems} */
+ private ArrayList<ClassDefItem> orderedDefs;
+
+ /**
+ * Constructs an instance. The file offset is initially unknown.
+ *
+ * @param file {@code non-null;} file that this instance is part of
+ */
+ public ClassDefsSection(DexFile file) {
+ super("class_defs", file, 4);
+
+ classDefs = new TreeMap<Type, ClassDefItem>();
+ orderedDefs = null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Collection<? extends Item> items() {
+ if (orderedDefs != null) {
+ return orderedDefs;
+ }
+
+ return classDefs.values();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public IndexedItem get(Constant cst) {
+ if (cst == null) {
+ throw new NullPointerException("cst == null");
+ }
+
+ throwIfNotPrepared();
+
+ Type type = ((CstType) cst).getClassType();
+ IndexedItem result = classDefs.get(type);
+
+ if (result == null) {
+ throw new IllegalArgumentException("not found");
+ }
+
+ return result;
+ }
+
+ /**
+ * Writes the portion of the file header that refers to this instance.
+ *
+ * @param out {@code non-null;} where to write
+ */
+ public void writeHeaderPart(AnnotatedOutput out) {
+ throwIfNotPrepared();
+
+ int sz = classDefs.size();
+ int offset = (sz == 0) ? 0 : getFileOffset();
+
+ if (out.annotates()) {
+ out.annotate(4, "class_defs_size: " + Hex.u4(sz));
+ out.annotate(4, "class_defs_off: " + Hex.u4(offset));
+ }
+
+ out.writeInt(sz);
+ out.writeInt(offset);
+ }
+
+ /**
+ * Adds an element to this instance. It is illegal to attempt to add more
+ * than one class with the same name.
+ *
+ * @param clazz {@code non-null;} the class def to add
+ */
+ public void add(ClassDefItem clazz) {
+ Type type;
+
+ try {
+ type = clazz.getThisClass().getClassType();
+ } catch (NullPointerException ex) {
+ // Elucidate the exception.
+ throw new NullPointerException("clazz == null");
+ }
+
+ throwIfPrepared();
+
+ if (classDefs.get(type) != null) {
+ throw new IllegalArgumentException("already added: " + type);
+ }
+
+ classDefs.put(type, clazz);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void orderItems() {
+ int sz = classDefs.size();
+ int idx = 0;
+
+ orderedDefs = new ArrayList<ClassDefItem>(sz);
+
+ /*
+ * Iterate over all the classes, recursively assigning an
+ * index to each, implicitly skipping the ones that have
+ * already been assigned by the time this (top-level)
+ * iteration reaches them.
+ */
+ for (Type type : classDefs.keySet()) {
+ idx = orderItems0(type, idx, sz - idx);
+ }
+ }
+
+ /**
+ * Helper for {@link #orderItems}, which recursively assigns indices
+ * to classes.
+ *
+ * @param type {@code null-ok;} type ref to assign, if any
+ * @param idx {@code >= 0;} the next index to assign
+ * @param maxDepth maximum recursion depth; if negative, this will
+ * throw an exception indicating class definition circularity
+ * @return {@code >= 0;} the next index to assign
+ */
+ private int orderItems0(Type type, int idx, int maxDepth) {
+ ClassDefItem c = classDefs.get(type);
+
+ if ((c == null) || (c.hasIndex())) {
+ return idx;
+ }
+
+ if (maxDepth < 0) {
+ throw new RuntimeException("class circularity with " + type);
+ }
+
+ maxDepth--;
+
+ CstType superclassCst = c.getSuperclass();
+ if (superclassCst != null) {
+ Type superclass = superclassCst.getClassType();
+ idx = orderItems0(superclass, idx, maxDepth);
+ }
+
+ TypeList interfaces = c.getInterfaces();
+ int sz = interfaces.size();
+ for (int i = 0; i < sz; i++) {
+ idx = orderItems0(interfaces.getType(i), idx, maxDepth);
+ }
+
+ c.setIndex(idx);
+ orderedDefs.add(c);
+ return idx + 1;
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/CodeItem.java b/dx/src/com/android/dx/dex/file/CodeItem.java
new file mode 100644
index 0000000..a47f68a
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/CodeItem.java
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.dex.code.CstInsn;
+import com.android.dx.dex.code.CatchTable;
+import com.android.dx.dex.code.DalvCode;
+import com.android.dx.dex.code.DalvInsn;
+import com.android.dx.dex.code.DalvInsnList;
+import com.android.dx.dex.code.LocalList;
+import com.android.dx.dex.code.PositionList;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstMemberRef;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.ExceptionWithContext;
+import com.android.dx.util.Hex;
+
+import java.io.PrintWriter;
+import java.util.HashSet;
+
+/**
+ * Representation of all the parts needed for concrete methods in a
+ * {@code dex} file.
+ */
+public final class CodeItem extends OffsettedItem {
+ /** file alignment of this class, in bytes */
+ private static final int ALIGNMENT = 4;
+
+ /** write size of the header of this class, in bytes */
+ private static final int HEADER_SIZE = 16;
+
+ /** {@code non-null;} method that this code implements */
+ private final CstMethodRef ref;
+
+ /** {@code non-null;} the bytecode instructions and associated data */
+ private final DalvCode code;
+
+ /** {@code null-ok;} the catches, if needed; set in {@link #addContents} */
+ private CatchStructs catches;
+
+ /** whether this instance is for a {@code static} method */
+ private final boolean isStatic;
+
+ /**
+ * {@code non-null;} list of possibly-thrown exceptions; just used in
+ * generating debugging output (listings)
+ */
+ private final TypeList throwsList;
+
+ /**
+ * {@code null-ok;} the debug info or {@code null} if there is none;
+ * set in {@link #addContents}
+ */
+ private DebugInfoItem debugInfo;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param ref {@code non-null;} method that this code implements
+ * @param code {@code non-null;} the underlying code
+ * @param isStatic whether this instance is for a {@code static}
+ * method
+ * @param throwsList {@code non-null;} list of possibly-thrown exceptions,
+ * just used in generating debugging output (listings)
+ */
+ public CodeItem(CstMethodRef ref, DalvCode code, boolean isStatic,
+ TypeList throwsList) {
+ super(ALIGNMENT, -1);
+
+ if (ref == null) {
+ throw new NullPointerException("ref == null");
+ }
+
+ if (code == null) {
+ throw new NullPointerException("code == null");
+ }
+
+ if (throwsList == null) {
+ throw new NullPointerException("throwsList == null");
+ }
+
+ this.ref = ref;
+ this.code = code;
+ this.isStatic = isStatic;
+ this.throwsList = throwsList;
+ this.catches = null;
+ this.debugInfo = null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ItemType itemType() {
+ return ItemType.TYPE_CODE_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ public void addContents(DexFile file) {
+ MixedItemSection byteData = file.getByteData();
+ TypeIdsSection typeIds = file.getTypeIds();
+
+ if (code.hasPositions() || code.hasLocals()) {
+ debugInfo = new DebugInfoItem(code, isStatic, ref);
+ byteData.add(debugInfo);
+ }
+
+ if (code.hasAnyCatches()) {
+ for (Type type : code.getCatchTypes()) {
+ typeIds.intern(type);
+ }
+ catches = new CatchStructs(code);
+ }
+
+ for (Constant c : code.getInsnConstants()) {
+ file.internIfAppropriate(c);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return "CodeItem{" + toHuman() + "}";
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toHuman() {
+ return ref.toHuman();
+ }
+
+ /**
+ * Gets the reference to the method this instance implements.
+ *
+ * @return {@code non-null;} the method reference
+ */
+ public CstMethodRef getRef() {
+ return ref;
+ }
+
+ /**
+ * Does a human-friendly dump of this instance.
+ *
+ * @param out {@code non-null;} where to dump
+ * @param prefix {@code non-null;} per-line prefix to use
+ * @param verbose whether to be verbose with the output
+ */
+ public void debugPrint(PrintWriter out, String prefix, boolean verbose) {
+ out.println(ref.toHuman() + ":");
+
+ DalvInsnList insns = code.getInsns();
+ out.println("regs: " + Hex.u2(getRegistersSize()) +
+ "; ins: " + Hex.u2(getInsSize()) + "; outs: " +
+ Hex.u2(getOutsSize()));
+
+ insns.debugPrint(out, prefix, verbose);
+
+ String prefix2 = prefix + " ";
+
+ if (catches != null) {
+ out.print(prefix);
+ out.println("catches");
+ catches.debugPrint(out, prefix2);
+ }
+
+ if (debugInfo != null) {
+ out.print(prefix);
+ out.println("debug info");
+ debugInfo.debugPrint(out, prefix2);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void place0(Section addedTo, int offset) {
+ final DexFile file = addedTo.getFile();
+ int catchesSize;
+
+ /*
+ * In order to get the catches and insns, all the code's
+ * constants need to be assigned indices.
+ */
+ code.assignIndices(new DalvCode.AssignIndicesCallback() {
+ public int getIndex(Constant cst) {
+ IndexedItem item = file.findItemOrNull(cst);
+ if (item == null) {
+ return -1;
+ }
+ return item.getIndex();
+ }
+ });
+
+ if (catches != null) {
+ catches.encode(file);
+ catchesSize = catches.writeSize();
+ } else {
+ catchesSize = 0;
+ }
+
+ /*
+ * The write size includes the header, two bytes per code
+ * unit, post-code padding if necessary, and however much
+ * space the catches need.
+ */
+
+ int insnsSize = code.getInsns().codeSize();
+ if ((insnsSize & 1) != 0) {
+ insnsSize++;
+ }
+
+ setWriteSize(HEADER_SIZE + (insnsSize * 2) + catchesSize);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void writeTo0(DexFile file, AnnotatedOutput out) {
+ boolean annotates = out.annotates();
+ int regSz = getRegistersSize();
+ int outsSz = getOutsSize();
+ int insSz = getInsSize();
+ int insnsSz = code.getInsns().codeSize();
+ boolean needPadding = (insnsSz & 1) != 0;
+ int triesSz = (catches == null) ? 0 : catches.triesSize();
+ int debugOff = (debugInfo == null) ? 0 : debugInfo.getAbsoluteOffset();
+
+ if (annotates) {
+ out.annotate(0, offsetString() + ' ' + ref.toHuman());
+ out.annotate(2, " registers_size: " + Hex.u2(regSz));
+ out.annotate(2, " ins_size: " + Hex.u2(insSz));
+ out.annotate(2, " outs_size: " + Hex.u2(outsSz));
+ out.annotate(2, " tries_size: " + Hex.u2(triesSz));
+ out.annotate(4, " debug_off: " + Hex.u4(debugOff));
+ out.annotate(4, " insns_size: " + Hex.u4(insnsSz));
+
+ // This isn't represented directly here, but it is useful to see.
+ int size = throwsList.size();
+ if (size != 0) {
+ out.annotate(0, " throws " + StdTypeList.toHuman(throwsList));
+ }
+ }
+
+ out.writeShort(regSz);
+ out.writeShort(insSz);
+ out.writeShort(outsSz);
+ out.writeShort(triesSz);
+ out.writeInt(debugOff);
+ out.writeInt(insnsSz);
+
+ writeCodes(file, out);
+
+ if (catches != null) {
+ if (needPadding) {
+ if (annotates) {
+ out.annotate(2, " padding: 0");
+ }
+ out.writeShort(0);
+ }
+
+ catches.writeTo(file, out);
+ }
+
+ if (annotates) {
+ /*
+ * These are pointed at in the code header (above), but it's less
+ * distracting to expand on them at the bottom of the code.
+ */
+ if (debugInfo != null) {
+ out.annotate(0, " debug info");
+ debugInfo.annotateTo(file, out, " ");
+ }
+ }
+ }
+
+ /**
+ * Helper for {@link #writeTo0} which writes out the actual bytecode.
+ *
+ * @param file {@code non-null;} file we are part of
+ * @param out {@code non-null;} where to write to
+ */
+ private void writeCodes(DexFile file, AnnotatedOutput out) {
+ DalvInsnList insns = code.getInsns();
+
+ try {
+ insns.writeTo(out);
+ } catch (RuntimeException ex) {
+ throw ExceptionWithContext.withContext(ex, "...while writing " +
+ "instructions for " + ref.toHuman());
+ }
+ }
+
+ /**
+ * Get the in registers count.
+ *
+ * @return the count
+ */
+ private int getInsSize() {
+ return ref.getParameterWordCount(isStatic);
+ }
+
+ /**
+ * Get the out registers count.
+ *
+ * @return the count
+ */
+ private int getOutsSize() {
+ return code.getInsns().getOutsSize();
+ }
+
+ /**
+ * Get the total registers count.
+ *
+ * @return the count
+ */
+ private int getRegistersSize() {
+ return code.getInsns().getRegistersSize();
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/DebugInfoConstants.java b/dx/src/com/android/dx/dex/file/DebugInfoConstants.java
new file mode 100644
index 0000000..78b6b04
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/DebugInfoConstants.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+/**
+ * Constants for the dex debug info state machine format.
+ */
+public interface DebugInfoConstants {
+
+ /*
+ * normal opcodes
+ */
+
+ /**
+ * Terminates a debug info sequence for a method.<p>
+ * Args: none
+ *
+ */
+ static final int DBG_END_SEQUENCE = 0x00;
+
+ /**
+ * Advances the program counter/address register without emitting
+ * a positions entry.<p>
+ *
+ * Args:
+ * <ol>
+ * <li>Unsigned LEB128 &mdash; amount to advance pc by
+ * </ol>
+ */
+ static final int DBG_ADVANCE_PC = 0x01;
+
+ /**
+ * Advances the line register without emitting
+ * a positions entry.<p>
+ *
+ * Args:
+ * <ol>
+ * <li>Signed LEB128 &mdash; amount to change line register by.
+ * </ol>
+ */
+ static final int DBG_ADVANCE_LINE = 0x02;
+
+ /**
+ * Introduces a local variable at the current address.<p>
+ *
+ * Args:
+ * <ol>
+ * <li>Unsigned LEB128 &mdash; register that will contain local.
+ * <li>Unsigned LEB128 &mdash; string index (shifted by 1) of local name.
+ * <li>Unsigned LEB128 &mdash; type index (shifted by 1) of type.
+ * </ol>
+ */
+ static final int DBG_START_LOCAL = 0x03;
+
+ /**
+ * Introduces a local variable at the current address with a type
+ * signature specified.<p>
+ *
+ * Args:
+ * <ol>
+ * <li>Unsigned LEB128 &mdash; register that will contain local.
+ * <li>Unsigned LEB128 &mdash; string index (shifted by 1) of local name.
+ * <li>Unsigned LEB128 &mdash; type index (shifted by 1) of type.
+ * <li>Unsigned LEB128 &mdash; string index (shifted by 1) of
+ * type signature.
+ * </ol>
+ */
+ static final int DBG_START_LOCAL_EXTENDED = 0x04;
+
+ /**
+ * Marks a currently-live local variable as out of scope at the
+ * current address.<p>
+ *
+ * Args:
+ * <ol>
+ * <li>Unsigned LEB128 &mdash; register that contained local
+ * </ol>
+ */
+ static final int DBG_END_LOCAL = 0x05;
+
+ /**
+ * Re-introduces a local variable at the current address. The name
+ * and type are the same as the last local that was live in the specified
+ * register.<p>
+ *
+ * Args:
+ * <ol>
+ * <li>Unsigned LEB128 &mdash; register to re-start.
+ * </ol>
+ */
+ static final int DBG_RESTART_LOCAL = 0x06;
+
+
+ /**
+ * Sets the "prologue_end" state machine register, indicating that the
+ * next position entry that is added should be considered the end of
+ * a method prologue (an appropriate place for a method breakpoint).<p>
+ *
+ * The prologue_end register is cleared by any special
+ * ({@code >= OPCODE_BASE}) opcode.
+ */
+ static final int DBG_SET_PROLOGUE_END = 0x07;
+
+ /**
+ * Sets the "epilogue_begin" state machine register, indicating that the
+ * next position entry that is added should be considered the beginning of
+ * a method epilogue (an appropriate place to suspend execution before
+ * method exit).<p>
+ *
+ * The epilogue_begin register is cleared by any special
+ * ({@code >= OPCODE_BASE}) opcode.
+ */
+ static final int DBG_SET_EPILOGUE_BEGIN = 0x08;
+
+ /**
+ * Sets the current file that that line numbers refer to. All subsequent
+ * line number entries make reference to this source file name, instead
+ * of the default name specified in code_item.
+ *
+ * Args:
+ * <ol>
+ * <li>Unsigned LEB128 &mdash; string index (shifted by 1) of source
+ * file name.
+ * </ol>
+ */
+ static final int DBG_SET_FILE = 0x09;
+
+ /* IF YOU ADD A NEW OPCODE, increase OPCODE_BASE */
+
+ /*
+ * "special opcode" configuration, essentially what's found in
+ * the line number program header in DWARFv3, Section 6.2.4
+ */
+
+ /** the smallest value a special opcode can take */
+ static final int DBG_FIRST_SPECIAL = 0x0a;
+ static final int DBG_LINE_BASE = -4;
+ static final int DBG_LINE_RANGE = 15;
+ // MIN_INSN_LENGTH is always 1
+}
diff --git a/dx/src/com/android/dx/dex/file/DebugInfoDecoder.java b/dx/src/com/android/dx/dex/file/DebugInfoDecoder.java
new file mode 100644
index 0000000..e823816
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/DebugInfoDecoder.java
@@ -0,0 +1,653 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.dex.code.DalvCode;
+import com.android.dx.dex.code.DalvInsnList;
+import com.android.dx.dex.code.LocalList;
+import com.android.dx.dex.code.PositionList;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Prototype;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.ExceptionWithContext;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.android.dx.dex.file.DebugInfoConstants.*;
+
+/**
+ * A decoder for the dex debug info state machine format.
+ * This code exists mostly as a reference implementation and test for
+ * for the {@code DebugInfoEncoder}
+ */
+public class DebugInfoDecoder {
+ /** encoded debug info */
+ private final byte[] encoded;
+
+ /** positions decoded */
+ private final ArrayList<PositionEntry> positions;
+
+ /** locals decoded */
+ private final ArrayList<LocalEntry> locals;
+
+ /** size of code block in code units */
+ private final int codesize;
+
+ /** indexed by register, the last local variable live in a reg */
+ private final LocalEntry[] lastEntryForReg;
+
+ /** method descriptor of method this debug info is for */
+ private final Prototype desc;
+
+ /** true if method is static */
+ private final boolean isStatic;
+
+ /** dex file this debug info will be stored in */
+ private final DexFile file;
+
+ /**
+ * register size, in register units, of the register space
+ * used by this method
+ */
+ private final int regSize;
+
+ /** current decoding state: line number */
+ private int line = 1;
+
+ /** current decoding state: bytecode address */
+ private int address = 0;
+
+ /** string index of the string "this" */
+ private final int thisStringIdx;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param encoded encoded debug info
+ * @param codesize size of code block in code units
+ * @param regSize register size, in register units, of the register space
+ * used by this method
+ * @param isStatic true if method is static
+ * @param ref method descriptor of method this debug info is for
+ * @param file dex file this debug info will be stored in
+ */
+ DebugInfoDecoder(byte[] encoded, int codesize, int regSize,
+ boolean isStatic, CstMethodRef ref, DexFile file) {
+ if (encoded == null) {
+ throw new NullPointerException("encoded == null");
+ }
+
+ this.encoded = encoded;
+ this.isStatic = isStatic;
+ this.desc = ref.getPrototype();
+ this.file = file;
+ this.regSize = regSize;
+
+ positions = new ArrayList<PositionEntry>();
+ locals = new ArrayList<LocalEntry>();
+ this.codesize = codesize;
+ lastEntryForReg = new LocalEntry[regSize];
+
+ int idx = -1;
+
+ try {
+ idx = file.getStringIds().indexOf(new CstUtf8("this"));
+ } catch (IllegalArgumentException ex) {
+ /*
+ * Silently tolerate not finding "this". It just means that
+ * no method has local variable info that looks like
+ * a standard instance method.
+ */
+ }
+
+ thisStringIdx = idx;
+ }
+
+ /**
+ * An entry in the resulting postions table
+ */
+ static private class PositionEntry {
+ /** bytecode address */
+ public int address;
+
+ /** line number */
+ public int line;
+
+ public PositionEntry(int address, int line) {
+ this.address = address;
+ this.line = line;
+ }
+ }
+
+ /**
+ * An entry in the resulting locals table
+ */
+ static private class LocalEntry {
+ /** address of event */
+ public int address;
+
+ /** {@code true} iff it's a local start */
+ public boolean isStart;
+
+ /** register number */
+ public int reg;
+
+ /** index of name in strings table */
+ public int nameIndex;
+
+ /** index of type in types table */
+ public int typeIndex;
+
+ /** index of type signature in strings table */
+ public int signatureIndex;
+
+ public LocalEntry(int address, boolean isStart, int reg, int nameIndex,
+ int typeIndex, int signatureIndex) {
+ this.address = address;
+ this.isStart = isStart;
+ this.reg = reg;
+ this.nameIndex = nameIndex;
+ this.typeIndex = typeIndex;
+ this.signatureIndex = signatureIndex;
+ }
+
+ public String toString() {
+ return String.format("[%x %s v%d %04x %04x %04x]",
+ address, isStart ? "start" : "end", reg,
+ nameIndex, typeIndex, signatureIndex);
+ }
+ }
+
+ /**
+ * Gets the decoded positions list.
+ * Valid after calling {@code decode}.
+ *
+ * @return positions list in ascending address order.
+ */
+ public List<PositionEntry> getPositionList() {
+ return positions;
+ }
+
+ /**
+ * Gets the decoded locals list, in ascending start-address order.
+ * Valid after calling {@code decode}.
+ *
+ * @return locals list in ascending address order.
+ */
+ public List<LocalEntry> getLocals() {
+ return locals;
+ }
+
+ /**
+ * Decodes the debug info sequence.
+ */
+ public void decode() {
+ try {
+ decode0();
+ } catch (Exception ex) {
+ throw ExceptionWithContext.withContext(ex,
+ "...while decoding debug info");
+ }
+ }
+
+ /**
+ * Reads a string index. String indicies are offset by 1, and a 0 value
+ * in the stream (-1 as returned by this method) means "null"
+ *
+ * @param bs
+ * @return index into file's string ids table, -1 means null
+ * @throws IOException
+ */
+ private int readStringIndex(InputStream bs) throws IOException {
+ int offsetIndex = readUnsignedLeb128(bs);
+
+ return offsetIndex - 1;
+ }
+
+ /**
+ * Gets the register that begins the method's parameter range (including
+ * the 'this' parameter for non-static methods). The range continues until
+ * {@code regSize}
+ *
+ * @return register as noted above.
+ */
+ private int getParamBase() {
+ return regSize
+ - desc.getParameterTypes().getWordCount() - (isStatic? 0 : 1);
+ }
+
+ private void decode0() throws IOException {
+ ByteArrayInputStream bs = new ByteArrayInputStream(encoded);
+
+ line = readUnsignedLeb128(bs);
+ int szParams = readUnsignedLeb128(bs);
+ StdTypeList params = desc.getParameterTypes();
+ int curReg = getParamBase();
+
+ if (szParams != params.size()) {
+ throw new RuntimeException(
+ "Mismatch between parameters_size and prototype");
+ }
+
+ if (!isStatic) {
+ // Start off with implicit 'this' entry
+ LocalEntry thisEntry =
+ new LocalEntry(0, true, curReg, thisStringIdx, 0, 0);
+ locals.add(thisEntry);
+ lastEntryForReg[curReg] = thisEntry;
+ curReg++;
+ }
+
+ for (int i = 0; i < szParams; i++) {
+ Type paramType = params.getType(i);
+ LocalEntry le;
+
+ int nameIdx = readStringIndex(bs);
+
+ if (nameIdx == -1) {
+ /*
+ * Unnamed parameter; often but not always filled in by an
+ * extended start op after the prologue
+ */
+ le = new LocalEntry(0, true, curReg, -1, 0, 0);
+ } else {
+ // TODO: Final 0 should be idx of paramType.getDescriptor().
+ le = new LocalEntry(0, true, curReg, nameIdx, 0, 0);
+ }
+
+ locals.add(le);
+ lastEntryForReg[curReg] = le;
+ curReg += paramType.getCategory();
+ }
+
+ for (;;) {
+ int opcode = bs.read();
+
+ if (opcode < 0) {
+ throw new RuntimeException
+ ("Reached end of debug stream without "
+ + "encountering end marker");
+ }
+
+ switch (opcode) {
+ case DBG_START_LOCAL: {
+ int reg = readUnsignedLeb128(bs);
+ int nameIdx = readStringIndex(bs);
+ int typeIdx = readStringIndex(bs);
+ LocalEntry le = new LocalEntry(
+ address, true, reg, nameIdx, typeIdx, 0);
+
+ locals.add(le);
+ lastEntryForReg[reg] = le;
+ }
+ break;
+
+ case DBG_START_LOCAL_EXTENDED: {
+ int reg = readUnsignedLeb128(bs);
+ int nameIdx = readStringIndex(bs);
+ int typeIdx = readStringIndex(bs);
+ int sigIdx = readStringIndex(bs);
+ LocalEntry le = new LocalEntry(
+ address, true, reg, nameIdx, typeIdx, sigIdx);
+
+ locals.add(le);
+ lastEntryForReg[reg] = le;
+ }
+ break;
+
+ case DBG_RESTART_LOCAL: {
+ int reg = readUnsignedLeb128(bs);
+ LocalEntry prevle;
+ LocalEntry le;
+
+ try {
+ prevle = lastEntryForReg[reg];
+
+ if (prevle.isStart) {
+ throw new RuntimeException("nonsensical "
+ + "RESTART_LOCAL on live register v"
+ + reg);
+ }
+
+ le = new LocalEntry(address, true, reg,
+ prevle.nameIndex, prevle.typeIndex, 0);
+ } catch (NullPointerException ex) {
+ throw new RuntimeException(
+ "Encountered RESTART_LOCAL on new v" + reg);
+ }
+
+ locals.add(le);
+ lastEntryForReg[reg] = le;
+ }
+ break;
+
+ case DBG_END_LOCAL: {
+ int reg = readUnsignedLeb128(bs);
+ LocalEntry prevle;
+ LocalEntry le;
+
+ try {
+ prevle = lastEntryForReg[reg];
+
+ if (!prevle.isStart) {
+ throw new RuntimeException("nonsensical "
+ + "END_LOCAL on dead register v" + reg);
+ }
+
+ le = new LocalEntry(address, false, reg,
+ prevle.nameIndex, prevle.typeIndex,
+ prevle.signatureIndex);
+ } catch (NullPointerException ex) {
+ throw new RuntimeException(
+ "Encountered END_LOCAL on new v" + reg);
+ }
+
+ locals.add(le);
+ lastEntryForReg[reg] = le;
+ }
+ break;
+
+ case DBG_END_SEQUENCE:
+ // all done
+ return;
+
+ case DBG_ADVANCE_PC:
+ address += readUnsignedLeb128(bs);
+ break;
+
+ case DBG_ADVANCE_LINE:
+ line += readSignedLeb128(bs);
+ break;
+
+ case DBG_SET_PROLOGUE_END:
+ //TODO do something with this.
+ break;
+
+ case DBG_SET_EPILOGUE_BEGIN:
+ //TODO do something with this.
+ break;
+
+ case DBG_SET_FILE:
+ //TODO do something with this.
+ break;
+
+ default:
+ if (opcode < DBG_FIRST_SPECIAL) {
+ throw new RuntimeException(
+ "Invalid extended opcode encountered "
+ + opcode);
+ }
+
+ int adjopcode = opcode - DBG_FIRST_SPECIAL;
+
+ address += adjopcode / DBG_LINE_RANGE;
+ line += DBG_LINE_BASE + (adjopcode % DBG_LINE_RANGE);
+
+ positions.add(new PositionEntry(address, line));
+ break;
+
+ }
+ }
+ }
+
+ /**
+ * Validates an encoded debug info stream against data used to encode it,
+ * throwing an exception if they do not match. Used to validate the
+ * encoder.
+ *
+ * @param info encoded debug info
+ * @param file {@code non-null;} file to refer to during decoding
+ * @param ref {@code non-null;} method whose info is being decoded
+ * @param code {@code non-null;} original code object that was encoded
+ * @param isStatic whether the method is static
+ */
+ public static void validateEncode(byte[] info, DexFile file,
+ CstMethodRef ref, DalvCode code, boolean isStatic) {
+ PositionList pl = code.getPositions();
+ LocalList ll = code.getLocals();
+ DalvInsnList insns = code.getInsns();
+ int codeSize = insns.codeSize();
+ int countRegisters = insns.getRegistersSize();
+
+ try {
+ validateEncode0(info, codeSize, countRegisters,
+ isStatic, ref, file, pl, ll);
+ } catch (RuntimeException ex) {
+ System.err.println("instructions:");
+ insns.debugPrint(System.err, " ", true);
+ System.err.println("local list:");
+ ll.debugPrint(System.err, " ");
+ throw ExceptionWithContext.withContext(ex,
+ "while processing " + ref.toHuman());
+ }
+ }
+
+ private static void validateEncode0(byte[] info, int codeSize,
+ int countRegisters, boolean isStatic, CstMethodRef ref,
+ DexFile file, PositionList pl, LocalList ll) {
+ DebugInfoDecoder decoder
+ = new DebugInfoDecoder(info, codeSize, countRegisters,
+ isStatic, ref, file);
+
+ decoder.decode();
+
+ /*
+ * Go through the decoded position entries, matching up
+ * with original entries.
+ */
+
+ List<PositionEntry> decodedEntries = decoder.getPositionList();
+
+ if (decodedEntries.size() != pl.size()) {
+ throw new RuntimeException(
+ "Decoded positions table not same size was "
+ + decodedEntries.size() + " expected " + pl.size());
+ }
+
+ for (PositionEntry entry : decodedEntries) {
+ boolean found = false;
+ for (int i = pl.size() - 1; i >= 0; i--) {
+ PositionList.Entry ple = pl.get(i);
+
+ if (entry.line == ple.getPosition().getLine()
+ && entry.address == ple.getAddress()) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ throw new RuntimeException ("Could not match position entry: "
+ + entry.address + ", " + entry.line);
+ }
+ }
+
+ /*
+ * Go through the original local list, in order, matching up
+ * with decoded entries.
+ */
+
+ List<LocalEntry> decodedLocals = decoder.getLocals();
+ int thisStringIdx = decoder.thisStringIdx;
+ int decodedSz = decodedLocals.size();
+ int paramBase = decoder.getParamBase();
+
+ /*
+ * Preflight to fill in any parameters that were skipped in
+ * the prologue (including an implied "this") but then
+ * identified by full signature.
+ */
+ for (int i = 0; i < decodedSz; i++) {
+ LocalEntry entry = decodedLocals.get(i);
+ int idx = entry.nameIndex;
+
+ if ((idx < 0) || (idx == thisStringIdx)) {
+ for (int j = i + 1; j < decodedSz; j++) {
+ LocalEntry e2 = decodedLocals.get(j);
+ if (e2.address != 0) {
+ break;
+ }
+ if ((entry.reg == e2.reg) && e2.isStart) {
+ decodedLocals.set(i, e2);
+ decodedLocals.remove(j);
+ decodedSz--;
+ break;
+ }
+ }
+ }
+ }
+
+ int origSz = ll.size();
+ int decodeAt = 0;
+ boolean problem = false;
+
+ for (int i = 0; i < origSz; i++) {
+ LocalList.Entry origEntry = ll.get(i);
+
+ if (origEntry.getDisposition()
+ == LocalList.Disposition.END_REPLACED) {
+ /*
+ * The encoded list doesn't represent replacements, so
+ * ignore them for the sake of comparison.
+ */
+ continue;
+ }
+
+ LocalEntry decodedEntry;
+
+ do {
+ decodedEntry = decodedLocals.get(decodeAt);
+ if (decodedEntry.nameIndex >= 0) {
+ break;
+ }
+ /*
+ * A negative name index means this is an anonymous
+ * parameter, and we shouldn't expect to see it in the
+ * original list. So, skip it.
+ */
+ decodeAt++;
+ } while (decodeAt < decodedSz);
+
+ int decodedAddress = decodedEntry.address;
+
+ if (decodedEntry.reg != origEntry.getRegister()) {
+ System.err.println("local register mismatch at orig " + i +
+ " / decoded " + decodeAt);
+ problem = true;
+ break;
+ }
+
+ if (decodedEntry.isStart != origEntry.isStart()) {
+ System.err.println("local start/end mismatch at orig " + i +
+ " / decoded " + decodeAt);
+ problem = true;
+ break;
+ }
+
+ /*
+ * The secondary check here accounts for the fact that a
+ * parameter might not be marked as starting at 0 in the
+ * original list.
+ */
+ if ((decodedAddress != origEntry.getAddress())
+ && !((decodedAddress == 0)
+ && (decodedEntry.reg >= paramBase))) {
+ System.err.println("local address mismatch at orig " + i +
+ " / decoded " + decodeAt);
+ problem = true;
+ break;
+ }
+
+ decodeAt++;
+ }
+
+ if (problem) {
+ System.err.println("decoded locals:");
+ for (LocalEntry e : decodedLocals) {
+ System.err.println(" " + e);
+ }
+ throw new RuntimeException("local table problem");
+ }
+ }
+
+ /**
+ * Reads a DWARFv3-style signed LEB128 integer to the specified stream.
+ * See DWARF v3 section 7.6. An invalid sequence produces an IOException.
+ *
+ * @param bs stream to input from
+ * @return read value
+ * @throws IOException on invalid sequence in addition to
+ * those caused by the InputStream
+ */
+ public static int readSignedLeb128(InputStream bs) throws IOException {
+ int result = 0;
+ int cur;
+ int count = 0;
+ int signBits = -1;
+
+ do {
+ cur = bs.read();
+ result |= (cur & 0x7f) << (count * 7);
+ signBits <<= 7;
+ count++;
+ } while (((cur & 0x80) == 0x80) && count < 5);
+
+ if ((cur & 0x80) == 0x80) {
+ throw new IOException ("invalid LEB128 sequence");
+ }
+
+ // Sign extend if appropriate
+ if (((signBits >> 1) & result) != 0 ) {
+ result |= signBits;
+ }
+
+ return result;
+ }
+
+ /**
+ * Reads a DWARFv3-style unsigned LEB128 integer to the specified stream.
+ * See DWARF v3 section 7.6. An invalid sequence produces an IOException.
+ *
+ * @param bs stream to input from
+ * @return read value, which should be treated as an unsigned value.
+ * @throws IOException on invalid sequence in addition to
+ * those caused by the InputStream
+ */
+ public static int readUnsignedLeb128(InputStream bs) throws IOException {
+ int result = 0;
+ int cur;
+ int count = 0;
+
+ do {
+ cur = bs.read();
+ result |= (cur & 0x7f) << (count * 7);
+ count++;
+ } while (((cur & 0x80) == 0x80) && count < 5);
+
+ if ((cur & 0x80) == 0x80) {
+ throw new IOException ("invalid LEB128 sequence");
+ }
+
+ return result;
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/DebugInfoEncoder.java b/dx/src/com/android/dx/dex/file/DebugInfoEncoder.java
new file mode 100644
index 0000000..08b6637
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/DebugInfoEncoder.java
@@ -0,0 +1,920 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.dex.code.LocalList;
+import com.android.dx.dex.code.PositionList;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Prototype;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.ByteArrayAnnotatedOutput;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.ExceptionWithContext;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.BitSet;
+
+import static com.android.dx.dex.file.DebugInfoConstants.*;
+
+/**
+ * An encoder for the dex debug info state machine format. The format
+ * for each method enrty is as follows:
+ * <ol>
+ * <li> signed LEB128: initial value for line register.
+ * <li> n instances of signed LEB128: string indicies (offset by 1)
+ * for each method argument in left-to-right order
+ * with {@code this} excluded. A value of '0' indicates "no name"
+ * <li> A sequence of special or normal opcodes as defined in
+ * {@code DebugInfoConstants}.
+ * <li> A single terminating {@code OP_END_SEQUENCE}
+ * </ol>
+ */
+public final class DebugInfoEncoder {
+ private static final boolean DEBUG = false;
+
+ /** {@code null-ok;} positions (line numbers) to encode */
+ private final PositionList positions;
+
+ /** {@code null-ok;} local variables to encode */
+ private final LocalList locals;
+
+ private final ByteArrayAnnotatedOutput output;
+ private final DexFile file;
+ private final int codeSize;
+ private final int regSize;
+
+ private final Prototype desc;
+ private final boolean isStatic;
+
+ /** current encoding state: bytecode address */
+ private int address = 0;
+
+ /** current encoding state: line number */
+ private int line = 1;
+
+ /**
+ * if non-null: the output to write annotations to. No normal
+ * output is written to this.
+ */
+ private AnnotatedOutput annotateTo;
+
+ /** if non-null: another possible output for annotations */
+ private PrintWriter debugPrint;
+
+ /** if non-null: the prefix for each annotation or debugPrint line */
+ private String prefix;
+
+ /** true if output should be consumed during annotation */
+ private boolean shouldConsume;
+
+ /** indexed by register; last local alive in register */
+ private final LocalList.Entry[] lastEntryForReg;
+
+ /**
+ * Creates an instance.
+ *
+ * @param positions {@code null-ok;} positions (line numbers) to encode
+ * @param locals {@code null-ok;} local variables to encode
+ * @param file {@code null-ok;} may only be {@code null} if simply using
+ * this class to do a debug print
+ * @param codeSize
+ * @param regSize
+ * @param isStatic
+ * @param ref
+ */
+ public DebugInfoEncoder(PositionList positions, LocalList locals,
+ DexFile file, int codeSize, int regSize,
+ boolean isStatic, CstMethodRef ref) {
+ this.positions = positions;
+ this.locals = locals;
+ this.file = file;
+ this.desc = ref.getPrototype();
+ this.isStatic = isStatic;
+ this.codeSize = codeSize;
+ this.regSize = regSize;
+
+ output = new ByteArrayAnnotatedOutput();
+ lastEntryForReg = new LocalList.Entry[regSize];
+ }
+
+ /**
+ * Annotates or writes a message to the {@code debugPrint} writer
+ * if applicable.
+ *
+ * @param length the number of bytes associated with this message
+ * @param message the message itself
+ */
+ private void annotate(int length, String message) {
+ if (prefix != null) {
+ message = prefix + message;
+ }
+
+ if (annotateTo != null) {
+ annotateTo.annotate(shouldConsume ? length : 0, message);
+ }
+
+ if (debugPrint != null) {
+ debugPrint.println(message);
+ }
+ }
+
+ /**
+ * Converts this (PositionList, LocalList) pair into a state machine
+ * sequence.
+ *
+ * @return {@code non-null;} encoded byte sequence without padding and
+ * terminated with a {@code 0x00} byte
+ */
+ public byte[] convert() {
+ try {
+ byte[] ret;
+ ret = convert0();
+
+ if (DEBUG) {
+ for (int i = 0 ; i < ret.length; i++) {
+ System.err.printf("byte %02x\n", (0xff & ret[i]));
+ }
+ }
+
+ return ret;
+ } catch (IOException ex) {
+ throw ExceptionWithContext
+ .withContext(ex, "...while encoding debug info");
+ }
+ }
+
+ /**
+ * Converts and produces annotations on a stream. Does not write
+ * actual bits to the {@code AnnotatedOutput}.
+ *
+ * @param prefix {@code null-ok;} prefix to attach to each line of output
+ * @param debugPrint {@code null-ok;} if specified, an alternate output for
+ * annotations
+ * @param out {@code null-ok;} if specified, where annotations should go
+ * @param consume whether to claim to have consumed output for
+ * {@code out}
+ * @return {@code non-null;} encoded output
+ */
+ public byte[] convertAndAnnotate(String prefix, PrintWriter debugPrint,
+ AnnotatedOutput out, boolean consume) {
+ this.prefix = prefix;
+ this.debugPrint = debugPrint;
+ annotateTo = out;
+ shouldConsume = consume;
+
+ byte[] result = convert();
+
+ return result;
+ }
+
+ private byte[] convert0() throws IOException {
+ ArrayList<PositionList.Entry> sortedPositions = buildSortedPositions();
+ ArrayList<LocalList.Entry> methodArgs = extractMethodArguments();
+
+ emitHeader(sortedPositions, methodArgs);
+
+ // TODO: Make this mark be the actual prologue end.
+ output.writeByte(DBG_SET_PROLOGUE_END);
+
+ if (annotateTo != null || debugPrint != null) {
+ annotate(1, String.format("%04x: prologue end",address));
+ }
+
+ int positionsSz = sortedPositions.size();
+ int localsSz = locals.size();
+
+ // Current index in sortedPositions
+ int curPositionIdx = 0;
+ // Current index in locals
+ int curLocalIdx = 0;
+
+ for (;;) {
+ /*
+ * Emit any information for the current address.
+ */
+
+ curLocalIdx = emitLocalsAtAddress(curLocalIdx);
+ curPositionIdx =
+ emitPositionsAtAddress(curPositionIdx, sortedPositions);
+
+ /*
+ * Figure out what the next important address is.
+ */
+
+ int nextAddrL = Integer.MAX_VALUE; // local variable
+ int nextAddrP = Integer.MAX_VALUE; // position (line number)
+
+ if (curLocalIdx < localsSz) {
+ nextAddrL = locals.get(curLocalIdx).getAddress();
+ }
+
+ if (curPositionIdx < positionsSz) {
+ nextAddrP = sortedPositions.get(curPositionIdx).getAddress();
+ }
+
+ int next = Math.min(nextAddrP, nextAddrL);
+
+ // No next important address == done.
+ if (next == Integer.MAX_VALUE) {
+ break;
+ }
+
+ /*
+ * If the only work remaining are local ends at the end of the
+ * block, stop here. Those are implied anyway.
+ */
+ if (next == codeSize
+ && nextAddrL == Integer.MAX_VALUE
+ && nextAddrP == Integer.MAX_VALUE) {
+ break;
+ }
+
+ if (next == nextAddrP) {
+ // Combined advance PC + position entry
+ emitPosition(sortedPositions.get(curPositionIdx++));
+ } else {
+ emitAdvancePc(next - address);
+ }
+ }
+
+ emitEndSequence();
+
+ return output.toByteArray();
+ }
+
+ /**
+ * Emits all local variable activity that occurs at the current
+ * {@link #address} starting at the given index into {@code
+ * locals} and including all subsequent activity at the same
+ * address.
+ *
+ * @param curLocalIdx Current index in locals
+ * @return new value for {@code curLocalIdx}
+ * @throws IOException
+ */
+ private int emitLocalsAtAddress(int curLocalIdx)
+ throws IOException {
+ int sz = locals.size();
+
+ // TODO: Don't emit ends implied by starts.
+
+ while ((curLocalIdx < sz)
+ && (locals.get(curLocalIdx).getAddress() == address)) {
+ LocalList.Entry entry = locals.get(curLocalIdx++);
+ int reg = entry.getRegister();
+ LocalList.Entry prevEntry = lastEntryForReg[reg];
+
+ if (entry == prevEntry) {
+ /*
+ * Here we ignore locals entries for parameters,
+ * which have already been represented and placed in the
+ * lastEntryForReg array.
+ */
+ continue;
+ }
+
+ // At this point we have a new entry one way or another.
+ lastEntryForReg[reg] = entry;
+
+ if (entry.isStart()) {
+ if ((prevEntry != null) && entry.matches(prevEntry)) {
+ /*
+ * The previous local in this register has the same
+ * name and type as the one being introduced now, so
+ * use the more efficient "restart" form.
+ */
+ if (prevEntry.isStart()) {
+ /*
+ * We should never be handed a start when a
+ * a matching local is already active.
+ */
+ throw new RuntimeException("shouldn't happen");
+ }
+ emitLocalRestart(entry);
+ } else {
+ emitLocalStart(entry);
+ }
+ } else {
+ /*
+ * Only emit a local end if it is *not* due to a direct
+ * replacement. Direct replacements imply an end of the
+ * previous local in the same register.
+ *
+ * TODO: Make sure the runtime can deal with implied
+ * local ends from category-2 interactions, and when so,
+ * also stop emitting local ends for those cases.
+ */
+ if (entry.getDisposition()
+ != LocalList.Disposition.END_REPLACED) {
+ emitLocalEnd(entry);
+ }
+ }
+ }
+
+ return curLocalIdx;
+ }
+
+ /**
+ * Emits all positions that occur at the current {@code address}
+ *
+ * @param curPositionIdx Current index in sortedPositions
+ * @param sortedPositions positions, sorted by ascending address
+ * @return new value for {@code curPositionIdx}
+ * @throws IOException
+ */
+ private int emitPositionsAtAddress(int curPositionIdx,
+ ArrayList<PositionList.Entry> sortedPositions)
+ throws IOException {
+ int positionsSz = sortedPositions.size();
+ while ((curPositionIdx < positionsSz)
+ && (sortedPositions.get(curPositionIdx).getAddress()
+ == address)) {
+ emitPosition(sortedPositions.get(curPositionIdx++));
+ }
+ return curPositionIdx;
+ }
+
+ /**
+ * Emits the header sequence, which consists of LEB128-encoded initial
+ * line number and string indicies for names of all non-"this" arguments.
+ *
+ * @param sortedPositions positions, sorted by ascending address
+ * @param methodArgs local list entries for method argumens arguments,
+ * in left-to-right order omitting "this"
+ * @throws IOException
+ */
+ private void emitHeader(ArrayList<PositionList.Entry> sortedPositions,
+ ArrayList<LocalList.Entry> methodArgs) throws IOException {
+ boolean annotate = (annotateTo != null) || (debugPrint != null);
+ int mark = output.getCursor();
+
+ // Start by initializing the line number register.
+ if (sortedPositions.size() > 0) {
+ PositionList.Entry entry = sortedPositions.get(0);
+ line = entry.getPosition().getLine();
+ }
+ output.writeUnsignedLeb128(line);
+
+ if (annotate) {
+ annotate(output.getCursor() - mark, "line_start: " + line);
+ }
+
+ int curParam = getParamBase();
+ // paramTypes will not include 'this'
+ StdTypeList paramTypes = desc.getParameterTypes();
+ int szParamTypes = paramTypes.size();
+
+ /*
+ * Initialize lastEntryForReg to have an initial
+ * entry for the 'this' pointer.
+ */
+ if (!isStatic) {
+ for (LocalList.Entry arg : methodArgs) {
+ if (curParam == arg.getRegister()) {
+ lastEntryForReg[curParam] = arg;
+ break;
+ }
+ }
+ curParam++;
+ }
+
+ // Write out the number of parameter entries that will follow.
+ mark = output.getCursor();
+ output.writeUnsignedLeb128(szParamTypes);
+
+ if (annotate) {
+ annotate(output.getCursor() - mark,
+ String.format("parameters_size: %04x", szParamTypes));
+ }
+
+ /*
+ * Then emit the string indicies of all the method parameters.
+ * Note that 'this', if applicable, is excluded.
+ */
+ for (int i = 0; i < szParamTypes; i++) {
+ Type pt = paramTypes.get(i);
+ LocalList.Entry found = null;
+
+ mark = output.getCursor();
+
+ for (LocalList.Entry arg : methodArgs) {
+ if (curParam == arg.getRegister()) {
+ found = arg;
+
+ if (arg.getSignature() != null) {
+ /*
+ * Parameters with signatures will be re-emitted
+ * in complete as LOCAL_START_EXTENDED's below.
+ */
+ emitStringIndex(null);
+ } else {
+ emitStringIndex(arg.getName());
+ }
+ lastEntryForReg[curParam] = arg;
+
+ break;
+ }
+ }
+
+ if (found == null) {
+ /*
+ * Emit a null symbol for "unnamed." This is common
+ * for, e.g., synthesized methods and inner-class
+ * this$0 arguments.
+ */
+ emitStringIndex(null);
+ }
+
+ if (annotate) {
+ String parameterName
+ = (found == null || found.getSignature() != null)
+ ? "<unnamed>" : found.getName().toHuman();
+ annotate(output.getCursor() - mark,
+ "parameter " + parameterName + " "
+ + RegisterSpec.PREFIX + curParam);
+ }
+
+ curParam += pt.getCategory();
+ }
+
+ /*
+ * If anything emitted above has a type signature, emit it again as
+ * a LOCAL_RESTART_EXTENDED
+ */
+
+ for (LocalList.Entry arg : lastEntryForReg) {
+ if (arg == null) {
+ continue;
+ }
+
+ CstUtf8 signature = arg.getSignature();
+
+ if (signature != null) {
+ emitLocalStartExtended(arg);
+ }
+ }
+ }
+
+ /**
+ * Builds a list of position entries, sorted by ascending address.
+ *
+ * @return A sorted positions list
+ */
+ private ArrayList<PositionList.Entry> buildSortedPositions() {
+ int sz = (positions == null) ? 0 : positions.size();
+ ArrayList<PositionList.Entry> result = new ArrayList(sz);
+
+ for (int i = 0; i < sz; i++) {
+ result.add(positions.get(i));
+ }
+
+ // Sort ascending by address.
+ Collections.sort (result, new Comparator<PositionList.Entry>() {
+ public int compare (PositionList.Entry a, PositionList.Entry b) {
+ return a.getAddress() - b.getAddress();
+ }
+
+ public boolean equals (Object obj) {
+ return obj == this;
+ }
+ });
+ return result;
+ }
+
+ /**
+ * Gets the register that begins the method's parameter range (including
+ * the 'this' parameter for non-static methods). The range continues until
+ * {@code regSize}
+ *
+ * @return register as noted above
+ */
+ private int getParamBase() {
+ return regSize
+ - desc.getParameterTypes().getWordCount() - (isStatic? 0 : 1);
+ }
+
+ /**
+ * Extracts method arguments from a locals list. These will be collected
+ * from the input list and sorted by ascending register in the
+ * returned list.
+ *
+ * @return list of non-{@code this} method argument locals,
+ * sorted by ascending register
+ */
+ private ArrayList<LocalList.Entry> extractMethodArguments() {
+ ArrayList<LocalList.Entry> result
+ = new ArrayList(desc.getParameterTypes().size());
+ int argBase = getParamBase();
+ BitSet seen = new BitSet(regSize - argBase);
+ int sz = locals.size();
+
+ for (int i = 0; i < sz; i++) {
+ LocalList.Entry e = locals.get(i);
+ int reg = e.getRegister();
+
+ if (reg < argBase) {
+ continue;
+ }
+
+ // only the lowest-start-address entry is included.
+ if (seen.get(reg - argBase)) {
+ continue;
+ }
+
+ seen.set(reg - argBase);
+ result.add(e);
+ }
+
+ // Sort by ascending register.
+ Collections.sort(result, new Comparator<LocalList.Entry>() {
+ public int compare(LocalList.Entry a, LocalList.Entry b) {
+ return a.getRegister() - b.getRegister();
+ }
+
+ public boolean equals(Object obj) {
+ return obj == this;
+ }
+ });
+
+ return result;
+ }
+
+ /**
+ * Returns a string representation of this LocalList entry that is
+ * appropriate for emitting as an annotation.
+ *
+ * @param e {@code non-null;} entry
+ * @return {@code non-null;} annotation string
+ */
+ private String entryAnnotationString(LocalList.Entry e) {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append(RegisterSpec.PREFIX);
+ sb.append(e.getRegister());
+ sb.append(' ');
+
+ CstUtf8 name = e.getName();
+ if (name == null) {
+ sb.append("null");
+ } else {
+ sb.append(name.toHuman());
+ }
+ sb.append(' ');
+
+ CstType type = e.getType();
+ if (type == null) {
+ sb.append("null");
+ } else {
+ sb.append(type.toHuman());
+ }
+
+ CstUtf8 signature = e.getSignature();
+
+ if (signature != null) {
+ sb.append(' ');
+ sb.append(signature.toHuman());
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Emits a {@link DebugInfoConstants#DBG_RESTART_LOCAL DBG_RESTART_LOCAL}
+ * sequence.
+ *
+ * @param entry entry associated with this restart
+ * @throws IOException
+ */
+ private void emitLocalRestart(LocalList.Entry entry)
+ throws IOException {
+
+ int mark = output.getCursor();
+
+ output.writeByte(DBG_RESTART_LOCAL);
+ emitUnsignedLeb128(entry.getRegister());
+
+ if (annotateTo != null || debugPrint != null) {
+ annotate(output.getCursor() - mark,
+ String.format("%04x: +local restart %s",
+ address, entryAnnotationString(entry)));
+ }
+
+ if (DEBUG) {
+ System.err.println("emit local restart");
+ }
+ }
+
+ /**
+ * Emits a string index as an unsigned LEB128. The actual value written
+ * is shifted by 1, so that the '0' value is reserved for "null". The
+ * null symbol is used in some cases by the parameter name list
+ * at the beginning of the sequence.
+ *
+ * @param string {@code null-ok;} string to emit
+ * @throws IOException
+ */
+ private void emitStringIndex(CstUtf8 string) throws IOException {
+ if ((string == null) || (file == null)) {
+ output.writeUnsignedLeb128(0);
+ } else {
+ output.writeUnsignedLeb128(
+ 1 + file.getStringIds().indexOf(string));
+ }
+
+ if (DEBUG) {
+ System.err.printf("Emit string %s\n",
+ string == null ? "<null>" : string.toQuoted());
+ }
+ }
+
+ /**
+ * Emits a type index as an unsigned LEB128. The actual value written
+ * is shifted by 1, so that the '0' value is reserved for "null".
+ *
+ * @param type {@code null-ok;} type to emit
+ * @throws IOException
+ */
+ private void emitTypeIndex(CstType type) throws IOException {
+ if ((type == null) || (file == null)) {
+ output.writeUnsignedLeb128(0);
+ } else {
+ output.writeUnsignedLeb128(
+ 1 + file.getTypeIds().indexOf(type));
+ }
+
+ if (DEBUG) {
+ System.err.printf("Emit type %s\n",
+ type == null ? "<null>" : type.toHuman());
+ }
+ }
+
+ /**
+ * Emits a {@link DebugInfoConstants#DBG_START_LOCAL DBG_START_LOCAL} or
+ * {@link DebugInfoConstants#DBG_START_LOCAL_EXTENDED
+ * DBG_START_LOCAL_EXTENDED} sequence.
+ *
+ * @param entry entry to emit
+ * @throws IOException
+ */
+ private void emitLocalStart(LocalList.Entry entry)
+ throws IOException {
+
+ if (entry.getSignature() != null) {
+ emitLocalStartExtended(entry);
+ return;
+ }
+
+ int mark = output.getCursor();
+
+ output.writeByte(DBG_START_LOCAL);
+
+ emitUnsignedLeb128(entry.getRegister());
+ emitStringIndex(entry.getName());
+ emitTypeIndex(entry.getType());
+
+ if (annotateTo != null || debugPrint != null) {
+ annotate(output.getCursor() - mark,
+ String.format("%04x: +local %s", address,
+ entryAnnotationString(entry)));
+ }
+
+ if (DEBUG) {
+ System.err.println("emit local start");
+ }
+ }
+
+ /**
+ * Emits a {@link DebugInfoConstants#DBG_START_LOCAL_EXTENDED
+ * DBG_START_LOCAL_EXTENDED} sequence.
+ *
+ * @param entry entry to emit
+ * @throws IOException
+ */
+ private void emitLocalStartExtended(LocalList.Entry entry)
+ throws IOException {
+
+ int mark = output.getCursor();
+
+ output.writeByte(DBG_START_LOCAL_EXTENDED);
+
+ emitUnsignedLeb128(entry.getRegister());
+ emitStringIndex(entry.getName());
+ emitTypeIndex(entry.getType());
+ emitStringIndex(entry.getSignature());
+
+ if (annotateTo != null || debugPrint != null) {
+ annotate(output.getCursor() - mark,
+ String.format("%04x: +localx %s", address,
+ entryAnnotationString(entry)));
+ }
+
+ if (DEBUG) {
+ System.err.println("emit local start");
+ }
+ }
+
+ /**
+ * Emits a {@link DebugInfoConstants#DBG_END_LOCAL DBG_END_LOCAL} sequence.
+ *
+ * @param entry {@code entry non-null;} entry associated with end.
+ * @throws IOException
+ */
+ private void emitLocalEnd(LocalList.Entry entry)
+ throws IOException {
+
+ int mark = output.getCursor();
+
+ output.writeByte(DBG_END_LOCAL);
+ output.writeUnsignedLeb128(entry.getRegister());
+
+ if (annotateTo != null || debugPrint != null) {
+ annotate(output.getCursor() - mark,
+ String.format("%04x: -local %s", address,
+ entryAnnotationString(entry)));
+ }
+
+ if (DEBUG) {
+ System.err.println("emit local end");
+ }
+ }
+
+ /**
+ * Emits the necessary byte sequences to emit the given position table
+ * entry. This will typically be a single special opcode, although
+ * it may also require DBG_ADVANCE_PC or DBG_ADVANCE_LINE.
+ *
+ * @param entry position entry to emit.
+ * @throws IOException
+ */
+ private void emitPosition(PositionList.Entry entry)
+ throws IOException {
+
+ SourcePosition pos = entry.getPosition();
+ int newLine = pos.getLine();
+ int newAddress = entry.getAddress();
+
+ int opcode;
+
+ int deltaLines = newLine - line;
+ int deltaAddress = newAddress - address;
+
+ if (deltaAddress < 0) {
+ throw new RuntimeException(
+ "Position entries must be in ascending address order");
+ }
+
+ if ((deltaLines < DBG_LINE_BASE)
+ || (deltaLines > (DBG_LINE_BASE + DBG_LINE_RANGE -1))) {
+ emitAdvanceLine(deltaLines);
+ deltaLines = 0;
+ }
+
+ opcode = computeOpcode (deltaLines, deltaAddress);
+
+ if ((opcode & ~0xff) > 0) {
+ emitAdvancePc(deltaAddress);
+ deltaAddress = 0;
+ opcode = computeOpcode (deltaLines, deltaAddress);
+
+ if ((opcode & ~0xff) > 0) {
+ emitAdvanceLine(deltaLines);
+ deltaLines = 0;
+ opcode = computeOpcode (deltaLines, deltaAddress);
+ }
+ }
+
+ output.writeByte(opcode);
+
+ line += deltaLines;
+ address += deltaAddress;
+
+ if (annotateTo != null || debugPrint != null) {
+ annotate(1,
+ String.format("%04x: line %d", address, line));
+ }
+ }
+
+ /**
+ * Computes a special opcode that will encode the given position change.
+ * If the return value is > 0xff, then the request cannot be fulfilled.
+ * Essentially the same as described in "DWARF Debugging Format Version 3"
+ * section 6.2.5.1.
+ *
+ * @param deltaLines {@code >= DBG_LINE_BASE, <= DBG_LINE_BASE +
+ * DBG_LINE_RANGE;} the line change to encode
+ * @param deltaAddress {@code >= 0;} the address change to encode
+ * @return {@code <= 0xff} if in range, otherwise parameters are out
+ * of range
+ */
+ private static int computeOpcode(int deltaLines, int deltaAddress) {
+ if (deltaLines < DBG_LINE_BASE
+ || deltaLines > (DBG_LINE_BASE + DBG_LINE_RANGE -1)) {
+
+ throw new RuntimeException("Parameter out of range");
+ }
+
+ return (deltaLines - DBG_LINE_BASE)
+ + (DBG_LINE_RANGE * deltaAddress) + DBG_FIRST_SPECIAL;
+ }
+
+ /**
+ * Emits an {@link DebugInfoConstants#DBG_ADVANCE_LINE DBG_ADVANCE_LINE}
+ * sequence.
+ *
+ * @param deltaLines amount to change line number register by
+ * @throws IOException
+ */
+ private void emitAdvanceLine(int deltaLines) throws IOException {
+ int mark = output.getCursor();
+
+ output.writeByte(DBG_ADVANCE_LINE);
+ output.writeSignedLeb128(deltaLines);
+ line += deltaLines;
+
+ if (annotateTo != null || debugPrint != null) {
+ annotate(output.getCursor() - mark,
+ String.format("line = %d", line));
+ }
+
+ if (DEBUG) {
+ System.err.printf("Emitting advance_line for %d\n", deltaLines);
+ }
+ }
+
+ /**
+ * Emits an {@link DebugInfoConstants#DBG_ADVANCE_PC DBG_ADVANCE_PC}
+ * sequence.
+ *
+ * @param deltaAddress {@code >= 0;} amount to change program counter by
+ * @throws IOException
+ */
+ private void emitAdvancePc(int deltaAddress) throws IOException {
+ int mark = output.getCursor();
+
+ output.writeByte(DBG_ADVANCE_PC);
+ output.writeUnsignedLeb128(deltaAddress);
+ address += deltaAddress;
+
+ if (annotateTo != null || debugPrint != null) {
+ annotate(output.getCursor() - mark,
+ String.format("%04x: advance pc", address));
+ }
+
+ if (DEBUG) {
+ System.err.printf("Emitting advance_pc for %d\n", deltaAddress);
+ }
+ }
+
+ /**
+ * Emits an unsigned LEB128 value.
+ *
+ * @param n {@code >= 0;} value to emit. Note that, although this can
+ * represent integers larger than Integer.MAX_VALUE, we currently don't
+ * allow that.
+ * @throws IOException
+ */
+ private void emitUnsignedLeb128(int n) throws IOException {
+ // We'll never need the top end of the unsigned range anyway.
+ if (n < 0) {
+ throw new RuntimeException(
+ "Signed value where unsigned required: " + n);
+ }
+
+ output.writeUnsignedLeb128(n);
+ }
+
+ /**
+ * Emits the {@link DebugInfoConstants#DBG_END_SEQUENCE DBG_END_SEQUENCE}
+ * bytecode.
+ */
+ private void emitEndSequence() {
+ output.writeByte(DBG_END_SEQUENCE);
+
+ if (annotateTo != null || debugPrint != null) {
+ annotate(1, "end sequence");
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/DebugInfoItem.java b/dx/src/com/android/dx/dex/file/DebugInfoItem.java
new file mode 100644
index 0000000..6a41b18
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/DebugInfoItem.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.dex.code.DalvCode;
+import com.android.dx.dex.code.DalvInsnList;
+import com.android.dx.dex.code.LocalList;
+import com.android.dx.dex.code.PositionList;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.ExceptionWithContext;
+
+import java.io.PrintWriter;
+
+public class DebugInfoItem extends OffsettedItem {
+ /** the required alignment for instances of this class */
+ private static final int ALIGNMENT = 1;
+
+ private static final boolean ENABLE_ENCODER_SELF_CHECK = false;
+
+ /** {@code non-null;} the code this item represents */
+ private final DalvCode code;
+
+ private byte[] encoded;
+
+ private final boolean isStatic;
+ private final CstMethodRef ref;
+
+ public DebugInfoItem(DalvCode code, boolean isStatic, CstMethodRef ref) {
+ // We don't know the write size yet.
+ super (ALIGNMENT, -1);
+
+ if (code == null) {
+ throw new NullPointerException("code == null");
+ }
+
+ this.code = code;
+ this.isStatic = isStatic;
+ this.ref = ref;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ItemType itemType() {
+ return ItemType.TYPE_DEBUG_INFO_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addContents(DexFile file) {
+ // No contents to add.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void place0(Section addedTo, int offset) {
+ // Encode the data and note the size.
+
+ try {
+ encoded = encode(addedTo.getFile(), null, null, null, false);
+ setWriteSize(encoded.length);
+ } catch (RuntimeException ex) {
+ throw ExceptionWithContext.withContext(ex,
+ "...while placing debug info for " + ref.toHuman());
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toHuman() {
+ throw new RuntimeException("unsupported");
+ }
+
+ /**
+ * Writes annotations for the elements of this list, as
+ * zero-length. This is meant to be used for dumping this instance
+ * directly after a code dump (with the real local list actually
+ * existing elsewhere in the output).
+ *
+ * @param file {@code non-null;} the file to use for referencing other sections
+ * @param out {@code non-null;} where to annotate to
+ * @param prefix {@code null-ok;} prefix to attach to each line of output
+ */
+ public void annotateTo(DexFile file, AnnotatedOutput out, String prefix) {
+ encode(file, prefix, null, out, false);
+ }
+
+ /**
+ * Does a human-friendly dump of this instance.
+ *
+ * @param out {@code non-null;} where to dump
+ * @param prefix {@code non-null;} prefix to attach to each line of output
+ */
+ public void debugPrint(PrintWriter out, String prefix) {
+ encode(null, prefix, out, null, false);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void writeTo0(DexFile file, AnnotatedOutput out) {
+ if (out.annotates()) {
+ /*
+ * Re-run the encoder to generate the annotations,
+ * but write the bits from the original encode
+ */
+
+ out.annotate(offsetString() + " debug info");
+ encode(file, null, null, out, true);
+ }
+
+ out.write(encoded);
+ }
+
+ /**
+ * Performs debug info encoding.
+ *
+ * @param file {@code null-ok;} file to refer to during encoding
+ * @param prefix {@code null-ok;} prefix to attach to each line of output
+ * @param debugPrint {@code null-ok;} if specified, an alternate output for
+ * annotations
+ * @param out {@code null-ok;} if specified, where annotations should go
+ * @param consume whether to claim to have consumed output for
+ * {@code out}
+ * @return {@code non-null;} the encoded array
+ */
+ private byte[] encode(DexFile file, String prefix, PrintWriter debugPrint,
+ AnnotatedOutput out, boolean consume) {
+ byte[] result = encode0(file, prefix, debugPrint, out, consume);
+
+ if (ENABLE_ENCODER_SELF_CHECK && (file != null)) {
+ try {
+ DebugInfoDecoder.validateEncode(result, file, ref, code,
+ isStatic);
+ } catch (RuntimeException ex) {
+ // Reconvert, annotating to System.err.
+ encode0(file, "", new PrintWriter(System.err, true), null,
+ false);
+ throw ex;
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Helper for {@link #encode} to do most of the work.
+ *
+ * @param file {@code null-ok;} file to refer to during encoding
+ * @param prefix {@code null-ok;} prefix to attach to each line of output
+ * @param debugPrint {@code null-ok;} if specified, an alternate output for
+ * annotations
+ * @param out {@code null-ok;} if specified, where annotations should go
+ * @param consume whether to claim to have consumed output for
+ * {@code out}
+ * @return {@code non-null;} the encoded array
+ */
+ private byte[] encode0(DexFile file, String prefix, PrintWriter debugPrint,
+ AnnotatedOutput out, boolean consume) {
+ PositionList positions = code.getPositions();
+ LocalList locals = code.getLocals();
+ DalvInsnList insns = code.getInsns();
+ int codeSize = insns.codeSize();
+ int regSize = insns.getRegistersSize();
+
+ DebugInfoEncoder encoder =
+ new DebugInfoEncoder(positions, locals,
+ file, codeSize, regSize, isStatic, ref);
+
+ byte[] result;
+
+ if ((debugPrint == null) && (out == null)) {
+ result = encoder.convert();
+ } else {
+ result = encoder.convertAndAnnotate(prefix, debugPrint, out,
+ consume);
+ }
+
+ return result;
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/DexFile.java b/dx/src/com/android/dx/dex/file/DexFile.java
new file mode 100644
index 0000000..1cc9358
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/DexFile.java
@@ -0,0 +1,647 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstBaseMethodRef;
+import com.android.dx.rop.cst.CstEnumRef;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.ByteArrayAnnotatedOutput;
+import com.android.dx.util.ExceptionWithContext;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.security.DigestException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.zip.Adler32;
+
+import static com.android.dx.dex.file.MixedItemSection.SortType;
+
+/**
+ * Representation of an entire {@code .dex} (Dalvik EXecutable)
+ * file, which itself consists of a set of Dalvik classes.
+ */
+public final class DexFile {
+ /** {@code non-null;} word data section */
+ private final MixedItemSection wordData;
+
+ /**
+ * {@code non-null;} type lists section. This is word data, but separating
+ * it from {@link #wordData} helps break what would otherwise be a
+ * circular dependency between the that and {@link #protoIds}.
+ */
+ private final MixedItemSection typeLists;
+
+ /**
+ * {@code non-null;} map section. The map needs to be in a section by itself
+ * for the self-reference mechanics to work in a reasonably
+ * straightforward way. See {@link MapItem#addMap} for more detail.
+ */
+ private final MixedItemSection map;
+
+ /** {@code non-null;} string data section */
+ private final MixedItemSection stringData;
+
+ /** {@code non-null;} string identifiers section */
+ private final StringIdsSection stringIds;
+
+ /** {@code non-null;} type identifiers section */
+ private final TypeIdsSection typeIds;
+
+ /** {@code non-null;} prototype identifiers section */
+ private final ProtoIdsSection protoIds;
+
+ /** {@code non-null;} field identifiers section */
+ private final FieldIdsSection fieldIds;
+
+ /** {@code non-null;} method identifiers section */
+ private final MethodIdsSection methodIds;
+
+ /** {@code non-null;} class definitions section */
+ private final ClassDefsSection classDefs;
+
+ /** {@code non-null;} class data section */
+ private final MixedItemSection classData;
+
+ /** {@code non-null;} byte data section */
+ private final MixedItemSection byteData;
+
+ /** {@code non-null;} file header */
+ private final HeaderSection header;
+
+ /**
+ * {@code non-null;} array of sections in the order they will appear in the
+ * final output file
+ */
+ private final Section[] sections;
+
+ /** {@code >= -1;} total file size or {@code -1} if unknown */
+ private int fileSize;
+
+ /** {@code >= 40;} maximum width of the file dump */
+ private int dumpWidth;
+
+ /**
+ * Constructs an instance. It is initially empty.
+ */
+ public DexFile() {
+ header = new HeaderSection(this);
+ typeLists = new MixedItemSection(null, this, 4, SortType.NONE);
+ wordData = new MixedItemSection("word_data", this, 4, SortType.TYPE);
+ stringData =
+ new MixedItemSection("string_data", this, 1, SortType.INSTANCE);
+ classData = new MixedItemSection(null, this, 1, SortType.NONE);
+ byteData = new MixedItemSection("byte_data", this, 1, SortType.TYPE);
+ stringIds = new StringIdsSection(this);
+ typeIds = new TypeIdsSection(this);
+ protoIds = new ProtoIdsSection(this);
+ fieldIds = new FieldIdsSection(this);
+ methodIds = new MethodIdsSection(this);
+ classDefs = new ClassDefsSection(this);
+ map = new MixedItemSection("map", this, 4, SortType.NONE);
+
+ /*
+ * This is the list of sections in the order they appear in
+ * the final output.
+ */
+ sections = new Section[] {
+ header, stringIds, typeIds, protoIds, fieldIds, methodIds,
+ classDefs, wordData, typeLists, stringData, byteData,
+ classData, map };
+
+ fileSize = -1;
+ dumpWidth = 79;
+ }
+
+ /**
+ * Adds a class to this instance. It is illegal to attempt to add more
+ * than one class with the same name.
+ *
+ * @param clazz {@code non-null;} the class to add
+ */
+ public void add(ClassDefItem clazz) {
+ classDefs.add(clazz);
+ }
+
+ /**
+ * Gets the class definition with the given name, if any.
+ *
+ * @param name {@code non-null;} the class name to look for
+ * @return {@code null-ok;} the class with the given name, or {@code null}
+ * if there is no such class
+ */
+ public ClassDefItem getClassOrNull(String name) {
+ try {
+ Type type = Type.internClassName(name);
+ return (ClassDefItem) classDefs.get(new CstType(type));
+ } catch (IllegalArgumentException ex) {
+ // Translate exception, per contract.
+ return null;
+ }
+ }
+
+ /**
+ * Writes the contents of this instance as either a binary or a
+ * human-readable form, or both.
+ *
+ * @param out {@code null-ok;} where to write to
+ * @param humanOut {@code null-ok;} where to write human-oriented output to
+ * @param verbose whether to be verbose when writing human-oriented output
+ */
+ public void writeTo(OutputStream out, Writer humanOut, boolean verbose)
+ throws IOException {
+ boolean annotate = (humanOut != null);
+ ByteArrayAnnotatedOutput result = toDex0(annotate, verbose);
+
+ if (out != null) {
+ out.write(result.getArray());
+ }
+
+ if (annotate) {
+ result.writeAnnotationsTo(humanOut);
+ }
+ }
+
+ /**
+ * Returns the contents of this instance as a {@code .dex} file,
+ * in {@code byte[]} form.
+ *
+ * @param humanOut {@code null-ok;} where to write human-oriented output to
+ * @param verbose whether to be verbose when writing human-oriented output
+ * @return {@code non-null;} a {@code .dex} file for this instance
+ */
+ public byte[] toDex(Writer humanOut, boolean verbose)
+ throws IOException {
+ boolean annotate = (humanOut != null);
+ ByteArrayAnnotatedOutput result = toDex0(annotate, verbose);
+
+ if (annotate) {
+ result.writeAnnotationsTo(humanOut);
+ }
+
+ return result.getArray();
+ }
+
+ /**
+ * Sets the maximum width of the human-oriented dump of the instance.
+ *
+ * @param dumpWidth {@code >= 40;} the width
+ */
+ public void setDumpWidth(int dumpWidth) {
+ if (dumpWidth < 40) {
+ throw new IllegalArgumentException("dumpWidth < 40");
+ }
+
+ this.dumpWidth = dumpWidth;
+ }
+
+ /**
+ * Gets the total file size, if known.
+ *
+ * <p>This is package-scope in order to allow
+ * the {@link HeaderSection} to set itself up properly.</p>
+ *
+ * @return {@code >= 0;} the total file size
+ * @throws RuntimeException thrown if the file size is not yet known
+ */
+ /*package*/ int getFileSize() {
+ if (fileSize < 0) {
+ throw new RuntimeException("file size not yet known");
+ }
+
+ return fileSize;
+ }
+
+ /**
+ * Gets the string data section.
+ *
+ * <p>This is package-scope in order to allow
+ * the various {@link Item} instances to add items to the
+ * instance.</p>
+ *
+ * @return {@code non-null;} the string data section
+ */
+ /*package*/ MixedItemSection getStringData() {
+ return stringData;
+ }
+
+ /**
+ * Gets the word data section.
+ *
+ * <p>This is package-scope in order to allow
+ * the various {@link Item} instances to add items to the
+ * instance.</p>
+ *
+ * @return {@code non-null;} the word data section
+ */
+ /*package*/ MixedItemSection getWordData() {
+ return wordData;
+ }
+
+ /**
+ * Gets the type lists section.
+ *
+ * <p>This is package-scope in order to allow
+ * the various {@link Item} instances to add items to the
+ * instance.</p>
+ *
+ * @return {@code non-null;} the word data section
+ */
+ /*package*/ MixedItemSection getTypeLists() {
+ return typeLists;
+ }
+
+ /**
+ * Gets the map section.
+ *
+ * <p>This is package-scope in order to allow the header section
+ * to query it.</p>
+ *
+ * @return {@code non-null;} the map section
+ */
+ /*package*/ MixedItemSection getMap() {
+ return map;
+ }
+
+ /**
+ * Gets the string identifiers section.
+ *
+ * <p>This is package-scope in order to allow
+ * the various {@link Item} instances to add items to the
+ * instance.</p>
+ *
+ * @return {@code non-null;} the string identifiers section
+ */
+ /*package*/ StringIdsSection getStringIds() {
+ return stringIds;
+ }
+
+ /**
+ * Gets the class definitions section.
+ *
+ * <p>This is package-scope in order to allow
+ * the various {@link Item} instances to add items to the
+ * instance.</p>
+ *
+ * @return {@code non-null;} the class definitions section
+ */
+ /*package*/ ClassDefsSection getClassDefs() {
+ return classDefs;
+ }
+
+ /**
+ * Gets the class data section.
+ *
+ * <p>This is package-scope in order to allow
+ * the various {@link Item} instances to add items to the
+ * instance.</p>
+ *
+ * @return {@code non-null;} the class data section
+ */
+ /*package*/ MixedItemSection getClassData() {
+ return classData;
+ }
+
+ /**
+ * Gets the type identifiers section.
+ *
+ * <p>This is package-scope in order to allow
+ * the various {@link Item} instances to add items to the
+ * instance.</p>
+ *
+ * @return {@code non-null;} the class identifiers section
+ */
+ /*package*/ TypeIdsSection getTypeIds() {
+ return typeIds;
+ }
+
+ /**
+ * Gets the prototype identifiers section.
+ *
+ * <p>This is package-scope in order to allow
+ * the various {@link Item} instances to add items to the
+ * instance.</p>
+ *
+ * @return {@code non-null;} the prototype identifiers section
+ */
+ /*package*/ ProtoIdsSection getProtoIds() {
+ return protoIds;
+ }
+
+ /**
+ * Gets the field identifiers section.
+ *
+ * <p>This is package-scope in order to allow
+ * the various {@link Item} instances to add items to the
+ * instance.</p>
+ *
+ * @return {@code non-null;} the field identifiers section
+ */
+ /*package*/ FieldIdsSection getFieldIds() {
+ return fieldIds;
+ }
+
+ /**
+ * Gets the method identifiers section.
+ *
+ * <p>This is package-scope in order to allow
+ * the various {@link Item} instances to add items to the
+ * instance.</p>
+ *
+ * @return {@code non-null;} the method identifiers section
+ */
+ /*package*/ MethodIdsSection getMethodIds() {
+ return methodIds;
+ }
+
+ /**
+ * Gets the byte data section.
+ *
+ * <p>This is package-scope in order to allow
+ * the various {@link Item} instances to add items to the
+ * instance.</p>
+ *
+ * @return {@code non-null;} the byte data section
+ */
+ /*package*/ MixedItemSection getByteData() {
+ return byteData;
+ }
+
+ /**
+ * Gets the first section of the file that is to be considered
+ * part of the data section.
+ *
+ * <p>This is package-scope in order to allow the header section
+ * to query it.</p>
+ *
+ * @return {@code non-null;} the section
+ */
+ /*package*/ Section getFirstDataSection() {
+ return wordData;
+ }
+
+ /**
+ * Gets the last section of the file that is to be considered
+ * part of the data section.
+ *
+ * <p>This is package-scope in order to allow the header section
+ * to query it.</p>
+ *
+ * @return {@code non-null;} the section
+ */
+ /*package*/ Section getLastDataSection() {
+ return map;
+ }
+
+ /**
+ * Interns the given constant in the appropriate section of this
+ * instance, or do nothing if the given constant isn't the sort
+ * that should be interned.
+ *
+ * @param cst {@code non-null;} constant to possibly intern
+ */
+ /*package*/ void internIfAppropriate(Constant cst) {
+ if (cst instanceof CstString) {
+ stringIds.intern((CstString) cst);
+ } else if (cst instanceof CstUtf8) {
+ stringIds.intern((CstUtf8) cst);
+ } else if (cst instanceof CstType) {
+ typeIds.intern((CstType) cst);
+ } else if (cst instanceof CstBaseMethodRef) {
+ methodIds.intern((CstBaseMethodRef) cst);
+ } else if (cst instanceof CstFieldRef) {
+ fieldIds.intern((CstFieldRef) cst);
+ } else if (cst instanceof CstEnumRef) {
+ fieldIds.intern(((CstEnumRef) cst).getFieldRef());
+ } else if (cst == null) {
+ throw new NullPointerException("cst == null");
+ }
+ }
+
+ /**
+ * Gets the {@link IndexedItem} corresponding to the given constant,
+ * if it is a constant that has such a correspondence, or return
+ * {@code null} if it isn't such a constant. This will throw
+ * an exception if the given constant <i>should</i> have been found
+ * but wasn't.
+ *
+ * @param cst {@code non-null;} the constant to look up
+ * @return {@code null-ok;} its corresponding item, if it has a corresponding
+ * item, or {@code null} if it's not that sort of constant
+ */
+ /*package*/ IndexedItem findItemOrNull(Constant cst) {
+ IndexedItem item;
+
+ if (cst instanceof CstString) {
+ return stringIds.get(cst);
+ } else if (cst instanceof CstType) {
+ return typeIds.get(cst);
+ } else if (cst instanceof CstBaseMethodRef) {
+ return methodIds.get(cst);
+ } else if (cst instanceof CstFieldRef) {
+ return fieldIds.get(cst);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the contents of this instance as a {@code .dex} file,
+ * in a {@link ByteArrayAnnotatedOutput} instance.
+ *
+ * @param annotate whether or not to keep annotations
+ * @param verbose if annotating, whether to be verbose
+ * @return {@code non-null;} a {@code .dex} file for this instance
+ */
+ private ByteArrayAnnotatedOutput toDex0(boolean annotate,
+ boolean verbose) {
+ /*
+ * The following is ordered so that the prepare() calls which
+ * add items happen before the calls to the sections that get
+ * added to.
+ */
+
+ classDefs.prepare();
+ classData.prepare();
+ wordData.prepare();
+ byteData.prepare();
+ methodIds.prepare();
+ fieldIds.prepare();
+ protoIds.prepare();
+ typeLists.prepare();
+ typeIds.prepare();
+ stringIds.prepare();
+ stringData.prepare();
+ header.prepare();
+
+ // Place the sections within the file.
+
+ int count = sections.length;
+ int offset = 0;
+
+ for (int i = 0; i < count; i++) {
+ Section one = sections[i];
+ int placedAt = one.setFileOffset(offset);
+ if (placedAt < offset) {
+ throw new RuntimeException("bogus placement for section " + i);
+ }
+
+ try {
+ if (one == map) {
+ /*
+ * Inform the map of all the sections, and add it
+ * to the file. This can only be done after all
+ * the other items have been sorted and placed.
+ */
+ MapItem.addMap(sections, map);
+ map.prepare();
+ }
+
+ if (one instanceof MixedItemSection) {
+ /*
+ * Place the items of a MixedItemSection that just
+ * got placed.
+ */
+ ((MixedItemSection) one).placeItems();
+ }
+
+ offset = placedAt + one.writeSize();
+ } catch (RuntimeException ex) {
+ throw ExceptionWithContext.withContext(ex,
+ "...while writing section " + i);
+ }
+ }
+
+ // Write out all the sections.
+
+ fileSize = offset;
+ byte[] barr = new byte[fileSize];
+ ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(barr);
+
+ if (annotate) {
+ out.enableAnnotations(dumpWidth, verbose);
+ }
+
+ for (int i = 0; i < count; i++) {
+ try {
+ Section one = sections[i];
+ int zeroCount = one.getFileOffset() - out.getCursor();
+ if (zeroCount < 0) {
+ throw new ExceptionWithContext("excess write of " +
+ (-zeroCount));
+ }
+ out.writeZeroes(one.getFileOffset() - out.getCursor());
+ one.writeTo(out);
+ } catch (RuntimeException ex) {
+ ExceptionWithContext ec;
+ if (ex instanceof ExceptionWithContext) {
+ ec = (ExceptionWithContext) ex;
+ } else {
+ ec = new ExceptionWithContext(ex);
+ }
+ ec.addContext("...while writing section " + i);
+ throw ec;
+ }
+ }
+
+ if (out.getCursor() != fileSize) {
+ throw new RuntimeException("foreshortened write");
+ }
+
+ // Perform final bookkeeping.
+
+ calcSignature(barr);
+ calcChecksum(barr);
+
+ if (annotate) {
+ wordData.writeIndexAnnotation(out, ItemType.TYPE_CODE_ITEM,
+ "\nmethod code index:\n\n");
+ getStatistics().writeAnnotation(out);
+ out.finishAnnotating();
+ }
+
+ return out;
+ }
+
+ /**
+ * Generates and returns statistics for all the items in the file.
+ *
+ * @return {@code non-null;} the statistics
+ */
+ public Statistics getStatistics() {
+ Statistics stats = new Statistics();
+
+ for (Section s : sections) {
+ stats.addAll(s);
+ }
+
+ return stats;
+ }
+
+ /**
+ * Calculates the signature for the {@code .dex} file in the
+ * given array, and modify the array to contain it.
+ *
+ * @param bytes {@code non-null;} the bytes of the file
+ */
+ private static void calcSignature(byte[] bytes) {
+ MessageDigest md;
+
+ try {
+ md = MessageDigest.getInstance("SHA-1");
+ } catch (NoSuchAlgorithmException ex) {
+ throw new RuntimeException(ex);
+ }
+
+ md.update(bytes, 32, bytes.length - 32);
+
+ try {
+ int amt = md.digest(bytes, 12, 20);
+ if (amt != 20) {
+ throw new RuntimeException("unexpected digest write: " + amt +
+ " bytes");
+ }
+ } catch (DigestException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ /**
+ * Calculates the checksum for the {@code .dex} file in the
+ * given array, and modify the array to contain it.
+ *
+ * @param bytes {@code non-null;} the bytes of the file
+ */
+ private static void calcChecksum(byte[] bytes) {
+ Adler32 a32 = new Adler32();
+
+ a32.update(bytes, 12, bytes.length - 12);
+
+ int sum = (int) a32.getValue();
+
+ bytes[8] = (byte) sum;
+ bytes[9] = (byte) (sum >> 8);
+ bytes[10] = (byte) (sum >> 16);
+ bytes[11] = (byte) (sum >> 24);
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/EncodedArrayItem.java b/dx/src/com/android/dx/dex/file/EncodedArrayItem.java
new file mode 100644
index 0000000..4d904e7
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/EncodedArrayItem.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.annotation.Annotation;
+import com.android.dx.rop.annotation.AnnotationVisibility;
+import com.android.dx.rop.annotation.NameValuePair;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstAnnotation;
+import com.android.dx.rop.cst.CstArray;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.ByteArrayAnnotatedOutput;
+import com.android.dx.util.AnnotatedOutput;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+/**
+ * Encoded array of constant values.
+ */
+public final class EncodedArrayItem extends OffsettedItem {
+ /** the required alignment for instances of this class */
+ private static final int ALIGNMENT = 1;
+
+ /** {@code non-null;} the array to represent */
+ private final CstArray array;
+
+ /**
+ * {@code null-ok;} encoded form, ready for writing to a file; set during
+ * {@link #place0}
+ */
+ private byte[] encodedForm;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param array {@code non-null;} array to represent
+ */
+ public EncodedArrayItem(CstArray array) {
+ /*
+ * The write size isn't known up-front because (the variable-lengthed)
+ * leb128 type is used to represent some things.
+ */
+ super(ALIGNMENT, -1);
+
+ if (array == null) {
+ throw new NullPointerException("array == null");
+ }
+
+ this.array = array;
+ this.encodedForm = null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ItemType itemType() {
+ return ItemType.TYPE_ENCODED_ARRAY_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return array.hashCode();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected int compareTo0(OffsettedItem other) {
+ EncodedArrayItem otherArray = (EncodedArrayItem) other;
+
+ return array.compareTo(otherArray.array);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toHuman() {
+ return array.toHuman();
+ }
+
+ /** {@inheritDoc} */
+ public void addContents(DexFile file) {
+ ValueEncoder.addContents(file, array);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void place0(Section addedTo, int offset) {
+ // Encode the data and note the size.
+
+ ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput();
+ ValueEncoder encoder = new ValueEncoder(addedTo.getFile(), out);
+
+ encoder.writeArray(array, false);
+ encodedForm = out.toByteArray();
+ setWriteSize(encodedForm.length);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void writeTo0(DexFile file, AnnotatedOutput out) {
+ boolean annotates = out.annotates();
+
+ if (annotates) {
+ out.annotate(0, offsetString() + " encoded array");
+
+ /*
+ * The output is to be annotated, so redo the work previously
+ * done by place0(), except this time annotations will actually
+ * get emitted.
+ */
+ ValueEncoder encoder = new ValueEncoder(file, out);
+ encoder.writeArray(array, true);
+ } else {
+ out.write(encodedForm);
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/EncodedField.java b/dx/src/com/android/dx/dex/file/EncodedField.java
new file mode 100644
index 0000000..f2a8184
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/EncodedField.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+import com.android.dx.util.Leb128Utils;
+
+import java.io.PrintWriter;
+
+/**
+ * Representation of a field of a class, of any sort.
+ */
+public final class EncodedField extends EncodedMember
+ implements Comparable<EncodedField> {
+ /** {@code non-null;} constant for the field */
+ private final CstFieldRef field;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param field {@code non-null;} constant for the field
+ * @param accessFlags access flags
+ */
+ public EncodedField(CstFieldRef field, int accessFlags) {
+ super(accessFlags);
+
+ if (field == null) {
+ throw new NullPointerException("field == null");
+ }
+
+ /*
+ * TODO: Maybe check accessFlags, at least for
+ * easily-checked stuff?
+ */
+
+ this.field = field;
+ }
+
+ /** {@inheritDoc} */
+ public int hashCode() {
+ return field.hashCode();
+ }
+
+ /** {@inheritDoc} */
+ public boolean equals(Object other) {
+ if (! (other instanceof EncodedField)) {
+ return false;
+ }
+
+ return compareTo((EncodedField) other) == 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p><b>Note:</b> This compares the method constants only,
+ * ignoring any associated code, because it should never be the
+ * case that two different items with the same method constant
+ * ever appear in the same list (or same file, even).</p>
+ */
+ public int compareTo(EncodedField other) {
+ return field.compareTo(other.field);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer(100);
+
+ sb.append(getClass().getName());
+ sb.append('{');
+ sb.append(Hex.u2(getAccessFlags()));
+ sb.append(' ');
+ sb.append(field);
+ sb.append('}');
+ return sb.toString();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addContents(DexFile file) {
+ FieldIdsSection fieldIds = file.getFieldIds();
+ fieldIds.intern(field);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public CstUtf8 getName() {
+ return field.getNat().getName();
+ }
+
+ /** {@inheritDoc} */
+ public String toHuman() {
+ return field.toHuman();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void debugPrint(PrintWriter out, boolean verbose) {
+ // TODO: Maybe put something better here?
+ out.println(toString());
+ }
+
+ /**
+ * Gets the constant for the field.
+ *
+ * @return {@code non-null;} the constant
+ */
+ public CstFieldRef getRef() {
+ return field;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int encode(DexFile file, AnnotatedOutput out,
+ int lastIndex, int dumpSeq) {
+ int fieldIdx = file.getFieldIds().indexOf(field);
+ int diff = fieldIdx - lastIndex;
+ int accessFlags = getAccessFlags();
+
+ if (out.annotates()) {
+ out.annotate(0, String.format(" [%x] %s", dumpSeq,
+ field.toHuman()));
+ out.annotate(Leb128Utils.unsignedLeb128Size(diff),
+ " field_idx: " + Hex.u4(fieldIdx));
+ out.annotate(Leb128Utils.unsignedLeb128Size(accessFlags),
+ " access_flags: " +
+ AccessFlags.fieldString(accessFlags));
+ }
+
+ out.writeUnsignedLeb128(diff);
+ out.writeUnsignedLeb128(accessFlags);
+
+ return fieldIdx;
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/EncodedMember.java b/dx/src/com/android/dx/dex/file/EncodedMember.java
new file mode 100644
index 0000000..5099325
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/EncodedMember.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.ToHuman;
+
+import java.io.PrintWriter;
+
+/**
+ * Representation of a member (field or method) of a class, for the
+ * purposes of encoding it inside a {@link ClassDataItem}.
+ */
+public abstract class EncodedMember implements ToHuman {
+ /** access flags */
+ private final int accessFlags;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param accessFlags access flags for the member
+ */
+ public EncodedMember(int accessFlags) {
+ this.accessFlags = accessFlags;
+ }
+
+ /**
+ * Gets the access flags.
+ *
+ * @return the access flags
+ */
+ public final int getAccessFlags() {
+ return accessFlags;
+ }
+
+ /**
+ * Gets the name.
+ *
+ * @return {@code non-null;} the name
+ */
+ public abstract CstUtf8 getName();
+
+ /**
+ * Does a human-friendly dump of this instance.
+ *
+ * @param out {@code non-null;} where to dump
+ * @param verbose whether to be verbose with the output
+ */
+ public abstract void debugPrint(PrintWriter out, boolean verbose);
+
+ /**
+ * Populates a {@link DexFile} with items from within this instance.
+ *
+ * @param file {@code non-null;} the file to populate
+ */
+ public abstract void addContents(DexFile file);
+
+ /**
+ * Encodes this instance to the given output.
+ *
+ * @param file {@code non-null;} file this instance is part of
+ * @param out {@code non-null;} where to write to
+ * @param lastIndex {@code >= 0;} the previous member index value encoded, or
+ * {@code 0} if this is the first element to encode
+ * @param dumpSeq {@code >= 0;} sequence number of this instance for
+ * annotation purposes
+ * @return {@code >= 0;} the member index value that was encoded
+ */
+ public abstract int encode(DexFile file, AnnotatedOutput out,
+ int lastIndex, int dumpSeq);
+}
diff --git a/dx/src/com/android/dx/dex/file/EncodedMethod.java b/dx/src/com/android/dx/dex/file/EncodedMethod.java
new file mode 100644
index 0000000..1b0770f
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/EncodedMethod.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.dex.code.DalvCode;
+import com.android.dx.rop.code.AccessFlags;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+import com.android.dx.util.Leb128Utils;
+
+import java.io.PrintWriter;
+
+/**
+ * Class that representats a method of a class.
+ */
+public final class EncodedMethod extends EncodedMember
+ implements Comparable<EncodedMethod> {
+ /** {@code non-null;} constant for the method */
+ private final CstMethodRef method;
+
+ /**
+ * {@code null-ok;} code for the method, if the method is neither
+ * {@code abstract} nor {@code native}
+ */
+ private final CodeItem code;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param method {@code non-null;} constant for the method
+ * @param accessFlags access flags
+ * @param code {@code null-ok;} code for the method, if it is neither
+ * {@code abstract} nor {@code native}
+ * @param throwsList {@code non-null;} list of possibly-thrown exceptions,
+ * just used in generating debugging output (listings)
+ */
+ public EncodedMethod(CstMethodRef method, int accessFlags,
+ DalvCode code, TypeList throwsList) {
+ super(accessFlags);
+
+ if (method == null) {
+ throw new NullPointerException("method == null");
+ }
+
+ this.method = method;
+
+ if (code == null) {
+ this.code = null;
+ } else {
+ boolean isStatic = (accessFlags & AccessFlags.ACC_STATIC) != 0;
+ this.code = new CodeItem(method, code, isStatic, throwsList);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public boolean equals(Object other) {
+ if (! (other instanceof EncodedMethod)) {
+ return false;
+ }
+
+ return compareTo((EncodedMethod) other) == 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p><b>Note:</b> This compares the method constants only,
+ * ignoring any associated code, because it should never be the
+ * case that two different items with the same method constant
+ * ever appear in the same list (or same file, even).</p>
+ */
+ public int compareTo(EncodedMethod other) {
+ return method.compareTo(other.method);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer(100);
+
+ sb.append(getClass().getName());
+ sb.append('{');
+ sb.append(Hex.u2(getAccessFlags()));
+ sb.append(' ');
+ sb.append(method);
+
+ if (code != null) {
+ sb.append(' ');
+ sb.append(code);
+ }
+
+ sb.append('}');
+
+ return sb.toString();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addContents(DexFile file) {
+ MethodIdsSection methodIds = file.getMethodIds();
+ MixedItemSection wordData = file.getWordData();
+
+ methodIds.intern(method);
+
+ if (code != null) {
+ wordData.add(code);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public final String toHuman() {
+ return method.toHuman();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final CstUtf8 getName() {
+ return method.getNat().getName();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void debugPrint(PrintWriter out, boolean verbose) {
+ if (code == null) {
+ out.println(getRef().toHuman() + ": abstract or native");
+ } else {
+ code.debugPrint(out, " ", verbose);
+ }
+ }
+
+ /**
+ * Gets the constant for the method.
+ *
+ * @return {@code non-null;} the constant
+ */
+ public final CstMethodRef getRef() {
+ return method;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int encode(DexFile file, AnnotatedOutput out,
+ int lastIndex, int dumpSeq) {
+ int methodIdx = file.getMethodIds().indexOf(method);
+ int diff = methodIdx - lastIndex;
+ int accessFlags = getAccessFlags();
+ int codeOff = OffsettedItem.getAbsoluteOffsetOr0(code);
+ boolean hasCode = (codeOff != 0);
+ boolean shouldHaveCode = (accessFlags &
+ (AccessFlags.ACC_ABSTRACT | AccessFlags.ACC_NATIVE)) == 0;
+
+ /*
+ * Verify that code appears if and only if a method is
+ * declared to have it.
+ */
+ if (hasCode != shouldHaveCode) {
+ throw new UnsupportedOperationException(
+ "code vs. access_flags mismatch");
+ }
+
+ if (out.annotates()) {
+ out.annotate(0, String.format(" [%x] %s", dumpSeq,
+ method.toHuman()));
+ out.annotate(Leb128Utils.unsignedLeb128Size(diff),
+ " method_idx: " + Hex.u4(methodIdx));
+ out.annotate(Leb128Utils.unsignedLeb128Size(accessFlags),
+ " access_flags: " +
+ AccessFlags.methodString(accessFlags));
+ out.annotate(Leb128Utils.unsignedLeb128Size(codeOff),
+ " code_off: " + Hex.u4(codeOff));
+ }
+
+ out.writeUnsignedLeb128(diff);
+ out.writeUnsignedLeb128(accessFlags);
+ out.writeUnsignedLeb128(codeOff);
+
+ return methodIdx;
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/FieldAnnotationStruct.java b/dx/src/com/android/dx/dex/file/FieldAnnotationStruct.java
new file mode 100644
index 0000000..f363d41
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/FieldAnnotationStruct.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.annotation.Annotations;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+import com.android.dx.util.ToHuman;
+
+/**
+ * Association of a field and its annotations.
+ */
+public final class FieldAnnotationStruct
+ implements ToHuman, Comparable<FieldAnnotationStruct> {
+ /** {@code non-null;} the field in question */
+ private final CstFieldRef field;
+
+ /** {@code non-null;} the associated annotations */
+ private AnnotationSetItem annotations;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param field {@code non-null;} the field in question
+ * @param annotations {@code non-null;} the associated annotations
+ */
+ public FieldAnnotationStruct(CstFieldRef field,
+ AnnotationSetItem annotations) {
+ if (field == null) {
+ throw new NullPointerException("field == null");
+ }
+
+ if (annotations == null) {
+ throw new NullPointerException("annotations == null");
+ }
+
+ this.field = field;
+ this.annotations = annotations;
+ }
+
+ /** {@inheritDoc} */
+ public int hashCode() {
+ return field.hashCode();
+ }
+
+ /** {@inheritDoc} */
+ public boolean equals(Object other) {
+ if (! (other instanceof FieldAnnotationStruct)) {
+ return false;
+ }
+
+ return field.equals(((FieldAnnotationStruct) other).field);
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(FieldAnnotationStruct other) {
+ return field.compareTo(other.field);
+ }
+
+ /** {@inheritDoc} */
+ public void addContents(DexFile file) {
+ FieldIdsSection fieldIds = file.getFieldIds();
+ MixedItemSection wordData = file.getWordData();
+
+ fieldIds.intern(field);
+ annotations = wordData.intern(annotations);
+ }
+
+ /** {@inheritDoc} */
+ public void writeTo(DexFile file, AnnotatedOutput out) {
+ int fieldIdx = file.getFieldIds().indexOf(field);
+ int annotationsOff = annotations.getAbsoluteOffset();
+
+ if (out.annotates()) {
+ out.annotate(0, " " + field.toHuman());
+ out.annotate(4, " field_idx: " + Hex.u4(fieldIdx));
+ out.annotate(4, " annotations_off: " +
+ Hex.u4(annotationsOff));
+ }
+
+ out.writeInt(fieldIdx);
+ out.writeInt(annotationsOff);
+ }
+
+ /** {@inheritDoc} */
+ public String toHuman() {
+ return field.toHuman() + ": " + annotations;
+ }
+
+ /**
+ * Gets the field this item is for.
+ *
+ * @return {@code non-null;} the field
+ */
+ public CstFieldRef getField() {
+ return field;
+ }
+
+ /**
+ * Gets the associated annotations.
+ *
+ * @return {@code non-null;} the annotations
+ */
+ public Annotations getAnnotations() {
+ return annotations.getAnnotations();
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/FieldIdItem.java b/dx/src/com/android/dx/dex/file/FieldIdItem.java
new file mode 100644
index 0000000..ecb1d3d
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/FieldIdItem.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.CstFieldRef;
+
+/**
+ * Representation of a field reference inside a Dalvik file.
+ */
+public final class FieldIdItem extends MemberIdItem {
+ /**
+ * Constructs an instance.
+ *
+ * @param field {@code non-null;} the constant for the field
+ */
+ public FieldIdItem(CstFieldRef field) {
+ super(field);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ItemType itemType() {
+ return ItemType.TYPE_FIELD_ID_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addContents(DexFile file) {
+ super.addContents(file);
+
+ TypeIdsSection typeIds = file.getTypeIds();
+ typeIds.intern(getFieldRef().getType());
+ }
+
+ /**
+ * Gets the field constant.
+ *
+ * @return {@code non-null;} the constant
+ */
+ public CstFieldRef getFieldRef() {
+ return (CstFieldRef) getRef();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected int getTypoidIdx(DexFile file) {
+ TypeIdsSection typeIds = file.getTypeIds();
+ return typeIds.indexOf(getFieldRef().getType());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected String getTypoidName() {
+ return "type_idx";
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/FieldIdsSection.java b/dx/src/com/android/dx/dex/file/FieldIdsSection.java
new file mode 100644
index 0000000..c320731
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/FieldIdsSection.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+import java.util.Collection;
+import java.util.TreeMap;
+
+/**
+ * Field refs list section of a {@code .dex} file.
+ */
+public final class FieldIdsSection extends MemberIdsSection {
+ /**
+ * {@code non-null;} map from field constants to {@link
+ * FieldIdItem} instances
+ */
+ private final TreeMap<CstFieldRef, FieldIdItem> fieldIds;
+
+ /**
+ * Constructs an instance. The file offset is initially unknown.
+ *
+ * @param file {@code non-null;} file that this instance is part of
+ */
+ public FieldIdsSection(DexFile file) {
+ super("field_ids", file);
+
+ fieldIds = new TreeMap<CstFieldRef, FieldIdItem>();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Collection<? extends Item> items() {
+ return fieldIds.values();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public IndexedItem get(Constant cst) {
+ if (cst == null) {
+ throw new NullPointerException("cst == null");
+ }
+
+ throwIfNotPrepared();
+
+ IndexedItem result = fieldIds.get((CstFieldRef) cst);
+
+ if (result == null) {
+ throw new IllegalArgumentException("not found");
+ }
+
+ return result;
+ }
+
+ /**
+ * Writes the portion of the file header that refers to this instance.
+ *
+ * @param out {@code non-null;} where to write
+ */
+ public void writeHeaderPart(AnnotatedOutput out) {
+ throwIfNotPrepared();
+
+ int sz = fieldIds.size();
+ int offset = (sz == 0) ? 0 : getFileOffset();
+
+ if (out.annotates()) {
+ out.annotate(4, "field_ids_size: " + Hex.u4(sz));
+ out.annotate(4, "field_ids_off: " + Hex.u4(offset));
+ }
+
+ out.writeInt(sz);
+ out.writeInt(offset);
+ }
+
+ /**
+ * Interns an element into this instance.
+ *
+ * @param field {@code non-null;} the reference to intern
+ * @return {@code non-null;} the interned reference
+ */
+ public FieldIdItem intern(CstFieldRef field) {
+ if (field == null) {
+ throw new NullPointerException("field == null");
+ }
+
+ throwIfPrepared();
+
+ FieldIdItem result = fieldIds.get(field);
+
+ if (result == null) {
+ result = new FieldIdItem(field);
+ fieldIds.put(field, result);
+ }
+
+ return result;
+ }
+
+ /**
+ * Gets the index of the given reference, which must have been added
+ * to this instance.
+ *
+ * @param ref {@code non-null;} the reference to look up
+ * @return {@code >= 0;} the reference's index
+ */
+ public int indexOf(CstFieldRef ref) {
+ if (ref == null) {
+ throw new NullPointerException("ref == null");
+ }
+
+ throwIfNotPrepared();
+
+ FieldIdItem item = fieldIds.get(ref);
+
+ if (item == null) {
+ throw new IllegalArgumentException("not found");
+ }
+
+ return item.getIndex();
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/HeaderItem.java b/dx/src/com/android/dx/dex/file/HeaderItem.java
new file mode 100644
index 0000000..f95ff44
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/HeaderItem.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+/**
+ * File header section of a {@code .dex} file.
+ */
+public final class HeaderItem extends IndexedItem {
+ /**
+ * {@code non-null;} the file format magic number, represented as the
+ * low-order bytes of a string
+ */
+ private static final String MAGIC = "dex\n035\0";
+
+ /** size of this section, in bytes */
+ private static final int HEADER_SIZE = 0x70;
+
+ /** the endianness tag */
+ private static final int ENDIAN_TAG = 0x12345678;
+
+ /**
+ * Constructs an instance.
+ */
+ public HeaderItem() {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ItemType itemType() {
+ return ItemType.TYPE_HEADER_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int writeSize() {
+ return HEADER_SIZE;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addContents(DexFile file) {
+ // Nothing to do here.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(DexFile file, AnnotatedOutput out) {
+ int mapOff = file.getMap().getFileOffset();
+ Section firstDataSection = file.getFirstDataSection();
+ Section lastDataSection = file.getLastDataSection();
+ int dataOff = firstDataSection.getFileOffset();
+ int dataSize = lastDataSection.getFileOffset() +
+ lastDataSection.writeSize() - dataOff;
+
+ if (out.annotates()) {
+ out.annotate(8, "magic: " + new CstUtf8(MAGIC).toQuoted());
+ out.annotate(4, "checksum");
+ out.annotate(20, "signature");
+ out.annotate(4, "file_size: " +
+ Hex.u4(file.getFileSize()));
+ out.annotate(4, "header_size: " + Hex.u4(HEADER_SIZE));
+ out.annotate(4, "endian_tag: " + Hex.u4(ENDIAN_TAG));
+ out.annotate(4, "link_size: 0");
+ out.annotate(4, "link_off: 0");
+ out.annotate(4, "map_off: " + Hex.u4(mapOff));
+ }
+
+ // Write the magic number.
+ for (int i = 0; i < 8; i++) {
+ out.writeByte(MAGIC.charAt(i));
+ }
+
+ // Leave space for the checksum and signature.
+ out.writeZeroes(24);
+
+ out.writeInt(file.getFileSize());
+ out.writeInt(HEADER_SIZE);
+ out.writeInt(ENDIAN_TAG);
+
+ /*
+ * Write zeroes for the link size and data, as the output
+ * isn't a staticly linked file.
+ */
+ out.writeZeroes(8);
+
+ out.writeInt(mapOff);
+
+ // Write out each section's respective header part.
+ file.getStringIds().writeHeaderPart(out);
+ file.getTypeIds().writeHeaderPart(out);
+ file.getProtoIds().writeHeaderPart(out);
+ file.getFieldIds().writeHeaderPart(out);
+ file.getMethodIds().writeHeaderPart(out);
+ file.getClassDefs().writeHeaderPart(out);
+
+ if (out.annotates()) {
+ out.annotate(4, "data_size: " + Hex.u4(dataSize));
+ out.annotate(4, "data_off: " + Hex.u4(dataOff));
+ }
+
+ out.writeInt(dataSize);
+ out.writeInt(dataOff);
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/HeaderSection.java b/dx/src/com/android/dx/dex/file/HeaderSection.java
new file mode 100644
index 0000000..21da488
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/HeaderSection.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.Constant;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * File header section of a {@code .dex} file.
+ */
+public final class HeaderSection extends UniformItemSection {
+ /** {@code non-null;} the list of the one item in the section */
+ private final List<HeaderItem> list;
+
+ /**
+ * Constructs an instance. The file offset is initially unknown.
+ *
+ * @param file {@code non-null;} file that this instance is part of
+ */
+ public HeaderSection(DexFile file) {
+ super(null, file, 4);
+
+ HeaderItem item = new HeaderItem();
+ item.setIndex(0);
+
+ this.list = Collections.singletonList(item);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public IndexedItem get(Constant cst) {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Collection<? extends Item> items() {
+ return list;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void orderItems() {
+ // Nothing to do here.
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/IdItem.java b/dx/src/com/android/dx/dex/file/IdItem.java
new file mode 100644
index 0000000..1bd2b5f
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/IdItem.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.CstType;
+
+/**
+ * Representation of a reference to an item inside a Dalvik file.
+ */
+public abstract class IdItem extends IndexedItem {
+ /**
+ * {@code non-null;} the type constant for the defining class of
+ * the reference
+ */
+ private final CstType type;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param type {@code non-null;} the type constant for the defining
+ * class of the reference
+ */
+ public IdItem(CstType type) {
+ if (type == null) {
+ throw new NullPointerException("type == null");
+ }
+
+ this.type = type;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addContents(DexFile file) {
+ TypeIdsSection typeIds = file.getTypeIds();
+ typeIds.intern(type);
+ }
+
+ /**
+ * Gets the type constant for the defining class of the
+ * reference.
+ *
+ * @return {@code non-null;} the type constant
+ */
+ public final CstType getDefiningClass() {
+ return type;
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/IndexedItem.java b/dx/src/com/android/dx/dex/file/IndexedItem.java
new file mode 100644
index 0000000..9ba4783
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/IndexedItem.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+/**
+ * An item in a Dalvik file which is referenced by index.
+ */
+public abstract class IndexedItem extends Item {
+ /** {@code >= -1;} assigned index of the item, or {@code -1} if not
+ * yet assigned */
+ private int index;
+
+ /**
+ * Constructs an instance. The index is initially unassigned.
+ */
+ public IndexedItem() {
+ index = -1;
+ }
+
+ /**
+ * Gets whether or not this instance has been assigned an index.
+ *
+ * @return {@code true} iff this instance has been assigned an index
+ */
+ public final boolean hasIndex() {
+ return (index >= 0);
+ }
+
+ /**
+ * Gets the item index.
+ *
+ * @return {@code >= 0;} the index
+ * @throws RuntimeException thrown if the item index is not yet assigned
+ */
+ public final int getIndex() {
+ if (index < 0) {
+ throw new RuntimeException("index not yet set");
+ }
+
+ return index;
+ }
+
+ /**
+ * Sets the item index. This method may only ever be called once
+ * per instance, and this will throw a {@code RuntimeException} if
+ * called a second (or subsequent) time.
+ *
+ * @param index {@code >= 0;} the item index
+ */
+ public final void setIndex(int index) {
+ if (this.index != -1) {
+ throw new RuntimeException("index already set");
+ }
+
+ this.index = index;
+ }
+
+ /**
+ * Gets the index of this item as a string, suitable for including in
+ * annotations.
+ *
+ * @return {@code non-null;} the index string
+ */
+ public final String indexString() {
+ return '[' + Integer.toHexString(index) + ']';
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/Item.java b/dx/src/com/android/dx/dex/file/Item.java
new file mode 100644
index 0000000..cf2b380
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/Item.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.util.AnnotatedOutput;
+
+/**
+ * Base class for any structurally-significant and (potentially)
+ * repeated piece of a Dalvik file.
+ */
+public abstract class Item {
+ /**
+ * Constructs an instance.
+ */
+ public Item() {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Returns the item type for this instance.
+ *
+ * @return {@code non-null;} the item type
+ */
+ public abstract ItemType itemType();
+
+ /**
+ * Returns the human name for the particular type of item this
+ * instance is.
+ *
+ * @return {@code non-null;} the name
+ */
+ public final String typeName() {
+ return itemType().toHuman();
+ }
+
+ /**
+ * Gets the size of this instance when written, in bytes.
+ *
+ * @return {@code >= 0;} the write size
+ */
+ public abstract int writeSize();
+
+ /**
+ * Populates a {@link DexFile} with items from within this instance.
+ * This will <i>not</i> add an item to the file for this instance itself
+ * (which should have been done by whatever refers to this instance).
+ *
+ * <p><b>Note:</b> Subclasses must override this to do something
+ * appropriate.</p>
+ *
+ * @param file {@code non-null;} the file to populate
+ */
+ public abstract void addContents(DexFile file);
+
+ /**
+ * Writes the representation of this instance to the given data section,
+ * using the given {@link DexFile} to look things up as needed.
+ * If this instance keeps track of its offset, then this method will
+ * note the written offset and will also throw an exception if this
+ * instance has already been written.
+ *
+ * @param file {@code non-null;} the file to use for reference
+ * @param out {@code non-null;} where to write to
+ */
+ public abstract void writeTo(DexFile file, AnnotatedOutput out);
+}
diff --git a/dx/src/com/android/dx/dex/file/ItemType.java b/dx/src/com/android/dx/dex/file/ItemType.java
new file mode 100644
index 0000000..2fe97ab
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/ItemType.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.util.ToHuman;
+
+/**
+ * Enumeration of all the top-level item types.
+ */
+public enum ItemType implements ToHuman {
+ TYPE_HEADER_ITEM( 0x0000, "header_item"),
+ TYPE_STRING_ID_ITEM( 0x0001, "string_id_item"),
+ TYPE_TYPE_ID_ITEM( 0x0002, "type_id_item"),
+ TYPE_PROTO_ID_ITEM( 0x0003, "proto_id_item"),
+ TYPE_FIELD_ID_ITEM( 0x0004, "field_id_item"),
+ TYPE_METHOD_ID_ITEM( 0x0005, "method_id_item"),
+ TYPE_CLASS_DEF_ITEM( 0x0006, "class_def_item"),
+ TYPE_MAP_LIST( 0x1000, "map_list"),
+ TYPE_TYPE_LIST( 0x1001, "type_list"),
+ TYPE_ANNOTATION_SET_REF_LIST( 0x1002, "annotation_set_ref_list"),
+ TYPE_ANNOTATION_SET_ITEM( 0x1003, "annotation_set_item"),
+ TYPE_CLASS_DATA_ITEM( 0x2000, "class_data_item"),
+ TYPE_CODE_ITEM( 0x2001, "code_item"),
+ TYPE_STRING_DATA_ITEM( 0x2002, "string_data_item"),
+ TYPE_DEBUG_INFO_ITEM( 0x2003, "debug_info_item"),
+ TYPE_ANNOTATION_ITEM( 0x2004, "annotation_item"),
+ TYPE_ENCODED_ARRAY_ITEM( 0x2005, "encoded_array_item"),
+ TYPE_ANNOTATIONS_DIRECTORY_ITEM(0x2006, "annotations_directory_item"),
+ TYPE_MAP_ITEM( -1, "map_item"),
+ TYPE_TYPE_ITEM( -1, "type_item"),
+ TYPE_EXCEPTION_HANDLER_ITEM( -1, "exception_handler_item"),
+ TYPE_ANNOTATION_SET_REF_ITEM( -1, "annotation_set_ref_item");
+
+ /** value when represented in a {@link MapItem} */
+ private final int mapValue;
+
+ /** {@code non-null;} name of the type */
+ private final String typeName;
+
+ /** {@code non-null;} the short human name */
+ private final String humanName;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param mapValue value when represented in a {@link MapItem}
+ * @param typeName {@code non-null;} name of the type
+ */
+ private ItemType(int mapValue, String typeName) {
+ this.mapValue = mapValue;
+ this.typeName = typeName;
+
+ // Make the human name.
+ String human = typeName;
+ if (human.endsWith("_item")) {
+ human = human.substring(0, human.length() - 5);
+ }
+ this.humanName = human.replace('_', ' ');
+ }
+
+ /**
+ * Gets the map value.
+ *
+ * @return the map value
+ */
+ public int getMapValue() {
+ return mapValue;
+ }
+
+ /**
+ * Gets the type name.
+ *
+ * @return {@code non-null;} the type name
+ */
+ public String getTypeName() {
+ return typeName;
+ }
+
+ /** {@inheritDoc} */
+ public String toHuman() {
+ return humanName;
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/MapItem.java b/dx/src/com/android/dx/dex/file/MapItem.java
new file mode 100644
index 0000000..d78dc91
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/MapItem.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+import java.util.ArrayList;
+
+/**
+ * Class that represents a map item.
+ */
+public final class MapItem extends OffsettedItem {
+ /** file alignment of this class, in bytes */
+ private static final int ALIGNMENT = 4;
+
+ /** write size of this class, in bytes: three {@code uint}s */
+ private static final int WRITE_SIZE = (4 * 3);
+
+ /** {@code non-null;} item type this instance covers */
+ private final ItemType type;
+
+ /** {@code non-null;} section this instance covers */
+ private final Section section;
+
+ /**
+ * {@code null-ok;} first item covered or {@code null} if this is
+ * a self-reference
+ */
+ private final Item firstItem;
+
+ /**
+ * {@code null-ok;} last item covered or {@code null} if this is
+ * a self-reference
+ */
+ private final Item lastItem;
+
+ /**
+ * {@code > 0;} count of items covered; {@code 1} if this
+ * is a self-reference
+ */
+ private final int itemCount;
+
+ /**
+ * Constructs a list item with instances of this class representing
+ * the contents of the given array of sections, adding it to the
+ * given map section.
+ *
+ * @param sections {@code non-null;} the sections
+ * @param mapSection {@code non-null;} the section that the resulting map
+ * should be added to; it should be empty on entry to this method
+ */
+ public static void addMap(Section[] sections,
+ MixedItemSection mapSection) {
+ if (sections == null) {
+ throw new NullPointerException("sections == null");
+ }
+
+ if (mapSection.items().size() != 0) {
+ throw new IllegalArgumentException(
+ "mapSection.items().size() != 0");
+ }
+
+ ArrayList<MapItem> items = new ArrayList<MapItem>(50);
+
+ for (Section section : sections) {
+ ItemType currentType = null;
+ Item firstItem = null;
+ Item lastItem = null;
+ int count = 0;
+
+ for (Item item : section.items()) {
+ ItemType type = item.itemType();
+ if (type != currentType) {
+ if (count != 0) {
+ items.add(new MapItem(currentType, section,
+ firstItem, lastItem, count));
+ }
+ currentType = type;
+ firstItem = item;
+ count = 0;
+ }
+ lastItem = item;
+ count++;
+ }
+
+ if (count != 0) {
+ // Add a MapItem for the final items in the section.
+ items.add(new MapItem(currentType, section,
+ firstItem, lastItem, count));
+ } else if (section == mapSection) {
+ // Add a MapItem for the self-referential section.
+ items.add(new MapItem(mapSection));
+ }
+ }
+
+ mapSection.add(
+ new UniformListItem<MapItem>(ItemType.TYPE_MAP_LIST, items));
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param type {@code non-null;} item type this instance covers
+ * @param section {@code non-null;} section this instance covers
+ * @param firstItem {@code non-null;} first item covered
+ * @param lastItem {@code non-null;} last item covered
+ * @param itemCount {@code > 0;} count of items covered
+ */
+ private MapItem(ItemType type, Section section, Item firstItem,
+ Item lastItem, int itemCount) {
+ super(ALIGNMENT, WRITE_SIZE);
+
+ if (type == null) {
+ throw new NullPointerException("type == null");
+ }
+
+ if (section == null) {
+ throw new NullPointerException("section == null");
+ }
+
+ if (firstItem == null) {
+ throw new NullPointerException("firstItem == null");
+ }
+
+ if (lastItem == null) {
+ throw new NullPointerException("lastItem == null");
+ }
+
+ if (itemCount <= 0) {
+ throw new IllegalArgumentException("itemCount <= 0");
+ }
+
+ this.type = type;
+ this.section = section;
+ this.firstItem = firstItem;
+ this.lastItem = lastItem;
+ this.itemCount = itemCount;
+ }
+
+ /**
+ * Constructs a self-referential instance. This instance is meant to
+ * represent the section containing the {@code map_list}.
+ *
+ * @param section {@code non-null;} section this instance covers
+ */
+ private MapItem(Section section) {
+ super(ALIGNMENT, WRITE_SIZE);
+
+ if (section == null) {
+ throw new NullPointerException("section == null");
+ }
+
+ this.type = ItemType.TYPE_MAP_LIST;
+ this.section = section;
+ this.firstItem = null;
+ this.lastItem = null;
+ this.itemCount = 1;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ItemType itemType() {
+ return ItemType.TYPE_MAP_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer(100);
+
+ sb.append(getClass().getName());
+ sb.append('{');
+ sb.append(section.toString());
+ sb.append(' ');
+ sb.append(type.toHuman());
+ sb.append('}');
+
+ return sb.toString();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addContents(DexFile file) {
+ // We have nothing to add.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final String toHuman() {
+ return toString();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void writeTo0(DexFile file, AnnotatedOutput out) {
+ int value = type.getMapValue();
+ int offset;
+
+ if (firstItem == null) {
+ offset = section.getFileOffset();
+ } else {
+ offset = section.getAbsoluteItemOffset(firstItem);
+ }
+
+ if (out.annotates()) {
+ out.annotate(0, offsetString() + ' ' + type.getTypeName() +
+ " map");
+ out.annotate(2, " type: " + Hex.u2(value) + " // " +
+ type.toString());
+ out.annotate(2, " unused: 0");
+ out.annotate(4, " size: " + Hex.u4(itemCount));
+ out.annotate(4, " offset: " + Hex.u4(offset));
+ }
+
+ out.writeShort(value);
+ out.writeShort(0); // unused
+ out.writeInt(itemCount);
+ out.writeInt(offset);
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/MemberIdItem.java b/dx/src/com/android/dx/dex/file/MemberIdItem.java
new file mode 100644
index 0000000..d3a61d4
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/MemberIdItem.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.CstMemberRef;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+/**
+ * Representation of a member (field or method) reference inside a
+ * Dalvik file.
+ */
+public abstract class MemberIdItem extends IdItem {
+ /** size of instances when written out to a file, in bytes */
+ public static final int WRITE_SIZE = 8;
+
+ /** {@code non-null;} the constant for the member */
+ private final CstMemberRef cst;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param cst {@code non-null;} the constant for the member
+ */
+ public MemberIdItem(CstMemberRef cst) {
+ super(cst.getDefiningClass());
+
+ this.cst = cst;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int writeSize() {
+ return WRITE_SIZE;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addContents(DexFile file) {
+ super.addContents(file);
+
+ StringIdsSection stringIds = file.getStringIds();
+ stringIds.intern(getRef().getNat().getName());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final void writeTo(DexFile file, AnnotatedOutput out) {
+ TypeIdsSection typeIds = file.getTypeIds();
+ StringIdsSection stringIds = file.getStringIds();
+ CstNat nat = cst.getNat();
+ int classIdx = typeIds.indexOf(getDefiningClass());
+ int nameIdx = stringIds.indexOf(nat.getName());
+ int typoidIdx = getTypoidIdx(file);
+
+ if (out.annotates()) {
+ out.annotate(0, indexString() + ' ' + cst.toHuman());
+ out.annotate(2, " class_idx: " + Hex.u2(classIdx));
+ out.annotate(2, String.format(" %-10s %s", getTypoidName() + ':',
+ Hex.u2(typoidIdx)));
+ out.annotate(4, " name_idx: " + Hex.u4(nameIdx));
+ }
+
+ out.writeShort(classIdx);
+ out.writeShort(typoidIdx);
+ out.writeInt(nameIdx);
+ }
+
+ /**
+ * Returns the index of the type-like thing associated with
+ * this item, in order that it may be written out. Subclasses must
+ * override this to get whatever it is they need to store.
+ *
+ * @param file {@code non-null;} the file being written
+ * @return the index in question
+ */
+ protected abstract int getTypoidIdx(DexFile file);
+
+ /**
+ * Returns the field name of the type-like thing associated with
+ * this item, for listing-generating purposes. Subclasses must override
+ * this.
+ *
+ * @return {@code non-null;} the name in question
+ */
+ protected abstract String getTypoidName();
+
+ /**
+ * Gets the member constant.
+ *
+ * @return {@code non-null;} the constant
+ */
+ public final CstMemberRef getRef() {
+ return cst;
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/MemberIdsSection.java b/dx/src/com/android/dx/dex/file/MemberIdsSection.java
new file mode 100644
index 0000000..ef0d8cd
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/MemberIdsSection.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+/**
+ * Member (field or method) refs list section of a {@code .dex} file.
+ */
+public abstract class MemberIdsSection extends UniformItemSection {
+ /**
+ * Constructs an instance. The file offset is initially unknown.
+ *
+ * @param name {@code null-ok;} the name of this instance, for annotation
+ * purposes
+ * @param file {@code non-null;} file that this instance is part of
+ */
+ public MemberIdsSection(String name, DexFile file) {
+ super(name, file, 4);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void orderItems() {
+ int idx = 0;
+
+ for (Object i : items()) {
+ ((MemberIdItem) i).setIndex(idx);
+ idx++;
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/MethodAnnotationStruct.java b/dx/src/com/android/dx/dex/file/MethodAnnotationStruct.java
new file mode 100644
index 0000000..38f7ce4
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/MethodAnnotationStruct.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.annotation.Annotations;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+import com.android.dx.util.ToHuman;
+
+/**
+ * Association of a method and its annotations.
+ */
+public final class MethodAnnotationStruct
+ implements ToHuman, Comparable<MethodAnnotationStruct> {
+ /** {@code non-null;} the method in question */
+ private final CstMethodRef method;
+
+ /** {@code non-null;} the associated annotations */
+ private AnnotationSetItem annotations;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param method {@code non-null;} the method in question
+ * @param annotations {@code non-null;} the associated annotations
+ */
+ public MethodAnnotationStruct(CstMethodRef method,
+ AnnotationSetItem annotations) {
+ if (method == null) {
+ throw new NullPointerException("method == null");
+ }
+
+ if (annotations == null) {
+ throw new NullPointerException("annotations == null");
+ }
+
+ this.method = method;
+ this.annotations = annotations;
+ }
+
+ /** {@inheritDoc} */
+ public int hashCode() {
+ return method.hashCode();
+ }
+
+ /** {@inheritDoc} */
+ public boolean equals(Object other) {
+ if (! (other instanceof MethodAnnotationStruct)) {
+ return false;
+ }
+
+ return method.equals(((MethodAnnotationStruct) other).method);
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(MethodAnnotationStruct other) {
+ return method.compareTo(other.method);
+ }
+
+ /** {@inheritDoc} */
+ public void addContents(DexFile file) {
+ MethodIdsSection methodIds = file.getMethodIds();
+ MixedItemSection wordData = file.getWordData();
+
+ methodIds.intern(method);
+ annotations = wordData.intern(annotations);
+ }
+
+ /** {@inheritDoc} */
+ public void writeTo(DexFile file, AnnotatedOutput out) {
+ int methodIdx = file.getMethodIds().indexOf(method);
+ int annotationsOff = annotations.getAbsoluteOffset();
+
+ if (out.annotates()) {
+ out.annotate(0, " " + method.toHuman());
+ out.annotate(4, " method_idx: " + Hex.u4(methodIdx));
+ out.annotate(4, " annotations_off: " +
+ Hex.u4(annotationsOff));
+ }
+
+ out.writeInt(methodIdx);
+ out.writeInt(annotationsOff);
+ }
+
+ /** {@inheritDoc} */
+ public String toHuman() {
+ return method.toHuman() + ": " + annotations;
+ }
+
+ /**
+ * Gets the method this item is for.
+ *
+ * @return {@code non-null;} the method
+ */
+ public CstMethodRef getMethod() {
+ return method;
+ }
+
+ /**
+ * Gets the associated annotations.
+ *
+ * @return {@code non-null;} the annotations
+ */
+ public Annotations getAnnotations() {
+ return annotations.getAnnotations();
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/MethodIdItem.java b/dx/src/com/android/dx/dex/file/MethodIdItem.java
new file mode 100644
index 0000000..f2ff4f9
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/MethodIdItem.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.CstBaseMethodRef;
+
+/**
+ * Representation of a method reference inside a Dalvik file.
+ */
+public final class MethodIdItem extends MemberIdItem {
+ /**
+ * Constructs an instance.
+ *
+ * @param method {@code non-null;} the constant for the method
+ */
+ public MethodIdItem(CstBaseMethodRef method) {
+ super(method);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ItemType itemType() {
+ return ItemType.TYPE_METHOD_ID_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addContents(DexFile file) {
+ super.addContents(file);
+
+ ProtoIdsSection protoIds = file.getProtoIds();
+ protoIds.intern(getMethodRef().getPrototype());
+ }
+
+ /**
+ * Gets the method constant.
+ *
+ * @return {@code non-null;} the constant
+ */
+ public CstBaseMethodRef getMethodRef() {
+ return (CstBaseMethodRef) getRef();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected int getTypoidIdx(DexFile file) {
+ ProtoIdsSection protoIds = file.getProtoIds();
+ return protoIds.indexOf(getMethodRef().getPrototype());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected String getTypoidName() {
+ return "proto_idx";
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/MethodIdsSection.java b/dx/src/com/android/dx/dex/file/MethodIdsSection.java
new file mode 100644
index 0000000..fa0cd3c
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/MethodIdsSection.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstBaseMethodRef;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+import java.util.Collection;
+import java.util.TreeMap;
+
+/**
+ * Method refs list section of a {@code .dex} file.
+ */
+public final class MethodIdsSection extends MemberIdsSection {
+ /**
+ * {@code non-null;} map from method constants to {@link
+ * MethodIdItem} instances
+ */
+ private final TreeMap<CstBaseMethodRef, MethodIdItem> methodIds;
+
+ /**
+ * Constructs an instance. The file offset is initially unknown.
+ *
+ * @param file {@code non-null;} file that this instance is part of
+ */
+ public MethodIdsSection(DexFile file) {
+ super("method_ids", file);
+
+ methodIds = new TreeMap<CstBaseMethodRef, MethodIdItem>();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Collection<? extends Item> items() {
+ return methodIds.values();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public IndexedItem get(Constant cst) {
+ if (cst == null) {
+ throw new NullPointerException("cst == null");
+ }
+
+ throwIfNotPrepared();
+
+ IndexedItem result = methodIds.get((CstBaseMethodRef) cst);
+
+ if (result == null) {
+ throw new IllegalArgumentException("not found");
+ }
+
+ return result;
+ }
+
+ /**
+ * Writes the portion of the file header that refers to this instance.
+ *
+ * @param out {@code non-null;} where to write
+ */
+ public void writeHeaderPart(AnnotatedOutput out) {
+ throwIfNotPrepared();
+
+ int sz = methodIds.size();
+ int offset = (sz == 0) ? 0 : getFileOffset();
+
+ if (out.annotates()) {
+ out.annotate(4, "method_ids_size: " + Hex.u4(sz));
+ out.annotate(4, "method_ids_off: " + Hex.u4(offset));
+ }
+
+ out.writeInt(sz);
+ out.writeInt(offset);
+ }
+
+ /**
+ * Interns an element into this instance.
+ *
+ * @param method {@code non-null;} the reference to intern
+ * @return {@code non-null;} the interned reference
+ */
+ public MethodIdItem intern(CstBaseMethodRef method) {
+ if (method == null) {
+ throw new NullPointerException("method == null");
+ }
+
+ throwIfPrepared();
+
+ MethodIdItem result = methodIds.get(method);
+
+ if (result == null) {
+ result = new MethodIdItem(method);
+ methodIds.put(method, result);
+ }
+
+ return result;
+ }
+
+ /**
+ * Gets the index of the given reference, which must have been added
+ * to this instance.
+ *
+ * @param ref {@code non-null;} the reference to look up
+ * @return {@code >= 0;} the reference's index
+ */
+ public int indexOf(CstBaseMethodRef ref) {
+ if (ref == null) {
+ throw new NullPointerException("ref == null");
+ }
+
+ throwIfNotPrepared();
+
+ MethodIdItem item = methodIds.get(ref);
+
+ if (item == null) {
+ throw new IllegalArgumentException("not found");
+ }
+
+ return item.getIndex();
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/MixedItemSection.java b/dx/src/com/android/dx/dex/file/MixedItemSection.java
new file mode 100644
index 0000000..b885306
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/MixedItemSection.java
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.ExceptionWithContext;
+import com.android.dx.util.Hex;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.TreeMap;
+
+/**
+ * A section of a {@code .dex} file which consists of a sequence of
+ * {@link OffsettedItem} objects, which may each be of a different concrete
+ * class and/or size.
+ *
+ * <b>Note:</b> It is invalid for an item in an instance of this class to
+ * have a larger alignment requirement than the alignment of this instance.
+ */
+public final class MixedItemSection extends Section {
+ static enum SortType {
+ /** no sorting */
+ NONE,
+
+ /** sort by type only */
+ TYPE,
+
+ /** sort in class-major order, with instances sorted per-class */
+ INSTANCE;
+ };
+
+ /** {@code non-null;} sorter which sorts instances by type */
+ private static final Comparator<OffsettedItem> TYPE_SORTER =
+ new Comparator<OffsettedItem>() {
+ public int compare(OffsettedItem item1, OffsettedItem item2) {
+ ItemType type1 = item1.itemType();
+ ItemType type2 = item2.itemType();
+ return type1.compareTo(type2);
+ }
+ };
+
+ /** {@code non-null;} the items in this part */
+ private final ArrayList<OffsettedItem> items;
+
+ /** {@code non-null;} items that have been explicitly interned */
+ private final HashMap<OffsettedItem, OffsettedItem> interns;
+
+ /** {@code non-null;} how to sort the items */
+ private final SortType sort;
+
+ /**
+ * {@code >= -1;} the current size of this part, in bytes, or {@code -1}
+ * if not yet calculated
+ */
+ private int writeSize;
+
+ /**
+ * Constructs an instance. The file offset is initially unknown.
+ *
+ * @param name {@code null-ok;} the name of this instance, for annotation
+ * purposes
+ * @param file {@code non-null;} file that this instance is part of
+ * @param alignment {@code > 0;} alignment requirement for the final output;
+ * must be a power of 2
+ * @param sort how the items should be sorted in the final output
+ */
+ public MixedItemSection(String name, DexFile file, int alignment,
+ SortType sort) {
+ super(name, file, alignment);
+
+ this.items = new ArrayList<OffsettedItem>(100);
+ this.interns = new HashMap<OffsettedItem, OffsettedItem>(100);
+ this.sort = sort;
+ this.writeSize = -1;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Collection<? extends Item> items() {
+ return items;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int writeSize() {
+ throwIfNotPrepared();
+ return writeSize;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getAbsoluteItemOffset(Item item) {
+ OffsettedItem oi = (OffsettedItem) item;
+ return oi.getAbsoluteOffset();
+ }
+
+ /**
+ * Gets the size of this instance, in items.
+ *
+ * @return {@code >= 0;} the size
+ */
+ public int size() {
+ return items.size();
+ }
+
+ /**
+ * Writes the portion of the file header that refers to this instance.
+ *
+ * @param out {@code non-null;} where to write
+ */
+ public void writeHeaderPart(AnnotatedOutput out) {
+ throwIfNotPrepared();
+
+ if (writeSize == -1) {
+ throw new RuntimeException("write size not yet set");
+ }
+
+ int sz = writeSize;
+ int offset = (sz == 0) ? 0 : getFileOffset();
+ String name = getName();
+
+ if (name == null) {
+ name = "<unnamed>";
+ }
+
+ int spaceCount = 15 - name.length();
+ char[] spaceArr = new char[spaceCount];
+ Arrays.fill(spaceArr, ' ');
+ String spaces = new String(spaceArr);
+
+ if (out.annotates()) {
+ out.annotate(4, name + "_size:" + spaces + Hex.u4(sz));
+ out.annotate(4, name + "_off: " + spaces + Hex.u4(offset));
+ }
+
+ out.writeInt(sz);
+ out.writeInt(offset);
+ }
+
+ /**
+ * Adds an item to this instance. This will in turn tell the given item
+ * that it has been added to this instance. It is invalid to add the
+ * same item to more than one instance, nor to add the same items
+ * multiple times to a single instance.
+ *
+ * @param item {@code non-null;} the item to add
+ */
+ public void add(OffsettedItem item) {
+ throwIfPrepared();
+
+ try {
+ if (item.getAlignment() > getAlignment()) {
+ throw new IllegalArgumentException(
+ "incompatible item alignment");
+ }
+ } catch (NullPointerException ex) {
+ // Elucidate the exception.
+ throw new NullPointerException("item == null");
+ }
+
+ items.add(item);
+ }
+
+ /**
+ * Interns an item in this instance, returning the interned instance
+ * (which may not be the one passed in). This will add the item if no
+ * equal item has been added.
+ *
+ * @param item {@code non-null;} the item to intern
+ * @return {@code non-null;} the equivalent interned instance
+ */
+ public <T extends OffsettedItem> T intern(T item) {
+ throwIfPrepared();
+
+ OffsettedItem result = interns.get(item);
+
+ if (result != null) {
+ return (T) result;
+ }
+
+ add(item);
+ interns.put(item, item);
+ return item;
+ }
+
+ /**
+ * Gets an item which was previously interned.
+ *
+ * @param item {@code non-null;} the item to look for
+ * @return {@code non-null;} the equivalent already-interned instance
+ */
+ public <T extends OffsettedItem> T get(T item) {
+ throwIfNotPrepared();
+
+ OffsettedItem result = interns.get(item);
+
+ if (result != null) {
+ return (T) result;
+ }
+
+ throw new NoSuchElementException(item.toString());
+ }
+
+ /**
+ * Writes an index of contents of the items in this instance of the
+ * given type. If there are none, this writes nothing. If there are any,
+ * then the index is preceded by the given intro string.
+ *
+ * @param out {@code non-null;} where to write to
+ * @param itemType {@code non-null;} the item type of interest
+ * @param intro {@code non-null;} the introductory string for non-empty indices
+ */
+ public void writeIndexAnnotation(AnnotatedOutput out, ItemType itemType,
+ String intro) {
+ throwIfNotPrepared();
+
+ TreeMap<String, OffsettedItem> index =
+ new TreeMap<String, OffsettedItem>();
+
+ for (OffsettedItem item : items) {
+ if (item.itemType() == itemType) {
+ String label = item.toHuman();
+ index.put(label, item);
+ }
+ }
+
+ if (index.size() == 0) {
+ return;
+ }
+
+ out.annotate(0, intro);
+
+ for (Map.Entry<String, OffsettedItem> entry : index.entrySet()) {
+ String label = entry.getKey();
+ OffsettedItem item = entry.getValue();
+ out.annotate(0, item.offsetString() + ' ' + label + '\n');
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void prepare0() {
+ DexFile file = getFile();
+
+ /*
+ * It's okay for new items to be added as a result of an
+ * addContents() call; we just have to deal with the possibility.
+ */
+
+ int i = 0;
+ for (;;) {
+ int sz = items.size();
+ if (i >= sz) {
+ break;
+ }
+
+ for (/*i*/; i < sz; i++) {
+ OffsettedItem one = items.get(i);
+ one.addContents(file);
+ }
+ }
+ }
+
+ /**
+ * Places all the items in this instance at particular offsets. This
+ * will call {@link OffsettedItem#place} on each item. If an item
+ * does not know its write size before the call to {@code place},
+ * it is that call which is responsible for setting the write size.
+ * This method may only be called once per instance; subsequent calls
+ * will throw an exception.
+ */
+ public void placeItems() {
+ throwIfNotPrepared();
+
+ switch (sort) {
+ case INSTANCE: {
+ Collections.sort(items);
+ break;
+ }
+ case TYPE: {
+ Collections.sort(items, TYPE_SORTER);
+ break;
+ }
+ }
+
+ int sz = items.size();
+ int outAt = 0;
+ for (int i = 0; i < sz; i++) {
+ OffsettedItem one = items.get(i);
+ try {
+ int placedAt = one.place(this, outAt);
+
+ if (placedAt < outAt) {
+ throw new RuntimeException("bogus place() result for " +
+ one);
+ }
+
+ outAt = placedAt + one.writeSize();
+ } catch (RuntimeException ex) {
+ throw ExceptionWithContext.withContext(ex,
+ "...while placing " + one);
+ }
+ }
+
+ writeSize = outAt;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void writeTo0(AnnotatedOutput out) {
+ boolean annotates = out.annotates();
+ boolean first = true;
+ DexFile file = getFile();
+ int at = 0;
+
+ for (OffsettedItem one : items) {
+ if (annotates) {
+ if (first) {
+ first = false;
+ } else {
+ out.annotate(0, "\n");
+ }
+ }
+
+ int alignMask = one.getAlignment() - 1;
+ int writeAt = (at + alignMask) & ~alignMask;
+
+ if (at != writeAt) {
+ out.writeZeroes(writeAt - at);
+ at = writeAt;
+ }
+
+ one.writeTo(file, out);
+ at += one.writeSize();
+ }
+
+ if (at != writeSize) {
+ throw new RuntimeException("output size mismatch");
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/OffsettedItem.java b/dx/src/com/android/dx/dex/file/OffsettedItem.java
new file mode 100644
index 0000000..7721470
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/OffsettedItem.java
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.ExceptionWithContext;
+
+/**
+ * An item in a Dalvik file which is referenced by absolute offset.
+ */
+public abstract class OffsettedItem extends Item
+ implements Comparable<OffsettedItem> {
+ /** {@code > 0;} alignment requirement */
+ private final int alignment;
+
+ /** {@code >= -1;} the size of this instance when written, in bytes, or
+ * {@code -1} if not yet known */
+ private int writeSize;
+
+ /**
+ * {@code null-ok;} section the item was added to, or {@code null} if
+ * not yet added
+ */
+ private Section addedTo;
+
+ /**
+ * {@code >= -1;} assigned offset of the item from the start of its section,
+ * or {@code -1} if not yet assigned
+ */
+ private int offset;
+
+ /**
+ * Gets the absolute offset of the given item, returning {@code 0}
+ * if handed {@code null}.
+ *
+ * @param item {@code null-ok;} the item in question
+ * @return {@code >= 0;} the item's absolute offset, or {@code 0}
+ * if {@code item == null}
+ */
+ public static int getAbsoluteOffsetOr0(OffsettedItem item) {
+ if (item == null) {
+ return 0;
+ }
+
+ return item.getAbsoluteOffset();
+ }
+
+ /**
+ * Constructs an instance. The offset is initially unassigned.
+ *
+ * @param alignment {@code > 0;} output alignment requirement; must be a
+ * power of 2
+ * @param writeSize {@code >= -1;} the size of this instance when written,
+ * in bytes, or {@code -1} if not immediately known
+ */
+ public OffsettedItem(int alignment, int writeSize) {
+ Section.validateAlignment(alignment);
+
+ if (writeSize < -1) {
+ throw new IllegalArgumentException("writeSize < -1");
+ }
+
+ this.alignment = alignment;
+ this.writeSize = writeSize;
+ this.addedTo = null;
+ this.offset = -1;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Comparisons for this class are defined to be type-major (if the
+ * types don't match then the objects are not equal), with
+ * {@link #compareTo0} deciding same-type comparisons.
+ */
+ @Override
+ public final boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+
+ OffsettedItem otherItem = (OffsettedItem) other;
+ ItemType thisType = itemType();
+ ItemType otherType = otherItem.itemType();
+
+ if (thisType != otherType) {
+ return false;
+ }
+
+ return (compareTo0(otherItem) == 0);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Comparisons for this class are defined to be class-major (if the
+ * classes don't match then the objects are not equal), with
+ * {@link #compareTo0} deciding same-class comparisons.
+ */
+ public final int compareTo(OffsettedItem other) {
+ if (this == other) {
+ return 0;
+ }
+
+ ItemType thisType = itemType();
+ ItemType otherType = other.itemType();
+
+ if (thisType != otherType) {
+ return thisType.compareTo(otherType);
+ }
+
+ return compareTo0(other);
+ }
+
+ /**
+ * Sets the write size of this item. This may only be called once
+ * per instance, and only if the size was unknown upon instance
+ * creation.
+ *
+ * @param writeSize {@code > 0;} the write size, in bytes
+ */
+ public final void setWriteSize(int writeSize) {
+ if (writeSize < 0) {
+ throw new IllegalArgumentException("writeSize < 0");
+ }
+
+ if (this.writeSize >= 0) {
+ throw new UnsupportedOperationException("writeSize already set");
+ }
+
+ this.writeSize = writeSize;
+ }
+
+ /** {@inheritDoc}
+ *
+ * @throws UnsupportedOperationException thrown if the write size
+ * is not yet known
+ */
+ @Override
+ public final int writeSize() {
+ if (writeSize < 0) {
+ throw new UnsupportedOperationException("writeSize is unknown");
+ }
+
+ return writeSize;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final void writeTo(DexFile file, AnnotatedOutput out) {
+ out.alignTo(alignment);
+
+ try {
+ if (writeSize < 0) {
+ throw new UnsupportedOperationException(
+ "writeSize is unknown");
+ }
+ out.assertCursor(getAbsoluteOffset());
+ } catch (RuntimeException ex) {
+ throw ExceptionWithContext.withContext(ex,
+ "...while writing " + this);
+ }
+
+ writeTo0(file, out);
+ }
+
+ /**
+ * Gets the relative item offset. The offset is from the start of
+ * the section which the instance was written to.
+ *
+ * @return {@code >= 0;} the offset
+ * @throws RuntimeException thrown if the offset is not yet known
+ */
+ public final int getRelativeOffset() {
+ if (offset < 0) {
+ throw new RuntimeException("offset not yet known");
+ }
+
+ return offset;
+ }
+
+ /**
+ * Gets the absolute item offset. The offset is from the start of
+ * the file which the instance was written to.
+ *
+ * @return {@code >= 0;} the offset
+ * @throws RuntimeException thrown if the offset is not yet known
+ */
+ public final int getAbsoluteOffset() {
+ if (offset < 0) {
+ throw new RuntimeException("offset not yet known");
+ }
+
+ return addedTo.getAbsoluteOffset(offset);
+ }
+
+ /**
+ * Indicates that this item has been added to the given section at
+ * the given offset. It is only valid to call this method once per
+ * instance.
+ *
+ * @param addedTo {@code non-null;} the section this instance has
+ * been added to
+ * @param offset {@code >= 0;} the desired offset from the start of the
+ * section where this instance was placed
+ * @return {@code >= 0;} the offset that this instance should be placed at
+ * in order to meet its alignment constraint
+ */
+ public final int place(Section addedTo, int offset) {
+ if (addedTo == null) {
+ throw new NullPointerException("addedTo == null");
+ }
+
+ if (offset < 0) {
+ throw new IllegalArgumentException("offset < 0");
+ }
+
+ if (this.addedTo != null) {
+ throw new RuntimeException("already written");
+ }
+
+ int mask = alignment - 1;
+ offset = (offset + mask) & ~mask;
+
+ this.addedTo = addedTo;
+ this.offset = offset;
+
+ place0(addedTo, offset);
+
+ return offset;
+ }
+
+ /**
+ * Gets the alignment requirement of this instance. An instance should
+ * only be written when so aligned.
+ *
+ * @return {@code > 0;} the alignment requirement; must be a power of 2
+ */
+ public final int getAlignment() {
+ return alignment;
+ }
+
+ /**
+ * Gets the absolute offset of this item as a string, suitable for
+ * including in annotations.
+ *
+ * @return {@code non-null;} the offset string
+ */
+ public final String offsetString() {
+ return '[' + Integer.toHexString(getAbsoluteOffset()) + ']';
+ }
+
+ /**
+ * Gets a short human-readable string representing this instance.
+ *
+ * @return {@code non-null;} the human form
+ */
+ public abstract String toHuman();
+
+ /**
+ * Compares this instance to another which is guaranteed to be of
+ * the same class. The default implementation of this method is to
+ * throw an exception (unsupported operation). If a particular
+ * class needs to actually sort, then it should override this
+ * method.
+ *
+ * @param other {@code non-null;} instance to compare to
+ * @return {@code -1}, {@code 0}, or {@code 1}, depending
+ * on the sort order of this instance and the other
+ */
+ protected int compareTo0(OffsettedItem other) {
+ throw new UnsupportedOperationException("unsupported");
+ }
+
+ /**
+ * Does additional work required when placing an instance. The
+ * default implementation of this method is a no-op. If a
+ * particular class needs to do something special, then it should
+ * override this method. In particular, if this instance did not
+ * know its write size up-front, then this method is responsible
+ * for setting it.
+ *
+ * @param addedTo {@code non-null;} the section this instance has been added to
+ * @param offset {@code >= 0;} the offset from the start of the
+ * section where this instance was placed
+ */
+ protected void place0(Section addedTo, int offset) {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Performs the actual write of the contents of this instance to
+ * the given data section. This is called by {@link #writeTo},
+ * which will have taken care of ensuring alignment.
+ *
+ * @param file {@code non-null;} the file to use for reference
+ * @param out {@code non-null;} where to write to
+ */
+ protected abstract void writeTo0(DexFile file, AnnotatedOutput out);
+}
diff --git a/dx/src/com/android/dx/dex/file/ParameterAnnotationStruct.java b/dx/src/com/android/dx/dex/file/ParameterAnnotationStruct.java
new file mode 100644
index 0000000..078c219
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/ParameterAnnotationStruct.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.annotation.Annotations;
+import com.android.dx.rop.annotation.AnnotationsList;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+import com.android.dx.util.ToHuman;
+
+import java.util.ArrayList;
+
+/**
+ * Association of a method and its parameter annotations.
+ */
+public final class ParameterAnnotationStruct
+ implements ToHuman, Comparable<ParameterAnnotationStruct> {
+ /** {@code non-null;} the method in question */
+ private final CstMethodRef method;
+
+ /** {@code non-null;} the associated annotations list */
+ private final AnnotationsList annotationsList;
+
+ /** {@code non-null;} the associated annotations list, as an item */
+ private final UniformListItem<AnnotationSetRefItem> annotationsItem;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param method {@code non-null;} the method in question
+ * @param annotationsList {@code non-null;} the associated annotations list
+ */
+ public ParameterAnnotationStruct(CstMethodRef method,
+ AnnotationsList annotationsList) {
+ if (method == null) {
+ throw new NullPointerException("method == null");
+ }
+
+ if (annotationsList == null) {
+ throw new NullPointerException("annotationsList == null");
+ }
+
+ this.method = method;
+ this.annotationsList = annotationsList;
+
+ /*
+ * Construct an item for the annotations list. TODO: This
+ * requires way too much copying; fix it.
+ */
+
+ int size = annotationsList.size();
+ ArrayList<AnnotationSetRefItem> arrayList = new
+ ArrayList<AnnotationSetRefItem>(size);
+
+ for (int i = 0; i < size; i++) {
+ Annotations annotations = annotationsList.get(i);
+ AnnotationSetItem item = new AnnotationSetItem(annotations);
+ arrayList.add(new AnnotationSetRefItem(item));
+ }
+
+ this.annotationsItem = new UniformListItem<AnnotationSetRefItem>(
+ ItemType.TYPE_ANNOTATION_SET_REF_LIST, arrayList);
+ }
+
+ /** {@inheritDoc} */
+ public int hashCode() {
+ return method.hashCode();
+ }
+
+ /** {@inheritDoc} */
+ public boolean equals(Object other) {
+ if (! (other instanceof ParameterAnnotationStruct)) {
+ return false;
+ }
+
+ return method.equals(((ParameterAnnotationStruct) other).method);
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(ParameterAnnotationStruct other) {
+ return method.compareTo(other.method);
+ }
+
+ /** {@inheritDoc} */
+ public void addContents(DexFile file) {
+ MethodIdsSection methodIds = file.getMethodIds();
+ MixedItemSection wordData = file.getWordData();
+
+ methodIds.intern(method);
+ wordData.add(annotationsItem);
+ }
+
+ /** {@inheritDoc} */
+ public void writeTo(DexFile file, AnnotatedOutput out) {
+ int methodIdx = file.getMethodIds().indexOf(method);
+ int annotationsOff = annotationsItem.getAbsoluteOffset();
+
+ if (out.annotates()) {
+ out.annotate(0, " " + method.toHuman());
+ out.annotate(4, " method_idx: " + Hex.u4(methodIdx));
+ out.annotate(4, " annotations_off: " +
+ Hex.u4(annotationsOff));
+ }
+
+ out.writeInt(methodIdx);
+ out.writeInt(annotationsOff);
+ }
+
+ /** {@inheritDoc} */
+ public String toHuman() {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append(method.toHuman());
+ sb.append(": ");
+
+ boolean first = true;
+ for (AnnotationSetRefItem item : annotationsItem.getItems()) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append(", ");
+ }
+ sb.append(item.toHuman());
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Gets the method this item is for.
+ *
+ * @return {@code non-null;} the method
+ */
+ public CstMethodRef getMethod() {
+ return method;
+ }
+
+ /**
+ * Gets the associated annotations list.
+ *
+ * @return {@code non-null;} the annotations list
+ */
+ public AnnotationsList getAnnotationsList() {
+ return annotationsList;
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/ProtoIdItem.java b/dx/src/com/android/dx/dex/file/ProtoIdItem.java
new file mode 100644
index 0000000..31cf8fb
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/ProtoIdItem.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Prototype;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+/**
+ * Representation of a method prototype reference inside a Dalvik file.
+ */
+public final class ProtoIdItem extends IndexedItem {
+ /** size of instances when written out to a file, in bytes */
+ public static final int WRITE_SIZE = 12;
+
+ /** {@code non-null;} the wrapped prototype */
+ private final Prototype prototype;
+
+ /** {@code non-null;} the short-form of the prototype */
+ private final CstUtf8 shortForm;
+
+ /**
+ * {@code null-ok;} the list of parameter types or {@code null} if this
+ * prototype has no parameters
+ */
+ private TypeListItem parameterTypes;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param prototype {@code non-null;} the constant for the prototype
+ */
+ public ProtoIdItem(Prototype prototype) {
+ if (prototype == null) {
+ throw new NullPointerException("prototype == null");
+ }
+
+ this.prototype = prototype;
+ this.shortForm = makeShortForm(prototype);
+
+ StdTypeList parameters = prototype.getParameterTypes();
+ this.parameterTypes = (parameters.size() == 0) ? null
+ : new TypeListItem(parameters);
+ }
+
+ /**
+ * Creates the short-form of the given prototype.
+ *
+ * @param prototype {@code non-null;} the prototype
+ * @return {@code non-null;} the short form
+ */
+ private static CstUtf8 makeShortForm(Prototype prototype) {
+ StdTypeList parameters = prototype.getParameterTypes();
+ int size = parameters.size();
+ StringBuilder sb = new StringBuilder(size + 1);
+
+ sb.append(shortFormCharFor(prototype.getReturnType()));
+
+ for (int i = 0; i < size; i++) {
+ sb.append(shortFormCharFor(parameters.getType(i)));
+ }
+
+ return new CstUtf8(sb.toString());
+ }
+
+ /**
+ * Gets the short-form character for the given type.
+ *
+ * @param type {@code non-null;} the type
+ * @return the corresponding short-form character
+ */
+ private static char shortFormCharFor(Type type) {
+ char descriptorChar = type.getDescriptor().charAt(0);
+
+ if (descriptorChar == '[') {
+ return 'L';
+ }
+
+ return descriptorChar;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ItemType itemType() {
+ return ItemType.TYPE_PROTO_ID_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int writeSize() {
+ return WRITE_SIZE;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addContents(DexFile file) {
+ StringIdsSection stringIds = file.getStringIds();
+ TypeIdsSection typeIds = file.getTypeIds();
+ MixedItemSection typeLists = file.getTypeLists();
+
+ typeIds.intern(prototype.getReturnType());
+ stringIds.intern(shortForm);
+
+ if (parameterTypes != null) {
+ parameterTypes = typeLists.intern(parameterTypes);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(DexFile file, AnnotatedOutput out) {
+ int shortyIdx = file.getStringIds().indexOf(shortForm);
+ int returnIdx = file.getTypeIds().indexOf(prototype.getReturnType());
+ int paramsOff = OffsettedItem.getAbsoluteOffsetOr0(parameterTypes);
+
+ if (out.annotates()) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(prototype.getReturnType().toHuman());
+ sb.append(" proto(");
+
+ StdTypeList params = prototype.getParameterTypes();
+ int size = params.size();
+
+ for (int i = 0; i < size; i++) {
+ if (i != 0) {
+ sb.append(", ");
+ }
+ sb.append(params.getType(i).toHuman());
+ }
+
+ sb.append(")");
+ out.annotate(0, indexString() + ' ' + sb.toString());
+ out.annotate(4, " shorty_idx: " + Hex.u4(shortyIdx) +
+ " // " + shortForm.toQuoted());
+ out.annotate(4, " return_type_idx: " + Hex.u4(returnIdx) +
+ " // " + prototype.getReturnType().toHuman());
+ out.annotate(4, " parameters_off: " + Hex.u4(paramsOff));
+ }
+
+ out.writeInt(shortyIdx);
+ out.writeInt(returnIdx);
+ out.writeInt(paramsOff);
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/ProtoIdsSection.java b/dx/src/com/android/dx/dex/file/ProtoIdsSection.java
new file mode 100644
index 0000000..dc6e8ad
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/ProtoIdsSection.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.type.Prototype;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+import java.util.Collection;
+import java.util.TreeMap;
+
+/**
+ * Proto (method prototype) identifiers list section of a
+ * {@code .dex} file.
+ */
+public final class ProtoIdsSection extends UniformItemSection {
+ /**
+ * {@code non-null;} map from method prototypes to {@link ProtoIdItem} instances
+ */
+ private final TreeMap<Prototype, ProtoIdItem> protoIds;
+
+ /**
+ * Constructs an instance. The file offset is initially unknown.
+ *
+ * @param file {@code non-null;} file that this instance is part of
+ */
+ public ProtoIdsSection(DexFile file) {
+ super("proto_ids", file, 4);
+
+ protoIds = new TreeMap<Prototype, ProtoIdItem>();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Collection<? extends Item> items() {
+ return protoIds.values();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public IndexedItem get(Constant cst) {
+ throw new UnsupportedOperationException("unsupported");
+ }
+
+ /**
+ * Writes the portion of the file header that refers to this instance.
+ *
+ * @param out {@code non-null;} where to write
+ */
+ public void writeHeaderPart(AnnotatedOutput out) {
+ throwIfNotPrepared();
+
+ int sz = protoIds.size();
+ int offset = (sz == 0) ? 0 : getFileOffset();
+
+ if (sz > 65536) {
+ throw new UnsupportedOperationException("too many proto ids");
+ }
+
+ if (out.annotates()) {
+ out.annotate(4, "proto_ids_size: " + Hex.u4(sz));
+ out.annotate(4, "proto_ids_off: " + Hex.u4(offset));
+ }
+
+ out.writeInt(sz);
+ out.writeInt(offset);
+ }
+
+ /**
+ * Interns an element into this instance.
+ *
+ * @param prototype {@code non-null;} the prototype to intern
+ * @return {@code non-null;} the interned reference
+ */
+ public ProtoIdItem intern(Prototype prototype) {
+ if (prototype == null) {
+ throw new NullPointerException("prototype == null");
+ }
+
+ throwIfPrepared();
+
+ ProtoIdItem result = protoIds.get(prototype);
+
+ if (result == null) {
+ result = new ProtoIdItem(prototype);
+ protoIds.put(prototype, result);
+ }
+
+ return result;
+ }
+
+ /**
+ * Gets the index of the given prototype, which must have
+ * been added to this instance.
+ *
+ * @param prototype {@code non-null;} the prototype to look up
+ * @return {@code >= 0;} the reference's index
+ */
+ public int indexOf(Prototype prototype) {
+ if (prototype == null) {
+ throw new NullPointerException("prototype == null");
+ }
+
+ throwIfNotPrepared();
+
+ ProtoIdItem item = protoIds.get(prototype);
+
+ if (item == null) {
+ throw new IllegalArgumentException("not found");
+ }
+
+ return item.getIndex();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void orderItems() {
+ int idx = 0;
+
+ for (Object i : items()) {
+ ((ProtoIdItem) i).setIndex(idx);
+ idx++;
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/Section.java b/dx/src/com/android/dx/dex/file/Section.java
new file mode 100644
index 0000000..3f04216
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/Section.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.util.AnnotatedOutput;
+
+import java.util.Collection;
+
+/**
+ * A section of a {@code .dex} file. Each section consists of a list
+ * of items of some sort or other.
+ */
+public abstract class Section {
+ /** {@code null-ok;} name of this part, for annotation purposes */
+ private final String name;
+
+ /** {@code non-null;} file that this instance is part of */
+ private final DexFile file;
+
+ /** {@code > 0;} alignment requirement for the final output;
+ * must be a power of 2 */
+ private final int alignment;
+
+ /** {@code >= -1;} offset from the start of the file to this part, or
+ * {@code -1} if not yet known */
+ private int fileOffset;
+
+ /** whether {@link #prepare} has been called successfully on this
+ * instance */
+ private boolean prepared;
+
+ /**
+ * Validates an alignment.
+ *
+ * @param alignment the alignment
+ * @throws IllegalArgumentException thrown if {@code alignment}
+ * isn't a positive power of 2
+ */
+ public static void validateAlignment(int alignment) {
+ if ((alignment <= 0) ||
+ (alignment & (alignment - 1)) != 0) {
+ throw new IllegalArgumentException("invalid alignment");
+ }
+ }
+
+ /**
+ * Constructs an instance. The file offset is initially unknown.
+ *
+ * @param name {@code null-ok;} the name of this instance, for annotation
+ * purposes
+ * @param file {@code non-null;} file that this instance is part of
+ * @param alignment {@code > 0;} alignment requirement for the final output;
+ * must be a power of 2
+ */
+ public Section(String name, DexFile file, int alignment) {
+ if (file == null) {
+ throw new NullPointerException("file == null");
+ }
+
+ validateAlignment(alignment);
+
+ this.name = name;
+ this.file = file;
+ this.alignment = alignment;
+ this.fileOffset = -1;
+ this.prepared = false;
+ }
+
+ /**
+ * Gets the file that this instance is part of.
+ *
+ * @return {@code non-null;} the file
+ */
+ public final DexFile getFile() {
+ return file;
+ }
+
+ /**
+ * Gets the alignment for this instance's final output.
+ *
+ * @return {@code > 0;} the alignment
+ */
+ public final int getAlignment() {
+ return alignment;
+ }
+
+ /**
+ * Gets the offset from the start of the file to this part. This
+ * throws an exception if the offset has not yet been set.
+ *
+ * @return {@code >= 0;} the file offset
+ */
+ public final int getFileOffset() {
+ if (fileOffset < 0) {
+ throw new RuntimeException("fileOffset not set");
+ }
+
+ return fileOffset;
+ }
+
+ /**
+ * Sets the file offset. It is only valid to call this method once
+ * once per instance.
+ *
+ * @param fileOffset {@code >= 0;} the desired offset from the start of the
+ * file where this for this instance
+ * @return {@code >= 0;} the offset that this instance should be placed at
+ * in order to meet its alignment constraint
+ */
+ public final int setFileOffset(int fileOffset) {
+ if (fileOffset < 0) {
+ throw new IllegalArgumentException("fileOffset < 0");
+ }
+
+ if (this.fileOffset >= 0) {
+ throw new RuntimeException("fileOffset already set");
+ }
+
+ int mask = alignment - 1;
+ fileOffset = (fileOffset + mask) & ~mask;
+
+ this.fileOffset = fileOffset;
+
+ return fileOffset;
+ }
+
+ /**
+ * Writes this instance to the given raw data object.
+ *
+ * @param out {@code non-null;} where to write to
+ */
+ public final void writeTo(AnnotatedOutput out) {
+ throwIfNotPrepared();
+ align(out);
+
+ int cursor = out.getCursor();
+
+ if (fileOffset < 0) {
+ fileOffset = cursor;
+ } else if (fileOffset != cursor) {
+ throw new RuntimeException("alignment mismatch: for " + this +
+ ", at " + cursor +
+ ", but expected " + fileOffset);
+ }
+
+ if (out.annotates()) {
+ if (name != null) {
+ out.annotate(0, "\n" + name + ":");
+ } else if (cursor != 0) {
+ out.annotate(0, "\n");
+ }
+ }
+
+ writeTo0(out);
+ }
+
+ /**
+ * Returns the absolute file offset, given an offset from the
+ * start of this instance's output. This is only valid to call
+ * once this instance has been assigned a file offset (via {@link
+ * #setFileOffset}).
+ *
+ * @param relative {@code >= 0;} the relative offset
+ * @return {@code >= 0;} the corresponding absolute file offset
+ */
+ public final int getAbsoluteOffset(int relative) {
+ if (relative < 0) {
+ throw new IllegalArgumentException("relative < 0");
+ }
+
+ if (fileOffset < 0) {
+ throw new RuntimeException("fileOffset not yet set");
+ }
+
+ return fileOffset + relative;
+ }
+
+ /**
+ * Returns the absolute file offset of the given item which must
+ * be contained in this section. This is only valid to call
+ * once this instance has been assigned a file offset (via {@link
+ * #setFileOffset}).
+ *
+ * <p><b>Note:</b> Subclasses must implement this as appropriate for
+ * their contents.</p>
+ *
+ * @param item {@code non-null;} the item in question
+ * @return {@code >= 0;} the item's absolute file offset
+ */
+ public abstract int getAbsoluteItemOffset(Item item);
+
+ /**
+ * Prepares this instance for writing. This performs any necessary
+ * prerequisites, including particularly adding stuff to other
+ * sections. This method may only be called once per instance;
+ * subsequent calls will throw an exception.
+ */
+ public final void prepare() {
+ throwIfPrepared();
+ prepare0();
+ prepared = true;
+ }
+
+ /**
+ * Gets the collection of all the items in this section.
+ * It is not valid to attempt to change the returned list.
+ *
+ * @return {@code non-null;} the items
+ */
+ public abstract Collection<? extends Item> items();
+
+ /**
+ * Does the main work of {@link #prepare}.
+ */
+ protected abstract void prepare0();
+
+ /**
+ * Gets the size of this instance when output, in bytes.
+ *
+ * @return {@code >= 0;} the size of this instance, in bytes
+ */
+ public abstract int writeSize();
+
+ /**
+ * Throws an exception if {@link #prepare} has not been
+ * called on this instance.
+ */
+ protected final void throwIfNotPrepared() {
+ if (!prepared) {
+ throw new RuntimeException("not prepared");
+ }
+ }
+
+ /**
+ * Throws an exception if {@link #prepare} has already been called
+ * on this instance.
+ */
+ protected final void throwIfPrepared() {
+ if (prepared) {
+ throw new RuntimeException("already prepared");
+ }
+ }
+
+ /**
+ * Aligns the output of the given data to the alignment of this instance.
+ *
+ * @param out {@code non-null;} the output to align
+ */
+ protected final void align(AnnotatedOutput out) {
+ out.alignTo(alignment);
+ }
+
+ /**
+ * Writes this instance to the given raw data object. This gets
+ * called by {@link #writeTo} after aligning the cursor of
+ * {@code out} and verifying that either the assigned file
+ * offset matches the actual cursor {@code out} or that the
+ * file offset was not previously assigned, in which case it gets
+ * assigned to {@code out}'s cursor.
+ *
+ * @param out {@code non-null;} where to write to
+ */
+ protected abstract void writeTo0(AnnotatedOutput out);
+
+ /**
+ * Returns the name of this section, for annotation purposes.
+ *
+ * @return {@code null-ok;} name of this part, for annotation purposes
+ */
+ protected final String getName() {
+ return name;
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/Statistics.java b/dx/src/com/android/dx/dex/file/Statistics.java
new file mode 100644
index 0000000..62e1832
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/Statistics.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.util.AnnotatedOutput;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.TreeMap;
+
+/**
+ * Statistics about the contents of a file.
+ */
+public final class Statistics {
+ /** {@code non-null;} data about each type of item */
+ private final HashMap<String, Data> dataMap;
+
+ /**
+ * Constructs an instance.
+ */
+ public Statistics() {
+ dataMap = new HashMap<String, Data>(50);
+ }
+
+ /**
+ * Adds the given item to the statistics.
+ *
+ * @param item {@code non-null;} the item to add
+ */
+ public void add(Item item) {
+ String typeName = item.typeName();
+ Data data = dataMap.get(typeName);
+
+ if (data == null) {
+ dataMap.put(typeName, new Data(item, typeName));
+ } else {
+ data.add(item);
+ }
+ }
+
+ /**
+ * Adds the given list of items to the statistics.
+ *
+ * @param list {@code non-null;} the list of items to add
+ */
+ public void addAll(Section list) {
+ Collection<? extends Item> items = list.items();
+ for (Item item : items) {
+ add(item);
+ }
+ }
+
+ /**
+ * Writes the statistics as an annotation.
+ *
+ * @param out {@code non-null;} where to write to
+ */
+ public final void writeAnnotation(AnnotatedOutput out) {
+ if (dataMap.size() == 0) {
+ return;
+ }
+
+ out.annotate(0, "\nstatistics:\n");
+
+ TreeMap<String, Data> sortedData = new TreeMap<String, Data>();
+
+ for (Data data : dataMap.values()) {
+ sortedData.put(data.name, data);
+ }
+
+ for (Data data : sortedData.values()) {
+ data.writeAnnotation(out);
+ }
+ }
+
+ public String toHuman() {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append("Statistics:\n");
+
+ TreeMap<String, Data> sortedData = new TreeMap<String, Data>();
+
+ for (Data data : dataMap.values()) {
+ sortedData.put(data.name, data);
+ }
+
+ for (Data data : sortedData.values()) {
+ sb.append(data.toHuman());
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Statistical data about a particular class.
+ */
+ private static class Data {
+ /** {@code non-null;} name to use as a label */
+ private final String name;
+
+ /** {@code >= 0;} number of instances */
+ private int count;
+
+ /** {@code >= 0;} total size of instances in bytes */
+ private int totalSize;
+
+ /** {@code >= 0;} largest size of any individual item */
+ private int largestSize;
+
+ /** {@code >= 0;} smallest size of any individual item */
+ private int smallestSize;
+
+ /**
+ * Constructs an instance for the given item.
+ *
+ * @param item {@code non-null;} item in question
+ * @param name {@code non-null;} type name to use
+ */
+ public Data(Item item, String name) {
+ int size = item.writeSize();
+
+ this.name = name;
+ this.count = 1;
+ this.totalSize = size;
+ this.largestSize = size;
+ this.smallestSize = size;
+ }
+
+ /**
+ * Incorporates a new item. This assumes the type name matches.
+ *
+ * @param item {@code non-null;} item to incorporate
+ */
+ public void add(Item item) {
+ int size = item.writeSize();
+
+ count++;
+ totalSize += size;
+
+ if (size > largestSize) {
+ largestSize = size;
+ }
+
+ if (size < smallestSize) {
+ smallestSize = size;
+ }
+ }
+
+ /**
+ * Writes this instance as an annotation.
+ *
+ * @param out {@code non-null;} where to write to
+ */
+ public void writeAnnotation(AnnotatedOutput out) {
+ out.annotate(toHuman());
+ }
+
+ /**
+ * Generates a human-readable string for this data item.
+ *
+ * @return string for human consumption.
+ */
+ public String toHuman() {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append(" " + name + ": " +
+ count + " item" + (count == 1 ? "" : "s") + "; " +
+ totalSize + " bytes total\n");
+
+ if (smallestSize == largestSize) {
+ sb.append(" " + smallestSize + " bytes/item\n");
+ } else {
+ int average = totalSize / count;
+ sb.append(" " + smallestSize + ".." + largestSize +
+ " bytes/item; average " + average + "\n");
+ }
+
+ return sb.toString();
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/StringDataItem.java b/dx/src/com/android/dx/dex/file/StringDataItem.java
new file mode 100644
index 0000000..80dbced
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/StringDataItem.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+import com.android.dx.util.Leb128Utils;
+
+/**
+ * Representation of string data for a particular string, in a Dalvik file.
+ */
+public final class StringDataItem extends OffsettedItem {
+ /** {@code non-null;} the string value */
+ private final CstUtf8 value;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param value {@code non-null;} the string value
+ */
+ public StringDataItem(CstUtf8 value) {
+ super(1, writeSize(value));
+
+ this.value = value;
+ }
+
+ /**
+ * Gets the write size for a given value.
+ *
+ * @param value {@code non-null;} the string value
+ * @return {@code >= 2}; the write size, in bytes
+ */
+ private static int writeSize(CstUtf8 value) {
+ int utf16Size = value.getUtf16Size();
+
+ // The +1 is for the '\0' termination byte.
+ return Leb128Utils.unsignedLeb128Size(utf16Size)
+ + value.getUtf8Size() + 1;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ItemType itemType() {
+ return ItemType.TYPE_STRING_DATA_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addContents(DexFile file) {
+ // Nothing to do here.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo0(DexFile file, AnnotatedOutput out) {
+ ByteArray bytes = value.getBytes();
+ int utf16Size = value.getUtf16Size();
+
+ if (out.annotates()) {
+ out.annotate(Leb128Utils.unsignedLeb128Size(utf16Size),
+ "utf16_size: " + Hex.u4(utf16Size));
+ out.annotate(bytes.size() + 1, value.toQuoted());
+ }
+
+ out.writeUnsignedLeb128(utf16Size);
+ out.write(bytes);
+ out.writeByte(0);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toHuman() {
+ return value.toQuoted();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected int compareTo0(OffsettedItem other) {
+ StringDataItem otherData = (StringDataItem) other;
+
+ return value.compareTo(otherData.value);
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/StringIdItem.java b/dx/src/com/android/dx/dex/file/StringIdItem.java
new file mode 100644
index 0000000..cd0d57b
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/StringIdItem.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+/**
+ * Representation of a string inside a Dalvik file.
+ */
+public final class StringIdItem
+ extends IndexedItem implements Comparable {
+ /** size of instances when written out to a file, in bytes */
+ public static final int WRITE_SIZE = 4;
+
+ /** {@code non-null;} the string value */
+ private final CstUtf8 value;
+
+ /** {@code null-ok;} associated string data object, if known */
+ private StringDataItem data;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param value {@code non-null;} the string value
+ */
+ public StringIdItem(CstUtf8 value) {
+ if (value == null) {
+ throw new NullPointerException("value == null");
+ }
+
+ this.value = value;
+ this.data = null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof StringIdItem)) {
+ return false;
+ }
+
+ StringIdItem otherString = (StringIdItem) other;
+ return value.equals(otherString.value);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(Object other) {
+ StringIdItem otherString = (StringIdItem) other;
+ return value.compareTo(otherString.value);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ItemType itemType() {
+ return ItemType.TYPE_STRING_ID_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int writeSize() {
+ return WRITE_SIZE;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addContents(DexFile file) {
+ if (data == null) {
+ // The string data hasn't yet been added, so add it.
+ MixedItemSection stringData = file.getStringData();
+ data = new StringDataItem(value);
+ stringData.add(data);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(DexFile file, AnnotatedOutput out) {
+ int dataOff = data.getAbsoluteOffset();
+
+ if (out.annotates()) {
+ out.annotate(0, indexString() + ' ' + value.toQuoted(100));
+ out.annotate(4, " string_data_off: " + Hex.u4(dataOff));
+ }
+
+ out.writeInt(dataOff);
+ }
+
+ /**
+ * Gets the string value.
+ *
+ * @return {@code non-null;} the value
+ */
+ public CstUtf8 getValue() {
+ return value;
+ }
+
+ /**
+ * Gets the associated data object for this instance, if known.
+ *
+ * @return {@code null-ok;} the associated data object or {@code null}
+ * if not yet known
+ */
+ public StringDataItem getData() {
+ return data;
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/StringIdsSection.java b/dx/src/com/android/dx/dex/file/StringIdsSection.java
new file mode 100644
index 0000000..9039f43
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/StringIdsSection.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+import java.util.Collection;
+import java.util.TreeMap;
+
+/**
+ * Strings list section of a {@code .dex} file.
+ */
+public final class StringIdsSection
+ extends UniformItemSection {
+ /**
+ * {@code non-null;} map from string constants to {@link
+ * StringIdItem} instances
+ */
+ private final TreeMap<CstUtf8, StringIdItem> strings;
+
+ /**
+ * Constructs an instance. The file offset is initially unknown.
+ *
+ * @param file {@code non-null;} file that this instance is part of
+ */
+ public StringIdsSection(DexFile file) {
+ super("string_ids", file, 4);
+
+ strings = new TreeMap<CstUtf8, StringIdItem>();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Collection<? extends Item> items() {
+ return strings.values();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public IndexedItem get(Constant cst) {
+ if (cst == null) {
+ throw new NullPointerException("cst == null");
+ }
+
+ throwIfNotPrepared();
+
+ if (cst instanceof CstString) {
+ cst = ((CstString) cst).getString();
+ }
+
+ IndexedItem result = strings.get((CstUtf8) cst);
+
+ if (result == null) {
+ throw new IllegalArgumentException("not found");
+ }
+
+ return result;
+ }
+
+ /**
+ * Writes the portion of the file header that refers to this instance.
+ *
+ * @param out {@code non-null;} where to write
+ */
+ public void writeHeaderPart(AnnotatedOutput out) {
+ throwIfNotPrepared();
+
+ int sz = strings.size();
+ int offset = (sz == 0) ? 0 : getFileOffset();
+
+ if (out.annotates()) {
+ out.annotate(4, "string_ids_size: " + Hex.u4(sz));
+ out.annotate(4, "string_ids_off: " + Hex.u4(offset));
+ }
+
+ out.writeInt(sz);
+ out.writeInt(offset);
+ }
+
+ /**
+ * Interns an element into this instance.
+ *
+ * @param string {@code non-null;} the string to intern, as a regular Java
+ * {@code String}
+ * @return {@code non-null;} the interned string
+ */
+ public StringIdItem intern(String string) {
+ CstUtf8 utf8 = new CstUtf8(string);
+ return intern(new StringIdItem(utf8));
+ }
+
+ /**
+ * Interns an element into this instance.
+ *
+ * @param string {@code non-null;} the string to intern, as a {@link CstString}
+ * @return {@code non-null;} the interned string
+ */
+ public StringIdItem intern(CstString string) {
+ CstUtf8 utf8 = string.getString();
+ return intern(new StringIdItem(utf8));
+ }
+
+ /**
+ * Interns an element into this instance.
+ *
+ * @param string {@code non-null;} the string to intern, as a constant
+ * @return {@code non-null;} the interned string
+ */
+ public StringIdItem intern(CstUtf8 string) {
+ return intern(new StringIdItem(string));
+ }
+
+ /**
+ * Interns an element into this instance.
+ *
+ * @param string {@code non-null;} the string to intern
+ * @return {@code non-null;} the interned string
+ */
+ public StringIdItem intern(StringIdItem string) {
+ if (string == null) {
+ throw new NullPointerException("string == null");
+ }
+
+ throwIfPrepared();
+
+ CstUtf8 value = string.getValue();
+ StringIdItem already = strings.get(value);
+
+ if (already != null) {
+ return already;
+ }
+
+ strings.put(value, string);
+ return string;
+ }
+
+ /**
+ * Interns the components of a name-and-type into this instance.
+ *
+ * @param nat {@code non-null;} the name-and-type
+ */
+ public void intern(CstNat nat) {
+ intern(nat.getName());
+ intern(nat.getDescriptor());
+ }
+
+ /**
+ * Gets the index of the given string, which must have been added
+ * to this instance.
+ *
+ * @param string {@code non-null;} the string to look up
+ * @return {@code >= 0;} the string's index
+ */
+ public int indexOf(CstUtf8 string) {
+ if (string == null) {
+ throw new NullPointerException("string == null");
+ }
+
+ throwIfNotPrepared();
+
+ StringIdItem s = strings.get(string);
+
+ if (s == null) {
+ throw new IllegalArgumentException("not found");
+ }
+
+ return s.getIndex();
+ }
+
+ /**
+ * Gets the index of the given string, which must have been added
+ * to this instance.
+ *
+ * @param string {@code non-null;} the string to look up
+ * @return {@code >= 0;} the string's index
+ */
+ public int indexOf(CstString string) {
+ return indexOf(string.getString());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void orderItems() {
+ int idx = 0;
+
+ for (StringIdItem s : strings.values()) {
+ s.setIndex(idx);
+ idx++;
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/TypeIdItem.java b/dx/src/com/android/dx/dex/file/TypeIdItem.java
new file mode 100644
index 0000000..c257e00
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/TypeIdItem.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+/**
+ * Representation of a type reference inside a Dalvik file.
+ */
+public final class TypeIdItem extends IdItem {
+ /** size of instances when written out to a file, in bytes */
+ public static final int WRITE_SIZE = 4;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param type {@code non-null;} the constant for the type
+ */
+ public TypeIdItem(CstType type) {
+ super(type);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ItemType itemType() {
+ return ItemType.TYPE_TYPE_ID_ITEM;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int writeSize() {
+ return WRITE_SIZE;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addContents(DexFile file) {
+ file.getStringIds().intern(getDefiningClass().getDescriptor());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeTo(DexFile file, AnnotatedOutput out) {
+ CstType type = getDefiningClass();
+ CstUtf8 descriptor = type.getDescriptor();
+ int idx = file.getStringIds().indexOf(descriptor);
+
+ if (out.annotates()) {
+ out.annotate(0, indexString() + ' ' + descriptor.toHuman());
+ out.annotate(4, " descriptor_idx: " + Hex.u4(idx));
+ }
+
+ out.writeInt(idx);
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/TypeIdsSection.java b/dx/src/com/android/dx/dex/file/TypeIdsSection.java
new file mode 100644
index 0000000..bcc8250
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/TypeIdsSection.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+import java.util.Collection;
+import java.util.TreeMap;
+
+/**
+ * Type identifiers list section of a {@code .dex} file.
+ */
+public final class TypeIdsSection extends UniformItemSection {
+ /**
+ * {@code non-null;} map from types to {@link TypeIdItem} instances
+ */
+ private final TreeMap<Type, TypeIdItem> typeIds;
+
+ /**
+ * Constructs an instance. The file offset is initially unknown.
+ *
+ * @param file {@code non-null;} file that this instance is part of
+ */
+ public TypeIdsSection(DexFile file) {
+ super("type_ids", file, 4);
+
+ typeIds = new TreeMap<Type, TypeIdItem>();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Collection<? extends Item> items() {
+ return typeIds.values();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public IndexedItem get(Constant cst) {
+ if (cst == null) {
+ throw new NullPointerException("cst == null");
+ }
+
+ throwIfNotPrepared();
+
+ Type type = ((CstType) cst).getClassType();
+ IndexedItem result = typeIds.get(type);
+
+ if (result == null) {
+ throw new IllegalArgumentException("not found: " + cst);
+ }
+
+ return result;
+ }
+
+ /**
+ * Writes the portion of the file header that refers to this instance.
+ *
+ * @param out {@code non-null;} where to write
+ */
+ public void writeHeaderPart(AnnotatedOutput out) {
+ throwIfNotPrepared();
+
+ int sz = typeIds.size();
+ int offset = (sz == 0) ? 0 : getFileOffset();
+
+ if (sz > 65536) {
+ throw new UnsupportedOperationException("too many type ids");
+ }
+
+ if (out.annotates()) {
+ out.annotate(4, "type_ids_size: " + Hex.u4(sz));
+ out.annotate(4, "type_ids_off: " + Hex.u4(offset));
+ }
+
+ out.writeInt(sz);
+ out.writeInt(offset);
+ }
+
+ /**
+ * Interns an element into this instance.
+ *
+ * @param type {@code non-null;} the type to intern
+ * @return {@code non-null;} the interned reference
+ */
+ public TypeIdItem intern(Type type) {
+ if (type == null) {
+ throw new NullPointerException("type == null");
+ }
+
+ throwIfPrepared();
+
+ TypeIdItem result = typeIds.get(type);
+
+ if (result == null) {
+ result = new TypeIdItem(new CstType(type));
+ typeIds.put(type, result);
+ }
+
+ return result;
+ }
+
+ /**
+ * Interns an element into this instance.
+ *
+ * @param type {@code non-null;} the type to intern
+ * @return {@code non-null;} the interned reference
+ */
+ public TypeIdItem intern(CstType type) {
+ if (type == null) {
+ throw new NullPointerException("type == null");
+ }
+
+ throwIfPrepared();
+
+ Type typePerSe = type.getClassType();
+ TypeIdItem result = typeIds.get(typePerSe);
+
+ if (result == null) {
+ result = new TypeIdItem(type);
+ typeIds.put(typePerSe, result);
+ }
+
+ return result;
+ }
+
+ /**
+ * Gets the index of the given type, which must have
+ * been added to this instance.
+ *
+ * @param type {@code non-null;} the type to look up
+ * @return {@code >= 0;} the reference's index
+ */
+ public int indexOf(Type type) {
+ if (type == null) {
+ throw new NullPointerException("type == null");
+ }
+
+ throwIfNotPrepared();
+
+ TypeIdItem item = typeIds.get(type);
+
+ if (item == null) {
+ throw new IllegalArgumentException("not found: " + type);
+ }
+
+ return item.getIndex();
+ }
+
+ /**
+ * Gets the index of the given type, which must have
+ * been added to this instance.
+ *
+ * @param type {@code non-null;} the type to look up
+ * @return {@code >= 0;} the reference's index
+ */
+ public int indexOf(CstType type) {
+ if (type == null) {
+ throw new NullPointerException("type == null");
+ }
+
+ return indexOf(type.getClassType());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void orderItems() {
+ int idx = 0;
+
+ for (Object i : items()) {
+ ((TypeIdItem) i).setIndex(idx);
+ idx++;
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/TypeListItem.java b/dx/src/com/android/dx/dex/file/TypeListItem.java
new file mode 100644
index 0000000..b815dd3
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/TypeListItem.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+/**
+ * Representation of a list of class references.
+ */
+public final class TypeListItem extends OffsettedItem {
+ /** alignment requirement */
+ private static final int ALIGNMENT = 4;
+
+ /** element size in bytes */
+ private static final int ELEMENT_SIZE = 2;
+
+ /** header size in bytes */
+ private static final int HEADER_SIZE = 4;
+
+ /** {@code non-null;} the actual list */
+ private final TypeList list;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param list {@code non-null;} the actual list
+ */
+ public TypeListItem(TypeList list) {
+ super(ALIGNMENT, (list.size() * ELEMENT_SIZE) + HEADER_SIZE);
+
+ this.list = list;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return StdTypeList.hashContents(list);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ItemType itemType() {
+ return ItemType.TYPE_TYPE_LIST;
+ }
+
+ /** {@inheritDoc} */
+ public void addContents(DexFile file) {
+ TypeIdsSection typeIds = file.getTypeIds();
+ int sz = list.size();
+
+ for (int i = 0; i < sz; i++) {
+ typeIds.intern(list.getType(i));
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toHuman() {
+ throw new RuntimeException("unsupported");
+ }
+
+ /**
+ * Gets the underlying list.
+ *
+ * @return {@code non-null;} the list
+ */
+ public TypeList getList() {
+ return list;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void writeTo0(DexFile file, AnnotatedOutput out) {
+ TypeIdsSection typeIds = file.getTypeIds();
+ int sz = list.size();
+
+ if (out.annotates()) {
+ out.annotate(0, offsetString() + " type_list");
+ out.annotate(HEADER_SIZE, " size: " + Hex.u4(sz));
+ for (int i = 0; i < sz; i++) {
+ Type one = list.getType(i);
+ int idx = typeIds.indexOf(one);
+ out.annotate(ELEMENT_SIZE,
+ " " + Hex.u2(idx) + " // " + one.toHuman());
+ }
+ }
+
+ out.writeInt(sz);
+
+ for (int i = 0; i < sz; i++) {
+ out.writeShort(typeIds.indexOf(list.getType(i)));
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected int compareTo0(OffsettedItem other) {
+ TypeList thisList = this.list;
+ TypeList otherList = ((TypeListItem) other).list;
+
+ return StdTypeList.compareContents(thisList, otherList);
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/UniformItemSection.java b/dx/src/com/android/dx/dex/file/UniformItemSection.java
new file mode 100644
index 0000000..d8c09ab
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/UniformItemSection.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.util.AnnotatedOutput;
+
+import java.util.Collection;
+
+/**
+ * A section of a {@code .dex} file which consists of a sequence of
+ * {@link Item} objects. Each of the items must have the same size in
+ * the output.
+ */
+public abstract class UniformItemSection extends Section {
+ /**
+ * Constructs an instance. The file offset is initially unknown.
+ *
+ * @param name {@code null-ok;} the name of this instance, for annotation
+ * purposes
+ * @param file {@code non-null;} file that this instance is part of
+ * @param alignment {@code > 0;} alignment requirement for the final output;
+ * must be a power of 2
+ */
+ public UniformItemSection(String name, DexFile file, int alignment) {
+ super(name, file, alignment);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final int writeSize() {
+ Collection<? extends Item> items = items();
+ int sz = items.size();
+
+ if (sz == 0) {
+ return 0;
+ }
+
+ // Since each item has to be the same size, we can pick any.
+ return sz * items.iterator().next().writeSize();
+ }
+
+ /**
+ * Gets the item corresponding to the given {@link Constant}. This
+ * will throw an exception if the constant is not found, including
+ * if this instance isn't the sort that maps constants to {@link
+ * IndexedItem} instances.
+ *
+ * @param cst {@code non-null;} constant to look for
+ * @return {@code non-null;} the corresponding item found in this instance
+ */
+ public abstract IndexedItem get(Constant cst);
+
+ /** {@inheritDoc} */
+ @Override
+ protected final void prepare0() {
+ DexFile file = getFile();
+
+ orderItems();
+
+ for (Item one : items()) {
+ one.addContents(file);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected final void writeTo0(AnnotatedOutput out) {
+ DexFile file = getFile();
+ int alignment = getAlignment();
+
+ for (Item one : items()) {
+ one.writeTo(file, out);
+ out.alignTo(alignment);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final int getAbsoluteItemOffset(Item item) {
+ /*
+ * Since all items must be the same size, we can use the size
+ * of the one we're given to calculate its offset.
+ */
+ IndexedItem ii = (IndexedItem) item;
+ int relativeOffset = ii.getIndex() * ii.writeSize();
+
+ return getAbsoluteOffset(relativeOffset);
+ }
+
+ /**
+ * Alters or picks the order for items in this instance if desired,
+ * so that subsequent calls to {@link #items} will yield a
+ * so-ordered collection. If the items in this instance are indexed,
+ * then this method should also assign indices.
+ */
+ protected abstract void orderItems();
+}
diff --git a/dx/src/com/android/dx/dex/file/UniformListItem.java b/dx/src/com/android/dx/dex/file/UniformListItem.java
new file mode 100644
index 0000000..88919c7
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/UniformListItem.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Class that represents a contiguous list of uniform items. Each
+ * item in the list, in particular, must have the same write size and
+ * alignment.
+ *
+ * <p>This class inherits its alignment from its items, bumped up to
+ * {@code 4} if the items have a looser alignment requirement. If
+ * it is more than {@code 4}, then there will be a gap after the
+ * output list size (which is four bytes) and before the first item.</p>
+ *
+ * @param <T> type of element contained in an instance
+ */
+public final class UniformListItem<T extends OffsettedItem>
+ extends OffsettedItem {
+ /** the size of the list header */
+ private static final int HEADER_SIZE = 4;
+
+ /** {@code non-null;} the item type */
+ private final ItemType itemType;
+
+ /** {@code non-null;} the contents */
+ private final List<T> items;
+
+ /**
+ * Constructs an instance. It is illegal to modify the given list once
+ * it is used to construct an instance of this class.
+ *
+ * @param itemType {@code non-null;} the type of the item
+ * @param items {@code non-null and non-empty;} list of items to represent
+ */
+ public UniformListItem(ItemType itemType, List<T> items) {
+ super(getAlignment(items), writeSize(items));
+
+ if (itemType == null) {
+ throw new NullPointerException("itemType == null");
+ }
+
+ this.items = items;
+ this.itemType = itemType;
+ }
+
+ /**
+ * Helper for {@link #UniformListItem}, which returns the alignment
+ * requirement implied by the given list. See the header comment for
+ * more details.
+ *
+ * @param items {@code non-null;} list of items being represented
+ * @return {@code >= 4;} the alignment requirement
+ */
+ private static int getAlignment(List<? extends OffsettedItem> items) {
+ try {
+ // Since they all must have the same alignment, any one will do.
+ return Math.max(HEADER_SIZE, items.get(0).getAlignment());
+ } catch (IndexOutOfBoundsException ex) {
+ // Translate the exception.
+ throw new IllegalArgumentException("items.size() == 0");
+ } catch (NullPointerException ex) {
+ // Translate the exception.
+ throw new NullPointerException("items == null");
+ }
+ }
+
+ /**
+ * Calculates the write size for the given list.
+ *
+ * @param items {@code non-null;} the list in question
+ * @return {@code >= 0;} the write size
+ */
+ private static int writeSize(List<? extends OffsettedItem> items) {
+ /*
+ * This class assumes all included items are the same size,
+ * an assumption which is verified in place0().
+ */
+ OffsettedItem first = items.get(0);
+ return (items.size() * first.writeSize()) + getAlignment(items);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ItemType itemType() {
+ return itemType;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer(100);
+
+ sb.append(getClass().getName());
+ sb.append(items);
+
+ return sb.toString();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addContents(DexFile file) {
+ for (OffsettedItem i : items) {
+ i.addContents(file);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final String toHuman() {
+ StringBuffer sb = new StringBuffer(100);
+ boolean first = true;
+
+ sb.append("{");
+
+ for (OffsettedItem i : items) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append(", ");
+ }
+ sb.append(i.toHuman());
+ }
+
+ sb.append("}");
+ return sb.toString();
+ }
+
+ /**
+ * Gets the underlying list of items.
+ *
+ * @return {@code non-null;} the list
+ */
+ public final List<T> getItems() {
+ return items;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void place0(Section addedTo, int offset) {
+ offset += headerSize();
+
+ boolean first = true;
+ int theSize = -1;
+ int theAlignment = -1;
+
+ for (OffsettedItem i : items) {
+ int size = i.writeSize();
+ if (first) {
+ theSize = size;
+ theAlignment = i.getAlignment();
+ first = false;
+ } else {
+ if (size != theSize) {
+ throw new UnsupportedOperationException(
+ "item size mismatch");
+ }
+ if (i.getAlignment() != theAlignment) {
+ throw new UnsupportedOperationException(
+ "item alignment mismatch");
+ }
+ }
+
+ offset = i.place(addedTo, offset) + size;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void writeTo0(DexFile file, AnnotatedOutput out) {
+ int size = items.size();
+
+ if (out.annotates()) {
+ out.annotate(0, offsetString() + " " + typeName());
+ out.annotate(4, " size: " + Hex.u4(size));
+ }
+
+ out.writeInt(size);
+
+ for (OffsettedItem i : items) {
+ i.writeTo(file, out);
+ }
+ }
+
+ /**
+ * Get the size of the header of this list.
+ *
+ * @return {@code >= 0;} the header size
+ */
+ private int headerSize() {
+ /*
+ * Because of how this instance was set up, this is the same
+ * as the alignment.
+ */
+ return getAlignment();
+ }
+}
diff --git a/dx/src/com/android/dx/dex/file/ValueEncoder.java b/dx/src/com/android/dx/dex/file/ValueEncoder.java
new file mode 100644
index 0000000..fba64a7
--- /dev/null
+++ b/dx/src/com/android/dx/dex/file/ValueEncoder.java
@@ -0,0 +1,529 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.dex.file;
+
+import com.android.dx.rop.annotation.Annotation;
+import com.android.dx.rop.annotation.NameValuePair;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstAnnotation;
+import com.android.dx.rop.cst.CstArray;
+import com.android.dx.rop.cst.CstBoolean;
+import com.android.dx.rop.cst.CstByte;
+import com.android.dx.rop.cst.CstChar;
+import com.android.dx.rop.cst.CstDouble;
+import com.android.dx.rop.cst.CstEnumRef;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstFloat;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.CstKnownNull;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.rop.cst.CstLong;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstShort;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.AnnotatedOutput;
+import com.android.dx.util.Hex;
+
+import java.util.Collection;
+
+/**
+ * Handler for writing out {@code encoded_values} and parts
+ * thereof.
+ */
+public final class ValueEncoder {
+ /** annotation value type constant: {@code byte} */
+ private static final int VALUE_BYTE = 0x00;
+
+ /** annotation value type constant: {@code short} */
+ private static final int VALUE_SHORT = 0x02;
+
+ /** annotation value type constant: {@code char} */
+ private static final int VALUE_CHAR = 0x03;
+
+ /** annotation value type constant: {@code int} */
+ private static final int VALUE_INT = 0x04;
+
+ /** annotation value type constant: {@code long} */
+ private static final int VALUE_LONG = 0x06;
+
+ /** annotation value type constant: {@code float} */
+ private static final int VALUE_FLOAT = 0x10;
+
+ /** annotation value type constant: {@code double} */
+ private static final int VALUE_DOUBLE = 0x11;
+
+ /** annotation value type constant: {@code string} */
+ private static final int VALUE_STRING = 0x17;
+
+ /** annotation value type constant: {@code type} */
+ private static final int VALUE_TYPE = 0x18;
+
+ /** annotation value type constant: {@code field} */
+ private static final int VALUE_FIELD = 0x19;
+
+ /** annotation value type constant: {@code method} */
+ private static final int VALUE_METHOD = 0x1a;
+
+ /** annotation value type constant: {@code enum} */
+ private static final int VALUE_ENUM = 0x1b;
+
+ /** annotation value type constant: {@code array} */
+ private static final int VALUE_ARRAY = 0x1c;
+
+ /** annotation value type constant: {@code annotation} */
+ private static final int VALUE_ANNOTATION = 0x1d;
+
+ /** annotation value type constant: {@code null} */
+ private static final int VALUE_NULL = 0x1e;
+
+ /** annotation value type constant: {@code boolean} */
+ private static final int VALUE_BOOLEAN = 0x1f;
+
+ /** {@code non-null;} file being written */
+ private final DexFile file;
+
+ /** {@code non-null;} output stream to write to */
+ private final AnnotatedOutput out;
+
+ /**
+ * Construct an instance.
+ *
+ * @param file {@code non-null;} file being written
+ * @param out {@code non-null;} output stream to write to
+ */
+ public ValueEncoder(DexFile file, AnnotatedOutput out) {
+ if (file == null) {
+ throw new NullPointerException("file == null");
+ }
+
+ if (out == null) {
+ throw new NullPointerException("out == null");
+ }
+
+ this.file = file;
+ this.out = out;
+ }
+
+ /**
+ * Writes out the encoded form of the given constant.
+ *
+ * @param cst {@code non-null;} the constant to write
+ */
+ public void writeConstant(Constant cst) {
+ int type = constantToValueType(cst);
+ int arg;
+
+ switch (type) {
+ case VALUE_BYTE:
+ case VALUE_SHORT:
+ case VALUE_INT:
+ case VALUE_LONG: {
+ long value = ((CstLiteralBits) cst).getLongBits();
+ writeSignedIntegralValue(type, value);
+ break;
+ }
+ case VALUE_CHAR: {
+ long value = ((CstLiteralBits) cst).getLongBits();
+ writeUnsignedIntegralValue(type, value);
+ break;
+ }
+ case VALUE_FLOAT: {
+ // Shift value left 32 so that right-zero-extension works.
+ long value = ((CstFloat) cst).getLongBits() << 32;
+ writeRightZeroExtendedValue(type, value);
+ break;
+ }
+ case VALUE_DOUBLE: {
+ long value = ((CstDouble) cst).getLongBits();
+ writeRightZeroExtendedValue(type, value);
+ break;
+ }
+ case VALUE_STRING: {
+ int index = file.getStringIds().indexOf((CstString) cst);
+ writeUnsignedIntegralValue(type, (long) index);
+ break;
+ }
+ case VALUE_TYPE: {
+ int index = file.getTypeIds().indexOf((CstType) cst);
+ writeUnsignedIntegralValue(type, (long) index);
+ break;
+ }
+ case VALUE_FIELD: {
+ int index = file.getFieldIds().indexOf((CstFieldRef) cst);
+ writeUnsignedIntegralValue(type, (long) index);
+ break;
+ }
+ case VALUE_METHOD: {
+ int index = file.getMethodIds().indexOf((CstMethodRef) cst);
+ writeUnsignedIntegralValue(type, (long) index);
+ break;
+ }
+ case VALUE_ENUM: {
+ CstFieldRef fieldRef = ((CstEnumRef) cst).getFieldRef();
+ int index = file.getFieldIds().indexOf(fieldRef);
+ writeUnsignedIntegralValue(type, (long) index);
+ break;
+ }
+ case VALUE_ARRAY: {
+ out.writeByte(type);
+ writeArray((CstArray) cst, false);
+ break;
+ }
+ case VALUE_ANNOTATION: {
+ out.writeByte(type);
+ writeAnnotation(((CstAnnotation) cst).getAnnotation(),
+ false);
+ break;
+ }
+ case VALUE_NULL: {
+ out.writeByte(type);
+ break;
+ }
+ case VALUE_BOOLEAN: {
+ int value = ((CstBoolean) cst).getIntBits();
+ out.writeByte(type | (value << 5));
+ break;
+ }
+ default: {
+ throw new RuntimeException("Shouldn't happen");
+ }
+ }
+ }
+
+ /**
+ * Gets the value type for the given constant.
+ *
+ * @param cst {@code non-null;} the constant
+ * @return the value type; one of the {@code VALUE_*} constants
+ * defined by this class
+ */
+ private static int constantToValueType(Constant cst) {
+ /*
+ * TODO: Constant should probable have an associated enum, so this
+ * can be a switch().
+ */
+ if (cst instanceof CstByte) {
+ return VALUE_BYTE;
+ } else if (cst instanceof CstShort) {
+ return VALUE_SHORT;
+ } else if (cst instanceof CstChar) {
+ return VALUE_CHAR;
+ } else if (cst instanceof CstInteger) {
+ return VALUE_INT;
+ } else if (cst instanceof CstLong) {
+ return VALUE_LONG;
+ } else if (cst instanceof CstFloat) {
+ return VALUE_FLOAT;
+ } else if (cst instanceof CstDouble) {
+ return VALUE_DOUBLE;
+ } else if (cst instanceof CstString) {
+ return VALUE_STRING;
+ } else if (cst instanceof CstType) {
+ return VALUE_TYPE;
+ } else if (cst instanceof CstFieldRef) {
+ return VALUE_FIELD;
+ } else if (cst instanceof CstMethodRef) {
+ return VALUE_METHOD;
+ } else if (cst instanceof CstEnumRef) {
+ return VALUE_ENUM;
+ } else if (cst instanceof CstArray) {
+ return VALUE_ARRAY;
+ } else if (cst instanceof CstAnnotation) {
+ return VALUE_ANNOTATION;
+ } else if (cst instanceof CstKnownNull) {
+ return VALUE_NULL;
+ } else if (cst instanceof CstBoolean) {
+ return VALUE_BOOLEAN;
+ } else {
+ throw new RuntimeException("Shouldn't happen");
+ }
+ }
+
+ /**
+ * Writes out the encoded form of the given array, that is, as
+ * an {@code encoded_array} and not including a
+ * {@code value_type} prefix. If the output stream keeps
+ * (debugging) annotations and {@code topLevel} is
+ * {@code true}, then this method will write (debugging)
+ * annotations.
+ *
+ * @param array {@code non-null;} array instance to write
+ * @param topLevel {@code true} iff the given annotation is the
+ * top-level annotation or {@code false} if it is a sub-annotation
+ * of some other annotation
+ */
+ public void writeArray(CstArray array, boolean topLevel) {
+ boolean annotates = topLevel && out.annotates();
+ CstArray.List list = ((CstArray) array).getList();
+ int size = list.size();
+
+ if (annotates) {
+ out.annotate(" size: " + Hex.u4(size));
+ }
+
+ out.writeUnsignedLeb128(size);
+
+ for (int i = 0; i < size; i++) {
+ Constant cst = list.get(i);
+ if (annotates) {
+ out.annotate(" [" + Integer.toHexString(i) + "] " +
+ constantToHuman(cst));
+ }
+ writeConstant(cst);
+ }
+
+ if (annotates) {
+ out.endAnnotation();
+ }
+ }
+
+ /**
+ * Writes out the encoded form of the given annotation, that is,
+ * as an {@code encoded_annotation} and not including a
+ * {@code value_type} prefix. If the output stream keeps
+ * (debugging) annotations and {@code topLevel} is
+ * {@code true}, then this method will write (debugging)
+ * annotations.
+ *
+ * @param annotation {@code non-null;} annotation instance to write
+ * @param topLevel {@code true} iff the given annotation is the
+ * top-level annotation or {@code false} if it is a sub-annotation
+ * of some other annotation
+ */
+ public void writeAnnotation(Annotation annotation, boolean topLevel) {
+ boolean annotates = topLevel && out.annotates();
+ StringIdsSection stringIds = file.getStringIds();
+ TypeIdsSection typeIds = file.getTypeIds();
+
+ CstType type = annotation.getType();
+ int typeIdx = typeIds.indexOf(type);
+
+ if (annotates) {
+ out.annotate(" type_idx: " + Hex.u4(typeIdx) + " // " +
+ type.toHuman());
+ }
+
+ out.writeUnsignedLeb128(typeIds.indexOf(annotation.getType()));
+
+ Collection<NameValuePair> pairs = annotation.getNameValuePairs();
+ int size = pairs.size();
+
+ if (annotates) {
+ out.annotate(" size: " + Hex.u4(size));
+ }
+
+ out.writeUnsignedLeb128(size);
+
+ int at = 0;
+ for (NameValuePair pair : pairs) {
+ CstUtf8 name = pair.getName();
+ int nameIdx = stringIds.indexOf(name);
+ Constant value = pair.getValue();
+
+ if (annotates) {
+ out.annotate(0, " elements[" + at + "]:");
+ at++;
+ out.annotate(" name_idx: " + Hex.u4(nameIdx) + " // " +
+ name.toHuman());
+ }
+
+ out.writeUnsignedLeb128(nameIdx);
+
+ if (annotates) {
+ out.annotate(" value: " + constantToHuman(value));
+ }
+
+ writeConstant(value);
+ }
+
+ if (annotates) {
+ out.endAnnotation();
+ }
+ }
+
+ /**
+ * Gets the colloquial type name and human form of the type of the
+ * given constant, when used as an encoded value.
+ *
+ * @param cst {@code non-null;} the constant
+ * @return {@code non-null;} its type name and human form
+ */
+ public static String constantToHuman(Constant cst) {
+ int type = constantToValueType(cst);
+
+ if (type == VALUE_NULL) {
+ return "null";
+ }
+
+ StringBuilder sb = new StringBuilder();
+
+ sb.append(cst.typeName());
+ sb.append(' ');
+ sb.append(cst.toHuman());
+
+ return sb.toString();
+ }
+
+ /**
+ * Helper for {@link #writeConstant}, which writes out the value
+ * for any signed integral type.
+ *
+ * @param type the type constant
+ * @param value {@code long} bits of the value
+ */
+ private void writeSignedIntegralValue(int type, long value) {
+ /*
+ * Figure out how many bits are needed to represent the value,
+ * including a sign bit: The bit count is subtracted from 65
+ * and not 64 to account for the sign bit. The xor operation
+ * has the effect of leaving non-negative values alone and
+ * unary complementing negative values (so that a leading zero
+ * count always returns a useful number for our present
+ * purpose).
+ */
+ int requiredBits =
+ 65 - Long.numberOfLeadingZeros(value ^ (value >> 63));
+
+ // Round up the requiredBits to a number of bytes.
+ int requiredBytes = (requiredBits + 0x07) >> 3;
+
+ /*
+ * Write the header byte, which includes the type and
+ * requiredBytes - 1.
+ */
+ out.writeByte(type | ((requiredBytes - 1) << 5));
+
+ // Write the value, per se.
+ while (requiredBytes > 0) {
+ out.writeByte((byte) value);
+ value >>= 8;
+ requiredBytes--;
+ }
+ }
+
+ /**
+ * Helper for {@link #writeConstant}, which writes out the value
+ * for any unsigned integral type.
+ *
+ * @param type the type constant
+ * @param value {@code long} bits of the value
+ */
+ private void writeUnsignedIntegralValue(int type, long value) {
+ // Figure out how many bits are needed to represent the value.
+ int requiredBits = 64 - Long.numberOfLeadingZeros(value);
+ if (requiredBits == 0) {
+ requiredBits = 1;
+ }
+
+ // Round up the requiredBits to a number of bytes.
+ int requiredBytes = (requiredBits + 0x07) >> 3;
+
+ /*
+ * Write the header byte, which includes the type and
+ * requiredBytes - 1.
+ */
+ out.writeByte(type | ((requiredBytes - 1) << 5));
+
+ // Write the value, per se.
+ while (requiredBytes > 0) {
+ out.writeByte((byte) value);
+ value >>= 8;
+ requiredBytes--;
+ }
+ }
+
+ /**
+ * Helper for {@link #writeConstant}, which writes out a
+ * right-zero-extended value.
+ *
+ * @param type the type constant
+ * @param value {@code long} bits of the value
+ */
+ private void writeRightZeroExtendedValue(int type, long value) {
+ // Figure out how many bits are needed to represent the value.
+ int requiredBits = 64 - Long.numberOfTrailingZeros(value);
+ if (requiredBits == 0) {
+ requiredBits = 1;
+ }
+
+ // Round up the requiredBits to a number of bytes.
+ int requiredBytes = (requiredBits + 0x07) >> 3;
+
+ // Scootch the first bits to be written down to the low-order bits.
+ value >>= 64 - (requiredBytes * 8);
+
+ /*
+ * Write the header byte, which includes the type and
+ * requiredBytes - 1.
+ */
+ out.writeByte(type | ((requiredBytes - 1) << 5));
+
+ // Write the value, per se.
+ while (requiredBytes > 0) {
+ out.writeByte((byte) value);
+ value >>= 8;
+ requiredBytes--;
+ }
+ }
+
+
+ /**
+ * Helper for {@code addContents()} methods, which adds
+ * contents for a particular {@link Annotation}, calling itself
+ * recursively should it encounter a nested annotation.
+ *
+ * @param file {@code non-null;} the file to add to
+ * @param annotation {@code non-null;} the annotation to add contents for
+ */
+ public static void addContents(DexFile file, Annotation annotation) {
+ TypeIdsSection typeIds = file.getTypeIds();
+ StringIdsSection stringIds = file.getStringIds();
+
+ typeIds.intern(annotation.getType());
+
+ for (NameValuePair pair : annotation.getNameValuePairs()) {
+ stringIds.intern(pair.getName());
+ addContents(file, pair.getValue());
+ }
+ }
+
+ /**
+ * Helper for {@code addContents()} methods, which adds
+ * contents for a particular constant, calling itself recursively
+ * should it encounter a {@link CstArray} and calling {@link
+ * #addContents(DexFile,Annotation)} recursively should it
+ * encounter a {@link CstAnnotation}.
+ *
+ * @param file {@code non-null;} the file to add to
+ * @param cst {@code non-null;} the constant to add contents for
+ */
+ public static void addContents(DexFile file, Constant cst) {
+ if (cst instanceof CstAnnotation) {
+ addContents(file, ((CstAnnotation) cst).getAnnotation());
+ } else if (cst instanceof CstArray) {
+ CstArray.List list = ((CstArray) cst).getList();
+ int size = list.size();
+ for (int i = 0; i < size; i++) {
+ addContents(file, list.get(i));
+ }
+ } else {
+ file.internIfAppropriate(cst);
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/rop/annotation/Annotation.java b/dx/src/com/android/dx/rop/annotation/Annotation.java
new file mode 100644
index 0000000..ad6d67e
--- /dev/null
+++ b/dx/src/com/android/dx/rop/annotation/Annotation.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.annotation;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstAnnotation;
+import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.cst.TypedConstant;
+import com.android.dx.util.Hex;
+import com.android.dx.util.MutabilityControl;
+import com.android.dx.util.ToHuman;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.TreeMap;
+
+/**
+ * An annotation on an element of a class. Annotations have an
+ * associated type and additionally consist of a set of (name, value)
+ * pairs, where the names are unique.
+ */
+public final class Annotation extends MutabilityControl
+ implements Comparable<Annotation>, ToHuman {
+ /** {@code non-null;} type of the annotation */
+ private final CstType type;
+
+ /** {@code non-null;} the visibility of the annotation */
+ private final AnnotationVisibility visibility;
+
+ /** {@code non-null;} map from names to {@link NameValuePair} instances */
+ private final TreeMap<CstUtf8, NameValuePair> elements;
+
+ /**
+ * Construct an instance. It initially contains no elements.
+ *
+ * @param type {@code non-null;} type of the annotation
+ * @param visibility {@code non-null;} the visibility of the annotation
+ */
+ public Annotation(CstType type, AnnotationVisibility visibility) {
+ if (type == null) {
+ throw new NullPointerException("type == null");
+ }
+
+ if (visibility == null) {
+ throw new NullPointerException("visibility == null");
+ }
+
+ this.type = type;
+ this.visibility = visibility;
+ this.elements = new TreeMap<CstUtf8, NameValuePair>();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+ if (! (other instanceof Annotation)) {
+ return false;
+ }
+
+ Annotation otherAnnotation = (Annotation) other;
+
+ if (! (type.equals(otherAnnotation.type)
+ && (visibility == otherAnnotation.visibility))) {
+ return false;
+ }
+
+ return elements.equals(otherAnnotation.elements);
+ }
+
+ /** {@inheritDoc} */
+ public int hashCode() {
+ int hash = type.hashCode();
+ hash = (hash * 31) + elements.hashCode();
+ hash = (hash * 31) + visibility.hashCode();
+ return hash;
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(Annotation other) {
+ int result = type.compareTo(other.type);
+
+ if (result != 0) {
+ return result;
+ }
+
+ result = visibility.compareTo(other.visibility);
+
+ if (result != 0) {
+ return result;
+ }
+
+ Iterator<NameValuePair> thisIter = elements.values().iterator();
+ Iterator<NameValuePair> otherIter = other.elements.values().iterator();
+
+ while (thisIter.hasNext() && otherIter.hasNext()) {
+ NameValuePair thisOne = thisIter.next();
+ NameValuePair otherOne = otherIter.next();
+
+ result = thisOne.compareTo(otherOne);
+ if (result != 0) {
+ return result;
+ }
+ }
+
+ if (thisIter.hasNext()) {
+ return 1;
+ } else if (otherIter.hasNext()) {
+ return -1;
+ }
+
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return toHuman();
+ }
+
+ /** {@inheritDoc} */
+ public String toHuman() {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append(visibility.toHuman());
+ sb.append("-annotation ");
+ sb.append(type.toHuman());
+ sb.append(" {");
+
+ boolean first = true;
+ for (NameValuePair pair : elements.values()) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append(", ");
+ }
+ sb.append(pair.getName().toHuman());
+ sb.append(": ");
+ sb.append(pair.getValue().toHuman());
+ }
+
+ sb.append("}");
+ return sb.toString();
+ }
+
+ /**
+ * Gets the type of this instance.
+ *
+ * @return {@code non-null;} the type
+ */
+ public CstType getType() {
+ return type;
+ }
+
+ /**
+ * Gets the visibility of this instance.
+ *
+ * @return {@code non-null;} the visibility
+ */
+ public AnnotationVisibility getVisibility() {
+ return visibility;
+ }
+
+ /**
+ * Put an element into the set of (name, value) pairs for this instance.
+ * If there is a preexisting element with the same name, it will be
+ * replaced by this method.
+ *
+ * @param pair {@code non-null;} the (name, value) pair to place into this instance
+ */
+ public void put(NameValuePair pair) {
+ throwIfImmutable();
+
+ if (pair == null) {
+ throw new NullPointerException("pair == null");
+ }
+
+ elements.put(pair.getName(), pair);
+ }
+
+ /**
+ * Add an element to the set of (name, value) pairs for this instance.
+ * It is an error to call this method if there is a preexisting element
+ * with the same name.
+ *
+ * @param pair {@code non-null;} the (name, value) pair to add to this instance
+ */
+ public void add(NameValuePair pair) {
+ throwIfImmutable();
+
+ if (pair == null) {
+ throw new NullPointerException("pair == null");
+ }
+
+ CstUtf8 name = pair.getName();
+
+ if (elements.get(name) != null) {
+ throw new IllegalArgumentException("name already added: " + name);
+ }
+
+ elements.put(name, pair);
+ }
+
+ /**
+ * Gets the set of name-value pairs contained in this instance. The
+ * result is always unmodifiable.
+ *
+ * @return {@code non-null;} the set of name-value pairs
+ */
+ public Collection<NameValuePair> getNameValuePairs() {
+ return Collections.unmodifiableCollection(elements.values());
+ }
+}
diff --git a/dx/src/com/android/dx/rop/annotation/AnnotationVisibility.java b/dx/src/com/android/dx/rop/annotation/AnnotationVisibility.java
new file mode 100644
index 0000000..c717b8c
--- /dev/null
+++ b/dx/src/com/android/dx/rop/annotation/AnnotationVisibility.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.annotation;
+
+import com.android.dx.util.ToHuman;
+
+/**
+ * Visibility scope of an annotation.
+ */
+public enum AnnotationVisibility implements ToHuman {
+ RUNTIME("runtime"),
+ BUILD("build"),
+ SYSTEM("system"),
+ EMBEDDED("embedded");
+
+ /** {@code non-null;} the human-oriented string representation */
+ private final String human;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param human {@code non-null;} the human-oriented string representation
+ */
+ private AnnotationVisibility(String human) {
+ this.human = human;
+ }
+
+ /** {@inheritDoc} */
+ public String toHuman() {
+ return human;
+ }
+}
diff --git a/dx/src/com/android/dx/rop/annotation/Annotations.java b/dx/src/com/android/dx/rop/annotation/Annotations.java
new file mode 100644
index 0000000..807d5d4
--- /dev/null
+++ b/dx/src/com/android/dx/rop/annotation/Annotations.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.annotation;
+
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.util.MutabilityControl;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.TreeMap;
+
+/**
+ * List of {@link Annotation} instances.
+ */
+public final class Annotations extends MutabilityControl
+ implements Comparable<Annotations> {
+ /** {@code non-null;} immutable empty instance */
+ public static final Annotations EMPTY = new Annotations();
+
+ static {
+ EMPTY.setImmutable();
+ }
+
+ /** {@code non-null;} map from types to annotations */
+ private final TreeMap<CstType, Annotation> annotations;
+
+ /**
+ * Constructs an immutable instance which is the combination of the
+ * two given instances. The two instances must contain disjoint sets
+ * of types.
+ *
+ * @param a1 {@code non-null;} an instance
+ * @param a2 {@code non-null;} the other instance
+ * @return {@code non-null;} the combination
+ * @throws IllegalArgumentException thrown if there is a duplicate type
+ */
+ public static Annotations combine(Annotations a1, Annotations a2) {
+ Annotations result = new Annotations();
+
+ result.addAll(a1);
+ result.addAll(a2);
+ result.setImmutable();
+
+ return result;
+ }
+
+ /**
+ * Constructs an immutable instance which is the combination of the
+ * given instance with the given additional annotation. The latter's
+ * type must not already appear in the former.
+ *
+ * @param annotations {@code non-null;} the instance to augment
+ * @param annotation {@code non-null;} the additional annotation
+ * @return {@code non-null;} the combination
+ * @throws IllegalArgumentException thrown if there is a duplicate type
+ */
+ public static Annotations combine(Annotations annotations,
+ Annotation annotation) {
+ Annotations result = new Annotations();
+
+ result.addAll(annotations);
+ result.add(annotation);
+ result.setImmutable();
+
+ return result;
+ }
+
+ /**
+ * Constructs an empty instance.
+ */
+ public Annotations() {
+ annotations = new TreeMap<CstType, Annotation>();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return annotations.hashCode();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+ if (! (other instanceof Annotations)) {
+ return false;
+ }
+
+ Annotations otherAnnotations = (Annotations) other;
+
+ return annotations.equals(otherAnnotations.annotations);
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(Annotations other) {
+ Iterator<Annotation> thisIter = annotations.values().iterator();
+ Iterator<Annotation> otherIter = other.annotations.values().iterator();
+
+ while (thisIter.hasNext() && otherIter.hasNext()) {
+ Annotation thisOne = thisIter.next();
+ Annotation otherOne = otherIter.next();
+
+ int result = thisOne.compareTo(otherOne);
+ if (result != 0) {
+ return result;
+ }
+ }
+
+ if (thisIter.hasNext()) {
+ return 1;
+ } else if (otherIter.hasNext()) {
+ return -1;
+ }
+
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ boolean first = true;
+
+ sb.append("annotations{");
+
+ for (Annotation a : annotations.values()) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append(", ");
+ }
+ sb.append(a.toHuman());
+ }
+
+ sb.append("}");
+ return sb.toString();
+ }
+
+ /**
+ * Gets the number of elements in this instance.
+ *
+ * @return {@code >= 0;} the size
+ */
+ public int size() {
+ return annotations.size();
+ }
+
+ /**
+ * Adds an element to this instance. There must not already be an
+ * element of the same type.
+ *
+ * @param annotation {@code non-null;} the element to add
+ * @throws IllegalArgumentException thrown if there is a duplicate type
+ */
+ public void add(Annotation annotation) {
+ throwIfImmutable();
+
+ if (annotation == null) {
+ throw new NullPointerException("annotation == null");
+ }
+
+ CstType type = annotation.getType();
+
+ if (annotations.containsKey(type)) {
+ throw new IllegalArgumentException("duplicate type: " +
+ type.toHuman());
+ }
+
+ annotations.put(type, annotation);
+ }
+
+ /**
+ * Adds all of the elements of the given instance to this one. The
+ * instances must not have any duplicate types.
+ *
+ * @param toAdd {@code non-null;} the annotations to add
+ * @throws IllegalArgumentException thrown if there is a duplicate type
+ */
+ public void addAll(Annotations toAdd) {
+ throwIfImmutable();
+
+ if (toAdd == null) {
+ throw new NullPointerException("toAdd == null");
+ }
+
+ for (Annotation a : toAdd.annotations.values()) {
+ add(a);
+ }
+ }
+
+ /**
+ * Gets the set of annotations contained in this instance. The
+ * result is always unmodifiable.
+ *
+ * @return {@code non-null;} the set of annotations
+ */
+ public Collection<Annotation> getAnnotations() {
+ return Collections.unmodifiableCollection(annotations.values());
+ }
+}
diff --git a/dx/src/com/android/dx/rop/annotation/AnnotationsList.java b/dx/src/com/android/dx/rop/annotation/AnnotationsList.java
new file mode 100644
index 0000000..b97b385
--- /dev/null
+++ b/dx/src/com/android/dx/rop/annotation/AnnotationsList.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.annotation;
+
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * List of {@link Annotations} instances.
+ */
+public final class AnnotationsList
+ extends FixedSizeList {
+ /** {@code non-null;} immutable empty instance */
+ public static final AnnotationsList EMPTY = new AnnotationsList(0);
+
+ /**
+ * Constructs an immutable instance which is the combination of
+ * the two given instances. The two instances must each have the
+ * same number of elements, and each pair of elements must contain
+ * disjoint sets of types.
+ *
+ * @param list1 {@code non-null;} an instance
+ * @param list2 {@code non-null;} the other instance
+ * @return {@code non-null;} the combination
+ */
+ public static AnnotationsList combine(AnnotationsList list1,
+ AnnotationsList list2) {
+ int size = list1.size();
+
+ if (size != list2.size()) {
+ throw new IllegalArgumentException("list1.size() != list2.size()");
+ }
+
+ AnnotationsList result = new AnnotationsList(size);
+
+ for (int i = 0; i < size; i++) {
+ Annotations a1 = list1.get(i);
+ Annotations a2 = list2.get(i);
+ result.set(i, Annotations.combine(a1, a2));
+ }
+
+ result.setImmutable();
+ return result;
+ }
+
+ /**
+ * Constructs an instance. All indices initially contain {@code null}.
+ *
+ * @param size the size of the list
+ */
+ public AnnotationsList(int size) {
+ super(size);
+ }
+
+ /**
+ * Gets the element at the given index. It is an error to call
+ * this with the index for an element which was never set; if you
+ * do that, this will throw {@code NullPointerException}.
+ *
+ * @param n {@code >= 0, < size();} which index
+ * @return {@code non-null;} element at that index
+ */
+ public Annotations get(int n) {
+ return (Annotations) get0(n);
+ }
+
+ /**
+ * Sets the element at the given index. The given element must be
+ * immutable.
+ *
+ * @param n {@code >= 0, < size();} which index
+ * @param a {@code null-ok;} the element to set at {@code n}
+ */
+ public void set(int n, Annotations a) {
+ a.throwIfMutable();
+ set0(n, a);
+ }
+}
diff --git a/dx/src/com/android/dx/rop/annotation/NameValuePair.java b/dx/src/com/android/dx/rop/annotation/NameValuePair.java
new file mode 100644
index 0000000..39a9dfd
--- /dev/null
+++ b/dx/src/com/android/dx/rop/annotation/NameValuePair.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.annotation;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.CstUtf8;
+
+/**
+ * A (name, value) pair. These are used as the contents of an annotation.
+ */
+public final class NameValuePair implements Comparable<NameValuePair> {
+ /** {@code non-null;} the name */
+ private final CstUtf8 name;
+
+ /** {@code non-null;} the value */
+ private final Constant value;
+
+ /**
+ * Construct an instance.
+ *
+ * @param name {@code non-null;} the name
+ * @param value {@code non-null;} the value
+ */
+ public NameValuePair(CstUtf8 name, Constant value) {
+ if (name == null) {
+ throw new NullPointerException("name == null");
+ }
+
+ if (value == null) {
+ throw new NullPointerException("value == null");
+ }
+
+ // Reject CstUtf8 values. (They should be CstStrings.)
+ if (value instanceof CstUtf8) {
+ throw new IllegalArgumentException("bad value: " + value);
+ }
+
+ this.name = name;
+ this.value = value;
+ }
+
+ /** {@inheritDoc} */
+ public String toString() {
+ return name.toHuman() + ":" + value;
+ }
+
+ /** {@inheritDoc} */
+ public int hashCode() {
+ return name.hashCode() * 31 + value.hashCode();
+ }
+
+ /** {@inheritDoc} */
+ public boolean equals(Object other) {
+ if (! (other instanceof NameValuePair)) {
+ return false;
+ }
+
+ NameValuePair otherPair = (NameValuePair) other;
+
+ return name.equals(otherPair.name)
+ && value.equals(otherPair.value);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>Instances of this class compare in name-major and value-minor
+ * order.</p>
+ */
+ public int compareTo(NameValuePair other) {
+ int result = name.compareTo(other.name);
+
+ if (result != 0) {
+ return result;
+ }
+
+ return value.compareTo(other.value);
+ }
+
+ /**
+ * Gets the name.
+ *
+ * @return {@code non-null;} the name
+ */
+ public CstUtf8 getName() {
+ return name;
+ }
+
+ /**
+ * Gets the value.
+ *
+ * @return {@code non-null;} the value
+ */
+ public Constant getValue() {
+ return value;
+ }
+}
diff --git a/dx/src/com/android/dx/rop/code/AccessFlags.java b/dx/src/com/android/dx/rop/code/AccessFlags.java
new file mode 100644
index 0000000..2d84fe8
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/AccessFlags.java
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.util.Hex;
+
+/**
+ * Constants used as "access flags" in various places in classes, and
+ * related utilities. Although, at the rop layer, flags are generally
+ * ignored, this is the layer of communication, and as such, this
+ * package is where these definitions belong. The flag definitions are
+ * identical to Java access flags, but {@code ACC_SUPER} isn't
+ * used at all in translated code, and {@code ACC_SYNCHRONIZED}
+ * is only used in a very limited way.
+ */
+public final class AccessFlags {
+ /** public member / class */
+ public static final int ACC_PUBLIC = 0x0001;
+
+ /** private member */
+ public static final int ACC_PRIVATE = 0x0002;
+
+ /** protected member */
+ public static final int ACC_PROTECTED = 0x0004;
+
+ /** static member */
+ public static final int ACC_STATIC = 0x0008;
+
+ /** final member / class */
+ public static final int ACC_FINAL = 0x0010;
+
+ /**
+ * synchronized method; only valid in dex files for {@code native}
+ * methods
+ */
+ public static final int ACC_SYNCHRONIZED = 0x0020;
+
+ /**
+ * class with new-style {@code invokespecial} for superclass
+ * method access
+ */
+ public static final int ACC_SUPER = 0x0020;
+
+ /** volatile field */
+ public static final int ACC_VOLATILE = 0x0040;
+
+ /** bridge method (generated) */
+ public static final int ACC_BRIDGE = 0x0040;
+
+ /** transient field */
+ public static final int ACC_TRANSIENT = 0x0080;
+
+ /** varargs method */
+ public static final int ACC_VARARGS = 0x0080;
+
+ /** native method */
+ public static final int ACC_NATIVE = 0x0100;
+
+ /** "class" is in fact an public static final interface */
+ public static final int ACC_INTERFACE = 0x0200;
+
+ /** abstract method / class */
+ public static final int ACC_ABSTRACT = 0x0400;
+
+ /**
+ * method with strict floating point ({@code strictfp})
+ * behavior
+ */
+ public static final int ACC_STRICT = 0x0800;
+
+ /** synthetic member */
+ public static final int ACC_SYNTHETIC = 0x1000;
+
+ /** class is an annotation type */
+ public static final int ACC_ANNOTATION = 0x2000;
+
+ /**
+ * class is an enumerated type; field is an element of an enumerated
+ * type
+ */
+ public static final int ACC_ENUM = 0x4000;
+
+ /** method is a constructor */
+ public static final int ACC_CONSTRUCTOR = 0x10000;
+
+ /**
+ * method was declared {@code synchronized}; has no effect on
+ * execution (other than inspecting this flag, per se)
+ */
+ public static final int ACC_DECLARED_SYNCHRONIZED = 0x20000;
+
+ /** flags defined on classes */
+ public static final int CLASS_FLAGS =
+ ACC_PUBLIC | ACC_FINAL | ACC_SUPER | ACC_INTERFACE | ACC_ABSTRACT |
+ ACC_SYNTHETIC | ACC_ANNOTATION | ACC_ENUM;
+
+ /** flags defined on inner classes */
+ public static final int INNER_CLASS_FLAGS =
+ ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL |
+ ACC_INTERFACE | ACC_ABSTRACT | ACC_SYNTHETIC | ACC_ANNOTATION |
+ ACC_ENUM;
+
+ /** flags defined on fields */
+ public static final int FIELD_FLAGS =
+ ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL |
+ ACC_VOLATILE | ACC_TRANSIENT | ACC_SYNTHETIC | ACC_ENUM;
+
+ /** flags defined on methods */
+ public static final int METHOD_FLAGS =
+ ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL |
+ ACC_SYNCHRONIZED | ACC_BRIDGE | ACC_VARARGS | ACC_NATIVE |
+ ACC_ABSTRACT | ACC_STRICT | ACC_SYNTHETIC | ACC_CONSTRUCTOR |
+ ACC_DECLARED_SYNCHRONIZED;
+
+ /** indicates conversion of class flags */
+ private static final int CONV_CLASS = 1;
+
+ /** indicates conversion of field flags */
+ private static final int CONV_FIELD = 2;
+
+ /** indicates conversion of method flags */
+ private static final int CONV_METHOD = 3;
+
+ /**
+ * This class is uninstantiable.
+ */
+ private AccessFlags() {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Returns a human-oriented string representing the given access flags,
+ * as defined on classes (not fields or methods).
+ *
+ * @param flags the flags
+ * @return {@code non-null;} human-oriented string
+ */
+ public static String classString(int flags) {
+ return humanHelper(flags, CLASS_FLAGS, CONV_CLASS);
+ }
+
+ /**
+ * Returns a human-oriented string representing the given access flags,
+ * as defined on inner classes.
+ *
+ * @param flags the flags
+ * @return {@code non-null;} human-oriented string
+ */
+ public static String innerClassString(int flags) {
+ return humanHelper(flags, INNER_CLASS_FLAGS, CONV_CLASS);
+ }
+
+ /**
+ * Returns a human-oriented string representing the given access flags,
+ * as defined on fields (not classes or methods).
+ *
+ * @param flags the flags
+ * @return {@code non-null;} human-oriented string
+ */
+ public static String fieldString(int flags) {
+ return humanHelper(flags, FIELD_FLAGS, CONV_FIELD);
+ }
+
+ /**
+ * Returns a human-oriented string representing the given access flags,
+ * as defined on methods (not classes or fields).
+ *
+ * @param flags the flags
+ * @return {@code non-null;} human-oriented string
+ */
+ public static String methodString(int flags) {
+ return humanHelper(flags, METHOD_FLAGS, CONV_METHOD);
+ }
+
+ /**
+ * Returns whether the flag {@code ACC_PUBLIC} is on in the given
+ * flags.
+ *
+ * @param flags the flags to check
+ * @return the value of the {@code ACC_PUBLIC} flag
+ */
+ public static boolean isPublic(int flags) {
+ return (flags & ACC_PUBLIC) != 0;
+ }
+
+ /**
+ * Returns whether the flag {@code ACC_PROTECTED} is on in the given
+ * flags.
+ *
+ * @param flags the flags to check
+ * @return the value of the {@code ACC_PROTECTED} flag
+ */
+ public static boolean isProtected(int flags) {
+ return (flags & ACC_PROTECTED) != 0;
+ }
+
+ /**
+ * Returns whether the flag {@code ACC_PRIVATE} is on in the given
+ * flags.
+ *
+ * @param flags the flags to check
+ * @return the value of the {@code ACC_PRIVATE} flag
+ */
+ public static boolean isPrivate(int flags) {
+ return (flags & ACC_PRIVATE) != 0;
+ }
+
+ /**
+ * Returns whether the flag {@code ACC_STATIC} is on in the given
+ * flags.
+ *
+ * @param flags the flags to check
+ * @return the value of the {@code ACC_STATIC} flag
+ */
+ public static boolean isStatic(int flags) {
+ return (flags & ACC_STATIC) != 0;
+ }
+
+ /**
+ * Returns whether the flag {@code ACC_SYNCHRONIZED} is on in
+ * the given flags.
+ *
+ * @param flags the flags to check
+ * @return the value of the {@code ACC_SYNCHRONIZED} flag
+ */
+ public static boolean isSynchronized(int flags) {
+ return (flags & ACC_SYNCHRONIZED) != 0;
+ }
+
+ /**
+ * Returns whether the flag {@code ACC_ABSTRACT} is on in the given
+ * flags.
+ *
+ * @param flags the flags to check
+ * @return the value of the {@code ACC_ABSTRACT} flag
+ */
+ public static boolean isAbstract(int flags) {
+ return (flags & ACC_ABSTRACT) != 0;
+ }
+
+ /**
+ * Returns whether the flag {@code ACC_NATIVE} is on in the given
+ * flags.
+ *
+ * @param flags the flags to check
+ * @return the value of the {@code ACC_NATIVE} flag
+ */
+ public static boolean isNative(int flags) {
+ return (flags & ACC_NATIVE) != 0;
+ }
+
+ /**
+ * Returns whether the flag {@code ACC_ANNOTATION} is on in the given
+ * flags.
+ *
+ * @param flags the flags to check
+ * @return the value of the {@code ACC_ANNOTATION} flag
+ */
+ public static boolean isAnnotation(int flags) {
+ return (flags & ACC_ANNOTATION) != 0;
+ }
+
+ /**
+ * Returns whether the flag {@code ACC_DECLARED_SYNCHRONIZED} is
+ * on in the given flags.
+ *
+ * @param flags the flags to check
+ * @return the value of the {@code ACC_DECLARED_SYNCHRONIZED} flag
+ */
+ public static boolean isDeclaredSynchronized(int flags) {
+ return (flags & ACC_DECLARED_SYNCHRONIZED) != 0;
+ }
+
+ /**
+ * Helper to return a human-oriented string representing the given
+ * access flags.
+ *
+ * @param flags the defined flags
+ * @param mask mask for the "defined" bits
+ * @param what what the flags represent (one of {@code CONV_*})
+ * @return {@code non-null;} human-oriented string
+ */
+ private static String humanHelper(int flags, int mask, int what) {
+ StringBuffer sb = new StringBuffer(80);
+ int extra = flags & ~mask;
+
+ flags &= mask;
+
+ if ((flags & ACC_PUBLIC) != 0) {
+ sb.append("|public");
+ }
+ if ((flags & ACC_PRIVATE) != 0) {
+ sb.append("|private");
+ }
+ if ((flags & ACC_PROTECTED) != 0) {
+ sb.append("|protected");
+ }
+ if ((flags & ACC_STATIC) != 0) {
+ sb.append("|static");
+ }
+ if ((flags & ACC_FINAL) != 0) {
+ sb.append("|final");
+ }
+ if ((flags & ACC_SYNCHRONIZED) != 0) {
+ if (what == CONV_CLASS) {
+ sb.append("|super");
+ } else {
+ sb.append("|synchronized");
+ }
+ }
+ if ((flags & ACC_VOLATILE) != 0) {
+ if (what == CONV_METHOD) {
+ sb.append("|bridge");
+ } else {
+ sb.append("|volatile");
+ }
+ }
+ if ((flags & ACC_TRANSIENT) != 0) {
+ if (what == CONV_METHOD) {
+ sb.append("|varargs");
+ } else {
+ sb.append("|transient");
+ }
+ }
+ if ((flags & ACC_NATIVE) != 0) {
+ sb.append("|native");
+ }
+ if ((flags & ACC_INTERFACE) != 0) {
+ sb.append("|interface");
+ }
+ if ((flags & ACC_ABSTRACT) != 0) {
+ sb.append("|abstract");
+ }
+ if ((flags & ACC_STRICT) != 0) {
+ sb.append("|strictfp");
+ }
+ if ((flags & ACC_SYNTHETIC) != 0) {
+ sb.append("|synthetic");
+ }
+ if ((flags & ACC_ANNOTATION) != 0) {
+ sb.append("|annotation");
+ }
+ if ((flags & ACC_ENUM) != 0) {
+ sb.append("|enum");
+ }
+ if ((flags & ACC_CONSTRUCTOR) != 0) {
+ sb.append("|constructor");
+ }
+ if ((flags & ACC_DECLARED_SYNCHRONIZED) != 0) {
+ sb.append("|declared_synchronized");
+ }
+
+ if ((extra != 0) || (sb.length() == 0)) {
+ sb.append('|');
+ sb.append(Hex.u2(extra));
+ }
+
+ return sb.substring(1);
+ }
+}
diff --git a/dx/src/com/android/dx/rop/code/BasicBlock.java b/dx/src/com/android/dx/rop/code/BasicBlock.java
new file mode 100644
index 0000000..d6ee886
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/BasicBlock.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IntList;
+import com.android.dx.util.LabeledItem;
+
+/**
+ * Basic block of register-based instructions.
+ */
+public final class BasicBlock implements LabeledItem {
+ /** {@code >= 0;} target label for this block */
+ private final int label;
+
+ /** {@code non-null;} list of instructions in this block */
+ private final InsnList insns;
+
+ /**
+ * {@code non-null;} full list of successors that this block may
+ * branch to
+ */
+ private final IntList successors;
+
+ /**
+ * {@code >= -1;} the primary / standard-flow / "default" successor, or
+ * {@code -1} if this block has no successors (that is, it
+ * exits the function/method)
+ */
+ private final int primarySuccessor;
+
+ /**
+ * Constructs an instance. The predecessor set is set to {@code null}.
+ *
+ * @param label {@code >= 0;} target label for this block
+ * @param insns {@code non-null;} list of instructions in this block
+ * @param successors {@code non-null;} full list of successors that this
+ * block may branch to
+ * @param primarySuccessor {@code >= -1;} the primary / standard-flow /
+ * "default" successor, or {@code -1} if this block has no
+ * successors (that is, it exits the function/method or is an
+ * unconditional throw)
+ */
+ public BasicBlock(int label, InsnList insns, IntList successors,
+ int primarySuccessor) {
+ if (label < 0) {
+ throw new IllegalArgumentException("label < 0");
+ }
+
+ try {
+ insns.throwIfMutable();
+ } catch (NullPointerException ex) {
+ // Elucidate exception.
+ throw new NullPointerException("insns == null");
+ }
+
+ int sz = insns.size();
+
+ if (sz == 0) {
+ throw new IllegalArgumentException("insns.size() == 0");
+ }
+
+ for (int i = sz - 2; i >= 0; i--) {
+ Rop one = insns.get(i).getOpcode();
+ if (one.getBranchingness() != Rop.BRANCH_NONE) {
+ throw new IllegalArgumentException("insns[" + i + "] is a " +
+ "branch or can throw");
+ }
+ }
+
+ Insn lastInsn = insns.get(sz - 1);
+ if (lastInsn.getOpcode().getBranchingness() == Rop.BRANCH_NONE) {
+ throw new IllegalArgumentException("insns does not end with " +
+ "a branch or throwing " +
+ "instruction");
+ }
+
+ try {
+ successors.throwIfMutable();
+ } catch (NullPointerException ex) {
+ // Elucidate exception.
+ throw new NullPointerException("successors == null");
+ }
+
+ if (primarySuccessor < -1) {
+ throw new IllegalArgumentException("primarySuccessor < -1");
+ }
+
+ if (primarySuccessor >= 0 && !successors.contains(primarySuccessor)) {
+ throw new IllegalArgumentException(
+ "primarySuccessor not in successors");
+ }
+
+ this.label = label;
+ this.insns = insns;
+ this.successors = successors;
+ this.primarySuccessor = primarySuccessor;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Instances of this class compare by identity. That is,
+ * {@code x.equals(y)} is only true if {@code x == y}.
+ */
+ @Override
+ public boolean equals(Object other) {
+ return (this == other);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Return the identity hashcode of this instance. This is proper,
+ * since instances of this class compare by identity (see {@link #equals}).
+ */
+ @Override
+ public int hashCode() {
+ return System.identityHashCode(this);
+ }
+
+ /**
+ * Gets the target label of this block.
+ *
+ * @return {@code >= 0;} the label
+ */
+ public int getLabel() {
+ return label;
+ }
+
+ /**
+ * Gets the list of instructions inside this block.
+ *
+ * @return {@code non-null;} the instruction list
+ */
+ public InsnList getInsns() {
+ return insns;
+ }
+
+ /**
+ * Gets the list of successors that this block may branch to.
+ *
+ * @return {@code non-null;} the successors list
+ */
+ public IntList getSuccessors() {
+ return successors;
+ }
+
+ /**
+ * Gets the primary successor of this block.
+ *
+ * @return {@code >= -1;} the primary successor, or {@code -1} if this
+ * block has no successors at all
+ */
+ public int getPrimarySuccessor() {
+ return primarySuccessor;
+ }
+
+ /**
+ * Gets the secondary successor of this block. It is only valid to call
+ * this method on blocks that have exactly two successors.
+ *
+ * @return {@code >= 0;} the secondary successor
+ */
+ public int getSecondarySuccessor() {
+ if (successors.size() != 2) {
+ throw new UnsupportedOperationException(
+ "block doesn't have exactly two successors");
+ }
+
+ int succ = successors.get(0);
+ if (succ == primarySuccessor) {
+ succ = successors.get(1);
+ }
+
+ return succ;
+ }
+
+ /**
+ * Gets the first instruction of this block. This is just a
+ * convenient shorthand for {@code getInsns().get(0)}.
+ *
+ * @return {@code non-null;} the first instruction
+ */
+ public Insn getFirstInsn() {
+ return insns.get(0);
+ }
+
+ /**
+ * Gets the last instruction of this block. This is just a
+ * convenient shorthand for {@code getInsns().getLast()}.
+ *
+ * @return {@code non-null;} the last instruction
+ */
+ public Insn getLastInsn() {
+ return insns.getLast();
+ }
+
+ /**
+ * Returns whether this block might throw an exception. This is
+ * just a convenient shorthand for {@code getLastInsn().canThrow()}.
+ *
+ * @return {@code true} iff this block might throw an
+ * exception
+ */
+ public boolean canThrow() {
+ return insns.getLast().canThrow();
+ }
+
+ /**
+ * Returns whether this block has any associated exception handlers.
+ * This is just a shorthand for inspecting the last instruction in
+ * the block to see if it could throw, and if so, whether it in fact
+ * has any associated handlers.
+ *
+ * @return {@code true} iff this block has any associated
+ * exception handlers
+ */
+ public boolean hasExceptionHandlers() {
+ Insn lastInsn = insns.getLast();
+ return lastInsn.getCatches().size() != 0;
+ }
+
+ /**
+ * Returns the exception handler types associated with this block,
+ * if any. This is just a shorthand for inspecting the last
+ * instruction in the block to see if it could throw, and if so,
+ * grabbing the catch list out of it. If not, this returns an
+ * empty list (not {@code null}).
+ *
+ * @return {@code non-null;} the exception handler types associated with
+ * this block
+ */
+ public TypeList getExceptionHandlerTypes() {
+ Insn lastInsn = insns.getLast();
+ return lastInsn.getCatches();
+ }
+
+ /**
+ * Returns an instance that is identical to this one, except that
+ * the registers in each instruction are offset by the given
+ * amount.
+ *
+ * @param delta the amount to offset register numbers by
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public BasicBlock withRegisterOffset(int delta) {
+ return new BasicBlock(label, insns.withRegisterOffset(delta),
+ successors, primarySuccessor);
+ }
+
+ public String toString() {
+ return '{' + Hex.u2(label) + '}';
+ }
+
+ /**
+ * BasicBlock visitor interface
+ */
+ public interface Visitor {
+ /**
+ * Visits a basic block
+ * @param b block visited
+ */
+ public void visitBlock (BasicBlock b);
+ }
+}
diff --git a/dx/src/com/android/dx/rop/code/BasicBlockList.java b/dx/src/com/android/dx/rop/code/BasicBlockList.java
new file mode 100644
index 0000000..ea7b497
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/BasicBlockList.java
@@ -0,0 +1,398 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IntList;
+import com.android.dx.util.LabeledList;
+
+/**
+ * List of {@link BasicBlock} instances.
+ */
+public final class BasicBlockList extends LabeledList {
+ /**
+ * {@code >= -1;} the count of registers required by this method or
+ * {@code -1} if not yet calculated
+ */
+ private int regCount;
+
+ /**
+ * Constructs an instance. All indices initially contain {@code null},
+ * and the first-block label is initially {@code -1}.
+ *
+ * @param size the size of the list
+ */
+ public BasicBlockList(int size) {
+ super(size);
+
+ regCount = -1;
+ }
+
+ /**
+ * Constructs a mutable copy for {@code getMutableCopy()}.
+ *
+ * @param old block to copy
+ */
+ private BasicBlockList (BasicBlockList old) {
+ super(old);
+ regCount = old.regCount;
+ }
+
+
+ /**
+ * Gets the element at the given index. It is an error to call
+ * this with the index for an element which was never set; if you
+ * do that, this will throw {@code NullPointerException}.
+ *
+ * @param n {@code >= 0, < size();} which index
+ * @return {@code non-null;} element at that index
+ */
+ public BasicBlock get(int n) {
+ return (BasicBlock) get0(n);
+ }
+
+ /**
+ * Sets the basic block at the given index.
+ *
+ * @param n {@code >= 0, < size();} which index
+ * @param bb {@code null-ok;} the element to set at {@code n}
+ */
+ public void set(int n, BasicBlock bb) {
+ super.set(n, bb);
+
+ // Reset regCount, since it will need to be recalculated.
+ regCount = -1;
+ }
+
+ /**
+ * Returns how many registers this method requires. This is simply
+ * the maximum of register-number-plus-category referred to by this
+ * instance's instructions (indirectly through {@link BasicBlock}
+ * instances).
+ *
+ * @return {@code >= 0;} the register count
+ */
+ public int getRegCount() {
+ if (regCount == -1) {
+ RegCountVisitor visitor = new RegCountVisitor();
+ forEachInsn(visitor);
+ regCount = visitor.getRegCount();
+ }
+
+ return regCount;
+ }
+
+ /**
+ * Gets the total instruction count for this instance. This is the
+ * sum of the instruction counts of each block.
+ *
+ * @return {@code >= 0;} the total instruction count
+ */
+ public int getInstructionCount() {
+ int sz = size();
+ int result = 0;
+
+ for (int i = 0; i < sz; i++) {
+ BasicBlock one = (BasicBlock) getOrNull0(i);
+ if (one != null) {
+ result += one.getInsns().size();
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Gets the total instruction count for this instance, ignoring
+ * mark-local instructions which are not actually emitted.
+ *
+ * @return {@code >= 0;} the total instruction count
+ */
+ public int getEffectiveInstructionCount() {
+ int sz = size();
+ int result = 0;
+
+ for (int i = 0; i < sz; i++) {
+ BasicBlock one = (BasicBlock) getOrNull0(i);
+ if (one != null) {
+ InsnList insns = one.getInsns();
+ int insnsSz = insns.size();
+
+ for (int j = 0; j < insnsSz; j++) {
+ Insn insn = insns.get(j);
+
+ if (insn.getOpcode().getOpcode() != RegOps.MARK_LOCAL) {
+ result++;
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+
+ /**
+ * Gets the first block in the list with the given label, if any.
+ *
+ * @param label {@code >= 0;} the label to look for
+ * @return {@code non-null;} the so-labelled block
+ * @throws IllegalArgumentException thrown if the label isn't found
+ */
+ public BasicBlock labelToBlock(int label) {
+ int idx = indexOfLabel(label);
+
+ if (idx < 0) {
+ throw new IllegalArgumentException("no such label: "
+ + Hex.u2(label));
+ }
+
+ return get(idx);
+ }
+
+ /**
+ * Visits each instruction of each block in the list, in order.
+ *
+ * @param visitor {@code non-null;} visitor to use
+ */
+ public void forEachInsn(Insn.Visitor visitor) {
+ int sz = size();
+
+ for (int i = 0; i < sz; i++) {
+ BasicBlock one = get(i);
+ InsnList insns = one.getInsns();
+ insns.forEach(visitor);
+ }
+ }
+
+ /**
+ * Returns an instance that is identical to this one, except that
+ * the registers in each instruction are offset by the given
+ * amount. Mutability of the result is inherited from the
+ * original.
+ *
+ * @param delta the amount to offset register numbers by
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public BasicBlockList withRegisterOffset(int delta) {
+ int sz = size();
+ BasicBlockList result = new BasicBlockList(sz);
+
+ for (int i = 0; i < sz; i++) {
+ BasicBlock one = (BasicBlock) get0(i);
+ if (one != null) {
+ result.set(i, one.withRegisterOffset(delta));
+ }
+ }
+
+ if (isImmutable()) {
+ result.setImmutable();
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns a mutable copy of this list.
+ *
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public BasicBlockList getMutableCopy() {
+ return new BasicBlockList(this);
+ }
+
+ /**
+ * Gets the preferred successor for the given block. If the block
+ * only has one successor, then that is the preferred successor.
+ * Otherwise, if the block has a primay successor, then that is
+ * the preferred successor. If the block has no successors, then
+ * this returns {@code null}.
+ *
+ * @param block {@code non-null;} the block in question
+ * @return {@code null-ok;} the preferred successor, if any
+ */
+ public BasicBlock preferredSuccessorOf(BasicBlock block) {
+ int primarySuccessor = block.getPrimarySuccessor();
+ IntList successors = block.getSuccessors();
+ int succSize = successors.size();
+
+ switch (succSize) {
+ case 0: {
+ return null;
+ }
+ case 1: {
+ return labelToBlock(successors.get(0));
+ }
+ }
+
+ if (primarySuccessor != -1) {
+ return labelToBlock(primarySuccessor);
+ } else {
+ return labelToBlock(successors.get(0));
+ }
+ }
+
+ /**
+ * Compares the catches of two blocks for equality. This includes
+ * both the catch types and target labels.
+ *
+ * @param block1 {@code non-null;} one block to compare
+ * @param block2 {@code non-null;} the other block to compare
+ * @return {@code true} if the two blocks' non-primary successors
+ * are identical
+ */
+ public boolean catchesEqual(BasicBlock block1,
+ BasicBlock block2) {
+ TypeList catches1 = block1.getExceptionHandlerTypes();
+ TypeList catches2 = block2.getExceptionHandlerTypes();
+
+ if (!StdTypeList.equalContents(catches1, catches2)) {
+ return false;
+ }
+
+ IntList succ1 = block1.getSuccessors();
+ IntList succ2 = block2.getSuccessors();
+ int size = succ1.size(); // Both are guaranteed to be the same size.
+
+ int primary1 = block1.getPrimarySuccessor();
+ int primary2 = block2.getPrimarySuccessor();
+
+ if (((primary1 == -1) || (primary2 == -1))
+ && (primary1 != primary2)) {
+ /*
+ * For the current purpose, both blocks in question must
+ * either both have a primary or both not have a primary to
+ * be considered equal, and it turns out here that that's not
+ * the case.
+ */
+ return false;
+ }
+
+ for (int i = 0; i < size; i++) {
+ int label1 = succ1.get(i);
+ int label2 = succ2.get(i);
+
+ if (label1 == primary1) {
+ /*
+ * It should be the case that block2's primary is at the
+ * same index. If not, we consider the blocks unequal for
+ * the current purpose.
+ */
+ if (label2 != primary2) {
+ return false;
+ }
+ continue;
+ }
+
+ if (label1 != label2) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Instruction visitor class for counting registers used.
+ */
+ private static class RegCountVisitor
+ implements Insn.Visitor {
+ /** {@code >= 0;} register count in-progress */
+ private int regCount;
+
+ /**
+ * Constructs an instance.
+ */
+ public RegCountVisitor() {
+ regCount = 0;
+ }
+
+ /**
+ * Gets the register count.
+ *
+ * @return {@code >= 0;} the count
+ */
+ public int getRegCount() {
+ return regCount;
+ }
+
+ /** {@inheritDoc} */
+ public void visitPlainInsn(PlainInsn insn) {
+ visit(insn);
+ }
+
+ /** {@inheritDoc} */
+ public void visitPlainCstInsn(PlainCstInsn insn) {
+ visit(insn);
+ }
+
+ /** {@inheritDoc} */
+ public void visitSwitchInsn(SwitchInsn insn) {
+ visit(insn);
+ }
+
+ /** {@inheritDoc} */
+ public void visitThrowingCstInsn(ThrowingCstInsn insn) {
+ visit(insn);
+ }
+
+ /** {@inheritDoc} */
+ public void visitThrowingInsn(ThrowingInsn insn) {
+ visit(insn);
+ }
+
+ /** {@inheritDoc} */
+ public void visitFillArrayDataInsn(FillArrayDataInsn insn) {
+ visit(insn);
+ }
+
+ /**
+ * Helper for all the {@code visit*} methods.
+ *
+ * @param insn {@code non-null;} instruction being visited
+ */
+ private void visit(Insn insn) {
+ RegisterSpec result = insn.getResult();
+
+ if (result != null) {
+ processReg(result);
+ }
+
+ RegisterSpecList sources = insn.getSources();
+ int sz = sources.size();
+
+ for (int i = 0; i < sz; i++) {
+ processReg(sources.get(i));
+ }
+ }
+
+ /**
+ * Processes the given register spec.
+ *
+ * @param spec {@code non-null;} the register spec
+ */
+ private void processReg(RegisterSpec spec) {
+ int reg = spec.getNextReg();
+
+ if (reg > regCount) {
+ regCount = reg;
+ }
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/rop/code/ConservativeTranslationAdvice.java b/dx/src/com/android/dx/rop/code/ConservativeTranslationAdvice.java
new file mode 100644
index 0000000..6c48acf
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/ConservativeTranslationAdvice.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+/**
+ * Implementation of {@link TranslationAdvice} which conservatively answers
+ * {@code false} to all methods.
+ */
+public final class ConservativeTranslationAdvice
+ implements TranslationAdvice {
+ /** {@code non-null;} standard instance of this class */
+ public static final ConservativeTranslationAdvice THE_ONE =
+ new ConservativeTranslationAdvice();
+
+ /**
+ * This class is not publicly instantiable. Use {@link #THE_ONE}.
+ */
+ private ConservativeTranslationAdvice() {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ public boolean hasConstantOperation(Rop opcode,
+ RegisterSpec sourceA, RegisterSpec sourceB) {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public boolean requiresSourcesInOrder(Rop opcode,
+ RegisterSpecList sources) {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public int getMaxOptimalRegisterCount() {
+ return Integer.MAX_VALUE;
+ }
+}
diff --git a/dx/src/com/android/dx/rop/code/CstInsn.java b/dx/src/com/android/dx/rop/code/CstInsn.java
new file mode 100644
index 0000000..d7de2f4
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/CstInsn.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.cst.Constant;
+
+/**
+ * Instruction which contains an explicit reference to a constant.
+ */
+public abstract class CstInsn
+ extends Insn {
+ /** {@code non-null;} the constant */
+ private final Constant cst;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param opcode {@code non-null;} the opcode
+ * @param position {@code non-null;} source position
+ * @param result {@code null-ok;} spec for the result, if any
+ * @param sources {@code non-null;} specs for all the sources
+ * @param cst {@code non-null;} constant
+ */
+ public CstInsn(Rop opcode, SourcePosition position, RegisterSpec result,
+ RegisterSpecList sources, Constant cst) {
+ super(opcode, position, result, sources);
+
+ if (cst == null) {
+ throw new NullPointerException("cst == null");
+ }
+
+ this.cst = cst;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getInlineString() {
+ return cst.toHuman();
+ }
+
+ /**
+ * Gets the constant.
+ *
+ * @return {@code non-null;} the constant
+ */
+ public Constant getConstant() {
+ return cst;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean contentEquals(Insn b) {
+ /*
+ * The cast (CstInsn)b below should always succeed since
+ * Insn.contentEquals compares classes of this and b.
+ */
+ return super.contentEquals(b)
+ && cst.equals(((CstInsn)b).getConstant());
+ }
+}
diff --git a/dx/src/com/android/dx/rop/code/DexTranslationAdvice.java b/dx/src/com/android/dx/rop/code/DexTranslationAdvice.java
new file mode 100644
index 0000000..8dbc00b
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/DexTranslationAdvice.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.type.Type;
+
+/**
+ * Implementation of {@link TranslationAdvice} which represents what
+ * the dex format will be able to represent.
+ */
+public final class DexTranslationAdvice
+ implements TranslationAdvice {
+ /** {@code non-null;} standard instance of this class */
+ public static final DexTranslationAdvice THE_ONE =
+ new DexTranslationAdvice();
+
+ /** debug advice for disabling invoke-range optimization */
+ public static final DexTranslationAdvice NO_SOURCES_IN_ORDER =
+ new DexTranslationAdvice(true);
+
+ /**
+ * The minimum source width, in register units, for an invoke
+ * instruction that requires its sources to be in order and contiguous.
+ */
+ private static final int MIN_INVOKE_IN_ORDER = 6;
+
+ /** when true: always returns false for requiresSourcesInOrder */
+ private final boolean disableSourcesInOrder;
+
+ /**
+ * This class is not publicly instantiable. Use {@link #THE_ONE}.
+ */
+ private DexTranslationAdvice() {
+ disableSourcesInOrder = false;
+ }
+
+ private DexTranslationAdvice(boolean disableInvokeRange) {
+ this.disableSourcesInOrder = disableInvokeRange;
+ }
+
+ /** {@inheritDoc} */
+ public boolean hasConstantOperation(Rop opcode,
+ RegisterSpec sourceA, RegisterSpec sourceB) {
+ if (sourceA.getType() != Type.INT) {
+ return false;
+ }
+
+ if (! (sourceB.getTypeBearer() instanceof CstInteger)) {
+ return false;
+ }
+
+ CstInteger cst = (CstInteger) sourceB.getTypeBearer();
+
+ // TODO handle rsub
+ switch (opcode.getOpcode()) {
+ // These have 8 and 16 bit cst representations
+ case RegOps.REM:
+ case RegOps.ADD:
+ case RegOps.MUL:
+ case RegOps.DIV:
+ case RegOps.AND:
+ case RegOps.OR:
+ case RegOps.XOR:
+ return cst.fitsIn16Bits();
+ // These only have 8 bit cst reps
+ case RegOps.SHL:
+ case RegOps.SHR:
+ case RegOps.USHR:
+ return cst.fitsIn8Bits();
+ default:
+ return false;
+ }
+ }
+
+ /** {@inheritDoc} */
+ public boolean requiresSourcesInOrder(Rop opcode,
+ RegisterSpecList sources) {
+
+ return !disableSourcesInOrder && opcode.isCallLike()
+ && totalRopWidth(sources) >= MIN_INVOKE_IN_ORDER;
+ }
+
+ /**
+ * Calculates the total rop width of the list of SSA registers
+ *
+ * @param sources {@code non-null;} list of SSA registers
+ * @return {@code >= 0;} rop-form width in register units
+ */
+ private int totalRopWidth(RegisterSpecList sources) {
+ int sz = sources.size();
+ int total = 0;
+
+ for (int i = 0; i < sz; i++) {
+ total += sources.get(i).getCategory();
+ }
+
+ return total;
+ }
+
+ /** {@inheritDoc} */
+ public int getMaxOptimalRegisterCount() {
+ return 16;
+ }
+}
diff --git a/dx/src/com/android/dx/rop/code/Exceptions.java b/dx/src/com/android/dx/rop/code/Exceptions.java
new file mode 100644
index 0000000..1e27a8c
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/Exceptions.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+
+/**
+ * Common exception types.
+ */
+public final class Exceptions {
+ /** {@code non-null;} the type {@code java.lang.ArithmeticException} */
+ public static final Type TYPE_ArithmeticException =
+ Type.intern("Ljava/lang/ArithmeticException;");
+
+ /**
+ * {@code non-null;} the type
+ * {@code java.lang.ArrayIndexOutOfBoundsException}
+ */
+ public static final Type TYPE_ArrayIndexOutOfBoundsException =
+ Type.intern("Ljava/lang/ArrayIndexOutOfBoundsException;");
+
+ /** {@code non-null;} the type {@code java.lang.ArrayStoreException} */
+ public static final Type TYPE_ArrayStoreException =
+ Type.intern("Ljava/lang/ArrayStoreException;");
+
+ /** {@code non-null;} the type {@code java.lang.ClassCastException} */
+ public static final Type TYPE_ClassCastException =
+ Type.intern("Ljava/lang/ClassCastException;");
+
+ /** {@code non-null;} the type {@code java.lang.Error} */
+ public static final Type TYPE_Error = Type.intern("Ljava/lang/Error;");
+
+ /**
+ * {@code non-null;} the type
+ * {@code java.lang.IllegalMonitorStateException}
+ */
+ public static final Type TYPE_IllegalMonitorStateException =
+ Type.intern("Ljava/lang/IllegalMonitorStateException;");
+
+ /** {@code non-null;} the type {@code java.lang.NegativeArraySizeException} */
+ public static final Type TYPE_NegativeArraySizeException =
+ Type.intern("Ljava/lang/NegativeArraySizeException;");
+
+ /** {@code non-null;} the type {@code java.lang.NullPointerException} */
+ public static final Type TYPE_NullPointerException =
+ Type.intern("Ljava/lang/NullPointerException;");
+
+ /** {@code non-null;} the list {@code [java.lang.Error]} */
+ public static final StdTypeList LIST_Error = StdTypeList.make(TYPE_Error);
+
+ /**
+ * {@code non-null;} the list {@code[java.lang.Error,
+ * java.lang.ArithmeticException]}
+ */
+ public static final StdTypeList LIST_Error_ArithmeticException =
+ StdTypeList.make(TYPE_Error, TYPE_ArithmeticException);
+
+ /**
+ * {@code non-null;} the list {@code[java.lang.Error,
+ * java.lang.ClassCastException]}
+ */
+ public static final StdTypeList LIST_Error_ClassCastException =
+ StdTypeList.make(TYPE_Error, TYPE_ClassCastException);
+
+ /**
+ * {@code non-null;} the list {@code [java.lang.Error,
+ * java.lang.NegativeArraySizeException]}
+ */
+ public static final StdTypeList LIST_Error_NegativeArraySizeException =
+ StdTypeList.make(TYPE_Error, TYPE_NegativeArraySizeException);
+
+ /**
+ * {@code non-null;} the list {@code [java.lang.Error,
+ * java.lang.NullPointerException]}
+ */
+ public static final StdTypeList LIST_Error_NullPointerException =
+ StdTypeList.make(TYPE_Error, TYPE_NullPointerException);
+
+ /**
+ * {@code non-null;} the list {@code [java.lang.Error,
+ * java.lang.NullPointerException,
+ * java.lang.ArrayIndexOutOfBoundsException]}
+ */
+ public static final StdTypeList LIST_Error_Null_ArrayIndexOutOfBounds =
+ StdTypeList.make(TYPE_Error,
+ TYPE_NullPointerException,
+ TYPE_ArrayIndexOutOfBoundsException);
+
+ /**
+ * {@code non-null;} the list {@code [java.lang.Error,
+ * java.lang.NullPointerException,
+ * java.lang.ArrayIndexOutOfBoundsException,
+ * java.lang.ArrayStoreException]}
+ */
+ public static final StdTypeList LIST_Error_Null_ArrayIndex_ArrayStore =
+ StdTypeList.make(TYPE_Error,
+ TYPE_NullPointerException,
+ TYPE_ArrayIndexOutOfBoundsException,
+ TYPE_ArrayStoreException);
+
+ /**
+ * {@code non-null;} the list {@code [java.lang.Error,
+ * java.lang.NullPointerException,
+ * java.lang.IllegalMonitorStateException]}
+ */
+ public static final StdTypeList
+ LIST_Error_Null_IllegalMonitorStateException =
+ StdTypeList.make(TYPE_Error,
+ TYPE_NullPointerException,
+ TYPE_IllegalMonitorStateException);
+
+ /**
+ * This class is uninstantiable.
+ */
+ private Exceptions() {
+ // This space intentionally left blank.
+ }
+}
diff --git a/dx/src/com/android/dx/rop/code/FillArrayDataInsn.java b/dx/src/com/android/dx/rop/code/FillArrayDataInsn.java
new file mode 100644
index 0000000..ed9345d
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/FillArrayDataInsn.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.rop.type.StdTypeList;
+
+import java.util.ArrayList;
+
+/**
+ * Instruction which fills a newly created array with a predefined list of
+ * constant values.
+ */
+public final class FillArrayDataInsn
+ extends Insn {
+
+ /** non-null: initial values to fill the newly created array */
+ private final ArrayList<Constant> initValues;
+
+ /**
+ * non-null: type of the array. Will be used to determine the width of
+ * elements in the array-data table.
+ */
+ private final Constant arrayType;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param opcode {@code non-null;} the opcode
+ * @param position {@code non-null;} source position
+ * @param sources {@code non-null;} specs for all the sources
+ * @param initValues {@code non-null;} list of initial values to fill the array
+ * @param cst {@code non-null;} type of the new array
+ */
+ public FillArrayDataInsn(Rop opcode, SourcePosition position,
+ RegisterSpecList sources,
+ ArrayList<Constant> initValues,
+ Constant cst) {
+ super(opcode, position, null, sources);
+
+ if (opcode.getBranchingness() != Rop.BRANCH_NONE) {
+ throw new IllegalArgumentException("bogus branchingness");
+ }
+
+ this.initValues = initValues;
+ this.arrayType = cst;
+ }
+
+
+ /** {@inheritDoc} */
+ @Override
+ public TypeList getCatches() {
+ return StdTypeList.EMPTY;
+ }
+
+ /**
+ * Return the list of init values
+ * @return {@code non-null;} list of init values
+ */
+ public ArrayList<Constant> getInitValues() {
+ return initValues;
+ }
+
+ /**
+ * Return the type of the newly created array
+ * @return {@code non-null;} array type
+ */
+ public Constant getConstant() {
+ return arrayType;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void accept(Visitor visitor) {
+ visitor.visitFillArrayDataInsn(this);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Insn withAddedCatch(Type type) {
+ throw new UnsupportedOperationException("unsupported");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Insn withRegisterOffset(int delta) {
+ return new FillArrayDataInsn(getOpcode(), getPosition(),
+ getSources().withOffset(delta),
+ initValues, arrayType);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Insn withNewRegisters(RegisterSpec result,
+ RegisterSpecList sources) {
+
+ return new FillArrayDataInsn(getOpcode(), getPosition(),
+ sources, initValues, arrayType);
+ }
+}
diff --git a/dx/src/com/android/dx/rop/code/Insn.java b/dx/src/com/android/dx/rop/code/Insn.java
new file mode 100644
index 0000000..dad2852
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/Insn.java
@@ -0,0 +1,458 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.ToHuman;
+
+/**
+ * A register-based instruction. An instruction is the combination of
+ * an opcode (which specifies operation and source/result types), a
+ * list of actual sources and result registers/values, and additional
+ * information.
+ */
+public abstract class Insn implements ToHuman {
+ /** {@code non-null;} opcode */
+ private final Rop opcode;
+
+ /** {@code non-null;} source position */
+ private final SourcePosition position;
+
+ /** {@code null-ok;} spec for the result of this instruction, if any */
+ private final RegisterSpec result;
+
+ /** {@code non-null;} specs for all the sources of this instruction */
+ private final RegisterSpecList sources;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param opcode {@code non-null;} the opcode
+ * @param position {@code non-null;} source position
+ * @param result {@code null-ok;} spec for the result, if any
+ * @param sources {@code non-null;} specs for all the sources
+ */
+ public Insn(Rop opcode, SourcePosition position, RegisterSpec result,
+ RegisterSpecList sources) {
+ if (opcode == null) {
+ throw new NullPointerException("opcode == null");
+ }
+
+ if (position == null) {
+ throw new NullPointerException("position == null");
+ }
+
+ if (sources == null) {
+ throw new NullPointerException("sources == null");
+ }
+
+ this.opcode = opcode;
+ this.position = position;
+ this.result = result;
+ this.sources = sources;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Instances of this class compare by identity. That is,
+ * {@code x.equals(y)} is only true if {@code x == y}.
+ */
+ @Override
+ public final boolean equals(Object other) {
+ return (this == other);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * This implementation returns the identity hashcode of this
+ * instance. This is proper, since instances of this class compare
+ * by identity (see {@link #equals}).
+ */
+ @Override
+ public final int hashCode() {
+ return System.identityHashCode(this);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return toStringWithInline(getInlineString());
+ }
+
+ /**
+ * Gets a human-oriented (and slightly lossy) string for this instance.
+ *
+ * @return {@code non-null;} the human string form
+ */
+ public String toHuman() {
+ return toHumanWithInline(getInlineString());
+ }
+
+ /**
+ * Gets an "inline" string portion for toHuman(), if available. This
+ * is the portion that appears after the Rop opcode
+ *
+ * @return {@code null-ok;} if non-null, the inline text for toHuman()
+ */
+ public String getInlineString() {
+ return null;
+ }
+
+ /**
+ * Gets the opcode.
+ *
+ * @return {@code non-null;} the opcode
+ */
+ public final Rop getOpcode() {
+ return opcode;
+ }
+
+ /**
+ * Gets the source position.
+ *
+ * @return {@code non-null;} the source position
+ */
+ public final SourcePosition getPosition() {
+ return position;
+ }
+
+ /**
+ * Gets the result spec, if any. A return value of {@code null}
+ * means this instruction returns nothing.
+ *
+ * @return {@code null-ok;} the result spec, if any
+ */
+ public final RegisterSpec getResult() {
+ return result;
+ }
+
+ /**
+ * Gets the spec of a local variable assignment that occurs at this
+ * instruction, or null if no local variable assignment occurs. This
+ * may be the result register, or for {@code mark-local} insns
+ * it may be the source.
+ *
+ * @return {@code null-ok;} a named register spec or null
+ */
+ public final RegisterSpec getLocalAssignment() {
+ RegisterSpec assignment;
+ if (opcode.getOpcode() == RegOps.MARK_LOCAL) {
+ assignment = sources.get(0);
+ } else {
+ assignment = result;
+ }
+
+ if (assignment == null) {
+ return null;
+ }
+
+ LocalItem localItem = assignment.getLocalItem();
+
+ if (localItem == null) {
+ return null;
+ }
+
+ return assignment;
+ }
+
+ /**
+ * Gets the source specs.
+ *
+ * @return {@code non-null;} the source specs
+ */
+ public final RegisterSpecList getSources() {
+ return sources;
+ }
+
+ /**
+ * Gets whether this instruction can possibly throw an exception. This
+ * is just a convenient wrapper for {@code getOpcode().canThrow()}.
+ *
+ * @return {@code true} iff this instruction can possibly throw
+ */
+ public final boolean canThrow() {
+ return opcode.canThrow();
+ }
+
+ /**
+ * Gets the list of possibly-caught exceptions. This returns {@link
+ * StdTypeList#EMPTY} if this instruction has no handlers,
+ * which can be <i>either</i> if this instruction can't possibly
+ * throw or if it merely doesn't handle any of its possible
+ * exceptions. To determine whether this instruction can throw,
+ * use {@link #canThrow}.
+ *
+ * @return {@code non-null;} the catches list
+ */
+ public abstract TypeList getCatches();
+
+ /**
+ * Calls the appropriate method on the given visitor, depending on the
+ * class of this instance. Subclasses must override this.
+ *
+ * @param visitor {@code non-null;} the visitor to call on
+ */
+ public abstract void accept(Visitor visitor);
+
+ /**
+ * Returns an instance that is just like this one, except that it
+ * has a catch list with the given item appended to the end. This
+ * method throws an exception if this instance can't possibly
+ * throw. To determine whether this instruction can throw, use
+ * {@link #canThrow}.
+ *
+ * @param type {@code non-null;} type to append to the catch list
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public abstract Insn withAddedCatch(Type type);
+
+ /**
+ * Returns an instance that is just like this one, except that all
+ * register references have been offset by the given delta.
+ *
+ * @param delta the amount to offset register references by
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public abstract Insn withRegisterOffset(int delta);
+
+ /**
+ * Returns an instance that is just like this one, except that, if
+ * possible, the insn is converted into a version in which the last
+ * source (if it is a constant) is represented directly rather than
+ * as a register reference. {@code this} is returned in cases where
+ * the translation is not possible.
+ *
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public Insn withLastSourceLiteral() {
+ return this;
+ }
+
+ /**
+ * Returns an exact copy of this Insn
+ *
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public Insn copy() {
+ return withRegisterOffset(0);
+ }
+
+
+ /**
+ * Compares, handling nulls safely
+ *
+ * @param a first object
+ * @param b second object
+ * @return true if they're equal or both null.
+ */
+ private static boolean equalsHandleNulls (Object a, Object b) {
+ return (a == b) || ((a != null) && a.equals(b));
+ }
+
+ /**
+ * Compares Insn contents, since {@code Insn.equals()} is defined
+ * to be an identity compare. Insn's are {@code contentEquals()}
+ * if they have the same opcode, registers, source position, and other
+ * metadata.
+ *
+ * @return true in the case described above
+ */
+ public boolean contentEquals(Insn b) {
+ return opcode == b.getOpcode()
+ && position.equals(b.getPosition())
+ && (getClass() == b.getClass())
+ && equalsHandleNulls(result, b.getResult())
+ && equalsHandleNulls(sources, b.getSources())
+ && StdTypeList.equalContents(getCatches(), b.getCatches());
+ }
+
+ /**
+ * Returns an instance that is just like this one, except
+ * with new result and source registers.
+ *
+ * @param result {@code null-ok;} new result register
+ * @param sources {@code non-null;} new sources registers
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public abstract Insn withNewRegisters(RegisterSpec result,
+ RegisterSpecList sources);
+
+ /**
+ * Returns the string form of this instance, with the given bit added in
+ * the standard location for an inline argument.
+ *
+ * @param extra {@code null-ok;} the inline argument string
+ * @return {@code non-null;} the string form
+ */
+ protected final String toStringWithInline(String extra) {
+ StringBuffer sb = new StringBuffer(80);
+
+ sb.append("Insn{");
+ sb.append(position);
+ sb.append(' ');
+ sb.append(opcode);
+
+ if (extra != null) {
+ sb.append(' ');
+ sb.append(extra);
+ }
+
+ sb.append(" :: ");
+
+ if (result != null) {
+ sb.append(result);
+ sb.append(" <- ");
+ }
+
+ sb.append(sources);
+ sb.append('}');
+
+ return sb.toString();
+ }
+
+ /**
+ * Returns the human string form of this instance, with the given
+ * bit added in the standard location for an inline argument.
+ *
+ * @param extra {@code null-ok;} the inline argument string
+ * @return {@code non-null;} the human string form
+ */
+ protected final String toHumanWithInline(String extra) {
+ StringBuffer sb = new StringBuffer(80);
+
+ sb.append(position);
+ sb.append(": ");
+ sb.append(opcode.getNickname());
+
+ if (extra != null) {
+ sb.append("(");
+ sb.append(extra);
+ sb.append(")");
+ }
+
+ if (result == null) {
+ sb.append(" .");
+ } else {
+ sb.append(" ");
+ sb.append(result.toHuman());
+ }
+
+ sb.append(" <-");
+
+ int sz = sources.size();
+ if (sz == 0) {
+ sb.append(" .");
+ } else {
+ for (int i = 0; i < sz; i++) {
+ sb.append(" ");
+ sb.append(sources.get(i).toHuman());
+ }
+ }
+
+ return sb.toString();
+ }
+
+
+ /**
+ * Visitor interface for this (outer) class.
+ */
+ public static interface Visitor {
+ /**
+ * Visits a {@link PlainInsn}.
+ *
+ * @param insn {@code non-null;} the instruction to visit
+ */
+ public void visitPlainInsn(PlainInsn insn);
+
+ /**
+ * Visits a {@link PlainCstInsn}.
+ *
+ * @param insn {@code non-null;} the instruction to visit
+ */
+ public void visitPlainCstInsn(PlainCstInsn insn);
+
+ /**
+ * Visits a {@link SwitchInsn}.
+ *
+ * @param insn {@code non-null;} the instruction to visit
+ */
+ public void visitSwitchInsn(SwitchInsn insn);
+
+ /**
+ * Visits a {@link ThrowingCstInsn}.
+ *
+ * @param insn {@code non-null;} the instruction to visit
+ */
+ public void visitThrowingCstInsn(ThrowingCstInsn insn);
+
+ /**
+ * Visits a {@link ThrowingInsn}.
+ *
+ * @param insn {@code non-null;} the instruction to visit
+ */
+ public void visitThrowingInsn(ThrowingInsn insn);
+
+ /**
+ * Visits a {@link FillArrayDataInsn}.
+ *
+ * @param insn {@code non-null;} the instruction to visit
+ */
+ public void visitFillArrayDataInsn(FillArrayDataInsn insn);
+ }
+
+ /**
+ * Base implementation of {@link Visitor}, which has empty method
+ * bodies for all methods.
+ */
+ public static class BaseVisitor implements Visitor {
+ /** {@inheritDoc} */
+ public void visitPlainInsn(PlainInsn insn) {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ public void visitPlainCstInsn(PlainCstInsn insn) {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ public void visitSwitchInsn(SwitchInsn insn) {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ public void visitThrowingCstInsn(ThrowingCstInsn insn) {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ public void visitThrowingInsn(ThrowingInsn insn) {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ public void visitFillArrayDataInsn(FillArrayDataInsn insn) {
+ // This space intentionally left blank.
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/rop/code/InsnList.java b/dx/src/com/android/dx/rop/code/InsnList.java
new file mode 100644
index 0000000..88abd72
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/InsnList.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * List of {@link Insn} instances.
+ */
+public final class InsnList
+ extends FixedSizeList {
+ /**
+ * Constructs an instance. All indices initially contain {@code null}.
+ *
+ * @param size the size of the list
+ */
+ public InsnList(int size) {
+ super(size);
+ }
+
+ /**
+ * Gets the element at the given index. It is an error to call
+ * this with the index for an element which was never set; if you
+ * do that, this will throw {@code NullPointerException}.
+ *
+ * @param n {@code >= 0, < size();} which index
+ * @return {@code non-null;} element at that index
+ */
+ public Insn get(int n) {
+ return (Insn) get0(n);
+ }
+
+ /**
+ * Sets the instruction at the given index.
+ *
+ * @param n {@code >= 0, < size();} which index
+ * @param insn {@code non-null;} the instruction to set at {@code n}
+ */
+ public void set(int n, Insn insn) {
+ set0(n, insn);
+ }
+
+ /**
+ * Gets the last instruction. This is just a convenient shorthand for
+ * {@code get(size() - 1)}.
+ *
+ * @return {@code non-null;} the last instruction
+ */
+ public Insn getLast() {
+ return get(size() - 1);
+ }
+
+ /**
+ * Visits each instruction in the list, in order.
+ *
+ * @param visitor {@code non-null;} visitor to use
+ */
+ public void forEach(Insn.Visitor visitor) {
+ int sz = size();
+
+ for (int i = 0; i < sz; i++) {
+ get(i).accept(visitor);
+ }
+ }
+
+ /**
+ * Compares the contents of this {@code InsnList} with another.
+ * The blocks must have the same number of insns, and each Insn must
+ * also return true to {@code Insn.contentEquals()}.
+ *
+ * @param b to compare
+ * @return true in the case described above.
+ */
+ public boolean contentEquals(InsnList b) {
+ if (b == null) return false;
+
+ int sz = size();
+
+ if (sz != b.size()) return false;
+
+ for (int i = 0; i < sz; i++) {
+ if (!get(i).contentEquals(b.get(i))) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns an instance that is identical to this one, except that
+ * the registers in each instruction are offset by the given
+ * amount. Mutability of the result is inherited from the
+ * original.
+ *
+ * @param delta the amount to offset register numbers by
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public InsnList withRegisterOffset(int delta) {
+ int sz = size();
+ InsnList result = new InsnList(sz);
+
+ for (int i = 0; i < sz; i++) {
+ Insn one = (Insn) get0(i);
+ if (one != null) {
+ result.set0(i, one.withRegisterOffset(delta));
+ }
+ }
+
+ if (isImmutable()) {
+ result.setImmutable();
+ }
+
+ return result;
+ }
+}
diff --git a/dx/src/com/android/dx/rop/code/LocalItem.java b/dx/src/com/android/dx/rop/code/LocalItem.java
new file mode 100644
index 0000000..82b227c
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/LocalItem.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.cst.CstUtf8;
+
+/**
+ * A local variable item: either a name or a signature or both.
+ */
+public class LocalItem implements Comparable<LocalItem> {
+ /** {@code null-ok;} local variable name */
+ private final CstUtf8 name;
+
+ /** {@code null-ok;} local variable signature */
+ private final CstUtf8 signature;
+
+ /**
+ * Make a new item. If both name and signature are null, null is returned.
+ *
+ * TODO: intern these
+ *
+ * @param name {@code null-ok;} local variable name
+ * @param signature {@code null-ok;} local variable signature
+ * @return {@code non-null;} appropriate instance.
+ */
+ public static LocalItem make(CstUtf8 name, CstUtf8 signature) {
+ if (name == null && signature == null) {
+ return null;
+ }
+
+ return new LocalItem (name, signature);
+ }
+
+ /**
+ * Constructs instance.
+ *
+ * @param name {@code null-ok;} local variable name
+ * @param signature {@code null-ok;} local variable signature
+ */
+ private LocalItem(CstUtf8 name, CstUtf8 signature) {
+ this.name = name;
+ this.signature = signature;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof LocalItem)) {
+ return false;
+ }
+
+ LocalItem local = (LocalItem) other;
+
+ return 0 == compareTo(local);
+ }
+
+ /**
+ * Compares two strings like String.compareTo(), excepts treats a null
+ * as the least-possible string value.
+ *
+ * @return negative integer, zero, or positive integer in accordance
+ * with Comparable.compareTo()
+ */
+ private static int compareHandlesNulls(CstUtf8 a, CstUtf8 b) {
+ if (a == b) {
+ return 0;
+ } else if (a == null) {
+ return -1;
+ } else if (b == null) {
+ return 1;
+ } else {
+ return a.compareTo(b);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(LocalItem local) {
+ int ret;
+
+ ret = compareHandlesNulls(name, local.name);
+
+ if (ret != 0) {
+ return ret;
+ }
+
+ ret = compareHandlesNulls(signature, local.signature);
+
+ return ret;
+ }
+
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return (name == null ? 0 : name.hashCode()) * 31
+ + (signature == null ? 0 : signature.hashCode());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ if (name != null && signature == null) {
+ return name.toQuoted();
+ } else if (name == null && signature == null) {
+ return "";
+ }
+
+ return "[" + (name == null ? "" : name.toQuoted())
+ + "|" + (signature == null ? "" : signature.toQuoted());
+ }
+
+ /**
+ * Gets name.
+ *
+ * @return {@code null-ok;} name
+ */
+ public CstUtf8 getName() {
+ return name;
+ }
+
+ /**
+ * Gets signature.
+ *
+ * @return {@code null-ok;} signature
+ */
+ public CstUtf8 getSignature() {
+ return signature;
+ }
+}
diff --git a/dx/src/com/android/dx/rop/code/LocalVariableExtractor.java b/dx/src/com/android/dx/rop/code/LocalVariableExtractor.java
new file mode 100644
index 0000000..c2c4021
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/LocalVariableExtractor.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.util.Bits;
+import com.android.dx.util.IntList;
+
+/**
+ * Code to figure out which local variables are active at which points in
+ * a method.
+ */
+public final class LocalVariableExtractor {
+ /** {@code non-null;} method being extracted from */
+ private final RopMethod method;
+
+ /** {@code non-null;} block list for the method */
+ private final BasicBlockList blocks;
+
+ /** {@code non-null;} result in-progress */
+ private final LocalVariableInfo resultInfo;
+
+ /** {@code non-null;} work set indicating blocks needing to be processed */
+ private final int[] workSet;
+
+ /**
+ * Extracts out all the local variable information from the given method.
+ *
+ * @param method {@code non-null;} the method to extract from
+ * @return {@code non-null;} the extracted information
+ */
+ public static LocalVariableInfo extract(RopMethod method) {
+ LocalVariableExtractor lve = new LocalVariableExtractor(method);
+ return lve.doit();
+ }
+
+ /**
+ * Constructs an instance. This method is private. Use {@link #extract}.
+ *
+ * @param method {@code non-null;} the method to extract from
+ */
+ private LocalVariableExtractor(RopMethod method) {
+ if (method == null) {
+ throw new NullPointerException("method == null");
+ }
+
+ BasicBlockList blocks = method.getBlocks();
+ int maxLabel = blocks.getMaxLabel();
+
+ this.method = method;
+ this.blocks = blocks;
+ this.resultInfo = new LocalVariableInfo(method);
+ this.workSet = Bits.makeBitSet(maxLabel);
+ }
+
+ /**
+ * Does the extraction.
+ *
+ * @return {@code non-null;} the extracted information
+ */
+ private LocalVariableInfo doit() {
+ for (int label = method.getFirstLabel();
+ label >= 0;
+ label = Bits.findFirst(workSet, 0)) {
+ Bits.clear(workSet, label);
+ processBlock(label);
+ }
+
+ resultInfo.setImmutable();
+ return resultInfo;
+ }
+
+ /**
+ * Processes a single block.
+ *
+ * @param label {@code >= 0;} label of the block to process
+ */
+ private void processBlock(int label) {
+ RegisterSpecSet primaryState = resultInfo.mutableCopyOfStarts(label);
+ BasicBlock block = blocks.labelToBlock(label);
+ InsnList insns = block.getInsns();
+ int insnSz = insns.size();
+
+ /*
+ * We may have to treat the last instruction specially: If it
+ * can (but doesn't always) throw, and the exception can be
+ * caught within the same method, then we need to use the
+ * state *before* executing it to be what is merged into
+ * exception targets.
+ */
+ boolean canThrowDuringLastInsn = block.hasExceptionHandlers() &&
+ (insns.getLast().getResult() != null);
+ int freezeSecondaryStateAt = insnSz - 1;
+ RegisterSpecSet secondaryState = primaryState;
+
+ /*
+ * Iterate over the instructions, adding information for each place
+ * that the active variable set changes.
+ */
+
+ for (int i = 0; i < insnSz; i++) {
+ if (canThrowDuringLastInsn && (i == freezeSecondaryStateAt)) {
+ // Until this point, primaryState == secondaryState.
+ primaryState.setImmutable();
+ primaryState = primaryState.mutableCopy();
+ }
+
+ Insn insn = insns.get(i);
+ RegisterSpec result;
+
+ result = insn.getLocalAssignment();
+
+ if (result == null) {
+ /*
+ * If an assignment assigns over an existing local, make
+ * sure to mark the local as going out of scope.
+ */
+
+ result = insn.getResult();
+
+ if (result != null
+ && primaryState.get(result.getReg()) != null) {
+ primaryState.remove(primaryState.get(result.getReg()));
+ }
+ continue;
+ }
+
+ result = result.withSimpleType();
+
+ RegisterSpec already = primaryState.get(result);
+ /*
+ * The equals() check ensures we only add new info if
+ * the instruction causes a change to the set of
+ * active variables.
+ */
+ if (!result.equals(already)) {
+ /*
+ * If this insn represents a local moving from one register
+ * to another, remove the association between the old register
+ * and the local.
+ */
+ RegisterSpec previous
+ = primaryState.localItemToSpec(result.getLocalItem());
+
+ if (previous != null
+ && (previous.getReg() != result.getReg())) {
+
+ primaryState.remove(previous);
+ }
+
+ resultInfo.addAssignment(insn, result);
+ primaryState.put(result);
+ }
+ }
+
+ primaryState.setImmutable();
+
+ /*
+ * Merge this state into the start state for each successor,
+ * and update the work set where required (that is, in cases
+ * where the start state for a block changes).
+ */
+
+ IntList successors = block.getSuccessors();
+ int succSz = successors.size();
+ int primarySuccessor = block.getPrimarySuccessor();
+
+ for (int i = 0; i < succSz; i++) {
+ int succ = successors.get(i);
+ RegisterSpecSet state = (succ == primarySuccessor) ?
+ primaryState : secondaryState;
+
+ if (resultInfo.mergeStarts(succ, state)) {
+ Bits.set(workSet, succ);
+ }
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/rop/code/LocalVariableInfo.java b/dx/src/com/android/dx/rop/code/LocalVariableInfo.java
new file mode 100644
index 0000000..99a10ee
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/LocalVariableInfo.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.util.MutabilityControl;
+
+import java.util.HashMap;
+
+/**
+ * Container for local variable information for a particular {@link
+ * RopMethod}.
+ */
+public final class LocalVariableInfo
+ extends MutabilityControl {
+ /** {@code >= 0;} the register count for the method */
+ private final int regCount;
+
+ /**
+ * {@code non-null;} {@link RegisterSpecSet} to use when indicating a block
+ * that has no locals; it is empty and immutable but has an appropriate
+ * max size for the method
+ */
+ private final RegisterSpecSet emptySet;
+
+ /**
+ * {@code non-null;} array consisting of register sets representing the
+ * sets of variables already assigned upon entry to each block,
+ * where array indices correspond to block labels
+ */
+ private final RegisterSpecSet[] blockStarts;
+
+ /** {@code non-null;} map from instructions to the variable each assigns */
+ private final HashMap<Insn, RegisterSpec> insnAssignments;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param method {@code non-null;} the method being represented by this instance
+ */
+ public LocalVariableInfo(RopMethod method) {
+ if (method == null) {
+ throw new NullPointerException("method == null");
+ }
+
+ BasicBlockList blocks = method.getBlocks();
+ int maxLabel = blocks.getMaxLabel();
+
+ this.regCount = blocks.getRegCount();
+ this.emptySet = new RegisterSpecSet(regCount);
+ this.blockStarts = new RegisterSpecSet[maxLabel];
+ this.insnAssignments =
+ new HashMap<Insn, RegisterSpec>(blocks.getInstructionCount());
+
+ emptySet.setImmutable();
+ }
+
+ /**
+ * Sets the register set associated with the start of the block with
+ * the given label.
+ *
+ * @param label {@code >= 0;} the block label
+ * @param specs {@code non-null;} the register set to associate with the block
+ */
+ public void setStarts(int label, RegisterSpecSet specs) {
+ throwIfImmutable();
+
+ if (specs == null) {
+ throw new NullPointerException("specs == null");
+ }
+
+ try {
+ blockStarts[label] = specs;
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ // Translate the exception.
+ throw new IllegalArgumentException("bogus label");
+ }
+ }
+
+ /**
+ * Merges the given register set into the set for the block with the
+ * given label. If there was not already an associated set, then this
+ * is the same as calling {@link #setStarts}. Otherwise, this will
+ * merge the two sets and call {@link #setStarts} on the result of the
+ * merge.
+ *
+ * @param label {@code >= 0;} the block label
+ * @param specs {@code non-null;} the register set to merge into the start set
+ * for the block
+ * @return {@code true} if the merge resulted in an actual change
+ * to the associated set (including storing one for the first time) or
+ * {@code false} if there was no change
+ */
+ public boolean mergeStarts(int label, RegisterSpecSet specs) {
+ RegisterSpecSet start = getStarts0(label);
+ boolean changed = false;
+
+ if (start == null) {
+ setStarts(label, specs);
+ return true;
+ }
+
+ RegisterSpecSet newStart = start.mutableCopy();
+ newStart.intersect(specs, true);
+
+ if (start.equals(newStart)) {
+ return false;
+ }
+
+ newStart.setImmutable();
+ setStarts(label, newStart);
+
+ return true;
+ }
+
+ /**
+ * Gets the register set associated with the start of the block
+ * with the given label. This returns an empty set with the appropriate
+ * max size if no set was associated with the block in question.
+ *
+ * @param label {@code >= 0;} the block label
+ * @return {@code non-null;} the associated register set
+ */
+ public RegisterSpecSet getStarts(int label) {
+ RegisterSpecSet result = getStarts0(label);
+
+ return (result != null) ? result : emptySet;
+ }
+
+ /**
+ * Gets the register set associated with the start of the given
+ * block. This is just convenient shorthand for
+ * {@code getStarts(block.getLabel())}.
+ *
+ * @param block {@code non-null;} the block in question
+ * @return {@code non-null;} the associated register set
+ */
+ public RegisterSpecSet getStarts(BasicBlock block) {
+ return getStarts(block.getLabel());
+ }
+
+ /**
+ * Gets a mutable copy of the register set associated with the
+ * start of the block with the given label. This returns a
+ * newly-allocated empty {@link RegisterSpecSet} of appropriate
+ * max size if there is not yet any set associated with the block.
+ *
+ * @param label {@code >= 0;} the block label
+ * @return {@code non-null;} the associated register set
+ */
+ public RegisterSpecSet mutableCopyOfStarts(int label) {
+ RegisterSpecSet result = getStarts0(label);
+
+ return (result != null) ?
+ result.mutableCopy() : new RegisterSpecSet(regCount);
+ }
+
+ /**
+ * Adds an assignment association for the given instruction and
+ * register spec. This throws an exception if the instruction
+ * doesn't actually perform a named variable assignment.
+ *
+ * <b>Note:</b> Although the instruction contains its own spec for
+ * the result, it still needs to be passed in explicitly to this
+ * method, since the spec that is stored here should always have a
+ * simple type and the one in the instruction can be an arbitrary
+ * {@link TypeBearer} (such as a constant value).
+ *
+ * @param insn {@code non-null;} the instruction in question
+ * @param spec {@code non-null;} the associated register spec
+ */
+ public void addAssignment(Insn insn, RegisterSpec spec) {
+ throwIfImmutable();
+
+ if (insn == null) {
+ throw new NullPointerException("insn == null");
+ }
+
+ if (spec == null) {
+ throw new NullPointerException("spec == null");
+ }
+
+ insnAssignments.put(insn, spec);
+ }
+
+ /**
+ * Gets the named register being assigned by the given instruction, if
+ * previously stored in this instance.
+ *
+ * @param insn {@code non-null;} instruction in question
+ * @return {@code null-ok;} the named register being assigned, if any
+ */
+ public RegisterSpec getAssignment(Insn insn) {
+ return insnAssignments.get(insn);
+ }
+
+ /**
+ * Gets the number of assignments recorded by this instance.
+ *
+ * @return {@code >= 0;} the number of assignments
+ */
+ public int getAssignmentCount() {
+ return insnAssignments.size();
+ }
+
+ public void debugDump() {
+ for (int label = 0 ; label < blockStarts.length; label++) {
+ if (blockStarts[label] == null) {
+ continue;
+ }
+
+ if (blockStarts[label] == emptySet) {
+ System.out.printf("%04x: empty set\n", label);
+ } else {
+ System.out.printf("%04x: %s\n", label, blockStarts[label]);
+ }
+ }
+ }
+
+ /**
+ * Helper method, to get the starts for a label, throwing the
+ * right exception for range problems.
+ *
+ * @param label {@code >= 0;} the block label
+ * @return {@code null-ok;} associated register set or {@code null} if there
+ * is none
+ */
+ private RegisterSpecSet getStarts0(int label) {
+ try {
+ return blockStarts[label];
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ // Translate the exception.
+ throw new IllegalArgumentException("bogus label");
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/rop/code/PlainCstInsn.java b/dx/src/com/android/dx/rop/code/PlainCstInsn.java
new file mode 100644
index 0000000..fffa76b
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/PlainCstInsn.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+
+/**
+ * Instruction which contains an explicit reference to a constant
+ * but which cannot throw an exception.
+ */
+public final class PlainCstInsn
+ extends CstInsn {
+ /**
+ * Constructs an instance.
+ *
+ * @param opcode {@code non-null;} the opcode
+ * @param position {@code non-null;} source position
+ * @param result {@code null-ok;} spec for the result, if any
+ * @param sources {@code non-null;} specs for all the sources
+ * @param cst {@code non-null;} the constant
+ */
+ public PlainCstInsn(Rop opcode, SourcePosition position,
+ RegisterSpec result, RegisterSpecList sources,
+ Constant cst) {
+ super(opcode, position, result, sources, cst);
+
+ if (opcode.getBranchingness() != Rop.BRANCH_NONE) {
+ throw new IllegalArgumentException("bogus branchingness");
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public TypeList getCatches() {
+ return StdTypeList.EMPTY;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void accept(Visitor visitor) {
+ visitor.visitPlainCstInsn(this);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Insn withAddedCatch(Type type) {
+ throw new UnsupportedOperationException("unsupported");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Insn withRegisterOffset(int delta) {
+ return new PlainCstInsn(getOpcode(), getPosition(),
+ getResult().withOffset(delta),
+ getSources().withOffset(delta),
+ getConstant());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Insn withNewRegisters(RegisterSpec result,
+ RegisterSpecList sources) {
+
+ return new PlainCstInsn(getOpcode(), getPosition(),
+ result,
+ sources,
+ getConstant());
+
+ }
+}
diff --git a/dx/src/com/android/dx/rop/code/PlainInsn.java b/dx/src/com/android/dx/rop/code/PlainInsn.java
new file mode 100644
index 0000000..3fd2ba5
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/PlainInsn.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.rop.cst.Constant;
+
+/**
+ * Plain instruction, which has no embedded data and which cannot possibly
+ * throw an exception.
+ */
+public final class PlainInsn
+ extends Insn {
+ /**
+ * Constructs an instance.
+ *
+ * @param opcode {@code non-null;} the opcode
+ * @param position {@code non-null;} source position
+ * @param result {@code null-ok;} spec for the result, if any
+ * @param sources {@code non-null;} specs for all the sources
+ */
+ public PlainInsn(Rop opcode, SourcePosition position,
+ RegisterSpec result, RegisterSpecList sources) {
+ super(opcode, position, result, sources);
+
+ switch (opcode.getBranchingness()) {
+ case Rop.BRANCH_SWITCH:
+ case Rop.BRANCH_THROW: {
+ throw new IllegalArgumentException("bogus branchingness");
+ }
+ }
+
+ if (result != null && opcode.getBranchingness() != Rop.BRANCH_NONE) {
+ // move-result-pseudo is required here
+ throw new IllegalArgumentException
+ ("can't mix branchingness with result");
+ }
+ }
+
+ /**
+ * Constructs a single-source instance.
+ *
+ * @param opcode {@code non-null;} the opcode
+ * @param position {@code non-null;} source position
+ * @param result {@code null-ok;} spec for the result, if any
+ * @param source {@code non-null;} spec for the source
+ */
+ public PlainInsn(Rop opcode, SourcePosition position, RegisterSpec result,
+ RegisterSpec source) {
+ this(opcode, position, result, RegisterSpecList.make(source));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public TypeList getCatches() {
+ return StdTypeList.EMPTY;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void accept(Visitor visitor) {
+ visitor.visitPlainInsn(this);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Insn withAddedCatch(Type type) {
+ throw new UnsupportedOperationException("unsupported");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Insn withRegisterOffset(int delta) {
+ return new PlainInsn(getOpcode(), getPosition(),
+ getResult().withOffset(delta),
+ getSources().withOffset(delta));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Insn withLastSourceLiteral() {
+ RegisterSpecList sources = getSources();
+ int szSources = sources.size();
+
+ if (szSources == 0) {
+ return this;
+ }
+
+ TypeBearer lastType = sources.get(szSources - 1).getTypeBearer();
+
+ if (!lastType.isConstant()) {
+ return this;
+ }
+
+ Constant cst = (Constant) lastType;
+
+ RegisterSpecList newSources = sources.withoutLast();
+
+ Rop newRop;
+ try {
+ newRop = Rops.ropFor(getOpcode().getOpcode(),
+ getResult(), newSources, (Constant)lastType);
+ } catch (IllegalArgumentException ex) {
+ // There's no rop for this case
+ return this;
+ }
+
+ return new PlainCstInsn(newRop, getPosition(),
+ getResult(), newSources, cst);
+ }
+
+
+ /** {@inheritDoc} */
+ @Override
+ public Insn withNewRegisters(RegisterSpec result,
+ RegisterSpecList sources) {
+
+ return new PlainInsn(getOpcode(), getPosition(),
+ result,
+ sources);
+
+ }
+}
diff --git a/dx/src/com/android/dx/rop/code/RegOps.java b/dx/src/com/android/dx/rop/code/RegOps.java
new file mode 100644
index 0000000..bdf9342
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/RegOps.java
@@ -0,0 +1,399 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.util.Hex;
+
+/**
+ * All the register-based opcodes, and related utilities.
+ *
+ * <p><b>Note:</b> Opcode descriptions use a rough pseudocode. {@code r}
+ * is the result register, {@code x} is the first argument,
+ * {@code y} is the second argument, and {@code z} is the
+ * third argument. The expression which describes
+ * the operation uses Java-ish syntax but is preceded by type indicators for
+ * each of the values.
+ */
+public final class RegOps {
+ /** {@code nop()} */
+ public static final int NOP = 1;
+
+ /** {@code T: any type; r,x: T :: r = x;} */
+ public static final int MOVE = 2;
+
+ /** {@code T: any type; r,param(x): T :: r = param(x)} */
+ public static final int MOVE_PARAM = 3;
+
+ /**
+ * {@code T: Throwable; r: T :: r = caught_exception}.
+ * <b>Note:</b> This opcode should only ever be used in the
+ * first instruction of a block, and such blocks must be
+ * the start of an exception handler.
+ */
+ public static final int MOVE_EXCEPTION = 4;
+
+ /** {@code T: any type; r, literal: T :: r = literal;} */
+ public static final int CONST = 5;
+
+ /** {@code goto label} */
+ public static final int GOTO = 6;
+
+ /**
+ * {@code T: int or Object; x,y: T :: if (x == y) goto
+ * label}
+ */
+ public static final int IF_EQ = 7;
+
+ /**
+ * {@code T: int or Object; x,y: T :: if (x != y) goto
+ * label}
+ */
+ public static final int IF_NE = 8;
+
+ /** {@code x,y: int :: if (x < y) goto label} */
+ public static final int IF_LT = 9;
+
+ /** {@code x,y: int :: if (x >= y) goto label} */
+ public static final int IF_GE = 10;
+
+ /** {@code x,y: int :: if (x <= y) goto label} */
+ public static final int IF_LE = 11;
+
+ /** {@code x,y: int :: if (x > y) goto label} */
+ public static final int IF_GT = 12;
+
+ /** {@code x: int :: goto table[x]} */
+ public static final int SWITCH = 13;
+
+ /** {@code T: any numeric type; r,x,y: T :: r = x + y} */
+ public static final int ADD = 14;
+
+ /** {@code T: any numeric type; r,x,y: T :: r = x - y} */
+ public static final int SUB = 15;
+
+ /** {@code T: any numeric type; r,x,y: T :: r = x * y} */
+ public static final int MUL = 16;
+
+ /** {@code T: any numeric type; r,x,y: T :: r = x / y} */
+ public static final int DIV = 17;
+
+ /**
+ * {@code T: any numeric type; r,x,y: T :: r = x % y}
+ * (Java-style remainder)
+ */
+ public static final int REM = 18;
+
+ /** {@code T: any numeric type; r,x: T :: r = -x} */
+ public static final int NEG = 19;
+
+ /** {@code T: any integral type; r,x,y: T :: r = x & y} */
+ public static final int AND = 20;
+
+ /** {@code T: any integral type; r,x,y: T :: r = x | y} */
+ public static final int OR = 21;
+
+ /** {@code T: any integral type; r,x,y: T :: r = x ^ y} */
+ public static final int XOR = 22;
+
+ /**
+ * {@code T: any integral type; r,x: T; y: int :: r = x << y}
+ */
+ public static final int SHL = 23;
+
+ /**
+ * {@code T: any integral type; r,x: T; y: int :: r = x >> y}
+ * (signed right-shift)
+ */
+ public static final int SHR = 24;
+
+ /**
+ * {@code T: any integral type; r,x: T; y: int :: r = x >>> y}
+ * (unsigned right-shift)
+ */
+ public static final int USHR = 25;
+
+ /** {@code T: any integral type; r,x: T :: r = ~x} */
+ public static final int NOT = 26;
+
+ /**
+ * {@code T: any numeric type; r: int; x,y: T :: r = (x == y) ? 0
+ * : (x > y) ? 1 : -1} (Java-style "cmpl" where a NaN is
+ * considered "less than" all other values; also used for integral
+ * comparisons)
+ */
+ public static final int CMPL = 27;
+
+ /**
+ * {@code T: any floating point type; r: int; x,y: T :: r = (x == y) ? 0
+ * : (x < y) ? -1 : 1} (Java-style "cmpg" where a NaN is
+ * considered "greater than" all other values)
+ */
+ public static final int CMPG = 28;
+
+ /**
+ * {@code T: any numeric type; U: any numeric type; r: T; x: U ::
+ * r = (T) x} (numeric type conversion between the four
+ * "real" numeric types)
+ */
+ public static final int CONV = 29;
+
+ /**
+ * {@code r,x: int :: r = (x << 24) >> 24} (Java-style
+ * convert int to byte)
+ */
+ public static final int TO_BYTE = 30;
+
+ /**
+ * {@code r,x: int :: r = x & 0xffff} (Java-style convert int to char)
+ */
+ public static final int TO_CHAR = 31;
+
+ /**
+ * {@code r,x: int :: r = (x << 16) >> 16} (Java-style
+ * convert int to short)
+ */
+ public static final int TO_SHORT = 32;
+
+ /** {@code T: return type for the method; x: T; return x} */
+ public static final int RETURN = 33;
+
+ /** {@code T: any type; r: int; x: T[]; :: r = x.length} */
+ public static final int ARRAY_LENGTH = 34;
+
+ /** {@code x: Throwable :: throw(x)} */
+ public static final int THROW = 35;
+
+ /** {@code x: Object :: monitorenter(x)} */
+ public static final int MONITOR_ENTER = 36;
+
+ /** {@code x: Object :: monitorexit(x)} */
+ public static final int MONITOR_EXIT = 37;
+
+ /** {@code T: any type; r: T; x: T[]; y: int :: r = x[y]} */
+ public static final int AGET = 38;
+
+ /** {@code T: any type; x: T; y: T[]; z: int :: x[y] = z} */
+ public static final int APUT = 39;
+
+ /**
+ * {@code T: any non-array object type :: r =
+ * alloc(T)} (allocate heap space for an object)
+ */
+ public static final int NEW_INSTANCE = 40;
+
+ /** {@code T: any array type; r: T; x: int :: r = new T[x]} */
+ public static final int NEW_ARRAY = 41;
+
+ /**
+ * {@code T: any array type; r: T; x: int; v0..vx: T :: r = new T[x]
+ * {v0, ..., vx}}
+ */
+ public static final int FILLED_NEW_ARRAY = 42;
+
+ /**
+ * {@code T: any object type; x: Object :: (T) x} (can
+ * throw {@code ClassCastException})
+ */
+ public static final int CHECK_CAST = 43;
+
+ /**
+ * {@code T: any object type; x: Object :: x instanceof T}
+ */
+ public static final int INSTANCE_OF = 44;
+
+ /**
+ * {@code T: any type; r: T; x: Object; f: instance field spec of
+ * type T :: r = x.f}
+ */
+ public static final int GET_FIELD = 45;
+
+ /**
+ * {@code T: any type; r: T; f: static field spec of type T :: r =
+ * f}
+ */
+ public static final int GET_STATIC = 46;
+
+ /**
+ * {@code T: any type; x: T; y: Object; f: instance field spec of type
+ * T :: y.f = x}
+ */
+ public static final int PUT_FIELD = 47;
+
+ /**
+ * {@code T: any type; f: static field spec of type T; x: T :: f = x}
+ */
+ public static final int PUT_STATIC = 48;
+
+ /**
+ * {@code Tr, T0, T1...: any types; r: Tr; m: static method spec;
+ * y0: T0; y1: T1 ... :: r = m(y0, y1, ...)} (call static
+ * method)
+ */
+ public static final int INVOKE_STATIC = 49;
+
+ /**
+ * {@code Tr, T0, T1...: any types; r: Tr; x: Object; m: instance method
+ * spec; y0: T0; y1: T1 ... :: r = x.m(y0, y1, ...)} (call normal
+ * virtual method)
+ */
+ public static final int INVOKE_VIRTUAL = 50;
+
+ /**
+ * {@code Tr, T0, T1...: any types; r: Tr; x: Object; m: instance method
+ * spec; y0: T0; y1: T1 ... :: r = x.m(y0, y1, ...)} (call
+ * superclass virtual method)
+ */
+ public static final int INVOKE_SUPER = 51;
+
+ /**
+ * {@code Tr, T0, T1...: any types; r: Tr; x: Object; m: instance method
+ * spec; y0: T0; y1: T1 ... :: r = x.m(y0, y1, ...)} (call
+ * direct/special method)
+ */
+ public static final int INVOKE_DIRECT = 52;
+
+ /**
+ * {@code Tr, T0, T1...: any types; r: Tr; x: Object; m: interface
+ * (instance) method spec; y0: T0; y1: T1 ... :: r = x.m(y0, y1,
+ * ...)} (call interface method)
+ */
+ public static final int INVOKE_INTERFACE = 53;
+
+ /**
+ * {@code T0: any type; name: local variable name :: mark(name,T0)}
+ * (mark beginning or end of local variable name)
+ */
+ public static final int MARK_LOCAL = 54;
+
+ /**
+ * {@code T: Any type; r: T :: r = return_type}.
+ * <b>Note:</b> This opcode should only ever be used in the
+ * first instruction of a block following an invoke-*.
+ */
+ public static final int MOVE_RESULT = 55;
+
+ /**
+ * {@code T: Any type; r: T :: r = return_type}.
+ * <b>Note:</b> This opcode should only ever be used in the
+ * first instruction of a block following a non-invoke throwing insn
+ */
+ public static final int MOVE_RESULT_PSEUDO = 56;
+
+ /** {@code T: Any primitive type; v0..vx: T :: {v0, ..., vx}} */
+ public static final int FILL_ARRAY_DATA = 57;
+
+ /**
+ * This class is uninstantiable.
+ */
+ private RegOps() {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Gets the name of the given opcode.
+ *
+ * @param opcode {@code >= 0, <= 255;} the opcode
+ * @return {@code non-null;} its name
+ */
+ public static String opName(int opcode) {
+ switch (opcode) {
+ case NOP: return "nop";
+ case MOVE: return "move";
+ case MOVE_PARAM: return "move-param";
+ case MOVE_EXCEPTION: return "move-exception";
+ case CONST: return "const";
+ case GOTO: return "goto";
+ case IF_EQ: return "if-eq";
+ case IF_NE: return "if-ne";
+ case IF_LT: return "if-lt";
+ case IF_GE: return "if-ge";
+ case IF_LE: return "if-le";
+ case IF_GT: return "if-gt";
+ case SWITCH: return "switch";
+ case ADD: return "add";
+ case SUB: return "sub";
+ case MUL: return "mul";
+ case DIV: return "div";
+ case REM: return "rem";
+ case NEG: return "neg";
+ case AND: return "and";
+ case OR: return "or";
+ case XOR: return "xor";
+ case SHL: return "shl";
+ case SHR: return "shr";
+ case USHR: return "ushr";
+ case NOT: return "not";
+ case CMPL: return "cmpl";
+ case CMPG: return "cmpg";
+ case CONV: return "conv";
+ case TO_BYTE: return "to-byte";
+ case TO_CHAR: return "to-char";
+ case TO_SHORT: return "to-short";
+ case RETURN: return "return";
+ case ARRAY_LENGTH: return "array-length";
+ case THROW: return "throw";
+ case MONITOR_ENTER: return "monitor-enter";
+ case MONITOR_EXIT: return "monitor-exit";
+ case AGET: return "aget";
+ case APUT: return "aput";
+ case NEW_INSTANCE: return "new-instance";
+ case NEW_ARRAY: return "new-array";
+ case FILLED_NEW_ARRAY: return "filled-new-array";
+ case CHECK_CAST: return "check-cast";
+ case INSTANCE_OF: return "instance-of";
+ case GET_FIELD: return "get-field";
+ case GET_STATIC: return "get-static";
+ case PUT_FIELD: return "put-field";
+ case PUT_STATIC: return "put-static";
+ case INVOKE_STATIC: return "invoke-static";
+ case INVOKE_VIRTUAL: return "invoke-virtual";
+ case INVOKE_SUPER: return "invoke-super";
+ case INVOKE_DIRECT: return "invoke-direct";
+ case INVOKE_INTERFACE: return "invoke-interface";
+ case MOVE_RESULT: return "move-result";
+ case MOVE_RESULT_PSEUDO: return "move-result-pseudo";
+ case FILL_ARRAY_DATA: return "fill-array-data";
+ }
+
+ return "unknown-" + Hex.u1(opcode);
+ }
+
+ /**
+ * Given an IF_* RegOp, returns the right-to-left flipped version. For
+ * example, IF_GT becomes IF_LT.
+ *
+ * @param opcode An IF_* RegOp
+ * @return flipped IF Regop
+ */
+ public static int flippedIfOpcode(final int opcode) {
+ switch (opcode) {
+ case RegOps.IF_EQ:
+ case RegOps.IF_NE:
+ return opcode;
+ case RegOps.IF_LT:
+ return RegOps.IF_GT;
+ case RegOps.IF_GE:
+ return RegOps.IF_LE;
+ case RegOps.IF_LE:
+ return RegOps.IF_GE;
+ case RegOps.IF_GT:
+ return RegOps.IF_LT;
+ default:
+ throw new RuntimeException("Unrecognized IF regop: " + opcode);
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/rop/code/RegisterSpec.java b/dx/src/com/android/dx/rop/code/RegisterSpec.java
new file mode 100644
index 0000000..f1ac563
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/RegisterSpec.java
@@ -0,0 +1,650 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.util.ToHuman;
+
+import java.util.HashMap;
+
+/**
+ * Combination of a register number and a type, used as the sources and
+ * destinations of register-based operations.
+ */
+public final class RegisterSpec
+ implements TypeBearer, ToHuman, Comparable<RegisterSpec> {
+ /** {@code non-null;} string to prefix register numbers with */
+ public static final String PREFIX = "v";
+
+ /** {@code non-null;} intern table for instances */
+ private static final HashMap<Object, RegisterSpec> theInterns =
+ new HashMap<Object, RegisterSpec>(1000);
+
+ /** {@code non-null;} common comparison instance used while interning */
+ private static final ForComparison theInterningItem = new ForComparison();
+
+ /** {@code >= 0;} register number */
+ private final int reg;
+
+ /** {@code non-null;} type loaded or stored */
+ private final TypeBearer type;
+
+ /** {@code null-ok;} local variable info associated with this register, if any */
+ private final LocalItem local;
+
+ /**
+ * Intern the given triple as an instance of this class.
+ *
+ * @param reg {@code >= 0;} the register number
+ * @param type {@code non-null;} the type (or possibly actual value) which
+ * is loaded from or stored to the indicated register
+ * @param local {@code null-ok;} the associated local variable, if any
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ private static RegisterSpec intern(int reg, TypeBearer type,
+ LocalItem local) {
+ theInterningItem.set(reg, type, local);
+ RegisterSpec found = theInterns.get(theInterningItem);
+
+ if (found != null) {
+ return found;
+ }
+
+ found = theInterningItem.toRegisterSpec();
+ theInterns.put(found, found);
+ return found;
+ }
+
+ /**
+ * Returns an instance for the given register number and type, with
+ * no variable info. This method is allowed to return shared
+ * instances (but doesn't necessarily do so).
+ *
+ * @param reg {@code >= 0;} the register number
+ * @param type {@code non-null;} the type (or possibly actual value) which
+ * is loaded from or stored to the indicated register
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public static RegisterSpec make(int reg, TypeBearer type) {
+ return intern(reg, type, null);
+ }
+
+ /**
+ * Returns an instance for the given register number, type, and
+ * variable info. This method is allowed to return shared
+ * instances (but doesn't necessarily do so).
+ *
+ * @param reg {@code >= 0;} the register number
+ * @param type {@code non-null;} the type (or possibly actual value) which
+ * is loaded from or stored to the indicated register
+ * @param local {@code non-null;} the associated local variable
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public static RegisterSpec make(int reg, TypeBearer type,
+ LocalItem local) {
+ if (local == null) {
+ throw new NullPointerException("local == null");
+ }
+
+ return intern(reg, type, local);
+ }
+
+ /**
+ * Returns an instance for the given register number, type, and
+ * variable info. This method is allowed to return shared
+ * instances (but doesn't necessarily do so).
+ *
+ * @param reg {@code >= 0;} the register number
+ * @param type {@code non-null;} the type (or possibly actual value) which
+ * is loaded from or stored to the indicated register
+ * @param local {@code null-ok;} the associated variable info or null for
+ * none
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public static RegisterSpec makeLocalOptional(
+ int reg, TypeBearer type, LocalItem local) {
+
+ return intern(reg, type, local);
+ }
+
+ /**
+ * Gets the string form for the given register number.
+ *
+ * @param reg {@code >= 0;} the register number
+ * @return {@code non-null;} the string form
+ */
+ public static String regString(int reg) {
+ return PREFIX + reg;
+ }
+
+ /**
+ * Constructs an instance. This constructor is private. Use
+ * {@link #make}.
+ *
+ * @param reg {@code >= 0;} the register number
+ * @param type {@code non-null;} the type (or possibly actual value) which
+ * is loaded from or stored to the indicated register
+ * @param local {@code null-ok;} the associated local variable, if any
+ */
+ private RegisterSpec(int reg, TypeBearer type, LocalItem local) {
+ if (reg < 0) {
+ throw new IllegalArgumentException("reg < 0");
+ }
+
+ if (type == null) {
+ throw new NullPointerException("type == null");
+ }
+
+ this.reg = reg;
+ this.type = type;
+ this.local = local;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof RegisterSpec)) {
+ if (other instanceof ForComparison) {
+ ForComparison fc = (ForComparison) other;
+ return equals(fc.reg, fc.type, fc.local);
+ }
+ return false;
+ }
+
+ RegisterSpec spec = (RegisterSpec) other;
+ return equals(spec.reg, spec.type, spec.local);
+ }
+
+ /**
+ * Like {@code equals}, but only consider the simple types of the
+ * registers. That is, this compares {@code getType()} on the types
+ * to ignore whatever arbitrary extra stuff might be carried around
+ * by an outer {@link TypeBearer}.
+ *
+ * @param other {@code null-ok;} spec to compare to
+ * @return {@code true} iff {@code this} and {@code other} are equal
+ * in the stated way
+ */
+ public boolean equalsUsingSimpleType(RegisterSpec other) {
+ if (!matchesVariable(other)) {
+ return false;
+ }
+
+ return (reg == other.reg);
+ }
+
+ /**
+ * Like {@link #equalsUsingSimpleType} but ignoring the register number.
+ * This is useful to determine if two instances refer to the "same"
+ * local variable.
+ *
+ * @param other {@code null-ok;} spec to compare to
+ * @return {@code true} iff {@code this} and {@code other} are equal
+ * in the stated way
+ */
+ public boolean matchesVariable(RegisterSpec other) {
+ if (other == null) {
+ return false;
+ }
+
+ return type.getType().equals(other.type.getType())
+ && ((local == other.local)
+ || ((local != null) && local.equals(other.local)));
+ }
+
+ /**
+ * Helper for {@link #equals} and {@link #ForComparison.equals},
+ * which actually does the test.
+ *
+ * @param reg value of the instance variable, for another instance
+ * @param type value of the instance variable, for another instance
+ * @param local value of the instance variable, for another instance
+ * @return whether this instance is equal to one with the given
+ * values
+ */
+ private boolean equals(int reg, TypeBearer type, LocalItem local) {
+ return (this.reg == reg)
+ && this.type.equals(type)
+ && ((this.local == local)
+ || ((this.local != null) && this.local.equals(local)));
+ }
+
+ /**
+ * Compares by (in priority order) register number, unwrapped type
+ * (that is types not {@link TypeBearer}s, and local info.
+ *
+ * @param other {@code non-null;} spec to compare to
+ * @return {@code -1..1;} standard result of comparison
+ */
+ public int compareTo(RegisterSpec other) {
+ if (this.reg < other.reg) {
+ return -1;
+ } else if (this.reg > other.reg) {
+ return 1;
+ }
+
+ int compare = type.getType().compareTo(other.type.getType());
+
+ if (compare != 0) {
+ return compare;
+ }
+
+ if (this.local == null) {
+ return (other.local == null) ? 0 : -1;
+ } else if (other.local == null) {
+ return 1;
+ }
+
+ return this.local.compareTo(other.local);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return hashCodeOf(reg, type, local);
+ }
+
+ /**
+ * Helper for {@link #hashCode} and {@link #ForComparison.hashCode},
+ * which actually does the calculation.
+ *
+ * @param reg value of the instance variable
+ * @param type value of the instance variable
+ * @param local value of the instance variable
+ * @return the hash code
+ */
+ private static int hashCodeOf(int reg, TypeBearer type, LocalItem local) {
+ int hash = (local != null) ? local.hashCode() : 0;
+
+ hash = (hash * 31 + type.hashCode()) * 31 + reg;
+ return hash;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return toString0(false);
+ }
+
+ /** {@inheritDoc} */
+ public String toHuman() {
+ return toString0(true);
+ }
+
+ /** {@inheritDoc} */
+ public Type getType() {
+ return type.getType();
+ }
+
+ /** {@inheritDoc} */
+ public TypeBearer getFrameType() {
+ return type.getFrameType();
+ }
+
+ /** {@inheritDoc} */
+ public final int getBasicType() {
+ return type.getBasicType();
+ }
+
+ /** {@inheritDoc} */
+ public final int getBasicFrameType() {
+ return type.getBasicFrameType();
+ }
+
+ /** {@inheritDoc} */
+ public final boolean isConstant() {
+ return false;
+ }
+
+ /**
+ * Gets the register number.
+ *
+ * @return {@code >= 0;} the register number
+ */
+ public int getReg() {
+ return reg;
+ }
+
+ /**
+ * Gets the type (or actual value) which is loaded from or stored
+ * to the register associated with this instance.
+ *
+ * @return {@code non-null;} the type
+ */
+ public TypeBearer getTypeBearer() {
+ return type;
+ }
+
+ /**
+ * Gets the variable info associated with this instance, if any.
+ *
+ * @return {@code null-ok;} the variable info, or {@code null} if this
+ * instance has none
+ */
+ public LocalItem getLocalItem() {
+ return local;
+ }
+
+ /**
+ * Gets the next available register number after the one in this
+ * instance. This is equal to the register number plus the width
+ * (category) of the type used. Among other things, this may also
+ * be used to determine the minimum required register count
+ * implied by this instance.
+ *
+ * @return {@code >= 0;} the required registers size
+ */
+ public int getNextReg() {
+ return reg + getCategory();
+ }
+
+ /**
+ * Gets the category of this instance's type. This is just a convenient
+ * shorthand for {@code getType().getCategory()}.
+ *
+ * @see #isCategory1
+ * @see #isCategory2
+ * @return {@code 1..2;} the category of this instance's type
+ */
+ public int getCategory() {
+ return type.getType().getCategory();
+ }
+
+ /**
+ * Gets whether this instance's type is category 1. This is just a
+ * convenient shorthand for {@code getType().isCategory1()}.
+ *
+ * @see #getCategory
+ * @see #isCategory2
+ * @return whether or not this instance's type is of category 1
+ */
+ public boolean isCategory1() {
+ return type.getType().isCategory1();
+ }
+
+ /**
+ * Gets whether this instance's type is category 2. This is just a
+ * convenient shorthand for {@code getType().isCategory2()}.
+ *
+ * @see #getCategory
+ * @see #isCategory1
+ * @return whether or not this instance's type is of category 2
+ */
+ public boolean isCategory2() {
+ return type.getType().isCategory2();
+ }
+
+ /**
+ * Gets the string form for just the register number of this instance.
+ *
+ * @return {@code non-null;} the register string form
+ */
+ public String regString() {
+ return regString(reg);
+ }
+
+ /**
+ * Returns an instance that is the intersection between this instance
+ * and the given one, if any. The intersection is defined as follows:
+ *
+ * <ul>
+ * <li>If {@code other} is {@code null}, then the result
+ * is {@code null}.
+ * <li>If the register numbers don't match, then the intersection
+ * is {@code null}. Otherwise, the register number of the
+ * intersection is the same as the one in the two instances.</li>
+ * <li>If the types returned by {@code getType()} are not
+ * {@code equals()}, then the intersection is null.</li>
+ * <li>If the type bearers returned by {@code getTypeBearer()}
+ * are {@code equals()}, then the intersection's type bearer
+ * is the one from this instance. Otherwise, the intersection's
+ * type bearer is the {@code getType()} of this instance.</li>
+ * <li>If the locals are {@code equals()}, then the local info
+ * of the intersection is the local info of this instance. Otherwise,
+ * the local info of the intersection is {@code null}.</li>
+ * </ul>
+ *
+ * @param other {@code null-ok;} instance to intersect with (or {@code null})
+ * @param localPrimary whether local variables are primary to the
+ * intersection; if {@code true}, then the only non-null
+ * results occur when registers being intersected have equal local
+ * infos (or both have {@code null} local infos)
+ * @return {@code null-ok;} the intersection
+ */
+ public RegisterSpec intersect(RegisterSpec other, boolean localPrimary) {
+ if (this == other) {
+ // Easy out.
+ return this;
+ }
+
+ if ((other == null) || (reg != other.getReg())) {
+ return null;
+ }
+
+ LocalItem resultLocal =
+ ((local == null) || !local.equals(other.getLocalItem()))
+ ? null : local;
+ boolean sameName = (resultLocal == local);
+
+ if (localPrimary && !sameName) {
+ return null;
+ }
+
+ Type thisType = getType();
+ Type otherType = other.getType();
+
+ // Note: Types are always interned.
+ if (thisType != otherType) {
+ return null;
+ }
+
+ TypeBearer resultTypeBearer =
+ type.equals(other.getTypeBearer()) ? type : thisType;
+
+ if ((resultTypeBearer == type) && sameName) {
+ // It turns out that the intersection is "this" after all.
+ return this;
+ }
+
+ return (resultLocal == null) ? make(reg, resultTypeBearer) :
+ make(reg, resultTypeBearer, resultLocal);
+ }
+
+ /**
+ * Returns an instance that is identical to this one, except that the
+ * register number is replaced by the given one.
+ *
+ * @param newReg {@code >= 0;} the new register number
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public RegisterSpec withReg(int newReg) {
+ if (reg == newReg) {
+ return this;
+ }
+
+ return makeLocalOptional(newReg, type, local);
+ }
+
+ /**
+ * Returns an instance that is identical to this one, except that
+ * the type is replaced by the given one.
+ *
+ * @param newType {@code non-null;} the new type
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public RegisterSpec withType(TypeBearer newType) {
+ return makeLocalOptional(reg, newType, local);
+ }
+
+ /**
+ * Returns an instance that is identical to this one, except that the
+ * register number is offset by the given amount.
+ *
+ * @param delta the amount to offset the register number by
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public RegisterSpec withOffset(int delta) {
+ if (delta == 0) {
+ return this;
+ }
+
+ return withReg(reg + delta);
+ }
+
+ /**
+ * Returns an instance that is identical to this one, except that
+ * the type bearer is replaced by the actual underlying type
+ * (thereby stripping off non-type information) with any
+ * initialization information stripped away as well.
+ *
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public RegisterSpec withSimpleType() {
+ TypeBearer orig = type;
+ Type newType;
+
+ if (orig instanceof Type) {
+ newType = (Type) orig;
+ } else {
+ newType = orig.getType();
+ }
+
+ if (newType.isUninitialized()) {
+ newType = newType.getInitializedType();
+ }
+
+ if (newType == orig) {
+ return this;
+ }
+
+ return makeLocalOptional(reg, newType, local);
+ }
+
+ /**
+ * Returns an instance that is identical to this one except that the
+ * local variable is as specified in the parameter.
+ *
+ * @param local {@code null-ok;} the local item or null for none
+ * @return an appropriate instance
+ */
+ public RegisterSpec withLocalItem(LocalItem local) {
+ if ((this.local== local)
+ || ((this.local != null) && this.local.equals(local))) {
+
+ return this;
+ }
+
+ return makeLocalOptional(reg, type, local);
+ }
+
+
+ /**
+ * Helper for {@link #toString} and {@link #toHuman}.
+ *
+ * @param human whether to be human-oriented
+ * @return {@code non-null;} the string form
+ */
+ private String toString0(boolean human) {
+ StringBuffer sb = new StringBuffer(40);
+
+ sb.append(regString());
+ sb.append(":");
+
+ if (local != null) {
+ sb.append(local.toString());
+ }
+
+ Type justType = type.getType();
+ sb.append(justType);
+
+ if (justType != type) {
+ sb.append("=");
+ if (human && (type instanceof Constant)) {
+ sb.append(((Constant) type).toHuman());
+ } else {
+ sb.append(type);
+ }
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Holder of register spec data for the purposes of comparison (so that
+ * {@code RegisterSpec} itself can still keep {@code final}
+ * instance variables.
+ */
+ private static class ForComparison {
+ /** {@code >= 0;} register number */
+ private int reg;
+
+ /** {@code non-null;} type loaded or stored */
+ private TypeBearer type;
+
+ /**
+ * {@code null-ok;} local variable associated with this
+ * register, if any
+ */
+ private LocalItem local;
+
+ /**
+ * Set all the instance variables.
+ *
+ * @param reg {@code >= 0;} the register number
+ * @param type {@code non-null;} the type (or possibly actual
+ * value) which is loaded from or stored to the indicated
+ * register
+ * @param local {@code null-ok;} the associated local variable, if any
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public void set(int reg, TypeBearer type, LocalItem local) {
+ this.reg = reg;
+ this.type = type;
+ this.local = local;
+ }
+
+ /**
+ * Construct a {@code RegisterSpec} of this instance's
+ * contents.
+ *
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public RegisterSpec toRegisterSpec() {
+ return new RegisterSpec(reg, type, local);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof RegisterSpec)) {
+ return false;
+ }
+
+ RegisterSpec spec = (RegisterSpec) other;
+ return spec.equals(reg, type, local);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return hashCodeOf(reg, type, local);
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/rop/code/RegisterSpecList.java b/dx/src/com/android/dx/rop/code/RegisterSpecList.java
new file mode 100644
index 0000000..e900787
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/RegisterSpecList.java
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * List of {@link RegisterSpec} instances.
+ */
+public final class RegisterSpecList
+ extends FixedSizeList implements TypeList {
+ /** {@code non-null;} no-element instance */
+ public static final RegisterSpecList EMPTY = new RegisterSpecList(0);
+
+ /**
+ * Makes a single-element instance.
+ *
+ * @param spec {@code non-null;} the element
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public static RegisterSpecList make(RegisterSpec spec) {
+ RegisterSpecList result = new RegisterSpecList(1);
+ result.set(0, spec);
+ return result;
+ }
+
+ /**
+ * Makes a two-element instance.
+ *
+ * @param spec0 {@code non-null;} the first element
+ * @param spec1 {@code non-null;} the second element
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public static RegisterSpecList make(RegisterSpec spec0,
+ RegisterSpec spec1) {
+ RegisterSpecList result = new RegisterSpecList(2);
+ result.set(0, spec0);
+ result.set(1, spec1);
+ return result;
+ }
+
+ /**
+ * Makes a three-element instance.
+ *
+ * @param spec0 {@code non-null;} the first element
+ * @param spec1 {@code non-null;} the second element
+ * @param spec2 {@code non-null;} the third element
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public static RegisterSpecList make(RegisterSpec spec0, RegisterSpec spec1,
+ RegisterSpec spec2) {
+ RegisterSpecList result = new RegisterSpecList(3);
+ result.set(0, spec0);
+ result.set(1, spec1);
+ result.set(2, spec2);
+ return result;
+ }
+
+ /**
+ * Makes a four-element instance.
+ *
+ * @param spec0 {@code non-null;} the first element
+ * @param spec1 {@code non-null;} the second element
+ * @param spec2 {@code non-null;} the third element
+ * @param spec3 {@code non-null;} the fourth element
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public static RegisterSpecList make(RegisterSpec spec0, RegisterSpec spec1,
+ RegisterSpec spec2,
+ RegisterSpec spec3) {
+ RegisterSpecList result = new RegisterSpecList(4);
+ result.set(0, spec0);
+ result.set(1, spec1);
+ result.set(2, spec2);
+ result.set(3, spec3);
+ return result;
+ }
+
+ /**
+ * Constructs an instance. All indices initially contain {@code null}.
+ *
+ * @param size the size of the list
+ */
+ public RegisterSpecList(int size) {
+ super(size);
+ }
+
+ /** {@inheritDoc} */
+ public Type getType(int n) {
+ return get(n).getType().getType();
+ }
+
+ /** {@inheritDoc} */
+ public int getWordCount() {
+ int sz = size();
+ int result = 0;
+
+ for (int i = 0; i < sz; i++) {
+ result += getType(i).getCategory();
+ }
+
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ public TypeList withAddedType(Type type) {
+ throw new UnsupportedOperationException("unsupported");
+ }
+
+ /**
+ * Gets the indicated element. It is an error to call this with the
+ * index for an element which was never set; if you do that, this
+ * will throw {@code NullPointerException}.
+ *
+ * @param n {@code >= 0, < size();} which element
+ * @return {@code non-null;} the indicated element
+ */
+ public RegisterSpec get(int n) {
+ return (RegisterSpec) get0(n);
+ }
+
+ /**
+ * Returns a RegisterSpec in this list that uses the specified register,
+ * or null if there is none in this list.
+ * @param reg Register to find
+ * @return RegisterSpec that uses argument or null.
+ */
+ public RegisterSpec specForRegister(int reg) {
+ int sz = size();
+ for (int i = 0; i < sz; i++) {
+ RegisterSpec rs;
+
+ rs = get(i);
+
+ if (rs.getReg() == reg) {
+ return rs;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the index of a RegisterSpec in this list that uses the specified
+ * register, or -1 if none in this list uses the register.
+ * @param reg Register to find
+ * @return index of RegisterSpec or -1
+ */
+ public int indexOfRegister(int reg) {
+ int sz = size();
+ for (int i = 0; i < sz; i++) {
+ RegisterSpec rs;
+
+ rs = get(i);
+
+ if (rs.getReg() == reg) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * Sets the element at the given index.
+ *
+ * @param n {@code >= 0, < size();} which element
+ * @param spec {@code non-null;} the value to store
+ */
+ public void set(int n, RegisterSpec spec) {
+ set0(n, spec);
+ }
+
+ /**
+ * Gets the minimum required register count implied by this
+ * instance. This is equal to the highest register number referred
+ * to plus the widest width (largest category) of the type used in
+ * that register.
+ *
+ * @return {@code >= 0;} the required registers size
+ */
+ public int getRegistersSize() {
+ int sz = size();
+ int result = 0;
+
+ for (int i = 0; i < sz; i++) {
+ RegisterSpec spec = (RegisterSpec) get0(i);
+ if (spec != null) {
+ int min = spec.getNextReg();
+ if (min > result) {
+ result = min;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns a new instance, which is the same as this instance,
+ * except that it has an additional element prepended to the original.
+ * Mutability of the result is inherited from the original.
+ *
+ * @param spec {@code non-null;} the new first spec (to prepend)
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public RegisterSpecList withFirst(RegisterSpec spec) {
+ int sz = size();
+ RegisterSpecList result = new RegisterSpecList(sz + 1);
+
+ for (int i = 0; i < sz; i++) {
+ result.set0(i + 1, get0(i));
+ }
+
+ result.set0(0, spec);
+ if (isImmutable()) {
+ result.setImmutable();
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns a new instance, which is the same as this instance,
+ * except that its first element is removed. Mutability of the
+ * result is inherited from the original.
+ *
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public RegisterSpecList withoutFirst() {
+ int newSize = size() - 1;
+
+ if (newSize == 0) {
+ return EMPTY;
+ }
+
+ RegisterSpecList result = new RegisterSpecList(newSize);
+
+ for (int i = 0; i < newSize; i++) {
+ result.set0(i, get0(i + 1));
+ }
+
+ if (isImmutable()) {
+ result.setImmutable();
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns a new instance, which is the same as this instance,
+ * except that its last element is removed. Mutability of the
+ * result is inherited from the original.
+ *
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public RegisterSpecList withoutLast() {
+ int newSize = size() - 1;
+
+ if (newSize == 0) {
+ return EMPTY;
+ }
+
+ RegisterSpecList result = new RegisterSpecList(newSize);
+
+ for (int i = 0; i < newSize; i++) {
+ result.set0(i, get0(i));
+ }
+
+ if (isImmutable()) {
+ result.setImmutable();
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns an instance that is identical to this one, except that
+ * all register numbers are offset by the given amount. Mutability
+ * of the result is inherited from the original.
+ *
+ * @param delta the amount to offset the register numbers by
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public RegisterSpecList withOffset(int delta) {
+ int sz = size();
+
+ if (sz == 0) {
+ // Don't bother making a new zero-element instance.
+ return this;
+ }
+
+ RegisterSpecList result = new RegisterSpecList(sz);
+
+ for (int i = 0; i < sz; i++) {
+ RegisterSpec one = (RegisterSpec) get0(i);
+ if (one != null) {
+ result.set0(i, one.withOffset(delta));
+ }
+ }
+
+ if (isImmutable()) {
+ result.setImmutable();
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns an instance that is identical to this one, except that
+ * all register numbers are renumbered sequentially from the given
+ * base, with the first number duplicated if indicated.
+ *
+ * @param base the base register number
+ * @param duplicateFirst whether to duplicate the first number
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public RegisterSpecList withSequentialRegisters(int base,
+ boolean duplicateFirst) {
+ int sz = size();
+
+ if (sz == 0) {
+ // Don't bother making a new zero-element instance.
+ return this;
+ }
+
+ RegisterSpecList result = new RegisterSpecList(sz);
+
+ for (int i = 0; i < sz; i++) {
+ RegisterSpec one = (RegisterSpec) get0(i);
+ result.set0(i, one.withReg(base));
+ if (duplicateFirst) {
+ duplicateFirst = false;
+ } else {
+ base += one.getCategory();
+ }
+ }
+
+ if (isImmutable()) {
+ result.setImmutable();
+ }
+
+ return result;
+ }
+
+}
diff --git a/dx/src/com/android/dx/rop/code/RegisterSpecSet.java b/dx/src/com/android/dx/rop/code/RegisterSpecSet.java
new file mode 100644
index 0000000..d16a82a
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/RegisterSpecSet.java
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.util.MutabilityControl;
+import com.android.dx.rop.cst.CstUtf8;
+
+/**
+ * Set of {@link RegisterSpec} instances, where a given register number
+ * may appear only once in the set.
+ */
+public final class RegisterSpecSet
+ extends MutabilityControl {
+ /** {@code non-null;} no-element instance */
+ public static final RegisterSpecSet EMPTY = new RegisterSpecSet(0);
+
+ /**
+ * {@code non-null;} array of register specs, where each element is
+ * {@code null} or is an instance whose {@code reg}
+ * matches the array index
+ */
+ private final RegisterSpec[] specs;
+
+ /** {@code >= -1;} size of the set or {@code -1} if not yet calculated */
+ private int size;
+
+ /**
+ * Constructs an instance. The instance is initially empty.
+ *
+ * @param maxSize {@code >= 0;} the maximum register number (exclusive) that
+ * may be represented in this instance
+ */
+ public RegisterSpecSet(int maxSize) {
+ super(maxSize != 0);
+
+ this.specs = new RegisterSpec[maxSize];
+ this.size = 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof RegisterSpecSet)) {
+ return false;
+ }
+
+ RegisterSpecSet otherSet = (RegisterSpecSet) other;
+ RegisterSpec[] otherSpecs = otherSet.specs;
+ int len = specs.length;
+
+ if ((len != otherSpecs.length) || (size() != otherSet.size())) {
+ return false;
+ }
+
+ for (int i = 0; i < len; i++) {
+ RegisterSpec s1 = specs[i];
+ RegisterSpec s2 = otherSpecs[i];
+
+ if (s1 == s2) {
+ continue;
+ }
+
+ if ((s1 == null) || !s1.equals(s2)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ int len = specs.length;
+ int hash = 0;
+
+ for (int i = 0; i < len; i++) {
+ RegisterSpec spec = specs[i];
+ int oneHash = (spec == null) ? 0 : spec.hashCode();
+ hash = (hash * 31) + oneHash;
+ }
+
+ return hash;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ int len = specs.length;
+ StringBuffer sb = new StringBuffer(len * 25);
+
+ sb.append('{');
+
+ boolean any = false;
+ for (int i = 0; i < len; i++) {
+ RegisterSpec spec = specs[i];
+ if (spec != null) {
+ if (any) {
+ sb.append(", ");
+ } else {
+ any = true;
+ }
+ sb.append(spec);
+ }
+ }
+
+ sb.append('}');
+ return sb.toString();
+ }
+
+ /**
+ * Gets the maximum number of registers that may be in this instance, which
+ * is also the maximum-plus-one of register numbers that may be
+ * represented.
+ *
+ * @return {@code >= 0;} the maximum size
+ */
+ public int getMaxSize() {
+ return specs.length;
+ }
+
+ /**
+ * Gets the current size of this instance.
+ *
+ * @return {@code >= 0;} the size
+ */
+ public int size() {
+ int result = size;
+
+ if (result < 0) {
+ int len = specs.length;
+
+ result = 0;
+ for (int i = 0; i < len; i++) {
+ if (specs[i] != null) {
+ result++;
+ }
+ }
+
+ size = result;
+ }
+
+ return result;
+ }
+
+ /**
+ * Gets the element with the given register number, if any.
+ *
+ * @param reg {@code >= 0;} the desired register number
+ * @return {@code null-ok;} the element with the given register number or
+ * {@code null} if there is none
+ */
+ public RegisterSpec get(int reg) {
+ try {
+ return specs[reg];
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ // Translate the exception.
+ throw new IllegalArgumentException("bogus reg");
+ }
+ }
+
+ /**
+ * Gets the element with the same register number as the given
+ * spec, if any. This is just a convenient shorthand for
+ * {@code get(spec.getReg())}.
+ *
+ * @param spec {@code non-null;} spec with the desired register number
+ * @return {@code null-ok;} the element with the matching register number or
+ * {@code null} if there is none
+ */
+ public RegisterSpec get(RegisterSpec spec) {
+ return get(spec.getReg());
+ }
+
+ /**
+ * Returns the spec in this set that's currently associated with a
+ * given local (type, name, and signature), or {@code null} if there is
+ * none. This ignores the register number of the given spec but
+ * matches on everything else.
+ *
+ * @param spec {@code non-null;} local to look for
+ * @return {@code null-ok;} first register found that matches, if any
+ */
+ public RegisterSpec findMatchingLocal(RegisterSpec spec) {
+ int length = specs.length;
+
+ for (int reg = 0; reg < length; reg++) {
+ RegisterSpec s = specs[reg];
+
+ if (s == null) {
+ continue;
+ }
+
+ if (spec.matchesVariable(s)) {
+ return s;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the spec in this set that's currently associated with a given
+ * local (name and signature), or {@code null} if there is none.
+ *
+ * @param local {@code non-null;} local item to search for
+ * @return {@code null-ok;} first register found with matching name and signature
+ */
+ public RegisterSpec localItemToSpec(LocalItem local) {
+ int length = specs.length;
+
+ for (int reg = 0; reg < length; reg++) {
+ RegisterSpec spec = specs[reg];
+
+ if ((spec != null) && local.equals(spec.getLocalItem())) {
+ return spec;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Removes a spec from the set. Only the register number
+ * of the parameter is significant.
+ *
+ * @param toRemove {@code non-null;} register to remove.
+ */
+ public void remove(RegisterSpec toRemove) {
+ try {
+ specs[toRemove.getReg()] = null;
+ size = -1;
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ // Translate the exception.
+ throw new IllegalArgumentException("bogus reg");
+ }
+ }
+
+ /**
+ * Puts the given spec into the set. If there is already an element in
+ * the set with the same register number, it is replaced. Additionally,
+ * if the previous element is for a category-2 register, then that
+ * previous element is nullified. Finally, if the given spec is for
+ * a category-2 register, then the immediately subsequent element
+ * is nullified.
+ *
+ * @param spec {@code non-null;} the register spec to put in the instance
+ */
+ public void put(RegisterSpec spec) {
+ throwIfImmutable();
+
+ if (spec == null) {
+ throw new NullPointerException("spec == null");
+ }
+
+ size = -1;
+
+ try {
+ int reg = spec.getReg();
+ specs[reg] = spec;
+
+ if (reg > 0) {
+ int prevReg = reg - 1;
+ RegisterSpec prevSpec = specs[prevReg];
+ if ((prevSpec != null) && (prevSpec.getCategory() == 2)) {
+ specs[prevReg] = null;
+ }
+ }
+
+ if (spec.getCategory() == 2) {
+ specs[reg + 1] = null;
+ }
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ // Translate the exception.
+ throw new IllegalArgumentException("spec.getReg() out of range");
+ }
+ }
+
+ /**
+ * Put the entire contents of the given set into this one.
+ *
+ * @param set {@code non-null;} the set to put into this instance
+ */
+ public void putAll(RegisterSpecSet set) {
+ int max = set.getMaxSize();
+
+ for (int i = 0; i < max; i++) {
+ RegisterSpec spec = set.get(i);
+ if (spec != null) {
+ put(spec);
+ }
+ }
+ }
+
+ /**
+ * Intersects this instance with the given one, modifying this
+ * instance. The intersection consists of the pairwise
+ * {@link RegisterSpec#intersect} of corresponding elements from
+ * this instance and the given one where both are non-null.
+ *
+ * @param other {@code non-null;} set to intersect with
+ * @param localPrimary whether local variables are primary to
+ * the intersection; if {@code true}, then the only non-null
+ * result elements occur when registers being intersected have
+ * equal names (or both have {@code null} names)
+ */
+ public void intersect(RegisterSpecSet other, boolean localPrimary) {
+ throwIfImmutable();
+
+ RegisterSpec[] otherSpecs = other.specs;
+ int thisLen = specs.length;
+ int len = Math.min(thisLen, otherSpecs.length);
+
+ size = -1;
+
+ for (int i = 0; i < len; i++) {
+ RegisterSpec spec = specs[i];
+
+ if (spec == null) {
+ continue;
+ }
+
+ RegisterSpec intersection =
+ spec.intersect(otherSpecs[i], localPrimary);
+ if (intersection != spec) {
+ specs[i] = intersection;
+ }
+ }
+
+ for (int i = len; i < thisLen; i++) {
+ specs[i] = null;
+ }
+ }
+
+ /**
+ * Returns an instance that is identical to this one, except that
+ * all register numbers are offset by the given amount. Mutability
+ * of the result is inherited from the original.
+ *
+ * @param delta the amount to offset the register numbers by
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public RegisterSpecSet withOffset(int delta) {
+ int len = specs.length;
+ RegisterSpecSet result = new RegisterSpecSet(len + delta);
+
+ for (int i = 0; i < len; i++) {
+ RegisterSpec spec = specs[i];
+ if (spec != null) {
+ result.put(spec.withOffset(delta));
+ }
+ }
+
+ result.size = size;
+
+ if (isImmutable()) {
+ result.setImmutable();
+ }
+
+ return result;
+ }
+
+ /**
+ * Makes and return a mutable copy of this instance.
+ *
+ * @return {@code non-null;} the mutable copy
+ */
+ public RegisterSpecSet mutableCopy() {
+ int len = specs.length;
+ RegisterSpecSet copy = new RegisterSpecSet(len);
+
+ for (int i = 0; i < len; i++) {
+ RegisterSpec spec = specs[i];
+ if (spec != null) {
+ copy.put(spec);
+ }
+ }
+
+ copy.size = size;
+
+ return copy;
+ }
+}
diff --git a/dx/src/com/android/dx/rop/code/Rop.java b/dx/src/com/android/dx/rop/code/Rop.java
new file mode 100644
index 0000000..8224584
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/Rop.java
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.Hex;
+
+/**
+ * Class that describes all the immutable parts of register-based operations.
+ */
+public final class Rop {
+ /** minimum {@code BRANCH_*} value */
+ public static final int BRANCH_MIN = 1;
+
+ /** indicates a non-branching op */
+ public static final int BRANCH_NONE = 1;
+
+ /** indicates a function/method return */
+ public static final int BRANCH_RETURN = 2;
+
+ /** indicates an unconditional goto */
+ public static final int BRANCH_GOTO = 3;
+
+ /** indicates a two-way branch */
+ public static final int BRANCH_IF = 4;
+
+ /** indicates a switch-style branch */
+ public static final int BRANCH_SWITCH = 5;
+
+ /** indicates a throw-style branch (both always-throws and may-throw) */
+ public static final int BRANCH_THROW = 6;
+
+ /** maximum {@code BRANCH_*} value */
+ public static final int BRANCH_MAX = 6;
+
+ /** the opcode; one of the constants in {@link RegOps} */
+ private final int opcode;
+
+ /**
+ * {@code non-null;} result type of this operation; {@link Type#VOID} for
+ * no-result operations
+ */
+ private final Type result;
+
+ /** {@code non-null;} types of all the sources of this operation */
+ private final TypeList sources;
+
+ /** {@code non-null;} list of possible types thrown by this operation */
+ private final TypeList exceptions;
+
+ /**
+ * the branchingness of this op; one of the {@code BRANCH_*}
+ * constants in this class
+ */
+ private final int branchingness;
+
+ /** whether this is a function/method call op or similar */
+ private final boolean isCallLike;
+
+ /** {@code null-ok;} nickname, if specified (used for debugging) */
+ private final String nickname;
+
+ /**
+ * Constructs an instance. This method is private. Use one of the
+ * public constructors.
+ *
+ * @param opcode the opcode; one of the constants in {@link RegOps}
+ * @param result {@code non-null;} result type of this operation; {@link
+ * Type#VOID} for no-result operations
+ * @param sources {@code non-null;} types of all the sources of this operation
+ * @param exceptions {@code non-null;} list of possible types thrown by this
+ * operation
+ * @param branchingness the branchingness of this op; one of the
+ * {@code BRANCH_*} constants
+ * @param isCallLike whether the op is a function/method call or similar
+ * @param nickname {@code null-ok;} optional nickname (used for debugging)
+ */
+ public Rop(int opcode, Type result, TypeList sources,
+ TypeList exceptions, int branchingness, boolean isCallLike,
+ String nickname) {
+ if (result == null) {
+ throw new NullPointerException("result == null");
+ }
+
+ if (sources == null) {
+ throw new NullPointerException("sources == null");
+ }
+
+ if (exceptions == null) {
+ throw new NullPointerException("exceptions == null");
+ }
+
+ if ((branchingness < BRANCH_MIN) || (branchingness > BRANCH_MAX)) {
+ throw new IllegalArgumentException("bogus branchingness");
+ }
+
+ if ((exceptions.size() != 0) && (branchingness != BRANCH_THROW)) {
+ throw new IllegalArgumentException("exceptions / branchingness " +
+ "mismatch");
+ }
+
+ this.opcode = opcode;
+ this.result = result;
+ this.sources = sources;
+ this.exceptions = exceptions;
+ this.branchingness = branchingness;
+ this.isCallLike = isCallLike;
+ this.nickname = nickname;
+ }
+
+ /**
+ * Constructs an instance. The constructed instance is never a
+ * call-like op (see {@link #isCallLike}).
+ *
+ * @param opcode the opcode; one of the constants in {@link RegOps}
+ * @param result {@code non-null;} result type of this operation; {@link
+ * Type#VOID} for no-result operations
+ * @param sources {@code non-null;} types of all the sources of this operation
+ * @param exceptions {@code non-null;} list of possible types thrown by this
+ * operation
+ * @param branchingness the branchingness of this op; one of the
+ * {@code BRANCH_*} constants
+ * @param nickname {@code null-ok;} optional nickname (used for debugging)
+ */
+ public Rop(int opcode, Type result, TypeList sources,
+ TypeList exceptions, int branchingness, String nickname) {
+ this(opcode, result, sources, exceptions, branchingness, false,
+ nickname);
+ }
+
+ /**
+ * Constructs a no-exception instance. The constructed instance is never a
+ * call-like op (see {@link #isCallLike}).
+ *
+ * @param opcode the opcode; one of the constants in {@link RegOps}
+ * @param result {@code non-null;} result type of this operation; {@link
+ * Type#VOID} for no-result operations
+ * @param sources {@code non-null;} types of all the sources of this operation
+ * @param branchingness the branchingness of this op; one of the
+ * {@code BRANCH_*} constants
+ * @param nickname {@code null-ok;} optional nickname (used for debugging)
+ */
+ public Rop(int opcode, Type result, TypeList sources, int branchingness,
+ String nickname) {
+ this(opcode, result, sources, StdTypeList.EMPTY, branchingness, false,
+ nickname);
+ }
+
+ /**
+ * Constructs a non-branching no-exception instance. The
+ * {@code branchingness} is always {@code BRANCH_NONE},
+ * and it is never a call-like op (see {@link #isCallLike}).
+ *
+ * @param opcode the opcode; one of the constants in {@link RegOps}
+ * @param result {@code non-null;} result type of this operation; {@link
+ * Type#VOID} for no-result operations
+ * @param sources {@code non-null;} types of all the sources of this operation
+ * @param nickname {@code null-ok;} optional nickname (used for debugging)
+ */
+ public Rop(int opcode, Type result, TypeList sources, String nickname) {
+ this(opcode, result, sources, StdTypeList.EMPTY, Rop.BRANCH_NONE,
+ false, nickname);
+ }
+
+ /**
+ * Constructs a non-empty exceptions instance. Its
+ * {@code branchingness} is always {@code BRANCH_THROW},
+ * but it is never a call-like op (see {@link #isCallLike}).
+ *
+ * @param opcode the opcode; one of the constants in {@link RegOps}
+ * @param result {@code non-null;} result type of this operation; {@link
+ * Type#VOID} for no-result operations
+ * @param sources {@code non-null;} types of all the sources of this operation
+ * @param exceptions {@code non-null;} list of possible types thrown by this
+ * operation
+ * @param nickname {@code null-ok;} optional nickname (used for debugging)
+ */
+ public Rop(int opcode, Type result, TypeList sources, TypeList exceptions,
+ String nickname) {
+ this(opcode, result, sources, exceptions, Rop.BRANCH_THROW, false,
+ nickname);
+ }
+
+ /**
+ * Constructs a non-nicknamed instance with non-empty exceptions, which
+ * is always a call-like op (see {@link #isCallLike}). Its
+ * {@code branchingness} is always {@code BRANCH_THROW}.
+ *
+ * @param opcode the opcode; one of the constants in {@link RegOps}
+ * @param sources {@code non-null;} types of all the sources of this operation
+ * @param exceptions {@code non-null;} list of possible types thrown by this
+ * operation
+ */
+ public Rop(int opcode, TypeList sources, TypeList exceptions) {
+ this(opcode, Type.VOID, sources, exceptions, Rop.BRANCH_THROW, true,
+ null);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ // Easy out.
+ return true;
+ }
+
+ if (!(other instanceof Rop)) {
+ return false;
+ }
+
+ Rop rop = (Rop) other;
+
+ return (opcode == rop.opcode) &&
+ (branchingness == rop.branchingness) &&
+ (result == rop.result) &&
+ sources.equals(rop.sources) &&
+ exceptions.equals(rop.exceptions);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ int h = (opcode * 31) + branchingness;
+ h = (h * 31) + result.hashCode();
+ h = (h * 31) + sources.hashCode();
+ h = (h * 31) + exceptions.hashCode();
+
+ return h;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer(40);
+
+ sb.append("Rop{");
+
+ sb.append(RegOps.opName(opcode));
+
+ if (result != Type.VOID) {
+ sb.append(" ");
+ sb.append(result);
+ } else {
+ sb.append(" .");
+ }
+
+ sb.append(" <-");
+
+ int sz = sources.size();
+ if (sz == 0) {
+ sb.append(" .");
+ } else {
+ for (int i = 0; i < sz; i++) {
+ sb.append(' ');
+ sb.append(sources.getType(i));
+ }
+ }
+
+ if (isCallLike) {
+ sb.append(" call");
+ }
+
+ sz = exceptions.size();
+ if (sz != 0) {
+ sb.append(" throws");
+ for (int i = 0; i < sz; i++) {
+ sb.append(' ');
+ Type one = exceptions.getType(i);
+ if (one == Type.THROWABLE) {
+ sb.append("<any>");
+ } else {
+ sb.append(exceptions.getType(i));
+ }
+ }
+ } else {
+ switch (branchingness) {
+ case BRANCH_NONE: sb.append(" flows"); break;
+ case BRANCH_RETURN: sb.append(" returns"); break;
+ case BRANCH_GOTO: sb.append(" gotos"); break;
+ case BRANCH_IF: sb.append(" ifs"); break;
+ case BRANCH_SWITCH: sb.append(" switches"); break;
+ default: sb.append(" " + Hex.u1(branchingness)); break;
+ }
+ }
+
+ sb.append('}');
+
+ return sb.toString();
+ }
+
+ /**
+ * Gets the opcode.
+ *
+ * @return the opcode
+ */
+ public int getOpcode() {
+ return opcode;
+ }
+
+ /**
+ * Gets the result type. A return value of {@link Type#VOID}
+ * means this operation returns nothing.
+ *
+ * @return {@code null-ok;} the result spec
+ */
+ public Type getResult() {
+ return result;
+ }
+
+ /**
+ * Gets the source types.
+ *
+ * @return {@code non-null;} the source types
+ */
+ public TypeList getSources() {
+ return sources;
+ }
+
+ /**
+ * Gets the list of exception types that might be thrown.
+ *
+ * @return {@code non-null;} the list of exception types
+ */
+ public TypeList getExceptions() {
+ return exceptions;
+ }
+
+ /**
+ * Gets the branchingness of this instance.
+ *
+ * @return the branchingness
+ */
+ public int getBranchingness() {
+ return branchingness;
+ }
+
+ /**
+ * Gets whether this opcode is a function/method call or similar.
+ *
+ * @return {@code true} iff this opcode is call-like
+ */
+ public boolean isCallLike() {
+ return isCallLike;
+ }
+
+
+ /**
+ * Gets whether this opcode is commutative (the order of its sources are
+ * unimportant) or not. All commutative Rops have exactly two sources and
+ * have no branchiness.
+ *
+ * @return true if rop is commutative
+ */
+ public boolean isCommutative() {
+ switch (opcode) {
+ case RegOps.AND:
+ case RegOps.OR:
+ case RegOps.XOR:
+ case RegOps.ADD:
+ case RegOps.MUL:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Gets the nickname. If this instance has no nickname, this returns
+ * the result of calling {@link #toString}.
+ *
+ * @return {@code non-null;} the nickname
+ */
+ public String getNickname() {
+ if (nickname != null) {
+ return nickname;
+ }
+
+ return toString();
+ }
+
+ /**
+ * Gets whether this operation can possibly throw an exception. This
+ * is just a convenient wrapper for
+ * {@code getExceptions().size() != 0}.
+ *
+ * @return {@code true} iff this operation can possibly throw
+ */
+ public final boolean canThrow() {
+ return (exceptions.size() != 0);
+ }
+}
diff --git a/dx/src/com/android/dx/rop/code/RopMethod.java b/dx/src/com/android/dx/rop/code/RopMethod.java
new file mode 100644
index 0000000..591d325
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/RopMethod.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.util.Bits;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IntList;
+
+/**
+ * All of the parts that make up a method at the rop layer.
+ */
+public final class RopMethod {
+ /** {@code non-null;} basic block list of the method */
+ private final BasicBlockList blocks;
+
+ /** {@code >= 0;} label for the block which starts the method */
+ private final int firstLabel;
+
+ /**
+ * {@code null-ok;} array of predecessors for each block, indexed by block
+ * label
+ */
+ private IntList[] predecessors;
+
+ /**
+ * {@code null-ok;} the predecessors for the implicit "exit" block, that is
+ * the labels for the blocks that return, if calculated
+ */
+ private IntList exitPredecessors;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param blocks {@code non-null;} basic block list of the method
+ * @param firstLabel {@code >= 0;} the label of the first block to execute
+ */
+ public RopMethod(BasicBlockList blocks, int firstLabel) {
+ if (blocks == null) {
+ throw new NullPointerException("blocks == null");
+ }
+
+ if (firstLabel < 0) {
+ throw new IllegalArgumentException("firstLabel < 0");
+ }
+
+ this.blocks = blocks;
+ this.firstLabel = firstLabel;
+
+ this.predecessors = null;
+ this.exitPredecessors = null;
+ }
+
+ /**
+ * Gets the basic block list for this method.
+ *
+ * @return {@code non-null;} the list
+ */
+ public BasicBlockList getBlocks() {
+ return blocks;
+ }
+
+ /**
+ * Gets the label for the first block in the method that this list
+ * represents.
+ *
+ * @return {@code >= 0;} the first-block label
+ */
+ public int getFirstLabel() {
+ return firstLabel;
+ }
+
+ /**
+ * Gets the predecessors associated with the given block. This throws
+ * an exception if there is no block with the given label.
+ *
+ * @param label {@code >= 0;} the label of the block in question
+ * @return {@code non-null;} the predecessors of that block
+ */
+ public IntList labelToPredecessors(int label) {
+ if (exitPredecessors == null) {
+ calcPredecessors();
+ }
+
+ IntList result = predecessors[label];
+
+ if (result == null) {
+ throw new RuntimeException("no such block: " + Hex.u2(label));
+ }
+
+ return result;
+ }
+
+ /**
+ * Gets the exit predecessors for this instance.
+ *
+ * @return {@code non-null;} the exit predecessors
+ */
+ public IntList getExitPredecessors() {
+ if (exitPredecessors == null) {
+ calcPredecessors();
+ }
+
+ return exitPredecessors;
+ }
+
+
+ /**
+ * Returns an instance that is identical to this one, except that
+ * the registers in each instruction are offset by the given
+ * amount.
+ *
+ * @param delta the amount to offset register numbers by
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public RopMethod withRegisterOffset(int delta) {
+ RopMethod result = new RopMethod(blocks.withRegisterOffset(delta),
+ firstLabel);
+
+ if (exitPredecessors != null) {
+ /*
+ * The predecessors have been calculated. It's safe to
+ * inject these into the new instance, since the
+ * transformation being applied doesn't affect the
+ * predecessors.
+ */
+ result.exitPredecessors = exitPredecessors;
+ result.predecessors = predecessors;
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the predecessor sets for each block as well as for the
+ * exit.
+ */
+ private void calcPredecessors() {
+ int maxLabel = blocks.getMaxLabel();
+ IntList[] predecessors = new IntList[maxLabel];
+ IntList exitPredecessors = new IntList(10);
+ int sz = blocks.size();
+
+ /*
+ * For each block, find its successors, and add the block's label to
+ * the successor's predecessors.
+ */
+ for (int i = 0; i < sz; i++) {
+ BasicBlock one = blocks.get(i);
+ int label = one.getLabel();
+ IntList successors = one.getSuccessors();
+ int ssz = successors.size();
+ if (ssz == 0) {
+ // This block exits.
+ exitPredecessors.add(label);
+ } else {
+ for (int j = 0; j < ssz; j++) {
+ int succLabel = successors.get(j);
+ IntList succPreds = predecessors[succLabel];
+ if (succPreds == null) {
+ succPreds = new IntList(10);
+ predecessors[succLabel] = succPreds;
+ }
+ succPreds.add(label);
+ }
+ }
+ }
+
+ // Sort and immutablize all the predecessor lists.
+ for (int i = 0; i < maxLabel; i++) {
+ IntList preds = predecessors[i];
+ if (preds != null) {
+ preds.sort();
+ preds.setImmutable();
+ }
+ }
+
+ exitPredecessors.sort();
+ exitPredecessors.setImmutable();
+
+ /*
+ * The start label might not ever have had any predecessors
+ * added to it (probably doesn't, because of how Java gets
+ * translated into rop form). So, check for this and rectify
+ * the situation if required.
+ */
+ if (predecessors[firstLabel] == null) {
+ predecessors[firstLabel] = IntList.EMPTY;
+ }
+
+ this.predecessors = predecessors;
+ this.exitPredecessors = exitPredecessors;
+ }
+}
diff --git a/dx/src/com/android/dx/rop/code/Rops.java b/dx/src/com/android/dx/rop/code/Rops.java
new file mode 100644
index 0000000..9085ff4
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/Rops.java
@@ -0,0 +1,2086 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstBaseMethodRef;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.type.Prototype;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.rop.type.TypeList;
+
+/**
+ * Standard instances of {@link Rop}.
+ */
+public final class Rops {
+ /** {@code nop()} */
+ public static final Rop NOP =
+ new Rop(RegOps.NOP, Type.VOID, StdTypeList.EMPTY, "nop");
+
+ /** {@code r,x: int :: r = x;} */
+ public static final Rop MOVE_INT =
+ new Rop(RegOps.MOVE, Type.INT, StdTypeList.INT, "move-int");
+
+ /** {@code r,x: long :: r = x;} */
+ public static final Rop MOVE_LONG =
+ new Rop(RegOps.MOVE, Type.LONG, StdTypeList.LONG, "move-long");
+
+ /** {@code r,x: float :: r = x;} */
+ public static final Rop MOVE_FLOAT =
+ new Rop(RegOps.MOVE, Type.FLOAT, StdTypeList.FLOAT, "move-float");
+
+ /** {@code r,x: double :: r = x;} */
+ public static final Rop MOVE_DOUBLE =
+ new Rop(RegOps.MOVE, Type.DOUBLE, StdTypeList.DOUBLE, "move-double");
+
+ /** {@code r,x: Object :: r = x;} */
+ public static final Rop MOVE_OBJECT =
+ new Rop(RegOps.MOVE, Type.OBJECT, StdTypeList.OBJECT, "move-object");
+
+ /**
+ * {@code r,x: ReturnAddress :: r = x;}
+ *
+ * Note that this rop-form instruction has no dex-form equivilent and
+ * must be removed before the dex conversion.
+ */
+ public static final Rop MOVE_RETURN_ADDRESS =
+ new Rop(RegOps.MOVE, Type.RETURN_ADDRESS,
+ StdTypeList.RETURN_ADDRESS, "move-return-address");
+
+ /** {@code r,param(x): int :: r = param(x);} */
+ public static final Rop MOVE_PARAM_INT =
+ new Rop(RegOps.MOVE_PARAM, Type.INT, StdTypeList.EMPTY,
+ "move-param-int");
+
+ /** {@code r,param(x): long :: r = param(x);} */
+ public static final Rop MOVE_PARAM_LONG =
+ new Rop(RegOps.MOVE_PARAM, Type.LONG, StdTypeList.EMPTY,
+ "move-param-long");
+
+ /** {@code r,param(x): float :: r = param(x);} */
+ public static final Rop MOVE_PARAM_FLOAT =
+ new Rop(RegOps.MOVE_PARAM, Type.FLOAT, StdTypeList.EMPTY,
+ "move-param-float");
+
+ /** {@code r,param(x): double :: r = param(x);} */
+ public static final Rop MOVE_PARAM_DOUBLE =
+ new Rop(RegOps.MOVE_PARAM, Type.DOUBLE, StdTypeList.EMPTY,
+ "move-param-double");
+
+ /** {@code r,param(x): Object :: r = param(x);} */
+ public static final Rop MOVE_PARAM_OBJECT =
+ new Rop(RegOps.MOVE_PARAM, Type.OBJECT, StdTypeList.EMPTY,
+ "move-param-object");
+
+ /** {@code r, literal: int :: r = literal;} */
+ public static final Rop CONST_INT =
+ new Rop(RegOps.CONST, Type.INT, StdTypeList.EMPTY, "const-int");
+
+ /** {@code r, literal: long :: r = literal;} */
+ public static final Rop CONST_LONG =
+ new Rop(RegOps.CONST, Type.LONG, StdTypeList.EMPTY, "const-long");
+
+ /** {@code r, literal: float :: r = literal;} */
+ public static final Rop CONST_FLOAT =
+ new Rop(RegOps.CONST, Type.FLOAT, StdTypeList.EMPTY, "const-float");
+
+ /** {@code r, literal: double :: r = literal;} */
+ public static final Rop CONST_DOUBLE =
+ new Rop(RegOps.CONST, Type.DOUBLE, StdTypeList.EMPTY, "const-double");
+
+ /** {@code r, literal: Object :: r = literal;} */
+ public static final Rop CONST_OBJECT =
+ new Rop(RegOps.CONST, Type.OBJECT, StdTypeList.EMPTY,
+ Exceptions.LIST_Error, "const-object");
+
+ /** {@code r, literal: Object :: r = literal;} */
+ public static final Rop CONST_OBJECT_NOTHROW =
+ new Rop(RegOps.CONST, Type.OBJECT, StdTypeList.EMPTY,
+ "const-object-nothrow");
+
+ /** {@code goto label} */
+ public static final Rop GOTO =
+ new Rop(RegOps.GOTO, Type.VOID, StdTypeList.EMPTY, Rop.BRANCH_GOTO,
+ "goto");
+
+ /** {@code x: int :: if (x == 0) goto label} */
+ public static final Rop IF_EQZ_INT =
+ new Rop(RegOps.IF_EQ, Type.VOID, StdTypeList.INT, Rop.BRANCH_IF,
+ "if-eqz-int");
+
+ /** {@code x: int :: if (x != 0) goto label} */
+ public static final Rop IF_NEZ_INT =
+ new Rop(RegOps.IF_NE, Type.VOID, StdTypeList.INT, Rop.BRANCH_IF,
+ "if-nez-int");
+
+ /** {@code x: int :: if (x < 0) goto label} */
+ public static final Rop IF_LTZ_INT =
+ new Rop(RegOps.IF_LT, Type.VOID, StdTypeList.INT, Rop.BRANCH_IF,
+ "if-ltz-int");
+
+ /** {@code x: int :: if (x >= 0) goto label} */
+ public static final Rop IF_GEZ_INT =
+ new Rop(RegOps.IF_GE, Type.VOID, StdTypeList.INT, Rop.BRANCH_IF,
+ "if-gez-int");
+
+ /** {@code x: int :: if (x <= 0) goto label} */
+ public static final Rop IF_LEZ_INT =
+ new Rop(RegOps.IF_LE, Type.VOID, StdTypeList.INT, Rop.BRANCH_IF,
+ "if-lez-int");
+
+ /** {@code x: int :: if (x > 0) goto label} */
+ public static final Rop IF_GTZ_INT =
+ new Rop(RegOps.IF_GT, Type.VOID, StdTypeList.INT, Rop.BRANCH_IF,
+ "if-gtz-int");
+
+ /** {@code x: Object :: if (x == null) goto label} */
+ public static final Rop IF_EQZ_OBJECT =
+ new Rop(RegOps.IF_EQ, Type.VOID, StdTypeList.OBJECT, Rop.BRANCH_IF,
+ "if-eqz-object");
+
+ /** {@code x: Object :: if (x != null) goto label} */
+ public static final Rop IF_NEZ_OBJECT =
+ new Rop(RegOps.IF_NE, Type.VOID, StdTypeList.OBJECT, Rop.BRANCH_IF,
+ "if-nez-object");
+
+ /** {@code x,y: int :: if (x == y) goto label} */
+ public static final Rop IF_EQ_INT =
+ new Rop(RegOps.IF_EQ, Type.VOID, StdTypeList.INT_INT, Rop.BRANCH_IF,
+ "if-eq-int");
+
+ /** {@code x,y: int :: if (x != y) goto label} */
+ public static final Rop IF_NE_INT =
+ new Rop(RegOps.IF_NE, Type.VOID, StdTypeList.INT_INT, Rop.BRANCH_IF,
+ "if-ne-int");
+
+ /** {@code x,y: int :: if (x < y) goto label} */
+ public static final Rop IF_LT_INT =
+ new Rop(RegOps.IF_LT, Type.VOID, StdTypeList.INT_INT, Rop.BRANCH_IF,
+ "if-lt-int");
+
+ /** {@code x,y: int :: if (x >= y) goto label} */
+ public static final Rop IF_GE_INT =
+ new Rop(RegOps.IF_GE, Type.VOID, StdTypeList.INT_INT, Rop.BRANCH_IF,
+ "if-ge-int");
+
+ /** {@code x,y: int :: if (x <= y) goto label} */
+ public static final Rop IF_LE_INT =
+ new Rop(RegOps.IF_LE, Type.VOID, StdTypeList.INT_INT, Rop.BRANCH_IF,
+ "if-le-int");
+
+ /** {@code x,y: int :: if (x > y) goto label} */
+ public static final Rop IF_GT_INT =
+ new Rop(RegOps.IF_GT, Type.VOID, StdTypeList.INT_INT, Rop.BRANCH_IF,
+ "if-gt-int");
+
+ /** {@code x,y: Object :: if (x == y) goto label} */
+ public static final Rop IF_EQ_OBJECT =
+ new Rop(RegOps.IF_EQ, Type.VOID, StdTypeList.OBJECT_OBJECT,
+ Rop.BRANCH_IF, "if-eq-object");
+
+ /** {@code x,y: Object :: if (x != y) goto label} */
+ public static final Rop IF_NE_OBJECT =
+ new Rop(RegOps.IF_NE, Type.VOID, StdTypeList.OBJECT_OBJECT,
+ Rop.BRANCH_IF, "if-ne-object");
+
+ /** {@code x: int :: goto switchtable[x]} */
+ public static final Rop SWITCH =
+ new Rop(RegOps.SWITCH, Type.VOID, StdTypeList.INT, Rop.BRANCH_SWITCH,
+ "switch");
+
+ /** {@code r,x,y: int :: r = x + y;} */
+ public static final Rop ADD_INT =
+ new Rop(RegOps.ADD, Type.INT, StdTypeList.INT_INT, "add-int");
+
+ /** {@code r,x,y: long :: r = x + y;} */
+ public static final Rop ADD_LONG =
+ new Rop(RegOps.ADD, Type.LONG, StdTypeList.LONG_LONG, "add-long");
+
+ /** {@code r,x,y: float :: r = x + y;} */
+ public static final Rop ADD_FLOAT =
+ new Rop(RegOps.ADD, Type.FLOAT, StdTypeList.FLOAT_FLOAT, "add-float");
+
+ /** {@code r,x,y: double :: r = x + y;} */
+ public static final Rop ADD_DOUBLE =
+ new Rop(RegOps.ADD, Type.DOUBLE, StdTypeList.DOUBLE_DOUBLE,
+ Rop.BRANCH_NONE, "add-double");
+
+ /** {@code r,x,y: int :: r = x - y;} */
+ public static final Rop SUB_INT =
+ new Rop(RegOps.SUB, Type.INT, StdTypeList.INT_INT, "sub-int");
+
+ /** {@code r,x,y: long :: r = x - y;} */
+ public static final Rop SUB_LONG =
+ new Rop(RegOps.SUB, Type.LONG, StdTypeList.LONG_LONG, "sub-long");
+
+ /** {@code r,x,y: float :: r = x - y;} */
+ public static final Rop SUB_FLOAT =
+ new Rop(RegOps.SUB, Type.FLOAT, StdTypeList.FLOAT_FLOAT, "sub-float");
+
+ /** {@code r,x,y: double :: r = x - y;} */
+ public static final Rop SUB_DOUBLE =
+ new Rop(RegOps.SUB, Type.DOUBLE, StdTypeList.DOUBLE_DOUBLE,
+ Rop.BRANCH_NONE, "sub-double");
+
+ /** {@code r,x,y: int :: r = x * y;} */
+ public static final Rop MUL_INT =
+ new Rop(RegOps.MUL, Type.INT, StdTypeList.INT_INT, "mul-int");
+
+ /** {@code r,x,y: long :: r = x * y;} */
+ public static final Rop MUL_LONG =
+ new Rop(RegOps.MUL, Type.LONG, StdTypeList.LONG_LONG, "mul-long");
+
+ /** {@code r,x,y: float :: r = x * y;} */
+ public static final Rop MUL_FLOAT =
+ new Rop(RegOps.MUL, Type.FLOAT, StdTypeList.FLOAT_FLOAT, "mul-float");
+
+ /** {@code r,x,y: double :: r = x * y;} */
+ public static final Rop MUL_DOUBLE =
+ new Rop(RegOps.MUL, Type.DOUBLE, StdTypeList.DOUBLE_DOUBLE,
+ Rop.BRANCH_NONE, "mul-double");
+
+ /** {@code r,x,y: int :: r = x / y;} */
+ public static final Rop DIV_INT =
+ new Rop(RegOps.DIV, Type.INT, StdTypeList.INT_INT,
+ Exceptions.LIST_Error_ArithmeticException, "div-int");
+
+ /** {@code r,x,y: long :: r = x / y;} */
+ public static final Rop DIV_LONG =
+ new Rop(RegOps.DIV, Type.LONG, StdTypeList.LONG_LONG,
+ Exceptions.LIST_Error_ArithmeticException, "div-long");
+
+ /** {@code r,x,y: float :: r = x / y;} */
+ public static final Rop DIV_FLOAT =
+ new Rop(RegOps.DIV, Type.FLOAT, StdTypeList.FLOAT_FLOAT, "div-float");
+
+ /** {@code r,x,y: double :: r = x / y;} */
+ public static final Rop DIV_DOUBLE =
+ new Rop(RegOps.DIV, Type.DOUBLE, StdTypeList.DOUBLE_DOUBLE,
+ "div-double");
+
+ /** {@code r,x,y: int :: r = x % y;} */
+ public static final Rop REM_INT =
+ new Rop(RegOps.REM, Type.INT, StdTypeList.INT_INT,
+ Exceptions.LIST_Error_ArithmeticException, "rem-int");
+
+ /** {@code r,x,y: long :: r = x % y;} */
+ public static final Rop REM_LONG =
+ new Rop(RegOps.REM, Type.LONG, StdTypeList.LONG_LONG,
+ Exceptions.LIST_Error_ArithmeticException, "rem-long");
+
+ /** {@code r,x,y: float :: r = x % y;} */
+ public static final Rop REM_FLOAT =
+ new Rop(RegOps.REM, Type.FLOAT, StdTypeList.FLOAT_FLOAT, "rem-float");
+
+ /** {@code r,x,y: double :: r = x % y;} */
+ public static final Rop REM_DOUBLE =
+ new Rop(RegOps.REM, Type.DOUBLE, StdTypeList.DOUBLE_DOUBLE,
+ "rem-double");
+
+ /** {@code r,x: int :: r = -x;} */
+ public static final Rop NEG_INT =
+ new Rop(RegOps.NEG, Type.INT, StdTypeList.INT, "neg-int");
+
+ /** {@code r,x: long :: r = -x;} */
+ public static final Rop NEG_LONG =
+ new Rop(RegOps.NEG, Type.LONG, StdTypeList.LONG, "neg-long");
+
+ /** {@code r,x: float :: r = -x;} */
+ public static final Rop NEG_FLOAT =
+ new Rop(RegOps.NEG, Type.FLOAT, StdTypeList.FLOAT, "neg-float");
+
+ /** {@code r,x: double :: r = -x;} */
+ public static final Rop NEG_DOUBLE =
+ new Rop(RegOps.NEG, Type.DOUBLE, StdTypeList.DOUBLE, "neg-double");
+
+ /** {@code r,x,y: int :: r = x & y;} */
+ public static final Rop AND_INT =
+ new Rop(RegOps.AND, Type.INT, StdTypeList.INT_INT, "and-int");
+
+ /** {@code r,x,y: long :: r = x & y;} */
+ public static final Rop AND_LONG =
+ new Rop(RegOps.AND, Type.LONG, StdTypeList.LONG_LONG, "and-long");
+
+ /** {@code r,x,y: int :: r = x | y;} */
+ public static final Rop OR_INT =
+ new Rop(RegOps.OR, Type.INT, StdTypeList.INT_INT, "or-int");
+
+ /** {@code r,x,y: long :: r = x | y;} */
+ public static final Rop OR_LONG =
+ new Rop(RegOps.OR, Type.LONG, StdTypeList.LONG_LONG, "or-long");
+
+ /** {@code r,x,y: int :: r = x ^ y;} */
+ public static final Rop XOR_INT =
+ new Rop(RegOps.XOR, Type.INT, StdTypeList.INT_INT, "xor-int");
+
+ /** {@code r,x,y: long :: r = x ^ y;} */
+ public static final Rop XOR_LONG =
+ new Rop(RegOps.XOR, Type.LONG, StdTypeList.LONG_LONG, "xor-long");
+
+ /** {@code r,x,y: int :: r = x << y;} */
+ public static final Rop SHL_INT =
+ new Rop(RegOps.SHL, Type.INT, StdTypeList.INT_INT, "shl-int");
+
+ /** {@code r,x: long; y: int :: r = x << y;} */
+ public static final Rop SHL_LONG =
+ new Rop(RegOps.SHL, Type.LONG, StdTypeList.LONG_INT, "shl-long");
+
+ /** {@code r,x,y: int :: r = x >> y;} */
+ public static final Rop SHR_INT =
+ new Rop(RegOps.SHR, Type.INT, StdTypeList.INT_INT, "shr-int");
+
+ /** {@code r,x: long; y: int :: r = x >> y;} */
+ public static final Rop SHR_LONG =
+ new Rop(RegOps.SHR, Type.LONG, StdTypeList.LONG_INT, "shr-long");
+
+ /** {@code r,x,y: int :: r = x >>> y;} */
+ public static final Rop USHR_INT =
+ new Rop(RegOps.USHR, Type.INT, StdTypeList.INT_INT, "ushr-int");
+
+ /** {@code r,x: long; y: int :: r = x >>> y;} */
+ public static final Rop USHR_LONG =
+ new Rop(RegOps.USHR, Type.LONG, StdTypeList.LONG_INT, "ushr-long");
+
+ /** {@code r,x: int :: r = ~x;} */
+ public static final Rop NOT_INT =
+ new Rop(RegOps.NOT, Type.INT, StdTypeList.INT, "not-int");
+
+ /** {@code r,x: long :: r = ~x;} */
+ public static final Rop NOT_LONG =
+ new Rop(RegOps.NOT, Type.LONG, StdTypeList.LONG, "not-long");
+
+ /** {@code r,x,c: int :: r = x + c;} */
+ public static final Rop ADD_CONST_INT =
+ new Rop(RegOps.ADD, Type.INT, StdTypeList.INT, "add-const-int");
+
+ /** {@code r,x,c: long :: r = x + c;} */
+ public static final Rop ADD_CONST_LONG =
+ new Rop(RegOps.ADD, Type.LONG, StdTypeList.LONG, "add-const-long");
+
+ /** {@code r,x,c: float :: r = x + c;} */
+ public static final Rop ADD_CONST_FLOAT =
+ new Rop(RegOps.ADD, Type.FLOAT, StdTypeList.FLOAT, "add-const-float");
+
+ /** {@code r,x,c: double :: r = x + c;} */
+ public static final Rop ADD_CONST_DOUBLE =
+ new Rop(RegOps.ADD, Type.DOUBLE, StdTypeList.DOUBLE,
+ "add-const-double");
+
+ /** {@code r,x,c: int :: r = x - c;} */
+ public static final Rop SUB_CONST_INT =
+ new Rop(RegOps.SUB, Type.INT, StdTypeList.INT, "sub-const-int");
+
+ /** {@code r,x,c: long :: r = x - c;} */
+ public static final Rop SUB_CONST_LONG =
+ new Rop(RegOps.SUB, Type.LONG, StdTypeList.LONG, "sub-const-long");
+
+ /** {@code r,x,c: float :: r = x - c;} */
+ public static final Rop SUB_CONST_FLOAT =
+ new Rop(RegOps.SUB, Type.FLOAT, StdTypeList.FLOAT, "sub-const-float");
+
+ /** {@code r,x,c: double :: r = x - c;} */
+ public static final Rop SUB_CONST_DOUBLE =
+ new Rop(RegOps.SUB, Type.DOUBLE, StdTypeList.DOUBLE,
+ "sub-const-double");
+
+ /** {@code r,x,c: int :: r = x * c;} */
+ public static final Rop MUL_CONST_INT =
+ new Rop(RegOps.MUL, Type.INT, StdTypeList.INT, "mul-const-int");
+
+ /** {@code r,x,c: long :: r = x * c;} */
+ public static final Rop MUL_CONST_LONG =
+ new Rop(RegOps.MUL, Type.LONG, StdTypeList.LONG, "mul-const-long");
+
+ /** {@code r,x,c: float :: r = x * c;} */
+ public static final Rop MUL_CONST_FLOAT =
+ new Rop(RegOps.MUL, Type.FLOAT, StdTypeList.FLOAT, "mul-const-float");
+
+ /** {@code r,x,c: double :: r = x * c;} */
+ public static final Rop MUL_CONST_DOUBLE =
+ new Rop(RegOps.MUL, Type.DOUBLE, StdTypeList.DOUBLE,
+ "mul-const-double");
+
+ /** {@code r,x,c: int :: r = x / c;} */
+ public static final Rop DIV_CONST_INT =
+ new Rop(RegOps.DIV, Type.INT, StdTypeList.INT,
+ Exceptions.LIST_Error_ArithmeticException, "div-const-int");
+
+ /** {@code r,x,c: long :: r = x / c;} */
+ public static final Rop DIV_CONST_LONG =
+ new Rop(RegOps.DIV, Type.LONG, StdTypeList.LONG,
+ Exceptions.LIST_Error_ArithmeticException, "div-const-long");
+
+ /** {@code r,x,c: float :: r = x / c;} */
+ public static final Rop DIV_CONST_FLOAT =
+ new Rop(RegOps.DIV, Type.FLOAT, StdTypeList.FLOAT, "div-const-float");
+
+ /** {@code r,x,c: double :: r = x / c;} */
+ public static final Rop DIV_CONST_DOUBLE =
+ new Rop(RegOps.DIV, Type.DOUBLE, StdTypeList.DOUBLE,
+ "div-const-double");
+
+ /** {@code r,x,c: int :: r = x % c;} */
+ public static final Rop REM_CONST_INT =
+ new Rop(RegOps.REM, Type.INT, StdTypeList.INT,
+ Exceptions.LIST_Error_ArithmeticException, "rem-const-int");
+
+ /** {@code r,x,c: long :: r = x % c;} */
+ public static final Rop REM_CONST_LONG =
+ new Rop(RegOps.REM, Type.LONG, StdTypeList.LONG,
+ Exceptions.LIST_Error_ArithmeticException, "rem-const-long");
+
+ /** {@code r,x,c: float :: r = x % c;} */
+ public static final Rop REM_CONST_FLOAT =
+ new Rop(RegOps.REM, Type.FLOAT, StdTypeList.FLOAT, "rem-const-float");
+
+ /** {@code r,x,c: double :: r = x % c;} */
+ public static final Rop REM_CONST_DOUBLE =
+ new Rop(RegOps.REM, Type.DOUBLE, StdTypeList.DOUBLE,
+ "rem-const-double");
+
+ /** {@code r,x,c: int :: r = x & c;} */
+ public static final Rop AND_CONST_INT =
+ new Rop(RegOps.AND, Type.INT, StdTypeList.INT, "and-const-int");
+
+ /** {@code r,x,c: long :: r = x & c;} */
+ public static final Rop AND_CONST_LONG =
+ new Rop(RegOps.AND, Type.LONG, StdTypeList.LONG, "and-const-long");
+
+ /** {@code r,x,c: int :: r = x | c;} */
+ public static final Rop OR_CONST_INT =
+ new Rop(RegOps.OR, Type.INT, StdTypeList.INT, "or-const-int");
+
+ /** {@code r,x,c: long :: r = x | c;} */
+ public static final Rop OR_CONST_LONG =
+ new Rop(RegOps.OR, Type.LONG, StdTypeList.LONG, "or-const-long");
+
+ /** {@code r,x,c: int :: r = x ^ c;} */
+ public static final Rop XOR_CONST_INT =
+ new Rop(RegOps.XOR, Type.INT, StdTypeList.INT, "xor-const-int");
+
+ /** {@code r,x,c: long :: r = x ^ c;} */
+ public static final Rop XOR_CONST_LONG =
+ new Rop(RegOps.XOR, Type.LONG, StdTypeList.LONG, "xor-const-long");
+
+ /** {@code r,x,c: int :: r = x << c;} */
+ public static final Rop SHL_CONST_INT =
+ new Rop(RegOps.SHL, Type.INT, StdTypeList.INT, "shl-const-int");
+
+ /** {@code r,x: long; c: int :: r = x << c;} */
+ public static final Rop SHL_CONST_LONG =
+ new Rop(RegOps.SHL, Type.LONG, StdTypeList.INT, "shl-const-long");
+
+ /** {@code r,x,c: int :: r = x >> c;} */
+ public static final Rop SHR_CONST_INT =
+ new Rop(RegOps.SHR, Type.INT, StdTypeList.INT, "shr-const-int");
+
+ /** {@code r,x: long; c: int :: r = x >> c;} */
+ public static final Rop SHR_CONST_LONG =
+ new Rop(RegOps.SHR, Type.LONG, StdTypeList.INT, "shr-const-long");
+
+ /** {@code r,x,c: int :: r = x >>> c;} */
+ public static final Rop USHR_CONST_INT =
+ new Rop(RegOps.USHR, Type.INT, StdTypeList.INT, "ushr-const-int");
+
+ /** {@code r,x: long; c: int :: r = x >>> c;} */
+ public static final Rop USHR_CONST_LONG =
+ new Rop(RegOps.USHR, Type.LONG, StdTypeList.INT, "ushr-const-long");
+
+ /** {@code r: int; x,y: long :: r = cmp(x, y);} */
+ public static final Rop CMPL_LONG =
+ new Rop(RegOps.CMPL, Type.INT, StdTypeList.LONG_LONG, "cmpl-long");
+
+ /** {@code r: int; x,y: float :: r = cmpl(x, y);} */
+ public static final Rop CMPL_FLOAT =
+ new Rop(RegOps.CMPL, Type.INT, StdTypeList.FLOAT_FLOAT, "cmpl-float");
+
+ /** {@code r: int; x,y: double :: r = cmpl(x, y);} */
+ public static final Rop CMPL_DOUBLE =
+ new Rop(RegOps.CMPL, Type.INT, StdTypeList.DOUBLE_DOUBLE,
+ "cmpl-double");
+
+ /** {@code r: int; x,y: float :: r = cmpg(x, y);} */
+ public static final Rop CMPG_FLOAT =
+ new Rop(RegOps.CMPG, Type.INT, StdTypeList.FLOAT_FLOAT, "cmpg-float");
+
+ /** {@code r: int; x,y: double :: r = cmpg(x, y);} */
+ public static final Rop CMPG_DOUBLE =
+ new Rop(RegOps.CMPG, Type.INT, StdTypeList.DOUBLE_DOUBLE,
+ "cmpg-double");
+
+ /** {@code r: int; x: long :: r = (int) x} */
+ public static final Rop CONV_L2I =
+ new Rop(RegOps.CONV, Type.INT, StdTypeList.LONG, "conv-l2i");
+
+ /** {@code r: int; x: float :: r = (int) x} */
+ public static final Rop CONV_F2I =
+ new Rop(RegOps.CONV, Type.INT, StdTypeList.FLOAT, "conv-f2i");
+
+ /** {@code r: int; x: double :: r = (int) x} */
+ public static final Rop CONV_D2I =
+ new Rop(RegOps.CONV, Type.INT, StdTypeList.DOUBLE, "conv-d2i");
+
+ /** {@code r: long; x: int :: r = (long) x} */
+ public static final Rop CONV_I2L =
+ new Rop(RegOps.CONV, Type.LONG, StdTypeList.INT, "conv-i2l");
+
+ /** {@code r: long; x: float :: r = (long) x} */
+ public static final Rop CONV_F2L =
+ new Rop(RegOps.CONV, Type.LONG, StdTypeList.FLOAT, "conv-f2l");
+
+ /** {@code r: long; x: double :: r = (long) x} */
+ public static final Rop CONV_D2L =
+ new Rop(RegOps.CONV, Type.LONG, StdTypeList.DOUBLE, "conv-d2l");
+
+ /** {@code r: float; x: int :: r = (float) x} */
+ public static final Rop CONV_I2F =
+ new Rop(RegOps.CONV, Type.FLOAT, StdTypeList.INT, "conv-i2f");
+
+ /** {@code r: float; x: long :: r = (float) x} */
+ public static final Rop CONV_L2F =
+ new Rop(RegOps.CONV, Type.FLOAT, StdTypeList.LONG, "conv-l2f");
+
+ /** {@code r: float; x: double :: r = (float) x} */
+ public static final Rop CONV_D2F =
+ new Rop(RegOps.CONV, Type.FLOAT, StdTypeList.DOUBLE, "conv-d2f");
+
+ /** {@code r: double; x: int :: r = (double) x} */
+ public static final Rop CONV_I2D =
+ new Rop(RegOps.CONV, Type.DOUBLE, StdTypeList.INT, "conv-i2d");
+
+ /** {@code r: double; x: long :: r = (double) x} */
+ public static final Rop CONV_L2D =
+ new Rop(RegOps.CONV, Type.DOUBLE, StdTypeList.LONG, "conv-l2d");
+
+ /** {@code r: double; x: float :: r = (double) x} */
+ public static final Rop CONV_F2D =
+ new Rop(RegOps.CONV, Type.DOUBLE, StdTypeList.FLOAT, "conv-f2d");
+
+ /**
+ * {@code r,x: int :: r = (x << 24) >> 24} (Java-style
+ * convert int to byte)
+ */
+ public static final Rop TO_BYTE =
+ new Rop(RegOps.TO_BYTE, Type.INT, StdTypeList.INT, "to-byte");
+
+ /**
+ * {@code r,x: int :: r = x & 0xffff} (Java-style
+ * convert int to char)
+ */
+ public static final Rop TO_CHAR =
+ new Rop(RegOps.TO_CHAR, Type.INT, StdTypeList.INT, "to-char");
+
+ /**
+ * {@code r,x: int :: r = (x << 16) >> 16} (Java-style
+ * convert int to short)
+ */
+ public static final Rop TO_SHORT =
+ new Rop(RegOps.TO_SHORT, Type.INT, StdTypeList.INT, "to-short");
+
+ /** {@code return void} */
+ public static final Rop RETURN_VOID =
+ new Rop(RegOps.RETURN, Type.VOID, StdTypeList.EMPTY, Rop.BRANCH_RETURN,
+ "return-void");
+
+ /** {@code x: int; return x} */
+ public static final Rop RETURN_INT =
+ new Rop(RegOps.RETURN, Type.VOID, StdTypeList.INT, Rop.BRANCH_RETURN,
+ "return-int");
+
+ /** {@code x: long; return x} */
+ public static final Rop RETURN_LONG =
+ new Rop(RegOps.RETURN, Type.VOID, StdTypeList.LONG, Rop.BRANCH_RETURN,
+ "return-long");
+
+ /** {@code x: float; return x} */
+ public static final Rop RETURN_FLOAT =
+ new Rop(RegOps.RETURN, Type.VOID, StdTypeList.FLOAT, Rop.BRANCH_RETURN,
+ "return-float");
+
+ /** {@code x: double; return x} */
+ public static final Rop RETURN_DOUBLE =
+ new Rop(RegOps.RETURN, Type.VOID, StdTypeList.DOUBLE,
+ Rop.BRANCH_RETURN, "return-double");
+
+ /** {@code x: Object; return x} */
+ public static final Rop RETURN_OBJECT =
+ new Rop(RegOps.RETURN, Type.VOID, StdTypeList.OBJECT,
+ Rop.BRANCH_RETURN, "return-object");
+
+ /** {@code T: any type; r: int; x: T[]; :: r = x.length} */
+ public static final Rop ARRAY_LENGTH =
+ new Rop(RegOps.ARRAY_LENGTH, Type.INT, StdTypeList.OBJECT,
+ Exceptions.LIST_Error_NullPointerException, "array-length");
+
+ /** {@code x: Throwable :: throw(x)} */
+ public static final Rop THROW =
+ new Rop(RegOps.THROW, Type.VOID, StdTypeList.THROWABLE,
+ StdTypeList.THROWABLE, "throw");
+
+ /** {@code x: Object :: monitorenter(x)} */
+ public static final Rop MONITOR_ENTER =
+ new Rop(RegOps.MONITOR_ENTER, Type.VOID, StdTypeList.OBJECT,
+ Exceptions.LIST_Error_NullPointerException, "monitor-enter");
+
+ /** {@code x: Object :: monitorexit(x)} */
+ public static final Rop MONITOR_EXIT =
+ new Rop(RegOps.MONITOR_EXIT, Type.VOID, StdTypeList.OBJECT,
+ Exceptions.LIST_Error_Null_IllegalMonitorStateException,
+ "monitor-exit");
+
+ /** {@code r,y: int; x: int[] :: r = x[y]} */
+ public static final Rop AGET_INT =
+ new Rop(RegOps.AGET, Type.INT, StdTypeList.INTARR_INT,
+ Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds,
+ "aget-int");
+
+ /** {@code r: long; x: long[]; y: int :: r = x[y]} */
+ public static final Rop AGET_LONG =
+ new Rop(RegOps.AGET, Type.LONG, StdTypeList.LONGARR_INT,
+ Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds,
+ "aget-long");
+
+ /** {@code r: float; x: float[]; y: int :: r = x[y]} */
+ public static final Rop AGET_FLOAT =
+ new Rop(RegOps.AGET, Type.FLOAT, StdTypeList.FLOATARR_INT,
+ Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds,
+ "aget-float");
+
+ /** {@code r: double; x: double[]; y: int :: r = x[y]} */
+ public static final Rop AGET_DOUBLE =
+ new Rop(RegOps.AGET, Type.DOUBLE, StdTypeList.DOUBLEARR_INT,
+ Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds,
+ "aget-double");
+
+ /** {@code r: Object; x: Object[]; y: int :: r = x[y]} */
+ public static final Rop AGET_OBJECT =
+ new Rop(RegOps.AGET, Type.OBJECT, StdTypeList.OBJECTARR_INT,
+ Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds,
+ "aget-object");
+
+ /** {@code r: boolean; x: boolean[]; y: int :: r = x[y]} */
+ public static final Rop AGET_BOOLEAN =
+ new Rop(RegOps.AGET, Type.INT, StdTypeList.BOOLEANARR_INT,
+ Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds,
+ "aget-boolean");
+
+ /** {@code r: byte; x: byte[]; y: int :: r = x[y]} */
+ public static final Rop AGET_BYTE =
+ new Rop(RegOps.AGET, Type.INT, StdTypeList.BYTEARR_INT,
+ Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, "aget-byte");
+
+ /** {@code r: char; x: char[]; y: int :: r = x[y]} */
+ public static final Rop AGET_CHAR =
+ new Rop(RegOps.AGET, Type.INT, StdTypeList.CHARARR_INT,
+ Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, "aget-char");
+
+ /** {@code r: short; x: short[]; y: int :: r = x[y]} */
+ public static final Rop AGET_SHORT =
+ new Rop(RegOps.AGET, Type.INT, StdTypeList.SHORTARR_INT,
+ Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds,
+ "aget-short");
+
+ /** {@code x,z: int; y: int[] :: y[z] = x} */
+ public static final Rop APUT_INT =
+ new Rop(RegOps.APUT, Type.VOID, StdTypeList.INT_INTARR_INT,
+ Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, "aput-int");
+
+ /** {@code x: long; y: long[]; z: int :: y[z] = x} */
+ public static final Rop APUT_LONG =
+ new Rop(RegOps.APUT, Type.VOID, StdTypeList.LONG_LONGARR_INT,
+ Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds, "aput-long");
+
+ /** {@code x: float; y: float[]; z: int :: y[z] = x} */
+ public static final Rop APUT_FLOAT =
+ new Rop(RegOps.APUT, Type.VOID, StdTypeList.FLOAT_FLOATARR_INT,
+ Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds,
+ "aput-float");
+
+ /** {@code x: double; y: double[]; z: int :: y[z] = x} */
+ public static final Rop APUT_DOUBLE =
+ new Rop(RegOps.APUT, Type.VOID, StdTypeList.DOUBLE_DOUBLEARR_INT,
+ Exceptions.LIST_Error_Null_ArrayIndexOutOfBounds,
+ "aput-double");
+
+ /** {@code x: Object; y: Object[]; z: int :: y[z] = x} */
+ public static final Rop APUT_OBJECT =
+ new Rop(RegOps.APUT, Type.VOID, StdTypeList.OBJECT_OBJECTARR_INT,
+ Exceptions.LIST_Error_Null_ArrayIndex_ArrayStore,
+ "aput-object");
+
+ /** {@code x: boolean; y: boolean[]; z: int :: y[z] = x} */
+ public static final Rop APUT_BOOLEAN =
+ new Rop(RegOps.APUT, Type.VOID, StdTypeList.INT_BOOLEANARR_INT,
+ Exceptions.LIST_Error_Null_ArrayIndex_ArrayStore,
+ "aput-boolean");
+
+ /** {@code x: byte; y: byte[]; z: int :: y[z] = x} */
+ public static final Rop APUT_BYTE =
+ new Rop(RegOps.APUT, Type.VOID, StdTypeList.INT_BYTEARR_INT,
+ Exceptions.LIST_Error_Null_ArrayIndex_ArrayStore, "aput-byte");
+
+ /** {@code x: char; y: char[]; z: int :: y[z] = x} */
+ public static final Rop APUT_CHAR =
+ new Rop(RegOps.APUT, Type.VOID, StdTypeList.INT_CHARARR_INT,
+ Exceptions.LIST_Error_Null_ArrayIndex_ArrayStore, "aput-char");
+
+ /** {@code x: short; y: short[]; z: int :: y[z] = x} */
+ public static final Rop APUT_SHORT =
+ new Rop(RegOps.APUT, Type.VOID, StdTypeList.INT_SHORTARR_INT,
+ Exceptions.LIST_Error_Null_ArrayIndex_ArrayStore,
+ "aput-short");
+
+ /**
+ * {@code T: any non-array object type :: r =
+ * alloc(T)} (allocate heap space for an object)
+ */
+ public static final Rop NEW_INSTANCE =
+ new Rop(RegOps.NEW_INSTANCE, Type.OBJECT, StdTypeList.EMPTY,
+ Exceptions.LIST_Error, "new-instance");
+
+ /** {@code r: int[]; x: int :: r = new int[x]} */
+ public static final Rop NEW_ARRAY_INT =
+ new Rop(RegOps.NEW_ARRAY, Type.INT_ARRAY, StdTypeList.INT,
+ Exceptions.LIST_Error_NegativeArraySizeException,
+ "new-array-int");
+
+ /** {@code r: long[]; x: int :: r = new long[x]} */
+ public static final Rop NEW_ARRAY_LONG =
+ new Rop(RegOps.NEW_ARRAY, Type.LONG_ARRAY, StdTypeList.INT,
+ Exceptions.LIST_Error_NegativeArraySizeException,
+ "new-array-long");
+
+ /** {@code r: float[]; x: int :: r = new float[x]} */
+ public static final Rop NEW_ARRAY_FLOAT =
+ new Rop(RegOps.NEW_ARRAY, Type.FLOAT_ARRAY, StdTypeList.INT,
+ Exceptions.LIST_Error_NegativeArraySizeException,
+ "new-array-float");
+
+ /** {@code r: double[]; x: int :: r = new double[x]} */
+ public static final Rop NEW_ARRAY_DOUBLE =
+ new Rop(RegOps.NEW_ARRAY, Type.DOUBLE_ARRAY, StdTypeList.INT,
+ Exceptions.LIST_Error_NegativeArraySizeException,
+ "new-array-double");
+
+ /** {@code r: boolean[]; x: int :: r = new boolean[x]} */
+ public static final Rop NEW_ARRAY_BOOLEAN =
+ new Rop(RegOps.NEW_ARRAY, Type.BOOLEAN_ARRAY, StdTypeList.INT,
+ Exceptions.LIST_Error_NegativeArraySizeException,
+ "new-array-boolean");
+
+ /** {@code r: byte[]; x: int :: r = new byte[x]} */
+ public static final Rop NEW_ARRAY_BYTE =
+ new Rop(RegOps.NEW_ARRAY, Type.BYTE_ARRAY, StdTypeList.INT,
+ Exceptions.LIST_Error_NegativeArraySizeException,
+ "new-array-byte");
+
+ /** {@code r: char[]; x: int :: r = new char[x]} */
+ public static final Rop NEW_ARRAY_CHAR =
+ new Rop(RegOps.NEW_ARRAY, Type.CHAR_ARRAY, StdTypeList.INT,
+ Exceptions.LIST_Error_NegativeArraySizeException,
+ "new-array-char");
+
+ /** {@code r: short[]; x: int :: r = new short[x]} */
+ public static final Rop NEW_ARRAY_SHORT =
+ new Rop(RegOps.NEW_ARRAY, Type.SHORT_ARRAY, StdTypeList.INT,
+ Exceptions.LIST_Error_NegativeArraySizeException,
+ "new-array-short");
+
+ /**
+ * {@code T: any non-array object type; x: Object :: (T) x} (can
+ * throw {@code ClassCastException})
+ */
+ public static final Rop CHECK_CAST =
+ new Rop(RegOps.CHECK_CAST, Type.VOID, StdTypeList.OBJECT,
+ Exceptions.LIST_Error_ClassCastException, "check-cast");
+
+ /**
+ * {@code T: any non-array object type; x: Object :: x instanceof
+ * T}. Note: This is listed as throwing {@code Error}
+ * explicitly because the op <i>can</i> throw, but there are no
+ * other predefined exceptions for it.
+ */
+ public static final Rop INSTANCE_OF =
+ new Rop(RegOps.INSTANCE_OF, Type.INT, StdTypeList.OBJECT,
+ Exceptions.LIST_Error, "instance-of");
+
+ /**
+ * {@code r: int; x: Object; f: instance field spec of
+ * type int :: r = x.f}
+ */
+ public static final Rop GET_FIELD_INT =
+ new Rop(RegOps.GET_FIELD, Type.INT, StdTypeList.OBJECT,
+ Exceptions.LIST_Error_NullPointerException, "get-field-int");
+
+ /**
+ * {@code r: long; x: Object; f: instance field spec of
+ * type long :: r = x.f}
+ */
+ public static final Rop GET_FIELD_LONG =
+ new Rop(RegOps.GET_FIELD, Type.LONG, StdTypeList.OBJECT,
+ Exceptions.LIST_Error_NullPointerException, "get-field-long");
+
+ /**
+ * {@code r: float; x: Object; f: instance field spec of
+ * type float :: r = x.f}
+ */
+ public static final Rop GET_FIELD_FLOAT =
+ new Rop(RegOps.GET_FIELD, Type.FLOAT, StdTypeList.OBJECT,
+ Exceptions.LIST_Error_NullPointerException,
+ "get-field-float");
+
+ /**
+ * {@code r: double; x: Object; f: instance field spec of
+ * type double :: r = x.f}
+ */
+ public static final Rop GET_FIELD_DOUBLE =
+ new Rop(RegOps.GET_FIELD, Type.DOUBLE, StdTypeList.OBJECT,
+ Exceptions.LIST_Error_NullPointerException,
+ "get-field-double");
+
+ /**
+ * {@code r: Object; x: Object; f: instance field spec of
+ * type Object :: r = x.f}
+ */
+ public static final Rop GET_FIELD_OBJECT =
+ new Rop(RegOps.GET_FIELD, Type.OBJECT, StdTypeList.OBJECT,
+ Exceptions.LIST_Error_NullPointerException,
+ "get-field-object");
+
+ /**
+ * {@code r: boolean; x: Object; f: instance field spec of
+ * type boolean :: r = x.f}
+ */
+ public static final Rop GET_FIELD_BOOLEAN =
+ new Rop(RegOps.GET_FIELD, Type.INT, StdTypeList.OBJECT,
+ Exceptions.LIST_Error_NullPointerException,
+ "get-field-boolean");
+
+ /**
+ * {@code r: byte; x: Object; f: instance field spec of
+ * type byte :: r = x.f}
+ */
+ public static final Rop GET_FIELD_BYTE =
+ new Rop(RegOps.GET_FIELD, Type.INT, StdTypeList.OBJECT,
+ Exceptions.LIST_Error_NullPointerException,
+ "get-field-byte");
+
+ /**
+ * {@code r: char; x: Object; f: instance field spec of
+ * type char :: r = x.f}
+ */
+ public static final Rop GET_FIELD_CHAR =
+ new Rop(RegOps.GET_FIELD, Type.INT, StdTypeList.OBJECT,
+ Exceptions.LIST_Error_NullPointerException,
+ "get-field-char");
+
+ /**
+ * {@code r: short; x: Object; f: instance field spec of
+ * type short :: r = x.f}
+ */
+ public static final Rop GET_FIELD_SHORT =
+ new Rop(RegOps.GET_FIELD, Type.INT, StdTypeList.OBJECT,
+ Exceptions.LIST_Error_NullPointerException,
+ "get-field-short");
+
+ /** {@code r: int; f: static field spec of type int :: r = f} */
+ public static final Rop GET_STATIC_INT =
+ new Rop(RegOps.GET_STATIC, Type.INT, StdTypeList.EMPTY,
+ Exceptions.LIST_Error, "get-static-int");
+
+ /** {@code r: long; f: static field spec of type long :: r = f} */
+ public static final Rop GET_STATIC_LONG =
+ new Rop(RegOps.GET_STATIC, Type.LONG, StdTypeList.EMPTY,
+ Exceptions.LIST_Error, "get-static-long");
+
+ /** {@code r: float; f: static field spec of type float :: r = f} */
+ public static final Rop GET_STATIC_FLOAT =
+ new Rop(RegOps.GET_STATIC, Type.FLOAT, StdTypeList.EMPTY,
+ Exceptions.LIST_Error, "get-static-float");
+
+ /** {@code r: double; f: static field spec of type double :: r = f} */
+ public static final Rop GET_STATIC_DOUBLE =
+ new Rop(RegOps.GET_STATIC, Type.DOUBLE, StdTypeList.EMPTY,
+ Exceptions.LIST_Error, "get-static-double");
+
+ /** {@code r: Object; f: static field spec of type Object :: r = f} */
+ public static final Rop GET_STATIC_OBJECT =
+ new Rop(RegOps.GET_STATIC, Type.OBJECT, StdTypeList.EMPTY,
+ Exceptions.LIST_Error, "get-static-object");
+
+ /** {@code r: boolean; f: static field spec of type boolean :: r = f} */
+ public static final Rop GET_STATIC_BOOLEAN =
+ new Rop(RegOps.GET_STATIC, Type.INT, StdTypeList.EMPTY,
+ Exceptions.LIST_Error, "get-field-boolean");
+
+ /** {@code r: byte; f: static field spec of type byte :: r = f} */
+ public static final Rop GET_STATIC_BYTE =
+ new Rop(RegOps.GET_STATIC, Type.INT, StdTypeList.EMPTY,
+ Exceptions.LIST_Error, "get-field-byte");
+
+ /** {@code r: char; f: static field spec of type char :: r = f} */
+ public static final Rop GET_STATIC_CHAR =
+ new Rop(RegOps.GET_STATIC, Type.INT, StdTypeList.EMPTY,
+ Exceptions.LIST_Error, "get-field-char");
+
+ /** {@code r: short; f: static field spec of type short :: r = f} */
+ public static final Rop GET_STATIC_SHORT =
+ new Rop(RegOps.GET_STATIC, Type.INT, StdTypeList.EMPTY,
+ Exceptions.LIST_Error, "get-field-short");
+
+ /**
+ * {@code x: int; y: Object; f: instance field spec of type
+ * int :: y.f = x}
+ */
+ public static final Rop PUT_FIELD_INT =
+ new Rop(RegOps.PUT_FIELD, Type.VOID, StdTypeList.INT_OBJECT,
+ Exceptions.LIST_Error_NullPointerException, "put-field-int");
+
+ /**
+ * {@code x: long; y: Object; f: instance field spec of type
+ * long :: y.f = x}
+ */
+ public static final Rop PUT_FIELD_LONG =
+ new Rop(RegOps.PUT_FIELD, Type.VOID, StdTypeList.LONG_OBJECT,
+ Exceptions.LIST_Error_NullPointerException, "put-field-long");
+
+ /**
+ * {@code x: float; y: Object; f: instance field spec of type
+ * float :: y.f = x}
+ */
+ public static final Rop PUT_FIELD_FLOAT =
+ new Rop(RegOps.PUT_FIELD, Type.VOID, StdTypeList.FLOAT_OBJECT,
+ Exceptions.LIST_Error_NullPointerException,
+ "put-field-float");
+
+ /**
+ * {@code x: double; y: Object; f: instance field spec of type
+ * double :: y.f = x}
+ */
+ public static final Rop PUT_FIELD_DOUBLE =
+ new Rop(RegOps.PUT_FIELD, Type.VOID, StdTypeList.DOUBLE_OBJECT,
+ Exceptions.LIST_Error_NullPointerException,
+ "put-field-double");
+
+ /**
+ * {@code x: Object; y: Object; f: instance field spec of type
+ * Object :: y.f = x}
+ */
+ public static final Rop PUT_FIELD_OBJECT =
+ new Rop(RegOps.PUT_FIELD, Type.VOID, StdTypeList.OBJECT_OBJECT,
+ Exceptions.LIST_Error_NullPointerException,
+ "put-field-object");
+
+ /**
+ * {@code x: int; y: Object; f: instance field spec of type
+ * boolean :: y.f = x}
+ */
+ public static final Rop PUT_FIELD_BOOLEAN =
+ new Rop(RegOps.PUT_FIELD, Type.VOID, StdTypeList.INT_OBJECT,
+ Exceptions.LIST_Error_NullPointerException,
+ "put-field-boolean");
+
+ /**
+ * {@code x: int; y: Object; f: instance field spec of type
+ * byte :: y.f = x}
+ */
+ public static final Rop PUT_FIELD_BYTE =
+ new Rop(RegOps.PUT_FIELD, Type.VOID, StdTypeList.INT_OBJECT,
+ Exceptions.LIST_Error_NullPointerException,
+ "put-field-byte");
+
+ /**
+ * {@code x: int; y: Object; f: instance field spec of type
+ * char :: y.f = x}
+ */
+ public static final Rop PUT_FIELD_CHAR =
+ new Rop(RegOps.PUT_FIELD, Type.VOID, StdTypeList.INT_OBJECT,
+ Exceptions.LIST_Error_NullPointerException,
+ "put-field-char");
+
+ /**
+ * {@code x: int; y: Object; f: instance field spec of type
+ * short :: y.f = x}
+ */
+ public static final Rop PUT_FIELD_SHORT =
+ new Rop(RegOps.PUT_FIELD, Type.VOID, StdTypeList.INT_OBJECT,
+ Exceptions.LIST_Error_NullPointerException,
+ "put-field-short");
+
+ /** {@code f: static field spec of type int; x: int :: f = x} */
+ public static final Rop PUT_STATIC_INT =
+ new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.INT,
+ Exceptions.LIST_Error, "put-static-int");
+
+ /** {@code f: static field spec of type long; x: long :: f = x} */
+ public static final Rop PUT_STATIC_LONG =
+ new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.LONG,
+ Exceptions.LIST_Error, "put-static-long");
+
+ /** {@code f: static field spec of type float; x: float :: f = x} */
+ public static final Rop PUT_STATIC_FLOAT =
+ new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.FLOAT,
+ Exceptions.LIST_Error, "put-static-float");
+
+ /** {@code f: static field spec of type double; x: double :: f = x} */
+ public static final Rop PUT_STATIC_DOUBLE =
+ new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.DOUBLE,
+ Exceptions.LIST_Error, "put-static-double");
+
+ /** {@code f: static field spec of type Object; x: Object :: f = x} */
+ public static final Rop PUT_STATIC_OBJECT =
+ new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.OBJECT,
+ Exceptions.LIST_Error, "put-static-object");
+
+ /**
+ * {@code f: static field spec of type boolean; x: boolean :: f =
+ * x}
+ */
+ public static final Rop PUT_STATIC_BOOLEAN =
+ new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.INT,
+ Exceptions.LIST_Error, "put-static-boolean");
+
+ /** {@code f: static field spec of type byte; x: byte :: f = x} */
+ public static final Rop PUT_STATIC_BYTE =
+ new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.INT,
+ Exceptions.LIST_Error, "put-static-byte");
+
+ /** {@code f: static field spec of type char; x: char :: f = x} */
+ public static final Rop PUT_STATIC_CHAR =
+ new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.INT,
+ Exceptions.LIST_Error, "put-static-char");
+
+ /** {@code f: static field spec of type short; x: short :: f = x} */
+ public static final Rop PUT_STATIC_SHORT =
+ new Rop(RegOps.PUT_STATIC, Type.VOID, StdTypeList.INT,
+ Exceptions.LIST_Error, "put-static-short");
+
+ /** {@code x: Int :: local variable begins in x} */
+ public static final Rop MARK_LOCAL_INT =
+ new Rop (RegOps.MARK_LOCAL, Type.VOID,
+ StdTypeList.INT, "mark-local-int");
+
+ /** {@code x: Long :: local variable begins in x} */
+ public static final Rop MARK_LOCAL_LONG =
+ new Rop (RegOps.MARK_LOCAL, Type.VOID,
+ StdTypeList.LONG, "mark-local-long");
+
+ /** {@code x: Float :: local variable begins in x} */
+ public static final Rop MARK_LOCAL_FLOAT =
+ new Rop (RegOps.MARK_LOCAL, Type.VOID,
+ StdTypeList.FLOAT, "mark-local-float");
+
+ /** {@code x: Double :: local variable begins in x} */
+ public static final Rop MARK_LOCAL_DOUBLE =
+ new Rop (RegOps.MARK_LOCAL, Type.VOID,
+ StdTypeList.DOUBLE, "mark-local-double");
+
+ /** {@code x: Object :: local variable begins in x} */
+ public static final Rop MARK_LOCAL_OBJECT =
+ new Rop (RegOps.MARK_LOCAL, Type.VOID,
+ StdTypeList.OBJECT, "mark-local-object");
+
+ /** {@code T: Any primitive type; v0..vx: T :: {v0, ..., vx}} */
+ public static final Rop FILL_ARRAY_DATA =
+ new Rop(RegOps.FILL_ARRAY_DATA, Type.VOID, StdTypeList.EMPTY,
+ "fill-array-data");
+
+ /**
+ * Returns the appropriate rop for the given opcode, destination,
+ * and sources. The result is typically, but not necessarily, a
+ * shared instance.
+ *
+ * <p><b>Note:</b> This method does not do complete error checking on
+ * its arguments, and so it may return an instance which seemed "right
+ * enough" even though in actuality the passed arguments don't quite
+ * match what is returned. TODO: Revisit this issue.</p>
+ *
+ * @param opcode the opcode
+ * @param dest {@code non-null;} destination (result) type, or
+ * {@link Type#VOID} if none
+ * @param sources {@code non-null;} list of source types
+ * @param cst {@code null-ok;} associated constant, if any
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop ropFor(int opcode, TypeBearer dest, TypeList sources,
+ Constant cst) {
+ switch (opcode) {
+ case RegOps.NOP: return NOP;
+ case RegOps.MOVE: return opMove(dest);
+ case RegOps.MOVE_PARAM: return opMoveParam(dest);
+ case RegOps.MOVE_EXCEPTION: return opMoveException(dest);
+ case RegOps.CONST: return opConst(dest);
+ case RegOps.GOTO: return GOTO;
+ case RegOps.IF_EQ: return opIfEq(sources);
+ case RegOps.IF_NE: return opIfNe(sources);
+ case RegOps.IF_LT: return opIfLt(sources);
+ case RegOps.IF_GE: return opIfGe(sources);
+ case RegOps.IF_LE: return opIfLe(sources);
+ case RegOps.IF_GT: return opIfGt(sources);
+ case RegOps.SWITCH: return SWITCH;
+ case RegOps.ADD: return opAdd(sources);
+ case RegOps.SUB: return opSub(sources);
+ case RegOps.MUL: return opMul(sources);
+ case RegOps.DIV: return opDiv(sources);
+ case RegOps.REM: return opRem(sources);
+ case RegOps.NEG: return opNeg(dest);
+ case RegOps.AND: return opAnd(sources);
+ case RegOps.OR: return opOr(sources);
+ case RegOps.XOR: return opXor(sources);
+ case RegOps.SHL: return opShl(sources);
+ case RegOps.SHR: return opShr(sources);
+ case RegOps.USHR: return opUshr(sources);
+ case RegOps.NOT: return opNot(dest);
+ case RegOps.CMPL: return opCmpl(sources.getType(0));
+ case RegOps.CMPG: return opCmpg(sources.getType(0));
+ case RegOps.CONV: return opConv(dest, sources.getType(0));
+ case RegOps.TO_BYTE: return TO_BYTE;
+ case RegOps.TO_CHAR: return TO_CHAR;
+ case RegOps.TO_SHORT: return TO_SHORT;
+ case RegOps.RETURN: {
+ if (sources.size() == 0) {
+ return RETURN_VOID;
+ }
+ return opReturn(sources.getType(0));
+ }
+ case RegOps.ARRAY_LENGTH: return ARRAY_LENGTH;
+ case RegOps.THROW: return THROW;
+ case RegOps.MONITOR_ENTER: return MONITOR_ENTER;
+ case RegOps.MONITOR_EXIT: return MONITOR_EXIT;
+ case RegOps.AGET: {
+ Type source = sources.getType(0);
+ Type componentType;
+ if (source == Type.KNOWN_NULL) {
+ /*
+ * Treat a known-null as an array of the expected
+ * result type.
+ */
+ componentType = dest.getType();
+ } else {
+ componentType = source.getComponentType();
+ }
+ return opAget(componentType);
+ }
+ case RegOps.APUT: {
+ Type source = sources.getType(1);
+ Type componentType;
+ if (source == Type.KNOWN_NULL) {
+ /*
+ * Treat a known-null as an array of the type being
+ * stored.
+ */
+ componentType = sources.getType(0);
+ } else {
+ componentType = source.getComponentType();
+ }
+ return opAput(componentType);
+ }
+ case RegOps.NEW_INSTANCE: return NEW_INSTANCE;
+ case RegOps.NEW_ARRAY: return opNewArray(dest.getType());
+ case RegOps.CHECK_CAST: return CHECK_CAST;
+ case RegOps.INSTANCE_OF: return INSTANCE_OF;
+ case RegOps.GET_FIELD: return opGetField(dest);
+ case RegOps.GET_STATIC: return opGetStatic(dest);
+ case RegOps.PUT_FIELD: return opPutField(sources.getType(0));
+ case RegOps.PUT_STATIC: return opPutStatic(sources.getType(0));
+ case RegOps.INVOKE_STATIC: {
+ return opInvokeStatic(((CstMethodRef) cst).getPrototype());
+ }
+ case RegOps.INVOKE_VIRTUAL: {
+ CstBaseMethodRef cstMeth = (CstMethodRef) cst;
+ Prototype meth = cstMeth.getPrototype();
+ CstType definer = cstMeth.getDefiningClass();
+ meth = meth.withFirstParameter(definer.getClassType());
+ return opInvokeVirtual(meth);
+ }
+ case RegOps.INVOKE_SUPER: {
+ CstBaseMethodRef cstMeth = (CstMethodRef) cst;
+ Prototype meth = cstMeth.getPrototype();
+ CstType definer = cstMeth.getDefiningClass();
+ meth = meth.withFirstParameter(definer.getClassType());
+ return opInvokeSuper(meth);
+ }
+ case RegOps.INVOKE_DIRECT: {
+ CstBaseMethodRef cstMeth = (CstMethodRef) cst;
+ Prototype meth = cstMeth.getPrototype();
+ CstType definer = cstMeth.getDefiningClass();
+ meth = meth.withFirstParameter(definer.getClassType());
+ return opInvokeDirect(meth);
+ }
+ case RegOps.INVOKE_INTERFACE: {
+ CstBaseMethodRef cstMeth = (CstMethodRef) cst;
+ Prototype meth = cstMeth.getPrototype();
+ CstType definer = cstMeth.getDefiningClass();
+ meth = meth.withFirstParameter(definer.getClassType());
+ return opInvokeInterface(meth);
+ }
+ }
+
+ throw new RuntimeException("unknown opcode " + RegOps.opName(opcode));
+ }
+
+ /**
+ * Returns the appropriate {@code move} rop for the given type. The
+ * result is a shared instance.
+ *
+ * @param type {@code non-null;} type of value being moved
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opMove(TypeBearer type) {
+ switch (type.getBasicFrameType()) {
+ case Type.BT_INT: return MOVE_INT;
+ case Type.BT_LONG: return MOVE_LONG;
+ case Type.BT_FLOAT: return MOVE_FLOAT;
+ case Type.BT_DOUBLE: return MOVE_DOUBLE;
+ case Type.BT_OBJECT: return MOVE_OBJECT;
+ case Type.BT_ADDR: return MOVE_RETURN_ADDRESS;
+ }
+
+ return throwBadType(type);
+ }
+
+ /**
+ * Returns the appropriate {@code move-param} rop for the
+ * given type. The result is a shared instance.
+ *
+ * @param type {@code non-null;} type of value being moved
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opMoveParam(TypeBearer type) {
+ switch (type.getBasicFrameType()) {
+ case Type.BT_INT: return MOVE_PARAM_INT;
+ case Type.BT_LONG: return MOVE_PARAM_LONG;
+ case Type.BT_FLOAT: return MOVE_PARAM_FLOAT;
+ case Type.BT_DOUBLE: return MOVE_PARAM_DOUBLE;
+ case Type.BT_OBJECT: return MOVE_PARAM_OBJECT;
+ }
+
+ return throwBadType(type);
+ }
+
+ /**
+ * Returns the appropriate {@code move-exception} rop for the
+ * given type. The result may be a shared instance.
+ *
+ * @param type {@code non-null;} type of the exception
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opMoveException(TypeBearer type) {
+ return new Rop(RegOps.MOVE_EXCEPTION, type.getType(),
+ StdTypeList.EMPTY, (String) null);
+ }
+
+ /**
+ * Returns the appropriate {@code move-result} rop for the
+ * given type. The result may be a shared instance.
+ *
+ * @param type {@code non-null;} type of the parameter
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opMoveResult(TypeBearer type) {
+ return new Rop(RegOps.MOVE_RESULT, type.getType(),
+ StdTypeList.EMPTY, (String) null);
+ }
+
+ /**
+ * Returns the appropriate {@code move-result-pseudo} rop for the
+ * given type. The result may be a shared instance.
+ *
+ * @param type {@code non-null;} type of the parameter
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opMoveResultPseudo(TypeBearer type) {
+ return new Rop(RegOps.MOVE_RESULT_PSEUDO, type.getType(),
+ StdTypeList.EMPTY, (String) null);
+ }
+
+ /**
+ * Returns the appropriate {@code const} rop for the given
+ * type. The result is a shared instance.
+ *
+ * @param type {@code non-null;} type of the constant
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opConst(TypeBearer type) {
+ if (type.getType() == Type.KNOWN_NULL) {
+ return CONST_OBJECT_NOTHROW;
+ }
+
+ switch (type.getBasicFrameType()) {
+ case Type.BT_INT: return CONST_INT;
+ case Type.BT_LONG: return CONST_LONG;
+ case Type.BT_FLOAT: return CONST_FLOAT;
+ case Type.BT_DOUBLE: return CONST_DOUBLE;
+ case Type.BT_OBJECT: return CONST_OBJECT;
+ }
+
+ return throwBadType(type);
+ }
+
+ /**
+ * Returns the appropriate {@code if-eq} rop for the given
+ * sources. The result is a shared instance.
+ *
+ * @param types {@code non-null;} source types
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opIfEq(TypeList types) {
+ return pickIf(types, IF_EQZ_INT, IF_EQZ_OBJECT,
+ IF_EQ_INT, IF_EQ_OBJECT);
+ }
+
+ /**
+ * Returns the appropriate {@code if-ne} rop for the given
+ * sources. The result is a shared instance.
+ *
+ * @param types {@code non-null;} source types
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opIfNe(TypeList types) {
+ return pickIf(types, IF_NEZ_INT, IF_NEZ_OBJECT,
+ IF_NE_INT, IF_NE_OBJECT);
+ }
+
+ /**
+ * Returns the appropriate {@code if-lt} rop for the given
+ * sources. The result is a shared instance.
+ *
+ * @param types {@code non-null;} source types
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opIfLt(TypeList types) {
+ return pickIf(types, IF_LTZ_INT, null, IF_LT_INT, null);
+ }
+
+ /**
+ * Returns the appropriate {@code if-ge} rop for the given
+ * sources. The result is a shared instance.
+ *
+ * @param types {@code non-null;} source types
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opIfGe(TypeList types) {
+ return pickIf(types, IF_GEZ_INT, null, IF_GE_INT, null);
+ }
+
+ /**
+ * Returns the appropriate {@code if-gt} rop for the given
+ * sources. The result is a shared instance.
+ *
+ * @param types {@code non-null;} source types
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opIfGt(TypeList types) {
+ return pickIf(types, IF_GTZ_INT, null, IF_GT_INT, null);
+ }
+
+ /**
+ * Returns the appropriate {@code if-le} rop for the given
+ * sources. The result is a shared instance.
+ *
+ * @param types {@code non-null;} source types
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opIfLe(TypeList types) {
+ return pickIf(types, IF_LEZ_INT, null, IF_LE_INT, null);
+ }
+
+ /**
+ * Helper for all the {@code if*}-related methods, which
+ * checks types and picks one of the four variants, throwing if
+ * there's a problem.
+ *
+ * @param types {@code non-null;} the types
+ * @param intZ {@code non-null;} the int-to-0 comparison
+ * @param objZ {@code null-ok;} the object-to-null comparison
+ * @param intInt {@code non-null;} the int-to-int comparison
+ * @param objObj {@code non-null;} the object-to-object comparison
+ * @return {@code non-null;} the appropriate instance
+ */
+ private static Rop pickIf(TypeList types, Rop intZ, Rop objZ, Rop intInt,
+ Rop objObj) {
+ switch(types.size()) {
+ case 1: {
+ switch (types.getType(0).getBasicFrameType()) {
+ case Type.BT_INT: {
+ return intZ;
+ }
+ case Type.BT_OBJECT: {
+ if (objZ != null) {
+ return objZ;
+ }
+ }
+ }
+ break;
+ }
+ case 2: {
+ int bt = types.getType(0).getBasicFrameType();
+ if (bt == types.getType(1).getBasicFrameType()) {
+ switch (bt) {
+ case Type.BT_INT: {
+ return intInt;
+ }
+ case Type.BT_OBJECT: {
+ if (objObj != null) {
+ return objObj;
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ return throwBadTypes(types);
+ }
+
+ /**
+ * Returns the appropriate {@code add} rop for the given
+ * types. The result is a shared instance.
+ *
+ * @param types {@code non-null;} types of the sources
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opAdd(TypeList types) {
+ return pickBinaryOp(types, ADD_CONST_INT, ADD_CONST_LONG,
+ ADD_CONST_FLOAT, ADD_CONST_DOUBLE, ADD_INT,
+ ADD_LONG, ADD_FLOAT, ADD_DOUBLE);
+ }
+
+ /**
+ * Returns the appropriate {@code sub} rop for the given
+ * types. The result is a shared instance.
+ *
+ * @param types {@code non-null;} types of the sources
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opSub(TypeList types) {
+ return pickBinaryOp(types, SUB_CONST_INT, SUB_CONST_LONG,
+ SUB_CONST_FLOAT, SUB_CONST_DOUBLE, SUB_INT,
+ SUB_LONG, SUB_FLOAT, SUB_DOUBLE);
+ }
+
+ /**
+ * Returns the appropriate {@code mul} rop for the given
+ * types. The result is a shared instance.
+ *
+ * @param types {@code non-null;} types of the sources
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opMul(TypeList types) {
+ return pickBinaryOp(types, MUL_CONST_INT, MUL_CONST_LONG,
+ MUL_CONST_FLOAT, MUL_CONST_DOUBLE, MUL_INT,
+ MUL_LONG, MUL_FLOAT, MUL_DOUBLE);
+ }
+
+ /**
+ * Returns the appropriate {@code div} rop for the given
+ * types. The result is a shared instance.
+ *
+ * @param types {@code non-null;} types of the sources
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opDiv(TypeList types) {
+ return pickBinaryOp(types, DIV_CONST_INT, DIV_CONST_LONG,
+ DIV_CONST_FLOAT, DIV_CONST_DOUBLE, DIV_INT,
+ DIV_LONG, DIV_FLOAT, DIV_DOUBLE);
+ }
+
+ /**
+ * Returns the appropriate {@code rem} rop for the given
+ * types. The result is a shared instance.
+ *
+ * @param types {@code non-null;} types of the sources
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opRem(TypeList types) {
+ return pickBinaryOp(types, REM_CONST_INT, REM_CONST_LONG,
+ REM_CONST_FLOAT, REM_CONST_DOUBLE, REM_INT,
+ REM_LONG, REM_FLOAT, REM_DOUBLE);
+ }
+
+ /**
+ * Returns the appropriate {@code and} rop for the given
+ * types. The result is a shared instance.
+ *
+ * @param types {@code non-null;} types of the sources
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opAnd(TypeList types) {
+ return pickBinaryOp(types, AND_CONST_INT, AND_CONST_LONG, null, null,
+ AND_INT, AND_LONG, null, null);
+ }
+
+ /**
+ * Returns the appropriate {@code or} rop for the given
+ * types. The result is a shared instance.
+ *
+ * @param types {@code non-null;} types of the sources
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opOr(TypeList types) {
+ return pickBinaryOp(types, OR_CONST_INT, OR_CONST_LONG, null, null,
+ OR_INT, OR_LONG, null, null);
+ }
+
+ /**
+ * Returns the appropriate {@code xor} rop for the given
+ * types. The result is a shared instance.
+ *
+ * @param types {@code non-null;} types of the sources
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opXor(TypeList types) {
+ return pickBinaryOp(types, XOR_CONST_INT, XOR_CONST_LONG, null, null,
+ XOR_INT, XOR_LONG, null, null);
+ }
+
+ /**
+ * Returns the appropriate {@code shl} rop for the given
+ * types. The result is a shared instance.
+ *
+ * @param types {@code non-null;} types of the sources
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opShl(TypeList types) {
+ return pickBinaryOp(types, SHL_CONST_INT, SHL_CONST_LONG, null, null,
+ SHL_INT, SHL_LONG, null, null);
+ }
+
+ /**
+ * Returns the appropriate {@code shr} rop for the given
+ * types. The result is a shared instance.
+ *
+ * @param types {@code non-null;} types of the sources
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opShr(TypeList types) {
+ return pickBinaryOp(types, SHR_CONST_INT, SHR_CONST_LONG, null, null,
+ SHR_INT, SHR_LONG, null, null);
+ }
+
+ /**
+ * Returns the appropriate {@code ushr} rop for the given
+ * types. The result is a shared instance.
+ *
+ * @param types {@code non-null;} types of the sources
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opUshr(TypeList types) {
+ return pickBinaryOp(types, USHR_CONST_INT, USHR_CONST_LONG, null, null,
+ USHR_INT, USHR_LONG, null, null);
+ }
+
+ /**
+ * Returns the appropriate binary arithmetic rop for the given type
+ * and arguments. The result is a shared instance.
+ *
+ * @param types {@code non-null;} sources of the operation
+ * @param int1 {@code non-null;} the int-to-constant rop
+ * @param long1 {@code non-null;} the long-to-constant rop
+ * @param float1 {@code null-ok;} the float-to-constant rop, if any
+ * @param double1 {@code null-ok;} the double-to-constant rop, if any
+ * @param int2 {@code non-null;} the int-to-int rop
+ * @param long2 {@code non-null;} the long-to-long or long-to-int rop
+ * @param float2 {@code null-ok;} the float-to-float rop, if any
+ * @param double2 {@code null-ok;} the double-to-double rop, if any
+ * @return {@code non-null;} an appropriate instance
+ */
+ private static Rop pickBinaryOp(TypeList types, Rop int1, Rop long1,
+ Rop float1, Rop double1, Rop int2,
+ Rop long2, Rop float2, Rop double2) {
+ int bt1 = types.getType(0).getBasicFrameType();
+ Rop result = null;
+
+ switch (types.size()) {
+ case 1: {
+ switch(bt1) {
+ case Type.BT_INT: return int1;
+ case Type.BT_LONG: return long1;
+ case Type.BT_FLOAT: result = float1; break;
+ case Type.BT_DOUBLE: result = double1; break;
+ }
+ break;
+ }
+ case 2: {
+ switch(bt1) {
+ case Type.BT_INT: return int2;
+ case Type.BT_LONG: return long2;
+ case Type.BT_FLOAT: result = float2; break;
+ case Type.BT_DOUBLE: result = double2; break;
+ }
+ break;
+ }
+ }
+
+ if (result == null) {
+ return throwBadTypes(types);
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns the appropriate {@code neg} rop for the given type. The
+ * result is a shared instance.
+ *
+ * @param type {@code non-null;} type of value being operated on
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opNeg(TypeBearer type) {
+ switch (type.getBasicFrameType()) {
+ case Type.BT_INT: return NEG_INT;
+ case Type.BT_LONG: return NEG_LONG;
+ case Type.BT_FLOAT: return NEG_FLOAT;
+ case Type.BT_DOUBLE: return NEG_DOUBLE;
+ }
+
+ return throwBadType(type);
+ }
+
+ /**
+ * Returns the appropriate {@code not} rop for the given type. The
+ * result is a shared instance.
+ *
+ * @param type {@code non-null;} type of value being operated on
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opNot(TypeBearer type) {
+ switch (type.getBasicFrameType()) {
+ case Type.BT_INT: return NOT_INT;
+ case Type.BT_LONG: return NOT_LONG;
+ }
+
+ return throwBadType(type);
+ }
+
+ /**
+ * Returns the appropriate {@code cmpl} rop for the given type. The
+ * result is a shared instance.
+ *
+ * @param type {@code non-null;} type of value being compared
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opCmpl(TypeBearer type) {
+ switch (type.getBasicType()) {
+ case Type.BT_LONG: return CMPL_LONG;
+ case Type.BT_FLOAT: return CMPL_FLOAT;
+ case Type.BT_DOUBLE: return CMPL_DOUBLE;
+ }
+
+ return throwBadType(type);
+ }
+
+ /**
+ * Returns the appropriate {@code cmpg} rop for the given type. The
+ * result is a shared instance.
+ *
+ * @param type {@code non-null;} type of value being compared
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opCmpg(TypeBearer type) {
+ switch (type.getBasicType()) {
+ case Type.BT_FLOAT: return CMPG_FLOAT;
+ case Type.BT_DOUBLE: return CMPG_DOUBLE;
+ }
+
+ return throwBadType(type);
+ }
+
+ /**
+ * Returns the appropriate {@code conv} rop for the given types. The
+ * result is a shared instance.
+ *
+ * @param dest {@code non-null;} target value type
+ * @param source {@code non-null;} source value type
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opConv(TypeBearer dest, TypeBearer source) {
+ int dbt = dest.getBasicFrameType();
+ switch (source.getBasicFrameType()) {
+ case Type.BT_INT: {
+ switch (dbt) {
+ case Type.BT_LONG: return CONV_I2L;
+ case Type.BT_FLOAT: return CONV_I2F;
+ case Type.BT_DOUBLE: return CONV_I2D;
+ }
+ }
+ case Type.BT_LONG: {
+ switch (dbt) {
+ case Type.BT_INT: return CONV_L2I;
+ case Type.BT_FLOAT: return CONV_L2F;
+ case Type.BT_DOUBLE: return CONV_L2D;
+ }
+ }
+ case Type.BT_FLOAT: {
+ switch (dbt) {
+ case Type.BT_INT: return CONV_F2I;
+ case Type.BT_LONG: return CONV_F2L;
+ case Type.BT_DOUBLE: return CONV_F2D;
+ }
+ }
+ case Type.BT_DOUBLE: {
+ switch (dbt) {
+ case Type.BT_INT: return CONV_D2I;
+ case Type.BT_LONG: return CONV_D2L;
+ case Type.BT_FLOAT: return CONV_D2F;
+ }
+ }
+ }
+
+ return throwBadTypes(StdTypeList.make(dest.getType(),
+ source.getType()));
+ }
+
+ /**
+ * Returns the appropriate {@code return} rop for the given type. The
+ * result is a shared instance.
+ *
+ * @param type {@code non-null;} type of value being returned
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opReturn(TypeBearer type) {
+ switch (type.getBasicFrameType()) {
+ case Type.BT_INT: return RETURN_INT;
+ case Type.BT_LONG: return RETURN_LONG;
+ case Type.BT_FLOAT: return RETURN_FLOAT;
+ case Type.BT_DOUBLE: return RETURN_DOUBLE;
+ case Type.BT_OBJECT: return RETURN_OBJECT;
+ case Type.BT_VOID: return RETURN_VOID;
+ }
+
+ return throwBadType(type);
+ }
+
+ /**
+ * Returns the appropriate {@code aget} rop for the given type. The
+ * result is a shared instance.
+ *
+ * @param type {@code non-null;} element type of array being accessed
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opAget(TypeBearer type) {
+ switch (type.getBasicType()) {
+ case Type.BT_INT: return AGET_INT;
+ case Type.BT_LONG: return AGET_LONG;
+ case Type.BT_FLOAT: return AGET_FLOAT;
+ case Type.BT_DOUBLE: return AGET_DOUBLE;
+ case Type.BT_OBJECT: return AGET_OBJECT;
+ case Type.BT_BOOLEAN: return AGET_BOOLEAN;
+ case Type.BT_BYTE: return AGET_BYTE;
+ case Type.BT_CHAR: return AGET_CHAR;
+ case Type.BT_SHORT: return AGET_SHORT;
+ }
+
+ return throwBadType(type);
+ }
+
+ /**
+ * Returns the appropriate {@code aput} rop for the given type. The
+ * result is a shared instance.
+ *
+ * @param type {@code non-null;} element type of array being accessed
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opAput(TypeBearer type) {
+ switch (type.getBasicType()) {
+ case Type.BT_INT: return APUT_INT;
+ case Type.BT_LONG: return APUT_LONG;
+ case Type.BT_FLOAT: return APUT_FLOAT;
+ case Type.BT_DOUBLE: return APUT_DOUBLE;
+ case Type.BT_OBJECT: return APUT_OBJECT;
+ case Type.BT_BOOLEAN: return APUT_BOOLEAN;
+ case Type.BT_BYTE: return APUT_BYTE;
+ case Type.BT_CHAR: return APUT_CHAR;
+ case Type.BT_SHORT: return APUT_SHORT;
+ }
+
+ return throwBadType(type);
+ }
+
+ /**
+ * Returns the appropriate {@code new-array} rop for the given
+ * type. The result is a shared instance.
+ *
+ * @param arrayType {@code non-null;} array type of array being created
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opNewArray(TypeBearer arrayType) {
+ Type type = arrayType.getType();
+ Type elementType = type.getComponentType();
+
+ switch (elementType.getBasicType()) {
+ case Type.BT_INT: return NEW_ARRAY_INT;
+ case Type.BT_LONG: return NEW_ARRAY_LONG;
+ case Type.BT_FLOAT: return NEW_ARRAY_FLOAT;
+ case Type.BT_DOUBLE: return NEW_ARRAY_DOUBLE;
+ case Type.BT_BOOLEAN: return NEW_ARRAY_BOOLEAN;
+ case Type.BT_BYTE: return NEW_ARRAY_BYTE;
+ case Type.BT_CHAR: return NEW_ARRAY_CHAR;
+ case Type.BT_SHORT: return NEW_ARRAY_SHORT;
+ case Type.BT_OBJECT: {
+ return new Rop(RegOps.NEW_ARRAY, type, StdTypeList.INT,
+ Exceptions.LIST_Error_NegativeArraySizeException,
+ "new-array-object");
+ }
+ }
+
+ return throwBadType(type);
+ }
+
+ /**
+ * Returns the appropriate {@code filled-new-array} rop for the given
+ * type. The result may be a shared instance.
+ *
+ * @param arrayType {@code non-null;} type of array being created
+ * @param count {@code >= 0;} number of elements that the array should have
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opFilledNewArray(TypeBearer arrayType, int count) {
+ Type type = arrayType.getType();
+ Type elementType = type.getComponentType();
+
+ if (elementType.isCategory2()) {
+ return throwBadType(arrayType);
+ }
+
+ if (count < 0) {
+ throw new IllegalArgumentException("count < 0");
+ }
+
+ StdTypeList sourceTypes = new StdTypeList(count);
+
+ for (int i = 0; i < count; i++) {
+ sourceTypes.set(i, elementType);
+ }
+
+ // Note: The resulting rop is considered call-like.
+ return new Rop(RegOps.FILLED_NEW_ARRAY,
+ sourceTypes,
+ Exceptions.LIST_Error);
+ }
+
+ /**
+ * Returns the appropriate {@code get-field} rop for the given
+ * type. The result is a shared instance.
+ *
+ * @param type {@code non-null;} type of the field in question
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opGetField(TypeBearer type) {
+ switch (type.getBasicType()) {
+ case Type.BT_INT: return GET_FIELD_INT;
+ case Type.BT_LONG: return GET_FIELD_LONG;
+ case Type.BT_FLOAT: return GET_FIELD_FLOAT;
+ case Type.BT_DOUBLE: return GET_FIELD_DOUBLE;
+ case Type.BT_OBJECT: return GET_FIELD_OBJECT;
+ case Type.BT_BOOLEAN: return GET_FIELD_BOOLEAN;
+ case Type.BT_BYTE: return GET_FIELD_BYTE;
+ case Type.BT_CHAR: return GET_FIELD_CHAR;
+ case Type.BT_SHORT: return GET_FIELD_SHORT;
+ }
+
+ return throwBadType(type);
+ }
+
+ /**
+ * Returns the appropriate {@code put-field} rop for the given
+ * type. The result is a shared instance.
+ *
+ * @param type {@code non-null;} type of the field in question
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opPutField(TypeBearer type) {
+ switch (type.getBasicType()) {
+ case Type.BT_INT: return PUT_FIELD_INT;
+ case Type.BT_LONG: return PUT_FIELD_LONG;
+ case Type.BT_FLOAT: return PUT_FIELD_FLOAT;
+ case Type.BT_DOUBLE: return PUT_FIELD_DOUBLE;
+ case Type.BT_OBJECT: return PUT_FIELD_OBJECT;
+ case Type.BT_BOOLEAN: return PUT_FIELD_BOOLEAN;
+ case Type.BT_BYTE: return PUT_FIELD_BYTE;
+ case Type.BT_CHAR: return PUT_FIELD_CHAR;
+ case Type.BT_SHORT: return PUT_FIELD_SHORT;
+ }
+
+ return throwBadType(type);
+ }
+
+ /**
+ * Returns the appropriate {@code get-static} rop for the given
+ * type. The result is a shared instance.
+ *
+ * @param type {@code non-null;} type of the field in question
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opGetStatic(TypeBearer type) {
+ switch (type.getBasicType()) {
+ case Type.BT_INT: return GET_STATIC_INT;
+ case Type.BT_LONG: return GET_STATIC_LONG;
+ case Type.BT_FLOAT: return GET_STATIC_FLOAT;
+ case Type.BT_DOUBLE: return GET_STATIC_DOUBLE;
+ case Type.BT_OBJECT: return GET_STATIC_OBJECT;
+ case Type.BT_BOOLEAN: return GET_STATIC_BOOLEAN;
+ case Type.BT_BYTE: return GET_STATIC_BYTE;
+ case Type.BT_CHAR: return GET_STATIC_CHAR;
+ case Type.BT_SHORT: return GET_STATIC_SHORT;
+ }
+
+ return throwBadType(type);
+ }
+
+ /**
+ * Returns the appropriate {@code put-static} rop for the given
+ * type. The result is a shared instance.
+ *
+ * @param type {@code non-null;} type of the field in question
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opPutStatic(TypeBearer type) {
+ switch (type.getBasicType()) {
+ case Type.BT_INT: return PUT_STATIC_INT;
+ case Type.BT_LONG: return PUT_STATIC_LONG;
+ case Type.BT_FLOAT: return PUT_STATIC_FLOAT;
+ case Type.BT_DOUBLE: return PUT_STATIC_DOUBLE;
+ case Type.BT_OBJECT: return PUT_STATIC_OBJECT;
+ case Type.BT_BOOLEAN: return PUT_STATIC_BOOLEAN;
+ case Type.BT_BYTE: return PUT_STATIC_BYTE;
+ case Type.BT_CHAR: return PUT_STATIC_CHAR;
+ case Type.BT_SHORT: return PUT_STATIC_SHORT;
+ }
+
+ return throwBadType(type);
+ }
+
+ /**
+ * Returns the appropriate {@code invoke-static} rop for the
+ * given type. The result is typically a newly-allocated instance.
+ *
+ * @param meth {@code non-null;} descriptor of the method
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opInvokeStatic(Prototype meth) {
+ return new Rop(RegOps.INVOKE_STATIC,
+ meth.getParameterFrameTypes(),
+ StdTypeList.THROWABLE);
+ }
+
+ /**
+ * Returns the appropriate {@code invoke-virtual} rop for the
+ * given type. The result is typically a newly-allocated instance.
+ *
+ * @param meth {@code non-null;} descriptor of the method, including the
+ * {@code this} parameter
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opInvokeVirtual(Prototype meth) {
+ return new Rop(RegOps.INVOKE_VIRTUAL,
+ meth.getParameterFrameTypes(),
+ StdTypeList.THROWABLE);
+ }
+
+ /**
+ * Returns the appropriate {@code invoke-super} rop for the
+ * given type. The result is typically a newly-allocated instance.
+ *
+ * @param meth {@code non-null;} descriptor of the method, including the
+ * {@code this} parameter
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opInvokeSuper(Prototype meth) {
+ return new Rop(RegOps.INVOKE_SUPER,
+ meth.getParameterFrameTypes(),
+ StdTypeList.THROWABLE);
+ }
+
+ /**
+ * Returns the appropriate {@code invoke-direct} rop for the
+ * given type. The result is typically a newly-allocated instance.
+ *
+ * @param meth {@code non-null;} descriptor of the method, including the
+ * {@code this} parameter
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opInvokeDirect(Prototype meth) {
+ return new Rop(RegOps.INVOKE_DIRECT,
+ meth.getParameterFrameTypes(),
+ StdTypeList.THROWABLE);
+ }
+
+ /**
+ * Returns the appropriate {@code invoke-interface} rop for the
+ * given type. The result is typically a newly-allocated instance.
+ *
+ * @param meth {@code non-null;} descriptor of the method, including the
+ * {@code this} parameter
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opInvokeInterface(Prototype meth) {
+ return new Rop(RegOps.INVOKE_INTERFACE,
+ meth.getParameterFrameTypes(),
+ StdTypeList.THROWABLE);
+ }
+
+ /**
+ * Returns the appropriate {@code mark-local} rop for the given type.
+ * The result is a shared instance.
+ *
+ * @param type {@code non-null;} type of value being marked
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static Rop opMarkLocal(TypeBearer type) {
+ switch (type.getBasicFrameType()) {
+ case Type.BT_INT: return MARK_LOCAL_INT;
+ case Type.BT_LONG: return MARK_LOCAL_LONG;
+ case Type.BT_FLOAT: return MARK_LOCAL_FLOAT;
+ case Type.BT_DOUBLE: return MARK_LOCAL_DOUBLE;
+ case Type.BT_OBJECT: return MARK_LOCAL_OBJECT;
+ }
+
+ return throwBadType(type);
+ }
+
+ /**
+ * This class is uninstantiable.
+ */
+ private Rops() {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Throws the right exception to complain about a bogus type.
+ *
+ * @param type {@code non-null;} the bad type
+ * @return never
+ */
+ private static Rop throwBadType(TypeBearer type) {
+ throw new IllegalArgumentException("bad type: " + type);
+ }
+
+ /**
+ * Throws the right exception to complain about a bogus list of types.
+ *
+ * @param types {@code non-null;} the bad types
+ * @return never
+ */
+ private static Rop throwBadTypes(TypeList types) {
+ throw new IllegalArgumentException("bad types: " + types);
+ }
+}
diff --git a/dx/src/com/android/dx/rop/code/SourcePosition.java b/dx/src/com/android/dx/rop/code/SourcePosition.java
new file mode 100644
index 0000000..f7a7961
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/SourcePosition.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.util.Hex;
+
+/**
+ * Information about a source position for code, which includes both a
+ * line number and original bytecode address.
+ */
+public final class SourcePosition {
+ /** {@code non-null;} convenient "no information known" instance */
+ public static final SourcePosition NO_INFO =
+ new SourcePosition(null, -1, -1);
+
+ /** {@code null-ok;} name of the file of origin or {@code null} if unknown */
+ private final CstUtf8 sourceFile;
+
+ /**
+ * {@code >= -1;} the bytecode address, or {@code -1} if that
+ * information is unknown
+ */
+ private final int address;
+
+ /**
+ * {@code >= -1;} the line number, or {@code -1} if that
+ * information is unknown
+ */
+ private final int line;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param sourceFile {@code null-ok;} name of the file of origin or
+ * {@code null} if unknown
+ * @param address {@code >= -1;} original bytecode address or {@code -1}
+ * if unknown
+ * @param line {@code >= -1;} original line number or {@code -1} if
+ * unknown
+ */
+ public SourcePosition(CstUtf8 sourceFile, int address, int line) {
+ if (address < -1) {
+ throw new IllegalArgumentException("address < -1");
+ }
+
+ if (line < -1) {
+ throw new IllegalArgumentException("line < -1");
+ }
+
+ this.sourceFile = sourceFile;
+ this.address = address;
+ this.line = line;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer(50);
+
+ if (sourceFile != null) {
+ sb.append(sourceFile.toHuman());
+ sb.append(":");
+ }
+
+ if (line >= 0) {
+ sb.append(line);
+ }
+
+ sb.append('@');
+
+ if (address < 0) {
+ sb.append("????");
+ } else {
+ sb.append(Hex.u2(address));
+ }
+
+ return sb.toString();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof SourcePosition)) {
+ return false;
+ }
+
+ if (this == other) {
+ return true;
+ }
+
+ SourcePosition pos = (SourcePosition) other;
+
+ return (address == pos.address) && sameLineAndFile(pos);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return sourceFile.hashCode() + address + line;
+ }
+
+ /**
+ * Returns whether the lines match between this instance and
+ * the one given.
+ *
+ * @param other {@code non-null;} the instance to compare to
+ * @return {@code true} iff the lines match
+ */
+ public boolean sameLine(SourcePosition other) {
+ return (line == other.line);
+ }
+
+ /**
+ * Returns whether the lines and files match between this instance and
+ * the one given.
+ *
+ * @param other {@code non-null;} the instance to compare to
+ * @return {@code true} iff the lines and files match
+ */
+ public boolean sameLineAndFile(SourcePosition other) {
+ return (line == other.line) &&
+ ((sourceFile == other.sourceFile) ||
+ ((sourceFile != null) && sourceFile.equals(other.sourceFile)));
+ }
+
+ /**
+ * Gets the source file, if known.
+ *
+ * @return {@code null-ok;} the source file or {@code null} if unknown
+ */
+ public CstUtf8 getSourceFile() {
+ return sourceFile;
+ }
+
+ /**
+ * Gets the original bytecode address.
+ *
+ * @return {@code >= -1;} the address or {@code -1} if unknown
+ */
+ public int getAddress() {
+ return address;
+ }
+
+ /**
+ * Gets the original line number.
+ *
+ * @return {@code >= -1;} the original line number or {@code -1} if
+ * unknown
+ */
+ public int getLine() {
+ return line;
+ }
+}
diff --git a/dx/src/com/android/dx/rop/code/SwitchInsn.java b/dx/src/com/android/dx/rop/code/SwitchInsn.java
new file mode 100644
index 0000000..31bb94d
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/SwitchInsn.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+import com.android.dx.util.IntList;
+
+/**
+ * Instruction which contains switch cases.
+ */
+public final class SwitchInsn
+ extends Insn {
+ /** {@code non-null;} list of switch cases */
+ private final IntList cases;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param opcode {@code non-null;} the opcode
+ * @param position {@code non-null;} source position
+ * @param result {@code null-ok;} spec for the result, if any
+ * @param sources {@code non-null;} specs for all the sources
+ * @param cases {@code non-null;} list of switch cases
+ */
+ public SwitchInsn(Rop opcode, SourcePosition position, RegisterSpec result,
+ RegisterSpecList sources, IntList cases) {
+ super(opcode, position, result, sources);
+
+ if (opcode.getBranchingness() != Rop.BRANCH_SWITCH) {
+ throw new IllegalArgumentException("bogus branchingness");
+ }
+
+ if (cases == null) {
+ throw new NullPointerException("cases == null");
+ }
+
+ this.cases = cases;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getInlineString() {
+ return cases.toString();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public TypeList getCatches() {
+ return StdTypeList.EMPTY;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void accept(Visitor visitor) {
+ visitor.visitSwitchInsn(this);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Insn withAddedCatch(Type type) {
+ throw new UnsupportedOperationException("unsupported");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Insn withRegisterOffset(int delta) {
+ return new SwitchInsn(getOpcode(), getPosition(),
+ getResult().withOffset(delta),
+ getSources().withOffset(delta),
+ cases);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p> SwitchInsn always compares false. The current use for this method
+ * never encounters {@code SwitchInsn}s
+ */
+ @Override
+ public boolean contentEquals(Insn b) {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Insn withNewRegisters(RegisterSpec result,
+ RegisterSpecList sources) {
+
+ return new SwitchInsn(getOpcode(), getPosition(),
+ result,
+ sources,
+ cases);
+ }
+
+ /**
+ * Gets the list of switch cases.
+ *
+ * @return {@code non-null;} the case list
+ */
+ public IntList getCases() {
+ return cases;
+ }
+}
diff --git a/dx/src/com/android/dx/rop/code/ThrowingCstInsn.java b/dx/src/com/android/dx/rop/code/ThrowingCstInsn.java
new file mode 100644
index 0000000..cdd21d1
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/ThrowingCstInsn.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+
+/**
+ * Instruction which contains an explicit reference to a constant
+ * and which might throw an exception.
+ */
+public final class ThrowingCstInsn
+ extends CstInsn {
+ /** {@code non-null;} list of exceptions caught */
+ private final TypeList catches;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param opcode {@code non-null;} the opcode
+ * @param position {@code non-null;} source position
+ * @param sources {@code non-null;} specs for all the sources
+ * @param catches {@code non-null;} list of exceptions caught
+ * @param cst {@code non-null;} the constant
+ */
+ public ThrowingCstInsn(Rop opcode, SourcePosition position,
+ RegisterSpecList sources,
+ TypeList catches, Constant cst) {
+ super(opcode, position, null, sources, cst);
+
+ if (opcode.getBranchingness() != Rop.BRANCH_THROW) {
+ throw new IllegalArgumentException("bogus branchingness");
+ }
+
+ if (catches == null) {
+ throw new NullPointerException("catches == null");
+ }
+
+ this.catches = catches;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getInlineString() {
+ return getConstant().toHuman() + " " +
+ ThrowingInsn.toCatchString(catches);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public TypeList getCatches() {
+ return catches;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void accept(Visitor visitor) {
+ visitor.visitThrowingCstInsn(this);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Insn withAddedCatch(Type type) {
+ return new ThrowingCstInsn(getOpcode(), getPosition(),
+ getSources(), catches.withAddedType(type),
+ getConstant());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Insn withRegisterOffset(int delta) {
+ return new ThrowingCstInsn(getOpcode(), getPosition(),
+ getSources().withOffset(delta),
+ catches,
+ getConstant());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Insn withNewRegisters(RegisterSpec result,
+ RegisterSpecList sources) {
+
+ return new ThrowingCstInsn(getOpcode(), getPosition(),
+ sources,
+ catches,
+ getConstant());
+ }
+
+
+}
diff --git a/dx/src/com/android/dx/rop/code/ThrowingInsn.java b/dx/src/com/android/dx/rop/code/ThrowingInsn.java
new file mode 100644
index 0000000..6561d41
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/ThrowingInsn.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeList;
+
+/**
+ * Instruction which possibly throws. The {@code successors} list in the
+ * basic block an instance of this class is inside corresponds in-order to
+ * the list of exceptions handled by this instruction, with the
+ * no-exception case appended as the final target.
+ */
+public final class ThrowingInsn
+ extends Insn {
+ /** {@code non-null;} list of exceptions caught */
+ private final TypeList catches;
+
+ /**
+ * Gets the string form of a register spec list to be used as a catches
+ * list.
+ *
+ * @param catches {@code non-null;} the catches list
+ * @return {@code non-null;} the string form
+ */
+ public static String toCatchString(TypeList catches) {
+ StringBuffer sb = new StringBuffer(100);
+
+ sb.append("catch");
+
+ int sz = catches.size();
+ for (int i = 0; i < sz; i++) {
+ sb.append(" ");
+ sb.append(catches.getType(i).toHuman());
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param opcode {@code non-null;} the opcode
+ * @param position {@code non-null;} source position
+ * @param sources {@code non-null;} specs for all the sources
+ * @param catches {@code non-null;} list of exceptions caught
+ */
+ public ThrowingInsn(Rop opcode, SourcePosition position,
+ RegisterSpecList sources,
+ TypeList catches) {
+ super(opcode, position, null, sources);
+
+ if (opcode.getBranchingness() != Rop.BRANCH_THROW) {
+ throw new IllegalArgumentException("bogus branchingness");
+ }
+
+ if (catches == null) {
+ throw new NullPointerException("catches == null");
+ }
+
+ this.catches = catches;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getInlineString() {
+ return toCatchString(catches);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public TypeList getCatches() {
+ return catches;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void accept(Visitor visitor) {
+ visitor.visitThrowingInsn(this);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Insn withAddedCatch(Type type) {
+ return new ThrowingInsn(getOpcode(), getPosition(),
+ getSources(), catches.withAddedType(type));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Insn withRegisterOffset(int delta) {
+ return new ThrowingInsn(getOpcode(), getPosition(),
+ getSources().withOffset(delta),
+ catches);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Insn withNewRegisters(RegisterSpec result,
+ RegisterSpecList sources) {
+
+ return new ThrowingInsn(getOpcode(), getPosition(),
+ sources,
+ catches);
+ }
+}
diff --git a/dx/src/com/android/dx/rop/code/TranslationAdvice.java b/dx/src/com/android/dx/rop/code/TranslationAdvice.java
new file mode 100644
index 0000000..832d84d
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/TranslationAdvice.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.code;
+
+/**
+ * Interface for "advice" passed from the late stage of translation back
+ * to the early stage. This allows for the final target architecture to
+ * exert its influence early in the translation process without having
+ * the early stage code be explicitly tied to the target.
+ */
+public interface TranslationAdvice {
+ /**
+ * Returns an indication of whether the target can directly represent an
+ * instruction with the given opcode operating on the given arguments,
+ * where the last source argument is used as a constant. (That is, the
+ * last argument must have a type which indicates it is a known constant.)
+ * The instruction associated must have exactly two sources.
+ *
+ * @param opcode {@code non-null;} the opcode
+ * @param sourceA {@code non-null;} the first source
+ * @param sourceB {@code non-null;} the second source
+ * @return {@code true} iff the target can represent the operation
+ * using a constant for the last argument
+ */
+ public boolean hasConstantOperation(Rop opcode,
+ RegisterSpec sourceA, RegisterSpec sourceB);
+
+ /**
+ * Returns true if the translation target requires the sources of the
+ * specified opcode to be in order and contiguous (eg, for an invoke-range)
+ *
+ * @param opcode {@code non-null;} opcode
+ * @param sources {@code non-null;} source list
+ * @return {@code true} iff the target requires the sources to be
+ * in order and contiguous.
+ */
+ public boolean requiresSourcesInOrder(Rop opcode, RegisterSpecList sources);
+
+ /**
+ * Gets the maximum register width that can be represented optimally.
+ * For example, Dex bytecode does not have instruction forms that take
+ * register numbers larger than 15 for all instructions so
+ * DexTranslationAdvice returns 15 here.
+ *
+ * @return register count noted above
+ */
+ public int getMaxOptimalRegisterCount();
+}
diff --git a/dx/src/com/android/dx/rop/code/package.html b/dx/src/com/android/dx/rop/code/package.html
new file mode 100644
index 0000000..86566b4
--- /dev/null
+++ b/dx/src/com/android/dx/rop/code/package.html
@@ -0,0 +1,8 @@
+<body>
+<p>Classes relating to a register-based opcode system.</p>
+
+<p><b>PACKAGES USED:</b>
+<ul>
+<li><code>com.android.dx.util</code></li>
+</ul>
+</body>
diff --git a/dx/src/com/android/dx/rop/cst/Constant.java b/dx/src/com/android/dx/rop/cst/Constant.java
new file mode 100644
index 0000000..3ef035e
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/Constant.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.util.ToHuman;
+
+/**
+ * Base class for constants of all sorts.
+ */
+public abstract class Constant
+ implements ToHuman, Comparable<Constant> {
+ /**
+ * Returns {@code true} if this instance is a category-2 constant,
+ * meaning it takes up two slots in the constant pool, or
+ * {@code false} if this instance is category-1.
+ *
+ * @return {@code true} iff this instance is category-2
+ */
+ public abstract boolean isCategory2();
+
+ /**
+ * Returns the human name for the particular type of constant
+ * this instance is.
+ *
+ * @return {@code non-null;} the name
+ */
+ public abstract String typeName();
+
+ /**
+ * {@inheritDoc}
+ *
+ * This compares in class-major and value-minor order.
+ */
+ public final int compareTo(Constant other) {
+ Class clazz = getClass();
+ Class otherClazz = other.getClass();
+
+ if (clazz != otherClazz) {
+ return clazz.getName().compareTo(otherClazz.getName());
+ }
+
+ return compareTo0(other);
+ }
+
+ /**
+ * Compare the values of this and another instance, which are guaranteed
+ * to be of the same class. Subclasses must implement this.
+ *
+ * @param other {@code non-null;} the instance to compare to
+ * @return {@code -1}, {@code 0}, or {@code 1}, as usual
+ * for a comparison
+ */
+ protected abstract int compareTo0(Constant other);
+}
diff --git a/dx/src/com/android/dx/rop/cst/ConstantPool.java b/dx/src/com/android/dx/rop/cst/ConstantPool.java
new file mode 100644
index 0000000..efc394d
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/ConstantPool.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+/**
+ * Interface for constant pools, which are, more or less, just lists of
+ * {@link Constant} objects.
+ */
+public interface ConstantPool {
+ /**
+ * Get the "size" of the constant pool. This corresponds to the
+ * class file field {@code constant_pool_count}, and is in fact
+ * always at least one more than the actual size of the constant pool,
+ * as element {@code 0} is always invalid.
+ *
+ * @return {@code >= 1;} the size
+ */
+ public int size();
+
+ /**
+ * Get the {@code n}th entry in the constant pool, which must
+ * be valid.
+ *
+ * @param n {@code n >= 0, n < size();} the constant pool index
+ * @return {@code non-null;} the corresponding entry
+ * @throws IllegalArgumentException thrown if {@code n} is
+ * in-range but invalid
+ */
+ public Constant get(int n);
+
+ /**
+ * Get the {@code n}th entry in the constant pool, which must
+ * be valid unless {@code n == 0}, in which case {@code null}
+ * is returned.
+ *
+ * @param n {@code n >= 0, n < size();} the constant pool index
+ * @return {@code null-ok;} the corresponding entry, if {@code n != 0}
+ * @throws IllegalArgumentException thrown if {@code n} is
+ * in-range and non-zero but invalid
+ */
+ public Constant get0Ok(int n);
+
+ /**
+ * Get the {@code n}th entry in the constant pool, or
+ * {@code null} if the index is in-range but invalid. In
+ * particular, {@code null} is returned for index {@code 0}
+ * as well as the index after any entry which is defined to take up
+ * two slots (that is, {@code Long} and {@code Double}
+ * entries).
+ *
+ * @param n {@code n >= 0, n < size();} the constant pool index
+ * @return {@code null-ok;} the corresponding entry, or {@code null} if
+ * the index is in-range but invalid
+ */
+ public Constant getOrNull(int n);
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstAnnotation.java b/dx/src/com/android/dx/rop/cst/CstAnnotation.java
new file mode 100644
index 0000000..8cdf1df
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstAnnotation.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.annotation.Annotation;
+
+/**
+ * Constant type that represents an annotation.
+ */
+public final class CstAnnotation extends Constant {
+ /** {@code non-null;} the actual annotation */
+ private final Annotation annotation;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param annotation {@code non-null;} the annotation to hold
+ */
+ public CstAnnotation(Annotation annotation) {
+ if (annotation == null) {
+ throw new NullPointerException("annotation == null");
+ }
+
+ annotation.throwIfMutable();
+
+ this.annotation = annotation;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+ if (! (other instanceof CstAnnotation)) {
+ return false;
+ }
+
+ return annotation.equals(((CstAnnotation) other).annotation);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return annotation.hashCode();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected int compareTo0(Constant other) {
+ return annotation.compareTo(((CstAnnotation) other).annotation);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return annotation.toString();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String typeName() {
+ return "annotation";
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCategory2() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public String toHuman() {
+ return annotation.toString();
+ }
+
+ /**
+ * Get the underlying annotation.
+ *
+ * @return {@code non-null;} the annotation
+ */
+ public Annotation getAnnotation() {
+ return annotation;
+ }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstArray.java b/dx/src/com/android/dx/rop/cst/CstArray.java
new file mode 100644
index 0000000..cb7d54d
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstArray.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * Constant type to represent a fixed array of other constants. The contents
+ * may be of any type <i>other</i> than {@link CstUtf8}.
+ */
+public final class CstArray extends Constant {
+ /** {@code non-null;} the actual list of contents */
+ private final List list;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param list {@code non-null;} the actual list of contents
+ */
+ public CstArray(List list) {
+ if (list == null) {
+ throw new NullPointerException("list == null");
+ }
+
+ list.throwIfMutable();
+
+ this.list = list;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+ if (! (other instanceof CstArray)) {
+ return false;
+ }
+
+ return list.equals(((CstArray) other).list);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return list.hashCode();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected int compareTo0(Constant other) {
+ return list.compareTo(((CstArray) other).list);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return list.toString("array{", ", ", "}");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String typeName() {
+ return "array";
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCategory2() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public String toHuman() {
+ return list.toHuman("{", ", ", "}");
+ }
+
+ /**
+ * Get the underlying list.
+ *
+ * @return {@code non-null;} the list
+ */
+ public List getList() {
+ return list;
+ }
+
+ /**
+ * List of {@link Constant} instances.
+ */
+ public static final class List
+ extends FixedSizeList implements Comparable<List> {
+ /**
+ * Constructs an instance. All indices initially contain
+ * {@code null}.
+ *
+ * @param size the size of the list
+ */
+ public List(int size) {
+ super(size);
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(List other) {
+ int thisSize = size();
+ int otherSize = other.size();
+ int compareSize = (thisSize < otherSize) ? thisSize : otherSize;
+
+ for (int i = 0; i < compareSize; i++) {
+ Constant thisItem = (Constant) get0(i);
+ Constant otherItem = (Constant) other.get0(i);
+ int compare = thisItem.compareTo(otherItem);
+ if (compare != 0) {
+ return compare;
+ }
+ }
+
+ if (thisSize < otherSize) {
+ return -1;
+ } else if (thisSize > otherSize) {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ /**
+ * Gets the element at the given index. It is an error to call
+ * this with the index for an element which was never set; if you
+ * do that, this will throw {@code NullPointerException}.
+ *
+ * @param n {@code >= 0, < size();} which index
+ * @return {@code non-null;} element at that index
+ */
+ public Constant get(int n) {
+ return (Constant) get0(n);
+ }
+
+ /**
+ * Sets the element at the given index.
+ *
+ * @param n {@code >= 0, < size();} which index
+ * @param a {@code null-ok;} the element to set at {@code n}
+ */
+ public void set(int n, Constant a) {
+ if (a instanceof CstUtf8) {
+ throw new IllegalArgumentException("bad value: " + a);
+ }
+
+ set0(n, a);
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstBaseMethodRef.java b/dx/src/com/android/dx/rop/cst/CstBaseMethodRef.java
new file mode 100644
index 0000000..5b0aeb6
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstBaseMethodRef.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Prototype;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+
+/**
+ * Base class for constants of "methodish" type.
+ *
+ * <p><b>Note:</b> As a {@link TypeBearer}, this class bears the return type
+ * of the method.</p>
+ */
+public abstract class CstBaseMethodRef
+ extends CstMemberRef {
+ /** {@code non-null;} the raw prototype for this method */
+ private final Prototype prototype;
+
+ /**
+ * {@code null-ok;} the prototype for this method taken to be an instance
+ * method, or {@code null} if not yet calculated
+ */
+ private Prototype instancePrototype;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param definingClass {@code non-null;} the type of the defining class
+ * @param nat {@code non-null;} the name-and-type
+ */
+ /*package*/ CstBaseMethodRef(CstType definingClass, CstNat nat) {
+ super(definingClass, nat);
+
+ String descriptor = getNat().getDescriptor().getString();
+ this.prototype = Prototype.intern(descriptor);
+ this.instancePrototype = null;
+ }
+
+ /**
+ * Gets the raw prototype of this method. This doesn't include a
+ * {@code this} argument.
+ *
+ * @return {@code non-null;} the method prototype
+ */
+ public final Prototype getPrototype() {
+ return prototype;
+ }
+
+ /**
+ * Gets the prototype of this method as either a
+ * {@code static} or instance method. In the case of a
+ * {@code static} method, this is the same as the raw
+ * prototype. In the case of an instance method, this has an
+ * appropriately-typed {@code this} argument as the first
+ * one.
+ *
+ * @param isStatic whether the method should be considered static
+ * @return {@code non-null;} the method prototype
+ */
+ public final Prototype getPrototype(boolean isStatic) {
+ if (isStatic) {
+ return prototype;
+ } else {
+ if (instancePrototype == null) {
+ Type thisType = getDefiningClass().getClassType();
+ instancePrototype = prototype.withFirstParameter(thisType);
+ }
+ return instancePrototype;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected final int compareTo0(Constant other) {
+ int cmp = super.compareTo0(other);
+
+ if (cmp != 0) {
+ return cmp;
+ }
+
+ CstBaseMethodRef otherMethod = (CstBaseMethodRef) other;
+ return prototype.compareTo(otherMethod.prototype);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * In this case, this method returns the <i>return type</i> of this method.
+ *
+ * @return {@code non-null;} the method's return type
+ */
+ public final Type getType() {
+ return prototype.getReturnType();
+ }
+
+ /**
+ * Gets the number of words of parameters required by this
+ * method's descriptor. Since instances of this class have no way
+ * to know if they will be used in a {@code static} or
+ * instance context, one has to indicate this explicitly as an
+ * argument. This method is just a convenient shorthand for
+ * {@code getPrototype().getParameterTypes().getWordCount()},
+ * plus {@code 1} if the method is to be treated as an
+ * instance method.
+ *
+ * @param isStatic whether the method should be considered static
+ * @return {@code >= 0;} the argument word count
+ */
+ public final int getParameterWordCount(boolean isStatic) {
+ return getPrototype(isStatic).getParameterTypes().getWordCount();
+ }
+
+ /**
+ * Gets whether this is a reference to an instance initialization
+ * method. This is just a convenient shorthand for
+ * {@code getNat().isInstanceInit()}.
+ *
+ * @return {@code true} iff this is a reference to an
+ * instance initialization method
+ */
+ public final boolean isInstanceInit() {
+ return getNat().isInstanceInit();
+ }
+
+ /**
+ * Gets whether this is a reference to a class initialization
+ * method. This is just a convenient shorthand for
+ * {@code getNat().isClassInit()}.
+ *
+ * @return {@code true} iff this is a reference to an
+ * instance initialization method
+ */
+ public final boolean isClassInit() {
+ return getNat().isClassInit();
+ }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstBoolean.java b/dx/src/com/android/dx/rop/cst/CstBoolean.java
new file mode 100644
index 0000000..5ff858a
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstBoolean.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+
+/**
+ * Constants of type {@code boolean}.
+ */
+public final class CstBoolean
+ extends CstLiteral32 {
+ /** {@code non-null;} instance representing {@code false} */
+ public static final CstBoolean VALUE_FALSE = new CstBoolean(false);
+
+ /** {@code non-null;} instance representing {@code true} */
+ public static final CstBoolean VALUE_TRUE = new CstBoolean(true);
+
+ /**
+ * Makes an instance for the given value. This will return an
+ * already-allocated instance.
+ *
+ * @param value the {@code boolean} value
+ * @return {@code non-null;} the appropriate instance
+ */
+ public static CstBoolean make(boolean value) {
+ return value ? VALUE_TRUE : VALUE_FALSE;
+ }
+
+ /**
+ * Makes an instance for the given {@code int} value. This
+ * will return an already-allocated instance.
+ *
+ * @param value must be either {@code 0} or {@code 1}
+ * @return {@code non-null;} the appropriate instance
+ */
+ public static CstBoolean make(int value) {
+ if (value == 0) {
+ return VALUE_FALSE;
+ } else if (value == 1) {
+ return VALUE_TRUE;
+ } else {
+ throw new IllegalArgumentException("bogus value: " + value);
+ }
+ }
+
+ /**
+ * Constructs an instance. This constructor is private; use {@link #make}.
+ *
+ * @param value the {@code boolean} value
+ */
+ private CstBoolean(boolean value) {
+ super(value ? 1 : 0);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return getValue() ? "boolean{true}" : "boolean{false}";
+ }
+
+ /** {@inheritDoc} */
+ public Type getType() {
+ return Type.BOOLEAN;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String typeName() {
+ return "boolean";
+ }
+
+ /** {@inheritDoc} */
+ public String toHuman() {
+ return getValue() ? "true" : "false";
+ }
+
+ /**
+ * Gets the {@code boolean} value.
+ *
+ * @return the value
+ */
+ public boolean getValue() {
+ return (getIntBits() == 0) ? false : true;
+ }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstByte.java b/dx/src/com/android/dx/rop/cst/CstByte.java
new file mode 100644
index 0000000..fc8f58f
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstByte.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.Hex;
+
+/**
+ * Constants of type {@code byte}.
+ */
+public final class CstByte
+ extends CstLiteral32 {
+ /** {@code non-null;} the value {@code 0} as an instance of this class */
+ public static final CstByte VALUE_0 = make((byte) 0);
+
+ /**
+ * Makes an instance for the given value. This may (but does not
+ * necessarily) return an already-allocated instance.
+ *
+ * @param value the {@code byte} value
+ */
+ public static CstByte make(byte value) {
+ return new CstByte(value);
+ }
+
+ /**
+ * Makes an instance for the given {@code int} value. This
+ * may (but does not necessarily) return an already-allocated
+ * instance.
+ *
+ * @param value the value, which must be in range for a {@code byte}
+ * @return {@code non-null;} the appropriate instance
+ */
+ public static CstByte make(int value) {
+ byte cast = (byte) value;
+
+ if (cast != value) {
+ throw new IllegalArgumentException("bogus byte value: " +
+ value);
+ }
+
+ return make(cast);
+ }
+
+ /**
+ * Constructs an instance. This constructor is private; use {@link #make}.
+ *
+ * @param value the {@code byte} value
+ */
+ private CstByte(byte value) {
+ super(value);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ int value = getIntBits();
+ return "byte{0x" + Hex.u1(value) + " / " + value + '}';
+ }
+
+ /** {@inheritDoc} */
+ public Type getType() {
+ return Type.BYTE;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String typeName() {
+ return "byte";
+ }
+
+ /** {@inheritDoc} */
+ public String toHuman() {
+ return Integer.toString(getIntBits());
+ }
+
+ /**
+ * Gets the {@code byte} value.
+ *
+ * @return the value
+ */
+ public byte getValue() {
+ return (byte) getIntBits();
+ }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstChar.java b/dx/src/com/android/dx/rop/cst/CstChar.java
new file mode 100644
index 0000000..21d8b67
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstChar.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.Hex;
+
+/**
+ * Constants of type {@code char}.
+ */
+public final class CstChar
+ extends CstLiteral32 {
+ /** {@code non-null;} the value {@code 0} as an instance of this class */
+ public static final CstChar VALUE_0 = make((char) 0);
+
+ /**
+ * Makes an instance for the given value. This may (but does not
+ * necessarily) return an already-allocated instance.
+ *
+ * @param value the {@code char} value
+ */
+ public static CstChar make(char value) {
+ return new CstChar(value);
+ }
+
+ /**
+ * Makes an instance for the given {@code int} value. This
+ * may (but does not necessarily) return an already-allocated
+ * instance.
+ *
+ * @param value the value, which must be in range for a {@code char}
+ * @return {@code non-null;} the appropriate instance
+ */
+ public static CstChar make(int value) {
+ char cast = (char) value;
+
+ if (cast != value) {
+ throw new IllegalArgumentException("bogus char value: " +
+ value);
+ }
+
+ return make(cast);
+ }
+
+ /**
+ * Constructs an instance. This constructor is private; use {@link #make}.
+ *
+ * @param value the {@code char} value
+ */
+ private CstChar(char value) {
+ super(value);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ int value = getIntBits();
+ return "char{0x" + Hex.u2(value) + " / " + value + '}';
+ }
+
+ /** {@inheritDoc} */
+ public Type getType() {
+ return Type.CHAR;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String typeName() {
+ return "char";
+ }
+
+ /** {@inheritDoc} */
+ public String toHuman() {
+ return Integer.toString(getIntBits());
+ }
+
+ /**
+ * Gets the {@code char} value.
+ *
+ * @return the value
+ */
+ public char getValue() {
+ return (char) getIntBits();
+ }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstDouble.java b/dx/src/com/android/dx/rop/cst/CstDouble.java
new file mode 100644
index 0000000..8f1766f
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstDouble.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.Hex;
+
+/**
+ * Constants of type {@code CONSTANT_Double_info}.
+ */
+public final class CstDouble
+ extends CstLiteral64 {
+ /** {@code non-null;} instance representing {@code 0} */
+ public static final CstDouble VALUE_0 =
+ new CstDouble(Double.doubleToLongBits(0.0));
+
+ /** {@code non-null;} instance representing {@code 1} */
+ public static final CstDouble VALUE_1 =
+ new CstDouble(Double.doubleToLongBits(1.0));
+
+ /**
+ * Makes an instance for the given value. This may (but does not
+ * necessarily) return an already-allocated instance.
+ *
+ * @param bits the {@code double} value as {@code long} bits
+ */
+ public static CstDouble make(long bits) {
+ /*
+ * Note: Javadoc notwithstanding, this implementation always
+ * allocates.
+ */
+ return new CstDouble(bits);
+ }
+
+ /**
+ * Constructs an instance. This constructor is private; use {@link #make}.
+ *
+ * @param bits the {@code double} value as {@code long} bits
+ */
+ private CstDouble(long bits) {
+ super(bits);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ long bits = getLongBits();
+ return "double{0x" + Hex.u8(bits) + " / " +
+ Double.longBitsToDouble(bits) + '}';
+ }
+
+ /** {@inheritDoc} */
+ public Type getType() {
+ return Type.DOUBLE;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String typeName() {
+ return "double";
+ }
+
+ /** {@inheritDoc} */
+ public String toHuman() {
+ return Double.toString(Double.longBitsToDouble(getLongBits()));
+ }
+
+ /**
+ * Gets the {@code double} value.
+ *
+ * @return the value
+ */
+ public double getValue() {
+ return Double.longBitsToDouble(getLongBits());
+ }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstEnumRef.java b/dx/src/com/android/dx/rop/cst/CstEnumRef.java
new file mode 100644
index 0000000..641ab3f
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstEnumRef.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+
+/**
+ * Constant type to represent a reference to a particular constant
+ * value of an enumerated type.
+ */
+public final class CstEnumRef extends CstMemberRef {
+ /** {@code null-ok;} the corresponding field ref, lazily initialized */
+ private CstFieldRef fieldRef;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param nat {@code non-null;} the name-and-type; the defining class is derived
+ * from this
+ */
+ public CstEnumRef(CstNat nat) {
+ super(new CstType(nat.getFieldType()), nat);
+
+ fieldRef = null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String typeName() {
+ return "enum";
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <b>Note:</b> This returns the enumerated type.
+ */
+ public Type getType() {
+ return getDefiningClass().getClassType();
+ }
+
+ /**
+ * Get a {@link CstFieldRef} that corresponds with this instance.
+ *
+ * @return {@code non-null;} the corresponding field reference
+ */
+ public CstFieldRef getFieldRef() {
+ if (fieldRef == null) {
+ fieldRef = new CstFieldRef(getDefiningClass(), getNat());
+ }
+
+ return fieldRef;
+ }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstFieldRef.java b/dx/src/com/android/dx/rop/cst/CstFieldRef.java
new file mode 100644
index 0000000..06f0b15
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstFieldRef.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+
+/**
+ * Constants of type {@code CONSTANT_Fieldref_info}.
+ */
+public final class CstFieldRef extends CstMemberRef {
+ /**
+ * Returns an instance of this class that represents the static
+ * field which should hold the class corresponding to a given
+ * primitive type. For example, if given {@link Type#INT}, this
+ * method returns an instance corresponding to the field
+ * {@code java.lang.Integer.TYPE}.
+ *
+ * @param primitiveType {@code non-null;} the primitive type
+ * @return {@code non-null;} the corresponding static field
+ */
+ public static CstFieldRef forPrimitiveType(Type primitiveType) {
+ return new CstFieldRef(CstType.forBoxedPrimitiveType(primitiveType),
+ CstNat.PRIMITIVE_TYPE_NAT);
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param definingClass {@code non-null;} the type of the defining class
+ * @param nat {@code non-null;} the name-and-type
+ */
+ public CstFieldRef(CstType definingClass, CstNat nat) {
+ super(definingClass, nat);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String typeName() {
+ return "field";
+ }
+
+ /**
+ * Returns the type of this field.
+ *
+ * @return {@code non-null;} the field's type
+ */
+ public Type getType() {
+ return getNat().getFieldType();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected int compareTo0(Constant other) {
+ int cmp = super.compareTo0(other);
+
+ if (cmp != 0) {
+ return cmp;
+ }
+
+ CstFieldRef otherField = (CstFieldRef) other;
+ CstUtf8 thisDescriptor = getNat().getDescriptor();
+ CstUtf8 otherDescriptor = otherField.getNat().getDescriptor();
+ return thisDescriptor.compareTo(otherDescriptor);
+ }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstFloat.java b/dx/src/com/android/dx/rop/cst/CstFloat.java
new file mode 100644
index 0000000..0a2354a
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstFloat.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.Hex;
+
+/**
+ * Constants of type {@code CONSTANT_Float_info}.
+ */
+public final class CstFloat
+ extends CstLiteral32 {
+ /** {@code non-null;} instance representing {@code 0} */
+ public static final CstFloat VALUE_0 = make(Float.floatToIntBits(0.0f));
+
+ /** {@code non-null;} instance representing {@code 1} */
+ public static final CstFloat VALUE_1 = make(Float.floatToIntBits(1.0f));
+
+ /** {@code non-null;} instance representing {@code 2} */
+ public static final CstFloat VALUE_2 = make(Float.floatToIntBits(2.0f));
+
+ /**
+ * Makes an instance for the given value. This may (but does not
+ * necessarily) return an already-allocated instance.
+ *
+ * @param bits the {@code float} value as {@code int} bits
+ */
+ public static CstFloat make(int bits) {
+ /*
+ * Note: Javadoc notwithstanding, this implementation always
+ * allocates.
+ */
+ return new CstFloat(bits);
+ }
+
+ /**
+ * Constructs an instance. This constructor is private; use {@link #make}.
+ *
+ * @param bits the {@code float} value as {@code int} bits
+ */
+ private CstFloat(int bits) {
+ super(bits);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ int bits = getIntBits();
+ return "float{0x" + Hex.u4(bits) + " / " +
+ Float.intBitsToFloat(bits) + '}';
+ }
+
+ /** {@inheritDoc} */
+ public Type getType() {
+ return Type.FLOAT;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String typeName() {
+ return "float";
+ }
+
+ /** {@inheritDoc} */
+ public String toHuman() {
+ return Float.toString(Float.intBitsToFloat(getIntBits()));
+ }
+
+ /**
+ * Gets the {@code float} value.
+ *
+ * @return the value
+ */
+ public float getValue() {
+ return Float.intBitsToFloat(getIntBits());
+ }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstInteger.java b/dx/src/com/android/dx/rop/cst/CstInteger.java
new file mode 100644
index 0000000..3691fc0
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstInteger.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.Hex;
+
+/**
+ * Constants of type {@code CONSTANT_Integer_info}.
+ */
+public final class CstInteger
+ extends CstLiteral32 {
+ /** {@code non-null;} array of cached instances */
+ private static final CstInteger[] cache = new CstInteger[511];
+
+ /** {@code non-null;} instance representing {@code -1} */
+ public static final CstInteger VALUE_M1 = make(-1);
+
+ /** {@code non-null;} instance representing {@code 0} */
+ public static final CstInteger VALUE_0 = make(0);
+
+ /** {@code non-null;} instance representing {@code 1} */
+ public static final CstInteger VALUE_1 = make(1);
+
+ /** {@code non-null;} instance representing {@code 2} */
+ public static final CstInteger VALUE_2 = make(2);
+
+ /** {@code non-null;} instance representing {@code 3} */
+ public static final CstInteger VALUE_3 = make(3);
+
+ /** {@code non-null;} instance representing {@code 4} */
+ public static final CstInteger VALUE_4 = make(4);
+
+ /** {@code non-null;} instance representing {@code 5} */
+ public static final CstInteger VALUE_5 = make(5);
+
+ /**
+ * Makes an instance for the given value. This may (but does not
+ * necessarily) return an already-allocated instance.
+ *
+ * @param value the {@code int} value
+ * @return {@code non-null;} the appropriate instance
+ */
+ public static CstInteger make(int value) {
+ /*
+ * Note: No need to synchronize, since we don't make any sort
+ * of guarantee about ==, and it's okay to overwrite existing
+ * entries too.
+ */
+ int idx = (value & 0x7fffffff) % cache.length;
+ CstInteger obj = cache[idx];
+
+ if ((obj != null) && (obj.getValue() == value)) {
+ return obj;
+ }
+
+ obj = new CstInteger(value);
+ cache[idx] = obj;
+ return obj;
+ }
+
+ /**
+ * Constructs an instance. This constructor is private; use {@link #make}.
+ *
+ * @param value the {@code int} value
+ */
+ private CstInteger(int value) {
+ super(value);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ int value = getIntBits();
+ return "int{0x" + Hex.u4(value) + " / " + value + '}';
+ }
+
+ /** {@inheritDoc} */
+ public Type getType() {
+ return Type.INT;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String typeName() {
+ return "int";
+ }
+
+ /** {@inheritDoc} */
+ public String toHuman() {
+ return Integer.toString(getIntBits());
+ }
+
+ /**
+ * Gets the {@code int} value.
+ *
+ * @return the value
+ */
+ public int getValue() {
+ return getIntBits();
+ }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstInterfaceMethodRef.java b/dx/src/com/android/dx/rop/cst/CstInterfaceMethodRef.java
new file mode 100644
index 0000000..8b8cb30
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstInterfaceMethodRef.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+/**
+ * Constants of type {@code CONSTANT_InterfaceMethodref_info}.
+ */
+public final class CstInterfaceMethodRef
+ extends CstBaseMethodRef {
+ /**
+ * {@code null-ok;} normal {@link CstMethodRef} that corresponds to this
+ * instance, if calculated
+ */
+ private CstMethodRef methodRef;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param definingClass {@code non-null;} the type of the defining class
+ * @param nat {@code non-null;} the name-and-type
+ */
+ public CstInterfaceMethodRef(CstType definingClass, CstNat nat) {
+ super(definingClass, nat);
+ methodRef = null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String typeName() {
+ return "ifaceMethod";
+ }
+
+ /**
+ * Gets a normal (non-interface) {@link CstMethodRef} that corresponds to
+ * this instance.
+ *
+ * @return {@code non-null;} an appropriate instance
+ */
+ public CstMethodRef toMethodRef() {
+ if (methodRef == null) {
+ methodRef = new CstMethodRef(getDefiningClass(), getNat());
+ }
+
+ return methodRef;
+ }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstKnownNull.java b/dx/src/com/android/dx/rop/cst/CstKnownNull.java
new file mode 100644
index 0000000..a80322c
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstKnownNull.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+
+/**
+ * Constant type to represent a known-{@code null} value.
+ */
+public final class CstKnownNull extends CstLiteralBits {
+ /** {@code non-null;} unique instance of this class */
+ public static final CstKnownNull THE_ONE = new CstKnownNull();
+
+ /**
+ * Constructs an instance. This class is not publicly instantiable. Use
+ * {@link #THE_ONE}.
+ */
+ private CstKnownNull() {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof CstKnownNull);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return 0x4466757a;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected int compareTo0(Constant other) {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return "known-null";
+ }
+
+ /** {@inheritDoc} */
+ public Type getType() {
+ return Type.KNOWN_NULL;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String typeName() {
+ return "known-null";
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCategory2() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public String toHuman() {
+ return "null";
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean fitsInInt() {
+ // See comment in getIntBits().
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * As "literal bits," a known-null is always represented as the
+ * number zero.
+ */
+ @Override
+ public int getIntBits() {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * As "literal bits," a known-null is always represented as the
+ * number zero.
+ */
+ @Override
+ public long getLongBits() {
+ return 0;
+ }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstLiteral32.java b/dx/src/com/android/dx/rop/cst/CstLiteral32.java
new file mode 100644
index 0000000..042cbd9
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstLiteral32.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+/**
+ * Constants which are literal 32-bit values of some sort.
+ */
+public abstract class CstLiteral32
+ extends CstLiteralBits {
+ /** the value as {@code int} bits */
+ private final int bits;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param bits the value as {@code int} bits
+ */
+ /*package*/ CstLiteral32(int bits) {
+ this.bits = bits;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final boolean equals(Object other) {
+ return (other != null) &&
+ (getClass() == other.getClass()) &&
+ bits == ((CstLiteral32) other).bits;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final int hashCode() {
+ return bits;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected int compareTo0(Constant other) {
+ int otherBits = ((CstLiteral32) other).bits;
+
+ if (bits < otherBits) {
+ return -1;
+ } else if (bits > otherBits) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final boolean isCategory2() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final boolean fitsInInt() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final int getIntBits() {
+ return bits;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final long getLongBits() {
+ return (long) bits;
+ }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstLiteral64.java b/dx/src/com/android/dx/rop/cst/CstLiteral64.java
new file mode 100644
index 0000000..94cfa8c
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstLiteral64.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+/**
+ * Constants which are literal 64-bit values of some sort.
+ */
+public abstract class CstLiteral64
+ extends CstLiteralBits {
+ /** the value as {@code long} bits */
+ private final long bits;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param bits the value as {@code long} bits
+ */
+ /*package*/ CstLiteral64(long bits) {
+ this.bits = bits;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final boolean equals(Object other) {
+ return (other != null) &&
+ (getClass() == other.getClass()) &&
+ bits == ((CstLiteral64) other).bits;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final int hashCode() {
+ return (int) bits ^ (int) (bits >> 32);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected int compareTo0(Constant other) {
+ long otherBits = ((CstLiteral64) other).bits;
+
+ if (bits < otherBits) {
+ return -1;
+ } else if (bits > otherBits) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final boolean isCategory2() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final boolean fitsInInt() {
+ return (int) bits == bits;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final int getIntBits() {
+ return (int) bits;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final long getLongBits() {
+ return bits;
+ }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstLiteralBits.java b/dx/src/com/android/dx/rop/cst/CstLiteralBits.java
new file mode 100644
index 0000000..8bf13a2
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstLiteralBits.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+/**
+ * Constants which are literal bitwise values of some sort.
+ */
+public abstract class CstLiteralBits
+ extends TypedConstant {
+ /**
+ * Returns whether or not this instance's value may be accurately
+ * represented as an {@code int}. The rule is that if there
+ * is an {@code int} which may be sign-extended to yield this
+ * instance's value, then this method returns {@code true}.
+ * Otherwise, it returns {@code false}.
+ *
+ * @return {@code true} iff this instance fits in an {@code int}
+ */
+ public abstract boolean fitsInInt();
+
+ /**
+ * Gets the value as {@code int} bits. If this instance contains
+ * more bits than fit in an {@code int}, then this returns only
+ * the low-order bits.
+ *
+ * @return the bits
+ */
+ public abstract int getIntBits();
+
+ /**
+ * Gets the value as {@code long} bits. If this instance contains
+ * fewer bits than fit in a {@code long}, then the result of this
+ * method is the sign extension of the value.
+ *
+ * @return the bits
+ */
+ public abstract long getLongBits();
+
+ /**
+ * Returns true if this value can fit in 16 bits with sign-extension.
+ *
+ * @return true if the sign-extended lower 16 bits are the same as
+ * the value.
+ */
+ public boolean fitsIn16Bits() {
+ if (! fitsInInt()) {
+ return false;
+ }
+
+ int bits = getIntBits();
+ return (short) bits == bits;
+ }
+
+ /**
+ * Returns true if this value can fit in 8 bits with sign-extension.
+ *
+ * @return true if the sign-extended lower 8 bits are the same as
+ * the value.
+ */
+ public boolean fitsIn8Bits() {
+ if (! fitsInInt()) {
+ return false;
+ }
+
+ int bits = getIntBits();
+ return (byte) bits == bits;
+ }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstLong.java b/dx/src/com/android/dx/rop/cst/CstLong.java
new file mode 100644
index 0000000..d159529
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstLong.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.Hex;
+
+/**
+ * Constants of type {@code CONSTANT_Long_info}.
+ */
+public final class CstLong
+ extends CstLiteral64 {
+ /** {@code non-null;} instance representing {@code 0} */
+ public static final CstLong VALUE_0 = make(0);
+
+ /** {@code non-null;} instance representing {@code 1} */
+ public static final CstLong VALUE_1 = make(1);
+
+ /**
+ * Makes an instance for the given value. This may (but does not
+ * necessarily) return an already-allocated instance.
+ *
+ * @param value the {@code long} value
+ */
+ public static CstLong make(long value) {
+ /*
+ * Note: Javadoc notwithstanding, this implementation always
+ * allocates.
+ */
+ return new CstLong(value);
+ }
+
+ /**
+ * Constructs an instance. This constructor is private; use {@link #make}.
+ *
+ * @param value the {@code long} value
+ */
+ private CstLong(long value) {
+ super(value);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ long value = getLongBits();
+ return "long{0x" + Hex.u8(value) + " / " + value + '}';
+ }
+
+ /** {@inheritDoc} */
+ public Type getType() {
+ return Type.LONG;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String typeName() {
+ return "long";
+ }
+
+ /** {@inheritDoc} */
+ public String toHuman() {
+ return Long.toString(getLongBits());
+ }
+
+ /**
+ * Gets the {@code long} value.
+ *
+ * @return the value
+ */
+ public long getValue() {
+ return getLongBits();
+ }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstMemberRef.java b/dx/src/com/android/dx/rop/cst/CstMemberRef.java
new file mode 100644
index 0000000..0791087
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstMemberRef.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+/**
+ * Constants of type {@code CONSTANT_*ref_info}.
+ */
+public abstract class CstMemberRef extends TypedConstant {
+ /** {@code non-null;} the type of the defining class */
+ private final CstType definingClass;
+
+ /** {@code non-null;} the name-and-type */
+ private final CstNat nat;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param definingClass {@code non-null;} the type of the defining class
+ * @param nat {@code non-null;} the name-and-type
+ */
+ /*package*/ CstMemberRef(CstType definingClass, CstNat nat) {
+ if (definingClass == null) {
+ throw new NullPointerException("definingClass == null");
+ }
+
+ if (nat == null) {
+ throw new NullPointerException("nat == null");
+ }
+
+ this.definingClass = definingClass;
+ this.nat = nat;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final boolean equals(Object other) {
+ if ((other == null) || (getClass() != other.getClass())) {
+ return false;
+ }
+
+ CstMemberRef otherRef = (CstMemberRef) other;
+ return definingClass.equals(otherRef.definingClass) &&
+ nat.equals(otherRef.nat);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final int hashCode() {
+ return (definingClass.hashCode() * 31) ^ nat.hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p><b>Note:</b> This implementation just compares the defining
+ * class and name, and it is up to subclasses to compare the rest
+ * after calling {@code super.compareTo0()}.</p>
+ */
+ @Override
+ protected int compareTo0(Constant other) {
+ CstMemberRef otherMember = (CstMemberRef) other;
+ int cmp = definingClass.compareTo(otherMember.definingClass);
+
+ if (cmp != 0) {
+ return cmp;
+ }
+
+ CstUtf8 thisName = nat.getName();
+ CstUtf8 otherName = otherMember.nat.getName();
+
+ return thisName.compareTo(otherName);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final String toString() {
+ return typeName() + '{' + toHuman() + '}';
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final boolean isCategory2() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public final String toHuman() {
+ return definingClass.toHuman() + '.' + nat.toHuman();
+ }
+
+ /**
+ * Gets the type of the defining class.
+ *
+ * @return {@code non-null;} the type of defining class
+ */
+ public final CstType getDefiningClass() {
+ return definingClass;
+ }
+
+ /**
+ * Gets the defining name-and-type.
+ *
+ * @return {@code non-null;} the name-and-type
+ */
+ public final CstNat getNat() {
+ return nat;
+ }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstMethodRef.java b/dx/src/com/android/dx/rop/cst/CstMethodRef.java
new file mode 100644
index 0000000..075bc7c
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstMethodRef.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+/**
+ * Constants of type {@code CONSTANT_Methodref_info}.
+ */
+public final class CstMethodRef
+ extends CstBaseMethodRef {
+ /**
+ * Constructs an instance.
+ *
+ * @param definingClass {@code non-null;} the type of the defining class
+ * @param nat {@code non-null;} the name-and-type
+ */
+ public CstMethodRef(CstType definingClass, CstNat nat) {
+ super(definingClass, nat);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String typeName() {
+ return "method";
+ }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstNat.java b/dx/src/com/android/dx/rop/cst/CstNat.java
new file mode 100644
index 0000000..8a2c591
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstNat.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+
+/**
+ * Constants of type {@code CONSTANT_NameAndType_info}.
+ */
+public final class CstNat extends Constant {
+ /**
+ * {@code non-null;} the instance for name {@code TYPE} and descriptor
+ * {@code java.lang.Class}, which is useful when dealing with
+ * wrapped primitives
+ */
+ public static final CstNat PRIMITIVE_TYPE_NAT =
+ new CstNat(new CstUtf8("TYPE"),
+ new CstUtf8("Ljava/lang/Class;"));
+
+ /** {@code non-null;} the name */
+ private final CstUtf8 name;
+
+ /** {@code non-null;} the descriptor (type) */
+ private final CstUtf8 descriptor;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param name {@code non-null;} the name
+ * @param descriptor {@code non-null;} the descriptor
+ */
+ public CstNat(CstUtf8 name, CstUtf8 descriptor) {
+ if (name == null) {
+ throw new NullPointerException("name == null");
+ }
+
+ if (descriptor == null) {
+ throw new NullPointerException("descriptor == null");
+ }
+
+ this.name = name;
+ this.descriptor = descriptor;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof CstNat)) {
+ return false;
+ }
+
+ CstNat otherNat = (CstNat) other;
+ return name.equals(otherNat.name) &&
+ descriptor.equals(otherNat.descriptor);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return (name.hashCode() * 31) ^ descriptor.hashCode();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected int compareTo0(Constant other) {
+ CstNat otherNat = (CstNat) other;
+ int cmp = name.compareTo(otherNat.name);
+
+ if (cmp != 0) {
+ return cmp;
+ }
+
+ return descriptor.compareTo(otherNat.descriptor);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return "nat{" + toHuman() + '}';
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String typeName() {
+ return "nat";
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCategory2() {
+ return false;
+ }
+
+ /**
+ * Gets the name.
+ *
+ * @return {@code non-null;} the name
+ */
+ public CstUtf8 getName() {
+ return name;
+ }
+
+ /**
+ * Gets the descriptor.
+ *
+ * @return {@code non-null;} the descriptor
+ */
+ public CstUtf8 getDescriptor() {
+ return descriptor;
+ }
+
+ /**
+ * Returns an unadorned but human-readable version of the name-and-type
+ * value.
+ *
+ * @return {@code non-null;} the human form
+ */
+ public String toHuman() {
+ return name.toHuman() + ':' + descriptor.toHuman();
+ }
+
+ /**
+ * Gets the field type corresponding to this instance's descriptor.
+ * This method is only valid to call if the descriptor in fact describes
+ * a field (and not a method).
+ *
+ * @return {@code non-null;} the field type
+ */
+ public Type getFieldType() {
+ return Type.intern(descriptor.getString());
+ }
+
+ /**
+ * Gets whether this instance has the name of a standard instance
+ * initialization method. This is just a convenient shorthand for
+ * {@code getName().getString().equals("<init>")}.
+ *
+ * @return {@code true} iff this is a reference to an
+ * instance initialization method
+ */
+ public final boolean isInstanceInit() {
+ return name.getString().equals("<init>");
+ }
+
+ /**
+ * Gets whether this instance has the name of a standard class
+ * initialization method. This is just a convenient shorthand for
+ * {@code getName().getString().equals("<clinit>")}.
+ *
+ * @return {@code true} iff this is a reference to an
+ * instance initialization method
+ */
+ public final boolean isClassInit() {
+ return name.getString().equals("<clinit>");
+ }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstShort.java b/dx/src/com/android/dx/rop/cst/CstShort.java
new file mode 100644
index 0000000..5be1022
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstShort.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.Hex;
+
+/**
+ * Constants of type {@code short}.
+ */
+public final class CstShort
+ extends CstLiteral32 {
+ /** {@code non-null;} the value {@code 0} as an instance of this class */
+ public static final CstShort VALUE_0 = make((short) 0);
+
+ /**
+ * Makes an instance for the given value. This may (but does not
+ * necessarily) return an already-allocated instance.
+ *
+ * @param value the {@code short} value
+ * @return {@code non-null;} the appropriate instance
+ */
+ public static CstShort make(short value) {
+ return new CstShort(value);
+ }
+
+ /**
+ * Makes an instance for the given {@code int} value. This
+ * may (but does not necessarily) return an already-allocated
+ * instance.
+ *
+ * @param value the value, which must be in range for a {@code short}
+ * @return {@code non-null;} the appropriate instance
+ */
+ public static CstShort make(int value) {
+ short cast = (short) value;
+
+ if (cast != value) {
+ throw new IllegalArgumentException("bogus short value: " +
+ value);
+ }
+
+ return make(cast);
+ }
+
+ /**
+ * Constructs an instance. This constructor is private; use {@link #make}.
+ *
+ * @param value the {@code short} value
+ */
+ private CstShort(short value) {
+ super(value);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ int value = getIntBits();
+ return "short{0x" + Hex.u2(value) + " / " + value + '}';
+ }
+
+ /** {@inheritDoc} */
+ public Type getType() {
+ return Type.SHORT;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String typeName() {
+ return "short";
+ }
+
+ /** {@inheritDoc} */
+ public String toHuman() {
+ return Integer.toString(getIntBits());
+ }
+
+ /**
+ * Gets the {@code short} value.
+ *
+ * @return the value
+ */
+ public short getValue() {
+ return (short) getIntBits();
+ }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstString.java b/dx/src/com/android/dx/rop/cst/CstString.java
new file mode 100644
index 0000000..7dbfa02
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstString.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+
+/**
+ * Constants of type {@code CONSTANT_String_info}.
+ */
+public final class CstString
+ extends TypedConstant {
+ /** {@code non-null;} the string value */
+ private final CstUtf8 string;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param string {@code non-null;} the string value
+ */
+ public CstString(CstUtf8 string) {
+ if (string == null) {
+ throw new NullPointerException("string == null");
+ }
+
+ this.string = string;
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param string {@code non-null;} the string value
+ */
+ public CstString(String string) {
+ this(new CstUtf8(string));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof CstString)) {
+ return false;
+ }
+
+ return string.equals(((CstString) other).string);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return string.hashCode();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected int compareTo0(Constant other) {
+ return string.compareTo(((CstString) other).string);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return "string{" + toHuman() + '}';
+ }
+
+ /** {@inheritDoc} */
+ public Type getType() {
+ return Type.STRING;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String typeName() {
+ return "string";
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCategory2() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public String toHuman() {
+ return string.toQuoted();
+ }
+
+ /**
+ * Gets the string value.
+ *
+ * @return {@code non-null;} the string value
+ */
+ public CstUtf8 getString() {
+ return string;
+ }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstType.java b/dx/src/com/android/dx/rop/cst/CstType.java
new file mode 100644
index 0000000..593adf8
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstType.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+
+import java.util.HashMap;
+
+/**
+ * Constants that represent an arbitrary type (reference or primitive).
+ */
+public final class CstType extends TypedConstant {
+ /** {@code non-null;} map of interned types */
+ private static final HashMap<Type, CstType> interns =
+ new HashMap<Type, CstType>(100);
+
+ /** {@code non-null;} instance corresponding to the class {@code Object} */
+ public static final CstType OBJECT = intern(Type.OBJECT);
+
+ /** {@code non-null;} instance corresponding to the class {@code Boolean} */
+ public static final CstType BOOLEAN = intern(Type.BOOLEAN_CLASS);
+
+ /** {@code non-null;} instance corresponding to the class {@code Byte} */
+ public static final CstType BYTE = intern(Type.BYTE_CLASS);
+
+ /** {@code non-null;} instance corresponding to the class {@code Character} */
+ public static final CstType CHARACTER = intern(Type.CHARACTER_CLASS);
+
+ /** {@code non-null;} instance corresponding to the class {@code Double} */
+ public static final CstType DOUBLE = intern(Type.DOUBLE_CLASS);
+
+ /** {@code non-null;} instance corresponding to the class {@code Float} */
+ public static final CstType FLOAT = intern(Type.FLOAT_CLASS);
+
+ /** {@code non-null;} instance corresponding to the class {@code Long} */
+ public static final CstType LONG = intern(Type.LONG_CLASS);
+
+ /** {@code non-null;} instance corresponding to the class {@code Integer} */
+ public static final CstType INTEGER = intern(Type.INTEGER_CLASS);
+
+ /** {@code non-null;} instance corresponding to the class {@code Short} */
+ public static final CstType SHORT = intern(Type.SHORT_CLASS);
+
+ /** {@code non-null;} instance corresponding to the class {@code Void} */
+ public static final CstType VOID = intern(Type.VOID_CLASS);
+
+ /** {@code non-null;} instance corresponding to the type {@code boolean[]} */
+ public static final CstType BOOLEAN_ARRAY = intern(Type.BOOLEAN_ARRAY);
+
+ /** {@code non-null;} instance corresponding to the type {@code byte[]} */
+ public static final CstType BYTE_ARRAY = intern(Type.BYTE_ARRAY);
+
+ /** {@code non-null;} instance corresponding to the type {@code char[]} */
+ public static final CstType CHAR_ARRAY = intern(Type.CHAR_ARRAY);
+
+ /** {@code non-null;} instance corresponding to the type {@code double[]} */
+ public static final CstType DOUBLE_ARRAY = intern(Type.DOUBLE_ARRAY);
+
+ /** {@code non-null;} instance corresponding to the type {@code float[]} */
+ public static final CstType FLOAT_ARRAY = intern(Type.FLOAT_ARRAY);
+
+ /** {@code non-null;} instance corresponding to the type {@code long[]} */
+ public static final CstType LONG_ARRAY = intern(Type.LONG_ARRAY);
+
+ /** {@code non-null;} instance corresponding to the type {@code int[]} */
+ public static final CstType INT_ARRAY = intern(Type.INT_ARRAY);
+
+ /** {@code non-null;} instance corresponding to the type {@code short[]} */
+ public static final CstType SHORT_ARRAY = intern(Type.SHORT_ARRAY);
+
+ /** {@code non-null;} the underlying type */
+ private final Type type;
+
+ /**
+ * {@code null-ok;} the type descriptor corresponding to this instance, if
+ * calculated
+ */
+ private CstUtf8 descriptor;
+
+ /**
+ * Returns an instance of this class that represents the wrapper
+ * class corresponding to a given primitive type. For example, if
+ * given {@link Type#INT}, this method returns the class reference
+ * {@code java.lang.Integer}.
+ *
+ * @param primitiveType {@code non-null;} the primitive type
+ * @return {@code non-null;} the corresponding wrapper class
+ */
+ public static CstType forBoxedPrimitiveType(Type primitiveType) {
+ switch (primitiveType.getBasicType()) {
+ case Type.BT_BOOLEAN: return BOOLEAN;
+ case Type.BT_BYTE: return BYTE;
+ case Type.BT_CHAR: return CHARACTER;
+ case Type.BT_DOUBLE: return DOUBLE;
+ case Type.BT_FLOAT: return FLOAT;
+ case Type.BT_INT: return INTEGER;
+ case Type.BT_LONG: return LONG;
+ case Type.BT_SHORT: return SHORT;
+ case Type.BT_VOID: return VOID;
+ }
+
+ throw new IllegalArgumentException("not primitive: " + primitiveType);
+ }
+
+ /**
+ * Returns an interned instance of this class for the given type.
+ *
+ * @param type {@code non-null;} the underlying type
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public static CstType intern(Type type) {
+ CstType cst = interns.get(type);
+
+ if (cst == null) {
+ cst = new CstType(type);
+ interns.put(type, cst);
+ }
+
+ return cst;
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param type {@code non-null;} the underlying type
+ */
+ public CstType(Type type) {
+ if (type == null) {
+ throw new NullPointerException("type == null");
+ }
+
+ if (type == type.KNOWN_NULL) {
+ throw new UnsupportedOperationException(
+ "KNOWN_NULL is not representable");
+ }
+
+ this.type = type;
+ this.descriptor = null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof CstType)) {
+ return false;
+ }
+
+ return type == ((CstType) other).type;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return type.hashCode();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected int compareTo0(Constant other) {
+ String thisDescriptor = type.getDescriptor();
+ String otherDescriptor = ((CstType) other).type.getDescriptor();
+ return thisDescriptor.compareTo(otherDescriptor);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return "type{" + toHuman() + '}';
+ }
+
+ /** {@inheritDoc} */
+ public Type getType() {
+ return Type.CLASS;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String typeName() {
+ return "type";
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCategory2() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public String toHuman() {
+ return type.toHuman();
+ }
+
+ /**
+ * Gets the underlying type (as opposed to the type corresponding
+ * to this instance as a constant, which is always
+ * {@code Class}).
+ *
+ * @return {@code non-null;} the type corresponding to the name
+ */
+ public Type getClassType() {
+ return type;
+ }
+
+ /**
+ * Gets the type descriptor for this instance.
+ *
+ * @return {@code non-null;} the descriptor
+ */
+ public CstUtf8 getDescriptor() {
+ if (descriptor == null) {
+ descriptor = new CstUtf8(type.getDescriptor());
+ }
+
+ return descriptor;
+ }
+}
diff --git a/dx/src/com/android/dx/rop/cst/CstUtf8.java b/dx/src/com/android/dx/rop/cst/CstUtf8.java
new file mode 100644
index 0000000..5cfc1f3
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/CstUtf8.java
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.util.ByteArray;
+import com.android.dx.util.Hex;
+
+/**
+ * Constants of type {@code CONSTANT_Utf8_info}.
+ */
+public final class CstUtf8 extends Constant {
+ /**
+ * {@code non-null;} instance representing {@code ""}, that is, the
+ * empty string
+ */
+ public static final CstUtf8 EMPTY_STRING = new CstUtf8("");
+
+ /** {@code non-null;} the UTF-8 value as a string */
+ private final String string;
+
+ /** {@code non-null;} the UTF-8 value as bytes */
+ private final ByteArray bytes;
+
+ /**
+ * Converts a string into its Java-style UTF-8 form. Java-style UTF-8
+ * differs from normal UTF-8 in the handling of character '\0' and
+ * surrogate pairs.
+ *
+ * @param string {@code non-null;} the string to convert
+ * @return {@code non-null;} the UTF-8 bytes for it
+ */
+ public static byte[] stringToUtf8Bytes(String string) {
+ int len = string.length();
+ byte[] bytes = new byte[len * 3]; // Avoid having to reallocate.
+ int outAt = 0;
+
+ for (int i = 0; i < len; i++) {
+ char c = string.charAt(i);
+ if ((c != 0) && (c < 0x80)) {
+ bytes[outAt] = (byte) c;
+ outAt++;
+ } else if (c < 0x800) {
+ bytes[outAt] = (byte) (((c >> 6) & 0x1f) | 0xc0);
+ bytes[outAt + 1] = (byte) ((c & 0x3f) | 0x80);
+ outAt += 2;
+ } else {
+ bytes[outAt] = (byte) (((c >> 12) & 0x0f) | 0xe0);
+ bytes[outAt + 1] = (byte) (((c >> 6) & 0x3f) | 0x80);
+ bytes[outAt + 2] = (byte) ((c & 0x3f) | 0x80);
+ outAt += 3;
+ }
+ }
+
+ byte[] result = new byte[outAt];
+ System.arraycopy(bytes, 0, result, 0, outAt);
+ return result;
+ }
+
+ /**
+ * Converts an array of UTF-8 bytes into a string.
+ *
+ * @param bytes {@code non-null;} the bytes to convert
+ * @return {@code non-null;} the converted string
+ */
+ public static String utf8BytesToString(ByteArray bytes) {
+ int length = bytes.size();
+ char[] chars = new char[length]; // This is sized to avoid a realloc.
+ int outAt = 0;
+
+ for (int at = 0; length > 0; /*at*/) {
+ int v0 = bytes.getUnsignedByte(at);
+ char out;
+ switch (v0 >> 4) {
+ case 0x00: case 0x01: case 0x02: case 0x03:
+ case 0x04: case 0x05: case 0x06: case 0x07: {
+ // 0XXXXXXX -- single-byte encoding
+ length--;
+ if (v0 == 0) {
+ // A single zero byte is illegal.
+ return throwBadUtf8(v0, at);
+ }
+ out = (char) v0;
+ at++;
+ break;
+ }
+ case 0x0c: case 0x0d: {
+ // 110XXXXX -- two-byte encoding
+ length -= 2;
+ if (length < 0) {
+ return throwBadUtf8(v0, at);
+ }
+ int v1 = bytes.getUnsignedByte(at + 1);
+ if ((v1 & 0xc0) != 0x80) {
+ return throwBadUtf8(v1, at + 1);
+ }
+ int value = ((v0 & 0x1f) << 6) | (v1 & 0x3f);
+ if ((value != 0) && (value < 0x80)) {
+ /*
+ * This should have been represented with
+ * one-byte encoding.
+ */
+ return throwBadUtf8(v1, at + 1);
+ }
+ out = (char) value;
+ at += 2;
+ break;
+ }
+ case 0x0e: {
+ // 1110XXXX -- three-byte encoding
+ length -= 3;
+ if (length < 0) {
+ return throwBadUtf8(v0, at);
+ }
+ int v1 = bytes.getUnsignedByte(at + 1);
+ if ((v1 & 0xc0) != 0x80) {
+ return throwBadUtf8(v1, at + 1);
+ }
+ int v2 = bytes.getUnsignedByte(at + 2);
+ if ((v1 & 0xc0) != 0x80) {
+ return throwBadUtf8(v2, at + 2);
+ }
+ int value = ((v0 & 0x0f) << 12) | ((v1 & 0x3f) << 6) |
+ (v2 & 0x3f);
+ if (value < 0x800) {
+ /*
+ * This should have been represented with one- or
+ * two-byte encoding.
+ */
+ return throwBadUtf8(v2, at + 2);
+ }
+ out = (char) value;
+ at += 3;
+ break;
+ }
+ default: {
+ // 10XXXXXX, 1111XXXX -- illegal
+ return throwBadUtf8(v0, at);
+ }
+ }
+ chars[outAt] = out;
+ outAt++;
+ }
+
+ return new String(chars, 0, outAt);
+ }
+
+ /**
+ * Helper for {@link #utf8BytesToString}, which throws the right
+ * exception for a bogus utf-8 byte.
+ *
+ * @param value the byte value
+ * @param offset the file offset
+ * @return never
+ * @throws IllegalArgumentException always thrown
+ */
+ private static String throwBadUtf8(int value, int offset) {
+ throw new IllegalArgumentException("bad utf-8 byte " + Hex.u1(value) +
+ " at offset " + Hex.u4(offset));
+ }
+
+ /**
+ * Constructs an instance from a {@code String}.
+ *
+ * @param string {@code non-null;} the UTF-8 value as a string
+ */
+ public CstUtf8(String string) {
+ if (string == null) {
+ throw new NullPointerException("string == null");
+ }
+
+ this.string = string.intern();
+ this.bytes = new ByteArray(stringToUtf8Bytes(string));
+ }
+
+ /**
+ * Constructs an instance from some UTF-8 bytes.
+ *
+ * @param bytes {@code non-null;} array of the UTF-8 bytes
+ */
+ public CstUtf8(ByteArray bytes) {
+ if (bytes == null) {
+ throw new NullPointerException("bytes == null");
+ }
+
+ this.bytes = bytes;
+ this.string = utf8BytesToString(bytes).intern();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof CstUtf8)) {
+ return false;
+ }
+
+ return string.equals(((CstUtf8) other).string);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return string.hashCode();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected int compareTo0(Constant other) {
+ return string.compareTo(((CstUtf8) other).string);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return "utf8{\"" + toHuman() + "\"}";
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String typeName() {
+ return "utf8";
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCategory2() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public String toHuman() {
+ int len = string.length();
+ StringBuilder sb = new StringBuilder(len * 3 / 2);
+
+ for (int i = 0; i < len; i++) {
+ char c = string.charAt(i);
+ if ((c >= ' ') && (c < 0x7f)) {
+ if ((c == '\'') || (c == '\"') || (c == '\\')) {
+ sb.append('\\');
+ }
+ sb.append(c);
+ } else if (c <= 0x7f) {
+ switch (c) {
+ case '\n': sb.append("\\n"); break;
+ case '\r': sb.append("\\r"); break;
+ case '\t': sb.append("\\t"); break;
+ default: {
+ /*
+ * Represent the character as an octal escape.
+ * If the next character is a valid octal
+ * digit, disambiguate by using the
+ * three-digit form.
+ */
+ char nextChar =
+ (i < (len - 1)) ? string.charAt(i + 1) : 0;
+ boolean displayZero =
+ (nextChar >= '0') && (nextChar <= '7');
+ sb.append('\\');
+ for (int shift = 6; shift >= 0; shift -= 3) {
+ char outChar = (char) (((c >> shift) & 7) + '0');
+ if ((outChar != '0') || displayZero) {
+ sb.append(outChar);
+ displayZero = true;
+ }
+ }
+ if (! displayZero) {
+ // Ironic edge case: The original value was 0.
+ sb.append('0');
+ }
+ break;
+ }
+ }
+ } else {
+ sb.append("\\u");
+ sb.append(Character.forDigit(c >> 12, 16));
+ sb.append(Character.forDigit((c >> 8) & 0x0f, 16));
+ sb.append(Character.forDigit((c >> 4) & 0x0f, 16));
+ sb.append(Character.forDigit(c & 0x0f, 16));
+ }
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Gets the value as a human-oriented string, surrounded by double
+ * quotes.
+ *
+ * @return {@code non-null;} the quoted string
+ */
+ public String toQuoted() {
+ return '\"' + toHuman() + '\"';
+ }
+
+ /**
+ * Gets the value as a human-oriented string, surrounded by double
+ * quotes, but ellipsizes the result if it is longer than the given
+ * maximum length
+ *
+ * @param maxLength {@code >= 5;} the maximum length of the string to return
+ * @return {@code non-null;} the quoted string
+ */
+ public String toQuoted(int maxLength) {
+ String string = toHuman();
+ int length = string.length();
+ String ellipses;
+
+ if (length <= (maxLength - 2)) {
+ ellipses = "";
+ } else {
+ string = string.substring(0, maxLength - 5);
+ ellipses = "...";
+ }
+
+ return '\"' + string + ellipses + '\"';
+ }
+
+ /**
+ * Gets the UTF-8 value as a string.
+ * The returned string is always already interned.
+ *
+ * @return {@code non-null;} the UTF-8 value as a string
+ */
+ public String getString() {
+ return string;
+ }
+
+ /**
+ * Gets the UTF-8 value as UTF-8 encoded bytes.
+ *
+ * @return {@code non-null;} an array of the UTF-8 bytes
+ */
+ public ByteArray getBytes() {
+ return bytes;
+ }
+
+ /**
+ * Gets the size of this instance as UTF-8 code points. That is,
+ * get the number of bytes in the UTF-8 encoding of this instance.
+ *
+ * @return {@code >= 0;} the UTF-8 size
+ */
+ public int getUtf8Size() {
+ return bytes.size();
+ }
+
+ /**
+ * Gets the size of this instance as UTF-16 code points. That is,
+ * get the number of 16-bit chars in the UTF-16 encoding of this
+ * instance. This is the same as the {@code length} of the
+ * Java {@code String} representation of this instance.
+ *
+ * @return {@code >= 0;} the UTF-16 size
+ */
+ public int getUtf16Size() {
+ return string.length();
+ }
+}
diff --git a/dx/src/com/android/dx/rop/cst/StdConstantPool.java b/dx/src/com/android/dx/rop/cst/StdConstantPool.java
new file mode 100644
index 0000000..244395d
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/StdConstantPool.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.util.ExceptionWithContext;
+import com.android.dx.util.Hex;
+import com.android.dx.util.MutabilityControl;
+
+/**
+ * Standard implementation of {@link ConstantPool}, which directly stores
+ * an array of {@link Constant} objects and can be made immutable.
+ */
+public final class StdConstantPool
+ extends MutabilityControl implements ConstantPool {
+ /** {@code non-null;} array of entries */
+ private final Constant[] entries;
+
+ /**
+ * Constructs an instance. All indices initially contain {@code null}.
+ *
+ * @param size the size of the pool; this corresponds to the
+ * class file field {@code constant_pool_count}, and is in fact
+ * always at least one more than the actual size of the constant pool,
+ * as element {@code 0} is always invalid.
+ */
+ public StdConstantPool(int size) {
+ super(size > 1);
+
+ if (size < 1) {
+ throw new IllegalArgumentException("size < 1");
+ }
+
+ entries = new Constant[size];
+ }
+
+ /** {@inheritDoc} */
+ public int size() {
+ return entries.length;
+ }
+
+ /** {@inheritDoc} */
+ public Constant getOrNull(int n) {
+ try {
+ return entries[n];
+ } catch (IndexOutOfBoundsException ex) {
+ // Translate the exception.
+ return throwInvalid(n);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public Constant get0Ok(int n) {
+ if (n == 0) {
+ return null;
+ }
+
+ return get(n);
+ }
+
+ /** {@inheritDoc} */
+ public Constant get(int n) {
+ try {
+ Constant result = entries[n];
+
+ if (result == null) {
+ throwInvalid(n);
+ }
+
+ return result;
+ } catch (IndexOutOfBoundsException ex) {
+ // Translate the exception.
+ return throwInvalid(n);
+ }
+ }
+
+ /**
+ * Sets the entry at the given index.
+ *
+ * @param n {@code >= 1, < size();} which entry
+ * @param cst {@code null-ok;} the constant to store
+ */
+ public void set(int n, Constant cst) {
+ throwIfImmutable();
+
+ boolean cat2 = (cst != null) && cst.isCategory2();
+
+ if (n < 1) {
+ throw new IllegalArgumentException("n < 1");
+ }
+
+ if (cat2) {
+ // Storing a category-2 entry nulls out the next index.
+ if (n == (entries.length - 1)) {
+ throw new IllegalArgumentException("(n == size - 1) && " +
+ "cst.isCategory2()");
+ }
+ entries[n + 1] = null;
+ }
+
+ if ((cst != null) && (entries[n] == null)) {
+ /*
+ * Overwriting the second half of a category-2 entry nulls out
+ * the first half.
+ */
+ Constant prev = entries[n - 1];
+ if ((prev != null) && prev.isCategory2()) {
+ entries[n - 1] = null;
+ }
+ }
+
+ entries[n] = cst;
+ }
+
+ /**
+ * Throws the right exception for an invalid cpi.
+ *
+ * @param idx the bad cpi
+ * @return never
+ * @throws ExceptionWithContext always thrown
+ */
+ private static Constant throwInvalid(int idx) {
+ throw new ExceptionWithContext("invalid constant pool index " +
+ Hex.u2(idx));
+ }
+}
diff --git a/dx/src/com/android/dx/rop/cst/TypedConstant.java b/dx/src/com/android/dx/rop/cst/TypedConstant.java
new file mode 100644
index 0000000..c250c46
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/TypedConstant.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.TypeBearer;
+
+/**
+ * Base class for constants which implement {@link TypeBearer}.
+ */
+public abstract class TypedConstant
+ extends Constant implements TypeBearer {
+ /**
+ * {@inheritDoc}
+ *
+ * This implentation always returns {@code this}.
+ */
+ public final TypeBearer getFrameType() {
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ public final int getBasicType() {
+ return getType().getBasicType();
+ }
+
+ /** {@inheritDoc} */
+ public final int getBasicFrameType() {
+ return getType().getBasicFrameType();
+ }
+
+ /** {@inheritDoc} */
+ public final boolean isConstant() {
+ return true;
+ }
+}
diff --git a/dx/src/com/android/dx/rop/cst/Zeroes.java b/dx/src/com/android/dx/rop/cst/Zeroes.java
new file mode 100644
index 0000000..7250b5a
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/Zeroes.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.cst;
+
+import com.android.dx.rop.type.Type;
+
+/**
+ * Utility for turning types into zeroes.
+ */
+public final class Zeroes {
+ /**
+ * This class is uninstantiable.
+ */
+ private Zeroes() {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Gets the "zero" (or {@code null}) value for the given type.
+ *
+ * @param type {@code non-null;} the type in question
+ * @return {@code non-null;} its "zero" value
+ */
+ public static Constant zeroFor(Type type) {
+ switch (type.getBasicType()) {
+ case Type.BT_BOOLEAN: return CstBoolean.VALUE_FALSE;
+ case Type.BT_BYTE: return CstByte.VALUE_0;
+ case Type.BT_CHAR: return CstChar.VALUE_0;
+ case Type.BT_DOUBLE: return CstDouble.VALUE_0;
+ case Type.BT_FLOAT: return CstFloat.VALUE_0;
+ case Type.BT_INT: return CstInteger.VALUE_0;
+ case Type.BT_LONG: return CstLong.VALUE_0;
+ case Type.BT_SHORT: return CstShort.VALUE_0;
+ case Type.BT_OBJECT: return CstKnownNull.THE_ONE;
+ default: {
+ throw new UnsupportedOperationException("no zero for type: " +
+ type.toHuman());
+ }
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/rop/cst/package.html b/dx/src/com/android/dx/rop/cst/package.html
new file mode 100644
index 0000000..c784d16
--- /dev/null
+++ b/dx/src/com/android/dx/rop/cst/package.html
@@ -0,0 +1,9 @@
+<body>
+<p>Interfaces and implementation of things related to the constant pool.</p>
+
+<p><b>PACKAGES USED:</b>
+<ul>
+<li><code>com.android.dx.rop.type</code></li>
+<li><code>com.android.dx.util</code></li>
+</ul>
+</body>
diff --git a/dx/src/com/android/dx/rop/package-info.java b/dx/src/com/android/dx/rop/package-info.java
new file mode 100644
index 0000000..aaf21ee
--- /dev/null
+++ b/dx/src/com/android/dx/rop/package-info.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop;
+
+/**
+ * <h1>An Introduction to Rop Form</h1>
+ *
+ * This package contains classes associated with dx's {@code Rop}
+ * intermediate form.<p>
+ *
+ * The Rop form is intended to represent the instructions and the control-flow
+ * graph in a reasonably programmatically useful form while closely mirroring
+ * the dex instruction set.<p>
+ *
+ * <h2>Key Classes</h2>
+ *
+ * <ul>
+ * <li> {@link RopMethod}, the representation of an individual method
+ * <li> {@link BasicBlock} and its per-method container, {@link BasicBlockList},
+ * the representation of control flow elements.
+ * <li> {@link Insn} and its subclasses along with its per-basic block
+ * container {@link InsnList}. {@code Insn} instances represent
+ * individual instructions in the abstract register machine.
+ * <li> {@link RegisterSpec} and its container {@link RegisterSpecList}. A
+ * register spec encodes register number, register width, type information,
+ * and potentially local variable information as well for instruction sources
+ * and results.
+ * <li> {@link Rop} instances represent opcodes in the abstract machine. Many
+ * {@code Rop} instances are singletons defined in static fields in
+ * {@link Rops}. The rest are constructed dynamically using static methods
+ * in {@code Rops}
+ * <li> {@link RegOps} lists numeric constants for the opcodes
+ * <li> {@link Constant} and its subclasses represent constant data values
+ * that opcodes may refer to.
+ * <li> {@link Type} instances represent the core data types that can be
+ * handled by the abstract machine.
+ * <li> The {@link TypeBearer} interface is implemented by classes that
+ * represent a core data type, but may also have secondary information
+ * (such as constant value) associated with them.
+ * <ul>
+ *
+ * <h2>Control-Flow Graph</h2>
+ *
+ * Each method is separated into a list of basic blocks. For the most part,
+ * basic blocks are referred to by a positive integer
+ * {@link BasicBlock#getLabel label}, which is always unique per method. The
+ * label value is typically derived from a bytecode address from the source
+ * bytecode. Blocks that don't originate directly from source bytecode have
+ * labels generated for them in a mostly arbitrary order.<p>
+ *
+ * Blocks are referred to by their label, for the most part, because
+ * {@code BasicBlock} instances are immutable and thus any modification to
+ * the control flow graph or the instruction list results in replacement
+ * instances (with identical labels) being created.<p>
+ *
+ * A method has a single {@link RopMethod#getFirstLabel entry block} and 0
+ * to N {@link RopMethod#getExitPredecessors exit predecessor blocks} which
+ * will return. All blocks that are not the entry block will have at least
+ * one predecessor (or are unreachable and should be removed from the block
+ * list). All blocks that are not exit predecessors must have at least one
+ * successor.<p>
+ *
+ * Since all blocks must branch, all blocks must have, as their final
+ * instruction, an instruction whose opcode has a {@link Rop#getBranchingness
+ * branchingness} other than {@link Rop.BRANCH_NONE}. Furthermore, branching
+ * instructions may only be the final instruction in any basic block. If
+ * no other terminating opcode is appropriate, use a {@link Rops#GOTO GOTO}.<p>
+ *
+ * Typically a block will have a {@link BasicBlock#getPrimarySuccessor
+ * primary successor} which distinguishes a particular control flow path.
+ * For {Rops#isCallLike}call or call-like} opcodes, this is the path taken
+ * in the non-exceptional case, where all other successors represent
+ * various exception paths. For comparison operators such as
+ * {@link Rops#IF_EQZ_INT}, the primary successor represents the path taken
+ * if the <b>condition evaluates to false</b>. For {@link SwitchInsn switch
+ * instructions}, the primary successor is the default case.<p>
+ *
+ * A basic block's successor list is ordered and may refer to unique labels
+ * multiple times. For example, if a switch statement contains multiple case
+ * statements for the same code path, a single basic block will likely
+ * appear in the successor list multiple times. In general, the
+ * significance of the successor list's order (like the significance of
+ * the primary successor) is a property of the final instruction of the basic
+ * block. A basic block containing a {@link ThrowingInsn}, for example, has
+ * its successor list in an order identical to the
+ * {@link ThrowingInsn#getCatches} instruction's catches list, with the
+ * primary successor (the no-exception case) listed at the end.
+ *
+ * It is legal for a basic block to have no primary successor. An obvious
+ * example of this is a block that terminates in a {@link Rops#THROW throw}
+ * instruction where a catch block exists inside the current method for that
+ * exception class. Since the only possible path is the exception path, only
+ * the exception path (which cannot be a primary successor) is a successor.
+ * An example of this is shown in {@code dx/tests/092-ssa-cfg-edge-cases}.
+ *
+ * <h2>Rop Instructions</h2>
+ *
+ * <h3>move-result and move-result-pseudo</h3>
+ *
+ * An instruction that may throw an exception may not specify a result. This
+ * is necessary because the result register will not be assigned to if an
+ * exception occurs while processing said instruction and a result assignment
+ * may not occur. Since result assignments only occur in the non-exceptional
+ * case, the result assignments for throwing instructions can be said to occur
+ * at the beginning of the primary successor block rather than at the end of
+ * the current block. The Rop form represents the result assignments this way.
+ * Throwing instructions may not directly specify results. Instead, result
+ * assignments are represented by {@link
+ * Rops#MOVE_RESULT move-result} or {@link Rops#MOVE_RESULT_PSEUDO
+ * move-result-pseudo} instructions at the top of the primary successor block.
+ *
+ * Only a single {@code move-result} or {@code move-result-pseudo}
+ * may exist in any block and it must be exactly the first instruction in the
+ * block.
+ *
+ * A {@code move-result} instruction is used for the results of call-like
+ * instructions. If the value produced by a {@code move-result} is not
+ * used by the method, it may be eliminated as dead code.
+ *
+ * A {@code move-result-pseudo} instruction is used for the results of
+ * non-call-like throwing instructions. It may never be considered dead code
+ * since the final dex instruction will always indicate a result register.
+ * If a required {@code move-result-pseudo} instruction is not found
+ * during conversion to dex bytecode, an exception will be thrown.
+ *
+ * <h3>move-exception</h3>
+ *
+ * A {@link RegOps.MOVE_EXCEPTION move-exception} instruction may appear at
+ * the start of a catch block, and represents the obtaining of the thrown
+ * exception instance. It may only occur as the first instruction in a
+ * basic block, and any basic block in which it occurs must be reachable only
+ * as an exception successor.
+ *
+ * <h3>move-param</h3>
+ *
+ * A {@link RegOps.MOVE_PARAM move-param} instruction represents a method
+ * parameter. Every {@code move-param} instruction is a
+ * {@link PlainCstInsn}. The index of the method parameter they refer to is
+ * carried as the {@link CstInteger integer constant} associated with the
+ * instruction.
+ *
+ * Any number of {@code move-param} instructions referring to the same
+ * parameter index may be included in a method's instruction lists. They
+ * have no restrictions on placement beyond those of any other
+ * {@link Rop.BRANCH_NONE} instruction. Note that the SSA optimizer arranges the
+ * parameter assignments to align with the dex bytecode calling conventions.
+ * With parameter assignments so arranged, the
+ * {@link com.android.dx.dex.code.RopTranslator} sees Rop {@code move-param}
+ * instructions as unnecessary in dex form and eliminates them.
+ *
+ * <h3>mark-local</h3>
+ *
+ * A {@link RegOps.MARK_LOCAL mark-local} instruction indicates that a local
+ * variable becomes live in a specified register specified register for the
+ * purposes of debug information. A {@code mark-local} instruction has
+ * a single source (the register which will now be considered a local variable)
+ * and no results. The instruction has no side effect.<p>
+ *
+ * In a typical case, a local variable's lifetime begins with an
+ * assignment. The local variable whose information is present in a result's
+ * {@link RegisterSpec#getLocalItem LocalItem} is considered to begin (or move
+ * between registers) when the instruction is executed.<p>
+ *
+ * However, sometimes a local variable can begin its life or move without
+ * an assignment occurring. A common example of this is occurs in the Rop
+ * representation of the following code:<p>
+ *
+ * <pre>
+ * try {
+ * Object foo = null;
+ * foo = new Object();
+ * } catch (Throwable ex) { }
+ * </pre>
+ *
+ * An object's initialization occurs in two steps. First, a
+ * {@code new-instance} instruction is executed, whose result is stored in a
+ * register. However, that register can not yet be considered to contain
+ * "foo". That's because the instance's constructor method must be called
+ * via an {@code invoke} instruction. The constructor method, however, may
+ * throw an exception. And if an exception occurs, then "foo" should remain
+ * null. So "foo" becomes the value of the result of the {@code new-instance}
+ * instruction after the (void) constructor method is invoked and
+ * returns successfully. In such a case, a {@code mark-local} will
+ * typically occur at the beginning of the primary successor block following
+ * the invocation to the constructor.
+ */
diff --git a/dx/src/com/android/dx/rop/type/Prototype.java b/dx/src/com/android/dx/rop/type/Prototype.java
new file mode 100644
index 0000000..cbd5328
--- /dev/null
+++ b/dx/src/com/android/dx/rop/type/Prototype.java
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.type;
+
+import java.util.HashMap;
+
+/**
+ * Representation of a method decriptor. Instances of this class are
+ * generally interned and may be usefully compared with each other
+ * using {@code ==}.
+ */
+public final class Prototype implements Comparable<Prototype> {
+ /** {@code non-null;} intern table mapping string descriptors to instances */
+ private static final HashMap<String, Prototype> internTable =
+ new HashMap<String, Prototype>(500);
+
+ /** {@code non-null;} method descriptor */
+ private final String descriptor;
+
+ /** {@code non-null;} return type */
+ private final Type returnType;
+
+ /** {@code non-null;} list of parameter types */
+ private final StdTypeList parameterTypes;
+
+ /** {@code null-ok;} list of parameter frame types, if calculated */
+ private StdTypeList parameterFrameTypes;
+
+ /**
+ * Returns the unique instance corresponding to the
+ * given method descriptor. See vmspec-2 sec4.3.3 for details on the
+ * field descriptor syntax.
+ *
+ * @param descriptor {@code non-null;} the descriptor
+ * @return {@code non-null;} the corresponding instance
+ * @throws IllegalArgumentException thrown if the descriptor has
+ * invalid syntax
+ */
+ public static Prototype intern(String descriptor) {
+ if (descriptor == null) {
+ throw new NullPointerException("descriptor == null");
+ }
+
+ Prototype result = internTable.get(descriptor);
+ if (result != null) {
+ return result;
+ }
+
+ Type[] params = makeParameterArray(descriptor);
+ int paramCount = 0;
+ int at = 1;
+
+ for (;;) {
+ int startAt = at;
+ char c = descriptor.charAt(at);
+ if (c == ')') {
+ at++;
+ break;
+ }
+
+ // Skip array markers.
+ while (c == '[') {
+ at++;
+ c = descriptor.charAt(at);
+ }
+
+ if (c == 'L') {
+ // It looks like the start of a class name; find the end.
+ int endAt = descriptor.indexOf(';', at);
+ if (endAt == -1) {
+ throw new IllegalArgumentException("bad descriptor");
+ }
+ at = endAt + 1;
+ } else {
+ at++;
+ }
+
+ params[paramCount] =
+ Type.intern(descriptor.substring(startAt, at));
+ paramCount++;
+ }
+
+ Type returnType = Type.internReturnType(descriptor.substring(at));
+ StdTypeList parameterTypes = new StdTypeList(paramCount);
+
+ for (int i = 0; i < paramCount; i++) {
+ parameterTypes.set(i, params[i]);
+ }
+
+ result = new Prototype(descriptor, returnType, parameterTypes);
+ return putIntern(result);
+ }
+
+ /**
+ * Helper for {@link #intern} which returns an empty array to
+ * populate with parsed parameter types, and which also ensures
+ * that there is a '(' at the start of the descriptor and a
+ * single ')' somewhere before the end.
+ *
+ * @param descriptor {@code non-null;} the descriptor string
+ * @return {@code non-null;} array large enough to hold all parsed parameter
+ * types, but which is likely actually larger than needed
+ */
+ private static Type[] makeParameterArray(String descriptor) {
+ int length = descriptor.length();
+
+ if (descriptor.charAt(0) != '(') {
+ throw new IllegalArgumentException("bad descriptor");
+ }
+
+ /*
+ * This is a cheesy way to establish an upper bound on the
+ * number of parameters: Just count capital letters.
+ */
+ int closeAt = 0;
+ int maxParams = 0;
+ for (int i = 1; i < length; i++) {
+ char c = descriptor.charAt(i);
+ if (c == ')') {
+ closeAt = i;
+ break;
+ }
+ if ((c >= 'A') && (c <= 'Z')) {
+ maxParams++;
+ }
+ }
+
+ if ((closeAt == 0) || (closeAt == (length - 1))) {
+ throw new IllegalArgumentException("bad descriptor");
+ }
+
+ if (descriptor.indexOf(')', closeAt + 1) != -1) {
+ throw new IllegalArgumentException("bad descriptor");
+ }
+
+ return new Type[maxParams];
+ }
+
+ /**
+ * Interns an instance, adding to the descriptor as necessary based
+ * on the given definer, name, and flags. For example, an init
+ * method has an uninitialized object of type {@code definer}
+ * as its first argument.
+ *
+ * @param descriptor {@code non-null;} the descriptor string
+ * @param definer {@code non-null;} class the method is defined on
+ * @param isStatic whether this is a static method
+ * @param isInit whether this is an init method
+ * @return {@code non-null;} the interned instance
+ */
+ public static Prototype intern(String descriptor, Type definer,
+ boolean isStatic, boolean isInit) {
+ Prototype base = intern(descriptor);
+
+ if (isStatic) {
+ return base;
+ }
+
+ if (isInit) {
+ definer = definer.asUninitialized(Integer.MAX_VALUE);
+ }
+
+ return base.withFirstParameter(definer);
+ }
+
+ /**
+ * Interns an instance which consists of the given number of
+ * {@code int}s along with the given return type
+ *
+ * @param returnType {@code non-null;} the return type
+ * @param count {@code > 0;} the number of elements in the prototype
+ * @return {@code non-null;} the interned instance
+ */
+ public static Prototype internInts(Type returnType, int count) {
+ // Make the descriptor...
+
+ StringBuffer sb = new StringBuffer(100);
+
+ sb.append('(');
+
+ for (int i = 0; i < count; i++) {
+ sb.append('I');
+ }
+
+ sb.append(')');
+ sb.append(returnType.getDescriptor());
+
+ // ...and intern it.
+ return intern(sb.toString());
+ }
+
+ /**
+ * Constructs an instance. This is a private constructor; use one
+ * of the public static methods to get instances.
+ *
+ * @param descriptor {@code non-null;} the descriptor string
+ */
+ private Prototype(String descriptor, Type returnType,
+ StdTypeList parameterTypes) {
+ if (descriptor == null) {
+ throw new NullPointerException("descriptor == null");
+ }
+
+ if (returnType == null) {
+ throw new NullPointerException("returnType == null");
+ }
+
+ if (parameterTypes == null) {
+ throw new NullPointerException("parameterTypes == null");
+ }
+
+ this.descriptor = descriptor;
+ this.returnType = returnType;
+ this.parameterTypes = parameterTypes;
+ this.parameterFrameTypes = null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ /*
+ * Since externally-visible instances are interned, this
+ * check helps weed out some easy cases.
+ */
+ return true;
+ }
+
+ if (!(other instanceof Prototype)) {
+ return false;
+ }
+
+ return descriptor.equals(((Prototype) other).descriptor);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return descriptor.hashCode();
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(Prototype other) {
+ if (this == other) {
+ return 0;
+ }
+
+ /*
+ * The return type is the major order, and then args in order,
+ * and then the shorter list comes first (similar to string
+ * sorting).
+ */
+
+ int result = returnType.compareTo(other.returnType);
+
+ if (result != 0) {
+ return result;
+ }
+
+ int thisSize = parameterTypes.size();
+ int otherSize = other.parameterTypes.size();
+ int size = Math.min(thisSize, otherSize);
+
+ for (int i = 0; i < size; i++) {
+ Type thisType = parameterTypes.get(i);
+ Type otherType = other.parameterTypes.get(i);
+
+ result = thisType.compareTo(otherType);
+
+ if (result != 0) {
+ return result;
+ }
+ }
+
+ if (thisSize < otherSize) {
+ return -1;
+ } else if (thisSize > otherSize) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return descriptor;
+ }
+
+ /**
+ * Gets the descriptor string.
+ *
+ * @return {@code non-null;} the descriptor
+ */
+ public String getDescriptor() {
+ return descriptor;
+ }
+
+ /**
+ * Gets the return type.
+ *
+ * @return {@code non-null;} the return type
+ */
+ public Type getReturnType() {
+ return returnType;
+ }
+
+ /**
+ * Gets the list of parameter types.
+ *
+ * @return {@code non-null;} the list of parameter types
+ */
+ public StdTypeList getParameterTypes() {
+ return parameterTypes;
+ }
+
+ /**
+ * Gets the list of frame types corresponding to the list of parameter
+ * types. The difference between the two lists (if any) is that all
+ * "intlike" types (see {@link Type#isIntlike}) are replaced by
+ * {@link Type#INT}.
+ *
+ * @return {@code non-null;} the list of parameter frame types
+ */
+ public StdTypeList getParameterFrameTypes() {
+ if (parameterFrameTypes == null) {
+ int sz = parameterTypes.size();
+ StdTypeList list = new StdTypeList(sz);
+ boolean any = false;
+ for (int i = 0; i < sz; i++) {
+ Type one = parameterTypes.get(i);
+ if (one.isIntlike()) {
+ any = true;
+ one = Type.INT;
+ }
+ list.set(i, one);
+ }
+ parameterFrameTypes = any ? list : parameterTypes;
+ }
+
+ return parameterFrameTypes;
+ }
+
+ /**
+ * Returns a new interned instance, which is the same as this instance,
+ * except that it has an additional parameter prepended to the original's
+ * argument list.
+ *
+ * @param param {@code non-null;} the new first parameter
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public Prototype withFirstParameter(Type param) {
+ String newDesc = "(" + param.getDescriptor() + descriptor.substring(1);
+ StdTypeList newParams = parameterTypes.withFirst(param);
+
+ newParams.setImmutable();
+
+ Prototype result =
+ new Prototype(newDesc, returnType, newParams);
+
+ return putIntern(result);
+ }
+
+ /**
+ * Puts the given instance in the intern table if it's not already
+ * there. If a conflicting value is already in the table, then leave it.
+ * Return the interned value.
+ *
+ * @param desc {@code non-null;} instance to make interned
+ * @return {@code non-null;} the actual interned object
+ */
+ private static Prototype putIntern(Prototype desc) {
+ synchronized (internTable) {
+ String descriptor = desc.getDescriptor();
+ Prototype already = internTable.get(descriptor);
+ if (already != null) {
+ return already;
+ }
+ internTable.put(descriptor, desc);
+ return desc;
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/rop/type/StdTypeList.java b/dx/src/com/android/dx/rop/type/StdTypeList.java
new file mode 100644
index 0000000..fe6647c
--- /dev/null
+++ b/dx/src/com/android/dx/rop/type/StdTypeList.java
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.type;
+
+import com.android.dx.util.FixedSizeList;
+
+/**
+ * Standard implementation of {@link TypeList}.
+ */
+public final class StdTypeList
+ extends FixedSizeList implements TypeList {
+ /** {@code non-null;} no-element instance */
+ public static final StdTypeList EMPTY = new StdTypeList(0);
+
+ /** {@code non-null;} the list {@code [int]} */
+ public static final StdTypeList INT = StdTypeList.make(Type.INT);
+
+ /** {@code non-null;} the list {@code [long]} */
+ public static final StdTypeList LONG = StdTypeList.make(Type.LONG);
+
+ /** {@code non-null;} the list {@code [float]} */
+ public static final StdTypeList FLOAT = StdTypeList.make(Type.FLOAT);
+
+ /** {@code non-null;} the list {@code [double]} */
+ public static final StdTypeList DOUBLE = StdTypeList.make(Type.DOUBLE);
+
+ /** {@code non-null;} the list {@code [Object]} */
+ public static final StdTypeList OBJECT = StdTypeList.make(Type.OBJECT);
+
+ /** {@code non-null;} the list {@code [ReturnAddress]} */
+ public static final StdTypeList RETURN_ADDRESS
+ = StdTypeList.make(Type.RETURN_ADDRESS);
+
+ /** {@code non-null;} the list {@code [Throwable]} */
+ public static final StdTypeList THROWABLE =
+ StdTypeList.make(Type.THROWABLE);
+
+ /** {@code non-null;} the list {@code [int, int]} */
+ public static final StdTypeList INT_INT =
+ StdTypeList.make(Type.INT, Type.INT);
+
+ /** {@code non-null;} the list {@code [long, long]} */
+ public static final StdTypeList LONG_LONG =
+ StdTypeList.make(Type.LONG, Type.LONG);
+
+ /** {@code non-null;} the list {@code [float, float]} */
+ public static final StdTypeList FLOAT_FLOAT =
+ StdTypeList.make(Type.FLOAT, Type.FLOAT);
+
+ /** {@code non-null;} the list {@code [double, double]} */
+ public static final StdTypeList DOUBLE_DOUBLE =
+ StdTypeList.make(Type.DOUBLE, Type.DOUBLE);
+
+ /** {@code non-null;} the list {@code [Object, Object]} */
+ public static final StdTypeList OBJECT_OBJECT =
+ StdTypeList.make(Type.OBJECT, Type.OBJECT);
+
+ /** {@code non-null;} the list {@code [int, Object]} */
+ public static final StdTypeList INT_OBJECT =
+ StdTypeList.make(Type.INT, Type.OBJECT);
+
+ /** {@code non-null;} the list {@code [long, Object]} */
+ public static final StdTypeList LONG_OBJECT =
+ StdTypeList.make(Type.LONG, Type.OBJECT);
+
+ /** {@code non-null;} the list {@code [float, Object]} */
+ public static final StdTypeList FLOAT_OBJECT =
+ StdTypeList.make(Type.FLOAT, Type.OBJECT);
+
+ /** {@code non-null;} the list {@code [double, Object]} */
+ public static final StdTypeList DOUBLE_OBJECT =
+ StdTypeList.make(Type.DOUBLE, Type.OBJECT);
+
+ /** {@code non-null;} the list {@code [long, int]} */
+ public static final StdTypeList LONG_INT =
+ StdTypeList.make(Type.LONG, Type.INT);
+
+ /** {@code non-null;} the list {@code [int[], int]} */
+ public static final StdTypeList INTARR_INT =
+ StdTypeList.make(Type.INT_ARRAY, Type.INT);
+
+ /** {@code non-null;} the list {@code [long[], int]} */
+ public static final StdTypeList LONGARR_INT =
+ StdTypeList.make(Type.LONG_ARRAY, Type.INT);
+
+ /** {@code non-null;} the list {@code [float[], int]} */
+ public static final StdTypeList FLOATARR_INT =
+ StdTypeList.make(Type.FLOAT_ARRAY, Type.INT);
+
+ /** {@code non-null;} the list {@code [double[], int]} */
+ public static final StdTypeList DOUBLEARR_INT =
+ StdTypeList.make(Type.DOUBLE_ARRAY, Type.INT);
+
+ /** {@code non-null;} the list {@code [Object[], int]} */
+ public static final StdTypeList OBJECTARR_INT =
+ StdTypeList.make(Type.OBJECT_ARRAY, Type.INT);
+
+ /** {@code non-null;} the list {@code [boolean[], int]} */
+ public static final StdTypeList BOOLEANARR_INT =
+ StdTypeList.make(Type.BOOLEAN_ARRAY, Type.INT);
+
+ /** {@code non-null;} the list {@code [byte[], int]} */
+ public static final StdTypeList BYTEARR_INT =
+ StdTypeList.make(Type.BYTE_ARRAY, Type.INT);
+
+ /** {@code non-null;} the list {@code [char[], int]} */
+ public static final StdTypeList CHARARR_INT =
+ StdTypeList.make(Type.CHAR_ARRAY, Type.INT);
+
+ /** {@code non-null;} the list {@code [short[], int]} */
+ public static final StdTypeList SHORTARR_INT =
+ StdTypeList.make(Type.SHORT_ARRAY, Type.INT);
+
+ /** {@code non-null;} the list {@code [int, int[], int]} */
+ public static final StdTypeList INT_INTARR_INT =
+ StdTypeList.make(Type.INT, Type.INT_ARRAY, Type.INT);
+
+ /** {@code non-null;} the list {@code [long, long[], int]} */
+ public static final StdTypeList LONG_LONGARR_INT =
+ StdTypeList.make(Type.LONG, Type.LONG_ARRAY, Type.INT);
+
+ /** {@code non-null;} the list {@code [float, float[], int]} */
+ public static final StdTypeList FLOAT_FLOATARR_INT =
+ StdTypeList.make(Type.FLOAT, Type.FLOAT_ARRAY, Type.INT);
+
+ /** {@code non-null;} the list {@code [double, double[], int]} */
+ public static final StdTypeList DOUBLE_DOUBLEARR_INT =
+ StdTypeList.make(Type.DOUBLE, Type.DOUBLE_ARRAY, Type.INT);
+
+ /** {@code non-null;} the list {@code [Object, Object[], int]} */
+ public static final StdTypeList OBJECT_OBJECTARR_INT =
+ StdTypeList.make(Type.OBJECT, Type.OBJECT_ARRAY, Type.INT);
+
+ /** {@code non-null;} the list {@code [int, boolean[], int]} */
+ public static final StdTypeList INT_BOOLEANARR_INT =
+ StdTypeList.make(Type.INT, Type.BOOLEAN_ARRAY, Type.INT);
+
+ /** {@code non-null;} the list {@code [int, byte[], int]} */
+ public static final StdTypeList INT_BYTEARR_INT =
+ StdTypeList.make(Type.INT, Type.BYTE_ARRAY, Type.INT);
+
+ /** {@code non-null;} the list {@code [int, char[], int]} */
+ public static final StdTypeList INT_CHARARR_INT =
+ StdTypeList.make(Type.INT, Type.CHAR_ARRAY, Type.INT);
+
+ /** {@code non-null;} the list {@code [int, short[], int]} */
+ public static final StdTypeList INT_SHORTARR_INT =
+ StdTypeList.make(Type.INT, Type.SHORT_ARRAY, Type.INT);
+
+ /**
+ * Makes a single-element instance.
+ *
+ * @param type {@code non-null;} the element
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public static StdTypeList make(Type type) {
+ StdTypeList result = new StdTypeList(1);
+ result.set(0, type);
+ return result;
+ }
+
+ /**
+ * Makes a two-element instance.
+ *
+ * @param type0 {@code non-null;} the first element
+ * @param type1 {@code non-null;} the second element
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public static StdTypeList make(Type type0, Type type1) {
+ StdTypeList result = new StdTypeList(2);
+ result.set(0, type0);
+ result.set(1, type1);
+ return result;
+ }
+
+ /**
+ * Makes a three-element instance.
+ *
+ * @param type0 {@code non-null;} the first element
+ * @param type1 {@code non-null;} the second element
+ * @param type2 {@code non-null;} the third element
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public static StdTypeList make(Type type0, Type type1, Type type2) {
+ StdTypeList result = new StdTypeList(3);
+ result.set(0, type0);
+ result.set(1, type1);
+ result.set(2, type2);
+ return result;
+ }
+
+ /**
+ * Makes a four-element instance.
+ *
+ * @param type0 {@code non-null;} the first element
+ * @param type1 {@code non-null;} the second element
+ * @param type2 {@code non-null;} the third element
+ * @param type3 {@code non-null;} the fourth element
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public static StdTypeList make(Type type0, Type type1, Type type2,
+ Type type3) {
+ StdTypeList result = new StdTypeList(4);
+ result.set(0, type0);
+ result.set(1, type1);
+ result.set(2, type2);
+ result.set(3, type3);
+ return result;
+ }
+
+ /**
+ * Returns the given list as a comma-separated list of human forms. This
+ * is a static method so as to work on arbitrary {@link TypeList}
+ * instances.
+ *
+ * @param list {@code non-null;} the list to convert
+ * @return {@code non-null;} the human form
+ */
+ public static String toHuman(TypeList list) {
+ int size = list.size();
+
+ if (size == 0) {
+ return "<empty>";
+ }
+
+ StringBuffer sb = new StringBuffer(100);
+
+ for (int i = 0; i < size; i++) {
+ if (i != 0) {
+ sb.append(", ");
+ }
+ sb.append(list.getType(i).toHuman());
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Returns a hashcode of the contents of the given list. This
+ * is a static method so as to work on arbitrary {@link TypeList}
+ * instances.
+ *
+ * @param list {@code non-null;} the list to inspect
+ * @return {@code non-null;} the hash code
+ */
+ public static int hashContents(TypeList list) {
+ int size = list.size();
+ int hash = 0;
+
+ for (int i = 0; i < size; i++) {
+ hash = (hash * 31) + list.getType(i).hashCode();
+ }
+
+ return hash;
+ }
+
+ /**
+ * Compares the contents of the given two instances for equality. This
+ * is a static method so as to work on arbitrary {@link TypeList}
+ * instances.
+ *
+ * @param list1 {@code non-null;} one list to compare
+ * @param list2 {@code non-null;} another list to compare
+ * @return whether the two lists contain corresponding equal elements
+ */
+ public static boolean equalContents(TypeList list1, TypeList list2) {
+ int size = list1.size();
+
+ if (list2.size() != size) {
+ return false;
+ }
+
+ for (int i = 0; i < size; i++) {
+ if (! list1.getType(i).equals(list2.getType(i))) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Compares the contents of the given two instances for ordering. This
+ * is a static method so as to work on arbitrary {@link TypeList}
+ * instances.
+ *
+ * @param list1 {@code non-null;} one list to compare
+ * @param list2 {@code non-null;} another list to compare
+ * @return the order of the two lists
+ */
+ public static int compareContents(TypeList list1, TypeList list2) {
+ int size1 = list1.size();
+ int size2 = list2.size();
+ int size = Math.min(size1, size2);
+
+ for (int i = 0; i < size; i++) {
+ int comparison = list1.getType(i).compareTo(list2.getType(i));
+ if (comparison != 0) {
+ return comparison;
+ }
+ }
+
+ if (size1 == size2) {
+ return 0;
+ } else if (size1 < size2) {
+ return -1;
+ } else {
+ return 1;
+ }
+ }
+
+ /**
+ * Constructs an instance. All indices initially contain {@code null}.
+ *
+ * @param size the size of the list
+ */
+ public StdTypeList(int size) {
+ super(size);
+ }
+
+ /** {@inheritDoc} */
+ public Type getType(int n) {
+ return get(n);
+ }
+
+ /** {@inheritDoc} */
+ public int getWordCount() {
+ int sz = size();
+ int result = 0;
+
+ for (int i = 0; i < sz; i++) {
+ result += get(i).getCategory();
+ }
+
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ public TypeList withAddedType(Type type) {
+ int sz = size();
+ StdTypeList result = new StdTypeList(sz + 1);
+
+ for (int i = 0; i < sz; i++) {
+ result.set0(i, get0(i));
+ }
+
+ result.set(sz, type);
+ result.setImmutable();
+ return result;
+ }
+
+ /**
+ * Gets the indicated element. It is an error to call this with the
+ * index for an element which was never set; if you do that, this
+ * will throw {@code NullPointerException}.
+ *
+ * @param n {@code >= 0, < size();} which element
+ * @return {@code non-null;} the indicated element
+ */
+ public Type get(int n) {
+ return (Type) get0(n);
+ }
+
+ /**
+ * Sets the type at the given index.
+ *
+ * @param n {@code >= 0, < size();} which element
+ * @param type {@code non-null;} the type to store
+ */
+ public void set(int n, Type type) {
+ set0(n, type);
+ }
+
+ /**
+ * Returns a new instance, which is the same as this instance,
+ * except that it has an additional type prepended to the
+ * original.
+ *
+ * @param type {@code non-null;} the new first element
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public StdTypeList withFirst(Type type) {
+ int sz = size();
+ StdTypeList result = new StdTypeList(sz + 1);
+
+ result.set0(0, type);
+ for (int i = 0; i < sz; i++) {
+ result.set0(i + 1, getOrNull0(i));
+ }
+
+ return result;
+ }
+}
diff --git a/dx/src/com/android/dx/rop/type/Type.java b/dx/src/com/android/dx/rop/type/Type.java
new file mode 100644
index 0000000..16b16f5
--- /dev/null
+++ b/dx/src/com/android/dx/rop/type/Type.java
@@ -0,0 +1,855 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.type;
+
+import com.android.dx.util.Hex;
+
+import java.util.HashMap;
+
+/**
+ * Representation of a value type, such as may appear in a field, in a
+ * local, on a stack, or in a method descriptor. Instances of this
+ * class are generally interned and may be usefully compared with each
+ * other using {@code ==}.
+ */
+public final class Type implements TypeBearer, Comparable<Type> {
+ /** {@code non-null;} intern table mapping string descriptors to instances */
+ private static final HashMap<String, Type> internTable =
+ new HashMap<String, Type>(500);
+
+ /** basic type constant for {@code void} */
+ public static final int BT_VOID = 0;
+
+ /** basic type constant for {@code boolean} */
+ public static final int BT_BOOLEAN = 1;
+
+ /** basic type constant for {@code byte} */
+ public static final int BT_BYTE = 2;
+
+ /** basic type constant for {@code char} */
+ public static final int BT_CHAR = 3;
+
+ /** basic type constant for {@code double} */
+ public static final int BT_DOUBLE = 4;
+
+ /** basic type constant for {@code float} */
+ public static final int BT_FLOAT = 5;
+
+ /** basic type constant for {@code int} */
+ public static final int BT_INT = 6;
+
+ /** basic type constant for {@code long} */
+ public static final int BT_LONG = 7;
+
+ /** basic type constant for {@code short} */
+ public static final int BT_SHORT = 8;
+
+ /** basic type constant for {@code Object} */
+ public static final int BT_OBJECT = 9;
+
+ /** basic type constant for a return address */
+ public static final int BT_ADDR = 10;
+
+ /** count of basic type constants */
+ public static final int BT_COUNT = 11;
+
+ /** {@code non-null;} instance representing {@code boolean} */
+ public static final Type BOOLEAN = new Type("Z", BT_BOOLEAN);
+
+ /** {@code non-null;} instance representing {@code byte} */
+ public static final Type BYTE = new Type("B", BT_BYTE);
+
+ /** {@code non-null;} instance representing {@code char} */
+ public static final Type CHAR = new Type("C", BT_CHAR);
+
+ /** {@code non-null;} instance representing {@code double} */
+ public static final Type DOUBLE = new Type("D", BT_DOUBLE);
+
+ /** {@code non-null;} instance representing {@code float} */
+ public static final Type FLOAT = new Type("F", BT_FLOAT);
+
+ /** {@code non-null;} instance representing {@code int} */
+ public static final Type INT = new Type("I", BT_INT);
+
+ /** {@code non-null;} instance representing {@code long} */
+ public static final Type LONG = new Type("J", BT_LONG);
+
+ /** {@code non-null;} instance representing {@code short} */
+ public static final Type SHORT = new Type("S", BT_SHORT);
+
+ /** {@code non-null;} instance representing {@code void} */
+ public static final Type VOID = new Type("V", BT_VOID);
+
+ /** {@code non-null;} instance representing a known-{@code null} */
+ public static final Type KNOWN_NULL = new Type("<null>", BT_OBJECT);
+
+ /** {@code non-null;} instance representing a subroutine return address */
+ public static final Type RETURN_ADDRESS = new Type("<addr>", BT_ADDR);
+
+ static {
+ /*
+ * Put all the primitive types into the intern table. This needs
+ * to happen before the array types below get interned.
+ */
+ putIntern(BOOLEAN);
+ putIntern(BYTE);
+ putIntern(CHAR);
+ putIntern(DOUBLE);
+ putIntern(FLOAT);
+ putIntern(INT);
+ putIntern(LONG);
+ putIntern(SHORT);
+ /*
+ * Note: VOID isn't put in the intern table, since it's special and
+ * shouldn't be found by a normal call to intern().
+ */
+ }
+
+ /**
+ * {@code non-null;} instance representing
+ * {@code java.lang.annotation.Annotation}
+ */
+ public static final Type ANNOTATION =
+ intern("Ljava/lang/annotation/Annotation;");
+
+ /** {@code non-null;} instance representing {@code java.lang.Class} */
+ public static final Type CLASS = intern("Ljava/lang/Class;");
+
+ /** {@code non-null;} instance representing {@code java.lang.Cloneable} */
+ public static final Type CLONEABLE = intern("Ljava/lang/Cloneable;");
+
+ /** {@code non-null;} instance representing {@code java.lang.Object} */
+ public static final Type OBJECT = intern("Ljava/lang/Object;");
+
+ /** {@code non-null;} instance representing {@code java.io.Serializable} */
+ public static final Type SERIALIZABLE = intern("Ljava/io/Serializable;");
+
+ /** {@code non-null;} instance representing {@code java.lang.String} */
+ public static final Type STRING = intern("Ljava/lang/String;");
+
+ /** {@code non-null;} instance representing {@code java.lang.Throwable} */
+ public static final Type THROWABLE = intern("Ljava/lang/Throwable;");
+
+ /**
+ * {@code non-null;} instance representing {@code java.lang.Boolean}; the
+ * suffix on the name helps disambiguate this from the instance
+ * representing a primitive type
+ */
+ public static final Type BOOLEAN_CLASS = intern("Ljava/lang/Boolean;");
+
+ /**
+ * {@code non-null;} instance representing {@code java.lang.Byte}; the
+ * suffix on the name helps disambiguate this from the instance
+ * representing a primitive type
+ */
+ public static final Type BYTE_CLASS = intern("Ljava/lang/Byte;");
+
+ /**
+ * {@code non-null;} instance representing {@code java.lang.Character}; the
+ * suffix on the name helps disambiguate this from the instance
+ * representing a primitive type
+ */
+ public static final Type CHARACTER_CLASS = intern("Ljava/lang/Character;");
+
+ /**
+ * {@code non-null;} instance representing {@code java.lang.Double}; the
+ * suffix on the name helps disambiguate this from the instance
+ * representing a primitive type
+ */
+ public static final Type DOUBLE_CLASS = intern("Ljava/lang/Double;");
+
+ /**
+ * {@code non-null;} instance representing {@code java.lang.Float}; the
+ * suffix on the name helps disambiguate this from the instance
+ * representing a primitive type
+ */
+ public static final Type FLOAT_CLASS = intern("Ljava/lang/Float;");
+
+ /**
+ * {@code non-null;} instance representing {@code java.lang.Integer}; the
+ * suffix on the name helps disambiguate this from the instance
+ * representing a primitive type
+ */
+ public static final Type INTEGER_CLASS = intern("Ljava/lang/Integer;");
+
+ /**
+ * {@code non-null;} instance representing {@code java.lang.Long}; the
+ * suffix on the name helps disambiguate this from the instance
+ * representing a primitive type
+ */
+ public static final Type LONG_CLASS = intern("Ljava/lang/Long;");
+
+ /**
+ * {@code non-null;} instance representing {@code java.lang.Short}; the
+ * suffix on the name helps disambiguate this from the instance
+ * representing a primitive type
+ */
+ public static final Type SHORT_CLASS = intern("Ljava/lang/Short;");
+
+ /**
+ * {@code non-null;} instance representing {@code java.lang.Void}; the
+ * suffix on the name helps disambiguate this from the instance
+ * representing a primitive type
+ */
+ public static final Type VOID_CLASS = intern("Ljava/lang/Void;");
+
+ /** {@code non-null;} instance representing {@code boolean[]} */
+ public static final Type BOOLEAN_ARRAY = BOOLEAN.getArrayType();
+
+ /** {@code non-null;} instance representing {@code byte[]} */
+ public static final Type BYTE_ARRAY = BYTE.getArrayType();
+
+ /** {@code non-null;} instance representing {@code char[]} */
+ public static final Type CHAR_ARRAY = CHAR.getArrayType();
+
+ /** {@code non-null;} instance representing {@code double[]} */
+ public static final Type DOUBLE_ARRAY = DOUBLE.getArrayType();
+
+ /** {@code non-null;} instance representing {@code float[]} */
+ public static final Type FLOAT_ARRAY = FLOAT.getArrayType();
+
+ /** {@code non-null;} instance representing {@code int[]} */
+ public static final Type INT_ARRAY = INT.getArrayType();
+
+ /** {@code non-null;} instance representing {@code long[]} */
+ public static final Type LONG_ARRAY = LONG.getArrayType();
+
+ /** {@code non-null;} instance representing {@code Object[]} */
+ public static final Type OBJECT_ARRAY = OBJECT.getArrayType();
+
+ /** {@code non-null;} instance representing {@code short[]} */
+ public static final Type SHORT_ARRAY = SHORT.getArrayType();
+
+ /** {@code non-null;} field descriptor for the type */
+ private final String descriptor;
+
+ /**
+ * basic type corresponding to this type; one of the
+ * {@code BT_*} constants
+ */
+ private final int basicType;
+
+ /**
+ * {@code >= -1;} for an uninitialized type, bytecode index that this
+ * instance was allocated at; {@code Integer.MAX_VALUE} if it
+ * was an incoming uninitialized instance; {@code -1} if this
+ * is an <i>inititialized</i> instance
+ */
+ private final int newAt;
+
+ /**
+ * {@code null-ok;} the internal-form class name corresponding to this type, if
+ * calculated; only valid if {@code this} is a reference type and
+ * additionally not a return address
+ */
+ private String className;
+
+ /**
+ * {@code null-ok;} the type corresponding to an array of this type, if
+ * calculated
+ */
+ private Type arrayType;
+
+ /**
+ * {@code null-ok;} the type corresponding to elements of this type, if
+ * calculated; only valid if {@code this} is an array type
+ */
+ private Type componentType;
+
+ /**
+ * {@code null-ok;} the type corresponding to the initialized version of
+ * this type, if this instance is in fact an uninitialized type
+ */
+ private Type initializedType;
+
+ /**
+ * Returns the unique instance corresponding to the type with the
+ * given descriptor. See vmspec-2 sec4.3.2 for details on the
+ * field descriptor syntax. This method does <i>not</i> allow
+ * {@code "V"} (that is, type {@code void}) as a valid
+ * descriptor.
+ *
+ * @param descriptor {@code non-null;} the descriptor
+ * @return {@code non-null;} the corresponding instance
+ * @throws IllegalArgumentException thrown if the descriptor has
+ * invalid syntax
+ */
+ public static Type intern(String descriptor) {
+ Type result = internTable.get(descriptor);
+ if (result != null) {
+ return result;
+ }
+
+ char firstChar;
+ try {
+ firstChar = descriptor.charAt(0);
+ } catch (IndexOutOfBoundsException ex) {
+ // Translate the exception.
+ throw new IllegalArgumentException("descriptor is empty");
+ } catch (NullPointerException ex) {
+ // Elucidate the exception.
+ throw new NullPointerException("descriptor == null");
+ }
+
+ if (firstChar == '[') {
+ /*
+ * Recursively strip away array markers to get at the underlying
+ * type, and build back on to form the result.
+ */
+ result = intern(descriptor.substring(1));
+ return result.getArrayType();
+ }
+
+ /*
+ * If the first character isn't '[' and it wasn't found in the
+ * intern cache, then it had better be the descriptor for a class.
+ */
+
+ int length = descriptor.length();
+ if ((firstChar != 'L') ||
+ (descriptor.charAt(length - 1) != ';')) {
+ throw new IllegalArgumentException("bad descriptor");
+ }
+
+ /*
+ * Validate the characters of the class name itself. Note that
+ * vmspec-2 does not have a coherent definition for valid
+ * internal-form class names, and the definition here is fairly
+ * liberal: A name is considered valid as long as it doesn't
+ * contain any of '[' ';' '.' '(' ')', and it has no more than one
+ * '/' in a row, and no '/' at either end.
+ */
+
+ int limit = (length - 1); // Skip the final ';'.
+ for (int i = 1; i < limit; i++) {
+ char c = descriptor.charAt(i);
+ switch (c) {
+ case '[':
+ case ';':
+ case '.':
+ case '(':
+ case ')': {
+ throw new IllegalArgumentException("bad descriptor");
+ }
+ case '/': {
+ if ((i == 1) ||
+ (i == (length - 1)) ||
+ (descriptor.charAt(i - 1) == '/')) {
+ throw new IllegalArgumentException("bad descriptor");
+ }
+ break;
+ }
+ }
+ }
+
+ result = new Type(descriptor, BT_OBJECT);
+ return putIntern(result);
+ }
+
+ /**
+ * Returns the unique instance corresponding to the type with the
+ * given descriptor, allowing {@code "V"} to return the type
+ * for {@code void}. Other than that one caveat, this method
+ * is identical to {@link #intern}.
+ *
+ * @param descriptor {@code non-null;} the descriptor
+ * @return {@code non-null;} the corresponding instance
+ * @throws IllegalArgumentException thrown if the descriptor has
+ * invalid syntax
+ */
+ public static Type internReturnType(String descriptor) {
+ try {
+ if (descriptor.equals("V")) {
+ // This is the one special case where void may be returned.
+ return VOID;
+ }
+ } catch (NullPointerException ex) {
+ // Elucidate the exception.
+ throw new NullPointerException("descriptor == null");
+ }
+
+ return intern(descriptor);
+ }
+
+ /**
+ * Returns the unique instance corresponding to the type of the
+ * class with the given name. Calling this method is equivalent to
+ * calling {@code intern(name)} if {@code name} begins
+ * with {@code "["} and calling {@code intern("L" + name + ";")}
+ * in all other cases.
+ *
+ * @param name {@code non-null;} the name of the class whose type is desired
+ * @return {@code non-null;} the corresponding type
+ * @throws IllegalArgumentException thrown if the name has
+ * invalid syntax
+ */
+ public static Type internClassName(String name) {
+ if (name == null) {
+ throw new NullPointerException("name == null");
+ }
+
+ if (name.startsWith("[")) {
+ return intern(name);
+ }
+
+ return intern('L' + name + ';');
+ }
+
+ /**
+ * Constructs an instance corresponding to an "uninitialized type."
+ * This is a private constructor; use one of the public static
+ * methods to get instances.
+ *
+ * @param descriptor {@code non-null;} the field descriptor for the type
+ * @param basicType basic type corresponding to this type; one of the
+ * {@code BT_*} constants
+ * @param newAt {@code >= -1;} allocation bytecode index
+ */
+ private Type(String descriptor, int basicType, int newAt) {
+ if (descriptor == null) {
+ throw new NullPointerException("descriptor == null");
+ }
+
+ if ((basicType < 0) || (basicType >= BT_COUNT)) {
+ throw new IllegalArgumentException("bad basicType");
+ }
+
+ if (newAt < -1) {
+ throw new IllegalArgumentException("newAt < -1");
+ }
+
+ this.descriptor = descriptor;
+ this.basicType = basicType;
+ this.newAt = newAt;
+ this.arrayType = null;
+ this.componentType = null;
+ this.initializedType = null;
+ }
+
+ /**
+ * Constructs an instance corresponding to an "initialized type."
+ * This is a private constructor; use one of the public static
+ * methods to get instances.
+ *
+ * @param descriptor {@code non-null;} the field descriptor for the type
+ * @param basicType basic type corresponding to this type; one of the
+ * {@code BT_*} constants
+ */
+ private Type(String descriptor, int basicType) {
+ this(descriptor, basicType, -1);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ /*
+ * Since externally-visible types are interned, this check
+ * helps weed out some easy cases.
+ */
+ return true;
+ }
+
+ if (!(other instanceof Type)) {
+ return false;
+ }
+
+ return descriptor.equals(((Type) other).descriptor);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return descriptor.hashCode();
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(Type other) {
+ return descriptor.compareTo(other.descriptor);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return descriptor;
+ }
+
+ /** {@inheritDoc} */
+ public String toHuman() {
+ switch (basicType) {
+ case BT_VOID: return "void";
+ case BT_BOOLEAN: return "boolean";
+ case BT_BYTE: return "byte";
+ case BT_CHAR: return "char";
+ case BT_DOUBLE: return "double";
+ case BT_FLOAT: return "float";
+ case BT_INT: return "int";
+ case BT_LONG: return "long";
+ case BT_SHORT: return "short";
+ case BT_OBJECT: break;
+ default: return descriptor;
+ }
+
+ if (isArray()) {
+ return getComponentType().toHuman() + "[]";
+ }
+
+ // Remove the "L...;" around the type and convert "/" to ".".
+ return getClassName().replace("/", ".");
+ }
+
+ /** {@inheritDoc} */
+ public Type getType() {
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ public Type getFrameType() {
+ switch (basicType) {
+ case BT_BOOLEAN:
+ case BT_BYTE:
+ case BT_CHAR:
+ case BT_INT:
+ case BT_SHORT: {
+ return INT;
+ }
+ }
+
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ public int getBasicType() {
+ return basicType;
+ }
+
+ /** {@inheritDoc} */
+ public int getBasicFrameType() {
+ switch (basicType) {
+ case BT_BOOLEAN:
+ case BT_BYTE:
+ case BT_CHAR:
+ case BT_INT:
+ case BT_SHORT: {
+ return BT_INT;
+ }
+ }
+
+ return basicType;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isConstant() {
+ return false;
+ }
+
+ /**
+ * Gets the descriptor.
+ *
+ * @return {@code non-null;} the descriptor
+ */
+ public String getDescriptor() {
+ return descriptor;
+ }
+
+ /**
+ * Gets the name of the class this type corresponds to, in internal
+ * form. This method is only valid if this instance is for a
+ * normal reference type (that is, a reference type and
+ * additionally not a return address).
+ *
+ * @return {@code non-null;} the internal-form class name
+ */
+ public String getClassName() {
+ if (className == null) {
+ if (!isReference()) {
+ throw new IllegalArgumentException("not an object type: " +
+ descriptor);
+ }
+
+ if (descriptor.charAt(0) == '[') {
+ className = descriptor;
+ } else {
+ className = descriptor.substring(1, descriptor.length() - 1);
+ }
+ }
+
+ return className;
+ }
+
+ /**
+ * Gets the category. Most instances are category 1. {@code long}
+ * and {@code double} are the only category 2 types.
+ *
+ * @see #isCategory1
+ * @see #isCategory2
+ * @return the category
+ */
+ public int getCategory() {
+ switch (basicType) {
+ case BT_LONG:
+ case BT_DOUBLE: {
+ return 2;
+ }
+ }
+
+ return 1;
+ }
+
+ /**
+ * Returns whether or not this is a category 1 type.
+ *
+ * @see #getCategory
+ * @see #isCategory2
+ * @return whether or not this is a category 1 type
+ */
+ public boolean isCategory1() {
+ switch (basicType) {
+ case BT_LONG:
+ case BT_DOUBLE: {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns whether or not this is a category 2 type.
+ *
+ * @see #getCategory
+ * @see #isCategory1
+ * @return whether or not this is a category 2 type
+ */
+ public boolean isCategory2() {
+ switch (basicType) {
+ case BT_LONG:
+ case BT_DOUBLE: {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Gets whether this type is "intlike." An intlike type is one which, when
+ * placed on a stack or in a local, is automatically converted to an
+ * {@code int}.
+ *
+ * @return whether this type is "intlike"
+ */
+ public boolean isIntlike() {
+ switch (basicType) {
+ case BT_BOOLEAN:
+ case BT_BYTE:
+ case BT_CHAR:
+ case BT_INT:
+ case BT_SHORT: {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Gets whether this type is a primitive type. All types are either
+ * primitive or reference types.
+ *
+ * @return whether this type is primitive
+ */
+ public boolean isPrimitive() {
+ switch (basicType) {
+ case BT_BOOLEAN:
+ case BT_BYTE:
+ case BT_CHAR:
+ case BT_DOUBLE:
+ case BT_FLOAT:
+ case BT_INT:
+ case BT_LONG:
+ case BT_SHORT:
+ case BT_VOID: {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Gets whether this type is a normal reference type. A normal
+ * reference type is a reference type that is not a return
+ * address. This method is just convenient shorthand for
+ * {@code getBasicType() == Type.BT_OBJECT}.
+ *
+ * @return whether this type is a normal reference type
+ */
+ public boolean isReference() {
+ return (basicType == BT_OBJECT);
+ }
+
+ /**
+ * Gets whether this type is an array type. If this method returns
+ * {@code true}, then it is safe to use {@link #getComponentType}
+ * to determine the component type.
+ *
+ * @return whether this type is an array type
+ */
+ public boolean isArray() {
+ return (descriptor.charAt(0) == '[');
+ }
+
+ /**
+ * Gets whether this type is an array type or is a known-null, and
+ * hence is compatible with array types.
+ *
+ * @return whether this type is an array type
+ */
+ public boolean isArrayOrKnownNull() {
+ return isArray() || equals(KNOWN_NULL);
+ }
+
+ /**
+ * Gets whether this type represents an uninitialized instance. An
+ * uninitialized instance is what one gets back from the {@code new}
+ * opcode, and remains uninitialized until a valid constructor is
+ * invoked on it.
+ *
+ * @return whether this type is "uninitialized"
+ */
+ public boolean isUninitialized() {
+ return (newAt >= 0);
+ }
+
+ /**
+ * Gets the bytecode index at which this uninitialized type was
+ * allocated. This returns {@code Integer.MAX_VALUE} if this
+ * type is an uninitialized incoming parameter (i.e., the
+ * {@code this} of an {@code <init>} method) or
+ * {@code -1} if this type is in fact <i>initialized</i>.
+ *
+ * @return {@code >= -1;} the allocation bytecode index
+ */
+ public int getNewAt() {
+ return newAt;
+ }
+
+ /**
+ * Gets the initialized type corresponding to this instance, but only
+ * if this instance is in fact an uninitialized object type.
+ *
+ * @return {@code non-null;} the initialized type
+ */
+ public Type getInitializedType() {
+ if (initializedType == null) {
+ throw new IllegalArgumentException("initialized type: " +
+ descriptor);
+ }
+
+ return initializedType;
+ }
+
+ /**
+ * Gets the type corresponding to an array of this type.
+ *
+ * @return {@code non-null;} the array type
+ */
+ public Type getArrayType() {
+ if (arrayType == null) {
+ arrayType = putIntern(new Type('[' + descriptor, BT_OBJECT));
+ }
+
+ return arrayType;
+ }
+
+ /**
+ * Gets the component type of this type. This method is only valid on
+ * array types.
+ *
+ * @return {@code non-null;} the component type
+ */
+ public Type getComponentType() {
+ if (componentType == null) {
+ if (descriptor.charAt(0) != '[') {
+ throw new IllegalArgumentException("not an array type: " +
+ descriptor);
+ }
+ componentType = intern(descriptor.substring(1));
+ }
+
+ return componentType;
+ }
+
+ /**
+ * Returns a new interned instance which is identical to this one, except
+ * it is indicated as uninitialized and allocated at the given bytecode
+ * index. This instance must be an initialized object type.
+ *
+ * @param newAt {@code >= 0;} the allocation bytecode index
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public Type asUninitialized(int newAt) {
+ if (newAt < 0) {
+ throw new IllegalArgumentException("newAt < 0");
+ }
+
+ if (!isReference()) {
+ throw new IllegalArgumentException("not a reference type: " +
+ descriptor);
+ }
+
+ if (isUninitialized()) {
+ /*
+ * Dealing with uninitialized types as a starting point is
+ * a pain, and it's not clear that it'd ever be used, so
+ * just disallow it.
+ */
+ throw new IllegalArgumentException("already uninitialized: " +
+ descriptor);
+ }
+
+ /*
+ * Create a new descriptor that is unique and shouldn't conflict
+ * with "normal" type descriptors
+ */
+ String newDesc = 'N' + Hex.u2(newAt) + descriptor;
+ Type result = new Type(newDesc, BT_OBJECT, newAt);
+ result.initializedType = this;
+ return putIntern(result);
+ }
+
+ /**
+ * Puts the given instance in the intern table if it's not already
+ * there. If a conflicting value is already in the table, then leave it.
+ * Return the interned value.
+ *
+ * @param type {@code non-null;} instance to make interned
+ * @return {@code non-null;} the actual interned object
+ */
+ private static Type putIntern(Type type) {
+ synchronized (internTable) {
+ String descriptor = type.getDescriptor();
+ Type already = internTable.get(descriptor);
+ if (already != null) {
+ return already;
+ }
+ internTable.put(descriptor, type);
+ return type;
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/rop/type/TypeBearer.java b/dx/src/com/android/dx/rop/type/TypeBearer.java
new file mode 100644
index 0000000..b03dbaf
--- /dev/null
+++ b/dx/src/com/android/dx/rop/type/TypeBearer.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.type;
+
+import com.android.dx.util.ToHuman;
+
+/**
+ * Object which has an associated type, possibly itself.
+ */
+public interface TypeBearer
+ extends ToHuman {
+ /**
+ * Gets the type associated with this instance.
+ *
+ * @return {@code non-null;} the type
+ */
+ public Type getType();
+
+ /**
+ * Gets the frame type corresponding to this type. This method returns
+ * {@code this}, except if {@link Type#isIntlike} on the underlying
+ * type returns {@code true} but the underlying type is not in
+ * fact {@link Type#INT}, in which case this method returns an instance
+ * whose underlying type <i>is</i> {@code INT}.
+ *
+ * @return {@code non-null;} the frame type for this instance
+ */
+ public TypeBearer getFrameType();
+
+ /**
+ * Gets the basic type corresponding to this instance.
+ *
+ * @return the basic type; one of the {@code BT_*} constants
+ * defined by {@link Type}
+ */
+ public int getBasicType();
+
+ /**
+ * Gets the basic type corresponding to this instance's frame type. This
+ * is equivalent to {@code getFrameType().getBasicType()}, and
+ * is the same as calling {@code getFrameType()} unless this
+ * instance is an int-like type, in which case this method returns
+ * {@code BT_INT}.
+ *
+ * @see #getBasicType
+ * @see #getFrameType
+ *
+ * @return the basic frame type; one of the {@code BT_*} constants
+ * defined by {@link Type}
+ */
+ public int getBasicFrameType();
+
+ /**
+ * Returns whether this instance represents a constant value.
+ *
+ * @return {@code true} if this instance represents a constant value
+ * and {@code false} if not
+ */
+ public boolean isConstant();
+}
diff --git a/dx/src/com/android/dx/rop/type/TypeList.java b/dx/src/com/android/dx/rop/type/TypeList.java
new file mode 100644
index 0000000..de2d62e
--- /dev/null
+++ b/dx/src/com/android/dx/rop/type/TypeList.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.rop.type;
+
+/**
+ * List of {@link Type} instances (or of things that contain types).
+ */
+public interface TypeList {
+ /**
+ * Returns whether this instance is mutable. Note that the
+ * {@code TypeList} interface itself doesn't provide any
+ * means of mutation, but that doesn't mean that there isn't an
+ * extra-interface way of mutating an instance.
+ *
+ * @return {@code true} if this instance is mutable or
+ * {@code false} if it is immutable
+ */
+ public boolean isMutable();
+
+ /**
+ * Gets the size of this list.
+ *
+ * @return {@code >= 0;} the size
+ */
+ public int size();
+
+ /**
+ * Gets the indicated element. It is an error to call this with the
+ * index for an element which was never set; if you do that, this
+ * will throw {@code NullPointerException}.
+ *
+ * @param n {@code >= 0, < size();} which element
+ * @return {@code non-null;} the indicated element
+ */
+ public Type getType(int n);
+
+ /**
+ * Gets the number of 32-bit words required to hold instances of
+ * all the elements of this list. This is a sum of the widths (categories)
+ * of all the elements.
+ *
+ * @return {@code >= 0;} the required number of words
+ */
+ public int getWordCount();
+
+ /**
+ * Returns a new instance which is identical to this one, except that
+ * the given item is appended to the end and it is guaranteed to be
+ * immutable.
+ *
+ * @param type {@code non-null;} item to append
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public TypeList withAddedType(Type type);
+}
diff --git a/dx/src/com/android/dx/rop/type/package.html b/dx/src/com/android/dx/rop/type/package.html
new file mode 100644
index 0000000..93d9d5f
--- /dev/null
+++ b/dx/src/com/android/dx/rop/type/package.html
@@ -0,0 +1,8 @@
+<body>
+<p>Implementation of classes that represent types (classes or primitives).</p>
+
+<p><b>PACKAGES USED:</b>
+<ul>
+<li><code>com.android.dx.util</code></li>
+</ul>
+</body>
diff --git a/dx/src/com/android/dx/ssa/BasicRegisterMapper.java b/dx/src/com/android/dx/ssa/BasicRegisterMapper.java
new file mode 100644
index 0000000..83045b2
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/BasicRegisterMapper.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.util.IntList;
+
+/**
+ * This class maps one register space into another, with
+ * each mapping built up individually and added via addMapping()
+ */
+public class BasicRegisterMapper extends RegisterMapper {
+ /** indexed by old register, containing new name */
+ private IntList oldToNew;
+
+ /** running count of used registers in new namespace */
+ private int runningCountNewRegisters;
+
+ /**
+ * Creates a new OneToOneRegisterMapper.
+ *
+ * @param countOldRegisters the number of registers in the old name space
+ */
+ public BasicRegisterMapper(int countOldRegisters) {
+ oldToNew = new IntList(countOldRegisters);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getNewRegisterCount() {
+ return runningCountNewRegisters;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RegisterSpec map(RegisterSpec registerSpec) {
+ if (registerSpec == null) {
+ return null;
+ }
+
+ int newReg;
+ try {
+ newReg = oldToNew.get(registerSpec.getReg());
+ } catch (IndexOutOfBoundsException ex) {
+ newReg = -1;
+ }
+
+ if (newReg < 0) {
+ throw new RuntimeException("no mapping specified for register");
+ }
+
+ return registerSpec.withReg(newReg);
+ }
+
+ /**
+ * Returns the new-namespace mapping for the specified
+ * old-namespace register, or -1 if one exists.
+ *
+ * @param oldReg {@code >= 0;} old-namespace register
+ * @return new-namespace register or -1 if none
+ */
+ public int oldToNew(int oldReg) {
+ if (oldReg >= oldToNew.size()) {
+ return -1;
+ }
+
+ return oldToNew.get(oldReg);
+ }
+
+ /** {@inheritDoc} */
+ public String toHuman() {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append("Old\tNew\n");
+ int sz = oldToNew.size();
+
+ for (int i = 0; i < sz; i++) {
+ sb.append(i);
+ sb.append('\t');
+ sb.append(oldToNew.get(i));
+ sb.append('\n');
+ }
+
+ sb.append("new reg count:");
+
+ sb.append(runningCountNewRegisters);
+ sb.append('\n');
+
+ return sb.toString();
+ }
+
+ /**
+ * Adds a mapping to the mapper. If oldReg has already been mapped,
+ * overwrites previous mapping with new mapping.
+ *
+ * @param oldReg {@code >= 0;} old register
+ * @param newReg {@code >= 0;} new register
+ * @param category {@code 1..2;} width of reg
+ */
+ public void addMapping(int oldReg, int newReg, int category) {
+ if (oldReg >= oldToNew.size()) {
+ // expand the array as necessary
+ for (int i = oldReg - oldToNew.size(); i >= 0; i--) {
+ oldToNew.add(-1);
+ }
+ }
+
+ oldToNew.set(oldReg, newReg);
+
+ if (runningCountNewRegisters < (newReg + category)) {
+ runningCountNewRegisters = newReg + category;
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/ssa/ConstCollector.java b/dx/src/com/android/dx/ssa/ConstCollector.java
new file mode 100644
index 0000000..7aa512d
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/ConstCollector.java
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.*;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstString;
+import com.android.dx.rop.cst.TypedConstant;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Collects constants that are used more than once at the top of the
+ * method block. This increases register usage by about 5% but decreases
+ * insn size by about 3%.
+ */
+public class ConstCollector {
+ /** Maximum constants to collect per method. Puts cap on reg use */
+ private static final int MAX_COLLECTED_CONSTANTS = 5;
+
+ /**
+ * Also collect string consts, although they can throw exceptions.
+ * This is off now because it just doesn't seem to gain a whole lot.
+ * TODO if you turn this on, you must change SsaInsn.hasSideEffect()
+ * to return false for const-string insns whose exceptions are not
+ * caught in the current method.
+ */
+ private static boolean COLLECT_STRINGS = false;
+
+ /**
+ * If true, allow one local var to be involved with a collected const.
+ * Turned off because it mostly just inserts more moves.
+ */
+ private static boolean COLLECT_ONE_LOCAL = false;
+
+ /** method we're processing */
+ private final SsaMethod ssaMeth;
+
+ /**
+ * Processes a method.
+ *
+ * @param ssaMethod {@code non-null;} method to process
+ */
+ public static void process(SsaMethod ssaMethod) {
+ ConstCollector cc = new ConstCollector(ssaMethod);
+ cc.run();
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param ssaMethod {@code non-null;} method to process
+ */
+ private ConstCollector(SsaMethod ssaMethod) {
+ this.ssaMeth = ssaMethod;
+ }
+
+ /**
+ * Applies the optimization.
+ */
+ private void run() {
+ int regSz = ssaMeth.getRegCount();
+
+ ArrayList<TypedConstant> constantList
+ = getConstsSortedByCountUse();
+
+ int toCollect = Math.min(constantList.size(), MAX_COLLECTED_CONSTANTS);
+
+ SsaBasicBlock start = ssaMeth.getEntryBlock();
+
+ // Constant to new register containing the constant
+ HashMap<TypedConstant, RegisterSpec> newRegs
+ = new HashMap<TypedConstant, RegisterSpec> (toCollect);
+
+ for (int i = 0; i < toCollect; i++) {
+ TypedConstant cst = constantList.get(i);
+ RegisterSpec result
+ = RegisterSpec.make(ssaMeth.makeNewSsaReg(), cst);
+
+ Rop constRop = Rops.opConst(cst);
+
+ if (constRop.getBranchingness() == Rop.BRANCH_NONE) {
+ start.addInsnToHead(
+ new PlainCstInsn(Rops.opConst(cst),
+ SourcePosition.NO_INFO, result,
+ RegisterSpecList.EMPTY, cst));
+ } else {
+ // We need two new basic blocks along with the new insn
+ SsaBasicBlock entryBlock = ssaMeth.getEntryBlock();
+ SsaBasicBlock successorBlock
+ = entryBlock.getPrimarySuccessor();
+
+ // Insert a block containing the const insn.
+ SsaBasicBlock constBlock
+ = entryBlock.insertNewSuccessor(successorBlock);
+
+ constBlock.replaceLastInsn(
+ new ThrowingCstInsn(constRop, SourcePosition.NO_INFO,
+ RegisterSpecList.EMPTY,
+ StdTypeList.EMPTY, cst));
+
+ // Insert a block containing the move-result-pseudo insn.
+
+ SsaBasicBlock resultBlock
+ = constBlock.insertNewSuccessor(successorBlock);
+ PlainInsn insn
+ = new PlainInsn(
+ Rops.opMoveResultPseudo(result.getTypeBearer()),
+ SourcePosition.NO_INFO,
+ result, RegisterSpecList.EMPTY);
+
+ resultBlock.addInsnToHead(insn);
+ }
+
+ newRegs.put(cst, result);
+ }
+
+ updateConstUses(newRegs, regSz);
+ }
+
+ /**
+ * Gets all of the collectable constant values used in this method,
+ * sorted by most used first. Skips non-collectable consts, such as
+ * non-string object constants
+ *
+ * @return {@code non-null;} list of constants in most-to-least used order
+ */
+ private ArrayList<TypedConstant> getConstsSortedByCountUse() {
+ int regSz = ssaMeth.getRegCount();
+
+ final HashMap<TypedConstant, Integer> countUses
+ = new HashMap<TypedConstant, Integer>();
+
+ /*
+ * Each collected constant can be used by just one local
+ * (used only if COLLECT_ONE_LOCAL is true).
+ */
+ final HashSet<TypedConstant> usedByLocal
+ = new HashSet<TypedConstant>();
+
+ // Count how many times each const value is used.
+ for (int i = 0; i < regSz; i++) {
+ SsaInsn insn = ssaMeth.getDefinitionForRegister(i);
+
+ if (insn == null) continue;
+
+ RegisterSpec result = insn.getResult();
+ TypeBearer typeBearer = result.getTypeBearer();
+
+ if (!typeBearer.isConstant()) continue;
+
+ TypedConstant cst = (TypedConstant) typeBearer;
+
+ if (insn.canThrow()) {
+ /*
+ * Don't move anything other than strings -- the risk
+ * of changing where an exception is thrown is too high.
+ */
+ if (!(cst instanceof CstString) || !COLLECT_STRINGS) {
+ continue;
+ }
+ /*
+ * We can't move any throwable const whose throw will be
+ * caught, so don't count them.
+ */
+ if (insn.getBlock().getSuccessors().cardinality() > 1) {
+ continue;
+ }
+ }
+
+ /*
+ * TODO: Might be nice to try and figure out which local
+ * wins most when collected.
+ */
+ if (ssaMeth.isRegALocal(result)) {
+ if (!COLLECT_ONE_LOCAL) {
+ continue;
+ } else {
+ if (usedByLocal.contains(cst)) {
+ // Count one local usage only.
+ continue;
+ } else {
+ usedByLocal.add(cst);
+ }
+ }
+ }
+
+ Integer has = countUses.get(cst);
+ if (has == null) {
+ countUses.put(cst, 1);
+ } else {
+ countUses.put(cst, has + 1);
+ }
+ }
+
+ // Collect constants that have been reused.
+ ArrayList<TypedConstant> constantList = new ArrayList<TypedConstant>();
+ for (Map.Entry<TypedConstant, Integer> entry : countUses.entrySet()) {
+ if (entry.getValue() > 1) {
+ constantList.add(entry.getKey());
+ }
+ }
+
+ // Sort by use, with most used at the beginning of the list.
+ Collections.sort(constantList, new Comparator<Constant>() {
+ public int compare(Constant a, Constant b) {
+ int ret;
+ ret = countUses.get(b) - countUses.get(a);
+
+ if (ret == 0) {
+ /*
+ * Provide sorting determinisim for constants with same
+ * usage count.
+ */
+ ret = a.compareTo(b);
+ }
+
+ return ret;
+ }
+
+ public boolean equals (Object obj) {
+ return obj == this;
+ }
+ });
+
+ return constantList;
+ }
+
+ /**
+ * Inserts mark-locals if necessary when changing a register. If
+ * the definition of {@code origReg} is associated with a local
+ * variable, then insert a mark-local for {@code newReg} just below
+ * it. We expect the definition of {@code origReg} to ultimately
+ * be removed by the dead code eliminator
+ *
+ * @param origReg {@code non-null;} original register
+ * @param newReg {@code non-null;} new register that will replace
+ * {@code origReg}
+ */
+ private void fixLocalAssignment(RegisterSpec origReg,
+ RegisterSpec newReg) {
+ for (SsaInsn use : ssaMeth.getUseListForRegister(origReg.getReg())) {
+ RegisterSpec localAssignment = use.getLocalAssignment();
+ if (localAssignment == null) {
+ continue;
+ }
+
+ if (use.getResult() == null) {
+ /*
+ * This is a mark-local. it will be updated when all uses
+ * are updated.
+ */
+ continue;
+ }
+
+ LocalItem local = localAssignment.getLocalItem();
+
+ // Un-associate original use.
+ use.setResultLocal(null);
+
+ // Now add a mark-local to the new reg immediately after.
+ newReg = newReg.withLocalItem(local);
+
+ SsaInsn newInsn
+ = SsaInsn.makeFromRop(
+ new PlainInsn(Rops.opMarkLocal(newReg),
+ SourcePosition.NO_INFO, null,
+ RegisterSpecList.make(newReg)),
+ use.getBlock());
+
+ ArrayList<SsaInsn> insns = use.getBlock().getInsns();
+
+ insns.add(insns.indexOf(use) + 1, newInsn);
+ }
+ }
+
+ /**
+ * Updates all uses of various consts to use the values in the newly
+ * assigned registers.
+ *
+ * @param newRegs {@code non-null;} mapping between constant and new reg
+ * @param origRegCount {@code >=0;} original SSA reg count, not including
+ * newly added constant regs
+ */
+ private void updateConstUses(HashMap<TypedConstant, RegisterSpec> newRegs,
+ int origRegCount) {
+
+ /*
+ * set of constants associated with a local variable; used
+ * only if COLLECT_ONE_LOCAL is true.
+ */
+ final HashSet<TypedConstant> usedByLocal
+ = new HashSet<TypedConstant>();
+
+ final ArrayList<SsaInsn>[] useList = ssaMeth.getUseListCopy();
+
+ for (int i = 0; i < origRegCount; i++) {
+ SsaInsn insn = ssaMeth.getDefinitionForRegister(i);
+
+ if (insn == null) {
+ continue;
+ }
+
+ final RegisterSpec origReg = insn.getResult();
+ TypeBearer typeBearer = insn.getResult().getTypeBearer();
+
+ if (!typeBearer.isConstant()) continue;
+
+ TypedConstant cst = (TypedConstant) typeBearer;
+ final RegisterSpec newReg = newRegs.get(cst);
+
+ if (newReg == null) {
+ continue;
+ }
+
+ if (ssaMeth.isRegALocal(origReg)) {
+ if (!COLLECT_ONE_LOCAL) {
+ continue;
+ } else {
+ /*
+ * TODO: If the same local gets the same cst
+ * multiple times, it would be nice to reuse the
+ * register.
+ */
+ if (usedByLocal.contains(cst)) {
+ continue;
+ } else {
+ usedByLocal.add(cst);
+ fixLocalAssignment(origReg, newRegs.get(cst));
+ }
+ }
+ }
+
+ // maps an original const register to the new collected register
+ RegisterMapper mapper = new RegisterMapper() {
+ @Override
+ public int getNewRegisterCount() {
+ return ssaMeth.getRegCount();
+ }
+
+ @Override
+ public RegisterSpec map(RegisterSpec registerSpec) {
+ if (registerSpec.getReg() == origReg.getReg()) {
+ return newReg.withLocalItem(
+ registerSpec.getLocalItem());
+ }
+
+ return registerSpec;
+ }
+ };
+
+ for (SsaInsn use : useList[origReg.getReg()]) {
+ if (use.canThrow()
+ && use.getBlock().getSuccessors().cardinality() > 1) {
+ continue;
+ }
+ use.mapSourceRegisters(mapper);
+ }
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/ssa/DeadCodeRemover.java b/dx/src/com/android/dx/ssa/DeadCodeRemover.java
new file mode 100644
index 0000000..2a29050
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/DeadCodeRemover.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.RegOps;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.Rop;
+import com.android.dx.rop.code.PlainInsn;
+import com.android.dx.rop.code.Rops;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.rop.code.Insn;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.HashSet;
+
+/**
+ * A variation on Appel Algorithm 19.12 "Dead code elimination in SSA form".
+ *
+ * TODO this algorithm is more efficient if run in reverse from exit
+ * block to entry block.
+ */
+public class DeadCodeRemover {
+ /** method we're processing */
+ private final SsaMethod ssaMeth;
+
+ /** ssaMeth.getRegCount() */
+ private final int regCount;
+
+ /**
+ * indexed by register: whether reg should be examined
+ * (does it correspond to a no-side-effect insn?)
+ */
+ private final BitSet worklist;
+
+ /** use list indexed by register; modified during operation */
+ private final ArrayList<SsaInsn>[] useList;
+
+ /**
+ * Process a method with the dead-code remver
+ *
+ * @param ssaMethod method to process
+ */
+ public static void process(SsaMethod ssaMethod) {
+ DeadCodeRemover dc = new DeadCodeRemover(ssaMethod);
+ dc.run();
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param ssaMethod method to process
+ */
+ private DeadCodeRemover(SsaMethod ssaMethod) {
+ this.ssaMeth = ssaMethod;
+
+ regCount = ssaMethod.getRegCount();
+ worklist = new BitSet(regCount);
+ useList = ssaMeth.getUseListCopy();
+ }
+
+ /**
+ * Runs the dead code remover.
+ */
+ private void run() {
+ HashSet<SsaInsn> deletedInsns = (HashSet<SsaInsn>) new HashSet();
+
+ ssaMeth.forEachInsn(new NoSideEffectVisitor(worklist));
+
+ int regV;
+
+ while ( 0 <= (regV = worklist.nextSetBit(0)) ) {
+ worklist.clear(regV);
+
+ if (useList[regV].size() == 0
+ || isCircularNoSideEffect(regV, null)) {
+
+ SsaInsn insnS = ssaMeth.getDefinitionForRegister(regV);
+
+ // This insn has already been deleted.
+ if (deletedInsns.contains(insnS)) {
+ continue;
+ }
+
+ RegisterSpecList sources = insnS.getSources();
+
+ int sz = sources.size();
+ for (int i = 0; i < sz; i++) {
+ // Delete this insn from all usage lists.
+ RegisterSpec source = sources.get(i);
+ useList[source.getReg()].remove(insnS);
+
+ if (!hasSideEffect(
+ ssaMeth.getDefinitionForRegister(
+ source.getReg()))) {
+ /*
+ * Only registers whose definition has no side effect
+ * should be added back to the worklist.
+ */
+ worklist.set(source.getReg());
+ }
+ }
+
+ // Schedule this insn for later deletion.
+ deletedInsns.add(insnS);
+ }
+ }
+
+ ssaMeth.deleteInsns(deletedInsns);
+ }
+
+ /**
+ * Returns true if the only uses of this register form a circle of
+ * operations with no side effects.
+ *
+ * @param regV register to examine
+ * @param set a set of registers that we've already determined
+ * are only used as sources in operations with no side effect or null
+ * if this is the first recursion
+ * @return true if usage is circular without side effect
+ */
+ private boolean isCircularNoSideEffect(int regV, BitSet set) {
+ if ((set != null) && set.get(regV)) {
+ return true;
+ }
+
+ for (SsaInsn use : useList[regV]) {
+ if (hasSideEffect(use)) {
+ return false;
+ }
+ }
+
+ if (set == null) {
+ set = new BitSet(regCount);
+ }
+
+ // This register is only used in operations that have no side effect.
+ set.set(regV);
+
+ for (SsaInsn use : useList[regV]) {
+ RegisterSpec result = use.getResult();
+
+ if (result == null
+ || !isCircularNoSideEffect(result.getReg(), set)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns true if this insn has a side-effect. Returns true
+ * if the insn is null for reasons stated in the code block.
+ *
+ * @param insn {@code null-ok;} instruction in question
+ * @return true if it has a side-effect
+ */
+ private static boolean hasSideEffect(SsaInsn insn) {
+ if (insn == null) {
+ /* While false would seem to make more sense here, true
+ * prevents us from adding this back to a worklist unnecessarally.
+ */
+ return true;
+ }
+
+ return insn.hasSideEffect();
+ }
+
+ /**
+ * A callback class used to build up the initial worklist of
+ * registers defined by an instruction with no side effect.
+ */
+ static private class NoSideEffectVisitor implements SsaInsn.Visitor {
+ BitSet noSideEffectRegs;
+
+ /**
+ * Passes in data structures that will be filled out after
+ * ssaMeth.forEachInsn() is called with this instance.
+ *
+ * @param noSideEffectRegs to-build bitset of regs that are
+ * results of regs with no side effects
+ */
+ public NoSideEffectVisitor(BitSet noSideEffectRegs) {
+ this.noSideEffectRegs = noSideEffectRegs;
+ }
+
+ /** {@inheritDoc} */
+ public void visitMoveInsn (NormalSsaInsn insn) {
+ // If we're tracking local vars, some moves have side effects.
+ if (!hasSideEffect(insn)) {
+ noSideEffectRegs.set(insn.getResult().getReg());
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void visitPhiInsn (PhiInsn phi) {
+ // If we're tracking local vars, then some phis have side effects.
+ if (!hasSideEffect(phi)) {
+ noSideEffectRegs.set(phi.getResult().getReg());
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void visitNonMoveInsn (NormalSsaInsn insn) {
+ RegisterSpec result = insn.getResult();
+ if (!hasSideEffect(insn) && result != null) {
+ noSideEffectRegs.set(result.getReg());
+ }
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/ssa/DomFront.java b/dx/src/com/android/dx/ssa/DomFront.java
new file mode 100644
index 0000000..941a317
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/DomFront.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.util.IntSet;
+import com.android.dx.util.BitIntSet;
+import com.android.dx.util.ListIntSet;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.BitSet;
+
+/**
+ * Calculates the dominance-frontiers of a methot's basic blocks.
+ * Algorithm from "A Simple, Fast Dominance Algorithm" by Cooper,
+ * Harvey, and Kennedy; transliterated to Java.
+ */
+public class DomFront {
+ /** local debug flag */
+ private static boolean DEBUG = false;
+
+ /** {@code non-null;} method being processed */
+ private final SsaMethod meth;
+
+ private final ArrayList<SsaBasicBlock> nodes;
+
+ private final DomInfo[] domInfos;
+
+ /**
+ * Dominance-frontier information for a single basic block.
+ */
+ public static class DomInfo {
+ /**
+ * {@code null-ok;} the dominance frontier set indexed by
+ * block index
+ */
+ public IntSet dominanceFrontiers;
+
+ /** {@code >= 0 after run();} the index of the immediate dominator */
+ public int idom = -1;
+ }
+
+ /**
+ * Constructs instance. Call {@link DomFront#run} to process.
+ *
+ * @param meth {@code non-null;} method to process
+ */
+ public DomFront(SsaMethod meth) {
+ this.meth = meth;
+ nodes = meth.getBlocks();
+
+ int szNodes = nodes.size();
+ domInfos = new DomInfo[szNodes];
+
+ for (int i = 0; i < szNodes; i++) {
+ domInfos[i] = new DomInfo();
+ }
+ }
+
+ /**
+ * Calculates the dominance frontier information for the method.
+ *
+ * @return {@code non-null;} an array of DomInfo structures
+ */
+ public DomInfo[] run() {
+ int szNodes = nodes.size();
+
+ if (DEBUG) {
+ for (int i = 0; i < szNodes; i++) {
+ SsaBasicBlock node = nodes.get(i);
+ System.out.println("pred[" + i + "]: "
+ + node.getPredecessors());
+ }
+ }
+
+ Dominators methDom = Dominators.make(meth, domInfos, false);
+
+ if (DEBUG) {
+ for (int i = 0; i < szNodes; i++) {
+ DomInfo info = domInfos[i];
+ System.out.println("idom[" + i + "]: "
+ + info.idom);
+ }
+ }
+
+ buildDomTree();
+
+ if (DEBUG) {
+ debugPrintDomChildren();
+ }
+
+ for (int i = 0; i < szNodes; i++) {
+ domInfos[i].dominanceFrontiers
+ = SetFactory.makeDomFrontSet(szNodes);
+ }
+
+ calcDomFronts();
+
+ if (DEBUG) {
+ for (int i = 0; i < szNodes; i++) {
+ System.out.println("df[" + i + "]: "
+ + domInfos[i].dominanceFrontiers);
+ }
+ }
+
+ return domInfos;
+ }
+
+ private void debugPrintDomChildren() {
+ int szNodes = nodes.size();
+
+ for (int i = 0; i < szNodes; i++) {
+ SsaBasicBlock node = nodes.get(i);
+ StringBuffer sb = new StringBuffer();
+
+ sb.append('{');
+ boolean comma = false;
+ for (SsaBasicBlock child : node.getDomChildren()) {
+ if (comma) {
+ sb.append(',');
+ }
+ sb.append(child);
+ comma = true;
+ }
+ sb.append('}');
+
+ System.out.println("domChildren[" + node + "]: "
+ + sb);
+ }
+ }
+
+ /**
+ * The dominators algorithm leaves us knowing who the immediate dominator
+ * is for each node. This sweeps the node list and builds the proper
+ * dominance tree.
+ */
+ private void buildDomTree() {
+ int szNodes = nodes.size();
+
+ for (int i = 0; i < szNodes; i++) {
+ DomInfo info = domInfos[i];
+
+ if (info.idom == -1) continue;
+
+ SsaBasicBlock domParent = nodes.get(info.idom);
+ domParent.addDomChild(nodes.get(i));
+ }
+ }
+
+ /**
+ * Calculates the dominance-frontier set.
+ * from "A Simple, Fast Dominance Algorithm" by Cooper,
+ * Harvey, and Kennedy; transliterated to Java.
+ */
+ private void calcDomFronts() {
+ int szNodes = nodes.size();
+
+ for (int b = 0; b < szNodes; b++) {
+ SsaBasicBlock nb = nodes.get(b);
+ DomInfo nbInfo = domInfos[b];
+ BitSet pred = nb.getPredecessors();
+
+ if (pred.cardinality() > 1) {
+ for (int i = pred.nextSetBit(0); i >= 0;
+ i = pred.nextSetBit(i + 1)) {
+
+ for (int runnerIndex = i;
+ runnerIndex != nbInfo.idom; /* empty */) {
+ /*
+ * We can stop if we hit a block we already
+ * added label to, since we must be at a part
+ * of the dom tree we have seen before.
+ */
+ if (runnerIndex == -1) break;
+
+ DomInfo runnerInfo = domInfos[runnerIndex];
+
+ if (runnerInfo.dominanceFrontiers.has(b)) {
+ break;
+ }
+
+ // Add b to runner's dominance frontier set.
+ runnerInfo.dominanceFrontiers.add(b);
+ runnerIndex = runnerInfo.idom;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/ssa/Dominators.java b/dx/src/com/android/dx/ssa/Dominators.java
new file mode 100644
index 0000000..503e857
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/Dominators.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.HashSet;
+
+/**
+ * This class computes dominator and post-dominator information using the
+ * Lengauer-Tarjan method.
+ *
+ * See A Fast Algorithm for Finding Dominators in a Flowgraph
+ * T. Lengauer & R. Tarjan, ACM TOPLAS July 1979, pgs 121-141.
+ *
+ * This implementation runs in time O(n log n). The time bound
+ * could be changed to O(n * ack(n)) with a small change to the link and eval,
+ * and an addition of a child field to the DFS info. In reality, the constant
+ * overheads are high enough that the current method is faster in all but the
+ * strangest artificially constructed examples.
+ *
+ * The basic idea behind this algorithm is to perform a DFS walk, keeping track
+ * of various info about parents. We then use this info to calculate the
+ * dominators, using union-find structures to link together the DFS info,
+ * then finally evaluate the union-find results to get the dominators.
+ * This implementation is m log n because it does not perform union by
+ * rank to keep the union-find tree balanced.
+ */
+public final class Dominators {
+ /* postdom is true if we want post dominators */
+ private final boolean postdom;
+
+ /* {@code non-null;} method being processed */
+ private final SsaMethod meth;
+
+ /* Method's basic blocks. */
+ private final ArrayList<SsaBasicBlock> blocks;
+
+ /** indexed by basic block index */
+ private final DFSInfo[] info;
+
+ private final ArrayList<SsaBasicBlock> vertex;
+
+ /** {@code non-null;} the raw dominator info */
+ private final DomFront.DomInfo domInfos[];
+
+ /**
+ * Constructs an instance.
+ *
+ * @param meth {@code non-null;} method to process
+ * @param domInfos {@code non-null;} the raw dominator info
+ * @param postdom true for postdom information, false for normal dom info
+ */
+ private Dominators(SsaMethod meth, DomFront.DomInfo[] domInfos,
+ boolean postdom) {
+ this.meth = meth;
+ this.domInfos = domInfos;
+ this.postdom = postdom;
+ this.blocks = meth.getBlocks();
+ this.info = new DFSInfo[blocks.size() + 2];
+ this.vertex = new ArrayList<SsaBasicBlock>();
+ }
+
+ /**
+ * Constructs a fully-initialized instance. (This method exists so as
+ * to avoid calling a large amount of code in the constructor.)
+ *
+ * @param meth {@code non-null;} method to process
+ * @param domInfos {@code non-null;} the raw dominator info
+ * @param postdom true for postdom information, false for normal dom info
+ */
+ public static Dominators make(SsaMethod meth, DomFront.DomInfo[] domInfos,
+ boolean postdom) {
+ Dominators result = new Dominators(meth, domInfos, postdom);
+
+ result.run();
+ return result;
+ }
+
+ private BitSet getSuccs(SsaBasicBlock block) {
+ if (postdom) {
+ return block.getPredecessors();
+ } else {
+ return block.getSuccessors();
+ }
+ }
+
+ private BitSet getPreds(SsaBasicBlock block) {
+ if (postdom) {
+ return block.getSuccessors();
+ } else {
+ return block.getPredecessors();
+ }
+ }
+
+ /**
+ * Performs path compress on the DFS info.
+ *
+ * @param in Basic block whose DFS info we are path compressing.
+ */
+ private void compress(SsaBasicBlock in) {
+ DFSInfo bbInfo = info[in.getIndex()];
+ DFSInfo ancestorbbInfo = info[bbInfo.ancestor.getIndex()];
+
+ if (ancestorbbInfo.ancestor != null) {
+ ArrayList<SsaBasicBlock> worklist = new ArrayList<SsaBasicBlock>();
+ HashSet<SsaBasicBlock> visited = new HashSet<SsaBasicBlock>();
+ worklist.add(in);
+
+ while (!worklist.isEmpty()) {
+ int wsize = worklist.size();
+ SsaBasicBlock v = worklist.get(wsize - 1);
+ DFSInfo vbbInfo = info[v.getIndex()];
+ SsaBasicBlock vAncestor = vbbInfo.ancestor;
+ DFSInfo vabbInfo = info[vAncestor.getIndex()];
+
+ // Make sure we process our ancestor before ourselves.
+ if (visited.add(vAncestor) && vabbInfo.ancestor != null) {
+ worklist.add(vAncestor);
+ continue;
+ }
+ worklist.remove(wsize - 1);
+
+ // Update based on ancestor info.
+ if (vabbInfo.ancestor == null) {
+ continue;
+ }
+ SsaBasicBlock vAncestorRep = vabbInfo.rep;
+ SsaBasicBlock vRep = vbbInfo.rep;
+ if (info[vAncestorRep.getIndex()].semidom
+ < info[vRep.getIndex()].semidom) {
+ vbbInfo.rep = vAncestorRep;
+ }
+ vbbInfo.ancestor = vabbInfo.ancestor;
+ }
+ }
+ }
+
+ private SsaBasicBlock eval(SsaBasicBlock v) {
+ DFSInfo bbInfo = info[v.getIndex()];
+
+ if (bbInfo.ancestor == null) {
+ return v;
+ }
+
+ compress(v);
+ return bbInfo.rep;
+ }
+
+ /**
+ * Performs dominator/post-dominator calculation for the control
+ * flow graph.
+ *
+ * @param meth {@code non-null;} method to analyze
+ */
+ private void run() {
+ SsaBasicBlock root = postdom
+ ? meth.getExitBlock() : meth.getEntryBlock();
+
+ if (root != null) {
+ vertex.add(root);
+ domInfos[root.getIndex()].idom = root.getIndex();
+ }
+
+ /*
+ * First we perform a DFS numbering of the blocks, by
+ * numbering the dfs tree roots.
+ */
+
+ DfsWalker walker = new DfsWalker();
+ meth.forEachBlockDepthFirst(postdom, walker);
+
+ // the largest semidom number assigned
+ int dfsMax = vertex.size() - 1;
+
+ // Now calculate semidominators.
+ for (int i = dfsMax; i >= 2; --i) {
+ SsaBasicBlock w = vertex.get(i);
+ DFSInfo wInfo = info[w.getIndex()];
+
+ BitSet preds = getPreds(w);
+ for (int j = preds.nextSetBit(0);
+ j >= 0;
+ j = preds.nextSetBit(j + 1)) {
+ SsaBasicBlock predBlock = blocks.get(j);
+ DFSInfo predInfo = info[predBlock.getIndex()];
+
+ /*
+ * PredInfo may not exist in case the predecessor is
+ * not reachable.
+ */
+ if (predInfo != null) {
+ int predSemidom = info[eval(predBlock).getIndex()].semidom;
+ if (predSemidom < wInfo.semidom) {
+ wInfo.semidom = predSemidom;
+ }
+ }
+ }
+ info[vertex.get(wInfo.semidom).getIndex()].bucket.add(w);
+
+ /*
+ * Normally we would call link here, but in our O(m log n)
+ * implementation this is equivalent to the following
+ * single line.
+ */
+ wInfo.ancestor = wInfo.parent;
+
+ // Implicity define idom for each vertex.
+ ArrayList<SsaBasicBlock> wParentBucket;
+ wParentBucket = info[wInfo.parent.getIndex()].bucket;
+
+ while (!wParentBucket.isEmpty()) {
+ int lastItem = wParentBucket.size() - 1;
+ SsaBasicBlock last = wParentBucket.remove(lastItem);
+ SsaBasicBlock U = eval(last);
+ if (info[U.getIndex()].semidom
+ < info[last.getIndex()].semidom) {
+ domInfos[last.getIndex()].idom = U.getIndex();
+ } else {
+ domInfos[last.getIndex()].idom = wInfo.parent.getIndex();
+ }
+ }
+ }
+
+ // Now explicitly define the immediate dominator of each vertex
+ for (int i = 2; i <= dfsMax; ++i) {
+ SsaBasicBlock w = vertex.get(i);
+ if (domInfos[w.getIndex()].idom
+ != vertex.get(info[w.getIndex()].semidom).getIndex()) {
+ domInfos[w.getIndex()].idom
+ = domInfos[domInfos[w.getIndex()].idom].idom;
+ }
+ }
+ }
+
+ /**
+ * Callback for depth-first walk through control flow graph (either
+ * from the entry block or the exit block). Records the traversal order
+ * in the {@code info}list.
+ */
+ private class DfsWalker implements SsaBasicBlock.Visitor {
+ private int dfsNum = 0;
+
+ public void visitBlock(SsaBasicBlock v, SsaBasicBlock parent) {
+ DFSInfo bbInfo = new DFSInfo();
+ bbInfo.semidom = ++dfsNum;
+ bbInfo.rep = v;
+ bbInfo.parent = parent;
+ vertex.add(v);
+ info[v.getIndex()] = bbInfo;
+ }
+ }
+
+ private static final class DFSInfo {
+ public int semidom;
+ public SsaBasicBlock parent;
+
+ /**
+ * rep(resentative) is known as "label" in the paper. It is the node
+ * that our block's DFS info has been unioned to.
+ */
+ public SsaBasicBlock rep;
+
+ public SsaBasicBlock ancestor;
+ public ArrayList<SsaBasicBlock> bucket;
+
+ public DFSInfo() {
+ bucket = new ArrayList<SsaBasicBlock>();
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/ssa/EscapeAnalysis.java b/dx/src/com/android/dx/ssa/EscapeAnalysis.java
new file mode 100644
index 0000000..d270857
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/EscapeAnalysis.java
@@ -0,0 +1,843 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.Exceptions;
+import com.android.dx.rop.code.FillArrayDataInsn;
+import com.android.dx.rop.code.Insn;
+import com.android.dx.rop.code.PlainCstInsn;
+import com.android.dx.rop.code.PlainInsn;
+import com.android.dx.rop.code.RegOps;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.Rop;
+import com.android.dx.rop.code.Rops;
+import com.android.dx.rop.code.ThrowingCstInsn;
+import com.android.dx.rop.code.ThrowingInsn;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstNat;
+import com.android.dx.rop.cst.CstType;
+import com.android.dx.rop.cst.CstUtf8;
+import com.android.dx.rop.cst.TypedConstant;
+import com.android.dx.rop.cst.Zeroes;
+import com.android.dx.rop.type.StdTypeList;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * Simple intraprocedural escape analysis. Finds new arrays that don't escape
+ * the method they are created in and replaces the array values with registers.
+ */
+public class EscapeAnalysis {
+ /**
+ * Struct used to generate and maintain escape analysis results.
+ */
+ static class EscapeSet {
+ /** set containing all registers related to an object */
+ BitSet regSet;
+ /** escape state of the object */
+ EscapeState escape;
+ /** list of objects that are put into this object */
+ ArrayList<EscapeSet> childSets;
+ /** list of objects that this object is put into */
+ ArrayList<EscapeSet> parentSets;
+ /** flag to indicate this object is a scalar replaceable array */
+ boolean replaceableArray;
+
+ /**
+ * Constructs an instance of an EscapeSet
+ *
+ * @param reg the SSA register that defines the object
+ * @param size the number of registers in the method
+ * @param escState the lattice value to initially set this to
+ */
+ EscapeSet(int reg, int size, EscapeState escState) {
+ regSet = new BitSet(size);
+ regSet.set(reg);
+ escape = escState;
+ childSets = new ArrayList<EscapeSet>();
+ parentSets = new ArrayList<EscapeSet>();
+ replaceableArray = false;
+ }
+ }
+
+ /**
+ * Lattice values used to indicate escape state for an object. Analysis can
+ * only raise escape state values, not lower them.
+ *
+ * TOP - Used for objects that haven't been analyzed yet
+ * NONE - Object does not escape, and is eligible for scalar replacement.
+ * METHOD - Object remains local to method, but can't be scalar replaced.
+ * INTER - Object is passed between methods. (treated as globally escaping
+ * since this is an intraprocedural analysis)
+ * GLOBAL - Object escapes globally.
+ */
+ public enum EscapeState {
+ TOP, NONE, METHOD, INTER, GLOBAL
+ }
+
+ /** method we're processing */
+ private SsaMethod ssaMeth;
+ /** ssaMeth.getRegCount() */
+ private int regCount;
+ /** Lattice values for each object register group */
+ private ArrayList<EscapeSet> latticeValues;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param ssaMeth method to process
+ */
+ private EscapeAnalysis(SsaMethod ssaMeth) {
+ this.ssaMeth = ssaMeth;
+ this.regCount = ssaMeth.getRegCount();
+ this.latticeValues = new ArrayList<EscapeSet>();
+ }
+
+ /**
+ * Finds the index in the lattice for a particular register.
+ * Returns the size of the lattice if the register wasn't found.
+ *
+ * @param reg {@code non-null;} register being looked up
+ * @return index of the register or size of the lattice if it wasn't found.
+ */
+ private int findSetIndex(RegisterSpec reg) {
+ int i;
+ for (i = 0; i < latticeValues.size(); i++) {
+ EscapeSet e = latticeValues.get(i);
+ if (e.regSet.get(reg.getReg())) {
+ return i;
+ }
+ }
+ return i;
+ }
+
+ /**
+ * Finds the corresponding instruction for a given move result
+ *
+ * @param moveInsn {@code non-null;} a move result instruction
+ * @return {@code non-null;} the instruction that produces the result for
+ * the move
+ */
+ private SsaInsn getInsnForMove(SsaInsn moveInsn) {
+ int pred = moveInsn.getBlock().getPredecessors().nextSetBit(0);
+ ArrayList<SsaInsn> predInsns = ssaMeth.getBlocks().get(pred).getInsns();
+ return predInsns.get(predInsns.size()-1);
+ }
+
+ /**
+ * Finds the corresponding move result for a given instruction
+ *
+ * @param insn {@code non-null;} an instruction that must always be
+ * followed by a move result
+ * @return {@code non-null;} the move result for the given instruction
+ */
+ private SsaInsn getMoveForInsn(SsaInsn insn) {
+ int succ = insn.getBlock().getSuccessors().nextSetBit(0);
+ ArrayList<SsaInsn> succInsns = ssaMeth.getBlocks().get(succ).getInsns();
+ return succInsns.get(0);
+ }
+
+ /**
+ * Creates a link in the lattice between two EscapeSets due to a put
+ * instruction. The object being put is the child and the object being put
+ * into is the parent. A child set must always have an escape state at
+ * least as high as its parent.
+ *
+ * @param parentSet {@code non-null;} the EscapeSet for the object being put
+ * into
+ * @param childSet {@code non-null;} the EscapeSet for the object being put
+ */
+ private void addEdge(EscapeSet parentSet, EscapeSet childSet) {
+ if (!childSet.parentSets.contains(parentSet)) {
+ childSet.parentSets.add(parentSet);
+ }
+ if (!parentSet.childSets.contains(childSet)) {
+ parentSet.childSets.add(childSet);
+ }
+ }
+
+ /**
+ * Merges all links in the lattice among two EscapeSets. On return, the
+ * newNode will have its old links as well as all links from the oldNode.
+ * The oldNode has all its links removed.
+ *
+ * @param newNode {@code non-null;} the EscapeSet to merge all links into
+ * @param oldNode {@code non-null;} the EscapeSet to remove all links from
+ */
+ private void replaceNode(EscapeSet newNode, EscapeSet oldNode) {
+ for (EscapeSet e : oldNode.parentSets) {
+ e.childSets.remove(oldNode);
+ e.childSets.add(newNode);
+ newNode.parentSets.add(e);
+ }
+ for (EscapeSet e : oldNode.childSets) {
+ e.parentSets.remove(oldNode);
+ e.parentSets.add(newNode);
+ newNode.childSets.add(e);
+ }
+ }
+
+ /**
+ * Performs escape analysis on a method. Finds scalar replaceable arrays and
+ * replaces them with equivalent registers.
+ *
+ * @param ssaMethod {@code non-null;} method to process
+ */
+ public static void process(SsaMethod ssaMethod) {
+ new EscapeAnalysis(ssaMethod).run();
+ }
+
+ /**
+ * Process a single instruction, looking for new objects resulting from
+ * move result or move param.
+ *
+ * @param insn {@code non-null;} instruction to process
+ */
+ private void processInsn(SsaInsn insn) {
+ int op = insn.getOpcode().getOpcode();
+ RegisterSpec result = insn.getResult();
+ EscapeSet escSet;
+
+ // Identify new objects
+ if (op == RegOps.MOVE_RESULT_PSEUDO &&
+ result.getTypeBearer().getBasicType() == Type.BT_OBJECT) {
+ // Handle objects generated through move_result_pseudo
+ escSet = processMoveResultPseudoInsn(insn);
+ processRegister(result, escSet);
+ } else if (op == RegOps.MOVE_PARAM &&
+ result.getTypeBearer().getBasicType() == Type.BT_OBJECT) {
+ // Track method arguments that are objects
+ escSet = new EscapeSet(result.getReg(), regCount, EscapeState.NONE);
+ latticeValues.add(escSet);
+ processRegister(result, escSet);
+ } else if (op == RegOps.MOVE_RESULT &&
+ result.getTypeBearer().getBasicType() == Type.BT_OBJECT) {
+ // Track method return values that are objects
+ escSet = new EscapeSet(result.getReg(), regCount, EscapeState.NONE);
+ latticeValues.add(escSet);
+ processRegister(result, escSet);
+ }
+ }
+
+ /**
+ * Determine the origin of a move result pseudo instruction that generates
+ * an object. Creates a new EscapeSet for the new object accordingly.
+ *
+ * @param insn {@code non-null;} move result pseudo instruction to process
+ * @return {@code non-null;} an EscapeSet for the object referred to by the
+ * move result pseudo instruction
+ */
+ private EscapeSet processMoveResultPseudoInsn(SsaInsn insn) {
+ RegisterSpec result = insn.getResult();
+ SsaInsn prevSsaInsn = getInsnForMove(insn);
+ int prevOpcode = prevSsaInsn.getOpcode().getOpcode();
+ EscapeSet escSet;
+ RegisterSpec prevSource;
+
+ switch(prevOpcode) {
+ // New instance / Constant
+ case RegOps.NEW_INSTANCE:
+ case RegOps.CONST:
+ escSet = new EscapeSet(result.getReg(), regCount,
+ EscapeState.NONE);
+ break;
+ // New array
+ case RegOps.NEW_ARRAY:
+ case RegOps.FILLED_NEW_ARRAY:
+ prevSource = prevSsaInsn.getSources().get(0);
+ if (prevSource.getTypeBearer().isConstant()) {
+ // New fixed array
+ escSet = new EscapeSet(result.getReg(), regCount,
+ EscapeState.NONE);
+ escSet.replaceableArray = true;
+ } else {
+ // New variable array
+ escSet = new EscapeSet(result.getReg(), regCount,
+ EscapeState.GLOBAL);
+ }
+ break;
+ // Loading a static object
+ case RegOps.GET_STATIC:
+ escSet = new EscapeSet(result.getReg(), regCount,
+ EscapeState.GLOBAL);
+ break;
+ // Type cast / load an object from a field or array
+ case RegOps.CHECK_CAST:
+ case RegOps.GET_FIELD:
+ case RegOps.AGET:
+ prevSource = prevSsaInsn.getSources().get(0);
+ int setIndex = findSetIndex(prevSource);
+
+ // Set should already exist, try to find it
+ if (setIndex != latticeValues.size()) {
+ escSet = latticeValues.get(setIndex);
+ escSet.regSet.set(result.getReg());
+ return escSet;
+ }
+
+ // Set not found, must be either null or unknown
+ if (prevSource.getType() == Type.KNOWN_NULL) {
+ escSet = new EscapeSet(result.getReg(), regCount,
+ EscapeState.NONE);
+ } else {
+ escSet = new EscapeSet(result.getReg(), regCount,
+ EscapeState.GLOBAL);
+ }
+ break;
+ default:
+ return null;
+ }
+
+ // Add the newly created escSet to the lattice and return it
+ latticeValues.add(escSet);
+ return escSet;
+ }
+
+ /**
+ * Iterate through all the uses of a new object.
+ *
+ * @param result {@code non-null;} register where new object is stored
+ * @param escSet {@code non-null;} EscapeSet for the new object
+ */
+ private void processRegister(RegisterSpec result, EscapeSet escSet) {
+ ArrayList<RegisterSpec> regWorklist = new ArrayList<RegisterSpec>();
+ regWorklist.add(result);
+
+ // Go through the worklist
+ while (!regWorklist.isEmpty()) {
+ int listSize = regWorklist.size() - 1;
+ RegisterSpec def = regWorklist.remove(listSize);
+ List<SsaInsn> useList = ssaMeth.getUseListForRegister(def.getReg());
+
+ // Handle all the uses of this register
+ for (SsaInsn use : useList) {
+ Rop useOpcode = use.getOpcode();
+
+ if (useOpcode == null) {
+ // Handle phis
+ processPhiUse(use, escSet, regWorklist);
+ } else {
+ // Handle other opcodes
+ processUse(def, use, escSet, regWorklist);
+ }
+ }
+ }
+ }
+
+ /**
+ * Handles phi uses of new objects. Will merge together the sources of a phi
+ * into a single EscapeSet. Adds the result of the phi to the worklist so
+ * its uses can be followed.
+ *
+ * @param use {@code non-null;} phi use being processed
+ * @param escSet {@code non-null;} EscapeSet for the object
+ * @param regWorklist {@code non-null;} worklist of instructions left to
+ * process for this object
+ */
+ private void processPhiUse(SsaInsn use, EscapeSet escSet,
+ ArrayList<RegisterSpec> regWorklist) {
+ int setIndex = findSetIndex(use.getResult());
+ if (setIndex != latticeValues.size()) {
+ // Check if result is in a set already
+ EscapeSet mergeSet = latticeValues.get(setIndex);
+ if (mergeSet != escSet) {
+ // If it is, merge the sets and states, then delete the copy
+ escSet.replaceableArray = false;
+ escSet.regSet.or(mergeSet.regSet);
+ if (escSet.escape.compareTo(mergeSet.escape) < 0) {
+ escSet.escape = mergeSet.escape;
+ }
+ replaceNode(escSet, mergeSet);
+ latticeValues.remove(setIndex);
+ }
+ } else {
+ // If no set is found, add it to this escSet and the worklist
+ escSet.regSet.set(use.getResult().getReg());
+ regWorklist.add(use.getResult());
+ }
+ }
+
+ /**
+ * Handles non-phi uses of new objects. Checks to see how instruction is
+ * used and updates the escape state accordingly.
+ *
+ * @param def {@code non-null;} register holding definition of new object
+ * @param use {@code non-null;} use of object being processed
+ * @param escSet {@code non-null;} EscapeSet for the object
+ * @param regWorklist {@code non-null;} worklist of instructions left to
+ * process for this object
+ */
+ private void processUse(RegisterSpec def, SsaInsn use, EscapeSet escSet,
+ ArrayList<RegisterSpec> regWorklist) {
+ int useOpcode = use.getOpcode().getOpcode();
+ switch (useOpcode) {
+ case RegOps.MOVE:
+ // Follow uses of the move by adding it to the worklist
+ escSet.regSet.set(use.getResult().getReg());
+ regWorklist.add(use.getResult());
+ break;
+ case RegOps.IF_EQ:
+ case RegOps.IF_NE:
+ case RegOps.CHECK_CAST:
+ // Compared objects can't be replaced, so promote if necessary
+ if (escSet.escape.compareTo(EscapeState.METHOD) < 0) {
+ escSet.escape = EscapeState.METHOD;
+ }
+ break;
+ case RegOps.APUT:
+ // For array puts, check for a constant array index
+ RegisterSpec putIndex = use.getSources().get(2);
+ if (!putIndex.getTypeBearer().isConstant()) {
+ // If not constant, array can't be replaced
+ escSet.replaceableArray = false;
+ }
+ // Intentional fallthrough
+ case RegOps.PUT_FIELD:
+ // Skip non-object puts
+ RegisterSpec putValue = use.getSources().get(0);
+ if (putValue.getTypeBearer().getBasicType() != Type.BT_OBJECT) {
+ break;
+ }
+ escSet.replaceableArray = false;
+
+ // Raise 1st object's escape state to 2nd if 2nd is higher
+ RegisterSpecList sources = use.getSources();
+ if (sources.get(0).getReg() == def.getReg()) {
+ int setIndex = findSetIndex(sources.get(1));
+ if (setIndex != latticeValues.size()) {
+ EscapeSet parentSet = latticeValues.get(setIndex);
+ addEdge(parentSet, escSet);
+ if (escSet.escape.compareTo(parentSet.escape) < 0) {
+ escSet.escape = parentSet.escape;
+ }
+ }
+ } else {
+ int setIndex = findSetIndex(sources.get(0));
+ if (setIndex != latticeValues.size()) {
+ EscapeSet childSet = latticeValues.get(setIndex);
+ addEdge(escSet, childSet);
+ if (childSet.escape.compareTo(escSet.escape) < 0) {
+ childSet.escape = escSet.escape;
+ }
+ }
+ }
+ break;
+ case RegOps.AGET:
+ // For array gets, check for a constant array index
+ RegisterSpec getIndex = use.getSources().get(1);
+ if (!getIndex.getTypeBearer().isConstant()) {
+ // If not constant, array can't be replaced
+ escSet.replaceableArray = false;
+ }
+ break;
+ case RegOps.PUT_STATIC:
+ // Static puts cause an object to escape globally
+ escSet.escape = EscapeState.GLOBAL;
+ break;
+ case RegOps.INVOKE_STATIC:
+ case RegOps.INVOKE_VIRTUAL:
+ case RegOps.INVOKE_SUPER:
+ case RegOps.INVOKE_DIRECT:
+ case RegOps.INVOKE_INTERFACE:
+ case RegOps.RETURN:
+ case RegOps.THROW:
+ // These operations cause an object to escape interprocedurally
+ escSet.escape = EscapeState.INTER;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Performs scalar replacement on all eligible arrays.
+ */
+ private void scalarReplacement() {
+ // Iterate through lattice, looking for non-escaping replaceable arrays
+ for (EscapeSet escSet : latticeValues) {
+ if (!escSet.replaceableArray || escSet.escape != EscapeState.NONE) {
+ continue;
+ }
+
+ // Get the instructions for the definition and move of the array
+ int e = escSet.regSet.nextSetBit(0);
+ SsaInsn def = ssaMeth.getDefinitionForRegister(e);
+ SsaInsn prev = getInsnForMove(def);
+
+ // Create a map for the new registers that will be created
+ TypeBearer lengthReg = prev.getSources().get(0).getTypeBearer();
+ int length = ((CstLiteralBits) lengthReg).getIntBits();
+ ArrayList<RegisterSpec> newRegs =
+ new ArrayList<RegisterSpec>(length);
+ HashSet<SsaInsn> deletedInsns = new HashSet<SsaInsn>();
+
+ // Replace the definition of the array with registers
+ replaceDef(def, prev, length, newRegs);
+
+ // Mark definition instructions for deletion
+ deletedInsns.add(prev);
+ deletedInsns.add(def);
+
+ // Go through all uses of the array
+ List<SsaInsn> useList = ssaMeth.getUseListForRegister(e);
+ for (SsaInsn use : useList) {
+ // Replace the use with scalars and then mark it for deletion
+ replaceUse(use, prev, newRegs, deletedInsns);
+ deletedInsns.add(use);
+ }
+
+ // Delete all marked instructions
+ ssaMeth.deleteInsns(deletedInsns);
+ ssaMeth.onInsnsChanged();
+
+ // Convert the method back to SSA form
+ SsaConverter.updateSsaMethod(ssaMeth, regCount);
+
+ // Propagate and remove extra moves added by scalar replacement
+ movePropagate();
+ }
+ }
+
+ /**
+ * Replaces the instructions that define an array with equivalent registers.
+ * For each entry in the array, a register is created, initialized to zero.
+ * A mapping between this register and the corresponding array index is
+ * added.
+ *
+ * @param def {@code non-null;} move result instruction for array
+ * @param prev {@code non-null;} instruction for instantiating new array
+ * @param length size of the new array
+ * @param newRegs {@code non-null;} mapping of array indices to new
+ * registers to be populated
+ */
+ private void replaceDef(SsaInsn def, SsaInsn prev, int length,
+ ArrayList<RegisterSpec> newRegs) {
+ Type resultType = def.getResult().getType();
+
+ // Create new zeroed out registers for each element in the array
+ for (int i = 0; i < length; i++) {
+ Constant newZero = Zeroes.zeroFor(resultType.getComponentType());
+ TypedConstant typedZero = (TypedConstant) newZero;
+ RegisterSpec newReg =
+ RegisterSpec.make(ssaMeth.makeNewSsaReg(), typedZero);
+ newRegs.add(newReg);
+ insertPlainInsnBefore(def, RegisterSpecList.EMPTY, newReg,
+ RegOps.CONST, newZero);
+ }
+ }
+
+ /**
+ * Replaces the use for a scalar replaceable array. Gets and puts become
+ * move instructions, and array lengths and fills are handled. Can also
+ * identify ArrayIndexOutOfBounds exceptions and throw them if detected.
+ *
+ * @param use {@code non-null;} move result instruction for array
+ * @param prev {@code non-null;} instruction for instantiating new array
+ * @param newRegs {@code non-null;} mapping of array indices to new
+ * registers
+ * @param deletedInsns {@code non-null;} set of instructions marked for
+ * deletion
+ */
+ private void replaceUse(SsaInsn use, SsaInsn prev,
+ ArrayList<RegisterSpec> newRegs,
+ HashSet<SsaInsn> deletedInsns) {
+ int index;
+ int length = newRegs.size();
+ SsaInsn next;
+ RegisterSpecList sources;
+ RegisterSpec source, result;
+ CstLiteralBits indexReg;
+
+ switch (use.getOpcode().getOpcode()) {
+ case RegOps.AGET:
+ // Replace array gets with moves
+ next = getMoveForInsn(use);
+ sources = use.getSources();
+ indexReg = ((CstLiteralBits) sources.get(1).getTypeBearer());
+ index = indexReg.getIntBits();
+ if (index < length) {
+ source = newRegs.get(index);
+ result = source.withReg(next.getResult().getReg());
+ insertPlainInsnBefore(next, RegisterSpecList.make(source),
+ result, RegOps.MOVE, null);
+ } else {
+ // Throw an exception if the index is out of bounds
+ insertExceptionThrow(next, sources.get(1), deletedInsns);
+ deletedInsns.add(next.getBlock().getInsns().get(2));
+ }
+ deletedInsns.add(next);
+ break;
+ case RegOps.APUT:
+ // Replace array puts with moves
+ sources = use.getSources();
+ indexReg = ((CstLiteralBits) sources.get(2).getTypeBearer());
+ index = indexReg.getIntBits();
+ if (index < length) {
+ source = sources.get(0);
+ result = source.withReg(newRegs.get(index).getReg());
+ insertPlainInsnBefore(use, RegisterSpecList.make(source),
+ result, RegOps.MOVE, null);
+ // Update the newReg entry to mark value as unknown now
+ newRegs.set(index, result.withSimpleType());
+ } else {
+ // Throw an exception if the index is out of bounds
+ insertExceptionThrow(use, sources.get(2), deletedInsns);
+ }
+ break;
+ case RegOps.ARRAY_LENGTH:
+ // Replace array lengths with const instructions
+ TypeBearer lengthReg = prev.getSources().get(0).getTypeBearer();
+ //CstInteger lengthReg = CstInteger.make(length);
+ next = getMoveForInsn(use);
+ insertPlainInsnBefore(next, RegisterSpecList.EMPTY,
+ next.getResult(), RegOps.CONST,
+ (Constant) lengthReg);
+ deletedInsns.add(next);
+ break;
+ case RegOps.MARK_LOCAL:
+ // Remove mark local instructions
+ break;
+ case RegOps.FILL_ARRAY_DATA:
+ // Create const instructions for each fill value
+ Insn ropUse = use.getOriginalRopInsn();
+ FillArrayDataInsn fill = (FillArrayDataInsn) ropUse;
+ ArrayList<Constant> constList = fill.getInitValues();
+ for (int i = 0; i < length; i++) {
+ RegisterSpec newFill =
+ RegisterSpec.make(newRegs.get(i).getReg(),
+ (TypeBearer) constList.get(i));
+ insertPlainInsnBefore(use, RegisterSpecList.EMPTY, newFill,
+ RegOps.CONST, constList.get(i));
+ // Update the newRegs to hold the new const value
+ newRegs.set(i, newFill);
+ }
+ break;
+ default:
+ }
+ }
+
+ /**
+ * Identifies extra moves added by scalar replacement and propagates the
+ * source of the move to any users of the result.
+ */
+ private void movePropagate() {
+ for (int i = 0; i < ssaMeth.getRegCount(); i++) {
+ SsaInsn insn = ssaMeth.getDefinitionForRegister(i);
+
+ // Look for move instructions only
+ if (insn == null || insn.getOpcode() == null ||
+ insn.getOpcode().getOpcode() != RegOps.MOVE) {
+ continue;
+ }
+
+ final ArrayList<SsaInsn>[] useList = ssaMeth.getUseListCopy();
+ final RegisterSpec source = insn.getSources().get(0);
+ final RegisterSpec result = insn.getResult();
+
+ // Ignore moves that weren't added due to scalar replacement
+ if (source.getReg() < regCount && result.getReg() < regCount) {
+ continue;
+ }
+
+ // Create a mapping from source to result
+ RegisterMapper mapper = new RegisterMapper() {
+ @Override
+ public int getNewRegisterCount() {
+ return ssaMeth.getRegCount();
+ }
+
+ @Override
+ public RegisterSpec map(RegisterSpec registerSpec) {
+ if (registerSpec.getReg() == result.getReg()) {
+ return source;
+ }
+
+ return registerSpec;
+ }
+ };
+
+ // Modify all uses of the move to use the source of the move instead
+ for (SsaInsn use : useList[result.getReg()]) {
+ use.mapSourceRegisters(mapper);
+ }
+ }
+ }
+
+ /**
+ * Runs escape analysis and scalar replacement of arrays.
+ */
+ private void run() {
+ ssaMeth.forEachBlockDepthFirstDom(new SsaBasicBlock.Visitor() {
+ public void visitBlock (SsaBasicBlock block,
+ SsaBasicBlock unused) {
+ block.forEachInsn(new SsaInsn.Visitor() {
+ public void visitMoveInsn(NormalSsaInsn insn) {
+ // do nothing
+ }
+
+ public void visitPhiInsn(PhiInsn insn) {
+ // do nothing
+ }
+
+ public void visitNonMoveInsn(NormalSsaInsn insn) {
+ processInsn(insn);
+ }
+ });
+ }
+ });
+
+ // Go through lattice and promote fieldSets as necessary
+ for (EscapeSet e : latticeValues) {
+ if (e.escape != EscapeState.NONE) {
+ for (EscapeSet field : e.childSets) {
+ if (e.escape.compareTo(field.escape) > 0) {
+ field.escape = e.escape;
+ }
+ }
+ }
+ }
+
+ // Perform scalar replacement for arrays
+ scalarReplacement();
+ }
+
+ /**
+ * Replaces instructions that trigger an ArrayIndexOutofBounds exception
+ * with an actual throw of the exception.
+ *
+ * @param insn {@code non-null;} instruction causing the exception
+ * @param index {@code non-null;} index value that is out of bounds
+ * @param deletedInsns {@code non-null;} set of instructions marked for
+ * deletion
+ */
+ private void insertExceptionThrow(SsaInsn insn, RegisterSpec index,
+ HashSet<SsaInsn> deletedInsns) {
+ // Create a new ArrayIndexOutOfBoundsException
+ CstType exception =
+ new CstType(Exceptions.TYPE_ArrayIndexOutOfBoundsException);
+ insertThrowingInsnBefore(insn, RegisterSpecList.EMPTY, null,
+ RegOps.NEW_INSTANCE, exception);
+
+ // Add a successor block with a move result pseudo for the exception
+ SsaBasicBlock currBlock = insn.getBlock();
+ SsaBasicBlock newBlock =
+ currBlock.insertNewSuccessor(currBlock.getPrimarySuccessor());
+ SsaInsn newInsn = newBlock.getInsns().get(0);
+ RegisterSpec newReg =
+ RegisterSpec.make(ssaMeth.makeNewSsaReg(), exception);
+ insertPlainInsnBefore(newInsn, RegisterSpecList.EMPTY, newReg,
+ RegOps.MOVE_RESULT_PSEUDO, null);
+
+ // Add another successor block to initialize the exception
+ SsaBasicBlock newBlock2 =
+ newBlock.insertNewSuccessor(newBlock.getPrimarySuccessor());
+ SsaInsn newInsn2 = newBlock2.getInsns().get(0);
+ CstNat newNat = new CstNat(new CstUtf8("<init>"), new CstUtf8("(I)V"));
+ CstMethodRef newRef = new CstMethodRef(exception, newNat);
+ insertThrowingInsnBefore(newInsn2, RegisterSpecList.make(newReg, index),
+ null, RegOps.INVOKE_DIRECT, newRef);
+ deletedInsns.add(newInsn2);
+
+ // Add another successor block to throw the new exception
+ SsaBasicBlock newBlock3 =
+ newBlock2.insertNewSuccessor(newBlock2.getPrimarySuccessor());
+ SsaInsn newInsn3 = newBlock3.getInsns().get(0);
+ insertThrowingInsnBefore(newInsn3, RegisterSpecList.make(newReg), null,
+ RegOps.THROW, null);
+ newBlock3.replaceSuccessor(newBlock3.getPrimarySuccessorIndex(),
+ ssaMeth.getExitBlock().getIndex());
+ deletedInsns.add(newInsn3);
+ }
+
+ /**
+ * Inserts a new PlainInsn before the given instruction.
+ * TODO: move this somewhere more appropriate
+ *
+ * @param insn {@code non-null;} instruction to insert before
+ * @param newSources {@code non-null;} sources of new instruction
+ * @param newResult {@code non-null;} result of new instruction
+ * @param newOpcode opcode of new instruction
+ * @param cst {@code null-ok;} constant for new instruction, if any
+ */
+ private void insertPlainInsnBefore(SsaInsn insn,
+ RegisterSpecList newSources, RegisterSpec newResult, int newOpcode,
+ Constant cst) {
+
+ Insn originalRopInsn = insn.getOriginalRopInsn();
+ Rop newRop;
+ if (newOpcode == RegOps.MOVE_RESULT_PSEUDO) {
+ newRop = Rops.opMoveResultPseudo(newResult.getType());
+ } else {
+ newRop = Rops.ropFor(newOpcode, newResult, newSources, cst);
+ }
+
+ Insn newRopInsn;
+ if (cst == null) {
+ newRopInsn = new PlainInsn(newRop,
+ originalRopInsn.getPosition(), newResult, newSources);
+ } else {
+ newRopInsn = new PlainCstInsn(newRop,
+ originalRopInsn.getPosition(), newResult, newSources, cst);
+ }
+
+ NormalSsaInsn newInsn = new NormalSsaInsn(newRopInsn, insn.getBlock());
+ List<SsaInsn> insns = insn.getBlock().getInsns();
+
+ insns.add(insns.lastIndexOf(insn), newInsn);
+ ssaMeth.onInsnAdded(newInsn);
+ }
+
+ /**
+ * Inserts a new ThrowingInsn before the given instruction.
+ * TODO: move this somewhere more appropriate
+ *
+ * @param insn {@code non-null;} instruction to insert before
+ * @param newSources {@code non-null;} sources of new instruction
+ * @param newResult {@code non-null;} result of new instruction
+ * @param newOpcode opcode of new instruction
+ * @param cst {@code null-ok;} constant for new instruction, if any
+ */
+ private void insertThrowingInsnBefore(SsaInsn insn,
+ RegisterSpecList newSources, RegisterSpec newResult, int newOpcode,
+ Constant cst) {
+
+ Insn origRopInsn = insn.getOriginalRopInsn();
+ Rop newRop = Rops.ropFor(newOpcode, newResult, newSources, cst);
+ Insn newRopInsn;
+ if (cst == null) {
+ newRopInsn = new ThrowingInsn(newRop,
+ origRopInsn.getPosition(), newSources, StdTypeList.EMPTY);
+ } else {
+ newRopInsn = new ThrowingCstInsn(newRop,
+ origRopInsn.getPosition(), newSources, StdTypeList.EMPTY, cst);
+ }
+
+ NormalSsaInsn newInsn = new NormalSsaInsn(newRopInsn, insn.getBlock());
+ List<SsaInsn> insns = insn.getBlock().getInsns();
+
+ insns.add(insns.lastIndexOf(insn), newInsn);
+ ssaMeth.onInsnAdded(newInsn);
+ }
+}
diff --git a/dx/src/com/android/dx/ssa/InterferenceRegisterMapper.java b/dx/src/com/android/dx/ssa/InterferenceRegisterMapper.java
new file mode 100644
index 0000000..851249b
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/InterferenceRegisterMapper.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.ssa.back.InterferenceGraph;
+import com.android.dx.util.IntSet;
+import com.android.dx.util.BitIntSet;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+
+/**
+ * A register mapper that keeps track of the accumulated interference
+ * information for the registers in the new namespace.
+ *
+ * Please note that this mapper requires that the old namespace does not
+ * have variable register widths/categories, and the new namespace does.
+ */
+public class InterferenceRegisterMapper extends BasicRegisterMapper {
+ /**
+ * Array of interference sets. ArrayList is indexed by new namespace
+ * and BitIntSet's are indexed by old namespace. The list expands
+ * as needed and missing items are assumed to interfere with nothing.
+ *
+ * Bit sets are always used here, unlike elsewhere, because the max
+ * size of this matrix will be (countSsaRegs * countRopRegs), which may
+ * grow to hundreds of K but not megabytes.
+ */
+ private final ArrayList<BitIntSet> newRegInterference;
+
+ /** the interference graph for the old namespace */
+ private final InterferenceGraph oldRegInterference;
+
+ /**
+ * Constructs an instance
+ *
+ * @param countOldRegisters number of registers in old namespace
+ */
+ public InterferenceRegisterMapper(InterferenceGraph oldRegInterference,
+ int countOldRegisters) {
+ super(countOldRegisters);
+
+ newRegInterference = new ArrayList<BitIntSet>();
+ this.oldRegInterference = oldRegInterference;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addMapping(int oldReg, int newReg, int category) {
+ super.addMapping(oldReg, newReg, category);
+
+ addInterfence(newReg, oldReg);
+
+ if (category == 2) {
+ addInterfence(newReg + 1, oldReg);
+ }
+ }
+
+ /**
+ * Checks to see if old namespace reg {@code oldReg} interferes
+ * with what currently maps to {@code newReg}.
+ *
+ * @param oldReg old namespace register
+ * @param newReg new namespace register
+ * @param category category of old namespace register
+ * @return true if oldReg will interfere with newReg
+ */
+ public boolean interferes(int oldReg, int newReg, int category) {
+ if (newReg >= newRegInterference.size()) {
+ return false;
+ } else {
+ IntSet existing = newRegInterference.get(newReg);
+
+ if (existing == null) {
+ return false;
+ } else if (category == 1) {
+ return existing.has(oldReg);
+ } else {
+ return existing.has(oldReg)
+ || (interferes(oldReg, newReg+1, category-1));
+ }
+ }
+ }
+
+ /**
+ * Checks to see if old namespace reg {@code oldReg} interferes
+ * with what currently maps to {@code newReg}.
+ *
+ * @param oldSpec {@code non-null;} old namespace register
+ * @param newReg new namespace register
+ * @return true if oldReg will interfere with newReg
+ */
+ public boolean interferes(RegisterSpec oldSpec, int newReg) {
+ return interferes(oldSpec.getReg(), newReg, oldSpec.getCategory());
+ }
+
+ /**
+ * Adds a register's interference set to the interference list,
+ * growing it if necessary.
+ *
+ * @param newReg register in new namespace
+ * @param oldReg register in old namespace
+ */
+ private void addInterfence(int newReg, int oldReg) {
+ newRegInterference.ensureCapacity(newReg + 1);
+
+ while (newReg >= newRegInterference.size()) {
+ newRegInterference.add(new BitIntSet(newReg +1));
+ }
+
+ oldRegInterference.mergeInterferenceSet(
+ oldReg, newRegInterference.get(newReg));
+ }
+
+ /**
+ * Checks to see if any of a set of old-namespace registers are
+ * pinned to the specified new-namespace reg + category. Takes into
+ * account the category of the old-namespace registers.
+ *
+ * @param oldSpecs {@code non-null;} set of old-namespace regs
+ * @param newReg {@code >= 0;} new-namespace register
+ * @param targetCategory {@code 1..2;} the number of adjacent new-namespace
+ * registers (starting at ropReg) to consider
+ * @return true if any of the old-namespace register have been mapped
+ * to the new-namespace register + category
+ */
+ public boolean areAnyPinned(RegisterSpecList oldSpecs,
+ int newReg, int targetCategory) {
+ int sz = oldSpecs.size();
+
+ for (int i = 0; i < sz; i++) {
+ RegisterSpec oldSpec = oldSpecs.get(i);
+ int r = oldToNew(oldSpec.getReg());
+
+ /*
+ * If oldSpec is a category-2 register, then check both newReg
+ * and newReg - 1.
+ */
+ if (r == newReg
+ || (oldSpec.getCategory() == 2 && (r + 1) == newReg)
+ || (targetCategory == 2 && (r == newReg + 1))) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/dx/src/com/android/dx/ssa/LiteralOpUpgrader.java b/dx/src/com/android/dx/ssa/LiteralOpUpgrader.java
new file mode 100644
index 0000000..01d818d
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/LiteralOpUpgrader.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.TranslationAdvice;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.Insn;
+import com.android.dx.rop.code.Rop;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.PlainInsn;
+import com.android.dx.rop.code.Rops;
+import com.android.dx.rop.code.RegOps;
+import com.android.dx.rop.cst.CstLiteralBits;
+import com.android.dx.rop.type.TypeBearer;
+
+import java.util.List;
+
+/**
+ * Upgrades insn to their literal (constant-immediate) equivilent if possible.
+ * Also switches IF instructions that compare with a constant zero or null
+ * to be their IF_*Z equivalents.
+ */
+public class LiteralOpUpgrader {
+
+ /** method we're processing */
+ private final SsaMethod ssaMeth;
+
+ /**
+ * Process a method.
+ *
+ * @param ssaMethod {@code non-null;} method to process
+ */
+ public static void process(SsaMethod ssaMethod) {
+ LiteralOpUpgrader dc;
+
+ dc = new LiteralOpUpgrader(ssaMethod);
+
+ dc.run();
+ }
+
+ private LiteralOpUpgrader(SsaMethod ssaMethod) {
+ this.ssaMeth = ssaMethod;
+ }
+
+ /**
+ * Returns true if the register contains an integer 0 or a known-null
+ * object reference
+ *
+ * @param spec non-null spec
+ * @return true for 0 or null type bearers
+ */
+ private static boolean isConstIntZeroOrKnownNull(RegisterSpec spec) {
+ TypeBearer tb = spec.getTypeBearer();
+ if (tb instanceof CstLiteralBits) {
+ CstLiteralBits clb = (CstLiteralBits) tb;
+ return (clb.getLongBits() == 0);
+ }
+ return false;
+ }
+
+ /**
+ * Run the literal op upgrader
+ */
+ private void run() {
+ final TranslationAdvice advice = Optimizer.getAdvice();
+
+ ssaMeth.forEachInsn(new SsaInsn.Visitor() {
+ public void visitMoveInsn(NormalSsaInsn insn) {
+ // do nothing
+ }
+
+ public void visitPhiInsn(PhiInsn insn) {
+ // do nothing
+ }
+
+ public void visitNonMoveInsn(NormalSsaInsn insn) {
+
+ Insn originalRopInsn = insn.getOriginalRopInsn();
+ Rop opcode = originalRopInsn.getOpcode();
+ RegisterSpecList sources = insn.getSources();
+
+ if (sources.size() != 2 ) {
+ // We're only dealing with two-source insns here.
+ return;
+ }
+
+ if (opcode.getBranchingness() == Rop.BRANCH_IF) {
+ /*
+ * An if instruction can become an if-*z instruction.
+ */
+ if (isConstIntZeroOrKnownNull(sources.get(0))) {
+ replacePlainInsn(insn, sources.withoutFirst(),
+ RegOps.flippedIfOpcode(opcode.getOpcode()));
+ } else if (isConstIntZeroOrKnownNull(sources.get(1))) {
+ replacePlainInsn(insn, sources.withoutLast(),
+ opcode.getOpcode());
+ }
+ } else if (advice.hasConstantOperation(
+ opcode, sources.get(0), sources.get(1))) {
+ insn.upgradeToLiteral();
+ } else if (opcode.isCommutative()
+ && advice.hasConstantOperation(
+ opcode, sources.get(1), sources.get(0))) {
+ /*
+ * An instruction can be commuted to a literal operation
+ */
+
+ insn.setNewSources(
+ RegisterSpecList.make(
+ sources.get(1), sources.get(0)));
+
+ insn.upgradeToLiteral();
+ }
+ }
+ });
+ }
+
+ /**
+ * Replaces an SsaInsn containing a PlainInsn with a new PlainInsn. The
+ * new PlainInsn is contructed with a new RegOp and new sources.
+ *
+ * TODO move this somewhere else.
+ *
+ * @param insn {@code non-null;} an SsaInsn containing a PlainInsn
+ * @param newSources {@code non-null;} new sources list for new insn
+ * @param newOpcode A RegOp from {@link RegOps}
+ */
+ private void replacePlainInsn(NormalSsaInsn insn,
+ RegisterSpecList newSources, int newOpcode) {
+
+ Insn originalRopInsn = insn.getOriginalRopInsn();
+ Rop newRop = Rops.ropFor(newOpcode,
+ insn.getResult(), newSources, null);
+ Insn newRopInsn = new PlainInsn(newRop,
+ originalRopInsn.getPosition(), insn.getResult(),
+ newSources);
+ NormalSsaInsn newInsn
+ = new NormalSsaInsn(newRopInsn, insn.getBlock());
+
+ List<SsaInsn> insns = insn.getBlock().getInsns();
+
+ ssaMeth.onInsnRemoved(insn);
+ insns.set(insns.lastIndexOf(insn), newInsn);
+ ssaMeth.onInsnAdded(newInsn);
+ }
+}
diff --git a/dx/src/com/android/dx/ssa/LocalVariableExtractor.java b/dx/src/com/android/dx/ssa/LocalVariableExtractor.java
new file mode 100644
index 0000000..11d53cf
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/LocalVariableExtractor.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.RegisterSpecSet;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.util.IntList;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.List;
+
+/**
+ * Code to figure out which local variables are active at which points in
+ * a method. Stolen and retrofitted from
+ * com.android.dx.rop.code.LocalVariableExtractor
+ *
+ * TODO remove this. Allow Rop-form LocalVariableInfo to be passed in,
+ * converted, and adapted through edge-splitting.
+ */
+public class LocalVariableExtractor {
+ /** {@code non-null;} method being extracted from */
+ private final SsaMethod method;
+
+ /** {@code non-null;} block list for the method */
+ private final ArrayList<SsaBasicBlock> blocks;
+
+ /** {@code non-null;} result in-progress */
+ private final LocalVariableInfo resultInfo;
+
+ /** {@code non-null;} work set indicating blocks needing to be processed */
+ private final BitSet workSet;
+
+ /**
+ * Extracts out all the local variable information from the given method.
+ *
+ * @param method {@code non-null;} the method to extract from
+ * @return {@code non-null;} the extracted information
+ */
+ public static LocalVariableInfo extract(SsaMethod method) {
+ LocalVariableExtractor lve = new LocalVariableExtractor(method);
+ return lve.doit();
+ }
+
+ /**
+ * Constructs an instance. This method is private. Use {@link #extract}.
+ *
+ * @param method {@code non-null;} the method to extract from
+ */
+ private LocalVariableExtractor(SsaMethod method) {
+ if (method == null) {
+ throw new NullPointerException("method == null");
+ }
+
+ ArrayList<SsaBasicBlock> blocks = method.getBlocks();
+
+ this.method = method;
+ this.blocks = blocks;
+ this.resultInfo = new LocalVariableInfo(method);
+ this.workSet = new BitSet(blocks.size());
+ }
+
+ /**
+ * Does the extraction.
+ *
+ * @return {@code non-null;} the extracted information
+ */
+ private LocalVariableInfo doit() {
+
+ //FIXME why is this needed here?
+ if (method.getRegCount() > 0 ) {
+ for (int bi = method.getEntryBlockIndex();
+ bi >= 0;
+ bi = workSet.nextSetBit(0)) {
+ workSet.clear(bi);
+ processBlock(bi);
+ }
+ }
+
+ resultInfo.setImmutable();
+ return resultInfo;
+ }
+
+ /**
+ * Processes a single block.
+ *
+ * @param blockIndex {@code >= 0;} block index of the block to process
+ */
+ private void processBlock(int blockIndex) {
+ RegisterSpecSet primaryState
+ = resultInfo.mutableCopyOfStarts(blockIndex);
+ SsaBasicBlock block = blocks.get(blockIndex);
+ List<SsaInsn> insns = block.getInsns();
+ int insnSz = insns.size();
+
+ // The exit block has no insns and no successors
+ if (blockIndex == method.getExitBlockIndex()) {
+ return;
+ }
+
+ /*
+ * We may have to treat the last instruction specially: If it
+ * can (but doesn't always) throw, and the exception can be
+ * caught within the same method, then we need to use the
+ * state *before* executing it to be what is merged into
+ * exception targets.
+ */
+ SsaInsn lastInsn = insns.get(insnSz - 1);
+ boolean hasExceptionHandlers
+ = lastInsn.getOriginalRopInsn().getCatches().size() !=0 ;
+ boolean canThrowDuringLastInsn = hasExceptionHandlers
+ && (lastInsn.getResult() != null);
+ int freezeSecondaryStateAt = insnSz - 1;
+ RegisterSpecSet secondaryState = primaryState;
+
+ /*
+ * Iterate over the instructions, adding information for each place
+ * that the active variable set changes.
+ */
+
+ for (int i = 0; i < insnSz; i++) {
+ if (canThrowDuringLastInsn && (i == freezeSecondaryStateAt)) {
+ // Until this point, primaryState == secondaryState.
+ primaryState.setImmutable();
+ primaryState = primaryState.mutableCopy();
+ }
+
+ SsaInsn insn = insns.get(i);
+ RegisterSpec result;
+
+ result = insn.getLocalAssignment();
+
+ if (result == null) {
+ // We may be nuking an existing local
+
+ result = insn.getResult();
+
+ if (result != null && primaryState.get(result.getReg()) != null) {
+ primaryState.remove(primaryState.get(result.getReg()));
+ }
+ continue;
+ }
+
+ result = result.withSimpleType();
+
+ RegisterSpec already = primaryState.get(result);
+ /*
+ * The equals() check ensures we only add new info if
+ * the instruction causes a change to the set of
+ * active variables.
+ */
+ if (!result.equals(already)) {
+ /*
+ * If this insn represents a local moving from one register
+ * to another, remove the association between the old register
+ * and the local.
+ */
+ RegisterSpec previous
+ = primaryState.localItemToSpec(result.getLocalItem());
+
+ if (previous != null
+ && (previous.getReg() != result.getReg())) {
+
+ primaryState.remove(previous);
+ }
+
+ resultInfo.addAssignment(insn, result);
+ primaryState.put(result);
+ }
+ }
+
+ primaryState.setImmutable();
+
+ /*
+ * Merge this state into the start state for each successor,
+ * and update the work set where required (that is, in cases
+ * where the start state for a block changes).
+ */
+
+ IntList successors = block.getSuccessorList();
+ int succSz = successors.size();
+ int primarySuccessor = block.getPrimarySuccessorIndex();
+
+ for (int i = 0; i < succSz; i++) {
+ int succ = successors.get(i);
+ RegisterSpecSet state = (succ == primarySuccessor) ?
+ primaryState : secondaryState;
+
+ if (resultInfo.mergeStarts(succ, state)) {
+ workSet.set(succ);
+ }
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/ssa/LocalVariableInfo.java b/dx/src/com/android/dx/ssa/LocalVariableInfo.java
new file mode 100644
index 0000000..8845270
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/LocalVariableInfo.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.util.MutabilityControl;
+import com.android.dx.rop.code.RegisterSpecSet;
+import com.android.dx.rop.code.RegisterSpec;
+
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Container for local variable information for a particular {@link
+ * com.android.dx.ssa.SsaMethod}.
+ * Stolen from {@link com.android.dx.rop.code.LocalVariableInfo}.
+ */
+public class LocalVariableInfo extends MutabilityControl {
+ /** {@code >= 0;} the register count for the method */
+ private final int regCount;
+
+ /**
+ * {@code non-null;} {@link com.android.dx.rop.code.RegisterSpecSet} to use when indicating a block
+ * that has no locals; it is empty and immutable but has an appropriate
+ * max size for the method
+ */
+ private final RegisterSpecSet emptySet;
+
+ /**
+ * {@code non-null;} array consisting of register sets representing the
+ * sets of variables already assigned upon entry to each block,
+ * where array indices correspond to block indices
+ */
+ private final RegisterSpecSet[] blockStarts;
+
+ /** {@code non-null;} map from instructions to the variable each assigns */
+ private final HashMap<SsaInsn, RegisterSpec> insnAssignments;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param method {@code non-null;} the method being represented by this instance
+ */
+ public LocalVariableInfo(SsaMethod method) {
+ if (method == null) {
+ throw new NullPointerException("method == null");
+ }
+
+ List<SsaBasicBlock> blocks = method.getBlocks();
+
+ this.regCount = method.getRegCount();
+ this.emptySet = new RegisterSpecSet(regCount);
+ this.blockStarts = new RegisterSpecSet[blocks.size()];
+ this.insnAssignments =
+ new HashMap<SsaInsn, RegisterSpec>(/*hint here*/);
+
+ emptySet.setImmutable();
+ }
+
+ /**
+ * Sets the register set associated with the start of the block with
+ * the given index.
+ *
+ * @param index {@code >= 0;} the block index
+ * @param specs {@code non-null;} the register set to associate with the block
+ */
+ public void setStarts(int index, RegisterSpecSet specs) {
+ throwIfImmutable();
+
+ if (specs == null) {
+ throw new NullPointerException("specs == null");
+ }
+
+ try {
+ blockStarts[index] = specs;
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ // Translate the exception.
+ throw new IllegalArgumentException("bogus index");
+ }
+ }
+
+ /**
+ * Merges the given register set into the set for the block with the
+ * given index. If there was not already an associated set, then this
+ * is the same as calling {@link #setStarts}. Otherwise, this will
+ * merge the two sets and call {@link #setStarts} on the result of the
+ * merge.
+ *
+ * @param index {@code >= 0;} the block index
+ * @param specs {@code non-null;} the register set to merge into the start set
+ * for the block
+ * @return {@code true} if the merge resulted in an actual change
+ * to the associated set (including storing one for the first time) or
+ * {@code false} if there was no change
+ */
+ public boolean mergeStarts(int index, RegisterSpecSet specs) {
+ RegisterSpecSet start = getStarts0(index);
+ boolean changed = false;
+
+ if (start == null) {
+ setStarts(index, specs);
+ return true;
+ }
+
+ RegisterSpecSet newStart = start.mutableCopy();
+ newStart.intersect(specs, true);
+
+ if (start.equals(newStart)) {
+ return false;
+ }
+
+ newStart.setImmutable();
+ setStarts(index, newStart);
+
+ return true;
+ }
+
+ /**
+ * Gets the register set associated with the start of the block
+ * with the given index. This returns an empty set with the appropriate
+ * max size if no set was associated with the block in question.
+ *
+ * @param index {@code >= 0;} the block index
+ * @return {@code non-null;} the associated register set
+ */
+ public RegisterSpecSet getStarts(int index) {
+ RegisterSpecSet result = getStarts0(index);
+
+ return (result != null) ? result : emptySet;
+ }
+
+ /**
+ * Gets the register set associated with the start of the given
+ * block. This is just convenient shorthand for
+ * {@code getStarts(block.getLabel())}.
+ *
+ * @param block {@code non-null;} the block in question
+ * @return {@code non-null;} the associated register set
+ */
+ public RegisterSpecSet getStarts(SsaBasicBlock block) {
+ return getStarts(block.getIndex());
+ }
+
+ /**
+ * Gets a mutable copy of the register set associated with the
+ * start of the block with the given index. This returns a
+ * newly-allocated empty {@link RegisterSpecSet} of appropriate
+ * max size if there is not yet any set associated with the block.
+ *
+ * @param index {@code >= 0;} the block index
+ * @return {@code non-null;} the associated register set
+ */
+ public RegisterSpecSet mutableCopyOfStarts(int index) {
+ RegisterSpecSet result = getStarts0(index);
+
+ return (result != null) ?
+ result.mutableCopy() : new RegisterSpecSet(regCount);
+ }
+
+ /**
+ * Adds an assignment association for the given instruction and
+ * register spec. This throws an exception if the instruction
+ * doesn't actually perform a named variable assignment.
+ *
+ * <b>Note:</b> Although the instruction contains its own spec for
+ * the result, it still needs to be passed in explicitly to this
+ * method, since the spec that is stored here should always have a
+ * simple type and the one in the instruction can be an arbitrary
+ * {@link com.android.dx.rop.type.TypeBearer} (such as a constant value).
+ *
+ * @param insn {@code non-null;} the instruction in question
+ * @param spec {@code non-null;} the associated register spec
+ */
+ public void addAssignment(SsaInsn insn, RegisterSpec spec) {
+ throwIfImmutable();
+
+ if (insn == null) {
+ throw new NullPointerException("insn == null");
+ }
+
+ if (spec == null) {
+ throw new NullPointerException("spec == null");
+ }
+
+ insnAssignments.put(insn, spec);
+ }
+
+ /**
+ * Gets the named register being assigned by the given instruction, if
+ * previously stored in this instance.
+ *
+ * @param insn {@code non-null;} instruction in question
+ * @return {@code null-ok;} the named register being assigned, if any
+ */
+ public RegisterSpec getAssignment(SsaInsn insn) {
+ return insnAssignments.get(insn);
+ }
+
+ /**
+ * Gets the number of assignments recorded by this instance.
+ *
+ * @return {@code >= 0;} the number of assignments
+ */
+ public int getAssignmentCount() {
+ return insnAssignments.size();
+ }
+
+ public void debugDump() {
+ for (int index = 0 ; index < blockStarts.length; index++) {
+ if (blockStarts[index] == null) {
+ continue;
+ }
+
+ if (blockStarts[index] == emptySet) {
+ System.out.printf("%04x: empty set\n", index);
+ } else {
+ System.out.printf("%04x: %s\n", index, blockStarts[index]);
+ }
+ }
+ }
+
+ /**
+ * Helper method, to get the starts for a index, throwing the
+ * right exception for range problems.
+ *
+ * @param index {@code >= 0;} the block index
+ * @return {@code null-ok;} associated register set or {@code null} if there
+ * is none
+ */
+ private RegisterSpecSet getStarts0(int index) {
+ try {
+ return blockStarts[index];
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ // Translate the exception.
+ throw new IllegalArgumentException("bogus index");
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/ssa/MoveParamCombiner.java b/dx/src/com/android/dx/ssa/MoveParamCombiner.java
new file mode 100644
index 0000000..41660d2
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/MoveParamCombiner.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegOps;
+import com.android.dx.rop.code.CstInsn;
+import com.android.dx.rop.code.LocalItem;
+import com.android.dx.rop.cst.CstInteger;
+
+import java.util.HashSet;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Combine identical move-param insns, which may result from Ropper's
+ * handling of synchronized methods.
+ */
+public class MoveParamCombiner {
+
+ /** method to process */
+ private final SsaMethod ssaMeth;
+
+ /**
+ * Processes a method with this optimization step.
+ *
+ * @param ssaMethod method to process
+ */
+ public static void process(SsaMethod ssaMethod) {
+ new MoveParamCombiner(ssaMethod).run();
+ }
+
+ private MoveParamCombiner(SsaMethod ssaMeth) {
+ this.ssaMeth = ssaMeth;
+ }
+
+ /**
+ * Runs this optimization step.
+ */
+ private void run() {
+ // This will contain the definition specs for each parameter
+ final RegisterSpec[] paramSpecs
+ = new RegisterSpec[ssaMeth.getParamWidth()];
+
+ // Insns to delete when all done
+ final HashSet<SsaInsn> deletedInsns = new HashSet();
+
+ ssaMeth.forEachInsn(new SsaInsn.Visitor() {
+ public void visitMoveInsn (NormalSsaInsn insn) {
+ }
+ public void visitPhiInsn (PhiInsn phi) {
+ }
+ public void visitNonMoveInsn (NormalSsaInsn insn) {
+ if (insn.getOpcode().getOpcode() != RegOps.MOVE_PARAM) {
+ return;
+ }
+
+ int param = getParamIndex(insn);
+
+ if (paramSpecs[param] == null) {
+ paramSpecs[param] = insn.getResult();
+ } else {
+ final RegisterSpec specA = paramSpecs[param];
+ final RegisterSpec specB = insn.getResult();
+ LocalItem localA = specA.getLocalItem();
+ LocalItem localB = specB.getLocalItem();
+ LocalItem newLocal;
+
+ /*
+ * Is there local information to preserve?
+ */
+
+ if (localA == null) {
+ newLocal = localB;
+ } else if (localB == null) {
+ newLocal = localA;
+ } else if (localA.equals(localB)) {
+ newLocal = localA;
+ } else {
+ /*
+ * Oddly, these two identical move-params have distinct
+ * debug info. We'll just keep them distinct.
+ */
+ return;
+ }
+
+ ssaMeth.getDefinitionForRegister(specA.getReg())
+ .setResultLocal(newLocal);
+
+ /*
+ * Map all uses of specB to specA
+ */
+
+ RegisterMapper mapper = new RegisterMapper() {
+ /** @inheritDoc */
+ public int getNewRegisterCount() {
+ return ssaMeth.getRegCount();
+ }
+
+ /** @inheritDoc */
+ public RegisterSpec map(RegisterSpec registerSpec) {
+ if (registerSpec.getReg() == specB.getReg()) {
+ return specA;
+ }
+
+ return registerSpec;
+ }
+ };
+
+ List<SsaInsn> uses
+ = ssaMeth.getUseListForRegister(specB.getReg());
+
+ // Use list is modified by mapSourceRegisters
+ for (int i = uses.size() - 1; i >= 0; i--) {
+ SsaInsn use = uses.get(i);
+ use.mapSourceRegisters(mapper);
+ }
+
+ deletedInsns.add(insn);
+ }
+
+ }
+ });
+
+ ssaMeth.deleteInsns(deletedInsns);
+ }
+
+ /**
+ * Returns the parameter index associated with a move-param insn. Does
+ * not verify that the insn is a move-param insn.
+ *
+ * @param insn {@code non-null;} a move-param insn
+ * @return {@code >=0;} parameter index
+ */
+ private int getParamIndex(NormalSsaInsn insn) {
+ CstInsn cstInsn = (CstInsn)(insn.getOriginalRopInsn());
+
+ int param = ((CstInteger)cstInsn.getConstant()).getValue();
+ return param;
+ }
+
+}
diff --git a/dx/src/com/android/dx/ssa/NormalSsaInsn.java b/dx/src/com/android/dx/ssa/NormalSsaInsn.java
new file mode 100644
index 0000000..93d3647
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/NormalSsaInsn.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.*;
+
+/**
+ * A "normal" (non-phi) instruction in SSA form. Always wraps a rop insn.
+ */
+public final class NormalSsaInsn extends SsaInsn implements Cloneable {
+ /** {@code non-null;} rop insn that we're wrapping */
+ private Insn insn;
+
+ /**
+ * Creates an instance.
+ *
+ * @param insn Rop insn to wrap
+ * @param block block that contains this insn
+ */
+ NormalSsaInsn(final Insn insn, final SsaBasicBlock block) {
+ super(insn.getResult(), block);
+ this.insn = insn;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final void mapSourceRegisters(RegisterMapper mapper) {
+ RegisterSpecList oldSources = insn.getSources();
+ RegisterSpecList newSources = mapper.map(oldSources);
+
+ if (newSources != oldSources) {
+ insn = insn.withNewRegisters(getResult(), newSources);
+ getBlock().getParent().onSourcesChanged(this, oldSources);
+ }
+ }
+
+ /**
+ * Changes one of the insn's sources. New source should be of same type
+ * and category.
+ *
+ * @param index {@code >=0;} index of source to change
+ * @param newSpec spec for new source
+ */
+ public final void changeOneSource(int index, RegisterSpec newSpec) {
+ RegisterSpecList origSources = insn.getSources();
+ int sz = origSources.size();
+ RegisterSpecList newSources = new RegisterSpecList(sz);
+
+ for (int i = 0; i < sz; i++) {
+ newSources.set(i, i == index ? newSpec : origSources.get(i));
+ }
+
+ newSources.setImmutable();
+
+ RegisterSpec origSpec = origSources.get(index);
+ if (origSpec.getReg() != newSpec.getReg()) {
+ /*
+ * If the register remains unchanged, we're only changing
+ * the type or local var name so don't update use list
+ */
+ getBlock().getParent().onSourceChanged(this, origSpec, newSpec);
+ }
+
+ insn = insn.withNewRegisters(getResult(), newSources);
+ }
+
+ /**
+ * Changes the source list of the insn. New source list should be the
+ * same size and consist of sources of identical types.
+ *
+ * @param newSources non-null new sources list.
+ */
+ public final void setNewSources (RegisterSpecList newSources) {
+ RegisterSpecList origSources = insn.getSources();
+
+ if (origSources.size() != newSources.size()) {
+ throw new RuntimeException("Sources counts don't match");
+ }
+
+ insn = insn.withNewRegisters(getResult(), newSources);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public NormalSsaInsn clone() {
+ return (NormalSsaInsn) super.clone();
+ }
+
+ /**
+ * Like rop.Insn.getSources().
+ *
+ * @return {@code null-ok;} sources list
+ */
+ public RegisterSpecList getSources() {
+ return insn.getSources();
+ }
+
+ /** {@inheritDoc} */
+ public String toHuman() {
+ return toRopInsn().toHuman();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Insn toRopInsn() {
+ return insn.withNewRegisters(getResult(), insn.getSources());
+ }
+
+ /**
+ * @return the Rop opcode for this insn
+ */
+ @Override
+ public Rop getOpcode() {
+ return insn.getOpcode();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Insn getOriginalRopInsn() {
+ return insn;
+ }
+
+ /** {@inheritDoc} */
+ public RegisterSpec getLocalAssignment() {
+ RegisterSpec assignment;
+
+ if (insn.getOpcode().getOpcode() == RegOps.MARK_LOCAL) {
+ assignment = insn.getSources().get(0);
+ } else {
+ assignment = getResult();
+ }
+
+ if (assignment == null) {
+ return null;
+ }
+
+ LocalItem local = assignment.getLocalItem();
+
+ if (local == null) {
+ return null;
+ }
+
+ return assignment;
+ }
+
+ /**
+ * Upgrades this insn to a version that represents the constant last
+ * source literally. If the upgrade is not possible, this does nothing.
+ *
+ * @see Insn#withLastSourceLiteral
+ */
+ public void upgradeToLiteral() {
+ RegisterSpecList oldSources = insn.getSources();
+
+ insn = insn.withLastSourceLiteral();
+ getBlock().getParent().onSourcesChanged(this, oldSources);
+ }
+
+ /**
+ * @return true if this is a move (but not a move-operand) instruction
+ */
+ @Override
+ public boolean isNormalMoveInsn() {
+ return insn.getOpcode().getOpcode() == RegOps.MOVE;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isMoveException() {
+ return insn.getOpcode().getOpcode() == RegOps.MOVE_EXCEPTION;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean canThrow() {
+ return insn.canThrow();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void accept(Visitor v) {
+ if (isNormalMoveInsn()) {
+ v.visitMoveInsn(this);
+ } else {
+ v.visitNonMoveInsn(this);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isPhiOrMove() {
+ return isNormalMoveInsn();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * TODO: Increase the scope of this.
+ */
+ @Override
+ public boolean hasSideEffect() {
+ Rop opcode = getOpcode();
+
+ if (opcode.getBranchingness() != Rop.BRANCH_NONE) {
+ return true;
+ }
+
+ boolean hasLocalSideEffect
+ = Optimizer.getPreserveLocals() && getLocalAssignment() != null;
+
+ switch (opcode.getOpcode()) {
+ case RegOps.MOVE_RESULT:
+ case RegOps.MOVE:
+ case RegOps.CONST:
+ return hasLocalSideEffect;
+ default:
+ return true;
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/ssa/Optimizer.java b/dx/src/com/android/dx/ssa/Optimizer.java
new file mode 100644
index 0000000..06ed138
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/Optimizer.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.code.TranslationAdvice;
+import com.android.dx.ssa.back.LivenessAnalyzer;
+import com.android.dx.ssa.back.SsaToRop;
+
+import java.util.EnumSet;
+
+/**
+ * Runs a method through the SSA form conversion, any optimization algorithms,
+ * and returns it to rop form.
+ */
+public class Optimizer {
+ private static boolean preserveLocals = true;
+
+ private static TranslationAdvice advice;
+
+ /** optional optimizer steps */
+ public enum OptionalStep {
+ MOVE_PARAM_COMBINER, SCCP, LITERAL_UPGRADE, CONST_COLLECTOR,
+ ESCAPE_ANALYSIS
+ }
+
+ /**
+ * @return true if local variable information should be preserved, even
+ * at code size/register size cost
+ */
+ public static boolean getPreserveLocals() {
+ return preserveLocals;
+ }
+
+ /**
+ * @return {@code non-null;} translation advice
+ */
+ public static TranslationAdvice getAdvice() {
+ return advice;
+ }
+
+ /**
+ * Runs optimization algorthims over this method, and returns a new
+ * instance of RopMethod with the changes.
+ *
+ * @param rmeth method to process
+ * @param paramWidth the total width, in register-units, of this method's
+ * parameters
+ * @param isStatic true if this method has no 'this' pointer argument.
+ * @param inPreserveLocals true if local variable info should be preserved,
+ * at the cost of some registers and insns
+ * @param inAdvice {@code non-null;} translation advice
+ * @return optimized method
+ */
+ public static RopMethod optimize(RopMethod rmeth, int paramWidth,
+ boolean isStatic, boolean inPreserveLocals,
+ TranslationAdvice inAdvice) {
+
+ return optimize(rmeth, paramWidth, isStatic, inPreserveLocals, inAdvice,
+ EnumSet.allOf(OptionalStep.class));
+ }
+
+ /**
+ * Runs optimization algorthims over this method, and returns a new
+ * instance of RopMethod with the changes.
+ *
+ * @param rmeth method to process
+ * @param paramWidth the total width, in register-units, of this method's
+ * parameters
+ * @param isStatic true if this method has no 'this' pointer argument.
+ * @param inPreserveLocals true if local variable info should be preserved,
+ * at the cost of some registers and insns
+ * @param inAdvice {@code non-null;} translation advice
+ * @param steps set of optional optimization steps to run
+ * @return optimized method
+ */
+ public static RopMethod optimize(RopMethod rmeth, int paramWidth,
+ boolean isStatic, boolean inPreserveLocals,
+ TranslationAdvice inAdvice, EnumSet<OptionalStep> steps) {
+ SsaMethod ssaMeth = null;
+
+ preserveLocals = inPreserveLocals;
+ advice = inAdvice;
+
+ ssaMeth = SsaConverter.convertToSsaMethod(rmeth, paramWidth, isStatic);
+ runSsaFormSteps(ssaMeth, steps);
+
+ RopMethod resultMeth = SsaToRop.convertToRopMethod(ssaMeth, false);
+
+ if (resultMeth.getBlocks().getRegCount()
+ > advice.getMaxOptimalRegisterCount()) {
+ // Try to see if we can squeeze it under the register count bar
+ resultMeth = optimizeMinimizeRegisters(rmeth, paramWidth, isStatic,
+ steps);
+ }
+ return resultMeth;
+ }
+
+ /**
+ * Runs the optimizer with a strategy to minimize the number of rop-form
+ * registers used by the end result. Dex bytecode does not have instruction
+ * forms that take register numbers larger than 15 for all instructions.
+ * If we've produced a method that uses more than 16 registers, try again
+ * with a different strategy to see if we can get under the bar. The end
+ * result will be much more efficient.
+ *
+ * @param rmeth method to process
+ * @param paramWidth the total width, in register-units, of this method's
+ * parameters
+ * @param isStatic true if this method has no 'this' pointer argument.
+ * @param steps set of optional optimization steps to run
+ * @return optimized method
+ */
+ private static RopMethod optimizeMinimizeRegisters(RopMethod rmeth,
+ int paramWidth, boolean isStatic,
+ EnumSet<OptionalStep> steps) {
+ SsaMethod ssaMeth;
+ RopMethod resultMeth;
+
+ ssaMeth = SsaConverter.convertToSsaMethod(
+ rmeth, paramWidth, isStatic);
+
+ EnumSet<OptionalStep> newSteps = steps.clone();
+
+ /*
+ * CONST_COLLECTOR trades insns for registers, which is not an
+ * appropriate strategy here.
+ */
+ newSteps.remove(OptionalStep.CONST_COLLECTOR);
+
+ runSsaFormSteps(ssaMeth, newSteps);
+
+ resultMeth = SsaToRop.convertToRopMethod(ssaMeth, true);
+ return resultMeth;
+ }
+
+ private static void runSsaFormSteps(SsaMethod ssaMeth,
+ EnumSet<OptionalStep> steps) {
+ boolean needsDeadCodeRemover = true;
+
+ if (steps.contains(OptionalStep.MOVE_PARAM_COMBINER)) {
+ MoveParamCombiner.process(ssaMeth);
+ }
+
+ if (steps.contains(OptionalStep.SCCP)) {
+ SCCP.process(ssaMeth);
+ }
+
+ if (steps.contains(OptionalStep.LITERAL_UPGRADE)) {
+ LiteralOpUpgrader.process(ssaMeth);
+ DeadCodeRemover.process(ssaMeth);
+ needsDeadCodeRemover = false;
+ }
+
+ /*
+ * ESCAPE_ANALYSIS impacts debuggability, so left off by default
+ */
+ steps.remove(OptionalStep.ESCAPE_ANALYSIS);
+ if (steps.contains(OptionalStep.ESCAPE_ANALYSIS)) {
+ EscapeAnalysis.process(ssaMeth);
+ DeadCodeRemover.process(ssaMeth);
+ needsDeadCodeRemover = false;
+ }
+
+ if (steps.contains(OptionalStep.CONST_COLLECTOR)) {
+ ConstCollector.process(ssaMeth);
+ DeadCodeRemover.process(ssaMeth);
+ needsDeadCodeRemover = false;
+ }
+
+ // dead code remover must be run before phi type resolver
+ if (needsDeadCodeRemover) {
+ DeadCodeRemover.process(ssaMeth);
+ }
+
+ PhiTypeResolver.process(ssaMeth);
+ }
+
+ public static SsaMethod debugEdgeSplit(RopMethod rmeth, int paramWidth,
+ boolean isStatic, boolean inPreserveLocals,
+ TranslationAdvice inAdvice) {
+
+ preserveLocals = inPreserveLocals;
+ advice = inAdvice;
+
+ return SsaConverter.testEdgeSplit(rmeth, paramWidth, isStatic);
+ }
+
+ public static SsaMethod debugPhiPlacement(RopMethod rmeth, int paramWidth,
+ boolean isStatic, boolean inPreserveLocals,
+ TranslationAdvice inAdvice) {
+
+ preserveLocals = inPreserveLocals;
+ advice = inAdvice;
+
+ return SsaConverter.testPhiPlacement(rmeth, paramWidth, isStatic);
+ }
+
+ public static SsaMethod debugRenaming(RopMethod rmeth, int paramWidth,
+ boolean isStatic, boolean inPreserveLocals,
+ TranslationAdvice inAdvice) {
+
+ preserveLocals = inPreserveLocals;
+ advice = inAdvice;
+
+ return SsaConverter.convertToSsaMethod(rmeth, paramWidth, isStatic);
+ }
+
+ public static SsaMethod debugDeadCodeRemover(RopMethod rmeth,
+ int paramWidth, boolean isStatic, boolean inPreserveLocals,
+ TranslationAdvice inAdvice) {
+
+ SsaMethod ssaMeth;
+
+ preserveLocals = inPreserveLocals;
+ advice = inAdvice;
+
+ ssaMeth = SsaConverter.convertToSsaMethod(rmeth, paramWidth, isStatic);
+ DeadCodeRemover.process(ssaMeth);
+
+ return ssaMeth;
+ }
+
+ public static SsaMethod debugNoRegisterAllocation(RopMethod rmeth,
+ int paramWidth, boolean isStatic, boolean inPreserveLocals,
+ TranslationAdvice inAdvice, EnumSet<OptionalStep> steps) {
+
+ SsaMethod ssaMeth;
+
+ preserveLocals = inPreserveLocals;
+ advice = inAdvice;
+
+ ssaMeth = SsaConverter.convertToSsaMethod(rmeth, paramWidth, isStatic);
+
+ runSsaFormSteps(ssaMeth, steps);
+
+ LivenessAnalyzer.constructInterferenceGraph(ssaMeth);
+
+ return ssaMeth;
+ }
+}
diff --git a/dx/src/com/android/dx/ssa/PhiInsn.java b/dx/src/com/android/dx/ssa/PhiInsn.java
new file mode 100644
index 0000000..3ea876f
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/PhiInsn.java
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.*;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.util.Hex;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A Phi instruction (magical post-control-flow-merge) instruction
+ * in SSA form. Will be converted to moves in predecessor blocks before
+ * conversion back to ROP form.
+ */
+public final class PhiInsn extends SsaInsn {
+ /**
+ * result register. The original result register of the phi insn
+ * is needed during the renaming process after the new result
+ * register has already been chosen.
+ */
+ private final int ropResultReg;
+
+ /**
+ * {@code non-null;} operands of the instruction; built up by
+ * {@link #addPhiOperand}
+ */
+ private final ArrayList<Operand> operands = new ArrayList<Operand>();
+
+ /** {@code null-ok;} source registers; constructed lazily */
+ private RegisterSpecList sources;
+
+ /**
+ * Constructs a new phi insn with no operands.
+ *
+ * @param resultReg the result reg for this phi insn
+ * @param block block containing this insn.
+ */
+ public PhiInsn(RegisterSpec resultReg, SsaBasicBlock block) {
+ super(resultReg, block);
+ ropResultReg = resultReg.getReg();
+ }
+
+ /**
+ * Makes a phi insn with a void result type.
+ *
+ * @param resultReg the result register for this phi insn.
+ * @param block block containing this insn.
+ */
+ public PhiInsn(final int resultReg, final SsaBasicBlock block) {
+ /*
+ * The result type here is bogus: The type depends on the
+ * operand and will be derived later.
+ */
+ super(RegisterSpec.make(resultReg, Type.VOID), block);
+ ropResultReg = resultReg;
+ }
+
+ /** {@inheritDoc} */
+ public PhiInsn clone() {
+ throw new UnsupportedOperationException("can't clone phi");
+ }
+
+ /**
+ * Updates the TypeBearers of all the sources (phi operands) to be
+ * the current TypeBearer of the register-defining instruction's result.
+ * This is used during phi-type resolution.<p>
+ *
+ * Note that local association of operands are preserved in this step.
+ *
+ * @param ssaMeth method that contains this insn
+ */
+ public void updateSourcesToDefinitions(SsaMethod ssaMeth) {
+ for (Operand o : operands) {
+ RegisterSpec def
+ = ssaMeth.getDefinitionForRegister(
+ o.regSpec.getReg()).getResult();
+
+ o.regSpec = o.regSpec.withType(def.getType());
+ }
+
+ sources = null;
+ }
+
+ /**
+ * Changes the result type. Used during phi type resolution
+ *
+ * @param type {@code non-null;} new TypeBearer
+ * @param local {@code null-ok;} new local info, if available
+ */
+ public void changeResultType(TypeBearer type, LocalItem local) {
+ setResult(RegisterSpec.makeLocalOptional(
+ getResult().getReg(), type, local));
+ }
+
+ /**
+ * Gets the original rop-form result reg. This is useful during renaming.
+ *
+ * @return the original rop-form result reg
+ */
+ public int getRopResultReg() {
+ return ropResultReg;
+ }
+
+ /**
+ * Adds an operand to this phi instruction.
+ *
+ * @param registerSpec register spec, including type and reg of operand
+ * @param predBlock predecessor block to be associated with this operand
+ */
+ public void addPhiOperand(RegisterSpec registerSpec,
+ SsaBasicBlock predBlock) {
+ operands.add(new Operand(registerSpec, predBlock.getIndex(),
+ predBlock.getRopLabel()));
+
+ // Un-cache sources, in case someone has already called getSources().
+ sources = null;
+ }
+
+ /**
+ * Gets the index of the pred block associated with the RegisterSpec
+ * at the particular getSources() index.
+ *
+ * @param sourcesIndex index of source in getSources()
+ * @return block index
+ */
+ public int predBlockIndexForSourcesIndex(int sourcesIndex) {
+ return operands.get(sourcesIndex).blockIndex;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Always returns null for {@code PhiInsn}s.
+ */
+ @Override
+ public Rop getOpcode() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Always returns null for {@code PhiInsn}s.
+ */
+ @Override
+ public Insn getOriginalRopInsn() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Always returns false for {@code PhiInsn}s.
+ */
+ @Override
+ public boolean canThrow() {
+ return false;
+ }
+
+ /**
+ * Gets sources. Constructed lazily from phi operand data structures and
+ * then cached.
+ *
+ * @return {@code non-null;} sources list
+ */
+ public RegisterSpecList getSources() {
+ if (sources != null) {
+ return sources;
+ }
+
+ if (operands.size() == 0) {
+ // How'd this happen? A phi insn with no operand?
+ return RegisterSpecList.EMPTY;
+ }
+
+ int szSources = operands.size();
+ sources = new RegisterSpecList(szSources);
+
+ for (int i = 0; i < szSources; i++) {
+ Operand o = operands.get(i);
+
+ sources.set(i, o.regSpec);
+ }
+
+ sources.setImmutable();
+ return sources;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isRegASource(int reg) {
+ /*
+ * Avoid creating a sources list in case it has not already been
+ * created.
+ */
+
+ for (Operand o : operands) {
+ if (o.regSpec.getReg() == reg) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @return true if all operands use the same register
+ */
+ public boolean areAllOperandsEqual() {
+ if (operands.size() == 0 ) {
+ // This should never happen.
+ return true;
+ }
+
+ int firstReg = operands.get(0).regSpec.getReg();
+ for (Operand o : operands) {
+ if (firstReg != o.regSpec.getReg()) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final void mapSourceRegisters(RegisterMapper mapper) {
+ for (Operand o : operands) {
+ RegisterSpec old = o.regSpec;
+ o.regSpec = mapper.map(old);
+ if (old != o.regSpec) {
+ getBlock().getParent().onSourceChanged(this, old, o.regSpec);
+ }
+ }
+ sources = null;
+ }
+
+ /**
+ * Always throws an exeption, since a phi insn may not be
+ * converted back to rop form.
+ *
+ * @return always throws exception
+ */
+ @Override
+ public Insn toRopInsn() {
+ throw new IllegalArgumentException(
+ "Cannot convert phi insns to rop form");
+ }
+
+ /**
+ * Returns the list of predecessor blocks associated with all operands
+ * that have {@code reg} as an operand register.
+ *
+ * @param reg register to look up
+ * @param ssaMeth method we're operating on
+ * @return list of predecessor blocks, empty if none
+ */
+ public List<SsaBasicBlock> predBlocksForReg(int reg, SsaMethod ssaMeth) {
+ ArrayList<SsaBasicBlock> ret = new ArrayList<SsaBasicBlock>();
+
+ for (Operand o : operands) {
+ if (o.regSpec.getReg() == reg) {
+ ret.add(ssaMeth.getBlocks().get(o.blockIndex));
+ }
+ }
+
+ return ret;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isPhiOrMove() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean hasSideEffect() {
+ return Optimizer.getPreserveLocals() && getLocalAssignment() != null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void accept(SsaInsn.Visitor v) {
+ v.visitPhiInsn(this);
+ }
+
+ /** {@inheritDoc} */
+ public String toHuman() {
+ return toHumanWithInline(null);
+ }
+
+ /**
+ * Returns human-readable string for listing dumps. This method
+ * allows sub-classes to specify extra text.
+ *
+ * @param extra {@code null-ok;} the argument to print after the opcode
+ * @return human-readable string for listing dumps
+ */
+ protected final String toHumanWithInline(String extra) {
+ StringBuffer sb = new StringBuffer(80);
+
+ sb.append(SourcePosition.NO_INFO);
+ sb.append(": phi");
+
+ if (extra != null) {
+ sb.append("(");
+ sb.append(extra);
+ sb.append(")");
+ }
+
+ RegisterSpec result = getResult();
+
+ if (result == null) {
+ sb.append(" .");
+ } else {
+ sb.append(" ");
+ sb.append(result.toHuman());
+ }
+
+ sb.append(" <-");
+
+ int sz = getSources().size();
+ if (sz == 0) {
+ sb.append(" .");
+ } else {
+ for (int i = 0; i < sz; i++) {
+ sb.append(" ");
+ sb.append(sources.get(i).toHuman()
+ + "[b="
+ + Hex.u2(operands.get(i).ropLabel) + "]");
+ }
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * A single phi operand, consiting of source register and block index
+ * for move.
+ */
+ private static class Operand {
+ public RegisterSpec regSpec;
+ public final int blockIndex;
+ public final int ropLabel; // only used for debugging
+
+ public Operand(RegisterSpec regSpec, int blockIndex, int ropLabel) {
+ this.regSpec = regSpec;
+ this.blockIndex = blockIndex;
+ this.ropLabel = ropLabel;
+ }
+ }
+
+ /**
+ * Visitor interface for instances of this (outer) class.
+ */
+ public static interface Visitor {
+ public void visitPhiInsn(PhiInsn insn);
+ }
+}
diff --git a/dx/src/com/android/dx/ssa/PhiTypeResolver.java b/dx/src/com/android/dx/ssa/PhiTypeResolver.java
new file mode 100644
index 0000000..4b8b4e3
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/PhiTypeResolver.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.cf.code.Merger;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.LocalItem;
+import com.android.dx.rop.type.Type;
+import com.android.dx.rop.type.TypeBearer;
+
+import java.util.BitSet;
+import java.util.List;
+
+/**
+ * Resolves the result types of phi instructions. When phi instructions
+ * are inserted, their result types are set to BT_VOID (which is a nonsensical
+ * type for a register) but must be resolve to a real type before converting
+ * out of SSA form.<p>
+ *
+ * The resolve is done as an iterative merge of each phi's operand types.
+ * Phi operands may be themselves be the result of unresolved phis,
+ * and the algorithm tries to find the most-fit type (for example, if every
+ * operand is the same constant value or the same local variable info, we want
+ * that to be reflected).<p>
+ *
+ * This algorithm assumes a dead-code remover has already removed all
+ * circular-only phis that may have been inserted.
+ */
+public class PhiTypeResolver {
+
+ SsaMethod ssaMeth;
+ /** indexed by register; all registers still defined by unresolved phis */
+ private final BitSet worklist;
+
+ /**
+ * Resolves all phi types in the method
+ * @param ssaMeth method to process
+ */
+ public static void process (SsaMethod ssaMeth) {
+ new PhiTypeResolver(ssaMeth).run();
+ }
+
+ private PhiTypeResolver(SsaMethod ssaMeth) {
+ this.ssaMeth = ssaMeth;
+ worklist = new BitSet(ssaMeth.getRegCount());
+ }
+
+ /**
+ * Runs the phi-type resolver.
+ */
+ private void run() {
+
+ int regCount = ssaMeth.getRegCount();
+
+ for (int reg = 0; reg < regCount; reg++) {
+ SsaInsn definsn = ssaMeth.getDefinitionForRegister(reg);
+
+ if (definsn != null
+ && (definsn.getResult().getBasicType() == Type.BT_VOID)) {
+ worklist.set(reg);
+ }
+ }
+
+ int reg;
+ while ( 0 <= (reg = worklist.nextSetBit(0))) {
+ worklist.clear(reg);
+
+ /*
+ * definitions on the worklist have a type of BT_VOID, which
+ * must have originated from a PhiInsn.
+ */
+ PhiInsn definsn = (PhiInsn)ssaMeth.getDefinitionForRegister(reg);
+
+ if (resolveResultType(definsn)) {
+ /*
+ * If the result type has changed, re-resolve all phis
+ * that use this.
+ */
+
+ List<SsaInsn> useList = ssaMeth.getUseListForRegister(reg);
+
+ int sz = useList.size();
+ for (int i = 0; i < sz; i++ ) {
+ SsaInsn useInsn = useList.get(i);
+ RegisterSpec resultReg = useInsn.getResult();
+ if (resultReg != null && useInsn instanceof PhiInsn) {
+ worklist.set(resultReg.getReg());
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns true if a and b are equal, whether
+ * or not either of them are null.
+ * @param a
+ * @param b
+ * @return true if equal
+ */
+ private static boolean equalsHandlesNulls(LocalItem a, LocalItem b) {
+ return (a == b) || ((a != null) && a.equals(b));
+ }
+
+ /**
+ * Resolves the result of a phi insn based on its operands. The "void"
+ * type, which is a nonsensical type for a register, is used for
+ * registers defined by as-of-yet-unresolved phi operations.
+ *
+ * @return true if the result type changed, false if no change
+ */
+ boolean resolveResultType(PhiInsn insn) {
+ insn.updateSourcesToDefinitions(ssaMeth);
+
+ RegisterSpecList sources = insn.getSources();
+
+ // Start by finding the first non-void operand
+ RegisterSpec first = null;
+ int firstIndex = -1;
+
+ int szSources = sources.size();
+ for (int i = 0 ; i <szSources ; i++) {
+ RegisterSpec rs = sources.get(i);
+
+ if (rs.getBasicType() != Type.BT_VOID) {
+ first = rs;
+ firstIndex = i;
+ }
+ }
+
+ if (first == null) {
+ // All operands are void -- we're not ready to resolve yet
+ return false;
+ }
+
+ LocalItem firstLocal = first.getLocalItem();
+ TypeBearer mergedType = first.getType();
+ boolean sameLocals = true;
+ for (int i = 0 ; i < szSources ; i++) {
+ if (i == firstIndex) {
+ continue;
+ }
+
+ RegisterSpec rs = sources.get(i);
+
+ // Just skip void (unresolved phi results) for now
+ if (rs.getBasicType() == Type.BT_VOID){
+ continue;
+ }
+
+ sameLocals = sameLocals
+ && equalsHandlesNulls(firstLocal, rs.getLocalItem());
+
+ mergedType = Merger.mergeType(mergedType, rs.getType());
+ }
+
+ TypeBearer newResultType;
+
+ if (mergedType != null) {
+ newResultType = mergedType;
+ } else {
+ StringBuilder sb = new StringBuilder();
+
+ for (int i = 0; i < szSources; i++) {
+ sb.append(sources.get(i).toString());
+ sb.append(' ');
+ }
+
+ throw new RuntimeException ("Couldn't map types in phi insn:" + sb);
+ }
+
+ LocalItem newLocal = sameLocals ? firstLocal : null;
+
+ RegisterSpec result = insn.getResult();
+
+ if ((result.getTypeBearer() == newResultType)
+ && equalsHandlesNulls(newLocal, result.getLocalItem())) {
+ return false;
+ }
+
+ insn.changeResultType(newResultType, newLocal);
+
+ return true;
+ }
+}
diff --git a/dx/src/com/android/dx/ssa/RegisterMapper.java b/dx/src/com/android/dx/ssa/RegisterMapper.java
new file mode 100644
index 0000000..bef941f
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/RegisterMapper.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.util.ToHuman;
+
+/**
+ * Represents a mapping between two register numbering schemes.
+ * Subclasses of this may be mutable, and as such the mapping provided
+ * is only valid for the lifetime of the method call in which
+ * instances of this class are passed.
+ */
+public abstract class RegisterMapper {
+ /**
+ * Gets the count of registers (really, the total register width, since
+ * category width is counted) in the new namespace.
+ * @return >= 0 width of new namespace.
+ */
+ public abstract int getNewRegisterCount();
+
+ /**
+ * @param registerSpec old register
+ * @return register in new space
+ */
+ public abstract RegisterSpec map(RegisterSpec registerSpec);
+
+ /**
+ *
+ * @param sources old register list
+ * @return new mapped register list, or old if nothing has changed.
+ */
+ public final RegisterSpecList map(RegisterSpecList sources) {
+ int sz = sources.size();
+ RegisterSpecList newSources = new RegisterSpecList(sz);
+
+ for (int i = 0; i < sz; i++) {
+ newSources.set(i, map(sources.get(i)));
+ }
+
+ newSources.setImmutable();
+
+ // Return the old sources if nothing has changed.
+ return newSources.equals(sources) ? sources : newSources;
+ }
+}
diff --git a/dx/src/com/android/dx/ssa/SCCP.java b/dx/src/com/android/dx/ssa/SCCP.java
new file mode 100644
index 0000000..42abbb2
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/SCCP.java
@@ -0,0 +1,483 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.CstInsn;
+import com.android.dx.rop.code.Insn;
+import com.android.dx.rop.code.RegOps;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.Rop;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.cst.Constant;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.rop.cst.TypedConstant;
+import com.android.dx.rop.type.TypeBearer;
+import com.android.dx.rop.type.Type;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+
+/**
+ * A small variant of Wegman and Zadeck's Sparse Conditional Constant
+ * Propagation algorithm.
+ */
+public class SCCP {
+ /** Lattice values */
+ private static final int TOP = 0;
+ private static final int CONSTANT = 1;
+ private static final int VARYING = 2;
+ /** method we're processing */
+ private SsaMethod ssaMeth;
+ /** ssaMeth.getRegCount() */
+ private int regCount;
+ /** Lattice values for each SSA register */
+ private int[] latticeValues;
+ /** For those registers that are constant, this is the constant value */
+ private Constant[] latticeConstants;
+ /** Worklist of basic blocks to be processed */
+ private ArrayList<SsaBasicBlock> cfgWorklist;
+ /** Bitset containing bits for each block that has been found executable */
+ private BitSet executableBlocks;
+ /** Worklist for SSA edges. This is a list of registers to process */
+ private ArrayList<SsaInsn> ssaWorklist;
+ /**
+ * Worklist for SSA edges that represent varying values. It makes the
+ * algorithm much faster if you move all values to VARYING as fast as
+ * possible.
+ */
+ private ArrayList<SsaInsn> varyingWorklist;
+
+ private SCCP(SsaMethod ssaMeth) {
+ this.ssaMeth = ssaMeth;
+ this.regCount = ssaMeth.getRegCount();
+ this.latticeValues = new int[this.regCount];
+ this.latticeConstants = new Constant[this.regCount];
+ this.cfgWorklist = new ArrayList<SsaBasicBlock>();
+ this.executableBlocks = new BitSet(ssaMeth.getBlocks().size());
+ this.ssaWorklist = new ArrayList<SsaInsn>();
+ this.varyingWorklist = new ArrayList<SsaInsn>();
+ for (int i = 0; i < this.regCount; i++) {
+ latticeValues[i] = TOP;
+ latticeConstants[i] = null;
+ }
+ }
+
+ /**
+ * Performs sparse conditional constant propagation on a method.
+ * @param ssaMethod Method to process
+ */
+ public static void process (SsaMethod ssaMethod) {
+ new SCCP(ssaMethod).run();
+ }
+
+ /**
+ * Add a new SSA basic block to the CFG worklist
+ * @param ssaBlock Block to add
+ */
+ private void addBlockToWorklist(SsaBasicBlock ssaBlock) {
+ if (!executableBlocks.get(ssaBlock.getIndex())) {
+ cfgWorklist.add(ssaBlock);
+ executableBlocks.set(ssaBlock.getIndex());
+ }
+ }
+
+ /**
+ * Adds an SSA register's uses to the SSA worklist.
+ * @param reg SSA register
+ * @param latticeValue new lattice value for @param reg.
+ */
+ private void addUsersToWorklist(int reg, int latticeValue) {
+ if (latticeValue == VARYING) {
+ for (SsaInsn insn : ssaMeth.getUseListForRegister(reg)) {
+ varyingWorklist.add(insn);
+ }
+ } else {
+ for (SsaInsn insn : ssaMeth.getUseListForRegister(reg)) {
+ ssaWorklist.add(insn);
+ }
+ }
+ }
+
+ /**
+ * Sets a lattice value for a register to value.
+ * @param reg SSA register
+ * @param value Lattice value
+ * @param cst Constant value (may be null)
+ * @return true if the lattice value changed.
+ */
+ private boolean setLatticeValueTo(int reg, int value, Constant cst) {
+ if (value != CONSTANT) {
+ if (latticeValues[reg] != value) {
+ latticeValues[reg] = value;
+ return true;
+ }
+ return false;
+ } else {
+ if (latticeValues[reg] != value
+ || !latticeConstants[reg].equals(cst)) {
+ latticeValues[reg] = value;
+ latticeConstants[reg] = cst;
+ return true;
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Simulates a PHI node and set the lattice for the result
+ * to the approriate value.
+ * Meet values:
+ * TOP x anything = anything
+ * VARYING x anything = VARYING
+ * CONSTANT x CONSTANT = CONSTANT if equal constants, VARYING otherwise
+ * @param insn PHI to simulate.
+ */
+ private void simulatePhi(PhiInsn insn) {
+ int phiResultReg = insn.getResult().getReg();
+
+ if (latticeValues[phiResultReg] == VARYING) {
+ return;
+ }
+
+ RegisterSpecList sources = insn.getSources();
+ int phiResultValue = TOP;
+ Constant phiConstant = null;
+ int sourceSize = sources.size();
+
+ for (int i = 0; i < sourceSize; i++) {
+ int predBlockIndex = insn.predBlockIndexForSourcesIndex(i);
+ int sourceReg = sources.get(i).getReg();
+ int sourceRegValue = latticeValues[sourceReg];
+
+ if (!executableBlocks.get(predBlockIndex)
+ || sourceRegValue == TOP) {
+ continue;
+ }
+
+ if (sourceRegValue == CONSTANT) {
+ if (phiConstant == null) {
+ phiConstant = latticeConstants[sourceReg];
+ phiResultValue = CONSTANT;
+ } else if (!latticeConstants[sourceReg].equals(phiConstant)){
+ phiResultValue = VARYING;
+ break;
+ }
+
+ } else if (sourceRegValue == VARYING) {
+ phiResultValue = VARYING;
+ break;
+ }
+ }
+ if (setLatticeValueTo(phiResultReg, phiResultValue, phiConstant)) {
+ addUsersToWorklist(phiResultReg, phiResultValue);
+ }
+ }
+
+ /**
+ * Simulate a block and note the results in the lattice.
+ * @param block Block to visit
+ */
+ private void simulateBlock(SsaBasicBlock block) {
+ for (SsaInsn insn : block.getInsns()) {
+ if (insn instanceof PhiInsn) {
+ simulatePhi((PhiInsn) insn);
+ } else {
+ simulateStmt(insn);
+ }
+ }
+ }
+ private static String latticeValName(int latticeVal) {
+ switch (latticeVal) {
+ case TOP: return "TOP";
+ case CONSTANT: return "CONSTANT";
+ case VARYING: return "VARYING";
+ default: return "UNKNOWN";
+ }
+ }
+
+ /**
+ * Simplifies a jump statement.
+ * @param insn jump to simplify
+ * @return an instruction representing the simplified jump.
+ */
+ private Insn simplifyJump (Insn insn) {
+ return insn;
+ }
+
+ /**
+ * Simulates math insns, if possible.
+ *
+ * @param insn non-null insn to simulate
+ * @return constant result or null if not simulatable.
+ */
+ private Constant simulateMath(SsaInsn insn) {
+ Insn ropInsn = insn.getOriginalRopInsn();
+ int opcode = insn.getOpcode().getOpcode();
+ RegisterSpecList sources = insn.getSources();
+ int regA = sources.get(0).getReg();
+ Constant cA;
+ Constant cB;
+
+ if (latticeValues[regA] != CONSTANT) {
+ cA = null;
+ } else {
+ cA = latticeConstants[regA];
+ }
+
+ if (sources.size() == 1) {
+ CstInsn cstInsn = (CstInsn) ropInsn;
+ cB = cstInsn.getConstant();
+ } else { /* sources.size() == 2 */
+ int regB = sources.get(1).getReg();
+ if (latticeValues[regB] != CONSTANT) {
+ cB = null;
+ } else {
+ cB = latticeConstants[regB];
+ }
+ }
+
+ if (cA == null || cB == null) {
+ //TODO handle a constant of 0 with MUL or AND
+ return null;
+ }
+
+ switch (insn.getResult().getBasicType()) {
+ case Type.BT_INT:
+ int vR;
+ boolean skip=false;
+
+ int vA = ((CstInteger) cA).getValue();
+ int vB = ((CstInteger) cB).getValue();
+
+ switch (opcode) {
+ case RegOps.ADD:
+ vR = vA + vB;
+ break;
+ case RegOps.SUB:
+ vR = vA - vB;
+ break;
+ case RegOps.MUL:
+ vR = vA * vB;
+ break;
+ case RegOps.DIV:
+ if (vB == 0) {
+ skip = true;
+ vR = 0; // just to hide a warning
+ } else {
+ vR = vA / vB;
+ }
+ break;
+ case RegOps.AND:
+ vR = vA & vB;
+ break;
+ case RegOps.OR:
+ vR = vA | vB;
+ break;
+ case RegOps.XOR:
+ vR = vA ^ vB;
+ break;
+ case RegOps.SHL:
+ vR = vA << vB;
+ break;
+ case RegOps.SHR:
+ vR = vA >> vB;
+ break;
+ case RegOps.USHR:
+ vR = vA >>> vB;
+ break;
+ case RegOps.REM:
+ vR = vA % vB;
+ break;
+ default:
+ throw new RuntimeException("Unexpected op");
+ }
+
+ return skip ? null : CstInteger.make(vR);
+
+ default:
+ // not yet supported
+ return null;
+ }
+ }
+
+ /**
+ * Simulates a statement and set the result lattice value.
+ * @param insn instruction to simulate
+ */
+ private void simulateStmt(SsaInsn insn) {
+ Insn ropInsn = insn.getOriginalRopInsn();
+ if (ropInsn.getOpcode().getBranchingness() != Rop.BRANCH_NONE
+ || ropInsn.getOpcode().isCallLike()) {
+ ropInsn = simplifyJump (ropInsn);
+ /* TODO: If jump becomes constant, only take true edge. */
+ SsaBasicBlock block = insn.getBlock();
+ int successorSize = block.getSuccessorList().size();
+ for (int i = 0; i < successorSize; i++) {
+ int successor = block.getSuccessorList().get(i);
+ addBlockToWorklist(ssaMeth.getBlocks().get(successor));
+ }
+ }
+
+ if (insn.getResult() == null) {
+ return;
+ }
+
+ /* TODO: Simplify statements when possible using the constants. */
+ int resultReg = insn.getResult().getReg();
+ int resultValue = VARYING;
+ Constant resultConstant = null;
+ int opcode = insn.getOpcode().getOpcode();
+ switch (opcode) {
+ case RegOps.CONST: {
+ CstInsn cstInsn = (CstInsn)ropInsn;
+ resultValue = CONSTANT;
+ resultConstant = cstInsn.getConstant();
+ break;
+ }
+ case RegOps.MOVE: {
+ if (insn.getSources().size() == 1) {
+ int sourceReg = insn.getSources().get(0).getReg();
+ resultValue = latticeValues[sourceReg];
+ resultConstant = latticeConstants[sourceReg];
+ }
+ break;
+ }
+
+ case RegOps.ADD:
+ case RegOps.SUB:
+ case RegOps.MUL:
+ case RegOps.DIV:
+ case RegOps.AND:
+ case RegOps.OR:
+ case RegOps.XOR:
+ case RegOps.SHL:
+ case RegOps.SHR:
+ case RegOps.USHR:
+ case RegOps.REM:
+
+ resultConstant = simulateMath(insn);
+
+ if (resultConstant == null) {
+ resultValue = VARYING;
+ } else {
+ resultValue = CONSTANT;
+ }
+ break;
+ /* TODO: Handle non-int arithmetic.
+ TODO: Eliminate check casts that we can prove the type of. */
+ default: {}
+ }
+ if (setLatticeValueTo(resultReg, resultValue, resultConstant)) {
+ addUsersToWorklist(resultReg, resultValue);
+ }
+ }
+
+ private void run() {
+ SsaBasicBlock firstBlock = ssaMeth.getEntryBlock();
+ addBlockToWorklist(firstBlock);
+
+ /* Empty all the worklists by propagating our values */
+ while (!cfgWorklist.isEmpty()
+ || !ssaWorklist.isEmpty()
+ || !varyingWorklist.isEmpty()) {
+ while (!cfgWorklist.isEmpty()) {
+ int listSize = cfgWorklist.size() - 1;
+ SsaBasicBlock block = cfgWorklist.remove(listSize);
+ simulateBlock(block);
+ }
+ while (!varyingWorklist.isEmpty()) {
+ int listSize = varyingWorklist.size() - 1;
+ SsaInsn insn = varyingWorklist.remove(listSize);
+
+ if (!executableBlocks.get(insn.getBlock().getIndex())) {
+ continue;
+ }
+
+ if (insn instanceof PhiInsn) {
+ simulatePhi((PhiInsn)insn);
+ } else {
+ simulateStmt(insn);
+ }
+ }
+ while (!ssaWorklist.isEmpty()) {
+ int listSize = ssaWorklist.size() - 1;
+ SsaInsn insn = ssaWorklist.remove(listSize);
+
+ if (!executableBlocks.get(insn.getBlock().getIndex())) {
+ continue;
+ }
+
+ if (insn instanceof PhiInsn) {
+ simulatePhi((PhiInsn)insn);
+ } else {
+ simulateStmt(insn);
+ }
+ }
+ }
+
+ replaceConstants();
+ }
+
+ /**
+ * Replaces TypeBearers in source register specs with constant type
+ * bearers if possible. These are then referenced in later optimization
+ * steps.
+ */
+ private void replaceConstants() {
+ for (int reg = 0; reg < regCount; reg++) {
+ if (latticeValues[reg] != CONSTANT) {
+ continue;
+ }
+ if (!(latticeConstants[reg] instanceof TypedConstant)) {
+ // We can't do much with these
+ continue;
+ }
+
+ SsaInsn defn = ssaMeth.getDefinitionForRegister(reg);
+ TypeBearer typeBearer = defn.getResult().getTypeBearer();
+
+ if (typeBearer.isConstant()) {
+ /*
+ * The definition was a constant already.
+ * The uses should be as well.
+ */
+ continue;
+ }
+
+ /*
+ * Update the sources RegisterSpec's of all non-move uses.
+ * These will be used in later steps.
+ */
+ for (SsaInsn insn : ssaMeth.getUseListForRegister(reg)) {
+ if (insn.isPhiOrMove()) {
+ continue;
+ }
+
+ NormalSsaInsn nInsn = (NormalSsaInsn) insn;
+ RegisterSpecList sources = insn.getSources();
+
+ int index = sources.indexOfRegister(reg);
+
+ RegisterSpec spec = sources.get(index);
+ RegisterSpec newSpec
+ = spec.withType((TypedConstant)latticeConstants[reg]);
+
+ nInsn.changeOneSource(index, newSpec);
+ }
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/ssa/SetFactory.java b/dx/src/com/android/dx/ssa/SetFactory.java
new file mode 100644
index 0000000..92e965f
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/SetFactory.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.util.BitIntSet;
+import com.android.dx.util.IntSet;
+import com.android.dx.util.ListIntSet;
+
+
+/**
+ * Makes int sets for various parts of the optimizer.
+ */
+public final class SetFactory {
+
+ /**
+ * BitIntSet/ListIntSet threshold for dominance frontier sets. These
+ * sets are kept per basic block until phi placement and tend to be,
+ * like the CFG itself, very sparse at large sizes.
+ *
+ * A value of 3072 here is somewhere around 1.125mb of total bitset size.
+ */
+ private static final int DOMFRONT_SET_THRESHOLD_SIZE = 3072;
+
+ /**
+ * BitIntSet/ListIntSet threshold for interference graph sets. These
+ * sets are kept per register until register allocation is done.
+ *
+ * A value of 3072 here is somewhere around 1.125mb of total bitset size.
+ */
+ private static final int INTERFERENCE_SET_THRESHOLD_SIZE = 3072;
+
+ /**
+ * BitIntSet/ListIntSet threshold for the live in/out sets kept by
+ * {@link SsaBasicBlock}. These are sets of SSA registers kept per basic
+ * block during register allocation.
+ *
+ * The total size of a bitset for this would be the count of blocks
+ * times the size of registers. The threshold value here is merely
+ * the register count, which is typically on the order of the block
+ * count as well.
+ */
+ private static final int LIVENESS_SET_THRESHOLD_SIZE = 3072;
+
+
+ /**
+ * Make IntSet for the dominance-frontier sets.
+ *
+ * @param szBlocks {@code >=0;} count of basic blocks in method
+ * @return {@code non-null;} appropriate set
+ */
+ /*package*/ static IntSet makeDomFrontSet(int szBlocks) {
+ return szBlocks <= DOMFRONT_SET_THRESHOLD_SIZE
+ ? new BitIntSet(szBlocks)
+ : new ListIntSet();
+ }
+
+ /**
+ * Make IntSet for the interference graph sets. Public because
+ * InterferenceGraph is in another package.
+ *
+ * @param countRegs {@code >=0;} count of SSA registers used in method
+ * @return {@code non-null;} appropriate set
+ */
+ public static IntSet makeInterferenceSet(int countRegs) {
+ return countRegs <= INTERFERENCE_SET_THRESHOLD_SIZE
+ ? new BitIntSet(countRegs)
+ : new ListIntSet();
+ }
+
+ /**
+ * Make IntSet for register live in/out sets.
+ *
+ * @param countRegs {@code >=0;} count of SSA registers used in method
+ * @return {@code non-null;} appropriate set
+ */
+ /*package*/ static IntSet makeLivenessSet(int countRegs) {
+ return countRegs <= LIVENESS_SET_THRESHOLD_SIZE
+ ? new BitIntSet(countRegs)
+ : new ListIntSet();
+ }
+}
diff --git a/dx/src/com/android/dx/ssa/SsaBasicBlock.java b/dx/src/com/android/dx/ssa/SsaBasicBlock.java
new file mode 100644
index 0000000..499f59f
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/SsaBasicBlock.java
@@ -0,0 +1,1032 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.BasicBlock;
+import com.android.dx.rop.code.BasicBlockList;
+import com.android.dx.rop.code.Insn;
+import com.android.dx.rop.code.InsnList;
+import com.android.dx.rop.code.PlainInsn;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.Rop;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.code.Rops;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.util.Hex;
+import com.android.dx.util.IntList;
+import com.android.dx.util.IntSet;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * An SSA representation of a basic block.
+ */
+public final class SsaBasicBlock {
+ /**
+ * {@code non-null;} comparator for instances of this class that
+ * just compares block labels
+ */
+ public static final Comparator<SsaBasicBlock> LABEL_COMPARATOR =
+ new LabelComparator();
+
+ /** {@code non-null;} insn list associated with this instance */
+ private ArrayList<SsaInsn> insns;
+
+ /** {@code non-null;} predecessor set (by block list index) */
+ private BitSet predecessors;
+
+ /** {@code non-null;} successor set (by block list index) */
+ private BitSet successors;
+
+ /**
+ * {@code non-null;} ordered successor list
+ * (same block may be listed more than once)
+ */
+ private IntList successorList;
+
+ /**
+ * block list index of primary successor, or {@code -1} for no primary
+ * successor
+ */
+ private int primarySuccessor = -1;
+
+ /** label of block in rop form */
+ private int ropLabel;
+
+ /** {@code non-null;} method we belong to */
+ private SsaMethod parent;
+
+ /** our index into parent.getBlock() */
+ private int index;
+
+ /** list of dom children */
+ private final ArrayList<SsaBasicBlock> domChildren;
+
+ /**
+ * the number of moves added to the end of the block during the
+ * phi-removal process. Retained for subsequent move scheduling.
+ */
+ private int movesFromPhisAtEnd = 0;
+
+ /**
+ * the number of moves added to the beginning of the block during the
+ * phi-removal process. Retained for subsequent move scheduling.
+ */
+ private int movesFromPhisAtBeginning = 0;
+
+ /**
+ * contains last computed value of reachability of this block, or -1
+ * if reachability hasn't been calculated yet
+ */
+ private int reachable = -1;
+
+ /**
+ * {@code null-ok;} indexed by reg: the regs that are live-in at
+ * this block
+ */
+ private IntSet liveIn;
+
+ /**
+ * {@code null-ok;} indexed by reg: the regs that are live-out at
+ * this block
+ */
+ private IntSet liveOut;
+
+ /**
+ * Creates a new empty basic block.
+ *
+ * @param basicBlockIndex index this block will have
+ * @param ropLabel original rop-form label
+ * @param parent method of this block
+ */
+ public SsaBasicBlock(final int basicBlockIndex, final int ropLabel,
+ final SsaMethod parent) {
+ this.parent = parent;
+ this.index = basicBlockIndex;
+ this.insns = new ArrayList<SsaInsn>();
+ this.ropLabel = ropLabel;
+
+ this.predecessors = new BitSet(parent.getBlocks().size());
+ this.successors = new BitSet(parent.getBlocks().size());
+ this.successorList = new IntList();
+
+ domChildren = new ArrayList<SsaBasicBlock>();
+ }
+
+ /**
+ * Creates a new SSA basic block from a ROP form basic block.
+ *
+ * @param rmeth original method
+ * @param basicBlockIndex index this block will have
+ * @param parent method of this block predecessor set will be
+ * updated
+ * @return new instance
+ */
+ public static SsaBasicBlock newFromRop(RopMethod rmeth,
+ int basicBlockIndex, final SsaMethod parent) {
+ BasicBlockList ropBlocks = rmeth.getBlocks();
+ BasicBlock bb = ropBlocks.get(basicBlockIndex);
+ SsaBasicBlock result =
+ new SsaBasicBlock(basicBlockIndex, bb.getLabel(), parent);
+ InsnList ropInsns = bb.getInsns();
+
+ result.insns.ensureCapacity(ropInsns.size());
+
+ for (int i = 0, sz = ropInsns.size() ; i < sz ; i++) {
+ result.insns.add(new NormalSsaInsn (ropInsns.get(i), result));
+ }
+
+ result.predecessors = SsaMethod.bitSetFromLabelList(
+ ropBlocks,
+ rmeth.labelToPredecessors(bb.getLabel()));
+
+ result.successors
+ = SsaMethod.bitSetFromLabelList(ropBlocks, bb.getSuccessors());
+
+ result.successorList
+ = SsaMethod.indexListFromLabelList(ropBlocks,
+ bb.getSuccessors());
+
+ if (result.successorList.size() != 0) {
+ int primarySuccessor = bb.getPrimarySuccessor();
+
+ result.primarySuccessor = (primarySuccessor < 0)
+ ? -1 : ropBlocks.indexOfLabel(primarySuccessor);
+ }
+
+ return result;
+ }
+
+ /**
+ * Adds a basic block as a dom child for this block. Used when constructing
+ * the dom tree.
+ *
+ * @param child {@code non-null;} new dom child
+ */
+ public void addDomChild(SsaBasicBlock child) {
+ domChildren.add(child);
+ }
+
+ /**
+ * Gets the dom children for this node. Don't modify this list.
+ *
+ * @return {@code non-null;} list of dom children
+ */
+ public ArrayList<SsaBasicBlock> getDomChildren() {
+ return domChildren;
+ }
+
+ /**
+ * Adds a phi insn to the beginning of this block. The result type of
+ * the phi will be set to void, to indicate that it's currently unknown.
+ *
+ * @param reg {@code >=0;} result reg
+ */
+ public void addPhiInsnForReg(int reg) {
+ insns.add(0, new PhiInsn(reg, this));
+ }
+
+ /**
+ * Adds a phi insn to the beginning of this block. This is to be used
+ * when the result type or local-association can be determined at phi
+ * insert time.
+ *
+ * @param resultSpec {@code non-null;} reg
+ */
+ public void addPhiInsnForReg(RegisterSpec resultSpec) {
+ insns.add(0, new PhiInsn(resultSpec, this));
+ }
+
+ /**
+ * Adds an insn to the head of this basic block, just after any phi
+ * insns.
+ *
+ * @param insn {@code non-null;} rop-form insn to add
+ */
+ public void addInsnToHead(Insn insn) {
+ SsaInsn newInsn = SsaInsn.makeFromRop(insn, this);
+ insns.add(getCountPhiInsns(), newInsn);
+ parent.onInsnAdded(newInsn);
+ }
+
+ /**
+ * Replaces the last insn in this block. The provided insn must have
+ * some branchingness.
+ *
+ * @param insn {@code non-null;} rop-form insn to add, which must branch.
+ */
+ public void replaceLastInsn(Insn insn) {
+ if (insn.getOpcode().getBranchingness() == Rop.BRANCH_NONE) {
+ throw new IllegalArgumentException("last insn must branch");
+ }
+
+ SsaInsn oldInsn = insns.get(insns.size() - 1);
+ SsaInsn newInsn = SsaInsn.makeFromRop(insn, this);
+
+ insns.set(insns.size() - 1, newInsn);
+
+ parent.onInsnRemoved(oldInsn);
+ parent.onInsnAdded(newInsn);
+ }
+
+ /**
+ * Visits each phi insn.
+ *
+ * @param v {@code non-null;} the callback
+ */
+ public void forEachPhiInsn(PhiInsn.Visitor v) {
+ int sz = insns.size();
+
+ for (int i = 0; i < sz; i++) {
+ SsaInsn insn = insns.get(i);
+ if (insn instanceof PhiInsn) {
+ v.visitPhiInsn((PhiInsn) insn);
+ } else {
+ /*
+ * Presently we assume PhiInsn's are in a continuous
+ * block at the top of the list
+ */
+ break;
+ }
+ }
+ }
+
+ /**
+ * Deletes all phi insns. Do this after adding appropriate move insns.
+ */
+ public void removeAllPhiInsns() {
+ /*
+ * Presently we assume PhiInsn's are in a continuous
+ * block at the top of the list.
+ */
+
+ insns.subList(0, getCountPhiInsns()).clear();
+ }
+
+ /**
+ * Gets the number of phi insns at the top of this basic block.
+ *
+ * @return count of phi insns
+ */
+ private int getCountPhiInsns() {
+ int countPhiInsns;
+
+ int sz = insns.size();
+ for (countPhiInsns = 0; countPhiInsns < sz; countPhiInsns++) {
+ SsaInsn insn = insns.get(countPhiInsns);
+ if (!(insn instanceof PhiInsn)) {
+ break;
+ }
+ }
+
+ return countPhiInsns;
+ }
+
+ /**
+ * @return {@code non-null;} the (mutable) instruction list for this block,
+ * with phi insns at the beginning
+ */
+ public ArrayList<SsaInsn> getInsns() {
+ return insns;
+ }
+
+ /**
+ * @return {@code non-null;} the (mutable) list of phi insns for this block
+ */
+ public List<SsaInsn> getPhiInsns() {
+ return insns.subList(0, getCountPhiInsns());
+ }
+
+ /**
+ * @return the block index of this block
+ */
+ public int getIndex() {
+ return index;
+ }
+
+ /**
+ * @return the label of this block in rop form
+ */
+ public int getRopLabel() {
+ return ropLabel;
+ }
+
+ /**
+ * @return the label of this block in rop form as a hex string
+ */
+ public String getRopLabelString() {
+ return Hex.u2(ropLabel);
+ }
+
+ /**
+ * @return {@code non-null;} predecessors set, indexed by block index
+ */
+ public BitSet getPredecessors() {
+ return predecessors;
+ }
+
+ /**
+ * @return {@code non-null;} successors set, indexed by block index
+ */
+ public BitSet getSuccessors() {
+ return successors;
+ }
+
+ /**
+ * @return {@code non-null;} ordered successor list, containing block
+ * indicies
+ */
+ public IntList getSuccessorList() {
+ return successorList;
+ }
+
+ /**
+ * @return {@code >= -1;} block index of primary successor or
+ * {@code -1} if no primary successor
+ */
+ public int getPrimarySuccessorIndex() {
+ return primarySuccessor;
+ }
+
+ /**
+ * @return rop label of primary successor
+ */
+ public int getPrimarySuccessorRopLabel() {
+ return parent.blockIndexToRopLabel(primarySuccessor);
+ }
+
+ /**
+ * @return {@code null-ok;} the primary successor block or {@code null}
+ * if there is none
+ */
+ public SsaBasicBlock getPrimarySuccessor() {
+ if (primarySuccessor < 0) {
+ return null;
+ } else {
+ return parent.getBlocks().get(primarySuccessor);
+ }
+ }
+
+ /**
+ * @return successor list of rop labels
+ */
+ public IntList getRopLabelSuccessorList() {
+ IntList result = new IntList(successorList.size());
+
+ int sz = successorList.size();
+
+ for (int i = 0; i < sz; i++) {
+ result.add(parent.blockIndexToRopLabel(successorList.get(i)));
+ }
+ return result;
+ }
+
+ /**
+ * @return {@code non-null;} method that contains this block
+ */
+ public SsaMethod getParent() {
+ return parent;
+ }
+
+ /**
+ * Inserts a new empty GOTO block as a predecessor to this block.
+ * All previous predecessors will be predecessors to the new block.
+ *
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public SsaBasicBlock insertNewPredecessor() {
+ SsaBasicBlock newPred = parent.makeNewGotoBlock();
+
+ // Update the new block.
+ newPred.predecessors = predecessors;
+ newPred.successors.set(index) ;
+ newPred.successorList.add(index);
+ newPred.primarySuccessor = index;
+
+
+ // Update us.
+ predecessors = new BitSet(parent.getBlocks().size());
+ predecessors.set(newPred.index);
+
+ // Update our (soon-to-be) old predecessors.
+ for (int i = newPred.predecessors.nextSetBit(0); i >= 0;
+ i = newPred.predecessors.nextSetBit(i + 1)) {
+
+ SsaBasicBlock predBlock = parent.getBlocks().get(i);
+
+ predBlock.replaceSuccessor(index, newPred.index);
+ }
+
+ return newPred;
+ }
+
+ /**
+ * Constructs and inserts a new empty GOTO block {@code Z} between
+ * this block ({@code A}) and a current successor block
+ * ({@code B}). The new block will replace B as A's successor and
+ * A as B's predecessor. A and B will no longer be directly connected.
+ * If B is listed as a successor multiple times, all references
+ * are replaced.
+ *
+ * @param other current successor (B)
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public SsaBasicBlock insertNewSuccessor(SsaBasicBlock other) {
+ SsaBasicBlock newSucc = parent.makeNewGotoBlock();
+
+ if (!successors.get(other.index)) {
+ throw new RuntimeException("Block " + other.getRopLabelString()
+ + " not successor of " + getRopLabelString());
+ }
+
+ // Update the new block.
+ newSucc.predecessors.set(this.index);
+ newSucc.successors.set(other.index) ;
+ newSucc.successorList.add(other.index);
+ newSucc.primarySuccessor = other.index;
+
+ // Update us.
+ for (int i = successorList.size() - 1 ; i >= 0; i--) {
+ if (successorList.get(i) == other.index) {
+ successorList.set(i, newSucc.index);
+ }
+ }
+
+ if (primarySuccessor == other.index) {
+ primarySuccessor = newSucc.index;
+ }
+ successors.clear(other.index);
+ successors.set(newSucc.index);
+
+ // Update "other".
+ other.predecessors.set(newSucc.index);
+ other.predecessors.set(index, successors.get(other.index));
+
+ return newSucc;
+ }
+
+ /**
+ * Replaces an old successor with a new successor. This will throw
+ * RuntimeException if {@code oldIndex} was not a successor.
+ *
+ * @param oldIndex index of old successor block
+ * @param newIndex index of new successor block
+ */
+ public void replaceSuccessor(int oldIndex, int newIndex) {
+ if (oldIndex == newIndex) {
+ return;
+ }
+
+ // Update us.
+ successors.set(newIndex);
+
+ if (primarySuccessor == oldIndex) {
+ primarySuccessor = newIndex;
+ }
+
+ for (int i = successorList.size() - 1 ; i >= 0; i--) {
+ if (successorList.get(i) == oldIndex) {
+ successorList.set(i, newIndex);
+ }
+ }
+
+ successors.clear(oldIndex);
+
+ // Update new successor.
+ parent.getBlocks().get(newIndex).predecessors.set(index);
+
+ // Update old successor.
+ parent.getBlocks().get(oldIndex).predecessors.clear(index);
+ }
+
+ /**
+ * Removes a successor from this block's successor list.
+ *
+ * @param oldIndex index of successor block to remove
+ */
+ public void removeSuccessor(int oldIndex) {
+ int removeIndex = 0;
+
+ for (int i = successorList.size() - 1; i >= 0; i--) {
+ if (successorList.get(i) == oldIndex) {
+ removeIndex = i;
+ } else {
+ primarySuccessor = successorList.get(i);
+ }
+ }
+
+ successorList.removeIndex(removeIndex);
+ successors.clear(oldIndex);
+ parent.getBlocks().get(oldIndex).predecessors.clear(index);
+ }
+
+ /**
+ * Attaches block to an exit block if necessary. If this block
+ * is not an exit predecessor or is the exit block, this block does
+ * nothing. For use by {@link com.android.dx.ssa.SsaMethod#makeExitBlock}
+ *
+ * @param exitBlock {@code non-null;} exit block
+ */
+ public void exitBlockFixup(SsaBasicBlock exitBlock) {
+ if (this == exitBlock) {
+ return;
+ }
+
+ if (successorList.size() == 0) {
+ /*
+ * This is an exit predecessor.
+ * Set the successor to the exit block
+ */
+ successors.set(exitBlock.index);
+ successorList.add(exitBlock.index);
+ primarySuccessor = exitBlock.index;
+ exitBlock.predecessors.set(this.index);
+ }
+ }
+
+ /**
+ * Adds a move instruction to the end of this basic block, just
+ * before the last instruction. If the result of the final instruction
+ * is the source in question, then the move is placed at the beginning of
+ * the primary successor block. This is for unversioned registers.
+ *
+ * @param result move destination
+ * @param source move source
+ */
+ public void addMoveToEnd(RegisterSpec result, RegisterSpec source) {
+
+ if (result.getReg() == source.getReg()) {
+ // Sometimes we end up with no-op moves. Ignore them here.
+ return;
+ }
+
+ /*
+ * The last Insn has to be a normal SSA insn: a phi can't branch
+ * or return or cause an exception, etc.
+ */
+ NormalSsaInsn lastInsn;
+ lastInsn = (NormalSsaInsn)insns.get(insns.size()-1);
+
+ if (lastInsn.getResult() != null || lastInsn.getSources().size() > 0) {
+ /*
+ * The final insn in this block has a source or result
+ * register, and the moves we may need to place and
+ * schedule may interfere. We need to insert this
+ * instruction at the beginning of the primary successor
+ * block instead. We know this is safe, because when we
+ * edge-split earlier, we ensured that each successor has
+ * only us as a predecessor.
+ */
+
+ for (int i = successors.nextSetBit(0)
+ ; i >= 0
+ ; i = successors.nextSetBit(i + 1)) {
+
+ SsaBasicBlock succ;
+
+ succ = parent.getBlocks().get(i);
+ succ.addMoveToBeginning(result, source);
+ }
+ } else {
+ /*
+ * We can safely add a move to the end of the block just
+ * before the last instruction, because the final insn does
+ * not assign to anything.
+ */
+ RegisterSpecList sources = RegisterSpecList.make(source);
+ NormalSsaInsn toAdd = new NormalSsaInsn(
+ new PlainInsn(Rops.opMove(result.getType()),
+ SourcePosition.NO_INFO, result, sources), this);
+
+ insns.add(insns.size() - 1, toAdd);
+
+ movesFromPhisAtEnd++;
+ }
+ }
+
+ /**
+ * Adds a move instruction after the phi insn block.
+ *
+ * @param result move destination
+ * @param source move source
+ */
+ public void addMoveToBeginning (RegisterSpec result, RegisterSpec source) {
+ if (result.getReg() == source.getReg()) {
+ // Sometimes we end up with no-op moves. Ignore them here.
+ return;
+ }
+
+ RegisterSpecList sources = RegisterSpecList.make(source);
+ NormalSsaInsn toAdd = new NormalSsaInsn(
+ new PlainInsn(Rops.opMove(result.getType()),
+ SourcePosition.NO_INFO, result, sources), this);
+
+ insns.add(getCountPhiInsns(), toAdd);
+ movesFromPhisAtBeginning++;
+ }
+
+ /**
+ * Sets the register as used in a bitset, taking into account its
+ * category/width.
+ *
+ * @param regsUsed set, indexed by register number
+ * @param rs register to mark as used
+ */
+ private static void setRegsUsed (BitSet regsUsed, RegisterSpec rs) {
+ regsUsed.set(rs.getReg());
+ if (rs.getCategory() > 1) {
+ regsUsed.set(rs.getReg() + 1);
+ }
+ }
+
+ /**
+ * Checks to see if the register is used in a bitset, taking
+ * into account its category/width.
+ *
+ * @param regsUsed set, indexed by register number
+ * @param rs register to mark as used
+ * @return true if register is fully or partially (for the case of wide
+ * registers) used.
+ */
+ private static boolean checkRegUsed (BitSet regsUsed, RegisterSpec rs) {
+ int reg = rs.getReg();
+ int category = rs.getCategory();
+
+ return regsUsed.get(reg)
+ || (category == 2 ? regsUsed.get(reg + 1) : false);
+ }
+
+ /**
+ * Ensures that all move operations in this block occur such that
+ * reads of any register happen before writes to that register.
+ * NOTE: caller is expected to returnSpareRegisters()!
+ *
+ * TODO: See Briggs, et al "Practical Improvements to the Construction and
+ * Destruction of Static Single Assignment Form" section 5. a) This can
+ * be done in three passes.
+ *
+ * @param toSchedule List of instructions. Must consist only of moves.
+ */
+ private void scheduleUseBeforeAssigned(List<SsaInsn> toSchedule) {
+ BitSet regsUsedAsSources = new BitSet(parent.getRegCount());
+
+ // TODO: Get rid of this.
+ BitSet regsUsedAsResults = new BitSet(parent.getRegCount());
+
+ int sz = toSchedule.size();
+
+ int insertPlace = 0;
+
+ while (insertPlace < sz) {
+ int oldInsertPlace = insertPlace;
+
+ // Record all registers used as sources in this block.
+ for (int i = insertPlace; i < sz; i++) {
+ setRegsUsed(regsUsedAsSources,
+ toSchedule.get(i).getSources().get(0));
+
+ setRegsUsed(regsUsedAsResults,
+ toSchedule.get(i).getResult());
+ }
+
+ /*
+ * If there are no circular dependencies, then there exists
+ * n instructions where n > 1 whose result is not used as a source.
+ */
+ for (int i = insertPlace; i <sz; i++) {
+ SsaInsn insn = toSchedule.get(i);
+
+ /*
+ * Move these n registers to the front, since they overwrite
+ * nothing.
+ */
+ if (!checkRegUsed(regsUsedAsSources, insn.getResult())) {
+ Collections.swap(toSchedule, i, insertPlace++);
+ }
+ }
+
+ /*
+ * If we've made no progress in this iteration, there's a
+ * circular dependency. Split it using the temp reg.
+ */
+ if (oldInsertPlace == insertPlace) {
+
+ SsaInsn insnToSplit = null;
+
+ // Find an insn whose result is used as a source.
+ for (int i = insertPlace; i < sz; i++) {
+ SsaInsn insn = toSchedule.get(i);
+ if (checkRegUsed(regsUsedAsSources, insn.getResult())
+ && checkRegUsed(regsUsedAsResults,
+ insn.getSources().get(0))) {
+
+ insnToSplit = insn;
+ /*
+ * We're going to split this insn; move it to the
+ * front.
+ */
+ Collections.swap(toSchedule, insertPlace, i);
+ break;
+ }
+ }
+
+ // At least one insn will be set above.
+
+ RegisterSpec result = insnToSplit.getResult();
+ RegisterSpec tempSpec = result.withReg(
+ parent.borrowSpareRegister(result.getCategory()));
+
+ NormalSsaInsn toAdd = new NormalSsaInsn(
+ new PlainInsn(Rops.opMove(result.getType()),
+ SourcePosition.NO_INFO,
+ tempSpec,
+ insnToSplit.getSources()), this);
+
+ toSchedule.add(insertPlace++, toAdd);
+
+ RegisterSpecList newSources = RegisterSpecList.make(tempSpec);
+
+ NormalSsaInsn toReplace = new NormalSsaInsn(
+ new PlainInsn(Rops.opMove(result.getType()),
+ SourcePosition.NO_INFO,
+ result,
+ newSources), this);
+
+ toSchedule.set(insertPlace, toReplace);
+
+ // The size changed.
+ sz = toSchedule.size();
+ }
+
+ regsUsedAsSources.clear();
+ regsUsedAsResults.clear();
+ }
+ }
+
+ /**
+ * Adds {@code regV} to the live-out list for this block. This is called
+ * by the liveness analyzer.
+ *
+ * @param regV register that is live-out for this block.
+ */
+ public void addLiveOut (int regV) {
+ if (liveOut == null) {
+ liveOut = SetFactory.makeLivenessSet(parent.getRegCount());
+ }
+
+ liveOut.add(regV);
+ }
+
+ /**
+ * Adds {@code regV} to the live-in list for this block. This is
+ * called by the liveness analyzer.
+ *
+ * @param regV register that is live-in for this block.
+ */
+ public void addLiveIn (int regV) {
+ if (liveIn == null) {
+ liveIn = SetFactory.makeLivenessSet(parent.getRegCount());
+ }
+
+ liveIn.add(regV);
+ }
+
+ /**
+ * Returns the set of live-in registers. Valid after register
+ * interference graph has been generated, otherwise empty.
+ *
+ * @return {@code non-null;} live-in register set.
+ */
+ public IntSet getLiveInRegs() {
+ if (liveIn == null) {
+ liveIn = SetFactory.makeLivenessSet(parent.getRegCount());
+ }
+ return liveIn;
+ }
+
+ /**
+ * Returns the set of live-out registers. Valid after register
+ * interference graph has been generated, otherwise empty.
+ *
+ * @return {@code non-null;} live-out register set
+ */
+ public IntSet getLiveOutRegs() {
+ if (liveOut == null) {
+ liveOut = SetFactory.makeLivenessSet(parent.getRegCount());
+ }
+ return liveOut;
+ }
+
+ /**
+ * @return true if this is the one-and-only exit block for this method
+ */
+ public boolean isExitBlock() {
+ return index == parent.getExitBlockIndex();
+ }
+
+ /**
+ * Returns true if this block was last calculated to be reachable.
+ * Recalculates reachability if value has never been computed.
+ *
+ * @return {@code true} if reachable
+ */
+ public boolean isReachable() {
+ if (reachable == -1) {
+ parent.computeReachability();
+ }
+ return (reachable == 1);
+ }
+
+ /**
+ * Sets reachability of block to specified value
+ *
+ * @param reach new value of reachability for block
+ */
+ public void setReachable(int reach) {
+ reachable = reach;
+ }
+
+ /**
+ * Sorts move instructions added via {@code addMoveToEnd} during
+ * phi removal so that results don't overwrite sources that are used.
+ * For use after all phis have been removed and all calls to
+ * addMoveToEnd() have been made.<p>
+ *
+ * This is necessary because copy-propogation may have left us in a state
+ * where the same basic block has the same register as a phi operand
+ * and a result. In this case, the register in the phi operand always
+ * refers value before any other phis have executed.
+ */
+ public void scheduleMovesFromPhis() {
+ if (movesFromPhisAtBeginning > 1) {
+ List<SsaInsn> toSchedule;
+
+ toSchedule = insns.subList(0, movesFromPhisAtBeginning);
+
+ scheduleUseBeforeAssigned(toSchedule);
+
+ SsaInsn firstNonPhiMoveInsn = insns.get(movesFromPhisAtBeginning);
+
+ /*
+ * TODO: It's actually possible that this case never happens,
+ * because a move-exception block, having only one predecessor
+ * in SSA form, perhaps is never on a dominance frontier.
+ */
+ if (firstNonPhiMoveInsn.isMoveException()) {
+ if (true) {
+ /*
+ * We've yet to observe this case, and if it can
+ * occur the code written to handle it probably
+ * does not work.
+ */
+ throw new RuntimeException(
+ "Unexpected: moves from "
+ +"phis before move-exception");
+ } else {
+ /*
+ * A move-exception insn must be placed first in this block
+ * We need to move it there, and deal with possible
+ * interference.
+ */
+ boolean moveExceptionInterferes = false;
+
+ int moveExceptionResult
+ = firstNonPhiMoveInsn.getResult().getReg();
+
+ /*
+ * Does the move-exception result reg interfere with the
+ * phi moves?
+ */
+ for (SsaInsn insn : toSchedule) {
+ if (insn.isResultReg(moveExceptionResult)
+ || insn.isRegASource(moveExceptionResult)) {
+ moveExceptionInterferes = true;
+ break;
+ }
+ }
+
+ if (!moveExceptionInterferes) {
+ // This is the easy case.
+ insns.remove(movesFromPhisAtBeginning);
+ insns.add(0, firstNonPhiMoveInsn);
+ } else {
+ /*
+ * We need to move the result to a spare reg
+ * and move it back.
+ */
+ RegisterSpec originalResultSpec
+ = firstNonPhiMoveInsn.getResult();
+ int spareRegister = parent.borrowSpareRegister(
+ originalResultSpec.getCategory());
+
+ // We now move it to a spare register.
+ firstNonPhiMoveInsn.changeResultReg(spareRegister);
+ RegisterSpec tempSpec =
+ firstNonPhiMoveInsn.getResult();
+
+ insns.add(0, firstNonPhiMoveInsn);
+
+ // And here we move it back.
+
+ NormalSsaInsn toAdd = new NormalSsaInsn(
+ new PlainInsn(
+ Rops.opMove(tempSpec.getType()),
+ SourcePosition.NO_INFO,
+ originalResultSpec,
+ RegisterSpecList.make(tempSpec)),
+ this);
+
+
+ /*
+ * Place it immediately after the phi-moves,
+ * overwriting the move-exception that was there.
+ */
+ insns.set(movesFromPhisAtBeginning + 1, toAdd);
+ }
+ }
+ }
+ }
+
+ if (movesFromPhisAtEnd > 1) {
+ scheduleUseBeforeAssigned(
+ insns.subList(insns.size() - movesFromPhisAtEnd - 1,
+ insns.size() - 1));
+ }
+
+ // Return registers borrowed here and in scheduleUseBeforeAssigned().
+ parent.returnSpareRegisters();
+
+ }
+
+ /**
+ * Visits all insns in this block.
+ *
+ * @param visitor {@code non-null;} callback interface
+ */
+ public void forEachInsn(SsaInsn.Visitor visitor) {
+ // This gets called a LOT, and not using an iterator
+ // saves a lot of allocations and reduces memory usage
+ int len = insns.size();
+ for (int i = 0; i < len; i++) {
+ insns.get(i).accept(visitor);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return "{" + index + ":" + Hex.u2(ropLabel) + '}';
+ }
+
+ /**
+ * Visitor interface for basic blocks.
+ */
+ public interface Visitor {
+ /**
+ * Indicates a block has been visited by an iterator method.
+ *
+ * @param v {@code non-null;} block visited
+ * @param parent {@code null-ok;} parent node if applicable
+ */
+ void visitBlock (SsaBasicBlock v, SsaBasicBlock parent);
+ }
+
+ /**
+ * Label comparator.
+ */
+ public static final class LabelComparator
+ implements Comparator<SsaBasicBlock> {
+ /** {@inheritDoc} */
+ public int compare(SsaBasicBlock b1, SsaBasicBlock b2) {
+ int label1 = b1.ropLabel;
+ int label2 = b2.ropLabel;
+
+ if (label1 < label2) {
+ return -1;
+ } else if (label1 > label2) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/ssa/SsaConverter.java b/dx/src/com/android/dx/ssa/SsaConverter.java
new file mode 100644
index 0000000..5cd8b6f
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/SsaConverter.java
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.util.IntIterator;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+
+/**
+ * Converts ROP methods to SSA Methods
+ */
+public class SsaConverter {
+ public static final boolean DEBUG = false;
+
+ /**
+ * Returns an SSA representation, edge-split and with phi
+ * functions placed.
+ *
+ * @param rmeth input
+ * @param paramWidth the total width, in register-units, of the method's
+ * parameters
+ * @param isStatic {@code true} if this method has no {@code this}
+ * pointer argument
+ * @return output in SSA form
+ */
+ public static SsaMethod convertToSsaMethod(RopMethod rmeth,
+ int paramWidth, boolean isStatic) {
+ SsaMethod result
+ = SsaMethod.newFromRopMethod(rmeth, paramWidth, isStatic);
+
+ edgeSplit(result);
+
+ LocalVariableInfo localInfo = LocalVariableExtractor.extract(result);
+
+ placePhiFunctions(result, localInfo, 0);
+ new SsaRenamer(result).run();
+
+ /*
+ * The exit block, added here, is not considered for edge splitting
+ * or phi placement since no actual control flows to it.
+ */
+ result.makeExitBlock();
+
+ return result;
+ }
+
+ /**
+ * Updates an SSA representation, placing phi functions and renaming all
+ * registers above a certain threshold number.
+ *
+ * @param ssaMeth input
+ * @param threshold registers below this number are unchanged
+ */
+ public static void updateSsaMethod(SsaMethod ssaMeth, int threshold) {
+ LocalVariableInfo localInfo = LocalVariableExtractor.extract(ssaMeth);
+ placePhiFunctions(ssaMeth, localInfo, threshold);
+ new SsaRenamer(ssaMeth, threshold).run();
+ }
+
+ /**
+ * Returns an SSA represention with only the edge-splitter run.
+ *
+ * @param rmeth method to process
+ * @param paramWidth width of all arguments in the method
+ * @param isStatic {@code true} if this method has no {@code this}
+ * pointer argument
+ * @return an SSA represention with only the edge-splitter run
+ */
+ public static SsaMethod testEdgeSplit (RopMethod rmeth, int paramWidth,
+ boolean isStatic) {
+ SsaMethod result;
+
+ result = SsaMethod.newFromRopMethod(rmeth, paramWidth, isStatic);
+
+ edgeSplit(result);
+ return result;
+ }
+
+ /**
+ * Returns an SSA represention with only the steps through the
+ * phi placement run.
+ *
+ * @param rmeth method to process
+ * @param paramWidth width of all arguments in the method
+ * @param isStatic {@code true} if this method has no {@code this}
+ * pointer argument
+ * @return an SSA represention with only the edge-splitter run
+ */
+ public static SsaMethod testPhiPlacement (RopMethod rmeth, int paramWidth,
+ boolean isStatic) {
+ SsaMethod result;
+
+ result = SsaMethod.newFromRopMethod(rmeth, paramWidth, isStatic);
+
+ edgeSplit(result);
+
+ LocalVariableInfo localInfo = LocalVariableExtractor.extract(result);
+
+ placePhiFunctions(result, localInfo, 0);
+ return result;
+ }
+
+ /**
+ * See Appel section 19.1:
+ *
+ * Converts CFG into "edge-split" form, such that each node either a
+ * unique successor or unique predecessor.<p>
+ *
+ * In addition, the SSA form we use enforces a further constraint,
+ * requiring each block with a final instruction that returns a
+ * value to have a primary successor that has no other
+ * predecessor. This ensures move statements can always be
+ * inserted correctly when phi statements are removed.
+ *
+ * @param result method to process
+ */
+ private static void edgeSplit(SsaMethod result) {
+ edgeSplitPredecessors(result);
+ edgeSplitMoveExceptionsAndResults(result);
+ edgeSplitSuccessors(result);
+ }
+
+ /**
+ * Inserts Z nodes as new predecessors for every node that has multiple
+ * successors and multiple predecessors.
+ *
+ * @param result {@code non-null;} method to process
+ */
+ private static void edgeSplitPredecessors(SsaMethod result) {
+ ArrayList<SsaBasicBlock> blocks = result.getBlocks();
+
+ /*
+ * New blocks are added to the end of the block list during
+ * this iteration.
+ */
+ for (int i = blocks.size() - 1; i >= 0; i-- ) {
+ SsaBasicBlock block = blocks.get(i);
+ if (nodeNeedsUniquePredecessor(block)) {
+ block.insertNewPredecessor();
+ }
+ }
+ }
+
+ /**
+ * @param block {@code non-null;} block in question
+ * @return {@code true} if this node needs to have a unique
+ * predecessor created for it
+ */
+ private static boolean nodeNeedsUniquePredecessor(SsaBasicBlock block) {
+ /*
+ * Any block with that has both multiple successors and multiple
+ * predecessors needs a new predecessor node.
+ */
+
+ int countPredecessors = block.getPredecessors().cardinality();
+ int countSuccessors = block.getSuccessors().cardinality();
+
+ return (countPredecessors > 1 && countSuccessors > 1);
+ }
+
+ /**
+ * In ROP form, move-exception must occur as the first insn in a block
+ * immediately succeeding the insn that could thrown an exception.
+ * We may need room to insert move insns later, so make sure to split
+ * any block that starts with a move-exception such that there is a
+ * unique move-exception block for each predecessor.
+ *
+ * @param ssaMeth method to process
+ */
+ private static void edgeSplitMoveExceptionsAndResults(SsaMethod ssaMeth) {
+ ArrayList<SsaBasicBlock> blocks = ssaMeth.getBlocks();
+
+ /*
+ * New blocks are added to the end of the block list during
+ * this iteration.
+ */
+ for (int i = blocks.size() - 1; i >= 0; i-- ) {
+ SsaBasicBlock block = blocks.get(i);
+
+ /*
+ * Any block that starts with a move-exception and has more than
+ * one predecessor...
+ */
+ if (!block.isExitBlock()
+ && block.getPredecessors().cardinality() > 1
+ && block.getInsns().get(0).isMoveException()) {
+
+ // block.getPredecessors() is changed in the loop below.
+ BitSet preds = (BitSet)block.getPredecessors().clone();
+ for (int j = preds.nextSetBit(0); j >= 0;
+ j = preds.nextSetBit(j + 1)) {
+ SsaBasicBlock predecessor = blocks.get(j);
+ SsaBasicBlock zNode
+ = predecessor.insertNewSuccessor(block);
+
+ /*
+ * Make sure to place the move-exception as the
+ * first insn.
+ */
+ zNode.getInsns().add(0, block.getInsns().get(0).clone());
+ }
+
+ // Remove the move-exception from the original block.
+ block.getInsns().remove(0);
+ }
+ }
+ }
+
+ /**
+ * Inserts Z nodes for every node that needs a new
+ * successor.
+ *
+ * @param result {@code non-null;} method to process
+ */
+ private static void edgeSplitSuccessors(SsaMethod result) {
+ ArrayList<SsaBasicBlock> blocks = result.getBlocks();
+
+ /*
+ * New blocks are added to the end of the block list during
+ * this iteration.
+ */
+ for (int i = blocks.size() - 1; i >= 0; i-- ) {
+ SsaBasicBlock block = blocks.get(i);
+
+ // Successors list is modified in loop below.
+ BitSet successors = (BitSet)block.getSuccessors().clone();
+ for (int j = successors.nextSetBit(0);
+ j >= 0; j = successors.nextSetBit(j+1)) {
+
+ SsaBasicBlock succ = blocks.get(j);
+
+ if (needsNewSuccessor(block, succ)) {
+ block.insertNewSuccessor(succ);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns {@code true} if block and successor need a Z-node
+ * between them. Presently, this is {@code true} if the final
+ * instruction has any sources or results and the current
+ * successor block has more than one predecessor.
+ *
+ * @param block predecessor node
+ * @param succ successor node
+ * @return {@code true} if a Z node is needed
+ */
+ private static boolean needsNewSuccessor(SsaBasicBlock block,
+ SsaBasicBlock succ) {
+ ArrayList<SsaInsn> insns = block.getInsns();
+ SsaInsn lastInsn = insns.get(insns.size() - 1);
+
+ return ((lastInsn.getResult() != null)
+ || (lastInsn.getSources().size() > 0))
+ && succ.getPredecessors().cardinality() > 1;
+ }
+
+ /**
+ * See Appel algorithm 19.6:
+ *
+ * Place Phi functions in appropriate locations.
+ *
+ * @param ssaMeth {@code non-null;} method to process.
+ * Modifications are made in-place.
+ * @param localInfo {@code non-null;} local variable info, used
+ * when placing phis
+ * @param threshold registers below this number are ignored
+ */
+ private static void placePhiFunctions (SsaMethod ssaMeth,
+ LocalVariableInfo localInfo, int threshold) {
+ ArrayList<SsaBasicBlock> ssaBlocks;
+ int regCount;
+ int blockCount;
+
+ ssaBlocks = ssaMeth.getBlocks();
+ blockCount = ssaBlocks.size();
+ regCount = ssaMeth.getRegCount() - threshold;
+
+ DomFront df = new DomFront(ssaMeth);
+ DomFront.DomInfo[] domInfos = df.run();
+
+ // Bit set of registers vs block index "definition sites"
+ BitSet[] defsites = new BitSet[regCount];
+
+ // Bit set of registers vs block index "phi placement sites"
+ BitSet[] phisites = new BitSet[regCount];
+
+ for (int i = 0; i < regCount; i++) {
+ defsites[i] = new BitSet(blockCount);
+ phisites[i] = new BitSet(blockCount);
+ }
+
+ /*
+ * For each register, build a set of all basic blocks where
+ * containing an assignment to that register.
+ */
+ for (int bi = 0, s = ssaBlocks.size(); bi < s; bi++) {
+ SsaBasicBlock b = ssaBlocks.get(bi);
+
+ for (SsaInsn insn : b.getInsns()) {
+ RegisterSpec rs = insn.getResult();
+
+ if (rs != null && rs.getReg() - threshold >= 0) {
+ defsites[rs.getReg() - threshold].set(bi);
+ }
+ }
+ }
+
+ if (DEBUG) {
+ System.out.println("defsites");
+
+ for (int i = 0; i < regCount; i++) {
+ StringBuilder sb = new StringBuilder();
+ sb.append('v').append(i).append(": ");
+ sb.append(defsites[i].toString());
+ System.out.println(sb);
+ }
+ }
+
+ BitSet worklist;
+
+ /*
+ * For each register, compute all locations for phi placement
+ * based on dominance-frontier algorithm.
+ */
+ for (int reg = 0, s = regCount; reg < s; reg++) {
+ int workBlockIndex;
+
+ /* Worklist set starts out with each node where reg is assigned. */
+
+ worklist = (BitSet) (defsites[reg].clone());
+
+ while (0 <= (workBlockIndex = worklist.nextSetBit(0))) {
+ worklist.clear(workBlockIndex);
+ IntIterator dfIterator
+ = domInfos[workBlockIndex].dominanceFrontiers.iterator();
+
+ while (dfIterator.hasNext()) {
+ int dfBlockIndex = dfIterator.next();
+
+ if (!phisites[reg].get(dfBlockIndex)) {
+ phisites[reg].set(dfBlockIndex);
+
+ int tReg = reg + threshold;
+ RegisterSpec rs
+ = localInfo.getStarts(dfBlockIndex).get(tReg);
+
+ if (rs == null) {
+ ssaBlocks.get(dfBlockIndex).addPhiInsnForReg(tReg);
+ } else {
+ ssaBlocks.get(dfBlockIndex).addPhiInsnForReg(rs);
+ }
+
+ if (!defsites[reg].get(dfBlockIndex)) {
+ worklist.set(dfBlockIndex);
+ }
+ }
+ }
+ }
+ }
+
+ if (DEBUG) {
+ System.out.println("phisites");
+
+ for (int i = 0; i < regCount; i++) {
+ StringBuilder sb = new StringBuilder();
+ sb.append('v').append(i).append(": ");
+ sb.append(phisites[i].toString());
+ System.out.println(sb);
+ }
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/ssa/SsaInsn.java b/dx/src/com/android/dx/ssa/SsaInsn.java
new file mode 100644
index 0000000..ca7a1a2
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/SsaInsn.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.*;
+import com.android.dx.util.ToHuman;
+
+/**
+ * An instruction in SSA form
+ */
+public abstract class SsaInsn implements ToHuman, Cloneable {
+ /** {@code non-null;} the block that contains this instance */
+ private final SsaBasicBlock block;
+
+ /** {@code null-ok;} result register */
+ private RegisterSpec result;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param result {@code null-ok;} initial result register. May be changed.
+ * @param block {@code non-null;} block containing this insn. Can
+ * never change.
+ */
+ protected SsaInsn(RegisterSpec result, SsaBasicBlock block) {
+ if (block == null) {
+ throw new NullPointerException("block == null");
+ }
+
+ this.block = block;
+ this.result = result;
+ }
+
+ /**
+ * Makes a new SSA insn form a rop insn.
+ *
+ * @param insn {@code non-null;} rop insn
+ * @param block {@code non-null;} owning block
+ * @return {@code non-null;} an appropriately constructed instance
+ */
+ public static SsaInsn makeFromRop(Insn insn, SsaBasicBlock block) {
+ return new NormalSsaInsn(insn, block);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public SsaInsn clone() {
+ try {
+ return (SsaInsn)super.clone();
+ } catch (CloneNotSupportedException ex) {
+ throw new RuntimeException ("unexpected", ex);
+ }
+ }
+
+ /**
+ * Like {@link com.android.dx.rop.code.Insn getResult()}.
+ *
+ * @return result register
+ */
+ public RegisterSpec getResult() {
+ return result;
+ }
+
+ /**
+ * Set the result register.
+ *
+ * @param result {@code non-null;} the new result register
+ */
+ protected void setResult(RegisterSpec result) {
+ if (result == null) {
+ throw new NullPointerException("result == null");
+ }
+
+ this.result = result;
+ }
+
+ /**
+ * Like {@link com.android.dx.rop.code.Insn getSources()}.
+ *
+ * @return {@code non-null;} sources list
+ */
+ abstract public RegisterSpecList getSources();
+
+ /**
+ * Gets the block to which this insn instance belongs.
+ *
+ * @return owning block
+ */
+ public SsaBasicBlock getBlock() {
+ return block;
+ }
+
+ /**
+ * Returns whether or not the specified reg is the result reg.
+ *
+ * @param reg register to test
+ * @return true if there is a result and it is stored in the specified
+ * register
+ */
+ public boolean isResultReg(int reg) {
+ return result != null && result.getReg() == reg;
+ }
+
+
+ /**
+ * Changes the result register if this insn has a result. This is used
+ * during renaming.
+ *
+ * @param reg new result register
+ */
+ public void changeResultReg(int reg) {
+ if (result != null) {
+ result = result.withReg(reg);
+ }
+ }
+
+ /**
+ * Sets the local association for the result of this insn. This is
+ * sometimes updated during the SsaRenamer process.
+ *
+ * @param local {@code null-ok;} new debug/local variable info
+ */
+ public final void setResultLocal(LocalItem local) {
+ LocalItem oldItem = result.getLocalItem();
+
+ if (local != oldItem && (local == null
+ || !local.equals(result.getLocalItem()))) {
+ result = RegisterSpec.makeLocalOptional(
+ result.getReg(), result.getType(), local);
+ }
+ }
+
+ /**
+ * Map registers after register allocation.
+ *
+ * @param mapper {@code non-null;} mapping from old to new registers
+ */
+ public final void mapRegisters(RegisterMapper mapper) {
+ RegisterSpec oldResult = result;
+
+ result = mapper.map(result);
+ block.getParent().updateOneDefinition(this, oldResult);
+ mapSourceRegisters(mapper);
+ }
+
+ /**
+ * Maps only source registers.
+ *
+ * @param mapper new mapping
+ */
+ abstract public void mapSourceRegisters(RegisterMapper mapper);
+
+ /**
+ * Returns the Rop opcode for this insn, or null if this is a phi insn.
+ *
+ * TODO: Move this up into NormalSsaInsn.
+ *
+ * @return {@code null-ok;} Rop opcode if there is one.
+ */
+ abstract public Rop getOpcode();
+
+ /**
+ * Returns the original Rop insn for this insn, or null if this is
+ * a phi insn.
+ *
+ * TODO: Move this up into NormalSsaInsn.
+ *
+ * @return {@code null-ok;} Rop insn if there is one.
+ */
+ abstract public Insn getOriginalRopInsn();
+
+ /**
+ * Gets the spec of a local variable assignment that occurs at this
+ * instruction, or null if no local variable assignment occurs. This
+ * may be the result register, or for {@code mark-local} insns
+ * it may be the source.
+ *
+ * @see com.android.dx.rop.code.Insn#getLocalAssignment()
+ *
+ * @return {@code null-ok;} a local-associated register spec or null
+ */
+ public RegisterSpec getLocalAssignment() {
+ if (result != null && result.getLocalItem() != null) {
+ return result;
+ }
+
+ return null;
+ }
+
+ /**
+ * Indicates whether the specified register is amongst the registers
+ * used as sources for this instruction.
+ *
+ * @param reg the register in question
+ * @return true if the reg is a source
+ */
+ public boolean isRegASource(int reg) {
+ return null != getSources().specForRegister(reg);
+ }
+
+ /**
+ * Transform back to ROP form.
+ *
+ * TODO: Move this up into NormalSsaInsn.
+ *
+ * @return {@code non-null;} a ROP representation of this instruction, with
+ * updated registers.
+ */
+ public abstract Insn toRopInsn();
+
+ /**
+ * @return true if this is a PhiInsn or a normal move insn
+ */
+ public abstract boolean isPhiOrMove();
+
+ /**
+ * Returns true if this insn is considered to have a side effect beyond
+ * that of assigning to the result reg.
+ *
+ * @return true if this insn is considered to have a side effect beyond
+ * that of assigning to the result reg.
+ */
+ public abstract boolean hasSideEffect();
+
+ /**
+ * @return true if this is a move (but not a move-operand or
+ * move-exception) instruction
+ */
+ public boolean isNormalMoveInsn() {
+ return false;
+ }
+
+ /**
+ * @return true if this is a move-exception instruction.
+ * These instructions must immediately follow a preceeding invoke*
+ */
+ public boolean isMoveException() {
+ return false;
+ }
+
+ /**
+ * @return true if this instruction can throw.
+ */
+ abstract public boolean canThrow();
+
+ /**
+ * Accepts a visitor.
+ *
+ * @param v {@code non-null} the visitor
+ */
+ public abstract void accept(Visitor v);
+
+ /**
+ * Visitor interface for this class.
+ */
+ public static interface Visitor {
+ /**
+ * Any non-phi move instruction
+ * @param insn {@code non-null;} the instruction to visit
+ */
+ public void visitMoveInsn(NormalSsaInsn insn);
+
+ /**
+ * Any phi insn
+ * @param insn {@code non-null;} the instruction to visit
+ */
+ public void visitPhiInsn(PhiInsn insn);
+
+ /**
+ * Any insn that isn't a move or a phi (which is also a move).
+ * @param insn {@code non-null;} the instruction to visit
+ */
+ public void visitNonMoveInsn(NormalSsaInsn insn);
+ }
+}
diff --git a/dx/src/com/android/dx/ssa/SsaMethod.java b/dx/src/com/android/dx/ssa/SsaMethod.java
new file mode 100644
index 0000000..4c2bd85
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/SsaMethod.java
@@ -0,0 +1,873 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.BasicBlockList;
+import com.android.dx.rop.code.Insn;
+import com.android.dx.rop.code.PlainInsn;
+import com.android.dx.rop.code.RegOps;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.Rop;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.code.Rops;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.util.IntList;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.Stack;
+
+/**
+ * A method in SSA form.
+ */
+public final class SsaMethod {
+ /** basic blocks, indexed by block index */
+ private ArrayList<SsaBasicBlock> blocks;
+
+ /** Index of first executed block in method */
+ private int entryBlockIndex;
+
+ /**
+ * Index of exit block, which exists only in SSA form,
+ * or or {@code -1} if there is none
+ */
+ private int exitBlockIndex;
+
+ /** total number of registers required */
+ private int registerCount;
+
+ /** first register number to use for any temporary "spares" */
+ private int spareRegisterBase;
+
+ /** current count of spare registers used */
+ private int borrowedSpareRegisters;
+
+ /** really one greater than the max label */
+ private int maxLabel;
+
+ /** the total width, in register-units, of the method's parameters */
+ private final int paramWidth;
+
+ /** true if this method has no {@code this} pointer argument */
+ private final boolean isStatic;
+
+ /**
+ * indexed by register: the insn where said register is defined or null
+ * if undefined. null until (lazily) created.
+ */
+ private SsaInsn[] definitionList;
+
+ /** indexed by register: the list of all insns that use a register */
+ private ArrayList<SsaInsn>[] useList;
+
+ /** A version of useList with each List unmodifiable */
+ private List<SsaInsn>[] unmodifiableUseList;
+
+ /**
+ * "back-convert mode". Set during back-conversion when registers
+ * are about to be mapped into a non-SSA namespace. When true,
+ * use and def lists are unavailable.
+ *
+ * TODO: Remove this mode, and place the functionality elsewhere
+ */
+ private boolean backMode;
+
+ /**
+ * @param ropMethod rop-form method to convert from
+ * @param paramWidth the total width, in register-units, of the
+ * method's parameters
+ * @param isStatic {@code true} if this method has no {@code this}
+ * pointer argument
+ */
+ public static SsaMethod newFromRopMethod(RopMethod ropMethod,
+ int paramWidth, boolean isStatic) {
+ SsaMethod result = new SsaMethod(ropMethod, paramWidth, isStatic);
+
+ result.convertRopToSsaBlocks(ropMethod);
+
+ return result;
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param ropMethod {@code non-null;} the original rop-form method that
+ * this instance is based on
+ * @param paramWidth the total width, in register-units, of the
+ * method's parameters
+ * @param isStatic {@code true} if this method has no {@code this}
+ * pointer argument
+ */
+ private SsaMethod(RopMethod ropMethod, int paramWidth, boolean isStatic) {
+ this.paramWidth = paramWidth;
+ this.isStatic = isStatic;
+ this.backMode = false;
+ this.maxLabel = ropMethod.getBlocks().getMaxLabel();
+ this.registerCount = ropMethod.getBlocks().getRegCount();
+ this.spareRegisterBase = registerCount;
+ }
+
+ /**
+ * Builds a BitSet of block indices from a basic block list and a list
+ * of labels taken from Rop form.
+ *
+ * @param blocks Rop blocks
+ * @param labelList list of rop block labels
+ * @return BitSet of block indices
+ */
+ static BitSet bitSetFromLabelList(BasicBlockList blocks,
+ IntList labelList) {
+ BitSet result = new BitSet(blocks.size());
+
+ for (int i = 0, sz = labelList.size(); i < sz; i++) {
+ result.set(blocks.indexOfLabel(labelList.get(i)));
+ }
+
+ return result;
+ }
+
+ /**
+ * Builds an IntList of block indices from a basic block list and a list
+ * of labels taken from Rop form.
+ *
+ * @param ropBlocks Rop blocks
+ * @param labelList list of rop block labels
+ * @return IntList of block indices
+ */
+ public static IntList indexListFromLabelList(BasicBlockList ropBlocks,
+ IntList labelList) {
+
+ IntList result = new IntList(labelList.size());
+
+ for (int i = 0, sz = labelList.size(); i < sz; i++) {
+ result.add(ropBlocks.indexOfLabel(labelList.get(i)));
+ }
+
+ return result;
+ }
+
+ private void convertRopToSsaBlocks(RopMethod rmeth) {
+ BasicBlockList ropBlocks = rmeth.getBlocks();
+ int sz = ropBlocks.size();
+
+ blocks = new ArrayList<SsaBasicBlock>(sz + 2);
+
+ for (int i = 0; i < sz; i++) {
+ SsaBasicBlock sbb = SsaBasicBlock.newFromRop(rmeth, i, this);
+ blocks.add(sbb);
+ }
+
+ // Add an no-op entry block.
+ int origEntryBlockIndex = rmeth.getBlocks()
+ .indexOfLabel(rmeth.getFirstLabel());
+
+ SsaBasicBlock entryBlock
+ = blocks.get(origEntryBlockIndex).insertNewPredecessor();
+
+ entryBlockIndex = entryBlock.getIndex();
+ exitBlockIndex = -1; // This gets made later.
+ }
+
+ /**
+ * Creates an exit block and attaches it to the CFG if this method
+ * exits. Methods that never exit will not have an exit block. This
+ * is called after edge-splitting and phi insertion, since the edges
+ * going into the exit block should not be considered in those steps.
+ */
+ /*package*/ void makeExitBlock() {
+ if (exitBlockIndex >= 0) {
+ throw new RuntimeException("must be called at most once");
+ }
+
+ exitBlockIndex = blocks.size();
+ SsaBasicBlock exitBlock
+ = new SsaBasicBlock(exitBlockIndex, maxLabel++, this);
+
+ blocks.add(exitBlock);
+
+ for (SsaBasicBlock block : blocks) {
+ block.exitBlockFixup(exitBlock);
+ }
+
+ if (exitBlock.getPredecessors().cardinality() == 0) {
+ // In cases where there is no exit...
+ blocks.remove(exitBlockIndex);
+ exitBlockIndex = -1;
+ maxLabel--;
+ }
+ }
+
+ /**
+ * Gets a new {@code GOTO} insn.
+ *
+ * @param block block to which this GOTO will be added
+ * (not it's destination!)
+ * @return an appropriately-constructed instance.
+ */
+ private static SsaInsn getGoto(SsaBasicBlock block) {
+ return new NormalSsaInsn (
+ new PlainInsn(Rops.GOTO, SourcePosition.NO_INFO,
+ null, RegisterSpecList.EMPTY), block);
+ }
+
+ /**
+ * Makes a new basic block for this method, which is empty besides
+ * a single {@code GOTO}. Successors and predecessors are not yet
+ * set.
+ *
+ * @return new block
+ */
+ public SsaBasicBlock makeNewGotoBlock() {
+ int newIndex = blocks.size();
+ SsaBasicBlock newBlock = new SsaBasicBlock(newIndex, maxLabel++, this);
+
+ newBlock.getInsns().add(getGoto(newBlock));
+ blocks.add(newBlock);
+
+ return newBlock;
+ }
+
+ /**
+ * @return block index of first execution block
+ */
+ public int getEntryBlockIndex() {
+ return entryBlockIndex;
+ }
+
+ /**
+ * @return first execution block
+ */
+ public SsaBasicBlock getEntryBlock() {
+ return blocks.get(entryBlockIndex);
+ }
+
+ /**
+ * @return block index of exit block or {@code -1} if there is none
+ */
+ public int getExitBlockIndex() {
+ return exitBlockIndex;
+ }
+
+ /**
+ * @return {@code null-ok;} block of exit block or {@code null} if
+ * there is none
+ */
+ public SsaBasicBlock getExitBlock() {
+ return exitBlockIndex < 0 ? null : blocks.get(exitBlockIndex);
+ }
+
+ /**
+ * @param bi block index or {@code -1} for none
+ * @return rop label or {code -1} if {@code bi} was {@code -1}
+ */
+ public int blockIndexToRopLabel(int bi) {
+ if (bi < 0) {
+ return -1;
+ }
+ return blocks.get(bi).getRopLabel();
+ }
+
+ /**
+ * @return count of registers used in this method
+ */
+ public int getRegCount() {
+ return registerCount;
+ }
+
+ /**
+ * @return the total width, in register units, of the method's
+ * parameters
+ */
+ public int getParamWidth() {
+ return paramWidth;
+ }
+
+ /**
+ * Returns {@code true} if this is a static method.
+ *
+ * @return {@code true} if this is a static method
+ */
+ public boolean isStatic() {
+ return isStatic;
+ }
+
+ /**
+ * Borrows a register to use as a temp. Used in the phi removal process.
+ * Call returnSpareRegisters() when done.
+ *
+ * @param category width (1 or 2) of the register
+ * @return register number to use
+ */
+ public int borrowSpareRegister(int category) {
+ int result = spareRegisterBase + borrowedSpareRegisters;
+
+ borrowedSpareRegisters += category;
+ registerCount = Math.max(registerCount, result + category);
+
+ return result;
+ }
+
+ /**
+ * Returns all borrowed registers.
+ */
+ public void returnSpareRegisters() {
+ borrowedSpareRegisters = 0;
+ }
+
+ /**
+ * @return {@code non-null;} basic block list. Do not modify.
+ */
+ public ArrayList<SsaBasicBlock> getBlocks() {
+ return blocks;
+ }
+
+ /**
+ * Returns the count of reachable blocks in this method: blocks that have
+ * predecessors (or are the start block)
+ *
+ * @return {@code >= 0;} number of reachable basic blocks
+ */
+ public int getCountReachableBlocks() {
+ int ret = 0;
+
+ for (SsaBasicBlock b : blocks) {
+ // Blocks that have been disconnected don't count.
+ if (b.isReachable()) {
+ ret++;
+ }
+ }
+
+ return ret;
+ }
+
+ /**
+ * Computes reachability for all blocks in the method. First clears old
+ * values from all blocks, then starts with the entry block and walks down
+ * the control flow graph, marking all blocks it finds as reachable.
+ */
+ public void computeReachability() {
+ for (SsaBasicBlock block : blocks) {
+ block.setReachable(0);
+ }
+
+ ArrayList<SsaBasicBlock> blockList = new ArrayList<SsaBasicBlock>();
+ blockList.add(this.getEntryBlock());
+
+ while (!blockList.isEmpty()) {
+ SsaBasicBlock block = blockList.remove(0);
+ if (block.isReachable()) continue;
+
+ block.setReachable(1);
+ BitSet succs = block.getSuccessors();
+ for (int i = succs.nextSetBit(0); i >= 0;
+ i = succs.nextSetBit(i + 1)) {
+ blockList.add(blocks.get(i));
+ }
+ }
+ }
+
+ /**
+ * Remaps unversioned registers.
+ *
+ * @param mapper maps old registers to new.
+ */
+ public void mapRegisters(RegisterMapper mapper) {
+ for (SsaBasicBlock block : getBlocks()) {
+ for (SsaInsn insn : block.getInsns()) {
+ insn.mapRegisters(mapper);
+ }
+ }
+
+ registerCount = mapper.getNewRegisterCount();
+ spareRegisterBase = registerCount;
+ }
+
+ /**
+ * Returns the insn that defines the given register
+ * @param reg register in question
+ * @return insn (actual instance from code) that defined this reg or null
+ * if reg is not defined.
+ */
+ public SsaInsn getDefinitionForRegister(int reg) {
+ if (backMode) {
+ throw new RuntimeException("No def list in back mode");
+ }
+
+ if (definitionList != null) {
+ return definitionList[reg];
+ }
+
+ definitionList = new SsaInsn[getRegCount()];
+
+ forEachInsn(new SsaInsn.Visitor() {
+ public void visitMoveInsn (NormalSsaInsn insn) {
+ definitionList[insn.getResult().getReg()] = insn;
+ }
+ public void visitPhiInsn (PhiInsn phi) {
+ definitionList[phi.getResult().getReg()] = phi;
+ }
+ public void visitNonMoveInsn (NormalSsaInsn insn) {
+ RegisterSpec result = insn.getResult();
+ if (result != null) {
+ definitionList[insn.getResult().getReg()] = insn;
+ }
+ }
+ });
+
+ return definitionList[reg];
+ }
+
+ /**
+ * Builds useList and unmodifiableUseList.
+ */
+ private void buildUseList() {
+ if (backMode) {
+ throw new RuntimeException("No use list in back mode");
+ }
+
+ useList = new ArrayList[registerCount];
+
+ for (int i = 0; i < registerCount; i++) {
+ useList[i] = new ArrayList();
+ }
+
+ forEachInsn(new SsaInsn.Visitor() {
+ /** {@inheritDoc} */
+ public void visitMoveInsn (NormalSsaInsn insn) {
+ addToUses(insn);
+ }
+ /** {@inheritDoc} */
+ public void visitPhiInsn (PhiInsn phi) {
+ addToUses(phi);
+ }
+ /** {@inheritDoc} */
+ public void visitNonMoveInsn (NormalSsaInsn insn) {
+ addToUses(insn);
+ }
+ /**
+ * Adds specified insn to the uses list for all of its sources.
+ * @param insn {@code non-null;} insn to process
+ */
+ private void addToUses(SsaInsn insn) {
+ RegisterSpecList rl = insn.getSources();
+ int sz = rl.size();
+
+ for (int i = 0; i < sz; i++) {
+ useList[rl.get(i).getReg()].add(insn);
+ }
+ }
+ });
+
+ unmodifiableUseList = new List[registerCount];
+
+ for (int i = 0; i < registerCount; i++) {
+ unmodifiableUseList[i] = Collections.unmodifiableList(useList[i]);
+ }
+ }
+
+ /**
+ * Updates the use list for a single change in source register.
+ *
+ * @param insn {@code non-null;} insn being changed
+ * @param oldSource {@code null-ok;} The source that was used, if
+ * applicable
+ * @param newSource {@code non-null;} the new source being used
+ */
+ /*package*/ void onSourceChanged(SsaInsn insn,
+ RegisterSpec oldSource, RegisterSpec newSource) {
+ if (useList == null) return;
+
+ if (oldSource != null) {
+ int reg = oldSource.getReg();
+ useList[reg].remove(insn);
+ }
+
+ int reg = newSource.getReg();
+ if (useList.length <= reg) {
+ useList = null;
+ return;
+ }
+ useList[reg].add(insn);
+ }
+
+ /**
+ * Updates the use list for a source list change.
+ *
+ * @param insn {@code insn non-null;} insn being changed.
+ * {@code insn.getSources()} must return the new source list.
+ * @param oldSources {@code null-ok;} list of sources that were
+ * previously used
+ */
+ /*package*/ void onSourcesChanged(SsaInsn insn,
+ RegisterSpecList oldSources) {
+ if (useList == null) return;
+
+ if (oldSources != null) {
+ removeFromUseList(insn, oldSources);
+ }
+
+ RegisterSpecList sources = insn.getSources();
+ int szNew = sources.size();
+
+ for (int i = 0; i < szNew; i++) {
+ int reg = sources.get(i).getReg();
+ useList[reg].add(insn);
+ }
+ }
+
+ /**
+ * Removes a given {@code insn} from the use lists for the given
+ * {@code oldSources} (rather than the sources currently
+ * returned by insn.getSources()).
+ *
+ * @param insn {@code non-null;} insn in question
+ * @param oldSources {@code null-ok;} registers whose use lists
+ * {@code insn} should be removed form
+ */
+ private void removeFromUseList(SsaInsn insn, RegisterSpecList oldSources) {
+ if (oldSources == null) {
+ return;
+ }
+
+ int szNew = oldSources.size();
+ for (int i = 0; i < szNew; i++) {
+ if (!useList[oldSources.get(i).getReg()].remove(insn)) {
+ throw new RuntimeException("use not found");
+ }
+ }
+ }
+
+ /**
+ * Adds an insn to both the use and def lists. For use when adding
+ * a new insn to the method.
+ *
+ * @param insn {@code non-null;} insn to add
+ */
+ /*package*/ void onInsnAdded(SsaInsn insn) {
+ onSourcesChanged(insn, null);
+ updateOneDefinition(insn, null);
+ }
+
+ /**
+ * Removes an instruction from use and def lists. For use during
+ * instruction removal.
+ *
+ * @param insn {@code non-null;} insn to remove
+ */
+ /*package*/ void onInsnRemoved(SsaInsn insn) {
+ if (useList != null) {
+ removeFromUseList(insn, insn.getSources());
+ }
+
+ RegisterSpec resultReg = insn.getResult();
+ if (definitionList != null && resultReg != null) {
+ definitionList[resultReg.getReg()] = null;
+ }
+ }
+
+ /**
+ * Indicates that the instruction list has changed or the SSA register
+ * count has increased, so that internal datastructures that rely on
+ * it should be rebuild. In general, the various other on* methods
+ * should be called in preference when changes occur if they are
+ * applicable.
+ */
+ public void onInsnsChanged() {
+ // Definition list will need to be recomputed
+ definitionList = null;
+
+ // Use list will need to be recomputed
+ useList = null;
+ unmodifiableUseList = null;
+ }
+
+ /**
+ * Updates a single definition.
+ *
+ * @param insn {@code non-null;} insn who's result should be recorded as
+ * a definition
+ * @param oldResult {@code null-ok;} a previous result that should
+ * be no longer considered a definition by this insn
+ */
+ /*package*/ void updateOneDefinition(SsaInsn insn,
+ RegisterSpec oldResult) {
+ if (definitionList == null) return;
+
+ if (oldResult != null) {
+ int reg = oldResult.getReg();
+ definitionList[reg] = null;
+ }
+
+ RegisterSpec resultReg = insn.getResult();
+
+ if (resultReg != null) {
+ int reg = resultReg.getReg();
+
+ if (definitionList[reg] != null) {
+ throw new RuntimeException("Duplicate add of insn");
+ } else {
+ definitionList[resultReg.getReg()] = insn;
+ }
+ }
+ }
+
+ /**
+ * Returns the list of all source uses (not results) for a register.
+ *
+ * @param reg register in question
+ * @return unmodifiable instruction list
+ */
+ public List<SsaInsn> getUseListForRegister(int reg) {
+
+ if (unmodifiableUseList == null) {
+ buildUseList();
+ }
+
+ return unmodifiableUseList[reg];
+ }
+
+ /**
+ * Returns a modifiable copy of the register use list.
+ *
+ * @return modifiable copy of the use-list, indexed by register
+ */
+ public ArrayList<SsaInsn>[] getUseListCopy() {
+ if (useList == null) {
+ buildUseList();
+ }
+
+ ArrayList<SsaInsn>[] useListCopy
+ = (ArrayList<SsaInsn>[])(new ArrayList[registerCount]);
+
+ for (int i = 0; i < registerCount; i++) {
+ useListCopy[i] = (ArrayList<SsaInsn>)(new ArrayList(useList[i]));
+ }
+
+ return useListCopy;
+ }
+
+ /**
+ * Checks to see if the given SSA reg is ever associated with a local
+ * local variable. Each SSA reg may be associated with at most one
+ * local var.
+ *
+ * @param spec {@code non-null;} ssa reg
+ * @return true if reg is ever associated with a local
+ */
+ public boolean isRegALocal(RegisterSpec spec) {
+ SsaInsn defn = getDefinitionForRegister(spec.getReg());
+
+ if (defn == null) {
+ // version 0 registers are never used as locals
+ return false;
+ }
+
+ // Does the definition have a local associated with it?
+ if (defn.getLocalAssignment() != null) return true;
+
+ // If not, is there a mark-local insn?
+ for (SsaInsn use : getUseListForRegister(spec.getReg())) {
+ Insn insn = use.getOriginalRopInsn();
+
+ if (insn != null
+ && insn.getOpcode().getOpcode() == RegOps.MARK_LOCAL) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Sets the new register count after renaming.
+ *
+ * @param newRegCount new register count
+ */
+ /*package*/ void setNewRegCount(int newRegCount) {
+ registerCount = newRegCount;
+ spareRegisterBase = registerCount;
+ onInsnsChanged();
+ }
+
+ /**
+ * Makes a new SSA register. For use after renaming has completed.
+ *
+ * @return {@code >=0;} new SSA register.
+ */
+ public int makeNewSsaReg() {
+ int reg = registerCount++;
+ spareRegisterBase = registerCount;
+ onInsnsChanged();
+ return reg;
+ }
+
+ /**
+ * Visits all insns in this method.
+ *
+ * @param visitor {@code non-null;} callback interface
+ */
+ public void forEachInsn(SsaInsn.Visitor visitor) {
+ for (SsaBasicBlock block : blocks) {
+ block.forEachInsn(visitor);
+ }
+ }
+
+ /**
+ * Visits each phi insn in this method
+ * @param v {@code non-null;} callback.
+ *
+ */
+ public void forEachPhiInsn(PhiInsn.Visitor v) {
+ for (SsaBasicBlock block : blocks) {
+ block.forEachPhiInsn(v);
+ }
+ }
+
+
+ /**
+ * Walks the basic block tree in depth-first order, calling the visitor
+ * method once for every block. This depth-first walk may be run forward
+ * from the method entry point or backwards from the method exit points.
+ *
+ * @param reverse true if this should walk backwards from the exit points
+ * @param v {@code non-null;} callback interface. {@code parent} is set
+ * unless this is the root node
+ */
+ public void forEachBlockDepthFirst(boolean reverse,
+ SsaBasicBlock.Visitor v) {
+ BitSet visited = new BitSet(blocks.size());
+
+ // We push the parent first, then the child on the stack.
+ Stack<SsaBasicBlock> stack = new Stack<SsaBasicBlock>();
+
+ SsaBasicBlock rootBlock = reverse ? getExitBlock() : getEntryBlock();
+
+ if (rootBlock == null) {
+ // in the case there's no exit block
+ return;
+ }
+
+ stack.add(null); // Start with null parent.
+ stack.add(rootBlock);
+
+ while (stack.size() > 0) {
+ SsaBasicBlock cur = stack.pop();
+ SsaBasicBlock parent = stack.pop();
+
+ if (!visited.get(cur.getIndex())) {
+ BitSet children
+ = reverse ? cur.getPredecessors() : cur.getSuccessors();
+ for (int i = children.nextSetBit(0); i >= 0
+ ; i = children.nextSetBit(i + 1)) {
+ stack.add(cur);
+ stack.add(blocks.get(i));
+ }
+ visited.set(cur.getIndex());
+ v.visitBlock(cur, parent);
+ }
+ }
+ }
+
+ /**
+ * Visits blocks in dom-tree order, starting at the current node.
+ * The {@code parent} parameter of the Visitor.visitBlock callback
+ * is currently always set to null.
+ *
+ * @param v {@code non-null;} callback interface
+ */
+ public void forEachBlockDepthFirstDom(SsaBasicBlock.Visitor v) {
+ BitSet visited = new BitSet(getBlocks().size());
+ Stack<SsaBasicBlock> stack = new Stack<SsaBasicBlock>();
+
+ stack.add(getEntryBlock());
+
+ while (stack.size() > 0) {
+ SsaBasicBlock cur = stack.pop();
+ ArrayList<SsaBasicBlock> curDomChildren = cur.getDomChildren();
+
+ if (!visited.get(cur.getIndex())) {
+ // We walk the tree this way for historical reasons...
+ for (int i = curDomChildren.size() - 1; i >= 0; i--) {
+ SsaBasicBlock child = curDomChildren.get(i);
+ stack.add(child);
+ }
+ visited.set(cur.getIndex());
+ v.visitBlock(cur, null);
+ }
+ }
+ }
+
+ /**
+ * Deletes all insns in the set from this method.
+ *
+ * @param deletedInsns {@code non-null;} insns to delete
+ */
+ public void deleteInsns(Set<SsaInsn> deletedInsns) {
+ for (SsaBasicBlock block : getBlocks()) {
+ ArrayList<SsaInsn> insns = block.getInsns();
+
+ for (int i = insns.size() - 1; i >= 0; i--) {
+ SsaInsn insn = insns.get(i);
+
+ if (deletedInsns.contains(insn)) {
+ onInsnRemoved(insn);
+ insns.remove(i);
+ }
+ }
+
+ // Check to see if we need to add a GOTO
+
+ int insnsSz = insns.size();
+ SsaInsn lastInsn = (insnsSz == 0) ? null : insns.get(insnsSz - 1);
+
+ if (block != getExitBlock() && (insnsSz == 0
+ || lastInsn.getOriginalRopInsn() == null
+ || lastInsn.getOriginalRopInsn().getOpcode()
+ .getBranchingness() == Rop.BRANCH_NONE)) {
+ // We managed to eat a throwable insn
+
+ Insn gotoInsn = new PlainInsn(Rops.GOTO,
+ SourcePosition.NO_INFO, null, RegisterSpecList.EMPTY);
+ insns.add(SsaInsn.makeFromRop(gotoInsn, block));
+
+ // Remove secondary successors from this block
+ BitSet succs = block.getSuccessors();
+ for (int i = succs.nextSetBit(0); i >= 0;
+ i = succs.nextSetBit(i + 1)) {
+ if (i != block.getPrimarySuccessorIndex()) {
+ block.removeSuccessor(i);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Sets "back-convert mode". Set during back-conversion when registers
+ * are about to be mapped into a non-SSA namespace. When true,
+ * use and def lists are unavailable.
+ */
+ public void setBackMode() {
+ backMode = true;
+ useList = null;
+ definitionList = null;
+ }
+}
diff --git a/dx/src/com/android/dx/ssa/SsaRenamer.java b/dx/src/com/android/dx/ssa/SsaRenamer.java
new file mode 100644
index 0000000..58e4142
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/SsaRenamer.java
@@ -0,0 +1,662 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+import com.android.dx.rop.code.LocalItem;
+import com.android.dx.rop.code.PlainInsn;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.Rops;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.rop.type.Type;
+import com.android.dx.util.IntList;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.HashMap;
+import java.util.HashSet;
+
+/**
+ * Complete transformation to SSA form by renaming all registers accessed.<p>
+ *
+ * See Appel algorithm 19.7<p>
+ *
+ * Unlike the original algorithm presented in Appel, this renamer converts
+ * to a new flat (versionless) register space. The "version 0" registers,
+ * which represent the initial state of the Rop registers and should never
+ * actually be meaningfully accessed in a legal program, are represented
+ * as the first N registers in the SSA namespace. Subsequent assignments
+ * are assigned new unique names. Note that the incoming Rop representation
+ * has a concept of register widths, where 64-bit values are stored into
+ * two adjoining Rop registers. This adjoining register representation is
+ * ignored in SSA form conversion and while in SSA form, each register can be e
+ * either 32 or 64 bits wide depending on use. The adjoining-register
+ * represention is re-created later when converting back to Rop form. <p>
+ *
+ * But, please note, the SSA Renamer's ignoring of the adjoining-register ROP
+ * representation means that unaligned accesses to 64-bit registers are not
+ * supported. For example, you cannot do a 32-bit operation on a portion of
+ * a 64-bit register. This will never be observed to happen when coming
+ * from Java code, of course.<p>
+ *
+ * The implementation here, rather than keeping a single register version
+ * stack for the entire method as the dom tree is walked, instead keeps
+ * a mapping table for the current block being processed. Once the
+ * current block has been processed, this mapping table is then copied
+ * and used as the initial state for child blocks.<p>
+ */
+public class SsaRenamer implements Runnable {
+ /** debug flag */
+ private static final boolean DEBUG = false;
+
+ /** method we're processing */
+ private final SsaMethod ssaMeth;
+
+ /** next available SSA register */
+ private int nextSsaReg;
+
+ /** the number of original rop registers */
+ private final int ropRegCount;
+
+ /** work only on registers above this value */
+ private int threshold;
+
+ /**
+ * indexed by block index; register version state for each block start.
+ * This list is updated by each dom parent for its children. The only
+ * sub-arrays that exist at any one time are the start states for blocks
+ * yet to be processed by a {@code BlockRenamer} instance.
+ */
+ private final RegisterSpec[][] startsForBlocks;
+
+ /** map of SSA register number to debug (local var names) or null of n/a */
+ private final ArrayList<LocalItem> ssaRegToLocalItems;
+
+ /**
+ * maps SSA registers back to the original rop number. Used for
+ * debug only.
+ */
+ private IntList ssaRegToRopReg;
+
+ /**
+ * Constructs an instance of the renamer
+ *
+ * @param ssaMeth {@code non-null;} un-renamed SSA method that will
+ * be renamed.
+ */
+ public SsaRenamer(SsaMethod ssaMeth) {
+ ropRegCount = ssaMeth.getRegCount();
+
+ this.ssaMeth = ssaMeth;
+
+ /*
+ * Reserve the first N registers in the SSA register space for
+ * "version 0" registers.
+ */
+ nextSsaReg = ropRegCount;
+ threshold = 0;
+ startsForBlocks = new RegisterSpec[ssaMeth.getBlocks().size()][];
+
+ ssaRegToLocalItems = new ArrayList<LocalItem>();
+
+ if (DEBUG) {
+ ssaRegToRopReg = new IntList(ropRegCount);
+ }
+
+ /*
+ * Appel 19.7
+ *
+ * Initialization:
+ * for each variable a // register i
+ * Count[a] <- 0 // nextSsaReg, flattened
+ * Stack[a] <- 0 // versionStack
+ * push 0 onto Stack[a]
+ *
+ */
+
+ // top entry for the version stack is version 0
+ RegisterSpec[] initialRegMapping = new RegisterSpec[ropRegCount];
+ for (int i = 0; i < ropRegCount; i++) {
+ // everyone starts with a version 0 register
+ initialRegMapping[i] = RegisterSpec.make(i, Type.VOID);
+
+ if (DEBUG) {
+ ssaRegToRopReg.add(i);
+ }
+ }
+
+ // Initial state for entry block
+ startsForBlocks[ssaMeth.getEntryBlockIndex()] = initialRegMapping;
+ }
+
+ /**
+ * Constructs an instance of the renamer with threshold set
+ *
+ * @param ssaMeth {@code non-null;} un-renamed SSA method that will
+ * be renamed.
+ * @param thresh registers below this number are unchanged
+ */
+ public SsaRenamer(SsaMethod ssaMeth, int thresh) {
+ this(ssaMeth);
+ threshold = thresh;
+ }
+
+ /**
+ * Performs renaming transformation, modifying the method's instructions
+ * in-place.
+ */
+ public void run() {
+ // Rename each block in dom-tree DFS order.
+ ssaMeth.forEachBlockDepthFirstDom(new SsaBasicBlock.Visitor() {
+ public void visitBlock (SsaBasicBlock block,
+ SsaBasicBlock unused) {
+ new BlockRenamer(block).process();
+ }
+ });
+
+ ssaMeth.setNewRegCount(nextSsaReg);
+ ssaMeth.onInsnsChanged();
+
+ if (DEBUG) {
+ System.out.println("SSA\tRop");
+ /*
+ * We're going to compute the version of the rop register
+ * by keeping a running total of how many times the rop
+ * register has been mapped.
+ */
+ int[] versions = new int[ropRegCount];
+
+ int sz = ssaRegToRopReg.size();
+ for (int i = 0; i < sz; i++) {
+ int ropReg = ssaRegToRopReg.get(i);
+ System.out.println(i + "\t" + ropReg + "["
+ + versions[ropReg] + "]");
+ versions[ropReg]++;
+ }
+ }
+ }
+
+ /**
+ * Duplicates a RegisterSpec array.
+ *
+ * @param orig {@code non-null;} array to duplicate
+ * @return {@code non-null;} new instance
+ */
+ private static RegisterSpec[] dupArray(RegisterSpec[] orig) {
+ RegisterSpec[] copy = new RegisterSpec[orig.length];
+
+ System.arraycopy(orig, 0, copy, 0, orig.length);
+
+ return copy;
+ }
+
+ /**
+ * Gets a local variable item for a specified register.
+ *
+ * @param ssaReg register in SSA name space
+ * @return {@code null-ok;} Local variable name or null if none
+ */
+ private LocalItem getLocalForNewReg(int ssaReg) {
+ if (ssaReg < ssaRegToLocalItems.size()) {
+ return ssaRegToLocalItems.get(ssaReg);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Records a debug (local variable) name for a specified register.
+ *
+ * @param ssaReg non-null named register spec in SSA name space
+ */
+ private void setNameForSsaReg(RegisterSpec ssaReg) {
+ int reg = ssaReg.getReg();
+ LocalItem local = ssaReg.getLocalItem();
+
+ ssaRegToLocalItems.ensureCapacity(reg + 1);
+ while (ssaRegToLocalItems.size() <= reg) {
+ ssaRegToLocalItems.add(null);
+ }
+
+ ssaRegToLocalItems.set(reg, local);
+ }
+
+ /**
+ * Returns true if this SSA register is below the specified threshold.
+ * Used when most code is already in SSA form, and renaming is needed only
+ * for registers above a certain threshold.
+ *
+ * @param ssaReg the SSA register in question
+ * @return {@code true} if its register number is below the threshold
+ */
+ private boolean isBelowThresholdRegister(int ssaReg) {
+ return ssaReg < threshold;
+ }
+
+ /**
+ * Returns true if this SSA register is a "version 0"
+ * register. All version 0 registers are assigned the first N register
+ * numbers, where N is the count of original rop registers.
+ *
+ * @param ssaReg the SSA register in question
+ * @return true if it is a version 0 register.
+ */
+ private boolean isVersionZeroRegister(int ssaReg) {
+ return ssaReg < ropRegCount;
+ }
+
+ /**
+ * Returns true if a and b are equal or are both null.
+ *
+ * @param a null-ok
+ * @param b null-ok
+ * @return Returns true if a and b are equal or are both null
+ */
+ private static boolean equalsHandlesNulls(Object a, Object b) {
+ return a == b || (a != null && a.equals(b));
+ }
+
+ /**
+ * Processes all insns in a block and renames their registers
+ * as appropriate.
+ */
+ private class BlockRenamer implements SsaInsn.Visitor{
+ /** {@code non-null;} block we're processing. */
+ private final SsaBasicBlock block;
+
+ /**
+ * {@code non-null;} indexed by old register name. The current
+ * top of the version stack as seen by this block. It's
+ * initialized from the ending state of its dom parent,
+ * updated as the block's instructions are processed, and then
+ * copied to each one of its dom children.
+ */
+ private final RegisterSpec[] currentMapping;
+
+ /**
+ * contains the set of moves we need to keep to preserve local
+ * var info. All other moves will be deleted.
+ */
+ private final HashSet<SsaInsn> movesToKeep;
+
+ /**
+ * maps the set of insns to replace after renaming is finished
+ * on the block.
+ */
+ private final HashMap<SsaInsn, SsaInsn> insnsToReplace;
+
+ private final RenamingMapper mapper;
+
+ /**
+ * Constructs a block renamer instance. Call {@code process}
+ * to process.
+ *
+ * @param block {@code non-null;} block to process
+ */
+ BlockRenamer(final SsaBasicBlock block) {
+ this.block = block;
+ currentMapping = startsForBlocks[block.getIndex()];
+ movesToKeep = new HashSet<SsaInsn>();
+ insnsToReplace = new HashMap<SsaInsn, SsaInsn>();
+ mapper = new RenamingMapper();
+
+ // We don't need our own start state anymore
+ startsForBlocks[block.getIndex()] = null;
+ }
+
+ /**
+ * Provides a register mapping between the old register space
+ * and the current renaming mapping. The mapping is updated
+ * as the current block's instructions are processed.
+ */
+ private class RenamingMapper extends RegisterMapper {
+ public RenamingMapper() {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getNewRegisterCount() {
+ return nextSsaReg;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RegisterSpec map(RegisterSpec registerSpec) {
+ if (registerSpec == null) return null;
+
+ int reg = registerSpec.getReg();
+
+ // For debugging: assert that the mapped types are compatible.
+ if (DEBUG) {
+ RegisterSpec newVersion = currentMapping[reg];
+ if (newVersion.getBasicType() != Type.BT_VOID
+ && registerSpec.getBasicFrameType()
+ != newVersion.getBasicFrameType()) {
+
+ throw new RuntimeException(
+ "mapping registers of incompatible types! "
+ + registerSpec
+ + " " + currentMapping[reg]);
+ }
+ }
+
+ return registerSpec.withReg(currentMapping[reg].getReg());
+ }
+ }
+
+ /**
+ * Renames all the variables in this block and inserts appriopriate
+ * phis in successor blocks.
+ */
+ public void process() {
+ /*
+ * From Appel:
+ *
+ * Rename(n) =
+ * for each statement S in block n // 'statement' in 'block'
+ */
+
+ block.forEachInsn(this);
+
+ updateSuccessorPhis();
+
+ // Delete all move insns in this block.
+ ArrayList<SsaInsn> insns = block.getInsns();
+ int szInsns = insns.size();
+
+ for (int i = szInsns - 1; i >= 0 ; i--) {
+ SsaInsn insn = insns.get(i);
+ SsaInsn replaceInsn;
+
+ replaceInsn = insnsToReplace.get(insn);
+
+ if (replaceInsn != null) {
+ insns.set(i, replaceInsn);
+ } else if (insn.isNormalMoveInsn()
+ && !movesToKeep.contains(insn)) {
+ insns.remove(i);
+ }
+ }
+
+ // Store the start states for our dom children.
+ boolean first = true;
+ for (SsaBasicBlock child : block.getDomChildren()) {
+ if (child != block) {
+ // Don't bother duplicating the array for the first child.
+ RegisterSpec[] childStart = first ? currentMapping
+ : dupArray(currentMapping);
+
+ startsForBlocks[child.getIndex()] = childStart;
+ first = false;
+ }
+ }
+
+ // currentMapping is owned by a child now.
+ }
+
+ /**
+ * Enforces a few contraints when a register mapping is added.
+ *
+ * <ol>
+ * <li> Ensures that all new SSA registers specs in the mapping
+ * table with the same register number are identical. In effect, once
+ * an SSA register spec has received or lost a local variable name,
+ * then every old-namespace register that maps to it should gain or
+ * lose its local variable name as well.
+ * <li> Records the local name associated with the
+ * register so that a register is never associated with more than one
+ * local.
+ * <li> ensures that only one SSA register
+ * at a time is considered to be associated with a local variable. When
+ * {@code currentMapping} is updated and the newly added element
+ * is named, strip that name from any other SSA registers.
+ * </ol>
+ *
+ * @param ropReg {@code >= 0;} rop register number
+ * @param ssaReg {@code non-null;} an SSA register that has just
+ * been added to {@code currentMapping}
+ */
+ private void addMapping(int ropReg, RegisterSpec ssaReg) {
+ int ssaRegNum = ssaReg.getReg();
+ LocalItem ssaRegLocal = ssaReg.getLocalItem();
+
+ currentMapping[ropReg] = ssaReg;
+
+ /*
+ * Ensure all SSA register specs with the same reg are identical.
+ */
+ for (int i = currentMapping.length - 1; i >= 0; i--) {
+ RegisterSpec cur = currentMapping[i];
+
+ if (ssaRegNum == cur.getReg()) {
+ currentMapping[i] = ssaReg;
+ }
+ }
+
+ // All further steps are for registers with local information.
+ if (ssaRegLocal == null) {
+ return;
+ }
+
+ // Record that this SSA reg has been associated with a local.
+ setNameForSsaReg(ssaReg);
+
+ // Ensure that no other SSA regs are associated with this local.
+ for (int i = currentMapping.length - 1; i >= 0; i--) {
+ RegisterSpec cur = currentMapping[i];
+
+ if (ssaRegNum != cur.getReg()
+ && ssaRegLocal.equals(cur.getLocalItem())) {
+ currentMapping[i] = cur.withLocalItem(null);
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Phi insns have their result registers renamed.
+ */
+ public void visitPhiInsn(PhiInsn phi) {
+ /* don't process sources for phi's */
+ processResultReg(phi);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Move insns are treated as a simple mapping operation, and
+ * will later be removed unless they represent a local variable
+ * assignment. If they represent a local variable assignement, they
+ * are preserved.
+ */
+ public void visitMoveInsn(NormalSsaInsn insn) {
+ /*
+ * For moves: copy propogate the move if we can, but don't
+ * if we need to preserve local variable info and the
+ * result has a different name than the source.
+ */
+
+ RegisterSpec ropResult = insn.getResult();
+ int ropResultReg = ropResult.getReg();
+ int ropSourceReg = insn.getSources().get(0).getReg();
+
+ insn.mapSourceRegisters(mapper);
+ int ssaSourceReg = insn.getSources().get(0).getReg();
+
+ LocalItem sourceLocal
+ = currentMapping[ropSourceReg].getLocalItem();
+ LocalItem resultLocal = ropResult.getLocalItem();
+
+ /*
+ * A move from a register that's currently associated with a local
+ * to one that will not be associated with a local does not need
+ * to be preserved, but the local association should remain.
+ * Hence, we inherit the sourceLocal where the resultLocal is null.
+ */
+
+ LocalItem newLocal
+ = (resultLocal == null) ? sourceLocal : resultLocal;
+ LocalItem associatedLocal = getLocalForNewReg(ssaSourceReg);
+
+ /*
+ * If we take the new local, will only one local have ever
+ * been associated with this SSA reg?
+ */
+ boolean onlyOneAssociatedLocal
+ = associatedLocal == null || newLocal == null
+ || newLocal.equals(associatedLocal);
+
+ /*
+ * If we're going to copy-propogate, then the ssa register
+ * spec that's going to go into the mapping is made up of
+ * the source register number mapped from above, the type
+ * of the result, and the name either from the result (if
+ * specified) or inherited from the existing mapping.
+ *
+ * The move source has incomplete type information in null
+ * object cases, so the result type is used.
+ */
+ RegisterSpec ssaReg
+ = RegisterSpec.makeLocalOptional(
+ ssaSourceReg, ropResult.getType(), newLocal);
+
+ if (!Optimizer.getPreserveLocals() || (onlyOneAssociatedLocal
+ && equalsHandlesNulls(newLocal, sourceLocal)) &&
+ threshold == 0) {
+ /*
+ * We don't have to keep this move to preserve local
+ * information. Either the name is the same, or the result
+ * register spec is unnamed.
+ */
+
+ addMapping(ropResultReg, ssaReg);
+ } else if (onlyOneAssociatedLocal && sourceLocal == null &&
+ threshold == 0) {
+ /*
+ * The register was previously unnamed. This means that a
+ * local starts after it's first assignment in SSA form
+ */
+
+ RegisterSpecList ssaSources = RegisterSpecList.make(
+ RegisterSpec.make(ssaReg.getReg(),
+ ssaReg.getType(), newLocal));
+
+ SsaInsn newInsn
+ = SsaInsn.makeFromRop(
+ new PlainInsn(Rops.opMarkLocal(ssaReg),
+ SourcePosition.NO_INFO, null, ssaSources),block);
+
+ insnsToReplace.put(insn, newInsn);
+
+ // Just map as above.
+ addMapping(ropResultReg, ssaReg);
+ } else {
+ /*
+ * Do not copy-propogate, since the two registers have
+ * two different local-variable names.
+ */
+ processResultReg(insn);
+
+ movesToKeep.add(insn);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * All insns that are not move or phi insns have their source registers
+ * mapped ot the current mapping. Their result registers are then
+ * renamed to a new SSA register which is then added to the current
+ * register mapping.
+ */
+ public void visitNonMoveInsn(NormalSsaInsn insn) {
+ /* for each use of some variable X in S */
+ insn.mapSourceRegisters(mapper);
+
+ processResultReg(insn);
+ }
+
+ /**
+ * Renames the result register of this insn and updates the
+ * current register mapping. Does nothing if this insn has no result.
+ * Applied to all non-move insns.
+ *
+ * @param insn insn to process.
+ */
+ void processResultReg(SsaInsn insn) {
+ RegisterSpec ropResult = insn.getResult();
+
+ if (ropResult == null) {
+ return;
+ }
+
+ int ropReg = ropResult.getReg();
+ if (isBelowThresholdRegister(ropReg)) {
+ return;
+ }
+
+ insn.changeResultReg(nextSsaReg);
+ addMapping(ropReg, insn.getResult());
+
+ if (DEBUG) {
+ ssaRegToRopReg.add(ropReg);
+ }
+
+ nextSsaReg++;
+ }
+
+ /**
+ * Updates the phi insns in successor blocks with operands based
+ * on the current mapping of the rop register the phis represent.
+ */
+ private void updateSuccessorPhis() {
+ PhiInsn.Visitor visitor = new PhiInsn.Visitor() {
+ public void visitPhiInsn (PhiInsn insn) {
+ int ropReg;
+
+ ropReg = insn.getRopResultReg();
+ if (isBelowThresholdRegister(ropReg)) {
+ return;
+ }
+
+ /*
+ * Never add a version 0 register as a phi
+ * operand. Version 0 registers represent the
+ * initial register state, and thus are never
+ * significant. Furthermore, the register liveness
+ * algorithm doesn't properly count them as "live
+ * in" at the beginning of the method.
+ */
+
+ RegisterSpec stackTop = currentMapping[ropReg];
+ if (!isVersionZeroRegister(stackTop.getReg())) {
+ insn.addPhiOperand(stackTop, block);
+ }
+ }
+ };
+
+ BitSet successors = block.getSuccessors();
+ for (int i = successors.nextSetBit(0); i >= 0;
+ i = successors.nextSetBit(i + 1)) {
+ SsaBasicBlock successor = ssaMeth.getBlocks().get(i);
+ successor.forEachPhiInsn(visitor);
+ }
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/ssa/_tests/_DomFront.java b/dx/src/com/android/dx/ssa/_tests/_DomFront.java
new file mode 100644
index 0000000..3d891c9
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/_tests/_DomFront.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa._tests;
+
+import junit.framework.TestCase;
+
+/**
+ * Test the class {@code com.android.dx.ssa.DomFront}.
+ */
+public class _DomFront
+ extends TestCase {
+
+ public void test_one() {
+
+ }
+
+
+}
diff --git a/dx/src/com/android/dx/ssa/back/FirstFitAllocator.java b/dx/src/com/android/dx/ssa/back/FirstFitAllocator.java
new file mode 100644
index 0000000..6416e84
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/back/FirstFitAllocator.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa.back;
+
+import com.android.dx.rop.code.CstInsn;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.ssa.NormalSsaInsn;
+import com.android.dx.ssa.BasicRegisterMapper;
+import com.android.dx.ssa.RegisterMapper;
+import com.android.dx.ssa.SsaMethod;
+import com.android.dx.util.IntSet;
+import com.android.dx.util.BitIntSet;
+
+import java.util.BitSet;
+import java.util.ArrayList;
+
+/**
+ * Allocates registers via a naive n^2 register allocator.
+ * This allocator does not try to co-locate local variables or deal
+ * intelligently with different size register uses.
+ */
+public class FirstFitAllocator extends RegisterAllocator {
+ /**
+ * If true, allocator places parameters at the top of the frame
+ * in calling-convention order.
+ */
+ private static final boolean PRESLOT_PARAMS = true;
+
+ /** indexed by old reg; the set of old regs we've mapped */
+ private final BitSet mapped;
+
+ /** {@inheritDoc} */
+ public FirstFitAllocator(
+ final SsaMethod ssaMeth, final InterferenceGraph interference) {
+ super(ssaMeth, interference);
+
+ mapped = new BitSet(ssaMeth.getRegCount());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean wantsParamsMovedHigh() {
+ return PRESLOT_PARAMS;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RegisterMapper allocateRegisters() {
+ int oldRegCount = ssaMeth.getRegCount();
+
+ BasicRegisterMapper mapper
+ = new BasicRegisterMapper(oldRegCount);
+
+ int nextNewRegister = 0;
+
+ if (PRESLOT_PARAMS) {
+ /*
+ * Reserve space for the params at the bottom of the register
+ * space. Later, we'll flip the params to the end of the register
+ * space.
+ */
+
+ nextNewRegister = ssaMeth.getParamWidth();
+ }
+
+ for (int i = 0; i < oldRegCount; i++) {
+ if (mapped.get(i)) {
+ // we already got this one
+ continue;
+ }
+
+ int maxCategory = getCategoryForSsaReg(i);
+ IntSet current = new BitIntSet(oldRegCount);
+
+ interference.mergeInterferenceSet(i, current);
+
+ boolean isPreslotted = false;
+ int newReg = 0;
+
+ if (PRESLOT_PARAMS && isDefinitionMoveParam(i)) {
+ // Any move-param definition must be a NormalSsaInsn
+ NormalSsaInsn defInsn = (NormalSsaInsn)
+ ssaMeth.getDefinitionForRegister(i);
+
+ newReg = paramNumberFromMoveParam(defInsn);
+
+ mapper.addMapping(i, newReg, maxCategory);
+ isPreslotted = true;
+ } else {
+ mapper.addMapping(i, nextNewRegister, maxCategory);
+ newReg = nextNewRegister;
+ }
+
+ for (int j = i + 1; j < oldRegCount; j++) {
+ if (mapped.get(j) || isDefinitionMoveParam(j)) {
+ continue;
+ }
+
+ /*
+ * If reg j doesn't interfere with the current mapping.
+ * Also, if this is a pre-slotted method parameter, we
+ * can't use more than the original param width.
+ */
+ if (!current.has(j)
+ && !(isPreslotted
+ && (maxCategory < getCategoryForSsaReg(j)))) {
+
+ interference.mergeInterferenceSet(j, current);
+
+ maxCategory = Math.max(maxCategory,
+ getCategoryForSsaReg(j));
+
+ mapper.addMapping(j, newReg, maxCategory);
+ mapped.set(j);
+ }
+ }
+
+ mapped.set(i);
+ if (!isPreslotted) {
+ nextNewRegister += maxCategory;
+ }
+ }
+
+ return mapper;
+ }
+
+ /**
+ * Returns the parameter number that this move-param insn refers to
+ * @param ndefInsn a move-param insn (otherwise, exceptions will be thrown)
+ * @return parameter number (offset in the total parameter width)
+ */
+ private int paramNumberFromMoveParam(NormalSsaInsn ndefInsn) {
+ CstInsn origInsn = (CstInsn) ndefInsn.getOriginalRopInsn();
+
+ return ((CstInteger) origInsn.getConstant()).getValue();
+ }
+}
diff --git a/dx/src/com/android/dx/ssa/back/FirstFitLocalCombiningAllocator.java b/dx/src/com/android/dx/ssa/back/FirstFitLocalCombiningAllocator.java
new file mode 100644
index 0000000..0cffcfa
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/back/FirstFitLocalCombiningAllocator.java
@@ -0,0 +1,958 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa.back;
+
+import com.android.dx.rop.code.*;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.ssa.InterferenceRegisterMapper;
+import com.android.dx.ssa.RegisterMapper;
+import com.android.dx.ssa.SsaInsn;
+import com.android.dx.ssa.SsaMethod;
+import com.android.dx.ssa.NormalSsaInsn;
+import com.android.dx.ssa.PhiInsn;
+import com.android.dx.ssa.Optimizer;
+import com.android.dx.ssa.SsaBasicBlock;
+import com.android.dx.util.IntSet;
+import com.android.dx.util.IntIterator;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Allocates registers in a first-fit fashion, with the bottom reserved for
+ * method parameters and all SSAregisters representing the same local variable
+ * kept together if possible.
+ */
+public class FirstFitLocalCombiningAllocator extends RegisterAllocator {
+ /** local debug flag */
+ private static final boolean DEBUG = false;
+
+ /** maps local variable to a list of associated SSA registers */
+ private final Map<LocalItem, ArrayList<RegisterSpec>> localVariables;
+
+ /** list of move-result-pesudo instructions seen in this method */
+ private final ArrayList<NormalSsaInsn> moveResultPseudoInsns;
+
+ /** list of invoke-range instructions seen in this method */
+ private final ArrayList<NormalSsaInsn> invokeRangeInsns;
+
+ /** indexed by SSA reg; the set of SSA regs we've mapped */
+ private final BitSet ssaRegsMapped;
+
+ /** Register mapper which will be our result */
+ private final InterferenceRegisterMapper mapper;
+
+ /** end of rop registers range (starting at 0) reserved for parameters */
+ private final int paramRangeEnd;
+
+ /** set of rop registers reserved for parameters or local variables */
+ private final BitSet reservedRopRegs;
+
+ /** set of rop registers that have been used by anything */
+ private final BitSet usedRopRegs;
+
+ /** true if converter should take steps to minimize rop-form registers */
+ private final boolean minimizeRegisters;
+
+ /**
+ * Constructs instance.
+ *
+ * @param ssaMeth {@code non-null;} method to process
+ * @param interference non-null interference graph for SSA registers
+ * @param minimizeRegisters true if converter should take steps to
+ * minimize rop-form registers
+ */
+ public FirstFitLocalCombiningAllocator(
+ SsaMethod ssaMeth, InterferenceGraph interference,
+ boolean minimizeRegisters) {
+ super(ssaMeth, interference);
+
+ ssaRegsMapped = new BitSet(ssaMeth.getRegCount());
+
+ mapper = new InterferenceRegisterMapper(
+ interference, ssaMeth.getRegCount());
+
+ this.minimizeRegisters = minimizeRegisters;
+
+ /*
+ * Reserve space for the params at the bottom of the register
+ * space. Later, we'll flip the params to the end of the register
+ * space.
+ */
+
+ paramRangeEnd = ssaMeth.getParamWidth();
+
+ reservedRopRegs = new BitSet(paramRangeEnd * 2);
+ reservedRopRegs.set(0, paramRangeEnd);
+ usedRopRegs = new BitSet(paramRangeEnd * 2);
+ localVariables = new TreeMap<LocalItem, ArrayList<RegisterSpec>>();
+ moveResultPseudoInsns = new ArrayList<NormalSsaInsn>();
+ invokeRangeInsns = new ArrayList<NormalSsaInsn>();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean wantsParamsMovedHigh() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RegisterMapper allocateRegisters() {
+
+ analyzeInstructions();
+
+ if (DEBUG) {
+ printLocalVars();
+ }
+
+ if (DEBUG) System.out.println("--->Mapping local-associated params");
+ handleLocalAssociatedParams();
+
+ if (DEBUG) System.out.println("--->Mapping other params");
+ handleUnassociatedParameters();
+
+ if (DEBUG) System.out.println("--->Mapping invoke-range");
+ handleInvokeRangeInsns();
+
+ if (DEBUG) {
+ System.out.println("--->Mapping local-associated non-params");
+ }
+ handleLocalAssociatedOther();
+
+ if (DEBUG) System.out.println("--->Mapping check-cast results");
+ handleCheckCastResults();
+
+ if (DEBUG) System.out.println("--->Mapping others");
+ handleNormalUnassociated();
+
+ return mapper;
+ }
+
+ /**
+ * Dumps local variable table to stdout for debugging.
+ */
+ private void printLocalVars() {
+ System.out.println("Printing local vars");
+ for (Map.Entry<LocalItem, ArrayList<RegisterSpec>> e :
+ localVariables.entrySet()) {
+ StringBuilder regs = new StringBuilder();
+
+ regs.append('{');
+ regs.append(' ');
+ for (RegisterSpec reg : e.getValue()) {
+ regs.append('v');
+ regs.append(reg.getReg());
+ regs.append(' ');
+ }
+ regs.append('}');
+ System.out.printf("Local: %s Registers: %s\n", e.getKey(), regs);
+ }
+ }
+
+ /**
+ * Maps all local-associated parameters to rop registers.
+ */
+ private void handleLocalAssociatedParams() {
+ for (ArrayList<RegisterSpec> ssaRegs : localVariables.values()) {
+ int sz = ssaRegs.size();
+ int paramIndex = -1;
+ int paramCategory = 0;
+
+ // First, find out if this local variable is a parameter.
+ for (int i = 0; i < sz; i++) {
+ RegisterSpec ssaSpec = ssaRegs.get(i);
+ int ssaReg = ssaSpec.getReg();
+
+ paramIndex = getParameterIndexForReg(ssaReg);
+
+ if (paramIndex >= 0) {
+ paramCategory = ssaSpec.getCategory();
+ addMapping(ssaSpec, paramIndex);
+ break;
+ }
+ }
+
+ if (paramIndex < 0) {
+ // This local wasn't a parameter.
+ continue;
+ }
+
+ // Any remaining local-associated registers will be mapped later.
+ tryMapRegs(ssaRegs, paramIndex, paramCategory, true);
+ }
+ }
+
+ /**
+ * Gets the parameter index for SSA registers that are method parameters.
+ * {@code -1} is returned for non-parameter registers.
+ *
+ * @param ssaReg {@code >=0;} SSA register to look up
+ * @return parameter index or {@code -1} if not a parameter
+ */
+ private int getParameterIndexForReg(int ssaReg) {
+ SsaInsn defInsn = ssaMeth.getDefinitionForRegister(ssaReg);
+ if (defInsn == null) {
+ return -1;
+ }
+
+ Rop opcode = defInsn.getOpcode();
+
+ // opcode == null for phi insns.
+ if (opcode != null && opcode.getOpcode() == RegOps.MOVE_PARAM) {
+ CstInsn origInsn = (CstInsn) defInsn.getOriginalRopInsn();
+ return ((CstInteger) origInsn.getConstant()).getValue();
+ }
+
+ return -1;
+ }
+
+ /**
+ * Maps all local-associated registers that are not parameters.
+ * Tries to find an unreserved range that's wide enough for all of
+ * the SSA registers, and then tries to map them all to that
+ * range. If not all fit, a new range is tried until all registers
+ * have been fit.
+ */
+ private void handleLocalAssociatedOther() {
+ for (ArrayList<RegisterSpec> specs : localVariables.values()) {
+ int ropReg = 0;
+
+ boolean done;
+ do {
+ int maxCategory = 1;
+
+ // Compute max category for remaining unmapped registers.
+ int sz = specs.size();
+ for (int i = 0; i < sz; i++) {
+ RegisterSpec ssaSpec = specs.get(i);
+ int category = ssaSpec.getCategory();
+ if (!ssaRegsMapped.get(ssaSpec.getReg())
+ && category > maxCategory) {
+ maxCategory = category;
+ }
+ }
+
+ ropReg = findRopRegForLocal(ropReg, maxCategory);
+
+ done = tryMapRegs(specs, ropReg, maxCategory, true);
+
+ // Increment for next call to findNext.
+ ropReg++;
+ } while (!done);
+ }
+ }
+
+ /**
+ * Tries to map a list of SSA registers into the a rop reg, marking
+ * used rop space as reserved. SSA registers that don't fit are left
+ * unmapped.
+ *
+ * @param specs {@code non-null;} SSA registers to attempt to map
+ * @param ropReg {@code >=0;} rop register to map to
+ * @param maxAllowedCategory {@code 1..2;} maximum category
+ * allowed in mapping.
+ * @param markReserved do so if {@code true}
+ * @return {@code true} if all registers were mapped, {@code false}
+ * if some remain unmapped
+ */
+ private boolean tryMapRegs(
+ ArrayList<RegisterSpec> specs, int ropReg,
+ int maxAllowedCategory, boolean markReserved) {
+ boolean remaining = false;
+ for (RegisterSpec spec : specs) {
+ if (ssaRegsMapped.get(spec.getReg())) {
+ continue;
+ }
+
+ boolean succeeded;
+ succeeded = tryMapReg(spec, ropReg, maxAllowedCategory);
+ remaining = !succeeded || remaining;
+ if (succeeded && markReserved) {
+ // This only needs to be called once really with
+ // the widest category used, but <shrug>
+ markReserved(ropReg, spec.getCategory());
+ }
+ }
+ return !remaining;
+ }
+
+ /**
+ * Tries to map an SSA register to a rop register.
+ *
+ * @param ssaSpec {@code non-null;} SSA register
+ * @param ropReg {@code >=0;} rop register
+ * @param maxAllowedCategory {@code 1..2;} the maximum category
+ * that the SSA register is allowed to be
+ * @return {@code true} if map succeeded, {@code false} if not
+ */
+ private boolean tryMapReg(RegisterSpec ssaSpec, int ropReg,
+ int maxAllowedCategory) {
+ if (ssaSpec.getCategory() <= maxAllowedCategory
+ && !ssaRegsMapped.get(ssaSpec.getReg())
+ && canMapReg(ssaSpec, ropReg)) {
+ addMapping(ssaSpec, ropReg);
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Marks a range of rop registers as "reserved for a local variable."
+ *
+ * @param ropReg {@code >= 0;} rop register to reserve
+ * @param category {@code > 0;} width to reserve
+ */
+ private void markReserved(int ropReg, int category) {
+ reservedRopRegs.set(ropReg, ropReg + category, true);
+ }
+
+ /**
+ * Checks to see if any rop registers in the specified range are reserved
+ * for local variables or parameters.
+ *
+ * @param ropRangeStart {@code >= 0;} lowest rop register
+ * @param width {@code > 0;} number of rop registers in range.
+ * @return {@code true} if any register in range is marked reserved
+ */
+ private boolean rangeContainsReserved(int ropRangeStart, int width) {
+ for (int i = ropRangeStart; i < (ropRangeStart + width); i++) {
+ if (reservedRopRegs.get(i)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if given rop register represents the {@code this} pointer
+ * for a non-static method.
+ *
+ * @param startReg rop register
+ * @return true if the "this" pointer is located here.
+ */
+ private boolean isThisPointerReg(int startReg) {
+ // "this" is always the first parameter.
+ return startReg == 0 && !ssaMeth.isStatic();
+ }
+
+ /**
+ * Finds a range of unreserved rop registers.
+ *
+ * @param startReg {@code >= 0;} a rop register to start the search at
+ * @param width {@code > 0;} the width, in registers, required.
+ * @return {@code >= 0;} start of available register range.
+ */
+ private int findNextUnreservedRopReg(int startReg, int width) {
+ if (minimizeRegisters && !isThisPointerReg(startReg)) {
+ return startReg;
+ }
+
+ int reg;
+
+ reg = reservedRopRegs.nextClearBit(startReg);
+
+ while (true) {
+ int i = 1;
+
+ while (i < width && !reservedRopRegs.get(reg + i)) {
+ i++;
+ }
+
+ if (i == width) {
+ return reg;
+ }
+
+ reg = reservedRopRegs.nextClearBit(reg + i);
+ }
+ }
+
+ /**
+ * Finds a range of rop regs that can be used for local variables.
+ * If {@code MIX_LOCALS_AND_OTHER} is {@code false}, this means any
+ * rop register that has not yet been used.
+ *
+ * @param startReg {@code >= 0;} a rop register to start the search at
+ * @param width {@code > 0;} the width, in registers, required.
+ * @return {@code >= 0;} start of available register range.
+ */
+ private int findRopRegForLocal(int startReg, int width) {
+ if (minimizeRegisters && !isThisPointerReg(startReg)) {
+ return startReg;
+ }
+
+ int reg;
+
+ reg = usedRopRegs.nextClearBit(startReg);
+
+ while (true) {
+ int i = 1;
+
+ while (i < width && !usedRopRegs.get(reg + i)) {
+ i++;
+ }
+
+ if (i == width) {
+ return reg;
+ }
+
+ reg = usedRopRegs.nextClearBit(reg + i);
+ }
+ }
+
+ /**
+ * Maps any parameter that isn't local-associated, which can happen
+ * in the case where there is no java debug info.
+ */
+ private void handleUnassociatedParameters() {
+ int szSsaRegs = ssaMeth.getRegCount();
+
+ for (int ssaReg = 0; ssaReg < szSsaRegs; ssaReg++) {
+ if (ssaRegsMapped.get(ssaReg)) {
+ // We already did this one above
+ continue;
+ }
+
+ int paramIndex = getParameterIndexForReg(ssaReg);
+
+ RegisterSpec ssaSpec = getDefinitionSpecForSsaReg(ssaReg);
+ if (paramIndex >= 0) {
+ addMapping(ssaSpec, paramIndex);
+ }
+ }
+ }
+
+ /**
+ * Handles all insns that want a register range for their sources.
+ */
+ private void handleInvokeRangeInsns() {
+ for (NormalSsaInsn insn : invokeRangeInsns) {
+ adjustAndMapSourceRangeRange(insn);
+ }
+ }
+
+ /**
+ * Handles check cast results to reuse the same source register if
+ * possible.
+ */
+ private void handleCheckCastResults() {
+ for (NormalSsaInsn insn : moveResultPseudoInsns) {
+ RegisterSpec moveRegSpec = insn.getResult();
+ int moveReg = moveRegSpec.getReg();
+ BitSet predBlocks = insn.getBlock().getPredecessors();
+
+ // Expect one predecessor block only
+ if (predBlocks.cardinality() != 1) {
+ continue;
+ }
+
+ SsaBasicBlock predBlock =
+ ssaMeth.getBlocks().get(predBlocks.nextSetBit(0));
+ ArrayList<SsaInsn> insnList = predBlock.getInsns();
+
+ /**
+ * If the predecessor block has a check-cast, it will be the last
+ * instruction
+ */
+ SsaInsn checkCastInsn = insnList.get(insnList.size() - 1);
+ if (checkCastInsn.getOpcode().getOpcode() != RegOps.CHECK_CAST) {
+ continue;
+ }
+
+ RegisterSpec checkRegSpec = checkCastInsn.getSources().get(0);
+ int checkReg = checkRegSpec.getReg();
+
+ // Assume none of the register is mapped yet
+ int ropReg = 0;
+
+ /**
+ * See if either register is already mapped. Most likely the move
+ * result will be mapped already since the cast result is stored
+ * in a local variable.
+ */
+ if (ssaRegsMapped.get(moveReg)) {
+ ropReg = mapper.oldToNew(moveReg);
+ } else if (ssaRegsMapped.get(checkReg)) {
+ ropReg = mapper.oldToNew(checkReg);
+ }
+
+ ArrayList<RegisterSpec> ssaRegs = new ArrayList<RegisterSpec>(2);
+ ssaRegs.add(moveRegSpec);
+ ssaRegs.add(checkRegSpec);
+ int category = checkRegSpec.getCategory();
+
+ while (!tryMapRegs(ssaRegs, ropReg, category, false)) {
+ ropReg = findNextUnreservedRopReg(ropReg + 1, category);
+ }
+ }
+ }
+
+ /**
+ * Maps all non-parameter, non-local variable registers.
+ */
+ private void handleNormalUnassociated() {
+ int szSsaRegs = ssaMeth.getRegCount();
+
+ for (int ssaReg = 0; ssaReg < szSsaRegs; ssaReg++) {
+ if (ssaRegsMapped.get(ssaReg)) {
+ // We already did this one
+ continue;
+ }
+
+ RegisterSpec ssaSpec = getDefinitionSpecForSsaReg(ssaReg);
+
+ if (ssaSpec == null) continue;
+
+ int category = ssaSpec.getCategory();
+ // Find a rop reg that does not interfere
+ int ropReg = findNextUnreservedRopReg(0, category);
+ while (!canMapReg(ssaSpec, ropReg)) {
+ ropReg = findNextUnreservedRopReg(ropReg + 1, category);
+ }
+
+ addMapping(ssaSpec, ropReg);
+ }
+ }
+
+ /**
+ * Checks to see if {@code ssaSpec} can be mapped to
+ * {@code ropReg}. Checks interference graph and ensures
+ * the range does not cross the parameter range.
+ *
+ * @param ssaSpec {@code non-null;} SSA spec
+ * @param ropReg prosepctive new-namespace reg
+ * @return {@code true} if mapping is possible
+ */
+ private boolean canMapReg(RegisterSpec ssaSpec, int ropReg) {
+ int category = ssaSpec.getCategory();
+ return !(spansParamRange(ropReg, category)
+ || mapper.interferes(ssaSpec, ropReg));
+ }
+
+ /**
+ * Returns true if the specified rop register + category
+ * will cross the boundry between the lower {@code paramWidth}
+ * registers reserved for method params and the upper registers. We cannot
+ * allocate a register that spans the param block and the normal block,
+ * because we will be moving the param block to high registers later.
+ *
+ * @param ssaReg register in new namespace
+ * @param category width that the register will have
+ * @return {@code true} in the case noted above
+ */
+ private boolean spansParamRange(int ssaReg, int category) {
+ return ((ssaReg < paramRangeEnd)
+ && ((ssaReg + category) > paramRangeEnd));
+ }
+
+ /**
+ * Analyze each instruction and find out all the local variable assignments
+ * and move-result-pseudo/invoke-range instrucitons.
+ */
+ private void analyzeInstructions() {
+ ssaMeth.forEachInsn(new SsaInsn.Visitor() {
+ /** {@inheritDoc} */
+ public void visitMoveInsn(NormalSsaInsn insn) {
+ processInsn(insn);
+ }
+
+ /** {@inheritDoc} */
+ public void visitPhiInsn(PhiInsn insn) {
+ processInsn(insn);
+ }
+
+ /** {@inheritDoc} */
+ public void visitNonMoveInsn(NormalSsaInsn insn) {
+ processInsn(insn);
+ }
+
+ /**
+ * This method collects three types of instructions:
+ *
+ * 1) Adds a local variable assignment to the
+ * {@code localVariables} map.
+ * 2) Add move-result-pseudo to the
+ * {@code moveResultPseudoInsns} list.
+ * 3) Add invoke-range to the
+ * {@code invokeRangeInsns} list.
+ *
+ * @param insn {@code non-null;} insn that may represent a
+ * local variable assignment
+ */
+ private void processInsn(SsaInsn insn) {
+ RegisterSpec assignment;
+ assignment = insn.getLocalAssignment();
+
+ if (assignment != null) {
+ LocalItem local = assignment.getLocalItem();
+
+ ArrayList<RegisterSpec> regList
+ = localVariables.get(local);
+
+ if (regList == null) {
+ regList = new ArrayList<RegisterSpec>();
+ localVariables.put(local, regList);
+ }
+
+ regList.add(assignment);
+ }
+
+ if (insn instanceof NormalSsaInsn) {
+ if (insn.getOpcode().getOpcode() ==
+ RegOps.MOVE_RESULT_PSEUDO) {
+ moveResultPseudoInsns.add((NormalSsaInsn) insn);
+ } else if (Optimizer.getAdvice().requiresSourcesInOrder(
+ insn.getOriginalRopInsn().getOpcode(),
+ insn.getSources())) {
+ invokeRangeInsns.add((NormalSsaInsn) insn);
+ }
+ }
+
+ }
+ });
+ }
+
+ /**
+ * Adds a mapping from an SSA register to a rop register.
+ * {@link #canMapReg} should have already been called.
+ *
+ * @param ssaSpec {@code non-null;} SSA register to map from
+ * @param ropReg {@code >=0;} rop register to map to
+ */
+ private void addMapping(RegisterSpec ssaSpec, int ropReg) {
+ int ssaReg = ssaSpec.getReg();
+
+ // An assertion.
+ if (ssaRegsMapped.get(ssaReg) || !canMapReg(ssaSpec, ropReg)) {
+ throw new RuntimeException(
+ "attempt to add invalid register mapping");
+ }
+
+ if (DEBUG) {
+ System.out.printf("Add mapping s%d -> v%d c:%d\n",
+ ssaSpec.getReg(), ropReg, ssaSpec.getCategory());
+ }
+
+ int category = ssaSpec.getCategory();
+ mapper.addMapping(ssaSpec.getReg(), ropReg, category);
+ ssaRegsMapped.set(ssaReg);
+ usedRopRegs.set(ropReg, ropReg + category);
+ }
+
+
+ /**
+ * Maps the source registers of the specified instruction such that they
+ * will fall in a contiguous range in rop form. Moves are inserted as
+ * necessary to allow the range to be allocated.
+ *
+ * @param insn {@code non-null;} insn whos sources to process
+ */
+ private void adjustAndMapSourceRangeRange(NormalSsaInsn insn) {
+ int newRegStart = findRangeAndAdjust(insn);
+
+ RegisterSpecList sources = insn.getSources();
+ int szSources = sources.size();
+ int nextRopReg = newRegStart;
+
+ for (int i = 0; i < szSources; i++) {
+ RegisterSpec source = sources.get(i);
+ int sourceReg = source.getReg();
+ int category = source.getCategory();
+ int curRopReg = nextRopReg;
+ nextRopReg += category;
+
+ if (ssaRegsMapped.get(sourceReg)) {
+ continue;
+ }
+
+ LocalItem localItem = getLocalItemForReg(sourceReg);
+ addMapping(source, curRopReg);
+
+ if (localItem != null) {
+ markReserved(curRopReg, category);
+ ArrayList<RegisterSpec> similarRegisters
+ = localVariables.get(localItem);
+
+ int szSimilar = similarRegisters.size();
+
+ /*
+ * Try to map all SSA registers also associated with
+ * this local.
+ */
+ for (int j = 0; j < szSimilar; j++) {
+ RegisterSpec similarSpec = similarRegisters.get(j);
+ int similarReg = similarSpec.getReg();
+
+ // Don't map anything that's also a source.
+ if (-1 != sources.indexOfRegister(similarReg)) {
+ continue;
+ }
+
+ // Registers left unmapped will get handled later.
+ tryMapReg(similarSpec, curRopReg, category);
+ }
+ }
+ }
+ }
+
+ /**
+ * Find a contiguous rop register range that fits the specified
+ * instruction's sources. First, try to center the range around
+ * sources that have already been mapped to rop registers. If that fails,
+ * just find a new contiguous range that doesn't interfere.
+ *
+ * @param insn {@code non-null;} the insn whose sources need to
+ * fit. Must be last insn in basic block.
+ * @return {@code >= 0;} rop register of start of range
+ */
+ private int findRangeAndAdjust(NormalSsaInsn insn) {
+ RegisterSpecList sources = insn.getSources();
+ int szSources = sources.size();
+ // the category for each source index
+ int categoriesForIndex[] = new int[szSources];
+ int rangeLength = 0;
+
+ // Compute rangeLength and categoriesForIndex
+ for (int i = 0; i < szSources; i++) {
+ int category = sources.get(i).getCategory();
+ categoriesForIndex[i] = category;
+ rangeLength += categoriesForIndex[i];
+ }
+
+ // the highest score of fits tried so far
+ int maxScore = Integer.MIN_VALUE;
+ // the high scoring range's start
+ int resultRangeStart = -1;
+ // by source index: set of sources needing moves in high scoring plan
+ BitSet resultMovesRequired = null;
+
+ /*
+ * First, go through each source that's already been mapped. Try
+ * to center the range around the rop register this source is mapped
+ * to.
+ */
+ int rangeStartOffset = 0;
+ for (int i = 0; i < szSources; i++) {
+ int ssaCenterReg = sources.get(i).getReg();
+
+ if (i != 0) {
+ rangeStartOffset -= categoriesForIndex[i - 1];
+ }
+ if (!ssaRegsMapped.get(ssaCenterReg)) {
+ continue;
+ }
+
+ int rangeStart = mapper.oldToNew(ssaCenterReg) + rangeStartOffset;
+
+ if (rangeStart < 0 || spansParamRange(rangeStart, rangeLength)) {
+ continue;
+ }
+
+ BitSet curMovesRequired = new BitSet(szSources);
+
+ int fitWidth
+ = fitPlanForRange(rangeStart, insn, categoriesForIndex,
+ curMovesRequired);
+
+ if (fitWidth < 0) {
+ continue;
+ }
+
+ int score = fitWidth - curMovesRequired.cardinality();
+
+ if (score > maxScore) {
+ maxScore = score;
+ resultRangeStart = rangeStart;
+ resultMovesRequired = curMovesRequired;
+ }
+
+ if (fitWidth == rangeLength) {
+ // We can't do any better than this, so stop here
+ break;
+ }
+ }
+
+ /*
+ * If we were unable to find a plan for a fit centered around
+ * an already-mapped source, just try to find a range of
+ * registers we can move the range into.
+ */
+
+ if (resultRangeStart == -1) {
+ resultMovesRequired = new BitSet(szSources);
+
+ resultRangeStart = findAnyFittingRange(insn, rangeLength,
+ categoriesForIndex, resultMovesRequired);
+ }
+
+ /*
+ * Now, insert any moves required.
+ */
+
+ for (int i = resultMovesRequired.nextSetBit(0); i >= 0;
+ i = resultMovesRequired.nextSetBit(i+1)) {
+ insn.changeOneSource(i, insertMoveBefore(insn, sources.get(i)));
+ }
+
+ return resultRangeStart;
+ }
+
+ /**
+ * Finds an unreserved range that will fit the sources of the
+ * specified instruction. Does not bother trying to center the range
+ * around an already-mapped source register;
+ *
+ * @param insn {@code non-null;} insn to build range for
+ * @param rangeLength {@code >=0;} length required in register units
+ * @param categoriesForIndex {@code non-null;} indexed by source index;
+ * the category for each source
+ * @param outMovesRequired {@code non-null;} an output parameter indexed by
+ * source index that will contain the set of sources which need
+ * moves inserted
+ * @return the rop register that starts the fitting range
+ */
+ private int findAnyFittingRange(NormalSsaInsn insn, int rangeLength,
+ int[] categoriesForIndex, BitSet outMovesRequired) {
+ int rangeStart = 0;
+ while (true) {
+ rangeStart = findNextUnreservedRopReg(rangeStart, rangeLength);
+ int fitWidth
+ = fitPlanForRange(rangeStart, insn,
+ categoriesForIndex, outMovesRequired);
+
+ if (fitWidth >= 0) {
+ break;
+ }
+ rangeStart++;
+ outMovesRequired.clear();
+ }
+ return rangeStart;
+ }
+
+ /**
+ * Attempts to build a plan for fitting a range of sources into rop
+ * registers.
+ *
+ * @param ropReg {@code >= 0;} rop reg that begins range
+ * @param insn {@code non-null;} insn to plan range for
+ * @param categoriesForIndex {@code non-null;} indexed by source index;
+ * the category for each source
+ * @param outMovesRequired {@code non-null;} an output parameter indexed by
+ * source index that will contain the set of sources which need
+ * moves inserted
+ * @return the width of the fit that that does not involve added moves or
+ * {@code -1} if "no fit possible"
+ */
+ private int fitPlanForRange(int ropReg, NormalSsaInsn insn,
+ int[] categoriesForIndex, BitSet outMovesRequired) {
+ RegisterSpecList sources = insn.getSources();
+ int szSources = sources.size();
+ int fitWidth = 0;
+ IntSet liveOut = insn.getBlock().getLiveOutRegs();
+ RegisterSpecList liveOutSpecs = ssaSetToSpecs(liveOut);
+
+ // An SSA reg may only be mapped into a range once.
+ BitSet seen = new BitSet(ssaMeth.getRegCount());
+
+ for (int i = 0; i < szSources ; i++) {
+ RegisterSpec ssaSpec = sources.get(i);
+ int ssaReg = ssaSpec.getReg();
+ int category = categoriesForIndex[i];
+
+ if (i != 0) {
+ ropReg += categoriesForIndex[i-1];
+ }
+
+ if (ssaRegsMapped.get(ssaReg)
+ && mapper.oldToNew(ssaReg) == ropReg) {
+ // This is a register that is already mapped appropriately.
+ fitWidth += category;
+ } else if (rangeContainsReserved(ropReg, category)) {
+ fitWidth = -1;
+ break;
+ } else if (!ssaRegsMapped.get(ssaReg)
+ && canMapReg(ssaSpec, ropReg)
+ && !seen.get(ssaReg)) {
+ // This is a register that can be mapped appropriately.
+ fitWidth += category;
+ } else if (!mapper.areAnyPinned(liveOutSpecs, ropReg, category)
+ && !mapper.areAnyPinned(sources, ropReg, category)) {
+ /*
+ * This is a source that can be moved. We can insert a
+ * move as long as:
+ *
+ * * no SSA register pinned to the desired rop reg
+ * is live out on the block
+ *
+ * * no SSA register pinned to desired rop reg is
+ * a source of this insn (since this may require
+ * overlapping moves, which we can't presently handle)
+ */
+
+ outMovesRequired.set(i);
+ } else {
+ fitWidth = -1;
+ break;
+ }
+
+ seen.set(ssaReg);
+ }
+ return fitWidth;
+ }
+
+ /**
+ * Converts a bit set of SSA registers into a RegisterSpecList containing
+ * the definition specs of all the registers.
+ *
+ * @param ssaSet {@code non-null;} set of SSA registers
+ * @return list of RegisterSpecs as noted above
+ */
+ RegisterSpecList ssaSetToSpecs(IntSet ssaSet) {
+ RegisterSpecList result = new RegisterSpecList(ssaSet.elements());
+
+ IntIterator iter = ssaSet.iterator();
+
+ int i = 0;
+ while (iter.hasNext()) {
+ result.set(i++, getDefinitionSpecForSsaReg(iter.next()));
+ }
+
+ return result;
+ }
+
+ /**
+ * Gets a local item associated with an ssa register, if one exists.
+ *
+ * @param ssaReg {@code >= 0;} SSA register
+ * @return {@code null-ok;} associated local item or null
+ */
+ private LocalItem getLocalItemForReg(int ssaReg) {
+ for (Map.Entry<LocalItem, ArrayList<RegisterSpec>> entry :
+ localVariables.entrySet()) {
+ for (RegisterSpec spec : entry.getValue()) {
+ if (spec.getReg() == ssaReg) {
+ return entry.getKey();
+ }
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/dx/src/com/android/dx/ssa/back/IdenticalBlockCombiner.java b/dx/src/com/android/dx/ssa/back/IdenticalBlockCombiner.java
new file mode 100644
index 0000000..515b04f
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/back/IdenticalBlockCombiner.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa.back;
+
+import com.android.dx.rop.code.BasicBlock;
+import com.android.dx.rop.code.BasicBlockList;
+import com.android.dx.rop.code.CstInsn;
+import com.android.dx.rop.code.Insn;
+import com.android.dx.rop.code.InsnList;
+import com.android.dx.rop.code.RegOps;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.code.SwitchInsn;
+import com.android.dx.util.IntList;
+
+import java.util.BitSet;
+
+/**
+ * Searches for basic blocks that all have the same successor and insns
+ * but different predecessors. These blocks are then combined into a single
+ * block and the now-unused blocks are deleted. These identical blocks
+ * frequently are created when catch blocks are edge-split.
+ */
+public class IdenticalBlockCombiner {
+ private final RopMethod ropMethod;
+ private final BasicBlockList blocks;
+ private final BasicBlockList newBlocks;
+
+ /**
+ * Constructs instance. Call {@code process()} to run.
+ *
+ * @param rm {@code non-null;} instance to process
+ */
+ public IdenticalBlockCombiner(RopMethod rm) {
+ ropMethod = rm;
+ blocks = ropMethod.getBlocks();
+ newBlocks = blocks.getMutableCopy();
+ }
+
+ /**
+ * Runs algorithm. TODO: This is n^2, and could be made linear-ish with
+ * a hash. In particular, hash the contents of each block and only
+ * compare blocks with the same hash.
+ *
+ * @return {@code non-null;} new method that has been processed
+ */
+ public RopMethod process() {
+ int szBlocks = blocks.size();
+ // indexed by label
+ BitSet toDelete = new BitSet(blocks.getMaxLabel());
+
+ // For each non-deleted block...
+ for (int bindex = 0; bindex < szBlocks; bindex++) {
+ BasicBlock b = blocks.get(bindex);
+
+ if (toDelete.get(b.getLabel())) {
+ // doomed block
+ continue;
+ }
+
+ IntList preds = ropMethod.labelToPredecessors(b.getLabel());
+
+ // ...look at all of it's predecessors that have only one succ...
+ int szPreds = preds.size();
+ for (int i = 0; i < szPreds; i++) {
+ int iLabel = preds.get(i);
+
+ BasicBlock iBlock = blocks.labelToBlock(iLabel);
+
+ if (toDelete.get(iLabel)
+ || iBlock.getSuccessors().size() > 1
+ || iBlock.getFirstInsn().getOpcode().getOpcode() ==
+ RegOps.MOVE_RESULT) {
+ continue;
+ }
+
+ IntList toCombine = new IntList();
+
+ // ...and see if they can be combined with any other preds...
+ for (int j = i + 1; j < szPreds; j++) {
+ int jLabel = preds.get(j);
+ BasicBlock jBlock = blocks.labelToBlock(jLabel);
+
+ if (jBlock.getSuccessors().size() == 1
+ && compareInsns(iBlock, jBlock)) {
+
+ toCombine.add(jLabel);
+ toDelete.set(jLabel);
+ }
+ }
+
+ combineBlocks(iLabel, toCombine);
+ }
+ }
+
+ for (int i = szBlocks - 1; i >= 0; i--) {
+ if (toDelete.get(newBlocks.get(i).getLabel())) {
+ newBlocks.set(i, null);
+ }
+ }
+
+ newBlocks.shrinkToFit();
+ newBlocks.setImmutable();
+
+ return new RopMethod(newBlocks, ropMethod.getFirstLabel());
+ }
+
+ /**
+ * Helper method to compare the contents of two blocks.
+ *
+ * @param a {@code non-null;} a block to compare
+ * @param b {@code non-null;} another block to compare
+ * @return {@code true} iff the two blocks' instructions are the same
+ */
+ private static boolean compareInsns(BasicBlock a, BasicBlock b) {
+ return a.getInsns().contentEquals(b.getInsns());
+ }
+
+ /**
+ * Combines blocks proven identical into one alpha block, re-writing
+ * all of the successor links that point to the beta blocks to point
+ * to the alpha block instead.
+ *
+ * @param alphaLabel block that will replace all the beta block
+ * @param betaLabels label list of blocks to combine
+ */
+ private void combineBlocks(int alphaLabel, IntList betaLabels) {
+ int szBetas = betaLabels.size();
+
+ for (int i = 0; i < szBetas; i++) {
+ int betaLabel = betaLabels.get(i);
+ BasicBlock bb = blocks.labelToBlock(betaLabel);
+ IntList preds = ropMethod.labelToPredecessors(bb.getLabel());
+ int szPreds = preds.size();
+
+ for (int j = 0; j < szPreds; j++) {
+ BasicBlock predBlock = newBlocks.labelToBlock(preds.get(j));
+ replaceSucc(predBlock, betaLabel, alphaLabel);
+ }
+ }
+ }
+
+ /**
+ * Replaces one of a block's successors with a different label. Constructs
+ * an updated BasicBlock instance and places it in {@code newBlocks}.
+ *
+ * @param block block to replace
+ * @param oldLabel label of successor to replace
+ * @param newLabel label of new successor
+ */
+ private void replaceSucc(BasicBlock block, int oldLabel, int newLabel) {
+ IntList newSuccessors = block.getSuccessors().mutableCopy();
+ int newPrimarySuccessor;
+
+ newSuccessors.set(newSuccessors.indexOf(oldLabel), newLabel);
+ newPrimarySuccessor = block.getPrimarySuccessor();
+
+ if (newPrimarySuccessor == oldLabel) {
+ newPrimarySuccessor = newLabel;
+ }
+
+ newSuccessors.setImmutable();
+
+ BasicBlock newBB = new BasicBlock(block.getLabel(),
+ block.getInsns(), newSuccessors, newPrimarySuccessor);
+
+ newBlocks.set(newBlocks.indexOfLabel(block.getLabel()), newBB);
+ }
+}
diff --git a/dx/src/com/android/dx/ssa/back/InterferenceGraph.java b/dx/src/com/android/dx/ssa/back/InterferenceGraph.java
new file mode 100644
index 0000000..e6cde62
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/back/InterferenceGraph.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa.back;
+
+import com.android.dx.ssa.SsaMethod;
+import com.android.dx.ssa.SsaBasicBlock;
+import com.android.dx.ssa.SsaInsn;
+import com.android.dx.ssa.PhiInsn;
+import com.android.dx.ssa.SetFactory;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.util.IntSet;
+import com.android.dx.util.BitIntSet;
+import com.android.dx.util.ListIntSet;
+
+import java.util.BitSet;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * A register interference graph
+ */
+public class InterferenceGraph {
+ /**
+ * {@code non-null;} interference graph, indexed by register in
+ * both dimensions
+ */
+ private final ArrayList<IntSet> interference;
+
+ /**
+ * Creates a new graph.
+ *
+ * @param countRegs {@code >= 0;} the start count of registers in
+ * the namespace. New registers can be added subsequently.
+ */
+ public InterferenceGraph(int countRegs) {
+ interference = new ArrayList<IntSet>(countRegs);
+
+ for (int i = 0; i < countRegs; i++) {
+ interference.add(SetFactory.makeInterferenceSet(countRegs));
+ }
+ }
+
+ /**
+ * Adds a register pair to the interference/liveness graph. Parameter
+ * order is insignificant.
+ *
+ * @param regV one register index
+ * @param regW another register index
+ */
+ public void add(int regV, int regW) {
+ ensureCapacity(Math.max(regV, regW) + 1);
+
+ interference.get(regV).add(regW);
+ interference.get(regW).add(regV);
+ }
+
+ /**
+ * Dumps interference graph to stdout for debugging.
+ */
+ public void dumpToStdout() {
+ int oldRegCount = interference.size();
+
+ for (int i = 0; i < oldRegCount; i++) {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append("Reg " + i + ":" + interference.get(i).toString());
+
+ System.out.println(sb.toString());
+ }
+ }
+
+ /**
+ * Merges the interference set for a register into a given bit set
+ *
+ * @param reg {@code >= 0;} register
+ * @param set {@code non-null;} interference set; will be merged
+ * with set for given register
+ */
+ public void mergeInterferenceSet(int reg, IntSet set) {
+ if (reg < interference.size()) {
+ set.merge(interference.get(reg));
+ }
+ }
+
+ /**
+ * Ensures that the interference graph is appropriately sized.
+ *
+ * @param size requested minumum size
+ */
+ private void ensureCapacity(int size) {
+ int countRegs = interference.size();
+
+ interference.ensureCapacity(size);
+
+ for (int i = countRegs; i < size; i++) {
+ interference.add(SetFactory.makeInterferenceSet(size));
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/ssa/back/LivenessAnalyzer.java b/dx/src/com/android/dx/ssa/back/LivenessAnalyzer.java
new file mode 100644
index 0000000..a293e6f
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/back/LivenessAnalyzer.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa.back;
+
+import com.android.dx.ssa.SsaMethod;
+import com.android.dx.ssa.SsaBasicBlock;
+import com.android.dx.ssa.SsaInsn;
+import com.android.dx.ssa.PhiInsn;
+import com.android.dx.rop.code.RegisterSpec;
+
+import java.util.BitSet;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * From Appel "Modern Compiler Implementation in Java" algorithm 19.17
+ * Calculate the live ranges for register {@code reg}.<p>
+ *
+ * v = regV <p>
+ * s = insn <p>
+ * M = visitedBlocks <p>
+ */
+public class LivenessAnalyzer {
+ /**
+ * {@code non-null;} index by basic block indexed set of basic blocks
+ * that have already been visited. "M" as written in the original Appel
+ * algorithm.
+ */
+ private final BitSet visitedBlocks;
+
+ /**
+ * {@code non-null;} set of blocks remaing to visit as "live out as block"
+ */
+ private final BitSet liveOutBlocks;
+
+ /**
+ * {@code >=0;} SSA register currently being analyzed.
+ * "v" in the original Appel algorithm.
+ */
+ private final int regV;
+
+ /** method to process */
+ private final SsaMethod ssaMeth;
+
+ /** interference graph being updated */
+ private final InterferenceGraph interference;
+
+ /** block "n" in Appel 19.17 */
+ private SsaBasicBlock blockN;
+
+ /** index of statement {@code s} in {@code blockN} */
+ private int statementIndex;
+
+ /** the next function to call */
+ private NextFunction nextFunction;
+
+ /** constants for {@link #nextFunction} */
+ private static enum NextFunction {
+ LIVE_IN_AT_STATEMENT,
+ LIVE_OUT_AT_STATEMENT,
+ LIVE_OUT_AT_BLOCK,
+ DONE;
+ }
+
+ /**
+ * Runs register liveness algorithm for a method, updating the
+ * live in/out information in {@code SsaBasicBlock} instances and
+ * returning an interference graph.
+ *
+ * @param ssaMeth {@code non-null;} method to process
+ * @return {@code non-null;} interference graph indexed by SSA
+ * registers in both directions
+ */
+ public static InterferenceGraph constructInterferenceGraph(
+ SsaMethod ssaMeth) {
+ int szRegs = ssaMeth.getRegCount();
+ InterferenceGraph interference = new InterferenceGraph(szRegs);
+
+ for (int i = 0; i < szRegs; i++) {
+ new LivenessAnalyzer(ssaMeth, i, interference).run();
+ }
+
+ coInterferePhis(ssaMeth, interference);
+
+ return interference;
+ }
+
+ /**
+ * Makes liveness analyzer instance for specific register.
+ *
+ * @param ssaMeth {@code non-null;} method to process
+ * @param reg register whose liveness to analyze
+ * @param interference {@code non-null;} indexed by SSA reg in
+ * both dimensions; graph to update
+ *
+ */
+ private LivenessAnalyzer(SsaMethod ssaMeth, int reg,
+ InterferenceGraph interference) {
+ int blocksSz = ssaMeth.getBlocks().size();
+
+ this.ssaMeth = ssaMeth;
+ this.regV = reg;
+ visitedBlocks = new BitSet(blocksSz);
+ liveOutBlocks = new BitSet(blocksSz);
+ this.interference = interference;
+ }
+
+ /**
+ * The algorithm in Appel is presented in partial tail-recursion
+ * form. Obviously, that's not efficient in java, so this function
+ * serves as the dispatcher instead.
+ */
+ private void handleTailRecursion() {
+ while (nextFunction != NextFunction.DONE) {
+ switch (nextFunction) {
+ case LIVE_IN_AT_STATEMENT:
+ nextFunction = NextFunction.DONE;
+ liveInAtStatement();
+ break;
+
+ case LIVE_OUT_AT_STATEMENT:
+ nextFunction = NextFunction.DONE;
+ liveOutAtStatement();
+ break;
+
+ case LIVE_OUT_AT_BLOCK:
+ nextFunction = NextFunction.DONE;
+ liveOutAtBlock();
+ break;
+
+ default:
+ }
+ }
+ }
+
+ /**
+ * From Appel algorithm 19.17.
+ */
+ public void run() {
+ List<SsaInsn> useList = ssaMeth.getUseListForRegister(regV);
+
+ for (SsaInsn insn : useList) {
+ nextFunction = NextFunction.DONE;
+
+ if (insn instanceof PhiInsn) {
+ // If s is a phi-function with V as it's ith argument.
+ PhiInsn phi = (PhiInsn) insn;
+
+ for (SsaBasicBlock pred :
+ phi.predBlocksForReg(regV, ssaMeth)) {
+ blockN = pred;
+
+ nextFunction = NextFunction.LIVE_OUT_AT_BLOCK;
+ handleTailRecursion();
+ }
+ } else {
+ blockN = insn.getBlock();
+ statementIndex = blockN.getInsns().indexOf(insn);
+
+ if (statementIndex < 0) {
+ throw new RuntimeException(
+ "insn not found in it's own block");
+ }
+
+ nextFunction = NextFunction.LIVE_IN_AT_STATEMENT;
+ handleTailRecursion();
+ }
+ }
+
+ int nextLiveOutBlock;
+ while ((nextLiveOutBlock = liveOutBlocks.nextSetBit(0)) >= 0) {
+ blockN = ssaMeth.getBlocks().get(nextLiveOutBlock);
+ liveOutBlocks.clear(nextLiveOutBlock);
+ nextFunction = NextFunction.LIVE_OUT_AT_BLOCK;
+ handleTailRecursion();
+ }
+ }
+
+ /**
+ * "v is live-out at n."
+ */
+ private void liveOutAtBlock() {
+ if (! visitedBlocks.get(blockN.getIndex())) {
+ visitedBlocks.set(blockN.getIndex());
+
+ blockN.addLiveOut(regV);
+
+ ArrayList<SsaInsn> insns;
+
+ insns = blockN.getInsns();
+
+ // Live out at last statement in blockN
+ statementIndex = insns.size() - 1;
+ nextFunction = NextFunction.LIVE_OUT_AT_STATEMENT;
+ }
+ }
+
+ /**
+ * "v is live-in at s."
+ */
+ private void liveInAtStatement() {
+ // if s is the first statement in block N
+ if (statementIndex == 0) {
+ // v is live-in at n
+ blockN.addLiveIn(regV);
+
+ BitSet preds = blockN.getPredecessors();
+
+ liveOutBlocks.or(preds);
+ } else {
+ // Let s' be the statement preceeding s
+ statementIndex -= 1;
+ nextFunction = NextFunction.LIVE_OUT_AT_STATEMENT;
+ }
+ }
+
+ /**
+ * "v is live-out at s."
+ */
+ private void liveOutAtStatement() {
+ SsaInsn statement = blockN.getInsns().get(statementIndex);
+ RegisterSpec rs = statement.getResult();
+
+ if (!statement.isResultReg(regV)) {
+ if (rs != null) {
+ interference.add(regV, rs.getReg());
+ }
+ nextFunction = NextFunction.LIVE_IN_AT_STATEMENT;
+ }
+ }
+
+ /**
+ * Ensures that all the phi result registers for all the phis in the
+ * same basic block interfere with each other. This is needed since
+ * the dead code remover has allowed through "dead-end phis" whose
+ * results are not used except as local assignments. Without this step,
+ * a the result of a dead-end phi might be assigned the same register
+ * as the result of another phi, and the phi removal move scheduler may
+ * generate moves that over-write the live result.
+ *
+ * @param ssaMeth {@code non-null;} method to pricess
+ * @param interference {@code non-null;} interference graph
+ */
+ private static void coInterferePhis(SsaMethod ssaMeth,
+ InterferenceGraph interference) {
+ for (SsaBasicBlock b : ssaMeth.getBlocks()) {
+ List<SsaInsn> phis = b.getPhiInsns();
+
+ int szPhis = phis.size();
+
+ for (int i = 0; i < szPhis; i++) {
+ for (int j = 0; j < szPhis; j++) {
+ if (i == j) {
+ continue;
+ }
+
+ interference.add(phis.get(i).getResult().getReg(),
+ phis.get(j).getResult().getReg());
+ }
+ }
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/ssa/back/NullRegisterAllocator.java b/dx/src/com/android/dx/ssa/back/NullRegisterAllocator.java
new file mode 100644
index 0000000..0205c11
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/back/NullRegisterAllocator.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa.back;
+
+import com.android.dx.ssa.BasicRegisterMapper;
+import com.android.dx.ssa.RegisterMapper;
+import com.android.dx.ssa.SsaMethod;
+
+import java.util.BitSet;
+import java.util.ArrayList;
+
+/**
+ * A register allocator that maps SSA register n to Rop register 2*n,
+ * essentially preserving the original mapping and remaining agnostic
+ * about normal or wide categories. Used for debugging.
+ */
+public class NullRegisterAllocator extends RegisterAllocator {
+ /** {@inheritDoc} */
+ public NullRegisterAllocator(SsaMethod ssaMeth,
+ InterferenceGraph interference) {
+ super(ssaMeth, interference);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean wantsParamsMovedHigh() {
+ // We're not smart enough for this.
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RegisterMapper allocateRegisters() {
+ int oldRegCount = ssaMeth.getRegCount();
+
+ BasicRegisterMapper mapper = new BasicRegisterMapper(oldRegCount);
+
+ for (int i = 0; i < oldRegCount; i++) {
+ mapper.addMapping(i, i*2, 2);
+ }
+
+ return mapper;
+ }
+}
diff --git a/dx/src/com/android/dx/ssa/back/RegisterAllocator.java b/dx/src/com/android/dx/ssa/back/RegisterAllocator.java
new file mode 100644
index 0000000..1f9f70f
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/back/RegisterAllocator.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa.back;
+
+import com.android.dx.rop.code.RegOps;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.PlainInsn;
+import com.android.dx.rop.code.Rops;
+import com.android.dx.rop.code.SourcePosition;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.ssa.NormalSsaInsn;
+import com.android.dx.ssa.RegisterMapper;
+import com.android.dx.ssa.SsaInsn;
+import com.android.dx.ssa.SsaMethod;
+import com.android.dx.ssa.SsaBasicBlock;
+import com.android.dx.util.IntSet;
+import com.android.dx.util.IntIterator;
+
+import java.util.BitSet;
+import java.util.ArrayList;
+
+/**
+ * Base class of all register allocators.
+ */
+public abstract class RegisterAllocator {
+ /** method being processed */
+ protected final SsaMethod ssaMeth;
+
+ /** interference graph, indexed by register in both dimensions */
+ protected final InterferenceGraph interference;
+
+ /**
+ * Creates an instance. Call {@code allocateRegisters} to run.
+ * @param ssaMeth method to process.
+ * @param interference Interference graph, indexed by register in both
+ * dimensions.
+ */
+ public RegisterAllocator(SsaMethod ssaMeth,
+ InterferenceGraph interference) {
+ this.ssaMeth = ssaMeth;
+ this.interference = interference;
+ }
+
+ /**
+ * Indicates whether the method params were allocated at the bottom
+ * of the namespace, and thus should be moved up to the top of the
+ * namespace after phi removal.
+ *
+ * @return {@code true} if params should be moved from low to high
+ */
+ public abstract boolean wantsParamsMovedHigh();
+
+ /**
+ * Runs the algorithm.
+ *
+ * @return a register mapper to apply to the {@code SsaMethod}
+ */
+ public abstract RegisterMapper allocateRegisters();
+
+ /**
+ * Returns the category (width) of the definition site of the register.
+ * Returns {@code 1} for undefined registers.
+ *
+ * @param reg register
+ * @return {@code 1..2}
+ */
+ protected final int getCategoryForSsaReg(int reg) {
+ SsaInsn definition = ssaMeth.getDefinitionForRegister(reg);
+
+ if (definition == null) {
+ // an undefined reg
+ return 1;
+ } else {
+ return definition.getResult().getCategory();
+ }
+ }
+
+ /**
+ * Returns the RegisterSpec of the definition of the register.
+ *
+ * @param reg {@code >= 0;} SSA register
+ * @return definition spec of the register or null if it is never defined
+ * (for the case of "version 0" SSA registers)
+ */
+ protected final RegisterSpec getDefinitionSpecForSsaReg(int reg) {
+ SsaInsn definition = ssaMeth.getDefinitionForRegister(reg);
+
+ return definition == null ? null : definition.getResult();
+ }
+
+ /**
+ * Returns true if the definition site of this register is a
+ * move-param (ie, this is a method parameter).
+ *
+ * @param reg register in question
+ * @return {@code true} if this is a method parameter
+ */
+ protected boolean isDefinitionMoveParam(int reg) {
+ SsaInsn defInsn = ssaMeth.getDefinitionForRegister(reg);
+
+ if (defInsn instanceof NormalSsaInsn) {
+ NormalSsaInsn ndefInsn = (NormalSsaInsn) defInsn;
+
+ return ndefInsn.getOpcode().getOpcode() == RegOps.MOVE_PARAM;
+ }
+
+ return false;
+ }
+
+ /**
+ * Inserts a move instruction for a specified SSA register before a
+ * specified instruction, creating a new SSA register and adjusting the
+ * interference graph in the process. The insn currently must be the
+ * last insn in a block.
+ *
+ * @param insn {@code non-null;} insn to insert move before, must
+ * be last insn in block
+ * @param reg {@code non-null;} SSA register to duplicate
+ * @return {@code non-null;} spec of new SSA register created by move
+ */
+ protected final RegisterSpec insertMoveBefore(SsaInsn insn,
+ RegisterSpec reg) {
+ SsaBasicBlock block = insn.getBlock();
+ ArrayList<SsaInsn> insns = block.getInsns();
+ int insnIndex = insns.indexOf(insn);
+
+ if (insnIndex < 0) {
+ throw new IllegalArgumentException (
+ "specified insn is not in this block");
+ }
+
+ if (insnIndex != insns.size() - 1) {
+ /*
+ * Presently, the interference updater only works when
+ * adding before the last insn, and the last insn must have no
+ * result
+ */
+ throw new IllegalArgumentException(
+ "Adding move here not supported:" + insn.toHuman());
+ }
+
+ /*
+ * Get new register and make new move instruction.
+ */
+
+ // The new result must not have an associated local variable.
+ RegisterSpec newRegSpec = RegisterSpec.make(ssaMeth.makeNewSsaReg(),
+ reg.getTypeBearer());
+
+ SsaInsn toAdd = SsaInsn.makeFromRop(
+ new PlainInsn(Rops.opMove(newRegSpec.getType()),
+ SourcePosition.NO_INFO, newRegSpec,
+ RegisterSpecList.make(reg)), block);
+
+ insns.add(insnIndex, toAdd);
+
+ int newReg = newRegSpec.getReg();
+
+ /*
+ * Adjust interference graph based on what's live out of the current
+ * block and what's used by the final instruction.
+ */
+
+ IntSet liveOut = block.getLiveOutRegs();
+ IntIterator liveOutIter = liveOut.iterator();
+
+ while (liveOutIter.hasNext()) {
+ interference.add(newReg, liveOutIter.next());
+ }
+
+ // Everything that's a source in the last insn interferes.
+ RegisterSpecList sources = insn.getSources();
+ int szSources = sources.size();
+
+ for (int i = 0; i < szSources; i++) {
+ interference.add(newReg, sources.get(i).getReg());
+ }
+
+ ssaMeth.onInsnsChanged();
+
+ return newRegSpec;
+ }
+}
diff --git a/dx/src/com/android/dx/ssa/back/SsaToRop.java b/dx/src/com/android/dx/ssa/back/SsaToRop.java
new file mode 100644
index 0000000..d9d2c45
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/back/SsaToRop.java
@@ -0,0 +1,386 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa.back;
+
+import com.android.dx.rop.code.BasicBlock;
+import com.android.dx.rop.code.BasicBlockList;
+import com.android.dx.rop.code.CstInsn;
+import com.android.dx.rop.code.InsnList;
+import com.android.dx.rop.code.RegOps;
+import com.android.dx.rop.code.RegisterSpec;
+import com.android.dx.rop.code.RegisterSpecList;
+import com.android.dx.rop.code.RopMethod;
+import com.android.dx.rop.code.Rops;
+import com.android.dx.rop.code.Rop;
+import com.android.dx.rop.cst.CstInteger;
+import com.android.dx.ssa.NormalSsaInsn;
+import com.android.dx.ssa.BasicRegisterMapper;
+import com.android.dx.ssa.PhiInsn;
+import com.android.dx.ssa.RegisterMapper;
+import com.android.dx.ssa.SsaBasicBlock;
+import com.android.dx.ssa.SsaInsn;
+import com.android.dx.ssa.SsaMethod;
+import com.android.dx.util.IntList;
+import com.android.dx.util.Hex;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Converts a method in SSA form to ROP form.
+ */
+public class SsaToRop {
+ /** local debug flag */
+ private static final boolean DEBUG = false;
+
+ /** {@code non-null;} method to process */
+ private final SsaMethod ssaMeth;
+
+ /**
+ * {@code true} if the converter should attempt to minimize
+ * the rop-form register count
+ */
+ private final boolean minimizeRegisters;
+
+ /** {@code non-null;} interference graph */
+ private final InterferenceGraph interference;
+
+ /**
+ * Converts a method in SSA form to ROP form.
+ *
+ * @param ssaMeth {@code non-null;} method to process
+ * @param minimizeRegisters {@code true} if the converter should
+ * attempt to minimize the rop-form register count
+ * @return {@code non-null;} rop-form output
+ */
+ public static RopMethod convertToRopMethod(SsaMethod ssaMeth,
+ boolean minimizeRegisters) {
+ return new SsaToRop(ssaMeth, minimizeRegisters).convert();
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param ssaMeth {@code non-null;} method to process
+ * @param minimizeRegisters {@code true} if the converter should
+ * attempt to minimize the rop-form register count
+ */
+ private SsaToRop(SsaMethod ssaMethod, boolean minimizeRegisters) {
+ this.minimizeRegisters = minimizeRegisters;
+ this.ssaMeth = ssaMethod;
+ this.interference =
+ LivenessAnalyzer.constructInterferenceGraph(ssaMethod);
+ }
+
+ /**
+ * Performs the conversion.
+ *
+ * @return {@code non-null;} rop-form output
+ */
+ private RopMethod convert() {
+ if (DEBUG) {
+ interference.dumpToStdout();
+ }
+
+ // These are other allocators for debugging or historical comparison:
+ // allocator = new NullRegisterAllocator(ssaMeth, interference);
+ // allocator = new FirstFitAllocator(ssaMeth, interference);
+
+ RegisterAllocator allocator =
+ new FirstFitLocalCombiningAllocator(ssaMeth, interference,
+ minimizeRegisters);
+
+ RegisterMapper mapper = allocator.allocateRegisters();
+
+ if (DEBUG) {
+ System.out.println("Printing reg map");
+ System.out.println(((BasicRegisterMapper)mapper).toHuman());
+ }
+
+ ssaMeth.setBackMode();
+
+ ssaMeth.mapRegisters(mapper);
+
+ removePhiFunctions();
+
+ if (allocator.wantsParamsMovedHigh()) {
+ moveParametersToHighRegisters();
+ }
+
+ removeEmptyGotos();
+
+ RopMethod ropMethod = new RopMethod(convertBasicBlocks(),
+ ssaMeth.blockIndexToRopLabel(ssaMeth.getEntryBlockIndex()));
+ ropMethod = new IdenticalBlockCombiner(ropMethod).process();
+
+ return ropMethod;
+ }
+
+ /**
+ * Removes all blocks containing only GOTOs from the control flow.
+ * Although much of this work will be done later when converting
+ * from rop to dex, not all simplification cases can be handled
+ * there. Furthermore, any no-op block between the exit block and
+ * blocks containing the real return or throw statements must be
+ * removed.
+ */
+ private void removeEmptyGotos() {
+ final ArrayList<SsaBasicBlock> blocks = ssaMeth.getBlocks();
+
+ ssaMeth.forEachBlockDepthFirst(false, new SsaBasicBlock.Visitor() {
+ public void visitBlock(SsaBasicBlock b, SsaBasicBlock parent) {
+ ArrayList<SsaInsn> insns = b.getInsns();
+
+ if ((insns.size() == 1)
+ && (insns.get(0).getOpcode() == Rops.GOTO)) {
+ BitSet preds = (BitSet) b.getPredecessors().clone();
+
+ for (int i = preds.nextSetBit(0); i >= 0;
+ i = preds.nextSetBit(i + 1)) {
+ SsaBasicBlock pb = blocks.get(i);
+ pb.replaceSuccessor(b.getIndex(),
+ b.getPrimarySuccessorIndex());
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * See Appel 19.6. To remove the phi instructions in an edge-split
+ * SSA representation we know we can always insert a move in a
+ * predecessor block.
+ */
+ private void removePhiFunctions() {
+ ArrayList<SsaBasicBlock> blocks = ssaMeth.getBlocks();
+
+ for (SsaBasicBlock block : blocks) {
+ // Add moves in all the pred blocks for each phi insn.
+ block.forEachPhiInsn(new PhiVisitor(blocks));
+
+ // Delete the phi insns.
+ block.removeAllPhiInsns();
+ }
+
+ /*
+ * After all move insns have been added, sort them so they don't
+ * destructively interfere.
+ */
+ for (SsaBasicBlock block : blocks) {
+ block.scheduleMovesFromPhis();
+ }
+ }
+
+ /**
+ * Helper for {@link #removePhiFunctions}: PhiSuccessorUpdater for
+ * adding move instructions to predecessors based on phi insns.
+ */
+ private static class PhiVisitor implements PhiInsn.Visitor {
+ private final ArrayList<SsaBasicBlock> blocks;
+
+ public PhiVisitor(ArrayList<SsaBasicBlock> blocks) {
+ this.blocks = blocks;
+ }
+
+ public void visitPhiInsn(PhiInsn insn) {
+ RegisterSpecList sources = insn.getSources();
+ RegisterSpec result = insn.getResult();
+ int sz = sources.size();
+
+ for (int i = 0; i < sz; i++) {
+ RegisterSpec source = sources.get(i);
+ SsaBasicBlock predBlock = blocks.get(
+ insn.predBlockIndexForSourcesIndex(i));
+
+ predBlock.addMoveToEnd(result, source);
+ }
+ }
+ }
+
+ /**
+ * Moves the parameter registers, which allocateRegisters() places
+ * at the bottom of the frame, up to the top of the frame to match
+ * Dalvik calling convention.
+ */
+ private void moveParametersToHighRegisters() {
+ int paramWidth = ssaMeth.getParamWidth();
+ BasicRegisterMapper mapper
+ = new BasicRegisterMapper(ssaMeth.getRegCount());
+ int regCount = ssaMeth.getRegCount();
+
+ for (int i = 0; i < regCount; i++) {
+ if (i < paramWidth) {
+ mapper.addMapping(i, regCount - paramWidth + i, 1);
+ } else {
+ mapper.addMapping(i, i - paramWidth, 1);
+ }
+ }
+
+ if (DEBUG) {
+ System.out.printf("Moving %d registers from 0 to %d\n",
+ paramWidth, regCount - paramWidth);
+ }
+
+ ssaMeth.mapRegisters(mapper);
+ }
+
+ /**
+ * @return rop-form basic block list
+ */
+ private BasicBlockList convertBasicBlocks() {
+ ArrayList<SsaBasicBlock> blocks = ssaMeth.getBlocks();
+
+ // Exit block may be null.
+ SsaBasicBlock exitBlock = ssaMeth.getExitBlock();
+
+ ssaMeth.computeReachability();
+ int ropBlockCount = ssaMeth.getCountReachableBlocks();
+
+ // Don't count the exit block, if it exists.
+ ropBlockCount -= (exitBlock == null) ? 0 : 1;
+
+ BasicBlockList result = new BasicBlockList(ropBlockCount);
+
+ // Convert all the reachable blocks except the exit block.
+ int ropBlockIndex = 0;
+ for (SsaBasicBlock b : blocks) {
+ if (b.isReachable() && b != exitBlock) {
+ result.set(ropBlockIndex++, convertBasicBlock(b));
+ }
+ }
+
+ // The exit block, which is discarded, must do nothing.
+ if (exitBlock != null && exitBlock.getInsns().size() != 0) {
+ throw new RuntimeException(
+ "Exit block must have no insns when leaving SSA form");
+ }
+
+ return result;
+ }
+
+ /**
+ * Validates that a basic block is a valid end predecessor. It must
+ * end in a RETURN or a THROW. Throws a runtime exception on error.
+ *
+ * @param b {@code non-null;} block to validate
+ * @throws RuntimeException on error
+ */
+ private void verifyValidExitPredecessor(SsaBasicBlock b) {
+ ArrayList<SsaInsn> insns = b.getInsns();
+ SsaInsn lastInsn = insns.get(insns.size() - 1);
+ Rop opcode = lastInsn.getOpcode();
+
+ if (opcode.getBranchingness() != Rop.BRANCH_RETURN
+ && opcode != Rops.THROW) {
+ throw new RuntimeException("Exit predecessor must end"
+ + " in valid exit statement.");
+ }
+ }
+
+ /**
+ * Converts a single basic block to rop form.
+ *
+ * @param block SSA block to process
+ * @return {@code non-null;} ROP block
+ */
+ private BasicBlock convertBasicBlock(SsaBasicBlock block) {
+ IntList successorList = block.getRopLabelSuccessorList();
+ int primarySuccessorLabel = block.getPrimarySuccessorRopLabel();
+
+ // Filter out any reference to the SSA form's exit block.
+
+ // Exit block may be null.
+ SsaBasicBlock exitBlock = ssaMeth.getExitBlock();
+ int exitRopLabel = (exitBlock == null) ? -1 : exitBlock.getRopLabel();
+
+ if (successorList.contains(exitRopLabel)) {
+ if (successorList.size() > 1) {
+ throw new RuntimeException(
+ "Exit predecessor must have no other successors"
+ + Hex.u2(block.getRopLabel()));
+ } else {
+ successorList = IntList.EMPTY;
+ primarySuccessorLabel = -1;
+
+ verifyValidExitPredecessor(block);
+ }
+ }
+
+ successorList.setImmutable();
+
+ BasicBlock result = new BasicBlock(
+ block.getRopLabel(), convertInsns(block.getInsns()),
+ successorList,
+ primarySuccessorLabel);
+
+ return result;
+ }
+
+ /**
+ * Converts an insn list to rop form.
+ *
+ * @param ssaInsns {@code non-null;} old instructions
+ * @return {@code non-null;} immutable instruction list
+ */
+ private InsnList convertInsns(ArrayList<SsaInsn> ssaInsns) {
+ int insnCount = ssaInsns.size();
+ InsnList result = new InsnList(insnCount);
+
+ for (int i = 0; i < insnCount; i++) {
+ result.set(i, ssaInsns.get(i).toRopInsn());
+ }
+
+ result.setImmutable();
+
+ return result;
+ }
+
+ /**
+ * <b>Note:</b> This method is not presently used.
+ *
+ * @return a list of registers ordered by most-frequently-used to
+ * least-frequently-used. Each register is listed once and only
+ * once.
+ */
+ public int[] getRegistersByFrequency() {
+ int regCount = ssaMeth.getRegCount();
+ Integer[] ret = new Integer[regCount];
+
+ for (int i = 0; i < regCount; i++) {
+ ret[i] = i;
+ }
+
+ Arrays.sort(ret, new Comparator<Integer>() {
+ public int compare(Integer o1, Integer o2) {
+ return ssaMeth.getUseListForRegister(o2).size()
+ - ssaMeth.getUseListForRegister(o1).size();
+ }
+ });
+
+ int result[] = new int[regCount];
+
+ for (int i = 0; i < regCount; i++) {
+ result[i] = ret[i];
+ }
+
+ return result;
+ }
+}
diff --git a/dx/src/com/android/dx/ssa/package-info.java b/dx/src/com/android/dx/ssa/package-info.java
new file mode 100644
index 0000000..582a327
--- /dev/null
+++ b/dx/src/com/android/dx/ssa/package-info.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.ssa;
+
+/**
+ * <h1>An introduction to SSA Form</h1>
+ *
+ * This package contains classes associated with dx's {@code SSA}
+ * intermediate form. This form is a static-single-assignment representation of
+ * Rop-form a method with Rop-form-like instructions (with the addition of a
+ * {@link PhiInsn phi instriction}. This form is intended to make it easy to
+ * implement basic optimization steps and register allocation so that a
+ * reasonably efficient register machine representation can be produced from a
+ * stack machine source bytecode.<p>
+ *
+ * <h2>Key Classes</h2>
+ *
+ * <h3>Classes related to conversion and lifetime</h3>
+ * <ul>
+ * <li> {@link Optimizer} is a singleton class containing methods for
+ * converting, optimizing, and then back-converting Rop-form methods. It's the
+ * typical gateway into the rest of the package.
+ * <li> {@link SsaConverter} converts a Rop-form method to SSA form.
+ * <li> {@link SsaToRop} converts an SSA-form method back to Rop form.
+ * </ul>
+ *
+ * <h3>Classes related to method representation</h3>
+ * <ul>
+ * <li> A {@link SsaMethod} instance represents a method.
+ * <li> A {@link SsaBasicBlock} instance represents a basic block, whose
+ * semantics are quite similar to basic blocks in
+ * {@link com.android.dx.rop Rop form}.
+ * <li> {@link PhiInsn} instances represent "phi" operators defined in SSA
+ * literature. They must be the first N instructions in a basic block.
+ * <li> {@link NormalSsaInsn} instances represent instructions that directly
+ * correspond to {@code Rop} form.
+ * </ul>
+ *
+ * <h3>Classes related to optimization steps</h3>
+ * <ul>
+ * <li> {@link MoveParamCombiner} is a simple step that ensures each method
+ * parameter is represented by at most one SSA register.
+ * <li> {@link SCCP} is a (partially implemented) sparse-conditional
+ * constant propogator.
+ * <li> {@link LiteralOpUpgrader} is a step that attempts to use constant
+ * information to convert math and comparison instructions into
+ * constant-bearing "literal ops" in cases where they can be represented in the
+ * output form (see {@link TranslationAdvice#hasConstantOperation}).
+ * <li> {@link ConstCollector} is a step that attempts to trade (modest)
+ * increased register space for decreased instruction count in cases where
+ * the same constant value is used repeatedly in a single method.
+ * <li> {@link DeadCodeRemover} is a dead code remover. This phase must
+ * always be run to remove unused phi instructions.
+ * </ul>
+ *
+ * <h2>SSA Lifetime</h2>
+ * The representation of a method in SSA form obeys slightly different
+ * constraints depending upon whether it is in the process of being converted
+ * into or out of SSA form.
+ *
+ * <h3>Conversion into SSA Form</h3>
+ *
+ * {@link SsaConverter#convertToSsaMethod} takes a {@code RopMethod} and
+ * returns a fully-converted {@code SsaMethod}. The conversion process
+ * is roughly as follows:
+ *
+ * <ol>
+ * <li> The Rop-form method, its blocks and their instructions are directly
+ * wrapped in {@code SsaMethod}, {@code SsaBasicBlock} and
+ * {@code SsaInsn} instances. Nothing else changes.
+ * <li> Critical control-flow graph edges are {@link SsaConverter#edgeSplit
+ * split} and new basic blocks inserted as required to meet the constraints
+ * necessary for the ultimate SSA representation.
+ * <li> A {@link LocalVariableExtractor} is run to produce a table of
+ * Rop registers to local variables necessary during phi placement. This
+ * step could also be done in Rop form and then updated through the preceding
+ * steps.
+ * <li> {@code Phi} instructions are {link SsaConverter#placePhiFunctions}
+ * placed in a semi-pruned fashion, which requires computation of {@link
+ * Dominators dominance graph} and each node's {@link DomFront
+ * dominance-frontier set}.
+ * <li> Finally, source and result registers for all instructions are {@link
+ * SsaRenamer renamed} such that each assignment is given a unique register
+ * number (register categories or widths, significant in Rop form, do not
+ * exist in SSA). Move instructions are eliminated except where necessary
+ * to preserve local variable assignments.
+ * </ol>
+ *
+ */
diff --git a/dx/src/com/android/dx/util/AnnotatedOutput.java b/dx/src/com/android/dx/util/AnnotatedOutput.java
new file mode 100644
index 0000000..7a9ea29
--- /dev/null
+++ b/dx/src/com/android/dx/util/AnnotatedOutput.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+/**
+ * Interface for a binary output destination that may be augmented
+ * with textual annotations.
+ */
+public interface AnnotatedOutput
+ extends Output {
+ /**
+ * Get whether this instance will actually keep annotations.
+ *
+ * @return {@code true} iff annotations are being kept
+ */
+ public boolean annotates();
+
+ /**
+ * Get whether this instance is intended to keep verbose annotations.
+ * Annotators may use the result of calling this method to inform their
+ * annotation activity.
+ *
+ * @return {@code true} iff annotations are to be verbose
+ */
+ public boolean isVerbose();
+
+ /**
+ * Add an annotation for the subsequent output. Any previously
+ * open annotation will be closed by this call, and the new
+ * annotation marks all subsequent output until another annotation
+ * call.
+ *
+ * @param msg {@code non-null;} the annotation message
+ */
+ public void annotate(String msg);
+
+ /**
+ * Add an annotation for a specified amount of subsequent
+ * output. Any previously open annotation will be closed by this
+ * call. If there is already pending annotation from one or more
+ * previous calls to this method, the new call "consumes" output
+ * after all the output covered by the previous calls.
+ *
+ * @param amt {@code >= 0;} the amount of output for this annotation to
+ * cover
+ * @param msg {@code non-null;} the annotation message
+ */
+ public void annotate(int amt, String msg);
+
+ /**
+ * End the most recent annotation. Subsequent output will be unannotated,
+ * until the next call to {@link #annotate}.
+ */
+ public void endAnnotation();
+
+ /**
+ * Get the maximum width of the annotated output. This is advisory:
+ * Implementations of this interface are encouraged to deal with too-wide
+ * output, but annotaters are encouraged to attempt to avoid exceeding
+ * the indicated width.
+ *
+ * @return {@code >= 1;} the maximum width
+ */
+ public int getAnnotationWidth();
+}
diff --git a/dx/src/com/android/dx/util/BitIntSet.java b/dx/src/com/android/dx/util/BitIntSet.java
new file mode 100644
index 0000000..b364f0c
--- /dev/null
+++ b/dx/src/com/android/dx/util/BitIntSet.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+import java.util.NoSuchElementException;
+
+/**
+ * A set of integers, represented by a bit set
+ */
+public class BitIntSet implements IntSet {
+
+ /** also accessed in ListIntSet */
+ int[] bits;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param max the maximum value of ints in this set.
+ */
+ public BitIntSet(int max) {
+ bits = Bits.makeBitSet(max);
+ }
+
+ /** @inheritDoc */
+ public void add(int value) {
+ ensureCapacity(value);
+ Bits.set(bits, value, true);
+ }
+
+ /**
+ * Ensures that the bit set has the capacity to represent the given value.
+ *
+ * @param value {@code >= 0;} value to represent
+ */
+ private void ensureCapacity(int value) {
+ if (value >= Bits.getMax(bits)) {
+ int[] newBits = Bits.makeBitSet(
+ Math.max(value + 1, 2 * Bits.getMax(bits)));
+ System.arraycopy(bits, 0, newBits, 0, bits.length);
+ bits = newBits;
+ }
+ }
+
+ /** @inheritDoc */
+ public void remove(int value) {
+ if (value < Bits.getMax(bits)) {
+ Bits.set(bits, value, false);
+ }
+ }
+
+ /** @inheritDoc */
+ public boolean has(int value) {
+ return (value < Bits.getMax(bits)) && Bits.get(bits, value);
+ }
+
+ /** @inheritDoc */
+ public void merge(IntSet other) {
+ if (other instanceof BitIntSet) {
+ BitIntSet o = (BitIntSet) other;
+ ensureCapacity(Bits.getMax(o.bits) + 1);
+ Bits.or(bits, o.bits);
+ } else if (other instanceof ListIntSet) {
+ ListIntSet o = (ListIntSet) other;
+ int sz = o.ints.size();
+
+ if (sz > 0) {
+ ensureCapacity(o.ints.get(sz - 1));
+ }
+ for (int i = 0; i < o.ints.size(); i++) {
+ Bits.set(bits, o.ints.get(i), true);
+ }
+ } else {
+ IntIterator iter = other.iterator();
+ while (iter.hasNext()) {
+ add(iter.next());
+ }
+ }
+ }
+
+ /** @inheritDoc */
+ public int elements() {
+ return Bits.bitCount(bits);
+ }
+
+ /** @inheritDoc */
+ public IntIterator iterator() {
+ return new IntIterator() {
+ private int idx = Bits.findFirst(bits, 0);
+
+ /** @inheritDoc */
+ public boolean hasNext() {
+ return idx >= 0;
+ }
+
+ /** @inheritDoc */
+ public int next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+
+ int ret = idx;
+
+ idx = Bits.findFirst(bits, idx+1);
+
+ return ret;
+ }
+ };
+ }
+
+ /** @inheritDoc */
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append('{');
+
+ boolean first = true;
+ for (int i = Bits.findFirst(bits, 0)
+ ; i >= 0
+ ; i = Bits.findFirst(bits, i + 1)) {
+ if (!first) {
+ sb.append(", ");
+ }
+ first = false;
+ sb.append(i);
+ }
+
+ sb.append('}');
+
+ return sb.toString();
+ }
+}
diff --git a/dx/src/com/android/dx/util/Bits.java b/dx/src/com/android/dx/util/Bits.java
new file mode 100644
index 0000000..cbc0a5b
--- /dev/null
+++ b/dx/src/com/android/dx/util/Bits.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+/**
+ * Utilities for treating {@code int[]}s as bit sets.
+ */
+public final class Bits {
+ /**
+ * This class is uninstantiable.
+ */
+ private Bits() {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Constructs a bit set to contain bits up to the given index (exclusive).
+ *
+ * @param max {@code >= 0;} the maximum bit index (exclusive)
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public static int[] makeBitSet(int max) {
+ int size = (max + 0x1f) >> 5;
+ return new int[size];
+ }
+
+ /**
+ * Gets the maximum index (exclusive) for the given bit set.
+ *
+ * @param bits {@code non-null;} bit set in question
+ * @return {@code >= 0;} the maximum index (exclusive) that may be set
+ */
+ public static int getMax(int[] bits) {
+ return bits.length * 0x20;
+ }
+
+ /**
+ * Gets the value of the bit at the given index.
+ *
+ * @param bits {@code non-null;} bit set to operate on
+ * @param idx {@code >= 0, < getMax(set);} which bit
+ * @return the value of the indicated bit
+ */
+ public static boolean get(int[] bits, int idx) {
+ int arrayIdx = idx >> 5;
+ int bit = 1 << (idx & 0x1f);
+ return (bits[arrayIdx] & bit) != 0;
+ }
+
+ /**
+ * Sets the given bit to the given value.
+ *
+ * @param bits {@code non-null;} bit set to operate on
+ * @param idx {@code >= 0, < getMax(set);} which bit
+ * @param value the new value for the bit
+ */
+ public static void set(int[] bits, int idx, boolean value) {
+ int arrayIdx = idx >> 5;
+ int bit = 1 << (idx & 0x1f);
+
+ if (value) {
+ bits[arrayIdx] |= bit;
+ } else {
+ bits[arrayIdx] &= ~bit;
+ }
+ }
+
+ /**
+ * Sets the given bit to {@code true}.
+ *
+ * @param bits {@code non-null;} bit set to operate on
+ * @param idx {@code >= 0, < getMax(set);} which bit
+ */
+ public static void set(int[] bits, int idx) {
+ int arrayIdx = idx >> 5;
+ int bit = 1 << (idx & 0x1f);
+ bits[arrayIdx] |= bit;
+ }
+
+ /**
+ * Sets the given bit to {@code false}.
+ *
+ * @param bits {@code non-null;} bit set to operate on
+ * @param idx {@code >= 0, < getMax(set);} which bit
+ */
+ public static void clear(int[] bits, int idx) {
+ int arrayIdx = idx >> 5;
+ int bit = 1 << (idx & 0x1f);
+ bits[arrayIdx] &= ~bit;
+ }
+
+ /**
+ * Returns whether or not the given bit set is empty, that is, whether
+ * no bit is set to {@code true}.
+ *
+ * @param bits {@code non-null;} bit set to operate on
+ * @return {@code true} iff all bits are {@code false}
+ */
+ public static boolean isEmpty(int[] bits) {
+ int len = bits.length;
+
+ for (int i = 0; i < len; i++) {
+ if (bits[i] != 0) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Gets the number of bits set to {@code true} in the given bit set.
+ *
+ * @param bits {@code non-null;} bit set to operate on
+ * @return {@code >= 0;} the bit count (aka population count) of the set
+ */
+ public static int bitCount(int[] bits) {
+ int len = bits.length;
+ int count = 0;
+
+ for (int i = 0; i < len; i++) {
+ count += Integer.bitCount(bits[i]);
+ }
+
+ return count;
+ }
+
+ /**
+ * Returns whether any bits are set to {@code true} in the
+ * specified range.
+ *
+ * @param bits {@code non-null;} bit set to operate on
+ * @param start {@code >= 0;} index of the first bit in the range (inclusive)
+ * @param end {@code >= 0;} index of the last bit in the range (exclusive)
+ * @return {@code true} if any bit is set to {@code true} in
+ * the indicated range
+ */
+ public static boolean anyInRange(int[] bits, int start, int end) {
+ int idx = findFirst(bits, start);
+ return (idx >= 0) && (idx < end);
+ }
+
+ /**
+ * Finds the lowest-order bit set at or after the given index in the
+ * given bit set.
+ *
+ * @param bits {@code non-null;} bit set to operate on
+ * @param idx {@code >= 0;} minimum index to return
+ * @return {@code >= -1;} lowest-order bit set at or after {@code idx},
+ * or {@code -1} if there is no appropriate bit index to return
+ */
+ public static int findFirst(int[] bits, int idx) {
+ int len = bits.length;
+ int minBit = idx & 0x1f;
+
+ for (int arrayIdx = idx >> 5; arrayIdx < len; arrayIdx++) {
+ int word = bits[arrayIdx];
+ if (word != 0) {
+ int bitIdx = findFirst(word, minBit);
+ if (bitIdx >= 0) {
+ return (arrayIdx << 5) + bitIdx;
+ }
+ }
+ minBit = 0;
+ }
+
+ return -1;
+ }
+
+ /**
+ * Finds the lowest-order bit set at or after the given index in the
+ * given {@code int}.
+ *
+ * @param value the value in question
+ * @param idx 0..31 the minimum bit index to return
+ * @return {@code >= -1;} lowest-order bit set at or after {@code idx},
+ * or {@code -1} if there is no appropriate bit index to return
+ */
+ public static int findFirst(int value, int idx) {
+ value &= ~((1 << idx) - 1); // Mask off too-low bits.
+ int result = Integer.numberOfTrailingZeros(value);
+ return (result == 32) ? -1 : result;
+ }
+
+ /**
+ * Ors bit array {@code b} into bit array {@code a}.
+ * {@code a.length} must be greater than or equal to
+ * {@code b.length}.
+ *
+ * @param a {@code non-null;} int array to be ored with other argument. This
+ * argument is modified.
+ * @param b {@code non-null;} int array to be ored into {@code a}. This
+ * argument is not modified.
+ */
+ public static void or(int[] a, int[] b) {
+ for (int i = 0; i < b.length; i++) {
+ a[i] |= b[i];
+ }
+ }
+
+ public static String toHuman(int[] bits) {
+ StringBuilder sb = new StringBuilder();
+
+ boolean needsComma = false;
+
+ sb.append('{');
+
+ int bitsLength = 32 * bits.length;
+ for (int i = 0; i < bitsLength; i++) {
+ if (Bits.get(bits, i)) {
+ if (needsComma) {
+ sb.append(',');
+ }
+ needsComma = true;
+ sb.append(i);
+ }
+ }
+ sb.append('}');
+
+ return sb.toString();
+ }
+}
diff --git a/dx/src/com/android/dx/util/ByteArray.java b/dx/src/com/android/dx/util/ByteArray.java
new file mode 100644
index 0000000..21d0457
--- /dev/null
+++ b/dx/src/com/android/dx/util/ByteArray.java
@@ -0,0 +1,361 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Wrapper for a {@code byte[]}, which provides read-only access and
+ * can "reveal" a partial slice of the underlying array.
+ *
+ * <b>Note:</b> Multibyte accessors all use big-endian order.
+ */
+public final class ByteArray {
+ /** {@code non-null;} underlying array */
+ private final byte[] bytes;
+
+ /** {@code >= 0}; start index of the slice (inclusive) */
+ private final int start;
+
+ /** {@code >= 0, <= bytes.length}; size computed as
+ * {@code end - start} (in the constructor) */
+ private final int size;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param bytes {@code non-null;} the underlying array
+ * @param start {@code >= 0;} start index of the slice (inclusive)
+ * @param end {@code >= start, <= bytes.length;} end index of
+ * the slice (exclusive)
+ */
+ public ByteArray(byte[] bytes, int start, int end) {
+ if (bytes == null) {
+ throw new NullPointerException("bytes == null");
+ }
+
+ if (start < 0) {
+ throw new IllegalArgumentException("start < 0");
+ }
+
+ if (end < start) {
+ throw new IllegalArgumentException("end < start");
+ }
+
+ if (end > bytes.length) {
+ throw new IllegalArgumentException("end > bytes.length");
+ }
+
+ this.bytes = bytes;
+ this.start = start;
+ this.size = end - start;
+ }
+
+ /**
+ * Constructs an instance from an entire {@code byte[]}.
+ *
+ * @param bytes {@code non-null;} the underlying array
+ */
+ public ByteArray(byte[] bytes) {
+ this(bytes, 0, bytes.length);
+ }
+
+ /**
+ * Gets the size of the array, in bytes.
+ *
+ * @return {@code >= 0;} the size
+ */
+ public int size() {
+ return size;
+ }
+
+ /**
+ * Returns a slice (that is, a sub-array) of this instance.
+ *
+ * @param start {@code >= 0;} start index of the slice (inclusive)
+ * @param end {@code >= start, <= size();} end index of
+ * the slice (exclusive)
+ * @return {@code non-null;} the slice
+ */
+ public ByteArray slice(int start, int end) {
+ checkOffsets(start, end);
+ return new ByteArray(bytes, start + this.start, end + this.start);
+ }
+
+ /**
+ * Returns the offset into the given array represented by the given
+ * offset into this instance.
+ *
+ * @param offset offset into this instance
+ * @param bytes {@code non-null;} (alleged) underlying array
+ * @return corresponding offset into {@code bytes}
+ * @throws IllegalArgumentException thrown if {@code bytes} is
+ * not the underlying array of this instance
+ */
+ public int underlyingOffset(int offset, byte[] bytes) {
+ if (bytes != this.bytes) {
+ throw new IllegalArgumentException("wrong bytes");
+ }
+
+ return start + offset;
+ }
+
+ /**
+ * Gets the {@code signed byte} value at a particular offset.
+ *
+ * @param off {@code >= 0, < size();} offset to fetch
+ * @return {@code signed byte} at that offset
+ */
+ public int getByte(int off) {
+ checkOffsets(off, off + 1);
+ return getByte0(off);
+ }
+
+ /**
+ * Gets the {@code signed short} value at a particular offset.
+ *
+ * @param off {@code >= 0, < (size() - 1);} offset to fetch
+ * @return {@code signed short} at that offset
+ */
+ public int getShort(int off) {
+ checkOffsets(off, off + 2);
+ return (getByte0(off) << 8) | getUnsignedByte0(off + 1);
+ }
+
+ /**
+ * Gets the {@code signed int} value at a particular offset.
+ *
+ * @param off {@code >= 0, < (size() - 3);} offset to fetch
+ * @return {@code signed int} at that offset
+ */
+ public int getInt(int off) {
+ checkOffsets(off, off + 4);
+ return (getByte0(off) << 24) |
+ (getUnsignedByte0(off + 1) << 16) |
+ (getUnsignedByte0(off + 2) << 8) |
+ getUnsignedByte0(off + 3);
+ }
+
+ /**
+ * Gets the {@code signed long} value at a particular offset.
+ *
+ * @param off {@code >= 0, < (size() - 7);} offset to fetch
+ * @return {@code signed int} at that offset
+ */
+ public long getLong(int off) {
+ checkOffsets(off, off + 8);
+ int part1 = (getByte0(off) << 24) |
+ (getUnsignedByte0(off + 1) << 16) |
+ (getUnsignedByte0(off + 2) << 8) |
+ getUnsignedByte0(off + 3);
+ int part2 = (getByte0(off + 4) << 24) |
+ (getUnsignedByte0(off + 5) << 16) |
+ (getUnsignedByte0(off + 6) << 8) |
+ getUnsignedByte0(off + 7);
+
+ return (part2 & 0xffffffffL) | ((long) part1) << 32;
+ }
+
+ /**
+ * Gets the {@code unsigned byte} value at a particular offset.
+ *
+ * @param off {@code >= 0, < size();} offset to fetch
+ * @return {@code unsigned byte} at that offset
+ */
+ public int getUnsignedByte(int off) {
+ checkOffsets(off, off + 1);
+ return getUnsignedByte0(off);
+ }
+
+ /**
+ * Gets the {@code unsigned short} value at a particular offset.
+ *
+ * @param off {@code >= 0, < (size() - 1);} offset to fetch
+ * @return {@code unsigned short} at that offset
+ */
+ public int getUnsignedShort(int off) {
+ checkOffsets(off, off + 2);
+ return (getUnsignedByte0(off) << 8) | getUnsignedByte0(off + 1);
+ }
+
+ /**
+ * Copies the contents of this instance into the given raw
+ * {@code byte[]} at the given offset. The given array must be
+ * large enough.
+ *
+ * @param out {@code non-null;} array to hold the output
+ * @param offset {@code non-null;} index into {@code out} for the first
+ * byte of output
+ */
+ public void getBytes(byte[] out, int offset) {
+ if ((out.length - offset) < size) {
+ throw new IndexOutOfBoundsException("(out.length - offset) < " +
+ "size()");
+ }
+
+ System.arraycopy(bytes, start, out, offset, size);
+ }
+
+ /**
+ * Checks a range of offsets for validity, throwing if invalid.
+ *
+ * @param s start offset (inclusive)
+ * @param e end offset (exclusive)
+ */
+ private void checkOffsets(int s, int e) {
+ if ((s < 0) || (e < s) || (e > size)) {
+ throw new IllegalArgumentException("bad range: " + s + ".." + e +
+ "; actual size " + size);
+ }
+ }
+
+ /**
+ * Gets the {@code signed byte} value at the given offset,
+ * without doing any argument checking.
+ *
+ * @param off offset to fetch
+ * @return byte at that offset
+ */
+ private int getByte0(int off) {
+ return bytes[start + off];
+ }
+
+ /**
+ * Gets the {@code unsigned byte} value at the given offset,
+ * without doing any argument checking.
+ *
+ * @param off offset to fetch
+ * @return byte at that offset
+ */
+ private int getUnsignedByte0(int off) {
+ return bytes[start + off] & 0xff;
+ }
+
+ /**
+ * Gets a {@code DataInputStream} that reads from this instance,
+ * with the cursor starting at the beginning of this instance's data.
+ * <b>Note:</b> The returned instance may be cast to {@link #GetCursor}
+ * if needed.
+ *
+ * @return {@code non-null;} an appropriately-constructed
+ * {@code DataInputStream} instance
+ */
+ public MyDataInputStream makeDataInputStream() {
+ return new MyDataInputStream(makeInputStream());
+ }
+
+ /**
+ * Gets a {@code InputStream} that reads from this instance,
+ * with the cursor starting at the beginning of this instance's data.
+ * <b>Note:</b> The returned instance may be cast to {@link #GetCursor}
+ * if needed.
+ *
+ * @return {@code non-null;} an appropriately-constructed
+ * {@code InputStream} instancex
+ */
+ public MyInputStream makeInputStream() {
+ return new MyInputStream();
+ }
+
+ /**
+ * Helper interface that allows one to get the cursor (of a stream).
+ */
+ public interface GetCursor {
+ /**
+ * Gets the current cursor.
+ *
+ * @return {@code 0..size();} the cursor
+ */
+ public int getCursor();
+ }
+
+ /**
+ * Helper class for {@link #makeInputStream}, which implements the
+ * stream functionality.
+ */
+ public class MyInputStream extends InputStream {
+ /** 0..size; the cursor */
+ private int cursor;
+
+ /** 0..size; the mark */
+ private int mark;
+
+ public MyInputStream() {
+ cursor = 0;
+ mark = 0;
+ }
+
+ public int read() throws IOException {
+ if (cursor >= size) {
+ return -1;
+ }
+
+ int result = getUnsignedByte0(cursor);
+ cursor++;
+ return result;
+ }
+
+ public int read(byte[] arr, int offset, int length) {
+ if ((offset + length) > arr.length) {
+ length = arr.length - offset;
+ }
+
+ int maxLength = size - cursor;
+ if (length > maxLength) {
+ length = maxLength;
+ }
+
+ System.arraycopy(bytes, cursor + start, arr, offset, length);
+ cursor += length;
+ return length;
+ }
+
+ public int available() {
+ return size - cursor;
+ }
+
+ public void mark(int reserve) {
+ mark = cursor;
+ }
+
+ public void reset() {
+ cursor = mark;
+ }
+
+ public boolean markSupported() {
+ return true;
+ }
+ }
+
+ /**
+ * Helper class for {@link #makeDataInputStream}. This is used
+ * simply so that the cursor of a wrapped {@link #MyInputStream}
+ * instance may be easily determined.
+ */
+ public static class MyDataInputStream extends DataInputStream {
+ /** {@code non-null;} the underlying {@link #MyInputStream} */
+ private final MyInputStream wrapped;
+
+ public MyDataInputStream(MyInputStream wrapped) {
+ super(wrapped);
+
+ this.wrapped = wrapped;
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/util/ByteArrayAnnotatedOutput.java b/dx/src/com/android/dx/util/ByteArrayAnnotatedOutput.java
new file mode 100644
index 0000000..6d0615e
--- /dev/null
+++ b/dx/src/com/android/dx/util/ByteArrayAnnotatedOutput.java
@@ -0,0 +1,639 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+
+/**
+ * Implementation of {@link AnnotatedOutput} which stores the written data
+ * into a {@code byte[]}.
+ *
+ * <p><b>Note:</b> As per the {@link Output} interface, multi-byte
+ * writes all use little-endian order.</p>
+ */
+public final class ByteArrayAnnotatedOutput
+ implements AnnotatedOutput {
+ /** default size for stretchy instances */
+ private static final int DEFAULT_SIZE = 1000;
+
+ /**
+ * whether the instance is stretchy, that is, whether its array
+ * may be resized to increase capacity
+ */
+ private final boolean stretchy;
+
+ /** {@code non-null;} the data itself */
+ private byte[] data;
+
+ /** {@code >= 0;} current output cursor */
+ private int cursor;
+
+ /** whether annotations are to be verbose */
+ private boolean verbose;
+
+ /**
+ * {@code null-ok;} list of annotations, or {@code null} if this instance
+ * isn't keeping them
+ */
+ private ArrayList<Annotation> annotations;
+
+ /** {@code >= 40 (if used);} the desired maximum annotation width */
+ private int annotationWidth;
+
+ /**
+ * {@code >= 8 (if used);} the number of bytes of hex output to use
+ * in annotations
+ */
+ private int hexCols;
+
+ /**
+ * Constructs an instance with a fixed maximum size. Note that the
+ * given array is the only one that will be used to store data. In
+ * particular, no reallocation will occur in order to expand the
+ * capacity of the resulting instance. Also, the constructed
+ * instance does not keep annotations by default.
+ *
+ * @param data {@code non-null;} data array to use for output
+ */
+ public ByteArrayAnnotatedOutput(byte[] data) {
+ this(data, false);
+ }
+
+ /**
+ * Constructs a "stretchy" instance. The underlying array may be
+ * reallocated. The constructed instance does not keep annotations
+ * by default.
+ */
+ public ByteArrayAnnotatedOutput() {
+ this(new byte[DEFAULT_SIZE], true);
+ }
+
+ /**
+ * Internal constructor.
+ *
+ * @param data {@code non-null;} data array to use for output
+ * @param stretchy whether the instance is to be stretchy
+ */
+ private ByteArrayAnnotatedOutput(byte[] data, boolean stretchy) {
+ if (data == null) {
+ throw new NullPointerException("data == null");
+ }
+
+ this.stretchy = stretchy;
+ this.data = data;
+ this.cursor = 0;
+ this.verbose = false;
+ this.annotations = null;
+ this.annotationWidth = 0;
+ this.hexCols = 0;
+ }
+
+ /**
+ * Gets the underlying {@code byte[]} of this instance, which
+ * may be larger than the number of bytes written
+ *
+ * @see #toByteArray
+ *
+ * @return {@code non-null;} the {@code byte[]}
+ */
+ public byte[] getArray() {
+ return data;
+ }
+
+ /**
+ * Constructs and returns a new {@code byte[]} that contains
+ * the written contents exactly (that is, with no extra unwritten
+ * bytes at the end).
+ *
+ * @see #getArray
+ *
+ * @return {@code non-null;} an appropriately-constructed array
+ */
+ public byte[] toByteArray() {
+ byte[] result = new byte[cursor];
+ System.arraycopy(data, 0, result, 0, cursor);
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ public int getCursor() {
+ return cursor;
+ }
+
+ /** {@inheritDoc} */
+ public void assertCursor(int expectedCursor) {
+ if (cursor != expectedCursor) {
+ throw new ExceptionWithContext("expected cursor " +
+ expectedCursor + "; actual value: " + cursor);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void writeByte(int value) {
+ int writeAt = cursor;
+ int end = writeAt + 1;
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ data[writeAt] = (byte) value;
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public void writeShort(int value) {
+ int writeAt = cursor;
+ int end = writeAt + 2;
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ data[writeAt] = (byte) value;
+ data[writeAt + 1] = (byte) (value >> 8);
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public void writeInt(int value) {
+ int writeAt = cursor;
+ int end = writeAt + 4;
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ data[writeAt] = (byte) value;
+ data[writeAt + 1] = (byte) (value >> 8);
+ data[writeAt + 2] = (byte) (value >> 16);
+ data[writeAt + 3] = (byte) (value >> 24);
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public void writeLong(long value) {
+ int writeAt = cursor;
+ int end = writeAt + 8;
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ int half = (int) value;
+ data[writeAt] = (byte) half;
+ data[writeAt + 1] = (byte) (half >> 8);
+ data[writeAt + 2] = (byte) (half >> 16);
+ data[writeAt + 3] = (byte) (half >> 24);
+
+ half = (int) (value >> 32);
+ data[writeAt + 4] = (byte) half;
+ data[writeAt + 5] = (byte) (half >> 8);
+ data[writeAt + 6] = (byte) (half >> 16);
+ data[writeAt + 7] = (byte) (half >> 24);
+
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public int writeUnsignedLeb128(int value) {
+ int remaining = value >> 7;
+ int count = 0;
+
+ while (remaining != 0) {
+ writeByte((value & 0x7f) | 0x80);
+ value = remaining;
+ remaining >>= 7;
+ count++;
+ }
+
+ writeByte(value & 0x7f);
+ return count + 1;
+ }
+
+ /** {@inheritDoc} */
+ public int writeSignedLeb128(int value) {
+ int remaining = value >> 7;
+ int count = 0;
+ boolean hasMore = true;
+ int end = ((value & Integer.MIN_VALUE) == 0) ? 0 : -1;
+
+ while (hasMore) {
+ hasMore = (remaining != end)
+ || ((remaining & 1) != ((value >> 6) & 1));
+
+ writeByte((value & 0x7f) | (hasMore ? 0x80 : 0));
+ value = remaining;
+ remaining >>= 7;
+ count++;
+ }
+
+ return count;
+ }
+
+ /** {@inheritDoc} */
+ public void write(ByteArray bytes) {
+ int blen = bytes.size();
+ int writeAt = cursor;
+ int end = writeAt + blen;
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ bytes.getBytes(data, writeAt);
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public void write(byte[] bytes, int offset, int length) {
+ int writeAt = cursor;
+ int end = writeAt + length;
+ int bytesEnd = offset + length;
+
+ // twos-complement math trick: ((x < 0) || (y < 0)) <=> ((x|y) < 0)
+ if (((offset | length | end) < 0) || (bytesEnd > bytes.length)) {
+ throw new IndexOutOfBoundsException("bytes.length " +
+ bytes.length + "; " +
+ offset + "..!" + end);
+ }
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ System.arraycopy(bytes, offset, data, writeAt, length);
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public void write(byte[] bytes) {
+ write(bytes, 0, bytes.length);
+ }
+
+ /** {@inheritDoc} */
+ public void writeZeroes(int count) {
+ if (count < 0) {
+ throw new IllegalArgumentException("count < 0");
+ }
+
+ int end = cursor + count;
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ /*
+ * There is no need to actually write zeroes, since the array is
+ * already preinitialized with zeroes.
+ */
+
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public void alignTo(int alignment) {
+ int mask = alignment - 1;
+
+ if ((alignment < 0) || ((mask & alignment) != 0)) {
+ throw new IllegalArgumentException("bogus alignment");
+ }
+
+ int end = (cursor + mask) & ~mask;
+
+ if (stretchy) {
+ ensureCapacity(end);
+ } else if (end > data.length) {
+ throwBounds();
+ return;
+ }
+
+ /*
+ * There is no need to actually write zeroes, since the array is
+ * already preinitialized with zeroes.
+ */
+
+ cursor = end;
+ }
+
+ /** {@inheritDoc} */
+ public boolean annotates() {
+ return (annotations != null);
+ }
+
+ /** {@inheritDoc} */
+ public boolean isVerbose() {
+ return verbose;
+ }
+
+ /** {@inheritDoc} */
+ public void annotate(String msg) {
+ if (annotations == null) {
+ return;
+ }
+
+ endAnnotation();
+ annotations.add(new Annotation(cursor, msg));
+ }
+
+ /** {@inheritDoc} */
+ public void annotate(int amt, String msg) {
+ if (annotations == null) {
+ return;
+ }
+
+ endAnnotation();
+
+ int asz = annotations.size();
+ int lastEnd = (asz == 0) ? 0 : annotations.get(asz - 1).getEnd();
+ int startAt;
+
+ if (lastEnd <= cursor) {
+ startAt = cursor;
+ } else {
+ startAt = lastEnd;
+ }
+
+ annotations.add(new Annotation(startAt, startAt + amt, msg));
+ }
+
+ /** {@inheritDoc} */
+ public void endAnnotation() {
+ if (annotations == null) {
+ return;
+ }
+
+ int sz = annotations.size();
+
+ if (sz != 0) {
+ annotations.get(sz - 1).setEndIfUnset(cursor);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public int getAnnotationWidth() {
+ int leftWidth = 8 + (hexCols * 2) + (hexCols / 2);
+
+ return annotationWidth - leftWidth;
+ }
+
+ /**
+ * Indicates that this instance should keep annotations. This method may
+ * be called only once per instance, and only before any data has been
+ * written to the it.
+ *
+ * @param annotationWidth {@code >= 40;} the desired maximum annotation width
+ * @param verbose whether or not to indicate verbose annotations
+ */
+ public void enableAnnotations(int annotationWidth, boolean verbose) {
+ if ((annotations != null) || (cursor != 0)) {
+ throw new RuntimeException("cannot enable annotations");
+ }
+
+ if (annotationWidth < 40) {
+ throw new IllegalArgumentException("annotationWidth < 40");
+ }
+
+ int hexCols = (((annotationWidth - 7) / 15) + 1) & ~1;
+ if (hexCols < 6) {
+ hexCols = 6;
+ } else if (hexCols > 10) {
+ hexCols = 10;
+ }
+
+ this.annotations = new ArrayList<Annotation>(1000);
+ this.annotationWidth = annotationWidth;
+ this.hexCols = hexCols;
+ this.verbose = verbose;
+ }
+
+ /**
+ * Finishes up annotation processing. This closes off any open
+ * annotations and removes annotations that don't refer to written
+ * data.
+ */
+ public void finishAnnotating() {
+ // Close off the final annotation, if any.
+ endAnnotation();
+
+ // Remove annotations that refer to unwritten data.
+ if (annotations != null) {
+ int asz = annotations.size();
+ while (asz > 0) {
+ Annotation last = annotations.get(asz - 1);
+ if (last.getStart() > cursor) {
+ annotations.remove(asz - 1);
+ asz--;
+ } else if (last.getEnd() > cursor) {
+ last.setEnd(cursor);
+ break;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Writes the annotated content of this instance to the given writer.
+ *
+ * @param out {@code non-null;} where to write to
+ */
+ public void writeAnnotationsTo(Writer out) throws IOException {
+ int width2 = getAnnotationWidth();
+ int width1 = annotationWidth - width2 - 1;
+
+ TwoColumnOutput twoc = new TwoColumnOutput(out, width1, width2, "|");
+ Writer left = twoc.getLeft();
+ Writer right = twoc.getRight();
+ int leftAt = 0; // left-hand byte output cursor
+ int rightAt = 0; // right-hand annotation index
+ int rightSz = annotations.size();
+
+ while ((leftAt < cursor) && (rightAt < rightSz)) {
+ Annotation a = annotations.get(rightAt);
+ int start = a.getStart();
+ int end;
+ String text;
+
+ if (leftAt < start) {
+ // This is an area with no annotation.
+ end = start;
+ start = leftAt;
+ text = "";
+ } else {
+ // This is an area with an annotation.
+ end = a.getEnd();
+ text = a.getText();
+ rightAt++;
+ }
+
+ left.write(Hex.dump(data, start, end - start, start, hexCols, 6));
+ right.write(text);
+ twoc.flush();
+ leftAt = end;
+ }
+
+ if (leftAt < cursor) {
+ // There is unannotated output at the end.
+ left.write(Hex.dump(data, leftAt, cursor - leftAt, leftAt,
+ hexCols, 6));
+ }
+
+ while (rightAt < rightSz) {
+ // There are zero-byte annotations at the end.
+ right.write(annotations.get(rightAt).getText());
+ rightAt++;
+ }
+
+ twoc.flush();
+ }
+
+ /**
+ * Throws the excpetion for when an attempt is made to write past the
+ * end of the instance.
+ */
+ private static void throwBounds() {
+ throw new IndexOutOfBoundsException("attempt to write past the end");
+ }
+
+ /**
+ * Reallocates the underlying array if necessary. Calls to this method
+ * should be guarded by a test of {@link #stretchy}.
+ *
+ * @param desiredSize {@code >= 0;} the desired minimum total size of the array
+ */
+ private void ensureCapacity(int desiredSize) {
+ if (data.length < desiredSize) {
+ byte[] newData = new byte[desiredSize * 2 + 1000];
+ System.arraycopy(data, 0, newData, 0, cursor);
+ data = newData;
+ }
+ }
+
+ /**
+ * Annotation on output.
+ */
+ private static class Annotation {
+ /** {@code >= 0;} start of annotated range (inclusive) */
+ private final int start;
+
+ /**
+ * {@code >= 0;} end of annotated range (exclusive);
+ * {@code Integer.MAX_VALUE} if unclosed
+ */
+ private int end;
+
+ /** {@code non-null;} annotation text */
+ private final String text;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param start {@code >= 0;} start of annotated range
+ * @param end {@code >= start;} end of annotated range (exclusive) or
+ * {@code Integer.MAX_VALUE} if unclosed
+ * @param text {@code non-null;} annotation text
+ */
+ public Annotation(int start, int end, String text) {
+ this.start = start;
+ this.end = end;
+ this.text = text;
+ }
+
+ /**
+ * Constructs an instance. It is initally unclosed.
+ *
+ * @param start {@code >= 0;} start of annotated range
+ * @param text {@code non-null;} annotation text
+ */
+ public Annotation(int start, String text) {
+ this(start, Integer.MAX_VALUE, text);
+ }
+
+ /**
+ * Sets the end as given, but only if the instance is unclosed;
+ * otherwise, do nothing.
+ *
+ * @param end {@code >= start;} the end
+ */
+ public void setEndIfUnset(int end) {
+ if (this.end == Integer.MAX_VALUE) {
+ this.end = end;
+ }
+ }
+
+ /**
+ * Sets the end as given.
+ *
+ * @param end {@code >= start;} the end
+ */
+ public void setEnd(int end) {
+ this.end = end;
+ }
+
+ /**
+ * Gets the start.
+ *
+ * @return the start
+ */
+ public int getStart() {
+ return start;
+ }
+
+ /**
+ * Gets the end.
+ *
+ * @return the end
+ */
+ public int getEnd() {
+ return end;
+ }
+
+ /**
+ * Gets the text.
+ *
+ * @return {@code non-null;} the text
+ */
+ public String getText() {
+ return text;
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/util/ExceptionWithContext.java b/dx/src/com/android/dx/util/ExceptionWithContext.java
new file mode 100644
index 0000000..7f8523c
--- /dev/null
+++ b/dx/src/com/android/dx/util/ExceptionWithContext.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+
+/**
+ * Exception which carries around structured context.
+ */
+public class ExceptionWithContext
+ extends RuntimeException {
+ /** {@code non-null;} human-oriented context of the exception */
+ private StringBuffer context;
+
+ /**
+ * Augments the given exception with the given context, and return the
+ * result. The result is either the given exception if it was an
+ * {@link ExceptionWithContext}, or a newly-constructed exception if it
+ * was not.
+ *
+ * @param ex {@code non-null;} the exception to augment
+ * @param str {@code non-null;} context to add
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static ExceptionWithContext withContext(Throwable ex, String str) {
+ ExceptionWithContext ewc;
+
+ if (ex instanceof ExceptionWithContext) {
+ ewc = (ExceptionWithContext) ex;
+ } else {
+ ewc = new ExceptionWithContext(ex);
+ }
+
+ ewc.addContext(str);
+ return ewc;
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param message human-oriented message
+ */
+ public ExceptionWithContext(String message) {
+ this(message, null);
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param cause {@code null-ok;} exception that caused this one
+ */
+ public ExceptionWithContext(Throwable cause) {
+ this(null, cause);
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param message human-oriented message
+ * @param cause {@code null-ok;} exception that caused this one
+ */
+ public ExceptionWithContext(String message, Throwable cause) {
+ super((message != null) ? message :
+ (cause != null) ? cause.getMessage() : null,
+ cause);
+
+ if (cause instanceof ExceptionWithContext) {
+ String ctx = ((ExceptionWithContext) cause).context.toString();
+ context = new StringBuffer(ctx.length() + 200);
+ context.append(ctx);
+ } else {
+ context = new StringBuffer(200);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void printStackTrace(PrintStream out) {
+ super.printStackTrace(out);
+ out.println(context);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void printStackTrace(PrintWriter out) {
+ super.printStackTrace(out);
+ out.println(context);
+ }
+
+ /**
+ * Adds a line of context to this instance.
+ *
+ * @param str {@code non-null;} new context
+ */
+ public void addContext(String str) {
+ if (str == null) {
+ throw new NullPointerException("str == null");
+ }
+
+ context.append(str);
+ if (!str.endsWith("\n")) {
+ context.append('\n');
+ }
+ }
+
+ /**
+ * Gets the context.
+ *
+ * @return {@code non-null;} the context
+ */
+ public String getContext() {
+ return context.toString();
+ }
+
+ /**
+ * Prints the message and context.
+ *
+ * @param out {@code non-null;} where to print to
+ */
+ public void printContext(PrintStream out) {
+ out.println(getMessage());
+ out.print(context);
+ }
+
+ /**
+ * Prints the message and context.
+ *
+ * @param out {@code non-null;} where to print to
+ */
+ public void printContext(PrintWriter out) {
+ out.println(getMessage());
+ out.print(context);
+ }
+}
diff --git a/dx/src/com/android/dx/util/FileUtils.java b/dx/src/com/android/dx/util/FileUtils.java
new file mode 100644
index 0000000..098c5ab
--- /dev/null
+++ b/dx/src/com/android/dx/util/FileUtils.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+
+/**
+ * File I/O utilities.
+ */
+public final class FileUtils {
+ /**
+ * This class is uninstantiable.
+ */
+ private FileUtils() {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Reads the named file, translating {@link IOException} to a
+ * {@link RuntimeException} of some sort.
+ *
+ * @param fileName {@code non-null;} name of the file to read
+ * @return {@code non-null;} contents of the file
+ */
+ public static byte[] readFile(String fileName) {
+ File file = new File(fileName);
+ return readFile(file);
+ }
+
+ /**
+ * Reads the given file, translating {@link IOException} to a
+ * {@link RuntimeException} of some sort.
+ *
+ * @param file {@code non-null;} the file to read
+ * @return {@code non-null;} contents of the file
+ */
+ public static byte[] readFile(File file) {
+ if (!file.exists()) {
+ throw new RuntimeException(file + ": file not found");
+ }
+
+ if (!file.isFile()) {
+ throw new RuntimeException(file + ": not a file");
+ }
+
+ if (!file.canRead()) {
+ throw new RuntimeException(file + ": file not readable");
+ }
+
+ long longLength = file.length();
+ int length = (int) longLength;
+ if (length != longLength) {
+ throw new RuntimeException(file + ": file too long");
+ }
+
+ byte[] result = new byte[length];
+
+ try {
+ FileInputStream in = new FileInputStream(file);
+ int at = 0;
+ while (length > 0) {
+ int amt = in.read(result, at, length);
+ if (amt == -1) {
+ throw new RuntimeException(file + ": unexpected EOF");
+ }
+ at += amt;
+ length -= amt;
+ }
+ in.close();
+ } catch (IOException ex) {
+ throw new RuntimeException(file + ": trouble reading", ex);
+ }
+
+ return result;
+ }
+}
diff --git a/dx/src/com/android/dx/util/FixedSizeList.java b/dx/src/com/android/dx/util/FixedSizeList.java
new file mode 100644
index 0000000..fb3af04
--- /dev/null
+++ b/dx/src/com/android/dx/util/FixedSizeList.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+import java.util.Arrays;
+
+/**
+ * Simple (mostly) fixed-size list of objects, which may be made immutable.
+ */
+public class FixedSizeList
+ extends MutabilityControl implements ToHuman {
+ /** {@code non-null;} array of elements */
+ private Object[] arr;
+
+ /**
+ * Constructs an instance. All indices initially contain {@code null}.
+ *
+ * @param size the size of the list
+ */
+ public FixedSizeList(int size) {
+ super(size != 0);
+
+ try {
+ arr = new Object[size];
+ } catch (NegativeArraySizeException ex) {
+ // Translate the exception.
+ throw new IllegalArgumentException("size < 0");
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ // Easy out.
+ return true;
+ }
+
+ if ((other == null) || (getClass() != other.getClass())) {
+ // Another easy out.
+ return false;
+ }
+
+ FixedSizeList list = (FixedSizeList) other;
+ return Arrays.equals(arr, list.arr);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(arr);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ String name = getClass().getName();
+
+ return toString0(name.substring(name.lastIndexOf('.') + 1) + '{',
+ ", ",
+ "}",
+ false);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * This method will only work if every element of the list
+ * implements {@link ToHuman}.
+ */
+ public String toHuman() {
+ String name = getClass().getName();
+
+ return toString0(name.substring(name.lastIndexOf('.') + 1) + '{',
+ ", ",
+ "}",
+ true);
+ }
+
+ /**
+ * Gets a customized string form for this instance.
+ *
+ * @param prefix {@code null-ok;} prefix for the start of the result
+ * @param separator {@code null-ok;} separator to insert between each item
+ * @param suffix {@code null-ok;} suffix for the end of the result
+ * @return {@code non-null;} the custom string
+ */
+ public String toString(String prefix, String separator, String suffix) {
+ return toString0(prefix, separator, suffix, false);
+ }
+
+ /**
+ * Gets a customized human string for this instance. This method will
+ * only work if every element of the list implements {@link
+ * ToHuman}.
+ *
+ * @param prefix {@code null-ok;} prefix for the start of the result
+ * @param separator {@code null-ok;} separator to insert between each item
+ * @param suffix {@code null-ok;} suffix for the end of the result
+ * @return {@code non-null;} the custom string
+ */
+ public String toHuman(String prefix, String separator, String suffix) {
+ return toString0(prefix, separator, suffix, true);
+ }
+
+ /**
+ * Gets the number of elements in this list.
+ */
+ public final int size() {
+ return arr.length;
+ }
+
+ /**
+ * Shrinks this instance to fit, by removing any unset
+ * ({@code null}) elements, leaving the remaining elements in
+ * their original order.
+ */
+ public void shrinkToFit() {
+ int sz = arr.length;
+ int newSz = 0;
+
+ for (int i = 0; i < sz; i++) {
+ if (arr[i] != null) {
+ newSz++;
+ }
+ }
+
+ if (sz == newSz) {
+ return;
+ }
+
+ throwIfImmutable();
+
+ Object[] newa = new Object[newSz];
+ int at = 0;
+
+ for (int i = 0; i < sz; i++) {
+ Object one = arr[i];
+ if (one != null) {
+ newa[at] = one;
+ at++;
+ }
+ }
+
+ arr = newa;
+ if (newSz == 0) {
+ setImmutable();
+ }
+ }
+
+ /**
+ * Gets the indicated element. It is an error to call this with the
+ * index for an element which was never set; if you do that, this
+ * will throw {@code NullPointerException}. This method is
+ * protected so that subclasses may offer a safe type-checked
+ * public interface to their clients.
+ *
+ * @param n {@code >= 0, < size();} which element
+ * @return {@code non-null;} the indicated element
+ */
+ protected final Object get0(int n) {
+ try {
+ Object result = arr[n];
+
+ if (result == null) {
+ throw new NullPointerException("unset: " + n);
+ }
+
+ return result;
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ // Translate the exception.
+ return throwIndex(n);
+ }
+ }
+
+ /**
+ * Gets the indicated element, allowing {@code null}s to be
+ * returned. This method is protected so that subclasses may
+ * (optionally) offer a safe type-checked public interface to
+ * their clients.
+ *
+ * @param n {@code >= 0, < size();} which element
+ * @return {@code null-ok;} the indicated element
+ */
+ protected final Object getOrNull0(int n) {
+ return arr[n];
+ }
+
+ /**
+ * Sets the element at the given index, but without doing any type
+ * checks on the element. This method is protected so that
+ * subclasses may offer a safe type-checked public interface to
+ * their clients.
+ *
+ * @param n {@code >= 0, < size();} which element
+ * @param obj {@code null-ok;} the value to store
+ */
+ protected final void set0(int n, Object obj) {
+ throwIfImmutable();
+
+ try {
+ arr[n] = obj;
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ // Translate the exception.
+ throwIndex(n);
+ }
+ }
+
+ /**
+ * Throws the appropriate exception for the given index value.
+ *
+ * @param n the index value
+ * @return never
+ * @throws IndexOutOfBoundsException always thrown
+ */
+ private Object throwIndex(int n) {
+ if (n < 0) {
+ throw new IndexOutOfBoundsException("n < 0");
+ }
+
+ throw new IndexOutOfBoundsException("n >= size()");
+ }
+
+ /**
+ * Helper for {@link #toString} and {@link #toHuman}, which both of
+ * those call to pretty much do everything.
+ *
+ * @param prefix {@code null-ok;} prefix for the start of the result
+ * @param separator {@code null-ok;} separator to insert between each item
+ * @param suffix {@code null-ok;} suffix for the end of the result
+ * @param human whether the output is to be human
+ * @return {@code non-null;} the custom string
+ */
+ private String toString0(String prefix, String separator, String suffix,
+ boolean human) {
+ int len = arr.length;
+ StringBuffer sb = new StringBuffer(len * 10 + 10);
+
+ if (prefix != null) {
+ sb.append(prefix);
+ }
+
+ for (int i = 0; i < len; i++) {
+ if ((i != 0) && (separator != null)) {
+ sb.append(separator);
+ }
+
+ if (human) {
+ sb.append(((ToHuman) arr[i]).toHuman());
+ } else {
+ sb.append(arr[i]);
+ }
+ }
+
+ if (suffix != null) {
+ sb.append(suffix);
+ }
+
+ return sb.toString();
+ }
+
+}
diff --git a/dx/src/com/android/dx/util/Hex.java b/dx/src/com/android/dx/util/Hex.java
new file mode 100644
index 0000000..e95669c
--- /dev/null
+++ b/dx/src/com/android/dx/util/Hex.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+/**
+ * Utilities for formatting numbers as hexadecimal.
+ */
+public final class Hex {
+ /**
+ * This class is uninstantiable.
+ */
+ private Hex() {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Formats a {@code long} as an 8-byte unsigned hex value.
+ *
+ * @param v value to format
+ * @return {@code non-null;} formatted form
+ */
+ public static String u8(long v) {
+ char[] result = new char[16];
+ for (int i = 0; i < 16; i++) {
+ result[15 - i] = Character.forDigit((int) v & 0x0f, 16);
+ v >>= 4;
+ }
+
+ return new String(result);
+ }
+
+ /**
+ * Formats an {@code int} as a 4-byte unsigned hex value.
+ *
+ * @param v value to format
+ * @return {@code non-null;} formatted form
+ */
+ public static String u4(int v) {
+ char[] result = new char[8];
+ for (int i = 0; i < 8; i++) {
+ result[7 - i] = Character.forDigit(v & 0x0f, 16);
+ v >>= 4;
+ }
+
+ return new String(result);
+ }
+
+ /**
+ * Formats an {@code int} as a 3-byte unsigned hex value.
+ *
+ * @param v value to format
+ * @return {@code non-null;} formatted form
+ */
+ public static String u3(int v) {
+ char[] result = new char[6];
+ for (int i = 0; i < 6; i++) {
+ result[5 - i] = Character.forDigit(v & 0x0f, 16);
+ v >>= 4;
+ }
+
+ return new String(result);
+ }
+
+ /**
+ * Formats an {@code int} as a 2-byte unsigned hex value.
+ *
+ * @param v value to format
+ * @return {@code non-null;} formatted form
+ */
+ public static String u2(int v) {
+ char[] result = new char[4];
+ for (int i = 0; i < 4; i++) {
+ result[3 - i] = Character.forDigit(v & 0x0f, 16);
+ v >>= 4;
+ }
+
+ return new String(result);
+ }
+
+ /**
+ * Formats an {@code int} as either a 2-byte unsigned hex value
+ * (if the value is small enough) or a 4-byte unsigned hex value (if
+ * not).
+ *
+ * @param v value to format
+ * @return {@code non-null;} formatted form
+ */
+ public static String u2or4(int v) {
+ if (v == (char) v) {
+ return u2(v);
+ } else {
+ return u4(v);
+ }
+ }
+
+ /**
+ * Formats an {@code int} as a 1-byte unsigned hex value.
+ *
+ * @param v value to format
+ * @return {@code non-null;} formatted form
+ */
+ public static String u1(int v) {
+ char[] result = new char[2];
+ for (int i = 0; i < 2; i++) {
+ result[1 - i] = Character.forDigit(v & 0x0f, 16);
+ v >>= 4;
+ }
+
+ return new String(result);
+ }
+
+ /**
+ * Formats an {@code int} as a 4-bit unsigned hex nibble.
+ *
+ * @param v value to format
+ * @return {@code non-null;} formatted form
+ */
+ public static String uNibble(int v) {
+ char[] result = new char[1];
+
+ result[0] = Character.forDigit(v & 0x0f, 16);
+ return new String(result);
+ }
+
+ /**
+ * Formats a {@code long} as an 8-byte signed hex value.
+ *
+ * @param v value to format
+ * @return {@code non-null;} formatted form
+ */
+ public static String s8(long v) {
+ char[] result = new char[17];
+
+ if (v < 0) {
+ result[0] = '-';
+ v = -v;
+ } else {
+ result[0] = '+';
+ }
+
+ for (int i = 0; i < 16; i++) {
+ result[16 - i] = Character.forDigit((int) v & 0x0f, 16);
+ v >>= 4;
+ }
+
+ return new String(result);
+ }
+
+ /**
+ * Formats an {@code int} as a 4-byte signed hex value.
+ *
+ * @param v value to format
+ * @return {@code non-null;} formatted form
+ */
+ public static String s4(int v) {
+ char[] result = new char[9];
+
+ if (v < 0) {
+ result[0] = '-';
+ v = -v;
+ } else {
+ result[0] = '+';
+ }
+
+ for (int i = 0; i < 8; i++) {
+ result[8 - i] = Character.forDigit(v & 0x0f, 16);
+ v >>= 4;
+ }
+
+ return new String(result);
+ }
+
+ /**
+ * Formats an {@code int} as a 2-byte signed hex value.
+ *
+ * @param v value to format
+ * @return {@code non-null;} formatted form
+ */
+ public static String s2(int v) {
+ char[] result = new char[5];
+
+ if (v < 0) {
+ result[0] = '-';
+ v = -v;
+ } else {
+ result[0] = '+';
+ }
+
+ for (int i = 0; i < 4; i++) {
+ result[4 - i] = Character.forDigit(v & 0x0f, 16);
+ v >>= 4;
+ }
+
+ return new String(result);
+ }
+
+ /**
+ * Formats an {@code int} as a 1-byte signed hex value.
+ *
+ * @param v value to format
+ * @return {@code non-null;} formatted form
+ */
+ public static String s1(int v) {
+ char[] result = new char[3];
+
+ if (v < 0) {
+ result[0] = '-';
+ v = -v;
+ } else {
+ result[0] = '+';
+ }
+
+ for (int i = 0; i < 2; i++) {
+ result[2 - i] = Character.forDigit(v & 0x0f, 16);
+ v >>= 4;
+ }
+
+ return new String(result);
+ }
+
+ /**
+ * Formats a hex dump of a portion of a {@code byte[]}. The result
+ * is always newline-terminated, unless the passed-in length was zero,
+ * in which case the result is always the empty string ({@code ""}).
+ *
+ * @param arr {@code non-null;} array to format
+ * @param offset {@code >= 0;} offset to the part to dump
+ * @param length {@code >= 0;} number of bytes to dump
+ * @param outOffset {@code >= 0;} first output offset to print
+ * @param bpl {@code >= 0;} number of bytes of output per line
+ * @param addressLength {@code {2,4,6,8};} number of characters for each address
+ * header
+ * @return {@code non-null;} a string of the dump
+ */
+ public static String dump(byte[] arr, int offset, int length,
+ int outOffset, int bpl, int addressLength) {
+ int end = offset + length;
+
+ // twos-complement math trick: ((x < 0) || (y < 0)) <=> ((x|y) < 0)
+ if (((offset | length | end) < 0) || (end > arr.length)) {
+ throw new IndexOutOfBoundsException("arr.length " +
+ arr.length + "; " +
+ offset + "..!" + end);
+ }
+
+ if (outOffset < 0) {
+ throw new IllegalArgumentException("outOffset < 0");
+ }
+
+ if (length == 0) {
+ return "";
+ }
+
+ StringBuffer sb = new StringBuffer(length * 4 + 6);
+ boolean bol = true;
+ int col = 0;
+
+ while (length > 0) {
+ if (col == 0) {
+ String astr;
+ switch (addressLength) {
+ case 2: astr = Hex.u1(outOffset); break;
+ case 4: astr = Hex.u2(outOffset); break;
+ case 6: astr = Hex.u3(outOffset); break;
+ default: astr = Hex.u4(outOffset); break;
+ }
+ sb.append(astr);
+ sb.append(": ");
+ } else if ((col & 1) == 0) {
+ sb.append(' ');
+ }
+ sb.append(Hex.u1(arr[offset]));
+ outOffset++;
+ offset++;
+ col++;
+ if (col == bpl) {
+ sb.append('\n');
+ col = 0;
+ }
+ length--;
+ }
+
+ if (col != 0) {
+ sb.append('\n');
+ }
+
+ return sb.toString();
+ }
+}
diff --git a/dx/src/com/android/dx/util/HexParser.java b/dx/src/com/android/dx/util/HexParser.java
new file mode 100644
index 0000000..1b34345
--- /dev/null
+++ b/dx/src/com/android/dx/util/HexParser.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+/**
+ * Utilities for parsing hexadecimal text.
+ */
+public final class HexParser {
+ /**
+ * This class is uninstantiable.
+ */
+ private HexParser() {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Parses the given text as hex, returning a {@code byte[]}
+ * corresponding to the text. The format is simple: Each line may
+ * start with a hex offset followed by a colon (which is verified
+ * and presumably used just as a comment), and then consists of
+ * hex digits freely interspersed with whitespace. If a pound sign
+ * is encountered, it and the rest of the line are ignored as a
+ * comment. If a double quote is encountered, then the ASCII value
+ * of the subsequent characters is used, until the next double
+ * quote. Quoted strings may not span multiple lines.
+ *
+ * @param src {@code non-null;} the source string
+ * @return {@code non-null;} the parsed form
+ */
+ public static byte[] parse(String src) {
+ int len = src.length();
+ byte[] result = new byte[len / 2];
+ int at = 0;
+ int outAt = 0;
+
+ while (at < len) {
+ int nlAt = src.indexOf('\n', at);
+ if (nlAt < 0) {
+ nlAt = len;
+ }
+ int poundAt = src.indexOf('#', at);
+
+ String line;
+ if ((poundAt >= 0) && (poundAt < nlAt)) {
+ line = src.substring(at, poundAt);
+ } else {
+ line = src.substring(at, nlAt);
+ }
+ at = nlAt + 1;
+
+ int colonAt = line.indexOf(':');
+
+ atCheck:
+ if (colonAt != -1) {
+ int quoteAt = line.indexOf('\"');
+ if ((quoteAt != -1) && (quoteAt < colonAt)) {
+ break atCheck;
+ }
+
+ String atStr = line.substring(0, colonAt).trim();
+ line = line.substring(colonAt + 1);
+ int alleged = Integer.parseInt(atStr, 16);
+ if (alleged != outAt) {
+ throw new RuntimeException("bogus offset marker: " +
+ atStr);
+ }
+ }
+
+ int lineLen = line.length();
+ int value = -1;
+ boolean quoteMode = false;
+
+ for (int i = 0; i < lineLen; i++) {
+ char c = line.charAt(i);
+
+ if (quoteMode) {
+ if (c == '\"') {
+ quoteMode = false;
+ } else {
+ result[outAt] = (byte) c;
+ outAt++;
+ }
+ continue;
+ }
+
+ if (c <= ' ') {
+ continue;
+ }
+ if (c == '\"') {
+ if (value != -1) {
+ throw new RuntimeException("spare digit around " +
+ "offset " + Hex.u4(outAt));
+ }
+ quoteMode = true;
+ continue;
+ }
+
+ int digVal = Character.digit(c, 16);
+ if (digVal == -1) {
+ throw new RuntimeException("bogus digit character: \"" +
+ c + "\"");
+ }
+ if (value == -1) {
+ value = digVal;
+ } else {
+ result[outAt] = (byte) ((value << 4) | digVal);
+ outAt++;
+ value = -1;
+ }
+ }
+
+ if (value != -1) {
+ throw new RuntimeException("spare digit around offset " +
+ Hex.u4(outAt));
+ }
+
+ if (quoteMode) {
+ throw new RuntimeException("unterminated quote around " +
+ "offset " + Hex.u4(outAt));
+ }
+ }
+
+ if (outAt < result.length) {
+ byte[] newr = new byte[outAt];
+ System.arraycopy(result, 0, newr, 0, outAt);
+ result = newr;
+ }
+
+ return result;
+ }
+}
diff --git a/dx/src/com/android/dx/util/IndentingWriter.java b/dx/src/com/android/dx/util/IndentingWriter.java
new file mode 100644
index 0000000..3424e37
--- /dev/null
+++ b/dx/src/com/android/dx/util/IndentingWriter.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+import java.io.FilterWriter;
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * Writer that wraps another writer and passes width-limited and
+ * optionally-prefixed output to its subordinate. When lines are
+ * wrapped they are automatically indented based on the start of the
+ * line.
+ */
+public final class IndentingWriter extends FilterWriter {
+ /** {@code null-ok;} optional prefix for every line */
+ private final String prefix;
+
+ /** {@code > 0;} the maximum output width */
+ private final int width;
+
+ /** {@code > 0;} the maximum indent */
+ private final int maxIndent;
+
+ /** {@code >= 0;} current output column (zero-based) */
+ private int column;
+
+ /** whether indent spaces are currently being collected */
+ private boolean collectingIndent;
+
+ /** {@code >= 0;} current indent amount */
+ private int indent;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param out {@code non-null;} writer to send final output to
+ * @param width {@code >= 0;} the maximum output width (not including
+ * {@code prefix}), or {@code 0} for no maximum
+ * @param prefix {@code non-null;} the prefix for each line
+ */
+ public IndentingWriter(Writer out, int width, String prefix) {
+ super(out);
+
+ if (out == null) {
+ throw new NullPointerException("out == null");
+ }
+
+ if (width < 0) {
+ throw new IllegalArgumentException("width < 0");
+ }
+
+ if (prefix == null) {
+ throw new NullPointerException("prefix == null");
+ }
+
+ this.width = (width != 0) ? width : Integer.MAX_VALUE;
+ this.maxIndent = width >> 1;
+ this.prefix = (prefix.length() == 0) ? null : prefix;
+
+ bol();
+ }
+
+ /**
+ * Constructs a no-prefix instance.
+ *
+ * @param out {@code non-null;} writer to send final output to
+ * @param width {@code >= 0;} the maximum output width (not including
+ * {@code prefix}), or {@code 0} for no maximum
+ */
+ public IndentingWriter(Writer out, int width) {
+ this(out, width, "");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void write(int c) throws IOException {
+ synchronized (lock) {
+ if (collectingIndent) {
+ if (c == ' ') {
+ indent++;
+ if (indent >= maxIndent) {
+ indent = maxIndent;
+ collectingIndent = false;
+ }
+ } else {
+ collectingIndent = false;
+ }
+ }
+
+ if ((column == width) && (c != '\n')) {
+ out.write('\n');
+ column = 0;
+ /*
+ * Note: No else, so this should fall through to the next
+ * if statement.
+ */
+ }
+
+ if (column == 0) {
+ if (prefix != null) {
+ out.write(prefix);
+ }
+
+ if (!collectingIndent) {
+ for (int i = 0; i < indent; i++) {
+ out.write(' ');
+ }
+ column = indent;
+ }
+ }
+
+ out.write(c);
+
+ if (c == '\n') {
+ bol();
+ } else {
+ column++;
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void write(char[] cbuf, int off, int len) throws IOException {
+ synchronized (lock) {
+ while (len > 0) {
+ write(cbuf[off]);
+ off++;
+ len--;
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void write(String str, int off, int len) throws IOException {
+ synchronized (lock) {
+ while (len > 0) {
+ write(str.charAt(off));
+ off++;
+ len--;
+ }
+ }
+ }
+
+ /**
+ * Indicates that output is at the beginning of a line.
+ */
+ private void bol() {
+ column = 0;
+ collectingIndent = (maxIndent != 0);
+ indent = 0;
+ }
+}
diff --git a/dx/src/com/android/dx/util/IntIterator.java b/dx/src/com/android/dx/util/IntIterator.java
new file mode 100644
index 0000000..4caa439
--- /dev/null
+++ b/dx/src/com/android/dx/util/IntIterator.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+/**
+ * An iterator for a list of ints.
+ */
+public interface IntIterator {
+
+ /**
+ * Checks to see if the iterator has a next value.
+ *
+ * @return true if next() will succeed
+ */
+ boolean hasNext();
+
+ /**
+ * Returns the next value in the iterator.
+ *
+ * @return next value
+ * @throws java.util.NoSuchElementException if no next element exists
+ */
+ int next();
+}
diff --git a/dx/src/com/android/dx/util/IntList.java b/dx/src/com/android/dx/util/IntList.java
new file mode 100644
index 0000000..089fead
--- /dev/null
+++ b/dx/src/com/android/dx/util/IntList.java
@@ -0,0 +1,452 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+import java.util.Arrays;
+
+/**
+ * Simple list of {@code int}s.
+ */
+public final class IntList extends MutabilityControl {
+ /** {@code non-null;} immutable, no-element instance */
+ public static final IntList EMPTY = new IntList(0);
+
+ /** {@code non-null;} array of elements */
+ private int[] values;
+
+ /** {@code >= 0;} current size of the list */
+ private int size;
+
+ /** whether the values are currently sorted */
+ private boolean sorted;
+
+ static {
+ EMPTY.setImmutable();
+ }
+
+ /**
+ * Constructs a new immutable instance with the given element.
+ *
+ * @param value the sole value in the list
+ */
+ public static IntList makeImmutable(int value) {
+ IntList result = new IntList(1);
+
+ result.add(value);
+ result.setImmutable();
+
+ return result;
+ }
+
+ /**
+ * Constructs a new immutable instance with the given elements.
+ *
+ * @param value0 the first value in the list
+ * @param value1 the second value in the list
+ */
+ public static IntList makeImmutable(int value0, int value1) {
+ IntList result = new IntList(2);
+
+ result.add(value0);
+ result.add(value1);
+ result.setImmutable();
+
+ return result;
+ }
+
+ /**
+ * Constructs an empty instance with a default initial capacity.
+ */
+ public IntList() {
+ this(4);
+ }
+
+ /**
+ * Constructs an empty instance.
+ *
+ * @param initialCapacity {@code >= 0;} initial capacity of the list
+ */
+ public IntList(int initialCapacity) {
+ super(true);
+
+ try {
+ values = new int[initialCapacity];
+ } catch (NegativeArraySizeException ex) {
+ // Translate the exception.
+ throw new IllegalArgumentException("size < 0");
+ }
+
+ size = 0;
+ sorted = true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ int result = 0;
+
+ for (int i = 0; i < size; i++) {
+ result = (result * 31) + values[i];
+ }
+
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (! (other instanceof IntList)) {
+ return false;
+ }
+
+ IntList otherList = (IntList) other;
+
+ if (sorted != otherList.sorted) {
+ return false;
+ }
+
+ if (size != otherList.size) {
+ return false;
+ }
+
+ for (int i = 0; i < size; i++) {
+ if (values[i] != otherList.values[i]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer(size * 5 + 10);
+
+ sb.append('{');
+
+ for (int i = 0; i < size; i++) {
+ if (i != 0) {
+ sb.append(", ");
+ }
+ sb.append(values[i]);
+ }
+
+ sb.append('}');
+
+ return sb.toString();
+ }
+
+ /**
+ * Gets the number of elements in this list.
+ */
+ public int size() {
+ return size;
+ }
+
+ /**
+ * Gets the indicated value.
+ *
+ * @param n {@code >= 0, < size();} which element
+ * @return the indicated element's value
+ */
+ public int get(int n) {
+ if (n >= size) {
+ throw new IndexOutOfBoundsException("n >= size()");
+ }
+
+ try {
+ return values[n];
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ // Translate exception.
+ throw new IndexOutOfBoundsException("n < 0");
+ }
+ }
+
+ /**
+ * Sets the value at the given index.
+ *
+ * @param n {@code >= 0, < size();} which element
+ * @param value value to store
+ */
+ public void set(int n, int value) {
+ throwIfImmutable();
+
+ if (n >= size) {
+ throw new IndexOutOfBoundsException("n >= size()");
+ }
+
+ try {
+ values[n] = value;
+ sorted = false;
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ // Translate the exception.
+ if (n < 0) {
+ throw new IllegalArgumentException("n < 0");
+ }
+ }
+ }
+
+ /**
+ * Adds an element to the end of the list. This will increase the
+ * list's capacity if necessary.
+ *
+ * @param value the value to add
+ */
+ public void add(int value) {
+ throwIfImmutable();
+
+ growIfNeeded();
+
+ values[size++] = value;
+
+ if (sorted && (size > 1)) {
+ sorted = (value >= values[size - 2]);
+ }
+ }
+
+ /**
+ * Inserts element into specified index, moving elements at and above
+ * that index up one. May not be used to insert at an index beyond the
+ * current size (that is, insertion as a last element is legal but
+ * no further).
+ *
+ * @param n {@code >= 0, <=size();} index of where to insert
+ * @param value value to insert
+ */
+ public void insert(int n, int value) {
+ if (n > size) {
+ throw new IndexOutOfBoundsException("n > size()");
+ }
+
+ growIfNeeded();
+
+ System.arraycopy (values, n, values, n+1, size - n);
+ values[n] = value;
+ size++;
+
+ sorted = sorted
+ && (n == 0 || value > values[n-1])
+ && (n == (size - 1) || value < values[n+1]);
+ }
+
+ /**
+ * Removes an element at a given index, shifting elements at greater
+ * indicies down one.
+ *
+ * @param n {@code >=0, < size();} index of element to remove
+ */
+ public void removeIndex(int n) {
+ if (n >= size) {
+ throw new IndexOutOfBoundsException("n >= size()");
+ }
+
+ System.arraycopy (values, n + 1, values, n, size - n - 1);
+ size--;
+
+ // sort status is unchanged
+ }
+
+ /**
+ * Increases size of array if needed
+ */
+ private void growIfNeeded() {
+ if (size == values.length) {
+ // Resize.
+ int[] newv = new int[size * 3 / 2 + 10];
+ System.arraycopy(values, 0, newv, 0, size);
+ values = newv;
+ }
+ }
+
+ /**
+ * Returns the last element in the array without modifying the array
+ *
+ * @return last value in the array.
+ * @exception IndexOutOfBoundsException if stack is empty.
+ */
+ public int top() {
+ return get(size - 1);
+ }
+
+ /**
+ * Pops an element off the end of the list and decreasing the size by one.
+ *
+ * @return value from what was the last element.
+ * @exception IndexOutOfBoundsException if stack is empty.
+ */
+ public int pop() {
+ throwIfImmutable();
+
+ int result;
+
+ result = get(size-1);
+ size--;
+
+ return result;
+ }
+
+ /**
+ * Pops N elements off the end of the list and decreasing the size by N.
+ *
+ * @param n {@code >= 0;} number of elements to remove from end.
+ * @exception IndexOutOfBoundsException if stack is smaller than N
+ */
+ public void pop(int n) {
+ throwIfImmutable();
+
+ size -= n;
+ }
+
+ /**
+ * Shrinks the size of the list.
+ *
+ * @param newSize {@code >= 0;} the new size
+ */
+ public void shrink(int newSize) {
+ if (newSize < 0) {
+ throw new IllegalArgumentException("newSize < 0");
+ }
+
+ if (newSize > size) {
+ throw new IllegalArgumentException("newSize > size");
+ }
+
+ throwIfImmutable();
+
+ size = newSize;
+ }
+
+ /**
+ * Makes and returns a mutable copy of the list.
+ *
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public IntList mutableCopy() {
+ int sz = size;
+ IntList result = new IntList(sz);
+
+ for (int i = 0; i < sz; i++) {
+ result.add(values[i]);
+ }
+
+ return result;
+ }
+
+ /**
+ * Sorts the elements in the list in-place.
+ */
+ public void sort() {
+ throwIfImmutable();
+
+ if (!sorted) {
+ Arrays.sort(values, 0, size);
+ sorted = true;
+ }
+ }
+
+ /**
+ * Returns the index of the given value, or -1 if the value does not
+ * appear in the list. This will do a binary search if the list is
+ * sorted or a linear search if not.
+ * @param value value to find
+ * @return index of value or -1
+ */
+ public int indexOf(int value) {
+ int ret = binarysearch(value);
+
+ return ret >= 0 ? ret : -1;
+
+ }
+
+ /**
+ * Performs a binary search on a sorted list, returning the index of
+ * the given value if it is present or
+ * {@code (-(insertion point) - 1)} if the value is not present.
+ * If the list is not sorted, then reverts to linear search and returns
+ * {@code -size()} if the element is not found.
+ *
+ * @param value value to find
+ * @return index of value or {@code (-(insertion point) - 1)} if the
+ * value is not present
+ */
+ public int binarysearch(int value) {
+ int sz = size;
+
+ if (!sorted) {
+ // Linear search.
+ for (int i = 0; i < sz; i++) {
+ if (values[i] == value) {
+ return i;
+ }
+ }
+
+ return -sz;
+ }
+
+ /*
+ * Binary search. This variant does only one value comparison
+ * per iteration but does one more iteration on average than
+ * the variant that includes a value equality check per
+ * iteration.
+ */
+
+ int min = -1;
+ int max = sz;
+
+ while (max > (min + 1)) {
+ /*
+ * The guessIdx calculation is equivalent to ((min + max)
+ * / 2) but won't go wonky when min and max are close to
+ * Integer.MAX_VALUE.
+ */
+ int guessIdx = min + ((max - min) >> 1);
+ int guess = values[guessIdx];
+
+ if (value <= guess) {
+ max = guessIdx;
+ } else {
+ min = guessIdx;
+ }
+ }
+
+ if ((max != sz)) {
+ return (value == values[max]) ? max : (-max - 1);
+ } else {
+ return -sz - 1;
+ }
+ }
+
+
+ /**
+ * Returns whether or not the given value appears in the list.
+ * This will do a binary search if the list is sorted or a linear
+ * search if not.
+ *
+ * @see #sort
+ *
+ * @param value value to look for
+ * @return whether the list contains the given value
+ */
+ public boolean contains(int value) {
+ return indexOf(value) >= 0;
+ }
+}
diff --git a/dx/src/com/android/dx/util/IntSet.java b/dx/src/com/android/dx/util/IntSet.java
new file mode 100644
index 0000000..33b6bdd
--- /dev/null
+++ b/dx/src/com/android/dx/util/IntSet.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+/**
+ * A set of integers
+ */
+public interface IntSet {
+
+ /**
+ * Adds an int to a set
+ *
+ * @param value int to add
+ */
+ void add(int value);
+
+ /**
+ * Removes an int from a set.
+ *
+ * @param value int to remove
+ */
+ void remove(int value);
+
+ /**
+ * Checks to see if a value is in the set
+ *
+ * @param value int to check
+ * @return true if in set
+ */
+ boolean has(int value);
+
+ /**
+ * Merges {@code other} into this set, so this set becomes the
+ * union of the two.
+ *
+ * @param other {@code non-null;} other set to merge with.
+ */
+ void merge(IntSet other);
+
+ /**
+ * Returns the count of unique elements in this set.
+ *
+ * @return {@code > = 0;} count of unique elements
+ */
+ int elements();
+
+ /**
+ * Iterates the set
+ *
+ * @return {@code non-null;} a set iterator
+ */
+ IntIterator iterator();
+}
diff --git a/dx/src/com/android/dx/util/LabeledItem.java b/dx/src/com/android/dx/util/LabeledItem.java
new file mode 100644
index 0000000..b4856cf
--- /dev/null
+++ b/dx/src/com/android/dx/util/LabeledItem.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+/**
+ * An item that has an integer label.
+ */
+public interface LabeledItem {
+
+ /*
+ * Gets the label of this block.
+ *
+ * @return {@code >= 0;} the label
+ */
+ public int getLabel();
+}
diff --git a/dx/src/com/android/dx/util/LabeledList.java b/dx/src/com/android/dx/util/LabeledList.java
new file mode 100644
index 0000000..5b6e125
--- /dev/null
+++ b/dx/src/com/android/dx/util/LabeledList.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+import com.android.dx.cf.code.ByteBlock;
+
+/**
+ * A list of labeled items, allowing easy lookup by label.
+ */
+public class LabeledList extends FixedSizeList {
+
+ /**
+ * Sparse array indexed by label to FixedSizeList index.
+ * -1 = invalid label.
+ */
+ private final IntList labelToIndex;
+
+ /** @inheritDoc */
+ public LabeledList(int size) {
+ super(size);
+
+ labelToIndex = new IntList(size);
+ }
+
+ /**
+ * Constructs a new instance that is a copy of the old instance.
+ *
+ * @param old instance to copy
+ */
+ protected LabeledList(LabeledList old) {
+ super(old.size());
+ labelToIndex = old.labelToIndex.mutableCopy();
+
+ int sz = old.size();
+
+ for (int i = 0; i < sz; i++) {
+ Object one = old.get0(i);
+ if (one != null) {
+ set0(i, one);
+ }
+ }
+ }
+
+ /**
+ * Gets the maximum label (exclusive) of any block added to this instance.
+ *
+ * @return {@code >= 0;} the maximum label
+ */
+ public int getMaxLabel() {
+ int sz = labelToIndex.size();
+
+ // Gobble any deleted labels that may be at the end...
+ int i;
+ for (i = sz - 1; (i >= 0) && (labelToIndex.get(i) < 0); i--)
+ ;
+
+ int newSize = i+1;
+
+ labelToIndex.shrink(newSize);
+
+ return newSize;
+ }
+
+ /**
+ * Removes a label from the label-to-index mapping
+ * @param oldLabel label to remove
+ */
+ protected void removeLabel(int oldLabel) {
+ labelToIndex.set(oldLabel, -1);
+ }
+
+ /**
+ * Adds a label and index to the label-to-index mapping
+ * @param label new label
+ * @param index index of block.
+ */
+ protected void addLabelIndex(int label, int index) {
+ int origSz = labelToIndex.size();
+
+ for (int i = 0; i <= (label - origSz); i++) {
+ labelToIndex.add(-1);
+ }
+
+ labelToIndex.set(label, index);
+ }
+
+ /**
+ * Gets the index of the first item in the list with the given
+ * label, if any.
+ *
+ * @param label {@code >= 0;} the label to look for
+ * @return {@code >= -1;} the index of the so-labelled item, or {@code -1}
+ * if none is found
+ */
+ public int indexOfLabel(int label) {
+ if (label >= labelToIndex.size()) {
+ return -1;
+ } else {
+ return labelToIndex.get(label);
+ }
+ }
+
+ /** @inheritDoc */
+ @Override
+ public void shrinkToFit() {
+ super.shrinkToFit();
+
+ rebuildLabelToIndex();
+ }
+
+ /**
+ * Rebuilds the label-to-index mapping after a shrinkToFit().
+ * Note: assumes that the labels that are in the list are the same
+ * although the indicies may have changed.
+ */
+ protected void rebuildLabelToIndex() {
+ int szItems = size();
+
+ for (int i = 0; i < szItems; i++) {
+ LabeledItem li = (LabeledItem)get0(i);
+
+ if (li != null) {
+ labelToIndex.set(li.getLabel(), i);
+ }
+ }
+ }
+
+ /**
+ * Sets the element at the given index.
+ *
+ * @param n {@code >= 0, < size();} which element
+ * @param item {@code null-ok;} the value to store
+ */
+ protected void set(int n, LabeledItem item) {
+ LabeledItem old = (LabeledItem) getOrNull0(n);
+
+ set0(n, item);
+
+ if (old != null) {
+ removeLabel(old.getLabel());
+ }
+
+ if (item != null) {
+ addLabelIndex(item.getLabel(), n);
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/util/Leb128Utils.java b/dx/src/com/android/dx/util/Leb128Utils.java
new file mode 100644
index 0000000..5d450ea
--- /dev/null
+++ b/dx/src/com/android/dx/util/Leb128Utils.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+/**
+ * LEB128 (little-endian base 128) utilities.
+ */
+public final class Leb128Utils {
+ /**
+ * This class is uninstantiable.
+ */
+ private Leb128Utils() {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Gets the number of bytes in the unsigned LEB128 encoding of the
+ * given value.
+ *
+ * @param value the value in question
+ * @return its write size, in bytes
+ */
+ public static int unsignedLeb128Size(int value) {
+ // TODO: This could be much cleverer.
+
+ int remaining = value >> 7;
+ int count = 0;
+
+ while (remaining != 0) {
+ remaining >>= 7;
+ count++;
+ }
+
+ return count + 1;
+ }
+
+ /**
+ * Gets the number of bytes in the signed LEB128 encoding of the
+ * given value.
+ *
+ * @param value the value in question
+ * @return its write size, in bytes
+ */
+ public static int signedLeb128Size(int value) {
+ // TODO: This could be much cleverer.
+
+ int remaining = value >> 7;
+ int count = 0;
+ boolean hasMore = true;
+ int end = ((value & Integer.MIN_VALUE) == 0) ? 0 : -1;
+
+ while (hasMore) {
+ hasMore = (remaining != end)
+ || ((remaining & 1) != ((value >> 6) & 1));
+
+ value = remaining;
+ remaining >>= 7;
+ count++;
+ }
+
+ return count;
+ }
+}
diff --git a/dx/src/com/android/dx/util/ListIntSet.java b/dx/src/com/android/dx/util/ListIntSet.java
new file mode 100644
index 0000000..2b4fc21
--- /dev/null
+++ b/dx/src/com/android/dx/util/ListIntSet.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+import java.util.NoSuchElementException;
+
+/**
+ * A set of integers, represented by a list
+ */
+public class ListIntSet implements IntSet {
+
+ /** also accessed in BitIntSet */
+ final IntList ints;
+
+ /**
+ * Constructs an instance
+ */
+ public ListIntSet() {
+ ints = new IntList();
+ ints.sort();
+ }
+
+ /** @inheritDoc */
+ public void add(int value) {
+ int index = ints.binarysearch(value);
+
+ if (index < 0) {
+ ints.insert(-(index + 1), value);
+ }
+ }
+
+ /** @inheritDoc */
+ public void remove(int value) {
+ int index = ints.indexOf(value);
+
+ if (index >= 0) {
+ ints.removeIndex(index);
+ }
+ }
+
+ /** @inheritDoc */
+ public boolean has(int value) {
+ return ints.indexOf(value) >= 0;
+ }
+
+ /** @inheritDoc */
+ public void merge(IntSet other) {
+ if (other instanceof ListIntSet) {
+ ListIntSet o = (ListIntSet) other;
+ int szThis = ints.size();
+ int szOther = o.ints.size();
+
+ int i = 0;
+ int j = 0;
+
+ while (j < szOther && i < szThis) {
+ while (j < szOther && o.ints.get(j) < ints.get(i)) {
+ add(o.ints.get(j++));
+ }
+ if (j == szOther) {
+ break;
+ }
+ while (i < szThis && o.ints.get(j) >= ints.get(i)) {
+ i++;
+ }
+ }
+
+ while (j < szOther) {
+ add(o.ints.get(j++));
+ }
+
+ ints.sort();
+ } else if (other instanceof BitIntSet) {
+ BitIntSet o = (BitIntSet) other;
+
+ for (int i = 0; i >= 0; i = Bits.findFirst(o.bits, i + 1)) {
+ ints.add(i);
+ }
+ ints.sort();
+ } else {
+ IntIterator iter = other.iterator();
+ while (iter.hasNext()) {
+ add(iter.next());
+ }
+ }
+ }
+
+ /** @inheritDoc */
+ public int elements() {
+ return ints.size();
+ }
+
+ /** @inheritDoc */
+ public IntIterator iterator() {
+ return new IntIterator() {
+ private int idx = 0;
+
+ /** @inheritDoc */
+ public boolean hasNext() {
+ return idx < ints.size();
+ }
+
+ /** @inheritDoc */
+ public int next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+
+ return ints.get(idx++);
+ }
+ };
+ }
+
+ /** @inheritDoc */
+ public String toString() {
+ return ints.toString();
+ }
+}
diff --git a/dx/src/com/android/dx/util/MutabilityControl.java b/dx/src/com/android/dx/util/MutabilityControl.java
new file mode 100644
index 0000000..14e0f2e
--- /dev/null
+++ b/dx/src/com/android/dx/util/MutabilityControl.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+/**
+ * Very simple base class that implements a flag to control the mutability
+ * of instances. This class just provides the flag and a utility to check
+ * and throw the right exception, but it is up to subclasses to place calls
+ * to the checker in all the right places.
+ */
+public class MutabilityControl {
+ /** whether this instance is mutable */
+ private boolean mutable;
+
+ /**
+ * Constructs an instance. It is initially mutable.
+ */
+ public MutabilityControl() {
+ mutable = true;
+ }
+
+ /**
+ * Constructs an instance, explicitly indicating the mutability.
+ *
+ * @param mutable {@code true} iff this instance is mutable
+ */
+ public MutabilityControl(boolean mutable) {
+ this.mutable = mutable;
+ }
+
+ /**
+ * Makes this instance immutable.
+ */
+ public void setImmutable() {
+ mutable = false;
+ }
+
+ /**
+ * Checks to see whether or not this instance is immutable. This is the
+ * same as calling {@code !isMutable()}.
+ *
+ * @return {@code true} iff this instance is immutable
+ */
+ public final boolean isImmutable() {
+ return !mutable;
+ }
+
+ /**
+ * Checks to see whether or not this instance is mutable.
+ *
+ * @return {@code true} iff this instance is mutable
+ */
+ public final boolean isMutable() {
+ return mutable;
+ }
+
+ /**
+ * Throws {@link MutabilityException} if this instance is
+ * immutable.
+ */
+ public final void throwIfImmutable() {
+ if (!mutable) {
+ throw new MutabilityException("immutable instance");
+ }
+ }
+
+ /**
+ * Throws {@link MutabilityException} if this instance is mutable.
+ */
+ public final void throwIfMutable() {
+ if (mutable) {
+ throw new MutabilityException("mutable instance");
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/util/MutabilityException.java b/dx/src/com/android/dx/util/MutabilityException.java
new file mode 100644
index 0000000..bd21651
--- /dev/null
+++ b/dx/src/com/android/dx/util/MutabilityException.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+/**
+ * Exception due to a mutability problem.
+ */
+public class MutabilityException
+ extends ExceptionWithContext {
+ public MutabilityException(String message) {
+ super(message);
+ }
+
+ public MutabilityException(Throwable cause) {
+ super(cause);
+ }
+
+ public MutabilityException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/dx/src/com/android/dx/util/Output.java b/dx/src/com/android/dx/util/Output.java
new file mode 100644
index 0000000..402fa83
--- /dev/null
+++ b/dx/src/com/android/dx/util/Output.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+/**
+ * Interface for a sink for binary output. This is similar to
+ * {@code java.util.DataOutput}, but no {@code IOExceptions}
+ * are declared, and multibyte output is defined to be little-endian.
+ */
+public interface Output {
+ /**
+ * Gets the current cursor position. This is the same as the number of
+ * bytes written to this instance.
+ *
+ * @return {@code >= 0;} the cursor position
+ */
+ public int getCursor();
+
+ /**
+ * Asserts that the cursor is the given value.
+ *
+ * @param expectedCursor the expected cursor value
+ * @throws RuntimeException thrown if {@code getCursor() !=
+ * expectedCursor}
+ */
+ public void assertCursor(int expectedCursor);
+
+ /**
+ * Writes a {@code byte} to this instance.
+ *
+ * @param value the value to write; all but the low 8 bits are ignored
+ */
+ public void writeByte(int value);
+
+ /**
+ * Writes a {@code short} to this instance.
+ *
+ * @param value the value to write; all but the low 16 bits are ignored
+ */
+ public void writeShort(int value);
+
+ /**
+ * Writes an {@code int} to this instance.
+ *
+ * @param value the value to write
+ */
+ public void writeInt(int value);
+
+ /**
+ * Writes a {@code long} to this instance.
+ *
+ * @param value the value to write
+ */
+ public void writeLong(long value);
+
+ /**
+ * Writes a DWARFv3-style unsigned LEB128 integer. For details,
+ * see the "Dalvik Executable Format" document or DWARF v3 section
+ * 7.6.
+ *
+ * @param value value to write, treated as an unsigned value
+ * @return {@code 1..5;} the number of bytes actually written
+ */
+ public int writeUnsignedLeb128(int value);
+
+ /**
+ * Writes a DWARFv3-style unsigned LEB128 integer. For details,
+ * see the "Dalvik Executable Format" document or DWARF v3 section
+ * 7.6.
+ *
+ * @param value value to write
+ * @return {@code 1..5;} the number of bytes actually written
+ */
+ public int writeSignedLeb128(int value);
+
+ /**
+ * Writes a {@link ByteArray} to this instance.
+ *
+ * @param bytes {@code non-null;} the array to write
+ */
+ public void write(ByteArray bytes);
+
+ /**
+ * Writes a portion of a {@code byte[]} to this instance.
+ *
+ * @param bytes {@code non-null;} the array to write
+ * @param offset {@code >= 0;} offset into {@code bytes} for the first
+ * byte to write
+ * @param length {@code >= 0;} number of bytes to write
+ */
+ public void write(byte[] bytes, int offset, int length);
+
+ /**
+ * Writes a {@code byte[]} to this instance. This is just
+ * a convenient shorthand for {@code write(bytes, 0, bytes.length)}.
+ *
+ * @param bytes {@code non-null;} the array to write
+ */
+ public void write(byte[] bytes);
+
+ /**
+ * Writes the given number of {@code 0} bytes.
+ *
+ * @param count {@code >= 0;} the number of zeroes to write
+ */
+ public void writeZeroes(int count);
+
+ /**
+ * Adds extra bytes if necessary (with value {@code 0}) to
+ * force alignment of the output cursor as given.
+ *
+ * @param alignment {@code > 0;} the alignment; must be a power of two
+ */
+ public void alignTo(int alignment);
+}
diff --git a/dx/src/com/android/dx/util/ToHuman.java b/dx/src/com/android/dx/util/ToHuman.java
new file mode 100644
index 0000000..b3a31a5
--- /dev/null
+++ b/dx/src/com/android/dx/util/ToHuman.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+/**
+ * Simple interface for objects that can return a "human" (as opposed to
+ * a complete but often hard to read) string form.
+ */
+public interface ToHuman {
+ /**
+ * Return the "human" string form of this instance. This is
+ * generally less "debuggy" than {@code toString()}.
+ *
+ * @return {@code non-null;} the human string form
+ */
+ public String toHuman();
+}
diff --git a/dx/src/com/android/dx/util/TwoColumnOutput.java b/dx/src/com/android/dx/util/TwoColumnOutput.java
new file mode 100644
index 0000000..ed2ab9f
--- /dev/null
+++ b/dx/src/com/android/dx/util/TwoColumnOutput.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+
+/**
+ * Class that takes a combined output destination and provides two
+ * output writers, one of which ends up writing to the left column and
+ * one which goes on the right.
+ */
+public final class TwoColumnOutput {
+ /** {@code non-null;} underlying writer for final output */
+ private final Writer out;
+
+ /** {@code > 0;} the left column width */
+ private final int leftWidth;
+
+ /** {@code non-null;} pending left column output */
+ private final StringBuffer leftBuf;
+
+ /** {@code non-null;} pending right column output */
+ private final StringBuffer rightBuf;
+
+ /** {@code non-null;} left column writer */
+ private final IndentingWriter leftColumn;
+
+ /** {@code non-null;} right column writer */
+ private final IndentingWriter rightColumn;
+
+ /**
+ * Turns the given two strings (with widths) and spacer into a formatted
+ * two-column string.
+ *
+ * @param s1 {@code non-null;} first string
+ * @param width1 {@code > 0;} width of the first column
+ * @param spacer {@code non-null;} spacer string
+ * @param s2 {@code non-null;} second string
+ * @param width2 {@code > 0;} width of the second column
+ * @return {@code non-null;} an appropriately-formatted string
+ */
+ public static String toString(String s1, int width1, String spacer,
+ String s2, int width2) {
+ int len1 = s1.length();
+ int len2 = s2.length();
+
+ StringWriter sw = new StringWriter((len1 + len2) * 3);
+ TwoColumnOutput twoOut =
+ new TwoColumnOutput(sw, width1, width2, spacer);
+
+ try {
+ twoOut.getLeft().write(s1);
+ twoOut.getRight().write(s2);
+ } catch (IOException ex) {
+ throw new RuntimeException("shouldn't happen", ex);
+ }
+
+ twoOut.flush();
+ return sw.toString();
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param out {@code non-null;} writer to send final output to
+ * @param leftWidth {@code > 0;} width of the left column, in characters
+ * @param rightWidth {@code > 0;} width of the right column, in characters
+ * @param spacer {@code non-null;} spacer string to sit between the two columns
+ */
+ public TwoColumnOutput(Writer out, int leftWidth, int rightWidth,
+ String spacer) {
+ if (out == null) {
+ throw new NullPointerException("out == null");
+ }
+
+ if (leftWidth < 1) {
+ throw new IllegalArgumentException("leftWidth < 1");
+ }
+
+ if (rightWidth < 1) {
+ throw new IllegalArgumentException("rightWidth < 1");
+ }
+
+ if (spacer == null) {
+ throw new NullPointerException("spacer == null");
+ }
+
+ StringWriter leftWriter = new StringWriter(1000);
+ StringWriter rightWriter = new StringWriter(1000);
+
+ this.out = out;
+ this.leftWidth = leftWidth;
+ this.leftBuf = leftWriter.getBuffer();
+ this.rightBuf = rightWriter.getBuffer();
+ this.leftColumn = new IndentingWriter(leftWriter, leftWidth);
+ this.rightColumn =
+ new IndentingWriter(rightWriter, rightWidth, spacer);
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param out {@code non-null;} stream to send final output to
+ * @param leftWidth {@code >= 1;} width of the left column, in characters
+ * @param rightWidth {@code >= 1;} width of the right column, in characters
+ * @param spacer {@code non-null;} spacer string to sit between the two columns
+ */
+ public TwoColumnOutput(OutputStream out, int leftWidth, int rightWidth,
+ String spacer) {
+ this(new OutputStreamWriter(out), leftWidth, rightWidth, spacer);
+ }
+
+ /**
+ * Gets the writer to use to write to the left column.
+ *
+ * @return {@code non-null;} the left column writer
+ */
+ public Writer getLeft() {
+ return leftColumn;
+ }
+
+ /**
+ * Gets the writer to use to write to the right column.
+ *
+ * @return {@code non-null;} the right column writer
+ */
+ public Writer getRight() {
+ return rightColumn;
+ }
+
+ /**
+ * Flushes the output. If there are more lines of pending output in one
+ * column, then the other column will get filled with blank lines.
+ */
+ public void flush() {
+ try {
+ appendNewlineIfNecessary(leftBuf, leftColumn);
+ appendNewlineIfNecessary(rightBuf, rightColumn);
+ outputFullLines();
+ flushLeft();
+ flushRight();
+ } catch (IOException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ /**
+ * Outputs to the final destination as many full line pairs as
+ * there are in the pending output, removing those lines from
+ * their respective buffers. This method terminates when at
+ * least one of the two column buffers is empty.
+ */
+ private void outputFullLines() throws IOException {
+ for (;;) {
+ int leftLen = leftBuf.indexOf("\n");
+ if (leftLen < 0) {
+ return;
+ }
+
+ int rightLen = rightBuf.indexOf("\n");
+ if (rightLen < 0) {
+ return;
+ }
+
+ if (leftLen != 0) {
+ out.write(leftBuf.substring(0, leftLen));
+ }
+
+ if (rightLen != 0) {
+ writeSpaces(out, leftWidth - leftLen);
+ out.write(rightBuf.substring(0, rightLen));
+ }
+
+ out.write('\n');
+
+ leftBuf.delete(0, leftLen + 1);
+ rightBuf.delete(0, rightLen + 1);
+ }
+ }
+
+ /**
+ * Flushes the left column buffer, printing it and clearing the buffer.
+ * If the buffer is already empty, this does nothing.
+ */
+ private void flushLeft() throws IOException {
+ appendNewlineIfNecessary(leftBuf, leftColumn);
+
+ while (leftBuf.length() != 0) {
+ rightColumn.write('\n');
+ outputFullLines();
+ }
+ }
+
+ /**
+ * Flushes the right column buffer, printing it and clearing the buffer.
+ * If the buffer is already empty, this does nothing.
+ */
+ private void flushRight() throws IOException {
+ appendNewlineIfNecessary(rightBuf, rightColumn);
+
+ while (rightBuf.length() != 0) {
+ leftColumn.write('\n');
+ outputFullLines();
+ }
+ }
+
+ /**
+ * Appends a newline to the given buffer via the given writer, but
+ * only if it isn't empty and doesn't already end with one.
+ *
+ * @param buf {@code non-null;} the buffer in question
+ * @param out {@code non-null;} the writer to use
+ */
+ private static void appendNewlineIfNecessary(StringBuffer buf,
+ Writer out)
+ throws IOException {
+ int len = buf.length();
+
+ if ((len != 0) && (buf.charAt(len - 1) != '\n')) {
+ out.write('\n');
+ }
+ }
+
+ /**
+ * Writes the given number of spaces to the given writer.
+ *
+ * @param out {@code non-null;} where to write
+ * @param amt {@code >= 0;} the number of spaces to write
+ */
+ private static void writeSpaces(Writer out, int amt) throws IOException {
+ while (amt > 0) {
+ out.write(' ');
+ amt--;
+ }
+ }
+}
diff --git a/dx/src/com/android/dx/util/Warning.java b/dx/src/com/android/dx/util/Warning.java
new file mode 100644
index 0000000..3c23c7c
--- /dev/null
+++ b/dx/src/com/android/dx/util/Warning.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+/**
+ * Exception which is meant to indicate a non-fatal warning.
+ */
+public class Warning extends RuntimeException {
+ /**
+ * Constructs an instance.
+ *
+ * @param message human-oriented message
+ */
+ public Warning(String message) {
+ super(message);
+ }
+}
diff --git a/dx/src/com/android/dx/util/Writers.java b/dx/src/com/android/dx/util/Writers.java
new file mode 100644
index 0000000..eba845c
--- /dev/null
+++ b/dx/src/com/android/dx/util/Writers.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util;
+
+import java.io.PrintWriter;
+import java.io.Writer;
+
+/**
+ * Utilities for dealing with {@code Writer}s.
+ */
+public final class Writers {
+ /**
+ * This class is uninstantiable.
+ */
+ private Writers() {
+ // This space intentionally left blank.
+ }
+
+ /**
+ * Makes a {@code PrintWriter} for the given {@code Writer},
+ * returning the given writer if it already happens to be the right
+ * class.
+ *
+ * @param writer {@code non-null;} writer to (possibly) wrap
+ * @return {@code non-null;} an appropriate instance
+ */
+ public static PrintWriter printWriterFor(Writer writer) {
+ if (writer instanceof PrintWriter) {
+ return (PrintWriter) writer;
+ }
+
+ return new PrintWriter(writer);
+ }
+}
diff --git a/dx/src/com/android/dx/util/_tests/_BitIntSet.java b/dx/src/com/android/dx/util/_tests/_BitIntSet.java
new file mode 100644
index 0000000..e26d7a4
--- /dev/null
+++ b/dx/src/com/android/dx/util/_tests/_BitIntSet.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util._tests;
+
+import com.android.dx.util.BitIntSet;
+import com.android.dx.util.IntIterator;
+import com.android.dx.util.ListIntSet;
+
+import junit.framework.TestCase;
+
+import java.util.NoSuchElementException;
+
+public class _BitIntSet extends TestCase {
+ public void test_basic() {
+ BitIntSet set = new BitIntSet(32);
+
+ assertEquals(0, set.elements());
+
+ set.add(0);
+ set.add(1);
+ set.add(31);
+
+ assertTrue(set.has(0));
+ assertTrue(set.has(1));
+ assertTrue(set.has(31));
+
+ assertEquals(3, set.elements());
+
+ assertFalse(set.has(2));
+ assertFalse(set.has(7));
+ assertFalse(set.has(30));
+ }
+
+ public void test_iterator() {
+ BitIntSet set = new BitIntSet(32);
+
+ set.add(0);
+ set.add(0);
+ set.add(1);
+ set.add(1);
+ set.add(31);
+ set.add(31);
+
+ IntIterator iter = set.iterator();
+
+ assertTrue(iter.hasNext());
+ assertEquals(iter.next(), 0);
+ assertTrue(iter.hasNext());
+ assertEquals(iter.next(), 1);
+ assertTrue(iter.hasNext());
+ assertEquals(iter.next(), 31);
+
+ assertFalse(iter.hasNext());
+
+ try {
+ iter.next();
+ fail();
+ } catch (NoSuchElementException ex) {
+ // exception excepted
+ }
+ }
+
+ public void test_remove() {
+ BitIntSet set = new BitIntSet(32);
+
+ set.add(0);
+ set.add(1);
+ set.add(31);
+
+ assertTrue(set.has(0));
+ assertTrue(set.has(1));
+ assertTrue(set.has(31));
+
+ assertFalse(set.has(2));
+ assertFalse(set.has(7));
+ assertFalse(set.has(30));
+
+ set.remove(0);
+
+ assertFalse(set.has(0));
+
+ assertTrue(set.has(1));
+ assertTrue(set.has(31));
+ }
+
+ /**
+ * Tests the auto-expansion of the set
+ */
+ public void test_expand() {
+ BitIntSet set = new BitIntSet(32);
+ int[] values = {0, 1, 31, 32, 128};
+
+ for (int i = 0; i < values.length; i++) {
+ set.add(values[i]);
+ }
+
+ IntIterator iter = set.iterator();
+
+ for (int i = 0; i < values.length; i++) {
+ assertTrue(iter.hasNext());
+ assertEquals(values[i], iter.next());
+ }
+ assertFalse(iter.hasNext());
+ }
+
+ public void test_merge() {
+ BitIntSet setA = new BitIntSet(32);
+ int[] valuesA = {0, 1, 31};
+
+ for (int i = 0; i < valuesA.length; i++) {
+ setA.add(valuesA[i]);
+ }
+
+ BitIntSet setB = new BitIntSet(32);
+ int[] valuesB = {0, 5, 6, 8, 31};
+
+ for (int i = 0; i < valuesB.length; i++) {
+ setB.add(valuesB[i]);
+ }
+
+ setA.merge(setB);
+
+ for (int i = 0; i < valuesA.length; i++) {
+ assertTrue(setA.has(valuesA[i]));
+ }
+
+ for (int i = 0; i < valuesB.length; i++) {
+ assertTrue(setA.has(valuesB[i]));
+ }
+ }
+
+ public void test_mergeWithListIntSet() {
+ BitIntSet setA = new BitIntSet(32);
+ int[] valuesA = {0, 1, 31};
+
+ for (int i = 0; i < valuesA.length; i++) {
+ setA.add(valuesA[i]);
+ }
+
+ ListIntSet setB = new ListIntSet();
+ int[] valuesB = {0, 5, 6, 8, 31};
+
+ for (int i = 0; i < valuesB.length; i++) {
+ setB.add(valuesB[i]);
+ }
+
+ setA.merge(setB);
+
+ for (int i = 0; i < valuesA.length; i++) {
+ assertTrue(setA.has(valuesA[i]));
+ }
+
+ for (int i = 0; i < valuesB.length; i++) {
+ assertTrue(setA.has(valuesB[i]));
+ }
+ }
+
+ public void test_mergeAndExpand() {
+ BitIntSet setA = new BitIntSet(32);
+ int[] valuesA = {0, 1, 31};
+
+ for (int i = 0; i < valuesA.length; i++) {
+ setA.add(valuesA[i]);
+ }
+
+ BitIntSet setB = new BitIntSet(32);
+ int[] valuesB = {0, 5, 6, 32, 127};
+
+ for (int i = 0; i < valuesB.length; i++) {
+ setB.add(valuesB[i]);
+ }
+
+ setA.merge(setB);
+
+ for (int i = 0; i < valuesA.length; i++) {
+ assertTrue(setA.has(valuesA[i]));
+ }
+
+ for (int i = 0; i < valuesB.length; i++) {
+ assertTrue(setA.has(valuesB[i]));
+ }
+ }
+
+ public void test_toString() {
+ BitIntSet set = new BitIntSet(32);
+
+ assertEquals(set.toString(), "{}");
+
+ set.add(1);
+
+ assertEquals(set.toString(), "{1}");
+
+ set.add(2);
+
+ assertEquals(set.toString(), "{1, 2}");
+ }
+}
diff --git a/dx/src/com/android/dx/util/_tests/_Bits.java b/dx/src/com/android/dx/util/_tests/_Bits.java
new file mode 100644
index 0000000..a95fc14
--- /dev/null
+++ b/dx/src/com/android/dx/util/_tests/_Bits.java
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util._tests;
+
+import com.android.dx.util.Bits;
+
+import junit.framework.TestCase;
+
+/**
+ * Test the class {@code com.android.dx.util.Bits}.
+ */
+public class _Bits
+ extends TestCase {
+ public void test_makeBitSet() {
+ assertEquals(label(0), 0, Bits.makeBitSet(0).length);
+
+ for (int i = 1; i <= 32; i++) {
+ assertEquals(label(i), 1, Bits.makeBitSet(i).length);
+ }
+
+ for (int i = 33; i <= 64; i++) {
+ assertEquals(label(i), 2, Bits.makeBitSet(i).length);
+ }
+
+ for (int i = 65; i < 4000; i += 101) {
+ int expect = i >> 5;
+ if ((expect * 32) < i) {
+ expect++;
+ }
+ assertEquals(label(i), expect, Bits.makeBitSet(i).length);
+ }
+ }
+
+ public void test_getMax() {
+ for (int i = 0; i < 4000; i += 59) {
+ int expect = i >> 5;
+ if ((expect * 32) < i) {
+ expect++;
+ }
+ assertEquals(label(i), expect * 32,
+ Bits.getMax(new int[expect]));
+ }
+ }
+
+ public void test1_get() {
+ int[] bits = Bits.makeBitSet(100);
+
+ for (int i = 0; i < 100; i++) {
+ assertFalse(label(i), Bits.get(bits, i));
+ }
+ }
+
+ public void test2_get() {
+ int[] bits = Bits.makeBitSet(100);
+ for (int i = 0; i < bits.length; i++) {
+ bits[i] = -1;
+ }
+
+ for (int i = 0; i < 100; i++) {
+ assertTrue(label(i), Bits.get(bits, i));
+ }
+ }
+
+ public void test3_get() {
+ int[] bits = Bits.makeBitSet(100);
+
+ for (int i = 0; i < 100; i++) {
+ Bits.set(bits, i, (i % 5) == 0);
+ }
+
+ for (int i = 0; i < 100; i++) {
+ boolean expect = (i % 5) == 0;
+ assertTrue(label(i), Bits.get(bits, i) == expect);
+ }
+ }
+
+ public void test1_set1() {
+ int[] bits = Bits.makeBitSet(50);
+ bits[1] = -1;
+
+ Bits.set(bits, 0, true);
+ Bits.set(bits, 3, true);
+ Bits.set(bits, 6, true);
+ Bits.set(bits, 3, false);
+ Bits.set(bits, 35, false);
+ Bits.set(bits, 38, false);
+ Bits.set(bits, 42, false);
+ Bits.set(bits, 38, true);
+
+ assertEquals(label(1), 0x41, bits[0]);
+ assertEquals(label(2), 0xfffffbf7, bits[1]);
+ }
+
+ public void test2_set1() {
+ int[] bits = Bits.makeBitSet(100);
+
+ for (int i = 0; i < 100; i++) {
+ if ((i % 3) == 0) {
+ Bits.set(bits, i, true);
+ }
+ }
+
+ for (int i = 0; i < 100; i++) {
+ if ((i % 5) == 0) {
+ Bits.set(bits, i, false);
+ }
+ }
+
+ for (int i = 0; i < 100; i++) {
+ if ((i % 7) == 0) {
+ Bits.set(bits, i, true);
+ }
+ }
+
+ for (int i = 0; i < 100; i++) {
+ boolean expect = ((i % 7) == 0) ||
+ (((i % 3) == 0) && ((i % 5) != 0));
+ assertTrue(label(i), Bits.get(bits, i) == expect);
+ }
+ }
+
+ public void test_set2() {
+ int[] bits = Bits.makeBitSet(100);
+
+ for (int i = 0; i < 100; i++) {
+ if ((i % 11) == 0) {
+ Bits.set(bits, i);
+ }
+ }
+
+ for (int i = 0; i < 100; i++) {
+ boolean expect = (i % 11) == 0;
+ assertTrue(label(i), Bits.get(bits, i) == expect);
+ }
+ }
+
+ public void test_clear() {
+ int[] bits = Bits.makeBitSet(100);
+ for (int i = 0; i < bits.length; i++) {
+ bits[i] = -1;
+ }
+
+ for (int i = 0; i < 100; i++) {
+ if ((i % 5) == 0) {
+ Bits.clear(bits, i);
+ }
+ }
+
+ for (int i = 0; i < 100; i++) {
+ boolean expect = (i % 5) != 0;
+ assertTrue(label(i), Bits.get(bits, i) == expect);
+ }
+ }
+
+ public void test1_isEmpty() {
+ for (int i = 0; i < 10; i++) {
+ assertTrue(label(i), Bits.isEmpty(new int[i]));
+ }
+ }
+
+ public void test2_isEmpty() {
+ for (int i = 1; i < 1000; i += 11) {
+ int[] bits = Bits.makeBitSet(i);
+ for (int j = i % 11; j >= 0; j--) {
+ int x = i - 1 - (j * 13);
+ if (x >= 0) {
+ Bits.set(bits, x);
+ }
+ }
+ assertFalse(label(i), Bits.isEmpty(bits));
+ }
+ }
+
+ public void test1_bitCount() {
+ for (int i = 0; i < 10; i++) {
+ assertEquals(label(i), 0, Bits.bitCount(new int[i]));
+ }
+ }
+
+ public void test2_bitCount() {
+ for (int i = 1; i < 1000; i += 13) {
+ int[] bits = Bits.makeBitSet(i);
+ int count = 0;
+ for (int j = 0; j < i; j += 20) {
+ Bits.set(bits, j);
+ count++;
+ }
+ for (int j = 7; j < i; j += 11) {
+ if (!Bits.get(bits, j)) {
+ Bits.set(bits, j);
+ count++;
+ }
+ }
+ for (int j = 3; j < i; j += 17) {
+ if (!Bits.get(bits, j)) {
+ Bits.set(bits, j);
+ count++;
+ }
+ }
+ assertEquals(label(i), count, Bits.bitCount(bits));
+ }
+ }
+
+ public void test1_anyInRange() {
+ int[] bits = new int[100];
+
+ for (int i = 0; i < 100; i += 11) {
+ assertFalse(label(i), Bits.anyInRange(bits, 0, i));
+ }
+ }
+
+ public void test2_anyInRange() {
+ int[] bits = new int[100];
+
+ for (int i = 0; i < 100; i += 11) {
+ assertFalse(label(i), Bits.anyInRange(bits, i, 100));
+ }
+ }
+
+ public void test3_anyInRange() {
+ int[] bits = new int[100];
+
+ for (int i = 0; i < 50; i += 7) {
+ assertFalse(label(i), Bits.anyInRange(bits, i, 100 - i));
+ }
+ }
+
+ public void test4_anyInRange() {
+ int[] bits = new int[100];
+ for (int i = 0; i < bits.length; i++) {
+ bits[i] = -1;
+ }
+
+ for (int i = 1; i < 100; i += 11) {
+ assertTrue(label(i), Bits.anyInRange(bits, 0, i));
+ }
+ }
+
+ public void test5_anyInRange() {
+ int[] bits = new int[100];
+ for (int i = 0; i < bits.length; i++) {
+ bits[i] = -1;
+ }
+
+ for (int i = 1; i < 100; i += 11) {
+ assertTrue(label(i), Bits.anyInRange(bits, i, 100));
+ }
+ }
+
+ public void test6_anyInRange() {
+ int[] bits = new int[100];
+ for (int i = 0; i < bits.length; i++) {
+ bits[i] = -1;
+ }
+
+ for (int i = 0; i < 50; i += 7) {
+ assertTrue(label(i), Bits.anyInRange(bits, i, 100 - i));
+ }
+ }
+
+ public void test1_findFirst1() {
+ int[] bits = new int[100];
+
+ for (int i = 0; i < 100; i++) {
+ assertEquals(label(i), -1, Bits.findFirst(bits, i));
+ }
+ }
+
+ public void test2_findFirst1() {
+ int[] bits = new int[100];
+ for (int i = 0; i < bits.length; i++) {
+ bits[i] = -1;
+ }
+
+ for (int i = 0; i < 100; i++) {
+ assertEquals(label(i), i, Bits.findFirst(bits, i));
+ }
+ }
+
+ public void test3_findFirst1() {
+ int[] bits = new int[100];
+
+ for (int i = 25; i < 80; i++) {
+ for (int j = 0; j < bits.length; j++) {
+ bits[j] = 0;
+ }
+
+ Bits.set(bits, i - 5);
+ Bits.set(bits, i + 5);
+ Bits.set(bits, i + 10);
+ Bits.set(bits, i + 20);
+ assertEquals(label(i), i + 5, Bits.findFirst(bits, i));
+ }
+ }
+
+ public void test1_findFirst2() {
+ for (int i = 0; i < 32; i++) {
+ assertEquals(label(i), -1, Bits.findFirst(0, i));
+ }
+ }
+
+ public void test2_findFirst2() {
+ for (int i = 0; i < 32; i++) {
+ assertEquals(label(i), i, Bits.findFirst(-1, i));
+ }
+ }
+
+ public void test3_findFirst2() {
+ for (int i = 0; i < 32; i++) {
+ assertEquals(label(i), -1, Bits.findFirst((1 << i) >>> 1, i));
+ }
+ }
+
+ public void test4_findFirst2() {
+ for (int i = 0; i < 32; i++) {
+ assertEquals(label(i), i, Bits.findFirst(1 << i, i));
+ }
+ }
+
+ public void test5_findFirst2() {
+ for (int i = 0; i < 31; i++) {
+ assertEquals(label(i), i + 1, Bits.findFirst(1 << (i + 1), i));
+ }
+ }
+
+ public void test6_findFirst2() {
+ for (int i = 0; i < 32; i++) {
+ int value = (1 << i);
+ value |= (value >>> 1);
+ assertEquals(label(i), i, Bits.findFirst(value, i));
+ }
+ }
+
+ private static String label(int n) {
+ return "(" + n + ")";
+ }
+}
diff --git a/dx/src/com/android/dx/util/_tests/_IntList.java b/dx/src/com/android/dx/util/_tests/_IntList.java
new file mode 100644
index 0000000..dadbd54
--- /dev/null
+++ b/dx/src/com/android/dx/util/_tests/_IntList.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util._tests;
+
+import com.android.dx.util.IntList;
+
+import junit.framework.TestCase;
+
+/**
+ * Test the class {@code com.android.dx.util.IntList}.
+ */
+public class _IntList
+ extends TestCase {
+ // TODO: Add tests for the rest of the methods.
+
+ public void test_contains() {
+ for (int sz = 0; sz < 100; sz++) {
+ IntList list = new IntList(sz);
+ for (int i = 0; i < sz; i++) {
+ list.add(i * 2);
+ }
+ for (int i = (sz * 2) - 1; i >= 0; i--) {
+ boolean contains = list.contains(i);
+ if ((i & 1) == 0) {
+ assertTrue(label(sz, i), contains);
+ } else {
+ assertFalse(label(sz, i), contains);
+ }
+ }
+ assertFalse(label(sz, -1), list.contains(-1));
+ assertFalse(label(sz, sz * 2), list.contains(sz * 2));
+ }
+ }
+
+ public void test_addSorted() {
+ IntList list = new IntList(2);
+
+ list.add(9);
+ list.add(12);
+
+ assertTrue(list.contains(9));
+ assertTrue(list.contains(12));
+ }
+
+ public void test_addUnsorted() {
+ IntList list = new IntList(2);
+
+ list.add(12);
+ list.add(9);
+
+ assertTrue(list.contains(12));
+ assertTrue(list.contains(9));
+ }
+
+ private static String label(int n, int m) {
+ return "(" + n + "/" + m + ")";
+ }
+}
diff --git a/dx/src/com/android/dx/util/_tests/_ListIntSet.java b/dx/src/com/android/dx/util/_tests/_ListIntSet.java
new file mode 100644
index 0000000..ccd5991
--- /dev/null
+++ b/dx/src/com/android/dx/util/_tests/_ListIntSet.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.util._tests;
+
+import com.android.dx.util.BitIntSet;
+import com.android.dx.util.ListIntSet;
+import com.android.dx.util.IntIterator;
+
+import junit.framework.TestCase;
+
+import java.util.NoSuchElementException;
+
+public class _ListIntSet extends TestCase {
+ public void test_basic() {
+ ListIntSet set = new ListIntSet();
+
+ assertEquals(0, set.elements());
+
+ set.add(31);
+ set.add(0);
+ set.add(1);
+
+ assertTrue(set.has(0));
+ assertTrue(set.has(1));
+ assertTrue(set.has(31));
+
+ assertEquals(3, set.elements());
+
+ assertFalse(set.has(2));
+ assertFalse(set.has(7));
+ assertFalse(set.has(30));
+ }
+
+ public void test_iterator() {
+ ListIntSet set = new ListIntSet();
+
+ set.add(0);
+ set.add(0);
+ set.add(1);
+ set.add(1);
+ set.add(31);
+ set.add(31);
+
+ IntIterator iter = set.iterator();
+
+ assertTrue(iter.hasNext());
+ assertEquals(iter.next(), 0);
+ assertTrue(iter.hasNext());
+ assertEquals(iter.next(), 1);
+ assertTrue(iter.hasNext());
+ assertEquals(iter.next(), 31);
+
+ assertFalse(iter.hasNext());
+
+ try {
+ iter.next();
+ fail();
+ } catch (NoSuchElementException ex) {
+ // exception excepted
+ }
+ }
+
+ public void test_empty() {
+ ListIntSet set = new ListIntSet();
+
+ IntIterator iter = set.iterator();
+
+ assertFalse(iter.hasNext());
+ }
+
+ public void test_remove() {
+ ListIntSet set = new ListIntSet();
+
+ set.add(0);
+ set.add(1);
+ set.add(31);
+
+ assertTrue(set.has(0));
+ assertTrue(set.has(1));
+ assertTrue(set.has(31));
+
+ assertFalse(set.has(2));
+ assertFalse(set.has(7));
+ assertFalse(set.has(30));
+
+ set.remove(0);
+
+ assertFalse(set.has(0));
+
+ assertTrue(set.has(1));
+ assertTrue(set.has(31));
+ }
+
+ public void test_mergeA() {
+ ListIntSet setA = new ListIntSet();
+ int[] valuesA = {0, 1, 31};
+
+ for (int i = 0; i < valuesA.length; i++) {
+ setA.add(valuesA[i]);
+ }
+
+ ListIntSet setB = new ListIntSet();
+ int[] valuesB = {0, 5, 6, 32, 127, 128};
+
+ for (int i = 0; i < valuesB.length; i++) {
+ setB.add(valuesB[i]);
+ }
+
+ setA.merge(setB);
+
+ for (int i = 0; i < valuesA.length; i++) {
+ assertTrue(setA.has(valuesA[i]));
+ }
+
+ for (int i = 0; i < valuesB.length; i++) {
+ assertTrue(setA.has(valuesB[i]));
+ }
+
+ }
+
+ public void test_mergeB() {
+ ListIntSet setA = new ListIntSet();
+ int[] valuesA = {0, 1, 31, 129, 130};
+
+ for (int i = 0; i < valuesA.length; i++) {
+ setA.add(valuesA[i]);
+ }
+
+ ListIntSet setB = new ListIntSet();
+ int[] valuesB = {0, 5, 6, 32, 127,128};
+
+ for (int i = 0; i < valuesB.length; i++) {
+ setB.add(valuesB[i]);
+ }
+
+ setA.merge(setB);
+
+ for (int i = 0; i < valuesA.length; i++) {
+ assertTrue(setA.has(valuesA[i]));
+ }
+
+ for (int i = 0; i < valuesB.length; i++) {
+ assertTrue(setA.has(valuesB[i]));
+ }
+
+ }
+
+ public void test_mergeWithBitIntSet() {
+ ListIntSet setA = new ListIntSet();
+ int[] valuesA = {0, 1, 31, 129, 130};
+
+ for (int i = 0; i < valuesA.length; i++) {
+ setA.add(valuesA[i]);
+ }
+
+ BitIntSet setB = new BitIntSet(129);
+ int[] valuesB = {0, 5, 6, 32, 127,128};
+
+ for (int i = 0; i < valuesB.length; i++) {
+ setB.add(valuesB[i]);
+ }
+
+ setA.merge(setB);
+
+ for (int i = 0; i < valuesA.length; i++) {
+ assertTrue(setA.has(valuesA[i]));
+ }
+
+ for (int i = 0; i < valuesB.length; i++) {
+ assertTrue(setA.has(valuesB[i]));
+ }
+
+ }
+
+ public void test_toString() {
+ ListIntSet set = new ListIntSet();
+
+ assertEquals(set.toString(), "{}");
+
+ set.add(1);
+
+ assertEquals(set.toString(), "{1}");
+
+ set.add(2);
+
+ assertEquals(set.toString(), "{1, 2}");
+ }
+
+}
diff --git a/dx/src/com/android/dx/util/package.html b/dx/src/com/android/dx/util/package.html
new file mode 100644
index 0000000..8e81e94
--- /dev/null
+++ b/dx/src/com/android/dx/util/package.html
@@ -0,0 +1,3 @@
+<body>
+<p>Utility classes for class file access/manipulation.</p>
+</body>
diff --git a/dx/src/junit/extensions/ActiveTestSuite.java b/dx/src/junit/extensions/ActiveTestSuite.java
new file mode 100644
index 0000000..1a3f163
--- /dev/null
+++ b/dx/src/junit/extensions/ActiveTestSuite.java
@@ -0,0 +1,64 @@
+package junit.extensions;
+
+import junit.framework.*;
+
+/**
+ * A TestSuite for active Tests. It runs each
+ * test in a separate thread and waits until all
+ * threads have terminated.
+ * -- Aarhus Radisson Scandinavian Center 11th floor
+ */
+public class ActiveTestSuite extends TestSuite {
+ private volatile int fActiveTestDeathCount;
+
+ public ActiveTestSuite() {
+ }
+
+ public ActiveTestSuite(Class theClass) {
+ super(theClass);
+ }
+
+ public ActiveTestSuite(String name) {
+ super (name);
+ }
+
+ public ActiveTestSuite(Class theClass, String name) {
+ super(theClass, name);
+ }
+
+ public void run(TestResult result) {
+ fActiveTestDeathCount= 0;
+ super.run(result);
+ waitUntilFinished();
+ }
+
+ public void runTest(final Test test, final TestResult result) {
+ Thread t= new Thread() {
+ public void run() {
+ try {
+ // inlined due to limitation in VA/Java
+ //ActiveTestSuite.super.runTest(test, result);
+ test.run(result);
+ } finally {
+ ActiveTestSuite.this.runFinished(test);
+ }
+ }
+ };
+ t.start();
+ }
+
+ synchronized void waitUntilFinished() {
+ while (fActiveTestDeathCount < testCount()) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ return; // ignore
+ }
+ }
+ }
+
+ synchronized public void runFinished(Test test) {
+ fActiveTestDeathCount++;
+ notifyAll();
+ }
+}
diff --git a/dx/src/junit/extensions/ExceptionTestCase.java b/dx/src/junit/extensions/ExceptionTestCase.java
new file mode 100644
index 0000000..fae5746
--- /dev/null
+++ b/dx/src/junit/extensions/ExceptionTestCase.java
@@ -0,0 +1,46 @@
+package junit.extensions;
+
+import junit.framework.*;
+
+/**
+ * A TestCase that expects an Exception of class fExpected to be thrown.
+ * The other way to check that an expected exception is thrown is:
+ * <pre>
+ * try {
+ * shouldThrow();
+ * }
+ * catch (SpecialException e) {
+ * return;
+ * }
+ * fail("Expected SpecialException");
+ * </pre>
+ *
+ * To use ExceptionTestCase, create a TestCase like:
+ * <pre>
+ * new ExceptionTestCase("testShouldThrow", SpecialException.class);
+ * </pre>
+ */
+public class ExceptionTestCase extends TestCase {
+ Class<?> fExpected;
+
+ public ExceptionTestCase(String name, Class exception) {
+ super(name);
+ fExpected= exception;
+ }
+ /**
+ * Execute the test method expecting that an Exception of
+ * class fExpected or one of its subclasses will be thrown
+ */
+ protected void runTest() throws Throwable {
+ try {
+ super.runTest();
+ }
+ catch (Exception e) {
+ if (fExpected.isAssignableFrom(e.getClass()))
+ return;
+ else
+ throw e;
+ }
+ fail("Expected exception " + fExpected);
+ }
+}
diff --git a/dx/src/junit/extensions/RepeatedTest.java b/dx/src/junit/extensions/RepeatedTest.java
new file mode 100644
index 0000000..ebce775
--- /dev/null
+++ b/dx/src/junit/extensions/RepeatedTest.java
@@ -0,0 +1,31 @@
+package junit.extensions;
+
+import junit.framework.*;
+
+/**
+ * A Decorator that runs a test repeatedly.
+ *
+ */
+public class RepeatedTest extends TestDecorator {
+ private int fTimesRepeat;
+
+ public RepeatedTest(Test test, int repeat) {
+ super(test);
+ if (repeat < 0)
+ throw new IllegalArgumentException("Repetition count must be > 0");
+ fTimesRepeat= repeat;
+ }
+ public int countTestCases() {
+ return super.countTestCases()*fTimesRepeat;
+ }
+ public void run(TestResult result) {
+ for (int i= 0; i < fTimesRepeat; i++) {
+ if (result.shouldStop())
+ break;
+ super.run(result);
+ }
+ }
+ public String toString() {
+ return super.toString()+"(repeated)";
+ }
+}
diff --git a/dx/src/junit/extensions/TestDecorator.java b/dx/src/junit/extensions/TestDecorator.java
new file mode 100644
index 0000000..a8e9e7d
--- /dev/null
+++ b/dx/src/junit/extensions/TestDecorator.java
@@ -0,0 +1,38 @@
+package junit.extensions;
+
+import junit.framework.*;
+
+/**
+ * A Decorator for Tests. Use TestDecorator as the base class
+ * for defining new test decorators. Test decorator subclasses
+ * can be introduced to add behaviour before or after a test
+ * is run.
+ *
+ */
+public class TestDecorator extends Assert implements Test {
+ protected Test fTest;
+
+ public TestDecorator(Test test) {
+ fTest= test;
+ }
+ /**
+ * The basic run behaviour.
+ */
+ public void basicRun(TestResult result) {
+ fTest.run(result);
+ }
+ public int countTestCases() {
+ return fTest.countTestCases();
+ }
+ public void run(TestResult result) {
+ basicRun(result);
+ }
+
+ public String toString() {
+ return fTest.toString();
+ }
+
+ public Test getTest() {
+ return fTest;
+ }
+}
diff --git a/dx/src/junit/extensions/TestSetup.java b/dx/src/junit/extensions/TestSetup.java
new file mode 100644
index 0000000..f1c25fa
--- /dev/null
+++ b/dx/src/junit/extensions/TestSetup.java
@@ -0,0 +1,37 @@
+package junit.extensions;
+
+import junit.framework.*;
+
+/**
+ * A Decorator to set up and tear down additional fixture state.
+ * Subclass TestSetup and insert it into your tests when you want
+ * to set up additional state once before the tests are run.
+ */
+public class TestSetup extends TestDecorator {
+
+ public TestSetup(Test test) {
+ super(test);
+ }
+ public void run(final TestResult result) {
+ Protectable p= new Protectable() {
+ public void protect() throws Exception {
+ setUp();
+ basicRun(result);
+ tearDown();
+ }
+ };
+ result.runProtected(this, p);
+ }
+ /**
+ * Sets up the fixture. Override to set up additional fixture
+ * state.
+ */
+ protected void setUp() throws Exception {
+ }
+ /**
+ * Tears down the fixture. Override to tear down the additional
+ * fixture state.
+ */
+ protected void tearDown() throws Exception {
+ }
+}
diff --git a/dx/src/junit/framework/Assert.java b/dx/src/junit/framework/Assert.java
new file mode 100644
index 0000000..eb5d960
--- /dev/null
+++ b/dx/src/junit/framework/Assert.java
@@ -0,0 +1,291 @@
+package junit.framework;
+
+/**
+ * A set of assert methods. Messages are only displayed when an assert fails.
+ */
+
+public class Assert {
+ /**
+ * Protect constructor since it is a static only class
+ */
+ protected Assert() {
+ }
+
+ /**
+ * Asserts that a condition is true. If it isn't it throws
+ * an AssertionFailedError with the given message.
+ */
+ static public void assertTrue(String message, boolean condition) {
+ if (!condition)
+ fail(message);
+ }
+ /**
+ * Asserts that a condition is true. If it isn't it throws
+ * an AssertionFailedError.
+ */
+ static public void assertTrue(boolean condition) {
+ assertTrue(null, condition);
+ }
+ /**
+ * Asserts that a condition is false. If it isn't it throws
+ * an AssertionFailedError with the given message.
+ */
+ static public void assertFalse(String message, boolean condition) {
+ assertTrue(message, !condition);
+ }
+ /**
+ * Asserts that a condition is false. If it isn't it throws
+ * an AssertionFailedError.
+ */
+ static public void assertFalse(boolean condition) {
+ assertFalse(null, condition);
+ }
+ /**
+ * Fails a test with the given message.
+ */
+ static public void fail(String message) {
+ throw new AssertionFailedError(message);
+ }
+ /**
+ * Fails a test with no message.
+ */
+ static public void fail() {
+ fail(null);
+ }
+ /**
+ * Asserts that two objects are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, Object expected, Object actual) {
+ if (expected == null && actual == null)
+ return;
+ if (expected != null && expected.equals(actual))
+ return;
+ failNotEquals(message, expected, actual);
+ }
+ /**
+ * Asserts that two objects are equal. If they are not
+ * an AssertionFailedError is thrown.
+ */
+ static public void assertEquals(Object expected, Object actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two Strings are equal.
+ */
+ static public void assertEquals(String message, String expected, String actual) {
+ if (expected == null && actual == null)
+ return;
+ if (expected != null && expected.equals(actual))
+ return;
+ throw new ComparisonFailure(message, expected, actual);
+ }
+ /**
+ * Asserts that two Strings are equal.
+ */
+ static public void assertEquals(String expected, String actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two doubles are equal concerning a delta. If they are not
+ * an AssertionFailedError is thrown with the given message. If the expected
+ * value is infinity then the delta value is ignored.
+ */
+ static public void assertEquals(String message, double expected, double actual, double delta) {
+ // handle infinity specially since subtracting to infinite values gives NaN and the
+ // the following test fails
+ if (Double.isInfinite(expected)) {
+ if (!(expected == actual))
+ failNotEquals(message, new Double(expected), new Double(actual));
+ } else if (!(Math.abs(expected-actual) <= delta)) // Because comparison with NaN always returns false
+ failNotEquals(message, new Double(expected), new Double(actual));
+ }
+ /**
+ * Asserts that two doubles are equal concerning a delta. If the expected
+ * value is infinity then the delta value is ignored.
+ */
+ static public void assertEquals(double expected, double actual, double delta) {
+ assertEquals(null, expected, actual, delta);
+ }
+ /**
+ * Asserts that two floats are equal concerning a delta. If they are not
+ * an AssertionFailedError is thrown with the given message. If the expected
+ * value is infinity then the delta value is ignored.
+ */
+ static public void assertEquals(String message, float expected, float actual, float delta) {
+ // handle infinity specially since subtracting to infinite values gives NaN and the
+ // the following test fails
+ if (Float.isInfinite(expected)) {
+ if (!(expected == actual))
+ failNotEquals(message, new Float(expected), new Float(actual));
+ } else if (!(Math.abs(expected-actual) <= delta))
+ failNotEquals(message, new Float(expected), new Float(actual));
+ }
+ /**
+ * Asserts that two floats are equal concerning a delta. If the expected
+ * value is infinity then the delta value is ignored.
+ */
+ static public void assertEquals(float expected, float actual, float delta) {
+ assertEquals(null, expected, actual, delta);
+ }
+ /**
+ * Asserts that two longs are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, long expected, long actual) {
+ assertEquals(message, new Long(expected), new Long(actual));
+ }
+ /**
+ * Asserts that two longs are equal.
+ */
+ static public void assertEquals(long expected, long actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two booleans are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, boolean expected, boolean actual) {
+ assertEquals(message, new Boolean(expected), new Boolean(actual));
+ }
+ /**
+ * Asserts that two booleans are equal.
+ */
+ static public void assertEquals(boolean expected, boolean actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two bytes are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, byte expected, byte actual) {
+ assertEquals(message, new Byte(expected), new Byte(actual));
+ }
+ /**
+ * Asserts that two bytes are equal.
+ */
+ static public void assertEquals(byte expected, byte actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two chars are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, char expected, char actual) {
+ assertEquals(message, new Character(expected), new Character(actual));
+ }
+ /**
+ * Asserts that two chars are equal.
+ */
+ static public void assertEquals(char expected, char actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two shorts are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, short expected, short actual) {
+ assertEquals(message, new Short(expected), new Short(actual));
+ }
+ /**
+ * Asserts that two shorts are equal.
+ */
+ static public void assertEquals(short expected, short actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two ints are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, int expected, int actual) {
+ assertEquals(message, new Integer(expected), new Integer(actual));
+ }
+ /**
+ * Asserts that two ints are equal.
+ */
+ static public void assertEquals(int expected, int actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that an object isn't null.
+ */
+ static public void assertNotNull(Object object) {
+ assertNotNull(null, object);
+ }
+ /**
+ * Asserts that an object isn't null. If it is
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertNotNull(String message, Object object) {
+ assertTrue(message, object != null);
+ }
+ /**
+ * Asserts that an object is null.
+ */
+ static public void assertNull(Object object) {
+ assertNull(null, object);
+ }
+ /**
+ * Asserts that an object is null. If it is not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertNull(String message, Object object) {
+ assertTrue(message, object == null);
+ }
+ /**
+ * Asserts that two objects refer to the same object. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertSame(String message, Object expected, Object actual) {
+ if (expected == actual)
+ return;
+ failNotSame(message, expected, actual);
+ }
+ /**
+ * Asserts that two objects refer to the same object. If they are not
+ * the same an AssertionFailedError is thrown.
+ */
+ static public void assertSame(Object expected, Object actual) {
+ assertSame(null, expected, actual);
+ }
+ /**
+ * Asserts that two objects refer to the same object. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertNotSame(String message, Object expected, Object actual) {
+ if (expected == actual)
+ failSame(message);
+ }
+ /**
+ * Asserts that two objects refer to the same object. If they are not
+ * the same an AssertionFailedError is thrown.
+ */
+ static public void assertNotSame(Object expected, Object actual) {
+ assertNotSame(null, expected, actual);
+ }
+
+ static private void failSame(String message) {
+ String formatted= "";
+ if (message != null)
+ formatted= message+" ";
+ fail(formatted+"expected not same");
+ }
+
+ static private void failNotSame(String message, Object expected, Object actual) {
+ String formatted= "";
+ if (message != null)
+ formatted= message+" ";
+ fail(formatted+"expected same:<"+expected+"> was not:<"+actual+">");
+ }
+
+ static private void failNotEquals(String message, Object expected, Object actual) {
+ fail(format(message, expected, actual));
+ }
+
+ static String format(String message, Object expected, Object actual) {
+ String formatted= "";
+ if (message != null)
+ formatted= message+" ";
+ return formatted+"expected:<"+expected+"> but was:<"+actual+">";
+ }
+}
diff --git a/dx/src/junit/framework/AssertionFailedError.java b/dx/src/junit/framework/AssertionFailedError.java
new file mode 100644
index 0000000..9aee001
--- /dev/null
+++ b/dx/src/junit/framework/AssertionFailedError.java
@@ -0,0 +1,13 @@
+package junit.framework;
+
+/**
+ * Thrown when an assertion failed.
+ */
+public class AssertionFailedError extends Error {
+
+ public AssertionFailedError () {
+ }
+ public AssertionFailedError (String message) {
+ super (message);
+ }
+}
diff --git a/dx/src/junit/framework/ComparisonFailure.java b/dx/src/junit/framework/ComparisonFailure.java
new file mode 100644
index 0000000..1bfe591
--- /dev/null
+++ b/dx/src/junit/framework/ComparisonFailure.java
@@ -0,0 +1,68 @@
+package junit.framework;
+
+/**
+ * Thrown when an assert equals for Strings failed.
+ *
+ * Inspired by a patch from Alex Chaffee mailto:alex@purpletech.com
+ */
+public class ComparisonFailure extends AssertionFailedError {
+ private String fExpected;
+ private String fActual;
+
+ /**
+ * Constructs a comparison failure.
+ * @param message the identifying message or null
+ * @param expected the expected string value
+ * @param actual the actual string value
+ */
+ public ComparisonFailure (String message, String expected, String actual) {
+ super (message);
+ fExpected= expected;
+ fActual= actual;
+ }
+
+ /**
+ * Returns "..." in place of common prefix and "..." in
+ * place of common suffix between expected and actual.
+ *
+ * @see java.lang.Throwable#getMessage()
+ */
+ public String getMessage() {
+ if (fExpected == null || fActual == null)
+ return Assert.format(super.getMessage(), fExpected, fActual);
+
+ int end= Math.min(fExpected.length(), fActual.length());
+
+ int i= 0;
+ for(; i < end; i++) {
+ if (fExpected.charAt(i) != fActual.charAt(i))
+ break;
+ }
+ int j= fExpected.length()-1;
+ int k= fActual.length()-1;
+ for (; k >= i && j >= i; k--,j--) {
+ if (fExpected.charAt(j) != fActual.charAt(k))
+ break;
+ }
+ String actual, expected;
+
+ // equal strings
+ if (j < i && k < i) {
+ expected= fExpected;
+ actual= fActual;
+ } else {
+ expected= fExpected.substring(i, j+1);
+ actual= fActual.substring(i, k+1);
+ if (i <= end && i > 0) {
+ expected= "..."+expected;
+ actual= "..."+actual;
+ }
+
+ if (j < fExpected.length()-1)
+ expected= expected+"...";
+ if (k < fActual.length()-1)
+ actual= actual+"...";
+ }
+ return Assert.format(super.getMessage(), expected, actual);
+ }
+}
diff --git a/dx/src/junit/framework/Protectable.java b/dx/src/junit/framework/Protectable.java
new file mode 100644
index 0000000..8243555
--- /dev/null
+++ b/dx/src/junit/framework/Protectable.java
@@ -0,0 +1,14 @@
+package junit.framework;
+
+/**
+ * A <em>Protectable</em> can be run and can throw a Throwable.
+ *
+ * @see TestResult
+ */
+public interface Protectable {
+
+ /**
+ * Run the the following method protected.
+ */
+ public abstract void protect() throws Throwable;
+}
diff --git a/dx/src/junit/framework/Test.java b/dx/src/junit/framework/Test.java
new file mode 100644
index 0000000..1c6d57b
--- /dev/null
+++ b/dx/src/junit/framework/Test.java
@@ -0,0 +1,17 @@
+package junit.framework;
+
+/**
+ * A <em>Test</em> can be run and collect its results.
+ *
+ * @see TestResult
+ */
+public interface Test {
+ /**
+ * Counts the number of test cases that will be run by this test.
+ */
+ public abstract int countTestCases();
+ /**
+ * Runs a test and collects its result in a TestResult instance.
+ */
+ public abstract void run(TestResult result);
+}
diff --git a/dx/src/junit/framework/TestCase.java b/dx/src/junit/framework/TestCase.java
new file mode 100644
index 0000000..8988c45
--- /dev/null
+++ b/dx/src/junit/framework/TestCase.java
@@ -0,0 +1,197 @@
+package junit.framework;
+
+import java.lang.reflect.*;
+
+/**
+ * A test case defines the fixture to run multiple tests. To define a test case<br>
+ * 1) implement a subclass of TestCase<br>
+ * 2) define instance variables that store the state of the fixture<br>
+ * 3) initialize the fixture state by overriding <code>setUp</code><br>
+ * 4) clean-up after a test by overriding <code>tearDown</code>.<br>
+ * Each test runs in its own fixture so there
+ * can be no side effects among test runs.
+ * Here is an example:
+ * <pre>
+ * public class MathTest extends TestCase {
+ * protected double fValue1;
+ * protected double fValue2;
+ *
+ * protected void setUp() {
+ * fValue1= 2.0;
+ * fValue2= 3.0;
+ * }
+ * }
+ * </pre>
+ *
+ * For each test implement a method which interacts
+ * with the fixture. Verify the expected results with assertions specified
+ * by calling <code>assertTrue</code> with a boolean.
+ * <pre>
+ * public void testAdd() {
+ * double result= fValue1 + fValue2;
+ * assertTrue(result == 5.0);
+ * }
+ * </pre>
+ * Once the methods are defined you can run them. The framework supports
+ * both a static type safe and more dynamic way to run a test.
+ * In the static way you override the runTest method and define the method to
+ * be invoked. A convenient way to do so is with an anonymous inner class.
+ * <pre>
+ * TestCase test= new MathTest("add") {
+ * public void runTest() {
+ * testAdd();
+ * }
+ * };
+ * test.run();
+ * </pre>
+ * The dynamic way uses reflection to implement <code>runTest</code>. It dynamically finds
+ * and invokes a method.
+ * In this case the name of the test case has to correspond to the test method
+ * to be run.
+ * <pre>
+ * TestCase= new MathTest("testAdd");
+ * test.run();
+ * </pre>
+ * The tests to be run can be collected into a TestSuite. JUnit provides
+ * different <i>test runners</i> which can run a test suite and collect the results.
+ * A test runner either expects a static method <code>suite</code> as the entry
+ * point to get a test to run or it will extract the suite automatically.
+ * <pre>
+ * public static Test suite() {
+ * suite.addTest(new MathTest("testAdd"));
+ * suite.addTest(new MathTest("testDivideByZero"));
+ * return suite;
+ * }
+ * </pre>
+ * @see TestResult
+ * @see TestSuite
+ */
+
+public abstract class TestCase extends Assert implements Test {
+ /**
+ * the name of the test case
+ */
+ private String fName;
+
+ /**
+ * No-arg constructor to enable serialization. This method
+ * is not intended to be used by mere mortals without calling setName().
+ */
+ public TestCase() {
+ fName= null;
+ }
+ /**
+ * Constructs a test case with the given name.
+ */
+ public TestCase(String name) {
+ fName= name;
+ }
+ /**
+ * Counts the number of test cases executed by run(TestResult result).
+ */
+ public int countTestCases() {
+ return 1;
+ }
+ /**
+ * Creates a default TestResult object
+ *
+ * @see TestResult
+ */
+ protected TestResult createResult() {
+ return new TestResult();
+ }
+ /**
+ * A convenience method to run this test, collecting the results with a
+ * default TestResult object.
+ *
+ * @see TestResult
+ */
+ public TestResult run() {
+ TestResult result= createResult();
+ run(result);
+ return result;
+ }
+ /**
+ * Runs the test case and collects the results in TestResult.
+ */
+ public void run(TestResult result) {
+ result.run(this);
+ }
+ /**
+ * Runs the bare test sequence.
+ * @exception Throwable if any exception is thrown
+ */
+ public void runBare() throws Throwable {
+ setUp();
+ try {
+ runTest();
+ }
+ finally {
+ tearDown();
+ }
+ }
+ /**
+ * Override to run the test and assert its state.
+ * @exception Throwable if any exception is thrown
+ */
+ protected void runTest() throws Throwable {
+ assertNotNull(fName);
+ Method runMethod= null;
+ try {
+ // use getMethod to get all public inherited
+ // methods. getDeclaredMethods returns all
+ // methods of this class but excludes the
+ // inherited ones.
+ runMethod= getClass().getMethod(fName, (Class[]) null);
+ } catch (NoSuchMethodException e) {
+ fail("Method \""+fName+"\" not found");
+ }
+ if (!Modifier.isPublic(runMethod.getModifiers())) {
+ fail("Method \""+fName+"\" should be public");
+ }
+
+ try {
+ runMethod.invoke(this, (Object[]) new Class[0]);
+ }
+ catch (InvocationTargetException e) {
+ e.fillInStackTrace();
+ throw e.getTargetException();
+ }
+ catch (IllegalAccessException e) {
+ e.fillInStackTrace();
+ throw e;
+ }
+ }
+ /**
+ * Sets up the fixture, for example, open a network connection.
+ * This method is called before a test is executed.
+ */
+ protected void setUp() throws Exception {
+ }
+ /**
+ * Tears down the fixture, for example, close a network connection.
+ * This method is called after a test is executed.
+ */
+ protected void tearDown() throws Exception {
+ }
+ /**
+ * Returns a string representation of the test case
+ */
+ public String toString() {
+ return getName() + "(" + getClass().getName() + ")";
+ }
+ /**
+ * Gets the name of a TestCase
+ * @return returns a String
+ */
+ public String getName() {
+ return fName;
+ }
+ /**
+ * Sets the name of a TestCase
+ * @param name The name to set
+ */
+ public void setName(String name) {
+ fName= name;
+ }
+}
diff --git a/dx/src/junit/framework/TestFailure.java b/dx/src/junit/framework/TestFailure.java
new file mode 100644
index 0000000..664747d
--- /dev/null
+++ b/dx/src/junit/framework/TestFailure.java
@@ -0,0 +1,57 @@
+package junit.framework;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+
+/**
+ * A <code>TestFailure</code> collects a failed test together with
+ * the caught exception.
+ * @see TestResult
+ */
+public class TestFailure extends Object {
+ protected Test fFailedTest;
+ protected Throwable fThrownException;
+
+
+ /**
+ * Constructs a TestFailure with the given test and exception.
+ */
+ public TestFailure(Test failedTest, Throwable thrownException) {
+ fFailedTest= failedTest;
+ fThrownException= thrownException;
+ }
+ /**
+ * Gets the failed test.
+ */
+ public Test failedTest() {
+ return fFailedTest;
+ }
+ /**
+ * Gets the thrown exception.
+ */
+ public Throwable thrownException() {
+ return fThrownException;
+ }
+ /**
+ * Returns a short description of the failure.
+ */
+ public String toString() {
+ StringBuffer buffer= new StringBuffer();
+ buffer.append(fFailedTest+": "+fThrownException.getMessage());
+ return buffer.toString();
+ }
+ public String trace() {
+ StringWriter stringWriter= new StringWriter();
+ PrintWriter writer= new PrintWriter(stringWriter);
+ thrownException().printStackTrace(writer);
+ StringBuffer buffer= stringWriter.getBuffer();
+ return buffer.toString();
+ }
+ public String exceptionMessage() {
+ return thrownException().getMessage();
+ }
+ public boolean isFailure() {
+ return thrownException() instanceof AssertionFailedError;
+ }
+}
diff --git a/dx/src/junit/framework/TestListener.java b/dx/src/junit/framework/TestListener.java
new file mode 100644
index 0000000..7558187
--- /dev/null
+++ b/dx/src/junit/framework/TestListener.java
@@ -0,0 +1,23 @@
+package junit.framework;
+
+/**
+ * A Listener for test progress
+ */
+public interface TestListener {
+ /**
+ * An error occurred.
+ */
+ public void addError(Test test, Throwable t);
+ /**
+ * A failure occurred.
+ */
+ public void addFailure(Test test, AssertionFailedError t);
+ /**
+ * A test ended.
+ */
+ public void endTest(Test test);
+ /**
+ * A test started.
+ */
+ public void startTest(Test test);
+}
diff --git a/dx/src/junit/framework/TestResult.java b/dx/src/junit/framework/TestResult.java
new file mode 100644
index 0000000..4b1a7e2
--- /dev/null
+++ b/dx/src/junit/framework/TestResult.java
@@ -0,0 +1,166 @@
+package junit.framework;
+
+import java.util.Vector;
+import java.util.Enumeration;
+
+/**
+ * A <code>TestResult</code> collects the results of executing
+ * a test case. It is an instance of the Collecting Parameter pattern.
+ * The test framework distinguishes between <i>failures</i> and <i>errors</i>.
+ * A failure is anticipated and checked for with assertions. Errors are
+ * unanticipated problems like an <code>ArrayIndexOutOfBoundsException</code>.
+ *
+ * @see Test
+ */
+public class TestResult extends Object {
+ protected Vector<TestFailure> fFailures;
+ protected Vector<TestFailure> fErrors;
+ protected Vector<TestListener> fListeners;
+ protected int fRunTests;
+ private boolean fStop;
+
+ public TestResult() {
+ fFailures= new Vector<TestFailure>();
+ fErrors= new Vector<TestFailure>();
+ fListeners= new Vector<TestListener>();
+ fRunTests= 0;
+ fStop= false;
+ }
+ /**
+ * Adds an error to the list of errors. The passed in exception
+ * caused the error.
+ */
+ public synchronized void addError(Test test, Throwable t) {
+ fErrors.addElement(new TestFailure(test, t));
+ for (Enumeration e= cloneListeners().elements(); e.hasMoreElements(); ) {
+ ((TestListener)e.nextElement()).addError(test, t);
+ }
+ }
+ /**
+ * Adds a failure to the list of failures. The passed in exception
+ * caused the failure.
+ */
+ public synchronized void addFailure(Test test, AssertionFailedError t) {
+ fFailures.addElement(new TestFailure(test, t));
+ for (Enumeration e= cloneListeners().elements(); e.hasMoreElements(); ) {
+ ((TestListener)e.nextElement()).addFailure(test, t);
+ }
+ }
+ /**
+ * Registers a TestListener
+ */
+ public synchronized void addListener(TestListener listener) {
+ fListeners.addElement(listener);
+ }
+ /**
+ * Unregisters a TestListener
+ */
+ public synchronized void removeListener(TestListener listener) {
+ fListeners.removeElement(listener);
+ }
+ /**
+ * Returns a copy of the listeners.
+ */
+ private synchronized Vector cloneListeners() {
+ return (Vector)fListeners.clone();
+ }
+ /**
+ * Informs the result that a test was completed.
+ */
+ public void endTest(Test test) {
+ for (Enumeration e= cloneListeners().elements(); e.hasMoreElements(); ) {
+ ((TestListener)e.nextElement()).endTest(test);
+ }
+ }
+ /**
+ * Gets the number of detected errors.
+ */
+ public synchronized int errorCount() {
+ return fErrors.size();
+ }
+ /**
+ * Returns an Enumeration for the errors
+ */
+ public synchronized Enumeration errors() {
+ return fErrors.elements();
+ }
+ /**
+ * Gets the number of detected failures.
+ */
+ public synchronized int failureCount() {
+ return fFailures.size();
+ }
+ /**
+ * Returns an Enumeration for the failures
+ */
+ public synchronized Enumeration failures() {
+ return fFailures.elements();
+ }
+ /**
+ * Runs a TestCase.
+ */
+ protected void run(final TestCase test) {
+ startTest(test);
+ Protectable p= new Protectable() {
+ public void protect() throws Throwable {
+ test.runBare();
+ }
+ };
+ runProtected(test, p);
+
+ endTest(test);
+ }
+ /**
+ * Gets the number of run tests.
+ */
+ public synchronized int runCount() {
+ return fRunTests;
+ }
+ /**
+ * Runs a TestCase.
+ */
+ public void runProtected(final Test test, Protectable p) {
+ try {
+ p.protect();
+ }
+ catch (AssertionFailedError e) {
+ addFailure(test, e);
+ }
+ catch (ThreadDeath e) { // don't catch ThreadDeath by accident
+ throw e;
+ }
+ catch (Throwable e) {
+ addError(test, e);
+ }
+ }
+ /**
+ * Checks whether the test run should stop
+ */
+ public synchronized boolean shouldStop() {
+ return fStop;
+ }
+ /**
+ * Informs the result that a test will be started.
+ */
+ public void startTest(Test test) {
+ final int count= test.countTestCases();
+ synchronized(this) {
+ fRunTests+= count;
+ }
+ for (Enumeration e= cloneListeners().elements(); e.hasMoreElements(); ) {
+ ((TestListener)e.nextElement()).startTest(test);
+ }
+ }
+ /**
+ * Marks that the test run should stop.
+ */
+ public synchronized void stop() {
+ fStop= true;
+ }
+ /**
+ * Returns whether the entire test was successful or not.
+ */
+ public synchronized boolean wasSuccessful() {
+ return failureCount() == 0 && errorCount() == 0;
+ }
+}
diff --git a/dx/src/junit/framework/TestSuite.java b/dx/src/junit/framework/TestSuite.java
new file mode 100644
index 0000000..2987ad1
--- /dev/null
+++ b/dx/src/junit/framework/TestSuite.java
@@ -0,0 +1,265 @@
+package junit.framework;
+
+import java.util.Vector;
+import java.util.Enumeration;
+import java.io.PrintWriter;import java.io.StringWriter;import java.lang.reflect.*;
+import java.lang.reflect.Constructor;
+
+/**
+ * A <code>TestSuite</code> is a <code>Composite</code> of Tests.
+ * It runs a collection of test cases. Here is an example using
+ * the dynamic test definition.
+ * <pre>
+ * TestSuite suite= new TestSuite();
+ * suite.addTest(new MathTest("testAdd"));
+ * suite.addTest(new MathTest("testDivideByZero"));
+ * </pre>
+ * Alternatively, a TestSuite can extract the tests to be run automatically.
+ * To do so you pass the class of your TestCase class to the
+ * TestSuite constructor.
+ * <pre>
+ * TestSuite suite= new TestSuite(MathTest.class);
+ * </pre>
+ * This constructor creates a suite with all the methods
+ * starting with "test" that take no arguments.
+ *
+ * @see Test
+ */
+public class TestSuite implements Test {
+
+ private Vector<Test> fTests= new Vector<Test>(10);
+ private String fName;
+
+ /**
+ * Constructs an empty TestSuite.
+ */
+ public TestSuite() {
+ }
+
+ /**
+ * Constructs a TestSuite from the given class with the given name.
+ * @see TestSuite#TestSuite(Class)
+ */
+ public TestSuite(Class theClass, String name) {
+ this(theClass);
+ setName(name);
+ }
+
+ /**
+ * Constructs a TestSuite from the given class. Adds all the methods
+ * starting with "test" as test cases to the suite.
+ * Parts of this method was written at 2337 meters in the Huffihutte,
+ * Kanton Uri
+ */
+ public TestSuite(final Class theClass) {
+ fName= theClass.getName();
+ try {
+ getTestConstructor(theClass); // Avoid generating multiple error messages
+ } catch (NoSuchMethodException e) {
+ addTest(warning("Class "+theClass.getName()+" has no public constructor TestCase(String name) or TestCase()"));
+ return;
+ }
+
+ if (!Modifier.isPublic(theClass.getModifiers())) {
+ addTest(warning("Class "+theClass.getName()+" is not public"));
+ return;
+ }
+
+ Class superClass= theClass;
+ Vector<String> names= new Vector<String>();
+ while (Test.class.isAssignableFrom(superClass)) {
+ Method[] methods= superClass.getDeclaredMethods();
+ for (int i= 0; i < methods.length; i++) {
+ addTestMethod(methods[i], names, theClass);
+ }
+ superClass= superClass.getSuperclass();
+ }
+ if (fTests.size() == 0)
+ addTest(warning("No tests found in "+theClass.getName()));
+ }
+
+ /**
+ * Constructs an empty TestSuite.
+ */
+ public TestSuite(String name) {
+ setName(name);
+ }
+
+ /**
+ * Adds a test to the suite.
+ */
+ public void addTest(Test test) {
+ fTests.addElement(test);
+ }
+
+ /**
+ * Adds the tests from the given class to the suite
+ */
+ public void addTestSuite(Class testClass) {
+ addTest(new TestSuite(testClass));
+ }
+
+ private void addTestMethod(Method m, Vector<String> names, Class theClass) {
+ String name= m.getName();
+ if (names.contains(name))
+ return;
+ if (! isPublicTestMethod(m)) {
+ if (isTestMethod(m))
+ addTest(warning("Test method isn't public: "+m.getName()));
+ return;
+ }
+ names.addElement(name);
+ addTest(createTest(theClass, name));
+ }
+
+ /**
+ * ...as the moon sets over the early morning Merlin, Oregon
+ * mountains, our intrepid adventurers type...
+ */
+ static public Test createTest(Class theClass, String name) {
+ Constructor constructor;
+ try {
+ constructor= getTestConstructor(theClass);
+ } catch (NoSuchMethodException e) {
+ return warning("Class "+theClass.getName()+" has no public constructor TestCase(String name) or TestCase()");
+ }
+ Object test;
+ try {
+ if (constructor.getParameterTypes().length == 0) {
+ test= constructor.newInstance(new Object[0]);
+ if (test instanceof TestCase)
+ ((TestCase) test).setName(name);
+ } else {
+ test= constructor.newInstance(new Object[]{name});
+ }
+ } catch (InstantiationException e) {
+ return(warning("Cannot instantiate test case: "+name+" ("+exceptionToString(e)+")"));
+ } catch (InvocationTargetException e) {
+ return(warning("Exception in constructor: "+name+" ("+exceptionToString(e.getTargetException())+")"));
+ } catch (IllegalAccessException e) {
+ return(warning("Cannot access test case: "+name+" ("+exceptionToString(e)+")"));
+ }
+ return (Test) test;
+ }
+
+ /**
+ * Converts the stack trace into a string
+ */
+ private static String exceptionToString(Throwable t) {
+ StringWriter stringWriter= new StringWriter();
+ PrintWriter writer= new PrintWriter(stringWriter);
+ t.printStackTrace(writer);
+ return stringWriter.toString();
+
+ }
+
+ /**
+ * Counts the number of test cases that will be run by this test.
+ */
+ public int countTestCases() {
+ int count= 0;
+ for (Enumeration e= tests(); e.hasMoreElements(); ) {
+ Test test= (Test)e.nextElement();
+ count= count + test.countTestCases();
+ }
+ return count;
+ }
+
+ /**
+ * Gets a constructor which takes a single String as
+ * its argument or a no arg constructor.
+ */
+ public static Constructor getTestConstructor(Class theClass) throws NoSuchMethodException {
+ Class[] args= { String.class };
+ try {
+ return theClass.getConstructor(args);
+ } catch (NoSuchMethodException e) {
+ // fall through
+ }
+ return theClass.getConstructor(new Class[0]);
+ }
+
+ private boolean isPublicTestMethod(Method m) {
+ return isTestMethod(m) && Modifier.isPublic(m.getModifiers());
+ }
+
+ private boolean isTestMethod(Method m) {
+ String name= m.getName();
+ Class[] parameters= m.getParameterTypes();
+ Class returnType= m.getReturnType();
+ return parameters.length == 0 && name.startsWith("test") && returnType.equals(Void.TYPE);
+ }
+
+ /**
+ * Runs the tests and collects their result in a TestResult.
+ */
+ public void run(TestResult result) {
+ for (Enumeration e= tests(); e.hasMoreElements(); ) {
+ if (result.shouldStop() )
+ break;
+ Test test= (Test)e.nextElement();
+ runTest(test, result);
+ }
+ }
+
+ public void runTest(Test test, TestResult result) {
+ test.run(result);
+ }
+
+ /**
+ * Returns the test at the given index
+ */
+ public Test testAt(int index) {
+ return (Test)fTests.elementAt(index);
+ }
+
+ /**
+ * Returns the number of tests in this suite
+ */
+ public int testCount() {
+ return fTests.size();
+ }
+
+ /**
+ * Returns the tests as an enumeration
+ */
+ public Enumeration tests() {
+ return fTests.elements();
+ }
+
+ /**
+ */
+ public String toString() {
+ if (getName() != null)
+ return getName();
+ return super.toString();
+ }
+
+ /**
+ * Sets the name of the suite.
+ * @param name The name to set
+ */
+ public void setName(String name) {
+ fName= name;
+ }
+
+ /**
+ * Returns the name of the suite. Not all
+ * test suites have a name and this method
+ * can return null.
+ */
+ public String getName() {
+ return fName;
+ }
+
+ /**
+ * Returns a test which will fail and log a warning message.
+ */
+ private static Test warning(final String message) {
+ return new TestCase("warning") {
+ protected void runTest() {
+ fail(message);
+ }
+ };
+ }
+}
diff --git a/dx/src/junit/runner/BaseTestRunner.java b/dx/src/junit/runner/BaseTestRunner.java
new file mode 100644
index 0000000..39c8c2f
--- /dev/null
+++ b/dx/src/junit/runner/BaseTestRunner.java
@@ -0,0 +1,323 @@
+package junit.runner;
+
+import junit.framework.*;
+import java.lang.reflect.*;
+import java.text.NumberFormat;
+import java.io.*;
+import java.util.*;
+
+/**
+ * Base class for all test runners.
+ * This class was born live on stage in Sardinia during XP2000.
+ */
+public abstract class BaseTestRunner implements TestListener {
+ public static final String SUITE_METHODNAME= "suite";
+
+ private static Properties fPreferences;
+ static int fgMaxMessageLength= 500;
+ static boolean fgFilterStack= true;
+ boolean fLoading= true;
+
+ /*
+ * Implementation of TestListener
+ */
+ public synchronized void startTest(Test test) {
+ testStarted(test.toString());
+ }
+
+ protected static void setPreferences(Properties preferences) {
+ fPreferences= preferences;
+ }
+
+ protected static Properties getPreferences() {
+ if (fPreferences == null) {
+ fPreferences= new Properties();
+ fPreferences.put("loading", "true");
+ fPreferences.put("filterstack", "true");
+ readPreferences();
+ }
+ return fPreferences;
+ }
+
+ public static void savePreferences() throws IOException {
+ FileOutputStream fos= new FileOutputStream(getPreferencesFile());
+ try {
+ getPreferences().store(fos, "");
+ } finally {
+ fos.close();
+ }
+ }
+
+ public void setPreference(String key, String value) {
+ getPreferences().setProperty(key, value);
+ }
+
+ public synchronized void endTest(Test test) {
+ testEnded(test.toString());
+ }
+
+ public synchronized void addError(final Test test, final Throwable t) {
+ testFailed(TestRunListener.STATUS_ERROR, test, t);
+ }
+
+ public synchronized void addFailure(final Test test, final AssertionFailedError t) {
+ testFailed(TestRunListener.STATUS_FAILURE, test, t);
+ }
+
+ // TestRunListener implementation
+
+ public abstract void testStarted(String testName);
+
+ public abstract void testEnded(String testName);
+
+ public abstract void testFailed(int status, Test test, Throwable t);
+
+ /**
+ * Returns the Test corresponding to the given suite. This is
+ * a template method, subclasses override runFailed(), clearStatus().
+ */
+ public Test getTest(String suiteClassName) {
+ if (suiteClassName.length() <= 0) {
+ clearStatus();
+ return null;
+ }
+ Class testClass= null;
+ try {
+ testClass= loadSuiteClass(suiteClassName);
+ } catch (ClassNotFoundException e) {
+ String clazz= e.getMessage();
+ if (clazz == null)
+ clazz= suiteClassName;
+ runFailed("Class not found \""+clazz+"\"");
+ return null;
+ } catch(Exception e) {
+ runFailed("Error: "+e.toString());
+ return null;
+ }
+ Method suiteMethod= null;
+ try {
+ suiteMethod= testClass.getMethod(SUITE_METHODNAME, new Class[0]);
+ } catch(Exception e) {
+ // try to extract a test suite automatically
+ clearStatus();
+ return new TestSuite(testClass);
+ }
+ if (! Modifier.isStatic(suiteMethod.getModifiers())) {
+ runFailed("Suite() method must be static");
+ return null;
+ }
+ Test test= null;
+ try {
+ test= (Test)suiteMethod.invoke(null, (Object[]) new Class[0]); // static method
+ if (test == null)
+ return test;
+ }
+ catch (InvocationTargetException e) {
+ runFailed("Failed to invoke suite():" + e.getTargetException().toString());
+ return null;
+ }
+ catch (IllegalAccessException e) {
+ runFailed("Failed to invoke suite():" + e.toString());
+ return null;
+ }
+
+ clearStatus();
+ return test;
+ }
+
+ /**
+ * Returns the formatted string of the elapsed time.
+ */
+ public String elapsedTimeAsString(long runTime) {
+ return NumberFormat.getInstance().format((double)runTime/1000);
+ }
+
+ /**
+ * Processes the command line arguments and
+ * returns the name of the suite class to run or null
+ */
+ protected String processArguments(String[] args) {
+ String suiteName= null;
+ for (int i= 0; i < args.length; i++) {
+ if (args[i].equals("-noloading")) {
+ setLoading(false);
+ } else if (args[i].equals("-nofilterstack")) {
+ fgFilterStack= false;
+ } else if (args[i].equals("-c")) {
+ if (args.length > i+1)
+ suiteName= extractClassName(args[i+1]);
+ else
+ System.out.println("Missing Test class name");
+ i++;
+ } else {
+ suiteName= args[i];
+ }
+ }
+ return suiteName;
+ }
+
+ /**
+ * Sets the loading behaviour of the test runner
+ */
+ public void setLoading(boolean enable) {
+ fLoading= enable;
+ }
+ /**
+ * Extract the class name from a String in VA/Java style
+ */
+ public String extractClassName(String className) {
+ if(className.startsWith("Default package for"))
+ return className.substring(className.lastIndexOf(".")+1);
+ return className;
+ }
+
+ /**
+ * Truncates a String to the maximum length.
+ */
+ public static String truncate(String s) {
+ if (fgMaxMessageLength != -1 && s.length() > fgMaxMessageLength)
+ s= s.substring(0, fgMaxMessageLength)+"...";
+ return s;
+ }
+
+ /**
+ * Override to define how to handle a failed loading of
+ * a test suite.
+ */
+ protected abstract void runFailed(String message);
+
+ /**
+ * Returns the loaded Class for a suite name.
+ */
+ protected Class loadSuiteClass(String suiteClassName) throws ClassNotFoundException {
+ return getLoader().load(suiteClassName);
+ }
+
+ /**
+ * Clears the status message.
+ */
+ protected void clearStatus() { // Belongs in the GUI TestRunner class
+ }
+
+ /**
+ * Returns the loader to be used.
+ */
+ public TestSuiteLoader getLoader() {
+ if (useReloadingTestSuiteLoader())
+ return new ReloadingTestSuiteLoader();
+ return new StandardTestSuiteLoader();
+ }
+
+ protected boolean useReloadingTestSuiteLoader() {
+ return getPreference("loading").equals("true") && !inVAJava() && fLoading;
+ }
+
+ private static File getPreferencesFile() {
+ String home= System.getProperty("user.home");
+ return new File(home, "junit.properties");
+ }
+
+ private static void readPreferences() {
+ InputStream is= null;
+ try {
+ is= new FileInputStream(getPreferencesFile());
+ setPreferences(new Properties(getPreferences()));
+ getPreferences().load(is);
+ } catch (IOException e) {
+ try {
+ if (is != null)
+ is.close();
+ } catch (IOException e1) {
+ }
+ }
+ }
+
+ public static String getPreference(String key) {
+ return getPreferences().getProperty(key);
+ }
+
+ public static int getPreference(String key, int dflt) {
+ String value= getPreference(key);
+ int intValue= dflt;
+ if (value == null)
+ return intValue;
+ try {
+ intValue= Integer.parseInt(value);
+ } catch (NumberFormatException ne) {
+ }
+ return intValue;
+ }
+
+ public static boolean inVAJava() {
+ try {
+ Class.forName("com.ibm.uvm.tools.DebugSupport");
+ }
+ catch (Exception e) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Returns a filtered stack trace
+ */
+ public static String getFilteredTrace(Throwable t) {
+ StringWriter stringWriter= new StringWriter();
+ PrintWriter writer= new PrintWriter(stringWriter);
+ t.printStackTrace(writer);
+ StringBuffer buffer= stringWriter.getBuffer();
+ String trace= buffer.toString();
+ return BaseTestRunner.getFilteredTrace(trace);
+ }
+
+ /**
+ * Filters stack frames from internal JUnit classes
+ */
+ public static String getFilteredTrace(String stack) {
+ if (showStackRaw())
+ return stack;
+
+ StringWriter sw= new StringWriter();
+ PrintWriter pw= new PrintWriter(sw);
+ StringReader sr= new StringReader(stack);
+ BufferedReader br= new BufferedReader(sr);
+
+ String line;
+ try {
+ while ((line= br.readLine()) != null) {
+ if (!filterLine(line))
+ pw.println(line);
+ }
+ } catch (Exception IOException) {
+ return stack; // return the stack unfiltered
+ }
+ return sw.toString();
+ }
+
+ protected static boolean showStackRaw() {
+ return !getPreference("filterstack").equals("true") || fgFilterStack == false;
+ }
+
+ static boolean filterLine(String line) {
+ String[] patterns= new String[] {
+ "junit.framework.TestCase",
+ "junit.framework.TestResult",
+ "junit.framework.TestSuite",
+ "junit.framework.Assert.", // don't filter AssertionFailure
+ "junit.swingui.TestRunner",
+ "junit.awtui.TestRunner",
+ "junit.textui.TestRunner",
+ "java.lang.reflect.Method.invoke("
+ };
+ for (int i= 0; i < patterns.length; i++) {
+ if (line.indexOf(patterns[i]) > 0)
+ return true;
+ }
+ return false;
+ }
+
+ static {
+ fgMaxMessageLength= getPreference("maxmessage", fgMaxMessageLength);
+ }
+
+}
diff --git a/dx/src/junit/runner/ClassPathTestCollector.java b/dx/src/junit/runner/ClassPathTestCollector.java
new file mode 100644
index 0000000..24bf1e9
--- /dev/null
+++ b/dx/src/junit/runner/ClassPathTestCollector.java
@@ -0,0 +1,80 @@
+package junit.runner;
+
+import java.util.*;
+import java.io.*;
+
+/**
+ * An implementation of a TestCollector that consults the
+ * class path. It considers all classes on the class path
+ * excluding classes in JARs. It leaves it up to subclasses
+ * to decide whether a class is a runnable Test.
+ *
+ * @see TestCollector
+ */
+public abstract class ClassPathTestCollector implements TestCollector {
+
+ static final int SUFFIX_LENGTH= ".class".length();
+
+ public ClassPathTestCollector() {
+ }
+
+ public Enumeration collectTests() {
+ String classPath= System.getProperty("java.class.path");
+ Hashtable result = collectFilesInPath(classPath);
+ return result.elements();
+ }
+
+ public Hashtable collectFilesInPath(String classPath) {
+ Hashtable result= collectFilesInRoots(splitClassPath(classPath));
+ return result;
+ }
+
+ Hashtable collectFilesInRoots(Vector roots) {
+ Hashtable<String,String> result= new Hashtable<String,String>(100);
+ Enumeration e= roots.elements();
+ while (e.hasMoreElements())
+ gatherFiles(new File((String)e.nextElement()), "", result);
+ return result;
+ }
+
+ void gatherFiles(File classRoot, String classFileName, Hashtable<String,String> result) {
+ File thisRoot= new File(classRoot, classFileName);
+ if (thisRoot.isFile()) {
+ if (isTestClass(classFileName)) {
+ String className= classNameFromFile(classFileName);
+ result.put(className, className);
+ }
+ return;
+ }
+ String[] contents= thisRoot.list();
+ if (contents != null) {
+ for (int i= 0; i < contents.length; i++)
+ gatherFiles(classRoot, classFileName+File.separatorChar+contents[i], result);
+ }
+ }
+
+ Vector splitClassPath(String classPath) {
+ Vector<String> result= new Vector<String>();
+ String separator= System.getProperty("path.separator");
+ StringTokenizer tokenizer= new StringTokenizer(classPath, separator);
+ while (tokenizer.hasMoreTokens())
+ result.addElement(tokenizer.nextToken());
+ return result;
+ }
+
+ protected boolean isTestClass(String classFileName) {
+ return
+ classFileName.endsWith(".class") &&
+ classFileName.indexOf('$') < 0 &&
+ classFileName.indexOf("Test") > 0;
+ }
+
+ protected String classNameFromFile(String classFileName) {
+ // convert /a/b.class to a.b
+ String s= classFileName.substring(0, classFileName.length()-SUFFIX_LENGTH);
+ String s2= s.replace(File.separatorChar, '.');
+ if (s2.startsWith("."))
+ return s2.substring(1);
+ return s2;
+ }
+}
diff --git a/dx/src/junit/runner/FailureDetailView.java b/dx/src/junit/runner/FailureDetailView.java
new file mode 100644
index 0000000..762326b
--- /dev/null
+++ b/dx/src/junit/runner/FailureDetailView.java
@@ -0,0 +1,23 @@
+package junit.runner;
+
+import java.awt.Component;
+
+import junit.framework.*;
+
+/**
+ * A view to show a details about a failure
+ */
+public interface FailureDetailView {
+ /**
+ * Returns the component used to present the TraceView
+ */
+ public Component getComponent();
+ /**
+ * Shows details of a TestFailure
+ */
+ public void showFailure(TestFailure failure);
+ /**
+ * Clears the view
+ */
+ public void clear();
+}
diff --git a/dx/src/junit/runner/LoadingTestCollector.java b/dx/src/junit/runner/LoadingTestCollector.java
new file mode 100644
index 0000000..b138359
--- /dev/null
+++ b/dx/src/junit/runner/LoadingTestCollector.java
@@ -0,0 +1,69 @@
+package junit.runner;
+
+import java.lang.reflect.*;
+import junit.runner.*;
+import junit.framework.*;
+
+/**
+ * An implementation of a TestCollector that loads
+ * all classes on the class path and tests whether
+ * it is assignable from Test or provides a static suite method.
+ * @see TestCollector
+ */
+public class LoadingTestCollector extends ClassPathTestCollector {
+
+ TestCaseClassLoader fLoader;
+
+ public LoadingTestCollector() {
+ fLoader= new TestCaseClassLoader();
+ }
+
+ protected boolean isTestClass(String classFileName) {
+ try {
+ if (classFileName.endsWith(".class")) {
+ Class testClass= classFromFile(classFileName);
+ return (testClass != null) && isTestClass(testClass);
+ }
+ }
+ catch (ClassNotFoundException expected) {
+ }
+ catch (NoClassDefFoundError notFatal) {
+ }
+ return false;
+ }
+
+ Class classFromFile(String classFileName) throws ClassNotFoundException {
+ String className= classNameFromFile(classFileName);
+ if (!fLoader.isExcluded(className))
+ return fLoader.loadClass(className, false);
+ return null;
+ }
+
+ boolean isTestClass(Class testClass) {
+ if (hasSuiteMethod(testClass))
+ return true;
+ if (Test.class.isAssignableFrom(testClass) &&
+ Modifier.isPublic(testClass.getModifiers()) &&
+ hasPublicConstructor(testClass))
+ return true;
+ return false;
+ }
+
+ boolean hasSuiteMethod(Class testClass) {
+ try {
+ testClass.getMethod(BaseTestRunner.SUITE_METHODNAME, new Class[0]);
+ } catch(Exception e) {
+ return false;
+ }
+ return true;
+ }
+
+ boolean hasPublicConstructor(Class testClass) {
+ try {
+ TestSuite.getTestConstructor(testClass);
+ } catch(NoSuchMethodException e) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/dx/src/junit/runner/ReloadingTestSuiteLoader.java b/dx/src/junit/runner/ReloadingTestSuiteLoader.java
new file mode 100644
index 0000000..f7ef919
--- /dev/null
+++ b/dx/src/junit/runner/ReloadingTestSuiteLoader.java
@@ -0,0 +1,19 @@
+package junit.runner;
+
+/**
+ * A TestSuite loader that can reload classes.
+ */
+public class ReloadingTestSuiteLoader implements TestSuiteLoader {
+
+ public Class load(String suiteClassName) throws ClassNotFoundException {
+ return createLoader().loadClass(suiteClassName, true);
+ }
+
+ public Class reload(Class aClass) throws ClassNotFoundException {
+ return createLoader().loadClass(aClass.getName(), true);
+ }
+
+ protected TestCaseClassLoader createLoader() {
+ return new TestCaseClassLoader();
+ }
+}
diff --git a/dx/src/junit/runner/SimpleTestCollector.java b/dx/src/junit/runner/SimpleTestCollector.java
new file mode 100644
index 0000000..9d1956a
--- /dev/null
+++ b/dx/src/junit/runner/SimpleTestCollector.java
@@ -0,0 +1,20 @@
+package junit.runner;
+
+/**
+ * An implementation of a TestCollector that considers
+ * a class to be a test class when it contains the
+ * pattern "Test" in its name
+ * @see TestCollector
+ */
+public class SimpleTestCollector extends ClassPathTestCollector {
+
+ public SimpleTestCollector() {
+ }
+
+ protected boolean isTestClass(String classFileName) {
+ return
+ classFileName.endsWith(".class") &&
+ classFileName.indexOf('$') < 0 &&
+ classFileName.indexOf("Test") > 0;
+ }
+}
diff --git a/dx/src/junit/runner/Sorter.java b/dx/src/junit/runner/Sorter.java
new file mode 100644
index 0000000..e614ab4
--- /dev/null
+++ b/dx/src/junit/runner/Sorter.java
@@ -0,0 +1,38 @@
+package junit.runner;
+
+import java.util.*;
+
+import junit.runner.*;
+
+/**
+ * A custom quick sort with support to customize the swap behaviour.
+ * NOTICE: We can't use the the sorting support from the JDK 1.2 collection
+ * classes because of the JDK 1.1.7 compatibility.
+ */
+public class Sorter {
+ public static interface Swapper {
+ public void swap(Vector values, int left, int right);
+ }
+
+ public static void sortStrings(Vector values , int left, int right, Swapper swapper) {
+ int oleft= left;
+ int oright= right;
+ String mid= (String)values.elementAt((left + right) / 2);
+ do {
+ while (((String)(values.elementAt(left))).compareTo(mid) < 0)
+ left++;
+ while (mid.compareTo((String)(values.elementAt(right))) < 0)
+ right--;
+ if (left <= right) {
+ swapper.swap(values, left, right);
+ left++;
+ right--;
+ }
+ } while (left <= right);
+
+ if (oleft < right)
+ sortStrings(values, oleft, right, swapper);
+ if (left < oright)
+ sortStrings(values, left, oright, swapper);
+ }
+}
diff --git a/dx/src/junit/runner/StandardTestSuiteLoader.java b/dx/src/junit/runner/StandardTestSuiteLoader.java
new file mode 100644
index 0000000..e2bd765
--- /dev/null
+++ b/dx/src/junit/runner/StandardTestSuiteLoader.java
@@ -0,0 +1,19 @@
+package junit.runner;
+
+/**
+ * The standard test suite loader. It can only load the same class once.
+ */
+public class StandardTestSuiteLoader implements TestSuiteLoader {
+ /**
+ * Uses the system class loader to load the test class
+ */
+ public Class load(String suiteClassName) throws ClassNotFoundException {
+ return Class.forName(suiteClassName);
+ }
+ /**
+ * Uses the system class loader to load the test class
+ */
+ public Class reload(Class aClass) throws ClassNotFoundException {
+ return aClass;
+ }
+}
diff --git a/dx/src/junit/runner/TestCaseClassLoader.java b/dx/src/junit/runner/TestCaseClassLoader.java
new file mode 100644
index 0000000..543641a
--- /dev/null
+++ b/dx/src/junit/runner/TestCaseClassLoader.java
@@ -0,0 +1,226 @@
+package junit.runner;
+
+import java.util.*;
+import java.io.*;
+import java.net.URL;
+import java.util.zip.*;
+
+/**
+ * A custom class loader which enables the reloading
+ * of classes for each test run. The class loader
+ * can be configured with a list of package paths that
+ * should be excluded from loading. The loading
+ * of these packages is delegated to the system class
+ * loader. They will be shared across test runs.
+ * <p>
+ * The list of excluded package paths is specified in
+ * a properties file "excluded.properties" that is located in
+ * the same place as the TestCaseClassLoader class.
+ * <p>
+ * <b>Known limitation:</b> the TestCaseClassLoader cannot load classes
+ * from jar files.
+ */
+
+
+public class TestCaseClassLoader extends ClassLoader {
+ /** scanned class path */
+ private Vector<String> fPathItems;
+ /** default excluded paths */
+ private String[] defaultExclusions= {
+ "junit.framework.",
+ "junit.extensions.",
+ "junit.runner."
+ };
+ /** name of excluded properties file */
+ static final String EXCLUDED_FILE= "excluded.properties";
+ /** excluded paths */
+ private Vector<String> fExcluded;
+
+ /**
+ * Constructs a TestCaseLoader. It scans the class path
+ * and the excluded package paths
+ */
+ public TestCaseClassLoader() {
+ this(System.getProperty("java.class.path"));
+ }
+
+ /**
+ * Constructs a TestCaseLoader. It scans the class path
+ * and the excluded package paths
+ */
+ public TestCaseClassLoader(String classPath) {
+ scanPath(classPath);
+ readExcludedPackages();
+ }
+
+ private void scanPath(String classPath) {
+ String separator= System.getProperty("path.separator");
+ fPathItems= new Vector<String>(10);
+ StringTokenizer st= new StringTokenizer(classPath, separator);
+ while (st.hasMoreTokens()) {
+ fPathItems.addElement(st.nextToken());
+ }
+ }
+
+ public URL getResource(String name) {
+ return ClassLoader.getSystemResource(name);
+ }
+
+ public InputStream getResourceAsStream(String name) {
+ return ClassLoader.getSystemResourceAsStream(name);
+ }
+
+ public boolean isExcluded(String name) {
+ for (int i= 0; i < fExcluded.size(); i++) {
+ if (name.startsWith((String) fExcluded.elementAt(i))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public synchronized Class loadClass(String name, boolean resolve)
+ throws ClassNotFoundException {
+
+ Class c= findLoadedClass(name);
+ if (c != null)
+ return c;
+ //
+ // Delegate the loading of excluded classes to the
+ // standard class loader.
+ //
+ if (isExcluded(name)) {
+ try {
+ c= findSystemClass(name);
+ return c;
+ } catch (ClassNotFoundException e) {
+ // keep searching
+ }
+ }
+ if (c == null) {
+ byte[] data= lookupClassData(name);
+ if (data == null)
+ throw new ClassNotFoundException();
+ c= defineClass(name, data, 0, data.length);
+ }
+ if (resolve)
+ resolveClass(c);
+ return c;
+ }
+
+ private byte[] lookupClassData(String className) throws ClassNotFoundException {
+ byte[] data= null;
+ for (int i= 0; i < fPathItems.size(); i++) {
+ String path= (String) fPathItems.elementAt(i);
+ String fileName= className.replace('.', '/')+".class";
+ if (isJar(path)) {
+ data= loadJarData(path, fileName);
+ } else {
+ data= loadFileData(path, fileName);
+ }
+ if (data != null)
+ return data;
+ }
+ throw new ClassNotFoundException(className);
+ }
+
+ boolean isJar(String pathEntry) {
+ return pathEntry.endsWith(".jar") ||
+ pathEntry.endsWith(".zip") ||
+ pathEntry.endsWith(".apk");
+ }
+
+ private byte[] loadFileData(String path, String fileName) {
+ File file= new File(path, fileName);
+ if (file.exists()) {
+ return getClassData(file);
+ }
+ return null;
+ }
+
+ private byte[] getClassData(File f) {
+ try {
+ FileInputStream stream= new FileInputStream(f);
+ ByteArrayOutputStream out= new ByteArrayOutputStream(1000);
+ byte[] b= new byte[1000];
+ int n;
+ while ((n= stream.read(b)) != -1)
+ out.write(b, 0, n);
+ stream.close();
+ out.close();
+ return out.toByteArray();
+
+ } catch (IOException e) {
+ }
+ return null;
+ }
+
+ private byte[] loadJarData(String path, String fileName) {
+ ZipFile zipFile= null;
+ InputStream stream= null;
+ File archive= new File(path);
+ if (!archive.exists())
+ return null;
+ try {
+ zipFile= new ZipFile(archive);
+ } catch(IOException io) {
+ return null;
+ }
+ ZipEntry entry= zipFile.getEntry(fileName);
+ if (entry == null)
+ return null;
+ int size= (int) entry.getSize();
+ try {
+ stream= zipFile.getInputStream(entry);
+ byte[] data= new byte[size];
+ int pos= 0;
+ while (pos < size) {
+ int n= stream.read(data, pos, data.length - pos);
+ pos += n;
+ }
+ zipFile.close();
+ return data;
+ } catch (IOException e) {
+ } finally {
+ try {
+ if (stream != null)
+ stream.close();
+ } catch (IOException e) {
+ }
+ }
+ return null;
+ }
+
+ private void readExcludedPackages() {
+ fExcluded= new Vector<String>(10);
+ for (int i= 0; i < defaultExclusions.length; i++)
+ fExcluded.addElement(defaultExclusions[i]);
+
+ InputStream is= getClass().getResourceAsStream(EXCLUDED_FILE);
+ if (is == null)
+ return;
+ Properties p= new Properties();
+ try {
+ p.load(is);
+ }
+ catch (IOException e) {
+ return;
+ } finally {
+ try {
+ is.close();
+ } catch (IOException e) {
+ }
+ }
+ for (Enumeration e= p.propertyNames(); e.hasMoreElements(); ) {
+ String key= (String)e.nextElement();
+ if (key.startsWith("excluded.")) {
+ String path= p.getProperty(key);
+ path= path.trim();
+ if (path.endsWith("*"))
+ path= path.substring(0, path.length()-1);
+ if (path.length() > 0)
+ fExcluded.addElement(path);
+ }
+ }
+ }
+}
diff --git a/dx/src/junit/runner/TestCollector.java b/dx/src/junit/runner/TestCollector.java
new file mode 100644
index 0000000..73efb4e
--- /dev/null
+++ b/dx/src/junit/runner/TestCollector.java
@@ -0,0 +1,16 @@
+package junit.runner;
+
+import java.util.*;
+
+
+/**
+ * Collects Test class names to be presented
+ * by the TestSelector.
+ * @see TestSelector
+ */
+public interface TestCollector {
+ /**
+ * Returns an enumeration of Strings with qualified class names
+ */
+ public Enumeration collectTests();
+}
diff --git a/dx/src/junit/runner/TestRunListener.java b/dx/src/junit/runner/TestRunListener.java
new file mode 100644
index 0000000..b11ef07
--- /dev/null
+++ b/dx/src/junit/runner/TestRunListener.java
@@ -0,0 +1,19 @@
+package junit.runner;
+/**
+ * A listener interface for observing the
+ * execution of a test run. Unlike TestListener,
+ * this interface using only primitive objects,
+ * making it suitable for remote test execution.
+ */
+ public interface TestRunListener {
+ /* test status constants*/
+ public static final int STATUS_ERROR= 1;
+ public static final int STATUS_FAILURE= 2;
+
+ public void testRunStarted(String testSuiteName, int testCount);
+ public void testRunEnded(long elapsedTime);
+ public void testRunStopped(long elapsedTime);
+ public void testStarted(String testName);
+ public void testEnded(String testName);
+ public void testFailed(int status, String testName, String trace);
+}
diff --git a/dx/src/junit/runner/TestSuiteLoader.java b/dx/src/junit/runner/TestSuiteLoader.java
new file mode 100644
index 0000000..39a4cf7
--- /dev/null
+++ b/dx/src/junit/runner/TestSuiteLoader.java
@@ -0,0 +1,9 @@
+package junit.runner;
+
+/**
+ * An interface to define how a test suite should be loaded.
+ */
+public interface TestSuiteLoader {
+ abstract public Class load(String suiteClassName) throws ClassNotFoundException;
+ abstract public Class reload(Class aClass) throws ClassNotFoundException;
+}
diff --git a/dx/src/junit/runner/Version.java b/dx/src/junit/runner/Version.java
new file mode 100644
index 0000000..b4541ab
--- /dev/null
+++ b/dx/src/junit/runner/Version.java
@@ -0,0 +1,14 @@
+package junit.runner;
+
+/**
+ * This class defines the current version of JUnit
+ */
+public class Version {
+ private Version() {
+ // don't instantiate
+ }
+
+ public static String id() {
+ return "3.8.1";
+ }
+}
diff --git a/dx/src/junit/runner/excluded.properties b/dx/src/junit/runner/excluded.properties
new file mode 100644
index 0000000..3284628
--- /dev/null
+++ b/dx/src/junit/runner/excluded.properties
@@ -0,0 +1,12 @@
+#
+# The list of excluded package paths for the TestCaseClassLoader
+#
+excluded.0=sun.*
+excluded.1=com.sun.*
+excluded.2=org.omg.*
+excluded.3=javax.*
+excluded.4=sunw.*
+excluded.5=java.*
+excluded.6=org.w3c.dom.*
+excluded.7=org.xml.sax.*
+excluded.8=net.jini.*
diff --git a/dx/src/junit/runner/logo.gif b/dx/src/junit/runner/logo.gif
new file mode 100644
index 0000000..d0e1547
--- /dev/null
+++ b/dx/src/junit/runner/logo.gif
Binary files differ
diff --git a/dx/src/junit/runner/smalllogo.gif b/dx/src/junit/runner/smalllogo.gif
new file mode 100644
index 0000000..7b25eaf
--- /dev/null
+++ b/dx/src/junit/runner/smalllogo.gif
Binary files differ
diff --git a/dx/src/junit/textui/ResultPrinter.java b/dx/src/junit/textui/ResultPrinter.java
new file mode 100644
index 0000000..1ebb7a1
--- /dev/null
+++ b/dx/src/junit/textui/ResultPrinter.java
@@ -0,0 +1,139 @@
+
+package junit.textui;
+
+import java.io.PrintStream;
+import java.text.NumberFormat;
+import java.util.Enumeration;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestFailure;
+import junit.framework.TestListener;
+import junit.framework.TestResult;
+import junit.runner.BaseTestRunner;
+
+public class ResultPrinter implements TestListener {
+ PrintStream fWriter;
+ int fColumn= 0;
+
+ public ResultPrinter(PrintStream writer) {
+ fWriter= writer;
+ }
+
+ /* API for use by textui.TestRunner
+ */
+
+ synchronized void print(TestResult result, long runTime) {
+ printHeader(runTime);
+ printErrors(result);
+ printFailures(result);
+ printFooter(result);
+ }
+
+ void printWaitPrompt() {
+ getWriter().println();
+ getWriter().println("<RETURN> to continue");
+ }
+
+ /* Internal methods
+ */
+
+ protected void printHeader(long runTime) {
+ getWriter().println();
+ getWriter().println("Time: "+elapsedTimeAsString(runTime));
+ }
+
+ protected void printErrors(TestResult result) {
+ printDefects(result.errors(), result.errorCount(), "error");
+ }
+
+ protected void printFailures(TestResult result) {
+ printDefects(result.failures(), result.failureCount(), "failure");
+ }
+
+ protected void printDefects(Enumeration booBoos, int count, String type) {
+ if (count == 0) return;
+ if (count == 1)
+ getWriter().println("There was " + count + " " + type + ":");
+ else
+ getWriter().println("There were " + count + " " + type + "s:");
+ for (int i= 1; booBoos.hasMoreElements(); i++) {
+ printDefect((TestFailure) booBoos.nextElement(), i);
+ }
+ }
+
+ public void printDefect(TestFailure booBoo, int count) { // only public for testing purposes
+ printDefectHeader(booBoo, count);
+ printDefectTrace(booBoo);
+ }
+
+ protected void printDefectHeader(TestFailure booBoo, int count) {
+ // I feel like making this a println, then adding a line giving the throwable a chance to print something
+ // before we get to the stack trace.
+ getWriter().print(count + ") " + booBoo.failedTest());
+ }
+
+ protected void printDefectTrace(TestFailure booBoo) {
+ getWriter().print(BaseTestRunner.getFilteredTrace(booBoo.trace()));
+ }
+
+ protected void printFooter(TestResult result) {
+ if (result.wasSuccessful()) {
+ getWriter().println();
+ getWriter().print("OK");
+ getWriter().println (" (" + result.runCount() + " test" + (result.runCount() == 1 ? "": "s") + ")");
+
+ } else {
+ getWriter().println();
+ getWriter().println("FAILURES!!!");
+ getWriter().println("Tests run: "+result.runCount()+
+ ", Failures: "+result.failureCount()+
+ ", Errors: "+result.errorCount());
+ }
+ getWriter().println();
+ }
+
+
+ /**
+ * Returns the formatted string of the elapsed time.
+ * Duplicated from BaseTestRunner. Fix it.
+ */
+ protected String elapsedTimeAsString(long runTime) {
+ return NumberFormat.getInstance().format((double)runTime/1000);
+ }
+
+ public PrintStream getWriter() {
+ return fWriter;
+ }
+ /**
+ * @see junit.framework.TestListener#addError(Test, Throwable)
+ */
+ public void addError(Test test, Throwable t) {
+ getWriter().print("E");
+ }
+
+ /**
+ * @see junit.framework.TestListener#addFailure(Test, AssertionFailedError)
+ */
+ public void addFailure(Test test, AssertionFailedError t) {
+ getWriter().print("F");
+ }
+
+ /**
+ * @see junit.framework.TestListener#endTest(Test)
+ */
+ public void endTest(Test test) {
+ }
+
+ /**
+ * @see junit.framework.TestListener#startTest(Test)
+ */
+ public void startTest(Test test) {
+ getWriter().print(".");
+ if (fColumn++ >= 40) {
+ getWriter().println();
+ fColumn= 0;
+ }
+ }
+
+}
diff --git a/dx/src/junit/textui/TestRunner.java b/dx/src/junit/textui/TestRunner.java
new file mode 100644
index 0000000..8bdc325
--- /dev/null
+++ b/dx/src/junit/textui/TestRunner.java
@@ -0,0 +1,189 @@
+package junit.textui;
+
+
+import java.io.PrintStream;
+
+import junit.framework.*;
+import junit.runner.*;
+
+/**
+ * A command line based tool to run tests.
+ * <pre>
+ * java junit.textui.TestRunner [-wait] TestCaseClass
+ * </pre>
+ * TestRunner expects the name of a TestCase class as argument.
+ * If this class defines a static <code>suite</code> method it
+ * will be invoked and the returned test is run. Otherwise all
+ * the methods starting with "test" having no arguments are run.
+ * <p>
+ * When the wait command line argument is given TestRunner
+ * waits until the users types RETURN.
+ * <p>
+ * TestRunner prints a trace as the tests are executed followed by a
+ * summary at the end.
+ */
+public class TestRunner extends BaseTestRunner {
+ private ResultPrinter fPrinter;
+
+ public static final int SUCCESS_EXIT= 0;
+ public static final int FAILURE_EXIT= 1;
+ public static final int EXCEPTION_EXIT= 2;
+
+ /**
+ * Constructs a TestRunner.
+ */
+ public TestRunner() {
+ this(System.out);
+ }
+
+ /**
+ * Constructs a TestRunner using the given stream for all the output
+ */
+ public TestRunner(PrintStream writer) {
+ this(new ResultPrinter(writer));
+ }
+
+ /**
+ * Constructs a TestRunner using the given ResultPrinter all the output
+ */
+ public TestRunner(ResultPrinter printer) {
+ fPrinter= printer;
+ }
+
+ /**
+ * Runs a suite extracted from a TestCase subclass.
+ */
+ static public void run(Class testClass) {
+ run(new TestSuite(testClass));
+ }
+
+ /**
+ * Runs a single test and collects its results.
+ * This method can be used to start a test run
+ * from your program.
+ * <pre>
+ * public static void main (String[] args) {
+ * test.textui.TestRunner.run(suite());
+ * }
+ * </pre>
+ */
+ static public TestResult run(Test test) {
+ TestRunner runner= new TestRunner();
+ return runner.doRun(test);
+ }
+
+ /**
+ * Runs a single test and waits until the user
+ * types RETURN.
+ */
+ static public void runAndWait(Test suite) {
+ TestRunner aTestRunner= new TestRunner();
+ aTestRunner.doRun(suite, true);
+ }
+
+ /**
+ * Always use the StandardTestSuiteLoader. Overridden from
+ * BaseTestRunner.
+ */
+ public TestSuiteLoader getLoader() {
+ return new StandardTestSuiteLoader();
+ }
+
+ public void testFailed(int status, Test test, Throwable t) {
+ }
+
+ public void testStarted(String testName) {
+ }
+
+ public void testEnded(String testName) {
+ }
+
+ /**
+ * Creates the TestResult to be used for the test run.
+ */
+ protected TestResult createTestResult() {
+ return new TestResult();
+ }
+
+ public TestResult doRun(Test test) {
+ return doRun(test, false);
+ }
+
+ public TestResult doRun(Test suite, boolean wait) {
+ TestResult result= createTestResult();
+ result.addListener(fPrinter);
+ long startTime= System.currentTimeMillis();
+ suite.run(result);
+ long endTime= System.currentTimeMillis();
+ long runTime= endTime-startTime;
+ fPrinter.print(result, runTime);
+
+ pause(wait);
+ return result;
+ }
+
+ protected void pause(boolean wait) {
+ if (!wait) return;
+ fPrinter.printWaitPrompt();
+ try {
+ System.in.read();
+ }
+ catch(Exception e) {
+ }
+ }
+
+ public static void main(String args[]) {
+ TestRunner aTestRunner= new TestRunner();
+ try {
+ TestResult r= aTestRunner.start(args);
+ if (!r.wasSuccessful())
+ System.exit(FAILURE_EXIT);
+ System.exit(SUCCESS_EXIT);
+ } catch(Exception e) {
+ System.err.println(e.getMessage());
+ System.exit(EXCEPTION_EXIT);
+ }
+ }
+
+ /**
+ * Starts a test run. Analyzes the command line arguments
+ * and runs the given test suite.
+ */
+ protected TestResult start(String args[]) throws Exception {
+ String testCase= "";
+ boolean wait= false;
+
+ for (int i= 0; i < args.length; i++) {
+ if (args[i].equals("-wait"))
+ wait= true;
+ else if (args[i].equals("-c"))
+ testCase= extractClassName(args[++i]);
+ else if (args[i].equals("-v"))
+ System.err.println("JUnit "+Version.id()+" by Kent Beck and Erich Gamma");
+ else
+ testCase= args[i];
+ }
+
+ if (testCase.equals(""))
+ throw new Exception("Usage: TestRunner [-wait] testCaseName, where name is the name of the TestCase class");
+
+ try {
+ Test suite= getTest(testCase);
+ return doRun(suite, wait);
+ }
+ catch(Exception e) {
+ throw new Exception("Could not create and run test suite: "+e);
+ }
+ }
+
+ protected void runFailed(String message) {
+ System.err.println(message);
+ System.exit(FAILURE_EXIT);
+ }
+
+ public void setPrinter(ResultPrinter printer) {
+ fPrinter= printer;
+ }
+
+
+}
diff --git a/dx/tests/001-nop/expected.txt b/dx/tests/001-nop/expected.txt
new file mode 100644
index 0000000..d4a85ce
--- /dev/null
+++ b/dx/tests/001-nop/expected.txt
@@ -0,0 +1 @@
+I am a jelly donut.
diff --git a/dx/tests/001-nop/info.txt b/dx/tests/001-nop/info.txt
new file mode 100644
index 0000000..9942f10
--- /dev/null
+++ b/dx/tests/001-nop/info.txt
@@ -0,0 +1,2 @@
+This is a sample no-op test, which does at least serve to verify that the
+test harness is working.
diff --git a/dx/tests/001-nop/run b/dx/tests/001-nop/run
new file mode 100644
index 0000000..51637c1
--- /dev/null
+++ b/dx/tests/001-nop/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+echo 'I am a jelly donut.'
diff --git a/dx/tests/002-minimal-valid/expected.txt b/dx/tests/002-minimal-valid/expected.txt
new file mode 100644
index 0000000..3877fb5
--- /dev/null
+++ b/dx/tests/002-minimal-valid/expected.txt
@@ -0,0 +1,46 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 000a
+
+constant_pool:
+ 0001: method{java.lang.Object.<init>:()V}
+ 0002: type{Small}
+ 0003: type{java.lang.Object}
+ 0004: utf8{"<init>"}
+ 0005: utf8{"()V"}
+ 0006: utf8{"Code"}
+ 0007: nat{<init>:()V}
+ 0008: utf8{"Small"}
+ 0009: utf8{"java/lang/Object"}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0001
+
+methods[0]:
+ access_flags: public
+ name: <init>
+ descriptor: ()V
+ attributes_count: 0001
+
+ attributes[0]:
+ name: Code
+ length: 00000011
+ max_stack: 0001
+ max_locals: 0001
+ code_length: 00000005
+ 0000: aload_0 // 00
+ 0001: invokespecial method{java.lang.Object.<init>:()V}
+ 0004: return
+ exception_table_length: 0000
+ attributes_count: 0000
+ end attributes[0]
+end methods[0]
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/002-minimal-valid/info.txt b/dx/tests/002-minimal-valid/info.txt
new file mode 100644
index 0000000..f296af8
--- /dev/null
+++ b/dx/tests/002-minimal-valid/info.txt
@@ -0,0 +1 @@
+This is just a dump of a simple but valid class.
diff --git a/dx/tests/002-minimal-valid/run b/dx/tests/002-minimal-valid/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/002-minimal-valid/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/002-minimal-valid/small-class.txt b/dx/tests/002-minimal-valid/small-class.txt
new file mode 100644
index 0000000..25a323f
--- /dev/null
+++ b/dx/tests/002-minimal-valid/small-class.txt
@@ -0,0 +1,49 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe # magic
+0000 # minor_version
+002e # major_version
+000a # constant_pool_count
+
+#
+# constant_pool
+#
+0a 0003 0007 # 0001: method[0003, 0007]
+07 0008 # 0002: class[0008]
+07 0009 # 0003: class[0009]
+01 0006 "<init>" # 0004: utf8["<init>"]
+01 0003 "()V" # 0005: utf8["()V"]
+01 0004 "Code" # 0006: utf8["Code"]
+0c 0004 0005 # 0007: nat[0004, 0005]
+01 0005 "Small" # 0008: utf8["Small"]
+01 0010 "java/lang/Object" # 0009: utf8["java/lang/Object"]
+
+0021 # access_flags
+0002 # this_class
+0003 # super_class
+0000 # interfaces_count
+0000 # fields_count
+0001 # methods_count
+
+#
+# methods[0]
+#
+0001 # access_flags
+0004 # name
+0005 # descriptor
+0001 # attributes_count
+# attributes[0]
+0006 # name
+0000 0011 # length
+0001 # max_stack
+0001 # max_locals
+0000 0005 # code_length
+2a # 0000: aload_0
+b7 0001 # 0001: invokespecial method[java/lang/Object.<init>:()V]
+b1 # 0004: return
+0000 # exception_table_length
+0000 # attributes_count
+
+0000 # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-bad-magic.txt b/dx/tests/003-magic-version-access/class-bad-magic.txt
new file mode 100644
index 0000000..f3c64bd
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-bad-magic.txt
@@ -0,0 +1,25 @@
+#
+# classfile with a bad magic value
+#
+
+dead babe # magic
+0000 # minor_version
+0031 # major_version
+0005 # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small" # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001 # 0003: class[Small]
+07 0002 # 0004: class[java/lang/Object]
+
+ffff # access_flags
+0003 # this_class
+0004 # super_class
+0000 # interfaces_count
+0000 # fields_count
+0000 # methods_count
+
+0000 # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-44.0.txt b/dx/tests/003-magic-version-access/class-version-44.0.txt
new file mode 100644
index 0000000..2d9055c
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-44.0.txt
@@ -0,0 +1,25 @@
+#
+# classfile with an out-of-range version.
+#
+
+cafe babe # magic
+0000 # minor_version
+002c # major_version
+0005 # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small" # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001 # 0003: class[Small]
+07 0002 # 0004: class[java/lang/Object]
+
+ffff # access_flags
+0003 # this_class
+0004 # super_class
+0000 # interfaces_count
+0000 # fields_count
+0000 # methods_count
+
+0000 # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-44.65535.txt b/dx/tests/003-magic-version-access/class-version-44.65535.txt
new file mode 100644
index 0000000..0f2b582
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-44.65535.txt
@@ -0,0 +1,25 @@
+#
+# classfile with an out-of-range version.
+#
+
+cafe babe # magic
+ffff # minor_version
+002c # major_version
+0005 # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small" # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001 # 0003: class[Small]
+07 0002 # 0004: class[java/lang/Object]
+
+ffff # access_flags
+0003 # this_class
+0004 # super_class
+0000 # interfaces_count
+0000 # fields_count
+0000 # methods_count
+
+0000 # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-45.0.txt b/dx/tests/003-magic-version-access/class-version-45.0.txt
new file mode 100644
index 0000000..335079d
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-45.0.txt
@@ -0,0 +1,25 @@
+#
+# classfile with the lowest valid version, 45.0 (0x2d.0x00)
+#
+
+cafe babe # magic
+0000 # minor_version
+002d # major_version
+0005 # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small" # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001 # 0003: class[Small]
+07 0002 # 0004: class[java/lang/Object]
+
+ffff # access_flags
+0003 # this_class
+0004 # super_class
+0000 # interfaces_count
+0000 # fields_count
+0000 # methods_count
+
+0000 # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-45.65535.txt b/dx/tests/003-magic-version-access/class-version-45.65535.txt
new file mode 100644
index 0000000..2b31404
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-45.65535.txt
@@ -0,0 +1,25 @@
+#
+# classfile with the valid version 45.65535 (0x2d.0xffff)
+#
+
+cafe babe # magic
+ffff # minor_version
+002d # major_version
+0005 # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small" # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001 # 0003: class[Small]
+07 0002 # 0004: class[java/lang/Object]
+
+ffff # access_flags
+0003 # this_class
+0004 # super_class
+0000 # interfaces_count
+0000 # fields_count
+0000 # methods_count
+
+0000 # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-48.0.txt b/dx/tests/003-magic-version-access/class-version-48.0.txt
new file mode 100644
index 0000000..551b221
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-48.0.txt
@@ -0,0 +1,25 @@
+#
+# classfile with the valid version 48.0 (0x30.0x00)
+#
+
+cafe babe # magic
+0000 # minor_version
+0030 # major_version
+0005 # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small" # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001 # 0003: class[Small]
+07 0002 # 0004: class[java/lang/Object]
+
+ffff # access_flags
+0003 # this_class
+0004 # super_class
+0000 # interfaces_count
+0000 # fields_count
+0000 # methods_count
+
+0000 # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-48.65535.txt b/dx/tests/003-magic-version-access/class-version-48.65535.txt
new file mode 100644
index 0000000..ac95b52
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-48.65535.txt
@@ -0,0 +1,25 @@
+#
+# classfile with the valid version 48.65535 (0x30.0xffff)
+#
+
+cafe babe # magic
+ffff # minor_version
+0030 # major_version
+0005 # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small" # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001 # 0003: class[Small]
+07 0002 # 0004: class[java/lang/Object]
+
+ffff # access_flags
+0003 # this_class
+0004 # super_class
+0000 # interfaces_count
+0000 # fields_count
+0000 # methods_count
+
+0000 # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-49.0.txt b/dx/tests/003-magic-version-access/class-version-49.0.txt
new file mode 100644
index 0000000..0b30fcd
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-49.0.txt
@@ -0,0 +1,25 @@
+#
+# classfile with the highest valid version, 49.0 (0x31.0x00)
+#
+
+cafe babe # magic
+0000 # minor_version
+0031 # major_version
+0005 # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small" # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001 # 0003: class[Small]
+07 0002 # 0004: class[java/lang/Object]
+
+ffff # access_flags
+0003 # this_class
+0004 # super_class
+0000 # interfaces_count
+0000 # fields_count
+0000 # methods_count
+
+0000 # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-49.1.txt b/dx/tests/003-magic-version-access/class-version-49.1.txt
new file mode 100644
index 0000000..9eb477c
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-49.1.txt
@@ -0,0 +1,26 @@
+#
+# classfile with a minor version 1 higher than the highest valid
+# version. 49.1 (0x31.0x01)
+#
+
+cafe babe # magic
+0001 # minor_version
+0031 # major_version
+0005 # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small" # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001 # 0003: class[Small]
+07 0002 # 0004: class[java/lang/Object]
+
+ffff # access_flags
+0003 # this_class
+0004 # super_class
+0000 # interfaces_count
+0000 # fields_count
+0000 # methods_count
+
+0000 # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-49.65535.txt b/dx/tests/003-magic-version-access/class-version-49.65535.txt
new file mode 100644
index 0000000..668631b
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-49.65535.txt
@@ -0,0 +1,26 @@
+#
+# classfile with an invalid version, with the same major version
+# as the highest valid version. 49.65535 (0x31.0xffff)
+#
+
+cafe babe # magic
+ffff # minor_version
+0031 # major_version
+0005 # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small" # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001 # 0003: class[Small]
+07 0002 # 0004: class[java/lang/Object]
+
+ffff # access_flags
+0003 # this_class
+0004 # super_class
+0000 # interfaces_count
+0000 # fields_count
+0000 # methods_count
+
+0000 # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-50.0.txt b/dx/tests/003-magic-version-access/class-version-50.0.txt
new file mode 100644
index 0000000..fa67077
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-50.0.txt
@@ -0,0 +1,26 @@
+#
+# classfile with an invalid version, with a higher major version
+# than the highest valid version. 50.0 (0x32.0x00)
+#
+
+cafe babe # magic
+0000 # minor_version
+0032 # major_version
+0005 # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small" # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001 # 0003: class[Small]
+07 0002 # 0004: class[java/lang/Object]
+
+ffff # access_flags
+0003 # this_class
+0004 # super_class
+0000 # interfaces_count
+0000 # fields_count
+0000 # methods_count
+
+0000 # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-50.1.txt b/dx/tests/003-magic-version-access/class-version-50.1.txt
new file mode 100644
index 0000000..9543be1
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-50.1.txt
@@ -0,0 +1,26 @@
+#
+# classfile with an invalid version, with a higher major version
+# than the highest valid version. 50.0 (0x32.0x00)
+#
+
+cafe babe # magic
+0001 # minor_version
+0032 # major_version
+0005 # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small" # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001 # 0003: class[Small]
+07 0002 # 0004: class[java/lang/Object]
+
+ffff # access_flags
+0003 # this_class
+0004 # super_class
+0000 # interfaces_count
+0000 # fields_count
+0000 # methods_count
+
+0000 # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-50.65535.txt b/dx/tests/003-magic-version-access/class-version-50.65535.txt
new file mode 100644
index 0000000..9db1958
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-50.65535.txt
@@ -0,0 +1,26 @@
+#
+# classfile with an invalid version, with a higher major version
+# than the highest valid version. 50.0 (0x32.0x00)
+#
+
+cafe babe # magic
+ffff # minor_version
+0032 # major_version
+0005 # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small" # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001 # 0003: class[Small]
+07 0002 # 0004: class[java/lang/Object]
+
+ffff # access_flags
+0003 # this_class
+0004 # super_class
+0000 # interfaces_count
+0000 # fields_count
+0000 # methods_count
+
+0000 # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-51.0.txt b/dx/tests/003-magic-version-access/class-version-51.0.txt
new file mode 100644
index 0000000..2ffb4cd
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-51.0.txt
@@ -0,0 +1,26 @@
+#
+# classfile with an invalid version, with a higher major version
+# than the highest valid version. 50.0 (0x32.0x00)
+#
+
+cafe babe # magic
+0000 # minor_version
+0033 # major_version
+0005 # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small" # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001 # 0003: class[Small]
+07 0002 # 0004: class[java/lang/Object]
+
+ffff # access_flags
+0003 # this_class
+0004 # super_class
+0000 # interfaces_count
+0000 # fields_count
+0000 # methods_count
+
+0000 # attributes_count
diff --git a/dx/tests/003-magic-version-access/expected.txt b/dx/tests/003-magic-version-access/expected.txt
new file mode 100644
index 0000000..a632922
--- /dev/null
+++ b/dx/tests/003-magic-version-access/expected.txt
@@ -0,0 +1,243 @@
+reading class-bad-magic.txt...
+begin classfile
+magic: deadbabe
+minor_version: 0000
+major_version: 0031
+
+trouble parsing:
+bad class file magic (deadbabe) or version (0031.0000)
+...while parsing class-bad-magic.txt
+reading class-version-44.0.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002c
+
+trouble parsing:
+bad class file magic (cafebabe) or version (002c.0000)
+...while parsing class-version-44.0.txt
+reading class-version-44.65535.txt...
+begin classfile
+magic: cafebabe
+minor_version: ffff
+major_version: 002c
+
+trouble parsing:
+bad class file magic (cafebabe) or version (002c.ffff)
+...while parsing class-version-44.65535.txt
+reading class-version-45.0.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002d
+constant_pool_count: 0005
+
+constant_pool:
+ 0001: utf8{"Small"}
+ 0002: utf8{"java/lang/Object"}
+ 0003: type{Small}
+ 0004: type{java.lang.Object}
+end constant_pool
+access_flags: public|final|super|interface|abstract|synthetic|annotation|enum|89ce
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
+reading class-version-45.65535.txt...
+begin classfile
+magic: cafebabe
+minor_version: ffff
+major_version: 002d
+constant_pool_count: 0005
+
+constant_pool:
+ 0001: utf8{"Small"}
+ 0002: utf8{"java/lang/Object"}
+ 0003: type{Small}
+ 0004: type{java.lang.Object}
+end constant_pool
+access_flags: public|final|super|interface|abstract|synthetic|annotation|enum|89ce
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
+reading class-version-48.0.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 0030
+constant_pool_count: 0005
+
+constant_pool:
+ 0001: utf8{"Small"}
+ 0002: utf8{"java/lang/Object"}
+ 0003: type{Small}
+ 0004: type{java.lang.Object}
+end constant_pool
+access_flags: public|final|super|interface|abstract|synthetic|annotation|enum|89ce
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
+reading class-version-48.65535.txt...
+begin classfile
+magic: cafebabe
+minor_version: ffff
+major_version: 0030
+constant_pool_count: 0005
+
+constant_pool:
+ 0001: utf8{"Small"}
+ 0002: utf8{"java/lang/Object"}
+ 0003: type{Small}
+ 0004: type{java.lang.Object}
+end constant_pool
+access_flags: public|final|super|interface|abstract|synthetic|annotation|enum|89ce
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
+reading class-version-49.0.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 0031
+constant_pool_count: 0005
+
+constant_pool:
+ 0001: utf8{"Small"}
+ 0002: utf8{"java/lang/Object"}
+ 0003: type{Small}
+ 0004: type{java.lang.Object}
+end constant_pool
+access_flags: public|final|super|interface|abstract|synthetic|annotation|enum|89ce
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
+reading class-version-49.1.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0001
+major_version: 0031
+constant_pool_count: 0005
+
+constant_pool:
+ 0001: utf8{"Small"}
+ 0002: utf8{"java/lang/Object"}
+ 0003: type{Small}
+ 0004: type{java.lang.Object}
+end constant_pool
+access_flags: public|final|super|interface|abstract|synthetic|annotation|enum|89ce
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
+reading class-version-49.65535.txt...
+begin classfile
+magic: cafebabe
+minor_version: ffff
+major_version: 0031
+constant_pool_count: 0005
+
+constant_pool:
+ 0001: utf8{"Small"}
+ 0002: utf8{"java/lang/Object"}
+ 0003: type{Small}
+ 0004: type{java.lang.Object}
+end constant_pool
+access_flags: public|final|super|interface|abstract|synthetic|annotation|enum|89ce
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
+reading class-version-50.0.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 0032
+constant_pool_count: 0005
+
+constant_pool:
+ 0001: utf8{"Small"}
+ 0002: utf8{"java/lang/Object"}
+ 0003: type{Small}
+ 0004: type{java.lang.Object}
+end constant_pool
+access_flags: public|final|super|interface|abstract|synthetic|annotation|enum|89ce
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
+reading class-version-50.1.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0001
+major_version: 0032
+
+trouble parsing:
+bad class file magic (cafebabe) or version (0032.0001)
+...while parsing class-version-50.1.txt
+reading class-version-50.65535.txt...
+begin classfile
+magic: cafebabe
+minor_version: ffff
+major_version: 0032
+
+trouble parsing:
+bad class file magic (cafebabe) or version (0032.ffff)
+...while parsing class-version-50.65535.txt
+reading class-version-51.0.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 0033
+
+trouble parsing:
+bad class file magic (cafebabe) or version (0033.0000)
+...while parsing class-version-51.0.txt
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 0031
+constant_pool_count: 0005
+
+constant_pool:
+ 0001: utf8{"Small"}
+ 0002: utf8{"java/lang/Object"}
+ 0003: type{Small}
+ 0004: type{java.lang.Object}
+end constant_pool
+access_flags: public|final|super|interface|abstract|synthetic|annotation|enum|89ce
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/003-magic-version-access/info.txt b/dx/tests/003-magic-version-access/info.txt
new file mode 100644
index 0000000..4d6a697
--- /dev/null
+++ b/dx/tests/003-magic-version-access/info.txt
@@ -0,0 +1,9 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bits of parsing tested here are:
+
+* magic number
+* major / minor version numbers
+* class access_flags
diff --git a/dx/tests/003-magic-version-access/run b/dx/tests/003-magic-version-access/run
new file mode 100644
index 0000000..24de48e
--- /dev/null
+++ b/dx/tests/003-magic-version-access/run
@@ -0,0 +1,46 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# The tests that don't specify "--debug" are expected to
+# throw exceptions. If --debug is on we'll get a stack
+# trace, which is unpredictable and doesn't work well with
+# expected.txt vs. out.txt comparisons.
+
+# Bad magic (throws an expection)
+dx --dump --strict class-bad-magic.txt
+
+# Too small (throws an exception)
+dx --dump --strict class-version-44.0.txt
+dx --dump --strict class-version-44.65535.txt
+
+# Just right
+dx --debug --dump --width=100 class-version-45.0.txt
+dx --debug --dump --width=100 class-version-45.65535.txt
+dx --debug --dump --width=100 class-version-48.0.txt
+dx --debug --dump --width=100 class-version-48.65535.txt
+dx --debug --dump --width=100 class-version-49.0.txt
+dx --debug --dump --width=100 class-version-49.1.txt
+dx --debug --dump --width=100 class-version-49.65535.txt
+dx --debug --dump --width=100 class-version-50.0.txt
+
+# Too big (throws an exception)
+dx --dump --strict class-version-50.1.txt
+dx --dump --strict class-version-50.65535.txt
+dx --dump --strict class-version-51.0.txt
+
+# Show that we can dump the access flags even when they
+# don't make any sense.
+dx --debug --dump --width=100 small-class.txt
diff --git a/dx/tests/003-magic-version-access/small-class.txt b/dx/tests/003-magic-version-access/small-class.txt
new file mode 100644
index 0000000..3eb7402
--- /dev/null
+++ b/dx/tests/003-magic-version-access/small-class.txt
@@ -0,0 +1,25 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe # magic
+0000 # minor_version
+0031 # major_version
+0005 # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small" # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001 # 0003: class[Small]
+07 0002 # 0004: class[java/lang/Object]
+
+ffff # access_flags
+0003 # this_class
+0004 # super_class
+0000 # interfaces_count
+0000 # fields_count
+0000 # methods_count
+
+0000 # attributes_count
diff --git a/dx/tests/004-cp-bottom-up/expected.txt b/dx/tests/004-cp-bottom-up/expected.txt
new file mode 100644
index 0000000..4edbed5
--- /dev/null
+++ b/dx/tests/004-cp-bottom-up/expected.txt
@@ -0,0 +1,34 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0014
+
+constant_pool:
+ 0001: utf8{"Small"}
+ 0002: utf8{"java/lang/Object"}
+ 0003: type{Small}
+ 0004: type{java.lang.Object}
+ 0005: utf8{"blort"}
+ 0006: utf8{"x/y/Zzz"}
+ 0007: utf8{"()V"}
+ 0008: nat{blort:x/y/Zzz}
+ 0009: nat{blort:()V}
+ 000a: field{Small.blort:x/y/Zzz}
+ 000b: method{Small.blort:()V}
+ 000c: ifaceMethod{Small.blort:()V}
+ 000d: string{"Small"}
+ 000e: int{0x12345678 / 305419896}
+ 000f: float{0x42f6e666 / 123.45}
+ 0010: long{0x123456789abcdef0 / 1311768467463790320}
+ 0012: double{0x411958955f8a0903 / 415269.3433}
+end constant_pool
+access_flags: public
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/004-cp-bottom-up/info.txt b/dx/tests/004-cp-bottom-up/info.txt
new file mode 100644
index 0000000..f78a626
--- /dev/null
+++ b/dx/tests/004-cp-bottom-up/info.txt
@@ -0,0 +1,8 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the constant pool contains
+at least one valid entry of each possible constant pool type, and that
+entries that are referred to by other entries always occur before the
+referring entries.
diff --git a/dx/tests/004-cp-bottom-up/run b/dx/tests/004-cp-bottom-up/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/004-cp-bottom-up/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/004-cp-bottom-up/small-class.txt b/dx/tests/004-cp-bottom-up/small-class.txt
new file mode 100644
index 0000000..8a68cbf
--- /dev/null
+++ b/dx/tests/004-cp-bottom-up/small-class.txt
@@ -0,0 +1,38 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe # magic
+0000 # minor_version
+002e # major_version
+0014 # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small" # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001 # 0003: class[Small]
+07 0002 # 0004: class[java/lang/Object]
+01 0005 "blort" # 0005: utf8["blort"]
+01 0007 "x/y/Zzz" # 0006: utf8["x/y/Zzz"]
+01 0003 "()V" # 0007: utf8["()V"]
+0c 0005 0006 # 0008: nat[blort:x/y/Zzz]
+0c 0005 0007 # 0009: nat[blort:()V]
+09 0003 0008 # 000a: field[Small.blort:x/y/Zzz]
+0a 0003 0009 # 000b: method[Small.blort:()V]
+0b 0003 0009 # 000c: ifaceMethod[Small.blort:()V]
+08 0001 # 000d: string["Small"]
+03 12345678 # 000e: integer[0x12345678]
+04 42f6e666 # 000f: float[123.45]
+05 12345678 9abcdef0 # 0010: long[0x1234567890abcdef0]
+06 41195895 5f8a0903 # 0012: double[415269.3433]
+
+0001 # access_flags
+0003 # this_class
+0004 # super_class
+0000 # interfaces_count
+0000 # fields_count
+0000 # methods_count
+
+0000 # attributes_count
diff --git a/dx/tests/005-cp-top-down/expected.txt b/dx/tests/005-cp-top-down/expected.txt
new file mode 100644
index 0000000..791b9da
--- /dev/null
+++ b/dx/tests/005-cp-top-down/expected.txt
@@ -0,0 +1,34 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0014
+
+constant_pool:
+ 0001: double{0x411958955f8a0903 / 415269.3433}
+ 0003: long{0x123456789abcdef0 / 1311768467463790320}
+ 0005: float{0x42f6e666 / 123.45}
+ 0006: int{0x12345678 / 305419896}
+ 0007: string{"Small"}
+ 0008: ifaceMethod{Small.blort:()V}
+ 0009: method{Small.blort:()V}
+ 000a: field{Small.blort:x/y/Zzz}
+ 000b: nat{blort:()V}
+ 000c: nat{blort:x/y/Zzz}
+ 000d: utf8{"()V"}
+ 000e: utf8{"x/y/Zzz"}
+ 000f: utf8{"blort"}
+ 0010: type{java.lang.Object}
+ 0011: type{Small}
+ 0012: utf8{"java/lang/Object"}
+ 0013: utf8{"Small"}
+end constant_pool
+access_flags: public
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/005-cp-top-down/info.txt b/dx/tests/005-cp-top-down/info.txt
new file mode 100644
index 0000000..5842fb3
--- /dev/null
+++ b/dx/tests/005-cp-top-down/info.txt
@@ -0,0 +1,8 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the constant pool contains
+at least one valid entry of each possible constant pool type, and that
+entries that are referred to by other entries always occur after the
+referring entries.
diff --git a/dx/tests/005-cp-top-down/run b/dx/tests/005-cp-top-down/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/005-cp-top-down/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/005-cp-top-down/small-class.txt b/dx/tests/005-cp-top-down/small-class.txt
new file mode 100644
index 0000000..a681f65
--- /dev/null
+++ b/dx/tests/005-cp-top-down/small-class.txt
@@ -0,0 +1,38 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe # magic
+0000 # minor_version
+002e # major_version
+0014 # constant_pool_count
+
+#
+# constant_pool
+#
+06 41195895 5f8a0903 # 0001: double[415269.3433]
+05 12345678 9abcdef0 # 0003: long[0x1234567890abcdef0]
+04 42f6e666 # 0005: float[123.45]
+03 12345678 # 0006: integer[0x12345678]
+08 0013 # 0007: string["Small"]
+0b 0011 000b # 0008: ifaceMethod[Small.blort:()V]
+0a 0011 000b # 0009: method[Small.blort:()V]
+09 0011 000c # 000a: field[Small.blort:x/y/Zzz]
+0c 000f 000d # 000b: nat[blort:()V]
+0c 000f 000e # 000c: nat[blort:x/y/Zzz]
+01 0003 "()V" # 000d: utf8["()V"]
+01 0007 "x/y/Zzz" # 000e: utf8["x/y/Zzz"]
+01 0005 "blort" # 000f: utf8["blort"]
+07 0012 # 0010: class[java/lang/Object]
+07 0013 # 0011: class[Small]
+01 0010 "java/lang/Object" # 0012: utf8["java/lang/Object"]
+01 0005 "Small" # 0013: utf8["Small"]
+
+0001 # access_flags
+0011 # this_class
+0010 # super_class
+0000 # interfaces_count
+0000 # fields_count
+0000 # methods_count
+
+0000 # attributes_count
diff --git a/dx/tests/006-interfaces/expected.txt b/dx/tests/006-interfaces/expected.txt
new file mode 100644
index 0000000..09e066b
--- /dev/null
+++ b/dx/tests/006-interfaces/expected.txt
@@ -0,0 +1,31 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 000b
+
+constant_pool:
+ 0001: utf8{"java/lang/Object"}
+ 0002: utf8{"Small"}
+ 0003: utf8{"Foo"}
+ 0004: utf8{"Bar"}
+ 0005: utf8{"Baz"}
+ 0006: type{java.lang.Object}
+ 0007: type{Small}
+ 0008: type{Foo}
+ 0009: type{Bar}
+ 000a: type{Baz}
+end constant_pool
+access_flags: public
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0003
+interfaces:
+ type{Foo}
+ type{Bar}
+ type{Baz}
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/006-interfaces/info.txt b/dx/tests/006-interfaces/info.txt
new file mode 100644
index 0000000..0959482
--- /dev/null
+++ b/dx/tests/006-interfaces/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a non-empty
+interfaces list.
diff --git a/dx/tests/006-interfaces/run b/dx/tests/006-interfaces/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/006-interfaces/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/006-interfaces/small-class.txt b/dx/tests/006-interfaces/small-class.txt
new file mode 100644
index 0000000..ea24923
--- /dev/null
+++ b/dx/tests/006-interfaces/small-class.txt
@@ -0,0 +1,33 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe # magic
+0000 # minor_version
+002e # major_version
+000b # constant_pool_count
+
+#
+# constant_pool
+#
+01 0010 "java/lang/Object" # 0001: utf8["java/lang/Object"]
+01 0005 "Small" # 0002: utf8["Small"]
+01 0003 "Foo" # 0003: utf8["Foo"]
+01 0003 "Bar" # 0004: utf8["Bar"]
+01 0003 "Baz" # 0005: utf8["Baz"]
+07 0001 # 0006: class[java/lang/Object]
+07 0002 # 0007: class[Small]
+07 0003 # 0008: class[Foo]
+07 0004 # 0009: class[Bar]
+07 0005 # 000a: class[Baz]
+
+0001 # access_flags
+0007 # this_class
+0006 # super_class
+0003 # interfaces_count
+0008 0009 000a # interfaces
+
+0000 # fields_count
+0000 # methods_count
+
+0000 # attributes_count
diff --git a/dx/tests/007-no-superclass/expected.txt b/dx/tests/007-no-superclass/expected.txt
new file mode 100644
index 0000000..d635c9a
--- /dev/null
+++ b/dx/tests/007-no-superclass/expected.txt
@@ -0,0 +1,19 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0003
+
+constant_pool:
+ 0001: utf8{"java/lang/Object"}
+ 0002: type{java.lang.Object}
+end constant_pool
+access_flags: public
+this_class: type{java.lang.Object}
+super_class: (none)
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/007-no-superclass/info.txt b/dx/tests/007-no-superclass/info.txt
new file mode 100644
index 0000000..5f941d0
--- /dev/null
+++ b/dx/tests/007-no-superclass/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class is Object-like and
+has no superclass.
diff --git a/dx/tests/007-no-superclass/run b/dx/tests/007-no-superclass/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/007-no-superclass/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/007-no-superclass/small-class.txt b/dx/tests/007-no-superclass/small-class.txt
new file mode 100644
index 0000000..6fd4408
--- /dev/null
+++ b/dx/tests/007-no-superclass/small-class.txt
@@ -0,0 +1,24 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe # magic
+0000 # minor_version
+002e # major_version
+0003 # constant_pool_count
+
+#
+# constant_pool
+#
+01 0010 "java/lang/Object" # 0001: utf8["java/lang/Object"]
+07 0001 # 0002: class[java/lang/Object]
+
+0001 # access_flags
+0002 # this_class
+0000 # super_class
+0000 # interfaces_count
+
+0000 # fields_count
+0000 # methods_count
+
+0000 # attributes_count
diff --git a/dx/tests/008-field/expected.txt b/dx/tests/008-field/expected.txt
new file mode 100644
index 0000000..9e3bcaf
--- /dev/null
+++ b/dx/tests/008-field/expected.txt
@@ -0,0 +1,30 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0007
+
+constant_pool:
+ 0001: utf8{"Small"}
+ 0002: utf8{"java/lang/Object"}
+ 0003: type{Small}
+ 0004: type{java.lang.Object}
+ 0005: utf8{"foo"}
+ 0006: utf8{"I"}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0001
+
+fields[0]:
+ access_flags: public|private|protected|static|final|volatile|transient|synthetic|enum|af20
+ name: foo
+ descriptor: I
+ attributes_count: 0000
+end fields[0]
+methods_count: 0000
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/008-field/info.txt b/dx/tests/008-field/info.txt
new file mode 100644
index 0000000..0b8e92f
--- /dev/null
+++ b/dx/tests/008-field/info.txt
@@ -0,0 +1,7 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+simple field with no attributes and with every access flag turned on
+(so that the names can be verified in debugging output).
diff --git a/dx/tests/008-field/run b/dx/tests/008-field/run
new file mode 100644
index 0000000..1c759d6
--- /dev/null
+++ b/dx/tests/008-field/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump --width=200 small-class.txt
diff --git a/dx/tests/008-field/small-class.txt b/dx/tests/008-field/small-class.txt
new file mode 100644
index 0000000..81eb164
--- /dev/null
+++ b/dx/tests/008-field/small-class.txt
@@ -0,0 +1,34 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe # magic
+0000 # minor_version
+002e # major_version
+0007 # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small" # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001 # 0003: class[Small]
+07 0002 # 0004: class[java/lang/Object]
+01 0003 "foo" # 0005: utf8["foo"]
+01 0001 "I" # 0006: utf8["I"]
+
+0021 # access_flags
+0003 # this_class
+0004 # super_class
+0000 # interfaces_count
+
+0001 # fields_count
+
+# fields[0]
+ffff # access_flags
+0005 # name
+0006 # descriptor
+0000 # attributes_count
+
+0000 # methods_count
+0000 # attributes_count
diff --git a/dx/tests/009-method/expected.txt b/dx/tests/009-method/expected.txt
new file mode 100644
index 0000000..3c0d6ad
--- /dev/null
+++ b/dx/tests/009-method/expected.txt
@@ -0,0 +1,30 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0007
+
+constant_pool:
+ 0001: utf8{"Small"}
+ 0002: utf8{"java/lang/Object"}
+ 0003: type{Small}
+ 0004: type{java.lang.Object}
+ 0005: utf8{"foo"}
+ 0006: utf8{"()V"}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0001
+
+methods[0]:
+ access_flags: public|private|protected|static|final|synchronized|bridge|varargs|native|abstract|strictfp|synthetic|e200
+ name: foo
+ descriptor: ()V
+ attributes_count: 0000
+end methods[0]
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/009-method/info.txt b/dx/tests/009-method/info.txt
new file mode 100644
index 0000000..3df2f09
--- /dev/null
+++ b/dx/tests/009-method/info.txt
@@ -0,0 +1,7 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+simple method with no attributes and with every access flag turned on
+(so that the names can be verified in debugging output).
diff --git a/dx/tests/009-method/run b/dx/tests/009-method/run
new file mode 100644
index 0000000..1c759d6
--- /dev/null
+++ b/dx/tests/009-method/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump --width=200 small-class.txt
diff --git a/dx/tests/009-method/small-class.txt b/dx/tests/009-method/small-class.txt
new file mode 100644
index 0000000..54fbc5f
--- /dev/null
+++ b/dx/tests/009-method/small-class.txt
@@ -0,0 +1,34 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe # magic
+0000 # minor_version
+002e # major_version
+0007 # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small" # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001 # 0003: class[Small]
+07 0002 # 0004: class[java/lang/Object]
+01 0003 "foo" # 0005: utf8["foo"]
+01 0003 "()V" # 0006: utf8["()V"]
+
+0021 # access_flags
+0003 # this_class
+0004 # super_class
+0000 # interfaces_count
+0000 # fields_count
+
+0001 # methods_count
+
+# methods[0]
+ffff # access_flags
+0005 # name
+0006 # descriptor
+0000 # attributes_count
+
+0000 # attributes_count
diff --git a/dx/tests/010-class-attrib-InnerClasses/expected.txt b/dx/tests/010-class-attrib-InnerClasses/expected.txt
new file mode 100644
index 0000000..590ed2e
--- /dev/null
+++ b/dx/tests/010-class-attrib-InnerClasses/expected.txt
@@ -0,0 +1,46 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0008
+
+constant_pool:
+ 0001: utf8{"Small"}
+ 0002: utf8{"java/lang/Object"}
+ 0003: type{Small}
+ 0004: type{java.lang.Object}
+ 0005: utf8{"InnerClasses"}
+ 0006: utf8{"Zorch"}
+ 0007: type{Zorch}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0001
+
+attributes[0]:
+ name: InnerClasses
+ length: 00000022
+ number_of_classes: 0004
+ inner_class: type{Small}
+ outer_class: (none)
+ name: (none)
+ access_flags: public
+ inner_class: type{Small}
+ outer_class: (none)
+ name: utf8{"Small"}
+ access_flags: private
+ inner_class: type{Small}
+ outer_class: type{Zorch}
+ name: (none)
+ access_flags: protected
+ inner_class: type{Zorch}
+ outer_class: type{Small}
+ name: utf8{"Zorch"}
+ access_flags: public|private|protected|static|final|interface|abstract|synthetic|annotation|enum|89e0
+end attributes[0]
+end classfile
diff --git a/dx/tests/010-class-attrib-InnerClasses/info.txt b/dx/tests/010-class-attrib-InnerClasses/info.txt
new file mode 100644
index 0000000..305b035
--- /dev/null
+++ b/dx/tests/010-class-attrib-InnerClasses/info.txt
@@ -0,0 +1,7 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+class-level InnerClasses attribute, which is syntactically valid and contains
+one entry for each of the possible combinations of null-vs-valid cpe.
diff --git a/dx/tests/010-class-attrib-InnerClasses/run b/dx/tests/010-class-attrib-InnerClasses/run
new file mode 100644
index 0000000..1c759d6
--- /dev/null
+++ b/dx/tests/010-class-attrib-InnerClasses/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump --width=200 small-class.txt
diff --git a/dx/tests/010-class-attrib-InnerClasses/small-class.txt b/dx/tests/010-class-attrib-InnerClasses/small-class.txt
new file mode 100644
index 0000000..54fd19d
--- /dev/null
+++ b/dx/tests/010-class-attrib-InnerClasses/small-class.txt
@@ -0,0 +1,37 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe # magic
+0000 # minor_version
+002e # major_version
+0008 # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small" # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001 # 0003: class[Small]
+07 0002 # 0004: class[java/lang/Object]
+01 000c "InnerClasses" # 0005: utf8["InnerClasses"]
+01 0005 "Zorch" # 0006: utf8["Zorch"]
+07 0006 # 0007: class[Zorch]
+
+0021 # access_flags
+0003 # this_class
+0004 # super_class
+0000 # interfaces_count
+0000 # fields_count
+0000 # methods_count
+
+0001 # attributes_count
+
+# attribute[0]
+0005 # name
+00000022 # length
+0004 # number_of_classes
+0003 0000 0000 0001 # Small / null / null / public
+0003 0000 0001 0002 # Small / null / "Small" / private
+0003 0007 0000 0004 # Small / Zorch / null / protected
+0007 0003 0006 ffff # Zorch / Small / "Zorch" / all-bits
diff --git a/dx/tests/011-class-attrib-Synthetic/expected.txt b/dx/tests/011-class-attrib-Synthetic/expected.txt
new file mode 100644
index 0000000..85e2bff
--- /dev/null
+++ b/dx/tests/011-class-attrib-Synthetic/expected.txt
@@ -0,0 +1,27 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0006
+
+constant_pool:
+ 0001: utf8{"Small"}
+ 0002: utf8{"java/lang/Object"}
+ 0003: type{Small}
+ 0004: type{java.lang.Object}
+ 0005: utf8{"Synthetic"}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0001
+
+attributes[0]:
+ name: Synthetic
+ length: 00000000
+end attributes[0]
+end classfile
diff --git a/dx/tests/011-class-attrib-Synthetic/info.txt b/dx/tests/011-class-attrib-Synthetic/info.txt
new file mode 100644
index 0000000..bfd443e
--- /dev/null
+++ b/dx/tests/011-class-attrib-Synthetic/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+class-level Synthetic attribute, which is syntactically valid.
diff --git a/dx/tests/011-class-attrib-Synthetic/run b/dx/tests/011-class-attrib-Synthetic/run
new file mode 100644
index 0000000..1c759d6
--- /dev/null
+++ b/dx/tests/011-class-attrib-Synthetic/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump --width=200 small-class.txt
diff --git a/dx/tests/011-class-attrib-Synthetic/small-class.txt b/dx/tests/011-class-attrib-Synthetic/small-class.txt
new file mode 100644
index 0000000..bc3281b
--- /dev/null
+++ b/dx/tests/011-class-attrib-Synthetic/small-class.txt
@@ -0,0 +1,30 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe # magic
+0000 # minor_version
+002e # major_version
+0006 # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small" # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001 # 0003: class[Small]
+07 0002 # 0004: class[java/lang/Object]
+01 0009 "Synthetic" # 0005: utf8["Synthetic"]
+
+0021 # access_flags
+0003 # this_class
+0004 # super_class
+0000 # interfaces_count
+0000 # fields_count
+0000 # methods_count
+
+0001 # attributes_count
+
+# attribute[0]
+0005 # name
+00000000 # length
diff --git a/dx/tests/012-class-attrib-SourceFile/expected.txt b/dx/tests/012-class-attrib-SourceFile/expected.txt
new file mode 100644
index 0000000..c795cde
--- /dev/null
+++ b/dx/tests/012-class-attrib-SourceFile/expected.txt
@@ -0,0 +1,29 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0007
+
+constant_pool:
+ 0001: utf8{"Small"}
+ 0002: utf8{"java/lang/Object"}
+ 0003: type{Small}
+ 0004: type{java.lang.Object}
+ 0005: utf8{"SourceFile"}
+ 0006: utf8{"Blort.java"}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0001
+
+attributes[0]:
+ name: SourceFile
+ length: 00000002
+ source: utf8{"Blort.java"}
+end attributes[0]
+end classfile
diff --git a/dx/tests/012-class-attrib-SourceFile/info.txt b/dx/tests/012-class-attrib-SourceFile/info.txt
new file mode 100644
index 0000000..f1d8985
--- /dev/null
+++ b/dx/tests/012-class-attrib-SourceFile/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+class-level SourceFile attribute, which is syntactically valid.
diff --git a/dx/tests/012-class-attrib-SourceFile/run b/dx/tests/012-class-attrib-SourceFile/run
new file mode 100644
index 0000000..1c759d6
--- /dev/null
+++ b/dx/tests/012-class-attrib-SourceFile/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump --width=200 small-class.txt
diff --git a/dx/tests/012-class-attrib-SourceFile/small-class.txt b/dx/tests/012-class-attrib-SourceFile/small-class.txt
new file mode 100644
index 0000000..3c514be
--- /dev/null
+++ b/dx/tests/012-class-attrib-SourceFile/small-class.txt
@@ -0,0 +1,32 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe # magic
+0000 # minor_version
+002e # major_version
+0007 # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small" # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001 # 0003: class[Small]
+07 0002 # 0004: class[java/lang/Object]
+01 000a "SourceFile" # 0005: utf8["SourceFile"]
+01 000a "Blort.java" # 0006: utf8["Blort.java"]
+
+0021 # access_flags
+0003 # this_class
+0004 # super_class
+0000 # interfaces_count
+0000 # fields_count
+0000 # methods_count
+
+0001 # attributes_count
+
+# attribute[0]
+0005 # name
+00000002 # length
+0006 # "Blort.java"
diff --git a/dx/tests/013-class-attrib-Deprecated/expected.txt b/dx/tests/013-class-attrib-Deprecated/expected.txt
new file mode 100644
index 0000000..4476c89
--- /dev/null
+++ b/dx/tests/013-class-attrib-Deprecated/expected.txt
@@ -0,0 +1,27 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0006
+
+constant_pool:
+ 0001: utf8{"Small"}
+ 0002: utf8{"java/lang/Object"}
+ 0003: type{Small}
+ 0004: type{java.lang.Object}
+ 0005: utf8{"Deprecated"}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0001
+
+attributes[0]:
+ name: Deprecated
+ length: 00000000
+end attributes[0]
+end classfile
diff --git a/dx/tests/013-class-attrib-Deprecated/info.txt b/dx/tests/013-class-attrib-Deprecated/info.txt
new file mode 100644
index 0000000..8164fe1
--- /dev/null
+++ b/dx/tests/013-class-attrib-Deprecated/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+class-level Deprecated attribute, which is syntactically valid.
diff --git a/dx/tests/013-class-attrib-Deprecated/run b/dx/tests/013-class-attrib-Deprecated/run
new file mode 100644
index 0000000..1c759d6
--- /dev/null
+++ b/dx/tests/013-class-attrib-Deprecated/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump --width=200 small-class.txt
diff --git a/dx/tests/013-class-attrib-Deprecated/small-class.txt b/dx/tests/013-class-attrib-Deprecated/small-class.txt
new file mode 100644
index 0000000..03d7e24
--- /dev/null
+++ b/dx/tests/013-class-attrib-Deprecated/small-class.txt
@@ -0,0 +1,30 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe # magic
+0000 # minor_version
+002e # major_version
+0006 # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small" # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001 # 0003: class[Small]
+07 0002 # 0004: class[java/lang/Object]
+01 000a "Deprecated" # 0005: utf8["Deprecated"]
+
+0021 # access_flags
+0003 # this_class
+0004 # super_class
+0000 # interfaces_count
+0000 # fields_count
+0000 # methods_count
+
+0001 # attributes_count
+
+# attribute[0]
+0005 # name
+00000000 # length
diff --git a/dx/tests/014-field-attrib-ConstantValue/expected.txt b/dx/tests/014-field-attrib-ConstantValue/expected.txt
new file mode 100644
index 0000000..b3d91a5
--- /dev/null
+++ b/dx/tests/014-field-attrib-ConstantValue/expected.txt
@@ -0,0 +1,162 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 001f
+
+constant_pool:
+ 0001: utf8{"Small"}
+ 0002: utf8{"java/lang/Object"}
+ 0003: type{Small}
+ 0004: type{java.lang.Object}
+ 0005: utf8{"ConstantValue"}
+ 0006: utf8{"a"}
+ 0007: utf8{"b"}
+ 0008: utf8{"c"}
+ 0009: utf8{"d"}
+ 000a: utf8{"e"}
+ 000b: utf8{"f"}
+ 000c: utf8{"g"}
+ 000d: utf8{"h"}
+ 000e: utf8{"i"}
+ 000f: string{"Small"}
+ 0010: int{0x8191a1b1 / -2121162319}
+ 0011: float{0xbffeb852 / -1.99}
+ 0012: long{0x80818283f0f1f2f3 / -9186918261664386317}
+ 0014: double{0xbfffd70a3d70a3d7 / -1.99}
+ 0016: utf8{"B"}
+ 0017: utf8{"C"}
+ 0018: utf8{"D"}
+ 0019: utf8{"F"}
+ 001a: utf8{"I"}
+ 001b: utf8{"J"}
+ 001c: utf8{"S"}
+ 001d: utf8{"Z"}
+ 001e: utf8{"Ljava/lang/String;"}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0009
+
+fields[0]:
+ access_flags: public
+ name: a
+ descriptor: B
+ attributes_count: 0001
+
+ attributes[0]:
+ name: ConstantValue
+ length: 00000002
+ value: int{0x8191a1b1 / -2121162319}
+ end attributes[0]
+end fields[0]
+
+fields[1]:
+ access_flags: private
+ name: b
+ descriptor: C
+ attributes_count: 0001
+
+ attributes[0]:
+ name: ConstantValue
+ length: 00000002
+ value: int{0x8191a1b1 / -2121162319}
+ end attributes[0]
+end fields[1]
+
+fields[2]:
+ access_flags: protected
+ name: c
+ descriptor: D
+ attributes_count: 0001
+
+ attributes[0]:
+ name: ConstantValue
+ length: 00000002
+ value: double{0xbfffd70a3d70a3d7 / -1.99}
+ end attributes[0]
+end fields[2]
+
+fields[3]:
+ access_flags: static
+ name: d
+ descriptor: F
+ attributes_count: 0001
+
+ attributes[0]:
+ name: ConstantValue
+ length: 00000002
+ value: float{0xbffeb852 / -1.99}
+ end attributes[0]
+end fields[3]
+
+fields[4]:
+ access_flags: final
+ name: e
+ descriptor: I
+ attributes_count: 0001
+
+ attributes[0]:
+ name: ConstantValue
+ length: 00000002
+ value: int{0x8191a1b1 / -2121162319}
+ end attributes[0]
+end fields[4]
+
+fields[5]:
+ access_flags: volatile
+ name: f
+ descriptor: J
+ attributes_count: 0001
+
+ attributes[0]:
+ name: ConstantValue
+ length: 00000002
+ value: long{0x80818283f0f1f2f3 / -9186918261664386317}
+ end attributes[0]
+end fields[5]
+
+fields[6]:
+ access_flags: transient
+ name: g
+ descriptor: S
+ attributes_count: 0001
+
+ attributes[0]:
+ name: ConstantValue
+ length: 00000002
+ value: int{0x8191a1b1 / -2121162319}
+ end attributes[0]
+end fields[6]
+
+fields[7]:
+ access_flags: public|static|final
+ name: h
+ descriptor: Z
+ attributes_count: 0001
+
+ attributes[0]:
+ name: ConstantValue
+ length: 00000002
+ value: int{0x8191a1b1 / -2121162319}
+ end attributes[0]
+end fields[7]
+
+fields[8]:
+ access_flags: public|static|final
+ name: i
+ descriptor: Ljava/lang/String;
+ attributes_count: 0001
+
+ attributes[0]:
+ name: ConstantValue
+ length: 00000002
+ value: string{"Small"}
+ end attributes[0]
+end fields[8]
+methods_count: 0000
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/014-field-attrib-ConstantValue/info.txt b/dx/tests/014-field-attrib-ConstantValue/info.txt
new file mode 100644
index 0000000..313159c
--- /dev/null
+++ b/dx/tests/014-field-attrib-ConstantValue/info.txt
@@ -0,0 +1,7 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a series of
+fields, each with a single ConstantValue attribute, which points at one
+of the appropriate sorts of cpes.
diff --git a/dx/tests/014-field-attrib-ConstantValue/run b/dx/tests/014-field-attrib-ConstantValue/run
new file mode 100644
index 0000000..1c759d6
--- /dev/null
+++ b/dx/tests/014-field-attrib-ConstantValue/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump --width=200 small-class.txt
diff --git a/dx/tests/014-field-attrib-ConstantValue/small-class.txt b/dx/tests/014-field-attrib-ConstantValue/small-class.txt
new file mode 100644
index 0000000..8d869de
--- /dev/null
+++ b/dx/tests/014-field-attrib-ConstantValue/small-class.txt
@@ -0,0 +1,140 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe # magic
+0000 # minor_version
+002e # major_version
+001f # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small" # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001 # 0003: class[Small]
+07 0002 # 0004: class[java/lang/Object]
+01 000d "ConstantValue" # 0005: utf8["ConstantValue"]
+01 0001 "a" # 0006: utf8["a"]
+01 0001 "b" # 0007: utf8["b"]
+01 0001 "c" # 0008: utf8["c"]
+01 0001 "d" # 0009: utf8["d"]
+01 0001 "e" # 000a: utf8["e"]
+01 0001 "f" # 000b: utf8["f"]
+01 0001 "g" # 000c: utf8["g"]
+01 0001 "h" # 000d: utf8["h"]
+01 0001 "i" # 000e: utf8["i"]
+08 0001 # 000f: string["Small"]
+03 8191a1b1 # 0010: integer[0x8191a1b1]
+04 bffeb852 # 0011: float[-1.99]
+05 80818283 f0f1f2f3 # 0012: long[0x80818283f0f1f2f3]
+06 bfffd70a 3d70a3d7 # 0014: double[-1.99]
+01 0001 "B" # 0016: utf8["B"]
+01 0001 "C" # 0017: utf8["C"]
+01 0001 "D" # 0018: utf8["D"]
+01 0001 "F" # 0019: utf8["F"]
+01 0001 "I" # 001a: utf8["I"]
+01 0001 "J" # 001b: utf8["J"]
+01 0001 "S" # 001c: utf8["S"]
+01 0001 "Z" # 001d: utf8["Z"]
+01 0012 "Ljava/lang/String;" # 001e: utf8["Ljava/lang/String;"]
+
+0021 # access_flags
+0003 # this_class
+0004 # super_class
+0000 # interfaces_count
+
+0009 # fields_count
+
+# fields[0]
+0001 # access_flags
+0006 # "a"
+0016 # "B"
+0001 # attributes_count
+# attributes[0]
+0005 # name
+00000002 # length
+0010 # value
+
+# fields[1]
+0002 # access_flags
+0007 # "b"
+0017 # "C"
+0001 # attributes_count
+# attributes[0]
+0005 # name
+00000002 # length
+0010 # value
+
+# fields[2]
+0004 # access_flags
+0008 # "c"
+0018 # "D"
+0001 # attributes_count
+# attributes[0]
+0005 # name
+00000002 # length
+0014 # value
+
+# fields[3]
+0008 # access_flags
+0009 # "d"
+0019 # "F"
+0001 # attributes_count
+# attributes[0]
+0005 # name
+00000002 # length
+0011 # value
+
+# fields[4]
+0010 # access_flags
+000a # "e"
+001a # "I"
+0001 # attributes_count
+# attributes[0]
+0005 # name
+00000002 # length
+0010 # value
+
+# fields[5]
+0040 # access_flags
+000b # "f"
+001b # "J"
+0001 # attributes_count
+# attributes[0]
+0005 # name
+00000002 # length
+0012 # value
+
+# fields[6]
+0080 # access_flags
+000c # "g"
+001c # "Z"
+0001 # attributes_count
+# attributes[0]
+0005 # name
+00000002 # length
+0010 # value
+
+# fields[7]
+0019 # access_flags
+000d # "h"
+001d # "S"
+0001 # attributes_count
+# attributes[0]
+0005 # name
+00000002 # length
+0010 # value
+
+# fields[8]
+0019 # access_flags
+000e # "i"
+001e # "Ljava/lang/String;"
+0001 # attributes_count
+# attributes[0]
+0005 # name
+00000002 # length
+000f # value
+
+0000 # methods_count
+0000 # attributes_count
diff --git a/dx/tests/015-field-attrib-Synthetic/expected.txt b/dx/tests/015-field-attrib-Synthetic/expected.txt
new file mode 100644
index 0000000..bdb4519
--- /dev/null
+++ b/dx/tests/015-field-attrib-Synthetic/expected.txt
@@ -0,0 +1,36 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0008
+
+constant_pool:
+ 0001: utf8{"Small"}
+ 0002: utf8{"java/lang/Object"}
+ 0003: type{Small}
+ 0004: type{java.lang.Object}
+ 0005: utf8{"Synthetic"}
+ 0006: utf8{"a"}
+ 0007: utf8{"I"}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0001
+
+fields[0]:
+ access_flags: public
+ name: a
+ descriptor: I
+ attributes_count: 0001
+
+ attributes[0]:
+ name: Synthetic
+ length: 00000000
+ end attributes[0]
+end fields[0]
+methods_count: 0000
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/015-field-attrib-Synthetic/info.txt b/dx/tests/015-field-attrib-Synthetic/info.txt
new file mode 100644
index 0000000..6037cfa
--- /dev/null
+++ b/dx/tests/015-field-attrib-Synthetic/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+field with a valid Synthetic attribute.
diff --git a/dx/tests/015-field-attrib-Synthetic/run b/dx/tests/015-field-attrib-Synthetic/run
new file mode 100644
index 0000000..1c759d6
--- /dev/null
+++ b/dx/tests/015-field-attrib-Synthetic/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump --width=200 small-class.txt
diff --git a/dx/tests/015-field-attrib-Synthetic/small-class.txt b/dx/tests/015-field-attrib-Synthetic/small-class.txt
new file mode 100644
index 0000000..e5f429e
--- /dev/null
+++ b/dx/tests/015-field-attrib-Synthetic/small-class.txt
@@ -0,0 +1,38 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe # magic
+0000 # minor_version
+002e # major_version
+0008 # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small" # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001 # 0003: class[Small]
+07 0002 # 0004: class[java/lang/Object]
+01 0009 "Synthetic" # 0005: utf8["Synthetic"]
+01 0001 "a" # 0006: utf8["a"]
+01 0001 "I" # 0007: utf8["I"]
+
+0021 # access_flags
+0003 # this_class
+0004 # super_class
+0000 # interfaces_count
+
+0001 # fields_count
+
+# fields[0]
+0001 # access_flags
+0006 # "a"
+0007 # "I"
+0001 # attributes_count
+# attributes[0]
+0005 # name
+00000000 # length
+
+0000 # methods_count
+0000 # attributes_count
diff --git a/dx/tests/016-field-attrib-Deprecated/expected.txt b/dx/tests/016-field-attrib-Deprecated/expected.txt
new file mode 100644
index 0000000..1b5547f
--- /dev/null
+++ b/dx/tests/016-field-attrib-Deprecated/expected.txt
@@ -0,0 +1,36 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0008
+
+constant_pool:
+ 0001: utf8{"Small"}
+ 0002: utf8{"java/lang/Object"}
+ 0003: type{Small}
+ 0004: type{java.lang.Object}
+ 0005: utf8{"Deprecated"}
+ 0006: utf8{"a"}
+ 0007: utf8{"I"}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0001
+
+fields[0]:
+ access_flags: public
+ name: a
+ descriptor: I
+ attributes_count: 0001
+
+ attributes[0]:
+ name: Deprecated
+ length: 00000000
+ end attributes[0]
+end fields[0]
+methods_count: 0000
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/016-field-attrib-Deprecated/info.txt b/dx/tests/016-field-attrib-Deprecated/info.txt
new file mode 100644
index 0000000..1185981
--- /dev/null
+++ b/dx/tests/016-field-attrib-Deprecated/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+field with a valid Deprecated attribute.
diff --git a/dx/tests/016-field-attrib-Deprecated/run b/dx/tests/016-field-attrib-Deprecated/run
new file mode 100644
index 0000000..1c759d6
--- /dev/null
+++ b/dx/tests/016-field-attrib-Deprecated/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump --width=200 small-class.txt
diff --git a/dx/tests/016-field-attrib-Deprecated/small-class.txt b/dx/tests/016-field-attrib-Deprecated/small-class.txt
new file mode 100644
index 0000000..2448ce4
--- /dev/null
+++ b/dx/tests/016-field-attrib-Deprecated/small-class.txt
@@ -0,0 +1,38 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe # magic
+0000 # minor_version
+002e # major_version
+0008 # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small" # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001 # 0003: class[Small]
+07 0002 # 0004: class[java/lang/Object]
+01 000a "Deprecated" # 0005: utf8["Deprecated"]
+01 0001 "a" # 0006: utf8["a"]
+01 0001 "I" # 0007: utf8["I"]
+
+0021 # access_flags
+0003 # this_class
+0004 # super_class
+0000 # interfaces_count
+
+0001 # fields_count
+
+# fields[0]
+0001 # access_flags
+0006 # "a"
+0007 # "I"
+0001 # attributes_count
+# attributes[0]
+0005 # name
+00000000 # length
+
+0000 # methods_count
+0000 # attributes_count
diff --git a/dx/tests/017-method-attrib-Code/expected.txt b/dx/tests/017-method-attrib-Code/expected.txt
new file mode 100644
index 0000000..c43730c
--- /dev/null
+++ b/dx/tests/017-method-attrib-Code/expected.txt
@@ -0,0 +1,42 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0008
+
+constant_pool:
+ 0001: type{Small}
+ 0002: type{java.lang.Object}
+ 0003: utf8{"Small"}
+ 0004: utf8{"java/lang/Object"}
+ 0005: utf8{"blort"}
+ 0006: utf8{"()V"}
+ 0007: utf8{"Code"}
+end constant_pool
+access_flags: public
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0001
+
+methods[0]:
+ access_flags: public
+ name: blort
+ descriptor: ()V
+ attributes_count: 0001
+
+ attributes[0]:
+ name: Code
+ length: 0000000d
+ max_stack: 0001
+ max_locals: 0001
+ code_length: 00000001
+ 0000: return
+ exception_table_length: 0000
+ attributes_count: 0000
+ end attributes[0]
+end methods[0]
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/017-method-attrib-Code/info.txt b/dx/tests/017-method-attrib-Code/info.txt
new file mode 100644
index 0000000..a3ed24c
--- /dev/null
+++ b/dx/tests/017-method-attrib-Code/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+method with a minimal but syntactically valid Code attribute.
diff --git a/dx/tests/017-method-attrib-Code/run b/dx/tests/017-method-attrib-Code/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/017-method-attrib-Code/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/017-method-attrib-Code/small-class.txt b/dx/tests/017-method-attrib-Code/small-class.txt
new file mode 100644
index 0000000..699005b
--- /dev/null
+++ b/dx/tests/017-method-attrib-Code/small-class.txt
@@ -0,0 +1,43 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe # magic
+0000 # minor_version
+002e # major_version
+0008 # constant_pool_count
+
+#
+# constant_pool
+#
+07 0003 # 0001: class[Small]
+07 0004 # 0002: class[java/lang/Object]
+01 0005 "Small" # 0003: utf8["Small"]
+01 0010 "java/lang/Object" # 0004: utf8["java/lang/Object"]
+01 0005 "blort" # 0005: utf8["blort"]
+01 0003 "()V" # 0006: utf8["()V"]
+01 0004 "Code" # 0007: utf8["Code"]
+
+0001 # access_flags
+0001 # this_class
+0002 # super_class
+0000 # interfaces_count
+0000 # fields_count
+0001 # methods_count
+
+# methods[0]
+0001 # access_flags
+0005 # name
+0006 # descriptor
+0001 # attributes_count
+# attributes[0]
+0007 # name
+0000000d # length
+0001 # max_stack
+0001 # max_locals
+00000001 # code_length
+b1 # 0000: return
+0000 # exception_table_length
+0000 # attributes_count
+
+0000 # attributes_count
diff --git a/dx/tests/018-method-attrib-Exceptions/expected.txt b/dx/tests/018-method-attrib-Exceptions/expected.txt
new file mode 100644
index 0000000..15f923f
--- /dev/null
+++ b/dx/tests/018-method-attrib-Exceptions/expected.txt
@@ -0,0 +1,40 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 000a
+
+constant_pool:
+ 0001: type{Small}
+ 0002: type{java.lang.Object}
+ 0003: utf8{"Small"}
+ 0004: utf8{"java/lang/Object"}
+ 0005: utf8{"blort"}
+ 0006: utf8{"()V"}
+ 0007: utf8{"Exceptions"}
+ 0008: utf8{"java/lang/Error"}
+ 0009: type{java.lang.Error}
+end constant_pool
+access_flags: public
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0001
+
+methods[0]:
+ access_flags: public|abstract
+ name: blort
+ descriptor: ()V
+ attributes_count: 0001
+
+ attributes[0]:
+ name: Exceptions
+ length: 00000004
+ number_of_exceptions: 0001
+ type{java.lang.Error}
+ end attributes[0]
+end methods[0]
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/018-method-attrib-Exceptions/info.txt b/dx/tests/018-method-attrib-Exceptions/info.txt
new file mode 100644
index 0000000..4a3738d
--- /dev/null
+++ b/dx/tests/018-method-attrib-Exceptions/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+method with a syntactically valid Exceptions attribute.
diff --git a/dx/tests/018-method-attrib-Exceptions/run b/dx/tests/018-method-attrib-Exceptions/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/018-method-attrib-Exceptions/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/018-method-attrib-Exceptions/small-class.txt b/dx/tests/018-method-attrib-Exceptions/small-class.txt
new file mode 100644
index 0000000..5d05b43
--- /dev/null
+++ b/dx/tests/018-method-attrib-Exceptions/small-class.txt
@@ -0,0 +1,41 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe # magic
+0000 # minor_version
+002e # major_version
+000a # constant_pool_count
+
+#
+# constant_pool
+#
+07 0003 # 0001: class[Small]
+07 0004 # 0002: class[java/lang/Object]
+01 0005 "Small" # 0003: utf8["Small"]
+01 0010 "java/lang/Object" # 0004: utf8["java/lang/Object"]
+01 0005 "blort" # 0005: utf8["blort"]
+01 0003 "()V" # 0006: utf8["()V"]
+01 000a "Exceptions" # 0007: utf8["Exceptions"]
+01 000f "java/lang/Error" # 0008: utf8["java/lang/Error"]
+07 0008 # 0009: class[java/lang/Error]
+
+0001 # access_flags
+0001 # this_class
+0002 # super_class
+0000 # interfaces_count
+0000 # fields_count
+0001 # methods_count
+
+# methods[0]
+0401 # access_flags
+0005 # name
+0006 # descriptor
+0001 # attributes_count
+# attributes[0]
+0007 # name
+00000004 # length
+0001 # number_of_exceptions
+0009 # class[java/lang/Error]
+
+0000 # attributes_count
diff --git a/dx/tests/019-method-attrib-Synthetic/expected.txt b/dx/tests/019-method-attrib-Synthetic/expected.txt
new file mode 100644
index 0000000..5d1d9cb
--- /dev/null
+++ b/dx/tests/019-method-attrib-Synthetic/expected.txt
@@ -0,0 +1,36 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0008
+
+constant_pool:
+ 0001: type{Small}
+ 0002: type{java.lang.Object}
+ 0003: utf8{"Small"}
+ 0004: utf8{"java/lang/Object"}
+ 0005: utf8{"blort"}
+ 0006: utf8{"()V"}
+ 0007: utf8{"Synthetic"}
+end constant_pool
+access_flags: public
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0001
+
+methods[0]:
+ access_flags: public|abstract
+ name: blort
+ descriptor: ()V
+ attributes_count: 0001
+
+ attributes[0]:
+ name: Synthetic
+ length: 00000000
+ end attributes[0]
+end methods[0]
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/019-method-attrib-Synthetic/info.txt b/dx/tests/019-method-attrib-Synthetic/info.txt
new file mode 100644
index 0000000..eddf4fb
--- /dev/null
+++ b/dx/tests/019-method-attrib-Synthetic/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+method with a syntactically valid Synthetic attribute.
diff --git a/dx/tests/019-method-attrib-Synthetic/run b/dx/tests/019-method-attrib-Synthetic/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/019-method-attrib-Synthetic/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/019-method-attrib-Synthetic/small-class.txt b/dx/tests/019-method-attrib-Synthetic/small-class.txt
new file mode 100644
index 0000000..458dae8
--- /dev/null
+++ b/dx/tests/019-method-attrib-Synthetic/small-class.txt
@@ -0,0 +1,37 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe # magic
+0000 # minor_version
+002e # major_version
+0008 # constant_pool_count
+
+#
+# constant_pool
+#
+07 0003 # 0001: class[Small]
+07 0004 # 0002: class[java/lang/Object]
+01 0005 "Small" # 0003: utf8["Small"]
+01 0010 "java/lang/Object" # 0004: utf8["java/lang/Object"]
+01 0005 "blort" # 0005: utf8["blort"]
+01 0003 "()V" # 0006: utf8["()V"]
+01 0009 "Synthetic" # 0007: utf8["Synthetic"]
+
+0001 # access_flags
+0001 # this_class
+0002 # super_class
+0000 # interfaces_count
+0000 # fields_count
+0001 # methods_count
+
+# methods[0]
+0401 # access_flags
+0005 # name
+0006 # descriptor
+0001 # attributes_count
+# attributes[0]
+0007 # name
+00000000 # length
+
+0000 # attributes_count
diff --git a/dx/tests/020-method-attrib-Deprecated/expected.txt b/dx/tests/020-method-attrib-Deprecated/expected.txt
new file mode 100644
index 0000000..8da8aa8
--- /dev/null
+++ b/dx/tests/020-method-attrib-Deprecated/expected.txt
@@ -0,0 +1,36 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0008
+
+constant_pool:
+ 0001: type{Small}
+ 0002: type{java.lang.Object}
+ 0003: utf8{"Small"}
+ 0004: utf8{"java/lang/Object"}
+ 0005: utf8{"blort"}
+ 0006: utf8{"()V"}
+ 0007: utf8{"Deprecated"}
+end constant_pool
+access_flags: public
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0001
+
+methods[0]:
+ access_flags: public|abstract
+ name: blort
+ descriptor: ()V
+ attributes_count: 0001
+
+ attributes[0]:
+ name: Deprecated
+ length: 00000000
+ end attributes[0]
+end methods[0]
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/020-method-attrib-Deprecated/info.txt b/dx/tests/020-method-attrib-Deprecated/info.txt
new file mode 100644
index 0000000..b7c6266
--- /dev/null
+++ b/dx/tests/020-method-attrib-Deprecated/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+method with a syntactically valid Deprecated attribute.
diff --git a/dx/tests/020-method-attrib-Deprecated/run b/dx/tests/020-method-attrib-Deprecated/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/020-method-attrib-Deprecated/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/020-method-attrib-Deprecated/small-class.txt b/dx/tests/020-method-attrib-Deprecated/small-class.txt
new file mode 100644
index 0000000..f906733
--- /dev/null
+++ b/dx/tests/020-method-attrib-Deprecated/small-class.txt
@@ -0,0 +1,37 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe # magic
+0000 # minor_version
+002e # major_version
+0008 # constant_pool_count
+
+#
+# constant_pool
+#
+07 0003 # 0001: class[Small]
+07 0004 # 0002: class[java/lang/Object]
+01 0005 "Small" # 0003: utf8["Small"]
+01 0010 "java/lang/Object" # 0004: utf8["java/lang/Object"]
+01 0005 "blort" # 0005: utf8["blort"]
+01 0003 "()V" # 0006: utf8["()V"]
+01 000a "Deprecated" # 0007: utf8["Deprecated"]
+
+0001 # access_flags
+0001 # this_class
+0002 # super_class
+0000 # interfaces_count
+0000 # fields_count
+0001 # methods_count
+
+# methods[0]
+0401 # access_flags
+0005 # name
+0006 # descriptor
+0001 # attributes_count
+# attributes[0]
+0007 # name
+00000000 # length
+
+0000 # attributes_count
diff --git a/dx/tests/021-code-attrib-LineNumberTable/expected.txt b/dx/tests/021-code-attrib-LineNumberTable/expected.txt
new file mode 100644
index 0000000..3f29310
--- /dev/null
+++ b/dx/tests/021-code-attrib-LineNumberTable/expected.txt
@@ -0,0 +1,52 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0009
+
+constant_pool:
+ 0001: type{Small}
+ 0002: type{java.lang.Object}
+ 0003: utf8{"Small"}
+ 0004: utf8{"java/lang/Object"}
+ 0005: utf8{"blort"}
+ 0006: utf8{"()V"}
+ 0007: utf8{"Code"}
+ 0008: utf8{"LineNumberTable"}
+end constant_pool
+access_flags: public
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0001
+
+methods[0]:
+ access_flags: public
+ name: blort
+ descriptor: ()V
+ attributes_count: 0001
+
+ attributes[0]:
+ name: Code
+ length: 0000001e
+ max_stack: 0001
+ max_locals: 0001
+ code_length: 00000002
+ 0000: return
+ 0001: return
+ exception_table_length: 0000
+ attributes_count: 0001
+
+ attributes[0]:
+ name: LineNumberTable
+ length: 0000000a
+ line_number_table_length: 0002
+ 0000 17
+ 0001 34
+ end attributes[0]
+ end attributes[0]
+end methods[0]
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/021-code-attrib-LineNumberTable/info.txt b/dx/tests/021-code-attrib-LineNumberTable/info.txt
new file mode 100644
index 0000000..2ffc798
--- /dev/null
+++ b/dx/tests/021-code-attrib-LineNumberTable/info.txt
@@ -0,0 +1,7 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+method with a simple Code attribute, which itself has a syntactically
+valid LineNumberTable attribute.
diff --git a/dx/tests/021-code-attrib-LineNumberTable/run b/dx/tests/021-code-attrib-LineNumberTable/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/021-code-attrib-LineNumberTable/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/021-code-attrib-LineNumberTable/small-class.txt b/dx/tests/021-code-attrib-LineNumberTable/small-class.txt
new file mode 100644
index 0000000..c28c39a
--- /dev/null
+++ b/dx/tests/021-code-attrib-LineNumberTable/small-class.txt
@@ -0,0 +1,51 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe # magic
+0000 # minor_version
+002e # major_version
+0009 # constant_pool_count
+
+#
+# constant_pool
+#
+07 0003 # 0001: class[Small]
+07 0004 # 0002: class[java/lang/Object]
+01 0005 "Small" # 0003: utf8["Small"]
+01 0010 "java/lang/Object" # 0004: utf8["java/lang/Object"]
+01 0005 "blort" # 0005: utf8["blort"]
+01 0003 "()V" # 0006: utf8["()V"]
+01 0004 "Code" # 0007: utf8["Code"]
+01 000f "LineNumberTable" # 0008: utf8["LineNumberTable"]
+
+0001 # access_flags
+0001 # this_class
+0002 # super_class
+0000 # interfaces_count
+0000 # fields_count
+0001 # methods_count
+
+# methods[0]
+0001 # access_flags
+0005 # name
+0006 # descriptor
+0001 # attributes_count
+# attributes[0]
+0007 # name
+0000001e # length
+0001 # max_stack
+0001 # max_locals
+00000002 # code_length
+b1 # 0000: return
+b1 # 0001: return
+0000 # exception_table_length
+0001 # attributes_count
+# attributes[0]
+0008 # name
+0000000a # length
+0002 # line_number_table_length
+0000 0011 # offset 0000, line #17
+0001 0022 # offset 0001, line #34
+
+0000 # attributes_count
diff --git a/dx/tests/022-code-attrib-LocalVariableTable/expected.txt b/dx/tests/022-code-attrib-LocalVariableTable/expected.txt
new file mode 100644
index 0000000..80091ed
--- /dev/null
+++ b/dx/tests/022-code-attrib-LocalVariableTable/expected.txt
@@ -0,0 +1,57 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 000d
+
+constant_pool:
+ 0001: type{Small}
+ 0002: type{java.lang.Object}
+ 0003: utf8{"Small"}
+ 0004: utf8{"java/lang/Object"}
+ 0005: utf8{"blort"}
+ 0006: utf8{"()V"}
+ 0007: utf8{"Code"}
+ 0008: utf8{"LocalVariableTable"}
+ 0009: utf8{"foo"}
+ 000a: utf8{"bar"}
+ 000b: utf8{"baz"}
+ 000c: utf8{"[I"}
+end constant_pool
+access_flags: public
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0001
+
+methods[0]:
+ access_flags: public
+ name: blort
+ descriptor: ()V
+ attributes_count: 0001
+
+ attributes[0]:
+ name: Code
+ length: 00000034
+ max_stack: 0001
+ max_locals: 0002
+ code_length: 00000002
+ 0000: return
+ 0001: return
+ exception_table_length: 0000
+ attributes_count: 0001
+
+ attributes[0]:
+ name: LocalVariableTable
+ length: 00000020
+ local_variable_table_length: 0003
+ 0000..0002 0000 foo [I
+ 0000..0001 0001 bar [I
+ 0001..0002 0001 baz [I
+ end attributes[0]
+ end attributes[0]
+end methods[0]
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/022-code-attrib-LocalVariableTable/info.txt b/dx/tests/022-code-attrib-LocalVariableTable/info.txt
new file mode 100644
index 0000000..5b44286
--- /dev/null
+++ b/dx/tests/022-code-attrib-LocalVariableTable/info.txt
@@ -0,0 +1,7 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+method with a simple Code attribute, which itself has a syntactically
+valid LocalVariableTable attribute.
diff --git a/dx/tests/022-code-attrib-LocalVariableTable/run b/dx/tests/022-code-attrib-LocalVariableTable/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/022-code-attrib-LocalVariableTable/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/022-code-attrib-LocalVariableTable/small-class.txt b/dx/tests/022-code-attrib-LocalVariableTable/small-class.txt
new file mode 100644
index 0000000..abbf8ea
--- /dev/null
+++ b/dx/tests/022-code-attrib-LocalVariableTable/small-class.txt
@@ -0,0 +1,56 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe # magic
+0000 # minor_version
+002e # major_version
+000d # constant_pool_count
+
+#
+# constant_pool
+#
+07 0003 # 0001: class[Small]
+07 0004 # 0002: class[java/lang/Object]
+01 0005 "Small" # 0003: utf8["Small"]
+01 0010 "java/lang/Object" # 0004: utf8["java/lang/Object"]
+01 0005 "blort" # 0005: utf8["blort"]
+01 0003 "()V" # 0006: utf8["()V"]
+01 0004 "Code" # 0007: utf8["Code"]
+01 0012 "LocalVariableTable" # 0008: utf8["LocalVariableTable"]
+01 0003 "foo" # 0009: utf8["foo"]
+01 0003 "bar" # 000a: utf8["bar"]
+01 0003 "baz" # 000b: utf8["baz"]
+01 0002 "[I" # 000c: utf8["[I"]
+
+0001 # access_flags
+0001 # this_class
+0002 # super_class
+0000 # interfaces_count
+0000 # fields_count
+0001 # methods_count
+
+# methods[0]
+0001 # access_flags
+0005 # name
+0006 # descriptor
+0001 # attributes_count
+# attributes[0]
+0007 # name
+00000034 # length
+0001 # max_stack
+0002 # max_locals
+00000002 # code_length
+b1 # 0000: return
+b1 # 0001: return
+0000 # exception_table_length
+0001 # attributes_count
+# attributes[0]
+0008 # name
+00000020 # length
+0003 # local_variable_table_length
+0000 0002 0009 000c 0000 # 0000..0002 foo:[I #0000
+0000 0001 000a 000c 0001 # 0000..0001 bar:[I #0001
+0001 0001 000b 000c 0001 # 0001..0002 baz:[I #0001
+
+0000 # attributes_count
diff --git a/dx/tests/023-code-exception-table/expected.txt b/dx/tests/023-code-exception-table/expected.txt
new file mode 100644
index 0000000..552e5d3
--- /dev/null
+++ b/dx/tests/023-code-exception-table/expected.txt
@@ -0,0 +1,51 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 000c
+
+constant_pool:
+ 0001: type{Small}
+ 0002: type{java.lang.Object}
+ 0003: utf8{"Small"}
+ 0004: utf8{"java/lang/Object"}
+ 0005: utf8{"blort"}
+ 0006: utf8{"()V"}
+ 0007: utf8{"Code"}
+ 0008: utf8{"java/lang/Error"}
+ 0009: utf8{"java/lang/Exception"}
+ 000a: type{java.lang.Error}
+ 000b: type{java.lang.Exception}
+end constant_pool
+access_flags: public
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0001
+
+methods[0]:
+ access_flags: public
+ name: blort
+ descriptor: ()V
+ attributes_count: 0001
+
+ attributes[0]:
+ name: Code
+ length: 00000027
+ max_stack: 0001
+ max_locals: 0001
+ code_length: 00000003
+ 0000: return
+ 0001: return
+ 0002: return
+ exception_table_length: 0003
+ 0000..0002 -> 0002 java.lang.Error
+ 0000..0001 -> 0001 java.lang.Exception
+ 0001..0002 -> 0002 <any>
+ attributes_count: 0000
+ end attributes[0]
+end methods[0]
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/023-code-exception-table/info.txt b/dx/tests/023-code-exception-table/info.txt
new file mode 100644
index 0000000..2dbb5da
--- /dev/null
+++ b/dx/tests/023-code-exception-table/info.txt
@@ -0,0 +1,7 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+method with a minimal but syntactically valid Code attribute, which
+sports a non-empty syntactically valid exception table.
diff --git a/dx/tests/023-code-exception-table/run b/dx/tests/023-code-exception-table/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/023-code-exception-table/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/023-code-exception-table/small-class.txt b/dx/tests/023-code-exception-table/small-class.txt
new file mode 100644
index 0000000..f8ff1f3
--- /dev/null
+++ b/dx/tests/023-code-exception-table/small-class.txt
@@ -0,0 +1,52 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe # magic
+0000 # minor_version
+002e # major_version
+000c # constant_pool_count
+
+#
+# constant_pool
+#
+07 0003 # 0001: class[Small]
+07 0004 # 0002: class[java/lang/Object]
+01 0005 "Small" # 0003: utf8["Small"]
+01 0010 "java/lang/Object" # 0004: utf8["java/lang/Object"]
+01 0005 "blort" # 0005: utf8["blort"]
+01 0003 "()V" # 0006: utf8["()V"]
+01 0004 "Code" # 0007: utf8["Code"]
+01 000f "java/lang/Error" # 0008: utf8["java/lang/Error"]
+01 0013 "java/lang/Exception" # 0009: utf8["java/lang/Exception"]
+07 0008 # 000a: class[java/lang/Error]
+07 0009 # 000b: class[java/lang/Exception]
+
+0001 # access_flags
+0001 # this_class
+0002 # super_class
+0000 # interfaces_count
+0000 # fields_count
+0001 # methods_count
+
+# methods[0]
+0001 # access_flags
+0005 # name
+0006 # descriptor
+0001 # attributes_count
+# attributes[0]
+0007 # name
+00000027 # length
+0001 # max_stack
+0001 # max_locals
+00000003 # code_length
+b1 # 0000: return
+b1 # 0001: return
+b1 # 0002: return
+0003 # exception_table_length
+0000 0002 0002 000a # 0000..0002 -> 0002 java/lang/Error
+0000 0001 0001 000b # 0000..0001 -> 0001 java/lang/Exception
+0001 0002 0002 0000 # 0001..0002 -> 0002 <any>
+0000 # attributes_count
+
+0000 # attributes_count
diff --git a/dx/tests/024-code-bytecode/expected.txt b/dx/tests/024-code-bytecode/expected.txt
new file mode 100644
index 0000000..4637474
--- /dev/null
+++ b/dx/tests/024-code-bytecode/expected.txt
@@ -0,0 +1,294 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0017
+
+constant_pool:
+ 0001: type{Small}
+ 0002: type{java.lang.Object}
+ 0003: utf8{"Small"}
+ 0004: utf8{"java/lang/Object"}
+ 0005: utf8{"blort"}
+ 0006: utf8{"()V"}
+ 0007: utf8{"Code"}
+ 0008: string{"Small"}
+ 0009: int{0x12345678 / 305419896}
+ 000a: float{0x42f6e666 / 123.45}
+ 000b: long{0x123456789abcdef0 / 1311768467463790320}
+ 000d: double{0x411958955f8a0903 / 415269.3433}
+ 000f: utf8{"blort"}
+ 0010: utf8{"x/y/Zzz"}
+ 0011: utf8{"()V"}
+ 0012: nat{blort:x/y/Zzz}
+ 0013: nat{blort:()V}
+ 0014: field{Small.blort:x/y/Zzz}
+ 0015: method{Small.blort:()V}
+ 0016: ifaceMethod{Small.blort:()V}
+end constant_pool
+access_flags: public
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0001
+
+methods[0]:
+ access_flags: public
+ name: blort
+ descriptor: ()V
+ attributes_count: 0001
+
+ attributes[0]:
+ name: Code
+ length: 000001dc
+ max_stack: 0001
+ max_locals: 0001
+ code_length: 000001d0
+ 0000: nop
+ 0001: aconst_null
+ 0002: iconst_m1 // #-01
+ 0003: iconst_0 // #+00
+ 0004: iconst_1 // #+01
+ 0005: iconst_2 // #+02
+ 0006: iconst_3 // #+03
+ 0007: iconst_4 // #+04
+ 0008: iconst_5 // #+05
+ 0009: lconst_0 // +00
+ 000a: lconst_1 // +01
+ 000b: fconst_0 // 0.0
+ 000c: fconst_1 // 1.0
+ 000d: fconst_2 // 2.0
+ 000e: dconst_0 // 0.0
+ 000f: dconst_1 // 1.0
+ 0010: bipush #+45
+ 0012: sipush #+5432
+ 0015: ldc string{"Small"}
+ 0017: ldc #+12345678
+ 0019: ldc #42f6e666 // 123.45
+ 001b: ldc_w string{"Small"}
+ 001e: ldc_w #+12345678
+ 0021: ldc_w #42f6e666 // 123.45
+ 0024: ldc2_w #+123456789abcdef0
+ 0027: ldc2_w #411958955f8a0903 // 415269.3433
+ 002a: iload 01
+ 002c: lload 02 // category-2
+ 002e: fload 03
+ 0030: dload 04 // category-2
+ 0032: aload 05
+ 0034: iload_0 // 00
+ 0035: iload_1 // 01
+ 0036: iload_2 // 02
+ 0037: iload_3 // 03
+ 0038: lload_0 // 00, category-2
+ 0039: lload_1 // 01, category-2
+ 003a: lload_2 // 02, category-2
+ 003b: lload_3 // 03, category-2
+ 003c: fload_0 // 00
+ 003d: fload_1 // 01
+ 003e: fload_2 // 02
+ 003f: fload_3 // 03
+ 0040: dload_0 // 00, category-2
+ 0041: dload_1 // 01, category-2
+ 0042: dload_2 // 02, category-2
+ 0043: dload_3 // 03, category-2
+ 0044: aload_0 // 00
+ 0045: aload_1 // 01
+ 0046: aload_2 // 02
+ 0047: aload_3 // 03
+ 0048: iaload
+ 0049: laload
+ 004a: faload
+ 004b: daload
+ 004c: aaload
+ 004d: baload
+ 004e: caload
+ 004f: saload
+ 0050: istore 41
+ 0052: lstore 42 // category-2
+ 0054: fstore 43
+ 0056: dstore 44 // category-2
+ 0058: astore 45
+ 005a: istore_0 // 00
+ 005b: istore_1 // 01
+ 005c: istore_2 // 02
+ 005d: istore_3 // 03
+ 005e: lstore_0 // 00, category-2
+ 005f: lstore_1 // 01, category-2
+ 0060: lstore_2 // 02, category-2
+ 0061: lstore_3 // 03, category-2
+ 0062: fstore_0 // 00
+ 0063: fstore_1 // 01
+ 0064: fstore_2 // 02
+ 0065: fstore_3 // 03
+ 0066: dstore_0 // 00, category-2
+ 0067: dstore_1 // 01, category-2
+ 0068: dstore_2 // 02, category-2
+ 0069: dstore_3 // 03, category-2
+ 006a: astore_0 // 00
+ 006b: astore_1 // 01
+ 006c: astore_2 // 02
+ 006d: astore_3 // 03
+ 006e: iastore
+ 006f: lastore
+ 0070: fastore
+ 0071: dastore
+ 0072: aastore
+ 0073: bastore
+ 0074: castore
+ 0075: sastore
+ 0076: pop
+ 0077: pop2
+ 0078: dup
+ 0079: dup_x1
+ 007a: dup_x2
+ 007b: dup2
+ 007c: dup2_x1
+ 007d: dup2_x2
+ 007e: swap
+ 007f: iadd
+ 0080: ladd
+ 0081: fadd
+ 0082: dadd
+ 0083: isub
+ 0084: lsub
+ 0085: fsub
+ 0086: dsub
+ 0087: imul
+ 0088: lmul
+ 0089: fmul
+ 008a: dmul
+ 008b: idiv
+ 008c: ldiv
+ 008d: fdiv
+ 008e: ddiv
+ 008f: irem
+ 0090: lrem
+ 0091: frem
+ 0092: drem
+ 0093: ineg
+ 0094: lneg
+ 0095: fneg
+ 0096: dneg
+ 0097: ishl
+ 0098: lshl
+ 0099: ishr
+ 009a: lshr
+ 009b: iushr
+ 009c: lushr
+ 009d: iand
+ 009e: land
+ 009f: ior
+ 00a0: lor
+ 00a1: ixor
+ 00a2: lxor
+ 00a3: iinc 05, #-01
+ 00a6: i2l
+ 00a7: i2f
+ 00a8: i2d
+ 00a9: l2i
+ 00aa: l2f
+ 00ab: l2d
+ 00ac: f2i
+ 00ad: f2l
+ 00ae: f2d
+ 00af: d2i
+ 00b0: d2l
+ 00b1: d2f
+ 00b2: i2b
+ 00b3: i2c
+ 00b4: i2s
+ 00b5: lcmp
+ 00b6: fcmpl
+ 00b7: fcmpg
+ 00b8: dcmpl
+ 00b9: dcmpg
+ 00ba: ifeq 00ba
+ 00bd: ifne 00ba
+ 00c0: iflt 00ba
+ 00c3: ifge 00ba
+ 00c6: ifgt 00ba
+ 00c9: ifle 00ba
+ 00cc: if_icmpeq 00db
+ 00cf: if_icmpne 00db
+ 00d2: if_icmplt 00db
+ 00d5: if_icmpge 00db
+ 00d8: if_icmpgt 00db
+ 00db: if_icmple 00db
+ 00de: if_acmpeq 00de
+ 00e1: if_acmpne 00e1
+ 00e4: goto 0000
+ 00e7: jsr 00e7
+ 00ea: ret 2f
+ 00ec: tableswitch
+ +12340000: 0000
+ +12340001: 0001
+ +12340002: 0002
+ +12340003: 0003
+ +12340004: 0004
+ +12340005: 0005
+ +12340006: 0007
+ +12340007: 0009
+ default: 00ea
+ 011c: lookupswitch
+ -7689edcc: 0148
+ +00001000: 0149
+ +03333333: 0149
+ +79787776: 014b
+ default: 00ec
+ 0148: ireturn
+ 0149: lreturn
+ 014a: freturn
+ 014b: dreturn
+ 014c: areturn
+ 014d: return
+ 014e: getstatic field{Small.blort:x/y/Zzz}
+ 0151: putstatic field{Small.blort:x/y/Zzz}
+ 0154: getfield field{Small.blort:x/y/Zzz}
+ 0157: putfield field{Small.blort:x/y/Zzz}
+ 015a: invokevirtual method{Small.blort:()V}
+ 015d: invokespecial method{Small.blort:()V}
+ 0160: invokestatic method{Small.blort:()V}
+ 0163: invokeinterface ifaceMethod{Small.blort:()V}, 0001
+ 0168: unused_ba
+ 0169: new type{Small}
+ 016c: newarray boolean
+ 016e: newarray char
+ 0170: newarray float
+ 0172: newarray double
+ 0174: newarray byte
+ 0176: newarray short
+ 0178: newarray int
+ 017a: newarray long
+ 017c: anewarray type{Small}
+ 017f: arraylength
+ 0180: athrow
+ 0181: checkcast type{java.lang.Object}
+ 0184: instanceof type{java.lang.Object}
+ 0187: monitorenter
+ 0188: monitorexit
+ 0189: wide iload 0123
+ 018d: wide lload 0124 // category-2
+ 0191: wide fload 0125
+ 0195: wide dload 0126 // category-2
+ 0199: wide aload 0127
+ 019d: wide istore 20f0
+ 01a1: wide lstore 20f1 // category-2
+ 01a5: wide fstore 20f2
+ 01a9: wide dstore 20f3 // category-2
+ 01ad: wide astore 20f4
+ 01b1: wide ret ffff
+ 01b5: wide iinc 0002, #+1000
+ 01bb: multianewarray type{java.lang.Object}, 04
+ 01bf: ifnull 0000
+ 01c2: ifnonnull 01c2
+ 01c5: goto_w 700001c5
+ 01ca: jsr_w 000001c5
+ 01cf: unused_ca
+ exception_table_length: 0000
+ attributes_count: 0000
+ end attributes[0]
+end methods[0]
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/024-code-bytecode/info.txt b/dx/tests/024-code-bytecode/info.txt
new file mode 100644
index 0000000..d1264a8
--- /dev/null
+++ b/dx/tests/024-code-bytecode/info.txt
@@ -0,0 +1,7 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+method with a Code attribute, and the code[] array of the attribute has
+one instance of each interesting variant of each possible bytecode.
diff --git a/dx/tests/024-code-bytecode/run b/dx/tests/024-code-bytecode/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/024-code-bytecode/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/024-code-bytecode/small-class.txt b/dx/tests/024-code-bytecode/small-class.txt
new file mode 100644
index 0000000..2526cf2
--- /dev/null
+++ b/dx/tests/024-code-bytecode/small-class.txt
@@ -0,0 +1,304 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe # magic
+0000 # minor_version
+002e # major_version
+0017 # constant_pool_count
+
+#
+# constant_pool
+#
+07 0003 # 0001: class[Small]
+07 0004 # 0002: class[java/lang/Object]
+01 0005 "Small" # 0003: utf8["Small"]
+01 0010 "java/lang/Object" # 0004: utf8["java/lang/Object"]
+01 0005 "blort" # 0005: utf8["blort"]
+01 0003 "()V" # 0006: utf8["()V"]
+01 0004 "Code" # 0007: utf8["Code"]
+08 0003 # 0008: string["Small"]
+03 12345678 # 0009: integer[0x12345678]
+04 42f6e666 # 000a: float[123.45]
+05 12345678 9abcdef0 # 000b: long[0x1234567890abcdef0]
+06 41195895 5f8a0903 # 000d: double[415269.3433]
+01 0005 "blort" # 000f: utf8["blort"]
+01 0007 "x/y/Zzz" # 0010: utf8["x/y/Zzz"]
+01 0003 "()V" # 0011: utf8["()V"]
+0c 000f 0010 # 0012: nat[blort:x/y/Zzz]
+0c 000f 0011 # 0013: nat[blort:()V]
+09 0001 0012 # 0014: field[Small.blort:x/y/Zzz]
+0a 0001 0013 # 0015: method[Small.blort:()V]
+0b 0001 0013 # 0016: ifaceMethod[Small.blort:()V]
+
+0001 # access_flags
+0001 # this_class
+0002 # super_class
+0000 # interfaces_count
+0000 # fields_count
+0001 # methods_count
+
+# methods[0]
+0001 # access_flags
+0005 # name
+0006 # descriptor
+0001 # attributes_count
+# attributes[0]
+0007 # name
+000001dc # length (note: == code_length + 0x0c)
+0001 # max_stack
+0001 # max_locals
+000001d0 # code_length
+
+00 # 0000: nop
+01 # 0001: aconst_null
+02 # 0002: aconst_m1
+03 # 0003: iconst_0
+04 # 0004: iconst_1
+05 # 0005: iconst_2
+06 # 0006: iconst_3
+07 # 0007: iconst_4
+08 # 0008: iconst_5
+09 # 0009: lconst_0
+0a # 000a: lconst_1
+0b # 000b: fconst_0
+0c # 000c: fconst_1
+0d # 000d: fconst_2
+0e # 000e: dconst_0
+0f # 000f: dconst_1
+10 45 # 0010: bipush #+45
+11 5432 # 0012: sipush #+5432
+12 08 # 0015: ldc <string>
+12 09 # 0017: ldc <integer>
+12 0a # 0019: ldc <float>
+13 0008 # 001b: ldc_w <string>
+13 0009 # 001e: ldc_w <integer>
+13 000a # 0021: ldc_w <float>
+14 000b # 0024: ldc2_w <long>
+14 000d # 0027: ldc2_w <double>
+15 01 # 002a: iload 01
+16 02 # 002c: lload 02
+17 03 # 002e: fload 03
+18 04 # 0030: dload 04
+19 05 # 0032: aload 05
+1a # 0034: iload_0
+1b # 0035: iload_1
+1c # 0036: iload_2
+1d # 0037: iload_3
+1e # 0038: lload_0
+1f # 0039: lload_1
+20 # 003a: lload_2
+21 # 003b: lload_3
+22 # 003c: fload_0
+23 # 003d: fload_1
+24 # 003e: fload_2
+25 # 003f: fload_3
+26 # 0040: dload_0
+27 # 0041: dload_1
+28 # 0042: dload_2
+29 # 0043: dload_3
+2a # 0044: aload_0
+2b # 0045: aload_1
+2c # 0046: aload_2
+2d # 0047: aload_3
+2e # 0048: iaload
+2f # 0049: laload
+30 # 004a: faload
+31 # 004b: daload
+32 # 004c: aaload
+33 # 004d: baload
+34 # 004e: caload
+35 # 004f: saload
+36 41 # 0050: istore 41
+37 42 # 0052: lstore 42
+38 43 # 0054: fstore 43
+39 44 # 0056: dstore 44
+3a 45 # 0058: astore 45
+3b # 005a: istore_0
+3c # 005b: istore_1
+3d # 005c: istore_2
+3e # 005d: istore_3
+3f # 005e: lstore_0
+40 # 005f: lstore_1
+41 # 0060: lstore_2
+42 # 0061: lstore_3
+43 # 0062: fstore_0
+44 # 0063: fstore_1
+45 # 0064: fstore_2
+46 # 0065: fstore_3
+47 # 0066: dstore_0
+48 # 0067: dstore_1
+49 # 0068: dstore_2
+4a # 0069: dstore_3
+4b # 006a: astore_0
+4c # 006b: astore_1
+4d # 006c: astore_2
+4e # 006d: astore_3
+4f # 006e: iastore
+50 # 006f: lastore
+51 # 0070: fastore
+52 # 0071: dastore
+53 # 0072: aastore
+54 # 0073: bastore
+55 # 0074: castore
+56 # 0075: sastore
+57 # 0076: pop
+58 # 0077: pop2
+59 # 0078: dup
+5a # 0079: dup_x1
+5b # 007a: dup_x2
+5c # 007b: dup2
+5d # 007c: dup2_x1
+5e # 007d: dup2_x2
+5f # 007e: swap
+60 # 007f: iadd
+61 # 0080: ladd
+62 # 0081: fadd
+63 # 0082: dadd
+64 # 0083: isub
+65 # 0084: lsub
+66 # 0085: fsub
+67 # 0086: dsub
+68 # 0087: imul
+69 # 0088: lmul
+6a # 0089: fmul
+6b # 008a: dmul
+6c # 008b: idiv
+6d # 008c: ldiv
+6e # 008d: fdiv
+6f # 008e: ddiv
+70 # 008f: irem
+71 # 0090: lrem
+72 # 0091: frem
+73 # 0092: drem
+74 # 0093: ineg
+75 # 0094: lneg
+76 # 0095: fneg
+77 # 0096: dneg
+78 # 0097: ishl
+79 # 0098: lshl
+7a # 0099: ishr
+7b # 009a: lshr
+7c # 009b: iushr
+7d # 009c: lushr
+7e # 009d: iand
+7f # 009e: land
+80 # 009f: ior
+81 # 00a0: lor
+82 # 00a1: ixor
+83 # 00a2: lxor
+84 05 ff # 00a3: iinc 05, #-1
+85 # 00a6: i2l
+86 # 00a7: i2f
+87 # 00a8: i2d
+88 # 00a9: l2i
+89 # 00aa: l2f
+8a # 00ab: l2d
+8b # 00ac: f2i
+8c # 00ad: f2l
+8d # 00ae: f2d
+8e # 00af: d2i
+8f # 00b0: d2l
+90 # 00b1: d2f
+91 # 00b2: i2b
+92 # 00b3: i2c
+93 # 00b4: i2s
+94 # 00b5: lcmp
+95 # 00b6: fcmpl
+96 # 00b7: fcmpg
+97 # 00b8: dcmpl
+98 # 00b9: dcmpg
+99 0000 # 00ba: ifeq 00ba
+9a fffd # 00bd: ifne 00ba
+9b fffa # 00c0: iflt 00ba
+9c fff7 # 00c3: ifge 00ba
+9d fff4 # 00c6: ifgt 00ba
+9e fff1 # 00c9: ifle 00ba
+9f 000f # 00cc: if_icmpeq 00db
+a0 000c # 00cf: if_icmpne 00db
+a1 0009 # 00d2: if_icmplt 00db
+a2 0006 # 00d5: if_icmpge 00db
+a3 0003 # 00d8: if_icmpgt 00db
+a4 0000 # 00db: if_icmple 00db
+a5 0000 # 00de: if_acmpeq 00de
+a6 0000 # 00e1: if_acmpne 00e1
+a7 ff1c # 00e4: goto 0000
+a8 0000 # 00e7: jsr 00e7
+a9 2f # 00ea: ret 2f
+aa 000000 # 00ec: tableswitch + padding
+ fffffffe # default: 000000ea
+ 12340000 # low: 12340000
+ 12340007 # high: 12340007
+ ffffff14 # [0]: 00000000
+ ffffff15 # [1]: 00000001
+ ffffff16 # [2]: 00000002
+ ffffff17 # [3]: 00000003
+ ffffff18 # [4]: 00000004
+ ffffff19 # [5]: 00000005
+ ffffff1b # [6]: 00000007
+ ffffff1d # [7]: 00000009
+ab 000000 # 011c: lookupswitch + padding
+ ffffffd0 # default: 000000ec
+ 00000004 # npairs: 4
+ 89761234 # match[0]: 89761234
+ 0000002c # offset[0]: 0148
+ 00001000 # match[1]: 00001000
+ 0000002d # offset[1]: 0149
+ 03333333 # match[2]: 03333333
+ 0000002d # offset[2]: 0149
+ 79787776 # match[3]: 79787776
+ 0000002f # offset[3]: 014b
+ac # 0148: ireturn
+ad # 0149: lreturn
+ae # 014a: freturn
+af # 014b: dreturn
+b0 # 014c: areturn
+b1 # 014d: return
+b2 0014 # 014e: getstatic 0014
+b3 0014 # 0151: putstatic 0014
+b4 0014 # 0154: getfield 0014
+b5 0014 # 0157: putfield 0014
+b6 0015 # 015a: invokevirtual 0015
+b7 0015 # 015d: invokespecial 0015
+b8 0015 # 0160: invokestatic 0015
+b9 0016 01 00 # 0163: invokeinterface 0016
+ba # 0168: <unused>
+bb 0001 # 0169: new 0001
+bc 04 # 016c: newarray boolean
+bc 05 # 016e: newarray char
+bc 06 # 0170: newarray float
+bc 07 # 0172: newarray double
+bc 08 # 0174: newarray byte
+bc 09 # 0176: newarray short
+bc 0a # 0178: newarray int
+bc 0b # 017a: newarray long
+bd 0001 # 017c: anewarray 0001
+be # 017f: arraylength
+bf # 0180: athrow
+c0 0002 # 0181: checkcast 0002
+c1 0002 # 0184: instanceof 0002
+c2 # 0187: monitorenter
+c3 # 0188: monitorexit
+c415 0123 # 0189: wide iload 0123
+c416 0124 # 018d: wide lload 0124
+c417 0125 # 0191: wide fload 0125
+c418 0126 # 0195: wide dload 0126
+c419 0127 # 0199: wide aload 0127
+c436 20f0 # 019d: wide istore 20f0
+c437 20f1 # 01a1: wide lstore 20f1
+c438 20f2 # 01a5: wide fstore 20f2
+c439 20f3 # 01a9: wide dstore 20f3
+c43a 20f4 # 01ad: wide astore 20f4
+c4a9 ffff # 01b1: wide ret ffff
+c484 0002 1000 # 01b5: wide iinc 0002, 1000
+c5 0002 04 # 01bb: multianewarray 0002, #04
+c6 fe41 # 01bf: ifnull 0000
+c7 0000 # 01c2: ifnonnull 01c2
+c8 70000000 # 01c5: goto_w 700001c5
+c9 fffffffb # 01ca: jsr_w 000001c5
+ca # 01cf: <unused>
+
+0000 # exception_table_length
+0000 # attributes_count
+
+0000 # attributes_count
diff --git a/dx/tests/025-class-attrib-Signature/expected.txt b/dx/tests/025-class-attrib-Signature/expected.txt
new file mode 100644
index 0000000..5ff56ed
--- /dev/null
+++ b/dx/tests/025-class-attrib-Signature/expected.txt
@@ -0,0 +1,29 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0007
+
+constant_pool:
+ 0001: utf8{"Small"}
+ 0002: utf8{"java/lang/Object"}
+ 0003: type{Small}
+ 0004: type{java.lang.Object}
+ 0005: utf8{"Signature"}
+ 0006: utf8{"LYo;"}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0001
+
+attributes[0]:
+ name: Signature
+ length: 00000002
+ signature: utf8{"LYo;"}
+end attributes[0]
+end classfile
diff --git a/dx/tests/025-class-attrib-Signature/info.txt b/dx/tests/025-class-attrib-Signature/info.txt
new file mode 100644
index 0000000..3d0ce51
--- /dev/null
+++ b/dx/tests/025-class-attrib-Signature/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+class-level Signature attribute, which is syntactically valid.
diff --git a/dx/tests/025-class-attrib-Signature/run b/dx/tests/025-class-attrib-Signature/run
new file mode 100644
index 0000000..1c759d6
--- /dev/null
+++ b/dx/tests/025-class-attrib-Signature/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump --width=200 small-class.txt
diff --git a/dx/tests/025-class-attrib-Signature/small-class.txt b/dx/tests/025-class-attrib-Signature/small-class.txt
new file mode 100644
index 0000000..d332e3a
--- /dev/null
+++ b/dx/tests/025-class-attrib-Signature/small-class.txt
@@ -0,0 +1,32 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe # magic
+0000 # minor_version
+002e # major_version
+0007 # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small" # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001 # 0003: class[Small]
+07 0002 # 0004: class[java/lang/Object]
+01 0009 "Signature" # 0005: utf8["Signature"]
+01 0004 "LYo;" # 0006: utf8["Yo"]
+
+0021 # access_flags
+0003 # this_class
+0004 # super_class
+0000 # interfaces_count
+0000 # fields_count
+0000 # methods_count
+
+0001 # attributes_count
+
+# attribute[0]
+0005 # name
+00000002 # length
+0006 # signature \ No newline at end of file
diff --git a/dx/tests/026-field-attrib-Signature/expected.txt b/dx/tests/026-field-attrib-Signature/expected.txt
new file mode 100644
index 0000000..c2e840e
--- /dev/null
+++ b/dx/tests/026-field-attrib-Signature/expected.txt
@@ -0,0 +1,38 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0009
+
+constant_pool:
+ 0001: utf8{"Small"}
+ 0002: utf8{"java/lang/Object"}
+ 0003: type{Small}
+ 0004: type{java.lang.Object}
+ 0005: utf8{"Signature"}
+ 0006: utf8{"a"}
+ 0007: utf8{"I"}
+ 0008: utf8{"LYo;"}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0001
+
+fields[0]:
+ access_flags: public
+ name: a
+ descriptor: I
+ attributes_count: 0001
+
+ attributes[0]:
+ name: Signature
+ length: 00000002
+ signature: utf8{"LYo;"}
+ end attributes[0]
+end fields[0]
+methods_count: 0000
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/026-field-attrib-Signature/info.txt b/dx/tests/026-field-attrib-Signature/info.txt
new file mode 100644
index 0000000..5b21ccb
--- /dev/null
+++ b/dx/tests/026-field-attrib-Signature/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+field with a syntactically valid Signature attribute.
diff --git a/dx/tests/026-field-attrib-Signature/run b/dx/tests/026-field-attrib-Signature/run
new file mode 100644
index 0000000..1c759d6
--- /dev/null
+++ b/dx/tests/026-field-attrib-Signature/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump --width=200 small-class.txt
diff --git a/dx/tests/026-field-attrib-Signature/small-class.txt b/dx/tests/026-field-attrib-Signature/small-class.txt
new file mode 100644
index 0000000..07d56a0
--- /dev/null
+++ b/dx/tests/026-field-attrib-Signature/small-class.txt
@@ -0,0 +1,40 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe # magic
+0000 # minor_version
+002e # major_version
+0009 # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small" # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001 # 0003: class[Small]
+07 0002 # 0004: class[java/lang/Object]
+01 0009 "Signature" # 0005: utf8["Signature"]
+01 0001 "a" # 0006: utf8["a"]
+01 0001 "I" # 0007: utf8["I"]
+01 0004 "LYo;" # 0008: utf8["Yo"]
+
+0021 # access_flags
+0003 # this_class
+0004 # super_class
+0000 # interfaces_count
+
+0001 # fields_count
+
+# fields[0]
+0001 # access_flags
+0006 # "a"
+0007 # "I"
+0001 # attributes_count
+# attributes[0]
+0005 # name
+00000002 # length
+0008 # signature
+
+0000 # methods_count
+0000 # attributes_count
diff --git a/dx/tests/027-method-attrib-Signature/expected.txt b/dx/tests/027-method-attrib-Signature/expected.txt
new file mode 100644
index 0000000..abc97c0
--- /dev/null
+++ b/dx/tests/027-method-attrib-Signature/expected.txt
@@ -0,0 +1,38 @@
+reading small-class.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0009
+
+constant_pool:
+ 0001: type{Small}
+ 0002: type{java.lang.Object}
+ 0003: utf8{"Small"}
+ 0004: utf8{"java/lang/Object"}
+ 0005: utf8{"blort"}
+ 0006: utf8{"()V"}
+ 0007: utf8{"Signature"}
+ 0008: utf8{"LYo;"}
+end constant_pool
+access_flags: public
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0001
+
+methods[0]:
+ access_flags: public|abstract
+ name: blort
+ descriptor: ()V
+ attributes_count: 0001
+
+ attributes[0]:
+ name: Signature
+ length: 00000002
+ signature: utf8{"LYo;"}
+ end attributes[0]
+end methods[0]
+attributes_count: 0000
+end classfile
diff --git a/dx/tests/027-method-attrib-Signature/info.txt b/dx/tests/027-method-attrib-Signature/info.txt
new file mode 100644
index 0000000..ea18c03
--- /dev/null
+++ b/dx/tests/027-method-attrib-Signature/info.txt
@@ -0,0 +1,6 @@
+This is a dump of a simple class which is valid in structure but is overall
+invalid. That being said, the system should still have no trouble parsing and
+dumping it.
+
+The salient bit of parsing tested here is that the class has a single
+method with a syntactically valid Signature attribute.
diff --git a/dx/tests/027-method-attrib-Signature/run b/dx/tests/027-method-attrib-Signature/run
new file mode 100644
index 0000000..16b7755
--- /dev/null
+++ b/dx/tests/027-method-attrib-Signature/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump small-class.txt
diff --git a/dx/tests/027-method-attrib-Signature/small-class.txt b/dx/tests/027-method-attrib-Signature/small-class.txt
new file mode 100644
index 0000000..c1cd6e3
--- /dev/null
+++ b/dx/tests/027-method-attrib-Signature/small-class.txt
@@ -0,0 +1,39 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe # magic
+0000 # minor_version
+002e # major_version
+0009 # constant_pool_count
+
+#
+# constant_pool
+#
+07 0003 # 0001: class[Small]
+07 0004 # 0002: class[java/lang/Object]
+01 0005 "Small" # 0003: utf8["Small"]
+01 0010 "java/lang/Object" # 0004: utf8["java/lang/Object"]
+01 0005 "blort" # 0005: utf8["blort"]
+01 0003 "()V" # 0006: utf8["()V"]
+01 0009 "Signature" # 0007: utf8["Signature"]
+01 0004 "LYo;" # 0008: utf8["Yo"]
+
+0001 # access_flags
+0001 # this_class
+0002 # super_class
+0000 # interfaces_count
+0000 # fields_count
+0001 # methods_count
+
+# methods[0]
+0401 # access_flags
+0005 # name
+0006 # descriptor
+0001 # attributes_count
+# attributes[0]
+0007 # name
+00000002 # length
+0008 # signature
+
+0000 # attributes_count
diff --git a/dx/tests/028-class-attrib-EnclosingMethod/expected.txt b/dx/tests/028-class-attrib-EnclosingMethod/expected.txt
new file mode 100644
index 0000000..15e9524
--- /dev/null
+++ b/dx/tests/028-class-attrib-EnclosingMethod/expected.txt
@@ -0,0 +1,61 @@
+reading small-class-1.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0006
+
+constant_pool:
+ 0001: utf8{"Small"}
+ 0002: utf8{"java/lang/Object"}
+ 0003: type{Small}
+ 0004: type{java.lang.Object}
+ 0005: utf8{"EnclosingMethod"}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0001
+
+attributes[0]:
+ name: EnclosingMethod
+ length: 00000004
+ class: type{Small}
+ method: (none)
+end attributes[0]
+end classfile
+reading small-class-2.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 002e
+constant_pool_count: 0009
+
+constant_pool:
+ 0001: utf8{"Small"}
+ 0002: utf8{"java/lang/Object"}
+ 0003: type{Small}
+ 0004: type{java.lang.Object}
+ 0005: utf8{"EnclosingMethod"}
+ 0006: utf8{"zorp"}
+ 0007: utf8{"()V"}
+ 0008: nat{zorp:()V}
+end constant_pool
+access_flags: public|super
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0001
+
+attributes[0]:
+ name: EnclosingMethod
+ length: 00000004
+ class: type{Small}
+ method: nat{zorp:()V}
+end attributes[0]
+end classfile
diff --git a/dx/tests/028-class-attrib-EnclosingMethod/info.txt b/dx/tests/028-class-attrib-EnclosingMethod/info.txt
new file mode 100644
index 0000000..9600803
--- /dev/null
+++ b/dx/tests/028-class-attrib-EnclosingMethod/info.txt
@@ -0,0 +1,8 @@
+This is a dump of two simple classes which are valid in structure but
+are overall invalid. That being said, the system should still have no
+trouble parsing and dumping them.
+
+The salient bit of parsing tested here is that each class has a single
+class-level EnclosingMethod attribute, which is syntactically valid. There
+are two possible variants (method may be null), and this test contains one
+of each.
diff --git a/dx/tests/028-class-attrib-EnclosingMethod/run b/dx/tests/028-class-attrib-EnclosingMethod/run
new file mode 100644
index 0000000..f33a338
--- /dev/null
+++ b/dx/tests/028-class-attrib-EnclosingMethod/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump --width=200 small-class-1.txt small-class-2.txt
diff --git a/dx/tests/028-class-attrib-EnclosingMethod/small-class-1.txt b/dx/tests/028-class-attrib-EnclosingMethod/small-class-1.txt
new file mode 100644
index 0000000..aeaf2a3
--- /dev/null
+++ b/dx/tests/028-class-attrib-EnclosingMethod/small-class-1.txt
@@ -0,0 +1,32 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe # magic
+0000 # minor_version
+002e # major_version
+0006 # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small" # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001 # 0003: class[Small]
+07 0002 # 0004: class[java/lang/Object]
+01 000f "EnclosingMethod" # 0005: utf8["EnclosingMethod"]
+
+0021 # access_flags
+0003 # this_class
+0004 # super_class
+0000 # interfaces_count
+0000 # fields_count
+0000 # methods_count
+
+0001 # attributes_count
+
+# attribute[0]
+0005 # name
+00000004 # length
+0003 # class
+0000 # method
diff --git a/dx/tests/028-class-attrib-EnclosingMethod/small-class-2.txt b/dx/tests/028-class-attrib-EnclosingMethod/small-class-2.txt
new file mode 100644
index 0000000..8e6148e
--- /dev/null
+++ b/dx/tests/028-class-attrib-EnclosingMethod/small-class-2.txt
@@ -0,0 +1,35 @@
+#
+# sample small-but-valid classfile
+#
+
+cafe babe # magic
+0000 # minor_version
+002e # major_version
+0009 # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small" # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001 # 0003: class[Small]
+07 0002 # 0004: class[java/lang/Object]
+01 000f "EnclosingMethod" # 0005: utf8["EnclosingMethod"]
+01 0004 "zorp" # 0006: utf8["zorp"]
+01 0003 "()V" # 0007: utf8["()V"]
+0c 0006 0007 # 0008: nat[zorp:()V]
+
+0021 # access_flags
+0003 # this_class
+0004 # super_class
+0000 # interfaces_count
+0000 # fields_count
+0000 # methods_count
+
+0001 # attributes_count
+
+# attribute[0]
+0005 # name
+00000004 # length
+0003 # class
+0008 # method
diff --git a/dx/tests/029-unit-Bits/expected.txt b/dx/tests/029-unit-Bits/expected.txt
new file mode 100644
index 0000000..5418338
--- /dev/null
+++ b/dx/tests/029-unit-Bits/expected.txt
@@ -0,0 +1 @@
+Yay!
diff --git a/dx/tests/029-unit-Bits/info.txt b/dx/tests/029-unit-Bits/info.txt
new file mode 100644
index 0000000..fa56715
--- /dev/null
+++ b/dx/tests/029-unit-Bits/info.txt
@@ -0,0 +1 @@
+Unit test for com.android.dx.util.Bits.
diff --git a/dx/tests/029-unit-Bits/run b/dx/tests/029-unit-Bits/run
new file mode 100644
index 0000000..9937ce5
--- /dev/null
+++ b/dx/tests/029-unit-Bits/run
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --junit com.android.dx.util._tests._Bits > unit-out.txt
+
+if [ "$?" = "0" ]; then
+ echo "Yay!"
+else
+ cat unit-out.txt
+fi
diff --git a/dx/tests/030-minimal-jasmin/blort.j b/dx/tests/030-minimal-jasmin/blort.j
new file mode 100644
index 0000000..45722bc
--- /dev/null
+++ b/dx/tests/030-minimal-jasmin/blort.j
@@ -0,0 +1,28 @@
+; Copyright (C) 2007 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class blort
+.super java/lang/Object
+
+.method public <init>()V
+ .limit locals 2
+ .limit stack 3
+
+ aload_0
+ dup
+ dup
+ astore_1
+ pop2
+ return
+.end method
diff --git a/dx/tests/030-minimal-jasmin/expected.txt b/dx/tests/030-minimal-jasmin/expected.txt
new file mode 100644
index 0000000..de52b4d
--- /dev/null
+++ b/dx/tests/030-minimal-jasmin/expected.txt
@@ -0,0 +1,7 @@
+Generated: ./blort.class
+ 0000: aload_0 // 00
+ 0001: dup
+ 0002: dup
+ 0003: astore_1 // 01
+ 0004: pop2
+ 0005: return
diff --git a/dx/tests/030-minimal-jasmin/info.txt b/dx/tests/030-minimal-jasmin/info.txt
new file mode 100644
index 0000000..816c244
--- /dev/null
+++ b/dx/tests/030-minimal-jasmin/info.txt
@@ -0,0 +1,2 @@
+This test is just a minimal test involving assembling a jasmin source
+file and then dumping it. It doesn't test any features in particular.
diff --git a/dx/tests/030-minimal-jasmin/run b/dx/tests/030-minimal-jasmin/run
new file mode 100644
index 0000000..6a50596
--- /dev/null
+++ b/dx/tests/030-minimal-jasmin/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+jasmin -d . blort.j
+dx --debug --dump --width=200 blort.class | grep ' 000.:'
diff --git a/dx/tests/031-bb-dead-code/blort.j b/dx/tests/031-bb-dead-code/blort.j
new file mode 100644
index 0000000..b6854f2
--- /dev/null
+++ b/dx/tests/031-bb-dead-code/blort.j
@@ -0,0 +1,182 @@
+; Copyright (C) 2007 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class blort
+.super java/lang/Object
+
+.method public <init>()V
+ .limit locals 1
+
+ aload_0
+ invokespecial java/lang/Object/<init>()V
+ return
+.end method
+
+; dead code after the last reachable instruction in a method
+.method public test_deadend1()V
+ return
+ aload_0
+.end method
+
+; dead code after the last reachable instruction in a method
+.method public test_deadend2()V
+ ireturn
+ aload_0
+ aload_0
+.end method
+
+; dead code after the last reachable instruction in a method
+.method public test_deadend3()V
+ aconst_null
+ athrow
+ sipush 0x1234
+.end method
+
+; make sure an exception handler for a dead range doesn't get enlivened
+.method public test_dead_exception_handler()V
+ return
+ nop
+blort:
+ nop
+ nop
+ return
+handler:
+ nop
+ return
+ .catch all from blort to handler using handler
+.end method
+
+; dead code after goto instruction
+.method public test_dead_goto()V
+ goto blort
+ nop
+blort:
+ return
+.end method
+
+; dead code after ret instruction
+.method public test_dead_ret()V
+ ifeq blort
+ ret 0
+ iconst_m1
+blort:
+ return
+.end method
+
+; dead code after tableswitch instruction
+.method public test_dead_tableswitch()V
+ tableswitch 0x10
+ blort
+ default: blort
+ nop
+ nop
+ nop
+ aload_0
+ aload_1
+ aload_2
+ aload_3
+blort:
+ return
+.end method
+
+; dead code after lookupswitch instruction
+.method public test_dead_lookupswitch()V
+ lookupswitch
+ 0x10: blort
+ 0x20: blort
+ default: blort
+ ldc "WHYA REYO UREA DING THIS ?"
+blort:
+ return
+.end method
+
+; dead code after ireturn instruction
+.method public test_dead_ireturn()V
+ ifeq blort
+ ireturn
+ iconst_1
+blort:
+ return
+.end method
+
+; dead code after lreturn instruction
+.method public test_dead_lreturn()V
+ ifeq blort
+ lreturn
+ iconst_1
+blort:
+ return
+.end method
+
+; dead code after freturn instruction
+.method public test_dead_freturn()V
+ ifeq blort
+ freturn
+ iconst_1
+blort:
+ return
+.end method
+
+; dead code after dreturn instruction
+.method public test_dead_dreturn()V
+ ifeq blort
+ dreturn
+ iconst_1
+blort:
+ return
+.end method
+
+; dead code after areturn instruction
+.method public test_dead_areturn()V
+ ifeq blort
+ areturn
+ iconst_1
+blort:
+ return
+.end method
+
+; dead code after return instruction
+.method public test_dead_return()V
+ ifeq blort
+ return
+ iconst_1
+blort:
+ return
+.end method
+
+; dead code after athrow instruction
+.method public test_dead_athrow()V
+ ifeq blort
+ athrow
+ iconst_1
+blort:
+ return
+.end method
+
+; dead code after wide ret instruction
+.method public test_dead_wideret()V
+ ifeq blort
+ ret 0x0100
+ iconst_1
+blort:
+ return
+.end method
+
+; dead code after goto_w instruction
+.method public test_dead_goto_w()V
+ goto_w blort
+ iconst_1
+blort:
+ return
+.end method
diff --git a/dx/tests/031-bb-dead-code/expected.txt b/dx/tests/031-bb-dead-code/expected.txt
new file mode 100644
index 0000000..1cd4959
--- /dev/null
+++ b/dx/tests/031-bb-dead-code/expected.txt
@@ -0,0 +1,190 @@
+Generated: ./blort.class
+reading blort.class...
+method <init> ()V
+block 0000: 0000..0004
+ 0000: aload_0 // 00
+ 0001: invokespecial method{java.lang.Object.<init>:()V}
+ next 0004
+block 0004: 0004..0005
+ 0004: return
+ returns
+
+method test_deadend1 ()V
+block 0000: 0000..0001
+ 0000: return
+ returns
+dead code 0001..0002
+
+method test_deadend2 ()V
+block 0000: 0000..0001
+ 0000: ireturn
+ returns
+dead code 0001..0003
+
+method test_deadend3 ()V
+block 0000: 0000..0002
+ 0000: aconst_null
+ 0001: athrow
+ returns
+dead code 0002..0005
+
+method test_dead_exception_handler ()V
+block 0000: 0000..0001
+ 0000: return
+ returns
+dead code 0001..0007
+
+method test_dead_goto ()V
+block 0000: 0000..0003
+ 0000: goto 0004
+ next 0004
+dead code 0003..0004
+block 0004: 0004..0005
+ 0004: return
+ returns
+
+method test_dead_ret ()V
+block 0000: 0000..0003
+ 0000: ifeq 0006
+ next 0003
+ next 0006
+block 0003: 0003..0005
+ 0003: ret 00
+ returns
+dead code 0005..0006
+block 0006: 0006..0007
+ 0006: return
+ returns
+
+method test_dead_tableswitch ()V
+block 0000: 0000..0014
+ 0000: tableswitch
+ default: 001b
+ next 001b
+dead code 0014..001b
+block 001b: 001b..001c
+ 001b: return
+ returns
+
+method test_dead_lookupswitch ()V
+block 0000: 0000..001c
+ 0000: lookupswitch
+ default: 001e
+ next 001e
+dead code 001c..001e
+block 001e: 001e..001f
+ 001e: return
+ returns
+
+method test_dead_ireturn ()V
+block 0000: 0000..0003
+ 0000: ifeq 0005
+ next 0003
+ next 0005
+block 0003: 0003..0004
+ 0003: ireturn
+ returns
+dead code 0004..0005
+block 0005: 0005..0006
+ 0005: return
+ returns
+
+method test_dead_lreturn ()V
+block 0000: 0000..0003
+ 0000: ifeq 0005
+ next 0003
+ next 0005
+block 0003: 0003..0004
+ 0003: lreturn
+ returns
+dead code 0004..0005
+block 0005: 0005..0006
+ 0005: return
+ returns
+
+method test_dead_freturn ()V
+block 0000: 0000..0003
+ 0000: ifeq 0005
+ next 0003
+ next 0005
+block 0003: 0003..0004
+ 0003: freturn
+ returns
+dead code 0004..0005
+block 0005: 0005..0006
+ 0005: return
+ returns
+
+method test_dead_dreturn ()V
+block 0000: 0000..0003
+ 0000: ifeq 0005
+ next 0003
+ next 0005
+block 0003: 0003..0004
+ 0003: dreturn
+ returns
+dead code 0004..0005
+block 0005: 0005..0006
+ 0005: return
+ returns
+
+method test_dead_areturn ()V
+block 0000: 0000..0003
+ 0000: ifeq 0005
+ next 0003
+ next 0005
+block 0003: 0003..0004
+ 0003: areturn
+ returns
+dead code 0004..0005
+block 0005: 0005..0006
+ 0005: return
+ returns
+
+method test_dead_return ()V
+block 0000: 0000..0003
+ 0000: ifeq 0005
+ next 0003
+ next 0005
+block 0003: 0003..0004
+ 0003: return
+ returns
+dead code 0004..0005
+block 0005: 0005..0006
+ 0005: return
+ returns
+
+method test_dead_athrow ()V
+block 0000: 0000..0003
+ 0000: ifeq 0005
+ next 0003
+ next 0005
+block 0003: 0003..0004
+ 0003: athrow
+ returns
+dead code 0004..0005
+block 0005: 0005..0006
+ 0005: return
+ returns
+
+method test_dead_wideret ()V
+block 0000: 0000..0003
+ 0000: ifeq 0008
+ next 0003
+ next 0008
+block 0003: 0003..0007
+ 0003: wide ret 0100
+ returns
+dead code 0007..0008
+block 0008: 0008..0009
+ 0008: return
+ returns
+
+method test_dead_goto_w ()V
+block 0000: 0000..0005
+ 0000: goto_w 00000006
+ next 0006
+dead code 0005..0006
+block 0006: 0006..0007
+ 0006: return
+ returns
diff --git a/dx/tests/031-bb-dead-code/info.txt b/dx/tests/031-bb-dead-code/info.txt
new file mode 100644
index 0000000..d5aa398
--- /dev/null
+++ b/dx/tests/031-bb-dead-code/info.txt
@@ -0,0 +1,3 @@
+This test checks to see that the basic block recognizer properly omits
+dead code. There is at least one example of dead code after each instruction
+that *doesn't* flow to the next instruction.
diff --git a/dx/tests/031-bb-dead-code/run b/dx/tests/031-bb-dead-code/run
new file mode 100644
index 0000000..4f82e15
--- /dev/null
+++ b/dx/tests/031-bb-dead-code/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+jasmin -d . blort.j
+dx --debug --dump --basic-blocks --width=200 blort.class
diff --git a/dx/tests/032-bb-live-code/blort.j b/dx/tests/032-bb-live-code/blort.j
new file mode 100644
index 0000000..05790bb
--- /dev/null
+++ b/dx/tests/032-bb-live-code/blort.j
@@ -0,0 +1,342 @@
+; Copyright (C) 2007 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class blort
+.super java/lang/Object
+
+.method public <init>()V
+ .limit locals 1
+
+ aload_0
+ invokespecial java/lang/Object/<init>()V
+ return
+.end method
+
+; Test that an exception handler for a live range is enlivened.
+.method public test_live_exception([I)V
+ nop
+ nop
+start:
+ aload_0
+ arraylength
+end1:
+ nop
+end2:
+ return
+handler1:
+ return
+handler2:
+ return
+ .catch java/lang/RuntimeException from start to end2 using handler2
+ .catch all from start to end1 using handler1
+.end method
+
+; Test that an exception handler for a live range is dead as long as
+; the covered code can't possibly throw.
+.method public test_dead_exception()V
+ nop
+ nop
+start:
+ nop
+end1:
+ nop
+end2:
+ return
+handler1:
+ return
+handler2:
+ return
+ .catch java/lang/RuntimeException from start to end2 using handler2
+ .catch all from start to end1 using handler1
+.end method
+
+; Test all the if* variants.
+.method public test_ifs()V
+ ifeq x0
+ ifne x1
+ iflt x2
+ ifge x3
+ ifgt x4
+ ifle x5
+ if_icmpeq x6
+ if_icmpne x7
+ if_icmplt x8
+ if_icmpge x9
+ if_icmpgt x10
+ if_icmple x11
+ if_acmpeq x12
+ if_acmpne x13
+ ifnull x14
+ ifnonnull x15
+ return
+x0:
+ return
+x1:
+ return
+x2:
+ return
+x3:
+ return
+x4:
+ return
+x5:
+ return
+x6:
+ return
+x7:
+ return
+x8:
+ return
+x9:
+ return
+x10:
+ return
+x11:
+ return
+x12:
+ return
+x13:
+ return
+x14:
+ return
+x15:
+ return
+.end method
+
+; Test jsr and jsr_w.
+.method public test_jsr()V
+ jsr j1
+ jsr_w j2
+ return
+j1:
+ astore_0
+ ret 0
+j2:
+ astore_0
+ ret_w 0
+.end method
+
+; Test tableswitch.
+.method public test_tableswitch()V
+ tableswitch 0x10
+ t1
+ t2
+ default: t3
+t1:
+ return
+t2:
+ return
+t3:
+ return
+.end method
+
+; Test lookupswitch.
+.method public test_lookupswitch()V
+ lookupswitch
+ 0x05: s1
+ 0x10: s2
+ default: s3
+s1:
+ return
+s2:
+ return
+s3:
+ return
+.end method
+
+; Test every non-branching op.
+.method public test_nonbranch()V
+ nop
+ aconst_null
+ iconst_m1
+ iconst_0
+ iconst_1
+ iconst_2
+ iconst_3
+ iconst_4
+ iconst_5
+ lconst_0
+ lconst_1
+ fconst_0
+ fconst_1
+ fconst_2
+ dconst_0
+ dconst_1
+ bipush 0x10
+ sipush 0x1000
+ ldc "x"
+ ldc_w "y"
+ ldc2_w 3.0
+ iload 5
+ lload 5
+ fload 5
+ dload 5
+ aload 5
+ iload_0
+ iload_1
+ iload_2
+ iload_3
+ lload_0
+ lload_1
+ lload_2
+ lload_3
+ fload_0
+ fload_1
+ fload_2
+ fload_3
+ dload_0
+ dload_1
+ dload_2
+ dload_3
+ aload_0
+ aload_1
+ aload_2
+ aload_3
+ iaload
+ laload
+ faload
+ daload
+ aaload
+ baload
+ caload
+ saload
+ istore 5
+ lstore 5
+ fstore 5
+ dstore 5
+ astore 5
+ istore_0
+ istore_1
+ istore_2
+ istore_3
+ lstore_0
+ lstore_1
+ lstore_2
+ lstore_3
+ fstore_0
+ fstore_1
+ fstore_2
+ fstore_3
+ dstore_0
+ dstore_1
+ dstore_2
+ dstore_3
+ astore_0
+ astore_1
+ astore_2
+ astore_3
+ iastore
+ lastore
+ fastore
+ dastore
+ aastore
+ bastore
+ castore
+ sastore
+ pop
+ pop2
+ dup
+ dup_x1
+ dup_x2
+ dup2
+ dup2_x1
+ dup2_x2
+ swap
+ iadd
+ ladd
+ fadd
+ dadd
+ isub
+ lsub
+ fsub
+ dsub
+ imul
+ lmul
+ fmul
+ dmul
+ idiv
+ ldiv
+ fdiv
+ ddiv
+ irem
+ lrem
+ frem
+ drem
+ ineg
+ lneg
+ fneg
+ dneg
+ ishl
+ lshl
+ ishr
+ lshr
+ iushr
+ lushr
+ iand
+ land
+ ior
+ lor
+ ixor
+ lxor
+ iinc 5 0x10
+ i2l
+ i2f
+ i2d
+ l2i
+ l2f
+ l2d
+ f2i
+ f2l
+ f2d
+ d2i
+ d2l
+ d2f
+ i2b
+ i2c
+ i2s
+ lcmp
+ fcmpl
+ fcmpg
+ dcmpl
+ dcmpg
+ getstatic blort/x I
+ putstatic blort/x I
+ getfield blort/x I
+ putfield blort/x I
+ invokevirtual blort/x()V
+ invokespecial blort/x()V
+ invokestatic blort/x()V
+ invokeinterface blort/x()V 1
+ new blort
+ newarray int
+ anewarray blort
+ arraylength
+ checkcast blort
+ instanceof blort
+ monitorenter
+ monitorexit
+ iload 0x100
+ lload 0x100
+ fload 0x100
+ dload 0x100
+ aload 0x100
+ istore 0x100
+ lstore 0x100
+ fstore 0x100
+ dstore 0x100
+ astore 0x100
+ iinc 0x123 0x321
+ multianewarray [[[I 2
+ return
+.end method
diff --git a/dx/tests/032-bb-live-code/expected.txt b/dx/tests/032-bb-live-code/expected.txt
new file mode 100644
index 0000000..26444af
--- /dev/null
+++ b/dx/tests/032-bb-live-code/expected.txt
@@ -0,0 +1,497 @@
+Generated: ./blort.class
+reading blort.class...
+method <init> ()V
+block 0000: 0000..0004
+ 0000: aload_0 // 00
+ 0001: invokespecial method{java.lang.Object.<init>:()V}
+ next 0004
+block 0004: 0004..0005
+ 0004: return
+ returns
+
+method test_live_exception ([I)V
+block 0000: 0000..0002
+ 0000: nop
+ 0001: nop
+ next 0002
+block 0002: 0002..0004
+ 0002: aload_0 // 00
+ 0003: arraylength
+ next 0007
+ next 0006
+ next 0004
+ catch java.lang.RuntimeException -> 0007
+ catch <any> -> 0006
+block 0004: 0004..0005
+ 0004: nop
+ next 0005
+block 0005: 0005..0006
+ 0005: return
+ returns
+block 0006: 0006..0007
+ 0006: return
+ returns
+block 0007: 0007..0008
+ 0007: return
+ returns
+
+method test_dead_exception ()V
+block 0000: 0000..0002
+ 0000: nop
+ 0001: nop
+ next 0002
+block 0002: 0002..0003
+ 0002: nop
+ next 0003
+block 0003: 0003..0004
+ 0003: nop
+ next 0004
+block 0004: 0004..0005
+ 0004: return
+ returns
+block 0005: 0005..0006
+ 0005: return
+ returns
+block 0006: 0006..0007
+ 0006: return
+ returns
+
+method test_ifs ()V
+block 0000: 0000..0003
+ 0000: ifeq 0031
+ next 0003
+ next 0031
+block 0003: 0003..0006
+ 0003: ifne 0032
+ next 0006
+ next 0032
+block 0006: 0006..0009
+ 0006: iflt 0033
+ next 0009
+ next 0033
+block 0009: 0009..000c
+ 0009: ifge 0034
+ next 000c
+ next 0034
+block 000c: 000c..000f
+ 000c: ifgt 0035
+ next 000f
+ next 0035
+block 000f: 000f..0012
+ 000f: ifle 0036
+ next 0012
+ next 0036
+block 0012: 0012..0015
+ 0012: if_icmpeq 0037
+ next 0015
+ next 0037
+block 0015: 0015..0018
+ 0015: if_icmpne 0038
+ next 0018
+ next 0038
+block 0018: 0018..001b
+ 0018: if_icmplt 0039
+ next 001b
+ next 0039
+block 001b: 001b..001e
+ 001b: if_icmpge 003a
+ next 001e
+ next 003a
+block 001e: 001e..0021
+ 001e: if_icmpgt 003b
+ next 0021
+ next 003b
+block 0021: 0021..0024
+ 0021: if_icmple 003c
+ next 0024
+ next 003c
+block 0024: 0024..0027
+ 0024: if_acmpeq 003d
+ next 0027
+ next 003d
+block 0027: 0027..002a
+ 0027: if_acmpne 003e
+ next 002a
+ next 003e
+block 002a: 002a..002d
+ 002a: ifnull 003f
+ next 002d
+ next 003f
+block 002d: 002d..0030
+ 002d: ifnonnull 0040
+ next 0030
+ next 0040
+block 0030: 0030..0031
+ 0030: return
+ returns
+block 0031: 0031..0032
+ 0031: return
+ returns
+block 0032: 0032..0033
+ 0032: return
+ returns
+block 0033: 0033..0034
+ 0033: return
+ returns
+block 0034: 0034..0035
+ 0034: return
+ returns
+block 0035: 0035..0036
+ 0035: return
+ returns
+block 0036: 0036..0037
+ 0036: return
+ returns
+block 0037: 0037..0038
+ 0037: return
+ returns
+block 0038: 0038..0039
+ 0038: return
+ returns
+block 0039: 0039..003a
+ 0039: return
+ returns
+block 003a: 003a..003b
+ 003a: return
+ returns
+block 003b: 003b..003c
+ 003b: return
+ returns
+block 003c: 003c..003d
+ 003c: return
+ returns
+block 003d: 003d..003e
+ 003d: return
+ returns
+block 003e: 003e..003f
+ 003e: return
+ returns
+block 003f: 003f..0040
+ 003f: return
+ returns
+block 0040: 0040..0041
+ 0040: return
+ returns
+
+method test_jsr ()V
+block 0000: 0000..0003
+ 0000: jsr 0009
+ next 0003
+ next 0009
+block 0003: 0003..0008
+ 0003: jsr_w 0000000c
+ next 0008
+ next 000c
+block 0008: 0008..0009
+ 0008: return
+ returns
+block 0009: 0009..000c
+ 0009: astore_0 // 00
+ 000a: ret 00
+ returns
+block 000c: 000c..0011
+ 000c: astore_0 // 00
+ 000d: wide ret 0000
+ returns
+
+method test_tableswitch ()V
+block 0000: 0000..0018
+ 0000: tableswitch
+ +00000010: 0018
+ +00000011: 0019
+ default: 001a
+ next 0018
+ next 0019
+ next 001a
+block 0018: 0018..0019
+ 0018: return
+ returns
+block 0019: 0019..001a
+ 0019: return
+ returns
+block 001a: 001a..001b
+ 001a: return
+ returns
+
+method test_lookupswitch ()V
+block 0000: 0000..001c
+ 0000: lookupswitch
+ +00000005: 001c
+ +00000010: 001d
+ default: 001e
+ next 001c
+ next 001d
+ next 001e
+block 001c: 001c..001d
+ 001c: return
+ returns
+block 001d: 001d..001e
+ 001d: return
+ returns
+block 001e: 001e..001f
+ 001e: return
+ returns
+
+method test_nonbranch ()V
+block 0000: 0000..0017
+ 0000: nop
+ 0001: aconst_null
+ 0002: iconst_m1 // #-01
+ 0003: iconst_0 // #+00
+ 0004: iconst_1 // #+01
+ 0005: iconst_2 // #+02
+ 0006: iconst_3 // #+03
+ 0007: iconst_4 // #+04
+ 0008: iconst_5 // #+05
+ 0009: lconst_0 // +00
+ 000a: lconst_1 // +01
+ 000b: fconst_0 // 0.0
+ 000c: fconst_1 // 1.0
+ 000d: fconst_2 // 2.0
+ 000e: dconst_0 // 0.0
+ 000f: dconst_1 // 1.0
+ 0010: bipush #+10
+ 0012: sipush #+1000
+ 0015: ldc string{"x"}
+ next 0017
+block 0017: 0017..001a
+ 0017: ldc_w string{"y"}
+ next 001a
+block 001a: 001a..003c
+ 001a: ldc2_w #4008000000000000 // 3.0
+ 001d: iload 05
+ 001f: lload 05 // category-2
+ 0021: fload 05
+ 0023: dload 05 // category-2
+ 0025: aload 05
+ 0027: iload_0 // 00
+ 0028: iload_1 // 01
+ 0029: iload_2 // 02
+ 002a: iload_3 // 03
+ 002b: lload_0 // 00, category-2
+ 002c: lload_1 // 01, category-2
+ 002d: lload_2 // 02, category-2
+ 002e: lload_3 // 03, category-2
+ 002f: fload_0 // 00
+ 0030: fload_1 // 01
+ 0031: fload_2 // 02
+ 0032: fload_3 // 03
+ 0033: dload_0 // 00, category-2
+ 0034: dload_1 // 01, category-2
+ 0035: dload_2 // 02, category-2
+ 0036: dload_3 // 03, category-2
+ 0037: aload_0 // 00
+ 0038: aload_1 // 01
+ 0039: aload_2 // 02
+ 003a: aload_3 // 03
+ 003b: iaload
+ next 003c
+block 003c: 003c..003d
+ 003c: laload
+ next 003d
+block 003d: 003d..003e
+ 003d: faload
+ next 003e
+block 003e: 003e..003f
+ 003e: daload
+ next 003f
+block 003f: 003f..0040
+ 003f: aaload
+ next 0040
+block 0040: 0040..0041
+ 0040: baload
+ next 0041
+block 0041: 0041..0042
+ 0041: caload
+ next 0042
+block 0042: 0042..0043
+ 0042: saload
+ next 0043
+block 0043: 0043..0062
+ 0043: istore 05
+ 0045: lstore 05 // category-2
+ 0047: fstore 05
+ 0049: dstore 05 // category-2
+ 004b: astore 05
+ 004d: istore_0 // 00
+ 004e: istore_1 // 01
+ 004f: istore_2 // 02
+ 0050: istore_3 // 03
+ 0051: lstore_0 // 00, category-2
+ 0052: lstore_1 // 01, category-2
+ 0053: lstore_2 // 02, category-2
+ 0054: lstore_3 // 03, category-2
+ 0055: fstore_0 // 00
+ 0056: fstore_1 // 01
+ 0057: fstore_2 // 02
+ 0058: fstore_3 // 03
+ 0059: dstore_0 // 00, category-2
+ 005a: dstore_1 // 01, category-2
+ 005b: dstore_2 // 02, category-2
+ 005c: dstore_3 // 03, category-2
+ 005d: astore_0 // 00
+ 005e: astore_1 // 01
+ 005f: astore_2 // 02
+ 0060: astore_3 // 03
+ 0061: iastore
+ next 0062
+block 0062: 0062..0063
+ 0062: lastore
+ next 0063
+block 0063: 0063..0064
+ 0063: fastore
+ next 0064
+block 0064: 0064..0065
+ 0064: dastore
+ next 0065
+block 0065: 0065..0066
+ 0065: aastore
+ next 0066
+block 0066: 0066..0067
+ 0066: bastore
+ next 0067
+block 0067: 0067..0068
+ 0067: castore
+ next 0068
+block 0068: 0068..0069
+ 0068: sastore
+ next 0069
+block 0069: 0069..007f
+ 0069: pop
+ 006a: pop2
+ 006b: dup
+ 006c: dup_x1
+ 006d: dup_x2
+ 006e: dup2
+ 006f: dup2_x1
+ 0070: dup2_x2
+ 0071: swap
+ 0072: iadd
+ 0073: ladd
+ 0074: fadd
+ 0075: dadd
+ 0076: isub
+ 0077: lsub
+ 0078: fsub
+ 0079: dsub
+ 007a: imul
+ 007b: lmul
+ 007c: fmul
+ 007d: dmul
+ 007e: idiv
+ next 007f
+block 007f: 007f..0080
+ 007f: ldiv
+ next 0080
+block 0080: 0080..0083
+ 0080: fdiv
+ 0081: ddiv
+ 0082: irem
+ next 0083
+block 0083: 0083..0084
+ 0083: lrem
+ next 0084
+block 0084: 0084..00b0
+ 0084: frem
+ 0085: drem
+ 0086: ineg
+ 0087: lneg
+ 0088: fneg
+ 0089: dneg
+ 008a: ishl
+ 008b: lshl
+ 008c: ishr
+ 008d: lshr
+ 008e: iushr
+ 008f: lushr
+ 0090: iand
+ 0091: land
+ 0092: ior
+ 0093: lor
+ 0094: ixor
+ 0095: lxor
+ 0096: iinc 05, #+10
+ 0099: i2l
+ 009a: i2f
+ 009b: i2d
+ 009c: l2i
+ 009d: l2f
+ 009e: l2d
+ 009f: f2i
+ 00a0: f2l
+ 00a1: f2d
+ 00a2: d2i
+ 00a3: d2l
+ 00a4: d2f
+ 00a5: i2b
+ 00a6: i2c
+ 00a7: i2s
+ 00a8: lcmp
+ 00a9: fcmpl
+ 00aa: fcmpg
+ 00ab: dcmpl
+ 00ac: dcmpg
+ 00ad: getstatic field{blort.x:I}
+ next 00b0
+block 00b0: 00b0..00b3
+ 00b0: putstatic field{blort.x:I}
+ next 00b3
+block 00b3: 00b3..00b6
+ 00b3: getfield field{blort.x:I}
+ next 00b6
+block 00b6: 00b6..00b9
+ 00b6: putfield field{blort.x:I}
+ next 00b9
+block 00b9: 00b9..00bc
+ 00b9: invokevirtual method{blort.x:()V}
+ next 00bc
+block 00bc: 00bc..00bf
+ 00bc: invokespecial method{blort.x:()V}
+ next 00bf
+block 00bf: 00bf..00c2
+ 00bf: invokestatic method{blort.x:()V}
+ next 00c2
+block 00c2: 00c2..00c7
+ 00c2: invokeinterface ifaceMethod{blort.x:()V}, 0001
+ next 00c7
+block 00c7: 00c7..00ca
+ 00c7: new type{blort}
+ next 00ca
+block 00ca: 00ca..00cc
+ 00ca: newarray int
+ next 00cc
+block 00cc: 00cc..00cf
+ 00cc: anewarray type{blort}
+ next 00cf
+block 00cf: 00cf..00d0
+ 00cf: arraylength
+ next 00d0
+block 00d0: 00d0..00d3
+ 00d0: checkcast type{blort}
+ next 00d3
+block 00d3: 00d3..00d6
+ 00d3: instanceof type{blort}
+ next 00d6
+block 00d6: 00d6..00d7
+ 00d6: monitorenter
+ next 00d7
+block 00d7: 00d7..00d8
+ 00d7: monitorexit
+ next 00d8
+block 00d8: 00d8..010a
+ 00d8: wide iload 0100
+ 00dc: wide lload 0100 // category-2
+ 00e0: wide fload 0100
+ 00e4: wide dload 0100 // category-2
+ 00e8: wide aload 0100
+ 00ec: wide istore 0100
+ 00f0: wide lstore 0100 // category-2
+ 00f4: wide fstore 0100
+ 00f8: wide dstore 0100 // category-2
+ 00fc: wide astore 0100
+ 0100: wide iinc 0123, #+0321
+ 0106: multianewarray type{int[][][]}, 02
+ next 010a
+block 010a: 010a..010b
+ 010a: return
+ returns
diff --git a/dx/tests/032-bb-live-code/info.txt b/dx/tests/032-bb-live-code/info.txt
new file mode 100644
index 0000000..f94b435
--- /dev/null
+++ b/dx/tests/032-bb-live-code/info.txt
@@ -0,0 +1,5 @@
+This test checks to see that the basic block recognizer properly
+includes as live code all code which could possibly be flowed
+to. There is at least one example of each instruction which allows
+flow to the subsequent instruction, and all forks of each conditional
+branch are checked for liveness as well.
diff --git a/dx/tests/032-bb-live-code/run b/dx/tests/032-bb-live-code/run
new file mode 100644
index 0000000..4f82e15
--- /dev/null
+++ b/dx/tests/032-bb-live-code/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+jasmin -d . blort.j
+dx --debug --dump --basic-blocks --width=200 blort.class
diff --git a/dx/tests/033-unit-IntList/expected.txt b/dx/tests/033-unit-IntList/expected.txt
new file mode 100644
index 0000000..5418338
--- /dev/null
+++ b/dx/tests/033-unit-IntList/expected.txt
@@ -0,0 +1 @@
+Yay!
diff --git a/dx/tests/033-unit-IntList/info.txt b/dx/tests/033-unit-IntList/info.txt
new file mode 100644
index 0000000..7d7e8aa
--- /dev/null
+++ b/dx/tests/033-unit-IntList/info.txt
@@ -0,0 +1 @@
+Unit test for com.android.dx.util.IntList.
diff --git a/dx/tests/033-unit-IntList/run b/dx/tests/033-unit-IntList/run
new file mode 100644
index 0000000..16ca6fb
--- /dev/null
+++ b/dx/tests/033-unit-IntList/run
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --junit com.android.dx.util._tests._IntList > unit-out.txt
+
+if [ "$?" = "0" ]; then
+ echo "Yay!"
+else
+ cat unit-out.txt
+fi
diff --git a/dx/tests/034-dex-minimal/blort.j b/dx/tests/034-dex-minimal/blort.j
new file mode 100644
index 0000000..b4715b2
--- /dev/null
+++ b/dx/tests/034-dex-minimal/blort.j
@@ -0,0 +1,16 @@
+; Copyright (C) 2007 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class blort
+.super java/lang/Object
diff --git a/dx/tests/034-dex-minimal/expected.txt b/dx/tests/034-dex-minimal/expected.txt
new file mode 100644
index 0000000..a8f81c8
--- /dev/null
+++ b/dx/tests/034-dex-minimal/expected.txt
@@ -0,0 +1,70 @@
+000000: 6465 780a 3033|magic: "dex\n035\0"
+000006: 3500 |
+000008: be0b 70d9 |checksum
+00000c: 1d9c 3f88 730d|signature
+000012: 0ed6 caa3 77d4|
+000018: 5204 65e7 322d|
+00001e: 365a |
+000020: 8c00 0000 |file_size: 0000008c
+000024: 7000 0000 |header_size: 00000070
+000028: 7856 3412 |endian_tag: 12345678
+00002c: 0000 0000 |link_size: 0
+000030: 0000 0000 |link_off: 0
+000034: 7000 0000 |map_off: 00000070
+000038: 0000 0000 |string_ids_size: 00000000
+00003c: 0000 0000 |string_ids_off: 00000000
+000040: 0000 0000 |type_ids_size: 00000000
+000044: 0000 0000 |type_ids_off: 00000000
+000048: 0000 0000 |proto_ids_size: 00000000
+00004c: 0000 0000 |proto_ids_off: 00000000
+000050: 0000 0000 |field_ids_size: 00000000
+000054: 0000 0000 |field_ids_off: 00000000
+000058: 0000 0000 |method_ids_size: 00000000
+00005c: 0000 0000 |method_ids_off: 00000000
+000060: 0000 0000 |class_defs_size: 00000000
+000064: 0000 0000 |class_defs_off: 00000000
+000068: 1c00 0000 |data_size: 0000001c
+00006c: 7000 0000 |data_off: 00000070
+ |
+ |string_ids:
+ |
+ |type_ids:
+ |
+ |proto_ids:
+ |
+ |field_ids:
+ |
+ |method_ids:
+ |
+ |class_defs:
+ |
+ |word_data:
+ |
+ |
+ |string_data:
+ |
+ |byte_data:
+ |
+ |
+ |map:
+ |[70] map list
+000070: 0200 0000 | size: 00000002
+ |[74] header_item map
+000074: 0000 | type: 0000 // TYPE_HEADER_ITEM
+000076: 0000 | unused: 0
+000078: 0100 0000 | size: 00000001
+00007c: 0000 0000 | offset: 00000000
+ |[80] map_list map
+000080: 0010 | type: 1000 // TYPE_MAP_LIST
+000082: 0000 | unused: 0
+000084: 0100 0000 | size: 00000001
+000088: 7000 0000 | offset: 00000070
+ |
+ |statistics:
+ | header: 1 item; 112 bytes total
+ | 112 bytes/item
+ | map list: 1 item; 28 bytes total
+ | 28 bytes/item
+
+processing blort.class...
+Good!
diff --git a/dx/tests/034-dex-minimal/info.txt b/dx/tests/034-dex-minimal/info.txt
new file mode 100644
index 0000000..13bb9ab
--- /dev/null
+++ b/dx/tests/034-dex-minimal/info.txt
@@ -0,0 +1,9 @@
+This is a smoke test of a couple cases of minimal dex creation:
+
+The first test tries to create the truly minimal dex file by using the
+--no-files option. The output dump of this is checked to make sure it
+is exactly correct. (There is arguably only one "right" answer.)
+
+The second test tries to convert a minimal classfile and ensures that
+the conversion runs without failure, though the contents of the
+converted file are not checked for correctness.
diff --git a/dx/tests/034-dex-minimal/run b/dx/tests/034-dex-minimal/run
new file mode 100644
index 0000000..bf90624
--- /dev/null
+++ b/dx/tests/034-dex-minimal/run
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dex --dump-to=- --no-files
+echo ""
+
+jasmin -d . blort.j >/dev/null
+dx --verbose --debug --dex --output=blort.dex blort.class
+if [ -r blort.dex ]; then
+ echo Good!
+fi
diff --git a/dx/tests/035-dex-instance-var/blort.j b/dx/tests/035-dex-instance-var/blort.j
new file mode 100644
index 0000000..1bcdf8e
--- /dev/null
+++ b/dx/tests/035-dex-instance-var/blort.j
@@ -0,0 +1,18 @@
+; Copyright (C) 2007 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class blort
+.super java/lang/Object
+
+.field public x I
diff --git a/dx/tests/035-dex-instance-var/expected.txt b/dx/tests/035-dex-instance-var/expected.txt
new file mode 100644
index 0000000..a1db6a4
--- /dev/null
+++ b/dx/tests/035-dex-instance-var/expected.txt
@@ -0,0 +1 @@
+Good!
diff --git a/dx/tests/035-dex-instance-var/info.txt b/dx/tests/035-dex-instance-var/info.txt
new file mode 100644
index 0000000..294c9eb
--- /dev/null
+++ b/dx/tests/035-dex-instance-var/info.txt
@@ -0,0 +1,4 @@
+This is a smoke test of dex conversion, which ensures that the
+conversion runs without failure, though the contents of the converted
+file are not checked for correctness. This test is of a classfile with
+just an instance variable.
diff --git a/dx/tests/035-dex-instance-var/run b/dx/tests/035-dex-instance-var/run
new file mode 100644
index 0000000..d7aa973
--- /dev/null
+++ b/dx/tests/035-dex-instance-var/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+jasmin -d . blort.j >/dev/null
+dx --debug --dex --output=blort.dex blort.class
+if [ -r blort.dex ]; then
+ echo Good!
+fi
diff --git a/dx/tests/036-dex-static-var/blort.j b/dx/tests/036-dex-static-var/blort.j
new file mode 100644
index 0000000..260f446
--- /dev/null
+++ b/dx/tests/036-dex-static-var/blort.j
@@ -0,0 +1,18 @@
+; Copyright (C) 2007 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class blort
+.super java/lang/Object
+
+.field public static x I
diff --git a/dx/tests/036-dex-static-var/expected.txt b/dx/tests/036-dex-static-var/expected.txt
new file mode 100644
index 0000000..a1db6a4
--- /dev/null
+++ b/dx/tests/036-dex-static-var/expected.txt
@@ -0,0 +1 @@
+Good!
diff --git a/dx/tests/036-dex-static-var/info.txt b/dx/tests/036-dex-static-var/info.txt
new file mode 100644
index 0000000..5c562eb
--- /dev/null
+++ b/dx/tests/036-dex-static-var/info.txt
@@ -0,0 +1,4 @@
+This is a smoke test of dex conversion, which ensures that the
+conversion runs without failure, though the contents of the converted
+file are not checked for correctness. This test is of a classfile with
+just a static variable.
diff --git a/dx/tests/036-dex-static-var/run b/dx/tests/036-dex-static-var/run
new file mode 100644
index 0000000..d7aa973
--- /dev/null
+++ b/dx/tests/036-dex-static-var/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+jasmin -d . blort.j >/dev/null
+dx --debug --dex --output=blort.dex blort.class
+if [ -r blort.dex ]; then
+ echo Good!
+fi
diff --git a/dx/tests/037-dex-static-final-var/blort.j b/dx/tests/037-dex-static-final-var/blort.j
new file mode 100644
index 0000000..ac0332e
--- /dev/null
+++ b/dx/tests/037-dex-static-final-var/blort.j
@@ -0,0 +1,18 @@
+; Copyright (C) 2007 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class blort
+.super java/lang/Object
+
+.field public static final x I = 10
diff --git a/dx/tests/037-dex-static-final-var/expected.txt b/dx/tests/037-dex-static-final-var/expected.txt
new file mode 100644
index 0000000..a1db6a4
--- /dev/null
+++ b/dx/tests/037-dex-static-final-var/expected.txt
@@ -0,0 +1 @@
+Good!
diff --git a/dx/tests/037-dex-static-final-var/info.txt b/dx/tests/037-dex-static-final-var/info.txt
new file mode 100644
index 0000000..03d798f
--- /dev/null
+++ b/dx/tests/037-dex-static-final-var/info.txt
@@ -0,0 +1,4 @@
+This is a smoke test of dex conversion, which ensures that the
+conversion runs without failure, though the contents of the converted
+file are not checked for correctness. This test is of a classfile with
+just a final static variable.
diff --git a/dx/tests/037-dex-static-final-var/run b/dx/tests/037-dex-static-final-var/run
new file mode 100644
index 0000000..d7aa973
--- /dev/null
+++ b/dx/tests/037-dex-static-final-var/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+jasmin -d . blort.j >/dev/null
+dx --debug --dex --output=blort.dex blort.class
+if [ -r blort.dex ]; then
+ echo Good!
+fi
diff --git a/dx/tests/038-dex-instance-method/blort.j b/dx/tests/038-dex-instance-method/blort.j
new file mode 100644
index 0000000..7ddd7b5
--- /dev/null
+++ b/dx/tests/038-dex-instance-method/blort.j
@@ -0,0 +1,21 @@
+; Copyright (C) 2007 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class blort
+.super java/lang/Object
+
+.method public blort()V
+ nop
+ return
+.end method
diff --git a/dx/tests/038-dex-instance-method/expected.txt b/dx/tests/038-dex-instance-method/expected.txt
new file mode 100644
index 0000000..a1db6a4
--- /dev/null
+++ b/dx/tests/038-dex-instance-method/expected.txt
@@ -0,0 +1 @@
+Good!
diff --git a/dx/tests/038-dex-instance-method/info.txt b/dx/tests/038-dex-instance-method/info.txt
new file mode 100644
index 0000000..6aa93c3
--- /dev/null
+++ b/dx/tests/038-dex-instance-method/info.txt
@@ -0,0 +1,4 @@
+This is a smoke test of dex conversion, which ensures that the
+conversion runs without failure, though the contents of the converted
+file are not checked for correctness. This test is of a classfile with
+just an instance method.
diff --git a/dx/tests/038-dex-instance-method/run b/dx/tests/038-dex-instance-method/run
new file mode 100644
index 0000000..d7aa973
--- /dev/null
+++ b/dx/tests/038-dex-instance-method/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+jasmin -d . blort.j >/dev/null
+dx --debug --dex --output=blort.dex blort.class
+if [ -r blort.dex ]; then
+ echo Good!
+fi
diff --git a/dx/tests/039-dex-static-method/blort.j b/dx/tests/039-dex-static-method/blort.j
new file mode 100644
index 0000000..5b74c57
--- /dev/null
+++ b/dx/tests/039-dex-static-method/blort.j
@@ -0,0 +1,21 @@
+; Copyright (C) 2007 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class blort
+.super java/lang/Object
+
+.method public static blort()V
+ nop
+ return
+.end method
diff --git a/dx/tests/039-dex-static-method/expected.txt b/dx/tests/039-dex-static-method/expected.txt
new file mode 100644
index 0000000..a1db6a4
--- /dev/null
+++ b/dx/tests/039-dex-static-method/expected.txt
@@ -0,0 +1 @@
+Good!
diff --git a/dx/tests/039-dex-static-method/info.txt b/dx/tests/039-dex-static-method/info.txt
new file mode 100644
index 0000000..59c8d6e
--- /dev/null
+++ b/dx/tests/039-dex-static-method/info.txt
@@ -0,0 +1,4 @@
+This is a smoke test of dex conversion, which ensures that the
+conversion runs without failure, though the contents of the converted
+file are not checked for correctness. This test is of a classfile with
+just a static method.
diff --git a/dx/tests/039-dex-static-method/run b/dx/tests/039-dex-static-method/run
new file mode 100644
index 0000000..d7aa973
--- /dev/null
+++ b/dx/tests/039-dex-static-method/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+jasmin -d . blort.j >/dev/null
+dx --debug --dex --output=blort.dex blort.class
+if [ -r blort.dex ]; then
+ echo Good!
+fi
diff --git a/dx/tests/040-dex-constructor/blort.j b/dx/tests/040-dex-constructor/blort.j
new file mode 100644
index 0000000..e0ae441
--- /dev/null
+++ b/dx/tests/040-dex-constructor/blort.j
@@ -0,0 +1,21 @@
+; Copyright (C) 2007 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class blort
+.super java/lang/Object
+
+.method public <init>()V
+ nop
+ return
+.end method
diff --git a/dx/tests/040-dex-constructor/expected.txt b/dx/tests/040-dex-constructor/expected.txt
new file mode 100644
index 0000000..a1db6a4
--- /dev/null
+++ b/dx/tests/040-dex-constructor/expected.txt
@@ -0,0 +1 @@
+Good!
diff --git a/dx/tests/040-dex-constructor/info.txt b/dx/tests/040-dex-constructor/info.txt
new file mode 100644
index 0000000..65eef93
--- /dev/null
+++ b/dx/tests/040-dex-constructor/info.txt
@@ -0,0 +1,4 @@
+This is a smoke test of dex conversion, which ensures that the
+conversion runs without failure, though the contents of the converted
+file are not checked for correctness. This test is of a classfile with
+just a constructor.
diff --git a/dx/tests/040-dex-constructor/run b/dx/tests/040-dex-constructor/run
new file mode 100644
index 0000000..d7aa973
--- /dev/null
+++ b/dx/tests/040-dex-constructor/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+jasmin -d . blort.j >/dev/null
+dx --debug --dex --output=blort.dex blort.class
+if [ -r blort.dex ]; then
+ echo Good!
+fi
diff --git a/dx/tests/041-dex-abstract-method/blort.j b/dx/tests/041-dex-abstract-method/blort.j
new file mode 100644
index 0000000..8074fae
--- /dev/null
+++ b/dx/tests/041-dex-abstract-method/blort.j
@@ -0,0 +1,19 @@
+; Copyright (C) 2007 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class blort
+.super java/lang/Object
+
+.method public abstract blort()V
+.end method
diff --git a/dx/tests/041-dex-abstract-method/expected.txt b/dx/tests/041-dex-abstract-method/expected.txt
new file mode 100644
index 0000000..a1db6a4
--- /dev/null
+++ b/dx/tests/041-dex-abstract-method/expected.txt
@@ -0,0 +1 @@
+Good!
diff --git a/dx/tests/041-dex-abstract-method/info.txt b/dx/tests/041-dex-abstract-method/info.txt
new file mode 100644
index 0000000..e004388
--- /dev/null
+++ b/dx/tests/041-dex-abstract-method/info.txt
@@ -0,0 +1,4 @@
+This is a smoke test of dex conversion, which ensures that the
+conversion runs without failure, though the contents of the converted
+file are not checked for correctness. This test is of a classfile with
+just an abstract instance method.
diff --git a/dx/tests/041-dex-abstract-method/run b/dx/tests/041-dex-abstract-method/run
new file mode 100644
index 0000000..d7aa973
--- /dev/null
+++ b/dx/tests/041-dex-abstract-method/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+jasmin -d . blort.j >/dev/null
+dx --debug --dex --output=blort.dex blort.class
+if [ -r blort.dex ]; then
+ echo Good!
+fi
diff --git a/dx/tests/042-dex-ignore-result/Blort.java b/dx/tests/042-dex-ignore-result/Blort.java
new file mode 100644
index 0000000..2df4e66
--- /dev/null
+++ b/dx/tests/042-dex-ignore-result/Blort.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ static public int hello() {
+ return 10;
+ }
+
+ static public void ouch() {
+ hello();
+ hello();
+ }
+}
diff --git a/dx/tests/042-dex-ignore-result/expected.txt b/dx/tests/042-dex-ignore-result/expected.txt
new file mode 100644
index 0000000..a1db6a4
--- /dev/null
+++ b/dx/tests/042-dex-ignore-result/expected.txt
@@ -0,0 +1 @@
+Good!
diff --git a/dx/tests/042-dex-ignore-result/info.txt b/dx/tests/042-dex-ignore-result/info.txt
new file mode 100644
index 0000000..6289664
--- /dev/null
+++ b/dx/tests/042-dex-ignore-result/info.txt
@@ -0,0 +1,5 @@
+This is a smoke test of dex conversion, which ensures that the
+conversion runs without failure, though the contents of the converted
+file are not checked for correctness. This test is of the case of a
+method call to a method which returns a value, where that value is
+ignored.
diff --git a/dx/tests/042-dex-ignore-result/run b/dx/tests/042-dex-ignore-result/run
new file mode 100644
index 0000000..d035d09
--- /dev/null
+++ b/dx/tests/042-dex-ignore-result/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --output=blort.dex Blort.class
+if [ -r blort.dex ]; then
+ echo Good!
+fi
diff --git a/dx/tests/043-dex-two-classes/Blort.java b/dx/tests/043-dex-two-classes/Blort.java
new file mode 100644
index 0000000..235907d
--- /dev/null
+++ b/dx/tests/043-dex-two-classes/Blort.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ // This space intentionally left blank.
+}
diff --git a/dx/tests/043-dex-two-classes/Zorch.java b/dx/tests/043-dex-two-classes/Zorch.java
new file mode 100644
index 0000000..c9dc4eb
--- /dev/null
+++ b/dx/tests/043-dex-two-classes/Zorch.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Zorch
+{
+ // This space intentionally left blank.
+}
diff --git a/dx/tests/043-dex-two-classes/expected.txt b/dx/tests/043-dex-two-classes/expected.txt
new file mode 100644
index 0000000..e88eb13
--- /dev/null
+++ b/dx/tests/043-dex-two-classes/expected.txt
@@ -0,0 +1,3 @@
+processing Blort.class...
+processing Zorch.class...
+Good!
diff --git a/dx/tests/043-dex-two-classes/info.txt b/dx/tests/043-dex-two-classes/info.txt
new file mode 100644
index 0000000..785fb9b
--- /dev/null
+++ b/dx/tests/043-dex-two-classes/info.txt
@@ -0,0 +1,4 @@
+This is a smoke test of dex conversion, which ensures that the
+conversion runs without failure, though the contents of the converted
+file are not checked for correctness. This test just makes sure that
+an attempt to combine two classes into a .dex file succeeds.
diff --git a/dx/tests/043-dex-two-classes/run b/dx/tests/043-dex-two-classes/run
new file mode 100644
index 0000000..41d3a0b
--- /dev/null
+++ b/dx/tests/043-dex-two-classes/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java Zorch.java
+dx --debug --verbose --dex --output=blort.dex *.class
+if [ -r blort.dex ]; then
+ echo Good!
+fi
diff --git a/dx/tests/044-dex-math-ops/Blort.java b/dx/tests/044-dex-math-ops/Blort.java
new file mode 100644
index 0000000..75095b5
--- /dev/null
+++ b/dx/tests/044-dex-math-ops/Blort.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ private volatile int i;
+ private volatile long l;
+ private volatile float f;
+ private volatile double d;
+
+ public void blort(int i1, int i2) {
+ i = -i1;
+ i = ~i1;
+ i = i1 + i2;
+ i = i1 - i2;
+ i = i1 * i2;
+ i = i1 / i2;
+ i = i1 % i2;
+ i = i1 & i2;
+ i = i1 | i2;
+ i = i1 ^ i2;
+ i = i1 << i2;
+ i = i1 >> i2;
+ i = i1 >>> i2;
+ }
+
+ public void blort(long l1, long l2) {
+ l = -l1;
+ l = ~l1;
+ l = l1 + l2;
+ l = l1 - l2;
+ l = l1 * l2;
+ l = l1 / l2;
+ l = l1 % l2;
+ l = l1 & l2;
+ l = l1 | l2;
+ l = l1 ^ l2;
+ l = l1 << l2;
+ l = l1 >> l2;
+ l = l1 >>> l2;
+ }
+
+ public void blort(float f1, float f2) {
+ f = -f1;
+ f = f1 + f2;
+ f = f1 - f2;
+ f = f1 * f2;
+ f = f1 / f2;
+ f = f1 % f2;
+ }
+
+ public void blort(double d1, double d2) {
+ d = -d1;
+ d = d1 + d2;
+ d = d1 - d2;
+ d = d1 * d2;
+ d = d1 / d2;
+ d = d1 % d2;
+ }
+}
diff --git a/dx/tests/044-dex-math-ops/expected.txt b/dx/tests/044-dex-math-ops/expected.txt
new file mode 100644
index 0000000..f6f8b79
--- /dev/null
+++ b/dx/tests/044-dex-math-ops/expected.txt
@@ -0,0 +1,213 @@
+Blort.blort:(DD)V:
+regs: 000f; ins: 0005; outs: 0000
+ 0000: move-object v0, v10
+ 0001: move-wide v1, v11
+ 0002: move-wide v3, v13
+ 0003: move-object v5, v0
+ 0004: move-wide v6, v1
+ 0005: neg-double v6, v6
+ 0006: iput-wide v6, v5, Blort.d:D
+ 0008: move-object v5, v0
+ 0009: move-wide v6, v1
+ 000a: move-wide v8, v3
+ 000b: add-double/2addr v6, v8
+ 000c: iput-wide v6, v5, Blort.d:D
+ 000e: move-object v5, v0
+ 000f: move-wide v6, v1
+ 0010: move-wide v8, v3
+ 0011: sub-double/2addr v6, v8
+ 0012: iput-wide v6, v5, Blort.d:D
+ 0014: move-object v5, v0
+ 0015: move-wide v6, v1
+ 0016: move-wide v8, v3
+ 0017: mul-double/2addr v6, v8
+ 0018: iput-wide v6, v5, Blort.d:D
+ 001a: move-object v5, v0
+ 001b: move-wide v6, v1
+ 001c: move-wide v8, v3
+ 001d: div-double/2addr v6, v8
+ 001e: iput-wide v6, v5, Blort.d:D
+ 0020: move-object v5, v0
+ 0021: move-wide v6, v1
+ 0022: move-wide v8, v3
+ 0023: rem-double/2addr v6, v8
+ 0024: iput-wide v6, v5, Blort.d:D
+ 0026: return-void
+Blort.blort:(FF)V:
+regs: 0009; ins: 0003; outs: 0000
+ 0000: move-object v0, v6
+ 0001: move v1, v7
+ 0002: move v2, v8
+ 0003: move-object v3, v0
+ 0004: move v4, v1
+ 0005: neg-float v4, v4
+ 0006: iput v4, v3, Blort.f:F
+ 0008: move-object v3, v0
+ 0009: move v4, v1
+ 000a: move v5, v2
+ 000b: add-float/2addr v4, v5
+ 000c: iput v4, v3, Blort.f:F
+ 000e: move-object v3, v0
+ 000f: move v4, v1
+ 0010: move v5, v2
+ 0011: sub-float/2addr v4, v5
+ 0012: iput v4, v3, Blort.f:F
+ 0014: move-object v3, v0
+ 0015: move v4, v1
+ 0016: move v5, v2
+ 0017: mul-float/2addr v4, v5
+ 0018: iput v4, v3, Blort.f:F
+ 001a: move-object v3, v0
+ 001b: move v4, v1
+ 001c: move v5, v2
+ 001d: div-float/2addr v4, v5
+ 001e: iput v4, v3, Blort.f:F
+ 0020: move-object v3, v0
+ 0021: move v4, v1
+ 0022: move v5, v2
+ 0023: rem-float/2addr v4, v5
+ 0024: iput v4, v3, Blort.f:F
+ 0026: return-void
+Blort.blort:(II)V:
+regs: 0009; ins: 0003; outs: 0000
+ 0000: move-object v0, v6
+ 0001: move v1, v7
+ 0002: move v2, v8
+ 0003: move-object v3, v0
+ 0004: move v4, v1
+ 0005: neg-int v4, v4
+ 0006: iput v4, v3, Blort.i:I
+ 0008: move-object v3, v0
+ 0009: move v4, v1
+ 000a: const/4 v5, #int -1 // #f
+ 000b: xor-int/lit8 v4, v4, #int -1 // #ff
+ 000d: iput v4, v3, Blort.i:I
+ 000f: move-object v3, v0
+ 0010: move v4, v1
+ 0011: move v5, v2
+ 0012: add-int/2addr v4, v5
+ 0013: iput v4, v3, Blort.i:I
+ 0015: move-object v3, v0
+ 0016: move v4, v1
+ 0017: move v5, v2
+ 0018: sub-int/2addr v4, v5
+ 0019: iput v4, v3, Blort.i:I
+ 001b: move-object v3, v0
+ 001c: move v4, v1
+ 001d: move v5, v2
+ 001e: mul-int/2addr v4, v5
+ 001f: iput v4, v3, Blort.i:I
+ 0021: move-object v3, v0
+ 0022: move v4, v1
+ 0023: move v5, v2
+ 0024: div-int/2addr v4, v5
+ 0025: iput v4, v3, Blort.i:I
+ 0027: move-object v3, v0
+ 0028: move v4, v1
+ 0029: move v5, v2
+ 002a: rem-int/2addr v4, v5
+ 002b: iput v4, v3, Blort.i:I
+ 002d: move-object v3, v0
+ 002e: move v4, v1
+ 002f: move v5, v2
+ 0030: and-int/2addr v4, v5
+ 0031: iput v4, v3, Blort.i:I
+ 0033: move-object v3, v0
+ 0034: move v4, v1
+ 0035: move v5, v2
+ 0036: or-int/2addr v4, v5
+ 0037: iput v4, v3, Blort.i:I
+ 0039: move-object v3, v0
+ 003a: move v4, v1
+ 003b: move v5, v2
+ 003c: xor-int/2addr v4, v5
+ 003d: iput v4, v3, Blort.i:I
+ 003f: move-object v3, v0
+ 0040: move v4, v1
+ 0041: move v5, v2
+ 0042: shl-int/2addr v4, v5
+ 0043: iput v4, v3, Blort.i:I
+ 0045: move-object v3, v0
+ 0046: move v4, v1
+ 0047: move v5, v2
+ 0048: shr-int/2addr v4, v5
+ 0049: iput v4, v3, Blort.i:I
+ 004b: move-object v3, v0
+ 004c: move v4, v1
+ 004d: move v5, v2
+ 004e: ushr-int/2addr v4, v5
+ 004f: iput v4, v3, Blort.i:I
+ 0051: return-void
+Blort.blort:(JJ)V:
+regs: 000f; ins: 0005; outs: 0000
+ 0000: move-object v0, v10
+ 0001: move-wide v1, v11
+ 0002: move-wide v3, v13
+ 0003: move-object v5, v0
+ 0004: move-wide v6, v1
+ 0005: neg-long v6, v6
+ 0006: iput-wide v6, v5, Blort.l:J
+ 0008: move-object v5, v0
+ 0009: move-wide v6, v1
+ 000a: const-wide/16 v8, #long -1 // #ffff
+ 000c: xor-long/2addr v6, v8
+ 000d: iput-wide v6, v5, Blort.l:J
+ 000f: move-object v5, v0
+ 0010: move-wide v6, v1
+ 0011: move-wide v8, v3
+ 0012: add-long/2addr v6, v8
+ 0013: iput-wide v6, v5, Blort.l:J
+ 0015: move-object v5, v0
+ 0016: move-wide v6, v1
+ 0017: move-wide v8, v3
+ 0018: sub-long/2addr v6, v8
+ 0019: iput-wide v6, v5, Blort.l:J
+ 001b: move-object v5, v0
+ 001c: move-wide v6, v1
+ 001d: move-wide v8, v3
+ 001e: mul-long/2addr v6, v8
+ 001f: iput-wide v6, v5, Blort.l:J
+ 0021: move-object v5, v0
+ 0022: move-wide v6, v1
+ 0023: move-wide v8, v3
+ 0024: div-long/2addr v6, v8
+ 0025: iput-wide v6, v5, Blort.l:J
+ 0027: move-object v5, v0
+ 0028: move-wide v6, v1
+ 0029: move-wide v8, v3
+ 002a: rem-long/2addr v6, v8
+ 002b: iput-wide v6, v5, Blort.l:J
+ 002d: move-object v5, v0
+ 002e: move-wide v6, v1
+ 002f: move-wide v8, v3
+ 0030: and-long/2addr v6, v8
+ 0031: iput-wide v6, v5, Blort.l:J
+ 0033: move-object v5, v0
+ 0034: move-wide v6, v1
+ 0035: move-wide v8, v3
+ 0036: or-long/2addr v6, v8
+ 0037: iput-wide v6, v5, Blort.l:J
+ 0039: move-object v5, v0
+ 003a: move-wide v6, v1
+ 003b: move-wide v8, v3
+ 003c: xor-long/2addr v6, v8
+ 003d: iput-wide v6, v5, Blort.l:J
+ 003f: move-object v5, v0
+ 0040: move-wide v6, v1
+ 0041: move-wide v8, v3
+ 0042: long-to-int v8, v8
+ 0043: shl-long/2addr v6, v8
+ 0044: iput-wide v6, v5, Blort.l:J
+ 0046: move-object v5, v0
+ 0047: move-wide v6, v1
+ 0048: move-wide v8, v3
+ 0049: long-to-int v8, v8
+ 004a: shr-long/2addr v6, v8
+ 004b: iput-wide v6, v5, Blort.l:J
+ 004d: move-object v5, v0
+ 004e: move-wide v6, v1
+ 004f: move-wide v8, v3
+ 0050: long-to-int v8, v8
+ 0051: ushr-long/2addr v6, v8
+ 0052: iput-wide v6, v5, Blort.l:J
+ 0054: return-void
diff --git a/dx/tests/044-dex-math-ops/info.txt b/dx/tests/044-dex-math-ops/info.txt
new file mode 100644
index 0000000..733dcb6
--- /dev/null
+++ b/dx/tests/044-dex-math-ops/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+simple uses of all the math ops end up getting converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/044-dex-math-ops/run b/dx/tests/044-dex-math-ops/run
new file mode 100644
index 0000000..d8d4273
--- /dev/null
+++ b/dx/tests/044-dex-math-ops/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+ --dump-method=Blort.blort *.class
diff --git a/dx/tests/045-dex-switch-ops/Blort.java b/dx/tests/045-dex-switch-ops/Blort.java
new file mode 100644
index 0000000..598bd69
--- /dev/null
+++ b/dx/tests/045-dex-switch-ops/Blort.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ public int switchTest1(int x) {
+ switch (x) {
+ case 1: {
+ return 2;
+ }
+ case 2: {
+ return 3;
+ }
+ case 3: {
+ return 4;
+ }
+ case 4: {
+ return 5;
+ }
+ }
+
+ return 6;
+ }
+
+ public int switchTest2(int x) {
+ switch (x) {
+ case 1: {
+ return 2;
+ }
+ case 10: {
+ return 3;
+ }
+ case 100: {
+ return 4;
+ }
+ case 1000: {
+ return 50;
+ }
+ }
+
+ return 6;
+ }
+}
diff --git a/dx/tests/045-dex-switch-ops/expected.txt b/dx/tests/045-dex-switch-ops/expected.txt
new file mode 100644
index 0000000..46476ea
--- /dev/null
+++ b/dx/tests/045-dex-switch-ops/expected.txt
@@ -0,0 +1,53 @@
+Blort.switchTest1:(I)I:
+regs: 0005; ins: 0002; outs: 0000
+ 0000: move-object v0, v3
+ 0001: move v1, v4
+ 0002: move v2, v1
+ 0003: packed-switch v2, 0016 // +0013
+ 0006: const/4 v2, #int 6 // #6
+ 0007: move v0, v2
+ 0008: return v0
+ 0009: const/4 v2, #int 2 // #2
+ 000a: move v0, v2
+ 000b: goto 0008 // -0003
+ 000c: const/4 v2, #int 3 // #3
+ 000d: move v0, v2
+ 000e: goto 0008 // -0006
+ 000f: const/4 v2, #int 4 // #4
+ 0010: move v0, v2
+ 0011: goto 0008 // -0009
+ 0012: const/4 v2, #int 5 // #5
+ 0013: move v0, v2
+ 0014: goto 0008 // -000c
+ 0015: nop // spacer
+ 0016: packed-switch-data // for switch @ 0003
+ 1: 00000009 // +00000006
+ 2: 0000000c // +00000009
+ 3: 0000000f // +0000000c
+ 4: 00000012 // +0000000f
+Blort.switchTest2:(I)I:
+regs: 0005; ins: 0002; outs: 0000
+ 0000: move-object v0, v3
+ 0001: move v1, v4
+ 0002: move v2, v1
+ 0003: sparse-switch v2, 0016 // +0013
+ 0006: const/4 v2, #int 6 // #6
+ 0007: move v0, v2
+ 0008: return v0
+ 0009: const/4 v2, #int 2 // #2
+ 000a: move v0, v2
+ 000b: goto 0008 // -0003
+ 000c: const/4 v2, #int 3 // #3
+ 000d: move v0, v2
+ 000e: goto 0008 // -0006
+ 000f: const/4 v2, #int 4 // #4
+ 0010: move v0, v2
+ 0011: goto 0008 // -0009
+ 0012: const/16 v2, #int 50 // #0032
+ 0014: move v0, v2
+ 0015: goto 0008 // -000d
+ 0016: sparse-switch-data // for switch @ 0003
+ 1: 00000009 // +00000006
+ 10: 0000000c // +00000009
+ 100: 0000000f // +0000000c
+ 1000: 00000012 // +0000000f
diff --git a/dx/tests/045-dex-switch-ops/info.txt b/dx/tests/045-dex-switch-ops/info.txt
new file mode 100644
index 0000000..492dc0b
--- /dev/null
+++ b/dx/tests/045-dex-switch-ops/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+both kinds of switch op get converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/045-dex-switch-ops/run b/dx/tests/045-dex-switch-ops/run
new file mode 100644
index 0000000..8d1b65e
--- /dev/null
+++ b/dx/tests/045-dex-switch-ops/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+ --dump-method=Blort.switchTest'*' *.class
diff --git a/dx/tests/046-dex-exceptions/Blort.java b/dx/tests/046-dex-exceptions/Blort.java
new file mode 100644
index 0000000..a0b6c0b
--- /dev/null
+++ b/dx/tests/046-dex-exceptions/Blort.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ public static int maybeThrow(int x) {
+ if (x < 10) {
+ throw new RuntimeException();
+ }
+
+ return x;
+ }
+
+ public static int exTest1(int x) {
+ try {
+ maybeThrow(x);
+ return 1;
+ } catch (RuntimeException ex) {
+ return 2;
+ }
+ }
+
+ public static int exTest2(int x) {
+ try {
+ x++;
+ x = maybeThrow(x);
+ } catch (RuntimeException ex) {
+ return 1;
+ }
+
+ // Since the code in the try block can't possibly throw, there
+ // should not be a catch in the final result.
+ try {
+ x++;
+ } catch (RuntimeException ex) {
+ return 2;
+ }
+
+ try {
+ return maybeThrow(x);
+ } catch (RuntimeException ex) {
+ return 3;
+ }
+ }
+}
diff --git a/dx/tests/046-dex-exceptions/expected.txt b/dx/tests/046-dex-exceptions/expected.txt
new file mode 100644
index 0000000..933a547
--- /dev/null
+++ b/dx/tests/046-dex-exceptions/expected.txt
@@ -0,0 +1,48 @@
+Blort.exTest1:(I)I:
+regs: 0004; ins: 0001; outs: 0001
+ 0000: move v0, v3
+ 0001: move v2, v0
+ 0002: invoke-static {v2}, Blort.maybeThrow:(I)I
+ 0005: move-result v2
+ 0006: const/4 v2, #int 1 // #1
+ 0007: move v0, v2
+ 0008: return v0
+ 0009: move-exception v2
+ 000a: move-object v1, v2
+ 000b: const/4 v2, #int 2 // #2
+ 000c: move v0, v2
+ 000d: goto 0008 // -0005
+ catches
+ tries:
+ try 0002..0005
+ catch java.lang.RuntimeException -> 0009
+Blort.exTest2:(I)I:
+regs: 0004; ins: 0001; outs: 0001
+ 0000: move v0, v3
+ 0001: add-int/lit8 v0, v0, #int 1 // #01
+ 0003: move v2, v0
+ 0004: invoke-static {v2}, Blort.maybeThrow:(I)I
+ 0007: move-result v2
+ 0008: move v0, v2
+ 0009: add-int/lit8 v0, v0, #int 1 // #01
+ 000b: move v2, v0
+ 000c: invoke-static {v2}, Blort.maybeThrow:(I)I
+ 000f: move-result v2
+ 0010: move v0, v2
+ 0011: return v0
+ 0012: move-exception v2
+ 0013: move-object v1, v2
+ 0014: const/4 v2, #int 1 // #1
+ 0015: move v0, v2
+ 0016: goto 0011 // -0005
+ 0017: move-exception v2
+ 0018: move-object v1, v2
+ 0019: const/4 v2, #int 3 // #3
+ 001a: move v0, v2
+ 001b: goto 0011 // -000a
+ catches
+ tries:
+ try 0004..0007
+ catch java.lang.RuntimeException -> 0012
+ try 000c..000f
+ catch java.lang.RuntimeException -> 0017
diff --git a/dx/tests/046-dex-exceptions/info.txt b/dx/tests/046-dex-exceptions/info.txt
new file mode 100644
index 0000000..a4bc065
--- /dev/null
+++ b/dx/tests/046-dex-exceptions/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+a couple simple cases of exception handling get converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/046-dex-exceptions/run b/dx/tests/046-dex-exceptions/run
new file mode 100644
index 0000000..8c821b6
--- /dev/null
+++ b/dx/tests/046-dex-exceptions/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals --dump-method=Blort.exTest'*' *.class
diff --git a/dx/tests/047-dex-wide-args/Blort.java b/dx/tests/047-dex-wide-args/Blort.java
new file mode 100644
index 0000000..fa0f1d8
--- /dev/null
+++ b/dx/tests/047-dex-wide-args/Blort.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ public static long test1(int w, long x, int y, long z) {
+ return w + x + y + z;
+ }
+
+ public static long test2(long w, int x, long y, int z) {
+ return w + x + y + z;
+ }
+}
diff --git a/dx/tests/047-dex-wide-args/expected.txt b/dx/tests/047-dex-wide-args/expected.txt
new file mode 100644
index 0000000..cc353fc
--- /dev/null
+++ b/dx/tests/047-dex-wide-args/expected.txt
@@ -0,0 +1,34 @@
+Blort.test1:(IJIJ)J:
+regs: 0010; ins: 0006; outs: 0000
+ 0000: move v0, v10
+ 0001: move-wide v1, v11
+ 0002: move v3, v13
+ 0003: move-wide v4, v14
+ 0004: move v6, v0
+ 0005: int-to-long v6, v6
+ 0006: move-wide v8, v1
+ 0007: add-long/2addr v6, v8
+ 0008: move v8, v3
+ 0009: int-to-long v8, v8
+ 000a: add-long/2addr v6, v8
+ 000b: move-wide v8, v4
+ 000c: add-long/2addr v6, v8
+ 000d: move-wide v0, v6
+ 000e: return-wide v0
+Blort.test2:(JIJI)J:
+regs: 0010; ins: 0006; outs: 0000
+ 0000: move-wide v0, v10
+ 0001: move v2, v12
+ 0002: move-wide v3, v13
+ 0003: move v5, v15
+ 0004: move-wide v6, v0
+ 0005: move v8, v2
+ 0006: int-to-long v8, v8
+ 0007: add-long/2addr v6, v8
+ 0008: move-wide v8, v3
+ 0009: add-long/2addr v6, v8
+ 000a: move v8, v5
+ 000b: int-to-long v8, v8
+ 000c: add-long/2addr v6, v8
+ 000d: move-wide v0, v6
+ 000e: return-wide v0
diff --git a/dx/tests/047-dex-wide-args/info.txt b/dx/tests/047-dex-wide-args/info.txt
new file mode 100644
index 0000000..357204d
--- /dev/null
+++ b/dx/tests/047-dex-wide-args/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+wide (category-2) arguments get treated reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/047-dex-wide-args/run b/dx/tests/047-dex-wide-args/run
new file mode 100644
index 0000000..52f1131
--- /dev/null
+++ b/dx/tests/047-dex-wide-args/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+ --dump-method=Blort.test'*' *.class
diff --git a/dx/tests/048-dex-new-array/Blort.java b/dx/tests/048-dex-new-array/Blort.java
new file mode 100644
index 0000000..1af0cde
--- /dev/null
+++ b/dx/tests/048-dex-new-array/Blort.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ public static void sink(Object x) {
+ // Do nothing.
+ }
+
+ public static void test() {
+ sink(new boolean[0]);
+ sink(new byte[1]);
+ sink(new char[2]);
+ sink(new short[3]);
+ sink(new int[4]);
+ sink(new long[5]);
+ sink(new float[6]);
+ sink(new double[7]);
+ sink(new Object[0]);
+ }
+}
diff --git a/dx/tests/048-dex-new-array/expected.txt b/dx/tests/048-dex-new-array/expected.txt
new file mode 100644
index 0000000..15332ca
--- /dev/null
+++ b/dx/tests/048-dex-new-array/expected.txt
@@ -0,0 +1,29 @@
+Blort.test:()V:
+regs: 0002; ins: 0000; outs: 0001
+ 0000: const/4 v1, #int 0 // #0
+ 0001: new-array v0, v1, boolean[]
+ 0003: invoke-static {v0}, Blort.sink:(Ljava/lang/Object;)V
+ 0006: const/4 v0, #int 1 // #1
+ 0007: new-array v0, v0, byte[]
+ 0009: invoke-static {v0}, Blort.sink:(Ljava/lang/Object;)V
+ 000c: const/4 v0, #int 2 // #2
+ 000d: new-array v0, v0, char[]
+ 000f: invoke-static {v0}, Blort.sink:(Ljava/lang/Object;)V
+ 0012: const/4 v0, #int 3 // #3
+ 0013: new-array v0, v0, short[]
+ 0015: invoke-static {v0}, Blort.sink:(Ljava/lang/Object;)V
+ 0018: const/4 v0, #int 4 // #4
+ 0019: new-array v0, v0, int[]
+ 001b: invoke-static {v0}, Blort.sink:(Ljava/lang/Object;)V
+ 001e: const/4 v0, #int 5 // #5
+ 001f: new-array v0, v0, long[]
+ 0021: invoke-static {v0}, Blort.sink:(Ljava/lang/Object;)V
+ 0024: const/4 v0, #int 6 // #6
+ 0025: new-array v0, v0, float[]
+ 0027: invoke-static {v0}, Blort.sink:(Ljava/lang/Object;)V
+ 002a: const/4 v0, #int 7 // #7
+ 002b: new-array v0, v0, double[]
+ 002d: invoke-static {v0}, Blort.sink:(Ljava/lang/Object;)V
+ 0030: new-array v0, v1, java.lang.Object[]
+ 0032: invoke-static {v0}, Blort.sink:(Ljava/lang/Object;)V
+ 0035: return-void
diff --git a/dx/tests/048-dex-new-array/info.txt b/dx/tests/048-dex-new-array/info.txt
new file mode 100644
index 0000000..a2ff173
--- /dev/null
+++ b/dx/tests/048-dex-new-array/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+simple array construction expressions get converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/048-dex-new-array/run b/dx/tests/048-dex-new-array/run
new file mode 100644
index 0000000..9927f1a
--- /dev/null
+++ b/dx/tests/048-dex-new-array/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --positions=none --no-locals --dump-method=Blort.test *.class
diff --git a/dx/tests/049-dex-instanceof/Blort.java b/dx/tests/049-dex-instanceof/Blort.java
new file mode 100644
index 0000000..ad41f30
--- /dev/null
+++ b/dx/tests/049-dex-instanceof/Blort.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ public static boolean test(Object x) {
+ return x instanceof Blort;
+ }
+}
diff --git a/dx/tests/049-dex-instanceof/expected.txt b/dx/tests/049-dex-instanceof/expected.txt
new file mode 100644
index 0000000..77f903c
--- /dev/null
+++ b/dx/tests/049-dex-instanceof/expected.txt
@@ -0,0 +1,7 @@
+Blort.test:(Ljava/lang/Object;)Z:
+regs: 0003; ins: 0001; outs: 0000
+ 0000: move-object v0, v2
+ 0001: move-object v1, v0
+ 0002: instance-of v1, v1, Blort
+ 0004: move v0, v1
+ 0005: return v0
diff --git a/dx/tests/049-dex-instanceof/info.txt b/dx/tests/049-dex-instanceof/info.txt
new file mode 100644
index 0000000..a6c82b2
--- /dev/null
+++ b/dx/tests/049-dex-instanceof/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+instanceof expressions get converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/049-dex-instanceof/run b/dx/tests/049-dex-instanceof/run
new file mode 100644
index 0000000..7578321
--- /dev/null
+++ b/dx/tests/049-dex-instanceof/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+ --dump-method=Blort.test *.class
diff --git a/dx/tests/050-dex-checkcast/Blort.java b/dx/tests/050-dex-checkcast/Blort.java
new file mode 100644
index 0000000..5441eec
--- /dev/null
+++ b/dx/tests/050-dex-checkcast/Blort.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ public static Blort test(Object x) {
+ return (Blort) x;
+ }
+}
diff --git a/dx/tests/050-dex-checkcast/expected.txt b/dx/tests/050-dex-checkcast/expected.txt
new file mode 100644
index 0000000..96f7f20
--- /dev/null
+++ b/dx/tests/050-dex-checkcast/expected.txt
@@ -0,0 +1,7 @@
+Blort.test:(Ljava/lang/Object;)LBlort;:
+regs: 0003; ins: 0001; outs: 0000
+ 0000: move-object v0, v2
+ 0001: move-object v1, v0
+ 0002: check-cast v1, Blort
+ 0004: move-object v0, v1
+ 0005: return-object v0
diff --git a/dx/tests/050-dex-checkcast/info.txt b/dx/tests/050-dex-checkcast/info.txt
new file mode 100644
index 0000000..b23daa4
--- /dev/null
+++ b/dx/tests/050-dex-checkcast/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+checked cast expressions get converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/050-dex-checkcast/run b/dx/tests/050-dex-checkcast/run
new file mode 100644
index 0000000..7578321
--- /dev/null
+++ b/dx/tests/050-dex-checkcast/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+ --dump-method=Blort.test *.class
diff --git a/dx/tests/051-dex-explicit-null/Blort.java b/dx/tests/051-dex-explicit-null/Blort.java
new file mode 100644
index 0000000..9045c50
--- /dev/null
+++ b/dx/tests/051-dex-explicit-null/Blort.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ public static Object test1() {
+ return null;
+ }
+
+ public static void test2(Object x) {
+ x = null;
+ }
+}
diff --git a/dx/tests/051-dex-explicit-null/expected.txt b/dx/tests/051-dex-explicit-null/expected.txt
new file mode 100644
index 0000000..18ce092
--- /dev/null
+++ b/dx/tests/051-dex-explicit-null/expected.txt
@@ -0,0 +1,10 @@
+Blort.test1:()Ljava/lang/Object;:
+regs: 0001; ins: 0000; outs: 0000
+ 0000: const/4 v0, #null // #0
+ 0001: return-object v0
+Blort.test2:(Ljava/lang/Object;)V:
+regs: 0003; ins: 0001; outs: 0000
+ 0000: move-object v0, v2
+ 0001: const/4 v1, #null // #0
+ 0002: move-object v0, v1
+ 0003: return-void
diff --git a/dx/tests/051-dex-explicit-null/info.txt b/dx/tests/051-dex-explicit-null/info.txt
new file mode 100644
index 0000000..8a03dc3
--- /dev/null
+++ b/dx/tests/051-dex-explicit-null/info.txt
@@ -0,0 +1,7 @@
+This is a smoke test of dex conversion, which checks to see that
+assigning a reference variable to null works, as well as explicitly
+returning a null.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/051-dex-explicit-null/run b/dx/tests/051-dex-explicit-null/run
new file mode 100644
index 0000000..52f1131
--- /dev/null
+++ b/dx/tests/051-dex-explicit-null/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+ --dump-method=Blort.test'*' *.class
diff --git a/dx/tests/052-dex-static-var-access/Blort.java b/dx/tests/052-dex-static-var-access/Blort.java
new file mode 100644
index 0000000..3dd0e78
--- /dev/null
+++ b/dx/tests/052-dex-static-var-access/Blort.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ public static boolean staticBoolean;
+ public static byte staticByte;
+ public static char staticChar;
+ public static short staticShort;
+ public static int staticInt;
+ public static long staticLong;
+ public static float staticFloat;
+ public static double staticDouble;
+ public static Object staticObject;
+
+ public static Object test1() {
+ int x = staticByte + staticChar + staticShort + staticInt +
+ (int) staticLong + (int) staticFloat + (int) staticDouble;
+
+ if (staticBoolean && (x > 0)) {;
+ return staticObject;
+ } else {
+ return null;
+ }
+ }
+
+ public static void test2(boolean b, int i, Object o) {
+ staticBoolean = b;
+ staticByte = (byte) i;
+ staticChar = (char) i;
+ staticShort = (short) i;
+ staticInt = i;
+ staticLong = i;
+ staticFloat = i;
+ staticDouble = i;
+ staticObject = o;
+ }
+}
diff --git a/dx/tests/052-dex-static-var-access/expected.txt b/dx/tests/052-dex-static-var-access/expected.txt
new file mode 100644
index 0000000..239f984
--- /dev/null
+++ b/dx/tests/052-dex-static-var-access/expected.txt
@@ -0,0 +1,59 @@
+Blort.test1:()Ljava/lang/Object;:
+regs: 0004; ins: 0000; outs: 0000
+ 0000: sget-byte v1, Blort.staticByte:B
+ 0002: sget-char v2, Blort.staticChar:C
+ 0004: add-int/2addr v1, v2
+ 0005: sget-short v2, Blort.staticShort:S
+ 0007: add-int/2addr v1, v2
+ 0008: sget v2, Blort.staticInt:I
+ 000a: add-int/2addr v1, v2
+ 000b: sget-wide v2, Blort.staticLong:J
+ 000d: long-to-int v2, v2
+ 000e: add-int/2addr v1, v2
+ 000f: sget v2, Blort.staticFloat:F
+ 0011: float-to-int v2, v2
+ 0012: add-int/2addr v1, v2
+ 0013: sget-wide v2, Blort.staticDouble:D
+ 0015: double-to-int v2, v2
+ 0016: add-int/2addr v1, v2
+ 0017: move v0, v1
+ 0018: sget-boolean v1, Blort.staticBoolean:Z
+ 001a: if-eqz v1, 0023 // +0009
+ 001c: move v1, v0
+ 001d: if-lez v1, 0023 // +0006
+ 001f: sget-object v1, Blort.staticObject:Ljava/lang/Object;
+ 0021: move-object v0, v1
+ 0022: return-object v0
+ 0023: const/4 v1, #null // #0
+ 0024: move-object v0, v1
+ 0025: goto 0022 // -0003
+Blort.test2:(ZILjava/lang/Object;)V:
+regs: 0008; ins: 0003; outs: 0000
+ 0000: move v0, v5
+ 0001: move v1, v6
+ 0002: move-object v2, v7
+ 0003: move v3, v0
+ 0004: sput-boolean v3, Blort.staticBoolean:Z
+ 0006: move v3, v1
+ 0007: int-to-byte v3, v3
+ 0008: sput-byte v3, Blort.staticByte:B
+ 000a: move v3, v1
+ 000b: int-to-char v3, v3
+ 000c: sput-char v3, Blort.staticChar:C
+ 000e: move v3, v1
+ 000f: int-to-short v3, v3
+ 0010: sput-short v3, Blort.staticShort:S
+ 0012: move v3, v1
+ 0013: sput v3, Blort.staticInt:I
+ 0015: move v3, v1
+ 0016: int-to-long v3, v3
+ 0017: sput-wide v3, Blort.staticLong:J
+ 0019: move v3, v1
+ 001a: int-to-float v3, v3
+ 001b: sput v3, Blort.staticFloat:F
+ 001d: move v3, v1
+ 001e: int-to-double v3, v3
+ 001f: sput-wide v3, Blort.staticDouble:D
+ 0021: move-object v3, v2
+ 0022: sput-object v3, Blort.staticObject:Ljava/lang/Object;
+ 0024: return-void
diff --git a/dx/tests/052-dex-static-var-access/info.txt b/dx/tests/052-dex-static-var-access/info.txt
new file mode 100644
index 0000000..c3c3b0b
--- /dev/null
+++ b/dx/tests/052-dex-static-var-access/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+static variable access works.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/052-dex-static-var-access/run b/dx/tests/052-dex-static-var-access/run
new file mode 100644
index 0000000..52f1131
--- /dev/null
+++ b/dx/tests/052-dex-static-var-access/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+ --dump-method=Blort.test'*' *.class
diff --git a/dx/tests/053-dex-instance-var-access/Blort.java b/dx/tests/053-dex-instance-var-access/Blort.java
new file mode 100644
index 0000000..eb62d62
--- /dev/null
+++ b/dx/tests/053-dex-instance-var-access/Blort.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ public boolean insBoolean;
+ public byte insByte;
+ public char insChar;
+ public short insShort;
+ public int insInt;
+ public long insLong;
+ public float insFloat;
+ public double insDouble;
+ public Object insObject;
+
+ public Object test1() {
+ int x = insByte + insChar + insShort + insInt +
+ (int) insLong + (int) insFloat + (int) insDouble;
+
+ if (insBoolean && (x > 0)) {;
+ return insObject;
+ } else {
+ return null;
+ }
+ }
+
+ public void test2(boolean b, int i, Object o) {
+ insBoolean = b;
+ insByte = (byte) i;
+ insChar = (char) i;
+ insShort = (short) i;
+ insInt = i;
+ insLong = i;
+ insFloat = i;
+ insDouble = i;
+ insObject = o;
+ }
+}
diff --git a/dx/tests/053-dex-instance-var-access/expected.txt b/dx/tests/053-dex-instance-var-access/expected.txt
new file mode 100644
index 0000000..b73f617
--- /dev/null
+++ b/dx/tests/053-dex-instance-var-access/expected.txt
@@ -0,0 +1,79 @@
+Blort.test1:()Ljava/lang/Object;:
+regs: 0006; ins: 0001; outs: 0000
+ 0000: move-object v0, v5
+ 0001: move-object v2, v0
+ 0002: iget-byte v2, v2, Blort.insByte:B
+ 0004: move-object v3, v0
+ 0005: iget-char v3, v3, Blort.insChar:C
+ 0007: add-int/2addr v2, v3
+ 0008: move-object v3, v0
+ 0009: iget-short v3, v3, Blort.insShort:S
+ 000b: add-int/2addr v2, v3
+ 000c: move-object v3, v0
+ 000d: iget v3, v3, Blort.insInt:I
+ 000f: add-int/2addr v2, v3
+ 0010: move-object v3, v0
+ 0011: iget-wide v3, v3, Blort.insLong:J
+ 0013: long-to-int v3, v3
+ 0014: add-int/2addr v2, v3
+ 0015: move-object v3, v0
+ 0016: iget v3, v3, Blort.insFloat:F
+ 0018: float-to-int v3, v3
+ 0019: add-int/2addr v2, v3
+ 001a: move-object v3, v0
+ 001b: iget-wide v3, v3, Blort.insDouble:D
+ 001d: double-to-int v3, v3
+ 001e: add-int/2addr v2, v3
+ 001f: move v1, v2
+ 0020: move-object v2, v0
+ 0021: iget-boolean v2, v2, Blort.insBoolean:Z
+ 0023: if-eqz v2, 002d // +000a
+ 0025: move v2, v1
+ 0026: if-lez v2, 002d // +0007
+ 0028: move-object v2, v0
+ 0029: iget-object v2, v2, Blort.insObject:Ljava/lang/Object;
+ 002b: move-object v0, v2
+ 002c: return-object v0
+ 002d: const/4 v2, #null // #0
+ 002e: move-object v0, v2
+ 002f: goto 002c // -0003
+Blort.test2:(ZILjava/lang/Object;)V:
+regs: 000b; ins: 0004; outs: 0000
+ 0000: move-object v0, v7
+ 0001: move v1, v8
+ 0002: move v2, v9
+ 0003: move-object v3, v10
+ 0004: move-object v4, v0
+ 0005: move v5, v1
+ 0006: iput-boolean v5, v4, Blort.insBoolean:Z
+ 0008: move-object v4, v0
+ 0009: move v5, v2
+ 000a: int-to-byte v5, v5
+ 000b: iput-byte v5, v4, Blort.insByte:B
+ 000d: move-object v4, v0
+ 000e: move v5, v2
+ 000f: int-to-char v5, v5
+ 0010: iput-char v5, v4, Blort.insChar:C
+ 0012: move-object v4, v0
+ 0013: move v5, v2
+ 0014: int-to-short v5, v5
+ 0015: iput-short v5, v4, Blort.insShort:S
+ 0017: move-object v4, v0
+ 0018: move v5, v2
+ 0019: iput v5, v4, Blort.insInt:I
+ 001b: move-object v4, v0
+ 001c: move v5, v2
+ 001d: int-to-long v5, v5
+ 001e: iput-wide v5, v4, Blort.insLong:J
+ 0020: move-object v4, v0
+ 0021: move v5, v2
+ 0022: int-to-float v5, v5
+ 0023: iput v5, v4, Blort.insFloat:F
+ 0025: move-object v4, v0
+ 0026: move v5, v2
+ 0027: int-to-double v5, v5
+ 0028: iput-wide v5, v4, Blort.insDouble:D
+ 002a: move-object v4, v0
+ 002b: move-object v5, v3
+ 002c: iput-object v5, v4, Blort.insObject:Ljava/lang/Object;
+ 002e: return-void
diff --git a/dx/tests/053-dex-instance-var-access/info.txt b/dx/tests/053-dex-instance-var-access/info.txt
new file mode 100644
index 0000000..4f95797
--- /dev/null
+++ b/dx/tests/053-dex-instance-var-access/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+instance variable access works.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/053-dex-instance-var-access/run b/dx/tests/053-dex-instance-var-access/run
new file mode 100644
index 0000000..52f1131
--- /dev/null
+++ b/dx/tests/053-dex-instance-var-access/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+ --dump-method=Blort.test'*' *.class
diff --git a/dx/tests/054-dex-high16/Blort.java b/dx/tests/054-dex-high16/Blort.java
new file mode 100644
index 0000000..9fba972
--- /dev/null
+++ b/dx/tests/054-dex-high16/Blort.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ public static void sink(int i) {
+ // Do nothing.
+ }
+
+ public static void sink(long l) {
+ // Do nothing.
+ }
+
+ public static void sink(float f) {
+ // Do nothing.
+ }
+
+ public static void sink(double d) {
+ // Do nothing.
+ }
+
+ public static void testInt() {
+ sink(Integer.MIN_VALUE);
+ sink(0x40000000);
+ sink(0x20000000);
+ sink(0x10000000);
+ sink(0x00080000);
+ sink(0x00040000);
+ sink(0x00020000);
+ sink(0x00010000);
+ sink(0x56780000);
+ }
+
+ public static void testLong() {
+ sink(Long.MIN_VALUE);
+ sink(0x4000000000000000L);
+ sink(0x2000000000000000L);
+ sink(0x1000000000000000L);
+ sink(0x0008000000000000L);
+ sink(0x0004000000000000L);
+ sink(0x0002000000000000L);
+ sink(0x0001000000000000L);
+ sink(0x5678000000000000L);
+ }
+
+ public static void testFloat() {
+ sink(Float.NEGATIVE_INFINITY);
+ sink(-0.0f);
+ sink(1.0f);
+ sink(Float.POSITIVE_INFINITY);
+ sink(Float.NaN);
+ }
+
+ public static void testDouble() {
+ sink(Double.NEGATIVE_INFINITY);
+ sink(-0.0);
+ sink(1.0);
+ sink(Double.POSITIVE_INFINITY);
+ sink(Double.NaN);
+ }
+}
diff --git a/dx/tests/054-dex-high16/expected.txt b/dx/tests/054-dex-high16/expected.txt
new file mode 100644
index 0000000..b9d6cf3
--- /dev/null
+++ b/dx/tests/054-dex-high16/expected.txt
@@ -0,0 +1,68 @@
+Blort.testDouble:()V:
+regs: 0002; ins: 0000; outs: 0002
+ 0000: const-wide/high16 v0, #double -Infinity // #fff0000000000000
+ 0002: invoke-static {v0, v1}, Blort.sink:(D)V
+ 0005: const-wide/high16 v0, #double -0.0 // #8000000000000000
+ 0007: invoke-static {v0, v1}, Blort.sink:(D)V
+ 000a: const-wide/high16 v0, #double 1.0 // #3ff0000000000000
+ 000c: invoke-static {v0, v1}, Blort.sink:(D)V
+ 000f: const-wide/high16 v0, #double Infinity // #7ff0000000000000
+ 0011: invoke-static {v0, v1}, Blort.sink:(D)V
+ 0014: const-wide/high16 v0, #double NaN // #7ff8000000000000
+ 0016: invoke-static {v0, v1}, Blort.sink:(D)V
+ 0019: return-void
+Blort.testFloat:()V:
+regs: 0001; ins: 0000; outs: 0001
+ 0000: const/high16 v0, #float -Infinity // #ff800000
+ 0002: invoke-static {v0}, Blort.sink:(F)V
+ 0005: const/high16 v0, #float -0.0 // #80000000
+ 0007: invoke-static {v0}, Blort.sink:(F)V
+ 000a: const/high16 v0, #float 1.0 // #3f800000
+ 000c: invoke-static {v0}, Blort.sink:(F)V
+ 000f: const/high16 v0, #float Infinity // #7f800000
+ 0011: invoke-static {v0}, Blort.sink:(F)V
+ 0014: const/high16 v0, #float NaN // #7fc00000
+ 0016: invoke-static {v0}, Blort.sink:(F)V
+ 0019: return-void
+Blort.testInt:()V:
+regs: 0001; ins: 0000; outs: 0001
+ 0000: const/high16 v0, #int -2147483648 // #80000000
+ 0002: invoke-static {v0}, Blort.sink:(I)V
+ 0005: const/high16 v0, #int 1073741824 // #40000000
+ 0007: invoke-static {v0}, Blort.sink:(I)V
+ 000a: const/high16 v0, #int 536870912 // #20000000
+ 000c: invoke-static {v0}, Blort.sink:(I)V
+ 000f: const/high16 v0, #int 268435456 // #10000000
+ 0011: invoke-static {v0}, Blort.sink:(I)V
+ 0014: const/high16 v0, #int 524288 // #00080000
+ 0016: invoke-static {v0}, Blort.sink:(I)V
+ 0019: const/high16 v0, #int 262144 // #00040000
+ 001b: invoke-static {v0}, Blort.sink:(I)V
+ 001e: const/high16 v0, #int 131072 // #00020000
+ 0020: invoke-static {v0}, Blort.sink:(I)V
+ 0023: const/high16 v0, #int 65536 // #00010000
+ 0025: invoke-static {v0}, Blort.sink:(I)V
+ 0028: const/high16 v0, #int 1450704896 // #56780000
+ 002a: invoke-static {v0}, Blort.sink:(I)V
+ 002d: return-void
+Blort.testLong:()V:
+regs: 0002; ins: 0000; outs: 0002
+ 0000: const-wide/high16 v0, #long -9223372036854775808 // #8000000000000000
+ 0002: invoke-static {v0, v1}, Blort.sink:(J)V
+ 0005: const-wide/high16 v0, #long 4611686018427387904 // #4000000000000000
+ 0007: invoke-static {v0, v1}, Blort.sink:(J)V
+ 000a: const-wide/high16 v0, #long 2305843009213693952 // #2000000000000000
+ 000c: invoke-static {v0, v1}, Blort.sink:(J)V
+ 000f: const-wide/high16 v0, #long 1152921504606846976 // #1000000000000000
+ 0011: invoke-static {v0, v1}, Blort.sink:(J)V
+ 0014: const-wide/high16 v0, #long 2251799813685248 // #0008000000000000
+ 0016: invoke-static {v0, v1}, Blort.sink:(J)V
+ 0019: const-wide/high16 v0, #long 1125899906842624 // #0004000000000000
+ 001b: invoke-static {v0, v1}, Blort.sink:(J)V
+ 001e: const-wide/high16 v0, #long 562949953421312 // #0002000000000000
+ 0020: invoke-static {v0, v1}, Blort.sink:(J)V
+ 0023: const-wide/high16 v0, #long 281474976710656 // #0001000000000000
+ 0025: invoke-static {v0, v1}, Blort.sink:(J)V
+ 0028: const-wide/high16 v0, #long 6230730084467081216 // #5678000000000000
+ 002a: invoke-static {v0, v1}, Blort.sink:(J)V
+ 002d: return-void
diff --git a/dx/tests/054-dex-high16/info.txt b/dx/tests/054-dex-high16/info.txt
new file mode 100644
index 0000000..ef1fac4
--- /dev/null
+++ b/dx/tests/054-dex-high16/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+"high16" constants get converted properly.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/054-dex-high16/run b/dx/tests/054-dex-high16/run
new file mode 100644
index 0000000..a2c7458
--- /dev/null
+++ b/dx/tests/054-dex-high16/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --positions=none --no-locals \
+ --dump-method=Blort.test'*' *.class
diff --git a/dx/tests/055-dex-explicit-throw/Blort.java b/dx/tests/055-dex-explicit-throw/Blort.java
new file mode 100644
index 0000000..e6720d9
--- /dev/null
+++ b/dx/tests/055-dex-explicit-throw/Blort.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ private static RuntimeException theException = new RuntimeException();
+
+ public static void test1() {
+ throw theException;
+ }
+
+ public static int test2() {
+ try {
+ throw theException;
+ } catch (RuntimeException ex) {
+ return 1;
+ }
+ }
+}
diff --git a/dx/tests/055-dex-explicit-throw/expected.txt b/dx/tests/055-dex-explicit-throw/expected.txt
new file mode 100644
index 0000000..b7e4c33
--- /dev/null
+++ b/dx/tests/055-dex-explicit-throw/expected.txt
@@ -0,0 +1,17 @@
+Blort.test1:()V:
+regs: 0001; ins: 0000; outs: 0000
+ 0000: sget-object v0, Blort.theException:Ljava/lang/RuntimeException;
+ 0002: throw v0
+Blort.test2:()I:
+regs: 0002; ins: 0000; outs: 0000
+ 0000: sget-object v1, Blort.theException:Ljava/lang/RuntimeException;
+ 0002: throw v1
+ 0003: move-exception v1
+ 0004: move-object v0, v1
+ 0005: const/4 v1, #int 1 // #1
+ 0006: move v0, v1
+ 0007: return v0
+ catches
+ tries:
+ try 0000..0003
+ catch java.lang.RuntimeException -> 0003
diff --git a/dx/tests/055-dex-explicit-throw/info.txt b/dx/tests/055-dex-explicit-throw/info.txt
new file mode 100644
index 0000000..b3b7808
--- /dev/null
+++ b/dx/tests/055-dex-explicit-throw/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+explicit use of "throw" gets converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/055-dex-explicit-throw/run b/dx/tests/055-dex-explicit-throw/run
new file mode 100644
index 0000000..52f1131
--- /dev/null
+++ b/dx/tests/055-dex-explicit-throw/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+ --dump-method=Blort.test'*' *.class
diff --git a/dx/tests/056-dex-call-interface/Blort.java b/dx/tests/056-dex-call-interface/Blort.java
new file mode 100644
index 0000000..4175f05
--- /dev/null
+++ b/dx/tests/056-dex-call-interface/Blort.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ public static int test(Zorch z, double d) {
+ z.zorch1();
+ z.zorch2(d);
+ int x = z.zorch3(z);
+ int y = (int) z.zorch4();
+ return x + y;
+ }
+}
diff --git a/dx/tests/056-dex-call-interface/Zorch.java b/dx/tests/056-dex-call-interface/Zorch.java
new file mode 100644
index 0000000..03e3762
--- /dev/null
+++ b/dx/tests/056-dex-call-interface/Zorch.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public interface Zorch
+{
+ public void zorch1();
+ public void zorch2(double d);
+ public int zorch3(Object o);
+ public long zorch4();
+}
diff --git a/dx/tests/056-dex-call-interface/expected.txt b/dx/tests/056-dex-call-interface/expected.txt
new file mode 100644
index 0000000..faf18c4
--- /dev/null
+++ b/dx/tests/056-dex-call-interface/expected.txt
@@ -0,0 +1,24 @@
+Blort.test:(LZorch;D)I:
+regs: 000b; ins: 0003; outs: 0003
+ 0000: move-object v0, v8
+ 0001: move-wide v1, v9
+ 0002: move-object v5, v0
+ 0003: invoke-interface {v5}, Zorch.zorch1:()V
+ 0006: move-object v5, v0
+ 0007: move-wide v6, v1
+ 0008: invoke-interface {v5, v6, v7}, Zorch.zorch2:(D)V
+ 000b: move-object v5, v0
+ 000c: move-object v6, v0
+ 000d: invoke-interface {v5, v6}, Zorch.zorch3:(Ljava/lang/Object;)I
+ 0010: move-result v5
+ 0011: move v3, v5
+ 0012: move-object v5, v0
+ 0013: invoke-interface {v5}, Zorch.zorch4:()J
+ 0016: move-result-wide v5
+ 0017: long-to-int v5, v5
+ 0018: move v4, v5
+ 0019: move v5, v3
+ 001a: move v6, v4
+ 001b: add-int/2addr v5, v6
+ 001c: move v0, v5
+ 001d: return v0
diff --git a/dx/tests/056-dex-call-interface/info.txt b/dx/tests/056-dex-call-interface/info.txt
new file mode 100644
index 0000000..2f964a2
--- /dev/null
+++ b/dx/tests/056-dex-call-interface/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+a couple cases of interface method invocation work properly.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/056-dex-call-interface/run b/dx/tests/056-dex-call-interface/run
new file mode 100644
index 0000000..1076ae1
--- /dev/null
+++ b/dx/tests/056-dex-call-interface/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+ --dump-method=Blort.test Blort.class
diff --git a/dx/tests/057-dex-call-virtual/Blort.java b/dx/tests/057-dex-call-virtual/Blort.java
new file mode 100644
index 0000000..75ee7d8
--- /dev/null
+++ b/dx/tests/057-dex-call-virtual/Blort.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ public static int test(Zorch z) {
+ z.zorch1();
+ return z.zorch2(5);
+ }
+}
diff --git a/dx/tests/057-dex-call-virtual/Zorch.java b/dx/tests/057-dex-call-virtual/Zorch.java
new file mode 100644
index 0000000..867eaed
--- /dev/null
+++ b/dx/tests/057-dex-call-virtual/Zorch.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Zorch
+{
+ public void zorch1() {
+ // This space intentionally left blank.
+ }
+
+ public int zorch2(int x) {
+ return 0;
+ }
+}
diff --git a/dx/tests/057-dex-call-virtual/expected.txt b/dx/tests/057-dex-call-virtual/expected.txt
new file mode 100644
index 0000000..ea50d35
--- /dev/null
+++ b/dx/tests/057-dex-call-virtual/expected.txt
@@ -0,0 +1,11 @@
+Blort.test:(LZorch;)I:
+regs: 0004; ins: 0001; outs: 0002
+ 0000: move-object v0, v3
+ 0001: move-object v1, v0
+ 0002: invoke-virtual {v1}, Zorch.zorch1:()V
+ 0005: move-object v1, v0
+ 0006: const/4 v2, #int 5 // #5
+ 0007: invoke-virtual {v1, v2}, Zorch.zorch2:(I)I
+ 000a: move-result v1
+ 000b: move v0, v1
+ 000c: return v0
diff --git a/dx/tests/057-dex-call-virtual/info.txt b/dx/tests/057-dex-call-virtual/info.txt
new file mode 100644
index 0000000..452c9cb
--- /dev/null
+++ b/dx/tests/057-dex-call-virtual/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+a couple cases of regular virtual method invocation work properly.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/057-dex-call-virtual/run b/dx/tests/057-dex-call-virtual/run
new file mode 100644
index 0000000..1076ae1
--- /dev/null
+++ b/dx/tests/057-dex-call-virtual/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+ --dump-method=Blort.test Blort.class
diff --git a/dx/tests/058-dex-call-direct/Blort.java b/dx/tests/058-dex-call-direct/Blort.java
new file mode 100644
index 0000000..6d7fa7f
--- /dev/null
+++ b/dx/tests/058-dex-call-direct/Blort.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ public static int test(Blort b) {
+ b.zorch1();
+ return b.zorch2(5);
+ }
+
+ private void zorch1() {
+ // This space intentionally left blank.
+ }
+
+ private int zorch2(int x) {
+ return 1;
+ }
+}
diff --git a/dx/tests/058-dex-call-direct/expected.txt b/dx/tests/058-dex-call-direct/expected.txt
new file mode 100644
index 0000000..11820a3
--- /dev/null
+++ b/dx/tests/058-dex-call-direct/expected.txt
@@ -0,0 +1,11 @@
+Blort.test:(LBlort;)I:
+regs: 0004; ins: 0001; outs: 0002
+ 0000: move-object v0, v3
+ 0001: move-object v1, v0
+ 0002: invoke-direct {v1}, Blort.zorch1:()V
+ 0005: move-object v1, v0
+ 0006: const/4 v2, #int 5 // #5
+ 0007: invoke-direct {v1, v2}, Blort.zorch2:(I)I
+ 000a: move-result v1
+ 000b: move v0, v1
+ 000c: return v0
diff --git a/dx/tests/058-dex-call-direct/info.txt b/dx/tests/058-dex-call-direct/info.txt
new file mode 100644
index 0000000..a22c479
--- /dev/null
+++ b/dx/tests/058-dex-call-direct/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+a couple cases of direct instance method invocation work properly.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/058-dex-call-direct/run b/dx/tests/058-dex-call-direct/run
new file mode 100644
index 0000000..1076ae1
--- /dev/null
+++ b/dx/tests/058-dex-call-direct/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+ --dump-method=Blort.test Blort.class
diff --git a/dx/tests/059-dex-call-super/Blort.java b/dx/tests/059-dex-call-super/Blort.java
new file mode 100644
index 0000000..efb451e
--- /dev/null
+++ b/dx/tests/059-dex-call-super/Blort.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+ extends Zorch
+{
+ public int test1() {
+ super.zorch1();
+ return super.zorch2(5);
+ }
+
+ public void test2() {
+ super.test2();
+ }
+}
diff --git a/dx/tests/059-dex-call-super/Zorch.java b/dx/tests/059-dex-call-super/Zorch.java
new file mode 100644
index 0000000..aa668ab
--- /dev/null
+++ b/dx/tests/059-dex-call-super/Zorch.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Zorch
+{
+ public void zorch1() {
+ // This space intentionally left blank.
+ }
+
+ public int zorch2(int x) {
+ return 0;
+ }
+
+ public void test2() {
+ // This space intentionally left blank.
+ }
+}
diff --git a/dx/tests/059-dex-call-super/expected.txt b/dx/tests/059-dex-call-super/expected.txt
new file mode 100644
index 0000000..8bb3bde
--- /dev/null
+++ b/dx/tests/059-dex-call-super/expected.txt
@@ -0,0 +1,17 @@
+Blort.test1:()I:
+regs: 0004; ins: 0001; outs: 0002
+ 0000: move-object v0, v3
+ 0001: move-object v1, v0
+ 0002: invoke-super {v1}, Zorch.zorch1:()V
+ 0005: move-object v1, v0
+ 0006: const/4 v2, #int 5 // #5
+ 0007: invoke-super {v1, v2}, Zorch.zorch2:(I)I
+ 000a: move-result v1
+ 000b: move v0, v1
+ 000c: return v0
+Blort.test2:()V:
+regs: 0003; ins: 0001; outs: 0001
+ 0000: move-object v0, v2
+ 0001: move-object v1, v0
+ 0002: invoke-super {v1}, Zorch.test2:()V
+ 0005: return-void
diff --git a/dx/tests/059-dex-call-super/info.txt b/dx/tests/059-dex-call-super/info.txt
new file mode 100644
index 0000000..ff88814
--- /dev/null
+++ b/dx/tests/059-dex-call-super/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+a couple cases of superclass virtual method invocation work properly.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/059-dex-call-super/run b/dx/tests/059-dex-call-super/run
new file mode 100644
index 0000000..e5eb509
--- /dev/null
+++ b/dx/tests/059-dex-call-super/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+ --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/060-dex-call-static/Blort.java b/dx/tests/060-dex-call-static/Blort.java
new file mode 100644
index 0000000..8ad2e27
--- /dev/null
+++ b/dx/tests/060-dex-call-static/Blort.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ public static int test() {
+ Zorch.zorch1();
+ return Zorch.zorch2(5);
+ }
+}
diff --git a/dx/tests/060-dex-call-static/Zorch.java b/dx/tests/060-dex-call-static/Zorch.java
new file mode 100644
index 0000000..8ccc448
--- /dev/null
+++ b/dx/tests/060-dex-call-static/Zorch.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Zorch
+{
+ public static void zorch1() {
+ // This space intentionally left blank.
+ }
+
+ public static int zorch2(int x) {
+ return 1;
+ }
+}
diff --git a/dx/tests/060-dex-call-static/expected.txt b/dx/tests/060-dex-call-static/expected.txt
new file mode 100644
index 0000000..9432989
--- /dev/null
+++ b/dx/tests/060-dex-call-static/expected.txt
@@ -0,0 +1,7 @@
+Blort.test:()I:
+regs: 0001; ins: 0000; outs: 0001
+ 0000: invoke-static {}, Zorch.zorch1:()V
+ 0003: const/4 v0, #int 5 // #5
+ 0004: invoke-static {v0}, Zorch.zorch2:(I)I
+ 0007: move-result v0
+ 0008: return v0
diff --git a/dx/tests/060-dex-call-static/info.txt b/dx/tests/060-dex-call-static/info.txt
new file mode 100644
index 0000000..1253355
--- /dev/null
+++ b/dx/tests/060-dex-call-static/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+a couple cases of static method invocation work properly.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/060-dex-call-static/run b/dx/tests/060-dex-call-static/run
new file mode 100644
index 0000000..346856c
--- /dev/null
+++ b/dx/tests/060-dex-call-static/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . *.java
+dx --debug --dex --positions=none --no-locals \
+ --dump-method=Blort.test Blort.class
diff --git a/dx/tests/061-dex-try-catch/Blort.java b/dx/tests/061-dex-try-catch/Blort.java
new file mode 100644
index 0000000..903f40e
--- /dev/null
+++ b/dx/tests/061-dex-try-catch/Blort.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ public static void caught() {
+ // This space intentionally left blank.
+ }
+
+ public static void zorch(int x) {
+ // This space intentionally left blank.
+ }
+
+ public static void test1(int x) {
+ // In this test, the code being try-caught can't possibly throw.
+ try {
+ x = 0;
+ } catch (RuntimeException ex) {
+ caught();
+ }
+ }
+
+ public static void test2(String[] sa) {
+ // In this test, the code being try-caught doesn't contain any
+ // constant pool references.
+ try {
+ int x = sa.length;
+ } catch (RuntimeException ex) {
+ caught();
+ }
+ }
+
+ public static void test3() {
+ // In this test, the code being try-caught contains a constant
+ // pool reference.
+ try {
+ zorch(1);
+ } catch (RuntimeException ex) {
+ caught();
+ }
+ }
+
+ public static void test4(String[] sa) {
+ // In this test, the code being try-caught contains one
+ // throwing instruction that has a constant pool reference and
+ // one that doesn't.
+ try {
+ zorch(sa.length);
+ } catch (RuntimeException ex) {
+ caught();
+ }
+ }
+}
diff --git a/dx/tests/061-dex-try-catch/expected.txt b/dx/tests/061-dex-try-catch/expected.txt
new file mode 100644
index 0000000..d66f404
--- /dev/null
+++ b/dx/tests/061-dex-try-catch/expected.txt
@@ -0,0 +1,49 @@
+Blort.test1:(I)V:
+regs: 0004; ins: 0001; outs: 0000
+ 0000: move v0, v3
+ 0001: const/4 v2, #int 0 // #0
+ 0002: move v0, v2
+ 0003: return-void
+Blort.test2:([Ljava/lang/String;)V:
+regs: 0004; ins: 0001; outs: 0000
+ 0000: move-object v0, v3
+ 0001: move-object v2, v0
+ 0002: array-length v2, v2
+ 0003: move v1, v2
+ 0004: return-void
+ 0005: move-exception v2
+ 0006: move-object v1, v2
+ 0007: invoke-static {}, Blort.caught:()V
+ 000a: goto 0004 // -0006
+ catches
+ tries:
+ try 0002..0003
+ catch java.lang.RuntimeException -> 0005
+Blort.test3:()V:
+regs: 0002; ins: 0000; outs: 0001
+ 0000: const/4 v1, #int 1 // #1
+ 0001: invoke-static {v1}, Blort.zorch:(I)V
+ 0004: return-void
+ 0005: move-exception v1
+ 0006: move-object v0, v1
+ 0007: invoke-static {}, Blort.caught:()V
+ 000a: goto 0004 // -0006
+ catches
+ tries:
+ try 0001..0004
+ catch java.lang.RuntimeException -> 0005
+Blort.test4:([Ljava/lang/String;)V:
+regs: 0004; ins: 0001; outs: 0001
+ 0000: move-object v0, v3
+ 0001: move-object v2, v0
+ 0002: array-length v2, v2
+ 0003: invoke-static {v2}, Blort.zorch:(I)V
+ 0006: return-void
+ 0007: move-exception v2
+ 0008: move-object v1, v2
+ 0009: invoke-static {}, Blort.caught:()V
+ 000c: goto 0006 // -0006
+ catches
+ tries:
+ try 0002..0006
+ catch java.lang.RuntimeException -> 0007
diff --git a/dx/tests/061-dex-try-catch/info.txt b/dx/tests/061-dex-try-catch/info.txt
new file mode 100644
index 0000000..409b251
--- /dev/null
+++ b/dx/tests/061-dex-try-catch/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+a couple cases of try-catch work properly.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/061-dex-try-catch/run b/dx/tests/061-dex-try-catch/run
new file mode 100644
index 0000000..e5eb509
--- /dev/null
+++ b/dx/tests/061-dex-try-catch/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+ --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/062-dex-synch-method/Blort.java b/dx/tests/062-dex-synch-method/Blort.java
new file mode 100644
index 0000000..4aca417
--- /dev/null
+++ b/dx/tests/062-dex-synch-method/Blort.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ public synchronized void testInstance1() {
+ // This space intentionally left blank.
+ }
+
+ public synchronized void testInstance2(Object x) {
+ x.hashCode();
+ }
+
+ public synchronized int testInstance3(int x, int y, int z) {
+ if (x == 1) {
+ return 1;
+ } else {
+ return 2;
+ }
+ }
+
+ public synchronized long testInstance4(long x) {
+ if (x == 1) {
+ return 1;
+ } else {
+ return 2;
+ }
+ }
+
+ public synchronized void testInstance5() {
+ testInstance5();
+ }
+
+ public static synchronized void testStatic1() {
+ // This space intentionally left blank.
+ }
+
+ public static synchronized void testStatic2(Object x) {
+ x.hashCode();
+ }
+
+ public static synchronized int testStatic3(int x, int y, int z) {
+ if (x == 1) {
+ return 1;
+ } else {
+ return 2;
+ }
+ }
+
+ public static synchronized long testStatic4(long x) {
+ if (x == 1) {
+ return 1;
+ } else {
+ return 2;
+ }
+ }
+
+ public static synchronized void testStatic5() {
+ testStatic5();
+ }
+}
diff --git a/dx/tests/062-dex-synch-method/expected.txt b/dx/tests/062-dex-synch-method/expected.txt
new file mode 100644
index 0000000..5585038
--- /dev/null
+++ b/dx/tests/062-dex-synch-method/expected.txt
@@ -0,0 +1,146 @@
+Blort.testInstance1:()V:
+regs: 0003; ins: 0001; outs: 0000
+ 0000: move-object v0, v2
+ 0001: move-object v1, v2
+ 0002: monitor-enter v1
+ 0003: monitor-exit v1
+ 0004: return-void
+Blort.testInstance2:(Ljava/lang/Object;)V:
+regs: 0006; ins: 0002; outs: 0001
+ 0000: move-object v0, v4
+ 0001: move-object v1, v5
+ 0002: move-object v3, v4
+ 0003: monitor-enter v3
+ 0004: move-object v2, v1
+ 0005: invoke-virtual {v2}, java.lang.Object.hashCode:()I
+ 0008: move-result v2
+ 0009: monitor-exit v3
+ 000a: return-void
+ 000b: move-exception v0
+ 000c: monitor-exit v3
+ 000d: throw v0
+ catches
+ tries:
+ try 0005..0008
+ catch <any> -> 000b
+Blort.testInstance3:(III)I:
+regs: 000b; ins: 0004; outs: 0000
+ 0000: move-object v0, v7
+ 0001: move v1, v8
+ 0002: move v2, v9
+ 0003: move v3, v10
+ 0004: move-object v6, v7
+ 0005: monitor-enter v6
+ 0006: move v4, v1
+ 0007: const/4 v5, #int 1 // #1
+ 0008: if-ne v4, v5, 000e // +0006
+ 000a: const/4 v4, #int 1 // #1
+ 000b: move v0, v4
+ 000c: monitor-exit v6
+ 000d: return v0
+ 000e: const/4 v4, #int 2 // #2
+ 000f: move v0, v4
+ 0010: goto 000c // -0004
+Blort.testInstance4:(J)J:
+regs: 000b; ins: 0003; outs: 0000
+ 0000: move-object v0, v8
+ 0001: move-wide v1, v9
+ 0002: move-object v7, v8
+ 0003: monitor-enter v7
+ 0004: move-wide v3, v1
+ 0005: const-wide/16 v5, #long 1 // #0001
+ 0007: cmp-long v3, v3, v5
+ 0009: if-nez v3, 0010 // +0007
+ 000b: const-wide/16 v3, #long 1 // #0001
+ 000d: move-wide v0, v3
+ 000e: monitor-exit v7
+ 000f: return-wide v0
+ 0010: const-wide/16 v3, #long 2 // #0002
+ 0012: move-wide v0, v3
+ 0013: goto 000e // -0005
+Blort.testInstance5:()V:
+regs: 0004; ins: 0001; outs: 0001
+ 0000: move-object v0, v3
+ 0001: move-object v2, v3
+ 0002: monitor-enter v2
+ 0003: move-object v1, v0
+ 0004: invoke-virtual {v1}, Blort.testInstance5:()V
+ 0007: monitor-exit v2
+ 0008: return-void
+ 0009: move-exception v0
+ 000a: monitor-exit v2
+ 000b: throw v0
+ catches
+ tries:
+ try 0004..0007
+ catch <any> -> 0009
+Blort.testStatic1:()V:
+regs: 0002; ins: 0000; outs: 0000
+ 0000: const-class v1, Blort
+ 0002: monitor-enter v1
+ 0003: monitor-exit v1
+ 0004: return-void
+Blort.testStatic2:(Ljava/lang/Object;)V:
+regs: 0004; ins: 0001; outs: 0001
+ 0000: move-object v0, v3
+ 0001: const-class v2, Blort
+ 0003: monitor-enter v2
+ 0004: move-object v1, v0
+ 0005: invoke-virtual {v1}, java.lang.Object.hashCode:()I
+ 0008: move-result v1
+ 0009: monitor-exit v2
+ 000a: return-void
+ 000b: move-exception v0
+ 000c: monitor-exit v2
+ 000d: throw v0
+ catches
+ tries:
+ try 0005..0008
+ catch <any> -> 000b
+Blort.testStatic3:(III)I:
+regs: 0009; ins: 0003; outs: 0000
+ 0000: move v0, v6
+ 0001: move v1, v7
+ 0002: move v2, v8
+ 0003: const-class v5, Blort
+ 0005: monitor-enter v5
+ 0006: move v3, v0
+ 0007: const/4 v4, #int 1 // #1
+ 0008: if-ne v3, v4, 000e // +0006
+ 000a: const/4 v3, #int 1 // #1
+ 000b: move v0, v3
+ 000c: monitor-exit v5
+ 000d: return v0
+ 000e: const/4 v3, #int 2 // #2
+ 000f: move v0, v3
+ 0010: goto 000c // -0004
+Blort.testStatic4:(J)J:
+regs: 0009; ins: 0002; outs: 0000
+ 0000: move-wide v0, v7
+ 0001: const-class v6, Blort
+ 0003: monitor-enter v6
+ 0004: move-wide v2, v0
+ 0005: const-wide/16 v4, #long 1 // #0001
+ 0007: cmp-long v2, v2, v4
+ 0009: if-nez v2, 0010 // +0007
+ 000b: const-wide/16 v2, #long 1 // #0001
+ 000d: move-wide v0, v2
+ 000e: monitor-exit v6
+ 000f: return-wide v0
+ 0010: const-wide/16 v2, #long 2 // #0002
+ 0012: move-wide v0, v2
+ 0013: goto 000e // -0005
+Blort.testStatic5:()V:
+regs: 0002; ins: 0000; outs: 0000
+ 0000: const-class v1, Blort
+ 0002: monitor-enter v1
+ 0003: invoke-static {}, Blort.testStatic5:()V
+ 0006: monitor-exit v1
+ 0007: return-void
+ 0008: move-exception v0
+ 0009: monitor-exit v1
+ 000a: throw v0
+ catches
+ tries:
+ try 0003..0006
+ catch <any> -> 0008
diff --git a/dx/tests/062-dex-synch-method/info.txt b/dx/tests/062-dex-synch-method/info.txt
new file mode 100644
index 0000000..0e82727
--- /dev/null
+++ b/dx/tests/062-dex-synch-method/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+a couple cases of synchronized methods get converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/062-dex-synch-method/run b/dx/tests/062-dex-synch-method/run
new file mode 100644
index 0000000..e5eb509
--- /dev/null
+++ b/dx/tests/062-dex-synch-method/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+ --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/063-dex-empty-switch/Blort.java b/dx/tests/063-dex-empty-switch/Blort.java
new file mode 100644
index 0000000..f538995
--- /dev/null
+++ b/dx/tests/063-dex-empty-switch/Blort.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ public int test1(int x) {
+ switch (x) {
+ default: return 1;
+ }
+ }
+
+ public int test2(int x) {
+ switch (x) {
+ default: x = 1;
+ }
+
+ return x;
+ }
+}
diff --git a/dx/tests/063-dex-empty-switch/expected.txt b/dx/tests/063-dex-empty-switch/expected.txt
new file mode 100644
index 0000000..e4d1a46
--- /dev/null
+++ b/dx/tests/063-dex-empty-switch/expected.txt
@@ -0,0 +1,18 @@
+Blort.test1:(I)I:
+regs: 0005; ins: 0002; outs: 0000
+ 0000: move-object v0, v3
+ 0001: move v1, v4
+ 0002: move v2, v1
+ 0003: const/4 v2, #int 1 // #1
+ 0004: move v0, v2
+ 0005: return v0
+Blort.test2:(I)I:
+regs: 0005; ins: 0002; outs: 0000
+ 0000: move-object v0, v3
+ 0001: move v1, v4
+ 0002: move v2, v1
+ 0003: const/4 v2, #int 1 // #1
+ 0004: move v1, v2
+ 0005: move v2, v1
+ 0006: move v0, v2
+ 0007: return v0
diff --git a/dx/tests/063-dex-empty-switch/info.txt b/dx/tests/063-dex-empty-switch/info.txt
new file mode 100644
index 0000000..38f212b
--- /dev/null
+++ b/dx/tests/063-dex-empty-switch/info.txt
@@ -0,0 +1,7 @@
+This is a smoke test of dex conversion, which checks to see that
+a couple cases of empty (that is, default-only) switch statements
+get converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/063-dex-empty-switch/run b/dx/tests/063-dex-empty-switch/run
new file mode 100644
index 0000000..e5eb509
--- /dev/null
+++ b/dx/tests/063-dex-empty-switch/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+ --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/064-dex-array-access/Blort.java b/dx/tests/064-dex-array-access/Blort.java
new file mode 100644
index 0000000..fa03ec0
--- /dev/null
+++ b/dx/tests/064-dex-array-access/Blort.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ public boolean test01(boolean[] x) {
+ x[0] = true;
+ return x[1];
+ }
+
+ public byte test02(byte[] x) {
+ x[0] = 5;
+ return x[1];
+ }
+
+ public short test03(short[] x) {
+ x[0] = 5;
+ return x[1];
+ }
+
+ public char test04(char[] x) {
+ x[0] = 5;
+ return x[1];
+ }
+
+ public int test05(int[] x) {
+ x[0] = 5;
+ return x[1];
+ }
+
+ public long test06(long[] x) {
+ x[0] = 5;
+ return x[1];
+ }
+
+ public float test07(float[] x) {
+ x[0] = 2.0f;
+ return x[1];
+ }
+
+ public double test08(double[] x) {
+ x[0] = 2.0;
+ return x[1];
+ }
+
+ public Object test09(Object[] x) {
+ x[0] = null;
+ return x[1];
+ }
+
+ public static Object test10(Object[][] x) {
+ x[0][0] = null;
+ return x[1][2];
+ }
+
+ public static int test11(Object x) {
+ int[][][] arr = (int[][][]) x;
+ arr[0][0][0] = 123;
+ return arr[1][2][3];
+ }
+}
diff --git a/dx/tests/064-dex-array-access/expected.txt b/dx/tests/064-dex-array-access/expected.txt
new file mode 100644
index 0000000..ae251e7
--- /dev/null
+++ b/dx/tests/064-dex-array-access/expected.txt
@@ -0,0 +1,157 @@
+Blort.test01:([Z)Z:
+regs: 0007; ins: 0002; outs: 0000
+ 0000: move-object v0, v5
+ 0001: move-object v1, v6
+ 0002: move-object v2, v1
+ 0003: const/4 v3, #int 0 // #0
+ 0004: const/4 v4, #int 1 // #1
+ 0005: aput-boolean v4, v2, v3
+ 0007: move-object v2, v1
+ 0008: const/4 v3, #int 1 // #1
+ 0009: aget-boolean v2, v2, v3
+ 000b: move v0, v2
+ 000c: return v0
+Blort.test02:([B)B:
+regs: 0007; ins: 0002; outs: 0000
+ 0000: move-object v0, v5
+ 0001: move-object v1, v6
+ 0002: move-object v2, v1
+ 0003: const/4 v3, #int 0 // #0
+ 0004: const/4 v4, #int 5 // #5
+ 0005: aput-byte v4, v2, v3
+ 0007: move-object v2, v1
+ 0008: const/4 v3, #int 1 // #1
+ 0009: aget-byte v2, v2, v3
+ 000b: move v0, v2
+ 000c: return v0
+Blort.test03:([S)S:
+regs: 0007; ins: 0002; outs: 0000
+ 0000: move-object v0, v5
+ 0001: move-object v1, v6
+ 0002: move-object v2, v1
+ 0003: const/4 v3, #int 0 // #0
+ 0004: const/4 v4, #int 5 // #5
+ 0005: aput-short v4, v2, v3
+ 0007: move-object v2, v1
+ 0008: const/4 v3, #int 1 // #1
+ 0009: aget-short v2, v2, v3
+ 000b: move v0, v2
+ 000c: return v0
+Blort.test04:([C)C:
+regs: 0007; ins: 0002; outs: 0000
+ 0000: move-object v0, v5
+ 0001: move-object v1, v6
+ 0002: move-object v2, v1
+ 0003: const/4 v3, #int 0 // #0
+ 0004: const/4 v4, #int 5 // #5
+ 0005: aput-char v4, v2, v3
+ 0007: move-object v2, v1
+ 0008: const/4 v3, #int 1 // #1
+ 0009: aget-char v2, v2, v3
+ 000b: move v0, v2
+ 000c: return v0
+Blort.test05:([I)I:
+regs: 0007; ins: 0002; outs: 0000
+ 0000: move-object v0, v5
+ 0001: move-object v1, v6
+ 0002: move-object v2, v1
+ 0003: const/4 v3, #int 0 // #0
+ 0004: const/4 v4, #int 5 // #5
+ 0005: aput v4, v2, v3
+ 0007: move-object v2, v1
+ 0008: const/4 v3, #int 1 // #1
+ 0009: aget v2, v2, v3
+ 000b: move v0, v2
+ 000c: return v0
+Blort.test06:([J)J:
+regs: 0008; ins: 0002; outs: 0000
+ 0000: move-object v0, v6
+ 0001: move-object v1, v7
+ 0002: move-object v2, v1
+ 0003: const/4 v3, #int 0 // #0
+ 0004: const-wide/16 v4, #long 5 // #0005
+ 0006: aput-wide v4, v2, v3
+ 0008: move-object v2, v1
+ 0009: const/4 v3, #int 1 // #1
+ 000a: aget-wide v2, v2, v3
+ 000c: move-wide v0, v2
+ 000d: return-wide v0
+Blort.test07:([F)F:
+regs: 0007; ins: 0002; outs: 0000
+ 0000: move-object v0, v5
+ 0001: move-object v1, v6
+ 0002: move-object v2, v1
+ 0003: const/4 v3, #int 0 // #0
+ 0004: const/high16 v4, #float 2.0 // #40000000
+ 0006: aput v4, v2, v3
+ 0008: move-object v2, v1
+ 0009: const/4 v3, #int 1 // #1
+ 000a: aget v2, v2, v3
+ 000c: move v0, v2
+ 000d: return v0
+Blort.test08:([D)D:
+regs: 0008; ins: 0002; outs: 0000
+ 0000: move-object v0, v6
+ 0001: move-object v1, v7
+ 0002: move-object v2, v1
+ 0003: const/4 v3, #int 0 // #0
+ 0004: const-wide/high16 v4, #double 2.0 // #4000000000000000
+ 0006: aput-wide v4, v2, v3
+ 0008: move-object v2, v1
+ 0009: const/4 v3, #int 1 // #1
+ 000a: aget-wide v2, v2, v3
+ 000c: move-wide v0, v2
+ 000d: return-wide v0
+Blort.test09:([Ljava/lang/Object;)Ljava/lang/Object;:
+regs: 0007; ins: 0002; outs: 0000
+ 0000: move-object v0, v5
+ 0001: move-object v1, v6
+ 0002: move-object v2, v1
+ 0003: const/4 v3, #int 0 // #0
+ 0004: const/4 v4, #null // #0
+ 0005: aput-object v4, v2, v3
+ 0007: move-object v2, v1
+ 0008: const/4 v3, #int 1 // #1
+ 0009: aget-object v2, v2, v3
+ 000b: move-object v0, v2
+ 000c: return-object v0
+Blort.test10:([[Ljava/lang/Object;)Ljava/lang/Object;:
+regs: 0005; ins: 0001; outs: 0000
+ 0000: move-object v0, v4
+ 0001: move-object v1, v0
+ 0002: const/4 v2, #int 0 // #0
+ 0003: aget-object v1, v1, v2
+ 0005: const/4 v2, #int 0 // #0
+ 0006: const/4 v3, #null // #0
+ 0007: aput-object v3, v1, v2
+ 0009: move-object v1, v0
+ 000a: const/4 v2, #int 1 // #1
+ 000b: aget-object v1, v1, v2
+ 000d: const/4 v2, #int 2 // #2
+ 000e: aget-object v1, v1, v2
+ 0010: move-object v0, v1
+ 0011: return-object v0
+Blort.test11:(Ljava/lang/Object;)I:
+regs: 0006; ins: 0001; outs: 0000
+ 0000: move-object v0, v5
+ 0001: move-object v2, v0
+ 0002: check-cast v2, int[][][]
+ 0004: check-cast v2, int[][][]
+ 0006: move-object v1, v2
+ 0007: move-object v2, v1
+ 0008: const/4 v3, #int 0 // #0
+ 0009: aget-object v2, v2, v3
+ 000b: const/4 v3, #int 0 // #0
+ 000c: aget-object v2, v2, v3
+ 000e: const/4 v3, #int 0 // #0
+ 000f: const/16 v4, #int 123 // #007b
+ 0011: aput v4, v2, v3
+ 0013: move-object v2, v1
+ 0014: const/4 v3, #int 1 // #1
+ 0015: aget-object v2, v2, v3
+ 0017: const/4 v3, #int 2 // #2
+ 0018: aget-object v2, v2, v3
+ 001a: const/4 v3, #int 3 // #3
+ 001b: aget v2, v2, v3
+ 001d: move v0, v2
+ 001e: return v0
diff --git a/dx/tests/064-dex-array-access/info.txt b/dx/tests/064-dex-array-access/info.txt
new file mode 100644
index 0000000..8f81663
--- /dev/null
+++ b/dx/tests/064-dex-array-access/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+a few cases of array access get converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/064-dex-array-access/run b/dx/tests/064-dex-array-access/run
new file mode 100644
index 0000000..e5eb509
--- /dev/null
+++ b/dx/tests/064-dex-array-access/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+ --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/065-dex-new-array/Blort.java b/dx/tests/065-dex-new-array/Blort.java
new file mode 100644
index 0000000..7a7eaa4
--- /dev/null
+++ b/dx/tests/065-dex-new-array/Blort.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ public boolean[] test1() {
+ return new boolean[1];
+ }
+
+ public byte[] test2() {
+ return new byte[1];
+ }
+
+ public short[] test3() {
+ return new short[1];
+ }
+
+ public char[] test4() {
+ return new char[1];
+ }
+
+ public int[] test5() {
+ return new int[1];
+ }
+
+ public long[] test6() {
+ return new long[1];
+ }
+
+ public float[] test7() {
+ return new float[1];
+ }
+
+ public double[] test8() {
+ return new double[1];
+ }
+
+ public Object[] test9() {
+ return new Object[1];
+ }
+}
diff --git a/dx/tests/065-dex-new-array/expected.txt b/dx/tests/065-dex-new-array/expected.txt
new file mode 100644
index 0000000..0b26182
--- /dev/null
+++ b/dx/tests/065-dex-new-array/expected.txt
@@ -0,0 +1,63 @@
+Blort.test1:()[Z:
+regs: 0003; ins: 0001; outs: 0000
+ 0000: move-object v0, v2
+ 0001: const/4 v1, #int 1 // #1
+ 0002: new-array v1, v1, boolean[]
+ 0004: move-object v0, v1
+ 0005: return-object v0
+Blort.test2:()[B:
+regs: 0003; ins: 0001; outs: 0000
+ 0000: move-object v0, v2
+ 0001: const/4 v1, #int 1 // #1
+ 0002: new-array v1, v1, byte[]
+ 0004: move-object v0, v1
+ 0005: return-object v0
+Blort.test3:()[S:
+regs: 0003; ins: 0001; outs: 0000
+ 0000: move-object v0, v2
+ 0001: const/4 v1, #int 1 // #1
+ 0002: new-array v1, v1, short[]
+ 0004: move-object v0, v1
+ 0005: return-object v0
+Blort.test4:()[C:
+regs: 0003; ins: 0001; outs: 0000
+ 0000: move-object v0, v2
+ 0001: const/4 v1, #int 1 // #1
+ 0002: new-array v1, v1, char[]
+ 0004: move-object v0, v1
+ 0005: return-object v0
+Blort.test5:()[I:
+regs: 0003; ins: 0001; outs: 0000
+ 0000: move-object v0, v2
+ 0001: const/4 v1, #int 1 // #1
+ 0002: new-array v1, v1, int[]
+ 0004: move-object v0, v1
+ 0005: return-object v0
+Blort.test6:()[J:
+regs: 0003; ins: 0001; outs: 0000
+ 0000: move-object v0, v2
+ 0001: const/4 v1, #int 1 // #1
+ 0002: new-array v1, v1, long[]
+ 0004: move-object v0, v1
+ 0005: return-object v0
+Blort.test7:()[F:
+regs: 0003; ins: 0001; outs: 0000
+ 0000: move-object v0, v2
+ 0001: const/4 v1, #int 1 // #1
+ 0002: new-array v1, v1, float[]
+ 0004: move-object v0, v1
+ 0005: return-object v0
+Blort.test8:()[D:
+regs: 0003; ins: 0001; outs: 0000
+ 0000: move-object v0, v2
+ 0001: const/4 v1, #int 1 // #1
+ 0002: new-array v1, v1, double[]
+ 0004: move-object v0, v1
+ 0005: return-object v0
+Blort.test9:()[Ljava/lang/Object;:
+regs: 0003; ins: 0001; outs: 0000
+ 0000: move-object v0, v2
+ 0001: const/4 v1, #int 1 // #1
+ 0002: new-array v1, v1, java.lang.Object[]
+ 0004: move-object v0, v1
+ 0005: return-object v0
diff --git a/dx/tests/065-dex-new-array/info.txt b/dx/tests/065-dex-new-array/info.txt
new file mode 100644
index 0000000..f4d2cc5
--- /dev/null
+++ b/dx/tests/065-dex-new-array/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+a few cases of array construction get converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/065-dex-new-array/run b/dx/tests/065-dex-new-array/run
new file mode 100644
index 0000000..e5eb509
--- /dev/null
+++ b/dx/tests/065-dex-new-array/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+ --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/066-dex-try-catch-rethrow/Blort.java b/dx/tests/066-dex-try-catch-rethrow/Blort.java
new file mode 100644
index 0000000..cdb00ea
--- /dev/null
+++ b/dx/tests/066-dex-try-catch-rethrow/Blort.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ public static Object zorch1(String s) {
+ return null;
+ }
+
+ public static void test1() {
+ try {
+ zorch1("x");
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ public static void zorch2(String s) {
+ // This space intentionally left blank.
+ }
+
+ public static void test2() {
+ try {
+ zorch2("x");
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ public static int zorch3(String s) {
+ return 0;
+ }
+
+ public static void test3() {
+ try {
+ zorch3("x");
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ public static Object zorch4(int x) {
+ return null;
+ }
+
+ public static void test4() {
+ try {
+ zorch4(1);
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ public static Object zorch5(int x) {
+ return null;
+ }
+
+ public static Object test5() {
+ try {
+ return zorch5(1);
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+}
diff --git a/dx/tests/066-dex-try-catch-rethrow/expected.txt b/dx/tests/066-dex-try-catch-rethrow/expected.txt
new file mode 100644
index 0000000..13af56c
--- /dev/null
+++ b/dx/tests/066-dex-try-catch-rethrow/expected.txt
@@ -0,0 +1,95 @@
+Blort.test1:()V:
+regs: 0005; ins: 0000; outs: 0002
+ 0000: const-string v1, "x"
+ 0002: invoke-static {v1}, Blort.zorch1:(Ljava/lang/String;)Ljava/lang/Object;
+ 0005: move-result-object v1
+ 0006: return-void
+ 0007: move-exception v1
+ 0008: move-object v0, v1
+ 0009: new-instance v1, java.lang.RuntimeException
+ 000b: move-object v4, v1
+ 000c: move-object v1, v4
+ 000d: move-object v2, v4
+ 000e: move-object v3, v0
+ 000f: invoke-direct {v2, v3}, java.lang.RuntimeException.<init>:(Ljava/lang/Throwable;)V
+ 0012: throw v1
+ catches
+ tries:
+ try 0000..0005
+ catch java.lang.Exception -> 0007
+Blort.test2:()V:
+regs: 0005; ins: 0000; outs: 0002
+ 0000: const-string v1, "x"
+ 0002: invoke-static {v1}, Blort.zorch2:(Ljava/lang/String;)V
+ 0005: return-void
+ 0006: move-exception v1
+ 0007: move-object v0, v1
+ 0008: new-instance v1, java.lang.RuntimeException
+ 000a: move-object v4, v1
+ 000b: move-object v1, v4
+ 000c: move-object v2, v4
+ 000d: move-object v3, v0
+ 000e: invoke-direct {v2, v3}, java.lang.RuntimeException.<init>:(Ljava/lang/Throwable;)V
+ 0011: throw v1
+ catches
+ tries:
+ try 0000..0005
+ catch java.lang.Exception -> 0006
+Blort.test3:()V:
+regs: 0005; ins: 0000; outs: 0002
+ 0000: const-string v1, "x"
+ 0002: invoke-static {v1}, Blort.zorch3:(Ljava/lang/String;)I
+ 0005: move-result v1
+ 0006: return-void
+ 0007: move-exception v1
+ 0008: move-object v0, v1
+ 0009: new-instance v1, java.lang.RuntimeException
+ 000b: move-object v4, v1
+ 000c: move-object v1, v4
+ 000d: move-object v2, v4
+ 000e: move-object v3, v0
+ 000f: invoke-direct {v2, v3}, java.lang.RuntimeException.<init>:(Ljava/lang/Throwable;)V
+ 0012: throw v1
+ catches
+ tries:
+ try 0000..0005
+ catch java.lang.Exception -> 0007
+Blort.test4:()V:
+regs: 0005; ins: 0000; outs: 0002
+ 0000: const/4 v1, #int 1 // #1
+ 0001: invoke-static {v1}, Blort.zorch4:(I)Ljava/lang/Object;
+ 0004: move-result-object v1
+ 0005: return-void
+ 0006: move-exception v1
+ 0007: move-object v0, v1
+ 0008: new-instance v1, java.lang.RuntimeException
+ 000a: move-object v4, v1
+ 000b: move-object v1, v4
+ 000c: move-object v2, v4
+ 000d: move-object v3, v0
+ 000e: invoke-direct {v2, v3}, java.lang.RuntimeException.<init>:(Ljava/lang/Throwable;)V
+ 0011: throw v1
+ catches
+ tries:
+ try 0001..0004
+ catch java.lang.Exception -> 0006
+Blort.test5:()Ljava/lang/Object;:
+regs: 0005; ins: 0000; outs: 0002
+ 0000: const/4 v1, #int 1 // #1
+ 0001: invoke-static {v1}, Blort.zorch5:(I)Ljava/lang/Object;
+ 0004: move-result-object v1
+ 0005: move-object v0, v1
+ 0006: return-object v0
+ 0007: move-exception v1
+ 0008: move-object v0, v1
+ 0009: new-instance v1, java.lang.RuntimeException
+ 000b: move-object v4, v1
+ 000c: move-object v1, v4
+ 000d: move-object v2, v4
+ 000e: move-object v3, v0
+ 000f: invoke-direct {v2, v3}, java.lang.RuntimeException.<init>:(Ljava/lang/Throwable;)V
+ 0012: throw v1
+ catches
+ tries:
+ try 0001..0004
+ catch java.lang.Exception -> 0007
diff --git a/dx/tests/066-dex-try-catch-rethrow/info.txt b/dx/tests/066-dex-try-catch-rethrow/info.txt
new file mode 100644
index 0000000..b2696b8
--- /dev/null
+++ b/dx/tests/066-dex-try-catch-rethrow/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which looks at a few cases of
+a try-catch where the exception handler rethrows.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/066-dex-try-catch-rethrow/run b/dx/tests/066-dex-try-catch-rethrow/run
new file mode 100644
index 0000000..e5eb509
--- /dev/null
+++ b/dx/tests/066-dex-try-catch-rethrow/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+ --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/067-dex-switch-and-try/Blort.java b/dx/tests/067-dex-switch-and-try/Blort.java
new file mode 100644
index 0000000..e6435e1
--- /dev/null
+++ b/dx/tests/067-dex-switch-and-try/Blort.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ static public void blort() {
+ // This space intentionally left blank.
+ }
+
+ // This test has a try-catch but the try code can't possibly throw.
+ public int test1(int x) {
+ try {
+ switch (x) {
+ case 1: {
+ x = 10;
+ break;
+ }
+ case 2: {
+ x = 20;
+ break;
+ }
+ }
+ } catch (RuntimeException ex) {
+ // Ignore it.
+ }
+
+ return x;
+ }
+
+ // This test has a try-catch where the try code can theoretically throw.
+ public int test2(int x) {
+ try {
+ switch (x) {
+ case 1: {
+ x = 10;
+ blort();
+ break;
+ }
+ case 2: {
+ x = 20;
+ break;
+ }
+ }
+ } catch (RuntimeException ex) {
+ // Ignore it.
+ }
+
+ return x;
+ }
+
+ // This test has a switch with a case that has a try-catch where
+ // the try code can theoretically throw, but it would be caught
+ // inside the case itself.
+ public int test3(int x) {
+ switch (x) {
+ case 1: {
+ try {
+ x = 10;
+ blort();
+ } catch (RuntimeException ex) {
+ // Ignore it.
+ }
+ break;
+ }
+ case 2: {
+ x = 20;
+ break;
+ }
+ }
+
+ return x;
+ }
+
+ // This test has a try-catch that has a switch with a case that
+ // has a try-catch where the try code can theoretically throw, but
+ // it would be caught inside the case itself, so the outer
+ // exception handler should be considered dead.
+ public int test4(int x) {
+ try {
+ switch (x) {
+ case 1: {
+ try {
+ x = 10;
+ blort();
+ } catch (RuntimeException ex) {
+ // Ignore it.
+ }
+ break;
+ }
+ case 2: {
+ x = 20;
+ break;
+ }
+ }
+ } catch (RuntimeException ex) {
+ return 4;
+ }
+
+ return x;
+ }
+}
diff --git a/dx/tests/067-dex-switch-and-try/expected.txt b/dx/tests/067-dex-switch-and-try/expected.txt
new file mode 100644
index 0000000..5e55bf4
--- /dev/null
+++ b/dx/tests/067-dex-switch-and-try/expected.txt
@@ -0,0 +1,100 @@
+Blort.test1:(I)I:
+regs: 0006; ins: 0002; outs: 0000
+ 0000: move-object v0, v4
+ 0001: move v1, v5
+ 0002: move v3, v1
+ 0003: packed-switch v3, 0012 // +000f
+ 0006: move v3, v1
+ 0007: move v0, v3
+ 0008: return v0
+ 0009: const/16 v3, #int 10 // #000a
+ 000b: move v1, v3
+ 000c: goto 0006 // -0006
+ 000d: const/16 v3, #int 20 // #0014
+ 000f: move v1, v3
+ 0010: goto 0006 // -000a
+ 0011: nop // spacer
+ 0012: packed-switch-data // for switch @ 0003
+ 1: 00000009 // +00000006
+ 2: 0000000d // +0000000a
+Blort.test2:(I)I:
+regs: 0006; ins: 0002; outs: 0000
+ 0000: move-object v0, v4
+ 0001: move v1, v5
+ 0002: move v3, v1
+ 0003: packed-switch v3, 0018 // +0015
+ 0006: move v3, v1
+ 0007: move v0, v3
+ 0008: return v0
+ 0009: const/16 v3, #int 10 // #000a
+ 000b: move v1, v3
+ 000c: invoke-static {}, Blort.blort:()V
+ 000f: goto 0006 // -0009
+ 0010: const/16 v3, #int 20 // #0014
+ 0012: move v1, v3
+ 0013: goto 0006 // -000d
+ 0014: move-exception v3
+ 0015: move-object v2, v3
+ 0016: goto 0006 // -0010
+ 0017: nop // spacer
+ 0018: packed-switch-data // for switch @ 0003
+ 1: 00000009 // +00000006
+ 2: 00000010 // +0000000d
+ catches
+ tries:
+ try 000c..000f
+ catch java.lang.RuntimeException -> 0014
+Blort.test3:(I)I:
+regs: 0006; ins: 0002; outs: 0000
+ 0000: move-object v0, v4
+ 0001: move v1, v5
+ 0002: move v3, v1
+ 0003: packed-switch v3, 0018 // +0015
+ 0006: move v3, v1
+ 0007: move v0, v3
+ 0008: return v0
+ 0009: const/16 v3, #int 10 // #000a
+ 000b: move v1, v3
+ 000c: invoke-static {}, Blort.blort:()V
+ 000f: goto 0006 // -0009
+ 0010: move-exception v3
+ 0011: move-object v2, v3
+ 0012: goto 0006 // -000c
+ 0013: const/16 v3, #int 20 // #0014
+ 0015: move v1, v3
+ 0016: goto 0006 // -0010
+ 0017: nop // spacer
+ 0018: packed-switch-data // for switch @ 0003
+ 1: 00000009 // +00000006
+ 2: 00000013 // +00000010
+ catches
+ tries:
+ try 000c..000f
+ catch java.lang.RuntimeException -> 0010
+Blort.test4:(I)I:
+regs: 0006; ins: 0002; outs: 0000
+ 0000: move-object v0, v4
+ 0001: move v1, v5
+ 0002: move v3, v1
+ 0003: packed-switch v3, 0018 // +0015
+ 0006: move v3, v1
+ 0007: move v0, v3
+ 0008: return v0
+ 0009: const/16 v3, #int 10 // #000a
+ 000b: move v1, v3
+ 000c: invoke-static {}, Blort.blort:()V
+ 000f: goto 0006 // -0009
+ 0010: move-exception v3
+ 0011: move-object v2, v3
+ 0012: goto 0006 // -000c
+ 0013: const/16 v3, #int 20 // #0014
+ 0015: move v1, v3
+ 0016: goto 0006 // -0010
+ 0017: nop // spacer
+ 0018: packed-switch-data // for switch @ 0003
+ 1: 00000009 // +00000006
+ 2: 00000013 // +00000010
+ catches
+ tries:
+ try 000c..000f
+ catch java.lang.RuntimeException -> 0010
diff --git a/dx/tests/067-dex-switch-and-try/info.txt b/dx/tests/067-dex-switch-and-try/info.txt
new file mode 100644
index 0000000..68e8117
--- /dev/null
+++ b/dx/tests/067-dex-switch-and-try/info.txt
@@ -0,0 +1,7 @@
+This is a smoke test of dex conversion, which looks at a couple cases of
+embedding a switch statement in a try-catch and vice versa. This test
+was created specifically because of a bug with exactly this situation.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/067-dex-switch-and-try/run b/dx/tests/067-dex-switch-and-try/run
new file mode 100644
index 0000000..e5eb509
--- /dev/null
+++ b/dx/tests/067-dex-switch-and-try/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+ --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/068-dex-infinite-loop/Blort.java b/dx/tests/068-dex-infinite-loop/Blort.java
new file mode 100644
index 0000000..f026407
--- /dev/null
+++ b/dx/tests/068-dex-infinite-loop/Blort.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ public static boolean zorch() {
+ return true;
+ }
+
+ public static void test1() {
+ for (;;) {
+ // This space intentionally left blank.
+ }
+ }
+
+ public static void test2() {
+ while (zorch()) {
+ // This space intentionally left blank.
+ }
+ }
+
+ public static void test3() {
+ while (zorch()) {
+ zorch();
+ }
+ }
+
+ public static void test4() {
+ for (;;) {
+ if (zorch()) {
+ break;
+ }
+
+ while (zorch()) {
+ zorch();
+ }
+ }
+ }
+}
diff --git a/dx/tests/068-dex-infinite-loop/expected.txt b/dx/tests/068-dex-infinite-loop/expected.txt
new file mode 100644
index 0000000..1a84bd9
--- /dev/null
+++ b/dx/tests/068-dex-infinite-loop/expected.txt
@@ -0,0 +1,28 @@
+Blort.test1:()V:
+regs: 0000; ins: 0000; outs: 0000
+ 0000: goto/32 0000 // +0000
+Blort.test2:()V:
+regs: 0001; ins: 0000; outs: 0000
+ 0000: invoke-static {}, Blort.zorch:()Z
+ 0003: move-result v0
+ 0004: if-nez v0, 0000 // -0004
+ 0006: return-void
+Blort.test3:()V:
+regs: 0001; ins: 0000; outs: 0000
+ 0000: invoke-static {}, Blort.zorch:()Z
+ 0003: move-result v0
+ 0004: if-eqz v0, 000a // +0006
+ 0006: invoke-static {}, Blort.zorch:()Z
+ 0009: goto 0000 // -0009
+ 000a: return-void
+Blort.test4:()V:
+regs: 0001; ins: 0000; outs: 0000
+ 0000: invoke-static {}, Blort.zorch:()Z
+ 0003: move-result v0
+ 0004: if-eqz v0, 0007 // +0003
+ 0006: return-void
+ 0007: invoke-static {}, Blort.zorch:()Z
+ 000a: move-result v0
+ 000b: if-eqz v0, 0000 // -000b
+ 000d: invoke-static {}, Blort.zorch:()Z
+ 0010: goto 0007 // -0009
diff --git a/dx/tests/068-dex-infinite-loop/info.txt b/dx/tests/068-dex-infinite-loop/info.txt
new file mode 100644
index 0000000..358f0a8
--- /dev/null
+++ b/dx/tests/068-dex-infinite-loop/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which makes sure that a couple
+cases of (potentially) infinite loops translate reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/068-dex-infinite-loop/run b/dx/tests/068-dex-infinite-loop/run
new file mode 100644
index 0000000..3fe95cc
--- /dev/null
+++ b/dx/tests/068-dex-infinite-loop/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . *.java
+dx --debug --dex --positions=none --no-locals \
+ --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/069-dex-source-position/Blort.java b/dx/tests/069-dex-source-position/Blort.java
new file mode 100644
index 0000000..8c0c5c0
--- /dev/null
+++ b/dx/tests/069-dex-source-position/Blort.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ public static int test(int x) {
+ if (x == 0) { // line 6
+ return 1; // line 7
+ } else {
+ try {
+ x = test(x - 1); // line 10
+ } catch (RuntimeException ex) { // line 11
+ return 2; // line 12
+ }
+ x += test(x - 2); // line 14
+ return x; // line 15
+ }
+ }
+}
diff --git a/dx/tests/069-dex-source-position/expected.txt b/dx/tests/069-dex-source-position/expected.txt
new file mode 100644
index 0000000..853ee65
--- /dev/null
+++ b/dx/tests/069-dex-source-position/expected.txt
@@ -0,0 +1,134 @@
+Blort.test:(I)I:
+regs: 0006; ins: 0001; outs: 0001
+ 0000: move v0, v5
+ 0001: move v2, v0
+ 0002: if-nez v2, 0007 // +0005
+ 0004: const/4 v2, #int 1 // #1
+ 0005: move v0, v2
+ 0006: return v0
+ 0007: move v2, v0
+ 0008: const/4 v3, #int 1 // #1
+ 0009: sub-int/2addr v2, v3
+ 000a: invoke-static {v2}, Blort.test:(I)I
+ 000d: move-result v2
+ 000e: move v0, v2
+ 000f: move v2, v0
+ 0010: move v3, v0
+ 0011: const/4 v4, #int 2 // #2
+ 0012: sub-int/2addr v3, v4
+ 0013: invoke-static {v3}, Blort.test:(I)I
+ 0016: move-result v3
+ 0017: add-int/2addr v2, v3
+ 0018: move v0, v2
+ 0019: move v2, v0
+ 001a: move v0, v2
+ 001b: goto 0006 // -0015
+ 001c: move-exception v2
+ 001d: move-object v1, v2
+ 001e: const/4 v2, #int 2 // #2
+ 001f: move v0, v2
+ 0020: goto 0006 // -001a
+ catches
+ tries:
+ try 000a..000d
+ catch java.lang.RuntimeException -> 001c
+Blort.test:(I)I:
+regs: 0006; ins: 0001; outs: 0001
+ 0000: move v0, v5
+ 0001: move v2, v0
+ 0002: if-nez v2, 0007 // +0005
+ 0004: const/4 v2, #int 1 // #1
+ 0005: move v0, v2
+ 0006: return v0
+ 0007: move v2, v0
+ 0008: const/4 v3, #int 1 // #1
+ 0009: sub-int/2addr v2, v3
+ 000a: invoke-static {v2}, Blort.test:(I)I
+ 000d: move-result v2
+ 000e: move v0, v2
+ 000f: move v2, v0
+ 0010: move v3, v0
+ 0011: const/4 v4, #int 2 // #2
+ 0012: sub-int/2addr v3, v4
+ 0013: invoke-static {v3}, Blort.test:(I)I
+ 0016: move-result v3
+ 0017: add-int/2addr v2, v3
+ 0018: move v0, v2
+ 0019: move v2, v0
+ 001a: move v0, v2
+ 001b: goto 0006 // -0015
+ 001c: move-exception v2
+ 001d: move-object v1, v2
+ 001e: const/4 v2, #int 2 // #2
+ 001f: move v0, v2
+ 0020: goto 0006 // -001a
+ catches
+ tries:
+ try 000a..000d
+ catch java.lang.RuntimeException -> 001c
+ debug info
+ line_start: 20
+ parameters_size: 0001
+ parameter <unnamed> v5
+ 0000: prologue end
+ 0000: line 20
+ 0004: line 21
+ 0006: line 29
+ line = 24
+ 0007: line 24
+ 000f: line 28
+ 0019: line 29
+ 001c: line 25
+ 001e: line 26
+ end sequence
+ source file: "Blort.java"
+Blort.test:(I)I:
+regs: 0006; ins: 0001; outs: 0001
+ 0000: move v0, v5
+ 0001: move v2, v0
+ 0002: if-nez v2, 0007 // +0005
+ 0004: const/4 v2, #int 1 // #1
+ 0005: move v0, v2
+ 0006: return v0
+ 0007: move v2, v0
+ 0008: const/4 v3, #int 1 // #1
+ 0009: sub-int/2addr v2, v3
+ 000a: invoke-static {v2}, Blort.test:(I)I
+ 000d: move-result v2
+ 000e: move v0, v2
+ 000f: move v2, v0
+ 0010: move v3, v0
+ 0011: const/4 v4, #int 2 // #2
+ 0012: sub-int/2addr v3, v4
+ 0013: invoke-static {v3}, Blort.test:(I)I
+ 0016: move-result v3
+ 0017: add-int/2addr v2, v3
+ 0018: move v0, v2
+ 0019: move v2, v0
+ 001a: move v0, v2
+ 001b: goto 0006 // -0015
+ 001c: move-exception v2
+ 001d: move-object v1, v2
+ 001e: const/4 v2, #int 2 // #2
+ 001f: move v0, v2
+ 0020: goto 0006 // -001a
+ catches
+ tries:
+ try 000a..000d
+ catch java.lang.RuntimeException -> 001c
+ debug info
+ line_start: 20
+ parameters_size: 0001
+ parameter <unnamed> v5
+ 0000: prologue end
+ 0000: line 20
+ 0004: line 21
+ 0006: line 29
+ line = 24
+ 0007: line 24
+ 000f: line 28
+ 0019: line 29
+ 001c: line 25
+ 001e: line 26
+ end sequence
+ source file: "Blort.java"
diff --git a/dx/tests/069-dex-source-position/info.txt b/dx/tests/069-dex-source-position/info.txt
new file mode 100644
index 0000000..28c8b51
--- /dev/null
+++ b/dx/tests/069-dex-source-position/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which makes sure that source
+position information is faithfully reproduced (or not, as directed).
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/069-dex-source-position/run b/dx/tests/069-dex-source-position/run
new file mode 100644
index 0000000..98c2630
--- /dev/null
+++ b/dx/tests/069-dex-source-position/run
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+ --dump-method=Blort.test'*' Blort.class
+dx --debug --dex --no-optimize --positions=important --no-locals \
+ --dump-method=Blort.test'*' Blort.class
+dx --debug --dex --no-optimize --positions=lines --no-locals \
+ --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/070-dex-multianewarray/Blort.java b/dx/tests/070-dex-multianewarray/Blort.java
new file mode 100644
index 0000000..ef812cf
--- /dev/null
+++ b/dx/tests/070-dex-multianewarray/Blort.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ public static Object test01() {
+ Object[][] x = new Object[2][5];
+ return x;
+ }
+
+ public static Object test02() {
+ Object[][][] x = new Object[4][1][];
+ return x;
+ }
+
+ public static Object test03() {
+ Object[][][] x = new Object[7][2][4];
+ return x;
+ }
+
+ public static Object test04() {
+ Object[][][] x = new Object[3][0][0];
+ return x;
+ }
+
+ public static Object test05() {
+ Object[][][][] x = new Object[1][3][5][7];
+ return x;
+ }
+
+ public static Object test06() {
+ Object[][][][][] x = new Object[8][7][2][3][4];
+ return x;
+ }
+
+ public static Object test07() {
+ Object[][][][][][] x = new Object[8][7][2][3][4][];
+ return x;
+ }
+
+ public static Object test08() {
+ Object[][][][][][][] x = new Object[8][7][2][3][4][][];
+ return x;
+ }
+
+ public static boolean[][] test09() {
+ return new boolean[1][2];
+ }
+
+ public static byte[][] test10() {
+ return new byte[3][4];
+ }
+
+ public static char[][] test11() {
+ return new char[5][6];
+ }
+
+ public static double[][] test12() {
+ return new double[7][8];
+ }
+
+ public static float[][] test13() {
+ return new float[9][1];
+ }
+
+ public static int[][][] test14() {
+ return new int[5][3][2];
+ }
+
+ public static long[][][] test15() {
+ return new long[3][4][7];
+ }
+
+ public static short[][][][] test16() {
+ return new short[5][4][3][2];
+ }
+
+ public static String[][][][][] test17() {
+ return new String[5][4][3][2][1];
+ }
+
+ public static Runnable[][][][][][] test18() {
+ return new Runnable[5][4][3][2][1][8];
+ }
+}
diff --git a/dx/tests/070-dex-multianewarray/expected.txt b/dx/tests/070-dex-multianewarray/expected.txt
new file mode 100644
index 0000000..9ddbabe
--- /dev/null
+++ b/dx/tests/070-dex-multianewarray/expected.txt
@@ -0,0 +1,246 @@
+Blort.test01:()Ljava/lang/Object;:
+regs: 0003; ins: 0000; outs: 0002
+ 0000: const/4 v1, #int 2 // #2
+ 0001: const/4 v2, #int 5 // #5
+ 0002: filled-new-array {v1, v2}, int[]
+ 0005: move-result-object v2
+ 0006: const-class v1, java.lang.Object
+ 0008: invoke-static {v1, v2}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+ 000b: move-result-object v1
+ 000c: check-cast v1, java.lang.Object[][]
+ 000e: move-object v0, v1
+ 000f: move-object v1, v0
+ 0010: move-object v0, v1
+ 0011: return-object v0
+Blort.test02:()Ljava/lang/Object;:
+regs: 0003; ins: 0000; outs: 0002
+ 0000: const/4 v1, #int 4 // #4
+ 0001: const/4 v2, #int 1 // #1
+ 0002: filled-new-array {v1, v2}, int[]
+ 0005: move-result-object v2
+ 0006: const-class v1, java.lang.Object[]
+ 0008: invoke-static {v1, v2}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+ 000b: move-result-object v1
+ 000c: check-cast v1, java.lang.Object[][][]
+ 000e: move-object v0, v1
+ 000f: move-object v1, v0
+ 0010: move-object v0, v1
+ 0011: return-object v0
+Blort.test03:()Ljava/lang/Object;:
+regs: 0004; ins: 0000; outs: 0002
+ 0000: const/4 v1, #int 7 // #7
+ 0001: const/4 v2, #int 2 // #2
+ 0002: const/4 v3, #int 4 // #4
+ 0003: filled-new-array {v1, v2, v3}, int[]
+ 0006: move-result-object v2
+ 0007: const-class v1, java.lang.Object
+ 0009: invoke-static {v1, v2}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+ 000c: move-result-object v1
+ 000d: check-cast v1, java.lang.Object[][][]
+ 000f: move-object v0, v1
+ 0010: move-object v1, v0
+ 0011: move-object v0, v1
+ 0012: return-object v0
+Blort.test04:()Ljava/lang/Object;:
+regs: 0004; ins: 0000; outs: 0002
+ 0000: const/4 v1, #int 3 // #3
+ 0001: const/4 v2, #int 0 // #0
+ 0002: const/4 v3, #int 0 // #0
+ 0003: filled-new-array {v1, v2, v3}, int[]
+ 0006: move-result-object v2
+ 0007: const-class v1, java.lang.Object
+ 0009: invoke-static {v1, v2}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+ 000c: move-result-object v1
+ 000d: check-cast v1, java.lang.Object[][][]
+ 000f: move-object v0, v1
+ 0010: move-object v1, v0
+ 0011: move-object v0, v1
+ 0012: return-object v0
+Blort.test05:()Ljava/lang/Object;:
+regs: 0005; ins: 0000; outs: 0002
+ 0000: const/4 v1, #int 1 // #1
+ 0001: const/4 v2, #int 3 // #3
+ 0002: const/4 v3, #int 5 // #5
+ 0003: const/4 v4, #int 7 // #7
+ 0004: filled-new-array {v1, v2, v3, v4}, int[]
+ 0007: move-result-object v2
+ 0008: const-class v1, java.lang.Object
+ 000a: invoke-static {v1, v2}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+ 000d: move-result-object v1
+ 000e: check-cast v1, java.lang.Object[][][][]
+ 0010: move-object v0, v1
+ 0011: move-object v1, v0
+ 0012: move-object v0, v1
+ 0013: return-object v0
+Blort.test06:()Ljava/lang/Object;:
+regs: 0006; ins: 0000; outs: 0002
+ 0000: const/16 v1, #int 8 // #0008
+ 0002: const/4 v2, #int 7 // #7
+ 0003: const/4 v3, #int 2 // #2
+ 0004: const/4 v4, #int 3 // #3
+ 0005: const/4 v5, #int 4 // #4
+ 0006: filled-new-array {v1, v2, v3, v4, v5}, int[]
+ 0009: move-result-object v2
+ 000a: const-class v1, java.lang.Object
+ 000c: invoke-static {v1, v2}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+ 000f: move-result-object v1
+ 0010: check-cast v1, java.lang.Object[][][][][]
+ 0012: move-object v0, v1
+ 0013: move-object v1, v0
+ 0014: move-object v0, v1
+ 0015: return-object v0
+Blort.test07:()Ljava/lang/Object;:
+regs: 0006; ins: 0000; outs: 0002
+ 0000: const/16 v1, #int 8 // #0008
+ 0002: const/4 v2, #int 7 // #7
+ 0003: const/4 v3, #int 2 // #2
+ 0004: const/4 v4, #int 3 // #3
+ 0005: const/4 v5, #int 4 // #4
+ 0006: filled-new-array {v1, v2, v3, v4, v5}, int[]
+ 0009: move-result-object v2
+ 000a: const-class v1, java.lang.Object[]
+ 000c: invoke-static {v1, v2}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+ 000f: move-result-object v1
+ 0010: check-cast v1, java.lang.Object[][][][][][]
+ 0012: move-object v0, v1
+ 0013: move-object v1, v0
+ 0014: move-object v0, v1
+ 0015: return-object v0
+Blort.test08:()Ljava/lang/Object;:
+regs: 0006; ins: 0000; outs: 0002
+ 0000: const/16 v1, #int 8 // #0008
+ 0002: const/4 v2, #int 7 // #7
+ 0003: const/4 v3, #int 2 // #2
+ 0004: const/4 v4, #int 3 // #3
+ 0005: const/4 v5, #int 4 // #4
+ 0006: filled-new-array {v1, v2, v3, v4, v5}, int[]
+ 0009: move-result-object v2
+ 000a: const-class v1, java.lang.Object[][]
+ 000c: invoke-static {v1, v2}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+ 000f: move-result-object v1
+ 0010: check-cast v1, java.lang.Object[][][][][][][]
+ 0012: move-object v0, v1
+ 0013: move-object v1, v0
+ 0014: move-object v0, v1
+ 0015: return-object v0
+Blort.test09:()[[Z:
+regs: 0002; ins: 0000; outs: 0002
+ 0000: const/4 v0, #int 1 // #1
+ 0001: const/4 v1, #int 2 // #2
+ 0002: filled-new-array {v0, v1}, int[]
+ 0005: move-result-object v1
+ 0006: sget-object v0, java.lang.Boolean.TYPE:Ljava/lang/Class;
+ 0008: invoke-static {v0, v1}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+ 000b: move-result-object v0
+ 000c: check-cast v0, boolean[][]
+ 000e: return-object v0
+Blort.test10:()[[B:
+regs: 0002; ins: 0000; outs: 0002
+ 0000: const/4 v0, #int 3 // #3
+ 0001: const/4 v1, #int 4 // #4
+ 0002: filled-new-array {v0, v1}, int[]
+ 0005: move-result-object v1
+ 0006: sget-object v0, java.lang.Byte.TYPE:Ljava/lang/Class;
+ 0008: invoke-static {v0, v1}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+ 000b: move-result-object v0
+ 000c: check-cast v0, byte[][]
+ 000e: return-object v0
+Blort.test11:()[[C:
+regs: 0002; ins: 0000; outs: 0002
+ 0000: const/4 v0, #int 5 // #5
+ 0001: const/4 v1, #int 6 // #6
+ 0002: filled-new-array {v0, v1}, int[]
+ 0005: move-result-object v1
+ 0006: sget-object v0, java.lang.Character.TYPE:Ljava/lang/Class;
+ 0008: invoke-static {v0, v1}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+ 000b: move-result-object v0
+ 000c: check-cast v0, char[][]
+ 000e: return-object v0
+Blort.test12:()[[D:
+regs: 0002; ins: 0000; outs: 0002
+ 0000: const/4 v0, #int 7 // #7
+ 0001: const/16 v1, #int 8 // #0008
+ 0003: filled-new-array {v0, v1}, int[]
+ 0006: move-result-object v1
+ 0007: sget-object v0, java.lang.Double.TYPE:Ljava/lang/Class;
+ 0009: invoke-static {v0, v1}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+ 000c: move-result-object v0
+ 000d: check-cast v0, double[][]
+ 000f: return-object v0
+Blort.test13:()[[F:
+regs: 0002; ins: 0000; outs: 0002
+ 0000: const/16 v0, #int 9 // #0009
+ 0002: const/4 v1, #int 1 // #1
+ 0003: filled-new-array {v0, v1}, int[]
+ 0006: move-result-object v1
+ 0007: sget-object v0, java.lang.Float.TYPE:Ljava/lang/Class;
+ 0009: invoke-static {v0, v1}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+ 000c: move-result-object v0
+ 000d: check-cast v0, float[][]
+ 000f: return-object v0
+Blort.test14:()[[[I:
+regs: 0003; ins: 0000; outs: 0002
+ 0000: const/4 v0, #int 5 // #5
+ 0001: const/4 v1, #int 3 // #3
+ 0002: const/4 v2, #int 2 // #2
+ 0003: filled-new-array {v0, v1, v2}, int[]
+ 0006: move-result-object v1
+ 0007: sget-object v0, java.lang.Integer.TYPE:Ljava/lang/Class;
+ 0009: invoke-static {v0, v1}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+ 000c: move-result-object v0
+ 000d: check-cast v0, int[][][]
+ 000f: return-object v0
+Blort.test15:()[[[J:
+regs: 0003; ins: 0000; outs: 0002
+ 0000: const/4 v0, #int 3 // #3
+ 0001: const/4 v1, #int 4 // #4
+ 0002: const/4 v2, #int 7 // #7
+ 0003: filled-new-array {v0, v1, v2}, int[]
+ 0006: move-result-object v1
+ 0007: sget-object v0, java.lang.Long.TYPE:Ljava/lang/Class;
+ 0009: invoke-static {v0, v1}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+ 000c: move-result-object v0
+ 000d: check-cast v0, long[][][]
+ 000f: return-object v0
+Blort.test16:()[[[[S:
+regs: 0004; ins: 0000; outs: 0002
+ 0000: const/4 v0, #int 5 // #5
+ 0001: const/4 v1, #int 4 // #4
+ 0002: const/4 v2, #int 3 // #3
+ 0003: const/4 v3, #int 2 // #2
+ 0004: filled-new-array {v0, v1, v2, v3}, int[]
+ 0007: move-result-object v1
+ 0008: sget-object v0, java.lang.Short.TYPE:Ljava/lang/Class;
+ 000a: invoke-static {v0, v1}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+ 000d: move-result-object v0
+ 000e: check-cast v0, short[][][][]
+ 0010: return-object v0
+Blort.test17:()[[[[[Ljava/lang/String;:
+regs: 0005; ins: 0000; outs: 0002
+ 0000: const/4 v0, #int 5 // #5
+ 0001: const/4 v1, #int 4 // #4
+ 0002: const/4 v2, #int 3 // #3
+ 0003: const/4 v3, #int 2 // #2
+ 0004: const/4 v4, #int 1 // #1
+ 0005: filled-new-array {v0, v1, v2, v3, v4}, int[]
+ 0008: move-result-object v1
+ 0009: const-class v0, java.lang.String
+ 000b: invoke-static {v0, v1}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+ 000e: move-result-object v0
+ 000f: check-cast v0, java.lang.String[][][][][]
+ 0011: return-object v0
+Blort.test18:()[[[[[[Ljava/lang/Runnable;:
+regs: 0006; ins: 0000; outs: 0002
+ 0000: const/4 v0, #int 5 // #5
+ 0001: const/4 v1, #int 4 // #4
+ 0002: const/4 v2, #int 3 // #3
+ 0003: const/4 v3, #int 2 // #2
+ 0004: const/4 v4, #int 1 // #1
+ 0005: const/16 v5, #int 8 // #0008
+ 0007: filled-new-array/range {v0..v5}, int[]
+ 000a: move-result-object v1
+ 000b: const-class v0, java.lang.Runnable
+ 000d: invoke-static {v0, v1}, java.lang.reflect.Array.newInstance:(Ljava/lang/Class;[I)Ljava/lang/Object;
+ 0010: move-result-object v0
+ 0011: check-cast v0, java.lang.Runnable[][][][][][]
+ 0013: return-object v0
diff --git a/dx/tests/070-dex-multianewarray/info.txt b/dx/tests/070-dex-multianewarray/info.txt
new file mode 100644
index 0000000..1251f0c
--- /dev/null
+++ b/dx/tests/070-dex-multianewarray/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that a few
+cases of multidimensional array construction get converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/070-dex-multianewarray/run b/dx/tests/070-dex-multianewarray/run
new file mode 100644
index 0000000..e5eb509
--- /dev/null
+++ b/dx/tests/070-dex-multianewarray/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+ --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/071-dex-java-stack-ops/blort.j b/dx/tests/071-dex-java-stack-ops/blort.j
new file mode 100644
index 0000000..848a84e
--- /dev/null
+++ b/dx/tests/071-dex-java-stack-ops/blort.j
@@ -0,0 +1,319 @@
+; Copyright (C) 2007 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class Blort
+.super java/lang/Object
+
+; Methods to "consume" an int.
+.method public static consume1(I)V
+.limit stack 0
+.limit locals 1
+ nop
+ return
+.end method
+
+.method public static consume2(I)V
+.limit stack 0
+.limit locals 1
+ nop
+ return
+.end method
+
+.method public static consume3(I)V
+.limit stack 0
+.limit locals 1
+ nop
+ return
+.end method
+
+.method public static consume4(I)V
+.limit stack 0
+.limit locals 1
+ nop
+ return
+.end method
+
+.method public static consume5(I)V
+.limit stack 0
+.limit locals 1
+ nop
+ return
+.end method
+
+.method public static consume6(I)V
+.limit stack 0
+.limit locals 1
+ nop
+ return
+.end method
+
+; Methods to "consume" a long.
+.method public static consume1(J)V
+.limit stack 0
+.limit locals 2
+ nop
+ return
+.end method
+
+.method public static consume2(J)V
+.limit stack 0
+.limit locals 2
+ nop
+ return
+.end method
+
+.method public static consume3(J)V
+.limit stack 0
+.limit locals 2
+ nop
+ return
+.end method
+
+.method public static consume4(J)V
+.limit stack 0
+.limit locals 2
+ nop
+ return
+.end method
+
+; Test of "pop" opcode. This should end up causing a call to consume1(0).
+.method public static test_pop()V
+.limit stack 2
+.limit locals 0
+ iconst_0
+ iconst_1
+ pop ; A1 -> (empty)
+ invokestatic Blort/consume1(I)V
+ return
+.end method
+
+; Test of "pop2" opcode, form 1. This should end up causing a call
+; to consume1(0).
+.method public static test_pop2_form1()V
+.limit stack 3
+.limit locals 0
+ iconst_0
+ iconst_1
+ iconst_2
+ pop2 ; A1 B1 -> (empty)
+ invokestatic Blort/consume1(I)V
+ return
+.end method
+
+; Test of "pop2" opcode, form 2. This should end up causing a call
+; to consume1(0).
+.method public static test_pop2_form2()V
+.limit stack 3
+.limit locals 0
+ iconst_0
+ lconst_0
+ pop2 ; A2 -> (empty)
+ invokestatic Blort/consume1(I)V
+ return
+.end method
+
+; Test of "dup" opcode. This should end up causing these calls in order:
+; consume1(0), consume2(0).
+.method public static test_dup()V
+.limit stack 2
+.limit locals 0
+ iconst_0
+ dup ; A1 -> A1 A1
+ invokestatic Blort/consume1(I)V
+ invokestatic Blort/consume2(I)V
+ return
+.end method
+
+; Test of "dup_x1" opcode. This should end up causing these calls in order:
+; consume1(1), consume2(0), consume3(1).
+.method public static test_dup_x1()V
+.limit stack 3
+.limit locals 0
+ iconst_0
+ iconst_1
+ dup_x1 ; A1 B1 -> B1 A1 B1
+ invokestatic Blort/consume1(I)V
+ invokestatic Blort/consume2(I)V
+ invokestatic Blort/consume3(I)V
+ return
+.end method
+
+; Test of "dup_x2" opcode, form 1. This should end up causing these calls
+; in order: consume1(2), consume2(1), consume3(0), consume4(2).
+.method public static test_dup_x2_form1()V
+.limit stack 4
+.limit locals 0
+ iconst_0
+ iconst_1
+ iconst_2
+ dup_x2 ; A1 B1 C1 -> C1 A1 B1 C1
+ invokestatic Blort/consume1(I)V
+ invokestatic Blort/consume2(I)V
+ invokestatic Blort/consume3(I)V
+ invokestatic Blort/consume4(I)V
+ return
+.end method
+
+; Test of "dup_x2" opcode, form 2. This should end up causing these calls
+; in order: consume1(1), consume2(0L), consume3(1).
+.method public static test_dup_x2_form2()V
+.limit stack 4
+.limit locals 0
+ lconst_0
+ iconst_1
+ dup_x2 ; A2 B1 -> B1 A2 B1
+ invokestatic Blort/consume1(I)V
+ invokestatic Blort/consume2(J)V
+ invokestatic Blort/consume3(I)V
+ return
+.end method
+
+; Test of "dup2" opcode, form 1. This should end up causing these calls
+; in order: consume1(1), consume2(0), consume3(1), consume4(0).
+.method public static test_dup2_form1()V
+.limit stack 4
+.limit locals 0
+ iconst_0
+ iconst_1
+ dup2 ; A1 B1 -> A1 B1 A1 B1
+ invokestatic Blort/consume1(I)V
+ invokestatic Blort/consume2(I)V
+ invokestatic Blort/consume3(I)V
+ invokestatic Blort/consume4(I)V
+ return
+.end method
+
+; Test of "dup2" opcode, form 2. This should end up causing these calls
+; in order: consume1(0L), consume2(0L).
+.method public static test_dup2_form2()V
+.limit stack 4
+.limit locals 0
+ lconst_0
+ dup2 ; A2 -> A2 A2
+ invokestatic Blort/consume1(J)V
+ invokestatic Blort/consume2(J)V
+ return
+.end method
+
+; Test of "dup2_x1" opcode, form 1. This should end up causing these calls
+; in order: consume1(1), consume2(2), consume3(0), consume4(1), consume5(2).
+.method public static test_dup2_x1_form1()V
+.limit stack 5
+.limit locals 0
+ iconst_0
+ iconst_1
+ iconst_2
+ dup2_x1 ; A1 B1 C1 -> B1 C1 A1 B1 C1
+ invokestatic Blort/consume1(I)V
+ invokestatic Blort/consume2(I)V
+ invokestatic Blort/consume3(I)V
+ invokestatic Blort/consume4(I)V
+ invokestatic Blort/consume5(I)V
+ return
+.end method
+
+
+; Test of "dup2_x1" opcode, form 2. This should end up causing these calls
+; in order: consume1(1L), consume2(2), consume3(1L).
+.method public static test_dup2_x1_form2()V
+.limit stack 5
+.limit locals 0
+ iconst_0
+ lconst_1
+ dup2_x1 ; A1 B2 -> B2 A1 B2
+ invokestatic Blort/consume1(J)V
+ invokestatic Blort/consume2(I)V
+ invokestatic Blort/consume3(J)V
+ return
+.end method
+
+; Test of "dup2_x2" opcode, form 1. This should end up causing these calls
+; in order: consume1(3), consume2(2), consume3(1), consume4(0), consume5(3),
+; consume6(2).
+.method public static test_dup2_x2_form1()V
+.limit stack 6
+.limit locals 0
+ iconst_0
+ iconst_1
+ iconst_2
+ iconst_3
+ dup2_x2 ; A1 B1 C1 D1 -> C1 D1 A1 B1 C1 D1
+ invokestatic Blort/consume1(I)V
+ invokestatic Blort/consume2(I)V
+ invokestatic Blort/consume3(I)V
+ invokestatic Blort/consume4(I)V
+ invokestatic Blort/consume5(I)V
+ invokestatic Blort/consume6(I)V
+ return
+.end method
+
+; Test of "dup2_x2" opcode, form 2. This should end up causing these calls
+; in order: consume1(2L), consume2(1), consume3(0), consume4(2L).
+.method public static test_dup2_x2_form2()V
+.limit stack 6
+.limit locals 0
+ iconst_0
+ iconst_1
+ ldc2_w 2
+ dup2_x2 ; A1 B1 C2 -> C2 A1 B1 C2
+ invokestatic Blort/consume1(J)V
+ invokestatic Blort/consume2(I)V
+ invokestatic Blort/consume3(I)V
+ invokestatic Blort/consume4(J)V
+ return
+.end method
+
+; Test of "dup2_x2" opcode, form 3. This should end up causing these calls
+; in order: consume1(2), consume2(1), consume3(0L), consume4(2), consume5(1).
+.method public static test_dup2_x2_form3()V
+.limit stack 6
+.limit locals 0
+ lconst_0
+ iconst_1
+ iconst_2
+ dup2_x2 ; A2 B1 C1 -> B1 C1 A2 B1 C1
+ invokestatic Blort/consume1(I)V
+ invokestatic Blort/consume2(I)V
+ invokestatic Blort/consume3(J)V
+ invokestatic Blort/consume4(I)V
+ invokestatic Blort/consume5(I)V
+ return
+.end method
+
+; Test of "dup2_x2" opcode, form 4. This should end up causing these calls
+; in order: consume1(1L), consume2(0L), consume3(1L).
+.method public static test_dup2_x2_form4()V
+.limit stack 6
+.limit locals 0
+ lconst_0
+ lconst_1
+ dup2_x2 ; A2 B2 -> B2 A2 B2
+ invokestatic Blort/consume1(J)V
+ invokestatic Blort/consume2(J)V
+ invokestatic Blort/consume3(J)V
+ return
+.end method
+
+; Test of "swap" opcode. This should end up causing these calls
+; in order: consume1(0), consume2(1).
+.method public static test_swap()V
+.limit stack 2
+.limit locals 0
+ iconst_0
+ iconst_1
+ swap ; A1 B1 -> B1 A1
+ invokestatic Blort/consume1(I)V
+ invokestatic Blort/consume2(I)V
+ return
+.end method
diff --git a/dx/tests/071-dex-java-stack-ops/expected.txt b/dx/tests/071-dex-java-stack-ops/expected.txt
new file mode 100644
index 0000000..3ba8ef3
--- /dev/null
+++ b/dx/tests/071-dex-java-stack-ops/expected.txt
@@ -0,0 +1,210 @@
+Blort.test_dup:()V:
+regs: 0003; ins: 0000; outs: 0001
+ 0000: const/4 v0, #int 0 // #0
+ 0001: move v2, v0
+ 0002: move v0, v2
+ 0003: move v1, v2
+ 0004: invoke-static {v1}, Blort.consume1:(I)V
+ 0007: invoke-static {v0}, Blort.consume2:(I)V
+ 000a: return-void
+Blort.test_dup2_form1:()V:
+regs: 0006; ins: 0000; outs: 0001
+ 0000: const/4 v0, #int 0 // #0
+ 0001: const/4 v1, #int 1 // #1
+ 0002: move v4, v0
+ 0003: move v5, v1
+ 0004: move v0, v4
+ 0005: move v1, v5
+ 0006: move v2, v4
+ 0007: move v3, v5
+ 0008: invoke-static {v3}, Blort.consume1:(I)V
+ 000b: invoke-static {v2}, Blort.consume2:(I)V
+ 000e: invoke-static {v1}, Blort.consume3:(I)V
+ 0011: invoke-static {v0}, Blort.consume4:(I)V
+ 0014: return-void
+Blort.test_dup2_form2:()V:
+regs: 0006; ins: 0000; outs: 0002
+ 0000: const-wide/16 v0, #long 0 // #0000
+ 0002: move-wide v4, v0
+ 0003: move-wide v0, v4
+ 0004: move-wide v2, v4
+ 0005: invoke-static {v2, v3}, Blort.consume1:(J)V
+ 0008: invoke-static {v0, v1}, Blort.consume2:(J)V
+ 000b: return-void
+Blort.test_dup2_x1_form1:()V:
+regs: 0008; ins: 0000; outs: 0001
+ 0000: const/4 v0, #int 0 // #0
+ 0001: const/4 v1, #int 1 // #1
+ 0002: const/4 v2, #int 2 // #2
+ 0003: move v5, v0
+ 0004: move v6, v1
+ 0005: move v7, v2
+ 0006: move v0, v6
+ 0007: move v1, v7
+ 0008: move v2, v5
+ 0009: move v3, v6
+ 000a: move v4, v7
+ 000b: invoke-static {v4}, Blort.consume1:(I)V
+ 000e: invoke-static {v3}, Blort.consume2:(I)V
+ 0011: invoke-static {v2}, Blort.consume3:(I)V
+ 0014: invoke-static {v1}, Blort.consume4:(I)V
+ 0017: invoke-static {v0}, Blort.consume5:(I)V
+ 001a: return-void
+Blort.test_dup2_x1_form2:()V:
+regs: 0008; ins: 0000; outs: 0002
+ 0000: const/4 v0, #int 0 // #0
+ 0001: const-wide/16 v1, #long 1 // #0001
+ 0003: move v5, v0
+ 0004: move-wide v6, v1
+ 0005: move-wide v0, v6
+ 0006: move v2, v5
+ 0007: move-wide v3, v6
+ 0008: invoke-static {v3, v4}, Blort.consume1:(J)V
+ 000b: invoke-static {v2}, Blort.consume2:(I)V
+ 000e: invoke-static {v0, v1}, Blort.consume3:(J)V
+ 0011: return-void
+Blort.test_dup2_x2_form1:()V:
+regs: 000a; ins: 0000; outs: 0001
+ 0000: const/4 v0, #int 0 // #0
+ 0001: const/4 v1, #int 1 // #1
+ 0002: const/4 v2, #int 2 // #2
+ 0003: const/4 v3, #int 3 // #3
+ 0004: move v6, v0
+ 0005: move v7, v1
+ 0006: move v8, v2
+ 0007: move v9, v3
+ 0008: move v0, v8
+ 0009: move v1, v9
+ 000a: move v2, v6
+ 000b: move v3, v7
+ 000c: move v4, v8
+ 000d: move v5, v9
+ 000e: invoke-static {v5}, Blort.consume1:(I)V
+ 0011: invoke-static {v4}, Blort.consume2:(I)V
+ 0014: invoke-static {v3}, Blort.consume3:(I)V
+ 0017: invoke-static {v2}, Blort.consume4:(I)V
+ 001a: invoke-static {v1}, Blort.consume5:(I)V
+ 001d: invoke-static {v0}, Blort.consume6:(I)V
+ 0020: return-void
+Blort.test_dup2_x2_form2:()V:
+regs: 000a; ins: 0000; outs: 0002
+ 0000: const/4 v0, #int 0 // #0
+ 0001: const/4 v1, #int 1 // #1
+ 0002: const-wide/16 v2, #long 2 // #0002
+ 0004: move v6, v0
+ 0005: move v7, v1
+ 0006: move-wide v8, v2
+ 0007: move-wide v0, v8
+ 0008: move v2, v6
+ 0009: move v3, v7
+ 000a: move-wide v4, v8
+ 000b: invoke-static {v4, v5}, Blort.consume1:(J)V
+ 000e: invoke-static {v3}, Blort.consume2:(I)V
+ 0011: invoke-static {v2}, Blort.consume3:(I)V
+ 0014: invoke-static {v0, v1}, Blort.consume4:(J)V
+ 0017: return-void
+Blort.test_dup2_x2_form3:()V:
+regs: 000a; ins: 0000; outs: 0002
+ 0000: const-wide/16 v0, #long 0 // #0000
+ 0002: const/4 v2, #int 1 // #1
+ 0003: const/4 v3, #int 2 // #2
+ 0004: move-wide v6, v0
+ 0005: move v8, v2
+ 0006: move v9, v3
+ 0007: move v0, v8
+ 0008: move v1, v9
+ 0009: move-wide v2, v6
+ 000a: move v4, v8
+ 000b: move v5, v9
+ 000c: invoke-static {v5}, Blort.consume1:(I)V
+ 000f: invoke-static {v4}, Blort.consume2:(I)V
+ 0012: invoke-static {v2, v3}, Blort.consume3:(J)V
+ 0015: invoke-static {v1}, Blort.consume4:(I)V
+ 0018: invoke-static {v0}, Blort.consume5:(I)V
+ 001b: return-void
+Blort.test_dup2_x2_form4:()V:
+regs: 000a; ins: 0000; outs: 0002
+ 0000: const-wide/16 v0, #long 0 // #0000
+ 0002: const-wide/16 v2, #long 1 // #0001
+ 0004: move-wide v6, v0
+ 0005: move-wide v8, v2
+ 0006: move-wide v0, v8
+ 0007: move-wide v2, v6
+ 0008: move-wide v4, v8
+ 0009: invoke-static {v4, v5}, Blort.consume1:(J)V
+ 000c: invoke-static {v2, v3}, Blort.consume2:(J)V
+ 000f: invoke-static {v0, v1}, Blort.consume3:(J)V
+ 0012: return-void
+Blort.test_dup_x1:()V:
+regs: 0005; ins: 0000; outs: 0001
+ 0000: const/4 v0, #int 0 // #0
+ 0001: const/4 v1, #int 1 // #1
+ 0002: move v3, v0
+ 0003: move v4, v1
+ 0004: move v0, v4
+ 0005: move v1, v3
+ 0006: move v2, v4
+ 0007: invoke-static {v2}, Blort.consume1:(I)V
+ 000a: invoke-static {v1}, Blort.consume2:(I)V
+ 000d: invoke-static {v0}, Blort.consume3:(I)V
+ 0010: return-void
+Blort.test_dup_x2_form1:()V:
+regs: 0007; ins: 0000; outs: 0001
+ 0000: const/4 v0, #int 0 // #0
+ 0001: const/4 v1, #int 1 // #1
+ 0002: const/4 v2, #int 2 // #2
+ 0003: move v4, v0
+ 0004: move v5, v1
+ 0005: move v6, v2
+ 0006: move v0, v6
+ 0007: move v1, v4
+ 0008: move v2, v5
+ 0009: move v3, v6
+ 000a: invoke-static {v3}, Blort.consume1:(I)V
+ 000d: invoke-static {v2}, Blort.consume2:(I)V
+ 0010: invoke-static {v1}, Blort.consume3:(I)V
+ 0013: invoke-static {v0}, Blort.consume4:(I)V
+ 0016: return-void
+Blort.test_dup_x2_form2:()V:
+regs: 0007; ins: 0000; outs: 0002
+ 0000: const-wide/16 v0, #long 0 // #0000
+ 0002: const/4 v2, #int 1 // #1
+ 0003: move-wide v4, v0
+ 0004: move v6, v2
+ 0005: move v0, v6
+ 0006: move-wide v1, v4
+ 0007: move v3, v6
+ 0008: invoke-static {v3}, Blort.consume1:(I)V
+ 000b: invoke-static {v1, v2}, Blort.consume2:(J)V
+ 000e: invoke-static {v0}, Blort.consume3:(I)V
+ 0011: return-void
+Blort.test_pop:()V:
+regs: 0002; ins: 0000; outs: 0001
+ 0000: const/4 v0, #int 0 // #0
+ 0001: const/4 v1, #int 1 // #1
+ 0002: invoke-static {v0}, Blort.consume1:(I)V
+ 0005: return-void
+Blort.test_pop2_form1:()V:
+regs: 0003; ins: 0000; outs: 0001
+ 0000: const/4 v0, #int 0 // #0
+ 0001: const/4 v1, #int 1 // #1
+ 0002: const/4 v2, #int 2 // #2
+ 0003: invoke-static {v0}, Blort.consume1:(I)V
+ 0006: return-void
+Blort.test_pop2_form2:()V:
+regs: 0003; ins: 0000; outs: 0001
+ 0000: const/4 v0, #int 0 // #0
+ 0001: const-wide/16 v1, #long 0 // #0000
+ 0003: invoke-static {v0}, Blort.consume1:(I)V
+ 0006: return-void
+Blort.test_swap:()V:
+regs: 0004; ins: 0000; outs: 0001
+ 0000: const/4 v0, #int 0 // #0
+ 0001: const/4 v1, #int 1 // #1
+ 0002: move v2, v0
+ 0003: move v3, v1
+ 0004: move v0, v3
+ 0005: move v1, v2
+ 0006: invoke-static {v1}, Blort.consume1:(I)V
+ 0009: invoke-static {v0}, Blort.consume2:(I)V
+ 000c: return-void
diff --git a/dx/tests/071-dex-java-stack-ops/info.txt b/dx/tests/071-dex-java-stack-ops/info.txt
new file mode 100644
index 0000000..6c5383a
--- /dev/null
+++ b/dx/tests/071-dex-java-stack-ops/info.txt
@@ -0,0 +1,7 @@
+This is a smoke test of dex conversion, which checks to see that at
+least one case of each of the possible forms of Java stack
+manipulation op translate reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/071-dex-java-stack-ops/run b/dx/tests/071-dex-java-stack-ops/run
new file mode 100644
index 0000000..52d8a77
--- /dev/null
+++ b/dx/tests/071-dex-java-stack-ops/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+jasmin -d . blort.j >/dev/null
+dx --debug --dex --no-optimize --positions=none --no-locals \
+ --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/072-dex-switch-edge-cases/Blort.java b/dx/tests/072-dex-switch-edge-cases/Blort.java
new file mode 100644
index 0000000..2d4d107
--- /dev/null
+++ b/dx/tests/072-dex-switch-edge-cases/Blort.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ // Empty switch statement. (Note: This is the same as a default-only
+ // switch statement, since under the covers every switch statement
+ // has a default of some sort.)
+ public int test1(int x) {
+ switch (x) {
+ // This space intentionally left blank.
+ }
+
+ return 0;
+ }
+
+ // Single element.
+ public int test2(int x) {
+ switch (x) {
+ case 0: return 0;
+ }
+
+ return 1;
+ }
+
+ // Single element: Integer.MIN_VALUE.
+ public int test3(int x) {
+ switch (x) {
+ case Integer.MIN_VALUE: return 0;
+ }
+
+ return 1;
+ }
+
+ // Single element: Integer.MAX_VALUE.
+ public int test4(int x) {
+ switch (x) {
+ case Integer.MAX_VALUE: return 0;
+ }
+
+ return 1;
+ }
+
+ // Two elements: 0 and Integer.MIN_VALUE.
+ public int test5(int x) {
+ switch (x) {
+ case 0: return 0;
+ case Integer.MIN_VALUE: return 1;
+ }
+
+ return 2;
+ }
+
+ // Two elements: 0 and Integer.MAX_VALUE.
+ public int test6(int x) {
+ switch (x) {
+ case 0: return 0;
+ case Integer.MAX_VALUE: return 1;
+ }
+
+ return 2;
+ }
+
+ // Two elements: Integer.MIN_VALUE and Integer.MAX_VALUE.
+ public int test7(int x) {
+ switch (x) {
+ case Integer.MIN_VALUE: return 0;
+ case Integer.MAX_VALUE: return 1;
+ }
+
+ return 2;
+ }
+
+ // Two elements: Large enough to be packed but such that 32 bit
+ // threshold calculations could overflow.
+ public int test8(int x) {
+ switch (x) {
+ case 0: return 0;
+ case 0x4cccccc8: return 1;
+ }
+
+ return 2;
+ }
+}
diff --git a/dx/tests/072-dex-switch-edge-cases/expected.txt b/dx/tests/072-dex-switch-edge-cases/expected.txt
new file mode 100644
index 0000000..6659284
--- /dev/null
+++ b/dx/tests/072-dex-switch-edge-cases/expected.txt
@@ -0,0 +1,126 @@
+Blort.test1:(I)I:
+regs: 0005; ins: 0002; outs: 0000
+ 0000: move-object v0, v3
+ 0001: move v1, v4
+ 0002: move v2, v1
+ 0003: const/4 v2, #int 0 // #0
+ 0004: move v0, v2
+ 0005: return v0
+Blort.test2:(I)I:
+regs: 0005; ins: 0002; outs: 0000
+ 0000: move-object v0, v3
+ 0001: move v1, v4
+ 0002: move v2, v1
+ 0003: packed-switch v2, 000c // +0009
+ 0006: const/4 v2, #int 1 // #1
+ 0007: move v0, v2
+ 0008: return v0
+ 0009: const/4 v2, #int 0 // #0
+ 000a: move v0, v2
+ 000b: goto 0008 // -0003
+ 000c: packed-switch-data // for switch @ 0003
+ 0: 00000009 // +00000006
+Blort.test3:(I)I:
+regs: 0005; ins: 0002; outs: 0000
+ 0000: move-object v0, v3
+ 0001: move v1, v4
+ 0002: move v2, v1
+ 0003: packed-switch v2, 000c // +0009
+ 0006: const/4 v2, #int 1 // #1
+ 0007: move v0, v2
+ 0008: return v0
+ 0009: const/4 v2, #int 0 // #0
+ 000a: move v0, v2
+ 000b: goto 0008 // -0003
+ 000c: packed-switch-data // for switch @ 0003
+ -2147483648: 00000009 // +00000006
+Blort.test4:(I)I:
+regs: 0005; ins: 0002; outs: 0000
+ 0000: move-object v0, v3
+ 0001: move v1, v4
+ 0002: move v2, v1
+ 0003: packed-switch v2, 000c // +0009
+ 0006: const/4 v2, #int 1 // #1
+ 0007: move v0, v2
+ 0008: return v0
+ 0009: const/4 v2, #int 0 // #0
+ 000a: move v0, v2
+ 000b: goto 0008 // -0003
+ 000c: packed-switch-data // for switch @ 0003
+ 2147483647: 00000009 // +00000006
+Blort.test5:(I)I:
+regs: 0005; ins: 0002; outs: 0000
+ 0000: move-object v0, v3
+ 0001: move v1, v4
+ 0002: move v2, v1
+ 0003: sparse-switch v2, 0010 // +000d
+ 0006: const/4 v2, #int 2 // #2
+ 0007: move v0, v2
+ 0008: return v0
+ 0009: const/4 v2, #int 0 // #0
+ 000a: move v0, v2
+ 000b: goto 0008 // -0003
+ 000c: const/4 v2, #int 1 // #1
+ 000d: move v0, v2
+ 000e: goto 0008 // -0006
+ 000f: nop // spacer
+ 0010: sparse-switch-data // for switch @ 0003
+ -2147483648: 0000000c // +00000009
+ 0: 00000009 // +00000006
+Blort.test6:(I)I:
+regs: 0005; ins: 0002; outs: 0000
+ 0000: move-object v0, v3
+ 0001: move v1, v4
+ 0002: move v2, v1
+ 0003: sparse-switch v2, 0010 // +000d
+ 0006: const/4 v2, #int 2 // #2
+ 0007: move v0, v2
+ 0008: return v0
+ 0009: const/4 v2, #int 0 // #0
+ 000a: move v0, v2
+ 000b: goto 0008 // -0003
+ 000c: const/4 v2, #int 1 // #1
+ 000d: move v0, v2
+ 000e: goto 0008 // -0006
+ 000f: nop // spacer
+ 0010: sparse-switch-data // for switch @ 0003
+ 0: 00000009 // +00000006
+ 2147483647: 0000000c // +00000009
+Blort.test7:(I)I:
+regs: 0005; ins: 0002; outs: 0000
+ 0000: move-object v0, v3
+ 0001: move v1, v4
+ 0002: move v2, v1
+ 0003: sparse-switch v2, 0010 // +000d
+ 0006: const/4 v2, #int 2 // #2
+ 0007: move v0, v2
+ 0008: return v0
+ 0009: const/4 v2, #int 0 // #0
+ 000a: move v0, v2
+ 000b: goto 0008 // -0003
+ 000c: const/4 v2, #int 1 // #1
+ 000d: move v0, v2
+ 000e: goto 0008 // -0006
+ 000f: nop // spacer
+ 0010: sparse-switch-data // for switch @ 0003
+ -2147483648: 00000009 // +00000006
+ 2147483647: 0000000c // +00000009
+Blort.test8:(I)I:
+regs: 0005; ins: 0002; outs: 0000
+ 0000: move-object v0, v3
+ 0001: move v1, v4
+ 0002: move v2, v1
+ 0003: sparse-switch v2, 0010 // +000d
+ 0006: const/4 v2, #int 2 // #2
+ 0007: move v0, v2
+ 0008: return v0
+ 0009: const/4 v2, #int 0 // #0
+ 000a: move v0, v2
+ 000b: goto 0008 // -0003
+ 000c: const/4 v2, #int 1 // #1
+ 000d: move v0, v2
+ 000e: goto 0008 // -0006
+ 000f: nop // spacer
+ 0010: sparse-switch-data // for switch @ 0003
+ 0: 00000009 // +00000006
+ 1288490184: 0000000c // +00000009
diff --git a/dx/tests/072-dex-switch-edge-cases/info.txt b/dx/tests/072-dex-switch-edge-cases/info.txt
new file mode 100644
index 0000000..4c7b42c
--- /dev/null
+++ b/dx/tests/072-dex-switch-edge-cases/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+a bunch of switch op edge cases get converted reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/072-dex-switch-edge-cases/run b/dx/tests/072-dex-switch-edge-cases/run
new file mode 100644
index 0000000..52f1131
--- /dev/null
+++ b/dx/tests/072-dex-switch-edge-cases/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+ --dump-method=Blort.test'*' *.class
diff --git a/dx/tests/073-dex-null-array-refs/Blort.java b/dx/tests/073-dex-null-array-refs/Blort.java
new file mode 100644
index 0000000..e16e0f4
--- /dev/null
+++ b/dx/tests/073-dex-null-array-refs/Blort.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ public static Object test1() {
+ return ((Object[]) null)[0];
+ }
+
+ public static void test2() {
+ ((Object[]) null)[0] = null;
+ }
+
+ public static int test3() {
+ return ((Object[]) null).length;
+ }
+
+ public static Object test4() {
+ Object[] arr = null;
+ return arr[0];
+ }
+
+ public static void test5() {
+ Object[] arr = null;
+ arr[0] = null;
+ }
+
+ public static int test6() {
+ Object[] arr = null;
+ return arr.length;
+ }
+
+ public static Object test7(Object[] arr) {
+ if (check()) {
+ arr = null;
+ }
+
+ return arr[0];
+ }
+
+ public static void test8(Object[] arr) {
+ if (check()) {
+ arr = null;
+ }
+
+ arr[0] = null;
+ }
+
+ public static int test9(Object[] arr) {
+ if (check()) {
+ arr = null;
+ }
+
+ return arr.length;
+ }
+
+ public static boolean check() {
+ return true;
+ }
+}
diff --git a/dx/tests/073-dex-null-array-refs/expected.txt b/dx/tests/073-dex-null-array-refs/expected.txt
new file mode 100644
index 0000000..7f3ee21
--- /dev/null
+++ b/dx/tests/073-dex-null-array-refs/expected.txt
@@ -0,0 +1,85 @@
+Blort.test1:()Ljava/lang/Object;:
+regs: 0002; ins: 0000; outs: 0000
+ 0000: const/4 v0, #null // #0
+ 0001: check-cast v0, java.lang.Object[]
+ 0003: const/4 v1, #int 0 // #0
+ 0004: aget-object v0, v0, v1
+ 0006: return-object v0
+Blort.test2:()V:
+regs: 0003; ins: 0000; outs: 0000
+ 0000: const/4 v0, #null // #0
+ 0001: check-cast v0, java.lang.Object[]
+ 0003: const/4 v1, #int 0 // #0
+ 0004: const/4 v2, #null // #0
+ 0005: aput-object v2, v0, v1
+ 0007: return-void
+Blort.test3:()I:
+regs: 0001; ins: 0000; outs: 0000
+ 0000: const/4 v0, #null // #0
+ 0001: check-cast v0, java.lang.Object[]
+ 0003: array-length v0, v0
+ 0004: return v0
+Blort.test4:()Ljava/lang/Object;:
+regs: 0003; ins: 0000; outs: 0000
+ 0000: const/4 v1, #null // #0
+ 0001: move-object v0, v1
+ 0002: move-object v1, v0
+ 0003: const/4 v2, #int 0 // #0
+ 0004: aget-object v1, v1, v2
+ 0006: move-object v0, v1
+ 0007: return-object v0
+Blort.test5:()V:
+regs: 0004; ins: 0000; outs: 0000
+ 0000: const/4 v1, #null // #0
+ 0001: move-object v0, v1
+ 0002: move-object v1, v0
+ 0003: const/4 v2, #int 0 // #0
+ 0004: const/4 v3, #null // #0
+ 0005: aput-object v3, v1, v2
+ 0007: return-void
+Blort.test6:()I:
+regs: 0002; ins: 0000; outs: 0000
+ 0000: const/4 v1, #null // #0
+ 0001: move-object v0, v1
+ 0002: move-object v1, v0
+ 0003: array-length v1, v1
+ 0004: move v0, v1
+ 0005: return v0
+Blort.test7:([Ljava/lang/Object;)Ljava/lang/Object;:
+regs: 0004; ins: 0001; outs: 0000
+ 0000: move-object v0, v3
+ 0001: invoke-static {}, Blort.check:()Z
+ 0004: move-result v1
+ 0005: if-eqz v1, 0009 // +0004
+ 0007: const/4 v1, #null // #0
+ 0008: move-object v0, v1
+ 0009: move-object v1, v0
+ 000a: const/4 v2, #int 0 // #0
+ 000b: aget-object v1, v1, v2
+ 000d: move-object v0, v1
+ 000e: return-object v0
+Blort.test8:([Ljava/lang/Object;)V:
+regs: 0005; ins: 0001; outs: 0000
+ 0000: move-object v0, v4
+ 0001: invoke-static {}, Blort.check:()Z
+ 0004: move-result v1
+ 0005: if-eqz v1, 0009 // +0004
+ 0007: const/4 v1, #null // #0
+ 0008: move-object v0, v1
+ 0009: move-object v1, v0
+ 000a: const/4 v2, #int 0 // #0
+ 000b: const/4 v3, #null // #0
+ 000c: aput-object v3, v1, v2
+ 000e: return-void
+Blort.test9:([Ljava/lang/Object;)I:
+regs: 0003; ins: 0001; outs: 0000
+ 0000: move-object v0, v2
+ 0001: invoke-static {}, Blort.check:()Z
+ 0004: move-result v1
+ 0005: if-eqz v1, 0009 // +0004
+ 0007: const/4 v1, #null // #0
+ 0008: move-object v0, v1
+ 0009: move-object v1, v0
+ 000a: array-length v1, v1
+ 000b: move v0, v1
+ 000c: return v0
diff --git a/dx/tests/073-dex-null-array-refs/info.txt b/dx/tests/073-dex-null-array-refs/info.txt
new file mode 100644
index 0000000..ca3b161
--- /dev/null
+++ b/dx/tests/073-dex-null-array-refs/info.txt
@@ -0,0 +1,7 @@
+This is a smoke test of dex conversion, which checks to see that
+a bunch of cases convert reasonably, where necessarily or possibly
+null array references are used.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/073-dex-null-array-refs/run b/dx/tests/073-dex-null-array-refs/run
new file mode 100644
index 0000000..52f1131
--- /dev/null
+++ b/dx/tests/073-dex-null-array-refs/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+ --dump-method=Blort.test'*' *.class
diff --git a/dx/tests/074-dex-form35c-edge-case/Blort.java b/dx/tests/074-dex-form35c-edge-case/Blort.java
new file mode 100644
index 0000000..979263f
--- /dev/null
+++ b/dx/tests/074-dex-form35c-edge-case/Blort.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ public void test()
+ {
+ int i1 = 0;
+ int i2 = 0;
+ int i3 = 0;
+ int i4 = 0;
+ int i5 = 0;
+ int i6 = 0;
+ int i7 = 0;
+ int i8 = 0;
+ int i9 = 0;
+ int i10 = 0;
+ int i11 = 0;
+ int i12 = 0;
+ int i13 = 0;
+
+ blort(0);
+ }
+
+ public void blort(long x) {
+ // blank
+ }
+}
diff --git a/dx/tests/074-dex-form35c-edge-case/expected.txt b/dx/tests/074-dex-form35c-edge-case/expected.txt
new file mode 100644
index 0000000..6afdcd7
--- /dev/null
+++ b/dx/tests/074-dex-form35c-edge-case/expected.txt
@@ -0,0 +1,33 @@
+Blort.test:()V:
+regs: 0012; ins: 0001; outs: 0003
+ 0000: move-object/from16 v0, v17
+ 0002: const/4 v14, #int 0 // #0
+ 0003: move v1, v14
+ 0004: const/4 v14, #int 0 // #0
+ 0005: move v2, v14
+ 0006: const/4 v14, #int 0 // #0
+ 0007: move v3, v14
+ 0008: const/4 v14, #int 0 // #0
+ 0009: move v4, v14
+ 000a: const/4 v14, #int 0 // #0
+ 000b: move v5, v14
+ 000c: const/4 v14, #int 0 // #0
+ 000d: move v6, v14
+ 000e: const/4 v14, #int 0 // #0
+ 000f: move v7, v14
+ 0010: const/4 v14, #int 0 // #0
+ 0011: move v8, v14
+ 0012: const/4 v14, #int 0 // #0
+ 0013: move v9, v14
+ 0014: const/4 v14, #int 0 // #0
+ 0015: move v10, v14
+ 0016: const/4 v14, #int 0 // #0
+ 0017: move v11, v14
+ 0018: const/4 v14, #int 0 // #0
+ 0019: move v12, v14
+ 001a: const/4 v14, #int 0 // #0
+ 001b: move v13, v14
+ 001c: move-object v14, v0
+ 001d: const-wide/16 v15, #long 0 // #0000
+ 001f: invoke-virtual/range {v14..v16}, Blort.blort:(J)V
+ 0022: return-void
diff --git a/dx/tests/074-dex-form35c-edge-case/info.txt b/dx/tests/074-dex-form35c-edge-case/info.txt
new file mode 100644
index 0000000..51d83bd
--- /dev/null
+++ b/dx/tests/074-dex-form35c-edge-case/info.txt
@@ -0,0 +1,8 @@
+This is a smoke test of dex conversion, which checks to see that
+an edge case of instruction format 35c works, where a reference
+is made to register 15 as a category-2 value, meaning that
+the instruction has to be rewritten to use a different format.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/074-dex-form35c-edge-case/run b/dx/tests/074-dex-form35c-edge-case/run
new file mode 100644
index 0000000..7578321
--- /dev/null
+++ b/dx/tests/074-dex-form35c-edge-case/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+ --dump-method=Blort.test *.class
diff --git a/dx/tests/075-dex-cat2-value-merge/Blort.java b/dx/tests/075-dex-cat2-value-merge/Blort.java
new file mode 100644
index 0000000..0aa4c5a
--- /dev/null
+++ b/dx/tests/075-dex-cat2-value-merge/Blort.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ public static void test(long[] arr)
+ {
+ long x = 0;
+
+ for (;;) {
+ x += arr[0];
+ }
+ }
+}
diff --git a/dx/tests/075-dex-cat2-value-merge/expected.txt b/dx/tests/075-dex-cat2-value-merge/expected.txt
new file mode 100644
index 0000000..e1b9b1b
--- /dev/null
+++ b/dx/tests/075-dex-cat2-value-merge/expected.txt
@@ -0,0 +1,12 @@
+Blort.test:([J)V:
+regs: 0008; ins: 0001; outs: 0000
+ 0000: move-object v0, v7
+ 0001: const-wide/16 v3, #long 0 // #0000
+ 0003: move-wide v1, v3
+ 0004: move-wide v3, v1
+ 0005: move-object v5, v0
+ 0006: const/4 v6, #int 0 // #0
+ 0007: aget-wide v5, v5, v6
+ 0009: add-long/2addr v3, v5
+ 000a: move-wide v1, v3
+ 000b: goto 0004 // -0007
diff --git a/dx/tests/075-dex-cat2-value-merge/info.txt b/dx/tests/075-dex-cat2-value-merge/info.txt
new file mode 100644
index 0000000..de411b8
--- /dev/null
+++ b/dx/tests/075-dex-cat2-value-merge/info.txt
@@ -0,0 +1,7 @@
+This is a smoke test of dex conversion, which checks to see that
+when a known value of category-2 gets merged during control
+flow analysis, things don't break.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/075-dex-cat2-value-merge/run b/dx/tests/075-dex-cat2-value-merge/run
new file mode 100644
index 0000000..7578321
--- /dev/null
+++ b/dx/tests/075-dex-cat2-value-merge/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+ --dump-method=Blort.test *.class
diff --git a/dx/tests/076-dex-synch-and-stack/Blort.java b/dx/tests/076-dex-synch-and-stack/Blort.java
new file mode 100644
index 0000000..8a83492
--- /dev/null
+++ b/dx/tests/076-dex-synch-and-stack/Blort.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ public synchronized void test() {
+ new Object();
+ }
+}
diff --git a/dx/tests/076-dex-synch-and-stack/expected.txt b/dx/tests/076-dex-synch-and-stack/expected.txt
new file mode 100644
index 0000000..eba839e
--- /dev/null
+++ b/dx/tests/076-dex-synch-and-stack/expected.txt
@@ -0,0 +1,19 @@
+Blort.test:()V:
+regs: 0006; ins: 0001; outs: 0001
+ 0000: move-object v0, v5
+ 0001: move-object v3, v5
+ 0002: monitor-enter v3
+ 0003: new-instance v1, java.lang.Object
+ 0005: move-object v4, v1
+ 0006: move-object v1, v4
+ 0007: move-object v2, v4
+ 0008: invoke-direct {v2}, java.lang.Object.<init>:()V
+ 000b: monitor-exit v3
+ 000c: return-void
+ 000d: move-exception v0
+ 000e: monitor-exit v3
+ 000f: throw v0
+ catches
+ tries:
+ try 0003..000b
+ catch <any> -> 000d
diff --git a/dx/tests/076-dex-synch-and-stack/info.txt b/dx/tests/076-dex-synch-and-stack/info.txt
new file mode 100644
index 0000000..ab5206f
--- /dev/null
+++ b/dx/tests/076-dex-synch-and-stack/info.txt
@@ -0,0 +1,7 @@
+This is a smoke test of dex conversion, which checks to make sure that
+the synchronized method conversion doesn't interact poorly with stack
+operation unwinding.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/076-dex-synch-and-stack/run b/dx/tests/076-dex-synch-and-stack/run
new file mode 100644
index 0000000..7578321
--- /dev/null
+++ b/dx/tests/076-dex-synch-and-stack/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+ --dump-method=Blort.test *.class
diff --git a/dx/tests/077-dex-code-alignment/Blort.java b/dx/tests/077-dex-code-alignment/Blort.java
new file mode 100644
index 0000000..3ec041a
--- /dev/null
+++ b/dx/tests/077-dex-code-alignment/Blort.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ public static void justReturn1() {
+ // This space intentionally left blank.
+ }
+
+ public static void justReturn2() {
+ // This space intentionally left blank.
+ }
+}
diff --git a/dx/tests/077-dex-code-alignment/expected.txt b/dx/tests/077-dex-code-alignment/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dx/tests/077-dex-code-alignment/expected.txt
diff --git a/dx/tests/077-dex-code-alignment/info.txt b/dx/tests/077-dex-code-alignment/info.txt
new file mode 100644
index 0000000..0dd662b
--- /dev/null
+++ b/dx/tests/077-dex-code-alignment/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to make sure that
+code arrays are 4-byte aligned within a dex file.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/077-dex-code-alignment/run b/dx/tests/077-dex-code-alignment/run
new file mode 100644
index 0000000..f311dbf
--- /dev/null
+++ b/dx/tests/077-dex-code-alignment/run
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+
+# The awk script below attempts to filter out everything but the
+# essentials: That methods justReturn1() and justReturn2() contain
+# a single "return-void" code unit, and that there is an empty (0x0000)
+# code unit between the two of them.
+
+dx --debug --dex --positions=none --no-locals --dump-to=- *.class | awk '
+BEGIN { codes = 0; dump = 0; }
+/codes:/ { codes = 1; }
+codes && /justReturn/ { dump = 1; print "method start"; }
+/string_data:/ { codes = 0; dump = 0; }
+dump && /^......: .... / { print $2; }
+'
diff --git a/dx/tests/078-dex-local-variable-table/Blort.java b/dx/tests/078-dex-local-variable-table/Blort.java
new file mode 100644
index 0000000..7291445
--- /dev/null
+++ b/dx/tests/078-dex-local-variable-table/Blort.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ public static void test01(Object x) {
+ x.hashCode();
+ }
+
+ public static Object test02() {
+ Object[] arr = null;
+ return arr[0];
+ }
+
+ public static String test03(int x) {
+ String foo = null;
+ return foo;
+ }
+
+ public static String test04(int x) {
+ String foo = null;
+ if (x < 0) {
+ foo = "bar";
+ }
+ return foo;
+ }
+
+ public static int test05(Object x) {
+ int[] arr = (int[]) x;
+ arr[0] = 123;
+ return arr[0];
+ }
+
+ public static int test06(int x) {
+ if (x < 10) {
+ int y = 1;
+ return y;
+ } else {
+ int y = 2;
+ return y;
+ }
+ }
+
+ // Test for representation of boolean.
+ public static void test07(boolean x) {
+ boolean y = x;
+ }
+
+ // Test for representation of byte.
+ public static void test08(byte x) {
+ byte y = x;
+ }
+
+ // Test for representation of char.
+ public static void test09(char x) {
+ char y = x;
+ }
+
+ // Test for representation of double.
+ public static void test10(double x) {
+ double y = x;
+ }
+
+ // Test for representation of float.
+ public static void test11(float x) {
+ float y = x;
+ }
+
+ // Test for representation of int.
+ public static void test12(int x) {
+ int y = x;
+ }
+
+ // Test for representation of long.
+ public static void test13(long x) {
+ long y = x;
+ }
+
+ // Test for representation of short.
+ public static void test14(short x) {
+ short y = x;
+ }
+
+ // Test for representation of Object.
+ public static void test15(Object x) {
+ Object y = x;
+ }
+
+ // Test for representation of String (as a token example of a non-Object
+ // reference type).
+ public static void test16(String x) {
+ String y = x;
+ }
+
+ // Test for representation of int[] (as a token example of an array class).
+ public static void test17(int[] x) {
+ int[] y = x;
+ }
+}
diff --git a/dx/tests/078-dex-local-variable-table/expected.txt b/dx/tests/078-dex-local-variable-table/expected.txt
new file mode 100644
index 0000000..3e5cd69
--- /dev/null
+++ b/dx/tests/078-dex-local-variable-table/expected.txt
@@ -0,0 +1,314 @@
+Blort.test01:(Ljava/lang/Object;)V:
+regs: 0003; ins: 0001; outs: 0001
+ 0000: move-object v0, v2
+ 0001: move-object v1, v0
+ 0002: invoke-virtual {v1}, java.lang.Object.hashCode:()I
+ 0005: move-result v1
+ 0006: return-void
+ debug info
+ line_start: 1
+ parameters_size: 0001
+ parameter <unnamed> v2
+ 0000: prologue end
+ 0001: advance pc
+ 0001: +local v0 x java.lang.Object
+ end sequence
+Blort.test02:()Ljava/lang/Object;:
+regs: 0003; ins: 0000; outs: 0000
+ 0000: const/4 v1, #null // #0
+ 0001: move-object v0, v1
+ 0002: move-object v1, v0
+ 0003: const/4 v2, #int 0 // #0
+ 0004: aget-object v1, v1, v2
+ 0006: move-object v0, v1
+ 0007: return-object v0
+ debug info
+ line_start: 1
+ parameters_size: 0000
+ 0000: prologue end
+ 0002: advance pc
+ 0002: +local v0 arr java.lang.Object[]
+ 0007: advance pc
+ 0007: -local v0 arr java.lang.Object[]
+ end sequence
+Blort.test03:(I)Ljava/lang/String;:
+regs: 0004; ins: 0001; outs: 0000
+ 0000: move v0, v3
+ 0001: const/4 v2, #null // #0
+ 0002: move-object v1, v2
+ 0003: move-object v2, v1
+ 0004: move-object v0, v2
+ 0005: return-object v0
+ debug info
+ line_start: 1
+ parameters_size: 0001
+ parameter <unnamed> v3
+ 0000: prologue end
+ 0001: advance pc
+ 0001: +local v0 x int
+ 0003: advance pc
+ 0003: +local v1 foo java.lang.String
+ 0005: advance pc
+ 0005: -local v0 x int
+ end sequence
+Blort.test04:(I)Ljava/lang/String;:
+regs: 0004; ins: 0001; outs: 0000
+ 0000: move v0, v3
+ 0001: const/4 v2, #null // #0
+ 0002: move-object v1, v2
+ 0003: move v2, v0
+ 0004: if-gez v2, 0009 // +0005
+ 0006: const-string v2, "bar"
+ 0008: move-object v1, v2
+ 0009: move-object v2, v1
+ 000a: move-object v0, v2
+ 000b: return-object v0
+ debug info
+ line_start: 1
+ parameters_size: 0001
+ parameter <unnamed> v3
+ 0000: prologue end
+ 0001: advance pc
+ 0001: +local v0 x int
+ 0003: advance pc
+ 0003: +local v1 foo java.lang.String
+ 000b: advance pc
+ 000b: -local v0 x int
+ end sequence
+Blort.test05:(Ljava/lang/Object;)I:
+regs: 0006; ins: 0001; outs: 0000
+ 0000: move-object v0, v5
+ 0001: move-object v2, v0
+ 0002: check-cast v2, int[]
+ 0004: check-cast v2, int[]
+ 0006: move-object v1, v2
+ 0007: move-object v2, v1
+ 0008: const/4 v3, #int 0 // #0
+ 0009: const/16 v4, #int 123 // #007b
+ 000b: aput v4, v2, v3
+ 000d: move-object v2, v1
+ 000e: const/4 v3, #int 0 // #0
+ 000f: aget v2, v2, v3
+ 0011: move v0, v2
+ 0012: return v0
+ debug info
+ line_start: 1
+ parameters_size: 0001
+ parameter <unnamed> v5
+ 0000: prologue end
+ 0001: advance pc
+ 0001: +local v0 x java.lang.Object
+ 0007: advance pc
+ 0007: +local v1 arr int[]
+ 0012: advance pc
+ 0012: -local v0 x java.lang.Object
+ end sequence
+Blort.test06:(I)I:
+regs: 0005; ins: 0001; outs: 0000
+ 0000: move v0, v4
+ 0001: move v2, v0
+ 0002: const/16 v3, #int 10 // #000a
+ 0004: if-ge v2, v3, 000b // +0007
+ 0006: const/4 v2, #int 1 // #1
+ 0007: move v1, v2
+ 0008: move v2, v1
+ 0009: move v0, v2
+ 000a: return v0
+ 000b: const/4 v2, #int 2 // #2
+ 000c: move v1, v2
+ 000d: move v2, v1
+ 000e: move v0, v2
+ 000f: goto 000a // -0005
+ debug info
+ line_start: 1
+ parameters_size: 0001
+ parameter <unnamed> v4
+ 0000: prologue end
+ 0001: advance pc
+ 0001: +local v0 x int
+ 0008: advance pc
+ 0008: +local v1 y int
+ 000a: advance pc
+ 000a: -local v0 x int
+ 000b: advance pc
+ 000b: -local v1 y int
+ 000b: +local restart v0 x int
+ 000d: advance pc
+ 000d: +local restart v1 y int
+ end sequence
+Blort.test07:(Z)V:
+regs: 0004; ins: 0001; outs: 0000
+ 0000: move v0, v3
+ 0001: move v2, v0
+ 0002: move v1, v2
+ 0003: return-void
+ debug info
+ line_start: 1
+ parameters_size: 0001
+ parameter <unnamed> v3
+ 0000: prologue end
+ 0001: advance pc
+ 0001: +local v0 x boolean
+ 0003: advance pc
+ 0003: +local v1 y boolean
+ end sequence
+Blort.test08:(B)V:
+regs: 0004; ins: 0001; outs: 0000
+ 0000: move v0, v3
+ 0001: move v2, v0
+ 0002: move v1, v2
+ 0003: return-void
+ debug info
+ line_start: 1
+ parameters_size: 0001
+ parameter <unnamed> v3
+ 0000: prologue end
+ 0001: advance pc
+ 0001: +local v0 x byte
+ 0003: advance pc
+ 0003: +local v1 y byte
+ end sequence
+Blort.test09:(C)V:
+regs: 0004; ins: 0001; outs: 0000
+ 0000: move v0, v3
+ 0001: move v2, v0
+ 0002: move v1, v2
+ 0003: return-void
+ debug info
+ line_start: 1
+ parameters_size: 0001
+ parameter <unnamed> v3
+ 0000: prologue end
+ 0001: advance pc
+ 0001: +local v0 x char
+ 0003: advance pc
+ 0003: +local v1 y char
+ end sequence
+Blort.test10:(D)V:
+regs: 0008; ins: 0002; outs: 0000
+ 0000: move-wide v0, v6
+ 0001: move-wide v4, v0
+ 0002: move-wide v2, v4
+ 0003: return-void
+ debug info
+ line_start: 1
+ parameters_size: 0001
+ parameter <unnamed> v6
+ 0000: prologue end
+ 0001: advance pc
+ 0001: +local v0 x double
+ 0003: advance pc
+ 0003: +local v2 y double
+ end sequence
+Blort.test11:(F)V:
+regs: 0004; ins: 0001; outs: 0000
+ 0000: move v0, v3
+ 0001: move v2, v0
+ 0002: move v1, v2
+ 0003: return-void
+ debug info
+ line_start: 1
+ parameters_size: 0001
+ parameter <unnamed> v3
+ 0000: prologue end
+ 0001: advance pc
+ 0001: +local v0 x float
+ 0003: advance pc
+ 0003: +local v1 y float
+ end sequence
+Blort.test12:(I)V:
+regs: 0004; ins: 0001; outs: 0000
+ 0000: move v0, v3
+ 0001: move v2, v0
+ 0002: move v1, v2
+ 0003: return-void
+ debug info
+ line_start: 1
+ parameters_size: 0001
+ parameter <unnamed> v3
+ 0000: prologue end
+ 0001: advance pc
+ 0001: +local v0 x int
+ 0003: advance pc
+ 0003: +local v1 y int
+ end sequence
+Blort.test13:(J)V:
+regs: 0008; ins: 0002; outs: 0000
+ 0000: move-wide v0, v6
+ 0001: move-wide v4, v0
+ 0002: move-wide v2, v4
+ 0003: return-void
+ debug info
+ line_start: 1
+ parameters_size: 0001
+ parameter <unnamed> v6
+ 0000: prologue end
+ 0001: advance pc
+ 0001: +local v0 x long
+ 0003: advance pc
+ 0003: +local v2 y long
+ end sequence
+Blort.test14:(S)V:
+regs: 0004; ins: 0001; outs: 0000
+ 0000: move v0, v3
+ 0001: move v2, v0
+ 0002: move v1, v2
+ 0003: return-void
+ debug info
+ line_start: 1
+ parameters_size: 0001
+ parameter <unnamed> v3
+ 0000: prologue end
+ 0001: advance pc
+ 0001: +local v0 x short
+ 0003: advance pc
+ 0003: +local v1 y short
+ end sequence
+Blort.test15:(Ljava/lang/Object;)V:
+regs: 0004; ins: 0001; outs: 0000
+ 0000: move-object v0, v3
+ 0001: move-object v2, v0
+ 0002: move-object v1, v2
+ 0003: return-void
+ debug info
+ line_start: 1
+ parameters_size: 0001
+ parameter <unnamed> v3
+ 0000: prologue end
+ 0001: advance pc
+ 0001: +local v0 x java.lang.Object
+ 0003: advance pc
+ 0003: +local v1 y java.lang.Object
+ end sequence
+Blort.test16:(Ljava/lang/String;)V:
+regs: 0004; ins: 0001; outs: 0000
+ 0000: move-object v0, v3
+ 0001: move-object v2, v0
+ 0002: move-object v1, v2
+ 0003: return-void
+ debug info
+ line_start: 1
+ parameters_size: 0001
+ parameter <unnamed> v3
+ 0000: prologue end
+ 0001: advance pc
+ 0001: +local v0 x java.lang.String
+ 0003: advance pc
+ 0003: +local v1 y java.lang.String
+ end sequence
+Blort.test17:([I)V:
+regs: 0004; ins: 0001; outs: 0000
+ 0000: move-object v0, v3
+ 0001: move-object v2, v0
+ 0002: move-object v1, v2
+ 0003: return-void
+ debug info
+ line_start: 1
+ parameters_size: 0001
+ parameter <unnamed> v3
+ 0000: prologue end
+ 0001: advance pc
+ 0001: +local v0 x int[]
+ 0003: advance pc
+ 0003: +local v1 y int[]
+ end sequence
diff --git a/dx/tests/078-dex-local-variable-table/info.txt b/dx/tests/078-dex-local-variable-table/info.txt
new file mode 100644
index 0000000..7834271
--- /dev/null
+++ b/dx/tests/078-dex-local-variable-table/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to make sure that
+local variable tables get emitted properly.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/078-dex-local-variable-table/run b/dx/tests/078-dex-local-variable-table/run
new file mode 100644
index 0000000..426f1e6
--- /dev/null
+++ b/dx/tests/078-dex-local-variable-table/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -g -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --dump-method=Blort.test'*' \
+ *.class
diff --git a/dx/tests/079-dex-local-variable-renumbering/Blort.java b/dx/tests/079-dex-local-variable-renumbering/Blort.java
new file mode 100644
index 0000000..c8453a2
--- /dev/null
+++ b/dx/tests/079-dex-local-variable-renumbering/Blort.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ public static int test1(int x) {
+ float f0 = 0.0f;
+ float f1 = 0.0f;
+ float f2 = 0.0f;
+ float f3 = 0.0f;
+ float f4 = 0.0f;
+ float f5 = 0.0f;
+ float f6 = 0.0f;
+ float f7 = 0.0f;
+ float f8 = 0.0f;
+ float f9 = 0.0f;
+ float f10 = 0.0f;
+ float f11 = 0.0f;
+ float f12 = 0.0f;
+ float f13 = 0.0f;
+ float f14 = 0.0f;
+ float f15 = 0.0f;
+ int x16 = x;
+ return -x16;
+ }
+}
diff --git a/dx/tests/079-dex-local-variable-renumbering/expected.txt b/dx/tests/079-dex-local-variable-renumbering/expected.txt
new file mode 100644
index 0000000..94ba113
--- /dev/null
+++ b/dx/tests/079-dex-local-variable-renumbering/expected.txt
@@ -0,0 +1,87 @@
+Blort.test1:(I)I:
+regs: 0015; ins: 0001; outs: 0000
+ 0000: move/from16 v1, v20
+ 0002: const/16 v19, #float 0.0 // #0000
+ 0004: move/from16 v2, v19
+ 0006: const/16 v19, #float 0.0 // #0000
+ 0008: move/from16 v3, v19
+ 000a: const/16 v19, #float 0.0 // #0000
+ 000c: move/from16 v4, v19
+ 000e: const/16 v19, #float 0.0 // #0000
+ 0010: move/from16 v5, v19
+ 0012: const/16 v19, #float 0.0 // #0000
+ 0014: move/from16 v6, v19
+ 0016: const/16 v19, #float 0.0 // #0000
+ 0018: move/from16 v7, v19
+ 001a: const/16 v19, #float 0.0 // #0000
+ 001c: move/from16 v8, v19
+ 001e: const/16 v19, #float 0.0 // #0000
+ 0020: move/from16 v9, v19
+ 0022: const/16 v19, #float 0.0 // #0000
+ 0024: move/from16 v10, v19
+ 0026: const/16 v19, #float 0.0 // #0000
+ 0028: move/from16 v11, v19
+ 002a: const/16 v19, #float 0.0 // #0000
+ 002c: move/from16 v12, v19
+ 002e: const/16 v19, #float 0.0 // #0000
+ 0030: move/from16 v13, v19
+ 0032: const/16 v19, #float 0.0 // #0000
+ 0034: move/from16 v14, v19
+ 0036: const/16 v19, #float 0.0 // #0000
+ 0038: move/from16 v15, v19
+ 003a: const/16 v19, #float 0.0 // #0000
+ 003c: move/from16 v16, v19
+ 003e: const/16 v19, #float 0.0 // #0000
+ 0040: move/from16 v17, v19
+ 0042: move/from16 v19, v1
+ 0044: move/from16 v18, v19
+ 0046: move/from16 v19, v18
+ 0048: move/from16 v0, v19
+ 004a: neg-int v0, v0
+ 004b: move/from16 v19, v0
+ 004d: move/from16 v1, v19
+ 004f: return v1
+ debug info
+ line_start: 1
+ parameters_size: 0001
+ parameter <unnamed> v20
+ 0000: prologue end
+ 0002: advance pc
+ 0002: +local v1 x int
+ 0006: advance pc
+ 0006: +local v2 f0 float
+ 000a: advance pc
+ 000a: +local v3 f1 float
+ 000e: advance pc
+ 000e: +local v4 f2 float
+ 0012: advance pc
+ 0012: +local v5 f3 float
+ 0016: advance pc
+ 0016: +local v6 f4 float
+ 001a: advance pc
+ 001a: +local v7 f5 float
+ 001e: advance pc
+ 001e: +local v8 f6 float
+ 0022: advance pc
+ 0022: +local v9 f7 float
+ 0026: advance pc
+ 0026: +local v10 f8 float
+ 002a: advance pc
+ 002a: +local v11 f9 float
+ 002e: advance pc
+ 002e: +local v12 f10 float
+ 0032: advance pc
+ 0032: +local v13 f11 float
+ 0036: advance pc
+ 0036: +local v14 f12 float
+ 003a: advance pc
+ 003a: +local v15 f13 float
+ 003e: advance pc
+ 003e: +local v16 f14 float
+ 0042: advance pc
+ 0042: +local v17 f15 float
+ 0046: advance pc
+ 0046: +local v18 x16 int
+ 004f: advance pc
+ 004f: -local v1 x int
+ end sequence
diff --git a/dx/tests/079-dex-local-variable-renumbering/info.txt b/dx/tests/079-dex-local-variable-renumbering/info.txt
new file mode 100644
index 0000000..249b23f
--- /dev/null
+++ b/dx/tests/079-dex-local-variable-renumbering/info.txt
@@ -0,0 +1,7 @@
+This is a smoke test of dex conversion, which checks to make sure that
+local variable tables stay in sync when the register set gets renumbered
+to make room for low scratch registers.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/079-dex-local-variable-renumbering/run b/dx/tests/079-dex-local-variable-renumbering/run
new file mode 100644
index 0000000..426f1e6
--- /dev/null
+++ b/dx/tests/079-dex-local-variable-renumbering/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -g -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --dump-method=Blort.test'*' \
+ *.class
diff --git a/dx/tests/080-dex-exception-tables/Blort.java b/dx/tests/080-dex-exception-tables/Blort.java
new file mode 100644
index 0000000..2143f7f
--- /dev/null
+++ b/dx/tests/080-dex-exception-tables/Blort.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ public static void call1() { }
+ public static void call2() { }
+ public static void call3() { }
+ public static void call4() { }
+ public static void call5() { }
+
+ public static int test1() {
+ try {
+ call1();
+ call2();
+ } catch (IndexOutOfBoundsException ex) {
+ return 10;
+ } catch (RuntimeException ex) {
+ return 11;
+ }
+
+ call3();
+ return 12;
+ }
+
+ public static int test2() {
+ try {
+ call1();
+ try {
+ call2();
+ } catch (IndexOutOfBoundsException ex) {
+ return 10;
+ }
+ call3();
+ } catch (RuntimeException ex) {
+ return 11;
+ }
+
+ return 12;
+ }
+
+ public static int test3() {
+ try {
+ call1();
+ try {
+ call2();
+ try {
+ call3();
+ } catch (NullPointerException ex) {
+ return 10;
+ }
+ call4();
+ } catch (IndexOutOfBoundsException ex) {
+ return 11;
+ }
+ call5();
+ } catch (RuntimeException ex) {
+ return 12;
+ }
+
+ return 13;
+ }
+
+ public static int test4() {
+ try {
+ call1();
+ try {
+ call2();
+ try {
+ call3();
+ } catch (NullPointerException ex) {
+ return 10;
+ }
+ } catch (IndexOutOfBoundsException ex) {
+ return 11;
+ }
+ call5();
+ } catch (RuntimeException ex) {
+ return 12;
+ }
+
+ return 13;
+ }
+
+ public static int test5() {
+ try {
+ call1();
+ try {
+ call2();
+ try {
+ call3();
+ } catch (NullPointerException ex) {
+ return 10;
+ }
+ } catch (IndexOutOfBoundsException ex) {
+ return 11;
+ }
+ } catch (RuntimeException ex) {
+ return 12;
+ }
+
+ return 13;
+ }
+
+ public static int test6() {
+ try {
+ try {
+ try {
+ call1();
+ } catch (NullPointerException ex) {
+ return 10;
+ }
+ call2();
+ } catch (IndexOutOfBoundsException ex) {
+ return 11;
+ }
+ call3();
+ } catch (RuntimeException ex) {
+ return 12;
+ }
+
+ call4();
+ return 13;
+ }
+
+ public static int test7() {
+ try {
+ call1();
+ } catch (RuntimeException ex) {
+ return 10;
+ }
+
+ try {
+ call2();
+ } catch (RuntimeException ex) {
+ return 11;
+ }
+
+ return 12;
+ }
+
+ public static int test8() {
+ try {
+ call1();
+ call2();
+ } catch (RuntimeException ex) {
+ return 10;
+ }
+
+ try {
+ call3();
+ call4();
+ } catch (RuntimeException ex) {
+ return 11;
+ }
+
+ return 12;
+ }
+
+ public static int test9() {
+ try {
+ call1();
+ try {
+ call2();
+ } catch (IllegalArgumentException ex) {
+ return 10;
+ }
+ } catch (RuntimeException ex) {
+ return 11;
+ }
+
+ try {
+ call3();
+ try {
+ call4();
+ } catch (IllegalArgumentException ex) {
+ return 12;
+ }
+ } catch (RuntimeException ex) {
+ return 13;
+ }
+
+ return 14;
+ }
+
+}
diff --git a/dx/tests/080-dex-exception-tables/expected.txt b/dx/tests/080-dex-exception-tables/expected.txt
new file mode 100644
index 0000000..4cf43f1
--- /dev/null
+++ b/dx/tests/080-dex-exception-tables/expected.txt
@@ -0,0 +1,286 @@
+Blort.test1:()I:
+regs: 0002; ins: 0000; outs: 0000
+ 0000: invoke-static {}, Blort.call1:()V
+ 0003: invoke-static {}, Blort.call2:()V
+ 0006: invoke-static {}, Blort.call3:()V
+ 0009: const/16 v1, #int 12 // #000c
+ 000b: move v0, v1
+ 000c: return v0
+ 000d: move-exception v1
+ 000e: move-object v0, v1
+ 000f: const/16 v1, #int 10 // #000a
+ 0011: move v0, v1
+ 0012: goto 000c // -0006
+ 0013: move-exception v1
+ 0014: move-object v0, v1
+ 0015: const/16 v1, #int 11 // #000b
+ 0017: move v0, v1
+ 0018: goto 000c // -000c
+ catches
+ tries:
+ try 0000..0006
+ catch java.lang.IndexOutOfBoundsException -> 000d,
+ java.lang.RuntimeException -> 0013
+Blort.test2:()I:
+regs: 0002; ins: 0000; outs: 0000
+ 0000: invoke-static {}, Blort.call1:()V
+ 0003: invoke-static {}, Blort.call2:()V
+ 0006: invoke-static {}, Blort.call3:()V
+ 0009: const/16 v1, #int 12 // #000c
+ 000b: move v0, v1
+ 000c: return v0
+ 000d: move-exception v1
+ 000e: move-object v0, v1
+ 000f: const/16 v1, #int 10 // #000a
+ 0011: move v0, v1
+ 0012: goto 000c // -0006
+ 0013: move-exception v1
+ 0014: move-object v0, v1
+ 0015: const/16 v1, #int 11 // #000b
+ 0017: move v0, v1
+ 0018: goto 000c // -000c
+ catches
+ tries:
+ try 0000..0003
+ catch java.lang.RuntimeException -> 0013
+ try 0003..0006
+ catch java.lang.IndexOutOfBoundsException -> 000d,
+ java.lang.RuntimeException -> 0013
+ try 0006..0009
+ catch java.lang.RuntimeException -> 0013
+Blort.test3:()I:
+regs: 0002; ins: 0000; outs: 0000
+ 0000: invoke-static {}, Blort.call1:()V
+ 0003: invoke-static {}, Blort.call2:()V
+ 0006: invoke-static {}, Blort.call3:()V
+ 0009: invoke-static {}, Blort.call4:()V
+ 000c: invoke-static {}, Blort.call5:()V
+ 000f: const/16 v1, #int 13 // #000d
+ 0011: move v0, v1
+ 0012: return v0
+ 0013: move-exception v1
+ 0014: move-object v0, v1
+ 0015: const/16 v1, #int 10 // #000a
+ 0017: move v0, v1
+ 0018: goto 0012 // -0006
+ 0019: move-exception v1
+ 001a: move-object v0, v1
+ 001b: const/16 v1, #int 11 // #000b
+ 001d: move v0, v1
+ 001e: goto 0012 // -000c
+ 001f: move-exception v1
+ 0020: move-object v0, v1
+ 0021: const/16 v1, #int 12 // #000c
+ 0023: move v0, v1
+ 0024: goto 0012 // -0012
+ catches
+ tries:
+ try 0000..0003
+ catch java.lang.RuntimeException -> 001f
+ try 0003..0006
+ catch java.lang.IndexOutOfBoundsException -> 0019,
+ java.lang.RuntimeException -> 001f
+ try 0006..0009
+ catch java.lang.NullPointerException -> 0013,
+ java.lang.IndexOutOfBoundsException -> 0019,
+ java.lang.RuntimeException -> 001f
+ try 0009..000c
+ catch java.lang.IndexOutOfBoundsException -> 0019,
+ java.lang.RuntimeException -> 001f
+ try 000c..000f
+ catch java.lang.RuntimeException -> 001f
+Blort.test4:()I:
+regs: 0002; ins: 0000; outs: 0000
+ 0000: invoke-static {}, Blort.call1:()V
+ 0003: invoke-static {}, Blort.call2:()V
+ 0006: invoke-static {}, Blort.call3:()V
+ 0009: invoke-static {}, Blort.call5:()V
+ 000c: const/16 v1, #int 13 // #000d
+ 000e: move v0, v1
+ 000f: return v0
+ 0010: move-exception v1
+ 0011: move-object v0, v1
+ 0012: const/16 v1, #int 10 // #000a
+ 0014: move v0, v1
+ 0015: goto 000f // -0006
+ 0016: move-exception v1
+ 0017: move-object v0, v1
+ 0018: const/16 v1, #int 11 // #000b
+ 001a: move v0, v1
+ 001b: goto 000f // -000c
+ 001c: move-exception v1
+ 001d: move-object v0, v1
+ 001e: const/16 v1, #int 12 // #000c
+ 0020: move v0, v1
+ 0021: goto 000f // -0012
+ catches
+ tries:
+ try 0000..0003
+ catch java.lang.RuntimeException -> 001c
+ try 0003..0006
+ catch java.lang.IndexOutOfBoundsException -> 0016,
+ java.lang.RuntimeException -> 001c
+ try 0006..0009
+ catch java.lang.NullPointerException -> 0010,
+ java.lang.IndexOutOfBoundsException -> 0016,
+ java.lang.RuntimeException -> 001c
+ try 0009..000c
+ catch java.lang.RuntimeException -> 001c
+Blort.test5:()I:
+regs: 0002; ins: 0000; outs: 0000
+ 0000: invoke-static {}, Blort.call1:()V
+ 0003: invoke-static {}, Blort.call2:()V
+ 0006: invoke-static {}, Blort.call3:()V
+ 0009: const/16 v1, #int 13 // #000d
+ 000b: move v0, v1
+ 000c: return v0
+ 000d: move-exception v1
+ 000e: move-object v0, v1
+ 000f: const/16 v1, #int 10 // #000a
+ 0011: move v0, v1
+ 0012: goto 000c // -0006
+ 0013: move-exception v1
+ 0014: move-object v0, v1
+ 0015: const/16 v1, #int 11 // #000b
+ 0017: move v0, v1
+ 0018: goto 000c // -000c
+ 0019: move-exception v1
+ 001a: move-object v0, v1
+ 001b: const/16 v1, #int 12 // #000c
+ 001d: move v0, v1
+ 001e: goto 000c // -0012
+ catches
+ tries:
+ try 0000..0003
+ catch java.lang.RuntimeException -> 0019
+ try 0003..0006
+ catch java.lang.IndexOutOfBoundsException -> 0013,
+ java.lang.RuntimeException -> 0019
+ try 0006..0009
+ catch java.lang.NullPointerException -> 000d,
+ java.lang.IndexOutOfBoundsException -> 0013,
+ java.lang.RuntimeException -> 0019
+Blort.test6:()I:
+regs: 0002; ins: 0000; outs: 0000
+ 0000: invoke-static {}, Blort.call1:()V
+ 0003: invoke-static {}, Blort.call2:()V
+ 0006: invoke-static {}, Blort.call3:()V
+ 0009: invoke-static {}, Blort.call4:()V
+ 000c: const/16 v1, #int 13 // #000d
+ 000e: move v0, v1
+ 000f: return v0
+ 0010: move-exception v1
+ 0011: move-object v0, v1
+ 0012: const/16 v1, #int 10 // #000a
+ 0014: move v0, v1
+ 0015: goto 000f // -0006
+ 0016: move-exception v1
+ 0017: move-object v0, v1
+ 0018: const/16 v1, #int 11 // #000b
+ 001a: move v0, v1
+ 001b: goto 000f // -000c
+ 001c: move-exception v1
+ 001d: move-object v0, v1
+ 001e: const/16 v1, #int 12 // #000c
+ 0020: move v0, v1
+ 0021: goto 000f // -0012
+ catches
+ tries:
+ try 0000..0003
+ catch java.lang.NullPointerException -> 0010,
+ java.lang.IndexOutOfBoundsException -> 0016,
+ java.lang.RuntimeException -> 001c
+ try 0003..0006
+ catch java.lang.IndexOutOfBoundsException -> 0016,
+ java.lang.RuntimeException -> 001c
+ try 0006..0009
+ catch java.lang.RuntimeException -> 001c
+Blort.test7:()I:
+regs: 0002; ins: 0000; outs: 0000
+ 0000: invoke-static {}, Blort.call1:()V
+ 0003: invoke-static {}, Blort.call2:()V
+ 0006: const/16 v1, #int 12 // #000c
+ 0008: move v0, v1
+ 0009: return v0
+ 000a: move-exception v1
+ 000b: move-object v0, v1
+ 000c: const/16 v1, #int 10 // #000a
+ 000e: move v0, v1
+ 000f: goto 0009 // -0006
+ 0010: move-exception v1
+ 0011: move-object v0, v1
+ 0012: const/16 v1, #int 11 // #000b
+ 0014: move v0, v1
+ 0015: goto 0009 // -000c
+ catches
+ tries:
+ try 0000..0003
+ catch java.lang.RuntimeException -> 000a
+ try 0003..0006
+ catch java.lang.RuntimeException -> 0010
+Blort.test8:()I:
+regs: 0002; ins: 0000; outs: 0000
+ 0000: invoke-static {}, Blort.call1:()V
+ 0003: invoke-static {}, Blort.call2:()V
+ 0006: invoke-static {}, Blort.call3:()V
+ 0009: invoke-static {}, Blort.call4:()V
+ 000c: const/16 v1, #int 12 // #000c
+ 000e: move v0, v1
+ 000f: return v0
+ 0010: move-exception v1
+ 0011: move-object v0, v1
+ 0012: const/16 v1, #int 10 // #000a
+ 0014: move v0, v1
+ 0015: goto 000f // -0006
+ 0016: move-exception v1
+ 0017: move-object v0, v1
+ 0018: const/16 v1, #int 11 // #000b
+ 001a: move v0, v1
+ 001b: goto 000f // -000c
+ catches
+ tries:
+ try 0000..0006
+ catch java.lang.RuntimeException -> 0010
+ try 0006..000c
+ catch java.lang.RuntimeException -> 0016
+Blort.test9:()I:
+regs: 0002; ins: 0000; outs: 0000
+ 0000: invoke-static {}, Blort.call1:()V
+ 0003: invoke-static {}, Blort.call2:()V
+ 0006: invoke-static {}, Blort.call3:()V
+ 0009: invoke-static {}, Blort.call4:()V
+ 000c: const/16 v1, #int 14 // #000e
+ 000e: move v0, v1
+ 000f: return v0
+ 0010: move-exception v1
+ 0011: move-object v0, v1
+ 0012: const/16 v1, #int 10 // #000a
+ 0014: move v0, v1
+ 0015: goto 000f // -0006
+ 0016: move-exception v1
+ 0017: move-object v0, v1
+ 0018: const/16 v1, #int 11 // #000b
+ 001a: move v0, v1
+ 001b: goto 000f // -000c
+ 001c: move-exception v1
+ 001d: move-object v0, v1
+ 001e: const/16 v1, #int 12 // #000c
+ 0020: move v0, v1
+ 0021: goto 000f // -0012
+ 0022: move-exception v1
+ 0023: move-object v0, v1
+ 0024: const/16 v1, #int 13 // #000d
+ 0026: move v0, v1
+ 0027: goto 000f // -0018
+ catches
+ tries:
+ try 0000..0003
+ catch java.lang.RuntimeException -> 0016
+ try 0003..0006
+ catch java.lang.IllegalArgumentException -> 0010,
+ java.lang.RuntimeException -> 0016
+ try 0006..0009
+ catch java.lang.RuntimeException -> 0022
+ try 0009..000c
+ catch java.lang.IllegalArgumentException -> 001c,
+ java.lang.RuntimeException -> 0022
diff --git a/dx/tests/080-dex-exception-tables/info.txt b/dx/tests/080-dex-exception-tables/info.txt
new file mode 100644
index 0000000..99f2cbc
--- /dev/null
+++ b/dx/tests/080-dex-exception-tables/info.txt
@@ -0,0 +1,8 @@
+This is a smoke test of dex conversion, which checks to make sure that
+exception handler tables get built reasonably (combining entries that
+ought to be combined, listing entries in a correct and sensible order,
+etc.).
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/080-dex-exception-tables/run b/dx/tests/080-dex-exception-tables/run
new file mode 100644
index 0000000..3acfcfd
--- /dev/null
+++ b/dx/tests/080-dex-exception-tables/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -g -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+ --dump-method=Blort.test'*' *.class
diff --git a/dx/tests/081-dex-throws-list/Blort.java b/dx/tests/081-dex-throws-list/Blort.java
new file mode 100644
index 0000000..6011c9c
--- /dev/null
+++ b/dx/tests/081-dex-throws-list/Blort.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ public int test1()
+ throws RuntimeException {
+ throw new RuntimeException();
+ }
+
+ public int test2()
+ throws Throwable, IllegalArgumentException {
+ throw new IllegalArgumentException();
+ }
+}
diff --git a/dx/tests/081-dex-throws-list/expected.txt b/dx/tests/081-dex-throws-list/expected.txt
new file mode 100644
index 0000000..1350edf
--- /dev/null
+++ b/dx/tests/081-dex-throws-list/expected.txt
@@ -0,0 +1,4 @@
+Blort.test1:()I:
+ system-annotation dalvik.annotation.Throws {value: {java.lang.RuntimeException}}
+Blort.test2:()I:
+ system-annotation dalvik.annotation.Throws {value: {java.lang.Throwable, java.lang.IllegalArgumentException}}
diff --git a/dx/tests/081-dex-throws-list/info.txt b/dx/tests/081-dex-throws-list/info.txt
new file mode 100644
index 0000000..eb4bdd7
--- /dev/null
+++ b/dx/tests/081-dex-throws-list/info.txt
@@ -0,0 +1,7 @@
+This is a smoke test of dex conversion, which checks to make sure that
+throws lists (that is, list of declared exceptions on methods) get
+represented reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/081-dex-throws-list/run b/dx/tests/081-dex-throws-list/run
new file mode 100644
index 0000000..2236cf2
--- /dev/null
+++ b/dx/tests/081-dex-throws-list/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -g -d . Blort.java
+dx --debug --dex --positions=none --no-locals --dump-method=Blort.test'*' \
+ *.class | grep 'Blort\|Throws'
diff --git a/dx/tests/082-dex-throws-list-sharing/Blort.java b/dx/tests/082-dex-throws-list-sharing/Blort.java
new file mode 100644
index 0000000..31591d0
--- /dev/null
+++ b/dx/tests/082-dex-throws-list-sharing/Blort.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ public int test1()
+ throws RuntimeException {
+ throw new RuntimeException();
+ }
+
+ public int test2()
+ throws RuntimeException {
+ throw new RuntimeException();
+ }
+
+ public int test3()
+ throws Error, UnsupportedOperationException {
+ throw new RuntimeException();
+ }
+
+ public int test4()
+ throws Error, UnsupportedOperationException {
+ throw new RuntimeException();
+ }
+}
diff --git a/dx/tests/082-dex-throws-list-sharing/expected.txt b/dx/tests/082-dex-throws-list-sharing/expected.txt
new file mode 100644
index 0000000..0f33924
--- /dev/null
+++ b/dx/tests/082-dex-throws-list-sharing/expected.txt
@@ -0,0 +1,2 @@
+java.lang.RuntimeException
+java.lang.Error, java.lang.UnsupportedOperationException
diff --git a/dx/tests/082-dex-throws-list-sharing/info.txt b/dx/tests/082-dex-throws-list-sharing/info.txt
new file mode 100644
index 0000000..3b7dca1
--- /dev/null
+++ b/dx/tests/082-dex-throws-list-sharing/info.txt
@@ -0,0 +1,7 @@
+This is a smoke test of dex conversion, which checks to make sure that
+identical throws lists in different methods get collapsed into a single
+dex file structure.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/082-dex-throws-list-sharing/run b/dx/tests/082-dex-throws-list-sharing/run
new file mode 100644
index 0000000..6eed9bd
--- /dev/null
+++ b/dx/tests/082-dex-throws-list-sharing/run
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -g -d . Blort.java
+dx --debug --dex --positions=none --no-locals --dump-to=- --dump-width=200 \
+ *.class | grep '^[0-9a-f].*value.*Exception' \
+ | sed -e 's/^[^{]*{//g' -e 's/}//g'
diff --git a/dx/tests/083-ssa-phi-placement/Blort.java b/dx/tests/083-ssa-phi-placement/Blort.java
new file mode 100644
index 0000000..ed3264d
--- /dev/null
+++ b/dx/tests/083-ssa-phi-placement/Blort.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+
+ public int phiTest() {
+ int i = 1;
+ int j = 1;
+ int k = 0;
+
+ while (k < 100) {
+ if (j < 20) {
+ j = i;
+ k++;
+ } else {
+ j = k;
+ k += 2;
+ }
+ }
+
+ return j;
+ }
+
+ /**
+ * This method uses no registers.
+ */
+ public static void noVars() {
+ }
+
+ /**
+ * This method requires an ordered successor list with
+ * multiple identically-valued entries.
+ */
+ Object fd;
+ public Object getOption(int optID) throws RuntimeException
+ {
+ if (fd == null) {
+ throw new RuntimeException("socket not created");
+ }
+
+ int value = 0;
+ switch (optID)
+ {
+ case 1:
+ case 2:
+ return new Integer(value);
+ case 3:
+ default:
+ return Boolean.valueOf(value != 0);
+ }
+ }
+}
diff --git a/dx/tests/083-ssa-phi-placement/expected.txt b/dx/tests/083-ssa-phi-placement/expected.txt
new file mode 100644
index 0000000..9a0cb0b
--- /dev/null
+++ b/dx/tests/083-ssa-phi-placement/expected.txt
@@ -0,0 +1,345 @@
+reading Blort.class...
+method <init> ()V
+first 000c
+block 000a
+ pred 000c
+ live in:{}
+ Blort.java:17@0000: move-param-object(0) v0:NffffLBlort; <- .
+ Blort.java:17@0000: goto . <- .
+ next 0000
+ live out:{}
+block 0000
+ pred 000a
+ live in:{}
+ Blort.java:17@0000: move-object v1:NffffLBlort; <- v0:NffffLBlort;
+ Blort.java:17@0001: Rop{invoke-direct . <- Ljava/lang/Object; call throws <an
+ y>}(java.lang.Object.<init>:()V catch) . <- v1:NffffLBlort;
+ next 0004
+ live out:{}
+block 0004
+ pred 0000
+ live in:{}
+ Blort.java:17@0004: goto . <- .
+ next 000b
+ live out:{}
+block 000b
+ pred 0004
+ live in:{}
+ Blort.java:17@0004: return-void . <- .
+ returns
+ live out:{}
+block 000c
+ live in:{}
+ @????: goto . <- .
+ next 000a
+ live out:{}
+
+method phiTest ()I
+first 0048
+block 0046
+ pred 0048
+ live in:{}
+ Blort.java:21@0000: move-param-object(0) v0:LBlort; <- .
+ Blort.java:21@0000: goto . <- .
+ next 0000
+ live out:{}
+block 0000
+ pred 0046
+ live in:{}
+ Blort.java:21@0000: const-int(1) v4:I=1 <- .
+ Blort.java:21@0001: move-int v1:I <- v4:I=1
+ Blort.java:22@0002: const-int(1) v4:I=1 <- .
+ Blort.java:22@0003: move-int v2:I <- v4:I=1
+ Blort.java:23@0004: const-int(0) v4:I=0 <- .
+ Blort.java:23@0005: move-int v3:I <- v4:I=0
+ Blort.java:23@0005: goto . <- .
+ next 0049
+ live out:{}
+block 0006
+ pred 0049
+ live in:{}
+ Blort.java:25@0006: move-int v4:I <- v3:I
+ Blort.java:25@0007: const-int(100) v5:I=100 <- .
+ Blort.java:25@0009: if-ge-int . <- v4:I v5:I=100
+ next 000c *
+ next 0022
+ live out:{}
+block 000c
+ pred 0006
+ live in:{}
+ Blort.java:26@000c: move-int v4:I <- v2:I
+ Blort.java:26@000d: const-int(20) v5:I=20 <- .
+ Blort.java:26@000f: if-ge-int . <- v4:I v5:I=20
+ next 0012 *
+ next 001a
+ live out:{}
+block 0012
+ pred 000c
+ live in:{}
+ Blort.java:27@0012: move-int v4:I <- v1:I
+ Blort.java:27@0013: move-int v2:I <- v4:I
+ Blort.java:28@0014: add-const-int(1) v3:I <- v3:I
+ Blort.java:28@0017: goto . <- .
+ next 0049
+ live out:{}
+block 001a
+ pred 000c
+ live in:{}
+ Blort.java:30@001a: move-int v4:I <- v3:I
+ Blort.java:30@001b: move-int v2:I <- v4:I
+ Blort.java:31@001c: add-const-int(2) v3:I <- v3:I
+ Blort.java:31@001f: goto . <- .
+ next 0049
+ live out:{}
+block 0022
+ pred 0006
+ live in:{}
+ Blort.java:35@0022: move-int v4:I <- v2:I
+ Blort.java:35@0023: move-int v0:I <- v4:I
+ Blort.java:35@0023: goto . <- .
+ next 0047
+ live out:{}
+block 0047
+ pred 0022
+ live in:{}
+ Blort.java:35@0023: return-int . <- v0:I
+ returns
+ live out:{}
+block 0048
+ live in:{}
+ @????: goto . <- .
+ next 0046
+ live out:{}
+block 0049
+ pred 0000
+ pred 0012
+ pred 001a
+ live in:{}
+ @????: phi v5:V <- .
+ @????: phi v4:V <- .
+ @????: phi v3:V <- .
+ @????: phi v2:V <- .
+ @????: goto . <- .
+ next 0006
+ live out:{}
+
+method noVars ()V
+first 0004
+block 0002
+ pred 0004
+ live in:{}
+ Blort.java:42@0000: goto . <- .
+ next 0000
+ live out:{}
+block 0000
+ pred 0002
+ live in:{}
+ Blort.java:42@0000: goto . <- .
+ next 0003
+ live out:{}
+block 0003
+ pred 0000
+ live in:{}
+ Blort.java:42@0000: return-void . <- .
+ returns
+ live out:{}
+block 0004
+ live in:{}
+ @????: goto . <- .
+ next 0002
+ live out:{}
+
+method getOption (I)Ljava/lang/Object;
+first 0098
+block 008c
+ pred 0098
+ live in:{}
+ Blort.java:51@0000: move-param-object(0) v0:LBlort; <- .
+ Blort.java:51@0000: move-param-int(1) v1:I <- .
+ Blort.java:51@0000: goto . <- .
+ next 0000
+ live out:{}
+block 0093
+ pred 0000
+ live in:{}
+ Blort.java:51@0001: Rop{move-result-pseudo Ljava/lang/Object; <- . flows} v3:
+ Ljava/lang/Object; <- .
+ Blort.java:51@0001: goto . <- .
+ next 0004
+ live out:{}
+block 0000
+ pred 008c
+ live in:{}
+ Blort.java:51@0000: move-object v3:LBlort; <- v0:LBlort;
+ Blort.java:51@0001: get-field-object(Blort.fd:Ljava/lang/Object; catch) . <-
+ v3:LBlort;
+ next 0093
+ live out:{}
+block 0004
+ pred 0093
+ live in:{}
+ Blort.java:51@0004: if-nez-object . <- v3:Ljava/lang/Object;
+ next 0007 *
+ next 0011
+ live out:{}
+block 0094
+ pred 0007
+ live in:{}
+ Blort.java:52@0007: Rop{move-result-pseudo N0007Ljava/lang/RuntimeException;
+ <- . flows} v3:N0007Ljava/lang/RuntimeException; <- .
+ Blort.java:52@0007: goto . <- .
+ next 000a
+ live out:{}
+block 0007
+ pred 0004
+ live in:{}
+ Blort.java:52@0007: new-instance(java.lang.RuntimeException catch) . <- .
+ next 0094
+ live out:{}
+block 0095
+ pred 000a
+ live in:{}
+ Blort.java:52@000b: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v5:
+ Ljava/lang/String;="socket not created" <- .
+ Blort.java:52@000b: goto . <- .
+ next 000d
+ live out:{}
+block 000a
+ pred 0094
+ live in:{}
+ Blort.java:52@000a: move-object v6:N0007Ljava/lang/RuntimeException; <- v3:N0
+ 007Ljava/lang/RuntimeException;
+ Blort.java:52@000a: move-object v3:N0007Ljava/lang/RuntimeException; <- v6:N0
+ 007Ljava/lang/RuntimeException;
+ Blort.java:52@000a: move-object v4:N0007Ljava/lang/RuntimeException; <- v6:N0
+ 007Ljava/lang/RuntimeException;
+ Blort.java:52@000b: const-object("socket not created" catch) . <- .
+ next 0095
+ live out:{}
+block 000d
+ pred 0095
+ live in:{}
+ Blort.java:52@000d: Rop{invoke-direct . <- Ljava/lang/RuntimeException; Ljava
+ /lang/String; call throws <any>}(java.lang.RuntimeException.<init>:(Ljava/lan
+ g/String;)V catch) . <- v4:N0007Ljava/lang/RuntimeException; v5:Ljava/lang/St
+ ring;="socket not created"
+ next 0010
+ live out:{}
+block 0010
+ pred 000d
+ live in:{}
+ Blort.java:52@0010: throw(catch) . <- v3:Ljava/lang/RuntimeException;
+ returns
+ live out:{}
+block 0011
+ pred 0004
+ live in:{}
+ Blort.java:55@0011: const-int(0) v3:I=0 <- .
+ Blort.java:55@0012: move-int v2:I <- v3:I=0
+ Blort.java:56@0013: move-int v3:I <- v1:I
+ Blort.java:56@0014: switch({1, 2}) . <- v3:I
+ next 0030
+ next 0030
+ next 0039 *
+ live out:{}
+block 0096
+ pred 0030
+ live in:{}
+ Blort.java:60@0030: Rop{move-result-pseudo N0030Ljava/lang/Integer; <- . flow
+ s} v3:N0030Ljava/lang/Integer; <- .
+ Blort.java:60@0030: goto . <- .
+ next 0033
+ live out:{}
+block 0030
+ pred 0011
+ live in:{}
+ Blort.java:60@0030: new-instance(java.lang.Integer catch) . <- .
+ next 0096
+ live out:{}
+block 0033
+ pred 0096
+ live in:{}
+ Blort.java:60@0033: move-object v6:N0030Ljava/lang/Integer; <- v3:N0030Ljava/
+ lang/Integer;
+ Blort.java:60@0033: move-object v3:N0030Ljava/lang/Integer; <- v6:N0030Ljava/
+ lang/Integer;
+ Blort.java:60@0033: move-object v4:N0030Ljava/lang/Integer; <- v6:N0030Ljava/
+ lang/Integer;
+ Blort.java:60@0034: move-int v5:I <- v2:I
+ Blort.java:60@0035: Rop{invoke-direct . <- Ljava/lang/Integer; I call throws
+ <any>}(java.lang.Integer.<init>:(I)V catch) . <- v4:N0030Ljava/lang/Integer;
+ v5:I
+ next 0038
+ live out:{}
+block 0038
+ pred 0033
+ live in:{}
+ Blort.java:60@0038: move-object v0:Ljava/lang/Integer; <- v3:Ljava/lang/Integ
+ er;
+ Blort.java:60@0038: goto . <- .
+ next 008d
+ live out:{}
+block 0039
+ pred 0011
+ live in:{}
+ Blort.java:63@0039: move-int v3:I <- v2:I
+ Blort.java:63@003a: if-eqz-int . <- v3:I
+ next 003d *
+ next 0041
+ live out:{}
+block 003d
+ pred 0039
+ live in:{}
+ Blort.java:63@003d: const-int(1) v3:I=1 <- .
+ Blort.java:63@003e: goto . <- .
+ next 0042
+ live out:{}
+block 0041
+ pred 0039
+ live in:{}
+ Blort.java:63@0041: const-int(0) v3:I=0 <- .
+ Blort.java:63@0041: goto . <- .
+ next 0042
+ live out:{}
+block 0097
+ pred 0042
+ live in:{}
+ Blort.java:63@0042: Rop{move-result Ljava/lang/Boolean; <- . flows} v3:Ljava/
+ lang/Boolean; <- .
+ Blort.java:63@0042: goto . <- .
+ next 0045
+ live out:{}
+block 0042
+ pred 003d
+ pred 0041
+ live in:{}
+ @????: phi v3:V <- .
+ Blort.java:63@0042: Rop{invoke-static . <- I call throws <any>}(java.lang.Boo
+ lean.valueOf:(Z)Ljava/lang/Boolean; catch) . <- v3:I
+ next 0097
+ live out:{}
+block 0045
+ pred 0097
+ live in:{}
+ Blort.java:63@0045: move-object v0:Ljava/lang/Boolean; <- v3:Ljava/lang/Boole
+ an;
+ Blort.java:63@0045: goto . <- .
+ next 008d
+ live out:{}
+block 008d
+ pred 0038
+ pred 0045
+ live in:{}
+ @????: phi v6:V <- .
+ @????: phi v5:V <- .
+ @????: phi v4:V <- .
+ @????: phi v3:V <- .
+ @????: phi v0:V <- .
+ Blort.java:63@0045: return-object . <- v0:Ljava/lang/Object;
+ returns
+ live out:{}
+block 0098
+ live in:{}
+ @????: goto . <- .
+ next 008c
+ live out:{}
diff --git a/dx/tests/083-ssa-phi-placement/info.txt b/dx/tests/083-ssa-phi-placement/info.txt
new file mode 100644
index 0000000..8d4eebf
--- /dev/null
+++ b/dx/tests/083-ssa-phi-placement/info.txt
@@ -0,0 +1,5 @@
+This is a test case for the phi placement algorthim used in the conversion to SSA form.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/083-ssa-phi-placement/run b/dx/tests/083-ssa-phi-placement/run
new file mode 100644
index 0000000..ddcb597
--- /dev/null
+++ b/dx/tests/083-ssa-phi-placement/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --dump --ssa-blocks --ssa-step=phi-placement Blort.class
diff --git a/dx/tests/084-dex-high-register-moves/Blort.java b/dx/tests/084-dex-high-register-moves/Blort.java
new file mode 100644
index 0000000..e68ebd8
--- /dev/null
+++ b/dx/tests/084-dex-high-register-moves/Blort.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ private static int i;
+ private static long l;
+ private static Object o;
+
+ public static void test() {
+ int i0 = 0;
+ int i1 = 0;
+ int i2 = 0;
+ int i3 = 0;
+ int i4 = 0;
+ int i5 = 0;
+ int i6 = 0;
+ int i7 = 0;
+ int i8 = 0;
+ int i9 = 0;
+ int i10 = 0;
+ int i11 = 0;
+ int i12 = 0;
+ int i13 = 0;
+ int i14 = 0;
+ int i15 = 0;
+
+ int ix = i;
+ long lx = l;
+ Object ox = o;
+
+ i = -ix;
+ l = -lx;
+ i = (ox instanceof String) ? 0 : 1;
+ }
+}
diff --git a/dx/tests/084-dex-high-register-moves/expected.txt b/dx/tests/084-dex-high-register-moves/expected.txt
new file mode 100644
index 0000000..33466c4
--- /dev/null
+++ b/dx/tests/084-dex-high-register-moves/expected.txt
@@ -0,0 +1,60 @@
+Blort.test:()V:
+regs: 0018; ins: 0000; outs: 0000
+ 0000: const/16 v22, #int 0 // #0000
+ 0002: move/from16 v2, v22
+ 0004: const/16 v22, #int 0 // #0000
+ 0006: move/from16 v3, v22
+ 0008: const/16 v22, #int 0 // #0000
+ 000a: move/from16 v4, v22
+ 000c: const/16 v22, #int 0 // #0000
+ 000e: move/from16 v5, v22
+ 0010: const/16 v22, #int 0 // #0000
+ 0012: move/from16 v6, v22
+ 0014: const/16 v22, #int 0 // #0000
+ 0016: move/from16 v7, v22
+ 0018: const/16 v22, #int 0 // #0000
+ 001a: move/from16 v8, v22
+ 001c: const/16 v22, #int 0 // #0000
+ 001e: move/from16 v9, v22
+ 0020: const/16 v22, #int 0 // #0000
+ 0022: move/from16 v10, v22
+ 0024: const/16 v22, #int 0 // #0000
+ 0026: move/from16 v11, v22
+ 0028: const/16 v22, #int 0 // #0000
+ 002a: move/from16 v12, v22
+ 002c: const/16 v22, #int 0 // #0000
+ 002e: move/from16 v13, v22
+ 0030: const/16 v22, #int 0 // #0000
+ 0032: move/from16 v14, v22
+ 0034: const/16 v22, #int 0 // #0000
+ 0036: move/from16 v15, v22
+ 0038: const/16 v22, #int 0 // #0000
+ 003a: move/from16 v16, v22
+ 003c: const/16 v22, #int 0 // #0000
+ 003e: move/from16 v17, v22
+ 0040: sget v22, Blort.i:I
+ 0042: move/from16 v18, v22
+ 0044: sget-wide v22, Blort.l:J
+ 0046: move-wide/from16 v19, v22
+ 0048: sget-object v22, Blort.o:Ljava/lang/Object;
+ 004a: move-object/from16 v21, v22
+ 004c: move/from16 v22, v18
+ 004e: move/from16 v0, v22
+ 0050: neg-int v0, v0
+ 0051: move/from16 v22, v0
+ 0053: sput v22, Blort.i:I
+ 0055: move-wide/from16 v22, v19
+ 0057: move-wide/from16 v0, v22
+ 0059: neg-long v0, v0
+ 005a: move-wide/from16 v22, v0
+ 005c: sput-wide v22, Blort.l:J
+ 005e: move-object/from16 v22, v21
+ 0060: move-object/from16 v0, v22
+ 0062: instance-of v0, v0, java.lang.String
+ 0064: move/from16 v22, v0
+ 0066: if-eqz v22, 006d // +0007
+ 0068: const/16 v22, #int 0 // #0000
+ 006a: sput v22, Blort.i:I
+ 006c: return-void
+ 006d: const/16 v22, #int 1 // #0001
+ 006f: goto 006a // -0005
diff --git a/dx/tests/084-dex-high-register-moves/info.txt b/dx/tests/084-dex-high-register-moves/info.txt
new file mode 100644
index 0000000..77f0945
--- /dev/null
+++ b/dx/tests/084-dex-high-register-moves/info.txt
@@ -0,0 +1,7 @@
+This is a smoke test of dex conversion, which checks to make sure that
+high registers are moved to and from low registers with
+type-appropriate instructions.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/084-dex-high-register-moves/run b/dx/tests/084-dex-high-register-moves/run
new file mode 100644
index 0000000..3acfcfd
--- /dev/null
+++ b/dx/tests/084-dex-high-register-moves/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -g -d . Blort.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+ --dump-method=Blort.test'*' *.class
diff --git a/dx/tests/085-dex-jsr-ret/blort.j b/dx/tests/085-dex-jsr-ret/blort.j
new file mode 100644
index 0000000..2616723
--- /dev/null
+++ b/dx/tests/085-dex-jsr-ret/blort.j
@@ -0,0 +1,69 @@
+; Copyright (C) 2007 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class blort
+.super java/lang/Object
+
+.method public <init>()V
+ .limit locals 2
+ .limit stack 3
+
+ aload_0
+ dup
+ dup
+ astore_1
+ pop2
+ return
+.end method
+
+; Test jsr and jsr_w.
+.method public test_jsr()Ljava/lang/Object;
+ .limit locals 3
+ .limit stack 4
+ aload_0
+ jsr j1
+ aload_0
+ pop
+ ; Call j1 with different locals
+ ldc 10
+ astore_0
+ jsr j1
+ aload_0
+ pop
+ jsr j3
+ areturn
+j1:
+ astore_2
+ jsr_w j2
+ ret 2
+j2:
+ ; a subroutine with two returns and a catch block
+ astore_1
+ dup
+ dup
+ ; Just something that could throw an exception...
+ invokevirtual blort.test_jsr()V
+ ifnonnull j2a
+ ret_w 1
+j2a:
+ ret_w 1
+j3:
+ ; a subroutine that does not return
+ pop
+ areturn
+catchBlock:
+ areturn
+
+.catch java/lang/Throwable from j2 to j2a using catchBlock
+.end method
diff --git a/dx/tests/085-dex-jsr-ret/expected.txt b/dx/tests/085-dex-jsr-ret/expected.txt
new file mode 100644
index 0000000..ba61996
--- /dev/null
+++ b/dx/tests/085-dex-jsr-ret/expected.txt
@@ -0,0 +1,171 @@
+Generated: ./blort.class
+reading blort.class...
+method <init> ()V
+first 0002
+block 0002
+ blort.j:@0000: move-param-object(0) v0:NffffLblort; <- .
+ blort.j:@0000: goto . <- .
+ next 0000
+block 0000
+ pred 0002
+ blort.j:@0000: move-object v2:NffffLblort; <- v0:NffffLblort;
+ blort.j:@0001: move-object v5:NffffLblort; <- v2:NffffLblort;
+ blort.j:@0001: move-object v2:NffffLblort; <- v5:NffffLblort;
+ blort.j:@0001: move-object v3:NffffLblort; <- v5:NffffLblort;
+ blort.j:@0002: move-object v5:NffffLblort; <- v3:NffffLblort;
+ blort.j:@0002: move-object v3:NffffLblort; <- v5:NffffLblort;
+ blort.j:@0002: move-object v4:NffffLblort; <- v5:NffffLblort;
+ blort.j:@0003: move-object v1:NffffLblort; <- v4:NffffLblort;
+ blort.j:@0005: goto . <- .
+ next 0003
+block 0003
+ pred 0000
+ blort.j:@0005: return-void . <- .
+ returns
+
+method test_jsr ()Ljava/lang/Object;
+first 005c
+block 005c
+ blort.j:@0000: move-param-object(0) v0:Lblort; <- .
+ blort.j:@0000: goto . <- .
+ next 0000
+block 0000
+ pred 005c
+ blort.j:@0000: move-object v3:Lblort; <- v0:Lblort;
+ blort.j:@0000: goto . <- .
+ next 0001
+block 0004
+ pred 0065
+ blort.j:@0004: move-object v4:Lblort; <- v0:Lblort;
+ blort.j:@0006: const-int(10) v4:I=10 <- .
+ blort.j:@0008: move-int v0:I=10 <- v4:I=10
+ blort.j:@0008: goto . <- .
+ next 0009
+block 000c
+ pred 006e
+ blort.j:@000c: move-int v4:I=10 <- v0:I=10
+ blort.j:@000c: goto . <- .
+ next 000e
+block 005d
+ pred 006b
+ pred 0074
+ pred 0075
+ blort.j:@002c: return-object . <- v0:Ljava/lang/Object;
+ returns
+block 0063
+ pred 0001
+ blort.j:@0012: goto . <- .
+ next 0064
+block 0066
+ pred 0064
+ blort.j:@001b: move-object v7:Lblort; <- v3:Lblort;
+ blort.j:@001b: move-object v3:Lblort; <- v7:Lblort;
+ blort.j:@001b: move-object v4:Lblort; <- v7:Lblort;
+ blort.j:@001c: move-object v7:Lblort; <- v4:Lblort;
+ blort.j:@001c: move-object v4:Lblort; <- v7:Lblort;
+ blort.j:@001c: move-object v5:Lblort; <- v7:Lblort;
+ blort.j:@001d: Rop{invoke-virtual . <- Lblort; call throws <any>}(blort.test_
+ jsr:()V catch java.lang.Throwable) . <- v5:Lblort;
+ next 0067
+ next 0068 *
+block 0068
+ pred 0066
+ blort.j:@0020: if-nez-object . <- v4:Lblort;
+ next 0069 *
+ next 006a
+block 0069
+ pred 0068
+ @????: goto . <- .
+ next 0065
+block 006a
+ pred 0068
+ @????: goto . <- .
+ next 0065
+block 0067
+ pred 0066
+ blort.j:@002d: Rop{move-exception Ljava/lang/Throwable; <- . flows} v3:Ljava/
+ lang/Throwable; <- .
+ blort.j:@002d: goto . <- .
+ next 006b
+block 006b
+ pred 0067
+ blort.j:@002d: move-object v0:Ljava/lang/Class;=java.lang.Throwable <- v3:Lja
+ va/lang/Class;=java.lang.Throwable
+ blort.j:@002d: goto . <- .
+ next 005d
+block 0064
+ pred 0063
+ @????: goto . <- .
+ next 0066
+block 0065
+ pred 0069
+ pred 006a
+ @????: goto . <- .
+ next 0004
+block 0001
+ pred 0000
+ @????: goto . <- .
+ next 0063
+block 006c
+ pred 0009
+ blort.j:@0012: goto . <- .
+ next 006d
+block 006f
+ pred 006d
+ blort.j:@001b: move-object v7:Lblort; <- v3:Lblort;
+ blort.j:@001b: move-object v3:Lblort; <- v7:Lblort;
+ blort.j:@001b: move-object v4:Lblort; <- v7:Lblort;
+ blort.j:@001c: move-object v7:Lblort; <- v4:Lblort;
+ blort.j:@001c: move-object v4:Lblort; <- v7:Lblort;
+ blort.j:@001c: move-object v5:Lblort; <- v7:Lblort;
+ blort.j:@001d: Rop{invoke-virtual . <- Lblort; call throws <any>}(blort.test_
+ jsr:()V catch java.lang.Throwable) . <- v5:Lblort;
+ next 0070
+ next 0071 *
+block 0071
+ pred 006f
+ blort.j:@0020: if-nez-object . <- v4:Lblort;
+ next 0072 *
+ next 0073
+block 0072
+ pred 0071
+ @????: goto . <- .
+ next 006e
+block 0073
+ pred 0071
+ @????: goto . <- .
+ next 006e
+block 0070
+ pred 006f
+ blort.j:@002d: Rop{move-exception Ljava/lang/Throwable; <- . flows} v3:Ljava/
+ lang/Throwable; <- .
+ blort.j:@002d: goto . <- .
+ next 0074
+block 0074
+ pred 0070
+ blort.j:@002d: move-object v0:Ljava/lang/Class;=java.lang.Throwable <- v3:Lja
+ va/lang/Class;=java.lang.Throwable
+ blort.j:@002d: goto . <- .
+ next 005d
+block 006d
+ pred 006c
+ @????: goto . <- .
+ next 006f
+block 006e
+ pred 0072
+ pred 0073
+ @????: goto . <- .
+ next 000c
+block 0009
+ pred 0004
+ @????: goto . <- .
+ next 006c
+block 0075
+ pred 000e
+ blort.j:@002c: move-object v0:Lblort; <- v3:Lblort;
+ blort.j:@002c: goto . <- .
+ next 005d
+block 000e
+ pred 000c
+ @????: goto . <- .
+ next 0075
diff --git a/dx/tests/085-dex-jsr-ret/info.txt b/dx/tests/085-dex-jsr-ret/info.txt
new file mode 100644
index 0000000..8e164d6
--- /dev/null
+++ b/dx/tests/085-dex-jsr-ret/info.txt
@@ -0,0 +1 @@
+Tests handling of the Java jsr/jsr_w/ret bytecodes.
diff --git a/dx/tests/085-dex-jsr-ret/run b/dx/tests/085-dex-jsr-ret/run
new file mode 100644
index 0000000..00a7404
--- /dev/null
+++ b/dx/tests/085-dex-jsr-ret/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+jasmin -d . blort.j
+dx --dump --rop-blocks blort.class
diff --git a/dx/tests/086-ssa-edge-split/Blort.java b/dx/tests/086-ssa-edge-split/Blort.java
new file mode 100644
index 0000000..d12b3f9
--- /dev/null
+++ b/dx/tests/086-ssa-edge-split/Blort.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ /**
+ * This method requires the edge-splitter to add a node
+ * to get to the finally block, since there are
+ * two exception sources.
+ *
+ */
+ public int edgeSplitPredTest(int x) {
+ int y = 1;
+
+ try {
+ Integer.toString(x);
+ Integer.toString(x);
+ y++;
+ } finally {
+ return y;
+ }
+ }
+
+ /**
+ * just because this should do nothing
+ */
+ void voidFunction() {
+ }
+
+ /**
+ * Current SSA form requires each move-exception block to have
+ * a unique predecessor
+ */
+ void edgeSplitMoveException() {
+ try {
+ hashCode();
+ hashCode();
+ } catch (Throwable tr) {
+ }
+ }
+
+ /**
+ * Presently, any basic block ending in an instruction with
+ * a result needs to have a unique successor. This appies
+ * only to the block between the switch instruction and the return
+ * in this case.
+ */
+ int edgeSplitSuccessor(int x) {
+ int y = 0;
+
+ switch(x) {
+ case 1: y++;
+ break;
+ case 2: y++;
+ break;
+ case 3: y++;
+ break;
+ }
+ return y;
+ }
+}
diff --git a/dx/tests/086-ssa-edge-split/expected.txt b/dx/tests/086-ssa-edge-split/expected.txt
new file mode 100644
index 0000000..e59af31
--- /dev/null
+++ b/dx/tests/086-ssa-edge-split/expected.txt
@@ -0,0 +1,343 @@
+reading Blort.class...
+method <init> ()V
+first 000c
+block 000a
+ pred 000c
+ live in:{}
+ Blort.java:17@0000: move-param-object(0) v0:NffffLBlort; <- .
+ Blort.java:17@0000: goto . <- .
+ next 0000
+ live out:{}
+block 0000
+ pred 000a
+ live in:{}
+ Blort.java:17@0000: move-object v1:NffffLBlort; <- v0:NffffLBlort;
+ Blort.java:17@0001: Rop{invoke-direct . <- Ljava/lang/Object; call throws <any>}(java.lang.Object.<init>:()V catch) . <- v1:NffffLBlort;
+ next 0004
+ live out:{}
+block 0004
+ pred 0000
+ live in:{}
+ Blort.java:17@0004: goto . <- .
+ next 000b
+ live out:{}
+block 000b
+ pred 0004
+ live in:{}
+ Blort.java:17@0004: return-void . <- .
+ returns
+ live out:{}
+block 000c
+ live in:{}
+ @????: goto . <- .
+ next 000a
+ live out:{}
+
+method edgeSplitPredTest (I)I
+first 002f
+block 0026
+ pred 002f
+ live in:{}
+ Blort.java:26@0000: move-param-object(0) v0:LBlort; <- .
+ Blort.java:26@0000: move-param-int(1) v1:I <- .
+ Blort.java:26@0000: goto . <- .
+ next 0000
+ live out:{}
+block 0000
+ pred 0026
+ live in:{}
+ Blort.java:26@0000: const-int(1) v4:I=1 <- .
+ Blort.java:26@0001: move-int v2:I <- v4:I=1
+ Blort.java:26@0001: goto . <- .
+ next 0002
+ live out:{}
+block 002d
+ pred 0002
+ live in:{}
+ Blort.java:29@0003: Rop{move-result Ljava/lang/String; <- . flows} v4:Ljava/lang/String; <- .
+ Blort.java:29@0003: goto . <- .
+ next 0006
+ live out:{}
+block 0002
+ pred 0000
+ live in:{}
+ Blort.java:29@0002: move-int v4:I <- v1:I
+ Blort.java:29@0003: Rop{invoke-static . <- I call throws <any>}(java.lang.Integer.toString:(I)Ljava/lang/String; catch java.lang.Object) . <- v4:I
+ next 0030
+ next 002d *
+ live out:{}
+block 002e
+ pred 0006
+ live in:{}
+ Blort.java:30@0008: Rop{move-result Ljava/lang/String; <- . flows} v4:Ljava/lang/String; <- .
+ Blort.java:30@0008: goto . <- .
+ next 000b
+ live out:{}
+block 0006
+ pred 002d
+ live in:{}
+ Blort.java:30@0007: move-int v4:I <- v1:I
+ Blort.java:30@0008: Rop{invoke-static . <- I call throws <any>}(java.lang.Integer.toString:(I)Ljava/lang/String; catch java.lang.Object) . <- v4:I
+ next 0031
+ next 002e *
+ live out:{}
+block 000b
+ pred 002e
+ live in:{}
+ Blort.java:31@000c: add-const-int(1) v2:I <- v2:I
+ Blort.java:31@000c: goto . <- .
+ next 000f
+ live out:{}
+block 000f
+ pred 000b
+ live in:{}
+ Blort.java:33@000f: move-int v4:I <- v2:I
+ Blort.java:33@0010: move-int v0:I <- v4:I
+ Blort.java:33@0010: goto . <- .
+ next 0027
+ live out:{}
+block 0011
+ pred 0024
+ live in:{}
+ Blort.java:33@0011: move-object v3:Ljava/lang/Class;=java.lang.Object <- v4:Ljava/lang/Class;=java.lang.Object
+ Blort.java:33@0011: goto . <- .
+ next 0012
+ live out:{}
+block 0012
+ pred 0011
+ live in:{}
+ Blort.java:33@0012: move-int v4:I <- v2:I
+ Blort.java:33@0013: move-int v0:I <- v4:I
+ Blort.java:33@0013: goto . <- .
+ next 0027
+ live out:{}
+block 0027
+ pred 000f
+ pred 0012
+ live in:{}
+ Blort.java:33@0010: return-int . <- v0:I
+ returns
+ live out:{}
+block 0024
+ pred 0030
+ pred 0031
+ live in:{}
+ Blort.java:33@0011: goto . <- .
+ next 0011
+ live out:{}
+block 002f
+ live in:{}
+ @????: goto . <- .
+ next 0026
+ live out:{}
+block 0030
+ pred 0002
+ live in:{}
+ Blort.java:33@0011: Rop{move-exception Ljava/lang/Object; <- . flows} v4:Ljava/lang/Object; <- .
+ @????: goto . <- .
+ next 0024
+ live out:{}
+block 0031
+ pred 0006
+ live in:{}
+ Blort.java:33@0011: Rop{move-exception Ljava/lang/Object; <- . flows} v4:Ljava/lang/Object; <- .
+ @????: goto . <- .
+ next 0024
+ live out:{}
+
+method voidFunction ()V
+first 0004
+block 0002
+ pred 0004
+ live in:{}
+ Blort.java:41@0000: move-param-object(0) v0:LBlort; <- .
+ Blort.java:41@0000: goto . <- .
+ next 0000
+ live out:{}
+block 0000
+ pred 0002
+ live in:{}
+ Blort.java:41@0000: goto . <- .
+ next 0003
+ live out:{}
+block 0003
+ pred 0000
+ live in:{}
+ Blort.java:41@0000: return-void . <- .
+ returns
+ live out:{}
+block 0004
+ live in:{}
+ @????: goto . <- .
+ next 0002
+ live out:{}
+
+method edgeSplitMoveException ()V
+first 0027
+block 001e
+ pred 0027
+ live in:{}
+ Blort.java:49@0000: move-param-object(0) v0:LBlort; <- .
+ Blort.java:49@0000: goto . <- .
+ next 0000
+ live out:{}
+block 0025
+ pred 0000
+ live in:{}
+ Blort.java:49@0001: Rop{move-result I <- . flows} v2:I <- .
+ Blort.java:49@0001: goto . <- .
+ next 0004
+ live out:{}
+block 0000
+ pred 001e
+ live in:{}
+ Blort.java:49@0000: move-object v2:LBlort; <- v0:LBlort;
+ Blort.java:49@0001: Rop{invoke-virtual . <- Ljava/lang/Object; call throws <any>}(java.lang.Object.hashCode:()I catch java.lang.Throwable) . <- v2:LBlort;
+ next 0028
+ next 0025 *
+ live out:{}
+block 0026
+ pred 0004
+ live in:{}
+ Blort.java:50@0006: Rop{move-result I <- . flows} v2:I <- .
+ Blort.java:50@0006: goto . <- .
+ next 0009
+ live out:{}
+block 0004
+ pred 0025
+ live in:{}
+ Blort.java:50@0005: move-object v2:LBlort; <- v0:LBlort;
+ Blort.java:50@0006: Rop{invoke-virtual . <- Ljava/lang/Object; call throws <any>}(java.lang.Object.hashCode:()I catch java.lang.Throwable) . <- v2:LBlort;
+ next 0029
+ next 0026 *
+ live out:{}
+block 0009
+ pred 0026
+ live in:{}
+ @????: goto . <- .
+ next 000a
+ live out:{}
+block 000a
+ pred 0009
+ live in:{}
+ Blort.java:52@000a: goto . <- .
+ next 000e
+ live out:{}
+block 000d
+ pred 001c
+ live in:{}
+ Blort.java:51@000d: move-object v1:Ljava/lang/Class;=java.lang.Throwable <- v2:Ljava/lang/Class;=java.lang.Throwable
+ Blort.java:51@000d: goto . <- .
+ next 000e
+ live out:{}
+block 000e
+ pred 000a
+ pred 000d
+ live in:{}
+ Blort.java:53@000e: goto . <- .
+ next 001f
+ live out:{}
+block 001f
+ pred 000e
+ live in:{}
+ Blort.java:53@000e: return-void . <- .
+ returns
+ live out:{}
+block 001c
+ pred 0028
+ pred 0029
+ live in:{}
+ Blort.java:51@000d: goto . <- .
+ next 000d
+ live out:{}
+block 0027
+ live in:{}
+ @????: goto . <- .
+ next 001e
+ live out:{}
+block 0028
+ pred 0000
+ live in:{}
+ Blort.java:51@000d: Rop{move-exception Ljava/lang/Throwable; <- . flows} v2:Ljava/lang/Throwable; <- .
+ @????: goto . <- .
+ next 001c
+ live out:{}
+block 0029
+ pred 0004
+ live in:{}
+ Blort.java:51@000d: Rop{move-exception Ljava/lang/Throwable; <- . flows} v2:Ljava/lang/Throwable; <- .
+ @????: goto . <- .
+ next 001c
+ live out:{}
+
+method edgeSplitSuccessor (I)I
+first 005a
+block 0058
+ pred 005a
+ live in:{}
+ Blort.java:62@0000: move-param-object(0) v0:LBlort; <- .
+ Blort.java:62@0000: move-param-int(1) v1:I <- .
+ Blort.java:62@0000: goto . <- .
+ next 0000
+ live out:{}
+block 0000
+ pred 0058
+ live in:{}
+ Blort.java:62@0000: const-int(0) v3:I=0 <- .
+ Blort.java:62@0001: move-int v2:I <- v3:I=0
+ Blort.java:64@0002: move-int v3:I <- v1:I
+ Blort.java:64@0003: switch({1, 2, 3}) . <- v3:I
+ next 001c
+ next 0022
+ next 0028
+ next 005b *
+ live out:{}
+block 001c
+ pred 0000
+ live in:{}
+ Blort.java:65@001c: add-const-int(1) v2:I <- v2:I
+ Blort.java:66@001f: goto . <- .
+ next 002b
+ live out:{}
+block 0022
+ pred 0000
+ live in:{}
+ Blort.java:67@0022: add-const-int(1) v2:I <- v2:I
+ Blort.java:68@0025: goto . <- .
+ next 002b
+ live out:{}
+block 0028
+ pred 0000
+ live in:{}
+ Blort.java:69@0028: add-const-int(1) v2:I <- v2:I
+ Blort.java:69@0028: goto . <- .
+ next 002b
+ live out:{}
+block 002b
+ pred 001c
+ pred 0022
+ pred 0028
+ pred 005b
+ live in:{}
+ Blort.java:72@002b: move-int v3:I <- v2:I
+ Blort.java:72@002c: move-int v0:I <- v3:I
+ Blort.java:72@002c: goto . <- .
+ next 0059
+ live out:{}
+block 0059
+ pred 002b
+ live in:{}
+ Blort.java:72@002c: return-int . <- v0:I
+ returns
+ live out:{}
+block 005a
+ live in:{}
+ @????: goto . <- .
+ next 0058
+ live out:{}
+block 005b
+ pred 0000
+ live in:{}
+ @????: goto . <- .
+ next 002b
+ live out:{}
diff --git a/dx/tests/086-ssa-edge-split/info.txt b/dx/tests/086-ssa-edge-split/info.txt
new file mode 100644
index 0000000..ff873e8
--- /dev/null
+++ b/dx/tests/086-ssa-edge-split/info.txt
@@ -0,0 +1,5 @@
+This is a test case for the edge-splitting algorthim used in the conversion to SSA form.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/086-ssa-edge-split/run b/dx/tests/086-ssa-edge-split/run
new file mode 100644
index 0000000..914deb1
--- /dev/null
+++ b/dx/tests/086-ssa-edge-split/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --dump --width=1000 --ssa-blocks --ssa-step=edge-split Blort.class
diff --git a/dx/tests/087-ssa-local-vars/Blort.java b/dx/tests/087-ssa-local-vars/Blort.java
new file mode 100644
index 0000000..7ce8a91
--- /dev/null
+++ b/dx/tests/087-ssa-local-vars/Blort.java
@@ -0,0 +1,94 @@
+import java.io.IOException;
+class Blort {
+ private static void arrayCopyTest(int k) {
+ // A local variable assigned from an argument
+ int j = k;
+ // These two locals are defined once and used multiple times
+ String[] stringArray = new String[8];
+ Object[] objectArray = new Object[8];
+ // Should cause another move to be inserted
+ Object anotherOne = objectArray;
+
+ if (anotherOne != null) {
+ System.out.println("foo");
+ }
+
+ // "i" is used in a loop
+ for (int i = 0; i < stringArray.length; i++)
+ stringArray[i] = new String(Integer.toString(i));
+
+ System.out.println("string -> object");
+ System.arraycopy(stringArray, 0, objectArray, 0, stringArray.length);
+ System.out.println("object -> string");
+ System.arraycopy(objectArray, 0, stringArray, 0, stringArray.length);
+ System.out.println("object -> string (modified)");
+ objectArray[4] = new Object();
+ try {
+ System.arraycopy(objectArray, 0, stringArray, 0,stringArray.length);
+ } catch (ArrayStoreException ase) {
+ // "ase" is an unused local which still must be preserved
+ System.out.println("caught ArrayStoreException (expected)");
+ }
+ }
+
+ private void testConstructor() {
+ Blort foo = null;
+ try {
+ foo = new Blort();
+ } catch (Exception ex) {
+ }
+ System.err.println(foo);
+ }
+ /**
+ * Stolen from
+ * java/android/org/apache/http/impl/io/AbstractMessageParser.java
+ * Simplified.
+ *
+ * Checks to see that local variable assignment is preserved through
+ * phi's. The key component here is the assignment of previous = current.
+ */
+ public static void parseHeaderGroup(
+ final Object headGroup,
+ final Object inbuffer,
+ int maxHeaderCount,
+ int maxLineLen)
+ throws IOException {
+
+ StringBuilder current = null;
+ StringBuilder previous = null;
+ for (;;) {
+ if (current == null) {
+ current = new StringBuilder(64);
+ } else {
+ current.length();
+ }
+ int l = inbuffer.hashCode();
+ if (l == -1 || current.length() < 1) {
+ break;
+ }
+
+ if ((current.charAt(0) == ' ' || current.charAt(0) == '\t') && previous != null) {
+ int i = 0;
+ while (i < current.length()) {
+ char ch = current.charAt(i);
+ if (ch != ' ' && ch != '\t') {
+ break;
+ }
+ i++;
+ }
+ if (maxLineLen > 0
+ && previous.length() + 1 + current.length() - i > maxLineLen) {
+ throw new IOException("Maximum line length limit exceeded");
+ }
+ previous.append(' ');
+ previous.append(current, i, current.length() - i);
+ } else {
+ previous = current;
+ current = null;
+ }
+ if (maxHeaderCount > 0) {
+ throw new IOException("Maximum header count exceeded");
+ }
+ }
+ }
+}
diff --git a/dx/tests/087-ssa-local-vars/expected.txt b/dx/tests/087-ssa-local-vars/expected.txt
new file mode 100644
index 0000000..6c0a6f7
--- /dev/null
+++ b/dx/tests/087-ssa-local-vars/expected.txt
@@ -0,0 +1,1266 @@
+reading Blort.class...
+method <init> ()V
+first 000c
+block 000a
+ pred 000c
+ live in:{}
+ Blort.java:2@0000: move-param-object(0) v2:"this"NffffLBlort; <- .
+ Blort.java:2@0000: goto . <- .
+ next 0000
+ live out:{2}
+block 0000
+ pred 000a
+ live in:{2}
+ Blort.java:2@0001: Rop{invoke-direct . <- Ljava/lang/Object; call throws <any
+ >}(java.lang.Object.<init>:()V catch) . <- v2:NffffLBlort;
+ next 0004
+ live out:{}
+block 0004
+ pred 0000
+ live in:{}
+ Blort.java:2@0004: goto . <- .
+ next 000b
+ live out:{}
+block 000b
+ pred 0004
+ live in:{}
+ Blort.java:2@0004: return-void . <- .
+ next 000d
+ live out:{}
+block 000c
+ live in:{}
+ @????: goto . <- .
+ next 000a
+ live out:{}
+block 000d
+ pred 000b
+ live in:{}
+ returns
+ live out:{}
+
+method arrayCopyTest (I)V
+first 012c
+block 0112
+ pred 012c
+ live in:{62, 63}
+ Blort.java:5@0000: move-param-int(0) v12:"k"I <- .
+ Blort.java:5@0000: goto . <- .
+ next 0000
+ live out:{12, 62, 63}
+block 0119
+ pred 0000
+ live in:{62, 63}
+ Blort.java:7@0004: Rop{move-result-pseudo [Ljava/lang/String; <- . flows} v15
+ :[Ljava/lang/String; <- .
+ Blort.java:7@0004: goto . <- .
+ next 0007
+ live out:{15, 62, 63}
+block 0000
+ pred 0112
+ live in:{12, 62, 63}
+ Blort.java:5@0001: move-int v13:"j"I <- v12:I
+ Blort.java:7@0004: new-array-object(java.lang.String[] catch) . <- v63:I=8
+ next 0119
+ live out:{62, 63}
+block 011a
+ pred 0007
+ live in:{15, 62}
+ Blort.java:8@000a: Rop{move-result-pseudo [Ljava/lang/Object; <- . flows} v17
+ :[Ljava/lang/Object; <- .
+ Blort.java:8@000a: goto . <- .
+ next 000d
+ live out:{15, 17, 62}
+block 0007
+ pred 0119
+ live in:{15, 62, 63}
+ @????: mark-local-object . <- v15:"stringArray"[Ljava/lang/String;
+ Blort.java:8@000a: new-array-object(java.lang.Object[] catch) . <- v63:I=8
+ next 011a
+ live out:{15, 62}
+block 000d
+ pred 011a
+ live in:{15, 17, 62}
+ @????: mark-local-object . <- v17:"objectArray"[Ljava/lang/Object;
+ Blort.java:10@000f: move-object v18:"anotherOne"[Ljava/lang/Object; <- v17:[L
+ java/lang/Object;
+ Blort.java:12@0013: if-eqz-object . <- v18:[Ljava/lang/Object;
+ next 0016 *
+ next 0131
+ live out:{15, 17, 62}
+block 011b
+ pred 0016
+ live in:{15, 17, 62}
+ Blort.java:13@0016: Rop{move-result-pseudo Ljava/io/PrintStream; <- . flows}
+ v19:Ljava/io/PrintStream; <- .
+ Blort.java:13@0016: goto . <- .
+ next 0019
+ live out:{15, 17, 19, 62}
+block 0016
+ pred 000d
+ live in:{15, 17, 62}
+ Blort.java:13@0016: get-static-object(java.lang.System.out:Ljava/io/PrintStre
+ am; catch) . <- .
+ next 011b
+ live out:{15, 17, 62}
+block 011c
+ pred 0019
+ live in:{15, 17, 19, 62}
+ Blort.java:13@0019: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v20
+ :Ljava/lang/String;="foo" <- .
+ Blort.java:13@0019: goto . <- .
+ next 001b
+ live out:{15, 17, 19, 20, 62}
+block 0019
+ pred 011b
+ live in:{15, 17, 19, 62}
+ Blort.java:13@0019: const-object("foo" catch) . <- .
+ next 011c
+ live out:{15, 17, 19, 62}
+block 001b
+ pred 011c
+ live in:{15, 17, 19, 20, 62}
+ Blort.java:13@001b: Rop{invoke-virtual . <- Ljava/io/PrintStream; Ljava/lang/
+ String; call throws <any>}(java.io.PrintStream.println:(Ljava/lang/String;)V
+ catch) . <- v19:Ljava/io/PrintStream; v20:Ljava/lang/String;="foo"
+ next 0130
+ live out:{15, 17, 62}
+block 001e
+ pred 0130
+ pred 0131
+ live in:{15, 17, 62}
+ Blort.java:17@001e: const-int(0) v23:I=0 <- .
+ @????: mark-local-int . <- v23:"i"I
+ Blort.java:17@001f: goto . <- .
+ next 0021
+ live out:{15, 17, 23, 62}
+block 011d
+ pred 0021
+ live in:{15, 17, 30, 62}
+ Blort.java:17@0024: Rop{move-result-pseudo I <- . flows} v31:I <- .
+ Blort.java:17@0024: goto . <- .
+ next 0025
+ live out:{15, 17, 30, 31, 62}
+block 0021
+ pred 001e
+ pred 0038
+ live in:{15, 17, 62}
+ @????: phi v30:"i"I <- v23:"i"I[b=001e] v34:"i"I[b=0038]
+ Blort.java:17@0024: array-length(catch) . <- v15:[Ljava/lang/String;
+ next 011d
+ live out:{15, 17, 30, 62}
+block 0025
+ pred 011d
+ live in:{15, 17, 30, 31, 62}
+ Blort.java:17@0025: if-ge-int . <- v30:I v31:I
+ next 0028 *
+ next 003e
+ live out:{15, 17, 30, 62}
+block 011e
+ pred 0028
+ live in:{15, 17, 30, 62}
+ Blort.java:18@002b: Rop{move-result-pseudo N002bLjava/lang/String; <- . flows
+ } v32:N002bLjava/lang/String; <- .
+ Blort.java:18@002b: goto . <- .
+ next 002e
+ live out:{15, 17, 30, 32, 62}
+block 0028
+ pred 0025
+ live in:{15, 17, 30, 62}
+ Blort.java:18@002b: new-instance(java.lang.String catch) . <- .
+ next 011e
+ live out:{15, 17, 30, 62}
+block 011f
+ pred 002e
+ live in:{15, 17, 30, 32, 62}
+ Blort.java:18@0031: Rop{move-result Ljava/lang/String; <- . flows} v33:Ljava/
+ lang/String; <- .
+ Blort.java:18@0031: goto . <- .
+ next 0034
+ live out:{15, 17, 30, 32, 33, 62}
+block 002e
+ pred 011e
+ live in:{15, 17, 30, 32, 62}
+ Blort.java:18@0031: Rop{invoke-static . <- I call throws <any>}(java.lang.Int
+ eger.toString:(I)Ljava/lang/String; catch) . <- v30:I
+ next 011f
+ live out:{15, 17, 30, 32, 62}
+block 0034
+ pred 011f
+ live in:{15, 17, 30, 32, 33, 62}
+ Blort.java:18@0034: Rop{invoke-direct . <- Ljava/lang/String; Ljava/lang/Stri
+ ng; call throws <any>}(java.lang.String.<init>:(Ljava/lang/String;)V catch) .
+ <- v32:N002bLjava/lang/String; v33:Ljava/lang/String;
+ next 0037
+ live out:{15, 17, 30, 32, 62}
+block 0037
+ pred 0034
+ live in:{15, 17, 30, 32, 62}
+ Blort.java:18@0037: aput-object(catch) . <- v32:Ljava/lang/String; v15:[Ljava
+ /lang/String; v30:I
+ next 0038
+ live out:{15, 17, 30, 62}
+block 0038
+ pred 0037
+ live in:{15, 17, 30, 62}
+ Blort.java:17@0038: add-const-int(1) v34:"i"I <- v30:I
+ Blort.java:17@003b: goto . <- .
+ next 0021
+ live out:{15, 17, 34, 62}
+block 0120
+ pred 003e
+ live in:{15, 17, 62}
+ Blort.java:20@003e: Rop{move-result-pseudo Ljava/io/PrintStream; <- . flows}
+ v35:Ljava/io/PrintStream; <- .
+ Blort.java:20@003e: goto . <- .
+ next 0041
+ live out:{15, 17, 35, 62}
+block 003e
+ pred 0025
+ live in:{15, 17, 62}
+ Blort.java:20@003e: get-static-object(java.lang.System.out:Ljava/io/PrintStre
+ am; catch) . <- .
+ next 0120
+ live out:{15, 17, 62}
+block 0121
+ pred 0041
+ live in:{15, 17, 35, 62}
+ Blort.java:20@0041: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v36
+ :Ljava/lang/String;="string -> object" <- .
+ Blort.java:20@0041: goto . <- .
+ next 0043
+ live out:{15, 17, 35, 36, 62}
+block 0041
+ pred 0120
+ live in:{15, 17, 35, 62}
+ Blort.java:20@0041: const-object("string -> object" catch) . <- .
+ next 0121
+ live out:{15, 17, 35, 62}
+block 0043
+ pred 0121
+ live in:{15, 17, 35, 36, 62}
+ Blort.java:20@0043: Rop{invoke-virtual . <- Ljava/io/PrintStream; Ljava/lang/
+ String; call throws <any>}(java.io.PrintStream.println:(Ljava/lang/String;)V
+ catch) . <- v35:Ljava/io/PrintStream; v36:Ljava/lang/String;="string -> objec
+ t"
+ next 0046
+ live out:{15, 17, 62}
+block 0122
+ pred 0046
+ live in:{15, 17, 62}
+ Blort.java:21@004b: Rop{move-result-pseudo I <- . flows} v39:I <- .
+ Blort.java:21@004b: goto . <- .
+ next 004c
+ live out:{15, 17, 39, 62}
+block 0046
+ pred 0043
+ live in:{15, 17, 62}
+ Blort.java:21@004b: array-length(catch) . <- v15:[Ljava/lang/String;
+ next 0122
+ live out:{15, 17, 62}
+block 004c
+ pred 0122
+ live in:{15, 17, 39, 62}
+ Blort.java:21@004c: Rop{invoke-static . <- Ljava/lang/Object; I Ljava/lang/Ob
+ ject; I I call throws <any>}(java.lang.System.arraycopy:(Ljava/lang/Object;IL
+ java/lang/Object;II)V catch) . <- v15:[Ljava/lang/String; v62:I=0 v17:[Ljava/
+ lang/Object; v62:I=0 v39:I
+ next 004f
+ live out:{15, 17, 62}
+block 0123
+ pred 004f
+ live in:{15, 17, 62}
+ Blort.java:22@004f: Rop{move-result-pseudo Ljava/io/PrintStream; <- . flows}
+ v40:Ljava/io/PrintStream; <- .
+ Blort.java:22@004f: goto . <- .
+ next 0052
+ live out:{15, 17, 40, 62}
+block 004f
+ pred 004c
+ live in:{15, 17, 62}
+ Blort.java:22@004f: get-static-object(java.lang.System.out:Ljava/io/PrintStre
+ am; catch) . <- .
+ next 0123
+ live out:{15, 17, 62}
+block 0124
+ pred 0052
+ live in:{15, 17, 40, 62}
+ Blort.java:22@0052: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v41
+ :Ljava/lang/String;="object -> string" <- .
+ Blort.java:22@0052: goto . <- .
+ next 0054
+ live out:{15, 17, 40, 41, 62}
+block 0052
+ pred 0123
+ live in:{15, 17, 40, 62}
+ Blort.java:22@0052: const-object("object -> string" catch) . <- .
+ next 0124
+ live out:{15, 17, 40, 62}
+block 0054
+ pred 0124
+ live in:{15, 17, 40, 41, 62}
+ Blort.java:22@0054: Rop{invoke-virtual . <- Ljava/io/PrintStream; Ljava/lang/
+ String; call throws <any>}(java.io.PrintStream.println:(Ljava/lang/String;)V
+ catch) . <- v40:Ljava/io/PrintStream; v41:Ljava/lang/String;="object -> strin
+ g"
+ next 0057
+ live out:{15, 17, 62}
+block 0125
+ pred 0057
+ live in:{15, 17, 62}
+ Blort.java:23@005c: Rop{move-result-pseudo I <- . flows} v44:I <- .
+ Blort.java:23@005c: goto . <- .
+ next 005d
+ live out:{15, 17, 44, 62}
+block 0057
+ pred 0054
+ live in:{15, 17, 62}
+ Blort.java:23@005c: array-length(catch) . <- v15:[Ljava/lang/String;
+ next 0125
+ live out:{15, 17, 62}
+block 005d
+ pred 0125
+ live in:{15, 17, 44, 62}
+ Blort.java:23@005d: Rop{invoke-static . <- Ljava/lang/Object; I Ljava/lang/Ob
+ ject; I I call throws <any>}(java.lang.System.arraycopy:(Ljava/lang/Object;IL
+ java/lang/Object;II)V catch) . <- v17:[Ljava/lang/Object; v62:I=0 v15:[Ljava/
+ lang/String; v62:I=0 v44:I
+ next 0060
+ live out:{15, 17}
+block 0126
+ pred 0060
+ live in:{15, 17}
+ Blort.java:24@0060: Rop{move-result-pseudo Ljava/io/PrintStream; <- . flows}
+ v45:Ljava/io/PrintStream; <- .
+ Blort.java:24@0060: goto . <- .
+ next 0063
+ live out:{15, 17, 45}
+block 0060
+ pred 005d
+ live in:{15, 17}
+ Blort.java:24@0060: get-static-object(java.lang.System.out:Ljava/io/PrintStre
+ am; catch) . <- .
+ next 0126
+ live out:{15, 17}
+block 0127
+ pred 0063
+ live in:{15, 17, 45}
+ Blort.java:24@0063: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v46
+ :Ljava/lang/String;="object -> string (modified)" <- .
+ Blort.java:24@0063: goto . <- .
+ next 0065
+ live out:{15, 17, 45, 46}
+block 0063
+ pred 0126
+ live in:{15, 17, 45}
+ Blort.java:24@0063: const-object("object -> string (modified)" catch) . <- .
+ next 0127
+ live out:{15, 17, 45}
+block 0065
+ pred 0127
+ live in:{15, 17, 45, 46}
+ Blort.java:24@0065: Rop{invoke-virtual . <- Ljava/io/PrintStream; Ljava/lang/
+ String; call throws <any>}(java.io.PrintStream.println:(Ljava/lang/String;)V
+ catch) . <- v45:Ljava/io/PrintStream; v46:Ljava/lang/String;="object -> strin
+ g (modified)"
+ next 0068
+ live out:{15, 17}
+block 0128
+ pred 0068
+ live in:{15, 17, 47}
+ Blort.java:25@006a: Rop{move-result-pseudo N006aLjava/lang/Object; <- . flows
+ } v48:N006aLjava/lang/Object; <- .
+ Blort.java:25@006a: goto . <- .
+ next 006d
+ live out:{15, 17, 47, 48}
+block 0068
+ pred 0065
+ live in:{15, 17}
+ Blort.java:25@0069: const-int(4) v47:I=4 <- .
+ Blort.java:25@006a: new-instance(java.lang.Object catch) . <- .
+ next 0128
+ live out:{15, 17, 47}
+block 006d
+ pred 0128
+ live in:{15, 17, 47, 48}
+ Blort.java:25@006e: Rop{invoke-direct . <- Ljava/lang/Object; call throws <an
+ y>}(java.lang.Object.<init>:()V catch) . <- v48:N006aLjava/lang/Object;
+ next 0071
+ live out:{15, 17, 47, 48}
+block 0071
+ pred 006d
+ live in:{15, 17, 47, 48}
+ Blort.java:25@0071: aput-object(catch) . <- v48:Ljava/lang/Object; v17:[Ljava
+ /lang/Object; v47:I=4
+ next 0072
+ live out:{15, 17}
+block 0129
+ pred 0072
+ live in:{15, 17, 49, 50}
+ Blort.java:27@0077: Rop{move-result-pseudo I <- . flows} v51:I <- .
+ Blort.java:27@0077: goto . <- .
+ next 0078
+ live out:{15, 17, 49, 50, 51}
+block 0072
+ pred 0071
+ live in:{15, 17}
+ Blort.java:27@0073: const-int(0) v49:I=0 <- .
+ Blort.java:27@0075: const-int(0) v50:I=0 <- .
+ Blort.java:27@0077: array-length(catch java.lang.ArrayStoreException) . <- v1
+ 5:[Ljava/lang/String;
+ next 012d
+ next 0129 *
+ live out:{15, 17, 49, 50}
+block 0078
+ pred 0129
+ live in:{15, 17, 49, 50, 51}
+ Blort.java:27@0078: Rop{invoke-static . <- Ljava/lang/Object; I Ljava/lang/Ob
+ ject; I I call throws <any>}(java.lang.System.arraycopy:(Ljava/lang/Object;IL
+ java/lang/Object;II)V catch java.lang.ArrayStoreException) . <- v17:[Ljava/la
+ ng/Object; v49:I=0 v15:[Ljava/lang/String; v50:I=0 v51:I
+ next 012e
+ next 007b *
+ live out:{}
+block 007b
+ pred 0078
+ live in:{}
+ Blort.java:31@007b: goto . <- .
+ next 0088
+ live out:{}
+block 012a
+ pred 007e
+ live in:{}
+ Blort.java:30@0080: Rop{move-result-pseudo Ljava/io/PrintStream; <- . flows}
+ v59:Ljava/io/PrintStream; <- .
+ Blort.java:30@0080: goto . <- .
+ next 0083
+ live out:{59}
+block 007e
+ pred 0107
+ live in:{58}
+ @????: mark-local-object . <- v58:"ase"Ljava/lang/ArrayStoreException;
+ Blort.java:30@0080: get-static-object(java.lang.System.out:Ljava/io/PrintStre
+ am; catch) . <- .
+ next 012a
+ live out:{}
+block 012b
+ pred 0083
+ live in:{59}
+ Blort.java:30@0083: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v60
+ :Ljava/lang/String;="caught ArrayStoreException (expected)" <- .
+ Blort.java:30@0083: goto . <- .
+ next 0085
+ live out:{59, 60}
+block 0083
+ pred 012a
+ live in:{59}
+ Blort.java:30@0083: const-object("caught ArrayStoreException (expected)" catc
+ h) . <- .
+ next 012b
+ live out:{59}
+block 0085
+ pred 012b
+ live in:{59, 60}
+ Blort.java:30@0085: Rop{invoke-virtual . <- Ljava/io/PrintStream; Ljava/lang/
+ String; call throws <any>}(java.io.PrintStream.println:(Ljava/lang/String;)V
+ catch) . <- v59:Ljava/io/PrintStream; v60:Ljava/lang/String;="caught ArraySto
+ reException (expected)"
+ next 012f
+ live out:{}
+block 0088
+ pred 007b
+ pred 012f
+ live in:{}
+ Blort.java:32@0088: goto . <- .
+ next 0113
+ live out:{}
+block 0113
+ pred 0088
+ live in:{}
+ Blort.java:32@0088: return-void . <- .
+ next 0132
+ live out:{}
+block 0107
+ pred 012d
+ pred 012e
+ live in:{}
+ @????: phi v58:Ljava/lang/ArrayStoreException; <- v52:Ljava/lang/ArrayStoreEx
+ ception;[b=012e] v61:Ljava/lang/ArrayStoreException;[b=012d]
+ Blort.java:28@007e: goto . <- .
+ next 007e
+ live out:{58}
+block 012c
+ live in:{}
+ @????: const-int(8) v63:I=8 <- .
+ @????: const-int(0) v62:I=0 <- .
+ @????: goto . <- .
+ next 0112
+ live out:{62, 63}
+block 012d
+ pred 0072
+ live in:{}
+ Blort.java:28@007e: Rop{move-exception Ljava/lang/ArrayStoreException; <- . f
+ lows} v61:Ljava/lang/ArrayStoreException; <- .
+ @????: goto . <- .
+ next 0107
+ live out:{61}
+block 012e
+ pred 0078
+ live in:{}
+ Blort.java:28@007e: Rop{move-exception Ljava/lang/ArrayStoreException; <- . f
+ lows} v52:Ljava/lang/ArrayStoreException; <- .
+ @????: goto . <- .
+ next 0107
+ live out:{52}
+block 012f
+ pred 0085
+ live in:{}
+ @????: goto . <- .
+ next 0088
+ live out:{}
+block 0130
+ pred 001b
+ live in:{15, 17, 62}
+ @????: goto . <- .
+ next 001e
+ live out:{15, 17, 62}
+block 0131
+ pred 000d
+ live in:{15, 17, 62}
+ @????: goto . <- .
+ next 001e
+ live out:{15, 17, 62}
+block 0132
+ pred 0113
+ live in:{}
+ returns
+ live out:{}
+
+method testConstructor ()V
+first 0035
+block 002c
+ pred 0035
+ live in:{}
+ Blort.java:35@0000: move-param-object(0) v6:"this"LBlort; <- .
+ Blort.java:35@0000: goto . <- .
+ next 0000
+ live out:{}
+block 0000
+ pred 002c
+ live in:{}
+ Blort.java:35@0000: const-object-nothrow(null) v7:<null>=null <- .
+ @????: mark-local-object . <- v7:"foo"LBlort;
+ Blort.java:35@0001: goto . <- .
+ next 0002
+ live out:{7}
+block 0033
+ pred 0002
+ live in:{7}
+ Blort.java:37@0002: Rop{move-result-pseudo N0002LBlort; <- . flows} v8:N0002L
+ Blort; <- .
+ Blort.java:37@0002: goto . <- .
+ next 0005
+ live out:{7, 8}
+block 0002
+ pred 0000
+ live in:{7}
+ Blort.java:37@0002: new-instance(Blort catch java.lang.Exception) . <- .
+ next 0036
+ next 0033 *
+ live out:{7}
+block 0005
+ pred 0033
+ live in:{7, 8}
+ Blort.java:37@0006: Rop{invoke-direct . <- LBlort; call throws <any>}(Blort.<
+ init>:()V catch java.lang.Exception) . <- v8:N0002LBlort;
+ next 0037
+ next 0009 *
+ live out:{7, 8}
+block 0009
+ pred 0005
+ live in:{8}
+ @????: mark-local-object . <- v8:"foo"LBlort;
+ Blort.java:37@0009: goto . <- .
+ next 000a
+ live out:{8}
+block 000a
+ pred 0009
+ live in:{8}
+ Blort.java:39@000a: goto . <- .
+ next 000e
+ live out:{8}
+block 000d
+ pred 0023
+ live in:{7}
+ Blort.java:38@000d: goto . <- .
+ next 000e
+ live out:{7}
+block 0034
+ pred 000e
+ live in:{14}
+ Blort.java:40@000e: Rop{move-result-pseudo Ljava/io/PrintStream; <- . flows}
+ v15:Ljava/io/PrintStream; <- .
+ Blort.java:40@000e: goto . <- .
+ next 0011
+ live out:{14, 15}
+block 000e
+ pred 000a
+ pred 000d
+ live in:{}
+ @????: phi v14:"foo"LBlort; <- v8:"foo"LBlort;[b=000a] v7:"foo"LBlort;[b=000d
+ ]
+ Blort.java:40@000e: get-static-object(java.lang.System.err:Ljava/io/PrintStre
+ am; catch) . <- .
+ next 0034
+ live out:{14}
+block 0011
+ pred 0034
+ live in:{14, 15}
+ Blort.java:40@0012: Rop{invoke-virtual . <- Ljava/io/PrintStream; Ljava/lang/
+ Object; call throws <any>}(java.io.PrintStream.println:(Ljava/lang/Object;)V
+ catch) . <- v15:Ljava/io/PrintStream; v14:LBlort;
+ next 0015
+ live out:{}
+block 0015
+ pred 0011
+ live in:{}
+ Blort.java:41@0015: goto . <- .
+ next 002d
+ live out:{}
+block 002d
+ pred 0015
+ live in:{}
+ Blort.java:41@0015: return-void . <- .
+ next 0038
+ live out:{}
+block 0023
+ pred 0036
+ pred 0037
+ live in:{7}
+ Blort.java:38@000d: goto . <- .
+ next 000d
+ live out:{7}
+block 0035
+ live in:{}
+ @????: goto . <- .
+ next 002c
+ live out:{}
+block 0036
+ pred 0002
+ live in:{7}
+ Blort.java:38@000d: Rop{move-exception Ljava/lang/Exception; <- . flows} v19:
+ Ljava/lang/Exception; <- .
+ @????: goto . <- .
+ next 0023
+ live out:{7}
+block 0037
+ pred 0005
+ live in:{7}
+ Blort.java:38@000d: Rop{move-exception Ljava/lang/Exception; <- . flows} v9:L
+ java/lang/Exception; <- .
+ @????: goto . <- .
+ next 0023
+ live out:{7}
+block 0038
+ pred 002d
+ live in:{}
+ returns
+ live out:{}
+
+method parseHeaderGroup (Ljava/lang/Object;Ljava/lang/Object;II)V
+first 01c6
+block 01ae
+ pred 01c6
+ live in:{99, 100, 101}
+ Blort.java:57@0000: move-param-object(0) v15:"headGroup"Ljava/lang/Object; <-
+ .
+ Blort.java:57@0000: move-param-object(1) v16:"inbuffer"Ljava/lang/Object; <-
+ .
+ Blort.java:57@0000: move-param-int(2) v17:"maxHeaderCount"I <- .
+ Blort.java:57@0000: move-param-int(3) v18:"maxLineLen"I <- .
+ Blort.java:57@0000: goto . <- .
+ next 0000
+ live out:{16, 17, 18, 99, 100, 101}
+block 0000
+ pred 01ae
+ live in:{16, 17, 18, 99, 100, 101}
+ Blort.java:57@0000: const-object-nothrow(null) v19:<null>=null <- .
+ @????: mark-local-object . <- v19:"current"Ljava/lang/StringBuilder;
+ Blort.java:58@0003: const-object-nothrow(null) v20:<null>=null <- .
+ @????: mark-local-object . <- v20:"previous"Ljava/lang/StringBuilder;
+ Blort.java:58@0004: goto . <- .
+ next 01ca
+ live out:{16, 17, 18, 19, 20, 99, 100, 101}
+block 0006
+ pred 01ca
+ live in:{16, 17, 18, 30, 31, 99, 100, 101}
+ Blort.java:60@0008: if-nez-object . <- v31:Ljava/lang/StringBuilder;
+ next 000b *
+ next 0019
+ live out:{16, 17, 18, 30, 31, 99, 100, 101}
+block 01b5
+ pred 000b
+ live in:{16, 17, 18, 30, 99, 100, 101}
+ Blort.java:61@000b: Rop{move-result-pseudo N000bLjava/lang/StringBuilder; <-
+ . flows} v32:N000bLjava/lang/StringBuilder; <- .
+ Blort.java:61@000b: goto . <- .
+ next 000e
+ live out:{16, 17, 18, 30, 32, 99, 100, 101}
+block 000b
+ pred 0006
+ live in:{16, 17, 18, 30, 99, 100, 101}
+ Blort.java:61@000b: new-instance(java.lang.StringBuilder catch) . <- .
+ next 01b5
+ live out:{16, 17, 18, 30, 99, 100, 101}
+block 000e
+ pred 01b5
+ live in:{16, 17, 18, 30, 32, 99, 100, 101}
+ Blort.java:61@000f: const-int(64) v33:I=64 <- .
+ Blort.java:61@0011: Rop{invoke-direct . <- Ljava/lang/StringBuilder; I call t
+ hrows <any>}(java.lang.StringBuilder.<init>:(I)V catch) . <- v32:N000bLjava/l
+ ang/StringBuilder; v33:I=64
+ next 0014
+ live out:{16, 17, 18, 30, 32, 99, 100, 101}
+block 0014
+ pred 000e
+ live in:{16, 17, 18, 30, 32, 99, 100, 101}
+ @????: mark-local-object . <- v32:"current"Ljava/lang/StringBuilder;
+ Blort.java:61@0016: goto . <- .
+ next 001f
+ live out:{16, 17, 18, 30, 32, 99, 100, 101}
+block 01b6
+ pred 0019
+ live in:{16, 17, 18, 30, 31, 99, 100, 101}
+ Blort.java:63@001b: goto . <- .
+ next 001e
+ live out:{16, 17, 18, 30, 31, 99, 100, 101}
+block 0019
+ pred 0006
+ live in:{16, 17, 18, 30, 31, 99, 100, 101}
+ Blort.java:63@001b: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; call th
+ rows <any>}(java.lang.StringBuilder.length:()I catch) . <- v31:Ljava/lang/Str
+ ingBuilder;
+ next 01b6
+ live out:{16, 17, 18, 30, 31, 99, 100, 101}
+block 001e
+ pred 01b6
+ live in:{16, 17, 18, 30, 31, 99, 100, 101}
+ @????: goto . <- .
+ next 001f
+ live out:{16, 17, 18, 30, 31, 99, 100, 101}
+block 01b7
+ pred 001f
+ live in:{16, 17, 18, 30, 39, 99, 100, 101}
+ Blort.java:65@0020: Rop{move-result I <- . flows} v40:I <- .
+ Blort.java:65@0020: goto . <- .
+ next 0023
+ live out:{16, 17, 18, 30, 39, 40, 99, 100, 101}
+block 001f
+ pred 0014
+ pred 001e
+ live in:{16, 17, 18, 30, 99, 100, 101}
+ @????: phi v39:"current"Ljava/lang/StringBuilder; <- v32:"current"Ljava/lang/
+ StringBuilder;[b=0014] v31:"current"Ljava/lang/StringBuilder;[b=001e]
+ Blort.java:65@0020: Rop{invoke-virtual . <- Ljava/lang/Object; call throws <a
+ ny>}(java.lang.Object.hashCode:()I catch) . <- v16:Ljava/lang/Object;
+ next 01b7
+ live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 0023
+ pred 01b7
+ live in:{16, 17, 18, 30, 39, 40, 99, 100, 101}
+ @????: mark-local-int . <- v40:"l"I
+ Blort.java:66@0027: const-int(-1) v41:I=-1 <- .
+ Blort.java:66@0028: if-eq-int . <- v40:I v41:I=-1
+ next 002b *
+ next 01d4
+ live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 01b8
+ pred 002b
+ live in:{16, 17, 18, 30, 39, 99, 100, 101}
+ Blort.java:66@002d: Rop{move-result I <- . flows} v42:I <- .
+ Blort.java:66@002d: goto . <- .
+ next 0030
+ live out:{16, 17, 18, 30, 39, 42, 99, 100, 101}
+block 002b
+ pred 0023
+ live in:{16, 17, 18, 30, 39, 99, 100, 101}
+ Blort.java:66@002d: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; call th
+ rows <any>}(java.lang.StringBuilder.length:()I catch) . <- v39:Ljava/lang/Str
+ ingBuilder;
+ next 01b8
+ live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 0030
+ pred 01b8
+ live in:{16, 17, 18, 30, 39, 42, 99, 100, 101}
+ Blort.java:66@0030: const-int(1) v43:I=1 <- .
+ Blort.java:66@0031: if-ge-int . <- v42:I v43:I=1
+ next 0034 *
+ next 0037
+ live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 0034
+ pred 0030
+ live in:{}
+ Blort.java:67@0034: goto . <- .
+ next 00d6
+ live out:{}
+block 01b9
+ pred 0037
+ live in:{16, 17, 18, 30, 39, 99, 100, 101}
+ Blort.java:70@003a: Rop{move-result C <- . flows} v45:C <- .
+ Blort.java:70@003a: goto . <- .
+ next 003d
+ live out:{16, 17, 18, 30, 39, 45, 99, 100, 101}
+block 0037
+ pred 0030
+ live in:{16, 17, 18, 30, 39, 99, 100, 101}
+ Blort.java:70@003a: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; I call
+ throws <any>}(java.lang.StringBuilder.charAt:(I)C catch) . <- v39:Ljava/lang/
+ StringBuilder; v100:I=0
+ next 01b9
+ live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 003d
+ pred 01b9
+ live in:{16, 17, 18, 30, 39, 45, 99, 100, 101}
+ Blort.java:70@003f: if-eq-int . <- v45:I v99:I=32
+ next 0042 *
+ next 01d3
+ live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 01ba
+ pred 0042
+ live in:{16, 17, 18, 30, 39, 99, 100, 101}
+ Blort.java:70@0045: Rop{move-result C <- . flows} v48:C <- .
+ Blort.java:70@0045: goto . <- .
+ next 0048
+ live out:{16, 17, 18, 30, 39, 48, 99, 100, 101}
+block 0042
+ pred 003d
+ live in:{16, 17, 18, 30, 39, 99, 100, 101}
+ Blort.java:70@0045: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; I call
+ throws <any>}(java.lang.StringBuilder.charAt:(I)C catch) . <- v39:Ljava/lang/
+ StringBuilder; v100:I=0
+ next 01ba
+ live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 0048
+ pred 01ba
+ live in:{16, 17, 18, 30, 39, 48, 99, 100, 101}
+ Blort.java:70@004a: if-ne-int . <- v48:I v101:I=9
+ next 01d2 *
+ next 01d1
+ live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 004d
+ pred 01c9
+ live in:{16, 17, 18, 30, 39, 99, 100, 101}
+ Blort.java:70@004f: if-eqz-object . <- v30:Ljava/lang/StringBuilder;
+ next 0052 *
+ next 01d0
+ live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 0052
+ pred 004d
+ live in:{16, 17, 18, 30, 39, 99, 100, 101}
+ Blort.java:71@0052: const-int(0) v67:I=0 <- .
+ @????: mark-local-int . <- v67:"i"I
+ Blort.java:71@0053: goto . <- .
+ next 0055
+ live out:{16, 17, 18, 30, 39, 67, 99, 100, 101}
+block 01bb
+ pred 0055
+ live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+ Blort.java:72@0059: Rop{move-result I <- . flows} v72:I <- .
+ Blort.java:72@0059: goto . <- .
+ next 005c
+ live out:{16, 17, 18, 30, 39, 71, 72, 99, 100, 101}
+block 0055
+ pred 0052
+ pred 0079
+ live in:{16, 17, 18, 30, 39, 99, 100, 101}
+ @????: phi v71:"i"I <- v67:"i"I[b=0052] v78:"i"I[b=0079]
+ Blort.java:72@0059: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; call th
+ rows <any>}(java.lang.StringBuilder.length:()I catch) . <- v39:Ljava/lang/Str
+ ingBuilder;
+ next 01bb
+ live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 005c
+ pred 01bb
+ live in:{16, 17, 18, 30, 39, 71, 72, 99, 100, 101}
+ Blort.java:72@005c: if-ge-int . <- v71:I v72:I
+ next 005f *
+ next 01cf
+ live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 01bc
+ pred 005f
+ live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+ Blort.java:73@0063: Rop{move-result C <- . flows} v73:C <- .
+ Blort.java:73@0063: goto . <- .
+ next 0066
+ live out:{16, 17, 18, 30, 39, 71, 73, 99, 100, 101}
+block 005f
+ pred 005c
+ live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+ Blort.java:73@0063: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; I call
+ throws <any>}(java.lang.StringBuilder.charAt:(I)C catch) . <- v39:Ljava/lang/
+ StringBuilder; v71:I
+ next 01bc
+ live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 0066
+ pred 01bc
+ live in:{16, 17, 18, 30, 39, 71, 73, 99, 100, 101}
+ @????: mark-local-int . <- v73:"ch"C
+ Blort.java:74@006c: if-eq-int . <- v73:I v99:I=32
+ next 006f *
+ next 01ce
+ live out:{16, 17, 18, 30, 39, 71, 73, 99, 100, 101}
+block 006f
+ pred 0066
+ live in:{16, 17, 18, 30, 39, 71, 73, 99, 100, 101}
+ Blort.java:74@0073: if-eq-int . <- v73:I v101:I=9
+ next 0076 *
+ next 01cd
+ live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 0076
+ pred 006f
+ live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+ Blort.java:75@0076: goto . <- .
+ next 01c8
+ live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 0079
+ pred 01cd
+ pred 01ce
+ live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+ Blort.java:77@0079: add-const-int(1) v78:"i"I <- v71:I
+ Blort.java:78@007c: goto . <- .
+ next 0055
+ live out:{16, 17, 18, 30, 39, 78, 99, 100, 101}
+block 007f
+ pred 01c8
+ live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+ Blort.java:79@0080: if-lez-int . <- v18:I
+ next 0083 *
+ next 01cc
+ live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 01bd
+ pred 0083
+ live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+ Blort.java:79@0085: Rop{move-result I <- . flows} v82:I <- .
+ Blort.java:79@0085: goto . <- .
+ next 0088
+ live out:{16, 17, 18, 30, 39, 71, 82, 99, 100, 101}
+block 0083
+ pred 007f
+ live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+ Blort.java:79@0085: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; call th
+ rows <any>}(java.lang.StringBuilder.length:()I catch) . <- v30:Ljava/lang/Str
+ ingBuilder;
+ next 01bd
+ live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 01be
+ pred 0088
+ live in:{16, 17, 18, 30, 39, 71, 84, 99, 100, 101}
+ Blort.java:79@008c: Rop{move-result I <- . flows} v85:I <- .
+ Blort.java:79@008c: goto . <- .
+ next 008f
+ live out:{16, 17, 18, 30, 39, 71, 84, 85, 99, 100, 101}
+block 0088
+ pred 01bd
+ live in:{16, 17, 18, 30, 39, 71, 82, 99, 100, 101}
+ Blort.java:79@0089: add-const-int(1) v84:I <- v82:I
+ Blort.java:79@008c: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; call th
+ rows <any>}(java.lang.StringBuilder.length:()I catch) . <- v39:Ljava/lang/Str
+ ingBuilder;
+ next 01be
+ live out:{16, 17, 18, 30, 39, 71, 84, 99, 100, 101}
+block 008f
+ pred 01be
+ live in:{16, 17, 18, 30, 39, 71, 84, 85, 99, 100, 101}
+ Blort.java:79@008f: add-int v86:I <- v84:I v85:I
+ Blort.java:79@0092: sub-int v87:I <- v86:I v71:I
+ Blort.java:79@0094: if-le-int . <- v87:I v18:I
+ next 0097 *
+ next 01cb
+ live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 01bf
+ pred 0097
+ live in:{}
+ Blort.java:81@0097: Rop{move-result-pseudo N0097Ljava/io/IOException; <- . fl
+ ows} v88:N0097Ljava/io/IOException; <- .
+ Blort.java:81@0097: goto . <- .
+ next 009a
+ live out:{88}
+block 0097
+ pred 008f
+ live in:{}
+ Blort.java:81@0097: new-instance(java.io.IOException catch) . <- .
+ next 01bf
+ live out:{}
+block 01c0
+ pred 009a
+ live in:{88}
+ Blort.java:81@009b: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v89
+ :Ljava/lang/String;="Maximum line length limit exceeded" <- .
+ Blort.java:81@009b: goto . <- .
+ next 009d
+ live out:{88, 89}
+block 009a
+ pred 01bf
+ live in:{88}
+ Blort.java:81@009b: const-object("Maximum line length limit exceeded" catch)
+ . <- .
+ next 01c0
+ live out:{88}
+block 009d
+ pred 01c0
+ live in:{88, 89}
+ Blort.java:81@009d: Rop{invoke-direct . <- Ljava/io/IOException; Ljava/lang/S
+ tring; call throws <any>}(java.io.IOException.<init>:(Ljava/lang/String;)V ca
+ tch) . <- v88:N0097Ljava/io/IOException; v89:Ljava/lang/String;="Maximum line
+ length limit exceeded"
+ next 00a0
+ live out:{88}
+block 00a0
+ pred 009d
+ live in:{88}
+ Blort.java:81@00a0: throw(catch) . <- v88:Ljava/io/IOException;
+ next 01d5
+ live out:{}
+block 01c1
+ pred 00a1
+ live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+ Blort.java:83@00a5: goto . <- .
+ next 00a8
+ live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 00a1
+ pred 01cb
+ pred 01cc
+ live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+ Blort.java:83@00a5: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; I call
+ throws <any>}(java.lang.StringBuilder.append:(C)Ljava/lang/StringBuilder; cat
+ ch) . <- v30:Ljava/lang/StringBuilder; v99:I=32
+ next 01c1
+ live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 01c2
+ pred 00a8
+ live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+ Blort.java:84@00b1: Rop{move-result I <- . flows} v94:I <- .
+ Blort.java:84@00b1: goto . <- .
+ next 00b4
+ live out:{16, 17, 18, 30, 39, 71, 94, 99, 100, 101}
+block 00a8
+ pred 01c1
+ live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+ Blort.java:84@00b1: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; call th
+ rows <any>}(java.lang.StringBuilder.length:()I catch) . <- v39:Ljava/lang/Str
+ ingBuilder;
+ next 01c2
+ live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 01c3
+ pred 00b4
+ live in:{16, 17, 18, 30, 39, 99, 100, 101}
+ Blort.java:84@00b7: goto . <- .
+ next 00ba
+ live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 00b4
+ pred 01c2
+ live in:{16, 17, 18, 30, 39, 71, 94, 99, 100, 101}
+ Blort.java:84@00b6: sub-int v95:I <- v94:I v71:I
+ Blort.java:84@00b7: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+ ang/CharSequence; I I call throws <any>}(java.lang.StringBuilder.append:(Ljav
+ a/lang/CharSequence;II)Ljava/lang/StringBuilder; catch) . <- v30:Ljava/lang/S
+ tringBuilder; v39:Ljava/lang/StringBuilder; v71:I v95:I
+ next 01c3
+ live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 00ba
+ pred 01c3
+ live in:{16, 17, 18, 30, 39, 99, 100, 101}
+ Blort.java:85@00bb: goto . <- .
+ next 01c7
+ live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 00be
+ pred 01d0
+ pred 01d1
+ live in:{16, 17, 18, 39, 99, 100, 101}
+ Blort.java:86@00c0: move-object v52:"previous"Ljava/lang/StringBuilder; <- v3
+ 9:Ljava/lang/StringBuilder;
+ Blort.java:87@00c2: const-object-nothrow(null) v53:<null>=null <- .
+ @????: mark-local-object . <- v53:"current"Ljava/lang/StringBuilder;
+ Blort.java:87@00c3: goto . <- .
+ next 01c7
+ live out:{16, 17, 18, 52, 53, 99, 100, 101}
+block 00c5
+ pred 01c7
+ live in:{16, 17, 18, 61, 62, 99, 100, 101}
+ Blort.java:89@00c6: if-lez-int . <- v17:I
+ next 00c9 *
+ next 00d3
+ live out:{16, 17, 18, 61, 62, 99, 100, 101}
+block 01c4
+ pred 00c9
+ live in:{}
+ Blort.java:90@00c9: Rop{move-result-pseudo N00c9Ljava/io/IOException; <- . fl
+ ows} v63:N00c9Ljava/io/IOException; <- .
+ Blort.java:90@00c9: goto . <- .
+ next 00cc
+ live out:{63}
+block 00c9
+ pred 00c5
+ live in:{}
+ Blort.java:90@00c9: new-instance(java.io.IOException catch) . <- .
+ next 01c4
+ live out:{}
+block 01c5
+ pred 00cc
+ live in:{63}
+ Blort.java:90@00cd: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v64
+ :Ljava/lang/String;="Maximum header count exceeded" <- .
+ Blort.java:90@00cd: goto . <- .
+ next 00cf
+ live out:{63, 64}
+block 00cc
+ pred 01c4
+ live in:{63}
+ Blort.java:90@00cd: const-object("Maximum header count exceeded" catch) . <-
+ .
+ next 01c5
+ live out:{63}
+block 00cf
+ pred 01c5
+ live in:{63, 64}
+ Blort.java:90@00cf: Rop{invoke-direct . <- Ljava/io/IOException; Ljava/lang/S
+ tring; call throws <any>}(java.io.IOException.<init>:(Ljava/lang/String;)V ca
+ tch) . <- v63:N00c9Ljava/io/IOException; v64:Ljava/lang/String;="Maximum head
+ er count exceeded"
+ next 00d2
+ live out:{63}
+block 00d2
+ pred 00cf
+ live in:{63}
+ Blort.java:90@00d2: throw(catch) . <- v63:Ljava/io/IOException;
+ next 01d5
+ live out:{}
+block 00d3
+ pred 00c5
+ live in:{16, 17, 18, 61, 62, 99, 100, 101}
+ Blort.java:92@00d3: goto . <- .
+ next 01ca
+ live out:{16, 17, 18, 61, 62, 99, 100, 101}
+block 00d6
+ pred 0034
+ pred 01d4
+ live in:{}
+ Blort.java:93@00d6: goto . <- .
+ next 01af
+ live out:{}
+block 01af
+ pred 00d6
+ live in:{}
+ Blort.java:93@00d6: return-void . <- .
+ next 01d5
+ live out:{}
+block 01c6
+ live in:{}
+ @????: const-int(9) v101:I=9 <- .
+ @????: const-int(0) v100:I=0 <- .
+ @????: const-int(32) v99:I=32 <- .
+ @????: goto . <- .
+ next 01ae
+ live out:{99, 100, 101}
+block 01c7
+ pred 00ba
+ pred 00be
+ live in:{16, 17, 18, 99, 100, 101}
+ @????: phi v61:"previous"Ljava/lang/StringBuilder; <- v52:"previous"Ljava/lan
+ g/StringBuilder;[b=00be] v30:"previous"Ljava/lang/StringBuilder;[b=00ba]
+ @????: phi v62:"current"Ljava/lang/StringBuilder; <- v53:"current"Ljava/lang/
+ StringBuilder;[b=00be] v39:"current"Ljava/lang/StringBuilder;[b=00ba]
+ @????: goto . <- .
+ next 00c5
+ live out:{16, 17, 18, 61, 62, 99, 100, 101}
+block 01c8
+ pred 0076
+ pred 01cf
+ live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+ @????: goto . <- .
+ next 007f
+ live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 01c9
+ pred 01d2
+ pred 01d3
+ live in:{16, 17, 18, 30, 39, 99, 100, 101}
+ @????: goto . <- .
+ next 004d
+ live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 01ca
+ pred 0000
+ pred 00d3
+ live in:{16, 17, 18, 99, 100, 101}
+ @????: phi v30:"previous"Ljava/lang/StringBuilder; <- v20:"previous"Ljava/lan
+ g/StringBuilder;[b=0000] v61:"previous"Ljava/lang/StringBuilder;[b=00d3]
+ @????: phi v31:"current"Ljava/lang/StringBuilder; <- v19:"current"Ljava/lang/
+ StringBuilder;[b=0000] v62:"current"Ljava/lang/StringBuilder;[b=00d3]
+ @????: goto . <- .
+ next 0006
+ live out:{16, 17, 18, 30, 31, 99, 100, 101}
+block 01cb
+ pred 008f
+ live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+ @????: goto . <- .
+ next 00a1
+ live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 01cc
+ pred 007f
+ live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+ @????: goto . <- .
+ next 00a1
+ live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 01cd
+ pred 006f
+ live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+ @????: goto . <- .
+ next 0079
+ live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 01ce
+ pred 0066
+ live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+ @????: goto . <- .
+ next 0079
+ live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 01cf
+ pred 005c
+ live in:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+ @????: goto . <- .
+ next 01c8
+ live out:{16, 17, 18, 30, 39, 71, 99, 100, 101}
+block 01d0
+ pred 004d
+ live in:{16, 17, 18, 39, 99, 100, 101}
+ @????: goto . <- .
+ next 00be
+ live out:{16, 17, 18, 39, 99, 100, 101}
+block 01d1
+ pred 0048
+ live in:{16, 17, 18, 39, 99, 100, 101}
+ @????: goto . <- .
+ next 00be
+ live out:{16, 17, 18, 39, 99, 100, 101}
+block 01d2
+ pred 0048
+ live in:{16, 17, 18, 30, 39, 99, 100, 101}
+ @????: goto . <- .
+ next 01c9
+ live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 01d3
+ pred 003d
+ live in:{16, 17, 18, 30, 39, 99, 100, 101}
+ @????: goto . <- .
+ next 01c9
+ live out:{16, 17, 18, 30, 39, 99, 100, 101}
+block 01d4
+ pred 0023
+ live in:{}
+ @????: goto . <- .
+ next 00d6
+ live out:{}
+block 01d5
+ pred 00a0
+ pred 00d2
+ pred 01af
+ live in:{}
+ returns
+ live out:{}
diff --git a/dx/tests/087-ssa-local-vars/info.txt b/dx/tests/087-ssa-local-vars/info.txt
new file mode 100644
index 0000000..6e9d675
--- /dev/null
+++ b/dx/tests/087-ssa-local-vars/info.txt
@@ -0,0 +1,5 @@
+This is a test case to ensure proper preservation of local variable information through the register renamer and dead code remover at the beginning of the SSA conversion.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/087-ssa-local-vars/run b/dx/tests/087-ssa-local-vars/run
new file mode 100644
index 0000000..ae97c15
--- /dev/null
+++ b/dx/tests/087-ssa-local-vars/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -g -d . Blort.java
+dx --dump --ssa-blocks Blort.class
diff --git a/dx/tests/088-ssa-combine-blocks/Blort.java b/dx/tests/088-ssa-combine-blocks/Blort.java
new file mode 100644
index 0000000..bf49dbe
--- /dev/null
+++ b/dx/tests/088-ssa-combine-blocks/Blort.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ /**
+ * just because this should do nothing
+ */
+ void voidFunction() {
+ }
+
+ /**
+ * Current SSA form requires each move-exception block to have
+ * a unique predecessor
+ */
+ void edgeSplitMoveException() {
+ try {
+ hashCode();
+ hashCode();
+ } catch (Throwable tr) {
+ }
+ }
+
+ /**
+ * An empty infinite loop for the empty goto optimizer
+ */
+ void infiniteLoop() {
+ while (true) {
+ }
+ }
+}
diff --git a/dx/tests/088-ssa-combine-blocks/expected.txt b/dx/tests/088-ssa-combine-blocks/expected.txt
new file mode 100644
index 0000000..7bf445d
--- /dev/null
+++ b/dx/tests/088-ssa-combine-blocks/expected.txt
@@ -0,0 +1,82 @@
+reading Blort.class...
+method <init> ()V
+first 000c
+block 000a
+ pred 000c
+ Blort.java:17@0000: move-param-object(0) v0:NffffLBlort; <- .
+ Blort.java:17@0000: goto . <- .
+ next 0000
+block 0000
+ pred 000a
+ Blort.java:17@0001: Rop{invoke-direct . <- Ljava/lang/Object; call throws <any>}(java.lang.Object.<init>:()V catch) . <- v0:NffffLBlort;
+ next 000b
+block 000b
+ pred 0000
+ Blort.java:17@0004: return-void . <- .
+ returns
+block 000c
+ @????: goto . <- .
+ next 000a
+
+method voidFunction ()V
+first 0004
+block 0002
+ pred 0004
+ Blort.java:23@0000: move-param-object(0) v0:LBlort; <- .
+ Blort.java:23@0000: goto . <- .
+ next 0003
+block 0003
+ pred 0002
+ Blort.java:23@0000: return-void . <- .
+ returns
+block 0004
+ @????: goto . <- .
+ next 0002
+
+method edgeSplitMoveException ()V
+first 0027
+block 001e
+ pred 0027
+ Blort.java:31@0000: move-param-object(0) v1:LBlort; <- .
+ Blort.java:31@0000: goto . <- .
+ next 0000
+block 0000
+ pred 001e
+ Blort.java:31@0001: Rop{invoke-virtual . <- Ljava/lang/Object; call throws <any>}(java.lang.Object.hashCode:()I catch java.lang.Throwable) . <- v1:LBlort;
+ next 0028
+ next 0004 *
+block 0004
+ pred 0000
+ Blort.java:32@0006: Rop{invoke-virtual . <- Ljava/lang/Object; call throws <any>}(java.lang.Object.hashCode:()I catch java.lang.Throwable) . <- v1:LBlort;
+ next 0028
+ next 001f *
+block 001f
+ pred 0004
+ pred 0028
+ Blort.java:35@000e: return-void . <- .
+ returns
+block 0027
+ @????: goto . <- .
+ next 001e
+block 0028
+ pred 0000
+ pred 0004
+ Blort.java:33@000d: Rop{move-exception Ljava/lang/Throwable; <- . flows} v0:Ljava/lang/Throwable; <- .
+ @????: goto . <- .
+ next 001f
+
+method infiniteLoop ()V
+first 0003
+block 0002
+ pred 0003
+ Blort.java:41@0000: move-param-object(0) v0:LBlort; <- .
+ Blort.java:41@0000: goto . <- .
+ next 0000
+block 0000
+ pred 0000
+ pred 0002
+ Blort.java:41@0000: goto . <- .
+ next 0000
+block 0003
+ @????: goto . <- .
+ next 0002
diff --git a/dx/tests/088-ssa-combine-blocks/info.txt b/dx/tests/088-ssa-combine-blocks/info.txt
new file mode 100644
index 0000000..603c1c0
--- /dev/null
+++ b/dx/tests/088-ssa-combine-blocks/info.txt
@@ -0,0 +1,5 @@
+This is a test case for the identical-block combining algorithm, which runs after the SSA optimizer to recombine identical blocks (usually exception handlers) created during edge-splitting.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/088-ssa-combine-blocks/run b/dx/tests/088-ssa-combine-blocks/run
new file mode 100644
index 0000000..29fe559
--- /dev/null
+++ b/dx/tests/088-ssa-combine-blocks/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java
+dx --dump --width=1000 --optimize --rop-blocks Blort.class
diff --git a/dx/tests/089-dex-define-object/Class.java b/dx/tests/089-dex-define-object/Class.java
new file mode 100644
index 0000000..4de5e70
--- /dev/null
+++ b/dx/tests/089-dex-define-object/Class.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.lang;
+
+public class Class<T> {
+ // This space intentionally left blank.
+}
diff --git a/dx/tests/089-dex-define-object/Object.java b/dx/tests/089-dex-define-object/Object.java
new file mode 100644
index 0000000..575c736
--- /dev/null
+++ b/dx/tests/089-dex-define-object/Object.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.lang;
+
+public class Object {
+ public Object() {
+ // This space intentionally left blank.
+ }
+
+ public boolean equals(Object o) {
+ return true;
+ }
+
+ protected void finalize() {
+ // This space intentionally left blank.
+ }
+
+ public final native Class<? extends Object> getClass();
+ public native int hashCode();
+ public final native void notify();
+ public final native void notifyAll();
+
+ public String toString() {
+ return "blort";
+ }
+
+ public final void wait() {
+ wait(0, 0);
+ }
+
+ public final void wait(long time) {
+ wait(time, 0);
+ }
+
+ public final native void wait(long time, int frac);
+}
diff --git a/dx/tests/089-dex-define-object/String.java b/dx/tests/089-dex-define-object/String.java
new file mode 100644
index 0000000..26e7370
--- /dev/null
+++ b/dx/tests/089-dex-define-object/String.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.lang;
+
+public final class String {
+ // This space intentionally left blank.
+}
diff --git a/dx/tests/089-dex-define-object/expected.txt b/dx/tests/089-dex-define-object/expected.txt
new file mode 100644
index 0000000..a1db6a4
--- /dev/null
+++ b/dx/tests/089-dex-define-object/expected.txt
@@ -0,0 +1 @@
+Good!
diff --git a/dx/tests/089-dex-define-object/info.txt b/dx/tests/089-dex-define-object/info.txt
new file mode 100644
index 0000000..e035834
--- /dev/null
+++ b/dx/tests/089-dex-define-object/info.txt
@@ -0,0 +1,4 @@
+This tests that a stripped down definition of the class Object can in
+fact be converted to a dex file. This test ensures that the conversion
+runs without failure, though the contents of the converted file are
+not checked for correctness.
diff --git a/dx/tests/089-dex-define-object/run b/dx/tests/089-dex-define-object/run
new file mode 100644
index 0000000..e53e147
--- /dev/null
+++ b/dx/tests/089-dex-define-object/run
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . *.java
+dx --debug --dex --core-library --output=blort.dex */*/*.class
+if [ -r blort.dex ]; then
+ echo Good!
+fi
diff --git a/dx/tests/090-dex-unify-arrays/Blort.java b/dx/tests/090-dex-unify-arrays/Blort.java
new file mode 100644
index 0000000..507153e
--- /dev/null
+++ b/dx/tests/090-dex-unify-arrays/Blort.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ /*
+ * Note: The use of the casts after the "?" in the following are
+ * to avoid a bug in some (source code level) compilers.
+ */
+
+ static public Object test1(boolean b) {
+ return (b ? new String[1] : new Integer[1])[0];
+ }
+
+ static public int test2(boolean b) {
+ Object o = b ? (Object) new int[1] : new float[1];
+ return o.hashCode();
+ }
+
+ static public int test3(boolean b) {
+ Object o = b ? (Object) new char[1] : new double[1];
+ return o.hashCode();
+ }
+
+ static public int test4(boolean b) {
+ Object o = b ? (Object) new long[1] : new boolean[1];
+ return o.hashCode();
+ }
+
+ static public int test5(boolean b) {
+ Object o = b ? (Object) new short[1] : new Object[1];
+ return o.hashCode();
+ }
+
+ static public int test6(boolean b) {
+ Object o = b ? (Object) new byte[1] : new boolean[1];
+ return o.hashCode();
+ }
+
+ static public Object test7(boolean b) {
+ return (b ? new String[1] : new int[1][])[0];
+ }
+
+ static public Object[] test8(boolean b) {
+ return (b ? new String[1][] : new int[1][][])[0];
+ }
+}
diff --git a/dx/tests/090-dex-unify-arrays/expected.txt b/dx/tests/090-dex-unify-arrays/expected.txt
new file mode 100644
index 0000000..7a4125f
--- /dev/null
+++ b/dx/tests/090-dex-unify-arrays/expected.txt
@@ -0,0 +1,122 @@
+Blort.test1:(Z)Ljava/lang/Object;:
+regs: 0004; ins: 0001; outs: 0000
+ 0000: move v0, v3
+ 0001: move v1, v0
+ 0002: if-eqz v1, 000c // +000a
+ 0004: const/4 v1, #int 1 // #1
+ 0005: new-array v1, v1, java.lang.String[]
+ 0007: const/4 v2, #int 0 // #0
+ 0008: aget-object v1, v1, v2
+ 000a: move-object v0, v1
+ 000b: return-object v0
+ 000c: const/4 v1, #int 1 // #1
+ 000d: new-array v1, v1, java.lang.Integer[]
+ 000f: goto 0007 // -0008
+Blort.test2:(Z)I:
+regs: 0004; ins: 0001; outs: 0001
+ 0000: move v0, v3
+ 0001: move v2, v0
+ 0002: if-eqz v2, 000f // +000d
+ 0004: const/4 v2, #int 1 // #1
+ 0005: new-array v2, v2, int[]
+ 0007: move-object v1, v2
+ 0008: move-object v2, v1
+ 0009: invoke-virtual {v2}, java.lang.Object.hashCode:()I
+ 000c: move-result v2
+ 000d: move v0, v2
+ 000e: return v0
+ 000f: const/4 v2, #int 1 // #1
+ 0010: new-array v2, v2, float[]
+ 0012: goto 0007 // -000b
+Blort.test3:(Z)I:
+regs: 0004; ins: 0001; outs: 0001
+ 0000: move v0, v3
+ 0001: move v2, v0
+ 0002: if-eqz v2, 000f // +000d
+ 0004: const/4 v2, #int 1 // #1
+ 0005: new-array v2, v2, char[]
+ 0007: move-object v1, v2
+ 0008: move-object v2, v1
+ 0009: invoke-virtual {v2}, java.lang.Object.hashCode:()I
+ 000c: move-result v2
+ 000d: move v0, v2
+ 000e: return v0
+ 000f: const/4 v2, #int 1 // #1
+ 0010: new-array v2, v2, double[]
+ 0012: goto 0007 // -000b
+Blort.test4:(Z)I:
+regs: 0004; ins: 0001; outs: 0001
+ 0000: move v0, v3
+ 0001: move v2, v0
+ 0002: if-eqz v2, 000f // +000d
+ 0004: const/4 v2, #int 1 // #1
+ 0005: new-array v2, v2, long[]
+ 0007: move-object v1, v2
+ 0008: move-object v2, v1
+ 0009: invoke-virtual {v2}, java.lang.Object.hashCode:()I
+ 000c: move-result v2
+ 000d: move v0, v2
+ 000e: return v0
+ 000f: const/4 v2, #int 1 // #1
+ 0010: new-array v2, v2, boolean[]
+ 0012: goto 0007 // -000b
+Blort.test5:(Z)I:
+regs: 0004; ins: 0001; outs: 0001
+ 0000: move v0, v3
+ 0001: move v2, v0
+ 0002: if-eqz v2, 000f // +000d
+ 0004: const/4 v2, #int 1 // #1
+ 0005: new-array v2, v2, short[]
+ 0007: move-object v1, v2
+ 0008: move-object v2, v1
+ 0009: invoke-virtual {v2}, java.lang.Object.hashCode:()I
+ 000c: move-result v2
+ 000d: move v0, v2
+ 000e: return v0
+ 000f: const/4 v2, #int 1 // #1
+ 0010: new-array v2, v2, java.lang.Object[]
+ 0012: goto 0007 // -000b
+Blort.test6:(Z)I:
+regs: 0004; ins: 0001; outs: 0001
+ 0000: move v0, v3
+ 0001: move v2, v0
+ 0002: if-eqz v2, 000f // +000d
+ 0004: const/4 v2, #int 1 // #1
+ 0005: new-array v2, v2, byte[]
+ 0007: move-object v1, v2
+ 0008: move-object v2, v1
+ 0009: invoke-virtual {v2}, java.lang.Object.hashCode:()I
+ 000c: move-result v2
+ 000d: move v0, v2
+ 000e: return v0
+ 000f: const/4 v2, #int 1 // #1
+ 0010: new-array v2, v2, boolean[]
+ 0012: goto 0007 // -000b
+Blort.test7:(Z)Ljava/lang/Object;:
+regs: 0004; ins: 0001; outs: 0000
+ 0000: move v0, v3
+ 0001: move v1, v0
+ 0002: if-eqz v1, 000c // +000a
+ 0004: const/4 v1, #int 1 // #1
+ 0005: new-array v1, v1, java.lang.String[]
+ 0007: const/4 v2, #int 0 // #0
+ 0008: aget-object v1, v1, v2
+ 000a: move-object v0, v1
+ 000b: return-object v0
+ 000c: const/4 v1, #int 1 // #1
+ 000d: new-array v1, v1, int[][]
+ 000f: goto 0007 // -0008
+Blort.test8:(Z)[Ljava/lang/Object;:
+regs: 0004; ins: 0001; outs: 0000
+ 0000: move v0, v3
+ 0001: move v1, v0
+ 0002: if-eqz v1, 000c // +000a
+ 0004: const/4 v1, #int 1 // #1
+ 0005: new-array v1, v1, java.lang.String[][]
+ 0007: const/4 v2, #int 0 // #0
+ 0008: aget-object v1, v1, v2
+ 000a: move-object v0, v1
+ 000b: return-object v0
+ 000c: const/4 v1, #int 1 // #1
+ 000d: new-array v1, v1, int[][][]
+ 000f: goto 0007 // -0008
diff --git a/dx/tests/090-dex-unify-arrays/info.txt b/dx/tests/090-dex-unify-arrays/info.txt
new file mode 100644
index 0000000..018fd25
--- /dev/null
+++ b/dx/tests/090-dex-unify-arrays/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+array type unification works properly.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/090-dex-unify-arrays/run b/dx/tests/090-dex-unify-arrays/run
new file mode 100644
index 0000000..47b709c
--- /dev/null
+++ b/dx/tests/090-dex-unify-arrays/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+ --dump-method=Blort.test'*' Blort.class
diff --git a/dx/tests/091-ssa-const-collector/Blort.java b/dx/tests/091-ssa-const-collector/Blort.java
new file mode 100644
index 0000000..ea99a0d
--- /dev/null
+++ b/dx/tests/091-ssa-const-collector/Blort.java
@@ -0,0 +1,64 @@
+
+class Blort {
+ /** Class constructors for enums use a lot of const's */
+ enum Foo {
+ ONE,TWO,THREE,FOUR,FIVE,SIX,SEVEN,EIGHT
+ }
+
+ /** all uses of 10 should be combined except the local assignment */
+ void testNumeric() {
+ int foo = 10;
+
+ for (int i = 0; i < 10; i++){
+ foo += i * 10;
+ }
+
+ for (int i = 0; i < 10; i++){
+ foo += i + 10;
+ }
+ }
+
+ void testStrings() {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append("foo");
+ sb.append("foo");
+ sb.append("foo");
+ sb.append("foo");
+ sb.append("foo");
+ sb.append("foo");
+ }
+
+ void testCaughtStrings() {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append("foo");
+ sb.append("foo");
+ sb.append("foo");
+ try {
+ sb.append("foo");
+ sb.append("foo");
+ sb.append("foo");
+ } catch (Throwable tr) {
+ System.out.println("foo");
+ }
+ }
+
+ /** local variables cannot be intermingled */
+ void testLocalVars() {
+ int i = 10;
+ int j = 10;
+ int k = 10;
+ int a = 10;
+ int b = 10;
+ int c = 10;
+
+ i *= 10;
+ }
+
+ void testNull(Object a) {
+ a.equals(null);
+ a.equals(null);
+
+ }
+}
diff --git a/dx/tests/091-ssa-const-collector/expected.txt b/dx/tests/091-ssa-const-collector/expected.txt
new file mode 100644
index 0000000..0edb6e8
--- /dev/null
+++ b/dx/tests/091-ssa-const-collector/expected.txt
@@ -0,0 +1,475 @@
+reading Blort.class...
+method <init> ()V
+first 000c
+block 000a
+ pred 000c
+ Blort.java:2@0000: move-param-object(0) v0:"this"NffffLBlort; <- .
+ Blort.java:2@0000: goto . <- .
+ next 0000
+block 0000
+ pred 000a
+ Blort.java:2@0001: Rop{invoke-direct . <- Ljava/lang/Object; call throws <any
+ >}(java.lang.Object.<init>:()V catch) . <- v0:NffffLBlort;
+ next 000b
+block 000b
+ pred 0000
+ Blort.java:4@0004: return-void . <- .
+ returns
+block 000c
+ @????: goto . <- .
+ next 000a
+
+method testNumeric ()V
+first 005e
+block 005c
+ pred 005e
+ Blort.java:10@0000: move-param-object(0) v4:"this"LBlort; <- .
+ Blort.java:10@0000: goto . <- .
+ next 0000
+block 0000
+ pred 005c
+ Blort.java:10@0000: const-int(10) v0:I=10 <- .
+ @????: mark-local-int . <- v0:"foo"I
+ Blort.java:12@0003: const-int(0) v1:I=0 <- .
+ @????: mark-local-int . <- v1:"i"I
+ Blort.java:12@0004: goto . <- .
+ next 0005
+block 0005
+ pred 0000
+ pred 000b
+ Blort.java:12@0008: if-ge-int . <- v1:I v3:I=10
+ next 000b *
+ next 0018
+block 000b
+ pred 0005
+ Blort.java:13@000f: mul-const-int(10) v2:I <- v1:I
+ Blort.java:13@0010: add-int v0:I <- v0:I v2:I
+ @????: mark-local-int . <- v0:"foo"I
+ Blort.java:12@0012: add-const-int(1) v1:"i"I <- v1:I
+ Blort.java:12@0015: goto . <- .
+ next 0005
+block 0018
+ pred 0005
+ Blort.java:16@0018: const-int(0) v1:I=0 <- .
+ @????: mark-local-int . <- v1:"i"I
+ Blort.java:16@0019: goto . <- .
+ next 001a
+block 001a
+ pred 0018
+ pred 0020
+ Blort.java:16@001d: if-ge-int . <- v1:I v3:I=10
+ next 0020 *
+ next 005d
+block 0020
+ pred 001a
+ Blort.java:17@0024: add-const-int(10) v2:I <- v1:I
+ Blort.java:17@0025: add-int v0:I <- v0:I v2:I
+ @????: mark-local-int . <- v0:"foo"I
+ Blort.java:16@0027: add-const-int(1) v1:"i"I <- v1:I
+ Blort.java:16@002a: goto . <- .
+ next 001a
+block 005d
+ pred 001a
+ Blort.java:19@002d: return-void . <- .
+ returns
+block 005e
+ @????: const-int(10) v3:I=10 <- .
+ @????: goto . <- .
+ next 005c
+
+method testStrings ()V
+first 0078
+block 0064
+ pred 007b
+ Blort.java:22@0000: move-param-object(0) v3:"this"LBlort; <- .
+ Blort.java:22@0000: goto . <- .
+ next 0000
+block 006b
+ pred 0000
+ Blort.java:22@0000: Rop{move-result-pseudo N0000Ljava/lang/StringBuilder; <-
+ . flows} v0:N0000Ljava/lang/StringBuilder; <- .
+ Blort.java:22@0000: goto . <- .
+ next 0003
+block 0000
+ pred 0064
+ Blort.java:22@0000: new-instance(java.lang.StringBuilder catch) . <- .
+ next 006b
+block 0003
+ pred 006b
+ Blort.java:22@0004: Rop{invoke-direct . <- Ljava/lang/StringBuilder; call thr
+ ows <any>}(java.lang.StringBuilder.<init>:()V catch) . <- v0:N0000Ljava/lang/
+ StringBuilder;
+ next 0007
+block 006c
+ pred 0007
+ Blort.java:24@0009: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v1:
+ Ljava/lang/String;="foo" <- .
+ Blort.java:24@0009: goto . <- .
+ next 000b
+block 0007
+ pred 0003
+ @????: mark-local-object . <- v0:"sb"Ljava/lang/StringBuilder;
+ Blort.java:24@0009: const-object("foo" catch) . <- .
+ next 006c
+block 000b
+ pred 006c
+ Blort.java:24@000b: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+ ang/String; call throws <any>}(java.lang.StringBuilder.append:(Ljava/lang/Str
+ ing;)Ljava/lang/StringBuilder; catch) . <- v0:Ljava/lang/StringBuilder; v2:Lj
+ ava/lang/String;="foo"
+ next 000e
+block 006e
+ pred 000e
+ Blort.java:25@0010: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v1:
+ Ljava/lang/String;="foo" <- .
+ Blort.java:25@0010: goto . <- .
+ next 0012
+block 000e
+ pred 000b
+ Blort.java:25@0010: const-object("foo" catch) . <- .
+ next 006e
+block 0012
+ pred 006e
+ Blort.java:25@0012: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+ ang/String; call throws <any>}(java.lang.StringBuilder.append:(Ljava/lang/Str
+ ing;)Ljava/lang/StringBuilder; catch) . <- v0:Ljava/lang/StringBuilder; v2:Lj
+ ava/lang/String;="foo"
+ next 0015
+block 0070
+ pred 0015
+ Blort.java:26@0017: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v1:
+ Ljava/lang/String;="foo" <- .
+ Blort.java:26@0017: goto . <- .
+ next 0019
+block 0015
+ pred 0012
+ Blort.java:26@0017: const-object("foo" catch) . <- .
+ next 0070
+block 0019
+ pred 0070
+ Blort.java:26@0019: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+ ang/String; call throws <any>}(java.lang.StringBuilder.append:(Ljava/lang/Str
+ ing;)Ljava/lang/StringBuilder; catch) . <- v0:Ljava/lang/StringBuilder; v2:Lj
+ ava/lang/String;="foo"
+ next 001c
+block 0072
+ pred 001c
+ Blort.java:27@001e: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v1:
+ Ljava/lang/String;="foo" <- .
+ Blort.java:27@001e: goto . <- .
+ next 0020
+block 001c
+ pred 0019
+ Blort.java:27@001e: const-object("foo" catch) . <- .
+ next 0072
+block 0020
+ pred 0072
+ Blort.java:27@0020: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+ ang/String; call throws <any>}(java.lang.StringBuilder.append:(Ljava/lang/Str
+ ing;)Ljava/lang/StringBuilder; catch) . <- v0:Ljava/lang/StringBuilder; v2:Lj
+ ava/lang/String;="foo"
+ next 0023
+block 0074
+ pred 0023
+ Blort.java:28@0025: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v1:
+ Ljava/lang/String;="foo" <- .
+ Blort.java:28@0025: goto . <- .
+ next 0027
+block 0023
+ pred 0020
+ Blort.java:28@0025: const-object("foo" catch) . <- .
+ next 0074
+block 0027
+ pred 0074
+ Blort.java:28@0027: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+ ang/String; call throws <any>}(java.lang.StringBuilder.append:(Ljava/lang/Str
+ ing;)Ljava/lang/StringBuilder; catch) . <- v0:Ljava/lang/StringBuilder; v2:Lj
+ ava/lang/String;="foo"
+ next 002a
+block 0076
+ pred 002a
+ Blort.java:29@002c: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v1:
+ Ljava/lang/String;="foo" <- .
+ Blort.java:29@002c: goto . <- .
+ next 002e
+block 002a
+ pred 0027
+ Blort.java:29@002c: const-object("foo" catch) . <- .
+ next 0076
+block 002e
+ pred 0076
+ Blort.java:29@002e: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+ ang/String; call throws <any>}(java.lang.StringBuilder.append:(Ljava/lang/Str
+ ing;)Ljava/lang/StringBuilder; catch) . <- v0:Ljava/lang/StringBuilder; v2:Lj
+ ava/lang/String;="foo"
+ next 0065
+block 0065
+ pred 002e
+ Blort.java:30@0032: return-void . <- .
+ returns
+block 0078
+ @????: goto . <- .
+ next 007a
+block 007a
+ pred 0078
+ @????: const-object("foo" catch) . <- .
+ next 007b
+block 007b
+ pred 007a
+ @????: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v2:Ljava/lang/St
+ ring;="foo" <- .
+ @????: goto . <- .
+ next 0064
+
+method testCaughtStrings ()V
+first 0094
+block 007e
+ pred 009e
+ Blort.java:33@0000: move-param-object(0) v5:"this"LBlort; <- .
+ Blort.java:33@0000: goto . <- .
+ next 0000
+block 0085
+ pred 0000
+ Blort.java:33@0000: Rop{move-result-pseudo N0000Ljava/lang/StringBuilder; <-
+ . flows} v0:N0000Ljava/lang/StringBuilder; <- .
+ Blort.java:33@0000: goto . <- .
+ next 0003
+block 0000
+ pred 007e
+ Blort.java:33@0000: new-instance(java.lang.StringBuilder catch) . <- .
+ next 0085
+block 0003
+ pred 0085
+ Blort.java:33@0004: Rop{invoke-direct . <- Ljava/lang/StringBuilder; call thr
+ ows <any>}(java.lang.StringBuilder.<init>:()V catch) . <- v0:N0000Ljava/lang/
+ StringBuilder;
+ next 0007
+block 0086
+ pred 0007
+ Blort.java:35@0009: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v2:
+ Ljava/lang/String;="foo" <- .
+ Blort.java:35@0009: goto . <- .
+ next 000b
+block 0007
+ pred 0003
+ @????: mark-local-object . <- v0:"sb"Ljava/lang/StringBuilder;
+ Blort.java:35@0009: const-object("foo" catch) . <- .
+ next 0086
+block 000b
+ pred 0086
+ Blort.java:35@000b: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+ ang/String; call throws <any>}(java.lang.StringBuilder.append:(Ljava/lang/Str
+ ing;)Ljava/lang/StringBuilder; catch) . <- v0:Ljava/lang/StringBuilder; v4:Lj
+ ava/lang/String;="foo"
+ next 000e
+block 0088
+ pred 000e
+ Blort.java:36@0010: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v2:
+ Ljava/lang/String;="foo" <- .
+ Blort.java:36@0010: goto . <- .
+ next 0012
+block 000e
+ pred 000b
+ Blort.java:36@0010: const-object("foo" catch) . <- .
+ next 0088
+block 0012
+ pred 0088
+ Blort.java:36@0012: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+ ang/String; call throws <any>}(java.lang.StringBuilder.append:(Ljava/lang/Str
+ ing;)Ljava/lang/StringBuilder; catch) . <- v0:Ljava/lang/StringBuilder; v4:Lj
+ ava/lang/String;="foo"
+ next 0015
+block 008a
+ pred 0015
+ Blort.java:37@0017: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v2:
+ Ljava/lang/String;="foo" <- .
+ Blort.java:37@0017: goto . <- .
+ next 0019
+block 0015
+ pred 0012
+ Blort.java:37@0017: const-object("foo" catch) . <- .
+ next 008a
+block 0019
+ pred 008a
+ Blort.java:37@0019: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+ ang/String; call throws <any>}(java.lang.StringBuilder.append:(Ljava/lang/Str
+ ing;)Ljava/lang/StringBuilder; catch) . <- v0:Ljava/lang/StringBuilder; v4:Lj
+ ava/lang/String;="foo"
+ next 001d
+block 008c
+ pred 001d
+ Blort.java:39@001e: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v2:
+ Ljava/lang/String;="foo" <- .
+ Blort.java:39@001e: goto . <- .
+ next 0020
+block 001d
+ pred 0019
+ Blort.java:39@001e: const-object("foo" catch java.lang.Throwable) . <- .
+ next 0095
+ next 008c *
+block 0020
+ pred 008c
+ Blort.java:39@0020: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+ ang/String; call throws <any>}(java.lang.StringBuilder.append:(Ljava/lang/Str
+ ing;)Ljava/lang/StringBuilder; catch java.lang.Throwable) . <- v0:Ljava/lang/
+ StringBuilder; v2:Ljava/lang/String;="foo"
+ next 0095
+ next 0023 *
+block 008e
+ pred 0023
+ Blort.java:40@0025: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v2:
+ Ljava/lang/String;="foo" <- .
+ Blort.java:40@0025: goto . <- .
+ next 0027
+block 0023
+ pred 0020
+ Blort.java:40@0025: const-object("foo" catch java.lang.Throwable) . <- .
+ next 0095
+ next 008e *
+block 0027
+ pred 008e
+ Blort.java:40@0027: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+ ang/String; call throws <any>}(java.lang.StringBuilder.append:(Ljava/lang/Str
+ ing;)Ljava/lang/StringBuilder; catch java.lang.Throwable) . <- v0:Ljava/lang/
+ StringBuilder; v2:Ljava/lang/String;="foo"
+ next 0095
+ next 002a *
+block 0090
+ pred 002a
+ Blort.java:41@002c: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v2:
+ Ljava/lang/String;="foo" <- .
+ Blort.java:41@002c: goto . <- .
+ next 002e
+block 002a
+ pred 0027
+ Blort.java:41@002c: const-object("foo" catch java.lang.Throwable) . <- .
+ next 0095
+ next 0090 *
+block 002e
+ pred 0090
+ Blort.java:41@002e: Rop{invoke-virtual . <- Ljava/lang/StringBuilder; Ljava/l
+ ang/String; call throws <any>}(java.lang.StringBuilder.append:(Ljava/lang/Str
+ ing;)Ljava/lang/StringBuilder; catch java.lang.Throwable) . <- v0:Ljava/lang/
+ StringBuilder; v2:Ljava/lang/String;="foo"
+ next 0095
+ next 007f *
+block 0092
+ pred 0035
+ Blort.java:43@0036: Rop{move-result-pseudo Ljava/io/PrintStream; <- . flows}
+ v2:Ljava/io/PrintStream; <- .
+ Blort.java:43@0036: goto . <- .
+ next 0039
+block 0035
+ pred 0095
+ @????: mark-local-object . <- v1:"tr"Ljava/lang/Throwable;
+ Blort.java:43@0036: get-static-object(java.lang.System.out:Ljava/io/PrintStre
+ am; catch) . <- .
+ next 0092
+block 0093
+ pred 0039
+ Blort.java:43@0039: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v3:
+ Ljava/lang/String;="foo" <- .
+ Blort.java:43@0039: goto . <- .
+ next 003b
+block 0039
+ pred 0092
+ Blort.java:43@0039: const-object("foo" catch) . <- .
+ next 0093
+block 003b
+ pred 0093
+ Blort.java:43@003b: Rop{invoke-virtual . <- Ljava/io/PrintStream; Ljava/lang/
+ String; call throws <any>}(java.io.PrintStream.println:(Ljava/lang/String;)V
+ catch) . <- v2:Ljava/io/PrintStream; v4:Ljava/lang/String;="foo"
+ next 007f
+block 007f
+ pred 002e
+ pred 003b
+ Blort.java:45@003e: return-void . <- .
+ returns
+block 0094
+ @????: goto . <- .
+ next 009d
+block 0095
+ pred 001d
+ pred 0020
+ pred 0023
+ pred 0027
+ pred 002a
+ pred 002e
+ Blort.java:42@0035: Rop{move-exception Ljava/lang/Throwable; <- . flows} v2:L
+ java/lang/Throwable; <- .
+ @????: move-object v1:Ljava/lang/Throwable; <- v2:Ljava/lang/Throwable;
+ @????: goto . <- .
+ next 0035
+block 009d
+ pred 0094
+ @????: const-object("foo" catch) . <- .
+ next 009e
+block 009e
+ pred 009d
+ @????: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v4:Ljava/lang/St
+ ring;="foo" <- .
+ @????: goto . <- .
+ next 007e
+
+method testLocalVars ()V
+first 0004
+block 0002
+ pred 0004
+ Blort.java:49@0000: move-param-object(0) v6:"this"LBlort; <- .
+ Blort.java:49@0000: goto . <- .
+ next 0000
+block 0000
+ pred 0002
+ Blort.java:49@0000: const-int(10) v3:I=10 <- .
+ @????: mark-local-int . <- v3:"i"I
+ Blort.java:50@0003: const-int(10) v4:I=10 <- .
+ @????: mark-local-int . <- v4:"j"I
+ Blort.java:51@0006: const-int(10) v5:I=10 <- .
+ @????: mark-local-int . <- v5:"k"I
+ Blort.java:52@0009: const-int(10) v0:I=10 <- .
+ @????: mark-local-int . <- v0:"a"I
+ Blort.java:53@000d: const-int(10) v1:I=10 <- .
+ @????: mark-local-int . <- v1:"b"I
+ Blort.java:54@0011: const-int(10) v2:I=10 <- .
+ @????: mark-local-int . <- v2:"c"I
+ Blort.java:56@0018: mul-const-int(10) v3:I <- v3:I
+ @????: mark-local-int . <- v3:"i"I=100
+ Blort.java:57@001a: goto . <- .
+ next 0003
+block 0003
+ pred 0000
+ Blort.java:57@001a: return-void . <- .
+ returns
+block 0004
+ @????: goto . <- .
+ next 0002
+
+method testNull (Ljava/lang/Object;)V
+first 0021
+block 0018
+ pred 0021
+ Blort.java:60@0000: move-param-object(0) v1:"this"LBlort; <- .
+ Blort.java:60@0000: move-param-object(1) v2:"a"Ljava/lang/Object; <- .
+ Blort.java:60@0000: goto . <- .
+ next 0000
+block 0000
+ pred 0018
+ Blort.java:60@0002: Rop{invoke-virtual . <- Ljava/lang/Object; Ljava/lang/Obj
+ ect; call throws <any>}(java.lang.Object.equals:(Ljava/lang/Object;)Z catch)
+ . <- v2:Ljava/lang/Object; v0:<null>=null
+ next 0005
+block 0005
+ pred 0000
+ Blort.java:61@0008: Rop{invoke-virtual . <- Ljava/lang/Object; Ljava/lang/Obj
+ ect; call throws <any>}(java.lang.Object.equals:(Ljava/lang/Object;)Z catch)
+ . <- v2:Ljava/lang/Object; v0:<null>=null
+ next 0019
+block 0019
+ pred 0005
+ Blort.java:63@000c: return-void . <- .
+ returns
+block 0021
+ @????: const-object-nothrow(null) v0:<null>=null <- .
+ @????: goto . <- .
+ next 0018
diff --git a/dx/tests/091-ssa-const-collector/info.txt b/dx/tests/091-ssa-const-collector/info.txt
new file mode 100644
index 0000000..020d3b3
--- /dev/null
+++ b/dx/tests/091-ssa-const-collector/info.txt
@@ -0,0 +1,5 @@
+This test case tests the "const collector" optimization step.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/091-ssa-const-collector/run b/dx/tests/091-ssa-const-collector/run
new file mode 100644
index 0000000..aa37784
--- /dev/null
+++ b/dx/tests/091-ssa-const-collector/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -g -d . Blort.java
+dx --dump --optimize --rop-blocks Blort.class
diff --git a/dx/tests/092-ssa-cfg-edge-cases/Blort.java b/dx/tests/092-ssa-cfg-edge-cases/Blort.java
new file mode 100644
index 0000000..8b3602f
--- /dev/null
+++ b/dx/tests/092-ssa-cfg-edge-cases/Blort.java
@@ -0,0 +1,20 @@
+
+class Blort {
+
+ void testMultipleIdenticalSuccessors(int foo) {
+ switch(foo) {
+ case 1:
+ case 2:
+ case 3:
+ System.out.println("foo");
+ break;
+ }
+ }
+
+ void testNoPrimarySuccessor() {
+ try {
+ throw new RuntimeException();
+ } catch (RuntimeException ex){
+ }
+ }
+}
diff --git a/dx/tests/092-ssa-cfg-edge-cases/expected.txt b/dx/tests/092-ssa-cfg-edge-cases/expected.txt
new file mode 100644
index 0000000..41fefc4
--- /dev/null
+++ b/dx/tests/092-ssa-cfg-edge-cases/expected.txt
@@ -0,0 +1,120 @@
+reading Blort.class...
+method <init> ()V
+first 000c
+block 000a
+ pred 000c
+ Blort.java:2@0000: move-param-object(0) v0:"this"NffffLBlort; <- .
+ Blort.java:2@0000: goto . <- .
+ next 0000
+block 0000
+ pred 000a
+ Blort.java:2@0001: Rop{invoke-direct . <- Ljava/lang/Object; call throws <any
+ >}(java.lang.Object.<init>:()V catch) . <- v0:NffffLBlort;
+ next 000b
+block 000b
+ pred 0000
+ Blort.java:2@0004: return-void . <- .
+ returns
+block 000c
+ @????: goto . <- .
+ next 000a
+
+method testMultipleIdenticalSuccessors (I)V
+first 0053
+block 004a
+ pred 0053
+ Blort.java:5@0000: move-param-object(0) v2:"this"LBlort; <- .
+ Blort.java:5@0000: move-param-int(1) v3:"foo"I <- .
+ Blort.java:5@0000: goto . <- .
+ next 0000
+block 0000
+ pred 004a
+ Blort.java:5@0001: switch({1, 2, 3}) . <- v3:I
+ next 001c
+ next 001c
+ next 001c
+ next 004b *
+block 0051
+ pred 001c
+ Blort.java:9@001c: Rop{move-result-pseudo Ljava/io/PrintStream; <- . flows} v
+ 0:Ljava/io/PrintStream; <- .
+ Blort.java:9@001c: goto . <- .
+ next 001f
+block 001c
+ pred 0000
+ pred 0000
+ pred 0000
+ Blort.java:9@001c: get-static-object(java.lang.System.out:Ljava/io/PrintStrea
+ m; catch) . <- .
+ next 0051
+block 0052
+ pred 001f
+ Blort.java:9@001f: Rop{move-result-pseudo Ljava/lang/String; <- . flows} v1:L
+ java/lang/String;="foo" <- .
+ Blort.java:9@001f: goto . <- .
+ next 0021
+block 001f
+ pred 0051
+ Blort.java:9@001f: const-object("foo" catch) . <- .
+ next 0052
+block 0021
+ pred 0052
+ Blort.java:9@0021: Rop{invoke-virtual . <- Ljava/io/PrintStream; Ljava/lang/S
+ tring; call throws <any>}(java.io.PrintStream.println:(Ljava/lang/String;)V c
+ atch) . <- v0:Ljava/io/PrintStream; v1:Ljava/lang/String;="foo"
+ next 004b
+block 004b
+ pred 0000
+ pred 0021
+ Blort.java:12@0024: return-void . <- .
+ returns
+block 0053
+ @????: goto . <- .
+ next 004a
+
+method testNoPrimarySuccessor ()V
+first 001a
+block 0012
+ pred 001a
+ Blort.java:16@0000: move-param-object(0) v1:"this"LBlort; <- .
+ Blort.java:16@0000: goto . <- .
+ next 0000
+block 0019
+ pred 0000
+ Blort.java:16@0000: Rop{move-result-pseudo N0000Ljava/lang/RuntimeException;
+ <- . flows} v0:N0000Ljava/lang/RuntimeException; <- .
+ Blort.java:16@0000: goto . <- .
+ next 0003
+block 0000
+ pred 0012
+ Blort.java:16@0000: new-instance(java.lang.RuntimeException catch java.lang.R
+ untimeException) . <- .
+ next 001b
+ next 0019 *
+block 0003
+ pred 0019
+ Blort.java:16@0004: Rop{invoke-direct . <- Ljava/lang/RuntimeException; call
+ throws <any>}(java.lang.RuntimeException.<init>:()V catch java.lang.RuntimeEx
+ ception) . <- v0:N0000Ljava/lang/RuntimeException;
+ next 001b
+ next 0007 *
+block 0007
+ pred 0003
+ Blort.java:16@0007: throw(catch java.lang.RuntimeException) . <- v0:Ljava/lan
+ g/RuntimeException;
+ next 001b
+block 0013
+ pred 001b
+ Blort.java:19@0009: return-void . <- .
+ returns
+block 001a
+ @????: goto . <- .
+ next 0012
+block 001b
+ pred 0000
+ pred 0003
+ pred 0007
+ Blort.java:17@0008: Rop{move-exception Ljava/lang/RuntimeException; <- . flow
+ s} v0:Ljava/lang/RuntimeException; <- .
+ @????: goto . <- .
+ next 0013
diff --git a/dx/tests/092-ssa-cfg-edge-cases/info.txt b/dx/tests/092-ssa-cfg-edge-cases/info.txt
new file mode 100644
index 0000000..7c56302
--- /dev/null
+++ b/dx/tests/092-ssa-cfg-edge-cases/info.txt
@@ -0,0 +1,5 @@
+This test case runs a few odd control flow graphs through the optimizer.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/092-ssa-cfg-edge-cases/run b/dx/tests/092-ssa-cfg-edge-cases/run
new file mode 100644
index 0000000..090a4d7
--- /dev/null
+++ b/dx/tests/092-ssa-cfg-edge-cases/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -g -d . Blort.java
+dx --dump --optimize --rop-blocks Blort.class
diff --git a/dx/tests/093-ssa-invoke-range/Blort.java b/dx/tests/093-ssa-invoke-range/Blort.java
new file mode 100644
index 0000000..f2bb66c
--- /dev/null
+++ b/dx/tests/093-ssa-invoke-range/Blort.java
@@ -0,0 +1,69 @@
+
+class Blort {
+
+ static void methodThatNeedsInvokeRange
+ (int a, int b, int c, int d, int e, int f) {
+ }
+
+ void testNoLocals() {
+ methodThatNeedsInvokeRange(5, 0, 5, 0, 5, 0);
+ }
+
+ void testMixedLocals() {
+ int src = 6;
+ int dest = 7;
+
+ methodThatNeedsInvokeRange(src, 0, dest, 1, 5, 0);
+ methodThatNeedsInvokeRange(src, 0, dest, 1, 5, 0);
+ }
+
+ // here the current algorithm partial-overlapping will stumble a bit
+ // The register containing "zero" will be marked as "reserved for locals"
+ // Then the subsequent arraycopy will need a whole new set of 5 registers
+ void testMixedWorseCase() {
+ int src = 6;
+ int dest = 7;
+ int zero = 0;
+
+ methodThatNeedsInvokeRange(src, zero, dest, 1, 5, 0);
+ methodThatNeedsInvokeRange(src, 0, dest, 1, 5, 0);
+ }
+
+ void testAllParams(int a, int b, int c, int d, int e, int f) {
+ methodThatNeedsInvokeRange(a, b, c, d, e, f);
+ }
+
+ // this could try to make use of param positions, but doesn't
+ static void testTailParams(int destPos, int length) {
+ int src = 6;
+ int dest = 7;
+
+ methodThatNeedsInvokeRange(src, 0, dest, 0, destPos, length);
+ }
+
+
+ // This presently requires a whole N new registers
+ void testFlip() {
+ int src = 6;
+ int dest = 7;
+
+ methodThatNeedsInvokeRange(src, 0, dest, 1, 5, 0);
+ methodThatNeedsInvokeRange(dest, 0, src, 1, 5, 0);
+ }
+
+ // ensure that an attempt to combine registers for a local
+ // with a differing category doesn't mess us up.
+ long testMixedCategory(boolean foo) {
+ if (foo) {
+ int offset = 1;
+ int src = 6;
+ int dest = 7;
+
+ methodThatNeedsInvokeRange(src, 0, dest, offset, 5, 0);
+ return offset;
+ } else {
+ long offset = System.currentTimeMillis();;
+ return offset;
+ }
+ }
+}
diff --git a/dx/tests/093-ssa-invoke-range/expected.txt b/dx/tests/093-ssa-invoke-range/expected.txt
new file mode 100644
index 0000000..bb383c0
--- /dev/null
+++ b/dx/tests/093-ssa-invoke-range/expected.txt
@@ -0,0 +1,301 @@
+reading Blort.class...
+method <init> ()V
+first 000c
+block 000a
+ pred 000c
+ Blort.java:2@0000: move-param-object(0) v0:"this"NffffLBlort; <- .
+ Blort.java:2@0000: goto . <- .
+ next 0000
+block 0000
+ pred 000a
+ Blort.java:2@0001: Rop{invoke-direct . <- Ljava/lang/Object; call throws <any
+ >}(java.lang.Object.<init>:()V catch) . <- v0:NffffLBlort;
+ next 000b
+block 000b
+ pred 0000
+ Blort.java:2@0004: return-void . <- .
+ returns
+block 000c
+ @????: goto . <- .
+ next 000a
+
+method methodThatNeedsInvokeRange (IIIIII)V
+first 0004
+block 0002
+ pred 0004
+ Blort.java:6@0000: move-param-int(0) v0:"a"I <- .
+ Blort.java:6@0000: move-param-int(1) v1:"b"I <- .
+ Blort.java:6@0000: move-param-int(2) v2:"c"I <- .
+ Blort.java:6@0000: move-param-int(3) v3:"d"I <- .
+ Blort.java:6@0000: move-param-int(4) v4:"e"I <- .
+ Blort.java:6@0000: move-param-int(5) v5:"f"I <- .
+ Blort.java:6@0000: goto . <- .
+ next 0003
+block 0003
+ pred 0002
+ Blort.java:6@0000: return-void . <- .
+ returns
+block 0004
+ @????: goto . <- .
+ next 0002
+
+method testNoLocals ()V
+first 0016
+block 0014
+ pred 0016
+ Blort.java:9@0000: move-param-object(0) v6:"this"LBlort; <- .
+ Blort.java:9@0000: goto . <- .
+ next 0000
+block 0000
+ pred 0014
+ @????: move-int v2:I=5 <- v0:I=5
+ @????: move-int v3:I=0 <- v1:I=0
+ @????: move-int v4:I=5 <- v0:I=5
+ @????: move-int v5:I=0 <- v1:I=0
+ Blort.java:9@0006: Rop{invoke-static . <- I I I I I I call throws <any>}(Blor
+ t.methodThatNeedsInvokeRange:(IIIIII)V catch) . <- v0:I=5 v1:I=0 v2:I=5 v3:I=
+ 0 v4:I=5 v5:I=0
+ next 0015
+block 0015
+ pred 0000
+ Blort.java:10@0009: return-void . <- .
+ returns
+block 0016
+ @????: const-int(5) v0:I=5 <- .
+ @????: const-int(0) v1:I=0 <- .
+ @????: goto . <- .
+ next 0014
+
+method testMixedLocals ()V
+first 0034
+block 0032
+ pred 0034
+ Blort.java:13@0000: move-param-object(0) v6:"this"LBlort; <- .
+ Blort.java:13@0000: goto . <- .
+ next 0000
+block 0000
+ pred 0032
+ Blort.java:13@0000: const-int(6) v0:I=6 <- .
+ @????: mark-local-int . <- v0:"src"I
+ Blort.java:14@0003: const-int(7) v2:I=7 <- .
+ @????: mark-local-int . <- v2:"dest"I
+ @????: move-int v5:I=0 <- v1:I=0
+ Blort.java:16@000c: Rop{invoke-static . <- I I I I I I call throws <any>}(Blo
+ rt.methodThatNeedsInvokeRange:(IIIIII)V catch) . <- v0:I v1:I=0 v2:I v3:I=1 v
+ 4:I=5 v5:I=0
+ next 000f
+block 000f
+ pred 0000
+ @????: move-int v5:I=0 <- v1:I=0
+ Blort.java:17@0015: Rop{invoke-static . <- I I I I I I call throws <any>}(Blo
+ rt.methodThatNeedsInvokeRange:(IIIIII)V catch) . <- v0:I v1:I=0 v2:I v3:I=1 v
+ 4:I=5 v5:I=0
+ next 0033
+block 0033
+ pred 000f
+ Blort.java:18@0018: return-void . <- .
+ returns
+block 0034
+ @????: const-int(5) v4:I=5 <- .
+ @????: const-int(1) v3:I=1 <- .
+ @????: const-int(0) v1:I=0 <- .
+ @????: goto . <- .
+ next 0032
+
+method testMixedWorseCase ()V
+first 0038
+block 0036
+ pred 0038
+ Blort.java:24@0000: move-param-object(0) v12:"this"LBlort; <- .
+ Blort.java:24@0000: goto . <- .
+ next 0000
+block 0000
+ pred 0036
+ Blort.java:24@0000: const-int(6) v0:I=6 <- .
+ @????: mark-local-int . <- v0:"src"I
+ Blort.java:25@0003: const-int(7) v2:I=7 <- .
+ @????: mark-local-int . <- v2:"dest"I
+ Blort.java:26@0006: const-int(0) v1:I=0 <- .
+ @????: mark-local-int . <- v1:"zero"I
+ Blort.java:28@000e: Rop{invoke-static . <- I I I I I I call throws <any>}(Blo
+ rt.methodThatNeedsInvokeRange:(IIIIII)V catch) . <- v0:I v1:I v2:I v3:I=1 v4:
+ I=5 v5:I=0
+ next 0011
+block 0011
+ pred 0000
+ @????: move-int v6:I <- v0:I
+ @????: move-int v7:I=0 <- v5:I=0
+ @????: move-int v8:I <- v2:I
+ @????: move-int v9:I=1 <- v3:I=1
+ @????: move-int v10:I=5 <- v4:I=5
+ @????: move-int v11:I=0 <- v5:I=0
+ Blort.java:29@0017: Rop{invoke-static . <- I I I I I I call throws <any>}(Blo
+ rt.methodThatNeedsInvokeRange:(IIIIII)V catch) . <- v6:I v7:I=0 v8:I v9:I=1 v
+ 10:I=5 v11:I=0
+ next 0037
+block 0037
+ pred 0011
+ Blort.java:30@001a: return-void . <- .
+ returns
+block 0038
+ @????: const-int(5) v4:I=5 <- .
+ @????: const-int(1) v3:I=1 <- .
+ @????: const-int(0) v5:I=0 <- .
+ @????: goto . <- .
+ next 0036
+
+method testAllParams (IIIIII)V
+first 001c
+block 001a
+ pred 001c
+ Blort.java:33@0000: move-param-object(0) v0:"this"LBlort; <- .
+ Blort.java:33@0000: move-param-int(1) v1:"a"I <- .
+ Blort.java:33@0000: move-param-int(2) v2:"b"I <- .
+ Blort.java:33@0000: move-param-int(3) v3:"c"I <- .
+ Blort.java:33@0000: move-param-int(4) v4:"d"I <- .
+ Blort.java:33@0000: move-param-int(5) v5:"e"I <- .
+ Blort.java:33@0000: move-param-int(6) v6:"f"I <- .
+ Blort.java:33@0000: goto . <- .
+ next 0000
+block 0000
+ pred 001a
+ Blort.java:33@0009: Rop{invoke-static . <- I I I I I I call throws <any>}(Blo
+ rt.methodThatNeedsInvokeRange:(IIIIII)V catch) . <- v1:I v2:I v3:I v4:I v5:I
+ v6:I
+ next 001b
+block 001b
+ pred 0000
+ Blort.java:34@000c: return-void . <- .
+ returns
+block 001c
+ @????: goto . <- .
+ next 001a
+
+method testTailParams (II)V
+first 0022
+block 0020
+ pred 0022
+ Blort.java:38@0000: move-param-int(0) v6:"destPos"I <- .
+ Blort.java:38@0000: move-param-int(1) v7:"length"I <- .
+ Blort.java:38@0000: goto . <- .
+ next 0000
+block 0000
+ pred 0020
+ Blort.java:38@0000: const-int(6) v0:I=6 <- .
+ @????: mark-local-int . <- v0:"src"I
+ Blort.java:39@0003: const-int(7) v2:I=7 <- .
+ @????: mark-local-int . <- v2:"dest"I
+ @????: move-int v3:I=0 <- v1:I=0
+ @????: move-int v4:I <- v6:I
+ @????: move-int v5:I <- v7:I
+ Blort.java:41@000c: Rop{invoke-static . <- I I I I I I call throws <any>}(Blo
+ rt.methodThatNeedsInvokeRange:(IIIIII)V catch) . <- v0:I v1:I=0 v2:I v3:I=0 v
+ 4:I v5:I
+ next 0021
+block 0021
+ pred 0000
+ Blort.java:42@000f: return-void . <- .
+ returns
+block 0022
+ @????: const-int(0) v1:I=0 <- .
+ @????: goto . <- .
+ next 0020
+
+method testFlip ()V
+first 0034
+block 0032
+ pred 0034
+ Blort.java:47@0000: move-param-object(0) v11:"this"LBlort; <- .
+ Blort.java:47@0000: goto . <- .
+ next 0000
+block 0000
+ pred 0032
+ Blort.java:47@0000: const-int(6) v0:I=6 <- .
+ @????: mark-local-int . <- v0:"src"I
+ Blort.java:48@0003: const-int(7) v2:I=7 <- .
+ @????: mark-local-int . <- v2:"dest"I
+ @????: move-int v5:I=0 <- v1:I=0
+ Blort.java:50@000c: Rop{invoke-static . <- I I I I I I call throws <any>}(Blo
+ rt.methodThatNeedsInvokeRange:(IIIIII)V catch) . <- v0:I v1:I=0 v2:I v3:I=1 v
+ 4:I=5 v5:I=0
+ next 000f
+block 000f
+ pred 0000
+ @????: move-int v5:I <- v2:I
+ @????: move-int v6:I=0 <- v1:I=0
+ @????: move-int v7:I <- v0:I
+ @????: move-int v8:I=1 <- v3:I=1
+ @????: move-int v9:I=5 <- v4:I=5
+ @????: move-int v10:I=0 <- v1:I=0
+ Blort.java:51@0015: Rop{invoke-static . <- I I I I I I call throws <any>}(Blo
+ rt.methodThatNeedsInvokeRange:(IIIIII)V catch) . <- v5:I v6:I=0 v7:I v8:I=1 v
+ 9:I=5 v10:I=0
+ next 0033
+block 0033
+ pred 000f
+ Blort.java:52@0018: return-void . <- .
+ returns
+block 0034
+ @????: const-int(5) v4:I=5 <- .
+ @????: const-int(1) v3:I=1 <- .
+ @????: const-int(0) v1:I=0 <- .
+ @????: goto . <- .
+ next 0032
+
+method testMixedCategory (Z)J
+first 0044
+block 003c
+ pred 0044
+ Blort.java:57@0000: move-param-object(0) v8:"this"LBlort; <- .
+ Blort.java:57@0000: move-param-int(1) v9:"foo"Z <- .
+ Blort.java:57@0000: goto . <- .
+ next 0000
+block 0000
+ pred 003c
+ Blort.java:57@0001: if-eqz-int . <- v9:I
+ next 0004 *
+ next 001a
+block 0004
+ pred 0000
+ Blort.java:58@0004: const-int(1) v3:I=1 <- .
+ @????: mark-local-int . <- v3:"offset"I
+ Blort.java:59@0006: const-int(6) v0:I=6 <- .
+ @????: mark-local-int . <- v0:"src"I
+ Blort.java:60@0009: const-int(7) v2:I=7 <- .
+ @????: mark-local-int . <- v2:"dest"I
+ Blort.java:62@0012: const-int(5) v4:I=5 <- .
+ @????: move-int v5:I=0 <- v1:I=0
+ Blort.java:62@0014: Rop{invoke-static . <- I I I I I I call throws <any>}(Blo
+ rt.methodThatNeedsInvokeRange:(IIIIII)V catch) . <- v0:I v1:I=0 v2:I v3:I v4:
+ I=5 v5:I=0
+ next 0017
+block 0017
+ pred 0004
+ Blort.java:63@0018: conv-i2l v4:J <- v3:I
+ Blort.java:63@0019: goto . <- .
+ next 003d
+block 0043
+ pred 001a
+ Blort.java:65@001a: Rop{move-result J <- . flows} v6:J <- .
+ Blort.java:65@001a: goto . <- .
+ next 001d
+block 001a
+ pred 0000
+ Blort.java:65@001a: Rop{invoke-static . <- . call throws <any>}(java.lang.Sys
+ tem.currentTimeMillis:()J catch) . <- .
+ next 0043
+block 001d
+ pred 0043
+ @????: mark-local-long . <- v6:"offset"J
+ @????: move-long v4:J <- v6:"offset"J
+ Blort.java:66@001f: goto . <- .
+ next 003d
+block 003d
+ pred 0017
+ pred 001d
+ Blort.java:66@001f: return-long . <- v4:J
+ returns
+block 0044
+ @????: const-int(0) v1:I=0 <- .
+ @????: goto . <- .
+ next 003c
diff --git a/dx/tests/093-ssa-invoke-range/info.txt b/dx/tests/093-ssa-invoke-range/info.txt
new file mode 100644
index 0000000..372bed7
--- /dev/null
+++ b/dx/tests/093-ssa-invoke-range/info.txt
@@ -0,0 +1,6 @@
+This test case checks the ability of the register allocator to plan
+for dex's invoke-range instruction.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/093-ssa-invoke-range/run b/dx/tests/093-ssa-invoke-range/run
new file mode 100644
index 0000000..aa37784
--- /dev/null
+++ b/dx/tests/093-ssa-invoke-range/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -g -d . Blort.java
+dx --dump --optimize --rop-blocks Blort.class
diff --git a/dx/tests/094-scala-locals/blort.j b/dx/tests/094-scala-locals/blort.j
new file mode 100644
index 0000000..7c711ef
--- /dev/null
+++ b/dx/tests/094-scala-locals/blort.j
@@ -0,0 +1,44 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class Blort
+.super java/lang/Object
+
+.method public static returnint()I
+ .limit stack 1
+ ldc 10
+ ireturn
+.end method
+
+.method public static scalalocals()V
+ .limit locals 5
+ .limit stack 5
+ .var 4 is x I from start to end
+start:
+ invokestatic blort/returnint()I
+ invokestatic blort/returnint()I
+ invokestatic blort/returnint()I
+ invokestatic blort/returnint()I
+ dup
+ istore 4
+ istore 2
+ istore 3
+ istore 1
+ istore 0
+ iload_2
+ istore 4
+ iload_3
+end:
+ return
+.end method
diff --git a/dx/tests/094-scala-locals/expected.txt b/dx/tests/094-scala-locals/expected.txt
new file mode 100644
index 0000000..c74db70
--- /dev/null
+++ b/dx/tests/094-scala-locals/expected.txt
@@ -0,0 +1,85 @@
+reading Blort.class...
+method scalalocals ()V
+first 0025
+block 001a
+ pred 0025
+ live in:{}
+ blort.j:@0000: goto . <- .
+ next 0000
+ live out:{}
+block 0021
+ pred 0000
+ live in:{}
+ blort.j:@0000: goto . <- .
+ next 0003
+ live out:{}
+block 0000
+ pred 001a
+ live in:{}
+ blort.j:@0000: Rop{invoke-static . <- . call throws <any>}(blort.returnint:()
+ I catch) . <- .
+ next 0021
+ live out:{}
+block 0022
+ pred 0003
+ live in:{}
+ blort.j:@0003: goto . <- .
+ next 0006
+ live out:{}
+block 0003
+ pred 0021
+ live in:{}
+ blort.j:@0003: Rop{invoke-static . <- . call throws <any>}(blort.returnint:()
+ I catch) . <- .
+ next 0022
+ live out:{}
+block 0023
+ pred 0006
+ live in:{}
+ blort.j:@0006: goto . <- .
+ next 0009
+ live out:{}
+block 0006
+ pred 0022
+ live in:{}
+ blort.j:@0006: Rop{invoke-static . <- . call throws <any>}(blort.returnint:()
+ I catch) . <- .
+ next 0023
+ live out:{}
+block 0024
+ pred 0009
+ live in:{}
+ blort.j:@0009: Rop{move-result I <- . flows} v14:I <- .
+ blort.j:@0009: goto . <- .
+ next 000c
+ live out:{14}
+block 0009
+ pred 0023
+ live in:{}
+ blort.j:@0009: Rop{invoke-static . <- . call throws <any>}(blort.returnint:()
+ I catch) . <- .
+ next 0024
+ live out:{}
+block 000c
+ pred 0024
+ live in:{14}
+ @????: mark-local-int . <- v14:"x"I
+ blort.j:@001b: goto . <- .
+ next 001b
+ live out:{}
+block 001b
+ pred 000c
+ live in:{}
+ blort.j:@001b: return-void . <- .
+ next 0026
+ live out:{}
+block 0025
+ live in:{}
+ @????: goto . <- .
+ next 001a
+ live out:{}
+block 0026
+ pred 001b
+ live in:{}
+ returns
+ live out:{}
diff --git a/dx/tests/094-scala-locals/info.txt b/dx/tests/094-scala-locals/info.txt
new file mode 100644
index 0000000..cb1a42e
--- /dev/null
+++ b/dx/tests/094-scala-locals/info.txt
@@ -0,0 +1,8 @@
+This is a smoke test of the SSA renamer's local variable preserver.
+It tests a case observed from Scala, wherein a local variable is assigned
+an identical value twice. The correct result should be only a single
+mark-local, with the second assignment eaten by copy-propogation.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/094-scala-locals/run b/dx/tests/094-scala-locals/run
new file mode 100644
index 0000000..4bbfa79
--- /dev/null
+++ b/dx/tests/094-scala-locals/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+jasmin -d . blort.j >/dev/null
+dx --debug --dump --ssa-blocks --method=scalalocals Blort.class
diff --git a/dx/tests/095-dex-const-string-jumbo/Blort.java b/dx/tests/095-dex-const-string-jumbo/Blort.java
new file mode 100644
index 0000000..a0271b5
--- /dev/null
+++ b/dx/tests/095-dex-const-string-jumbo/Blort.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort {
+ static public void consume(String s) {
+ // This space intentionally left blank.
+ }
+
+ public void test() {
+ consume("zorch");
+ }
+}
diff --git a/dx/tests/095-dex-const-string-jumbo/expected.txt b/dx/tests/095-dex-const-string-jumbo/expected.txt
new file mode 100644
index 0000000..a4014d9
--- /dev/null
+++ b/dx/tests/095-dex-const-string-jumbo/expected.txt
@@ -0,0 +1,6 @@
+Blort.test:()V:
+regs: 0003; ins: 0001; outs: 0001
+ 0000: move-object v0, v2
+ 0001: const-string/jumbo v1, "zorch"
+ 0004: invoke-static {v1}, Blort.consume:(Ljava/lang/String;)V
+ 0007: return-void
diff --git a/dx/tests/095-dex-const-string-jumbo/info.txt b/dx/tests/095-dex-const-string-jumbo/info.txt
new file mode 100644
index 0000000..c14fd8e
--- /dev/null
+++ b/dx/tests/095-dex-const-string-jumbo/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+const-string/jumbo gets emitted appropriately.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/095-dex-const-string-jumbo/run b/dx/tests/095-dex-const-string-jumbo/run
new file mode 100644
index 0000000..a1c7365
--- /dev/null
+++ b/dx/tests/095-dex-const-string-jumbo/run
@@ -0,0 +1,41 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Write out files with 32768 total static string declarations, so that
+# the reference to "zorch" in the real test file will be guaranteed to
+# need a jumbo string reference (it sorts last after all the others).
+# Note: Each string reference is stored in a separate static variable,
+# and that variable's name is also represented in the strings, which
+# is why we can just have 32768 and not 65536 declarations.
+
+awk '
+BEGIN {
+ writeFile("Zorch1", 0, 16383);
+ writeFile("Zorch2", 16384, 32767);
+}
+function writeFile(name, start, end) {
+ fileName = name ".java";
+ printf("public class %s {\n", name) > fileName;
+ for (i = start; i <= end; i++) {
+ printf(" static public final String s%d = \"%d\";\n",
+ i, i) > fileName;
+ }
+ printf("}\n") > fileName;
+}'
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+ --dump-method=Blort.test *.class
diff --git a/dx/tests/096-dex-giant-catch/Blort.java b/dx/tests/096-dex-giant-catch/Blort.java
new file mode 100644
index 0000000..f5f6e8d
--- /dev/null
+++ b/dx/tests/096-dex-giant-catch/Blort.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort {
+ static public void blort(long v1, long v2, long v3, long v4,
+ long v5, long v6, long v7, long v8) {
+ // This space intentionally left blank.
+ }
+}
diff --git a/dx/tests/096-dex-giant-catch/expected.txt b/dx/tests/096-dex-giant-catch/expected.txt
new file mode 100644
index 0000000..e71992e
--- /dev/null
+++ b/dx/tests/096-dex-giant-catch/expected.txt
@@ -0,0 +1,5 @@
+ catches
+ try 0024..00010017
+ catch java.lang.RuntimeException -> 00011260
+ try 0001003b..0001125f
+ catch java.lang.RuntimeException -> 00011260
diff --git a/dx/tests/096-dex-giant-catch/info.txt b/dx/tests/096-dex-giant-catch/info.txt
new file mode 100644
index 0000000..b81ce94
--- /dev/null
+++ b/dx/tests/096-dex-giant-catch/info.txt
@@ -0,0 +1,7 @@
+This is a smoke test of dex conversion, which checks to see that
+very long catch ranges (that cover >= 65536 code units) get emitted
+appropriately.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/096-dex-giant-catch/run b/dx/tests/096-dex-giant-catch/run
new file mode 100644
index 0000000..c81d04c
--- /dev/null
+++ b/dx/tests/096-dex-giant-catch/run
@@ -0,0 +1,40 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Write out a file with a really huge catch range.
+
+awk '
+BEGIN {
+ fileName = "Zorch.java";
+ printf("public class Zorch {\n") > fileName;
+ printf(" static public void test() {\n") > fileName;
+ printf(" try {\n") > fileName;
+ for (i = 0; i <= 1800; i++) {
+ d = i + 1000000;
+ printf(" Blort.blort(100%dL, 200%dL, 300%dL, 400%dL, 500%dL, " \
+ "600%dL, 700%dL, 800%dL);\n",
+ d, d + 1, d + 2, d + 3, d + 4, d + 5, d + 6, d + 7) > fileName;
+ }
+ printf(" } catch (RuntimeException ex) {\n") > fileName;
+ printf(" throw ex;\n") > fileName;
+ printf(" }\n") > fileName;
+ printf(" }\n") > fileName;
+ printf("}\n") > fileName;
+}'
+
+$JAVAC -d . *.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+ --dump-method=Zorch.test Zorch.class | grep 'try\|catch'
diff --git a/dx/tests/097-dex-branch-offset-zero/Blort.java b/dx/tests/097-dex-branch-offset-zero/Blort.java
new file mode 100644
index 0000000..5033c8f
--- /dev/null
+++ b/dx/tests/097-dex-branch-offset-zero/Blort.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort
+{
+ public void test1() {
+ for (;;) /*empty*/ ;
+ }
+
+ public void test2(int x) {
+ while (x > 0) /*empty*/ ;
+ }
+
+ public void test3(int x, int y) {
+ while (x < y) /*empty*/ ;
+ }
+}
diff --git a/dx/tests/097-dex-branch-offset-zero/expected.txt b/dx/tests/097-dex-branch-offset-zero/expected.txt
new file mode 100644
index 0000000..2b021a5
--- /dev/null
+++ b/dx/tests/097-dex-branch-offset-zero/expected.txt
@@ -0,0 +1 @@
+No bad branches found.
diff --git a/dx/tests/097-dex-branch-offset-zero/info.txt b/dx/tests/097-dex-branch-offset-zero/info.txt
new file mode 100644
index 0000000..4bf9502
--- /dev/null
+++ b/dx/tests/097-dex-branch-offset-zero/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to make sure that
+the only non-switch branches to offset 0 happen using the goto/32 opcode.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/097-dex-branch-offset-zero/run b/dx/tests/097-dex-branch-offset-zero/run
new file mode 100644
index 0000000..34539eb
--- /dev/null
+++ b/dx/tests/097-dex-branch-offset-zero/run
@@ -0,0 +1,27 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -g -d . Blort.java
+dx --debug --dex --positions=none --no-locals --dump-method=Blort.test'*' \
+ *.class | grep '[-+][0-9]' | grep -v 'goto/32.*+00*$' | grep '// +00*$'
+
+if [ "$?" = "1" ]; then
+ echo "No bad branches found."
+else
+ # Redo the dx command without filters, to aid with debugging.
+ dx --debug --dex --positions=none --no-locals --dump-method=Blort.test'*' \
+ *.class
+fi
diff --git a/dx/tests/098-dex-jsr-ret-throw/ViewDebug$ViewServer.class b/dx/tests/098-dex-jsr-ret-throw/ViewDebug$ViewServer.class
new file mode 100644
index 0000000..ff992ca
--- /dev/null
+++ b/dx/tests/098-dex-jsr-ret-throw/ViewDebug$ViewServer.class
Binary files differ
diff --git a/dx/tests/098-dex-jsr-ret-throw/expected.txt b/dx/tests/098-dex-jsr-ret-throw/expected.txt
new file mode 100644
index 0000000..b5b1b93
--- /dev/null
+++ b/dx/tests/098-dex-jsr-ret-throw/expected.txt
@@ -0,0 +1,652 @@
+reading ViewDebug$ViewServer.class...
+method run ()V
+first 0162
+block 0162
+ ViewDebug.java:564@0000: move-param-object(0) v0:"this"Landroid/view/ViewDebu
+ g$ViewServer; <- .
+ ViewDebug.java:564@0000: goto . <- .
+ next 0000
+block 0169
+ pred 0000
+ ViewDebug.java:564@0001: Rop{move-result-pseudo Ljava/net/ServerSocket; <- .
+ flows} v11:Ljava/net/ServerSocket; <- .
+ ViewDebug.java:564@0001: goto . <- .
+ next 0004
+block 0000
+ pred 0162
+ ViewDebug.java:564@0000: move-object v11:Landroid/view/ViewDebug$ViewServer;
+ <- v0:Landroid/view/ViewDebug$ViewServer;
+ ViewDebug.java:564@0001: get-field-object(android.view.ViewDebug$ViewServer.m
+ ViewServerSocket:Ljava/net/ServerSocket; catch) . <- v11:Landroid/view/ViewDe
+ bug$ViewServer;
+ next 0169
+block 0004
+ pred 0169
+ ViewDebug.java:564@0004: move-object v1:"server"Ljava/net/ServerSocket; <- v1
+ 1:Ljava/net/ServerSocket;
+ ViewDebug.java:564@0004: goto . <- .
+ next 0005
+block 016a
+ pred 0005
+ ViewDebug.java:566@0005: Rop{move-result Ljava/lang/Thread; <- . flows} v11:L
+ java/lang/Thread; <- .
+ ViewDebug.java:566@0005: goto . <- .
+ next 0008
+block 0005
+ pred 0004
+ pred 00ad
+ ViewDebug.java:566@0005: Rop{invoke-static . <- . call throws <any>}(java.lan
+ g.Thread.currentThread:()Ljava/lang/Thread; catch) . <- .
+ next 016a
+block 016b
+ pred 0008
+ ViewDebug.java:566@0009: Rop{move-result-pseudo Ljava/lang/Thread; <- . flows
+ } v12:Ljava/lang/Thread; <- .
+ ViewDebug.java:566@0009: goto . <- .
+ next 000c
+block 0008
+ pred 016a
+ ViewDebug.java:566@0008: move-object v12:Landroid/view/ViewDebug$ViewServer;
+ <- v0:Landroid/view/ViewDebug$ViewServer;
+ ViewDebug.java:566@0009: get-field-object(android.view.ViewDebug$ViewServer.m
+ Thread:Ljava/lang/Thread; catch) . <- v12:Landroid/view/ViewDebug$ViewServer;
+ next 016b
+block 000c
+ pred 016b
+ ViewDebug.java:566@000c: if-ne-object . <- v11:Ljava/lang/Thread; v12:Ljava/l
+ ang/Thread;
+ next 000f *
+ next 00b0
+block 000f
+ pred 000c
+ ViewDebug.java:567@000f: const-object-nothrow(null) v11:<null>=null <- .
+ ViewDebug.java:567@0010: move-object v2:"client"Ljava/net/Socket; <- v11:<nul
+ l>=null
+ ViewDebug.java:567@0010: goto . <- .
+ next 0011
+block 016c
+ pred 0011
+ ViewDebug.java:569@0012: Rop{move-result Ljava/net/Socket; <- . flows} v11:Lj
+ ava/net/Socket; <- .
+ ViewDebug.java:569@0012: goto . <- .
+ next 0015
+block 0011
+ pred 000f
+ ViewDebug.java:569@0011: move-object v11:Ljava/net/ServerSocket; <- v1:Ljava/
+ net/ServerSocket;
+ ViewDebug.java:569@0012: Rop{invoke-virtual . <- Ljava/net/ServerSocket; call
+ throws <any>}(java.net.ServerSocket.accept:()Ljava/net/Socket; catch java.io
+ .IOException java.lang.Object) . <- v11:Ljava/net/ServerSocket;
+ next 0130
+ next 0140
+ next 016c *
+block 0015
+ pred 016c
+ ViewDebug.java:569@0015: move-object v2:"client"Ljava/net/Socket; <- v11:Ljav
+ a/net/Socket;
+ ViewDebug.java:571@0016: const-object-nothrow(null) v11:<null>=null <- .
+ ViewDebug.java:571@0017: move-object v3:"in"Ljava/io/BufferedReader; <- v11:<
+ null>=null
+ ViewDebug.java:571@0017: goto . <- .
+ next 0018
+block 016d
+ pred 0018
+ ViewDebug.java:573@0018: Rop{move-result-pseudo N0018Ljava/io/BufferedReader;
+ <- . flows} v11:N0018Ljava/io/BufferedReader; <- .
+ ViewDebug.java:573@0018: goto . <- .
+ next 001b
+block 0018
+ pred 0015
+ ViewDebug.java:573@0018: new-instance(java.io.BufferedReader catch java.lang.
+ Object) . <- .
+ next 0116
+ next 016d *
+block 016e
+ pred 001b
+ ViewDebug.java:573@001c: Rop{move-result-pseudo N001cLjava/io/InputStreamRead
+ er; <- . flows} v13:N001cLjava/io/InputStreamReader; <- .
+ ViewDebug.java:573@001c: goto . <- .
+ next 001f
+block 001b
+ pred 016d
+ ViewDebug.java:573@001b: move-object v16:N0018Ljava/io/BufferedReader; <- v11
+ :N0018Ljava/io/BufferedReader;
+ ViewDebug.java:573@001b: move-object v11:N0018Ljava/io/BufferedReader; <- v16
+ :N0018Ljava/io/BufferedReader;
+ ViewDebug.java:573@001b: move-object v12:N0018Ljava/io/BufferedReader; <- v16
+ :N0018Ljava/io/BufferedReader;
+ ViewDebug.java:573@001c: new-instance(java.io.InputStreamReader catch java.la
+ ng.Object) . <- .
+ next 0116
+ next 016e *
+block 016f
+ pred 001f
+ ViewDebug.java:573@0021: Rop{move-result Ljava/io/InputStream; <- . flows} v1
+ 5:Ljava/io/InputStream; <- .
+ ViewDebug.java:573@0021: goto . <- .
+ next 0024
+block 001f
+ pred 016e
+ ViewDebug.java:573@001f: move-object v16:N001cLjava/io/InputStreamReader; <-
+ v13:N001cLjava/io/InputStreamReader;
+ ViewDebug.java:573@001f: move-object v13:N001cLjava/io/InputStreamReader; <-
+ v16:N001cLjava/io/InputStreamReader;
+ ViewDebug.java:573@001f: move-object v14:N001cLjava/io/InputStreamReader; <-
+ v16:N001cLjava/io/InputStreamReader;
+ ViewDebug.java:573@0020: move-object v15:Ljava/net/Socket; <- v2:Ljava/net/So
+ cket;
+ ViewDebug.java:573@0021: Rop{invoke-virtual . <- Ljava/net/Socket; call throw
+ s <any>}(java.net.Socket.getInputStream:()Ljava/io/InputStream; catch java.la
+ ng.Object) . <- v15:Ljava/net/Socket;
+ next 0116
+ next 016f *
+block 0024
+ pred 016f
+ ViewDebug.java:573@0024: Rop{invoke-direct . <- Ljava/io/InputStreamReader; L
+ java/io/InputStream; call throws <any>}(java.io.InputStreamReader.<init>:(Lja
+ va/io/InputStream;)V catch java.lang.Object) . <- v14:N001cLjava/io/InputStre
+ amReader; v15:Ljava/io/InputStream;
+ next 0116
+ next 0027 *
+block 0027
+ pred 0024
+ ViewDebug.java:573@0027: Rop{invoke-direct . <- Ljava/io/BufferedReader; Ljav
+ a/io/Reader; call throws <any>}(java.io.BufferedReader.<init>:(Ljava/io/Reade
+ r;)V catch java.lang.Object) . <- v12:N0018Ljava/io/BufferedReader; v13:Ljava
+ /io/InputStreamReader;
+ next 0116
+ next 002a *
+block 0170
+ pred 002a
+ ViewDebug.java:574@002c: Rop{move-result Ljava/lang/String; <- . flows} v11:L
+ java/lang/String; <- .
+ ViewDebug.java:574@002c: goto . <- .
+ next 002f
+block 002a
+ pred 0027
+ ViewDebug.java:573@002a: move-object v3:"in"Ljava/io/BufferedReader; <- v11:L
+ java/io/BufferedReader;
+ ViewDebug.java:574@002b: move-object v11:Ljava/io/BufferedReader; <- v3:Ljava
+ /io/BufferedReader;
+ ViewDebug.java:574@002c: Rop{invoke-virtual . <- Ljava/io/BufferedReader; cal
+ l throws <any>}(java.io.BufferedReader.readLine:()Ljava/lang/String; catch ja
+ va.lang.Object) . <- v11:Ljava/io/BufferedReader;
+ next 0116
+ next 0170 *
+block 0171
+ pred 002f
+ ViewDebug.java:576@0031: Rop{move-result-pseudo Ljava/lang/String; <- . flows
+ } v11:Ljava/lang/String;="DUMP" <- .
+ ViewDebug.java:576@0031: goto . <- .
+ next 0033
+block 002f
+ pred 0170
+ ViewDebug.java:574@002f: move-object v4:"command"Ljava/lang/String; <- v11:Lj
+ ava/lang/String;
+ ViewDebug.java:576@0031: const-object("DUMP" catch java.lang.Object) . <- .
+ next 0116
+ next 0171 *
+block 0172
+ pred 0033
+ ViewDebug.java:576@0035: Rop{move-result Z <- . flows} v11:Z <- .
+ ViewDebug.java:576@0035: goto . <- .
+ next 0038
+block 0033
+ pred 0171
+ ViewDebug.java:576@0033: move-object v12:Ljava/lang/String; <- v4:Ljava/lang/
+ String;
+ ViewDebug.java:576@0035: Rop{invoke-virtual . <- Ljava/lang/String; Ljava/lan
+ g/String; call throws <any>}(java.lang.String.equalsIgnoreCase:(Ljava/lang/St
+ ring;)Z catch java.lang.Object) . <- v11:Ljava/lang/String;="DUMP" v12:Ljava/
+ lang/String;
+ next 0116
+ next 0172 *
+block 0038
+ pred 0172
+ ViewDebug.java:576@0038: if-eqz-int . <- v11:I
+ next 003b *
+ next 0042
+block 003b
+ pred 0038
+ ViewDebug.java:577@003b: move-object v11:Ljava/net/Socket; <- v2:Ljava/net/So
+ cket;
+ ViewDebug.java:577@003c: Rop{invoke-static . <- Ljava/net/Socket; call throws
+ <any>}(android.view.ViewDebug$ViewServer.dump:(Ljava/net/Socket;)V catch jav
+ a.lang.Object) . <- v11:Ljava/net/Socket;
+ next 0116
+ next 003f *
+block 003f
+ pred 003b
+ ViewDebug.java:577@003f: goto . <- .
+ next 005f
+block 0173
+ pred 0042
+ ViewDebug.java:579@0044: Rop{move-result-pseudo Ljava/lang/String; <- . flows
+ } v12:Ljava/lang/String;=" " <- .
+ ViewDebug.java:579@0044: goto . <- .
+ next 0046
+block 0042
+ pred 0038
+ ViewDebug.java:579@0042: move-object v11:Ljava/lang/String; <- v4:Ljava/lang/
+ String;
+ ViewDebug.java:579@0044: const-object(" " catch java.lang.Object) . <- .
+ next 0116
+ next 0173 *
+block 0174
+ pred 0046
+ ViewDebug.java:579@0046: Rop{move-result [Ljava/lang/String; <- . flows} v11:
+ [Ljava/lang/String; <- .
+ ViewDebug.java:579@0046: goto . <- .
+ next 0049
+block 0046
+ pred 0173
+ ViewDebug.java:579@0046: Rop{invoke-virtual . <- Ljava/lang/String; Ljava/lan
+ g/String; call throws <any>}(java.lang.String.split:(Ljava/lang/String;)[Ljav
+ a/lang/String; catch java.lang.Object) . <- v11:Ljava/lang/String; v12:Ljava/
+ lang/String;=" "
+ next 0116
+ next 0174 *
+block 0175
+ pred 0049
+ ViewDebug.java:580@004b: Rop{move-result-pseudo Ljava/lang/String; <- . flows
+ } v11:Ljava/lang/String;="CAPTURE" <- .
+ ViewDebug.java:580@004b: goto . <- .
+ next 004d
+block 0049
+ pred 0174
+ ViewDebug.java:579@0049: move-object v5:"params"[Ljava/lang/String; <- v11:[L
+ java/lang/String;
+ ViewDebug.java:580@004b: const-object("CAPTURE" catch java.lang.Object) . <-
+ .
+ next 0116
+ next 0175 *
+block 0176
+ pred 004d
+ ViewDebug.java:580@0050: Rop{move-result-pseudo Ljava/lang/String; <- . flows
+ } v12:Ljava/lang/String; <- .
+ ViewDebug.java:580@0050: goto . <- .
+ next 0051
+block 004d
+ pred 0175
+ ViewDebug.java:580@004d: move-object v12:[Ljava/lang/String; <- v5:[Ljava/lan
+ g/String;
+ ViewDebug.java:580@004f: const-int(0) v13:I=0 <- .
+ ViewDebug.java:580@0050: aget-object(catch java.lang.Object) . <- v12:[Ljava/
+ lang/String; v13:I=0
+ next 0116
+ next 0176 *
+block 0177
+ pred 0051
+ ViewDebug.java:580@0051: Rop{move-result Z <- . flows} v11:Z <- .
+ ViewDebug.java:580@0051: goto . <- .
+ next 0054
+block 0051
+ pred 0176
+ ViewDebug.java:580@0051: Rop{invoke-virtual . <- Ljava/lang/String; Ljava/lan
+ g/String; call throws <any>}(java.lang.String.equalsIgnoreCase:(Ljava/lang/St
+ ring;)Z catch java.lang.Object) . <- v11:Ljava/lang/String;="CAPTURE" v12:Lja
+ va/lang/String;
+ next 0116
+ next 0177 *
+block 0054
+ pred 0177
+ ViewDebug.java:580@0054: if-eqz-int . <- v11:I
+ next 0057 *
+ next 005f
+block 0178
+ pred 0057
+ ViewDebug.java:581@005b: Rop{move-result-pseudo Ljava/lang/String; <- . flows
+ } v12:Ljava/lang/String; <- .
+ ViewDebug.java:581@005b: goto . <- .
+ next 005c
+block 0057
+ pred 0054
+ ViewDebug.java:581@0057: move-object v11:Ljava/net/Socket; <- v2:Ljava/net/So
+ cket;
+ ViewDebug.java:581@0058: move-object v12:[Ljava/lang/String; <- v5:[Ljava/lan
+ g/String;
+ ViewDebug.java:581@005a: const-int(1) v13:I=1 <- .
+ ViewDebug.java:581@005b: aget-object(catch java.lang.Object) . <- v12:[Ljava/
+ lang/String; v13:I=1
+ next 0116
+ next 0178 *
+block 005c
+ pred 0178
+ ViewDebug.java:581@005c: Rop{invoke-static . <- Ljava/net/Socket; Ljava/lang/
+ String; call throws <any>}(android.view.ViewDebug$ViewServer.capture:(Ljava/n
+ et/Socket;Ljava/lang/String;)V catch java.lang.Object) . <- v11:Ljava/net/Soc
+ ket; v12:Ljava/lang/String;
+ next 0116
+ next 005f *
+block 0065
+ pred 0116
+ ViewDebug.java:586@0065: move-object v6:Ljava/lang/Class;=java.lang.Object <-
+ v11:Ljava/lang/Class;=java.lang.Object
+ ViewDebug.java:586@0065: goto . <- .
+ next 0067
+block 0062
+ pred 018d
+ ViewDebug.java:589@0062: goto . <- .
+ next 0079
+block 006a
+ pred 018a
+ ViewDebug.java:586@006a: move-object v11:Ljava/lang/Class;=java.lang.Object <
+ - v6:Ljava/lang/Class;=java.lang.Object
+ ViewDebug.java:586@006c: throw(catch java.io.IOException java.lang.Object) .
+ <- v11:Ljava/lang/Class;=java.lang.Object
+ next 0130
+ next 0140
+block 0179
+ pred 007f
+ ViewDebug.java:591@0080: Rop{move-result-pseudo Ljava/lang/String; <- . flows
+ } v11:Ljava/lang/String;="ViewServer" <- .
+ ViewDebug.java:591@0080: goto . <- .
+ next 0082
+block 007f
+ pred 0130
+ ViewDebug.java:590@007f: move-object v3:"e"Ljava/io/IOException; <- v11:Ljava
+ /lang/Class;=java.io.IOException
+ ViewDebug.java:591@0080: const-object("ViewServer" catch java.lang.Object) .
+ <- .
+ next 0140
+ next 0179 *
+block 017a
+ pred 0082
+ ViewDebug.java:591@0082: Rop{move-result-pseudo Ljava/lang/String; <- . flows
+ } v12:Ljava/lang/String;="Connection error: " <- .
+ ViewDebug.java:591@0082: goto . <- .
+ next 0084
+block 0082
+ pred 0179
+ ViewDebug.java:591@0082: const-object("Connection error: " catch java.lang.Ob
+ ject) . <- .
+ next 0140
+ next 017a *
+block 017b
+ pred 0084
+ ViewDebug.java:591@0085: Rop{move-result I <- . flows} v11:I <- .
+ ViewDebug.java:591@0085: goto . <- .
+ next 0088
+block 0084
+ pred 017a
+ ViewDebug.java:591@0084: move-object v13:Ljava/io/IOException; <- v3:Ljava/io
+ /IOException;
+ ViewDebug.java:591@0085: Rop{invoke-static . <- Ljava/lang/String; Ljava/lang
+ /String; Ljava/lang/Throwable; call throws <any>}(android.util.Log.w:(Ljava/l
+ ang/String;Ljava/lang/String;Ljava/lang/Throwable;)I catch java.lang.Object)
+ . <- v11:Ljava/lang/String;="ViewServer" v12:Ljava/lang/String;="Connection e
+ rror: " v13:Ljava/io/IOException;
+ next 0140
+ next 017b *
+block 0088
+ pred 017b
+ @????: goto . <- .
+ next 0089
+block 008f
+ pred 0140
+ ViewDebug.java:593@008f: move-object v8:Ljava/lang/Class;=java.lang.Object <-
+ v11:Ljava/lang/Class;=java.lang.Object
+ ViewDebug.java:593@008f: goto . <- .
+ next 0091
+block 007c
+ pred 0190
+ ViewDebug.java:600@007c: goto . <- .
+ next 00ad
+block 008c
+ pred 0184
+ ViewDebug.java:600@008c: goto . <- .
+ next 00ad
+block 0094
+ pred 017e
+ ViewDebug.java:593@0094: move-object v11:Ljava/lang/Class;=java.lang.Object <
+ - v8:Ljava/lang/Class;=java.lang.Object
+ ViewDebug.java:593@0096: throw(catch) . <- v11:Ljava/lang/Class;=java.lang.Ob
+ ject
+ returns
+block 00ad
+ pred 007c
+ pred 008c
+ ViewDebug.java:601@00ad: goto . <- .
+ next 0005
+block 00b0
+ pred 000c
+ ViewDebug.java:602@00b0: goto . <- .
+ next 0163
+block 0163
+ pred 00b0
+ ViewDebug.java:602@00b0: return-void . <- .
+ returns
+block 0116
+ pred 0018
+ pred 001b
+ pred 001f
+ pred 0024
+ pred 0027
+ pred 002a
+ pred 002f
+ pred 0033
+ pred 003b
+ pred 0042
+ pred 0046
+ pred 0049
+ pred 004d
+ pred 0051
+ pred 0057
+ pred 005c
+ ViewDebug.java:586@0065: Rop{move-exception Ljava/lang/Object; <- . flows} v1
+ 1:Ljava/lang/Object; <- .
+ ViewDebug.java:586@0065: goto . <- .
+ next 0065
+block 0130
+ pred 0011
+ pred 006a
+ pred 0189
+ pred 018c
+ ViewDebug.java:590@007f: Rop{move-exception Ljava/io/IOException; <- . flows}
+ v11:Ljava/io/IOException; <- .
+ ViewDebug.java:590@007f: goto . <- .
+ next 007f
+block 0140
+ pred 0011
+ pred 006a
+ pred 007f
+ pred 0082
+ pred 0084
+ pred 0189
+ pred 018c
+ ViewDebug.java:593@008f: Rop{move-exception Ljava/lang/Object; <- . flows} v1
+ 1:Ljava/lang/Object; <- .
+ ViewDebug.java:593@008f: goto . <- .
+ next 008f
+block 017c
+ pred 0091
+ ViewDebug.java:593@0099: move-object v11:Ljava/net/Socket; <- v2:Ljava/net/So
+ cket;
+ ViewDebug.java:593@009a: if-eqz-object . <- v11:Ljava/net/Socket;
+ next 017d *
+ next 017e
+block 017d
+ pred 017c
+ ViewDebug.java:595@009d: move-object v11:Ljava/net/Socket; <- v2:Ljava/net/So
+ cket;
+ ViewDebug.java:595@009e: Rop{invoke-virtual . <- Ljava/net/Socket; call throw
+ s <any>}(java.net.Socket.close:()V catch java.io.IOException) . <- v11:Ljava/
+ net/Socket;
+ next 017f
+ next 0180 *
+block 0180
+ pred 017d
+ ViewDebug.java:598@00a1: goto . <- .
+ next 017e
+block 017e
+ pred 017c
+ pred 0180
+ pred 0181
+ @????: goto . <- .
+ next 0094
+block 017f
+ pred 017d
+ ViewDebug.java:596@00a4: Rop{move-exception Ljava/io/IOException; <- . flows}
+ v11:Ljava/io/IOException; <- .
+ ViewDebug.java:596@00a4: goto . <- .
+ next 0181
+block 0181
+ pred 017f
+ ViewDebug.java:596@00a4: move-object v10:"e"Ljava/io/IOException; <- v11:Ljav
+ a/lang/Class;=java.io.IOException
+ ViewDebug.java:597@00a6: move-object v11:Ljava/io/IOException; <- v10:Ljava/i
+ o/IOException;
+ ViewDebug.java:597@00a8: Rop{invoke-virtual . <- Ljava/io/IOException; call t
+ hrows <any>}(java.io.IOException.printStackTrace:()V catch) . <- v11:Ljava/io
+ /IOException;
+ next 017e
+block 0091
+ pred 008f
+ @????: goto . <- .
+ next 017c
+block 0182
+ pred 0089
+ ViewDebug.java:593@0099: move-object v11:Ljava/net/Socket; <- v2:Ljava/net/So
+ cket;
+ ViewDebug.java:593@009a: if-eqz-object . <- v11:Ljava/net/Socket;
+ next 0183 *
+ next 0184
+block 0183
+ pred 0182
+ ViewDebug.java:595@009d: move-object v11:Ljava/net/Socket; <- v2:Ljava/net/So
+ cket;
+ ViewDebug.java:595@009e: Rop{invoke-virtual . <- Ljava/net/Socket; call throw
+ s <any>}(java.net.Socket.close:()V catch java.io.IOException) . <- v11:Ljava/
+ net/Socket;
+ next 0185
+ next 0186 *
+block 0186
+ pred 0183
+ ViewDebug.java:598@00a1: goto . <- .
+ next 0184
+block 0184
+ pred 0182
+ pred 0186
+ pred 0187
+ @????: goto . <- .
+ next 008c
+block 0185
+ pred 0183
+ ViewDebug.java:596@00a4: Rop{move-exception Ljava/io/IOException; <- . flows}
+ v11:Ljava/io/IOException; <- .
+ ViewDebug.java:596@00a4: goto . <- .
+ next 0187
+block 0187
+ pred 0185
+ ViewDebug.java:596@00a4: move-object v10:"e"Ljava/io/IOException; <- v11:Ljav
+ a/lang/Class;=java.io.IOException
+ ViewDebug.java:597@00a6: move-object v11:Ljava/io/IOException; <- v10:Ljava/i
+ o/IOException;
+ ViewDebug.java:597@00a8: Rop{invoke-virtual . <- Ljava/io/IOException; call t
+ hrows <any>}(java.io.IOException.printStackTrace:()V catch) . <- v11:Ljava/io
+ /IOException;
+ next 0184
+block 0089
+ pred 0088
+ @????: goto . <- .
+ next 0182
+block 0188
+ pred 0067
+ ViewDebug.java:586@006f: move-object v11:Ljava/io/BufferedReader; <- v3:Ljava
+ /io/BufferedReader;
+ ViewDebug.java:586@0070: if-eqz-object . <- v11:Ljava/io/BufferedReader;
+ next 0189 *
+ next 018a
+block 0189
+ pred 0188
+ ViewDebug.java:587@0073: move-object v11:Ljava/io/BufferedReader; <- v3:Ljava
+ /io/BufferedReader;
+ ViewDebug.java:587@0074: Rop{invoke-virtual . <- Ljava/io/BufferedReader; cal
+ l throws <any>}(java.io.BufferedReader.close:()V catch java.io.IOException ja
+ va.lang.Object) . <- v11:Ljava/io/BufferedReader;
+ next 0130
+ next 0140
+ next 018a *
+block 018a
+ pred 0188
+ pred 0189
+ @????: goto . <- .
+ next 006a
+block 0067
+ pred 0065
+ @????: goto . <- .
+ next 0188
+block 018b
+ pred 005f
+ ViewDebug.java:586@006f: move-object v11:Ljava/io/BufferedReader; <- v3:Ljava
+ /io/BufferedReader;
+ ViewDebug.java:586@0070: if-eqz-object . <- v11:Ljava/io/BufferedReader;
+ next 018c *
+ next 018d
+block 018c
+ pred 018b
+ ViewDebug.java:587@0073: move-object v11:Ljava/io/BufferedReader; <- v3:Ljava
+ /io/BufferedReader;
+ ViewDebug.java:587@0074: Rop{invoke-virtual . <- Ljava/io/BufferedReader; cal
+ l throws <any>}(java.io.BufferedReader.close:()V catch java.io.IOException ja
+ va.lang.Object) . <- v11:Ljava/io/BufferedReader;
+ next 0130
+ next 0140
+ next 018d *
+block 018d
+ pred 018b
+ pred 018c
+ @????: goto . <- .
+ next 0062
+block 005f
+ pred 003f
+ pred 0054
+ pred 005c
+ @????: goto . <- .
+ next 018b
+block 018e
+ pred 0079
+ ViewDebug.java:593@0099: move-object v11:Ljava/net/Socket; <- v2:Ljava/net/So
+ cket;
+ ViewDebug.java:593@009a: if-eqz-object . <- v11:Ljava/net/Socket;
+ next 018f *
+ next 0190
+block 018f
+ pred 018e
+ ViewDebug.java:595@009d: move-object v11:Ljava/net/Socket; <- v2:Ljava/net/So
+ cket;
+ ViewDebug.java:595@009e: Rop{invoke-virtual . <- Ljava/net/Socket; call throw
+ s <any>}(java.net.Socket.close:()V catch java.io.IOException) . <- v11:Ljava/
+ net/Socket;
+ next 0191
+ next 0192 *
+block 0192
+ pred 018f
+ ViewDebug.java:598@00a1: goto . <- .
+ next 0190
+block 0190
+ pred 018e
+ pred 0192
+ pred 0193
+ @????: goto . <- .
+ next 007c
+block 0191
+ pred 018f
+ ViewDebug.java:596@00a4: Rop{move-exception Ljava/io/IOException; <- . flows}
+ v11:Ljava/io/IOException; <- .
+ ViewDebug.java:596@00a4: goto . <- .
+ next 0193
+block 0193
+ pred 0191
+ ViewDebug.java:596@00a4: move-object v10:"e"Ljava/io/IOException; <- v11:Ljav
+ a/lang/Class;=java.io.IOException
+ ViewDebug.java:597@00a6: move-object v11:Ljava/io/IOException; <- v10:Ljava/i
+ o/IOException;
+ ViewDebug.java:597@00a8: Rop{invoke-virtual . <- Ljava/io/IOException; call t
+ hrows <any>}(java.io.IOException.printStackTrace:()V catch) . <- v11:Ljava/io
+ /IOException;
+ next 0190
+block 0079
+ pred 0062
+ @????: goto . <- .
+ next 018e
diff --git a/dx/tests/098-dex-jsr-ret-throw/info.txt b/dx/tests/098-dex-jsr-ret-throw/info.txt
new file mode 100644
index 0000000..9dcd39d
--- /dev/null
+++ b/dx/tests/098-dex-jsr-ret-throw/info.txt
@@ -0,0 +1,4 @@
+The enclosed class file was generated with javac version 1.5.0_13-b05.
+It contains an example of a subroutine being exited by a "throw" instruction in
+such a way that it caused the frame merge and subroutine inliner
+algorithms to not converge. This was bug #1137450.
diff --git a/dx/tests/098-dex-jsr-ret-throw/run b/dx/tests/098-dex-jsr-ret-throw/run
new file mode 100755
index 0000000..dfc7b89
--- /dev/null
+++ b/dx/tests/098-dex-jsr-ret-throw/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+dx --debug --dump --method=run --rop-blocks 'ViewDebug$ViewServer.class'
diff --git a/dx/tests/099-dex-core-library-error/Blort.java b/dx/tests/099-dex-core-library-error/Blort.java
new file mode 100644
index 0000000..6f619d7
--- /dev/null
+++ b/dx/tests/099-dex-core-library-error/Blort.java
@@ -0,0 +1,5 @@
+package java.blort;
+
+public class Blort {
+ // This space intentionally left blank.
+}
diff --git a/dx/tests/099-dex-core-library-error/Muffins.java b/dx/tests/099-dex-core-library-error/Muffins.java
new file mode 100644
index 0000000..7ee4c4c
--- /dev/null
+++ b/dx/tests/099-dex-core-library-error/Muffins.java
@@ -0,0 +1,5 @@
+package javax.net;
+
+public class Muffins {
+ // This space intentionally left blank.
+}
diff --git a/dx/tests/099-dex-core-library-error/Zorch.java b/dx/tests/099-dex-core-library-error/Zorch.java
new file mode 100644
index 0000000..57c311f
--- /dev/null
+++ b/dx/tests/099-dex-core-library-error/Zorch.java
@@ -0,0 +1,5 @@
+package javax.zorch;
+
+public class Zorch {
+ // This space intentionally left blank.
+}
diff --git a/dx/tests/099-dex-core-library-error/expected.txt b/dx/tests/099-dex-core-library-error/expected.txt
new file mode 100644
index 0000000..d9c405b
--- /dev/null
+++ b/dx/tests/099-dex-core-library-error/expected.txt
@@ -0,0 +1,5 @@
+exit code: 1
+exit code: 1
+exit code: 0
+Found zorch.dex
+Done
diff --git a/dx/tests/099-dex-core-library-error/info.txt b/dx/tests/099-dex-core-library-error/info.txt
new file mode 100644
index 0000000..3a62267
--- /dev/null
+++ b/dx/tests/099-dex-core-library-error/info.txt
@@ -0,0 +1,3 @@
+This tests that attempts to define core classes fail and that
+an attempt to define a legal javax.* class succeeds. (Only *some*
+javax packages are considered to be off-limits.)
diff --git a/dx/tests/099-dex-core-library-error/run b/dx/tests/099-dex-core-library-error/run
new file mode 100644
index 0000000..f063266
--- /dev/null
+++ b/dx/tests/099-dex-core-library-error/run
@@ -0,0 +1,37 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . *.java
+
+dx --debug --dex --output=blort.dex java/blort/Blort.class >/dev/null 2>&1
+echo "exit code: $?"
+if [ -r blort.dex ]; then
+ echo Found blort.dex
+fi
+
+dx --debug --dex --output=muffins.dex javax/net/Muffins.class >/dev/null 2>&1
+echo "exit code: $?"
+if [ -r muffins.dex ]; then
+ echo Found muffins.dex
+fi
+
+dx --debug --dex --output=zorch.dex javax/zorch/Zorch.class >/dev/null 2>&1
+echo "exit code: $?"
+if [ -r zorch.dex ]; then
+ echo Found zorch.dex
+fi
+
+echo Done
diff --git a/dx/tests/100-local-mismatch/blort1.j b/dx/tests/100-local-mismatch/blort1.j
new file mode 100644
index 0000000..327557e
--- /dev/null
+++ b/dx/tests/100-local-mismatch/blort1.j
@@ -0,0 +1,28 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class Blort1
+.super java/lang/Object
+
+.method public static basicTypeMismatch1()V
+ .limit locals 1
+ .limit stack 1
+ .var 0 is x Ljava/lang/Object; from start to end
+ bipush 1
+ istore_0
+start:
+ nop
+end:
+ return
+.end method
diff --git a/dx/tests/100-local-mismatch/blort2.j b/dx/tests/100-local-mismatch/blort2.j
new file mode 100644
index 0000000..6fc79cc
--- /dev/null
+++ b/dx/tests/100-local-mismatch/blort2.j
@@ -0,0 +1,28 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class Blort2
+.super java/lang/Object
+
+.method public static basicTypeMismatch2()V
+ .limit locals 1
+ .limit stack 1
+ .var 0 is x I from start to end
+ aconst_null
+ astore_0
+start:
+ nop
+end:
+ return
+.end method
diff --git a/dx/tests/100-local-mismatch/blort3.j b/dx/tests/100-local-mismatch/blort3.j
new file mode 100644
index 0000000..0fdcb89
--- /dev/null
+++ b/dx/tests/100-local-mismatch/blort3.j
@@ -0,0 +1,28 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class Blort3
+.super java/lang/Object
+
+.method public static arrayMismatch1()V
+ .limit locals 1
+ .limit stack 1
+ .var 0 is x [B from start to end
+ bipush 1
+ istore_0
+start:
+ nop
+end:
+ return
+.end method
diff --git a/dx/tests/100-local-mismatch/blort4.j b/dx/tests/100-local-mismatch/blort4.j
new file mode 100644
index 0000000..1ef207d
--- /dev/null
+++ b/dx/tests/100-local-mismatch/blort4.j
@@ -0,0 +1,28 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class Blort4
+.super java/lang/Object
+
+.method public static arrayMismatch2()V
+ .limit locals 1
+ .limit stack 1
+ .var 0 is x [Ljava/lang/Object; from start to end
+ ldc "hello"
+ astore_0
+start:
+ nop
+end:
+ return
+.end method
diff --git a/dx/tests/100-local-mismatch/expected.txt b/dx/tests/100-local-mismatch/expected.txt
new file mode 100644
index 0000000..235b206
--- /dev/null
+++ b/dx/tests/100-local-mismatch/expected.txt
@@ -0,0 +1,9 @@
+TEST 1
+com.android.dx.cf.code.SimException: local variable type mismatch: attempt to set or access a value of type int using a local variable of type java.lang.Object. This is symptomatic of .class transformation tools that ignore local variable information.
+TEST 2
+com.android.dx.cf.code.SimException: local variable type mismatch: attempt to set or access a value of type java.lang.Object using a local variable of type int. This is symptomatic of .class transformation tools that ignore local variable information.
+TEST 3
+com.android.dx.cf.code.SimException: local variable type mismatch: attempt to set or access a value of type int using a local variable of type byte[]. This is symptomatic of .class transformation tools that ignore local variable information.
+TEST 4
+com.android.dx.cf.code.SimException: local variable type mismatch: attempt to set or access a value of type java.lang.String using a local variable of type java.lang.Object[]. This is symptomatic of .class transformation tools that ignore local variable information.
+DONE
diff --git a/dx/tests/100-local-mismatch/info.txt b/dx/tests/100-local-mismatch/info.txt
new file mode 100644
index 0000000..89b6e10
--- /dev/null
+++ b/dx/tests/100-local-mismatch/info.txt
@@ -0,0 +1,3 @@
+This is a smoke test that makes sure that dx complains when a local
+variable table entry fundamentally disagrees with an instruction that
+accesses that local.
diff --git a/dx/tests/100-local-mismatch/run b/dx/tests/100-local-mismatch/run
new file mode 100644
index 0000000..fbcf1ed
--- /dev/null
+++ b/dx/tests/100-local-mismatch/run
@@ -0,0 +1,34 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+jasmin -d . blort1.j >/dev/null
+jasmin -d . blort2.j >/dev/null
+jasmin -d . blort3.j >/dev/null
+jasmin -d . blort4.j >/dev/null
+
+echo "TEST 1"
+dx --dex Blort1.class 2>&1 | grep mismatch
+
+echo "TEST 2"
+dx --dex Blort2.class 2>&1 | grep mismatch
+
+echo "TEST 3"
+dx --dex Blort3.class 2>&1 | grep mismatch
+
+echo "TEST 4"
+dx --dex Blort4.class 2>&1 | grep mismatch
+
+echo "DONE"
diff --git a/dx/tests/101-verify-wide-math/expected.txt b/dx/tests/101-verify-wide-math/expected.txt
new file mode 100644
index 0000000..4bd352d
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/expected.txt
@@ -0,0 +1,54 @@
+Generated: ./op_d2f.class
+d2f: expected failure occurred
+Generated: ./op_d2i.class
+d2i: expected failure occurred
+Generated: ./op_d2l.class
+d2l: expected failure occurred
+Generated: ./op_dadd.class
+dadd: expected failure occurred
+Generated: ./op_dcmpg.class
+dcmpg: expected failure occurred
+Generated: ./op_dcmpl.class
+dcmpl: expected failure occurred
+Generated: ./op_ddiv.class
+ddiv: expected failure occurred
+Generated: ./op_dmul.class
+dmul: expected failure occurred
+Generated: ./op_dneg.class
+dneg: expected failure occurred
+Generated: ./op_drem.class
+drem: expected failure occurred
+Generated: ./op_dsub.class
+dsub: expected failure occurred
+Generated: ./op_l2d.class
+l2d: expected failure occurred
+Generated: ./op_l2f.class
+l2f: expected failure occurred
+Generated: ./op_l2i.class
+l2i: expected failure occurred
+Generated: ./op_ladd.class
+ladd: expected failure occurred
+Generated: ./op_land.class
+land: expected failure occurred
+Generated: ./op_lcmp.class
+lcmp: expected failure occurred
+Generated: ./op_ldiv.class
+ldiv: expected failure occurred
+Generated: ./op_lmul.class
+lmul: expected failure occurred
+Generated: ./op_lneg.class
+lneg: expected failure occurred
+Generated: ./op_lor.class
+lor: expected failure occurred
+Generated: ./op_lrem.class
+lrem: expected failure occurred
+Generated: ./op_lshl.class
+lshl: expected failure occurred
+Generated: ./op_lshr.class
+lshr: expected failure occurred
+Generated: ./op_lsub.class
+lsub: expected failure occurred
+Generated: ./op_lushr.class
+lushr: expected failure occurred
+Generated: ./op_lxor.class
+lxor: expected failure occurred
diff --git a/dx/tests/101-verify-wide-math/info.txt b/dx/tests/101-verify-wide-math/info.txt
new file mode 100644
index 0000000..6ec551d
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/info.txt
@@ -0,0 +1,3 @@
+This tests that wide-taking (category-2) "calculation" opcodes (math
+ops, comparisons, etc.) verify that their arguments are actually of
+the appropriate types.
diff --git a/dx/tests/101-verify-wide-math/op_d2f.j b/dx/tests/101-verify-wide-math/op_d2f.j
new file mode 100644
index 0000000..65a3c9d
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_d2f.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_d2f
+.super java/lang/Object
+
+.method public static test(II)F
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ d2f
+ freturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_d2i.j b/dx/tests/101-verify-wide-math/op_d2i.j
new file mode 100644
index 0000000..6e8976c
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_d2i.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_d2i
+.super java/lang/Object
+
+.method public static test(II)I
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ d2i
+ ireturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_d2l.j b/dx/tests/101-verify-wide-math/op_d2l.j
new file mode 100644
index 0000000..f8e24c9
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_d2l.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_d2l
+.super java/lang/Object
+
+.method public static test(II)J
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ d2l
+ lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_dadd.j b/dx/tests/101-verify-wide-math/op_dadd.j
new file mode 100644
index 0000000..232c541
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_dadd.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dadd
+.super java/lang/Object
+
+.method public static test(II)D
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ iload_1
+ dadd
+ dreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_dcmpg.j b/dx/tests/101-verify-wide-math/op_dcmpg.j
new file mode 100644
index 0000000..cd1b151
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_dcmpg.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dcmpg
+.super java/lang/Object
+
+.method public static test(II)I
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ iload_1
+ dcmpg
+ ireturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_dcmpl.j b/dx/tests/101-verify-wide-math/op_dcmpl.j
new file mode 100644
index 0000000..dd54c52
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_dcmpl.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dcmpl
+.super java/lang/Object
+
+.method public static test(II)I
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ iload_1
+ dcmpl
+ ireturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_ddiv.j b/dx/tests/101-verify-wide-math/op_ddiv.j
new file mode 100644
index 0000000..b9ee329
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_ddiv.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_ddiv
+.super java/lang/Object
+
+.method public static test(II)D
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ iload_1
+ ddiv
+ dreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_dmul.j b/dx/tests/101-verify-wide-math/op_dmul.j
new file mode 100644
index 0000000..f915e79
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_dmul.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dmul
+.super java/lang/Object
+
+.method public static test(II)D
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ iload_1
+ dmul
+ dreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_dneg.j b/dx/tests/101-verify-wide-math/op_dneg.j
new file mode 100644
index 0000000..98fd9df
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_dneg.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dneg
+.super java/lang/Object
+
+.method public static test(II)D
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ dneg
+ dreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_drem.j b/dx/tests/101-verify-wide-math/op_drem.j
new file mode 100644
index 0000000..c0fca65
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_drem.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_drem
+.super java/lang/Object
+
+.method public static test(II)D
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ iload_1
+ drem
+ dreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_dsub.j b/dx/tests/101-verify-wide-math/op_dsub.j
new file mode 100644
index 0000000..e04f505
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_dsub.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dsub
+.super java/lang/Object
+
+.method public static test(II)D
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ iload_1
+ dsub
+ dreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_l2d.j b/dx/tests/101-verify-wide-math/op_l2d.j
new file mode 100644
index 0000000..d4ac0a8
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_l2d.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_l2d
+.super java/lang/Object
+
+.method public static test(I)D
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ l2d
+ dreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_l2f.j b/dx/tests/101-verify-wide-math/op_l2f.j
new file mode 100644
index 0000000..2dbe9d2
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_l2f.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_l2f
+.super java/lang/Object
+
+.method public static test(I)F
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ l2f
+ freturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_l2i.j b/dx/tests/101-verify-wide-math/op_l2i.j
new file mode 100644
index 0000000..1b4e68a
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_l2i.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_l2i
+.super java/lang/Object
+
+.method public static test(I)I
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ l2i
+ ireturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_ladd.j b/dx/tests/101-verify-wide-math/op_ladd.j
new file mode 100644
index 0000000..1dbb6f8
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_ladd.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_ladd
+.super java/lang/Object
+
+.method public static test(II)J
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ iload_1
+ ladd
+ lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_land.j b/dx/tests/101-verify-wide-math/op_land.j
new file mode 100644
index 0000000..e8a55bb
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_land.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_land
+.super java/lang/Object
+
+.method public static test(II)J
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ iload_1
+ land
+ lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_lcmp.j b/dx/tests/101-verify-wide-math/op_lcmp.j
new file mode 100644
index 0000000..b651c9c
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_lcmp.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_lcmp
+.super java/lang/Object
+
+.method public static test(II)I
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ iload_1
+ lcmp
+ lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_ldiv.j b/dx/tests/101-verify-wide-math/op_ldiv.j
new file mode 100644
index 0000000..677daa2
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_ldiv.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_ldiv
+.super java/lang/Object
+
+.method public static test(II)J
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ iload_1
+ ldiv
+ lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_lmul.j b/dx/tests/101-verify-wide-math/op_lmul.j
new file mode 100644
index 0000000..074d67c
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_lmul.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_lmul
+.super java/lang/Object
+
+.method public static test(II)J
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ iload_1
+ lmul
+ lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_lneg.j b/dx/tests/101-verify-wide-math/op_lneg.j
new file mode 100644
index 0000000..18d5780
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_lneg.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_lneg
+.super java/lang/Object
+
+.method public static test(I)J
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ lneg
+ lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_lor.j b/dx/tests/101-verify-wide-math/op_lor.j
new file mode 100644
index 0000000..267ff1f
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_lor.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_lor
+.super java/lang/Object
+
+.method public static test(II)J
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ iload_1
+ lor
+ lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_lrem.j b/dx/tests/101-verify-wide-math/op_lrem.j
new file mode 100644
index 0000000..5e0df6e
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_lrem.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_lrem
+.super java/lang/Object
+
+.method public static test(II)J
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ iload_1
+ lrem
+ lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_lshl.j b/dx/tests/101-verify-wide-math/op_lshl.j
new file mode 100644
index 0000000..bc16ea5
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_lshl.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_lshl
+.super java/lang/Object
+
+.method public static test(II)J
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ iload_1
+ lshl
+ lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_lshr.j b/dx/tests/101-verify-wide-math/op_lshr.j
new file mode 100644
index 0000000..b93fb2f
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_lshr.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_lshr
+.super java/lang/Object
+
+.method public static test(II)J
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ iload_1
+ lshr
+ lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_lsub.j b/dx/tests/101-verify-wide-math/op_lsub.j
new file mode 100644
index 0000000..823d899
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_lsub.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_lsub
+.super java/lang/Object
+
+.method public static test(II)J
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ iload_1
+ lsub
+ lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_lushr.j b/dx/tests/101-verify-wide-math/op_lushr.j
new file mode 100644
index 0000000..aa9feb2
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_lushr.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_lushr
+.super java/lang/Object
+
+.method public static test(II)J
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ iload_1
+ lushr
+ lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/op_lxor.j b/dx/tests/101-verify-wide-math/op_lxor.j
new file mode 100644
index 0000000..3897c96
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/op_lxor.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_lxor
+.super java/lang/Object
+
+.method public static test(II)J
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ iload_1
+ lxor
+ lreturn
+.end method
diff --git a/dx/tests/101-verify-wide-math/run b/dx/tests/101-verify-wide-math/run
new file mode 100644
index 0000000..a5ecd58
--- /dev/null
+++ b/dx/tests/101-verify-wide-math/run
@@ -0,0 +1,54 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+function oneop()
+{
+ jasmin -d . op_"$1".j
+ dx --debug --dex op_"$1".class >/dev/null 2>&1
+ if [ "$?" = "0" ]; then
+ dx --debug --dex --dump-method="op_$1.test*" op_"$1".class
+ else
+ echo "$1: expected failure occurred"
+ fi
+}
+
+oneop d2f
+oneop d2i
+oneop d2l
+oneop dadd
+oneop dcmpg
+oneop dcmpl
+oneop ddiv
+oneop dmul
+oneop dneg
+oneop drem
+oneop dsub
+oneop l2d
+oneop l2f
+oneop l2i
+oneop ladd
+oneop land
+oneop lcmp
+oneop ldiv
+oneop lmul
+oneop lneg
+oneop lor
+oneop lrem
+oneop lshl
+oneop lshr
+oneop lsub
+oneop lushr
+oneop lxor
diff --git a/dx/tests/102-verify-nonwide-math/expected.txt b/dx/tests/102-verify-nonwide-math/expected.txt
new file mode 100644
index 0000000..3f857b1
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/expected.txt
@@ -0,0 +1,48 @@
+Generated: ./op_f2d.class
+f2d: expected failure occurred
+Generated: ./op_f2i.class
+f2i: expected failure occurred
+Generated: ./op_f2l.class
+f2l: expected failure occurred
+Generated: ./op_fadd.class
+fadd: expected failure occurred
+Generated: ./op_fdiv.class
+fdiv: expected failure occurred
+Generated: ./op_fmul.class
+fmul: expected failure occurred
+Generated: ./op_fneg.class
+fneg: expected failure occurred
+Generated: ./op_frem.class
+frem: expected failure occurred
+Generated: ./op_fsub.class
+fsub: expected failure occurred
+Generated: ./op_i2d.class
+i2d: expected failure occurred
+Generated: ./op_i2f.class
+i2f: expected failure occurred
+Generated: ./op_i2l.class
+i2l: expected failure occurred
+Generated: ./op_iadd.class
+iadd: expected failure occurred
+Generated: ./op_iand.class
+iand: expected failure occurred
+Generated: ./op_idiv.class
+idiv: expected failure occurred
+Generated: ./op_imul.class
+imul: expected failure occurred
+Generated: ./op_ineg.class
+ineg: expected failure occurred
+Generated: ./op_ior.class
+ior: expected failure occurred
+Generated: ./op_irem.class
+irem: expected failure occurred
+Generated: ./op_ishl.class
+ishl: expected failure occurred
+Generated: ./op_ishr.class
+ishr: expected failure occurred
+Generated: ./op_isub.class
+isub: expected failure occurred
+Generated: ./op_iushr.class
+iushr: expected failure occurred
+Generated: ./op_ixor.class
+ixor: expected failure occurred
diff --git a/dx/tests/102-verify-nonwide-math/info.txt b/dx/tests/102-verify-nonwide-math/info.txt
new file mode 100644
index 0000000..10e52ba
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/info.txt
@@ -0,0 +1,3 @@
+This tests that non-wide-taking (category-1) "calculation" opcodes (math
+ops, comparisons, etc.) to verify that their arguments are actually of
+the appropriate types.
diff --git a/dx/tests/102-verify-nonwide-math/op_f2d.j b/dx/tests/102-verify-nonwide-math/op_f2d.j
new file mode 100644
index 0000000..75e8917
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_f2d.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_f2d
+.super java/lang/Object
+
+.method public static test(I)D
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ f2d
+ dreturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_f2i.j b/dx/tests/102-verify-nonwide-math/op_f2i.j
new file mode 100644
index 0000000..2d36af7
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_f2i.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_f2i
+.super java/lang/Object
+
+.method public static test(I)I
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ f2i
+ ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_f2l.j b/dx/tests/102-verify-nonwide-math/op_f2l.j
new file mode 100644
index 0000000..fae9e21
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_f2l.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_f2l
+.super java/lang/Object
+
+.method public static test(I)J
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ f2l
+ lreturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_fadd.j b/dx/tests/102-verify-nonwide-math/op_fadd.j
new file mode 100644
index 0000000..dc3743f
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_fadd.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_fadd
+.super java/lang/Object
+
+.method public static test(II)F
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ iload_1
+ fadd
+ freturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_fdiv.j b/dx/tests/102-verify-nonwide-math/op_fdiv.j
new file mode 100644
index 0000000..8609be2
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_fdiv.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_fdiv
+.super java/lang/Object
+
+.method public static test(II)F
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ iload_1
+ fdiv
+ freturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_fmul.j b/dx/tests/102-verify-nonwide-math/op_fmul.j
new file mode 100644
index 0000000..fe4661c
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_fmul.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_fmul
+.super java/lang/Object
+
+.method public static test(II)F
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ iload_1
+ fmul
+ freturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_fneg.j b/dx/tests/102-verify-nonwide-math/op_fneg.j
new file mode 100644
index 0000000..34898bd
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_fneg.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_fneg
+.super java/lang/Object
+
+.method public static test(II)F
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ fneg
+ freturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_frem.j b/dx/tests/102-verify-nonwide-math/op_frem.j
new file mode 100644
index 0000000..17f4602
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_frem.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_frem
+.super java/lang/Object
+
+.method public static test(II)F
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ iload_1
+ frem
+ freturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_fsub.j b/dx/tests/102-verify-nonwide-math/op_fsub.j
new file mode 100644
index 0000000..692f4f8
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_fsub.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_fsub
+.super java/lang/Object
+
+.method public static test(II)F
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ iload_1
+ fsub
+ freturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_i2d.j b/dx/tests/102-verify-nonwide-math/op_i2d.j
new file mode 100644
index 0000000..6c73dbe
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_i2d.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_i2d
+.super java/lang/Object
+
+.method public static test(FF)D
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ i2d
+ dreturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_i2f.j b/dx/tests/102-verify-nonwide-math/op_i2f.j
new file mode 100644
index 0000000..cee0b84
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_i2f.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_i2f
+.super java/lang/Object
+
+.method public static test(FF)F
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ i2f
+ freturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_i2l.j b/dx/tests/102-verify-nonwide-math/op_i2l.j
new file mode 100644
index 0000000..d6f2daa
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_i2l.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_i2l
+.super java/lang/Object
+
+.method public static test(FF)J
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ i2l
+ lreturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_iadd.j b/dx/tests/102-verify-nonwide-math/op_iadd.j
new file mode 100644
index 0000000..e3d92e3
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_iadd.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_iadd
+.super java/lang/Object
+
+.method public static test(FF)I
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ fload_1
+ iadd
+ ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_iand.j b/dx/tests/102-verify-nonwide-math/op_iand.j
new file mode 100644
index 0000000..063738c
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_iand.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_iand
+.super java/lang/Object
+
+.method public static test(FF)I
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ fload_1
+ iand
+ ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_idiv.j b/dx/tests/102-verify-nonwide-math/op_idiv.j
new file mode 100644
index 0000000..2e3e3a3
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_idiv.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_idiv
+.super java/lang/Object
+
+.method public static test(FF)I
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ fload_1
+ idiv
+ ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_imul.j b/dx/tests/102-verify-nonwide-math/op_imul.j
new file mode 100644
index 0000000..7f4f612
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_imul.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_imul
+.super java/lang/Object
+
+.method public static test(FF)I
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ fload_1
+ imul
+ ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_ineg.j b/dx/tests/102-verify-nonwide-math/op_ineg.j
new file mode 100644
index 0000000..68fcb8c
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_ineg.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_ineg
+.super java/lang/Object
+
+.method public static test(FF)I
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ ineg
+ ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_ior.j b/dx/tests/102-verify-nonwide-math/op_ior.j
new file mode 100644
index 0000000..c8c3a4b
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_ior.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_ior
+.super java/lang/Object
+
+.method public static test(FF)I
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ fload_1
+ ior
+ ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_irem.j b/dx/tests/102-verify-nonwide-math/op_irem.j
new file mode 100644
index 0000000..b22f1fd
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_irem.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_irem
+.super java/lang/Object
+
+.method public static test(FF)I
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ fload_1
+ irem
+ ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_ishl.j b/dx/tests/102-verify-nonwide-math/op_ishl.j
new file mode 100644
index 0000000..e617182
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_ishl.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_ishl
+.super java/lang/Object
+
+.method public static test(FF)I
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ fload_1
+ ishl
+ ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_ishr.j b/dx/tests/102-verify-nonwide-math/op_ishr.j
new file mode 100644
index 0000000..64eba11
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_ishr.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_ishr
+.super java/lang/Object
+
+.method public static test(FF)I
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ fload_1
+ ishr
+ ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_isub.j b/dx/tests/102-verify-nonwide-math/op_isub.j
new file mode 100644
index 0000000..ee789b0
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_isub.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_isub
+.super java/lang/Object
+
+.method public static test(FF)I
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ fload_1
+ isub
+ ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_iushr.j b/dx/tests/102-verify-nonwide-math/op_iushr.j
new file mode 100644
index 0000000..73c34f1
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_iushr.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_iushr
+.super java/lang/Object
+
+.method public static test(FF)I
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ fload_1
+ iushr
+ ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/op_ixor.j b/dx/tests/102-verify-nonwide-math/op_ixor.j
new file mode 100644
index 0000000..68f095b
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/op_ixor.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_ixor
+.super java/lang/Object
+
+.method public static test(FF)I
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ fload_1
+ ixor
+ ireturn
+.end method
diff --git a/dx/tests/102-verify-nonwide-math/run b/dx/tests/102-verify-nonwide-math/run
new file mode 100644
index 0000000..eb4a294
--- /dev/null
+++ b/dx/tests/102-verify-nonwide-math/run
@@ -0,0 +1,51 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+function oneop()
+{
+ jasmin -d . op_"$1".j
+ dx --debug --dex op_"$1".class >/dev/null 2>&1
+ if [ "$?" = "0" ]; then
+ dx --debug --dex --dump-method="op_$1.test*" op_"$1".class
+ else
+ echo "$1: expected failure occurred"
+ fi
+}
+
+oneop f2d
+oneop f2i
+oneop f2l
+oneop fadd
+oneop fdiv
+oneop fmul
+oneop fneg
+oneop frem
+oneop fsub
+oneop i2d
+oneop i2f
+oneop i2l
+oneop iadd
+oneop iand
+oneop idiv
+oneop imul
+oneop ineg
+oneop ior
+oneop irem
+oneop ishl
+oneop ishr
+oneop isub
+oneop iushr
+oneop ixor
diff --git a/dx/tests/103-verify-branch-ops/expected.txt b/dx/tests/103-verify-branch-ops/expected.txt
new file mode 100644
index 0000000..2c96704
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/expected.txt
@@ -0,0 +1,36 @@
+Generated: ./op_if_acmpeq.class
+if_acmpeq: expected failure occurred
+Generated: ./op_if_acmpne.class
+if_acmpne: expected failure occurred
+Generated: ./op_if_icmpeq.class
+if_icmpeq: expected failure occurred
+Generated: ./op_if_icmpge.class
+if_icmpge: expected failure occurred
+Generated: ./op_if_icmpgt.class
+if_icmpgt: expected failure occurred
+Generated: ./op_if_icmple.class
+if_icmple: expected failure occurred
+Generated: ./op_if_icmplt.class
+if_icmplt: expected failure occurred
+Generated: ./op_if_icmpne.class
+if_icmpne: expected failure occurred
+Generated: ./op_ifeq.class
+ifeq: expected failure occurred
+Generated: ./op_ifge.class
+ifge: expected failure occurred
+Generated: ./op_ifgt.class
+ifgt: expected failure occurred
+Generated: ./op_ifle.class
+ifle: expected failure occurred
+Generated: ./op_iflt.class
+iflt: expected failure occurred
+Generated: ./op_ifne.class
+ifne: expected failure occurred
+Generated: ./op_ifnonnull.class
+ifnonnull: expected failure occurred
+Generated: ./op_ifnull.class
+ifnull: expected failure occurred
+Generated: ./op_lookupswitch.class
+lookupswitch: expected failure occurred
+Generated: ./op_tableswitch.class
+tableswitch: expected failure occurred
diff --git a/dx/tests/103-verify-branch-ops/info.txt b/dx/tests/103-verify-branch-ops/info.txt
new file mode 100644
index 0000000..8705e83
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/info.txt
@@ -0,0 +1,2 @@
+This tests branch opcodes to verify that their arguments are actually of
+the appropriate types.
diff --git a/dx/tests/103-verify-branch-ops/op_if_acmpeq.j b/dx/tests/103-verify-branch-ops/op_if_acmpeq.j
new file mode 100644
index 0000000..4339200
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_if_acmpeq.j
@@ -0,0 +1,28 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_if_acmpeq
+.super java/lang/Object
+
+.method public static test(FF)V
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ fload_1
+ if_acmpeq blort
+ nop
+blort:
+ return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_if_acmpne.j b/dx/tests/103-verify-branch-ops/op_if_acmpne.j
new file mode 100644
index 0000000..57a317b
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_if_acmpne.j
@@ -0,0 +1,28 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_if_acmpne
+.super java/lang/Object
+
+.method public static test(FF)V
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ fload_1
+ if_acmpne blort
+ nop
+blort:
+ return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_if_icmpeq.j b/dx/tests/103-verify-branch-ops/op_if_icmpeq.j
new file mode 100644
index 0000000..1f141bc
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_if_icmpeq.j
@@ -0,0 +1,28 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_if_icmpeq
+.super java/lang/Object
+
+.method public static test(FF)V
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ fload_1
+ if_icmpeq blort
+ nop
+blort:
+ return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_if_icmpge.j b/dx/tests/103-verify-branch-ops/op_if_icmpge.j
new file mode 100644
index 0000000..6ae2e3f
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_if_icmpge.j
@@ -0,0 +1,28 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_if_icmpge
+.super java/lang/Object
+
+.method public static test(FF)V
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ fload_1
+ if_icmpge blort
+ nop
+blort:
+ return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_if_icmpgt.j b/dx/tests/103-verify-branch-ops/op_if_icmpgt.j
new file mode 100644
index 0000000..4e67282
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_if_icmpgt.j
@@ -0,0 +1,28 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_if_icmpgt
+.super java/lang/Object
+
+.method public static test(FF)V
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ fload_1
+ if_icmpgt blort
+ nop
+blort:
+ return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_if_icmple.j b/dx/tests/103-verify-branch-ops/op_if_icmple.j
new file mode 100644
index 0000000..3511800
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_if_icmple.j
@@ -0,0 +1,28 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_if_icmple
+.super java/lang/Object
+
+.method public static test(FF)V
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ fload_1
+ if_icmple blort
+ nop
+blort:
+ return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_if_icmplt.j b/dx/tests/103-verify-branch-ops/op_if_icmplt.j
new file mode 100644
index 0000000..89527f5
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_if_icmplt.j
@@ -0,0 +1,28 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_if_icmplt
+.super java/lang/Object
+
+.method public static test(FF)V
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ fload_1
+ if_icmplt blort
+ nop
+blort:
+ return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_if_icmpne.j b/dx/tests/103-verify-branch-ops/op_if_icmpne.j
new file mode 100644
index 0000000..a94faee
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_if_icmpne.j
@@ -0,0 +1,28 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_if_icmpne
+.super java/lang/Object
+
+.method public static test(FF)V
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ fload_1
+ if_icmpne blort
+ nop
+blort:
+ return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_ifeq.j b/dx/tests/103-verify-branch-ops/op_ifeq.j
new file mode 100644
index 0000000..620e1c9
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_ifeq.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_ifeq
+.super java/lang/Object
+
+.method public static test(FF)V
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ ifeq blort
+ nop
+blort:
+ return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_ifge.j b/dx/tests/103-verify-branch-ops/op_ifge.j
new file mode 100644
index 0000000..c46b176
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_ifge.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_ifge
+.super java/lang/Object
+
+.method public static test(FF)V
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ ifge blort
+ nop
+blort:
+ return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_ifgt.j b/dx/tests/103-verify-branch-ops/op_ifgt.j
new file mode 100644
index 0000000..8165038
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_ifgt.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_ifgt
+.super java/lang/Object
+
+.method public static test(FF)V
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ ifgt blort
+ nop
+blort:
+ return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_ifle.j b/dx/tests/103-verify-branch-ops/op_ifle.j
new file mode 100644
index 0000000..758943f
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_ifle.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_ifle
+.super java/lang/Object
+
+.method public static test(FF)V
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ ifle blort
+ nop
+blort:
+ return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_iflt.j b/dx/tests/103-verify-branch-ops/op_iflt.j
new file mode 100644
index 0000000..5091355
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_iflt.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_iflt
+.super java/lang/Object
+
+.method public static test(FF)V
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ iflt blort
+ nop
+blort:
+ return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_ifne.j b/dx/tests/103-verify-branch-ops/op_ifne.j
new file mode 100644
index 0000000..bb2dadc
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_ifne.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_ifne
+.super java/lang/Object
+
+.method public static test(FF)V
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ ifne blort
+ nop
+blort:
+ return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_ifnonnull.j b/dx/tests/103-verify-branch-ops/op_ifnonnull.j
new file mode 100644
index 0000000..f2422f0
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_ifnonnull.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_ifnonnull
+.super java/lang/Object
+
+.method public static test(IF)V
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ ifnonnull blort
+ nop
+blort:
+ return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_ifnull.j b/dx/tests/103-verify-branch-ops/op_ifnull.j
new file mode 100644
index 0000000..c253b09
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_ifnull.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_ifnull
+.super java/lang/Object
+
+.method public static test(FF)V
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ ifnull blort
+ nop
+blort:
+ return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_lookupswitch.j b/dx/tests/103-verify-branch-ops/op_lookupswitch.j
new file mode 100644
index 0000000..21fe8f7
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_lookupswitch.j
@@ -0,0 +1,33 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_lookupswitch
+.super java/lang/Object
+
+.method public static test(FF)V
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ lookupswitch
+ 0x05: t1
+ 0x10: t2
+ default: t3
+t1:
+ nop
+t2:
+ nop
+t3:
+ return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/op_tableswitch.j b/dx/tests/103-verify-branch-ops/op_tableswitch.j
new file mode 100644
index 0000000..657feff
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/op_tableswitch.j
@@ -0,0 +1,33 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_tableswitch
+.super java/lang/Object
+
+.method public static test(FF)V
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ tableswitch 0x10
+ t1
+ t2
+ default: t3
+t1:
+ nop
+t2:
+ nop
+t3:
+ return
+.end method
diff --git a/dx/tests/103-verify-branch-ops/run b/dx/tests/103-verify-branch-ops/run
new file mode 100644
index 0000000..c9ca89f
--- /dev/null
+++ b/dx/tests/103-verify-branch-ops/run
@@ -0,0 +1,45 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+function oneop()
+{
+ jasmin -d . op_"$1".j
+ dx --debug --dex op_"$1".class >/dev/null 2>&1
+ if [ "$?" = "0" ]; then
+ dx --debug --dex --dump-method="op_$1.test*" op_"$1".class
+ else
+ echo "$1: expected failure occurred"
+ fi
+}
+
+oneop if_acmpeq
+oneop if_acmpne
+oneop if_icmpeq
+oneop if_icmpge
+oneop if_icmpgt
+oneop if_icmple
+oneop if_icmplt
+oneop if_icmpne
+oneop ifeq
+oneop ifge
+oneop ifgt
+oneop ifle
+oneop iflt
+oneop ifne
+oneop ifnonnull
+oneop ifnull
+oneop lookupswitch
+oneop tableswitch
diff --git a/dx/tests/104-verify-return-ops/expected.txt b/dx/tests/104-verify-return-ops/expected.txt
new file mode 100644
index 0000000..5bd9dfd
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/expected.txt
@@ -0,0 +1,22 @@
+Generated: ./op_areturn.class
+areturn: expected failure occurred
+Generated: ./op_dreturn.class
+dreturn: expected failure occurred
+Generated: ./op_freturn.class
+freturn: expected failure occurred
+Generated: ./op_ireturn.class
+ireturn: expected failure occurred
+Generated: ./op_lreturn.class
+lreturn: expected failure occurred
+Generated: ./op_sig_areturn.class
+sig_areturn: expected failure occurred
+Generated: ./op_sig_dreturn.class
+sig_dreturn: expected failure occurred
+Generated: ./op_sig_freturn.class
+sig_freturn: expected failure occurred
+Generated: ./op_sig_ireturn.class
+sig_ireturn: expected failure occurred
+Generated: ./op_sig_lreturn.class
+sig_lreturn: expected failure occurred
+Generated: ./op_sig_return.class
+sig_return: expected failure occurred
diff --git a/dx/tests/104-verify-return-ops/info.txt b/dx/tests/104-verify-return-ops/info.txt
new file mode 100644
index 0000000..1f5e634
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/info.txt
@@ -0,0 +1,2 @@
+This tests that return opcodes verify that their arguments are actually of
+the appropriate types and that the opcode matches the method signature.
diff --git a/dx/tests/104-verify-return-ops/op_areturn.j b/dx/tests/104-verify-return-ops/op_areturn.j
new file mode 100644
index 0000000..0b25088
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/op_areturn.j
@@ -0,0 +1,24 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_areturn
+.super java/lang/Object
+
+.method public static test(F)Ljava/lang/Object;
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ areturn
+.end method
diff --git a/dx/tests/104-verify-return-ops/op_dreturn.j b/dx/tests/104-verify-return-ops/op_dreturn.j
new file mode 100644
index 0000000..1075fe1
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/op_dreturn.j
@@ -0,0 +1,24 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dreturn
+.super java/lang/Object
+
+.method public static test(F)D
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ dreturn
+.end method
diff --git a/dx/tests/104-verify-return-ops/op_freturn.j b/dx/tests/104-verify-return-ops/op_freturn.j
new file mode 100644
index 0000000..6586ce6
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/op_freturn.j
@@ -0,0 +1,24 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_freturn
+.super java/lang/Object
+
+.method public static test(I)F
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ freturn
+.end method
diff --git a/dx/tests/104-verify-return-ops/op_ireturn.j b/dx/tests/104-verify-return-ops/op_ireturn.j
new file mode 100644
index 0000000..e93dda8
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/op_ireturn.j
@@ -0,0 +1,24 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_ireturn
+.super java/lang/Object
+
+.method public static test(F)I
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ ireturn
+.end method
diff --git a/dx/tests/104-verify-return-ops/op_lreturn.j b/dx/tests/104-verify-return-ops/op_lreturn.j
new file mode 100644
index 0000000..349f353
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/op_lreturn.j
@@ -0,0 +1,24 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_lreturn
+.super java/lang/Object
+
+.method public static test(F)J
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ lreturn
+.end method
diff --git a/dx/tests/104-verify-return-ops/op_sig_areturn.j b/dx/tests/104-verify-return-ops/op_sig_areturn.j
new file mode 100644
index 0000000..f1ea1b4
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/op_sig_areturn.j
@@ -0,0 +1,24 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_sig_areturn
+.super java/lang/Object
+
+.method public static test(I)F
+ .limit locals 2
+ .limit stack 3
+
+ aconst_null
+ areturn
+.end method
diff --git a/dx/tests/104-verify-return-ops/op_sig_dreturn.j b/dx/tests/104-verify-return-ops/op_sig_dreturn.j
new file mode 100644
index 0000000..fa6fcd2
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/op_sig_dreturn.j
@@ -0,0 +1,24 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_sig_dreturn
+.super java/lang/Object
+
+.method public static test(D)F
+ .limit locals 2
+ .limit stack 3
+
+ dload_0
+ dreturn
+.end method
diff --git a/dx/tests/104-verify-return-ops/op_sig_freturn.j b/dx/tests/104-verify-return-ops/op_sig_freturn.j
new file mode 100644
index 0000000..be5dea8
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/op_sig_freturn.j
@@ -0,0 +1,24 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_sig_freturn
+.super java/lang/Object
+
+.method public static test(F)I
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ freturn
+.end method
diff --git a/dx/tests/104-verify-return-ops/op_sig_ireturn.j b/dx/tests/104-verify-return-ops/op_sig_ireturn.j
new file mode 100644
index 0000000..ab3aa40
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/op_sig_ireturn.j
@@ -0,0 +1,24 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_sig_ireturn
+.super java/lang/Object
+
+.method public static test(I)F
+ .limit locals 2
+ .limit stack 3
+
+ iload_0
+ ireturn
+.end method
diff --git a/dx/tests/104-verify-return-ops/op_sig_lreturn.j b/dx/tests/104-verify-return-ops/op_sig_lreturn.j
new file mode 100644
index 0000000..e5a121d
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/op_sig_lreturn.j
@@ -0,0 +1,24 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_sig_lreturn
+.super java/lang/Object
+
+.method public static test(J)F
+ .limit locals 2
+ .limit stack 3
+
+ lload_0
+ lreturn
+.end method
diff --git a/dx/tests/104-verify-return-ops/op_sig_return.j b/dx/tests/104-verify-return-ops/op_sig_return.j
new file mode 100644
index 0000000..26c0678
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/op_sig_return.j
@@ -0,0 +1,23 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_sig_return
+.super java/lang/Object
+
+.method public static test(I)F
+ .limit locals 2
+ .limit stack 3
+
+ return
+.end method
diff --git a/dx/tests/104-verify-return-ops/run b/dx/tests/104-verify-return-ops/run
new file mode 100644
index 0000000..fbce332
--- /dev/null
+++ b/dx/tests/104-verify-return-ops/run
@@ -0,0 +1,38 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+function oneop()
+{
+ jasmin -d . op_"$1".j
+ dx --debug --dex op_"$1".class >/dev/null 2>&1
+ if [ "$?" = "0" ]; then
+ dx --debug --dex --dump-method="op_$1.test*" op_"$1".class
+ else
+ echo "$1: expected failure occurred"
+ fi
+}
+
+oneop areturn
+oneop dreturn
+oneop freturn
+oneop ireturn
+oneop lreturn
+oneop sig_areturn
+oneop sig_dreturn
+oneop sig_freturn
+oneop sig_ireturn
+oneop sig_lreturn
+oneop sig_return
diff --git a/dx/tests/105-verify-load-store-ops/expected.txt b/dx/tests/105-verify-load-store-ops/expected.txt
new file mode 100644
index 0000000..409ead0
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/expected.txt
@@ -0,0 +1,82 @@
+Generated: ./op_aaload.class
+aaload: expected failure occurred
+Generated: ./op_aastore.class
+aastore: expected failure occurred
+Generated: ./op_astore.class
+astore: expected failure occurred
+Generated: ./op_astore_0.class
+astore_0: expected failure occurred
+Generated: ./op_astore_1.class
+astore_1: expected failure occurred
+Generated: ./op_astore_2.class
+astore_2: expected failure occurred
+Generated: ./op_astore_3.class
+astore_3: expected failure occurred
+Generated: ./op_baload.class
+baload: expected failure occurred
+Generated: ./op_bastore.class
+bastore: expected failure occurred
+Generated: ./op_caload.class
+caload: expected failure occurred
+Generated: ./op_castore.class
+castore: expected failure occurred
+Generated: ./op_daload.class
+daload: expected failure occurred
+Generated: ./op_dastore.class
+dastore: expected failure occurred
+Generated: ./op_dstore.class
+dstore: expected failure occurred
+Generated: ./op_dstore_0.class
+dstore_0: expected failure occurred
+Generated: ./op_dstore_1.class
+dstore_1: expected failure occurred
+Generated: ./op_dstore_2.class
+dstore_2: expected failure occurred
+Generated: ./op_dstore_3.class
+dstore_3: expected failure occurred
+Generated: ./op_faload.class
+faload: expected failure occurred
+Generated: ./op_fastore.class
+fastore: expected failure occurred
+Generated: ./op_fstore.class
+fstore: expected failure occurred
+Generated: ./op_fstore_0.class
+fstore_0: expected failure occurred
+Generated: ./op_fstore_1.class
+fstore_1: expected failure occurred
+Generated: ./op_fstore_2.class
+fstore_2: expected failure occurred
+Generated: ./op_fstore_3.class
+fstore_3: expected failure occurred
+Generated: ./op_iaload.class
+iaload: expected failure occurred
+Generated: ./op_iastore.class
+iastore: expected failure occurred
+Generated: ./op_istore.class
+istore: expected failure occurred
+Generated: ./op_istore_0.class
+istore_0: expected failure occurred
+Generated: ./op_istore_1.class
+istore_1: expected failure occurred
+Generated: ./op_istore_2.class
+istore_2: expected failure occurred
+Generated: ./op_istore_3.class
+istore_3: expected failure occurred
+Generated: ./op_laload.class
+laload: expected failure occurred
+Generated: ./op_lastore.class
+lastore: expected failure occurred
+Generated: ./op_lstore.class
+lstore: expected failure occurred
+Generated: ./op_lstore_0.class
+lstore_0: expected failure occurred
+Generated: ./op_lstore_1.class
+lstore_1: expected failure occurred
+Generated: ./op_lstore_2.class
+lstore_2: expected failure occurred
+Generated: ./op_lstore_3.class
+lstore_3: expected failure occurred
+Generated: ./op_saload.class
+saload: expected failure occurred
+Generated: ./op_sastore.class
+sastore: expected failure occurred
diff --git a/dx/tests/105-verify-load-store-ops/info.txt b/dx/tests/105-verify-load-store-ops/info.txt
new file mode 100644
index 0000000..a3a41d6
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/info.txt
@@ -0,0 +1,2 @@
+This tests that load and store opcodes verify that their arguments are
+actually of the appropriate types.
diff --git a/dx/tests/105-verify-load-store-ops/op_aaload.j b/dx/tests/105-verify-load-store-ops/op_aaload.j
new file mode 100644
index 0000000..f77827e
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_aaload.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_aaload
+.super java/lang/Object
+
+.method public static test([F)V
+ .limit locals 2
+ .limit stack 3
+
+ aload_0
+ iconst_0
+ aaload
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_aastore.j b/dx/tests/105-verify-load-store-ops/op_aastore.j
new file mode 100644
index 0000000..58e1576
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_aastore.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_aastore
+.super java/lang/Object
+
+.method public static test([I)V
+ .limit locals 2
+ .limit stack 4
+
+ aload_0
+ iconst_0
+ aconst_null
+ aastore
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_astore.j b/dx/tests/105-verify-load-store-ops/op_astore.j
new file mode 100644
index 0000000..25131bf
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_astore.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_astore
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 6
+ .limit stack 4
+
+ fconst_0
+ astore 5
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_astore_0.j b/dx/tests/105-verify-load-store-ops/op_astore_0.j
new file mode 100644
index 0000000..b509c12
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_astore_0.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_astore_0
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 6
+ .limit stack 4
+
+ fconst_0
+ astore_0
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_astore_1.j b/dx/tests/105-verify-load-store-ops/op_astore_1.j
new file mode 100644
index 0000000..a6c1043
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_astore_1.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_astore_1
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 6
+ .limit stack 4
+
+ fconst_0
+ astore_1
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_astore_2.j b/dx/tests/105-verify-load-store-ops/op_astore_2.j
new file mode 100644
index 0000000..cb84ee8
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_astore_2.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_astore_2
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 6
+ .limit stack 4
+
+ fconst_0
+ astore_2
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_astore_3.j b/dx/tests/105-verify-load-store-ops/op_astore_3.j
new file mode 100644
index 0000000..c716ba2
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_astore_3.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_astore_3
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 6
+ .limit stack 4
+
+ fconst_0
+ astore_3
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_baload.j b/dx/tests/105-verify-load-store-ops/op_baload.j
new file mode 100644
index 0000000..cfcaf74
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_baload.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_baload
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ iconst_0
+ baload
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_bastore.j b/dx/tests/105-verify-load-store-ops/op_bastore.j
new file mode 100644
index 0000000..587fcd3
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_bastore.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_bastore
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 2
+ .limit stack 4
+
+ fload_0
+ iconst_0
+ iconst_0
+ bastore
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_caload.j b/dx/tests/105-verify-load-store-ops/op_caload.j
new file mode 100644
index 0000000..ceaf09f
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_caload.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_caload
+.super java/lang/Object
+
+.method public static test(D)V
+ .limit locals 2
+ .limit stack 3
+
+ dload_0
+ iconst_0
+ caload
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_castore.j b/dx/tests/105-verify-load-store-ops/op_castore.j
new file mode 100644
index 0000000..5bd493e
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_castore.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_castore
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 2
+ .limit stack 4
+
+ fload_0
+ iconst_0
+ iconst_0
+ castore
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_daload.j b/dx/tests/105-verify-load-store-ops/op_daload.j
new file mode 100644
index 0000000..895d6be
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_daload.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_daload
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ iconst_0
+ daload
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_dastore.j b/dx/tests/105-verify-load-store-ops/op_dastore.j
new file mode 100644
index 0000000..b102f79
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_dastore.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dastore
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 2
+ .limit stack 4
+
+ fload_0
+ iconst_0
+ dconst_0
+ dastore
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_dstore.j b/dx/tests/105-verify-load-store-ops/op_dstore.j
new file mode 100644
index 0000000..d656a84
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_dstore.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dstore
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 6
+ .limit stack 4
+
+ fconst_0
+ dstore 5
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_dstore_0.j b/dx/tests/105-verify-load-store-ops/op_dstore_0.j
new file mode 100644
index 0000000..cb3da3a
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_dstore_0.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dstore_0
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 6
+ .limit stack 4
+
+ fconst_0
+ dstore_0
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_dstore_1.j b/dx/tests/105-verify-load-store-ops/op_dstore_1.j
new file mode 100644
index 0000000..45fcf9b
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_dstore_1.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dstore_1
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 6
+ .limit stack 4
+
+ fconst_0
+ dstore_1
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_dstore_2.j b/dx/tests/105-verify-load-store-ops/op_dstore_2.j
new file mode 100644
index 0000000..7c167d4
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_dstore_2.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dstore_2
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 6
+ .limit stack 4
+
+ fconst_0
+ dstore_2
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_dstore_3.j b/dx/tests/105-verify-load-store-ops/op_dstore_3.j
new file mode 100644
index 0000000..17222e0
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_dstore_3.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dstore_3
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 6
+ .limit stack 4
+
+ fconst_0
+ dstore_3
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_faload.j b/dx/tests/105-verify-load-store-ops/op_faload.j
new file mode 100644
index 0000000..1c17a8e
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_faload.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_faload
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ iconst_0
+ faload
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_fastore.j b/dx/tests/105-verify-load-store-ops/op_fastore.j
new file mode 100644
index 0000000..799555e
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_fastore.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_fastore
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 2
+ .limit stack 4
+
+ fload_0
+ iconst_0
+ fconst_0
+ fastore
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_fstore.j b/dx/tests/105-verify-load-store-ops/op_fstore.j
new file mode 100644
index 0000000..5c61ebe
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_fstore.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_fstore
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 6
+ .limit stack 4
+
+ iconst_0
+ fstore 5
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_fstore_0.j b/dx/tests/105-verify-load-store-ops/op_fstore_0.j
new file mode 100644
index 0000000..d3033e9
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_fstore_0.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_fstore_0
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 6
+ .limit stack 4
+
+ iconst_0
+ fstore_0
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_fstore_1.j b/dx/tests/105-verify-load-store-ops/op_fstore_1.j
new file mode 100644
index 0000000..0abca8f
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_fstore_1.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_fstore_1
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 6
+ .limit stack 4
+
+ iconst_0
+ fstore_1
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_fstore_2.j b/dx/tests/105-verify-load-store-ops/op_fstore_2.j
new file mode 100644
index 0000000..5cd1ebc
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_fstore_2.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_fstore_2
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 6
+ .limit stack 4
+
+ iconst_0
+ fstore_2
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_fstore_3.j b/dx/tests/105-verify-load-store-ops/op_fstore_3.j
new file mode 100644
index 0000000..a232307
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_fstore_3.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_fstore_3
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 6
+ .limit stack 4
+
+ iconst_0
+ fstore_3
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_iaload.j b/dx/tests/105-verify-load-store-ops/op_iaload.j
new file mode 100644
index 0000000..3face08
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_iaload.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_iaload
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ iconst_0
+ iaload
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_iastore.j b/dx/tests/105-verify-load-store-ops/op_iastore.j
new file mode 100644
index 0000000..d090e37
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_iastore.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_iastore
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 2
+ .limit stack 4
+
+ fload_0
+ iconst_0
+ iconst_0
+ iastore
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_istore.j b/dx/tests/105-verify-load-store-ops/op_istore.j
new file mode 100644
index 0000000..138d709
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_istore.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_istore
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 6
+ .limit stack 4
+
+ fconst_0
+ istore 5
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_istore_0.j b/dx/tests/105-verify-load-store-ops/op_istore_0.j
new file mode 100644
index 0000000..2644c3d
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_istore_0.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_istore_0
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 6
+ .limit stack 4
+
+ fconst_0
+ istore_0
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_istore_1.j b/dx/tests/105-verify-load-store-ops/op_istore_1.j
new file mode 100644
index 0000000..03534ee
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_istore_1.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_istore_1
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 6
+ .limit stack 4
+
+ fconst_0
+ istore_1
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_istore_2.j b/dx/tests/105-verify-load-store-ops/op_istore_2.j
new file mode 100644
index 0000000..e1a80b3
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_istore_2.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_istore_2
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 6
+ .limit stack 4
+
+ fconst_0
+ istore_2
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_istore_3.j b/dx/tests/105-verify-load-store-ops/op_istore_3.j
new file mode 100644
index 0000000..43c226f
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_istore_3.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_istore_3
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 6
+ .limit stack 4
+
+ fconst_0
+ istore_3
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_laload.j b/dx/tests/105-verify-load-store-ops/op_laload.j
new file mode 100644
index 0000000..3143604
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_laload.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_laload
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ iconst_0
+ laload
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_lastore.j b/dx/tests/105-verify-load-store-ops/op_lastore.j
new file mode 100644
index 0000000..b7ea069
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_lastore.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_lastore
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 2
+ .limit stack 4
+
+ fload_0
+ iconst_0
+ lconst_0
+ lastore
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_lstore.j b/dx/tests/105-verify-load-store-ops/op_lstore.j
new file mode 100644
index 0000000..fde6974
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_lstore.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_lstore
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 6
+ .limit stack 4
+
+ fconst_0
+ lstore 5
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_lstore_0.j b/dx/tests/105-verify-load-store-ops/op_lstore_0.j
new file mode 100644
index 0000000..e98eab4
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_lstore_0.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_lstore_0
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 6
+ .limit stack 4
+
+ fconst_0
+ lstore_0
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_lstore_1.j b/dx/tests/105-verify-load-store-ops/op_lstore_1.j
new file mode 100644
index 0000000..0e2291a
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_lstore_1.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_lstore_1
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 6
+ .limit stack 4
+
+ fconst_0
+ lstore_1
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_lstore_2.j b/dx/tests/105-verify-load-store-ops/op_lstore_2.j
new file mode 100644
index 0000000..a84702d
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_lstore_2.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_lstore_2
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 6
+ .limit stack 4
+
+ dconst_0
+ lstore_2
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_lstore_3.j b/dx/tests/105-verify-load-store-ops/op_lstore_3.j
new file mode 100644
index 0000000..c35ace8
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_lstore_3.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_lstore_3
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 6
+ .limit stack 4
+
+ fconst_0
+ lstore_3
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_saload.j b/dx/tests/105-verify-load-store-ops/op_saload.j
new file mode 100644
index 0000000..4a80939
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_saload.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_saload
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 2
+ .limit stack 3
+
+ fload_0
+ iconst_0
+ saload
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/op_sastore.j b/dx/tests/105-verify-load-store-ops/op_sastore.j
new file mode 100644
index 0000000..c97dd66
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/op_sastore.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_sastore
+.super java/lang/Object
+
+.method public static test(F)V
+ .limit locals 2
+ .limit stack 4
+
+ fload_0
+ iconst_0
+ iconst_0
+ sastore
+ return
+.end method
diff --git a/dx/tests/105-verify-load-store-ops/run b/dx/tests/105-verify-load-store-ops/run
new file mode 100644
index 0000000..adf987c
--- /dev/null
+++ b/dx/tests/105-verify-load-store-ops/run
@@ -0,0 +1,68 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+function oneop()
+{
+ jasmin -d . op_"$1".j
+ dx --debug --dex op_"$1".class >/dev/null 2>&1
+ if [ "$?" = "0" ]; then
+ dx --debug --dex --dump-method="op_$1.test*" op_"$1".class
+ else
+ echo "$1: expected failure occurred"
+ fi
+}
+
+oneop aaload
+oneop aastore
+oneop astore
+oneop astore_0
+oneop astore_1
+oneop astore_2
+oneop astore_3
+oneop baload
+oneop bastore
+oneop caload
+oneop castore
+oneop daload
+oneop dastore
+oneop dstore
+oneop dstore_0
+oneop dstore_1
+oneop dstore_2
+oneop dstore_3
+oneop faload
+oneop fastore
+oneop fstore
+oneop fstore_0
+oneop fstore_1
+oneop fstore_2
+oneop fstore_3
+oneop iaload
+oneop iastore
+oneop istore
+oneop istore_0
+oneop istore_1
+oneop istore_2
+oneop istore_3
+oneop laload
+oneop lastore
+oneop lstore
+oneop lstore_0
+oneop lstore_1
+oneop lstore_2
+oneop lstore_3
+oneop saload
+oneop sastore
diff --git a/dx/tests/106-verify-object-ops/expected.txt b/dx/tests/106-verify-object-ops/expected.txt
new file mode 100644
index 0000000..ce36b7e
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/expected.txt
@@ -0,0 +1,32 @@
+Generated: ./op_anewarray.class
+anewarray: expected failure occurred
+Generated: ./op_arraylength.class
+arraylength: expected failure occurred
+Generated: ./op_athrow.class
+athrow: expected failure occurred
+Generated: ./op_checkcast.class
+checkcast: expected failure occurred
+Generated: ./op_getfield.class
+getfield: expected failure occurred
+Generated: ./op_instanceof.class
+instanceof: expected failure occurred
+Generated: ./op_invokeinterface.class
+invokeinterface: expected failure occurred
+Generated: ./op_invokespecial.class
+invokespecial: expected failure occurred
+Generated: ./op_invokestatic.class
+invokestatic: expected failure occurred
+Generated: ./op_invokevirtual.class
+invokevirtual: expected failure occurred
+Generated: ./op_monitorenter.class
+monitorenter: expected failure occurred
+Generated: ./op_monitorexit.class
+monitorexit: expected failure occurred
+Generated: ./op_multianewarray.class
+multianewarray: expected failure occurred
+Generated: ./op_newarray.class
+newarray: expected failure occurred
+Generated: ./op_putfield.class
+putfield: expected failure occurred
+Generated: ./op_putstatic.class
+putstatic: expected failure occurred
diff --git a/dx/tests/106-verify-object-ops/info.txt b/dx/tests/106-verify-object-ops/info.txt
new file mode 100644
index 0000000..85295d7
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/info.txt
@@ -0,0 +1,2 @@
+This tests that the various "objecty" opcodes verify that their
+arguments are actually of the appropriate types.
diff --git a/dx/tests/106-verify-object-ops/op_anewarray.j b/dx/tests/106-verify-object-ops/op_anewarray.j
new file mode 100644
index 0000000..348acbd
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_anewarray.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_anewarray
+.super java/lang/Object
+
+.method public static test()V
+ .limit locals 2
+ .limit stack 3
+
+ fconst_0
+ anewarray java/lang/Object
+ return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_arraylength.j b/dx/tests/106-verify-object-ops/op_arraylength.j
new file mode 100644
index 0000000..df5af82
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_arraylength.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_arraylength
+.super java/lang/Object
+
+.method public static test()V
+ .limit locals 2
+ .limit stack 3
+
+ fconst_0
+ arraylength
+ return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_athrow.j b/dx/tests/106-verify-object-ops/op_athrow.j
new file mode 100644
index 0000000..a5a5be3
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_athrow.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_athrow
+.super java/lang/Object
+
+.method public static test()V
+ .limit locals 2
+ .limit stack 3
+
+ fconst_0
+ athrow
+ return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_checkcast.j b/dx/tests/106-verify-object-ops/op_checkcast.j
new file mode 100644
index 0000000..d921ec4
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_checkcast.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_checkcast
+.super java/lang/Object
+
+.method public static test()V
+ .limit locals 2
+ .limit stack 3
+
+ fconst_0
+ checkcast java/lang/Object
+ return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_getfield.j b/dx/tests/106-verify-object-ops/op_getfield.j
new file mode 100644
index 0000000..4d5f782
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_getfield.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_getfield
+.super java/lang/Object
+
+.method public static test()V
+ .limit locals 2
+ .limit stack 3
+
+ fconst_0
+ getfield blort/x I
+ return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_instanceof.j b/dx/tests/106-verify-object-ops/op_instanceof.j
new file mode 100644
index 0000000..8a938f5
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_instanceof.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_instanceof
+.super java/lang/Object
+
+.method public static test()V
+ .limit locals 2
+ .limit stack 3
+
+ fconst_0
+ instanceof java/lang/Object
+ return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_invokeinterface.j b/dx/tests/106-verify-object-ops/op_invokeinterface.j
new file mode 100644
index 0000000..2f1528f
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_invokeinterface.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_invokeinterface
+.super java/lang/Object
+
+.method public static test()V
+ .limit locals 2
+ .limit stack 3
+
+ fconst_0
+ invokeinterface blort/x()V 1
+ return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_invokespecial.j b/dx/tests/106-verify-object-ops/op_invokespecial.j
new file mode 100644
index 0000000..87baffc
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_invokespecial.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_invokespecial
+.super java/lang/Object
+
+.method public static test()V
+ .limit locals 2
+ .limit stack 3
+
+ fconst_0
+ invokespecial blort/x()V
+ return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_invokestatic.j b/dx/tests/106-verify-object-ops/op_invokestatic.j
new file mode 100644
index 0000000..80247bd
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_invokestatic.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_invokestatic
+.super java/lang/Object
+
+.method public static test()V
+ .limit locals 2
+ .limit stack 3
+
+ fconst_0
+ invokestatic blort/x(I)V
+ return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_invokevirtual.j b/dx/tests/106-verify-object-ops/op_invokevirtual.j
new file mode 100644
index 0000000..d7ba9b5
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_invokevirtual.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_invokevirtual
+.super java/lang/Object
+
+.method public static test()V
+ .limit locals 2
+ .limit stack 3
+
+ fconst_0
+ invokevirtual blort/x()V
+ return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_monitorenter.j b/dx/tests/106-verify-object-ops/op_monitorenter.j
new file mode 100644
index 0000000..95e23d8
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_monitorenter.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_monitorenter
+.super java/lang/Object
+
+.method public static test()V
+ .limit locals 2
+ .limit stack 3
+
+ fconst_0
+ monitorenter
+ return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_monitorexit.j b/dx/tests/106-verify-object-ops/op_monitorexit.j
new file mode 100644
index 0000000..50e5fe2
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_monitorexit.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_monitorexit
+.super java/lang/Object
+
+.method public static test()V
+ .limit locals 2
+ .limit stack 3
+
+ fconst_0
+ monitorexit
+ return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_multianewarray.j b/dx/tests/106-verify-object-ops/op_multianewarray.j
new file mode 100644
index 0000000..d02f474
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_multianewarray.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_multianewarray
+.super java/lang/Object
+
+.method public static test()V
+ .limit locals 2
+ .limit stack 3
+
+ fconst_0
+ iconst_0
+ multianewarray [[[I 2
+ return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_newarray.j b/dx/tests/106-verify-object-ops/op_newarray.j
new file mode 100644
index 0000000..16ab256
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_newarray.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_newarray
+.super java/lang/Object
+
+.method public static test()V
+ .limit locals 2
+ .limit stack 3
+
+ fconst_0
+ newarray short
+ return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_putfield.j b/dx/tests/106-verify-object-ops/op_putfield.j
new file mode 100644
index 0000000..eb33fc9
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_putfield.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_putfield
+.super java/lang/Object
+
+.method public static test()V
+ .limit locals 2
+ .limit stack 3
+
+ fconst_0
+ iconst_0
+ putfield blort/x I
+ return
+.end method
diff --git a/dx/tests/106-verify-object-ops/op_putstatic.j b/dx/tests/106-verify-object-ops/op_putstatic.j
new file mode 100644
index 0000000..221f08b
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/op_putstatic.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_putstatic
+.super java/lang/Object
+
+.method public static test()V
+ .limit locals 2
+ .limit stack 3
+
+ fconst_0
+ putstatic blort/x I
+ return
+.end method
diff --git a/dx/tests/106-verify-object-ops/run b/dx/tests/106-verify-object-ops/run
new file mode 100644
index 0000000..f512210
--- /dev/null
+++ b/dx/tests/106-verify-object-ops/run
@@ -0,0 +1,43 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+function oneop()
+{
+ jasmin -d . op_"$1".j
+ dx --debug --dex op_"$1".class >/dev/null 2>&1
+ if [ "$?" = "0" ]; then
+ dx --debug --dex --dump-method="op_$1.test*" op_"$1".class
+ else
+ echo "$1: expected failure occurred"
+ fi
+}
+
+oneop anewarray
+oneop arraylength
+oneop athrow
+oneop checkcast
+oneop getfield
+oneop instanceof
+oneop invokeinterface
+oneop invokespecial
+oneop invokestatic
+oneop invokevirtual
+oneop monitorenter
+oneop monitorexit
+oneop multianewarray
+oneop newarray
+oneop putfield
+oneop putstatic
diff --git a/dx/tests/107-verify-stack-ops/expected.txt b/dx/tests/107-verify-stack-ops/expected.txt
new file mode 100644
index 0000000..812025d
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/expected.txt
@@ -0,0 +1,34 @@
+Generated: ./op_dup.class
+dup: expected failure occurred
+Generated: ./op_dup_x1_case1.class
+dup_x1_case1: expected failure occurred
+Generated: ./op_dup_x1_case2.class
+dup_x1_case2: expected failure occurred
+Generated: ./op_dup_x2_case1.class
+dup_x2_case1: expected failure occurred
+Generated: ./op_dup_x2_case2.class
+dup_x2_case2: expected failure occurred
+Generated: ./op_dup_x2_case3.class
+dup_x2_case3: expected failure occurred
+Generated: ./op_dup2.class
+dup2: expected failure occurred
+Generated: ./op_dup2_x1_case1.class
+dup2_x1_case1: expected failure occurred
+Generated: ./op_dup2_x1_case2.class
+dup2_x1_case2: expected failure occurred
+Generated: ./op_dup2_x1_case3.class
+dup2_x1_case3: expected failure occurred
+Generated: ./op_dup2_x2_case1.class
+dup2_x2_case1: expected failure occurred
+Generated: ./op_dup2_x2_case2.class
+dup2_x2_case2: expected failure occurred
+Generated: ./op_dup2_x2_case3.class
+dup2_x2_case3: expected failure occurred
+Generated: ./op_pop.class
+pop: expected failure occurred
+Generated: ./op_pop2.class
+pop2: expected failure occurred
+Generated: ./op_swap_case1.class
+swap_case1: expected failure occurred
+Generated: ./op_swap_case2.class
+swap_case2: expected failure occurred
diff --git a/dx/tests/107-verify-stack-ops/info.txt b/dx/tests/107-verify-stack-ops/info.txt
new file mode 100644
index 0000000..c489ace
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/info.txt
@@ -0,0 +1,2 @@
+This tests that the various stack manipulation opcodes verify that their
+arguments are actually of the appropriate categories.
diff --git a/dx/tests/107-verify-stack-ops/op_dup.j b/dx/tests/107-verify-stack-ops/op_dup.j
new file mode 100644
index 0000000..6c9ebc2
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dup
+.super java/lang/Object
+
+.method public static test()V
+ .limit locals 2
+ .limit stack 6
+
+ dconst_0
+ dup
+ return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup2.j b/dx/tests/107-verify-stack-ops/op_dup2.j
new file mode 100644
index 0000000..d17ce20
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup2.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dup2
+.super java/lang/Object
+
+.method public static test()V
+ .limit locals 2
+ .limit stack 6
+
+ dconst_0
+ iconst_0
+ dup2
+ return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup2_case1.j b/dx/tests/107-verify-stack-ops/op_dup2_case1.j
new file mode 100644
index 0000000..7b014f7
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup2_case1.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dup2_case1
+.super java/lang/Object
+
+.method public static test()V
+ .limit locals 2
+ .limit stack 6
+
+ dconst_0
+ iconst_0
+ dup2
+ return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup2_x1_case1.j b/dx/tests/107-verify-stack-ops/op_dup2_x1_case1.j
new file mode 100644
index 0000000..26667f0
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup2_x1_case1.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dup2_x1_case1
+.super java/lang/Object
+
+.method public static test()V
+ .limit locals 2
+ .limit stack 8
+
+ dconst_0
+ iconst_0
+ iconst_0
+ dup2_x1
+ return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup2_x1_case2.j b/dx/tests/107-verify-stack-ops/op_dup2_x1_case2.j
new file mode 100644
index 0000000..35e97c4
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup2_x1_case2.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dup2_x1_case2
+.super java/lang/Object
+
+.method public static test()V
+ .limit locals 2
+ .limit stack 8
+
+ iconst_0
+ dconst_0
+ iconst_0
+ dup2_x1
+ return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup2_x1_case3.j b/dx/tests/107-verify-stack-ops/op_dup2_x1_case3.j
new file mode 100644
index 0000000..d15ccc3
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup2_x1_case3.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dup2_x1_case3
+.super java/lang/Object
+
+.method public static test()V
+ .limit locals 2
+ .limit stack 8
+
+ iconst_0
+ dconst_0
+ dconst_0
+ dup2_x1
+ return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup2_x2_case1.j b/dx/tests/107-verify-stack-ops/op_dup2_x2_case1.j
new file mode 100644
index 0000000..e2538a0
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup2_x2_case1.j
@@ -0,0 +1,28 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dup2_x2_case1
+.super java/lang/Object
+
+.method public static test()V
+ .limit locals 2
+ .limit stack 8
+
+ dconst_0
+ iconst_0
+ iconst_0
+ iconst_0
+ dup2_x2
+ return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup2_x2_case2.j b/dx/tests/107-verify-stack-ops/op_dup2_x2_case2.j
new file mode 100644
index 0000000..1e2645c
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup2_x2_case2.j
@@ -0,0 +1,28 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dup2_x2_case2
+.super java/lang/Object
+
+.method public static test()V
+ .limit locals 2
+ .limit stack 8
+
+ iconst_0
+ iconst_0
+ dconst_0
+ iconst_0
+ dup2_x2
+ return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup2_x2_case3.j b/dx/tests/107-verify-stack-ops/op_dup2_x2_case3.j
new file mode 100644
index 0000000..dad31e5
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup2_x2_case3.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dup2_x2_case3
+.super java/lang/Object
+
+.method public static test()V
+ .limit locals 2
+ .limit stack 8
+
+ dconst_0
+ iconst_0
+ dconst_0
+ dup2_x2
+ return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup_x1_case1.j b/dx/tests/107-verify-stack-ops/op_dup_x1_case1.j
new file mode 100644
index 0000000..037b0f8
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup_x1_case1.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dup_x1_case1
+.super java/lang/Object
+
+.method public static test()V
+ .limit locals 2
+ .limit stack 6
+
+ iconst_0
+ dconst_0
+ dup_x1
+ return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup_x1_case2.j b/dx/tests/107-verify-stack-ops/op_dup_x1_case2.j
new file mode 100644
index 0000000..fa52b16
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup_x1_case2.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dup_x1_case2
+.super java/lang/Object
+
+.method public static test()V
+ .limit locals 2
+ .limit stack 6
+
+ dconst_0
+ iconst_0
+ dup_x1
+ return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup_x2_case1.j b/dx/tests/107-verify-stack-ops/op_dup_x2_case1.j
new file mode 100644
index 0000000..7c4e89c
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup_x2_case1.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dup_x2_case1
+.super java/lang/Object
+
+.method public static test()V
+ .limit locals 2
+ .limit stack 6
+
+ dconst_0
+ iconst_0
+ iconst_0
+ dup_x2
+ return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup_x2_case2.j b/dx/tests/107-verify-stack-ops/op_dup_x2_case2.j
new file mode 100644
index 0000000..c4aa545
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup_x2_case2.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dup_x2_case2
+.super java/lang/Object
+
+.method public static test()V
+ .limit locals 2
+ .limit stack 6
+
+ iconst_0
+ iconst_0
+ dconst_0
+ dup_x2
+ return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_dup_x2_case3.j b/dx/tests/107-verify-stack-ops/op_dup_x2_case3.j
new file mode 100644
index 0000000..f920d8c
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_dup_x2_case3.j
@@ -0,0 +1,27 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_dup_x2_case3
+.super java/lang/Object
+
+.method public static test()V
+ .limit locals 2
+ .limit stack 6
+
+ iconst_0
+ dconst_0
+ dconst_0
+ dup_x2
+ return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_pop.j b/dx/tests/107-verify-stack-ops/op_pop.j
new file mode 100644
index 0000000..1b74e2c
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_pop.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_pop
+.super java/lang/Object
+
+.method public static test()V
+ .limit locals 2
+ .limit stack 6
+
+ dconst_0
+ pop
+ return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_pop2.j b/dx/tests/107-verify-stack-ops/op_pop2.j
new file mode 100644
index 0000000..ef2d122
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_pop2.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_pop2
+.super java/lang/Object
+
+.method public static test()V
+ .limit locals 2
+ .limit stack 6
+
+ dconst_0
+ iconst_0
+ pop2
+ return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_pop2_case2.j b/dx/tests/107-verify-stack-ops/op_pop2_case2.j
new file mode 100644
index 0000000..f9d6ea3
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_pop2_case2.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_pop2_case2
+.super java/lang/Object
+
+.method public static test()V
+ .limit locals 2
+ .limit stack 6
+
+ dconst_0
+ iconst_0
+ pop2
+ return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_swap_case1.j b/dx/tests/107-verify-stack-ops/op_swap_case1.j
new file mode 100644
index 0000000..f6b4ab7
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_swap_case1.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_swap_case1
+.super java/lang/Object
+
+.method public static test()V
+ .limit locals 2
+ .limit stack 6
+
+ dconst_0
+ iconst_0
+ swap
+ return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/op_swap_case2.j b/dx/tests/107-verify-stack-ops/op_swap_case2.j
new file mode 100644
index 0000000..6e0fcba
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/op_swap_case2.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class op_swap_case2
+.super java/lang/Object
+
+.method public static test()V
+ .limit locals 2
+ .limit stack 6
+
+ iconst_0
+ dconst_0
+ swap
+ return
+.end method
diff --git a/dx/tests/107-verify-stack-ops/run b/dx/tests/107-verify-stack-ops/run
new file mode 100644
index 0000000..a55c639
--- /dev/null
+++ b/dx/tests/107-verify-stack-ops/run
@@ -0,0 +1,44 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+function oneop()
+{
+ jasmin -d . op_"$1".j
+ dx --debug --dex op_"$1".class >/dev/null 2>&1
+ if [ "$?" = "0" ]; then
+ dx --debug --dex --dump-method="op_$1.test*" op_"$1".class
+ else
+ echo "$1: expected failure occurred"
+ fi
+}
+
+oneop dup
+oneop dup_x1_case1
+oneop dup_x1_case2
+oneop dup_x2_case1
+oneop dup_x2_case2
+oneop dup_x2_case3
+oneop dup2
+oneop dup2_x1_case1
+oneop dup2_x1_case2
+oneop dup2_x1_case3
+oneop dup2_x2_case1
+oneop dup2_x2_case2
+oneop dup2_x2_case3
+oneop pop
+oneop pop2
+oneop swap_case1
+oneop swap_case2
diff --git a/dx/tests/108-string-annotation/Blort.java b/dx/tests/108-string-annotation/Blort.java
new file mode 100644
index 0000000..9bb52e4
--- /dev/null
+++ b/dx/tests/108-string-annotation/Blort.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort {
+ @Frotz(name = "grue")
+ public static void testSingle() {
+ // This space intentionally left blank.
+ }
+
+ @Fizmo(names = "gruesome")
+ public static void testArray1() {
+ // This space intentionally left blank.
+ }
+
+ @Fizmo(names = {"awful", "awesome"})
+ public static void testArray2() {
+ // This space intentionally left blank.
+ }
+}
diff --git a/dx/tests/108-string-annotation/Fizmo.java b/dx/tests/108-string-annotation/Fizmo.java
new file mode 100644
index 0000000..a20bba0
--- /dev/null
+++ b/dx/tests/108-string-annotation/Fizmo.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public @interface Fizmo {
+ String[] names();
+}
diff --git a/dx/tests/108-string-annotation/Frotz.java b/dx/tests/108-string-annotation/Frotz.java
new file mode 100644
index 0000000..3ad5426
--- /dev/null
+++ b/dx/tests/108-string-annotation/Frotz.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public @interface Frotz {
+ String name();
+}
diff --git a/dx/tests/108-string-annotation/expected.txt b/dx/tests/108-string-annotation/expected.txt
new file mode 100644
index 0000000..57da807
--- /dev/null
+++ b/dx/tests/108-string-annotation/expected.txt
@@ -0,0 +1,12 @@
+
+ elements[0]:
+ name
+ value: string "grue"
+
+ elements[0]:
+ names
+ value: array {"gruesome"}
+
+ elements[0]:
+ names
+ value: array {"awful", "awesome"}
diff --git a/dx/tests/108-string-annotation/info.txt b/dx/tests/108-string-annotation/info.txt
new file mode 100644
index 0000000..6c85834
--- /dev/null
+++ b/dx/tests/108-string-annotation/info.txt
@@ -0,0 +1,6 @@
+This is a smoke test of dex conversion, which checks to see that
+string annotations get represented reasonably.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/108-string-annotation/run b/dx/tests/108-string-annotation/run
new file mode 100644
index 0000000..d89053f
--- /dev/null
+++ b/dx/tests/108-string-annotation/run
@@ -0,0 +1,47 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -d . Blort.java Fizmo.java Frotz.java
+dx --debug --dex --no-optimize --positions=none --no-locals \
+ --dump-to=- *.class | cut -f 2 -d '|' | awk '
+
+BEGIN {
+ dumping = 0;
+}
+
+/annotation$/ {
+ dumping = 1;
+ printf("\n");
+ next;
+}
+
+/^[ ]*$/ {
+ dumping = 0;
+ next;
+}
+
+dumping && /^ elements/ {
+ print;
+}
+
+dumping && /^ name_idx/ {
+ printf(" %s\n", $4);
+}
+
+dumping && /^ value/ {
+ print;
+}
+'
diff --git a/dx/tests/109-int-branch/blort.j b/dx/tests/109-int-branch/blort.j
new file mode 100644
index 0000000..8517177
--- /dev/null
+++ b/dx/tests/109-int-branch/blort.j
@@ -0,0 +1,99 @@
+; Copyright (C) 2008 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class blort
+.super java/lang/Object
+
+.method public static test1(ZBCSI[I)V
+ .limit locals 6
+ .limit stack 3
+
+ iload_0
+ iload_1
+ if_icmpeq zorch
+
+ iload_2
+ iload_3
+ if_icmpne zorch
+
+ iload 4
+ aload 5
+ iconst_0
+ iaload
+ if_icmplt zorch
+
+ aload 5
+ iconst_0
+ iaload
+ iload_0
+ if_icmpgt zorch
+
+ iload 4
+ iload_1
+ if_icmpge zorch
+
+ nop
+
+zorch:
+ return
+.end method
+
+.method public static test2(I)Ljava/lang/Object;
+ .limit locals 2
+ .limit stack 3
+
+ aconst_null
+ astore 1
+
+ aload_1
+ iconst_0
+ iaload
+ iload_0
+ if_icmpge zorch
+
+ nop
+
+zorch:
+ aconst_null
+ areturn
+.end method
+
+.method public static test3(I[I)Ljava/lang/Object;
+ .limit locals 3
+ .limit stack 3
+
+ aconst_null
+ astore 2
+
+frotz:
+ aload_2
+ ifnonnull fizmo
+
+ aload_1
+ astore_2
+ goto frotz
+
+fizmo:
+ aload_2
+ iconst_0
+ iaload
+ iload_0
+ if_icmpge zorch
+
+ nop
+
+zorch:
+ aconst_null
+ areturn
+.end method
diff --git a/dx/tests/109-int-branch/expected.txt b/dx/tests/109-int-branch/expected.txt
new file mode 100644
index 0000000..d73bfa0
--- /dev/null
+++ b/dx/tests/109-int-branch/expected.txt
@@ -0,0 +1,67 @@
+Generated: ./blort.class
+blort.test1:(ZBCSI[I)V:
+regs: 000f; ins: 0006; outs: 0000
+ 0000: move v0, v9
+ 0001: move v1, v10
+ 0002: move v2, v11
+ 0003: move v3, v12
+ 0004: move v4, v13
+ 0005: move-object v5, v14
+ 0006: move v6, v0
+ 0007: move v7, v1
+ 0008: if-eq v6, v7, 0021 // +0019
+ 000a: move v6, v2
+ 000b: move v7, v3
+ 000c: if-ne v6, v7, 0021 // +0015
+ 000e: move v6, v4
+ 000f: move-object v7, v5
+ 0010: const/4 v8, #int 0 // #0
+ 0011: aget v7, v7, v8
+ 0013: if-lt v6, v7, 0021 // +000e
+ 0015: move-object v6, v5
+ 0016: const/4 v7, #int 0 // #0
+ 0017: aget v6, v6, v7
+ 0019: move v7, v0
+ 001a: if-gt v6, v7, 0021 // +0007
+ 001c: move v6, v4
+ 001d: move v7, v1
+ 001e: if-ge v6, v7, 0021 // +0003
+ 0020: nop
+ 0021: return-void
+ source file: "blort.j"
+blort.test2:(I)Ljava/lang/Object;:
+regs: 0005; ins: 0001; outs: 0000
+ 0000: move v0, v4
+ 0001: const/4 v2, #null // #0
+ 0002: move-object v1, v2
+ 0003: move-object v2, v1
+ 0004: const/4 v3, #int 0 // #0
+ 0005: aget-object v2, v2, v3
+ 0007: move v3, v0
+ 0008: if-ge v2, v3, 000b // +0003
+ 000a: nop
+ 000b: const/4 v2, #null // #0
+ 000c: move-object v0, v2
+ 000d: return-object v0
+ source file: "blort.j"
+blort.test3:(I[I)Ljava/lang/Object;:
+regs: 0007; ins: 0002; outs: 0000
+ 0000: move v0, v5
+ 0001: move-object v1, v6
+ 0002: const/4 v3, #null // #0
+ 0003: move-object v2, v3
+ 0004: move-object v3, v2
+ 0005: if-nez v3, 000a // +0005
+ 0007: move-object v3, v1
+ 0008: move-object v2, v3
+ 0009: goto 0004 // -0005
+ 000a: move-object v3, v2
+ 000b: const/4 v4, #int 0 // #0
+ 000c: aget v3, v3, v4
+ 000e: move v4, v0
+ 000f: if-ge v3, v4, 0012 // +0003
+ 0011: nop
+ 0012: const/4 v3, #null // #0
+ 0013: move-object v0, v3
+ 0014: return-object v0
+ source file: "blort.j"
diff --git a/dx/tests/109-int-branch/info.txt b/dx/tests/109-int-branch/info.txt
new file mode 100644
index 0000000..e650a12
--- /dev/null
+++ b/dx/tests/109-int-branch/info.txt
@@ -0,0 +1,6 @@
+This tests that an int branch with valid arguments is properly translated.
+(Regression test.)
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/109-int-branch/run b/dx/tests/109-int-branch/run
new file mode 100644
index 0000000..68b17ea
--- /dev/null
+++ b/dx/tests/109-int-branch/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+jasmin -d . blort.j
+dx --debug --dex --no-optimize --dump-method="blort.test*" blort.class
diff --git a/dx/tests/110-dex-preserve-this/Blort.java b/dx/tests/110-dex-preserve-this/Blort.java
new file mode 100644
index 0000000..e2965b3
--- /dev/null
+++ b/dx/tests/110-dex-preserve-this/Blort.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort {
+ public int test() {
+ Object z = "";
+ Number t = new Integer(3);
+ if (z instanceof Integer) {
+ return 3;
+ }
+ return ((Integer) t);
+ }
+}
diff --git a/dx/tests/110-dex-preserve-this/expected.txt b/dx/tests/110-dex-preserve-this/expected.txt
new file mode 100644
index 0000000..e91d4bf
--- /dev/null
+++ b/dx/tests/110-dex-preserve-this/expected.txt
@@ -0,0 +1 @@
+this: v4
diff --git a/dx/tests/110-dex-preserve-this/info.txt b/dx/tests/110-dex-preserve-this/info.txt
new file mode 100644
index 0000000..913c196
--- /dev/null
+++ b/dx/tests/110-dex-preserve-this/info.txt
@@ -0,0 +1,10 @@
+This is a smoke test of dex conversion, which checks to see that a
+"this" argument is never reused for a temporary. (Background: Popular
+debuggers will get confused if "this" is reused, and it arguably
+should be the case that the target object of an instance method being
+executed ought never be gc'ed anyway, and overwriting "this" could in
+fact cause that to happen.)
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/110-dex-preserve-this/run b/dx/tests/110-dex-preserve-this/run
new file mode 100644
index 0000000..9770237
--- /dev/null
+++ b/dx/tests/110-dex-preserve-this/run
@@ -0,0 +1,41 @@
+#!/bin/bash
+#
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -g -d . Blort.java
+dx --debug --dex --positions=none \
+ --dump-to=dump.txt --dump-method="Blort.test" *.class
+
+cat dump.txt | awk '
+BEGIN {
+ thisReg = "BOGUS";
+}
+# Find the number of registers; in this case the last register is "this".
+/^regs:/ {
+ regs = substr($2, 4, 1);
+ thisReg = "v" (regs - 1);
+ printf("this: %s\n", thisReg);
+}
+# Output any lines that mention the "this" register.
+{
+ count = split($0, words, /,? */);
+ for (i = 1; i <= count; i++) {
+ if (words[i] == thisReg) {
+ printf("%s\n", $0);
+ break;
+ }
+ }
+}
+' \ No newline at end of file
diff --git a/dx/tests/111-use-null-as-array/Blort.java b/dx/tests/111-use-null-as-array/Blort.java
new file mode 100644
index 0000000..79d2b4f
--- /dev/null
+++ b/dx/tests/111-use-null-as-array/Blort.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort {
+ public static boolean test_getBooleanArray() {
+ boolean[] arr = null;
+ return arr[1];
+ }
+
+ public static byte test_getByteArray() {
+ byte[] arr = null;
+ return arr[2];
+ }
+
+ public static char test_getCharArray() {
+ char[] arr = null;
+ return arr[3];
+ }
+
+ public static double test_getDoubleArray() {
+ double[] arr = null;
+ return arr[4];
+ }
+
+ public static float test_getFloatArray() {
+ float[] arr = null;
+ return arr[5];
+ }
+
+ public static int test_getIntArray() {
+ int[] arr = null;
+ return arr[6];
+ }
+
+ public static long test_getLongArray() {
+ long[] arr = null;
+ return arr[7];
+ }
+
+ public static Object test_getObjectArray() {
+ Object[] arr = null;
+ return arr[8];
+ }
+
+ public static short test_getShortArray() {
+ short[] arr = null;
+ return arr[9];
+ }
+
+ public static void test_setBooleanArray() {
+ boolean[] arr = null;
+ arr[1] = true;
+ }
+
+ public static void test_setByteArray() {
+ byte[] arr = null;
+ arr[2] = (byte) 3;
+ }
+
+ public static void test_setCharArray() {
+ char[] arr = null;
+ arr[4] = (char) 5;
+ }
+
+ public static void test_setDoubleArray() {
+ double[] arr = null;
+ arr[6] = 7.0F;
+ }
+
+ public static void test_setFloatArray() {
+ float[] arr = null;
+ arr[8] = 9.0F;
+ }
+
+ public static void test_setIntArray() {
+ int[] arr = null;
+ arr[10] = 11;
+ }
+
+ public static void test_setLongArray() {
+ long[] arr = null;
+ arr[12] = 13;
+ }
+
+ public static void test_setObjectArray() {
+ Object[] arr = null;
+ arr[14] = "blort";
+ }
+
+ public static void test_setShortArray() {
+ short[] arr = null;
+ arr[15] = (short) 16;
+ }
+}
diff --git a/dx/tests/111-use-null-as-array/expected.txt b/dx/tests/111-use-null-as-array/expected.txt
new file mode 100644
index 0000000..7e2116b
--- /dev/null
+++ b/dx/tests/111-use-null-as-array/expected.txt
@@ -0,0 +1,116 @@
+Blort.test_getBooleanArray:()Z:
+regs: 0002; ins: 0000; outs: 0000
+ 0000: const/4 v0, #null // #0
+ 0001: const/4 v1, #int 1 // #1
+ 0002: aget-byte v0, v0, v1
+ 0004: return v0
+Blort.test_getByteArray:()B:
+regs: 0002; ins: 0000; outs: 0000
+ 0000: const/4 v0, #null // #0
+ 0001: const/4 v1, #int 2 // #2
+ 0002: aget-byte v0, v0, v1
+ 0004: return v0
+Blort.test_getCharArray:()C:
+regs: 0002; ins: 0000; outs: 0000
+ 0000: const/4 v0, #null // #0
+ 0001: const/4 v1, #int 3 // #3
+ 0002: aget-char v0, v0, v1
+ 0004: return v0
+Blort.test_getDoubleArray:()D:
+regs: 0002; ins: 0000; outs: 0000
+ 0000: const/4 v0, #null // #0
+ 0001: const/4 v1, #int 4 // #4
+ 0002: aget-wide v0, v0, v1
+ 0004: return-wide v0
+Blort.test_getFloatArray:()F:
+regs: 0002; ins: 0000; outs: 0000
+ 0000: const/4 v0, #null // #0
+ 0001: const/4 v1, #int 5 // #5
+ 0002: aget v0, v0, v1
+ 0004: return v0
+Blort.test_getIntArray:()I:
+regs: 0002; ins: 0000; outs: 0000
+ 0000: const/4 v0, #null // #0
+ 0001: const/4 v1, #int 6 // #6
+ 0002: aget v0, v0, v1
+ 0004: return v0
+Blort.test_getLongArray:()J:
+regs: 0002; ins: 0000; outs: 0000
+ 0000: const/4 v0, #null // #0
+ 0001: const/4 v1, #int 7 // #7
+ 0002: aget-wide v0, v0, v1
+ 0004: return-wide v0
+Blort.test_getObjectArray:()Ljava/lang/Object;:
+regs: 0002; ins: 0000; outs: 0000
+ 0000: const/4 v0, #null // #0
+ 0001: const/16 v1, #int 8 // #0008
+ 0003: aget-object v0, v0, v1
+ 0005: return-object v0
+Blort.test_getShortArray:()S:
+regs: 0002; ins: 0000; outs: 0000
+ 0000: const/4 v0, #null // #0
+ 0001: const/16 v1, #int 9 // #0009
+ 0003: aget-short v0, v0, v1
+ 0005: return v0
+Blort.test_setBooleanArray:()V:
+regs: 0002; ins: 0000; outs: 0000
+ 0000: const/4 v1, #int 1 // #1
+ 0001: const/4 v0, #null // #0
+ 0002: aput v1, v0, v1
+ 0004: return-void
+Blort.test_setByteArray:()V:
+regs: 0003; ins: 0000; outs: 0000
+ 0000: const/4 v0, #null // #0
+ 0001: const/4 v1, #int 2 // #2
+ 0002: const/4 v2, #int 3 // #3
+ 0003: aput v2, v0, v1
+ 0005: return-void
+Blort.test_setCharArray:()V:
+regs: 0003; ins: 0000; outs: 0000
+ 0000: const/4 v0, #null // #0
+ 0001: const/4 v1, #int 4 // #4
+ 0002: const/4 v2, #int 5 // #5
+ 0003: aput v2, v0, v1
+ 0005: return-void
+Blort.test_setDoubleArray:()V:
+regs: 0004; ins: 0000; outs: 0000
+ 0000: const/4 v0, #null // #0
+ 0001: const/4 v1, #int 6 // #6
+ 0002: const-wide/high16 v2, #double 7.0 // #401c000000000000
+ 0004: aput-wide v2, v0, v1
+ 0006: return-void
+Blort.test_setFloatArray:()V:
+regs: 0003; ins: 0000; outs: 0000
+ 0000: const/4 v0, #null // #0
+ 0001: const/16 v1, #int 8 // #0008
+ 0003: const/high16 v2, #float 9.0 // #41100000
+ 0005: aput v2, v0, v1
+ 0007: return-void
+Blort.test_setIntArray:()V:
+regs: 0003; ins: 0000; outs: 0000
+ 0000: const/4 v0, #null // #0
+ 0001: const/16 v1, #int 10 // #000a
+ 0003: const/16 v2, #int 11 // #000b
+ 0005: aput v2, v0, v1
+ 0007: return-void
+Blort.test_setLongArray:()V:
+regs: 0004; ins: 0000; outs: 0000
+ 0000: const/4 v0, #null // #0
+ 0001: const/16 v1, #int 12 // #000c
+ 0003: const-wide/16 v2, #long 13 // #000d
+ 0005: aput-wide v2, v0, v1
+ 0007: return-void
+Blort.test_setObjectArray:()V:
+regs: 0003; ins: 0000; outs: 0000
+ 0000: const/4 v0, #null // #0
+ 0001: const/16 v1, #int 14 // #000e
+ 0003: const-string v2, "blort"
+ 0005: aput-object v2, v0, v1
+ 0007: return-void
+Blort.test_setShortArray:()V:
+regs: 0003; ins: 0000; outs: 0000
+ 0000: const/4 v0, #null // #0
+ 0001: const/16 v1, #int 15 // #000f
+ 0003: const/16 v2, #int 16 // #0010
+ 0005: aput v2, v0, v1
+ 0007: return-void
diff --git a/dx/tests/111-use-null-as-array/info.txt b/dx/tests/111-use-null-as-array/info.txt
new file mode 100644
index 0000000..624386d
--- /dev/null
+++ b/dx/tests/111-use-null-as-array/info.txt
@@ -0,0 +1,18 @@
+This is a smoke test of dex conversion, which checks to see that uses
+of a known-null in contexts that require a specific type end up getting
+converted to the type in question. When executed, this sort of code
+will inevitably throw a NullPointerException, but if the opcode weren't
+correct, they would instead incorrectly fail verification.
+
+If you inspect the expected output of this test, you will see that
+there are some surprising instructions in there, such as using
+aget-byte for what was a boolean[] in the source code. In these cases,
+the resulting output is still correct (passes verification and will
+throw a NullPointerException if ever executed). However, it happens
+that during translation there simply wasn't enough information to
+recover the "true" original meaning at the level of actual opcode
+selection.
+
+This test compares emitted code against a known-good (via eyeballing)
+version, so it is possible for this test to spuriously fail if other
+aspects of conversion end up altering the output in innocuous ways.
diff --git a/dx/tests/111-use-null-as-array/run b/dx/tests/111-use-null-as-array/run
new file mode 100644
index 0000000..7e4e1e8
--- /dev/null
+++ b/dx/tests/111-use-null-as-array/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$JAVAC -g -d . Blort.java
+dx --debug --dex --positions=none --no-locals \
+ --dump-to=- --dump-method="Blort.test*" *.class
diff --git a/dx/tests/112-dex-return-jsr-result/blort.j b/dx/tests/112-dex-return-jsr-result/blort.j
new file mode 100644
index 0000000..6f6cddb
--- /dev/null
+++ b/dx/tests/112-dex-return-jsr-result/blort.j
@@ -0,0 +1,41 @@
+; Copyright (C) 2010 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class blort
+.super java/lang/Object
+
+.method public static zorch(ZLjava/lang/Object;)Ljava/lang/Object;
+ .limit locals 3
+ .limit stack 1
+
+ iload_0
+ ifeq thenBlock
+ jsr subroutine
+ goto endBlock
+
+thenBlock:
+ jsr subroutine
+ goto endBlock
+
+subroutine:
+ astore_2
+ aload_1
+ invokestatic java/lang/String/valueOf(Ljava/lang/Object;)Ljava/lang/String;
+ astore_1
+ ret 2
+
+endBlock:
+ aload_1
+ areturn
+.end method
diff --git a/dx/tests/112-dex-return-jsr-result/expected.txt b/dx/tests/112-dex-return-jsr-result/expected.txt
new file mode 100644
index 0000000..0e32069
--- /dev/null
+++ b/dx/tests/112-dex-return-jsr-result/expected.txt
@@ -0,0 +1,2 @@
+Generated: ./blort.class
+total invokes: 2
diff --git a/dx/tests/112-dex-return-jsr-result/info.txt b/dx/tests/112-dex-return-jsr-result/info.txt
new file mode 100644
index 0000000..289ebb2
--- /dev/null
+++ b/dx/tests/112-dex-return-jsr-result/info.txt
@@ -0,0 +1,6 @@
+This test checks to make sure a result returned more-or-less directly
+from a jsr subroutine ends up being represented appropriately.
+
+In particular, this is a regression test for a bug which caused a
+goto instruction to be interposed between an invoke instruction and
+its associated move-result.
diff --git a/dx/tests/112-dex-return-jsr-result/run b/dx/tests/112-dex-return-jsr-result/run
new file mode 100644
index 0000000..4075d16
--- /dev/null
+++ b/dx/tests/112-dex-return-jsr-result/run
@@ -0,0 +1,63 @@
+#!/bin/bash
+#
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# The awk fun here tries to cull out all but the salient bits. The aim
+# is to check to see that there are two invoke-static instructions, each
+# followed directly by a move-result-object.
+
+jasmin -d . blort.j
+dx --debug --dex --dump-to=- --dump-method=blort.zorch --dump-width=200 \
+ blort.class | awk '
+
+BEGIN {
+ invokeAt = -1;
+ moveAt = -1;
+ invokeCount = 0;
+ failed = 0;
+}
+
+# Note: This has to be done before the test clause below.
+/move-result-object/ {
+ moveAt = NR;
+}
+
+(invokeAt > 0) {
+ if (moveAt != (invokeAt + 1)) {
+ failed = 1;
+ }
+ invokeAt = -1;
+ moveAt = -1;
+}
+
+# Note: This has to be done after the test clause above.
+/invoke-static/ {
+ invokeAt = NR;
+ invokeCount++;
+}
+
+END {
+ printf("total invokes: %d\n", invokeCount);
+ if (failed) {
+ exit 1;
+ }
+}
+'
+
+if [ "$?" = "1" ]; then
+ # The test failed. Be helpful and print the entire method body.
+ dx --debug --dex --dump-to=- --dump-method=blort.zorch --dump-width=200 \
+ blort.class
+fi
diff --git a/dx/tests/113-old-style-inner-class/Blort.java b/dx/tests/113-old-style-inner-class/Blort.java
new file mode 100644
index 0000000..89b1ba0
--- /dev/null
+++ b/dx/tests/113-old-style-inner-class/Blort.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Blort {
+ public static Runnable theRunnable = new Runnable() {
+ public void run() { }
+ };
+
+ public Runnable create() {
+ return new Runnable() {
+ public void run() { }
+ };
+ }
+}
diff --git a/dx/tests/113-old-style-inner-class/expected.txt b/dx/tests/113-old-style-inner-class/expected.txt
new file mode 100644
index 0000000..d58d6db
--- /dev/null
+++ b/dx/tests/113-old-style-inner-class/expected.txt
@@ -0,0 +1,2 @@
+warning:
+warning:
diff --git a/dx/tests/113-old-style-inner-class/info.txt b/dx/tests/113-old-style-inner-class/info.txt
new file mode 100644
index 0000000..91b3aad
--- /dev/null
+++ b/dx/tests/113-old-style-inner-class/info.txt
@@ -0,0 +1,3 @@
+This is a smoke test of dex conversion, which makes sure that
+converstion of old-style anonymous inner classes (whose representation
+is information-lossy) causes a warning to be issued.
diff --git a/dx/tests/113-old-style-inner-class/run b/dx/tests/113-old-style-inner-class/run
new file mode 100644
index 0000000..c93661f
--- /dev/null
+++ b/dx/tests/113-old-style-inner-class/run
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# We compile for a 1.4 target to suppress the use of EnclosingMethod
+# attributes.
+${JAVAC} -source 1.4 -target 1.4 -d . Blort.java
+
+# We expect there to be two warning lines, one for each of the inner classes.
+dx --debug --dex *.class 2>&1 | awk '/^warning:/ { print $1 }'
diff --git a/dx/tests/run-all-tests b/dx/tests/run-all-tests
new file mode 100755
index 0000000..68062c2
--- /dev/null
+++ b/dx/tests/run-all-tests
@@ -0,0 +1,57 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+while [ -h "${prog}" ]; do
+ newProg=`/bin/ls -ld "${prog}"`
+ newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+ if expr "x${newProg}" : 'x/' >/dev/null; then
+ prog="${newProg}"
+ else
+ progdir=`dirname "${prog}"`
+ prog="${progdir}/${newProg}"
+ fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+
+passed=0
+failed=0
+failNames=""
+
+for i in *; do
+ if [ -d "$i" -a -r "$i" ]; then
+ ./run-test "$i"
+ if [ "$?" = "0" ]; then
+ ((passed += 1))
+ else
+ ((failed += 1))
+ failNames="$failNames $i"
+ fi
+ fi
+done
+
+echo "passed: $passed test(s)"
+echo "failed: $failed test(s)"
+
+for i in $failNames; do
+ echo "failed: $i"
+done
diff --git a/dx/tests/run-test b/dx/tests/run-test
new file mode 100755
index 0000000..a9221de
--- /dev/null
+++ b/dx/tests/run-test
@@ -0,0 +1,149 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+while [ -h "${prog}" ]; do
+ newProg=`/bin/ls -ld "${prog}"`
+ newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+ if expr "x${newProg}" : 'x/' >/dev/null; then
+ prog="${newProg}"
+ else
+ progdir=`dirname "${prog}"`
+ prog="${progdir}/${newProg}"
+ fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+
+export JAVAC="${progdir}/../../../prebuilt/common/openjdk/bin/javac"
+if [ "!" -e "$JAVAC" ]; then
+ JAVAC="javac"
+fi
+
+info="info.txt"
+run="run"
+expected="expected.txt"
+output="out.txt"
+
+dev_mode="no"
+if [ "x$1" = "x--dev" ]; then
+ dev_mode="yes"
+ shift
+fi
+
+update_mode="no"
+if [ "x$1" = "x--update" ]; then
+ update_mode="yes"
+ shift
+fi
+
+usage="no"
+if [ "x$1" = "x--help" ]; then
+ usage="yes"
+else
+ if [ "x$1" = "x" ]; then
+ testdir=`basename "$oldwd"`
+ else
+ testdir="$1"
+ fi
+
+ if [ '!' -d "$testdir" ]; then
+ td2=`echo ${testdir}-*`
+ if [ '!' -d "$td2" ]; then
+ echo "${testdir}: no such test directory" 1>&2
+ usage="yes"
+ fi
+ testdir="$td2"
+ fi
+fi
+
+if [ "$usage" = "yes" ]; then
+ prog=`basename $prog`
+ (
+ echo "usage:"
+ echo " $prog --help Print this message."
+ echo " $prog testname Run test normally."
+ echo " $prog --dev testname Development mode (dump to stdout)."
+ echo " $prog --update testname Update mode (replace expected.txt)."
+ echo " Omitting the test name uses the current directory as the test."
+ ) 1>&2
+ exit 1
+fi
+
+td_info="$testdir"/"$info"
+td_run="$testdir"/"$run"
+td_expected="$testdir"/"$expected"
+
+tmpdir=/tmp/test-$$
+
+if [ '!' '(' -r "$td_info" -a -r "$td_run" -a -r "$td_expected" ')' ]; then
+ echo "${testdir}: missing files" 1>&2
+ exit 1
+fi
+
+# copy the test to a temp dir and run it
+
+echo "${testdir}: running..." 1>&2
+
+rm -rf "$tmpdir"
+cp -Rp "$testdir" "$tmpdir"
+cd "$tmpdir"
+chmod 755 "$run"
+
+#PATH="${progdir}/../build/bin:${PATH}"
+
+good="no"
+if [ "$dev_mode" = "yes" ]; then
+ "./$run" 2>&1
+ echo "exit status: $?" 1>&2
+ good="yes"
+elif [ "$update_mode" = "yes" ]; then
+ "./$run" >"${progdir}/$td_expected" 2>&1
+ good="yes"
+else
+ "./$run" >"$output" 2>&1
+ cmp -s "$expected" "$output"
+ if [ "$?" = "0" ]; then
+ # output == expected
+ good="yes"
+ echo "$testdir"': succeeded!' 1>&2
+ fi
+fi
+
+if [ "$good" = "yes" ]; then
+ cd "$oldwd"
+ rm -rf "$tmpdir"
+ exit 0
+fi
+
+(
+ echo "${testdir}: FAILED!"
+ echo ' '
+ echo '#################### info'
+ cat "$info" | sed 's/^/# /g'
+ echo '#################### diffs'
+ diff -u "$expected" "$output"
+ echo '####################'
+ echo ' '
+ echo "files left in $tmpdir"
+) 1>&2
+
+exit 1
diff --git a/hit/samples/android.hprof b/hit/samples/android.hprof
new file mode 100644
index 0000000..bb8d2e0
--- /dev/null
+++ b/hit/samples/android.hprof
Binary files differ
diff --git a/hit/src/com/android/hit/ArrayInstance.java b/hit/src/com/android/hit/ArrayInstance.java
new file mode 100644
index 0000000..42e8803
--- /dev/null
+++ b/hit/src/com/android/hit/ArrayInstance.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.util.Set;
+
+public class ArrayInstance extends Instance {
+ private int mType;
+ private int mNumEntries;
+ private byte[] mData;
+
+ public ArrayInstance(long id, StackTrace stack, int type, int numEntries,
+ byte[] data) {
+ mId = id;
+ mStack = stack;
+ mType = type;
+ mNumEntries = numEntries;
+ mData = data;
+ }
+
+ public final void resolveReferences(State state) {
+ if (mType != Types.OBJECT) {
+ return;
+ }
+
+ /*
+ * mData holds a stream of object instance ids
+ * Spin through them all and list ourselves as a reference holder.
+ */
+ int idSize = Types.getTypeSize(mType);
+ final int N = mNumEntries;
+
+ ByteArrayInputStream bais = new ByteArrayInputStream(mData);
+ DataInputStream dis = new DataInputStream(bais);
+
+ for (int i = 0; i < N; i++) {
+ long id;
+
+ try {
+ if (idSize == 4) {
+ id = dis.readInt();
+ } else {
+ id = dis.readLong();
+ }
+
+ Instance instance = state.findReference(id);
+
+ if (instance != null) {
+ instance.addParent(this);
+ }
+ } catch (java.io.IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ @Override
+ public final int getSize() {
+ return mData.length;
+ }
+
+ @Override
+ public final void visit(Set<Instance> resultSet, Filter filter) {
+ // If we're in the set then we and our children have been visited
+ if (resultSet.contains(this)) {
+ return;
+ }
+
+ if (null != filter) {
+ if (filter.accept(this)) {
+ resultSet.add(this);
+ }
+ } else {
+ resultSet.add(this);
+ }
+
+ if (mType != Types.OBJECT) {
+ return;
+ }
+
+ /*
+ * mData holds a stream of object instance ids
+ * Spin through them all and visit them
+ */
+ int idSize = Types.getTypeSize(mType);
+ final int N = mNumEntries;
+
+ ByteArrayInputStream bais = new ByteArrayInputStream(mData);
+ DataInputStream dis = new DataInputStream(bais);
+ State state = mHeap.mState;
+
+ for (int i = 0; i < N; i++) {
+ long id;
+
+ try {
+ if (idSize == 4) {
+ id = dis.readInt();
+ } else {
+ id = dis.readLong();
+ }
+
+ Instance instance = state.findReference(id);
+
+ if (instance != null) {
+ instance.visit(resultSet, filter);
+ }
+ } catch (java.io.IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ @Override
+ public final String getTypeName() {
+ return Types.getTypeName(mType) + "[" + mNumEntries + "]";
+ }
+
+ public final String toString() {
+ return String.format("%s@0x08x", getTypeName(), mId);
+ }
+
+ @Override
+ public String describeReferenceTo(long referent) {
+ // If this isn't an object array then we can't refer to an object
+ if (mType != Types.OBJECT) {
+ return super.describeReferenceTo(referent);
+ }
+
+ int idSize = Types.getTypeSize(mType);
+ final int N = mNumEntries;
+ int numRefs = 0;
+ StringBuilder result = new StringBuilder("Elements [");
+ ByteArrayInputStream bais = new ByteArrayInputStream(mData);
+ DataInputStream dis = new DataInputStream(bais);
+
+ /*
+ * Spin through all the objects and build up a string describing
+ * all of the array elements that refer to the target object.
+ */
+ for (int i = 0; i < N; i++) {
+ long id;
+
+ try {
+ if (idSize == 4) {
+ id = dis.readInt();
+ } else {
+ id = dis.readLong();
+ }
+
+ if (id == referent) {
+ numRefs++;
+
+ if (numRefs > 1) {
+ result.append(", ");
+ }
+
+ result.append(i);
+ }
+ } catch (java.io.IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ if (numRefs == 0) {
+ return super.describeReferenceTo(referent);
+ }
+
+ result.append("]");
+
+ return result.toString();
+ }
+}
diff --git a/hit/src/com/android/hit/ClassInstance.java b/hit/src/com/android/hit/ClassInstance.java
new file mode 100644
index 0000000..0869769
--- /dev/null
+++ b/hit/src/com/android/hit/ClassInstance.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.Set;
+
+public class ClassInstance extends Instance {
+ private byte[] mFieldValues;
+
+ public ClassInstance(long id, StackTrace stack, long classId) {
+ mId = id;
+ mStack = stack;
+ mClassId = classId;
+ }
+
+ public final void loadFieldData(DataInputStream in, int numBytes)
+ throws IOException {
+ mFieldValues = new byte[numBytes];
+ in.readFully(mFieldValues);
+ }
+
+ @Override
+ public void resolveReferences(State state) {
+ ClassObj isa = mHeap.mState.findClass(mClassId);
+
+ resolve(state, isa, isa.mStaticFieldTypes, isa.mStaticFieldValues);
+ resolve(state, isa, isa.mFieldTypes, mFieldValues);
+ }
+
+ private void resolve(State state, ClassObj isa, int[] types,
+ byte[] values) {
+ ByteArrayInputStream bais = new ByteArrayInputStream(values);
+ DataInputStream dis = new DataInputStream(bais);
+ final int N = types.length;
+
+ /*
+ * Spin through the list of fields, find all object references,
+ * and list ourselves as a reference holder.
+ */
+ try {
+ for (int i = 0; i < N; i++) {
+ int type = types[i];
+ int size = Types.getTypeSize(type);
+
+ if (type == Types.OBJECT) {
+ long id;
+
+ if (size == 4) {
+ id = dis.readInt();
+ } else {
+ id = dis.readLong();
+ }
+
+ Instance instance = state.findReference(id);
+
+ if (instance != null) {
+ instance.addParent(this);
+ }
+ } else {
+ dis.skipBytes(size);
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public final int getSize() {
+ ClassObj isa = mHeap.mState.findClass(mClassId);
+
+ return isa.getSize();
+ }
+
+ @Override
+ public final void visit(Set<Instance> resultSet, Filter filter) {
+ if (resultSet.contains(this)) {
+ return;
+ }
+
+ if (filter != null) {
+ if (filter.accept(this)) {
+ resultSet.add(this);
+ }
+ } else {
+ resultSet.add(this);
+ }
+
+ State state = mHeap.mState;
+ ClassObj isa = state.findClass(mClassId);
+ int[] types = isa.mFieldTypes;
+ ByteArrayInputStream bais = new ByteArrayInputStream(mFieldValues);
+ DataInputStream dis = new DataInputStream(bais);
+ final int N = types.length;
+
+ /*
+ * Spin through the list of fields, find all object references,
+ * and list ourselves as a reference holder.
+ */
+ try {
+ for (int i = 0; i < N; i++) {
+ int type = types[i];
+ int size = Types.getTypeSize(type);
+
+ if (type == Types.OBJECT) {
+ long id;
+
+ if (size == 4) {
+ id = dis.readInt();
+ } else {
+ id = dis.readLong();
+ }
+
+ Instance instance = state.findReference(id);
+
+ if (instance != null) {
+ instance.visit(resultSet, filter);
+ }
+ } else {
+ dis.skipBytes(size);
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public final String getTypeName() {
+ ClassObj theClass = mHeap.mState.findClass(mClassId);
+
+ return theClass.mClassName;
+ }
+
+ public final String toString() {
+ return String.format("%s@0x%08x", getTypeName(), mId);
+ }
+
+ @Override
+ public String describeReferenceTo(long referent) {
+ ClassObj isa = mHeap.mState.findClass(mClassId);
+ int[] types = isa.mFieldTypes;
+ String[] fieldNames = isa.mFieldNames;
+ ByteArrayInputStream bais = new ByteArrayInputStream(mFieldValues);
+ DataInputStream dis = new DataInputStream(bais);
+ final int N = types.length;
+ StringBuilder result = new StringBuilder("Referenced in field(s):");
+ int numReferences = 0;
+
+ /*
+ * Spin through the list of fields, add info about the field
+ * references to the output text.
+ */
+ try {
+ for (int i = 0; i < N; i++) {
+ int type = types[i];
+ int size = Types.getTypeSize(type);
+
+ if (type == Types.OBJECT) {
+ long id;
+
+ if (size == 4) {
+ id = dis.readInt();
+ } else {
+ id = dis.readLong();
+ }
+
+ if (id == referent) {
+ numReferences++;
+ result.append("\n ");
+ result.append(fieldNames[i]);
+ }
+ } else {
+ dis.skipBytes(size);
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ /*
+ * TODO: perform a similar loop over the static fields of isa
+ */
+
+ if (numReferences == 0) {
+ return super.describeReferenceTo(referent);
+ }
+
+ return result.toString();
+ }
+}
diff --git a/hit/src/com/android/hit/ClassObj.java b/hit/src/com/android/hit/ClassObj.java
new file mode 100644
index 0000000..1e3ac28
--- /dev/null
+++ b/hit/src/com/android/hit/ClassObj.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+
+public class ClassObj extends Instance implements Comparable<ClassObj> {
+ String mClassName;
+ long mSuperclassId;
+
+ String[] mFieldNames;
+ int[] mFieldTypes;
+
+ String[] mStaticFieldNames;
+ int[] mStaticFieldTypes;
+ byte[] mStaticFieldValues;
+
+ ArrayList<Instance> mInstances = new ArrayList<Instance>();
+ Set<ClassObj> mSubclasses = new HashSet<ClassObj>();
+
+ int mSize;
+
+ public ClassObj(long id, StackTrace stack, String className) {
+ mId = id;
+ mStack = stack;
+ mClassName = className;
+ }
+
+ @Override
+ public final void resolveReferences(State state) {
+ ByteArrayInputStream bais =
+ new ByteArrayInputStream(mStaticFieldValues);
+ DataInputStream dis = new DataInputStream(bais);
+ int[] types = mStaticFieldTypes;
+ final int N = types.length;
+
+ /*
+ * Spin through the list of static fields, find all object references,
+ * and list ourselves as a reference holder. Also add them to
+ * the list of root objects.
+ */
+ try {
+ for (int i = 0; i < N; i++) {
+ int type = types[i];
+ int size = Types.getTypeSize(type);
+
+ if (type == Types.OBJECT) {
+ long id;
+
+ if (size == 4) {
+ id = dis.readInt();
+ } else {
+ id = dis.readLong();
+ }
+
+ RootObj root = new RootObj(RootType.JAVA_STATIC, id);
+
+ if (id == 0) {
+ root.mComment = String.format(
+ "Static field %s:%s null",
+ mClassName,
+ mStaticFieldNames[i]);
+ } else {
+ Instance instance = state.findReference(id);
+
+ instance.addParent(this);
+
+ root.mComment = String.format(
+ "Static field %s:%s %s [%s] 0x%08x",
+ mClassName,
+ mStaticFieldNames[i],
+ instance.getTypeName(),
+ instance.mHeap.mName,
+ id);
+ }
+
+ mHeap.addRoot(root);
+ } else {
+ dis.skipBytes(size);
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+
+ // Lastly, add ourself as a subclass of our superclass
+ if (mSuperclassId != 0) {
+ ClassObj superclass = state.findClass(mSuperclassId);
+
+ superclass.addSubclass(this);
+ }
+ }
+
+ public final void addSubclass(ClassObj subclass) {
+ mSubclasses.add(subclass);
+ }
+
+ public final void dumpSubclasses() {
+ for (ClassObj subclass: mSubclasses) {
+ System.out.println(" " + subclass.mClassName);
+ }
+ }
+
+ public final String toString() {
+ return mClassName.replace('/', '.');
+ }
+
+ public final void addInstance(Instance instance) {
+ mInstances.add(instance);
+ }
+
+ public final void setSuperclassId(long id) {
+ mSuperclassId = id;
+ }
+
+ public final void setFieldNames(String[] names) {
+ mFieldNames = names;
+ }
+
+ public final void setFieldTypes(int[] types) {
+ mFieldTypes = types;
+ }
+
+ public final void setStaticFieldNames(String[] names) {
+ mStaticFieldNames = names;
+ }
+
+ public final void setStaticFieldTypes(int[] types) {
+ mStaticFieldTypes = types;
+ }
+
+ public final void setStaticFieldValues(byte[] values) {
+ mStaticFieldValues = values;
+ }
+
+ public final void dump() {
+ System.out.println("+---------- ClassObj dump for: " + mClassName);
+
+ System.out.println("+----- Static fields");
+
+ for (int i = 0; i < mStaticFieldNames.length; i++) {
+ System.out.println(mStaticFieldNames[i] + ": "
+ + mStaticFieldTypes[i]);
+ }
+
+ System.out.println("+----- Instance fields");
+
+ for (int i = 0; i < mFieldNames.length; i++) {
+ System.out.println(mFieldNames[i] + ": " + mFieldTypes[i]);
+ }
+ }
+
+ @Override
+ public final String getTypeName() {
+ return "class " + mClassName;
+ }
+
+ @Override
+ public final void visit(Set<Instance> resultSet, Filter filter) {
+ if (resultSet.contains(this)) {
+ return;
+ }
+
+ if (filter != null) {
+ if (filter.accept(this)) {
+ resultSet.add(this);
+ }
+ } else {
+ resultSet.add(this);
+ }
+
+ ByteArrayInputStream bais =
+ new ByteArrayInputStream(mStaticFieldValues);
+ DataInputStream dis = new DataInputStream(bais);
+ int[] types = mStaticFieldTypes;
+ final int N = types.length;
+ State state = mHeap.mState;
+
+ /*
+ * Spin through the list of static fields, find all object references,
+ * and visit them.
+ */
+ try {
+ for (int i = 0; i < N; i++) {
+ int type = types[i];
+ int size = Types.getTypeSize(type);
+
+ if (type == Types.OBJECT) {
+ long id;
+
+ if (size == 4) {
+ id = dis.readInt();
+ } else {
+ id = dis.readLong();
+ }
+
+ Instance instance = state.findReference(id);
+
+ if (instance != null) {
+ instance.visit(resultSet, filter);
+ }
+ } else {
+ dis.skipBytes(size);
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public final int compareTo(ClassObj o) {
+ return mClassName.compareTo(o.mClassName);
+ }
+
+ public final boolean equals(Object o) {
+ if (! (o instanceof ClassObj)) {
+ return false;
+ }
+
+ return 0 == compareTo((ClassObj) o);
+ }
+}
diff --git a/hit/src/com/android/hit/Heap.java b/hit/src/com/android/hit/Heap.java
new file mode 100644
index 0000000..37b15dd
--- /dev/null
+++ b/hit/src/com/android/hit/Heap.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public class Heap {
+ String mName;
+
+ // List of individual stack frames
+ HashMap<Long, StackFrame> mFrames = new HashMap<Long, StackFrame>();
+
+ // List stack traces, which are lists of stack frames
+ HashMap<Integer, StackTrace> mTraces = new HashMap<Integer, StackTrace>();
+
+ // Root objects such as interned strings, jni locals, etc
+ ArrayList<RootObj> mRoots = new ArrayList<RootObj>();
+
+ // List of threads
+ HashMap<Integer, ThreadObj> mThreads = new HashMap<Integer, ThreadObj>();
+
+ // Class definitions
+ HashMap<Long, ClassObj> mClassesById = new HashMap<Long, ClassObj>();
+ HashMap<String, ClassObj> mClassesByName = new HashMap<String, ClassObj>();
+
+ // List of instances of above class definitions
+ HashMap<Long, Instance> mInstances = new HashMap<Long, Instance>();
+
+ // The super-state that this heap is part of
+ State mState;
+
+ public Heap(String name) {
+ mName = name;
+ }
+
+ public final void addStackFrame(StackFrame theFrame) {
+ mFrames.put(theFrame.mId, theFrame);
+ }
+
+ public final StackFrame getStackFrame(long id) {
+ return mFrames.get(id);
+ }
+
+ public final void addStackTrace(StackTrace theTrace) {
+ mTraces.put(theTrace.mSerialNumber, theTrace);
+ }
+
+ public final StackTrace getStackTrace(int traceSerialNumber) {
+ return mTraces.get(traceSerialNumber);
+ }
+
+ public final StackTrace getStackTraceAtDepth(int traceSerialNumber,
+ int depth) {
+ StackTrace trace = mTraces.get(traceSerialNumber);
+
+ if (trace != null) {
+ trace = trace.fromDepth(depth);
+ }
+
+ return trace;
+ }
+
+ public final void addRoot(RootObj root) {
+ root.mIndex = mRoots.size();
+ mRoots.add(root);
+ }
+
+ public final void addThread(ThreadObj thread, int serialNumber) {
+ mThreads.put(serialNumber, thread);
+ }
+
+ public final ThreadObj getThread(int serialNumber) {
+ return mThreads.get(serialNumber);
+ }
+
+ public final void addInstance(long id, Instance instance) {
+ mInstances.put(id, instance);
+ }
+
+ public final Instance getInstance(long id) {
+ return mInstances.get(id);
+ }
+
+ public final void addClass(long id, ClassObj theClass) {
+ mClassesById.put(id, theClass);
+ mClassesByName.put(theClass.mClassName, theClass);
+ }
+
+ public final ClassObj getClass(long id) {
+ return mClassesById.get(id);
+ }
+
+ public final ClassObj getClass(String name) {
+ return mClassesByName.get(name);
+ }
+
+ public final void dumpInstanceCounts() {
+ for (ClassObj theClass: mClassesById.values()) {
+ int count = theClass.mInstances.size();
+
+ if (count > 0) {
+ System.out.println(theClass + ": " + count);
+ }
+ }
+ }
+
+ public final void dumpSubclasses() {
+ for (ClassObj theClass: mClassesById.values()) {
+ int count = theClass.mSubclasses.size();
+
+ if (count > 0) {
+ System.out.println(theClass);
+ theClass.dumpSubclasses();
+ }
+ }
+ }
+
+ public final void dumpSizes() {
+ for (ClassObj theClass: mClassesById.values()) {
+ int size = 0;
+
+ for (Instance instance: theClass.mInstances) {
+ size += instance.getCompositeSize();
+ }
+
+ if (size > 0) {
+ System.out.println(theClass + ": base " + theClass.getSize()
+ + ", composite " + size);
+ }
+ }
+ }
+
+ /*
+ * Spin through all of the class instances and link them to their
+ * parent class definition objects. Then have each instance resolve
+ * its own internal object references.
+ */
+ public final void resolveInstanceRefs(State state) {
+ for (Instance instance : mInstances.values()) {
+ ClassObj theClass = mClassesById.get(instance.mClassId);
+
+ if (theClass == null) {
+ continue;
+ }
+
+ String name = theClass.mClassName;
+ String superclassName = "none";
+ ClassObj superClass = mClassesById.get(theClass.mSuperclassId);
+
+ if (superClass != null) {
+ superclassName = superClass.mClassName;
+ }
+
+ theClass.addInstance(instance);
+ instance.resolveReferences(state);
+ }
+ }
+
+ public final void resolveClassStatics(State state) {
+ for (ClassObj theClass: mClassesById.values()) {
+ theClass.resolveReferences(state);
+ }
+ }
+
+ public final void resolveRoots(State state) {
+ for (RootObj root: mRoots) {
+ root.resolveReferences(state);
+ }
+ }
+}
diff --git a/hit/src/com/android/hit/HprofParser.java b/hit/src/com/android/hit/HprofParser.java
new file mode 100644
index 0000000..98fef1e
--- /dev/null
+++ b/hit/src/com/android/hit/HprofParser.java
@@ -0,0 +1,611 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.InputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.util.HashMap;
+
+public class HprofParser
+{
+ private static final int STRING_IN_UTF8 = 0x01;
+ private static final int LOAD_CLASS = 0x02;
+ private static final int UNLOAD_CLASS = 0x03; // unused
+ private static final int STACK_FRAME = 0x04;
+ private static final int STACK_TRACE = 0x05;
+ private static final int ALLOC_SITES = 0x06; // unused
+ private static final int HEAP_SUMMARY = 0x07;
+ private static final int START_THREAD = 0x0a; // unused
+ private static final int END_THREAD = 0x0b; // unused
+ private static final int HEAP_DUMP = 0x0c;
+ private static final int HEAP_DUMP_SEGMENT = 0x1c;
+ private static final int HEAP_DUMP_END = 0x2c;
+ private static final int CPU_SAMPLES = 0x0d; // unused
+ private static final int CONTROL_SETTINGS = 0x0e; // unused
+
+ private static final int ROOT_UNKNOWN = 0xff;
+ private static final int ROOT_JNI_GLOBAL = 0x01;
+ private static final int ROOT_JNI_LOCAL = 0x02;
+ private static final int ROOT_JAVA_FRAME = 0x03;
+ private static final int ROOT_NATIVE_STACK = 0x04;
+ private static final int ROOT_STICKY_CLASS = 0x05;
+ private static final int ROOT_THREAD_BLOCK = 0x06;
+ private static final int ROOT_MONITOR_USED = 0x07;
+ private static final int ROOT_THREAD_OBJECT = 0x08;
+ private static final int ROOT_CLASS_DUMP = 0x20;
+ private static final int ROOT_INSTANCE_DUMP = 0x21;
+ private static final int ROOT_OBJECT_ARRAY_DUMP = 0x22;
+ private static final int ROOT_PRIMITIVE_ARRAY_DUMP = 0x23;
+
+ /**
+ * Android format addition
+ *
+ * Specifies information about which heap certain objects came from.
+ * When a sub-tag of this type appears in a HPROF_HEAP_DUMP or
+ * HPROF_HEAP_DUMP_SEGMENT record, entries that follow it will be
+ * associated with the specified heap. The HEAP_DUMP_INFO data is reset
+ * at the end of the HEAP_DUMP[_SEGMENT]. Multiple HEAP_DUMP_INFO entries
+ * may appear in a single HEAP_DUMP[_SEGMENT].
+ *
+ * Format:
+ * u1: Tag value (0xFE)
+ * u4: heap ID
+ * ID: heap name string ID
+ */
+ private static final int ROOT_HEAP_DUMP_INFO = 0xfe;
+ private static final int ROOT_INTERNED_STRING = 0x89;
+ private static final int ROOT_FINALIZING = 0x8a;
+ private static final int ROOT_DEBUGGER = 0x8b;
+ private static final int ROOT_REFERENCE_CLEANUP = 0x8c;
+ private static final int ROOT_VM_INTERNAL = 0x8d;
+ private static final int ROOT_JNI_MONITOR = 0x8e;
+ private static final int ROOT_UNREACHABLE = 0x90;
+ private static final int ROOT_PRIMITIVE_ARRAY_NODATA= 0xc3;
+
+ DataInputStream mInput;
+ int mIdSize;
+ State mState;
+
+ byte[] mFieldBuffer = new byte[8];
+
+ /*
+ * These are only needed while parsing so are not kept as part of the
+ * heap data.
+ */
+ HashMap<Long, String> mStrings = new HashMap<Long, String>();
+ HashMap<Long, String> mClassNames = new HashMap<Long, String>();
+
+ public HprofParser(DataInputStream in) {
+ mInput = in;
+ }
+
+ public final State parse() {
+ State state = new State();
+ mState = state;
+
+ try {
+ String s = readNullTerminatedString();
+ DataInputStream in = mInput;
+
+ mIdSize = in.readInt();
+ Types.setIdSize(mIdSize);
+
+ in.readLong(); // Timestamp, ignored for now
+
+ while (true) {
+ int tag = in.readUnsignedByte();
+ int timestamp = in.readInt();
+ int length = in.readInt();
+
+ switch (tag) {
+ case STRING_IN_UTF8:
+ loadString(length - 4);
+ break;
+
+ case LOAD_CLASS:
+ loadClass();
+ break;
+
+ case STACK_FRAME:
+ loadStackFrame();
+ break;
+
+ case STACK_TRACE:
+ loadStackTrace();
+ break;
+
+ case HEAP_DUMP:
+ loadHeapDump(length);
+ mState.setToDefaultHeap();
+ break;
+
+ case HEAP_DUMP_SEGMENT:
+ loadHeapDump(length);
+ mState.setToDefaultHeap();
+ break;
+
+ default:
+ skipFully(length);
+ }
+
+ }
+ } catch (EOFException eof) {
+ // this is fine
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ mState.resolveReferences();
+
+ return state;
+ }
+
+ private String readNullTerminatedString() throws IOException {
+ StringBuilder s = new StringBuilder();
+ DataInputStream in = mInput;
+
+ for (int c = in.read(); c != 0; c = in.read()) {
+ s.append((char) c);
+ }
+
+ return s.toString();
+ }
+
+ private long readId() throws IOException {
+ switch (mIdSize) {
+ case 1: return mInput.readUnsignedByte();
+ case 2: return mInput.readUnsignedShort();
+ case 4: return ((long) mInput.readInt()) & 0x00000000ffffffffL;
+ case 8: return mInput.readLong();
+ }
+
+ throw new IllegalArgumentException("ID Length must be 1, 2, 4, or 8");
+ }
+
+ private String readUTF8(int length) throws IOException {
+ byte[] b = new byte[length];
+
+ mInput.read(b);
+
+ return new String(b, "utf-8");
+ }
+
+ private void loadString(int length) throws IOException {
+ long id = readId();
+ String string = readUTF8(length);
+
+ mStrings.put(id, string);
+ }
+
+ private void loadClass() throws IOException {
+ DataInputStream in = mInput;
+ int serial = in.readInt();
+ long id = readId();
+ int stackTrace = in.readInt(); // unused
+ String name = mStrings.get(readId());
+
+ mClassNames.put(id, name);
+ }
+
+ private void loadStackFrame() throws IOException {
+ long id = readId();
+ String methodName = mStrings.get(readId());
+ String methodSignature = mStrings.get(readId());
+ String sourceFile = mStrings.get(readId());
+ int serial = mInput.readInt();
+ int lineNumber = mInput.readInt();
+
+ StackFrame frame = new StackFrame(id, methodName, methodSignature,
+ sourceFile, serial, lineNumber);
+
+ mState.addStackFrame(frame);
+ }
+
+ private void loadStackTrace() throws IOException {
+ int serialNumber = mInput.readInt();
+ int threadSerialNumber = mInput.readInt();
+ final int numFrames = mInput.readInt();
+ StackFrame[] frames = new StackFrame[numFrames];
+
+ for (int i = 0; i < numFrames; i++) {
+ frames[i] = mState.getStackFrame(readId());
+ }
+
+ StackTrace trace = new StackTrace(serialNumber, threadSerialNumber,
+ frames);
+
+ mState.addStackTrace(trace);
+ }
+
+ private void loadHeapDump(int length) throws IOException {
+ DataInputStream in = mInput;
+
+ while (length > 0) {
+ int tag = in.readUnsignedByte();
+ length--;
+
+ switch (tag) {
+ case ROOT_UNKNOWN:
+ length -= loadBasicObj(RootType.UNKNOWN);
+ break;
+
+ case ROOT_JNI_GLOBAL:
+ length -= loadBasicObj(RootType.NATIVE_STATIC);
+ readId(); // ignored
+ length -= mIdSize;
+ break;
+
+ case ROOT_JNI_LOCAL:
+ length -= loadJniLocal();
+ break;
+
+ case ROOT_JAVA_FRAME:
+ length -= loadJavaFrame();
+ break;
+
+ case ROOT_NATIVE_STACK:
+ length -= loadNativeStack();
+ break;
+
+ case ROOT_STICKY_CLASS:
+ length -= loadBasicObj(RootType.SYSTEM_CLASS);
+ break;
+
+ case ROOT_THREAD_BLOCK:
+ length -= loadThreadBlock();
+ break;
+
+ case ROOT_MONITOR_USED:
+ length -= loadBasicObj(RootType.BUSY_MONITOR);
+ break;
+
+ case ROOT_THREAD_OBJECT:
+ length -= loadThreadObject();
+ break;
+
+ case ROOT_CLASS_DUMP:
+ length -= loadClassDump();
+ break;
+
+ case ROOT_INSTANCE_DUMP:
+ length -= loadInstanceDump();
+ break;
+
+ case ROOT_OBJECT_ARRAY_DUMP:
+ length -= loadObjectArrayDump();
+ break;
+
+ case ROOT_PRIMITIVE_ARRAY_DUMP:
+ length -= loadPrimitiveArrayDump();
+ break;
+
+ case ROOT_PRIMITIVE_ARRAY_NODATA:
+ System.err.println("+--- PRIMITIVE ARRAY NODATA DUMP");
+ length -= loadPrimitiveArrayDump();
+
+ throw new IllegalArgumentException(
+ "Don't know how to load a nodata array");
+
+ case ROOT_HEAP_DUMP_INFO:
+ int heapId = mInput.readInt();
+ long heapNameId = readId();
+ String heapName = mStrings.get(heapNameId);
+
+ mState.setHeapTo(heapId, heapName);
+ length -= 4 + mIdSize;
+ break;
+
+ case ROOT_INTERNED_STRING:
+ length -= loadBasicObj(RootType.INTERNED_STRING);
+ break;
+
+ case ROOT_FINALIZING:
+ length -= loadBasicObj(RootType.FINALIZING);
+ break;
+
+ case ROOT_DEBUGGER:
+ length -= loadBasicObj(RootType.DEBUGGER);
+ break;
+
+ case ROOT_REFERENCE_CLEANUP:
+ length -= loadBasicObj(RootType.REFERENCE_CLEANUP);
+ break;
+
+ case ROOT_VM_INTERNAL:
+ length -= loadBasicObj(RootType.VM_INTERNAL);
+ break;
+
+ case ROOT_JNI_MONITOR:
+ length -= loadJniMonitor();
+ break;
+
+ case ROOT_UNREACHABLE:
+ length -= loadBasicObj(RootType.UNREACHABLE);
+ break;
+
+ default:
+ throw new IllegalArgumentException(
+ "loadHeapDump loop with unknown tag " + tag
+ + " with " + mInput.available()
+ + " bytes possibly remaining");
+ }
+ }
+ }
+
+ private int loadJniLocal() throws IOException {
+ long id = readId();
+ int threadSerialNumber = mInput.readInt();
+ int stackFrameNumber = mInput.readInt();
+ ThreadObj thread = mState.getThread(threadSerialNumber);
+ StackTrace trace = mState.getStackTraceAtDepth(thread.mStackTrace,
+ stackFrameNumber);
+ RootObj root = new RootObj(RootType.NATIVE_LOCAL, id,
+ threadSerialNumber, trace);
+
+ root.setHeap(mState.mCurrentHeap);
+ mState.addRoot(root);
+
+ return mIdSize + 4 + 4;
+ }
+
+ private int loadJavaFrame() throws IOException {
+ long id = readId();
+ int threadSerialNumber = mInput.readInt();
+ int stackFrameNumber = mInput.readInt();
+ ThreadObj thread = mState.getThread(threadSerialNumber);
+ StackTrace trace = mState.getStackTraceAtDepth(thread.mStackTrace,
+ stackFrameNumber);
+ RootObj root = new RootObj(RootType.JAVA_LOCAL, id, threadSerialNumber,
+ trace);
+
+ root.setHeap(mState.mCurrentHeap);
+ mState.addRoot(root);
+
+ return mIdSize + 4 + 4;
+ }
+
+ private int loadNativeStack() throws IOException {
+ long id = readId();
+ int threadSerialNumber = mInput.readInt();
+ ThreadObj thread = mState.getThread(threadSerialNumber);
+ StackTrace trace = mState.getStackTrace(thread.mStackTrace);
+ RootObj root = new RootObj(RootType.NATIVE_STACK, id,
+ threadSerialNumber, trace);
+
+ root.setHeap(mState.mCurrentHeap);
+ mState.addRoot(root);
+
+ return mIdSize + 4;
+ }
+
+ private int loadBasicObj(RootType type) throws IOException {
+ long id = readId();
+ RootObj root = new RootObj(type, id);
+
+ root.setHeap(mState.mCurrentHeap);
+ mState.addRoot(root);
+
+ return mIdSize;
+ }
+
+ private int loadThreadBlock() throws IOException {
+ long id = readId();
+ int threadSerialNumber = mInput.readInt();
+ ThreadObj thread = mState.getThread(threadSerialNumber);
+ StackTrace stack = mState.getStackTrace(thread.mStackTrace);
+ RootObj root = new RootObj(RootType.THREAD_BLOCK, id,
+ threadSerialNumber, stack);
+
+ root.setHeap(mState.mCurrentHeap);
+ mState.addRoot(root);
+
+ return mIdSize + 4;
+ }
+
+ private int loadThreadObject() throws IOException {
+ long id = readId();
+ int threadSerialNumber = mInput.readInt();
+ int stackSerialNumber = mInput.readInt();
+ ThreadObj thread = new ThreadObj(id, stackSerialNumber);
+
+ mState.addThread(thread, threadSerialNumber);
+
+ return mIdSize + 4 + 4;
+ }
+
+ private int loadClassDump() throws IOException {
+ int bytesRead = 0;
+ DataInputStream in = mInput;
+ long id = readId();
+ int stackSerialNumber = in.readInt();
+ StackTrace stack = mState.getStackTrace(stackSerialNumber);
+ long superClassId = readId();
+ long classLoaderId = readId();
+ long signersId = readId();
+ long protectionDomainId = readId();
+ long reserved1 = readId();
+ long reserved2 = readId();
+ int instanceSize = in.readInt();
+
+ bytesRead = (7 * mIdSize) + 4 + 4;
+
+ // Skip over the constant pool
+ int numEntries = in.readUnsignedShort();
+ bytesRead += 2;
+
+ for (int i = 0; i < numEntries; i++) {
+ in.readUnsignedShort();
+ bytesRead += 2 + skipValue();
+ }
+
+ // Static fields
+ numEntries = in.readUnsignedShort();
+ bytesRead += 2;
+
+ String[] staticFieldNames = new String[numEntries];
+ int[] staticFieldTypes = new int[numEntries];
+ ByteArrayOutputStream staticFieldValues = new ByteArrayOutputStream();
+ byte[] buffer = mFieldBuffer;
+
+ for (int i = 0; i < numEntries; i++) {
+ staticFieldNames[i] = mStrings.get(readId());
+
+ int fieldType = in.readByte();
+ int fieldSize = Types.getTypeSize(fieldType);
+ staticFieldTypes[i] = fieldType;
+
+ in.readFully(buffer, 0, fieldSize);
+ staticFieldValues.write(buffer, 0, fieldSize);
+
+ bytesRead += mIdSize + 1 + fieldSize;
+ }
+
+ // Instance fields
+ numEntries = in.readUnsignedShort();
+ bytesRead += 2;
+
+ String[] names = new String[numEntries];
+ int[] types = new int[numEntries];
+
+ for (int i = 0; i < numEntries; i++) {
+ long fieldName = readId();
+ int type = in.readUnsignedByte();
+
+ names[i] = mStrings.get(fieldName);
+ types[i] = type;
+
+ bytesRead += mIdSize + 1;
+ }
+
+ ClassObj theClass = new ClassObj(id, stack, mClassNames.get(id));
+
+ theClass.setStaticFieldNames(staticFieldNames);
+ theClass.setStaticFieldTypes(staticFieldTypes);
+ theClass.setStaticFieldValues(staticFieldValues.toByteArray());
+
+ theClass.setSuperclassId(superClassId);
+ theClass.setFieldNames(names);
+ theClass.setFieldTypes(types);
+ theClass.setSize(instanceSize);
+
+ theClass.setHeap(mState.mCurrentHeap);
+
+ mState.addClass(id, theClass);
+
+ return bytesRead;
+ }
+
+ private int loadInstanceDump() throws IOException {
+ long id = readId();
+ int stackId = mInput.readInt();
+ StackTrace stack = mState.getStackTrace(stackId);
+ long classId = readId();
+ int remaining = mInput.readInt();
+ ClassInstance instance = new ClassInstance(id, stack, classId);
+
+ instance.loadFieldData(mInput, remaining);
+ instance.setHeap(mState.mCurrentHeap);
+ mState.addInstance(id, instance);
+
+ return mIdSize + 4 + mIdSize + 4 + remaining;
+ }
+
+ private int loadObjectArrayDump() throws IOException {
+ long id = readId();
+ int stackId = mInput.readInt();
+ StackTrace stack = mState.getStackTrace(stackId);
+ int numElements = mInput.readInt();
+ long classId = readId();
+ int totalBytes = numElements * mIdSize;
+ byte[] data = new byte[totalBytes];
+ String className = mClassNames.get(classId);
+
+ mInput.readFully(data);
+
+ ArrayInstance array = new ArrayInstance(id, stack, Types.OBJECT,
+ numElements, data);
+
+ array.mClassId = classId;
+ array.setHeap(mState.mCurrentHeap);
+ mState.addInstance(id, array);
+
+ return mIdSize + 4 + 4 + mIdSize + totalBytes;
+ }
+
+ private int loadPrimitiveArrayDump() throws IOException {
+ long id = readId();
+ int stackId = mInput.readInt();
+ StackTrace stack = mState.getStackTrace(stackId);
+ int numElements = mInput.readInt();
+ int type = mInput.readUnsignedByte();
+ int size = Types.getTypeSize(type);
+ int totalBytes = numElements * size;
+ byte[] data = new byte[totalBytes];
+
+ mInput.readFully(data);
+
+ ArrayInstance array = new ArrayInstance(id, stack, type, numElements,
+ data);
+
+ array.setHeap(mState.mCurrentHeap);
+ mState.addInstance(id, array);
+
+ return mIdSize + 4 + 4 + 1 + totalBytes;
+ }
+
+ private int loadJniMonitor() throws IOException {
+ long id = readId();
+ int threadSerialNumber = mInput.readInt();
+ int stackDepth = mInput.readInt();
+ ThreadObj thread = mState.getThread(threadSerialNumber);
+ StackTrace trace = mState.getStackTraceAtDepth(thread.mStackTrace,
+ stackDepth);
+ RootObj root = new RootObj(RootType.NATIVE_MONITOR, id,
+ threadSerialNumber, trace);
+
+ root.setHeap(mState.mCurrentHeap);
+ mState.addRoot(root);
+
+ return mIdSize + 4 + 4;
+ }
+
+ private int skipValue() throws IOException {
+ int type = mInput.readUnsignedByte();
+ int size = Types.getTypeSize(type);
+
+ skipFully(size);
+
+ return size + 1;
+ }
+
+ /*
+ * BufferedInputStream will not skip(int) the entire requested number
+ * of bytes if it extends past the current buffer boundary. So, this
+ * routine is needed to actually skip over the requested number of bytes
+ * using as many iterations as needed.
+ */
+ private void skipFully(long numBytes) throws IOException {
+ while (numBytes > 0) {
+ long skipped = mInput.skip(numBytes);
+
+ numBytes -= skipped;
+ }
+ }
+}
diff --git a/hit/src/com/android/hit/Instance.java b/hit/src/com/android/hit/Instance.java
new file mode 100644
index 0000000..6afa2b2
--- /dev/null
+++ b/hit/src/com/android/hit/Instance.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+
+public abstract class Instance {
+ long mId;
+
+ // Id of the ClassObj of which this object is an instance
+ long mClassId;
+
+ // The stack in which this object was allocated
+ StackTrace mStack;
+
+ // The heap in which this object was allocated (app, zygote, etc)
+ Heap mHeap;
+
+ // The size of this object
+ int mSize;
+
+ public interface Filter {
+ public boolean accept(Instance instance);
+ }
+
+ // List of all objects that hold a live reference to this object
+ private ArrayList<Instance> mParents;
+
+ /*
+ * After the whole HPROF file is read and parsed this method will be
+ * called on all heap objects so that they can resolve their internal
+ * object references.
+ *
+ * The super-State is passed in because some object references (such
+ * as interned Strings and static class fields) may need to be searched
+ * for in a heap other than the one this Instance is in.
+ */
+ public abstract void resolveReferences(State state);
+
+ /*
+ * Some operations require gathering all the objects in a given section
+ * of the object graph. If non-null, the filter is applied to each
+ * node in the graph to determine if it should be added to the result
+ * set.
+ */
+ public abstract void visit(Set<Instance> resultSet, Filter filter);
+
+ public void setSize(int size) {
+ mSize = size;
+ }
+
+ public final int getCompositeSize() {
+ HashSet<Instance> set = new HashSet<Instance>();
+
+ visit(set, null);
+
+ int size = 0;
+
+ for (Instance instance: set) {
+ size += instance.getSize();
+ }
+
+ return size;
+ }
+
+ // Returns the instrinsic size of a given object
+ public int getSize() {
+ return mSize;
+ }
+
+ public abstract String getTypeName();
+
+ public void setHeap(Heap heap) {
+ mHeap = heap;
+ }
+
+ // Add to the list of objects that have a hard reference to this Instance
+ public void addParent(Instance parent) {
+ if (mParents == null) {
+ mParents = new ArrayList<Instance>();
+ }
+
+ mParents.add(parent);
+ }
+
+ public ArrayList<Instance> getParents() {
+ if (mParents == null) {
+ mParents = new ArrayList<Instance>();
+ }
+
+ return mParents;
+ }
+
+ /*
+ * If this object has a reference to the object identified by id, return
+ * a String describing the reference in detail.
+ */
+ public String describeReferenceTo(long id) {
+ return "No reference to 0x" + Long.toHexString(id);
+ }
+}
diff --git a/hit/src/com/android/hit/Main.java b/hit/src/com/android/hit/Main.java
new file mode 100644
index 0000000..4ed5c11
--- /dev/null
+++ b/hit/src/com/android/hit/Main.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+import java.io.BufferedInputStream;
+import java.io.DataInputStream;
+import java.io.FileInputStream;
+import java.util.Map;
+import java.util.Set;
+
+public class Main
+{
+ public static void main(String argv[]) {
+ FileInputStream fis;
+ BufferedInputStream bis;
+ DataInputStream dis;
+
+ try {
+ fis = new FileInputStream(argv[0]);
+ bis = new BufferedInputStream(fis);
+ dis = new DataInputStream(bis);
+
+ State state = (new HprofParser(dis)).parse();
+
+ dis.close();
+
+ testClassesQuery(state);
+ testAllClassesQuery(state);
+ testFindInstancesOf(state);
+ testFindAllInstancesOf(state);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private static void testClassesQuery(State state) {
+ String[] x = new String[] {
+ "char[",
+ "javax.",
+ "org.xml.sax"
+ };
+
+ Map<String, Set<ClassObj>> someClasses = Queries.classes(state, x);
+
+ for (String thePackage: someClasses.keySet()) {
+ System.out.println("------------------- " + thePackage);
+
+ Set<ClassObj> classes = someClasses.get(thePackage);
+
+ for (ClassObj theClass: classes) {
+ System.out.println(" " + theClass.mClassName);
+ }
+ }
+ }
+
+ private static void testAllClassesQuery(State state) {
+ Map<String, Set<ClassObj>> allClasses = Queries.allClasses(state);
+
+ for (String thePackage: allClasses.keySet()) {
+ System.out.println("------------------- " + thePackage);
+
+ Set<ClassObj> classes = allClasses.get(thePackage);
+
+ for (ClassObj theClass: classes) {
+ System.out.println(" " + theClass.mClassName);
+ }
+ }
+ }
+
+ private static void testFindInstancesOf(State state) {
+ Instance[] instances = Queries.instancesOf(state, "java.lang.String");
+
+ System.out.println("There are " + instances.length + " Strings.");
+ }
+
+ private static void testFindAllInstancesOf(State state) {
+ Instance[] instances = Queries.allInstancesOf(state,
+ "android.graphics.drawable.Drawable");
+
+ System.out.println("There are " + instances.length
+ + " instances of Drawables and its subclasses.");
+ }
+}
diff --git a/hit/src/com/android/hit/Queries.java b/hit/src/com/android/hit/Queries.java
new file mode 100644
index 0000000..0dac796
--- /dev/null
+++ b/hit/src/com/android/hit/Queries.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+public class Queries {
+ /*
+ * NOTES: Here's a list of the queries that can be done in hat and
+ * how you'd perform a similar query here in hit:
+ *
+ * hat hit
+ * ------------------------------------------------------------------------
+ * allClasses classes
+ * allClassesWithPlatform allClasses
+ * class findClass
+ * instances instancesOf
+ * allInstances allInstancesOf
+ * object findObject
+ * showRoots getRoots
+ * newInstances newInstances
+ *
+ * reachableFrom make a call to findObject to get the target
+ * parent object, this will give you an Instance.
+ * Then call visit(Set, Filter) on that to have
+ * it build the set of objects in its subgraph.
+ *
+ * rootsTo make a call to findObject on the leaf node
+ * in question, this will give you an Instance.
+ * Instances have an ArrayList of all of the
+ * parent objects that refer to it. You can
+ * follow those parent links until you hit an
+ * object whose parent is null or a ThreadObj.
+ * You've not successfully traced the paths to
+ * the roots.
+ */
+
+ private static final String DEFAULT_PACKAGE = "<default>";
+
+ /*
+ * Produce a collection of all classes, broken down by package.
+ * The keys of the resultant map iterate in sorted package order.
+ * The values of the map are the classes defined in each package.
+ */
+ public static Map<String, Set<ClassObj>> allClasses(State state) {
+ return classes(state, null);
+ }
+
+ public static Map<String, Set<ClassObj>> classes(State state,
+ String[] excludedPrefixes) {
+ TreeMap<String, Set<ClassObj>> result =
+ new TreeMap<String, Set<ClassObj>>();
+
+ Set<ClassObj> classes = new TreeSet<ClassObj>();
+
+ // Build a set of all classes across all heaps
+ for (Heap heap: state.mHeaps.values()) {
+ classes.addAll(heap.mClassesById.values());
+ }
+
+ // Filter it if needed
+ if (excludedPrefixes != null) {
+ final int N = excludedPrefixes.length;
+ Iterator<ClassObj> iter = classes.iterator();
+
+ while (iter.hasNext()) {
+ ClassObj theClass = iter.next();
+ String classPath = theClass.toString();
+
+ for (int i = 0; i < N; i++) {
+ if (classPath.startsWith(excludedPrefixes[i])) {
+ iter.remove();
+ break;
+ }
+ }
+ }
+ }
+
+ // Now that we have a final list of classes, group them by package
+ for (ClassObj theClass: classes) {
+ String packageName = DEFAULT_PACKAGE;
+ int lastDot = theClass.mClassName.lastIndexOf('.');
+
+ if (lastDot != -1) {
+ packageName = theClass.mClassName.substring(0, lastDot);
+ }
+
+ Set<ClassObj> classSet = result.get(packageName);
+
+ if (classSet == null) {
+ classSet = new TreeSet<ClassObj>();
+ result.put(packageName, classSet);
+ }
+
+ classSet.add(theClass);
+ }
+
+ return result;
+ }
+
+ /*
+ * It's sorta sad that this is a pass-through call, but it seems like
+ * having all of the hat-like query methods in one place is a good thing
+ * even if there is duplication of effort.
+ */
+ public static ClassObj findClass(State state, String name) {
+ return state.findClass(name);
+ }
+
+ /*
+ * Return an array of instances of the given class. This does not include
+ * instances of subclasses.
+ */
+ public static Instance[] instancesOf(State state, String baseClassName) {
+ ClassObj theClass = state.findClass(baseClassName);
+
+ if (theClass == null) {
+ throw new IllegalArgumentException("Class not found: "
+ + baseClassName);
+ }
+
+ Instance[] instances = new Instance[theClass.mInstances.size()];
+
+ return theClass.mInstances.toArray(instances);
+ }
+
+ /*
+ * Return an array of instances of the given class. This includes
+ * instances of subclasses.
+ */
+ public static Instance[] allInstancesOf(State state, String baseClassName) {
+ ClassObj theClass = state.findClass(baseClassName);
+
+ if (theClass == null) {
+ throw new IllegalArgumentException("Class not found: "
+ + baseClassName);
+ }
+
+ ArrayList<ClassObj> classList = new ArrayList<ClassObj>();
+
+ classList.add(theClass);
+ classList.addAll(traverseSubclasses(theClass));
+
+ ArrayList<Instance> instanceList = new ArrayList<Instance>();
+
+ for (ClassObj someClass: classList) {
+ instanceList.addAll(someClass.mInstances);
+ }
+
+ Instance[] result = new Instance[instanceList.size()];
+
+ instanceList.toArray(result);
+
+ return result;
+ }
+
+ private static ArrayList<ClassObj> traverseSubclasses(ClassObj base) {
+ ArrayList<ClassObj> result = new ArrayList<ClassObj>();
+
+ for (ClassObj subclass: base.mSubclasses) {
+ result.add(subclass);
+ result.addAll(traverseSubclasses(subclass));
+ }
+
+ return result;
+ }
+
+ /*
+ * Find a reference to an object based on its id. The id should be
+ * in hexadecimal.
+ */
+ public static Instance findObject(State state, String id) {
+ long id2 = Long.parseLong(id, 16);
+
+ return state.findReference(id2);
+ }
+
+ public static Collection<RootObj> getRoots(State state) {
+ HashSet<RootObj> result = new HashSet<RootObj>();
+
+ for (Heap heap: state.mHeaps.values()) {
+ result.addAll(heap.mRoots);
+ }
+
+ return result;
+ }
+
+ public static final Instance[] newInstances(State older, State newer) {
+ ArrayList<Instance> resultList = new ArrayList<Instance>();
+
+ for (Heap newHeap: newer.mHeaps.values()) {
+ Heap oldHeap = older.getHeap(newHeap.mName);
+
+ if (oldHeap == null) {
+ continue;
+ }
+
+ for (Instance instance: newHeap.mInstances.values()) {
+ Instance oldInstance = oldHeap.getInstance(instance.mId);
+
+ /*
+ * If this instance wasn't in the old heap, or was there,
+ * but that ID was for an obj of a different type, then we have
+ * a newly allocated object and we should report it in the
+ * results.
+ */
+ if ((oldInstance == null)
+ || (instance.mClassId != oldInstance.mClassId)) {
+ resultList.add(instance);
+ }
+ }
+ }
+
+ Instance[] resultArray = new Instance[resultList.size()];
+
+ return resultList.toArray(resultArray);
+ }
+}
diff --git a/hit/src/com/android/hit/RootObj.java b/hit/src/com/android/hit/RootObj.java
new file mode 100644
index 0000000..a9acd35
--- /dev/null
+++ b/hit/src/com/android/hit/RootObj.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+import java.util.Set;
+
+public class RootObj extends Instance {
+ RootType mType = RootType.UNKNOWN;
+ int mIndex;
+ int mThread;
+
+ /*
+ * These two fields are only used by roots that are static
+ * fields of class objects
+ */
+ long mParent;
+ String mComment;
+
+ public RootObj(RootType type) {
+ this(type, 0, 0, null);
+ }
+
+ public RootObj(RootType type, long id) {
+ this(type, id, 0, null);
+ }
+
+ public RootObj(RootType type, long id, int thread, StackTrace stack) {
+ mType = type;
+ mId = id;
+ mThread = thread;
+ mStack = stack;
+ }
+
+ public final String getClassName(State state) {
+ ClassObj theClass;
+
+ if (mType == RootType.SYSTEM_CLASS) {
+ theClass = state.findClass(mId);
+ } else {
+ Instance instance = state.findReference(mId);
+
+ theClass = state.findClass(instance.mClassId);
+ }
+
+ if (theClass == null) {
+ return "no class defined!!";
+ }
+
+ return theClass.mClassName;
+ }
+
+ @Override
+ public final int getSize() {
+ Instance instance = null;
+
+ if (mType == RootType.SYSTEM_CLASS) {
+ instance = mHeap.mState.findClass(mId);
+ } else {
+ instance = mHeap.mState.findReference(mId);
+ }
+
+ if (instance == null) {
+ return 0;
+ }
+
+ return instance.getSize();
+ }
+
+ @Override
+ public final void visit(Set<Instance> resultSet, Filter filter) {
+ if (resultSet.contains(this)) {
+ return;
+ }
+
+ if (filter != null) {
+ if (filter.accept(this)) {
+ resultSet.add(this);
+ }
+ } else {
+ resultSet.add(this);
+ }
+ }
+
+ @Override
+ public final void resolveReferences(State state) {
+ // Nothing to do here
+ }
+
+ @Override
+ public final String getTypeName() {
+ return "root " + mType.getName();
+ }
+
+ public final String toString() {
+ return String.format("%s@0x08x", mType.getName(), mId);
+ }
+}
diff --git a/hit/src/com/android/hit/RootType.java b/hit/src/com/android/hit/RootType.java
new file mode 100644
index 0000000..209ed1b
--- /dev/null
+++ b/hit/src/com/android/hit/RootType.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+public enum RootType {
+ UNREACHABLE (0, "unreachable object"),
+ INVALID_TYPE (1, "invalid type"),
+ INTERNED_STRING (2, "interned string"),
+ UNKNOWN (3, "unknown"),
+ SYSTEM_CLASS (4, "system class"),
+ VM_INTERNAL (5, "vm internal"),
+ DEBUGGER (6, "debugger"),
+ NATIVE_LOCAL (7, "native local"),
+ NATIVE_STATIC (8, "native static"),
+ THREAD_BLOCK (9, "thread block"),
+ BUSY_MONITOR (10, "busy monitor"),
+ NATIVE_MONITOR (11, "native monitor"),
+ REFERENCE_CLEANUP (12, "reference cleanup"),
+ FINALIZING (13, "finalizing"),
+ JAVA_LOCAL (14, "java local"),
+ NATIVE_STACK (15, "native stack"),
+ JAVA_STATIC (16, "java static");
+
+ private final int mType;
+ private final String mName;
+
+ RootType(int type, String name) {
+ mType = type;
+ mName = name;
+ }
+
+ public final int getType() {
+ return mType;
+ }
+
+ public final String getName() {
+ return mName;
+ }
+}
diff --git a/hit/src/com/android/hit/StackFrame.java b/hit/src/com/android/hit/StackFrame.java
new file mode 100644
index 0000000..2ae7f32
--- /dev/null
+++ b/hit/src/com/android/hit/StackFrame.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+public class StackFrame {
+ public static final int NO_LINE_NUMBER = 0;
+ public static final int UNKNOWN_LOCATION = -1;
+ public static final int COMPILED_METHOD = -2;
+ public static final int NATIVE_METHOD = -3;
+
+ long mId;
+ String mMethodName;
+ String mSignature;
+ String mFilename;
+ int mSerialNumber;
+ int mLineNumber;
+
+ public StackFrame(long id, String method, String sig, String file,
+ int serial, int line) {
+ mId = id;
+ mMethodName = method;
+ mSignature = sig;
+ mFilename = file;
+ mSerialNumber = serial;
+ mLineNumber = line;
+ }
+
+ private final String lineNumberString() {
+ switch (mLineNumber) {
+ case NO_LINE_NUMBER: return "No line number";
+ case UNKNOWN_LOCATION: return "Unknown line number";
+ case COMPILED_METHOD: return "Compiled method";
+ case NATIVE_METHOD: return "Native method";
+
+ default: return String.valueOf(mLineNumber);
+ }
+ }
+
+ public final String toString() {
+ return mMethodName
+ + mSignature.replace('/', '.')
+ + " - "
+ + mFilename + ":"
+ + lineNumberString();
+ }
+}
diff --git a/hit/src/com/android/hit/StackTrace.java b/hit/src/com/android/hit/StackTrace.java
new file mode 100644
index 0000000..53cb86d
--- /dev/null
+++ b/hit/src/com/android/hit/StackTrace.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+public class StackTrace {
+ int mSerialNumber;
+ int mThreadSerialNumber;
+ StackFrame[] mFrames;
+
+ /*
+ * For subsets of the stack frame we'll reference the parent list of frames
+ * but keep track of its offset into the parent's list of stack frame ids.
+ * This alleviates the need to constantly be duplicating subsections of the
+ * list of stack frame ids.
+ */
+ StackTrace mParent = null;
+ int mOffset = 0;
+
+ private StackTrace() {
+
+ }
+
+ public StackTrace(int serial, int thread, StackFrame[] frames) {
+ mSerialNumber = serial;
+ mThreadSerialNumber = thread;
+ mFrames = frames;
+ }
+
+ public final StackTrace fromDepth(int startingDepth) {
+ StackTrace result = new StackTrace();
+
+ if (mParent != null) {
+ result.mParent = mParent;
+ } else {
+ result.mParent = this;
+ }
+
+ result.mOffset = startingDepth + mOffset;
+
+ return result;
+ }
+
+ public final void dump() {
+ final int N = mFrames.length;
+
+ for (int i = 0; i < N; i++) {
+ System.out.println(mFrames[i].toString());
+ }
+ }
+}
diff --git a/hit/src/com/android/hit/State.java b/hit/src/com/android/hit/State.java
new file mode 100644
index 0000000..96c944d
--- /dev/null
+++ b/hit/src/com/android/hit/State.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/*
+ * State is a snapshot of all of the heaps, and related meta-data, for
+ * the runtime at a given instant.
+ *
+ * During parsing of the HPROF file HEAP_DUMP_INFO chunks change which heap
+ * is being referenced.
+ */
+public class State {
+ HashMap<Integer, Heap> mHeaps;
+ Heap mCurrentHeap;
+
+ public State() {
+ mHeaps = new HashMap<Integer, Heap>();
+ setToDefaultHeap();
+ }
+
+ public Heap setToDefaultHeap() {
+ return setHeapTo(0, "default");
+ }
+
+ public Heap setHeapTo(int id, String name) {
+ Heap heap = mHeaps.get(id);
+
+ if (heap == null) {
+ heap = new Heap(name);
+ heap.mState = this;
+ mHeaps.put(id, heap);
+ }
+
+ mCurrentHeap = heap;
+
+ return mCurrentHeap;
+ }
+
+ public Heap getHeap(int id) {
+ return mHeaps.get(id);
+ }
+
+ public Heap getHeap(String name) {
+ for (Heap heap: mHeaps.values()) {
+ if (heap.mName.equals(name)) {
+ return heap;
+ }
+ }
+
+ return null;
+ }
+
+ public final void addStackFrame(StackFrame theFrame) {
+ mCurrentHeap.addStackFrame(theFrame);
+ }
+
+ public final StackFrame getStackFrame(long id) {
+ return mCurrentHeap.getStackFrame(id);
+ }
+
+ public final void addStackTrace(StackTrace theTrace) {
+ mCurrentHeap.addStackTrace(theTrace);
+ }
+
+ public final StackTrace getStackTrace(int traceSerialNumber) {
+ return mCurrentHeap.getStackTrace(traceSerialNumber);
+ }
+
+ public final StackTrace getStackTraceAtDepth(int traceSerialNumber,
+ int depth) {
+ return mCurrentHeap.getStackTraceAtDepth(traceSerialNumber, depth);
+ }
+
+ public final void addRoot(RootObj root) {
+ mCurrentHeap.addRoot(root);
+ }
+
+ public final void addThread(ThreadObj thread, int serialNumber) {
+ mCurrentHeap.addThread(thread, serialNumber);
+ }
+
+ public final ThreadObj getThread(int serialNumber) {
+ return mCurrentHeap.getThread(serialNumber);
+ }
+
+ public final void addInstance(long id, Instance instance) {
+ mCurrentHeap.addInstance(id, instance);
+ }
+
+ public final void addClass(long id, ClassObj theClass) {
+ mCurrentHeap.addClass(id, theClass);
+ }
+
+ public final Instance findReference(long id) {
+ for (Heap heap: mHeaps.values()) {
+ Instance instance = heap.getInstance(id);
+
+ if (instance != null) {
+ return instance;
+ }
+ }
+
+ // Couldn't find an instance of a class, look for a class object
+ return findClass(id);
+ }
+
+ public final ClassObj findClass(long id) {
+ for (Heap heap: mHeaps.values()) {
+ ClassObj theClass = heap.getClass(id);
+
+ if (theClass != null) {
+ return theClass;
+ }
+ }
+
+ return null;
+ }
+
+ public final ClassObj findClass(String name) {
+ for (Heap heap: mHeaps.values()) {
+ ClassObj theClass = heap.getClass(name);
+
+ if (theClass != null) {
+ return theClass;
+ }
+ }
+
+ return null;
+ }
+
+ public final void dumpInstanceCounts() {
+ for (Heap heap: mHeaps.values()) {
+ System.out.println(
+ "+------------------ instance counts for heap: " + heap.mName);
+ heap.dumpInstanceCounts();
+ }
+ }
+
+ public final void dumpSizes() {
+ for (Heap heap: mHeaps.values()) {
+ System.out.println(
+ "+------------------ sizes for heap: " + heap.mName);
+ heap.dumpSizes();
+ }
+ }
+
+ public final void dumpSubclasses() {
+ for (Heap heap: mHeaps.values()) {
+ System.out.println(
+ "+------------------ subclasses for heap: " + heap.mName);
+ heap.dumpSubclasses();
+ }
+ }
+
+ public final void resolveReferences() {
+ for (Heap heap: mHeaps.values()) {
+ heap.resolveInstanceRefs(this);
+ heap.resolveClassStatics(this);
+ heap.resolveRoots(this);
+ }
+ }
+}
diff --git a/hit/src/com/android/hit/ThreadObj.java b/hit/src/com/android/hit/ThreadObj.java
new file mode 100644
index 0000000..32a73f0
--- /dev/null
+++ b/hit/src/com/android/hit/ThreadObj.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+public class ThreadObj {
+ long mId;
+ int mStackTrace;
+
+ public ThreadObj(long id, int stackTrace) {
+ mId = id;
+ mStackTrace = stackTrace;
+ }
+}
diff --git a/hit/src/com/android/hit/Types.java b/hit/src/com/android/hit/Types.java
new file mode 100644
index 0000000..62228ce
--- /dev/null
+++ b/hit/src/com/android/hit/Types.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hit;
+
+public class Types {
+ private static int mIdSize = 4;
+
+ public static final int OBJECT = 2;
+ public static final int BOOLEAN = 4;
+ public static final int CHAR = 5;
+ public static final int FLOAT = 6;
+ public static final int DOUBLE = 7;
+ public static final int BYTE = 8;
+ public static final int SHORT = 9;
+ public static final int INT = 10;
+ public static final int LONG = 11;
+
+ public static final void setIdSize(int size) {
+ mIdSize = size;
+ }
+
+ public static final int getTypeSize(int type) {
+ switch (type) {
+ case '[': return mIdSize; // array object
+ case 'L': return mIdSize; // object
+ case 'Z': return 1; // boolean
+ case 'C': return 2; // char
+ case 'F': return 4; // float
+ case 'D': return 8; // double
+ case 'B': return 1; // byte
+ case 'S': return 2; // short
+ case 'I': return 4; // int
+ case 'J': return 8; // long
+
+ case OBJECT: return mIdSize;
+ case BOOLEAN: return 1;
+ case CHAR: return 2;
+ case FLOAT: return 4;
+ case DOUBLE: return 8;
+ case BYTE: return 1;
+ case SHORT: return 2;
+ case INT: return 4;
+ case LONG: return 8;
+ }
+
+ throw new IllegalArgumentException("Illegal type signature: " + type);
+ }
+
+ public static final String getTypeName(int type) {
+ switch (type) {
+ case '[': return "array";
+ case 'L': return "object";
+ case 'Z': return "boolean";
+ case 'C': return "char";
+ case 'F': return "float";
+ case 'D': return "double";
+ case 'B': return "byte";
+ case 'S': return "short";
+ case 'I': return "int";
+ case 'J': return "long";
+
+ case OBJECT: return "object";
+ case BOOLEAN: return "boolean";
+ case CHAR: return "char";
+ case FLOAT: return "float";
+ case DOUBLE: return "double";
+ case BYTE: return "byte";
+ case SHORT: return "short";
+ case INT: return "int";
+ case LONG: return "long";
+ }
+
+ throw new IllegalArgumentException("Illegal type signature: " + type);
+ }
+}
diff --git a/hit/test/testparser b/hit/test/testparser
new file mode 100755
index 0000000..a3a722e
--- /dev/null
+++ b/hit/test/testparser
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+find ../src -name \*java | xargs javac -d build -Xlint:unchecked
+
+# debug launch line tha turns off the jit and runs a debug server
+#java -Djava.compiler=NONE -Xdebug -Xrunjdwp:transport=dt_socket,address=53635,server=y,suspend=y -cp build com.android.hit.Main ../samples/android.hprof
+
+java -cp build com.android.hit.Main ../samples/android.hprof
diff --git a/libdex/Android.mk b/libdex/Android.mk
new file mode 100644
index 0000000..558a7e5
--- /dev/null
+++ b/libdex/Android.mk
@@ -0,0 +1,68 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+dex_src_files := \
+ CmdUtils.c \
+ DexCatch.c \
+ DexClass.c \
+ DexDataMap.c \
+ DexFile.c \
+ DexInlines.c \
+ DexOptData.c \
+ DexProto.c \
+ DexSwapVerify.c \
+ InstrUtils.c \
+ Leb128.c \
+ OpCodeNames.c \
+ OptInvocation.c \
+ sha1.c \
+ SysUtil.c \
+ ZipArchive.c
+
+dex_include_files := \
+ dalvik \
+ $(JNI_H_INCLUDE) \
+ external/zlib \
+ external/safe-iop/include
+
+##
+##
+## Build the device version of libdex
+##
+##
+ifneq ($(SDK_ONLY),true) # SDK_only doesn't need device version
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(dex_src_files)
+LOCAL_C_INCLUDES += $(dex_include_files)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := libdex
+include $(BUILD_STATIC_LIBRARY)
+
+endif # !SDK_ONLY
+
+
+##
+##
+## Build the host version of libdex
+##
+##
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(dex_src_files)
+LOCAL_C_INCLUDES += $(dex_include_files)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := libdex
+include $(BUILD_HOST_STATIC_LIBRARY)
diff --git a/libdex/CmdUtils.c b/libdex/CmdUtils.c
new file mode 100644
index 0000000..e8fc635
--- /dev/null
+++ b/libdex/CmdUtils.c
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Some utility functions for use with command-line utilities.
+ */
+#include "DexFile.h"
+#include "ZipArchive.h"
+#include "CmdUtils.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+
+
+/*
+ * Extract "classes.dex" from archive file.
+ *
+ * If "quiet" is set, don't report common errors.
+ */
+UnzipToFileResult dexUnzipToFile(const char* zipFileName,
+ const char* outFileName, bool quiet)
+{
+ UnzipToFileResult result = kUTFRSuccess;
+ static const char* kFileToExtract = "classes.dex";
+ ZipArchive archive;
+ ZipEntry entry;
+ bool unlinkOnFailure = false;
+ int fd = -1;
+
+ if (dexZipOpenArchive(zipFileName, &archive) != 0) {
+ if (!quiet) {
+ fprintf(stderr, "Unable to open '%s' as zip archive\n",
+ zipFileName);
+ }
+ result = kUTFRNotZip;
+ goto bail;
+ }
+
+ fd = open(outFileName, O_WRONLY | O_CREAT | O_EXCL, 0600);
+ if (fd < 0) {
+ fprintf(stderr, "Unable to create output file '%s': %s\n",
+ outFileName, strerror(errno));
+ result = kUTFROutputFileProblem;
+ goto bail;
+ }
+
+ unlinkOnFailure = true;
+
+ entry = dexZipFindEntry(&archive, kFileToExtract);
+ if (entry == NULL) {
+ if (!quiet) {
+ fprintf(stderr, "Unable to find '%s' in '%s'\n",
+ kFileToExtract, zipFileName);
+ }
+ result = kUTFRNoClassesDex;
+ goto bail;
+ }
+
+ if (dexZipExtractEntryToFile(&archive, entry, fd) != 0) {
+ fprintf(stderr, "Extract of '%s' from '%s' failed\n",
+ kFileToExtract, zipFileName);
+ result = kUTFRBadZip;
+ goto bail;
+ }
+
+bail:
+ if (fd >= 0)
+ close(fd);
+ if (unlinkOnFailure && result != kUTFRSuccess)
+ unlink(outFileName);
+ dexZipCloseArchive(&archive);
+ return result;
+}
+
+/*
+ * Map the specified DEX file read-only (possibly after expanding it into a
+ * temp file from a Jar). Pass in a MemMapping struct to hold the info.
+ * If the file is an unoptimized DEX file, then byte-swapping and structural
+ * verification are performed on it before the memory is made read-only.
+ *
+ * The temp file is deleted after the map succeeds.
+ *
+ * This is intended for use by tools (e.g. dexdump) that need to get a
+ * read-only copy of a DEX file that could be in a number of different states.
+ *
+ * If "tempFileName" is NULL, a default value is used. The temp file is
+ * deleted after the map succeeds.
+ *
+ * If "quiet" is set, don't report common errors.
+ *
+ * Returns 0 (kUTFRSuccess) on success.
+ */
+UnzipToFileResult dexOpenAndMap(const char* fileName, const char* tempFileName,
+ MemMapping* pMap, bool quiet)
+{
+ UnzipToFileResult result = kUTFRGenericFailure;
+ int len = strlen(fileName);
+ char tempNameBuf[32];
+ bool removeTemp = false;
+ int fd = -1;
+
+ if (len < 5) {
+ if (!quiet) {
+ fprintf(stderr,
+ "ERROR: filename must end in .dex, .zip, .jar, or .apk\n");
+ }
+ result = kUTFRBadArgs;
+ goto bail;
+ }
+
+ if (strcasecmp(fileName + len -3, "dex") != 0) {
+ if (tempFileName == NULL) {
+ /*
+ * Try .zip/.jar/.apk, all of which are Zip archives with
+ * "classes.dex" inside. We need to extract the compressed
+ * data to a temp file, the location of which varies.
+ */
+ if (access("/tmp", W_OK) == 0)
+ sprintf(tempNameBuf, "/tmp/dex-temp-%d", getpid());
+ else
+ sprintf(tempNameBuf, "/sdcard/dex-temp-%d", getpid());
+
+ tempFileName = tempNameBuf;
+ }
+
+ result = dexUnzipToFile(fileName, tempFileName, quiet);
+
+ if (result == kUTFRSuccess) {
+ //printf("+++ Good unzip to '%s'\n", tempFileName);
+ fileName = tempFileName;
+ removeTemp = true;
+ } else if (result == kUTFRNotZip) {
+ if (!quiet) {
+ fprintf(stderr, "Not Zip, retrying as DEX\n");
+ }
+ } else {
+ if (!quiet && result == kUTFRNoClassesDex) {
+ fprintf(stderr, "Zip has no classes.dex\n");
+ }
+ goto bail;
+ }
+ }
+
+ result = kUTFRGenericFailure;
+
+ /*
+ * Pop open the (presumed) DEX file.
+ */
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+ fd = open(fileName, O_RDONLY | O_BINARY);
+ if (fd < 0) {
+ if (!quiet) {
+ fprintf(stderr, "ERROR: unable to open '%s': %s\n",
+ fileName, strerror(errno));
+ }
+ goto bail;
+ }
+
+ if (sysMapFileInShmemWritableReadOnly(fd, pMap) != 0) {
+ fprintf(stderr, "ERROR: Unable to map '%s'\n", fileName);
+ goto bail;
+ }
+
+ /*
+ * This call will fail if the file exists on a filesystem that
+ * doesn't support mprotect(). If that's the case, then the file
+ * will have already been mapped private-writable by the previous
+ * call, so we don't need to do anything special if this call
+ * returns non-zero.
+ */
+ sysChangeMapAccess(pMap->addr, pMap->length, true, pMap);
+
+ if (dexSwapAndVerifyIfNecessary(pMap->addr, pMap->length)) {
+ fprintf(stderr, "ERROR: Failed structural verification of '%s'\n",
+ fileName);
+ goto bail;
+ }
+
+ /*
+ * Similar to above, this call will fail if the file wasn't ever
+ * read-only to begin with. This is innocuous, though it is
+ * undesirable from a memory hygiene perspective.
+ */
+ sysChangeMapAccess(pMap->addr, pMap->length, false, pMap);
+
+ /*
+ * Success! Close the file and return with the start/length in pMap.
+ */
+ result = 0;
+
+bail:
+ if (fd >= 0)
+ close(fd);
+ if (removeTemp) {
+ if (unlink(tempFileName) != 0) {
+ fprintf(stderr, "WARNING: unable to remove temp '%s'\n",
+ tempFileName);
+ }
+ }
+ return result;
+}
diff --git a/libdex/CmdUtils.h b/libdex/CmdUtils.h
new file mode 100644
index 0000000..7a85287
--- /dev/null
+++ b/libdex/CmdUtils.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Access .dex (Dalvik Executable Format) files. The code here assumes that
+ * the DEX file has been rewritten (byte-swapped, word-aligned) and that
+ * the contents can be directly accessed as a collection of C arrays. Please
+ * see docs/dalvik/dex-format.html for a detailed description.
+ *
+ * The structure and field names were chosen to match those in the DEX spec.
+ *
+ * It's generally assumed that the DEX file will be stored in shared memory,
+ * obviating the need to copy code and constant pool entries into newly
+ * allocated storage. Maintaining local pointers to items in the shared area
+ * is valid and encouraged.
+ *
+ * All memory-mapped structures are 32-bit aligned unless otherwise noted.
+ */
+#ifndef _LIBDEX_CMDUTILS
+#define _LIBDEX_CMDUTILS
+
+/* encode the result of unzipping to a file */
+typedef enum UnzipToFileResult {
+ kUTFRSuccess = 0,
+ kUTFRGenericFailure,
+ kUTFRBadArgs,
+ kUTFRNotZip,
+ kUTFRNoClassesDex,
+ kUTFROutputFileProblem,
+ kUTFRBadZip,
+} UnzipToFileResult;
+
+/*
+ * Map the specified DEX file read-only (possibly after expanding it into a
+ * temp file from a Jar). Pass in a MemMapping struct to hold the info.
+ * If the file is an unoptimized DEX file, then byte-swapping and structural
+ * verification are performed on it before the memory is made read-only.
+ *
+ * The temp file is deleted after the map succeeds.
+ *
+ * This is intended for use by tools (e.g. dexdump) that need to get a
+ * read-only copy of a DEX file that could be in a number of different states.
+ *
+ * If "tempFileName" is NULL, a default value is used. The temp file is
+ * deleted after the map succeeds.
+ *
+ * If "quiet" is set, don't report common errors.
+ *
+ * Returns 0 (kUTFRSuccess) on success.
+ */
+UnzipToFileResult dexOpenAndMap(const char* fileName, const char* tempFileName,
+ MemMapping* pMap, bool quiet);
+
+/*
+ * Utility function to open a Zip archive, find "classes.dex", and extract
+ * it to a file.
+ */
+UnzipToFileResult dexUnzipToFile(const char* zipFileName,
+ const char* outFileName, bool quiet);
+
+#endif /*_LIBDEX_CMDUTILS*/
diff --git a/libdex/DexCatch.c b/libdex/DexCatch.c
new file mode 100644
index 0000000..ed97e87
--- /dev/null
+++ b/libdex/DexCatch.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Functions for dealing with try-catch info.
+ */
+
+#include "DexCatch.h"
+
+/* Get the first handler offset for the given DexCode.
+ * It's not 0 because the handlers list is prefixed with its size
+ * (in entries) as a uleb128. */
+u4 dexGetFirstHandlerOffset(const DexCode* pCode) {
+ if (pCode->triesSize == 0) {
+ return 0;
+ }
+
+ const u1* baseData = dexGetCatchHandlerData(pCode);
+ const u1* data = baseData;
+
+ readUnsignedLeb128(&data);
+
+ return data - baseData;
+}
+
+/* Get count of handler lists for the given DexCode. */
+u4 dexGetHandlersSize(const DexCode* pCode) {
+ if (pCode->triesSize == 0) {
+ return 0;
+ }
+
+ const u1* data = dexGetCatchHandlerData(pCode);
+
+ return readUnsignedLeb128(&data);
+}
+
+/* Helper for dexFindCatchHandlerOffset(), which does an actual search
+ * in the tries table. Returns -1 if there is no applicable handler. */
+int dexFindCatchHandlerOffset0(u2 triesSize, const DexTry* pTries,
+ u4 address) {
+ // Note: Signed type is important for max and min.
+ int min = 0;
+ int max = triesSize - 1;
+
+ while (max >= min) {
+ int guess = (min + max) >> 1;
+ const DexTry* pTry = &pTries[guess];
+ u4 start = pTry->startAddr;
+
+ if (address < start) {
+ max = guess - 1;
+ continue;
+ }
+
+ u4 end = start + pTry->insnCount;
+
+ if (address >= end) {
+ min = guess + 1;
+ continue;
+ }
+
+ // We have a winner!
+ return (int) pTry->handlerOff;
+ }
+
+ // No match.
+ return -1;
+}
+
+/* Get the handler offset just past the end of the one just iterated over.
+ * This ends the iteration if it wasn't already. */
+u4 dexCatchIteratorGetEndOffset(DexCatchIterator* pIterator,
+ const DexCode* pCode) {
+ while (dexCatchIteratorNext(pIterator) != NULL) /* empty */ ;
+
+ return (u4) (pIterator->pEncodedData - dexGetCatchHandlerData(pCode));
+}
diff --git a/libdex/DexCatch.h b/libdex/DexCatch.h
new file mode 100644
index 0000000..c0eec7c
--- /dev/null
+++ b/libdex/DexCatch.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Functions for dealing with try-catch info.
+ */
+
+#ifndef _LIBDEX_DEXCATCH
+#define _LIBDEX_DEXCATCH
+
+#include "DexFile.h"
+#include "Leb128.h"
+
+/*
+ * Catch handler entry, used while iterating over catch_handler_items.
+ */
+typedef struct DexCatchHandler {
+ u4 typeIdx; /* type index of the caught exception type */
+ u4 address; /* handler address */
+} DexCatchHandler;
+
+/* Get the first handler offset for the given DexCode.
+ * It's not 0 because the handlers list is prefixed with its size
+ * (in entries) as a uleb128. */
+u4 dexGetFirstHandlerOffset(const DexCode* pCode);
+
+/* Get count of handler lists for the given DexCode. */
+u4 dexGetHandlersSize(const DexCode* pCode);
+
+/*
+ * Iterator over catch handler data. This structure should be treated as
+ * opaque.
+ */
+typedef struct DexCatchIterator {
+ const u1* pEncodedData;
+ bool catchesAll;
+ u4 countRemaining;
+ DexCatchHandler handler;
+} DexCatchIterator;
+
+/* Initialize a DexCatchIterator to emptiness. This mostly exists to
+ * squelch innocuous warnings. */
+DEX_INLINE void dexCatchIteratorClear(DexCatchIterator* pIterator) {
+ pIterator->pEncodedData = NULL;
+ pIterator->catchesAll = false;
+ pIterator->countRemaining = 0;
+ pIterator->handler.typeIdx = 0;
+ pIterator->handler.address = 0;
+}
+
+/* Initialize a DexCatchIterator with a direct pointer to encoded handlers. */
+DEX_INLINE void dexCatchIteratorInitToPointer(DexCatchIterator* pIterator,
+ const u1* pEncodedData)
+{
+ s4 count = readSignedLeb128(&pEncodedData);
+
+ if (count <= 0) {
+ pIterator->catchesAll = true;
+ count = -count;
+ } else {
+ pIterator->catchesAll = false;
+ }
+
+ pIterator->pEncodedData = pEncodedData;
+ pIterator->countRemaining = count;
+}
+
+/* Initialize a DexCatchIterator to a particular handler offset. */
+DEX_INLINE void dexCatchIteratorInit(DexCatchIterator* pIterator,
+ const DexCode* pCode, u4 offset)
+{
+ dexCatchIteratorInitToPointer(pIterator,
+ dexGetCatchHandlerData(pCode) + offset);
+}
+
+/* Get the next item from a DexCatchIterator. Returns NULL if at end. */
+DEX_INLINE DexCatchHandler* dexCatchIteratorNext(DexCatchIterator* pIterator) {
+ if (pIterator->countRemaining == 0) {
+ if (! pIterator->catchesAll) {
+ return NULL;
+ }
+
+ pIterator->catchesAll = false;
+ pIterator->handler.typeIdx = kDexNoIndex;
+ } else {
+ u4 typeIdx = readUnsignedLeb128(&pIterator->pEncodedData);
+ pIterator->handler.typeIdx = typeIdx;
+ pIterator->countRemaining--;
+ }
+
+ pIterator->handler.address = readUnsignedLeb128(&pIterator->pEncodedData);
+ return &pIterator->handler;
+}
+
+/* Get the handler offset just past the end of the one just iterated over.
+ * This ends the iteration if it wasn't already. */
+u4 dexCatchIteratorGetEndOffset(DexCatchIterator* pIterator,
+ const DexCode* pCode);
+
+/* Helper for dexFindCatchHandler(). Do not call directly. */
+int dexFindCatchHandlerOffset0(u2 triesSize, const DexTry* pTries,
+ u4 address);
+
+/* Find the handler associated with a given address, if any.
+ * Initializes the given iterator and returns true if a match is
+ * found. Returns false if there is no applicable handler. */
+DEX_INLINE bool dexFindCatchHandler(DexCatchIterator *pIterator,
+ const DexCode* pCode, u4 address) {
+ u2 triesSize = pCode->triesSize;
+ int offset = -1;
+
+ // Short-circuit the overwhelmingly common cases.
+ switch (triesSize) {
+ case 0: {
+ break;
+ }
+ case 1: {
+ const DexTry* tries = dexGetTries(pCode);
+ u4 start = tries[0].startAddr;
+
+ if (address < start) {
+ break;
+ }
+
+ u4 end = start + tries[0].insnCount;
+
+ if (address >= end) {
+ break;
+ }
+
+ offset = tries[0].handlerOff;
+ break;
+ }
+ default: {
+ offset = dexFindCatchHandlerOffset0(triesSize, dexGetTries(pCode),
+ address);
+ }
+ }
+
+ if (offset < 0) {
+ dexCatchIteratorClear(pIterator); // This squelches warnings.
+ return false;
+ } else {
+ dexCatchIteratorInit(pIterator, pCode, offset);
+ return true;
+ }
+}
+
+#endif
diff --git a/libdex/DexClass.c b/libdex/DexClass.c
new file mode 100644
index 0000000..8a59e09
--- /dev/null
+++ b/libdex/DexClass.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Functions to deal with class definition structures in DEX files
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "DexClass.h"
+#include "Leb128.h"
+
+/* Helper for verification which reads and verifies a given number
+ * of uleb128 values. */
+static bool verifyUlebs(const u1* pData, const u1* pLimit, u4 count) {
+ bool okay = true;
+ u4 i;
+
+ while (okay && (count-- != 0)) {
+ readAndVerifyUnsignedLeb128(&pData, pLimit, &okay);
+ }
+
+ return okay;
+}
+
+/* Read and verify the header of a class_data_item. This updates the
+ * given data pointer to point past the end of the read data and
+ * returns an "okay" flag (that is, false == failure). */
+bool dexReadAndVerifyClassDataHeader(const u1** pData, const u1* pLimit,
+ DexClassDataHeader *pHeader) {
+ if (! verifyUlebs(*pData, pLimit, 4)) {
+ return false;
+ }
+
+ dexReadClassDataHeader(pData, pHeader);
+ return true;
+}
+
+/* Read and verify an encoded_field. This updates the
+ * given data pointer to point past the end of the read data and
+ * returns an "okay" flag (that is, false == failure).
+ *
+ * The lastIndex value should be set to 0 before the first field in
+ * a list is read. It is updated as fields are read and used in the
+ * decode process.
+ *
+ * The verification done by this function is of the raw data format
+ * only; it does not verify that access flags or indices
+ * are valid. */
+bool dexReadAndVerifyClassDataField(const u1** pData, const u1* pLimit,
+ DexField* pField, u4* lastIndex) {
+ if (! verifyUlebs(*pData, pLimit, 2)) {
+ return false;
+ }
+
+ dexReadClassDataField(pData, pField, lastIndex);
+ return true;
+}
+
+/* Read and verify an encoded_method. This updates the
+ * given data pointer to point past the end of the read data and
+ * returns an "okay" flag (that is, false == failure).
+ *
+ * The lastIndex value should be set to 0 before the first method in
+ * a list is read. It is updated as fields are read and used in the
+ * decode process.
+ *
+ * The verification done by this function is of the raw data format
+ * only; it does not verify that access flags, indices, or offsets
+ * are valid. */
+bool dexReadAndVerifyClassDataMethod(const u1** pData, const u1* pLimit,
+ DexMethod* pMethod, u4* lastIndex) {
+ if (! verifyUlebs(*pData, pLimit, 3)) {
+ return false;
+ }
+
+ dexReadClassDataMethod(pData, pMethod, lastIndex);
+ return true;
+}
+
+/* Read, verify, and return an entire class_data_item. This updates
+ * the given data pointer to point past the end of the read data. This
+ * function allocates a single chunk of memory for the result, which
+ * must subsequently be free()d. This function returns NULL if there
+ * was trouble parsing the data. If this function is passed NULL, it
+ * returns an initialized empty DexClassData structure.
+ *
+ * The verification done by this function is of the raw data format
+ * only; it does not verify that access flags, indices, or offsets
+ * are valid. */
+DexClassData* dexReadAndVerifyClassData(const u1** pData, const u1* pLimit) {
+ DexClassDataHeader header;
+ u4 lastIndex;
+
+ if (*pData == NULL) {
+ DexClassData* result = malloc(sizeof(DexClassData));
+ memset(result, 0, sizeof(*result));
+ return result;
+ }
+
+ if (! dexReadAndVerifyClassDataHeader(pData, pLimit, &header)) {
+ return NULL;
+ }
+
+ size_t resultSize = sizeof(DexClassData) +
+ (header.staticFieldsSize * sizeof(DexField)) +
+ (header.instanceFieldsSize * sizeof(DexField)) +
+ (header.directMethodsSize * sizeof(DexMethod)) +
+ (header.virtualMethodsSize * sizeof(DexMethod));
+
+ DexClassData* result = malloc(resultSize);
+ u1* ptr = ((u1*) result) + sizeof(DexClassData);
+ bool okay = true;
+ u4 i;
+
+ if (result == NULL) {
+ return NULL;
+ }
+
+ result->header = header;
+
+ if (header.staticFieldsSize != 0) {
+ result->staticFields = (DexField*) ptr;
+ ptr += header.staticFieldsSize * sizeof(DexField);
+ } else {
+ result->staticFields = NULL;
+ }
+
+ if (header.instanceFieldsSize != 0) {
+ result->instanceFields = (DexField*) ptr;
+ ptr += header.instanceFieldsSize * sizeof(DexField);
+ } else {
+ result->instanceFields = NULL;
+ }
+
+ if (header.directMethodsSize != 0) {
+ result->directMethods = (DexMethod*) ptr;
+ ptr += header.directMethodsSize * sizeof(DexMethod);
+ } else {
+ result->directMethods = NULL;
+ }
+
+ if (header.virtualMethodsSize != 0) {
+ result->virtualMethods = (DexMethod*) ptr;
+ } else {
+ result->virtualMethods = NULL;
+ }
+
+ lastIndex = 0;
+ for (i = 0; okay && (i < header.staticFieldsSize); i++) {
+ okay = dexReadAndVerifyClassDataField(pData, pLimit,
+ &result->staticFields[i], &lastIndex);
+ }
+
+ lastIndex = 0;
+ for (i = 0; okay && (i < header.instanceFieldsSize); i++) {
+ okay = dexReadAndVerifyClassDataField(pData, pLimit,
+ &result->instanceFields[i], &lastIndex);
+ }
+
+ lastIndex = 0;
+ for (i = 0; okay && (i < header.directMethodsSize); i++) {
+ okay = dexReadAndVerifyClassDataMethod(pData, pLimit,
+ &result->directMethods[i], &lastIndex);
+ }
+
+ lastIndex = 0;
+ for (i = 0; okay && (i < header.virtualMethodsSize); i++) {
+ okay = dexReadAndVerifyClassDataMethod(pData, pLimit,
+ &result->virtualMethods[i], &lastIndex);
+ }
+
+ if (! okay) {
+ free(result);
+ return NULL;
+ }
+
+ return result;
+}
diff --git a/libdex/DexClass.h b/libdex/DexClass.h
new file mode 100644
index 0000000..3d1e11b
--- /dev/null
+++ b/libdex/DexClass.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Functions to deal with class definition structures in DEX files
+ */
+
+#ifndef _LIBDEX_DEXCLASS
+#define _LIBDEX_DEXCLASS
+
+#include "DexFile.h"
+#include "Leb128.h"
+
+/* expanded form of a class_data_item header */
+typedef struct DexClassDataHeader {
+ u4 staticFieldsSize;
+ u4 instanceFieldsSize;
+ u4 directMethodsSize;
+ u4 virtualMethodsSize;
+} DexClassDataHeader;
+
+/* expanded form of encoded_field */
+typedef struct DexField {
+ u4 fieldIdx; /* index to a field_id_item */
+ u4 accessFlags;
+} DexField;
+
+/* expanded form of encoded_method */
+typedef struct DexMethod {
+ u4 methodIdx; /* index to a method_id_item */
+ u4 accessFlags;
+ u4 codeOff; /* file offset to a code_item */
+} DexMethod;
+
+/* expanded form of class_data_item. Note: If a particular item is
+ * absent (e.g., no static fields), then the corresponding pointer
+ * is set to NULL. */
+typedef struct DexClassData {
+ DexClassDataHeader header;
+ DexField* staticFields;
+ DexField* instanceFields;
+ DexMethod* directMethods;
+ DexMethod* virtualMethods;
+} DexClassData;
+
+/* Read and verify the header of a class_data_item. This updates the
+ * given data pointer to point past the end of the read data and
+ * returns an "okay" flag (that is, false == failure). */
+bool dexReadAndVerifyClassDataHeader(const u1** pData, const u1* pLimit,
+ DexClassDataHeader *pHeader);
+
+/* Read and verify an encoded_field. This updates the
+ * given data pointer to point past the end of the read data and
+ * returns an "okay" flag (that is, false == failure).
+ *
+ * The lastIndex value should be set to 0 before the first field in
+ * a list is read. It is updated as fields are read and used in the
+ * decode process.
+ *
+ * The verification done by this function is of the raw data format
+ * only; it does not verify that access flags or indices
+ * are valid. */
+bool dexReadAndVerifyClassDataField(const u1** pData, const u1* pLimit,
+ DexField* pField, u4* lastIndex);
+
+/* Read and verify an encoded_method. This updates the
+ * given data pointer to point past the end of the read data and
+ * returns an "okay" flag (that is, false == failure).
+ *
+ * The lastIndex value should be set to 0 before the first method in
+ * a list is read. It is updated as fields are read and used in the
+ * decode process.
+ *
+ * The verification done by this function is of the raw data format
+ * only; it does not verify that access flags, indices, or offsets
+ * are valid. */
+bool dexReadAndVerifyClassDataMethod(const u1** pData, const u1* pLimit,
+ DexMethod* pMethod, u4* lastIndex);
+
+/* Read, verify, and return an entire class_data_item. This updates
+ * the given data pointer to point past the end of the read data. This
+ * function allocates a single chunk of memory for the result, which
+ * must subsequently be free()d. This function returns NULL if there
+ * was trouble parsing the data. If this function is passed NULL, it
+ * returns an initialized empty DexClassData structure.
+ *
+ * The verification done by this function is of the raw data format
+ * only; it does not verify that access flags, indices, or offsets
+ * are valid. */
+DexClassData* dexReadAndVerifyClassData(const u1** pData, const u1* pLimit);
+
+/*
+ * Get the DexCode for a DexMethod. Returns NULL if the class is native
+ * or abstract.
+ */
+DEX_INLINE const DexCode* dexGetCode(const DexFile* pDexFile,
+ const DexMethod* pDexMethod)
+{
+ if (pDexMethod->codeOff == 0)
+ return NULL;
+ return (const DexCode*) (pDexFile->baseAddr + pDexMethod->codeOff);
+}
+
+
+/* Read the header of a class_data_item without verification. This
+ * updates the given data pointer to point past the end of the read
+ * data. */
+DEX_INLINE void dexReadClassDataHeader(const u1** pData,
+ DexClassDataHeader *pHeader) {
+ pHeader->staticFieldsSize = readUnsignedLeb128(pData);
+ pHeader->instanceFieldsSize = readUnsignedLeb128(pData);
+ pHeader->directMethodsSize = readUnsignedLeb128(pData);
+ pHeader->virtualMethodsSize = readUnsignedLeb128(pData);
+}
+
+/* Read an encoded_field without verification. This updates the
+ * given data pointer to point past the end of the read data.
+ *
+ * The lastIndex value should be set to 0 before the first field in
+ * a list is read. It is updated as fields are read and used in the
+ * decode process.
+ */
+DEX_INLINE void dexReadClassDataField(const u1** pData, DexField* pField,
+ u4* lastIndex) {
+ u4 index = *lastIndex + readUnsignedLeb128(pData);
+
+ pField->accessFlags = readUnsignedLeb128(pData);
+ pField->fieldIdx = index;
+ *lastIndex = index;
+}
+
+/* Read an encoded_method without verification. This updates the
+ * given data pointer to point past the end of the read data.
+ *
+ * The lastIndex value should be set to 0 before the first method in
+ * a list is read. It is updated as fields are read and used in the
+ * decode process.
+ */
+DEX_INLINE void dexReadClassDataMethod(const u1** pData, DexMethod* pMethod,
+ u4* lastIndex) {
+ u4 index = *lastIndex + readUnsignedLeb128(pData);
+
+ pMethod->accessFlags = readUnsignedLeb128(pData);
+ pMethod->codeOff = readUnsignedLeb128(pData);
+ pMethod->methodIdx = index;
+ *lastIndex = index;
+}
+
+#endif
diff --git a/libdex/DexDataMap.c b/libdex/DexDataMap.c
new file mode 100644
index 0000000..a9d429e
--- /dev/null
+++ b/libdex/DexDataMap.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Verification-time map of data section items
+ */
+
+#include "DexDataMap.h"
+#include <safe_iop.h>
+#include <stdlib.h>
+
+/*
+ * Allocate and initialize a DexDataMap. Returns NULL on failure.
+ */
+DexDataMap* dexDataMapAlloc(u4 maxCount) {
+ /*
+ * Allocate a single chunk for the DexDataMap per se as well as the
+ * two arrays.
+ */
+ size_t size = 0;
+ DexDataMap* map = NULL;
+
+ /*
+ * Avoiding pulling in safe_iop for safe_iopf.
+ */
+ if (!safe_mul(&size, maxCount, sizeof(u4) + sizeof(u2)) ||
+ !safe_add(&size, size, sizeof(DexDataMap))) {
+ return NULL;
+ }
+
+ map = malloc(size);
+
+ if (map == NULL) {
+ return NULL;
+ }
+
+ map->count = 0;
+ map->max = maxCount;
+ map->offsets = (u4*) (map + 1);
+ map->types = (u2*) (map->offsets + maxCount);
+
+ return map;
+}
+
+/*
+ * Free a DexDataMap.
+ */
+void dexDataMapFree(DexDataMap* map) {
+ /*
+ * Since everything got allocated together, everything can be freed
+ * in one fell swoop. Also, free(NULL) is a nop (per spec), so we
+ * don't have to worry about an explicit test for that.
+ */
+ free(map);
+}
+
+/*
+ * Add a new element to the map. The offset must be greater than the
+ * all previously added offsets.
+ */
+void dexDataMapAdd(DexDataMap* map, u4 offset, u2 type) {
+ assert(map != NULL);
+ assert(map->count < map->max);
+
+ if ((map->count != 0) &&
+ (map->offsets[map->count - 1] >= offset)) {
+ LOGE("Out-of-order data map offset: 0x%x then 0x%x\n",
+ map->offsets[map->count - 1], offset);
+ return;
+ }
+
+ map->offsets[map->count] = offset;
+ map->types[map->count] = type;
+ map->count++;
+}
+
+/*
+ * Get the type associated with the given offset. This returns -1 if
+ * there is no entry for the given offset.
+ */
+int dexDataMapGet(DexDataMap* map, u4 offset) {
+ assert(map != NULL);
+
+ // Note: Signed type is important for max and min.
+ int min = 0;
+ int max = map->count - 1;
+ u4* offsets = map->offsets;
+
+ while (max >= min) {
+ int guessIdx = (min + max) >> 1;
+ u4 guess = offsets[guessIdx];
+
+ if (offset < guess) {
+ max = guessIdx - 1;
+ } else if (offset > guess) {
+ min = guessIdx + 1;
+ } else {
+ // We have a winner!
+ return map->types[guessIdx];
+ }
+ }
+
+ // No match.
+ return -1;
+}
+
+/*
+ * Verify that there is an entry in the map, mapping the given offset to
+ * the given type. This will return true if such an entry exists and
+ * return false as well as log an error if not.
+ */
+bool dexDataMapVerify(DexDataMap* map, u4 offset, u2 type) {
+ int found = dexDataMapGet(map, offset);
+
+ if (found == type) {
+ return true;
+ }
+
+ if (found < 0) {
+ LOGE("No data map entry found @ 0x%x; expected %x\n",
+ offset, type);
+ } else {
+ LOGE("Unexpected data map entry @ 0x%x: expected %x, found %x\n",
+ offset, type, found);
+ }
+
+ return false;
+}
diff --git a/libdex/DexDataMap.h b/libdex/DexDataMap.h
new file mode 100644
index 0000000..fa556d5
--- /dev/null
+++ b/libdex/DexDataMap.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Verification-time map of data section items
+ */
+
+#ifndef _LIBDEX_DEXDATAMAP
+#define _LIBDEX_DEXDATAMAP
+
+#include "DexFile.h"
+
+typedef struct DexDataMap {
+ u4 count; /* number of items currently in the map */
+ u4 max; /* maximum number of items that may be held */
+ u4* offsets; /* array of item offsets */
+ u2* types; /* corresponding array of item types */
+} DexDataMap;
+
+/*
+ * Allocate and initialize a DexDataMap. Returns NULL on failure.
+ */
+DexDataMap* dexDataMapAlloc(u4 maxCount);
+
+/*
+ * Free a DexDataMap.
+ */
+void dexDataMapFree(DexDataMap* map);
+
+/*
+ * Add a new element to the map. The offset must be greater than the
+ * all previously added offsets.
+ */
+void dexDataMapAdd(DexDataMap* map, u4 offset, u2 type);
+
+/*
+ * Get the type associated with the given offset. This returns -1 if
+ * there is no entry for the given offset.
+ */
+int dexDataMapGet(DexDataMap* map, u4 offset);
+
+/*
+ * Verify that there is an entry in the map, mapping the given offset to
+ * the given type. This will return true if such an entry exists and
+ * return false as well as log an error if not.
+ */
+bool dexDataMapVerify(DexDataMap* map, u4 offset, u2 type);
+
+/*
+ * Like dexDataMapVerify(), but also accept a 0 offset as valid.
+ */
+DEX_INLINE bool dexDataMapVerify0Ok(DexDataMap* map, u4 offset, u2 type) {
+ if (offset == 0) {
+ return true;
+ }
+
+ return dexDataMapVerify(map, offset, type);
+}
+
+#endif /*_LIBDEX_DEXDATAMAP*/
diff --git a/libdex/DexFile.c b/libdex/DexFile.c
new file mode 100644
index 0000000..f997b94
--- /dev/null
+++ b/libdex/DexFile.c
@@ -0,0 +1,1032 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Access the contents of a .dex file.
+ */
+
+#include "DexFile.h"
+#include "DexOptData.h"
+#include "DexProto.h"
+#include "DexCatch.h"
+#include "Leb128.h"
+#include "sha1.h"
+#include "ZipArchive.h"
+
+#include <zlib.h>
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+
+
+/*
+ * Verifying checksums is good, but it slows things down and causes us to
+ * touch every page. In the "optimized" world, it doesn't work at all,
+ * because we rewrite the contents.
+ */
+static const bool kVerifyChecksum = false;
+static const bool kVerifySignature = false;
+
+
+/* Compare two '\0'-terminated modified UTF-8 strings, using Unicode
+ * code point values for comparison. This treats different encodings
+ * for the same code point as equivalent, except that only a real '\0'
+ * byte is considered the string terminator. The return value is as
+ * for strcmp(). */
+int dexUtf8Cmp(const char* s1, const char* s2) {
+ for (;;) {
+ if (*s1 == '\0') {
+ if (*s2 == '\0') {
+ return 0;
+ }
+ return -1;
+ } else if (*s2 == '\0') {
+ return 1;
+ }
+
+ int utf1 = dexGetUtf16FromUtf8(&s1);
+ int utf2 = dexGetUtf16FromUtf8(&s2);
+ int diff = utf1 - utf2;
+
+ if (diff != 0) {
+ return diff;
+ }
+ }
+}
+
+/* for dexIsValidMemberNameUtf8(), a bit vector indicating valid low ascii */
+u4 DEX_MEMBER_VALID_LOW_ASCII[4] = {
+ 0x00000000, // 00..1f low control characters; nothing valid
+ 0x03ff2010, // 20..3f digits and symbols; valid: '0'..'9', '$', '-'
+ 0x87fffffe, // 40..5f uppercase etc.; valid: 'A'..'Z', '_'
+ 0x07fffffe // 60..7f lowercase etc.; valid: 'a'..'z'
+};
+
+/* Helper for dexIsValidMemberNameUtf8(); do not call directly. */
+bool dexIsValidMemberNameUtf8_0(const char** pUtf8Ptr) {
+ /*
+ * It's a multibyte encoded character. Decode it and analyze. We
+ * accept anything that isn't (a) an improperly encoded low value,
+ * (b) an improper surrogate pair, (c) an encoded '\0', (d) a high
+ * control character, or (e) a high space, layout, or special
+ * character (U+00a0, U+2000..U+200f, U+2028..U+202f,
+ * U+fff0..U+ffff).
+ */
+
+ u2 utf16 = dexGetUtf16FromUtf8(pUtf8Ptr);
+
+ // Perform follow-up tests based on the high 8 bits.
+ switch (utf16 >> 8) {
+ case 0x00: {
+ // It's only valid if it's above the ISO-8859-1 high space (0xa0).
+ return (utf16 > 0x00a0);
+ }
+ case 0xd8:
+ case 0xd9:
+ case 0xda:
+ case 0xdb: {
+ /*
+ * It's a leading surrogate. Check to see that a trailing
+ * surrogate follows.
+ */
+ utf16 = dexGetUtf16FromUtf8(pUtf8Ptr);
+ return (utf16 >= 0xdc00) && (utf16 <= 0xdfff);
+ }
+ case 0xdc:
+ case 0xdd:
+ case 0xde:
+ case 0xdf: {
+ // It's a trailing surrogate, which is not valid at this point.
+ return false;
+ }
+ case 0x20:
+ case 0xff: {
+ // It's in the range that has spaces, controls, and specials.
+ switch (utf16 & 0xfff8) {
+ case 0x2000:
+ case 0x2008:
+ case 0x2028:
+ case 0xfff0:
+ case 0xfff8: {
+ return false;
+ }
+ }
+ break;
+ }
+ }
+
+ return true;
+}
+
+/* Return whether the given string is a valid field or method name. */
+bool dexIsValidMemberName(const char* s) {
+ bool angleName = false;
+
+ switch (*s) {
+ case '\0': {
+ // The empty string is not a valid name.
+ return false;
+ }
+ case '<': {
+ /*
+ * '<' is allowed only at the start of a name, and if present,
+ * means that the name must end with '>'.
+ */
+ angleName = true;
+ s++;
+ break;
+ }
+ }
+
+ for (;;) {
+ switch (*s) {
+ case '\0': {
+ return !angleName;
+ }
+ case '>': {
+ return angleName && s[1] == '\0';
+ }
+ }
+ if (!dexIsValidMemberNameUtf8(&s)) {
+ return false;
+ }
+ }
+}
+
+/* Return whether the given string is a valid type descriptor. */
+bool dexIsValidTypeDescriptor(const char* s) {
+ int arrayCount = 0;
+
+ while (*s == '[') {
+ arrayCount++;
+ s++;
+ }
+
+ if (arrayCount > 255) {
+ // Arrays may have no more than 255 dimensions.
+ return false;
+ }
+
+ switch (*(s++)) {
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'F':
+ case 'I':
+ case 'J':
+ case 'S':
+ case 'Z': {
+ // These are all single-character descriptors for primitive types.
+ return (*s == '\0');
+ }
+ case 'V': {
+ // You can't have an array of void.
+ return (arrayCount == 0) && (*s == '\0');
+ }
+ case 'L': {
+ // Break out and continue below.
+ break;
+ }
+ default: {
+ // Oddball descriptor character.
+ return false;
+ }
+ }
+
+ // We just consumed the 'L' that introduces a class name.
+
+ bool slashOrFirst = true; // first character or just encountered a slash
+ for (;;) {
+ u1 c = (u1) *s;
+ switch (c) {
+ case '\0': {
+ // Premature end.
+ return false;
+ }
+ case ';': {
+ /*
+ * Make sure that this is the end of the string and that
+ * it doesn't end with an empty component (including the
+ * degenerate case of "L;").
+ */
+ return (s[1] == '\0') && !slashOrFirst;
+ }
+ case '/': {
+ if (slashOrFirst) {
+ // Slash at start or two slashes in a row.
+ return false;
+ }
+ slashOrFirst = true;
+ s++;
+ break;
+ }
+ default: {
+ if (!dexIsValidMemberNameUtf8(&s)) {
+ return false;
+ }
+ slashOrFirst = false;
+ break;
+ }
+ }
+ }
+}
+
+/* Return whether the given string is a valid reference descriptor. This
+ * is true if dexIsValidTypeDescriptor() returns true and the descriptor
+ * is for a class or array and not a primitive type. */
+bool dexIsReferenceDescriptor(const char* s) {
+ if (!dexIsValidTypeDescriptor(s)) {
+ return false;
+ }
+
+ return (s[0] == 'L') || (s[0] == '[');
+}
+
+/* Return whether the given string is a valid class descriptor. This
+ * is true if dexIsValidTypeDescriptor() returns true and the descriptor
+ * is for a class and not an array or primitive type. */
+bool dexIsClassDescriptor(const char* s) {
+ if (!dexIsValidTypeDescriptor(s)) {
+ return false;
+ }
+
+ return s[0] == 'L';
+}
+
+/* Return whether the given string is a valid field type descriptor. This
+ * is true if dexIsValidTypeDescriptor() returns true and the descriptor
+ * is for anything but "void". */
+bool dexIsFieldDescriptor(const char* s) {
+ if (!dexIsValidTypeDescriptor(s)) {
+ return false;
+ }
+
+ return s[0] != 'V';
+}
+
+/* Return the UTF-8 encoded string with the specified string_id index,
+ * also filling in the UTF-16 size (number of 16-bit code points).*/
+const char* dexStringAndSizeById(const DexFile* pDexFile, u4 idx,
+ u4* utf16Size) {
+ const DexStringId* pStringId = dexGetStringId(pDexFile, idx);
+ const u1* ptr = pDexFile->baseAddr + pStringId->stringDataOff;
+
+ *utf16Size = readUnsignedLeb128(&ptr);
+ return (const char*) ptr;
+}
+
+/*
+ * Format an SHA-1 digest for printing. tmpBuf must be able to hold at
+ * least kSHA1DigestOutputLen bytes.
+ */
+const char* dvmSHA1DigestToStr(const unsigned char digest[], char* tmpBuf);
+
+/*
+ * Compute a SHA-1 digest on a range of bytes.
+ */
+static void dexComputeSHA1Digest(const unsigned char* data, size_t length,
+ unsigned char digest[])
+{
+ SHA1_CTX context;
+ SHA1Init(&context);
+ SHA1Update(&context, data, length);
+ SHA1Final(digest, &context);
+}
+
+/*
+ * Format the SHA-1 digest into the buffer, which must be able to hold at
+ * least kSHA1DigestOutputLen bytes. Returns a pointer to the buffer,
+ */
+static const char* dexSHA1DigestToStr(const unsigned char digest[],char* tmpBuf)
+{
+ static const char hexDigit[] = "0123456789abcdef";
+ char* cp;
+ int i;
+
+ cp = tmpBuf;
+ for (i = 0; i < kSHA1DigestLen; i++) {
+ *cp++ = hexDigit[digest[i] >> 4];
+ *cp++ = hexDigit[digest[i] & 0x0f];
+ }
+ *cp++ = '\0';
+
+ assert(cp == tmpBuf + kSHA1DigestOutputLen);
+
+ return tmpBuf;
+}
+
+/*
+ * Compute a hash code on a UTF-8 string, for use with internal hash tables.
+ *
+ * This may or may not be compatible with UTF-8 hash functions used inside
+ * the Dalvik VM.
+ *
+ * The basic "multiply by 31 and add" approach does better on class names
+ * than most other things tried (e.g. adler32).
+ */
+static u4 classDescriptorHash(const char* str)
+{
+ u4 hash = 1;
+
+ while (*str != '\0')
+ hash = hash * 31 + *str++;
+
+ return hash;
+}
+
+/*
+ * Add an entry to the class lookup table. We hash the string and probe
+ * until we find an open slot.
+ */
+static void classLookupAdd(DexFile* pDexFile, DexClassLookup* pLookup,
+ int stringOff, int classDefOff, int* pNumProbes)
+{
+ const char* classDescriptor =
+ (const char*) (pDexFile->baseAddr + stringOff);
+ const DexClassDef* pClassDef =
+ (const DexClassDef*) (pDexFile->baseAddr + classDefOff);
+ u4 hash = classDescriptorHash(classDescriptor);
+ int mask = pLookup->numEntries-1;
+ int idx = hash & mask;
+
+ /*
+ * Find the first empty slot. We oversized the table, so this is
+ * guaranteed to finish.
+ */
+ int probes = 0;
+ while (pLookup->table[idx].classDescriptorOffset != 0) {
+ idx = (idx + 1) & mask;
+ probes++;
+ }
+ //if (probes > 1)
+ // LOGW("classLookupAdd: probes=%d\n", probes);
+
+ pLookup->table[idx].classDescriptorHash = hash;
+ pLookup->table[idx].classDescriptorOffset = stringOff;
+ pLookup->table[idx].classDefOffset = classDefOff;
+ *pNumProbes = probes;
+}
+
+/*
+ * Round up to the next highest power of 2.
+ *
+ * Found on http://graphics.stanford.edu/~seander/bithacks.html.
+ */
+u4 dexRoundUpPower2(u4 val)
+{
+ val--;
+ val |= val >> 1;
+ val |= val >> 2;
+ val |= val >> 4;
+ val |= val >> 8;
+ val |= val >> 16;
+ val++;
+
+ return val;
+}
+
+/*
+ * Create the class lookup hash table.
+ *
+ * Returns newly-allocated storage.
+ */
+DexClassLookup* dexCreateClassLookup(DexFile* pDexFile)
+{
+ DexClassLookup* pLookup;
+ int allocSize;
+ int i, numEntries;
+ int numProbes, totalProbes, maxProbes;
+
+ numProbes = totalProbes = maxProbes = 0;
+
+ assert(pDexFile != NULL);
+
+ /*
+ * Using a factor of 3 results in far less probing than a factor of 2,
+ * but almost doubles the flash storage requirements for the bootstrap
+ * DEX files. The overall impact on class loading performance seems
+ * to be minor. We could probably get some performance improvement by
+ * using a secondary hash.
+ */
+ numEntries = dexRoundUpPower2(pDexFile->pHeader->classDefsSize * 2);
+ allocSize = offsetof(DexClassLookup, table)
+ + numEntries * sizeof(pLookup->table[0]);
+
+ pLookup = (DexClassLookup*) calloc(1, allocSize);
+ if (pLookup == NULL)
+ return NULL;
+ pLookup->size = allocSize;
+ pLookup->numEntries = numEntries;
+
+ for (i = 0; i < (int)pDexFile->pHeader->classDefsSize; i++) {
+ const DexClassDef* pClassDef;
+ const char* pString;
+
+ pClassDef = dexGetClassDef(pDexFile, i);
+ pString = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
+
+ classLookupAdd(pDexFile, pLookup,
+ (u1*)pString - pDexFile->baseAddr,
+ (u1*)pClassDef - pDexFile->baseAddr, &numProbes);
+
+ if (numProbes > maxProbes)
+ maxProbes = numProbes;
+ totalProbes += numProbes;
+ }
+
+ LOGV("Class lookup: classes=%d slots=%d (%d%% occ) alloc=%d"
+ " total=%d max=%d\n",
+ pDexFile->pHeader->classDefsSize, numEntries,
+ (100 * pDexFile->pHeader->classDefsSize) / numEntries,
+ allocSize, totalProbes, maxProbes);
+
+ return pLookup;
+}
+
+
+/*
+ * Set up the basic raw data pointers of a DexFile. This function isn't
+ * meant for general use.
+ */
+void dexFileSetupBasicPointers(DexFile* pDexFile, const u1* data) {
+ DexHeader *pHeader = (DexHeader*) data;
+
+ pDexFile->baseAddr = data;
+ pDexFile->pHeader = pHeader;
+ pDexFile->pStringIds = (const DexStringId*) (data + pHeader->stringIdsOff);
+ pDexFile->pTypeIds = (const DexTypeId*) (data + pHeader->typeIdsOff);
+ pDexFile->pFieldIds = (const DexFieldId*) (data + pHeader->fieldIdsOff);
+ pDexFile->pMethodIds = (const DexMethodId*) (data + pHeader->methodIdsOff);
+ pDexFile->pProtoIds = (const DexProtoId*) (data + pHeader->protoIdsOff);
+ pDexFile->pClassDefs = (const DexClassDef*) (data + pHeader->classDefsOff);
+ pDexFile->pLinkData = (const DexLink*) (data + pHeader->linkOff);
+}
+
+/*
+ * Parse an optimized or unoptimized .dex file sitting in memory. This is
+ * called after the byte-ordering and structure alignment has been fixed up.
+ *
+ * On success, return a newly-allocated DexFile.
+ */
+DexFile* dexFileParse(const u1* data, size_t length, int flags)
+{
+ DexFile* pDexFile = NULL;
+ const DexHeader* pHeader;
+ const u1* magic;
+ int result = -1;
+
+ if (length < sizeof(DexHeader)) {
+ LOGE("too short to be a valid .dex\n");
+ goto bail; /* bad file format */
+ }
+
+ pDexFile = (DexFile*) malloc(sizeof(DexFile));
+ if (pDexFile == NULL)
+ goto bail; /* alloc failure */
+ memset(pDexFile, 0, sizeof(DexFile));
+
+ /*
+ * Peel off the optimized header.
+ */
+ if (memcmp(data, DEX_OPT_MAGIC, 4) == 0) {
+ magic = data;
+ if (memcmp(magic+4, DEX_OPT_MAGIC_VERS, 4) != 0) {
+ LOGE("bad opt version (0x%02x %02x %02x %02x)\n",
+ magic[4], magic[5], magic[6], magic[7]);
+ goto bail;
+ }
+
+ pDexFile->pOptHeader = (const DexOptHeader*) data;
+ LOGV("Good opt header, DEX offset is %d, flags=0x%02x\n",
+ pDexFile->pOptHeader->dexOffset, pDexFile->pOptHeader->flags);
+
+ /* parse the optimized dex file tables */
+ if (!dexParseOptData(data, length, pDexFile))
+ goto bail;
+
+ /* ignore the opt header and appended data from here on out */
+ data += pDexFile->pOptHeader->dexOffset;
+ length -= pDexFile->pOptHeader->dexOffset;
+ if (pDexFile->pOptHeader->dexLength > length) {
+ LOGE("File truncated? stored len=%d, rem len=%d\n",
+ pDexFile->pOptHeader->dexLength, (int) length);
+ goto bail;
+ }
+ length = pDexFile->pOptHeader->dexLength;
+ }
+
+ dexFileSetupBasicPointers(pDexFile, data);
+ pHeader = pDexFile->pHeader;
+
+ magic = pHeader->magic;
+ if (memcmp(magic, DEX_MAGIC, 4) != 0) {
+ /* not expected */
+ LOGE("bad magic number (0x%02x %02x %02x %02x)\n",
+ magic[0], magic[1], magic[2], magic[3]);
+ goto bail;
+ }
+ if (memcmp(magic+4, DEX_MAGIC_VERS, 4) != 0) {
+ LOGE("bad dex version (0x%02x %02x %02x %02x)\n",
+ magic[4], magic[5], magic[6], magic[7]);
+ goto bail;
+ }
+
+ /*
+ * Verify the checksum(s). This is reasonably quick, but does require
+ * touching every byte in the DEX file. The base checksum changes after
+ * byte-swapping and DEX optimization.
+ */
+ if (flags & kDexParseVerifyChecksum) {
+ u4 adler = dexComputeChecksum(pHeader);
+ if (adler != pHeader->checksum) {
+ LOGE("ERROR: bad checksum (%08x vs %08x)\n",
+ adler, pHeader->checksum);
+ if (!(flags & kDexParseContinueOnError))
+ goto bail;
+ } else {
+ LOGV("+++ adler32 checksum (%08x) verified\n", adler);
+ }
+
+ const DexOptHeader* pOptHeader = pDexFile->pOptHeader;
+ if (pOptHeader != NULL) {
+ adler = dexComputeOptChecksum(pOptHeader);
+ if (adler != pOptHeader->checksum) {
+ LOGE("ERROR: bad opt checksum (%08x vs %08x)\n",
+ adler, pOptHeader->checksum);
+ if (!(flags & kDexParseContinueOnError))
+ goto bail;
+ } else {
+ LOGV("+++ adler32 opt checksum (%08x) verified\n", adler);
+ }
+ }
+ }
+
+ /*
+ * Verify the SHA-1 digest. (Normally we don't want to do this --
+ * the digest is used to uniquely identify the original DEX file, and
+ * can't be computed for verification after the DEX is byte-swapped
+ * and optimized.)
+ */
+ if (kVerifySignature) {
+ unsigned char sha1Digest[kSHA1DigestLen];
+ const int nonSum = sizeof(pHeader->magic) + sizeof(pHeader->checksum) +
+ kSHA1DigestLen;
+
+ dexComputeSHA1Digest(data + nonSum, length - nonSum, sha1Digest);
+ if (memcmp(sha1Digest, pHeader->signature, kSHA1DigestLen) != 0) {
+ char tmpBuf1[kSHA1DigestOutputLen];
+ char tmpBuf2[kSHA1DigestOutputLen];
+ LOGE("ERROR: bad SHA1 digest (%s vs %s)\n",
+ dexSHA1DigestToStr(sha1Digest, tmpBuf1),
+ dexSHA1DigestToStr(pHeader->signature, tmpBuf2));
+ if (!(flags & kDexParseContinueOnError))
+ goto bail;
+ } else {
+ LOGV("+++ sha1 digest verified\n");
+ }
+ }
+
+ if (pHeader->fileSize != length) {
+ LOGE("ERROR: stored file size (%d) != expected (%d)\n",
+ (int) pHeader->fileSize, (int) length);
+ if (!(flags & kDexParseContinueOnError))
+ goto bail;
+ }
+
+ if (pHeader->classDefsSize == 0) {
+ LOGE("ERROR: DEX file has no classes in it, failing\n");
+ goto bail;
+ }
+
+ /*
+ * Success!
+ */
+ result = 0;
+
+bail:
+ if (result != 0 && pDexFile != NULL) {
+ dexFileFree(pDexFile);
+ pDexFile = NULL;
+ }
+ return pDexFile;
+}
+
+/*
+ * Free up the DexFile and any associated data structures.
+ *
+ * Note we may be called with a partially-initialized DexFile.
+ */
+void dexFileFree(DexFile* pDexFile)
+{
+ if (pDexFile == NULL)
+ return;
+
+ free(pDexFile);
+}
+
+/*
+ * Look up a class definition entry by descriptor.
+ *
+ * "descriptor" should look like "Landroid/debug/Stuff;".
+ */
+const DexClassDef* dexFindClass(const DexFile* pDexFile,
+ const char* descriptor)
+{
+ const DexClassLookup* pLookup = pDexFile->pClassLookup;
+ u4 hash;
+ int idx, mask;
+
+ hash = classDescriptorHash(descriptor);
+ mask = pLookup->numEntries - 1;
+ idx = hash & mask;
+
+ /*
+ * Search until we find a matching entry or an empty slot.
+ */
+ while (true) {
+ int offset;
+
+ offset = pLookup->table[idx].classDescriptorOffset;
+ if (offset == 0)
+ return NULL;
+
+ if (pLookup->table[idx].classDescriptorHash == hash) {
+ const char* str;
+
+ str = (const char*) (pDexFile->baseAddr + offset);
+ if (strcmp(str, descriptor) == 0) {
+ return (const DexClassDef*)
+ (pDexFile->baseAddr + pLookup->table[idx].classDefOffset);
+ }
+ }
+
+ idx = (idx + 1) & mask;
+ }
+}
+
+
+/*
+ * Compute the DEX file checksum for a memory-mapped DEX file.
+ */
+u4 dexComputeChecksum(const DexHeader* pHeader)
+{
+ const u1* start = (const u1*) pHeader;
+
+ uLong adler = adler32(0L, Z_NULL, 0);
+ const int nonSum = sizeof(pHeader->magic) + sizeof(pHeader->checksum);
+
+ return (u4) adler32(adler, start + nonSum, pHeader->fileSize - nonSum);
+}
+
+/*
+ * Compute the size, in bytes, of a DexCode.
+ */
+size_t dexGetDexCodeSize(const DexCode* pCode)
+{
+ /*
+ * The catch handler data is the last entry. It has a variable number
+ * of variable-size pieces, so we need to create an iterator.
+ */
+ u4 handlersSize;
+ u4 offset;
+ u4 ui;
+
+ if (pCode->triesSize != 0) {
+ handlersSize = dexGetHandlersSize(pCode);
+ offset = dexGetFirstHandlerOffset(pCode);
+ } else {
+ handlersSize = 0;
+ offset = 0;
+ }
+
+ for (ui = 0; ui < handlersSize; ui++) {
+ DexCatchIterator iterator;
+ dexCatchIteratorInit(&iterator, pCode, offset);
+ offset = dexCatchIteratorGetEndOffset(&iterator, pCode);
+ }
+
+ const u1* handlerData = dexGetCatchHandlerData(pCode);
+
+ //LOGD("+++ pCode=%p handlerData=%p last offset=%d\n",
+ // pCode, handlerData, offset);
+
+ /* return the size of the catch handler + everything before it */
+ return (handlerData - (u1*) pCode) + offset;
+}
+
+
+/*
+ * ===========================================================================
+ * Debug info
+ * ===========================================================================
+ */
+
+/*
+ * Decode the arguments in a method signature, which looks something
+ * like "(ID[Ljava/lang/String;)V".
+ *
+ * Returns the type signature letter for the next argument, or ')' if
+ * there are no more args. Advances "pSig" to point to the character
+ * after the one returned.
+ */
+static char decodeSignature(const char** pSig)
+{
+ const char* sig = *pSig;
+
+ if (*sig == '(')
+ sig++;
+
+ if (*sig == 'L') {
+ /* object ref */
+ while (*++sig != ';')
+ ;
+ *pSig = sig+1;
+ return 'L';
+ }
+ if (*sig == '[') {
+ /* array; advance past array type */
+ while (*++sig == '[')
+ ;
+ if (*sig == 'L') {
+ while (*++sig != ';')
+ ;
+ }
+ *pSig = sig+1;
+ return '[';
+ }
+ if (*sig == '\0')
+ return *sig; /* don't advance further */
+
+ *pSig = sig+1;
+ return *sig;
+}
+
+/*
+ * returns the length of a type string, given the start of the
+ * type string. Used for the case where the debug info format
+ * references types that are inside a method type signature.
+ */
+static int typeLength (const char *type) {
+ // Assumes any leading '(' has already been gobbled
+ const char *end = type;
+ decodeSignature(&end);
+ return end - type;
+}
+
+/*
+ * Reads a string index as encoded for the debug info format,
+ * returning a string pointer or NULL as appropriate.
+ */
+static const char* readStringIdx(const DexFile* pDexFile,
+ const u1** pStream) {
+ u4 stringIdx = readUnsignedLeb128(pStream);
+
+ // Remember, encoded string indicies have 1 added to them.
+ if (stringIdx == 0) {
+ return NULL;
+ } else {
+ return dexStringById(pDexFile, stringIdx - 1);
+ }
+}
+
+/*
+ * Reads a type index as encoded for the debug info format, returning
+ * a string pointer for its descriptor or NULL as appropriate.
+ */
+static const char* readTypeIdx(const DexFile* pDexFile,
+ const u1** pStream) {
+ u4 typeIdx = readUnsignedLeb128(pStream);
+
+ // Remember, encoded type indicies have 1 added to them.
+ if (typeIdx == 0) {
+ return NULL;
+ } else {
+ return dexStringByTypeIdx(pDexFile, typeIdx - 1);
+ }
+}
+
+/* access_flag value indicating that a method is static */
+#define ACC_STATIC 0x0008
+
+typedef struct LocalInfo {
+ const char *name;
+ const char *descriptor;
+ const char *signature;
+ u2 startAddress;
+ bool live;
+} LocalInfo;
+
+static void emitLocalCbIfLive (void *cnxt, int reg, u4 endAddress,
+ LocalInfo *localInReg, DexDebugNewLocalCb localCb)
+{
+ if (localCb != NULL && localInReg[reg].live) {
+ localCb(cnxt, reg, localInReg[reg].startAddress, endAddress,
+ localInReg[reg].name,
+ localInReg[reg].descriptor,
+ localInReg[reg].signature == NULL
+ ? "" : localInReg[reg].signature );
+ }
+}
+
+// TODO optimize localCb == NULL case
+void dexDecodeDebugInfo(
+ const DexFile* pDexFile,
+ const DexCode* pCode,
+ const char* classDescriptor,
+ u4 protoIdx,
+ u4 accessFlags,
+ DexDebugNewPositionCb posCb, DexDebugNewLocalCb localCb,
+ void* cnxt)
+{
+ const u1 *stream = dexGetDebugInfoStream(pDexFile, pCode);
+ u4 line;
+ u4 parametersSize;
+ u4 address = 0;
+ LocalInfo localInReg[pCode->registersSize];
+ u4 insnsSize = pCode->insnsSize;
+ DexProto proto = { pDexFile, protoIdx };
+
+ memset(localInReg, 0, sizeof(LocalInfo) * pCode->registersSize);
+
+ if (stream == NULL) {
+ goto end;
+ }
+
+ line = readUnsignedLeb128(&stream);
+ parametersSize = readUnsignedLeb128(&stream);
+
+ u2 argReg = pCode->registersSize - pCode->insSize;
+
+ if ((accessFlags & ACC_STATIC) == 0) {
+ /*
+ * The code is an instance method, which means that there is
+ * an initial this parameter. Also, the proto list should
+ * contain exactly one fewer argument word than the insSize
+ * indicates.
+ */
+ assert(pCode->insSize == (dexProtoComputeArgsSize(&proto) + 1));
+ localInReg[argReg].name = "this";
+ localInReg[argReg].descriptor = classDescriptor;
+ localInReg[argReg].startAddress = 0;
+ localInReg[argReg].live = true;
+ argReg++;
+ } else {
+ assert(pCode->insSize == dexProtoComputeArgsSize(&proto));
+ }
+
+ DexParameterIterator iterator;
+ dexParameterIteratorInit(&iterator, &proto);
+
+ while (parametersSize-- != 0) {
+ const char* descriptor = dexParameterIteratorNextDescriptor(&iterator);
+ const char *name;
+ int reg;
+
+ if ((argReg >= pCode->registersSize) || (descriptor == NULL)) {
+ goto invalid_stream;
+ }
+
+ name = readStringIdx(pDexFile, &stream);
+ reg = argReg;
+
+ switch (descriptor[0]) {
+ case 'D':
+ case 'J':
+ argReg += 2;
+ break;
+ default:
+ argReg += 1;
+ break;
+ }
+
+ if (name != NULL) {
+ localInReg[reg].name = name;
+ localInReg[reg].descriptor = descriptor;
+ localInReg[reg].signature = NULL;
+ localInReg[reg].startAddress = address;
+ localInReg[reg].live = true;
+ }
+ }
+
+ for (;;) {
+ u1 opcode = *stream++;
+ u2 reg;
+
+ switch (opcode) {
+ case DBG_END_SEQUENCE:
+ goto end;
+
+ case DBG_ADVANCE_PC:
+ address += readUnsignedLeb128(&stream);
+ break;
+
+ case DBG_ADVANCE_LINE:
+ line += readSignedLeb128(&stream);
+ break;
+
+ case DBG_START_LOCAL:
+ case DBG_START_LOCAL_EXTENDED:
+ reg = readUnsignedLeb128(&stream);
+ if (reg > pCode->registersSize) goto invalid_stream;
+
+ // Emit what was previously there, if anything
+ emitLocalCbIfLive (cnxt, reg, address,
+ localInReg, localCb);
+
+ localInReg[reg].name = readStringIdx(pDexFile, &stream);
+ localInReg[reg].descriptor = readTypeIdx(pDexFile, &stream);
+ if (opcode == DBG_START_LOCAL_EXTENDED) {
+ localInReg[reg].signature
+ = readStringIdx(pDexFile, &stream);
+ } else {
+ localInReg[reg].signature = NULL;
+ }
+ localInReg[reg].startAddress = address;
+ localInReg[reg].live = true;
+ break;
+
+ case DBG_END_LOCAL:
+ reg = readUnsignedLeb128(&stream);
+ if (reg > pCode->registersSize) goto invalid_stream;
+
+ emitLocalCbIfLive (cnxt, reg, address, localInReg, localCb);
+ localInReg[reg].live = false;
+ break;
+
+ case DBG_RESTART_LOCAL:
+ reg = readUnsignedLeb128(&stream);
+ if (reg > pCode->registersSize) goto invalid_stream;
+
+ if (localInReg[reg].name == NULL
+ || localInReg[reg].descriptor == NULL) {
+ goto invalid_stream;
+ }
+
+ /*
+ * If the register is live, the "restart" is superfluous,
+ * and we don't want to mess with the existing start address.
+ */
+ if (!localInReg[reg].live) {
+ localInReg[reg].startAddress = address;
+ localInReg[reg].live = true;
+ }
+ break;
+
+ case DBG_SET_PROLOGUE_END:
+ case DBG_SET_EPILOGUE_BEGIN:
+ case DBG_SET_FILE:
+ break;
+
+ default: {
+ int adjopcode = opcode - DBG_FIRST_SPECIAL;
+
+ address += adjopcode / DBG_LINE_RANGE;
+ line += DBG_LINE_BASE + (adjopcode % DBG_LINE_RANGE);
+
+ if (posCb != NULL) {
+ int done;
+ done = posCb(cnxt, address, line);
+
+ if (done) {
+ // early exit
+ goto end;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+end:
+ {
+ int reg;
+ for (reg = 0; reg < pCode->registersSize; reg++) {
+ emitLocalCbIfLive (cnxt, reg, insnsSize, localInReg, localCb);
+ }
+ }
+ return;
+
+invalid_stream:
+ IF_LOGE() {
+ char* methodDescriptor = dexProtoCopyMethodDescriptor(&proto);
+ LOGE("Invalid debug info stream. class %s; proto %s",
+ classDescriptor, methodDescriptor);
+ free(methodDescriptor);
+ }
+}
diff --git a/libdex/DexFile.h b/libdex/DexFile.h
new file mode 100644
index 0000000..06dc864
--- /dev/null
+++ b/libdex/DexFile.h
@@ -0,0 +1,1056 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Access .dex (Dalvik Executable Format) files. The code here assumes that
+ * the DEX file has been rewritten (byte-swapped, word-aligned) and that
+ * the contents can be directly accessed as a collection of C arrays. Please
+ * see docs/dalvik/dex-format.html for a detailed description.
+ *
+ * The structure and field names were chosen to match those in the DEX spec.
+ *
+ * It's generally assumed that the DEX file will be stored in shared memory,
+ * obviating the need to copy code and constant pool entries into newly
+ * allocated storage. Maintaining local pointers to items in the shared area
+ * is valid and encouraged.
+ *
+ * All memory-mapped structures are 32-bit aligned unless otherwise noted.
+ */
+
+#ifndef _LIBDEX_DEXFILE
+#define _LIBDEX_DEXFILE
+
+#include "vm/Common.h" // basic type defs, e.g. u1/u2/u4/u8, and LOG
+#include "libdex/SysUtil.h"
+
+/*
+ * gcc-style inline management -- ensures we have a copy of all functions
+ * in the library, so code that links against us will work whether or not
+ * it was built with optimizations enabled.
+ */
+#ifndef _DEX_GEN_INLINES /* only defined by DexInlines.c */
+# define DEX_INLINE extern __inline__
+#else
+# define DEX_INLINE
+#endif
+
+/* DEX file magic number */
+#define DEX_MAGIC "dex\n"
+/* version, encoded in 4 bytes of ASCII */
+#define DEX_MAGIC_VERS "035\0"
+
+/* same, but for optimized DEX header */
+#define DEX_OPT_MAGIC "dey\n"
+#define DEX_OPT_MAGIC_VERS "036\0"
+
+#define DEX_DEP_MAGIC "deps"
+
+/*
+ * 160-bit SHA-1 digest.
+ */
+enum { kSHA1DigestLen = 20,
+ kSHA1DigestOutputLen = kSHA1DigestLen*2 +1 };
+
+/* general constants */
+enum {
+ kDexEndianConstant = 0x12345678, /* the endianness indicator */
+ kDexNoIndex = 0xffffffff, /* not a valid index value */
+};
+
+/*
+ * access flags and masks; the "standard" ones are all <= 0x4000
+ *
+ * Note: There are related declarations in vm/oo/Object.h in the ClassFlags
+ * enum.
+ */
+enum {
+ ACC_PUBLIC = 0x00000001, // class, field, method, ic
+ ACC_PRIVATE = 0x00000002, // field, method, ic
+ ACC_PROTECTED = 0x00000004, // field, method, ic
+ ACC_STATIC = 0x00000008, // field, method, ic
+ ACC_FINAL = 0x00000010, // class, field, method, ic
+ ACC_SYNCHRONIZED = 0x00000020, // method (only allowed on natives)
+ ACC_SUPER = 0x00000020, // class (not used in Dalvik)
+ ACC_VOLATILE = 0x00000040, // field
+ ACC_BRIDGE = 0x00000040, // method (1.5)
+ ACC_TRANSIENT = 0x00000080, // field
+ ACC_VARARGS = 0x00000080, // method (1.5)
+ ACC_NATIVE = 0x00000100, // method
+ ACC_INTERFACE = 0x00000200, // class, ic
+ ACC_ABSTRACT = 0x00000400, // class, method, ic
+ ACC_STRICT = 0x00000800, // method
+ ACC_SYNTHETIC = 0x00001000, // field, method, ic
+ ACC_ANNOTATION = 0x00002000, // class, ic (1.5)
+ ACC_ENUM = 0x00004000, // class, field, ic (1.5)
+ ACC_CONSTRUCTOR = 0x00010000, // method (Dalvik only)
+ ACC_DECLARED_SYNCHRONIZED =
+ 0x00020000, // method (Dalvik only)
+ ACC_CLASS_MASK =
+ (ACC_PUBLIC | ACC_FINAL | ACC_INTERFACE | ACC_ABSTRACT
+ | ACC_SYNTHETIC | ACC_ANNOTATION | ACC_ENUM),
+ ACC_INNER_CLASS_MASK =
+ (ACC_CLASS_MASK | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC),
+ ACC_FIELD_MASK =
+ (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL
+ | ACC_VOLATILE | ACC_TRANSIENT | ACC_SYNTHETIC | ACC_ENUM),
+ ACC_METHOD_MASK =
+ (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL
+ | ACC_SYNCHRONIZED | ACC_BRIDGE | ACC_VARARGS | ACC_NATIVE
+ | ACC_ABSTRACT | ACC_STRICT | ACC_SYNTHETIC | ACC_CONSTRUCTOR
+ | ACC_DECLARED_SYNCHRONIZED),
+};
+
+/* annotation constants */
+enum {
+ kDexVisibilityBuild = 0x00, /* annotation visibility */
+ kDexVisibilityRuntime = 0x01,
+ kDexVisibilitySystem = 0x02,
+
+ kDexAnnotationByte = 0x00,
+ kDexAnnotationShort = 0x02,
+ kDexAnnotationChar = 0x03,
+ kDexAnnotationInt = 0x04,
+ kDexAnnotationLong = 0x06,
+ kDexAnnotationFloat = 0x10,
+ kDexAnnotationDouble = 0x11,
+ kDexAnnotationString = 0x17,
+ kDexAnnotationType = 0x18,
+ kDexAnnotationField = 0x19,
+ kDexAnnotationMethod = 0x1a,
+ kDexAnnotationEnum = 0x1b,
+ kDexAnnotationArray = 0x1c,
+ kDexAnnotationAnnotation = 0x1d,
+ kDexAnnotationNull = 0x1e,
+ kDexAnnotationBoolean = 0x1f,
+
+ kDexAnnotationValueTypeMask = 0x1f, /* low 5 bits */
+ kDexAnnotationValueArgShift = 5,
+};
+
+/* map item type codes */
+enum {
+ kDexTypeHeaderItem = 0x0000,
+ kDexTypeStringIdItem = 0x0001,
+ kDexTypeTypeIdItem = 0x0002,
+ kDexTypeProtoIdItem = 0x0003,
+ kDexTypeFieldIdItem = 0x0004,
+ kDexTypeMethodIdItem = 0x0005,
+ kDexTypeClassDefItem = 0x0006,
+ kDexTypeMapList = 0x1000,
+ kDexTypeTypeList = 0x1001,
+ kDexTypeAnnotationSetRefList = 0x1002,
+ kDexTypeAnnotationSetItem = 0x1003,
+ kDexTypeClassDataItem = 0x2000,
+ kDexTypeCodeItem = 0x2001,
+ kDexTypeStringDataItem = 0x2002,
+ kDexTypeDebugInfoItem = 0x2003,
+ kDexTypeAnnotationItem = 0x2004,
+ kDexTypeEncodedArrayItem = 0x2005,
+ kDexTypeAnnotationsDirectoryItem = 0x2006,
+};
+
+/* auxillary data section chunk codes */
+enum {
+ kDexChunkClassLookup = 0x434c4b50, /* CLKP */
+ kDexChunkRegisterMaps = 0x524d4150, /* RMAP */
+
+ kDexChunkEnd = 0x41454e44, /* AEND */
+};
+
+/* debug info opcodes and constants */
+enum {
+ DBG_END_SEQUENCE = 0x00,
+ DBG_ADVANCE_PC = 0x01,
+ DBG_ADVANCE_LINE = 0x02,
+ DBG_START_LOCAL = 0x03,
+ DBG_START_LOCAL_EXTENDED = 0x04,
+ DBG_END_LOCAL = 0x05,
+ DBG_RESTART_LOCAL = 0x06,
+ DBG_SET_PROLOGUE_END = 0x07,
+ DBG_SET_EPILOGUE_BEGIN = 0x08,
+ DBG_SET_FILE = 0x09,
+ DBG_FIRST_SPECIAL = 0x0a,
+ DBG_LINE_BASE = -4,
+ DBG_LINE_RANGE = 15,
+};
+
+/*
+ * Direct-mapped "header_item" struct.
+ */
+typedef struct DexHeader {
+ u1 magic[8]; /* includes version number */
+ u4 checksum; /* adler32 checksum */
+ u1 signature[kSHA1DigestLen]; /* SHA-1 hash */
+ u4 fileSize; /* length of entire file */
+ u4 headerSize; /* offset to start of next section */
+ u4 endianTag;
+ u4 linkSize;
+ u4 linkOff;
+ u4 mapOff;
+ u4 stringIdsSize;
+ u4 stringIdsOff;
+ u4 typeIdsSize;
+ u4 typeIdsOff;
+ u4 protoIdsSize;
+ u4 protoIdsOff;
+ u4 fieldIdsSize;
+ u4 fieldIdsOff;
+ u4 methodIdsSize;
+ u4 methodIdsOff;
+ u4 classDefsSize;
+ u4 classDefsOff;
+ u4 dataSize;
+ u4 dataOff;
+} DexHeader;
+
+/*
+ * Direct-mapped "map_item".
+ */
+typedef struct DexMapItem {
+ u2 type; /* type code (see kDexType* above) */
+ u2 unused;
+ u4 size; /* count of items of the indicated type */
+ u4 offset; /* file offset to the start of data */
+} DexMapItem;
+
+/*
+ * Direct-mapped "map_list".
+ */
+typedef struct DexMapList {
+ u4 size; /* #of entries in list */
+ DexMapItem list[1]; /* entries */
+} DexMapList;
+
+/*
+ * Direct-mapped "string_id_item".
+ */
+typedef struct DexStringId {
+ u4 stringDataOff; /* file offset to string_data_item */
+} DexStringId;
+
+/*
+ * Direct-mapped "type_id_item".
+ */
+typedef struct DexTypeId {
+ u4 descriptorIdx; /* index into stringIds list for type descriptor */
+} DexTypeId;
+
+/*
+ * Direct-mapped "field_id_item".
+ */
+typedef struct DexFieldId {
+ u2 classIdx; /* index into typeIds list for defining class */
+ u2 typeIdx; /* index into typeIds for field type */
+ u4 nameIdx; /* index into stringIds for field name */
+} DexFieldId;
+
+/*
+ * Direct-mapped "method_id_item".
+ */
+typedef struct DexMethodId {
+ u2 classIdx; /* index into typeIds list for defining class */
+ u2 protoIdx; /* index into protoIds for method prototype */
+ u4 nameIdx; /* index into stringIds for method name */
+} DexMethodId;
+
+/*
+ * Direct-mapped "proto_id_item".
+ */
+typedef struct DexProtoId {
+ u4 shortyIdx; /* index into stringIds for shorty descriptor */
+ u4 returnTypeIdx; /* index into typeIds list for return type */
+ u4 parametersOff; /* file offset to type_list for parameter types */
+} DexProtoId;
+
+/*
+ * Direct-mapped "class_def_item".
+ */
+typedef struct DexClassDef {
+ u4 classIdx; /* index into typeIds for this class */
+ u4 accessFlags;
+ u4 superclassIdx; /* index into typeIds for superclass */
+ u4 interfacesOff; /* file offset to DexTypeList */
+ u4 sourceFileIdx; /* index into stringIds for source file name */
+ u4 annotationsOff; /* file offset to annotations_directory_item */
+ u4 classDataOff; /* file offset to class_data_item */
+ u4 staticValuesOff; /* file offset to DexEncodedArray */
+} DexClassDef;
+
+/*
+ * Direct-mapped "type_item".
+ */
+typedef struct DexTypeItem {
+ u2 typeIdx; /* index into typeIds */
+} DexTypeItem;
+
+/*
+ * Direct-mapped "type_list".
+ */
+typedef struct DexTypeList {
+ u4 size; /* #of entries in list */
+ DexTypeItem list[1]; /* entries */
+} DexTypeList;
+
+/*
+ * Direct-mapped "code_item".
+ *
+ * The "catches" table is used when throwing an exception,
+ * "debugInfo" is used when displaying an exception stack trace or
+ * debugging. An offset of zero indicates that there are no entries.
+ */
+typedef struct DexCode {
+ u2 registersSize;
+ u2 insSize;
+ u2 outsSize;
+ u2 triesSize;
+ u4 debugInfoOff; /* file offset to debug info stream */
+ u4 insnsSize; /* size of the insns array, in u2 units */
+ u2 insns[1];
+ /* followed by optional u2 padding */
+ /* followed by try_item[triesSize] */
+ /* followed by uleb128 handlersSize */
+ /* followed by catch_handler_item[handlersSize] */
+} DexCode;
+
+/*
+ * Direct-mapped "try_item".
+ */
+typedef struct DexTry {
+ u4 startAddr; /* start address, in 16-bit code units */
+ u2 insnCount; /* instruction count, in 16-bit code units */
+ u2 handlerOff; /* offset in encoded handler data to handlers */
+} DexTry;
+
+/*
+ * Link table. Currently undefined.
+ */
+typedef struct DexLink {
+ u1 bleargh;
+} DexLink;
+
+
+/*
+ * Direct-mapped "annotations_directory_item".
+ */
+typedef struct DexAnnotationsDirectoryItem {
+ u4 classAnnotationsOff; /* offset to DexAnnotationSetItem */
+ u4 fieldsSize; /* count of DexFieldAnnotationsItem */
+ u4 methodsSize; /* count of DexMethodAnnotationsItem */
+ u4 parametersSize; /* count of DexParameterAnnotationsItem */
+ /* followed by DexFieldAnnotationsItem[fieldsSize] */
+ /* followed by DexMethodAnnotationsItem[methodsSize] */
+ /* followed by DexParameterAnnotationsItem[parametersSize] */
+} DexAnnotationsDirectoryItem;
+
+/*
+ * Direct-mapped "field_annotations_item".
+ */
+typedef struct DexFieldAnnotationsItem {
+ u4 fieldIdx;
+ u4 annotationsOff; /* offset to DexAnnotationSetItem */
+} DexFieldAnnotationsItem;
+
+/*
+ * Direct-mapped "method_annotations_item".
+ */
+typedef struct DexMethodAnnotationsItem {
+ u4 methodIdx;
+ u4 annotationsOff; /* offset to DexAnnotationSetItem */
+} DexMethodAnnotationsItem;
+
+/*
+ * Direct-mapped "parameter_annotations_item".
+ */
+typedef struct DexParameterAnnotationsItem {
+ u4 methodIdx;
+ u4 annotationsOff; /* offset to DexAnotationSetRefList */
+} DexParameterAnnotationsItem;
+
+/*
+ * Direct-mapped "annotation_set_ref_item".
+ */
+typedef struct DexAnnotationSetRefItem {
+ u4 annotationsOff; /* offset to DexAnnotationSetItem */
+} DexAnnotationSetRefItem;
+
+/*
+ * Direct-mapped "annotation_set_ref_list".
+ */
+typedef struct DexAnnotationSetRefList {
+ u4 size;
+ DexAnnotationSetRefItem list[1];
+} DexAnnotationSetRefList;
+
+/*
+ * Direct-mapped "anotation_set_item".
+ */
+typedef struct DexAnnotationSetItem {
+ u4 size;
+ u4 entries[1]; /* offset to DexAnnotationItem */
+} DexAnnotationSetItem;
+
+/*
+ * Direct-mapped "annotation_item".
+ *
+ * NOTE: this structure is byte-aligned.
+ */
+typedef struct DexAnnotationItem {
+ u1 visibility;
+ u1 annotation[1]; /* data in encoded_annotation format */
+} DexAnnotationItem;
+
+/*
+ * Direct-mapped "encoded_array".
+ *
+ * NOTE: this structure is byte-aligned.
+ */
+typedef struct DexEncodedArray {
+ u1 array[1]; /* data in encoded_array format */
+} DexEncodedArray;
+
+/*
+ * Lookup table for classes. It provides a mapping from class name to
+ * class definition. Used by dexFindClass().
+ *
+ * We calculate this at DEX optimization time and embed it in the file so we
+ * don't need the same hash table in every VM. This is slightly slower than
+ * a hash table with direct pointers to the items, but because it's shared
+ * there's less of a penalty for using a fairly sparse table.
+ */
+typedef struct DexClassLookup {
+ int size; // total size, including "size"
+ int numEntries; // size of table[]; always power of 2
+ struct {
+ u4 classDescriptorHash; // class descriptor hash code
+ int classDescriptorOffset; // in bytes, from start of DEX
+ int classDefOffset; // in bytes, from start of DEX
+ } table[1];
+} DexClassLookup;
+
+/*
+ * Header added by DEX optimization pass. Values are always written in
+ * local byte and structure padding. The first field (magic + version)
+ * is guaranteed to be present and directly readable for all expected
+ * compiler configurations; the rest is version-dependent.
+ *
+ * Try to keep this simple and fixed-size.
+ */
+typedef struct DexOptHeader {
+ u1 magic[8]; /* includes version number */
+
+ u4 dexOffset; /* file offset of DEX header */
+ u4 dexLength;
+ u4 depsOffset; /* offset of optimized DEX dependency table */
+ u4 depsLength;
+ u4 optOffset; /* file offset of optimized data tables */
+ u4 optLength;
+
+ u4 flags; /* some info flags */
+ u4 checksum; /* adler32 checksum covering deps/opt */
+
+ /* pad for 64-bit alignment if necessary */
+} DexOptHeader;
+
+#define DEX_FLAG_VERIFIED (1) /* tried to verify all classes */
+#define DEX_OPT_FLAG_BIG (1<<1) /* swapped to big-endian */
+#define DEX_OPT_FLAG_FIELDS (1<<2) /* field access optimized */
+#define DEX_OPT_FLAG_INVOCATIONS (1<<3) /* method calls optimized */
+
+#define DEX_INTERFACE_CACHE_SIZE 128 /* must be power of 2 */
+
+/*
+ * Structure representing a DEX file.
+ *
+ * Code should regard DexFile as opaque, using the API calls provided here
+ * to access specific structures.
+ */
+typedef struct DexFile {
+ /* directly-mapped "opt" header */
+ const DexOptHeader* pOptHeader;
+
+ /* pointers to directly-mapped structs and arrays in base DEX */
+ const DexHeader* pHeader;
+ const DexStringId* pStringIds;
+ const DexTypeId* pTypeIds;
+ const DexFieldId* pFieldIds;
+ const DexMethodId* pMethodIds;
+ const DexProtoId* pProtoIds;
+ const DexClassDef* pClassDefs;
+ const DexLink* pLinkData;
+
+ /*
+ * These are mapped out of the "auxillary" section, and may not be
+ * included in the file.
+ */
+ const DexClassLookup* pClassLookup;
+ const void* pRegisterMapPool; // RegisterMapClassPool
+
+ /* points to start of DEX file data */
+ const u1* baseAddr;
+
+ /* track memory overhead for auxillary structures */
+ int overhead;
+
+ /* additional app-specific data structures associated with the DEX */
+ //void* auxData;
+} DexFile;
+
+/*
+ * Utility function -- rounds up to the nearest power of 2.
+ */
+u4 dexRoundUpPower2(u4 val);
+
+/*
+ * Parse an optimized or unoptimized .dex file sitting in memory.
+ *
+ * On success, return a newly-allocated DexFile.
+ */
+DexFile* dexFileParse(const u1* data, size_t length, int flags);
+
+/* bit values for "flags" argument to dexFileParse */
+enum {
+ kDexParseDefault = 0,
+ kDexParseVerifyChecksum = 1,
+ kDexParseContinueOnError = (1 << 1),
+};
+
+/*
+ * Fix the byte ordering of all fields in the DEX file, and do
+ * structural verification. This is only required for code that opens
+ * "raw" DEX files, such as the DEX optimizer.
+ *
+ * Return 0 on success.
+ */
+int dexSwapAndVerify(u1* addr, int len);
+
+/*
+ * Detect the file type of the given memory buffer via magic number.
+ * Call dexSwapAndVerify() on an unoptimized DEX file, do nothing
+ * but return successfully on an optimized DEX file, and report an
+ * error for all other cases.
+ *
+ * Return 0 on success.
+ */
+int dexSwapAndVerifyIfNecessary(u1* addr, int len);
+
+/*
+ * Compute DEX checksum.
+ */
+u4 dexComputeChecksum(const DexHeader* pHeader);
+
+/*
+ * Free a DexFile structure, along with any associated structures.
+ */
+void dexFileFree(DexFile* pDexFile);
+
+/*
+ * Create class lookup table.
+ */
+DexClassLookup* dexCreateClassLookup(DexFile* pDexFile);
+
+/*
+ * Find a class definition by descriptor.
+ */
+const DexClassDef* dexFindClass(const DexFile* pFile, const char* descriptor);
+
+/*
+ * Set up the basic raw data pointers of a DexFile. This function isn't
+ * meant for general use.
+ */
+void dexFileSetupBasicPointers(DexFile* pDexFile, const u1* data);
+
+/* return the DexMapList of the file, if any */
+DEX_INLINE const DexMapList* dexGetMap(const DexFile* pDexFile) {
+ u4 mapOff = pDexFile->pHeader->mapOff;
+
+ if (mapOff == 0) {
+ return NULL;
+ } else {
+ return (const DexMapList*) (pDexFile->baseAddr + mapOff);
+ }
+}
+
+/* return the const char* string data referred to by the given string_id */
+DEX_INLINE const char* dexGetStringData(const DexFile* pDexFile,
+ const DexStringId* pStringId) {
+ const u1* ptr = pDexFile->baseAddr + pStringId->stringDataOff;
+
+ // Skip the uleb128 length.
+ while (*(ptr++) > 0x7f) /* empty */ ;
+
+ return (const char*) ptr;
+}
+/* return the StringId with the specified index */
+DEX_INLINE const DexStringId* dexGetStringId(const DexFile* pDexFile, u4 idx) {
+ assert(idx < pDexFile->pHeader->stringIdsSize);
+ return &pDexFile->pStringIds[idx];
+}
+/* return the UTF-8 encoded string with the specified string_id index */
+DEX_INLINE const char* dexStringById(const DexFile* pDexFile, u4 idx) {
+ const DexStringId* pStringId = dexGetStringId(pDexFile, idx);
+ return dexGetStringData(pDexFile, pStringId);
+}
+
+/* Return the UTF-8 encoded string with the specified string_id index,
+ * also filling in the UTF-16 size (number of 16-bit code points).*/
+const char* dexStringAndSizeById(const DexFile* pDexFile, u4 idx,
+ u4* utf16Size);
+
+/* return the TypeId with the specified index */
+DEX_INLINE const DexTypeId* dexGetTypeId(const DexFile* pDexFile, u4 idx) {
+ assert(idx < pDexFile->pHeader->typeIdsSize);
+ return &pDexFile->pTypeIds[idx];
+}
+
+/*
+ * Get the descriptor string associated with a given type index.
+ * The caller should not free() the returned string.
+ */
+DEX_INLINE const char* dexStringByTypeIdx(const DexFile* pDexFile, u4 idx) {
+ const DexTypeId* typeId = dexGetTypeId(pDexFile, idx);
+ return dexStringById(pDexFile, typeId->descriptorIdx);
+}
+
+/* return the MethodId with the specified index */
+DEX_INLINE const DexMethodId* dexGetMethodId(const DexFile* pDexFile, u4 idx) {
+ assert(idx < pDexFile->pHeader->methodIdsSize);
+ return &pDexFile->pMethodIds[idx];
+}
+
+/* return the FieldId with the specified index */
+DEX_INLINE const DexFieldId* dexGetFieldId(const DexFile* pDexFile, u4 idx) {
+ assert(idx < pDexFile->pHeader->fieldIdsSize);
+ return &pDexFile->pFieldIds[idx];
+}
+
+/* return the ProtoId with the specified index */
+DEX_INLINE const DexProtoId* dexGetProtoId(const DexFile* pDexFile, u4 idx) {
+ assert(idx < pDexFile->pHeader->protoIdsSize);
+ return &pDexFile->pProtoIds[idx];
+}
+
+/*
+ * Get the parameter list from a ProtoId. The returns NULL if the ProtoId
+ * does not have a parameter list.
+ */
+DEX_INLINE const DexTypeList* dexGetProtoParameters(
+ const DexFile *pDexFile, const DexProtoId* pProtoId) {
+ if (pProtoId->parametersOff == 0) {
+ return NULL;
+ }
+ return (const DexTypeList*)
+ (pDexFile->baseAddr + pProtoId->parametersOff);
+}
+
+/* return the ClassDef with the specified index */
+DEX_INLINE const DexClassDef* dexGetClassDef(const DexFile* pDexFile, u4 idx) {
+ assert(idx < pDexFile->pHeader->classDefsSize);
+ return &pDexFile->pClassDefs[idx];
+}
+
+/* given a ClassDef pointer, recover its index */
+DEX_INLINE u4 dexGetIndexForClassDef(const DexFile* pDexFile,
+ const DexClassDef* pClassDef)
+{
+ assert(pClassDef >= pDexFile->pClassDefs &&
+ pClassDef < pDexFile->pClassDefs + pDexFile->pHeader->classDefsSize);
+ return pClassDef - pDexFile->pClassDefs;
+}
+
+/* get the interface list for a DexClass */
+DEX_INLINE const DexTypeList* dexGetInterfacesList(const DexFile* pDexFile,
+ const DexClassDef* pClassDef)
+{
+ if (pClassDef->interfacesOff == 0)
+ return NULL;
+ return (const DexTypeList*)
+ (pDexFile->baseAddr + pClassDef->interfacesOff);
+}
+/* return the Nth entry in a DexTypeList. */
+DEX_INLINE const DexTypeItem* dexGetTypeItem(const DexTypeList* pList,
+ u4 idx)
+{
+ assert(idx < pList->size);
+ return &pList->list[idx];
+}
+/* return the type_idx for the Nth entry in a TypeList */
+DEX_INLINE u4 dexTypeListGetIdx(const DexTypeList* pList, u4 idx) {
+ const DexTypeItem* pItem = dexGetTypeItem(pList, idx);
+ return pItem->typeIdx;
+}
+
+/* get the static values list for a DexClass */
+DEX_INLINE const DexEncodedArray* dexGetStaticValuesList(
+ const DexFile* pDexFile, const DexClassDef* pClassDef)
+{
+ if (pClassDef->staticValuesOff == 0)
+ return NULL;
+ return (const DexEncodedArray*)
+ (pDexFile->baseAddr + pClassDef->staticValuesOff);
+}
+
+/* get the annotations directory item for a DexClass */
+DEX_INLINE const DexAnnotationsDirectoryItem* dexGetAnnotationsDirectoryItem(
+ const DexFile* pDexFile, const DexClassDef* pClassDef)
+{
+ if (pClassDef->annotationsOff == 0)
+ return NULL;
+ return (const DexAnnotationsDirectoryItem*)
+ (pDexFile->baseAddr + pClassDef->annotationsOff);
+}
+
+/* get the source file string */
+DEX_INLINE const char* dexGetSourceFile(
+ const DexFile* pDexFile, const DexClassDef* pClassDef)
+{
+ if (pClassDef->sourceFileIdx == 0xffffffff)
+ return NULL;
+ return dexStringById(pDexFile, pClassDef->sourceFileIdx);
+}
+
+/* get the size, in bytes, of a DexCode */
+size_t dexGetDexCodeSize(const DexCode* pCode);
+
+/* Get the list of "tries" for the given DexCode. */
+DEX_INLINE const DexTry* dexGetTries(const DexCode* pCode) {
+ const u2* insnsEnd = &pCode->insns[pCode->insnsSize];
+
+ // Round to four bytes.
+ if ((((u4) insnsEnd) & 3) != 0) {
+ insnsEnd++;
+ }
+
+ return (const DexTry*) insnsEnd;
+}
+
+/* Get the base of the encoded data for the given DexCode. */
+DEX_INLINE const u1* dexGetCatchHandlerData(const DexCode* pCode) {
+ const DexTry* pTries = dexGetTries(pCode);
+ return (const u1*) &pTries[pCode->triesSize];
+}
+
+/* get a pointer to the start of the debugging data */
+DEX_INLINE const u1* dexGetDebugInfoStream(const DexFile* pDexFile,
+ const DexCode* pCode)
+{
+ if (pCode->debugInfoOff == 0) {
+ return NULL;
+ } else {
+ return pDexFile->baseAddr + pCode->debugInfoOff;
+ }
+}
+
+/*
+ * Callback for "new position table entry".
+ * Returning non-0 causes the decoder to stop early.
+ */
+typedef int (*DexDebugNewPositionCb)(void *cnxt, u4 address, u4 lineNum);
+
+/*
+ * Callback for "new locals table entry". "signature" is an empty string
+ * if no signature is available for an entry.
+ */
+typedef void (*DexDebugNewLocalCb)(void *cnxt, u2 reg, u4 startAddress,
+ u4 endAddress, const char *name, const char *descriptor,
+ const char *signature);
+
+/*
+ * Decode debug info for method.
+ *
+ * posCb is called in ascending address order.
+ * localCb is called in order of ascending end address.
+ */
+void dexDecodeDebugInfo(
+ const DexFile* pDexFile,
+ const DexCode* pDexCode,
+ const char* classDescriptor,
+ u4 protoIdx,
+ u4 accessFlags,
+ DexDebugNewPositionCb posCb, DexDebugNewLocalCb localCb,
+ void* cnxt);
+
+/* DexClassDef convenience - get class descriptor */
+DEX_INLINE const char* dexGetClassDescriptor(const DexFile* pDexFile,
+ const DexClassDef* pClassDef)
+{
+ return dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
+}
+
+/* DexClassDef convenience - get superclass descriptor */
+DEX_INLINE const char* dexGetSuperClassDescriptor(const DexFile* pDexFile,
+ const DexClassDef* pClassDef)
+{
+ if (pClassDef->superclassIdx == 0)
+ return NULL;
+ return dexStringByTypeIdx(pDexFile, pClassDef->superclassIdx);
+}
+
+/* DexClassDef convenience - get class_data_item pointer */
+DEX_INLINE const u1* dexGetClassData(const DexFile* pDexFile,
+ const DexClassDef* pClassDef)
+{
+ if (pClassDef->classDataOff == 0)
+ return NULL;
+ return (const u1*) (pDexFile->baseAddr + pClassDef->classDataOff);
+}
+
+/* Get an annotation set at a particular offset. */
+DEX_INLINE const DexAnnotationSetItem* dexGetAnnotationSetItem(
+ const DexFile* pDexFile, u4 offset)
+{
+ return (const DexAnnotationSetItem*) (pDexFile->baseAddr + offset);
+}
+/* get the class' annotation set */
+DEX_INLINE const DexAnnotationSetItem* dexGetClassAnnotationSet(
+ const DexFile* pDexFile, const DexAnnotationsDirectoryItem* pAnnoDir)
+{
+ if (pAnnoDir->classAnnotationsOff == 0)
+ return NULL;
+ return dexGetAnnotationSetItem(pDexFile, pAnnoDir->classAnnotationsOff);
+}
+
+/* get the class' field annotation list */
+DEX_INLINE const DexFieldAnnotationsItem* dexGetFieldAnnotations(
+ const DexFile* pDexFile, const DexAnnotationsDirectoryItem* pAnnoDir)
+{
+ if (pAnnoDir->fieldsSize == 0)
+ return NULL;
+
+ // Skip past the header to the start of the field annotations.
+ return (const DexFieldAnnotationsItem*) &pAnnoDir[1];
+}
+
+/* get field annotation list size */
+DEX_INLINE int dexGetFieldAnnotationsSize(const DexFile* pDexFile,
+ const DexAnnotationsDirectoryItem* pAnnoDir)
+{
+ return pAnnoDir->fieldsSize;
+}
+
+/* return a pointer to the field's annotation set */
+DEX_INLINE const DexAnnotationSetItem* dexGetFieldAnnotationSetItem(
+ const DexFile* pDexFile, const DexFieldAnnotationsItem* pItem)
+{
+ return dexGetAnnotationSetItem(pDexFile, pItem->annotationsOff);
+}
+
+/* get the class' method annotation list */
+DEX_INLINE const DexMethodAnnotationsItem* dexGetMethodAnnotations(
+ const DexFile* pDexFile, const DexAnnotationsDirectoryItem* pAnnoDir)
+{
+ if (pAnnoDir->methodsSize == 0)
+ return NULL;
+
+ /*
+ * Skip past the header and field annotations to the start of the
+ * method annotations.
+ */
+ const u1* addr = (const u1*) &pAnnoDir[1];
+ addr += pAnnoDir->fieldsSize * sizeof (DexFieldAnnotationsItem);
+ return (const DexMethodAnnotationsItem*) addr;
+}
+
+/* get method annotation list size */
+DEX_INLINE int dexGetMethodAnnotationsSize(const DexFile* pDexFile,
+ const DexAnnotationsDirectoryItem* pAnnoDir)
+{
+ return pAnnoDir->methodsSize;
+}
+
+/* return a pointer to the method's annotation set */
+DEX_INLINE const DexAnnotationSetItem* dexGetMethodAnnotationSetItem(
+ const DexFile* pDexFile, const DexMethodAnnotationsItem* pItem)
+{
+ return dexGetAnnotationSetItem(pDexFile, pItem->annotationsOff);
+}
+
+/* get the class' parameter annotation list */
+DEX_INLINE const DexParameterAnnotationsItem* dexGetParameterAnnotations(
+ const DexFile* pDexFile, const DexAnnotationsDirectoryItem* pAnnoDir)
+{
+ if (pAnnoDir->parametersSize == 0)
+ return NULL;
+
+ /*
+ * Skip past the header, field annotations, and method annotations
+ * to the start of the parameter annotations.
+ */
+ const u1* addr = (const u1*) &pAnnoDir[1];
+ addr += pAnnoDir->fieldsSize * sizeof (DexFieldAnnotationsItem);
+ addr += pAnnoDir->methodsSize * sizeof (DexMethodAnnotationsItem);
+ return (const DexParameterAnnotationsItem*) addr;
+}
+
+/* get method annotation list size */
+DEX_INLINE int dexGetParameterAnnotationsSize(const DexFile* pDexFile,
+ const DexAnnotationsDirectoryItem* pAnnoDir)
+{
+ return pAnnoDir->parametersSize;
+}
+
+/* return the parameter annotation ref list */
+DEX_INLINE const DexAnnotationSetRefList* dexGetParameterAnnotationSetRefList(
+ const DexFile* pDexFile, const DexParameterAnnotationsItem* pItem)
+{
+ return (const DexAnnotationSetRefList*)
+ (pDexFile->baseAddr + pItem->annotationsOff);
+}
+
+/* get method annotation list size */
+DEX_INLINE int dexGetParameterAnnotationSetRefSize(const DexFile* pDexFile,
+ const DexParameterAnnotationsItem* pItem)
+{
+ if (pItem->annotationsOff == 0)
+ return 0;
+ return dexGetParameterAnnotationSetRefList(pDexFile, pItem)->size;
+}
+
+/* return the Nth entry from an annotation set ref list */
+DEX_INLINE const DexAnnotationSetRefItem* dexGetParameterAnnotationSetRef(
+ const DexAnnotationSetRefList* pList, u4 idx)
+{
+ assert(idx < pList->size);
+ return &pList->list[idx];
+}
+
+/* given a DexAnnotationSetRefItem, return the DexAnnotationSetItem */
+DEX_INLINE const DexAnnotationSetItem* dexGetSetRefItemItem(
+ const DexFile* pDexFile, const DexAnnotationSetRefItem* pItem)
+{
+ return dexGetAnnotationSetItem(pDexFile, pItem->annotationsOff);
+}
+
+/* return the Nth annotation offset from a DexAnnotationSetItem */
+DEX_INLINE u4 dexGetAnnotationOff(
+ const DexAnnotationSetItem* pAnnoSet, u4 idx)
+{
+ assert(idx < pAnnoSet->size);
+ return pAnnoSet->entries[idx];
+}
+
+/* return the Nth annotation item from a DexAnnotationSetItem */
+DEX_INLINE const DexAnnotationItem* dexGetAnnotationItem(
+ const DexFile* pDexFile, const DexAnnotationSetItem* pAnnoSet, u4 idx)
+{
+ return (const DexAnnotationItem*)
+ (pDexFile->baseAddr + dexGetAnnotationOff(pAnnoSet, idx));
+}
+
+
+/*
+ * ===========================================================================
+ * Utility Functions
+ * ===========================================================================
+ */
+
+/*
+ * Retrieve the next UTF-16 character from a UTF-8 string.
+ *
+ * Advances "*pUtf8Ptr" to the start of the next character.
+ *
+ * WARNING: If a string is corrupted by dropping a '\0' in the middle
+ * of a 3-byte sequence, you can end up overrunning the buffer with
+ * reads (and possibly with the writes if the length was computed and
+ * cached before the damage). For performance reasons, this function
+ * assumes that the string being parsed is known to be valid (e.g., by
+ * already being verified). Most strings we process here are coming
+ * out of dex files or other internal translations, so the only real
+ * risk comes from the JNI NewStringUTF call.
+ */
+DEX_INLINE u2 dexGetUtf16FromUtf8(const char** pUtf8Ptr)
+{
+ unsigned int one, two, three;
+
+ one = *(*pUtf8Ptr)++;
+ if ((one & 0x80) != 0) {
+ /* two- or three-byte encoding */
+ two = *(*pUtf8Ptr)++;
+ if ((one & 0x20) != 0) {
+ /* three-byte encoding */
+ three = *(*pUtf8Ptr)++;
+ return ((one & 0x0f) << 12) |
+ ((two & 0x3f) << 6) |
+ (three & 0x3f);
+ } else {
+ /* two-byte encoding */
+ return ((one & 0x1f) << 6) |
+ (two & 0x3f);
+ }
+ } else {
+ /* one-byte encoding */
+ return one;
+ }
+}
+
+/* Compare two '\0'-terminated modified UTF-8 strings, using Unicode
+ * code point values for comparison. This treats different encodings
+ * for the same code point as equivalent, except that only a real '\0'
+ * byte is considered the string terminator. The return value is as
+ * for strcmp(). */
+int dexUtf8Cmp(const char* s1, const char* s2);
+
+
+/* for dexIsValidMemberNameUtf8(), a bit vector indicating valid low ascii */
+extern u4 DEX_MEMBER_VALID_LOW_ASCII[4];
+
+/* Helper for dexIsValidMemberUtf8(); do not call directly. */
+bool dexIsValidMemberNameUtf8_0(const char** pUtf8Ptr);
+
+/* Return whether the pointed-at modified-UTF-8 encoded character is
+ * valid as part of a member name, updating the pointer to point past
+ * the consumed character. This will consume two encoded UTF-16 code
+ * points if the character is encoded as a surrogate pair. Also, if
+ * this function returns false, then the given pointer may only have
+ * been partially advanced. */
+DEX_INLINE bool dexIsValidMemberNameUtf8(const char** pUtf8Ptr) {
+ u1 c = (u1) **pUtf8Ptr;
+ if (c <= 0x7f) {
+ // It's low-ascii, so check the table.
+ u4 wordIdx = c >> 5;
+ u4 bitIdx = c & 0x1f;
+ (*pUtf8Ptr)++;
+ return (DEX_MEMBER_VALID_LOW_ASCII[wordIdx] & (1 << bitIdx)) != 0;
+ }
+
+ /*
+ * It's a multibyte encoded character. Call a non-inline function
+ * for the heavy lifting.
+ */
+ return dexIsValidMemberNameUtf8_0(pUtf8Ptr);
+}
+
+/* Return whether the given string is a valid field or method name. */
+bool dexIsValidMemberName(const char* s);
+
+/* Return whether the given string is a valid type descriptor. */
+bool dexIsValidTypeDescriptor(const char* s);
+
+/* Return whether the given string is a valid reference descriptor. This
+ * is true if dexIsValidTypeDescriptor() returns true and the descriptor
+ * is for a class or array and not a primitive type. */
+bool dexIsReferenceDescriptor(const char* s);
+
+/* Return whether the given string is a valid class descriptor. This
+ * is true if dexIsValidTypeDescriptor() returns true and the descriptor
+ * is for a class and not an array or primitive type. */
+bool dexIsClassDescriptor(const char* s);
+
+/* Return whether the given string is a valid field type descriptor. This
+ * is true if dexIsValidTypeDescriptor() returns true and the descriptor
+ * is for anything but "void". */
+bool dexIsFieldDescriptor(const char* s);
+
+#endif /*_LIBDEX_DEXFILE*/
diff --git a/libdex/DexInlines.c b/libdex/DexInlines.c
new file mode 100644
index 0000000..6b3aed8
--- /dev/null
+++ b/libdex/DexInlines.c
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Generate non-inline copies of inline functions in header files.
+ */
+
+#define _DEX_GEN_INLINES
+
+#include "DexFile.h"
+
+#include "DexCatch.h"
+#include "DexClass.h"
+#include "DexDataMap.h"
+#include "DexProto.h"
+#include "InstrUtils.h"
+#include "Leb128.h"
+#include "ZipArchive.h"
diff --git a/libdex/DexOptData.c b/libdex/DexOptData.c
new file mode 100644
index 0000000..10a4b5d
--- /dev/null
+++ b/libdex/DexOptData.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Functions to parse and manipulate the additional data tables added
+ * to optimized .dex files.
+ */
+
+#include <zlib.h>
+
+#include "DexOptData.h"
+
+/*
+ * Check to see if a given data pointer is a valid double-word-aligned
+ * pointer into the given memory range (from start inclusive to end
+ * exclusive). Returns true if valid.
+ */
+static bool isValidPointer(const void* ptr, const void* start, const void* end)
+{
+ return (ptr >= start) && (ptr < end) && (((u4) ptr & 7) == 0);
+}
+
+/* (documented in header file) */
+u4 dexComputeOptChecksum(const DexOptHeader* pOptHeader)
+{
+ const u1* start = (const u1*) pOptHeader + pOptHeader->depsOffset;
+ const u1* end = (const u1*) pOptHeader +
+ pOptHeader->optOffset + pOptHeader->optLength;
+
+ uLong adler = adler32(0L, Z_NULL, 0);
+
+ return (u4) adler32(adler, start, end - start);
+}
+
+/* (documented in header file) */
+bool dexParseOptData(const u1* data, size_t length, DexFile* pDexFile)
+{
+ const void* pOptStart = data + pDexFile->pOptHeader->optOffset;
+ const void* pOptEnd = data + length;
+ const u4* pOpt = pOptStart;
+ u4 optLength = (const u1*) pOptEnd - (const u1*) pOptStart;
+
+ /*
+ * Make sure the opt data start is in range and aligned. This may
+ * seem like a superfluous check, but (a) if the file got
+ * truncated, it might turn out that pOpt >= pOptEnd; and (b)
+ * if the opt data header got corrupted, pOpt might not be
+ * properly aligned. This test will catch both of these cases.
+ */
+ if (!isValidPointer(pOpt, pOptStart, pOptEnd)) {
+ LOGE("Bogus opt data start pointer\n");
+ return false;
+ }
+
+ /* Make sure that the opt data length is a whole number of words. */
+ if ((optLength & 3) != 0) {
+ LOGE("Unaligned opt data area end\n");
+ return false;
+ }
+
+ /*
+ * Make sure that the opt data area is large enough to have at least
+ * one chunk header.
+ */
+ if (optLength < 8) {
+ LOGE("Undersized opt data area (%u)\n", optLength);
+ return false;
+ }
+
+ /* Process chunks until we see the end marker. */
+ while (*pOpt != kDexChunkEnd) {
+ if (!isValidPointer(pOpt + 2, pOptStart, pOptEnd)) {
+ LOGE("Bogus opt data content pointer at offset %u\n",
+ ((const u1*) pOpt) - data);
+ return false;
+ }
+
+ u4 size = *(pOpt + 1);
+ const u1* pOptData = (const u1*) (pOpt + 2);
+
+ /*
+ * The rounded size is 64-bit aligned and includes +8 for the
+ * type/size header (which was extracted immediately above).
+ */
+ u4 roundedSize = (size + 8 + 7) & ~7;
+ const u4* pNextOpt = pOpt + (roundedSize / sizeof(u4));
+
+ if (!isValidPointer(pNextOpt, pOptStart, pOptEnd)) {
+ LOGE("Opt data area problem for chunk of size %u at offset %u\n",
+ size, ((const u1*) pOpt) - data);
+ return false;
+ }
+
+ switch (*pOpt) {
+ case kDexChunkClassLookup:
+ pDexFile->pClassLookup = (const DexClassLookup*) pOptData;
+ break;
+ case kDexChunkRegisterMaps:
+ LOGV("+++ found register maps, size=%u\n", size);
+ pDexFile->pRegisterMapPool = pOptData;
+ break;
+ default:
+ LOGI("Unknown chunk 0x%08x (%c%c%c%c), size=%d in opt data area\n",
+ *pOpt,
+ (char) ((*pOpt) >> 24), (char) ((*pOpt) >> 16),
+ (char) ((*pOpt) >> 8), (char) (*pOpt),
+ size);
+ break;
+ }
+
+ pOpt = pNextOpt;
+ }
+
+ return true;
+}
diff --git a/libdex/DexOptData.h b/libdex/DexOptData.h
new file mode 100644
index 0000000..69eda01
--- /dev/null
+++ b/libdex/DexOptData.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Functions to parse and manipulate the additional data tables added
+ * to optimized .dex files.
+ */
+
+#ifndef _LIBDEX_DEXOPTDATA
+#define _LIBDEX_DEXOPTDATA
+
+#include "libdex/DexFile.h"
+
+/*
+ * Parse the optimized data tables in the given dex file.
+ *
+ * @param data pointer to the start of the entire dex file
+ * @param length length of the entire dex file, in bytes
+ * @param pDexFile pointer to the associated dex file structure
+ */
+bool dexParseOptData(const u1* data, size_t length, DexFile* pDexFile);
+
+/*
+ * Compute the checksum of the optimized data tables pointed at by the given
+ * header.
+ */
+u4 dexComputeOptChecksum(const DexOptHeader* pOptHeader);
+
+#endif /* def _LIBDEX_DEXOPTDATA */
diff --git a/libdex/DexProto.c b/libdex/DexProto.c
new file mode 100644
index 0000000..b5574dc
--- /dev/null
+++ b/libdex/DexProto.c
@@ -0,0 +1,533 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Functions for dealing with method prototypes
+ */
+
+#include "DexProto.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * ===========================================================================
+ * String Cache
+ * ===========================================================================
+ */
+
+/*
+ * Make sure that the given cache can hold a string of the given length,
+ * including the final '\0' byte.
+ */
+static void dexStringCacheAlloc(DexStringCache* pCache, size_t length) {
+ if (pCache->allocatedSize != 0) {
+ if (pCache->allocatedSize >= length) {
+ return;
+ }
+ free((void*) pCache->value);
+ }
+
+ if (length <= sizeof(pCache->buffer)) {
+ pCache->value = pCache->buffer;
+ pCache->allocatedSize = 0;
+ } else {
+ pCache->value = malloc(length);
+ pCache->allocatedSize = length;
+ }
+}
+
+/*
+ * Initialize the given DexStringCache. Use this function before passing
+ * one into any other function.
+ */
+void dexStringCacheInit(DexStringCache* pCache) {
+ pCache->value = pCache->buffer;
+ pCache->allocatedSize = 0;
+ pCache->buffer[0] = '\0';
+}
+
+/*
+ * Release the allocated contents of the given DexStringCache, if any.
+ * Use this function after your last use of a DexStringCache.
+ */
+void dexStringCacheRelease(DexStringCache* pCache) {
+ if (pCache->allocatedSize != 0) {
+ free((void*) pCache->value);
+ pCache->value = pCache->buffer;
+ pCache->allocatedSize = 0;
+ }
+}
+
+/*
+ * If the given DexStringCache doesn't already point at the given value,
+ * make a copy of it into the cache. This always returns a writable
+ * pointer to the contents (whether or not a copy had to be made). This
+ * function is intended to be used after making a call that at least
+ * sometimes doesn't populate a DexStringCache.
+ */
+char* dexStringCacheEnsureCopy(DexStringCache* pCache, const char* value) {
+ if (value != pCache->value) {
+ size_t length = strlen(value) + 1;
+ dexStringCacheAlloc(pCache, length);
+ memcpy(pCache->value, value, length);
+ }
+
+ return pCache->value;
+}
+
+/*
+ * Abandon the given DexStringCache, and return a writable copy of the
+ * given value (reusing the string cache's allocation if possible).
+ * The return value must be free()d by the caller. Use this instead of
+ * dexStringCacheRelease() if you want the buffer to survive past the
+ * scope of the DexStringCache.
+ */
+char* dexStringCacheAbandon(DexStringCache* pCache, const char* value) {
+ if ((value == pCache->value) && (pCache->allocatedSize != 0)) {
+ char* result = pCache->value;
+ pCache->allocatedSize = 0;
+ pCache->value = pCache->buffer;
+ return result;
+ } else {
+ return strdup(value);
+ }
+}
+
+
+/*
+ * ===========================================================================
+ * Method Prototypes
+ * ===========================================================================
+ */
+
+/*
+ * Return the DexProtoId from the given DexProto. The DexProto must
+ * actually refer to a DexProtoId.
+ */
+static inline const DexProtoId* getProtoId(const DexProto* pProto) {
+ return dexGetProtoId(pProto->dexFile, pProto->protoIdx);
+}
+
+/*
+ * Get the short-form method descriptor for the given prototype. The
+ * prototype must be protoIdx-based.
+ */
+const char* dexProtoGetShorty(const DexProto* pProto) {
+ const DexProtoId* protoId = getProtoId(pProto);
+
+ return dexStringById(pProto->dexFile, protoId->shortyIdx);
+}
+
+/*
+ * Get the full method descriptor for the given prototype.
+ */
+const char* dexProtoGetMethodDescriptor(const DexProto* pProto,
+ DexStringCache* pCache) {
+ const DexFile* dexFile = pProto->dexFile;
+ const DexProtoId* protoId = getProtoId(pProto);
+ const DexTypeList* typeList = dexGetProtoParameters(dexFile, protoId);
+ size_t length = 3; // parens and terminating '\0'
+ u4 paramCount = (typeList == NULL) ? 0 : typeList->size;
+ u4 i;
+
+ for (i = 0; i < paramCount; i++) {
+ u4 idx = dexTypeListGetIdx(typeList, i);
+ length += strlen(dexStringByTypeIdx(dexFile, idx));
+ }
+
+ length += strlen(dexStringByTypeIdx(dexFile, protoId->returnTypeIdx));
+
+ dexStringCacheAlloc(pCache, length);
+
+ char *at = (char*) pCache->value;
+ *(at++) = '(';
+
+ for (i = 0; i < paramCount; i++) {
+ u4 idx = dexTypeListGetIdx(typeList, i);
+ const char* desc = dexStringByTypeIdx(dexFile, idx);
+ strcpy(at, desc);
+ at += strlen(desc);
+ }
+
+ *(at++) = ')';
+
+ strcpy(at, dexStringByTypeIdx(dexFile, protoId->returnTypeIdx));
+ return pCache->value;
+}
+
+/*
+ * Get a copy of the descriptor string associated with the given prototype.
+ * The returned pointer must be free()ed by the caller.
+ */
+char* dexProtoCopyMethodDescriptor(const DexProto* pProto) {
+ DexStringCache cache;
+
+ dexStringCacheInit(&cache);
+ return dexStringCacheAbandon(&cache,
+ dexProtoGetMethodDescriptor(pProto, &cache));
+}
+
+/*
+ * Get the parameter descriptors for the given prototype. This is the
+ * concatenation of all the descriptors for all the parameters, in
+ * order, with no other adornment.
+ */
+const char* dexProtoGetParameterDescriptors(const DexProto* pProto,
+ DexStringCache* pCache) {
+ DexParameterIterator iterator;
+ size_t length = 1; /* +1 for the terminating '\0' */
+
+ dexParameterIteratorInit(&iterator, pProto);
+
+ for (;;) {
+ const char* descriptor = dexParameterIteratorNextDescriptor(&iterator);
+ if (descriptor == NULL) {
+ break;
+ }
+
+ length += strlen(descriptor);
+ }
+
+ dexParameterIteratorInit(&iterator, pProto);
+
+ dexStringCacheAlloc(pCache, length);
+ char *at = (char*) pCache->value;
+
+ for (;;) {
+ const char* descriptor = dexParameterIteratorNextDescriptor(&iterator);
+ if (descriptor == NULL) {
+ break;
+ }
+
+ strcpy(at, descriptor);
+ at += strlen(descriptor);
+ }
+
+ return pCache->value;
+}
+
+/*
+ * Get the type descriptor for the return type of the given prototype.
+ */
+const char* dexProtoGetReturnType(const DexProto* pProto) {
+ const DexProtoId* protoId = getProtoId(pProto);
+ return dexStringByTypeIdx(pProto->dexFile, protoId->returnTypeIdx);
+}
+
+/*
+ * Get the parameter count of the given prototype.
+ */
+size_t dexProtoGetParameterCount(const DexProto* pProto) {
+ const DexProtoId* protoId = getProtoId(pProto);
+ const DexTypeList* typeList =
+ dexGetProtoParameters(pProto->dexFile, protoId);
+ return (typeList == NULL) ? 0 : typeList->size;
+}
+
+/*
+ * Compute the number of parameter words (u4 units) required by the
+ * given prototype. For example, if the method takes (int, long) and
+ * returns double, this would return 3 (one for the int, two for the
+ * long, and the return type isn't relevant).
+ */
+int dexProtoComputeArgsSize(const DexProto* pProto) {
+ const char* shorty = dexProtoGetShorty(pProto);
+ int count = 0;
+
+ /* Skip the return type. */
+ shorty++;
+
+ for (;;) {
+ switch (*(shorty++)) {
+ case '\0': {
+ return count;
+ }
+ case 'D':
+ case 'J': {
+ count += 2;
+ break;
+ }
+ default: {
+ count++;
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * Common implementation for dexProtoCompare() and dexProtoCompareParameters().
+ */
+static int protoCompare(const DexProto* pProto1, const DexProto* pProto2,
+ bool compareReturnType) {
+
+ if (pProto1 == pProto2) {
+ // Easy out.
+ return 0;
+ } else {
+ const DexFile* dexFile1 = pProto1->dexFile;
+ const DexProtoId* protoId1 = getProtoId(pProto1);
+ const DexTypeList* typeList1 =
+ dexGetProtoParameters(dexFile1, protoId1);
+ int paramCount1 = (typeList1 == NULL) ? 0 : typeList1->size;
+
+ const DexFile* dexFile2 = pProto2->dexFile;
+ const DexProtoId* protoId2 = getProtoId(pProto2);
+ const DexTypeList* typeList2 =
+ dexGetProtoParameters(dexFile2, protoId2);
+ int paramCount2 = (typeList2 == NULL) ? 0 : typeList2->size;
+
+ if (protoId1 == protoId2) {
+ // Another easy out.
+ return 0;
+ }
+
+ // Compare return types.
+
+ if (compareReturnType) {
+ int result =
+ strcmp(dexStringByTypeIdx(dexFile1, protoId1->returnTypeIdx),
+ dexStringByTypeIdx(dexFile2, protoId2->returnTypeIdx));
+
+ if (result != 0) {
+ return result;
+ }
+ }
+
+ // Compare parameters.
+
+ int minParam = (paramCount1 > paramCount2) ? paramCount2 : paramCount1;
+ int i;
+
+ for (i = 0; i < minParam; i++) {
+ u4 idx1 = dexTypeListGetIdx(typeList1, i);
+ u4 idx2 = dexTypeListGetIdx(typeList2, i);
+ int result =
+ strcmp(dexStringByTypeIdx(dexFile1, idx1),
+ dexStringByTypeIdx(dexFile2, idx2));
+
+ if (result != 0) {
+ return result;
+ }
+ }
+
+ if (paramCount1 < paramCount2) {
+ return -1;
+ } else if (paramCount1 > paramCount2) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+}
+
+/*
+ * Compare the two prototypes. The two prototypes are compared
+ * with the return type as the major order, then the first arguments,
+ * then second, etc. If two prototypes are identical except that one
+ * has extra arguments, then the shorter argument is considered the
+ * earlier one in sort order (similar to strcmp()).
+ */
+int dexProtoCompare(const DexProto* pProto1, const DexProto* pProto2) {
+ return protoCompare(pProto1, pProto2, true);
+}
+
+/*
+ * Compare the two prototypes. The two prototypes are compared
+ * with the first argument as the major order, then second, etc. If two
+ * prototypes are identical except that one has extra arguments, then the
+ * shorter argument is considered the earlier one in sort order (similar
+ * to strcmp()).
+ */
+int dexProtoCompareParameters(const DexProto* pProto1, const DexProto* pProto2){
+ return protoCompare(pProto1, pProto2, false);
+}
+
+
+/*
+ * Helper for dexProtoCompareToDescriptor(), which gets the return type
+ * descriptor from a method descriptor string.
+ */
+static const char* methodDescriptorReturnType(const char* descriptor) {
+ const char* result = strchr(descriptor, ')');
+
+ if (result == NULL) {
+ return NULL;
+ }
+
+ // The return type is the character just past the ')'.
+ return result + 1;
+}
+
+/*
+ * Helper for dexProtoCompareToDescriptor(), which indicates the end
+ * of an embedded argument type descriptor, which is also the
+ * beginning of the next argument type descriptor. Since this is for
+ * argument types, it doesn't accept 'V' as a valid type descriptor.
+ */
+static const char* methodDescriptorNextType(const char* descriptor) {
+ // Skip any array references.
+
+ while (*descriptor == '[') {
+ descriptor++;
+ }
+
+ switch (*descriptor) {
+ case 'B': case 'C': case 'D': case 'F':
+ case 'I': case 'J': case 'S': case 'Z': {
+ return descriptor + 1;
+ }
+ case 'L': {
+ const char* result = strchr(descriptor + 1, ';');
+ if (result != NULL) {
+ // The type ends just past the ';'.
+ return result + 1;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Compare a prototype and a string method descriptor. The comparison
+ * is done as if the descriptor were converted to a prototype and compared
+ * with dexProtoCompare().
+ */
+int dexProtoCompareToDescriptor(const DexProto* proto,
+ const char* descriptor) {
+ // First compare the return types.
+
+ int result = strcmp(dexProtoGetReturnType(proto),
+ methodDescriptorReturnType(descriptor));
+
+ if (result != 0) {
+ return result;
+ }
+
+ // The return types match, so we have to check arguments.
+
+ DexParameterIterator iterator;
+ dexParameterIteratorInit(&iterator, proto);
+
+ // Skip the '('.
+ assert (*descriptor == '(');
+ descriptor++;
+
+ for (;;) {
+ const char* protoDesc = dexParameterIteratorNextDescriptor(&iterator);
+
+ if (*descriptor == ')') {
+ // It's the end of the descriptor string.
+ if (protoDesc == NULL) {
+ // It's also the end of the prototype's arguments.
+ return 0;
+ } else {
+ // The prototype still has more arguments.
+ return 1;
+ }
+ }
+
+ if (protoDesc == NULL) {
+ /*
+ * The prototype doesn't have arguments left, but the
+ * descriptor string does.
+ */
+ return -1;
+ }
+
+ // Both prototype and descriptor have arguments. Compare them.
+
+ const char* nextDesc = methodDescriptorNextType(descriptor);
+
+ for (;;) {
+ char c1 = *(protoDesc++);
+ char c2 = (descriptor < nextDesc) ? *(descriptor++) : '\0';
+
+ if (c1 < c2) {
+ // This includes the case where the proto is shorter.
+ return -1;
+ } else if (c1 > c2) {
+ // This includes the case where the desc is shorter.
+ return 1;
+ } else if (c1 == '\0') {
+ // The two types are equal in length. (c2 necessarily == '\0'.)
+ break;
+ }
+ }
+
+ /*
+ * If we made it here, the two arguments matched, and
+ * descriptor == nextDesc.
+ */
+ }
+}
+
+
+/*
+ * ===========================================================================
+ * Parameter Iterators
+ * ===========================================================================
+ */
+
+/*
+ * Initialize the given DexParameterIterator to be at the start of the
+ * parameters of the given prototype.
+ */
+void dexParameterIteratorInit(DexParameterIterator* pIterator,
+ const DexProto* pProto) {
+ pIterator->proto = pProto;
+ pIterator->cursor = 0;
+
+ pIterator->parameters =
+ dexGetProtoParameters(pProto->dexFile, getProtoId(pProto));
+ pIterator->parameterCount = (pIterator->parameters == NULL) ? 0
+ : pIterator->parameters->size;
+}
+
+/*
+ * Get the type_id index for the next parameter, if any. This returns
+ * kDexNoIndex if the last parameter has already been consumed.
+ */
+u4 dexParameterIteratorNextIndex(DexParameterIterator* pIterator) {
+ int cursor = pIterator->cursor;
+ int parameterCount = pIterator->parameterCount;
+
+ if (cursor >= parameterCount) {
+ // The iteration is complete.
+ return kDexNoIndex;
+ } else {
+ u4 idx = dexTypeListGetIdx(pIterator->parameters, cursor);
+ pIterator->cursor++;
+ return idx;
+ }
+}
+
+/*
+ * Get the type descriptor for the next parameter, if any. This returns
+ * NULL if the last parameter has already been consumed.
+ */
+const char* dexParameterIteratorNextDescriptor(
+ DexParameterIterator* pIterator) {
+ u4 idx = dexParameterIteratorNextIndex(pIterator);
+
+ if (idx == kDexNoIndex) {
+ return NULL;
+ }
+
+ return dexStringByTypeIdx(pIterator->proto->dexFile, idx);
+}
diff --git a/libdex/DexProto.h b/libdex/DexProto.h
new file mode 100644
index 0000000..1ef577b
--- /dev/null
+++ b/libdex/DexProto.h
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Functions for dealing with method prototypes
+ */
+
+#ifndef _LIBDEX_DEXPROTO
+#define _LIBDEX_DEXPROTO
+
+#include "DexFile.h"
+
+/*
+ * Single-thread single-string cache. This structure holds a pointer to
+ * a string which is semi-automatically manipulated by some of the
+ * method prototype functions. Functions which use in this struct
+ * generally return a string that is valid until the next
+ * time the same DexStringCache is used.
+ */
+typedef struct DexStringCache {
+ char* value; /* the latest value */
+ size_t allocatedSize; /* size of the allocated buffer, if allocated */
+ char buffer[120]; /* buffer used to hold small-enough results */
+} DexStringCache;
+
+/*
+ * Initialize the given DexStringCache. Use this function before passing
+ * one into any other function.
+ */
+void dexStringCacheInit(DexStringCache* pCache);
+
+/*
+ * Release the allocated contents of the given DexStringCache, if any.
+ * Use this function after your last use of a DexStringCache.
+ */
+void dexStringCacheRelease(DexStringCache* pCache);
+
+/*
+ * If the given DexStringCache doesn't already point at the given value,
+ * make a copy of it into the cache. This always returns a writable
+ * pointer to the contents (whether or not a copy had to be made). This
+ * function is intended to be used after making a call that at least
+ * sometimes doesn't populate a DexStringCache.
+ */
+char* dexStringCacheEnsureCopy(DexStringCache* pCache, const char* value);
+
+/*
+ * Abandon the given DexStringCache, and return a writable copy of the
+ * given value (reusing the string cache's allocation if possible).
+ * The return value must be free()d by the caller. Use this instead of
+ * dexStringCacheRelease() if you want the buffer to survive past the
+ * scope of the DexStringCache.
+ */
+char* dexStringCacheAbandon(DexStringCache* pCache, const char* value);
+
+/*
+ * Method prototype structure, which refers to a protoIdx in a
+ * particular DexFile.
+ */
+typedef struct DexProto {
+ const DexFile* dexFile; /* file the idx refers to */
+ u4 protoIdx; /* index into proto_ids table of dexFile */
+} DexProto;
+
+/*
+ * Set the given DexProto to refer to the prototype of the given MethodId.
+ */
+DEX_INLINE void dexProtoSetFromMethodId(DexProto* pProto,
+ const DexFile* pDexFile, const DexMethodId* pMethodId)
+{
+ pProto->dexFile = pDexFile;
+ pProto->protoIdx = pMethodId->protoIdx;
+}
+
+/*
+ * Get the short-form method descriptor for the given prototype. The
+ * prototype must be protoIdx-based.
+ */
+const char* dexProtoGetShorty(const DexProto* pProto);
+
+/*
+ * Get the full method descriptor for the given prototype.
+ */
+const char* dexProtoGetMethodDescriptor(const DexProto* pProto,
+ DexStringCache* pCache);
+
+/*
+ * Get a copy of the descriptor string associated with the given prototype.
+ * The returned pointer must be free()ed by the caller.
+ */
+char* dexProtoCopyMethodDescriptor(const DexProto* pProto);
+
+/*
+ * Get the parameter descriptors for the given prototype. This is the
+ * concatenation of all the descriptors for all the parameters, in
+ * order, with no other adornment.
+ */
+const char* dexProtoGetParameterDescriptors(const DexProto* pProto,
+ DexStringCache* pCache);
+
+/*
+ * Return the utf-8 encoded descriptor string from the proto of a MethodId.
+ */
+DEX_INLINE const char* dexGetDescriptorFromMethodId(const DexFile* pDexFile,
+ const DexMethodId* pMethodId, DexStringCache* pCache)
+{
+ DexProto proto;
+
+ dexProtoSetFromMethodId(&proto, pDexFile, pMethodId);
+ return dexProtoGetMethodDescriptor(&proto, pCache);
+}
+
+/*
+ * Get a copy of the utf-8 encoded method descriptor string from the
+ * proto of a MethodId. The returned pointer must be free()ed by the
+ * caller.
+ */
+DEX_INLINE char* dexCopyDescriptorFromMethodId(const DexFile* pDexFile,
+ const DexMethodId* pMethodId)
+{
+ DexProto proto;
+
+ dexProtoSetFromMethodId(&proto, pDexFile, pMethodId);
+ return dexProtoCopyMethodDescriptor(&proto);
+}
+
+/*
+ * Get the type descriptor for the return type of the given prototype.
+ */
+const char* dexProtoGetReturnType(const DexProto* pProto);
+
+/*
+ * Get the parameter count of the given prototype.
+ */
+size_t dexProtoGetParameterCount(const DexProto* pProto);
+
+/*
+ * Compute the number of parameter words (u4 units) required by the
+ * given prototype. For example, if the method takes (int, long) and
+ * returns double, this would return 3 (one for the int, two for the
+ * long, and the return type isn't relevant).
+ */
+int dexProtoComputeArgsSize(const DexProto* pProto);
+
+/*
+ * Compare the two prototypes. The two prototypes are compared
+ * with the return type as the major order, then the first arguments,
+ * then second, etc. If two prototypes are identical except that one
+ * has extra arguments, then the shorter argument is considered the
+ * earlier one in sort order (similar to strcmp()).
+ */
+int dexProtoCompare(const DexProto* pProto1, const DexProto* pProto2);
+
+/*
+ * Compare the two prototypes. The two prototypes are compared
+ * with the first argument as the major order, then second, etc. If two
+ * prototypes are identical except that one has extra arguments, then the
+ * shorter argument is considered the earlier one in sort order (similar
+ * to strcmp()).
+ */
+int dexProtoCompareParameters(const DexProto* pProto1, const DexProto* pProto2);
+
+/*
+ * Compare a prototype and a string method descriptor. The comparison
+ * is done as if the descriptor were converted to a prototype and compared
+ * with dexProtoCompare().
+ */
+int dexProtoCompareToDescriptor(const DexProto* proto, const char* descriptor);
+
+/*
+ * Single-thread prototype parameter iterator. This structure holds a
+ * pointer to a prototype and its parts, along with a cursor.
+ */
+typedef struct DexParameterIterator {
+ const DexProto* proto;
+ const DexTypeList* parameters;
+ int parameterCount;
+ int cursor;
+} DexParameterIterator;
+
+/*
+ * Initialize the given DexParameterIterator to be at the start of the
+ * parameters of the given prototype.
+ */
+void dexParameterIteratorInit(DexParameterIterator* pIterator,
+ const DexProto* pProto);
+
+/*
+ * Get the type_id index for the next parameter, if any. This returns
+ * kDexNoIndex if the last parameter has already been consumed.
+ */
+u4 dexParameterIteratorNextIndex(DexParameterIterator* pIterator);
+
+/*
+ * Get the type descriptor for the next parameter, if any. This returns
+ * NULL if the last parameter has already been consumed.
+ */
+const char* dexParameterIteratorNextDescriptor(
+ DexParameterIterator* pIterator);
+
+
+
+#endif /*_LIBDEX_DEXPROTO*/
diff --git a/libdex/DexSwapVerify.c b/libdex/DexSwapVerify.c
new file mode 100644
index 0000000..a467fa7
--- /dev/null
+++ b/libdex/DexSwapVerify.c
@@ -0,0 +1,2944 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Byte-swapping and verification of dex files.
+ */
+
+#include "DexFile.h"
+#include "DexClass.h"
+#include "DexDataMap.h"
+#include "DexProto.h"
+#include "Leb128.h"
+
+#include <safe_iop.h>
+#include <zlib.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef __BYTE_ORDER
+# error "byte ordering not defined"
+#endif
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+# define SWAP2(_value) (_value)
+# define SWAP4(_value) (_value)
+# define SWAP8(_value) (_value)
+#else
+# define SWAP2(_value) endianSwapU2((_value))
+# define SWAP4(_value) endianSwapU4((_value))
+# define SWAP8(_value) endianSwapU8((_value))
+static u2 endianSwapU2(u2 value) {
+ return (value >> 8) | (value << 8);
+}
+static u4 endianSwapU4(u4 value) {
+ /* ABCD --> CDAB --> DCBA */
+ value = (value >> 16) | (value << 16);
+ return ((value & 0xff00ff00) >> 8) | ((value << 8) & 0xff00ff00);
+}
+static u8 endianSwapU8(u8 value) {
+ /* ABCDEFGH --> EFGHABCD --> GHEFCDAB --> HGFEDCBA */
+ value = (value >> 32) | (value << 32);
+ value = ((value & 0xffff0000ffff0000ULL) >> 16) |
+ ((value << 16) & 0xffff0000ffff0000ULL);
+ return ((value & 0xff00ff00ff00ff00ULL) >> 8) |
+ ((value << 8) & 0xff00ff00ff00ff00ULL);
+}
+#endif
+
+#define SWAP_FIELD2(_field) (_field) = SWAP2(_field)
+#define SWAP_FIELD4(_field) (_field) = SWAP4(_field)
+#define SWAP_FIELD8(_field) (_field) = SWAP8(_field)
+
+/*
+ * Some information we pass around to help verify values.
+ */
+typedef struct CheckState {
+ const DexHeader* pHeader;
+ const u1* fileStart;
+ const u1* fileEnd; // points to fileStart + fileLen
+ u4 fileLen;
+ DexDataMap* pDataMap; // set after map verification
+ const DexFile* pDexFile; // set after intraitem verification
+
+ /*
+ * bitmap of type_id indices that have been used to define classes;
+ * initialized immediately before class_def cross-verification, and
+ * freed immediately after it
+ */
+ u4* pDefinedClassBits;
+
+ const void* previousItem; // set during section iteration
+} CheckState;
+
+/*
+ * Return the file offset of the given pointer.
+ */
+static inline u4 fileOffset(const CheckState* state, const void* ptr) {
+ return ((const u1*) ptr) - state->fileStart;
+}
+
+/*
+ * Return a pointer for the given file offset.
+ */
+static inline void* filePointer(const CheckState* state, u4 offset) {
+ return (void*) (state->fileStart + offset);
+}
+
+/*
+ * Verify that a pointer range, start inclusive to end exclusive, only
+ * covers bytes in the file and doesn't point beyond the end of the
+ * file. That is, the start must indicate a valid byte or may point at
+ * the byte just past the end of the file (but no further), and the
+ * end must be no less than the start and must also not point beyond
+ * the byte just past the end of the file.
+ */
+static inline bool checkPtrRange(const CheckState* state,
+ const void* start, const void* end, const char* label) {
+ const void* fileStart = state->fileStart;
+ const void* fileEnd = state->fileEnd;
+ if ((start < fileStart) || (start > fileEnd)
+ || (end < start) || (end > fileEnd)) {
+ LOGW("Bad offset range for %s: 0x%x..0x%x\n", label,
+ fileOffset(state, start), fileOffset(state, end));
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Verify that a range of offsets, start inclusive to end exclusive,
+ * are all valid. That is, the start must indicate a valid byte or may
+ * point at the byte just past the end of the file (but no further),
+ * and the end must be no less than the start and must also not point
+ * beyond the byte just past the end of the file.
+ *
+ * Assumes "const CheckState* state".
+ */
+#define CHECK_OFFSET_RANGE(_start, _end) { \
+ const u1* _startPtr = filePointer(state, (_start)); \
+ const u1* _endPtr = filePointer(state, (_end)); \
+ if (!checkPtrRange(state, _startPtr, _endPtr, \
+ #_start ".." #_end)) { \
+ return 0; \
+ } \
+ }
+
+/*
+ * Verify that a pointer range, start inclusive to end exclusive, only
+ * covers bytes in the file and doesn't point beyond the end of the
+ * file. That is, the start must indicate a valid byte or may point at
+ * the byte just past the end of the file (but no further), and the
+ * end must be no less than the start and must also not point beyond
+ * the byte just past the end of the file.
+ *
+ * Assumes "const CheckState* state".
+ */
+#define CHECK_PTR_RANGE(_start, _end) { \
+ if (!checkPtrRange(state, (_start), (_end), #_start ".." #_end)) { \
+ return 0; \
+ } \
+ }
+
+/*
+ * Make sure a list of items fits entirely within the file.
+ *
+ * Assumes "const CheckState* state" and "typeof(_count) == typeof(_elemSize)"
+ * If the type sizes or signs are mismatched, this will return 0.
+ */
+#define CHECK_LIST_SIZE(_ptr, _count, _elemSize) { \
+ const u1* _start = (const u1*) (_ptr); \
+ const u1* _end = _start + ((_count) * (_elemSize)); \
+ if (!safe_mul(NULL, (_count), (_elemSize)) || \
+ !checkPtrRange(state, _start, _end, #_ptr)) { \
+ return 0; \
+ } \
+ }
+
+/*
+ * Swap a field that is known to hold an absolute DEX file offset. Note:
+ * This does not check to see that the swapped offset points within the
+ * mapped file, since that should be handled (with even more rigor) by
+ * the cross-verification phase.
+ *
+ * Assumes "const CheckState* state".
+ */
+#define SWAP_OFFSET4(_field) { \
+ SWAP_FIELD4((_field)); \
+ }
+
+/*
+ * Verify that an index falls in a valid range.
+ */
+#define CHECK_INDEX(_field, _limit) { \
+ if ((_field) >= (_limit)) { \
+ LOGW("Bad index: %s(%u) > %s(%u)\n", \
+ #_field, (u4)(_field), #_limit, (u4)(_limit)); \
+ return 0; \
+ } \
+ }
+
+/*
+ * Swap an index, and verify that it falls in a valid range.
+ */
+#define SWAP_INDEX2(_field, _limit) { \
+ SWAP_FIELD2((_field)); \
+ CHECK_INDEX((_field), (_limit)); \
+ }
+
+/*
+ * Verify that an index falls in a valid range or is kDexNoIndex.
+ */
+#define CHECK_INDEX_OR_NOINDEX(_field, _limit) { \
+ if ((_field) != kDexNoIndex && (_field) >= (_limit)) { \
+ LOGW("Bad index: %s(%u) > %s(%u)\n", \
+ #_field, (u4)(_field), #_limit, (u4)(_limit)); \
+ return 0; \
+ } \
+ }
+
+/*
+ * Swap an index, and verify that it falls in a valid range.
+ */
+#define SWAP_INDEX4(_field, _limit) { \
+ SWAP_FIELD4((_field)); \
+ CHECK_INDEX((_field), (_limit)); \
+ }
+
+/*
+ * Swap an index, and verify that it falls in a valid range or is
+ * kDexNoIndex.
+ */
+#define SWAP_INDEX4_OR_NOINDEX(_field, _limit) { \
+ SWAP_FIELD4((_field)); \
+ CHECK_INDEX_OR_NOINDEX((_field), (_limit)); \
+ }
+
+/* Verify the definer of a given field_idx. */
+static bool verifyFieldDefiner(const CheckState* state, u4 definingClass,
+ u4 fieldIdx) {
+ const DexFieldId* field = dexGetFieldId(state->pDexFile, fieldIdx);
+ return field->classIdx == definingClass;
+}
+
+/* Verify the definer of a given method_idx. */
+static bool verifyMethodDefiner(const CheckState* state, u4 definingClass,
+ u4 methodIdx) {
+ const DexMethodId* meth = dexGetMethodId(state->pDexFile, methodIdx);
+ return meth->classIdx == definingClass;
+}
+
+/*
+ * Calculate the required size (in elements) of the array pointed at by
+ * pDefinedClassBits.
+ */
+static size_t calcDefinedClassBitsSize(const CheckState* state)
+{
+ // Divide typeIdsSize by 32 (0x20), rounding up.
+ return (state->pHeader->typeIdsSize + 0x1f) >> 5;
+}
+
+/*
+ * Set the given bit in pDefinedClassBits, returning its former value.
+ */
+static bool setDefinedClassBit(const CheckState* state, u4 typeIdx) {
+ u4 arrayIdx = typeIdx >> 5;
+ u4 bit = 1 << (typeIdx & 0x1f);
+ u4* element = &state->pDefinedClassBits[arrayIdx];
+ bool result = (*element & bit) != 0;
+
+ *element |= bit;
+
+ return result;
+}
+
+/*
+ * Swap the header_item.
+ */
+static bool swapDexHeader(const CheckState* state, DexHeader* pHeader)
+{
+ CHECK_PTR_RANGE(pHeader, pHeader + 1);
+
+ // magic is ok
+ SWAP_FIELD4(pHeader->checksum);
+ // signature is ok
+ SWAP_FIELD4(pHeader->fileSize);
+ SWAP_FIELD4(pHeader->headerSize);
+ SWAP_FIELD4(pHeader->endianTag);
+ SWAP_FIELD4(pHeader->linkSize);
+ SWAP_OFFSET4(pHeader->linkOff);
+ SWAP_OFFSET4(pHeader->mapOff);
+ SWAP_FIELD4(pHeader->stringIdsSize);
+ SWAP_OFFSET4(pHeader->stringIdsOff);
+ SWAP_FIELD4(pHeader->typeIdsSize);
+ SWAP_OFFSET4(pHeader->typeIdsOff);
+ SWAP_FIELD4(pHeader->fieldIdsSize);
+ SWAP_OFFSET4(pHeader->fieldIdsOff);
+ SWAP_FIELD4(pHeader->methodIdsSize);
+ SWAP_OFFSET4(pHeader->methodIdsOff);
+ SWAP_FIELD4(pHeader->protoIdsSize);
+ SWAP_OFFSET4(pHeader->protoIdsOff);
+ SWAP_FIELD4(pHeader->classDefsSize);
+ SWAP_OFFSET4(pHeader->classDefsOff);
+ SWAP_FIELD4(pHeader->dataSize);
+ SWAP_OFFSET4(pHeader->dataOff);
+
+ if (pHeader->endianTag != kDexEndianConstant) {
+ LOGE("Unexpected endian_tag: 0x%x\n", pHeader->endianTag);
+ return false;
+ }
+
+ // Assign variables so the diagnostic is prettier. (Hooray for macros.)
+ u4 linkOff = pHeader->linkOff;
+ u4 linkEnd = linkOff + pHeader->linkSize;
+ u4 dataOff = pHeader->dataOff;
+ u4 dataEnd = dataOff + pHeader->dataSize;
+ CHECK_OFFSET_RANGE(linkOff, linkEnd);
+ CHECK_OFFSET_RANGE(dataOff, dataEnd);
+
+ /*
+ * Note: The offsets and ranges of the other header items end up getting
+ * checked during the first iteration over the map.
+ */
+
+ return true;
+}
+
+/* Check the header section for sanity. */
+static bool checkHeaderSection(const CheckState* state, u4 sectionOffset,
+ u4 sectionCount, u4* endOffset) {
+ if (sectionCount != 1) {
+ LOGE("Multiple header items\n");
+ return false;
+ }
+
+ if (sectionOffset != 0) {
+ LOGE("Header at 0x%x; not at start of file\n", sectionOffset);
+ return false;
+ }
+
+ const DexHeader* pHeader = filePointer(state, 0);
+ *endOffset = pHeader->headerSize;
+ return true;
+}
+
+/*
+ * Helper for swapMap(), which turns a map type constant into a small
+ * one-bit-on integer, suitable for use in an int-sized bit set.
+ */
+static u4 mapTypeToBitMask(int mapType) {
+ switch (mapType) {
+ case kDexTypeHeaderItem: return 1 << 0;
+ case kDexTypeStringIdItem: return 1 << 1;
+ case kDexTypeTypeIdItem: return 1 << 2;
+ case kDexTypeProtoIdItem: return 1 << 3;
+ case kDexTypeFieldIdItem: return 1 << 4;
+ case kDexTypeMethodIdItem: return 1 << 5;
+ case kDexTypeClassDefItem: return 1 << 6;
+ case kDexTypeMapList: return 1 << 7;
+ case kDexTypeTypeList: return 1 << 8;
+ case kDexTypeAnnotationSetRefList: return 1 << 9;
+ case kDexTypeAnnotationSetItem: return 1 << 10;
+ case kDexTypeClassDataItem: return 1 << 11;
+ case kDexTypeCodeItem: return 1 << 12;
+ case kDexTypeStringDataItem: return 1 << 13;
+ case kDexTypeDebugInfoItem: return 1 << 14;
+ case kDexTypeAnnotationItem: return 1 << 15;
+ case kDexTypeEncodedArrayItem: return 1 << 16;
+ case kDexTypeAnnotationsDirectoryItem: return 1 << 17;
+ default: {
+ LOGE("Unknown map item type %04x\n", mapType);
+ return 0;
+ }
+ }
+}
+
+/*
+ * Helper for swapMap(), which indicates if an item type should appear
+ * in the data section.
+ */
+static bool isDataSectionType(int mapType) {
+ switch (mapType) {
+ case kDexTypeHeaderItem:
+ case kDexTypeStringIdItem:
+ case kDexTypeTypeIdItem:
+ case kDexTypeProtoIdItem:
+ case kDexTypeFieldIdItem:
+ case kDexTypeMethodIdItem:
+ case kDexTypeClassDefItem: {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/*
+ * Swap the map_list and verify what we can about it. Also, if verification
+ * passes, allocate the state's DexDataMap.
+ */
+static bool swapMap(CheckState* state, DexMapList* pMap)
+{
+ DexMapItem* item = pMap->list;
+ u4 count;
+ u4 dataItemCount = 0; // Total count of items in the data section.
+ u4 dataItemsLeft = state->pHeader->dataSize; // See use below.
+ u4 usedBits = 0; // Bit set: one bit per section
+ bool first = true;
+ u4 lastOffset = 0;
+
+ SWAP_FIELD4(pMap->size);
+ count = pMap->size;
+
+ CHECK_LIST_SIZE(item, count, sizeof(DexMapItem));
+
+ while (count--) {
+ SWAP_FIELD2(item->type);
+ SWAP_FIELD2(item->unused);
+ SWAP_FIELD4(item->size);
+ SWAP_OFFSET4(item->offset);
+
+ if (first) {
+ first = false;
+ } else if (lastOffset >= item->offset) {
+ LOGE("Out-of-order map item: 0x%x then 0x%x\n",
+ lastOffset, item->offset);
+ return false;
+ }
+
+ if (item->offset >= state->pHeader->fileSize) {
+ LOGE("Map item after end of file: %x, size 0x%x\n",
+ item->offset, state->pHeader->fileSize);
+ return false;
+ }
+
+ if (isDataSectionType(item->type)) {
+ u4 icount = item->size;
+
+ /*
+ * This sanity check on the data section items ensures that
+ * there are no more items than the number of bytes in
+ * the data section.
+ */
+ if (icount > dataItemsLeft) {
+ LOGE("Unrealistically many items in the data section: "
+ "at least %d\n", dataItemCount + icount);
+ return false;
+ }
+
+ dataItemsLeft -= icount;
+ dataItemCount += icount;
+ }
+
+ u4 bit = mapTypeToBitMask(item->type);
+
+ if (bit == 0) {
+ return false;
+ }
+
+ if ((usedBits & bit) != 0) {
+ LOGE("Duplicate map section of type 0x%x\n", item->type);
+ return false;
+ }
+
+ usedBits |= bit;
+ lastOffset = item->offset;
+ item++;
+ }
+
+ if ((usedBits & mapTypeToBitMask(kDexTypeHeaderItem)) == 0) {
+ LOGE("Map is missing header entry\n");
+ return false;
+ }
+
+ if ((usedBits & mapTypeToBitMask(kDexTypeMapList)) == 0) {
+ LOGE("Map is missing map_list entry\n");
+ return false;
+ }
+
+ if (((usedBits & mapTypeToBitMask(kDexTypeStringIdItem)) == 0)
+ && ((state->pHeader->stringIdsOff != 0)
+ || (state->pHeader->stringIdsSize != 0))) {
+ LOGE("Map is missing string_ids entry\n");
+ return false;
+ }
+
+ if (((usedBits & mapTypeToBitMask(kDexTypeTypeIdItem)) == 0)
+ && ((state->pHeader->typeIdsOff != 0)
+ || (state->pHeader->typeIdsSize != 0))) {
+ LOGE("Map is missing type_ids entry\n");
+ return false;
+ }
+
+ if (((usedBits & mapTypeToBitMask(kDexTypeProtoIdItem)) == 0)
+ && ((state->pHeader->protoIdsOff != 0)
+ || (state->pHeader->protoIdsSize != 0))) {
+ LOGE("Map is missing proto_ids entry\n");
+ return false;
+ }
+
+ if (((usedBits & mapTypeToBitMask(kDexTypeFieldIdItem)) == 0)
+ && ((state->pHeader->fieldIdsOff != 0)
+ || (state->pHeader->fieldIdsSize != 0))) {
+ LOGE("Map is missing field_ids entry\n");
+ return false;
+ }
+
+ if (((usedBits & mapTypeToBitMask(kDexTypeMethodIdItem)) == 0)
+ && ((state->pHeader->methodIdsOff != 0)
+ || (state->pHeader->methodIdsSize != 0))) {
+ LOGE("Map is missing method_ids entry\n");
+ return false;
+ }
+
+ if (((usedBits & mapTypeToBitMask(kDexTypeClassDefItem)) == 0)
+ && ((state->pHeader->classDefsOff != 0)
+ || (state->pHeader->classDefsSize != 0))) {
+ LOGE("Map is missing class_defs entry\n");
+ return false;
+ }
+
+ state->pDataMap = dexDataMapAlloc(dataItemCount);
+ if (state->pDataMap == NULL) {
+ LOGE("Unable to allocate data map (size 0x%x)\n", dataItemCount);
+ return false;
+ }
+
+ return true;
+}
+
+/* Check the map section for sanity. */
+static bool checkMapSection(const CheckState* state, u4 sectionOffset,
+ u4 sectionCount, u4* endOffset) {
+ if (sectionCount != 1) {
+ LOGE("Multiple map list items");
+ return false;
+ }
+
+ if (sectionOffset != state->pHeader->mapOff) {
+ LOGE("Map not at header-defined offset: 0x%x, expected 0x%x\n",
+ sectionOffset, state->pHeader->mapOff);
+ return false;
+ }
+
+ const DexMapList* pMap = filePointer(state, sectionOffset);
+
+ *endOffset =
+ sectionOffset + sizeof(u4) + (pMap->size * sizeof(DexMapItem));
+ return true;
+}
+
+/* Perform byte-swapping and intra-item verification on string_id_item. */
+static void* swapStringIdItem(const CheckState* state, void* ptr) {
+ DexStringId* item = ptr;
+
+ CHECK_PTR_RANGE(item, item + 1);
+ SWAP_OFFSET4(item->stringDataOff);
+
+ return item + 1;
+}
+
+/* Perform cross-item verification of string_id_item. */
+static void* crossVerifyStringIdItem(const CheckState* state, void* ptr) {
+ const DexStringId* item = ptr;
+
+ if (!dexDataMapVerify(state->pDataMap,
+ item->stringDataOff, kDexTypeStringDataItem)) {
+ return NULL;
+ }
+
+ const DexStringId* item0 = state->previousItem;
+ if (item0 != NULL) {
+ // Check ordering.
+ const char* s0 = dexGetStringData(state->pDexFile, item0);
+ const char* s1 = dexGetStringData(state->pDexFile, item);
+ if (dexUtf8Cmp(s0, s1) >= 0) {
+ LOGE("Out-of-order string_ids: '%s' then '%s'\n", s0, s1);
+ return NULL;
+ }
+ }
+
+ return (void*) (item + 1);
+}
+
+/* Perform byte-swapping and intra-item verification on type_id_item. */
+static void* swapTypeIdItem(const CheckState* state, void* ptr) {
+ DexTypeId* item = ptr;
+
+ CHECK_PTR_RANGE(item, item + 1);
+ SWAP_INDEX4(item->descriptorIdx, state->pHeader->stringIdsSize);
+
+ return item + 1;
+}
+
+/* Perform cross-item verification of type_id_item. */
+static void* crossVerifyTypeIdItem(const CheckState* state, void* ptr) {
+ const DexTypeId* item = ptr;
+ const char* descriptor =
+ dexStringById(state->pDexFile, item->descriptorIdx);
+
+ if (!dexIsValidTypeDescriptor(descriptor)) {
+ LOGE("Invalid type descriptor: '%s'\n", descriptor);
+ return NULL;
+ }
+
+ const DexTypeId* item0 = state->previousItem;
+ if (item0 != NULL) {
+ // Check ordering. This relies on string_ids being in order.
+ if (item0->descriptorIdx >= item->descriptorIdx) {
+ LOGE("Out-of-order type_ids: 0x%x then 0x%x\n",
+ item0->descriptorIdx, item->descriptorIdx);
+ return NULL;
+ }
+ }
+
+ return (void*) (item + 1);
+}
+
+/* Perform byte-swapping and intra-item verification on proto_id_item. */
+static void* swapProtoIdItem(const CheckState* state, void* ptr) {
+ DexProtoId* item = ptr;
+
+ CHECK_PTR_RANGE(item, item + 1);
+ SWAP_INDEX4(item->shortyIdx, state->pHeader->stringIdsSize);
+ SWAP_INDEX4(item->returnTypeIdx, state->pHeader->typeIdsSize);
+ SWAP_OFFSET4(item->parametersOff);
+
+ return item + 1;
+}
+
+/* Helper for crossVerifyProtoIdItem(), which checks a shorty character
+ * to see if it is compatible with a type descriptor. Returns true if
+ * so, false if not. */
+static bool shortyDescMatch(char shorty, const char* descriptor, bool
+ isReturnType) {
+ switch (shorty) {
+ case 'V': {
+ if (!isReturnType) {
+ LOGE("Invalid use of void\n");
+ return false;
+ }
+ // Fall through.
+ }
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'F':
+ case 'I':
+ case 'J':
+ case 'S':
+ case 'Z': {
+ if ((descriptor[0] != shorty) || (descriptor[1] != '\0')) {
+ LOGE("Shorty vs. primitive type mismatch: '%c', '%s'\n",
+ shorty, descriptor);
+ return false;
+ }
+ break;
+ }
+ case 'L': {
+ if ((descriptor[0] != 'L') && (descriptor[0] != '[')) {
+ LOGE("Shorty vs. type mismatch: '%c', '%s'\n",
+ shorty, descriptor);
+ return false;
+ }
+ break;
+ }
+ default: {
+ LOGE("Bogus shorty: '%c'\n", shorty);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/* Perform cross-item verification of proto_id_item. */
+static void* crossVerifyProtoIdItem(const CheckState* state, void* ptr) {
+ const DexProtoId* item = ptr;
+ const char* shorty =
+ dexStringById(state->pDexFile, item->shortyIdx);
+
+ if (!dexDataMapVerify0Ok(state->pDataMap,
+ item->parametersOff, kDexTypeTypeList)) {
+ return NULL;
+ }
+
+ if (!shortyDescMatch(*shorty,
+ dexStringByTypeIdx(state->pDexFile, item->returnTypeIdx),
+ true)) {
+ return NULL;
+ }
+
+ u4 protoIdx = item - state->pDexFile->pProtoIds;
+ DexProto proto = { state->pDexFile, protoIdx };
+ DexParameterIterator iterator;
+
+ dexParameterIteratorInit(&iterator, &proto);
+ shorty++; // Skip the return type.
+
+ for (;;) {
+ const char *desc = dexParameterIteratorNextDescriptor(&iterator);
+
+ if (desc == NULL) {
+ break;
+ }
+
+ if (*shorty == '\0') {
+ LOGE("Shorty is too short\n");
+ return NULL;
+ }
+
+ if (!shortyDescMatch(*shorty, desc, false)) {
+ return NULL;
+ }
+
+ shorty++;
+ }
+
+ if (*shorty != '\0') {
+ LOGE("Shorty is too long\n");
+ return NULL;
+ }
+
+ const DexProtoId* item0 = state->previousItem;
+ if (item0 != NULL) {
+ // Check ordering. This relies on type_ids being in order.
+ if (item0->returnTypeIdx > item->returnTypeIdx) {
+ LOGE("Out-of-order proto_id return types\n");
+ return NULL;
+ } else if (item0->returnTypeIdx == item->returnTypeIdx) {
+ bool badOrder = false;
+ DexProto proto0 = { state->pDexFile, protoIdx - 1 };
+ DexParameterIterator iterator0;
+
+ dexParameterIteratorInit(&iterator, &proto);
+ dexParameterIteratorInit(&iterator0, &proto0);
+
+ for (;;) {
+ u4 idx0 = dexParameterIteratorNextIndex(&iterator0);
+ u4 idx1 = dexParameterIteratorNextIndex(&iterator);
+
+ if (idx1 == kDexNoIndex) {
+ badOrder = true;
+ break;
+ }
+
+ if (idx0 == kDexNoIndex) {
+ break;
+ }
+
+ if (idx0 < idx1) {
+ break;
+ } else if (idx0 > idx1) {
+ badOrder = true;
+ break;
+ }
+ }
+
+ if (badOrder) {
+ LOGE("Out-of-order proto_id arguments\n");
+ return NULL;
+ }
+ }
+ }
+
+ return (void*) (item + 1);
+}
+
+/* Perform byte-swapping and intra-item verification on field_id_item. */
+static void* swapFieldIdItem(const CheckState* state, void* ptr) {
+ DexFieldId* item = ptr;
+
+ CHECK_PTR_RANGE(item, item + 1);
+ SWAP_INDEX2(item->classIdx, state->pHeader->typeIdsSize);
+ SWAP_INDEX2(item->typeIdx, state->pHeader->typeIdsSize);
+ SWAP_INDEX4(item->nameIdx, state->pHeader->stringIdsSize);
+
+ return item + 1;
+}
+
+/* Perform cross-item verification of field_id_item. */
+static void* crossVerifyFieldIdItem(const CheckState* state, void* ptr) {
+ const DexFieldId* item = ptr;
+ const char* s;
+
+ s = dexStringByTypeIdx(state->pDexFile, item->classIdx);
+ if (!dexIsClassDescriptor(s)) {
+ LOGE("Invalid descriptor for class_idx: '%s'\n", s);
+ return NULL;
+ }
+
+ s = dexStringByTypeIdx(state->pDexFile, item->typeIdx);
+ if (!dexIsFieldDescriptor(s)) {
+ LOGE("Invalid descriptor for type_idx: '%s'\n", s);
+ return NULL;
+ }
+
+ s = dexStringById(state->pDexFile, item->nameIdx);
+ if (!dexIsValidMemberName(s)) {
+ LOGE("Invalid name: '%s'\n", s);
+ return NULL;
+ }
+
+ const DexFieldId* item0 = state->previousItem;
+ if (item0 != NULL) {
+ // Check ordering. This relies on the other sections being in order.
+ bool done = false;
+ bool bogus = false;
+
+ if (item0->classIdx > item->classIdx) {
+ bogus = true;
+ done = true;
+ } else if (item0->classIdx < item->classIdx) {
+ done = true;
+ }
+
+ if (!done) {
+ if (item0->nameIdx > item->nameIdx) {
+ bogus = true;
+ done = true;
+ } else if (item0->nameIdx < item->nameIdx) {
+ done = true;
+ }
+ }
+
+ if (!done) {
+ if (item0->typeIdx >= item->typeIdx) {
+ bogus = true;
+ }
+ }
+
+ if (bogus) {
+ LOGE("Out-of-order field_ids\n");
+ return NULL;
+ }
+ }
+
+ return (void*) (item + 1);
+}
+
+/* Perform byte-swapping and intra-item verification on method_id_item. */
+static void* swapMethodIdItem(const CheckState* state, void* ptr) {
+ DexMethodId* item = ptr;
+
+ CHECK_PTR_RANGE(item, item + 1);
+ SWAP_INDEX2(item->classIdx, state->pHeader->typeIdsSize);
+ SWAP_INDEX2(item->protoIdx, state->pHeader->protoIdsSize);
+ SWAP_INDEX4(item->nameIdx, state->pHeader->stringIdsSize);
+
+ return item + 1;
+}
+
+/* Perform cross-item verification of method_id_item. */
+static void* crossVerifyMethodIdItem(const CheckState* state, void* ptr) {
+ const DexMethodId* item = ptr;
+ const char* s;
+
+ s = dexStringByTypeIdx(state->pDexFile, item->classIdx);
+ if (!dexIsReferenceDescriptor(s)) {
+ LOGE("Invalid descriptor for class_idx: '%s'\n", s);
+ return NULL;
+ }
+
+ s = dexStringById(state->pDexFile, item->nameIdx);
+ if (!dexIsValidMemberName(s)) {
+ LOGE("Invalid name: '%s'\n", s);
+ return NULL;
+ }
+
+ const DexMethodId* item0 = state->previousItem;
+ if (item0 != NULL) {
+ // Check ordering. This relies on the other sections being in order.
+ bool done = false;
+ bool bogus = false;
+
+ if (item0->classIdx > item->classIdx) {
+ bogus = true;
+ done = true;
+ } else if (item0->classIdx < item->classIdx) {
+ done = true;
+ }
+
+ if (!done) {
+ if (item0->nameIdx > item->nameIdx) {
+ bogus = true;
+ done = true;
+ } else if (item0->nameIdx < item->nameIdx) {
+ done = true;
+ }
+ }
+
+ if (!done) {
+ if (item0->protoIdx >= item->protoIdx) {
+ bogus = true;
+ }
+ }
+
+ if (bogus) {
+ LOGE("Out-of-order method_ids\n");
+ return NULL;
+ }
+ }
+
+ return (void*) (item + 1);
+}
+
+/* Perform byte-swapping and intra-item verification on class_def_item. */
+static void* swapClassDefItem(const CheckState* state, void* ptr) {
+ DexClassDef* item = ptr;
+
+ CHECK_PTR_RANGE(item, item + 1);
+ SWAP_INDEX4(item->classIdx, state->pHeader->typeIdsSize);
+ SWAP_FIELD4(item->accessFlags);
+ SWAP_INDEX4_OR_NOINDEX(item->superclassIdx, state->pHeader->typeIdsSize);
+ SWAP_OFFSET4(item->interfacesOff);
+ SWAP_INDEX4_OR_NOINDEX(item->sourceFileIdx, state->pHeader->stringIdsSize);
+ SWAP_OFFSET4(item->annotationsOff);
+ SWAP_OFFSET4(item->classDataOff);
+
+ return item + 1;
+}
+
+/* defined below */
+static u4 findFirstClassDataDefiner(const CheckState* state,
+ DexClassData* classData);
+static u4 findFirstAnnotationsDirectoryDefiner(const CheckState* state,
+ const DexAnnotationsDirectoryItem* dir);
+
+/* Helper for crossVerifyClassDefItem(), which checks a class_data_item to
+ * make sure all its references are to a given class. */
+static bool verifyClassDataIsForDef(const CheckState* state, u4 offset,
+ u4 definerIdx) {
+ if (offset == 0) {
+ return true;
+ }
+
+ const u1* data = filePointer(state, offset);
+ DexClassData* classData = dexReadAndVerifyClassData(&data, NULL);
+
+ if (classData == NULL) {
+ // Shouldn't happen, but bail here just in case.
+ return false;
+ }
+
+ /*
+ * The class_data_item verification ensures that
+ * it consistently refers to the same definer, so all we need to
+ * do is check the first one.
+ */
+ u4 dataDefiner = findFirstClassDataDefiner(state, classData);
+ bool result = (dataDefiner == definerIdx) || (dataDefiner == kDexNoIndex);
+
+ free(classData);
+ return result;
+}
+
+/* Helper for crossVerifyClassDefItem(), which checks an
+ * annotations_directory_item to make sure all its references are to a
+ * given class. */
+static bool verifyAnnotationsDirectoryIsForDef(const CheckState* state,
+ u4 offset, u4 definerIdx) {
+ if (offset == 0) {
+ return true;
+ }
+
+ const DexAnnotationsDirectoryItem* dir = filePointer(state, offset);
+ u4 annoDefiner = findFirstAnnotationsDirectoryDefiner(state, dir);
+
+ return (annoDefiner == definerIdx) || (annoDefiner == kDexNoIndex);
+}
+
+/* Perform cross-item verification of class_def_item. */
+static void* crossVerifyClassDefItem(const CheckState* state, void* ptr) {
+ const DexClassDef* item = ptr;
+ u4 classIdx = item->classIdx;
+ const char* descriptor = dexStringByTypeIdx(state->pDexFile, classIdx);
+
+ if (!dexIsClassDescriptor(descriptor)) {
+ LOGE("Invalid class: '%s'\n", descriptor);
+ return NULL;
+ }
+
+ if (setDefinedClassBit(state, classIdx)) {
+ LOGE("Duplicate class definition: '%s'\n", descriptor);
+ return NULL;
+ }
+
+ bool okay =
+ dexDataMapVerify0Ok(state->pDataMap,
+ item->interfacesOff, kDexTypeTypeList)
+ && dexDataMapVerify0Ok(state->pDataMap,
+ item->annotationsOff, kDexTypeAnnotationsDirectoryItem)
+ && dexDataMapVerify0Ok(state->pDataMap,
+ item->classDataOff, kDexTypeClassDataItem)
+ && dexDataMapVerify0Ok(state->pDataMap,
+ item->staticValuesOff, kDexTypeEncodedArrayItem);
+
+ if (!okay) {
+ return NULL;
+ }
+
+ if (item->superclassIdx != kDexNoIndex) {
+ descriptor = dexStringByTypeIdx(state->pDexFile, item->superclassIdx);
+ if (!dexIsClassDescriptor(descriptor)) {
+ LOGE("Invalid superclass: '%s'\n", descriptor);
+ return NULL;
+ }
+ }
+
+ const DexTypeList* interfaces =
+ dexGetInterfacesList(state->pDexFile, item);
+ if (interfaces != NULL) {
+ u4 size = interfaces->size;
+ u4 i;
+
+ /*
+ * Ensure that all interfaces refer to classes (not arrays or
+ * primitives).
+ */
+ for (i = 0; i < size; i++) {
+ descriptor = dexStringByTypeIdx(state->pDexFile,
+ dexTypeListGetIdx(interfaces, i));
+ if (!dexIsClassDescriptor(descriptor)) {
+ LOGE("Invalid interface: '%s'\n", descriptor);
+ return NULL;
+ }
+ }
+
+ /*
+ * Ensure that there are no duplicates. This is an O(N^2) test,
+ * but in practice the number of interfaces implemented by any
+ * given class is low. I will buy a milkshake for the
+ * first person to show me a realistic case for which this test
+ * would be unacceptably slow.
+ */
+ for (i = 1; i < size; i++) {
+ u4 idx1 = dexTypeListGetIdx(interfaces, i);
+ u4 j;
+ for (j = 0; j < i; j++) {
+ u4 idx2 = dexTypeListGetIdx(interfaces, j);
+ if (idx1 == idx2) {
+ LOGE("Duplicate interface: '%s'\n",
+ dexStringByTypeIdx(state->pDexFile, idx1));
+ return NULL;
+ }
+ }
+ }
+ }
+
+ if (!verifyClassDataIsForDef(state, item->classDataOff, item->classIdx)) {
+ LOGE("Invalid class_data_item\n");
+ return NULL;
+ }
+
+ if (!verifyAnnotationsDirectoryIsForDef(state, item->annotationsOff,
+ item->classIdx)) {
+ LOGE("Invalid annotations_directory_item\n");
+ return NULL;
+ }
+
+ return (void*) (item + 1);
+}
+
+/* Helper for swapAnnotationsDirectoryItem(), which performs
+ * byte-swapping and intra-item verification on an
+ * annotation_directory_item's field elements. */
+static u1* swapFieldAnnotations(const CheckState* state, u4 count, u1* addr) {
+ DexFieldAnnotationsItem* item = (DexFieldAnnotationsItem*) addr;
+ bool first = true;
+ u4 lastIdx = 0;
+
+ CHECK_LIST_SIZE(item, count, sizeof(DexFieldAnnotationsItem));
+
+ while (count--) {
+ SWAP_INDEX4(item->fieldIdx, state->pHeader->fieldIdsSize);
+ SWAP_OFFSET4(item->annotationsOff);
+
+ if (first) {
+ first = false;
+ } else if (lastIdx >= item->fieldIdx) {
+ LOGE("Out-of-order field_idx: 0x%x then 0x%x\n", lastIdx,
+ item->fieldIdx);
+ return NULL;
+ }
+
+ lastIdx = item->fieldIdx;
+ item++;
+ }
+
+ return (u1*) item;
+}
+
+/* Helper for swapAnnotationsDirectoryItem(), which performs
+ * byte-swapping and intra-item verification on an
+ * annotation_directory_item's method elements. */
+static u1* swapMethodAnnotations(const CheckState* state, u4 count, u1* addr) {
+ DexMethodAnnotationsItem* item = (DexMethodAnnotationsItem*) addr;
+ bool first = true;
+ u4 lastIdx = 0;
+
+ CHECK_LIST_SIZE(item, count, sizeof(DexMethodAnnotationsItem));
+
+ while (count--) {
+ SWAP_INDEX4(item->methodIdx, state->pHeader->methodIdsSize);
+ SWAP_OFFSET4(item->annotationsOff);
+
+ if (first) {
+ first = false;
+ } else if (lastIdx >= item->methodIdx) {
+ LOGE("Out-of-order method_idx: 0x%x then 0x%x\n", lastIdx,
+ item->methodIdx);
+ return NULL;
+ }
+
+ lastIdx = item->methodIdx;
+ item++;
+ }
+
+ return (u1*) item;
+}
+
+/* Helper for swapAnnotationsDirectoryItem(), which performs
+ * byte-swapping and intra-item verification on an
+ * annotation_directory_item's parameter elements. */
+static u1* swapParameterAnnotations(const CheckState* state, u4 count,
+ u1* addr) {
+ DexParameterAnnotationsItem* item = (DexParameterAnnotationsItem*) addr;
+ bool first = true;
+ u4 lastIdx = 0;
+
+ CHECK_LIST_SIZE(item, count, sizeof(DexParameterAnnotationsItem));
+
+ while (count--) {
+ SWAP_INDEX4(item->methodIdx, state->pHeader->methodIdsSize);
+ SWAP_OFFSET4(item->annotationsOff);
+
+ if (first) {
+ first = false;
+ } else if (lastIdx >= item->methodIdx) {
+ LOGE("Out-of-order method_idx: 0x%x then 0x%x\n", lastIdx,
+ item->methodIdx);
+ return NULL;
+ }
+
+ lastIdx = item->methodIdx;
+ item++;
+ }
+
+ return (u1*) item;
+}
+
+/* Perform byte-swapping and intra-item verification on
+ * annotations_directory_item. */
+static void* swapAnnotationsDirectoryItem(const CheckState* state, void* ptr) {
+ DexAnnotationsDirectoryItem* item = ptr;
+
+ CHECK_PTR_RANGE(item, item + 1);
+ SWAP_OFFSET4(item->classAnnotationsOff);
+ SWAP_FIELD4(item->fieldsSize);
+ SWAP_FIELD4(item->methodsSize);
+ SWAP_FIELD4(item->parametersSize);
+
+ u1* addr = (u1*) (item + 1);
+
+ if (item->fieldsSize != 0) {
+ addr = swapFieldAnnotations(state, item->fieldsSize, addr);
+ if (addr == NULL) {
+ return NULL;
+ }
+ }
+
+ if (item->methodsSize != 0) {
+ addr = swapMethodAnnotations(state, item->methodsSize, addr);
+ if (addr == NULL) {
+ return NULL;
+ }
+ }
+
+ if (item->parametersSize != 0) {
+ addr = swapParameterAnnotations(state, item->parametersSize, addr);
+ if (addr == NULL) {
+ return NULL;
+ }
+ }
+
+ return addr;
+}
+
+/* Helper for crossVerifyAnnotationsDirectoryItem(), which checks the
+ * field elements. */
+static const u1* crossVerifyFieldAnnotations(const CheckState* state, u4 count,
+ const u1* addr, u4 definingClass) {
+ const DexFieldAnnotationsItem* item = (DexFieldAnnotationsItem*) addr;
+
+ while (count--) {
+ if (!verifyFieldDefiner(state, definingClass, item->fieldIdx)) {
+ return NULL;
+ }
+ if (!dexDataMapVerify(state->pDataMap, item->annotationsOff,
+ kDexTypeAnnotationSetItem)) {
+ return NULL;
+ }
+ item++;
+ }
+
+ return (const u1*) item;
+}
+
+/* Helper for crossVerifyAnnotationsDirectoryItem(), which checks the
+ * method elements. */
+static const u1* crossVerifyMethodAnnotations(const CheckState* state,
+ u4 count, const u1* addr, u4 definingClass) {
+ const DexMethodAnnotationsItem* item = (DexMethodAnnotationsItem*) addr;
+
+ while (count--) {
+ if (!verifyMethodDefiner(state, definingClass, item->methodIdx)) {
+ return NULL;
+ }
+ if (!dexDataMapVerify(state->pDataMap, item->annotationsOff,
+ kDexTypeAnnotationSetItem)) {
+ return NULL;
+ }
+ item++;
+ }
+
+ return (const u1*) item;
+}
+
+/* Helper for crossVerifyAnnotationsDirectoryItem(), which checks the
+ * parameter elements. */
+static const u1* crossVerifyParameterAnnotations(const CheckState* state,
+ u4 count, const u1* addr, u4 definingClass) {
+ const DexParameterAnnotationsItem* item =
+ (DexParameterAnnotationsItem*) addr;
+
+ while (count--) {
+ if (!verifyMethodDefiner(state, definingClass, item->methodIdx)) {
+ return NULL;
+ }
+ if (!dexDataMapVerify(state->pDataMap, item->annotationsOff,
+ kDexTypeAnnotationSetRefList)) {
+ return NULL;
+ }
+ item++;
+ }
+
+ return (const u1*) item;
+}
+
+/* Helper for crossVerifyClassDefItem() and
+ * crossVerifyAnnotationsDirectoryItem(), which finds the type_idx of
+ * the definer of the first item in the data. */
+static u4 findFirstAnnotationsDirectoryDefiner(const CheckState* state,
+ const DexAnnotationsDirectoryItem* dir) {
+ if (dir->fieldsSize != 0) {
+ const DexFieldAnnotationsItem* fields =
+ dexGetFieldAnnotations(state->pDexFile, dir);
+ const DexFieldId* field =
+ dexGetFieldId(state->pDexFile, fields[0].fieldIdx);
+ return field->classIdx;
+ }
+
+ if (dir->methodsSize != 0) {
+ const DexMethodAnnotationsItem* methods =
+ dexGetMethodAnnotations(state->pDexFile, dir);
+ const DexMethodId* method =
+ dexGetMethodId(state->pDexFile, methods[0].methodIdx);
+ return method->classIdx;
+ }
+
+ if (dir->parametersSize != 0) {
+ const DexParameterAnnotationsItem* parameters =
+ dexGetParameterAnnotations(state->pDexFile, dir);
+ const DexMethodId* method =
+ dexGetMethodId(state->pDexFile, parameters[0].methodIdx);
+ return method->classIdx;
+ }
+
+ return kDexNoIndex;
+}
+
+/* Perform cross-item verification of annotations_directory_item. */
+static void* crossVerifyAnnotationsDirectoryItem(const CheckState* state,
+ void* ptr) {
+ const DexAnnotationsDirectoryItem* item = ptr;
+ u4 definingClass = findFirstAnnotationsDirectoryDefiner(state, item);
+
+ if (!dexDataMapVerify0Ok(state->pDataMap,
+ item->classAnnotationsOff, kDexTypeAnnotationSetItem)) {
+ return NULL;
+ }
+
+ const u1* addr = (const u1*) (item + 1);
+
+ if (item->fieldsSize != 0) {
+ addr = crossVerifyFieldAnnotations(state, item->fieldsSize, addr,
+ definingClass);
+ if (addr == NULL) {
+ return NULL;
+ }
+ }
+
+ if (item->methodsSize != 0) {
+ addr = crossVerifyMethodAnnotations(state, item->methodsSize, addr,
+ definingClass);
+ if (addr == NULL) {
+ return NULL;
+ }
+ }
+
+ if (item->parametersSize != 0) {
+ addr = crossVerifyParameterAnnotations(state, item->parametersSize,
+ addr, definingClass);
+ if (addr == NULL) {
+ return NULL;
+ }
+ }
+
+ return (void*) addr;
+}
+
+/* Perform byte-swapping and intra-item verification on type_list. */
+static void* swapTypeList(const CheckState* state, void* ptr)
+{
+ DexTypeList* pTypeList = ptr;
+ DexTypeItem* pType;
+ u4 count;
+
+ CHECK_PTR_RANGE(pTypeList, pTypeList + 1);
+ SWAP_FIELD4(pTypeList->size);
+ count = pTypeList->size;
+ pType = pTypeList->list;
+ CHECK_LIST_SIZE(pType, count, sizeof(DexTypeItem));
+
+ while (count--) {
+ SWAP_INDEX2(pType->typeIdx, state->pHeader->typeIdsSize);
+ pType++;
+ }
+
+ return pType;
+}
+
+/* Perform byte-swapping and intra-item verification on
+ * annotation_set_ref_list. */
+static void* swapAnnotationSetRefList(const CheckState* state, void* ptr) {
+ DexAnnotationSetRefList* list = ptr;
+ DexAnnotationSetRefItem* item;
+ u4 count;
+
+ CHECK_PTR_RANGE(list, list + 1);
+ SWAP_FIELD4(list->size);
+ count = list->size;
+ item = list->list;
+ CHECK_LIST_SIZE(item, count, sizeof(DexAnnotationSetRefItem));
+
+ while (count--) {
+ SWAP_OFFSET4(item->annotationsOff);
+ item++;
+ }
+
+ return item;
+}
+
+/* Perform cross-item verification of annotation_set_ref_list. */
+static void* crossVerifyAnnotationSetRefList(const CheckState* state,
+ void* ptr) {
+ const DexAnnotationSetRefList* list = ptr;
+ const DexAnnotationSetRefItem* item = list->list;
+ int count = list->size;
+
+ while (count--) {
+ if (!dexDataMapVerify0Ok(state->pDataMap,
+ item->annotationsOff, kDexTypeAnnotationSetItem)) {
+ return NULL;
+ }
+ item++;
+ }
+
+ return (void*) item;
+}
+
+/* Perform byte-swapping and intra-item verification on
+ * annotation_set_item. */
+static void* swapAnnotationSetItem(const CheckState* state, void* ptr) {
+ DexAnnotationSetItem* set = ptr;
+ u4* item;
+ u4 count;
+
+ CHECK_PTR_RANGE(set, set + 1);
+ SWAP_FIELD4(set->size);
+ count = set->size;
+ item = set->entries;
+ CHECK_LIST_SIZE(item, count, sizeof(u4));
+
+ while (count--) {
+ SWAP_OFFSET4(*item);
+ item++;
+ }
+
+ return item;
+}
+
+/* Helper for crossVerifyAnnotationSetItem(), which extracts the type_idx
+ * out of an annotation_item. */
+static u4 annotationItemTypeIdx(const DexAnnotationItem* item) {
+ const u1* data = item->annotation;
+ return readUnsignedLeb128(&data);
+}
+
+/* Perform cross-item verification of annotation_set_item. */
+static void* crossVerifyAnnotationSetItem(const CheckState* state, void* ptr) {
+ const DexAnnotationSetItem* set = ptr;
+ int count = set->size;
+ u4 lastIdx = 0;
+ bool first = true;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ if (!dexDataMapVerify0Ok(state->pDataMap,
+ dexGetAnnotationOff(set, i), kDexTypeAnnotationItem)) {
+ return NULL;
+ }
+
+ const DexAnnotationItem* annotation =
+ dexGetAnnotationItem(state->pDexFile, set, i);
+ u4 idx = annotationItemTypeIdx(annotation);
+
+ if (first) {
+ first = false;
+ } else if (lastIdx >= idx) {
+ LOGE("Out-of-order entry types: 0x%x then 0x%x\n",
+ lastIdx, idx);
+ return NULL;
+ }
+
+ lastIdx = idx;
+ }
+
+ return (void*) (set->entries + count);
+}
+
+/* Helper for verifyClassDataItem(), which checks a list of fields. */
+static bool verifyFields(const CheckState* state, u4 size,
+ DexField* fields, bool expectStatic) {
+ u4 i;
+
+ for (i = 0; i < size; i++) {
+ DexField* field = &fields[i];
+ u4 accessFlags = field->accessFlags;
+ bool isStatic = (accessFlags & ACC_STATIC) != 0;
+
+ CHECK_INDEX(field->fieldIdx, state->pHeader->fieldIdsSize);
+
+ if (isStatic != expectStatic) {
+ LOGE("Field in wrong list @ %d\n", i);
+ return false;
+ }
+
+ if ((accessFlags & ~ACC_FIELD_MASK) != 0) {
+ LOGE("Bogus field access flags %x @ %d\n", accessFlags, i);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/* Helper for verifyClassDataItem(), which checks a list of methods. */
+static bool verifyMethods(const CheckState* state, u4 size,
+ DexMethod* methods, bool expectDirect) {
+ u4 i;
+
+ for (i = 0; i < size; i++) {
+ DexMethod* method = &methods[i];
+
+ CHECK_INDEX(method->methodIdx, state->pHeader->methodIdsSize);
+
+ u4 accessFlags = method->accessFlags;
+ bool isDirect =
+ (accessFlags & (ACC_STATIC | ACC_PRIVATE | ACC_CONSTRUCTOR)) != 0;
+ bool expectCode = (accessFlags & (ACC_NATIVE | ACC_ABSTRACT)) == 0;
+ bool isSynchronized = (accessFlags & ACC_SYNCHRONIZED) != 0;
+ bool allowSynchronized = (accessFlags & ACC_NATIVE) != 0;
+
+ if (isDirect != expectDirect) {
+ LOGE("Method in wrong list @ %d\n", i);
+ return false;
+ }
+
+ if (((accessFlags & ~ACC_METHOD_MASK) != 0)
+ || (isSynchronized && !allowSynchronized)) {
+ LOGE("Bogus method access flags %x @ %d\n", accessFlags, i);
+ return false;
+ }
+
+ if (expectCode) {
+ if (method->codeOff == 0) {
+ LOGE("Unexpected zero code_off for access_flags %x\n",
+ accessFlags);
+ return false;
+ }
+ } else if (method->codeOff != 0) {
+ LOGE("Unexpected non-zero code_off 0x%x for access_flags %x\n",
+ method->codeOff, accessFlags);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/* Helper for verifyClassDataItem(), which does most of the work. */
+static bool verifyClassDataItem0(const CheckState* state,
+ DexClassData* classData) {
+ bool okay;
+
+ okay = verifyFields(state, classData->header.staticFieldsSize,
+ classData->staticFields, true);
+
+ if (!okay) {
+ LOGE("Trouble with static fields\n");
+ return false;
+ }
+
+ verifyFields(state, classData->header.instanceFieldsSize,
+ classData->instanceFields, false);
+
+ if (!okay) {
+ LOGE("Trouble with instance fields\n");
+ return false;
+ }
+
+ okay = verifyMethods(state, classData->header.directMethodsSize,
+ classData->directMethods, true);
+
+ if (!okay) {
+ LOGE("Trouble with direct methods\n");
+ return false;
+ }
+
+ okay = verifyMethods(state, classData->header.virtualMethodsSize,
+ classData->virtualMethods, false);
+
+ if (!okay) {
+ LOGE("Trouble with virtual methods\n");
+ return false;
+ }
+
+ return true;
+}
+
+/* Perform intra-item verification on class_data_item. */
+static void* intraVerifyClassDataItem(const CheckState* state, void* ptr) {
+ const u1* data = ptr;
+ DexClassData* classData = dexReadAndVerifyClassData(&data, state->fileEnd);
+
+ if (classData == NULL) {
+ LOGE("Unable to parse class_data_item\n");
+ return NULL;
+ }
+
+ bool okay = verifyClassDataItem0(state, classData);
+
+ free(classData);
+
+ if (!okay) {
+ return NULL;
+ }
+
+ return (void*) data;
+}
+
+/* Helper for crossVerifyClassDefItem() and
+ * crossVerifyClassDataItem(), which finds the type_idx of the definer
+ * of the first item in the data. */
+static u4 findFirstClassDataDefiner(const CheckState* state,
+ DexClassData* classData) {
+ if (classData->header.staticFieldsSize != 0) {
+ u4 fieldIdx = classData->staticFields[0].fieldIdx;
+ const DexFieldId* field = dexGetFieldId(state->pDexFile, fieldIdx);
+ return field->classIdx;
+ }
+
+ if (classData->header.instanceFieldsSize != 0) {
+ u4 fieldIdx = classData->instanceFields[0].fieldIdx;
+ const DexFieldId* field = dexGetFieldId(state->pDexFile, fieldIdx);
+ return field->classIdx;
+ }
+
+ if (classData->header.directMethodsSize != 0) {
+ u4 methodIdx = classData->directMethods[0].methodIdx;
+ const DexMethodId* meth = dexGetMethodId(state->pDexFile, methodIdx);
+ return meth->classIdx;
+ }
+
+ if (classData->header.virtualMethodsSize != 0) {
+ u4 methodIdx = classData->virtualMethods[0].methodIdx;
+ const DexMethodId* meth = dexGetMethodId(state->pDexFile, methodIdx);
+ return meth->classIdx;
+ }
+
+ return kDexNoIndex;
+}
+
+/* Perform cross-item verification of class_data_item. */
+static void* crossVerifyClassDataItem(const CheckState* state, void* ptr) {
+ const u1* data = ptr;
+ DexClassData* classData = dexReadAndVerifyClassData(&data, state->fileEnd);
+ u4 definingClass = findFirstClassDataDefiner(state, classData);
+ bool okay = true;
+ u4 i;
+
+ for (i = classData->header.staticFieldsSize; okay && (i > 0); /*i*/) {
+ i--;
+ const DexField* field = &classData->staticFields[i];
+ okay = verifyFieldDefiner(state, definingClass, field->fieldIdx);
+ }
+
+ for (i = classData->header.instanceFieldsSize; okay && (i > 0); /*i*/) {
+ i--;
+ const DexField* field = &classData->instanceFields[i];
+ okay = verifyFieldDefiner(state, definingClass, field->fieldIdx);
+ }
+
+ for (i = classData->header.directMethodsSize; okay && (i > 0); /*i*/) {
+ i--;
+ const DexMethod* meth = &classData->directMethods[i];
+ okay = dexDataMapVerify0Ok(state->pDataMap, meth->codeOff,
+ kDexTypeCodeItem)
+ && verifyMethodDefiner(state, definingClass, meth->methodIdx);
+ }
+
+ for (i = classData->header.virtualMethodsSize; okay && (i > 0); /*i*/) {
+ i--;
+ const DexMethod* meth = &classData->virtualMethods[i];
+ okay = dexDataMapVerify0Ok(state->pDataMap, meth->codeOff,
+ kDexTypeCodeItem)
+ && verifyMethodDefiner(state, definingClass, meth->methodIdx);
+ }
+
+ free(classData);
+
+ if (!okay) {
+ return NULL;
+ }
+
+ return (void*) data;
+}
+
+/* Helper for swapCodeItem(), which fills an array with all the valid
+ * handlerOff values for catch handlers and also verifies the handler
+ * contents. */
+static u4 setHandlerOffsAndVerify(const CheckState* state,
+ DexCode* code, u4 firstOffset, u4 handlersSize, u4* handlerOffs) {
+ const u1* fileEnd = state->fileEnd;
+ const u1* handlersBase = dexGetCatchHandlerData(code);
+ u4 offset = firstOffset;
+ bool okay = true;
+ u4 i;
+
+ for (i = 0; i < handlersSize; i++) {
+ const u1* ptr = handlersBase + offset;
+ int size = readAndVerifySignedLeb128(&ptr, fileEnd, &okay);
+ bool catchAll;
+
+ if (!okay) {
+ LOGE("Bogus size\n");
+ return 0;
+ }
+
+ if ((size < -65536) || (size > 65536)) {
+ LOGE("Invalid size: %d\n", size);
+ return 0;
+ }
+
+ if (size <= 0) {
+ catchAll = true;
+ size = -size;
+ } else {
+ catchAll = false;
+ }
+
+ handlerOffs[i] = offset;
+
+ while (size-- > 0) {
+ u4 typeIdx =
+ readAndVerifyUnsignedLeb128(&ptr, fileEnd, &okay);
+
+ if (!okay) {
+ LOGE("Bogus type_idx");
+ return 0;
+ }
+
+ CHECK_INDEX(typeIdx, state->pHeader->typeIdsSize);
+
+ u4 addr = readAndVerifyUnsignedLeb128(&ptr, fileEnd, &okay);
+
+ if (!okay) {
+ LOGE("Bogus addr");
+ return 0;
+ }
+
+ if (addr >= code->insnsSize) {
+ LOGE("Invalid addr: 0x%x", addr);
+ return 0;
+ }
+ }
+
+ if (catchAll) {
+ u4 addr = readAndVerifyUnsignedLeb128(&ptr, fileEnd, &okay);
+
+ if (!okay) {
+ LOGE("Bogus catch_all_addr");
+ return 0;
+ }
+
+ if (addr >= code->insnsSize) {
+ LOGE("Invalid catch_all_addr: 0x%x", addr);
+ return 0;
+ }
+ }
+
+ offset = ptr - handlersBase;
+ }
+
+ return offset;
+}
+
+/* Helper for swapCodeItem(), which does all the try-catch related
+ * swapping and verification. */
+static void* swapTriesAndCatches(const CheckState* state, DexCode* code) {
+ const u1* encodedHandlers = dexGetCatchHandlerData(code);
+ const u1* encodedPtr = encodedHandlers;
+ bool okay = true;
+ u4 handlersSize =
+ readAndVerifyUnsignedLeb128(&encodedPtr, state->fileEnd, &okay);
+
+ if (!okay) {
+ LOGE("Bogus handlers_size\n");
+ return NULL;
+ }
+
+ if ((handlersSize == 0) || (handlersSize >= 65536)) {
+ LOGE("Invalid handlers_size: %d\n", handlersSize);
+ return NULL;
+ }
+
+ u4 handlerOffs[handlersSize]; // list of valid handlerOff values
+ u4 endOffset = setHandlerOffsAndVerify(state, code,
+ encodedPtr - encodedHandlers,
+ handlersSize, handlerOffs);
+
+ if (endOffset == 0) {
+ return NULL;
+ }
+
+ DexTry* tries = (DexTry*) dexGetTries(code);
+ u4 count = code->triesSize;
+ u4 lastEnd = 0;
+
+ CHECK_LIST_SIZE(tries, count, sizeof(DexTry));
+
+ while (count--) {
+ u4 i;
+
+ SWAP_FIELD4(tries->startAddr);
+ SWAP_FIELD2(tries->insnCount);
+ SWAP_FIELD2(tries->handlerOff);
+
+ if (tries->startAddr < lastEnd) {
+ LOGE("Out-of-order try\n");
+ return NULL;
+ }
+
+ if (tries->startAddr >= code->insnsSize) {
+ LOGE("Invalid start_addr: 0x%x\n", tries->startAddr);
+ return NULL;
+ }
+
+ for (i = 0; i < handlersSize; i++) {
+ if (tries->handlerOff == handlerOffs[i]) {
+ break;
+ }
+ }
+
+ if (i == handlersSize) {
+ LOGE("Bogus handler offset: 0x%x\n", tries->handlerOff);
+ return NULL;
+ }
+
+ lastEnd = tries->startAddr + tries->insnCount;
+
+ if (lastEnd > code->insnsSize) {
+ LOGE("Invalid insn_count: 0x%x (end addr 0x%x)\n",
+ tries->insnCount, lastEnd);
+ return NULL;
+ }
+
+ tries++;
+ }
+
+ return (u1*) encodedHandlers + endOffset;
+}
+
+/* Perform byte-swapping and intra-item verification on code_item. */
+static void* swapCodeItem(const CheckState* state, void* ptr) {
+ DexCode* item = ptr;
+ u2* insns;
+ u4 count;
+
+ CHECK_PTR_RANGE(item, item + 1);
+ SWAP_FIELD2(item->registersSize);
+ SWAP_FIELD2(item->insSize);
+ SWAP_FIELD2(item->outsSize);
+ SWAP_FIELD2(item->triesSize);
+ SWAP_OFFSET4(item->debugInfoOff);
+ SWAP_FIELD4(item->insnsSize);
+
+ if (item->insSize > item->registersSize) {
+ LOGE("insSize (%u) > registersSize (%u)\n", item->insSize,
+ item->registersSize);
+ return NULL;
+ }
+
+ if ((item->outsSize > 5) && (item->outsSize > item->registersSize)) {
+ /*
+ * It's okay for outsSize to be up to five, even if registersSize
+ * is smaller, since the short forms of method invocation allow
+ * repetition of a register multiple times within a single parameter
+ * list. Longer parameter lists, though, need to be represented
+ * in-order in the register file.
+ */
+ LOGE("outsSize (%u) > registersSize (%u)\n", item->outsSize,
+ item->registersSize);
+ return NULL;
+ }
+
+ count = item->insnsSize;
+ insns = item->insns;
+ CHECK_LIST_SIZE(insns, count, sizeof(u2));
+
+ while (count--) {
+ *insns = SWAP2(*insns);
+ insns++;
+ }
+
+ if (item->triesSize == 0) {
+ ptr = insns;
+ } else {
+ if ((((u4) insns) & 3) != 0) {
+ // Four-byte alignment for the tries. Verify the spacer is a 0.
+ if (*insns != 0) {
+ LOGE("Non-zero padding: 0x%x\n", (u4) *insns);
+ return NULL;
+ }
+ }
+
+ ptr = swapTriesAndCatches(state, item);
+ }
+
+ return ptr;
+}
+
+/* Perform intra-item verification on string_data_item. */
+static void* intraVerifyStringDataItem(const CheckState* state, void* ptr) {
+ const u1* fileEnd = state->fileEnd;
+ const u1* data = ptr;
+ bool okay = true;
+ u4 utf16Size = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ u4 i;
+
+ if (!okay) {
+ LOGE("Bogus utf16_size\n");
+ return NULL;
+ }
+
+ for (i = 0; i < utf16Size; i++) {
+ if (data >= fileEnd) {
+ LOGE("String data would go beyond end-of-file\n");
+ return NULL;
+ }
+
+ u1 byte1 = *(data++);
+
+ // Switch on the high four bits.
+ switch (byte1 >> 4) {
+ case 0x00: {
+ // Special case of bit pattern 0xxx.
+ if (byte1 == 0) {
+ LOGE("String shorter than indicated utf16_size 0x%x\n",
+ utf16Size);
+ return NULL;
+ }
+ break;
+ }
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07: {
+ // Bit pattern 0xxx. No need for any extra bytes or checks.
+ break;
+ }
+ case 0x08:
+ case 0x09:
+ case 0x0a:
+ case 0x0b:
+ case 0x0f: {
+ /*
+ * Bit pattern 10xx or 1111, which are illegal start bytes.
+ * Note: 1111 is valid for normal UTF-8, but not the
+ * modified UTF-8 used here.
+ */
+ LOGE("Illegal start byte 0x%x\n", byte1);
+ return NULL;
+ }
+ case 0x0e: {
+ // Bit pattern 1110, so there are two additional bytes.
+ u1 byte2 = *(data++);
+ if ((byte2 & 0xc0) != 0x80) {
+ LOGE("Illegal continuation byte 0x%x\n", byte2);
+ return NULL;
+ }
+ u1 byte3 = *(data++);
+ if ((byte3 & 0xc0) != 0x80) {
+ LOGE("Illegal continuation byte 0x%x\n", byte3);
+ return NULL;
+ }
+ u2 value = ((byte1 & 0x0f) << 12) | ((byte2 & 0x3f) << 6)
+ | (byte3 & 0x3f);
+ if (value < 0x800) {
+ LOGE("Illegal representation for value %x\n", value);
+ return NULL;
+ }
+ break;
+ }
+ case 0x0c:
+ case 0x0d: {
+ // Bit pattern 110x, so there is one additional byte.
+ u1 byte2 = *(data++);
+ if ((byte2 & 0xc0) != 0x80) {
+ LOGE("Illegal continuation byte 0x%x\n", byte2);
+ return NULL;
+ }
+ u2 value = ((byte1 & 0x1f) << 6) | (byte2 & 0x3f);
+ if ((value != 0) && (value < 0x80)) {
+ LOGE("Illegal representation for value %x\n", value);
+ return NULL;
+ }
+ break;
+ }
+ }
+ }
+
+ if (*(data++) != '\0') {
+ LOGE("String longer than indicated utf16_size 0x%x\n", utf16Size);
+ return NULL;
+ }
+
+ return (void*) data;
+}
+
+/* Perform intra-item verification on debug_info_item. */
+static void* intraVerifyDebugInfoItem(const CheckState* state, void* ptr) {
+ const u1* fileEnd = state->fileEnd;
+ const u1* data = ptr;
+ bool okay = true;
+ u4 i;
+
+ readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+
+ if (!okay) {
+ LOGE("Bogus line_start\n");
+ return NULL;
+ }
+
+ u4 parametersSize =
+ readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+
+ if (!okay) {
+ LOGE("Bogus parameters_size\n");
+ return NULL;
+ }
+
+ if (parametersSize > 65536) {
+ LOGE("Invalid parameters_size: 0x%x\n", parametersSize);
+ return NULL;
+ }
+
+ for (i = 0; i < parametersSize; i++) {
+ u4 parameterName =
+ readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+
+ if (!okay) {
+ LOGE("Bogus parameter_name\n");
+ return NULL;
+ }
+
+ if (parameterName != 0) {
+ parameterName--;
+ CHECK_INDEX(parameterName, state->pHeader->stringIdsSize);
+ }
+ }
+
+ bool done = false;
+ while (!done) {
+ u1 opcode = *(data++);
+
+ switch (opcode) {
+ case DBG_END_SEQUENCE: {
+ done = true;
+ break;
+ }
+ case DBG_ADVANCE_PC: {
+ readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ break;
+ }
+ case DBG_ADVANCE_LINE: {
+ readAndVerifySignedLeb128(&data, fileEnd, &okay);
+ break;
+ }
+ case DBG_START_LOCAL: {
+ u4 idx;
+ u4 regNum = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ if (!okay) break;
+ if (regNum >= 65536) {
+ okay = false;
+ break;
+ }
+ idx = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ if (!okay) break;
+ if (idx != 0) {
+ idx--;
+ CHECK_INDEX(idx, state->pHeader->stringIdsSize);
+ }
+ idx = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ if (!okay) break;
+ if (idx != 0) {
+ idx--;
+ CHECK_INDEX(idx, state->pHeader->stringIdsSize);
+ }
+ break;
+ }
+ case DBG_END_LOCAL:
+ case DBG_RESTART_LOCAL: {
+ u4 regNum = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ if (!okay) break;
+ if (regNum >= 65536) {
+ okay = false;
+ break;
+ }
+ break;
+ }
+ case DBG_START_LOCAL_EXTENDED: {
+ u4 idx;
+ u4 regNum = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ if (!okay) break;
+ if (regNum >= 65536) {
+ okay = false;
+ break;
+ }
+ idx = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ if (!okay) break;
+ if (idx != 0) {
+ idx--;
+ CHECK_INDEX(idx, state->pHeader->stringIdsSize);
+ }
+ idx = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ if (!okay) break;
+ if (idx != 0) {
+ idx--;
+ CHECK_INDEX(idx, state->pHeader->stringIdsSize);
+ }
+ idx = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ if (!okay) break;
+ if (idx != 0) {
+ idx--;
+ CHECK_INDEX(idx, state->pHeader->stringIdsSize);
+ }
+ break;
+ }
+ case DBG_SET_FILE: {
+ u4 idx = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ if (!okay) break;
+ if (idx != 0) {
+ idx--;
+ CHECK_INDEX(idx, state->pHeader->stringIdsSize);
+ }
+ break;
+ }
+ default: {
+ // No arguments to parse for anything else.
+ }
+ }
+
+ if (!okay) {
+ LOGE("Bogus syntax for opcode %02x\n", opcode);
+ return NULL;
+ }
+ }
+
+ return (void*) data;
+}
+
+/* defined below */
+static const u1* verifyEncodedValue(const CheckState* state, const u1* data,
+ bool crossVerify);
+static const u1* verifyEncodedAnnotation(const CheckState* state,
+ const u1* data, bool crossVerify);
+
+/* Helper for verifyEncodedValue(), which reads a 1- to 4- byte unsigned
+ * little endian value. */
+static u4 readUnsignedLittleEndian(const CheckState* state, const u1** pData,
+ u4 size) {
+ const u1* data = *pData;
+ u4 result = 0;
+ u4 i;
+
+ CHECK_PTR_RANGE(data, data + size);
+
+ for (i = 0; i < size; i++) {
+ result |= ((u4) *(data++)) << (i * 8);
+ }
+
+ *pData = data;
+ return result;
+}
+
+/* Helper for *VerifyAnnotationItem() and *VerifyEncodedArrayItem(), which
+ * verifies an encoded_array. */
+static const u1* verifyEncodedArray(const CheckState* state,
+ const u1* data, bool crossVerify) {
+ bool okay = true;
+ u4 size = readAndVerifyUnsignedLeb128(&data, state->fileEnd, &okay);
+
+ if (!okay) {
+ LOGE("Bogus encoded_array size\n");
+ return NULL;
+ }
+
+ while (size--) {
+ data = verifyEncodedValue(state, data, crossVerify);
+ if (data == NULL) {
+ LOGE("Bogus encoded_array value\n");
+ return NULL;
+ }
+ }
+
+ return data;
+}
+
+/* Helper for *VerifyAnnotationItem() and *VerifyEncodedArrayItem(), which
+ * verifies an encoded_value. */
+static const u1* verifyEncodedValue(const CheckState* state,
+ const u1* data, bool crossVerify) {
+ CHECK_PTR_RANGE(data, data + 1);
+
+ u1 headerByte = *(data++);
+ u4 valueType = headerByte & kDexAnnotationValueTypeMask;
+ u4 valueArg = headerByte >> kDexAnnotationValueArgShift;
+
+ switch (valueType) {
+ case kDexAnnotationByte: {
+ if (valueArg != 0) {
+ LOGE("Bogus byte size 0x%x\n", valueArg);
+ return NULL;
+ }
+ data++;
+ break;
+ }
+ case kDexAnnotationShort:
+ case kDexAnnotationChar: {
+ if (valueArg > 1) {
+ LOGE("Bogus char/short size 0x%x\n", valueArg);
+ return NULL;
+ }
+ data += valueArg + 1;
+ break;
+ }
+ case kDexAnnotationInt:
+ case kDexAnnotationFloat: {
+ if (valueArg > 3) {
+ LOGE("Bogus int/float size 0x%x\n", valueArg);
+ return NULL;
+ }
+ data += valueArg + 1;
+ break;
+ }
+ case kDexAnnotationLong:
+ case kDexAnnotationDouble: {
+ data += valueArg + 1;
+ break;
+ }
+ case kDexAnnotationString: {
+ if (valueArg > 3) {
+ LOGE("Bogus string size 0x%x\n", valueArg);
+ return NULL;
+ }
+ u4 idx = readUnsignedLittleEndian(state, &data, valueArg + 1);
+ CHECK_INDEX(idx, state->pHeader->stringIdsSize);
+ break;
+ }
+ case kDexAnnotationType: {
+ if (valueArg > 3) {
+ LOGE("Bogus type size 0x%x\n", valueArg);
+ return NULL;
+ }
+ u4 idx = readUnsignedLittleEndian(state, &data, valueArg + 1);
+ CHECK_INDEX(idx, state->pHeader->typeIdsSize);
+ break;
+ }
+ case kDexAnnotationField:
+ case kDexAnnotationEnum: {
+ if (valueArg > 3) {
+ LOGE("Bogus field/enum size 0x%x\n", valueArg);
+ return NULL;
+ }
+ u4 idx = readUnsignedLittleEndian(state, &data, valueArg + 1);
+ CHECK_INDEX(idx, state->pHeader->fieldIdsSize);
+ break;
+ }
+ case kDexAnnotationMethod: {
+ if (valueArg > 3) {
+ LOGE("Bogus method size 0x%x\n", valueArg);
+ return NULL;
+ }
+ u4 idx = readUnsignedLittleEndian(state, &data, valueArg + 1);
+ CHECK_INDEX(idx, state->pHeader->methodIdsSize);
+ break;
+ }
+ case kDexAnnotationArray: {
+ if (valueArg != 0) {
+ LOGE("Bogus array value_arg 0x%x\n", valueArg);
+ return NULL;
+ }
+ data = verifyEncodedArray(state, data, crossVerify);
+ break;
+ }
+ case kDexAnnotationAnnotation: {
+ if (valueArg != 0) {
+ LOGE("Bogus annotation value_arg 0x%x\n", valueArg);
+ return NULL;
+ }
+ data = verifyEncodedAnnotation(state, data, crossVerify);
+ break;
+ }
+ case kDexAnnotationNull: {
+ if (valueArg != 0) {
+ LOGE("Bogus null value_arg 0x%x\n", valueArg);
+ return NULL;
+ }
+ // Nothing else to do for this type.
+ break;
+ }
+ case kDexAnnotationBoolean: {
+ if (valueArg > 1) {
+ LOGE("Bogus boolean value_arg 0x%x\n", valueArg);
+ return NULL;
+ }
+ // Nothing else to do for this type.
+ break;
+ }
+ default: {
+ LOGE("Bogus value_type 0x%x\n", valueType);
+ return NULL;
+ }
+ }
+
+ return data;
+}
+
+/* Helper for *VerifyAnnotationItem() and *VerifyEncodedArrayItem(), which
+ * verifies an encoded_annotation. */
+static const u1* verifyEncodedAnnotation(const CheckState* state,
+ const u1* data, bool crossVerify) {
+ const u1* fileEnd = state->fileEnd;
+ bool okay = true;
+ u4 idx = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+
+ if (!okay) {
+ LOGE("Bogus encoded_annotation type_idx\n");
+ return NULL;
+ }
+
+ CHECK_INDEX(idx, state->pHeader->typeIdsSize);
+
+ if (crossVerify) {
+ const char* descriptor = dexStringByTypeIdx(state->pDexFile, idx);
+ if (!dexIsClassDescriptor(descriptor)) {
+ LOGE("Bogus annotation type: '%s'\n", descriptor);
+ return NULL;
+ }
+ }
+
+ u4 size = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ u4 lastIdx = 0;
+ bool first = true;
+
+ if (!okay) {
+ LOGE("Bogus encoded_annotation size\n");
+ return NULL;
+ }
+
+ while (size--) {
+ idx = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+
+ if (!okay) {
+ LOGE("Bogus encoded_annotation name_idx\n");
+ return NULL;
+ }
+
+ CHECK_INDEX(idx, state->pHeader->stringIdsSize);
+
+ if (crossVerify) {
+ const char* name = dexStringById(state->pDexFile, idx);
+ if (!dexIsValidMemberName(name)) {
+ LOGE("Bogus annotation member name: '%s'\n", name);
+ return NULL;
+ }
+ }
+
+ if (first) {
+ first = false;
+ } else if (lastIdx >= idx) {
+ LOGE("Out-of-order encoded_annotation name_idx: 0x%x then 0x%x\n",
+ lastIdx, idx);
+ return NULL;
+ }
+
+ data = verifyEncodedValue(state, data, crossVerify);
+ lastIdx = idx;
+
+ if (data == NULL) {
+ return NULL;
+ }
+ }
+
+ return data;
+}
+
+/* Perform intra-item verification on encoded_array_item. */
+static void* intraVerifyEncodedArrayItem(const CheckState* state, void* ptr) {
+ return (void*) verifyEncodedArray(state, (const u1*) ptr, false);
+}
+
+/* Perform intra-item verification on annotation_item. */
+static void* intraVerifyAnnotationItem(const CheckState* state, void* ptr) {
+ const u1* data = ptr;
+
+ CHECK_PTR_RANGE(data, data + 1);
+
+ switch (*(data++)) {
+ case kDexVisibilityBuild:
+ case kDexVisibilityRuntime:
+ case kDexVisibilitySystem: {
+ break;
+ }
+ default: {
+ LOGE("Bogus annotation visibility: 0x%x\n", *data);
+ return NULL;
+ }
+ }
+
+ return (void*) verifyEncodedAnnotation(state, data, false);
+}
+
+/* Perform cross-item verification on annotation_item. */
+static void* crossVerifyAnnotationItem(const CheckState* state, void* ptr) {
+ const u1* data = ptr;
+
+ // Skip the visibility byte.
+ data++;
+
+ return (void*) verifyEncodedAnnotation(state, data, true);
+}
+
+
+
+
+/*
+ * Function to visit an individual top-level item type.
+ */
+typedef void* ItemVisitorFunction(const CheckState* state, void* ptr);
+
+/*
+ * Iterate over all the items in a section, optionally updating the
+ * data map (done if mapType is passed as non-negative). The section
+ * must consist of concatenated items of the same type.
+ */
+static bool iterateSectionWithOptionalUpdate(CheckState* state,
+ u4 offset, u4 count, ItemVisitorFunction* func, u4 alignment,
+ u4* nextOffset, int mapType) {
+ u4 alignmentMask = alignment - 1;
+ u4 i;
+
+ state->previousItem = NULL;
+
+ for (i = 0; i < count; i++) {
+ u4 newOffset = (offset + alignmentMask) & ~alignmentMask;
+ u1* ptr = filePointer(state, newOffset);
+
+ if (offset < newOffset) {
+ ptr = filePointer(state, offset);
+ if (offset < newOffset) {
+ CHECK_OFFSET_RANGE(offset, newOffset);
+ while (offset < newOffset) {
+ if (*ptr != '\0') {
+ LOGE("Non-zero padding 0x%02x @ %x\n", *ptr, offset);
+ return false;
+ }
+ ptr++;
+ offset++;
+ }
+ }
+ }
+
+ u1* newPtr = (u1*) func(state, ptr);
+ newOffset = fileOffset(state, newPtr);
+
+ if (newPtr == NULL) {
+ LOGE("Trouble with item %d @ offset 0x%x\n", i, offset);
+ return false;
+ }
+
+ if (newOffset > state->fileLen) {
+ LOGE("Item %d @ offset 0x%x ends out of bounds\n", i, offset);
+ return false;
+ }
+
+ if (mapType >= 0) {
+ dexDataMapAdd(state->pDataMap, offset, mapType);
+ }
+
+ state->previousItem = ptr;
+ offset = newOffset;
+ }
+
+ if (nextOffset != NULL) {
+ *nextOffset = offset;
+ }
+
+ return true;
+}
+
+/*
+ * Iterate over all the items in a section. The section must consist of
+ * concatenated items of the same type. This variant will not update the data
+ * map.
+ */
+static bool iterateSection(CheckState* state, u4 offset, u4 count,
+ ItemVisitorFunction* func, u4 alignment, u4* nextOffset) {
+ return iterateSectionWithOptionalUpdate(state, offset, count, func,
+ alignment, nextOffset, -1);
+}
+
+/*
+ * Like iterateSection(), but also check that the offset and count match
+ * a given pair of expected values.
+ */
+static bool checkBoundsAndIterateSection(CheckState* state,
+ u4 offset, u4 count, u4 expectedOffset, u4 expectedCount,
+ ItemVisitorFunction* func, u4 alignment, u4* nextOffset) {
+ if (offset != expectedOffset) {
+ LOGE("Bogus offset for section: got 0x%x; expected 0x%x\n",
+ offset, expectedOffset);
+ return false;
+ }
+
+ if (count != expectedCount) {
+ LOGE("Bogus size for section: got 0x%x; expected 0x%x\n",
+ count, expectedCount);
+ return false;
+ }
+
+ return iterateSection(state, offset, count, func, alignment, nextOffset);
+}
+
+/*
+ * Like iterateSection(), but also update the data section map and
+ * check that all the items fall within the data section.
+ */
+static bool iterateDataSection(CheckState* state, u4 offset, u4 count,
+ ItemVisitorFunction* func, u4 alignment, u4* nextOffset, int mapType) {
+ u4 dataStart = state->pHeader->dataOff;
+ u4 dataEnd = dataStart + state->pHeader->dataSize;
+
+ assert(nextOffset != NULL);
+
+ if ((offset < dataStart) || (offset >= dataEnd)) {
+ LOGE("Bogus offset for data subsection: 0x%x\n", offset);
+ return false;
+ }
+
+ if (!iterateSectionWithOptionalUpdate(state, offset, count, func,
+ alignment, nextOffset, mapType)) {
+ return false;
+ }
+
+ if (*nextOffset > dataEnd) {
+ LOGE("Out-of-bounds end of data subsection: 0x%x\n", *nextOffset);
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Byte-swap all items in the given map except the header and the map
+ * itself, both of which should have already gotten swapped. This also
+ * does all possible intra-item verification, that is, verification
+ * that doesn't need to assume the sanctity of the contents of *other*
+ * items. The intra-item limitation is because at the time an item is
+ * asked to verify itself, it can't assume that the items it refers to
+ * have been byte-swapped and verified.
+ */
+static bool swapEverythingButHeaderAndMap(CheckState* state,
+ DexMapList* pMap) {
+ const DexMapItem* item = pMap->list;
+ u4 lastOffset = 0;
+ u4 count = pMap->size;
+ bool okay = true;
+
+ while (okay && count--) {
+ u4 sectionOffset = item->offset;
+ u4 sectionCount = item->size;
+ u2 type = item->type;
+
+ if (lastOffset < sectionOffset) {
+ CHECK_OFFSET_RANGE(lastOffset, sectionOffset);
+ const u1* ptr = filePointer(state, lastOffset);
+ while (lastOffset < sectionOffset) {
+ if (*ptr != '\0') {
+ LOGE("Non-zero padding 0x%02x before section start @ %x\n",
+ *ptr, lastOffset);
+ okay = false;
+ break;
+ }
+ ptr++;
+ lastOffset++;
+ }
+ } else if (lastOffset > sectionOffset) {
+ LOGE("Section overlap or out-of-order map: %x, %x\n",
+ lastOffset, sectionOffset);
+ okay = false;
+ }
+
+ if (!okay) {
+ break;
+ }
+
+ switch (type) {
+ case kDexTypeHeaderItem: {
+ /*
+ * The header got swapped very early on, but do some
+ * additional sanity checking here.
+ */
+ okay = checkHeaderSection(state, sectionOffset, sectionCount,
+ &lastOffset);
+ break;
+ }
+ case kDexTypeStringIdItem: {
+ okay = checkBoundsAndIterateSection(state, sectionOffset,
+ sectionCount, state->pHeader->stringIdsOff,
+ state->pHeader->stringIdsSize, swapStringIdItem,
+ sizeof(u4), &lastOffset);
+ break;
+ }
+ case kDexTypeTypeIdItem: {
+ okay = checkBoundsAndIterateSection(state, sectionOffset,
+ sectionCount, state->pHeader->typeIdsOff,
+ state->pHeader->typeIdsSize, swapTypeIdItem,
+ sizeof(u4), &lastOffset);
+ break;
+ }
+ case kDexTypeProtoIdItem: {
+ okay = checkBoundsAndIterateSection(state, sectionOffset,
+ sectionCount, state->pHeader->protoIdsOff,
+ state->pHeader->protoIdsSize, swapProtoIdItem,
+ sizeof(u4), &lastOffset);
+ break;
+ }
+ case kDexTypeFieldIdItem: {
+ okay = checkBoundsAndIterateSection(state, sectionOffset,
+ sectionCount, state->pHeader->fieldIdsOff,
+ state->pHeader->fieldIdsSize, swapFieldIdItem,
+ sizeof(u4), &lastOffset);
+ break;
+ }
+ case kDexTypeMethodIdItem: {
+ okay = checkBoundsAndIterateSection(state, sectionOffset,
+ sectionCount, state->pHeader->methodIdsOff,
+ state->pHeader->methodIdsSize, swapMethodIdItem,
+ sizeof(u4), &lastOffset);
+ break;
+ }
+ case kDexTypeClassDefItem: {
+ okay = checkBoundsAndIterateSection(state, sectionOffset,
+ sectionCount, state->pHeader->classDefsOff,
+ state->pHeader->classDefsSize, swapClassDefItem,
+ sizeof(u4), &lastOffset);
+ break;
+ }
+ case kDexTypeMapList: {
+ /*
+ * The map section was swapped early on, but do some
+ * additional sanity checking here.
+ */
+ okay = checkMapSection(state, sectionOffset, sectionCount,
+ &lastOffset);
+ break;
+ }
+ case kDexTypeTypeList: {
+ okay = iterateDataSection(state, sectionOffset, sectionCount,
+ swapTypeList, sizeof(u4), &lastOffset, type);
+ break;
+ }
+ case kDexTypeAnnotationSetRefList: {
+ okay = iterateDataSection(state, sectionOffset, sectionCount,
+ swapAnnotationSetRefList, sizeof(u4), &lastOffset,
+ type);
+ break;
+ }
+ case kDexTypeAnnotationSetItem: {
+ okay = iterateDataSection(state, sectionOffset, sectionCount,
+ swapAnnotationSetItem, sizeof(u4), &lastOffset, type);
+ break;
+ }
+ case kDexTypeClassDataItem: {
+ okay = iterateDataSection(state, sectionOffset, sectionCount,
+ intraVerifyClassDataItem, sizeof(u1), &lastOffset,
+ type);
+ break;
+ }
+ case kDexTypeCodeItem: {
+ okay = iterateDataSection(state, sectionOffset, sectionCount,
+ swapCodeItem, sizeof(u4), &lastOffset, type);
+ break;
+ }
+ case kDexTypeStringDataItem: {
+ okay = iterateDataSection(state, sectionOffset, sectionCount,
+ intraVerifyStringDataItem, sizeof(u1), &lastOffset,
+ type);
+ break;
+ }
+ case kDexTypeDebugInfoItem: {
+ okay = iterateDataSection(state, sectionOffset, sectionCount,
+ intraVerifyDebugInfoItem, sizeof(u1), &lastOffset,
+ type);
+ break;
+ }
+ case kDexTypeAnnotationItem: {
+ okay = iterateDataSection(state, sectionOffset, sectionCount,
+ intraVerifyAnnotationItem, sizeof(u1), &lastOffset,
+ type);
+ break;
+ }
+ case kDexTypeEncodedArrayItem: {
+ okay = iterateDataSection(state, sectionOffset, sectionCount,
+ intraVerifyEncodedArrayItem, sizeof(u1), &lastOffset,
+ type);
+ break;
+ }
+ case kDexTypeAnnotationsDirectoryItem: {
+ okay = iterateDataSection(state, sectionOffset, sectionCount,
+ swapAnnotationsDirectoryItem, sizeof(u4), &lastOffset,
+ type);
+ break;
+ }
+ default: {
+ LOGE("Unknown map item type %04x\n", type);
+ return false;
+ }
+ }
+
+ if (!okay) {
+ LOGE("Swap of section type %04x failed\n", type);
+ }
+
+ item++;
+ }
+
+ return okay;
+}
+
+/*
+ * Perform cross-item verification on everything that needs it. This
+ * pass is only called after all items are byte-swapped and
+ * intra-verified (checked for internal consistency).
+ */
+static bool crossVerifyEverything(CheckState* state, DexMapList* pMap)
+{
+ const DexMapItem* item = pMap->list;
+ u4 count = pMap->size;
+ bool okay = true;
+
+ while (okay && count--) {
+ u4 sectionOffset = item->offset;
+ u4 sectionCount = item->size;
+
+ switch (item->type) {
+ case kDexTypeHeaderItem:
+ case kDexTypeMapList:
+ case kDexTypeTypeList:
+ case kDexTypeCodeItem:
+ case kDexTypeStringDataItem:
+ case kDexTypeDebugInfoItem:
+ case kDexTypeAnnotationItem:
+ case kDexTypeEncodedArrayItem: {
+ // There is no need for cross-item verification for these.
+ break;
+ }
+ case kDexTypeStringIdItem: {
+ okay = iterateSection(state, sectionOffset, sectionCount,
+ crossVerifyStringIdItem, sizeof(u4), NULL);
+ break;
+ }
+ case kDexTypeTypeIdItem: {
+ okay = iterateSection(state, sectionOffset, sectionCount,
+ crossVerifyTypeIdItem, sizeof(u4), NULL);
+ break;
+ }
+ case kDexTypeProtoIdItem: {
+ okay = iterateSection(state, sectionOffset, sectionCount,
+ crossVerifyProtoIdItem, sizeof(u4), NULL);
+ break;
+ }
+ case kDexTypeFieldIdItem: {
+ okay = iterateSection(state, sectionOffset, sectionCount,
+ crossVerifyFieldIdItem, sizeof(u4), NULL);
+ break;
+ }
+ case kDexTypeMethodIdItem: {
+ okay = iterateSection(state, sectionOffset, sectionCount,
+ crossVerifyMethodIdItem, sizeof(u4), NULL);
+ break;
+ }
+ case kDexTypeClassDefItem: {
+ // Allocate (on the stack) the "observed class_def" bits.
+ size_t arraySize = calcDefinedClassBitsSize(state);
+ u4 definedClassBits[arraySize];
+ memset(definedClassBits, 0, arraySize * sizeof(u4));
+ state->pDefinedClassBits = definedClassBits;
+
+ okay = iterateSection(state, sectionOffset, sectionCount,
+ crossVerifyClassDefItem, sizeof(u4), NULL);
+
+ state->pDefinedClassBits = NULL;
+ break;
+ }
+ case kDexTypeAnnotationSetRefList: {
+ okay = iterateSection(state, sectionOffset, sectionCount,
+ crossVerifyAnnotationSetRefList, sizeof(u4), NULL);
+ break;
+ }
+ case kDexTypeAnnotationSetItem: {
+ okay = iterateSection(state, sectionOffset, sectionCount,
+ crossVerifyAnnotationSetItem, sizeof(u4), NULL);
+ break;
+ }
+ case kDexTypeClassDataItem: {
+ okay = iterateSection(state, sectionOffset, sectionCount,
+ crossVerifyClassDataItem, sizeof(u1), NULL);
+ break;
+ }
+ case kDexTypeAnnotationsDirectoryItem: {
+ okay = iterateSection(state, sectionOffset, sectionCount,
+ crossVerifyAnnotationsDirectoryItem, sizeof(u4), NULL);
+ break;
+ }
+ default: {
+ LOGE("Unknown map item type %04x\n", item->type);
+ return false;
+ }
+ }
+
+ if (!okay) {
+ LOGE("Cross-item verify of section type %04x failed\n",
+ item->type);
+ }
+
+ item++;
+ }
+
+ return okay;
+}
+
+/*
+ * Fix the byte ordering of all fields in the DEX file, and do
+ * structural verification. This is only required for code that opens
+ * "raw" DEX files, such as the DEX optimizer.
+ *
+ * Returns 0 on success, nonzero on failure.
+ */
+int dexSwapAndVerify(u1* addr, int len)
+{
+ DexHeader* pHeader;
+ CheckState state;
+ bool okay = true;
+
+ memset(&state, 0, sizeof(state));
+ LOGV("+++ swapping and verifying\n");
+
+ /*
+ * Start by verifying the magic number. The caller verified that "len"
+ * says we have at least a header's worth of data.
+ */
+ pHeader = (DexHeader*) addr;
+ if (memcmp(pHeader->magic, DEX_MAGIC, 4) != 0) {
+ /* really shouldn't be here -- this is weird */
+ LOGE("ERROR: Can't byte swap: bad magic number "
+ "(0x%02x %02x %02x %02x)\n",
+ pHeader->magic[0], pHeader->magic[1],
+ pHeader->magic[2], pHeader->magic[3]);
+ okay = false;
+ }
+
+ if (okay && memcmp(pHeader->magic+4, DEX_MAGIC_VERS, 4) != 0) {
+ /* older or newer version we don't know how to read */
+ LOGE("ERROR: Can't byte swap: bad dex version "
+ "(0x%02x %02x %02x %02x)\n",
+ pHeader->magic[4], pHeader->magic[5],
+ pHeader->magic[6], pHeader->magic[7]);
+ okay = false;
+ }
+
+ if (okay) {
+ int expectedLen = (int) SWAP4(pHeader->fileSize);
+ if (len < expectedLen) {
+ LOGE("ERROR: Bad length: expected %d, got %d\n", expectedLen, len);
+ okay = false;
+ } else if (len != expectedLen) {
+ LOGW("WARNING: Odd length: expected %d, got %d\n", expectedLen,
+ len);
+ // keep going
+ }
+ }
+
+ if (okay) {
+ /*
+ * Compute the adler32 checksum and compare it to what's stored in
+ * the file. This isn't free, but chances are good that we just
+ * unpacked this from a jar file and have all of the pages sitting
+ * in memory, so it's pretty quick.
+ *
+ * This might be a big-endian system, so we need to do this before
+ * we byte-swap the header.
+ */
+ uLong adler = adler32(0L, Z_NULL, 0);
+ const int nonSum = sizeof(pHeader->magic) + sizeof(pHeader->checksum);
+ u4 storedFileSize = SWAP4(pHeader->fileSize);
+ u4 expectedChecksum = SWAP4(pHeader->checksum);
+
+ adler = adler32(adler, ((const u1*) pHeader) + nonSum,
+ storedFileSize - nonSum);
+
+ if (adler != expectedChecksum) {
+ LOGE("ERROR: bad checksum (%08lx, expected %08x)\n",
+ adler, expectedChecksum);
+ okay = false;
+ }
+ }
+
+ if (okay) {
+ state.fileStart = addr;
+ state.fileEnd = addr + len;
+ state.fileLen = len;
+ state.pDexFile = NULL;
+ state.pDataMap = NULL;
+ state.pDefinedClassBits = NULL;
+ state.previousItem = NULL;
+
+ /*
+ * Swap the header and check the contents.
+ */
+ okay = swapDexHeader(&state, pHeader);
+ }
+
+ if (okay) {
+ state.pHeader = pHeader;
+
+ if (pHeader->headerSize < sizeof(DexHeader)) {
+ LOGE("ERROR: Small header size %d, struct %d\n",
+ pHeader->headerSize, (int) sizeof(DexHeader));
+ okay = false;
+ } else if (pHeader->headerSize > sizeof(DexHeader)) {
+ LOGW("WARNING: Large header size %d, struct %d\n",
+ pHeader->headerSize, (int) sizeof(DexHeader));
+ // keep going?
+ }
+ }
+
+ if (okay) {
+ /*
+ * Look for the map. Swap it and then use it to find and swap
+ * everything else.
+ */
+ if (pHeader->mapOff != 0) {
+ DexFile dexFile;
+ DexMapList* pDexMap = (DexMapList*) (addr + pHeader->mapOff);
+
+ okay = okay && swapMap(&state, pDexMap);
+ okay = okay && swapEverythingButHeaderAndMap(&state, pDexMap);
+
+ dexFileSetupBasicPointers(&dexFile, addr);
+ state.pDexFile = &dexFile;
+
+ okay = okay && crossVerifyEverything(&state, pDexMap);
+ } else {
+ LOGE("ERROR: No map found; impossible to byte-swap and verify");
+ okay = false;
+ }
+ }
+
+ if (!okay) {
+ LOGE("ERROR: Byte swap + verify failed\n");
+ }
+
+ if (state.pDataMap != NULL) {
+ dexDataMapFree(state.pDataMap);
+ }
+
+ return !okay; // 0 == success
+}
+
+/*
+ * Detect the file type of the given memory buffer via magic number.
+ * Call dexSwapAndVerify() on an unoptimized DEX file, do nothing
+ * but return successfully on an optimized DEX file, and report an
+ * error for all other cases.
+ *
+ * Returns 0 on success, nonzero on failure.
+ */
+int dexSwapAndVerifyIfNecessary(u1* addr, int len)
+{
+ if (memcmp(addr, DEX_OPT_MAGIC, 4) == 0) {
+ // It is an optimized dex file.
+ return 0;
+ }
+
+ if (memcmp(addr, DEX_MAGIC, 4) == 0) {
+ // It is an unoptimized dex file.
+ return dexSwapAndVerify(addr, len);
+ }
+
+ LOGE("ERROR: Bad magic number (0x%02x %02x %02x %02x)\n",
+ addr[0], addr[1], addr[2], addr[3]);
+
+ return 1;
+}
diff --git a/libdex/InstrUtils.c b/libdex/InstrUtils.c
new file mode 100644
index 0000000..b98f002
--- /dev/null
+++ b/libdex/InstrUtils.c
@@ -0,0 +1,1261 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik instruction utility functions.
+ */
+#include "InstrUtils.h"
+
+#include <stdlib.h>
+
+
+/*
+ * Generate a table that holds the width of all instructions.
+ *
+ * Standard instructions have positive values, optimizer instructions
+ * have negative values, unimplemented instructions have a width of zero.
+ *
+ * I'm doing it with a giant switch statement because it's easier to
+ * maintain and update than a static table with 256 unadorned integers,
+ * and if we're missing a case gcc emits a "warning: enumeration value not
+ * handled" message.
+ *
+ * (To save space in the binary we could generate a static table with a
+ * command-line utility.)
+ *
+ * TODO: it doesn't look like we're using the negative values anymore.
+ * Consider switching to only positive values.
+ */
+InstructionWidth* dexCreateInstrWidthTable(void)
+{
+#ifdef __ARM_ARCH_7A__
+ /* hack to work around mysterious problem on emulator */
+ LOGD("creating instr width table\n");
+#endif
+ InstructionWidth* instrWidth;
+ int i;
+
+ instrWidth = malloc(sizeof(InstructionWidth) * kNumDalvikInstructions);
+ if (instrWidth == NULL)
+ return NULL;
+
+ for (i = 0; i < kNumDalvikInstructions; i++) {
+ OpCode opc = (OpCode) i;
+ int width = 0;
+
+ switch (opc) {
+ case OP_NOP: /* note data for e.g. switch-* encoded "inside" a NOP */
+ case OP_MOVE:
+ case OP_MOVE_WIDE:
+ case OP_MOVE_OBJECT:
+ case OP_MOVE_RESULT:
+ case OP_MOVE_RESULT_WIDE:
+ case OP_MOVE_RESULT_OBJECT:
+ case OP_MOVE_EXCEPTION:
+ case OP_RETURN_VOID:
+ case OP_RETURN:
+ case OP_RETURN_WIDE:
+ case OP_RETURN_OBJECT:
+ case OP_CONST_4:
+ case OP_MONITOR_ENTER:
+ case OP_MONITOR_EXIT:
+ case OP_ARRAY_LENGTH:
+ case OP_THROW:
+ case OP_GOTO:
+ case OP_NEG_INT:
+ case OP_NOT_INT:
+ case OP_NEG_LONG:
+ case OP_NOT_LONG:
+ case OP_NEG_FLOAT:
+ case OP_NEG_DOUBLE:
+ case OP_INT_TO_LONG:
+ case OP_INT_TO_FLOAT:
+ case OP_INT_TO_DOUBLE:
+ case OP_LONG_TO_INT:
+ case OP_LONG_TO_FLOAT:
+ case OP_LONG_TO_DOUBLE:
+ case OP_FLOAT_TO_INT:
+ case OP_FLOAT_TO_LONG:
+ case OP_FLOAT_TO_DOUBLE:
+ case OP_DOUBLE_TO_INT:
+ case OP_DOUBLE_TO_LONG:
+ case OP_DOUBLE_TO_FLOAT:
+ case OP_INT_TO_BYTE:
+ case OP_INT_TO_CHAR:
+ case OP_INT_TO_SHORT:
+ case OP_ADD_INT_2ADDR:
+ case OP_SUB_INT_2ADDR:
+ case OP_MUL_INT_2ADDR:
+ case OP_DIV_INT_2ADDR:
+ case OP_REM_INT_2ADDR:
+ case OP_AND_INT_2ADDR:
+ case OP_OR_INT_2ADDR:
+ case OP_XOR_INT_2ADDR:
+ case OP_SHL_INT_2ADDR:
+ case OP_SHR_INT_2ADDR:
+ case OP_USHR_INT_2ADDR:
+ case OP_ADD_LONG_2ADDR:
+ case OP_SUB_LONG_2ADDR:
+ case OP_MUL_LONG_2ADDR:
+ case OP_DIV_LONG_2ADDR:
+ case OP_REM_LONG_2ADDR:
+ case OP_AND_LONG_2ADDR:
+ case OP_OR_LONG_2ADDR:
+ case OP_XOR_LONG_2ADDR:
+ case OP_SHL_LONG_2ADDR:
+ case OP_SHR_LONG_2ADDR:
+ case OP_USHR_LONG_2ADDR:
+ case OP_ADD_FLOAT_2ADDR:
+ case OP_SUB_FLOAT_2ADDR:
+ case OP_MUL_FLOAT_2ADDR:
+ case OP_DIV_FLOAT_2ADDR:
+ case OP_REM_FLOAT_2ADDR:
+ case OP_ADD_DOUBLE_2ADDR:
+ case OP_SUB_DOUBLE_2ADDR:
+ case OP_MUL_DOUBLE_2ADDR:
+ case OP_DIV_DOUBLE_2ADDR:
+ case OP_REM_DOUBLE_2ADDR:
+ width = 1;
+ break;
+
+ case OP_MOVE_FROM16:
+ case OP_MOVE_WIDE_FROM16:
+ case OP_MOVE_OBJECT_FROM16:
+ case OP_CONST_16:
+ case OP_CONST_HIGH16:
+ case OP_CONST_WIDE_16:
+ case OP_CONST_WIDE_HIGH16:
+ case OP_CONST_STRING:
+ case OP_CONST_CLASS:
+ case OP_CHECK_CAST:
+ case OP_INSTANCE_OF:
+ case OP_NEW_INSTANCE:
+ case OP_NEW_ARRAY:
+ case OP_CMPL_FLOAT:
+ case OP_CMPG_FLOAT:
+ case OP_CMPL_DOUBLE:
+ case OP_CMPG_DOUBLE:
+ case OP_CMP_LONG:
+ case OP_GOTO_16:
+ case OP_IF_EQ:
+ case OP_IF_NE:
+ case OP_IF_LT:
+ case OP_IF_GE:
+ case OP_IF_GT:
+ case OP_IF_LE:
+ case OP_IF_EQZ:
+ case OP_IF_NEZ:
+ case OP_IF_LTZ:
+ case OP_IF_GEZ:
+ case OP_IF_GTZ:
+ case OP_IF_LEZ:
+ case OP_AGET:
+ case OP_AGET_WIDE:
+ case OP_AGET_OBJECT:
+ case OP_AGET_BOOLEAN:
+ case OP_AGET_BYTE:
+ case OP_AGET_CHAR:
+ case OP_AGET_SHORT:
+ case OP_APUT:
+ case OP_APUT_WIDE:
+ case OP_APUT_OBJECT:
+ case OP_APUT_BOOLEAN:
+ case OP_APUT_BYTE:
+ case OP_APUT_CHAR:
+ case OP_APUT_SHORT:
+ case OP_IGET:
+ case OP_IGET_WIDE:
+ case OP_IGET_OBJECT:
+ case OP_IGET_BOOLEAN:
+ case OP_IGET_BYTE:
+ case OP_IGET_CHAR:
+ case OP_IGET_SHORT:
+ case OP_IPUT:
+ case OP_IPUT_WIDE:
+ case OP_IPUT_OBJECT:
+ case OP_IPUT_BOOLEAN:
+ case OP_IPUT_BYTE:
+ case OP_IPUT_CHAR:
+ case OP_IPUT_SHORT:
+ case OP_SGET:
+ case OP_SGET_WIDE:
+ case OP_SGET_OBJECT:
+ case OP_SGET_BOOLEAN:
+ case OP_SGET_BYTE:
+ case OP_SGET_CHAR:
+ case OP_SGET_SHORT:
+ case OP_SPUT:
+ case OP_SPUT_WIDE:
+ case OP_SPUT_OBJECT:
+ case OP_SPUT_BOOLEAN:
+ case OP_SPUT_BYTE:
+ case OP_SPUT_CHAR:
+ case OP_SPUT_SHORT:
+ case OP_ADD_INT:
+ case OP_SUB_INT:
+ case OP_MUL_INT:
+ case OP_DIV_INT:
+ case OP_REM_INT:
+ case OP_AND_INT:
+ case OP_OR_INT:
+ case OP_XOR_INT:
+ case OP_SHL_INT:
+ case OP_SHR_INT:
+ case OP_USHR_INT:
+ case OP_ADD_LONG:
+ case OP_SUB_LONG:
+ case OP_MUL_LONG:
+ case OP_DIV_LONG:
+ case OP_REM_LONG:
+ case OP_AND_LONG:
+ case OP_OR_LONG:
+ case OP_XOR_LONG:
+ case OP_SHL_LONG:
+ case OP_SHR_LONG:
+ case OP_USHR_LONG:
+ case OP_ADD_FLOAT:
+ case OP_SUB_FLOAT:
+ case OP_MUL_FLOAT:
+ case OP_DIV_FLOAT:
+ case OP_REM_FLOAT:
+ case OP_ADD_DOUBLE:
+ case OP_SUB_DOUBLE:
+ case OP_MUL_DOUBLE:
+ case OP_DIV_DOUBLE:
+ case OP_REM_DOUBLE:
+ case OP_ADD_INT_LIT16:
+ case OP_RSUB_INT:
+ case OP_MUL_INT_LIT16:
+ case OP_DIV_INT_LIT16:
+ case OP_REM_INT_LIT16:
+ case OP_AND_INT_LIT16:
+ case OP_OR_INT_LIT16:
+ case OP_XOR_INT_LIT16:
+ case OP_ADD_INT_LIT8:
+ case OP_RSUB_INT_LIT8:
+ case OP_MUL_INT_LIT8:
+ case OP_DIV_INT_LIT8:
+ case OP_REM_INT_LIT8:
+ case OP_AND_INT_LIT8:
+ case OP_OR_INT_LIT8:
+ case OP_XOR_INT_LIT8:
+ case OP_SHL_INT_LIT8:
+ case OP_SHR_INT_LIT8:
+ case OP_USHR_INT_LIT8:
+ width = 2;
+ break;
+
+ case OP_MOVE_16:
+ case OP_MOVE_WIDE_16:
+ case OP_MOVE_OBJECT_16:
+ case OP_CONST:
+ case OP_CONST_WIDE_32:
+ case OP_CONST_STRING_JUMBO:
+ case OP_GOTO_32:
+ case OP_FILLED_NEW_ARRAY:
+ case OP_FILLED_NEW_ARRAY_RANGE:
+ case OP_FILL_ARRAY_DATA:
+ case OP_PACKED_SWITCH:
+ case OP_SPARSE_SWITCH:
+ case OP_INVOKE_VIRTUAL:
+ case OP_INVOKE_SUPER:
+ case OP_INVOKE_DIRECT:
+ case OP_INVOKE_STATIC:
+ case OP_INVOKE_INTERFACE:
+ case OP_INVOKE_VIRTUAL_RANGE:
+ case OP_INVOKE_SUPER_RANGE:
+ case OP_INVOKE_DIRECT_RANGE:
+ case OP_INVOKE_STATIC_RANGE:
+ case OP_INVOKE_INTERFACE_RANGE:
+ width = 3;
+ break;
+
+ case OP_CONST_WIDE:
+ width = 5;
+ break;
+
+ /*
+ * Optimized instructions. We return negative size values for these
+ * to distinguish them.
+ */
+ case OP_IGET_QUICK:
+ case OP_IGET_WIDE_QUICK:
+ case OP_IGET_OBJECT_QUICK:
+ case OP_IPUT_QUICK:
+ case OP_IPUT_WIDE_QUICK:
+ case OP_IPUT_OBJECT_QUICK:
+ case OP_IGET_VOLATILE:
+ case OP_IPUT_VOLATILE:
+ case OP_SGET_VOLATILE:
+ case OP_SPUT_VOLATILE:
+ case OP_IGET_OBJECT_VOLATILE:
+ case OP_IPUT_OBJECT_VOLATILE:
+ case OP_SGET_OBJECT_VOLATILE:
+ case OP_SPUT_OBJECT_VOLATILE:
+ case OP_IGET_WIDE_VOLATILE:
+ case OP_IPUT_WIDE_VOLATILE:
+ case OP_SGET_WIDE_VOLATILE:
+ case OP_SPUT_WIDE_VOLATILE:
+ case OP_THROW_VERIFICATION_ERROR:
+ width = -2;
+ break;
+ case OP_INVOKE_VIRTUAL_QUICK:
+ case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+ case OP_INVOKE_SUPER_QUICK:
+ case OP_INVOKE_SUPER_QUICK_RANGE:
+ case OP_EXECUTE_INLINE:
+ case OP_EXECUTE_INLINE_RANGE:
+ case OP_INVOKE_DIRECT_EMPTY:
+ width = -3;
+ break;
+
+ /* these should never appear when scanning bytecode */
+ case OP_UNUSED_3E:
+ case OP_UNUSED_3F:
+ case OP_UNUSED_40:
+ case OP_UNUSED_41:
+ case OP_UNUSED_42:
+ case OP_UNUSED_43:
+ case OP_UNUSED_73:
+ case OP_UNUSED_79:
+ case OP_UNUSED_7A:
+ case OP_BREAKPOINT:
+ case OP_UNUSED_F1:
+ case OP_UNUSED_FF:
+ assert(width == 0);
+ break;
+
+ /*
+ * DO NOT add a "default" clause here. Without it the compiler will
+ * complain if an instruction is missing (which is desirable).
+ */
+ }
+
+ instrWidth[opc] = width;
+ }
+
+ return instrWidth;
+}
+
+/*
+ * Generate a table that holds instruction flags.
+ */
+InstructionFlags* dexCreateInstrFlagsTable(void)
+{
+ InstructionFlags* instrFlags;
+ int i;
+
+ instrFlags = malloc(sizeof(InstructionFlags) * kNumDalvikInstructions);
+ if (instrFlags == NULL)
+ return NULL;
+
+ for (i = 0; i < kNumDalvikInstructions; i++) {
+ OpCode opc = (OpCode) i;
+ InstructionFlags flags = 0;
+
+ switch (opc) {
+ /* these don't affect the PC and can't cause an exception */
+ case OP_NOP:
+ case OP_MOVE:
+ case OP_MOVE_FROM16:
+ case OP_MOVE_16:
+ case OP_MOVE_WIDE:
+ case OP_MOVE_WIDE_FROM16:
+ case OP_MOVE_WIDE_16:
+ case OP_MOVE_OBJECT:
+ case OP_MOVE_OBJECT_FROM16:
+ case OP_MOVE_OBJECT_16:
+ case OP_MOVE_RESULT:
+ case OP_MOVE_RESULT_WIDE:
+ case OP_MOVE_RESULT_OBJECT:
+ case OP_MOVE_EXCEPTION:
+ case OP_CONST_4:
+ case OP_CONST_16:
+ case OP_CONST:
+ case OP_CONST_HIGH16:
+ case OP_CONST_WIDE_16:
+ case OP_CONST_WIDE_32:
+ case OP_CONST_WIDE:
+ case OP_CONST_WIDE_HIGH16:
+ case OP_FILL_ARRAY_DATA:
+ case OP_CMPL_FLOAT:
+ case OP_CMPG_FLOAT:
+ case OP_CMPL_DOUBLE:
+ case OP_CMPG_DOUBLE:
+ case OP_CMP_LONG:
+ case OP_NEG_INT:
+ case OP_NOT_INT:
+ case OP_NEG_LONG:
+ case OP_NOT_LONG:
+ case OP_NEG_FLOAT:
+ case OP_NEG_DOUBLE:
+ case OP_INT_TO_LONG:
+ case OP_INT_TO_FLOAT:
+ case OP_INT_TO_DOUBLE:
+ case OP_LONG_TO_INT:
+ case OP_LONG_TO_FLOAT:
+ case OP_LONG_TO_DOUBLE:
+ case OP_FLOAT_TO_INT:
+ case OP_FLOAT_TO_LONG:
+ case OP_FLOAT_TO_DOUBLE:
+ case OP_DOUBLE_TO_INT:
+ case OP_DOUBLE_TO_LONG:
+ case OP_DOUBLE_TO_FLOAT:
+ case OP_INT_TO_BYTE:
+ case OP_INT_TO_CHAR:
+ case OP_INT_TO_SHORT:
+ case OP_ADD_INT:
+ case OP_SUB_INT:
+ case OP_MUL_INT:
+ case OP_AND_INT:
+ case OP_OR_INT:
+ case OP_XOR_INT:
+ case OP_SHL_INT:
+ case OP_SHR_INT:
+ case OP_USHR_INT:
+ case OP_ADD_LONG:
+ case OP_SUB_LONG:
+ case OP_MUL_LONG:
+ case OP_AND_LONG:
+ case OP_OR_LONG:
+ case OP_XOR_LONG:
+ case OP_SHL_LONG:
+ case OP_SHR_LONG:
+ case OP_USHR_LONG:
+ case OP_ADD_FLOAT:
+ case OP_SUB_FLOAT:
+ case OP_MUL_FLOAT:
+ case OP_DIV_FLOAT:
+ case OP_REM_FLOAT:
+ case OP_ADD_DOUBLE:
+ case OP_SUB_DOUBLE:
+ case OP_MUL_DOUBLE:
+ case OP_DIV_DOUBLE: // div by zero just returns NaN
+ case OP_REM_DOUBLE:
+ case OP_ADD_INT_2ADDR:
+ case OP_SUB_INT_2ADDR:
+ case OP_MUL_INT_2ADDR:
+ case OP_AND_INT_2ADDR:
+ case OP_OR_INT_2ADDR:
+ case OP_XOR_INT_2ADDR:
+ case OP_SHL_INT_2ADDR:
+ case OP_SHR_INT_2ADDR:
+ case OP_USHR_INT_2ADDR:
+ case OP_ADD_LONG_2ADDR:
+ case OP_SUB_LONG_2ADDR:
+ case OP_MUL_LONG_2ADDR:
+ case OP_AND_LONG_2ADDR:
+ case OP_OR_LONG_2ADDR:
+ case OP_XOR_LONG_2ADDR:
+ case OP_SHL_LONG_2ADDR:
+ case OP_SHR_LONG_2ADDR:
+ case OP_USHR_LONG_2ADDR:
+ case OP_ADD_FLOAT_2ADDR:
+ case OP_SUB_FLOAT_2ADDR:
+ case OP_MUL_FLOAT_2ADDR:
+ case OP_DIV_FLOAT_2ADDR:
+ case OP_REM_FLOAT_2ADDR:
+ case OP_ADD_DOUBLE_2ADDR:
+ case OP_SUB_DOUBLE_2ADDR:
+ case OP_MUL_DOUBLE_2ADDR:
+ case OP_DIV_DOUBLE_2ADDR:
+ case OP_REM_DOUBLE_2ADDR:
+ case OP_ADD_INT_LIT16:
+ case OP_RSUB_INT:
+ case OP_MUL_INT_LIT16:
+ case OP_AND_INT_LIT16:
+ case OP_OR_INT_LIT16:
+ case OP_XOR_INT_LIT16:
+ case OP_ADD_INT_LIT8:
+ case OP_RSUB_INT_LIT8:
+ case OP_MUL_INT_LIT8:
+ case OP_AND_INT_LIT8:
+ case OP_OR_INT_LIT8:
+ case OP_XOR_INT_LIT8:
+ case OP_SHL_INT_LIT8:
+ case OP_SHR_INT_LIT8:
+ case OP_USHR_INT_LIT8:
+ flags = kInstrCanContinue;
+ break;
+
+ /* these don't affect the PC, but can cause exceptions */
+ case OP_CONST_STRING:
+ case OP_CONST_STRING_JUMBO:
+ case OP_CONST_CLASS:
+ case OP_MONITOR_ENTER:
+ case OP_MONITOR_EXIT:
+ case OP_CHECK_CAST:
+ case OP_INSTANCE_OF:
+ case OP_ARRAY_LENGTH:
+ case OP_NEW_INSTANCE:
+ case OP_NEW_ARRAY:
+ case OP_FILLED_NEW_ARRAY:
+ case OP_FILLED_NEW_ARRAY_RANGE:
+ case OP_AGET:
+ case OP_AGET_BOOLEAN:
+ case OP_AGET_BYTE:
+ case OP_AGET_CHAR:
+ case OP_AGET_SHORT:
+ case OP_AGET_WIDE:
+ case OP_AGET_OBJECT:
+ case OP_APUT:
+ case OP_APUT_BOOLEAN:
+ case OP_APUT_BYTE:
+ case OP_APUT_CHAR:
+ case OP_APUT_SHORT:
+ case OP_APUT_WIDE:
+ case OP_APUT_OBJECT:
+ case OP_IGET:
+ case OP_IGET_BOOLEAN:
+ case OP_IGET_BYTE:
+ case OP_IGET_CHAR:
+ case OP_IGET_SHORT:
+ case OP_IGET_WIDE:
+ case OP_IGET_OBJECT:
+ case OP_IPUT:
+ case OP_IPUT_BOOLEAN:
+ case OP_IPUT_BYTE:
+ case OP_IPUT_CHAR:
+ case OP_IPUT_SHORT:
+ case OP_IPUT_WIDE:
+ case OP_IPUT_OBJECT:
+ case OP_SGET:
+ case OP_SGET_BOOLEAN:
+ case OP_SGET_BYTE:
+ case OP_SGET_CHAR:
+ case OP_SGET_SHORT:
+ case OP_SGET_WIDE:
+ case OP_SGET_OBJECT:
+ case OP_SPUT:
+ case OP_SPUT_BOOLEAN:
+ case OP_SPUT_BYTE:
+ case OP_SPUT_CHAR:
+ case OP_SPUT_SHORT:
+ case OP_SPUT_WIDE:
+ case OP_SPUT_OBJECT:
+ case OP_DIV_INT:
+ case OP_REM_INT:
+ case OP_DIV_LONG:
+ case OP_REM_LONG:
+ case OP_DIV_INT_2ADDR:
+ case OP_REM_INT_2ADDR:
+ case OP_DIV_LONG_2ADDR:
+ case OP_REM_LONG_2ADDR:
+ case OP_DIV_INT_LIT16:
+ case OP_REM_INT_LIT16:
+ case OP_DIV_INT_LIT8:
+ case OP_REM_INT_LIT8:
+ flags = kInstrCanContinue | kInstrCanThrow;
+ break;
+
+ case OP_INVOKE_VIRTUAL:
+ case OP_INVOKE_VIRTUAL_RANGE:
+ case OP_INVOKE_SUPER:
+ case OP_INVOKE_SUPER_RANGE:
+ case OP_INVOKE_DIRECT:
+ case OP_INVOKE_DIRECT_RANGE:
+ case OP_INVOKE_STATIC:
+ case OP_INVOKE_STATIC_RANGE:
+ case OP_INVOKE_INTERFACE:
+ case OP_INVOKE_INTERFACE_RANGE:
+ flags = kInstrCanContinue | kInstrCanThrow | kInstrInvoke;
+ break;
+
+ case OP_RETURN_VOID:
+ case OP_RETURN:
+ case OP_RETURN_WIDE:
+ case OP_RETURN_OBJECT:
+ flags = kInstrCanReturn;
+ break;
+
+ case OP_THROW:
+ flags = kInstrCanThrow;
+ break;
+
+ /* unconditional branches */
+ case OP_GOTO:
+ case OP_GOTO_16:
+ case OP_GOTO_32:
+ flags = kInstrCanBranch | kInstrUnconditional;
+ break;
+
+ /* conditional branches */
+ case OP_IF_EQ:
+ case OP_IF_NE:
+ case OP_IF_LT:
+ case OP_IF_GE:
+ case OP_IF_GT:
+ case OP_IF_LE:
+ case OP_IF_EQZ:
+ case OP_IF_NEZ:
+ case OP_IF_LTZ:
+ case OP_IF_GEZ:
+ case OP_IF_GTZ:
+ case OP_IF_LEZ:
+ flags = kInstrCanBranch | kInstrCanContinue;
+ break;
+
+ /* switch statements; if value not in switch, it continues */
+ case OP_PACKED_SWITCH:
+ case OP_SPARSE_SWITCH:
+ flags = kInstrCanSwitch | kInstrCanContinue;
+ break;
+
+ /* verifier/optimizer-generated instructions */
+ case OP_THROW_VERIFICATION_ERROR:
+ flags = kInstrCanThrow;
+ break;
+ case OP_EXECUTE_INLINE:
+ case OP_EXECUTE_INLINE_RANGE:
+ flags = kInstrCanContinue | kInstrCanThrow;
+ break;
+ case OP_IGET_QUICK:
+ case OP_IGET_WIDE_QUICK:
+ case OP_IGET_OBJECT_QUICK:
+ case OP_IPUT_QUICK:
+ case OP_IPUT_WIDE_QUICK:
+ case OP_IPUT_OBJECT_QUICK:
+ case OP_IGET_VOLATILE:
+ case OP_IPUT_VOLATILE:
+ case OP_SGET_VOLATILE:
+ case OP_SPUT_VOLATILE:
+ case OP_IGET_OBJECT_VOLATILE:
+ case OP_IPUT_OBJECT_VOLATILE:
+ case OP_SGET_OBJECT_VOLATILE:
+ case OP_SPUT_OBJECT_VOLATILE:
+ case OP_IGET_WIDE_VOLATILE:
+ case OP_IPUT_WIDE_VOLATILE:
+ case OP_SGET_WIDE_VOLATILE:
+ case OP_SPUT_WIDE_VOLATILE:
+ flags = kInstrCanContinue | kInstrCanThrow;
+ break;
+
+ case OP_INVOKE_VIRTUAL_QUICK:
+ case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+ case OP_INVOKE_SUPER_QUICK:
+ case OP_INVOKE_SUPER_QUICK_RANGE:
+ case OP_INVOKE_DIRECT_EMPTY:
+ flags = kInstrCanContinue | kInstrCanThrow | kInstrInvoke;
+ break;
+
+ /* these should never appear when scanning code */
+ case OP_UNUSED_3E:
+ case OP_UNUSED_3F:
+ case OP_UNUSED_40:
+ case OP_UNUSED_41:
+ case OP_UNUSED_42:
+ case OP_UNUSED_43:
+ case OP_UNUSED_73:
+ case OP_UNUSED_79:
+ case OP_UNUSED_7A:
+ case OP_BREAKPOINT:
+ case OP_UNUSED_F1:
+ case OP_UNUSED_FF:
+ break;
+
+ /*
+ * DO NOT add a "default" clause here. Without it the compiler will
+ * complain if an instruction is missing (which is desirable).
+ */
+ }
+
+ instrFlags[opc] = flags;
+ }
+
+ return instrFlags;
+}
+
+/*
+ * Allocate and populate a 256-element array with instruction formats.
+ * Used in conjunction with dexDecodeInstruction.
+ */
+InstructionFormat* dexCreateInstrFormatTable(void)
+{
+ InstructionFormat* instFmt;
+ int i;
+
+ instFmt = malloc(sizeof(InstructionFormat) * kNumDalvikInstructions);
+ if (instFmt == NULL)
+ return NULL;
+
+ for (i = 0; i < kNumDalvikInstructions; i++) {
+ OpCode opc = (OpCode) i;
+ InstructionFormat fmt = kFmtUnknown;
+
+ switch (opc) {
+ case OP_GOTO:
+ fmt = kFmt10t;
+ break;
+ case OP_NOP:
+ case OP_RETURN_VOID:
+ fmt = kFmt10x;
+ break;
+ case OP_CONST_4:
+ fmt = kFmt11n;
+ break;
+ case OP_CONST_HIGH16:
+ case OP_CONST_WIDE_HIGH16:
+ fmt = kFmt21h;
+ break;
+ case OP_MOVE_RESULT:
+ case OP_MOVE_RESULT_WIDE:
+ case OP_MOVE_RESULT_OBJECT:
+ case OP_MOVE_EXCEPTION:
+ case OP_RETURN:
+ case OP_RETURN_WIDE:
+ case OP_RETURN_OBJECT:
+ case OP_MONITOR_ENTER:
+ case OP_MONITOR_EXIT:
+ case OP_THROW:
+ fmt = kFmt11x;
+ break;
+ case OP_MOVE:
+ case OP_MOVE_WIDE:
+ case OP_MOVE_OBJECT:
+ case OP_ARRAY_LENGTH:
+ case OP_NEG_INT:
+ case OP_NOT_INT:
+ case OP_NEG_LONG:
+ case OP_NOT_LONG:
+ case OP_NEG_FLOAT:
+ case OP_NEG_DOUBLE:
+ case OP_INT_TO_LONG:
+ case OP_INT_TO_FLOAT:
+ case OP_INT_TO_DOUBLE:
+ case OP_LONG_TO_INT:
+ case OP_LONG_TO_FLOAT:
+ case OP_LONG_TO_DOUBLE:
+ case OP_FLOAT_TO_INT:
+ case OP_FLOAT_TO_LONG:
+ case OP_FLOAT_TO_DOUBLE:
+ case OP_DOUBLE_TO_INT:
+ case OP_DOUBLE_TO_LONG:
+ case OP_DOUBLE_TO_FLOAT:
+ case OP_INT_TO_BYTE:
+ case OP_INT_TO_CHAR:
+ case OP_INT_TO_SHORT:
+ case OP_ADD_INT_2ADDR:
+ case OP_SUB_INT_2ADDR:
+ case OP_MUL_INT_2ADDR:
+ case OP_DIV_INT_2ADDR:
+ case OP_REM_INT_2ADDR:
+ case OP_AND_INT_2ADDR:
+ case OP_OR_INT_2ADDR:
+ case OP_XOR_INT_2ADDR:
+ case OP_SHL_INT_2ADDR:
+ case OP_SHR_INT_2ADDR:
+ case OP_USHR_INT_2ADDR:
+ case OP_ADD_LONG_2ADDR:
+ case OP_SUB_LONG_2ADDR:
+ case OP_MUL_LONG_2ADDR:
+ case OP_DIV_LONG_2ADDR:
+ case OP_REM_LONG_2ADDR:
+ case OP_AND_LONG_2ADDR:
+ case OP_OR_LONG_2ADDR:
+ case OP_XOR_LONG_2ADDR:
+ case OP_SHL_LONG_2ADDR:
+ case OP_SHR_LONG_2ADDR:
+ case OP_USHR_LONG_2ADDR:
+ case OP_ADD_FLOAT_2ADDR:
+ case OP_SUB_FLOAT_2ADDR:
+ case OP_MUL_FLOAT_2ADDR:
+ case OP_DIV_FLOAT_2ADDR:
+ case OP_REM_FLOAT_2ADDR:
+ case OP_ADD_DOUBLE_2ADDR:
+ case OP_SUB_DOUBLE_2ADDR:
+ case OP_MUL_DOUBLE_2ADDR:
+ case OP_DIV_DOUBLE_2ADDR:
+ case OP_REM_DOUBLE_2ADDR:
+ fmt = kFmt12x;
+ break;
+ case OP_GOTO_16:
+ fmt = kFmt20t;
+ break;
+ case OP_GOTO_32:
+ fmt = kFmt30t;
+ break;
+ case OP_CONST_STRING:
+ case OP_CONST_CLASS:
+ case OP_CHECK_CAST:
+ case OP_NEW_INSTANCE:
+ case OP_SGET:
+ case OP_SGET_WIDE:
+ case OP_SGET_OBJECT:
+ case OP_SGET_BOOLEAN:
+ case OP_SGET_BYTE:
+ case OP_SGET_CHAR:
+ case OP_SGET_SHORT:
+ case OP_SPUT:
+ case OP_SPUT_WIDE:
+ case OP_SPUT_OBJECT:
+ case OP_SPUT_BOOLEAN:
+ case OP_SPUT_BYTE:
+ case OP_SPUT_CHAR:
+ case OP_SPUT_SHORT:
+ fmt = kFmt21c;
+ break;
+ case OP_CONST_16:
+ case OP_CONST_WIDE_16:
+ fmt = kFmt21s;
+ break;
+ case OP_IF_EQZ:
+ case OP_IF_NEZ:
+ case OP_IF_LTZ:
+ case OP_IF_GEZ:
+ case OP_IF_GTZ:
+ case OP_IF_LEZ:
+ fmt = kFmt21t;
+ break;
+ case OP_FILL_ARRAY_DATA:
+ case OP_PACKED_SWITCH:
+ case OP_SPARSE_SWITCH:
+ fmt = kFmt31t;
+ break;
+ case OP_ADD_INT_LIT8:
+ case OP_RSUB_INT_LIT8:
+ case OP_MUL_INT_LIT8:
+ case OP_DIV_INT_LIT8:
+ case OP_REM_INT_LIT8:
+ case OP_AND_INT_LIT8:
+ case OP_OR_INT_LIT8:
+ case OP_XOR_INT_LIT8:
+ case OP_SHL_INT_LIT8:
+ case OP_SHR_INT_LIT8:
+ case OP_USHR_INT_LIT8:
+ fmt = kFmt22b;
+ break;
+ case OP_INSTANCE_OF:
+ case OP_NEW_ARRAY:
+ case OP_IGET:
+ case OP_IGET_WIDE:
+ case OP_IGET_OBJECT:
+ case OP_IGET_BOOLEAN:
+ case OP_IGET_BYTE:
+ case OP_IGET_CHAR:
+ case OP_IGET_SHORT:
+ case OP_IPUT:
+ case OP_IPUT_WIDE:
+ case OP_IPUT_OBJECT:
+ case OP_IPUT_BOOLEAN:
+ case OP_IPUT_BYTE:
+ case OP_IPUT_CHAR:
+ case OP_IPUT_SHORT:
+ fmt = kFmt22c;
+ break;
+ case OP_ADD_INT_LIT16:
+ case OP_RSUB_INT:
+ case OP_MUL_INT_LIT16:
+ case OP_DIV_INT_LIT16:
+ case OP_REM_INT_LIT16:
+ case OP_AND_INT_LIT16:
+ case OP_OR_INT_LIT16:
+ case OP_XOR_INT_LIT16:
+ fmt = kFmt22s;
+ break;
+ case OP_IF_EQ:
+ case OP_IF_NE:
+ case OP_IF_LT:
+ case OP_IF_GE:
+ case OP_IF_GT:
+ case OP_IF_LE:
+ fmt = kFmt22t;
+ break;
+ case OP_MOVE_FROM16:
+ case OP_MOVE_WIDE_FROM16:
+ case OP_MOVE_OBJECT_FROM16:
+ fmt = kFmt22x;
+ break;
+ case OP_CMPL_FLOAT:
+ case OP_CMPG_FLOAT:
+ case OP_CMPL_DOUBLE:
+ case OP_CMPG_DOUBLE:
+ case OP_CMP_LONG:
+ case OP_AGET:
+ case OP_AGET_WIDE:
+ case OP_AGET_OBJECT:
+ case OP_AGET_BOOLEAN:
+ case OP_AGET_BYTE:
+ case OP_AGET_CHAR:
+ case OP_AGET_SHORT:
+ case OP_APUT:
+ case OP_APUT_WIDE:
+ case OP_APUT_OBJECT:
+ case OP_APUT_BOOLEAN:
+ case OP_APUT_BYTE:
+ case OP_APUT_CHAR:
+ case OP_APUT_SHORT:
+ case OP_ADD_INT:
+ case OP_SUB_INT:
+ case OP_MUL_INT:
+ case OP_DIV_INT:
+ case OP_REM_INT:
+ case OP_AND_INT:
+ case OP_OR_INT:
+ case OP_XOR_INT:
+ case OP_SHL_INT:
+ case OP_SHR_INT:
+ case OP_USHR_INT:
+ case OP_ADD_LONG:
+ case OP_SUB_LONG:
+ case OP_MUL_LONG:
+ case OP_DIV_LONG:
+ case OP_REM_LONG:
+ case OP_AND_LONG:
+ case OP_OR_LONG:
+ case OP_XOR_LONG:
+ case OP_SHL_LONG:
+ case OP_SHR_LONG:
+ case OP_USHR_LONG:
+ case OP_ADD_FLOAT:
+ case OP_SUB_FLOAT:
+ case OP_MUL_FLOAT:
+ case OP_DIV_FLOAT:
+ case OP_REM_FLOAT:
+ case OP_ADD_DOUBLE:
+ case OP_SUB_DOUBLE:
+ case OP_MUL_DOUBLE:
+ case OP_DIV_DOUBLE:
+ case OP_REM_DOUBLE:
+ fmt = kFmt23x;
+ break;
+ case OP_CONST:
+ case OP_CONST_WIDE_32:
+ fmt = kFmt31i;
+ break;
+ case OP_CONST_STRING_JUMBO:
+ fmt = kFmt31c;
+ break;
+ case OP_MOVE_16:
+ case OP_MOVE_WIDE_16:
+ case OP_MOVE_OBJECT_16:
+ fmt = kFmt32x;
+ break;
+ case OP_FILLED_NEW_ARRAY:
+ case OP_INVOKE_VIRTUAL:
+ case OP_INVOKE_SUPER:
+ case OP_INVOKE_DIRECT:
+ case OP_INVOKE_STATIC:
+ case OP_INVOKE_INTERFACE:
+ fmt = kFmt35c;
+ break;
+ case OP_FILLED_NEW_ARRAY_RANGE:
+ case OP_INVOKE_VIRTUAL_RANGE:
+ case OP_INVOKE_SUPER_RANGE:
+ case OP_INVOKE_DIRECT_RANGE:
+ case OP_INVOKE_STATIC_RANGE:
+ case OP_INVOKE_INTERFACE_RANGE:
+ fmt = kFmt3rc;
+ break;
+ case OP_CONST_WIDE:
+ fmt = kFmt51l;
+ break;
+
+ /*
+ * Optimized instructions.
+ */
+ case OP_THROW_VERIFICATION_ERROR:
+ fmt = kFmt20bc;
+ break;
+ case OP_IGET_WIDE_VOLATILE:
+ case OP_IPUT_WIDE_VOLATILE:
+ case OP_IGET_VOLATILE:
+ case OP_IPUT_VOLATILE:
+ case OP_IGET_OBJECT_VOLATILE:
+ case OP_IPUT_OBJECT_VOLATILE:
+ fmt = kFmt22c;
+ break;
+ case OP_SGET_OBJECT_VOLATILE:
+ case OP_SPUT_OBJECT_VOLATILE:
+ case OP_SGET_VOLATILE:
+ case OP_SPUT_VOLATILE:
+ case OP_SGET_WIDE_VOLATILE:
+ case OP_SPUT_WIDE_VOLATILE:
+ fmt = kFmt21c;
+ break;
+ case OP_IGET_QUICK:
+ case OP_IGET_WIDE_QUICK:
+ case OP_IGET_OBJECT_QUICK:
+ case OP_IPUT_QUICK:
+ case OP_IPUT_WIDE_QUICK:
+ case OP_IPUT_OBJECT_QUICK:
+ fmt = kFmt22cs;
+ break;
+ case OP_INVOKE_VIRTUAL_QUICK:
+ case OP_INVOKE_SUPER_QUICK:
+ fmt = kFmt35ms;
+ break;
+ case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+ case OP_INVOKE_SUPER_QUICK_RANGE:
+ fmt = kFmt3rms;
+ break;
+ case OP_EXECUTE_INLINE:
+ fmt = kFmt3inline;
+ break;
+ case OP_EXECUTE_INLINE_RANGE:
+ fmt = kFmt3rinline;
+ break;
+ case OP_INVOKE_DIRECT_EMPTY:
+ fmt = kFmt35c;
+ break;
+
+ /* these should never appear when scanning code */
+ case OP_UNUSED_3E:
+ case OP_UNUSED_3F:
+ case OP_UNUSED_40:
+ case OP_UNUSED_41:
+ case OP_UNUSED_42:
+ case OP_UNUSED_43:
+ case OP_UNUSED_73:
+ case OP_UNUSED_79:
+ case OP_UNUSED_7A:
+ case OP_BREAKPOINT:
+ case OP_UNUSED_F1:
+ case OP_UNUSED_FF:
+ fmt = kFmtUnknown;
+ break;
+
+ /*
+ * DO NOT add a "default" clause here. Without it the compiler will
+ * complain if an instruction is missing (which is desirable).
+ */
+ }
+
+ instFmt[opc] = fmt;
+ }
+
+ return instFmt;
+}
+
+/*
+ * Copied from InterpCore.h. Used for instruction decoding.
+ */
+#define FETCH(_offset) (insns[(_offset)])
+#define INST_INST(_inst) ((_inst) & 0xff)
+#define INST_A(_inst) (((u2)(_inst) >> 8) & 0x0f)
+#define INST_B(_inst) ((u2)(_inst) >> 12)
+#define INST_AA(_inst) ((_inst) >> 8)
+
+/*
+ * Decode the instruction pointed to by "insns".
+ *
+ * Fills out the pieces of "pDec" that are affected by the current
+ * instruction. Does not touch anything else.
+ */
+void dexDecodeInstruction(const InstructionFormat* fmts, const u2* insns,
+ DecodedInstruction* pDec)
+{
+ u2 inst = *insns;
+
+ pDec->opCode = (OpCode) INST_INST(inst);
+
+ switch (dexGetInstrFormat(fmts, pDec->opCode)) {
+ case kFmt10x: // op
+ /* nothing to do; copy the AA bits out for the verifier */
+ pDec->vA = INST_AA(inst);
+ break;
+ case kFmt12x: // op vA, vB
+ pDec->vA = INST_A(inst);
+ pDec->vB = INST_B(inst);
+ break;
+ case kFmt11n: // op vA, #+B
+ pDec->vA = INST_A(inst);
+ pDec->vB = (s4) (INST_B(inst) << 28) >> 28; // sign extend 4-bit value
+ break;
+ case kFmt11x: // op vAA
+ pDec->vA = INST_AA(inst);
+ break;
+ case kFmt10t: // op +AA
+ pDec->vA = (s1) INST_AA(inst); // sign-extend 8-bit value
+ break;
+ case kFmt20t: // op +AAAA
+ pDec->vA = (s2) FETCH(1); // sign-extend 16-bit value
+ break;
+ case kFmt20bc: // op AA, thing@BBBB
+ case kFmt21c: // op vAA, thing@BBBB
+ case kFmt22x: // op vAA, vBBBB
+ pDec->vA = INST_AA(inst);
+ pDec->vB = FETCH(1);
+ break;
+ case kFmt21s: // op vAA, #+BBBB
+ case kFmt21t: // op vAA, +BBBB
+ pDec->vA = INST_AA(inst);
+ pDec->vB = (s2) FETCH(1); // sign-extend 16-bit value
+ break;
+ case kFmt21h: // op vAA, #+BBBB0000[00000000]
+ pDec->vA = INST_AA(inst);
+ /*
+ * The value should be treated as right-zero-extended, but we don't
+ * actually do that here. Among other things, we don't know if it's
+ * the top bits of a 32- or 64-bit value.
+ */
+ pDec->vB = FETCH(1);
+ break;
+ case kFmt23x: // op vAA, vBB, vCC
+ pDec->vA = INST_AA(inst);
+ pDec->vB = FETCH(1) & 0xff;
+ pDec->vC = FETCH(1) >> 8;
+ break;
+ case kFmt22b: // op vAA, vBB, #+CC
+ pDec->vA = INST_AA(inst);
+ pDec->vB = FETCH(1) & 0xff;
+ pDec->vC = (s1) (FETCH(1) >> 8); // sign-extend 8-bit value
+ break;
+ case kFmt22s: // op vA, vB, #+CCCC
+ case kFmt22t: // op vA, vB, +CCCC
+ pDec->vA = INST_A(inst);
+ pDec->vB = INST_B(inst);
+ pDec->vC = (s2) FETCH(1); // sign-extend 16-bit value
+ break;
+ case kFmt22c: // op vA, vB, thing@CCCC
+ case kFmt22cs: // [opt] op vA, vB, field offset CCCC
+ pDec->vA = INST_A(inst);
+ pDec->vB = INST_B(inst);
+ pDec->vC = FETCH(1);
+ break;
+ case kFmt30t: // op +AAAAAAAA
+ pDec->vA = FETCH(1) | ((u4) FETCH(2) << 16); // signed 32-bit value
+ break;
+ case kFmt31t: // op vAA, +BBBBBBBB
+ case kFmt31c: // op vAA, thing@BBBBBBBB
+ pDec->vA = INST_AA(inst);
+ pDec->vB = FETCH(1) | ((u4) FETCH(2) << 16); // 32-bit value
+ break;
+ case kFmt32x: // op vAAAA, vBBBB
+ pDec->vA = FETCH(1);
+ pDec->vB = FETCH(2);
+ break;
+ case kFmt31i: // op vAA, #+BBBBBBBB
+ pDec->vA = INST_AA(inst);
+ pDec->vB = FETCH(1) | ((u4) FETCH(2) << 16);
+ break;
+ case kFmt35c: // op vB, {vD..vG,vA}, thing@CCCC
+ case kFmt35ms: // [opt] invoke-virtual+super
+ {
+ /*
+ * The lettering changes that came about when we went from 4 args
+ * to 5 made the "range" versions of the calls different from
+ * the non-range versions. We have the choice between decoding
+ * them the way the spec shows and having lots of conditionals
+ * in the verifier, or mapping the values onto their original
+ * registers and leaving the verifier intact.
+ *
+ * Current plan is to leave the verifier alone. We can fix it
+ * later if it's architecturally unbearable.
+ *
+ * Bottom line: method constant is always in vB.
+ */
+ u2 regList;
+ int i, count;
+
+ pDec->vA = INST_B(inst);
+ pDec->vB = FETCH(1);
+ regList = FETCH(2);
+
+ if (pDec->vA > 5) {
+ LOGW("Invalid arg count in 35c/35ms (%d)\n", pDec->vA);
+ goto bail;
+ }
+ count = pDec->vA;
+ if (count == 5) {
+ /* 5th arg comes from A field in instruction */
+ pDec->arg[4] = INST_A(inst);
+ count--;
+ }
+ for (i = 0; i < count; i++) {
+ pDec->arg[i] = regList & 0x0f;
+ regList >>= 4;
+ }
+ /* copy arg[0] to vC; we don't have vD/vE/vF, so ignore those */
+ if (pDec->vA > 0)
+ pDec->vC = pDec->arg[0];
+ }
+ break;
+ case kFmt3inline: // [opt] inline invoke
+ {
+ u2 regList;
+ int i;
+
+ pDec->vA = INST_B(inst);
+ pDec->vB = FETCH(1);
+ regList = FETCH(2);
+
+ if (pDec->vA > 4) {
+ LOGW("Invalid arg count in 3inline (%d)\n", pDec->vA);
+ goto bail;
+ }
+ for (i = 0; i < (int) pDec->vA; i++) {
+ pDec->arg[i] = regList & 0x0f;
+ regList >>= 4;
+ }
+ /* copy arg[0] to vC; we don't have vD/vE/vF, so ignore those */
+ if (pDec->vA > 0)
+ pDec->vC = pDec->arg[0];
+ }
+ break;
+ case kFmt35fs: // [opt] invoke-interface
+ assert(false); // TODO
+ break;
+ case kFmt3rc: // op {vCCCC .. v(CCCC+AA-1)}, meth@BBBB
+ case kFmt3rms: // [opt] invoke-virtual+super/range
+ case kFmt3rinline: // [opt] execute-inline/range
+ pDec->vA = INST_AA(inst);
+ pDec->vB = FETCH(1);
+ pDec->vC = FETCH(2);
+ break;
+ case kFmt3rfs: // [opt] invoke-interface/range
+ assert(false); // TODO
+ break;
+ case kFmt51l: // op vAA, #+BBBBBBBBBBBBBBBB
+ pDec->vA = INST_AA(inst);
+ pDec->vB_wide = FETCH(1);
+ pDec->vB_wide |= (u8)FETCH(2) << 16;
+ pDec->vB_wide |= (u8)FETCH(3) << 32;
+ pDec->vB_wide |= (u8)FETCH(4) << 48;
+ break;
+ default:
+ LOGW("Can't decode unexpected format %d (op=%d)\n",
+ dexGetInstrFormat(fmts, pDec->opCode), pDec->opCode);
+ assert(false);
+ break;
+ }
+
+bail:
+ ;
+}
+
+/*
+ * Return the width of the specified instruction, or 0 if not defined. Also
+ * works for special OP_NOP entries, including switch statement data tables
+ * and array data.
+ */
+size_t dexGetInstrOrTableWidthAbs(const InstructionWidth* widths,
+ const u2* insns)
+{
+ size_t width;
+
+ if (*insns == kPackedSwitchSignature) {
+ width = 4 + insns[1] * 2;
+ } else if (*insns == kSparseSwitchSignature) {
+ width = 2 + insns[1] * 4;
+ } else if (*insns == kArrayDataSignature) {
+ u2 elemWidth = insns[1];
+ u4 len = insns[2] | (((u4)insns[3]) << 16);
+ width = 4 + (elemWidth * len + 1) / 2;
+ } else {
+ width = dexGetInstrWidthAbs(widths, INST_INST(insns[0]));
+ }
+ return width;
+}
diff --git a/libdex/InstrUtils.h b/libdex/InstrUtils.h
new file mode 100644
index 0000000..ad2bc34
--- /dev/null
+++ b/libdex/InstrUtils.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik instruction utility functions.
+ */
+#ifndef _LIBDEX_INSTRUTILS
+#define _LIBDEX_INSTRUTILS
+
+#include "DexFile.h"
+#include "OpCode.h"
+
+/*
+ * Dalvik-defined instruction formats.
+ *
+ * (This defines InstructionFormat as an unsigned char to reduce the size
+ * of the table. This isn't necessary with some compilers, which use an
+ * integer width appropriate for the number of enum values.)
+ *
+ * If you add or delete a format, you have to change some or all of:
+ * - this enum
+ * - the switch inside dexDecodeInstruction() in InstrUtils.c
+ * - the switch inside dumpInstruction() in DexDump.c
+ */
+typedef unsigned char InstructionFormat;
+enum InstructionFormat {
+ kFmtUnknown = 0,
+ kFmt10x, // op
+ kFmt12x, // op vA, vB
+ kFmt11n, // op vA, #+B
+ kFmt11x, // op vAA
+ kFmt10t, // op +AA
+ kFmt20bc, // op AA, thing@BBBB
+ kFmt20t, // op +AAAA
+ kFmt22x, // op vAA, vBBBB
+ kFmt21t, // op vAA, +BBBB
+ kFmt21s, // op vAA, #+BBBB
+ kFmt21h, // op vAA, #+BBBB00000[00000000]
+ kFmt21c, // op vAA, thing@BBBB
+ kFmt23x, // op vAA, vBB, vCC
+ kFmt22b, // op vAA, vBB, #+CC
+ kFmt22t, // op vA, vB, +CCCC
+ kFmt22s, // op vA, vB, #+CCCC
+ kFmt22c, // op vA, vB, thing@CCCC
+ kFmt22cs, // [opt] op vA, vB, field offset CCCC
+ kFmt32x, // op vAAAA, vBBBB
+ kFmt30t, // op +AAAAAAAA
+ kFmt31t, // op vAA, +BBBBBBBB
+ kFmt31i, // op vAA, #+BBBBBBBB
+ kFmt31c, // op vAA, thing@BBBBBBBB
+ kFmt35c, // op {vC, vD, vE, vF, vG}, thing@BBBB (B: count, A: vG)
+ kFmt35ms, // [opt] invoke-virtual+super
+ kFmt35fs, // [opt] invoke-interface
+ kFmt3rc, // op {vCCCC .. v(CCCC+AA-1)}, meth@BBBB
+ kFmt3rms, // [opt] invoke-virtual+super/range
+ kFmt3rfs, // [opt] invoke-interface/range
+ kFmt3inline, // [opt] inline invoke
+ kFmt3rinline, // [opt] inline invoke/range
+ kFmt51l, // op vAA, #+BBBBBBBBBBBBBBBB
+};
+
+/*
+ * Holds the contents of a decoded instruction.
+ */
+typedef struct DecodedInstruction {
+ u4 vA;
+ u4 vB;
+ u8 vB_wide; /* for kFmt51l */
+ u4 vC;
+ u4 arg[5]; /* vC/D/E/F/G in invoke or filled-new-array */
+ OpCode opCode;
+} DecodedInstruction;
+
+/*
+ * Instruction width, a value in the range -3 to 5.
+ */
+typedef signed char InstructionWidth;
+
+/*
+ * Instruction flags, used by the verifier and JIT to determine where
+ * control can flow to next. Expected to fit in 8 bits.
+ */
+typedef unsigned char InstructionFlags;
+enum InstructionFlags {
+ kInstrCanBranch = 1, // conditional or unconditional branch
+ kInstrCanContinue = 1 << 1, // flow can continue to next statement
+ kInstrCanSwitch = 1 << 2, // switch statement
+ kInstrCanThrow = 1 << 3, // could cause an exception to be thrown
+ kInstrCanReturn = 1 << 4, // returns, no additional statements
+ kInstrInvoke = 1 << 5, // a flavor of invoke
+ kInstrUnconditional = 1 << 6, // unconditional branch
+};
+
+
+/*
+ * Allocate and populate a 256-element array with instruction widths. A
+ * width of zero means the entry does not exist.
+ */
+InstructionWidth* dexCreateInstrWidthTable(void);
+
+#if 0 // no longer used
+/*
+ * Returns the width of the specified instruction, or 0 if not defined.
+ * Optimized instructions use negative values.
+ */
+DEX_INLINE int dexGetInstrWidth(const InstructionWidth* widths, OpCode opCode)
+{
+ // assert(/*opCode >= 0 &&*/ opCode < kNumDalvikInstructions);
+ return widths[opCode];
+}
+#endif
+
+/*
+ * Return the width of the specified instruction, or 0 if not defined.
+ */
+DEX_INLINE size_t dexGetInstrWidthAbs(const InstructionWidth* widths,
+ OpCode opCode)
+{
+ //assert(/*opCode >= 0 &&*/ opCode < kNumDalvikInstructions);
+
+ int val = widths[opCode];
+ if (val < 0)
+ val = -val;
+ /* XXX - the no-compare trick may be a cycle slower on ARM */
+ return val;
+}
+
+/*
+ * Return the width of the specified instruction, or 0 if not defined. Also
+ * works for special OP_NOP entries, including switch statement data tables
+ * and array data.
+ */
+size_t dexGetInstrOrTableWidthAbs(const InstructionWidth* widths,
+ const u2* insns);
+
+
+/*
+ * Allocate and populate a 256-element array with instruction flags.
+ */
+InstructionFlags* dexCreateInstrFlagsTable(void);
+
+/*
+ * Returns the flags for the specified opcode.
+ */
+DEX_INLINE int dexGetInstrFlags(const InstructionFlags* flags, OpCode opCode)
+{
+ //assert(/*opCode >= 0 &&*/ opCode < kNumDalvikInstructions);
+ return flags[opCode];
+}
+
+
+/*
+ * Allocate and populate a 256-element array with instruction formats.
+ */
+InstructionFormat* dexCreateInstrFormatTable(void);
+
+/*
+ * Return the instruction format for the specified opcode.
+ */
+DEX_INLINE InstructionFormat dexGetInstrFormat(const InstructionFormat* fmts,
+ OpCode opCode)
+{
+ //assert(/*opCode >= 0 &&*/ opCode < kNumDalvikInstructions);
+ return fmts[opCode];
+}
+
+/*
+ * Decode the instruction pointed to by "insns".
+ */
+void dexDecodeInstruction(const InstructionFormat* fmts, const u2* insns,
+ DecodedInstruction* pDec);
+
+#endif /*_LIBDEX_INSTRUTILS*/
diff --git a/libdex/Leb128.c b/libdex/Leb128.c
new file mode 100644
index 0000000..ed09e19
--- /dev/null
+++ b/libdex/Leb128.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Functions for interpreting LEB128 (little endian base 128) values
+ */
+
+#include "Leb128.h"
+
+/*
+ * Reads an unsigned LEB128 value, updating the given pointer to point
+ * just past the end of the read value and also indicating whether the
+ * value was syntactically valid. The only syntactically *invalid*
+ * values are ones that are five bytes long where the final byte has
+ * any but the low-order four bits set. Additionally, if the limit is
+ * passed as non-NULL and bytes would need to be read past the limit,
+ * then the read is considered invalid.
+ */
+int readAndVerifyUnsignedLeb128(const u1** pStream, const u1* limit,
+ bool* okay) {
+ const u1* ptr = *pStream;
+ int result = readUnsignedLeb128(pStream);
+
+ if (((limit != NULL) && (*pStream > limit))
+ || (((*pStream - ptr) == 5) && (ptr[4] > 0x0f))) {
+ *okay = false;
+ }
+
+ return result;
+}
+
+/*
+ * Reads a signed LEB128 value, updating the given pointer to point
+ * just past the end of the read value and also indicating whether the
+ * value was syntactically valid. The only syntactically *invalid*
+ * values are ones that are five bytes long where the final byte has
+ * any but the low-order four bits set. Additionally, if the limit is
+ * passed as non-NULL and bytes would need to be read past the limit,
+ * then the read is considered invalid.
+ */
+int readAndVerifySignedLeb128(const u1** pStream, const u1* limit,
+ bool* okay) {
+ const u1* ptr = *pStream;
+ int result = readSignedLeb128(pStream);
+
+ if (((limit != NULL) && (*pStream > limit))
+ || (((*pStream - ptr) == 5) && (ptr[4] > 0x0f))) {
+ *okay = false;
+ }
+
+ return result;
+}
diff --git a/libdex/Leb128.h b/libdex/Leb128.h
new file mode 100644
index 0000000..41799fe
--- /dev/null
+++ b/libdex/Leb128.h
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Functions for interpreting LEB128 (little endian base 128) values
+ */
+
+#ifndef _LIBDEX_LEB128
+#define _LIBDEX_LEB128
+
+#include "DexFile.h"
+
+/*
+ * Reads an unsigned LEB128 value, updating the given pointer to point
+ * just past the end of the read value. This function tolerates
+ * non-zero high-order bits in the fifth encoded byte.
+ */
+DEX_INLINE int readUnsignedLeb128(const u1** pStream) {
+ const u1* ptr = *pStream;
+ int result = *(ptr++);
+
+ if (result > 0x7f) {
+ int cur = *(ptr++);
+ result = (result & 0x7f) | ((cur & 0x7f) << 7);
+ if (cur > 0x7f) {
+ cur = *(ptr++);
+ result |= (cur & 0x7f) << 14;
+ if (cur > 0x7f) {
+ cur = *(ptr++);
+ result |= (cur & 0x7f) << 21;
+ if (cur > 0x7f) {
+ /*
+ * Note: We don't check to see if cur is out of
+ * range here, meaning we tolerate garbage in the
+ * high four-order bits.
+ */
+ cur = *(ptr++);
+ result |= cur << 28;
+ }
+ }
+ }
+ }
+
+ *pStream = ptr;
+ return result;
+}
+
+/*
+ * Reads a signed LEB128 value, updating the given pointer to point
+ * just past the end of the read value. This function tolerates
+ * non-zero high-order bits in the fifth encoded byte.
+ */
+DEX_INLINE int readSignedLeb128(const u1** pStream) {
+ const u1* ptr = *pStream;
+ int result = *(ptr++);
+
+ if (result <= 0x7f) {
+ result = (result << 25) >> 25;
+ } else {
+ int cur = *(ptr++);
+ result = (result & 0x7f) | ((cur & 0x7f) << 7);
+ if (cur <= 0x7f) {
+ result = (result << 18) >> 18;
+ } else {
+ cur = *(ptr++);
+ result |= (cur & 0x7f) << 14;
+ if (cur <= 0x7f) {
+ result = (result << 11) >> 11;
+ } else {
+ cur = *(ptr++);
+ result |= (cur & 0x7f) << 21;
+ if (cur <= 0x7f) {
+ result = (result << 4) >> 4;
+ } else {
+ /*
+ * Note: We don't check to see if cur is out of
+ * range here, meaning we tolerate garbage in the
+ * high four-order bits.
+ */
+ cur = *(ptr++);
+ result |= cur << 28;
+ }
+ }
+ }
+ }
+
+ *pStream = ptr;
+ return result;
+}
+
+/*
+ * Reads an unsigned LEB128 value, updating the given pointer to point
+ * just past the end of the read value and also indicating whether the
+ * value was syntactically valid. The only syntactically *invalid*
+ * values are ones that are five bytes long where the final byte has
+ * any but the low-order four bits set. Additionally, if the limit is
+ * passed as non-NULL and bytes would need to be read past the limit,
+ * then the read is considered invalid.
+ */
+int readAndVerifyUnsignedLeb128(const u1** pStream, const u1* limit,
+ bool* okay);
+
+/*
+ * Reads a signed LEB128 value, updating the given pointer to point
+ * just past the end of the read value and also indicating whether the
+ * value was syntactically valid. The only syntactically *invalid*
+ * values are ones that are five bytes long where the final byte has
+ * any but the low-order four bits set. Additionally, if the limit is
+ * passed as non-NULL and bytes would need to be read past the limit,
+ * then the read is considered invalid.
+ */
+int readAndVerifySignedLeb128(const u1** pStream, const u1* limit, bool* okay);
+
+
+/*
+ * Writes a 32-bit value in unsigned ULEB128 format.
+ *
+ * Returns the updated pointer.
+ */
+DEX_INLINE u1* writeUnsignedLeb128(u1* ptr, u4 data)
+{
+ while (true) {
+ u1 out = data & 0x7f;
+ if (out != data) {
+ *ptr++ = out | 0x80;
+ data >>= 7;
+ } else {
+ *ptr++ = out;
+ break;
+ }
+ }
+
+ return ptr;
+}
+
+/*
+ * Returns the number of bytes needed to encode "val" in ULEB128 form.
+ */
+DEX_INLINE int unsignedLeb128Size(u4 data)
+{
+ int count = 0;
+
+ do {
+ data >>= 7;
+ count++;
+ } while (data != 0);
+
+ return count;
+}
+
+#endif
diff --git a/libdex/OpCode.h b/libdex/OpCode.h
new file mode 100644
index 0000000..312ff01
--- /dev/null
+++ b/libdex/OpCode.h
@@ -0,0 +1,664 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik opcode enumeration.
+ */
+#ifndef _LIBDEX_OPCODE
+#define _LIBDEX_OPCODE
+
+/*
+ * If you add, delete, or renumber instructions, you need to change things
+ * in various places. Renumbering really only affects the "unused" opcodes,
+ * which are given explicit enumeration values to make it easier to find
+ * the places in the code that need to be updated when making changes --
+ * if you replace "OP_UNUSED_2D" and neglect to update a switch statement,
+ * the compiler will complain about an unknown value.
+ *
+ * Opcode definitions and attributes:
+ * - update the OpCode enum below
+ * - update the "goto table" definition macro, DEFINE_GOTO_TABLE(), below
+ * - update the instruction info table generators and (if you changed an
+ * instruction format) instruction decoder in InstrUtils.c
+ * - update the instruction format list in InstrUtils.h, if necessary
+ * - update the parallel definitions in the class dalvik.bytecode.Opcodes
+ *
+ * Interpreter:
+ * - implement/update the instruction in C in mterp/c/...
+ * - verify new code by running with "dalvik.vm.execution-mode =
+ * int:portable" or "-Xint:portable"
+ * - implement/update the instruction in ARM in mterp/armv5/...
+ * - verify by enabling ARM handler for that instruction in mterp config
+ * and running int:fast as above
+ * - repeat for other platforms (x86, ...)
+ * (see notes in mterp/ReadMe.txt for rebuilding instructions)
+ *
+ * Verifier / optimizer:
+ * - update some stuff in analysis/DexOptimize.c, analysis/DexVerify.c,
+ * and/or analysis/CodeVerify.c as needed
+ * - verify by running with verifier enabled (it's on by default)
+ *
+ * Tools:
+ * - update the OpCodeNames table in dexdump/OpCodeNames.c
+ * - update dexdump/DexDump.c if an instruction format has changed
+ *
+ * Note: The Dalvik VM tests (in the tests subdirectory) provide a convenient
+ * way to test most of the above without doing any rebuilds. In particular,
+ * test 003-omnibus-opcodes will exercise most of the opcodes.
+ */
+
+/*
+ * Dalvik opcode list.
+ */
+typedef enum OpCode {
+ OP_NOP = 0x00,
+
+ OP_MOVE = 0x01,
+ OP_MOVE_FROM16 = 0x02,
+ OP_MOVE_16 = 0x03,
+ OP_MOVE_WIDE = 0x04,
+ OP_MOVE_WIDE_FROM16 = 0x05,
+ OP_MOVE_WIDE_16 = 0x06,
+ OP_MOVE_OBJECT = 0x07,
+ OP_MOVE_OBJECT_FROM16 = 0x08,
+ OP_MOVE_OBJECT_16 = 0x09,
+
+ OP_MOVE_RESULT = 0x0a,
+ OP_MOVE_RESULT_WIDE = 0x0b,
+ OP_MOVE_RESULT_OBJECT = 0x0c,
+ OP_MOVE_EXCEPTION = 0x0d,
+
+ OP_RETURN_VOID = 0x0e,
+ OP_RETURN = 0x0f,
+ OP_RETURN_WIDE = 0x10,
+ OP_RETURN_OBJECT = 0x11,
+
+ OP_CONST_4 = 0x12,
+ OP_CONST_16 = 0x13,
+ OP_CONST = 0x14,
+ OP_CONST_HIGH16 = 0x15,
+ OP_CONST_WIDE_16 = 0x16,
+ OP_CONST_WIDE_32 = 0x17,
+ OP_CONST_WIDE = 0x18,
+ OP_CONST_WIDE_HIGH16 = 0x19,
+ OP_CONST_STRING = 0x1a,
+ OP_CONST_STRING_JUMBO = 0x1b,
+ OP_CONST_CLASS = 0x1c,
+
+ OP_MONITOR_ENTER = 0x1d,
+ OP_MONITOR_EXIT = 0x1e,
+
+ OP_CHECK_CAST = 0x1f,
+ OP_INSTANCE_OF = 0x20,
+
+ OP_ARRAY_LENGTH = 0x21,
+
+ OP_NEW_INSTANCE = 0x22,
+ OP_NEW_ARRAY = 0x23,
+
+ OP_FILLED_NEW_ARRAY = 0x24,
+ OP_FILLED_NEW_ARRAY_RANGE = 0x25,
+ OP_FILL_ARRAY_DATA = 0x26,
+
+ OP_THROW = 0x27,
+ OP_GOTO = 0x28,
+ OP_GOTO_16 = 0x29,
+ OP_GOTO_32 = 0x2a,
+ OP_PACKED_SWITCH = 0x2b,
+ OP_SPARSE_SWITCH = 0x2c,
+
+ OP_CMPL_FLOAT = 0x2d,
+ OP_CMPG_FLOAT = 0x2e,
+ OP_CMPL_DOUBLE = 0x2f,
+ OP_CMPG_DOUBLE = 0x30,
+ OP_CMP_LONG = 0x31,
+
+ OP_IF_EQ = 0x32,
+ OP_IF_NE = 0x33,
+ OP_IF_LT = 0x34,
+ OP_IF_GE = 0x35,
+ OP_IF_GT = 0x36,
+ OP_IF_LE = 0x37,
+ OP_IF_EQZ = 0x38,
+ OP_IF_NEZ = 0x39,
+ OP_IF_LTZ = 0x3a,
+ OP_IF_GEZ = 0x3b,
+ OP_IF_GTZ = 0x3c,
+ OP_IF_LEZ = 0x3d,
+
+ OP_UNUSED_3E = 0x3e,
+ OP_UNUSED_3F = 0x3f,
+ OP_UNUSED_40 = 0x40,
+ OP_UNUSED_41 = 0x41,
+ OP_UNUSED_42 = 0x42,
+ OP_UNUSED_43 = 0x43,
+
+ OP_AGET = 0x44,
+ OP_AGET_WIDE = 0x45,
+ OP_AGET_OBJECT = 0x46,
+ OP_AGET_BOOLEAN = 0x47,
+ OP_AGET_BYTE = 0x48,
+ OP_AGET_CHAR = 0x49,
+ OP_AGET_SHORT = 0x4a,
+ OP_APUT = 0x4b,
+ OP_APUT_WIDE = 0x4c,
+ OP_APUT_OBJECT = 0x4d,
+ OP_APUT_BOOLEAN = 0x4e,
+ OP_APUT_BYTE = 0x4f,
+ OP_APUT_CHAR = 0x50,
+ OP_APUT_SHORT = 0x51,
+
+ OP_IGET = 0x52,
+ OP_IGET_WIDE = 0x53,
+ OP_IGET_OBJECT = 0x54,
+ OP_IGET_BOOLEAN = 0x55,
+ OP_IGET_BYTE = 0x56,
+ OP_IGET_CHAR = 0x57,
+ OP_IGET_SHORT = 0x58,
+ OP_IPUT = 0x59,
+ OP_IPUT_WIDE = 0x5a,
+ OP_IPUT_OBJECT = 0x5b,
+ OP_IPUT_BOOLEAN = 0x5c,
+ OP_IPUT_BYTE = 0x5d,
+ OP_IPUT_CHAR = 0x5e,
+ OP_IPUT_SHORT = 0x5f,
+
+ OP_SGET = 0x60,
+ OP_SGET_WIDE = 0x61,
+ OP_SGET_OBJECT = 0x62,
+ OP_SGET_BOOLEAN = 0x63,
+ OP_SGET_BYTE = 0x64,
+ OP_SGET_CHAR = 0x65,
+ OP_SGET_SHORT = 0x66,
+ OP_SPUT = 0x67,
+ OP_SPUT_WIDE = 0x68,
+ OP_SPUT_OBJECT = 0x69,
+ OP_SPUT_BOOLEAN = 0x6a,
+ OP_SPUT_BYTE = 0x6b,
+ OP_SPUT_CHAR = 0x6c,
+ OP_SPUT_SHORT = 0x6d,
+
+ OP_INVOKE_VIRTUAL = 0x6e,
+ OP_INVOKE_SUPER = 0x6f,
+ OP_INVOKE_DIRECT = 0x70,
+ OP_INVOKE_STATIC = 0x71,
+ OP_INVOKE_INTERFACE = 0x72,
+
+ OP_UNUSED_73 = 0x73,
+
+ OP_INVOKE_VIRTUAL_RANGE = 0x74,
+ OP_INVOKE_SUPER_RANGE = 0x75,
+ OP_INVOKE_DIRECT_RANGE = 0x76,
+ OP_INVOKE_STATIC_RANGE = 0x77,
+ OP_INVOKE_INTERFACE_RANGE = 0x78,
+
+ OP_UNUSED_79 = 0x79,
+ OP_UNUSED_7A = 0x7a,
+
+ OP_NEG_INT = 0x7b,
+ OP_NOT_INT = 0x7c,
+ OP_NEG_LONG = 0x7d,
+ OP_NOT_LONG = 0x7e,
+ OP_NEG_FLOAT = 0x7f,
+ OP_NEG_DOUBLE = 0x80,
+ OP_INT_TO_LONG = 0x81,
+ OP_INT_TO_FLOAT = 0x82,
+ OP_INT_TO_DOUBLE = 0x83,
+ OP_LONG_TO_INT = 0x84,
+ OP_LONG_TO_FLOAT = 0x85,
+ OP_LONG_TO_DOUBLE = 0x86,
+ OP_FLOAT_TO_INT = 0x87,
+ OP_FLOAT_TO_LONG = 0x88,
+ OP_FLOAT_TO_DOUBLE = 0x89,
+ OP_DOUBLE_TO_INT = 0x8a,
+ OP_DOUBLE_TO_LONG = 0x8b,
+ OP_DOUBLE_TO_FLOAT = 0x8c,
+ OP_INT_TO_BYTE = 0x8d,
+ OP_INT_TO_CHAR = 0x8e,
+ OP_INT_TO_SHORT = 0x8f,
+
+ OP_ADD_INT = 0x90,
+ OP_SUB_INT = 0x91,
+ OP_MUL_INT = 0x92,
+ OP_DIV_INT = 0x93,
+ OP_REM_INT = 0x94,
+ OP_AND_INT = 0x95,
+ OP_OR_INT = 0x96,
+ OP_XOR_INT = 0x97,
+ OP_SHL_INT = 0x98,
+ OP_SHR_INT = 0x99,
+ OP_USHR_INT = 0x9a,
+
+ OP_ADD_LONG = 0x9b,
+ OP_SUB_LONG = 0x9c,
+ OP_MUL_LONG = 0x9d,
+ OP_DIV_LONG = 0x9e,
+ OP_REM_LONG = 0x9f,
+ OP_AND_LONG = 0xa0,
+ OP_OR_LONG = 0xa1,
+ OP_XOR_LONG = 0xa2,
+ OP_SHL_LONG = 0xa3,
+ OP_SHR_LONG = 0xa4,
+ OP_USHR_LONG = 0xa5,
+
+ OP_ADD_FLOAT = 0xa6,
+ OP_SUB_FLOAT = 0xa7,
+ OP_MUL_FLOAT = 0xa8,
+ OP_DIV_FLOAT = 0xa9,
+ OP_REM_FLOAT = 0xaa,
+ OP_ADD_DOUBLE = 0xab,
+ OP_SUB_DOUBLE = 0xac,
+ OP_MUL_DOUBLE = 0xad,
+ OP_DIV_DOUBLE = 0xae,
+ OP_REM_DOUBLE = 0xaf,
+
+ OP_ADD_INT_2ADDR = 0xb0,
+ OP_SUB_INT_2ADDR = 0xb1,
+ OP_MUL_INT_2ADDR = 0xb2,
+ OP_DIV_INT_2ADDR = 0xb3,
+ OP_REM_INT_2ADDR = 0xb4,
+ OP_AND_INT_2ADDR = 0xb5,
+ OP_OR_INT_2ADDR = 0xb6,
+ OP_XOR_INT_2ADDR = 0xb7,
+ OP_SHL_INT_2ADDR = 0xb8,
+ OP_SHR_INT_2ADDR = 0xb9,
+ OP_USHR_INT_2ADDR = 0xba,
+
+ OP_ADD_LONG_2ADDR = 0xbb,
+ OP_SUB_LONG_2ADDR = 0xbc,
+ OP_MUL_LONG_2ADDR = 0xbd,
+ OP_DIV_LONG_2ADDR = 0xbe,
+ OP_REM_LONG_2ADDR = 0xbf,
+ OP_AND_LONG_2ADDR = 0xc0,
+ OP_OR_LONG_2ADDR = 0xc1,
+ OP_XOR_LONG_2ADDR = 0xc2,
+ OP_SHL_LONG_2ADDR = 0xc3,
+ OP_SHR_LONG_2ADDR = 0xc4,
+ OP_USHR_LONG_2ADDR = 0xc5,
+
+ OP_ADD_FLOAT_2ADDR = 0xc6,
+ OP_SUB_FLOAT_2ADDR = 0xc7,
+ OP_MUL_FLOAT_2ADDR = 0xc8,
+ OP_DIV_FLOAT_2ADDR = 0xc9,
+ OP_REM_FLOAT_2ADDR = 0xca,
+ OP_ADD_DOUBLE_2ADDR = 0xcb,
+ OP_SUB_DOUBLE_2ADDR = 0xcc,
+ OP_MUL_DOUBLE_2ADDR = 0xcd,
+ OP_DIV_DOUBLE_2ADDR = 0xce,
+ OP_REM_DOUBLE_2ADDR = 0xcf,
+
+ OP_ADD_INT_LIT16 = 0xd0,
+ OP_RSUB_INT = 0xd1, /* no _LIT16 suffix for this */
+ OP_MUL_INT_LIT16 = 0xd2,
+ OP_DIV_INT_LIT16 = 0xd3,
+ OP_REM_INT_LIT16 = 0xd4,
+ OP_AND_INT_LIT16 = 0xd5,
+ OP_OR_INT_LIT16 = 0xd6,
+ OP_XOR_INT_LIT16 = 0xd7,
+
+ OP_ADD_INT_LIT8 = 0xd8,
+ OP_RSUB_INT_LIT8 = 0xd9,
+ OP_MUL_INT_LIT8 = 0xda,
+ OP_DIV_INT_LIT8 = 0xdb,
+ OP_REM_INT_LIT8 = 0xdc,
+ OP_AND_INT_LIT8 = 0xdd,
+ OP_OR_INT_LIT8 = 0xde,
+ OP_XOR_INT_LIT8 = 0xdf,
+ OP_SHL_INT_LIT8 = 0xe0,
+ OP_SHR_INT_LIT8 = 0xe1,
+ OP_USHR_INT_LIT8 = 0xe2,
+
+ /* verifier/optimizer output -- nothing below here is generated by "dx" */
+ OP_IGET_VOLATILE = 0xe3,
+ OP_IPUT_VOLATILE = 0xe4,
+ OP_SGET_VOLATILE = 0xe5,
+ OP_SPUT_VOLATILE = 0xe6,
+ OP_IGET_OBJECT_VOLATILE = 0xe7,
+
+ OP_IGET_WIDE_VOLATILE = 0xe8,
+ OP_IPUT_WIDE_VOLATILE = 0xe9,
+ OP_SGET_WIDE_VOLATILE = 0xea,
+ OP_SPUT_WIDE_VOLATILE = 0xeb,
+
+ /*
+ * The "breakpoint" instruction is special, in that it should never
+ * be seen by anything but the debug interpreter. During debugging
+ * it takes the place of an arbitrary opcode, which means operations
+ * like "tell me the opcode width so I can find the next instruction"
+ * aren't possible. (This is correctable, but probably not useful.)
+ */
+ OP_BREAKPOINT = 0xec,
+
+ OP_THROW_VERIFICATION_ERROR = 0xed,
+ OP_EXECUTE_INLINE = 0xee,
+ OP_EXECUTE_INLINE_RANGE = 0xef,
+
+ OP_INVOKE_DIRECT_EMPTY = 0xf0,
+ OP_UNUSED_F1 = 0xf1, /* OP_INVOKE_DIRECT_EMPTY_RANGE? */
+ OP_IGET_QUICK = 0xf2,
+ OP_IGET_WIDE_QUICK = 0xf3,
+ OP_IGET_OBJECT_QUICK = 0xf4,
+ OP_IPUT_QUICK = 0xf5,
+ OP_IPUT_WIDE_QUICK = 0xf6,
+ OP_IPUT_OBJECT_QUICK = 0xf7,
+
+ OP_INVOKE_VIRTUAL_QUICK = 0xf8,
+ OP_INVOKE_VIRTUAL_QUICK_RANGE = 0xf9,
+ OP_INVOKE_SUPER_QUICK = 0xfa,
+ OP_INVOKE_SUPER_QUICK_RANGE = 0xfb,
+ OP_IPUT_OBJECT_VOLATILE = 0xfc,
+ OP_SGET_OBJECT_VOLATILE = 0xfd,
+ OP_SPUT_OBJECT_VOLATILE = 0xfe,
+
+ OP_UNUSED_FF = 0xff, /* reserved for code expansion */
+} OpCode;
+
+#define kNumDalvikInstructions 256
+
+
+/*
+ * Switch-statement signatures are a "NOP" followed by a code. (A true NOP
+ * is 0x0000.)
+ */
+#define kPackedSwitchSignature 0x0100
+#define kSparseSwitchSignature 0x0200
+#define kArrayDataSignature 0x0300
+
+/*
+ * Macro used to generate computed goto tables for the C interpreter.
+ *
+ * The labels here must match up with the labels in the interpreter
+ * implementation. There is no direct connection between these and the
+ * numeric definitions above, but if the two get out of sync strange things
+ * will happen.
+ */
+#define DEFINE_GOTO_TABLE(_name) \
+ static const void* _name[kNumDalvikInstructions] = { \
+ /* 00..0f */ \
+ H(OP_NOP), \
+ H(OP_MOVE), \
+ H(OP_MOVE_FROM16), \
+ H(OP_MOVE_16), \
+ H(OP_MOVE_WIDE), \
+ H(OP_MOVE_WIDE_FROM16), \
+ H(OP_MOVE_WIDE_16), \
+ H(OP_MOVE_OBJECT), \
+ H(OP_MOVE_OBJECT_FROM16), \
+ H(OP_MOVE_OBJECT_16), \
+ H(OP_MOVE_RESULT), \
+ H(OP_MOVE_RESULT_WIDE), \
+ H(OP_MOVE_RESULT_OBJECT), \
+ H(OP_MOVE_EXCEPTION), \
+ H(OP_RETURN_VOID), \
+ H(OP_RETURN), \
+ /* 10..1f */ \
+ H(OP_RETURN_WIDE), \
+ H(OP_RETURN_OBJECT), \
+ H(OP_CONST_4), \
+ H(OP_CONST_16), \
+ H(OP_CONST), \
+ H(OP_CONST_HIGH16), \
+ H(OP_CONST_WIDE_16), \
+ H(OP_CONST_WIDE_32), \
+ H(OP_CONST_WIDE), \
+ H(OP_CONST_WIDE_HIGH16), \
+ H(OP_CONST_STRING), \
+ H(OP_CONST_STRING_JUMBO), \
+ H(OP_CONST_CLASS), \
+ H(OP_MONITOR_ENTER), \
+ H(OP_MONITOR_EXIT), \
+ H(OP_CHECK_CAST), \
+ /* 20..2f */ \
+ H(OP_INSTANCE_OF), \
+ H(OP_ARRAY_LENGTH), \
+ H(OP_NEW_INSTANCE), \
+ H(OP_NEW_ARRAY), \
+ H(OP_FILLED_NEW_ARRAY), \
+ H(OP_FILLED_NEW_ARRAY_RANGE), \
+ H(OP_FILL_ARRAY_DATA), \
+ H(OP_THROW), \
+ H(OP_GOTO), \
+ H(OP_GOTO_16), \
+ H(OP_GOTO_32), \
+ H(OP_PACKED_SWITCH), \
+ H(OP_SPARSE_SWITCH), \
+ H(OP_CMPL_FLOAT), \
+ H(OP_CMPG_FLOAT), \
+ H(OP_CMPL_DOUBLE), \
+ /* 30..3f */ \
+ H(OP_CMPG_DOUBLE), \
+ H(OP_CMP_LONG), \
+ H(OP_IF_EQ), \
+ H(OP_IF_NE), \
+ H(OP_IF_LT), \
+ H(OP_IF_GE), \
+ H(OP_IF_GT), \
+ H(OP_IF_LE), \
+ H(OP_IF_EQZ), \
+ H(OP_IF_NEZ), \
+ H(OP_IF_LTZ), \
+ H(OP_IF_GEZ), \
+ H(OP_IF_GTZ), \
+ H(OP_IF_LEZ), \
+ H(OP_UNUSED_3E), \
+ H(OP_UNUSED_3F), \
+ /* 40..4f */ \
+ H(OP_UNUSED_40), \
+ H(OP_UNUSED_41), \
+ H(OP_UNUSED_42), \
+ H(OP_UNUSED_43), \
+ H(OP_AGET), \
+ H(OP_AGET_WIDE), \
+ H(OP_AGET_OBJECT), \
+ H(OP_AGET_BOOLEAN), \
+ H(OP_AGET_BYTE), \
+ H(OP_AGET_CHAR), \
+ H(OP_AGET_SHORT), \
+ H(OP_APUT), \
+ H(OP_APUT_WIDE), \
+ H(OP_APUT_OBJECT), \
+ H(OP_APUT_BOOLEAN), \
+ H(OP_APUT_BYTE), \
+ /* 50..5f */ \
+ H(OP_APUT_CHAR), \
+ H(OP_APUT_SHORT), \
+ H(OP_IGET), \
+ H(OP_IGET_WIDE), \
+ H(OP_IGET_OBJECT), \
+ H(OP_IGET_BOOLEAN), \
+ H(OP_IGET_BYTE), \
+ H(OP_IGET_CHAR), \
+ H(OP_IGET_SHORT), \
+ H(OP_IPUT), \
+ H(OP_IPUT_WIDE), \
+ H(OP_IPUT_OBJECT), \
+ H(OP_IPUT_BOOLEAN), \
+ H(OP_IPUT_BYTE), \
+ H(OP_IPUT_CHAR), \
+ H(OP_IPUT_SHORT), \
+ /* 60..6f */ \
+ H(OP_SGET), \
+ H(OP_SGET_WIDE), \
+ H(OP_SGET_OBJECT), \
+ H(OP_SGET_BOOLEAN), \
+ H(OP_SGET_BYTE), \
+ H(OP_SGET_CHAR), \
+ H(OP_SGET_SHORT), \
+ H(OP_SPUT), \
+ H(OP_SPUT_WIDE), \
+ H(OP_SPUT_OBJECT), \
+ H(OP_SPUT_BOOLEAN), \
+ H(OP_SPUT_BYTE), \
+ H(OP_SPUT_CHAR), \
+ H(OP_SPUT_SHORT), \
+ H(OP_INVOKE_VIRTUAL), \
+ H(OP_INVOKE_SUPER), \
+ /* 70..7f */ \
+ H(OP_INVOKE_DIRECT), \
+ H(OP_INVOKE_STATIC), \
+ H(OP_INVOKE_INTERFACE), \
+ H(OP_UNUSED_73), \
+ H(OP_INVOKE_VIRTUAL_RANGE), \
+ H(OP_INVOKE_SUPER_RANGE), \
+ H(OP_INVOKE_DIRECT_RANGE), \
+ H(OP_INVOKE_STATIC_RANGE), \
+ H(OP_INVOKE_INTERFACE_RANGE), \
+ H(OP_UNUSED_79), \
+ H(OP_UNUSED_7A), \
+ H(OP_NEG_INT), \
+ H(OP_NOT_INT), \
+ H(OP_NEG_LONG), \
+ H(OP_NOT_LONG), \
+ H(OP_NEG_FLOAT), \
+ /* 80..8f */ \
+ H(OP_NEG_DOUBLE), \
+ H(OP_INT_TO_LONG), \
+ H(OP_INT_TO_FLOAT), \
+ H(OP_INT_TO_DOUBLE), \
+ H(OP_LONG_TO_INT), \
+ H(OP_LONG_TO_FLOAT), \
+ H(OP_LONG_TO_DOUBLE), \
+ H(OP_FLOAT_TO_INT), \
+ H(OP_FLOAT_TO_LONG), \
+ H(OP_FLOAT_TO_DOUBLE), \
+ H(OP_DOUBLE_TO_INT), \
+ H(OP_DOUBLE_TO_LONG), \
+ H(OP_DOUBLE_TO_FLOAT), \
+ H(OP_INT_TO_BYTE), \
+ H(OP_INT_TO_CHAR), \
+ H(OP_INT_TO_SHORT), \
+ /* 90..9f */ \
+ H(OP_ADD_INT), \
+ H(OP_SUB_INT), \
+ H(OP_MUL_INT), \
+ H(OP_DIV_INT), \
+ H(OP_REM_INT), \
+ H(OP_AND_INT), \
+ H(OP_OR_INT), \
+ H(OP_XOR_INT), \
+ H(OP_SHL_INT), \
+ H(OP_SHR_INT), \
+ H(OP_USHR_INT), \
+ H(OP_ADD_LONG), \
+ H(OP_SUB_LONG), \
+ H(OP_MUL_LONG), \
+ H(OP_DIV_LONG), \
+ H(OP_REM_LONG), \
+ /* a0..af */ \
+ H(OP_AND_LONG), \
+ H(OP_OR_LONG), \
+ H(OP_XOR_LONG), \
+ H(OP_SHL_LONG), \
+ H(OP_SHR_LONG), \
+ H(OP_USHR_LONG), \
+ H(OP_ADD_FLOAT), \
+ H(OP_SUB_FLOAT), \
+ H(OP_MUL_FLOAT), \
+ H(OP_DIV_FLOAT), \
+ H(OP_REM_FLOAT), \
+ H(OP_ADD_DOUBLE), \
+ H(OP_SUB_DOUBLE), \
+ H(OP_MUL_DOUBLE), \
+ H(OP_DIV_DOUBLE), \
+ H(OP_REM_DOUBLE), \
+ /* b0..bf */ \
+ H(OP_ADD_INT_2ADDR), \
+ H(OP_SUB_INT_2ADDR), \
+ H(OP_MUL_INT_2ADDR), \
+ H(OP_DIV_INT_2ADDR), \
+ H(OP_REM_INT_2ADDR), \
+ H(OP_AND_INT_2ADDR), \
+ H(OP_OR_INT_2ADDR), \
+ H(OP_XOR_INT_2ADDR), \
+ H(OP_SHL_INT_2ADDR), \
+ H(OP_SHR_INT_2ADDR), \
+ H(OP_USHR_INT_2ADDR), \
+ H(OP_ADD_LONG_2ADDR), \
+ H(OP_SUB_LONG_2ADDR), \
+ H(OP_MUL_LONG_2ADDR), \
+ H(OP_DIV_LONG_2ADDR), \
+ H(OP_REM_LONG_2ADDR), \
+ /* c0..cf */ \
+ H(OP_AND_LONG_2ADDR), \
+ H(OP_OR_LONG_2ADDR), \
+ H(OP_XOR_LONG_2ADDR), \
+ H(OP_SHL_LONG_2ADDR), \
+ H(OP_SHR_LONG_2ADDR), \
+ H(OP_USHR_LONG_2ADDR), \
+ H(OP_ADD_FLOAT_2ADDR), \
+ H(OP_SUB_FLOAT_2ADDR), \
+ H(OP_MUL_FLOAT_2ADDR), \
+ H(OP_DIV_FLOAT_2ADDR), \
+ H(OP_REM_FLOAT_2ADDR), \
+ H(OP_ADD_DOUBLE_2ADDR), \
+ H(OP_SUB_DOUBLE_2ADDR), \
+ H(OP_MUL_DOUBLE_2ADDR), \
+ H(OP_DIV_DOUBLE_2ADDR), \
+ H(OP_REM_DOUBLE_2ADDR), \
+ /* d0..df */ \
+ H(OP_ADD_INT_LIT16), \
+ H(OP_RSUB_INT), \
+ H(OP_MUL_INT_LIT16), \
+ H(OP_DIV_INT_LIT16), \
+ H(OP_REM_INT_LIT16), \
+ H(OP_AND_INT_LIT16), \
+ H(OP_OR_INT_LIT16), \
+ H(OP_XOR_INT_LIT16), \
+ H(OP_ADD_INT_LIT8), \
+ H(OP_RSUB_INT_LIT8), \
+ H(OP_MUL_INT_LIT8), \
+ H(OP_DIV_INT_LIT8), \
+ H(OP_REM_INT_LIT8), \
+ H(OP_AND_INT_LIT8), \
+ H(OP_OR_INT_LIT8), \
+ H(OP_XOR_INT_LIT8), \
+ /* e0..ef */ \
+ H(OP_SHL_INT_LIT8), \
+ H(OP_SHR_INT_LIT8), \
+ H(OP_USHR_INT_LIT8), \
+ H(OP_IGET_VOLATILE), \
+ H(OP_IPUT_VOLATILE), \
+ H(OP_SGET_VOLATILE), \
+ H(OP_SPUT_VOLATILE), \
+ H(OP_IGET_OBJECT_VOLATILE), \
+ H(OP_IGET_WIDE_VOLATILE), \
+ H(OP_IPUT_WIDE_VOLATILE), \
+ H(OP_SGET_WIDE_VOLATILE), \
+ H(OP_SPUT_WIDE_VOLATILE), \
+ H(OP_BREAKPOINT), \
+ H(OP_THROW_VERIFICATION_ERROR), \
+ H(OP_EXECUTE_INLINE), \
+ H(OP_EXECUTE_INLINE_RANGE), \
+ /* f0..ff */ \
+ H(OP_INVOKE_DIRECT_EMPTY), \
+ H(OP_UNUSED_F1), \
+ H(OP_IGET_QUICK), \
+ H(OP_IGET_WIDE_QUICK), \
+ H(OP_IGET_OBJECT_QUICK), \
+ H(OP_IPUT_QUICK), \
+ H(OP_IPUT_WIDE_QUICK), \
+ H(OP_IPUT_OBJECT_QUICK), \
+ H(OP_INVOKE_VIRTUAL_QUICK), \
+ H(OP_INVOKE_VIRTUAL_QUICK_RANGE), \
+ H(OP_INVOKE_SUPER_QUICK), \
+ H(OP_INVOKE_SUPER_QUICK_RANGE), \
+ H(OP_IPUT_OBJECT_VOLATILE), \
+ H(OP_SGET_OBJECT_VOLATILE), \
+ H(OP_SPUT_OBJECT_VOLATILE), \
+ H(OP_UNUSED_FF), \
+ };
+
+#endif /*_LIBDEX_OPCODE*/
diff --git a/libdex/OpCodeNames.c b/libdex/OpCodeNames.c
new file mode 100644
index 0000000..c182d4e
--- /dev/null
+++ b/libdex/OpCodeNames.c
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Table of Dalvik opcode names.
+ */
+#include "OpCodeNames.h"
+
+#include <assert.h>
+
+/*
+ * The following two lines work, but slashes and dashes both turn into
+ * underscores, and the strings are all upper case. The output is easier
+ * to read if we do the strings by hand (could probably write a
+ * post-processing function easily enough if maintenance becomes annoying).
+ */
+//#define H(_op) #_op
+//DEFINE_GOTO_TABLE(gOpNames)
+
+/*
+ * Dalvik opcode names.
+ */
+static const char* gOpNames[256] = {
+ /* 0x00 */
+ "nop",
+ "move",
+ "move/from16",
+ "move/16",
+ "move-wide",
+ "move-wide/from16",
+ "move-wide/16",
+ "move-object",
+ "move-object/from16",
+ "move-object/16",
+ "move-result",
+ "move-result-wide",
+ "move-result-object",
+ "move-exception",
+ "return-void",
+ "return",
+
+ /* 0x10 */
+ "return-wide",
+ "return-object",
+ "const/4",
+ "const/16",
+ "const",
+ "const/high16",
+ "const-wide/16",
+ "const-wide/32",
+ "const-wide",
+ "const-wide/high16",
+ "const-string",
+ "const-string/jumbo",
+ "const-class",
+ "monitor-enter",
+ "monitor-exit",
+ "check-cast",
+
+ /* 0x20 */
+ "instance-of",
+ "array-length",
+ "new-instance",
+ "new-array",
+ "filled-new-array",
+ "filled-new-array/range",
+ "fill-array-data",
+ "throw",
+ "goto",
+ "goto/16",
+ "goto/32",
+ "packed-switch",
+ "sparse-switch",
+ "cmpl-float",
+ "cmpg-float",
+ "cmpl-double",
+
+ /* 0x30 */
+ "cmpg-double",
+ "cmp-long",
+ "if-eq",
+ "if-ne",
+ "if-lt",
+ "if-ge",
+ "if-gt",
+ "if-le",
+ "if-eqz",
+ "if-nez",
+ "if-ltz",
+ "if-gez",
+ "if-gtz",
+ "if-lez",
+ "UNUSED",
+ "UNUSED",
+
+ /* 0x40 */
+ "UNUSED",
+ "UNUSED",
+ "UNUSED",
+ "UNUSED",
+ "aget",
+ "aget-wide",
+ "aget-object",
+ "aget-boolean",
+ "aget-byte",
+ "aget-char",
+ "aget-short",
+ "aput",
+ "aput-wide",
+ "aput-object",
+ "aput-boolean",
+ "aput-byte",
+
+ /* 0x50 */
+ "aput-char",
+ "aput-short",
+ "iget",
+ "iget-wide",
+ "iget-object",
+ "iget-boolean",
+ "iget-byte",
+ "iget-char",
+ "iget-short",
+ "iput",
+ "iput-wide",
+ "iput-object",
+ "iput-boolean",
+ "iput-byte",
+ "iput-char",
+ "iput-short",
+
+ /* 0x60 */
+ "sget",
+ "sget-wide",
+ "sget-object",
+ "sget-boolean",
+ "sget-byte",
+ "sget-char",
+ "sget-short",
+ "sput",
+ "sput-wide",
+ "sput-object",
+ "sput-boolean",
+ "sput-byte",
+ "sput-char",
+ "sput-short",
+ "invoke-virtual",
+ "invoke-super",
+
+ /* 0x70 */
+ "invoke-direct",
+ "invoke-static",
+ "invoke-interface",
+ "UNUSED",
+ "invoke-virtual/range",
+ "invoke-super/range",
+ "invoke-direct/range",
+ "invoke-static/range",
+ "invoke-interface/range",
+ "UNUSED",
+ "UNUSED",
+ "neg-int",
+ "not-int",
+ "neg-long",
+ "not-long",
+ "neg-float",
+
+ /* 0x80 */
+ "neg-double",
+ "int-to-long",
+ "int-to-float",
+ "int-to-double",
+ "long-to-int",
+ "long-to-float",
+ "long-to-double",
+ "float-to-int",
+ "float-to-long",
+ "float-to-double",
+ "double-to-int",
+ "double-to-long",
+ "double-to-float",
+ "int-to-byte",
+ "int-to-char",
+ "int-to-short",
+
+ /* 0x90 */
+ "add-int",
+ "sub-int",
+ "mul-int",
+ "div-int",
+ "rem-int",
+ "and-int",
+ "or-int",
+ "xor-int",
+ "shl-int",
+ "shr-int",
+ "ushr-int",
+ "add-long",
+ "sub-long",
+ "mul-long",
+ "div-long",
+ "rem-long",
+
+ /* 0xa0 */
+ "and-long",
+ "or-long",
+ "xor-long",
+ "shl-long",
+ "shr-long",
+ "ushr-long",
+ "add-float",
+ "sub-float",
+ "mul-float",
+ "div-float",
+ "rem-float",
+ "add-double",
+ "sub-double",
+ "mul-double",
+ "div-double",
+ "rem-double",
+
+ /* 0xb0 */
+ "add-int/2addr",
+ "sub-int/2addr",
+ "mul-int/2addr",
+ "div-int/2addr",
+ "rem-int/2addr",
+ "and-int/2addr",
+ "or-int/2addr",
+ "xor-int/2addr",
+ "shl-int/2addr",
+ "shr-int/2addr",
+ "ushr-int/2addr",
+ "add-long/2addr",
+ "sub-long/2addr",
+ "mul-long/2addr",
+ "div-long/2addr",
+ "rem-long/2addr",
+
+ /* 0xc0 */
+ "and-long/2addr",
+ "or-long/2addr",
+ "xor-long/2addr",
+ "shl-long/2addr",
+ "shr-long/2addr",
+ "ushr-long/2addr",
+ "add-float/2addr",
+ "sub-float/2addr",
+ "mul-float/2addr",
+ "div-float/2addr",
+ "rem-float/2addr",
+ "add-double/2addr",
+ "sub-double/2addr",
+ "mul-double/2addr",
+ "div-double/2addr",
+ "rem-double/2addr",
+
+ /* 0xd0 */
+ "add-int/lit16",
+ "rsub-int",
+ "mul-int/lit16",
+ "div-int/lit16",
+ "rem-int/lit16",
+ "and-int/lit16",
+ "or-int/lit16",
+ "xor-int/lit16",
+ "add-int/lit8",
+ "rsub-int/lit8",
+ "mul-int/lit8",
+ "div-int/lit8",
+ "rem-int/lit8",
+ "and-int/lit8",
+ "or-int/lit8",
+ "xor-int/lit8",
+
+ /* 0xe0 */
+ "shl-int/lit8",
+ "shr-int/lit8",
+ "ushr-int/lit8",
+ "+iget-volatile",
+ "+iput-volatile",
+ "+sget-volatile",
+ "+sput-volatile",
+ "+iget-object-volatile",
+ "+iget-wide-volatile",
+ "+iput-wide-volatile",
+ "+sget-wide-volatile",
+ "+sput-wide-volatile",
+ "^breakpoint", // does not appear in DEX files
+ "^throw-verification-error", // does not appear in DEX files
+ "+execute-inline",
+ "+execute-inline/range",
+
+ /* 0xf0 */
+ "+invoke-direct-empty",
+ "UNUSED",
+ "+iget-quick",
+ "+iget-wide-quick",
+ "+iget-object-quick",
+ "+iput-quick",
+ "+iput-wide-quick",
+ "+iput-object-quick",
+ "+invoke-virtual-quick",
+ "+invoke-virtual-quick/range",
+ "+invoke-super-quick",
+ "+invoke-super-quick/range",
+ "+iput-object-volatile",
+ "+sget-object-volatile",
+ "+sput-object-volatile",
+ "UNUSED",
+};
+
+/*
+ * Return the name of an opcode.
+ */
+const char* dexGetOpcodeName(OpCode op)
+{
+ assert(op >= 0 && op < kNumDalvikInstructions);
+ return gOpNames[op];
+}
diff --git a/libdex/OpCodeNames.h b/libdex/OpCodeNames.h
new file mode 100644
index 0000000..f81368d
--- /dev/null
+++ b/libdex/OpCodeNames.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik opcode names.
+ */
+#ifndef _LIBDEX_OPCODENAMES
+#define _LIBDEX_OPCODENAMES
+
+#include "OpCode.h"
+
+const char* dexGetOpcodeName(OpCode op);
+
+#endif /*_LIBDEX_OPCODENAMES*/
diff --git a/libdex/OptInvocation.c b/libdex/OptInvocation.c
new file mode 100644
index 0000000..df5f8d9
--- /dev/null
+++ b/libdex/OptInvocation.c
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Utility functions for managing an invocation of "dexopt".
+ */
+#include "vm/DalvikVersion.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <errno.h>
+
+#include "OptInvocation.h"
+#include "DexFile.h"
+
+static const char* kClassesDex = "classes.dex";
+
+
+/*
+ * Given the filename of a .jar or .dex file, construct the DEX file cache
+ * name.
+ *
+ * For a Jar, "subFileName" is the name of the entry (usually "classes.dex").
+ * For a DEX, it may be NULL.
+ *
+ * Returns a newly-allocated string, or NULL on failure.
+ */
+char* dexOptGenerateCacheFileName(const char* fileName, const char* subFileName)
+{
+ char nameBuf[512];
+ static const char kDexCachePath[] = "dalvik-cache";
+ char absoluteFile[sizeof(nameBuf)];
+ const size_t kBufLen = sizeof(nameBuf) - 1;
+ const char* dataRoot;
+ char* cp;
+
+ /*
+ * Get the absolute path of the Jar or DEX file.
+ */
+ absoluteFile[0] = '\0';
+ if (fileName[0] != '/') {
+ /*
+ * Generate the absolute path. This doesn't do everything it
+ * should, e.g. if filename is "./out/whatever" it doesn't crunch
+ * the leading "./" out, but it'll do.
+ */
+ if (getcwd(absoluteFile, kBufLen) == NULL) {
+ LOGE("Can't get CWD while opening jar file\n");
+ return NULL;
+ }
+ strncat(absoluteFile, "/", kBufLen);
+ }
+ strncat(absoluteFile, fileName, kBufLen);
+
+ /*
+ * Append the name of the Jar file entry, if any. This is not currently
+ * required, but will be if we start putting more than one DEX file
+ * in a Jar.
+ */
+ if (subFileName != NULL) {
+ strncat(absoluteFile, "/", kBufLen);
+ strncat(absoluteFile, subFileName, kBufLen);
+ }
+
+ /* Turn the path into a flat filename by replacing
+ * any slashes after the first one with '@' characters.
+ */
+ cp = absoluteFile + 1;
+ while (*cp != '\0') {
+ if (*cp == '/') {
+ *cp = '@';
+ }
+ cp++;
+ }
+
+ /* Build the name of the cache directory.
+ */
+ dataRoot = getenv("ANDROID_DATA");
+ if (dataRoot == NULL)
+ dataRoot = "/data";
+ snprintf(nameBuf, kBufLen, "%s/%s", dataRoot, kDexCachePath);
+
+ /* Tack on the file name for the actual cache file path.
+ */
+ strncat(nameBuf, absoluteFile, kBufLen);
+
+ LOGV("Cache file for '%s' '%s' is '%s'\n", fileName, subFileName, nameBuf);
+ return strdup(nameBuf);
+}
+
+/*
+ * Create a skeletal "opt" header in a new file. Most of the fields are
+ * initialized to garbage, but we fill in "dexOffset" so others can
+ * see how large the header is.
+ *
+ * "fd" must be positioned at the start of the file. On return, it will
+ * be positioned just past the header, and the place where the DEX data
+ * should go.
+ *
+ * Returns 0 on success, errno on failure.
+ */
+int dexOptCreateEmptyHeader(int fd)
+{
+ DexOptHeader optHdr;
+ ssize_t actual;
+
+ assert(lseek(fd, 0, SEEK_CUR) == 0);
+
+ /*
+ * The data is only expected to be readable on the current system, so
+ * we just write the structure. We do need the file offset to be 64-bit
+ * aligned to fulfill a DEX requirement.
+ */
+ assert((sizeof(optHdr) & 0x07) == 0);
+ memset(&optHdr, 0xff, sizeof(optHdr));
+ optHdr.dexOffset = sizeof(optHdr);
+ actual = write(fd, &optHdr, sizeof(optHdr));
+ if (actual != sizeof(optHdr)) {
+ int err = errno ? errno : -1;
+ LOGE("opt header write failed: %s", strerror(errno));
+ return errno;
+ }
+
+ return 0;
+}
diff --git a/libdex/OptInvocation.h b/libdex/OptInvocation.h
new file mode 100644
index 0000000..0352eb4
--- /dev/null
+++ b/libdex/OptInvocation.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Utility functions related to "dexopt".
+ */
+#ifndef _LIBDEX_OPTINVOCATION
+#define _LIBDEX_OPTINVOCATION
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Utility routines, used by the VM.
+ */
+char* dexOptGenerateCacheFileName(const char* fileName,
+ const char* subFileName);
+int dexOptCreateEmptyHeader(int fd);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /*_LIBDEX_OPTINVOCATION*/
diff --git a/libdex/SysUtil.c b/libdex/SysUtil.c
new file mode 100644
index 0000000..e2b1fff
--- /dev/null
+++ b/libdex/SysUtil.c
@@ -0,0 +1,408 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * System utilities.
+ */
+#include "DexFile.h"
+#include "SysUtil.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#ifdef HAVE_POSIX_FILEMAP
+# include <sys/mman.h>
+#endif
+#include <limits.h>
+#include <errno.h>
+
+#include <JNIHelp.h> // TEMP_FAILURE_RETRY may or may not be in unistd
+
+
+/*
+ * Create an anonymous shared memory segment large enough to hold "length"
+ * bytes. The actual segment may be larger because mmap() operates on
+ * page boundaries (usually 4K).
+ */
+static void* sysCreateAnonShmem(size_t length)
+{
+#ifdef HAVE_POSIX_FILEMAP
+ void* ptr;
+
+ ptr = mmap(NULL, length, PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANON, -1, 0);
+ if (ptr == MAP_FAILED) {
+ LOGW("mmap(%d, RW, SHARED|ANON) failed: %s\n", (int) length,
+ strerror(errno));
+ return NULL;
+ }
+
+ return ptr;
+#else
+ LOGE("sysCreateAnonShmem not implemented.\n");
+ return NULL;
+#endif
+}
+
+/*
+ * Create a private anonymous storage area.
+ */
+int sysCreatePrivateMap(size_t length, MemMapping* pMap)
+{
+ void* memPtr;
+
+ memPtr = sysCreateAnonShmem(length);
+ if (memPtr == NULL)
+ return -1;
+
+ pMap->addr = pMap->baseAddr = memPtr;
+ pMap->length = pMap->baseLength = length;
+ return 0;
+}
+
+/*
+ * Determine the current offset and remaining length of the open file.
+ */
+static int getFileStartAndLength(int fd, off_t *start_, size_t *length_)
+{
+ off_t start, end;
+ size_t length;
+
+ assert(start_ != NULL);
+ assert(length_ != NULL);
+
+ start = lseek(fd, 0L, SEEK_CUR);
+ end = lseek(fd, 0L, SEEK_END);
+ (void) lseek(fd, start, SEEK_SET);
+
+ if (start == (off_t) -1 || end == (off_t) -1) {
+ LOGE("could not determine length of file\n");
+ return -1;
+ }
+
+ length = end - start;
+ if (length == 0) {
+ LOGE("file is empty\n");
+ return -1;
+ }
+
+ *start_ = start;
+ *length_ = length;
+
+ return 0;
+}
+
+/*
+ * Pull the contents of a file into an new shared memory segment. We grab
+ * everything from fd's current offset on.
+ *
+ * We need to know the length ahead of time so we can allocate a segment
+ * of sufficient size.
+ */
+int sysLoadFileInShmem(int fd, MemMapping* pMap)
+{
+#ifdef HAVE_POSIX_FILEMAP
+ off_t start;
+ size_t length, actual;
+ void* memPtr;
+
+ assert(pMap != NULL);
+
+ if (getFileStartAndLength(fd, &start, &length) < 0)
+ return -1;
+
+ memPtr = sysCreateAnonShmem(length);
+ if (memPtr == NULL)
+ return -1;
+
+ actual = read(fd, memPtr, length);
+ if (actual != length) {
+ LOGE("only read %d of %d bytes\n", (int) actual, (int) length);
+ sysReleaseShmem(pMap);
+ return -1;
+ }
+
+ pMap->baseAddr = pMap->addr = memPtr;
+ pMap->baseLength = pMap->length = length;
+
+ return 0;
+#else
+ LOGE("sysLoadFileInShmem not implemented.\n");
+ return -1;
+#endif
+}
+
+#ifndef HAVE_POSIX_FILEMAP
+int sysFakeMapFile(int fd, MemMapping* pMap)
+{
+ /* No MMAP, just fake it by copying the bits.
+ For Win32 we could use MapViewOfFile if really necessary
+ (see libs/utils/FileMap.cpp).
+ */
+ off_t start;
+ size_t length;
+ void* memPtr;
+
+ assert(pMap != NULL);
+
+ if (getFileStartAndLength(fd, &start, &length) < 0)
+ return -1;
+
+ memPtr = malloc(length);
+ if (read(fd, memPtr, length) < 0) {
+ LOGW("read(fd=%d, start=%d, length=%d) failed: %s\n", (int) length,
+ fd, (int) start, strerror(errno));
+ return -1;
+ }
+
+ pMap->baseAddr = pMap->addr = memPtr;
+ pMap->baseLength = pMap->length = length;
+
+ return 0;
+}
+#endif
+
+/*
+ * Map a file (from fd's current offset) into a shared, read-only memory
+ * segment. The file offset must be a multiple of the system page size.
+ *
+ * On success, returns 0 and fills out "pMap". On failure, returns a nonzero
+ * value and does not disturb "pMap".
+ */
+int sysMapFileInShmemReadOnly(int fd, MemMapping* pMap)
+{
+#ifdef HAVE_POSIX_FILEMAP
+ off_t start;
+ size_t length;
+ void* memPtr;
+
+ assert(pMap != NULL);
+
+ if (getFileStartAndLength(fd, &start, &length) < 0)
+ return -1;
+
+ memPtr = mmap(NULL, length, PROT_READ, MAP_FILE | MAP_SHARED, fd, start);
+ if (memPtr == MAP_FAILED) {
+ LOGW("mmap(%d, RO, FILE|SHARED, %d, %d) failed: %s\n", (int) length,
+ fd, (int) start, strerror(errno));
+ return -1;
+ }
+
+ pMap->baseAddr = pMap->addr = memPtr;
+ pMap->baseLength = pMap->length = length;
+
+ return 0;
+#else
+ return sysFakeMapFile(fd, pMap);
+#endif
+}
+
+/*
+ * Map a file (from fd's current offset) into a private, read-write memory
+ * segment that will be marked read-only (a/k/a "writable read-only"). The
+ * file offset must be a multiple of the system page size.
+ *
+ * In some cases the mapping will be fully writable (e.g. for files on
+ * FAT filesystems).
+ *
+ * On success, returns 0 and fills out "pMap". On failure, returns a nonzero
+ * value and does not disturb "pMap".
+ */
+int sysMapFileInShmemWritableReadOnly(int fd, MemMapping* pMap)
+{
+#ifdef HAVE_POSIX_FILEMAP
+ off_t start;
+ size_t length;
+ void* memPtr;
+
+ assert(pMap != NULL);
+
+ if (getFileStartAndLength(fd, &start, &length) < 0)
+ return -1;
+
+ memPtr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_FILE | MAP_PRIVATE,
+ fd, start);
+ if (memPtr == MAP_FAILED) {
+ LOGW("mmap(%d, R/W, FILE|PRIVATE, %d, %d) failed: %s\n", (int) length,
+ fd, (int) start, strerror(errno));
+ return -1;
+ }
+ if (mprotect(memPtr, length, PROT_READ) < 0) {
+ /* this fails with EACCESS on FAT filesystems, e.g. /sdcard */
+ int err = errno;
+ LOGV("mprotect(%p, %d, PROT_READ) failed: %s\n",
+ memPtr, length, strerror(err));
+ LOGD("mprotect(RO) failed (%d), file will remain read-write\n", err);
+ }
+
+ pMap->baseAddr = pMap->addr = memPtr;
+ pMap->baseLength = pMap->length = length;
+
+ return 0;
+#else
+ return sysFakeMapFile(fd, pMap);
+#endif
+}
+
+/*
+ * Map part of a file into a shared, read-only memory segment. The "start"
+ * offset is absolute, not relative.
+ *
+ * On success, returns 0 and fills out "pMap". On failure, returns a nonzero
+ * value and does not disturb "pMap".
+ */
+int sysMapFileSegmentInShmem(int fd, off_t start, size_t length,
+ MemMapping* pMap)
+{
+#ifdef HAVE_POSIX_FILEMAP
+ size_t actualLength;
+ off_t actualStart;
+ int adjust;
+ void* memPtr;
+
+ assert(pMap != NULL);
+
+ /* adjust to be page-aligned */
+ adjust = start % SYSTEM_PAGE_SIZE;
+ actualStart = start - adjust;
+ actualLength = length + adjust;
+
+ memPtr = mmap(NULL, actualLength, PROT_READ, MAP_FILE | MAP_SHARED,
+ fd, actualStart);
+ if (memPtr == MAP_FAILED) {
+ LOGW("mmap(%d, R, FILE|SHARED, %d, %d) failed: %s\n",
+ (int) actualLength, fd, (int) actualStart, strerror(errno));
+ return -1;
+ }
+
+ pMap->baseAddr = memPtr;
+ pMap->baseLength = actualLength;
+ pMap->addr = (char*)memPtr + adjust;
+ pMap->length = length;
+
+ LOGVV("mmap seg (st=%d ln=%d): bp=%p bl=%d ad=%p ln=%d\n",
+ (int) start, (int) length,
+ pMap->baseAddr, (int) pMap->baseLength,
+ pMap->addr, (int) pMap->length);
+
+ return 0;
+#else
+ LOGE("sysMapFileSegmentInShmem not implemented.\n");
+ return -1;
+#endif
+}
+
+/*
+ * Change the access rights on one or more pages to read-only or read-write.
+ *
+ * Returns 0 on success.
+ */
+int sysChangeMapAccess(void* addr, size_t length, int wantReadWrite,
+ MemMapping* pMap)
+{
+#ifdef HAVE_POSIX_FILEMAP
+ /*
+ * Verify that "addr" is part of this mapping file.
+ */
+ if (addr < pMap->baseAddr ||
+ (u1*)addr >= (u1*)pMap->baseAddr + pMap->baseLength)
+ {
+ LOGE("Attempted to change %p; map is %p - %p\n",
+ addr, pMap->baseAddr, (u1*)pMap->baseAddr + pMap->baseLength);
+ return -1;
+ }
+
+ /*
+ * Align "addr" to a page boundary and adjust "length" appropriately.
+ * (The address must be page-aligned, the length doesn't need to be,
+ * but we do need to ensure we cover the same range.)
+ */
+ u1* alignAddr = (u1*) ((int) addr & ~(SYSTEM_PAGE_SIZE-1));
+ size_t alignLength = length + ((u1*) addr - alignAddr);
+
+ //LOGI("%p/%zd --> %p/%zd\n", addr, length, alignAddr, alignLength);
+ int prot = wantReadWrite ? (PROT_READ|PROT_WRITE) : (PROT_READ);
+ if (mprotect(alignAddr, alignLength, prot) != 0) {
+ int err = errno;
+ LOGV("mprotect (%p,%zd,%d) failed: %s\n",
+ alignAddr, alignLength, prot, strerror(errno));
+ return (errno != 0) ? errno : -1;
+ }
+#endif
+
+ /* for "fake" mapping, no need to do anything */
+ return 0;
+}
+
+/*
+ * Release a memory mapping.
+ */
+void sysReleaseShmem(MemMapping* pMap)
+{
+#ifdef HAVE_POSIX_FILEMAP
+ if (pMap->baseAddr == NULL && pMap->baseLength == 0)
+ return;
+
+ if (munmap(pMap->baseAddr, pMap->baseLength) < 0) {
+ LOGW("munmap(%p, %d) failed: %s\n",
+ pMap->baseAddr, (int)pMap->baseLength, strerror(errno));
+ } else {
+ LOGV("munmap(%p, %d) succeeded\n", pMap->baseAddr, pMap->baseLength);
+ pMap->baseAddr = NULL;
+ pMap->baseLength = 0;
+ }
+#else
+ /* Free the bits allocated by sysMapFileInShmem. */
+ if (pMap->baseAddr != NULL) {
+ free(pMap->baseAddr);
+ pMap->baseAddr = NULL;
+ }
+ pMap->baseLength = 0;
+#endif
+}
+
+/*
+ * Make a copy of a MemMapping.
+ */
+void sysCopyMap(MemMapping* dst, const MemMapping* src)
+{
+ memcpy(dst, src, sizeof(MemMapping));
+}
+
+/*
+ * Write until all bytes have been written.
+ *
+ * Returns 0 on success, or an errno value on failure.
+ */
+int sysWriteFully(int fd, const void* buf, size_t count, const char* logMsg)
+{
+ while (count != 0) {
+ ssize_t actual = TEMP_FAILURE_RETRY(write(fd, buf, count));
+ if (actual < 0) {
+ int err = errno;
+ LOGE("%s: write failed: %s\n", logMsg, strerror(err));
+ return err;
+ } else if (actual != (ssize_t) count) {
+ LOGD("%s: partial write (will retry): (%d of %zd)\n",
+ logMsg, (int) actual, count);
+ buf = (const void*) (((const u1*) buf) + actual);
+ }
+ count -= actual;
+ }
+
+ return 0;
+}
diff --git a/libdex/SysUtil.h b/libdex/SysUtil.h
new file mode 100644
index 0000000..01b4e1a
--- /dev/null
+++ b/libdex/SysUtil.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * System utilities.
+ */
+#ifndef _LIBDEX_SYSUTIL
+#define _LIBDEX_SYSUTIL
+
+#include <sys/types.h>
+
+/*
+ * System page size. Normally you're expected to get this from
+ * sysconf(_SC_PAGESIZE) or some system-specific define (usually PAGESIZE
+ * or PAGE_SIZE). If we use a simple #define the compiler can generate
+ * appropriate masks directly, so we define it here and verify it as the
+ * VM is starting up.
+ *
+ * Must be a power of 2.
+ */
+#define SYSTEM_PAGE_SIZE 4096
+
+/*
+ * Use this to keep track of mapped segments.
+ */
+typedef struct MemMapping {
+ void* addr; /* start of data */
+ size_t length; /* length of data */
+
+ void* baseAddr; /* page-aligned base address */
+ size_t baseLength; /* length of mapping */
+} MemMapping;
+
+/*
+ * Copy a map.
+ */
+void sysCopyMap(MemMapping* dst, const MemMapping* src);
+
+/*
+ * Load a file into a new shared memory segment. All data from the current
+ * offset to the end of the file is pulled in.
+ *
+ * The segment is read-write, allowing VM fixups. (It should be modified
+ * to support .gz/.zip compressed data.)
+ *
+ * On success, "pMap" is filled in, and zero is returned.
+ */
+int sysLoadFileInShmem(int fd, MemMapping* pMap);
+
+/*
+ * Map a file (from fd's current offset) into a shared,
+ * read-only memory segment.
+ *
+ * On success, "pMap" is filled in, and zero is returned.
+ */
+int sysMapFileInShmemReadOnly(int fd, MemMapping* pMap);
+
+/*
+ * Map a file (from fd's current offset) into a shared, read-only memory
+ * segment that can be made writable. (In some cases, such as when
+ * mapping a file on a FAT filesystem, the result may be fully writable.)
+ *
+ * On success, "pMap" is filled in, and zero is returned.
+ */
+int sysMapFileInShmemWritableReadOnly(int fd, MemMapping* pMap);
+
+/*
+ * Like sysMapFileInShmemReadOnly, but on only part of a file.
+ */
+int sysMapFileSegmentInShmem(int fd, off_t start, size_t length,
+ MemMapping* pMap);
+
+/*
+ * Create a private anonymous mapping, useful for large allocations.
+ *
+ * On success, "pMap" is filled in, and zero is returned.
+ */
+int sysCreatePrivateMap(size_t length, MemMapping* pMap);
+
+/*
+ * Change the access rights on one or more pages. If "wantReadWrite" is
+ * zero, the pages will be made read-only; otherwise they will be read-write.
+ *
+ * Returns 0 on success.
+ */
+int sysChangeMapAccess(void* addr, size_t length, int wantReadWrite,
+ MemMapping* pmap);
+
+/*
+ * Release the pages associated with a shared memory segment.
+ *
+ * This does not free "pMap"; it just releases the memory.
+ */
+void sysReleaseShmem(MemMapping* pMap);
+
+/*
+ * Write until all bytes have been written.
+ *
+ * Returns 0 on success, or an errno value on failure.
+ */
+int sysWriteFully(int fd, const void* buf, size_t count, const char* logMsg);
+
+#endif /*_DALVIK_SYSUTIL*/
diff --git a/libdex/ZipArchive.c b/libdex/ZipArchive.c
new file mode 100644
index 0000000..7feb417
--- /dev/null
+++ b/libdex/ZipArchive.c
@@ -0,0 +1,750 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Read-only access to Zip archives, with minimal heap allocation.
+ */
+#include "ZipArchive.h"
+
+#include <zlib.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <JNIHelp.h> // TEMP_FAILURE_RETRY may or may not be in unistd
+
+
+/*
+ * Zip file constants.
+ */
+#define kEOCDSignature 0x06054b50
+#define kEOCDLen 22
+#define kEOCDNumEntries 8 // offset to #of entries in file
+#define kEOCDSize 12 // size of the central directory
+#define kEOCDFileOffset 16 // offset to central directory
+
+#define kMaxCommentLen 65535 // longest possible in ushort
+#define kMaxEOCDSearch (kMaxCommentLen + kEOCDLen)
+
+#define kLFHSignature 0x04034b50
+#define kLFHLen 30 // excluding variable-len fields
+#define kLFHNameLen 26 // offset to filename length
+#define kLFHExtraLen 28 // offset to extra length
+
+#define kCDESignature 0x02014b50
+#define kCDELen 46 // excluding variable-len fields
+#define kCDEMethod 10 // offset to compression method
+#define kCDEModWhen 12 // offset to modification timestamp
+#define kCDECRC 16 // offset to entry CRC
+#define kCDECompLen 20 // offset to compressed length
+#define kCDEUncompLen 24 // offset to uncompressed length
+#define kCDENameLen 28 // offset to filename length
+#define kCDEExtraLen 30 // offset to extra length
+#define kCDECommentLen 32 // offset to comment length
+#define kCDELocalOffset 42 // offset to local hdr
+
+/*
+ * The values we return for ZipEntry use 0 as an invalid value, so we
+ * want to adjust the hash table index by a fixed amount. Using a large
+ * value helps insure that people don't mix & match arguments, e.g. with
+ * entry indices.
+ */
+#define kZipEntryAdj 10000
+
+/*
+ * Convert a ZipEntry to a hash table index, verifying that it's in a
+ * valid range.
+ */
+static int entryToIndex(const ZipArchive* pArchive, const ZipEntry entry)
+{
+ long ent = ((long) entry) - kZipEntryAdj;
+ if (ent < 0 || ent >= pArchive->mHashTableSize ||
+ pArchive->mHashTable[ent].name == NULL)
+ {
+ LOGW("Zip: invalid ZipEntry %p (%ld)\n", entry, ent);
+ return -1;
+ }
+ return ent;
+}
+
+/*
+ * Simple string hash function for non-null-terminated strings.
+ */
+static unsigned int computeHash(const char* str, int len)
+{
+ unsigned int hash = 0;
+
+ while (len--)
+ hash = hash * 31 + *str++;
+
+ return hash;
+}
+
+/*
+ * Add a new entry to the hash table.
+ */
+static void addToHash(ZipArchive* pArchive, const char* str, int strLen,
+ unsigned int hash)
+{
+ const int hashTableSize = pArchive->mHashTableSize;
+ int ent = hash & (hashTableSize - 1);
+
+ /*
+ * We over-allocated the table, so we're guaranteed to find an empty slot.
+ */
+ while (pArchive->mHashTable[ent].name != NULL)
+ ent = (ent + 1) & (hashTableSize-1);
+
+ pArchive->mHashTable[ent].name = str;
+ pArchive->mHashTable[ent].nameLen = strLen;
+}
+
+/*
+ * Get 2 little-endian bytes.
+ */
+static u2 get2LE(unsigned char const* pSrc)
+{
+ return pSrc[0] | (pSrc[1] << 8);
+}
+
+/*
+ * Get 4 little-endian bytes.
+ */
+static u4 get4LE(unsigned char const* pSrc)
+{
+ u4 result;
+
+ result = pSrc[0];
+ result |= pSrc[1] << 8;
+ result |= pSrc[2] << 16;
+ result |= pSrc[3] << 24;
+
+ return result;
+}
+
+/*
+ * Find the zip Central Directory and memory-map it.
+ *
+ * On success, returns 0 after populating fields from the EOCD area:
+ * mDirectoryOffset
+ * mDirectoryMap
+ * mNumEntries
+ */
+static int mapCentralDirectory(int fd, const char* debugFileName,
+ ZipArchive* pArchive)
+{
+ u1* scanBuf = NULL;
+ int result = -1;
+
+ /*
+ * Get and test file length.
+ */
+ off_t fileLength = lseek(fd, 0, SEEK_END);
+ if (fileLength < kEOCDLen) {
+ LOGV("Zip: length %ld is too small to be zip\n", (long) fileLength);
+ goto bail;
+ }
+
+ /*
+ * Perform the traditional EOCD snipe hunt.
+ *
+ * We're searching for the End of Central Directory magic number,
+ * which appears at the start of the EOCD block. It's followed by
+ * 18 bytes of EOCD stuff and up to 64KB of archive comment. We
+ * need to read the last part of the file into a buffer, dig through
+ * it to find the magic number, parse some values out, and use those
+ * to determine the extent of the CD.
+ *
+ * We start by pulling in the last part of the file.
+ */
+ size_t readAmount = kMaxEOCDSearch;
+ if (readAmount > (size_t) fileLength)
+ readAmount = fileLength;
+ off_t searchStart = fileLength - readAmount;
+
+ scanBuf = (u1*) malloc(readAmount);
+ if (lseek(fd, searchStart, SEEK_SET) != searchStart) {
+ LOGW("Zip: seek %ld failed: %s\n", (long) searchStart, strerror(errno));
+ goto bail;
+ }
+ ssize_t actual = TEMP_FAILURE_RETRY(read(fd, scanBuf, readAmount));
+ if (actual != (ssize_t) readAmount) {
+ LOGW("Zip: read %zd failed: %s\n", readAmount, strerror(errno));
+ goto bail;
+ }
+
+ /*
+ * Scan backward for the EOCD magic. In an archive without a trailing
+ * comment, we'll find it on the first try. (We may want to consider
+ * doing an initial minimal read; if we don't find it, retry with a
+ * second read as above.)
+ */
+ int i;
+ for (i = readAmount - kEOCDLen; i >= 0; i--) {
+ if (scanBuf[i] == 0x50 && get4LE(&scanBuf[i]) == kEOCDSignature) {
+ LOGV("+++ Found EOCD at buf+%d\n", i);
+ break;
+ }
+ }
+ if (i < 0) {
+ LOGD("Zip: EOCD not found, %s is not zip\n", debugFileName);
+ goto bail;
+ }
+
+ off_t eocdOffset = searchStart + i;
+ const u1* eocdPtr = scanBuf + i;
+
+ assert(eocdOffset < fileLength);
+
+ /*
+ * Grab the CD offset and size, and the number of entries in the
+ * archive. Verify that they look reasonable.
+ */
+ u4 numEntries = get2LE(eocdPtr + kEOCDNumEntries);
+ u4 dirSize = get4LE(eocdPtr + kEOCDSize);
+ u4 dirOffset = get4LE(eocdPtr + kEOCDFileOffset);
+
+ if ((long long) dirOffset + (long long) dirSize > (long long) eocdOffset) {
+ LOGW("Zip: bad offsets (dir %ld, size %u, eocd %ld)\n",
+ (long) dirOffset, dirSize, (long) eocdOffset);
+ goto bail;
+ }
+ if (numEntries == 0) {
+ LOGW("Zip: empty archive?\n");
+ goto bail;
+ }
+
+ LOGV("+++ numEntries=%d dirSize=%d dirOffset=%d\n",
+ numEntries, dirSize, dirOffset);
+
+ /*
+ * It all looks good. Create a mapping for the CD, and set the fields
+ * in pArchive.
+ */
+ if (sysMapFileSegmentInShmem(fd, dirOffset, dirSize,
+ &pArchive->mDirectoryMap) != 0)
+ {
+ LOGW("Zip: cd map failed\n");
+ goto bail;
+ }
+
+ pArchive->mNumEntries = numEntries;
+ pArchive->mDirectoryOffset = dirOffset;
+
+ result = 0;
+
+bail:
+ free(scanBuf);
+ return result;
+}
+
+/*
+ * Parses the Zip archive's Central Directory. Allocates and populates the
+ * hash table.
+ *
+ * Returns 0 on success.
+ */
+static int parseZipArchive(ZipArchive* pArchive)
+{
+ int result = -1;
+ const u1* cdPtr = (const u1*)pArchive->mDirectoryMap.addr;
+ size_t cdLength = pArchive->mDirectoryMap.length;
+ int numEntries = pArchive->mNumEntries;
+
+ /*
+ * Create hash table. We have a minimum 75% load factor, possibly as
+ * low as 50% after we round off to a power of 2. There must be at
+ * least one unused entry to avoid an infinite loop during creation.
+ */
+ pArchive->mHashTableSize = dexRoundUpPower2(1 + (numEntries * 4) / 3);
+ pArchive->mHashTable = (ZipHashEntry*)
+ calloc(pArchive->mHashTableSize, sizeof(ZipHashEntry));
+
+ /*
+ * Walk through the central directory, adding entries to the hash
+ * table and verifying values.
+ */
+ const u1* ptr = cdPtr;
+ int i;
+ for (i = 0; i < numEntries; i++) {
+ if (get4LE(ptr) != kCDESignature) {
+ LOGW("Zip: missed a central dir sig (at %d)\n", i);
+ goto bail;
+ }
+ if (ptr + kCDELen > cdPtr + cdLength) {
+ LOGW("Zip: ran off the end (at %d)\n", i);
+ goto bail;
+ }
+
+ long localHdrOffset = (long) get4LE(ptr + kCDELocalOffset);
+ if (localHdrOffset >= pArchive->mDirectoryOffset) {
+ LOGW("Zip: bad LFH offset %ld at entry %d\n", localHdrOffset, i);
+ goto bail;
+ }
+
+ unsigned int fileNameLen, extraLen, commentLen, hash;
+ fileNameLen = get2LE(ptr + kCDENameLen);
+ extraLen = get2LE(ptr + kCDEExtraLen);
+ commentLen = get2LE(ptr + kCDECommentLen);
+
+ /* add the CDE filename to the hash table */
+ hash = computeHash((const char*)ptr + kCDELen, fileNameLen);
+ addToHash(pArchive, (const char*)ptr + kCDELen, fileNameLen, hash);
+
+ ptr += kCDELen + fileNameLen + extraLen + commentLen;
+ if ((size_t)(ptr - cdPtr) > cdLength) {
+ LOGW("Zip: bad CD advance (%d vs %zd) at entry %d\n",
+ (int) (ptr - cdPtr), cdLength, i);
+ goto bail;
+ }
+ }
+ LOGV("+++ zip good scan %d entries\n", numEntries);
+
+ result = 0;
+
+bail:
+ return result;
+}
+
+/*
+ * Open the specified file read-only. We examine the contents and verify
+ * that it appears to be a valid zip file.
+ *
+ * This will be called on non-Zip files, especially during VM startup, so
+ * we don't want to be too noisy about certain types of failure. (Do
+ * we want a "quiet" flag?)
+ *
+ * On success, we fill out the contents of "pArchive" and return 0. On
+ * failure we return the errno value.
+ */
+int dexZipOpenArchive(const char* fileName, ZipArchive* pArchive)
+{
+ int fd, err;
+
+ LOGV("Opening as zip '%s' %p\n", fileName, pArchive);
+
+ memset(pArchive, 0, sizeof(ZipArchive));
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+ fd = open(fileName, O_RDONLY | O_BINARY, 0);
+ if (fd < 0) {
+ err = errno ? errno : -1;
+ LOGV("Unable to open '%s': %s\n", fileName, strerror(err));
+ return err;
+ }
+
+ return dexZipPrepArchive(fd, fileName, pArchive);
+}
+
+/*
+ * Prepare to access a ZipArchive through an open file descriptor.
+ *
+ * On success, we fill out the contents of "pArchive" and return 0.
+ */
+int dexZipPrepArchive(int fd, const char* debugFileName, ZipArchive* pArchive)
+{
+ int result = -1;
+
+ memset(pArchive, 0, sizeof(*pArchive));
+ pArchive->mFd = fd;
+
+ if (mapCentralDirectory(fd, debugFileName, pArchive) != 0)
+ goto bail;
+
+ if (parseZipArchive(pArchive) != 0) {
+ LOGV("Zip: parsing '%s' failed\n", debugFileName);
+ goto bail;
+ }
+
+ /* success */
+ result = 0;
+
+bail:
+ if (result != 0)
+ dexZipCloseArchive(pArchive);
+ return result;
+}
+
+
+/*
+ * Close a ZipArchive, closing the file and freeing the contents.
+ *
+ * NOTE: the ZipArchive may not have been fully created.
+ */
+void dexZipCloseArchive(ZipArchive* pArchive)
+{
+ LOGV("Closing archive %p\n", pArchive);
+
+ if (pArchive->mFd >= 0)
+ close(pArchive->mFd);
+
+ sysReleaseShmem(&pArchive->mDirectoryMap);
+
+ free(pArchive->mHashTable);
+
+ /* ensure nobody tries to use the ZipArchive after it's closed */
+ pArchive->mDirectoryOffset = -1;
+ pArchive->mFd = -1;
+ pArchive->mNumEntries = -1;
+ pArchive->mHashTableSize = -1;
+ pArchive->mHashTable = NULL;
+}
+
+
+/*
+ * Find a matching entry.
+ *
+ * Returns 0 if not found.
+ */
+ZipEntry dexZipFindEntry(const ZipArchive* pArchive, const char* entryName)
+{
+ int nameLen = strlen(entryName);
+ unsigned int hash = computeHash(entryName, nameLen);
+ const int hashTableSize = pArchive->mHashTableSize;
+ int ent = hash & (hashTableSize-1);
+
+ while (pArchive->mHashTable[ent].name != NULL) {
+ if (pArchive->mHashTable[ent].nameLen == nameLen &&
+ memcmp(pArchive->mHashTable[ent].name, entryName, nameLen) == 0)
+ {
+ /* match */
+ return (ZipEntry)(long)(ent + kZipEntryAdj);
+ }
+
+ ent = (ent + 1) & (hashTableSize-1);
+ }
+
+ return NULL;
+}
+
+#if 0
+/*
+ * Find the Nth entry.
+ *
+ * This currently involves walking through the sparse hash table, counting
+ * non-empty entries. If we need to speed this up we can either allocate
+ * a parallel lookup table or (perhaps better) provide an iterator interface.
+ */
+ZipEntry findEntryByIndex(ZipArchive* pArchive, int idx)
+{
+ if (idx < 0 || idx >= pArchive->mNumEntries) {
+ LOGW("Invalid index %d\n", idx);
+ return NULL;
+ }
+
+ int ent;
+ for (ent = 0; ent < pArchive->mHashTableSize; ent++) {
+ if (pArchive->mHashTable[ent].name != NULL) {
+ if (idx-- == 0)
+ return (ZipEntry) (ent + kZipEntryAdj);
+ }
+ }
+
+ return NULL;
+}
+#endif
+
+/*
+ * Get the useful fields from the zip entry.
+ *
+ * Returns non-zero if the contents of the fields (particularly the data
+ * offset) appear to be bogus.
+ */
+int dexZipGetEntryInfo(const ZipArchive* pArchive, ZipEntry entry,
+ int* pMethod, size_t* pUncompLen, size_t* pCompLen, off_t* pOffset,
+ long* pModWhen, long* pCrc32)
+{
+ int ent = entryToIndex(pArchive, entry);
+ if (ent < 0)
+ return -1;
+
+ /*
+ * Recover the start of the central directory entry from the filename
+ * pointer. The filename is the first entry past the fixed-size data,
+ * so we can just subtract back from that.
+ */
+ const unsigned char* basePtr = (const unsigned char*)
+ pArchive->mDirectoryMap.addr;
+ const unsigned char* ptr = (const unsigned char*)
+ pArchive->mHashTable[ent].name;
+ off_t cdOffset = pArchive->mDirectoryOffset;
+
+ ptr -= kCDELen;
+
+ int method = get2LE(ptr + kCDEMethod);
+ if (pMethod != NULL)
+ *pMethod = method;
+
+ if (pModWhen != NULL)
+ *pModWhen = get4LE(ptr + kCDEModWhen);
+ if (pCrc32 != NULL)
+ *pCrc32 = get4LE(ptr + kCDECRC);
+
+ size_t compLen = get4LE(ptr + kCDECompLen);
+ if (pCompLen != NULL)
+ *pCompLen = compLen;
+ size_t uncompLen = get4LE(ptr + kCDEUncompLen);
+ if (pUncompLen != NULL)
+ *pUncompLen = uncompLen;
+
+ /*
+ * If requested, determine the offset of the start of the data. All we
+ * have is the offset to the Local File Header, which is variable size,
+ * so we have to read the contents of the struct to figure out where
+ * the actual data starts.
+ *
+ * We also need to make sure that the lengths are not so large that
+ * somebody trying to map the compressed or uncompressed data runs
+ * off the end of the mapped region.
+ *
+ * Note we don't verify compLen/uncompLen if they don't request the
+ * dataOffset, because dataOffset is expensive to determine. However,
+ * if they don't have the file offset, they're not likely to be doing
+ * anything with the contents.
+ */
+ if (pOffset != NULL) {
+ long localHdrOffset = (long) get4LE(ptr + kCDELocalOffset);
+ if (localHdrOffset + kLFHLen >= cdOffset) {
+ LOGW("Zip: bad local hdr offset in zip\n");
+ return -1;
+ }
+
+ u1 lfhBuf[kLFHLen];
+ if (lseek(pArchive->mFd, localHdrOffset, SEEK_SET) != localHdrOffset) {
+ LOGW("Zip: failed seeking to lfh at offset %ld\n", localHdrOffset);
+ return -1;
+ }
+ ssize_t actual =
+ TEMP_FAILURE_RETRY(read(pArchive->mFd, lfhBuf, sizeof(lfhBuf)));
+ if (actual != sizeof(lfhBuf)) {
+ LOGW("Zip: failed reading lfh from offset %ld\n", localHdrOffset);
+ return -1;
+ }
+
+ if (get4LE(lfhBuf) != kLFHSignature) {
+ LOGW("Zip: didn't find signature at start of lfh, offset=%ld\n",
+ localHdrOffset);
+ return -1;
+ }
+
+ off_t dataOffset = localHdrOffset + kLFHLen
+ + get2LE(lfhBuf + kLFHNameLen) + get2LE(lfhBuf + kLFHExtraLen);
+ if (dataOffset >= cdOffset) {
+ LOGW("Zip: bad data offset %ld in zip\n", (long) dataOffset);
+ return -1;
+ }
+
+ /* check lengths */
+ if ((off_t)(dataOffset + compLen) > cdOffset) {
+ LOGW("Zip: bad compressed length in zip (%ld + %zd > %ld)\n",
+ (long) dataOffset, compLen, (long) cdOffset);
+ return -1;
+ }
+
+ if (method == kCompressStored &&
+ (off_t)(dataOffset + uncompLen) > cdOffset)
+ {
+ LOGW("Zip: bad uncompressed length in zip (%ld + %zd > %ld)\n",
+ (long) dataOffset, uncompLen, (long) cdOffset);
+ return -1;
+ }
+
+ *pOffset = dataOffset;
+ }
+ return 0;
+}
+
+/*
+ * Uncompress "deflate" data from the archive's file to an open file
+ * descriptor.
+ */
+static int inflateToFile(int inFd, int outFd, size_t uncompLen, size_t compLen)
+{
+ int result = -1;
+ const size_t kBufSize = 32768;
+ unsigned char* readBuf = (unsigned char*) malloc(kBufSize);
+ unsigned char* writeBuf = (unsigned char*) malloc(kBufSize);
+ z_stream zstream;
+ int zerr;
+
+ if (readBuf == NULL || writeBuf == NULL)
+ goto bail;
+
+ /*
+ * Initialize the zlib stream struct.
+ */
+ memset(&zstream, 0, sizeof(zstream));
+ zstream.zalloc = Z_NULL;
+ zstream.zfree = Z_NULL;
+ zstream.opaque = Z_NULL;
+ zstream.next_in = NULL;
+ zstream.avail_in = 0;
+ zstream.next_out = (Bytef*) writeBuf;
+ zstream.avail_out = kBufSize;
+ zstream.data_type = Z_UNKNOWN;
+
+ /*
+ * Use the undocumented "negative window bits" feature to tell zlib
+ * that there's no zlib header waiting for it.
+ */
+ zerr = inflateInit2(&zstream, -MAX_WBITS);
+ if (zerr != Z_OK) {
+ if (zerr == Z_VERSION_ERROR) {
+ LOGE("Installed zlib is not compatible with linked version (%s)\n",
+ ZLIB_VERSION);
+ } else {
+ LOGW("Call to inflateInit2 failed (zerr=%d)\n", zerr);
+ }
+ goto bail;
+ }
+
+ /*
+ * Loop while we have more to do.
+ */
+ do {
+ /* read as much as we can */
+ if (zstream.avail_in == 0) {
+ size_t getSize = (compLen > kBufSize) ? kBufSize : compLen;
+
+ ssize_t actual = TEMP_FAILURE_RETRY(read(inFd, readBuf, getSize));
+ if (actual != (ssize_t) getSize) {
+ LOGW("Zip: inflate read failed (%d vs %zd)\n",
+ (int)actual, getSize);
+ goto z_bail;
+ }
+
+ compLen -= getSize;
+
+ zstream.next_in = readBuf;
+ zstream.avail_in = getSize;
+ }
+
+ /* uncompress the data */
+ zerr = inflate(&zstream, Z_NO_FLUSH);
+ if (zerr != Z_OK && zerr != Z_STREAM_END) {
+ LOGW("Zip: inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n",
+ zerr, zstream.next_in, zstream.avail_in,
+ zstream.next_out, zstream.avail_out);
+ goto z_bail;
+ }
+
+ /* write when we're full or when we're done */
+ if (zstream.avail_out == 0 ||
+ (zerr == Z_STREAM_END && zstream.avail_out != kBufSize))
+ {
+ size_t writeSize = zstream.next_out - writeBuf;
+ if (sysWriteFully(outFd, writeBuf, writeSize, "Zip inflate") != 0)
+ goto z_bail;
+
+ zstream.next_out = writeBuf;
+ zstream.avail_out = kBufSize;
+ }
+ } while (zerr == Z_OK);
+
+ assert(zerr == Z_STREAM_END); /* other errors should've been caught */
+
+ /* paranoia */
+ if (zstream.total_out != uncompLen) {
+ LOGW("Zip: size mismatch on inflated file (%ld vs %zd)\n",
+ zstream.total_out, uncompLen);
+ goto z_bail;
+ }
+
+ result = 0;
+
+z_bail:
+ inflateEnd(&zstream); /* free up any allocated structures */
+
+bail:
+ free(readBuf);
+ free(writeBuf);
+ return result;
+}
+
+/*
+ * Copy bytes from input to output.
+ */
+static int copyFileToFile(int inFd, int outFd, size_t uncompLen)
+{
+ const size_t kBufSize = 32768;
+ unsigned char buf[kBufSize];
+
+ while (uncompLen != 0) {
+ size_t getSize = (uncompLen > kBufSize) ? kBufSize : uncompLen;
+
+ ssize_t actual = TEMP_FAILURE_RETRY(read(inFd, buf, getSize));
+ if (actual != (ssize_t) getSize) {
+ LOGW("Zip: copy read failed (%d vs %zd)\n", (int)actual, getSize);
+ return -1;
+ }
+
+ if (sysWriteFully(outFd, buf, getSize, "Zip copy") != 0)
+ return -1;
+
+ uncompLen -= getSize;
+ }
+
+ return 0;
+}
+
+/*
+ * Uncompress an entry, in its entirety, to an open file descriptor.
+ *
+ * TODO: this doesn't verify the data's CRC, but probably should (especially
+ * for uncompressed data).
+ */
+int dexZipExtractEntryToFile(const ZipArchive* pArchive,
+ const ZipEntry entry, int fd)
+{
+ int result = -1;
+ int ent = entryToIndex(pArchive, entry);
+ if (ent < 0) {
+ LOGW("Zip: extract can't find entry %p\n", entry);
+ goto bail;
+ }
+
+ int method;
+ size_t uncompLen, compLen;
+ off_t dataOffset;
+
+ if (dexZipGetEntryInfo(pArchive, entry, &method, &uncompLen, &compLen,
+ &dataOffset, NULL, NULL) != 0)
+ {
+ goto bail;
+ }
+ if (lseek(pArchive->mFd, dataOffset, SEEK_SET) != dataOffset) {
+ LOGW("Zip: lseek to data at %ld failed\n", (long) dataOffset);
+ goto bail;
+ }
+
+ if (method == kCompressStored) {
+ if (copyFileToFile(pArchive->mFd, fd, uncompLen) != 0)
+ goto bail;
+ } else {
+ if (inflateToFile(pArchive->mFd, fd, uncompLen, compLen) != 0)
+ goto bail;
+ }
+
+ result = 0;
+
+bail:
+ return result;
+}
diff --git a/libdex/ZipArchive.h b/libdex/ZipArchive.h
new file mode 100644
index 0000000..bf4edf9
--- /dev/null
+++ b/libdex/ZipArchive.h
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Read-only access to Zip archives, with minimal heap allocation.
+ */
+#ifndef _LIBDEX_ZIPARCHIVE
+#define _LIBDEX_ZIPARCHIVE
+
+#include "SysUtil.h"
+#include "DexFile.h" // need DEX_INLINE
+
+
+/*
+ * Trivial typedef to ensure that ZipEntry is not treated as a simple
+ * integer. We use NULL to indicate an invalid value.
+ */
+typedef void* ZipEntry;
+
+/*
+ * One entry in the hash table.
+ */
+typedef struct ZipHashEntry {
+ const char* name;
+ unsigned short nameLen;
+ //unsigned int hash;
+} ZipHashEntry;
+
+/*
+ * Read-only Zip archive.
+ *
+ * We want "open" and "find entry by name" to be fast operations, and
+ * we want to use as little memory as possible. We memory-map the zip
+ * central directory, and load a hash table with pointers to the filenames
+ * (which aren't null-terminated). The other fields are at a fixed offset
+ * from the filename, so we don't need to extract those (but we do need
+ * to byte-read and endian-swap them every time we want them).
+ *
+ * It's possible that somebody has handed us a massive (~1GB) zip archive,
+ * so we can't expect to mmap the entire file.
+ *
+ * To speed comparisons when doing a lookup by name, we could make the mapping
+ * "private" (copy-on-write) and null-terminate the filenames after verifying
+ * the record structure. However, this requires a private mapping of
+ * every page that the Central Directory touches. Easier to tuck a copy
+ * of the string length into the hash table entry.
+ */
+typedef struct ZipArchive {
+ /* open Zip archive */
+ int mFd;
+
+ /* mapped central directory area */
+ off_t mDirectoryOffset;
+ MemMapping mDirectoryMap;
+
+ /* number of entries in the Zip archive */
+ int mNumEntries;
+
+ /*
+ * We know how many entries are in the Zip archive, so we can have a
+ * fixed-size hash table. We probe on collisions.
+ */
+ int mHashTableSize;
+ ZipHashEntry* mHashTable;
+} ZipArchive;
+
+/* Zip compression methods we support */
+enum {
+ kCompressStored = 0, // no compression
+ kCompressDeflated = 8, // standard deflate
+};
+
+
+/*
+ * Open a Zip archive.
+ *
+ * On success, returns 0 and populates "pArchive". Returns nonzero errno
+ * value on failure.
+ */
+int dexZipOpenArchive(const char* fileName, ZipArchive* pArchive);
+
+/*
+ * Like dexZipOpenArchive, but takes a file descriptor open for reading
+ * at the start of the file. The descriptor must be mappable (this does
+ * not allow access to a stream).
+ *
+ * "debugFileName" will appear in error messages, but is not otherwise used.
+ */
+int dexZipPrepArchive(int fd, const char* debugFileName, ZipArchive* pArchive);
+
+/*
+ * Close archive, releasing resources associated with it.
+ *
+ * Depending on the implementation this could unmap pages used by classes
+ * stored in a Jar. This should only be done after unloading classes.
+ */
+void dexZipCloseArchive(ZipArchive* pArchive);
+
+/*
+ * Return the archive's file descriptor.
+ */
+DEX_INLINE int dexZipGetArchiveFd(const ZipArchive* pArchive) {
+ return pArchive->mFd;
+}
+
+/*
+ * Find an entry in the Zip archive, by name. Returns NULL if the entry
+ * was not found.
+ */
+ZipEntry dexZipFindEntry(const ZipArchive* pArchive,
+ const char* entryName);
+
+/*
+ * Retrieve one or more of the "interesting" fields. Non-NULL pointers
+ * are filled in.
+ *
+ * Returns 0 on success.
+ */
+int dexZipGetEntryInfo(const ZipArchive* pArchive, ZipEntry entry,
+ int* pMethod, size_t* pUncompLen, size_t* pCompLen, off_t* pOffset,
+ long* pModWhen, long* pCrc32);
+
+/*
+ * Simple accessors.
+ */
+DEX_INLINE long dexGetZipEntryOffset(const ZipArchive* pArchive,
+ const ZipEntry entry)
+{
+ off_t val = 0;
+ dexZipGetEntryInfo(pArchive, entry, NULL, NULL, NULL, &val, NULL, NULL);
+ return (long) val;
+}
+DEX_INLINE size_t dexGetZipEntryUncompLen(const ZipArchive* pArchive,
+ const ZipEntry entry)
+{
+ size_t val = 0;
+ dexZipGetEntryInfo(pArchive, entry, NULL, &val, NULL, NULL, NULL, NULL);
+ return val;
+}
+DEX_INLINE long dexGetZipEntryModTime(const ZipArchive* pArchive,
+ const ZipEntry entry)
+{
+ long val = 0;
+ dexZipGetEntryInfo(pArchive, entry, NULL, NULL, NULL, NULL, &val, NULL);
+ return val;
+}
+DEX_INLINE long dexGetZipEntryCrc32(const ZipArchive* pArchive,
+ const ZipEntry entry)
+{
+ long val = 0;
+ dexZipGetEntryInfo(pArchive, entry, NULL, NULL, NULL, NULL, NULL, &val);
+ return val;
+}
+
+/*
+ * Uncompress and write an entry to a file descriptor.
+ *
+ * Returns 0 on success.
+ */
+int dexZipExtractEntryToFile(const ZipArchive* pArchive,
+ const ZipEntry entry, int fd);
+
+/*
+ * Utility function to compute a CRC-32.
+ */
+u4 dexInitCrc32(void);
+u4 dexComputeCrc32(u4 crc, const void* buf, size_t len);
+
+#endif /*_LIBDEX_ZIPARCHIVE*/
diff --git a/libdex/sha1.c b/libdex/sha1.c
new file mode 100644
index 0000000..dc7e30a
--- /dev/null
+++ b/libdex/sha1.c
@@ -0,0 +1,514 @@
+/*
+ * Tweaked in various ways for Google/Android:
+ * - Changed from .cpp to .c.
+ * - Made argument to SHA1Update a const pointer, and enabled
+ * SHA1HANDSOFF. This incurs a speed penalty but prevents us from
+ * trashing the input.
+ * - Include <endian.h> to get endian info.
+ * - Split a small piece into a header file.
+ */
+
+/*
+sha1sum: inspired by md5sum.
+
+SHA-1 in C
+By Steve Reid <steve@edmweb.com>
+100% Public Domain
+
+-----------------
+Modified 7/98
+By James H. Brown <jbrown@burgoyne.com>
+Still 100% Public Domain
+
+bit machines
+Routine SHA1Update changed from
+ void SHA1Update(SHA1_CTX* context, unsigned char* data,
+ unsigned int len)
+to
+ void SHA1Update(SHA1_CTX* context, unsigned char* data,
+ unsigned long len)
+
+The 'len' parameter was declared an int which works fine on 32
+bit machines. However, on 16 bit machines an int is too small
+for the shifts being done against it. This caused the hash
+function to generate incorrect values if len was greater than
+8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update().
+
+Since the file IO in main() reads 16K at a time, any file 8K or
+larger would be guaranteed to generate the wrong hash (e.g.
+Test Vector #3, a million "a"s).
+
+I also changed the declaration of variables i & j in SHA1Update
+to unsigned long from unsigned int for the same reason.
+
+These changes should make no difference to any 32 bit
+implementations since an int and a long are the same size in
+those environments.
+
+--
+I also corrected a few compiler warnings generated by Borland
+C.
+1. Added #include <process.h> for exit() prototype
+2. Removed unused variable 'j' in SHA1Final
+3. Changed exit(0) to return(0) at end of main.
+
+ALL changes I made can be located by searching for comments
+containing 'JHB'
+
+-----------------
+Modified 13 August 2000
+By Michael Paul Johnson <mpj@cryptography.org>
+Still 100% Public Domain
+
+Changed command line syntax, added feature to automatically
+check files against their previous SHA-1 check values, kind of
+like md5sum does. Added functions hexval, verifyfile,
+and sha1file. Rewrote main().
+-----------------
+
+Test Vectors (from FIPS PUB 180-1)
+"abc"
+ A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+ 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+A million repetitions of "a"
+ 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+*/
+
+#define SHA1HANDSOFF /*Copies data before messing with it.*/
+
+/*#define CMDLINE * include main() and file processing */
+
+#include "sha1.h"
+
+#include <stdio.h>
+#include <string.h>
+#ifdef __BORLANDC__
+#include <dir.h>
+#include <dos.h>
+#include <process.h> /* prototype for exit() - JHB
+ needed for Win32, but chokes Linux - MPJ */
+#define X_LITTLE_ENDIAN /* This should be #define'd if true.*/
+#else
+# include <unistd.h>
+# include <stdlib.h>
+//# include <endian.h>
+
+#include "DexFile.h" // want common byte ordering def
+
+# if __BYTE_ORDER == __LITTLE_ENDIAN
+# define X_LITTLE_ENDIAN
+# endif
+#endif
+#include <ctype.h>
+
+#define LINESIZE 2048
+
+static void SHA1Transform(unsigned long state[5],
+ const unsigned char buffer[64]);
+
+#define rol(value,bits) \
+ (((value)<<(bits))|((value)>>(32-(bits))))
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from
+ SSLeay */
+#ifdef X_LITTLE_ENDIAN
+#define blk0(i) (block->l[i]=(rol(block->l[i],24)&0xFF00FF00) \
+ |(rol(block->l[i],8)&0x00FF00FF))
+#else
+#define blk0(i) block->l[i]
+#endif
+#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
+ ^block->l[(i+2)&15]^block->l[i&15],1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+
+static void SHA1Transform(unsigned long state[5],
+ const unsigned char buffer[64])
+{
+unsigned long a, b, c, d, e;
+typedef union {
+ unsigned char c[64];
+ unsigned long l[16];
+} CHAR64LONG16;
+CHAR64LONG16* block;
+#ifdef SHA1HANDSOFF
+static unsigned char workspace[64];
+ block = (CHAR64LONG16*)workspace;
+ memcpy(block, buffer, 64);
+#else
+ block = (CHAR64LONG16*)buffer;
+#endif
+ /* Copy context->state[] to working vars */
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+ e = state[4];
+ /* 4 rounds of 20 operations each. Loop unrolled. */
+ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2);
+ R0(c,d,e,a,b, 3); R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5);
+ R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); R0(c,d,e,a,b, 8);
+ R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+ R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14);
+ R0(a,b,c,d,e,15); R1(e,a,b,c,d,16); R1(d,e,a,b,c,17);
+ R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); R2(a,b,c,d,e,20);
+ R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+ R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26);
+ R2(d,e,a,b,c,27); R2(c,d,e,a,b,28); R2(b,c,d,e,a,29);
+ R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); R2(d,e,a,b,c,32);
+ R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+ R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38);
+ R2(b,c,d,e,a,39); R3(a,b,c,d,e,40); R3(e,a,b,c,d,41);
+ R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); R3(b,c,d,e,a,44);
+ R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+ R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50);
+ R3(e,a,b,c,d,51); R3(d,e,a,b,c,52); R3(c,d,e,a,b,53);
+ R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); R3(e,a,b,c,d,56);
+ R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+ R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62);
+ R4(c,d,e,a,b,63); R4(b,c,d,e,a,64); R4(a,b,c,d,e,65);
+ R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); R4(c,d,e,a,b,68);
+ R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+ R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74);
+ R4(a,b,c,d,e,75); R4(e,a,b,c,d,76); R4(d,e,a,b,c,77);
+ R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+
+ /* Add the working vars back into context.state[] */
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+ /* Wipe variables */
+/* a = b = c = d = e = 0; Nice try, but the compiler
+optimizes this out, anyway, and it produces an annoying
+warning. */
+}
+
+
+/* SHA1Init - Initialize new context */
+
+void SHA1Init(SHA1_CTX* context)
+{
+ /* SHA1 initialization constants */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xEFCDAB89;
+ context->state[2] = 0x98BADCFE;
+ context->state[3] = 0x10325476;
+ context->state[4] = 0xC3D2E1F0;
+ context->count[0] = context->count[1] = 0;
+}
+
+
+/* Run your data through this. */
+
+void SHA1Update(SHA1_CTX* context, const unsigned char* data,
+ unsigned long len) /* JHB */
+{
+ unsigned long i, j; /* JHB */
+
+ j = (context->count[0] >> 3) & 63;
+ if ((context->count[0] += len << 3) < (len << 3))
+ context->count[1]++;
+ context->count[1] += (len >> 29);
+ if ((j + len) > 63)
+ {
+ memcpy(&context->buffer[j], data, (i = 64-j));
+ SHA1Transform(context->state, context->buffer);
+ for ( ; i + 63 < len; i += 64) {
+ SHA1Transform(context->state, &data[i]);
+ }
+ j = 0;
+ }
+ else
+ i = 0;
+ memcpy(&context->buffer[j], &data[i], len - i);
+}
+
+
+/* Add padding and return the message digest. */
+
+void SHA1Final(unsigned char digest[HASHSIZE], SHA1_CTX*
+context)
+{
+unsigned long i; /* JHB */
+unsigned char finalcount[8];
+
+ for (i = 0; i < 8; i++)
+ {
+ finalcount[i] = (unsigned char)((context->count[(i>=4?
+ 0:1)]>>((3-(i&3))*8))&255);
+ /* Endian independent */
+ }
+ SHA1Update(context, (unsigned char *)"\200", 1);
+ while ((context->count[0] & 504) != 448) {
+ SHA1Update(context, (unsigned char *)"\0", 1);
+ }
+ SHA1Update(context, finalcount, 8);
+ /* Should cause a SHA1Transform() */
+ for (i = 0; i < HASHSIZE; i++) {
+ digest[i] = (unsigned char)
+ ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+ }
+ /* Wipe variables */
+ memset(context->buffer, 0, 64);
+ memset(context->state, 0, HASHSIZE);
+ memset(context->count, 0, 8);
+ memset(&finalcount, 0, 8);
+#ifdef SHA1HANDSOFF
+ /* make SHA1Transform overwrite it's own static vars */
+ SHA1Transform(context->state, context->buffer);
+#endif
+}
+
+
+
+#ifdef CMDLINE
+
+/* sha1file computes the SHA-1 hash of the named file and puts
+ it in the 20-byte array digest. If fname is NULL, stdin is
+ assumed.
+*/
+void sha1file(char *fname, unsigned char* digest)
+{
+ int bytesread;
+ SHA1_CTX context;
+ unsigned char buffer[16384];
+ FILE* f;
+
+ if (fname)
+ {
+ f = fopen(fname, "rb");
+ if (!f)
+ {
+ fprintf(stderr, "Can't open %s\n", fname);
+ memset(digest, 0, HASHSIZE);
+ return;
+ }
+ }
+ else
+ {
+ f = stdin;
+ }
+ SHA1Init(&context);
+ while (!feof(f))
+ {
+ bytesread = fread(buffer, 1, 16384, f);
+ SHA1Update(&context, buffer, bytesread);
+ }
+ SHA1Final(digest, &context);
+ if (fname)
+ fclose(f);
+}
+
+/* Convert ASCII hexidecimal digit to 4-bit value. */
+unsigned char hexval(char c)
+{
+ unsigned char h;
+
+ c = toupper(c);
+ if (c >= 'A')
+ h = c - 'A' + 10;
+ else
+ h = c - '0';
+ return h;
+}
+
+/* Verify a file created with sha1sum by redirecting output
+ to a file. */
+int verifyfile(char *fname)
+{
+ int j, k;
+ int found = 0;
+ unsigned char digest[HASHSIZE];
+ unsigned char expected_digest[HASHSIZE];
+ FILE *checkfile;
+ char checkline[LINESIZE];
+ char *s;
+ unsigned char err;
+
+ checkfile = fopen(fname, "rt");
+ if (!checkfile)
+ {
+ fprintf(stderr, "Can't open %s\n", fname);
+ return(0);
+ }
+ do
+ {
+ s = fgets(checkline, LINESIZE, checkfile);
+ if (s)
+ {
+ if ((strlen(checkline)>26)&&
+ 1 /*(!strncmp(checkline,"SHA1=", 5))*/)
+ {
+ /* Overwrite newline. */
+ checkline[strlen(checkline)-1]=0;
+ found = 1;
+
+ /* Read expected check value. */
+ for (k=0, j=5; k < HASHSIZE; k++)
+ {
+ expected_digest[k]=hexval(checkline[j++]);
+ expected_digest[k]=(expected_digest[k]<<4)
+ +hexval(checkline[j++]);
+ }
+
+ /* Compute fingerprints */
+ s = checkline+46;
+ sha1file(s, digest);
+
+ /* Compare fingerprints */
+ err = 0;
+ for (k=0; k<HASHSIZE; k++)
+ err |= digest[k]-
+ expected_digest[k];
+ if (err)
+ {
+ fprintf(stderr, "FAILED: %s\n"
+ " EXPECTED: ", s);
+ for (k=0; k<HASHSIZE; k++)
+ fprintf(stderr, "%02X",
+ expected_digest[k]);
+ fprintf(stderr,"\n FOUND: ");
+ for (k=0; k<HASHSIZE; k++)
+ fprintf(stderr, "%02X", digest[k]);
+ fprintf(stderr, "\n");
+ }
+ else
+ {
+ printf("OK: %s\n", s);
+ }
+ }
+ }
+ } while (s);
+ return found;
+}
+
+
+
+void syntax(char *progname)
+{
+ printf("\nsyntax:\n"
+ "%s [-c|-h][-q] file name[s]\n"
+ " -c = check files against previous check values\n"
+ " -g = generate SHA-1 check values (default action)\n"
+ " -h = display this help\n"
+ "For example,\n"
+ "sha1sum test.txt > check.txt\n"
+ "generates check value for test.txt in check.txt, and\n"
+ "sha1sum -c check.txt\n"
+ "checks test.txt against the check value in check.txt\n",
+ progname);
+ exit(1);
+}
+
+
+/**********************************************************/
+
+int main(int argc, char** argv)
+{
+ int i, j, k;
+ int check = 0;
+ int found = 0;
+ unsigned char digest[HASHSIZE];
+ unsigned char expected_digest[HASHSIZE];
+ FILE *checkfile;
+ char checkline[LINESIZE];
+ char *s;
+#ifdef __BORLANDC__
+ struct ffblk f;
+ int done;
+ char path[MAXPATH];
+ char drive[MAXDRIVE];
+ char dir[MAXDIR];
+ char name[MAXFILE];
+ char ext[MAXEXT];
+#endif
+ unsigned char err;
+
+ for (i = 1; i < argc; i++)
+ {
+ if (argv[i][0] == '-')
+ {
+ switch (argv[i][1])
+ {
+ case 'c':
+ case 'C':
+ check = 1;
+ break;
+ case 'g':
+ case 'G':
+ check = 0;
+ break;
+ default:
+ syntax(argv[0]);
+ }
+ }
+ }
+
+ for (i=1; i<argc; i++)
+ {
+ if (argv[i][0] != '-')
+ {
+#ifdef __BORLANDC__
+ fnsplit(argv[i], drive, dir, name, ext);
+ done = findfirst(argv[i], &f, FA_RDONLY |
+ FA_HIDDEN|FA_SYSTEM|FA_ARCH);
+ while (!done)
+ {
+ sprintf(path, "%s%s%s", drive, dir, f.ff_name);
+ s = path;
+#else
+ s = argv[i];
+#endif
+
+ if (check)
+ { /* Check fingerprint file. */
+ found |= verifyfile(s);
+ }
+ else
+ { /* Generate fingerprints & write to
+ stdout. */
+ sha1file(s, digest);
+ //printf("SHA1=");
+ for (j=0; j<HASHSIZE; j++)
+ printf("%02x", digest[j]);
+ printf(" %s\n", s);
+ found = 1;
+ }
+
+#ifdef __BORLANDC__
+ done = findnext(&f);
+ }
+#endif
+
+ }
+ }
+ if (!found)
+ {
+ if (check)
+ {
+ fprintf(stderr,
+ "No SHA1 lines found in %s\n",
+ argv[i]);
+ }
+ else
+ {
+ fprintf(stderr, "No files checked.\n");
+ syntax(argv[0]);
+ }
+ }
+ return(0); /* JHB */
+}
+
+#endif /*CMDLINE*/
diff --git a/libdex/sha1.h b/libdex/sha1.h
new file mode 100644
index 0000000..65cf667
--- /dev/null
+++ b/libdex/sha1.h
@@ -0,0 +1,20 @@
+/*
+ * See "sha1.c" for author info.
+ */
+#ifndef _DALVIK_SHA1
+#define _DALVIK_SHA1
+
+typedef struct {
+ unsigned long state[5];
+ unsigned long count[2];
+ unsigned char buffer[64];
+} SHA1_CTX;
+
+#define HASHSIZE 20
+
+void SHA1Init(SHA1_CTX* context);
+void SHA1Update(SHA1_CTX* context, const unsigned char* data,
+ unsigned long len);
+void SHA1Final(unsigned char digest[HASHSIZE], SHA1_CTX* context);
+
+#endif /*_DALVIK_SHA1*/
diff --git a/libnativehelper/Android.mk b/libnativehelper/Android.mk
new file mode 100644
index 0000000..630eec3
--- /dev/null
+++ b/libnativehelper/Android.mk
@@ -0,0 +1,88 @@
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+LOCAL_PATH := $(call my-dir)
+
+#
+# Common definitions for host and device.
+#
+
+src_files := \
+ JNIHelp.c \
+ Register.c
+
+c_includes := \
+ $(JNI_H_INCLUDE)
+
+# Any shared/static libs required by libjavacore
+# need to be mentioned here as well.
+# TODO: fix this requirement
+
+shared_libraries := \
+ libcrypto \
+ libicui18n \
+ libicuuc \
+ libsqlite \
+ libssl
+
+static_libraries := \
+ libjavacore \
+ libfdlibm
+
+
+
+#
+# Build for the target (device).
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(src_files)
+LOCAL_C_INCLUDES := $(c_includes)
+LOCAL_STATIC_LIBRARIES := $(static_libraries)
+LOCAL_SHARED_LIBRARIES := $(shared_libraries) libcutils libexpat liblog libutils libz
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := libnativehelper
+
+include $(BUILD_SHARED_LIBRARY)
+
+
+#
+# Build for the host.
+#
+
+ifeq ($(WITH_HOST_DALVIK),true)
+
+ include $(CLEAR_VARS)
+
+ LOCAL_SRC_FILES := $(src_files)
+ LOCAL_C_INCLUDES := $(c_includes)
+ LOCAL_WHOLE_STATIC_LIBRARIES := $(static_libraries:%=%-host)
+
+ ifeq ($(HOST_OS)-$(HOST_ARCH),darwin-x86)
+ # OSX has a lot of libraries built in, which we don't have to
+ # bother building; just include them on the ld line.
+ LOCAL_LDLIBS := -lexpat -lssl -lz -lcrypto -licucore -lsqlite3
+ LOCAL_WHOLE_STATIC_LIBRARIES += libutils
+ else
+ LOCAL_SHARED_LIBRARIES := $(shared_libraries)
+ LOCAL_STATIC_LIBRARIES := libcutils libexpat liblog libutils libz
+ endif
+
+ LOCAL_MODULE_TAGS := optional
+ LOCAL_MODULE := libnativehelper
+ include $(BUILD_HOST_STATIC_LIBRARY)
+
+endif
diff --git a/libnativehelper/JNIHelp.c b/libnativehelper/JNIHelp.c
new file mode 100644
index 0000000..59d457b
--- /dev/null
+++ b/libnativehelper/JNIHelp.c
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * JNI helper functions.
+ */
+#define LOG_TAG "JNIHelp"
+#include "JNIHelp.h"
+#include "utils/Log.h"
+
+#include <string.h>
+#include <assert.h>
+
+/*
+ * Register native JNI-callable methods.
+ *
+ * "className" looks like "java/lang/String".
+ */
+int jniRegisterNativeMethods(JNIEnv* env, const char* className,
+ const JNINativeMethod* gMethods, int numMethods)
+{
+ jclass clazz;
+
+ LOGV("Registering %s natives\n", className);
+ clazz = (*env)->FindClass(env, className);
+ if (clazz == NULL) {
+ LOGE("Native registration unable to find class '%s'\n", className);
+ return -1;
+ }
+
+ int result = 0;
+ if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
+ LOGE("RegisterNatives failed for '%s'\n", className);
+ result = -1;
+ }
+
+ (*env)->DeleteLocalRef(env, clazz);
+ return result;
+}
+
+/*
+ * Get a human-readable summary of an exception object. The buffer will
+ * be populated with the "binary" class name and, if present, the
+ * exception message.
+ */
+static void getExceptionSummary(JNIEnv* env, jthrowable exception, char* buf, size_t bufLen)
+{
+ int success = 0;
+
+ /* get the name of the exception's class */
+ jclass exceptionClazz = (*env)->GetObjectClass(env, exception); // can't fail
+ jclass classClazz = (*env)->GetObjectClass(env, exceptionClazz); // java.lang.Class, can't fail
+ jmethodID classGetNameMethod = (*env)->GetMethodID(
+ env, classClazz, "getName", "()Ljava/lang/String;");
+ jstring classNameStr = (*env)->CallObjectMethod(env, exceptionClazz, classGetNameMethod);
+ if (classNameStr != NULL) {
+ /* get printable string */
+ const char* classNameChars = (*env)->GetStringUTFChars(env, classNameStr, NULL);
+ if (classNameChars != NULL) {
+ /* if the exception has a message string, get that */
+ jmethodID throwableGetMessageMethod = (*env)->GetMethodID(
+ env, exceptionClazz, "getMessage", "()Ljava/lang/String;");
+ jstring messageStr = (*env)->CallObjectMethod(
+ env, exception, throwableGetMessageMethod);
+
+ if (messageStr != NULL) {
+ const char* messageChars = (*env)->GetStringUTFChars(env, messageStr, NULL);
+ if (messageChars != NULL) {
+ snprintf(buf, bufLen, "%s: %s", classNameChars, messageChars);
+ (*env)->ReleaseStringUTFChars(env, messageStr, messageChars);
+ } else {
+ (*env)->ExceptionClear(env); // clear OOM
+ snprintf(buf, bufLen, "%s: <error getting message>", classNameChars);
+ }
+ (*env)->DeleteLocalRef(env, messageStr);
+ } else {
+ strncpy(buf, classNameChars, bufLen);
+ buf[bufLen - 1] = '\0';
+ }
+
+ (*env)->ReleaseStringUTFChars(env, classNameStr, classNameChars);
+ success = 1;
+ }
+ (*env)->DeleteLocalRef(env, classNameStr);
+ }
+ (*env)->DeleteLocalRef(env, classClazz);
+ (*env)->DeleteLocalRef(env, exceptionClazz);
+
+ if (! success) {
+ (*env)->ExceptionClear(env);
+ snprintf(buf, bufLen, "%s", "<error getting class name>");
+ }
+}
+
+/*
+ * Formats an exception as a string with its stack trace.
+ */
+static void printStackTrace(JNIEnv* env, jthrowable exception, char* buf, size_t bufLen)
+{
+ int success = 0;
+
+ jclass stringWriterClazz = (*env)->FindClass(env, "java/io/StringWriter");
+ if (stringWriterClazz != NULL) {
+ jmethodID stringWriterCtor = (*env)->GetMethodID(env, stringWriterClazz,
+ "<init>", "()V");
+ jmethodID stringWriterToStringMethod = (*env)->GetMethodID(env, stringWriterClazz,
+ "toString", "()Ljava/lang/String;");
+
+ jclass printWriterClazz = (*env)->FindClass(env, "java/io/PrintWriter");
+ if (printWriterClazz != NULL) {
+ jmethodID printWriterCtor = (*env)->GetMethodID(env, printWriterClazz,
+ "<init>", "(Ljava/io/Writer;)V");
+
+ jobject stringWriterObj = (*env)->NewObject(env, stringWriterClazz, stringWriterCtor);
+ if (stringWriterObj != NULL) {
+ jobject printWriterObj = (*env)->NewObject(env, printWriterClazz, printWriterCtor,
+ stringWriterObj);
+ if (printWriterObj != NULL) {
+ jclass exceptionClazz = (*env)->GetObjectClass(env, exception); // can't fail
+ jmethodID printStackTraceMethod = (*env)->GetMethodID(
+ env, exceptionClazz, "printStackTrace", "(Ljava/io/PrintWriter;)V");
+
+ (*env)->CallVoidMethod(
+ env, exception, printStackTraceMethod, printWriterObj);
+ if (! (*env)->ExceptionCheck(env)) {
+ jstring messageStr = (*env)->CallObjectMethod(
+ env, stringWriterObj, stringWriterToStringMethod);
+ if (messageStr != NULL) {
+ jsize messageStrLength = (*env)->GetStringLength(env, messageStr);
+ if (messageStrLength >= (jsize) bufLen) {
+ messageStrLength = bufLen - 1;
+ }
+ (*env)->GetStringUTFRegion(env, messageStr, 0, messageStrLength, buf);
+ (*env)->DeleteLocalRef(env, messageStr);
+ buf[messageStrLength] = '\0';
+ success = 1;
+ }
+ }
+ (*env)->DeleteLocalRef(env, exceptionClazz);
+ (*env)->DeleteLocalRef(env, printWriterObj);
+ }
+ (*env)->DeleteLocalRef(env, stringWriterObj);
+ }
+ (*env)->DeleteLocalRef(env, printWriterClazz);
+ }
+ (*env)->DeleteLocalRef(env, stringWriterClazz);
+ }
+
+ if (! success) {
+ (*env)->ExceptionClear(env);
+ getExceptionSummary(env, exception, buf, bufLen);
+ }
+}
+
+/*
+ * Throw an exception with the specified class and an optional message.
+ *
+ * If an exception is currently pending, we log a warning message and
+ * clear it.
+ *
+ * Returns 0 if the specified exception was successfully thrown. (Some
+ * sort of exception will always be pending when this returns.)
+ */
+int jniThrowException(JNIEnv* env, const char* className, const char* msg)
+{
+ jclass exceptionClass;
+
+ if ((*env)->ExceptionCheck(env)) {
+ /* TODO: consider creating the new exception with this as "cause" */
+ char buf[256];
+
+ jthrowable exception = (*env)->ExceptionOccurred(env);
+ (*env)->ExceptionClear(env);
+
+ if (exception != NULL) {
+ getExceptionSummary(env, exception, buf, sizeof(buf));
+ LOGW("Discarding pending exception (%s) to throw %s\n", buf, className);
+ (*env)->DeleteLocalRef(env, exception);
+ }
+ }
+
+ exceptionClass = (*env)->FindClass(env, className);
+ if (exceptionClass == NULL) {
+ LOGE("Unable to find exception class %s\n", className);
+ /* ClassNotFoundException now pending */
+ return -1;
+ }
+
+ int result = 0;
+ if ((*env)->ThrowNew(env, exceptionClass, msg) != JNI_OK) {
+ LOGE("Failed throwing '%s' '%s'\n", className, msg);
+ /* an exception, most likely OOM, will now be pending */
+ result = -1;
+ }
+
+ (*env)->DeleteLocalRef(env, exceptionClass);
+ return result;
+}
+
+/*
+ * Throw a java.lang.NullPointerException, with an optional message.
+ */
+int jniThrowNullPointerException(JNIEnv* env, const char* msg)
+{
+ return jniThrowException(env, "java/lang/NullPointerException", msg);
+}
+
+/*
+ * Throw a java.lang.RuntimeException, with an optional message.
+ */
+int jniThrowRuntimeException(JNIEnv* env, const char* msg)
+{
+ return jniThrowException(env, "java/lang/RuntimeException", msg);
+}
+
+/*
+ * Throw a java.io.IOException, generating the message from errno.
+ */
+int jniThrowIOException(JNIEnv* env, int errnum)
+{
+ char buffer[80];
+ const char* message = jniStrError(errnum, buffer, sizeof(buffer));
+ return jniThrowException(env, "java/io/IOException", message);
+}
+
+/*
+ * Log an exception.
+ * If exception is NULL, logs the current exception in the JNI environment, if any.
+ */
+void jniLogException(JNIEnv* env, int priority, const char* tag, jthrowable exception)
+{
+ int currentException = 0;
+ if (exception == NULL) {
+ exception = (*env)->ExceptionOccurred(env);
+ if (exception == NULL) {
+ return;
+ }
+
+ (*env)->ExceptionClear(env);
+ currentException = 1;
+ }
+
+ char buffer[1024];
+ printStackTrace(env, exception, buffer, sizeof(buffer));
+ __android_log_write(priority, tag, buffer);
+
+ if (currentException) {
+ (*env)->Throw(env, exception); // rethrow
+ (*env)->DeleteLocalRef(env, exception);
+ }
+}
+
+const char* jniStrError(int errnum, char* buf, size_t buflen)
+{
+ // note: glibc has a nonstandard strerror_r that returns char* rather
+ // than POSIX's int.
+ // char *strerror_r(int errnum, char *buf, size_t n);
+ char* ret = (char*) strerror_r(errnum, buf, buflen);
+ if (((int)ret) == 0) {
+ //POSIX strerror_r, success
+ return buf;
+ } else if (((int)ret) == -1) {
+ //POSIX strerror_r, failure
+ // (Strictly, POSIX only guarantees a value other than 0. The safest
+ // way to implement this function is to use C++ and overload on the
+ // type of strerror_r to accurately distinguish GNU from POSIX. But
+ // realistic implementations will always return -1.)
+ snprintf(buf, buflen, "errno %d", errnum);
+ return buf;
+ } else {
+ //glibc strerror_r returning a string
+ return ret;
+ }
+}
+
+static struct CachedFields {
+ jclass fileDescriptorClass;
+ jmethodID fileDescriptorCtor;
+ jfieldID descriptorField;
+} gCachedFields;
+
+int registerJniHelp(JNIEnv* env) {
+ gCachedFields.fileDescriptorClass =
+ (*env)->NewGlobalRef(env, (*env)->FindClass(env, "java/io/FileDescriptor"));
+ if (gCachedFields.fileDescriptorClass == NULL) {
+ return -1;
+ }
+
+ gCachedFields.fileDescriptorCtor =
+ (*env)->GetMethodID(env, gCachedFields.fileDescriptorClass, "<init>", "()V");
+ if (gCachedFields.fileDescriptorCtor == NULL) {
+ return -1;
+ }
+
+ gCachedFields.descriptorField =
+ (*env)->GetFieldID(env, gCachedFields.fileDescriptorClass, "descriptor", "I");
+ if (gCachedFields.descriptorField == NULL) {
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Create a java.io.FileDescriptor given an integer fd
+ */
+jobject jniCreateFileDescriptor(JNIEnv* env, int fd) {
+ jobject fileDescriptor = (*env)->NewObject(env,
+ gCachedFields.fileDescriptorClass, gCachedFields.fileDescriptorCtor);
+ jniSetFileDescriptorOfFD(env, fileDescriptor, fd);
+ return fileDescriptor;
+}
+
+/*
+ * Get an int file descriptor from a java.io.FileDescriptor
+ */
+int jniGetFDFromFileDescriptor(JNIEnv* env, jobject fileDescriptor) {
+ return (*env)->GetIntField(env, fileDescriptor, gCachedFields.descriptorField);
+}
+
+/*
+ * Set the descriptor of a java.io.FileDescriptor
+ */
+void jniSetFileDescriptorOfFD(JNIEnv* env, jobject fileDescriptor, int value) {
+ (*env)->SetIntField(env, fileDescriptor, gCachedFields.descriptorField, value);
+}
diff --git a/libnativehelper/MODULE_LICENSE_APACHE2 b/libnativehelper/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libnativehelper/MODULE_LICENSE_APACHE2
diff --git a/libnativehelper/NOTICE b/libnativehelper/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/libnativehelper/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-2008, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/libnativehelper/README b/libnativehelper/README
new file mode 100644
index 0000000..5a5f5d4
--- /dev/null
+++ b/libnativehelper/README
@@ -0,0 +1,11 @@
+Support functions for Android's class libraries
+
+
+These are VM-agnostic native functions that implement methods for system
+class libraries. All code here:
+
+ - MUST not be associated with an android.* class (that code lives in
+ frameworks/base/).
+ - SHOULD be written in C rather than C++ where possible.
+
+Some helper functions are defined in include/nativehelper/JNIHelp.h.
diff --git a/libnativehelper/Register.c b/libnativehelper/Register.c
new file mode 100644
index 0000000..87017ac
--- /dev/null
+++ b/libnativehelper/Register.c
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * JNI helper functions.
+ */
+
+#include "jni.h"
+
+extern int registerCoreLibrariesJni(JNIEnv* env);
+extern int registerJniHelp(JNIEnv* env);
+
+/*
+ * Register all methods for system classes.
+ */
+int jniRegisterSystemMethods(JNIEnv* env)
+{
+ // We initialize JNIHelp.c first so that the core libraries can safely rely on it.
+ return registerJniHelp(env) != -1 && registerCoreLibrariesJni(env) != -1;
+}
diff --git a/libnativehelper/include/nativehelper/JNIHelp.h b/libnativehelper/include/nativehelper/JNIHelp.h
new file mode 100644
index 0000000..1c9f933
--- /dev/null
+++ b/libnativehelper/include/nativehelper/JNIHelp.h
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * JNI helper functions.
+ *
+ * This file may be included by C or C++ code, which is trouble because jni.h
+ * uses different typedefs for JNIEnv in each language.
+ */
+#ifndef _NATIVEHELPER_JNIHELP_H
+#define _NATIVEHELPER_JNIHELP_H
+
+#include "jni.h"
+#include "utils/Log.h"
+#include <unistd.h>
+
+#ifndef NELEM
+# define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Register one or more native methods with a particular class.
+ */
+int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
+ const JNINativeMethod* gMethods, int numMethods);
+
+/*
+ * Throw an exception with the specified class and an optional message.
+ * The "className" argument will be passed directly to FindClass, which
+ * takes strings with slashes (e.g. "java/lang/Object").
+ *
+ * Returns 0 on success, nonzero if something failed (e.g. the exception
+ * class couldn't be found).
+ *
+ * Currently aborts the VM if it can't throw the exception.
+ */
+int jniThrowException(C_JNIEnv* env, const char* className, const char* msg);
+
+/*
+ * Throw a java.lang.NullPointerException, with an optional message.
+ */
+int jniThrowNullPointerException(C_JNIEnv* env, const char* msg);
+
+/*
+ * Throw a java.lang.RuntimeException, with an optional message.
+ */
+int jniThrowRuntimeException(C_JNIEnv* env, const char* msg);
+
+/*
+ * Throw a java.io.IOException, generating the message from errno.
+ */
+int jniThrowIOException(C_JNIEnv* env, int errnum);
+
+/*
+ * Return a pointer to a locale-dependent error string explaining errno
+ * value 'errnum'. The returned pointer may or may not be equal to 'buf'.
+ * This function is thread-safe (unlike strerror) and portable (unlike
+ * strerror_r).
+ */
+const char* jniStrError(int errnum, char* buf, size_t buflen);
+
+/*
+ * Create a java.io.FileDescriptor given an integer fd
+ */
+jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd);
+
+/*
+ * Get an int file descriptor from a java.io.FileDescriptor
+ */
+int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor);
+
+/*
+ * Set an int file descriptor to a java.io.FileDescriptor
+ */
+void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value);
+
+/*
+ * Log a message and an exception.
+ * If exception is NULL, logs the current exception in the JNI environment.
+ */
+void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable exception);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+/*
+ * For C++ code, we provide inlines that map to the C functions. g++ always
+ * inlines these, even on non-optimized builds.
+ */
+#if defined(__cplusplus) && !defined(JNI_FORCE_C)
+inline int jniRegisterNativeMethods(JNIEnv* env, const char* className,
+ const JNINativeMethod* gMethods, int numMethods)
+{
+ return jniRegisterNativeMethods(&env->functions, className, gMethods,
+ numMethods);
+}
+inline int jniThrowException(JNIEnv* env, const char* className,
+ const char* msg)
+{
+ return jniThrowException(&env->functions, className, msg);
+}
+inline int jniThrowNullPointerException(JNIEnv* env, const char* msg)
+{
+ return jniThrowNullPointerException(&env->functions, msg);
+}
+inline int jniThrowRuntimeException(JNIEnv* env, const char* msg)
+{
+ return jniThrowRuntimeException(&env->functions, msg);
+}
+inline int jniThrowIOException(JNIEnv* env, int errnum)
+{
+ return jniThrowIOException(&env->functions, errnum);
+}
+inline jobject jniCreateFileDescriptor(JNIEnv* env, int fd)
+{
+ return jniCreateFileDescriptor(&env->functions, fd);
+}
+inline int jniGetFDFromFileDescriptor(JNIEnv* env, jobject fileDescriptor)
+{
+ return jniGetFDFromFileDescriptor(&env->functions, fileDescriptor);
+}
+inline void jniSetFileDescriptorOfFD(JNIEnv* env, jobject fileDescriptor,
+ int value)
+{
+ jniSetFileDescriptorOfFD(&env->functions, fileDescriptor, value);
+}
+inline void jniLogException(JNIEnv* env, int priority, const char* tag,
+ jthrowable exception = NULL)
+{
+ jniLogException(&env->functions, priority, tag, exception);
+}
+#endif
+
+/* Logging macros.
+ *
+ * Logs an exception. If the exception is omitted or NULL, logs the current exception
+ * from the JNI environment, if any.
+ */
+#define LOG_EX(env, priority, tag, ...) \
+ IF_LOG(priority, tag) jniLogException(env, ANDROID_##priority, tag, ##__VA_ARGS__)
+#define LOGV_EX(env, ...) LOG_EX(env, LOG_VERBOSE, LOG_TAG, ##__VA_ARGS__)
+#define LOGD_EX(env, ...) LOG_EX(env, LOG_DEBUG, LOG_TAG, ##__VA_ARGS__)
+#define LOGI_EX(env, ...) LOG_EX(env, LOG_INFO, LOG_TAG, ##__VA_ARGS__)
+#define LOGW_EX(env, ...) LOG_EX(env, LOG_WARN, LOG_TAG, ##__VA_ARGS__)
+#define LOGE_EX(env, ...) LOG_EX(env, LOG_ERROR, LOG_TAG, ##__VA_ARGS__)
+
+/*
+ * TEMP_FAILURE_RETRY is defined by some, but not all, versions of
+ * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's
+ * not already defined, then define it here.
+ */
+#ifndef TEMP_FAILURE_RETRY
+/* Used to retry syscalls that can return EINTR. */
+#define TEMP_FAILURE_RETRY(exp) ({ \
+ typeof (exp) _rc; \
+ do { \
+ _rc = (exp); \
+ } while (_rc == -1 && errno == EINTR); \
+ _rc; })
+#endif
+
+#endif /*_NATIVEHELPER_JNIHELP_H*/
diff --git a/libnativehelper/include/nativehelper/jni.h b/libnativehelper/include/nativehelper/jni.h
new file mode 100644
index 0000000..22b1d88
--- /dev/null
+++ b/libnativehelper/include/nativehelper/jni.h
@@ -0,0 +1,1155 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * JNI specification, as defined by Sun:
+ * http://java.sun.com/javase/6/docs/technotes/guides/jni/spec/jniTOC.html
+ *
+ * Everything here is expected to be VM-neutral.
+ */
+
+#ifndef _JNI_H
+#define _JNI_H
+
+#include <stdarg.h>
+
+/*
+ * Primitive types that match up with Java equivalents.
+ */
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h> /* C99 */
+typedef uint8_t jboolean; /* unsigned 8 bits */
+typedef int8_t jbyte; /* signed 8 bits */
+typedef uint16_t jchar; /* unsigned 16 bits */
+typedef int16_t jshort; /* signed 16 bits */
+typedef int32_t jint; /* signed 32 bits */
+typedef int64_t jlong; /* signed 64 bits */
+typedef float jfloat; /* 32-bit IEEE 754 */
+typedef double jdouble; /* 64-bit IEEE 754 */
+#else
+typedef unsigned char jboolean; /* unsigned 8 bits */
+typedef signed char jbyte; /* signed 8 bits */
+typedef unsigned short jchar; /* unsigned 16 bits */
+typedef short jshort; /* signed 16 bits */
+typedef int jint; /* signed 32 bits */
+typedef long long jlong; /* signed 64 bits */
+typedef float jfloat; /* 32-bit IEEE 754 */
+typedef double jdouble; /* 64-bit IEEE 754 */
+#endif
+
+/* "cardinal indices and sizes" */
+typedef jint jsize;
+
+#ifdef __cplusplus
+/*
+ * Reference types, in C++
+ */
+class _jobject {};
+class _jclass : public _jobject {};
+class _jstring : public _jobject {};
+class _jarray : public _jobject {};
+class _jobjectArray : public _jarray {};
+class _jbooleanArray : public _jarray {};
+class _jbyteArray : public _jarray {};
+class _jcharArray : public _jarray {};
+class _jshortArray : public _jarray {};
+class _jintArray : public _jarray {};
+class _jlongArray : public _jarray {};
+class _jfloatArray : public _jarray {};
+class _jdoubleArray : public _jarray {};
+class _jthrowable : public _jobject {};
+
+typedef _jobject* jobject;
+typedef _jclass* jclass;
+typedef _jstring* jstring;
+typedef _jarray* jarray;
+typedef _jobjectArray* jobjectArray;
+typedef _jbooleanArray* jbooleanArray;
+typedef _jbyteArray* jbyteArray;
+typedef _jcharArray* jcharArray;
+typedef _jshortArray* jshortArray;
+typedef _jintArray* jintArray;
+typedef _jlongArray* jlongArray;
+typedef _jfloatArray* jfloatArray;
+typedef _jdoubleArray* jdoubleArray;
+typedef _jthrowable* jthrowable;
+typedef _jobject* jweak;
+
+
+#else /* not __cplusplus */
+
+/*
+ * Reference types, in C.
+ */
+typedef void* jobject;
+typedef jobject jclass;
+typedef jobject jstring;
+typedef jobject jarray;
+typedef jarray jobjectArray;
+typedef jarray jbooleanArray;
+typedef jarray jbyteArray;
+typedef jarray jcharArray;
+typedef jarray jshortArray;
+typedef jarray jintArray;
+typedef jarray jlongArray;
+typedef jarray jfloatArray;
+typedef jarray jdoubleArray;
+typedef jobject jthrowable;
+typedef jobject jweak;
+
+#endif /* not __cplusplus */
+
+struct _jfieldID; /* opaque structure */
+typedef struct _jfieldID* jfieldID; /* field IDs */
+
+struct _jmethodID; /* opaque structure */
+typedef struct _jmethodID* jmethodID; /* method IDs */
+
+struct JNIInvokeInterface;
+
+typedef union jvalue {
+ jboolean z;
+ jbyte b;
+ jchar c;
+ jshort s;
+ jint i;
+ jlong j;
+ jfloat f;
+ jdouble d;
+ jobject l;
+} jvalue;
+
+typedef enum jobjectRefType {
+ JNIInvalidRefType = 0,
+ JNILocalRefType = 1,
+ JNIGlobalRefType = 2,
+ JNIWeakGlobalRefType = 3
+} jobjectRefType;
+
+typedef struct {
+ const char* name;
+ const char* signature;
+ void* fnPtr;
+} JNINativeMethod;
+
+struct _JNIEnv;
+struct _JavaVM;
+typedef const struct JNINativeInterface* C_JNIEnv;
+
+#if defined(__cplusplus)
+typedef _JNIEnv JNIEnv;
+typedef _JavaVM JavaVM;
+#else
+typedef const struct JNINativeInterface* JNIEnv;
+typedef const struct JNIInvokeInterface* JavaVM;
+#endif
+
+/*
+ * Table of interface function pointers.
+ */
+struct JNINativeInterface {
+ void* reserved0;
+ void* reserved1;
+ void* reserved2;
+ void* reserved3;
+
+ jint (*GetVersion)(JNIEnv *);
+
+ jclass (*DefineClass)(JNIEnv*, const char*, jobject, const jbyte*,
+ jsize);
+ jclass (*FindClass)(JNIEnv*, const char*);
+
+ jmethodID (*FromReflectedMethod)(JNIEnv*, jobject);
+ jfieldID (*FromReflectedField)(JNIEnv*, jobject);
+ /* spec doesn't show jboolean parameter */
+ jobject (*ToReflectedMethod)(JNIEnv*, jclass, jmethodID, jboolean);
+
+ jclass (*GetSuperclass)(JNIEnv*, jclass);
+ jboolean (*IsAssignableFrom)(JNIEnv*, jclass, jclass);
+
+ /* spec doesn't show jboolean parameter */
+ jobject (*ToReflectedField)(JNIEnv*, jclass, jfieldID, jboolean);
+
+ jint (*Throw)(JNIEnv*, jthrowable);
+ jint (*ThrowNew)(JNIEnv *, jclass, const char *);
+ jthrowable (*ExceptionOccurred)(JNIEnv*);
+ void (*ExceptionDescribe)(JNIEnv*);
+ void (*ExceptionClear)(JNIEnv*);
+ void (*FatalError)(JNIEnv*, const char*);
+
+ jint (*PushLocalFrame)(JNIEnv*, jint);
+ jobject (*PopLocalFrame)(JNIEnv*, jobject);
+
+ jobject (*NewGlobalRef)(JNIEnv*, jobject);
+ void (*DeleteGlobalRef)(JNIEnv*, jobject);
+ void (*DeleteLocalRef)(JNIEnv*, jobject);
+ jboolean (*IsSameObject)(JNIEnv*, jobject, jobject);
+
+ jobject (*NewLocalRef)(JNIEnv*, jobject);
+ jint (*EnsureLocalCapacity)(JNIEnv*, jint);
+
+ jobject (*AllocObject)(JNIEnv*, jclass);
+ jobject (*NewObject)(JNIEnv*, jclass, jmethodID, ...);
+ jobject (*NewObjectV)(JNIEnv*, jclass, jmethodID, va_list);
+ jobject (*NewObjectA)(JNIEnv*, jclass, jmethodID, jvalue*);
+
+ jclass (*GetObjectClass)(JNIEnv*, jobject);
+ jboolean (*IsInstanceOf)(JNIEnv*, jobject, jclass);
+ jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
+
+ jobject (*CallObjectMethod)(JNIEnv*, jobject, jmethodID, ...);
+ jobject (*CallObjectMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+ jobject (*CallObjectMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+ jboolean (*CallBooleanMethod)(JNIEnv*, jobject, jmethodID, ...);
+ jboolean (*CallBooleanMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+ jboolean (*CallBooleanMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+ jbyte (*CallByteMethod)(JNIEnv*, jobject, jmethodID, ...);
+ jbyte (*CallByteMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+ jbyte (*CallByteMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+ jchar (*CallCharMethod)(JNIEnv*, jobject, jmethodID, ...);
+ jchar (*CallCharMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+ jchar (*CallCharMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+ jshort (*CallShortMethod)(JNIEnv*, jobject, jmethodID, ...);
+ jshort (*CallShortMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+ jshort (*CallShortMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+ jint (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
+ jint (*CallIntMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+ jint (*CallIntMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+ jlong (*CallLongMethod)(JNIEnv*, jobject, jmethodID, ...);
+ jlong (*CallLongMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+ jlong (*CallLongMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+ jfloat (*CallFloatMethod)(JNIEnv*, jobject, jmethodID, ...);
+ jfloat (*CallFloatMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+ jfloat (*CallFloatMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+ jdouble (*CallDoubleMethod)(JNIEnv*, jobject, jmethodID, ...);
+ jdouble (*CallDoubleMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+ jdouble (*CallDoubleMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+ void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
+ void (*CallVoidMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+ void (*CallVoidMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+
+ jobject (*CallNonvirtualObjectMethod)(JNIEnv*, jobject, jclass,
+ jmethodID, ...);
+ jobject (*CallNonvirtualObjectMethodV)(JNIEnv*, jobject, jclass,
+ jmethodID, va_list);
+ jobject (*CallNonvirtualObjectMethodA)(JNIEnv*, jobject, jclass,
+ jmethodID, jvalue*);
+ jboolean (*CallNonvirtualBooleanMethod)(JNIEnv*, jobject, jclass,
+ jmethodID, ...);
+ jboolean (*CallNonvirtualBooleanMethodV)(JNIEnv*, jobject, jclass,
+ jmethodID, va_list);
+ jboolean (*CallNonvirtualBooleanMethodA)(JNIEnv*, jobject, jclass,
+ jmethodID, jvalue*);
+ jbyte (*CallNonvirtualByteMethod)(JNIEnv*, jobject, jclass,
+ jmethodID, ...);
+ jbyte (*CallNonvirtualByteMethodV)(JNIEnv*, jobject, jclass,
+ jmethodID, va_list);
+ jbyte (*CallNonvirtualByteMethodA)(JNIEnv*, jobject, jclass,
+ jmethodID, jvalue*);
+ jchar (*CallNonvirtualCharMethod)(JNIEnv*, jobject, jclass,
+ jmethodID, ...);
+ jchar (*CallNonvirtualCharMethodV)(JNIEnv*, jobject, jclass,
+ jmethodID, va_list);
+ jchar (*CallNonvirtualCharMethodA)(JNIEnv*, jobject, jclass,
+ jmethodID, jvalue*);
+ jshort (*CallNonvirtualShortMethod)(JNIEnv*, jobject, jclass,
+ jmethodID, ...);
+ jshort (*CallNonvirtualShortMethodV)(JNIEnv*, jobject, jclass,
+ jmethodID, va_list);
+ jshort (*CallNonvirtualShortMethodA)(JNIEnv*, jobject, jclass,
+ jmethodID, jvalue*);
+ jint (*CallNonvirtualIntMethod)(JNIEnv*, jobject, jclass,
+ jmethodID, ...);
+ jint (*CallNonvirtualIntMethodV)(JNIEnv*, jobject, jclass,
+ jmethodID, va_list);
+ jint (*CallNonvirtualIntMethodA)(JNIEnv*, jobject, jclass,
+ jmethodID, jvalue*);
+ jlong (*CallNonvirtualLongMethod)(JNIEnv*, jobject, jclass,
+ jmethodID, ...);
+ jlong (*CallNonvirtualLongMethodV)(JNIEnv*, jobject, jclass,
+ jmethodID, va_list);
+ jlong (*CallNonvirtualLongMethodA)(JNIEnv*, jobject, jclass,
+ jmethodID, jvalue*);
+ jfloat (*CallNonvirtualFloatMethod)(JNIEnv*, jobject, jclass,
+ jmethodID, ...);
+ jfloat (*CallNonvirtualFloatMethodV)(JNIEnv*, jobject, jclass,
+ jmethodID, va_list);
+ jfloat (*CallNonvirtualFloatMethodA)(JNIEnv*, jobject, jclass,
+ jmethodID, jvalue*);
+ jdouble (*CallNonvirtualDoubleMethod)(JNIEnv*, jobject, jclass,
+ jmethodID, ...);
+ jdouble (*CallNonvirtualDoubleMethodV)(JNIEnv*, jobject, jclass,
+ jmethodID, va_list);
+ jdouble (*CallNonvirtualDoubleMethodA)(JNIEnv*, jobject, jclass,
+ jmethodID, jvalue*);
+ void (*CallNonvirtualVoidMethod)(JNIEnv*, jobject, jclass,
+ jmethodID, ...);
+ void (*CallNonvirtualVoidMethodV)(JNIEnv*, jobject, jclass,
+ jmethodID, va_list);
+ void (*CallNonvirtualVoidMethodA)(JNIEnv*, jobject, jclass,
+ jmethodID, jvalue*);
+
+ jfieldID (*GetFieldID)(JNIEnv*, jclass, const char*, const char*);
+
+ jobject (*GetObjectField)(JNIEnv*, jobject, jfieldID);
+ jboolean (*GetBooleanField)(JNIEnv*, jobject, jfieldID);
+ jbyte (*GetByteField)(JNIEnv*, jobject, jfieldID);
+ jchar (*GetCharField)(JNIEnv*, jobject, jfieldID);
+ jshort (*GetShortField)(JNIEnv*, jobject, jfieldID);
+ jint (*GetIntField)(JNIEnv*, jobject, jfieldID);
+ jlong (*GetLongField)(JNIEnv*, jobject, jfieldID);
+ jfloat (*GetFloatField)(JNIEnv*, jobject, jfieldID);
+ jdouble (*GetDoubleField)(JNIEnv*, jobject, jfieldID);
+
+ void (*SetObjectField)(JNIEnv*, jobject, jfieldID, jobject);
+ void (*SetBooleanField)(JNIEnv*, jobject, jfieldID, jboolean);
+ void (*SetByteField)(JNIEnv*, jobject, jfieldID, jbyte);
+ void (*SetCharField)(JNIEnv*, jobject, jfieldID, jchar);
+ void (*SetShortField)(JNIEnv*, jobject, jfieldID, jshort);
+ void (*SetIntField)(JNIEnv*, jobject, jfieldID, jint);
+ void (*SetLongField)(JNIEnv*, jobject, jfieldID, jlong);
+ void (*SetFloatField)(JNIEnv*, jobject, jfieldID, jfloat);
+ void (*SetDoubleField)(JNIEnv*, jobject, jfieldID, jdouble);
+
+ jmethodID (*GetStaticMethodID)(JNIEnv*, jclass, const char*, const char*);
+
+ jobject (*CallStaticObjectMethod)(JNIEnv*, jclass, jmethodID, ...);
+ jobject (*CallStaticObjectMethodV)(JNIEnv*, jclass, jmethodID, va_list);
+ jobject (*CallStaticObjectMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
+ jboolean (*CallStaticBooleanMethod)(JNIEnv*, jclass, jmethodID, ...);
+ jboolean (*CallStaticBooleanMethodV)(JNIEnv*, jclass, jmethodID,
+ va_list);
+ jboolean (*CallStaticBooleanMethodA)(JNIEnv*, jclass, jmethodID,
+ jvalue*);
+ jbyte (*CallStaticByteMethod)(JNIEnv*, jclass, jmethodID, ...);
+ jbyte (*CallStaticByteMethodV)(JNIEnv*, jclass, jmethodID, va_list);
+ jbyte (*CallStaticByteMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
+ jchar (*CallStaticCharMethod)(JNIEnv*, jclass, jmethodID, ...);
+ jchar (*CallStaticCharMethodV)(JNIEnv*, jclass, jmethodID, va_list);
+ jchar (*CallStaticCharMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
+ jshort (*CallStaticShortMethod)(JNIEnv*, jclass, jmethodID, ...);
+ jshort (*CallStaticShortMethodV)(JNIEnv*, jclass, jmethodID, va_list);
+ jshort (*CallStaticShortMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
+ jint (*CallStaticIntMethod)(JNIEnv*, jclass, jmethodID, ...);
+ jint (*CallStaticIntMethodV)(JNIEnv*, jclass, jmethodID, va_list);
+ jint (*CallStaticIntMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
+ jlong (*CallStaticLongMethod)(JNIEnv*, jclass, jmethodID, ...);
+ jlong (*CallStaticLongMethodV)(JNIEnv*, jclass, jmethodID, va_list);
+ jlong (*CallStaticLongMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
+ jfloat (*CallStaticFloatMethod)(JNIEnv*, jclass, jmethodID, ...);
+ jfloat (*CallStaticFloatMethodV)(JNIEnv*, jclass, jmethodID, va_list);
+ jfloat (*CallStaticFloatMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
+ jdouble (*CallStaticDoubleMethod)(JNIEnv*, jclass, jmethodID, ...);
+ jdouble (*CallStaticDoubleMethodV)(JNIEnv*, jclass, jmethodID, va_list);
+ jdouble (*CallStaticDoubleMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
+ void (*CallStaticVoidMethod)(JNIEnv*, jclass, jmethodID, ...);
+ void (*CallStaticVoidMethodV)(JNIEnv*, jclass, jmethodID, va_list);
+ void (*CallStaticVoidMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
+
+ jfieldID (*GetStaticFieldID)(JNIEnv*, jclass, const char*,
+ const char*);
+
+ jobject (*GetStaticObjectField)(JNIEnv*, jclass, jfieldID);
+ jboolean (*GetStaticBooleanField)(JNIEnv*, jclass, jfieldID);
+ jbyte (*GetStaticByteField)(JNIEnv*, jclass, jfieldID);
+ jchar (*GetStaticCharField)(JNIEnv*, jclass, jfieldID);
+ jshort (*GetStaticShortField)(JNIEnv*, jclass, jfieldID);
+ jint (*GetStaticIntField)(JNIEnv*, jclass, jfieldID);
+ jlong (*GetStaticLongField)(JNIEnv*, jclass, jfieldID);
+ jfloat (*GetStaticFloatField)(JNIEnv*, jclass, jfieldID);
+ jdouble (*GetStaticDoubleField)(JNIEnv*, jclass, jfieldID);
+
+ void (*SetStaticObjectField)(JNIEnv*, jclass, jfieldID, jobject);
+ void (*SetStaticBooleanField)(JNIEnv*, jclass, jfieldID, jboolean);
+ void (*SetStaticByteField)(JNIEnv*, jclass, jfieldID, jbyte);
+ void (*SetStaticCharField)(JNIEnv*, jclass, jfieldID, jchar);
+ void (*SetStaticShortField)(JNIEnv*, jclass, jfieldID, jshort);
+ void (*SetStaticIntField)(JNIEnv*, jclass, jfieldID, jint);
+ void (*SetStaticLongField)(JNIEnv*, jclass, jfieldID, jlong);
+ void (*SetStaticFloatField)(JNIEnv*, jclass, jfieldID, jfloat);
+ void (*SetStaticDoubleField)(JNIEnv*, jclass, jfieldID, jdouble);
+
+ jstring (*NewString)(JNIEnv*, const jchar*, jsize);
+ jsize (*GetStringLength)(JNIEnv*, jstring);
+ const jchar* (*GetStringChars)(JNIEnv*, jstring, jboolean*);
+ void (*ReleaseStringChars)(JNIEnv*, jstring, const jchar*);
+ jstring (*NewStringUTF)(JNIEnv*, const char*);
+ jsize (*GetStringUTFLength)(JNIEnv*, jstring);
+ /* JNI spec says this returns const jbyte*, but that's inconsistent */
+ const char* (*GetStringUTFChars)(JNIEnv*, jstring, jboolean*);
+ void (*ReleaseStringUTFChars)(JNIEnv*, jstring, const char*);
+ jsize (*GetArrayLength)(JNIEnv*, jarray);
+ jobjectArray (*NewObjectArray)(JNIEnv*, jsize, jclass, jobject);
+ jobject (*GetObjectArrayElement)(JNIEnv*, jobjectArray, jsize);
+ void (*SetObjectArrayElement)(JNIEnv*, jobjectArray, jsize, jobject);
+
+ jbooleanArray (*NewBooleanArray)(JNIEnv*, jsize);
+ jbyteArray (*NewByteArray)(JNIEnv*, jsize);
+ jcharArray (*NewCharArray)(JNIEnv*, jsize);
+ jshortArray (*NewShortArray)(JNIEnv*, jsize);
+ jintArray (*NewIntArray)(JNIEnv*, jsize);
+ jlongArray (*NewLongArray)(JNIEnv*, jsize);
+ jfloatArray (*NewFloatArray)(JNIEnv*, jsize);
+ jdoubleArray (*NewDoubleArray)(JNIEnv*, jsize);
+
+ jboolean* (*GetBooleanArrayElements)(JNIEnv*, jbooleanArray, jboolean*);
+ jbyte* (*GetByteArrayElements)(JNIEnv*, jbyteArray, jboolean*);
+ jchar* (*GetCharArrayElements)(JNIEnv*, jcharArray, jboolean*);
+ jshort* (*GetShortArrayElements)(JNIEnv*, jshortArray, jboolean*);
+ jint* (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*);
+ jlong* (*GetLongArrayElements)(JNIEnv*, jlongArray, jboolean*);
+ jfloat* (*GetFloatArrayElements)(JNIEnv*, jfloatArray, jboolean*);
+ jdouble* (*GetDoubleArrayElements)(JNIEnv*, jdoubleArray, jboolean*);
+
+ void (*ReleaseBooleanArrayElements)(JNIEnv*, jbooleanArray,
+ jboolean*, jint);
+ void (*ReleaseByteArrayElements)(JNIEnv*, jbyteArray,
+ jbyte*, jint);
+ void (*ReleaseCharArrayElements)(JNIEnv*, jcharArray,
+ jchar*, jint);
+ void (*ReleaseShortArrayElements)(JNIEnv*, jshortArray,
+ jshort*, jint);
+ void (*ReleaseIntArrayElements)(JNIEnv*, jintArray,
+ jint*, jint);
+ void (*ReleaseLongArrayElements)(JNIEnv*, jlongArray,
+ jlong*, jint);
+ void (*ReleaseFloatArrayElements)(JNIEnv*, jfloatArray,
+ jfloat*, jint);
+ void (*ReleaseDoubleArrayElements)(JNIEnv*, jdoubleArray,
+ jdouble*, jint);
+
+ void (*GetBooleanArrayRegion)(JNIEnv*, jbooleanArray,
+ jsize, jsize, jboolean*);
+ void (*GetByteArrayRegion)(JNIEnv*, jbyteArray,
+ jsize, jsize, jbyte*);
+ void (*GetCharArrayRegion)(JNIEnv*, jcharArray,
+ jsize, jsize, jchar*);
+ void (*GetShortArrayRegion)(JNIEnv*, jshortArray,
+ jsize, jsize, jshort*);
+ void (*GetIntArrayRegion)(JNIEnv*, jintArray,
+ jsize, jsize, jint*);
+ void (*GetLongArrayRegion)(JNIEnv*, jlongArray,
+ jsize, jsize, jlong*);
+ void (*GetFloatArrayRegion)(JNIEnv*, jfloatArray,
+ jsize, jsize, jfloat*);
+ void (*GetDoubleArrayRegion)(JNIEnv*, jdoubleArray,
+ jsize, jsize, jdouble*);
+
+ /* spec shows these without const; some jni.h do, some don't */
+ void (*SetBooleanArrayRegion)(JNIEnv*, jbooleanArray,
+ jsize, jsize, const jboolean*);
+ void (*SetByteArrayRegion)(JNIEnv*, jbyteArray,
+ jsize, jsize, const jbyte*);
+ void (*SetCharArrayRegion)(JNIEnv*, jcharArray,
+ jsize, jsize, const jchar*);
+ void (*SetShortArrayRegion)(JNIEnv*, jshortArray,
+ jsize, jsize, const jshort*);
+ void (*SetIntArrayRegion)(JNIEnv*, jintArray,
+ jsize, jsize, const jint*);
+ void (*SetLongArrayRegion)(JNIEnv*, jlongArray,
+ jsize, jsize, const jlong*);
+ void (*SetFloatArrayRegion)(JNIEnv*, jfloatArray,
+ jsize, jsize, const jfloat*);
+ void (*SetDoubleArrayRegion)(JNIEnv*, jdoubleArray,
+ jsize, jsize, const jdouble*);
+
+ jint (*RegisterNatives)(JNIEnv*, jclass, const JNINativeMethod*,
+ jint);
+ jint (*UnregisterNatives)(JNIEnv*, jclass);
+ jint (*MonitorEnter)(JNIEnv*, jobject);
+ jint (*MonitorExit)(JNIEnv*, jobject);
+ jint (*GetJavaVM)(JNIEnv*, JavaVM**);
+
+ void (*GetStringRegion)(JNIEnv*, jstring, jsize, jsize, jchar*);
+ void (*GetStringUTFRegion)(JNIEnv*, jstring, jsize, jsize, char*);
+
+ void* (*GetPrimitiveArrayCritical)(JNIEnv*, jarray, jboolean*);
+ void (*ReleasePrimitiveArrayCritical)(JNIEnv*, jarray, void*, jint);
+
+ const jchar* (*GetStringCritical)(JNIEnv*, jstring, jboolean*);
+ void (*ReleaseStringCritical)(JNIEnv*, jstring, const jchar*);
+
+ jweak (*NewWeakGlobalRef)(JNIEnv*, jobject);
+ void (*DeleteWeakGlobalRef)(JNIEnv*, jweak);
+
+ jboolean (*ExceptionCheck)(JNIEnv*);
+
+ jobject (*NewDirectByteBuffer)(JNIEnv*, void*, jlong);
+ void* (*GetDirectBufferAddress)(JNIEnv*, jobject);
+ jlong (*GetDirectBufferCapacity)(JNIEnv*, jobject);
+
+ /* added in JNI 1.6 */
+ jobjectRefType (*GetObjectRefType)(JNIEnv*, jobject);
+};
+
+/*
+ * C++ object wrapper.
+ *
+ * This is usually overlaid on a C struct whose first element is a
+ * JNINativeInterface*. We rely somewhat on compiler behavior.
+ */
+struct _JNIEnv {
+ /* do not rename this; it does not seem to be entirely opaque */
+ const struct JNINativeInterface* functions;
+
+#if defined(__cplusplus)
+
+ jint GetVersion()
+ { return functions->GetVersion(this); }
+
+ jclass DefineClass(const char *name, jobject loader, const jbyte* buf,
+ jsize bufLen)
+ { return functions->DefineClass(this, name, loader, buf, bufLen); }
+
+ jclass FindClass(const char* name)
+ { return functions->FindClass(this, name); }
+
+ jmethodID FromReflectedMethod(jobject method)
+ { return functions->FromReflectedMethod(this, method); }
+
+ jfieldID FromReflectedField(jobject field)
+ { return functions->FromReflectedField(this, field); }
+
+ jobject ToReflectedMethod(jclass cls, jmethodID methodID, jboolean isStatic)
+ { return functions->ToReflectedMethod(this, cls, methodID, isStatic); }
+
+ jclass GetSuperclass(jclass clazz)
+ { return functions->GetSuperclass(this, clazz); }
+
+ jboolean IsAssignableFrom(jclass clazz1, jclass clazz2)
+ { return functions->IsAssignableFrom(this, clazz1, clazz2); }
+
+ jobject ToReflectedField(jclass cls, jfieldID fieldID, jboolean isStatic)
+ { return functions->ToReflectedField(this, cls, fieldID, isStatic); }
+
+ jint Throw(jthrowable obj)
+ { return functions->Throw(this, obj); }
+
+ jint ThrowNew(jclass clazz, const char* message)
+ { return functions->ThrowNew(this, clazz, message); }
+
+ jthrowable ExceptionOccurred()
+ { return functions->ExceptionOccurred(this); }
+
+ void ExceptionDescribe()
+ { functions->ExceptionDescribe(this); }
+
+ void ExceptionClear()
+ { functions->ExceptionClear(this); }
+
+ void FatalError(const char* msg)
+ { functions->FatalError(this, msg); }
+
+ jint PushLocalFrame(jint capacity)
+ { return functions->PushLocalFrame(this, capacity); }
+
+ jobject PopLocalFrame(jobject result)
+ { return functions->PopLocalFrame(this, result); }
+
+ jobject NewGlobalRef(jobject obj)
+ { return functions->NewGlobalRef(this, obj); }
+
+ void DeleteGlobalRef(jobject globalRef)
+ { functions->DeleteGlobalRef(this, globalRef); }
+
+ void DeleteLocalRef(jobject localRef)
+ { functions->DeleteLocalRef(this, localRef); }
+
+ jboolean IsSameObject(jobject ref1, jobject ref2)
+ { return functions->IsSameObject(this, ref1, ref2); }
+
+ jobject NewLocalRef(jobject ref)
+ { return functions->NewLocalRef(this, ref); }
+
+ jint EnsureLocalCapacity(jint capacity)
+ { return functions->EnsureLocalCapacity(this, capacity); }
+
+ jobject AllocObject(jclass clazz)
+ { return functions->AllocObject(this, clazz); }
+
+ jobject NewObject(jclass clazz, jmethodID methodID, ...)
+ {
+ va_list args;
+ va_start(args, methodID);
+ jobject result = functions->NewObjectV(this, clazz, methodID, args);
+ va_end(args);
+ return result;
+ }
+
+ jobject NewObjectV(jclass clazz, jmethodID methodID, va_list args)
+ { return functions->NewObjectV(this, clazz, methodID, args); }
+
+ jobject NewObjectA(jclass clazz, jmethodID methodID, jvalue* args)
+ { return functions->NewObjectA(this, clazz, methodID, args); }
+
+ jclass GetObjectClass(jobject obj)
+ { return functions->GetObjectClass(this, obj); }
+
+ jboolean IsInstanceOf(jobject obj, jclass clazz)
+ { return functions->IsInstanceOf(this, obj, clazz); }
+
+ jmethodID GetMethodID(jclass clazz, const char* name, const char* sig)
+ { return functions->GetMethodID(this, clazz, name, sig); }
+
+#define CALL_TYPE_METHOD(_jtype, _jname) \
+ _jtype Call##_jname##Method(jobject obj, jmethodID methodID, ...) \
+ { \
+ _jtype result; \
+ va_list args; \
+ va_start(args, methodID); \
+ result = functions->Call##_jname##MethodV(this, obj, methodID, \
+ args); \
+ va_end(args); \
+ return result; \
+ }
+#define CALL_TYPE_METHODV(_jtype, _jname) \
+ _jtype Call##_jname##MethodV(jobject obj, jmethodID methodID, \
+ va_list args) \
+ { return functions->Call##_jname##MethodV(this, obj, methodID, args); }
+#define CALL_TYPE_METHODA(_jtype, _jname) \
+ _jtype Call##_jname##MethodA(jobject obj, jmethodID methodID, \
+ jvalue* args) \
+ { return functions->Call##_jname##MethodA(this, obj, methodID, args); }
+
+#define CALL_TYPE(_jtype, _jname) \
+ CALL_TYPE_METHOD(_jtype, _jname) \
+ CALL_TYPE_METHODV(_jtype, _jname) \
+ CALL_TYPE_METHODA(_jtype, _jname)
+
+ CALL_TYPE(jobject, Object)
+ CALL_TYPE(jboolean, Boolean)
+ CALL_TYPE(jbyte, Byte)
+ CALL_TYPE(jchar, Char)
+ CALL_TYPE(jshort, Short)
+ CALL_TYPE(jint, Int)
+ CALL_TYPE(jlong, Long)
+ CALL_TYPE(jfloat, Float)
+ CALL_TYPE(jdouble, Double)
+
+ void CallVoidMethod(jobject obj, jmethodID methodID, ...)
+ {
+ va_list args;
+ va_start(args, methodID);
+ functions->CallVoidMethodV(this, obj, methodID, args);
+ va_end(args);
+ }
+ void CallVoidMethodV(jobject obj, jmethodID methodID, va_list args)
+ { functions->CallVoidMethodV(this, obj, methodID, args); }
+ void CallVoidMethodA(jobject obj, jmethodID methodID, jvalue* args)
+ { functions->CallVoidMethodA(this, obj, methodID, args); }
+
+#define CALL_NONVIRT_TYPE_METHOD(_jtype, _jname) \
+ _jtype CallNonvirtual##_jname##Method(jobject obj, jclass clazz, \
+ jmethodID methodID, ...) \
+ { \
+ _jtype result; \
+ va_list args; \
+ va_start(args, methodID); \
+ result = functions->CallNonvirtual##_jname##MethodV(this, obj, \
+ clazz, methodID, args); \
+ va_end(args); \
+ return result; \
+ }
+#define CALL_NONVIRT_TYPE_METHODV(_jtype, _jname) \
+ _jtype CallNonvirtual##_jname##MethodV(jobject obj, jclass clazz, \
+ jmethodID methodID, va_list args) \
+ { return functions->CallNonvirtual##_jname##MethodV(this, obj, clazz, \
+ methodID, args); }
+#define CALL_NONVIRT_TYPE_METHODA(_jtype, _jname) \
+ _jtype CallNonvirtual##_jname##MethodA(jobject obj, jclass clazz, \
+ jmethodID methodID, jvalue* args) \
+ { return functions->CallNonvirtual##_jname##MethodA(this, obj, clazz, \
+ methodID, args); }
+
+#define CALL_NONVIRT_TYPE(_jtype, _jname) \
+ CALL_NONVIRT_TYPE_METHOD(_jtype, _jname) \
+ CALL_NONVIRT_TYPE_METHODV(_jtype, _jname) \
+ CALL_NONVIRT_TYPE_METHODA(_jtype, _jname)
+
+ CALL_NONVIRT_TYPE(jobject, Object)
+ CALL_NONVIRT_TYPE(jboolean, Boolean)
+ CALL_NONVIRT_TYPE(jbyte, Byte)
+ CALL_NONVIRT_TYPE(jchar, Char)
+ CALL_NONVIRT_TYPE(jshort, Short)
+ CALL_NONVIRT_TYPE(jint, Int)
+ CALL_NONVIRT_TYPE(jlong, Long)
+ CALL_NONVIRT_TYPE(jfloat, Float)
+ CALL_NONVIRT_TYPE(jdouble, Double)
+
+ void CallNonvirtualVoidMethod(jobject obj, jclass clazz,
+ jmethodID methodID, ...)
+ {
+ va_list args;
+ va_start(args, methodID);
+ functions->CallNonvirtualVoidMethodV(this, obj, clazz, methodID, args);
+ va_end(args);
+ }
+ void CallNonvirtualVoidMethodV(jobject obj, jclass clazz,
+ jmethodID methodID, va_list args)
+ { functions->CallNonvirtualVoidMethodV(this, obj, clazz, methodID, args); }
+ void CallNonvirtualVoidMethodA(jobject obj, jclass clazz,
+ jmethodID methodID, jvalue* args)
+ { functions->CallNonvirtualVoidMethodA(this, obj, clazz, methodID, args); }
+
+ jfieldID GetFieldID(jclass clazz, const char* name, const char* sig)
+ { return functions->GetFieldID(this, clazz, name, sig); }
+
+ jobject GetObjectField(jobject obj, jfieldID fieldID)
+ { return functions->GetObjectField(this, obj, fieldID); }
+ jboolean GetBooleanField(jobject obj, jfieldID fieldID)
+ { return functions->GetBooleanField(this, obj, fieldID); }
+ jbyte GetByteField(jobject obj, jfieldID fieldID)
+ { return functions->GetByteField(this, obj, fieldID); }
+ jchar GetCharField(jobject obj, jfieldID fieldID)
+ { return functions->GetCharField(this, obj, fieldID); }
+ jshort GetShortField(jobject obj, jfieldID fieldID)
+ { return functions->GetShortField(this, obj, fieldID); }
+ jint GetIntField(jobject obj, jfieldID fieldID)
+ { return functions->GetIntField(this, obj, fieldID); }
+ jlong GetLongField(jobject obj, jfieldID fieldID)
+ { return functions->GetLongField(this, obj, fieldID); }
+ jfloat GetFloatField(jobject obj, jfieldID fieldID)
+ { return functions->GetFloatField(this, obj, fieldID); }
+ jdouble GetDoubleField(jobject obj, jfieldID fieldID)
+ { return functions->GetDoubleField(this, obj, fieldID); }
+
+ void SetObjectField(jobject obj, jfieldID fieldID, jobject value)
+ { functions->SetObjectField(this, obj, fieldID, value); }
+ void SetBooleanField(jobject obj, jfieldID fieldID, jboolean value)
+ { functions->SetBooleanField(this, obj, fieldID, value); }
+ void SetByteField(jobject obj, jfieldID fieldID, jbyte value)
+ { functions->SetByteField(this, obj, fieldID, value); }
+ void SetCharField(jobject obj, jfieldID fieldID, jchar value)
+ { functions->SetCharField(this, obj, fieldID, value); }
+ void SetShortField(jobject obj, jfieldID fieldID, jshort value)
+ { functions->SetShortField(this, obj, fieldID, value); }
+ void SetIntField(jobject obj, jfieldID fieldID, jint value)
+ { functions->SetIntField(this, obj, fieldID, value); }
+ void SetLongField(jobject obj, jfieldID fieldID, jlong value)
+ { functions->SetLongField(this, obj, fieldID, value); }
+ void SetFloatField(jobject obj, jfieldID fieldID, jfloat value)
+ { functions->SetFloatField(this, obj, fieldID, value); }
+ void SetDoubleField(jobject obj, jfieldID fieldID, jdouble value)
+ { functions->SetDoubleField(this, obj, fieldID, value); }
+
+ jmethodID GetStaticMethodID(jclass clazz, const char* name, const char* sig)
+ { return functions->GetStaticMethodID(this, clazz, name, sig); }
+
+#define CALL_STATIC_TYPE_METHOD(_jtype, _jname) \
+ _jtype CallStatic##_jname##Method(jclass clazz, jmethodID methodID, \
+ ...) \
+ { \
+ _jtype result; \
+ va_list args; \
+ va_start(args, methodID); \
+ result = functions->CallStatic##_jname##MethodV(this, clazz, \
+ methodID, args); \
+ va_end(args); \
+ return result; \
+ }
+#define CALL_STATIC_TYPE_METHODV(_jtype, _jname) \
+ _jtype CallStatic##_jname##MethodV(jclass clazz, jmethodID methodID, \
+ va_list args) \
+ { return functions->CallStatic##_jname##MethodV(this, clazz, methodID, \
+ args); }
+#define CALL_STATIC_TYPE_METHODA(_jtype, _jname) \
+ _jtype CallStatic##_jname##MethodA(jclass clazz, jmethodID methodID, \
+ jvalue* args) \
+ { return functions->CallStatic##_jname##MethodA(this, clazz, methodID, \
+ args); }
+
+#define CALL_STATIC_TYPE(_jtype, _jname) \
+ CALL_STATIC_TYPE_METHOD(_jtype, _jname) \
+ CALL_STATIC_TYPE_METHODV(_jtype, _jname) \
+ CALL_STATIC_TYPE_METHODA(_jtype, _jname)
+
+ CALL_STATIC_TYPE(jobject, Object)
+ CALL_STATIC_TYPE(jboolean, Boolean)
+ CALL_STATIC_TYPE(jbyte, Byte)
+ CALL_STATIC_TYPE(jchar, Char)
+ CALL_STATIC_TYPE(jshort, Short)
+ CALL_STATIC_TYPE(jint, Int)
+ CALL_STATIC_TYPE(jlong, Long)
+ CALL_STATIC_TYPE(jfloat, Float)
+ CALL_STATIC_TYPE(jdouble, Double)
+
+ void CallStaticVoidMethod(jclass clazz, jmethodID methodID, ...)
+ {
+ va_list args;
+ va_start(args, methodID);
+ functions->CallStaticVoidMethodV(this, clazz, methodID, args);
+ va_end(args);
+ }
+ void CallStaticVoidMethodV(jclass clazz, jmethodID methodID, va_list args)
+ { functions->CallStaticVoidMethodV(this, clazz, methodID, args); }
+ void CallStaticVoidMethodA(jclass clazz, jmethodID methodID, jvalue* args)
+ { functions->CallStaticVoidMethodA(this, clazz, methodID, args); }
+
+ jfieldID GetStaticFieldID(jclass clazz, const char* name, const char* sig)
+ { return functions->GetStaticFieldID(this, clazz, name, sig); }
+
+ jobject GetStaticObjectField(jclass clazz, jfieldID fieldID)
+ { return functions->GetStaticObjectField(this, clazz, fieldID); }
+ jboolean GetStaticBooleanField(jclass clazz, jfieldID fieldID)
+ { return functions->GetStaticBooleanField(this, clazz, fieldID); }
+ jbyte GetStaticByteField(jclass clazz, jfieldID fieldID)
+ { return functions->GetStaticByteField(this, clazz, fieldID); }
+ jchar GetStaticCharField(jclass clazz, jfieldID fieldID)
+ { return functions->GetStaticCharField(this, clazz, fieldID); }
+ jshort GetStaticShortField(jclass clazz, jfieldID fieldID)
+ { return functions->GetStaticShortField(this, clazz, fieldID); }
+ jint GetStaticIntField(jclass clazz, jfieldID fieldID)
+ { return functions->GetStaticIntField(this, clazz, fieldID); }
+ jlong GetStaticLongField(jclass clazz, jfieldID fieldID)
+ { return functions->GetStaticLongField(this, clazz, fieldID); }
+ jfloat GetStaticFloatField(jclass clazz, jfieldID fieldID)
+ { return functions->GetStaticFloatField(this, clazz, fieldID); }
+ jdouble GetStaticDoubleField(jclass clazz, jfieldID fieldID)
+ { return functions->GetStaticDoubleField(this, clazz, fieldID); }
+
+ void SetStaticObjectField(jclass clazz, jfieldID fieldID, jobject value)
+ { functions->SetStaticObjectField(this, clazz, fieldID, value); }
+ void SetStaticBooleanField(jclass clazz, jfieldID fieldID, jboolean value)
+ { functions->SetStaticBooleanField(this, clazz, fieldID, value); }
+ void SetStaticByteField(jclass clazz, jfieldID fieldID, jbyte value)
+ { functions->SetStaticByteField(this, clazz, fieldID, value); }
+ void SetStaticCharField(jclass clazz, jfieldID fieldID, jchar value)
+ { functions->SetStaticCharField(this, clazz, fieldID, value); }
+ void SetStaticShortField(jclass clazz, jfieldID fieldID, jshort value)
+ { functions->SetStaticShortField(this, clazz, fieldID, value); }
+ void SetStaticIntField(jclass clazz, jfieldID fieldID, jint value)
+ { functions->SetStaticIntField(this, clazz, fieldID, value); }
+ void SetStaticLongField(jclass clazz, jfieldID fieldID, jlong value)
+ { functions->SetStaticLongField(this, clazz, fieldID, value); }
+ void SetStaticFloatField(jclass clazz, jfieldID fieldID, jfloat value)
+ { functions->SetStaticFloatField(this, clazz, fieldID, value); }
+ void SetStaticDoubleField(jclass clazz, jfieldID fieldID, jdouble value)
+ { functions->SetStaticDoubleField(this, clazz, fieldID, value); }
+
+ jstring NewString(const jchar* unicodeChars, jsize len)
+ { return functions->NewString(this, unicodeChars, len); }
+
+ jsize GetStringLength(jstring string)
+ { return functions->GetStringLength(this, string); }
+
+ const jchar* GetStringChars(jstring string, jboolean* isCopy)
+ { return functions->GetStringChars(this, string, isCopy); }
+
+ void ReleaseStringChars(jstring string, const jchar* chars)
+ { functions->ReleaseStringChars(this, string, chars); }
+
+ jstring NewStringUTF(const char* bytes)
+ { return functions->NewStringUTF(this, bytes); }
+
+ jsize GetStringUTFLength(jstring string)
+ { return functions->GetStringUTFLength(this, string); }
+
+ const char* GetStringUTFChars(jstring string, jboolean* isCopy)
+ { return functions->GetStringUTFChars(this, string, isCopy); }
+
+ void ReleaseStringUTFChars(jstring string, const char* utf)
+ { functions->ReleaseStringUTFChars(this, string, utf); }
+
+ jsize GetArrayLength(jarray array)
+ { return functions->GetArrayLength(this, array); }
+
+ jobjectArray NewObjectArray(jsize length, jclass elementClass,
+ jobject initialElement)
+ { return functions->NewObjectArray(this, length, elementClass,
+ initialElement); }
+
+ jobject GetObjectArrayElement(jobjectArray array, jsize index)
+ { return functions->GetObjectArrayElement(this, array, index); }
+
+ void SetObjectArrayElement(jobjectArray array, jsize index, jobject value)
+ { functions->SetObjectArrayElement(this, array, index, value); }
+
+ jbooleanArray NewBooleanArray(jsize length)
+ { return functions->NewBooleanArray(this, length); }
+ jbyteArray NewByteArray(jsize length)
+ { return functions->NewByteArray(this, length); }
+ jcharArray NewCharArray(jsize length)
+ { return functions->NewCharArray(this, length); }
+ jshortArray NewShortArray(jsize length)
+ { return functions->NewShortArray(this, length); }
+ jintArray NewIntArray(jsize length)
+ { return functions->NewIntArray(this, length); }
+ jlongArray NewLongArray(jsize length)
+ { return functions->NewLongArray(this, length); }
+ jfloatArray NewFloatArray(jsize length)
+ { return functions->NewFloatArray(this, length); }
+ jdoubleArray NewDoubleArray(jsize length)
+ { return functions->NewDoubleArray(this, length); }
+
+ jboolean* GetBooleanArrayElements(jbooleanArray array, jboolean* isCopy)
+ { return functions->GetBooleanArrayElements(this, array, isCopy); }
+ jbyte* GetByteArrayElements(jbyteArray array, jboolean* isCopy)
+ { return functions->GetByteArrayElements(this, array, isCopy); }
+ jchar* GetCharArrayElements(jcharArray array, jboolean* isCopy)
+ { return functions->GetCharArrayElements(this, array, isCopy); }
+ jshort* GetShortArrayElements(jshortArray array, jboolean* isCopy)
+ { return functions->GetShortArrayElements(this, array, isCopy); }
+ jint* GetIntArrayElements(jintArray array, jboolean* isCopy)
+ { return functions->GetIntArrayElements(this, array, isCopy); }
+ jlong* GetLongArrayElements(jlongArray array, jboolean* isCopy)
+ { return functions->GetLongArrayElements(this, array, isCopy); }
+ jfloat* GetFloatArrayElements(jfloatArray array, jboolean* isCopy)
+ { return functions->GetFloatArrayElements(this, array, isCopy); }
+ jdouble* GetDoubleArrayElements(jdoubleArray array, jboolean* isCopy)
+ { return functions->GetDoubleArrayElements(this, array, isCopy); }
+
+ void ReleaseBooleanArrayElements(jbooleanArray array, jboolean* elems,
+ jint mode)
+ { functions->ReleaseBooleanArrayElements(this, array, elems, mode); }
+ void ReleaseByteArrayElements(jbyteArray array, jbyte* elems,
+ jint mode)
+ { functions->ReleaseByteArrayElements(this, array, elems, mode); }
+ void ReleaseCharArrayElements(jcharArray array, jchar* elems,
+ jint mode)
+ { functions->ReleaseCharArrayElements(this, array, elems, mode); }
+ void ReleaseShortArrayElements(jshortArray array, jshort* elems,
+ jint mode)
+ { functions->ReleaseShortArrayElements(this, array, elems, mode); }
+ void ReleaseIntArrayElements(jintArray array, jint* elems,
+ jint mode)
+ { functions->ReleaseIntArrayElements(this, array, elems, mode); }
+ void ReleaseLongArrayElements(jlongArray array, jlong* elems,
+ jint mode)
+ { functions->ReleaseLongArrayElements(this, array, elems, mode); }
+ void ReleaseFloatArrayElements(jfloatArray array, jfloat* elems,
+ jint mode)
+ { functions->ReleaseFloatArrayElements(this, array, elems, mode); }
+ void ReleaseDoubleArrayElements(jdoubleArray array, jdouble* elems,
+ jint mode)
+ { functions->ReleaseDoubleArrayElements(this, array, elems, mode); }
+
+ void GetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len,
+ jboolean* buf)
+ { functions->GetBooleanArrayRegion(this, array, start, len, buf); }
+ void GetByteArrayRegion(jbyteArray array, jsize start, jsize len,
+ jbyte* buf)
+ { functions->GetByteArrayRegion(this, array, start, len, buf); }
+ void GetCharArrayRegion(jcharArray array, jsize start, jsize len,
+ jchar* buf)
+ { functions->GetCharArrayRegion(this, array, start, len, buf); }
+ void GetShortArrayRegion(jshortArray array, jsize start, jsize len,
+ jshort* buf)
+ { functions->GetShortArrayRegion(this, array, start, len, buf); }
+ void GetIntArrayRegion(jintArray array, jsize start, jsize len,
+ jint* buf)
+ { functions->GetIntArrayRegion(this, array, start, len, buf); }
+ void GetLongArrayRegion(jlongArray array, jsize start, jsize len,
+ jlong* buf)
+ { functions->GetLongArrayRegion(this, array, start, len, buf); }
+ void GetFloatArrayRegion(jfloatArray array, jsize start, jsize len,
+ jfloat* buf)
+ { functions->GetFloatArrayRegion(this, array, start, len, buf); }
+ void GetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len,
+ jdouble* buf)
+ { functions->GetDoubleArrayRegion(this, array, start, len, buf); }
+
+ void SetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len,
+ const jboolean* buf)
+ { functions->SetBooleanArrayRegion(this, array, start, len, buf); }
+ void SetByteArrayRegion(jbyteArray array, jsize start, jsize len,
+ const jbyte* buf)
+ { functions->SetByteArrayRegion(this, array, start, len, buf); }
+ void SetCharArrayRegion(jcharArray array, jsize start, jsize len,
+ const jchar* buf)
+ { functions->SetCharArrayRegion(this, array, start, len, buf); }
+ void SetShortArrayRegion(jshortArray array, jsize start, jsize len,
+ const jshort* buf)
+ { functions->SetShortArrayRegion(this, array, start, len, buf); }
+ void SetIntArrayRegion(jintArray array, jsize start, jsize len,
+ const jint* buf)
+ { functions->SetIntArrayRegion(this, array, start, len, buf); }
+ void SetLongArrayRegion(jlongArray array, jsize start, jsize len,
+ const jlong* buf)
+ { functions->SetLongArrayRegion(this, array, start, len, buf); }
+ void SetFloatArrayRegion(jfloatArray array, jsize start, jsize len,
+ const jfloat* buf)
+ { functions->SetFloatArrayRegion(this, array, start, len, buf); }
+ void SetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len,
+ const jdouble* buf)
+ { functions->SetDoubleArrayRegion(this, array, start, len, buf); }
+
+ jint RegisterNatives(jclass clazz, const JNINativeMethod* methods,
+ jint nMethods)
+ { return functions->RegisterNatives(this, clazz, methods, nMethods); }
+
+ jint UnregisterNatives(jclass clazz)
+ { return functions->UnregisterNatives(this, clazz); }
+
+ jint MonitorEnter(jobject obj)
+ { return functions->MonitorEnter(this, obj); }
+
+ jint MonitorExit(jobject obj)
+ { return functions->MonitorExit(this, obj); }
+
+ jint GetJavaVM(JavaVM** vm)
+ { return functions->GetJavaVM(this, vm); }
+
+ void GetStringRegion(jstring str, jsize start, jsize len, jchar* buf)
+ { functions->GetStringRegion(this, str, start, len, buf); }
+
+ void GetStringUTFRegion(jstring str, jsize start, jsize len, char* buf)
+ { return functions->GetStringUTFRegion(this, str, start, len, buf); }
+
+ void* GetPrimitiveArrayCritical(jarray array, jboolean* isCopy)
+ { return functions->GetPrimitiveArrayCritical(this, array, isCopy); }
+
+ void ReleasePrimitiveArrayCritical(jarray array, void* carray, jint mode)
+ { functions->ReleasePrimitiveArrayCritical(this, array, carray, mode); }
+
+ const jchar* GetStringCritical(jstring string, jboolean* isCopy)
+ { return functions->GetStringCritical(this, string, isCopy); }
+
+ void ReleaseStringCritical(jstring string, const jchar* carray)
+ { functions->ReleaseStringCritical(this, string, carray); }
+
+ jweak NewWeakGlobalRef(jobject obj)
+ { return functions->NewWeakGlobalRef(this, obj); }
+
+ void DeleteWeakGlobalRef(jweak obj)
+ { functions->DeleteWeakGlobalRef(this, obj); }
+
+ jboolean ExceptionCheck()
+ { return functions->ExceptionCheck(this); }
+
+ jobject NewDirectByteBuffer(void* address, jlong capacity)
+ { return functions->NewDirectByteBuffer(this, address, capacity); }
+
+ void* GetDirectBufferAddress(jobject buf)
+ { return functions->GetDirectBufferAddress(this, buf); }
+
+ jlong GetDirectBufferCapacity(jobject buf)
+ { return functions->GetDirectBufferCapacity(this, buf); }
+
+ /* added in JNI 1.6 */
+ jobjectRefType GetObjectRefType(jobject obj)
+ { return functions->GetObjectRefType(this, obj); }
+#endif /*__cplusplus*/
+};
+
+
+/*
+ * JNI invocation interface.
+ */
+struct JNIInvokeInterface {
+ void* reserved0;
+ void* reserved1;
+ void* reserved2;
+
+ jint (*DestroyJavaVM)(JavaVM*);
+ jint (*AttachCurrentThread)(JavaVM*, JNIEnv**, void*);
+ jint (*DetachCurrentThread)(JavaVM*);
+ jint (*GetEnv)(JavaVM*, void**, jint);
+ jint (*AttachCurrentThreadAsDaemon)(JavaVM*, JNIEnv**, void*);
+};
+
+/*
+ * C++ version.
+ */
+struct _JavaVM {
+ const struct JNIInvokeInterface* functions;
+
+#if defined(__cplusplus)
+ jint DestroyJavaVM()
+ { return functions->DestroyJavaVM(this); }
+ jint AttachCurrentThread(JNIEnv** p_env, void* thr_args)
+ { return functions->AttachCurrentThread(this, p_env, thr_args); }
+ jint DetachCurrentThread()
+ { return functions->DetachCurrentThread(this); }
+ jint GetEnv(void** env, jint version)
+ { return functions->GetEnv(this, env, version); }
+ jint AttachCurrentThreadAsDaemon(JNIEnv** p_env, void* thr_args)
+ { return functions->AttachCurrentThreadAsDaemon(this, p_env, thr_args); }
+#endif /*__cplusplus*/
+};
+
+struct JavaVMAttachArgs {
+ jint version; /* must be >= JNI_VERSION_1_2 */
+ const char* name; /* NULL or name of thread as modified UTF-8 str */
+ jobject group; /* global ref of a ThreadGroup object, or NULL */
+};
+typedef struct JavaVMAttachArgs JavaVMAttachArgs;
+
+/*
+ * JNI 1.2+ initialization. (As of 1.6, the pre-1.2 structures are no
+ * longer supported.)
+ */
+typedef struct JavaVMOption {
+ const char* optionString;
+ void* extraInfo;
+} JavaVMOption;
+
+typedef struct JavaVMInitArgs {
+ jint version; /* use JNI_VERSION_1_2 or later */
+
+ jint nOptions;
+ JavaVMOption* options;
+ jboolean ignoreUnrecognized;
+} JavaVMInitArgs;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * VM initialization functions.
+ *
+ * Note these are the only symbols exported for JNI by the VM.
+ */
+jint JNI_GetDefaultJavaVMInitArgs(void*);
+jint JNI_CreateJavaVM(JavaVM**, JNIEnv**, void*);
+jint JNI_GetCreatedJavaVMs(JavaVM**, jsize, jsize*);
+
+/*
+ * Prototypes for functions exported by loadable shared libs. These are
+ * called by JNI, not provided by JNI.
+ */
+jint JNI_OnLoad(JavaVM* vm, void* reserved);
+void JNI_OnUnload(JavaVM* vm, void* reserved);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+/*
+ * Manifest constants.
+ */
+#define JNI_FALSE 0
+#define JNI_TRUE 1
+
+#define JNI_VERSION_1_1 0x00010001
+#define JNI_VERSION_1_2 0x00010002
+#define JNI_VERSION_1_4 0x00010004
+#define JNI_VERSION_1_6 0x00010006
+
+#define JNI_OK (0) /* no error */
+#define JNI_ERR (-1) /* generic error */
+#define JNI_EDETACHED (-2) /* thread detached from the VM */
+#define JNI_EVERSION (-3) /* JNI version error */
+
+#define JNI_COMMIT 1 /* copy content, do not free buffer */
+#define JNI_ABORT 2 /* free buffer w/o copying back */
+
+/* need these for Windows-aware headers */
+#define JNIIMPORT
+#define JNIEXPORT
+#define JNICALL
+
+#endif /*_JNI_H*/
diff --git a/tests/001-nop/build b/tests/001-nop/build
new file mode 100644
index 0000000..5233a2d
--- /dev/null
+++ b/tests/001-nop/build
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+# Nothing to do here.
diff --git a/tests/001-nop/expected.txt b/tests/001-nop/expected.txt
new file mode 100644
index 0000000..80a233e
--- /dev/null
+++ b/tests/001-nop/expected.txt
@@ -0,0 +1 @@
+Blort.
diff --git a/tests/001-nop/info.txt b/tests/001-nop/info.txt
new file mode 100644
index 0000000..9942f10
--- /dev/null
+++ b/tests/001-nop/info.txt
@@ -0,0 +1,2 @@
+This is a sample no-op test, which does at least serve to verify that the
+test harness is working.
diff --git a/tests/001-nop/run b/tests/001-nop/run
new file mode 100644
index 0000000..210296b
--- /dev/null
+++ b/tests/001-nop/run
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+echo "Blort."
diff --git a/tests/002-sleep/expected.txt b/tests/002-sleep/expected.txt
new file mode 100644
index 0000000..f994ce5
--- /dev/null
+++ b/tests/002-sleep/expected.txt
@@ -0,0 +1,2 @@
+Sleeping 1000 msec...
+Done sleeping
diff --git a/tests/002-sleep/info.txt b/tests/002-sleep/info.txt
new file mode 100644
index 0000000..9a0afe9
--- /dev/null
+++ b/tests/002-sleep/info.txt
@@ -0,0 +1,3 @@
+Test that Thread.sleep() operates reasonably. This test is actually
+mostly meant as an easy thing to modify in order to test other things
+in an ad-hoc way.
diff --git a/tests/002-sleep/src/Main.java b/tests/002-sleep/src/Main.java
new file mode 100644
index 0000000..c1a2d83
--- /dev/null
+++ b/tests/002-sleep/src/Main.java
@@ -0,0 +1,22 @@
+public class Main {
+ static public void main(String[] args) throws Exception {
+ int millis = 1000;
+
+ if (args.length != 0) {
+ millis = Integer.parseInt(args[0]);
+ }
+
+ System.out.println("Sleeping " + millis + " msec...");
+
+ long start = System.currentTimeMillis();
+ Thread.sleep(millis);
+ long elapsed = System.currentTimeMillis() - start;
+ long offBy = Math.abs(elapsed - millis);
+
+ System.out.println("Done sleeping");
+
+ if (offBy > 250) {
+ System.out.println("Actually slept about " + elapsed + " msec...");
+ }
+ }
+}
diff --git a/tests/003-omnibus-opcodes/build b/tests/003-omnibus-opcodes/build
new file mode 100644
index 0000000..9eb5ed3
--- /dev/null
+++ b/tests/003-omnibus-opcodes/build
@@ -0,0 +1,26 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Stop if something fails.
+set -e
+
+mkdir classes
+${JAVAC} -d classes `find src -name '*.java'`
+rm classes/UnresClass.class
+${JAVAC} -d classes `find src2 -name '*.java'`
+
+dx -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex classes
+zip test.jar classes.dex
diff --git a/tests/003-omnibus-opcodes/expected.txt b/tests/003-omnibus-opcodes/expected.txt
new file mode 100644
index 0000000..4895dc3
--- /dev/null
+++ b/tests/003-omnibus-opcodes/expected.txt
@@ -0,0 +1,74 @@
+(assertions are enabled)
+InstField assign...
+InstField check...
+InstField.nullCheck
+StaticField assign...
+StaticField check...
+IntMath.shiftTest1
+IntMath.shiftTest2
+IntMath.unsignedShiftTest
+IntMath.convTest
+IntMath.charSubTest
+IntMath.intOperTest
+IntMath.intOperCheck
+IntMath.longOperTest
+IntMath.longOperCheck
+IntMath.lit16Test
+IntMath.lit8Test
+IntMath.intShiftTest
+IntMath.intShiftCheck
+IntMath.longShiftTest
+IntMath.longShiftCheck
+IntMath.truncateTest
+IntMath.divideByZero
+IntMath.bigDivideOverflow
+IntMath.checkConsts
+IntMath.jlmTests
+FloatMath.convTest
+FloatMath.floatOperTest
+FloatMath.doubleOperTest
+FloatMath.checkConvI
+FloatMath.checkConvL
+FloatMath.checkConvF
+ 0: -2.0054409E9
+ 1: -8.613303E18
+ 2: -3.1415927
+-2.0054409E9, -8.6133031E18, -3.1415927
+FloatMath.checkConvD
+ 0: -2.005440939E9
+ 1: -8.613303245920329E18
+ 2: 123.45600128173828
+-2.005440939E9, -8.6133032459203287E18, 123.4560012817382
+FloatMath.checkConsts
+FloatMath.jlmTests
+IntMath.testIntCompare
+IntMath.testLongCompare
+IntMath.testFloatCompare
+IntMath.testDoubleCompare
+Monitor.run
+Switch.testSwitch
+Array check...
+Array.checkRange32
+Array.checkRange64
+Array.checkNegAlloc
+Classes.checkCast
+Classes.arrayInstance
+Goto.smallGoto
+Goto.smallGoto
+Goto.mediumGoto
+Goto.mediumGoto
+Goto.bigGoto
+Goto.bigGoto
+ MethodCallBase ctor
+ MethodCall ctor
+MethodCalls.manyArgs
+Throw.one
+Throw.twoA
+Throw.twoN
+Throw.rethrow
+UnresTest1...
+UnresTest1...
+UnresTest2...
+UnresTest2 done
+InternedString.run
+Done!
diff --git a/tests/003-omnibus-opcodes/info.txt b/tests/003-omnibus-opcodes/info.txt
new file mode 100644
index 0000000..6c0fbda
--- /dev/null
+++ b/tests/003-omnibus-opcodes/info.txt
@@ -0,0 +1 @@
+This is a smoke test of many Dalvik opcodes.
diff --git a/tests/003-omnibus-opcodes/src/Array.java b/tests/003-omnibus-opcodes/src/Array.java
new file mode 100644
index 0000000..f385dd8
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src/Array.java
@@ -0,0 +1,224 @@
+// Copyright 2008 The Android Open Source Project
+
+
+/**
+ * Exercise arrays.
+ */
+public class Array {
+
+ /*
+ * Verify array contents.
+ */
+ static void checkBytes(byte[] bytes) {
+ assert(bytes[0] == 0);
+ assert(bytes[1] == -1);
+ assert(bytes[2] == -2);
+ assert(bytes[3] == -3);
+ assert(bytes[4] == -4);
+ }
+ static void checkShorts(short[] shorts) {
+ assert(shorts[0] == 20);
+ assert(shorts[1] == 10);
+ assert(shorts[2] == 0);
+ assert(shorts[3] == -10);
+ assert(shorts[4] == -20);
+ }
+ static void checkChars(char[] chars) {
+ assert(chars[0] == 40000);
+ assert(chars[1] == 40001);
+ assert(chars[2] == 40002);
+ assert(chars[3] == 40003);
+ assert(chars[4] == 40004);
+ }
+ static void checkInts(int[] ints) {
+ assert(ints[0] == 70000);
+ assert(ints[1] == 70001);
+ assert(ints[2] == 70002);
+ assert(ints[3] == 70003);
+ assert(ints[4] == 70004);
+ }
+ static void checkBooleans(boolean[] booleans) {
+ assert(booleans[0]);
+ assert(booleans[1]);
+ assert(!booleans[2]);
+ assert(booleans[3]);
+ assert(!booleans[4]);
+ }
+ static void checkFloats(float[] floats) {
+ assert(floats[0] == -1.5);
+ assert(floats[1] == -0.5);
+ assert(floats[2] == 0.0);
+ assert(floats[3] == 0.5);
+ assert(floats[4] == 1.5);
+ }
+ static void checkLongs(long[] longs) {
+ assert(longs[0] == 0x1122334455667788L);
+ assert(longs[1] == 0x8877665544332211L);
+ assert(longs[2] == 0L);
+ assert(longs[3] == 1L);
+ assert(longs[4] == -1L);
+ }
+ static void checkStrings(String[] strings) {
+ assert(strings[0].equals("zero"));
+ assert(strings[1].equals("one"));
+ assert(strings[2].equals("two"));
+ assert(strings[3].equals("three"));
+ assert(strings[4].equals("four"));
+ }
+
+ /*
+ * Try bad range values, 32 bit get/put.
+ */
+ static void checkRange32(int[] ints, int[] empty, int negVal1, int negVal2){
+ System.out.println("Array.checkRange32");
+ int i = 0;
+
+ assert(ints.length == 5);
+
+ try {
+ i = ints[5]; // exact bound
+ assert(false);
+ } catch (ArrayIndexOutOfBoundsException aioobe) {
+ // good
+ }
+ try {
+ ints[5] = i; // exact bound
+ assert(false);
+ } catch (ArrayIndexOutOfBoundsException aioobe) {
+ // good
+ }
+ try {
+ i = ints[6]; // one past
+ assert(false);
+ } catch (ArrayIndexOutOfBoundsException aioobe) {
+ // good
+ }
+ try {
+ i = ints[negVal1]; // -1
+ assert(false);
+ } catch (ArrayIndexOutOfBoundsException aioobe) {
+ // good
+ }
+ try {
+ ints[negVal1] = i; // -1
+ assert(false);
+ } catch (ArrayIndexOutOfBoundsException aioobe) {
+ // good
+ }
+ try {
+ i = ints[negVal2]; // min int
+ assert(false);
+ } catch (ArrayIndexOutOfBoundsException aioobe) {
+ // good
+ }
+
+
+ try {
+ i = empty[1];
+ assert(false);
+ } catch (ArrayIndexOutOfBoundsException aioobe) {
+ // good
+ }
+ }
+
+ /*
+ * Try bad range values, 64 bit get/put.
+ */
+ static void checkRange64(long[] longs, int negVal1, int negVal2) {
+ System.out.println("Array.checkRange64");
+ long l = 0L;
+
+ assert(longs.length == 5);
+
+ try {
+ l = longs[5]; // exact bound
+ assert(false);
+ } catch (ArrayIndexOutOfBoundsException aioobe) {
+ // good
+ }
+ try {
+ longs[5] = l; // exact bound
+ assert(false);
+ } catch (ArrayIndexOutOfBoundsException aioobe) {
+ // good
+ }
+ try {
+ l = longs[6]; // one past
+ assert(false);
+ } catch (ArrayIndexOutOfBoundsException aioobe) {
+ // good
+ }
+ try {
+ l = longs[negVal1]; // -1
+ assert(false);
+ } catch (ArrayIndexOutOfBoundsException aioobe) {
+ // good
+ }
+ try {
+ longs[negVal1] = l; // -1
+ assert(false);
+ } catch (ArrayIndexOutOfBoundsException aioobe) {
+ // good
+ }
+ try {
+ l = longs[negVal2]; // min int
+ assert(false);
+ } catch (ArrayIndexOutOfBoundsException aioobe) {
+ // good
+ }
+ }
+
+ /*
+ * Test negative allocations of object and primitive arrays.
+ */
+ static void checkNegAlloc(int count) {
+ System.out.println("Array.checkNegAlloc");
+ String[] strings;
+ int[] ints;
+
+ try {
+ ints = new int[count];
+ assert(false);
+ } catch (NegativeArraySizeException nase) {
+ // good
+ }
+
+ try {
+ strings = new String[count];
+ assert(false);
+ } catch (NegativeArraySizeException nase) {
+ // good
+ }
+ }
+
+ public static void run() {
+ System.out.println("Array check...");
+
+ byte[] xBytes = new byte[] { 0, -1, -2, -3, -4 };
+ short[] xShorts = new short[] { 20, 10, 0, -10, -20 };
+ char[] xChars = new char[] { 40000, 40001, 40002, 40003, 40004 };
+ int[] xInts = new int[] { 70000, 70001, 70002, 70003, 70004 };
+ boolean[] xBooleans = new boolean[] { true, true, false, true, false };
+ float[] xFloats = new float[] { -1.5f, -0.5f, 0.0f, 0.5f, 1.5f };
+ long[] xLongs = new long[] {
+ 0x1122334455667788L, 0x8877665544332211L, 0L, 1L, -1l };
+ String[] xStrings = new String[] {
+ "zero", "one", "two", "three", "four" };
+
+ int[] xEmpty = new int[0];
+
+ checkBytes(xBytes);
+ checkShorts(xShorts);
+ checkChars(xChars);
+ checkInts(xInts);
+ checkBooleans(xBooleans);
+ checkFloats(xFloats);
+ checkLongs(xLongs);
+ checkStrings(xStrings);
+
+ checkRange32(xInts, xEmpty, -1, (int) 0x80000000);
+ checkRange64(xLongs, -1, (int) 0x80000000);
+
+ checkNegAlloc(-1);
+ }
+}
diff --git a/tests/003-omnibus-opcodes/src/Classes.java b/tests/003-omnibus-opcodes/src/Classes.java
new file mode 100644
index 0000000..c89ff3e
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src/Classes.java
@@ -0,0 +1,219 @@
+// Copyright 2008 The Android Open Source Project
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+/**
+ * Exercise some class-related instructions.
+ */
+public class Classes {
+ int mSome;
+
+ public void subFunc(boolean wantSub) {
+ assert(!wantSub);
+ }
+
+ void checkCast(Object thisRef, Object moreRef, Object nullRef) {
+ System.out.println("Classes.checkCast");
+
+ Classes classes;
+ MoreClasses more;
+
+ classes = (Classes) thisRef;
+ assert(thisRef instanceof Classes);
+ classes = (Classes) moreRef;
+ assert(moreRef instanceof Classes);
+
+ more = (MoreClasses) moreRef;
+ assert(moreRef instanceof MoreClasses);
+ assert(!(thisRef instanceof MoreClasses));
+
+ try {
+ more = (MoreClasses) thisRef;
+ assert(false);
+ } catch (ClassCastException cce) {
+ //System.out.println(" class cast msg: " + cce.getMessage());
+ //Dalvik throws terser message than Hotspot VM
+ assert(cce.getMessage().regionMatches(false, 0, "Classes", 0, 7));
+ }
+ assert(!(thisRef instanceof MoreClasses));
+
+ /* hopefully these classes cause a resolve */
+ try {
+ java.math.RoundingMode mode = (java.math.RoundingMode) thisRef;
+ assert(false);
+ } catch (ClassCastException cce) {
+ //System.out.println(" class cast msg: " + cce.getMessage());
+ //Dalvik throws terser message than Hotspot VM
+ assert(cce.getMessage().regionMatches(false, 0, "Classes", 0, 7));
+ }
+ assert(!(thisRef instanceof java.math.BigDecimal));
+
+ /* try some stuff with a null reference */
+ classes = (Classes) nullRef;
+ classes = (MoreClasses) nullRef;
+ more = (MoreClasses) nullRef;
+ assert(!(nullRef instanceof Classes));
+
+ }
+
+
+ static void xTests(Object x) {
+ assert( x instanceof Classes);
+ assert(!(x instanceof MoreClasses));
+ }
+ static void yTests(Object y) {
+ assert( y instanceof Classes);
+ assert( y instanceof MoreClasses);
+ }
+ static void xarTests(Object xar) {
+ assert( xar instanceof Object);
+ assert(!(xar instanceof Classes));
+ assert( xar instanceof Classes[]);
+ assert(!(xar instanceof MoreClasses[]));
+ assert( xar instanceof Object[]);
+ assert(!(xar instanceof Object[][]));
+ }
+ static void yarTests(Object yar) {
+ assert( yar instanceof Classes[]);
+ assert( yar instanceof MoreClasses[]);
+ }
+ static void xarararTests(Object xararar) {
+ assert( xararar instanceof Object);
+ assert( xararar instanceof Object[]);
+ assert(!(xararar instanceof Classes));
+ assert(!(xararar instanceof Classes[]));
+ assert(!(xararar instanceof Classes[][]));
+ assert( xararar instanceof Classes[][][]);
+ assert(!(xararar instanceof MoreClasses[][][]));
+ assert( xararar instanceof Object[][][]);
+ assert( xararar instanceof Serializable);
+ assert( xararar instanceof Serializable[]);
+ assert( xararar instanceof Serializable[][]);
+ assert(!(xararar instanceof Serializable[][][]));
+ }
+ static void yarararTests(Object yararar) {
+ assert( yararar instanceof Classes[][][]);
+ assert( yararar instanceof MoreClasses[][][]);
+ }
+ static void iarTests(Object iar) {
+ assert( iar instanceof Object);
+ assert(!(iar instanceof Object[]));
+ }
+ static void iararTests(Object iarar) {
+ assert( iarar instanceof Object);
+ assert( iarar instanceof Object[]);
+ assert(!(iarar instanceof Object[][]));
+ }
+
+ /*
+ * Exercise filled-new-array and test instanceof on arrays.
+ *
+ * We call out instead of using "instanceof" directly to avoid
+ * compiler optimizations.
+ */
+ static void arrayInstance() {
+ System.out.println("Classes.arrayInstance");
+
+ Classes x = new Classes();
+ Classes[] xar = new Classes[1];
+ Classes[][] xarar = new Classes[1][1];
+ Classes[][][] xararar = new Classes[1][2][3];
+ MoreClasses y = new MoreClasses();
+ MoreClasses[] yar = new MoreClasses[3];
+ MoreClasses[][] yarar = new MoreClasses[2][3];
+ MoreClasses[][][] yararar = new MoreClasses[1][2][3];
+ int[] iar = new int[1];
+ int[][] iarar = new int[1][1];
+ Object test;
+
+ xTests(x);
+ yTests(y);
+ xarTests(xar);
+ yarTests(yar);
+ xarararTests(xararar);
+ yarararTests(yararar);
+ iarTests(iar);
+ iararTests(iarar);
+
+ yararar[0] = yarar;
+ yararar[0][0] = yar;
+ yararar[0][1] = yar;
+ yararar[0][0][0] = y;
+ yararar[0][0][1] = y;
+ yararar[0][0][2] = y;
+ yararar[0][1][0] = y;
+ yararar[0][1][1] = y;
+ yararar[0][1][2] = y;
+
+ String strForm;
+
+ String[][][][] multi1 = new String[2][3][2][1];
+ multi1[0] = new String[2][3][2];
+ multi1[0][1] = new String[3][2];
+ multi1[0][1][2] = new String[2];
+ multi1[0][1][2][1] = "HELLO-1";
+ strForm = Arrays.deepToString(multi1);
+
+ String[][][][][] multi2 = new String[5][2][3][2][1];
+ multi2[0] = new String[5][2][3][2];
+ multi2[0][1] = new String[5][2][3];
+ multi2[0][1][2] = new String[5][2];
+ multi2[0][1][2][1] = new String[5];
+ multi2[0][1][2][1][4] = "HELLO-2";
+ strForm = Arrays.deepToString(multi2);
+
+
+ String[][][][][][] multi3 = new String[2][5][2][3][2][1];
+ multi3[0] = new String[2][][][][];
+ multi3[0][1] = new String[3][][][];
+ multi3[0][1][2] = new String[2][][];
+ multi3[0][1][2][1] = new String[5][];
+ multi3[0][1][2][1][4] = new String[2];
+ multi3[0][1][2][1][4][1] = "HELLO-3";
+ strForm = Arrays.deepToString(multi3);
+
+ // build up pieces
+ String[][][][][][] multi4 = new String[1][][][][][];
+ multi4[0] = new String[2][][][][];
+ multi4[0][1] = new String[3][][][];
+ multi4[0][1][2] = new String[2][][];
+ multi4[0][1][2][1] = new String[5][];
+ multi4[0][1][2][1][4] = new String[2];
+ multi4[0][1][2][1][4][1] = "HELLO-4";
+ strForm = Arrays.deepToString(multi4);
+
+ /* this is expected to fail; 1073921584 * 4 overflows 32 bits */
+ try {
+ String[][][][][] multiX = new String[5][2][3][2][1073921584];
+ assert(false);
+ } catch (Error e) {
+ //System.out.println(" Got expected failure: " + e);
+ }
+
+ }
+
+ public static void run() {
+ Classes classes = new Classes();
+ MoreClasses more = new MoreClasses();
+ classes.checkCast(classes, more, null);
+
+ more.subFunc(true);
+ more.superFunc(false);
+ arrayInstance();
+ }
+}
+
+class MoreClasses extends Classes {
+ int mMore;
+
+ public MoreClasses() {}
+
+ public void subFunc(boolean wantSub) {
+ assert(wantSub);
+ }
+
+ public void superFunc(boolean wantSub) {
+ super.subFunc(wantSub);
+ }
+}
diff --git a/tests/003-omnibus-opcodes/src/Compare.java b/tests/003-omnibus-opcodes/src/Compare.java
new file mode 100644
index 0000000..43a708a
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src/Compare.java
@@ -0,0 +1,171 @@
+// Copyright 2008 The Android Open Source Project
+
+
+
+/**
+ * Test comparison operators.
+ */
+public class Compare {
+
+ /*
+ * Test the integer comparisons in various ways.
+ */
+ static void testIntCompare(int minus, int plus, int plus2, int zero) {
+ System.out.println("IntMath.testIntCompare");
+
+ if (minus > plus)
+ assert(false);
+ if (minus >= plus)
+ assert(false);
+ if (plus < minus)
+ assert(false);
+ if (plus <= minus)
+ assert(false);
+ if (plus == minus)
+ assert(false);
+ if (plus != plus2)
+ assert(false);
+
+ /* try a branch-taken */
+ if (plus != minus) {
+ assert(true);
+ } else {
+ assert(false);
+ }
+
+ if (minus > 0)
+ assert(false);
+ if (minus >= 0)
+ assert(false);
+ if (plus < 0)
+ assert(false);
+ if (plus <= 0)
+ assert(false);
+ if (plus == 0)
+ assert(false);
+ if (zero != 0)
+ assert(false);
+
+ if (zero == 0) {
+ assert(true);
+ } else {
+ assert(false);
+ }
+ }
+
+ /*
+ * Test cmp-long.
+ *
+ * minus=-5, alsoMinus=0xFFFFFFFF00000009, plus=4, alsoPlus=8
+ */
+ static void testLongCompare(long minus, long alsoMinus, long plus,
+ long alsoPlus) {
+
+ System.out.println("IntMath.testLongCompare");
+ if (minus > plus)
+ assert(false);
+ if (plus < minus)
+ assert(false);
+ if (plus == minus)
+ assert(false);
+
+ if (plus >= plus+1)
+ assert(false);
+ if (minus >= minus+1)
+ assert(false);
+
+ /* try a branch-taken */
+ if (plus != minus) {
+ assert(true);
+ } else {
+ assert(false);
+ }
+
+ /* compare when high words are equal but low words differ */
+ if (plus > alsoPlus)
+ assert(false);
+ if (alsoPlus < plus)
+ assert(false);
+ if (alsoPlus == plus)
+ assert(false);
+
+ /* high words are equal, low words have apparently different signs */
+ if (minus < alsoMinus) // bug!
+ assert(false);
+ if (alsoMinus > minus)
+ assert(false);
+ if (alsoMinus == minus)
+ assert(false);
+ }
+
+ /*
+ * Test cmpl-float and cmpg-float.
+ */
+ static void testFloatCompare(float minus, float plus, float plus2,
+ float nan) {
+
+ System.out.println("IntMath.testFloatCompare");
+ if (minus > plus)
+ assert(false);
+ if (plus < minus)
+ assert(false);
+ if (plus == minus)
+ assert(false);
+ if (plus != plus2)
+ assert(false);
+
+ if (plus <= nan)
+ assert(false);
+ if (plus >= nan)
+ assert(false);
+ if (minus <= nan)
+ assert(false);
+ if (minus >= nan)
+ assert(false);
+ if (nan >= plus)
+ assert(false);
+ if (nan <= plus)
+ assert(false);
+
+ if (nan == nan)
+ assert(false);
+ }
+
+ static void testDoubleCompare(double minus, double plus, double plus2,
+ double nan) {
+
+ System.out.println("IntMath.testDoubleCompare");
+ if (minus > plus)
+ assert(false);
+ if (plus < minus)
+ assert(false);
+ if (plus == minus)
+ assert(false);
+ if (plus != plus2)
+ assert(false);
+
+ if (plus <= nan)
+ assert(false);
+ if (plus >= nan)
+ assert(false);
+ if (minus <= nan)
+ assert(false);
+ if (minus >= nan)
+ assert(false);
+ if (nan >= plus)
+ assert(false);
+ if (nan <= plus)
+ assert(false);
+
+ if (nan == nan)
+ assert(false);
+ }
+
+ public static void run() {
+ testIntCompare(-5, 4, 4, 0);
+ testLongCompare(-5L, -4294967287L, 4L, 8L);
+
+ testFloatCompare(-5.0f, 4.0f, 4.0f, (1.0f/0.0f) / (1.0f/0.0f));
+ testDoubleCompare(-5.0, 4.0, 4.0, (1.0/0.0) / (1.0/0.0));
+ }
+}
diff --git a/tests/003-omnibus-opcodes/src/FloatMath.java b/tests/003-omnibus-opcodes/src/FloatMath.java
new file mode 100644
index 0000000..3c49402
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src/FloatMath.java
@@ -0,0 +1,337 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Test arithmetic operations.
+ */
+public class FloatMath {
+
+ static void convTest() {
+ System.out.println("FloatMath.convTest");
+
+ float f;
+ double d;
+ int i;
+ long l;
+
+ /* float --> int */
+ f = 1234.5678f;
+ i = (int) f;
+ assert(i == 1234);
+
+ f = -1234.5678f;
+ i = (int) f;
+ assert(i == -1234);
+
+ /* float --> long */
+ f = 1238.5678f;
+ l = (long) f;
+ assert(l == 1238);
+
+ f = -1238.5678f;
+ l = (long) f;
+ assert(l == -1238);
+
+ /* float --> double */
+ f = 1238.5678f;
+ d = (double) f;
+ assert(d > 1238.567 && d < 1238.568);
+
+ /* double --> int */
+ d = 1234.5678;
+ i = (int) d;
+ assert(i == 1234);
+
+ d = -1234.5678;
+ i = (int) d;
+ assert(i == -1234);
+
+ /* double --> long */
+ d = 5678956789.0123;
+ l = (long) d;
+ assert(l == 5678956789L);
+
+ d = -5678956789.0123;
+ l = (long) d;
+ assert(l == -5678956789L);
+
+ /* double --> float */
+ d = 1238.5678;
+ f = (float) d;
+ assert(f > 1238.567 && f < 1238.568);
+
+ /* int --> long */
+ i = 7654;
+ l = (long) i;
+ assert(l == 7654L);
+
+ i = -7654;
+ l = (long) i;
+ assert(l == -7654L);
+
+ /* int --> float */
+ i = 1234;
+ f = (float) i;
+ assert(f > 1233.9f && f < 1234.1f);
+
+ i = -1234;
+ f = (float) i;
+ assert(f < -1233.9f && f > -1234.1f);
+
+ /* int --> double */
+ i = 1238;
+ d = (double) i;
+ assert(d > 1237.9f && d < 1238.1f);
+
+ i = -1238;
+ d = (double) i;
+ assert(d < -1237.9f && d > -1238.1f);
+
+ /* long --> int (with truncation) */
+ l = 5678956789L;
+ i = (int) l;
+ assert(i == 1383989493);
+
+ l = -5678956789L;
+ i = (int) l;
+ assert(i == -1383989493);
+
+ /* long --> float */
+ l = 5678956789L;
+ f = (float) l;
+ assert(f > 5.6789564E9 && f < 5.6789566E9);
+
+ l = -5678956789L;
+ f = (float) l;
+ assert(f < -5.6789564E9 && f > -5.6789566E9);
+
+ /* long --> double */
+ l = 6678956789L;
+ d = (double) l;
+ assert(d > 6.6789567E9 && d < 6.6789568E9);
+
+ l = -6678956789L;
+ d = (double) l;
+ assert(d < -6.6789567E9 && d > -6.6789568E9);
+ }
+
+ /*
+ * We pass in the arguments and return the results so the compiler
+ * doesn't do the math for us.
+ */
+ static float[] floatOperTest(float x, float y) {
+ System.out.println("FloatMath.floatOperTest");
+
+ float[] results = new float[9];
+
+ /* this seems to generate "op-float" instructions */
+ results[0] = x + y;
+ results[1] = x - y;
+ results[2] = x * y;
+ results[3] = x / y;
+ results[4] = x % -y;
+
+ /* this seems to generate "op-float/2addr" instructions */
+ results[8] = x + (((((x + y) - y) * y) / y) % y);
+
+ return results;
+ }
+ static void floatOperCheck(float[] results) {
+ assert(results[0] > 69996.99f && results[0] < 69997.01f);
+ assert(results[1] > 70002.99f && results[1] < 70003.01f);
+ assert(results[2] > -210000.01f && results[2] < -209999.99f);
+ assert(results[3] > -23333.34f && results[3] < -23333.32f);
+ assert(results[4] > 0.999f && results[4] < 1.001f);
+ assert(results[8] > 70000.99f && results[8] < 70001.01f);
+ }
+
+ /*
+ * We pass in the arguments and return the results so the compiler
+ * doesn't do the math for us.
+ */
+ static double[] doubleOperTest(double x, double y) {
+ System.out.println("FloatMath.doubleOperTest");
+
+ double[] results = new double[9];
+
+ /* this seems to generate "op-double" instructions */
+ results[0] = x + y;
+ results[1] = x - y;
+ results[2] = x * y;
+ results[3] = x / y;
+ results[4] = x % -y;
+
+ /* this seems to generate "op-double/2addr" instructions */
+ results[8] = x + (((((x + y) - y) * y) / y) % y);
+
+ return results;
+ }
+ static void doubleOperCheck(double[] results) {
+ assert(results[0] > 69996.99 && results[0] < 69997.01);
+ assert(results[1] > 70002.99 && results[1] < 70003.01);
+ assert(results[2] > -210000.01 && results[2] < -209999.99);
+ assert(results[3] > -23333.34 && results[3] < -23333.32);
+ assert(results[4] > 0.999 && results[4] < 1.001);
+ assert(results[8] > 70000.99 && results[8] < 70001.01);
+ }
+
+ /*
+ * Try to cause some unary operations.
+ */
+ static float unopTest(float f) {
+ f = -f;
+ return f;
+ }
+
+ static int[] convI(long l, float f, double d, float zero) {
+ int[] results = new int[6];
+ results[0] = (int) l;
+ results[1] = (int) f;
+ results[2] = (int) d;
+ results[3] = (int) (1.0f / zero); // +inf
+ results[4] = (int) (-1.0f / zero); // -inf
+ results[5] = (int) ((1.0f / zero) / (1.0f / zero)); // NaN
+ return results;
+ }
+ static void checkConvI(int[] results) {
+ System.out.println("FloatMath.checkConvI");
+ assert(results[0] == 0x44332211);
+ assert(results[1] == 123);
+ assert(results[2] == -3);
+ assert(results[3] == 0x7fffffff);
+ assert(results[4] == 0x80000000);
+ assert(results[5] == 0);
+ }
+
+ static long[] convL(int i, float f, double d, double zero) {
+ long[] results = new long[6];
+ results[0] = (long) i;
+ results[1] = (long) f;
+ results[2] = (long) d;
+ results[3] = (long) (1.0 / zero); // +inf
+ results[4] = (long) (-1.0 / zero); // -inf
+ results[5] = (long) ((1.0 / zero) / (1.0 / zero)); // NaN
+ return results;
+ }
+ static void checkConvL(long[] results) {
+ System.out.println("FloatMath.checkConvL");
+ assert(results[0] == 0xFFFFFFFF88776655L);
+ assert(results[1] == 123);
+ assert(results[2] == -3);
+ assert(results[3] == 0x7fffffffffffffffL);
+ assert(results[4] == 0x8000000000000000L);
+ assert(results[5] == 0);
+ }
+
+ static float[] convF(int i, long l, double d) {
+ float[] results = new float[3];
+ results[0] = (float) i;
+ results[1] = (float) l;
+ results[2] = (float) d;
+ return results;
+ }
+ static void checkConvF(float[] results) {
+ System.out.println("FloatMath.checkConvF");
+ // TODO: assert values
+ for (int i = 0; i < results.length; i++)
+ System.out.println(" " + i + ": " + results[i]);
+ System.out.println("-2.0054409E9, -8.6133031E18, -3.1415927");
+ }
+
+ static double[] convD(int i, long l, float f) {
+ double[] results = new double[3];
+ results[0] = (double) i;
+ results[1] = (double) l;
+ results[2] = (double) f;
+ return results;
+ }
+ static void checkConvD(double[] results) {
+ System.out.println("FloatMath.checkConvD");
+ // TODO: assert values
+ for (int i = 0; i < results.length; i++)
+ System.out.println(" " + i + ": " + results[i]);
+ System.out.println("-2.005440939E9, -8.6133032459203287E18, 123.4560012817382");
+ }
+
+ static void checkConsts() {
+ System.out.println("FloatMath.checkConsts");
+
+ float f = 10.0f; // const/special
+ assert(f > 9.9 && f < 10.1);
+
+ double d = 10.0; // const-wide/special
+ assert(d > 9.9 && d < 10.1);
+ }
+
+ /*
+ * Determine if two floating point numbers are approximately equal.
+ *
+ * (Assumes that floating point is generally working, so we can't use
+ * this for the first set of tests.)
+ */
+ static boolean approxEqual(float a, float b, float maxDelta) {
+ if (a > b)
+ return (a - b) < maxDelta;
+ else
+ return (b - a) < maxDelta;
+ }
+ static boolean approxEqual(double a, double b, double maxDelta) {
+ if (a > b)
+ return (a - b) < maxDelta;
+ else
+ return (b - a) < maxDelta;
+ }
+
+ /*
+ * Test some java.lang.Math functions.
+ *
+ * The method arguments are positive values.
+ */
+ static void jlmTests(float ff, double dd) {
+ System.out.println("FloatMath.jlmTests");
+
+ assert(approxEqual(Math.abs(ff), ff, 0.001f));
+ assert(approxEqual(Math.abs(-ff), ff, 0.001f));
+ assert(approxEqual(Math.min(ff, -5.0f), -5.0f, 0.001f));
+ assert(approxEqual(Math.max(ff, -5.0f), ff, 0.001f));
+
+ assert(approxEqual(Math.abs(dd), dd, 0.001));
+ assert(approxEqual(Math.abs(-dd), dd, 0.001));
+ assert(approxEqual(Math.min(dd, -5.0), -5.0, 0.001));
+ assert(approxEqual(Math.max(dd, -5.0), dd, 0.001));
+
+ double sq = Math.sqrt(dd);
+ assert(approxEqual(sq*sq, dd, 0.001));
+
+ assert(approxEqual(0.5403023058681398, Math.cos(1.0), 0.00000001));
+ assert(approxEqual(0.8414709848078965, Math.sin(1.0), 0.00000001));
+ }
+
+ public static void run() {
+ convTest();
+
+ float[] floatResults;
+ double[] doubleResults;
+ int[] intResults;
+ long[] longResults;
+
+ floatResults = floatOperTest(70000.0f, -3.0f);
+ floatOperCheck(floatResults);
+ doubleResults = doubleOperTest(70000.0, -3.0);
+ doubleOperCheck(doubleResults);
+
+ intResults = convI(0x8877665544332211L, 123.456f, -3.1415926535, 0.0f);
+ checkConvI(intResults);
+ longResults = convL(0x88776655, 123.456f, -3.1415926535, 0.0);
+ checkConvL(longResults);
+ floatResults = convF(0x88776655, 0x8877665544332211L, -3.1415926535);
+ checkConvF(floatResults);
+ doubleResults = convD(0x88776655, 0x8877665544332211L, 123.456f);
+ checkConvD(doubleResults);
+
+ unopTest(123.456f);
+
+ checkConsts();
+
+ jlmTests(3.14159f, 123456.78987654321);
+ }
+}
diff --git a/tests/003-omnibus-opcodes/src/Goto.java b/tests/003-omnibus-opcodes/src/Goto.java
new file mode 100644
index 0000000..d56ceae
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src/Goto.java
@@ -0,0 +1,2408 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Try to cause some gotos.
+ */
+class Goto {
+ static int filler(int i) {
+ return i+1;
+ }
+
+ static int smallGoto(boolean which) {
+ System.out.println("Goto.smallGoto");
+
+ int i = 0;
+
+ if (which) {
+ i += filler(i);
+ } else {
+ i -= filler(i);
+ }
+
+ return i;
+ }
+
+ static int mediumGoto(boolean which) {
+ System.out.println("Goto.mediumGoto");
+
+ int i = 0;
+
+ if (which) {
+ i += filler(i);
+ } else {
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ }
+
+ return i;
+ }
+
+ static int bigGoto(boolean which) {
+ System.out.println("Goto.bigGoto");
+
+ int i = 0;
+
+ if (which) {
+ i += filler(i);
+ } else {
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ }
+
+ return i;
+ }
+
+ public static void run() {
+ smallGoto(false);
+ smallGoto(true);
+ mediumGoto(false);
+ mediumGoto(true);
+ bigGoto(false);
+ bigGoto(true);
+ }
+};
diff --git a/tests/003-omnibus-opcodes/src/InstField.java b/tests/003-omnibus-opcodes/src/InstField.java
new file mode 100644
index 0000000..80b95ab
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src/InstField.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class InstField {
+ public boolean mBoolean1, mBoolean2;
+ public byte mByte1, mByte2;
+ public char mChar1, mChar2;
+ public short mShort1, mShort2;
+ public int mInt1, mInt2;
+ public float mFloat1, mFloat2;
+ public long mLong1, mLong2;
+ public double mDouble1, mDouble2;
+ public volatile long mVolatileLong1, mVolatileLong2;
+
+ public void run() {
+ assignFields();
+ checkFields();
+ InstField.nullCheck(null);
+ }
+
+ /*
+ * Check access to instance fields through a null pointer.
+ */
+ static public void nullCheck(InstField nully) {
+ System.out.println("InstField.nullCheck");
+ try {
+ int x = nully.mInt1;
+ assert(false);
+ } catch (NullPointerException npe) {
+ // good
+ }
+ try {
+ long l = nully.mLong1;
+ assert(false);
+ } catch (NullPointerException npe) {
+ // good
+ }
+ try {
+ nully.mInt1 = 5;
+ assert(false);
+ } catch (NullPointerException npe) {
+ // good
+ }
+ try {
+ nully.mLong1 = 17L;
+ assert(false);
+ } catch (NullPointerException npe) {
+ // good
+ }
+ }
+
+ public void assignFields() {
+ System.out.println("InstField assign...");
+ mBoolean1 = true;
+ mBoolean2 = false;
+ mByte1 = 127;
+ mByte2 = -128;
+ mChar1 = 32767;
+ mChar2 = 65535;
+ mShort1 = 32767;
+ mShort2 = -32768;
+ mInt1 = 65537;
+ mInt2 = -65537;
+ mFloat1 = 3.1415f;
+ mFloat2 = -1.0f / 0.0f; // -inf
+ mLong1 = 1234605616436508552L; // 0x1122334455667788
+ mLong2 = -1234605616436508552L;
+ mDouble1 = 3.1415926535;
+ mDouble2 = 1.0 / 0.0; // +inf
+ mVolatileLong1 = mLong1 - 1;
+ mVolatileLong2 = mLong2 + 1;
+ }
+
+ public void checkFields() {
+ System.out.println("InstField check...");
+ assert(mBoolean1);
+ assert(!mBoolean2);
+ assert(mByte1 == 127);
+ assert(mByte2 == -128);
+ assert(mChar1 == 32767);
+ assert(mChar2 == 65535);
+ assert(mShort1 == 32767);
+ assert(mShort2 == -32768);
+ assert(mInt1 == 65537);
+ assert(mInt2 == -65537);
+ assert(mFloat1 > 3.141f && mFloat1 < 3.142f);
+ assert(mFloat2 < mFloat1);
+ assert(mLong1 == 1234605616436508552L);
+ assert(mLong2 == -1234605616436508552L);
+ assert(mDouble1 > 3.141592653 && mDouble1 < 3.141592654);
+ assert(mDouble2 > mDouble1);
+ assert(mVolatileLong1 == 1234605616436508551L);
+ assert(mVolatileLong2 == -1234605616436508551L);
+ }
+}
diff --git a/tests/003-omnibus-opcodes/src/IntMath.java b/tests/003-omnibus-opcodes/src/IntMath.java
new file mode 100644
index 0000000..89194de
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src/IntMath.java
@@ -0,0 +1,492 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Test arithmetic operations.
+ */
+public class IntMath {
+
+ static void shiftTest1() {
+ System.out.println("IntMath.shiftTest1");
+
+ final int[] mBytes = {
+ 0x11, 0x22, 0x33, 0x44, 0x88, 0x99, 0xaa, 0xbb
+ };
+ long l;
+ int i1, i2;
+
+ i1 = mBytes[0] | mBytes[1] << 8 | mBytes[2] << 16 | mBytes[3] << 24;
+ i2 = mBytes[4] | mBytes[5] << 8 | mBytes[6] << 16 | mBytes[7] << 24;
+ l = i1 | ((long)i2 << 32);
+
+ assert(i1 == 0x44332211);
+ assert(i2 == 0xbbaa9988);
+ assert(l == 0xbbaa998844332211L);
+
+ l = (long)mBytes[0]
+ | (long)mBytes[1] << 8
+ | (long)mBytes[2] << 16
+ | (long)mBytes[3] << 24
+ | (long)mBytes[4] << 32
+ | (long)mBytes[5] << 40
+ | (long)mBytes[6] << 48
+ | (long)mBytes[7] << 56;
+
+ assert(l == 0xbbaa998844332211L);
+ }
+
+ static void shiftTest2() {
+ System.out.println("IntMath.shiftTest2");
+
+ long a = 0x11;
+ long b = 0x22;
+ long c = 0x33;
+ long d = 0x44;
+ long e = 0x55;
+ long f = 0x66;
+ long g = 0x77;
+ long h = 0x88;
+
+ long result = ((a << 56) | (b << 48) | (c << 40) | (d << 32) |
+ (e << 24) | (f << 16) | (g << 8) | h);
+
+ assert(result == 0x1122334455667788L);
+ }
+
+ static void unsignedShiftTest() {
+ System.out.println("IntMath.unsignedShiftTest");
+
+ byte b = -4;
+ short s = -4;
+ char c = 0xfffc;
+ int i = -4;
+
+ b >>>= 4;
+ s >>>= 4;
+ c >>>= 4;
+ i >>>= 4;
+
+ assert((int) b == -1);
+ assert((int) s == -1);
+ assert((int) c == 0x0fff);
+ assert(i == 268435455);
+ }
+
+ static void convTest() {
+ System.out.println("IntMath.convTest");
+
+ float f;
+ double d;
+ int i;
+ long l;
+
+ /* int --> long */
+ i = 7654;
+ l = (long) i;
+ assert(l == 7654L);
+
+ i = -7654;
+ l = (long) i;
+ assert(l == -7654L);
+
+ /* long --> int (with truncation) */
+ l = 5678956789L;
+ i = (int) l;
+ assert(i == 1383989493);
+
+ l = -5678956789L;
+ i = (int) l;
+ assert(i == -1383989493);
+ }
+
+ static void charSubTest() {
+ System.out.println("IntMath.charSubTest");
+
+ char char1 = 0x00e9;
+ char char2 = 0xffff;
+ int i;
+
+ /* chars are unsigned-expanded to ints before subtraction */
+ i = char1 - char2;
+ assert(i == 0xffff00ea);
+ }
+
+ /*
+ * We pass in the arguments and return the results so the compiler
+ * doesn't do the math for us. (x=70000, y=-3)
+ */
+ static int[] intOperTest(int x, int y) {
+ System.out.println("IntMath.intOperTest");
+
+ int[] results = new int[10];
+
+ /* this seems to generate "op-int" instructions */
+ results[0] = x + y;
+ results[1] = x - y;
+ results[2] = x * y;
+ results[3] = x * x;
+ results[4] = x / y;
+ results[5] = x % -y;
+ results[6] = x & y;
+ results[7] = x | y;
+ results[8] = x ^ y;
+
+ /* this seems to generate "op-int/2addr" instructions */
+ results[9] = x + ((((((((x + y) - y) * y) / y) % y) & y) | y) ^ y);
+
+ return results;
+ }
+ static void intOperCheck(int[] results) {
+ System.out.println("IntMath.intOperCheck");
+
+ /* check this edge case while we're here (div-int/2addr) */
+ int minInt = -2147483648;
+ int negOne = -results[5];
+ int plusOne = 1;
+ int result = (((minInt + plusOne) - plusOne) / negOne) / negOne;
+ assert(result == minInt);
+
+ assert(results[0] == 69997);
+ assert(results[1] == 70003);
+ assert(results[2] == -210000);
+ assert(results[3] == 605032704); // overflow / truncate
+ assert(results[4] == -23333);
+ assert(results[5] == 1);
+ assert(results[6] == 70000);
+ assert(results[7] == -3);
+ assert(results[8] == -70003);
+ assert(results[9] == 70000);
+ }
+
+ /*
+ * More operations, this time with 16-bit constants. (x=77777)
+ */
+ static int[] lit16Test(int x) {
+ System.out.println("IntMath.lit16Test");
+
+ int[] results = new int[8];
+
+ /* try to generate op-int/lit16" instructions */
+ results[0] = x + 1000;
+ results[1] = 1000 - x;
+ results[2] = x * 1000;
+ results[3] = x / 1000;
+ results[4] = x % 1000;
+ results[5] = x & 1000;
+ results[6] = x | -1000;
+ results[7] = x ^ -1000;
+ return results;
+ }
+ static void lit16Check(int[] results) {
+ assert(results[0] == 78777);
+ assert(results[1] == -76777);
+ assert(results[2] == 77777000);
+ assert(results[3] == 77);
+ assert(results[4] == 777);
+ assert(results[5] == 960);
+ assert(results[6] == -39);
+ assert(results[7] == -76855);
+ }
+
+ /*
+ * More operations, this time with 8-bit constants. (x=-55555)
+ */
+ static int[] lit8Test(int x) {
+ System.out.println("IntMath.lit8Test");
+
+ int[] results = new int[8];
+
+ /* try to generate op-int/lit8" instructions */
+ results[0] = x + 10;
+ results[1] = 10 - x;
+ results[2] = x * 10;
+ results[3] = x / 10;
+ results[4] = x % 10;
+ results[5] = x & 10;
+ results[6] = x | -10;
+ results[7] = x ^ -10;
+ return results;
+ }
+ static void lit8Check(int[] results) {
+ //for (int i = 0; i < results.length; i++)
+ // System.out.println(" " + i + ": " + results[i]);
+
+ /* check this edge case while we're here (div-int/lit8) */
+ int minInt = -2147483648;
+ int result = minInt / -1;
+ assert(result == minInt);
+
+ assert(results[0] == -55545);
+ assert(results[1] == 55565);
+ assert(results[2] == -555550);
+ assert(results[3] == -5555);
+ assert(results[4] == -5);
+ assert(results[5] == 8);
+ assert(results[6] == -1);
+ assert(results[7] == 55563);
+ }
+
+
+ /*
+ * Shift some data. (value=0xff00aa01, dist=8)
+ */
+ static int[] intShiftTest(int value, int dist) {
+ System.out.println("IntMath.intShiftTest");
+
+ int results[] = new int[4];
+
+ results[0] = value << dist;
+ results[1] = value >> dist;
+ results[2] = value >>> dist;
+
+ results[3] = (((value << dist) >> dist) >>> dist) << dist;
+ return results;
+ }
+ static void intShiftCheck(int[] results) {
+ System.out.println("IntMath.intShiftCheck");
+
+ assert(results[0] == 0x00aa0100);
+ assert(results[1] == 0xffff00aa);
+ assert(results[2] == 0x00ff00aa);
+ assert(results[3] == 0xaa00);
+ }
+
+ /*
+ * We pass in the arguments and return the results so the compiler
+ * doesn't do the math for us. (x=70000000000, y=-3)
+ */
+ static long[] longOperTest(long x, long y) {
+ System.out.println("IntMath.longOperTest");
+
+ long[] results = new long[10];
+
+ /* this seems to generate "op-long" instructions */
+ results[0] = x + y;
+ results[1] = x - y;
+ results[2] = x * y;
+ results[3] = x * x;
+ results[4] = x / y;
+ results[5] = x % -y;
+ results[6] = x & y;
+ results[7] = x | y;
+ results[8] = x ^ y;
+
+ /* this seems to generate "op-long/2addr" instructions */
+ results[9] = x + ((((((((x + y) - y) * y) / y) % y) & y) | y) ^ y);
+
+ return results;
+ }
+ static void longOperCheck(long[] results) {
+ System.out.println("IntMath.longOperCheck");
+
+ /* check this edge case while we're here (div-long/2addr) */
+ long minLong = -9223372036854775808L;
+ long negOne = -results[5];
+ long plusOne = 1;
+ long result = (((minLong + plusOne) - plusOne) / negOne) / negOne;
+ assert(result == minLong);
+
+ assert(results[0] == 69999999997L);
+ assert(results[1] == 70000000003L);
+ assert(results[2] == -210000000000L);
+ assert(results[3] == -6833923606740729856L); // overflow
+ assert(results[4] == -23333333333L);
+ assert(results[5] == 1);
+ assert(results[6] == 70000000000L);
+ assert(results[7] == -3);
+ assert(results[8] == -70000000003L);
+ assert(results[9] == 70000000000L);
+
+ assert(results.length == 10);
+ }
+
+ /*
+ * Shift some data. (value=0xd5aa96deff00aa01, dist=8)
+ */
+ static long[] longShiftTest(long value, int dist) {
+ System.out.println("IntMath.longShiftTest");
+
+ long results[] = new long[4];
+
+ results[0] = value << dist;
+ results[1] = value >> dist;
+ results[2] = value >>> dist;
+
+ results[3] = (((value << dist) >> dist) >>> dist) << dist;
+ return results;
+ }
+ static long longShiftCheck(long[] results) {
+ System.out.println("IntMath.longShiftCheck");
+
+ assert(results[0] == 0x96deff00aa010000L);
+ assert(results[1] == 0xffffd5aa96deff00L);
+ assert(results[2] == 0x0000d5aa96deff00L);
+ assert(results[3] == 0xffff96deff000000L);
+
+ assert(results.length == 4);
+
+ return results[0]; // test return-long
+ }
+
+
+ /*
+ * Try to cause some unary operations.
+ */
+ static int unopTest(int x) {
+ x = -x;
+ x ^= 0xffffffff;
+ return x;
+ }
+ static void unopCheck(int result) {
+ assert(result == 37);
+ }
+
+ static class Shorty {
+ public short mShort;
+ public char mChar;
+ public byte mByte;
+ };
+
+ /*
+ * Truncate an int.
+ */
+ static Shorty truncateTest(int x) {
+ System.out.println("IntMath.truncateTest");
+ Shorty shorts = new Shorty();
+
+ shorts.mShort = (short) x;
+ shorts.mChar = (char) x;
+ shorts.mByte = (byte) x;
+ return shorts;
+ }
+ static void truncateCheck(Shorty shorts) {
+ assert(shorts.mShort == -5597); // 0xea23
+ assert(shorts.mChar == 59939); // 0xea23
+ assert(shorts.mByte == 35); // 0x23
+ }
+
+ /*
+ * Verify that we get a divide-by-zero exception.
+ */
+ static void divideByZero(int z) {
+ System.out.println("IntMath.divideByZero");
+
+ try {
+ int x = 100 / z;
+ assert(false);
+ } catch (ArithmeticException ae) {
+ }
+
+ try {
+ int x = 100 % z;
+ assert(false);
+ } catch (ArithmeticException ae) {
+ }
+
+ try {
+ long x = 100L / z;
+ assert(false);
+ } catch (ArithmeticException ae) {
+ }
+
+ try {
+ long x = 100L % z;
+ assert(false);
+ } catch (ArithmeticException ae) {
+ }
+ }
+
+ /*
+ * Check an edge condition: dividing the most-negative integer by -1
+ * returns the most-negative integer, and doesn't cause an exception.
+ *
+ * Pass in -1, -1L.
+ */
+ static void bigDivideOverflow(int idiv, long ldiv) {
+ System.out.println("IntMath.bigDivideOverflow");
+ int mostNegInt = (int) 0x80000000;
+ long mostNegLong = (long) 0x8000000000000000L;
+
+ int intDivResult = mostNegInt / idiv;
+ int intModResult = mostNegInt % idiv;
+ long longDivResult = mostNegLong / ldiv;
+ long longModResult = mostNegLong % ldiv;
+
+ assert(intDivResult == mostNegInt);
+ assert(intModResult == 0);
+ assert(longDivResult == mostNegLong);
+ assert(longModResult == 0);
+ }
+
+ /*
+ * Check "const" instructions. We use negative values to ensure that
+ * sign-extension is happening.
+ */
+ static void checkConsts(byte small, short medium, int large, long huge) {
+ System.out.println("IntMath.checkConsts");
+
+ assert(small == 1); // const/4
+ assert(medium == -256); // const/16
+ assert(medium == -256L); // const-wide/16
+ assert(large == -88888); // const
+ assert(large == -88888L); // const-wide/32
+ assert(huge == 0x9922334455667788L); // const-wide
+ }
+
+ /*
+ * Test some java.lang.Math functions.
+ *
+ * The method arguments are positive values.
+ */
+ static void jlmTests(int ii, long ll) {
+ System.out.println("IntMath.jlmTests");
+
+ assert(Math.abs(ii) == ii);
+ assert(Math.abs(-ii) == ii);
+ assert(Math.min(ii, -5) == -5);
+ assert(Math.max(ii, -5) == ii);
+
+ assert(Math.abs(ll) == ll);
+ assert(Math.abs(-ll) == ll);
+ assert(Math.min(ll, -5L) == -5L);
+ assert(Math.max(ll, -5L) == ll);
+ }
+
+ public static void run() {
+ shiftTest1();
+ shiftTest2();
+ unsignedShiftTest();
+ convTest();
+ charSubTest();
+
+ int[] intResults;
+ long[] longResults;
+
+ intResults = intOperTest(70000, -3);
+ intOperCheck(intResults);
+ longResults = longOperTest(70000000000L, -3L);
+ longOperCheck(longResults);
+
+ intResults = lit16Test(77777);
+ lit16Check(intResults);
+ intResults = lit8Test(-55555);
+ lit8Check(intResults);
+
+ intResults = intShiftTest(0xff00aa01, 8);
+ intShiftCheck(intResults);
+ longResults = longShiftTest(0xd5aa96deff00aa01L, 16);
+ long longRet = longShiftCheck(longResults);
+ assert(longRet == 0x96deff00aa010000L);
+
+ Shorty shorts = truncateTest(-16717277); // 0xff00ea23
+ truncateCheck(shorts);
+
+ divideByZero(0);
+ bigDivideOverflow(-1, -1L);
+
+ checkConsts((byte) 1, (short) -256, -88888, 0x9922334455667788L);
+
+ unopCheck(unopTest(38));
+
+ jlmTests(12345, 0x1122334455667788L);
+ }
+}
diff --git a/tests/003-omnibus-opcodes/src/InternedString.java b/tests/003-omnibus-opcodes/src/InternedString.java
new file mode 100644
index 0000000..4baab0c
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src/InternedString.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.ref.*;
+
+public class InternedString {
+ public static final String CONST = "Class InternedString";
+
+ public static void run() {
+ System.out.println("InternedString.run");
+ testImmortalInternedString();
+ testDeadInternedString();
+ }
+
+ private static void testDeadInternedString() {
+ String s = "blah";
+ s = s + s;
+ WeakReference strRef = new WeakReference<String>(s.intern());
+ // Kill s, otherwise the string object is still accessible from root set
+ s = CONST;
+ System.gc();
+ // "blahblah" should disappear from the intern list
+ assert(strRef.get() == null);
+ }
+
+ private static void testImmortalInternedString() {
+ WeakReference strRef = new WeakReference<String>(CONST.intern());
+ System.gc();
+ // Class constant string should be entered to the interned table when
+ // loaded
+ assert(CONST == CONST.intern());
+ // and it should survive the gc
+ assert(strRef.get() != null);
+
+ String s = CONST;
+ // "Class InternedString" should remain on the intern list
+ strRef = new WeakReference<String>(s.intern());
+ // Kill s, otherwise the string object is still accessible from root set
+ s = "";
+ System.gc();
+ assert(strRef.get() == CONST);
+ }
+}
diff --git a/tests/003-omnibus-opcodes/src/Main.java b/tests/003-omnibus-opcodes/src/Main.java
new file mode 100644
index 0000000..fb39d76
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src/Main.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Dalvik instruction exerciser.
+ */
+public class Main {
+ /*
+ * Start up.
+ */
+ public static void main(String[] args) {
+ boolean assertEnabled = false;
+ assert assertEnabled = true;
+ if (!assertEnabled) {
+ System.out.println("FAIL: assert doesn't work (specify '-ea')\n");
+ throw new RuntimeException();
+ } else {
+ System.out.println("(assertions are enabled)");
+ }
+
+ Main main = new Main();
+ main.run();
+
+ /* run through the heap to see if we trashed something */
+ System.gc();
+
+ System.out.println("Done!");
+ }
+
+ public void run() {
+ InstField instField = new InstField();
+ instField.run();
+
+ StaticField.run();
+
+ IntMath.run();
+ FloatMath.run();
+ Compare.run();
+
+ Monitor.run();
+ Switch.run();
+ Array.run();
+ Classes.run();
+ Goto.run();
+ MethodCall.run();
+ Throw.run();
+
+ try {
+ UnresTest1.run();
+ } catch (VerifyError ve) {
+ System.out.println("Caught: " + ve);
+ }
+ try {
+ UnresTest1.run();
+ } catch (VerifyError ve) {
+ System.out.println("Caught (retry): " + ve);
+ }
+
+ try {
+ UnresTest2.run();
+ } catch (VerifyError ve) {
+ System.out.println("Caught: " + ve);
+ } catch (NoClassDefFoundError ncdfe) {
+ /* UnresClass can cause desktop Java to freak out */
+ System.out.println("NOTE: UnresTest2 not available");
+ }
+ InternedString.run();
+ }
+}
diff --git a/tests/003-omnibus-opcodes/src/MethodCall.java b/tests/003-omnibus-opcodes/src/MethodCall.java
new file mode 100644
index 0000000..f89194b
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src/MethodCall.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Try different kinds of method calls.
+ */
+public class MethodCall extends MethodCallBase {
+ MethodCall() {
+ super();
+ System.out.println(" MethodCall ctor");
+ }
+
+ /* overridden method */
+ int tryThing() {
+ int val = super.tryThing();
+ assert(val == 7);
+ return val;
+ }
+
+ /* do-nothing private instance method */
+ private void directly() {}
+
+ /*
+ * Function with many arguments.
+ */
+ static void manyArgs(int a0, long a1, int a2, long a3, int a4, long a5,
+ int a6, int a7, double a8, float a9, double a10, short a11, int a12,
+ char a13, int a14, int a15, byte a16, boolean a17, int a18, int a19,
+ long a20, long a21, int a22, int a23, int a24, int a25, int a26,
+ String[][] a27, String[] a28, String a29)
+ {
+ System.out.println("MethodCalls.manyArgs");
+ assert(a0 == 0);
+ assert(a9 > 8.99 && a9 < 9.01);
+ assert(a16 == -16);
+ assert(a25 == 25);
+ assert(a29.equals("twenty nine"));
+ }
+
+ public static void run() {
+ MethodCall inst = new MethodCall();
+
+ MethodCallBase base = inst;
+ base.tryThing();
+ inst.tryThing();
+
+ inst = null;
+ try {
+ inst.directly();
+ assert(false);
+ } catch (NullPointerException npe) {
+ // good
+ }
+
+ manyArgs(0, 1L, 2, 3L, 4, 5L, 6, 7, 8.0, 9.0f, 10.0, (short)11, 12,
+ (char)13, 14, 15, (byte)-16, true, 18, 19, 20L, 21L, 22, 23, 24,
+ 25, 26, null, null, "twenty nine");
+ }
+}
+
+class MethodCallBase {
+ MethodCallBase() {
+ System.out.println(" MethodCallBase ctor");
+ }
+
+ int tryThing() {
+ return 7;
+ }
+}
diff --git a/tests/003-omnibus-opcodes/src/Monitor.java b/tests/003-omnibus-opcodes/src/Monitor.java
new file mode 100644
index 0000000..66d7c65
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src/Monitor.java
@@ -0,0 +1,44 @@
+// Copyright 2008 The Android Open Source Project
+
+
+
+/**
+ * Exercise monitors.
+ */
+public class Monitor {
+ public static int mVal = 0;
+
+ public synchronized void subTest() {
+ Object obj = new Object();
+ synchronized (obj) {
+ mVal++;
+ obj = null; // does NOT cause a failure on exit
+ assert(obj == null);
+ }
+ }
+
+
+ public static void run() {
+ System.out.println("Monitor.run");
+
+ Object obj = null;
+
+ try {
+ synchronized (obj) {
+ mVal++;
+ }
+ assert(false);
+ } catch (NullPointerException npe) {
+ /* expected */
+ }
+
+ obj = new Object();
+ synchronized (obj) {
+ mVal++;
+ }
+
+ new Monitor().subTest();
+
+ assert(mVal == 2);
+ }
+}
diff --git a/tests/003-omnibus-opcodes/src/StaticField.java b/tests/003-omnibus-opcodes/src/StaticField.java
new file mode 100644
index 0000000..7ccdd7e
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src/StaticField.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class StaticField {
+ public static boolean mBoolean1, mBoolean2;
+ public static byte mByte1, mByte2;
+ public static char mChar1, mChar2;
+ public static short mShort1, mShort2;
+ public static int mInt1, mInt2;
+ public static float mFloat1, mFloat2;
+ public static long mLong1, mLong2;
+ public static double mDouble1, mDouble2;
+ public static volatile long mVolatileLong1, mVolatileLong2;
+
+ public static void run() {
+ assignFields();
+ checkFields();
+ }
+
+ public static void assignFields() {
+ System.out.println("StaticField assign...");
+ mBoolean1 = true;
+ mBoolean2 = false;
+ mByte1 = 127;
+ mByte2 = -128;
+ mChar1 = 32767;
+ mChar2 = 65535;
+ mShort1 = 32767;
+ mShort2 = -32768;
+ mInt1 = 65537;
+ mInt2 = -65537;
+ mFloat1 = 3.1415f;
+ mFloat2 = -1.0f / 0.0f; // -inf
+ mLong1 = 1234605616436508552L; // 0x1122334455667788
+ mLong2 = -1234605616436508552L;
+ mDouble1 = 3.1415926535;
+ mDouble2 = 1.0 / 0.0; // +inf
+ mVolatileLong1 = mLong1 - 1;
+ mVolatileLong2 = mLong2 + 1;
+ }
+
+ public static void checkFields() {
+ System.out.println("StaticField check...");
+ assert(mBoolean1);
+ assert(!mBoolean2);
+ assert(mByte1 == 127);
+ assert(mByte2 == -128);
+ assert(mChar1 == 32767);
+ assert(mChar2 == 65535);
+ assert(mShort1 == 32767);
+ assert(mShort2 == -32768);
+ assert(mInt1 == 65537);
+ assert(mInt2 == -65537);
+ assert(mFloat1 > 3.141f && mFloat2 < 3.142f);
+ assert(mFloat2 < mFloat1);
+ assert(mLong1 == 1234605616436508552L);
+ assert(mLong2 == -1234605616436508552L);
+ assert(mDouble1 > 3.141592653 && mDouble1 < 3.141592654);
+ assert(mDouble2 > mDouble1);
+ assert(mVolatileLong1 == 1234605616436508551L);
+ assert(mVolatileLong2 == -1234605616436508551L);
+ }
+}
diff --git a/tests/003-omnibus-opcodes/src/Switch.java b/tests/003-omnibus-opcodes/src/Switch.java
new file mode 100644
index 0000000..67c82b0
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src/Switch.java
@@ -0,0 +1,62 @@
+public class Switch {
+ /**
+ * Test switch() blocks
+ */
+ private static void testSwitch() {
+ System.out.println("Switch.testSwitch");
+
+ int a = 1;
+
+ switch (a) {
+ case -1: assert(false); break;
+ case 0: assert(false); break;
+ case 1: /*correct*/ break;
+ case 2: assert(false); break;
+ case 3: assert(false); break;
+ case 4: assert(false); break;
+ default: assert(false); break;
+ }
+ switch (a) {
+ case 3: assert(false); break;
+ case 4: assert(false); break;
+ default: /*correct*/ break;
+ }
+
+ a = 0x12345678;
+
+ switch (a) {
+ case 0x12345678: /*correct*/ break;
+ case 0x12345679: assert(false); break;
+ default: assert(false); break;
+ }
+ switch (a) {
+ case 57: assert(false); break;
+ case -6: assert(false); break;
+ case 0x12345678: /*correct*/ break;
+ case 22: assert(false); break;
+ case 3: assert(false); break;
+ default: assert(false); break;
+ }
+ switch (a) {
+ case -6: assert(false); break;
+ case 3: assert(false); break;
+ default: /*correct*/ break;
+ }
+
+ a = -5;
+ switch (a) {
+ case 12: assert(false); break;
+ case -5: /*correct*/ break;
+ case 0: assert(false); break;
+ default: assert(false); break;
+ }
+
+ switch (a) {
+ default: /*correct*/ break;
+ }
+ }
+
+ public static void run() {
+ testSwitch();
+ }
+}
diff --git a/tests/003-omnibus-opcodes/src/Throw.java b/tests/003-omnibus-opcodes/src/Throw.java
new file mode 100644
index 0000000..91ee6dd
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src/Throw.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Test exception throwing.
+ */
+public class Throw {
+ public void throwNullPointerException() {
+ throw new NullPointerException("npe!");
+ }
+
+ public void throwArithmeticException() {
+ throw new ArithmeticException();
+ }
+
+ public void one() {
+ System.out.println("Throw.one");
+ try {
+ throwNullPointerException();
+ assert(false);
+ } catch (Exception ex) {
+ // good
+ return;
+ }
+
+ assert(false);
+ }
+
+ public void twoA() {
+ System.out.println("Throw.twoA");
+ boolean gotN = false;
+ boolean gotA = false;
+ boolean gotWeird = false;
+
+ try {
+ try {
+ throwArithmeticException();
+ gotWeird = true;
+ } catch (ArithmeticException ae) {
+ gotA = true;
+ }
+ } catch (NullPointerException npe) {
+ gotN = true;
+ }
+
+ assert(gotA);
+ assert(!gotN);
+ assert(!gotWeird);
+ }
+
+ public void twoN() {
+ System.out.println("Throw.twoN");
+ boolean gotN = false;
+ boolean gotA = false;
+ boolean gotWeird = false;
+
+ try {
+ try {
+ throwNullPointerException();
+ gotWeird = true;
+ } catch (ArithmeticException ae) {
+ gotA = true;
+ }
+ } catch (NullPointerException npe) {
+ gotN = true;
+ }
+
+ assert(!gotA);
+ assert(gotN);
+ assert(!gotWeird);
+ }
+
+ public void rethrow() {
+ System.out.println("Throw.rethrow");
+ boolean caught = false;
+ boolean lly = false;
+ boolean second = false;
+
+ try {
+ try {
+ throwNullPointerException();
+ assert(false);
+ } catch (Exception ex) {
+ if (ex instanceof ArithmeticException) {
+ assert(false);
+ }
+ if (ex instanceof NullPointerException) {
+ caught = true;
+ throw (NullPointerException) ex;
+ }
+ } finally {
+ lly = true;
+ }
+ } catch (Exception ex) {
+ second = true;
+ }
+
+ assert(caught);
+ assert(lly);
+ assert(second);
+ }
+
+ public static void run() {
+ Throw th = new Throw();
+
+ th.one();
+ th.twoA();
+ th.twoN();
+ th.rethrow();
+ }
+}
diff --git a/tests/003-omnibus-opcodes/src/UnresClass.java b/tests/003-omnibus-opcodes/src/UnresClass.java
new file mode 100644
index 0000000..52b3d4f
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src/UnresClass.java
@@ -0,0 +1,9 @@
+/*
+ * Unresolved class.
+ *
+ * "happy" version.
+ */
+
+public class UnresClass {
+ int foo;
+}
diff --git a/tests/003-omnibus-opcodes/src/UnresStuff.java b/tests/003-omnibus-opcodes/src/UnresStuff.java
new file mode 100644
index 0000000..1d2a556
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src/UnresStuff.java
@@ -0,0 +1,22 @@
+/*
+ * Unresolved classes / fields / methods in a resolved class.
+ *
+ * "happy" version.
+ */
+
+public class UnresStuff {
+ public int instField;
+
+ public static int staticField;
+
+ public double wideInstField;
+ public static double wideStaticField;
+
+ public void virtualMethod() {
+ System.out.println("unres!");
+ }
+
+ public static void staticMethod() {
+ System.out.println("unres!");
+ }
+}
diff --git a/tests/003-omnibus-opcodes/src/UnresTest1.java b/tests/003-omnibus-opcodes/src/UnresTest1.java
new file mode 100644
index 0000000..5a80a7a
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src/UnresTest1.java
@@ -0,0 +1,80 @@
+/*
+ * Test failure to resolve class members.
+ */
+class UnresTest1 {
+ public static void run() {
+ System.out.println("UnresTest1...");
+
+ UnresStuff stuff = new UnresStuff();
+ try {
+ int x = stuff.instField;
+ assert(false);
+ } catch (NoSuchFieldError nsfe) {
+ // good
+ }
+ try { // hit the same one a second time
+ int x = stuff.instField;
+ assert(false);
+ } catch (NoSuchFieldError nsfe) {
+ // good
+ }
+ try {
+ stuff.instField = 5;
+ assert(false);
+ } catch (NoSuchFieldError nsfe) {
+ // good
+ }
+
+ try {
+ double d = stuff.wideInstField;
+ assert(false);
+ } catch (NoSuchFieldError nsfe) {
+ // good
+ }
+ try {
+ stuff.wideInstField = 0.0;
+ assert(false);
+ } catch (NoSuchFieldError nsfe) {
+ // good
+ }
+
+ try {
+ int y = UnresStuff.staticField;
+ assert(false);
+ } catch (NoSuchFieldError nsfe) {
+ // good
+ }
+ try {
+ UnresStuff.staticField = 17;
+ assert(false);
+ } catch (NoSuchFieldError nsfe) {
+ // good
+ }
+
+ try {
+ double d = UnresStuff.wideStaticField;
+ assert(false);
+ } catch (NoSuchFieldError nsfe) {
+ // good
+ }
+ try {
+ UnresStuff.wideStaticField = 1.0;
+ assert(false);
+ } catch (NoSuchFieldError nsfe) {
+ // good
+ }
+
+ try {
+ stuff.virtualMethod();
+ assert(false);
+ } catch (NoSuchMethodError nsfe) {
+ // good
+ }
+ try {
+ UnresStuff.staticMethod();
+ assert(false);
+ } catch (NoSuchMethodError nsfe) {
+ // good
+ }
+ }
+}
diff --git a/tests/003-omnibus-opcodes/src/UnresTest2.java b/tests/003-omnibus-opcodes/src/UnresTest2.java
new file mode 100644
index 0000000..768be8f
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src/UnresTest2.java
@@ -0,0 +1,49 @@
+/*
+ * Test failure to resolve classes.
+ */
+class UnresTest2 {
+ /*
+ * Try check-cast and instance-of.
+ */
+ static boolean checkCasts(Object obj) {
+ boolean foo = false;
+
+ try {
+ UnresClass un = (UnresClass) obj;
+ assert(false);
+ } catch (NoClassDefFoundError ncdfe) {
+ // good
+ }
+ try {
+ foo = obj instanceof UnresClass;
+ assert(false);
+ } catch (NoClassDefFoundError ncdfe) {
+ // good
+ }
+
+ return foo;
+ }
+
+ public static void run() {
+ System.out.println("UnresTest2...");
+ UnresClass un;
+ UnresStuff stuff = new UnresStuff();
+
+ try {
+ un = new UnresClass();
+ assert(false);
+ } catch (NoClassDefFoundError ncdfe) {
+ // good
+ }
+
+ try {
+ UnresClass[] uar = new UnresClass[3];
+ assert(false);
+ } catch (NoClassDefFoundError ncdfe) {
+ // good
+ }
+
+ checkCasts(stuff);
+ System.out.println("UnresTest2 done");
+ }
+}
diff --git a/tests/003-omnibus-opcodes/src2/UnresStuff.java b/tests/003-omnibus-opcodes/src2/UnresStuff.java
new file mode 100644
index 0000000..56f43af
--- /dev/null
+++ b/tests/003-omnibus-opcodes/src2/UnresStuff.java
@@ -0,0 +1,9 @@
+/*
+ * Unresolved classes / fields / methods in a resolved class.
+ *
+ * "happy" version.
+ */
+
+public class UnresStuff {
+ public int x;
+}
diff --git a/tests/004-annotations/expected.txt b/tests/004-annotations/expected.txt
new file mode 100644
index 0000000..063b3ed
--- /dev/null
+++ b/tests/004-annotations/expected.txt
@@ -0,0 +1,96 @@
+TestAnnotations...
+java.lang.String android.test.anno.TestAnnotations.thing1: @android.test.anno.AnnoArrayField(bb=[], cc=[a, b], dd=[0.987654321], ff=[3.1415927], ii=[], jj=[], ss=[], str=[], zz=[])
+java.lang.String android.test.anno.TestAnnotations.thing2: @android.test.anno.AnnoArrayField(bb=[-1, 0, 1], cc=[Q], dd=[0.3, 0.6, 0.9], ff=[1.1, 1.2, 1.3], ii=[1, 2, 3, 4], jj=[-5, 0, 5], ss=[12, 13, 14, 15, 16, 17], str=[hickory, dickory, dock], zz=[true, false, true])
+mapping is class [Landroid.test.anno.IntToString;
+ 0='@android.test.anno.IntToString(from=0, to=NORMAL_FOCUS)'
+ 1='@android.test.anno.IntToString(from=2, to=WEAK_FOCUS)'
+present(getFocusType, ExportedProperty): true
+present(getFocusType, AnnoSimpleType): false
+
+AnnoSimpleField true, SimplyNoted false
+annotations on TYPE class android.test.anno.SimplyNoted(2):
+ @android.test.anno.AnnoSimpleType()
+ interface android.test.anno.AnnoSimpleType
+ @android.test.anno.AnnoSimpleType2()
+ interface android.test.anno.AnnoSimpleType2
+
+ annotations on CTOR android.test.anno.SimplyNoted():
+ @android.test.anno.AnnoSimpleConstructor()
+ interface android.test.anno.AnnoSimpleConstructor
+ constructor parameter annotations:
+ annotations on CTOR android.test.anno.SimplyNoted(int):
+ @android.test.anno.AnnoSimpleConstructor()
+ interface android.test.anno.AnnoSimpleConstructor
+ constructor parameter annotations:
+ @android.test.anno.AnnoSimpleParameter()
+ interface android.test.anno.AnnoSimpleParameter
+ annotations on METH public int android.test.anno.SimplyNoted.foo():
+ @android.test.anno.AnnoSimpleMethod()
+ interface android.test.anno.AnnoSimpleMethod
+ method parameter annotations:
+ annotations on FIELD public static int android.test.anno.SimplyNoted.mOneFoo:
+ @android.test.anno.AnnoSimpleField()
+ interface android.test.anno.AnnoSimpleField
+ annotations on FIELD public int android.test.anno.SimplyNoted.mFoo:
+ @android.test.anno.AnnoSimpleField()
+ interface android.test.anno.AnnoSimpleField
+
+annotations on TYPE interface android.test.anno.INoted(1):
+ @android.test.anno.AnnoSimpleType2()
+ interface android.test.anno.AnnoSimpleType2
+
+ annotations on METH public abstract int android.test.anno.INoted.bar():
+ @android.test.anno.AnnoSimpleMethod()
+ interface android.test.anno.AnnoSimpleMethod
+ method parameter annotations:
+
+annotations on TYPE class android.test.anno.SubNoted(3):
+ @android.test.anno.AnnoFancyType(name=unknown, num=5)
+ interface android.test.anno.AnnoFancyType
+ @android.test.anno.AnnoSimpleType()
+ interface android.test.anno.AnnoSimpleType
+ @android.test.anno.AnnoSimpleType2()
+ interface android.test.anno.AnnoSimpleType2
+
+ annotations on CTOR public android.test.anno.SubNoted():
+ constructor parameter annotations:
+ annotations on METH public int android.test.anno.SubNoted.bar():
+ method parameter annotations:
+ annotations on FIELD int android.test.anno.SubNoted.mBar:
+
+annotations on TYPE class android.test.anno.FullyNoted(1):
+ @android.test.anno.AnnoFancyType(name=full, num=5)
+ interface android.test.anno.AnnoFancyType
+
+ annotations on CTOR android.test.anno.FullyNoted(int):
+ @android.test.anno.AnnoFancyConstructor(numArgs=1)
+ interface android.test.anno.AnnoFancyConstructor
+ constructor parameter annotations:
+ @android.test.anno.AnnoFancyParameter(factor=0.5)
+ interface android.test.anno.AnnoFancyParameter
+ annotations on METH public int android.test.anno.FullyNoted.bar(int,long) throws java.io.IOException,java.io.EOFException:
+ @android.test.anno.AnnoFancyMethod(biteMe=false, callMe=true, enumerated=FOO, someClass=class android.test.anno.SomeClass)
+ interface android.test.anno.AnnoFancyMethod
+ method parameter annotations:
+ @android.test.anno.AnnoSimpleParameter()
+ interface android.test.anno.AnnoSimpleParameter
+ @android.test.anno.AnnoFancyParameter(factor=3.7879912899761)
+ interface android.test.anno.AnnoFancyParameter
+ annotations on METH public int android.test.anno.FullyNoted.bar1(int,long) throws java.io.IOException:
+ @android.test.anno.AnnoFancyMethod(biteMe=true, callMe=false, enumerated=BAR, someClass=class android.test.anno.SomeClass)
+ interface android.test.anno.AnnoFancyMethod
+ method parameter annotations:
+ @android.test.anno.AnnoSimpleParameter()
+ interface android.test.anno.AnnoSimpleParameter
+ @android.test.anno.AnnoFancyParameter(factor=3.7879912899761)
+ interface android.test.anno.AnnoFancyParameter
+ annotations on METH public int android.test.anno.FullyNoted.notAnnotated():
+ method parameter annotations:
+ annotations on FIELD int android.test.anno.FullyNoted.mBar:
+ @android.test.anno.AnnoFancyField(nombre=fubar)
+ interface android.test.anno.AnnoFancyField
+ aff: @android.test.anno.AnnoFancyField(nombre=fubar) / class $Proxy17
+ --> nombre is 'fubar'
+
+SimplyNoted.get(AnnoSimpleType) = @android.test.anno.AnnoSimpleType()
+SubNoted.get(AnnoSimpleType) = @android.test.anno.AnnoSimpleType()
diff --git a/tests/004-annotations/info.txt b/tests/004-annotations/info.txt
new file mode 100644
index 0000000..c8c9280
--- /dev/null
+++ b/tests/004-annotations/info.txt
@@ -0,0 +1 @@
+Test a bunch of uses of annotations.
diff --git a/tests/004-annotations/src/Main.java b/tests/004-annotations/src/Main.java
new file mode 100644
index 0000000..e44722f
--- /dev/null
+++ b/tests/004-annotations/src/Main.java
@@ -0,0 +1,7 @@
+import android.test.anno.TestAnnotations;
+
+public class Main {
+ static public void main(String[] args) {
+ TestAnnotations.main(args);
+ }
+}
diff --git a/tests/004-annotations/src/android/test/anno/AnnoArrayField.java b/tests/004-annotations/src/android/test/anno/AnnoArrayField.java
new file mode 100644
index 0000000..681045c
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/AnnoArrayField.java
@@ -0,0 +1,19 @@
+package android.test.anno;
+
+import java.lang.annotation.*;
+
+@Documented
+@Inherited
+@Target({ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface AnnoArrayField {
+ boolean[] zz() default {};
+ byte[] bb() default {};
+ char[] cc() default {'a', 'b'};
+ short[] ss() default {};
+ int[] ii() default {};
+ float[] ff() default {3.141592654f};
+ long[] jj() default {};
+ double[] dd() default {0.987654321};
+ String[] str() default {};
+}
diff --git a/tests/004-annotations/src/android/test/anno/AnnoFancyConstructor.java b/tests/004-annotations/src/android/test/anno/AnnoFancyConstructor.java
new file mode 100644
index 0000000..19dadec
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/AnnoFancyConstructor.java
@@ -0,0 +1,10 @@
+package android.test.anno;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.CONSTRUCTOR)
+@Retention(RetentionPolicy.RUNTIME)
+
+public @interface AnnoFancyConstructor {
+ public int numArgs() default 0;
+}
diff --git a/tests/004-annotations/src/android/test/anno/AnnoFancyField.java b/tests/004-annotations/src/android/test/anno/AnnoFancyField.java
new file mode 100644
index 0000000..855ba56
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/AnnoFancyField.java
@@ -0,0 +1,12 @@
+package android.test.anno;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited // should have no effect
+@Documented
+
+public @interface AnnoFancyField {
+ public String nombre() default "no se";
+}
diff --git a/tests/004-annotations/src/android/test/anno/AnnoFancyMethod.java b/tests/004-annotations/src/android/test/anno/AnnoFancyMethod.java
new file mode 100644
index 0000000..3088866
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/AnnoFancyMethod.java
@@ -0,0 +1,14 @@
+package android.test.anno;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+
+public @interface AnnoFancyMethod {
+ enum AnnoFancyMethodEnum { FOO, BAR };
+ boolean callMe() default false;
+ boolean biteMe();
+ AnnoFancyMethodEnum enumerated() default AnnoFancyMethodEnum.FOO;
+ Class someClass() default SomeClass.class;
+}
diff --git a/tests/004-annotations/src/android/test/anno/AnnoFancyParameter.java b/tests/004-annotations/src/android/test/anno/AnnoFancyParameter.java
new file mode 100644
index 0000000..bc2ba7c
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/AnnoFancyParameter.java
@@ -0,0 +1,10 @@
+package android.test.anno;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.PARAMETER)
+@Retention(RetentionPolicy.RUNTIME)
+
+public @interface AnnoFancyParameter {
+ double factor();
+}
diff --git a/tests/004-annotations/src/android/test/anno/AnnoFancyType.java b/tests/004-annotations/src/android/test/anno/AnnoFancyType.java
new file mode 100644
index 0000000..745f838
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/AnnoFancyType.java
@@ -0,0 +1,11 @@
+package android.test.anno;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+
+public @interface AnnoFancyType {
+ public int num();
+ public String name() default "unknown";
+}
diff --git a/tests/004-annotations/src/android/test/anno/AnnoSimpleConstructor.java b/tests/004-annotations/src/android/test/anno/AnnoSimpleConstructor.java
new file mode 100644
index 0000000..d66b9ae
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/AnnoSimpleConstructor.java
@@ -0,0 +1,8 @@
+package android.test.anno;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.CONSTRUCTOR)
+@Retention(RetentionPolicy.RUNTIME)
+
+public @interface AnnoSimpleConstructor {}
diff --git a/tests/004-annotations/src/android/test/anno/AnnoSimpleField.java b/tests/004-annotations/src/android/test/anno/AnnoSimpleField.java
new file mode 100644
index 0000000..04193d2
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/AnnoSimpleField.java
@@ -0,0 +1,8 @@
+package android.test.anno;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+
+public @interface AnnoSimpleField {}
diff --git a/tests/004-annotations/src/android/test/anno/AnnoSimpleLocalVariable.java b/tests/004-annotations/src/android/test/anno/AnnoSimpleLocalVariable.java
new file mode 100644
index 0000000..a839fa2
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/AnnoSimpleLocalVariable.java
@@ -0,0 +1,8 @@
+package android.test.anno;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.LOCAL_VARIABLE)
+@Retention(RetentionPolicy.RUNTIME)
+
+public @interface AnnoSimpleLocalVariable {}
diff --git a/tests/004-annotations/src/android/test/anno/AnnoSimpleMethod.java b/tests/004-annotations/src/android/test/anno/AnnoSimpleMethod.java
new file mode 100644
index 0000000..fcd9c0f
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/AnnoSimpleMethod.java
@@ -0,0 +1,8 @@
+package android.test.anno;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+
+public @interface AnnoSimpleMethod {}
diff --git a/tests/004-annotations/src/android/test/anno/AnnoSimplePackage.java b/tests/004-annotations/src/android/test/anno/AnnoSimplePackage.java
new file mode 100644
index 0000000..4aadfe7
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/AnnoSimplePackage.java
@@ -0,0 +1,8 @@
+package android.test.anno;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.PACKAGE)
+@Retention(RetentionPolicy.RUNTIME)
+
+public @interface AnnoSimplePackage {}
diff --git a/tests/004-annotations/src/android/test/anno/AnnoSimpleParameter.java b/tests/004-annotations/src/android/test/anno/AnnoSimpleParameter.java
new file mode 100644
index 0000000..6e26ca3
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/AnnoSimpleParameter.java
@@ -0,0 +1,8 @@
+package android.test.anno;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.PARAMETER)
+@Retention(RetentionPolicy.RUNTIME)
+
+public @interface AnnoSimpleParameter {}
diff --git a/tests/004-annotations/src/android/test/anno/AnnoSimpleType.java b/tests/004-annotations/src/android/test/anno/AnnoSimpleType.java
new file mode 100644
index 0000000..3bba3db
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/AnnoSimpleType.java
@@ -0,0 +1,9 @@
+package android.test.anno;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+@Documented
+public @interface AnnoSimpleType {}
diff --git a/tests/004-annotations/src/android/test/anno/AnnoSimpleType2.java b/tests/004-annotations/src/android/test/anno/AnnoSimpleType2.java
new file mode 100644
index 0000000..f74b291
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/AnnoSimpleType2.java
@@ -0,0 +1,8 @@
+package android.test.anno;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface AnnoSimpleType2 {}
diff --git a/tests/004-annotations/src/android/test/anno/AnnoSimpleTypeInvis.java b/tests/004-annotations/src/android/test/anno/AnnoSimpleTypeInvis.java
new file mode 100644
index 0000000..541d82e
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/AnnoSimpleTypeInvis.java
@@ -0,0 +1,8 @@
+package android.test.anno;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.CLASS)
+
+public @interface AnnoSimpleTypeInvis {}
diff --git a/tests/004-annotations/src/android/test/anno/ExportedProperty.java b/tests/004-annotations/src/android/test/anno/ExportedProperty.java
new file mode 100644
index 0000000..810d28f
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/ExportedProperty.java
@@ -0,0 +1,12 @@
+/* part of test for array problem */
+package android.test.anno;
+
+import java.lang.annotation.*;
+
+@Target({ ElementType.FIELD, ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+
+public @interface ExportedProperty {
+ boolean resolveId() default false;
+ IntToString[] mapping() default { @IntToString(from = -1, to = "-1") };
+}
diff --git a/tests/004-annotations/src/android/test/anno/FullyNoted.java b/tests/004-annotations/src/android/test/anno/FullyNoted.java
new file mode 100644
index 0000000..76a7b04
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/FullyNoted.java
@@ -0,0 +1,39 @@
+
+package android.test.anno;
+
+import java.io.IOException;
+import java.io.EOFException;
+
+@AnnoFancyType(num=5, name="full")
+public class FullyNoted {
+ @AnnoFancyField(nombre="fubar")
+ int mBar;
+
+ @AnnoFancyConstructor(numArgs=1)
+ FullyNoted(@AnnoFancyParameter(factor=0.5) int bar)
+ {
+ mBar = bar;
+ }
+
+ @AnnoFancyMethod(callMe=true, biteMe=false)
+ public int bar(
+ @AnnoSimpleParameter int one,
+ @AnnoFancyParameter(factor=3.7879912899761) long two)
+ throws IOException, EOFException {
+
+ return 0;
+ }
+
+ @AnnoFancyMethod(biteMe=true, enumerated=AnnoFancyMethod.AnnoFancyMethodEnum.BAR)
+ public int bar1(
+ @AnnoSimpleParameter int one,
+ @AnnoFancyParameter(factor=3.7879912899761) long two)
+ throws IOException {
+
+ return 0;
+ }
+
+ public int notAnnotated() {
+ return 0;
+ }
+}
diff --git a/tests/004-annotations/src/android/test/anno/INoted.java b/tests/004-annotations/src/android/test/anno/INoted.java
new file mode 100644
index 0000000..b2cd007
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/INoted.java
@@ -0,0 +1,7 @@
+package android.test.anno;
+
+@AnnoSimpleType2
+public interface INoted {
+ @AnnoSimpleMethod
+ public int bar();
+}
diff --git a/tests/004-annotations/src/android/test/anno/IntToString.java b/tests/004-annotations/src/android/test/anno/IntToString.java
new file mode 100644
index 0000000..e84fcfa
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/IntToString.java
@@ -0,0 +1,12 @@
+/* part of test for array problem */
+package android.test.anno;
+
+import java.lang.annotation.*;
+
+@Target({ ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+
+public @interface IntToString {
+ int from();
+ String to();
+}
diff --git a/tests/004-annotations/src/android/test/anno/SimplyNoted.java b/tests/004-annotations/src/android/test/anno/SimplyNoted.java
new file mode 100644
index 0000000..95a3d24
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/SimplyNoted.java
@@ -0,0 +1,30 @@
+package android.test.anno;
+
+@AnnoSimpleType
+@AnnoSimpleType2
+@AnnoSimpleTypeInvis
+public class SimplyNoted {
+ @AnnoSimpleField
+ public int mFoo;
+
+ @AnnoSimpleField
+ public static int mOneFoo;
+
+ @AnnoSimpleConstructor
+ SimplyNoted() {
+ mFoo = 0;
+ }
+
+ @AnnoSimpleConstructor
+ SimplyNoted(@AnnoSimpleParameter int xyzzy) {
+ mFoo = xyzzy;
+ }
+
+ @AnnoSimpleMethod
+ public int foo() {
+ @AnnoSimpleLocalVariable
+ int bar = 5;
+
+ return bar;
+ }
+}
diff --git a/tests/004-annotations/src/android/test/anno/SomeClass.java b/tests/004-annotations/src/android/test/anno/SomeClass.java
new file mode 100644
index 0000000..c21d68d
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/SomeClass.java
@@ -0,0 +1,4 @@
+package android.test.anno;
+
+public class SomeClass {
+}
diff --git a/tests/004-annotations/src/android/test/anno/SubNoted.java b/tests/004-annotations/src/android/test/anno/SubNoted.java
new file mode 100644
index 0000000..2530346
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/SubNoted.java
@@ -0,0 +1,12 @@
+package android.test.anno;
+
+@AnnoFancyType(num=5) // first occurrence of AnnoFancyType
+ // we inherit @AnnoSimpleType
+@AnnoSimpleType2 // AnnoSimpleType2 here *and* inherited from parent
+public class SubNoted extends SimplyNoted implements INoted {
+ int mBar;
+
+ public int bar() {
+ return 0;
+ }
+}
diff --git a/tests/004-annotations/src/android/test/anno/TestAnnotations.java b/tests/004-annotations/src/android/test/anno/TestAnnotations.java
new file mode 100644
index 0000000..4ad32d5
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/TestAnnotations.java
@@ -0,0 +1,177 @@
+package android.test.anno;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.TreeMap;
+
+public class TestAnnotations {
+ /**
+ * Print the annotations in sorted order, so as to avoid
+ * any (legitimate) non-determinism with regard to the iteration order.
+ */
+ static private void printAnnotationArray(String prefix, Annotation[] arr) {
+ TreeMap<String, Annotation> sorted =
+ new TreeMap<String, Annotation>();
+
+ for (Annotation a : arr) {
+ sorted.put(a.annotationType().getName(), a);
+ }
+
+ for (Annotation a : sorted.values()) {
+ System.out.println(prefix + " " + a);
+ System.out.println(prefix + " " + a.annotationType());
+ }
+ }
+
+ static void printAnnotations(Class clazz) {
+ Annotation[] annos;
+ Annotation[][] parAnnos;
+
+ annos = clazz.getAnnotations();
+ System.out.println("annotations on TYPE " + clazz +
+ "(" + annos.length + "):");
+ printAnnotationArray("", annos);
+ System.out.println();
+
+ for (Constructor c: clazz.getDeclaredConstructors()) {
+ annos = c.getDeclaredAnnotations();
+ System.out.println(" annotations on CTOR " + c + ":");
+ printAnnotationArray(" ", annos);
+
+ System.out.println(" constructor parameter annotations:");
+ for (Annotation[] pannos: c.getParameterAnnotations()) {
+ printAnnotationArray(" ", pannos);
+ }
+ }
+
+ for (Method m: clazz.getDeclaredMethods()) {
+ annos = m.getDeclaredAnnotations();
+ System.out.println(" annotations on METH " + m + ":");
+ printAnnotationArray(" ", annos);
+
+ System.out.println(" method parameter annotations:");
+ for (Annotation[] pannos: m.getParameterAnnotations()) {
+ printAnnotationArray(" ", pannos);
+ }
+ }
+
+ for (Field f: clazz.getDeclaredFields()) {
+ annos = f.getDeclaredAnnotations();
+ System.out.println(" annotations on FIELD " + f + ":");
+ printAnnotationArray(" ", annos);
+
+ AnnoFancyField aff;
+ aff = (AnnoFancyField) f.getAnnotation(AnnoFancyField.class);
+ if (aff != null) {
+ System.out.println(" aff: " + aff + " / " + aff.getClass());
+ System.out.println(" --> nombre is '" + aff.nombre() + "'");
+ }
+ }
+ System.out.println();
+ }
+
+
+ @ExportedProperty(mapping = {
+ @IntToString(from = 0, to = "NORMAL_FOCUS"),
+ @IntToString(from = 2, to = "WEAK_FOCUS")
+ })
+ public int getFocusType() {
+ return 2;
+ }
+
+
+ @AnnoArrayField
+ String thing1;
+
+ @AnnoArrayField(
+ zz = {true,false,true},
+ bb = {-1,0,1},
+ cc = {'Q'},
+ ss = {12,13,14,15,16,17},
+ ii = {1,2,3,4},
+ ff = {1.1f,1.2f,1.3f},
+ jj = {-5,0,5},
+ dd = {0.3,0.6,0.9},
+ str = {"hickory","dickory","dock"}
+ )
+ String thing2;
+
+ public static void testArrays() {
+ TestAnnotations ta = new TestAnnotations();
+ Field field;
+ Annotation[] annotations;
+
+ try {
+ field = TestAnnotations.class.getDeclaredField("thing1");
+ annotations = field.getAnnotations();
+ System.out.println(field + ": " + annotations[0].toString());
+
+ field = TestAnnotations.class.getDeclaredField("thing2");
+ annotations = field.getAnnotations();
+ System.out.println(field + ": " + annotations[0].toString());
+ } catch (NoSuchFieldException nsfe) {
+ throw new RuntimeException(nsfe);
+ }
+ }
+
+ public static void testArrayProblem() {
+ Method meth;
+ ExportedProperty property;
+ final IntToString[] mapping;
+
+ try {
+ meth = TestAnnotations.class.getMethod("getFocusType",
+ (Class[])null);
+ } catch (NoSuchMethodException nsme) {
+ throw new RuntimeException(nsme);
+ }
+ property = meth.getAnnotation(ExportedProperty.class);
+ mapping = property.mapping();
+
+ System.out.println("mapping is " + mapping.getClass() +
+ "\n 0='" + mapping[0] + "'\n 1='" + mapping[1] + "'");
+
+ /* while we're here, check isAnnotationPresent on Method */
+ System.out.println("present(getFocusType, ExportedProperty): " +
+ meth.isAnnotationPresent(ExportedProperty.class));
+ System.out.println("present(getFocusType, AnnoSimpleType): " +
+ meth.isAnnotationPresent(AnnoSimpleType.class));
+
+ System.out.println("");
+ }
+
+
+
+ public static void main(String[] args) {
+ System.out.println("TestAnnotations...");
+
+ testArrays();
+ testArrayProblem();
+ //System.exit(0);
+
+ System.out.println(
+ "AnnoSimpleField " + AnnoSimpleField.class.isAnnotation() +
+ ", SimplyNoted " + SimplyNoted.class.isAnnotation());
+
+ Class clazz;
+ clazz = SimplyNoted.class;
+ printAnnotations(clazz);
+ clazz = INoted.class;
+ printAnnotations(clazz);
+ clazz = SubNoted.class;
+ printAnnotations(clazz);
+ clazz = FullyNoted.class;
+ printAnnotations(clazz);
+
+ Annotation anno;
+
+ // this is expected to be non-null
+ anno = SimplyNoted.class.getAnnotation(AnnoSimpleType.class);
+ System.out.println("SimplyNoted.get(AnnoSimpleType) = " + anno);
+ // this is non-null if the @Inherited tag is present
+ anno = SubNoted.class.getAnnotation(AnnoSimpleType.class);
+ System.out.println("SubNoted.get(AnnoSimpleType) = " + anno);
+ }
+}
diff --git a/tests/004-annotations/src/android/test/anno/package-info.java b/tests/004-annotations/src/android/test/anno/package-info.java
new file mode 100644
index 0000000..74b11f9
--- /dev/null
+++ b/tests/004-annotations/src/android/test/anno/package-info.java
@@ -0,0 +1,2 @@
+@AnnoSimplePackage
+package android.test.anno;
diff --git a/tests/005-args/expected.txt b/tests/005-args/expected.txt
new file mode 100644
index 0000000..094fbbb
--- /dev/null
+++ b/tests/005-args/expected.txt
@@ -0,0 +1,5 @@
+VALUES: 1122334455667788 9887766554433221 1122334455667788
+VALUES: 1234605616436508552 -7455860480511102431 1234605616436508552
+1234605616436508552
+j = 1234605616436508552
+a=123 c=q d=3.343434 j=1234605616436508552 f=0.12345
diff --git a/tests/005-args/info.txt b/tests/005-args/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/005-args/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/005-args/src/ArgsTest.java b/tests/005-args/src/ArgsTest.java
new file mode 100644
index 0000000..2b874cd
--- /dev/null
+++ b/tests/005-args/src/ArgsTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class ArgsTest{
+ public ArgsTest() {
+
+ }
+
+ private long mLongArray[] = new long[] {
+ 0x1122334455667788L, 0x9887766554433221L };
+
+ /**
+ *
+ * @param a
+ * @param c
+ * @param d
+ * @param j
+ * @param f
+ */
+ void argTest(int a, char c, double d, long j, float f) {
+ System.out.println("VALUES: " + Long.toHexString(mLongArray[0]) + " "
+ + Long.toHexString(mLongArray[1]) + " " + Long.toHexString(j));
+ System.out.println("VALUES: " + mLongArray[0] + " "
+ + mLongArray[1] + " " + j);
+
+ System.out.println(j);
+ System.out.println("j = " + j);
+ System.out.println("a=" + a + " c=" + c + " d=" + d
+ + " j=" + j + " f=" + f);
+ }
+}
diff --git a/tests/005-args/src/Main.java b/tests/005-args/src/Main.java
new file mode 100644
index 0000000..1240ec5
--- /dev/null
+++ b/tests/005-args/src/Main.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Test passing arguments of various sizes
+ */
+public class Main {
+ public static void main (String args[]) {
+ new ArgsTest()
+ .argTest(123, 'q', 3.343434, 0x1122334455667788L, 0.12345f);
+ }
+}
diff --git a/tests/006-count10/expected.txt b/tests/006-count10/expected.txt
new file mode 100644
index 0000000..8b1acc1
--- /dev/null
+++ b/tests/006-count10/expected.txt
@@ -0,0 +1,10 @@
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
diff --git a/tests/006-count10/info.txt b/tests/006-count10/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/006-count10/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/006-count10/src/Main.java b/tests/006-count10/src/Main.java
new file mode 100644
index 0000000..650d053
--- /dev/null
+++ b/tests/006-count10/src/Main.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Simple loop-and-print
+ */
+public class Main {
+ public static void main(String args[]) {
+ for (int i = 0; i < 10; i++) {
+ System.out.println(i);
+ }
+ }
+}
diff --git a/tests/007-exceptions/expected.txt b/tests/007-exceptions/expected.txt
new file mode 100644
index 0000000..2a31636
--- /dev/null
+++ b/tests/007-exceptions/expected.txt
@@ -0,0 +1,9 @@
+Got an NPE: second throw
+java.lang.NullPointerException: second throw
+ at Main.catchAndRethrow(Main.java:36)
+ at Main.main(Main.java:23)
+ at dalvik.system.NativeStart.main(Native Method)
+Caused by: java.lang.NullPointerException: first throw
+ at Main.throwNullPointerException(Main.java:43)
+ at Main.catchAndRethrow(Main.java:33)
+ ... 2 more
diff --git a/tests/007-exceptions/info.txt b/tests/007-exceptions/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/007-exceptions/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/007-exceptions/src/Main.java b/tests/007-exceptions/src/Main.java
new file mode 100644
index 0000000..c7da215
--- /dev/null
+++ b/tests/007-exceptions/src/Main.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Exceptions across method calls
+ */
+public class Main {
+ public static void main (String args[]) {
+ try {
+ catchAndRethrow();
+ } catch (NullPointerException npe) {
+ System.out.print("Got an NPE: ");
+ System.out.println(npe.getMessage());
+ npe.printStackTrace();
+ }
+ }
+
+ private static void catchAndRethrow() {
+ try {
+ throwNullPointerException();
+ } catch (NullPointerException npe) {
+ NullPointerException npe2;
+ npe2 = new NullPointerException("second throw");
+ npe2.initCause(npe);
+ throw npe2;
+ }
+ }
+
+ private static void throwNullPointerException() {
+ throw new NullPointerException("first throw");
+ }
+}
diff --git a/tests/008-instanceof/expected.txt b/tests/008-instanceof/expected.txt
new file mode 100644
index 0000000..77fd0cb
--- /dev/null
+++ b/tests/008-instanceof/expected.txt
@@ -0,0 +1,6 @@
+iface1.mFloaty = 5.0 wahoo
+aa.mFloaty = 5.0 wahoo
+bb.mWhoami = ImplB!
+aaOkay (false) = false
+bbOkay (true) = true
+Caught a ClassCastException (expected)
diff --git a/tests/008-instanceof/info.txt b/tests/008-instanceof/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/008-instanceof/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/008-instanceof/src/Iface1.java b/tests/008-instanceof/src/Iface1.java
new file mode 100644
index 0000000..ba17d45
--- /dev/null
+++ b/tests/008-instanceof/src/Iface1.java
@@ -0,0 +1,13 @@
+// Copyright 2005 The Android Open Source Project
+
+/**
+ * Test stuff.
+ */
+public interface Iface1 {
+
+ public int iFunc1(int ii);
+
+ public float mFloaty = 5.0f;
+
+ public String mWahoo = new String("wahoo");
+}
diff --git a/tests/008-instanceof/src/Iface2.java b/tests/008-instanceof/src/Iface2.java
new file mode 100644
index 0000000..83fe650
--- /dev/null
+++ b/tests/008-instanceof/src/Iface2.java
@@ -0,0 +1,9 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Another interface.
+ */
+public interface Iface2 {
+
+ public int iFunc2(int ii);
+}
diff --git a/tests/008-instanceof/src/Iface2Sub1.java b/tests/008-instanceof/src/Iface2Sub1.java
new file mode 100644
index 0000000..db3e905
--- /dev/null
+++ b/tests/008-instanceof/src/Iface2Sub1.java
@@ -0,0 +1,9 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Another interface.
+ */
+public interface Iface2Sub1 extends Iface2, Cloneable {
+
+ //public int iFunc2(int ii);
+}
diff --git a/tests/008-instanceof/src/ImplA.java b/tests/008-instanceof/src/ImplA.java
new file mode 100644
index 0000000..9007001
--- /dev/null
+++ b/tests/008-instanceof/src/ImplA.java
@@ -0,0 +1,14 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Blah.
+ */
+public class ImplA implements Iface1, Iface2 {
+
+ public int iFunc1(int ii) {
+ return ii+1;
+ }
+ public int iFunc2(int ii) {
+ return ii+2;
+ }
+}
diff --git a/tests/008-instanceof/src/ImplB.java b/tests/008-instanceof/src/ImplB.java
new file mode 100644
index 0000000..619fa00
--- /dev/null
+++ b/tests/008-instanceof/src/ImplB.java
@@ -0,0 +1,16 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Blah.
+ */
+public class ImplB implements Iface1, Iface2 {
+
+ public int iFunc1(int ii) {
+ return ii+10;
+ }
+ public int iFunc2(int ii) {
+ return ii+20;
+ }
+
+ public static String mWhoami = new String("ImplB!");
+}
diff --git a/tests/008-instanceof/src/ImplBSub.java b/tests/008-instanceof/src/ImplBSub.java
new file mode 100644
index 0000000..f3a7714
--- /dev/null
+++ b/tests/008-instanceof/src/ImplBSub.java
@@ -0,0 +1,14 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Interface test.
+ */
+public class ImplBSub extends ImplB implements /*Iface2,*/ Iface2Sub1 {
+
+ public int iFunc1(int ii) {
+ return ii+100;
+ }
+ public int iFunc2(int ii) {
+ return ii+200;
+ }
+}
diff --git a/tests/008-instanceof/src/Main.java b/tests/008-instanceof/src/Main.java
new file mode 100644
index 0000000..77f3f51
--- /dev/null
+++ b/tests/008-instanceof/src/Main.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Test instanceof
+ */
+public class Main {
+ public static void main(String args[]) {
+ Iface1 face1;
+ ImplA aa = new ImplA();
+ ImplBSub bb = new ImplBSub();
+ boolean aaOkay, bbOkay;
+
+ face1 = aa;
+ face1 = bb;
+
+ System.out.println("iface1.mFloaty = " + face1.mFloaty + " " + face1.mWahoo);
+ System.out.println("aa.mFloaty = " + aa.mFloaty + " " + aa.mWahoo);
+ System.out.println("bb.mWhoami = " + bb.mWhoami);
+
+ aaOkay = face1 instanceof ImplA;
+ System.out.print("aaOkay (false) = ");
+ System.out.println(aaOkay);
+ bbOkay = face1 instanceof ImplB;
+ System.out.print("bbOkay (true) = ");
+ System.out.println(bbOkay);
+
+ bb = (ImplBSub) face1;
+ try {
+ aa = (ImplA) face1;
+ }
+ catch (ClassCastException cce) {
+ System.out.println("Caught a ClassCastException (expected)");
+ }
+ }
+}
diff --git a/tests/009-instanceof2/expected.txt b/tests/009-instanceof2/expected.txt
new file mode 100644
index 0000000..74ad202
--- /dev/null
+++ b/tests/009-instanceof2/expected.txt
@@ -0,0 +1,5 @@
+instanceof Serializable = true
+instanceof Cloneable = true
+instanceof Runnable = false
+aaOkay (false) = false
+bbOkay (true) = true
diff --git a/tests/009-instanceof2/info.txt b/tests/009-instanceof2/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/009-instanceof2/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/009-instanceof2/src/Iface1.java b/tests/009-instanceof2/src/Iface1.java
new file mode 100644
index 0000000..ba17d45
--- /dev/null
+++ b/tests/009-instanceof2/src/Iface1.java
@@ -0,0 +1,13 @@
+// Copyright 2005 The Android Open Source Project
+
+/**
+ * Test stuff.
+ */
+public interface Iface1 {
+
+ public int iFunc1(int ii);
+
+ public float mFloaty = 5.0f;
+
+ public String mWahoo = new String("wahoo");
+}
diff --git a/tests/009-instanceof2/src/Iface2.java b/tests/009-instanceof2/src/Iface2.java
new file mode 100644
index 0000000..83fe650
--- /dev/null
+++ b/tests/009-instanceof2/src/Iface2.java
@@ -0,0 +1,9 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Another interface.
+ */
+public interface Iface2 {
+
+ public int iFunc2(int ii);
+}
diff --git a/tests/009-instanceof2/src/Iface2Sub1.java b/tests/009-instanceof2/src/Iface2Sub1.java
new file mode 100644
index 0000000..db3e905
--- /dev/null
+++ b/tests/009-instanceof2/src/Iface2Sub1.java
@@ -0,0 +1,9 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Another interface.
+ */
+public interface Iface2Sub1 extends Iface2, Cloneable {
+
+ //public int iFunc2(int ii);
+}
diff --git a/tests/009-instanceof2/src/ImplA.java b/tests/009-instanceof2/src/ImplA.java
new file mode 100644
index 0000000..9007001
--- /dev/null
+++ b/tests/009-instanceof2/src/ImplA.java
@@ -0,0 +1,14 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Blah.
+ */
+public class ImplA implements Iface1, Iface2 {
+
+ public int iFunc1(int ii) {
+ return ii+1;
+ }
+ public int iFunc2(int ii) {
+ return ii+2;
+ }
+}
diff --git a/tests/009-instanceof2/src/ImplB.java b/tests/009-instanceof2/src/ImplB.java
new file mode 100644
index 0000000..619fa00
--- /dev/null
+++ b/tests/009-instanceof2/src/ImplB.java
@@ -0,0 +1,16 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Blah.
+ */
+public class ImplB implements Iface1, Iface2 {
+
+ public int iFunc1(int ii) {
+ return ii+10;
+ }
+ public int iFunc2(int ii) {
+ return ii+20;
+ }
+
+ public static String mWhoami = new String("ImplB!");
+}
diff --git a/tests/009-instanceof2/src/ImplBSub.java b/tests/009-instanceof2/src/ImplBSub.java
new file mode 100644
index 0000000..f3a7714
--- /dev/null
+++ b/tests/009-instanceof2/src/ImplBSub.java
@@ -0,0 +1,14 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Interface test.
+ */
+public class ImplBSub extends ImplB implements /*Iface2,*/ Iface2Sub1 {
+
+ public int iFunc1(int ii) {
+ return ii+100;
+ }
+ public int iFunc2(int ii) {
+ return ii+200;
+ }
+}
diff --git a/tests/009-instanceof2/src/Main.java b/tests/009-instanceof2/src/Main.java
new file mode 100644
index 0000000..15a1e50
--- /dev/null
+++ b/tests/009-instanceof2/src/Main.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * more instanceof cases
+ */
+public class Main {
+ public static void main(String args[]) {
+ Iface1[] faceArray;
+ ImplA[] aaArray = new ImplA[5];
+ ImplBSub[] bbArray = new ImplBSub[5];
+ boolean aaOkay, bbOkay;
+
+ faceArray = aaArray;
+ faceArray = bbArray;
+
+ System.out.print("instanceof Serializable = ");
+ System.out.println((Object)aaArray instanceof java.io.Serializable);
+ System.out.print("instanceof Cloneable = ");
+ System.out.println((Object)aaArray instanceof java.lang.Cloneable);
+ System.out.print("instanceof Runnable = ");
+ System.out.println((Object)aaArray instanceof java.lang.Runnable);
+
+ aaOkay = faceArray instanceof ImplA[];
+ System.out.print("aaOkay (false) = ");
+ System.out.println(aaOkay);
+ bbOkay = faceArray instanceof ImplB[];
+ System.out.print("bbOkay (true) = ");
+ System.out.println(bbOkay);
+ }
+}
diff --git a/tests/010-instance/expected.txt b/tests/010-instance/expected.txt
new file mode 100644
index 0000000..219dd06
--- /dev/null
+++ b/tests/010-instance/expected.txt
@@ -0,0 +1,30 @@
+instance begin
+x instanceof X (true): true
+x instanceof Y (false): false
+y instanceof X (true): true
+y instanceof Y (true): true
+xar instanceof Object (true): true
+xar instanceof X (false): false
+xar instanceof X[] (true): true
+xar instanceof Y[] (false): false
+xar instanceof Object[] (true): true
+xar instanceof X[][] (false): false
+yar instanceof X[] (true): true
+xararar instanceof Object (true): true
+xararar instanceof Object[] (true): true
+xararar instanceof X (false): false
+xararar instanceof X[] (false): false
+xararar instanceof X[][] (false): false
+xararar instanceof X[][][] (true): true
+xararar instanceof Object[][][] (true): true
+xararar instanceof Serializable (true): true
+xararar instanceof Serializable[] (true): true
+xararar instanceof Serializable[][] (true): true
+xararar instanceof Serializable[][][] (false): false
+yararar instanceof X[][][] (true): true
+iar instanceof Object (true): true
+iar instanceof Object[] (false): false
+iarar instanceof Object (true): true
+iarar instanceof Object[] (true): true
+iarar instanceof Object[][] (false): false
+instanceof end
diff --git a/tests/010-instance/info.txt b/tests/010-instance/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/010-instance/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/010-instance/src/InstanceTest.java b/tests/010-instance/src/InstanceTest.java
new file mode 100644
index 0000000..78384ff
--- /dev/null
+++ b/tests/010-instance/src/InstanceTest.java
@@ -0,0 +1,93 @@
+// Copyright 2006 The Android Open Source Project
+
+import java.io.Serializable;
+
+/**
+ * Test some instanceof stuff.
+ */
+public class InstanceTest {
+ public static void main(String[] args) {
+ System.out.println("instance begin");
+
+ X x = new X();
+ X[] xar = new X[1];
+ X[][] xarar = new X[1][1];
+ X[][][] xararar = new X[1][1][1];
+ Y y = new Y();
+ Y[] yar = new Y[1];
+ Y[][] yarar = new Y[1][1];
+ Y[][][] yararar = new Y[1][1][1];
+ int[] iar = new int[1];
+ int[][] iarar = new int[1][1];
+ Object test;
+
+ test = x;
+ System.out.println("x instanceof X (true): " + (test instanceof X));
+ System.out.println("x instanceof Y (false): " + (test instanceof Y));
+ test = y;
+ System.out.println("y instanceof X (true): " + (test instanceof X));
+ System.out.println("y instanceof Y (true): " + (test instanceof Y));
+
+ test = xar;
+ System.out.println("xar instanceof Object (true): "
+ + (test instanceof Object));
+ System.out.println("xar instanceof X (false): "
+ + (test instanceof X));
+ System.out.println("xar instanceof X[] (true): "
+ + (test instanceof X[]));
+ System.out.println("xar instanceof Y[] (false): "
+ + (test instanceof Y[]));
+ System.out.println("xar instanceof Object[] (true): "
+ + (test instanceof Object[]));
+ System.out.println("xar instanceof X[][] (false): "
+ + (test instanceof X[][]));
+ test = yar;
+ System.out.println("yar instanceof X[] (true): "
+ + (test instanceof X[]));
+
+ test = xararar;
+ System.out.println("xararar instanceof Object (true): "
+ + (test instanceof Object));
+ System.out.println("xararar instanceof Object[] (true): "
+ + (test instanceof Object[]));
+ System.out.println("xararar instanceof X (false): "
+ + (test instanceof X));
+ System.out.println("xararar instanceof X[] (false): "
+ + (test instanceof X[]));
+ System.out.println("xararar instanceof X[][] (false): "
+ + (test instanceof X[][]));
+ System.out.println("xararar instanceof X[][][] (true): "
+ + (test instanceof X[][][]));
+ System.out.println("xararar instanceof Object[][][] (true): "
+ + (test instanceof Object[][][]));
+
+ System.out.println("xararar instanceof Serializable (true): "
+ + (test instanceof Serializable));
+ System.out.println("xararar instanceof Serializable[] (true): "
+ + (test instanceof Serializable[]));
+ System.out.println("xararar instanceof Serializable[][] (true): "
+ + (test instanceof Serializable[][]));
+ System.out.println("xararar instanceof Serializable[][][] (false): "
+ + (test instanceof Serializable[][][]));
+
+ test = yararar;
+ System.out.println("yararar instanceof X[][][] (true): "
+ + (test instanceof X[][][]));
+
+ test = iar;
+ System.out.println("iar instanceof Object (true): "
+ + (test instanceof Object));
+ System.out.println("iar instanceof Object[] (false): "
+ + (test instanceof Object[]));
+
+ test = iarar;
+ System.out.println("iarar instanceof Object (true): "
+ + (test instanceof Object));
+ System.out.println("iarar instanceof Object[] (true): "
+ + (test instanceof Object[]));
+ System.out.println("iarar instanceof Object[][] (false): "
+ + (test instanceof Object[][]));
+
+ System.out.println("instanceof end");
+ }
+}
diff --git a/tests/010-instance/src/Main.java b/tests/010-instance/src/Main.java
new file mode 100644
index 0000000..ab0ab5e
--- /dev/null
+++ b/tests/010-instance/src/Main.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * even more instanceof cases
+ */
+public class Main {
+ public static void main(String args[]) {
+ InstanceTest.main(null);
+ }
+}
diff --git a/tests/010-instance/src/X.java b/tests/010-instance/src/X.java
new file mode 100644
index 0000000..d664d48
--- /dev/null
+++ b/tests/010-instance/src/X.java
@@ -0,0 +1,8 @@
+class X {
+ public X() {
+ }
+
+ int foo() {
+ return 0;
+ }
+}
diff --git a/tests/010-instance/src/Y.java b/tests/010-instance/src/Y.java
new file mode 100644
index 0000000..2b2c371
--- /dev/null
+++ b/tests/010-instance/src/Y.java
@@ -0,0 +1,8 @@
+class Y extends X {
+ public Y() {
+ }
+
+ int bar() {
+ return 1;
+ }
+}
diff --git a/tests/011-array-copy/expected.txt b/tests/011-array-copy/expected.txt
new file mode 100644
index 0000000..0ef2029
--- /dev/null
+++ b/tests/011-array-copy/expected.txt
@@ -0,0 +1,4 @@
+string -> object
+object -> string
+object -> string (modified)
+caught ArrayStoreException (expected)
diff --git a/tests/011-array-copy/info.txt b/tests/011-array-copy/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/011-array-copy/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/011-array-copy/src/Iface1.java b/tests/011-array-copy/src/Iface1.java
new file mode 100644
index 0000000..ba17d45
--- /dev/null
+++ b/tests/011-array-copy/src/Iface1.java
@@ -0,0 +1,13 @@
+// Copyright 2005 The Android Open Source Project
+
+/**
+ * Test stuff.
+ */
+public interface Iface1 {
+
+ public int iFunc1(int ii);
+
+ public float mFloaty = 5.0f;
+
+ public String mWahoo = new String("wahoo");
+}
diff --git a/tests/011-array-copy/src/Iface2.java b/tests/011-array-copy/src/Iface2.java
new file mode 100644
index 0000000..83fe650
--- /dev/null
+++ b/tests/011-array-copy/src/Iface2.java
@@ -0,0 +1,9 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Another interface.
+ */
+public interface Iface2 {
+
+ public int iFunc2(int ii);
+}
diff --git a/tests/011-array-copy/src/ImplA.java b/tests/011-array-copy/src/ImplA.java
new file mode 100644
index 0000000..9007001
--- /dev/null
+++ b/tests/011-array-copy/src/ImplA.java
@@ -0,0 +1,14 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Blah.
+ */
+public class ImplA implements Iface1, Iface2 {
+
+ public int iFunc1(int ii) {
+ return ii+1;
+ }
+ public int iFunc2(int ii) {
+ return ii+2;
+ }
+}
diff --git a/tests/011-array-copy/src/Main.java b/tests/011-array-copy/src/Main.java
new file mode 100644
index 0000000..9cb8238
--- /dev/null
+++ b/tests/011-array-copy/src/Main.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * System.arraycopy cases
+ */
+public class Main {
+ public static void main(String args[]) {
+ String[] stringArray = new String[8];
+ Object[] objectArray = new Object[8];
+
+ for (int i = 0; i < stringArray.length; i++)
+ stringArray[i] = new String(Integer.toString(i));
+
+ System.out.println("string -> object");
+ System.arraycopy(stringArray, 0, objectArray, 0, stringArray.length);
+ System.out.println("object -> string");
+ System.arraycopy(objectArray, 0, stringArray, 0, stringArray.length);
+ System.out.println("object -> string (modified)");
+ objectArray[4] = new ImplA();
+ try {
+ System.arraycopy(objectArray, 0, stringArray, 0,stringArray.length);
+ }
+ catch (ArrayStoreException ase) {
+ System.out.println("caught ArrayStoreException (expected)");
+ }
+ }
+}
diff --git a/tests/012-math/expected.txt b/tests/012-math/expected.txt
new file mode 100644
index 0000000..af9708e
--- /dev/null
+++ b/tests/012-math/expected.txt
@@ -0,0 +1,32 @@
+res:10
+res:-4
+res:2
+res:-2
+res:21
+res:0
+res:3
+res:4
+res:384
+res:0
+res:0
+a:10
+a:3
+a:2
+a:-3
+a:-21
+a:-3
+a:-3
+a:-6
+a:-768
+a:-6
+a:33554431
+fres:10.0
+fres:-4.0
+fres:21.0
+fres:0.42857142857142855
+fres:3.0
+f:10.0
+f:3.0
+f:21.0
+f:3.0
+f:3.0
diff --git a/tests/012-math/info.txt b/tests/012-math/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/012-math/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/012-math/src/Main.java b/tests/012-math/src/Main.java
new file mode 100644
index 0000000..87ea40b
--- /dev/null
+++ b/tests/012-math/src/Main.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * test simple math opers
+ */
+public class Main {
+ public static void main(String args[]) {
+ int pad0, pad1, pad2, pad3, pad4, pad5, pad6, pad7;
+ int pad8, pad9, pad10, pad11, pad12, pad13, pad14, pad15;
+ int a, b, res;
+ //long a, b, res;
+
+ a = 3;
+ b = 7;
+
+ res = a + b;
+ System.out.println("res:" +res);
+ res = a - b;
+ System.out.println("res:" +res);
+ res = 5 - a;
+ System.out.println("res:" +res);
+ res = a - 5;
+ System.out.println("res:" +res);
+ res = a * b;
+ System.out.println("res:" +res);
+ res = a / b;
+ System.out.println("res:" +res);
+ res = a % b;
+ System.out.println("res:" +res);
+ res = a ^ b;
+ System.out.println("res:" +res);
+ res = a << b;
+ System.out.println("res:" +res);
+ res = a >> b;
+ System.out.println("res:" +res);
+ res = a >>> b;
+ System.out.println("res:" +res);
+
+ a += b;
+ System.out.println("a:" +a);
+ a -= b;
+ System.out.println("a:" +a);
+ a = 5 - a;
+ System.out.println("a:" +a);
+ a -= 5;
+ System.out.println("a:" +a);
+ a *= b;
+ System.out.println("a:" +a);
+ a /= b;
+ System.out.println("a:" +a);
+ a %= b;
+ System.out.println("a:" +a);
+ a ^= b;
+ System.out.println("a:" +a);
+ a <<= b;
+ System.out.println("a:" +a);
+ a >>= b;
+ System.out.println("a:" +a);
+ a >>>= b;
+ System.out.println("a:" +a);
+
+ double f, g, fres;
+
+ f = 3.0f;
+ g = 7.0f;
+
+ fres = f + g;
+ System.out.println("fres:" +fres);
+ fres = f - g;
+ System.out.println("fres:" +fres);
+ fres = f * g;
+ System.out.println("fres:" +fres);
+ fres = f / g;
+ System.out.println("fres:" +fres);
+ fres = f % g;
+ System.out.println("fres:" +fres);
+ f += g;
+ System.out.println("f:" +f);
+ f -= g;
+ System.out.println("f:" +f);
+ f *= g;
+ System.out.println("f:" +f);
+ f /= g;
+ System.out.println("f:" +f);
+ f %= g;
+ System.out.println("f:" +f);
+ }
+}
diff --git a/tests/013-math2/expected.txt b/tests/013-math2/expected.txt
new file mode 100644
index 0000000..d36c468
--- /dev/null
+++ b/tests/013-math2/expected.txt
@@ -0,0 +1 @@
+a:32003
diff --git a/tests/013-math2/info.txt b/tests/013-math2/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/013-math2/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/013-math2/src/Main.java b/tests/013-math2/src/Main.java
new file mode 100644
index 0000000..819571d
--- /dev/null
+++ b/tests/013-math2/src/Main.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * test add by a 16-bit constant
+ */
+public class Main {
+ public static void main(String args[]) {
+ int a, b, res;
+
+ a = 3;
+ b = 7;
+
+ // a 16-bit constant
+ a += 32000;
+ System.out.println("a:" +a);
+ }
+}
diff --git a/tests/014-math3/expected.txt b/tests/014-math3/expected.txt
new file mode 100644
index 0000000..bda3dc7
--- /dev/null
+++ b/tests/014-math3/expected.txt
@@ -0,0 +1 @@
+testMath3 success
diff --git a/tests/014-math3/info.txt b/tests/014-math3/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/014-math3/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/014-math3/src/Main.java b/tests/014-math3/src/Main.java
new file mode 100644
index 0000000..f55b17a
--- /dev/null
+++ b/tests/014-math3/src/Main.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Test math exceptions
+ */
+public class Main {
+ public static void main(String args[]) {
+ int expectedThrows = 2;
+ int i;
+ long j;
+ float f = 0.0f;
+ double d = 0.0;
+
+ try { i = 10 / 0; }
+ catch (ArithmeticException ae) {
+ expectedThrows--;
+ }
+
+ try { j = 10L / 0L; }
+ catch (ArithmeticException ae) {
+ expectedThrows--;
+ }
+
+ /*
+ * Floating point divide by zero doesn't throw an exception -- the
+ * result is just NaN.
+ */
+ try { f = 10.0f / f; }
+ catch (ArithmeticException ae) {
+ expectedThrows--;
+ }
+
+ try { d = 10.0 / d; }
+ catch (ArithmeticException ae) {
+ expectedThrows--;
+ }
+
+ if (expectedThrows != 0)
+ System.out.println("HEY: expected throws is " + expectedThrows);
+ else
+ System.out.println("testMath3 success");
+ }
+}
diff --git a/tests/015-switch/expected.txt b/tests/015-switch/expected.txt
new file mode 100644
index 0000000..ca3b518
--- /dev/null
+++ b/tests/015-switch/expected.txt
@@ -0,0 +1,10 @@
+CORRECT (one)
+CORRECT (not found)
+CORRECT (large)
+CORRECT (large2)
+CORRECT (large3)
+CORRECT (not found)
+CORRECT (not found)
+CORRECT (default only)
+CORRECT big sparse / first
+CORRECT big sparse / last
diff --git a/tests/015-switch/info.txt b/tests/015-switch/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/015-switch/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/015-switch/src/Main.java b/tests/015-switch/src/Main.java
new file mode 100644
index 0000000..7198e2b
--- /dev/null
+++ b/tests/015-switch/src/Main.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Test switch() blocks
+ */
+public class Main {
+ public static void main(String args[]) {
+ int a = 1;
+
+ switch (a) {
+ case -1: System.out.print("neg one\n"); break;
+ case 0: System.out.print("zero\n"); break;
+ case 1: System.out.print("CORRECT (one)\n"); break;
+ case 2: System.out.print("two\n"); break;
+ case 3: System.out.print("three\n"); break;
+ case 4: System.out.print("four\n"); break;
+ default: System.out.print("???\n"); break;
+ }
+ switch (a) {
+ case 3: System.out.print("three\n"); break;
+ case 4: System.out.print("four\n"); break;
+ default: System.out.print("CORRECT (not found)\n"); break;
+ }
+
+ a = 0x12345678;
+
+ switch (a) {
+ case 0x12345678: System.out.print("CORRECT (large)\n"); break;
+ case 0x12345679: System.out.print("large+1\n"); break;
+ default: System.out.print("nuts\n"); break;
+ }
+ switch (a) {
+ case 0x12345678: System.out.print("CORRECT (large2)\n"); break;
+ case 0x12345700: System.out.print("large+many\n"); break;
+ default: System.out.print("nuts\n"); break;
+ }
+ switch (a) {
+ case 57: System.out.print("fifty-seven!\n"); break;
+ case -6: System.out.print("neg six!\n"); break;
+ case 0x12345678: System.out.print("CORRECT (large3)\n"); break;
+ case 22: System.out.print("twenty-two!\n"); break;
+ case 3: System.out.print("three!\n"); break;
+ default: System.out.print("huh?\n"); break;
+ }
+ switch (a) {
+ case -6: System.out.print("neg six!\n"); break;
+ case 3: System.out.print("three!\n"); break;
+ default: System.out.print("CORRECT (not found)\n"); break;
+ }
+
+ a = -5;
+ switch (a) {
+ case 12: System.out.print("twelve\n"); break;
+ case -5: System.out.print("CORRECT (not found)\n"); break;
+ case 0: System.out.print("zero\n"); break;
+ default: System.out.print("wah?\n"); break;
+ }
+
+ switch (a) {
+ default: System.out.print("CORRECT (default only)\n"); break;
+ }
+
+ a = -10;
+ switch (a) {
+ case -10: System.out.print("CORRECT big sparse / first\n"); break;
+ case -5: System.out.print("neg five\n"); break;
+ case 0: System.out.print("zero\n"); break;
+ case 5: System.out.print("five\n"); break;
+ case 10: System.out.print("ten\n"); break;
+ case 15: System.out.print("fifteen\n"); break;
+ case 20: System.out.print("twenty\n"); break;
+ case 50: System.out.print("fifty\n"); break;
+ case 100: System.out.print("hundred\n"); break;
+ default: System.out.print("blah!\n"); break;
+ }
+
+ a = 100;
+ switch (a) {
+ case -10: System.out.print("neg ten\n"); break;
+ case -5: System.out.print("neg five\n"); break;
+ case 0: System.out.print("zero\n"); break;
+ case 5: System.out.print("five\n"); break;
+ case 10: System.out.print("ten\n"); break;
+ case 15: System.out.print("fifteen\n"); break;
+ case 20: System.out.print("twenty\n"); break;
+ case 50: System.out.print("fifty\n"); break;
+ case 100: System.out.print("CORRECT big sparse / last\n"); break;
+ default: System.out.print("blah!\n"); break;
+ }
+ }
+}
diff --git a/tests/016-intern/expected.txt b/tests/016-intern/expected.txt
new file mode 100644
index 0000000..7d91963
--- /dev/null
+++ b/tests/016-intern/expected.txt
@@ -0,0 +1 @@
+good! foobar
diff --git a/tests/016-intern/info.txt b/tests/016-intern/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/016-intern/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/016-intern/src/Main.java b/tests/016-intern/src/Main.java
new file mode 100644
index 0000000..4306863
--- /dev/null
+++ b/tests/016-intern/src/Main.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Interned strings
+ */
+public class Main {
+ public static void main(String args[]) {
+ String a, b;
+ String foo = "foo";
+ String bar = "bar";
+
+ a = foo.concat(bar).intern();
+ b = foo.concat(bar).intern();
+ if (a == b && foo != bar) {
+ System.out.println("good! " + a);
+ } else {
+ System.out.println("bad!");
+ }
+ }
+}
diff --git a/tests/017-float/expected.txt b/tests/017-float/expected.txt
new file mode 100644
index 0000000..2062f9e
--- /dev/null
+++ b/tests/017-float/expected.txt
@@ -0,0 +1,3 @@
+base values: d=3.1415926535 f=3.1415927
+base values: d=3.1415926535 f=3.1415927
+base values: f=3.1415927 d=3.1415926535
diff --git a/tests/017-float/info.txt b/tests/017-float/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/017-float/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/017-float/src/Main.java b/tests/017-float/src/Main.java
new file mode 100644
index 0000000..a5dbe1e
--- /dev/null
+++ b/tests/017-float/src/Main.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * I dont know what this test does.
+ */
+public class Main {
+ public static void main(String args[]) {
+ float f = 3.1415926535f;
+ double d = 3.1415926535;
+ //float fd = (float) d;
+ //Float off = new Float(f);
+ //Float ofd = new Float(d);
+ System.out.println("base values: d=" + d + " f=" + f);
+ System.out.println("base values: d=" + d + " f=" + f);
+ System.out.println("base values: f=" + f + " d=" + d);
+ //System.out.println("object values: off="
+ // + off.floatValue() + " ofd=" + ofd.floatValue());
+ }
+}
diff --git a/tests/018-stack-overflow/expected.txt b/tests/018-stack-overflow/expected.txt
new file mode 100644
index 0000000..7797816
--- /dev/null
+++ b/tests/018-stack-overflow/expected.txt
@@ -0,0 +1,2 @@
+caught SOE
+SOE test done
diff --git a/tests/018-stack-overflow/info.txt b/tests/018-stack-overflow/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/018-stack-overflow/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/018-stack-overflow/src/Main.java b/tests/018-stack-overflow/src/Main.java
new file mode 100644
index 0000000..f79c269
--- /dev/null
+++ b/tests/018-stack-overflow/src/Main.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * generate a stack overflow condition and catch it
+ */
+public class Main {
+ public static void main(String args[]) {
+ try {
+ stackOverflowTestSub(0.0, 0.0, 0.0);
+ }
+ catch (StackOverflowError soe) {
+ System.out.println("caught SOE");
+ }
+ System.out.println("SOE test done");
+ }
+
+ private static void stackOverflowTestSub(double pad1, double pad2,
+ double pad3) {
+ stackOverflowTestSub(pad1, pad2, pad3);
+ }
+}
diff --git a/tests/019-wrong-array-type/expected.txt b/tests/019-wrong-array-type/expected.txt
new file mode 100644
index 0000000..c0ed716
--- /dev/null
+++ b/tests/019-wrong-array-type/expected.txt
@@ -0,0 +1 @@
+Got correct array store exception
diff --git a/tests/019-wrong-array-type/info.txt b/tests/019-wrong-array-type/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/019-wrong-array-type/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/019-wrong-array-type/src/Main.java b/tests/019-wrong-array-type/src/Main.java
new file mode 100644
index 0000000..c424ae9
--- /dev/null
+++ b/tests/019-wrong-array-type/src/Main.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Stuff the wrong type object into an array.
+ */
+public class Main {
+ public static void main(String args[]) {
+ String[] strArray = new String[1];
+
+ Object[] objArray = strArray;
+
+ try {
+ objArray[0] = new Integer(1);
+ System.out.println("Array store succeeded?!");
+ } catch (ArrayStoreException ase) {
+ System.out.println("Got correct array store exception");
+ }
+ }
+}
diff --git a/tests/020-string/expected.txt b/tests/020-string/expected.txt
new file mode 100644
index 0000000..081fea3
--- /dev/null
+++ b/tests/020-string/expected.txt
@@ -0,0 +1,7 @@
+testStr is 'This is a very nice string'
+This is a very nice string
+Compare result is 32
+Compare unicode: -65302
+Got expected exception
+subStr is 'uick brown fox jumps over the lazy '
+Indexes are: 0:-1:0:43:33:-1:18:13:13:-1:18:18:-1:13:-1:-1:-1
diff --git a/tests/020-string/info.txt b/tests/020-string/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/020-string/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/020-string/src/Main.java b/tests/020-string/src/Main.java
new file mode 100644
index 0000000..bb8ce1f
--- /dev/null
+++ b/tests/020-string/src/Main.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Simple string test.
+ */
+public class Main {
+ public static void main(String args[]) {
+ basicTest();
+ indexTest();
+ }
+
+ public static void basicTest() {
+ String baseStr = "*** This is a very nice string!!!";
+ String testStr;
+ int i;
+
+ testStr = baseStr.substring(4, baseStr.length() - 3);
+ System.out.println("testStr is '" + testStr + "'");
+
+ /* sloppy for loop */
+ for (i = 0; i < testStr.length(); i++)
+ System.out.print(testStr.charAt(i));
+ System.out.print("\n");
+
+ String testStr2 = "This is a very nice strinG";
+ if (testStr.length() != testStr2.length())
+ System.out.println("WARNING: stringTest length mismatch");
+
+ System.out.println("Compare result is " + testStr.compareTo(testStr2));
+
+ // expected: -65302
+ String s1 = "\u0c6d\u0cb6\u0d00\u0000\u0080\u0080\u0080\u0000\u0002\u0002\u0002\u0000\u00e9\u00e9\u00e9";
+ String s2 = "\u0c6d\u0cb6\u0d00\u0000\u0080\u0080\u0080\u0000\u0002\u0002\u0002\u0000\uffff\uffff\uffff\u00e9\u00e9\u00e9";
+ System.out.println("Compare unicode: " + s1.compareTo(s2));
+
+ try {
+ testStr.charAt(500);
+ System.out.println("GLITCH: expected exception");
+ } catch (StringIndexOutOfBoundsException sioobe) {
+ System.out.println("Got expected exception");
+ }
+ }
+
+ public static void indexTest() {
+ String baseStr = "The quick brown fox jumps over the lazy dog!";
+ String subStr;
+
+ subStr = baseStr.substring(5, baseStr.length() - 4);
+ System.out.println("subStr is '" + subStr + "'");
+
+ System.out.println("Indexes are: " +
+ baseStr.indexOf('T') + ":" +
+ subStr.indexOf('T') + ":" +
+ subStr.indexOf('u') + ":" +
+ baseStr.indexOf('!') + ":" +
+ subStr.indexOf('y') + ":" +
+ subStr.indexOf('d') + ":" +
+ baseStr.indexOf('x') + ":" +
+ subStr.indexOf('x', 0) + ":" +
+ subStr.indexOf('x', -1) + ":" +
+ subStr.indexOf('x', 200) + ":" +
+ baseStr.indexOf('x', 17) + ":" +
+ baseStr.indexOf('x', 18) + ":" +
+ baseStr.indexOf('x', 19) + ":" +
+ subStr.indexOf('x', 13) + ":" +
+ subStr.indexOf('x', 14) + ":" +
+ subStr.indexOf('&') + ":" +
+ baseStr.indexOf(0x12341234));
+ }
+}
diff --git a/tests/021-string2/expected.txt b/tests/021-string2/expected.txt
new file mode 100644
index 0000000..bd7f049
--- /dev/null
+++ b/tests/021-string2/expected.txt
@@ -0,0 +1 @@
+Got expected npe
diff --git a/tests/021-string2/info.txt b/tests/021-string2/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/021-string2/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/021-string2/src/Main.java b/tests/021-string2/src/Main.java
new file mode 100644
index 0000000..87e4baf
--- /dev/null
+++ b/tests/021-string2/src/Main.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import junit.framework.Assert;
+
+/**
+ * more string tests
+ */
+public class Main {
+ public static void main(String args[]) {
+ String test = "0123456789";
+ String test1 = new String("0123456789"); // different object
+ String test2 = new String("0123456780"); // different value
+ String offset = new String("xxx0123456789yyy");
+ String sub = offset.substring(3, 13);
+ Object blah = new Object();
+
+ Assert.assertTrue(test.equals(test));
+ Assert.assertTrue(test.equals(test1));
+ Assert.assertFalse(test.equals(test2));
+
+ Assert.assertEquals(test.compareTo(test1), 0);
+ Assert.assertTrue(test1.compareTo(test2) > 0);
+ Assert.assertTrue(test2.compareTo(test1) < 0);
+
+ /* compare string with a nonzero offset, in left/right side */
+ Assert.assertEquals(test.compareTo(sub), 0);
+ Assert.assertEquals(sub.compareTo(test), 0);
+ Assert.assertTrue(test.equals(sub));
+ Assert.assertTrue(sub.equals(test));
+ /* same base, one is a substring */
+ Assert.assertFalse(offset.equals(sub));
+ Assert.assertFalse(sub.equals(offset));
+ /* wrong class */
+ Assert.assertFalse(test.equals(blah));
+
+ /* null ptr - throw */
+ try {
+ test.compareTo(null);
+ Assert.fail("didn't get expected npe");
+ } catch (NullPointerException npe) {
+ System.out.println("Got expected npe");
+ }
+ /* null ptr - ok */
+ Assert.assertFalse(test.equals(null));
+
+ test = test.substring(1);
+ Assert.assertTrue(test.equals("123456789"));
+ Assert.assertFalse(test.equals(test1));
+
+ test = test.substring(1);
+ Assert.assertTrue(test.equals("23456789"));
+
+ test = test.substring(1);
+ Assert.assertTrue(test.equals("3456789"));
+
+ test = test.substring(1);
+ Assert.assertTrue(test.equals("456789"));
+
+ test = test.substring(3,5);
+ Assert.assertTrue(test.equals("78"));
+
+ test = "this/is/a/path";
+ String[] strings = test.split("/");
+ Assert.assertEquals(4, strings.length);
+
+ Assert.assertEquals("this is a path", test.replaceAll("/", " "));
+ Assert.assertEquals("this is a path", test.replace("/", " "));
+ }
+}
diff --git a/tests/021-string2/src/junit/framework/Assert.java b/tests/021-string2/src/junit/framework/Assert.java
new file mode 100644
index 0000000..364e646
--- /dev/null
+++ b/tests/021-string2/src/junit/framework/Assert.java
@@ -0,0 +1,291 @@
+package junit.framework;
+
+/**
+ * A set of assert methods. Messages are only displayed when an assert fails.
+ */
+
+public class Assert {
+ /**
+ * Protect constructor since it is a static only class
+ */
+ protected Assert() {
+ }
+
+ /**
+ * Asserts that a condition is true. If it isn't it throws
+ * an AssertionFailedError with the given message.
+ */
+ static public void assertTrue(String message, boolean condition) {
+ if (!condition)
+ fail(message);
+ }
+ /**
+ * Asserts that a condition is true. If it isn't it throws
+ * an AssertionFailedError.
+ */
+ static public void assertTrue(boolean condition) {
+ assertTrue(null, condition);
+ }
+ /**
+ * Asserts that a condition is false. If it isn't it throws
+ * an AssertionFailedError with the given message.
+ */
+ static public void assertFalse(String message, boolean condition) {
+ assertTrue(message, !condition);
+ }
+ /**
+ * Asserts that a condition is false. If it isn't it throws
+ * an AssertionFailedError.
+ */
+ static public void assertFalse(boolean condition) {
+ assertFalse(null, condition);
+ }
+ /**
+ * Fails a test with the given message.
+ */
+ static public void fail(String message) {
+ throw new AssertionFailedError(message);
+ }
+ /**
+ * Fails a test with no message.
+ */
+ static public void fail() {
+ fail(null);
+ }
+ /**
+ * Asserts that two objects are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, Object expected, Object actual) {
+ if (expected == null && actual == null)
+ return;
+ if (expected != null && expected.equals(actual))
+ return;
+ failNotEquals(message, expected, actual);
+ }
+ /**
+ * Asserts that two objects are equal. If they are not
+ * an AssertionFailedError is thrown.
+ */
+ static public void assertEquals(Object expected, Object actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two Strings are equal.
+ */
+ static public void assertEquals(String message, String expected, String actual) {
+ if (expected == null && actual == null)
+ return;
+ if (expected != null && expected.equals(actual))
+ return;
+ throw new ComparisonFailure(message, expected, actual);
+ }
+ /**
+ * Asserts that two Strings are equal.
+ */
+ static public void assertEquals(String expected, String actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two doubles are equal concerning a delta. If they are not
+ * an AssertionFailedError is thrown with the given message. If the expected
+ * value is infinity then the delta value is ignored.
+ */
+ static public void assertEquals(String message, double expected, double actual, double delta) {
+ // handle infinity specially since subtracting to infinite values gives NaN and the
+ // the following test fails
+ if (Double.isInfinite(expected)) {
+ if (!(expected == actual))
+ failNotEquals(message, new Double(expected), new Double(actual));
+ } else if (!(Math.abs(expected-actual) <= delta)) // Because comparison with NaN always returns false
+ failNotEquals(message, new Double(expected), new Double(actual));
+ }
+ /**
+ * Asserts that two doubles are equal concerning a delta. If the expected
+ * value is infinity then the delta value is ignored.
+ */
+ static public void assertEquals(double expected, double actual, double delta) {
+ assertEquals(null, expected, actual, delta);
+ }
+ /**
+ * Asserts that two floats are equal concerning a delta. If they are not
+ * an AssertionFailedError is thrown with the given message. If the expected
+ * value is infinity then the delta value is ignored.
+ */
+ static public void assertEquals(String message, float expected, float actual, float delta) {
+ // handle infinity specially since subtracting to infinite values gives NaN and the
+ // the following test fails
+ if (Float.isInfinite(expected)) {
+ if (!(expected == actual))
+ failNotEquals(message, new Float(expected), new Float(actual));
+ } else if (!(Math.abs(expected-actual) <= delta))
+ failNotEquals(message, new Float(expected), new Float(actual));
+ }
+ /**
+ * Asserts that two floats are equal concerning a delta. If the expected
+ * value is infinity then the delta value is ignored.
+ */
+ static public void assertEquals(float expected, float actual, float delta) {
+ assertEquals(null, expected, actual, delta);
+ }
+ /**
+ * Asserts that two longs are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, long expected, long actual) {
+ assertEquals(message, new Long(expected), new Long(actual));
+ }
+ /**
+ * Asserts that two longs are equal.
+ */
+ static public void assertEquals(long expected, long actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two booleans are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, boolean expected, boolean actual) {
+ assertEquals(message, new Boolean(expected), new Boolean(actual));
+ }
+ /**
+ * Asserts that two booleans are equal.
+ */
+ static public void assertEquals(boolean expected, boolean actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two bytes are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, byte expected, byte actual) {
+ assertEquals(message, new Byte(expected), new Byte(actual));
+ }
+ /**
+ * Asserts that two bytes are equal.
+ */
+ static public void assertEquals(byte expected, byte actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two chars are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, char expected, char actual) {
+ assertEquals(message, new Character(expected), new Character(actual));
+ }
+ /**
+ * Asserts that two chars are equal.
+ */
+ static public void assertEquals(char expected, char actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two shorts are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, short expected, short actual) {
+ assertEquals(message, new Short(expected), new Short(actual));
+ }
+ /**
+ * Asserts that two shorts are equal.
+ */
+ static public void assertEquals(short expected, short actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two ints are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, int expected, int actual) {
+ assertEquals(message, new Integer(expected), new Integer(actual));
+ }
+ /**
+ * Asserts that two ints are equal.
+ */
+ static public void assertEquals(int expected, int actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that an object isn't null.
+ */
+ static public void assertNotNull(Object object) {
+ assertNotNull(null, object);
+ }
+ /**
+ * Asserts that an object isn't null. If it is
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertNotNull(String message, Object object) {
+ assertTrue(message, object != null);
+ }
+ /**
+ * Asserts that an object is null.
+ */
+ static public void assertNull(Object object) {
+ assertNull(null, object);
+ }
+ /**
+ * Asserts that an object is null. If it is not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertNull(String message, Object object) {
+ assertTrue(message, object == null);
+ }
+ /**
+ * Asserts that two objects refer to the same object. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertSame(String message, Object expected, Object actual) {
+ if (expected == actual)
+ return;
+ failNotSame(message, expected, actual);
+ }
+ /**
+ * Asserts that two objects refer to the same object. If they are not
+ * the same an AssertionFailedError is thrown.
+ */
+ static public void assertSame(Object expected, Object actual) {
+ assertSame(null, expected, actual);
+ }
+ /**
+ * Asserts that two objects refer to the same object. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertNotSame(String message, Object expected, Object actual) {
+ if (expected == actual)
+ failSame(message);
+ }
+ /**
+ * Asserts that two objects refer to the same object. If they are not
+ * the same an AssertionFailedError is thrown.
+ */
+ static public void assertNotSame(Object expected, Object actual) {
+ assertNotSame(null, expected, actual);
+ }
+
+ static private void failSame(String message) {
+ String formatted= "";
+ if (message != null)
+ formatted= message+" ";
+ fail(formatted+"expected not same");
+ }
+
+ static private void failNotSame(String message, Object expected, Object actual) {
+ String formatted= "";
+ if (message != null)
+ formatted= message+" ";
+ fail(formatted+"expected same:<"+expected+"> was not:<"+actual+">");
+ }
+
+ static private void failNotEquals(String message, Object expected, Object actual) {
+ fail(format(message, expected, actual));
+ }
+
+ static String format(String message, Object expected, Object actual) {
+ String formatted= "";
+ if (message != null)
+ formatted= message+" ";
+ return formatted+"expected:<"+expected+"> but was:<"+actual+">";
+ }
+}
diff --git a/tests/021-string2/src/junit/framework/AssertionFailedError.java b/tests/021-string2/src/junit/framework/AssertionFailedError.java
new file mode 100644
index 0000000..e9cb3a3
--- /dev/null
+++ b/tests/021-string2/src/junit/framework/AssertionFailedError.java
@@ -0,0 +1,13 @@
+package junit.framework;
+
+/**
+ * Thrown when an assertion failed.
+ */
+public class AssertionFailedError extends Error {
+
+ public AssertionFailedError () {
+ }
+ public AssertionFailedError (String message) {
+ super (message);
+ }
+}
diff --git a/tests/021-string2/src/junit/framework/ComparisonFailure.java b/tests/021-string2/src/junit/framework/ComparisonFailure.java
new file mode 100644
index 0000000..0cb2cee
--- /dev/null
+++ b/tests/021-string2/src/junit/framework/ComparisonFailure.java
@@ -0,0 +1,68 @@
+package junit.framework;
+
+/**
+ * Thrown when an assert equals for Strings failed.
+ *
+ * Inspired by a patch from Alex Chaffee mailto:alex@purpletech.com
+ */
+public class ComparisonFailure extends AssertionFailedError {
+ private String fExpected;
+ private String fActual;
+
+ /**
+ * Constructs a comparison failure.
+ * @param message the identifying message or null
+ * @param expected the expected string value
+ * @param actual the actual string value
+ */
+ public ComparisonFailure (String message, String expected, String actual) {
+ super (message);
+ fExpected= expected;
+ fActual= actual;
+ }
+
+ /**
+ * Returns "..." in place of common prefix and "..." in
+ * place of common suffix between expected and actual.
+ *
+ * @see java.lang.Throwable#getMessage()
+ */
+ public String getMessage() {
+ if (fExpected == null || fActual == null)
+ return Assert.format(super.getMessage(), fExpected, fActual);
+
+ int end= Math.min(fExpected.length(), fActual.length());
+
+ int i= 0;
+ for(; i < end; i++) {
+ if (fExpected.charAt(i) != fActual.charAt(i))
+ break;
+ }
+ int j= fExpected.length()-1;
+ int k= fActual.length()-1;
+ for (; k >= i && j >= i; k--,j--) {
+ if (fExpected.charAt(j) != fActual.charAt(k))
+ break;
+ }
+ String actual, expected;
+
+ // equal strings
+ if (j < i && k < i) {
+ expected= fExpected;
+ actual= fActual;
+ } else {
+ expected= fExpected.substring(i, j+1);
+ actual= fActual.substring(i, k+1);
+ if (i <= end && i > 0) {
+ expected= "..."+expected;
+ actual= "..."+actual;
+ }
+
+ if (j < fExpected.length()-1)
+ expected= expected+"...";
+ if (k < fActual.length()-1)
+ actual= actual+"...";
+ }
+ return Assert.format(super.getMessage(), expected, actual);
+ }
+}
diff --git a/tests/022-interface/expected.txt b/tests/022-interface/expected.txt
new file mode 100644
index 0000000..1212663
--- /dev/null
+++ b/tests/022-interface/expected.txt
@@ -0,0 +1,2 @@
+ImplBSub intf: 205
+ImplA: 7
diff --git a/tests/022-interface/info.txt b/tests/022-interface/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/022-interface/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/022-interface/src/Iface1.java b/tests/022-interface/src/Iface1.java
new file mode 100644
index 0000000..ba17d45
--- /dev/null
+++ b/tests/022-interface/src/Iface1.java
@@ -0,0 +1,13 @@
+// Copyright 2005 The Android Open Source Project
+
+/**
+ * Test stuff.
+ */
+public interface Iface1 {
+
+ public int iFunc1(int ii);
+
+ public float mFloaty = 5.0f;
+
+ public String mWahoo = new String("wahoo");
+}
diff --git a/tests/022-interface/src/Iface2.java b/tests/022-interface/src/Iface2.java
new file mode 100644
index 0000000..83fe650
--- /dev/null
+++ b/tests/022-interface/src/Iface2.java
@@ -0,0 +1,9 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Another interface.
+ */
+public interface Iface2 {
+
+ public int iFunc2(int ii);
+}
diff --git a/tests/022-interface/src/Iface2Sub1.java b/tests/022-interface/src/Iface2Sub1.java
new file mode 100644
index 0000000..db3e905
--- /dev/null
+++ b/tests/022-interface/src/Iface2Sub1.java
@@ -0,0 +1,9 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Another interface.
+ */
+public interface Iface2Sub1 extends Iface2, Cloneable {
+
+ //public int iFunc2(int ii);
+}
diff --git a/tests/022-interface/src/ImplA.java b/tests/022-interface/src/ImplA.java
new file mode 100644
index 0000000..9007001
--- /dev/null
+++ b/tests/022-interface/src/ImplA.java
@@ -0,0 +1,14 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Blah.
+ */
+public class ImplA implements Iface1, Iface2 {
+
+ public int iFunc1(int ii) {
+ return ii+1;
+ }
+ public int iFunc2(int ii) {
+ return ii+2;
+ }
+}
diff --git a/tests/022-interface/src/ImplB.java b/tests/022-interface/src/ImplB.java
new file mode 100644
index 0000000..619fa00
--- /dev/null
+++ b/tests/022-interface/src/ImplB.java
@@ -0,0 +1,16 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Blah.
+ */
+public class ImplB implements Iface1, Iface2 {
+
+ public int iFunc1(int ii) {
+ return ii+10;
+ }
+ public int iFunc2(int ii) {
+ return ii+20;
+ }
+
+ public static String mWhoami = new String("ImplB!");
+}
diff --git a/tests/022-interface/src/ImplBSub.java b/tests/022-interface/src/ImplBSub.java
new file mode 100644
index 0000000..f3a7714
--- /dev/null
+++ b/tests/022-interface/src/ImplBSub.java
@@ -0,0 +1,14 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Interface test.
+ */
+public class ImplBSub extends ImplB implements /*Iface2,*/ Iface2Sub1 {
+
+ public int iFunc1(int ii) {
+ return ii+100;
+ }
+ public int iFunc2(int ii) {
+ return ii+200;
+ }
+}
diff --git a/tests/022-interface/src/Main.java b/tests/022-interface/src/Main.java
new file mode 100644
index 0000000..9151e89
--- /dev/null
+++ b/tests/022-interface/src/Main.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * test calling through an interface
+ */
+public class Main {
+ public static void main(String args[]) {
+ int result = 0;
+ Iface2Sub1 faceObj;
+ ImplA faceObj2;
+
+ faceObj = new ImplBSub();
+
+ result = faceObj.iFunc2(5);
+ System.out.print("ImplBSub intf: ");
+ System.out.println(result);
+
+ faceObj2 = new ImplA();
+ result = faceObj2.iFunc2(5);
+ System.out.print("ImplA: ");
+ System.out.println(result);
+ }
+}
diff --git a/tests/023-many-interfaces/build b/tests/023-many-interfaces/build
new file mode 100644
index 0000000..fc81d62
--- /dev/null
+++ b/tests/023-many-interfaces/build
@@ -0,0 +1,28 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Stop if something fails.
+set -e
+
+# Write out a bunch of interface source files.
+gcc -o iface-gen iface-gen.c
+./iface-gen
+
+mkdir classes
+${JAVAC} -d classes src/*.java
+
+dx --debug --dex --dump-to=classes.lst --output=classes.dex classes
+zip test.jar classes.dex
diff --git a/tests/023-many-interfaces/expected.txt b/tests/023-many-interfaces/expected.txt
new file mode 100644
index 0000000..c6a0c61
--- /dev/null
+++ b/tests/023-many-interfaces/expected.txt
@@ -0,0 +1,9 @@
+testIface001: done
+testIface049: done
+testIface099: done
+testVirt001: done
+testVirt049: done
+testVirt099: done
+testInst001: done
+testInst049: done
+testInst099: done
diff --git a/tests/023-many-interfaces/iface-gen.c b/tests/023-many-interfaces/iface-gen.c
new file mode 100644
index 0000000..1e3284a
--- /dev/null
+++ b/tests/023-many-interfaces/iface-gen.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Generate a big pile of interface classes.
+ */
+#include <stdio.h>
+
+/*
+ * Create N interface files.
+ */
+static int createFiles(int count)
+{
+ FILE* fp;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ char nameBuf[32];
+
+ sprintf(nameBuf, "src/Interface%03d.java", i);
+ fp = fopen(nameBuf, "w");
+ if (fp == NULL) {
+ fprintf(stderr, "ERROR: unable to open %s\n", nameBuf);
+ return -1;
+ }
+
+ fprintf(fp, "interface Interface%03d {\n", i);
+ if ((i & 0x01) != 0)
+ fprintf(fp, " int func%03d();\n", i);
+ fprintf(fp, "}\n");
+ fclose(fp);
+ }
+
+ fp = fopen("func-decl", "w");
+ fprintf(fp, " implements\n");
+ for (i = 0; i < count; i++) {
+ fprintf(fp, " Interface%03d%s\n", i, (i == count-1) ? "" : ",");
+ }
+ fprintf(fp, "\n");
+ for (i = 1; i < count; i += 2) {
+ fprintf(fp, " public int func%03d() { return %d; }\n", i, i);
+ }
+ fclose(fp);
+
+ return 0;
+}
+
+int main()
+{
+ int result;
+
+ result = createFiles(100);
+
+ return (result != 0);
+}
diff --git a/tests/023-many-interfaces/info.txt b/tests/023-many-interfaces/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/023-many-interfaces/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/023-many-interfaces/src/Main.java b/tests/023-many-interfaces/src/Main.java
new file mode 100644
index 0000000..666a41c
--- /dev/null
+++ b/tests/023-many-interfaces/src/Main.java
@@ -0,0 +1,6 @@
+public class Main {
+ static public void main(String[] args) throws Exception {
+ boolean timing = (args.length >= 1) && args[0].equals("--timing");
+ ManyInterfaces.run(timing);
+ }
+}
diff --git a/tests/023-many-interfaces/src/ManyInterfaces.java b/tests/023-many-interfaces/src/ManyInterfaces.java
new file mode 100644
index 0000000..375938a
--- /dev/null
+++ b/tests/023-many-interfaces/src/ManyInterfaces.java
@@ -0,0 +1,413 @@
+// Copyright 2007 The Android Open Source Project
+
+/*
+Initial:
+test001: 2039901us (4079ns per call)
+test049: 3346619us (6693ns per call)
+test099: 4687402us (9374ns per call)
+testInst001: 1327216us (2654ns per use)
+testInst049: 1326995us (2653ns per use)
+testInst099: 1327735us (2655ns per use)
+
+After refactoring cache code: 2871ns per use
+After re-refactoring cache code: 2797ns per use
+
+After de-inlining invoke-interface:
+test001: 2164873us (4329ns per call)
+test049: 3303884us (6607ns per call)
+test099: 4656718us (9313ns per call)
+testInst001: 1401731us (2803ns per use)
+testInst049: 1401120us (2802ns per use)
+testInst099: 1401298us (2802ns per use)
+
+After adding caching for invoke-interface:
+testIface001: 1909330us (3818ns per call)
+testIface049: 1905204us (3810ns per call)
+testIface099: 1899012us (3798ns per call)
+testVirt001: 1825001us (3650ns per call)
+testVirt049: 1826161us (3652ns per call)
+testVirt099: 1823915us (3647ns per call)
+testInst001: 1393963us (2787ns per use)
+testInst049: 1393163us (2786ns per use)
+testInst099: 1390496us (2780ns per use)
+
+After repeating each operation 16 times inside the inner loop:
+testIface001: 1429472us (2726ns per call) * 2382ns
+testIface049: 1427847us (2723ns per call) * 2396ns
+testIface099: 1423707us (2715ns per call) * 2387ns
+testVirt001: 1277790us (2437ns per call) * 2118ns
+testVirt049: 1280276us (2441ns per call) * 2119ns
+testVirt099: 1272640us (2427ns per call) * 2118ns
+testInst001: 844694us (1611ns per use) * 1396ns
+testInst049: 845619us (1612ns per use) * 1395ns
+testInst099: 845526us (1612ns per use) * 1394ns
+('*' is with dx optimizations enabled)
+*/
+
+/**
+ * Semi-generated class with many interfaces.
+ */
+public class ManyInterfaces
+ implements
+ Interface000,
+ Interface001,
+ Interface002,
+ Interface003,
+ Interface004,
+ Interface005,
+ Interface006,
+ Interface007,
+ Interface008,
+ Interface009,
+ Interface010,
+ Interface011,
+ Interface012,
+ Interface013,
+ Interface014,
+ Interface015,
+ Interface016,
+ Interface017,
+ Interface018,
+ Interface019,
+ Interface020,
+ Interface021,
+ Interface022,
+ Interface023,
+ Interface024,
+ Interface025,
+ Interface026,
+ Interface027,
+ Interface028,
+ Interface029,
+ Interface030,
+ Interface031,
+ Interface032,
+ Interface033,
+ Interface034,
+ Interface035,
+ Interface036,
+ Interface037,
+ Interface038,
+ Interface039,
+ Interface040,
+ Interface041,
+ Interface042,
+ Interface043,
+ Interface044,
+ Interface045,
+ Interface046,
+ Interface047,
+ Interface048,
+ Interface049,
+ Interface050,
+ Interface051,
+ Interface052,
+ Interface053,
+ Interface054,
+ Interface055,
+ Interface056,
+ Interface057,
+ Interface058,
+ Interface059,
+ Interface060,
+ Interface061,
+ Interface062,
+ Interface063,
+ Interface064,
+ Interface065,
+ Interface066,
+ Interface067,
+ Interface068,
+ Interface069,
+ Interface070,
+ Interface071,
+ Interface072,
+ Interface073,
+ Interface074,
+ Interface075,
+ Interface076,
+ Interface077,
+ Interface078,
+ Interface079,
+ Interface080,
+ Interface081,
+ Interface082,
+ Interface083,
+ Interface084,
+ Interface085,
+ Interface086,
+ Interface087,
+ Interface088,
+ Interface089,
+ Interface090,
+ Interface091,
+ Interface092,
+ Interface093,
+ Interface094,
+ Interface095,
+ Interface096,
+ Interface097,
+ Interface098,
+ Interface099
+{
+ /** whether to report timing information */
+ private static boolean timing = false;
+
+ /**
+ * Report on a section.
+ */
+ private static void report(String label, long start, long end, int iter,
+ int rept) {
+ if (timing) {
+ System.out.println(label + ": " + (end - start) / 1000 + "us"
+ + " (" + (end - start) / (iter*rept) + "ns per call)");
+ } else {
+ System.out.println(label + ": done");
+ }
+ }
+
+ /**
+ * Run tests.
+ *
+ * @param timing whether to print out timing info
+ */
+ public static void run(boolean timing) {
+ ManyInterfaces.timing = timing;
+ ManyInterfaces obj = new ManyInterfaces();
+ Interface001 one;
+ Interface049 forty;
+ Interface099 ninety;
+ long start, end;
+ int iter = 32768;
+ int rept = 16;
+ int i;
+
+ /*
+ * Clear the heap. The various classes involved should already
+ * be loaded and ready as a result of instantiating ManyInterfaces.
+ */
+ System.gc();
+
+ start = System.nanoTime();
+ testIface001(obj, iter);
+ end = System.nanoTime();
+ report("testIface001", start, end, iter, rept);
+
+ start = System.nanoTime();
+ testIface049(obj, iter);
+ end = System.nanoTime();
+ report("testIface049", start, end, iter, rept);
+
+ start = System.nanoTime();
+ testIface099(obj, iter);
+ end = System.nanoTime();
+ report("testIface099", start, end, iter, rept);
+
+ start = System.nanoTime();
+ testVirt001(obj, iter);
+ end = System.nanoTime();
+ report("testVirt001", start, end, iter, rept);
+
+ start = System.nanoTime();
+ testVirt049(obj, iter);
+ end = System.nanoTime();
+ report("testVirt049", start, end, iter, rept);
+
+ start = System.nanoTime();
+ testVirt099(obj, iter);
+ end = System.nanoTime();
+ report("testVirt099", start, end, iter, rept);
+
+ start = System.nanoTime();
+ testInstance001(obj, iter);
+ end = System.nanoTime();
+ report("testInst001", start, end, iter, rept);
+
+ start = System.nanoTime();
+ testInstance049(obj, iter);
+ end = System.nanoTime();
+ report("testInst049", start, end, iter, rept);
+
+ start = System.nanoTime();
+ testInstance099(obj, iter);
+ end = System.nanoTime();
+ report("testInst099", start, end, iter, rept);
+ }
+
+ public int func001() { return 1; }
+ public int func003() { return 3; }
+ public int func005() { return 5; }
+ public int func007() { return 7; }
+ public int func009() { return 9; }
+ public int func011() { return 11; }
+ public int func013() { return 13; }
+ public int func015() { return 15; }
+ public int func017() { return 17; }
+ public int func019() { return 19; }
+ public int func021() { return 21; }
+ public int func023() { return 23; }
+ public int func025() { return 25; }
+ public int func027() { return 27; }
+ public int func029() { return 29; }
+ public int func031() { return 31; }
+ public int func033() { return 33; }
+ public int func035() { return 35; }
+ public int func037() { return 37; }
+ public int func039() { return 39; }
+ public int func041() { return 41; }
+ public int func043() { return 43; }
+ public int func045() { return 45; }
+ public int func047() { return 47; }
+ public int func049() { return 49; }
+ public int func051() { return 51; }
+ public int func053() { return 53; }
+ public int func055() { return 55; }
+ public int func057() { return 57; }
+ public int func059() { return 59; }
+ public int func061() { return 61; }
+ public int func063() { return 63; }
+ public int func065() { return 65; }
+ public int func067() { return 67; }
+ public int func069() { return 69; }
+ public int func071() { return 71; }
+ public int func073() { return 73; }
+ public int func075() { return 75; }
+ public int func077() { return 77; }
+ public int func079() { return 79; }
+ public int func081() { return 81; }
+ public int func083() { return 83; }
+ public int func085() { return 85; }
+ public int func087() { return 87; }
+ public int func089() { return 89; }
+ public int func091() { return 91; }
+ public int func093() { return 93; }
+ public int func095() { return 95; }
+ public int func097() { return 97; }
+ public int func099() { return 99; }
+
+ static void testIface001(Interface001 iface, int count) {
+ while (count-- != 0) {
+ iface.func001(); iface.func001(); iface.func001(); iface.func001();
+ iface.func001(); iface.func001(); iface.func001(); iface.func001();
+ iface.func001(); iface.func001(); iface.func001(); iface.func001();
+ iface.func001(); iface.func001(); iface.func001(); iface.func001();
+ }
+ }
+
+ static void testIface049(Interface049 iface, int count) {
+ while (count-- != 0) {
+ iface.func049(); iface.func049(); iface.func049(); iface.func049();
+ iface.func049(); iface.func049(); iface.func049(); iface.func049();
+ iface.func049(); iface.func049(); iface.func049(); iface.func049();
+ iface.func049(); iface.func049(); iface.func049(); iface.func049();
+ }
+ }
+
+ static void testIface099(Interface099 iface, int count) {
+ while (count-- != 0) {
+ iface.func099(); iface.func099(); iface.func099(); iface.func099();
+ iface.func099(); iface.func099(); iface.func099(); iface.func099();
+ iface.func099(); iface.func099(); iface.func099(); iface.func099();
+ iface.func099(); iface.func099(); iface.func099(); iface.func099();
+ }
+ }
+
+ static void testVirt001(ManyInterfaces obj, int count) {
+ while (count-- != 0) {
+ obj.func001(); obj.func001(); obj.func001(); obj.func001();
+ obj.func001(); obj.func001(); obj.func001(); obj.func001();
+ obj.func001(); obj.func001(); obj.func001(); obj.func001();
+ obj.func001(); obj.func001(); obj.func001(); obj.func001();
+ }
+ }
+
+ static void testVirt049(ManyInterfaces obj, int count) {
+ while (count-- != 0) {
+ obj.func049(); obj.func049(); obj.func049(); obj.func049();
+ obj.func049(); obj.func049(); obj.func049(); obj.func049();
+ obj.func049(); obj.func049(); obj.func049(); obj.func049();
+ obj.func049(); obj.func049(); obj.func049(); obj.func049();
+ }
+ }
+
+ static void testVirt099(ManyInterfaces obj, int count) {
+ while (count-- != 0) {
+ obj.func099(); obj.func099(); obj.func099(); obj.func099();
+ obj.func099(); obj.func099(); obj.func099(); obj.func099();
+ obj.func099(); obj.func099(); obj.func099(); obj.func099();
+ obj.func099(); obj.func099(); obj.func099(); obj.func099();
+ }
+ }
+
+ static void testInstance001(Object obj, int count) {
+ if (!(obj instanceof Interface001))
+ System.err.println("BAD");
+ while (count-- != 0) {
+ boolean is;
+ is = obj instanceof Interface001;
+ is = obj instanceof Interface001;
+ is = obj instanceof Interface001;
+ is = obj instanceof Interface001;
+ is = obj instanceof Interface001;
+ is = obj instanceof Interface001;
+ is = obj instanceof Interface001;
+ is = obj instanceof Interface001;
+ is = obj instanceof Interface001;
+ is = obj instanceof Interface001;
+ is = obj instanceof Interface001;
+ is = obj instanceof Interface001;
+ is = obj instanceof Interface001;
+ is = obj instanceof Interface001;
+ is = obj instanceof Interface001;
+ is = obj instanceof Interface001;
+ }
+ }
+
+ static void testInstance049(Object obj, int count) {
+ if (!(obj instanceof Interface049))
+ System.err.println("BAD");
+ while (count-- != 0) {
+ boolean is;
+ is = obj instanceof Interface049;
+ is = obj instanceof Interface049;
+ is = obj instanceof Interface049;
+ is = obj instanceof Interface049;
+ is = obj instanceof Interface049;
+ is = obj instanceof Interface049;
+ is = obj instanceof Interface049;
+ is = obj instanceof Interface049;
+ is = obj instanceof Interface049;
+ is = obj instanceof Interface049;
+ is = obj instanceof Interface049;
+ is = obj instanceof Interface049;
+ is = obj instanceof Interface049;
+ is = obj instanceof Interface049;
+ is = obj instanceof Interface049;
+ is = obj instanceof Interface049;
+ }
+ }
+
+ static void testInstance099(Object obj, int count) {
+ if (!(obj instanceof Interface099))
+ System.err.println("BAD");
+ while (count-- != 0) {
+ boolean is;
+ is = obj instanceof Interface099;
+ is = obj instanceof Interface099;
+ is = obj instanceof Interface099;
+ is = obj instanceof Interface099;
+ is = obj instanceof Interface099;
+ is = obj instanceof Interface099;
+ is = obj instanceof Interface099;
+ is = obj instanceof Interface099;
+ is = obj instanceof Interface099;
+ is = obj instanceof Interface099;
+ is = obj instanceof Interface099;
+ is = obj instanceof Interface099;
+ is = obj instanceof Interface099;
+ is = obj instanceof Interface099;
+ is = obj instanceof Interface099;
+ is = obj instanceof Interface099;
+ }
+ }
+}
diff --git a/tests/024-illegal-access/expected.txt b/tests/024-illegal-access/expected.txt
new file mode 100644
index 0000000..5f951f4
--- /dev/null
+++ b/tests/024-illegal-access/expected.txt
@@ -0,0 +1,2 @@
+Got expected failure 1
+Got expected failure 2
diff --git a/tests/024-illegal-access/info.txt b/tests/024-illegal-access/info.txt
new file mode 100644
index 0000000..16a284d
--- /dev/null
+++ b/tests/024-illegal-access/info.txt
@@ -0,0 +1,3 @@
+Test that an attempt to access a private field results in a verification
+error. Also try to access a non-public class in a different package with
+"instanceof".
diff --git a/tests/024-illegal-access/src/CheckInstanceof.java b/tests/024-illegal-access/src/CheckInstanceof.java
new file mode 100644
index 0000000..de48cd2
--- /dev/null
+++ b/tests/024-illegal-access/src/CheckInstanceof.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Make sure we're performing access checks on classes used in "instanceof".
+ */
+public class CheckInstanceof {
+ public static void main(Object obj) {
+ if (obj instanceof otherpkg.Package)
+ System.out.println("yes!");
+ else
+ System.out.println("no!");
+ }
+}
diff --git a/tests/024-illegal-access/src/Main.java b/tests/024-illegal-access/src/Main.java
new file mode 100644
index 0000000..bde73e9
--- /dev/null
+++ b/tests/024-illegal-access/src/Main.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ static public void main(String[] args) {
+ try {
+ PublicAccess.main();
+ System.err.println("ERROR: call 1 not expected to succeed");
+ } catch (VerifyError ve) {
+ // dalvik
+ System.out.println("Got expected failure 1");
+ } catch (IllegalAccessError iae) {
+ // reference
+ System.out.println("Got expected failure 1");
+ }
+
+ try {
+ CheckInstanceof.main(new Object());
+ System.err.println("ERROR: call 2 not expected to succeed");
+ } catch (VerifyError ve) {
+ // dalvik
+ System.out.println("Got expected failure 2");
+ } catch (IllegalAccessError iae) {
+ // reference
+ System.out.println("Got expected failure 2");
+ }
+ }
+}
diff --git a/tests/024-illegal-access/src/PublicAccess.java b/tests/024-illegal-access/src/PublicAccess.java
new file mode 100644
index 0000000..fdc0e9e
--- /dev/null
+++ b/tests/024-illegal-access/src/PublicAccess.java
@@ -0,0 +1,11 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Some stuff for access checks.
+ */
+public class PublicAccess {
+ public static void main() {
+ String shouldFail = SemiPrivate.mPrivvy;
+ System.out.println("Got " + shouldFail);
+ }
+}
diff --git a/tests/024-illegal-access/src/SemiPrivate.java b/tests/024-illegal-access/src/SemiPrivate.java
new file mode 100644
index 0000000..17b2ac0
--- /dev/null
+++ b/tests/024-illegal-access/src/SemiPrivate.java
@@ -0,0 +1,8 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Version with package scope access.
+ */
+public class SemiPrivate {
+ /* not private */ static String mPrivvy = "stuff";
+}
diff --git a/tests/024-illegal-access/src/otherpkg/Package.java b/tests/024-illegal-access/src/otherpkg/Package.java
new file mode 100644
index 0000000..bc295b5
--- /dev/null
+++ b/tests/024-illegal-access/src/otherpkg/Package.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package otherpkg;
+
+/*
+ * Package-scope class (public here).
+ */
+public class Package {
+}
diff --git a/tests/024-illegal-access/src2/SemiPrivate.java b/tests/024-illegal-access/src2/SemiPrivate.java
new file mode 100644
index 0000000..cf6f8e6
--- /dev/null
+++ b/tests/024-illegal-access/src2/SemiPrivate.java
@@ -0,0 +1,8 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Version with private access.
+ */
+public class SemiPrivate {
+ private static String mPrivvy = "stuff";
+}
diff --git a/tests/024-illegal-access/src2/otherpkg/Package.java b/tests/024-illegal-access/src2/otherpkg/Package.java
new file mode 100644
index 0000000..54d8341
--- /dev/null
+++ b/tests/024-illegal-access/src2/otherpkg/Package.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package otherpkg;
+
+/*
+ * Package-scope class.
+ */
+class Package {
+}
diff --git a/tests/025-access-controller/expected.txt b/tests/025-access-controller/expected.txt
new file mode 100644
index 0000000..75cfc99
--- /dev/null
+++ b/tests/025-access-controller/expected.txt
@@ -0,0 +1 @@
+AccessControllerTest: got 39
diff --git a/tests/025-access-controller/info.txt b/tests/025-access-controller/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/025-access-controller/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/025-access-controller/src/Main.java b/tests/025-access-controller/src/Main.java
new file mode 100644
index 0000000..84dc057
--- /dev/null
+++ b/tests/025-access-controller/src/Main.java
@@ -0,0 +1,14 @@
+// Copyright 2007 The Android Open Source Project
+
+import java.security.AccessController;
+
+/**
+ * Test java.security.AccessController.
+ */
+public class Main {
+ public static void main(String[] args) {
+ Privvy priv = new Privvy(38);
+ Integer result = AccessController.doPrivileged(priv);
+ System.out.println("AccessControllerTest: got " + result);
+ }
+}
diff --git a/tests/025-access-controller/src/Privvy.java b/tests/025-access-controller/src/Privvy.java
new file mode 100644
index 0000000..07a0678
--- /dev/null
+++ b/tests/025-access-controller/src/Privvy.java
@@ -0,0 +1,18 @@
+// Copyright 2007 The Android Open Source Project
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
+
+class Privvy implements PrivilegedAction<Integer> {
+
+ private Integer mValue;
+
+ public Privvy(int val) {
+ mValue = new Integer(val + 1);
+ }
+
+ public Integer run() {
+ return mValue;
+ }
+}
diff --git a/tests/026-access/expected.txt b/tests/026-access/expected.txt
new file mode 100644
index 0000000..dabfb37
--- /dev/null
+++ b/tests/026-access/expected.txt
@@ -0,0 +1,2 @@
+access test
+Blort.
diff --git a/tests/026-access/info.txt b/tests/026-access/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/026-access/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/026-access/src/Main.java b/tests/026-access/src/Main.java
new file mode 100644
index 0000000..9628259
--- /dev/null
+++ b/tests/026-access/src/Main.java
@@ -0,0 +1,12 @@
+// Copyright 2006 The Android Open Source Project
+
+import otherpackage.PublicAccess;
+
+public class Main {
+ public static void main(String[] args) {
+ System.out.println("access test");
+
+ PublicAccess pa = new PublicAccess();
+ pa.main();
+ }
+}
diff --git a/tests/026-access/src/otherpackage/PublicAccess.java b/tests/026-access/src/otherpackage/PublicAccess.java
new file mode 100644
index 0000000..996fa76
--- /dev/null
+++ b/tests/026-access/src/otherpackage/PublicAccess.java
@@ -0,0 +1,7 @@
+package otherpackage;
+
+public class PublicAccess {
+ static public void main() {
+ System.out.println("Blort.");
+ }
+}
diff --git a/tests/027-arithmetic/expected.txt b/tests/027-arithmetic/expected.txt
new file mode 100644
index 0000000..2dadf10
--- /dev/null
+++ b/tests/027-arithmetic/expected.txt
@@ -0,0 +1,18 @@
+f=1234.5677 --> i=1234
+f=-1234.5677 --> i=-1234
+d=1234.5678 --> i=1234
+d=-1234.5678 --> i=-1234
+d=5.6789567890123E9 --> l=5678956789
+d=-5.6789567890123E9 --> l=-5678956789
+i=7654 --> l=7654
+i=-7654 --> l=-7654
+l=5678956789 --> i=1383989493
+l=-5678956789 --> i=-1383989493
+i=1234 --> f=1234.0
+i=-1234 --> f=-1234.0
+values are 44332211 and bbaa9988
+First l is bbaa998844332211
+Second l is bbaa998844332211
+shiftTest2 l is 1122334455667788
+b=-1, s=-1, c=4095, i=268435455
+b=0xffffffff, s=0xffffffff, c=0xfff, i=0xfffffff
diff --git a/tests/027-arithmetic/info.txt b/tests/027-arithmetic/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/027-arithmetic/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/027-arithmetic/src/Main.java b/tests/027-arithmetic/src/Main.java
new file mode 100644
index 0000000..4d0f74e
--- /dev/null
+++ b/tests/027-arithmetic/src/Main.java
@@ -0,0 +1,141 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Test arithmetic operations.
+ */
+public class Main {
+
+ static void shiftTest1()
+ {
+ final int[] mBytes = {
+ 0x11, 0x22, 0x33, 0x44, 0x88, 0x99, 0xaa, 0xbb
+ };
+ long l;
+ int i1, i2;
+
+ i1 = mBytes[0] | mBytes[1] << 8 | mBytes[2] << 16 | mBytes[3] << 24;
+ i2 = mBytes[4] | mBytes[5] << 8 | mBytes[6] << 16 | mBytes[7] << 24;
+ l = i1 | ((long)i2 << 32);
+
+ System.out.println("values are " + Integer.toHexString(i1)
+ + " and " + Integer.toHexString(i2));
+
+ System.out.println("First l is " + Long.toHexString(l));
+
+ l = (long)mBytes[0]
+ | (long)mBytes[1] << 8
+ | (long)mBytes[2] << 16
+ | (long)mBytes[3] << 24
+ | (long)mBytes[4] << 32
+ | (long)mBytes[5] << 40
+ | (long)mBytes[6] << 48
+ | (long)mBytes[7] << 56;
+
+ System.out.println("Second l is " + Long.toHexString(l));
+ }
+
+ static void shiftTest2()
+ {
+ long a = 0x11;
+ long b = 0x22;
+ long c = 0x33;
+ long d = 0x44;
+ long e = 0x55;
+ long f = 0x66;
+ long g = 0x77;
+ long h = 0x88;
+
+ long result = ((a << 56) | (b << 48) | (c << 40) | (d << 32) |
+ (e << 24) | (f << 16) | (g << 8) | h);
+
+ System.out.println("shiftTest2 l is " + Long.toHexString(result));
+ }
+
+ static void convTest()
+ {
+ float f;
+ double d;
+ int i;
+ long l;
+
+ /* float --> int */
+ f = 1234.5678f;
+ i = (int) f;
+ System.out.println("f=" + f + " --> i=" + i);
+
+ f = -1234.5678f;
+ i = (int) f;
+ System.out.println("f=" + f + " --> i=" + i);
+
+ /* double --> int */
+ d = 1234.5678;
+ i = (int) d;
+ System.out.println("d=" + d + " --> i=" + i);
+
+ d = -1234.5678;
+ i = (int) d;
+ System.out.println("d=" + d + " --> i=" + i);
+
+ /* double --> long */
+ d = 5678956789.0123;
+ l = (long) d;
+ System.out.println("d=" + d + " --> l=" + l);
+
+ d = -5678956789.0123;
+ l = (long) d;
+ System.out.println("d=" + d + " --> l=" + l);
+
+ /* int --> long */
+ i = 7654;
+ l = (long) i;
+ System.out.println("i=" + i + " --> l=" + l);
+
+ i = -7654;
+ l = (long) i;
+ System.out.println("i=" + i + " --> l=" + l);
+
+ /* long --> int (with truncation) */
+ l = 5678956789L;
+ i = (int) l;
+ System.out.println("l=" + l + " --> i=" + i);
+
+ l = -5678956789L;
+ i = (int) l;
+ System.out.println("l=" + l + " --> i=" + i);
+
+ /* int --> float */
+ i = 1234;
+ f = (float) i;
+ System.out.println("i=" + i + " --> f=" + f);
+
+ i = -1234;
+ f = (float) i;
+ System.out.println("i=" + i + " --> f=" + f);
+ }
+
+ static void unsignedShiftTest()
+ {
+ byte b = -4;
+ short s = -4;
+ char c = 0xfffc;
+ int i = -4;
+
+ b >>>= 4;
+ s >>>= 4;
+ c >>>= 4;
+ i >>>= 4;
+
+ System.out.println("b=" + b + ", s=" + s + ", c=" + (int)c + ", i=" +i);
+ System.out.println("b=0x" + Integer.toHexString((int)b)
+ + ", s=0x" + Integer.toHexString((int)s)
+ + ", c=0x" + Integer.toHexString((int)c)
+ + ", i=0x" + Integer.toHexString(i));
+ }
+
+ public static void main(String[] args) {
+ convTest();
+ shiftTest1();
+ shiftTest2();
+ unsignedShiftTest();
+ }
+}
diff --git a/tests/028-array-write/expected.txt b/tests/028-array-write/expected.txt
new file mode 100644
index 0000000..85986b5
--- /dev/null
+++ b/tests/028-array-write/expected.txt
@@ -0,0 +1,3 @@
+Running writeTest...
+Running copyTest...
+Done!
diff --git a/tests/028-array-write/info.txt b/tests/028-array-write/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/028-array-write/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/028-array-write/src/Main.java b/tests/028-array-write/src/Main.java
new file mode 100644
index 0000000..6f36f84
--- /dev/null
+++ b/tests/028-array-write/src/Main.java
@@ -0,0 +1,68 @@
+// Copyright 2007 The Android Open Source Project
+
+/**
+ * Array write speed test.
+ */
+public class Main {
+ /** whether to report times */
+ static boolean timing = false;
+
+ static final int STORAGE_SIZE = 128*1024;
+ static int[] mStorage = new int[STORAGE_SIZE];
+
+ static public void report(long start, long end) {
+ if (! timing) {
+ return;
+ }
+
+ System.out.println("Finished in " + ((end - start) / 1000000.0)
+ + " msec");
+ }
+
+ static void writeArray(int val) {
+ for (int i = STORAGE_SIZE-1; i >= 0; i--)
+ mStorage[i] = val;
+ }
+
+ static void writeTest() {
+ long start, end;
+
+ writeArray(0); // touch all the memory
+
+ System.out.println("Running writeTest...");
+ start = System.nanoTime();
+ for (int i = 1; i < 20; i++)
+ writeArray(i);
+ end = System.nanoTime();
+
+ report(start, end);
+ }
+
+ static void copyTest() {
+ long start, end;
+
+ // touch once
+ System.arraycopy(mStorage, 0, mStorage,
+ STORAGE_SIZE/2, STORAGE_SIZE/2);
+
+ System.out.println("Running copyTest...");
+ start = System.nanoTime();
+ for (int i = 1; i < 35; i++) {
+ System.arraycopy(mStorage, 0, mStorage,
+ STORAGE_SIZE/2, STORAGE_SIZE/2);
+ }
+ end = System.nanoTime();
+
+ report(start, end);
+ }
+
+ public static void main(String[] args) {
+ if ((args.length >= 1) && args[0].equals("--timing")) {
+ timing = true;
+ }
+
+ writeTest();
+ copyTest();
+ System.out.println("Done!");
+ }
+}
diff --git a/tests/029-assert/expected.txt b/tests/029-assert/expected.txt
new file mode 100644
index 0000000..bf0efec
--- /dev/null
+++ b/tests/029-assert/expected.txt
@@ -0,0 +1 @@
+caught expected assert exception
diff --git a/tests/029-assert/info.txt b/tests/029-assert/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/029-assert/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/029-assert/src/Main.java b/tests/029-assert/src/Main.java
new file mode 100644
index 0000000..1e5cc7c
--- /dev/null
+++ b/tests/029-assert/src/Main.java
@@ -0,0 +1,16 @@
+// Copyright 2007 The Android Open Source Project
+
+/**
+ * Test Java language asserts.
+ */
+public class Main {
+ public static void main(String[] args) {
+ assert true;
+ try {
+ assert false;
+ System.out.println("GLITCH: didn't assert (is '-ea' set?)");
+ } catch (AssertionError ae) {
+ System.out.println("caught expected assert exception");
+ }
+ }
+}
diff --git a/tests/030-bad-finalizer/expected.txt b/tests/030-bad-finalizer/expected.txt
new file mode 100644
index 0000000..6e91dcc
--- /dev/null
+++ b/tests/030-bad-finalizer/expected.txt
@@ -0,0 +1,8 @@
+Constructed object.
+Nulled. Requestion gc.
+Finalizer started and spinning...
+Finalizer done spinning.
+Finalizer sleeping forever now.
+Requesting another GC.
+Requesting another GC.
+(segfault)
diff --git a/tests/030-bad-finalizer/info.txt b/tests/030-bad-finalizer/info.txt
new file mode 100644
index 0000000..0f76ad6
--- /dev/null
+++ b/tests/030-bad-finalizer/info.txt
@@ -0,0 +1,3 @@
+The finalizer for this class never finishes. Dalvik is expected to detect
+this situation and abort the VM (so you will likely see a "deadd00d"
+crash in the log output).
diff --git a/tests/030-bad-finalizer/run b/tests/030-bad-finalizer/run
new file mode 100644
index 0000000..8e03cd3
--- /dev/null
+++ b/tests/030-bad-finalizer/run
@@ -0,0 +1,27 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+${RUN} "$@" > original-output.txt
+
+cat original-output.txt | awk '
+/Segmentation fault/ {
+ # ignore the details of the line
+ print "(segfault)"
+ next;
+}
+{
+ print;
+}'
diff --git a/tests/030-bad-finalizer/src/BadFinalizer.java b/tests/030-bad-finalizer/src/BadFinalizer.java
new file mode 100644
index 0000000..3ff422b
--- /dev/null
+++ b/tests/030-bad-finalizer/src/BadFinalizer.java
@@ -0,0 +1,32 @@
+// Copyright 2007 The Android Open Source Project
+
+/**
+ * Class with a bad finalizer.
+ */
+public class BadFinalizer {
+ public static void snooze(int ms) {
+ try {
+ Thread.sleep(ms);
+ } catch (InterruptedException ie) {
+ System.out.println("Snooze: " + ie.getMessage());
+ }
+ }
+
+ protected void finalize() {
+ System.out.println("Finalizer started and spinning...");
+ int j = 0;
+
+ /* spin for a bit */
+ long start, end;
+ start = System.nanoTime();
+ for (int i = 0; i < 1000000; i++)
+ j++;
+ end = System.nanoTime();
+ System.out.println("Finalizer done spinning.");
+
+ System.out.println("Finalizer sleeping forever now.");
+ while (true) {
+ snooze(10000);
+ }
+ }
+}
diff --git a/tests/030-bad-finalizer/src/Main.java b/tests/030-bad-finalizer/src/Main.java
new file mode 100644
index 0000000..c063476
--- /dev/null
+++ b/tests/030-bad-finalizer/src/Main.java
@@ -0,0 +1,25 @@
+// Copyright 2007 The Android Open Source Project
+
+/**
+ * Test a class with a bad finalizer.
+ */
+public class Main {
+ public static void main(String[] args) {
+ BadFinalizer bf = new BadFinalizer();
+
+ System.out.println("Constructed object.");
+ bf = null;
+
+ System.out.println("Nulled. Requestion gc.");
+ System.gc();
+
+ for (int i = 0; i < 8; i++) {
+ BadFinalizer.snooze(5000);
+ System.out.println("Requesting another GC.");
+ System.gc();
+ }
+
+ System.out.println("Done waiting.");
+ System.exit(0);
+ }
+}
diff --git a/tests/031-class-attributes/expected.txt b/tests/031-class-attributes/expected.txt
new file mode 100644
index 0000000..47eaeee
--- /dev/null
+++ b/tests/031-class-attributes/expected.txt
@@ -0,0 +1,164 @@
+***** class ClassAttrs:
+ name: ClassAttrs
+ canonical: ClassAttrs
+ simple: ClassAttrs
+ genericSignature: null
+ super: class java.lang.Object
+ declaring: null
+ enclosing: null
+ enclosingCon: null
+ enclosingMeth: null
+ modifiers: 1
+ package: null
+ declaredClasses: [2] class ClassAttrs$PublicMemberClass, class ClassAttrs$MemberClass
+ member classes: [1] class ClassAttrs$PublicMemberClass
+ isAnnotation: false
+ isAnonymous: false
+ isArray: false
+ isEnum: false
+ isInterface: false
+ isLocalClass: false
+ isMemberClass: false
+ isPrimitive: false
+ isSynthetic: false
+***** class OtherClass:
+ name: OtherClass
+ canonical: OtherClass
+ simple: OtherClass
+ genericSignature: null
+ super: class java.lang.Object
+ declaring: null
+ enclosing: null
+ enclosingCon: null
+ enclosingMeth: null
+ modifiers: 0
+ package: null
+ declaredClasses: [0]
+ member classes: [0]
+ isAnnotation: false
+ isAnonymous: false
+ isArray: false
+ isEnum: false
+ isInterface: false
+ isLocalClass: false
+ isMemberClass: false
+ isPrimitive: false
+ isSynthetic: false
+***** class otherpackage.OtherPackageClass:
+ name: otherpackage.OtherPackageClass
+ canonical: otherpackage.OtherPackageClass
+ simple: OtherPackageClass
+ genericSignature: null
+ super: class java.lang.Object
+ declaring: null
+ enclosing: null
+ enclosingCon: null
+ enclosingMeth: null
+ modifiers: 1
+ package: package otherpackage
+ declaredClasses: [0]
+ member classes: [0]
+ isAnnotation: false
+ isAnonymous: false
+ isArray: false
+ isEnum: false
+ isInterface: false
+ isLocalClass: false
+ isMemberClass: false
+ isPrimitive: false
+ isSynthetic: false
+***** class ClassAttrs$1InnerNamed:
+ name: ClassAttrs$1InnerNamed
+ canonical: null
+ simple: InnerNamed
+ genericSignature: null
+ super: class java.lang.Object
+ declaring: null
+ enclosing: class ClassAttrs
+ enclosingCon: null
+ enclosingMeth: public static void ClassAttrs.main()
+ modifiers: 0
+ package: null
+ declaredClasses: [0]
+ member classes: [0]
+ isAnnotation: false
+ isAnonymous: false
+ isArray: false
+ isEnum: false
+ isInterface: false
+ isLocalClass: true
+ isMemberClass: false
+ isPrimitive: false
+ isSynthetic: false
+***** class ClassAttrs$1ConsInnerNamed:
+ name: ClassAttrs$1ConsInnerNamed
+ canonical: null
+ simple: ConsInnerNamed
+ genericSignature: null
+ super: class java.lang.Object
+ declaring: null
+ enclosing: class ClassAttrs
+ enclosingCon: ClassAttrs()
+ enclosingMeth: null
+ modifiers: 0
+ package: null
+ declaredClasses: [0]
+ member classes: [0]
+ isAnnotation: false
+ isAnonymous: false
+ isArray: false
+ isEnum: false
+ isInterface: false
+ isLocalClass: true
+ isMemberClass: false
+ isPrimitive: false
+ isSynthetic: false
+***** class ClassAttrs$1:
+ name: ClassAttrs$1
+ canonical: null
+ simple:
+ genericSignature: null
+ super: class OtherClass
+ declaring: null
+ enclosing: class ClassAttrs
+ enclosingCon: null
+ enclosingMeth: public static void ClassAttrs.main()
+ modifiers: 8
+ package: null
+ declaredClasses: [0]
+ member classes: [0]
+ isAnnotation: false
+ isAnonymous: true
+ isArray: false
+ isEnum: false
+ isInterface: false
+ isLocalClass: false
+ isMemberClass: false
+ isPrimitive: false
+ isSynthetic: false
+***** class ClassAttrs$MemberClass:
+ name: ClassAttrs$MemberClass
+ canonical: ClassAttrs.MemberClass
+ simple: MemberClass
+ genericSignature: <XYZ:Ljava/lang/Object;>Ljava/lang/Object;
+ super: class java.lang.Object
+ declaring: class ClassAttrs
+ enclosing: class ClassAttrs
+ enclosingCon: null
+ enclosingMeth: null
+ modifiers: 8
+ package: null
+ declaredClasses: [0]
+ member classes: [0]
+ isAnnotation: false
+ isAnonymous: false
+ isArray: false
+ isEnum: false
+ isInterface: false
+ isLocalClass: false
+ isMemberClass: true
+ isPrimitive: false
+ isSynthetic: false
+constructor signature: (LClassAttrs$MemberClass<TXYZ;>;)V
+method signature: ()Ljava/lang/Class<TXYZ;>;
+field signature: LClassAttrs$MemberClass<TXYZ;>;
diff --git a/tests/031-class-attributes/info.txt b/tests/031-class-attributes/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/031-class-attributes/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/031-class-attributes/src/ClassAttrs.java b/tests/031-class-attributes/src/ClassAttrs.java
new file mode 100644
index 0000000..c1407bd
--- /dev/null
+++ b/tests/031-class-attributes/src/ClassAttrs.java
@@ -0,0 +1,201 @@
+import otherpackage.OtherPackageClass;
+
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+
+public class ClassAttrs {
+ ClassAttrs() {
+ /* local, not anonymous, not member */
+ class ConsInnerNamed {
+ public void showMe() {
+ printClassAttrs(this.getClass());
+ }
+ }
+
+ ConsInnerNamed cinner = new ConsInnerNamed();
+ cinner.showMe();
+ }
+
+ public static void main() {
+ printClassAttrs(ClassAttrs.class);
+ printClassAttrs(OtherClass.class);
+ printClassAttrs(OtherPackageClass.class);
+
+ /* local, not anonymous, not member */
+ class InnerNamed {
+ public void showMe() {
+ printClassAttrs(this.getClass());
+ }
+ }
+ InnerNamed inner = new InnerNamed();
+ inner.showMe();
+
+ ClassAttrs attrs = new ClassAttrs();
+
+ /* anonymous, not local, not member */
+ printClassAttrs((new OtherClass() { int i = 5; }).getClass());
+
+ /* member, not anonymous, not local */
+ printClassAttrs(MemberClass.class);
+
+ try {
+ Constructor cons;
+ cons = MemberClass.class.getConstructor(
+ new Class[] { MemberClass.class });
+ System.out.println("constructor signature: "
+ + getSignatureAttribute(cons));
+
+ Method meth;
+ meth = MemberClass.class.getMethod("foo", (Class[]) null);
+ System.out.println("method signature: "
+ + getSignatureAttribute(meth));
+
+ Field field;
+ field = MemberClass.class.getField("mWha");
+ System.out.println("field signature: "
+ + getSignatureAttribute(field));
+ } catch (NoSuchMethodException nsme) {
+ System.err.println("FAILED: " + nsme);
+ } catch (NoSuchFieldException nsfe) {
+ System.err.println("FAILED: " + nsfe);
+ } catch (RuntimeException re) {
+ System.err.println("FAILED: " + re);
+ re.printStackTrace();
+ }
+ }
+
+ /* to call the (out-of-scope) <code>getSignatureAttribute</code> methods */
+ public static String getSignatureAttribute(Object obj) {
+ Method method;
+ try {
+ if (obj instanceof AccessibleObject) {
+ method = AccessibleObject.class.getDeclaredMethod(
+ "getSignatureAttribute");
+ } else {
+ // Should be a Class.
+ method = Class.class.getDeclaredMethod(
+ "getSignatureAttribute");
+ }
+ method.setAccessible(true);
+ } catch (NoSuchMethodException ex) {
+ System.err.println("getSignatureAttribute() not defined.");
+ ex.printStackTrace();
+ return "<unknown>";
+ }
+
+ try {
+ return (String) method.invoke(obj);
+ } catch (IllegalAccessException ex) {
+ throw new RuntimeException(ex);
+ } catch (InvocationTargetException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ /* for reflection testing */
+ static class MemberClass<XYZ> {
+ public MemberClass<XYZ> mWha;
+
+ public MemberClass(MemberClass<XYZ> memb) {
+ mWha = memb;
+ }
+
+ public Class<XYZ> foo() throws NoSuchMethodException {
+ return null;
+ }
+ }
+
+ /* for reflection testing (getClasses vs getDeclaredClasses) */
+ static public class PublicMemberClass {
+ float mBlah;
+ }
+
+ /*
+ * Dump a variety of class attributes.
+ */
+ public static void printClassAttrs(Class clazz) {
+ final boolean WORKING = false;
+ Class clazz2;
+
+ System.out.println("***** " + clazz + ":");
+
+ System.out.println(" name: "
+ + clazz.getName());
+ System.out.println(" canonical: "
+ + clazz.getCanonicalName());
+ System.out.println(" simple: "
+ + clazz.getSimpleName());
+ System.out.println(" genericSignature: "
+ + getSignatureAttribute(clazz));
+
+ System.out.println(" super: "
+ + clazz.getSuperclass());
+ if (WORKING) System.out.println(" genericSuperclass: "
+ + clazz.getGenericSuperclass());
+ System.out.println(" declaring: "
+ + clazz.getDeclaringClass());
+ System.out.println(" enclosing: "
+ + clazz.getEnclosingClass());
+ System.out.println(" enclosingCon: "
+ + clazz.getEnclosingConstructor());
+ System.out.println(" enclosingMeth: "
+ + clazz.getEnclosingMethod());
+ System.out.println(" modifiers: "
+ + clazz.getModifiers());
+ System.out.println(" package: "
+ + clazz.getPackage());
+
+ System.out.println(" declaredClasses: "
+ + stringifyTypeArray(clazz.getDeclaredClasses()));
+ System.out.println(" member classes: "
+ + stringifyTypeArray(clazz.getClasses()));
+
+ System.out.println(" isAnnotation: "
+ + clazz.isAnnotation());
+ System.out.println(" isAnonymous: "
+ + clazz.isAnonymousClass());
+ System.out.println(" isArray: "
+ + clazz.isArray());
+ System.out.println(" isEnum: "
+ + clazz.isEnum());
+ System.out.println(" isInterface: "
+ + clazz.isInterface());
+ System.out.println(" isLocalClass: "
+ + clazz.isLocalClass());
+ System.out.println(" isMemberClass: "
+ + clazz.isMemberClass());
+ System.out.println(" isPrimitive: "
+ + clazz.isPrimitive());
+ System.out.println(" isSynthetic: "
+ + clazz.isSynthetic());
+
+ if (WORKING) System.out.println(" genericInterfaces: "
+ + stringifyTypeArray(clazz.getGenericInterfaces()));
+ }
+
+ /*
+ * Convert an array of Type into a string. Start with an array count.
+ */
+ private static String stringifyTypeArray(Type[] types) {
+ StringBuilder stb = new StringBuilder();
+ boolean first = true;
+
+ stb.append("[" + types.length + "]");
+
+ for (Type t: types) {
+ if (first) {
+ stb.append(" ");
+ first = false;
+ } else {
+ stb.append(", ");
+ }
+ stb.append(t.toString());
+ }
+
+ return stb.toString();
+ }
+}
diff --git a/tests/031-class-attributes/src/Main.java b/tests/031-class-attributes/src/Main.java
new file mode 100644
index 0000000..bc6b749
--- /dev/null
+++ b/tests/031-class-attributes/src/Main.java
@@ -0,0 +1,5 @@
+public class Main {
+ public static void main(String[] args) {
+ ClassAttrs.main();
+ }
+}
diff --git a/tests/031-class-attributes/src/OtherClass.java b/tests/031-class-attributes/src/OtherClass.java
new file mode 100644
index 0000000..0b4526e
--- /dev/null
+++ b/tests/031-class-attributes/src/OtherClass.java
@@ -0,0 +1,2 @@
+class OtherClass {
+}
diff --git a/tests/031-class-attributes/src/otherpackage/OtherPackageClass.java b/tests/031-class-attributes/src/otherpackage/OtherPackageClass.java
new file mode 100644
index 0000000..9652b77
--- /dev/null
+++ b/tests/031-class-attributes/src/otherpackage/OtherPackageClass.java
@@ -0,0 +1,4 @@
+package otherpackage;
+
+public class OtherPackageClass {
+}
diff --git a/tests/032-concrete-sub/expected.txt b/tests/032-concrete-sub/expected.txt
new file mode 100644
index 0000000..56f25bb
--- /dev/null
+++ b/tests/032-concrete-sub/expected.txt
@@ -0,0 +1,6 @@
+calling abs.doStuff()
+In AbstractBase.doStuff (src2)
+Got expected exception from abs.doStuff().
+class modifiers=1025
+meth modifiers=1025
+Got expected failure
diff --git a/tests/032-concrete-sub/info.txt b/tests/032-concrete-sub/info.txt
new file mode 100644
index 0000000..1956994
--- /dev/null
+++ b/tests/032-concrete-sub/info.txt
@@ -0,0 +1,3 @@
+This tests some facets of abstract method handling, notably situations
+where a concrete class and its abstract superclass were compiled with
+different notions about which methods are abstract.
diff --git a/tests/032-concrete-sub/src/AbstractBase.java b/tests/032-concrete-sub/src/AbstractBase.java
new file mode 100644
index 0000000..6281189
--- /dev/null
+++ b/tests/032-concrete-sub/src/AbstractBase.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Abstract base class.
+ */
+public abstract class AbstractBase {
+ public void doStuff() {
+ System.out.println("In AbstractBase.doStuff");
+ }
+
+ public void abstractOrNot() {}
+}
diff --git a/tests/032-concrete-sub/src/ConcreteSub.java b/tests/032-concrete-sub/src/ConcreteSub.java
new file mode 100644
index 0000000..083f25d
--- /dev/null
+++ b/tests/032-concrete-sub/src/ConcreteSub.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Method;
+
+/**
+ * Test insertion of an abstract method in a superclass.
+ */
+public class ConcreteSub extends AbstractBase {
+ private static void callBase(AbstractBase abs) {
+ System.out.println("calling abs.doStuff()");
+ abs.doStuff();
+ }
+
+ public static void main() {
+ ConcreteSub sub = new ConcreteSub();
+
+ try {
+ callBase(sub);
+ } catch (AbstractMethodError ame) {
+ System.out.println("Got expected exception from abs.doStuff().");
+ }
+
+ /*
+ * Check reflection stuff.
+ */
+ Class absClass = AbstractBase.class;
+ Method meth;
+
+ System.out.println("class modifiers=" + absClass.getModifiers());
+
+ try {
+ meth = absClass.getMethod("redefineMe", (Class[]) null);
+ } catch (NoSuchMethodException nsme) {
+ nsme.printStackTrace();
+ return;
+ }
+ System.out.println("meth modifiers=" + meth.getModifiers());
+ }
+}
diff --git a/tests/032-concrete-sub/src/ConcreteSub2.java b/tests/032-concrete-sub/src/ConcreteSub2.java
new file mode 100644
index 0000000..0a9e67e
--- /dev/null
+++ b/tests/032-concrete-sub/src/ConcreteSub2.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Test conversion of a concrete method to an abstract method. This class
+ * will fail verification because there is no implementation of the
+ * abstractOrNot() method.
+ */
+public class ConcreteSub2 extends AbstractBase {
+ public void doStuff() {
+ abstractOrNot();
+ }
+}
diff --git a/tests/032-concrete-sub/src/Main.java b/tests/032-concrete-sub/src/Main.java
new file mode 100644
index 0000000..4a5193d
--- /dev/null
+++ b/tests/032-concrete-sub/src/Main.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Test insertion of an abstract method in a superclass.
+ */
+public class Main {
+ public static void main(String[] args) {
+ ConcreteSub.main();
+
+ try {
+ // Dalvik verifier stops here (VerifyError)
+ ConcreteSub2 blah = new ConcreteSub2();
+ // other VMs fail here (AbstractMethodError)
+ blah.doStuff();
+ System.err.println("Succeeded unexpectedly");
+ } catch (VerifyError ve) {
+ System.out.println("Got expected failure");
+ } catch (AbstractMethodError ame) {
+ System.out.println("Got expected failure");
+ }
+ }
+}
diff --git a/tests/032-concrete-sub/src2/AbstractBase.java b/tests/032-concrete-sub/src2/AbstractBase.java
new file mode 100644
index 0000000..534f738
--- /dev/null
+++ b/tests/032-concrete-sub/src2/AbstractBase.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Abstract base class.
+ */
+public abstract class AbstractBase {
+ public void doStuff() {
+ System.out.println("In AbstractBase.doStuff (src2)");
+ redefineMe();
+ }
+
+ public abstract void redefineMe();
+
+ public abstract void abstractOrNot();
+}
diff --git a/tests/033-class-init-deadlock/expected.txt b/tests/033-class-init-deadlock/expected.txt
new file mode 100644
index 0000000..387a426
--- /dev/null
+++ b/tests/033-class-init-deadlock/expected.txt
@@ -0,0 +1,7 @@
+Deadlock test starting.
+A initializing...
+B initializing...
+Deadlock test interupting threads.
+Deadlock test main thread bailing.
+A initialized: false
+B initialized: false
diff --git a/tests/033-class-init-deadlock/info.txt b/tests/033-class-init-deadlock/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/033-class-init-deadlock/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/033-class-init-deadlock/src/Main.java b/tests/033-class-init-deadlock/src/Main.java
new file mode 100644
index 0000000..27c4922
--- /dev/null
+++ b/tests/033-class-init-deadlock/src/Main.java
@@ -0,0 +1,51 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * This causes most VMs to lock up.
+ *
+ * Interrupting threads in class initialization should NOT work.
+ */
+public class Main {
+ public static boolean aInitialized = false;
+ public static boolean bInitialized = false;
+
+ static public void main(String[] args) {
+ Thread thread1, thread2;
+
+ System.out.println("Deadlock test starting.");
+ thread1 = new Thread() { public void run() { new A(); } };
+ thread2 = new Thread() { public void run() { new B(); } };
+ thread1.start();
+ thread2.start();
+
+ try { Thread.sleep(6000); } catch (InterruptedException ie) { }
+
+ System.out.println("Deadlock test interupting threads.");
+ thread1.interrupt();
+ thread2.interrupt();
+ System.out.println("Deadlock test main thread bailing.");
+ System.out.println("A initialized: " + aInitialized);
+ System.out.println("B initialized: " + bInitialized);
+ System.exit(0);
+ }
+}
+
+class A {
+ static {
+ System.out.println("A initializing...");
+ try { Thread.sleep(3000); } catch (InterruptedException ie) { }
+ new B();
+ System.out.println("A initialized");
+ Main.aInitialized = true;
+ }
+}
+
+class B {
+ static {
+ System.out.println("B initializing...");
+ try { Thread.sleep(3000); } catch (InterruptedException ie) { }
+ new A();
+ System.out.println("B initialized");
+ Main.bInitialized = true;
+ }
+}
diff --git a/tests/034-call-null/expected.txt b/tests/034-call-null/expected.txt
new file mode 100644
index 0000000..5ffbe05
--- /dev/null
+++ b/tests/034-call-null/expected.txt
@@ -0,0 +1,3 @@
+java.lang.NullPointerException
+ at Main.main(Main.java:12)
+ at dalvik.system.NativeStart.main(Native Method)
diff --git a/tests/034-call-null/info.txt b/tests/034-call-null/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/034-call-null/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/034-call-null/src/Main.java b/tests/034-call-null/src/Main.java
new file mode 100644
index 0000000..a0a129e
--- /dev/null
+++ b/tests/034-call-null/src/Main.java
@@ -0,0 +1,14 @@
+// Copyright 2008 The Android Open Source Project
+
+public class Main {
+ int mFoo = 27;
+
+ private void doStuff() {
+ System.out.println("mFoo is " + mFoo);
+ }
+
+ public static void main(String[] args) {
+ Main instance = null;
+ instance.doStuff();
+ }
+}
diff --git a/tests/035-enum/expected.txt b/tests/035-enum/expected.txt
new file mode 100644
index 0000000..50f2791
--- /dev/null
+++ b/tests/035-enum/expected.txt
@@ -0,0 +1,3 @@
+found field CRAWLING
+ synthetic? false
+ enum? true
diff --git a/tests/035-enum/info.txt b/tests/035-enum/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/035-enum/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/035-enum/src/Main.java b/tests/035-enum/src/Main.java
new file mode 100644
index 0000000..09fcc98
--- /dev/null
+++ b/tests/035-enum/src/Main.java
@@ -0,0 +1,23 @@
+// Copyright 2008 The Android Open Source Project
+
+import java.lang.reflect.Field;
+
+/**
+ * Try some stuff with enumerations.
+ */
+public class Main {
+ public enum Shubbery { GROUND, CRAWLING, HANGING }
+
+ public static void main(String[] args) {
+ Field field;
+ try {
+ field = Shubbery.class.getDeclaredField("CRAWLING");
+ } catch (NoSuchFieldException nsfe) {
+ throw new RuntimeException(nsfe);
+ }
+
+ System.out.println("found field " + field.getName());
+ System.out.println(" synthetic? " + field.isSynthetic());
+ System.out.println(" enum? " + field.isEnumConstant());
+ }
+}
diff --git a/tests/036-finalizer/expected.txt b/tests/036-finalizer/expected.txt
new file mode 100644
index 0000000..f9b29b0
--- /dev/null
+++ b/tests/036-finalizer/expected.txt
@@ -0,0 +1,14 @@
+wimp: wahoo
+gc
+finalizer executed: wahoo
+wimp: null
+finalize
+wimp: null
+sleep
+reborn: wahoo
+wimp: null
+reset reborn
+gc + finalize
+sleep
+reborn: nothing
+wimp: null
diff --git a/tests/036-finalizer/info.txt b/tests/036-finalizer/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/036-finalizer/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/036-finalizer/src/FinalizerTest.java b/tests/036-finalizer/src/FinalizerTest.java
new file mode 100644
index 0000000..420ec34
--- /dev/null
+++ b/tests/036-finalizer/src/FinalizerTest.java
@@ -0,0 +1,23 @@
+// Copyright 2008 The Android Open Source Project
+
+import java.lang.ref.WeakReference;
+
+public class FinalizerTest {
+ public static FinalizerTest mNothing = new FinalizerTest("nothing");
+ public static FinalizerTest mReborn = mNothing;
+
+ public String mMsg = "default";
+
+ public FinalizerTest(String msg) {
+ mMsg = msg;
+ }
+
+ public String toString() {
+ return mMsg;
+ }
+
+ protected void finalize() {
+ System.out.println("finalizer executed: " + mMsg);
+ mReborn = this;
+ }
+}
diff --git a/tests/036-finalizer/src/Main.java b/tests/036-finalizer/src/Main.java
new file mode 100644
index 0000000..c29cc11
--- /dev/null
+++ b/tests/036-finalizer/src/Main.java
@@ -0,0 +1,107 @@
+// Copyright 2008 The Android Open Source Project
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Some finalizer tests.
+ *
+ * This only works if System.runFinalization() causes finalizers to run
+ * immediately or very soon.
+ */
+public class Main {
+ private static void snooze(int ms) {
+ try {
+ Thread.sleep(ms);
+ } catch (InterruptedException ie) {
+ System.out.println("Snooze: " + ie.getMessage());
+ }
+ }
+
+ public static WeakReference makeRef() {
+ /*
+ * Make ft in another thread, so there is no danger of
+ * a conservative reference leaking onto the main thread's
+ * stack.
+ */
+
+ final WeakReference[] wimp = new WeakReference[1];
+ Thread t = new Thread() {
+ public void run() {
+ FinalizerTest ft = new FinalizerTest("wahoo");
+ wimp[0] = new WeakReference(ft);
+ ft = null;
+ }
+ };
+
+ t.start();
+
+ try {
+ t.join();
+ } catch (InterruptedException ie) {
+ throw new RuntimeException(ie);
+ }
+
+ return wimp[0];
+ }
+
+ public static String wimpString(final WeakReference wimp) {
+ /*
+ * Do the work in another thread, so there is no danger of a
+ * conservative reference to ft leaking onto the main thread's
+ * stack.
+ */
+
+ final String[] s = new String[1];
+ Thread t = new Thread() {
+ public void run() {
+ Object ref = wimp.get();
+ if (ref != null) {
+ s[0] = ref.toString();
+ }
+ }
+ };
+
+ t.start();
+
+ try {
+ t.join();
+ } catch (InterruptedException ie) {
+ throw new RuntimeException(ie);
+ }
+
+ return s[0];
+ }
+
+ public static void main(String[] args) {
+ WeakReference wimp = makeRef();
+
+ System.out.println("wimp: " + wimpString(wimp));
+
+ /* this will try to collect and finalize ft */
+ System.out.println("gc");
+ System.gc();
+
+ System.out.println("wimp: " + wimpString(wimp));
+ System.out.println("finalize");
+ System.runFinalization();
+ System.out.println("wimp: " + wimpString(wimp));
+
+ System.out.println("sleep");
+ snooze(1000);
+
+ System.out.println("reborn: " + FinalizerTest.mReborn);
+ System.out.println("wimp: " + wimpString(wimp));
+ System.out.println("reset reborn");
+ System.gc();
+ FinalizerTest.mReborn = FinalizerTest.mNothing;
+ System.out.println("gc + finalize");
+ System.gc();
+ System.runFinalization();
+
+ System.out.println("sleep");
+ snooze(1000);
+
+ System.out.println("reborn: " + FinalizerTest.mReborn);
+ System.out.println("wimp: " + wimpString(wimp));
+ }
+}
diff --git a/tests/037-inherit/expected.txt b/tests/037-inherit/expected.txt
new file mode 100644
index 0000000..1fb9912
--- /dev/null
+++ b/tests/037-inherit/expected.txt
@@ -0,0 +1,3 @@
+magic is 64.0
+ 0: 64.0
+ 1: 64.0
diff --git a/tests/037-inherit/info.txt b/tests/037-inherit/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/037-inherit/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/037-inherit/src/Main.java b/tests/037-inherit/src/Main.java
new file mode 100644
index 0000000..55b782e
--- /dev/null
+++ b/tests/037-inherit/src/Main.java
@@ -0,0 +1,37 @@
+public class Main {
+ static void arrayCluster(IMagic[] magicArray) {
+ int i;
+
+ for (i = 0; i < magicArray.length; i++)
+ System.out.println(" " + i + ": " + magicArray[i].getSomeData());
+ }
+
+ public static void main(String args[]) {
+ MagicClass magic = new MagicClass();
+
+ System.out.print("magic is ");
+ System.out.println(magic.getSomeData());
+
+ MagicClass magicArray[] = new MagicClass[2];
+ magicArray[0] = new MagicClass();
+ magicArray[1] = new MagicClass();
+ arrayCluster(magicArray);
+ }
+}
+
+class IntSource {
+ public int getMagicInt() { return 64; }
+}
+
+interface IMagic {
+ public double getSomeData();
+
+ IntSource mIntSource = new IntSource();
+ public int MAGIC_INT = mIntSource.getMagicInt();
+}
+
+class MagicClass implements IMagic {
+ public double getSomeData() {
+ return this.MAGIC_INT;
+ }
+}
diff --git a/tests/038-inner-null/expected.txt b/tests/038-inner-null/expected.txt
new file mode 100644
index 0000000..0be8ffd
--- /dev/null
+++ b/tests/038-inner-null/expected.txt
@@ -0,0 +1,5 @@
+new Special()
+java.lang.NullPointerException
+ at Main$Special.callInner(Main.java:17)
+ at Main.main(Main.java:6)
+ at dalvik.system.NativeStart.main(Native Method)
diff --git a/tests/038-inner-null/info.txt b/tests/038-inner-null/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/038-inner-null/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/038-inner-null/src/Main.java b/tests/038-inner-null/src/Main.java
new file mode 100644
index 0000000..acc8764
--- /dev/null
+++ b/tests/038-inner-null/src/Main.java
@@ -0,0 +1,27 @@
+// Copyright 2008 The Android Open Source Project
+
+public class Main {
+ public static void main(String[] args) {
+ Special special = new Special();
+ special.callInner();
+ }
+
+ public static class Special {
+ Blort mBlort = null;
+
+ Special() {
+ System.out.println("new Special()");
+ }
+
+ public void callInner() {
+ mBlort.repaint();
+ }
+ }
+
+ private class Blort {
+ public void repaint() {
+ System.out.println("shouldn't see this");
+ }
+ }
+
+}
diff --git a/tests/039-join-main/expected.txt b/tests/039-join-main/expected.txt
new file mode 100644
index 0000000..37e6d77
--- /dev/null
+++ b/tests/039-join-main/expected.txt
@@ -0,0 +1,5 @@
+Starting thread 'Joiner'
+@ JoinMainSub running
+JoinMain starter returning
+@ JoinMainSub successfully joined main
+@ JoinMainSub bailing
diff --git a/tests/039-join-main/info.txt b/tests/039-join-main/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/039-join-main/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/039-join-main/src/Main.java b/tests/039-join-main/src/Main.java
new file mode 100644
index 0000000..0644f1c
--- /dev/null
+++ b/tests/039-join-main/src/Main.java
@@ -0,0 +1,41 @@
+// Copyright 2007 The Android Open Source Project
+
+/**
+ * Make sure that a sub-thread can join the main thread.
+ */
+public class Main {
+ public static void main(String[] args) {
+ Thread t;
+
+ t = new Thread(new JoinMainSub(Thread.currentThread()), "Joiner");
+ System.out.print("Starting thread '" + t.getName() + "'\n");
+ t.start();
+
+ try { Thread.sleep(1000); }
+ catch (InterruptedException ie) {}
+
+ System.out.print("JoinMain starter returning\n");
+ }
+}
+
+class JoinMainSub implements Runnable {
+ private Thread mJoinMe;
+
+ public JoinMainSub(Thread joinMe) {
+ mJoinMe = joinMe;
+ }
+
+ public void run() {
+ System.out.print("@ JoinMainSub running\n");
+
+ try {
+ mJoinMe.join();
+ System.out.print("@ JoinMainSub successfully joined main\n");
+ } catch (InterruptedException ie) {
+ System.out.print("@ JoinMainSub interrupted!\n");
+ }
+ finally {
+ System.out.print("@ JoinMainSub bailing\n");
+ }
+ }
+}
diff --git a/tests/040-miranda/expected.txt b/tests/040-miranda/expected.txt
new file mode 100644
index 0000000..e22bbd9
--- /dev/null
+++ b/tests/040-miranda/expected.txt
@@ -0,0 +1,12 @@
+MirandaClass:
+ inInterface: true
+ inInterface2: 27
+ inAbstract: false
+MirandaAbstract / MirandaClass:
+ inInterface: true
+ inInterface2: 27
+ inAbstract: false
+MirandaAbstract / MirandaClass2:
+ inInterface: true
+ inInterface2: 28
+ inAbstract: true
diff --git a/tests/040-miranda/info.txt b/tests/040-miranda/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/040-miranda/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/040-miranda/src/Main.java b/tests/040-miranda/src/Main.java
new file mode 100644
index 0000000..558806a
--- /dev/null
+++ b/tests/040-miranda/src/Main.java
@@ -0,0 +1,27 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Miranda testing.
+ */
+public class Main {
+ public static void main(String[] args) {
+ MirandaClass mir = new MirandaClass();
+ System.out.println("MirandaClass:");
+ System.out.println(" inInterface: " + mir.inInterface());
+ System.out.println(" inInterface2: " + mir.inInterface2());
+ System.out.println(" inAbstract: " + mir.inAbstract());
+
+ /* try again through abstract class; results should be identical */
+ MirandaAbstract mira = mir;
+ System.out.println("MirandaAbstract / MirandaClass:");
+ System.out.println(" inInterface: " + mira.inInterface());
+ System.out.println(" inInterface2: " + mira.inInterface2());
+ System.out.println(" inAbstract: " + mira.inAbstract());
+
+ MirandaAbstract mira2 = new MirandaClass2();
+ System.out.println("MirandaAbstract / MirandaClass2:");
+ System.out.println(" inInterface: " + mira2.inInterface());
+ System.out.println(" inInterface2: " + mira2.inInterface2());
+ System.out.println(" inAbstract: " + mira2.inAbstract());
+ }
+}
diff --git a/tests/040-miranda/src/MirandaAbstract.java b/tests/040-miranda/src/MirandaAbstract.java
new file mode 100644
index 0000000..b603ce6
--- /dev/null
+++ b/tests/040-miranda/src/MirandaAbstract.java
@@ -0,0 +1,16 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Miranda testing.
+ */
+public abstract class MirandaAbstract implements MirandaInterface, MirandaInterface2
+{
+ protected MirandaAbstract() { }
+
+ //public abstract boolean inInterface();
+ //public abstract int inInterface2();
+
+ public boolean inAbstract() {
+ return true;
+ }
+}
diff --git a/tests/040-miranda/src/MirandaClass.java b/tests/040-miranda/src/MirandaClass.java
new file mode 100644
index 0000000..3bf6704
--- /dev/null
+++ b/tests/040-miranda/src/MirandaClass.java
@@ -0,0 +1,24 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Miranda testing.
+ */
+public class MirandaClass extends MirandaAbstract {
+
+ public MirandaClass() {}
+
+ public boolean inInterface() {
+ //System.out.println(" MirandaClass inInterface");
+ return true;
+ }
+
+ public int inInterface2() {
+ //System.out.println(" MirandaClass inInterface2");
+ return 27;
+ }
+
+ public boolean inAbstract() {
+ //System.out.println(" MirandaClass inAbstract");
+ return false;
+ }
+}
diff --git a/tests/040-miranda/src/MirandaClass2.java b/tests/040-miranda/src/MirandaClass2.java
new file mode 100644
index 0000000..e9bdf2b
--- /dev/null
+++ b/tests/040-miranda/src/MirandaClass2.java
@@ -0,0 +1,9 @@
+class MirandaClass2 extends MirandaAbstract {
+ public boolean inInterface() {
+ return true;
+ }
+
+ public int inInterface2() {
+ return 28;
+ }
+}
diff --git a/tests/040-miranda/src/MirandaInterface.java b/tests/040-miranda/src/MirandaInterface.java
new file mode 100644
index 0000000..2c0a59a
--- /dev/null
+++ b/tests/040-miranda/src/MirandaInterface.java
@@ -0,0 +1,10 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Miranda testing.
+ */
+public interface MirandaInterface {
+
+ public boolean inInterface();
+
+}
diff --git a/tests/040-miranda/src/MirandaInterface2.java b/tests/040-miranda/src/MirandaInterface2.java
new file mode 100644
index 0000000..83b6af8
--- /dev/null
+++ b/tests/040-miranda/src/MirandaInterface2.java
@@ -0,0 +1,12 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Miranda testing.
+ */
+public interface MirandaInterface2 {
+
+ public boolean inInterface();
+
+ public int inInterface2();
+
+}
diff --git a/tests/041-narrowing/expected.txt b/tests/041-narrowing/expected.txt
new file mode 100644
index 0000000..93b8590
--- /dev/null
+++ b/tests/041-narrowing/expected.txt
@@ -0,0 +1,38 @@
+
+Double.POSITIVE_INFINITY = 7ff0000000000000
+Double.NEGATIVE_INFINITY = fff0000000000000
+Float.POSITIVE_INFINITY = 7ff0000000000000
+Float.NEGATIVE_INFINITY = fff0000000000000
+Double.NaN = 7ff8000000000000
+Float.NaN = 7ff8000000000000
+
+(byte) Double.NaN = 00 expected: 00
+(short) Double.NaN = 0000 expected: 0000
+(int) Double.NaN = 00000000 expected: 00000000
+(long) Double.NaN = 0000000000000000 expected: 0000000000000000
+
+(byte) Float.NaN = 00 expected: 00
+(short) Float.NaN = 0000 expected: 0000
+(int) Float.NaN = 00000000 expected: 00000000
+(long) Float.NaN = 0000000000000000 expected: 0000000000000000
+
+(byte) Double.POSITIVE_INFINITY = ff expected: ff
+(short) Double.POSITIVE_INFINITY = ffff expected: ffff
+(int) Double.POSITIVE_INFINITY = 7fffffff expected: 7fffffff
+(long) Double.POSITIVE_INFINITY = 7fffffffffffffff expected: 7fffffffffffffff
+
+(byte) Double.NEGATIVE_INFINITY = 00 expected: 00
+(short) Double.NEGATIVE_INFINITY = 0000 expected: 0000
+(int) Double.NEGATIVE_INFINITY = 80000000 expected: 80000000
+(long) Double.NEGATIVE_INFINITY = 8000000000000000 expected: 8000000000000000
+
+(byte) Float.POSITIVE_INFINITY = ff expected: ff
+(short) Float.POSITIVE_INFINITY = ffff expected: ffff
+(int) Float.POSITIVE_INFINITY = 7fffffff expected: 7fffffff
+(long) Float.POSITIVE_INFINITY = 7fffffffffffffff expected: 7fffffffffffffff
+
+(byte) Float.NEGATIVE_INFINITY = 00 expected: 00
+(short) Float.NEGATIVE_INFINITY = 0000 expected: 0000
+(int) Float.NEGATIVE_INFINITY = 80000000 expected: 80000000
+(long) Float.NEGATIVE_INFINITY = 8000000000000000 expected: 8000000000000000
+
diff --git a/tests/041-narrowing/info.txt b/tests/041-narrowing/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/041-narrowing/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/041-narrowing/src/Main.java b/tests/041-narrowing/src/Main.java
new file mode 100644
index 0000000..eb9d64a
--- /dev/null
+++ b/tests/041-narrowing/src/Main.java
@@ -0,0 +1,99 @@
+public class Main {
+ public static void main(String[] args) {
+ test_printNarrowing();
+ }
+
+ public static void test_printNarrowing() {
+
+ System.out.println();
+ System.out.println("Double.POSITIVE_INFINITY = "
+ + Long.toHexString(Double.doubleToRawLongBits(Double.POSITIVE_INFINITY)));
+ System.out.println("Double.NEGATIVE_INFINITY = "
+ + Long.toHexString(Double.doubleToRawLongBits(Double.NEGATIVE_INFINITY)));
+ System.out.println("Float.POSITIVE_INFINITY = "
+ + Long.toHexString(Double.doubleToRawLongBits(Float.POSITIVE_INFINITY)));
+ System.out.println("Float.NEGATIVE_INFINITY = "
+ + Long.toHexString(Double.doubleToRawLongBits(Float.NEGATIVE_INFINITY)));
+ System.out.println("Double.NaN = "
+ + Long.toHexString(Double.doubleToRawLongBits(Double.NaN)));
+ System.out.println("Float.NaN = "
+ + Long.toHexString(Double.doubleToRawLongBits(Float.NaN)));
+ double dbl2 = Double.NaN;
+ System.out.println();
+ System.out.println("(byte) Double.NaN = "
+ + (Long.toHexString((byte)dbl2).equals("0") ? "00" : Long.toHexString((byte)dbl2)
+ .substring(6)) + " expected: 00");
+ System.out.println("(short) Double.NaN = "
+ + (Integer.toHexString((short)dbl2).equals("0") ? "0000" : Integer.toHexString(
+ (short)dbl2).substring(4)) + " expected: 0000");
+ System.out.println("(int) Double.NaN = "
+ + (Integer.toHexString((int)dbl2).equals("0") ? "00000000" : Integer
+ .toHexString((int)dbl2)) + " expected: 00000000");
+ System.out.println("(long) Double.NaN = "
+ + (Long.toHexString((long)dbl2).equals("0") ? "0000000000000000" : Long
+ .toHexString((long)dbl2)) + " expected: 0000000000000000");
+ float fl2 = Float.NaN;
+ System.out.println();
+ System.out.println("(byte) Float.NaN = "
+ + (Long.toHexString((byte)fl2).equals("0") ? "00" : Long.toHexString((byte)fl2)
+ .substring(6)) + " expected: 00");
+ System.out.println("(short) Float.NaN = "
+ + (Integer.toHexString((short)fl2).equals("0") ? "0000" : Integer.toHexString(
+ (short)fl2).substring(4)) + " expected: 0000");
+ System.out.println("(int) Float.NaN = "
+ + (Integer.toHexString((int)fl2).equals("0") ? "00000000" : Integer
+ .toHexString((int)fl2)) + " expected: 00000000");
+ System.out.println("(long) Float.NaN = "
+ + (Long.toHexString((long)fl2).equals("0") ? "0000000000000000" : Long
+ .toHexString((long)fl2)) + " expected: 0000000000000000");
+ double dbl3 = Double.POSITIVE_INFINITY;
+ System.out.println();
+ System.out.println("(byte) Double.POSITIVE_INFINITY = "
+ + (Integer.toHexString((byte)dbl3).equals("0") ? "00" : Integer.toHexString(
+ (byte)dbl3).substring(6)) + " expected: ff");
+ System.out.println("(short) Double.POSITIVE_INFINITY = "
+ + (Integer.toHexString((short)dbl3).equals("0") ? "0000" : Integer.toHexString(
+ (short)dbl3).substring(4)) + " expected: ffff");
+ System.out.println("(int) Double.POSITIVE_INFINITY = "
+ + Integer.toHexString((int)dbl3) + " expected: 7fffffff");
+ System.out.println("(long) Double.POSITIVE_INFINITY = " + Long.toHexString((long)dbl3)
+ + " expected: 7fffffffffffffff");
+ double dbl4 = Double.NEGATIVE_INFINITY;
+ System.out.println();
+ System.out.println("(byte) Double.NEGATIVE_INFINITY = "
+ + (Long.toHexString((byte)dbl4).equals("0") ? " 00" : Long
+ .toHexString((byte)dbl4)) + " expected: 00");
+ System.out.println("(short) Double.NEGATIVE_INFINITY = "
+ + (Integer.toHexString((short)dbl4).equals("0") ? " 0000" : Long
+ .toHexString((short)dbl4)) + " expected: 0000");
+ System.out.println("(int) Double.NEGATIVE_INFINITY = "
+ + Integer.toHexString((int)dbl4) + " expected: 80000000");
+ System.out.println("(long) Double.NEGATIVE_INFINITY = " + Long.toHexString((long)dbl4)
+ + " expected: 8000000000000000");
+ float fl3 = Float.POSITIVE_INFINITY;
+ System.out.println();
+ System.out.println("(byte) Float.POSITIVE_INFINITY = "
+ + (Integer.toHexString((byte)fl3).equals("0") ? "00" : Integer.toHexString(
+ (byte)fl3).substring(6)) + " expected: ff");
+ System.out.println("(short) Float.POSITIVE_INFINITY = "
+ + (Integer.toHexString((short)fl3).equals("0") ? "0000" : Integer.toHexString(
+ (short)fl3).substring(4)) + " expected: ffff");
+ System.out.println("(int) Float.POSITIVE_INFINITY = "
+ + Integer.toHexString((int)fl3) + " expected: 7fffffff");
+ System.out.println("(long) Float.POSITIVE_INFINITY = " + Long.toHexString((long)fl3)
+ + " expected: 7fffffffffffffff");
+ float fl4 = Float.NEGATIVE_INFINITY;
+ System.out.println();
+ System.out.println("(byte) Float.NEGATIVE_INFINITY = "
+ + (Long.toHexString((byte)fl4).equals("0") ? " 00" : Long
+ .toHexString((byte)fl4)) + " expected: 00");
+ System.out.println("(short) Float.NEGATIVE_INFINITY = "
+ + (Integer.toHexString((short)fl4).equals("0") ? " 0000" : Long
+ .toHexString((short)fl4)) + " expected: 0000");
+ System.out.println("(int) Float.NEGATIVE_INFINITY = "
+ + Integer.toHexString((int)fl4) + " expected: 80000000");
+ System.out.println("(long) Float.NEGATIVE_INFINITY = " + Long.toHexString((long)fl4)
+ + " expected: 8000000000000000");
+ System.out.println();
+ }
+}
diff --git a/tests/042-new-instance/expected.txt b/tests/042-new-instance/expected.txt
new file mode 100644
index 0000000..53447db
--- /dev/null
+++ b/tests/042-new-instance/expected.txt
@@ -0,0 +1,8 @@
+LocalClass succeeded
+Got expected PackageAccess complaint
+LocalClass3 succeeded
+Got expected InstantationError
+Cons LocalClass failed as expected
+Cons LocalClass2 succeeded
+Cons got expected PackageAccess complaint
+Cons got expected InstantationException
diff --git a/tests/042-new-instance/info.txt b/tests/042-new-instance/info.txt
new file mode 100644
index 0000000..49c9e02
--- /dev/null
+++ b/tests/042-new-instance/info.txt
@@ -0,0 +1,2 @@
+Test various permutations of Class.newInstance and Constructor.newInstance,
+looking for correct handling of access rights and abstract classes.
diff --git a/tests/042-new-instance/src/Main.java b/tests/042-new-instance/src/Main.java
new file mode 100644
index 0000000..8faef13
--- /dev/null
+++ b/tests/042-new-instance/src/Main.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Constructor;
+
+import java.lang.reflect.Constructor;
+
+/**
+ * Test instance creation.
+ */
+public class Main {
+ public static void main(String[] args) {
+ testClassNewInstance();
+ testConstructorNewInstance();
+ }
+
+ /**
+ * Tests Class.newInstance().
+ */
+ static void testClassNewInstance() {
+ // should succeed
+ try {
+ Class c = Class.forName("LocalClass");
+ Object obj = c.newInstance();
+ System.out.println("LocalClass succeeded");
+ } catch (Exception ex) {
+ System.err.println("LocalClass failed");
+ ex.printStackTrace();
+ }
+
+ // should fail
+ try {
+ Class c = Class.forName("otherpackage.PackageAccess");
+ Object obj = c.newInstance();
+ System.err.println("ERROR: PackageAccess succeeded unexpectedly");
+ } catch (IllegalAccessException iae) {
+ System.out.println("Got expected PackageAccess complaint");
+ } catch (Exception ex) {
+ System.err.println("Got unexpected PackageAccess failure");
+ ex.printStackTrace();
+ }
+
+ LocalClass3.main();
+
+ try {
+ MaybeAbstract ma = new MaybeAbstract();
+ System.err.println("ERROR: MaybeAbstract succeeded unexpectedly");
+ } catch (InstantiationError ie) {
+ System.out.println("Got expected InstantationError");
+ } catch (Exception ex) {
+ System.err.println("Got unexpected MaybeAbstract failure");
+ }
+ }
+
+ /**
+ * Tests Constructor.newInstance().
+ */
+ static void testConstructorNewInstance() {
+ // should fail -- getConstructor only returns public constructors
+ try {
+ Class c = Class.forName("LocalClass");
+ Constructor cons = c.getConstructor(new Class[0] /*(Class[])null*/);
+ System.err.println("Cons LocalClass succeeded unexpectedly");
+ } catch (NoSuchMethodException nsme) {
+ System.out.println("Cons LocalClass failed as expected");
+ } catch (Exception ex) {
+ System.err.println("Cons LocalClass failed strangely");
+ ex.printStackTrace();
+ }
+
+ // should succeed
+ try {
+ Class c = Class.forName("LocalClass2");
+ Constructor cons = c.getConstructor((Class[]) null);
+ Object obj = cons.newInstance();
+ System.out.println("Cons LocalClass2 succeeded");
+ } catch (Exception ex) {
+ System.err.println("Cons LocalClass2 failed");
+ ex.printStackTrace();
+ }
+
+ // should fail
+ try {
+ Class c = Class.forName("otherpackage.PackageAccess");
+ Constructor cons = c.getConstructor(new Class[0] /*(Class[])null*/);
+ System.err.println("ERROR: Cons PackageAccess succeeded unexpectedly");
+ } catch (NoSuchMethodException nsme) {
+ System.out.println("Cons got expected PackageAccess complaint");
+ } catch (Exception ex) {
+ System.err.println("Cons got unexpected PackageAccess failure");
+ ex.printStackTrace();
+ }
+
+ // should fail
+ try {
+ Class c = Class.forName("MaybeAbstract");
+ Constructor cons = c.getConstructor(new Class[0] /*(Class[])null*/);
+ Object obj = cons.newInstance();
+ System.err.println("ERROR: Cons MaybeAbstract succeeded unexpectedly");
+ } catch (InstantiationException ie) {
+ // note InstantiationException vs. InstantiationError
+ System.out.println("Cons got expected InstantationException");
+ } catch (Exception ex) {
+ System.err.println("Cons got unexpected MaybeAbstract failure");
+ ex.printStackTrace();
+ }
+ }
+}
+
+class LocalClass {
+ // this class has a default constructor with package visibility
+}
+
+class LocalClass2 {
+ public LocalClass2() {}
+}
+
+
+class LocalClass3 {
+ public static void main() {
+ try {
+ CC.newInstance();
+ System.out.println("LocalClass3 succeeded");
+ } catch (Exception ex) {
+ System.err.println("Got unexpected LocalClass3 failure");
+ ex.printStackTrace();
+ }
+ }
+
+ static class CC {
+ private CC() {}
+
+ static Object newInstance() {
+ try {
+ Class c = CC.class;
+ return c.newInstance();
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ return null;
+ }
+ }
+ }
+}
diff --git a/tests/042-new-instance/src/MaybeAbstract.java b/tests/042-new-instance/src/MaybeAbstract.java
new file mode 100644
index 0000000..6d3b05b
--- /dev/null
+++ b/tests/042-new-instance/src/MaybeAbstract.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public /*abstract*/ class MaybeAbstract {
+ public MaybeAbstract() {}
+ int foo() { return 0; }
+}
diff --git a/tests/042-new-instance/src/otherpackage/PackageAccess.java b/tests/042-new-instance/src/otherpackage/PackageAccess.java
new file mode 100644
index 0000000..0749d67
--- /dev/null
+++ b/tests/042-new-instance/src/otherpackage/PackageAccess.java
@@ -0,0 +1,6 @@
+package otherpackage;
+
+class PackageAccess {
+ /*package*/ PackageAccess() {
+ }
+}
diff --git a/tests/042-new-instance/src2/MaybeAbstract.java b/tests/042-new-instance/src2/MaybeAbstract.java
new file mode 100644
index 0000000..8b70a07
--- /dev/null
+++ b/tests/042-new-instance/src2/MaybeAbstract.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public abstract class MaybeAbstract {
+ public MaybeAbstract() {}
+ int foo() { return 0; }
+}
diff --git a/tests/043-privates/expected.txt b/tests/043-privates/expected.txt
new file mode 100644
index 0000000..2779ec7
--- /dev/null
+++ b/tests/043-privates/expected.txt
@@ -0,0 +1,6 @@
+PrivatePackage --> PrivatePackage!
+PrivatePackage --> PrivatePackage!
+PrivatePackage --> PrivatePackage!
+PrivatePackageSub --> PrivatePackageSub!
+PrivatePackage --> PrivatePackage!
+PrivatePackage --> PrivatePackage!
diff --git a/tests/043-privates/info.txt b/tests/043-privates/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/043-privates/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/043-privates/src/Main.java b/tests/043-privates/src/Main.java
new file mode 100644
index 0000000..73b4d79
--- /dev/null
+++ b/tests/043-privates/src/Main.java
@@ -0,0 +1,45 @@
+// Copyright 2007 The Android Open Source Project
+
+/**
+ * Make sure private methods don't inherit.
+ */
+public class Main {
+ public static void main(String args[]) {
+ PrivatePackage inst1 = new PrivatePackage();
+ PrivatePackage inst2 = new PrivatePackageSub();
+ PrivatePackageSub inst3 = new PrivatePackageSub();
+
+ System.out.println("PrivatePackage --> " + inst1.getStr());
+ System.out.println("PrivatePackage --> " + inst2.getStr());
+ System.out.println("PrivatePackage --> " + inst3.getStr());
+ System.out.println("PrivatePackageSub --> " + inst3.getStrSub());
+
+ inst1.stretchTest();
+ }
+}
+
+class PrivatePackage {
+ public String getStr() {
+ return privGetStr();
+ }
+
+ private String privGetStr() {
+ return "PrivatePackage!";
+ }
+
+ public void stretchTest() {
+ PrivatePackage inst = new PrivatePackageSub();
+ System.out.println("PrivatePackage --> " + inst.getStr());
+ System.out.println("PrivatePackage --> " + inst.privGetStr());
+ }
+}
+
+class PrivatePackageSub extends PrivatePackage {
+ public String getStrSub() {
+ return privGetStr();
+ }
+
+ private String privGetStr() {
+ return "PrivatePackageSub!";
+ }
+}
diff --git a/tests/044-proxy/expected.txt b/tests/044-proxy/expected.txt
new file mode 100644
index 0000000..69a94f2
--- /dev/null
+++ b/tests/044-proxy/expected.txt
@@ -0,0 +1,80 @@
+Invoke public abstract void Shapes.circle(int)
+ 0: 3
+--- circle 3
+Success: method circle res=null
+Invoke public abstract int Quads.rectangle(int,int)
+ 0: 10
+ 1: 20
+--- rectangle 10,20
+Success: method rectangle res=4
+Invoke public abstract java.lang.String Shapes.blob()
+ (no args)
+--- blob
+Success: method blob res=mix
+Invoke public abstract int Quads.rectangle(int,int)
+ 0: 15
+ 1: 25
+--- rectangle 15,25
+Success: method rectangle res=4
+Invoke public abstract int Quads.trapezoid(int,double,int)
+ 0: 6
+ 1: 81.18
+ 2: 4
+--- trap 6,4,81.18
+Success: method trapezoid res=8
+Invoke public abstract int Colors.red(float)
+ 0: 1.0
+--- red 1.0
+Success: method red res=0
+Invoke public abstract double Colors.blue(int)
+ 0: 777
+--- blue 777
+Success: method blue res=2.54
+Invoke public abstract int Colors.mauve(java.lang.String)
+ 0: sorry
+--- mauve sorry
+Success: method mauve res=3
+Invoke public abstract java.lang.String Shapes.blob()
+ (no args)
+--- blob
+Success: method blob res=mix
+Invoke public abstract void Shapes.upChuck()
+ (no args)
+Got expected ioobe
+Invoke public abstract void Shapes.upCheck() throws java.lang.InterruptedException
+ (no args)
+Got expected ie
+
+Proxy methods: [public native boolean $Proxy0.equals(java.lang.Object), public native int $Proxy0.hashCode(), public native java.lang.String $Proxy0.toString(), public native int $Proxy0.rectangle(int,int), public native int $Proxy0.square(int,int), public native int $Proxy0.trapezoid(int,double,int), public native java.lang.String $Proxy0.blob(), public native void $Proxy0.circle(int), public native void $Proxy0.upCheck(), public native void $Proxy0.upChuck(), public native double $Proxy0.blue(int), public native R0aa $Proxy0.checkMe(), public native int $Proxy0.green(double), public native int $Proxy0.mauve(java.lang.String), public native int $Proxy0.red(float)]
+Decl annos: []
+Param annos (1) : [[]]
+Proxy fields: [private static [[Ljava.lang.Throwable; $Proxy0.throws]
+Dupe threw expected exception
+Clash threw expected exception
+Clash2 threw expected exception
+Clash3 threw expected exception
+Clash4 threw expected exception
+Invoke public abstract void InterfaceW1.throwFunky()
+ (no args)
+Got expected UTE
+Invoke public abstract void InterfaceW1.throwFunky2() throws BaseException,java.lang.NoSuchMethodException,java.io.IOException
+ (no args)
+Got expected IOE
+Invoke public abstract void InterfaceW1.throwFunky2() throws BaseException,java.lang.NoSuchMethodException,java.io.IOException
+ (no args)
+Got expected IOE
+Invoke public abstract void InterfaceW1.throwException() throws BaseException
+ (no args)
+Got expected UTE
+Invoke public abstract void InterfaceW1.throwBase() throws BaseException
+ (no args)
+Got expected UTE
+Invoke public abstract void InterfaceW1.throwSub() throws BaseException
+ (no args)
+Got expected exception
+Invoke public abstract void InterfaceW1.throwSubSub() throws BaseException
+ (no args)
+Got expected exception
+Invoke public abstract void InterfaceW1.bothThrowBase() throws BaseException,SubException,SubSubException
+ (no args)
+Got expected exception
diff --git a/tests/044-proxy/info.txt b/tests/044-proxy/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/044-proxy/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/044-proxy/src/BasicTest.java b/tests/044-proxy/src/BasicTest.java
new file mode 100644
index 0000000..2a453c4
--- /dev/null
+++ b/tests/044-proxy/src/BasicTest.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Arrays;
+
+/**
+ * Do some basic tests.
+ */
+public class BasicTest {
+
+ public static void main(String[] args) {
+ Mix proxyMe = new Mix();
+ Object proxy = createProxy(proxyMe);
+
+ if (!Proxy.isProxyClass(proxy.getClass()))
+ System.err.println("not a proxy class?");
+ if (Proxy.getInvocationHandler(proxy) == null)
+ System.err.println("ERROR: Proxy.getInvocationHandler is null");
+
+ /* take it for a spin; verifies instanceof constraint */
+ Shapes shapes = (Shapes) proxy;
+ shapes.circle(3);
+ shapes.rectangle(10, 20);
+ shapes.blob();
+ Quads quads = (Quads) proxy;
+ quads.rectangle(15, 25);
+ quads.trapezoid(6, 81.18, 4);
+ Colors colors = (Colors) proxy;
+ colors.red(1.0f);
+ colors.blue(777);
+ colors.mauve("sorry");
+ colors.blob();
+
+ try {
+ shapes.upChuck();
+ System.out.println("Didn't get expected exception");
+ } catch (IndexOutOfBoundsException ioobe) {
+ System.out.println("Got expected ioobe");
+ }
+ try {
+ shapes.upCheck();
+ System.out.println("Didn't get expected exception");
+ } catch (InterruptedException ie) {
+ System.out.println("Got expected ie");
+ }
+
+ /*
+ * Exercise annotations on Proxy classes. This is mostly to ensure
+ * that annotation calls work correctly on generated classes.
+ */
+ System.out.println("");
+ Method[] methods = proxy.getClass().getDeclaredMethods();
+ System.out.println("Proxy methods: " + Arrays.deepToString(methods));
+ Method meth = methods[methods.length -1];
+ System.out.println("Decl annos: " + Arrays.deepToString(meth.getDeclaredAnnotations()));
+ Annotation[][] paramAnnos = meth.getParameterAnnotations();
+ System.out.println("Param annos (" + paramAnnos.length + ") : "
+ + Arrays.deepToString(paramAnnos));
+ Field[] fields = proxy.getClass().getDeclaredFields();
+ System.out.println("Proxy fields: " + Arrays.deepToString(fields));
+ }
+
+ static Object createProxy(Object proxyMe) {
+ /* declare an object that will handle the method calls */
+ InvocationHandler handler = new MyInvocationHandler(proxyMe);
+
+ /* create the proxy class */
+ Class proxyClass = Proxy.getProxyClass(Shapes.class.getClassLoader(),
+ new Class[] { Quads.class, Colors.class });
+
+ /* create a proxy object, passing the handler object in */
+ Object proxy = null;
+ try {
+ Constructor<Class> cons;
+ cons = proxyClass.getConstructor(
+ new Class[] { InvocationHandler.class });
+ //System.out.println("Constructor is " + cons);
+ proxy = cons.newInstance(new Object[] { handler });
+ } catch (NoSuchMethodException nsme) {
+ System.err.println("failed: " + nsme);
+ } catch (InstantiationException ie) {
+ System.err.println("failed: " + ie);
+ } catch (IllegalAccessException ie) {
+ System.err.println("failed: " + ie);
+ } catch (InvocationTargetException ite) {
+ System.err.println("failed: " + ite);
+ }
+
+ return proxy;
+ }
+}
+
+/*
+ * Some interfaces.
+ */
+interface Shapes {
+ public void circle(int r);
+ public int rectangle(int x, int y);
+
+ public String blob();
+
+ public R0base checkMe();
+ public void upChuck();
+ public void upCheck() throws InterruptedException;
+}
+
+interface Quads extends Shapes {
+ public int rectangle(int x, int y);
+ public int square(int x, int y);
+ public int trapezoid(int x, double off, int y);
+
+ public R0a checkMe();
+}
+
+/*
+ * More interfaces.
+ */
+interface Colors {
+ public int red(float howRed);
+ public int green(double howGreen);
+ public double blue(int howBlue);
+ public int mauve(String apology);
+
+ public String blob();
+
+ public R0aa checkMe();
+}
+
+/*
+ * Some return types.
+ */
+class R0base { int mBlah; }
+class R0a extends R0base { int mBlah_a; }
+class R0aa extends R0a { int mBlah_aa; }
+
+
+/*
+ * A class that implements them all.
+ */
+class Mix implements Quads, Colors {
+ public void circle(int r) {
+ System.out.println("--- circle " + r);
+ }
+ public int rectangle(int x, int y) {
+ System.out.println("--- rectangle " + x + "," + y);
+ return 4;
+ }
+ public int square(int x, int y) {
+ System.out.println("--- square " + x + "," + y);
+ return 4;
+ }
+ public int trapezoid(int x, double off, int y) {
+ System.out.println("--- trap " + x + "," + y + "," + off);
+ return 8;
+ }
+ public String blob() {
+ System.out.println("--- blob");
+ return "mix";
+ }
+
+ public int red(float howRed) {
+ System.out.println("--- red " + howRed);
+ return 0;
+ }
+ public int green(double howGreen) {
+ System.out.println("--- green " + howGreen);
+ return 1;
+ }
+ public double blue(int howBlue) {
+ System.out.println("--- blue " + howBlue);
+ return 2.54;
+ }
+ public int mauve(String apology) {
+ System.out.println("--- mauve " + apology);
+ return 3;
+ }
+
+ public R0aa checkMe() {
+ return null;
+ }
+ public void upChuck() {
+ throw new IndexOutOfBoundsException("upchuck");
+ }
+ public void upCheck() throws InterruptedException {
+ throw new InterruptedException("upcheck");
+ }
+}
+
+/*
+ * Invocation handler, defining the implementation of the proxy functions.
+ */
+class MyInvocationHandler implements InvocationHandler {
+ Object mObj;
+
+ public MyInvocationHandler(Object obj) {
+ mObj = obj;
+ }
+
+ /*
+ * This is called when anything gets invoked in the proxy object.
+ */
+ public Object invoke(Object proxy, Method method, Object[] args)
+ throws Throwable {
+
+ Object result = null;
+
+ // Trap Object calls. This is important here to avoid a recursive
+ // invocation of toString() in the print statements below.
+ if (method.getDeclaringClass() == java.lang.Object.class) {
+ //System.out.println("!!! object " + method.getName());
+ if (method.getName().equals("toString"))
+ return super.toString();
+ else if (method.getName().equals("hashCode"))
+ return Integer.valueOf(super.hashCode());
+ else if (method.getName().equals("equals"))
+ return Boolean.valueOf(super.equals(args[0]));
+ else
+ throw new RuntimeException("huh?");
+ }
+
+ System.out.println("Invoke " + method);
+ if (args == null || args.length == 0) {
+ System.out.println(" (no args)");
+ } else {
+ for (int i = 0; i < args.length; i++)
+ System.out.println(" " + i + ": " + args[i]);
+ }
+
+ try {
+ if (true)
+ result = method.invoke(mObj, args);
+ else
+ result = -1;
+ System.out.println("Success: method " + method.getName()
+ + " res=" + result);
+ } catch (InvocationTargetException ite) {
+ throw ite.getTargetException();
+ } catch (IllegalAccessException iae) {
+ throw new RuntimeException(iae);
+ }
+ return result;
+ }
+}
diff --git a/tests/044-proxy/src/Clash.java b/tests/044-proxy/src/Clash.java
new file mode 100644
index 0000000..adeffdc
--- /dev/null
+++ b/tests/044-proxy/src/Clash.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+/*
+ * Try to instantiate a proxy class with interfaces that have conflicting
+ * duplicate methods (primitive vs. object).
+ */
+public class Clash {
+ public static void main(String[] args) {
+ InvocationHandler handler = new ClashInvocationHandler();
+
+ /* try passing in the same interface twice */
+ try {
+ Proxy.newProxyInstance(Clash.class.getClassLoader(),
+ new Class[] { Interface1A.class, Interface1A.class },
+ handler);
+ System.err.println("Dupe did not throw expected exception");
+ } catch (IllegalArgumentException iae) {
+ System.out.println("Dupe threw expected exception");
+ }
+
+ try {
+ Proxy.newProxyInstance(Clash.class.getClassLoader(),
+ new Class[] { Interface1A.class, Interface1B.class },
+ handler);
+ System.err.println("Clash did not throw expected exception");
+ } catch (IllegalArgumentException iae) {
+ System.out.println("Clash threw expected exception");
+ }
+ }
+}
+
+interface Interface1A {
+ public int thisIsOkay();
+
+ public float thisIsTrouble();
+}
+
+interface Interface1B {
+ public int thisIsOkay();
+
+ public Object thisIsTrouble();
+}
+
+class ClashInvocationHandler implements InvocationHandler {
+ /* don't really need to do anything -- should never get this far */
+ public Object invoke(Object proxy, Method method, Object[] args)
+ throws Throwable {
+
+ return null;
+ }
+}
diff --git a/tests/044-proxy/src/Clash2.java b/tests/044-proxy/src/Clash2.java
new file mode 100644
index 0000000..2a384f4
--- /dev/null
+++ b/tests/044-proxy/src/Clash2.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+/*
+ * Try to instantiate a proxy class with interfaces that have conflicting
+ * duplicate methods (primitive types).
+ */
+public class Clash2 {
+ public static void main(String[] args) {
+ InvocationHandler handler = new Clash2InvocationHandler();
+
+ try {
+ Proxy.newProxyInstance(Clash.class.getClassLoader(),
+ new Class[] { Interface2A.class, Interface2B.class },
+ handler);
+ System.err.println("Clash2 did not throw expected exception");
+ } catch (IllegalArgumentException iae) {
+ System.out.println("Clash2 threw expected exception");
+ }
+ }
+}
+
+interface Interface2A {
+ public int thisIsOkay();
+
+ public int thisIsTrouble();
+}
+
+interface Interface2B {
+ public int thisIsOkay();
+
+ public short thisIsTrouble();
+}
+
+class Clash2InvocationHandler implements InvocationHandler {
+ /* don't really need to do anything -- should never get this far */
+ public Object invoke(Object proxy, Method method, Object[] args)
+ throws Throwable {
+
+ return null;
+ }
+}
diff --git a/tests/044-proxy/src/Clash3.java b/tests/044-proxy/src/Clash3.java
new file mode 100644
index 0000000..6d6f2f2
--- /dev/null
+++ b/tests/044-proxy/src/Clash3.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+/*
+ * Try to instantiate a proxy class with interfaces that have conflicting
+ * duplicate methods (type tree with interface).
+ */
+public class Clash3 {
+ public static void main(String[] args) {
+ InvocationHandler handler = new Clash3InvocationHandler();
+
+ try {
+ Proxy.newProxyInstance(Clash.class.getClassLoader(),
+ new Class[] {
+ Interface3a.class,
+ Interface3base.class,
+ Interface3aa.class,
+ Interface3b.class },
+ handler);
+ System.err.println("Clash3 did not throw expected exception");
+ } catch (IllegalArgumentException iae) {
+ System.out.println("Clash3 threw expected exception");
+ }
+ }
+}
+
+class R3base implements I3 { int mBlah; public void x() {} }
+class R3a extends R3base { int mBlah_a; }
+class R3aa extends R3a { int mBlah_aa; }
+class R3b implements I3 { int mBlah_b; public void x() {} }
+
+interface I3 {
+ void x();
+}
+
+interface Interface3base {
+ public R3base thisIsTrouble();
+}
+
+interface Interface3a {
+ public R3a thisIsTrouble();
+}
+interface Interface3aa {
+ public R3aa thisIsTrouble();
+}
+interface Interface3b {
+ public R3b thisIsTrouble();
+}
+
+class Clash3InvocationHandler implements InvocationHandler {
+ /* don't really need to do anything -- should never get this far */
+ public Object invoke(Object proxy, Method method, Object[] args)
+ throws Throwable {
+
+ return null;
+ }
+}
diff --git a/tests/044-proxy/src/Clash4.java b/tests/044-proxy/src/Clash4.java
new file mode 100644
index 0000000..1bfb37f
--- /dev/null
+++ b/tests/044-proxy/src/Clash4.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+/*
+ * Try to instantiate a proxy class with interfaces that have conflicting
+ * duplicate methods (tree of types).
+ */
+public class Clash4 {
+ public static void main(String[] args) {
+ InvocationHandler handler = new Clash4InvocationHandler();
+
+ try {
+ Proxy.newProxyInstance(Clash.class.getClassLoader(),
+ new Class[] {
+ Interface4a.class,
+ Interface4aa.class,
+ Interface4base.class,
+ Interface4b.class,
+ Interface4bb.class },
+ handler);
+ System.err.println("Clash4 did not throw expected exception");
+ } catch (IllegalArgumentException iae) {
+ System.out.println("Clash4 threw expected exception");
+ //System.out.println(iae);
+ }
+ }
+}
+
+class R4base { int mBlah; }
+class R4a extends R4base { int mBlah_a; }
+class R4aa extends R4a { int mBlah_aa; }
+class R4b extends R4base { int mBlah_b; }
+class R4bb extends R4b { int mBlah_bb; }
+
+interface Interface4base {
+ public R4base thisIsTrouble();
+}
+
+interface Interface4a {
+ public R4a thisIsTrouble();
+}
+interface Interface4aa {
+ public R4aa thisIsTrouble();
+}
+interface Interface4b {
+ public R4b thisIsTrouble();
+}
+interface Interface4bb {
+ public R4bb thisIsTrouble();
+}
+
+class Clash4InvocationHandler implements InvocationHandler {
+ /* don't really need to do anything -- should never get this far */
+ public Object invoke(Object proxy, Method method, Object[] args)
+ throws Throwable {
+
+ return null;
+ }
+}
diff --git a/tests/044-proxy/src/Main.java b/tests/044-proxy/src/Main.java
new file mode 100644
index 0000000..01926af
--- /dev/null
+++ b/tests/044-proxy/src/Main.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Test java.lang.reflect.Proxy
+ */
+public class Main {
+ public static void main(String[] args) {
+ BasicTest.main(null);
+ Clash.main(null);
+ Clash2.main(null);
+ Clash3.main(null);
+ Clash4.main(null);
+ WrappedThrow.main(null);
+ }
+}
diff --git a/tests/044-proxy/src/WrappedThrow.java b/tests/044-proxy/src/WrappedThrow.java
new file mode 100644
index 0000000..27ae84e
--- /dev/null
+++ b/tests/044-proxy/src/WrappedThrow.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.lang.reflect.UndeclaredThrowableException;
+
+/*
+ * Create a Proxy class that blah.
+ */
+public class WrappedThrow {
+ public static void main(String[] args) {
+ WTMix mix = new WTMix();
+ InvocationHandler handler = new WTInvocationHandler(mix);
+ Object proxy;
+
+ try {
+ proxy = Proxy.newProxyInstance(WrappedThrow.class.getClassLoader(),
+ new Class[] { InterfaceW1.class, InterfaceW2.class },
+ handler);
+ } catch (IllegalArgumentException iae) {
+ System.out.println("WT init failed");
+ return;
+ }
+
+ InterfaceW1 if1 = (InterfaceW1) proxy;
+ InterfaceW2 if2 = (InterfaceW2) proxy;
+ try {
+ if1.throwFunky();
+ System.err.println("No exception thrown");
+ } catch (UndeclaredThrowableException ute) {
+ System.out.println("Got expected UTE");
+ } catch (Throwable t) {
+ System.err.println("Got unexpected exception: " + t);
+ }
+
+ try {
+ if1.throwFunky2();
+ System.err.println("No exception thrown");
+ } catch (IOException ioe) {
+ System.out.println("Got expected IOE");
+ } catch (Throwable t) {
+ System.err.println("Got unexpected exception: " + t);
+ }
+
+ try {
+ if2.throwFunky2();
+ System.err.println("No exception thrown");
+ } catch (IOException ioe) {
+ System.out.println("Got expected IOE");
+ } catch (Throwable t) {
+ System.err.println("Got unexpected exception: " + t);
+ }
+
+ /*
+ * Throw exceptions, walking down the hierarchy.
+ */
+ try {
+ if1.throwException();
+ System.err.println("No exception thrown");
+ } catch (UndeclaredThrowableException ute) {
+ System.out.println("Got expected UTE");
+ } catch (Throwable t) {
+ System.err.println("Got unexpected exception: " + t);
+ }
+
+ try {
+ if1.throwBase();
+ System.err.println("No exception thrown");
+ } catch (UndeclaredThrowableException ute) {
+ System.out.println("Got expected UTE");
+ } catch (Throwable t) {
+ System.err.println("Got unexpected exception: " + t);
+ }
+
+ try {
+ if2.throwSub();
+ System.err.println("No exception thrown");
+ } catch (SubException se) {
+ System.out.println("Got expected exception");
+ } catch (Throwable t) {
+ System.err.println("Got unexpected exception: " + t);
+ }
+
+ try {
+ if2.throwSubSub();
+ System.err.println("No exception thrown");
+ } catch (SubException se) {
+ System.out.println("Got expected exception");
+ } catch (Throwable t) {
+ System.err.println("Got unexpected exception: " + t);
+ }
+
+ /*
+ * Make sure that, if the class explicitly allows the base
+ * class of an exception, that we still allow it.
+ */
+ try {
+ if1.bothThrowBase();
+ System.err.println("No exception thrown");
+ } catch (BaseException se) {
+ System.out.println("Got expected exception");
+ } catch (Throwable t) {
+ System.err.println("Got unexpected exception: " + t);
+ }
+ }
+}
+
+class BaseException extends Exception {}
+class SubException extends BaseException {}
+class SubSubException extends SubException {}
+
+interface InterfaceW1 {
+ public void throwFunky();
+
+ public void throwFunky2() throws BaseException,
+ NoSuchMethodException, IOException;
+
+ public void throwException() throws BaseException;
+ public void throwBase() throws BaseException;
+ public void throwSub() throws BaseException;
+ public void throwSubSub() throws BaseException;
+
+ public void bothThrowBase() throws BaseException, SubException, SubSubException;
+}
+
+interface InterfaceW2 {
+ public void throwFunky2() throws InterruptedException,
+ NoSuchMethodException, IOException;
+
+ public void throwException() throws SubException;
+ public void throwBase() throws SubException;
+ public void throwSub() throws SubException;
+ public void throwSubSub() throws SubException;
+
+ public void bothThrowBase() throws SubException, BaseException, SubSubException;
+}
+
+/**
+ * Implement all of the proxied interfaces.
+ */
+class WTMix implements InterfaceW1, InterfaceW2 {
+ public int dastardlyDeed() throws SubException {
+ System.out.println("Throwing SubException");
+ throw new SubException();
+ }
+
+ /* these don't actually get called; they just cause exceptions */
+ public void throwFunky() {}
+ public void throwFunky2() {}
+ public void throwException() throws SubException {}
+ public void throwBase() throws SubException {}
+ public void throwSub() throws SubException {}
+ public void throwSubSub() throws SubException {}
+
+ public void bothThrowBase() throws BaseException, SubException {}
+}
+
+/**
+ * Invocation handler for our proxy class.
+ */
+class WTInvocationHandler implements InvocationHandler {
+ private Object mObj;
+
+ public WTInvocationHandler(Object obj) {
+ mObj = obj;
+ }
+
+ /*
+ * This is called when anything gets invoked in the proxy object.
+ */
+ public Object invoke(Object proxy, Method method, Object[] args)
+ throws Throwable {
+
+ Object result = null;
+
+ // Trap Object calls. This is important here to avoid a recursive
+ // invocation of toString() in the print statements below.
+ if (method.getDeclaringClass() == java.lang.Object.class) {
+ //System.out.println("!!! object " + method.getName());
+ if (method.getName().equals("toString"))
+ return super.toString();
+ else if (method.getName().equals("hashCode"))
+ return Integer.valueOf(super.hashCode());
+ else if (method.getName().equals("equals"))
+ return Boolean.valueOf(super.equals(args[0]));
+ else
+ throw new RuntimeException("huh?");
+ }
+
+ System.out.println("Invoke " + method);
+ if (args == null || args.length == 0) {
+ System.out.println(" (no args)");
+ } else {
+ for (int i = 0; i < args.length; i++)
+ System.out.println(" " + i + ": " + args[i]);
+ }
+
+ try {
+ if (method.getName().equals("throwFunky"))
+ throw new InterruptedException("fake");
+ if (method.getName().equals("throwFunky2"))
+ throw new IOException("fake2");
+ if (method.getName().equals("throwException"))
+ throw new Exception();
+ if (method.getName().equals("throwBase"))
+ throw new BaseException();
+ if (method.getName().equals("throwSub"))
+ throw new SubException();
+ if (method.getName().equals("throwSubSub"))
+ throw new SubSubException();
+ if (method.getName().equals("bothThrowBase"))
+ throw new BaseException();
+
+ if (true)
+ result = method.invoke(mObj, args);
+ else
+ result = -1;
+ System.out.println("Success: method " + method.getName()
+ + " res=" + result);
+ } catch (InvocationTargetException ite) {
+ throw ite.getTargetException();
+ } catch (IllegalAccessException iae) {
+ throw new RuntimeException(iae);
+ }
+ return result;
+ }
+}
diff --git a/tests/045-reflect-array/expected.txt b/tests/045-reflect-array/expected.txt
new file mode 100644
index 0000000..5c609b5
--- /dev/null
+++ b/tests/045-reflect-array/expected.txt
@@ -0,0 +1,6 @@
+ReflectArrayTest.testSingleInt passed
+ReflectArrayTest.testSingle passed
+ReflectArrayTest.testMultiInt passed
+zero one two ++
+ReflectArrayTest.testMulti passed
+ReflectArrayTest passed
diff --git a/tests/045-reflect-array/info.txt b/tests/045-reflect-array/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/045-reflect-array/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/045-reflect-array/src/Main.java b/tests/045-reflect-array/src/Main.java
new file mode 100644
index 0000000..c70e291
--- /dev/null
+++ b/tests/045-reflect-array/src/Main.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2006 The Android Open Source Project
+ */
+
+import java.lang.reflect.Array;
+
+/**
+ * Test java.lang.reflect.Array.
+ */
+public class Main {
+ public static void main(String[] args) {
+ testSingleInt();
+ testSingle();
+ testMultiInt();
+ testMulti();
+
+ System.out.println("ReflectArrayTest passed");
+ }
+
+ static void testSingleInt() {
+ Object intArray;
+
+ intArray = Array.newInstance(Integer.TYPE, 2);
+
+ int[] array = (int[]) intArray;
+ array[0] = 5;
+ Array.setInt(intArray, 1, 6);
+
+ if (Array.getInt(intArray, 0) != 5)
+ throw new RuntimeException();
+ if (array[1] != 6)
+ throw new RuntimeException();
+ try {
+ array[2] = 27;
+ throw new RuntimeException("store should have failed");
+ }
+ catch (ArrayIndexOutOfBoundsException abe) {
+ }
+ if (array.length != Array.getLength(intArray) ||
+ array.length != 2)
+ {
+ throw new RuntimeException("bad len");
+ }
+
+ int[][] wrongArray;
+ try {
+ wrongArray = (int[][]) intArray;
+ throw new RuntimeException("cast should have failed");
+ }
+ catch (ClassCastException cce) {
+ }
+
+ intArray = Array.newInstance(Integer.TYPE, 0);
+ if (Array.getLength(intArray) != 0)
+ throw new RuntimeException();
+ System.out.println("ReflectArrayTest.testSingleInt passed");
+ }
+
+ static void testSingle() {
+ Object strArray;
+
+ strArray = Array.newInstance(String.class, 2);
+
+ String[] array = (String[]) strArray;
+ array[0] = "entry zero";
+ Array.set(strArray, 1, "entry one");
+
+ //System.out.println("array: " + array);
+
+ if (!"entry zero".equals(Array.get(strArray, 0)))
+ throw new RuntimeException();
+ if (!"entry one".equals(array[1]))
+ throw new RuntimeException();
+
+ if (array.length != Array.getLength(strArray) ||
+ array.length != 2)
+ {
+ throw new RuntimeException("bad len");
+ }
+ System.out.println("ReflectArrayTest.testSingle passed");
+ }
+
+ static void testMultiInt() {
+ Object intIntIntArray;
+ int[] dimensions = { 3, 2, 1 };
+
+ intIntIntArray = Array.newInstance(Integer.TYPE, dimensions);
+ int[][][] array3 = (int[][][]) intIntIntArray;
+
+ array3[0][0][0] = 123; // trouble
+ array3[2][1][0] = 456;
+
+ try {
+ array3[2][1][1] = 768;
+ throw new RuntimeException("store should have failed");
+ }
+ catch (ArrayIndexOutOfBoundsException abe) {
+ }
+ System.out.println("ReflectArrayTest.testMultiInt passed");
+ }
+
+ static void testMulti() {
+ Object strStrStrArray;
+ int[] dimensions = { 1, 2, 3 };
+
+ strStrStrArray = Array.newInstance(String.class, dimensions);
+ String[][][] array3 = (String[][][]) strStrStrArray;
+
+ array3[0][0][0] = "zero zero zero";
+ array3[0][1][2] = "zero one two";
+
+ try {
+ array3[1][0][0] = "bad store";
+ throw new RuntimeException("store should have failed");
+ }
+ catch (ArrayIndexOutOfBoundsException abe) {
+ }
+
+ try {
+ String[][] array2 = (String[][]) strStrStrArray;
+ throw new RuntimeException("expecting bad cast");
+ }
+ catch (ClassCastException cce) {
+ }
+
+ String[] strar = new String[4];
+ strar[2] = "zero one two ++";
+ array3[0][1] = strar;
+ System.out.println(array3[0][1][2]);
+ //System.out.println("array3: " + array3);
+
+
+ int[] dimensions2 = { 1, 2 };
+ strStrStrArray = Array.newInstance(String[].class, dimensions2);
+ array3 = (String[][][]) strStrStrArray;
+
+ array3[0][1] = new String[3];
+ array3[0][1][2] = "zero one two";
+ try {
+ array3[1][0][0] = "bad store";
+ throw new RuntimeException("store should have failed");
+ }
+ catch (ArrayIndexOutOfBoundsException abe) {
+ }
+ System.out.println("ReflectArrayTest.testMulti passed");
+ }
+}
diff --git a/tests/046-reflect/expected.txt b/tests/046-reflect/expected.txt
new file mode 100644
index 0000000..3be8d1c
--- /dev/null
+++ b/tests/046-reflect/expected.txt
@@ -0,0 +1,97 @@
+Method name is myMethod
+ Declaring class is Target
+ Arg 0: int
+ Exc 0: java.lang.NullPointerException
+ Exc 1: java.io.IOException
+ Return type is int
+ Access flags are 0x1
+Method name is myMethod
+ Declaring class is SuperTarget
+ Arg 0: float
+ Return type is int
+ Access flags are 0x1
+Method name is myNoargMethod
+ Declaring class is Target
+ Return type is void
+ Access flags are 0x9
+Method name is myMethod
+ Declaring class is Target
+ Arg 0: [Ljava.lang.String;
+ Arg 1: float
+ Arg 2: char
+ Return type is int
+ Access flags are 0x1
+SuperTarget constructor ()V
+Target constructor ()V
+Before, float is 3.1415925
+myMethod: hi there 3.1415925 Q !
+Result of invoke: 7
+Calling no-arg void-return method
+myNoargMethod ()V
+throwingMethod
+Invoke got expected exception:
+java.lang.reflect.InvocationTargetException
+java.lang.NullPointerException: gratuitous throw!
+
+Field name is string1
+ Declaring class is Target
+ Field type is java.lang.String
+ Access flags are 0x1
+ string1 value is 'hey'
+ ::: hey:yo:there
+ string1 value is now 'a new string'
+ ::: a new string:yo:there
+ got expected illegal obj store exc
+ got the other expected access exc
+ got expected arg exc
+pubLong initial value is 1122334455667788
+pubLong new value is 9988776655443322
+Field name is superInt
+ Declaring class is SuperTarget
+ Field type is int
+ Access flags are 0x1
+ superInt value is 1010101
+ superInt boxed is 1010101
+ superInt value is now 20202
+ superInt value (from short) is now 30303
+ superInt value is now 40404
+ got expected long->int failure
+ got expected long->int failure
+ got expected string->int failure
+ got expected int->short failure
+Field name is superClassInt
+ Declaring class is SuperTarget
+ Field type is int
+ Access flags are 0x9
+ superClassInt value is 1010102
+Field name is staticDouble
+ Declaring class is Target
+ Field type is double
+ Access flags are 0x9
+ staticDoubleVal value is 3.3
+ got expected double->long failure
+as expected: aPrivateInt not found
+Field name is constantString
+ Declaring class is Target
+ Field type is java.lang.String
+ Access flags are 0x19
+ Constant test value is a constant string
+Field name is cantTouchThis
+ Declaring class is Target
+ Field type is int
+ Access flags are 0x11
+ cantTouchThis is 77
+ got expected set-final failure
+
+ cantTouchThis is now 77
+cons modifiers=1
+SuperTarget constructor ()V
+Target constructor (IF)V : ii=7 ff=3.3333
+myMethod (I)I
+ arg=17 anInt=7
+ReflectTest done!
+checkType invoking null
+checkType got expected exception
+got methods
+NoisyInitUser is initializing
+NoisyInit is initializing
diff --git a/tests/046-reflect/info.txt b/tests/046-reflect/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/046-reflect/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/046-reflect/src/Main.java b/tests/046-reflect/src/Main.java
new file mode 100644
index 0000000..399a417
--- /dev/null
+++ b/tests/046-reflect/src/Main.java
@@ -0,0 +1,435 @@
+// Copyright 2006 The Android Open Source Project
+
+import java.lang.reflect.*;
+import java.io.IOException;
+import java.util.Collections;
+
+/**
+ * Reflection test.
+ */
+public class Main {
+ void printMethodInfo(Method meth) {
+ Class[] params, exceptions;
+ int i;
+
+ System.out.println("Method name is " + meth.getName());
+ System.out.println(" Declaring class is "
+ + meth.getDeclaringClass().getName());
+ params = meth.getParameterTypes();
+ for (i = 0; i < params.length; i++)
+ System.out.println(" Arg " + i + ": " + params[i].getName());
+ exceptions = meth.getExceptionTypes();
+ for (i = 0; i < exceptions.length; i++)
+ System.out.println(" Exc " + i + ": " + exceptions[i].getName());
+ System.out.println(" Return type is " + meth.getReturnType().getName());
+ System.out.println(" Access flags are 0x"
+ + Integer.toHexString(meth.getModifiers()));
+ //System.out.println(" GenericStr is " + meth.toGenericString());
+ }
+
+ void printFieldInfo(Field field) {
+ System.out.println("Field name is " + field.getName());
+ System.out.println(" Declaring class is "
+ + field.getDeclaringClass().getName());
+ System.out.println(" Field type is " + field.getType().getName());
+ System.out.println(" Access flags are 0x"
+ + Integer.toHexString(field.getModifiers()));
+ }
+
+ private void showStrings(Target instance)
+ throws NoSuchFieldException, IllegalAccessException {
+
+ Class target = Target.class;
+ String one, two, three, four;
+ Field field = null;
+
+ field = target.getField("string1");
+ one = (String) field.get(instance);
+
+ field = target.getField("string2");
+ two = (String) field.get(instance);
+
+ field = target.getField("string3");
+ three = (String) field.get(instance);
+
+ System.out.println(" ::: " + one + ":" + two + ":" + three);
+ }
+
+ public void run() {
+ Class target = Target.class;
+ Method meth = null;
+ Field field = null;
+ boolean excep;
+
+ try {
+ meth = target.getMethod("myMethod", new Class[] { int.class });
+
+ if (meth.getDeclaringClass() != target)
+ throw new RuntimeException();
+ printMethodInfo(meth);
+
+ meth = target.getMethod("myMethod", new Class[] { float.class });
+ printMethodInfo(meth);
+
+ meth = target.getMethod("myNoargMethod", (Class[]) null);
+ printMethodInfo(meth);
+
+ meth = target.getMethod("myMethod",
+ new Class[] { String[].class, float.class, char.class });
+ printMethodInfo(meth);
+
+ Target instance = new Target();
+ Object[] argList = new Object[] {
+ new String[] { "hi there" },
+ new Float(3.1415926f),
+ new Character('Q')
+ };
+ System.out.println("Before, float is "
+ + ((Float)argList[1]).floatValue());
+
+ Integer boxval;
+ boxval = (Integer) meth.invoke(instance, argList);
+ System.out.println("Result of invoke: " + boxval.intValue());
+
+ System.out.println("Calling no-arg void-return method");
+ meth = target.getMethod("myNoargMethod", (Class[]) null);
+ meth.invoke(instance, (Object[]) null);
+
+ /* try invoking a method that throws an exception */
+ meth = target.getMethod("throwingMethod", (Class[]) null);
+ try {
+ meth.invoke(instance, (Object[]) null);
+ System.out.println("GLITCH: didn't throw");
+ } catch (InvocationTargetException ite) {
+ System.out.println("Invoke got expected exception:");
+ System.out.println(ite.getClass().getName());
+ System.out.println(ite.getCause());
+ }
+ catch (Exception ex) {
+ System.out.println("GLITCH: invoke got wrong exception:");
+ ex.printStackTrace();
+ }
+ System.out.println("");
+
+
+ field = target.getField("string1");
+ if (field.getDeclaringClass() != target)
+ throw new RuntimeException();
+ printFieldInfo(field);
+ String strVal = (String) field.get(instance);
+ System.out.println(" string1 value is '" + strVal + "'");
+
+ showStrings(instance);
+
+ field.set(instance, new String("a new string"));
+ strVal = (String) field.get(instance);
+ System.out.println(" string1 value is now '" + strVal + "'");
+
+ showStrings(instance);
+
+ try {
+ field.set(instance, new Object());
+ System.out.println("WARNING: able to store Object into String");
+ }
+ catch (IllegalArgumentException iae) {
+ System.out.println(" got expected illegal obj store exc");
+ }
+
+
+ try {
+ String four;
+ field = target.getField("string4");
+ four = (String) field.get(instance);
+ System.out.println("WARNING: able to access string4: "
+ + four);
+ }
+ catch (IllegalAccessException iae) {
+ System.out.println(" got expected access exc");
+ }
+ catch (NoSuchFieldException nsfe) {
+ System.out.println(" got the other expected access exc");
+ }
+ try {
+ String three;
+ field = target.getField("string3");
+ three = (String) field.get(this);
+ System.out.println("WARNING: able to get string3 in wrong obj: "
+ + three);
+ }
+ catch (IllegalArgumentException iae) {
+ System.out.println(" got expected arg exc");
+ }
+
+ /*
+ * Try setting a field to null.
+ */
+ String four;
+ field = target.getDeclaredField("string3");
+ field.set(instance, null);
+
+ /*
+ * Do some stuff with long.
+ */
+ long longVal;
+ field = target.getField("pubLong");
+ longVal = field.getLong(instance);
+ System.out.println("pubLong initial value is " +
+ Long.toHexString(longVal));
+ field.setLong(instance, 0x9988776655443322L);
+ longVal = field.getLong(instance);
+ System.out.println("pubLong new value is " +
+ Long.toHexString(longVal));
+
+
+ field = target.getField("superInt");
+ if (field.getDeclaringClass() == target)
+ throw new RuntimeException();
+ printFieldInfo(field);
+ int intVal = field.getInt(instance);
+ System.out.println(" superInt value is " + intVal);
+ Integer boxedIntVal = (Integer) field.get(instance);
+ System.out.println(" superInt boxed is " + boxedIntVal);
+
+ field.set(instance, new Integer(20202));
+ intVal = field.getInt(instance);
+ System.out.println(" superInt value is now " + intVal);
+ field.setShort(instance, (short)30303);
+ intVal = field.getInt(instance);
+ System.out.println(" superInt value (from short) is now " +intVal);
+ field.setInt(instance, 40404);
+ intVal = field.getInt(instance);
+ System.out.println(" superInt value is now " + intVal);
+ try {
+ field.set(instance, new Long(123));
+ System.out.println("FAIL: expected exception not thrown");
+ }
+ catch (IllegalArgumentException iae) {
+ System.out.println(" got expected long->int failure");
+ }
+ try {
+ field.setLong(instance, 123);
+ System.out.println("FAIL: expected exception not thrown");
+ }
+ catch (IllegalArgumentException iae) {
+ System.out.println(" got expected long->int failure");
+ }
+ try {
+ field.set(instance, new String("abc"));
+ System.out.println("FAIL: expected exception not thrown");
+ }
+ catch (IllegalArgumentException iae) {
+ System.out.println(" got expected string->int failure");
+ }
+
+ try {
+ field.getShort(instance);
+ System.out.println("FAIL: expected exception not thrown");
+ }
+ catch (IllegalArgumentException iae) {
+ System.out.println(" got expected int->short failure");
+ }
+
+ field = target.getField("superClassInt");
+ printFieldInfo(field);
+ int superClassIntVal = field.getInt(instance);
+ System.out.println(" superClassInt value is " + superClassIntVal);
+
+ field = target.getField("staticDouble");
+ printFieldInfo(field);
+ double staticDoubleVal = field.getDouble(null);
+ System.out.println(" staticDoubleVal value is " + staticDoubleVal);
+
+ try {
+ field.getLong(instance);
+ System.out.println("FAIL: expected exception not thrown");
+ }
+ catch (IllegalArgumentException iae) {
+ System.out.println(" got expected double->long failure");
+ }
+
+ excep = false;
+ try {
+ field = target.getField("aPrivateInt");
+ printFieldInfo(field);
+ }
+ catch (NoSuchFieldException nsfe) {
+ System.out.println("as expected: aPrivateInt not found");
+ excep = true;
+ }
+ if (!excep)
+ System.out.println("BUG: got aPrivateInt");
+
+
+ field = target.getField("constantString");
+ printFieldInfo(field);
+ String val = (String) field.get(instance);
+ System.out.println(" Constant test value is " + val);
+
+
+ field = target.getField("cantTouchThis");
+ printFieldInfo(field);
+ intVal = field.getInt(instance);
+ System.out.println(" cantTouchThis is " + intVal);
+ try {
+ field.setInt(instance, 99);
+ System.out.println("ERROR: set-final succeeded\n");
+ } catch (IllegalAccessException iae) {
+ System.out.println(" got expected set-final failure\n");
+ }
+ intVal = field.getInt(instance);
+ System.out.println(" cantTouchThis is now " + intVal);
+
+ Constructor<Target> cons;
+ Target targ;
+ Object[] args;
+
+ cons = target.getConstructor(new Class[] { int.class,float.class });
+ args = new Object[] { new Integer(7), new Float(3.3333) };
+ System.out.println("cons modifiers=" + cons.getModifiers());
+ targ = cons.newInstance(args);
+ targ.myMethod(17);
+
+ }
+ catch (Exception ex) {
+ System.out.println("----- unexpected exception -----");
+ ex.printStackTrace();
+ }
+
+ System.out.println("ReflectTest done!");
+ }
+
+ public static void checkType() {
+ Method m;
+
+ try {
+ m = Collections.class.getDeclaredMethod("checkType",
+ Object.class, Class.class);
+ } catch (NoSuchMethodException nsme) {
+ nsme.printStackTrace();
+ return;
+ }
+
+ m.setAccessible(true);
+ try {
+ m.invoke(null, new Object(), Object.class);
+ } catch (IllegalAccessException iae) {
+ iae.printStackTrace();
+ return;
+ } catch (InvocationTargetException ite) {
+ ite.printStackTrace();
+ return;
+ }
+
+ try {
+ System.out.println("checkType invoking null");
+ m.invoke(null, new Object(), int.class);
+ System.out.println("ERROR: should throw InvocationTargetException");
+ } catch (InvocationTargetException ite) {
+ System.out.println("checkType got expected exception");
+ } catch (IllegalAccessException iae) {
+ iae.printStackTrace();
+ return;
+ }
+ }
+
+ public static void checkInit() {
+ Class niuClass = NoisyInitUser.class;
+ Method[] methods;
+
+ methods = niuClass.getDeclaredMethods();
+ System.out.println("got methods");
+ /* neither NoisyInit nor NoisyInitUser should be initialized yet */
+ NoisyInitUser niu = new NoisyInitUser();
+ NoisyInit ni = new NoisyInit();
+ }
+
+ public static void main(String[] args) {
+ Main test = new Main();
+ test.run();
+
+ checkType();
+ checkInit();
+ }
+}
+
+
+class SuperTarget {
+ public SuperTarget() {
+ System.out.println("SuperTarget constructor ()V");
+ superInt = 1010101;
+ superClassInt = 1010102;
+ }
+
+ public int myMethod(float floatArg) {
+ System.out.println("myMethod (F)I " + floatArg);
+ return 6;
+ }
+
+ public int superInt;
+ public static int superClassInt;
+}
+
+class Target extends SuperTarget {
+ public Target() {
+ System.out.println("Target constructor ()V");
+ }
+
+ public Target(int ii, float ff) {
+ System.out.println("Target constructor (IF)V : ii="
+ + ii + " ff=" + ff);
+ anInt = ii;
+ }
+
+ public int myMethod(int intarg) throws NullPointerException, IOException {
+ System.out.println("myMethod (I)I");
+ System.out.println(" arg=" + intarg + " anInt=" + anInt);
+ return 5;
+ }
+
+ public int myMethod(String[] strarg, float f, char c) {
+ System.out.println("myMethod: " + strarg[0] + " " + f + " " + c + " !");
+ return 7;
+ }
+
+ public static void myNoargMethod() {
+ System.out.println("myNoargMethod ()V");
+ }
+
+ public void throwingMethod() {
+ System.out.println("throwingMethod");
+ throw new NullPointerException("gratuitous throw!");
+ }
+
+ public void misc() {
+ System.out.println("misc");
+ }
+
+ public int anInt;
+ public String string1 = "hey";
+ public String string2 = "yo";
+ public String string3 = "there";
+ private String string4 = "naughty";
+ public static final String constantString = "a constant string";
+ private int aPrivateInt;
+
+ public final int cantTouchThis = 77;
+
+ public long pubLong = 0x1122334455667788L;
+
+ public static double staticDouble = 3.3;
+}
+
+class NoisyInit {
+ static {
+ System.out.println("NoisyInit is initializing");
+ //Throwable th = new Throwable();
+ //th.printStackTrace();
+ }
+}
+
+class NoisyInitUser {
+ static {
+ System.out.println("NoisyInitUser is initializing");
+ }
+ public void createNoisyInit(NoisyInit ni) {}
+}
diff --git a/tests/047-returns/expected.txt b/tests/047-returns/expected.txt
new file mode 100644
index 0000000..160f69c
--- /dev/null
+++ b/tests/047-returns/expected.txt
@@ -0,0 +1,10 @@
+pick 1
+one running
+one
+1
+pick 2
+two running
+two
+2
+pick 3
+three running
diff --git a/tests/047-returns/info.txt b/tests/047-returns/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/047-returns/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/047-returns/src/Main.java b/tests/047-returns/src/Main.java
new file mode 100644
index 0000000..d53c4a7
--- /dev/null
+++ b/tests/047-returns/src/Main.java
@@ -0,0 +1,65 @@
+// Copyright 2007 The Android Open Source Project
+
+/**
+ * Return stuff.
+ */
+public class Main {
+ public static void main(String[] args) {
+
+ System.out.println("pick 1");
+ pickOne(1).run();
+ System.out.println(((CommonInterface)pickOne(1)).doStuff());
+
+ System.out.println("pick 2");
+ pickOne(2).run();
+ System.out.println(((CommonInterface)pickOne(2)).doStuff());
+
+ System.out.println("pick 3");
+ pickOne(3).run();
+ }
+
+ public static Runnable pickOne(int which) {
+ Runnable runme;
+
+ if (which == 1)
+ runme = new ClassOne();
+ else if (which == 2)
+ runme = new ClassTwo();
+ else if (which == 3)
+ runme = new ClassThree();
+ else
+ runme = null;
+
+ return runme;
+ }
+}
+
+class ClassOne implements CommonInterface, Runnable {
+ public void run() {
+ System.out.println("one running");
+ }
+ public int doStuff() {
+ System.out.println("one");
+ return 1;
+ }
+}
+
+class ClassTwo implements CommonInterface, Runnable {
+ public void run() {
+ System.out.println("two running");
+ }
+ public int doStuff() {
+ System.out.println("two");
+ return 2;
+ }
+}
+
+class ClassThree implements Runnable {
+ public void run() {
+ System.out.println("three running");
+ }
+}
+
+interface CommonInterface {
+ int doStuff();
+}
diff --git a/tests/048-server-socket/expected.txt b/tests/048-server-socket/expected.txt
new file mode 100644
index 0000000..23c3e84
--- /dev/null
+++ b/tests/048-server-socket/expected.txt
@@ -0,0 +1,4 @@
+opened!
+closed!
+reopened!
+done
diff --git a/tests/048-server-socket/info.txt b/tests/048-server-socket/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/048-server-socket/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/048-server-socket/src/Main.java b/tests/048-server-socket/src/Main.java
new file mode 100644
index 0000000..55dbf9a
--- /dev/null
+++ b/tests/048-server-socket/src/Main.java
@@ -0,0 +1,52 @@
+// Copyright 2007 The Android Open Source Project
+
+import java.net.ServerSocket;
+import java.io.IOException;
+
+
+/**
+ * Quick server socket test.
+ */
+public class Main {
+ private static void snooze(int sec) {
+ try {
+ Thread.sleep(sec * 1000);
+ } catch (InterruptedException ie) {
+ ie.printStackTrace();
+ }
+ }
+
+ public static void main(String[] args) {
+ ServerSocket socket;
+
+ try {
+ socket = new ServerSocket(7890);
+ } catch (IOException ioe) {
+ System.out.println("couldn't open socket " + ioe.getMessage());
+ return;
+ }
+
+ System.out.println("opened!");
+ snooze(1);
+
+ try {
+ socket.close();
+ } catch (IOException ioe) {
+ System.out.println("couldn't close socket " + ioe.getMessage());
+ return;
+ }
+
+ System.out.println("closed!");
+ snooze(1);
+
+ try {
+ socket = new ServerSocket(7890);
+ } catch (IOException ioe) {
+ System.out.println("couldn't reopen socket " + ioe.getMessage());
+ return;
+ }
+
+ System.out.println("reopened!");
+ System.out.println("done");
+ }
+}
diff --git a/tests/049-show-object/expected.txt b/tests/049-show-object/expected.txt
new file mode 100644
index 0000000..4613c39
--- /dev/null
+++ b/tests/049-show-object/expected.txt
@@ -0,0 +1,11 @@
+d is 3.1415
+class: class [Ljava.lang.Object;
+0: null
+1: null
+2: null
+3: null
+4: null
+class: class [Ljava.lang.String;
+0: hey
+1: you
+2: there
diff --git a/tests/049-show-object/info.txt b/tests/049-show-object/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/049-show-object/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/049-show-object/src/Main.java b/tests/049-show-object/src/Main.java
new file mode 100644
index 0000000..d31eeda
--- /dev/null
+++ b/tests/049-show-object/src/Main.java
@@ -0,0 +1,34 @@
+// Copyright 2008 The Android Open Source Project
+
+/*
+ * Some basic operations for testing the debugger.
+ */
+public class Main {
+ long mLong = 0x1122334455667788L;
+
+ public Main() {
+ double d = 3.1415;
+ System.out.println("d is " + d);
+ }
+
+ public static void showObject(Object[] foo) {
+ int xyz = 27;
+ System.out.println("class: " + foo.getClass());
+
+ for (int i = 0; i < foo.length; i++) {
+ System.out.println(i + ": " + foo[i]);
+ }
+ }
+
+ public static void main(String[] args) {
+ int x = 5;
+ Main testObj = new Main();
+
+ Object[] array = new Object[5];
+ showObject(array);
+
+ String[] niftyStrings = new String[] { "hey", "you", "there" };
+ array = niftyStrings;
+ showObject(array);
+ }
+}
diff --git a/tests/050-sync-test/expected.txt b/tests/050-sync-test/expected.txt
new file mode 100644
index 0000000..c2a7031
--- /dev/null
+++ b/tests/050-sync-test/expected.txt
@@ -0,0 +1,34 @@
+Sleep Test
+GOING
+GONE
+
+Count Test
+going: 1
+going: 1
+going: 1
+going: 1
+going: 1
+going: 1
+going: 1
+going: 1
+going: 1
+going: 1
+Final result: 10
+going: 2
+going: 2
+going: 2
+going: 2
+going: 2
+going: 2
+going: 2
+going: 2
+going: 2
+going: 2
+Final result: 20
+main: all done
+
+Interrupt Test
+SleepyThread.run starting
+SleepyThread.run starting
+interrupting other (isAlive=true)
+thread#0 interrupted, flag=false
diff --git a/tests/050-sync-test/info.txt b/tests/050-sync-test/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/050-sync-test/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/050-sync-test/src/Main.java b/tests/050-sync-test/src/Main.java
new file mode 100644
index 0000000..c2ea192
--- /dev/null
+++ b/tests/050-sync-test/src/Main.java
@@ -0,0 +1,179 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Test synchronization primitives.
+ *
+ * TODO: this should be re-written to be a little more rigorous and/or
+ * useful. Also, the ThreadDeathHandler stuff should be exposed or
+ * split out.
+ */
+public class Main {
+ public static void main(String[] args) {
+ System.out.println("Sleep Test");
+ sleepTest();
+
+ System.out.println("\nCount Test");
+ countTest();
+
+ System.out.println("\nInterrupt Test");
+ interruptTest();
+ }
+
+ static void sleepTest() {
+ System.out.println("GOING");
+ try {
+ Thread.sleep(1000);
+ }
+ catch (InterruptedException ie) {
+ System.out.println("INTERRUPT!");
+ ie.printStackTrace();
+ }
+ System.out.println("GONE");
+ }
+
+ static void countTest() {
+ CpuThread one, two;
+
+ one = new CpuThread(1);
+ two = new CpuThread(2);
+
+ one.start();
+ two.start();
+
+ try {
+ Thread.sleep(100);
+ }
+ catch (InterruptedException ie) {
+ System.out.println("INTERRUPT!");
+ ie.printStackTrace();
+ }
+
+ //System.out.println("main: off and running");
+
+ try {
+ one.join();
+ two.join();
+ }
+ catch (InterruptedException ie) {
+ System.out.println("INTERRUPT!");
+ ie.printStackTrace();
+ }
+ System.out.println("main: all done");
+ }
+
+ static void interruptTest() {
+ SleepyThread sleepy, pesky;
+
+ sleepy = new SleepyThread(null);
+ pesky = new SleepyThread(sleepy);
+
+ sleepy.setPriority(4);
+ sleepy.start();
+ pesky.start();
+ pesky.setPriority(3);
+ }
+}
+
+class CpuThread extends Thread {
+ static Object mSyncable = new Object();
+ static int mCount = 0;
+ int mNumber;
+
+ CpuThread(int num) {
+ super("CpuThread " + num);
+ mNumber = num;
+ }
+
+ public void run() {
+ //System.out.print("thread running -- ");
+ //System.out.println(Thread.currentThread().getName());
+
+ for (int i = 0; i < 10; i++) {
+ output(mNumber);
+ }
+
+ System.out.print("Final result: ");
+ System.out.println(mCount);
+ }
+
+ void output(int num) {
+ /*
+ * Delete the next line; last "final result" should != 20.
+ */
+ synchronized (mSyncable)
+ {
+ int i, count;
+
+ count = mCount;
+
+ System.out.print("going: ");
+ System.out.println(num);
+
+ /* burn CPU; adjust end value so we exceed scheduler quantum */
+ for (int j = 0; j < 5000; j++)
+ ;
+
+ count++;
+ mCount = count;
+ }
+ }
+}
+
+class SleepyThread extends Thread {
+ private SleepyThread mOther;
+ private Integer[] mWaitOnMe; // any type of object will do
+
+ private static int count = 0;
+
+ SleepyThread(SleepyThread other) {
+ mOther = other;
+ mWaitOnMe = new Integer[] { 1, 2 };
+
+ setName("thread#" + count);
+ count++;
+ }
+
+ public void run() {
+ System.out.println("SleepyThread.run starting");
+
+ if (false) {
+ ThreadDeathHandler threadHandler =
+ new ThreadDeathHandler("SYNC THREAD");
+ Thread.currentThread().setUncaughtExceptionHandler(threadHandler);
+ throw new NullPointerException("die");
+ }
+
+ if (mOther == null) {
+ boolean intr = false;
+
+ try {
+ synchronized (mWaitOnMe) {
+ mWaitOnMe.wait(9000);
+ }
+ }
+ catch (InterruptedException ie) {
+ // Expecting this; interrupted should be false.
+ System.out.println(Thread.currentThread().getName() +
+ " interrupted, flag=" + Thread.interrupted());
+ intr = true;
+ }
+ catch (Exception ex) {
+ ex.printStackTrace();
+ }
+
+ if (!intr)
+ System.out.println("NOT INTERRUPTED");
+ } else {
+ try {
+ Thread.sleep(2000);
+ }
+ catch (InterruptedException ie) {
+ System.out.println("PESKY INTERRUPTED?");
+ }
+
+ System.out.println("interrupting other (isAlive="
+ + mOther.isAlive() + ")");
+ mOther.interrupt();
+ }
+ }
+}
diff --git a/tests/050-sync-test/src/ThreadDeathHandler.java b/tests/050-sync-test/src/ThreadDeathHandler.java
new file mode 100644
index 0000000..5ea61a5
--- /dev/null
+++ b/tests/050-sync-test/src/ThreadDeathHandler.java
@@ -0,0 +1,19 @@
+// Copyright 2007 The Android Open Source Project
+
+import java.lang.Thread.UncaughtExceptionHandler;
+
+/**
+ * Report death-by-uncaught-exception.
+ */
+public class ThreadDeathHandler implements Thread.UncaughtExceptionHandler {
+ private String mMyMessage;
+
+ public ThreadDeathHandler(String msg) {
+ mMyMessage = msg;
+ }
+
+ public void uncaughtException(Thread t, Throwable e) {
+ System.err.println("Uncaught exception " + mMyMessage + "!");
+ e.printStackTrace();
+ }
+}
diff --git a/tests/051-thread/expected.txt b/tests/051-thread/expected.txt
new file mode 100644
index 0000000..fbe32f6
--- /dev/null
+++ b/tests/051-thread/expected.txt
@@ -0,0 +1,518 @@
+running 0
+running 1
+running 2
+running 3
+running 4
+running 5
+running 6
+running 7
+running 8
+running 9
+running 10
+running 11
+running 12
+running 13
+running 14
+running 15
+running 16
+running 17
+running 18
+running 19
+running 20
+running 21
+running 22
+running 23
+running 24
+running 25
+running 26
+running 27
+running 28
+running 29
+running 30
+running 31
+running 32
+running 33
+running 34
+running 35
+running 36
+running 37
+running 38
+running 39
+running 40
+running 41
+running 42
+running 43
+running 44
+running 45
+running 46
+running 47
+running 48
+running 49
+running 50
+running 51
+running 52
+running 53
+running 54
+running 55
+running 56
+running 57
+running 58
+running 59
+running 60
+running 61
+running 62
+running 63
+running 64
+running 65
+running 66
+running 67
+running 68
+running 69
+running 70
+running 71
+running 72
+running 73
+running 74
+running 75
+running 76
+running 77
+running 78
+running 79
+running 80
+running 81
+running 82
+running 83
+running 84
+running 85
+running 86
+running 87
+running 88
+running 89
+running 90
+running 91
+running 92
+running 93
+running 94
+running 95
+running 96
+running 97
+running 98
+running 99
+running 100
+running 101
+running 102
+running 103
+running 104
+running 105
+running 106
+running 107
+running 108
+running 109
+running 110
+running 111
+running 112
+running 113
+running 114
+running 115
+running 116
+running 117
+running 118
+running 119
+running 120
+running 121
+running 122
+running 123
+running 124
+running 125
+running 126
+running 127
+running 128
+running 129
+running 130
+running 131
+running 132
+running 133
+running 134
+running 135
+running 136
+running 137
+running 138
+running 139
+running 140
+running 141
+running 142
+running 143
+running 144
+running 145
+running 146
+running 147
+running 148
+running 149
+running 150
+running 151
+running 152
+running 153
+running 154
+running 155
+running 156
+running 157
+running 158
+running 159
+running 160
+running 161
+running 162
+running 163
+running 164
+running 165
+running 166
+running 167
+running 168
+running 169
+running 170
+running 171
+running 172
+running 173
+running 174
+running 175
+running 176
+running 177
+running 178
+running 179
+running 180
+running 181
+running 182
+running 183
+running 184
+running 185
+running 186
+running 187
+running 188
+running 189
+running 190
+running 191
+running 192
+running 193
+running 194
+running 195
+running 196
+running 197
+running 198
+running 199
+running 200
+running 201
+running 202
+running 203
+running 204
+running 205
+running 206
+running 207
+running 208
+running 209
+running 210
+running 211
+running 212
+running 213
+running 214
+running 215
+running 216
+running 217
+running 218
+running 219
+running 220
+running 221
+running 222
+running 223
+running 224
+running 225
+running 226
+running 227
+running 228
+running 229
+running 230
+running 231
+running 232
+running 233
+running 234
+running 235
+running 236
+running 237
+running 238
+running 239
+running 240
+running 241
+running 242
+running 243
+running 244
+running 245
+running 246
+running 247
+running 248
+running 249
+running 250
+running 251
+running 252
+running 253
+running 254
+running 255
+running 256
+running 257
+running 258
+running 259
+running 260
+running 261
+running 262
+running 263
+running 264
+running 265
+running 266
+running 267
+running 268
+running 269
+running 270
+running 271
+running 272
+running 273
+running 274
+running 275
+running 276
+running 277
+running 278
+running 279
+running 280
+running 281
+running 282
+running 283
+running 284
+running 285
+running 286
+running 287
+running 288
+running 289
+running 290
+running 291
+running 292
+running 293
+running 294
+running 295
+running 296
+running 297
+running 298
+running 299
+running 300
+running 301
+running 302
+running 303
+running 304
+running 305
+running 306
+running 307
+running 308
+running 309
+running 310
+running 311
+running 312
+running 313
+running 314
+running 315
+running 316
+running 317
+running 318
+running 319
+running 320
+running 321
+running 322
+running 323
+running 324
+running 325
+running 326
+running 327
+running 328
+running 329
+running 330
+running 331
+running 332
+running 333
+running 334
+running 335
+running 336
+running 337
+running 338
+running 339
+running 340
+running 341
+running 342
+running 343
+running 344
+running 345
+running 346
+running 347
+running 348
+running 349
+running 350
+running 351
+running 352
+running 353
+running 354
+running 355
+running 356
+running 357
+running 358
+running 359
+running 360
+running 361
+running 362
+running 363
+running 364
+running 365
+running 366
+running 367
+running 368
+running 369
+running 370
+running 371
+running 372
+running 373
+running 374
+running 375
+running 376
+running 377
+running 378
+running 379
+running 380
+running 381
+running 382
+running 383
+running 384
+running 385
+running 386
+running 387
+running 388
+running 389
+running 390
+running 391
+running 392
+running 393
+running 394
+running 395
+running 396
+running 397
+running 398
+running 399
+running 400
+running 401
+running 402
+running 403
+running 404
+running 405
+running 406
+running 407
+running 408
+running 409
+running 410
+running 411
+running 412
+running 413
+running 414
+running 415
+running 416
+running 417
+running 418
+running 419
+running 420
+running 421
+running 422
+running 423
+running 424
+running 425
+running 426
+running 427
+running 428
+running 429
+running 430
+running 431
+running 432
+running 433
+running 434
+running 435
+running 436
+running 437
+running 438
+running 439
+running 440
+running 441
+running 442
+running 443
+running 444
+running 445
+running 446
+running 447
+running 448
+running 449
+running 450
+running 451
+running 452
+running 453
+running 454
+running 455
+running 456
+running 457
+running 458
+running 459
+running 460
+running 461
+running 462
+running 463
+running 464
+running 465
+running 466
+running 467
+running 468
+running 469
+running 470
+running 471
+running 472
+running 473
+running 474
+running 475
+running 476
+running 477
+running 478
+running 479
+running 480
+running 481
+running 482
+running 483
+running 484
+running 485
+running 486
+running 487
+running 488
+running 489
+running 490
+running 491
+running 492
+running 493
+running 494
+running 495
+running 496
+running 497
+running 498
+running 499
+running 500
+running 501
+running 502
+running 503
+running 504
+running 505
+running 506
+running 507
+running 508
+running 509
+running 510
+running 511
+Starting thread 'Thready'
+@ Thread running
+@ Got expected setDaemon exception
+@ Thread bailing
+Thread starter returning
+thread test done
diff --git a/tests/051-thread/info.txt b/tests/051-thread/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/051-thread/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/051-thread/src/Main.java b/tests/051-thread/src/Main.java
new file mode 100644
index 0000000..9acc89e
--- /dev/null
+++ b/tests/051-thread/src/Main.java
@@ -0,0 +1,73 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Test some basic thread stuff.
+ */
+public class Main {
+ public static void main(String[] args) {
+ for (int i = 0; i < 512; i++) {
+ MyThread myThread = new MyThread();
+ myThread.start();
+ try {
+ Thread.sleep(1);
+ } catch (InterruptedException ie) {
+ ie.printStackTrace();
+ }
+ }
+
+ go();
+ System.out.println("thread test done");
+ }
+
+ public static void go() {
+ Thread t = new Thread(null, new ThreadTestSub(), "Thready", 7168);
+
+ t.setDaemon(false);
+
+ System.out.print("Starting thread '" + t.getName() + "'\n");
+ t.start();
+
+ try {
+ t.join();
+ } catch (InterruptedException ex) {
+ ex.printStackTrace();
+ }
+
+ System.out.print("Thread starter returning\n");
+ }
+
+ /*
+ * Simple thread capacity test.
+ */
+ static class MyThread extends Thread {
+ private static int mCount = 0;
+ public void run() {
+ System.out.println("running " + (mCount++));
+ }
+ }
+}
+
+class ThreadTestSub implements Runnable {
+ public void run() {
+ System.out.print("@ Thread running\n");
+
+ try {
+ Thread.currentThread().setDaemon(true);
+ System.out.print("@ FAILED: setDaemon() succeeded\n");
+ } catch (IllegalThreadStateException itse) {
+ System.out.print("@ Got expected setDaemon exception\n");
+ }
+
+ //if (true)
+ // throw new NullPointerException();
+ try {
+ Thread.sleep(2000);
+ }
+ catch (InterruptedException ie) {
+ System.out.print("@ Interrupted!\n");
+ }
+ finally {
+ System.out.print("@ Thread bailing\n");
+ }
+ }
+}
diff --git a/tests/052-verifier-fun/expected.txt b/tests/052-verifier-fun/expected.txt
new file mode 100644
index 0000000..5662675
--- /dev/null
+++ b/tests/052-verifier-fun/expected.txt
@@ -0,0 +1,2 @@
+BlahOne
+Zorch.
diff --git a/tests/052-verifier-fun/info.txt b/tests/052-verifier-fun/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/052-verifier-fun/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/052-verifier-fun/src/Blah.java b/tests/052-verifier-fun/src/Blah.java
new file mode 100644
index 0000000..edd6c9d
--- /dev/null
+++ b/tests/052-verifier-fun/src/Blah.java
@@ -0,0 +1,4 @@
+public abstract class Blah {
+ public void unrelatedStuff() {
+ }
+}
diff --git a/tests/052-verifier-fun/src/BlahFeature.java b/tests/052-verifier-fun/src/BlahFeature.java
new file mode 100644
index 0000000..ea0e18a
--- /dev/null
+++ b/tests/052-verifier-fun/src/BlahFeature.java
@@ -0,0 +1,3 @@
+public interface BlahFeature {
+ public void doStuff();
+}
diff --git a/tests/052-verifier-fun/src/BlahOne.java b/tests/052-verifier-fun/src/BlahOne.java
new file mode 100644
index 0000000..ed423cd
--- /dev/null
+++ b/tests/052-verifier-fun/src/BlahOne.java
@@ -0,0 +1,5 @@
+public class BlahOne extends Blah implements BlahFeature {
+ public void doStuff() {
+ System.out.println("BlahOne");
+ }
+}
diff --git a/tests/052-verifier-fun/src/BlahTwo.java b/tests/052-verifier-fun/src/BlahTwo.java
new file mode 100644
index 0000000..cff3670
--- /dev/null
+++ b/tests/052-verifier-fun/src/BlahTwo.java
@@ -0,0 +1,5 @@
+public class BlahTwo extends Blah implements BlahFeature {
+ public void doStuff() {
+ System.out.println("BlahTwo");
+ }
+}
diff --git a/tests/052-verifier-fun/src/Main.java b/tests/052-verifier-fun/src/Main.java
new file mode 100644
index 0000000..ca960cf
--- /dev/null
+++ b/tests/052-verifier-fun/src/Main.java
@@ -0,0 +1,109 @@
+// Copyright 2007 The Android Open Source Project
+
+import java.lang.reflect.Type;
+
+/**
+ * Throw a few things at the verifier, all of which are expected to pass.
+ */
+public class Main {
+ static public void main(String[] args) {
+ tryBlah(1);
+
+ System.out.println("Zorch.");
+ }
+
+ /*
+ * Make sure the verifier is handling type merge of arrays of
+ * references correctly.
+ */
+ static Object[] arrayCheck1(int wanted) {
+ String[] arrayOne;
+ Integer[] arrayTwo;
+
+ arrayOne = new String[1];
+ arrayTwo = new Integer[1];
+
+ switch (wanted) {
+ case 0: return arrayOne;
+ case 1: return arrayTwo;
+ default: return null;
+ }
+ }
+
+ static Object arrayCheck1b(int wanted) {
+ String[] arrayOne;
+ Integer[] arrayTwo;
+ int[] arrayThree;
+
+ arrayOne = new String[1];
+ arrayTwo = new Integer[1];
+ arrayThree = new int[1];
+
+ switch (wanted) {
+ case 0: return arrayOne;
+ case 1: return arrayTwo;
+ case 2: return arrayThree;
+ default: return null;
+ }
+ }
+
+ static Object[] arrayCheck2(int wanted) {
+ String[][] arrayOne;
+ String[][] arrayTwo;
+ Integer[][] arrayThree;
+
+ arrayOne = new String[1][];
+ arrayTwo = new String[1][];
+ arrayThree = new Integer[1][];
+
+ switch (wanted) {
+ case 0: return arrayOne;
+ case 1: return arrayTwo;
+ case 2: return arrayThree;
+ default: return null;
+ }
+ }
+
+ static Object[] arrayCheck3(int wanted) {
+ String[][] arrayTwo;
+ String[][][][] arrayFour;
+
+ arrayTwo = new String[1][];
+ arrayFour = new String[1][][][];
+
+ switch (wanted) {
+ case 0: return arrayTwo;
+ case 1: return arrayFour;
+ default: return null;
+ }
+ }
+
+ /*
+ * Check return type merge.
+ */
+ private Type[] typeTest() {
+ if(this == null) {
+ return (Class<?>[])null;
+ }
+ return (Type[])null;
+ }
+
+
+ /*
+ * Exercise the blahs.
+ */
+ static void tryBlah(int num) {
+ BlahFeature feature = null; // interface ref
+
+ switch (num) {
+ case 1:
+ feature = new BlahOne();
+ break;
+ default:
+ feature = new BlahTwo();
+ break;
+ }
+
+ feature.doStuff();
+ }
+}
diff --git a/tests/053-wait-some/expected.txt b/tests/053-wait-some/expected.txt
new file mode 100644
index 0000000..182892c
--- /dev/null
+++ b/tests/053-wait-some/expected.txt
@@ -0,0 +1,7 @@
+Caught expected exception on neg arg
+Waiting for 200ms...
+Waiting for 500ms...
+Waiting for 1000ms...
+Waiting for 2000ms...
+Waiting for 3500ms...
+Waiting for 8000ms...
diff --git a/tests/053-wait-some/info.txt b/tests/053-wait-some/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/053-wait-some/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/053-wait-some/src/Main.java b/tests/053-wait-some/src/Main.java
new file mode 100644
index 0000000..51e6c52
--- /dev/null
+++ b/tests/053-wait-some/src/Main.java
@@ -0,0 +1,71 @@
+// Copyright 2007 The Android Open Source Project
+
+/**
+ * Exercise Object.wait(), comparing results against wall clock time.
+ */
+public class Main {
+ /* delays, in milliseconds */
+ private final static long[] DELAYS = {
+ 200, 500, 1000, 2000, 3500, 8000
+ };
+
+ public static void main(String[] args) {
+ boolean timing = (args.length >= 1) && args[0].equals("--timing");
+ doit(timing);
+ }
+
+ public static void doit(boolean timing) {
+ Object sleepy = new Object();
+ long start, end;
+
+ synchronized (sleepy) {
+ try {
+ sleepy.wait(-500);
+ System.out.println("HEY: didn't throw on negative arg");
+ } catch (IllegalArgumentException iae) {
+ System.out.println("Caught expected exception on neg arg");
+ } catch (InterruptedException ie) {
+ ie.printStackTrace();
+ }
+
+ for(long delay : DELAYS) {
+ System.out.println("Waiting for " + delay + "ms...");
+
+ start = System.currentTimeMillis();
+ try {
+ sleepy.wait(delay);
+ } catch (InterruptedException ie) {
+ ie.printStackTrace();
+ }
+ end = System.currentTimeMillis();
+
+ long elapsed = end - start;
+ boolean showTime = timing;
+
+ if (! timing) {
+ long epsilon = delay / 10;
+ if (epsilon > 50) {
+ epsilon = 50;
+ }
+
+ long min = delay - epsilon;
+ long max = delay + epsilon;
+
+ if (elapsed < min) {
+ System.out.println(" Elapsed time was too short");
+ showTime = true;
+ } else if (elapsed > max) {
+ System.out.println(" Elapsed time was too long: "
+ + "elapsed=" + elapsed + " max=" + max);
+ showTime = true;
+ }
+ }
+
+ if (showTime) {
+ System.out.println(" Wall clock elapsed "
+ + elapsed + "ms");
+ }
+ }
+ }
+ }
+}
diff --git a/tests/054-uncaught/expected.txt b/tests/054-uncaught/expected.txt
new file mode 100644
index 0000000..e7473be
--- /dev/null
+++ b/tests/054-uncaught/expected.txt
@@ -0,0 +1,21 @@
+Test 1
+Uncaught exception DEFAULT!
+java.lang.NullPointerException: Hi diddly-ho, neighborino.
+ at Main.catchTheUncaught(Main.java:49)
+ at Main$Helper.run(Main.java:60)
+Test 2
+Uncaught exception THREAD!
+java.lang.NullPointerException: Hi diddly-ho, neighborino.
+ at Main.catchTheUncaught(Main.java:49)
+ at Main$Helper.run(Main.java:60)
+Test 3
+Uncaught exception THREAD!
+java.lang.NullPointerException: Hi diddly-ho, neighborino.
+ at Main.catchTheUncaught(Main.java:49)
+ at Main$Helper.run(Main.java:60)
+Test 1
+Uncaught exception DEFAULT!
+java.lang.NullPointerException: Hi diddly-ho, neighborino.
+ at Main.catchTheUncaught(Main.java:49)
+ at Main.main(Main.java:12)
+ at dalvik.system.NativeStart.main(Native Method)
diff --git a/tests/054-uncaught/info.txt b/tests/054-uncaught/info.txt
new file mode 100644
index 0000000..08127da
--- /dev/null
+++ b/tests/054-uncaught/info.txt
@@ -0,0 +1,6 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+TODO: Real description goes here.
diff --git a/tests/054-uncaught/src/Main.java b/tests/054-uncaught/src/Main.java
new file mode 100644
index 0000000..4ee6b05
--- /dev/null
+++ b/tests/054-uncaught/src/Main.java
@@ -0,0 +1,63 @@
+// Copyright 2006 The Android Open Source Project
+
+/**
+ * Test the uncaught exception handler.
+ */
+public class Main {
+ public static void main(String[] args) {
+ testThread(1);
+ testThread(2);
+ testThread(3);
+
+ catchTheUncaught(1);
+ }
+
+ private static void testThread(int which) {
+ Thread t = new Helper(which);
+ t.start();
+
+ try {
+ t.join();
+ } catch (InterruptedException ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ static void catchTheUncaught(int which) {
+ ThreadDeathHandler defHandler = new ThreadDeathHandler("DEFAULT");
+ ThreadDeathHandler threadHandler = new ThreadDeathHandler("THREAD");
+
+ System.out.println("Test " + which);
+ switch (which) {
+ case 1: {
+ Thread.setDefaultUncaughtExceptionHandler(defHandler);
+ break;
+ }
+ case 2: {
+ Thread.currentThread().setUncaughtExceptionHandler(
+ threadHandler);
+ break;
+ }
+ case 3: {
+ Thread.setDefaultUncaughtExceptionHandler(defHandler);
+ Thread.currentThread().setUncaughtExceptionHandler(
+ threadHandler);
+ break;
+ }
+ }
+
+ throw new NullPointerException("Hi diddly-ho, neighborino.");
+ }
+
+ private static class Helper extends Thread {
+ private int which;
+
+ public Helper(int which) {
+ this.which = which;
+ }
+
+ public void run() {
+ catchTheUncaught(which);
+ }
+ }
+}
diff --git a/tests/054-uncaught/src/ThreadDeathHandler.java b/tests/054-uncaught/src/ThreadDeathHandler.java
new file mode 100644
index 0000000..5ea61a5
--- /dev/null
+++ b/tests/054-uncaught/src/ThreadDeathHandler.java
@@ -0,0 +1,19 @@
+// Copyright 2007 The Android Open Source Project
+
+import java.lang.Thread.UncaughtExceptionHandler;
+
+/**
+ * Report death-by-uncaught-exception.
+ */
+public class ThreadDeathHandler implements Thread.UncaughtExceptionHandler {
+ private String mMyMessage;
+
+ public ThreadDeathHandler(String msg) {
+ mMyMessage = msg;
+ }
+
+ public void uncaughtException(Thread t, Throwable e) {
+ System.err.println("Uncaught exception " + mMyMessage + "!");
+ e.printStackTrace();
+ }
+}
diff --git a/tests/055-enum-performance/expected.txt b/tests/055-enum-performance/expected.txt
new file mode 100644
index 0000000..d81c194
--- /dev/null
+++ b/tests/055-enum-performance/expected.txt
@@ -0,0 +1,12 @@
+FOUR
+ONE
+FOURTEEN
+NINE
+FIVE
+TWELVE
+SamePackagePublicEnum
+basis: performed 40000 iterations
+test1: performed 10000 iterations
+test2: performed 10000 iterations
+test3: performed 10000 iterations
+Timing is acceptable.
diff --git a/tests/055-enum-performance/info.txt b/tests/055-enum-performance/info.txt
new file mode 100644
index 0000000..2ea1b9d
--- /dev/null
+++ b/tests/055-enum-performance/info.txt
@@ -0,0 +1,2 @@
+This is a performance test of Enum.valueOf(). To see the numbers, invoke
+this test with the "--timing" option.
diff --git a/tests/055-enum-performance/src/Main.java b/tests/055-enum-performance/src/Main.java
new file mode 100644
index 0000000..64d03eb
--- /dev/null
+++ b/tests/055-enum-performance/src/Main.java
@@ -0,0 +1,199 @@
+import otherpackage.OtherPackagePublicEnum;
+
+public class Main {
+ /** used by {@link #basisCall} */
+ static private int basisTestValue = 12;
+
+ static public void main(String[] args) throws Exception {
+ boolean timing = (args.length >= 1) && args[0].equals("--timing");
+ run(timing);
+ }
+
+ static public void run(boolean timing) {
+ preTest();
+
+ long time0 = System.nanoTime();
+ int count1 = test1(500);
+ long time1 = System.nanoTime();
+ int count2 = test2(500);
+ long time2 = System.nanoTime();
+ int count3 = test3(500);
+ long time3 = System.nanoTime();
+ int count4 = basis(2000);
+ long time4 = System.nanoTime();
+
+ System.out.println("basis: performed " + count4 + " iterations");
+ System.out.println("test1: performed " + count1 + " iterations");
+ System.out.println("test2: performed " + count2 + " iterations");
+ System.out.println("test3: performed " + count3 + " iterations");
+
+ double msec1 = (time1 - time0) / (double) count1 / 1000000;
+ double msec2 = (time2 - time1) / (double) count2 / 1000000;
+ double msec3 = (time3 - time2) / (double) count3 / 1000000;
+ double basisMsec = (time4 - time3) / (double) count4 / 1000000;
+
+ double avg = (msec1 + msec2 + msec3) / 3;
+ if (avg < (basisMsec * 25)) {
+ System.out.println("Timing is acceptable.");
+ } else {
+ System.out.println("Iterations are taking too long!");
+ timing = true;
+ }
+
+ if (timing) {
+ System.out.printf("basis time: %.3g msec\n", basisMsec);
+ System.out.printf("test1: %.3g msec per iteration\n", msec1);
+ System.out.printf("test2: %.3g msec per iteration\n", msec2);
+ System.out.printf("test3: %.3g msec per iteration\n", msec3);
+ }
+
+ }
+
+ static public void preTest() {
+ /*
+ * This is meant to ensure that the basic enum functionality
+ * really is working.
+ */
+
+ Class<SamePackagePublicEnum> c = SamePackagePublicEnum.class;
+
+ System.out.println(Enum.valueOf(c, "FOUR"));
+ System.out.println(Enum.valueOf(c, "ONE"));
+ System.out.println(Enum.valueOf(c, "FOURTEEN"));
+ System.out.println(Enum.valueOf(c, "NINE"));
+ System.out.println(Enum.valueOf(c, "FIVE"));
+ System.out.println(Enum.valueOf(c, "TWELVE"));
+
+ System.out.println(Enum.valueOf(c, "ZERO").getClass().getName());
+ }
+
+ static public int basis(int iters) {
+ /*
+ * The basis time is the time taken to call a static method
+ * passing two arguments, which in turn accesses a static
+ * variable, compares a string, and does a little trivial math
+ * and a trivial comparison. (That is, this is a mini
+ * "omnibus" performance metric.) This is clearly going to be
+ * much faster than Enum.valueOf(), which is why we multiply
+ * the time before testing.
+ */
+ for (int i = iters; i > 0; i--) {
+ basisCall(i, "aname");
+ basisCall(i, "bname");
+ basisCall(i, "cname");
+ basisCall(i, "dname");
+ basisCall(i, "ename");
+ basisCall(i, "fname");
+ basisCall(i, "gname");
+ basisCall(i, "hname");
+ basisCall(i, "iname");
+ basisCall(i, "jname");
+ basisCall(i, "kname");
+ basisCall(i, "lname");
+ basisCall(i, "mname");
+ basisCall(i, "nname");
+ basisCall(i, "oname");
+ basisCall(i, "pname");
+ basisCall(i, "qname");
+ basisCall(i, "rname");
+ basisCall(i, "sname");
+ basisCall(i, "tname");
+ }
+
+ return iters * 20;
+ }
+
+ static public int basisCall(int i, String name) {
+ int compare = name.compareTo("fuzzbot");
+
+ if (i < (basisTestValue * compare)) {
+ return basisTestValue;
+ } else {
+ return i;
+ }
+ }
+
+ static public int test1(int iters) {
+ Class<SamePackagePublicEnum> c = SamePackagePublicEnum.class;
+ for (int i = iters; i > 0; i--) {
+ Enum.valueOf(c, "ZERO");
+ Enum.valueOf(c, "ONE");
+ Enum.valueOf(c, "TWO");
+ Enum.valueOf(c, "THREE");
+ Enum.valueOf(c, "FOUR");
+ Enum.valueOf(c, "FIVE");
+ Enum.valueOf(c, "SIX");
+ Enum.valueOf(c, "SEVEN");
+ Enum.valueOf(c, "EIGHT");
+ Enum.valueOf(c, "NINE");
+ Enum.valueOf(c, "TEN");
+ Enum.valueOf(c, "ELEVEN");
+ Enum.valueOf(c, "TWELVE");
+ Enum.valueOf(c, "THIRTEEN");
+ Enum.valueOf(c, "FOURTEEN");
+ Enum.valueOf(c, "FIFTEEN");
+ Enum.valueOf(c, "SIXTEEN");
+ Enum.valueOf(c, "SEVENTEEN");
+ Enum.valueOf(c, "EIGHTEEN");
+ Enum.valueOf(c, "NINETEEN");
+ }
+
+ return iters * 20;
+ }
+
+ static public int test2(int iters) {
+ Class<SamePackagePrivateEnum> c = SamePackagePrivateEnum.class;
+ for (int i = iters; i > 0; i--) {
+ Enum.valueOf(c, "ZERO");
+ Enum.valueOf(c, "ONE");
+ Enum.valueOf(c, "TWO");
+ Enum.valueOf(c, "THREE");
+ Enum.valueOf(c, "FOUR");
+ Enum.valueOf(c, "FIVE");
+ Enum.valueOf(c, "SIX");
+ Enum.valueOf(c, "SEVEN");
+ Enum.valueOf(c, "EIGHT");
+ Enum.valueOf(c, "NINE");
+ Enum.valueOf(c, "TEN");
+ Enum.valueOf(c, "ELEVEN");
+ Enum.valueOf(c, "TWELVE");
+ Enum.valueOf(c, "THIRTEEN");
+ Enum.valueOf(c, "FOURTEEN");
+ Enum.valueOf(c, "FIFTEEN");
+ Enum.valueOf(c, "SIXTEEN");
+ Enum.valueOf(c, "SEVENTEEN");
+ Enum.valueOf(c, "EIGHTEEN");
+ Enum.valueOf(c, "NINETEEN");
+ }
+
+ return iters * 20;
+ }
+
+ static public int test3(int iters) {
+ Class<OtherPackagePublicEnum> c = OtherPackagePublicEnum.class;
+ for (int i = iters; i > 0; i--) {
+ Enum.valueOf(c, "ZERO");
+ Enum.valueOf(c, "ONE");
+ Enum.valueOf(c, "TWO");
+ Enum.valueOf(c, "THREE");
+ Enum.valueOf(c, "FOUR");
+ Enum.valueOf(c, "FIVE");
+ Enum.valueOf(c, "SIX");
+ Enum.valueOf(c, "SEVEN");
+ Enum.valueOf(c, "EIGHT");
+ Enum.valueOf(c, "NINE");
+ Enum.valueOf(c, "TEN");
+ Enum.valueOf(c, "ELEVEN");
+ Enum.valueOf(c, "TWELVE");
+ Enum.valueOf(c, "THIRTEEN");
+ Enum.valueOf(c, "FOURTEEN");
+ Enum.valueOf(c, "FIFTEEN");
+ Enum.valueOf(c, "SIXTEEN");
+ Enum.valueOf(c, "SEVENTEEN");
+ Enum.valueOf(c, "EIGHTEEN");
+ Enum.valueOf(c, "NINETEEN");
+ }
+
+ return iters * 20;
+ }
+}
diff --git a/tests/055-enum-performance/src/SamePackagePrivateEnum.java b/tests/055-enum-performance/src/SamePackagePrivateEnum.java
new file mode 100644
index 0000000..b6759f6
--- /dev/null
+++ b/tests/055-enum-performance/src/SamePackagePrivateEnum.java
@@ -0,0 +1,5 @@
+/*package*/ enum SamePackagePrivateEnum {
+ ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE,
+ TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN, SIXTEEN,
+ SEVENTEEN, EIGHTEEN, NINETEEN;
+}
diff --git a/tests/055-enum-performance/src/SamePackagePublicEnum.java b/tests/055-enum-performance/src/SamePackagePublicEnum.java
new file mode 100644
index 0000000..3a1c230
--- /dev/null
+++ b/tests/055-enum-performance/src/SamePackagePublicEnum.java
@@ -0,0 +1,5 @@
+public enum SamePackagePublicEnum {
+ ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE,
+ TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN, SIXTEEN,
+ SEVENTEEN, EIGHTEEN, NINETEEN;
+}
diff --git a/tests/055-enum-performance/src/otherpackage/OtherPackagePublicEnum.java b/tests/055-enum-performance/src/otherpackage/OtherPackagePublicEnum.java
new file mode 100644
index 0000000..4ef4d78
--- /dev/null
+++ b/tests/055-enum-performance/src/otherpackage/OtherPackagePublicEnum.java
@@ -0,0 +1,7 @@
+package otherpackage;
+
+public enum OtherPackagePublicEnum {
+ ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE,
+ TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN, SIXTEEN,
+ SEVENTEEN, EIGHTEEN, NINETEEN;
+}
diff --git a/tests/056-const-string-jumbo/build b/tests/056-const-string-jumbo/build
new file mode 100644
index 0000000..c5e35db
--- /dev/null
+++ b/tests/056-const-string-jumbo/build
@@ -0,0 +1,47 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Stop if something fails.
+set -e
+
+# Write out files with 32768 total static string declarations, so that
+# the reference to "zorch" in the real test file will be guaranteed to
+# need a jumbo string reference (it sorts last after all the others).
+# Note: Each string reference is stored in a separate static variable,
+# and that variable's name is also represented in the strings, which
+# is why we can just have 32768 and not 65536 declarations.
+
+awk '
+BEGIN {
+ writeFile("Zorch1", 0, 16383);
+ writeFile("Zorch2", 16384, 32767);
+}
+function writeFile(name, start, end) {
+ fileName = "src/" name ".java";
+ printf("public class %s {\n", name) > fileName;
+ for (i = start; i <= end; i++) {
+ printf(" static public final String s%d = \"%d\";\n",
+ i, i) > fileName;
+ }
+ printf("}\n") > fileName;
+}'
+
+mkdir classes
+${JAVAC} -d classes src/*.java
+
+dx -JXmx500m --debug --dex --no-optimize --positions=none --no-locals \
+ --dump-to=classes.lst --output=classes.dex classes
+zip test.jar classes.dex
diff --git a/tests/056-const-string-jumbo/expected.txt b/tests/056-const-string-jumbo/expected.txt
new file mode 100644
index 0000000..bebbf9e
--- /dev/null
+++ b/tests/056-const-string-jumbo/expected.txt
@@ -0,0 +1 @@
+zorch
diff --git a/tests/056-const-string-jumbo/info.txt b/tests/056-const-string-jumbo/info.txt
new file mode 100644
index 0000000..c4ba856
--- /dev/null
+++ b/tests/056-const-string-jumbo/info.txt
@@ -0,0 +1 @@
+Test that the opcode const-string/jumbo works.
diff --git a/tests/056-const-string-jumbo/src/Main.java b/tests/056-const-string-jumbo/src/Main.java
new file mode 100644
index 0000000..68d6e53
--- /dev/null
+++ b/tests/056-const-string-jumbo/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ static public void main(String[] args) {
+ System.out.println("zorch");
+ }
+}
diff --git a/tests/057-iteration-performance/expected.txt b/tests/057-iteration-performance/expected.txt
new file mode 100644
index 0000000..9d59a5e
--- /dev/null
+++ b/tests/057-iteration-performance/expected.txt
@@ -0,0 +1,11 @@
+Running A...
+Running B...
+Running C...
+Running D...
+Running E...
+Running F...
+Running G...
+Running H...
+Done with runs.
+
+All times are within the expected ranges.
diff --git a/tests/057-iteration-performance/info.txt b/tests/057-iteration-performance/info.txt
new file mode 100644
index 0000000..36b5adc
--- /dev/null
+++ b/tests/057-iteration-performance/info.txt
@@ -0,0 +1,2 @@
+This is a performance test of various iterator uses. To see the numbers,
+invoke this test with the "--timing" option.
diff --git a/tests/057-iteration-performance/src/Main.java b/tests/057-iteration-performance/src/Main.java
new file mode 100644
index 0000000..d562802
--- /dev/null
+++ b/tests/057-iteration-performance/src/Main.java
@@ -0,0 +1,1108 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.ArrayList;
+
+/**
+ * The matrix of tests includes the A-E axis for loop body contents and
+ * the 0-5 axis for iterator style.
+ *
+ * <ul>
+ * <li>A: empty body</li>
+ * <li>B: array element access and update</li>
+ * <li>C: instance field access and update</li>
+ * <li>D: method call to empty method</li>
+ * <li>E: synch and then method call to empty method</li>
+ * <li>F: 5 method calls to empty method</li>
+ * <li>G: one small object allocation (empty constructor)</li>
+ * <li>H: copy 8k of bytes from one array to another</li>
+ * </ul>
+ *
+ * <ul>
+ * <li>0: for() loop backward to 0</li>
+ * <li>1: for() loop forward to local variable</li>
+ * <li>2: for() loop forward to array length</li>
+ * <li>3: for(:) loop over array</li>
+ * <li>4: for() loop forward to instance variable</li>
+ * <li>5: for() loop forward to trivial method call</li>
+ * <li>6: for(:) loop over ArrayList</li>
+ * </ul>
+ */
+public class Main {
+ static public final int BODIES = 8;
+ static public final int LOOPS = 7;
+
+ static public void main(String[] args) throws Exception {
+ boolean timing = (args.length >= 1) && args[0].equals("--timing");
+
+ int iters = 100;
+ double probeSec;
+
+ for (;;) {
+ long t0 = System.nanoTime();
+ runAllTests(iters, false);
+ long t1 = System.nanoTime();
+
+ probeSec = (t1 - t0) / 1000000000.0;
+ if (probeSec > 0.25) {
+ break;
+ }
+
+ iters *= 2;
+ }
+
+ // Attempt to arrange for the real test to take 20 seconds.
+ iters = (int) ((iters / probeSec) * 20);
+
+ if (timing) {
+ System.out.println("iters = " + iters);
+ }
+
+ run(timing, iters);
+ }
+
+ static private enum Normalization {
+ NONE, PER_COLUMN, TOP_LEFT;
+ }
+
+ static public void printTimings(double[][] timings, Normalization norm) {
+ System.out.println();
+ System.out.printf("%-7s A B C D E" +
+ " F G H\n",
+ (norm == Normalization.NONE) ? "(usec)" : "(ratio)");
+ System.out.println(" -------- -------- -------- -------- " +
+ "-------- -------- -------- --------");
+
+ double bases[] = new double[BODIES];
+ for (int i = 0; i < BODIES; i++) {
+ double n;
+ switch (norm) {
+ case PER_COLUMN: n = timings[i][0]; break;
+ case TOP_LEFT: n = timings[0][0]; break;
+ default /*NONE*/: n = 1.0; break;
+ }
+ bases[i] = n;
+ }
+
+ for (int i = 0; i < LOOPS; i++) {
+ System.out.printf("%4d: %8.3g %8.3g %8.3g %8.3g %8.3g %8.3g " +
+ "%8.3g %8.3g\n",
+ i,
+ timings[0][i] / bases[0],
+ timings[1][i] / bases[1],
+ timings[2][i] / bases[2],
+ timings[3][i] / bases[3],
+ timings[4][i] / bases[4],
+ timings[5][i] / bases[5],
+ timings[6][i] / bases[6],
+ timings[7][i] / bases[7]);
+ }
+ }
+
+ static public void run(boolean timing, int iters) {
+ double[][] timings = null; // assign to avoid apparent javac bug
+
+ // Try up to 5 times to get good times.
+ for (int i = 0; i < 5; i++) {
+ double[][] newTimings = runAllTests(iters, timing || (i == 0));
+
+ if (timings == null) {
+ timings = newTimings;
+ } else {
+ combineTimings(timings, newTimings, i);
+ }
+
+ if (checkTimes(timings, timing)) {
+ break;
+ }
+ }
+
+ System.out.println("Done with runs.");
+
+ boolean goodTimes = checkTimes(timings, true);
+
+ if (! goodTimes) {
+ timing = true;
+ }
+
+ if (timing) {
+ printTimings(timings, Normalization.NONE);
+ printTimings(timings, Normalization.TOP_LEFT);
+ printTimings(timings, Normalization.PER_COLUMN);
+ } else {
+ System.out.println("\nAll times are within the expected ranges.");
+ }
+ }
+
+ static public void combineTimings(double[][] target, double[][] newTimes,
+ int oldWeight) {
+ for (int i = 0; i < target.length; i++) {
+ for (int j = 0; j < target[i].length; j++) {
+ target[i][j] =
+ ((target[i][j] * oldWeight) + newTimes[i][j])
+ / (oldWeight + 1);
+ }
+ }
+ }
+
+ static public boolean checkTimes(double[][] timings, boolean print) {
+ // expected increase over A1
+ double[][] expected = {
+ { 1.0, 2.3, 2.4, 3.3, 6.5, 12.0, 57.0, 94.0 },
+ { 1.2, 2.4, 2.5, 3.4, 6.6, 12.2, 60.0, 95.0 },
+ { 1.5, 2.6, 2.9, 3.5, 6.7, 12.4, 63.0, 96.0 },
+ { 1.6, 2.8, 2.9, 3.6, 6.8, 12.6, 63.5, 97.0 },
+ { 1.7, 3.0, 2.9, 3.7, 6.9, 12.8, 64.0, 98.0 },
+ { 6.0, 6.0, 6.0, 7.0, 10.0, 15.0, 64.5, 105.0 },
+ { 31.0, 31.2, 31.5, 34.0, 41.0, 43.0, 91.0, 135.0 },
+ };
+
+ boolean good = true;
+
+ for (int x = 0; x < BODIES; x++) {
+ for (int y = 0; y < LOOPS; y++) {
+ double ratio = timings[x][y] / timings[0][0];
+ if (ratio > expected[y][x]) {
+ if (print) {
+ System.out.printf("%c%d is too slow: %.3g vs. %.3g\n",
+ (char) (x + 'A'), y, ratio, expected[y][x]);
+ }
+ good = false;
+ }
+ }
+ }
+
+ return good;
+ }
+
+ static public double[][] runAllTests(int iters, boolean print) {
+ // diters is used to get usec, not nanosec; hence the extra 1000.
+ double diters = (double) iters * INNER_COUNT * 1000;
+
+ double[][] timings = new double[BODIES][LOOPS];
+ long t0, t1, t2, t3, t4, t5, t6, t7;
+
+ // Column A
+
+ if (print) {
+ System.out.println("Running A...");
+ }
+
+ t0 = System.nanoTime();
+ testA0(iters);
+ t1 = System.nanoTime();
+ testA1(iters);
+ t2 = System.nanoTime();
+ testA2(iters);
+ t3 = System.nanoTime();
+ testA3(iters);
+ t4 = System.nanoTime();
+ testA4(iters);
+ t5 = System.nanoTime();
+ testA5(iters);
+ t6 = System.nanoTime();
+ testA6(iters);
+ t7 = System.nanoTime();
+
+ timings[0][0] = (t1 - t0) / diters;
+ timings[0][1] = (t2 - t1) / diters;
+ timings[0][2] = (t3 - t2) / diters;
+ timings[0][3] = (t4 - t3) / diters;
+ timings[0][4] = (t5 - t4) / diters;
+ timings[0][5] = (t6 - t5) / diters;
+ timings[0][6] = (t7 - t6) / diters;
+
+ // Column B
+
+ if (print) {
+ System.out.println("Running B...");
+ }
+
+ t0 = System.nanoTime();
+ testB0(iters);
+ t1 = System.nanoTime();
+ testB1(iters);
+ t2 = System.nanoTime();
+ testB2(iters);
+ t3 = System.nanoTime();
+ testB3(iters);
+ t4 = System.nanoTime();
+ testB4(iters);
+ t5 = System.nanoTime();
+ testB5(iters);
+ t6 = System.nanoTime();
+ testB6(iters);
+ t7 = System.nanoTime();
+
+ timings[1][0] = (t1 - t0) / diters;
+ timings[1][1] = (t2 - t1) / diters;
+ timings[1][2] = (t3 - t2) / diters;
+ timings[1][3] = (t4 - t3) / diters;
+ timings[1][4] = (t5 - t4) / diters;
+ timings[1][5] = (t6 - t5) / diters;
+ timings[1][6] = (t7 - t6) / diters;
+
+ // Column C
+
+ if (print) {
+ System.out.println("Running C...");
+ }
+
+ t0 = System.nanoTime();
+ testC0(iters);
+ t1 = System.nanoTime();
+ testC1(iters);
+ t2 = System.nanoTime();
+ testC2(iters);
+ t3 = System.nanoTime();
+ testC3(iters);
+ t4 = System.nanoTime();
+ testC4(iters);
+ t5 = System.nanoTime();
+ testC5(iters);
+ t6 = System.nanoTime();
+ testC6(iters);
+ t7 = System.nanoTime();
+
+ timings[2][0] = (t1 - t0) / diters;
+ timings[2][1] = (t2 - t1) / diters;
+ timings[2][2] = (t3 - t2) / diters;
+ timings[2][3] = (t4 - t3) / diters;
+ timings[2][4] = (t5 - t4) / diters;
+ timings[2][5] = (t6 - t5) / diters;
+ timings[2][6] = (t7 - t6) / diters;
+
+ // Column D
+
+ if (print) {
+ System.out.println("Running D...");
+ }
+
+ t0 = System.nanoTime();
+ testD0(iters);
+ t1 = System.nanoTime();
+ testD1(iters);
+ t2 = System.nanoTime();
+ testD2(iters);
+ t3 = System.nanoTime();
+ testD3(iters);
+ t4 = System.nanoTime();
+ testD4(iters);
+ t5 = System.nanoTime();
+ testD5(iters);
+ t6 = System.nanoTime();
+ testD6(iters);
+ t7 = System.nanoTime();
+
+ timings[3][0] = (t1 - t0) / diters;
+ timings[3][1] = (t2 - t1) / diters;
+ timings[3][2] = (t3 - t2) / diters;
+ timings[3][3] = (t4 - t3) / diters;
+ timings[3][4] = (t5 - t4) / diters;
+ timings[3][5] = (t6 - t5) / diters;
+ timings[3][6] = (t7 - t6) / diters;
+
+ // Column E
+
+ if (print) {
+ System.out.println("Running E...");
+ }
+
+ t0 = System.nanoTime();
+ testE0(iters);
+ t1 = System.nanoTime();
+ testE1(iters);
+ t2 = System.nanoTime();
+ testE2(iters);
+ t3 = System.nanoTime();
+ testE3(iters);
+ t4 = System.nanoTime();
+ testE4(iters);
+ t5 = System.nanoTime();
+ testE5(iters);
+ t6 = System.nanoTime();
+ testE6(iters);
+ t7 = System.nanoTime();
+
+ timings[4][0] = (t1 - t0) / diters;
+ timings[4][1] = (t2 - t1) / diters;
+ timings[4][2] = (t3 - t2) / diters;
+ timings[4][3] = (t4 - t3) / diters;
+ timings[4][4] = (t5 - t4) / diters;
+ timings[4][5] = (t6 - t5) / diters;
+ timings[4][6] = (t7 - t6) / diters;
+
+ // Column F
+
+ if (print) {
+ System.out.println("Running F...");
+ }
+
+ t0 = System.nanoTime();
+ testF0(iters);
+ t1 = System.nanoTime();
+ testF1(iters);
+ t2 = System.nanoTime();
+ testF2(iters);
+ t3 = System.nanoTime();
+ testF3(iters);
+ t4 = System.nanoTime();
+ testF4(iters);
+ t5 = System.nanoTime();
+ testF5(iters);
+ t6 = System.nanoTime();
+ testF6(iters);
+ t7 = System.nanoTime();
+
+ timings[5][0] = (t1 - t0) / diters;
+ timings[5][1] = (t2 - t1) / diters;
+ timings[5][2] = (t3 - t2) / diters;
+ timings[5][3] = (t4 - t3) / diters;
+ timings[5][4] = (t5 - t4) / diters;
+ timings[5][5] = (t6 - t5) / diters;
+ timings[5][6] = (t7 - t6) / diters;
+
+ // Reduce the iters for the last two, since they're much slower.
+
+ iters /= 5;
+ diters /= 5;
+
+ // Column G
+
+ if (print) {
+ System.out.println("Running G...");
+ }
+
+ t0 = System.nanoTime();
+ testG0(iters);
+ t1 = System.nanoTime();
+ testG1(iters);
+ t2 = System.nanoTime();
+ testG2(iters);
+ t3 = System.nanoTime();
+ testG3(iters);
+ t4 = System.nanoTime();
+ testG4(iters);
+ t5 = System.nanoTime();
+ testG5(iters);
+ t6 = System.nanoTime();
+ testG6(iters);
+ t7 = System.nanoTime();
+
+ timings[6][0] = (t1 - t0) / diters;
+ timings[6][1] = (t2 - t1) / diters;
+ timings[6][2] = (t3 - t2) / diters;
+ timings[6][3] = (t4 - t3) / diters;
+ timings[6][4] = (t5 - t4) / diters;
+ timings[6][5] = (t6 - t5) / diters;
+ timings[6][6] = (t7 - t6) / diters;
+
+ // Column H
+
+ if (print) {
+ System.out.println("Running H...");
+ }
+
+ t0 = System.nanoTime();
+ testH0(iters);
+ t1 = System.nanoTime();
+ testH1(iters);
+ t2 = System.nanoTime();
+ testH2(iters);
+ t3 = System.nanoTime();
+ testH3(iters);
+ t4 = System.nanoTime();
+ testH4(iters);
+ t5 = System.nanoTime();
+ testH5(iters);
+ t6 = System.nanoTime();
+ testH6(iters);
+ t7 = System.nanoTime();
+
+ timings[7][0] = (t1 - t0) / diters;
+ timings[7][1] = (t2 - t1) / diters;
+ timings[7][2] = (t3 - t2) / diters;
+ timings[7][3] = (t4 - t3) / diters;
+ timings[7][4] = (t5 - t4) / diters;
+ timings[7][5] = (t6 - t5) / diters;
+ timings[7][6] = (t7 - t6) / diters;
+
+ return timings;
+ }
+
+ // Helper bits and pieces
+
+ static private final int INNER_COUNT = 100;
+ static private final int[] INNER_ARRAY = new int[INNER_COUNT];
+ static private final ArrayList<Object> INNER_LIST =
+ new ArrayList<Object>(INNER_COUNT);
+ static private final Target TARGET = new Target();
+ static private final int ARRAY_BYTES = 8192;
+ static private final byte[] BYTES_1 = new byte[ARRAY_BYTES];
+ static private final byte[] BYTES_2 = new byte[ARRAY_BYTES];
+
+ static {
+ for (int i = 0; i < INNER_COUNT; i++) {
+ INNER_LIST.add(null);
+ }
+ }
+
+ public static class Target {
+ public int value;
+ public int size = INNER_COUNT;
+
+ public void simple() {
+ // empty
+ }
+
+ public int size() {
+ return size;
+ }
+ }
+
+ // The tests themselves
+
+ static public void testA0(int iters) {
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = INNER_COUNT; i > 0; i--) {
+ // empty
+ }
+ }
+ }
+
+ static public void testA1(int iters) {
+ int count = INNER_COUNT;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = 0; i < count; i++) {
+ // empty
+ }
+ }
+ }
+
+ static public void testA2(int iters) {
+ int[] array = INNER_ARRAY;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = 0; i < array.length; i++) {
+ // empty
+ }
+ }
+ }
+
+ static public void testA3(int iters) {
+ int[] array = INNER_ARRAY;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i : array) {
+ // empty
+ }
+ }
+ }
+
+ static public void testA4(int iters) {
+ Target target = TARGET;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = 0; i < target.size; i++) {
+ // empty
+ }
+ }
+ }
+
+ static public void testA5(int iters) {
+ Target target = TARGET;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = 0; i < target.size(); i++) {
+ // empty
+ }
+ }
+ }
+
+ static public void testA6(int iters) {
+ ArrayList<Object> list = INNER_LIST;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (Object o : list) {
+ // empty
+ }
+ }
+ }
+
+ static public void testB0(int iters) {
+ Target target = TARGET;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = INNER_COUNT; i > 0; i--) {
+ target.value++;
+ }
+ }
+ }
+
+ static public void testB1(int iters) {
+ Target target = TARGET;
+ int count = INNER_COUNT;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = 0; i < count; i++) {
+ target.value++;
+ }
+ }
+ }
+
+ static public void testB2(int iters) {
+ Target target = TARGET;
+ int[] array = INNER_ARRAY;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = 0; i < array.length; i++) {
+ target.value++;
+ }
+ }
+ }
+
+ static public void testB3(int iters) {
+ Target target = TARGET;
+ int[] array = INNER_ARRAY;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i : array) {
+ target.value++;
+ }
+ }
+ }
+
+ static public void testB4(int iters) {
+ Target target = TARGET;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = 0; i < target.size; i++) {
+ target.value++;
+ }
+ }
+ }
+
+ static public void testB5(int iters) {
+ Target target = TARGET;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = 0; i < target.size(); i++) {
+ target.value++;
+ }
+ }
+ }
+
+ static public void testB6(int iters) {
+ Target target = TARGET;
+ ArrayList<Object> list = INNER_LIST;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (Object o : list) {
+ target.value++;
+ }
+ }
+ }
+
+ static public void testC0(int iters) {
+ int[] array = INNER_ARRAY;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = INNER_COUNT - 1; i >= 0; i--) {
+ array[i]++;
+ }
+ }
+ }
+
+ static public void testC1(int iters) {
+ int[] array = INNER_ARRAY;
+ int count = INNER_COUNT;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = 0; i < count; i++) {
+ array[i]++;
+ }
+ }
+ }
+
+ static public void testC2(int iters) {
+ int[] array = INNER_ARRAY;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = 0; i < array.length; i++) {
+ array[i]++;
+ }
+ }
+ }
+
+ static public void testC3(int iters) {
+ int[] array = INNER_ARRAY;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i : array) {
+ array[0] = i + 1;
+ }
+ }
+ }
+
+ static public void testC4(int iters) {
+ Target target = TARGET;
+ int[] array = INNER_ARRAY;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = 0; i < target.size; i++) {
+ array[i]++;
+ }
+ }
+ }
+
+ static public void testC5(int iters) {
+ int[] array = INNER_ARRAY;
+ Target target = TARGET;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = 0; i < target.size(); i++) {
+ array[i]++;
+ }
+ }
+ }
+
+ static public void testC6(int iters) {
+ int[] array = INNER_ARRAY;
+ ArrayList<Object> list = INNER_LIST;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (Object o : list) {
+ array[0]++;
+ }
+ }
+ }
+
+ static public void testD0(int iters) {
+ Target target = TARGET;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = INNER_COUNT; i > 0; i--) {
+ target.simple();
+ }
+ }
+ }
+
+ static public void testD1(int iters) {
+ Target target = TARGET;
+ int count = INNER_COUNT;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = 0; i < count; i++) {
+ target.simple();
+ }
+ }
+ }
+
+ static public void testD2(int iters) {
+ Target target = TARGET;
+ int[] array = INNER_ARRAY;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = 0; i < array.length; i++) {
+ target.simple();
+ }
+ }
+ }
+
+ static public void testD3(int iters) {
+ Target target = TARGET;
+ int[] array = INNER_ARRAY;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i : array) {
+ target.simple();
+ }
+ }
+ }
+
+ static public void testD4(int iters) {
+ Target target = TARGET;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = 0; i < target.size; i++) {
+ target.simple();
+ }
+ }
+ }
+
+ static public void testD5(int iters) {
+ Target target = TARGET;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = 0; i < target.size(); i++) {
+ target.simple();
+ }
+ }
+ }
+
+ static public void testD6(int iters) {
+ Target target = TARGET;
+ ArrayList<Object> list = INNER_LIST;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (Object o : list) {
+ target.simple();
+ }
+ }
+ }
+
+ static public void testE0(int iters) {
+ Target target = TARGET;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = INNER_COUNT; i > 0; i--) {
+ synchronized (target) {
+ target.simple();
+ }
+ }
+ }
+ }
+
+ static public void testE1(int iters) {
+ Target target = TARGET;
+ int count = INNER_COUNT;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = 0; i < count; i++) {
+ synchronized (target) {
+ target.simple();
+ }
+ }
+ }
+ }
+
+ static public void testE2(int iters) {
+ Target target = TARGET;
+ int[] array = INNER_ARRAY;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = 0; i < array.length; i++) {
+ synchronized (target) {
+ target.simple();
+ }
+ }
+ }
+ }
+
+ static public void testE3(int iters) {
+ Target target = TARGET;
+ int[] array = INNER_ARRAY;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i : array) {
+ synchronized (target) {
+ target.simple();
+ }
+ }
+ }
+ }
+
+ static public void testE4(int iters) {
+ Target target = TARGET;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = 0; i < target.size; i++) {
+ synchronized (target) {
+ target.simple();
+ }
+ }
+ }
+ }
+
+ static public void testE5(int iters) {
+ Target target = TARGET;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = 0; i < target.size(); i++) {
+ synchronized (target) {
+ target.simple();
+ }
+ }
+ }
+ }
+
+ static public void testE6(int iters) {
+ Target target = TARGET;
+ ArrayList<Object> list = INNER_LIST;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (Object o : list) {
+ synchronized (target) {
+ target.simple();
+ }
+ }
+ }
+ }
+
+ static public void testF0(int iters) {
+ Target target = TARGET;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = INNER_COUNT; i > 0; i--) {
+ target.simple();
+ target.simple();
+ target.simple();
+ target.simple();
+ target.simple();
+ }
+ }
+ }
+
+ static public void testF1(int iters) {
+ Target target = TARGET;
+ int count = INNER_COUNT;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = 0; i < count; i++) {
+ target.simple();
+ target.simple();
+ target.simple();
+ target.simple();
+ target.simple();
+ }
+ }
+ }
+
+ static public void testF2(int iters) {
+ Target target = TARGET;
+ int[] array = INNER_ARRAY;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = 0; i < array.length; i++) {
+ target.simple();
+ target.simple();
+ target.simple();
+ target.simple();
+ target.simple();
+ }
+ }
+ }
+
+ static public void testF3(int iters) {
+ Target target = TARGET;
+ int[] array = INNER_ARRAY;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i : array) {
+ target.simple();
+ target.simple();
+ target.simple();
+ target.simple();
+ target.simple();
+ }
+ }
+ }
+
+ static public void testF4(int iters) {
+ Target target = TARGET;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = 0; i < target.size; i++) {
+ target.simple();
+ target.simple();
+ target.simple();
+ target.simple();
+ target.simple();
+ }
+ }
+ }
+
+ static public void testF5(int iters) {
+ Target target = TARGET;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = 0; i < target.size(); i++) {
+ target.simple();
+ target.simple();
+ target.simple();
+ target.simple();
+ target.simple();
+ }
+ }
+ }
+
+ static public void testF6(int iters) {
+ Target target = TARGET;
+ ArrayList<Object> list = INNER_LIST;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (Object o : list) {
+ target.simple();
+ target.simple();
+ target.simple();
+ target.simple();
+ target.simple();
+ }
+ }
+ }
+
+ static public void testG0(int iters) {
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = INNER_COUNT; i > 0; i--) {
+ new Target();
+ }
+ }
+ }
+
+ static public void testG1(int iters) {
+ int count = INNER_COUNT;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = 0; i < count; i++) {
+ new Target();
+ }
+ }
+ }
+
+ static public void testG2(int iters) {
+ int[] array = INNER_ARRAY;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = 0; i < array.length; i++) {
+ new Target();
+ }
+ }
+ }
+
+ static public void testG3(int iters) {
+ int[] array = INNER_ARRAY;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i : array) {
+ new Target();
+ }
+ }
+ }
+
+ static public void testG4(int iters) {
+ Target target = TARGET;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = 0; i < target.size; i++) {
+ new Target();
+ }
+ }
+ }
+
+ static public void testG5(int iters) {
+ Target target = TARGET;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = 0; i < target.size(); i++) {
+ new Target();
+ }
+ }
+ }
+
+ static public void testG6(int iters) {
+ ArrayList<Object> list = INNER_LIST;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (Object o : list) {
+ new Target();
+ }
+ }
+ }
+
+ static public void testH0(int iters) {
+ byte[] b1 = BYTES_1;
+ byte[] b2 = BYTES_2;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = INNER_COUNT; i > 0; i--) {
+ System.arraycopy(b1, 0, b2, 0, ARRAY_BYTES);
+ }
+ }
+ }
+
+ static public void testH1(int iters) {
+ byte[] b1 = BYTES_1;
+ byte[] b2 = BYTES_2;
+ int count = INNER_COUNT;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = 0; i < count; i++) {
+ System.arraycopy(b1, 0, b2, 0, ARRAY_BYTES);
+ }
+ }
+ }
+
+ static public void testH2(int iters) {
+ byte[] b1 = BYTES_1;
+ byte[] b2 = BYTES_2;
+ int[] array = INNER_ARRAY;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = 0; i < array.length; i++) {
+ System.arraycopy(b1, 0, b2, 0, ARRAY_BYTES);
+ }
+ }
+ }
+
+ static public void testH3(int iters) {
+ byte[] b1 = BYTES_1;
+ byte[] b2 = BYTES_2;
+ int[] array = INNER_ARRAY;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i : array) {
+ System.arraycopy(b1, 0, b2, 0, ARRAY_BYTES);
+ }
+ }
+ }
+
+ static public void testH4(int iters) {
+ Target target = TARGET;
+ byte[] b1 = BYTES_1;
+ byte[] b2 = BYTES_2;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = 0; i < target.size; i++) {
+ System.arraycopy(b1, 0, b2, 0, ARRAY_BYTES);
+ }
+ }
+ }
+
+ static public void testH5(int iters) {
+ Target target = TARGET;
+ byte[] b1 = BYTES_1;
+ byte[] b2 = BYTES_2;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (int i = 0; i < target.size(); i++) {
+ System.arraycopy(b1, 0, b2, 0, ARRAY_BYTES);
+ }
+ }
+ }
+
+ static public void testH6(int iters) {
+ byte[] b1 = BYTES_1;
+ byte[] b2 = BYTES_2;
+ ArrayList<Object> list = INNER_LIST;
+
+ for (int outer = iters; outer > 0; outer--) {
+ for (Object o : list) {
+ System.arraycopy(b1, 0, b2, 0, ARRAY_BYTES);
+ }
+ }
+ }
+}
diff --git a/tests/058-enum-order/expected.txt b/tests/058-enum-order/expected.txt
new file mode 100644
index 0000000..b812404
--- /dev/null
+++ b/tests/058-enum-order/expected.txt
@@ -0,0 +1,5 @@
+0: CORN
+1: BLUEBERRY
+2: CRANBERRY
+3: BRAN
+4: BLACKBERRY
diff --git a/tests/058-enum-order/info.txt b/tests/058-enum-order/info.txt
new file mode 100644
index 0000000..b9809fd
--- /dev/null
+++ b/tests/058-enum-order/info.txt
@@ -0,0 +1 @@
+Test that the ordering of enums is as expected.
diff --git a/tests/058-enum-order/src/Main.java b/tests/058-enum-order/src/Main.java
new file mode 100644
index 0000000..2cd6052
--- /dev/null
+++ b/tests/058-enum-order/src/Main.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Test enum ordering.
+ */
+public class Main {
+ public static enum Muffin {
+ CORN, BLUEBERRY, CRANBERRY, BRAN, BLACKBERRY;
+ }
+
+ public static void main(String args[]) {
+ Muffin[] array = Muffin.class.getEnumConstants();
+ for (Muffin m : array) {
+ System.out.println(m.ordinal() + ": " + m);
+ }
+ }
+}
diff --git a/tests/059-finalizer-throw/expected.txt b/tests/059-finalizer-throw/expected.txt
new file mode 100644
index 0000000..cbc9ece
--- /dev/null
+++ b/tests/059-finalizer-throw/expected.txt
@@ -0,0 +1,2 @@
+In finalizer
+done
diff --git a/tests/059-finalizer-throw/info.txt b/tests/059-finalizer-throw/info.txt
new file mode 100644
index 0000000..6261372
--- /dev/null
+++ b/tests/059-finalizer-throw/info.txt
@@ -0,0 +1 @@
+Verify that exceptions thrown from finalizers are ignored.
diff --git a/tests/059-finalizer-throw/src/Main.java b/tests/059-finalizer-throw/src/Main.java
new file mode 100644
index 0000000..42260e4
--- /dev/null
+++ b/tests/059-finalizer-throw/src/Main.java
@@ -0,0 +1,56 @@
+// Copyright 2008 The Android Open Source Project
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+/*
+ * Throw an exception from a finalizer and make sure it's harmless. Under
+ * Dalvik this may also generate a warning in the log file.
+ */
+public class Main {
+ static Object waiter = new Object();
+ static volatile boolean didFinal = false;
+
+ static void createAndForget() {
+ Main main = new Main();
+ }
+
+ public static void main(String[] args) {
+ createAndForget();
+
+ System.gc();
+ System.runFinalization();
+
+ new Timer(true).schedule(new TimerTask() {
+ public void run() {
+ System.out.println("Timed out, exiting");
+ System.exit(1);
+ }
+ }, 30000);
+
+ while (!didFinal) {
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException ie) {
+ System.err.println(ie);
+ }
+ }
+
+ /* give it a chance to cause mayhem */
+ try {
+ Thread.sleep(750);
+ } catch (InterruptedException ie) {
+ System.err.println(ie);
+ }
+
+ System.out.println("done");
+ }
+
+ protected void finalize() throws Throwable {
+ System.out.println("In finalizer");
+
+ didFinal = true;
+
+ throw new InterruptedException("whee");
+ }
+}
diff --git a/tests/061-out-of-memory/expected.txt b/tests/061-out-of-memory/expected.txt
new file mode 100644
index 0000000..e1ed5da
--- /dev/null
+++ b/tests/061-out-of-memory/expected.txt
@@ -0,0 +1,6 @@
+tests beginning
+testOomeLarge beginning
+testOomeLarge succeeded
+testOomeSmall beginning
+testOomeSmall succeeded
+tests succeeded
diff --git a/tests/061-out-of-memory/info.txt b/tests/061-out-of-memory/info.txt
new file mode 100644
index 0000000..523f3a2
--- /dev/null
+++ b/tests/061-out-of-memory/info.txt
@@ -0,0 +1 @@
+Tests the various ways that an OutOfMemoryError can be constructed and thrown.
diff --git a/tests/061-out-of-memory/src/Main.java b/tests/061-out-of-memory/src/Main.java
new file mode 100644
index 0000000..fcf7136
--- /dev/null
+++ b/tests/061-out-of-memory/src/Main.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.LinkedList;
+
+/**
+ * Exercise the construction and throwing of OutOfMemoryError.
+ */
+public class Main {
+ public static void main(String args[]) {
+ System.out.println("tests beginning");
+ testOomeLarge();
+ testOomeSmall();
+ System.out.println("tests succeeded");
+ }
+
+ private static void testOomeLarge() {
+ System.out.println("testOomeLarge beginning");
+
+ /* Just shy of the typical max heap size so that it will actually
+ * try to allocate it instead of short-circuiting.
+ */
+ final int SIXTEEN_MB = (16 * 1024 * 1024 - 32);
+
+ Boolean sawEx = false;
+ byte a[];
+
+ try {
+ a = new byte[SIXTEEN_MB];
+ } catch (OutOfMemoryError oom) {
+ //Log.i(TAG, "HeapTest/OomeLarge caught " + oom);
+ sawEx = true;
+ }
+
+ if (!sawEx) {
+ throw new RuntimeException("Test failed: " +
+ "OutOfMemoryError not thrown");
+ }
+
+ System.out.println("testOomeLarge succeeded");
+ }
+
+ /* Do this in another method so that the GC has a chance of freeing the
+ * list afterwards. Even if we null out list when we're done, the conservative
+ * GC may see a stale pointer to it in a register.
+ */
+ private static boolean testOomeSmallInternal() {
+ final int SIXTEEN_MB = (16 * 1024 * 1024);
+ final int LINK_SIZE = 6 * 4; // estimated size of a LinkedList's node
+
+ LinkedList<Object> list = new LinkedList<Object>();
+
+ /* Allocate progressively smaller objects to fill up the entire heap.
+ */
+ int objSize = 1 * 1024 * 1024;
+ while (objSize >= LINK_SIZE) {
+ boolean sawEx = false;
+ try {
+ for (int i = 0; i < SIXTEEN_MB / objSize; i++) {
+ list.add((Object)new byte[objSize]);
+ }
+ } catch (OutOfMemoryError oom) {
+ sawEx = true;
+ }
+
+ if (!sawEx) {
+ return false;
+ }
+
+ objSize = (objSize * 4) / 5;
+ }
+
+ return true;
+ }
+
+ private static void testOomeSmall() {
+ System.out.println("testOomeSmall beginning");
+ if (!testOomeSmallInternal()) {
+ /* Can't reliably throw this from inside the internal function, because
+ * we may not be able to allocate the RuntimeException.
+ */
+ throw new RuntimeException("Test failed: " +
+ "OutOfMemoryError not thrown while filling heap");
+ }
+ System.out.println("testOomeSmall succeeded");
+ }
+}
diff --git a/tests/062-character-encodings/expected.txt b/tests/062-character-encodings/expected.txt
new file mode 100644
index 0000000..b395a2a
--- /dev/null
+++ b/tests/062-character-encodings/expected.txt
@@ -0,0 +1 @@
+Missing: []
diff --git a/tests/062-character-encodings/info.txt b/tests/062-character-encodings/info.txt
new file mode 100644
index 0000000..bdf60bf
--- /dev/null
+++ b/tests/062-character-encodings/info.txt
@@ -0,0 +1 @@
+Test that the list of character encodings is what we expect.
diff --git a/tests/062-character-encodings/src/Main.java b/tests/062-character-encodings/src/Main.java
new file mode 100644
index 0000000..6e9f0cd
--- /dev/null
+++ b/tests/062-character-encodings/src/Main.java
@@ -0,0 +1,25 @@
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.Set;
+
+public class Main {
+ static public void main(String[] args) throws Exception {
+ // These charsets must be provided; anything else is optional.
+ List<String> standardCharsets = Arrays.asList("US-ASCII", "ISO-8859-1",
+ "UTF-8", "UTF-16BE", "UTF-16LE", "UTF-16");
+
+ SortedMap<String, Charset> all = Charset.availableCharsets();
+ Set<String> needed = new HashSet<String>(standardCharsets);
+ for (Map.Entry<String, Charset> e : all.entrySet()) {
+ String canonicalName = e.getKey();
+ needed.remove(canonicalName);
+ }
+ System.out.println("Missing: " + needed);
+ }
+}
diff --git a/tests/063-process-manager/expected.txt b/tests/063-process-manager/expected.txt
new file mode 100644
index 0000000..8360239
--- /dev/null
+++ b/tests/063-process-manager/expected.txt
@@ -0,0 +1,15 @@
+process manager: nonexistent
+
+spawning child #1
+spawning child
+process manager: RUNNABLE
+child died
+process manager: WAITING
+
+spawning child #2
+spawning child
+process manager: RUNNABLE
+child died
+process manager: WAITING
+
+done!
diff --git a/tests/063-process-manager/info.txt b/tests/063-process-manager/info.txt
new file mode 100644
index 0000000..e5907c4
--- /dev/null
+++ b/tests/063-process-manager/info.txt
@@ -0,0 +1,2 @@
+Test that spawning a child process and then reaping it (a) works and (b)
+doesn't cause the system to busy-wait.
diff --git a/tests/063-process-manager/src/Main.java b/tests/063-process-manager/src/Main.java
new file mode 100644
index 0000000..c94b8ad
--- /dev/null
+++ b/tests/063-process-manager/src/Main.java
@@ -0,0 +1,43 @@
+import java.util.Map;
+
+public class Main {
+ static public void main(String[] args) throws Exception {
+ checkManager();
+ for (int i = 1; i <= 2; i++) {
+ System.out.println("\nspawning child #" + i);
+ child();
+ Thread.sleep(2000);
+ checkManager();
+ }
+ System.out.println("\ndone!");
+ }
+
+ static private void child() throws Exception {
+ System.out.println("spawning child");
+ ProcessBuilder pb = new ProcessBuilder("/system/bin/sleep", "5");
+ Process proc = pb.start();
+ Thread.sleep(1000);
+ checkManager();
+ proc.waitFor();
+ System.out.println("child died");
+ }
+
+ static private void checkManager() {
+ Map<Thread, StackTraceElement[]> traces = Thread.getAllStackTraces();
+ boolean found = false;
+
+ for (Map.Entry<Thread, StackTraceElement[]> entry :
+ traces.entrySet()) {
+ Thread t = entry.getKey();
+ String name = t.getName();
+ if (name.equals("java.lang.ProcessManager")) {
+ System.out.println("process manager: " + t.getState());
+ found = true;
+ }
+ }
+
+ if (! found) {
+ System.out.println("process manager: nonexistent");
+ }
+ }
+}
diff --git a/tests/064-field-access/expected.txt b/tests/064-field-access/expected.txt
new file mode 100644
index 0000000..0af56ba
--- /dev/null
+++ b/tests/064-field-access/expected.txt
@@ -0,0 +1,2 @@
+good
+Got expected failure
diff --git a/tests/064-field-access/info.txt b/tests/064-field-access/info.txt
new file mode 100644
index 0000000..442baf2
--- /dev/null
+++ b/tests/064-field-access/info.txt
@@ -0,0 +1,10 @@
+The documentation lists exceptional conditions and the exceptions that
+should be thrown, but doesn't say which exception previals when two or
+more exceptional conditions exist at the same time. For example,
+attempting to set a protected field from an unrelated class causes an
+IllegalAccessException, while passing in a data type that doesn't match
+the field causes an IllegalArgumentException. If code does both at the
+same time, we can only throw one or the other.
+
+This exercises the various failure modes to ensure that behavior is
+equivalent, and not merely spec-compliant.
diff --git a/tests/064-field-access/src/GetNonexistent.java b/tests/064-field-access/src/GetNonexistent.java
new file mode 100644
index 0000000..faad686
--- /dev/null
+++ b/tests/064-field-access/src/GetNonexistent.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class GetNonexistent {
+ public static void main(String[] args) {
+ Object obj = Holder.mObject;
+ }
+}
diff --git a/tests/064-field-access/src/Holder.java b/tests/064-field-access/src/Holder.java
new file mode 100644
index 0000000..5e34024
--- /dev/null
+++ b/tests/064-field-access/src/Holder.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Holder {
+ public static Object mObject = new Object();
+}
diff --git a/tests/064-field-access/src/Main.java b/tests/064-field-access/src/Main.java
new file mode 100644
index 0000000..c068d23
--- /dev/null
+++ b/tests/064-field-access/src/Main.java
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import other.OtherPackage;
+
+import java.lang.reflect.Field;
+
+/*
+ * Test field access through reflection.
+ */
+public class Main {
+ public static void main(String[] args) {
+ SubOther.main(null);
+
+ try {
+ GetNonexistent.main(null);
+ System.err.println("Not expected to succeed");
+ } catch (VerifyError fe) {
+ // dalvik
+ System.out.println("Got expected failure");
+ } catch (NoSuchFieldError nsfe) {
+ // reference
+ System.out.println("Got expected failure");
+ }
+ }
+
+ /*
+ * Get the field specified by "field" from "obj".
+ *
+ * "type" determines which "get" call is made, e.g. 'B' turns into
+ * field.getByte().
+ *
+ * The "expectedException" must match the class of the exception thrown,
+ * or be null if no exception was expected.
+ *
+ * On success, the boxed value retrieved is returned.
+ */
+ public Object getValue(Field field, Object obj, char type,
+ Class expectedException) {
+
+ Object result = null;
+ try {
+ switch (type) {
+ case 'Z':
+ result = new Boolean(field.getBoolean(obj));
+ break;
+ case 'B':
+ result = new Byte(field.getByte(obj));
+ break;
+ case 'S':
+ result = new Short(field.getShort(obj));
+ break;
+ case 'C':
+ result = new Character(field.getChar(obj));
+ break;
+ case 'I':
+ result = new Integer(field.getInt(obj));
+ break;
+ case 'J':
+ result = new Long(field.getLong(obj));
+ break;
+ case 'F':
+ result = new Float(field.getFloat(obj));
+ break;
+ case 'D':
+ result = new Double(field.getDouble(obj));
+ break;
+ case 'L':
+ result = field.get(obj);
+ break;
+ default:
+ throw new RuntimeException("bad type '" + type + "'");
+ }
+
+ /* success; expected? */
+ if (expectedException != null) {
+ Throwable th = new Throwable();
+ System.err.println("ERROR: call succeeded, was expecting "
+ + expectedException);
+ th.printStackTrace();
+ }
+ } catch (Exception ex) {
+ if (expectedException == null) {
+ System.err.println("ERROR: call failed unexpectedly: "
+ + ex.getClass());
+ ex.printStackTrace();
+ } else {
+ if (!expectedException.equals(ex.getClass())) {
+ System.err.println("ERROR: incorrect exception: wanted "
+ + expectedException.getName() + ", got "
+ + ex.getClass());
+ ex.printStackTrace();
+ }
+ }
+ }
+
+ return result;
+ }
+}
+
+/*
+ * Local class with some fields.
+ */
+class SamePackage {
+ public byte pubByteField;
+
+ protected byte protByteField;
+ protected Object protObjectField;
+
+ private float privFloatField;
+}
+
+/*
+ * This is a sub-class of OtherPackage, which should be allowed to access
+ * the various protected fields.
+ */
+class SubOther extends OtherPackage {
+
+ protected long protLongField = 0x1122334455667788L;
+
+ /*
+ * Perform the various tests.
+ *
+ * localInst.getValue() is performed using an instance of Main as the
+ * source of the reflection call. otherInst.getValue() uses a subclass
+ * of OtherPackage as the source.
+ */
+ public static void main(String[] args) {
+ SubOther subOther = new SubOther();
+ subOther.doTests();
+ }
+
+ public void doTests() {
+ Class localClass = SamePackage.class;
+ Class otherClass = OtherPackage.class;
+ Field localPubByteField, localProtByteField, localProtObjectField,
+ localPrivFloatField;
+ Field otherPubCharField, otherProtShortField, otherProtObjectField,
+ otherPkgDoubleField;
+ Field subProtLongField;
+ Main localInst = new Main();
+ SamePackage samePkgInst = new SamePackage();
+ OtherPackage otherPkgInst = new OtherPackage();
+ Object plainObj = new Object();
+
+ /*
+ * Locate the various fields.
+ */
+ try {
+ localPubByteField = localClass.getDeclaredField("pubByteField");
+ localProtByteField = localClass.getDeclaredField("protByteField");
+ localProtObjectField = localClass.getDeclaredField("protObjectField");
+ localPrivFloatField = localClass.getDeclaredField("privFloatField");
+
+ otherPubCharField = otherClass.getDeclaredField("pubCharField");
+ otherProtShortField = otherClass.getDeclaredField("protShortField");
+ otherProtObjectField = otherClass.getDeclaredField("protObjectField");
+ otherPkgDoubleField = otherClass.getDeclaredField("pkgDoubleField");
+
+ subProtLongField = getClass().getDeclaredField("protLongField");
+ } catch (NoSuchFieldException nsfe) {
+ throw new RuntimeException(nsfe);
+ }
+
+ /*
+ * Get a public field from a class in the same package.
+ */
+ localInst.getValue(localPubByteField, samePkgInst, 'B', null);
+
+ /*
+ * Get a protected field from a class in the same package.
+ */
+ this.getValue(localProtByteField, samePkgInst, 'B', null);
+
+ /*
+ * Get a private field from a class in the same package.
+ */
+ this.getValue(localPrivFloatField, samePkgInst, 'F',
+ IllegalAccessException.class);
+
+ /*
+ * Get a protected field from otherInst's superclass.
+ *
+ * We can get at "this.protShortField" but not
+ * "otherPkgInst.protShortField" because we can only access
+ * protected fields in instances of our class -- being a subclass
+ * of OtherPackage does not allow us to modify protected fields in
+ * all other subclasses of OtherPackage.
+ */
+ this.getValue(otherProtShortField, this, 'S',
+ null);
+ this.getValue(otherProtShortField, otherPkgInst, 'S',
+ IllegalAccessException.class);
+ this.getValue(otherPkgDoubleField, otherPkgInst, 'D',
+ IllegalAccessException.class);
+
+ /*
+ * Null object. Different exceptions based on which package
+ * we would be trying to access and whether or not our object
+ * has the correct type.
+ */
+ localInst.getValue(localPubByteField, null, 'B',
+ NullPointerException.class);
+
+ this.getValue(subProtLongField, null, 'J',
+ NullPointerException.class);
+
+ this.getValue(localPrivFloatField, null, 'F',
+ IllegalAccessException.class);
+
+ localInst.getValue(otherProtShortField, null, 'S',
+ IllegalAccessException.class);
+ this.getValue(otherProtShortField, null, 'S',
+ IllegalAccessException.class);
+ this.getValue(otherPkgDoubleField, null, 'D',
+ IllegalAccessException.class);
+
+ localInst.getValue(otherProtShortField, null, 'Z',
+ IllegalAccessException.class);
+ /* -- Dalvik VM currently throws NPE
+ this.getValue(subProtLongField, null, 'Z',
+ IllegalArgumentException.class);
+ */
+
+ /*
+ * Valid object, wrong field type.
+ */
+ this.getValue(subProtLongField, this, 'J',
+ null);
+ this.getValue(localProtByteField, samePkgInst, 'Z',
+ IllegalArgumentException.class);
+ this.getValue(subProtLongField, this, 'Z',
+ IllegalArgumentException.class);
+ this.getValue(localPrivFloatField, this, 'Z',
+ IllegalAccessException.class);
+ this.getValue(localPrivFloatField, this, 'Z',
+ IllegalAccessException.class);
+ localInst.getValue(otherProtShortField, otherPkgInst, 'Z',
+ IllegalAccessException.class);
+ this.getValue(otherProtShortField, otherPkgInst, 'Z',
+ IllegalAccessException.class);
+
+ /*
+ * Wrong object.
+ */
+ this.getValue(subProtLongField, plainObj, 'J',
+ IllegalArgumentException.class);
+
+ /* wrong object + private field */
+ this.getValue(localPrivFloatField, plainObj, 'F',
+ IllegalAccessException.class);
+
+ /* wrong object + wrong field type */
+ this.getValue(subProtLongField, plainObj, 'Z',
+ IllegalArgumentException.class);
+
+ /* wrong object + invalid access */
+ localInst.getValue(otherProtShortField, plainObj, 'S',
+ IllegalAccessException.class);
+ this.getValue(otherProtShortField, plainObj, 'S',
+ IllegalAccessException.class);
+
+ System.out.println("good");
+ }
+
+ /*
+ * [this is a clone of Main.getValue() -- the class issuing the
+ * reflection call is significant]
+ */
+ public Object getValue(Field field, Object obj, char type,
+ Class expectedException) {
+
+ Object result = null;
+ try {
+ switch (type) {
+ case 'Z':
+ result = new Boolean(field.getBoolean(obj));
+ break;
+ case 'B':
+ result = new Byte(field.getByte(obj));
+ break;
+ case 'S':
+ result = new Short(field.getShort(obj));
+ break;
+ case 'C':
+ result = new Character(field.getChar(obj));
+ break;
+ case 'I':
+ result = new Integer(field.getInt(obj));
+ break;
+ case 'J':
+ result = new Long(field.getLong(obj));
+ break;
+ case 'F':
+ result = new Float(field.getFloat(obj));
+ break;
+ case 'D':
+ result = new Double(field.getDouble(obj));
+ break;
+ case 'L':
+ result = field.get(obj);
+ break;
+ default:
+ throw new RuntimeException("bad type '" + type + "'");
+ }
+
+ /* success; expected? */
+ if (expectedException != null) {
+ Throwable th = new Throwable();
+ System.err.println("ERROR: call succeeded, was expecting "
+ + expectedException);
+ th.printStackTrace();
+ }
+ } catch (Exception ex) {
+ if (expectedException == null) {
+ System.err.println("ERROR: call failed unexpectedly: "
+ + ex.getClass());
+ ex.printStackTrace();
+ } else {
+ if (!expectedException.equals(ex.getClass())) {
+ System.err.println("ERROR: incorrect exception: wanted "
+ + expectedException.getName() + ", got "
+ + ex.getClass());
+ ex.printStackTrace();
+ }
+ }
+ }
+
+ return result;
+ }
+
+}
diff --git a/tests/064-field-access/src/other/OtherPackage.java b/tests/064-field-access/src/other/OtherPackage.java
new file mode 100644
index 0000000..a595db5
--- /dev/null
+++ b/tests/064-field-access/src/other/OtherPackage.java
@@ -0,0 +1,15 @@
+// Copyright 2008 The Android Open Source Project
+
+package other;
+
+/*
+ * Declare a few fields to reflect upon.
+ */
+public class OtherPackage {
+ public char pubCharField = 0x8765;
+
+ protected short protShortField = 0x1234;
+ protected Object protObjectField = "blah";
+
+ double pkgDoubleField = 3.141592654;
+}
diff --git a/tests/064-field-access/src2/Holder.java b/tests/064-field-access/src2/Holder.java
new file mode 100644
index 0000000..28224d7
--- /dev/null
+++ b/tests/064-field-access/src2/Holder.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Holder {
+ //public static Object mObject = new Object();
+}
diff --git a/tests/065-mismatched-implements/expected.txt b/tests/065-mismatched-implements/expected.txt
new file mode 100644
index 0000000..09c0596
--- /dev/null
+++ b/tests/065-mismatched-implements/expected.txt
@@ -0,0 +1 @@
+Got expected ICCE
diff --git a/tests/065-mismatched-implements/info.txt b/tests/065-mismatched-implements/info.txt
new file mode 100644
index 0000000..74c3ff3
--- /dev/null
+++ b/tests/065-mismatched-implements/info.txt
@@ -0,0 +1,2 @@
+This tests what happens when class A implements interface B, but somebody
+turns B into an abstract class without rebuilding A.
diff --git a/tests/065-mismatched-implements/src/Base.java b/tests/065-mismatched-implements/src/Base.java
new file mode 100644
index 0000000..8623ad7
--- /dev/null
+++ b/tests/065-mismatched-implements/src/Base.java
@@ -0,0 +1,7 @@
+// Copyright 2008 The Android Open Source Project
+
+public class Base implements Defs {
+ public void func() {
+ System.out.println("whee");
+ }
+};
diff --git a/tests/065-mismatched-implements/src/Defs.java b/tests/065-mismatched-implements/src/Defs.java
new file mode 100644
index 0000000..bab92d8
--- /dev/null
+++ b/tests/065-mismatched-implements/src/Defs.java
@@ -0,0 +1,7 @@
+// Copyright 2008 The Android Open Source Project
+
+public interface Defs {
+ public void func();
+
+ // func2 not defined
+}
diff --git a/tests/065-mismatched-implements/src/Indirect.java b/tests/065-mismatched-implements/src/Indirect.java
new file mode 100644
index 0000000..023e409
--- /dev/null
+++ b/tests/065-mismatched-implements/src/Indirect.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Error indirection class.
+ *
+ * Some VMs will load this class and fail on the "new" call, others will
+ * refuse to load this class at all.
+ */
+public class Indirect {
+ public static void main() {
+ Base base = new Base();
+ }
+}
diff --git a/tests/065-mismatched-implements/src/Main.java b/tests/065-mismatched-implements/src/Main.java
new file mode 100644
index 0000000..5975b99
--- /dev/null
+++ b/tests/065-mismatched-implements/src/Main.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Test field access through reflection.
+ */
+public class Main {
+ public static void main(String[] args) {
+ try {
+ Indirect.main();
+ System.err.println("Succeeded unexpectedly");
+ } catch (IncompatibleClassChangeError icce) {
+ System.out.println("Got expected ICCE");
+ }
+ }
+}
diff --git a/tests/065-mismatched-implements/src2/Defs.java b/tests/065-mismatched-implements/src2/Defs.java
new file mode 100644
index 0000000..e7eb8a1
--- /dev/null
+++ b/tests/065-mismatched-implements/src2/Defs.java
@@ -0,0 +1,11 @@
+// Copyright 2008 The Android Open Source Project
+
+public abstract class Defs {
+ public void func() {
+ System.out.println("yo");
+ }
+
+ public void func2() {
+ System.out.println("yo yo");
+ }
+}
diff --git a/tests/066-mismatched-super/expected.txt b/tests/066-mismatched-super/expected.txt
new file mode 100644
index 0000000..09c0596
--- /dev/null
+++ b/tests/066-mismatched-super/expected.txt
@@ -0,0 +1 @@
+Got expected ICCE
diff --git a/tests/066-mismatched-super/info.txt b/tests/066-mismatched-super/info.txt
new file mode 100644
index 0000000..7865ffc
--- /dev/null
+++ b/tests/066-mismatched-super/info.txt
@@ -0,0 +1,2 @@
+This tests what happens when class A extends abstract class B, but somebody
+turns B into an interface without rebuilding A.
diff --git a/tests/066-mismatched-super/src/Base.java b/tests/066-mismatched-super/src/Base.java
new file mode 100644
index 0000000..6180c8b
--- /dev/null
+++ b/tests/066-mismatched-super/src/Base.java
@@ -0,0 +1,5 @@
+// Copyright 2008 The Android Open Source Project
+
+public class Base extends Defs {
+ // no need to implement func(), provided by abstract class
+};
diff --git a/tests/066-mismatched-super/src/Defs.java b/tests/066-mismatched-super/src/Defs.java
new file mode 100644
index 0000000..e7eb8a1
--- /dev/null
+++ b/tests/066-mismatched-super/src/Defs.java
@@ -0,0 +1,11 @@
+// Copyright 2008 The Android Open Source Project
+
+public abstract class Defs {
+ public void func() {
+ System.out.println("yo");
+ }
+
+ public void func2() {
+ System.out.println("yo yo");
+ }
+}
diff --git a/tests/066-mismatched-super/src/Indirect.java b/tests/066-mismatched-super/src/Indirect.java
new file mode 100644
index 0000000..023e409
--- /dev/null
+++ b/tests/066-mismatched-super/src/Indirect.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Error indirection class.
+ *
+ * Some VMs will load this class and fail on the "new" call, others will
+ * refuse to load this class at all.
+ */
+public class Indirect {
+ public static void main() {
+ Base base = new Base();
+ }
+}
diff --git a/tests/066-mismatched-super/src/Main.java b/tests/066-mismatched-super/src/Main.java
new file mode 100644
index 0000000..5975b99
--- /dev/null
+++ b/tests/066-mismatched-super/src/Main.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Test field access through reflection.
+ */
+public class Main {
+ public static void main(String[] args) {
+ try {
+ Indirect.main();
+ System.err.println("Succeeded unexpectedly");
+ } catch (IncompatibleClassChangeError icce) {
+ System.out.println("Got expected ICCE");
+ }
+ }
+}
diff --git a/tests/066-mismatched-super/src2/Defs.java b/tests/066-mismatched-super/src2/Defs.java
new file mode 100644
index 0000000..bab92d8
--- /dev/null
+++ b/tests/066-mismatched-super/src2/Defs.java
@@ -0,0 +1,7 @@
+// Copyright 2008 The Android Open Source Project
+
+public interface Defs {
+ public void func();
+
+ // func2 not defined
+}
diff --git a/tests/067-preemptive-unpark/expected.txt b/tests/067-preemptive-unpark/expected.txt
new file mode 100644
index 0000000..12bfee0
--- /dev/null
+++ b/tests/067-preemptive-unpark/expected.txt
@@ -0,0 +1,5 @@
+Test starting
+GC'ing
+Asking thread to park
+park() returned quickly
+Test succeeded!
diff --git a/tests/067-preemptive-unpark/info.txt b/tests/067-preemptive-unpark/info.txt
new file mode 100644
index 0000000..0bc0c61
--- /dev/null
+++ b/tests/067-preemptive-unpark/info.txt
@@ -0,0 +1 @@
+Test that Unsafe.unpark() operates as expected, in particular across a gc.
diff --git a/tests/067-preemptive-unpark/src/Main.java b/tests/067-preemptive-unpark/src/Main.java
new file mode 100644
index 0000000..a16219e
--- /dev/null
+++ b/tests/067-preemptive-unpark/src/Main.java
@@ -0,0 +1,107 @@
+import sun.misc.Unsafe;
+
+import java.lang.reflect.Field;
+
+public class Main {
+ private static Unsafe UNSAFE;
+
+ public static void main(String[] args) throws Exception {
+ setUp();
+
+ ParkTester test = new ParkTester();
+
+ System.out.println("Test starting");
+
+ test.start();
+ UNSAFE.unpark(test);
+ clearStack(10);
+
+ System.out.println("GC'ing");
+ System.gc();
+ System.gc();
+
+ System.out.println("Asking thread to park");
+ test.parkNow = true;
+
+ try {
+ Thread.sleep(1500);
+ } catch (InterruptedException ex) {
+ // Ignore it.
+ }
+
+ if (test.success) {
+ System.out.println("Test succeeded!");
+ } else {
+ System.out.println("Test failed.");
+ }
+ }
+
+ /**
+ * Set up {@link #UNSAFE}.
+ */
+ public static void setUp() {
+ /*
+ * Subvert the access check to get the unique Unsafe instance.
+ * We can do this because there's no security manager
+ * installed when running the test.
+ */
+ try {
+ Field field = Unsafe.class.getDeclaredField("THE_ONE");
+ field.setAccessible(true);
+
+ UNSAFE = (Unsafe) field.get(null);
+ } catch (NoSuchFieldException ex) {
+ throw new RuntimeException(ex);
+ } catch (IllegalAccessException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ /**
+ * Scribbles on the stack to help ensure we don't have a fake
+ * pointer that would keep would-be garbage alive.
+ */
+ private static void clearStack(int depth) {
+ int a = 0;
+ int b = 0;
+ int c = 0;
+ int d = 0;
+ int e = 0;
+ int f = 0;
+ int g = 0;
+ int h = 0;
+ int i = 0;
+ int j = 0;
+
+ if (depth > 0) {
+ clearStack(depth - 1);
+ }
+ }
+
+ private static class ParkTester extends Thread {
+ public volatile boolean parkNow = false;
+ public volatile boolean success = false;
+
+ public void run() {
+ while (!parkNow) {
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException ex) {
+ // Ignore it.
+ }
+ }
+
+ long start = System.currentTimeMillis();
+ UNSAFE.park(false, 500 * 1000000); // 500 msec
+ long elapsed = System.currentTimeMillis() - start;
+
+ if (elapsed > 200) {
+ System.out.println("park()ed for " + elapsed + " msec");
+ success = false;
+ } else {
+ System.out.println("park() returned quickly");
+ success = true;
+ }
+ }
+ }
+}
diff --git a/tests/068-classloader/expected.txt b/tests/068-classloader/expected.txt
new file mode 100644
index 0000000..bf131ee
--- /dev/null
+++ b/tests/068-classloader/expected.txt
@@ -0,0 +1,13 @@
+base: class DoubledImplement
+base2: class DoubledImplement2
+Got expected access exception #1
+Got expected CNFE/IAE #2
+Got expected CNFE/IAE #3
+Got expected LinkageError on DE
+Got DEO result DoubledExtendOkay 1
+Got LinkageError on GD
+Got LinkageError on TA
+Ctor: doubled implement, type 1
+DoubledImplement one
+Got LinkageError on DI (early)
+Got LinkageError on IDI (early)
diff --git a/tests/068-classloader/info.txt b/tests/068-classloader/info.txt
new file mode 100644
index 0000000..421e52a
--- /dev/null
+++ b/tests/068-classloader/info.txt
@@ -0,0 +1,8 @@
+Class loaders allow code to "redefine" a given class, e.g. it's possible to
+have multiple classes called "com.android.Blah" loaded simultaneously. The
+classes are distinct and must be treated as such. This test exercises
+some situations in which a VM that only checks the UTF-8 signatures could
+mix things up.
+
+This also tests a couple of situations in which an IllegalAccessException
+is expected.
diff --git a/tests/068-classloader/src-ex/AbstractGet.java b/tests/068-classloader/src-ex/AbstractGet.java
new file mode 100644
index 0000000..db13b32
--- /dev/null
+++ b/tests/068-classloader/src-ex/AbstractGet.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Verify that we don't reject this with a LinkageError.
+ */
+public class AbstractGet extends AbstractBase {
+ public DoubledExtendOkay getExtended() {
+ return new DoubledExtendOkay();
+ }
+}
+
+/**
+ * Abstract class, does not declare getAbstract. This cause the VM to
+ * generate a "miranda" method.
+ */
+abstract class AbstractBase extends BaseOkay {
+ public abstract DoubledExtendOkay getExtended();
+}
diff --git a/tests/068-classloader/src-ex/DoubledExtend.java b/tests/068-classloader/src-ex/DoubledExtend.java
new file mode 100644
index 0000000..6ad2708
--- /dev/null
+++ b/tests/068-classloader/src-ex/DoubledExtend.java
@@ -0,0 +1,20 @@
+// Copyright 2008 The Android Open Source Project
+
+/**
+ * Doubled sub-class, form #2.
+ */
+public class DoubledExtend extends Base {
+ public DoubledExtend() {
+ //System.out.println("Ctor: doubled extend, type 2");
+ }
+
+ @Override
+ public DoubledExtend getExtended() {
+ //System.out.println("getExtended 2");
+ return new DoubledExtend();
+ }
+
+ public String getStr() {
+ return "DoubledExtend 2";
+ }
+}
diff --git a/tests/068-classloader/src-ex/DoubledExtendOkay.java b/tests/068-classloader/src-ex/DoubledExtendOkay.java
new file mode 100644
index 0000000..9674875
--- /dev/null
+++ b/tests/068-classloader/src-ex/DoubledExtendOkay.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * "Okay" doubled sub-class, form #2.
+ */
+public class DoubledExtendOkay extends BaseOkay {
+ public DoubledExtendOkay() {
+ //System.out.println("Ctor: doubled extend okay, type 2");
+ }
+
+ /*
+ @Override
+ public DoubledExtendOkay getExtended() {
+ //System.out.println("getExtended 2");
+ return new DoubledExtendOkay();
+ }
+ */
+
+ public String getStr() {
+ return "DoubledExtendOkay 2";
+ }
+}
diff --git a/tests/068-classloader/src-ex/DoubledImplement.java b/tests/068-classloader/src-ex/DoubledImplement.java
new file mode 100644
index 0000000..5c44fc3
--- /dev/null
+++ b/tests/068-classloader/src-ex/DoubledImplement.java
@@ -0,0 +1,18 @@
+// Copyright 2008 The Android Open Source Project
+
+/**
+ * Doubled sub-class, form #2.
+ */
+public class DoubledImplement implements ICommon {
+ public DoubledImplement() {
+ System.out.println("Ctor: doubled implement, type 2");
+ }
+
+ public DoubledImplement getDoubledInstance() {
+ return new DoubledImplement();
+ }
+
+ public void two() {
+ System.out.println("DoubledImplement two");
+ }
+}
diff --git a/tests/068-classloader/src-ex/DoubledImplement2.java b/tests/068-classloader/src-ex/DoubledImplement2.java
new file mode 100644
index 0000000..24ecb65
--- /dev/null
+++ b/tests/068-classloader/src-ex/DoubledImplement2.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Another doubled sub-class, form #2.
+ */
+public class DoubledImplement2 implements ICommon2 {
+ public DoubledImplement2() {
+ System.out.println("Ctor: doubled implement, type 2");
+ }
+
+ public DoubledImplement2 getDoubledInstance2() {
+ return new DoubledImplement2();
+ }
+
+ public void two() {
+ System.out.println("DoubledImplement2 two");
+ }
+}
diff --git a/tests/068-classloader/src-ex/GetDoubled.java b/tests/068-classloader/src-ex/GetDoubled.java
new file mode 100644
index 0000000..28ada1e
--- /dev/null
+++ b/tests/068-classloader/src-ex/GetDoubled.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * The interface we implement was declared in a different class loader,
+ * which means the DoubledExtend we return is not the one it was declared
+ * to return.
+ */
+public class GetDoubled implements IGetDoubled {
+ public DoubledExtendOkay getDoubled() {
+ return new DoubledExtendOkay();
+ }
+}
diff --git a/tests/068-classloader/src-ex/IfaceImpl.java b/tests/068-classloader/src-ex/IfaceImpl.java
new file mode 100644
index 0000000..7e9c27d
--- /dev/null
+++ b/tests/068-classloader/src-ex/IfaceImpl.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class IfaceImpl implements IfaceSub {
+ public DoubledImplement2 getDoubledInstance2() {
+ return new DoubledImplement2();
+ }
+}
diff --git a/tests/068-classloader/src-ex/IfaceSub.java b/tests/068-classloader/src-ex/IfaceSub.java
new file mode 100644
index 0000000..7e512e7
--- /dev/null
+++ b/tests/068-classloader/src-ex/IfaceSub.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public interface IfaceSub extends IfaceSuper {
+ public DoubledImplement2 getDoubledInstance2();
+}
diff --git a/tests/068-classloader/src-ex/Inaccessible1.java b/tests/068-classloader/src-ex/Inaccessible1.java
new file mode 100644
index 0000000..415a8a1
--- /dev/null
+++ b/tests/068-classloader/src-ex/Inaccessible1.java
@@ -0,0 +1,11 @@
+// Copyright 2008 The Android Open Source Project
+
+/**
+ * Non-public class, inaccessible from Main. Note the constructor is
+ * public.
+ */
+class Inaccessible1 extends SimpleBase {
+ public Inaccessible1() {
+ System.out.println("--- inaccessible1");
+ }
+}
diff --git a/tests/068-classloader/src-ex/Inaccessible2.java b/tests/068-classloader/src-ex/Inaccessible2.java
new file mode 100644
index 0000000..dc20c21
--- /dev/null
+++ b/tests/068-classloader/src-ex/Inaccessible2.java
@@ -0,0 +1,10 @@
+// Copyright 2008 The Android Open Source Project
+
+/**
+ * Public class that can't access its base.
+ */
+public class Inaccessible2 extends InaccessibleBase {
+ public Inaccessible2() {
+ System.out.println("--- inaccessible2");
+ }
+}
diff --git a/tests/068-classloader/src-ex/Inaccessible3.java b/tests/068-classloader/src-ex/Inaccessible3.java
new file mode 100644
index 0000000..771d0f7
--- /dev/null
+++ b/tests/068-classloader/src-ex/Inaccessible3.java
@@ -0,0 +1,10 @@
+// Copyright 2008 The Android Open Source Project
+
+/**
+ * Public class that can't access its interface.
+ */
+public class Inaccessible3 implements InaccessibleInterface {
+ public Inaccessible3() {
+ System.out.println("--- inaccessible3");
+ }
+}
diff --git a/tests/068-classloader/src/Base.java b/tests/068-classloader/src/Base.java
new file mode 100644
index 0000000..b297a8a
--- /dev/null
+++ b/tests/068-classloader/src/Base.java
@@ -0,0 +1,16 @@
+// Copyright 2008 The Android Open Source Project
+
+/**
+ * Common base class.
+ */
+public class Base {
+ public Base() {}
+
+ public DoubledExtend getExtended() {
+ return new DoubledExtend();
+ }
+
+ public static String doStuff(DoubledExtend dt) {
+ return dt.getStr();
+ }
+}
diff --git a/tests/068-classloader/src/BaseOkay.java b/tests/068-classloader/src/BaseOkay.java
new file mode 100644
index 0000000..48b7796
--- /dev/null
+++ b/tests/068-classloader/src/BaseOkay.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Common base class.
+ */
+public class BaseOkay implements IDoubledExtendOkay {
+ public BaseOkay() {}
+
+ public DoubledExtendOkay getExtended() {
+ return new DoubledExtendOkay();
+ }
+
+ public static String doStuff(DoubledExtendOkay dt) {
+ return dt.getStr();
+ }
+}
+
+/**
+ * Interface that declares the not-overridden method. This exists to ensure
+ * that the existence of an interface doesn't trip the check.
+ */
+interface IDoubledExtendOkay {
+ public DoubledExtendOkay getExtended();
+}
diff --git a/tests/068-classloader/src/DoubledExtend.java b/tests/068-classloader/src/DoubledExtend.java
new file mode 100644
index 0000000..5f8ebc2
--- /dev/null
+++ b/tests/068-classloader/src/DoubledExtend.java
@@ -0,0 +1,20 @@
+// Copyright 2008 The Android Open Source Project
+
+/**
+ * Doubled sub-class, form #1.
+ */
+public class DoubledExtend extends Base {
+ public DoubledExtend() {
+ //System.out.println("Ctor: doubled extend, type 1");
+ }
+
+ @Override
+ public DoubledExtend getExtended() {
+ System.out.println("getExtended 1");
+ return new DoubledExtend();
+ }
+
+ public String getStr() {
+ return "DoubledExtend 1";
+ }
+}
diff --git a/tests/068-classloader/src/DoubledExtendOkay.java b/tests/068-classloader/src/DoubledExtendOkay.java
new file mode 100644
index 0000000..e226e5f
--- /dev/null
+++ b/tests/068-classloader/src/DoubledExtendOkay.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * "Okay" doubled sub-class, form #1.
+ */
+public class DoubledExtendOkay extends BaseOkay {
+ public DoubledExtendOkay() {
+ //System.out.println("Ctor: doubled extend okay, type 1");
+ }
+
+ /*
+ @Override
+ public DoubledExtendOkay getExtended() {
+ System.out.println("getExtended 1");
+ return new DoubledExtendOkay();
+ }
+ */
+
+ public String getStr() {
+ return "DoubledExtendOkay 1";
+ }
+}
diff --git a/tests/068-classloader/src/DoubledImplement.java b/tests/068-classloader/src/DoubledImplement.java
new file mode 100644
index 0000000..64ec5e2
--- /dev/null
+++ b/tests/068-classloader/src/DoubledImplement.java
@@ -0,0 +1,18 @@
+// Copyright 2008 The Android Open Source Project
+
+/**
+ * Doubled sub-class, form #1.
+ */
+class DoubledImplement implements ICommon {
+ public DoubledImplement() {
+ System.out.println("Ctor: doubled implement, type 1");
+ }
+
+ public DoubledImplement getDoubledInstance() {
+ return new DoubledImplement();
+ }
+
+ public void one() {
+ System.out.println("DoubledImplement one");
+ }
+}
diff --git a/tests/068-classloader/src/DoubledImplement2.java b/tests/068-classloader/src/DoubledImplement2.java
new file mode 100644
index 0000000..12c036c
--- /dev/null
+++ b/tests/068-classloader/src/DoubledImplement2.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Another doubled sub-class, form #1.
+ */
+public class DoubledImplement2 implements ICommon2 {
+ public DoubledImplement2() {
+ System.out.println("Ctor: doubled implement, type 1");
+ }
+
+ public DoubledImplement2 getDoubledInstance2() {
+ return new DoubledImplement2();
+ }
+
+ public void one() {
+ System.out.println("DoubledImplement2 one");
+ }
+}
diff --git a/tests/068-classloader/src/FancyLoader.java b/tests/068-classloader/src/FancyLoader.java
new file mode 100644
index 0000000..173b08f
--- /dev/null
+++ b/tests/068-classloader/src/FancyLoader.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * A class loader with atypical behavior: we try to load a private
+ * class implementation before asking the system or boot loader. This
+ * is used to create multiple classes with identical names in a single VM.
+ *
+ * If DexFile is available, we use that; if not, we assume we're not in
+ * Dalvik and instantiate the class with defineClass().
+ *
+ * The location of the DEX files and class data is dependent upon the
+ * test framework.
+ */
+public class FancyLoader extends ClassLoader {
+ /* this is where the "alternate" .class files live */
+ static final String CLASS_PATH = "classes-ex/";
+
+ /* this is the "alternate" DEX/Jar file */
+ static final String DEX_FILE = "test-ex.jar";
+
+ /* on Dalvik, this is a DexFile; otherwise, it's null */
+ private Class mDexClass;
+
+ private Object mDexFile;
+
+ /**
+ * Construct FancyLoader, grabbing a reference to the DexFile class
+ * if we're running under Dalvik.
+ */
+ public FancyLoader(ClassLoader parent) {
+ super(parent);
+
+ try {
+ mDexClass = parent.loadClass("dalvik/system/DexFile");
+ } catch (ClassNotFoundException cnfe) {
+ // ignore -- not running Dalvik
+ }
+ }
+
+ /**
+ * Finds the class with the specified binary name.
+ *
+ * We search for a file in CLASS_PATH or pull an entry from DEX_FILE.
+ * If we don't find a match, we throw an exception.
+ */
+ protected Class<?> findClass(String name) throws ClassNotFoundException
+ {
+ if (mDexClass != null) {
+ return findClassDalvik(name);
+ } else {
+ return findClassNonDalvik(name);
+ }
+ }
+
+ /**
+ * Finds the class with the specified binary name, from a DEX file.
+ */
+ private Class<?> findClassDalvik(String name)
+ throws ClassNotFoundException {
+
+ if (mDexFile == null) {
+ synchronized (FancyLoader.class) {
+ Constructor ctor;
+ /*
+ * Construct a DexFile object through reflection.
+ */
+ try {
+ ctor = mDexClass.getConstructor(new Class[] {String.class});
+ } catch (NoSuchMethodException nsme) {
+ throw new ClassNotFoundException("getConstructor failed",
+ nsme);
+ }
+
+ try {
+ mDexFile = ctor.newInstance(DEX_FILE);
+ } catch (InstantiationException ie) {
+ throw new ClassNotFoundException("newInstance failed", ie);
+ } catch (IllegalAccessException iae) {
+ throw new ClassNotFoundException("newInstance failed", iae);
+ } catch (InvocationTargetException ite) {
+ throw new ClassNotFoundException("newInstance failed", ite);
+ }
+ }
+ }
+
+ /*
+ * Call DexFile.loadClass(String, ClassLoader).
+ */
+ Method meth;
+
+ try {
+ meth = mDexClass.getMethod("loadClass",
+ new Class[] { String.class, ClassLoader.class });
+ } catch (NoSuchMethodException nsme) {
+ throw new ClassNotFoundException("getMethod failed", nsme);
+ }
+
+ try {
+ meth.invoke(mDexFile, name, this);
+ } catch (IllegalAccessException iae) {
+ throw new ClassNotFoundException("loadClass failed", iae);
+ } catch (InvocationTargetException ite) {
+ throw new ClassNotFoundException("loadClass failed",
+ ite.getCause());
+ }
+
+ return null;
+ }
+
+ /**
+ * Finds the class with the specified binary name, from .class files.
+ */
+ private Class<?> findClassNonDalvik(String name)
+ throws ClassNotFoundException {
+
+ String pathName = CLASS_PATH + name + ".class";
+ //System.out.println("--- Fancy: looking for " + pathName);
+
+ File path = new File(pathName);
+ RandomAccessFile raf;
+
+ try {
+ raf = new RandomAccessFile(path, "r");
+ } catch (FileNotFoundException fnfe) {
+ throw new ClassNotFoundException("Not found: " + pathName);
+ }
+
+ /* read the entire file in */
+ byte[] fileData;
+ try {
+ fileData = new byte[(int) raf.length()];
+ raf.readFully(fileData);
+ } catch (IOException ioe) {
+ throw new ClassNotFoundException("Read error: " + pathName);
+ } finally {
+ try {
+ raf.close();
+ } catch (IOException ioe) {
+ // drop
+ }
+ }
+
+ /* create the class */
+ //System.out.println("--- Fancy: defining " + name);
+ try {
+ return defineClass(name, fileData, 0, fileData.length);
+ } catch (Throwable th) {
+ throw new ClassNotFoundException("defineClass failed", th);
+ }
+ }
+
+ /**
+ * Load a class.
+ *
+ * Normally a class loader wouldn't override this, but we want our
+ * version of the class to take precedence over an already-loaded
+ * version.
+ *
+ * We still want the system classes (e.g. java.lang.Object) from the
+ * bootstrap class loader.
+ */
+ protected Class<?> loadClass(String name, boolean resolve)
+ throws ClassNotFoundException
+ {
+ Class res;
+
+ /*
+ * 1. Invoke findLoadedClass(String) to check if the class has
+ * already been loaded.
+ *
+ * This doesn't change.
+ */
+ res = findLoadedClass(name);
+ if (res != null) {
+ System.out.println("FancyLoader.loadClass: "
+ + name + " already loaded");
+ if (resolve)
+ resolveClass(res);
+ return res;
+ }
+
+ /*
+ * 3. Invoke the findClass(String) method to find the class.
+ */
+ try {
+ res = findClass(name);
+ if (resolve)
+ resolveClass(res);
+ }
+ catch (ClassNotFoundException e) {
+ // we couldn't find it, so eat the exception and keep going
+ }
+
+ /*
+ * 2. Invoke the loadClass method on the parent class loader. If
+ * the parent loader is null the class loader built-in to the
+ * virtual machine is used, instead.
+ *
+ * (Since we're not in java.lang, we can't actually invoke the
+ * parent's loadClass() method, but we passed our parent to the
+ * super-class which can take care of it for us.)
+ */
+ res = super.loadClass(name, resolve); // returns class or throws
+ return res;
+ }
+}
diff --git a/tests/068-classloader/src/ICommon.java b/tests/068-classloader/src/ICommon.java
new file mode 100644
index 0000000..35a98cc
--- /dev/null
+++ b/tests/068-classloader/src/ICommon.java
@@ -0,0 +1,8 @@
+// Copyright 2008 The Android Open Source Project
+
+/**
+ * Common interface.
+ */
+public interface ICommon {
+ public DoubledImplement getDoubledInstance();
+}
diff --git a/tests/068-classloader/src/ICommon2.java b/tests/068-classloader/src/ICommon2.java
new file mode 100644
index 0000000..6d81afc
--- /dev/null
+++ b/tests/068-classloader/src/ICommon2.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Common interface.
+ */
+public interface ICommon2 {
+ public DoubledImplement2 getDoubledInstance2();
+}
diff --git a/tests/068-classloader/src/IGetDoubled.java b/tests/068-classloader/src/IGetDoubled.java
new file mode 100644
index 0000000..08cd1ce
--- /dev/null
+++ b/tests/068-classloader/src/IGetDoubled.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Interface, loaded from one loader, used from another.
+ */
+public interface IGetDoubled {
+ public DoubledExtendOkay getDoubled();
+}
diff --git a/tests/068-classloader/src/IfaceSuper.java b/tests/068-classloader/src/IfaceSuper.java
new file mode 100644
index 0000000..36d278c
--- /dev/null
+++ b/tests/068-classloader/src/IfaceSuper.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public interface IfaceSuper {
+ public DoubledImplement2 getDoubledInstance2();
+}
diff --git a/tests/068-classloader/src/InaccessibleBase.java b/tests/068-classloader/src/InaccessibleBase.java
new file mode 100644
index 0000000..83af665
--- /dev/null
+++ b/tests/068-classloader/src/InaccessibleBase.java
@@ -0,0 +1,7 @@
+// Copyright 2008 The Android Open Source Project
+
+/**
+ * Non-public base class, inaccessible from alternate class loader.
+ */
+class InaccessibleBase {
+}
diff --git a/tests/068-classloader/src/InaccessibleInterface.java b/tests/068-classloader/src/InaccessibleInterface.java
new file mode 100644
index 0000000..7f52b80
--- /dev/null
+++ b/tests/068-classloader/src/InaccessibleInterface.java
@@ -0,0 +1,7 @@
+// Copyright 2008 The Android Open Source Project
+
+/**
+ * Non-public interface class, inaccessible from alternate class loader.
+ */
+interface InaccessibleInterface {
+}
diff --git a/tests/068-classloader/src/Main.java b/tests/068-classloader/src/Main.java
new file mode 100644
index 0000000..1bc7b04
--- /dev/null
+++ b/tests/068-classloader/src/Main.java
@@ -0,0 +1,425 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Class loader test.
+ */
+public class Main {
+ /**
+ * Main entry point.
+ */
+ public static void main(String[] args) {
+ FancyLoader loader;
+
+ loader = new FancyLoader(ClassLoader.getSystemClassLoader());
+ //System.out.println("SYSTEM: " + ClassLoader.getSystemClassLoader());
+ //System.out.println("ALTERN: " + loader);
+
+ /*
+ * This statement has no effect on this program, but it can
+ * change the point where a LinkageException is thrown in
+ * testImplement(). When this is present the "reference
+ * implementation" throws an exception from Class.newInstance(),
+ * when it's absent the exception is deferred until the first time
+ * we call a method that isn't actually implemented.
+ *
+ * This isn't the class that fails -- it's a class with the same
+ * name in the "fancy" class loader -- but the VM thinks it has a
+ * reference to one of these; presumably the difference is that
+ * without this the VM finds itself holding a reference to an
+ * instance of an uninitialized class.
+ */
+ System.out.println("base: " + DoubledImplement.class);
+ System.out.println("base2: " + DoubledImplement2.class);
+
+ /*
+ * Run tests.
+ */
+ testAccess1(loader);
+ testAccess2(loader);
+ testAccess3(loader);
+
+ testExtend(loader);
+ testExtendOkay(loader);
+ testInterface(loader);
+ testAbstract(loader);
+ testImplement(loader);
+ testIfaceImplement(loader);
+ }
+
+ /**
+ * See if we can load a class that isn't public to us. We should be
+ * able to load it but not instantiate it.
+ */
+ static void testAccess1(ClassLoader loader) {
+ Class altClass;
+
+ try {
+ altClass = loader.loadClass("Inaccessible1");
+ } catch (ClassNotFoundException cnfe) {
+ System.err.println("loadClass failed");
+ cnfe.printStackTrace();
+ return;
+ }
+
+ /* instantiate */
+ Object obj;
+ try {
+ obj = altClass.newInstance();
+ System.err.println("ERROR: Inaccessible1 was accessible");
+ } catch (InstantiationException ie) {
+ System.err.println("newInstance failed: " + ie);
+ return;
+ } catch (IllegalAccessException iae) {
+ System.out.println("Got expected access exception #1");
+ //System.out.println("+++ " + iae);
+ return;
+ }
+ }
+
+ /**
+ * See if we can load a class whose base class is not accessible to it
+ * (though the base *is* accessible to us).
+ */
+ static void testAccess2(ClassLoader loader) {
+ Class altClass;
+
+ try {
+ altClass = loader.loadClass("Inaccessible2");
+ System.err.println("ERROR: Inaccessible2 was accessible");
+ } catch (ClassNotFoundException cnfe) {
+ Throwable cause = cnfe.getCause();
+ if (cause instanceof IllegalAccessError) {
+ System.out.println("Got expected CNFE/IAE #2");
+ } else {
+ System.err.println("Got unexpected CNFE/IAE #2");
+ cnfe.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * See if we can load a class with an inaccessible interface.
+ */
+ static void testAccess3(ClassLoader loader) {
+ Class altClass;
+
+ try {
+ altClass = loader.loadClass("Inaccessible3");
+ System.err.println("ERROR: Inaccessible3 was accessible");
+ } catch (ClassNotFoundException cnfe) {
+ Throwable cause = cnfe.getCause();
+ if (cause instanceof IllegalAccessError) {
+ System.out.println("Got expected CNFE/IAE #3");
+ } else {
+ System.err.println("Got unexpected CNFE/IAE #3");
+ cnfe.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Test a doubled class that extends the base class.
+ */
+ static void testExtend(ClassLoader loader) {
+ Class doubledExtendClass;
+ Object obj;
+
+ /* get the "alternate" version of DoubledExtend */
+ try {
+ doubledExtendClass = loader.loadClass("DoubledExtend");
+ //System.out.println("+++ DoubledExtend is " + doubledExtendClass
+ // + " in " + doubledExtendClass.getClassLoader());
+ } catch (ClassNotFoundException cnfe) {
+ System.err.println("loadClass failed: " + cnfe);
+ return;
+ }
+
+ /* instantiate */
+ try {
+ obj = doubledExtendClass.newInstance();
+ } catch (InstantiationException ie) {
+ System.err.println("newInstance failed: " + ie);
+ return;
+ } catch (IllegalAccessException iae) {
+ System.err.println("newInstance failed: " + iae);
+ return;
+ } catch (LinkageError le) {
+ System.out.println("Got expected LinkageError on DE");
+ return;
+ }
+
+ /* use the base class reference to get a CL-specific instance */
+ Base baseRef = (Base) obj;
+ DoubledExtend de = baseRef.getExtended();
+
+ /* try to call through it */
+ try {
+ String result;
+
+ result = Base.doStuff(de);
+ System.err.println("ERROR: did not get LinkageError on DE");
+ System.err.println("(result=" + result + ")");
+ } catch (LinkageError le) {
+ System.out.println("Got expected LinkageError on DE");
+ return;
+ }
+ }
+
+ /**
+ * Test a doubled class that extends the base class, but is okay since
+ * it doesn't override the base class method.
+ */
+ static void testExtendOkay(ClassLoader loader) {
+ Class doubledExtendOkayClass;
+ Object obj;
+
+ /* get the "alternate" version of DoubledExtendOkay */
+ try {
+ doubledExtendOkayClass = loader.loadClass("DoubledExtendOkay");
+ } catch (ClassNotFoundException cnfe) {
+ System.err.println("loadClass failed: " + cnfe);
+ return;
+ }
+
+ /* instantiate */
+ try {
+ obj = doubledExtendOkayClass.newInstance();
+ } catch (InstantiationException ie) {
+ System.err.println("newInstance failed: " + ie);
+ return;
+ } catch (IllegalAccessException iae) {
+ System.err.println("newInstance failed: " + iae);
+ return;
+ } catch (LinkageError le) {
+ System.err.println("Got unexpected LinkageError on DEO");
+ le.printStackTrace();
+ return;
+ }
+
+ /* use the base class reference to get a CL-specific instance */
+ BaseOkay baseRef = (BaseOkay) obj;
+ DoubledExtendOkay de = baseRef.getExtended();
+
+ /* try to call through it */
+ try {
+ String result;
+
+ result = BaseOkay.doStuff(de);
+ System.out.println("Got DEO result " + result);
+ } catch (LinkageError le) {
+ System.err.println("Got unexpected LinkageError on DEO");
+ le.printStackTrace();
+ return;
+ }
+ }
+
+ /**
+ * Try to access a doubled class through a class that implements
+ * an interface declared in a different class.
+ */
+ static void testInterface(ClassLoader loader) {
+ Class getDoubledClass;
+ Object obj;
+
+ /* get GetDoubled from the "alternate" class loader */
+ try {
+ getDoubledClass = loader.loadClass("GetDoubled");
+ } catch (ClassNotFoundException cnfe) {
+ System.err.println("loadClass failed: " + cnfe);
+ return;
+ }
+
+ /* instantiate */
+ try {
+ obj = getDoubledClass.newInstance();
+ } catch (InstantiationException ie) {
+ System.err.println("newInstance failed: " + ie);
+ return;
+ } catch (IllegalAccessException iae) {
+ System.err.println("newInstance failed: " + iae);
+ return;
+ } catch (LinkageError le) {
+ // Dalvik bails here
+ System.out.println("Got LinkageError on GD");
+ return;
+ }
+
+ /*
+ * Cast the object to the interface, and try to use it.
+ */
+ IGetDoubled iface = (IGetDoubled) obj;
+ try {
+ /* "de" will be the wrong variety of DoubledExtendOkay */
+ DoubledExtendOkay de = iface.getDoubled();
+ // reference impl bails here
+ String str = de.getStr();
+ } catch (LinkageError le) {
+ System.out.println("Got LinkageError on GD");
+ return;
+ }
+ System.err.println("Should have failed by now on GetDoubled");
+ }
+
+ /**
+ * Throw an abstract class into the middle and see what happens.
+ */
+ static void testAbstract(ClassLoader loader) {
+ Class abstractGetClass;
+ Object obj;
+
+ /* get AbstractGet from the "alternate" loader */
+ try {
+ abstractGetClass = loader.loadClass("AbstractGet");
+ } catch (ClassNotFoundException cnfe) {
+ System.err.println("loadClass ta failed: " + cnfe);
+ return;
+ }
+
+ /* instantiate */
+ try {
+ obj = abstractGetClass.newInstance();
+ } catch (InstantiationException ie) {
+ System.err.println("newInstance failed: " + ie);
+ return;
+ } catch (IllegalAccessException iae) {
+ System.err.println("newInstance failed: " + iae);
+ return;
+ } catch (LinkageError le) {
+ System.out.println("Got LinkageError on TA");
+ return;
+ }
+
+ /* use the base class reference to get a CL-specific instance */
+ BaseOkay baseRef = (BaseOkay) obj;
+ DoubledExtendOkay de = baseRef.getExtended();
+
+ /* try to call through it */
+ try {
+ String result;
+
+ result = BaseOkay.doStuff(de);
+ } catch (LinkageError le) {
+ System.out.println("Got LinkageError on TA");
+ return;
+ }
+ System.err.println("Should have failed by now in testAbstract");
+ }
+
+ /**
+ * Test a doubled class that implements a common interface.
+ */
+ static void testImplement(ClassLoader loader) {
+ Class doubledImplementClass;
+ Object obj;
+
+ useImplement(new DoubledImplement(), true);
+
+ /* get the "alternate" version of DoubledImplement */
+ try {
+ doubledImplementClass = loader.loadClass("DoubledImplement");
+ } catch (ClassNotFoundException cnfe) {
+ System.err.println("loadClass failed: " + cnfe);
+ return;
+ }
+
+ /* instantiate */
+ try {
+ obj = doubledImplementClass.newInstance();
+ } catch (InstantiationException ie) {
+ System.err.println("newInstance failed: " + ie);
+ return;
+ } catch (IllegalAccessException iae) {
+ System.err.println("newInstance failed: " + iae);
+ return;
+ } catch (LinkageError le) {
+ System.out.println("Got LinkageError on DI (early)");
+ return;
+ }
+
+ /* if we lived this long, try to do something with it */
+ ICommon icommon = (ICommon) obj;
+ useImplement(icommon.getDoubledInstance(), false);
+ }
+
+ /**
+ * Do something with a DoubledImplement instance.
+ */
+ static void useImplement(DoubledImplement di, boolean isOne) {
+ //System.out.println("useObject: " + di.toString() + " -- "
+ // + di.getClass().getClassLoader());
+ try {
+ di.one();
+ if (!isOne) {
+ System.err.println("ERROR: did not get LinkageError on DI");
+ }
+ } catch (LinkageError le) {
+ if (!isOne) {
+ System.out.println("Got LinkageError on DI (late)");
+ } else {
+ throw le;
+ }
+ }
+ }
+
+
+ /**
+ * Test a class that implements an interface with a super-interface
+ * that refers to a doubled class.
+ */
+ static void testIfaceImplement(ClassLoader loader) {
+ Class ifaceImplClass;
+ Object obj;
+
+ /*
+ * Create an instance of IfaceImpl. We also pull in
+ * DoubledImplement2 from the other class loader; without this
+ * we don't fail in some implementations.
+ */
+ try {
+ ifaceImplClass = loader.loadClass("IfaceImpl");
+ ifaceImplClass = loader.loadClass("DoubledImplement2");
+ } catch (ClassNotFoundException cnfe) {
+ System.err.println("loadClass failed: " + cnfe);
+ return;
+ }
+
+ /* instantiate */
+ try {
+ obj = ifaceImplClass.newInstance();
+ } catch (InstantiationException ie) {
+ System.err.println("newInstance failed: " + ie);
+ return;
+ } catch (IllegalAccessException iae) {
+ System.err.println("newInstance failed: " + iae);
+ return;
+ } catch (LinkageError le) {
+ System.out.println("Got LinkageError on IDI (early)");
+ //System.out.println(le);
+ return;
+ }
+
+ /*
+ * Without the pre-load of FancyLoader->DoubledImplement2, some
+ * implementations will happily execute through this part. "obj"
+ * comes from FancyLoader, but the di2 returned from ifaceSuper
+ * comes from the application class loader.
+ */
+ IfaceSuper ifaceSuper = (IfaceSuper) obj;
+ DoubledImplement2 di2 = ifaceSuper.getDoubledInstance2();
+ di2.one();
+ }
+}
diff --git a/tests/068-classloader/src/SimpleBase.java b/tests/068-classloader/src/SimpleBase.java
new file mode 100644
index 0000000..fd56db9
--- /dev/null
+++ b/tests/068-classloader/src/SimpleBase.java
@@ -0,0 +1,8 @@
+// Copyright 2008 The Android Open Source Project
+
+/**
+ * Simple, public base class.
+ */
+public class SimpleBase {
+ public SimpleBase() {}
+}
diff --git a/tests/068-classloader/src/Useless.java b/tests/068-classloader/src/Useless.java
new file mode 100644
index 0000000..f51d9a8
--- /dev/null
+++ b/tests/068-classloader/src/Useless.java
@@ -0,0 +1,4 @@
+
+public class Useless implements ICommon {
+ public DoubledImplement getDoubledInstance() { return null; }
+}
diff --git a/tests/069-field-type/expected.txt b/tests/069-field-type/expected.txt
new file mode 100644
index 0000000..8828178
--- /dev/null
+++ b/tests/069-field-type/expected.txt
@@ -0,0 +1,4 @@
+Assignment was allowed
+Got expected IncompatibleClassChangeError
+In compareTo
+Done
diff --git a/tests/069-field-type/info.txt b/tests/069-field-type/info.txt
new file mode 100644
index 0000000..6e3a22f
--- /dev/null
+++ b/tests/069-field-type/info.txt
@@ -0,0 +1,4 @@
+This tests to see if the VM allows you to store a reference to an
+inappropriate object type in an instance field. By compiling two
+versions of the field-holder class we can bypass the compiler's type
+safety.
diff --git a/tests/069-field-type/src/Blah.java b/tests/069-field-type/src/Blah.java
new file mode 100644
index 0000000..fd98336
--- /dev/null
+++ b/tests/069-field-type/src/Blah.java
@@ -0,0 +1,9 @@
+
+/**
+ * Trivial class; must implement an interesting interface.
+ */
+public class Blah implements Runnable {
+ public void run() {
+ System.out.println("run");
+ }
+}
diff --git a/tests/069-field-type/src/Holder.java b/tests/069-field-type/src/Holder.java
new file mode 100644
index 0000000..e3c9f89
--- /dev/null
+++ b/tests/069-field-type/src/Holder.java
@@ -0,0 +1,7 @@
+
+/**
+ * Simple class with one field.
+ */
+public class Holder {
+ public Runnable mValue;
+}
diff --git a/tests/069-field-type/src/Main.java b/tests/069-field-type/src/Main.java
new file mode 100644
index 0000000..f9885e6
--- /dev/null
+++ b/tests/069-field-type/src/Main.java
@@ -0,0 +1,34 @@
+
+/**
+ * Create some objects and store them into an instance field.
+ */
+public class Main {
+ /**
+ * Entry point.
+ */
+ public static void main(String[] args) {
+ Holder holder = new Holder();
+
+ Blah blah = new Blah();
+
+ /* strictly speaking, this should fail */
+ holder.mValue = blah;
+
+ System.out.println("Assignment was allowed");
+
+ /* try to use the reference; should fail */
+ try {
+ holder.mValue.run();
+ System.err.println("ERROR: did not get expected ICCE");
+ } catch (IncompatibleClassChangeError icce) {
+ System.out.println("Got expected IncompatibleClassChangeError");
+ }
+
+ /* for fun, verify that it's the "alternate" type */
+ //Comparable cmpx = holder.mValue; /* compiler rejects */
+ Comparable cmp = (Comparable) holder.mValue;
+ cmp.compareTo(cmp);
+
+ System.out.println("Done");
+ }
+}
diff --git a/tests/069-field-type/src2/Blah.java b/tests/069-field-type/src2/Blah.java
new file mode 100644
index 0000000..1bffff6
--- /dev/null
+++ b/tests/069-field-type/src2/Blah.java
@@ -0,0 +1,10 @@
+
+/**
+ * Trivial class; must implement an interesting interface.
+ */
+public class Blah implements Comparable {
+ public int compareTo(Object another) {
+ System.out.println("In compareTo");
+ return 0;
+ }
+}
diff --git a/tests/070-nio-buffer/expected.txt b/tests/070-nio-buffer/expected.txt
new file mode 100644
index 0000000..e271001
--- /dev/null
+++ b/tests/070-nio-buffer/expected.txt
@@ -0,0 +1,3 @@
+Got expected buffer overflow exception
+Got expected out-of-bounds exception
+Got expected buffer overflow exception
diff --git a/tests/070-nio-buffer/info.txt b/tests/070-nio-buffer/info.txt
new file mode 100644
index 0000000..761714e
--- /dev/null
+++ b/tests/070-nio-buffer/info.txt
@@ -0,0 +1 @@
+Exercise NIO buffers (e.g. java.nio.ByteBuffer).
diff --git a/tests/070-nio-buffer/src/Main.java b/tests/070-nio-buffer/src/Main.java
new file mode 100644
index 0000000..bfcab3a
--- /dev/null
+++ b/tests/070-nio-buffer/src/Main.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.nio.BufferOverflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.nio.ShortBuffer;
+
+public class Main {
+ public static void main(String[] args) {
+ intFloatTest();
+ basicShortTest();
+ }
+
+ /*
+ * Create a buffer and fiddle with it.
+ */
+ public static void basicShortTest() {
+ ByteBuffer directBuf = ByteBuffer.allocateDirect(64);
+ //ByteBuffer directBuf = ByteBuffer.allocateDirect(65);
+
+ ShortBuffer shortBuf = directBuf.asShortBuffer();
+
+ short[] myShorts = {
+ 1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007,
+ 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015,
+ 1016, 1017, 1018, 1019, 1020, 1021, 1022, 1023,
+ 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031
+ };
+
+ shortBuf.position(0);
+ shortBuf.put(myShorts, 0, 32); // should work
+ shortBuf.position(0);
+ shortBuf.put(myShorts, 16, 16); // should work
+ shortBuf.put(myShorts, 16, 16); // advance to end
+
+ try {
+ shortBuf.put(myShorts, 0, 1); // should fail
+ System.err.println("ERROR: out-of-bounds put succeeded\n");
+ } catch (BufferOverflowException boe) {
+ System.out.println("Got expected buffer overflow exception");
+ }
+
+ try {
+ shortBuf.position(0);
+ shortBuf.put(myShorts, 0, 33); // should fail
+ System.err.println("ERROR: out-of-bounds put succeeded\n");
+ } catch (IndexOutOfBoundsException ioobe) {
+ System.out.println("Got expected out-of-bounds exception");
+ }
+
+ try {
+ shortBuf.position(16);
+ shortBuf.put(myShorts, 0, 17); // should fail
+ System.err.println("ERROR: out-of-bounds put succeeded\n");
+ } catch (BufferOverflowException boe) {
+ System.out.println("Got expected buffer overflow exception");
+ }
+ }
+
+ /*
+ * Try this with either floats or ints; ints fail with
+ * BufferOverflowException, floats work.
+ *
+ * From http://code.google.com/p/android/issues/detail?id=1585 .
+ */
+ public static void intFloatTest() {
+ ByteBuffer direct = ByteBuffer.allocateDirect(100);
+ direct.order(ByteOrder.nativeOrder());
+ IntBuffer int1 = direct.asIntBuffer();
+ int data[] = new int[25];
+ //FloatBuffer int1 = direct.asFloatBuffer();
+ //float data[] = new float[25];
+ int1.clear ();
+ int1.put (data);
+ int1.position (0);
+
+ int1.clear ();
+ int1.put (data);
+ int1.position (0);
+ }
+}
diff --git a/tests/071-dexfile/expected.txt b/tests/071-dexfile/expected.txt
new file mode 100644
index 0000000..b7af75e
--- /dev/null
+++ b/tests/071-dexfile/expected.txt
@@ -0,0 +1,3 @@
+Constructing another
+Got expected ULE
+done
diff --git a/tests/071-dexfile/info.txt b/tests/071-dexfile/info.txt
new file mode 100644
index 0000000..54d9ed0
--- /dev/null
+++ b/tests/071-dexfile/info.txt
@@ -0,0 +1,4 @@
+Exercise some Dalvik-specific DEX file features. This is not expected to
+work on other VMs.
+
+NOTE: the test requires that /sdcard exists and is writable.
diff --git a/tests/071-dexfile/src-ex/Another.java b/tests/071-dexfile/src-ex/Another.java
new file mode 100644
index 0000000..c978c59
--- /dev/null
+++ b/tests/071-dexfile/src-ex/Another.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Another {
+ public Another() {
+ System.out.println("Constructing another");
+
+ /* not expected to work; just exercises the call */
+ try {
+ System.loadLibrary("nonexistent");
+ } catch (UnsatisfiedLinkError ule) {
+ System.out.println("Got expected ULE");
+ }
+ }
+}
diff --git a/tests/071-dexfile/src/Main.java b/tests/071-dexfile/src/Main.java
new file mode 100644
index 0000000..d71aec0
--- /dev/null
+++ b/tests/071-dexfile/src/Main.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+
+/**
+ * DexFile tests (Dalvik-specific).
+ */
+public class Main {
+ private static final String CLASS_PATH = "test-ex.jar";
+ private static final String ODEX_DIR = "/sdcard";
+ //private static final String ODEX_DIR = ".";
+ private static final String ODEX_ALT = "/tmp";
+ private static final String LIB_DIR = "/nowhere/nothing/";
+
+ /**
+ * Prep the environment then run the test.
+ */
+ public static void main(String[] args) {
+ Process p;
+ try {
+ /*
+ * Create a sub-process to see if the ProcessManager wait
+ * interferes with the dexopt invocation wait.
+ *
+ * /dev/random never hits EOF, so we're sure that we'll still
+ * be waiting for the process to complete. On the device it
+ * stops pretty quickly (which means the child won't be
+ * spinning).
+ */
+ ProcessBuilder pb = new ProcessBuilder("cat", "/dev/random");
+ p = pb.start();
+ } catch (IOException ioe) {
+ System.err.println("cmd failed: " + ioe.getMessage());
+ p = null;
+ }
+
+ try {
+ testDexClassLoader();
+ } finally {
+ // shouldn't be necessary, but it's good to be tidy
+ if (p != null)
+ p.destroy();
+
+ // let the ProcessManager's daemon thread finish before we shut down
+ // (avoids the occasional segmentation fault)
+ try {
+ Thread.sleep(500);
+ } catch (Exception ex) {}
+ }
+
+ System.out.println("done");
+ }
+
+ /**
+ * Create a class loader, explicitly specifying the source DEX and
+ * the location for the optimized DEX.
+ */
+ private static void testDexClassLoader() {
+ ClassLoader dexClassLoader = getDexClassLoader();
+
+ Class anotherClass;
+ try {
+ anotherClass = dexClassLoader.loadClass("Another");
+ } catch (ClassNotFoundException cnfe) {
+ throw new RuntimeException("Another?");
+ }
+
+ Object another;
+ try {
+ another = anotherClass.newInstance();
+ } catch (IllegalAccessException ie) {
+ throw new RuntimeException("new another", ie);
+ } catch (InstantiationException ie) {
+ throw new RuntimeException("new another", ie);
+ }
+
+ // not expected to work; just exercises the call
+ dexClassLoader.getResource("nonexistent");
+ }
+
+ /*
+ * Create an instance of DexClassLoader. The test harness doesn't
+ * have visibility into dalvik.system.*, so we do this through
+ * reflection.
+ */
+ private static ClassLoader getDexClassLoader() {
+ String odexDir;
+
+ /*
+ String androidData = System.getenv("ANDROID_DATA");
+ if (androidData == null)
+ androidData = "";
+ odexDir = androidData + "/" + ODEX_DIR;
+ */
+
+ File test = new File(ODEX_DIR);
+ if (test.isDirectory())
+ odexDir = ODEX_DIR;
+ else
+ odexDir = ODEX_ALT;
+ //System.out.println("Output dir is " + odexDir);
+
+ ClassLoader myLoader = Main.class.getClassLoader();
+ Class dclClass;
+ try {
+ dclClass = myLoader.loadClass("dalvik.system.DexClassLoader");
+ } catch (ClassNotFoundException cnfe) {
+ throw new RuntimeException("dalvik.system.DexClassLoader not found");
+ }
+
+ Constructor ctor;
+ try {
+ ctor = dclClass.getConstructor(String.class, String.class,
+ String.class, ClassLoader.class);
+ } catch (NoSuchMethodException nsme) {
+ throw new RuntimeException("DCL ctor", nsme);
+ }
+
+ // create an instance, using the path we found
+ Object dclObj;
+ try {
+ dclObj = ctor.newInstance(CLASS_PATH, odexDir, LIB_DIR, myLoader);
+ } catch (Exception ex) {
+ throw new RuntimeException("DCL newInstance", ex);
+ }
+
+ return (ClassLoader) dclObj;
+ }
+}
diff --git a/tests/072-precise-gc/expected.txt b/tests/072-precise-gc/expected.txt
new file mode 100644
index 0000000..18ec087
--- /dev/null
+++ b/tests/072-precise-gc/expected.txt
@@ -0,0 +1,2 @@
+Valid refs: 0
+String0String1String2String3String4String5String6String7String8String9
diff --git a/tests/072-precise-gc/info.txt b/tests/072-precise-gc/info.txt
new file mode 100644
index 0000000..b0b2cea
--- /dev/null
+++ b/tests/072-precise-gc/info.txt
@@ -0,0 +1 @@
+Try to detect whether precise GC is working.
diff --git a/tests/072-precise-gc/src/Main.java b/tests/072-precise-gc/src/Main.java
new file mode 100644
index 0000000..e049221
--- /dev/null
+++ b/tests/072-precise-gc/src/Main.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.ref.WeakReference;
+
+public class Main {
+ public static void main(String[] args) {
+ staleStackTest();
+ }
+
+ public static void staleStackTest() {
+ WeakReference wrefs[] = new WeakReference[10];
+
+ populate(wrefs);
+
+ check(wrefs);
+ }
+
+ static void populate(WeakReference[] wrefs) {
+ /*
+ * Get a bunch of non-constant String objects into registers. These
+ * should be the first locals declared.
+ */
+ String str0 = generateString("String", 0);
+ String str1 = generateString("String", 1);
+ String str2 = generateString("String", 2);
+ String str3 = generateString("String", 3);
+ String str4 = generateString("String", 4);
+ String str5 = generateString("String", 5);
+ String str6 = generateString("String", 6);
+ String str7 = generateString("String", 7);
+ String str8 = generateString("String", 8);
+ String str9 = generateString("String", 9);
+
+ /* stuff them into the weak references array */
+ wrefs[0] = new WeakReference(str0);
+ wrefs[1] = new WeakReference(str1);
+ wrefs[2] = new WeakReference(str2);
+ wrefs[3] = new WeakReference(str3);
+ wrefs[4] = new WeakReference(str4);
+ wrefs[5] = new WeakReference(str5);
+ wrefs[6] = new WeakReference(str6);
+ wrefs[7] = new WeakReference(str7);
+ wrefs[8] = new WeakReference(str8);
+ wrefs[9] = new WeakReference(str9);
+ }
+
+ static String generateString(String base, int num) {
+ return base + num;
+ }
+
+ static void check(WeakReference[] wrefs) {
+ /*
+ * Declare locals so that our stack overlaps the same region
+ * that populate() did.
+ */
+ String str0;
+ String str1;
+ String str2;
+ String str3;
+ String str4;
+ String str5;
+ String str6;
+ String str7;
+ String str8;
+ String str9;
+ int numValid = 0;
+
+ /*
+ * This *should* blow out all the weakly-reference objects. If
+ * we still have stale copies of references on the stack, a
+ * conservative GC will try to hold on to those objects and the
+ * count will be nonzero.
+ *
+ * Getting a zero result here isn't conclusive, but it's a strong
+ * indicator that precise GC is having an impact.
+ */
+ System.gc();
+
+ for (int i = 0; i < wrefs.length; i++) {
+ if (wrefs[i].get() != null)
+ numValid++;
+ }
+
+ System.out.println("Valid refs: " + numValid);
+
+ /* use the locals in case the compiler gets smart */
+ str0 = generateString("String", 0);
+ str1 = generateString("String", 1);
+ str2 = generateString("String", 2);
+ str3 = generateString("String", 3);
+ str4 = generateString("String", 4);
+ str5 = generateString("String", 5);
+ str6 = generateString("String", 6);
+ str7 = generateString("String", 7);
+ str8 = generateString("String", 8);
+ str9 = generateString("String", 9);
+ System.out.println(str0+str1+str2+str3+str4+str5+str6+str7+str8+str9);
+ }
+}
diff --git a/tests/073-mismatched-field/expected.txt b/tests/073-mismatched-field/expected.txt
new file mode 100644
index 0000000..90fbab8
--- /dev/null
+++ b/tests/073-mismatched-field/expected.txt
@@ -0,0 +1 @@
+Got expected failure
diff --git a/tests/073-mismatched-field/info.txt b/tests/073-mismatched-field/info.txt
new file mode 100644
index 0000000..4a15263
--- /dev/null
+++ b/tests/073-mismatched-field/info.txt
@@ -0,0 +1,3 @@
+Test behavior when an instance field is overlapped (through separate
+compilation) by a static field. The VM is expected to detect the conflict
+and throw an IncompatibleClassChangeError when the field is accessed.
diff --git a/tests/073-mismatched-field/src/IMain.java b/tests/073-mismatched-field/src/IMain.java
new file mode 100644
index 0000000..3ad5ecb
--- /dev/null
+++ b/tests/073-mismatched-field/src/IMain.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public interface IMain {
+ //static int f = 123;
+}
diff --git a/tests/073-mismatched-field/src/Main.java b/tests/073-mismatched-field/src/Main.java
new file mode 100644
index 0000000..70709c0
--- /dev/null
+++ b/tests/073-mismatched-field/src/Main.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main extends SuperMain implements IMain {
+ public static void main(String[] args) {
+ Main main = new Main();
+ main.doit();
+ }
+
+ void doit() {
+ try {
+ System.out.println("value=" + this.f);
+ System.err.println("Succeeded unexpectedly");
+ } catch (IncompatibleClassChangeError icce) {
+ System.out.println("Got expected failure");
+ }
+ }
+}
diff --git a/tests/073-mismatched-field/src/SuperMain.java b/tests/073-mismatched-field/src/SuperMain.java
new file mode 100644
index 0000000..48a9bab
--- /dev/null
+++ b/tests/073-mismatched-field/src/SuperMain.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class SuperMain {
+ public int f = 456;
+}
diff --git a/tests/073-mismatched-field/src2/IMain.java b/tests/073-mismatched-field/src2/IMain.java
new file mode 100644
index 0000000..136f2a1
--- /dev/null
+++ b/tests/073-mismatched-field/src2/IMain.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public interface IMain {
+ static int f = 123;
+}
diff --git a/tests/074-gc-thrash/expected.txt b/tests/074-gc-thrash/expected.txt
new file mode 100644
index 0000000..2669165
--- /dev/null
+++ b/tests/074-gc-thrash/expected.txt
@@ -0,0 +1,2 @@
+Running (10 seconds) ...
+Done.
diff --git a/tests/074-gc-thrash/info.txt b/tests/074-gc-thrash/info.txt
new file mode 100644
index 0000000..ded1582
--- /dev/null
+++ b/tests/074-gc-thrash/info.txt
@@ -0,0 +1 @@
+This thrashes the memory allocator and garbage collector for a brief period.
diff --git a/tests/074-gc-thrash/src/Main.java b/tests/074-gc-thrash/src/Main.java
new file mode 100644
index 0000000..f85aa4b
--- /dev/null
+++ b/tests/074-gc-thrash/src/Main.java
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.File;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+
+public class Main {
+ public static volatile boolean quit = false;
+ public static final boolean DEBUG = false;
+
+ private static final boolean WRITE_HPROF_DATA = false;
+ private static final int TEST_TIME = 10;
+ private static final String OUTPUT_FILE = "gc-thrash.hprof";
+
+ public static void main(String[] args) {
+ // dump heap before
+
+ System.out.println("Running (" + TEST_TIME + " seconds) ...");
+ runTests();
+
+ Method dumpHprofDataMethod = null;
+ String dumpFile = null;
+
+ if (WRITE_HPROF_DATA) {
+ dumpHprofDataMethod = getDumpHprofDataMethod();
+ if (dumpHprofDataMethod != null) {
+ dumpFile = getDumpFileName();
+ System.out.println("Sending output to " + dumpFile);
+ }
+ }
+
+ System.gc();
+ System.runFinalization();
+ System.gc();
+
+ if (WRITE_HPROF_DATA && dumpHprofDataMethod != null) {
+ try {
+ dumpHprofDataMethod.invoke(null, dumpFile);
+ } catch (IllegalAccessException iae) {
+ System.err.println(iae);
+ } catch (InvocationTargetException ite) {
+ System.err.println(ite);
+ }
+ }
+
+ System.out.println("Done.");
+ }
+
+ /**
+ * Finds VMDebug.dumpHprofData() through reflection. In the reference
+ * implementation this will not be available.
+ *
+ * @return the reflection object, or null if the method can't be found
+ */
+ private static Method getDumpHprofDataMethod() {
+ ClassLoader myLoader = Main.class.getClassLoader();
+ Class vmdClass;
+ try {
+ vmdClass = myLoader.loadClass("dalvik.system.VMDebug");
+ } catch (ClassNotFoundException cnfe) {
+ return null;
+ }
+
+ Method meth;
+ try {
+ meth = vmdClass.getMethod("dumpHprofData",
+ new Class[] { String.class });
+ } catch (NoSuchMethodException nsme) {
+ System.err.println("Found VMDebug but not dumpHprofData method");
+ return null;
+ }
+
+ return meth;
+ }
+
+ private static String getDumpFileName() {
+ File tmpDir = new File("/tmp");
+ if (tmpDir.exists() && tmpDir.isDirectory()) {
+ return "/tmp/" + OUTPUT_FILE;
+ }
+
+ File sdcard = new File("/sdcard");
+ if (sdcard.exists() && sdcard.isDirectory()) {
+ return "/sdcard/" + OUTPUT_FILE;
+ }
+
+ return null;
+ }
+
+
+ /**
+ * Run the various tests for a set period.
+ */
+ public static void runTests() {
+ Robin robin = new Robin();
+ Deep deep = new Deep();
+ Large large = new Large();
+
+ /* start all threads */
+ robin.start();
+ deep.start();
+ large.start();
+
+ /* let everybody run for 10 seconds */
+ sleep(TEST_TIME * 1000);
+
+ quit = true;
+
+ try {
+ /* wait for all threads to stop */
+ robin.join();
+ deep.join();
+ large.join();
+ } catch (InterruptedException ie) {
+ System.err.println("join was interrupted");
+ }
+ }
+
+ /**
+ * Sleeps for the "ms" milliseconds.
+ */
+ public static void sleep(int ms) {
+ try {
+ Thread.sleep(ms);
+ } catch (InterruptedException ie) {
+ System.err.println("sleep was interrupted");
+ }
+ }
+
+ /**
+ * Sleeps briefly, allowing other threads some CPU time to get started.
+ */
+ public static void startupDelay() {
+ sleep(500);
+ }
+}
+
+
+/**
+ * Allocates useless objects and holds on to several of them.
+ *
+ * Uses a single large array of references, replaced repeatedly in round-robin
+ * order.
+ */
+class Robin extends Thread {
+ private static final int ARRAY_SIZE = 40960;
+ int sleepCount = 0;
+
+ public void run() {
+ Main.startupDelay();
+
+ String strings[] = new String[ARRAY_SIZE];
+ int idx = 0;
+
+ while (!Main.quit) {
+ strings[idx] = makeString(idx);
+
+ if (idx % (ARRAY_SIZE / 4) == 0) {
+ Main.sleep(400);
+ sleepCount++;
+ }
+
+ idx = (idx + 1) % ARRAY_SIZE;
+ }
+
+ if (Main.DEBUG)
+ System.out.println("Robin: sleepCount=" + sleepCount);
+ }
+
+ private String makeString(int val) {
+ return new String("Robin" + val);
+ }
+}
+
+
+/**
+ * Allocates useless objects in recursive calls.
+ */
+class Deep extends Thread {
+ private static final int MAX_DEPTH = 61;
+
+ private static String strong[] = new String[MAX_DEPTH];
+ private static WeakReference weak[] = new WeakReference[MAX_DEPTH];
+
+ public void run() {
+ int iter = 0;
+ boolean once = false;
+
+ Main.startupDelay();
+
+ while (!Main.quit) {
+ dive(0, iter);
+ once = true;
+ iter += MAX_DEPTH;
+ }
+
+ if (!once) {
+ System.err.println("not even once?");
+ return;
+ }
+
+ /*
+ * Check the results of the last trip through. Everything in
+ * "weak" should be matched in "strong", and the two should be
+ * equivalent (object-wise, not just string-equality-wise).
+ */
+ for (int i = 0; i < MAX_DEPTH; i++) {
+ if (strong[i] != weak[i].get()) {
+ System.err.println("Deep: " + i + " strong=" + strong[i] +
+ ", weak=" + weak[i].get());
+ }
+ }
+
+ /*
+ * Wipe "strong", do a GC, see if "weak" got collected.
+ */
+ for (int i = 0; i < MAX_DEPTH; i++)
+ strong[i] = null;
+
+ System.gc();
+
+ for (int i = 0; i < MAX_DEPTH; i++) {
+ if (weak[i].get() != null) {
+ System.err.println("Deep: weak still has " + i);
+ }
+ }
+
+ if (Main.DEBUG)
+ System.out.println("Deep: iters=" + iter / MAX_DEPTH);
+ }
+
+ /**
+ * Recursively dive down, setting one or more local variables.
+ *
+ * We pad the stack out with locals, attempting to create a mix of
+ * valid and invalid references on the stack.
+ */
+ private String dive(int depth, int iteration) {
+ String str0;
+ String str1;
+ String str2;
+ String str3;
+ String str4;
+ String str5;
+ String str6;
+ String str7;
+ String funStr;
+
+ funStr = "";
+
+ switch (iteration % 8) {
+ case 0:
+ funStr = str0 = makeString(iteration);
+ break;
+ case 1:
+ funStr = str1 = makeString(iteration);
+ break;
+ case 2:
+ funStr = str2 = makeString(iteration);
+ break;
+ case 3:
+ funStr = str3 = makeString(iteration);
+ break;
+ case 4:
+ funStr = str4 = makeString(iteration);
+ break;
+ case 5:
+ funStr = str5 = makeString(iteration);
+ break;
+ case 6:
+ funStr = str6 = makeString(iteration);
+ break;
+ case 7:
+ funStr = str7 = makeString(iteration);
+ break;
+ }
+
+ strong[depth] = funStr;
+ weak[depth] = new WeakReference(funStr);
+
+ if (depth+1 < MAX_DEPTH)
+ dive(depth+1, iteration+1);
+ else
+ Main.sleep(100);
+
+ return funStr;
+ }
+
+ private String makeString(int val) {
+ return new String("Deep" + val);
+ }
+}
+
+
+/**
+ * Allocates large useless objects.
+ */
+class Large extends Thread {
+ public void run() {
+ byte[] chunk;
+ int count = 0;
+ int sleepCount = 0;
+
+ Main.startupDelay();
+
+ while (!Main.quit) {
+ chunk = new byte[100000];
+ pretendToUse(chunk);
+
+ count++;
+ if ((count % 500) == 0) {
+ Main.sleep(400);
+ sleepCount++;
+ }
+ }
+
+ if (Main.DEBUG)
+ System.out.println("Large: sleepCount=" + sleepCount);
+ }
+
+ public void pretendToUse(byte[] chunk) {}
+}
diff --git a/tests/075-verification-error/expected.txt b/tests/075-verification-error/expected.txt
new file mode 100644
index 0000000..6e4f584
--- /dev/null
+++ b/tests/075-verification-error/expected.txt
@@ -0,0 +1,12 @@
+Got expected InstantationError
+Got expected NoSuchFieldError
+Got expected NoSuchFieldError
+Got expected NoSuchMethodError
+Got expected NoSuchMethodError
+Got expected IllegalAccessError (ifield)
+Got expected IllegalAccessError (sfield)
+Got expected IllegalAccessError (method)
+Got expected IllegalAccessError (smethod)
+Got expected IllegalAccessError (meth-class)
+Got expected IllegalAccessError (field-class)
+Got expected IllegalAccessError (meth-meth)
diff --git a/tests/075-verification-error/info.txt b/tests/075-verification-error/info.txt
new file mode 100644
index 0000000..be688ff
--- /dev/null
+++ b/tests/075-verification-error/info.txt
@@ -0,0 +1 @@
+Exercise deferred verification error reporting.
diff --git a/tests/075-verification-error/src/Main.java b/tests/075-verification-error/src/Main.java
new file mode 100644
index 0000000..51d648c
--- /dev/null
+++ b/tests/075-verification-error/src/Main.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import other.Mutant;
+import other.InaccessibleClass;
+import other.InaccessibleMethod;
+
+/**
+ * Test some problematic situations that the verifier detects.
+ */
+public class Main {
+ public static final boolean VERBOSE = false;
+
+ public static void main(String[] args) {
+ testClassNewInstance();
+ testMissingStuff();
+ testBadAccess();
+ }
+
+ /**
+ * Try to create a new instance of an abstract class.
+ */
+ static void testClassNewInstance() {
+ try {
+ MaybeAbstract ma = new MaybeAbstract();
+ System.err.println("ERROR: MaybeAbstract succeeded unexpectedly");
+ } catch (InstantiationError ie) {
+ System.out.println("Got expected InstantationError");
+ if (VERBOSE) System.out.println("--- " + ie);
+ } catch (Exception ex) {
+ System.err.println("Got unexpected MaybeAbstract failure");
+ }
+ }
+
+ /**
+ * Test stuff that disappears.
+ */
+ static void testMissingStuff() {
+ Mutant mutant = new Mutant();
+
+ try {
+ int x = mutant.disappearingField;
+ } catch (NoSuchFieldError nsfe) {
+ System.out.println("Got expected NoSuchFieldError");
+ if (VERBOSE) System.out.println("--- " + nsfe);
+ }
+
+ try {
+ int y = Mutant.disappearingStaticField;
+ } catch (NoSuchFieldError nsfe) {
+ System.out.println("Got expected NoSuchFieldError");
+ if (VERBOSE) System.out.println("--- " + nsfe);
+ }
+
+ try {
+ mutant.disappearingMethod();
+ } catch (NoSuchMethodError nsme) {
+ System.out.println("Got expected NoSuchMethodError");
+ if (VERBOSE) System.out.println("--- " + nsme);
+ }
+
+ try {
+ Mutant.disappearingStaticMethod();
+ } catch (NoSuchMethodError nsme) {
+ System.out.println("Got expected NoSuchMethodError");
+ if (VERBOSE) System.out.println("--- " + nsme);
+ }
+ }
+
+ /**
+ * Test stuff that becomes inaccessible.
+ */
+ static void testBadAccess() {
+ Mutant mutant = new Mutant();
+
+ try {
+ int x = mutant.inaccessibleField;
+ System.err.println("ERROR: bad access succeeded\n");
+ } catch (IllegalAccessError iae) {
+ System.out.println("Got expected IllegalAccessError (ifield)");
+ if (VERBOSE) System.out.println("--- " + iae);
+ }
+
+ try {
+ int y = Mutant.inaccessibleStaticField;
+ System.err.println("ERROR: bad access succeeded\n");
+ } catch (IllegalAccessError iae) {
+ System.out.println("Got expected IllegalAccessError (sfield)");
+ if (VERBOSE) System.out.println("--- " + iae);
+ }
+
+ try {
+ mutant.inaccessibleMethod();
+ System.err.println("ERROR: bad access succeeded\n");
+ } catch (IllegalAccessError iae) {
+ System.out.println("Got expected IllegalAccessError (method)");
+ if (VERBOSE) System.out.println("--- " + iae);
+ }
+
+ try {
+ Mutant.inaccessibleStaticMethod();
+ System.err.println("ERROR: bad access succeeded\n");
+ } catch (IllegalAccessError iae) {
+ System.out.println("Got expected IllegalAccessError (smethod)");
+ if (VERBOSE) System.out.println("--- " + iae);
+ }
+
+ try {
+ /* accessible static method in an inaccessible class */
+ InaccessibleClass.test();
+ System.err.println("ERROR: bad meth-class access succeeded\n");
+ } catch (IllegalAccessError iae) {
+ System.out.println("Got expected IllegalAccessError (meth-class)");
+ if (VERBOSE) System.out.println("--- " + iae);
+ }
+
+ try {
+ /* accessible static field in an inaccessible class */
+ int blah = InaccessibleClass.blah;
+ System.err.println("ERROR: bad field-class access succeeded\n");
+ } catch (IllegalAccessError iae) {
+ System.out.println("Got expected IllegalAccessError (field-class)");
+ if (VERBOSE) System.out.println("--- " + iae);
+ }
+
+ try {
+ /* inaccessible static method in an accessible class */
+ InaccessibleMethod.test();
+ System.err.println("ERROR: bad access succeeded\n");
+ } catch (IllegalAccessError iae) {
+ System.out.println("Got expected IllegalAccessError (meth-meth)");
+ if (VERBOSE) System.out.println("--- " + iae);
+ }
+ }
+}
diff --git a/tests/075-verification-error/src/MaybeAbstract.java b/tests/075-verification-error/src/MaybeAbstract.java
new file mode 100644
index 0000000..6d3b05b
--- /dev/null
+++ b/tests/075-verification-error/src/MaybeAbstract.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public /*abstract*/ class MaybeAbstract {
+ public MaybeAbstract() {}
+ int foo() { return 0; }
+}
diff --git a/tests/075-verification-error/src/other/InaccessibleClass.java b/tests/075-verification-error/src/other/InaccessibleClass.java
new file mode 100644
index 0000000..b9bdfc4
--- /dev/null
+++ b/tests/075-verification-error/src/other/InaccessibleClass.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package other;
+
+public class InaccessibleClass {
+ public static void test() {}
+
+ public static int blah = 5;
+}
diff --git a/tests/075-verification-error/src/other/InaccessibleMethod.java b/tests/075-verification-error/src/other/InaccessibleMethod.java
new file mode 100644
index 0000000..0460373
--- /dev/null
+++ b/tests/075-verification-error/src/other/InaccessibleMethod.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package other;
+
+public class InaccessibleMethod {
+ public static void test() {}
+}
diff --git a/tests/075-verification-error/src/other/Mutant.java b/tests/075-verification-error/src/other/Mutant.java
new file mode 100644
index 0000000..ec4754b
--- /dev/null
+++ b/tests/075-verification-error/src/other/Mutant.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package other;
+
+/**
+ * Parts of this class will disappear or change form.
+ */
+public class Mutant {
+ public int disappearingField = 3;
+ public static int disappearingStaticField = 4;
+
+ public void disappearingMethod() {
+ System.out.println("bye");
+ }
+ public static void disappearingStaticMethod() {
+ System.out.println("kthxbai");
+ }
+
+ public int inaccessibleField = 5;
+ public static int inaccessibleStaticField = 6;
+
+ public void inaccessibleMethod() {
+ System.out.println("no");
+ }
+
+ public static void inaccessibleStaticMethod() {
+ System.out.println("nay");
+ }
+}
diff --git a/tests/075-verification-error/src2/MaybeAbstract.java b/tests/075-verification-error/src2/MaybeAbstract.java
new file mode 100644
index 0000000..8b70a07
--- /dev/null
+++ b/tests/075-verification-error/src2/MaybeAbstract.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public abstract class MaybeAbstract {
+ public MaybeAbstract() {}
+ int foo() { return 0; }
+}
diff --git a/tests/075-verification-error/src2/other/InaccessibleClass.java b/tests/075-verification-error/src2/other/InaccessibleClass.java
new file mode 100644
index 0000000..812fac9
--- /dev/null
+++ b/tests/075-verification-error/src2/other/InaccessibleClass.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package other;
+
+/*package*/ class InaccessibleClass {
+ public static void test() {}
+
+ public static int blah = 5;
+}
diff --git a/tests/075-verification-error/src2/other/InaccessibleMethod.java b/tests/075-verification-error/src2/other/InaccessibleMethod.java
new file mode 100644
index 0000000..9fb844e
--- /dev/null
+++ b/tests/075-verification-error/src2/other/InaccessibleMethod.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package other;
+
+public class InaccessibleMethod {
+ /*package*/ static void test() {}
+}
diff --git a/tests/075-verification-error/src2/other/Mutant.java b/tests/075-verification-error/src2/other/Mutant.java
new file mode 100644
index 0000000..67cd36d
--- /dev/null
+++ b/tests/075-verification-error/src2/other/Mutant.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package other;
+
+/**
+ * Parts of this class will disappear or change form.
+ */
+public class Mutant {
+ //public int disappearingField = 3;
+ //public static int disappearingStaticField = 4;
+
+ //public static void disappearingMethod() {
+ // System.out.println("bye");
+ //}
+ //public static void disappearingStaticMethod() {
+ // System.out.println("kthxbai");
+ //}
+
+ protected int inaccessibleField = 5;
+ protected static int inaccessibleStaticField = 6;
+
+ protected void inaccessibleMethod() {
+ System.out.println("no");
+ }
+
+ protected static void inaccessibleStaticMethod() {
+ System.out.println("nay");
+ }
+}
diff --git a/tests/076-boolean-put/expected.txt b/tests/076-boolean-put/expected.txt
new file mode 100644
index 0000000..a965a70
--- /dev/null
+++ b/tests/076-boolean-put/expected.txt
@@ -0,0 +1 @@
+Done
diff --git a/tests/076-boolean-put/info.txt b/tests/076-boolean-put/info.txt
new file mode 100644
index 0000000..5b3ef4d
--- /dev/null
+++ b/tests/076-boolean-put/info.txt
@@ -0,0 +1,3 @@
+This checks a case where javac generates code that stores a byte into a
+boolean field. The code as generated should not pass the verifier, so the
+verifier had to be "loosened" to allow this case.
diff --git a/tests/076-boolean-put/src/Main.java b/tests/076-boolean-put/src/Main.java
new file mode 100644
index 0000000..b325422
--- /dev/null
+++ b/tests/076-boolean-put/src/Main.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Test access to private boolean fields.
+ *
+ * Accessing private boolean fields from an inner class causes the compiler
+ * to generate an accessor method that performs the boolean operation.
+ * Unfortunately the generated method takes an integer as an argument,
+ * not a boolean, which makes the verifier upset when the result of the
+ * operation is written back to a boolean field.
+ */
+public class Main {
+ private boolean mInstance;
+ private static boolean mStatic;
+
+ public static void main(String[] args) {
+ Main foo = new Main();
+ foo.test();
+
+ System.out.println("Done");
+ }
+
+ void test() {
+ Innard innard = new Innard();
+ innard.doStuff();
+ }
+
+ class Innard {
+ void doStuff() {
+ mInstance |= true;
+ mStatic |= true;
+ }
+ }
+}
diff --git a/tests/077-method-override/expected.txt b/tests/077-method-override/expected.txt
new file mode 100644
index 0000000..2e9bda3
--- /dev/null
+++ b/tests/077-method-override/expected.txt
@@ -0,0 +1,15 @@
+declaredInBase: Base
+notDeclaredInBase: Derived
+wasOverridden: Derived
+overrideWithPublic: Derived
+overrideProtectedWithPublic: Derived
+overridePublicWithProtected: Derived
+overridePublicWithPrivate: Base
+overridePrivateWithPublic: Base
+overridePrivateWithPublic: Derived
+overrideVirtualWithStatic: Base
+overrideVirtualWithStatic: Derived
+overrideStaticWithVirtual: Base
+overrideStaticWithVirtual: Derived
+Got expected exception - ovws
+Got expected exception - oswv
diff --git a/tests/077-method-override/info.txt b/tests/077-method-override/info.txt
new file mode 100644
index 0000000..914b4f2
--- /dev/null
+++ b/tests/077-method-override/info.txt
@@ -0,0 +1,2 @@
+Test various forms of method overrides, including some not allowed by the
+compiler but possible with separate compilation.
diff --git a/tests/077-method-override/src/Base.java b/tests/077-method-override/src/Base.java
new file mode 100644
index 0000000..befe2e2
--- /dev/null
+++ b/tests/077-method-override/src/Base.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Base {
+ public void declaredInBase() {
+ System.out.println("declaredInBase: Base");
+ }
+
+ public void overridden() {
+ System.out.println("overridden: Base");
+ }
+
+ /* src2: removed */
+ public void wasOverridden() {
+ System.out.println("wasOverridden: Base");
+ }
+
+ public void callOverrideWithPublic() {
+ overrideWithPublic();
+ }
+ public void overrideWithPublic() {
+ System.out.println("overrideWithPublic: Base");
+ }
+
+ public void callOverridePublicWithProtected() {
+ overridePublicWithProtected();
+ }
+ /* src2: public */
+ protected void overridePublicWithProtected() {
+ System.out.println("overridePublicWithProtected: Base");
+ }
+
+ public void callOverrideProtectedWithPublic() {
+ overrideProtectedWithPublic();
+ }
+ protected void overrideProtectedWithPublic() {
+ System.out.println("overrideProtectedWithPublic: Base");
+ }
+
+ public void callOverridePublicWithPrivate() {
+ overridePublicWithPrivate();
+ }
+ /* src2: public */
+ private void overridePublicWithPrivate() {
+ System.out.println("overridePublicWithPrivate: Base");
+ }
+
+ public void callOverridePrivateWithPublic() {
+ overridePrivateWithPublic();
+ }
+ private void overridePrivateWithPublic() {
+ System.out.println("overridePrivateWithPublic: Base");
+ }
+
+ public void callOverrideVirtualWithStatic() {
+ overrideVirtualWithStatic();
+ }
+ /* src2: non-static */
+ public static void overrideVirtualWithStatic() {
+ System.out.println("overrideVirtualWithStatic: Base");
+ }
+
+ public void callOverrideStaticWithVirtual() {
+ overrideStaticWithVirtual();
+ }
+ /* src2: static */
+ public void overrideStaticWithVirtual() {
+ System.out.println("overrideStaticWithVirtual: Base");
+ }
+}
diff --git a/tests/077-method-override/src/Derived.java b/tests/077-method-override/src/Derived.java
new file mode 100644
index 0000000..7dc43d0
--- /dev/null
+++ b/tests/077-method-override/src/Derived.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Derived extends Base {
+ public static void notDeclaredInBase() {
+ System.out.println("notDeclaredInBase: Derived");
+ }
+
+ public void overridden() {
+ System.out.println("overridden: Derived");
+ }
+
+ public void wasOverridden() {
+ System.out.println("wasOverridden: Derived");
+ }
+
+ public void overrideWithPublic() {
+ System.out.println("overrideWithPublic: Derived");
+ }
+
+ protected void overridePublicWithProtected() {
+ System.out.println("overridePublicWithProtected: Derived");
+ }
+
+ public void overrideProtectedWithPublic() {
+ System.out.println("overrideProtectedWithPublic: Derived");
+ }
+
+ private void overridePublicWithPrivate() {
+ System.out.println("overridePublicWithPrivate: Derived");
+ }
+
+ public void overridePrivateWithPublic() {
+ System.out.println("overridePrivateWithPublic: Derived");
+ }
+
+ /* not really an "override"; just has same method signature */
+ public static void overrideVirtualWithStatic() {
+ System.out.println("overrideVirtualWithStatic: Derived");
+ }
+
+ /* not really an "override"; just has same method signature */
+ public void overrideStaticWithVirtual() {
+ System.out.println("overrideStaticWithVirtual: Derived");
+ }
+}
diff --git a/tests/077-method-override/src/Main.java b/tests/077-method-override/src/Main.java
new file mode 100644
index 0000000..2d10ee0
--- /dev/null
+++ b/tests/077-method-override/src/Main.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void main(String args[]) {
+ Derived derived = new Derived();
+
+ derived.declaredInBase();
+ derived.notDeclaredInBase();
+ derived.wasOverridden();
+
+ derived.callOverrideWithPublic();
+ derived.callOverrideProtectedWithPublic();
+ derived.callOverridePublicWithProtected();
+ derived.callOverridePublicWithPrivate();
+ derived.callOverridePrivateWithPublic();
+ derived.overridePrivateWithPublic();
+ derived.callOverrideVirtualWithStatic();
+ derived.overrideVirtualWithStatic();
+ derived.callOverrideStaticWithVirtual();
+ derived.overrideStaticWithVirtual();
+
+ try {
+ ((Base)derived).overrideVirtualWithStatic();
+ } catch (NoSuchMethodError nsme) {
+ /* NSME is subclass of ICCE, so check it explicitly */
+ System.err.println("Got NSME - ovws");
+ } catch (IncompatibleClassChangeError icce) {
+ System.out.println("Got expected exception - ovws");
+ }
+
+ try {
+ ((Base)derived).overrideStaticWithVirtual();
+ } catch (NoSuchMethodError nsme) {
+ System.err.println("Got NSME - ovws");
+ } catch (IncompatibleClassChangeError icce) {
+ System.out.println("Got expected exception - oswv");
+ }
+ }
+}
diff --git a/tests/077-method-override/src2/Base.java b/tests/077-method-override/src2/Base.java
new file mode 100644
index 0000000..ab2a28b
--- /dev/null
+++ b/tests/077-method-override/src2/Base.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Base {
+ public void declaredInBase() {
+ System.out.println("declaredInBase: Base");
+ }
+
+ public void overridden() {
+ System.out.println("overridden: Base");
+ }
+
+ /* src2: removed */
+ //public void wasOverridden() {
+ // System.out.println("wasOverridden: Base");
+ //}
+
+ public void callOverrideWithPublic() {
+ overrideWithPublic();
+ }
+ public void overrideWithPublic() {
+ System.out.println("overrideWithPublic: Base");
+ }
+
+ public void callOverridePublicWithProtected() {
+ overridePublicWithProtected();
+ }
+ /* src2: public */
+ public void overridePublicWithProtected() {
+ System.out.println("overridePublicWithProtected: Base");
+ }
+
+ public void callOverrideProtectedWithPublic() {
+ overrideProtectedWithPublic();
+ }
+ protected void overrideProtectedWithPublic() {
+ System.out.println("overrideProtectedWithPublic: Base");
+ }
+
+ public void callOverridePublicWithPrivate() {
+ overridePublicWithPrivate();
+ }
+ /* src2: public */
+ public void overridePublicWithPrivate() {
+ System.out.println("overridePublicWithPrivate: Base");
+ }
+
+ public void callOverridePrivateWithPublic() {
+ overridePrivateWithPublic();
+ }
+ private void overridePrivateWithPublic() {
+ System.out.println("overridePrivateWithPublic: Base");
+ }
+
+ public void callOverrideVirtualWithStatic() {
+ overrideVirtualWithStatic();
+ }
+ /* src2: non-static */
+ public void overrideVirtualWithStatic() {
+ System.out.println("overrideVirtualWithStatic: Base");
+ }
+
+ public void callOverrideStaticWithVirtual() {
+ overrideStaticWithVirtual();
+ }
+ public static void overrideStaticWithVirtual() {
+ System.out.println("overrideStaticWithVirtual: Base");
+ }
+}
diff --git a/tests/078-polymorphic-virtual/expected.txt b/tests/078-polymorphic-virtual/expected.txt
new file mode 100644
index 0000000..0d29728
--- /dev/null
+++ b/tests/078-polymorphic-virtual/expected.txt
@@ -0,0 +1,3 @@
+10000000
+20000000
+30000000
diff --git a/tests/078-polymorphic-virtual/info.txt b/tests/078-polymorphic-virtual/info.txt
new file mode 100644
index 0000000..7c8a561
--- /dev/null
+++ b/tests/078-polymorphic-virtual/info.txt
@@ -0,0 +1,2 @@
+Stress test predicted chaining for overloaded virtual callsite with 3 resolved
+calless invoked 10,000,000 times each in three threads.
diff --git a/tests/078-polymorphic-virtual/src/Base.java b/tests/078-polymorphic-virtual/src/Base.java
new file mode 100644
index 0000000..ec3aadd
--- /dev/null
+++ b/tests/078-polymorphic-virtual/src/Base.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Base extends Thread {
+ int value;
+
+ public void run() {
+ for (int i = 0; i < 10000000; i++) {
+ incrimentValue();
+ }
+ }
+
+ public void incrimentValue() {
+ }
+
+ public int getValue() {
+ return value;
+ }
+}
diff --git a/tests/078-polymorphic-virtual/src/Derived1.java b/tests/078-polymorphic-virtual/src/Derived1.java
new file mode 100644
index 0000000..57bd3b0
--- /dev/null
+++ b/tests/078-polymorphic-virtual/src/Derived1.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Derived1 extends Base {
+ public void incrimentValue() {
+ value += 1;
+ }
+}
diff --git a/tests/078-polymorphic-virtual/src/Derived2.java b/tests/078-polymorphic-virtual/src/Derived2.java
new file mode 100644
index 0000000..1d7de57
--- /dev/null
+++ b/tests/078-polymorphic-virtual/src/Derived2.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Derived2 extends Base {
+ public void incrimentValue() {
+ value += 2;
+ }
+}
diff --git a/tests/078-polymorphic-virtual/src/Derived3.java b/tests/078-polymorphic-virtual/src/Derived3.java
new file mode 100644
index 0000000..c2594d2
--- /dev/null
+++ b/tests/078-polymorphic-virtual/src/Derived3.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Derived3 extends Base {
+ public void incrimentValue() {
+ value += 3;
+ }
+}
diff --git a/tests/078-polymorphic-virtual/src/Main.java b/tests/078-polymorphic-virtual/src/Main.java
new file mode 100644
index 0000000..0514e53
--- /dev/null
+++ b/tests/078-polymorphic-virtual/src/Main.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void main(String args[]) {
+ Derived1 derived1 = new Derived1();
+ Derived2 derived2 = new Derived2();
+ Derived3 derived3 = new Derived3();
+
+ derived1.start();
+ derived2.start();
+ derived3.start();
+
+ try {
+ derived1.join();
+ derived2.join();
+ derived3.join();
+ } catch (Exception e) {
+ System.out.println(e);
+ return;
+ }
+
+ System.out.println(derived1.getValue());
+ System.out.println(derived2.getValue());
+ System.out.println(derived3.getValue());
+ }
+}
diff --git a/tests/079-phantom/expected.txt b/tests/079-phantom/expected.txt
new file mode 100644
index 0000000..a932b77
--- /dev/null
+++ b/tests/079-phantom/expected.txt
@@ -0,0 +1,14 @@
+start
+Created Bitmap one: 10x10 (100)
+Created Bitmap two: 20x20 (101)
+Created Bitmap three/four: 20x20 (101)
+Drawing Bitmap two: 20x20 (101)
+nulling 1
+freeNativeStorage: 100
+nulling 2
+nulling 3
+nulling 4
+freeNativeStorage: 101
+intr
+Bitmap has shut down
+done
diff --git a/tests/079-phantom/info.txt b/tests/079-phantom/info.txt
new file mode 100644
index 0000000..d974e2c
--- /dev/null
+++ b/tests/079-phantom/info.txt
@@ -0,0 +1 @@
+Exercise phantom references.
diff --git a/tests/079-phantom/src/Bitmap.java b/tests/079-phantom/src/Bitmap.java
new file mode 100644
index 0000000..9d03cbd
--- /dev/null
+++ b/tests/079-phantom/src/Bitmap.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.PhantomReference;
+import java.util.ArrayList;
+
+public class Bitmap {
+ String mName; /* for debugging */
+ int mWidth, mHeight;
+ Bitmap.NativeWrapper mNativeWrapper;
+
+ private static int sSerial = 100;
+ private static ArrayList sPhantomList = new ArrayList<PhantomWrapper>();
+ private static ReferenceQueue<PhantomWrapper> sPhantomQueue =
+ new ReferenceQueue<PhantomWrapper>();
+ private static BitmapWatcher sWatcher = new BitmapWatcher(sPhantomQueue);
+ static {
+ sWatcher.start();
+ };
+
+ Bitmap(String name, int width, int height, Bitmap.NativeWrapper nativeData) {
+ mName = name;
+ mWidth = width;
+ mHeight = height;
+ mNativeWrapper = nativeData;
+
+ System.out.println("Created " + this);
+ }
+
+ public String toString() {
+ return "Bitmap " + mName + ": " + mWidth + "x" + mHeight + " (" +
+ mNativeWrapper.mNativeData + ")";
+ }
+
+ public void drawAt(int x, int y) {
+ System.out.println("Drawing " + this);
+ }
+
+ public static void shutDown() {
+ sWatcher.shutDown();
+ try {
+ sWatcher.join();
+ } catch (InterruptedException ie) {
+ System.out.println("join intr");
+ }
+ System.out.println("Bitmap has shut down");
+ }
+
+ /*
+ * Pretend we're allocating native storage. Just returns a unique
+ * serial number.
+ */
+ static Bitmap.NativeWrapper allocNativeStorage(int width, int height) {
+ int nativeData;
+
+ synchronized (Bitmap.class) {
+ nativeData = sSerial++;
+ }
+
+ Bitmap.NativeWrapper wrapper = new Bitmap.NativeWrapper(nativeData);
+ PhantomWrapper phan = new PhantomWrapper(wrapper, sPhantomQueue,
+ nativeData);
+ sPhantomList.add(phan);
+ return wrapper;
+ }
+
+ static void freeNativeStorage(int nativeDataPtr) {
+ System.out.println("freeNativeStorage: " + nativeDataPtr);
+ }
+
+ /*
+ * Wraps a native data pointer in an object. When this object is no
+ * longer referenced, we free the native data.
+ */
+ static class NativeWrapper {
+ public NativeWrapper(int nativeDataPtr) {
+ mNativeData = nativeDataPtr;
+ }
+ public int mNativeData;
+
+ /*
+ @Override
+ protected void finalize() throws Throwable {
+ System.out.println("finalized " + mNativeData);
+ }
+ */
+ }
+}
+
+/*
+ * Keep an eye on the native data.
+ *
+ * We keep a copy of the native data pointer value, and set the wrapper
+ * as our referent. We need the copy because you can't get the referred-to
+ * object back out of a PhantomReference.
+ */
+class PhantomWrapper extends PhantomReference {
+ PhantomWrapper(Bitmap.NativeWrapper wrapper,
+ ReferenceQueue<PhantomWrapper> queue, int nativeDataPtr)
+ {
+ super(wrapper, queue);
+ mNativeData = nativeDataPtr;
+ }
+
+ public int mNativeData;
+}
+
+/*
+ * Thread that watches for un-referenced bitmap data.
+ */
+class BitmapWatcher extends Thread {
+ ReferenceQueue<PhantomWrapper> mQueue;
+ volatile boolean mQuit = false;
+
+ BitmapWatcher(ReferenceQueue<PhantomWrapper> queue) {
+ mQueue = queue;
+ setName("Bitmap Watcher");
+ }
+
+ public void run() {
+ while (!mQuit) {
+ try {
+ PhantomWrapper ref = (PhantomWrapper) mQueue.remove();
+ //System.out.println("dequeued ref " + ref.mNativeData +
+ // " - " + ref);
+ Bitmap.freeNativeStorage(ref.mNativeData);
+ //ref.clear();
+ } catch (InterruptedException ie) {
+ System.out.println("intr");
+ }
+ }
+ }
+
+ public void shutDown() {
+ mQuit = true;
+ interrupt();
+ }
+}
diff --git a/tests/079-phantom/src/Main.java b/tests/079-phantom/src/Main.java
new file mode 100644
index 0000000..9c459c9
--- /dev/null
+++ b/tests/079-phantom/src/Main.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ Bitmap mBitmap1, mBitmap2, mBitmap3, mBitmap4;
+
+ public static void sleep(int ms) {
+ try {
+ Thread.sleep(ms);
+ } catch (InterruptedException ie) {
+ System.err.println("sleep interrupted");
+ }
+ }
+
+ public static void main(String args[]) {
+ System.out.println("start");
+
+ Main main = new Main();
+ main.run();
+
+ sleep(1000);
+ System.out.println("done");
+ }
+
+ public void run() {
+ createBitmaps();
+
+ System.gc();
+ sleep(250);
+
+ mBitmap2.drawAt(0, 0);
+
+ System.out.println("nulling 1");
+ mBitmap1 = null;
+ System.gc();
+ sleep(500);
+
+ System.out.println("nulling 2");
+ mBitmap2 = null;
+ System.gc();
+ sleep(500);
+
+ System.out.println("nulling 3");
+ mBitmap3 = null;
+ System.gc();
+ sleep(500);
+
+ System.out.println("nulling 4");
+ mBitmap4 = null;
+ System.gc();
+ sleep(500);
+
+ Bitmap.shutDown();
+ }
+
+ /*
+ * Create bitmaps.
+ *
+ * bitmap1 is 10x10 and unique
+ * bitmap2 and bitmap3 are 20x20 and share the same storage.
+ * bitmap4 is just another reference to bitmap3
+ *
+ * When we return there should be no local refs lurking on the stack.
+ */
+ public void createBitmaps() {
+ Bitmap.NativeWrapper dataA = Bitmap.allocNativeStorage(10, 10);
+ Bitmap.NativeWrapper dataB = Bitmap.allocNativeStorage(20, 20);
+ mBitmap1 = new Bitmap("one", 10, 10, dataA);
+ mBitmap2 = new Bitmap("two", 20, 20, dataB);
+ mBitmap3 = mBitmap4 = new Bitmap("three/four", 20, 20, dataB);
+ }
+}
diff --git a/tests/080-oom-throw/expected.txt b/tests/080-oom-throw/expected.txt
new file mode 100644
index 0000000..811f68c
--- /dev/null
+++ b/tests/080-oom-throw/expected.txt
@@ -0,0 +1,2 @@
+Array allocation failed
+Instance allocation failed
diff --git a/tests/080-oom-throw/info.txt b/tests/080-oom-throw/info.txt
new file mode 100644
index 0000000..e8ae6f6
--- /dev/null
+++ b/tests/080-oom-throw/info.txt
@@ -0,0 +1,3 @@
+Inject memory allocation failures for NEW_ARRAY and NEW_INSTANCE and make sure
+the JIT'ed code handles OOM exception correctly since it cannot fall back to
+the interpreter and re-execute the bytecode.
diff --git a/tests/080-oom-throw/src/Main.java b/tests/080-oom-throw/src/Main.java
new file mode 100644
index 0000000..3d75f3d
--- /dev/null
+++ b/tests/080-oom-throw/src/Main.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ static class ArrayMemEater {
+ static int blowup(char[][] holder, int size) {
+ int i = 0;
+ try {
+ for ( ; i < size; i++)
+ holder[i] = new char[128];
+ } catch (OutOfMemoryError oome) {
+ return i;
+ }
+
+ return size;
+ }
+
+ static void confuseCompilerOptimization(char[][] holder) {
+ }
+ }
+
+ static class InstanceMemEater {
+ InstanceMemEater next;
+ double d1, d2, d3, d4, d5, d6, d7, d8;
+
+ static InstanceMemEater blowup() {
+ InstanceMemEater memEater;
+ try {
+ memEater = new InstanceMemEater();
+ } catch (OutOfMemoryError e) {
+ memEater = null;
+ }
+ return memEater;
+ }
+
+ static void confuseCompilerOptimization(InstanceMemEater memEater) {
+ }
+ }
+
+ static void triggerArrayOOM() {
+ int size = 1 * 1024 * 1024;
+ char[][] holder = new char[size][];
+
+ int count = ArrayMemEater.blowup(holder, size);
+ ArrayMemEater.confuseCompilerOptimization(holder);
+ if (count < size) {
+ System.out.println("Array allocation failed");
+ }
+ }
+
+ static void triggerInstanceOOM() {
+ InstanceMemEater memEater = InstanceMemEater.blowup();
+ InstanceMemEater lastMemEater = memEater;
+ do {
+ lastMemEater.next = InstanceMemEater.blowup();
+ lastMemEater = lastMemEater.next;
+ } while (lastMemEater != null);
+ memEater.confuseCompilerOptimization(memEater);
+ System.out.println("Instance allocation failed");
+ }
+
+ public static void main(String[] args) {
+ triggerArrayOOM();
+ triggerInstanceOOM();
+ }
+}
diff --git a/tests/081-hot-exceptions/expected.txt b/tests/081-hot-exceptions/expected.txt
new file mode 100644
index 0000000..2432ff4
--- /dev/null
+++ b/tests/081-hot-exceptions/expected.txt
@@ -0,0 +1,2 @@
+sum = 0
+exception = 1024
diff --git a/tests/081-hot-exceptions/info.txt b/tests/081-hot-exceptions/info.txt
new file mode 100644
index 0000000..cc514f3
--- /dev/null
+++ b/tests/081-hot-exceptions/info.txt
@@ -0,0 +1,3 @@
+Make a hot exception-throwing path to stress test how the trace builder handles
+exceptions encountered during trace selection. The existence of exceptions will
+cause a control flow change to deviate from the current method.
diff --git a/tests/081-hot-exceptions/src/Main.java b/tests/081-hot-exceptions/src/Main.java
new file mode 100644
index 0000000..90e7af2
--- /dev/null
+++ b/tests/081-hot-exceptions/src/Main.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ static class ArrayObj {
+ int[] array;
+
+ int getArrayElement(int i) throws NullPointerException {
+ return array[i];
+ }
+ }
+
+ public static void main(String[] args) {
+ ArrayObj arrayObj2 = new ArrayObj();
+ int sum = 0;
+ int exception = 0;
+
+ for (int i = 0; i < 1024; i++) {
+ try {
+ // A hot method invocation that always encounters exceptions
+ sum += arrayObj2.getArrayElement(i);
+ } catch (NullPointerException npe) {
+ exception++;
+ }
+ }
+ System.out.println("sum = " + sum);
+ System.out.println("exception = " + exception);
+ }
+}
diff --git a/tests/082-inline-execute/expected.txt b/tests/082-inline-execute/expected.txt
new file mode 100644
index 0000000..5059fe8
--- /dev/null
+++ b/tests/082-inline-execute/expected.txt
@@ -0,0 +1,8 @@
+Length of : 0
+Length of x : 1
+Length of 01234567890123456789012345678901234567890123456789012345678901234567890123456789 : 80
+Now is the time[0] = "N"
+Now is the time[1] = "o"
+Now is the time[10] = " "
+Now is the time[last] = "e"
+Num throws 2000
diff --git a/tests/082-inline-execute/info.txt b/tests/082-inline-execute/info.txt
new file mode 100644
index 0000000..ddc31fe
--- /dev/null
+++ b/tests/082-inline-execute/info.txt
@@ -0,0 +1,8 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+This test covers the string inline-execute tests, and it done in a
+looping manner to ensure that the tests are translated when a Jit is
+active.
diff --git a/tests/082-inline-execute/src/Main.java b/tests/082-inline-execute/src/Main.java
new file mode 100644
index 0000000..b512091
--- /dev/null
+++ b/tests/082-inline-execute/src/Main.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Test for Jit's handling of string inline-execute. Should be tested
+ * twice - once using self-cosimulation (if available) and once without.
+ * The non-self-cosimulation test ensures that the answer computed the first
+ * time through (via the interpreter) is the same after looping enough
+ * to trigger translation.
+ */
+
+import junit.framework.Assert;
+
+public class Main {
+ public static void main(String args[]) {
+ int i;
+ stringLengthTest();
+ stringCharAtTest();
+ stringIndexOfTest();
+ for (i = 0; i < 1000; i++)
+ stringCompareToTest();
+ }
+
+ public static void stringLengthTest() {
+ String str0 = "";
+ String str1 = "x";
+ String str80 = "01234567890123456789012345678901234567890123456789012345678901234567890123456789";
+ int len0 = str0.length();
+ int len1 = str1.length();
+ int len80 = str80.length();
+ int i;
+
+ System.out.println("Length of " + str0 + " : " + len0);
+ System.out.println("Length of " + str1 + " : " + len1);
+ System.out.println("Length of " + str80 + " : " + len80);
+
+ for (i = 0; i < 1000; i++) {
+ assert(str0.length() == len0);
+ assert(str1.length() == len1);
+ assert(str80.length() == len80);
+ }
+ }
+
+ public static void stringCharAtTest() {
+ String testStr = "Now is the time";
+ int under = -1;
+ int over = testStr.length();
+ int numThrown = 0;
+ int numNotThrown = 0;
+ int at0 = testStr.charAt(0);
+ int at1 = testStr.charAt(1);
+ int at10 = testStr.charAt(10);
+ int atLast = testStr.charAt(testStr.length()-1);
+ int i;
+
+ System.out.println(testStr + "[0] = \"" + (char)at0 + "\"");
+ System.out.println(testStr + "[1] = \"" + (char)at1 + "\"");
+ System.out.println(testStr + "[10] = \"" + (char)at10 + "\"");
+ System.out.println(testStr + "[last] = \"" + (char)atLast + "\"");
+
+ for (i = 0; i < 1000; i++) {
+ assert(at0 == testStr.charAt(0));
+ assert(at1 == testStr.charAt(1));
+ assert(at10 == testStr.charAt(10));
+ assert(atLast == testStr.charAt(testStr.length()-1));
+ }
+
+ for (i = 0; i < 1000; i++) {
+ try {
+ testStr.charAt(under);
+ numNotThrown++;
+ } catch (StringIndexOutOfBoundsException sioobe) {
+ numThrown++;
+ }
+ try {
+ testStr.charAt(over);
+ numNotThrown++;
+ } catch (StringIndexOutOfBoundsException sioobe) {
+ numThrown++;
+ }
+ }
+ assert(numNotThrown == 0);
+ System.out.println("Num throws " + numThrown);
+ }
+
+
+ public static void stringIndexOfTest() {
+ String str0 = "";
+ String str3 = "abc";
+ String str10 = "abcdefghij";
+ String str40 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabc";
+ int i;
+
+ for (i = 0; i < 1000; i++) {
+ assert(str0.indexOf('a') == -1);
+ assert(str3.indexOf('a') == 0);
+ assert(str3.indexOf('b') == 1);
+ assert(str3.indexOf('c') == 2);
+ assert(str10.indexOf('j') == 9);
+ assert(str40.indexOf('a') == 0);
+ assert(str40.indexOf('b') == 38);
+ assert(str40.indexOf('c') == 39);
+ assert(str0.indexOf('a',20) == -1);
+ assert(str0.indexOf('a',0) == -1);
+ assert(str0.indexOf('a',-1) == -1);
+ assert(str3.indexOf('a',0) == 0);
+ assert(str3.indexOf('a',1) == -1);
+ assert(str3.indexOf('a',1234) == -1);
+ assert(str3.indexOf('b',0) == 1);
+ assert(str3.indexOf('b',1) == 1);
+ assert(str3.indexOf('c',2) == 2);
+ assert(str10.indexOf('j',5) == 9);
+ assert(str10.indexOf('j',9) == 9);
+ assert(str40.indexOf('a',10) == 10);
+ assert(str40.indexOf('b',40) == -1);
+ }
+
+ }
+
+ public static void stringCompareToTest() {
+ String test = "0123456789";
+ String test1 = new String("0123456789"); // different object
+ String test2 = new String("0123456780"); // different value
+ String offset = new String("xxx0123456789yyy");
+ String sub = offset.substring(3, 13);
+ String str32 = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
+ String str33 = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxy";
+ String lc = "abcdefg";
+ String uc = "ABCDEFG";
+ Object blah = new Object();
+
+ for (int i = 0; i < 100; i++) {
+ String y = lc.toUpperCase();
+ Assert.assertTrue(y.equals(uc));
+ }
+
+ Assert.assertEquals(str32.compareTo(str33), -1);
+ Assert.assertEquals(str33.compareTo(str32), 1);
+
+ Assert.assertTrue(test.equals(test));
+ Assert.assertTrue(test.equals(test1));
+ Assert.assertFalse(test.equals(test2));
+
+ Assert.assertEquals(test.compareTo(test1), 0);
+ Assert.assertTrue(test1.compareTo(test2) > 0);
+ Assert.assertTrue(test2.compareTo(test1) < 0);
+
+ /* compare string with a nonzero offset, in left/right side */
+ Assert.assertEquals(test.compareTo(sub), 0);
+ Assert.assertEquals(sub.compareTo(test), 0);
+ Assert.assertTrue(test.equals(sub));
+ Assert.assertTrue(sub.equals(test));
+ /* same base, one is a substring */
+ Assert.assertFalse(offset.equals(sub));
+ Assert.assertFalse(sub.equals(offset));
+ /* wrong class */
+ Assert.assertFalse(test.equals(blah));
+
+ /* null ptr - throw */
+ try {
+ test.compareTo(null);
+ Assert.fail("didn't get expected npe");
+ } catch (NullPointerException npe) {
+ }
+ /* null ptr - ok */
+ Assert.assertFalse(test.equals(null));
+
+ test = test.substring(1);
+ Assert.assertTrue(test.equals("123456789"));
+ Assert.assertFalse(test.equals(test1));
+
+ test = test.substring(1);
+ Assert.assertTrue(test.equals("23456789"));
+
+ test = test.substring(1);
+ Assert.assertTrue(test.equals("3456789"));
+
+ test = test.substring(1);
+ Assert.assertTrue(test.equals("456789"));
+
+ test = test.substring(3,5);
+ Assert.assertTrue(test.equals("78"));
+
+ test = "this/is/a/path";
+ String[] strings = test.split("/");
+ Assert.assertEquals(4, strings.length);
+
+ Assert.assertEquals("this is a path", test.replaceAll("/", " "));
+ Assert.assertEquals("this is a path", test.replace("/", " "));
+ }
+
+}
diff --git a/tests/082-inline-execute/src/junit/framework/Assert.java b/tests/082-inline-execute/src/junit/framework/Assert.java
new file mode 100644
index 0000000..364e646
--- /dev/null
+++ b/tests/082-inline-execute/src/junit/framework/Assert.java
@@ -0,0 +1,291 @@
+package junit.framework;
+
+/**
+ * A set of assert methods. Messages are only displayed when an assert fails.
+ */
+
+public class Assert {
+ /**
+ * Protect constructor since it is a static only class
+ */
+ protected Assert() {
+ }
+
+ /**
+ * Asserts that a condition is true. If it isn't it throws
+ * an AssertionFailedError with the given message.
+ */
+ static public void assertTrue(String message, boolean condition) {
+ if (!condition)
+ fail(message);
+ }
+ /**
+ * Asserts that a condition is true. If it isn't it throws
+ * an AssertionFailedError.
+ */
+ static public void assertTrue(boolean condition) {
+ assertTrue(null, condition);
+ }
+ /**
+ * Asserts that a condition is false. If it isn't it throws
+ * an AssertionFailedError with the given message.
+ */
+ static public void assertFalse(String message, boolean condition) {
+ assertTrue(message, !condition);
+ }
+ /**
+ * Asserts that a condition is false. If it isn't it throws
+ * an AssertionFailedError.
+ */
+ static public void assertFalse(boolean condition) {
+ assertFalse(null, condition);
+ }
+ /**
+ * Fails a test with the given message.
+ */
+ static public void fail(String message) {
+ throw new AssertionFailedError(message);
+ }
+ /**
+ * Fails a test with no message.
+ */
+ static public void fail() {
+ fail(null);
+ }
+ /**
+ * Asserts that two objects are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, Object expected, Object actual) {
+ if (expected == null && actual == null)
+ return;
+ if (expected != null && expected.equals(actual))
+ return;
+ failNotEquals(message, expected, actual);
+ }
+ /**
+ * Asserts that two objects are equal. If they are not
+ * an AssertionFailedError is thrown.
+ */
+ static public void assertEquals(Object expected, Object actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two Strings are equal.
+ */
+ static public void assertEquals(String message, String expected, String actual) {
+ if (expected == null && actual == null)
+ return;
+ if (expected != null && expected.equals(actual))
+ return;
+ throw new ComparisonFailure(message, expected, actual);
+ }
+ /**
+ * Asserts that two Strings are equal.
+ */
+ static public void assertEquals(String expected, String actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two doubles are equal concerning a delta. If they are not
+ * an AssertionFailedError is thrown with the given message. If the expected
+ * value is infinity then the delta value is ignored.
+ */
+ static public void assertEquals(String message, double expected, double actual, double delta) {
+ // handle infinity specially since subtracting to infinite values gives NaN and the
+ // the following test fails
+ if (Double.isInfinite(expected)) {
+ if (!(expected == actual))
+ failNotEquals(message, new Double(expected), new Double(actual));
+ } else if (!(Math.abs(expected-actual) <= delta)) // Because comparison with NaN always returns false
+ failNotEquals(message, new Double(expected), new Double(actual));
+ }
+ /**
+ * Asserts that two doubles are equal concerning a delta. If the expected
+ * value is infinity then the delta value is ignored.
+ */
+ static public void assertEquals(double expected, double actual, double delta) {
+ assertEquals(null, expected, actual, delta);
+ }
+ /**
+ * Asserts that two floats are equal concerning a delta. If they are not
+ * an AssertionFailedError is thrown with the given message. If the expected
+ * value is infinity then the delta value is ignored.
+ */
+ static public void assertEquals(String message, float expected, float actual, float delta) {
+ // handle infinity specially since subtracting to infinite values gives NaN and the
+ // the following test fails
+ if (Float.isInfinite(expected)) {
+ if (!(expected == actual))
+ failNotEquals(message, new Float(expected), new Float(actual));
+ } else if (!(Math.abs(expected-actual) <= delta))
+ failNotEquals(message, new Float(expected), new Float(actual));
+ }
+ /**
+ * Asserts that two floats are equal concerning a delta. If the expected
+ * value is infinity then the delta value is ignored.
+ */
+ static public void assertEquals(float expected, float actual, float delta) {
+ assertEquals(null, expected, actual, delta);
+ }
+ /**
+ * Asserts that two longs are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, long expected, long actual) {
+ assertEquals(message, new Long(expected), new Long(actual));
+ }
+ /**
+ * Asserts that two longs are equal.
+ */
+ static public void assertEquals(long expected, long actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two booleans are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, boolean expected, boolean actual) {
+ assertEquals(message, new Boolean(expected), new Boolean(actual));
+ }
+ /**
+ * Asserts that two booleans are equal.
+ */
+ static public void assertEquals(boolean expected, boolean actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two bytes are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, byte expected, byte actual) {
+ assertEquals(message, new Byte(expected), new Byte(actual));
+ }
+ /**
+ * Asserts that two bytes are equal.
+ */
+ static public void assertEquals(byte expected, byte actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two chars are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, char expected, char actual) {
+ assertEquals(message, new Character(expected), new Character(actual));
+ }
+ /**
+ * Asserts that two chars are equal.
+ */
+ static public void assertEquals(char expected, char actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two shorts are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, short expected, short actual) {
+ assertEquals(message, new Short(expected), new Short(actual));
+ }
+ /**
+ * Asserts that two shorts are equal.
+ */
+ static public void assertEquals(short expected, short actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two ints are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, int expected, int actual) {
+ assertEquals(message, new Integer(expected), new Integer(actual));
+ }
+ /**
+ * Asserts that two ints are equal.
+ */
+ static public void assertEquals(int expected, int actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that an object isn't null.
+ */
+ static public void assertNotNull(Object object) {
+ assertNotNull(null, object);
+ }
+ /**
+ * Asserts that an object isn't null. If it is
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertNotNull(String message, Object object) {
+ assertTrue(message, object != null);
+ }
+ /**
+ * Asserts that an object is null.
+ */
+ static public void assertNull(Object object) {
+ assertNull(null, object);
+ }
+ /**
+ * Asserts that an object is null. If it is not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertNull(String message, Object object) {
+ assertTrue(message, object == null);
+ }
+ /**
+ * Asserts that two objects refer to the same object. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertSame(String message, Object expected, Object actual) {
+ if (expected == actual)
+ return;
+ failNotSame(message, expected, actual);
+ }
+ /**
+ * Asserts that two objects refer to the same object. If they are not
+ * the same an AssertionFailedError is thrown.
+ */
+ static public void assertSame(Object expected, Object actual) {
+ assertSame(null, expected, actual);
+ }
+ /**
+ * Asserts that two objects refer to the same object. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertNotSame(String message, Object expected, Object actual) {
+ if (expected == actual)
+ failSame(message);
+ }
+ /**
+ * Asserts that two objects refer to the same object. If they are not
+ * the same an AssertionFailedError is thrown.
+ */
+ static public void assertNotSame(Object expected, Object actual) {
+ assertNotSame(null, expected, actual);
+ }
+
+ static private void failSame(String message) {
+ String formatted= "";
+ if (message != null)
+ formatted= message+" ";
+ fail(formatted+"expected not same");
+ }
+
+ static private void failNotSame(String message, Object expected, Object actual) {
+ String formatted= "";
+ if (message != null)
+ formatted= message+" ";
+ fail(formatted+"expected same:<"+expected+"> was not:<"+actual+">");
+ }
+
+ static private void failNotEquals(String message, Object expected, Object actual) {
+ fail(format(message, expected, actual));
+ }
+
+ static String format(String message, Object expected, Object actual) {
+ String formatted= "";
+ if (message != null)
+ formatted= message+" ";
+ return formatted+"expected:<"+expected+"> but was:<"+actual+">";
+ }
+}
diff --git a/tests/082-inline-execute/src/junit/framework/AssertionFailedError.java b/tests/082-inline-execute/src/junit/framework/AssertionFailedError.java
new file mode 100644
index 0000000..e9cb3a3
--- /dev/null
+++ b/tests/082-inline-execute/src/junit/framework/AssertionFailedError.java
@@ -0,0 +1,13 @@
+package junit.framework;
+
+/**
+ * Thrown when an assertion failed.
+ */
+public class AssertionFailedError extends Error {
+
+ public AssertionFailedError () {
+ }
+ public AssertionFailedError (String message) {
+ super (message);
+ }
+}
diff --git a/tests/082-inline-execute/src/junit/framework/ComparisonFailure.java b/tests/082-inline-execute/src/junit/framework/ComparisonFailure.java
new file mode 100644
index 0000000..0cb2cee
--- /dev/null
+++ b/tests/082-inline-execute/src/junit/framework/ComparisonFailure.java
@@ -0,0 +1,68 @@
+package junit.framework;
+
+/**
+ * Thrown when an assert equals for Strings failed.
+ *
+ * Inspired by a patch from Alex Chaffee mailto:alex@purpletech.com
+ */
+public class ComparisonFailure extends AssertionFailedError {
+ private String fExpected;
+ private String fActual;
+
+ /**
+ * Constructs a comparison failure.
+ * @param message the identifying message or null
+ * @param expected the expected string value
+ * @param actual the actual string value
+ */
+ public ComparisonFailure (String message, String expected, String actual) {
+ super (message);
+ fExpected= expected;
+ fActual= actual;
+ }
+
+ /**
+ * Returns "..." in place of common prefix and "..." in
+ * place of common suffix between expected and actual.
+ *
+ * @see java.lang.Throwable#getMessage()
+ */
+ public String getMessage() {
+ if (fExpected == null || fActual == null)
+ return Assert.format(super.getMessage(), fExpected, fActual);
+
+ int end= Math.min(fExpected.length(), fActual.length());
+
+ int i= 0;
+ for(; i < end; i++) {
+ if (fExpected.charAt(i) != fActual.charAt(i))
+ break;
+ }
+ int j= fExpected.length()-1;
+ int k= fActual.length()-1;
+ for (; k >= i && j >= i; k--,j--) {
+ if (fExpected.charAt(j) != fActual.charAt(k))
+ break;
+ }
+ String actual, expected;
+
+ // equal strings
+ if (j < i && k < i) {
+ expected= fExpected;
+ actual= fActual;
+ } else {
+ expected= fExpected.substring(i, j+1);
+ actual= fActual.substring(i, k+1);
+ if (i <= end && i > 0) {
+ expected= "..."+expected;
+ actual= "..."+actual;
+ }
+
+ if (j < fExpected.length()-1)
+ expected= expected+"...";
+ if (k < fActual.length()-1)
+ actual= actual+"...";
+ }
+ return Assert.format(super.getMessage(), expected, actual);
+ }
+}
diff --git a/tests/083-jit-regressions/expected.txt b/tests/083-jit-regressions/expected.txt
new file mode 100644
index 0000000..1f30d21
--- /dev/null
+++ b/tests/083-jit-regressions/expected.txt
@@ -0,0 +1,3 @@
+b2296099 passes
+b2302318 passes
+b2487514 passes
diff --git a/tests/083-jit-regressions/info.txt b/tests/083-jit-regressions/info.txt
new file mode 100644
index 0000000..b791aba
--- /dev/null
+++ b/tests/083-jit-regressions/info.txt
@@ -0,0 +1,10 @@
+This is a miscellaneous test that was imported into the new-at-the-time
+runtime test framework. The test is intended to exercise basic features,
+and as such cannot be build on top of junit, since failure of such basic
+features might disrupt junit.
+
+This test covers JIT regressions
+
+2296099 JIT shift bug
+2302318 Crash during spin-on-suspend testing
+2487514 Missed exception in PriorityBlockingQueueTest.testToArray1_BadArg
diff --git a/tests/083-jit-regressions/src/Main.java b/tests/083-jit-regressions/src/Main.java
new file mode 100644
index 0000000..1f1dee3
--- /dev/null
+++ b/tests/083-jit-regressions/src/Main.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.concurrent.*;
+
+/**
+ * Test for Jit regressions.
+ */
+public class Main {
+ public static void main(String args[]) throws Exception {
+ b2296099Test();
+ b2302318Test();
+ b2487514Test();
+ }
+
+ static void b2296099Test() throws Exception {
+ int x = -1190771042;
+ int dist = 360530809;
+ int xl = -1190771042;
+ int distl = 360530809;
+
+ for (int i = 0; i < 100000; i++) {
+ int b = rotateLeft(x, dist);
+ if (b != 1030884493)
+ throw new RuntimeException("Unexpected value: " + b
+ + " after " + i + " iterations");
+ }
+ for (int i = 0; i < 100000; i++) {
+ long bl = rotateLeft(xl, distl);
+ if (bl != 1030884493)
+ throw new RuntimeException("Unexpected value: " + bl
+ + " after " + i + " iterations");
+ }
+ System.out.println("b2296099 passes");
+ }
+
+ static int rotateLeft(int i, int distance) {
+ return ((i << distance) | (i >>> (-distance)));
+ }
+
+ static void b2302318Test() {
+ System.gc();
+
+ SpinThread slow = new SpinThread(Thread.MIN_PRIORITY);
+ SpinThread fast1 = new SpinThread(Thread.NORM_PRIORITY);
+ SpinThread fast2 = new SpinThread(Thread.MAX_PRIORITY);
+
+ slow.setDaemon(true);
+ fast1.setDaemon(true);
+ fast2.setDaemon(true);
+
+ fast2.start();
+ slow.start();
+ fast1.start();
+ try {
+ Thread.sleep(3000);
+ } catch (InterruptedException ie) {/*ignore */}
+ System.gc();
+
+ System.out.println("b2302318 passes");
+ }
+
+ static void b2487514Test() {
+ PriorityBlockingQueue q = new PriorityBlockingQueue(10);
+ int catchCount = 0;
+
+ q.offer(new Integer(0));
+ /*
+ * Warm up the code cache to have toArray() compiled. The key here is
+ * to pass a compatible type so that there are no exceptions when
+ * executing the method body (ie the APUT_OBJECT bytecode).
+ */
+ for (int i = 0; i < 1000; i++) {
+ Integer[] ints = (Integer[]) q.toArray(new Integer[5]);
+ }
+
+ /* Now pass an incompatible type which is guaranteed to throw */
+ for (int i = 0; i < 1000; i++) {
+ try {
+ Object[] obj = q.toArray(new String[5]);
+ }
+ catch (ArrayStoreException success) {
+ catchCount++;
+ }
+ }
+
+ if (catchCount == 1000) {
+ System.out.println("b2487514 passes");
+ }
+ else {
+ System.out.println("b2487514 fails: catchCount is " + catchCount +
+ " (expecting 1000)");
+ }
+ }
+}
+
+class SpinThread extends Thread {
+ int mPriority;
+
+ SpinThread(int prio) {
+ super("Spin prio=" + prio);
+ mPriority = prio;
+ }
+
+ public void run() {
+ setPriority(mPriority);
+ while (true) {}
+ }
+}
diff --git a/tests/084-class-init/expected.txt b/tests/084-class-init/expected.txt
new file mode 100644
index 0000000..9865edb
--- /dev/null
+++ b/tests/084-class-init/expected.txt
@@ -0,0 +1,8 @@
+Got expected EIIE for FIELD0
+Got expected NCDFE for FIELD0
+Got expected NCDFE for FIELD1
+SlowInit static block pre-sleep
+SlowInit static block post-sleep
+Fields (child thread): 111222333444
+MethodThread message
+Fields (main thread): 111222333444
diff --git a/tests/084-class-init/info.txt b/tests/084-class-init/info.txt
new file mode 100644
index 0000000..00fa31b
--- /dev/null
+++ b/tests/084-class-init/info.txt
@@ -0,0 +1 @@
+Test class initialization edge cases and race conditions.
diff --git a/tests/084-class-init/src/IntHolder.java b/tests/084-class-init/src/IntHolder.java
new file mode 100644
index 0000000..4012d6e
--- /dev/null
+++ b/tests/084-class-init/src/IntHolder.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/**
+ * Holds an int.
+ */
+public class IntHolder {
+ private int mValue = 0;
+
+ /**
+ * Constructs an IntHolder with the specified value. Throws an
+ * exception if the initial value is less than zero.
+ */
+ public IntHolder(int initialVal) {
+ if (initialVal < 0)
+ throw new RuntimeException("negative number");
+
+ mValue = initialVal;
+ }
+
+ public int getValue() {
+ return mValue;
+ }
+ public void setValue(int val) {
+ mValue = val;
+ }
+}
diff --git a/tests/084-class-init/src/Main.java b/tests/084-class-init/src/Main.java
new file mode 100644
index 0000000..89b76f5
--- /dev/null
+++ b/tests/084-class-init/src/Main.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void main(String[] args) {
+ checkExceptions();
+ checkTiming();
+ }
+
+ public static void sleep(int msec) {
+ try {
+ Thread.sleep(msec);
+ } catch (InterruptedException ie) {
+ System.err.println("sleep interrupted");
+ }
+ }
+
+ static void checkExceptions() {
+ try {
+ System.out.println(PartialInit.FIELD0);
+ System.err.println("Construction of PartialInit succeeded unexpectedly");
+ } catch (ExceptionInInitializerError eiie) {
+ System.out.println("Got expected EIIE for FIELD0");
+ }
+
+ try {
+ System.out.println(PartialInit.FIELD0);
+ System.err.println("Load of FIELD0 succeeded unexpectedly");
+ } catch (NoClassDefFoundError ncdfe) {
+ System.out.println("Got expected NCDFE for FIELD0");
+ }
+ try {
+ System.out.println(PartialInit.FIELD1);
+ System.err.println("Load of FIELD1 succeeded unexpectedly");
+ } catch (NoClassDefFoundError ncdfe) {
+ System.out.println("Got expected NCDFE for FIELD1");
+ }
+ }
+
+ static void checkTiming() {
+ FieldThread fieldThread = new FieldThread();
+ MethodThread methodThread = new MethodThread();
+
+ fieldThread.start();
+ methodThread.start();
+
+ /* start class init */
+ IntHolder zero = SlowInit.FIELD0;
+
+ /* init complete; allow other threads time to finish printing */
+ Main.sleep(500);
+
+ /* print all values */
+ System.out.println("Fields (main thread): " +
+ SlowInit.FIELD0.getValue() + SlowInit.FIELD1.getValue() +
+ SlowInit.FIELD2.getValue() + SlowInit.FIELD3.getValue());
+ }
+
+ static class FieldThread extends Thread {
+ public void run() {
+ /* allow class init to start */
+ Main.sleep(200);
+
+ /* print fields; should delay until class init completes */
+ System.out.println("Fields (child thread): " +
+ SlowInit.FIELD0.getValue() + SlowInit.FIELD1.getValue() +
+ SlowInit.FIELD2.getValue() + SlowInit.FIELD3.getValue());
+ }
+ }
+
+ static class MethodThread extends Thread {
+ public void run() {
+ /* allow class init to start */
+ Main.sleep(400);
+
+ /* use a method that shouldn't be accessible yet */
+ SlowInit.printMsg("MethodThread message");
+ }
+ }
+}
diff --git a/tests/084-class-init/src/PartialInit.java b/tests/084-class-init/src/PartialInit.java
new file mode 100644
index 0000000..d4c71ff
--- /dev/null
+++ b/tests/084-class-init/src/PartialInit.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/**
+ * Partially-initialized class.
+ */
+public class PartialInit {
+ public static final IntHolder FIELD0 = new IntHolder(1); // succeeds
+ public static final IntHolder FIELD1 = new IntHolder(-2); // throws
+}
diff --git a/tests/084-class-init/src/SlowInit.java b/tests/084-class-init/src/SlowInit.java
new file mode 100644
index 0000000..8ac72be
--- /dev/null
+++ b/tests/084-class-init/src/SlowInit.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Class that initializes with a pause.
+ */
+public class SlowInit {
+
+ public static final IntHolder FIELD0 = new IntHolder(0);
+ public static final IntHolder FIELD1 = new IntHolder(0);
+ public static final IntHolder FIELD2 = new IntHolder(0);
+ public static final IntHolder FIELD3 = new IntHolder(0);
+
+ public static void printMsg(String msg) {
+ System.out.println(msg);
+ }
+
+ static {
+ FIELD0.setValue(111);
+ FIELD1.setValue(222);
+ printMsg("SlowInit static block pre-sleep");
+ Main.sleep(600);
+ printMsg("SlowInit static block post-sleep");
+ FIELD2.setValue(333);
+ FIELD3.setValue(444);
+ };
+}
diff --git a/tests/084-old-style-inner-class/build b/tests/084-old-style-inner-class/build
new file mode 100644
index 0000000..dc6f3bb
--- /dev/null
+++ b/tests/084-old-style-inner-class/build
@@ -0,0 +1,29 @@
+#!/bin/bash
+#
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Stop if something fails.
+set -e
+
+# We compile for a 1.4 target to suppress the use of EnclosingMethod
+# attributes.
+mkdir classes
+${JAVAC} -source 1.4 -target 1.4 -d classes `find src -name '*.java'`
+
+# Suppress stderr to keep the inner class warnings out of the expected output.
+dx --debug --dex --dump-to=classes.lst --output=classes.dex \
+ --dump-width=1000 classes 2>/dev/null
+
+zip test.jar classes.dex
diff --git a/tests/084-old-style-inner-class/expected.txt b/tests/084-old-style-inner-class/expected.txt
new file mode 100644
index 0000000..63a0076
--- /dev/null
+++ b/tests/084-old-style-inner-class/expected.txt
@@ -0,0 +1,8 @@
+Class: Main$1
+ getDeclaringClass(): (null)
+ getEnclosingClass(): (null)
+ getEnclosingMethod(): (null)
+Class: Main$2
+ getDeclaringClass(): (null)
+ getEnclosingClass(): (null)
+ getEnclosingMethod(): (null)
diff --git a/tests/084-old-style-inner-class/info.txt b/tests/084-old-style-inner-class/info.txt
new file mode 100644
index 0000000..9e5c4f9
--- /dev/null
+++ b/tests/084-old-style-inner-class/info.txt
@@ -0,0 +1,2 @@
+Test that the conversion of an old-style (pre-1.5) inner class results
+in a loss of inner class reflection information.
diff --git a/tests/084-old-style-inner-class/src/Main.java b/tests/084-old-style-inner-class/src/Main.java
new file mode 100644
index 0000000..c9a5b72
--- /dev/null
+++ b/tests/084-old-style-inner-class/src/Main.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Method;
+
+/**
+ * Test reflection on old-style inner classes.
+ */
+public class Main {
+ private static Runnable theRunnable = new Runnable() {
+ public void run() { }
+ };
+
+ private static Runnable create() {
+ return new Runnable() {
+ public void run() { }
+ };
+ }
+
+ private static String nameOf(Class clazz) {
+ return (clazz == null) ? "(null)" : clazz.getName();
+ }
+
+ private static String nameOf(Method meth) {
+ return (meth == null) ? "(null)" : meth.toString();
+ }
+
+ private static void infoFor(Class clazz) {
+ System.out.println("Class: " + nameOf(clazz) + "\n" +
+ " getDeclaringClass(): " +
+ nameOf(clazz.getDeclaringClass()) + "\n" +
+ " getEnclosingClass(): " +
+ nameOf(clazz.getEnclosingClass()) + "\n" +
+ " getEnclosingMethod(): " +
+ nameOf(clazz.getEnclosingMethod()));
+ }
+
+ public static void main(String args[]) {
+ infoFor(theRunnable.getClass());
+ infoFor(create().getClass());
+ }
+}
diff --git a/tests/086-null-super/expected.txt b/tests/086-null-super/expected.txt
new file mode 100644
index 0000000..20c6796
--- /dev/null
+++ b/tests/086-null-super/expected.txt
@@ -0,0 +1 @@
+Got expected ITE/NPE
diff --git a/tests/086-null-super/info.txt b/tests/086-null-super/info.txt
new file mode 100644
index 0000000..f983bd0
--- /dev/null
+++ b/tests/086-null-super/info.txt
@@ -0,0 +1,7 @@
+ClassLoader.loadClass() is expected to throw an exception, usually
+ClassNotFound, if it can't find the given Class, and not return null.
+
+This is a regression test for a defect in Dalvik, which was assuming
+that if there was no exception, the value returned would be non-null.
+
+This test is not expected to work for the reference implementation.
diff --git a/tests/086-null-super/src/Main.java b/tests/086-null-super/src/Main.java
new file mode 100644
index 0000000..6decb20
--- /dev/null
+++ b/tests/086-null-super/src/Main.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * Class loader test.
+ */
+public class Main {
+ /**
+ * Thrown when an unexpected Exception is caught internally.
+ */
+ static class TestFailed extends Exception {
+ public TestFailed(Throwable cause) {
+ super(cause);
+ }
+ }
+
+ /**
+ * A class loader which loads classes from the dex file
+ * "test.jar". However, it will return null when asked to load the
+ * class InaccessibleSuper.
+ *
+ * When testing code calls BrokenDexLoader's findBrokenClass(),
+ * a BrokenDexLoader will be the defining loader for the class
+ * Inaccessible. The VM will call the defining loader for
+ * "InaccessibleSuper", which will return null, which the VM
+ * should be able to deal with gracefully.
+ *
+ * Note that this depends heavily on the Dalvik test harness.
+ */
+ static class BrokenDexLoader extends ClassLoader {
+
+ /** We return null when asked to load InaccessibleSuper. */
+ private static class InaccessibleSuper {}
+ private static class Inaccessible extends InaccessibleSuper {}
+
+ private static final String SUPERCLASS_NAME =
+ "Main$BrokenDexLoader$InaccessibleSuper";
+ private static final String CLASS_NAME =
+ "Main$BrokenDexLoader$Inaccessible";
+
+ private static final String DEX_FILE = "test.jar";
+
+ public BrokenDexLoader(ClassLoader parent) {
+ super(parent);
+ }
+
+ /**
+ * Finds the class with the specified binary name, from DEX_FILE.
+ *
+ * If we don't find a match, we throw an exception.
+ */
+ private Class<?> findDexClass(String name)
+ throws TestFailed, InvocationTargetException
+ {
+
+ try {
+ /*
+ * Find the DexFile class, and construct a DexFile object
+ * through reflection, then call loadCLass on it.
+ */
+ Class mDexClass = ClassLoader.getSystemClassLoader().
+ loadClass("dalvik/system/DexFile");
+ Constructor ctor = mDexClass.
+ getConstructor(new Class[] {String.class});
+ Object mDexFile = ctor.newInstance(DEX_FILE);
+ Method meth = mDexClass.
+ getMethod("loadClass",
+ new Class[] { String.class, ClassLoader.class });
+ /*
+ * Invoking loadClass on CLASS_NAME is expected to
+ * throw an InvocationTargetException. Anything else
+ * is an error we can't recover from.
+ */
+ meth.invoke(mDexFile, name, this);
+ } catch (NoSuchMethodException nsme) {
+ throw new TestFailed(nsme);
+ } catch (InstantiationException ie) {
+ throw new TestFailed(ie);
+ } catch (IllegalAccessException iae) {
+ throw new TestFailed(iae);
+ } catch (ClassNotFoundException cnfe) {
+ throw new TestFailed(cnfe);
+ }
+
+ return null;
+ }
+
+ /**
+ * Load a class.
+ *
+ * Return null if the class's name is SUPERCLASS_NAME;
+ * otherwise invoke the super's loadClass method.
+ */
+ public Class<?> loadClass(String name, boolean resolve)
+ throws ClassNotFoundException
+ {
+ if (SUPERCLASS_NAME.equals(name)) {
+ return null;
+ }
+
+ return super.loadClass(name, resolve);
+ }
+
+ /**
+ * Attempt to find the class with the superclass we refuse to
+ * load. This is expected to throw an
+ * InvocationTargetException, with a NullPointerException as
+ * its cause.
+ */
+ public void findBrokenClass()
+ throws TestFailed, InvocationTargetException
+ {
+ findDexClass(CLASS_NAME);
+ }
+ }
+
+ /**
+ * Main entry point.
+ */
+ public static void main(String[] args)
+ throws TestFailed, ClassNotFoundException {
+ /*
+ * Run test.
+ */
+ testFailLoadAndGc();
+ }
+
+ /**
+ * See if we can GC after a failed load.
+ */
+ static void testFailLoadAndGc() throws TestFailed {
+ try {
+ BrokenDexLoader loader;
+
+ loader = new BrokenDexLoader(ClassLoader.getSystemClassLoader());
+ loader.findBrokenClass();
+ System.err.println("ERROR: Inaccessible was accessible");
+ } catch (InvocationTargetException ite) {
+ Throwable cause = ite.getCause();
+ if (cause instanceof NullPointerException) {
+ System.err.println("Got expected ITE/NPE");
+ } else {
+ System.err.println("Got unexpected ITE");
+ ite.printStackTrace();
+ }
+ }
+ }
+}
diff --git a/tests/087-gc-after-link/expected.txt b/tests/087-gc-after-link/expected.txt
new file mode 100644
index 0000000..3b2d33a
--- /dev/null
+++ b/tests/087-gc-after-link/expected.txt
@@ -0,0 +1,2 @@
+Got expected ITE/NPE
+GC complete.
diff --git a/tests/087-gc-after-link/info.txt b/tests/087-gc-after-link/info.txt
new file mode 100644
index 0000000..9483838
--- /dev/null
+++ b/tests/087-gc-after-link/info.txt
@@ -0,0 +1,8 @@
+This test causes a linkage error, which calls dvmFreeClassInnards on
+the unlinked Class.
+
+This is a regression test for a defect in Dalvik, which was assuming
+that dvmFreeClassInnards could be called twice on the same class.
+
+This test is a modified version of test 086.
+This test is not expected to work for the reference implementation.
diff --git a/tests/087-gc-after-link/src/Main.java b/tests/087-gc-after-link/src/Main.java
new file mode 100644
index 0000000..24adaf7
--- /dev/null
+++ b/tests/087-gc-after-link/src/Main.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * Class loader test.
+ */
+public class Main {
+ /**
+ * Thrown when an unexpected Exception is caught internally.
+ */
+ static class TestFailed extends Exception {
+ public TestFailed(Throwable cause) {
+ super(cause);
+ }
+ }
+
+ /**
+ * A class loader which loads classes from the dex file
+ * "test.jar". However, it will return null when asked to load the
+ * class InaccessibleSuper.
+ *
+ * When testing code calls BrokenDexLoader's findBrokenClass(),
+ * a BrokenDexLoader will be the defining loader for the class
+ * Inaccessible. The VM will call the defining loader for
+ * "InaccessibleSuper", which will return null, which the VM
+ * should be able to deal with gracefully.
+ *
+ * Note that this depends heavily on the Dalvik test harness.
+ */
+ static class BrokenDexLoader extends ClassLoader {
+
+ /** We return null when asked to load InaccessibleSuper. */
+ private static class InaccessibleSuper {}
+ private static class Inaccessible extends InaccessibleSuper {}
+
+ private static final String SUPERCLASS_NAME =
+ "Main$BrokenDexLoader$InaccessibleSuper";
+ private static final String CLASS_NAME =
+ "Main$BrokenDexLoader$Inaccessible";
+
+ private static final String DEX_FILE = "test.jar";
+
+ public BrokenDexLoader(ClassLoader parent) {
+ super(parent);
+ }
+
+ /**
+ * Finds the class with the specified binary name, from DEX_FILE.
+ *
+ * If we don't find a match, we throw an exception.
+ */
+ private Class<?> findDexClass(String name)
+ throws TestFailed, InvocationTargetException
+ {
+
+ try {
+ /*
+ * Find the DexFile class, and construct a DexFile object
+ * through reflection, then call loadCLass on it.
+ */
+ Class mDexClass = ClassLoader.getSystemClassLoader().
+ loadClass("dalvik/system/DexFile");
+ Constructor ctor = mDexClass.
+ getConstructor(new Class[] {String.class});
+ Object mDexFile = ctor.newInstance(DEX_FILE);
+ Method meth = mDexClass.
+ getMethod("loadClass",
+ new Class[] { String.class, ClassLoader.class });
+ /*
+ * Invoking loadClass on CLASS_NAME is expected to
+ * throw an InvocationTargetException. Anything else
+ * is an error we can't recover from.
+ */
+ meth.invoke(mDexFile, name, this);
+ } catch (NoSuchMethodException nsme) {
+ throw new TestFailed(nsme);
+ } catch (InstantiationException ie) {
+ throw new TestFailed(ie);
+ } catch (IllegalAccessException iae) {
+ throw new TestFailed(iae);
+ } catch (ClassNotFoundException cnfe) {
+ throw new TestFailed(cnfe);
+ }
+
+ return null;
+ }
+
+ /**
+ * Load a class.
+ *
+ * Return null if the class's name is SUPERCLASS_NAME;
+ * otherwise invoke the super's loadClass method.
+ */
+ public Class<?> loadClass(String name, boolean resolve)
+ throws ClassNotFoundException
+ {
+ if (SUPERCLASS_NAME.equals(name)) {
+ return null;
+ }
+
+ return super.loadClass(name, resolve);
+ }
+
+ /**
+ * Attempt to find the class with the superclass we refuse to
+ * load. This is expected to throw an
+ * InvocationTargetException, with a NullPointerException as
+ * its cause.
+ */
+ public void findBrokenClass()
+ throws TestFailed, InvocationTargetException
+ {
+ findDexClass(CLASS_NAME);
+ }
+ }
+
+ /**
+ * Main entry point.
+ */
+ public static void main(String[] args)
+ throws TestFailed, ClassNotFoundException {
+ /*
+ * Run test.
+ */
+ testFailLoadAndGc();
+ }
+
+ /**
+ * See if we can GC after a failed load.
+ */
+ static void testFailLoadAndGc() throws TestFailed {
+ try {
+ BrokenDexLoader loader;
+
+ loader = new BrokenDexLoader(ClassLoader.getSystemClassLoader());
+ loader.findBrokenClass();
+ System.err.println("ERROR: Inaccessible was accessible");
+ } catch (InvocationTargetException ite) {
+ Throwable cause = ite.getCause();
+ if (cause instanceof NullPointerException) {
+ System.err.println("Got expected ITE/NPE");
+ } else {
+ System.err.println("Got unexpected ITE");
+ ite.printStackTrace();
+ }
+ }
+ System.gc();
+ System.out.println("GC complete.");
+ }
+}
diff --git a/tests/README.txt b/tests/README.txt
new file mode 100644
index 0000000..eb1ce36
--- /dev/null
+++ b/tests/README.txt
@@ -0,0 +1,13 @@
+VM test harness.
+
+Use "./run-all-tests" to run all tests, or "./run-test <number>" to run a
+single test. Run "./run-test" with no arguments to see command flags;
+in particular, the tests can be run on the desktop, on a USB-attached
+device, or using the desktop "reference implementation".
+
+
+For most tests, the sources are in the "src" subdirectory. Sources found
+in the "src2" directory are compiled separately but to the same output
+directory; this can be used to exercise "API mismatch" situations by
+replacing class files created in the first pass. The "src-ex" directory
+is built separately, and is intended for exercising class loaders.
diff --git a/tests/etc/default-build b/tests/etc/default-build
new file mode 100755
index 0000000..b8df442
--- /dev/null
+++ b/tests/etc/default-build
@@ -0,0 +1,43 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Stop if something fails.
+set -e
+
+mkdir classes
+${JAVAC} -d classes `find src -name '*.java'`
+
+if [ -r src2 ]; then
+ ${JAVAC} -d classes `find src2 -name '*.java'`
+fi
+
+dx -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex \
+ --dump-width=1000 classes
+zip test.jar classes.dex
+
+if [ -r src-ex ]; then
+ mkdir classes-ex
+ ${JAVAC} -d classes-ex -cp classes `find src-ex -name '*.java'`
+ dx -JXmx256m --debug --dex --dump-to=classes-ex.lst \
+ --output=classes-ex.dex --dump-width=1000 classes-ex
+
+ # quick shuffle so that the stored name is "classes.dex"
+ mv classes.dex classes-1.dex
+ mv classes-ex.dex classes.dex
+ zip test-ex.jar classes.dex
+ mv classes.dex classes-ex.dex
+ mv classes-1.dex classes.dex
+fi
diff --git a/tests/etc/default-run b/tests/etc/default-run
new file mode 100755
index 0000000..ecbbbc7
--- /dev/null
+++ b/tests/etc/default-run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+exec ${RUN} "$@"
diff --git a/tests/etc/local-run-test-jar b/tests/etc/local-run-test-jar
new file mode 100755
index 0000000..6155e3f
--- /dev/null
+++ b/tests/etc/local-run-test-jar
@@ -0,0 +1,153 @@
+#!/bin/sh
+#
+# Run the code in test.jar on a host-local virtual machine. The jar should
+# contain a top-level class named Main to run.
+#
+# Options:
+# --quiet -- don't chatter
+# --fast -- use the fast interpreter (the default)
+# --jit -- use the jit
+# --portable -- use the portable interpreter
+# --debug -- wait for debugger to attach
+# --valgrind -- use valgrind
+# --no-verify -- turn off verification (on by default)
+# --no-optimize -- turn off optimization (on by default)
+
+msg() {
+ if [ "$QUIET" = "n" ]; then
+ echo "$@"
+ fi
+}
+
+INTERP=""
+DEBUG="n"
+GDB="n"
+VERIFY="y"
+OPTIMIZE="y"
+VALGRIND="n"
+DEV_MODE="n"
+QUIET="n"
+PRECISE="y"
+
+while true; do
+ if [ "x$1" = "x--quiet" ]; then
+ QUIET="y"
+ shift
+ elif [ "x$1" = "x--jit" ]; then
+ INTERP="jit"
+ msg "Using jit"
+ shift
+ elif [ "x$1" = "x--fast" ]; then
+ INTERP="fast"
+ msg "Using fast interpreter"
+ shift
+ elif [ "x$1" = "x--portable" ]; then
+ INTERP="portable"
+ msg "Using portable interpreter"
+ shift
+ elif [ "x$1" = "x--debug" ]; then
+ DEBUG="y"
+ shift
+ elif [ "x$1" = "x--gdb" ]; then
+ GDB="y"
+ shift
+ elif [ "x$1" = "x--valgrind" ]; then
+ VALGRIND="y"
+ shift
+ elif [ "x$1" = "x--dev" ]; then
+ DEV_MODE="y"
+ shift
+ elif [ "x$1" = "x--no-verify" ]; then
+ VERIFY="n"
+ shift
+ elif [ "x$1" = "x--no-optimize" ]; then
+ OPTIMIZE="n"
+ shift
+ elif [ "x$1" = "x--no-precise" ]; then
+ PRECISE="n"
+ shift
+ elif [ "x$1" = "x--" ]; then
+ shift
+ break
+ elif expr "x$1" : "x--" >/dev/null 2>&1; then
+ echo "unknown option: $1" 1>&2
+ exit 1
+ else
+ break
+ fi
+done
+
+if [ "x$INTERP" = "x" ]; then
+ INTERP="fast"
+ msg "Using fast interpreter by default"
+fi
+
+if [ "$OPTIMIZE" = "y" ]; then
+ if [ "$VERIFY" = "y" ]; then
+ DEX_OPTIMIZE="-Xdexopt:verified"
+ else
+ DEX_OPTIMIZE="-Xdexopt:all"
+ fi
+ msg "Performing optimizations"
+else
+ DEX_OPTIMIZE="-Xdexopt:none"
+ msg "Skipping optimizations"
+fi
+
+if [ "$VERIFY" = "y" ]; then
+ DEX_VERIFY=""
+ msg "Performing verification"
+else
+ DEX_VERIFY="-Xverify:none"
+ msg "Skipping verification"
+fi
+
+if [ "$VALGRIND" = "y" ]; then
+ msg "Running with valgrind"
+ valgrind_cmd="valgrind"
+ #valgrind_cmd="valgrind --leak-check=full"
+else
+ valgrind_cmd=""
+fi
+
+if [ "$PRECISE" = "y" ]; then
+ GC_OPTS="-Xgc:precise -Xgenregmap"
+else
+ GC_OPTS="-Xgc:noprecise"
+fi
+
+msg "------------------------------"
+
+BASE="$OUT" # from build environment
+DATA_DIR=/tmp
+DEBUG_OPTS="-Xcheck:jni -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n"
+
+export ANDROID_PRINTF_LOG=brief
+if [ "$DEV_MODE" = "y" ]; then
+ export ANDROID_LOG_TAGS='*:d'
+else
+ export ANDROID_LOG_TAGS='*:s'
+fi
+export ANDROID_DATA="$DATA_DIR"
+export ANDROID_ROOT="${BASE}/system"
+export LD_LIBRARY_PATH="${BASE}/system/lib"
+export DYLD_LIBRARY_PATH="${BASE}/system/lib"
+
+exe="${BASE}/system/bin/dalvikvm"
+framework="${BASE}/system/framework"
+bpath="${framework}/core.jar:${framework}/ext.jar:${framework}/framework.jar"
+
+if [ "$DEBUG" = "y" ]; then
+ PORT=8000
+ msg "Waiting for debugger to connect on localhost:$PORT"
+ DEX_DEBUG="-agentlib:jdwp=transport=dt_socket,addres=$PORT,server=y,suspend=y"
+fi
+
+if [ "$GDB" = "y" ]; then
+ gdb=gdb
+ gdbargs="--args $exe"
+fi
+
+$valgrind_cmd $gdb $exe $gdbargs "-Xbootclasspath:${bpath}" \
+ $DEX_VERIFY $DEX_OPTIMIZE $DEX_DEBUG $GC_OPTS "-Xint:${INTERP}" -ea \
+ -cp test.jar Main "$@"
diff --git a/tests/etc/push-and-run-test-jar b/tests/etc/push-and-run-test-jar
new file mode 100755
index 0000000..df66a8e
--- /dev/null
+++ b/tests/etc/push-and-run-test-jar
@@ -0,0 +1,130 @@
+#!/bin/sh
+#
+# Run the code in test.jar on the device. The jar should contain a top-level
+# class named Main to run.
+#
+# Options:
+# --quiet -- don't chatter
+# --fast -- use the fast interpreter (the default)
+# --jit -- use the jit
+# --portable -- use the portable interpreter
+# --debug -- wait for debugger to attach
+# --zygote -- use the zygote (if so, all other options are ignored)
+# --dev -- development mode
+# --no-verify -- turn off verification (on by default)
+# --no-optimize -- turn off optimization (on by default)
+# --no-precise -- turn off precise GC (on by default)
+
+msg() {
+ if [ "$QUIET" = "n" ]; then
+ echo "$@"
+ fi
+}
+
+INTERP=""
+DEBUG="n"
+VERIFY="y"
+OPTIMIZE="y"
+ZYGOTE="n"
+QUIET="n"
+PRECISE="y"
+
+while true; do
+ if [ "x$1" = "x--quiet" ]; then
+ QUIET="y"
+ shift
+ elif [ "x$1" = "x--fast" ]; then
+ INTERP="fast"
+ msg "Using fast interpreter"
+ shift
+ elif [ "x$1" = "x--jit" ]; then
+ INTERP="jit"
+ msg "Using jit"
+ shift
+ elif [ "x$1" = "x--portable" ]; then
+ INTERP="portable"
+ msg "Using portable interpreter"
+ shift
+ elif [ "x$1" = "x--debug" ]; then
+ DEBUG="y"
+ shift
+ elif [ "x$1" = "x--zygote" ]; then
+ ZYGOTE="y"
+ msg "Spawning from zygote"
+ shift
+ elif [ "x$1" = "x--dev" ]; then
+ # not used; ignore
+ shift
+ elif [ "x$1" = "x--no-verify" ]; then
+ VERIFY="n"
+ shift
+ elif [ "x$1" = "x--no-optimize" ]; then
+ OPTIMIZE="n"
+ shift
+ elif [ "x$1" = "x--no-precise" ]; then
+ PRECISE="n"
+ shift
+ elif [ "x$1" = "x--" ]; then
+ shift
+ break
+ elif expr "x$1" : "x--" >/dev/null 2>&1; then
+ echo "unknown option: $1" 1>&2
+ exit 1
+ else
+ break
+ fi
+done
+
+if [ "$ZYGOTE" = "n" ]; then
+ if [ "x$INTERP" = "x" ]; then
+ INTERP="fast"
+ msg "Using fast interpreter by default"
+ fi
+
+ if [ "$OPTIMIZE" = "y" ]; then
+ if [ "$VERIFY" = "y" ]; then
+ DEX_OPTIMIZE="-Xdexopt:verified"
+ else
+ DEX_OPTIMIZE="-Xdexopt:all"
+ fi
+ msg "Performing optimizations"
+ else
+ DEX_OPTIMIZE="-Xdexopt:none"
+ msg "Skipping optimizations"
+ fi
+
+ if [ "$VERIFY" = "y" ]; then
+ DEX_VERIFY=""
+ msg "Performing verification"
+ else
+ DEX_VERIFY="-Xverify:none"
+ msg "Skipping verification"
+ fi
+fi
+
+msg "------------------------------"
+
+if [ "$QUIET" = "n" ]; then
+ adb push test.jar /data
+ adb push test-ex.jar /data
+else
+ adb push test.jar /data >/dev/null 2>&1
+ adb push test-ex.jar /data >/dev/null 2>&1
+fi
+
+if [ "$DEBUG" = "y" ]; then
+ DEX_DEBUG="-agentlib:jdwp=transport=dt_android_adb,server=y,suspend=y"
+fi
+
+if [ "$PRECISE" = "y" ]; then
+ GC_OPTS="-Xgc:precise -Xgenregmap"
+else
+ GC_OPTS="-Xgc:noprecise"
+fi
+
+if [ "$ZYGOTE" = "y" ]; then
+ adb shell cd /data \; dvz -classpath test.jar Main "$@"
+else
+ adb shell cd /data \; dalvikvm $DEX_VERIFY $DEX_OPTIMIZE $DEX_DEBUG \
+ $GC_OPTS -cp test.jar "-Xint:${INTERP}" -ea Main "$@"
+fi
diff --git a/tests/etc/reference-run-test-classes b/tests/etc/reference-run-test-classes
new file mode 100755
index 0000000..94c8050
--- /dev/null
+++ b/tests/etc/reference-run-test-classes
@@ -0,0 +1,60 @@
+#!/bin/sh
+#
+# Run the code in a classes directory on a host-local reference virtual
+# machine. The jar should contain a top-level class named Main to run.
+#
+# Options:
+# --quiet -- don't chatter
+# --debug -- wait for debugger to attach
+# --no-verify -- turn off verification (on by default)
+# --dev -- development mode
+
+msg() {
+ if [ "$QUIET" = "n" ]; then
+ echo "$@"
+ fi
+}
+
+DEBUG="n"
+QUIET="n"
+VERIFY="y"
+
+while true; do
+ if [ "x$1" = "x--quiet" ]; then
+ QUIET="y"
+ shift
+ elif [ "x$1" = "x--debug" ]; then
+ DEBUG="y"
+ shift
+ elif [ "x$1" = "x--no-verify" ]; then
+ VERIFY="n"
+ shift
+ elif [ "x$1" = "x--dev" ]; then
+ # not used; ignore
+ shift
+ elif [ "x$1" = "x--" ]; then
+ shift
+ break
+ elif expr "x$1" : "x--" >/dev/null 2>&1; then
+ echo "unknown option: $1" 1>&2
+ exit 1
+ else
+ break
+ fi
+done
+
+if [ "$VERIFY" = "y" ]; then
+ VERIFY_ARG="-Xverify:all"
+ msg "Performing verification"
+else
+ VERIFY_ARG="-Xverify:none"
+ msg "Skipping verification"
+fi
+
+if [ "$DEBUG" = "y" ]; then
+ PORT=8000
+ msg "Waiting for debugger to connect on localhost:$PORT"
+ DEBUG_OPTS="-agentlib:jdwp=transport=dt_socket,address=$PORT,server=y,suspend=y"
+fi
+
+${JAVA} ${DEBUG_OPTS} -ea ${VERIFY_ARG} -classpath classes Main "$@"
diff --git a/tests/run-all-tests b/tests/run-all-tests
new file mode 100755
index 0000000..ac01803
--- /dev/null
+++ b/tests/run-all-tests
@@ -0,0 +1,124 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+while [ -h "${prog}" ]; do
+ newProg=`/bin/ls -ld "${prog}"`
+ newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+ if expr "x${newProg}" : 'x/' >/dev/null; then
+ prog="${newProg}"
+ else
+ progdir=`dirname "${prog}"`
+ prog="${progdir}/${newProg}"
+ fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+
+run_args=""
+usage="no"
+
+while true; do
+ if [ "x$1" = "x--local" ]; then
+ run_args="${run_args} --local"
+ shift
+ elif [ "x$1" = "x--reference" ]; then
+ run_args="${run_args} --reference"
+ shift
+ elif [ "x$1" = "x--jit" ]; then
+ run_args="${run_args} --jit"
+ shift
+ elif [ "x$1" = "x--fast" ]; then
+ run_args="${run_args} --fast"
+ shift
+ elif [ "x$1" = "x--portable" ]; then
+ run_args="${run_args} --portable"
+ shift
+ elif [ "x$1" = "x--debug" ]; then
+ run_args="${run_args} --debug"
+ shift
+ elif [ "x$1" = "x--zygote" ]; then
+ run_args="${run_args} --zygote"
+ shift
+ elif [ "x$1" = "x--no-verify" ]; then
+ run_args="${run_args} --no-verify"
+ shift
+ elif [ "x$1" = "x--no-optimize" ]; then
+ run_args="${run_args} --no-optimize"
+ shift
+ elif [ "x$1" = "x--valgrind" ]; then
+ run_args="${run_args} --valgrind"
+ shift
+ elif [ "x$1" = "x--dev" ]; then
+ run_args="${run_args} --dev"
+ shift
+ elif [ "x$1" = "x--update" ]; then
+ run_args="${run_args} --update"
+ shift
+ elif [ "x$1" = "x--help" ]; then
+ usage="yes"
+ shift
+ elif expr "x$1" : "x--" >/dev/null 2>&1; then
+ echo "unknown option: $1" 1>&2
+ usage="yes"
+ break
+ else
+ break
+ fi
+done
+
+if [ "$usage" = "yes" ]; then
+ prog=`basename $prog`
+ (
+ echo "usage:"
+ echo " $prog --help Print this message."
+ echo " $prog [options] Run all tests with the given options."
+ echo " Options are all passed to run-test; refer to that for " \
+ "further documentation:"
+ echo " --debug --dev --fast --local --no-optimize --no-verify" \
+ "--portable"
+ echo " --reference --update --valgrind --zygote"
+ ) 1>&2
+ exit 1
+fi
+
+passed=0
+failed=0
+failNames=""
+
+for i in *; do
+ if [ -d "$i" -a -r "$i" -a -r "${i}/info.txt" ]; then
+ ./run-test ${run_args} "$i"
+ if [ "$?" = "0" ]; then
+ ((passed += 1))
+ else
+ ((failed += 1))
+ failNames="$failNames $i"
+ fi
+ fi
+done
+
+echo "passed: $passed test(s)"
+echo "failed: $failed test(s)"
+
+for i in $failNames; do
+ echo "failed: $i"
+done
diff --git a/tests/run-test b/tests/run-test
new file mode 100755
index 0000000..c5e2090
--- /dev/null
+++ b/tests/run-test
@@ -0,0 +1,254 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+while [ -h "${prog}" ]; do
+ newProg=`/bin/ls -ld "${prog}"`
+ newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+ if expr "x${newProg}" : 'x/' >/dev/null; then
+ prog="${newProg}"
+ else
+ progdir=`dirname "${prog}"`
+ prog="${progdir}/${newProg}"
+ fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+
+export JAVA="java"
+export JAVAC="javac -target 1.5"
+export RUN="${progdir}/etc/push-and-run-test-jar"
+
+info="info.txt"
+build="build"
+run="run"
+expected="expected.txt"
+output="output.txt"
+build_output="build-output.txt"
+run_args="--quiet"
+
+dev_mode="no"
+update_mode="no"
+debug_mode="no"
+usage="no"
+
+while true; do
+ if [ "x$1" = "x--local" ]; then
+ RUN="${progdir}/etc/local-run-test-jar"
+ shift
+ elif [ "x$1" = "x--reference" ]; then
+ RUN="${progdir}/etc/reference-run-test-classes"
+ shift
+ elif [ "x$1" = "x--jit" ]; then
+ run_args="${run_args} --jit"
+ shift
+ elif [ "x$1" = "x--fast" ]; then
+ run_args="${run_args} --fast"
+ shift
+ elif [ "x$1" = "x--portable" ]; then
+ run_args="${run_args} --portable"
+ shift
+ elif [ "x$1" = "x--debug" ]; then
+ run_args="${run_args} --debug"
+ shift
+ elif [ "x$1" = "x--gdb" ]; then
+ run_args="${run_args} --gdb"
+ dev_mode="yes"
+ shift
+ elif [ "x$1" = "x--zygote" ]; then
+ run_args="${run_args} --zygote"
+ shift
+ elif [ "x$1" = "x--no-verify" ]; then
+ run_args="${run_args} --no-verify"
+ shift
+ elif [ "x$1" = "x--no-optimize" ]; then
+ run_args="${run_args} --no-optimize"
+ shift
+ elif [ "x$1" = "x--no-precise" ]; then
+ run_args="${run_args} --no-precise"
+ shift
+ elif [ "x$1" = "x--valgrind" ]; then
+ run_args="${run_args} --valgrind"
+ shift
+ elif [ "x$1" = "x--dev" ]; then
+ run_args="${run_args} --dev"
+ dev_mode="yes"
+ shift
+ elif [ "x$1" = "x--update" ]; then
+ update_mode="yes"
+ shift
+ elif [ "x$1" = "x--help" ]; then
+ usage="yes"
+ shift
+ elif expr "x$1" : "x--" >/dev/null 2>&1; then
+ echo "unknown option: $1" 1>&2
+ usage="yes"
+ break
+ else
+ break
+ fi
+done
+
+if [ "$dev_mode" = "yes" -a "$update_mode" = "yes" ]; then
+ echo "--dev and --update are mutually exclusive" 1>&2
+ usage="yes"
+fi
+
+if [ "$usage" = "no" ]; then
+ if [ "x$1" = "x" -o "x$1" = "x-" ]; then
+ test_dir=`basename "$oldwd"`
+ else
+ test_dir="$1"
+ fi
+
+ if [ '!' -d "$test_dir" ]; then
+ td2=`echo ${test_dir}-*`
+ if [ '!' -d "$td2" ]; then
+ echo "${test_dir}: no such test directory" 1>&2
+ usage="yes"
+ fi
+ test_dir="$td2"
+ fi
+
+ # Shift to get rid of the test name argument. The rest of the arguments
+ # will get passed to the test run.
+ shift
+fi
+
+if [ "$usage" = "yes" ]; then
+ prog=`basename $prog`
+ (
+ echo "usage:"
+ echo " $prog --help Print this message."
+ echo " $prog [options] [test-name] Run test normally."
+ echo " $prog --dev [options] [test-name] Development mode" \
+ "(dumps to stdout)."
+ echo " $prog --update [options] [test-name] Update mode" \
+ "(replaces expected.txt)."
+ echo ' Omitting the test name or specifying "-" will use the' \
+ "current directory."
+ echo " Runtime Options:"
+ echo " --fast Use the fast interpreter (the default)."
+ echo " --jit Use the jit."
+ echo " --portable Use the portable interpreter."
+ echo " --debug Wait for a debugger to attach."
+ #echo " --gdb Run under gdb; incompatible with some tests."
+ echo " --no-verify Turn off verification (on by default)."
+ echo " --no-optimize Turn off optimization (on by default)."
+ echo " --no-precise Turn off precise GC (on by default)."
+ echo " --zygote Spawn the process from the Zygote." \
+ "If used, then the"
+ echo " other runtime options are ignored."
+ echo " --local Use a host-local virtual machine."
+ echo " --valgrind Use valgrind when running locally."
+ echo " --reference Use a host-local reference virtual machine."
+ ) 1>&2
+ exit 1
+fi
+
+cd "$test_dir"
+test_dir=`pwd`
+
+td_info="${test_dir}/${info}"
+td_expected="${test_dir}/${expected}"
+
+tmp_dir="/tmp/test-$$"
+
+if [ '!' '(' -r "$td_info" -a -r "$td_expected" ')' ]; then
+ echo "${test_dir}: missing files" 1>&2
+ exit 1
+fi
+
+# copy the test to a temp dir and run it
+
+echo "${test_dir}: running..." 1>&2
+
+rm -rf "$tmp_dir"
+cp -Rp "$test_dir" "$tmp_dir"
+cd "$tmp_dir"
+
+if [ '!' -r "$build" ]; then
+ cp "${progdir}/etc/default-build" build
+fi
+
+if [ '!' -r "$run" ]; then
+ cp "${progdir}/etc/default-run" run
+fi
+
+chmod 755 "$build"
+chmod 755 "$run"
+
+good="no"
+if [ "$dev_mode" = "yes" ]; then
+ "./${build}" 2>&1
+ echo "build exit status: $?" 1>&2
+ "./${run}" $run_args "$@" 2>&1
+ echo "run exit status: $?" 1>&2
+ good="yes"
+elif [ "$update_mode" = "yes" ]; then
+ "./${build}" >"$build_output" 2>&1
+ build_exit="$?"
+ if [ "$build_exit" = '0' ]; then
+ "./${run}" $run_args "$@" >"$output" 2>&1
+ sed -e 's/[[:cntrl:]]$//g' < "$output" >"${td_expected}"
+ good="yes"
+ else
+ cat "$build_output" 1>&2
+ echo "build exit status: $build_exit" 1>&2
+ fi
+else
+ "./${build}" >"$build_output" 2>&1
+ build_exit="$?"
+ if [ "$build_exit" = '0' ]; then
+ "./${run}" $run_args "$@" >"$output" 2>&1
+ else
+ cp "$build_output" "$output"
+ echo "build exit status: $build_exit" >>"$output"
+ fi
+ diff --strip-trailing-cr -q "$expected" "$output" >/dev/null
+ if [ "$?" = "0" ]; then
+ # output == expected
+ good="yes"
+ echo "${test_dir}: succeeded!" 1>&2
+ fi
+fi
+
+if [ "$good" = "yes" ]; then
+ cd "$oldwd"
+ rm -rf "$tmp_dir"
+ exit 0
+fi
+
+(
+ if [ "$update_mode" '!=' "yes" ]; then
+ echo "${test_dir}: FAILED!"
+ echo ' '
+ echo '#################### info'
+ cat "${td_info}" | sed 's/^/# /g'
+ echo '#################### diffs'
+ diff --strip-trailing-cr -u "$expected" "$output"
+ echo '####################'
+ echo ' '
+ fi
+ echo "files left in ${tmp_dir}"
+) 1>&2
+
+exit 1
diff --git a/tools/Android.mk b/tools/Android.mk
new file mode 100644
index 0000000..6571161
--- /dev/null
+++ b/tools/Android.mk
@@ -0,0 +1 @@
+include $(all-subdir-makefiles)
diff --git a/tools/deadcode.py b/tools/deadcode.py
new file mode 100755
index 0000000..2ef8c70
--- /dev/null
+++ b/tools/deadcode.py
@@ -0,0 +1,128 @@
+#!/usr/bin/env python
+
+import os
+import re
+import sys
+
+def SplitSections(buffer):
+ """Spin through the input buffer looking for section header lines.
+ When found, the name of the section is extracted. The entire contents
+ of that section is added to a result hashmap with the section name
+ as the key"""
+
+ # Match lines like
+ # |section_name:
+ # capturing section_name
+ headerPattern = re.compile(r'^\s+\|([a-z _]+)\:$', re.MULTILINE)
+
+ sections = {}
+ start = 0
+ anchor = -1
+ sectionName = ''
+
+ while True:
+ # Look for a section header
+ result = headerPattern.search(buffer, start)
+
+ # If there are no more, add a section from the last header to EOF
+ if result is None:
+ if anchor is not -1:
+ sections[sectionName] = buffer[anchor]
+ return sections
+
+ # Add the lines from the last header, to this one to the sections
+ # map indexed by the section name
+ if anchor is not -1:
+ sections[sectionName] = buffer[anchor:result.start()]
+
+ sectionName = result.group(1)
+ start = result.end()
+ anchor = start
+
+ return sections
+
+def FindMethods(section):
+ """Spin through the 'method code index' section and extract all
+ method signatures. When found, they are added to a result list."""
+
+ # Match lines like:
+ # |[abcd] com/example/app/Class.method:(args)return
+ # capturing the method signature
+ methodPattern = re.compile(r'^\s+\|\[\w{4}\] (.*)$', re.MULTILINE)
+
+ start = 0
+ methods = []
+
+ while True:
+ # Look for a method name
+ result = methodPattern.search(section, start)
+
+ if result is None:
+ return methods
+
+ # Add the captured signature to the method list
+ methods.append(result.group(1))
+ start = result.end()
+
+def CallsMethod(codes, method):
+ """Spin through all the input method signatures. For each one, return
+ whether or not there is method invokation line in the codes section that
+ lists the method as the target."""
+
+ start = 0
+
+ while True:
+ # Find the next reference to the method signature
+ match = codes.find(method, start)
+
+ if match is -1:
+ break;
+
+ # Find the beginning of the line the method reference is on
+ startOfLine = codes.rfind("\n", 0, match) + 1
+
+ # If the word 'invoke' comes between the beginning of the line
+ # and the method reference, then it is a call to that method rather
+ # than the beginning of the code section for that method.
+ if codes.find("invoke", startOfLine, match) is not -1:
+ return True
+
+ start = match + len(method)
+
+ return False
+
+
+
+def main():
+ if len(sys.argv) is not 2 or not sys.argv[1].endswith(".jar"):
+ print "Usage:", sys.argv[0], "<filename.jar>"
+ sys.exit()
+
+ command = 'dx --dex --dump-width=1000 --dump-to=-"" "%s"' % sys.argv[1]
+
+ pipe = os.popen(command)
+
+ # Read the whole dump file into memory
+ data = pipe.read()
+ sections = SplitSections(data)
+
+ pipe.close()
+ del(data)
+
+ methods = FindMethods(sections['method code index'])
+ codes = sections['codes']
+ del(sections)
+
+ print "Dead Methods:"
+ count = 0
+
+ for method in methods:
+ if not CallsMethod(codes, method):
+ print "\t", method
+ count += 1
+
+ if count is 0:
+ print "\tNone"
+
+if __name__ == '__main__':
+ main()
diff --git a/tools/dex-preopt b/tools/dex-preopt
new file mode 100755
index 0000000..a9b75a4
--- /dev/null
+++ b/tools/dex-preopt
@@ -0,0 +1,320 @@
+#!/bin/bash
+#
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Usage: dex-preopt [options] path/to/input.jar path/to/output.odex
+#
+# This tool runs a host build of dalvikvm in order to preoptimize dex
+# files that will be run on a device.
+#
+# The input may be any sort of jar file (including .apk files), as long
+# as it contains a classes.dex file. Note that optimized versions of
+# bootstrap classes must be created before this can be run on other files;
+# use the "--bootstrap" option to do this.
+#
+# The "output.odex" file must not already exist.
+#
+# This is expected to be running in a user build environment, where
+# "dexopt" is available on the host.
+#
+# Options:
+# --build-dir=path/to/out -- Specify where the base of the build tree is.
+# This is typically a directory named "out". If not specified, it is
+# assumed to be the current directory. The specified input and output
+# paths are taken to be relative to this directory.
+# --dexopt=path/to/dexopt -- Specify the path to the dexopt executable.
+# If unspecified, there must be a unique subdirectory of the build-dir
+# that looks like host/ARCH/bin which must contain dexopt.
+# --product-dir=path/to/product -- Specify the path, relative to the build
+# directory, where the product tree to be used is. This directory should
+# contain the boot classpath jar files. If not specified, then there
+# must be a unique directory in the build named "target/product/NAME",
+# and this is the directory that will be used.
+# --boot-dir=path/to/bootclasspath -- Specify the path, relative to the
+# product directory, of the directory where the boot classpath files
+# reside. If not specified, this defaults to "system/framework"
+# --boot-jars=list:of:jar:base:names -- Specify the list of base names
+# of bootstrap classpath elements, colon-separated. Order is significant
+# and must match the BOOTCLASSPATH that is eventually specified at
+# runtime on the device. This defaults to "core". However, this really
+# needs to match the target product's BOOTCLASSPATH, which, as of this
+# writing, doesn't have a super-strict way of being defined within the
+# build. You can find variations of it in different init.rc files under
+# system/core/rootdir or under product-specific directories.
+# --bootstrap -- Process the bootstrap classes. If this is specified,
+# then, instead of processing a specified input file, no other arguments
+# are taken, and what is processed is the entirety of the boot jar
+# list, in order.
+# --verify={none,remote,all} -- Specify what level of verification to
+# do. Defaults to "all".
+# --optimize={none,verified,all} -- Specify which classes to optimize.
+# Defaults to "verified".
+# --no-register-maps -- Indicate that the output should not contain
+# register maps. By default, register maps are created and included.
+# --uniprocessor -- Indicate that the output should target a uniprocessor.
+# By default, optimizations will be made that specifically target
+# SMP processors (which will merely be superfluous on uniprocessors).
+#
+
+# Defaults.
+dexopt=''
+buildDir='.'
+productDir=''
+bootDir='system/framework'
+bootstrap='no'
+doVerify='all'
+doOptimize='verified'
+doRegisterMaps='yes'
+doUniprocessor='no'
+bootJars='core'
+
+optimizeFlags='' # built up from the more human-friendly options
+bogus='no' # indicates if there was an error during processing arguments
+
+# Iterate over the arguments looking for options.
+while true; do
+ origOption="$1"
+
+ if [ "x${origOption}" = "x--" ]; then
+ # A raw "--" signals the end of option processing.
+ shift
+ break
+ fi
+
+ # Parse the option into components.
+ optionBeforeValue=`expr -- "${origOption}" : '--\([^=]*\)='`
+
+ if [ "$?" = '0' ]; then
+ # Option has the form "--option=value".
+ option="${optionBeforeValue}"
+ value=`expr -- "${origOption}" : '--[^=]*=\(.*\)'`
+ hasValue='yes'
+ else
+ option=`expr -- "${origOption}" : '--\(.*\)'`
+ if [ "$?" = '1' ]; then
+ # Not an option.
+ break
+ fi
+ # Option has the form "--option".
+ value=""
+ hasValue='no'
+ fi
+ shift
+
+ # Interpret the option
+ if [ "${option}" = 'build-dir' -a "${hasValue}" = 'yes' ]; then
+ buildDir="${value}"
+ elif [ "${option}" = 'dexopt' -a "${hasValue}" = 'yes' ]; then
+ dexopt="${value}"
+ elif [ "${option}" = 'boot-dir' -a "${hasValue}" = 'yes' ]; then
+ bootDir="${value}"
+ elif [ "${option}" = 'product-dir' -a "${hasValue}" = 'yes' ]; then
+ productDir="${value}"
+ elif [ "${option}" = 'boot-jars' -a "${hasValue}" = 'yes' ]; then
+ bootJars="${value}"
+ elif [ "${option}" = 'bootstrap' -a "${hasValue}" = 'no' ]; then
+ bootstrap='yes'
+ elif [ "${option}" = 'verify' -a "${hasValue}" = 'yes' ]; then
+ doVerify="${value}"
+ elif [ "${option}" = 'optimize' -a "${hasValue}" = 'yes' ]; then
+ doOptimize="${value}"
+ elif [ "${option}" = 'no-register-maps' -a "${hasValue}" = 'no' ]; then
+ doRegisterMaps='no'
+ elif [ "${option}" = 'uniprocessor' -a "${hasValue}" = 'no' ]; then
+ doUniprocessor='yes'
+ else
+ echo "unknown option: ${origOption}" 1>&2
+ bogus='yes'
+ fi
+done
+
+# Check and set up the input and output files. In the case of bootstrap
+# processing, verify that no files are specified.
+inputFile=$1
+outputFile=$2
+if [ "${bootstrap}" = 'yes' ]; then
+ if [ "$#" != '0' ]; then
+ echo "unexpected arguments in --bootstrap mode" 1>&2
+ bogus=yes
+ fi
+elif [ "$#" != '2' ]; then
+ echo "must specify input and output files (and no more arguments)" 1>&2
+ bogus=yes
+fi
+
+# Sanity-check the specified build directory.
+if [ "x${buildDir}" = 'x' ]; then
+ echo "must specify build directory" 1>&2
+ bogus=yes
+elif [ ! '(' -d "${buildDir}" -a -w "${buildDir}" ')' ]; then
+ echo "build-dir is not a writable directory: ${buildDir}" 1>&2
+ bogus=yes
+fi
+
+# Sanity-check the specified boot classpath directory.
+if [ "x${bootDir}" = 'x' ]; then
+ echo "must specify boot classpath directory" 1>&2
+ bogus=yes
+fi
+
+# Sanity-check the specified boot jar list.
+if [ "x${bootJars}" = 'x' ]; then
+ echo "must specify non-empty boot-jars list" 1>&2
+ bogus=yes
+fi
+
+# Sanity-check and expand the verify option.
+if [ "x${doVerify}" = 'xnone' ]; then
+ optimizeFlags="${optimizeFlags},v=n"
+elif [ "x${doVerify}" = 'xremote' ]; then
+ optimizeFlags="${optimizeFlags},v=r"
+elif [ "x${doVerify}" = 'xall' ]; then
+ optimizeFlags="${optimizeFlags},v=a"
+else
+ echo "bad value for --verify: ${doVerify}" 1>&2
+ bogus=yes
+fi
+
+# Sanity-check and expand the optimize option.
+if [ "x${doOptimize}" = 'xnone' ]; then
+ optimizeFlags="${optimizeFlags},o=n"
+elif [ "x${doOptimize}" = 'xverified' ]; then
+ optimizeFlags="${optimizeFlags},o=v"
+elif [ "x${doOptimize}" = 'xall' ]; then
+ optimizeFlags="${optimizeFlags},o=a"
+else
+ echo "bad value for --optimize: ${doOptimize}" 1>&2
+ bogus=yes
+fi
+
+# Expand the register maps selection, if necessary.
+if [ "${doRegisterMaps}" = 'yes' ]; then
+ optimizeFlags="${optimizeFlags},m=y"
+fi
+
+# Expand the uniprocessor directive, if necessary.
+if [ "${doUniprocessor}" = 'yes' ]; then
+ optimizeFlags="${optimizeFlags},u=y"
+else
+ optimizeFlags="${optimizeFlags},u=n"
+fi
+
+# Kill off the spare comma in optimizeFlags.
+optimizeFlags=`echo ${optimizeFlags} | sed 's/^,//'`
+
+# Error out if there was trouble.
+if [ "${bogus}" = 'yes' ]; then
+ # There was an error during option processing.
+ echo "usage: $0" 1>&2
+ echo ' [--build-dir=path/to/out] [--dexopt=path/to/dexopt]' 1>&2
+ echo ' [--product-dir=path/to/product] [--boot-dir=name]' 1>&2
+ echo ' [--boot-jars=list:of:names] [--bootstrap]' 1>&2
+ echo ' [--verify=type] [--optimize=type] [--no-register-maps]' 1>&2
+ echo ' [--uniprocessor] path/to/input.jar path/to/output.odex' 1>&2
+ exit 1
+fi
+
+# Cd to the build directory, un-symlinkifying it for clarity.
+cd "${buildDir}"
+cd "`/bin/pwd`"
+
+# If needed, find the default product directory.
+if [ "x${productDir}" = 'x' ]; then
+ productDir="`ls target/product`"
+ if [ "$?" != '0' ]; then
+ echo "can't find product directory" 1>&2
+ exit 1
+ elif [ `expr -- "${productDir}" : ".* "` != '0' ]; then
+ echo "ambiguous product directory" 1>&2
+ exit 1
+ fi
+ productDir="target/product/${productDir}"
+fi
+
+# Verify the product directory.
+if [ ! '(' -d "${productDir}" -a -w "${productDir}" ')' ]; then
+ echo "product-dir is not a writable directory: ${productDir}" 1>&2
+ exit 1
+fi
+
+# Expand and verify the boot classpath directory. We add "/./" here to
+# separate the build system part of the path from the target system
+# suffix part of the path. The dexopt executable (deep inside the vm
+# really) uses this to know how to generate the names of the
+# dependencies (since we don't want the device files to contain bits
+# of pathname from the host build system).
+productBootDir="${productDir}/./${bootDir}"
+if [ ! '(' -d "${productBootDir}" -a -w "${productBootDir}" ')' ]; then
+ echo "boot-dir is not a writable directory: ${productBootDir}" 1>&2
+ exit 1
+fi
+
+# Find the dexopt binary if necesasry, and verify it.
+if [ "x${dexopt}" = 'x' ]; then
+ dexopt="`ls host/*/bin/dexopt`"
+ if [ "$?" != '0' ]; then
+ echo "can't find dexopt binary" 1>&2
+ exit 1
+ elif [ `expr -- "${dexopt}" : ".* "` != '0' ]; then
+ echo "ambiguous host directory" 1>&2
+ exit 1
+ fi
+fi
+if [ ! -x "${dexopt}" ]; then
+ echo "dexopt binary is not executable: ${dexopt}" 1>&2
+ exit 1
+fi
+
+# Expand the bootJars into paths that are relative from the build
+# directory, maintaining the colon separators.
+BOOTCLASSPATH=`echo ":${bootJars}" | \
+ sed "s!:\([^:]*\)!:${productBootDir}/\1.jar!g" | \
+ sed 's/^://'`
+export BOOTCLASSPATH
+
+if [ "${bootstrap}" = 'yes' ]; then
+ # Split the boot classpath into separate elements and iterate over them,
+ # processing each, in order.
+ elements=`echo "${BOOTCLASSPATH}" | sed 's/:/ /g'`
+
+ for inputFile in $elements; do
+ echo "Processing ${inputFile}" 1>&2
+ outputFile="`dirname ${inputFile}`/`basename ${inputFile} .jar`.odex"
+ "${dexopt}" --preopt "${inputFile}" "${outputFile}" "${optimizeFlags}"
+ status="$?"
+ if [ "${status}" != '0' ]; then
+ exit "${status}"
+ fi
+ done
+else
+ echo "Processing ${inputFile}" 1>&2
+
+ bootJarFile=`expr -- "${inputFile}" : "${productDir}/${bootDir}/\(.*\)"`
+ if [ "x${bootJarFile}" != 'x' ]; then
+ # The input file is in the boot classpath directory, so it needs
+ # to have "/./" inserted into it (see longer description above).
+ inputFile="${productBootDir}/${bootJarFile}"
+ fi
+
+ "${dexopt}" --preopt "${inputFile}" "${outputFile}" "${optimizeFlags}"
+
+ status="$?"
+ if [ "${status}" != '0' ]; then
+ exit "${status}"
+ fi
+fi
+
+echo "Done!" 1>&2
diff --git a/tools/dexcheck b/tools/dexcheck
new file mode 100755
index 0000000..2ec8b29
--- /dev/null
+++ b/tools/dexcheck
@@ -0,0 +1,67 @@
+#!/bin/bash
+#
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# This tool checks the integrity of the optimized dex files on a single
+# Android device connected to your computer.
+#
+# Brief HOW-TO:
+#
+# 1. Disconnect all but one device from USB.
+# 2. Set up a standard shell environment (envsetup.sh, lunch, etc.).
+# 3. Run "adb root" if necessary to ensure read permission on
+# /data/dalvik-cache. If in doubt, run the command. Power users may
+# also use "su" followed by "chmod 777 /data/dalvik-cache".
+# 4. Run this script, e.g. from the build root, "dalvik/tools/dexcheck".
+#
+# If all of the dex files are okay, you will just see a series of
+# lines written to your shell window naming each of the files. If
+# there is a problem, though, you will see something like this:
+#
+# system@app@Maps.apk@classes.dex
+# Failure in system@app@Maps.apk@classes.dex: ERROR: DEX parse failed
+#
+# When this happens, the log ("adb logcat") will generally have at
+# least a little more information about the dex level of the problem.
+# However, any error at all usually indicates some form of lower level
+# filesystem or filesystem cache corruption.
+#
+
+# Get the list of files. Use "sed" to drop the trailing carriage return.
+files=`adb shell "cd /data/dalvik-cache; echo *" | sed -e s/.$//`
+if [ "$files" = "*" ]; then
+ echo 'ERROR: commands must run as root on device (try "adb root" first?)'
+ exit 1
+fi
+
+failure=0
+
+# Check each file in turn. This is much faster with "dexdump -c", but that
+# flag was not available in 1.6 and earlier.
+#
+# The dexdump found in older builds does not stop on checksum failures and
+# will likely crash.
+for file in $files; do
+ echo $file
+ errout=`adb shell "dexdump /data/dalvik-cache/$file > dev/null"`
+ errcount=`echo $errout | wc -w` > /dev/null
+ if [ $errcount != "0" ]; then
+ echo " Failure in $file: $errout"
+ failure=1
+ fi
+done
+
+exit $failure
diff --git a/tools/dexdeps/Android.mk b/tools/dexdeps/Android.mk
new file mode 100644
index 0000000..e1b7e73
--- /dev/null
+++ b/tools/dexdeps/Android.mk
@@ -0,0 +1,45 @@
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+# We use copy-file-to-new-target so that the installed
+# script files' timestamps are at least as new as the
+# .jar files they wrap.
+
+# the dexdeps script
+# ============================================================
+include $(CLEAR_VARS)
+LOCAL_IS_HOST_MODULE := true
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE := dexdeps
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(HOST_OUT_JAVA_LIBRARIES)/dexdeps$(COMMON_JAVA_PACKAGE_SUFFIX)
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/etc/dexdeps | $(ACP)
+ @echo "Copy: $(PRIVATE_MODULE) ($@)"
+ $(copy-file-to-new-target)
+ $(hide) chmod 755 $@
+
+INTERNAL_DALVIK_MODULES += $(LOCAL_INSTALLED_MODULE)
+
+# the other stuff
+# ============================================================
+subdirs := $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk, \
+ src \
+ ))
+
+include $(subdirs)
diff --git a/tools/dexdeps/README.txt b/tools/dexdeps/README.txt
new file mode 100644
index 0000000..060aecb
--- /dev/null
+++ b/tools/dexdeps/README.txt
@@ -0,0 +1,27 @@
+dexdeps -- DEX external dependency dump
+
+
+This tool dumps a list of fields and methods that a DEX file uses but does
+not define. When combined with a list of public APIs, it can be used to
+determine whether an APK is accessing fields and calling methods that it
+shouldn't be. It may also be useful in determining whether an application
+requires a certain minimum API level to execute.
+
+Basic usage:
+
+ dexdeps [options] <file.{dex,apk,jar}>
+
+For zip archives (including .jar and .apk), dexdeps will look for a
+"classes.dex" entry.
+
+Supported options are:
+
+ --format={brief,xml}
+
+ Specifies the output format.
+
+ "brief" produces one line of output for each field and method. Field
+ and argument types are shown as descriptor strings.
+
+ "xml" produces a larger output file, readable with an XML browser. Types
+ are shown in a more human-readable form (e.g. "[I" becomes "int[]").
diff --git a/tools/dexdeps/etc/dexdeps b/tools/dexdeps/etc/dexdeps
new file mode 100644
index 0000000..dc628bd
--- /dev/null
+++ b/tools/dexdeps/etc/dexdeps
@@ -0,0 +1,69 @@
+#!/bin/bash
+#
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+while [ -h "${prog}" ]; do
+ newProg=`/bin/ls -ld "${prog}"`
+ newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+ if expr "x${newProg}" : 'x/' >/dev/null; then
+ prog="${newProg}"
+ else
+ progdir=`dirname "${prog}"`
+ prog="${progdir}/${newProg}"
+ fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+cd "${oldwd}"
+
+jarfile=dexdeps.jar
+libdir="$progdir"
+if [ ! -r "$libdir/$jarfile" ]
+then
+ libdir=`dirname "$progdir"`/tools/lib
+fi
+if [ ! -r "$libdir/$jarfile" ]
+then
+ libdir=`dirname "$progdir"`/framework
+fi
+if [ ! -r "$libdir/$jarfile" ]
+then
+ echo `basename "$prog"`": can't find $jarfile"
+ exit 1
+fi
+
+javaOpts=""
+
+# Alternatively, this will extract any parameter "-Jxxx" from the command line
+# and pass them to Java (instead of to dexdeps).
+while expr "x$1" : 'x-J' >/dev/null; do
+ opt=`expr "$1" : '-J\(.*\)'`
+ javaOpts="${javaOpts} -${opt}"
+ shift
+done
+
+if [ "$OSTYPE" = "cygwin" ] ; then
+ jarpath=`cygpath -w "$libdir/$jarfile"`
+else
+ jarpath="$libdir/$jarfile"
+fi
+
+exec java $javaOpts -jar "$jarpath" "$@"
diff --git a/tools/dexdeps/etc/manifest.txt b/tools/dexdeps/etc/manifest.txt
new file mode 100644
index 0000000..7606744
--- /dev/null
+++ b/tools/dexdeps/etc/manifest.txt
@@ -0,0 +1 @@
+Main-Class: com.android.dexdeps.Main
diff --git a/tools/dexdeps/src/Android.mk b/tools/dexdeps/src/Android.mk
new file mode 100644
index 0000000..8e4abaf
--- /dev/null
+++ b/tools/dexdeps/src/Android.mk
@@ -0,0 +1,31 @@
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+
+# dexdeps java library
+# ============================================================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_JAR_MANIFEST := ../etc/manifest.txt
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE:= dexdeps
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+INTERNAL_DALVIK_MODULES += $(LOCAL_INSTALLED_MODULE)
+
+include $(BUILD_DROIDDOC)
diff --git a/tools/dexdeps/src/com/android/dexdeps/ClassRef.java b/tools/dexdeps/src/com/android/dexdeps/ClassRef.java
new file mode 100644
index 0000000..7f6edc9
--- /dev/null
+++ b/tools/dexdeps/src/com/android/dexdeps/ClassRef.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dexdeps;
+
+import java.util.ArrayList;
+
+public class ClassRef {
+ private String mClassName;
+ private ArrayList<FieldRef> mFieldRefs;
+ private ArrayList<MethodRef> mMethodRefs;
+
+ /**
+ * Initializes a new class reference.
+ */
+ public ClassRef(String className) {
+ mClassName = className;
+ mFieldRefs = new ArrayList<FieldRef>();
+ mMethodRefs = new ArrayList<MethodRef>();
+ }
+
+ /**
+ * Adds the field to the field list.
+ */
+ public void addField(FieldRef fref) {
+ mFieldRefs.add(fref);
+ }
+
+ /**
+ * Returns the field list as an array.
+ */
+ public FieldRef[] getFieldArray() {
+ return mFieldRefs.toArray(new FieldRef[mFieldRefs.size()]);
+ }
+
+ /**
+ * Adds the method to the method list.
+ */
+ public void addMethod(MethodRef mref) {
+ mMethodRefs.add(mref);
+ }
+
+ /**
+ * Returns the method list as an array.
+ */
+ public MethodRef[] getMethodArray() {
+ return mMethodRefs.toArray(new MethodRef[mMethodRefs.size()]);
+ }
+
+ /**
+ * Gets the class name.
+ */
+ public String getName() {
+ return mClassName;
+ }
+}
diff --git a/tools/dexdeps/src/com/android/dexdeps/DexData.java b/tools/dexdeps/src/com/android/dexdeps/DexData.java
new file mode 100644
index 0000000..7ce5d04
--- /dev/null
+++ b/tools/dexdeps/src/com/android/dexdeps/DexData.java
@@ -0,0 +1,593 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dexdeps;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.Arrays;
+
+/**
+ * Data extracted from a DEX file.
+ */
+public class DexData {
+ private RandomAccessFile mDexFile;
+ private HeaderItem mHeaderItem;
+ private String[] mStrings; // strings from string_data_*
+ private TypeIdItem[] mTypeIds;
+ private ProtoIdItem[] mProtoIds;
+ private FieldIdItem[] mFieldIds;
+ private MethodIdItem[] mMethodIds;
+ private ClassDefItem[] mClassDefs;
+
+ private byte tmpBuf[] = new byte[4];
+ private boolean isBigEndian = false;
+
+ /**
+ * Constructs a new DexData for this file.
+ */
+ public DexData(RandomAccessFile raf) {
+ mDexFile = raf;
+ }
+
+ /**
+ * Loads the contents of the DEX file into our data structures.
+ *
+ * @throws IOException if we encounter a problem while reading
+ * @throws DexDataException if the DEX contents look bad
+ */
+ public void load() throws IOException {
+ parseHeaderItem();
+
+ loadStrings();
+ loadTypeIds();
+ loadProtoIds();
+ loadFieldIds();
+ loadMethodIds();
+ loadClassDefs();
+
+ markInternalClasses();
+ }
+
+
+ /**
+ * Parses the interesting bits out of the header.
+ */
+ void parseHeaderItem() throws IOException {
+ mHeaderItem = new HeaderItem();
+
+ seek(0);
+
+ byte[] magic = new byte[8];
+ readBytes(magic);
+ if (!Arrays.equals(magic, HeaderItem.DEX_FILE_MAGIC)) {
+ System.err.println("Magic number is wrong -- are you sure " +
+ "this is a DEX file?");
+ throw new DexDataException();
+ }
+
+ /*
+ * Read the endian tag, so we properly swap things as we read
+ * them from here on.
+ */
+ seek(8+4+20+4+4);
+ mHeaderItem.endianTag = readInt();
+ if (mHeaderItem.endianTag == HeaderItem.ENDIAN_CONSTANT) {
+ /* do nothing */
+ } else if (mHeaderItem.endianTag == HeaderItem.REVERSE_ENDIAN_CONSTANT){
+ /* file is big-endian (!), reverse future reads */
+ isBigEndian = true;
+ } else {
+ System.err.println("Endian constant has unexpected value " +
+ Integer.toHexString(mHeaderItem.endianTag));
+ throw new DexDataException();
+ }
+
+ seek(8+4+20); // magic, checksum, signature
+ mHeaderItem.fileSize = readInt();
+ mHeaderItem.headerSize = readInt();
+ /*mHeaderItem.endianTag =*/ readInt();
+ /*mHeaderItem.linkSize =*/ readInt();
+ /*mHeaderItem.linkOff =*/ readInt();
+ /*mHeaderItem.mapOff =*/ readInt();
+ mHeaderItem.stringIdsSize = readInt();
+ mHeaderItem.stringIdsOff = readInt();
+ mHeaderItem.typeIdsSize = readInt();
+ mHeaderItem.typeIdsOff = readInt();
+ mHeaderItem.protoIdsSize = readInt();
+ mHeaderItem.protoIdsOff = readInt();
+ mHeaderItem.fieldIdsSize = readInt();
+ mHeaderItem.fieldIdsOff = readInt();
+ mHeaderItem.methodIdsSize = readInt();
+ mHeaderItem.methodIdsOff = readInt();
+ mHeaderItem.classDefsSize = readInt();
+ mHeaderItem.classDefsOff = readInt();
+ /*mHeaderItem.dataSize =*/ readInt();
+ /*mHeaderItem.dataOff =*/ readInt();
+ }
+
+ /**
+ * Loads the string table out of the DEX.
+ *
+ * First we read all of the string_id_items, then we read all of the
+ * string_data_item. Doing it this way should allow us to avoid
+ * seeking around in the file.
+ */
+ void loadStrings() throws IOException {
+ int count = mHeaderItem.stringIdsSize;
+ int stringOffsets[] = new int[count];
+
+ //System.out.println("reading " + count + " strings");
+
+ seek(mHeaderItem.stringIdsOff);
+ for (int i = 0; i < count; i++) {
+ stringOffsets[i] = readInt();
+ }
+
+ mStrings = new String[count];
+
+ seek(stringOffsets[0]);
+ for (int i = 0; i < count; i++) {
+ seek(stringOffsets[i]); // should be a no-op
+ mStrings[i] = readString();
+ //System.out.println("STR: " + i + ": " + mStrings[i]);
+ }
+ }
+
+ /**
+ * Loads the type ID list.
+ */
+ void loadTypeIds() throws IOException {
+ int count = mHeaderItem.typeIdsSize;
+ mTypeIds = new TypeIdItem[count];
+
+ //System.out.println("reading " + count + " typeIds");
+ seek(mHeaderItem.typeIdsOff);
+ for (int i = 0; i < count; i++) {
+ mTypeIds[i] = new TypeIdItem();
+ mTypeIds[i].descriptorIdx = readInt();
+
+ //System.out.println(i + ": " + mTypeIds[i].descriptorIdx +
+ // " " + mStrings[mTypeIds[i].descriptorIdx]);
+ }
+ }
+
+ /**
+ * Loads the proto ID list.
+ */
+ void loadProtoIds() throws IOException {
+ int count = mHeaderItem.protoIdsSize;
+ mProtoIds = new ProtoIdItem[count];
+
+ //System.out.println("reading " + count + " protoIds");
+ seek(mHeaderItem.protoIdsOff);
+
+ /*
+ * Read the proto ID items.
+ */
+ for (int i = 0; i < count; i++) {
+ mProtoIds[i] = new ProtoIdItem();
+ mProtoIds[i].shortyIdx = readInt();
+ mProtoIds[i].returnTypeIdx = readInt();
+ mProtoIds[i].parametersOff = readInt();
+
+ //System.out.println(i + ": " + mProtoIds[i].shortyIdx +
+ // " " + mStrings[mProtoIds[i].shortyIdx]);
+ }
+
+ /*
+ * Go back through and read the type lists.
+ */
+ for (int i = 0; i < count; i++) {
+ ProtoIdItem protoId = mProtoIds[i];
+
+ int offset = protoId.parametersOff;
+
+ if (offset == 0) {
+ protoId.types = new int[0];
+ continue;
+ } else {
+ seek(offset);
+ int size = readInt(); // #of entries in list
+ protoId.types = new int[size];
+
+ for (int j = 0; j < size; j++) {
+ protoId.types[j] = readShort() & 0xffff;
+ }
+ }
+ }
+ }
+
+ /**
+ * Loads the field ID list.
+ */
+ void loadFieldIds() throws IOException {
+ int count = mHeaderItem.fieldIdsSize;
+ mFieldIds = new FieldIdItem[count];
+
+ //System.out.println("reading " + count + " fieldIds");
+ seek(mHeaderItem.fieldIdsOff);
+ for (int i = 0; i < count; i++) {
+ mFieldIds[i] = new FieldIdItem();
+ mFieldIds[i].classIdx = readShort() & 0xffff;
+ mFieldIds[i].typeIdx = readShort() & 0xffff;
+ mFieldIds[i].nameIdx = readInt();
+
+ //System.out.println(i + ": " + mFieldIds[i].nameIdx +
+ // " " + mStrings[mFieldIds[i].nameIdx]);
+ }
+ }
+
+ /**
+ * Loads the method ID list.
+ */
+ void loadMethodIds() throws IOException {
+ int count = mHeaderItem.methodIdsSize;
+ mMethodIds = new MethodIdItem[count];
+
+ //System.out.println("reading " + count + " methodIds");
+ seek(mHeaderItem.methodIdsOff);
+ for (int i = 0; i < count; i++) {
+ mMethodIds[i] = new MethodIdItem();
+ mMethodIds[i].classIdx = readShort() & 0xffff;
+ mMethodIds[i].protoIdx = readShort() & 0xffff;
+ mMethodIds[i].nameIdx = readInt();
+
+ //System.out.println(i + ": " + mMethodIds[i].nameIdx +
+ // " " + mStrings[mMethodIds[i].nameIdx]);
+ }
+ }
+
+ /**
+ * Loads the class defs list.
+ */
+ void loadClassDefs() throws IOException {
+ int count = mHeaderItem.classDefsSize;
+ mClassDefs = new ClassDefItem[count];
+
+ //System.out.println("reading " + count + " classDefs");
+ seek(mHeaderItem.classDefsOff);
+ for (int i = 0; i < count; i++) {
+ mClassDefs[i] = new ClassDefItem();
+ mClassDefs[i].classIdx = readInt();
+
+ /* access_flags = */ readInt();
+ /* superclass_idx = */ readInt();
+ /* interfaces_off = */ readInt();
+ /* source_file_idx = */ readInt();
+ /* annotations_off = */ readInt();
+ /* class_data_off = */ readInt();
+ /* static_values_off = */ readInt();
+
+ //System.out.println(i + ": " + mClassDefs[i].classIdx + " " +
+ // mStrings[mTypeIds[mClassDefs[i].classIdx].descriptorIdx]);
+ }
+ }
+
+ /**
+ * Sets the "internal" flag on type IDs which are defined in the
+ * DEX file or within the VM (e.g. primitive classes and arrays).
+ */
+ void markInternalClasses() {
+ for (int i = mClassDefs.length -1; i >= 0; i--) {
+ mTypeIds[mClassDefs[i].classIdx].internal = true;
+ }
+
+ for (int i = 0; i < mTypeIds.length; i++) {
+ String className = mStrings[mTypeIds[i].descriptorIdx];
+
+ if (className.length() == 1) {
+ // primitive class
+ mTypeIds[i].internal = true;
+ } else if (className.charAt(0) == '[') {
+ mTypeIds[i].internal = true;
+ }
+
+ //System.out.println(i + " " +
+ // (mTypeIds[i].internal ? "INTERNAL" : "external") + " - " +
+ // mStrings[mTypeIds[i].descriptorIdx]);
+ }
+ }
+
+
+ /*
+ * =======================================================================
+ * Queries
+ * =======================================================================
+ */
+
+ /**
+ * Returns the class name, given an index into the type_ids table.
+ */
+ private String classNameFromTypeIndex(int idx) {
+ return mStrings[mTypeIds[idx].descriptorIdx];
+ }
+
+ /**
+ * Returns an array of method argument type strings, given an index
+ * into the proto_ids table.
+ */
+ private String[] argArrayFromProtoIndex(int idx) {
+ ProtoIdItem protoId = mProtoIds[idx];
+ String[] result = new String[protoId.types.length];
+
+ for (int i = 0; i < protoId.types.length; i++) {
+ result[i] = mStrings[mTypeIds[protoId.types[i]].descriptorIdx];
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns a string representing the method's return type, given an
+ * index into the proto_ids table.
+ */
+ private String returnTypeFromProtoIndex(int idx) {
+ ProtoIdItem protoId = mProtoIds[idx];
+ return mStrings[mTypeIds[protoId.returnTypeIdx].descriptorIdx];
+ }
+
+ /**
+ * Returns an array with all of the class references that don't
+ * correspond to classes in the DEX file. Each class reference has
+ * a list of the referenced fields and methods associated with
+ * that class.
+ */
+ public ClassRef[] getExternalReferences() {
+ // create a sparse array of ClassRef that parallels mTypeIds
+ ClassRef[] sparseRefs = new ClassRef[mTypeIds.length];
+
+ // create entries for all externally-referenced classes
+ int count = 0;
+ for (int i = 0; i < mTypeIds.length; i++) {
+ if (!mTypeIds[i].internal) {
+ sparseRefs[i] =
+ new ClassRef(mStrings[mTypeIds[i].descriptorIdx]);
+ count++;
+ }
+ }
+
+ // add fields and methods to the appropriate class entry
+ addExternalFieldReferences(sparseRefs);
+ addExternalMethodReferences(sparseRefs);
+
+ // crunch out the sparseness
+ ClassRef[] classRefs = new ClassRef[count];
+ int idx = 0;
+ for (int i = 0; i < mTypeIds.length; i++) {
+ if (sparseRefs[i] != null)
+ classRefs[idx++] = sparseRefs[i];
+ }
+
+ assert idx == count;
+
+ return classRefs;
+ }
+
+ /**
+ * Runs through the list of field references, inserting external
+ * references into the appropriate ClassRef.
+ */
+ private void addExternalFieldReferences(ClassRef[] sparseRefs) {
+ for (int i = 0; i < mFieldIds.length; i++) {
+ if (!mTypeIds[mFieldIds[i].classIdx].internal) {
+ FieldIdItem fieldId = mFieldIds[i];
+ FieldRef newFieldRef = new FieldRef(
+ classNameFromTypeIndex(fieldId.classIdx),
+ classNameFromTypeIndex(fieldId.typeIdx),
+ mStrings[fieldId.nameIdx]);
+ sparseRefs[mFieldIds[i].classIdx].addField(newFieldRef);
+ }
+ }
+ }
+
+ /**
+ * Runs through the list of method references, inserting external
+ * references into the appropriate ClassRef.
+ */
+ private void addExternalMethodReferences(ClassRef[] sparseRefs) {
+ for (int i = 0; i < mMethodIds.length; i++) {
+ if (!mTypeIds[mMethodIds[i].classIdx].internal) {
+ MethodIdItem methodId = mMethodIds[i];
+ MethodRef newMethodRef = new MethodRef(
+ classNameFromTypeIndex(methodId.classIdx),
+ argArrayFromProtoIndex(methodId.protoIdx),
+ returnTypeFromProtoIndex(methodId.protoIdx),
+ mStrings[methodId.nameIdx]);
+ sparseRefs[mMethodIds[i].classIdx].addMethod(newMethodRef);
+ }
+ }
+ }
+
+
+ /*
+ * =======================================================================
+ * Basic I/O functions
+ * =======================================================================
+ */
+
+ /**
+ * Seeks the DEX file to the specified absolute position.
+ */
+ void seek(int position) throws IOException {
+ mDexFile.seek(position);
+ }
+
+ /**
+ * Fills the buffer by reading bytes from the DEX file.
+ */
+ void readBytes(byte[] buffer) throws IOException {
+ mDexFile.readFully(buffer);
+ }
+
+ /**
+ * Reads a single signed byte value.
+ */
+ byte readByte() throws IOException {
+ mDexFile.readFully(tmpBuf, 0, 1);
+ return tmpBuf[0];
+ }
+
+ /**
+ * Reads a signed 16-bit integer, byte-swapping if necessary.
+ */
+ short readShort() throws IOException {
+ mDexFile.readFully(tmpBuf, 0, 2);
+ if (isBigEndian) {
+ return (short) ((tmpBuf[1] & 0xff) | ((tmpBuf[0] & 0xff) << 8));
+ } else {
+ return (short) ((tmpBuf[0] & 0xff) | ((tmpBuf[1] & 0xff) << 8));
+ }
+ }
+
+ /**
+ * Reads a signed 32-bit integer, byte-swapping if necessary.
+ */
+ int readInt() throws IOException {
+ mDexFile.readFully(tmpBuf, 0, 4);
+
+ if (isBigEndian) {
+ return (tmpBuf[3] & 0xff) | ((tmpBuf[2] & 0xff) << 8) |
+ ((tmpBuf[1] & 0xff) << 16) | ((tmpBuf[0] & 0xff) << 24);
+ } else {
+ return (tmpBuf[0] & 0xff) | ((tmpBuf[1] & 0xff) << 8) |
+ ((tmpBuf[2] & 0xff) << 16) | ((tmpBuf[3] & 0xff) << 24);
+ }
+ }
+
+ /**
+ * Reads a variable-length unsigned LEB128 value. Does not attempt to
+ * verify that the value is valid.
+ *
+ * @throws EOFException if we run off the end of the file
+ */
+ int readUnsignedLeb128() throws IOException {
+ int result = 0;
+ byte val;
+
+ do {
+ val = readByte();
+ result = (result << 7) | (val & 0x7f);
+ } while (val < 0);
+
+ return result;
+ }
+
+ /**
+ * Reads a UTF-8 string.
+ *
+ * We don't know how long the UTF-8 string is, so we have to read one
+ * byte at a time. We could make an educated guess based on the
+ * utf16_size and seek back if we get it wrong, but seeking backward
+ * may cause the underlying implementation to reload I/O buffers.
+ */
+ String readString() throws IOException {
+ int utf16len = readUnsignedLeb128();
+ byte inBuf[] = new byte[utf16len * 3]; // worst case
+ int idx;
+
+ for (idx = 0; idx < inBuf.length; idx++) {
+ byte val = readByte();
+ if (val == 0)
+ break;
+ inBuf[idx] = val;
+ }
+
+ return new String(inBuf, 0, idx, "UTF-8");
+ }
+
+
+ /*
+ * =======================================================================
+ * Internal "structure" declarations
+ * =======================================================================
+ */
+
+ /**
+ * Holds the contents of a header_item.
+ */
+ static class HeaderItem {
+ public int fileSize;
+ public int headerSize;
+ public int endianTag;
+ public int stringIdsSize, stringIdsOff;
+ public int typeIdsSize, typeIdsOff;
+ public int protoIdsSize, protoIdsOff;
+ public int fieldIdsSize, fieldIdsOff;
+ public int methodIdsSize, methodIdsOff;
+ public int classDefsSize, classDefsOff;
+
+ /* expected magic values */
+ public static final byte[] DEX_FILE_MAGIC = {
+ 0x64, 0x65, 0x78, 0x0a, 0x30, 0x33, 0x35, 0x00 };
+ public static final int ENDIAN_CONSTANT = 0x12345678;
+ public static final int REVERSE_ENDIAN_CONSTANT = 0x78563412;
+ }
+
+ /**
+ * Holds the contents of a type_id_item.
+ *
+ * This is chiefly a list of indices into the string table. We need
+ * some additional bits of data, such as whether or not the type ID
+ * represents a class defined in this DEX, so we use an object for
+ * each instead of a simple integer. (Could use a parallel array, but
+ * since this is a desktop app it's not essential.)
+ */
+ static class TypeIdItem {
+ public int descriptorIdx; // index into string_ids
+
+ public boolean internal; // defined within this DEX file?
+ }
+
+ /**
+ * Holds the contents of a proto_id_item.
+ */
+ static class ProtoIdItem {
+ public int shortyIdx; // index into string_ids
+ public int returnTypeIdx; // index into type_ids
+ public int parametersOff; // file offset to a type_list
+
+ public int types[]; // contents of type list
+ }
+
+ /**
+ * Holds the contents of a field_id_item.
+ */
+ static class FieldIdItem {
+ public int classIdx; // index into type_ids (defining class)
+ public int typeIdx; // index into type_ids (field type)
+ public int nameIdx; // index into string_ids
+ }
+
+ /**
+ * Holds the contents of a method_id_item.
+ */
+ static class MethodIdItem {
+ public int classIdx; // index into type_ids
+ public int protoIdx; // index into proto_ids
+ public int nameIdx; // index into string_ids
+ }
+
+ /**
+ * Holds the contents of a class_def_item.
+ *
+ * We don't really need a class for this, but there's some stuff in
+ * the class_def_item that we might want later.
+ */
+ static class ClassDefItem {
+ public int classIdx; // index into type_ids
+ }
+}
diff --git a/tools/dexdeps/src/com/android/dexdeps/DexDataException.java b/tools/dexdeps/src/com/android/dexdeps/DexDataException.java
new file mode 100644
index 0000000..873db94
--- /dev/null
+++ b/tools/dexdeps/src/com/android/dexdeps/DexDataException.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dexdeps;
+
+/**
+ * Bad data found inside a DEX file.
+ */
+public class DexDataException extends RuntimeException {
+}
diff --git a/tools/dexdeps/src/com/android/dexdeps/FieldRef.java b/tools/dexdeps/src/com/android/dexdeps/FieldRef.java
new file mode 100644
index 0000000..93e87cb
--- /dev/null
+++ b/tools/dexdeps/src/com/android/dexdeps/FieldRef.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dexdeps;
+
+public class FieldRef {
+ private String mDeclClass, mFieldType, mFieldName;
+
+ /**
+ * Initializes a new field reference.
+ */
+ public FieldRef(String declClass, String fieldType, String fieldName) {
+ mDeclClass = declClass;
+ mFieldType = fieldType;
+ mFieldName = fieldName;
+ }
+
+ /**
+ * Gets the name of the field's declaring class.
+ */
+ public String getDeclClassName() {
+ return mDeclClass;
+ }
+
+ /**
+ * Gets the type name. Examples: "Ljava/lang/String;", "[I".
+ */
+ public String getTypeName() {
+ return mFieldType;
+ }
+
+ /**
+ * Gets the field name.
+ */
+ public String getName() {
+ return mFieldName;
+ }
+}
diff --git a/tools/dexdeps/src/com/android/dexdeps/Main.java b/tools/dexdeps/src/com/android/dexdeps/Main.java
new file mode 100644
index 0000000..d48889d
--- /dev/null
+++ b/tools/dexdeps/src/com/android/dexdeps/Main.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dexdeps;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.RandomAccessFile;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipInputStream;
+
+public class Main {
+ private static final String CLASSES_DEX = "classes.dex";
+
+ private String mInputFileName;
+ private String mOutputFormat = "xml";
+
+ /**
+ * Entry point.
+ */
+ public static void main(String[] args) {
+ Main main = new Main();
+ main.run(args);
+ }
+
+ /**
+ * Start things up.
+ */
+ void run(String[] args) {
+ try {
+ parseArgs(args);
+ RandomAccessFile raf = openInputFile();
+ DexData dexData = new DexData(raf);
+ dexData.load();
+
+ Output.generate(dexData, mOutputFormat);
+ } catch (UsageException ue) {
+ usage();
+ System.exit(2);
+ } catch (IOException ioe) {
+ if (ioe.getMessage() != null)
+ System.err.println("Failed: " + ioe);
+ System.exit(1);
+ } catch (DexDataException dde) {
+ /* a message was already reported, just bail quietly */
+ System.exit(1);
+ }
+ }
+
+ /**
+ * Opens the input file, which could be a .dex or a .jar/.apk with a
+ * classes.dex inside. If the latter, we extract the contents to a
+ * temporary file.
+ */
+ RandomAccessFile openInputFile() throws IOException {
+ RandomAccessFile raf;
+
+ raf = openInputFileAsZip();
+ if (raf == null) {
+ File inputFile = new File(mInputFileName);
+ raf = new RandomAccessFile(inputFile, "r");
+ }
+
+ return raf;
+ }
+
+ /**
+ * Tries to open the input file as a Zip archive (jar/apk) with a
+ * "classes.dex" inside.
+ *
+ * @return a RandomAccessFile for classes.dex, or null if the input file
+ * is not a zip archive
+ * @throws IOException if the file isn't found, or it's a zip and
+ * classes.dex isn't found inside
+ */
+ RandomAccessFile openInputFileAsZip() throws IOException {
+ ZipFile zipFile;
+
+ /*
+ * Try it as a zip file.
+ */
+ try {
+ zipFile = new ZipFile(mInputFileName);
+ } catch (FileNotFoundException fnfe) {
+ /* not found, no point in retrying as non-zip */
+ System.err.println("Unable to open '" + mInputFileName + "': " +
+ fnfe.getMessage());
+ throw fnfe;
+ } catch (ZipException ze) {
+ /* not a zip */
+ return null;
+ }
+
+ /*
+ * We know it's a zip; see if there's anything useful inside. A
+ * failure here results in some type of IOException (of which
+ * ZipException is a subclass).
+ */
+ ZipEntry entry = zipFile.getEntry(CLASSES_DEX);
+ if (entry == null) {
+ System.err.println("Unable to find '" + CLASSES_DEX +
+ "' in '" + mInputFileName + "'");
+ zipFile.close();
+ throw new ZipException();
+ }
+
+ InputStream zis = zipFile.getInputStream(entry);
+
+ /*
+ * Create a temp file to hold the DEX data, open it, and delete it
+ * to ensure it doesn't hang around if we fail.
+ */
+ File tempFile = File.createTempFile("dexdeps", ".dex");
+ //System.out.println("+++ using temp " + tempFile);
+ RandomAccessFile raf = new RandomAccessFile(tempFile, "rw");
+ tempFile.delete();
+
+ /*
+ * Copy all data from input stream to output file.
+ */
+ byte copyBuf[] = new byte[32768];
+ int actual;
+
+ while (true) {
+ actual = zis.read(copyBuf);
+ if (actual == -1)
+ break;
+
+ raf.write(copyBuf, 0, actual);
+ }
+
+ zis.close();
+ raf.seek(0);
+
+ return raf;
+ }
+
+
+ /**
+ * Parses command-line arguments.
+ *
+ * @throws UsageException if arguments are missing or poorly formed
+ */
+ void parseArgs(String[] args) {
+ int idx;
+
+ for (idx = 0; idx < args.length; idx++) {
+ String arg = args[idx];
+
+ if (arg.equals("--") || !arg.startsWith("--")) {
+ break;
+ } else if (arg.startsWith("--format=")) {
+ mOutputFormat = arg.substring(arg.indexOf('=') + 1);
+ if (!mOutputFormat.equals("brief") &&
+ !mOutputFormat.equals("xml"))
+ {
+ System.err.println("Unknown format '" + mOutputFormat +"'");
+ throw new UsageException();
+ }
+ //System.out.println("+++ using format " + mOutputFormat);
+ } else {
+ System.err.println("Unknown option '" + arg + "'");
+ throw new UsageException();
+ }
+ }
+
+ // expecting one argument left
+ if (idx != args.length - 1) {
+ throw new UsageException();
+ }
+
+ mInputFileName = args[idx];
+ }
+
+ /**
+ * Prints command-line usage info.
+ */
+ void usage() {
+ System.err.println("DEX dependency scanner v1.1");
+ System.err.println("Copyright (C) 2009 The Android Open Source Project\n");
+ System.err.println("Usage: dexdeps [options] <file.{dex,apk,jar}>");
+ System.err.println("Options:");
+ System.err.println(" --format={xml,brief}");
+ }
+}
diff --git a/tools/dexdeps/src/com/android/dexdeps/MethodRef.java b/tools/dexdeps/src/com/android/dexdeps/MethodRef.java
new file mode 100644
index 0000000..67dce62
--- /dev/null
+++ b/tools/dexdeps/src/com/android/dexdeps/MethodRef.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dexdeps;
+
+public class MethodRef {
+ private String mDeclClass, mReturnType, mMethodName;
+ private String[] mArgTypes;
+
+ /**
+ * Initializes a new field reference.
+ */
+ public MethodRef(String declClass, String[] argTypes, String returnType,
+ String methodName) {
+ mDeclClass = declClass;
+ mArgTypes = argTypes;
+ mReturnType = returnType;
+ mMethodName = methodName;
+ }
+
+ /**
+ * Gets the name of the method's declaring class.
+ */
+ public String getDeclClassName() {
+ return mDeclClass;
+ }
+
+ /**
+ * Gets the method's descriptor.
+ */
+ public String getDescriptor() {
+ return descriptorFromProtoArray(mArgTypes, mReturnType);
+ }
+
+ /**
+ * Gets the method's name.
+ */
+ public String getName() {
+ return mMethodName;
+ }
+
+ /**
+ * Gets an array of method argument types.
+ */
+ public String[] getArgumentTypeNames() {
+ return mArgTypes;
+ }
+
+ /**
+ * Gets the method's return type. Examples: "Ljava/lang/String;", "[I".
+ */
+ public String getReturnTypeName() {
+ return mReturnType;
+ }
+
+ /**
+ * Returns the method descriptor, given the argument and return type
+ * prototype strings.
+ */
+ private static String descriptorFromProtoArray(String[] protos,
+ String returnType) {
+ StringBuilder builder = new StringBuilder();
+
+ builder.append("(");
+ for (int i = 0; i < protos.length; i++) {
+ builder.append(protos[i]);
+ }
+
+ builder.append(")");
+ builder.append(returnType);
+
+ return builder.toString();
+ }
+}
diff --git a/tools/dexdeps/src/com/android/dexdeps/Output.java b/tools/dexdeps/src/com/android/dexdeps/Output.java
new file mode 100644
index 0000000..b786825
--- /dev/null
+++ b/tools/dexdeps/src/com/android/dexdeps/Output.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dexdeps;
+
+/**
+ * Generate fancy output.
+ */
+public class Output {
+ private static final String IN0 = "";
+ private static final String IN1 = " ";
+ private static final String IN2 = " ";
+ private static final String IN3 = " ";
+ private static final String IN4 = " ";
+
+ public static void generate(DexData dexData, String format) {
+ if (format.equals("brief")) {
+ printBrief(dexData);
+ } else if (format.equals("xml")) {
+ printXml(dexData);
+ } else {
+ /* should've been trapped in arg handler */
+ throw new RuntimeException("unknown output format");
+ }
+ }
+
+ /**
+ * Prints the data in a simple human-readable format.
+ */
+ static void printBrief(DexData dexData) {
+ ClassRef[] externClassRefs = dexData.getExternalReferences();
+
+ printClassRefs(externClassRefs);
+ printFieldRefs(externClassRefs);
+ printMethodRefs(externClassRefs);
+ }
+
+ /**
+ * Prints the list of classes in a simple human-readable format.
+ */
+ static void printClassRefs(ClassRef[] classes) {
+ System.out.println("Classes:");
+ for (int i = 0; i < classes.length; i++) {
+ ClassRef ref = classes[i];
+
+ System.out.println(descriptorToDot(ref.getName()));
+ }
+ }
+
+ /**
+ * Prints the list of fields in a simple human-readable format.
+ */
+ static void printFieldRefs(ClassRef[] classes) {
+ System.out.println("\nFields:");
+ for (int i = 0; i < classes.length; i++) {
+ FieldRef[] fields = classes[i].getFieldArray();
+
+ for (int j = 0; j < fields.length; j++) {
+ FieldRef ref = fields[j];
+
+ System.out.println(descriptorToDot(ref.getDeclClassName()) +
+ "." + ref.getName() + " : " + ref.getTypeName());
+ }
+ }
+ }
+
+ /**
+ * Prints the list of methods in a simple human-readable format.
+ */
+ static void printMethodRefs(ClassRef[] classes) {
+ System.out.println("\nMethods:");
+ for (int i = 0; i < classes.length; i++) {
+ MethodRef[] methods = classes[i].getMethodArray();
+
+ for (int j = 0; j < methods.length; j++) {
+ MethodRef ref = methods[j];
+
+ System.out.println(descriptorToDot(ref.getDeclClassName()) +
+ "." + ref.getName() + " : " + ref.getDescriptor());
+ }
+ }
+ }
+
+
+ /**
+ * Prints the output in XML format.
+ *
+ * We shouldn't need to XML-escape the field/method info.
+ */
+ static void printXml(DexData dexData) {
+ ClassRef[] externClassRefs = dexData.getExternalReferences();
+
+ System.out.println(IN0 + "<external>");
+
+ /*
+ * Iterate through externClassRefs. For each class, dump all of
+ * the matching fields and methods.
+ */
+ String prevPackage = null;
+ for (int i = 0; i < externClassRefs.length; i++) {
+ ClassRef cref = externClassRefs[i];
+ String declClassName = cref.getName();
+ String className = classNameOnly(declClassName);
+ String packageName = packageNameOnly(declClassName);
+
+ /*
+ * If we're in a different package, emit the appropriate tags.
+ */
+ if (!packageName.equals(prevPackage)) {
+ if (prevPackage != null) {
+ System.out.println(IN1 + "</package>");
+ }
+
+ System.out.println(IN1 +
+ "<package name=\"" + packageName + "\">");
+
+ prevPackage = packageName;
+ }
+
+ System.out.println(IN2 + "<class name=\"" + className + "\">");
+ printXmlFields(cref);
+ printXmlMethods(cref);
+ System.out.println(IN2 + "</class>");
+ }
+
+ if (prevPackage != null)
+ System.out.println(IN1 + "</package>");
+ System.out.println(IN0 + "</external>");
+ }
+
+ /**
+ * Prints the externally-visible fields in XML format.
+ */
+ private static void printXmlFields(ClassRef cref) {
+ FieldRef[] fields = cref.getFieldArray();
+ for (int i = 0; i < fields.length; i++) {
+ FieldRef fref = fields[i];
+
+ System.out.println(IN3 + "<field name=\"" + fref.getName() +
+ "\" type=\"" + descriptorToDot(fref.getTypeName()) + "\"/>");
+ }
+ }
+
+ /**
+ * Prints the externally-visible methods in XML format.
+ */
+ private static void printXmlMethods(ClassRef cref) {
+ MethodRef[] methods = cref.getMethodArray();
+ for (int i = 0; i < methods.length; i++) {
+ MethodRef mref = methods[i];
+ String declClassName = mref.getDeclClassName();
+ boolean constructor;
+
+ constructor = mref.getName().equals("<init>");
+ if (constructor) {
+ // use class name instead of method name
+ System.out.println(IN3 + "<constructor name=\"" +
+ classNameOnly(declClassName) + "\">");
+ } else {
+ System.out.println(IN3 + "<method name=\"" + mref.getName() +
+ "\" return=\"" + descriptorToDot(mref.getReturnTypeName()) +
+ "\">");
+ }
+ String[] args = mref.getArgumentTypeNames();
+ for (int j = 0; j < args.length; j++) {
+ System.out.println(IN4 + "<parameter type=\"" +
+ descriptorToDot(args[j]) + "\"/>");
+ }
+ if (constructor) {
+ System.out.println(IN3 + "</constructor>");
+ } else {
+ System.out.println(IN3 + "</method>");
+ }
+ }
+ }
+
+
+ /*
+ * =======================================================================
+ * Utility functions
+ * =======================================================================
+ */
+
+ /**
+ * Converts a single-character primitive type into its human-readable
+ * equivalent.
+ */
+ static String primitiveTypeLabel(char typeChar) {
+ /* primitive type; substitute human-readable name in */
+ switch (typeChar) {
+ case 'B': return "byte";
+ case 'C': return "char";
+ case 'D': return "double";
+ case 'F': return "float";
+ case 'I': return "int";
+ case 'J': return "long";
+ case 'S': return "short";
+ case 'V': return "void";
+ case 'Z': return "boolean";
+ default:
+ /* huh? */
+ System.err.println("Unexpected class char " + typeChar);
+ assert false;
+ return "UNKNOWN";
+ }
+ }
+
+ /**
+ * Converts a type descriptor to human-readable "dotted" form. For
+ * example, "Ljava/lang/String;" becomes "java.lang.String", and
+ * "[I" becomes "int[].
+ */
+ static String descriptorToDot(String descr) {
+ int targetLen = descr.length();
+ int offset = 0;
+ int arrayDepth = 0;
+
+ /* strip leading [s; will be added to end */
+ while (targetLen > 1 && descr.charAt(offset) == '[') {
+ offset++;
+ targetLen--;
+ }
+ arrayDepth = offset;
+
+ if (targetLen == 1) {
+ descr = primitiveTypeLabel(descr.charAt(offset));
+ offset = 0;
+ targetLen = descr.length();
+ } else {
+ /* account for leading 'L' and trailing ';' */
+ if (targetLen >= 2 && descr.charAt(offset) == 'L' &&
+ descr.charAt(offset+targetLen-1) == ';')
+ {
+ targetLen -= 2; /* two fewer chars to copy */
+ offset++; /* skip the 'L' */
+ }
+ }
+
+ char[] buf = new char[targetLen + arrayDepth * 2];
+
+ /* copy class name over */
+ int i;
+ for (i = 0; i < targetLen; i++) {
+ char ch = descr.charAt(offset + i);
+ buf[i] = (ch == '/') ? '.' : ch;
+ }
+
+ /* add the appopriate number of brackets for arrays */
+ while (arrayDepth-- > 0) {
+ buf[i++] = '[';
+ buf[i++] = ']';
+ }
+ assert i == buf.length;
+
+ return new String(buf);
+ }
+
+ /**
+ * Extracts the class name from a type descriptor.
+ */
+ static String classNameOnly(String typeName) {
+ String dotted = descriptorToDot(typeName);
+
+ int start = dotted.lastIndexOf(".");
+ if (start < 0) {
+ return dotted;
+ } else {
+ return dotted.substring(start+1);
+ }
+ }
+
+ /**
+ * Extracts the package name from a type descriptor, and returns it in
+ * dotted form.
+ */
+ static String packageNameOnly(String typeName) {
+ String dotted = descriptorToDot(typeName);
+
+ int end = dotted.lastIndexOf(".");
+ if (end < 0) {
+ /* lives in default package */
+ return "";
+ } else {
+ return dotted.substring(0, end);
+ }
+ }
+}
diff --git a/tools/dexdeps/src/com/android/dexdeps/UsageException.java b/tools/dexdeps/src/com/android/dexdeps/UsageException.java
new file mode 100644
index 0000000..e695fc7
--- /dev/null
+++ b/tools/dexdeps/src/com/android/dexdeps/UsageException.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dexdeps;
+
+/**
+ * Tells the main entry point to show the usage information and bail.
+ */
+public class UsageException extends RuntimeException {
+}
diff --git a/tools/dmtracedump/Android.mk b/tools/dmtracedump/Android.mk
new file mode 100644
index 0000000..5d3146e
--- /dev/null
+++ b/tools/dmtracedump/Android.mk
@@ -0,0 +1,24 @@
+#
+# Copyright 2006 The Android Open Source Project
+#
+# Java method trace dump tool
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := TraceDump.c
+LOCAL_CFLAGS += -O0 -g
+LOCAL_C_INCLUDES += dalvik/vm
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := dmtracedump
+include $(BUILD_HOST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := CreateTestTrace.c
+LOCAL_CFLAGS += -O0 -g
+LOCAL_C_INCLUDES += dalvik/vm
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := create_test_dmtrace
+include $(BUILD_HOST_EXECUTABLE)
diff --git a/tools/dmtracedump/CreateTestTrace.c b/tools/dmtracedump/CreateTestTrace.c
new file mode 100644
index 0000000..0b85d51
--- /dev/null
+++ b/tools/dmtracedump/CreateTestTrace.c
@@ -0,0 +1,493 @@
+/* //device/tools/dmtracedump/CreateTrace.c
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+/*
+ * Create a test file in the format required by dmtrace.
+ */
+#define NOT_VM
+#include "Profile.h" // from VM header
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <time.h>
+#include <ctype.h>
+
+/*
+ * Values from the header of the data file.
+ */
+typedef struct DataHeader {
+ unsigned int magic;
+ short version;
+ short offsetToData;
+ long long startWhen;
+} DataHeader;
+
+#define VERSION 2
+int versionNumber = VERSION;
+int verbose = 0;
+
+DataHeader header = { 0x574f4c53, VERSION, sizeof(DataHeader), 0LL };
+
+char *versionHeader = "*version\n";
+char *clockDef = "clock=thread-cpu\n";
+
+char *keyThreads =
+"*threads\n"
+"1 main\n"
+"2 foo\n"
+"3 bar\n"
+"4 blah\n";
+
+char *keyEnd = "*end\n";
+
+typedef struct dataRecord {
+ unsigned int time;
+ int threadId;
+ unsigned int action; /* 0=entry, 1=exit, 2=exception exit */
+ char *fullName;
+ char *className;
+ char *methodName;
+ char *signature;
+ unsigned int methodId;
+} dataRecord;
+
+dataRecord *records;
+
+#define BUF_SIZE 1024
+char buf[BUF_SIZE];
+
+typedef struct stack {
+ dataRecord **frames;
+ int indentLevel;
+} stack;
+
+/* Mac OS doesn't have strndup(), so implement it here.
+ */
+char *strndup(const char *src, size_t len)
+{
+ char *dest = (char *) malloc(len + 1);
+ strncpy(dest, src, len);
+ dest[len] = 0;
+ return dest;
+}
+
+/*
+ * Parse the input file. It looks something like this:
+ * # This is a comment line
+ * 4 1 A
+ * 6 1 B
+ * 8 1 B
+ * 10 1 A
+ *
+ * where the first column is the time, the second column is the thread id,
+ * and the third column is the method (actually just the class name). The
+ * number of spaces between the 2nd and 3rd columns is the indentation and
+ * determines the call stack. Each called method must be indented by one
+ * more space. In the example above, A is called at time 4, A calls B at
+ * time 6, B returns at time 8, and A returns at time 10. Thread 1 is the
+ * only thread that is running.
+ *
+ * An alternative file format leaves out the first two columns:
+ * A
+ * B
+ * B
+ * A
+ *
+ * In this file format, the thread id is always 1, and the time starts at
+ * 2 and increments by 2 for each line.
+ */
+void parseInputFile(const char *inputFileName)
+{
+ unsigned int time = 0, threadId;
+ int len;
+ int linenum = 0;
+ int nextRecord = 0;
+ int indentLevel = 0;
+ stack *callStack;
+
+ FILE *inputFp = fopen(inputFileName, "r");
+ if (inputFp == NULL) {
+ perror(inputFileName);
+ exit(1);
+ }
+
+ /* Count the number of lines in the buffer */
+ int numRecords = 0;
+ int maxThreadId = 1;
+ int maxFrames = 0;
+ char *indentEnd;
+ while (fgets(buf, BUF_SIZE, inputFp)) {
+ char *cp = buf;
+ if (*cp == '#')
+ continue;
+ numRecords += 1;
+ if (isdigit(*cp)) {
+ int time = strtoul(cp, &cp, 0);
+ while (isspace(*cp))
+ cp += 1;
+ int threadId = strtoul(cp, &cp, 0);
+ if (maxThreadId < threadId)
+ maxThreadId = threadId;
+ }
+ indentEnd = cp;
+ while (isspace(*indentEnd))
+ indentEnd += 1;
+ if (indentEnd - cp + 1 > maxFrames)
+ maxFrames = indentEnd - cp + 1;
+ }
+ int numThreads = maxThreadId + 1;
+
+ /* Add space for a sentinel record at the end */
+ numRecords += 1;
+ records = (dataRecord *) malloc(sizeof(dataRecord) * numRecords);
+ callStack = (stack *) malloc(sizeof(stack) * numThreads);
+ int ii;
+ for (ii = 0; ii < numThreads; ++ii) {
+ callStack[ii].frames = NULL;
+ callStack[ii].indentLevel = 0;
+ }
+
+ rewind(inputFp);
+ while (fgets(buf, BUF_SIZE, inputFp)) {
+ int indent;
+ int action;
+ char *save_cp;
+
+ linenum += 1;
+ char *cp = buf;
+
+ /* Skip lines that start with '#' */
+ if (*cp == '#')
+ continue;
+
+ /* Get time and thread id */
+ if (!isdigit(*cp)) {
+ /* If the line does not begin with a digit, then fill in
+ * default values for the time and threadId.
+ */
+ time += 2;
+ threadId = 1;
+ } else {
+ time = strtoul(cp, &cp, 0);
+ while (isspace(*cp))
+ cp += 1;
+ threadId = strtoul(cp, &cp, 0);
+ cp += 1;
+ }
+
+ // Allocate space for the thread stack, if necessary
+ if (callStack[threadId].frames == NULL) {
+ dataRecord **stk;
+ stk = (dataRecord **) malloc(sizeof(dataRecord *) * maxFrames);
+ callStack[threadId].frames = stk;
+ }
+ indentLevel = callStack[threadId].indentLevel;
+
+ save_cp = cp;
+ while (isspace(*cp)) {
+ cp += 1;
+ }
+ indent = cp - save_cp + 1;
+ records[nextRecord].time = time;
+ records[nextRecord].threadId = threadId;
+
+ save_cp = cp;
+ while (*cp != '\n')
+ cp += 1;
+
+ /* Remove trailing spaces */
+ cp -= 1;
+ while (isspace(*cp))
+ cp -= 1;
+ cp += 1;
+ len = cp - save_cp;
+ records[nextRecord].fullName = strndup(save_cp, len);
+
+ /* Parse the name to support "class.method signature" */
+ records[nextRecord].className = NULL;
+ records[nextRecord].methodName = NULL;
+ records[nextRecord].signature = NULL;
+ cp = strchr(save_cp, '.');
+ if (cp) {
+ len = cp - save_cp;
+ if (len > 0)
+ records[nextRecord].className = strndup(save_cp, len);
+ save_cp = cp + 1;
+ cp = strchr(save_cp, ' ');
+ if (cp == NULL)
+ cp = strchr(save_cp, '\n');
+ if (cp && cp > save_cp) {
+ len = cp - save_cp;
+ records[nextRecord].methodName = strndup(save_cp, len);
+ save_cp = cp + 1;
+ cp = strchr(save_cp, ' ');
+ if (cp == NULL)
+ cp = strchr(save_cp, '\n');
+ if (cp && cp > save_cp) {
+ len = cp - save_cp;
+ records[nextRecord].signature = strndup(save_cp, len);
+ }
+ }
+ }
+
+ if (verbose) {
+ printf("Indent: %d; IndentLevel: %d; Line: %s", indent, indentLevel, buf);
+ }
+
+ action = 0;
+ if (indent == indentLevel + 1) { // Entering a method
+ if (verbose)
+ printf(" Entering %s\n", records[nextRecord].fullName);
+ callStack[threadId].frames[indentLevel] = &records[nextRecord];
+ } else if (indent == indentLevel) { // Exiting a method
+ // Exiting method must be currently on top of stack (unless stack is empty)
+ if (callStack[threadId].frames[indentLevel - 1] == NULL) {
+ if (verbose)
+ printf(" Exiting %s (past bottom of stack)\n", records[nextRecord].fullName);
+ callStack[threadId].frames[indentLevel - 1] = &records[nextRecord];
+ action = 1;
+ } else {
+ if (indentLevel < 1) {
+ fprintf(stderr, "Error: line %d: %s", linenum, buf);
+ fprintf(stderr, " expected positive (>0) indentation, found %d\n",
+ indent);
+ exit(1);
+ }
+ char *name = callStack[threadId].frames[indentLevel - 1]->fullName;
+ if (strcmp(name, records[nextRecord].fullName) == 0) {
+ if (verbose)
+ printf(" Exiting %s\n", name);
+ action = 1;
+ } else { // exiting method doesn't match stack's top method
+ fprintf(stderr, "Error: line %d: %s", linenum, buf);
+ fprintf(stderr, " expected exit from %s\n",
+ callStack[threadId].frames[indentLevel - 1]->fullName);
+ exit(1);
+ }
+ }
+ } else {
+ if (nextRecord != 0) {
+ fprintf(stderr, "Error: line %d: %s", linenum, buf);
+ fprintf(stderr, " expected indentation %d [+1], found %d\n",
+ indentLevel, indent);
+ exit(1);
+ }
+
+ if (verbose) {
+ printf(" Nonzero indent at first record\n");
+ printf(" Entering %s\n", records[nextRecord].fullName);
+ }
+
+ // This is the first line of data, so we allow a larger
+ // initial indent. This allows us to test popping off more
+ // frames than we entered.
+ indentLevel = indent - 1;
+ callStack[threadId].frames[indentLevel] = &records[nextRecord];
+ }
+
+ if (action == 0)
+ indentLevel += 1;
+ else
+ indentLevel -= 1;
+ records[nextRecord].action = action;
+ callStack[threadId].indentLevel = indentLevel;
+
+ nextRecord += 1;
+ }
+
+ /* Mark the last record with a sentinel */
+ memset(&records[nextRecord], 0, sizeof(dataRecord));
+}
+
+
+/*
+ * Write values to the binary data file.
+ */
+void write2LE(FILE* fp, unsigned short val)
+{
+ putc(val & 0xff, fp);
+ putc(val >> 8, fp);
+}
+
+void write4LE(FILE* fp, unsigned int val)
+{
+ putc(val & 0xff, fp);
+ putc((val >> 8) & 0xff, fp);
+ putc((val >> 16) & 0xff, fp);
+ putc((val >> 24) & 0xff, fp);
+}
+
+void write8LE(FILE* fp, unsigned long long val)
+{
+ putc(val & 0xff, fp);
+ putc((val >> 8) & 0xff, fp);
+ putc((val >> 16) & 0xff, fp);
+ putc((val >> 24) & 0xff, fp);
+ putc((val >> 32) & 0xff, fp);
+ putc((val >> 40) & 0xff, fp);
+ putc((val >> 48) & 0xff, fp);
+ putc((val >> 56) & 0xff, fp);
+}
+
+void writeDataRecord(FILE *dataFp, int threadId, unsigned int methodVal,
+ unsigned int elapsedTime)
+{
+ if (versionNumber == 1)
+ putc(threadId, dataFp);
+ else
+ write2LE(dataFp, threadId);
+ write4LE(dataFp, methodVal);
+ write4LE(dataFp, elapsedTime);
+}
+
+void writeDataHeader(FILE *dataFp)
+{
+ struct timeval tv;
+ struct timezone tz;
+
+ gettimeofday(&tv, &tz);
+ unsigned long long startTime = tv.tv_sec;
+ startTime = (startTime << 32) | tv.tv_usec;
+ header.version = versionNumber;
+ write4LE(dataFp, header.magic);
+ write2LE(dataFp, header.version);
+ write2LE(dataFp, header.offsetToData);
+ write8LE(dataFp, startTime);
+}
+
+void writeKeyMethods(FILE *keyFp)
+{
+ dataRecord *pRecord, *pNext;
+ char *methodStr = "*methods\n";
+ fwrite(methodStr, strlen(methodStr), 1, keyFp);
+
+ /* Assign method ids in multiples of 4 */
+ unsigned int methodId = 0;
+ for (pRecord = records; pRecord->fullName; ++pRecord) {
+ if (pRecord->methodId)
+ continue;
+ unsigned int id = ++methodId << 2;
+ pRecord->methodId = id;
+
+ /* Assign this id to all the other records that have the
+ * same name.
+ */
+ for (pNext = pRecord + 1; pNext->fullName; ++pNext) {
+ if (pNext->methodId)
+ continue;
+ if (strcmp(pRecord->fullName, pNext->fullName) == 0)
+ pNext->methodId = id;
+ }
+ if (pRecord->className == NULL || pRecord->methodName == NULL) {
+ fprintf(keyFp, "0x%x %s m ()\n",
+ pRecord->methodId, pRecord->fullName);
+ } else if (pRecord->signature == NULL) {
+ fprintf(keyFp, "0x%x %s %s ()\n",
+ pRecord->methodId, pRecord->className,
+ pRecord->methodName);
+ } else {
+ fprintf(keyFp, "0x%x %s %s %s\n",
+ pRecord->methodId, pRecord->className,
+ pRecord->methodName, pRecord->signature);
+ }
+ }
+}
+
+void writeKeys(FILE *keyFp)
+{
+ fprintf(keyFp, "%s%d\n%s", versionHeader, versionNumber, clockDef);
+ fwrite(keyThreads, strlen(keyThreads), 1, keyFp);
+ writeKeyMethods(keyFp);
+ fwrite(keyEnd, strlen(keyEnd), 1, keyFp);
+}
+
+void writeDataRecords(FILE *dataFp)
+{
+ dataRecord *pRecord;
+
+ for (pRecord = records; pRecord->fullName; ++pRecord) {
+ unsigned int val = METHOD_COMBINE(pRecord->methodId, pRecord->action);
+ writeDataRecord(dataFp, pRecord->threadId, val, pRecord->time);
+ }
+}
+
+void writeTrace(const char* traceFileName)
+{
+ FILE *fp = fopen(traceFileName, "w");
+ if (fp == NULL) {
+ perror(traceFileName);
+ exit(1);
+ }
+ writeKeys(fp);
+ writeDataHeader(fp);
+ writeDataRecords(fp);
+ fclose(fp);
+}
+
+int parseOptions(int argc, char **argv)
+{
+ int err = 0;
+ while (1) {
+ int opt = getopt(argc, argv, "v:d");
+ if (opt == -1)
+ break;
+ switch (opt) {
+ case 'v':
+ versionNumber = strtoul(optarg, NULL, 0);
+ if (versionNumber != 1 && versionNumber != 2) {
+ fprintf(stderr, "Error: version number (%d) must be 1 or 2\n",
+ versionNumber);
+ err = 1;
+ }
+ break;
+ case 'd':
+ verbose = 1;
+ break;
+ default:
+ err = 1;
+ break;
+ }
+ }
+ return err;
+}
+
+int main(int argc, char** argv)
+{
+ char *inputFile;
+ char *traceFileName = NULL;
+ int len;
+
+ if (parseOptions(argc, argv) || argc - optind != 2) {
+ fprintf(stderr, "Usage: %s [-v version] [-d] input_file trace_prefix\n",
+ argv[0]);
+ exit(1);
+ }
+
+ inputFile = argv[optind++];
+ parseInputFile(inputFile);
+ traceFileName = argv[optind++];
+
+ writeTrace(traceFileName);
+
+ return 0;
+}
diff --git a/tools/dmtracedump/TraceDump.c b/tools/dmtracedump/TraceDump.c
new file mode 100644
index 0000000..3f80064
--- /dev/null
+++ b/tools/dmtracedump/TraceDump.c
@@ -0,0 +1,3631 @@
+/* //device/tools/dmtracedump/TraceDump.c
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+/*
+ * Process dmtrace output.
+ *
+ * This is the wrong way to go about it -- C is a clumsy language for
+ * shuffling data around. It'll do for a first pass.
+ */
+#define NOT_VM
+#include "Profile.h" // from VM header
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <time.h>
+#include <errno.h>
+#include <assert.h>
+
+/* Version number in the key file. Version 1 uses one byte for the thread id.
+ * Version 2 uses two bytes for the thread ids.
+ */
+int versionNumber;
+
+/* arbitrarily limit indentation */
+#define MAX_STACK_DEPTH 10000
+
+/* thread list in key file is not reliable, so just max out */
+#define MAX_THREADS 32768
+
+/* Size of temporary buffers for escaping html strings */
+#define HTML_BUFSIZE 10240
+
+/* Size of methodId->method cache */
+#define METHOD_CACHE_SIZE 2048
+#define METHOD_CACHE_SIZE_MASK (METHOD_CACHE_SIZE - 1)
+
+/* Some filter constants */
+#define FILTER_TAG '*'
+#define FILTER_FLAG_THREAD '+'
+#define FILTER_TYPE_CLASS 0
+#define FILTER_TYPE_METHOD 1
+
+#define DEFAULT_ACTIVE_THREADS 8
+
+char *htmlHeader =
+"<html>\n<head>\n<script type=\"text/javascript\" src=\"%ssortable.js\"></script>\n"
+"<script langugage=\"javascript\">\n"
+"function toggle(item) {\n"
+" obj=document.getElementById(item);\n"
+" visible=(obj.style.display!=\"none\" && obj.style.display!=\"\");\n"
+" key=document.getElementById(\"x\" + item);\n"
+" if (visible) {\n"
+" obj.style.display=\"none\";\n"
+" key.innerHTML=\"+\";\n"
+" } else {\n"
+" obj.style.display=\"block\";\n"
+" key.innerHTML=\"-\";\n"
+" }\n"
+"}\n"
+"function onMouseOver(obj) {\n"
+" obj.style.background=\"lightblue\";\n"
+"}\n"
+"function onMouseOut(obj) {\n"
+" obj.style.background=\"white\";\n"
+"}\n"
+"</script>\n"
+"<style type=\"text/css\">\n"
+"div { font-family: courier; font-size: 13 }\n"
+"div.parent { margin-left: 15; display: none }\n"
+"div.leaf { margin-left: 10 }\n"
+"div.header { margin-left: 10 }\n"
+"div.link { margin-left: 10; cursor: move }\n"
+"span.parent { padding-right: 10; }\n"
+"span.leaf { padding-right: 10; }\n"
+"a img { border: 0;}\n"
+"table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}\n"
+"a { text-decoration: none; }\n"
+"a:hover { text-decoration: underline; }\n"
+"table.sortable th, table.sortable td { text-align: left;}"
+"table.sortable tr.odd td { background-color: #ddd; }\n"
+"table.sortable tr.even td { background-color: #fff; }\n"
+"</style>\n"
+"</head><body>\n\n";
+
+char *htmlFooter = "\n</body>\n</html>\n";
+char *profileSeparator =
+ "======================================================================";
+
+const char* tableHeader =
+ "<table class='sortable' id='%s'><tr>\n"
+ "<th>Method</th>\n"
+ "<th>Run 1 (us)</th>\n"
+ "<th>Run 2 (us)</th>\n"
+ "<th>Diff (us)</th>\n"
+ "<th>Diff (%%)</th>\n"
+ "<th>1: # calls</th>\n"
+ "<th>2: # calls</th>\n"
+ "</tr>\n";
+
+const char* tableHeaderMissing =
+ "<table class='sortable' id='%s'>\n"
+ "<th>Method</th>\n"
+ "<th>Exclusive</th>\n"
+ "<th>Inclusive</th>\n"
+ "<th># calls</th>\n";
+
+#define GRAPH_LABEL_VISITED 0x0001
+#define GRAPH_NODE_VISITED 0x0002
+
+/*
+ * Values from the header of the data file.
+ */
+typedef struct DataHeader {
+ unsigned int magic;
+ short version;
+ short offsetToData;
+ long long startWhen;
+} DataHeader;
+
+/*
+ * Entry from the thread list.
+ */
+typedef struct ThreadEntry {
+ int threadId;
+ const char* threadName;
+ uint64_t elapsedTime;
+} ThreadEntry;
+
+struct MethodEntry;
+typedef struct TimedMethod {
+ struct TimedMethod *next;
+ uint64_t elapsedInclusive;
+ int numCalls;
+ struct MethodEntry *method;
+} TimedMethod;
+
+typedef struct ClassEntry {
+ const char *className;
+ uint64_t elapsedExclusive;
+ int numMethods;
+ struct MethodEntry **methods; /* list of methods in this class */
+ int numCalls[2]; /* 0=normal, 1=recursive */
+} ClassEntry;
+
+typedef struct UniqueMethodEntry {
+ uint64_t elapsedExclusive;
+ int numMethods;
+ struct MethodEntry **methods; /* list of methods with same name */
+ int numCalls[2]; /* 0=normal, 1=recursive */
+} UniqueMethodEntry;
+
+/*
+ * Entry from the method list.
+ */
+typedef struct MethodEntry {
+ unsigned int methodId;
+ const char* className;
+ const char* methodName;
+ const char* signature;
+ const char* fileName;
+ int lineNum;
+ uint64_t elapsedExclusive;
+ uint64_t elapsedInclusive;
+ uint64_t topExclusive; /* non-recursive exclusive time */
+ uint64_t recursiveInclusive;
+ struct TimedMethod *parents[2]; /* 0=normal, 1=recursive */
+ struct TimedMethod *children[2]; /* 0=normal, 1=recursive */
+ int numCalls[2]; /* 0=normal, 1=recursive */
+ int index; /* used after sorting to number methods */
+ int recursiveEntries; /* number of entries on the stack */
+ int graphState; /* used when graphing to see if this method has been visited before */
+} MethodEntry;
+
+/*
+ * The parsed contents of the key file.
+ */
+typedef struct DataKeys {
+ char* fileData; /* contents of the entire file */
+ long fileLen;
+ int numThreads;
+ ThreadEntry* threads;
+ int numMethods;
+ MethodEntry* methods; /* 2 extra methods: "toplevel" and "unknown" */
+ int* methodCache; /* methodId->methodIndex mapping */
+ // TODO change to map methodId->method itself
+} DataKeys;
+
+#define TOPLEVEL_INDEX 0
+#define UNKNOWN_INDEX 1
+
+typedef struct StackEntry {
+ MethodEntry *method;
+ uint64_t entryTime;
+} StackEntry;
+
+typedef struct CallStack {
+ int top;
+ StackEntry calls[MAX_STACK_DEPTH];
+ uint64_t lastEventTime;
+ uint64_t threadStartTime;
+ uint64_t* remTimes;
+ // Note: remTimes keeps a sum of 'un-allocated' time for each thread, in case
+ // we need to allocate it to one (or many) filter later. This would happen when
+ // we see a method exit that maches a filter, but whose entry we hadn't seen.
+ // TODO: consider moving remTimes into FilterTimes and change logic appropriately
+} CallStack;
+
+typedef struct DiffEntry {
+ MethodEntry* method1;
+ MethodEntry* method2;
+ int64_t differenceExclusive;
+ int64_t differenceInclusive;
+ double differenceExclusivePercentage;
+ double differenceInclusivePercentage;
+} DiffEntry;
+
+// Global options
+typedef struct Options {
+ const char* traceFileName;
+ const char* diffFileName;
+ const char* graphFileName;
+ const char* filterFileName;
+ int keepDotFile;
+ int dump;
+ int outputHtml;
+ const char* sortableUrl;
+ int threshold;
+} Options;
+
+typedef struct TraceData {
+ int numClasses;
+ ClassEntry *classes;
+ CallStack *stacks[MAX_THREADS];
+ int depth[MAX_THREADS];
+ int numUniqueMethods;
+ UniqueMethodEntry *uniqueMethods;
+} TraceData;
+
+typedef struct FilterKey {
+ int type[2]; /* 0=class, 1=method; 2 needed for start and end keys */
+ uint32_t flags; /* 1st bit = include cross-thread time */
+ char* keys[2]; /* 2 needed for start and end keys */
+} FilterKey;
+
+typedef struct FilterTimes {
+ uint64_t totalWaitTime;
+ uint64_t* threadWaitTimes;
+ uint64_t* threadExecutionTimesWhileWaiting;
+ uint64_t* threadExecutionTimes;
+} FilterTimes;
+
+typedef struct Filter {
+ char* filterName;
+ FilterKey* filterKeys;
+ int numKeys;
+ int activeCount;
+ int* activeThreads;
+ int* activationKeys;
+ FilterTimes times;
+} Filter;
+
+int numFilters = 0; // global
+
+static Options gOptions;
+
+/* Escapes characters in the source string that are html special entities.
+ * The escaped string is written to "dest" which must be large enough to
+ * hold the result. A pointer to "dest" is returned. The characters and
+ * their corresponding escape sequences are:
+ * '<' &lt;
+ * '>' &gt;
+ * '&' &amp;
+ */
+char *htmlEscape(const char *src, char *dest, int len)
+{
+ char *destStart = dest;
+
+ if (src == NULL)
+ return NULL;
+
+ int nbytes = 0;
+ while (*src) {
+ if (*src == '<') {
+ nbytes += 4;
+ if (nbytes >= len)
+ break;
+ *dest++ = '&';
+ *dest++ = 'l';
+ *dest++ = 't';
+ *dest++ = ';';
+ } else if (*src == '>') {
+ nbytes += 4;
+ if (nbytes >= len)
+ break;
+ *dest++ = '&';
+ *dest++ = 'g';
+ *dest++ = 't';
+ *dest++ = ';';
+ } else if (*src == '&') {
+ nbytes += 5;
+ if (nbytes >= len)
+ break;
+ *dest++ = '&';
+ *dest++ = 'a';
+ *dest++ = 'm';
+ *dest++ = 'p';
+ *dest++ = ';';
+ } else {
+ nbytes += 1;
+ if (nbytes >= len)
+ break;
+ *dest++ = *src;
+ }
+ src += 1;
+ }
+ if (nbytes >= len) {
+ fprintf(stderr, "htmlEscape(): buffer overflow\n");
+ exit(1);
+ }
+ *dest = 0;
+
+ return destStart;
+}
+
+/* Initializes a MethodEntry
+ */
+void initMethodEntry(MethodEntry *method, unsigned int methodId,
+ const char *className, const char *methodName,
+ const char *signature, const char* fileName,
+ const char* lineNumStr)
+{
+ method->methodId = methodId;
+ method->className = className;
+ method->methodName = methodName;
+ method->signature = signature;
+ method->fileName = fileName;
+ method->lineNum = (lineNumStr != NULL) ? atoi(lineNumStr) : -1;
+ method->elapsedExclusive = 0;
+ method->elapsedInclusive = 0;
+ method->topExclusive = 0;
+ method->recursiveInclusive = 0;
+ method->parents[0] = NULL;
+ method->parents[1] = NULL;
+ method->children[0] = NULL;
+ method->children[1] = NULL;
+ method->numCalls[0] = 0;
+ method->numCalls[1] = 0;
+ method->index = 0;
+ method->recursiveEntries = 0;
+}
+
+/*
+ * This comparison function is called from qsort() to sort
+ * methods into decreasing order of exclusive elapsed time.
+ */
+int compareElapsedExclusive(const void *a, const void *b) {
+ uint64_t elapsed1, elapsed2;
+ int result;
+
+ const MethodEntry *methodA = *(const MethodEntry**)a;
+ const MethodEntry *methodB = *(const MethodEntry**)b;
+ elapsed1 = methodA->elapsedExclusive;
+ elapsed2 = methodB->elapsedExclusive;
+ if (elapsed1 < elapsed2)
+ return 1;
+ if (elapsed1 > elapsed2)
+ return -1;
+
+ /* If the elapsed times of two methods are equal, then sort them
+ * into alphabetical order.
+ */
+ result = strcmp(methodA->className, methodB->className);
+ if (result == 0) {
+ if (methodA->methodName == NULL || methodB->methodName == NULL) {
+ unsigned int idA = methodA->methodId;
+ unsigned int idB = methodB->methodId;
+ if (idA < idB)
+ return -1;
+ if (idA > idB)
+ return 1;
+ return 0;
+ }
+ result = strcmp(methodA->methodName, methodB->methodName);
+ if (result == 0)
+ result = strcmp(methodA->signature, methodB->signature);
+ }
+ return result;
+}
+
+/*
+ * This comparison function is called from qsort() to sort
+ * methods into decreasing order of inclusive elapsed time.
+ */
+int compareElapsedInclusive(const void *a, const void *b) {
+ const MethodEntry *methodA, *methodB;
+ uint64_t elapsed1, elapsed2;
+ int result;
+
+ methodA = *(MethodEntry const **)a;
+ methodB = *(MethodEntry const **)b;
+ elapsed1 = methodA->elapsedInclusive;
+ elapsed2 = methodB->elapsedInclusive;
+ if (elapsed1 < elapsed2)
+ return 1;
+ if (elapsed1 > elapsed2)
+ return -1;
+
+ /* If the elapsed times of two methods are equal, then sort them
+ * into alphabetical order.
+ */
+ result = strcmp(methodA->className, methodB->className);
+ if (result == 0) {
+ if (methodA->methodName == NULL || methodB->methodName == NULL) {
+ unsigned int idA = methodA->methodId;
+ unsigned int idB = methodB->methodId;
+ if (idA < idB)
+ return -1;
+ if (idA > idB)
+ return 1;
+ return 0;
+ }
+ result = strcmp(methodA->methodName, methodB->methodName);
+ if (result == 0)
+ result = strcmp(methodA->signature, methodB->signature);
+ }
+ return result;
+}
+
+/*
+ * This comparison function is called from qsort() to sort
+ * threads into decreasing order of elapsed time.
+ */
+int compareElapsed(const void *a, const void *b) {
+ const ThreadEntry *threadA, *threadB;
+ uint64_t elapsed1, elapsed2;
+ int result = 0;
+
+ threadA = (ThreadEntry const *)a;
+ threadB = (ThreadEntry const *)b;
+ elapsed1 = threadA->elapsedTime;
+ elapsed2 = threadB->elapsedTime;
+ if (elapsed1 < elapsed2)
+ return 1;
+ if (elapsed1 > elapsed2)
+ return -1;
+
+ /* If the elapsed times of two threads are equal, then sort them
+ * by thread id.
+ */
+ int idA = threadA->threadId;
+ int idB = threadB->threadId;
+ if (idA < idB)
+ result = -1;
+ if (idA > idB)
+ result = 1;
+
+ return result;
+}
+
+/*
+ * This comparison function is called from qsort() to sort
+ * TimedMethods into decreasing order of inclusive elapsed time.
+ */
+int compareTimedMethod(const void *a, const void *b) {
+ const TimedMethod *timedA, *timedB;
+ uint64_t elapsed1, elapsed2;
+ int result;
+
+ timedA = (TimedMethod const *)a;
+ timedB = (TimedMethod const *)b;
+ elapsed1 = timedA->elapsedInclusive;
+ elapsed2 = timedB->elapsedInclusive;
+ if (elapsed1 < elapsed2)
+ return 1;
+ if (elapsed1 > elapsed2)
+ return -1;
+
+ /* If the elapsed times of two methods are equal, then sort them
+ * into alphabetical order.
+ */
+ MethodEntry *methodA = timedA->method;
+ MethodEntry *methodB = timedB->method;
+ result = strcmp(methodA->className, methodB->className);
+ if (result == 0) {
+ if (methodA->methodName == NULL || methodB->methodName == NULL) {
+ unsigned int idA = methodA->methodId;
+ unsigned int idB = methodB->methodId;
+ if (idA < idB)
+ return -1;
+ if (idA > idB)
+ return 1;
+ return 0;
+ }
+ result = strcmp(methodA->methodName, methodB->methodName);
+ if (result == 0)
+ result = strcmp(methodA->signature, methodB->signature);
+ }
+ return result;
+}
+
+/*
+ * This comparison function is called from qsort() to sort
+ * MethodEntry pointers into alphabetical order of class names.
+ */
+int compareClassNames(const void *a, const void *b) {
+ int result;
+
+ const MethodEntry *methodA = *(const MethodEntry**)a;
+ const MethodEntry *methodB = *(const MethodEntry**)b;
+ result = strcmp(methodA->className, methodB->className);
+ if (result == 0) {
+ unsigned int idA = methodA->methodId;
+ unsigned int idB = methodB->methodId;
+ if (idA < idB)
+ return -1;
+ if (idA > idB)
+ return 1;
+ return 0;
+ }
+ return result;
+}
+
+/*
+ * This comparison function is called from qsort() to sort
+ * classes into decreasing order of exclusive elapsed time.
+ */
+int compareClassExclusive(const void *a, const void *b) {
+ uint64_t elapsed1, elapsed2;
+ int result;
+
+ const ClassEntry *classA = *(const ClassEntry**)a;
+ const ClassEntry *classB = *(const ClassEntry**)b;
+ elapsed1 = classA->elapsedExclusive;
+ elapsed2 = classB->elapsedExclusive;
+ if (elapsed1 < elapsed2)
+ return 1;
+ if (elapsed1 > elapsed2)
+ return -1;
+
+ /* If the elapsed times of two classs are equal, then sort them
+ * into alphabetical order.
+ */
+ result = strcmp(classA->className, classB->className);
+ if (result == 0) {
+ /* Break ties with the first method id. This is probably not
+ * needed.
+ */
+ unsigned int idA = classA->methods[0]->methodId;
+ unsigned int idB = classB->methods[0]->methodId;
+ if (idA < idB)
+ return -1;
+ if (idA > idB)
+ return 1;
+ return 0;
+ }
+ return result;
+}
+
+/*
+ * This comparison function is called from qsort() to sort
+ * MethodEntry pointers into alphabetical order by method name,
+ * then by class name.
+ */
+int compareMethodNames(const void *a, const void *b) {
+ int result;
+
+ const MethodEntry *methodA = *(const MethodEntry**)a;
+ const MethodEntry *methodB = *(const MethodEntry**)b;
+ if (methodA->methodName == NULL || methodB->methodName == NULL) {
+ return compareClassNames(a, b);
+ }
+ result = strcmp(methodA->methodName, methodB->methodName);
+ if (result == 0) {
+ result = strcmp(methodA->className, methodB->className);
+ if (result == 0) {
+ unsigned int idA = methodA->methodId;
+ unsigned int idB = methodB->methodId;
+ if (idA < idB)
+ return -1;
+ if (idA > idB)
+ return 1;
+ return 0;
+ }
+ }
+ return result;
+}
+
+/*
+ * This comparison function is called from qsort() to sort
+ * unique methods into decreasing order of exclusive elapsed time.
+ */
+int compareUniqueExclusive(const void *a, const void *b) {
+ uint64_t elapsed1, elapsed2;
+ int result;
+
+ const UniqueMethodEntry *uniqueA = *(const UniqueMethodEntry**)a;
+ const UniqueMethodEntry *uniqueB = *(const UniqueMethodEntry**)b;
+ elapsed1 = uniqueA->elapsedExclusive;
+ elapsed2 = uniqueB->elapsedExclusive;
+ if (elapsed1 < elapsed2)
+ return 1;
+ if (elapsed1 > elapsed2)
+ return -1;
+
+ /* If the elapsed times of two methods are equal, then sort them
+ * into alphabetical order.
+ */
+ result = strcmp(uniqueA->methods[0]->className,
+ uniqueB->methods[0]->className);
+ if (result == 0) {
+ unsigned int idA = uniqueA->methods[0]->methodId;
+ unsigned int idB = uniqueB->methods[0]->methodId;
+ if (idA < idB)
+ return -1;
+ if (idA > idB)
+ return 1;
+ return 0;
+ }
+ return result;
+}
+
+/*
+ * Free a DataKeys struct.
+ */
+void freeDataKeys(DataKeys* pKeys)
+{
+ if (pKeys == NULL)
+ return;
+
+ free(pKeys->fileData);
+ free(pKeys->threads);
+ free(pKeys->methods);
+ free(pKeys->methodCache);
+ free(pKeys);
+}
+
+/*
+ * Find the offset to the next occurrence of the specified character.
+ *
+ * "data" should point somewhere within the current line. "len" is the
+ * number of bytes left in the buffer.
+ *
+ * Returns -1 if we hit the end of the buffer.
+ */
+int findNextChar(const char* data, int len, char lookFor)
+{
+ const char* start = data;
+
+ while (len > 0) {
+ if (*data == lookFor)
+ return data - start;
+
+ data++;
+ len--;
+ }
+
+ return -1;
+}
+
+int countLinesToChar(const char* data, int len, const char toFind)
+{
+ int count = 0;
+ int next;
+
+ while (*data != toFind) {
+ next = findNextChar(data, len, '\n');
+ if (next < 0)
+ return count;
+ count++;
+ data += next+1;
+ len -= next+1;
+ }
+
+ return count;
+}
+
+/*
+ * Count the number of lines until the next token.
+ *
+ * Returns 0 if none found before EOF.
+ */
+int countLinesToToken(const char* data, int len)
+{
+ return countLinesToChar(data, len, TOKEN_CHAR);
+}
+
+/*
+ * Make sure we're at the start of the right section.
+ *
+ * Returns the length of the token line, or -1 if something is wrong.
+ */
+int checkToken(const char* data, int len, const char* cmpStr)
+{
+ int cmpLen = strlen(cmpStr);
+ int next;
+
+ if (*data != TOKEN_CHAR) {
+ fprintf(stderr,
+ "ERROR: not at start of %s (found '%.10s')\n", cmpStr, data);
+ return -1;
+ }
+
+ next = findNextChar(data, len, '\n');
+ if (next < cmpLen+1)
+ return -1;
+
+ if (strncmp(data+1, cmpStr, cmpLen) != 0) {
+ fprintf(stderr, "ERROR: '%s' not found (got '%.7s')\n", cmpStr, data+1);
+ return -1;
+ }
+
+ return next+1;
+}
+
+/*
+ * Parse the "*version" section.
+ */
+long parseVersion(DataKeys* pKeys, long offset, int verbose)
+{
+ char* data;
+ char* dataEnd;
+ int i, count, next;
+
+ if (offset < 0)
+ return -1;
+
+ data = pKeys->fileData + offset;
+ dataEnd = pKeys->fileData + pKeys->fileLen;
+ next = checkToken(data, dataEnd - data, "version");
+ if (next <= 0)
+ return -1;
+
+ data += next;
+
+ /*
+ * Count the number of items in the "version" section.
+ */
+ count = countLinesToToken(data, dataEnd - data);
+ if (count <= 0) {
+ fprintf(stderr,
+ "ERROR: failed while reading version (found %d)\n", count);
+ return -1;
+ }
+
+ /* find the end of the line */
+ next = findNextChar(data, dataEnd - data, '\n');
+ if (next < 0)
+ return -1;
+
+ data[next] = '\0';
+ versionNumber = strtoul(data, NULL, 0);
+ if (verbose)
+ printf("VERSION: %d\n", versionNumber);
+
+ data += next+1;
+
+ /* skip over the rest of the stuff, which is "name=value" lines */
+ for (i = 1; i < count; i++) {
+ next = findNextChar(data, dataEnd - data, '\n');
+ if (next < 0)
+ return -1;
+ //data[next] = '\0';
+ //printf("IGNORING: '%s'\n", data);
+ data += next+1;
+ }
+
+ return data - pKeys->fileData;
+}
+
+/*
+ * Parse the "*threads" section.
+ */
+long parseThreads(DataKeys* pKeys, long offset)
+{
+ char* data;
+ char* dataEnd;
+ int i, next, tab, count;
+
+ if (offset < 0)
+ return -1;
+
+ data = pKeys->fileData + offset;
+ dataEnd = pKeys->fileData + pKeys->fileLen;
+ next = checkToken(data, dataEnd - data, "threads");
+
+ data += next;
+
+ /*
+ * Count the number of thread entries (one per line).
+ */
+ count = countLinesToToken(data, dataEnd - data);
+ if (count <= 0) {
+ fprintf(stderr,
+ "ERROR: failed while reading threads (found %d)\n", count);
+ return -1;
+ }
+
+ //printf("+++ found %d threads\n", count);
+ pKeys->threads = (ThreadEntry*) malloc(sizeof(ThreadEntry) * count);
+ if (pKeys->threads == NULL)
+ return -1;
+
+ /*
+ * Extract all entries.
+ */
+ for (i = 0; i < count; i++) {
+ next = findNextChar(data, dataEnd - data, '\n');
+ assert(next > 0);
+ data[next] = '\0';
+
+ tab = findNextChar(data, next, '\t');
+ data[tab] = '\0';
+
+ pKeys->threads[i].threadId = atoi(data);
+ pKeys->threads[i].threadName = data + tab +1;
+
+ data += next+1;
+ }
+
+ pKeys->numThreads = count;
+ return data - pKeys->fileData;
+}
+
+/*
+ * Parse the "*methods" section.
+ */
+long parseMethods(DataKeys* pKeys, long offset)
+{
+ char* data;
+ char* dataEnd;
+ int i, next, count;
+
+ if (offset < 0)
+ return -1;
+
+ data = pKeys->fileData + offset;
+ dataEnd = pKeys->fileData + pKeys->fileLen;
+ next = checkToken(data, dataEnd - data, "methods");
+ if (next < 0)
+ return -1;
+
+ data += next;
+
+ /*
+ * Count the number of method entries (one per line).
+ */
+ count = countLinesToToken(data, dataEnd - data);
+ if (count <= 0) {
+ fprintf(stderr,
+ "ERROR: failed while reading methods (found %d)\n", count);
+ return -1;
+ }
+
+ /* Reserve an extra method at location 0 for the "toplevel" method,
+ * and another extra method for all other "unknown" methods.
+ */
+ count += 2;
+ pKeys->methods = (MethodEntry*) malloc(sizeof(MethodEntry) * count);
+ if (pKeys->methods == NULL)
+ return -1;
+ initMethodEntry(&pKeys->methods[TOPLEVEL_INDEX], 0, "(toplevel)",
+ NULL, NULL, NULL, NULL);
+ initMethodEntry(&pKeys->methods[UNKNOWN_INDEX], 0, "(unknown)",
+ NULL, NULL, NULL, NULL);
+
+ /*
+ * Extract all entries, starting with index 2.
+ */
+ for (i = UNKNOWN_INDEX + 1; i < count; i++) {
+ int tab1, tab2, tab3, tab4, tab5;
+ unsigned int id;
+ char* endptr;
+
+ next = findNextChar(data, dataEnd - data, '\n');
+ assert(next > 0);
+ data[next] = '\0';
+
+ tab1 = findNextChar(data, next, '\t');
+ tab2 = findNextChar(data+(tab1+1), next-(tab1+1), '\t');
+ tab3 = findNextChar(data+(tab1+tab2+2), next-(tab1+tab2+2), '\t');
+ tab4 = findNextChar(data+(tab1+tab2+tab3+3),
+ next-(tab1+tab2+tab3+3), '\t');
+ tab5 = findNextChar(data+(tab1+tab2+tab3+tab4+4),
+ next-(tab1+tab2+tab3+tab4+4), '\t');
+ if (tab1 < 0) {
+ fprintf(stderr, "ERROR: missing field on method line: '%s'\n",
+ data);
+ return -1;
+ }
+ assert(data[tab1] == '\t');
+ data[tab1] = '\0';
+
+ id = strtoul(data, &endptr, 0);
+ if (*endptr != '\0') {
+ fprintf(stderr, "ERROR: bad method ID '%s'\n", data);
+ return -1;
+ }
+
+ // Allow files that specify just a function name, instead of requiring
+ // "class \t method \t signature"
+ if (tab2 > 0 && tab3 > 0) {
+ tab2 += tab1+1;
+ tab3 += tab2+1;
+ assert(data[tab2] == '\t');
+ assert(data[tab3] == '\t');
+ data[tab2] = data[tab3] = '\0';
+
+ // This is starting to get awkward. Allow filename and line #.
+ if (tab4 > 0 && tab5 > 0) {
+ tab4 += tab3+1;
+ tab5 += tab4+1;
+
+ assert(data[tab4] == '\t');
+ assert(data[tab5] == '\t');
+ data[tab4] = data[tab5] = '\0';
+
+ initMethodEntry(&pKeys->methods[i], id, data + tab1 +1,
+ data + tab2 +1, data + tab3 +1, data + tab4 +1,
+ data + tab5 +1);
+ } else {
+ initMethodEntry(&pKeys->methods[i], id, data + tab1 +1,
+ data + tab2 +1, data + tab3 +1, NULL, NULL);
+ }
+ } else {
+ initMethodEntry(&pKeys->methods[i], id, data + tab1 +1,
+ NULL, NULL, NULL, NULL);
+ }
+
+ data += next+1;
+ }
+
+ pKeys->numMethods = count;
+ return data - pKeys->fileData;
+}
+
+/*
+ * Parse the "*end" section.
+ */
+long parseEnd(DataKeys* pKeys, long offset)
+{
+ char* data;
+ char* dataEnd;
+ int next;
+
+ if (offset < 0)
+ return -1;
+
+ data = pKeys->fileData + offset;
+ dataEnd = pKeys->fileData + pKeys->fileLen;
+ next = checkToken(data, dataEnd - data, "end");
+ if (next < 0)
+ return -1;
+
+ data += next;
+
+ return data - pKeys->fileData;
+}
+
+/*
+ * Sort the thread list entries.
+ */
+static int compareThreads(const void* thread1, const void* thread2)
+{
+ return ((const ThreadEntry*) thread1)->threadId -
+ ((const ThreadEntry*) thread2)->threadId;
+}
+
+void sortThreadList(DataKeys* pKeys)
+{
+ qsort(pKeys->threads, pKeys->numThreads, sizeof(pKeys->threads[0]),
+ compareThreads);
+}
+
+/*
+ * Sort the method list entries.
+ */
+static int compareMethods(const void* meth1, const void* meth2)
+{
+ unsigned int id1, id2;
+
+ id1 = ((const MethodEntry*) meth1)->methodId;
+ id2 = ((const MethodEntry*) meth2)->methodId;
+ if (id1 < id2)
+ return -1;
+ if (id1 > id2)
+ return 1;
+ return 0;
+}
+
+void sortMethodList(DataKeys* pKeys)
+{
+ qsort(pKeys->methods, pKeys->numMethods, sizeof(MethodEntry),
+ compareMethods);
+}
+
+/*
+ * Parse the key section, and return a copy of the parsed contents.
+ */
+DataKeys* parseKeys(FILE *fp, int verbose)
+{
+ DataKeys* pKeys = NULL;
+ long offset;
+ int i;
+
+ pKeys = (DataKeys*) calloc(1, sizeof(DataKeys));
+ if (pKeys == NULL)
+ goto fail;
+
+ /*
+ * We load the entire file into memory. We do this, rather than memory-
+ * mapping it, because we want to change some whitespace to NULs.
+ */
+ if (fseek(fp, 0L, SEEK_END) != 0) {
+ perror("fseek");
+ goto fail;
+ }
+ pKeys->fileLen = ftell(fp);
+ if (pKeys->fileLen == 0) {
+ fprintf(stderr, "Key file is empty.\n");
+ goto fail;
+ }
+ rewind(fp);
+
+ pKeys->fileData = (char*) malloc(pKeys->fileLen);
+ if (pKeys->fileData == NULL) {
+ fprintf(stderr, "ERROR: unable to alloc %ld bytes\n", pKeys->fileLen);
+ goto fail;
+ }
+
+ if (fread(pKeys->fileData, 1, pKeys->fileLen, fp) != (size_t) pKeys->fileLen)
+ {
+ fprintf(stderr, "ERROR: unable to read %ld bytes from trace file\n",
+ pKeys->fileLen);
+ goto fail;
+ }
+
+ offset = 0;
+
+ offset = parseVersion(pKeys, offset, verbose);
+ offset = parseThreads(pKeys, offset);
+ offset = parseMethods(pKeys, offset);
+ offset = parseEnd(pKeys, offset);
+ if (offset < 0)
+ goto fail;
+
+ /* Leave fp pointing to the beginning of the data section. */
+ fseek(fp, offset, SEEK_SET);
+
+ sortThreadList(pKeys);
+ sortMethodList(pKeys);
+
+ /*
+ * Dump list of threads.
+ */
+ if (verbose) {
+ printf("Threads (%d):\n", pKeys->numThreads);
+ for (i = 0; i < pKeys->numThreads; i++) {
+ printf("%2d %s\n",
+ pKeys->threads[i].threadId, pKeys->threads[i].threadName);
+ }
+ }
+
+#if 0
+ /*
+ * Dump list of methods.
+ */
+ if (verbose) {
+ printf("Methods (%d):\n", pKeys->numMethods);
+ for (i = 0; i < pKeys->numMethods; i++) {
+ printf("0x%08x %s : %s : %s\n",
+ pKeys->methods[i].methodId >> 2, pKeys->methods[i].className,
+ pKeys->methods[i].methodName, pKeys->methods[i].signature);
+ }
+ }
+#endif
+
+ return pKeys;
+
+fail:
+ freeDataKeys(pKeys);
+ return NULL;
+}
+
+
+/*
+ * Read values from the binary data file.
+ */
+
+/* Make the return value "unsigned int" instead of "unsigned short" so that
+ * we can detect EOF.
+ */
+unsigned int read2LE(FILE* fp)
+{
+ unsigned int val;
+
+ val = getc(fp);
+ val |= getc(fp) << 8;
+ return val;
+}
+unsigned int read4LE(FILE* fp)
+{
+ unsigned int val;
+
+ val = getc(fp);
+ val |= getc(fp) << 8;
+ val |= getc(fp) << 16;
+ val |= getc(fp) << 24;
+ return val;
+}
+unsigned long long read8LE(FILE* fp)
+{
+ unsigned long long val;
+
+ val = getc(fp);
+ val |= (unsigned long long) getc(fp) << 8;
+ val |= (unsigned long long) getc(fp) << 16;
+ val |= (unsigned long long) getc(fp) << 24;
+ val |= (unsigned long long) getc(fp) << 32;
+ val |= (unsigned long long) getc(fp) << 40;
+ val |= (unsigned long long) getc(fp) << 48;
+ val |= (unsigned long long) getc(fp) << 56;
+ return val;
+}
+
+/*
+ * Parse the header of the data section.
+ *
+ * Returns with the file positioned at the start of the record data.
+ */
+int parseDataHeader(FILE *fp, DataHeader* pHeader)
+{
+ pHeader->magic = read4LE(fp);
+ pHeader->version = read2LE(fp);
+ pHeader->offsetToData = read2LE(fp);
+ pHeader->startWhen = read8LE(fp);
+
+ if (fseek(fp, pHeader->offsetToData - 16, SEEK_CUR) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Look up a method by its method ID (using binary search).
+ *
+ * Returns NULL if no matching method was found.
+ */
+MethodEntry* lookupMethod(DataKeys* pKeys, unsigned int methodId)
+{
+ int hi, lo, mid;
+ unsigned int id;
+ int hashedId;
+
+ /* Create cache if it doesn't already exist */
+ if (pKeys->methodCache == NULL) {
+ pKeys->methodCache = (int*) calloc(METHOD_CACHE_SIZE, sizeof(int));
+ }
+
+ // ids are multiples of 4, so shift
+ hashedId = (methodId >> 2) & METHOD_CACHE_SIZE_MASK;
+ if (pKeys->methodCache[hashedId]) /* cache hit */
+ if (pKeys->methods[pKeys->methodCache[hashedId]].methodId == methodId)
+ return &pKeys->methods[pKeys->methodCache[hashedId]];
+
+ lo = 0;
+ hi = pKeys->numMethods - 1;
+
+ while (hi >= lo) {
+ mid = (hi + lo) / 2;
+
+ id = pKeys->methods[mid].methodId;
+ if (id == methodId) { /* match, put in cache */
+ hashedId = (methodId >> 2) & METHOD_CACHE_SIZE_MASK;
+ pKeys->methodCache[hashedId] = mid;
+ return &pKeys->methods[mid];
+ } else if (id < methodId) /* too low */
+ lo = mid + 1;
+ else /* too high */
+ hi = mid - 1;
+ }
+
+ return NULL;
+}
+
+/*
+ * Reads the next data record, and assigns the data values to threadId,
+ * methodVal and elapsedTime. On end-of-file, the threadId, methodVal,
+ * and elapsedTime are unchanged. Returns 1 on end-of-file, otherwise
+ * returns 0.
+ */
+int readDataRecord(FILE *dataFp, int *threadId, unsigned int *methodVal,
+ uint64_t *elapsedTime)
+{
+ int id;
+
+ /*
+ * TODO:
+ * This SHOULD NOT be keyed off of the global version number! Use
+ * a name=value setting in the version area instead!
+ */
+ if (versionNumber == 1) {
+ id = getc(dataFp);
+ } else {
+ id = read2LE(dataFp);
+ }
+ if (id == EOF)
+ return 1;
+ *threadId = id;
+
+ *methodVal = read4LE(dataFp);
+ *elapsedTime = read4LE(dataFp);
+ if (feof(dataFp)) {
+ fprintf(stderr, "WARNING: hit EOF mid-record\n");
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Read the key file and use it to produce formatted output from the
+ * data file.
+ */
+void dumpTrace()
+{
+ static const char* actionStr[] = { "ent", "xit", "unr", "???" };
+ MethodEntry bogusMethod = { 0, "???", "???", "???", "???", -1, 0, 0, 0, 0,
+ {NULL, NULL}, {NULL, NULL}, {0, 0}, 0, 0, -1 };
+ char bogusBuf[80];
+ char spaces[MAX_STACK_DEPTH+1];
+ FILE* dataFp = NULL;
+ DataHeader dataHeader;
+ DataKeys* pKeys = NULL;
+ int i;
+ TraceData traceData;
+
+ //printf("Dumping '%s' '%s'\n", dataFileName, keyFileName);
+
+ memset(spaces, '.', MAX_STACK_DEPTH);
+ spaces[MAX_STACK_DEPTH] = '\0';
+
+ for (i = 0; i < MAX_THREADS; i++)
+ traceData.depth[i] = 2; // adjust for return from start function
+
+ dataFp = fopen(gOptions.traceFileName, "r");
+ if (dataFp == NULL)
+ goto bail;
+
+ if ((pKeys = parseKeys(dataFp, 1)) == NULL)
+ goto bail;
+
+ if (parseDataHeader(dataFp, &dataHeader) < 0)
+ goto bail;
+
+ printf("Trace (threadID action usecs class.method signature):\n");
+
+ while (1) {
+ MethodEntry* method;
+ int threadId;
+ unsigned int methodVal;
+ uint64_t elapsedTime;
+ int action, printDepth;
+ unsigned int methodId, lastEnter = 0;
+ int mismatch = 0;
+ char depthNote;
+
+ /*
+ * Extract values from file.
+ */
+ if (readDataRecord(dataFp, &threadId, &methodVal, &elapsedTime))
+ break;
+
+ action = METHOD_ACTION(methodVal);
+ methodId = METHOD_ID(methodVal);
+
+ /*
+ * Generate a line of output.
+ */
+ if (action == METHOD_TRACE_ENTER) {
+ traceData.depth[threadId]++;
+ lastEnter = methodId;
+ } else {
+ /* quick test for mismatched adjacent enter/exit */
+ if (lastEnter != 0 && lastEnter != methodId)
+ mismatch = 1;
+ }
+
+ printDepth = traceData.depth[threadId];
+ depthNote = ' ';
+ if (printDepth < 0) {
+ printDepth = 0;
+ depthNote = '-';
+ } else if (printDepth > MAX_STACK_DEPTH) {
+ printDepth = MAX_STACK_DEPTH;
+ depthNote = '+';
+ }
+
+ method = lookupMethod(pKeys, methodId);
+ if (method == NULL) {
+ method = &bogusMethod;
+ sprintf(bogusBuf, "methodId: 0x%x", methodId);
+ method->signature = bogusBuf;
+ }
+
+ if (method->methodName) {
+ printf("%2d %s%c %8lld%c%s%s.%s %s\n", threadId,
+ actionStr[action], mismatch ? '!' : ' ',
+ elapsedTime, depthNote,
+ spaces + (MAX_STACK_DEPTH - printDepth),
+ method->className, method->methodName, method->signature);
+ } else {
+ printf("%2d %s%c %8lld%c%s%s\n", threadId,
+ actionStr[action], mismatch ? '!' : ' ',
+ elapsedTime, depthNote,
+ spaces + (MAX_STACK_DEPTH - printDepth),
+ method->className);
+ }
+
+ if (action != METHOD_TRACE_ENTER) {
+ traceData.depth[threadId]--; /* METHOD_TRACE_EXIT or METHOD_TRACE_UNROLL */
+ lastEnter = 0;
+ }
+
+ mismatch = 0;
+ }
+
+bail:
+ if (dataFp != NULL)
+ fclose(dataFp);
+ if (pKeys != NULL)
+ freeDataKeys(pKeys);
+}
+
+/* This routine adds the given time to the parent and child methods.
+ * This is called when the child routine exits, after the child has
+ * been popped from the stack. The elapsedTime parameter is the
+ * duration of the child routine, including time spent in called routines.
+ */
+void addInclusiveTime(MethodEntry *parent, MethodEntry *child,
+ uint64_t elapsedTime)
+{
+ TimedMethod *pTimed;
+
+#if 0
+ bool verbose = false;
+ if (strcmp(child->className, debugClassName) == 0)
+ verbose = true;
+#endif
+
+ int childIsRecursive = (child->recursiveEntries > 0);
+ int parentIsRecursive = (parent->recursiveEntries > 1);
+
+ if (child->recursiveEntries == 0) {
+ child->elapsedInclusive += elapsedTime;
+ } else if (child->recursiveEntries == 1) {
+ child->recursiveInclusive += elapsedTime;
+ }
+ child->numCalls[childIsRecursive] += 1;
+
+#if 0
+ if (verbose) {
+ fprintf(stderr,
+ "%s %d elapsedTime: %lld eI: %lld, rI: %lld\n",
+ child->className, child->recursiveEntries,
+ elapsedTime, child->elapsedInclusive,
+ child->recursiveInclusive);
+ }
+#endif
+
+ /* Find the child method in the parent */
+ TimedMethod *children = parent->children[parentIsRecursive];
+ for (pTimed = children; pTimed; pTimed = pTimed->next) {
+ if (pTimed->method == child) {
+ pTimed->elapsedInclusive += elapsedTime;
+ pTimed->numCalls += 1;
+ break;
+ }
+ }
+ if (pTimed == NULL) {
+ /* Allocate a new TimedMethod */
+ pTimed = (TimedMethod *) malloc(sizeof(TimedMethod));
+ pTimed->elapsedInclusive = elapsedTime;
+ pTimed->numCalls = 1;
+ pTimed->method = child;
+
+ /* Add it to the front of the list */
+ pTimed->next = children;
+ parent->children[parentIsRecursive] = pTimed;
+ }
+
+ /* Find the parent method in the child */
+ TimedMethod *parents = child->parents[childIsRecursive];
+ for (pTimed = parents; pTimed; pTimed = pTimed->next) {
+ if (pTimed->method == parent) {
+ pTimed->elapsedInclusive += elapsedTime;
+ pTimed->numCalls += 1;
+ break;
+ }
+ }
+ if (pTimed == NULL) {
+ /* Allocate a new TimedMethod */
+ pTimed = (TimedMethod *) malloc(sizeof(TimedMethod));
+ pTimed->elapsedInclusive = elapsedTime;
+ pTimed->numCalls = 1;
+ pTimed->method = parent;
+
+ /* Add it to the front of the list */
+ pTimed->next = parents;
+ child->parents[childIsRecursive] = pTimed;
+ }
+
+#if 0
+ if (verbose) {
+ fprintf(stderr,
+ " %s %d eI: %lld\n",
+ parent->className, parent->recursiveEntries,
+ pTimed->elapsedInclusive);
+ }
+#endif
+}
+
+/* Sorts a linked list and returns a newly allocated array containing
+ * the sorted entries.
+ */
+TimedMethod *sortTimedMethodList(TimedMethod *list, int *num)
+{
+ int ii;
+ TimedMethod *pTimed, *sorted;
+
+ /* Count the elements */
+ int num_entries = 0;
+ for (pTimed = list; pTimed; pTimed = pTimed->next)
+ num_entries += 1;
+ *num = num_entries;
+ if (num_entries == 0)
+ return NULL;
+
+ /* Copy all the list elements to a new array and sort them */
+ sorted = (TimedMethod *) malloc(sizeof(TimedMethod) * num_entries);
+ for (ii = 0, pTimed = list; pTimed; pTimed = pTimed->next, ++ii)
+ memcpy(&sorted[ii], pTimed, sizeof(TimedMethod));
+ qsort(sorted, num_entries, sizeof(TimedMethod), compareTimedMethod);
+
+ /* Fix up the "next" pointers so that they work. */
+ for (ii = 0; ii < num_entries - 1; ++ii)
+ sorted[ii].next = &sorted[ii + 1];
+ sorted[num_entries - 1].next = NULL;
+
+ return sorted;
+}
+
+/* Define flag values for printInclusiveMethod() */
+static const int kIsRecursive = 1;
+
+/* This prints the inclusive stats for all the parents or children of a
+ * method, depending on the list that is passed in.
+ */
+void printInclusiveMethod(MethodEntry *method, TimedMethod *list, int numCalls,
+ int flags)
+{
+ int num;
+ TimedMethod *pTimed;
+ char buf[80];
+ char *anchor_close;
+ char *spaces = " "; /* 6 spaces */
+ int num_spaces = strlen(spaces);
+ char *space_ptr = &spaces[num_spaces];
+ char *className, *methodName, *signature;
+ char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE];
+ char signatureBuf[HTML_BUFSIZE];
+
+ anchor_close = "";
+ if (gOptions.outputHtml)
+ anchor_close = "</a>";
+
+ TimedMethod *sorted = sortTimedMethodList(list, &num);
+ double methodTotal = method->elapsedInclusive;
+ for (pTimed = sorted; pTimed; pTimed = pTimed->next) {
+ MethodEntry *relative = pTimed->method;
+ className = (char*)(relative->className);
+ methodName = (char*)(relative->methodName);
+ signature = (char*)(relative->signature);
+ double per = 100.0 * pTimed->elapsedInclusive / methodTotal;
+ sprintf(buf, "[%d]", relative->index);
+ if (gOptions.outputHtml) {
+ int len = strlen(buf);
+ if (len > num_spaces)
+ len = num_spaces;
+ sprintf(buf, "<a href=\"#m%d\">[%d]",
+ relative->index, relative->index);
+ space_ptr = &spaces[len];
+ className = htmlEscape(className, classBuf, HTML_BUFSIZE);
+ methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE);
+ signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE);
+ }
+ int nCalls = numCalls;
+ if (nCalls == 0)
+ nCalls = relative->numCalls[0] + relative->numCalls[1];
+ if (relative->methodName) {
+ if (flags & kIsRecursive) {
+ // Don't display percentages for recursive functions
+ printf("%6s %5s %6s %s%6s%s %6d/%-6d %9llu %s.%s %s\n",
+ "", "", "",
+ space_ptr, buf, anchor_close,
+ pTimed->numCalls, nCalls,
+ pTimed->elapsedInclusive,
+ className, methodName, signature);
+ } else {
+ printf("%6s %5s %5.1f%% %s%6s%s %6d/%-6d %9llu %s.%s %s\n",
+ "", "", per,
+ space_ptr, buf, anchor_close,
+ pTimed->numCalls, nCalls,
+ pTimed->elapsedInclusive,
+ className, methodName, signature);
+ }
+ } else {
+ if (flags & kIsRecursive) {
+ // Don't display percentages for recursive functions
+ printf("%6s %5s %6s %s%6s%s %6d/%-6d %9llu %s\n",
+ "", "", "",
+ space_ptr, buf, anchor_close,
+ pTimed->numCalls, nCalls,
+ pTimed->elapsedInclusive,
+ className);
+ } else {
+ printf("%6s %5s %5.1f%% %s%6s%s %6d/%-6d %9llu %s\n",
+ "", "", per,
+ space_ptr, buf, anchor_close,
+ pTimed->numCalls, nCalls,
+ pTimed->elapsedInclusive,
+ className);
+ }
+ }
+ }
+}
+
+void countRecursiveEntries(CallStack *pStack, int top, MethodEntry *method)
+{
+ int ii;
+
+ method->recursiveEntries = 0;
+ for (ii = 0; ii < top; ++ii) {
+ if (pStack->calls[ii].method == method)
+ method->recursiveEntries += 1;
+ }
+}
+
+void stackDump(CallStack *pStack, int top)
+{
+ int ii;
+
+ for (ii = 0; ii < top; ++ii) {
+ MethodEntry *method = pStack->calls[ii].method;
+ uint64_t entryTime = pStack->calls[ii].entryTime;
+ if (method->methodName) {
+ fprintf(stderr, " %2d: %8llu %s.%s %s\n", ii, entryTime,
+ method->className, method->methodName, method->signature);
+ } else {
+ fprintf(stderr, " %2d: %8llu %s\n", ii, entryTime, method->className);
+ }
+ }
+}
+
+void outputTableOfContents()
+{
+ printf("<a name=\"contents\"></a>\n");
+ printf("<h2>Table of Contents</h2>\n");
+ printf("<ul>\n");
+ printf(" <li><a href=\"#exclusive\">Exclusive profile</a></li>\n");
+ printf(" <li><a href=\"#inclusive\">Inclusive profile</a></li>\n");
+ printf(" <li><a href=\"#thread\">Thread profile</a></li>\n");
+ printf(" <li><a href=\"#class\">Class/method profile</a></li>\n");
+ printf(" <li><a href=\"#method\">Method/class profile</a></li>\n");
+ printf("</ul>\n\n");
+}
+
+void outputNavigationBar()
+{
+ printf("<a href=\"#contents\">[Top]</a>\n");
+ printf("<a href=\"#exclusive\">[Exclusive]</a>\n");
+ printf("<a href=\"#inclusive\">[Inclusive]</a>\n");
+ printf("<a href=\"#thread\">[Thread]</a>\n");
+ printf("<a href=\"#class\">[Class]</a>\n");
+ printf("<a href=\"#method\">[Method]</a>\n");
+ printf("<br><br>\n");
+}
+
+void printExclusiveProfile(MethodEntry **pMethods, int numMethods,
+ uint64_t sumThreadTime)
+{
+ int ii;
+ MethodEntry* method;
+ double total, sum, per, sum_per;
+ char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE];
+ char signatureBuf[HTML_BUFSIZE];
+ char anchor_buf[80];
+ char *anchor_close = "";
+
+ total = sumThreadTime;
+ anchor_buf[0] = 0;
+ if (gOptions.outputHtml) {
+ anchor_close = "</a>";
+ printf("<a name=\"exclusive\"></a>\n");
+ printf("<hr>\n");
+ outputNavigationBar();
+ } else {
+ printf("\n%s\n", profileSeparator);
+ }
+
+ /* First, sort the methods into decreasing order of inclusive
+ * elapsed time so that we can assign the method indices.
+ */
+ qsort(pMethods, numMethods, sizeof(MethodEntry*), compareElapsedInclusive);
+
+ for (ii = 0; ii < numMethods; ++ii)
+ pMethods[ii]->index = ii;
+
+ /* Sort the methods into decreasing order of exclusive elapsed time.
+ */
+ qsort(pMethods, numMethods, sizeof(MethodEntry*),
+ compareElapsedExclusive);
+
+ printf("Total cycles: %llu\n\n", sumThreadTime);
+ if (gOptions.outputHtml) {
+ printf("<br><br>\n");
+ }
+ printf("Exclusive elapsed times for each method, not including time spent in\n");
+ printf("children, sorted by exclusive time.\n\n");
+ if (gOptions.outputHtml) {
+ printf("<br><br>\n<pre>\n");
+ }
+
+ printf(" Usecs self %% sum %% Method\n");
+ sum = 0;
+
+ for (ii = 0; ii < numMethods; ++ii) {
+ char *className, *methodName, *signature;
+
+ method = pMethods[ii];
+ /* Don't show methods with zero cycles */
+ if (method->elapsedExclusive == 0)
+ break;
+ className = (char*)(method->className);
+ methodName = (char*)(method->methodName);
+ signature = (char*)(method->signature);
+ sum += method->elapsedExclusive;
+ per = 100.0 * method->elapsedExclusive / total;
+ sum_per = 100.0 * sum / total;
+ if (gOptions.outputHtml) {
+ sprintf(anchor_buf, "<a href=\"#m%d\">", method->index);
+ className = htmlEscape(className, classBuf, HTML_BUFSIZE);
+ methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE);
+ signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE);
+ }
+ if (method->methodName) {
+ printf("%9llu %6.2f %6.2f %s[%d]%s %s.%s %s\n",
+ method->elapsedExclusive, per, sum_per,
+ anchor_buf, method->index, anchor_close,
+ className, methodName, signature);
+ } else {
+ printf("%9llu %6.2f %6.2f %s[%d]%s %s\n",
+ method->elapsedExclusive, per, sum_per,
+ anchor_buf, method->index, anchor_close,
+ className);
+ }
+ }
+ if (gOptions.outputHtml) {
+ printf("</pre>\n");
+ }
+}
+
+/* check to make sure that the child method meets the threshold of the parent */
+int checkThreshold(MethodEntry* parent, MethodEntry* child)
+{
+ double parentTime = parent->elapsedInclusive;
+ double childTime = child->elapsedInclusive;
+ int64_t percentage = (childTime / parentTime) * 100.0;
+ return (percentage < gOptions.threshold) ? 0 : 1;
+}
+
+void createLabels(FILE* file, MethodEntry* method)
+{
+ fprintf(file, "node%d[label = \"[%d] %s.%s (%llu, %llu, %d)\"]\n",
+ method->index, method->index, method->className, method->methodName,
+ method->elapsedInclusive / 1000,
+ method->elapsedExclusive / 1000,
+ method->numCalls[0]);
+
+ method->graphState = GRAPH_LABEL_VISITED;
+
+ TimedMethod* child;
+ for (child = method->children[0] ; child ; child = child->next) {
+ MethodEntry* childMethod = child->method;
+
+ if ((childMethod->graphState & GRAPH_LABEL_VISITED) == 0 && checkThreshold(method, childMethod)) {
+ createLabels(file, child->method);
+ }
+ }
+}
+
+void createLinks(FILE* file, MethodEntry* method)
+{
+ method->graphState |= GRAPH_NODE_VISITED;
+
+ TimedMethod* child;
+ for (child = method->children[0] ; child ; child = child->next) {
+ MethodEntry* childMethod = child->method;
+ if (checkThreshold(method, child->method)) {
+ fprintf(file, "node%d -> node%d\n", method->index, child->method->index);
+ // only visit children that haven't been visited before
+ if ((childMethod->graphState & GRAPH_NODE_VISITED) == 0) {
+ createLinks(file, child->method);
+ }
+ }
+ }
+}
+
+void createInclusiveProfileGraphNew(DataKeys* dataKeys)
+{
+ // create a temporary file in /tmp
+ char path[FILENAME_MAX];
+ if (gOptions.keepDotFile) {
+ snprintf(path, FILENAME_MAX, "%s.dot", gOptions.graphFileName);
+ } else {
+ snprintf(path, FILENAME_MAX, "/tmp/dot-%d-%d.dot", (int)time(NULL), rand());
+ }
+
+ FILE* file = fopen(path, "w+");
+
+ fprintf(file, "digraph g {\nnode [shape = record,height=.1];\n");
+
+ createLabels(file, dataKeys->methods);
+ createLinks(file, dataKeys->methods);
+
+ fprintf(file, "}");
+ fclose(file);
+
+ // now that we have the dot file generate the image
+ char command[1024];
+ snprintf(command, 1024, "dot -Tpng -o '%s' '%s'", gOptions.graphFileName, path);
+
+ system(command);
+
+ if (! gOptions.keepDotFile) {
+ remove(path);
+ }
+}
+
+void printInclusiveProfile(MethodEntry **pMethods, int numMethods,
+ uint64_t sumThreadTime)
+{
+ int ii;
+ MethodEntry* method;
+ double total, sum, per, sum_per;
+ char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE];
+ char signatureBuf[HTML_BUFSIZE];
+ char anchor_buf[80];
+
+ total = sumThreadTime;
+ anchor_buf[0] = 0;
+ if (gOptions.outputHtml) {
+ printf("<a name=\"inclusive\"></a>\n");
+ printf("<hr>\n");
+ outputNavigationBar();
+ } else {
+ printf("\n%s\n", profileSeparator);
+ }
+
+ /* Sort the methods into decreasing order of inclusive elapsed time. */
+ qsort(pMethods, numMethods, sizeof(MethodEntry*),
+ compareElapsedInclusive);
+
+ printf("\nInclusive elapsed times for each method and its parents and children,\n");
+ printf("sorted by inclusive time.\n\n");
+
+ if (gOptions.outputHtml) {
+ printf("<br><br>\n<pre>\n");
+ }
+
+ printf("index %%/total %%/self index calls usecs name\n");
+ for (ii = 0; ii < numMethods; ++ii) {
+ int num;
+ TimedMethod *pTimed;
+ double excl_per;
+ char buf[40];
+ char *className, *methodName, *signature;
+
+ method = pMethods[ii];
+ /* Don't show methods with zero cycles */
+ if (method->elapsedInclusive == 0)
+ break;
+
+ className = (char*)(method->className);
+ methodName = (char*)(method->methodName);
+ signature = (char*)(method->signature);
+
+ if (gOptions.outputHtml) {
+ printf("<a name=\"m%d\"></a>", method->index);
+ className = htmlEscape(className, classBuf, HTML_BUFSIZE);
+ methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE);
+ signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE);
+ }
+ printf("----------------------------------------------------\n");
+
+ /* Sort and print the parents */
+ int numCalls = method->numCalls[0] + method->numCalls[1];
+ printInclusiveMethod(method, method->parents[0], numCalls, 0);
+ if (method->parents[1]) {
+ printf(" +++++++++++++++++++++++++\n");
+ printInclusiveMethod(method, method->parents[1], numCalls,
+ kIsRecursive);
+ }
+
+ per = 100.0 * method->elapsedInclusive / total;
+ sprintf(buf, "[%d]", ii);
+ if (method->methodName) {
+ printf("%-6s %5.1f%% %5s %6s %6d+%-6d %9llu %s.%s %s\n",
+ buf,
+ per, "", "", method->numCalls[0], method->numCalls[1],
+ method->elapsedInclusive,
+ className, methodName, signature);
+ } else {
+ printf("%-6s %5.1f%% %5s %6s %6d+%-6d %9llu %s\n",
+ buf,
+ per, "", "", method->numCalls[0], method->numCalls[1],
+ method->elapsedInclusive,
+ className);
+ }
+ excl_per = 100.0 * method->topExclusive / method->elapsedInclusive;
+ printf("%6s %5s %5.1f%% %6s %6s %6s %9llu\n",
+ "", "", excl_per, "excl", "", "", method->topExclusive);
+
+ /* Sort and print the children */
+ printInclusiveMethod(method, method->children[0], 0, 0);
+ if (method->children[1]) {
+ printf(" +++++++++++++++++++++++++\n");
+ printInclusiveMethod(method, method->children[1], 0,
+ kIsRecursive);
+ }
+ }
+ if (gOptions.outputHtml) {
+ printf("</pre>\n");
+ }
+}
+
+void printThreadProfile(ThreadEntry *pThreads, int numThreads, uint64_t sumThreadTime, Filter** filters)
+{
+ int ii, jj;
+ ThreadEntry thread;
+ double total, per, sum_per;
+ uint64_t sum;
+ char threadBuf[HTML_BUFSIZE];
+ char anchor_buf[80];
+ int drawTable;
+
+ total = sumThreadTime;
+ anchor_buf[0] = 0;
+ if (gOptions.outputHtml) {
+ printf("<a name=\"thread\"></a>\n");
+ printf("<hr>\n");
+ outputNavigationBar();
+ } else {
+ printf("\n%s\n", profileSeparator);
+ }
+
+ /* Sort the threads into decreasing order of elapsed time. */
+ qsort(pThreads, numThreads, sizeof(ThreadEntry), compareElapsed);
+
+ printf("\nElapsed times for each thread, sorted by elapsed time.\n");
+ printf("Also includes percentage of time spent during the <i>execution</i> of any filters.\n\n");
+
+ if (gOptions.outputHtml) {
+ printf("<br><br>\n<pre>\n");
+ }
+
+ printf(" Usecs self %% sum %%");
+ for (ii = 0; ii < numFilters; ++ii) {
+ printf(" %s %%", filters[ii]->filterName);
+ }
+ printf(" tid ThreadName\n");
+ sum = 0;
+
+ for (ii = 0; ii < numThreads; ++ii) {
+ int threadId;
+ char *threadName;
+ uint64_t time;
+
+ thread = pThreads[ii];
+
+ threadId = thread.threadId;
+ threadName = (char*)(thread.threadName);
+ time = thread.elapsedTime;
+
+ sum += time;
+ per = 100.0 * time / total;
+ sum_per = 100.0 * sum / total;
+
+ if (gOptions.outputHtml) {
+ threadName = htmlEscape(threadName, threadBuf, HTML_BUFSIZE);
+ }
+
+ printf("%9llu %6.2f %6.2f", time, per, sum_per);
+ for (jj = 0; jj < numFilters; jj++) {
+ printf(" %6.2f", 100.0 * filters[jj]->times.threadExecutionTimes[threadId] / time);
+ }
+ printf(" %3d %s\n", threadId, threadName);
+ }
+
+ if (gOptions.outputHtml)
+ printf("</pre><br />");
+
+ printf("\n\nBreak-down of portion of time spent by each thread while waiting on a filter method.\n");
+
+ for (ii = 0; ii < numFilters; ++ii) {
+ // Draw a table for each filter that measures wait time
+ drawTable = 0;
+ for (jj = 0; jj < filters[ii]->numKeys; jj++)
+ if (filters[ii]->filterKeys[jj].flags == 1)
+ drawTable = 1;
+
+ if (drawTable) {
+
+ if (gOptions.outputHtml)
+ printf("<br/><br/>\n<pre>\n");
+ printf("Filter: %s\n", filters[ii]->filterName);
+ printf("Total waiting cycles: %llu (%6.2f%% of total)\n",
+ filters[ii]->times.totalWaitTime,
+ 100.0 * filters[ii]->times.totalWaitTime / sum);
+
+ if (filters[ii]->times.totalWaitTime > 0) {
+
+ printf("Details: \n\n");
+
+ printf(" Waiting cycles %% of total waiting time execution time while waiting thread name\n");
+
+ for (jj = 0; jj < numThreads; jj++) {
+
+ thread = pThreads[jj];
+
+ char *threadName;
+ threadName = (char*) thread.threadName;
+ if (gOptions.outputHtml) {
+ threadName = htmlEscape(threadName, threadBuf, HTML_BUFSIZE);
+ }
+
+ printf(" %9llu %6.2f %6.2f %s\n",
+ filters[ii]->times.threadWaitTimes[thread.threadId],
+ 100.0 * filters[ii]->times.threadWaitTimes[thread.threadId] / filters[ii]->times.totalWaitTime,
+ 100.0 * filters[ii]->times.threadExecutionTimesWhileWaiting[thread.threadId] / filters[ii]->times.totalWaitTime,
+ threadName);
+ }
+ }
+
+ if (gOptions.outputHtml)
+ printf("</pre>\n");
+
+ }
+ }
+
+}
+
+void createClassList(TraceData* traceData, MethodEntry **pMethods, int numMethods)
+{
+ int ii;
+
+ /* Sort the methods into alphabetical order to find the unique class
+ * names.
+ */
+ qsort(pMethods, numMethods, sizeof(MethodEntry*), compareClassNames);
+
+ /* Count the number of unique class names. */
+ const char *currentClassName = "";
+ const char *firstClassName = NULL;
+ traceData->numClasses = 0;
+ for (ii = 0; ii < numMethods; ++ii) {
+ if (pMethods[ii]->methodName == NULL) {
+ continue;
+ }
+ if (strcmp(pMethods[ii]->className, currentClassName) != 0) {
+ // Remember the first one
+ if (firstClassName == NULL) {
+ firstClassName = pMethods[ii]->className;
+ }
+ traceData->numClasses += 1;
+ currentClassName = pMethods[ii]->className;
+ }
+ }
+
+ if (traceData->numClasses == 0) {
+ traceData->classes = NULL;
+ return;
+ }
+
+ /* Allocate space for all of the unique class names */
+ traceData->classes = (ClassEntry *) malloc(sizeof(ClassEntry) * traceData->numClasses);
+
+ /* Initialize the classes array */
+ memset(traceData->classes, 0, sizeof(ClassEntry) * traceData->numClasses);
+ ClassEntry *pClass = traceData->classes;
+ pClass->className = currentClassName = firstClassName;
+ int prevNumMethods = 0;
+ for (ii = 0; ii < numMethods; ++ii) {
+ if (pMethods[ii]->methodName == NULL) {
+ continue;
+ }
+ if (strcmp(pMethods[ii]->className, currentClassName) != 0) {
+ pClass->numMethods = prevNumMethods;
+ (++pClass)->className = currentClassName = pMethods[ii]->className;
+ prevNumMethods = 0;
+ }
+ prevNumMethods += 1;
+ }
+ pClass->numMethods = prevNumMethods;
+
+ /* Create the array of MethodEntry pointers for each class */
+ pClass = NULL;
+ currentClassName = "";
+ int nextMethod = 0;
+ for (ii = 0; ii < numMethods; ++ii) {
+ if (pMethods[ii]->methodName == NULL) {
+ continue;
+ }
+ if (strcmp(pMethods[ii]->className, currentClassName) != 0) {
+ currentClassName = pMethods[ii]->className;
+ if (pClass == NULL)
+ pClass = traceData->classes;
+ else
+ pClass++;
+ /* Allocate space for the methods array */
+ int nbytes = sizeof(MethodEntry*) * pClass->numMethods;
+ pClass->methods = (MethodEntry**) malloc(nbytes);
+ nextMethod = 0;
+ }
+ pClass->methods[nextMethod++] = pMethods[ii];
+ }
+}
+
+/* Prints a number of html non-breaking spaces according so that the length
+ * of the string "buf" is at least "width" characters wide. If width is
+ * negative, then trailing spaces are added instead of leading spaces.
+ */
+void printHtmlField(char *buf, int width)
+{
+ int ii;
+
+ int leadingSpaces = 1;
+ if (width < 0) {
+ width = -width;
+ leadingSpaces = 0;
+ }
+ int len = strlen(buf);
+ int numSpaces = width - len;
+ if (numSpaces <= 0) {
+ printf("%s", buf);
+ return;
+ }
+ if (leadingSpaces == 0)
+ printf("%s", buf);
+ for (ii = 0; ii < numSpaces; ++ii)
+ printf("&nbsp;");
+ if (leadingSpaces == 1)
+ printf("%s", buf);
+}
+
+void printClassProfiles(TraceData* traceData, uint64_t sumThreadTime)
+{
+ int ii, jj;
+ MethodEntry* method;
+ double total, sum, per, sum_per;
+ char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE];
+ char signatureBuf[HTML_BUFSIZE];
+
+ total = sumThreadTime;
+ if (gOptions.outputHtml) {
+ printf("<a name=\"class\"></a>\n");
+ printf("<hr>\n");
+ outputNavigationBar();
+ } else {
+ printf("\n%s\n", profileSeparator);
+ }
+
+ if (traceData->numClasses == 0) {
+ printf("\nNo classes.\n");
+ if (gOptions.outputHtml) {
+ printf("<br><br>\n");
+ }
+ return;
+ }
+
+ printf("\nExclusive elapsed time for each class, summed over all the methods\n");
+ printf("in the class.\n\n");
+ if (gOptions.outputHtml) {
+ printf("<br><br>\n");
+ }
+
+ /* For each class, sum the exclusive times in all of the methods
+ * in that class. Also sum the number of method calls. Also
+ * sort the methods so the most expensive appear at the top.
+ */
+ ClassEntry *pClass = traceData->classes;
+ for (ii = 0; ii < traceData->numClasses; ++ii, ++pClass) {
+ //printf("%s %d methods\n", pClass->className, pClass->numMethods);
+ int numMethods = pClass->numMethods;
+ for (jj = 0; jj < numMethods; ++jj) {
+ method = pClass->methods[jj];
+ pClass->elapsedExclusive += method->elapsedExclusive;
+ pClass->numCalls[0] += method->numCalls[0];
+ pClass->numCalls[1] += method->numCalls[1];
+ }
+
+ /* Sort the methods into decreasing order of exclusive time */
+ qsort(pClass->methods, numMethods, sizeof(MethodEntry*),
+ compareElapsedExclusive);
+ }
+
+ /* Allocate an array of pointers to the classes for more efficient
+ * sorting.
+ */
+ ClassEntry **pClasses;
+ pClasses = (ClassEntry**) malloc(sizeof(ClassEntry*) * traceData->numClasses);
+ for (ii = 0; ii < traceData->numClasses; ++ii)
+ pClasses[ii] = &traceData->classes[ii];
+
+ /* Sort the classes into decreasing order of exclusive time */
+ qsort(pClasses, traceData->numClasses, sizeof(ClassEntry*), compareClassExclusive);
+
+ if (gOptions.outputHtml) {
+ printf("<div class=\"header\"><span class=\"parent\">&nbsp;</span>&nbsp;&nbsp;&nbsp;");
+ printf("Cycles %%/total Cumul.%% &nbsp;Calls+Recur&nbsp; Class</div>\n");
+ } else {
+ printf(" Cycles %%/total Cumul.%% Calls+Recur Class\n");
+ }
+
+ sum = 0;
+ for (ii = 0; ii < traceData->numClasses; ++ii) {
+ char *className, *methodName, *signature;
+
+ /* Skip classes with zero cycles */
+ pClass = pClasses[ii];
+ if (pClass->elapsedExclusive == 0)
+ break;
+
+ per = 100.0 * pClass->elapsedExclusive / total;
+ sum += pClass->elapsedExclusive;
+ sum_per = 100.0 * sum / total;
+ className = (char*)(pClass->className);
+ if (gOptions.outputHtml) {
+ char buf[80];
+
+ className = htmlEscape(className, classBuf, HTML_BUFSIZE);
+ printf("<div class=\"link\" onClick=\"javascript:toggle('d%d')\" onMouseOver=\"javascript:onMouseOver(this)\" onMouseOut=\"javascript:onMouseOut(this)\"><span class=\"parent\" id=\"xd%d\">+</span>", ii, ii);
+ sprintf(buf, "%llu", pClass->elapsedExclusive);
+ printHtmlField(buf, 9);
+ printf(" ");
+ sprintf(buf, "%.1f", per);
+ printHtmlField(buf, 7);
+ printf(" ");
+ sprintf(buf, "%.1f", sum_per);
+ printHtmlField(buf, 7);
+ printf(" ");
+ sprintf(buf, "%d", pClass->numCalls[0]);
+ printHtmlField(buf, 6);
+ printf("+");
+ sprintf(buf, "%d", pClass->numCalls[1]);
+ printHtmlField(buf, -6);
+ printf(" ");
+ printf("%s", className);
+ printf("</div>\n");
+ printf("<div class=\"parent\" id=\"d%d\">\n", ii);
+ } else {
+ printf("---------------------------------------------\n");
+ printf("%9llu %7.1f %7.1f %6d+%-6d %s\n",
+ pClass->elapsedExclusive, per, sum_per,
+ pClass->numCalls[0], pClass->numCalls[1],
+ className);
+ }
+
+ int numMethods = pClass->numMethods;
+ double classExclusive = pClass->elapsedExclusive;
+ double sumMethods = 0;
+ for (jj = 0; jj < numMethods; ++jj) {
+ method = pClass->methods[jj];
+ methodName = (char*)(method->methodName);
+ signature = (char*)(method->signature);
+ per = 100.0 * method->elapsedExclusive / classExclusive;
+ sumMethods += method->elapsedExclusive;
+ sum_per = 100.0 * sumMethods / classExclusive;
+ if (gOptions.outputHtml) {
+ char buf[80];
+
+ methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE);
+ signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE);
+ printf("<div class=\"leaf\"><span class=\"leaf\">&nbsp;</span>");
+ sprintf(buf, "%llu", method->elapsedExclusive);
+ printHtmlField(buf, 9);
+ printf("&nbsp;");
+ sprintf(buf, "%llu", method->elapsedInclusive);
+ printHtmlField(buf, 9);
+ printf("&nbsp;");
+ sprintf(buf, "%.1f", per);
+ printHtmlField(buf, 7);
+ printf("&nbsp;");
+ sprintf(buf, "%.1f", sum_per);
+ printHtmlField(buf, 7);
+ printf("&nbsp;");
+ sprintf(buf, "%d", method->numCalls[0]);
+ printHtmlField(buf, 6);
+ printf("+");
+ sprintf(buf, "%d", method->numCalls[1]);
+ printHtmlField(buf, -6);
+ printf("&nbsp;");
+ printf("<a href=\"#m%d\">[%d]</a>&nbsp;%s&nbsp;%s",
+ method->index, method->index, methodName, signature);
+ printf("</div>\n");
+ } else {
+ printf("%9llu %9llu %7.1f %7.1f %6d+%-6d [%d] %s %s\n",
+ method->elapsedExclusive,
+ method->elapsedInclusive,
+ per, sum_per,
+ method->numCalls[0], method->numCalls[1],
+ method->index, methodName, signature);
+ }
+ }
+ if (gOptions.outputHtml) {
+ printf("</div>\n");
+ }
+ }
+}
+
+void createUniqueMethodList(TraceData* traceData, MethodEntry **pMethods, int numMethods)
+{
+ int ii;
+
+ /* Sort the methods into alphabetical order of method names
+ * to find the unique method names.
+ */
+ qsort(pMethods, numMethods, sizeof(MethodEntry*), compareMethodNames);
+
+ /* Count the number of unique method names, ignoring class and
+ * signature.
+ */
+ const char *currentMethodName = "";
+ traceData->numUniqueMethods = 0;
+ for (ii = 0; ii < numMethods; ++ii) {
+ if (pMethods[ii]->methodName == NULL)
+ continue;
+ if (strcmp(pMethods[ii]->methodName, currentMethodName) != 0) {
+ traceData->numUniqueMethods += 1;
+ currentMethodName = pMethods[ii]->methodName;
+ }
+ }
+ if (traceData->numUniqueMethods == 0)
+ return;
+
+ /* Allocate space for pointers to all of the unique methods */
+ int nbytes = sizeof(UniqueMethodEntry) * traceData->numUniqueMethods;
+ traceData->uniqueMethods = (UniqueMethodEntry *) malloc(nbytes);
+
+ /* Initialize the uniqueMethods array */
+ memset(traceData->uniqueMethods, 0, nbytes);
+ UniqueMethodEntry *pUnique = traceData->uniqueMethods;
+ currentMethodName = NULL;
+ int prevNumMethods = 0;
+ for (ii = 0; ii < numMethods; ++ii) {
+ if (pMethods[ii]->methodName == NULL)
+ continue;
+ if (currentMethodName == NULL)
+ currentMethodName = pMethods[ii]->methodName;
+ if (strcmp(pMethods[ii]->methodName, currentMethodName) != 0) {
+ currentMethodName = pMethods[ii]->methodName;
+ pUnique->numMethods = prevNumMethods;
+ pUnique++;
+ prevNumMethods = 0;
+ }
+ prevNumMethods += 1;
+ }
+ pUnique->numMethods = prevNumMethods;
+
+ /* Create the array of MethodEntry pointers for each unique method */
+ pUnique = NULL;
+ currentMethodName = "";
+ int nextMethod = 0;
+ for (ii = 0; ii < numMethods; ++ii) {
+ if (pMethods[ii]->methodName == NULL)
+ continue;
+ if (strcmp(pMethods[ii]->methodName, currentMethodName) != 0) {
+ currentMethodName = pMethods[ii]->methodName;
+ if (pUnique == NULL)
+ pUnique = traceData->uniqueMethods;
+ else
+ pUnique++;
+ /* Allocate space for the methods array */
+ int nbytes = sizeof(MethodEntry*) * pUnique->numMethods;
+ pUnique->methods = (MethodEntry**) malloc(nbytes);
+ nextMethod = 0;
+ }
+ pUnique->methods[nextMethod++] = pMethods[ii];
+ }
+}
+
+void printMethodProfiles(TraceData* traceData, uint64_t sumThreadTime)
+{
+ int ii, jj;
+ MethodEntry* method;
+ double total, sum, per, sum_per;
+ char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE];
+ char signatureBuf[HTML_BUFSIZE];
+
+ if (traceData->numUniqueMethods == 0)
+ return;
+
+ total = sumThreadTime;
+ if (gOptions.outputHtml) {
+ printf("<a name=\"method\"></a>\n");
+ printf("<hr>\n");
+ outputNavigationBar();
+ } else {
+ printf("\n%s\n", profileSeparator);
+ }
+
+ printf("\nExclusive elapsed time for each method, summed over all the classes\n");
+ printf("that contain a method with the same name.\n\n");
+ if (gOptions.outputHtml) {
+ printf("<br><br>\n");
+ }
+
+ /* For each unique method, sum the exclusive times in all of the methods
+ * with the same name. Also sum the number of method calls. Also
+ * sort the methods so the most expensive appear at the top.
+ */
+ UniqueMethodEntry *pUnique = traceData->uniqueMethods;
+ for (ii = 0; ii < traceData->numUniqueMethods; ++ii, ++pUnique) {
+ int numMethods = pUnique->numMethods;
+ for (jj = 0; jj < numMethods; ++jj) {
+ method = pUnique->methods[jj];
+ pUnique->elapsedExclusive += method->elapsedExclusive;
+ pUnique->numCalls[0] += method->numCalls[0];
+ pUnique->numCalls[1] += method->numCalls[1];
+ }
+
+ /* Sort the methods into decreasing order of exclusive time */
+ qsort(pUnique->methods, numMethods, sizeof(MethodEntry*),
+ compareElapsedExclusive);
+ }
+
+ /* Allocate an array of pointers to the methods for more efficient
+ * sorting.
+ */
+ UniqueMethodEntry **pUniqueMethods;
+ int nbytes = sizeof(UniqueMethodEntry*) * traceData->numUniqueMethods;
+ pUniqueMethods = (UniqueMethodEntry**) malloc(nbytes);
+ for (ii = 0; ii < traceData->numUniqueMethods; ++ii)
+ pUniqueMethods[ii] = &traceData->uniqueMethods[ii];
+
+ /* Sort the methods into decreasing order of exclusive time */
+ qsort(pUniqueMethods, traceData->numUniqueMethods, sizeof(UniqueMethodEntry*),
+ compareUniqueExclusive);
+
+ if (gOptions.outputHtml) {
+ printf("<div class=\"header\"><span class=\"parent\">&nbsp;</span>&nbsp;&nbsp;&nbsp;");
+ printf("Cycles %%/total Cumul.%% &nbsp;Calls+Recur&nbsp; Method</div>\n");
+ } else {
+ printf(" Cycles %%/total Cumul.%% Calls+Recur Method\n");
+ }
+
+ sum = 0;
+ for (ii = 0; ii < traceData->numUniqueMethods; ++ii) {
+ char *className, *methodName, *signature;
+
+ /* Skip methods with zero cycles */
+ pUnique = pUniqueMethods[ii];
+ if (pUnique->elapsedExclusive == 0)
+ break;
+
+ per = 100.0 * pUnique->elapsedExclusive / total;
+ sum += pUnique->elapsedExclusive;
+ sum_per = 100.0 * sum / total;
+ methodName = (char*)(pUnique->methods[0]->methodName);
+ if (gOptions.outputHtml) {
+ char buf[80];
+
+ methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE);
+ printf("<div class=\"link\" onClick=\"javascript:toggle('e%d')\" onMouseOver=\"javascript:onMouseOver(this)\" onMouseOut=\"javascript:onMouseOut(this)\"><span class=\"parent\" id=\"xe%d\">+</span>", ii, ii);
+ sprintf(buf, "%llu", pUnique->elapsedExclusive);
+ printHtmlField(buf, 9);
+ printf(" ");
+ sprintf(buf, "%.1f", per);
+ printHtmlField(buf, 7);
+ printf(" ");
+ sprintf(buf, "%.1f", sum_per);
+ printHtmlField(buf, 7);
+ printf(" ");
+ sprintf(buf, "%d", pUnique->numCalls[0]);
+ printHtmlField(buf, 6);
+ printf("+");
+ sprintf(buf, "%d", pUnique->numCalls[1]);
+ printHtmlField(buf, -6);
+ printf(" ");
+ printf("%s", methodName);
+ printf("</div>\n");
+ printf("<div class=\"parent\" id=\"e%d\">\n", ii);
+ } else {
+ printf("---------------------------------------------\n");
+ printf("%9llu %7.1f %7.1f %6d+%-6d %s\n",
+ pUnique->elapsedExclusive, per, sum_per,
+ pUnique->numCalls[0], pUnique->numCalls[1],
+ methodName);
+ }
+ int numMethods = pUnique->numMethods;
+ double methodExclusive = pUnique->elapsedExclusive;
+ double sumMethods = 0;
+ for (jj = 0; jj < numMethods; ++jj) {
+ method = pUnique->methods[jj];
+ className = (char*)(method->className);
+ signature = (char*)(method->signature);
+ per = 100.0 * method->elapsedExclusive / methodExclusive;
+ sumMethods += method->elapsedExclusive;
+ sum_per = 100.0 * sumMethods / methodExclusive;
+ if (gOptions.outputHtml) {
+ char buf[80];
+
+ className = htmlEscape(className, classBuf, HTML_BUFSIZE);
+ signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE);
+ printf("<div class=\"leaf\"><span class=\"leaf\">&nbsp;</span>");
+ sprintf(buf, "%llu", method->elapsedExclusive);
+ printHtmlField(buf, 9);
+ printf("&nbsp;");
+ sprintf(buf, "%llu", method->elapsedInclusive);
+ printHtmlField(buf, 9);
+ printf("&nbsp;");
+ sprintf(buf, "%.1f", per);
+ printHtmlField(buf, 7);
+ printf("&nbsp;");
+ sprintf(buf, "%.1f", sum_per);
+ printHtmlField(buf, 7);
+ printf("&nbsp;");
+ sprintf(buf, "%d", method->numCalls[0]);
+ printHtmlField(buf, 6);
+ printf("+");
+ sprintf(buf, "%d", method->numCalls[1]);
+ printHtmlField(buf, -6);
+ printf("&nbsp;");
+ printf("<a href=\"#m%d\">[%d]</a>&nbsp;%s.%s&nbsp;%s",
+ method->index, method->index,
+ className, methodName, signature);
+ printf("</div>\n");
+ } else {
+ printf("%9llu %9llu %7.1f %7.1f %6d+%-6d [%d] %s.%s %s\n",
+ method->elapsedExclusive,
+ method->elapsedInclusive,
+ per, sum_per,
+ method->numCalls[0], method->numCalls[1],
+ method->index, className, methodName, signature);
+ }
+ }
+ if (gOptions.outputHtml) {
+ printf("</div>\n");
+ }
+ }
+}
+
+/*
+ * Determines whether the given FilterKey matches the method. The FilterKey's
+ * key that is used to match against the method is determined by index.
+ */
+int keyMatchesMethod(FilterKey filterKey, MethodEntry* method, int index)
+{
+ if (filterKey.type[index] == 0) { // Class
+#if 0
+ fprintf(stderr, " class is %s; filter key is %s\n", method->className, filterKey.keys[index]);
+#endif
+ if (strcmp(method->className, filterKey.keys[index]) == 0) {
+ return 1;
+ }
+ } else { // Method
+ if (method->methodName != NULL) {
+ // Get fully-qualified name
+ // TODO: parse class name and method name an put them in structure to avoid
+ // allocating memory here
+ char* str = malloc ((strlen(method->className) + strlen(method->methodName) + 2) * sizeof(char));
+ strcpy(str, method->className);
+ strcat(str, ".");
+ strcat(str, method->methodName);
+#if 0
+ fprintf(stderr, " method is %s; filter key is %s\n", str, filterKey.keys[index]);
+#endif
+ if (strcmp(str, filterKey.keys[index]) == 0) {
+ free(str);
+ return 1;
+ }
+ free(str);
+ }
+ }
+ return 0;
+}
+
+/*
+ * Adds the appropriate times to the given filter based on the given method. Activates and
+ * de-activates filters as necessary.
+ *
+ * A filter is activated when the given method matches the 'entry' key of one of its FilterKeys.
+ * It is de-activated when the method matches the 'exit' key of the same FilterKey that activated it
+ * in the first place. Thus, a filter may be active more than once on the same thread (activated by
+ * different FilterKeys). A filter may also be active on different threads at the same time.
+ *
+ * While the filter is active on thread 1, elapsed time is allocated to different buckets which
+ * include: thread execution time (i.e., time thread 1 spent executing while filter was active),
+ * thread waiting time (i.e., time thread 1 waited while other threads executed), and execution
+ * time while waiting (i.e., time thread x spent executing while thread 1 was waiting). We also
+ * keep track of the total waiting time for a given filter.
+ *
+ * Lastly, we keep track of remaining (un-allocated) time for cases in which we exit a method we
+ * had not entered before, and that method happens to match the 'exit' key of a FilterKey.
+ */
+int filterMethod(MethodEntry* method, Filter* filter, int entry, int threadId, int numThreads,
+ uint64_t elapsed, uint64_t remTime)
+{
+ int ii, jj;
+ int activeCount, addedWaitTimeThreadsCount;
+ int* activeThreads;
+ int* activationKeys;
+ int* addedWaitTimeThreads;
+
+ // flags
+ int addWaitTime = 0;
+ int deactivation = 0;
+ int addedExecutionTime = 0;
+ int addedExecutionTimeWhileWaiting = 0;
+ int addedWaitTime;
+ int addedRemTime = 0;
+ int threadKeyPairActive = 0;
+
+ if (filter->times.threadWaitTimes == NULL && filter->times.threadExecutionTimes == NULL &&
+ filter->times.threadExecutionTimesWhileWaiting == NULL) {
+ filter->times.threadWaitTimes = (uint64_t*) calloc(MAX_THREADS, sizeof(uint64_t));
+ filter->times.threadExecutionTimesWhileWaiting =
+ (uint64_t*) calloc(MAX_THREADS, sizeof(uint64_t));
+ filter->times.threadExecutionTimes = (uint64_t*) calloc(MAX_THREADS, sizeof(uint64_t));
+ }
+
+ int verbose = 0;
+
+ if (verbose)
+ fprintf(stderr,
+ "Running %s filter for class %s method %s, thread %d; activeCount: %d time: %llu\n",
+ filter->filterName, method->className, method->methodName, threadId,
+ filter->activeCount, elapsed);
+
+ // If active on some thread
+ if (filter->activeCount > 0) {
+
+ // Initialize active structures in case there are any de-activations
+ activeThreads = (int*) calloc(filter->activeCount, sizeof(int));
+ activationKeys = (int*) calloc(filter->activeCount, sizeof(int));
+ activeCount = 0;
+
+ // Initialize structure to help us determine which threads we've already added wait time to
+ addedWaitTimeThreads = (int*) calloc(filter->activeCount, sizeof(int));
+ addedWaitTimeThreadsCount = 0;
+
+ // Add times to appropriate sums and de-activate (if necessary)
+ for (ii = 0; ii < filter->activeCount; ii++) {
+
+ if (verbose) {
+ fprintf(stderr, " Analyzing active thread with id %d, activated by key [%s, %s]\n",
+ filter->activeThreads[ii],
+ filter->filterKeys[filter->activationKeys[ii]].keys[0],
+ filter->filterKeys[filter->activationKeys[ii]].keys[1]);
+ }
+
+ // If active on THIS thread -> add to execution time (only add once!)
+ if (filter->activeThreads[ii] == threadId && !addedExecutionTime) {
+ if (verbose)
+ fprintf(stderr, " Adding execution time to this thead\n");
+ filter->times.threadExecutionTimes[threadId] += elapsed;
+ addedExecutionTime = 1;
+ }
+
+ // If active on ANOTHER thread (or this one too) with CROSS_THREAD_FLAG -> add to
+ // both thread's waiting time + total
+ if (filter->filterKeys[filter->activationKeys[ii]].flags == 1) {
+
+ // Add time to thread that is waiting (add to each waiting thread at most once!)
+ addedWaitTime = 0;
+ for (jj = 0; jj < addedWaitTimeThreadsCount; jj++) {
+ if (addedWaitTimeThreads[jj] == filter->activeThreads[ii])
+ addedWaitTime = 1;
+ }
+ if (!addedWaitTime) {
+ if (verbose)
+ fprintf(stderr, " Adding wait time to waiting thread\n");
+ filter->times.threadWaitTimes[filter->activeThreads[ii]] += elapsed;
+ addedWaitTimeThreads[addedWaitTimeThreadsCount++] = filter->activeThreads[ii];
+ }
+
+ // Add execution time to this thread while the other is waiting (only add once!)
+ // [Flag is needed only because outside for loop might iterate through same
+ // thread twice?] TODO: verify
+ if (!addedExecutionTimeWhileWaiting) {
+ if (verbose)
+ fprintf(stderr, " Adding exec time to this thread while thread waits\n");
+ filter->times.threadExecutionTimesWhileWaiting[threadId] += elapsed;
+ addedExecutionTimeWhileWaiting = 1;
+ }
+
+ addWaitTime = 1;
+ }
+
+ // If a method exit matches the EXIT method of an ACTIVE key -> de-activate
+ // the KEY (not the entire filter!!)
+ if (!entry && keyMatchesMethod(filter->filterKeys[filter->activationKeys[ii]],
+ method, 1)) {
+ if (verbose)
+ fprintf(stderr, " Exit key matched!\n");
+
+ // Deactivate by removing (NOT adding) entries from activeThreads and activationKeys
+ deactivation = 1; // singal that lists should be replaced
+ } else {
+ // No de-activation -> copy old entries into new lists
+ activeThreads[activeCount] = filter->activeThreads[ii];
+ activationKeys[activeCount++] = filter->activationKeys[ii];
+ }
+ }
+
+ // If waiting on ANY thread, add wait time to total (but only ONCE!)
+ if (addWaitTime) {
+ filter->times.totalWaitTime += elapsed;
+ }
+
+ // If de-activation occurred, replace lists
+ if (deactivation) {
+ // TODO: Free memory from old lists
+
+ // Set new lists
+ filter->activeThreads = activeThreads;
+ filter->activationKeys = activationKeys;
+ filter->activeCount = activeCount;
+ } else {
+ // TODO: Free memory from new lists
+ }
+
+ } // Else, continue (we might be activating the filter on a different thread)
+
+
+ if (entry) { // ENTRY
+ if (verbose)
+ fprintf(stderr, " Here at the entry\n");
+ // If method matches entry key -> activate thread (do not add time since it's a new entry!)
+ for (ii = 0; ii < filter->numKeys; ii++) {
+ if (keyMatchesMethod(filter->filterKeys[ii], method, 0)) {
+ if (verbose)
+ fprintf(stderr, " Entry key matched!\n");
+ // Activate thread only if thread/key pair is not already active
+ for (jj = 0; jj < filter->activeCount; jj++) {
+ if (filter->activeThreads[jj] == threadId && filter->activationKeys[jj] == ii)
+ threadKeyPairActive = 1;
+ }
+ // TODO: WORRY ABOUT MEMORY WHEN ACTIVE_COUNT > DEFAULT_ACTIVE_THREAD (unlikely)
+ // TODO: what if the same thread is active multiple times by different keys?
+ // nothing, we just have to make sure we dont double-add, and we dont..
+ if (!threadKeyPairActive) {
+ filter->activeThreads[filter->activeCount] = threadId;
+ filter->activationKeys[filter->activeCount++] = ii;
+ }
+ }
+ }
+ } else { // EXIT
+ // If method matches a terminal key -> add remTime to total (no need to active/de-activate)
+ for (ii = 0; ii < filter->numKeys; ii++) {
+ if (!deactivation && keyMatchesMethod(filter->filterKeys[ii], method, 1) &&
+ keyMatchesMethod(filter->filterKeys[ii], method, 0)) {
+ // Add remTime(s)
+ // TODO: think about how we should add remTimes.. should we add remTime to threads
+ // that were waiting or being waited on? for now, keep it simple and just add the
+ // execution time to the current thread.
+ filter->times.threadExecutionTimes[threadId] += remTime;
+ addedRemTime = 1;
+ }
+ }
+ }
+
+ return addedExecutionTime | (addedRemTime << 1);
+}
+
+void dumpFilters(Filter** filters) {
+ int i;
+ for (i = 0; i < numFilters; i++) {
+ int j;
+ fprintf(stderr, "FILTER %s\n", filters[i]->filterName);
+ for (j = 0; j < filters[i]->numKeys; j++) {
+ fprintf(stderr, "Keys: %s, type %d", filters[i]->filterKeys[j].keys[0],
+ filters[i]->filterKeys[j].type[0]);
+ if (filters[i]->filterKeys[j].keys[1] != NULL) {
+ fprintf(stderr, " AND %s, type %d", filters[i]->filterKeys[j].keys[1],
+ filters[i]->filterKeys[j].type[1]);
+ }
+ fprintf(stderr, "; flags: %d\n", filters[i]->filterKeys[j].flags);
+ }
+ }
+}
+
+/*
+ * See parseFilters for required data format.
+ * 'data' must point to the beginning of a filter definition.
+ */
+char* parseFilter(char* data, char* dataEnd, Filter** filters, int num) {
+
+ Filter* filter;
+ int next, count, i;
+ int tmpOffset, tmpKeyLen;
+ char* tmpKey;
+ char* key1;
+ char* key2;
+
+ filter = (Filter*) malloc(sizeof(Filter));
+ filter->activeCount = 0;
+ filter->activeThreads = (int*) calloc(DEFAULT_ACTIVE_THREADS, sizeof(int));
+ filter->activationKeys = (int*) calloc(DEFAULT_ACTIVE_THREADS, sizeof(int));
+
+ next = findNextChar(data + 1, dataEnd - data - 1, '\n');
+ if (next < 0) {
+ // TODO: what should we do here?
+ // End of file reached...
+ }
+ data[next+1] = '\0';
+ filter->filterName = data + 1;
+ data += next + 2; // Careful
+
+ /*
+ * Count the number of keys (one per line).
+ */
+ count = countLinesToChar(data, dataEnd - data, FILTER_TAG);
+ if (count <= 0) {
+ fprintf(stderr,
+ "ERROR: failed while parsing filter %s (found %d keys)\n",
+ filter->filterName, count);
+ return NULL; // TODO: Should do something else
+ // Could return filter with 0 keys instead (probably better to avoid random segfaults)
+ }
+
+ filter->filterKeys = (FilterKey*) malloc(sizeof(FilterKey) * count);
+
+ /*
+ * Extract all entries.
+ */
+ tmpOffset = 0;
+ for (i = 0; i < count; i++) {
+ next = findNextChar(data, dataEnd - data, '\n');
+ // assert(next > 0); // TODO: revise... (skip if next == 0 ?)
+ data[next] = '\0';
+ tmpKey = data;
+
+ if (*data == FILTER_FLAG_THREAD) {
+ filter->filterKeys[i].flags = 1;
+ tmpKey++;
+ } else {
+ filter->filterKeys[i].flags = 0;
+ }
+
+ tmpOffset = findNextChar(tmpKey, next, ',');
+
+ if (tmpOffset < 0) {
+ // No comma, so only 1 key
+ key1 = tmpKey;
+ key2 = tmpKey;
+
+ // Get type for key1
+ filter->filterKeys[i].type[0] = FILTER_TYPE_CLASS; // default
+ tmpOffset = findNextChar(key1, next, '(');
+ if (tmpOffset > 0) {
+ if (findNextChar(key1, next, ')') == tmpOffset + 1) {
+ filter->filterKeys[i].type[0] = FILTER_TYPE_METHOD;
+ filter->filterKeys[i].type[1] = FILTER_TYPE_METHOD;
+ }
+ key1[tmpOffset] = '\0';
+ }
+ } else {
+ // Pair of keys
+ tmpKey[tmpOffset] = '\0';
+ key1 = tmpKey;
+ key2 = tmpKey + tmpOffset + 1;
+
+ // Get type for key1
+ filter->filterKeys[i].type[0] = FILTER_TYPE_CLASS;
+ tmpKeyLen = tmpOffset;
+ tmpOffset = findNextChar(key1, tmpKeyLen, '(');
+ if (tmpOffset > 0) {
+ if (findNextChar(key1, tmpKeyLen, ')') == tmpOffset + 1) {
+ filter->filterKeys[i].type[0] = FILTER_TYPE_METHOD;
+ }
+ key1[tmpOffset] = '\0';
+ }
+
+ // Get type for key2
+ filter->filterKeys[i].type[1] = FILTER_TYPE_CLASS;
+ tmpOffset = findNextChar(key2, next - tmpKeyLen, '(');
+ if (tmpOffset > 0) {
+ if (findNextChar(key2, next - tmpKeyLen, ')') == tmpOffset + 1) {
+ filter->filterKeys[i].type[1] = FILTER_TYPE_METHOD;
+ }
+ key2[tmpOffset] = '\0';
+ }
+ }
+
+ filter->filterKeys[i].keys[0] = key1;
+ filter->filterKeys[i].keys[1] = key2;
+ data += next+1;
+ }
+
+ filter->numKeys = count;
+ filters[num] = filter;
+
+ return data;
+}
+
+/*
+ * Parses filters from given file. The file must follow the following format:
+ *
+ * *FilterName <- creates a new filter with keys to follow
+ * A.method() <- key that triggers whenever A.method() enters/exit
+ * Class <- key that triggers whenever any method from Class enters/exits
+ * +CrossThread <- same as above, but keeps track of execution times accross threads
+ * B.m(),C.m() <- key that triggers filter on when B.m() enters and off when C.m() exits
+ *
+ * TODO: add concrete example to make things clear
+ */
+Filter** parseFilters(const char* filterFileName) {
+
+ Filter** filters = NULL;
+ FILE* fp = NULL;
+ long len;
+ char* data;
+ char* dataEnd;
+ char* dataStart;
+ int i, next, count;
+
+ fp = fopen(filterFileName, "r");
+ if (fp == NULL)
+ goto bail;
+
+ if (fseek(fp, 0L, SEEK_END) != 0) {
+ perror("fseek");
+ goto bail;
+ }
+
+ len = ftell(fp);
+ if (len == 0) {
+ fprintf(stderr, "WARNING: Filter file is empty.\n");
+ goto bail;
+ }
+ rewind(fp);
+
+ data = (char*) malloc(len);
+ if (data == NULL) {
+ fprintf(stderr, "ERROR: unable to alloc %ld bytes for filter file\n", len);
+ goto bail;
+ }
+
+ // Read file into memory
+ if (fread(data, 1, len, fp) != (size_t) len) {
+ fprintf(stderr, "ERROR: unable to read %ld bytes from filter file\n", len);
+ goto bail;
+ }
+
+ dataStart = data;
+ dataEnd = data + len;
+
+ // Figure out how many filters there are
+ numFilters = 0;
+ next = -1;
+
+ while (1) {
+ if (*data == FILTER_TAG)
+ numFilters++;
+ next = findNextChar(data, len, '\n');
+ if (next < 0)
+ break;
+ data += next+1;
+ len -= next+1;
+ }
+
+ if (numFilters == 0) {
+ fprintf(stderr, "WARNING: no filters found. Continuing without filters\n");
+ goto bail;
+ }
+
+ filters = (Filter**) calloc(numFilters, sizeof(Filter *));
+ if (filters == NULL) {
+ fprintf(stderr, "ERROR: unable to alloc memory for filters");
+ goto bail;
+ }
+
+ data = dataStart;
+ for (i = 0; i < numFilters; i++) {
+ data = parseFilter(data, dataEnd, filters, i);
+ }
+
+ return filters;
+
+bail:
+ if (fp != NULL)
+ fclose(fp);
+
+ return NULL;
+
+}
+
+
+/*
+ * Read the key and data files and return the MethodEntries for those files
+ */
+DataKeys* parseDataKeys(TraceData* traceData, const char* traceFileName,
+ uint64_t* threadTime, Filter** filters)
+{
+ DataKeys* dataKeys = NULL;
+ MethodEntry **pMethods = NULL;
+ MethodEntry* method;
+ FILE* dataFp = NULL;
+ DataHeader dataHeader;
+ int ii, jj, numThreads;
+ uint64_t currentTime;
+ MethodEntry* caller;
+
+ dataFp = fopen(traceFileName, "r");
+ if (dataFp == NULL)
+ goto bail;
+
+ if ((dataKeys = parseKeys(dataFp, 0)) == NULL)
+ goto bail;
+
+ if (parseDataHeader(dataFp, &dataHeader) < 0)
+ goto bail;
+
+ numThreads = dataKeys->numThreads;
+
+#if 0
+ FILE *dumpStream = fopen("debug", "w");
+#endif
+ while (1) {
+ int threadId;
+ unsigned int methodVal;
+ int action;
+ unsigned int methodId;
+ CallStack *pStack;
+
+ /*
+ * Extract values from file.
+ */
+ if (readDataRecord(dataFp, &threadId, &methodVal, &currentTime))
+ break;
+
+ action = METHOD_ACTION(methodVal);
+ methodId = METHOD_ID(methodVal);
+
+ /* Get the call stack for this thread */
+ pStack = traceData->stacks[threadId];
+
+ /* If there is no call stack yet for this thread, then allocate one */
+ if (pStack == NULL) {
+ pStack = malloc(sizeof(CallStack));
+ pStack->top = 0;
+ pStack->lastEventTime = currentTime;
+ pStack->threadStartTime = currentTime;
+ pStack->remTimes = (uint64_t*) calloc(numFilters, sizeof(uint64_t));
+ traceData->stacks[threadId] = pStack;
+ }
+
+ /* Lookup the current method */
+ method = lookupMethod(dataKeys, methodId);
+ if (method == NULL)
+ method = &dataKeys->methods[UNKNOWN_INDEX];
+
+#if 0
+ if (method->methodName) {
+ fprintf(dumpStream, "%2d %-8llu %d %8llu r %d c %d %s.%s %s\n",
+ threadId, currentTime, action, pStack->threadStartTime,
+ method->recursiveEntries,
+ pStack->top, method->className, method->methodName,
+ method->signature);
+ } else {
+ printf(dumpStream, "%2d %-8llu %d %8llu r %d c %d %s\n",
+ threadId, currentTime, action, pStack->threadStartTime,
+ method->recursiveEntries,
+ pStack->top, method->className);
+ }
+#endif
+
+ if (action == METHOD_TRACE_ENTER) {
+ /* This is a method entry */
+ if (pStack->top >= MAX_STACK_DEPTH) {
+ fprintf(stderr, "Stack overflow (exceeded %d frames)\n",
+ MAX_STACK_DEPTH);
+ exit(1);
+ }
+
+ /* Get the caller method */
+ if (pStack->top >= 1)
+ caller = pStack->calls[pStack->top - 1].method;
+ else
+ caller = &dataKeys->methods[TOPLEVEL_INDEX];
+ countRecursiveEntries(pStack, pStack->top, caller);
+ caller->elapsedExclusive += currentTime - pStack->lastEventTime;
+#if 0
+ if (caller->elapsedExclusive > 10000000)
+ fprintf(dumpStream, "%llu current %llu last %llu diff %llu\n",
+ caller->elapsedExclusive, currentTime,
+ pStack->lastEventTime,
+ currentTime - pStack->lastEventTime);
+#endif
+ if (caller->recursiveEntries <= 1) {
+ caller->topExclusive += currentTime - pStack->lastEventTime;
+ }
+
+ /* Push the method on the stack for this thread */
+ pStack->calls[pStack->top].method = method;
+ pStack->calls[pStack->top++].entryTime = currentTime;
+
+ // For each filter
+ int result = 0;
+ for (ii = 0; ii < numFilters; ii++) {
+ result = filterMethod(method, filters[ii], 1, threadId, numThreads,
+ currentTime - pStack->lastEventTime, pStack->remTimes[ii]);
+
+ // TODO: make remTimes work properly
+ // Consider moving remTimes handling together with the rest
+ // of time handling and clean up the return codes
+ /*
+ if (result == 0) { // no time added, no remTime added
+ pStack->remTimes[ii] += currentTime - pStack->lastEventTime;
+ } else if (result == 3 || result == 4) { // remTime added
+ // Reset remTime, since it's been added
+ pStack->remTimes[ii] = 0;
+ }
+ */
+ }
+
+ } else {
+ /* This is a method exit */
+ uint64_t entryTime = 0;
+
+ /* Pop the method off the stack for this thread */
+ if (pStack->top > 0) {
+ pStack->top -= 1;
+ entryTime = pStack->calls[pStack->top].entryTime;
+ if (method != pStack->calls[pStack->top].method) {
+ if (method->methodName) {
+ fprintf(stderr,
+ "Exit from method %s.%s %s does not match stack:\n",
+ method->className, method->methodName,
+ method->signature);
+ } else {
+ fprintf(stderr,
+ "Exit from method %s does not match stack:\n",
+ method->className);
+ }
+ stackDump(pStack, pStack->top + 1);
+ exit(1);
+ }
+ }
+
+ /* Get the caller method */
+ if (pStack->top >= 1)
+ caller = pStack->calls[pStack->top - 1].method;
+ else
+ caller = &dataKeys->methods[TOPLEVEL_INDEX];
+ countRecursiveEntries(pStack, pStack->top, caller);
+ countRecursiveEntries(pStack, pStack->top, method);
+ uint64_t elapsed = currentTime - entryTime;
+ addInclusiveTime(caller, method, elapsed);
+ method->elapsedExclusive += currentTime - pStack->lastEventTime;
+ if (method->recursiveEntries == 0) {
+ method->topExclusive += currentTime - pStack->lastEventTime;
+ }
+
+ // For each filter
+ int result = 0;
+ for (ii = 0; ii < numFilters; ii++) {
+ result = filterMethod(method, filters[ii], 0, threadId, numThreads,
+ currentTime - pStack->lastEventTime, pStack->remTimes[ii]);
+
+ // TODO: make remTimes work properly
+ /*
+ if (result == 0) { // no time added, no remTime added
+ pStack->remTimes[ii] += currentTime - pStack->lastEventTime;
+ } else if (result == 3 || result == 4) { // remTime added
+ // Reset remTime, since it's been added
+ pStack->remTimes[ii] = 0;
+ }
+ */
+ }
+
+ }
+ /* Remember the time of the last entry or exit event */
+ pStack->lastEventTime = currentTime;
+ }
+
+ /* If we have calls on the stack when the trace ends, then clean
+ * up the stack and add time to the callers by pretending that we
+ * are exiting from their methods now.
+ */
+ CallStack *pStack;
+ int threadId;
+ uint64_t elapsedTime = 0;
+ uint64_t sumThreadTime = 0;
+ for (threadId = 0; threadId < MAX_THREADS; ++threadId) {
+
+ pStack = traceData->stacks[threadId];
+
+ /* If this thread never existed, then continue with next thread */
+ if (pStack == NULL)
+ continue;
+
+ /* Calculate times spent in thread, and add it to total time */
+ elapsedTime = pStack->lastEventTime - pStack->threadStartTime;
+ sumThreadTime += elapsedTime;
+
+ for (ii = 0; ii < pStack->top; ++ii) {
+ //printf("in loop\n");
+
+ if (ii == 0)
+ caller = &dataKeys->methods[TOPLEVEL_INDEX];
+ else
+ caller = pStack->calls[ii - 1].method;
+ method = pStack->calls[ii].method;
+ countRecursiveEntries(pStack, ii, caller);
+ countRecursiveEntries(pStack, ii, method);
+
+ uint64_t entryTime = pStack->calls[ii].entryTime;
+ uint64_t elapsed = pStack->lastEventTime - entryTime;
+ addInclusiveTime(caller, method, elapsed);
+
+ // For each filter
+ int result = 0;
+ for (ii = 0; ii < numFilters; ii++) {
+ result = filterMethod(method, filters[ii], 0, threadId, numThreads,
+ currentTime - pStack->lastEventTime, pStack->remTimes[ii]);
+
+ // TODO: make remTimes work properly
+ /*
+ if (result == 0) { // no time added, no remTime added
+ pStack->remTimes[ii] += currentTime - pStack->lastEventTime;
+ } else if (result == 3 || result == 4) { // remTime added
+ // Reset remTime, since it's been added
+ pStack->remTimes[ii] = 0;
+ }
+ */
+ }
+ }
+
+ /* Save the per-thread elapsed time in the DataKeys struct */
+ for (ii = 0; ii < dataKeys->numThreads; ++ii) {
+ if (dataKeys->threads[ii].threadId == threadId) {
+ dataKeys->threads[ii].elapsedTime = elapsedTime;
+ }
+ }
+
+
+ }
+ caller = &dataKeys->methods[TOPLEVEL_INDEX];
+ caller->elapsedInclusive = sumThreadTime;
+
+#if 0
+ fclose(dumpStream);
+#endif
+
+ if (threadTime != NULL) {
+ *threadTime = sumThreadTime;
+ }
+
+bail:
+ if (dataFp != NULL)
+ fclose(dataFp);
+
+ return dataKeys;
+}
+
+MethodEntry** parseMethodEntries(DataKeys* dataKeys)
+{
+ int ii;
+ /* Create a new array of pointers to the methods and sort the pointers
+ * instead of the actual MethodEntry structs. We need to do this
+ * because there are other lists that contain pointers to the
+ * MethodEntry structs.
+ */
+ MethodEntry** pMethods = (MethodEntry**) malloc(sizeof(MethodEntry*) * dataKeys->numMethods);
+ for (ii = 0; ii < dataKeys->numMethods; ++ii) {
+ MethodEntry* entry = &dataKeys->methods[ii];
+ pMethods[ii] = entry;
+ }
+
+ return pMethods;
+}
+
+
+/*
+ * Produce a function profile from the following methods
+ */
+void profileTrace(TraceData* traceData, MethodEntry **pMethods, int numMethods, uint64_t sumThreadTime,
+ ThreadEntry *pThreads, int numThreads, Filter** filters)
+{
+ /* Print the html header, if necessary */
+ if (gOptions.outputHtml) {
+ printf(htmlHeader, gOptions.sortableUrl);
+ outputTableOfContents();
+ }
+
+ printExclusiveProfile(pMethods, numMethods, sumThreadTime);
+ printInclusiveProfile(pMethods, numMethods, sumThreadTime);
+
+ printThreadProfile(pThreads, numThreads, sumThreadTime, filters);
+
+ createClassList(traceData, pMethods, numMethods);
+ printClassProfiles(traceData, sumThreadTime);
+
+ createUniqueMethodList(traceData, pMethods, numMethods);
+ printMethodProfiles(traceData, sumThreadTime);
+
+ if (gOptions.outputHtml) {
+ printf("%s", htmlFooter);
+ }
+}
+
+int compareMethodNamesForDiff(const void *a, const void *b)
+{
+ int result;
+
+ const MethodEntry *methodA = *(const MethodEntry**)a;
+ const MethodEntry *methodB = *(const MethodEntry**)b;
+ if (methodA->methodName == NULL || methodB->methodName == NULL) {
+ return compareClassNames(a, b);
+ }
+ result = strcmp(methodA->methodName, methodB->methodName);
+ if (result == 0) {
+ result = strcmp(methodA->signature, methodB->signature);
+ if (result == 0) {
+ return strcmp(methodA->className, methodB->className);
+ }
+ }
+ return result;
+}
+
+int findMatch(MethodEntry** methods, int size, MethodEntry* matchThis)
+{
+ int i;
+
+ for (i = 0 ; i < size ; i++) {
+ MethodEntry* method = methods[i];
+
+ if (method != NULL && !compareMethodNamesForDiff(&method, &matchThis)) {
+// printf("%s.%s == %s.%s<br>\n", matchThis->className, matchThis->methodName,
+ // method->className, method->methodName);
+
+ return i;
+/* if (!compareMethodNames(&method, &matchThis)) {
+ return i;
+ }
+*/ }
+ }
+
+ return -1;
+}
+
+int compareDiffEntriesExculsive(const void *a, const void *b)
+{
+ int result;
+
+ const DiffEntry* entryA = (const DiffEntry*)a;
+ const DiffEntry* entryB = (const DiffEntry*)b;
+
+ if (entryA->differenceExclusive < entryB->differenceExclusive) {
+ return 1;
+ } else if (entryA->differenceExclusive > entryB->differenceExclusive) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int compareDiffEntriesInculsive(const void *a, const void *b)
+{
+ int result;
+
+ const DiffEntry* entryA = (const DiffEntry*)a;
+ const DiffEntry* entryB = (const DiffEntry*)b;
+
+ if (entryA->differenceInclusive < entryB->differenceInclusive) {
+ return 1;
+ } else if (entryA->differenceInclusive > entryB->differenceInclusive) {
+ return -1;
+ }
+
+ return 0;
+}
+
+void printMissingMethod(MethodEntry* method)
+{
+ char classBuf[HTML_BUFSIZE];
+ char methodBuf[HTML_BUFSIZE];
+ char* className;
+ char* methodName;
+
+ className = htmlEscape(method->className, classBuf, HTML_BUFSIZE);
+ methodName = htmlEscape(method->methodName, methodBuf, HTML_BUFSIZE);
+
+ if (gOptions.outputHtml) printf("<tr><td>\n");
+
+ printf("%s.%s ", className, methodName);
+ if (gOptions.outputHtml) printf("</td><td>");
+
+ printf("%lld ", method->elapsedExclusive);
+ if (gOptions.outputHtml) printf("</td><td>");
+
+ printf("%lld ", method->elapsedInclusive);
+ if (gOptions.outputHtml) printf("</td><td>");
+
+ printf("%d\n", method->numCalls[0]);
+ if (gOptions.outputHtml) printf("</td><td>\n");
+}
+
+
+void createDiff(DataKeys* d1, uint64_t sum1, DataKeys* d2, uint64_t sum2)
+{
+ MethodEntry** methods1 = parseMethodEntries(d1);
+ MethodEntry** methods2 = parseMethodEntries(d2);
+
+ // sort and assign the indicies
+ int i;
+ qsort(methods1, d1->numMethods, sizeof(MethodEntry*), compareElapsedInclusive);
+ for (i = 0; i < d1->numMethods; ++i) {
+ methods1[i]->index = i;
+ }
+
+ qsort(methods2, d2->numMethods, sizeof(MethodEntry*), compareElapsedInclusive);
+ for (i = 0; i < d2->numMethods; ++i) {
+ methods2[i]->index = i;
+ }
+
+ int max = (d1->numMethods < d2->numMethods) ? d2->numMethods : d1->numMethods;
+ max++;
+ DiffEntry* diffs = (DiffEntry*)malloc(max * sizeof(DiffEntry));
+ memset(diffs, 0, max * sizeof(DiffEntry));
+ DiffEntry* ptr = diffs;
+
+// printf("<br>d1->numMethods: %d d1->numMethods: %d<br>\n", d1->numMethods, d2->numMethods);
+
+ int matches = 0;
+
+ for (i = 0 ; i < d1->numMethods ; i++) {
+ int match = findMatch(methods2, d2->numMethods, methods1[i]);
+ if (match >= 0) {
+ ptr->method1 = methods1[i];
+ ptr->method2 = methods2[match];
+
+ uint64_t e1 = ptr->method1->elapsedExclusive;
+ uint64_t e2 = ptr->method2->elapsedExclusive;
+ if (e1 > 0) {
+ ptr->differenceExclusive = e2 - e1;
+ ptr->differenceExclusivePercentage = ((double)e2 / (double)e1) * 100.0;
+ }
+
+ uint64_t i1 = ptr->method1->elapsedInclusive;
+ uint64_t i2 = ptr->method2->elapsedInclusive;
+ if (i1 > 0) {
+ ptr->differenceInclusive = i2 - i1;
+ ptr->differenceInclusivePercentage = ((double)i2 / (double)i1) * 100.0;
+ }
+
+ // clear these out so we don't find them again and we know which ones
+ // we have left over
+ methods1[i] = NULL;
+ methods2[match] = NULL;
+ ptr++;
+
+ matches++;
+ }
+ }
+ ptr->method1 = NULL;
+ ptr->method2 = NULL;
+
+ qsort(diffs, matches, sizeof(DiffEntry), compareDiffEntriesExculsive);
+ ptr = diffs;
+
+ if (gOptions.outputHtml) {
+ printf(htmlHeader, gOptions.sortableUrl);
+ printf("<h3>Table of Contents</h3>\n");
+ printf("<ul>\n");
+ printf("<li><a href='#exclusive'>Exclusive</a>\n");
+ printf("<li><a href='#inclusive'>Inclusive</a>\n");
+ printf("</ul>\n");
+ printf("Run 1: %s<br>\n", gOptions.diffFileName);
+ printf("Run 2: %s<br>\n", gOptions.traceFileName);
+ printf("<a name=\"exclusive\"></a><h3 id=\"exclusive\">Exclusive</h3>\n");
+ printf(tableHeader, "exclusive_table");
+ }
+
+ char classBuf[HTML_BUFSIZE];
+ char methodBuf[HTML_BUFSIZE];
+ char* className;
+ char* methodName;
+
+ while (ptr->method1 != NULL && ptr->method2 != NULL) {
+ if (gOptions.outputHtml) printf("<tr><td>\n");
+
+ className = htmlEscape(ptr->method1->className, classBuf, HTML_BUFSIZE);
+ methodName = htmlEscape(ptr->method1->methodName, methodBuf, HTML_BUFSIZE);
+
+ printf("%s.%s ", className, methodName);
+ if (gOptions.outputHtml) printf("</td><td>");
+
+ printf("%lld ", ptr->method1->elapsedExclusive);
+ if (gOptions.outputHtml) printf("</td><td>");
+
+ printf("%llu ", ptr->method2->elapsedExclusive);
+ if (gOptions.outputHtml) printf("</td><td>");
+
+ printf("%lld ", ptr->differenceExclusive);
+ if (gOptions.outputHtml) printf("</td><td>");
+
+ printf("%.2f\n", ptr->differenceExclusivePercentage);
+ if (gOptions.outputHtml) printf("</td><td>\n");
+
+ printf("%d\n", ptr->method1->numCalls[0]);
+ if (gOptions.outputHtml) printf("</td><td>\n");
+
+ printf("%d\n", ptr->method2->numCalls[0]);
+ if (gOptions.outputHtml) printf("</td></tr>\n");
+
+ ptr++;
+ }
+
+ if (gOptions.outputHtml) printf("</table>\n");
+
+ if (gOptions.outputHtml) {
+ printf(htmlHeader, gOptions.sortableUrl);
+ printf("Run 1: %s<br>\n", gOptions.diffFileName);
+ printf("Run 2: %s<br>\n", gOptions.traceFileName);
+ printf("<a name=\"inclusive\"></a><h3 id=\"inculisve\">Inclusive</h3>\n");
+ printf(tableHeader, "inclusive_table");
+ }
+
+ qsort(diffs, matches, sizeof(DiffEntry), compareDiffEntriesInculsive);
+ ptr = diffs;
+
+ while (ptr->method1 != NULL && ptr->method2 != NULL) {
+ if (gOptions.outputHtml) printf("<tr><td>\n");
+
+ className = htmlEscape(ptr->method1->className, classBuf, HTML_BUFSIZE);
+ methodName = htmlEscape(ptr->method1->methodName, methodBuf, HTML_BUFSIZE);
+
+ printf("%s.%s ", className, methodName);
+ if (gOptions.outputHtml) printf("</td><td>");
+
+ printf("%lld ", ptr->method1->elapsedInclusive);
+ if (gOptions.outputHtml) printf("</td><td>");
+
+ printf("%llu ", ptr->method2->elapsedInclusive);
+ if (gOptions.outputHtml) printf("</td><td>");
+
+ printf("%lld ", ptr->differenceInclusive);
+ if (gOptions.outputHtml) printf("</td><td>");
+
+ printf("%.2f\n", ptr->differenceInclusivePercentage);
+ if (gOptions.outputHtml) printf("</td><td>\n");
+
+ printf("%d\n", ptr->method1->numCalls[0]);
+ if (gOptions.outputHtml) printf("</td><td>\n");
+
+ printf("%d\n", ptr->method2->numCalls[0]);
+ if (gOptions.outputHtml) printf("</td></tr>\n");
+
+ ptr++;
+ }
+
+ if (gOptions.outputHtml) {
+ printf("</table>\n");
+ printf("<h3>Run 1 methods not found in Run 2</h3>");
+ printf(tableHeaderMissing);
+ }
+
+ for (i = 0; i < d1->numMethods; ++i) {
+ if (methods1[i] != NULL) {
+ printMissingMethod(methods1[i]);
+ }
+ }
+
+ if (gOptions.outputHtml) {
+ printf("</table>\n");
+ printf("<h3>Run 2 methods not found in Run 1</h3>");
+ printf(tableHeaderMissing);
+ }
+
+ for (i = 0; i < d2->numMethods; ++i) {
+ if (methods2[i] != NULL) {
+ printMissingMethod(methods2[i]);
+ }
+ }
+
+ if (gOptions.outputHtml) printf("</body></html\n");
+}
+
+int usage(const char *program)
+{
+ fprintf(stderr, "usage: %s [-ho] [-s sortable] [-d trace-file-name] [-g outfile] [-f filter-file] trace-file-name\n", program);
+ fprintf(stderr, " -d trace-file-name - Diff with this trace\n");
+ fprintf(stderr, " -g outfile - Write graph to 'outfile'\n");
+ fprintf(stderr, " -f filter-file - Filter functions as specified in file\n");
+ fprintf(stderr, " -k - When writing a graph, keep the intermediate DOT file\n");
+ fprintf(stderr, " -h - Turn on HTML output\n");
+ fprintf(stderr, " -o - Dump the dmtrace file instead of profiling\n");
+ fprintf(stderr, " -s - URL base to where the sortable javascript file\n");
+ fprintf(stderr, " -t threshold - Threshold percentage for including nodes in the graph\n");
+ return 2;
+}
+
+// Returns true if there was an error
+int parseOptions(int argc, char **argv)
+{
+ while (1) {
+ int opt = getopt(argc, argv, "d:hg:kos:t:f:");
+ if (opt == -1)
+ break;
+ switch (opt) {
+ case 'd':
+ gOptions.diffFileName = optarg;
+ break;
+ case 'g':
+ gOptions.graphFileName = optarg;
+ break;
+ case 'f':
+ gOptions.filterFileName = optarg;
+ break;
+ case 'k':
+ gOptions.keepDotFile = 1;
+ break;
+ case 'h':
+ gOptions.outputHtml = 1;
+ break;
+ case 'o':
+ gOptions.dump = 1;
+ break;
+ case 's':
+ gOptions.sortableUrl = optarg;
+ break;
+ case 't':
+ gOptions.threshold = atoi(optarg);
+ break;
+ default:
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Parse args.
+ */
+int main(int argc, char** argv)
+{
+
+ gOptions.threshold = -1;
+
+ // Parse the options
+ if (parseOptions(argc, argv) || argc - optind != 1)
+ return usage(argv[0]);
+
+ gOptions.traceFileName = argv[optind];
+
+ if (gOptions.threshold < 0 || 100 <= gOptions.threshold) {
+ gOptions.threshold = 20;
+ }
+
+ if (gOptions.dump) {
+ dumpTrace();
+ return 0;
+ }
+
+ uint64_t sumThreadTime = 0;
+
+ Filter** filters = NULL;
+ if (gOptions.filterFileName != NULL) {
+ filters = parseFilters(gOptions.filterFileName);
+ }
+
+ TraceData data1;
+ memset(&data1, 0, sizeof(data1));
+ DataKeys* dataKeys = parseDataKeys(&data1, gOptions.traceFileName,
+ &sumThreadTime, filters);
+ if (dataKeys == NULL) {
+ fprintf(stderr, "Cannot read trace.\n");
+ exit(1);
+ }
+
+ if (gOptions.diffFileName != NULL) {
+ uint64_t sum2;
+ TraceData data2;
+ DataKeys* d2 = parseDataKeys(&data2, gOptions.diffFileName, &sum2, filters);
+
+ createDiff(d2, sum2, dataKeys, sumThreadTime);
+
+ freeDataKeys(d2);
+ } else {
+ MethodEntry** methods = parseMethodEntries(dataKeys);
+ profileTrace(&data1, methods, dataKeys->numMethods, sumThreadTime,
+ dataKeys->threads, dataKeys->numThreads, filters);
+ if (gOptions.graphFileName != NULL) {
+ createInclusiveProfileGraphNew(dataKeys);
+ }
+ free(methods);
+ }
+
+ freeDataKeys(dataKeys);
+
+ return 0;
+}
diff --git a/tools/dmtracedump/dmtracedump.pl b/tools/dmtracedump/dmtracedump.pl
new file mode 100755
index 0000000..6e487c6
--- /dev/null
+++ b/tools/dmtracedump/dmtracedump.pl
@@ -0,0 +1,18 @@
+#!/usr/bin/perl
+
+opendir(DIR, ".") || die "can't opendir $some_dir: $!";
+@traces = grep { /.*\.dmtrace\.data/ } readdir(DIR);
+
+foreach (@traces)
+{
+ $input = $_;
+ $input =~ s/\.data$//;
+
+ $output = "$input.html";
+
+ print("dmtracedump -h -p $input > $output\n");
+ system("dmtracedump -h -p '$input' > '$output'");
+
+}
+
+closedir DIR;
diff --git a/tools/dmtracedump/dumpdir.sh b/tools/dmtracedump/dumpdir.sh
new file mode 100644
index 0000000..81992a2
--- /dev/null
+++ b/tools/dmtracedump/dumpdir.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+FILES=`ls $1/*.data | sed "s/^\\(.*\\).data$/\\1/"`
+
+mkdir -p $2
+
+for F in $FILES
+do
+ G=$2/`echo $F | sed "s/.*\\///g"`.html
+ dmtracedump -h -p $F > $G
+done
diff --git a/tools/dmtracedump/filters b/tools/dmtracedump/filters
new file mode 100644
index 0000000..96a041c
--- /dev/null
+++ b/tools/dmtracedump/filters
@@ -0,0 +1,42 @@
+*GC
+dvmGcScanRootClassLoader
+mspace_walk_free_pages
+dvmCollectGarbageInternal
+doHeapWork
+dvmGetNextHeapWorkerObject
+GC
+GC2
+GC3
+*Net
+setsockopt
++sys_setsockopt [kernel]
+socketSelect
+send
+recv
+sendto
+recvfrom
++sys_sendto [kernel]
++sys_recvfrom [kernel]
+org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnection
+android.net.http.ConnectionThread
+PlainSocketImpl
+WebCore::HTMLTokenizer
+*IO
+select
++sys_select [kernel]
+*DB
+android.database.sqlite.SQLiteOpenHelper
+android.database.sqlite.SQLiteQueryBuilder
+android.database.sqlite.SQLiteDatabase
+android.database.sqlite.SQLiteDirectCursorDriver
+android.database.sqlite.SQLiteQuery
+android.database.sqlite.SQLiteProgram
+android.database.AbstractCursor
+android.database.sqlite.SQLiteCursor
+*UI
+android.view.View.draw()
+android.view.ViewGroup
+*Sync
++java.lang.Object.wait()
+*Useless
++android.widget.ProgressBar
diff --git a/tools/dmtracedump/tests/filters/run_tests.sh b/tools/dmtracedump/tests/filters/run_tests.sh
new file mode 100755
index 0000000..cdf87cb
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/run_tests.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+failed=0
+for file in $(find $1 -type f -iname 'test*'); do
+ case $file in
+ *testFilters) continue; ;;
+ *Expected) continue; ;;
+ *Trace) continue; ;;
+ *.html) continue; ;;
+ esac
+
+ echo "Running test for $file"
+
+# create_test_dmtrace $file tmp.trace
+ dmtracedump -f testFilters -h "$file"Trace > tmp.html 2> /dev/null
+
+ output=`diff tmp.html "$file"Expected 2>&1`
+ if [ ${#output} -eq 0 ]
+ then
+ echo " OK"
+ else
+ echo " Test failed: $output"
+ failed=`expr $failed + 1`
+ fi
+
+done
+
+rm tmp.trace
+rm tmp.html
+
+if [ $failed -gt 0 ]
+then
+ echo "$failed test(s) failed"
+else
+ echo "All tests passed successfully"
+fi
diff --git a/tools/dmtracedump/tests/filters/testFilters b/tools/dmtracedump/tests/filters/testFilters
new file mode 100644
index 0000000..2c3edb6
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testFilters
@@ -0,0 +1,9 @@
+*FirstFilter
++A.m(),B.m()
++C.m()
++R.m(),S.m()
+*SecondFilter
++D.m(),E.m()
++F.m()
+*RepeatedFilter
++R.m(),S.m()
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterDiffKeys b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterDiffKeys
new file mode 100644
index 0000000..b4367c6
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterDiffKeys
@@ -0,0 +1,19 @@
+# ____ ____ _________
+# __|A |___________|B |_____|Z |_______
+#
+# ___________ ____ ____
+# _______|Z |_____|D |_________|E |__
+#
+#
+0 1 A
+2 1 A
+0 2 Z
+4 2 Z
+2 1 B
+4 1 B
+4 2 D
+6 2 D
+4 1 Z
+8 1 Z
+6 2 E
+8 2 E
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterDiffKeysExpected b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterDiffKeysExpected
new file mode 100644
index 0000000..7fa789a
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterDiffKeysExpected
@@ -0,0 +1,232 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+ obj=document.getElementById(item);
+ visible=(obj.style.display!="none" && obj.style.display!="");
+ key=document.getElementById("x" + item);
+ if (visible) {
+ obj.style.display="none";
+ key.innerHTML="+";
+ } else {
+ obj.style.display="block";
+ key.innerHTML="-";
+ }
+}
+function onMouseOver(obj) {
+ obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+ obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+ <li><a href="#exclusive">Exclusive profile</a></li>
+ <li><a href="#inclusive">Inclusive profile</a></li>
+ <li><a href="#thread">Thread profile</a></li>
+ <li><a href="#class">Class/method profile</a></li>
+ <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 16
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+ Usecs self % sum % Method
+ 8 50.00 50.00 <a href="#m1">[1]</a> Z.m ()
+ 2 12.50 62.50 <a href="#m2">[2]</a> A.m ()
+ 2 12.50 75.00 <a href="#m3">[3]</a> B.m ()
+ 2 12.50 87.50 <a href="#m4">[4]</a> D.m ()
+ 2 12.50 100.00 <a href="#m5">[5]</a> E.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index %/total %/self index calls usecs name
+<a name="m0"></a>----------------------------------------------------
+[0] 100.0% 0+0 16 (toplevel)
+ 0.0% excl 0
+ 50.0% <a href="#m1">[1]</a> 2/2 8 Z.m ()
+ 12.5% <a href="#m2">[2]</a> 1/1 2 A.m ()
+ 12.5% <a href="#m3">[3]</a> 1/1 2 B.m ()
+ 12.5% <a href="#m4">[4]</a> 1/1 2 D.m ()
+ 12.5% <a href="#m5">[5]</a> 1/1 2 E.m ()
+<a name="m1"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 2/2 8 (toplevel)
+[1] 50.0% 2+0 8 Z.m ()
+ 100.0% excl 8
+<a name="m2"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[2] 12.5% 1+0 2 A.m ()
+ 100.0% excl 2
+<a name="m3"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[3] 12.5% 1+0 2 B.m ()
+ 100.0% excl 2
+<a name="m4"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[4] 12.5% 1+0 2 D.m ()
+ 100.0% excl 2
+<a name="m5"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[5] 12.5% 1+0 2 E.m ()
+ 100.0% excl 2
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+ Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
+ 8 50.00 50.00 50.00 0.00 0.00 1 main
+ 8 50.00 100.00 0.00 50.00 0.00 2 foo
+ 0 0.00 100.00 nan nan nan 3 bar
+ 0 0.00 100.00 nan nan nan 4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 8 ( 50.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 8 100.00 50.00 main
+ 0 0.00 50.00 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 8 ( 50.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 0 0.00 50.00 main
+ 8 100.00 50.00 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;62.5 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d3')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd3">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;87.5 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; D</div>
+<div class="parent" id="d3">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d4')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd4">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; E</div>
+<div class="parent" id="d4">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m5">[5]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;16 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;Z.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;62.5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;B.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;87.5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;D.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m5">[5]</a>&nbsp;E.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterDiffKeysTrace b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterDiffKeysTrace
new file mode 100644
index 0000000..8bc74ff
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterDiffKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterSameKeys b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterSameKeys
new file mode 100644
index 0000000..76cdea7
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterSameKeys
@@ -0,0 +1,19 @@
+# ____ ____ _________
+# __|R |___________|S |_____|Z |_______
+#
+# ___________ ____ ____
+# _______|Z |_____|R |_________|S |__
+#
+#
+0 1 R
+2 1 R
+0 2 Z
+4 2 Z
+2 1 S
+4 1 S
+4 2 R
+6 2 R
+4 1 Z
+8 1 Z
+6 2 S
+8 2 S
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterSameKeysExpected b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterSameKeysExpected
new file mode 100644
index 0000000..5672826
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterSameKeysExpected
@@ -0,0 +1,210 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+ obj=document.getElementById(item);
+ visible=(obj.style.display!="none" && obj.style.display!="");
+ key=document.getElementById("x" + item);
+ if (visible) {
+ obj.style.display="none";
+ key.innerHTML="+";
+ } else {
+ obj.style.display="block";
+ key.innerHTML="-";
+ }
+}
+function onMouseOver(obj) {
+ obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+ obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+ <li><a href="#exclusive">Exclusive profile</a></li>
+ <li><a href="#inclusive">Inclusive profile</a></li>
+ <li><a href="#thread">Thread profile</a></li>
+ <li><a href="#class">Class/method profile</a></li>
+ <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 16
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+ Usecs self % sum % Method
+ 8 50.00 50.00 <a href="#m1">[1]</a> Z.m ()
+ 4 25.00 75.00 <a href="#m2">[2]</a> R.m ()
+ 4 25.00 100.00 <a href="#m3">[3]</a> S.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index %/total %/self index calls usecs name
+<a name="m0"></a>----------------------------------------------------
+[0] 100.0% 0+0 16 (toplevel)
+ 0.0% excl 0
+ 50.0% <a href="#m1">[1]</a> 2/2 8 Z.m ()
+ 25.0% <a href="#m2">[2]</a> 2/2 4 R.m ()
+ 25.0% <a href="#m3">[3]</a> 2/2 4 S.m ()
+<a name="m1"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 2/2 8 (toplevel)
+[1] 50.0% 2+0 8 Z.m ()
+ 100.0% excl 8
+<a name="m2"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
+[2] 25.0% 2+0 4 R.m ()
+ 100.0% excl 4
+<a name="m3"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
+[3] 25.0% 2+0 4 S.m ()
+ 100.0% excl 4
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+ Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
+ 8 50.00 50.00 50.00 0.00 50.00 1 main
+ 8 50.00 100.00 50.00 0.00 50.00 2 foo
+ 0 0.00 100.00 nan nan nan 3 bar
+ 0 0.00 100.00 nan nan nan 4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 16 (100.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 8 50.00 50.00 main
+ 8 50.00 50.00 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 16 (100.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 8 50.00 50.00 main
+ 8 50.00 50.00 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; R</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; S</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;16 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;Z.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;R.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;S.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterSameKeysTrace b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterSameKeysTrace
new file mode 100644
index 0000000..9ec7378
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadDiffFilterSameKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterDiffKeys b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterDiffKeys
new file mode 100644
index 0000000..d1bcdd3
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterDiffKeys
@@ -0,0 +1,19 @@
+# ____ ____ _________
+# __|A |___________|B |_____|Z |_______
+#
+# ___________ ____ ____
+# _______|Z |_____|R |_________|S |__
+#
+#
+0 1 A
+2 1 A
+0 2 Z
+4 2 Z
+2 1 B
+4 1 B
+4 2 R
+6 2 R
+4 1 Z
+8 1 Z
+6 2 S
+8 2 S
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterDiffKeysExpected b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterDiffKeysExpected
new file mode 100644
index 0000000..ef56af5
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterDiffKeysExpected
@@ -0,0 +1,232 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+ obj=document.getElementById(item);
+ visible=(obj.style.display!="none" && obj.style.display!="");
+ key=document.getElementById("x" + item);
+ if (visible) {
+ obj.style.display="none";
+ key.innerHTML="+";
+ } else {
+ obj.style.display="block";
+ key.innerHTML="-";
+ }
+}
+function onMouseOver(obj) {
+ obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+ obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+ <li><a href="#exclusive">Exclusive profile</a></li>
+ <li><a href="#inclusive">Inclusive profile</a></li>
+ <li><a href="#thread">Thread profile</a></li>
+ <li><a href="#class">Class/method profile</a></li>
+ <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 16
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+ Usecs self % sum % Method
+ 8 50.00 50.00 <a href="#m1">[1]</a> Z.m ()
+ 2 12.50 62.50 <a href="#m2">[2]</a> A.m ()
+ 2 12.50 75.00 <a href="#m3">[3]</a> B.m ()
+ 2 12.50 87.50 <a href="#m4">[4]</a> R.m ()
+ 2 12.50 100.00 <a href="#m5">[5]</a> S.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index %/total %/self index calls usecs name
+<a name="m0"></a>----------------------------------------------------
+[0] 100.0% 0+0 16 (toplevel)
+ 0.0% excl 0
+ 50.0% <a href="#m1">[1]</a> 2/2 8 Z.m ()
+ 12.5% <a href="#m2">[2]</a> 1/1 2 A.m ()
+ 12.5% <a href="#m3">[3]</a> 1/1 2 B.m ()
+ 12.5% <a href="#m4">[4]</a> 1/1 2 R.m ()
+ 12.5% <a href="#m5">[5]</a> 1/1 2 S.m ()
+<a name="m1"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 2/2 8 (toplevel)
+[1] 50.0% 2+0 8 Z.m ()
+ 100.0% excl 8
+<a name="m2"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[2] 12.5% 1+0 2 A.m ()
+ 100.0% excl 2
+<a name="m3"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[3] 12.5% 1+0 2 B.m ()
+ 100.0% excl 2
+<a name="m4"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[4] 12.5% 1+0 2 R.m ()
+ 100.0% excl 2
+<a name="m5"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[5] 12.5% 1+0 2 S.m ()
+ 100.0% excl 2
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+ Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
+ 8 50.00 50.00 50.00 0.00 0.00 1 main
+ 8 50.00 100.00 50.00 0.00 50.00 2 foo
+ 0 0.00 100.00 nan nan nan 3 bar
+ 0 0.00 100.00 nan nan nan 4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 16 (100.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 8 50.00 50.00 main
+ 8 50.00 50.00 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 8 ( 50.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 0 0.00 50.00 main
+ 8 100.00 50.00 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;62.5 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d3')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd3">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;87.5 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; R</div>
+<div class="parent" id="d3">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d4')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd4">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; S</div>
+<div class="parent" id="d4">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m5">[5]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;16 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;Z.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;62.5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;B.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;87.5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;R.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m5">[5]</a>&nbsp;S.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterDiffKeysTrace b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterDiffKeysTrace
new file mode 100644
index 0000000..0559a6a
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterDiffKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterSameKeys b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterSameKeys
new file mode 100644
index 0000000..2bb68d7
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterSameKeys
@@ -0,0 +1,19 @@
+# ____ ____ _________
+# __|A |___________|B |_____|Z |_______
+#
+# ___________ ____ ____
+# _______|Z |_____|A |_________|B |__
+#
+#
+0 1 A
+2 1 A
+0 2 Z
+4 2 Z
+2 1 B
+4 1 B
+4 2 A
+6 2 A
+4 1 Z
+8 1 Z
+6 2 B
+8 2 B
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterSameKeysExpected b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterSameKeysExpected
new file mode 100644
index 0000000..50b2b98
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterSameKeysExpected
@@ -0,0 +1,203 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+ obj=document.getElementById(item);
+ visible=(obj.style.display!="none" && obj.style.display!="");
+ key=document.getElementById("x" + item);
+ if (visible) {
+ obj.style.display="none";
+ key.innerHTML="+";
+ } else {
+ obj.style.display="block";
+ key.innerHTML="-";
+ }
+}
+function onMouseOver(obj) {
+ obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+ obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+ <li><a href="#exclusive">Exclusive profile</a></li>
+ <li><a href="#inclusive">Inclusive profile</a></li>
+ <li><a href="#thread">Thread profile</a></li>
+ <li><a href="#class">Class/method profile</a></li>
+ <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 16
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+ Usecs self % sum % Method
+ 8 50.00 50.00 <a href="#m1">[1]</a> Z.m ()
+ 4 25.00 75.00 <a href="#m2">[2]</a> A.m ()
+ 4 25.00 100.00 <a href="#m3">[3]</a> B.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index %/total %/self index calls usecs name
+<a name="m0"></a>----------------------------------------------------
+[0] 100.0% 0+0 16 (toplevel)
+ 0.0% excl 0
+ 50.0% <a href="#m1">[1]</a> 2/2 8 Z.m ()
+ 25.0% <a href="#m2">[2]</a> 2/2 4 A.m ()
+ 25.0% <a href="#m3">[3]</a> 2/2 4 B.m ()
+<a name="m1"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 2/2 8 (toplevel)
+[1] 50.0% 2+0 8 Z.m ()
+ 100.0% excl 8
+<a name="m2"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
+[2] 25.0% 2+0 4 A.m ()
+ 100.0% excl 4
+<a name="m3"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
+[3] 25.0% 2+0 4 B.m ()
+ 100.0% excl 4
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+ Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
+ 8 50.00 50.00 50.00 0.00 0.00 1 main
+ 8 50.00 100.00 50.00 0.00 0.00 2 foo
+ 0 0.00 100.00 nan nan nan 3 bar
+ 0 0.00 100.00 nan nan nan 4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 16 (100.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 8 50.00 50.00 main
+ 8 50.00 50.00 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;16 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;Z.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;B.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterSameKeysTrace b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterSameKeysTrace
new file mode 100644
index 0000000..f113fcf
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointCrossThreadSameFilterSameKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterDiffKeys b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterDiffKeys
new file mode 100644
index 0000000..e7456c1
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterDiffKeys
@@ -0,0 +1,17 @@
+# ____ ____ ____ ________ ____ ____ ____
+# __|A ||Z ||B ||Z ||D ||Z ||E |__
+#
+0 1 A
+2 1 A
+2 1 Z
+4 1 Z
+4 1 B
+6 1 B
+6 1 Z
+10 1 Z
+10 1 D
+12 1 D
+12 1 Z
+14 1 Z
+14 1 E
+16 1 E
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterDiffKeysExpected b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterDiffKeysExpected
new file mode 100644
index 0000000..9349375
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterDiffKeysExpected
@@ -0,0 +1,232 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+ obj=document.getElementById(item);
+ visible=(obj.style.display!="none" && obj.style.display!="");
+ key=document.getElementById("x" + item);
+ if (visible) {
+ obj.style.display="none";
+ key.innerHTML="+";
+ } else {
+ obj.style.display="block";
+ key.innerHTML="-";
+ }
+}
+function onMouseOver(obj) {
+ obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+ obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+ <li><a href="#exclusive">Exclusive profile</a></li>
+ <li><a href="#inclusive">Inclusive profile</a></li>
+ <li><a href="#thread">Thread profile</a></li>
+ <li><a href="#class">Class/method profile</a></li>
+ <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 16
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+ Usecs self % sum % Method
+ 8 50.00 50.00 <a href="#m1">[1]</a> Z.m ()
+ 2 12.50 62.50 <a href="#m2">[2]</a> A.m ()
+ 2 12.50 75.00 <a href="#m3">[3]</a> B.m ()
+ 2 12.50 87.50 <a href="#m4">[4]</a> D.m ()
+ 2 12.50 100.00 <a href="#m5">[5]</a> E.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index %/total %/self index calls usecs name
+<a name="m0"></a>----------------------------------------------------
+[0] 100.0% 0+0 16 (toplevel)
+ 0.0% excl 0
+ 50.0% <a href="#m1">[1]</a> 3/3 8 Z.m ()
+ 12.5% <a href="#m2">[2]</a> 1/1 2 A.m ()
+ 12.5% <a href="#m3">[3]</a> 1/1 2 B.m ()
+ 12.5% <a href="#m4">[4]</a> 1/1 2 D.m ()
+ 12.5% <a href="#m5">[5]</a> 1/1 2 E.m ()
+<a name="m1"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 3/3 8 (toplevel)
+[1] 50.0% 3+0 8 Z.m ()
+ 100.0% excl 8
+<a name="m2"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[2] 12.5% 1+0 2 A.m ()
+ 100.0% excl 2
+<a name="m3"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[3] 12.5% 1+0 2 B.m ()
+ 100.0% excl 2
+<a name="m4"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[4] 12.5% 1+0 2 D.m ()
+ 100.0% excl 2
+<a name="m5"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[5] 12.5% 1+0 2 E.m ()
+ 100.0% excl 2
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+ Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
+ 16 100.00 100.00 37.50 37.50 0.00 1 main
+ 0 0.00 100.00 nan nan nan 2 foo
+ 0 0.00 100.00 nan nan nan 3 bar
+ 0 0.00 100.00 nan nan nan 4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 6 ( 37.50% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 6 100.00 100.00 main
+ 0 0.00 0.00 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 6 ( 37.50% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 6 100.00 100.00 main
+ 0 0.00 0.00 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;62.5 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d3')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd3">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;87.5 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; D</div>
+<div class="parent" id="d3">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d4')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd4">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; E</div>
+<div class="parent" id="d4">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m5">[5]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;16 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;7+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;Z.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;62.5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;B.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;87.5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;D.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m5">[5]</a>&nbsp;E.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterDiffKeysTrace b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterDiffKeysTrace
new file mode 100644
index 0000000..09983ba
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterDiffKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterSameKeys b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterSameKeys
new file mode 100644
index 0000000..b51f81e
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterSameKeys
@@ -0,0 +1,17 @@
+# ____ ____ ____ ________ ____ ____ ____
+# __|R ||Z ||S ||Z ||R ||Z ||S |__
+#
+0 1 R
+2 1 R
+2 1 Z
+4 1 Z
+4 1 S
+6 1 S
+6 1 Z
+10 1 Z
+10 1 R
+12 1 R
+12 1 Z
+14 1 Z
+14 1 S
+16 1 S
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterSameKeysExpected b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterSameKeysExpected
new file mode 100644
index 0000000..41f9625
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterSameKeysExpected
@@ -0,0 +1,210 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+ obj=document.getElementById(item);
+ visible=(obj.style.display!="none" && obj.style.display!="");
+ key=document.getElementById("x" + item);
+ if (visible) {
+ obj.style.display="none";
+ key.innerHTML="+";
+ } else {
+ obj.style.display="block";
+ key.innerHTML="-";
+ }
+}
+function onMouseOver(obj) {
+ obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+ obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+ <li><a href="#exclusive">Exclusive profile</a></li>
+ <li><a href="#inclusive">Inclusive profile</a></li>
+ <li><a href="#thread">Thread profile</a></li>
+ <li><a href="#class">Class/method profile</a></li>
+ <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 16
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+ Usecs self % sum % Method
+ 8 50.00 50.00 <a href="#m1">[1]</a> Z.m ()
+ 4 25.00 75.00 <a href="#m2">[2]</a> R.m ()
+ 4 25.00 100.00 <a href="#m3">[3]</a> S.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index %/total %/self index calls usecs name
+<a name="m0"></a>----------------------------------------------------
+[0] 100.0% 0+0 16 (toplevel)
+ 0.0% excl 0
+ 50.0% <a href="#m1">[1]</a> 3/3 8 Z.m ()
+ 25.0% <a href="#m2">[2]</a> 2/2 4 R.m ()
+ 25.0% <a href="#m3">[3]</a> 2/2 4 S.m ()
+<a name="m1"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 3/3 8 (toplevel)
+[1] 50.0% 3+0 8 Z.m ()
+ 100.0% excl 8
+<a name="m2"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
+[2] 25.0% 2+0 4 R.m ()
+ 100.0% excl 4
+<a name="m3"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
+[3] 25.0% 2+0 4 S.m ()
+ 100.0% excl 4
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+ Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
+ 16 100.00 100.00 75.00 0.00 75.00 1 main
+ 0 0.00 100.00 nan nan nan 2 foo
+ 0 0.00 100.00 nan nan nan 3 bar
+ 0 0.00 100.00 nan nan nan 4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 12 ( 75.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 12 100.00 100.00 main
+ 0 0.00 0.00 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 12 ( 75.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 12 100.00 100.00 main
+ 0 0.00 0.00 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; R</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; S</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;16 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;7+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;Z.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;R.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;S.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterSameKeysTrace b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterSameKeysTrace
new file mode 100644
index 0000000..2cccf07
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadDiffFilterSameKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterDiffKeys b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterDiffKeys
new file mode 100644
index 0000000..d4e41a4
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterDiffKeys
@@ -0,0 +1,16 @@
+# ____
+# ____ ____ ____ ________ ____|Z |____
+# __|A ||Z ||B ||Z ||C |__
+#
+0 1 A
+2 1 A
+2 1 Z
+4 1 Z
+4 1 B
+6 1 B
+6 1 Z
+10 1 Z
+10 1 C
+12 1 Z
+14 1 Z
+16 1 C
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterDiffKeysExpected b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterDiffKeysExpected
new file mode 100644
index 0000000..d81cccc
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterDiffKeysExpected
@@ -0,0 +1,216 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+ obj=document.getElementById(item);
+ visible=(obj.style.display!="none" && obj.style.display!="");
+ key=document.getElementById("x" + item);
+ if (visible) {
+ obj.style.display="none";
+ key.innerHTML="+";
+ } else {
+ obj.style.display="block";
+ key.innerHTML="-";
+ }
+}
+function onMouseOver(obj) {
+ obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+ obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+ <li><a href="#exclusive">Exclusive profile</a></li>
+ <li><a href="#inclusive">Inclusive profile</a></li>
+ <li><a href="#thread">Thread profile</a></li>
+ <li><a href="#class">Class/method profile</a></li>
+ <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 16
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+ Usecs self % sum % Method
+ 8 50.00 50.00 <a href="#m1">[1]</a> Z.m ()
+ 4 25.00 75.00 <a href="#m2">[2]</a> C.m ()
+ 2 12.50 87.50 <a href="#m3">[3]</a> A.m ()
+ 2 12.50 100.00 <a href="#m4">[4]</a> B.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index %/total %/self index calls usecs name
+<a name="m0"></a>----------------------------------------------------
+[0] 100.0% 0+0 16 (toplevel)
+ 0.0% excl 0
+ 37.5% <a href="#m2">[2]</a> 1/1 6 C.m ()
+ 37.5% <a href="#m1">[1]</a> 2/3 6 Z.m ()
+ 12.5% <a href="#m3">[3]</a> 1/1 2 A.m ()
+ 12.5% <a href="#m4">[4]</a> 1/1 2 B.m ()
+<a name="m1"></a>----------------------------------------------------
+ 75.0% <a href="#m0">[0]</a> 2/3 6 (toplevel)
+ 25.0% <a href="#m2">[2]</a> 1/3 2 C.m ()
+[1] 50.0% 3+0 8 Z.m ()
+ 100.0% excl 8
+<a name="m2"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 6 (toplevel)
+[2] 37.5% 1+0 6 C.m ()
+ 66.7% excl 4
+ 33.3% <a href="#m1">[1]</a> 1/3 2 Z.m ()
+<a name="m3"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[3] 12.5% 1+0 2 A.m ()
+ 100.0% excl 2
+<a name="m4"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[4] 12.5% 1+0 2 B.m ()
+ 100.0% excl 2
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+ Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
+ 16 100.00 100.00 75.00 0.00 0.00 1 main
+ 0 0.00 100.00 nan nan nan 2 foo
+ 0 0.00 100.00 nan nan nan 3 bar
+ 0 0.00 100.00 nan nan nan 4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 12 ( 75.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 12 100.00 100.00 main
+ 0 0.00 0.00 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; C</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;87.5 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d3')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd3">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d3">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;16 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;Z.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;C.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;87.5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;B.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterDiffKeysTrace b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterDiffKeysTrace
new file mode 100644
index 0000000..3f61656
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterDiffKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterSameKeys b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterSameKeys
new file mode 100644
index 0000000..0b3377d
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterSameKeys
@@ -0,0 +1,17 @@
+# ____ ____ ____ ________ ____ ____ ____
+# __|A ||Z ||B ||Z ||A ||Z ||B |__
+#
+0 1 A
+2 1 A
+2 1 Z
+4 1 Z
+4 1 B
+6 1 B
+6 1 Z
+10 1 Z
+10 1 A
+12 1 A
+12 1 Z
+14 1 Z
+14 1 B
+16 1 B
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterSameKeysExpected b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterSameKeysExpected
new file mode 100644
index 0000000..aa476b3
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterSameKeysExpected
@@ -0,0 +1,203 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+ obj=document.getElementById(item);
+ visible=(obj.style.display!="none" && obj.style.display!="");
+ key=document.getElementById("x" + item);
+ if (visible) {
+ obj.style.display="none";
+ key.innerHTML="+";
+ } else {
+ obj.style.display="block";
+ key.innerHTML="-";
+ }
+}
+function onMouseOver(obj) {
+ obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+ obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+ <li><a href="#exclusive">Exclusive profile</a></li>
+ <li><a href="#inclusive">Inclusive profile</a></li>
+ <li><a href="#thread">Thread profile</a></li>
+ <li><a href="#class">Class/method profile</a></li>
+ <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 16
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+ Usecs self % sum % Method
+ 8 50.00 50.00 <a href="#m1">[1]</a> Z.m ()
+ 4 25.00 75.00 <a href="#m2">[2]</a> A.m ()
+ 4 25.00 100.00 <a href="#m3">[3]</a> B.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index %/total %/self index calls usecs name
+<a name="m0"></a>----------------------------------------------------
+[0] 100.0% 0+0 16 (toplevel)
+ 0.0% excl 0
+ 50.0% <a href="#m1">[1]</a> 3/3 8 Z.m ()
+ 25.0% <a href="#m2">[2]</a> 2/2 4 A.m ()
+ 25.0% <a href="#m3">[3]</a> 2/2 4 B.m ()
+<a name="m1"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 3/3 8 (toplevel)
+[1] 50.0% 3+0 8 Z.m ()
+ 100.0% excl 8
+<a name="m2"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
+[2] 25.0% 2+0 4 A.m ()
+ 100.0% excl 4
+<a name="m3"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
+[3] 25.0% 2+0 4 B.m ()
+ 100.0% excl 4
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+ Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
+ 16 100.00 100.00 75.00 0.00 0.00 1 main
+ 0 0.00 100.00 nan nan nan 2 foo
+ 0 0.00 100.00 nan nan nan 3 bar
+ 0 0.00 100.00 nan nan nan 4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 12 ( 75.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 12 100.00 100.00 main
+ 0 0.00 0.00 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;16 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;7+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;Z.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;B.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterSameKeysTrace b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterSameKeysTrace
new file mode 100644
index 0000000..c6ddbe5
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingDisjointSingleThreadSameFilterSameKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterDiffKeys b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterDiffKeys
new file mode 100644
index 0000000..d87ac81
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterDiffKeys
@@ -0,0 +1,19 @@
+# ____ ____ ________
+# __|A |_____________________|B ||Z |__
+#
+# ____ ________ ____
+# _______|D ||Z ||E |______________
+#
+#
+0 1 A
+2 1 A
+0 2 D
+2 2 D
+2 2 Z
+6 2 Z
+6 2 E
+8 2 E
+2 1 B
+4 1 B
+4 1 Z
+8 1 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterDiffKeysExpected b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterDiffKeysExpected
new file mode 100644
index 0000000..a97f25c
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterDiffKeysExpected
@@ -0,0 +1,232 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+ obj=document.getElementById(item);
+ visible=(obj.style.display!="none" && obj.style.display!="");
+ key=document.getElementById("x" + item);
+ if (visible) {
+ obj.style.display="none";
+ key.innerHTML="+";
+ } else {
+ obj.style.display="block";
+ key.innerHTML="-";
+ }
+}
+function onMouseOver(obj) {
+ obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+ obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+ <li><a href="#exclusive">Exclusive profile</a></li>
+ <li><a href="#inclusive">Inclusive profile</a></li>
+ <li><a href="#thread">Thread profile</a></li>
+ <li><a href="#class">Class/method profile</a></li>
+ <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 16
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+ Usecs self % sum % Method
+ 8 50.00 50.00 <a href="#m1">[1]</a> Z.m ()
+ 2 12.50 62.50 <a href="#m2">[2]</a> A.m ()
+ 2 12.50 75.00 <a href="#m3">[3]</a> B.m ()
+ 2 12.50 87.50 <a href="#m4">[4]</a> D.m ()
+ 2 12.50 100.00 <a href="#m5">[5]</a> E.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index %/total %/self index calls usecs name
+<a name="m0"></a>----------------------------------------------------
+[0] 100.0% 0+0 16 (toplevel)
+ 0.0% excl 0
+ 50.0% <a href="#m1">[1]</a> 2/2 8 Z.m ()
+ 12.5% <a href="#m2">[2]</a> 1/1 2 A.m ()
+ 12.5% <a href="#m3">[3]</a> 1/1 2 B.m ()
+ 12.5% <a href="#m4">[4]</a> 1/1 2 D.m ()
+ 12.5% <a href="#m5">[5]</a> 1/1 2 E.m ()
+<a name="m1"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 2/2 8 (toplevel)
+[1] 50.0% 2+0 8 Z.m ()
+ 100.0% excl 8
+<a name="m2"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[2] 12.5% 1+0 2 A.m ()
+ 100.0% excl 2
+<a name="m3"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[3] 12.5% 1+0 2 B.m ()
+ 100.0% excl 2
+<a name="m4"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[4] 12.5% 1+0 2 D.m ()
+ 100.0% excl 2
+<a name="m5"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[5] 12.5% 1+0 2 E.m ()
+ 100.0% excl 2
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+ Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
+ 8 50.00 50.00 50.00 0.00 0.00 1 main
+ 8 50.00 100.00 0.00 100.00 0.00 2 foo
+ 0 0.00 100.00 nan nan nan 3 bar
+ 0 0.00 100.00 nan nan nan 4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 12 ( 75.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 12 100.00 33.33 main
+ 0 0.00 66.67 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 8 ( 50.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 0 0.00 0.00 main
+ 8 100.00 100.00 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;62.5 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d3')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd3">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;87.5 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; D</div>
+<div class="parent" id="d3">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d4')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd4">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; E</div>
+<div class="parent" id="d4">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m5">[5]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;16 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;Z.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;62.5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;B.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;87.5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;D.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m5">[5]</a>&nbsp;E.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterDiffKeysTrace b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterDiffKeysTrace
new file mode 100644
index 0000000..832bbfc
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterDiffKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterSameKeys b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterSameKeys
new file mode 100644
index 0000000..82ab142
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterSameKeys
@@ -0,0 +1,19 @@
+# ____ ____ ________
+# __|R |_____________________|S ||Z |__
+#
+# ____ ________ ____
+# _______|R ||Z ||S |______________
+#
+#
+0 1 R
+2 1 R
+0 2 R
+2 2 R
+2 2 Z
+6 2 Z
+6 2 S
+8 2 S
+2 1 S
+4 1 S
+4 1 Z
+8 1 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterSameKeysExpected b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterSameKeysExpected
new file mode 100644
index 0000000..623478e
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterSameKeysExpected
@@ -0,0 +1,210 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+ obj=document.getElementById(item);
+ visible=(obj.style.display!="none" && obj.style.display!="");
+ key=document.getElementById("x" + item);
+ if (visible) {
+ obj.style.display="none";
+ key.innerHTML="+";
+ } else {
+ obj.style.display="block";
+ key.innerHTML="-";
+ }
+}
+function onMouseOver(obj) {
+ obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+ obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+ <li><a href="#exclusive">Exclusive profile</a></li>
+ <li><a href="#inclusive">Inclusive profile</a></li>
+ <li><a href="#thread">Thread profile</a></li>
+ <li><a href="#class">Class/method profile</a></li>
+ <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 16
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+ Usecs self % sum % Method
+ 8 50.00 50.00 <a href="#m1">[1]</a> Z.m ()
+ 4 25.00 75.00 <a href="#m2">[2]</a> R.m ()
+ 4 25.00 100.00 <a href="#m3">[3]</a> S.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index %/total %/self index calls usecs name
+<a name="m0"></a>----------------------------------------------------
+[0] 100.0% 0+0 16 (toplevel)
+ 0.0% excl 0
+ 50.0% <a href="#m1">[1]</a> 2/2 8 Z.m ()
+ 25.0% <a href="#m2">[2]</a> 2/2 4 R.m ()
+ 25.0% <a href="#m3">[3]</a> 2/2 4 S.m ()
+<a name="m1"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 2/2 8 (toplevel)
+[1] 50.0% 2+0 8 Z.m ()
+ 100.0% excl 8
+<a name="m2"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
+[2] 25.0% 2+0 4 R.m ()
+ 100.0% excl 4
+<a name="m3"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
+[3] 25.0% 2+0 4 S.m ()
+ 100.0% excl 4
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+ Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
+ 8 50.00 50.00 50.00 0.00 50.00 1 main
+ 8 50.00 100.00 100.00 0.00 100.00 2 foo
+ 0 0.00 100.00 nan nan nan 3 bar
+ 0 0.00 100.00 nan nan nan 4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 12 ( 75.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 12 100.00 33.33 main
+ 8 66.67 66.67 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 12 ( 75.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 12 100.00 33.33 main
+ 8 66.67 66.67 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; R</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; S</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;16 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;Z.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;R.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;S.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterSameKeysTrace b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterSameKeysTrace
new file mode 100644
index 0000000..371f150
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadDiffFilterSameKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterDiffKeys b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterDiffKeys
new file mode 100644
index 0000000..511543f
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterDiffKeys
@@ -0,0 +1,19 @@
+# ____ ____ ________
+# __|A |_____________________|B ||Z |__
+#
+# ____ ________ ____
+# _______|R ||Z ||S |______________
+#
+#
+0 1 A
+2 1 A
+0 2 R
+2 2 R
+2 2 Z
+6 2 Z
+6 2 S
+8 2 S
+2 1 B
+4 1 B
+4 1 Z
+8 1 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterDiffKeysExpected b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterDiffKeysExpected
new file mode 100644
index 0000000..1193f5f
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterDiffKeysExpected
@@ -0,0 +1,232 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+ obj=document.getElementById(item);
+ visible=(obj.style.display!="none" && obj.style.display!="");
+ key=document.getElementById("x" + item);
+ if (visible) {
+ obj.style.display="none";
+ key.innerHTML="+";
+ } else {
+ obj.style.display="block";
+ key.innerHTML="-";
+ }
+}
+function onMouseOver(obj) {
+ obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+ obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+ <li><a href="#exclusive">Exclusive profile</a></li>
+ <li><a href="#inclusive">Inclusive profile</a></li>
+ <li><a href="#thread">Thread profile</a></li>
+ <li><a href="#class">Class/method profile</a></li>
+ <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 16
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+ Usecs self % sum % Method
+ 8 50.00 50.00 <a href="#m1">[1]</a> Z.m ()
+ 2 12.50 62.50 <a href="#m2">[2]</a> A.m ()
+ 2 12.50 75.00 <a href="#m3">[3]</a> B.m ()
+ 2 12.50 87.50 <a href="#m4">[4]</a> R.m ()
+ 2 12.50 100.00 <a href="#m5">[5]</a> S.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index %/total %/self index calls usecs name
+<a name="m0"></a>----------------------------------------------------
+[0] 100.0% 0+0 16 (toplevel)
+ 0.0% excl 0
+ 50.0% <a href="#m1">[1]</a> 2/2 8 Z.m ()
+ 12.5% <a href="#m2">[2]</a> 1/1 2 A.m ()
+ 12.5% <a href="#m3">[3]</a> 1/1 2 B.m ()
+ 12.5% <a href="#m4">[4]</a> 1/1 2 R.m ()
+ 12.5% <a href="#m5">[5]</a> 1/1 2 S.m ()
+<a name="m1"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 2/2 8 (toplevel)
+[1] 50.0% 2+0 8 Z.m ()
+ 100.0% excl 8
+<a name="m2"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[2] 12.5% 1+0 2 A.m ()
+ 100.0% excl 2
+<a name="m3"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[3] 12.5% 1+0 2 B.m ()
+ 100.0% excl 2
+<a name="m4"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[4] 12.5% 1+0 2 R.m ()
+ 100.0% excl 2
+<a name="m5"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[5] 12.5% 1+0 2 S.m ()
+ 100.0% excl 2
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+ Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
+ 8 50.00 50.00 50.00 0.00 0.00 1 main
+ 8 50.00 100.00 100.00 0.00 100.00 2 foo
+ 0 0.00 100.00 nan nan nan 3 bar
+ 0 0.00 100.00 nan nan nan 4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 12 ( 75.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 12 100.00 33.33 main
+ 8 66.67 66.67 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 8 ( 50.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 0 0.00 0.00 main
+ 8 100.00 100.00 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;62.5 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d3')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd3">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;87.5 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; R</div>
+<div class="parent" id="d3">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d4')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd4">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; S</div>
+<div class="parent" id="d4">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m5">[5]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;16 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;Z.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;62.5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;B.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;87.5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;R.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m5">[5]</a>&nbsp;S.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterDiffKeysTrace b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterDiffKeysTrace
new file mode 100644
index 0000000..9f87efc
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterDiffKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterSameKeys b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterSameKeys
new file mode 100644
index 0000000..6714ddd
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterSameKeys
@@ -0,0 +1,19 @@
+# ____ ____ ________
+# __|A |_____________________|B ||Z |__
+#
+# ____ ________ ____
+# _______|A ||Z ||B |______________
+#
+#
+0 1 A
+2 1 A
+0 2 A
+2 2 A
+2 2 Z
+6 2 Z
+6 2 B
+8 2 B
+2 1 B
+4 1 B
+4 1 Z
+8 1 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterSameKeysExpected b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterSameKeysExpected
new file mode 100644
index 0000000..79c2e63
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterSameKeysExpected
@@ -0,0 +1,203 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+ obj=document.getElementById(item);
+ visible=(obj.style.display!="none" && obj.style.display!="");
+ key=document.getElementById("x" + item);
+ if (visible) {
+ obj.style.display="none";
+ key.innerHTML="+";
+ } else {
+ obj.style.display="block";
+ key.innerHTML="-";
+ }
+}
+function onMouseOver(obj) {
+ obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+ obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+ <li><a href="#exclusive">Exclusive profile</a></li>
+ <li><a href="#inclusive">Inclusive profile</a></li>
+ <li><a href="#thread">Thread profile</a></li>
+ <li><a href="#class">Class/method profile</a></li>
+ <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 16
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+ Usecs self % sum % Method
+ 8 50.00 50.00 <a href="#m1">[1]</a> Z.m ()
+ 4 25.00 75.00 <a href="#m2">[2]</a> A.m ()
+ 4 25.00 100.00 <a href="#m3">[3]</a> B.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index %/total %/self index calls usecs name
+<a name="m0"></a>----------------------------------------------------
+[0] 100.0% 0+0 16 (toplevel)
+ 0.0% excl 0
+ 50.0% <a href="#m1">[1]</a> 2/2 8 Z.m ()
+ 25.0% <a href="#m2">[2]</a> 2/2 4 A.m ()
+ 25.0% <a href="#m3">[3]</a> 2/2 4 B.m ()
+<a name="m1"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 2/2 8 (toplevel)
+[1] 50.0% 2+0 8 Z.m ()
+ 100.0% excl 8
+<a name="m2"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
+[2] 25.0% 2+0 4 A.m ()
+ 100.0% excl 4
+<a name="m3"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
+[3] 25.0% 2+0 4 B.m ()
+ 100.0% excl 4
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+ Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
+ 8 50.00 50.00 50.00 0.00 0.00 1 main
+ 8 50.00 100.00 100.00 0.00 0.00 2 foo
+ 0 0.00 100.00 nan nan nan 3 bar
+ 0 0.00 100.00 nan nan nan 4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 12 ( 75.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 12 100.00 33.33 main
+ 8 66.67 66.67 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;16 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;Z.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;B.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterSameKeysTrace b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterSameKeysTrace
new file mode 100644
index 0000000..74e4c53
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapCrossThreadSameFilterSameKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterDiffKeys b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterDiffKeys
new file mode 100644
index 0000000..b92471f
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterDiffKeys
@@ -0,0 +1,13 @@
+# ____ ____ ____ ____ ____
+# __|A ||D ||E ||B ||Z |__
+#
+0 1 A
+2 1 A
+2 1 D
+4 1 D
+4 1 E
+6 1 E
+6 1 B
+8 1 B
+8 1 Z
+10 1 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterDiffKeysExpected b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterDiffKeysExpected
new file mode 100644
index 0000000..3b2ffc8
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterDiffKeysExpected
@@ -0,0 +1,232 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+ obj=document.getElementById(item);
+ visible=(obj.style.display!="none" && obj.style.display!="");
+ key=document.getElementById("x" + item);
+ if (visible) {
+ obj.style.display="none";
+ key.innerHTML="+";
+ } else {
+ obj.style.display="block";
+ key.innerHTML="-";
+ }
+}
+function onMouseOver(obj) {
+ obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+ obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+ <li><a href="#exclusive">Exclusive profile</a></li>
+ <li><a href="#inclusive">Inclusive profile</a></li>
+ <li><a href="#thread">Thread profile</a></li>
+ <li><a href="#class">Class/method profile</a></li>
+ <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 10
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+ Usecs self % sum % Method
+ 2 20.00 20.00 <a href="#m1">[1]</a> A.m ()
+ 2 20.00 40.00 <a href="#m2">[2]</a> B.m ()
+ 2 20.00 60.00 <a href="#m3">[3]</a> D.m ()
+ 2 20.00 80.00 <a href="#m4">[4]</a> E.m ()
+ 2 20.00 100.00 <a href="#m5">[5]</a> Z.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index %/total %/self index calls usecs name
+<a name="m0"></a>----------------------------------------------------
+[0] 100.0% 0+0 10 (toplevel)
+ 0.0% excl 0
+ 20.0% <a href="#m1">[1]</a> 1/1 2 A.m ()
+ 20.0% <a href="#m2">[2]</a> 1/1 2 B.m ()
+ 20.0% <a href="#m3">[3]</a> 1/1 2 D.m ()
+ 20.0% <a href="#m4">[4]</a> 1/1 2 E.m ()
+ 20.0% <a href="#m5">[5]</a> 1/1 2 Z.m ()
+<a name="m1"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[1] 20.0% 1+0 2 A.m ()
+ 100.0% excl 2
+<a name="m2"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[2] 20.0% 1+0 2 B.m ()
+ 100.0% excl 2
+<a name="m3"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[3] 20.0% 1+0 2 D.m ()
+ 100.0% excl 2
+<a name="m4"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[4] 20.0% 1+0 2 E.m ()
+ 100.0% excl 2
+<a name="m5"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[5] 20.0% 1+0 2 Z.m ()
+ 100.0% excl 2
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+ Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
+ 10 100.00 100.00 80.00 40.00 0.00 1 main
+ 0 0.00 100.00 nan nan nan 2 foo
+ 0 0.00 100.00 nan nan nan 3 bar
+ 0 0.00 100.00 nan nan nan 4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 8 ( 80.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 8 100.00 100.00 main
+ 0 0.00 0.00 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 4 ( 40.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 4 100.00 100.00 main
+ 0 0.00 0.00 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;&nbsp;40.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;&nbsp;60.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; D</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d3')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd3">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;&nbsp;80.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; E</div>
+<div class="parent" id="d3">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d4')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd4">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d4">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m5">[5]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;10 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;5+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;&nbsp;40.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;B.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;&nbsp;60.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;D.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;&nbsp;80.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;E.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m5">[5]</a>&nbsp;Z.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterDiffKeysTrace b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterDiffKeysTrace
new file mode 100644
index 0000000..c9c086c
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterDiffKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterSameKeys b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterSameKeys
new file mode 100644
index 0000000..27b2bf8
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterSameKeys
@@ -0,0 +1,13 @@
+# ____ ____ ____ ____ ____
+# __|R ||R ||S ||S ||Z |__
+#
+0 1 R
+2 1 R
+2 1 R
+4 1 R
+4 1 S
+6 1 S
+6 1 S
+8 1 S
+8 1 Z
+10 1 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterSameKeysExpected b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterSameKeysExpected
new file mode 100644
index 0000000..df55cd4
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterSameKeysExpected
@@ -0,0 +1,210 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+ obj=document.getElementById(item);
+ visible=(obj.style.display!="none" && obj.style.display!="");
+ key=document.getElementById("x" + item);
+ if (visible) {
+ obj.style.display="none";
+ key.innerHTML="+";
+ } else {
+ obj.style.display="block";
+ key.innerHTML="-";
+ }
+}
+function onMouseOver(obj) {
+ obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+ obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+ <li><a href="#exclusive">Exclusive profile</a></li>
+ <li><a href="#inclusive">Inclusive profile</a></li>
+ <li><a href="#thread">Thread profile</a></li>
+ <li><a href="#class">Class/method profile</a></li>
+ <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 10
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+ Usecs self % sum % Method
+ 4 40.00 40.00 <a href="#m1">[1]</a> R.m ()
+ 4 40.00 80.00 <a href="#m2">[2]</a> S.m ()
+ 2 20.00 100.00 <a href="#m3">[3]</a> Z.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index %/total %/self index calls usecs name
+<a name="m0"></a>----------------------------------------------------
+[0] 100.0% 0+0 10 (toplevel)
+ 0.0% excl 0
+ 40.0% <a href="#m1">[1]</a> 2/2 4 R.m ()
+ 40.0% <a href="#m2">[2]</a> 2/2 4 S.m ()
+ 20.0% <a href="#m3">[3]</a> 1/1 2 Z.m ()
+<a name="m1"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
+[1] 40.0% 2+0 4 R.m ()
+ 100.0% excl 4
+<a name="m2"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
+[2] 40.0% 2+0 4 S.m ()
+ 100.0% excl 4
+<a name="m3"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[3] 20.0% 1+0 2 Z.m ()
+ 100.0% excl 2
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+ Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
+ 10 100.00 100.00 80.00 0.00 80.00 1 main
+ 0 0.00 100.00 nan nan nan 2 foo
+ 0 0.00 100.00 nan nan nan 3 bar
+ 0 0.00 100.00 nan nan nan 4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 8 ( 80.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 8 100.00 100.00 main
+ 0 0.00 0.00 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 8 ( 80.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 8 100.00 100.00 main
+ 0 0.00 0.00 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;40.0 &nbsp;&nbsp;&nbsp;40.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; R</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;40.0 &nbsp;&nbsp;&nbsp;80.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; S</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;10 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;5+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;40.0&nbsp;&nbsp;&nbsp;&nbsp;40.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;R.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;40.0&nbsp;&nbsp;&nbsp;&nbsp;80.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;S.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;Z.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterSameKeysTrace b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterSameKeysTrace
new file mode 100644
index 0000000..0afca4d
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadDiffFilterSameKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterDiffKeys b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterDiffKeys
new file mode 100644
index 0000000..a494716
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterDiffKeys
@@ -0,0 +1,11 @@
+# ____ ____ ____ ____
+# __|A ||C ||B ||Z |__
+#
+0 1 A
+2 1 A
+2 1 C
+4 1 C
+4 1 B
+6 1 B
+6 1 Z
+8 1 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterDiffKeysExpected b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterDiffKeysExpected
new file mode 100644
index 0000000..720d05a
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterDiffKeysExpected
@@ -0,0 +1,214 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+ obj=document.getElementById(item);
+ visible=(obj.style.display!="none" && obj.style.display!="");
+ key=document.getElementById("x" + item);
+ if (visible) {
+ obj.style.display="none";
+ key.innerHTML="+";
+ } else {
+ obj.style.display="block";
+ key.innerHTML="-";
+ }
+}
+function onMouseOver(obj) {
+ obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+ obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+ <li><a href="#exclusive">Exclusive profile</a></li>
+ <li><a href="#inclusive">Inclusive profile</a></li>
+ <li><a href="#thread">Thread profile</a></li>
+ <li><a href="#class">Class/method profile</a></li>
+ <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 8
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+ Usecs self % sum % Method
+ 2 25.00 25.00 <a href="#m1">[1]</a> A.m ()
+ 2 25.00 50.00 <a href="#m2">[2]</a> B.m ()
+ 2 25.00 75.00 <a href="#m3">[3]</a> C.m ()
+ 2 25.00 100.00 <a href="#m4">[4]</a> Z.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index %/total %/self index calls usecs name
+<a name="m0"></a>----------------------------------------------------
+[0] 100.0% 0+0 8 (toplevel)
+ 0.0% excl 0
+ 25.0% <a href="#m1">[1]</a> 1/1 2 A.m ()
+ 25.0% <a href="#m2">[2]</a> 1/1 2 B.m ()
+ 25.0% <a href="#m3">[3]</a> 1/1 2 C.m ()
+ 25.0% <a href="#m4">[4]</a> 1/1 2 Z.m ()
+<a name="m1"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[1] 25.0% 1+0 2 A.m ()
+ 100.0% excl 2
+<a name="m2"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[2] 25.0% 1+0 2 B.m ()
+ 100.0% excl 2
+<a name="m3"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[3] 25.0% 1+0 2 C.m ()
+ 100.0% excl 2
+<a name="m4"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[4] 25.0% 1+0 2 Z.m ()
+ 100.0% excl 2
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+ Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
+ 8 100.00 100.00 75.00 0.00 0.00 1 main
+ 0 0.00 100.00 nan nan nan 2 foo
+ 0 0.00 100.00 nan nan nan 3 bar
+ 0 0.00 100.00 nan nan nan 4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 6 ( 75.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 6 100.00 100.00 main
+ 0 0.00 0.00 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; C</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d3')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd3">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d3">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;B.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;C.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;Z.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterDiffKeysTrace b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterDiffKeysTrace
new file mode 100644
index 0000000..c5f9a3e
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterDiffKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterSameKeys b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterSameKeys
new file mode 100644
index 0000000..bd645af
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterSameKeys
@@ -0,0 +1,13 @@
+# ____ ____ ____ ____ ____
+# __|A ||A ||B ||B ||Z |__
+#
+0 1 A
+2 1 A
+2 1 A
+4 1 A
+4 1 B
+6 1 B
+6 1 B
+8 1 B
+8 1 Z
+10 1 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterSameKeysExpected b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterSameKeysExpected
new file mode 100644
index 0000000..0e8f300
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterSameKeysExpected
@@ -0,0 +1,203 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+ obj=document.getElementById(item);
+ visible=(obj.style.display!="none" && obj.style.display!="");
+ key=document.getElementById("x" + item);
+ if (visible) {
+ obj.style.display="none";
+ key.innerHTML="+";
+ } else {
+ obj.style.display="block";
+ key.innerHTML="-";
+ }
+}
+function onMouseOver(obj) {
+ obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+ obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+ <li><a href="#exclusive">Exclusive profile</a></li>
+ <li><a href="#inclusive">Inclusive profile</a></li>
+ <li><a href="#thread">Thread profile</a></li>
+ <li><a href="#class">Class/method profile</a></li>
+ <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 10
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+ Usecs self % sum % Method
+ 4 40.00 40.00 <a href="#m1">[1]</a> A.m ()
+ 4 40.00 80.00 <a href="#m2">[2]</a> B.m ()
+ 2 20.00 100.00 <a href="#m3">[3]</a> Z.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index %/total %/self index calls usecs name
+<a name="m0"></a>----------------------------------------------------
+[0] 100.0% 0+0 10 (toplevel)
+ 0.0% excl 0
+ 40.0% <a href="#m1">[1]</a> 2/2 4 A.m ()
+ 40.0% <a href="#m2">[2]</a> 2/2 4 B.m ()
+ 20.0% <a href="#m3">[3]</a> 1/1 2 Z.m ()
+<a name="m1"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
+[1] 40.0% 2+0 4 A.m ()
+ 100.0% excl 4
+<a name="m2"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
+[2] 40.0% 2+0 4 B.m ()
+ 100.0% excl 4
+<a name="m3"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[3] 20.0% 1+0 2 Z.m ()
+ 100.0% excl 2
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+ Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
+ 10 100.00 100.00 80.00 0.00 0.00 1 main
+ 0 0.00 100.00 nan nan nan 2 foo
+ 0 0.00 100.00 nan nan nan 3 bar
+ 0 0.00 100.00 nan nan nan 4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 8 ( 80.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 8 100.00 100.00 main
+ 0 0.00 0.00 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;40.0 &nbsp;&nbsp;&nbsp;40.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;40.0 &nbsp;&nbsp;&nbsp;80.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;10 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;5+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;40.0&nbsp;&nbsp;&nbsp;&nbsp;40.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;40.0&nbsp;&nbsp;&nbsp;&nbsp;80.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;B.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;Z.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterSameKeysTrace b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterSameKeysTrace
new file mode 100644
index 0000000..65e381a
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingNestedOverlapSingleThreadSameFilterSameKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingPairCrossThread b/tools/dmtracedump/tests/filters/testWaitingPairCrossThread
new file mode 100644
index 0000000..6c93bc6
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPairCrossThread
@@ -0,0 +1,14 @@
+# ____ ____ ____
+# __|A |______|B ||Z |__
+#
+# _____
+# ________|Z |_________________
+#
+0 1 A
+2 1 A
+0 2 Z
+2 2 Z
+2 1 B
+4 1 B
+4 1 Z
+6 1 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingPairCrossThreadExpected b/tools/dmtracedump/tests/filters/testWaitingPairCrossThreadExpected
new file mode 100644
index 0000000..ed45fff
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPairCrossThreadExpected
@@ -0,0 +1,203 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+ obj=document.getElementById(item);
+ visible=(obj.style.display!="none" && obj.style.display!="");
+ key=document.getElementById("x" + item);
+ if (visible) {
+ obj.style.display="none";
+ key.innerHTML="+";
+ } else {
+ obj.style.display="block";
+ key.innerHTML="-";
+ }
+}
+function onMouseOver(obj) {
+ obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+ obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+ <li><a href="#exclusive">Exclusive profile</a></li>
+ <li><a href="#inclusive">Inclusive profile</a></li>
+ <li><a href="#thread">Thread profile</a></li>
+ <li><a href="#class">Class/method profile</a></li>
+ <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 8
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+ Usecs self % sum % Method
+ 4 50.00 50.00 <a href="#m1">[1]</a> Z.m ()
+ 2 25.00 75.00 <a href="#m2">[2]</a> A.m ()
+ 2 25.00 100.00 <a href="#m3">[3]</a> B.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index %/total %/self index calls usecs name
+<a name="m0"></a>----------------------------------------------------
+[0] 100.0% 0+0 8 (toplevel)
+ 0.0% excl 0
+ 50.0% <a href="#m1">[1]</a> 2/2 4 Z.m ()
+ 25.0% <a href="#m2">[2]</a> 1/1 2 A.m ()
+ 25.0% <a href="#m3">[3]</a> 1/1 2 B.m ()
+<a name="m1"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
+[1] 50.0% 2+0 4 Z.m ()
+ 100.0% excl 4
+<a name="m2"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[2] 25.0% 1+0 2 A.m ()
+ 100.0% excl 2
+<a name="m3"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[3] 25.0% 1+0 2 B.m ()
+ 100.0% excl 2
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+ Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
+ 6 75.00 75.00 66.67 0.00 0.00 1 main
+ 2 25.00 100.00 0.00 0.00 0.00 2 foo
+ 0 0.00 100.00 nan nan nan 3 bar
+ 0 0.00 100.00 nan nan nan 4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 6 ( 75.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 6 100.00 66.67 main
+ 0 0.00 33.33 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;Z.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;B.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingPairCrossThreadTrace b/tools/dmtracedump/tests/filters/testWaitingPairCrossThreadTrace
new file mode 100644
index 0000000..4e53dfd
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPairCrossThreadTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingPairSingleThread b/tools/dmtracedump/tests/filters/testWaitingPairSingleThread
new file mode 100644
index 0000000..45375ca
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPairSingleThread
@@ -0,0 +1,11 @@
+# ____ ____ ____ ____
+# __|A ||Z ||B ||Z |__
+#
+0 1 A
+2 1 A
+2 1 Z
+4 1 Z
+4 1 B
+6 1 B
+6 1 Z
+8 1 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingPairSingleThreadExpected b/tools/dmtracedump/tests/filters/testWaitingPairSingleThreadExpected
new file mode 100644
index 0000000..b3e2b3f
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPairSingleThreadExpected
@@ -0,0 +1,203 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+ obj=document.getElementById(item);
+ visible=(obj.style.display!="none" && obj.style.display!="");
+ key=document.getElementById("x" + item);
+ if (visible) {
+ obj.style.display="none";
+ key.innerHTML="+";
+ } else {
+ obj.style.display="block";
+ key.innerHTML="-";
+ }
+}
+function onMouseOver(obj) {
+ obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+ obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+ <li><a href="#exclusive">Exclusive profile</a></li>
+ <li><a href="#inclusive">Inclusive profile</a></li>
+ <li><a href="#thread">Thread profile</a></li>
+ <li><a href="#class">Class/method profile</a></li>
+ <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 8
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+ Usecs self % sum % Method
+ 4 50.00 50.00 <a href="#m1">[1]</a> Z.m ()
+ 2 25.00 75.00 <a href="#m2">[2]</a> A.m ()
+ 2 25.00 100.00 <a href="#m3">[3]</a> B.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index %/total %/self index calls usecs name
+<a name="m0"></a>----------------------------------------------------
+[0] 100.0% 0+0 8 (toplevel)
+ 0.0% excl 0
+ 50.0% <a href="#m1">[1]</a> 2/2 4 Z.m ()
+ 25.0% <a href="#m2">[2]</a> 1/1 2 A.m ()
+ 25.0% <a href="#m3">[3]</a> 1/1 2 B.m ()
+<a name="m1"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
+[1] 50.0% 2+0 4 Z.m ()
+ 100.0% excl 4
+<a name="m2"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[2] 25.0% 1+0 2 A.m ()
+ 100.0% excl 2
+<a name="m3"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[3] 25.0% 1+0 2 B.m ()
+ 100.0% excl 2
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+ Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
+ 8 100.00 100.00 75.00 0.00 0.00 1 main
+ 0 0.00 100.00 nan nan nan 2 foo
+ 0 0.00 100.00 nan nan nan 3 bar
+ 0 0.00 100.00 nan nan nan 4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 6 ( 75.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 6 100.00 100.00 main
+ 0 0.00 0.00 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;Z.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;B.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingPairSingleThreadTrace b/tools/dmtracedump/tests/filters/testWaitingPairSingleThreadTrace
new file mode 100644
index 0000000..3f29843
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPairSingleThreadTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterDiffKeys b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterDiffKeys
new file mode 100644
index 0000000..05995f3
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterDiffKeys
@@ -0,0 +1,23 @@
+# ____ ____ ____ ____
+# __|A |___________|B ||Z |____|Z |_______
+#
+# ____ ____ ____ ____
+# _______|Z ||D |___________|E |____|Z |__
+#
+#
+0 1 A
+2 1 A
+0 2 Z
+2 2 Z
+2 2 D
+4 2 D
+2 1 B
+4 1 B
+4 1 Z
+6 1 Z
+4 2 E
+6 2 E
+6 1 Z
+8 1 Z
+6 2 Z
+8 2 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterDiffKeysExpected b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterDiffKeysExpected
new file mode 100644
index 0000000..ba83cee
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterDiffKeysExpected
@@ -0,0 +1,232 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+ obj=document.getElementById(item);
+ visible=(obj.style.display!="none" && obj.style.display!="");
+ key=document.getElementById("x" + item);
+ if (visible) {
+ obj.style.display="none";
+ key.innerHTML="+";
+ } else {
+ obj.style.display="block";
+ key.innerHTML="-";
+ }
+}
+function onMouseOver(obj) {
+ obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+ obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+ <li><a href="#exclusive">Exclusive profile</a></li>
+ <li><a href="#inclusive">Inclusive profile</a></li>
+ <li><a href="#thread">Thread profile</a></li>
+ <li><a href="#class">Class/method profile</a></li>
+ <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 16
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+ Usecs self % sum % Method
+ 8 50.00 50.00 <a href="#m1">[1]</a> Z.m ()
+ 2 12.50 62.50 <a href="#m2">[2]</a> A.m ()
+ 2 12.50 75.00 <a href="#m3">[3]</a> B.m ()
+ 2 12.50 87.50 <a href="#m4">[4]</a> D.m ()
+ 2 12.50 100.00 <a href="#m5">[5]</a> E.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index %/total %/self index calls usecs name
+<a name="m0"></a>----------------------------------------------------
+[0] 100.0% 0+0 16 (toplevel)
+ 0.0% excl 0
+ 50.0% <a href="#m1">[1]</a> 4/4 8 Z.m ()
+ 12.5% <a href="#m2">[2]</a> 1/1 2 A.m ()
+ 12.5% <a href="#m3">[3]</a> 1/1 2 B.m ()
+ 12.5% <a href="#m4">[4]</a> 1/1 2 D.m ()
+ 12.5% <a href="#m5">[5]</a> 1/1 2 E.m ()
+<a name="m1"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 4/4 8 (toplevel)
+[1] 50.0% 4+0 8 Z.m ()
+ 100.0% excl 8
+<a name="m2"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[2] 12.5% 1+0 2 A.m ()
+ 100.0% excl 2
+<a name="m3"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[3] 12.5% 1+0 2 B.m ()
+ 100.0% excl 2
+<a name="m4"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[4] 12.5% 1+0 2 D.m ()
+ 100.0% excl 2
+<a name="m5"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[5] 12.5% 1+0 2 E.m ()
+ 100.0% excl 2
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+ Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
+ 8 50.00 50.00 50.00 0.00 0.00 1 main
+ 8 50.00 100.00 0.00 50.00 0.00 2 foo
+ 0 0.00 100.00 nan nan nan 3 bar
+ 0 0.00 100.00 nan nan nan 4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 8 ( 50.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 8 100.00 50.00 main
+ 0 0.00 50.00 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 8 ( 50.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 0 0.00 50.00 main
+ 8 100.00 50.00 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;62.5 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d3')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd3">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;87.5 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; D</div>
+<div class="parent" id="d3">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d4')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd4">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; E</div>
+<div class="parent" id="d4">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m5">[5]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;16 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;Z.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;62.5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;B.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;87.5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;D.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m5">[5]</a>&nbsp;E.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterDiffKeysTrace b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterDiffKeysTrace
new file mode 100644
index 0000000..30fbe38
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterDiffKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterSameKeys b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterSameKeys
new file mode 100644
index 0000000..f874464
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterSameKeys
@@ -0,0 +1,23 @@
+# ____ ____ ____ ____
+# __|R |___________|S ||Z |____|Z |_______
+#
+# ____ ____ ____ ____
+# _______|Z ||R |___________|S |____|Z |__
+#
+#
+0 1 R
+2 1 R
+0 2 Z
+2 2 Z
+2 2 R
+4 2 R
+2 1 S
+4 1 S
+4 1 Z
+6 1 Z
+4 2 S
+6 2 S
+6 1 Z
+8 1 Z
+6 2 Z
+8 2 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterSameKeysExpected b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterSameKeysExpected
new file mode 100644
index 0000000..93c4a05
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterSameKeysExpected
@@ -0,0 +1,210 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+ obj=document.getElementById(item);
+ visible=(obj.style.display!="none" && obj.style.display!="");
+ key=document.getElementById("x" + item);
+ if (visible) {
+ obj.style.display="none";
+ key.innerHTML="+";
+ } else {
+ obj.style.display="block";
+ key.innerHTML="-";
+ }
+}
+function onMouseOver(obj) {
+ obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+ obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+ <li><a href="#exclusive">Exclusive profile</a></li>
+ <li><a href="#inclusive">Inclusive profile</a></li>
+ <li><a href="#thread">Thread profile</a></li>
+ <li><a href="#class">Class/method profile</a></li>
+ <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 16
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+ Usecs self % sum % Method
+ 8 50.00 50.00 <a href="#m1">[1]</a> Z.m ()
+ 4 25.00 75.00 <a href="#m2">[2]</a> R.m ()
+ 4 25.00 100.00 <a href="#m3">[3]</a> S.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index %/total %/self index calls usecs name
+<a name="m0"></a>----------------------------------------------------
+[0] 100.0% 0+0 16 (toplevel)
+ 0.0% excl 0
+ 50.0% <a href="#m1">[1]</a> 4/4 8 Z.m ()
+ 25.0% <a href="#m2">[2]</a> 2/2 4 R.m ()
+ 25.0% <a href="#m3">[3]</a> 2/2 4 S.m ()
+<a name="m1"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 4/4 8 (toplevel)
+[1] 50.0% 4+0 8 Z.m ()
+ 100.0% excl 8
+<a name="m2"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
+[2] 25.0% 2+0 4 R.m ()
+ 100.0% excl 4
+<a name="m3"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
+[3] 25.0% 2+0 4 S.m ()
+ 100.0% excl 4
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+ Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
+ 8 50.00 50.00 50.00 0.00 50.00 1 main
+ 8 50.00 100.00 50.00 0.00 50.00 2 foo
+ 0 0.00 100.00 nan nan nan 3 bar
+ 0 0.00 100.00 nan nan nan 4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 12 ( 75.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 8 66.67 50.00 main
+ 8 66.67 50.00 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 12 ( 75.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 8 66.67 50.00 main
+ 8 66.67 50.00 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; R</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; S</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;16 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;Z.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;R.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;S.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterSameKeysTrace b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterSameKeysTrace
new file mode 100644
index 0000000..6dc1826
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadDiffFilterSameKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterDiffKeys b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterDiffKeys
new file mode 100644
index 0000000..bdc4373
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterDiffKeys
@@ -0,0 +1,23 @@
+# ____ ____ ____ ____
+# __|A |___________|B ||Z |____|Z |_______
+#
+# ____ ____ ____ ____
+# _______|Z ||R |___________|S |____|Z |__
+#
+#
+0 1 A
+2 1 A
+0 2 Z
+2 2 Z
+2 2 R
+4 2 R
+2 1 B
+4 1 B
+4 1 Z
+6 1 Z
+4 2 S
+6 2 S
+6 1 Z
+8 1 Z
+6 2 Z
+8 2 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterDiffKeysExpected b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterDiffKeysExpected
new file mode 100644
index 0000000..b154ed1
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterDiffKeysExpected
@@ -0,0 +1,232 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+ obj=document.getElementById(item);
+ visible=(obj.style.display!="none" && obj.style.display!="");
+ key=document.getElementById("x" + item);
+ if (visible) {
+ obj.style.display="none";
+ key.innerHTML="+";
+ } else {
+ obj.style.display="block";
+ key.innerHTML="-";
+ }
+}
+function onMouseOver(obj) {
+ obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+ obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+ <li><a href="#exclusive">Exclusive profile</a></li>
+ <li><a href="#inclusive">Inclusive profile</a></li>
+ <li><a href="#thread">Thread profile</a></li>
+ <li><a href="#class">Class/method profile</a></li>
+ <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 16
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+ Usecs self % sum % Method
+ 8 50.00 50.00 <a href="#m1">[1]</a> Z.m ()
+ 2 12.50 62.50 <a href="#m2">[2]</a> A.m ()
+ 2 12.50 75.00 <a href="#m3">[3]</a> B.m ()
+ 2 12.50 87.50 <a href="#m4">[4]</a> R.m ()
+ 2 12.50 100.00 <a href="#m5">[5]</a> S.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index %/total %/self index calls usecs name
+<a name="m0"></a>----------------------------------------------------
+[0] 100.0% 0+0 16 (toplevel)
+ 0.0% excl 0
+ 50.0% <a href="#m1">[1]</a> 4/4 8 Z.m ()
+ 12.5% <a href="#m2">[2]</a> 1/1 2 A.m ()
+ 12.5% <a href="#m3">[3]</a> 1/1 2 B.m ()
+ 12.5% <a href="#m4">[4]</a> 1/1 2 R.m ()
+ 12.5% <a href="#m5">[5]</a> 1/1 2 S.m ()
+<a name="m1"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 4/4 8 (toplevel)
+[1] 50.0% 4+0 8 Z.m ()
+ 100.0% excl 8
+<a name="m2"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[2] 12.5% 1+0 2 A.m ()
+ 100.0% excl 2
+<a name="m3"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[3] 12.5% 1+0 2 B.m ()
+ 100.0% excl 2
+<a name="m4"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[4] 12.5% 1+0 2 R.m ()
+ 100.0% excl 2
+<a name="m5"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[5] 12.5% 1+0 2 S.m ()
+ 100.0% excl 2
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+ Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
+ 8 50.00 50.00 50.00 0.00 0.00 1 main
+ 8 50.00 100.00 50.00 0.00 50.00 2 foo
+ 0 0.00 100.00 nan nan nan 3 bar
+ 0 0.00 100.00 nan nan nan 4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 12 ( 75.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 8 66.67 50.00 main
+ 8 66.67 50.00 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 8 ( 50.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 0 0.00 50.00 main
+ 8 100.00 50.00 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;62.5 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d3')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd3">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;&nbsp;87.5 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; R</div>
+<div class="parent" id="d3">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d4')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd4">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;12.5 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; S</div>
+<div class="parent" id="d4">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m5">[5]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;16 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;Z.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;62.5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;B.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;&nbsp;87.5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;R.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;12.5&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m5">[5]</a>&nbsp;S.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterDiffKeysTrace b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterDiffKeysTrace
new file mode 100644
index 0000000..efb0b1b
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterDiffKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterSameKeys b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterSameKeys
new file mode 100644
index 0000000..552ae40
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterSameKeys
@@ -0,0 +1,23 @@
+# ____ ____ ____ ____
+# __|A |___________|B ||Z |____|Z |_______
+#
+# ____ ____ ____ ____
+# _______|Z ||A |___________|B |____|Z |__
+#
+#
+0 1 A
+2 1 A
+0 2 Z
+2 2 Z
+2 2 A
+4 2 A
+2 1 B
+4 1 B
+4 1 Z
+6 1 Z
+4 2 B
+6 2 B
+6 1 Z
+8 1 Z
+6 2 Z
+8 2 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterSameKeysExpected b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterSameKeysExpected
new file mode 100644
index 0000000..82b0356
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterSameKeysExpected
@@ -0,0 +1,203 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+ obj=document.getElementById(item);
+ visible=(obj.style.display!="none" && obj.style.display!="");
+ key=document.getElementById("x" + item);
+ if (visible) {
+ obj.style.display="none";
+ key.innerHTML="+";
+ } else {
+ obj.style.display="block";
+ key.innerHTML="-";
+ }
+}
+function onMouseOver(obj) {
+ obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+ obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+ <li><a href="#exclusive">Exclusive profile</a></li>
+ <li><a href="#inclusive">Inclusive profile</a></li>
+ <li><a href="#thread">Thread profile</a></li>
+ <li><a href="#class">Class/method profile</a></li>
+ <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 16
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+ Usecs self % sum % Method
+ 8 50.00 50.00 <a href="#m1">[1]</a> Z.m ()
+ 4 25.00 75.00 <a href="#m2">[2]</a> A.m ()
+ 4 25.00 100.00 <a href="#m3">[3]</a> B.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index %/total %/self index calls usecs name
+<a name="m0"></a>----------------------------------------------------
+[0] 100.0% 0+0 16 (toplevel)
+ 0.0% excl 0
+ 50.0% <a href="#m1">[1]</a> 4/4 8 Z.m ()
+ 25.0% <a href="#m2">[2]</a> 2/2 4 A.m ()
+ 25.0% <a href="#m3">[3]</a> 2/2 4 B.m ()
+<a name="m1"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 4/4 8 (toplevel)
+[1] 50.0% 4+0 8 Z.m ()
+ 100.0% excl 8
+<a name="m2"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
+[2] 25.0% 2+0 4 A.m ()
+ 100.0% excl 4
+<a name="m3"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
+[3] 25.0% 2+0 4 B.m ()
+ 100.0% excl 4
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+ Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
+ 8 50.00 50.00 50.00 0.00 0.00 1 main
+ 8 50.00 100.00 50.00 0.00 0.00 2 foo
+ 0 0.00 100.00 nan nan nan 3 bar
+ 0 0.00 100.00 nan nan nan 4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 12 ( 75.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 8 66.67 50.00 main
+ 8 66.67 50.00 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;&nbsp;75.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;25.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;16 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;Z.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;&nbsp;75.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;25.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;B.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterSameKeysTrace b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterSameKeysTrace
new file mode 100644
index 0000000..497e925
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapCrossThreadSameFilterSameKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterDiffKeys b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterDiffKeys
new file mode 100644
index 0000000..edf03c5
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterDiffKeys
@@ -0,0 +1,13 @@
+# ____ ____ ____ ____ ____
+# __|A ||D ||B ||E ||Z |__
+#
+0 1 A
+2 1 A
+2 1 D
+4 1 D
+4 1 B
+6 1 B
+6 1 E
+8 1 E
+8 1 Z
+10 1 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterDiffKeysExpected b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterDiffKeysExpected
new file mode 100644
index 0000000..2d59720
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterDiffKeysExpected
@@ -0,0 +1,232 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+ obj=document.getElementById(item);
+ visible=(obj.style.display!="none" && obj.style.display!="");
+ key=document.getElementById("x" + item);
+ if (visible) {
+ obj.style.display="none";
+ key.innerHTML="+";
+ } else {
+ obj.style.display="block";
+ key.innerHTML="-";
+ }
+}
+function onMouseOver(obj) {
+ obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+ obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+ <li><a href="#exclusive">Exclusive profile</a></li>
+ <li><a href="#inclusive">Inclusive profile</a></li>
+ <li><a href="#thread">Thread profile</a></li>
+ <li><a href="#class">Class/method profile</a></li>
+ <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 10
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+ Usecs self % sum % Method
+ 2 20.00 20.00 <a href="#m1">[1]</a> A.m ()
+ 2 20.00 40.00 <a href="#m2">[2]</a> B.m ()
+ 2 20.00 60.00 <a href="#m3">[3]</a> D.m ()
+ 2 20.00 80.00 <a href="#m4">[4]</a> E.m ()
+ 2 20.00 100.00 <a href="#m5">[5]</a> Z.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index %/total %/self index calls usecs name
+<a name="m0"></a>----------------------------------------------------
+[0] 100.0% 0+0 10 (toplevel)
+ 0.0% excl 0
+ 20.0% <a href="#m1">[1]</a> 1/1 2 A.m ()
+ 20.0% <a href="#m2">[2]</a> 1/1 2 B.m ()
+ 20.0% <a href="#m3">[3]</a> 1/1 2 D.m ()
+ 20.0% <a href="#m4">[4]</a> 1/1 2 E.m ()
+ 20.0% <a href="#m5">[5]</a> 1/1 2 Z.m ()
+<a name="m1"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[1] 20.0% 1+0 2 A.m ()
+ 100.0% excl 2
+<a name="m2"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[2] 20.0% 1+0 2 B.m ()
+ 100.0% excl 2
+<a name="m3"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[3] 20.0% 1+0 2 D.m ()
+ 100.0% excl 2
+<a name="m4"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[4] 20.0% 1+0 2 E.m ()
+ 100.0% excl 2
+<a name="m5"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[5] 20.0% 1+0 2 Z.m ()
+ 100.0% excl 2
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+ Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
+ 10 100.00 100.00 60.00 60.00 0.00 1 main
+ 0 0.00 100.00 nan nan nan 2 foo
+ 0 0.00 100.00 nan nan nan 3 bar
+ 0 0.00 100.00 nan nan nan 4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 6 ( 60.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 6 100.00 100.00 main
+ 0 0.00 0.00 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 6 ( 60.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 6 100.00 100.00 main
+ 0 0.00 0.00 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;&nbsp;40.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;&nbsp;60.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; D</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d3')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd3">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;&nbsp;80.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; E</div>
+<div class="parent" id="d3">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d4')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd4">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d4">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m5">[5]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;10 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;5+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;&nbsp;40.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;B.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;&nbsp;60.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;D.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;&nbsp;80.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;E.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m5">[5]</a>&nbsp;Z.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterDiffKeysTrace b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterDiffKeysTrace
new file mode 100644
index 0000000..b9afef4
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterDiffKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterSameKeys b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterSameKeys
new file mode 100644
index 0000000..27b2bf8
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterSameKeys
@@ -0,0 +1,13 @@
+# ____ ____ ____ ____ ____
+# __|R ||R ||S ||S ||Z |__
+#
+0 1 R
+2 1 R
+2 1 R
+4 1 R
+4 1 S
+6 1 S
+6 1 S
+8 1 S
+8 1 Z
+10 1 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterSameKeysExpected b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterSameKeysExpected
new file mode 100644
index 0000000..df55cd4
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterSameKeysExpected
@@ -0,0 +1,210 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+ obj=document.getElementById(item);
+ visible=(obj.style.display!="none" && obj.style.display!="");
+ key=document.getElementById("x" + item);
+ if (visible) {
+ obj.style.display="none";
+ key.innerHTML="+";
+ } else {
+ obj.style.display="block";
+ key.innerHTML="-";
+ }
+}
+function onMouseOver(obj) {
+ obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+ obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+ <li><a href="#exclusive">Exclusive profile</a></li>
+ <li><a href="#inclusive">Inclusive profile</a></li>
+ <li><a href="#thread">Thread profile</a></li>
+ <li><a href="#class">Class/method profile</a></li>
+ <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 10
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+ Usecs self % sum % Method
+ 4 40.00 40.00 <a href="#m1">[1]</a> R.m ()
+ 4 40.00 80.00 <a href="#m2">[2]</a> S.m ()
+ 2 20.00 100.00 <a href="#m3">[3]</a> Z.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index %/total %/self index calls usecs name
+<a name="m0"></a>----------------------------------------------------
+[0] 100.0% 0+0 10 (toplevel)
+ 0.0% excl 0
+ 40.0% <a href="#m1">[1]</a> 2/2 4 R.m ()
+ 40.0% <a href="#m2">[2]</a> 2/2 4 S.m ()
+ 20.0% <a href="#m3">[3]</a> 1/1 2 Z.m ()
+<a name="m1"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
+[1] 40.0% 2+0 4 R.m ()
+ 100.0% excl 4
+<a name="m2"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
+[2] 40.0% 2+0 4 S.m ()
+ 100.0% excl 4
+<a name="m3"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[3] 20.0% 1+0 2 Z.m ()
+ 100.0% excl 2
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+ Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
+ 10 100.00 100.00 80.00 0.00 80.00 1 main
+ 0 0.00 100.00 nan nan nan 2 foo
+ 0 0.00 100.00 nan nan nan 3 bar
+ 0 0.00 100.00 nan nan nan 4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 8 ( 80.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 8 100.00 100.00 main
+ 0 0.00 0.00 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 8 ( 80.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 8 100.00 100.00 main
+ 0 0.00 0.00 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;40.0 &nbsp;&nbsp;&nbsp;40.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; R</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;40.0 &nbsp;&nbsp;&nbsp;80.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; S</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;10 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;5+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;40.0&nbsp;&nbsp;&nbsp;&nbsp;40.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;R.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;40.0&nbsp;&nbsp;&nbsp;&nbsp;80.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;S.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;Z.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterSameKeysTrace b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterSameKeysTrace
new file mode 100644
index 0000000..a52929a
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadDiffFilterSameKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterDiffKeys b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterDiffKeys
new file mode 100644
index 0000000..c53c90d
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterDiffKeys
@@ -0,0 +1,12 @@
+# ____
+# ____ ____|B |____ ____
+# __|A ||C ||Z |__
+#
+0 1 A
+2 1 A
+2 1 C
+4 1 B
+6 1 B
+8 1 C
+8 1 Z
+10 1 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterDiffKeysExpected b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterDiffKeysExpected
new file mode 100644
index 0000000..18ce892
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterDiffKeysExpected
@@ -0,0 +1,214 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+ obj=document.getElementById(item);
+ visible=(obj.style.display!="none" && obj.style.display!="");
+ key=document.getElementById("x" + item);
+ if (visible) {
+ obj.style.display="none";
+ key.innerHTML="+";
+ } else {
+ obj.style.display="block";
+ key.innerHTML="-";
+ }
+}
+function onMouseOver(obj) {
+ obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+ obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+ <li><a href="#exclusive">Exclusive profile</a></li>
+ <li><a href="#inclusive">Inclusive profile</a></li>
+ <li><a href="#thread">Thread profile</a></li>
+ <li><a href="#class">Class/method profile</a></li>
+ <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 10
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+ Usecs self % sum % Method
+ 4 40.00 40.00 <a href="#m1">[1]</a> C.m ()
+ 2 20.00 60.00 <a href="#m2">[2]</a> A.m ()
+ 2 20.00 80.00 <a href="#m3">[3]</a> B.m ()
+ 2 20.00 100.00 <a href="#m4">[4]</a> Z.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index %/total %/self index calls usecs name
+<a name="m0"></a>----------------------------------------------------
+[0] 100.0% 0+0 10 (toplevel)
+ 0.0% excl 0
+ 60.0% <a href="#m1">[1]</a> 1/1 6 C.m ()
+ 20.0% <a href="#m2">[2]</a> 1/1 2 A.m ()
+ 20.0% <a href="#m4">[4]</a> 1/1 2 Z.m ()
+<a name="m1"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 6 (toplevel)
+[1] 60.0% 1+0 6 C.m ()
+ 66.7% excl 4
+ 33.3% <a href="#m3">[3]</a> 1/1 2 B.m ()
+<a name="m2"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[2] 20.0% 1+0 2 A.m ()
+ 100.0% excl 2
+<a name="m3"></a>----------------------------------------------------
+ 100.0% <a href="#m1">[1]</a> 1/1 2 C.m ()
+[3] 20.0% 1+0 2 B.m ()
+ 100.0% excl 2
+<a name="m4"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[4] 20.0% 1+0 2 Z.m ()
+ 100.0% excl 2
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+ Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
+ 10 100.00 100.00 80.00 0.00 0.00 1 main
+ 0 0.00 100.00 nan nan nan 2 foo
+ 0 0.00 100.00 nan nan nan 3 bar
+ 0 0.00 100.00 nan nan nan 4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 8 ( 80.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 8 100.00 100.00 main
+ 0 0.00 0.00 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;40.0 &nbsp;&nbsp;&nbsp;40.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; C</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;&nbsp;60.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;&nbsp;80.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d3')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd3">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d3">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;10 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6&nbsp;&nbsp;&nbsp;&nbsp;40.0&nbsp;&nbsp;&nbsp;&nbsp;40.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;C.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;&nbsp;60.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;&nbsp;80.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;B.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m4">[4]</a>&nbsp;Z.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterDiffKeysTrace b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterDiffKeysTrace
new file mode 100644
index 0000000..23f4187
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterDiffKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterSameKeys b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterSameKeys
new file mode 100644
index 0000000..bd645af
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterSameKeys
@@ -0,0 +1,13 @@
+# ____ ____ ____ ____ ____
+# __|A ||A ||B ||B ||Z |__
+#
+0 1 A
+2 1 A
+2 1 A
+4 1 A
+4 1 B
+6 1 B
+6 1 B
+8 1 B
+8 1 Z
+10 1 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterSameKeysExpected b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterSameKeysExpected
new file mode 100644
index 0000000..0e8f300
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterSameKeysExpected
@@ -0,0 +1,203 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+ obj=document.getElementById(item);
+ visible=(obj.style.display!="none" && obj.style.display!="");
+ key=document.getElementById("x" + item);
+ if (visible) {
+ obj.style.display="none";
+ key.innerHTML="+";
+ } else {
+ obj.style.display="block";
+ key.innerHTML="-";
+ }
+}
+function onMouseOver(obj) {
+ obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+ obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+ <li><a href="#exclusive">Exclusive profile</a></li>
+ <li><a href="#inclusive">Inclusive profile</a></li>
+ <li><a href="#thread">Thread profile</a></li>
+ <li><a href="#class">Class/method profile</a></li>
+ <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 10
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+ Usecs self % sum % Method
+ 4 40.00 40.00 <a href="#m1">[1]</a> A.m ()
+ 4 40.00 80.00 <a href="#m2">[2]</a> B.m ()
+ 2 20.00 100.00 <a href="#m3">[3]</a> Z.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index %/total %/self index calls usecs name
+<a name="m0"></a>----------------------------------------------------
+[0] 100.0% 0+0 10 (toplevel)
+ 0.0% excl 0
+ 40.0% <a href="#m1">[1]</a> 2/2 4 A.m ()
+ 40.0% <a href="#m2">[2]</a> 2/2 4 B.m ()
+ 20.0% <a href="#m3">[3]</a> 1/1 2 Z.m ()
+<a name="m1"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
+[1] 40.0% 2+0 4 A.m ()
+ 100.0% excl 4
+<a name="m2"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
+[2] 40.0% 2+0 4 B.m ()
+ 100.0% excl 4
+<a name="m3"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
+[3] 20.0% 1+0 2 Z.m ()
+ 100.0% excl 2
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+ Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
+ 10 100.00 100.00 80.00 0.00 0.00 1 main
+ 0 0.00 100.00 nan nan nan 2 foo
+ 0 0.00 100.00 nan nan nan 3 bar
+ 0 0.00 100.00 nan nan nan 4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 8 ( 80.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 8 100.00 100.00 main
+ 0 0.00 0.00 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;40.0 &nbsp;&nbsp;&nbsp;40.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;40.0 &nbsp;&nbsp;&nbsp;80.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 &nbsp;&nbsp;&nbsp;20.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d2">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;10 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;5+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;40.0&nbsp;&nbsp;&nbsp;&nbsp;40.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;A.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;40.0&nbsp;&nbsp;&nbsp;&nbsp;80.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;B.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;20.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m3">[3]</a>&nbsp;Z.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterSameKeysTrace b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterSameKeysTrace
new file mode 100644
index 0000000..01e95cd
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingPartialOverlapSingleThreadSameFilterSameKeysTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingSoloCrossThread b/tools/dmtracedump/tests/filters/testWaitingSoloCrossThread
new file mode 100644
index 0000000..c9dbd1a
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingSoloCrossThread
@@ -0,0 +1,12 @@
+# ____ ____ ____
+# __|C ||Z |__
+#
+# ____
+# ________|Z |_____________
+#
+0 1 C
+0 2 Z
+2 2 Z
+4 1 C
+4 1 Z
+6 1 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingSoloCrossThreadExpected b/tools/dmtracedump/tests/filters/testWaitingSoloCrossThreadExpected
new file mode 100644
index 0000000..409b17e
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingSoloCrossThreadExpected
@@ -0,0 +1,192 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+ obj=document.getElementById(item);
+ visible=(obj.style.display!="none" && obj.style.display!="");
+ key=document.getElementById("x" + item);
+ if (visible) {
+ obj.style.display="none";
+ key.innerHTML="+";
+ } else {
+ obj.style.display="block";
+ key.innerHTML="-";
+ }
+}
+function onMouseOver(obj) {
+ obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+ obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+ <li><a href="#exclusive">Exclusive profile</a></li>
+ <li><a href="#inclusive">Inclusive profile</a></li>
+ <li><a href="#thread">Thread profile</a></li>
+ <li><a href="#class">Class/method profile</a></li>
+ <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 8
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+ Usecs self % sum % Method
+ 4 50.00 50.00 <a href="#m1">[1]</a> C.m ()
+ 4 50.00 100.00 <a href="#m2">[2]</a> Z.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index %/total %/self index calls usecs name
+<a name="m0"></a>----------------------------------------------------
+[0] 100.0% 0+0 8 (toplevel)
+ 0.0% excl 0
+ 50.0% <a href="#m1">[1]</a> 1/1 4 C.m ()
+ 50.0% <a href="#m2">[2]</a> 2/2 4 Z.m ()
+<a name="m1"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 4 (toplevel)
+[1] 50.0% 1+0 4 C.m ()
+ 100.0% excl 4
+<a name="m2"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
+[2] 50.0% 2+0 4 Z.m ()
+ 100.0% excl 4
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+ Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
+ 6 75.00 75.00 66.67 0.00 0.00 1 main
+ 2 25.00 100.00 0.00 0.00 0.00 2 foo
+ 0 0.00 100.00 nan nan nan 3 bar
+ 0 0.00 100.00 nan nan nan 4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 6 ( 75.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 6 100.00 66.67 main
+ 0 0.00 33.33 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; C</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;C.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;Z.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingSoloCrossThreadTrace b/tools/dmtracedump/tests/filters/testWaitingSoloCrossThreadTrace
new file mode 100644
index 0000000..e73f040
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingSoloCrossThreadTrace
Binary files differ
diff --git a/tools/dmtracedump/tests/filters/testWaitingSoloSingleThread b/tools/dmtracedump/tests/filters/testWaitingSoloSingleThread
new file mode 100644
index 0000000..3f0753e
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingSoloSingleThread
@@ -0,0 +1,10 @@
+# _____
+# ____|Z |____ ____
+# __|C ||Z |__
+#
+0 1 C
+2 1 Z
+4 1 Z
+6 1 C
+6 1 Z
+8 1 Z
diff --git a/tools/dmtracedump/tests/filters/testWaitingSoloSingleThreadExpected b/tools/dmtracedump/tests/filters/testWaitingSoloSingleThreadExpected
new file mode 100644
index 0000000..8d22b63
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingSoloSingleThreadExpected
@@ -0,0 +1,194 @@
+<html>
+<head>
+<script type="text/javascript" src="(null)sortable.js"></script>
+<script langugage="javascript">
+function toggle(item) {
+ obj=document.getElementById(item);
+ visible=(obj.style.display!="none" && obj.style.display!="");
+ key=document.getElementById("x" + item);
+ if (visible) {
+ obj.style.display="none";
+ key.innerHTML="+";
+ } else {
+ obj.style.display="block";
+ key.innerHTML="-";
+ }
+}
+function onMouseOver(obj) {
+ obj.style.background="lightblue";
+}
+function onMouseOut(obj) {
+ obj.style.background="white";
+}
+</script>
+<style type="text/css">
+div { font-family: courier; font-size: 13 }
+div.parent { margin-left: 15; display: none }
+div.leaf { margin-left: 10 }
+div.header { margin-left: 10 }
+div.link { margin-left: 10; cursor: move }
+span.parent { padding-right: 10; }
+span.leaf { padding-right: 10; }
+a img { border: 0;}
+table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
+a { text-decoration: none; }
+a:hover { text-decoration: underline; }
+table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
+table.sortable tr.even td { background-color: #fff; }
+</style>
+</head><body>
+
+<a name="contents"></a>
+<h2>Table of Contents</h2>
+<ul>
+ <li><a href="#exclusive">Exclusive profile</a></li>
+ <li><a href="#inclusive">Inclusive profile</a></li>
+ <li><a href="#thread">Thread profile</a></li>
+ <li><a href="#class">Class/method profile</a></li>
+ <li><a href="#method">Method/class profile</a></li>
+</ul>
+
+<a name="exclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+Total cycles: 8
+
+<br><br>
+Exclusive elapsed times for each method, not including time spent in
+children, sorted by exclusive time.
+
+<br><br>
+<pre>
+ Usecs self % sum % Method
+ 4 50.00 50.00 <a href="#m1">[1]</a> C.m ()
+ 4 50.00 100.00 <a href="#m2">[2]</a> Z.m ()
+</pre>
+<a name="inclusive"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Inclusive elapsed times for each method and its parents and children,
+sorted by inclusive time.
+
+<br><br>
+<pre>
+index %/total %/self index calls usecs name
+<a name="m0"></a>----------------------------------------------------
+[0] 100.0% 0+0 8 (toplevel)
+ 0.0% excl 0
+ 75.0% <a href="#m1">[1]</a> 1/1 6 C.m ()
+ 25.0% <a href="#m2">[2]</a> 1/2 2 Z.m ()
+<a name="m1"></a>----------------------------------------------------
+ 100.0% <a href="#m0">[0]</a> 1/1 6 (toplevel)
+[1] 75.0% 1+0 6 C.m ()
+ 66.7% excl 4
+ 33.3% <a href="#m2">[2]</a> 1/2 2 Z.m ()
+<a name="m2"></a>----------------------------------------------------
+ 50.0% <a href="#m0">[0]</a> 1/2 2 (toplevel)
+ 50.0% <a href="#m1">[1]</a> 1/2 2 C.m ()
+[2] 50.0% 2+0 4 Z.m ()
+ 100.0% excl 4
+</pre>
+<a name="thread"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Elapsed times for each thread, sorted by elapsed time.
+Also includes percentage of time spent during the <i>execution</i> of any filters.
+
+<br><br>
+<pre>
+ Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
+ 8 100.00 100.00 75.00 0.00 0.00 1 main
+ 0 0.00 100.00 nan nan nan 2 foo
+ 0 0.00 100.00 nan nan nan 3 bar
+ 0 0.00 100.00 nan nan nan 4 blah
+</pre><br />
+
+Break-down of portion of time spent by each thread while waiting on a filter method.
+<br/><br/>
+<pre>
+Filter: FirstFilter
+Total waiting cycles: 6 ( 75.00% of total)
+Details:
+
+ Waiting cycles % of total waiting time execution time while waiting thread name
+ 6 100.00 100.00 main
+ 0 0.00 0.00 foo
+ 0 0.00 0.00 bar
+ 0 0.00 0.00 blah
+</pre>
+<br/><br/>
+<pre>
+Filter: SecondFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<br/><br/>
+<pre>
+Filter: RepeatedFilter
+Total waiting cycles: 0 ( 0.00% of total)
+</pre>
+<a name="class"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each class, summed over all the methods
+in the class.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Class</div>
+<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; C</div>
+<div class="parent" id="d0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;m&nbsp;()</div>
+</div>
+<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 &nbsp;&nbsp;&nbsp;50.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Z</div>
+<div class="parent" id="d1">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;m&nbsp;()</div>
+</div>
+<a name="method"></a>
+<hr>
+<a href="#contents">[Top]</a>
+<a href="#exclusive">[Exclusive]</a>
+<a href="#inclusive">[Inclusive]</a>
+<a href="#thread">[Thread]</a>
+<a href="#class">[Class]</a>
+<a href="#method">[Method]</a>
+<br><br>
+
+Exclusive elapsed time for each method, summed over all the classes
+that contain a method with the same name.
+
+<br><br>
+<div class="header"><span class="parent">&nbsp;</span>&nbsp;&nbsp;&nbsp;Cycles %/total Cumul.% &nbsp;Calls+Recur&nbsp; Method</div>
+<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8 &nbsp;&nbsp;100.0 &nbsp;&nbsp;100.0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m</div>
+<div class="parent" id="e0">
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m1">[1]</a>&nbsp;C.m&nbsp;()</div>
+<div class="leaf"><span class="leaf">&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;50.0&nbsp;&nbsp;&nbsp;100.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2+0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#m2">[2]</a>&nbsp;Z.m&nbsp;()</div>
+</div>
+
+</body>
+</html>
diff --git a/tools/dmtracedump/tests/filters/testWaitingSoloSingleThreadTrace b/tools/dmtracedump/tests/filters/testWaitingSoloSingleThreadTrace
new file mode 100644
index 0000000..3a43c46
--- /dev/null
+++ b/tools/dmtracedump/tests/filters/testWaitingSoloSingleThreadTrace
Binary files differ
diff --git a/tools/gclog.py b/tools/gclog.py
new file mode 100755
index 0000000..d008f6a
--- /dev/null
+++ b/tools/gclog.py
@@ -0,0 +1,234 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Parse event log output, looking for GC events. Format them for human
+# consumption.
+#
+# ALL OUTPUT VALUES ARE APPROXIMATE. The event log data format uses a
+# 12-bit floating-point representation, which means there aren't enough
+# bits to accurately represent anything but small integers. Larger
+# values will be rounded off.
+#
+# The data is generated by dalvik/vm/alloc/HeapDebug.c.
+#
+
+import getopt
+import sys
+import os
+import re
+import time
+
+DEBUG = False # DEBUG is a global variable
+
+
+def unfloat12(f12):
+ """Unpack a float12 value"""
+ if f12 < 0:
+ raise DataParseError, "bad float12 value %s" % f12
+ return (f12 & 0x1ff) << ((f12 >> 9) * 4)
+
+
+def parseGlobalInfo(value):
+ """Parse event0 (global info)"""
+ value = int(value)
+
+ # Global information:
+ #
+ # [63 ] Must be zero
+ # [62-24] ASCII process identifier
+ # [23-12] GC time in ms
+ # [11- 0] Bytes freed
+ id = (value >> 24) & 0xffffffffff
+ gctime = unfloat12((value >> 12) & 0xfff)
+ bytes_freed = unfloat12(value & 0xfff)
+
+ idstr = "%c%c%c%c%c" % ( \
+ (id >> 32) & 0xff, \
+ (id >> 24) & 0xff, \
+ (id >> 16) & 0xff, \
+ (id >> 8) & 0xff, \
+ id & 0xff )
+
+ return ( idstr, gctime, bytes_freed )
+
+
+def parseAggHeapStats(value):
+ """Parse event1 (aggregated heap stats)"""
+ value = int(value)
+
+ # Aggregated heap stats:
+ #
+ # [63-62] 10
+ # [61-60] Reserved; must be zero
+ # [59-48] Objects freed
+ # [47-36] Actual size (current footprint)
+ # [35-24] Allowed size (current hard max)
+ # [23-12] Objects allocated
+ # [11- 0] Bytes allocated
+ freed = unfloat12((value >> 48) & 0xfff)
+ footprint = unfloat12((value >> 36) & 0xfff)
+ allowed = unfloat12((value >> 24) & 0xfff)
+ objs = unfloat12((value >> 12) & 0xfff)
+ bytes = unfloat12(value & 0xfff)
+
+ return ( freed, footprint, allowed, objs, bytes )
+
+
+def parseZygoteStats(value):
+ """Parse event2 (zygote heap stats)"""
+ value = int(value)
+
+ # Zygote heap stats (except for the soft limit, which belongs to the
+ # active heap):
+ #
+ # [63-62] 11
+ # [61-60] Reserved; must be zero
+ # [59-48] Soft Limit (for the active heap)
+ # [47-36] Actual size (current footprint)
+ # [35-24] Allowed size (current hard max)
+ # [23-12] Objects allocated
+ # [11- 0] Bytes allocated
+ soft_limit = unfloat12((value >> 48) & 0xfff)
+ actual = unfloat12((value >> 36) & 0xfff)
+ allowed = unfloat12((value >> 24) & 0xfff)
+ objs = unfloat12((value >> 12) & 0xfff)
+ bytes = unfloat12(value & 0xfff)
+
+ return ( soft_limit, actual, allowed, objs, bytes )
+
+
+def parseExternalStats(value):
+ """Parse event3 (external allocation stats)"""
+ value = int(value)
+
+ # Report the current external allocation stats and the native heap
+ # summary.
+ #
+ # [63-48] Reserved; must be zero (TODO: put new data in these slots)
+ # [47-36] dlmalloc_footprint
+ # [35-24] mallinfo: total allocated space
+ # [23-12] External byte limit
+ # [11- 0] External bytes allocated
+ footprint = unfloat12((value >> 36) & 0xfff) # currently disabled
+ total = unfloat12((value >> 24) & 0xfff) # currently disabled
+ limit = unfloat12((value >> 12) & 0xfff)
+ bytes = unfloat12(value & 0xfff)
+
+ return ( footprint, total, limit, bytes )
+
+
+def handleGcInfo(procFilter, timestamp, pid, values):
+ """Handle a single dvm_gc_info event"""
+
+ pid = int(pid)
+
+ global_info = parseGlobalInfo(values[0])
+
+ if len(procFilter) > 0:
+ if global_info[0] != procFilter:
+ return
+
+ heap_stats = parseAggHeapStats(values[1])
+ zygote = parseZygoteStats(values[2])
+ external = parseExternalStats(values[3])
+
+ print "%s %s(%d) softlim=%dKB, extlim=%dKB, extalloc=%dKB" % \
+ (timestamp, global_info[0], pid, zygote[0]/1024, external[2]/1024, external[3]/1024)
+
+ if DEBUG:
+ # print "RAW: %s %s (%s,%s,%s,%s)" % \
+ # (timestamp, pid, values[0], values[1], values[2], values[3])
+
+ print "+ id=\"%s\" time=%d freed=%d" % (global_info[0], global_info[1], global_info[2])
+ print "+ freed=%d foot=%d allow=%d objs=%d bytes=%d" % \
+ (heap_stats[0], heap_stats[1], heap_stats[2], heap_stats[3], heap_stats[4])
+ print "+ soft=%d act=%d allow=%d objs=%d bytes=%d" % \
+ (zygote[0], zygote[1], zygote[2], zygote[3], zygote[4])
+ print "+ foot=%d total=%d limit=%d alloc=%d" % \
+ (external[0], external[1], external[2], external[3])
+
+ print " freed %d objects / %d bytes in %dms" % \
+ (heap_stats[0], global_info[2], global_info[1])
+
+
+def filterInput(logPipe, processFilter):
+ """Loop until EOF, pulling out GC events"""
+
+ # 04-29 20:31:00.334 I/dvm_gc_info( 69): [8320808730292729543,-8916699241518090181,-4006371297196337158,8165229]
+ gc_info_re = re.compile(r"""
+ (\d+-\d+\ \d+:\d+:\d+)\.\d+ # extract the date (#1), ignoring ms
+ .* # filler, usually " I/"
+ dvm_gc_info # only interested in GC info lines
+ \(\s*(\d+)\) # extract the pid (#2)
+ :\ \[ # filler
+ ([0-9-]+),([0-9-]+),([0-9-]+),([0-9-]+) # four values, may be negative
+ \].* # junk to end of line
+ """, re.VERBOSE)
+
+ while True:
+ line = logPipe.readline()
+ if not line:
+ print "EOF hit"
+ return
+
+ match = gc_info_re.match(line)
+ if not match:
+ #print "no match on %s" % line.strip()
+ continue
+ else:
+ handleGcInfo(processFilter, match.group(1), match.group(2), ( match.group(3), \
+ match.group(4), match.group(5), match.group(6) ) )
+
+def PrintUsage():
+ print "usage: %s [-p procFilter] [-d]" % sys.argv[0]
+
+
+def start():
+ """Entry point"""
+
+ global DEBUG
+
+ procFilter = ""
+
+ opts, args = getopt.getopt(sys.argv[1:], "hdp:")
+
+ for opt, val in opts:
+ if opt == "-h":
+ PrintUsage()
+ sys.exit(2)
+ elif opt == "-p":
+ procFilter = val
+ elif opt == "-d":
+ DEBUG = True
+
+ print "procfilter = %s" % procFilter
+ print "DEBUG = %s" % DEBUG
+
+ # launch a logcat and read from it
+ command = 'adb logcat -v time -b events'
+ logPipe = os.popen(command)
+
+
+ try:
+ filterInput(logPipe, procFilter)
+ except KeyboardInterrupt, err:
+ print "Stopping on keyboard interrupt."
+
+ logPipe.close()
+
+
+start()
diff --git a/tools/gdbjithelper/Android.mk b/tools/gdbjithelper/Android.mk
new file mode 100644
index 0000000..087bc8f
--- /dev/null
+++ b/tools/gdbjithelper/Android.mk
@@ -0,0 +1,22 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := gdbjithelper.c
+LOCAL_CFLAGS += -O0 -g
+LOCAL_MODULE := gdbjithelper
+LOCAL_MODULE_TAGS := eng
+include $(BUILD_EXECUTABLE)
diff --git a/tools/gdbjithelper/README.txt b/tools/gdbjithelper/README.txt
new file mode 100644
index 0000000..aab1a00
--- /dev/null
+++ b/tools/gdbjithelper/README.txt
@@ -0,0 +1,65 @@
+Step 1
+
+If you see a native crash in the bugreport and the PC/LR are pointing to the
+code cache address range*, copy them into codePC and codeLR in gdbjithelper.c,
+respectively.
+
+*Caveats: debuggerd doesn't know the range of code cache. So apply this tool if
+the crashing address is not contained by any shared library.
+
+ #00 pc 463ba204
+ #01 lr 463ba1c9 <unknown>
+
+code around pc:
+463ba1e4 4300e119 4284aa7a f927f7b7 40112268
+463ba1f4 419da7f8 00002000 01000100 00080000
+463ba204 4191debc 01010000 4284aa74 68b00054
+463ba214 045cf205 cc016468 0718f2a5 d0102800
+463ba224 4c13c701 a20aa108 efb0f775 e008e010
+
+code around lr:
+463ba1a8 42e19e58 f2050050 cc01045c 0718f2a5
+463ba1b8 d00f2800 4c13c701 a20aa108 efe4f775
+463ba1c8 e007e010 29006bf8 6e77dc01 a10347b8
+463ba1d8 ef60f775 6db1480b 1c2d4788 4300e119
+463ba1e8 4284aa7a f927f7b7 40112268 419da7f8
+
+
+Step 2
+
+Push $OUT/EXECUTABLES/gdbjithelper_intermediates/LINKED/gdbjithelper to
+/system/bin on the device or emulator
+
+
+Step 3
+
+Debug the executable as usual:
+
+adb forward tcp:5039 tcp:5039
+adb shell gdbserver :5039 /system/bin/gdbjithelper &
+arm-eabi-gdb $OUT/symbols/system/bin/gdbjithelper
+(gdb) tar r :5039
+Remote debugging using :5039
+Remote debugging from host 127.0.0.1
+gdb: Unable to get location for thread creation breakpoint: requested event is not supported
+__dl__start () at bionic/linker/arch/arm/begin.S:35
+35 mov r0, sp
+gdb: Unable to get location for thread creation breakpoint: requested event is not supported
+Current language: auto; currently asm
+(gdb) c
+Continuing.
+[New Thread 596]
+codePC[0]: 0x4300e119
+codePC[1]: 0x4284aa7a
+ :
+
+
+Step 4
+
+Hit ctrl-C
+
+Issue the following command to see code around PC
+x /20i (char *) &codePC+1
+
+Issue the following command to see code around LR
+x /20i (char *) &codeLR+1
diff --git a/tools/gdbjithelper/gdbjithelper.c b/tools/gdbjithelper/gdbjithelper.c
new file mode 100644
index 0000000..817d5a4
--- /dev/null
+++ b/tools/gdbjithelper/gdbjithelper.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <malloc.h>
+#include <string.h>
+
+/* Currently debuggerd dumps 20 words each around PC and LR */
+#define NUM_DUMPED_WORDS 20
+
+volatile int done;
+
+/*
+ * See README.txt for detailed steps.
+ *
+ * If you see a native crash in the bugreport and the PC/LR are
+ * pointing to the code cache address range, copy them into the following
+ * arrays.
+ *
+ * #00 pc 463ba204
+ * #01 lr 463ba1c9 <unknown>
+ *
+ * code around pc:
+ * 463ba1e4 4300e119 4284aa7a f927f7b7 40112268
+ * 463ba1f4 419da7f8 00002000 01000100 00080000
+ * 463ba204 4191debc 01010000 4284aa74 68b00054
+ * 463ba214 045cf205 cc016468 0718f2a5 d0102800
+ * 463ba224 4c13c701 a20aa108 efb0f775 e008e010
+ *
+ * code around lr:
+ * 463ba1a8 42e19e58 f2050050 cc01045c 0718f2a5
+ * 463ba1b8 d00f2800 4c13c701 a20aa108 efe4f775
+ * 463ba1c8 e007e010 29006bf8 6e77dc01 a10347b8
+ * 463ba1d8 ef60f775 6db1480b 1c2d4788 4300e119
+ * 463ba1e8 4284aa7a f927f7b7 40112268 419da7f8
+ *
+ */
+
+int codePC[] = {
+ // Sample content
+ 0x4300e119, 0x4284aa7a, 0xf927f7b7, 0x40112268,
+ 0x419da7f8, 0x00002000, 0x01000100, 0x00080000,
+ 0x4191debc, 0x01010000, 0x4284aa74, 0x68b00054,
+ 0x045cf205, 0xcc016468, 0x0718f2a5, 0xd0102800,
+ 0x4c13c701, 0xa20aa108, 0xefb0f775, 0xe008e010,
+};
+
+int codeLR[] = {
+ // Sample content
+ 0x42e19e58, 0xf2050050, 0xcc01045c, 0x0718f2a5,
+ 0xd00f2800, 0x4c13c701, 0xa20aa108, 0xefe4f775,
+ 0xe007e010, 0x29006bf8, 0x6e77dc01, 0xa10347b8,
+ 0xef60f775, 0x6db1480b, 0x1c2d4788, 0x4300e119,
+ 0x4284aa7a, 0xf927f7b7, 0x40112268, 0x419da7f8,
+};
+
+/* For example: 463ba1e4 & 0xfff */
+#define START_PC_PAGE_OFFSET 0x1e4
+
+/* For example: 463ba1a8 & 0xfff */
+#define START_LR_PAGE_OFFSET 0x1a8
+
+/* Each points to a two-page buffer */
+char *codePCCache, *codeLRCache;
+
+void dumpCode(int *pc, int *lr)
+{
+ unsigned int i;
+
+ for (i = 0; i < NUM_DUMPED_WORDS; i++) {
+ printf("%p codePC[%d]: %#010x\n", pc + i, i, pc[i]);
+ }
+
+ for (i = 0; i < NUM_DUMPED_WORDS; i++) {
+ printf("%p codeLR[%d]: %#010x\n", lr + i, i, lr[i]);
+ }
+}
+
+int main()
+{
+ codePCCache = memalign(4096, 8192);
+ codeLRCache = memalign(4096, 8192);
+
+ memcpy(codePCCache + START_PC_PAGE_OFFSET, codePC, 4 * NUM_DUMPED_WORDS);
+ memcpy(codeLRCache + START_LR_PAGE_OFFSET, codeLR, 4 * NUM_DUMPED_WORDS);
+
+ dumpCode((int *) (codePCCache + START_PC_PAGE_OFFSET),
+ (int *) (codeLRCache + START_LR_PAGE_OFFSET));
+
+ while (!done) {
+ sleep(1000);
+ }
+ return 0;
+}
diff --git a/tools/get-hprof b/tools/get-hprof
new file mode 100755
index 0000000..d56d7ad
--- /dev/null
+++ b/tools/get-hprof
@@ -0,0 +1,41 @@
+#!/bin/sh
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Grab an hprof file using adb. If an argument is specified, grab
+# the so-named file. If no argument is specified, grab the last such file
+# as found by using "ls".
+
+FILE_BASE="$1"
+if [ "x$FILE_BASE" = "x" ]; then
+ # Note: substr() is to get rid of the final carriage return.
+ FILE_BASE=`adb shell ls -l '/data/misc/heap-dump*.hprof' | tail -1 | \
+ awk '{ printf("%s", substr($7, 1, length($7) - 1)); }'`
+ if [ "x$FILE_BASE" = "x" ]; then
+ echo "No file base defined."
+ exit 1
+ fi
+fi
+
+FILE_BASE=/data/misc/${FILE_BASE}
+OUT_FILE=heap-dump.hprof
+
+adb pull "$FILE_BASE" "$OUT_FILE"
+if [ $? -ne 0 ]; then
+ echo "Failed pulling $FILE_BASE."
+ exit 1
+fi
+echo "hat $OUT_FILE"
+exit 0
diff --git a/tools/hprof-conv/Android.mk b/tools/hprof-conv/Android.mk
new file mode 100644
index 0000000..dad399e
--- /dev/null
+++ b/tools/hprof-conv/Android.mk
@@ -0,0 +1,21 @@
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := HprofConv.c
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := hprof-conv
+include $(BUILD_HOST_EXECUTABLE)
diff --git a/tools/hprof-conv/HprofConv.c b/tools/hprof-conv/HprofConv.c
new file mode 100644
index 0000000..02cb7f4
--- /dev/null
+++ b/tools/hprof-conv/HprofConv.c
@@ -0,0 +1,718 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Strip Android-specific records out of hprof data, back-converting from
+ * 1.0.3 to 1.0.2. This removes some useful information, but allows
+ * Android hprof data to be handled by widely-available tools (like "jhat").
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+#include <assert.h>
+
+//#define VERBOSE_DEBUG
+#ifdef VERBOSE_DEBUG
+# define DBUG(...) fprintf(stderr, __VA_ARGS__)
+#else
+# define DBUG(...)
+#endif
+
+#ifndef FALSE
+# define FALSE 0
+# define TRUE (!FALSE)
+#endif
+
+typedef enum HprofBasicType {
+ HPROF_BASIC_OBJECT = 2,
+ HPROF_BASIC_BOOLEAN = 4,
+ HPROF_BASIC_CHAR = 5,
+ HPROF_BASIC_FLOAT = 6,
+ HPROF_BASIC_DOUBLE = 7,
+ HPROF_BASIC_BYTE = 8,
+ HPROF_BASIC_SHORT = 9,
+ HPROF_BASIC_INT = 10,
+ HPROF_BASIC_LONG = 11,
+} HprofBasicType;
+
+typedef enum HprofTag {
+ /* tags we must handle specially */
+ HPROF_TAG_HEAP_DUMP = 0x0c,
+ HPROF_TAG_HEAP_DUMP_SEGMENT = 0x1c,
+} HprofTag;
+
+typedef enum HprofHeapTag {
+ /* 1.0.2 tags */
+ HPROF_ROOT_UNKNOWN = 0xff,
+ HPROF_ROOT_JNI_GLOBAL = 0x01,
+ HPROF_ROOT_JNI_LOCAL = 0x02,
+ HPROF_ROOT_JAVA_FRAME = 0x03,
+ HPROF_ROOT_NATIVE_STACK = 0x04,
+ HPROF_ROOT_STICKY_CLASS = 0x05,
+ HPROF_ROOT_THREAD_BLOCK = 0x06,
+ HPROF_ROOT_MONITOR_USED = 0x07,
+ HPROF_ROOT_THREAD_OBJECT = 0x08,
+ HPROF_CLASS_DUMP = 0x20,
+ HPROF_INSTANCE_DUMP = 0x21,
+ HPROF_OBJECT_ARRAY_DUMP = 0x22,
+ HPROF_PRIMITIVE_ARRAY_DUMP = 0x23,
+
+ /* Android 1.0.3 tags */
+ HPROF_HEAP_DUMP_INFO = 0xfe,
+ HPROF_ROOT_INTERNED_STRING = 0x89,
+ HPROF_ROOT_FINALIZING = 0x8a,
+ HPROF_ROOT_DEBUGGER = 0x8b,
+ HPROF_ROOT_REFERENCE_CLEANUP = 0x8c,
+ HPROF_ROOT_VM_INTERNAL = 0x8d,
+ HPROF_ROOT_JNI_MONITOR = 0x8e,
+ HPROF_UNREACHABLE = 0x90, /* deprecated */
+ HPROF_PRIMITIVE_ARRAY_NODATA_DUMP = 0xc3,
+} HprofHeapTag;
+
+#define kIdentSize 4
+#define kRecHdrLen 9
+
+
+/*
+ * ===========================================================================
+ * Expanding buffer
+ * ===========================================================================
+ */
+
+/* simple struct */
+typedef struct {
+ unsigned char* storage;
+ size_t curLen;
+ size_t maxLen;
+} ExpandBuf;
+
+/*
+ * Create an ExpandBuf.
+ */
+static ExpandBuf* ebAlloc(void)
+{
+ static const int kInitialSize = 64;
+
+ ExpandBuf* newBuf = (ExpandBuf*) malloc(sizeof(ExpandBuf));
+ if (newBuf == NULL)
+ return NULL;
+ newBuf->storage = (unsigned char*) malloc(kInitialSize);
+ newBuf->curLen = 0;
+ newBuf->maxLen = kInitialSize;
+
+ return newBuf;
+}
+
+/*
+ * Release the storage associated with an ExpandBuf.
+ */
+static void ebFree(ExpandBuf* pBuf)
+{
+ if (pBuf != NULL) {
+ free(pBuf->storage);
+ free(pBuf);
+ }
+}
+
+/*
+ * Return a pointer to the data buffer.
+ *
+ * The pointer may change as data is added to the buffer, so this value
+ * should not be cached.
+ */
+static inline unsigned char* ebGetBuffer(ExpandBuf* pBuf)
+{
+ return pBuf->storage;
+}
+
+/*
+ * Get the amount of data currently in the buffer.
+ */
+static inline size_t ebGetLength(ExpandBuf* pBuf)
+{
+ return pBuf->curLen;
+}
+
+/*
+ * Empty the buffer.
+ */
+static void ebClear(ExpandBuf* pBuf)
+{
+ pBuf->curLen = 0;
+}
+
+/*
+ * Ensure that the buffer can hold at least "size" additional bytes.
+ */
+static int ebEnsureCapacity(ExpandBuf* pBuf, int size)
+{
+ assert(size > 0);
+
+ if (pBuf->curLen + size > pBuf->maxLen) {
+ int newSize = pBuf->curLen + size + 128; /* oversize slightly */
+ unsigned char* newStorage = realloc(pBuf->storage, newSize);
+ if (newStorage == NULL) {
+ fprintf(stderr, "ERROR: realloc failed on size=%d\n", newSize);
+ return -1;
+ }
+
+ pBuf->storage = newStorage;
+ pBuf->maxLen = newSize;
+ }
+
+ assert(pBuf->curLen + size <= pBuf->maxLen);
+ return 0;
+}
+
+/*
+ * Add data to the buffer after ensuring it can hold it.
+ */
+static int ebAddData(ExpandBuf* pBuf, const void* data, size_t count)
+{
+ ebEnsureCapacity(pBuf, count);
+ memcpy(pBuf->storage + pBuf->curLen, data, count);
+ pBuf->curLen += count;
+ return 0;
+}
+
+/*
+ * Read a NULL-terminated string from the input.
+ */
+static int ebReadString(ExpandBuf* pBuf, FILE* in)
+{
+ int ic;
+
+ do {
+ ebEnsureCapacity(pBuf, 1);
+
+ ic = getc(in);
+ if (feof(in) || ferror(in)) {
+ fprintf(stderr, "ERROR: failed reading input\n");
+ return -1;
+ }
+
+ pBuf->storage[pBuf->curLen++] = (unsigned char) ic;
+ } while (ic != 0);
+
+ return 0;
+}
+
+/*
+ * Read some data, adding it to the expanding buffer.
+ *
+ * This will ensure that the buffer has enough space to hold the new data
+ * (plus the previous contents).
+ */
+static int ebReadData(ExpandBuf* pBuf, FILE* in, size_t count, int eofExpected)
+{
+ size_t actual;
+
+ assert(count > 0);
+
+ ebEnsureCapacity(pBuf, count);
+ actual = fread(pBuf->storage + pBuf->curLen, 1, count, in);
+ if (actual != count) {
+ if (eofExpected && feof(in) && !ferror(in)) {
+ /* return without reporting an error */
+ } else {
+ fprintf(stderr, "ERROR: read %d of %d bytes\n", actual, count);
+ return -1;
+ }
+ }
+
+ pBuf->curLen += count;
+ assert(pBuf->curLen <= pBuf->maxLen);
+
+ return 0;
+}
+
+/*
+ * Write the data from the buffer. Resets the data count to zero.
+ */
+static int ebWriteData(ExpandBuf* pBuf, FILE* out)
+{
+ size_t actual;
+
+ assert(pBuf->curLen > 0);
+ assert(pBuf->curLen <= pBuf->maxLen);
+
+ actual = fwrite(pBuf->storage, 1, pBuf->curLen, out);
+ if (actual != pBuf->curLen) {
+ fprintf(stderr, "ERROR: write %d of %d bytes\n", actual, pBuf->curLen);
+ return -1;
+ }
+
+ pBuf->curLen = 0;
+
+ return 0;
+}
+
+
+/*
+ * ===========================================================================
+ * Hprof stuff
+ * ===========================================================================
+ */
+
+/*
+ * Get a 2-byte value, in big-endian order, from memory.
+ */
+static uint16_t get2BE(const unsigned char* buf)
+{
+ uint16_t val;
+
+ val = (buf[0] << 8) | buf[1];
+ return val;
+}
+
+/*
+ * Get a 4-byte value, in big-endian order, from memory.
+ */
+static uint32_t get4BE(const unsigned char* buf)
+{
+ uint32_t val;
+
+ val = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+ return val;
+}
+
+/*
+ * Set a 4-byte value, in big-endian order.
+ */
+static void set4BE(unsigned char* buf, uint32_t val)
+{
+ buf[0] = val >> 24;
+ buf[1] = val >> 16;
+ buf[2] = val >> 8;
+ buf[3] = val;
+}
+
+/*
+ * Get the size, in bytes, of one of the "basic types".
+ */
+static int computeBasicLen(HprofBasicType basicType)
+{
+ static const int sizes[] = { -1, -1, 4, -1, 1, 2, 4, 8, 1, 2, 4, 8 };
+ static const size_t maxSize = sizeof(sizes) / sizeof(sizes[0]);
+
+ assert(basicType >= 0);
+ if (basicType >= maxSize)
+ return -1;
+ return sizes[basicType];
+}
+
+/*
+ * Compute the length of a HPROF_CLASS_DUMP block.
+ */
+static int computeClassDumpLen(const unsigned char* origBuf, int len)
+{
+ const unsigned char* buf = origBuf;
+ int blockLen = 0;
+ int i, count;
+
+ blockLen += kIdentSize * 7 + 8;
+ buf += blockLen;
+ len -= blockLen;
+
+ if (len < 0)
+ return -1;
+
+ count = get2BE(buf);
+ buf += 2;
+ len -= 2;
+ DBUG("CDL: 1st count is %d\n", count);
+ for (i = 0; i < count; i++) {
+ HprofBasicType basicType;
+ int basicLen;
+
+ basicType = buf[2];
+ basicLen = computeBasicLen(basicType);
+ if (basicLen < 0) {
+ DBUG("ERROR: invalid basicType %d\n", basicType);
+ return -1;
+ }
+
+ buf += 2 + 1 + basicLen;
+ len -= 2 + 1 + basicLen;
+ if (len < 0)
+ return -1;
+ }
+
+ count = get2BE(buf);
+ buf += 2;
+ len -= 2;
+ DBUG("CDL: 2nd count is %d\n", count);
+ for (i = 0; i < count; i++) {
+ HprofBasicType basicType;
+ int basicLen;
+
+ basicType = buf[kIdentSize];
+ basicLen = computeBasicLen(basicType);
+ if (basicLen < 0) {
+ fprintf(stderr, "ERROR: invalid basicType %d\n", basicType);
+ return -1;
+ }
+
+ buf += kIdentSize + 1 + basicLen;
+ len -= kIdentSize + 1 + basicLen;
+ if (len < 0)
+ return -1;
+ }
+
+ count = get2BE(buf);
+ buf += 2;
+ len -= 2;
+ DBUG("CDL: 3rd count is %d\n", count);
+ for (i = 0; i < count; i++) {
+ buf += kIdentSize + 1;
+ len -= kIdentSize + 1;
+ if (len < 0)
+ return -1;
+ }
+
+ DBUG("Total class dump len: %d\n", buf - origBuf);
+ return buf - origBuf;
+}
+
+/*
+ * Compute the length of a HPROF_INSTANCE_DUMP block.
+ */
+static int computeInstanceDumpLen(const unsigned char* origBuf, int len)
+{
+ int extraCount = get4BE(origBuf + kIdentSize * 2 + 4);
+ return kIdentSize * 2 + 8 + extraCount;
+}
+
+/*
+ * Compute the length of a HPROF_OBJECT_ARRAY_DUMP block.
+ */
+static int computeObjectArrayDumpLen(const unsigned char* origBuf, int len)
+{
+ int arrayCount = get4BE(origBuf + kIdentSize + 4);
+ return kIdentSize * 2 + 8 + arrayCount * kIdentSize;
+}
+
+/*
+ * Compute the length of a HPROF_PRIMITIVE_ARRAY_DUMP block.
+ */
+static int computePrimitiveArrayDumpLen(const unsigned char* origBuf, int len)
+{
+ int arrayCount = get4BE(origBuf + kIdentSize + 4);
+ HprofBasicType basicType = origBuf[kIdentSize + 8];
+ int basicLen = computeBasicLen(basicType);
+
+ return kIdentSize + 9 + arrayCount * basicLen;
+}
+
+/*
+ * Crunch through a heap dump record, writing the original or converted
+ * data to "out".
+ */
+static int processHeapDump(ExpandBuf* pBuf, FILE* out)
+{
+ ExpandBuf* pOutBuf = ebAlloc();
+ unsigned char* origBuf = ebGetBuffer(pBuf);
+ unsigned char* buf = origBuf;
+ int len = ebGetLength(pBuf);
+ int result = -1;
+
+ pBuf = NULL; /* we just use the raw pointer from here forward */
+
+ /* copy the original header to the output buffer */
+ if (ebAddData(pOutBuf, buf, kRecHdrLen) != 0)
+ goto bail;
+
+ buf += kRecHdrLen; /* skip past record header */
+ len -= kRecHdrLen;
+
+ while (len > 0) {
+ unsigned char subType = buf[0];
+ int justCopy = TRUE;
+ int subLen;
+
+ DBUG("--- 0x%02x ", subType);
+ switch (subType) {
+ /* 1.0.2 types */
+ case HPROF_ROOT_UNKNOWN:
+ subLen = kIdentSize;
+ break;
+ case HPROF_ROOT_JNI_GLOBAL:
+ subLen = kIdentSize * 2;
+ break;
+ case HPROF_ROOT_JNI_LOCAL:
+ subLen = kIdentSize + 8;
+ break;
+ case HPROF_ROOT_JAVA_FRAME:
+ subLen = kIdentSize + 8;
+ break;
+ case HPROF_ROOT_NATIVE_STACK:
+ subLen = kIdentSize + 4;
+ break;
+ case HPROF_ROOT_STICKY_CLASS:
+ subLen = kIdentSize;
+ break;
+ case HPROF_ROOT_THREAD_BLOCK:
+ subLen = kIdentSize + 4;
+ break;
+ case HPROF_ROOT_MONITOR_USED:
+ subLen = kIdentSize;
+ break;
+ case HPROF_ROOT_THREAD_OBJECT:
+ subLen = kIdentSize + 8;
+ break;
+ case HPROF_CLASS_DUMP:
+ subLen = computeClassDumpLen(buf+1, len-1);
+ break;
+ case HPROF_INSTANCE_DUMP:
+ subLen = computeInstanceDumpLen(buf+1, len-1);
+ break;
+ case HPROF_OBJECT_ARRAY_DUMP:
+ subLen = computeObjectArrayDumpLen(buf+1, len-1);
+ break;
+ case HPROF_PRIMITIVE_ARRAY_DUMP:
+ subLen = computePrimitiveArrayDumpLen(buf+1, len-1);
+ break;
+
+ /* these were added for Android in 1.0.3 */
+ case HPROF_HEAP_DUMP_INFO:
+ justCopy = FALSE;
+ subLen = kIdentSize + 4;
+ // no 1.0.2 equivalent for this
+ break;
+ case HPROF_ROOT_INTERNED_STRING:
+ buf[0] = HPROF_ROOT_UNKNOWN;
+ subLen = kIdentSize;
+ break;
+ case HPROF_ROOT_FINALIZING:
+ buf[0] = HPROF_ROOT_UNKNOWN;
+ subLen = kIdentSize;
+ break;
+ case HPROF_ROOT_DEBUGGER:
+ buf[0] = HPROF_ROOT_UNKNOWN;
+ subLen = kIdentSize;
+ break;
+ case HPROF_ROOT_REFERENCE_CLEANUP:
+ buf[0] = HPROF_ROOT_UNKNOWN;
+ subLen = kIdentSize;
+ break;
+ case HPROF_ROOT_VM_INTERNAL:
+ buf[0] = HPROF_ROOT_UNKNOWN;
+ subLen = kIdentSize;
+ break;
+ case HPROF_ROOT_JNI_MONITOR:
+ /* keep the ident, drop the next 8 bytes */
+ buf[0] = HPROF_ROOT_UNKNOWN;
+ justCopy = FALSE;
+ ebAddData(pOutBuf, buf, 1 + kIdentSize);
+ subLen = kIdentSize + 8;
+ break;
+ case HPROF_UNREACHABLE:
+ buf[0] = HPROF_ROOT_UNKNOWN;
+ subLen = kIdentSize;
+ break;
+ case HPROF_PRIMITIVE_ARRAY_NODATA_DUMP:
+ buf[0] = HPROF_PRIMITIVE_ARRAY_DUMP;
+ buf[5] = buf[6] = buf[7] = buf[8] = 0; /* set array len to 0 */
+ subLen = kIdentSize + 9;
+ break;
+
+ /* shouldn't get here */
+ default:
+ fprintf(stderr, "ERROR: unexpected subtype 0x%02x at offset %d\n",
+ subType, buf - origBuf);
+ goto bail;
+ }
+
+ if (justCopy) {
+ /* copy source data */
+ DBUG("(%d)\n", 1 + subLen);
+ ebAddData(pOutBuf, buf, 1 + subLen);
+ } else {
+ /* other data has been written, or the sub-record omitted */
+ DBUG("(adv %d)\n", 1 + subLen);
+ }
+
+ /* advance to next entry */
+ buf += 1 + subLen;
+ len -= 1 + subLen;
+ }
+
+ /*
+ * Update the record length.
+ */
+ set4BE(ebGetBuffer(pOutBuf) + 5, ebGetLength(pOutBuf) - kRecHdrLen);
+
+ if (ebWriteData(pOutBuf, out) != 0)
+ goto bail;
+
+ result = 0;
+
+bail:
+ ebFree(pOutBuf);
+ return result;
+}
+
+/*
+ * Filter an hprof data file.
+ */
+static int filterData(FILE* in, FILE* out)
+{
+ ExpandBuf* pBuf;
+ int result = -1;
+
+ pBuf = ebAlloc();
+ if (pBuf == NULL)
+ goto bail;
+
+ /*
+ * Start with the header.
+ */
+ if (ebReadString(pBuf, in) != 0)
+ goto bail;
+
+ if (strcmp((const char*)ebGetBuffer(pBuf), "JAVA PROFILE 1.0.3") != 0) {
+ fprintf(stderr, "ERROR: expecting 1.0.3\n");
+ goto bail;
+ }
+
+ /* downgrade to 1.0.2 */
+ (ebGetBuffer(pBuf))[17] = '2';
+ if (ebWriteData(pBuf, out) != 0)
+ goto bail;
+
+ /*
+ * Copy:
+ * (4b) identifier size, always 4
+ * (8b) file creation date
+ */
+ if (ebReadData(pBuf, in, 12, FALSE) != 0)
+ goto bail;
+ if (ebWriteData(pBuf, out) != 0)
+ goto bail;
+
+ /*
+ * Read records until we hit EOF. Each record begins with:
+ * (1b) type
+ * (4b) timestamp
+ * (4b) length of data that follows
+ */
+ while (1) {
+ assert(ebGetLength(pBuf) == 0);
+
+ /* read type char */
+ if (ebReadData(pBuf, in, 1, TRUE) != 0)
+ goto bail;
+ if (feof(in))
+ break;
+
+ /* read the rest of the header */
+ if (ebReadData(pBuf, in, kRecHdrLen-1, FALSE) != 0)
+ goto bail;
+
+ unsigned char* buf = ebGetBuffer(pBuf);
+ unsigned char type;
+ unsigned int timestamp, length;
+
+ type = buf[0];
+ timestamp = get4BE(buf + 1);
+ length = get4BE(buf + 5);
+ buf = NULL; /* ptr invalid after next read op */
+
+ /* read the record data */
+ if (length != 0) {
+ if (ebReadData(pBuf, in, length, FALSE) != 0)
+ goto bail;
+ }
+
+ if (type == HPROF_TAG_HEAP_DUMP ||
+ type == HPROF_TAG_HEAP_DUMP_SEGMENT)
+ {
+ DBUG("Processing heap dump 0x%02x (%d bytes)\n",
+ type, length);
+ if (processHeapDump(pBuf, out) != 0)
+ goto bail;
+ ebClear(pBuf);
+ } else {
+ /* keep */
+ DBUG("Keeping 0x%02x (%d bytes)\n", type, length);
+ if (ebWriteData(pBuf, out) != 0)
+ goto bail;
+ }
+ }
+
+ result = 0;
+
+bail:
+ ebFree(pBuf);
+ return result;
+}
+
+/*
+ * Get args.
+ */
+int main(int argc, char** argv)
+{
+ FILE* in = stdin;
+ FILE* out = stdout;
+ int cc;
+
+ if (argc != 3) {
+ fprintf(stderr, "Usage: hprof-conf infile outfile\n\n");
+ fprintf(stderr,
+ "Specify '-' for either or both to use stdin/stdout.\n\n");
+
+ fprintf(stderr,
+ "Copyright (C) 2009 The Android Open Source Project\n\n"
+ "This software is built from source code licensed under the "
+ "Apache License,\n"
+ "Version 2.0 (the \"License\"). You may obtain a copy of the "
+ "License at\n\n"
+ " http://www.apache.org/licenses/LICENSE-2.0\n\n"
+ "See the associated NOTICE file for this software for further "
+ "details.\n");
+
+ return 2;
+ }
+
+ if (strcmp(argv[1], "-") != 0) {
+ in = fopen(argv[1], "rb");
+ if (in == NULL) {
+ fprintf(stderr, "ERROR: failed to open input '%s': %s\n",
+ argv[1], strerror(errno));
+ return 1;
+ }
+ }
+ if (strcmp(argv[2], "-") != 0) {
+ out = fopen(argv[2], "wb");
+ if (out == NULL) {
+ fprintf(stderr, "ERROR: failed to open output '%s': %s\n",
+ argv[2], strerror(errno));
+ if (in != stdin)
+ fclose(in);
+ return 1;
+ }
+ }
+
+ cc = filterData(in, out);
+
+ if (in != stdin)
+ fclose(in);
+ if (out != stdout)
+ fclose(out);
+ return (cc != 0);
+}
diff --git a/vm/AllocTracker.c b/vm/AllocTracker.c
new file mode 100644
index 0000000..168713c
--- /dev/null
+++ b/vm/AllocTracker.c
@@ -0,0 +1,653 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Allocation tracking and reporting. We maintain a circular buffer with
+ * the most recent allocations. The data can be viewed through DDMS.
+ *
+ * There are two basic approaches: manage the buffer with atomic updates
+ * and do a system-wide suspend when DDMS requests it, or protect all
+ * accesses with a mutex. The former is potentially more efficient, but
+ * the latter is much simpler and more reliable.
+ *
+ * Ideally we'd just use the object heap allocation mutex to guard this
+ * structure, but at the point we grab that (under dvmMalloc()) we're just
+ * allocating a collection of bytes and no longer have the class reference.
+ * Because this is an optional feature it's best to leave the existing
+ * code undisturbed and just use an additional lock.
+ *
+ * We don't currently track allocations of class objects. We could, but
+ * with the possible exception of Proxy objects they're not that interesting.
+ *
+ * TODO: if we add support for class unloading, we need to add the class
+ * references here to the root set (or just disable class unloading while
+ * this is active).
+ *
+ * TODO: consider making the parameters configurable, so DDMS can decide
+ * how many allocations it wants to see and what the stack depth should be.
+ * Changing the window size is easy, changing the max stack depth is harder
+ * because we go from an array of fixed-size structs to variable-sized data.
+ */
+#include "Dalvik.h"
+
+#define kMaxAllocRecordStackDepth 16 /* max 255 */
+#define kNumAllocRecords 512 /* MUST be power of 2 */
+
+/*
+ * Record the details of an allocation.
+ */
+struct AllocRecord {
+ ClassObject* clazz; /* class allocated in this block */
+ u4 size; /* total size requested */
+ u2 threadId; /* simple thread ID; could be recycled */
+
+ /* stack trace elements; unused entries have method==NULL */
+ struct {
+ const Method* method; /* which method we're executing in */
+ int pc; /* current execution offset, in 16-bit units */
+ } stackElem[kMaxAllocRecordStackDepth];
+
+ /*
+ * This was going to be either wall-clock time in seconds or monotonic
+ * time in milliseconds since the VM started, to give a rough sense for
+ * how long ago an allocation happened. This adds a system call per
+ * allocation, which is too much overhead.
+ */
+ //u4 timestamp;
+};
+
+/*
+ * Initialize a few things. This gets called early, so keep activity to
+ * a minimum.
+ */
+bool dvmAllocTrackerStartup(void)
+{
+ /* prep locks */
+ dvmInitMutex(&gDvm.allocTrackerLock);
+
+ /* initialized when enabled by DDMS */
+ assert(gDvm.allocRecords == NULL);
+
+ return true;
+}
+
+/*
+ * Release anything we're holding on to.
+ */
+void dvmAllocTrackerShutdown(void)
+{
+ free(gDvm.allocRecords);
+ dvmDestroyMutex(&gDvm.allocTrackerLock);
+}
+
+
+/*
+ * ===========================================================================
+ * Collection
+ * ===========================================================================
+ */
+
+/*
+ * Enable allocation tracking. Does nothing if tracking is already enabled.
+ *
+ * Returns "true" on success.
+ */
+bool dvmEnableAllocTracker(void)
+{
+ bool result = true;
+ dvmLockMutex(&gDvm.allocTrackerLock);
+
+ if (gDvm.allocRecords == NULL) {
+ LOGI("Enabling alloc tracker (%d entries, %d frames --> %d bytes)\n",
+ kNumAllocRecords, kMaxAllocRecordStackDepth,
+ sizeof(AllocRecord) * kNumAllocRecords);
+ gDvm.allocRecordHead = gDvm.allocRecordCount = 0;
+ gDvm.allocRecords =
+ (AllocRecord*) malloc(sizeof(AllocRecord) * kNumAllocRecords);
+
+ if (gDvm.allocRecords == NULL)
+ result = false;
+ }
+
+ dvmUnlockMutex(&gDvm.allocTrackerLock);
+ return result;
+}
+
+/*
+ * Disable allocation tracking. Does nothing if tracking is not enabled.
+ */
+void dvmDisableAllocTracker(void)
+{
+ dvmLockMutex(&gDvm.allocTrackerLock);
+
+ if (gDvm.allocRecords != NULL) {
+ free(gDvm.allocRecords);
+ gDvm.allocRecords = NULL;
+ }
+
+ dvmUnlockMutex(&gDvm.allocTrackerLock);
+}
+
+/*
+ * Get the last few stack frames.
+ */
+static void getStackFrames(Thread* self, AllocRecord* pRec)
+{
+ int stackDepth = 0;
+ void* fp;
+
+ fp = self->curFrame;
+
+ while ((fp != NULL) && (stackDepth < kMaxAllocRecordStackDepth)) {
+ const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+ const Method* method = saveArea->method;
+
+ if (!dvmIsBreakFrame(fp)) {
+ pRec->stackElem[stackDepth].method = method;
+ if (dvmIsNativeMethod(method)) {
+ pRec->stackElem[stackDepth].pc = 0;
+ } else {
+ assert(saveArea->xtra.currentPc >= method->insns &&
+ saveArea->xtra.currentPc <
+ method->insns + dvmGetMethodInsnsSize(method));
+ pRec->stackElem[stackDepth].pc =
+ (int) (saveArea->xtra.currentPc - method->insns);
+ }
+ stackDepth++;
+ }
+
+ assert(fp != saveArea->prevFrame);
+ fp = saveArea->prevFrame;
+ }
+
+ /* clear out the rest (normally there won't be any) */
+ while (stackDepth < kMaxAllocRecordStackDepth) {
+ pRec->stackElem[stackDepth].method = NULL;
+ pRec->stackElem[stackDepth].pc = 0;
+ stackDepth++;
+ }
+}
+
+/*
+ * Add a new allocation to the set.
+ */
+void dvmDoTrackAllocation(ClassObject* clazz, int size)
+{
+ dvmLockMutex(&gDvm.allocTrackerLock);
+ if (gDvm.allocRecords == NULL)
+ goto bail;
+
+ Thread* self = dvmThreadSelf();
+ if (self == NULL) {
+ LOGW("alloc tracker: no thread\n");
+ goto bail;
+ }
+
+ /* advance and clip */
+ if (++gDvm.allocRecordHead == kNumAllocRecords)
+ gDvm.allocRecordHead = 0;
+
+ AllocRecord* pRec = &gDvm.allocRecords[gDvm.allocRecordHead];
+
+ pRec->clazz = clazz;
+ pRec->size = size;
+ pRec->threadId = self->threadId;
+ getStackFrames(self, pRec);
+
+ if (gDvm.allocRecordCount < kNumAllocRecords)
+ gDvm.allocRecordCount++;
+
+bail:
+ dvmUnlockMutex(&gDvm.allocTrackerLock);
+}
+
+
+/*
+ * ===========================================================================
+ * Reporting
+ * ===========================================================================
+ */
+
+/*
+The data we send to DDMS contains everything we have recorded.
+
+Message header (all values big-endian):
+ (1b) message header len (to allow future expansion); includes itself
+ (1b) entry header len
+ (1b) stack frame len
+ (2b) number of entries
+ (4b) offset to string table from start of message
+ (2b) number of class name strings
+ (2b) number of method name strings
+ (2b) number of source file name strings
+ For each entry:
+ (4b) total allocation size
+ (2b) threadId
+ (2b) allocated object's class name index
+ (1b) stack depth
+ For each stack frame:
+ (2b) method's class name
+ (2b) method name
+ (2b) method source file
+ (2b) line number, clipped to 32767; -2 if native; -1 if no source
+ (xb) class name strings
+ (xb) method name strings
+ (xb) source file strings
+
+ As with other DDM traffic, strings are sent as a 4-byte length
+ followed by UTF-16 data.
+
+We send up 16-bit unsigned indexes into string tables. In theory there
+can be (kMaxAllocRecordStackDepth * kNumAllocRecords) unique strings in
+each table, but in practice there should be far fewer.
+
+The chief reason for using a string table here is to keep the size of
+the DDMS message to a minimum. This is partly to make the protocol
+efficient, but also because we have to form the whole thing up all at
+once in a memory buffer.
+
+We use separate string tables for class names, method names, and source
+files to keep the indexes small. There will generally be no overlap
+between the contents of these tables.
+*/
+const int kMessageHeaderLen = 15;
+const int kEntryHeaderLen = 9;
+const int kStackFrameLen = 8;
+
+/*
+ * Return the index of the head element.
+ *
+ * We point at the most-recently-written record, so if allocRecordCount is 1
+ * we want to use the current element. Take "head+1" and subtract count
+ * from it.
+ *
+ * We need to handle underflow in our circular buffer, so we add
+ * kNumAllocRecords and then mask it back down.
+ */
+inline static int headIndex(void)
+{
+ return (gDvm.allocRecordHead+1 + kNumAllocRecords - gDvm.allocRecordCount)
+ & (kNumAllocRecords-1);
+}
+
+/*
+ * Dump the contents of a PointerSet full of character pointers.
+ */
+static void dumpStringTable(PointerSet* strings)
+{
+ int count = dvmPointerSetGetCount(strings);
+ int i;
+
+ for (i = 0; i < count; i++)
+ printf(" %s\n", (const char*) dvmPointerSetGetEntry(strings, i));
+}
+
+/*
+ * Get the method's source file. If we don't know it, return "" instead
+ * of a NULL pointer.
+ */
+static const char* getMethodSourceFile(const Method* method)
+{
+ const char* fileName = dvmGetMethodSourceFile(method);
+ if (fileName == NULL)
+ fileName = "";
+ return fileName;
+}
+
+/*
+ * Generate string tables.
+ *
+ * Our source material is UTF-8 string constants from DEX files. If we
+ * want to be thorough we can generate a hash value for each string and
+ * use the VM hash table implementation, or we can do a quick & dirty job
+ * by just maintaining a list of unique pointers. If the same string
+ * constant appears in multiple DEX files we'll end up with duplicates,
+ * but in practice this shouldn't matter (and if it does, we can uniq-sort
+ * the result in a second pass).
+ */
+static bool populateStringTables(PointerSet* classNames,
+ PointerSet* methodNames, PointerSet* fileNames)
+{
+ int count = gDvm.allocRecordCount;
+ int idx = headIndex();
+ int classCount, methodCount, fileCount; /* debug stats */
+
+ classCount = methodCount = fileCount = 0;
+
+ while (count--) {
+ AllocRecord* pRec = &gDvm.allocRecords[idx];
+
+ dvmPointerSetAddEntry(classNames, pRec->clazz->descriptor);
+ classCount++;
+
+ int i;
+ for (i = 0; i < kMaxAllocRecordStackDepth; i++) {
+ if (pRec->stackElem[i].method == NULL)
+ break;
+
+ const Method* method = pRec->stackElem[i].method;
+ dvmPointerSetAddEntry(classNames, method->clazz->descriptor);
+ classCount++;
+ dvmPointerSetAddEntry(methodNames, method->name);
+ methodCount++;
+ dvmPointerSetAddEntry(fileNames, getMethodSourceFile(method));
+ fileCount++;
+ }
+
+ idx = (idx + 1) & (kNumAllocRecords-1);
+ }
+
+ LOGI("class %d/%d, method %d/%d, file %d/%d\n",
+ dvmPointerSetGetCount(classNames), classCount,
+ dvmPointerSetGetCount(methodNames), methodCount,
+ dvmPointerSetGetCount(fileNames), fileCount);
+
+ return true;
+}
+
+/*
+ * Generate the base info (i.e. everything but the string tables).
+ *
+ * This should be called twice. On the first call, "ptr" is NULL and
+ * "baseLen" is zero. The return value is used to allocate a buffer.
+ * On the second call, "ptr" points to a data buffer, and "baseLen"
+ * holds the value from the result of the first call.
+ *
+ * The size of the output data is returned.
+ */
+static size_t generateBaseOutput(u1* ptr, size_t baseLen,
+ const PointerSet* classNames, const PointerSet* methodNames,
+ const PointerSet* fileNames)
+{
+ u1* origPtr = ptr;
+ int count = gDvm.allocRecordCount;
+ int idx = headIndex();
+
+ if (origPtr != NULL) {
+ set1(&ptr[0], kMessageHeaderLen);
+ set1(&ptr[1], kEntryHeaderLen);
+ set1(&ptr[2], kStackFrameLen);
+ set2BE(&ptr[3], count);
+ set4BE(&ptr[5], baseLen);
+ set2BE(&ptr[9], dvmPointerSetGetCount(classNames));
+ set2BE(&ptr[11], dvmPointerSetGetCount(methodNames));
+ set2BE(&ptr[13], dvmPointerSetGetCount(fileNames));
+ }
+ ptr += kMessageHeaderLen;
+
+ while (count--) {
+ AllocRecord* pRec = &gDvm.allocRecords[idx];
+
+ /* compute depth */
+ int depth;
+ for (depth = 0; depth < kMaxAllocRecordStackDepth; depth++) {
+ if (pRec->stackElem[depth].method == NULL)
+ break;
+ }
+
+ /* output header */
+ if (origPtr != NULL) {
+ set4BE(&ptr[0], pRec->size);
+ set2BE(&ptr[4], pRec->threadId);
+ set2BE(&ptr[6],
+ dvmPointerSetFind(classNames, pRec->clazz->descriptor));
+ set1(&ptr[8], depth);
+ }
+ ptr += kEntryHeaderLen;
+
+ /* convert stack frames */
+ int i;
+ for (i = 0; i < depth; i++) {
+ if (origPtr != NULL) {
+ const Method* method = pRec->stackElem[i].method;
+ int lineNum;
+
+ lineNum = dvmLineNumFromPC(method, pRec->stackElem[i].pc);
+ if (lineNum > 32767)
+ lineNum = 32767;
+
+ set2BE(&ptr[0], dvmPointerSetFind(classNames,
+ method->clazz->descriptor));
+ set2BE(&ptr[2], dvmPointerSetFind(methodNames,
+ method->name));
+ set2BE(&ptr[4], dvmPointerSetFind(fileNames,
+ getMethodSourceFile(method)));
+ set2BE(&ptr[6], (u2)lineNum);
+ }
+ ptr += kStackFrameLen;
+ }
+
+ idx = (idx + 1) & (kNumAllocRecords-1);
+ }
+
+ return ptr - origPtr;
+}
+
+/*
+ * Compute the size required to store a string table. Includes the length
+ * word and conversion to UTF-16.
+ */
+static size_t computeStringTableSize(PointerSet* strings)
+{
+ int count = dvmPointerSetGetCount(strings);
+ size_t size = 0;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ const char* str = (const char*) dvmPointerSetGetEntry(strings, i);
+
+ size += 4 + dvmUtf8Len(str) * 2;
+ }
+
+ return size;
+}
+
+/*
+ * Convert a UTF-8 string to UTF-16. We also need to byte-swap the values
+ * to big-endian, and we can't assume even alignment on the target.
+ *
+ * Returns the string's length, in characters.
+ */
+int convertUtf8ToUtf16BEUA(u1* utf16Str, const char* utf8Str)
+{
+ u1* origUtf16Str = utf16Str;
+
+ while (*utf8Str != '\0') {
+ u2 utf16 = dexGetUtf16FromUtf8(&utf8Str); /* advances utf8Str */
+ set2BE(utf16Str, utf16);
+ utf16Str += 2;
+ }
+
+ return (utf16Str - origUtf16Str) / 2;
+}
+
+/*
+ * Output a string table serially.
+ */
+static size_t outputStringTable(PointerSet* strings, u1* ptr)
+{
+ int count = dvmPointerSetGetCount(strings);
+ u1* origPtr = ptr;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ const char* str = (const char*) dvmPointerSetGetEntry(strings, i);
+ int charLen;
+
+ /* copy UTF-8 string to big-endian unaligned UTF-16 */
+ charLen = convertUtf8ToUtf16BEUA(&ptr[4], str);
+ set4BE(&ptr[0], charLen);
+
+ ptr += 4 + charLen * 2;
+ }
+
+ return ptr - origPtr;
+}
+
+/*
+ * Generate a DDM packet with all of the tracked allocation data.
+ *
+ * On success, returns "true" with "*pData" and "*pDataLen" set.
+ */
+bool dvmGenerateTrackedAllocationReport(u1** pData, size_t* pDataLen)
+{
+ bool result = false;
+ u1* buffer = NULL;
+
+ dvmLockMutex(&gDvm.allocTrackerLock);
+
+ /*
+ * Part 1: generate string tables.
+ */
+ PointerSet* classNames = NULL;
+ PointerSet* methodNames = NULL;
+ PointerSet* fileNames = NULL;
+
+ /*
+ * Allocate storage. Usually there's 60-120 of each thing (sampled
+ * when max=512), but it varies widely and isn't closely bound to
+ * the number of allocations we've captured. The sets expand quickly
+ * if needed.
+ */
+ classNames = dvmPointerSetAlloc(128);
+ methodNames = dvmPointerSetAlloc(128);
+ fileNames = dvmPointerSetAlloc(128);
+ if (classNames == NULL || methodNames == NULL || fileNames == NULL) {
+ LOGE("Failed allocating pointer sets\n");
+ goto bail;
+ }
+
+ if (!populateStringTables(classNames, methodNames, fileNames))
+ goto bail;
+
+ if (false) {
+ printf("Classes:\n");
+ dumpStringTable(classNames);
+ printf("Methods:\n");
+ dumpStringTable(methodNames);
+ printf("Files:\n");
+ dumpStringTable(fileNames);
+ }
+
+ /*
+ * Part 2: compute the size of the output.
+ *
+ * (Could also just write to an expanding buffer.)
+ */
+ size_t baseSize, totalSize;
+ baseSize = generateBaseOutput(NULL, 0, classNames, methodNames, fileNames);
+ assert(baseSize > 0);
+ totalSize = baseSize;
+ totalSize += computeStringTableSize(classNames);
+ totalSize += computeStringTableSize(methodNames);
+ totalSize += computeStringTableSize(fileNames);
+ LOGI("Generated AT, size is %zd/%zd\n", baseSize, totalSize);
+
+ /*
+ * Part 3: allocate a buffer and generate the output.
+ */
+ u1* strPtr;
+
+ buffer = (u1*) malloc(totalSize);
+ strPtr = buffer + baseSize;
+ generateBaseOutput(buffer, baseSize, classNames, methodNames, fileNames);
+ strPtr += outputStringTable(classNames, strPtr);
+ strPtr += outputStringTable(methodNames, strPtr);
+ strPtr += outputStringTable(fileNames, strPtr);
+ if (strPtr - buffer != (int)totalSize) {
+ LOGE("size mismatch (%d vs %zd)\n", strPtr - buffer, totalSize);
+ dvmAbort();
+ }
+ //dvmPrintHexDump(buffer, totalSize);
+
+ *pData = buffer;
+ *pDataLen = totalSize;
+ buffer = NULL; // don't free -- caller will own
+ result = true;
+
+bail:
+ dvmPointerSetFree(classNames);
+ dvmPointerSetFree(methodNames);
+ dvmPointerSetFree(fileNames);
+ free(buffer);
+ dvmUnlockMutex(&gDvm.allocTrackerLock);
+ //dvmDumpTrackedAllocations(false);
+ return result;
+}
+
+/*
+ * Dump the tracked allocations to the log file.
+ *
+ * If "enable" is set, we try to enable the feature if it's not already
+ * active.
+ */
+void dvmDumpTrackedAllocations(bool enable)
+{
+ if (enable)
+ dvmEnableAllocTracker();
+
+ dvmLockMutex(&gDvm.allocTrackerLock);
+ if (gDvm.allocRecords == NULL)
+ goto bail;
+
+ /*
+ * "idx" is the head of the list. We want to start at the end of the
+ * list and move forward to the tail.
+ */
+ int idx = headIndex();
+ int count = gDvm.allocRecordCount;
+
+ LOGI("Tracked allocations, (head=%d count=%d)\n",
+ gDvm.allocRecordHead, count);
+ while (count--) {
+ AllocRecord* pRec = &gDvm.allocRecords[idx];
+ LOGI(" T=%-2d %6d %s\n",
+ pRec->threadId, pRec->size, pRec->clazz->descriptor);
+
+ if (true) {
+ int i;
+ for (i = 0; i < kMaxAllocRecordStackDepth; i++) {
+ if (pRec->stackElem[i].method == NULL)
+ break;
+
+ const Method* method = pRec->stackElem[i].method;
+ if (dvmIsNativeMethod(method)) {
+ LOGI(" %s.%s (Native)\n",
+ method->clazz->descriptor, method->name);
+ } else {
+ LOGI(" %s.%s +%d\n",
+ method->clazz->descriptor, method->name,
+ pRec->stackElem[i].pc);
+ }
+ }
+ }
+
+ /* pause periodically to help logcat catch up */
+ if ((count % 5) == 0)
+ usleep(40000);
+
+ idx = (idx + 1) & (kNumAllocRecords-1);
+ }
+
+bail:
+ dvmUnlockMutex(&gDvm.allocTrackerLock);
+ if (false) {
+ u1* data;
+ size_t dataLen;
+ if (dvmGenerateTrackedAllocationReport(&data, &dataLen))
+ free(data);
+ }
+}
diff --git a/vm/AllocTracker.h b/vm/AllocTracker.h
new file mode 100644
index 0000000..84ac9b8
--- /dev/null
+++ b/vm/AllocTracker.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Allocation tracking and reporting.
+ */
+#ifndef _DALVIK_ALLOCTRACKER
+#define _DALVIK_ALLOCTRACKER
+
+/* initialization */
+bool dvmAllocTrackerStartup(void);
+void dvmAllocTrackerShutdown(void);
+
+struct AllocRecord;
+typedef struct AllocRecord AllocRecord;
+
+/*
+ * Enable allocation tracking. Does nothing if tracking is already enabled.
+ */
+bool dvmEnableAllocTracker(void);
+
+/*
+ * Disable allocation tracking. Does nothing if tracking is not enabled.
+ */
+void dvmDisableAllocTracker(void);
+
+/*
+ * If allocation tracking is enabled, add a new entry to the set.
+ */
+#define dvmTrackAllocation(_clazz, _size) \
+ { \
+ if (gDvm.allocRecords != NULL) \
+ dvmDoTrackAllocation(_clazz, _size); \
+ }
+void dvmDoTrackAllocation(ClassObject* clazz, int size);
+
+/*
+ * Generate a DDM packet with all of the tracked allocation data.
+ *
+ * On success, returns "true" with "*pData" and "*pDataLen" set. "*pData"
+ * refers to newly-allocated storage that must be freed by the caller.
+ */
+bool dvmGenerateTrackedAllocationReport(u1** pData, size_t* pDataLen);
+
+/*
+ * Dump the tracked allocations to the log file. If "enable" is set, this
+ * will enable tracking if it's not already on.
+ */
+void dvmDumpTrackedAllocations(bool enable);
+
+#endif /*_DALVIK_ALLOCTRACKER*/
diff --git a/vm/Android.mk b/vm/Android.mk
new file mode 100644
index 0000000..3110639
--- /dev/null
+++ b/vm/Android.mk
@@ -0,0 +1,137 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Android.mk for Dalvik VM.
+#
+# This makefile builds both for host and target, and so the very large
+# swath of common definitions are factored out into a separate file to
+# minimize duplication.
+#
+# If you enable or disable optional features here (or in Dvm.mk),
+# rebuild the VM with:
+#
+# make clean-libdvm clean-libdvm_assert clean-libdvm_sv clean-libdvm_interp
+# make -j4 libdvm
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+#
+# Build for the target (device).
+#
+
+ifeq ($(TARGET_CPU_SMP),true)
+ target_smp_flag := -DANDROID_SMP=1
+else
+ target_smp_flag := -DANDROID_SMP=0
+endif
+host_smp_flag := -DANDROID_SMP=1
+
+# Build the installed version (libdvm.so) first
+include $(LOCAL_PATH)/ReconfigureDvm.mk
+
+# Overwrite default settings
+ifneq ($(TARGET_ARCH),x86)
+ifeq ($(TARGET_SIMULATOR),false)
+ LOCAL_PRELINK_MODULE := true
+endif
+endif
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := libdvm
+LOCAL_CFLAGS += $(target_smp_flag)
+include $(BUILD_SHARED_LIBRARY)
+
+# If WITH_JIT is configured, build multiple versions of libdvm.so to facilitate
+# correctness/performance bugs triage
+ifeq ($(WITH_JIT),true)
+
+ # Derivation #1
+ # Enable assert and JIT tuning
+ include $(LOCAL_PATH)/ReconfigureDvm.mk
+
+ # Enable assertions and JIT-tuning
+ LOCAL_CFLAGS += -UNDEBUG -DDEBUG=1 -DLOG_NDEBUG=1 -DWITH_DALVIK_ASSERT \
+ -DWITH_JIT_TUNING $(target_smp_flag)
+ LOCAL_MODULE := libdvm_assert
+ include $(BUILD_SHARED_LIBRARY)
+
+ # Derivation #2
+ # Enable assert and self-verification
+ include $(LOCAL_PATH)/ReconfigureDvm.mk
+
+ # Enable assertions and JIT self-verification
+ LOCAL_CFLAGS += -UNDEBUG -DDEBUG=1 -DLOG_NDEBUG=1 -DWITH_DALVIK_ASSERT \
+ -DWITH_SELF_VERIFICATION $(target_smp_flag)
+ LOCAL_MODULE := libdvm_sv
+ include $(BUILD_SHARED_LIBRARY)
+
+ # Devivation #3
+ # Compile out the JIT
+ WITH_JIT := false
+ include $(LOCAL_PATH)/ReconfigureDvm.mk
+
+ LOCAL_CFLAGS += $(target_smp_flag)
+ LOCAL_MODULE := libdvm_interp
+ include $(BUILD_SHARED_LIBRARY)
+
+endif
+
+#
+# Build for the host.
+#
+
+ifeq ($(WITH_HOST_DALVIK),true)
+
+ include $(CLEAR_VARS)
+
+ # Variables used in the included Dvm.mk.
+ dvm_os := $(HOST_OS)
+ dvm_arch := $(HOST_ARCH)
+ # Note: HOST_ARCH_VARIANT isn't defined.
+ dvm_arch_variant := $(HOST_ARCH)
+ dvm_simulator := false
+
+ include $(LOCAL_PATH)/Dvm.mk
+
+ LOCAL_SHARED_LIBRARIES += libcrypto libssl libicuuc libicui18n
+
+ LOCAL_LDLIBS := -lpthread -ldl
+ ifeq ($(HOST_OS),linux)
+ # need this for clock_gettime() in profiling
+ LOCAL_LDLIBS += -lrt
+ endif
+
+ # Build as a WHOLE static library so dependencies are available at link
+ # time. When building this target as a regular static library, certain
+ # dependencies like expat are not found by the linker.
+ LOCAL_WHOLE_STATIC_LIBRARIES += libexpat libcutils libdex liblog libnativehelper libutils libz
+
+ # The libffi from the source tree should never be used by host builds.
+ # The recommendation is that host builds should always either
+ # have sufficient custom code so that libffi isn't needed at all,
+ # or they should use the platform's provided libffi. So, if the common
+ # build rules decided to include it, axe it back out here.
+ ifneq (,$(findstring libffi,$(LOCAL_SHARED_LIBRARIES)))
+ LOCAL_SHARED_LIBRARIES := \
+ $(patsubst libffi, ,$(LOCAL_SHARED_LIBRARIES))
+ endif
+
+ LOCAL_CFLAGS += $(host_smp_flag)
+ LOCAL_MODULE_TAGS := optional
+ LOCAL_MODULE := libdvm
+
+ include $(BUILD_HOST_SHARED_LIBRARY)
+
+endif
diff --git a/vm/Atomic.c b/vm/Atomic.c
new file mode 100644
index 0000000..4473c85
--- /dev/null
+++ b/vm/Atomic.c
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+
+#include <cutils/atomic.h>
+
+/*
+ * Quasi-atomic 64-bit operations, for platforms that lack the real thing.
+ *
+ * TODO: unify ARMv6/x86/sh implementations using the to-be-written
+ * spin lock implementation. We don't want to rely on mutex innards,
+ * and it would be great if all platforms were running the same code.
+ */
+
+#if defined(HAVE_MACOSX_IPC)
+
+#include <libkern/OSAtomic.h>
+
+#if defined(__ppc__) \
+ || defined(__PPC__) \
+ || defined(__powerpc__) \
+ || defined(__powerpc) \
+ || defined(__POWERPC__) \
+ || defined(_M_PPC) \
+ || defined(__PPC)
+#define NEED_QUASIATOMICS 1
+#else
+
+int dvmQuasiAtomicCas64(int64_t oldvalue, int64_t newvalue,
+ volatile int64_t* addr)
+{
+ return OSAtomicCompareAndSwap64Barrier(oldvalue, newvalue,
+ (int64_t*)addr) == 0;
+}
+
+int64_t dvmQuasiAtomicSwap64(int64_t value, volatile int64_t* addr)
+{
+ int64_t oldValue;
+ do {
+ oldValue = *addr;
+ } while (dvmQuasiAtomicCas64(oldValue, value, addr));
+ return oldValue;
+}
+
+int64_t dvmQuasiAtomicRead64(volatile const int64_t* addr)
+{
+ return OSAtomicAdd64Barrier(0, addr);
+}
+#endif
+
+#elif defined(__i386__) || defined(__x86_64__)
+#define NEED_QUASIATOMICS 1
+
+#elif __arm__
+#include <machine/cpu-features.h>
+
+#ifdef __ARM_HAVE_LDREXD
+int64_t dvmQuasiAtomicSwap64(int64_t newvalue, volatile int64_t* addr)
+{
+ int64_t prev;
+ int status;
+ do {
+ __asm__ __volatile__ ("@ dvmQuasiAtomicSwap64\n"
+ "ldrexd %0, %H0, [%3]\n"
+ "strexd %1, %4, %H4, [%3]"
+ : "=&r" (prev), "=&r" (status), "+m"(*addr)
+ : "r" (addr), "r" (newvalue)
+ : "cc");
+ } while (__builtin_expect(status != 0, 0));
+ return prev;
+}
+
+int dvmQuasiAtomicCas64(int64_t oldvalue, int64_t newvalue,
+ volatile int64_t* addr)
+{
+ int64_t prev;
+ int status;
+ do {
+ __asm__ __volatile__ ("@ dvmQuasiAtomicCas64\n"
+ "ldrexd %0, %H0, [%3]\n"
+ "mov %1, #0\n"
+ "teq %0, %4\n"
+ "teqeq %H0, %H4\n"
+ "strexdeq %1, %5, %H5, [%3]"
+ : "=&r" (prev), "=&r" (status), "+m"(*addr)
+ : "r" (addr), "Ir" (oldvalue), "r" (newvalue)
+ : "cc");
+ } while (__builtin_expect(status != 0, 0));
+ return prev != oldvalue;
+}
+
+int64_t dvmQuasiAtomicRead64(volatile const int64_t* addr)
+{
+ int64_t value;
+ __asm__ __volatile__ ("@ dvmQuasiAtomicRead64\n"
+ "ldrexd %0, %H0, [%1]"
+ : "=&r" (value)
+ : "r" (addr));
+ return value;
+}
+
+#else
+
+// on the device, we implement the 64-bit atomic operations through
+// mutex locking. normally, this is bad because we must initialize
+// a pthread_mutex_t before being able to use it, and this means
+// having to do an initialization check on each function call, and
+// that's where really ugly things begin...
+//
+// BUT, as a special twist, we take advantage of the fact that in our
+// pthread library, a mutex is simply a volatile word whose value is always
+// initialized to 0. In other words, simply declaring a static mutex
+// object initializes it !
+//
+// another twist is that we use a small array of mutexes to dispatch
+// the contention locks from different memory addresses
+//
+
+#include <pthread.h>
+
+#define SWAP_LOCK_COUNT 32U
+static pthread_mutex_t _swap_locks[SWAP_LOCK_COUNT];
+
+#define SWAP_LOCK(addr) \
+ &_swap_locks[((unsigned)(void*)(addr) >> 3U) % SWAP_LOCK_COUNT]
+
+
+int64_t dvmQuasiAtomicSwap64(int64_t value, volatile int64_t* addr)
+{
+ int64_t oldValue;
+ pthread_mutex_t* lock = SWAP_LOCK(addr);
+
+ pthread_mutex_lock(lock);
+
+ oldValue = *addr;
+ *addr = value;
+
+ pthread_mutex_unlock(lock);
+ return oldValue;
+}
+
+int dvmQuasiAtomicCas64(int64_t oldvalue, int64_t newvalue,
+ volatile int64_t* addr)
+{
+ int result;
+ pthread_mutex_t* lock = SWAP_LOCK(addr);
+
+ pthread_mutex_lock(lock);
+
+ if (*addr == oldvalue) {
+ *addr = newvalue;
+ result = 0;
+ } else {
+ result = 1;
+ }
+ pthread_mutex_unlock(lock);
+ return result;
+}
+
+int64_t dvmQuasiAtomicRead64(volatile const int64_t* addr)
+{
+ int64_t result;
+ pthread_mutex_t* lock = SWAP_LOCK(addr);
+
+ pthread_mutex_lock(lock);
+ result = *addr;
+ pthread_mutex_unlock(lock);
+ return result;
+}
+
+#endif /*__ARM_HAVE_LDREXD*/
+
+/*****************************************************************************/
+#elif __sh__
+#define NEED_QUASIATOMICS 1
+
+#else
+#error "Unsupported atomic operations for this platform"
+#endif
+
+
+#if NEED_QUASIATOMICS
+
+/* Note that a spinlock is *not* a good idea in general
+ * since they can introduce subtle issues. For example,
+ * a real-time thread trying to acquire a spinlock already
+ * acquired by another thread will never yeld, making the
+ * CPU loop endlessly!
+ *
+ * However, this code is only used on the Linux simulator
+ * so it's probably ok for us.
+ *
+ * The alternative is to use a pthread mutex, but
+ * these must be initialized before being used, and
+ * then you have the problem of lazily initializing
+ * a mutex without any other synchronization primitive.
+ *
+ * TODO: these currently use sched_yield(), which is not guaranteed to
+ * do anything at all. We need to use dvmIterativeSleep or a wait /
+ * notify mechanism if the initial attempt fails.
+ */
+
+/* global spinlock for all 64-bit quasiatomic operations */
+static int32_t quasiatomic_spinlock = 0;
+
+int dvmQuasiAtomicCas64(int64_t oldvalue, int64_t newvalue,
+ volatile int64_t* addr)
+{
+ int result;
+
+ while (android_atomic_acquire_cas(0, 1, &quasiatomic_spinlock)) {
+#ifdef HAVE_WIN32_THREADS
+ Sleep(0);
+#else
+ sched_yield();
+#endif
+ }
+
+ if (*addr == oldvalue) {
+ *addr = newvalue;
+ result = 0;
+ } else {
+ result = 1;
+ }
+
+ android_atomic_release_store(0, &quasiatomic_spinlock);
+
+ return result;
+}
+
+int64_t dvmQuasiAtomicRead64(volatile const int64_t* addr)
+{
+ int64_t result;
+
+ while (android_atomic_acquire_cas(0, 1, &quasiatomic_spinlock)) {
+#ifdef HAVE_WIN32_THREADS
+ Sleep(0);
+#else
+ sched_yield();
+#endif
+ }
+
+ result = *addr;
+ android_atomic_release_store(0, &quasiatomic_spinlock);
+
+ return result;
+}
+
+int64_t dvmQuasiAtomicSwap64(int64_t value, volatile int64_t* addr)
+{
+ int64_t result;
+
+ while (android_atomic_acquire_cas(0, 1, &quasiatomic_spinlock)) {
+#ifdef HAVE_WIN32_THREADS
+ Sleep(0);
+#else
+ sched_yield();
+#endif
+ }
+
+ result = *addr;
+ *addr = value;
+ android_atomic_release_store(0, &quasiatomic_spinlock);
+
+ return result;
+}
+
+#endif /*NEED_QUASIATOMICS*/
diff --git a/vm/Atomic.h b/vm/Atomic.h
new file mode 100644
index 0000000..6c3a66f
--- /dev/null
+++ b/vm/Atomic.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Atomic operations
+ */
+#ifndef _DALVIK_ATOMIC
+#define _DALVIK_ATOMIC
+
+#include <cutils/atomic.h> /* use common Android atomic ops */
+#include <cutils/atomic-inline.h> /* and some uncommon ones */
+
+/*
+ * NOTE: Two "quasiatomic" operations on the exact same memory address
+ * are guaranteed to operate atomically with respect to each other,
+ * but no guarantees are made about quasiatomic operations mixed with
+ * non-quasiatomic operations on the same address, nor about
+ * quasiatomic operations that are performed on partially-overlapping
+ * memory.
+ *
+ * None of these provide a memory barrier.
+ */
+
+/*
+ * Swap the 64-bit value at "addr" with "value". Returns the previous
+ * value.
+ */
+int64_t dvmQuasiAtomicSwap64(int64_t value, volatile int64_t* addr);
+
+/*
+ * Read the 64-bit value at "addr".
+ */
+int64_t dvmQuasiAtomicRead64(volatile const int64_t* addr);
+
+/*
+ * If the value at "addr" is equal to "oldvalue", replace it with "newvalue"
+ * and return 0. Otherwise, don't swap, and return nonzero.
+ */
+int dvmQuasiAtomicCas64(int64_t oldvalue, int64_t newvalue,
+ volatile int64_t* addr);
+
+#endif /*_DALVIK_ATOMIC*/
diff --git a/vm/AtomicCache.c b/vm/AtomicCache.c
new file mode 100644
index 0000000..a6f48c2
--- /dev/null
+++ b/vm/AtomicCache.c
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Mutex-free cache. Each entry has two 32-bit keys, one 32-bit value,
+ * and a 32-bit version.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+
+/*
+ * I think modern C mandates that the results of a boolean expression are
+ * 0 or 1. If not, or we suddenly turn into C++ and bool != int, use this.
+ */
+#define BOOL_TO_INT(x) (x)
+//#define BOOL_TO_INT(x) ((x) ? 1 : 0)
+
+#define CPU_CACHE_WIDTH 32
+#define CPU_CACHE_WIDTH_1 (CPU_CACHE_WIDTH-1)
+
+#define ATOMIC_LOCK_FLAG (1 << 31)
+
+/*
+ * Allocate cache.
+ */
+AtomicCache* dvmAllocAtomicCache(int numEntries)
+{
+ AtomicCache* newCache;
+
+ newCache = (AtomicCache*) calloc(1, sizeof(AtomicCache));
+ if (newCache == NULL)
+ return NULL;
+
+ newCache->numEntries = numEntries;
+
+ newCache->entryAlloc = calloc(1,
+ sizeof(AtomicCacheEntry) * numEntries + CPU_CACHE_WIDTH);
+ if (newCache->entryAlloc == NULL)
+ return NULL;
+
+ /*
+ * Adjust storage to align on a 32-byte boundary. Each entry is 16 bytes
+ * wide. This ensures that each cache entry sits on a single CPU cache
+ * line.
+ */
+ assert(sizeof(AtomicCacheEntry) == 16);
+ newCache->entries = (AtomicCacheEntry*)
+ (((int) newCache->entryAlloc + CPU_CACHE_WIDTH_1) & ~CPU_CACHE_WIDTH_1);
+
+ return newCache;
+}
+
+/*
+ * Free cache.
+ */
+void dvmFreeAtomicCache(AtomicCache* cache)
+{
+ if (cache != NULL) {
+ free(cache->entryAlloc);
+ free(cache);
+ }
+}
+
+
+/*
+ * Update a cache entry.
+ *
+ * In the event of a collision with another thread, the update may be skipped.
+ *
+ * We only need "pCache" for stats.
+ */
+void dvmUpdateAtomicCache(u4 key1, u4 key2, u4 value, AtomicCacheEntry* pEntry,
+ u4 firstVersion
+#if CALC_CACHE_STATS > 0
+ , AtomicCache* pCache
+#endif
+ )
+{
+ /*
+ * The fields don't match, so we want to update them. There is a risk
+ * that another thread is also trying to update them, so we grab an
+ * ownership flag to lock out other threads.
+ *
+ * If the lock flag was already set in "firstVersion", somebody else
+ * was in mid-update, and we don't want to continue here. (This means
+ * that using "firstVersion" as the "before" argument to the CAS would
+ * succeed when it shouldn't and vice-versa -- we could also just pass
+ * in (firstVersion & ~ATOMIC_LOCK_FLAG) as the first argument.)
+ *
+ * NOTE: we don't deal with the situation where we overflow the version
+ * counter and trample the ATOMIC_LOCK_FLAG (at 2^31). Probably not
+ * a real concern.
+ */
+ if ((firstVersion & ATOMIC_LOCK_FLAG) != 0 ||
+ android_atomic_release_cas(
+ firstVersion, firstVersion | ATOMIC_LOCK_FLAG,
+ (volatile s4*) &pEntry->version) != 0)
+ {
+ /*
+ * We couldn't get the write lock. Return without updating the table.
+ */
+#if CALC_CACHE_STATS > 0
+ pCache->fail++;
+#endif
+ return;
+ }
+
+ /* must be even-valued on entry */
+ assert((firstVersion & 0x01) == 0);
+
+#if CALC_CACHE_STATS > 0
+ /* for stats, assume a key value of zero indicates an empty entry */
+ if (pEntry->key1 == 0)
+ pCache->fills++;
+ else
+ pCache->misses++;
+#endif
+
+ /*
+ * We have the write lock, but somebody could be reading this entry
+ * while we work. We use memory barriers to ensure that the state
+ * is always consistent when the version number is even.
+ */
+ u4 newVersion = (firstVersion | ATOMIC_LOCK_FLAG) + 1;
+ assert((newVersion & 0x01) == 1);
+
+ pEntry->version = newVersion;
+
+ android_atomic_release_store(key1, (int32_t*) &pEntry->key1);
+ pEntry->key2 = key2;
+ pEntry->value = value;
+
+ newVersion++;
+ android_atomic_release_store(newVersion, (int32_t*) &pEntry->version);
+
+ /*
+ * Clear the lock flag. Nobody else should have been able to modify
+ * pEntry->version, so if this fails the world is broken.
+ */
+ assert(newVersion == ((firstVersion + 2) | ATOMIC_LOCK_FLAG));
+ if (android_atomic_release_cas(
+ newVersion, newVersion & ~ATOMIC_LOCK_FLAG,
+ (volatile s4*) &pEntry->version) != 0)
+ {
+ //LOGE("unable to reset the instanceof cache ownership\n");
+ dvmAbort();
+ }
+}
+
+
+/*
+ * Dump the "instanceof" cache stats.
+ */
+void dvmDumpAtomicCacheStats(const AtomicCache* pCache)
+{
+ if (pCache == NULL)
+ return;
+ dvmFprintf(stdout,
+ "Cache stats: trv=%d fai=%d hit=%d mis=%d fil=%d %d%% (size=%d)\n",
+ pCache->trivial, pCache->fail, pCache->hits,
+ pCache->misses, pCache->fills,
+ (pCache->hits == 0) ? 0 :
+ pCache->hits * 100 /
+ (pCache->fail + pCache->hits + pCache->misses + pCache->fills),
+ pCache->numEntries);
+}
diff --git a/vm/AtomicCache.h b/vm/AtomicCache.h
new file mode 100644
index 0000000..686eac5
--- /dev/null
+++ b/vm/AtomicCache.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Mutex-free cache for key1+key2=value.
+ */
+#ifndef _DALVIK_ATOMICCACHE
+#define _DALVIK_ATOMICCACHE
+
+/*
+ * If set to "1", gather some stats on our caching success rate.
+ */
+#define CALC_CACHE_STATS 0
+
+
+/*
+ * One entry in the cache. We store two keys (e.g. the classes that are
+ * arguments to "instanceof") and one result (e.g. a boolean value).
+ *
+ * Must be exactly 16 bytes.
+ */
+typedef struct AtomicCacheEntry {
+ u4 key1;
+ u4 key2;
+ u4 value;
+ volatile u4 version; /* version and lock flag */
+} AtomicCacheEntry;
+
+/*
+ * One cache.
+ *
+ * Thought: we might be able to save a few cycles by storing the cache
+ * struct and "entries" separately, avoiding an indirection. (We already
+ * handle "numEntries" separately in ATOMIC_CACHE_LOOKUP.)
+ */
+typedef struct AtomicCache {
+ AtomicCacheEntry* entries; /* array of entries */
+ int numEntries; /* #of entries, must be power of 2 */
+
+ void* entryAlloc; /* memory allocated for entries */
+
+ /* cache stats; note we don't guarantee atomic increments for these */
+ int trivial; /* cache access not required */
+ int fail; /* contention failure */
+ int hits; /* found entry in cache */
+ int misses; /* entry was for other keys */
+ int fills; /* entry was empty */
+} AtomicCache;
+
+/*
+ * Do a cache lookup. We need to be able to read and write entries
+ * atomically. There are a couple of ways to do this:
+ * (1) Have a global lock. A mutex is too heavy, so instead we would use
+ * an atomic flag. If the flag is set, we could sit and spin,
+ * but if we're a high-priority thread that may cause a lockup.
+ * Better to just ignore the cache and do the full computation.
+ * (2) Have a "version" that gets incremented atomically when a write
+ * begins and again when it completes. Compare the version before
+ * and after doing reads. So long as we have memory barriers in the
+ * right place the compiler and CPU will do the right thing, allowing
+ * us to skip atomic ops in the common read case. The table updates
+ * are expensive, requiring two writes with barriers. We also need
+ * some sort of lock to ensure that nobody else tries to start an
+ * update while we're in the middle of one.
+ *
+ * We expect a 95+% hit rate for the things we use this for, so #2 is
+ * much better than #1.
+ *
+ * _cache is an AtomicCache*
+ * _cacheSize is _cache->cacheSize (can save a cycle avoiding the lookup)
+ * _key1, _key2 are the keys
+ *
+ * Define a function ATOMIC_CACHE_CALC that returns a 32-bit value. This
+ * will be invoked when we need to compute the value.
+ *
+ * Returns the value.
+ */
+#if CALC_CACHE_STATS > 0
+# define CACHE_XARG(_value) ,_value
+#else
+# define CACHE_XARG(_value)
+#endif
+#define ATOMIC_CACHE_LOOKUP(_cache, _cacheSize, _key1, _key2) ({ \
+ AtomicCacheEntry* pEntry; \
+ int hash; \
+ u4 firstVersion, secondVersion; \
+ u4 value; \
+ \
+ /* simple hash function */ \
+ hash = (((u4)(_key1) >> 2) ^ (u4)(_key2)) & ((_cacheSize)-1); \
+ pEntry = (_cache)->entries + hash; \
+ \
+ firstVersion = android_atomic_acquire_load((int32_t*)&pEntry->version); \
+ \
+ if (pEntry->key1 == (u4)(_key1) && pEntry->key2 == (u4)(_key2)) { \
+ /* \
+ * The fields match. Get the value, then read the version a \
+ * second time to verify that we didn't catch a partial update. \
+ * We're also hosed if "firstVersion" was odd, indicating that \
+ * an update was in progress before we got here (unlikely). \
+ */ \
+ value = android_atomic_acquire_load((int32_t*) &pEntry->value); \
+ secondVersion = pEntry->version; \
+ \
+ if ((firstVersion & 0x01) != 0 || firstVersion != secondVersion) { \
+ /* \
+ * We clashed with another thread. Instead of sitting and \
+ * spinning, which might not complete if we're a high priority \
+ * thread, just do the regular computation. \
+ */ \
+ if (CALC_CACHE_STATS) \
+ (_cache)->fail++; \
+ value = (u4) ATOMIC_CACHE_CALC; \
+ } else { \
+ /* all good */ \
+ if (CALC_CACHE_STATS) \
+ (_cache)->hits++; \
+ } \
+ } else { \
+ /* \
+ * Compute the result and update the cache. We really want this \
+ * to happen in a different method -- it makes the ARM frame \
+ * setup for this method simpler, which gives us a ~10% speed \
+ * boost. \
+ */ \
+ value = (u4) ATOMIC_CACHE_CALC; \
+ dvmUpdateAtomicCache((u4) (_key1), (u4) (_key2), value, pEntry, \
+ firstVersion CACHE_XARG(_cache) ); \
+ } \
+ value; \
+})
+
+/*
+ * Allocate a cache.
+ */
+AtomicCache* dvmAllocAtomicCache(int numEntries);
+
+/*
+ * Free a cache.
+ */
+void dvmFreeAtomicCache(AtomicCache* cache);
+
+/*
+ * Update a cache entry.
+ *
+ * Making the last argument optional, instead of merely unused, saves us
+ * a few percent in the ATOMIC_CACHE_LOOKUP time.
+ */
+void dvmUpdateAtomicCache(u4 key1, u4 key2, u4 value, AtomicCacheEntry* pEntry,
+ u4 firstVersion
+#if CALC_CACHE_STATS > 0
+ , AtomicCache* pCache
+#endif
+ );
+
+/*
+ * Debugging.
+ */
+void dvmDumpAtomicCacheStats(const AtomicCache* pCache);
+
+#endif /*_DALVIK_ATOMICCACHE*/
diff --git a/vm/Bits.h b/vm/Bits.h
new file mode 100644
index 0000000..38b016d
--- /dev/null
+++ b/vm/Bits.h
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Some handy functions for manipulating bits and bytes.
+ *
+ * These get inlined, so prefer small size over maximum speed.
+ */
+#ifndef _DALVIK_BITS
+#define _DALVIK_BITS
+
+#include "Common.h"
+#include "Inlines.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * Get 1 byte. (Included to make the code more legible.)
+ */
+INLINE u1 get1(unsigned const char* pSrc)
+{
+ return *pSrc;
+}
+
+/*
+ * Get 2 big-endian bytes.
+ */
+INLINE u2 get2BE(unsigned char const* pSrc)
+{
+ return (pSrc[0] << 8) | pSrc[1];
+}
+
+/*
+ * Get 4 big-endian bytes.
+ */
+INLINE u4 get4BE(unsigned char const* pSrc)
+{
+ return (pSrc[0] << 24) | (pSrc[1] << 16) | (pSrc[2] << 8) | pSrc[3];
+}
+
+/*
+ * Get 8 big-endian bytes.
+ */
+INLINE u8 get8BE(unsigned char const* pSrc)
+{
+ u4 low, high;
+
+ high = pSrc[0];
+ high = (high << 8) | pSrc[1];
+ high = (high << 8) | pSrc[2];
+ high = (high << 8) | pSrc[3];
+ low = pSrc[4];
+ low = (low << 8) | pSrc[5];
+ low = (low << 8) | pSrc[6];
+ low = (low << 8) | pSrc[7];
+
+ return ((u8) high << 32) | (u8) low;
+}
+
+/*
+ * Get 2 little-endian bytes.
+ */
+INLINE u2 get2LE(unsigned char const* pSrc)
+{
+ return pSrc[0] | (pSrc[1] << 8);
+}
+
+/*
+ * Get 4 little-endian bytes.
+ */
+INLINE u4 get4LE(unsigned char const* pSrc)
+{
+ u4 result;
+
+ result = pSrc[0];
+ result |= pSrc[1] << 8;
+ result |= pSrc[2] << 16;
+ result |= pSrc[3] << 24;
+
+ return result;
+}
+
+/*
+ * Get 8 little-endian bytes.
+ */
+INLINE u8 get8LE(unsigned char const* pSrc)
+{
+ u4 low, high;
+
+ low = pSrc[0];
+ low |= pSrc[1] << 8;
+ low |= pSrc[2] << 16;
+ low |= pSrc[3] << 24;
+ high = pSrc[4];
+ high |= pSrc[5] << 8;
+ high |= pSrc[6] << 16;
+ high |= pSrc[7] << 24;
+ return ((u8) high << 32) | (u8) low;
+}
+
+/*
+ * Grab 1 byte and advance the data pointer.
+ */
+INLINE u1 read1(unsigned const char** ppSrc)
+{
+ return *(*ppSrc)++;
+}
+
+/*
+ * Grab 2 big-endian bytes and advance the data pointer.
+ */
+INLINE u2 read2BE(unsigned char const** ppSrc)
+{
+ const unsigned char* pSrc = *ppSrc;
+
+ *ppSrc = pSrc + 2;
+ return pSrc[0] << 8 | pSrc[1];
+}
+
+/*
+ * Grab 4 big-endian bytes and advance the data pointer.
+ */
+INLINE u4 read4BE(unsigned char const** ppSrc)
+{
+ const unsigned char* pSrc = *ppSrc;
+ u4 result;
+
+ result = pSrc[0] << 24;
+ result |= pSrc[1] << 16;
+ result |= pSrc[2] << 8;
+ result |= pSrc[3];
+
+ *ppSrc = pSrc + 4;
+ return result;
+}
+
+/*
+ * Get 8 big-endian bytes and advance the data pointer.
+ */
+INLINE u8 read8BE(unsigned char const** ppSrc)
+{
+ const unsigned char* pSrc = *ppSrc;
+ u4 low, high;
+
+ high = pSrc[0];
+ high = (high << 8) | pSrc[1];
+ high = (high << 8) | pSrc[2];
+ high = (high << 8) | pSrc[3];
+ low = pSrc[4];
+ low = (low << 8) | pSrc[5];
+ low = (low << 8) | pSrc[6];
+ low = (low << 8) | pSrc[7];
+
+ *ppSrc = pSrc + 8;
+ return ((u8) high << 32) | (u8) low;
+}
+
+/*
+ * Grab 2 little-endian bytes and advance the data pointer.
+ */
+INLINE u2 read2LE(unsigned char const** ppSrc)
+{
+ const unsigned char* pSrc = *ppSrc;
+ *ppSrc = pSrc + 2;
+ return pSrc[0] | pSrc[1] << 8;
+}
+
+/*
+ * Grab 4 little-endian bytes and advance the data pointer.
+ */
+INLINE u4 read4LE(unsigned char const** ppSrc)
+{
+ const unsigned char* pSrc = *ppSrc;
+ u4 result;
+
+ result = pSrc[0];
+ result |= pSrc[1] << 8;
+ result |= pSrc[2] << 16;
+ result |= pSrc[3] << 24;
+
+ *ppSrc = pSrc + 4;
+ return result;
+}
+
+/*
+ * Get 8 little-endian bytes and advance the data pointer.
+ */
+INLINE u8 read8LE(unsigned char const** ppSrc)
+{
+ const unsigned char* pSrc = *ppSrc;
+ u4 low, high;
+
+ low = pSrc[0];
+ low |= pSrc[1] << 8;
+ low |= pSrc[2] << 16;
+ low |= pSrc[3] << 24;
+ high = pSrc[4];
+ high |= pSrc[5] << 8;
+ high |= pSrc[6] << 16;
+ high |= pSrc[7] << 24;
+
+ *ppSrc = pSrc + 8;
+ return ((u8) high << 32) | (u8) low;
+}
+
+/*
+ * Skip over a UTF-8 string (preceded by a 4-byte length).
+ */
+INLINE void skipUtf8String(unsigned char const** ppSrc)
+{
+ u4 length = read4BE(ppSrc);
+
+ (*ppSrc) += length;
+}
+
+/*
+ * Read a UTF-8 string into a fixed-size buffer, and null-terminate it.
+ *
+ * Returns the length of the original string.
+ */
+INLINE int readUtf8String(unsigned char const** ppSrc, char* buf, size_t bufLen)
+{
+ u4 length = read4BE(ppSrc);
+ size_t copyLen = (length < bufLen) ? length : bufLen-1;
+
+ memcpy(buf, *ppSrc, copyLen);
+ buf[copyLen] = '\0';
+
+ (*ppSrc) += length;
+ return length;
+}
+
+/*
+ * Read a UTF-8 string into newly-allocated storage, and null-terminate it.
+ *
+ * Returns the string and its length. (The latter is probably unnecessary
+ * for the way we're using UTF8.)
+ */
+INLINE char* readNewUtf8String(unsigned char const** ppSrc, size_t* pLength)
+{
+ u4 length = read4BE(ppSrc);
+ char* buf;
+
+ buf = (char*) malloc(length+1);
+
+ memcpy(buf, *ppSrc, length);
+ buf[length] = '\0';
+
+ (*ppSrc) += length;
+
+ *pLength = length;
+ return buf;
+}
+
+
+/*
+ * Set 1 byte. (Included to make code more consistent/legible.)
+ */
+INLINE void set1(u1* buf, u1 val)
+{
+ *buf = (u1)(val);
+}
+
+/*
+ * Set 2 big-endian bytes.
+ */
+INLINE void set2BE(u1* buf, u2 val)
+{
+ *buf++ = (u1)(val >> 8);
+ *buf = (u1)(val);
+}
+
+/*
+ * Set 4 big-endian bytes.
+ */
+INLINE void set4BE(u1* buf, u4 val)
+{
+ *buf++ = (u1)(val >> 24);
+ *buf++ = (u1)(val >> 16);
+ *buf++ = (u1)(val >> 8);
+ *buf = (u1)(val);
+}
+
+/*
+ * Set 8 big-endian bytes.
+ */
+INLINE void set8BE(u1* buf, u8 val)
+{
+ *buf++ = (u1)(val >> 56);
+ *buf++ = (u1)(val >> 48);
+ *buf++ = (u1)(val >> 40);
+ *buf++ = (u1)(val >> 32);
+ *buf++ = (u1)(val >> 24);
+ *buf++ = (u1)(val >> 16);
+ *buf++ = (u1)(val >> 8);
+ *buf = (u1)(val);
+}
+
+/*
+ * Set 2 little-endian bytes.
+ */
+INLINE void set2LE(u1* buf, u2 val)
+{
+ *buf++ = (u1)(val);
+ *buf = (u1)(val >> 8);
+}
+
+/*
+ * Set 4 little-endian bytes.
+ */
+INLINE void set4LE(u1* buf, u4 val)
+{
+ *buf++ = (u1)(val);
+ *buf++ = (u1)(val >> 8);
+ *buf++ = (u1)(val >> 16);
+ *buf = (u1)(val >> 24);
+}
+
+/*
+ * Set 8 little-endian bytes.
+ */
+INLINE void set8LE(u1* buf, u8 val)
+{
+ *buf++ = (u1)(val);
+ *buf++ = (u1)(val >> 8);
+ *buf++ = (u1)(val >> 16);
+ *buf++ = (u1)(val >> 24);
+ *buf++ = (u1)(val >> 32);
+ *buf++ = (u1)(val >> 40);
+ *buf++ = (u1)(val >> 48);
+ *buf = (u1)(val >> 56);
+}
+
+/*
+ * Stuff a UTF-8 string into the buffer.
+ */
+INLINE void setUtf8String(u1* buf, const u1* str)
+{
+ u4 strLen = strlen((const char*)str);
+
+ set4BE(buf, strLen);
+ memcpy(buf + sizeof(u4), str, strLen);
+}
+
+#endif /*_DALVIK_BITS*/
diff --git a/vm/CheckJni.c b/vm/CheckJni.c
new file mode 100644
index 0000000..59d34eb
--- /dev/null
+++ b/vm/CheckJni.c
@@ -0,0 +1,2810 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Support for -Xcheck:jni (the "careful" version of the JNI interfaces).
+ *
+ * We want to verify types, make sure class and field IDs are valid, and
+ * ensure that JNI's semantic expectations are being met. JNI seems to
+ * be relatively lax when it comes to requirements for permission checks,
+ * e.g. access to private methods is generally allowed from anywhere.
+ *
+ * TODO: keep a counter on global Get/Release. Report a warning if some Gets
+ * were not Released. Do not count explicit Add/DeleteGlobalRef calls (or
+ * count them separately, so we can complain if they exceed a certain
+ * threshold).
+ *
+ * TODO: verify that the methodID passed into the Call functions is for
+ * a method in the specified class.
+ */
+#include "Dalvik.h"
+#include "JniInternal.h"
+
+#include <zlib.h>
+
+static void abortMaybe(void); // fwd
+
+
+/*
+ * ===========================================================================
+ * JNI call bridge wrapper
+ * ===========================================================================
+ */
+
+/*
+ * Check the result of a native method call that returns an object reference.
+ *
+ * The primary goal here is to verify that native code is returning the
+ * correct type of object. If it's declared to return a String but actually
+ * returns a byte array, things will fail in strange ways later on.
+ *
+ * This can be a fairly expensive operation, since we have to look up the
+ * return type class by name in method->clazz' class loader. We take a
+ * shortcut here and allow the call to succeed if the descriptor strings
+ * match. This will allow some false-positives when a class is redefined
+ * by a class loader, but that's rare enough that it doesn't seem worth
+ * testing for.
+ *
+ * At this point, pResult->l has already been converted to an object pointer.
+ */
+static void checkCallResultCommon(const u4* args, JValue* pResult,
+ const Method* method, Thread* self)
+{
+ assert(pResult->l != NULL);
+ Object* resultObj = (Object*) pResult->l;
+ ClassObject* objClazz = resultObj->clazz;
+
+ /*
+ * Make sure that pResult->l is an instance of the type this
+ * method was expected to return.
+ */
+ const char* declType = dexProtoGetReturnType(&method->prototype);
+ const char* objType = objClazz->descriptor;
+ if (strcmp(declType, objType) == 0) {
+ /* names match; ignore class loader issues and allow it */
+ LOGV("Check %s.%s: %s io %s (FAST-OK)\n",
+ method->clazz->descriptor, method->name, objType, declType);
+ } else {
+ /*
+ * Names didn't match. We need to resolve declType in the context
+ * of method->clazz->classLoader, and compare the class objects
+ * for equality.
+ *
+ * Since we're returning an instance of declType, it's safe to
+ * assume that it has been loaded and initialized (or, for the case
+ * of an array, generated). However, the current class loader may
+ * not be listed as an initiating loader, so we can't just look for
+ * it in the loaded-classes list.
+ */
+ ClassObject* declClazz;
+
+ declClazz = dvmFindClassNoInit(declType, method->clazz->classLoader);
+ if (declClazz == NULL) {
+ LOGW("JNI WARNING: method declared to return '%s' returned '%s'\n",
+ declType, objType);
+ LOGW(" failed in %s.%s ('%s' not found)\n",
+ method->clazz->descriptor, method->name, declType);
+ abortMaybe();
+ return;
+ }
+ if (!dvmInstanceof(objClazz, declClazz)) {
+ LOGW("JNI WARNING: method declared to return '%s' returned '%s'\n",
+ declType, objType);
+ LOGW(" failed in %s.%s\n",
+ method->clazz->descriptor, method->name);
+ abortMaybe();
+ return;
+ } else {
+ LOGV("Check %s.%s: %s io %s (SLOW-OK)\n",
+ method->clazz->descriptor, method->name, objType, declType);
+ }
+ }
+}
+
+/*
+ * Determine if we need to check the return type coming out of the call.
+ *
+ * (We don't do this at the top of checkCallResultCommon() because this is on
+ * the critical path for native method calls.)
+ */
+static inline bool callNeedsCheck(const u4* args, JValue* pResult,
+ const Method* method, Thread* self)
+{
+ return (method->shorty[0] == 'L' && !dvmCheckException(self) &&
+ pResult->l != NULL);
+}
+
+/*
+ * Check a call into native code.
+ */
+void dvmCheckCallJNIMethod_general(const u4* args, JValue* pResult,
+ const Method* method, Thread* self)
+{
+ dvmCallJNIMethod_general(args, pResult, method, self);
+ if (callNeedsCheck(args, pResult, method, self))
+ checkCallResultCommon(args, pResult, method, self);
+}
+
+/*
+ * Check a synchronized call into native code.
+ */
+void dvmCheckCallJNIMethod_synchronized(const u4* args, JValue* pResult,
+ const Method* method, Thread* self)
+{
+ dvmCallJNIMethod_synchronized(args, pResult, method, self);
+ if (callNeedsCheck(args, pResult, method, self))
+ checkCallResultCommon(args, pResult, method, self);
+}
+
+/*
+ * Check a virtual call with no reference arguments (other than "this").
+ */
+void dvmCheckCallJNIMethod_virtualNoRef(const u4* args, JValue* pResult,
+ const Method* method, Thread* self)
+{
+ dvmCallJNIMethod_virtualNoRef(args, pResult, method, self);
+ if (callNeedsCheck(args, pResult, method, self))
+ checkCallResultCommon(args, pResult, method, self);
+}
+
+/*
+ * Check a static call with no reference arguments (other than "clazz").
+ */
+void dvmCheckCallJNIMethod_staticNoRef(const u4* args, JValue* pResult,
+ const Method* method, Thread* self)
+{
+ dvmCallJNIMethod_staticNoRef(args, pResult, method, self);
+ if (callNeedsCheck(args, pResult, method, self))
+ checkCallResultCommon(args, pResult, method, self);
+}
+
+
+/*
+ * ===========================================================================
+ * JNI function helpers
+ * ===========================================================================
+ */
+
+#define JNI_ENTER() dvmChangeStatus(NULL, THREAD_RUNNING)
+#define JNI_EXIT() dvmChangeStatus(NULL, THREAD_NATIVE)
+
+#define BASE_ENV(_env) (((JNIEnvExt*)_env)->baseFuncTable)
+#define BASE_VM(_vm) (((JavaVMExt*)_vm)->baseFuncTable)
+
+#define kRedundantDirectBufferTest false
+
+/*
+ * Flags passed into checkThread().
+ */
+#define kFlag_Default 0x0000
+
+#define kFlag_CritBad 0x0000 /* calling while in critical is bad */
+#define kFlag_CritOkay 0x0001 /* ...okay */
+#define kFlag_CritGet 0x0002 /* this is a critical "get" */
+#define kFlag_CritRelease 0x0003 /* this is a critical "release" */
+#define kFlag_CritMask 0x0003 /* bit mask to get "crit" value */
+
+#define kFlag_ExcepBad 0x0000 /* raised exceptions are bad */
+#define kFlag_ExcepOkay 0x0004 /* ...okay */
+
+/*
+ * Enter/exit macros for JNI env "check" functions. These do not change
+ * the thread state within the VM.
+ */
+#define CHECK_ENTER(_env, _flags) \
+ do { \
+ JNI_TRACE(true, true); \
+ checkThread(_env, _flags, __FUNCTION__); \
+ } while(false)
+
+#define CHECK_EXIT(_env) \
+ do { JNI_TRACE(false, true); } while(false)
+
+
+/*
+ * Enter/exit macros for JNI invocation interface "check" functions. These
+ * do not change the thread state within the VM.
+ *
+ * Set "_hasmeth" to true if we have a valid thread with a method pointer.
+ * We won't have one before attaching a thread, after detaching a thread, or
+ * after destroying the VM.
+ */
+#define CHECK_VMENTER(_vm, _hasmeth) \
+ do { JNI_TRACE(true, _hasmeth); } while(false)
+#define CHECK_VMEXIT(_vm, _hasmeth) \
+ do { JNI_TRACE(false, _hasmeth); } while(false)
+
+#define CHECK_FIELD_TYPE(_env, _obj, _fieldid, _prim, _isstatic) \
+ checkFieldType(_env, _obj, _fieldid, _prim, _isstatic, __FUNCTION__)
+#define CHECK_INST_FIELD_ID(_env, _obj, _fieldid) \
+ checkInstanceFieldID(_env, _obj, _fieldid, __FUNCTION__)
+#define CHECK_CLASS(_env, _clazz) \
+ checkClass(_env, _clazz, __FUNCTION__)
+#define CHECK_STRING(_env, _str) \
+ checkString(_env, _str, __FUNCTION__)
+#define CHECK_UTF_STRING(_env, _str, _nullok) \
+ checkUtfString(_env, _str, _nullok, __FUNCTION__)
+#define CHECK_CLASS_NAME(_env, _str) \
+ checkClassName(_env, _str, __FUNCTION__)
+#define CHECK_OBJECT(_env, _obj) \
+ checkObject(_env, _obj, __FUNCTION__)
+#define CHECK_ARRAY(_env, _array) \
+ checkArray(_env, _array, __FUNCTION__)
+#define CHECK_RELEASE_MODE(_env, _mode) \
+ checkReleaseMode(_env, _mode, __FUNCTION__)
+#define CHECK_LENGTH_POSITIVE(_env, _length) \
+ checkLengthPositive(_env, _length, __FUNCTION__)
+#define CHECK_NON_NULL(_env, _ptr) \
+ checkNonNull(_env, _ptr, __FUNCTION__)
+#define CHECK_SIG(_env, _methid, _sigbyte, _isstatic) \
+ checkSig(_env, _methid, _sigbyte, _isstatic, __FUNCTION__)
+#define CHECK_VIRTUAL_METHOD(_env, _obj, _methid) \
+ checkVirtualMethod(_env, _obj, _methid, __FUNCTION__)
+#define CHECK_STATIC_METHOD(_env, _clazz, _methid) \
+ checkStaticMethod(_env, _clazz, _methid, __FUNCTION__)
+#define CHECK_METHOD_ARGS_A(_env, _methid, _args) \
+ checkMethodArgsA(_env, _methid, _args, __FUNCTION__)
+#define CHECK_METHOD_ARGS_V(_env, _methid, _args) \
+ checkMethodArgsV(_env, _methid, _args, __FUNCTION__)
+
+/*
+ * Prints trace messages when a native method calls a JNI function such as
+ * NewByteArray. Enabled if both "-Xcheck:jni" and "-verbose:jni" are enabled.
+ */
+#define JNI_TRACE(_entry, _hasmeth) \
+ do { \
+ if (gDvm.verboseJni && (_entry)) { \
+ static const char* classDescriptor = "???"; \
+ static const char* methodName = "???"; \
+ if (_hasmeth) { \
+ const Method* meth = dvmGetCurrentJNIMethod(); \
+ classDescriptor = meth->clazz->descriptor; \
+ methodName = meth->name; \
+ } \
+ /* use +6 to drop the leading "Check_" */ \
+ LOGI("JNI: %s (from %s.%s)", \
+ (__FUNCTION__)+6, classDescriptor, methodName); \
+ } \
+ } while(false)
+
+/*
+ * Log the current location.
+ *
+ * "func" looks like "Check_DeleteLocalRef"; we drop the "Check_".
+ */
+static void showLocation(const Method* meth, const char* func)
+{
+ char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+ LOGW(" in %s.%s %s (%s)\n",
+ meth->clazz->descriptor, meth->name, desc, func + 6);
+ free(desc);
+}
+
+/*
+ * Abort if we are configured to bail out on JNI warnings.
+ */
+static void abortMaybe(void)
+{
+ JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
+ if (vm->warnError) {
+ dvmDumpThread(dvmThreadSelf(), false);
+ dvmAbort();
+ }
+}
+
+/*
+ * Verify that the current thread is (a) attached and (b) associated with
+ * this particular instance of JNIEnv.
+ *
+ * Verify that, if this thread previously made a critical "get" call, we
+ * do the corresponding "release" call before we try anything else.
+ *
+ * Verify that, if an exception has been raised, the native code doesn't
+ * make any JNI calls other than the Exception* methods.
+ *
+ * TODO? if we add support for non-JNI native calls, make sure that the
+ * method at the top of the interpreted stack is a JNI method call. (Or
+ * set a flag in the Thread/JNIEnv when the call is made and clear it on
+ * return?)
+ *
+ * NOTE: we are still in THREAD_NATIVE mode. A GC could happen at any time.
+ */
+static void checkThread(JNIEnv* env, int flags, const char* func)
+{
+ JNIEnvExt* threadEnv;
+ bool printWarn = false;
+ bool printException = false;
+
+ /* get the *correct* JNIEnv by going through our TLS pointer */
+ threadEnv = dvmGetJNIEnvForThread();
+
+ /*
+ * Verify that the JNIEnv we've been handed matches what we expected
+ * to receive.
+ */
+ if (threadEnv == NULL) {
+ LOGE("JNI ERROR: non-VM thread making JNI calls\n");
+ // don't set printWarn -- it'll try to call showLocation()
+ dvmAbort();
+ } else if ((JNIEnvExt*) env != threadEnv) {
+ if (dvmThreadSelf()->threadId != threadEnv->envThreadId) {
+ LOGE("JNI: threadEnv != thread->env?\n");
+ dvmAbort();
+ }
+
+ LOGW("JNI WARNING: threadid=%d using env from threadid=%d\n",
+ threadEnv->envThreadId, ((JNIEnvExt*)env)->envThreadId);
+ printWarn = true;
+
+ /* this is a bad idea -- need to throw as we exit, or abort func */
+ //dvmThrowException("Ljava/lang/RuntimeException;",
+ // "invalid use of JNI env ptr");
+ } else if (((JNIEnvExt*) env)->self != dvmThreadSelf()) {
+ /* correct JNIEnv*; make sure the "self" pointer is correct */
+ LOGE("JNI ERROR: env->self != thread-self (%p vs. %p)\n",
+ ((JNIEnvExt*) env)->self, dvmThreadSelf());
+ dvmAbort();
+ }
+
+ /*
+ * Check for critical resource misuse.
+ */
+ switch (flags & kFlag_CritMask) {
+ case kFlag_CritOkay: // okay to call this method
+ break;
+ case kFlag_CritBad: // not okay to call
+ if (threadEnv->critical) {
+ LOGW("JNI WARNING: threadid=%d using JNI after critical get\n",
+ threadEnv->envThreadId);
+ printWarn = true;
+ }
+ break;
+ case kFlag_CritGet: // this is a "get" call
+ /* don't check here; we allow nested gets */
+ threadEnv->critical++;
+ break;
+ case kFlag_CritRelease: // this is a "release" call
+ threadEnv->critical--;
+ if (threadEnv->critical < 0) {
+ LOGW("JNI WARNING: threadid=%d called too many crit releases\n",
+ threadEnv->envThreadId);
+ printWarn = true;
+ }
+ break;
+ default:
+ assert(false);
+ }
+
+ /*
+ * Check for raised exceptions.
+ */
+ if ((flags & kFlag_ExcepOkay) == 0 && dvmCheckException(dvmThreadSelf())) {
+ LOGW("JNI WARNING: JNI method called with exception raised\n");
+ printWarn = true;
+ printException = true;
+ }
+
+ if (printWarn)
+ showLocation(dvmGetCurrentJNIMethod(), func);
+ if (printException) {
+ LOGW("Pending exception is:\n");
+ dvmLogExceptionStackTrace();
+ }
+ if (printWarn)
+ abortMaybe();
+}
+
+/*
+ * Verify that the field is of the appropriate type. If the field has an
+ * object type, "obj" is the object we're trying to assign into it.
+ *
+ * Works for both static and instance fields.
+ */
+static void checkFieldType(JNIEnv* env, jobject jobj, jfieldID fieldID,
+ PrimitiveType prim, bool isStatic, const char* func)
+{
+ static const char* primNameList[] = {
+ "Object/Array", "boolean", "char", "float", "double",
+ "byte", "short", "int", "long", "void"
+ };
+ const char** primNames = &primNameList[1]; // shift up for PRIM_NOT
+ Field* field = (Field*) fieldID;
+ bool printWarn = false;
+
+ if (fieldID == NULL) {
+ LOGE("JNI ERROR: null field ID\n");
+ abortMaybe();
+ }
+
+ if (field->signature[0] == 'L' || field->signature[0] == '[') {
+ Object* obj = dvmDecodeIndirectRef(env, jobj);
+ if (obj != NULL) {
+ ClassObject* fieldClass =
+ dvmFindLoadedClass(field->signature);
+ ClassObject* objClass = obj->clazz;
+
+ assert(fieldClass != NULL);
+ assert(objClass != NULL);
+
+ if (!dvmInstanceof(objClass, fieldClass)) {
+ LOGW("JNI WARNING: field '%s' with type '%s' set with wrong type (%s)\n",
+ field->name, field->signature, objClass->descriptor);
+ printWarn = true;
+ }
+ }
+ } else if (field->signature[0] != PRIM_TYPE_TO_LETTER[prim]) {
+ LOGW("JNI WARNING: field '%s' with type '%s' set with wrong type (%s)\n",
+ field->name, field->signature, primNames[prim]);
+ printWarn = true;
+ } else if (isStatic && !dvmIsStaticField(field)) {
+ if (isStatic)
+ LOGW("JNI WARNING: accessing non-static field %s as static\n",
+ field->name);
+ else
+ LOGW("JNI WARNING: accessing static field %s as non-static\n",
+ field->name);
+ printWarn = true;
+ }
+
+ if (printWarn) {
+ showLocation(dvmGetCurrentJNIMethod(), func);
+ abortMaybe();
+ }
+}
+
+/*
+ * Verify that "jobj" is a valid object, and that it's an object that JNI
+ * is allowed to know about. We allow NULL references.
+ *
+ * Must be in "running" mode before calling here.
+ */
+static void checkObject0(JNIEnv* env, jobject jobj, const char* func)
+{
+ UNUSED_PARAMETER(env);
+ bool printWarn = false;
+
+ if (jobj == NULL)
+ return;
+
+ if (dvmIsWeakGlobalRef(jobj)) {
+ /*
+ * Normalize and continue. This will tell us if the PhantomReference
+ * object is valid.
+ */
+ jobj = dvmNormalizeWeakGlobalRef((jweak) jobj);
+ }
+
+ if (dvmGetJNIRefType(env, jobj) == JNIInvalidRefType) {
+ LOGW("JNI WARNING: %p is not a valid JNI reference\n", jobj);
+ printWarn = true;
+ } else {
+ Object* obj = dvmDecodeIndirectRef(env, jobj);
+
+ if (obj == NULL || !dvmIsValidObject(obj)) {
+ LOGW("JNI WARNING: native code passing in bad object %p %p (%s)\n",
+ jobj, obj, func);
+ printWarn = true;
+ }
+ }
+
+ if (printWarn) {
+ showLocation(dvmGetCurrentJNIMethod(), func);
+ abortMaybe();
+ }
+}
+
+/*
+ * Verify that "jobj" is a valid object, and that it's an object that JNI
+ * is allowed to know about. We allow NULL references.
+ *
+ * Switches to "running" mode before performing checks.
+ */
+static void checkObject(JNIEnv* env, jobject jobj, const char* func)
+{
+ JNI_ENTER();
+ checkObject0(env, jobj, func);
+ JNI_EXIT();
+}
+
+/*
+ * Verify that "clazz" actually points to a class object. (Also performs
+ * checkObject.)
+ *
+ * We probably don't need to identify where we're being called from,
+ * because the VM is most likely about to crash and leave a core dump
+ * if something is wrong.
+ *
+ * Because we're looking at an object on the GC heap, we have to switch
+ * to "running" mode before doing the checks.
+ */
+static void checkClass(JNIEnv* env, jclass jclazz, const char* func)
+{
+ JNI_ENTER();
+ bool printWarn = false;
+
+ Object* obj = dvmDecodeIndirectRef(env, jclazz);
+
+ if (obj == NULL) {
+ LOGW("JNI WARNING: received null jclass\n");
+ printWarn = true;
+ } else if (!dvmIsValidObject(obj)) {
+ LOGW("JNI WARNING: jclass points to invalid object %p\n", obj);
+ printWarn = true;
+ } else if (obj->clazz != gDvm.classJavaLangClass) {
+ LOGW("JNI WARNING: jclass arg is not a Class reference "
+ "(%p is instance of %s)\n",
+ jclazz, obj->clazz->descriptor);
+ printWarn = true;
+ }
+ JNI_EXIT();
+
+ if (printWarn)
+ abortMaybe();
+ else
+ checkObject(env, jclazz, func);
+}
+
+/*
+ * Verify that "str" is non-NULL and points to a String object.
+ *
+ * Since we're dealing with objects, switch to "running" mode.
+ */
+static void checkString(JNIEnv* env, jstring jstr, const char* func)
+{
+ JNI_ENTER();
+ bool printWarn = false;
+
+ Object* obj = dvmDecodeIndirectRef(env, jstr);
+
+ if (obj == NULL) {
+ LOGW("JNI WARNING: received null jstring (%s)\n", func);
+ printWarn = true;
+ } else if (obj->clazz != gDvm.classJavaLangString) {
+ /*
+ * TODO: we probably should test dvmIsValidObject first, because
+ * this will crash if "obj" is non-null but pointing to an invalid
+ * memory region. However, the "is valid" test is a little slow,
+ * we're doing it again over in checkObject().
+ */
+ if (dvmIsValidObject(obj))
+ LOGW("JNI WARNING: jstring %p points to non-string object (%s)\n",
+ jstr, func);
+ else
+ LOGW("JNI WARNING: jstring %p is bogus (%s)\n", jstr, func);
+ printWarn = true;
+ }
+ JNI_EXIT();
+
+ if (printWarn)
+ abortMaybe();
+ else
+ checkObject(env, jstr, func);
+}
+
+/*
+ * Verify that "bytes" points to valid "modified UTF-8" data.
+ */
+static void checkUtfString(JNIEnv* env, const char* bytes, bool nullOk,
+ const char* func)
+{
+ const char* origBytes = bytes;
+
+ if (bytes == NULL) {
+ if (!nullOk) {
+ LOGW("JNI WARNING: unexpectedly null UTF string\n");
+ goto fail;
+ }
+
+ return;
+ }
+
+ while (*bytes != '\0') {
+ u1 utf8 = *(bytes++);
+ // Switch on the high four bits.
+ switch (utf8 >> 4) {
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07: {
+ // Bit pattern 0xxx. No need for any extra bytes.
+ break;
+ }
+ case 0x08:
+ case 0x09:
+ case 0x0a:
+ case 0x0b:
+ case 0x0f: {
+ /*
+ * Bit pattern 10xx or 1111, which are illegal start bytes.
+ * Note: 1111 is valid for normal UTF-8, but not the
+ * modified UTF-8 used here.
+ */
+ LOGW("JNI WARNING: illegal start byte 0x%x\n", utf8);
+ goto fail;
+ }
+ case 0x0e: {
+ // Bit pattern 1110, so there are two additional bytes.
+ utf8 = *(bytes++);
+ if ((utf8 & 0xc0) != 0x80) {
+ LOGW("JNI WARNING: illegal continuation byte 0x%x\n", utf8);
+ goto fail;
+ }
+ // Fall through to take care of the final byte.
+ }
+ case 0x0c:
+ case 0x0d: {
+ // Bit pattern 110x, so there is one additional byte.
+ utf8 = *(bytes++);
+ if ((utf8 & 0xc0) != 0x80) {
+ LOGW("JNI WARNING: illegal continuation byte 0x%x\n", utf8);
+ goto fail;
+ }
+ break;
+ }
+ }
+ }
+
+ return;
+
+fail:
+ LOGW(" string: '%s'\n", origBytes);
+ showLocation(dvmGetCurrentJNIMethod(), func);
+ abortMaybe();
+}
+
+/*
+ * In some circumstances the VM will screen class names, but it doesn't
+ * for class lookup. When things get bounced through a class loader, they
+ * can actually get normalized a couple of times; as a result, passing in
+ * a class name like "java.lang.Thread" instead of "java/lang/Thread" will
+ * work in some circumstances.
+ *
+ * This is incorrect and could cause strange behavior or compatibility
+ * problems, so we want to screen that out here.
+ *
+ * We expect "full-qualified" class names, like "java/lang/Thread" or
+ * "[Ljava/lang/Object;".
+ */
+static void checkClassName(JNIEnv* env, const char* className, const char* func)
+{
+ const char* cp;
+
+ /* quick check for illegal chars */
+ cp = className;
+ while (*cp != '\0') {
+ if (*cp == '.') /* catch "java.lang.String" */
+ goto fail;
+ cp++;
+ }
+ if (*(cp-1) == ';' && *className == 'L')
+ goto fail; /* catch "Ljava/lang/String;" */
+
+ // TODO: need a more rigorous check here
+
+ return;
+
+fail:
+ LOGW("JNI WARNING: illegal class name '%s' (%s)\n", className, func);
+ LOGW(" (should be formed like 'java/lang/String')\n");
+ abortMaybe();
+}
+
+/*
+ * Verify that "array" is non-NULL and points to an Array object.
+ *
+ * Since we're dealing with objects, switch to "running" mode.
+ */
+static void checkArray(JNIEnv* env, jarray jarr, const char* func)
+{
+ JNI_ENTER();
+ bool printWarn = false;
+
+ Object* obj = dvmDecodeIndirectRef(env, jarr);
+
+ if (obj == NULL) {
+ LOGW("JNI WARNING: received null array (%s)\n", func);
+ printWarn = true;
+ } else if (obj->clazz->descriptor[0] != '[') {
+ if (dvmIsValidObject(obj))
+ LOGW("JNI WARNING: jarray %p points to non-array object (%s)\n",
+ jarr, obj->clazz->descriptor);
+ else
+ LOGW("JNI WARNING: jarray %p is bogus\n", jarr);
+ printWarn = true;
+ }
+
+ JNI_EXIT();
+
+ if (printWarn)
+ abortMaybe();
+ else
+ checkObject(env, jarr, func);
+}
+
+/*
+ * Verify that the "mode" argument passed to a primitive array Release
+ * function is one of the valid values.
+ */
+static void checkReleaseMode(JNIEnv* env, jint mode, const char* func)
+{
+ if (mode != 0 && mode != JNI_COMMIT && mode != JNI_ABORT) {
+ LOGW("JNI WARNING: bad value for mode (%d) (%s)\n", mode, func);
+ abortMaybe();
+ }
+}
+
+/*
+ * Verify that the length argument to array-creation calls is >= 0.
+ */
+static void checkLengthPositive(JNIEnv* env, jsize length, const char* func)
+{
+ if (length < 0) {
+ LOGW("JNI WARNING: negative length for array allocation (%s)\n", func);
+ abortMaybe();
+ }
+}
+
+/*
+ * Verify that the pointer value is non-NULL.
+ */
+static void checkNonNull(JNIEnv* env, const void* ptr, const char* func)
+{
+ if (ptr == NULL) {
+ LOGW("JNI WARNING: invalid null pointer (%s)\n", func);
+ abortMaybe();
+ }
+}
+
+/*
+ * Verify that the method's return type matches the type of call.
+ *
+ * "expectedSigByte" will be 'L' for all objects, including arrays.
+ */
+static void checkSig(JNIEnv* env, jmethodID methodID, char expectedSigByte,
+ bool isStatic, const char* func)
+{
+ const Method* meth = (const Method*) methodID;
+ bool printWarn = false;
+
+ if (expectedSigByte != meth->shorty[0]) {
+ LOGW("JNI WARNING: expected return type '%c'\n", expectedSigByte);
+ printWarn = true;
+ } else if (isStatic && !dvmIsStaticMethod(meth)) {
+ if (isStatic)
+ LOGW("JNI WARNING: calling non-static method with static call\n");
+ else
+ LOGW("JNI WARNING: calling static method with non-static call\n");
+ printWarn = true;
+ }
+
+ if (printWarn) {
+ char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+ LOGW(" calling %s.%s %s\n",
+ meth->clazz->descriptor, meth->name, desc);
+ free(desc);
+ showLocation(dvmGetCurrentJNIMethod(), func);
+ abortMaybe();
+ }
+}
+
+/*
+ * Verify that this static field ID is valid for this class.
+ */
+static void checkStaticFieldID(JNIEnv* env, jclass jclazz, jfieldID fieldID)
+{
+ ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+ StaticField* base = &clazz->sfields[0];
+ int fieldCount = clazz->sfieldCount;
+
+ if ((StaticField*) fieldID < base ||
+ (StaticField*) fieldID >= base + fieldCount)
+ {
+ LOGW("JNI WARNING: static fieldID %p not valid for class %s\n",
+ fieldID, clazz->descriptor);
+ LOGW(" base=%p count=%d\n", base, fieldCount);
+ abortMaybe();
+ }
+}
+
+/*
+ * Verify that this instance field ID is valid for this object.
+ */
+static void checkInstanceFieldID(JNIEnv* env, jobject jobj, jfieldID fieldID,
+ const char* func)
+{
+ JNI_ENTER();
+
+ if (jobj == NULL) {
+ LOGW("JNI WARNING: invalid null object (%s)\n", func);
+ abortMaybe();
+ goto bail;
+ }
+
+ Object* obj = dvmDecodeIndirectRef(env, jobj);
+ ClassObject* clazz = obj->clazz;
+
+ /*
+ * Check this class and all of its superclasses for a matching field.
+ * Don't need to scan interfaces.
+ */
+ while (clazz != NULL) {
+ if ((InstField*) fieldID >= clazz->ifields &&
+ (InstField*) fieldID < clazz->ifields + clazz->ifieldCount)
+ {
+ goto bail;
+ }
+
+ clazz = clazz->super;
+ }
+
+ LOGW("JNI WARNING: inst fieldID %p not valid for class %s\n",
+ fieldID, obj->clazz->descriptor);
+ abortMaybe();
+
+bail:
+ JNI_EXIT();
+}
+
+/*
+ * Verify that "methodID" is appropriate for "jobj".
+ *
+ * Make sure the object is an instance of the method's declaring class.
+ * (Note the methodID might point to a declaration in an interface; this
+ * will be handled automatically by the instanceof check.)
+ */
+static void checkVirtualMethod(JNIEnv* env, jobject jobj, jmethodID methodID,
+ const char* func)
+{
+ JNI_ENTER();
+
+ Object* obj = dvmDecodeIndirectRef(env, jobj);
+ const Method* meth = (const Method*) methodID;
+
+ if (!dvmInstanceof(obj->clazz, meth->clazz)) {
+ LOGW("JNI WARNING: can't call %s.%s on instance of %s\n",
+ meth->clazz->descriptor, meth->name, obj->clazz->descriptor);
+ abortMaybe();
+ }
+
+ JNI_EXIT();
+}
+
+/*
+ * Verify that "methodID" is appropriate for "clazz".
+ *
+ * A mismatch isn't dangerous, because the method defines the class. In
+ * fact, jclazz is unused in the implementation. It's best if we don't
+ * allow bad code in the system though.
+ *
+ * Instances of "jclazz" must be instances of the method's declaring class.
+ */
+static void checkStaticMethod(JNIEnv* env, jclass jclazz, jmethodID methodID,
+ const char* func)
+{
+ JNI_ENTER();
+
+ ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+ const Method* meth = (const Method*) methodID;
+
+ if (!dvmInstanceof(clazz, meth->clazz)) {
+ LOGW("JNI WARNING: can't call static %s.%s on class %s\n",
+ meth->clazz->descriptor, meth->name, clazz->descriptor);
+ // no abort
+ }
+
+ JNI_EXIT();
+}
+
+/*
+ * Verify that the reference arguments being passed in are appropriate for
+ * this method.
+ *
+ * At a minimum we want to make sure that the argument is a valid
+ * reference. We can also do a class lookup on the method signature
+ * and verify that the object is an instance of the appropriate class,
+ * but that's more expensive.
+ *
+ * The basic tests are redundant when indirect references are enabled,
+ * since reference arguments must always be converted explicitly. An
+ * instanceof test would not be redundant, but we're not doing that at
+ * this time.
+ */
+static void checkMethodArgsV(JNIEnv* env, jmethodID methodID, va_list args,
+ const char* func)
+{
+#ifndef USE_INDIRECT_REF
+ JNI_ENTER();
+
+ const Method* meth = (const Method*) methodID;
+ const char* desc = meth->shorty;
+
+ LOGV("V-checking %s.%s:%s...\n", meth->clazz->descriptor, meth->name, desc);
+
+ while (*++desc != '\0') { /* pre-incr to skip return type */
+ switch (*desc) {
+ case 'L':
+ { /* 'shorty' descr uses L for all refs, incl array */
+ jobject argObj = va_arg(args, jobject);
+ checkObject0(env, argObj, func);
+ }
+ break;
+ case 'D': /* 8-byte double */
+ case 'J': /* 8-byte long */
+ case 'F': /* floats normalized to doubles */
+ (void) va_arg(args, u8);
+ break;
+ default: /* Z B C S I -- all passed as 32-bit integers */
+ (void) va_arg(args, u4);
+ break;
+ }
+ }
+
+ JNI_EXIT();
+#endif
+}
+
+/*
+ * Same purpose as checkMethodArgsV, but with arguments in an array of
+ * jvalue structs.
+ */
+static void checkMethodArgsA(JNIEnv* env, jmethodID methodID, jvalue* args,
+ const char* func)
+{
+#ifndef USE_INDIRECT_REF
+ JNI_ENTER();
+
+ const Method* meth = (const Method*) methodID;
+ const char* desc = meth->shorty;
+ int idx = 0;
+
+ LOGV("A-checking %s.%s:%s...\n", meth->clazz->descriptor, meth->name, desc);
+
+ while (*++desc != '\0') { /* pre-incr to skip return type */
+ if (*desc == 'L') {
+ jobject argObj = args[idx].l;
+ checkObject0(env, argObj, func);
+ }
+
+ idx++;
+ }
+
+ JNI_EXIT();
+#endif
+}
+
+
+/*
+ * ===========================================================================
+ * Guarded arrays
+ * ===========================================================================
+ */
+
+#define kGuardLen 512 /* must be multiple of 2 */
+#define kGuardPattern 0xd5e3 /* uncommon values; d5e3d5e3 invalid addr */
+#define kGuardMagic 0xffd5aa96
+#define kGuardExtra sizeof(GuardExtra)
+
+/* this gets tucked in at the start of the buffer; struct size must be even */
+typedef struct GuardExtra {
+ u4 magic;
+ uLong adler;
+ size_t originalLen;
+ const void* originalPtr;
+} GuardExtra;
+
+/* find the GuardExtra given the pointer into the "live" data */
+inline static GuardExtra* getGuardExtra(const void* dataBuf)
+{
+ u1* fullBuf = ((u1*) dataBuf) - kGuardLen / 2;
+ return (GuardExtra*) fullBuf;
+}
+
+/*
+ * Create an oversized buffer to hold the contents of "buf". Copy it in,
+ * filling in the area around it with guard data.
+ *
+ * We use a 16-bit pattern to make a rogue memset less likely to elude us.
+ */
+static void* createGuardedCopy(const void* buf, size_t len, bool modOkay)
+{
+ GuardExtra* pExtra;
+ size_t newLen = (len + kGuardLen +1) & ~0x01;
+ u1* newBuf;
+ u2* pat;
+ int i;
+
+ newBuf = (u1*)malloc(newLen);
+ if (newBuf == NULL) {
+ LOGE("createGuardedCopy failed on alloc of %d bytes\n", newLen);
+ dvmAbort();
+ }
+
+ /* fill it in with a pattern */
+ pat = (u2*) newBuf;
+ for (i = 0; i < (int)newLen / 2; i++)
+ *pat++ = kGuardPattern;
+
+ /* copy the data in; note "len" could be zero */
+ memcpy(newBuf + kGuardLen / 2, buf, len);
+
+ /* if modification is not expected, grab a checksum */
+ uLong adler = 0;
+ if (!modOkay) {
+ adler = adler32(0L, Z_NULL, 0);
+ adler = adler32(adler, buf, len);
+ *(uLong*)newBuf = adler;
+ }
+
+ pExtra = (GuardExtra*) newBuf;
+ pExtra->magic = kGuardMagic;
+ pExtra->adler = adler;
+ pExtra->originalPtr = buf;
+ pExtra->originalLen = len;
+
+ return newBuf + kGuardLen / 2;
+}
+
+/*
+ * Verify the guard area and, if "modOkay" is false, that the data itself
+ * has not been altered.
+ *
+ * The caller has already checked that "dataBuf" is non-NULL.
+ */
+static bool checkGuardedCopy(const void* dataBuf, bool modOkay)
+{
+ static const u4 kMagicCmp = kGuardMagic;
+ const u1* fullBuf = ((const u1*) dataBuf) - kGuardLen / 2;
+ const GuardExtra* pExtra = getGuardExtra(dataBuf);
+ size_t len;
+ const u2* pat;
+ int i;
+
+ /*
+ * Before we do anything with "pExtra", check the magic number. We
+ * do the check with memcmp rather than "==" in case the pointer is
+ * unaligned. If it points to completely bogus memory we're going
+ * to crash, but there's no easy way around that.
+ */
+ if (memcmp(&pExtra->magic, &kMagicCmp, 4) != 0) {
+ u1 buf[4];
+ memcpy(buf, &pExtra->magic, 4);
+ LOGE("JNI: guard magic does not match (found 0x%02x%02x%02x%02x) "
+ "-- incorrect data pointer %p?\n",
+ buf[3], buf[2], buf[1], buf[0], dataBuf); /* assume little endian */
+ return false;
+ }
+
+ len = pExtra->originalLen;
+
+ /* check bottom half of guard; skip over optional checksum storage */
+ pat = (u2*) fullBuf;
+ for (i = kGuardExtra / 2; i < (int) (kGuardLen / 2 - kGuardExtra) / 2; i++)
+ {
+ if (pat[i] != kGuardPattern) {
+ LOGE("JNI: guard pattern(1) disturbed at %p + %d\n",
+ fullBuf, i*2);
+ return false;
+ }
+ }
+
+ int offset = kGuardLen / 2 + len;
+ if (offset & 0x01) {
+ /* odd byte; expected value depends on endian-ness of host */
+ const u2 patSample = kGuardPattern;
+ if (fullBuf[offset] != ((const u1*) &patSample)[1]) {
+ LOGE("JNI: guard pattern disturbed in odd byte after %p "
+ "(+%d) 0x%02x 0x%02x\n",
+ fullBuf, offset, fullBuf[offset], ((const u1*) &patSample)[1]);
+ return false;
+ }
+ offset++;
+ }
+
+ /* check top half of guard */
+ pat = (u2*) (fullBuf + offset);
+ for (i = 0; i < kGuardLen / 4; i++) {
+ if (pat[i] != kGuardPattern) {
+ LOGE("JNI: guard pattern(2) disturbed at %p + %d\n",
+ fullBuf, offset + i*2);
+ return false;
+ }
+ }
+
+ /*
+ * If modification is not expected, verify checksum. Strictly speaking
+ * this is wrong: if we told the client that we made a copy, there's no
+ * reason they can't alter the buffer.
+ */
+ if (!modOkay) {
+ uLong adler = adler32(0L, Z_NULL, 0);
+ adler = adler32(adler, dataBuf, len);
+ if (pExtra->adler != adler) {
+ LOGE("JNI: buffer modified (0x%08lx vs 0x%08lx) at addr %p\n",
+ pExtra->adler, adler, dataBuf);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/*
+ * Free up the guard buffer, scrub it, and return the original pointer.
+ */
+static void* freeGuardedCopy(void* dataBuf)
+{
+ u1* fullBuf = ((u1*) dataBuf) - kGuardLen / 2;
+ const GuardExtra* pExtra = getGuardExtra(dataBuf);
+ void* originalPtr = (void*) pExtra->originalPtr;
+ size_t len = pExtra->originalLen;
+
+ memset(dataBuf, 0xdd, len);
+ free(fullBuf);
+ return originalPtr;
+}
+
+/*
+ * Just pull out the original pointer.
+ */
+static void* getGuardedCopyOriginalPtr(const void* dataBuf)
+{
+ const GuardExtra* pExtra = getGuardExtra(dataBuf);
+ return (void*) pExtra->originalPtr;
+}
+
+/*
+ * Grab the data length.
+ */
+static size_t getGuardedCopyOriginalLen(const void* dataBuf)
+{
+ const GuardExtra* pExtra = getGuardExtra(dataBuf);
+ return pExtra->originalLen;
+}
+
+/*
+ * Return the width, in bytes, of a primitive type.
+ */
+static int dvmPrimitiveTypeWidth(PrimitiveType primType)
+{
+ static const int lengths[PRIM_MAX] = {
+ 1, // boolean
+ 2, // char
+ 4, // float
+ 8, // double
+ 1, // byte
+ 2, // short
+ 4, // int
+ 8, // long
+ -1, // void
+ };
+ assert(primType >= 0 && primType < PRIM_MAX);
+ return lengths[primType];
+}
+
+/*
+ * Create a guarded copy of a primitive array. Modifications to the copied
+ * data are allowed. Returns a pointer to the copied data.
+ */
+static void* createGuardedPACopy(JNIEnv* env, const jarray jarr,
+ jboolean* isCopy)
+{
+ ArrayObject* arrObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
+ PrimitiveType primType = arrObj->obj.clazz->elementClass->primitiveType;
+ int len = arrObj->length * dvmPrimitiveTypeWidth(primType);
+ void* result;
+
+ result = createGuardedCopy(arrObj->contents, len, true);
+
+ if (isCopy != NULL)
+ *isCopy = JNI_TRUE;
+
+ return result;
+}
+
+/*
+ * Perform the array "release" operation, which may or may not copy data
+ * back into the VM, and may or may not release the underlying storage.
+ */
+static void* releaseGuardedPACopy(JNIEnv* env, jarray jarr, void* dataBuf,
+ int mode)
+{
+ ArrayObject* arrObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
+ bool release, copyBack;
+ u1* result;
+
+ if (!checkGuardedCopy(dataBuf, true)) {
+ LOGE("JNI: failed guarded copy check in releaseGuardedPACopy\n");
+ abortMaybe();
+ return NULL;
+ }
+
+ switch (mode) {
+ case 0:
+ release = copyBack = true;
+ break;
+ case JNI_ABORT:
+ release = true;
+ copyBack = false;
+ break;
+ case JNI_COMMIT:
+ release = false;
+ copyBack = true;
+ break;
+ default:
+ LOGE("JNI: bad release mode %d\n", mode);
+ dvmAbort();
+ return NULL;
+ }
+
+ if (copyBack) {
+ size_t len = getGuardedCopyOriginalLen(dataBuf);
+ memcpy(arrObj->contents, dataBuf, len);
+ }
+
+ if (release) {
+ result = (u1*) freeGuardedCopy(dataBuf);
+ } else {
+ result = (u1*) getGuardedCopyOriginalPtr(dataBuf);
+ }
+
+ /* pointer is to the array contents; back up to the array object */
+ result -= offsetof(ArrayObject, contents);
+
+ return result;
+}
+
+
+/*
+ * ===========================================================================
+ * JNI functions
+ * ===========================================================================
+ */
+
+static jint Check_GetVersion(JNIEnv* env)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ jint result;
+ result = BASE_ENV(env)->GetVersion(env);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static jclass Check_DefineClass(JNIEnv* env, const char* name, jobject loader,
+ const jbyte* buf, jsize bufLen)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ CHECK_OBJECT(env, loader);
+ CHECK_UTF_STRING(env, name, false);
+ CHECK_CLASS_NAME(env, name);
+ jclass result;
+ result = BASE_ENV(env)->DefineClass(env, name, loader, buf, bufLen);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static jclass Check_FindClass(JNIEnv* env, const char* name)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ CHECK_UTF_STRING(env, name, false);
+ CHECK_CLASS_NAME(env, name);
+ jclass result;
+ result = BASE_ENV(env)->FindClass(env, name);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static jclass Check_GetSuperclass(JNIEnv* env, jclass clazz)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ CHECK_CLASS(env, clazz);
+ jclass result;
+ result = BASE_ENV(env)->GetSuperclass(env, clazz);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static jboolean Check_IsAssignableFrom(JNIEnv* env, jclass clazz1,
+ jclass clazz2)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ CHECK_CLASS(env, clazz1);
+ CHECK_CLASS(env, clazz2);
+ jboolean result;
+ result = BASE_ENV(env)->IsAssignableFrom(env, clazz1, clazz2);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static jmethodID Check_FromReflectedMethod(JNIEnv* env, jobject method)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ CHECK_OBJECT(env, method);
+ jmethodID result;
+ result = BASE_ENV(env)->FromReflectedMethod(env, method);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static jfieldID Check_FromReflectedField(JNIEnv* env, jobject field)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ CHECK_OBJECT(env, field);
+ jfieldID result;
+ result = BASE_ENV(env)->FromReflectedField(env, field);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static jobject Check_ToReflectedMethod(JNIEnv* env, jclass cls,
+ jmethodID methodID, jboolean isStatic)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ CHECK_CLASS(env, cls);
+ jobject result;
+ result = BASE_ENV(env)->ToReflectedMethod(env, cls, methodID, isStatic);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static jobject Check_ToReflectedField(JNIEnv* env, jclass cls, jfieldID fieldID,
+ jboolean isStatic)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ CHECK_CLASS(env, cls);
+ jobject result;
+ result = BASE_ENV(env)->ToReflectedField(env, cls, fieldID, isStatic);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static jint Check_Throw(JNIEnv* env, jthrowable obj)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ CHECK_OBJECT(env, obj);
+ jint result;
+ result = BASE_ENV(env)->Throw(env, obj);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static jint Check_ThrowNew(JNIEnv* env, jclass clazz, const char* message)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ CHECK_CLASS(env, clazz);
+ CHECK_UTF_STRING(env, message, true);
+ jint result;
+ result = BASE_ENV(env)->ThrowNew(env, clazz, message);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static jthrowable Check_ExceptionOccurred(JNIEnv* env)
+{
+ CHECK_ENTER(env, kFlag_ExcepOkay);
+ jthrowable result;
+ result = BASE_ENV(env)->ExceptionOccurred(env);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static void Check_ExceptionDescribe(JNIEnv* env)
+{
+ CHECK_ENTER(env, kFlag_ExcepOkay);
+ BASE_ENV(env)->ExceptionDescribe(env);
+ CHECK_EXIT(env);
+}
+
+static void Check_ExceptionClear(JNIEnv* env)
+{
+ CHECK_ENTER(env, kFlag_ExcepOkay);
+ BASE_ENV(env)->ExceptionClear(env);
+ CHECK_EXIT(env);
+}
+
+static void Check_FatalError(JNIEnv* env, const char* msg)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ CHECK_UTF_STRING(env, msg, true);
+ BASE_ENV(env)->FatalError(env, msg);
+ CHECK_EXIT(env);
+}
+
+static jint Check_PushLocalFrame(JNIEnv* env, jint capacity)
+{
+ CHECK_ENTER(env, kFlag_Default | kFlag_ExcepOkay);
+ jint result;
+ result = BASE_ENV(env)->PushLocalFrame(env, capacity);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static jobject Check_PopLocalFrame(JNIEnv* env, jobject res)
+{
+ CHECK_ENTER(env, kFlag_Default | kFlag_ExcepOkay);
+ CHECK_OBJECT(env, res);
+ jobject result;
+ result = BASE_ENV(env)->PopLocalFrame(env, res);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static jobject Check_NewGlobalRef(JNIEnv* env, jobject obj)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ CHECK_OBJECT(env, obj);
+ jobject result;
+ result = BASE_ENV(env)->NewGlobalRef(env, obj);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static void Check_DeleteGlobalRef(JNIEnv* env, jobject globalRef)
+{
+ CHECK_ENTER(env, kFlag_Default | kFlag_ExcepOkay);
+ CHECK_OBJECT(env, globalRef);
+#ifdef USE_INDIRECT_REF
+ if (globalRef != NULL &&
+ dvmGetJNIRefType(env, globalRef) != JNIGlobalRefType)
+ {
+ LOGW("JNI WARNING: DeleteGlobalRef on non-global %p (type=%d)\n",
+ globalRef, dvmGetJNIRefType(env, globalRef));
+ abortMaybe();
+ } else
+#endif
+ {
+ BASE_ENV(env)->DeleteGlobalRef(env, globalRef);
+ }
+ CHECK_EXIT(env);
+}
+
+static jobject Check_NewLocalRef(JNIEnv* env, jobject ref)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ CHECK_OBJECT(env, ref);
+ jobject result;
+ result = BASE_ENV(env)->NewLocalRef(env, ref);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static void Check_DeleteLocalRef(JNIEnv* env, jobject localRef)
+{
+ CHECK_ENTER(env, kFlag_Default | kFlag_ExcepOkay);
+ CHECK_OBJECT(env, localRef);
+#ifdef USE_INDIRECT_REF
+ if (localRef != NULL &&
+ dvmGetJNIRefType(env, localRef) != JNILocalRefType)
+ {
+ LOGW("JNI WARNING: DeleteLocalRef on non-local %p (type=%d)\n",
+ localRef, dvmGetJNIRefType(env, localRef));
+ abortMaybe();
+ } else
+#endif
+ {
+ BASE_ENV(env)->DeleteLocalRef(env, localRef);
+ }
+ CHECK_EXIT(env);
+}
+
+static jint Check_EnsureLocalCapacity(JNIEnv *env, jint capacity)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ jint result;
+ result = BASE_ENV(env)->EnsureLocalCapacity(env, capacity);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static jboolean Check_IsSameObject(JNIEnv* env, jobject ref1, jobject ref2)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ CHECK_OBJECT(env, ref1);
+ CHECK_OBJECT(env, ref2);
+ jboolean result;
+ result = BASE_ENV(env)->IsSameObject(env, ref1, ref2);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static jobject Check_AllocObject(JNIEnv* env, jclass clazz)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ CHECK_CLASS(env, clazz);
+ jobject result;
+ result = BASE_ENV(env)->AllocObject(env, clazz);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static jobject Check_NewObject(JNIEnv* env, jclass clazz, jmethodID methodID,
+ ...)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ CHECK_CLASS(env, clazz);
+ jobject result;
+ va_list args, tmpArgs;
+
+ va_start(args, methodID);
+
+ va_copy(tmpArgs, args);
+ CHECK_METHOD_ARGS_V(env, methodID, tmpArgs);
+ va_end(tmpArgs);
+
+ result = BASE_ENV(env)->NewObjectV(env, clazz, methodID, args);
+ va_end(args);
+
+ CHECK_EXIT(env);
+ return result;
+}
+static jobject Check_NewObjectV(JNIEnv* env, jclass clazz, jmethodID methodID,
+ va_list args)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ CHECK_CLASS(env, clazz);
+ jobject result;
+
+ va_list tmpArgs;
+ va_copy(tmpArgs, args);
+ CHECK_METHOD_ARGS_V(env, methodID, tmpArgs);
+ va_end(tmpArgs);
+
+ result = BASE_ENV(env)->NewObjectV(env, clazz, methodID, args);
+ CHECK_EXIT(env);
+ return result;
+}
+static jobject Check_NewObjectA(JNIEnv* env, jclass clazz, jmethodID methodID,
+ jvalue* args)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ CHECK_CLASS(env, clazz);
+ jobject result;
+
+ CHECK_METHOD_ARGS_A(env, methodID, args);
+ result = BASE_ENV(env)->NewObjectA(env, clazz, methodID, args);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static jclass Check_GetObjectClass(JNIEnv* env, jobject obj)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ CHECK_OBJECT(env, obj);
+ jclass result;
+ result = BASE_ENV(env)->GetObjectClass(env, obj);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static jboolean Check_IsInstanceOf(JNIEnv* env, jobject obj, jclass clazz)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ CHECK_OBJECT(env, obj);
+ CHECK_CLASS(env, clazz);
+ jboolean result;
+ result = BASE_ENV(env)->IsInstanceOf(env, obj, clazz);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static jmethodID Check_GetMethodID(JNIEnv* env, jclass clazz, const char* name,
+ const char* sig)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ CHECK_CLASS(env, clazz);
+ CHECK_UTF_STRING(env, name, false);
+ CHECK_UTF_STRING(env, sig, false);
+ jmethodID result;
+ result = BASE_ENV(env)->GetMethodID(env, clazz, name, sig);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static jfieldID Check_GetFieldID(JNIEnv* env, jclass clazz,
+ const char* name, const char* sig)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ CHECK_CLASS(env, clazz);
+ CHECK_UTF_STRING(env, name, false);
+ CHECK_UTF_STRING(env, sig, false);
+ jfieldID result;
+ result = BASE_ENV(env)->GetFieldID(env, clazz, name, sig);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static jmethodID Check_GetStaticMethodID(JNIEnv* env, jclass clazz,
+ const char* name, const char* sig)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ CHECK_CLASS(env, clazz);
+ CHECK_UTF_STRING(env, name, false);
+ CHECK_UTF_STRING(env, sig, false);
+ jmethodID result;
+ result = BASE_ENV(env)->GetStaticMethodID(env, clazz, name, sig);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static jfieldID Check_GetStaticFieldID(JNIEnv* env, jclass clazz,
+ const char* name, const char* sig)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ CHECK_CLASS(env, clazz);
+ CHECK_UTF_STRING(env, name, false);
+ CHECK_UTF_STRING(env, sig, false);
+ jfieldID result;
+ result = BASE_ENV(env)->GetStaticFieldID(env, clazz, name, sig);
+ CHECK_EXIT(env);
+ return result;
+}
+
+#define GET_STATIC_TYPE_FIELD(_ctype, _jname) \
+ static _ctype Check_GetStatic##_jname##Field(JNIEnv* env, jclass clazz, \
+ jfieldID fieldID) \
+ { \
+ CHECK_ENTER(env, kFlag_Default); \
+ CHECK_CLASS(env, clazz); \
+ _ctype result; \
+ checkStaticFieldID(env, clazz, fieldID); \
+ result = BASE_ENV(env)->GetStatic##_jname##Field(env, clazz, \
+ fieldID); \
+ CHECK_EXIT(env); \
+ return result; \
+ }
+GET_STATIC_TYPE_FIELD(jobject, Object);
+GET_STATIC_TYPE_FIELD(jboolean, Boolean);
+GET_STATIC_TYPE_FIELD(jbyte, Byte);
+GET_STATIC_TYPE_FIELD(jchar, Char);
+GET_STATIC_TYPE_FIELD(jshort, Short);
+GET_STATIC_TYPE_FIELD(jint, Int);
+GET_STATIC_TYPE_FIELD(jlong, Long);
+GET_STATIC_TYPE_FIELD(jfloat, Float);
+GET_STATIC_TYPE_FIELD(jdouble, Double);
+
+#define SET_STATIC_TYPE_FIELD(_ctype, _jname, _ftype) \
+ static void Check_SetStatic##_jname##Field(JNIEnv* env, jclass clazz, \
+ jfieldID fieldID, _ctype value) \
+ { \
+ CHECK_ENTER(env, kFlag_Default); \
+ CHECK_CLASS(env, clazz); \
+ checkStaticFieldID(env, clazz, fieldID); \
+ /* "value" arg only used when type == ref */ \
+ CHECK_FIELD_TYPE(env, (jobject)(u4)value, fieldID, _ftype, true); \
+ BASE_ENV(env)->SetStatic##_jname##Field(env, clazz, fieldID, \
+ value); \
+ CHECK_EXIT(env); \
+ }
+SET_STATIC_TYPE_FIELD(jobject, Object, PRIM_NOT);
+SET_STATIC_TYPE_FIELD(jboolean, Boolean, PRIM_BOOLEAN);
+SET_STATIC_TYPE_FIELD(jbyte, Byte, PRIM_BYTE);
+SET_STATIC_TYPE_FIELD(jchar, Char, PRIM_CHAR);
+SET_STATIC_TYPE_FIELD(jshort, Short, PRIM_SHORT);
+SET_STATIC_TYPE_FIELD(jint, Int, PRIM_INT);
+SET_STATIC_TYPE_FIELD(jlong, Long, PRIM_LONG);
+SET_STATIC_TYPE_FIELD(jfloat, Float, PRIM_FLOAT);
+SET_STATIC_TYPE_FIELD(jdouble, Double, PRIM_DOUBLE);
+
+#define GET_TYPE_FIELD(_ctype, _jname) \
+ static _ctype Check_Get##_jname##Field(JNIEnv* env, jobject obj, \
+ jfieldID fieldID) \
+ { \
+ CHECK_ENTER(env, kFlag_Default); \
+ CHECK_OBJECT(env, obj); \
+ _ctype result; \
+ CHECK_INST_FIELD_ID(env, obj, fieldID); \
+ result = BASE_ENV(env)->Get##_jname##Field(env, obj, fieldID); \
+ CHECK_EXIT(env); \
+ return result; \
+ }
+GET_TYPE_FIELD(jobject, Object);
+GET_TYPE_FIELD(jboolean, Boolean);
+GET_TYPE_FIELD(jbyte, Byte);
+GET_TYPE_FIELD(jchar, Char);
+GET_TYPE_FIELD(jshort, Short);
+GET_TYPE_FIELD(jint, Int);
+GET_TYPE_FIELD(jlong, Long);
+GET_TYPE_FIELD(jfloat, Float);
+GET_TYPE_FIELD(jdouble, Double);
+
+#define SET_TYPE_FIELD(_ctype, _jname, _ftype) \
+ static void Check_Set##_jname##Field(JNIEnv* env, jobject obj, \
+ jfieldID fieldID, _ctype value) \
+ { \
+ CHECK_ENTER(env, kFlag_Default); \
+ CHECK_OBJECT(env, obj); \
+ CHECK_INST_FIELD_ID(env, obj, fieldID); \
+ /* "value" arg only used when type == ref */ \
+ CHECK_FIELD_TYPE(env, (jobject)(u4) value, fieldID, _ftype, false); \
+ BASE_ENV(env)->Set##_jname##Field(env, obj, fieldID, value); \
+ CHECK_EXIT(env); \
+ }
+SET_TYPE_FIELD(jobject, Object, PRIM_NOT);
+SET_TYPE_FIELD(jboolean, Boolean, PRIM_BOOLEAN);
+SET_TYPE_FIELD(jbyte, Byte, PRIM_BYTE);
+SET_TYPE_FIELD(jchar, Char, PRIM_CHAR);
+SET_TYPE_FIELD(jshort, Short, PRIM_SHORT);
+SET_TYPE_FIELD(jint, Int, PRIM_INT);
+SET_TYPE_FIELD(jlong, Long, PRIM_LONG);
+SET_TYPE_FIELD(jfloat, Float, PRIM_FLOAT);
+SET_TYPE_FIELD(jdouble, Double, PRIM_DOUBLE);
+
+#define CALL_VIRTUAL(_ctype, _jname, _retdecl, _retasgn, _retok, _retsig) \
+ static _ctype Check_Call##_jname##Method(JNIEnv* env, jobject obj, \
+ jmethodID methodID, ...) \
+ { \
+ CHECK_ENTER(env, kFlag_Default); \
+ CHECK_OBJECT(env, obj); \
+ CHECK_SIG(env, methodID, _retsig, false); \
+ CHECK_VIRTUAL_METHOD(env, obj, methodID); \
+ _retdecl; \
+ va_list args, tmpArgs; \
+ va_start(args, methodID); \
+ va_copy(tmpArgs, args); \
+ CHECK_METHOD_ARGS_V(env, methodID, tmpArgs); \
+ va_end(tmpArgs); \
+ _retasgn BASE_ENV(env)->Call##_jname##MethodV(env, obj, methodID, \
+ args); \
+ va_end(args); \
+ CHECK_EXIT(env); \
+ return _retok; \
+ } \
+ static _ctype Check_Call##_jname##MethodV(JNIEnv* env, jobject obj, \
+ jmethodID methodID, va_list args) \
+ { \
+ CHECK_ENTER(env, kFlag_Default); \
+ CHECK_OBJECT(env, obj); \
+ CHECK_SIG(env, methodID, _retsig, false); \
+ CHECK_VIRTUAL_METHOD(env, obj, methodID); \
+ _retdecl; \
+ va_list tmpArgs; \
+ va_copy(tmpArgs, args); \
+ CHECK_METHOD_ARGS_V(env, methodID, tmpArgs); \
+ va_end(tmpArgs); \
+ _retasgn BASE_ENV(env)->Call##_jname##MethodV(env, obj, methodID, \
+ args); \
+ CHECK_EXIT(env); \
+ return _retok; \
+ } \
+ static _ctype Check_Call##_jname##MethodA(JNIEnv* env, jobject obj, \
+ jmethodID methodID, jvalue* args) \
+ { \
+ CHECK_ENTER(env, kFlag_Default); \
+ CHECK_OBJECT(env, obj); \
+ CHECK_SIG(env, methodID, _retsig, false); \
+ CHECK_VIRTUAL_METHOD(env, obj, methodID); \
+ _retdecl; \
+ CHECK_METHOD_ARGS_A(env, methodID, args); \
+ _retasgn BASE_ENV(env)->Call##_jname##MethodA(env, obj, methodID, \
+ args); \
+ CHECK_EXIT(env); \
+ return _retok; \
+ }
+CALL_VIRTUAL(jobject, Object, Object* result, result=, result, 'L');
+CALL_VIRTUAL(jboolean, Boolean, jboolean result, result=, result, 'Z');
+CALL_VIRTUAL(jbyte, Byte, jbyte result, result=, result, 'B');
+CALL_VIRTUAL(jchar, Char, jchar result, result=, result, 'C');
+CALL_VIRTUAL(jshort, Short, jshort result, result=, result, 'S');
+CALL_VIRTUAL(jint, Int, jint result, result=, result, 'I');
+CALL_VIRTUAL(jlong, Long, jlong result, result=, result, 'J');
+CALL_VIRTUAL(jfloat, Float, jfloat result, result=, result, 'F');
+CALL_VIRTUAL(jdouble, Double, jdouble result, result=, result, 'D');
+CALL_VIRTUAL(void, Void, , , , 'V');
+
+#define CALL_NONVIRTUAL(_ctype, _jname, _retdecl, _retasgn, _retok, \
+ _retsig) \
+ static _ctype Check_CallNonvirtual##_jname##Method(JNIEnv* env, \
+ jobject obj, jclass clazz, jmethodID methodID, ...) \
+ { \
+ CHECK_ENTER(env, kFlag_Default); \
+ CHECK_CLASS(env, clazz); \
+ CHECK_OBJECT(env, obj); \
+ CHECK_SIG(env, methodID, _retsig, false); \
+ CHECK_VIRTUAL_METHOD(env, obj, methodID); \
+ _retdecl; \
+ va_list args, tmpArgs; \
+ va_start(args, methodID); \
+ va_copy(tmpArgs, args); \
+ CHECK_METHOD_ARGS_V(env, methodID, tmpArgs); \
+ va_end(tmpArgs); \
+ _retasgn BASE_ENV(env)->CallNonvirtual##_jname##MethodV(env, obj, \
+ clazz, methodID, args); \
+ va_end(args); \
+ CHECK_EXIT(env); \
+ return _retok; \
+ } \
+ static _ctype Check_CallNonvirtual##_jname##MethodV(JNIEnv* env, \
+ jobject obj, jclass clazz, jmethodID methodID, va_list args) \
+ { \
+ CHECK_ENTER(env, kFlag_Default); \
+ CHECK_CLASS(env, clazz); \
+ CHECK_OBJECT(env, obj); \
+ CHECK_SIG(env, methodID, _retsig, false); \
+ CHECK_VIRTUAL_METHOD(env, obj, methodID); \
+ _retdecl; \
+ va_list tmpArgs; \
+ va_copy(tmpArgs, args); \
+ CHECK_METHOD_ARGS_V(env, methodID, tmpArgs); \
+ va_end(tmpArgs); \
+ _retasgn BASE_ENV(env)->CallNonvirtual##_jname##MethodV(env, obj, \
+ clazz, methodID, args); \
+ CHECK_EXIT(env); \
+ return _retok; \
+ } \
+ static _ctype Check_CallNonvirtual##_jname##MethodA(JNIEnv* env, \
+ jobject obj, jclass clazz, jmethodID methodID, jvalue* args) \
+ { \
+ CHECK_ENTER(env, kFlag_Default); \
+ CHECK_CLASS(env, clazz); \
+ CHECK_OBJECT(env, obj); \
+ CHECK_SIG(env, methodID, _retsig, false); \
+ CHECK_VIRTUAL_METHOD(env, obj, methodID); \
+ _retdecl; \
+ CHECK_METHOD_ARGS_A(env, methodID, args); \
+ _retasgn BASE_ENV(env)->CallNonvirtual##_jname##MethodA(env, obj, \
+ clazz, methodID, args); \
+ CHECK_EXIT(env); \
+ return _retok; \
+ }
+CALL_NONVIRTUAL(jobject, Object, Object* result, result=, result, 'L');
+CALL_NONVIRTUAL(jboolean, Boolean, jboolean result, result=, result, 'Z');
+CALL_NONVIRTUAL(jbyte, Byte, jbyte result, result=, result, 'B');
+CALL_NONVIRTUAL(jchar, Char, jchar result, result=, result, 'C');
+CALL_NONVIRTUAL(jshort, Short, jshort result, result=, result, 'S');
+CALL_NONVIRTUAL(jint, Int, jint result, result=, result, 'I');
+CALL_NONVIRTUAL(jlong, Long, jlong result, result=, result, 'J');
+CALL_NONVIRTUAL(jfloat, Float, jfloat result, result=, result, 'F');
+CALL_NONVIRTUAL(jdouble, Double, jdouble result, result=, result, 'D');
+CALL_NONVIRTUAL(void, Void, , , , 'V');
+
+
+#define CALL_STATIC(_ctype, _jname, _retdecl, _retasgn, _retok, _retsig) \
+ static _ctype Check_CallStatic##_jname##Method(JNIEnv* env, \
+ jclass clazz, jmethodID methodID, ...) \
+ { \
+ CHECK_ENTER(env, kFlag_Default); \
+ CHECK_CLASS(env, clazz); \
+ CHECK_SIG(env, methodID, _retsig, true); \
+ CHECK_STATIC_METHOD(env, clazz, methodID); \
+ _retdecl; \
+ va_list args, tmpArgs; \
+ va_start(args, methodID); \
+ va_copy(tmpArgs, args); \
+ CHECK_METHOD_ARGS_V(env, methodID, tmpArgs); \
+ va_end(tmpArgs); \
+ _retasgn BASE_ENV(env)->CallStatic##_jname##MethodV(env, clazz, \
+ methodID, args); \
+ va_end(args); \
+ CHECK_EXIT(env); \
+ return _retok; \
+ } \
+ static _ctype Check_CallStatic##_jname##MethodV(JNIEnv* env, \
+ jclass clazz, jmethodID methodID, va_list args) \
+ { \
+ CHECK_ENTER(env, kFlag_Default); \
+ CHECK_CLASS(env, clazz); \
+ CHECK_SIG(env, methodID, _retsig, true); \
+ CHECK_STATIC_METHOD(env, clazz, methodID); \
+ _retdecl; \
+ va_list tmpArgs; \
+ va_copy(tmpArgs, args); \
+ CHECK_METHOD_ARGS_V(env, methodID, tmpArgs); \
+ va_end(tmpArgs); \
+ _retasgn BASE_ENV(env)->CallStatic##_jname##MethodV(env, clazz, \
+ methodID, args); \
+ CHECK_EXIT(env); \
+ return _retok; \
+ } \
+ static _ctype Check_CallStatic##_jname##MethodA(JNIEnv* env, \
+ jclass clazz, jmethodID methodID, jvalue* args) \
+ { \
+ CHECK_ENTER(env, kFlag_Default); \
+ CHECK_CLASS(env, clazz); \
+ CHECK_SIG(env, methodID, _retsig, true); \
+ CHECK_STATIC_METHOD(env, clazz, methodID); \
+ _retdecl; \
+ CHECK_METHOD_ARGS_A(env, methodID, args); \
+ _retasgn BASE_ENV(env)->CallStatic##_jname##MethodA(env, clazz, \
+ methodID, args); \
+ CHECK_EXIT(env); \
+ return _retok; \
+ }
+CALL_STATIC(jobject, Object, Object* result, result=, result, 'L');
+CALL_STATIC(jboolean, Boolean, jboolean result, result=, result, 'Z');
+CALL_STATIC(jbyte, Byte, jbyte result, result=, result, 'B');
+CALL_STATIC(jchar, Char, jchar result, result=, result, 'C');
+CALL_STATIC(jshort, Short, jshort result, result=, result, 'S');
+CALL_STATIC(jint, Int, jint result, result=, result, 'I');
+CALL_STATIC(jlong, Long, jlong result, result=, result, 'J');
+CALL_STATIC(jfloat, Float, jfloat result, result=, result, 'F');
+CALL_STATIC(jdouble, Double, jdouble result, result=, result, 'D');
+CALL_STATIC(void, Void, , , , 'V');
+
+static jstring Check_NewString(JNIEnv* env, const jchar* unicodeChars,
+ jsize len)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ jstring result;
+ result = BASE_ENV(env)->NewString(env, unicodeChars, len);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static jsize Check_GetStringLength(JNIEnv* env, jstring string)
+{
+ CHECK_ENTER(env, kFlag_CritOkay);
+ CHECK_STRING(env, string);
+ jsize result;
+ result = BASE_ENV(env)->GetStringLength(env, string);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static const jchar* Check_GetStringChars(JNIEnv* env, jstring string,
+ jboolean* isCopy)
+{
+ CHECK_ENTER(env, kFlag_CritOkay);
+ CHECK_STRING(env, string);
+ const jchar* result;
+ result = BASE_ENV(env)->GetStringChars(env, string, isCopy);
+ if (((JNIEnvExt*)env)->forceDataCopy && result != NULL) {
+ // TODO: fix for indirect
+ int len = dvmStringLen(string) * 2;
+ result = (const jchar*) createGuardedCopy(result, len, false);
+ if (isCopy != NULL)
+ *isCopy = JNI_TRUE;
+ }
+ CHECK_EXIT(env);
+ return result;
+}
+
+static void Check_ReleaseStringChars(JNIEnv* env, jstring string,
+ const jchar* chars)
+{
+ CHECK_ENTER(env, kFlag_Default | kFlag_ExcepOkay);
+ CHECK_STRING(env, string);
+ CHECK_NON_NULL(env, chars);
+ if (((JNIEnvExt*)env)->forceDataCopy) {
+ if (!checkGuardedCopy(chars, false)) {
+ LOGE("JNI: failed guarded copy check in ReleaseStringChars\n");
+ abortMaybe();
+ return;
+ }
+ chars = (const jchar*) freeGuardedCopy((jchar*)chars);
+ }
+ BASE_ENV(env)->ReleaseStringChars(env, string, chars);
+ CHECK_EXIT(env);
+}
+
+static jstring Check_NewStringUTF(JNIEnv* env, const char* bytes)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ CHECK_UTF_STRING(env, bytes, true);
+ jstring result;
+ result = BASE_ENV(env)->NewStringUTF(env, bytes);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static jsize Check_GetStringUTFLength(JNIEnv* env, jstring string)
+{
+ CHECK_ENTER(env, kFlag_CritOkay);
+ CHECK_STRING(env, string);
+ jsize result;
+ result = BASE_ENV(env)->GetStringUTFLength(env, string);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static const char* Check_GetStringUTFChars(JNIEnv* env, jstring string,
+ jboolean* isCopy)
+{
+ CHECK_ENTER(env, kFlag_CritOkay);
+ CHECK_STRING(env, string);
+ const char* result;
+ result = BASE_ENV(env)->GetStringUTFChars(env, string, isCopy);
+ if (((JNIEnvExt*)env)->forceDataCopy && result != NULL) {
+ // TODO: fix for indirect
+ int len = dvmStringUtf8ByteLen(string) + 1;
+ result = (const char*) createGuardedCopy(result, len, false);
+ if (isCopy != NULL)
+ *isCopy = JNI_TRUE;
+ }
+ CHECK_EXIT(env);
+ return result;
+}
+
+static void Check_ReleaseStringUTFChars(JNIEnv* env, jstring string,
+ const char* utf)
+{
+ CHECK_ENTER(env, kFlag_ExcepOkay);
+ CHECK_STRING(env, string);
+ CHECK_NON_NULL(env, utf);
+ if (((JNIEnvExt*)env)->forceDataCopy) {
+ //int len = dvmStringUtf8ByteLen(string) + 1;
+ if (!checkGuardedCopy(utf, false)) {
+ LOGE("JNI: failed guarded copy check in ReleaseStringUTFChars\n");
+ abortMaybe();
+ return;
+ }
+ utf = (const char*) freeGuardedCopy((char*)utf);
+ }
+ BASE_ENV(env)->ReleaseStringUTFChars(env, string, utf);
+ CHECK_EXIT(env);
+}
+
+static jsize Check_GetArrayLength(JNIEnv* env, jarray array)
+{
+ CHECK_ENTER(env, kFlag_CritOkay);
+ CHECK_ARRAY(env, array);
+ jsize result;
+ result = BASE_ENV(env)->GetArrayLength(env, array);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static jobjectArray Check_NewObjectArray(JNIEnv* env, jsize length,
+ jclass elementClass, jobject initialElement)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ CHECK_CLASS(env, elementClass);
+ CHECK_OBJECT(env, initialElement);
+ CHECK_LENGTH_POSITIVE(env, length);
+ jobjectArray result;
+ result = BASE_ENV(env)->NewObjectArray(env, length, elementClass,
+ initialElement);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static jobject Check_GetObjectArrayElement(JNIEnv* env, jobjectArray array,
+ jsize index)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ CHECK_ARRAY(env, array);
+ jobject result;
+ result = BASE_ENV(env)->GetObjectArrayElement(env, array, index);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static void Check_SetObjectArrayElement(JNIEnv* env, jobjectArray array,
+ jsize index, jobject value)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ CHECK_ARRAY(env, array);
+ BASE_ENV(env)->SetObjectArrayElement(env, array, index, value);
+ CHECK_EXIT(env);
+}
+
+#define NEW_PRIMITIVE_ARRAY(_artype, _jname) \
+ static _artype Check_New##_jname##Array(JNIEnv* env, jsize length) \
+ { \
+ CHECK_ENTER(env, kFlag_Default); \
+ CHECK_LENGTH_POSITIVE(env, length); \
+ _artype result; \
+ result = BASE_ENV(env)->New##_jname##Array(env, length); \
+ CHECK_EXIT(env); \
+ return result; \
+ }
+NEW_PRIMITIVE_ARRAY(jbooleanArray, Boolean);
+NEW_PRIMITIVE_ARRAY(jbyteArray, Byte);
+NEW_PRIMITIVE_ARRAY(jcharArray, Char);
+NEW_PRIMITIVE_ARRAY(jshortArray, Short);
+NEW_PRIMITIVE_ARRAY(jintArray, Int);
+NEW_PRIMITIVE_ARRAY(jlongArray, Long);
+NEW_PRIMITIVE_ARRAY(jfloatArray, Float);
+NEW_PRIMITIVE_ARRAY(jdoubleArray, Double);
+
+
+#define GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
+ static _ctype* Check_Get##_jname##ArrayElements(JNIEnv* env, \
+ _ctype##Array array, jboolean* isCopy) \
+ { \
+ CHECK_ENTER(env, kFlag_Default); \
+ CHECK_ARRAY(env, array); \
+ _ctype* result; \
+ result = BASE_ENV(env)->Get##_jname##ArrayElements(env, \
+ array, isCopy); \
+ if (((JNIEnvExt*)env)->forceDataCopy && result != NULL) { \
+ result = (_ctype*) createGuardedPACopy(env, array, isCopy); \
+ } \
+ CHECK_EXIT(env); \
+ return result; \
+ }
+
+#define RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
+ static void Check_Release##_jname##ArrayElements(JNIEnv* env, \
+ _ctype##Array array, _ctype* elems, jint mode) \
+ { \
+ CHECK_ENTER(env, kFlag_Default | kFlag_ExcepOkay); \
+ CHECK_ARRAY(env, array); \
+ CHECK_NON_NULL(env, elems); \
+ CHECK_RELEASE_MODE(env, mode); \
+ if (((JNIEnvExt*)env)->forceDataCopy) { \
+ elems = (_ctype*) releaseGuardedPACopy(env, array, elems, mode);\
+ } \
+ BASE_ENV(env)->Release##_jname##ArrayElements(env, \
+ array, elems, mode); \
+ CHECK_EXIT(env); \
+ }
+
+#define GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
+ static void Check_Get##_jname##ArrayRegion(JNIEnv* env, \
+ _ctype##Array array, jsize start, jsize len, _ctype* buf) \
+ { \
+ CHECK_ENTER(env, kFlag_Default); \
+ CHECK_ARRAY(env, array); \
+ BASE_ENV(env)->Get##_jname##ArrayRegion(env, array, start, \
+ len, buf); \
+ CHECK_EXIT(env); \
+ }
+
+#define SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
+ static void Check_Set##_jname##ArrayRegion(JNIEnv* env, \
+ _ctype##Array array, jsize start, jsize len, const _ctype* buf) \
+ { \
+ CHECK_ENTER(env, kFlag_Default); \
+ CHECK_ARRAY(env, array); \
+ BASE_ENV(env)->Set##_jname##ArrayRegion(env, array, start, \
+ len, buf); \
+ CHECK_EXIT(env); \
+ }
+
+#define PRIMITIVE_ARRAY_FUNCTIONS(_ctype, _jname, _typechar) \
+ GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
+ RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
+ GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname); \
+ SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname);
+
+/* TODO: verify primitive array type matches call type */
+PRIMITIVE_ARRAY_FUNCTIONS(jboolean, Boolean, 'Z');
+PRIMITIVE_ARRAY_FUNCTIONS(jbyte, Byte, 'B');
+PRIMITIVE_ARRAY_FUNCTIONS(jchar, Char, 'C');
+PRIMITIVE_ARRAY_FUNCTIONS(jshort, Short, 'S');
+PRIMITIVE_ARRAY_FUNCTIONS(jint, Int, 'I');
+PRIMITIVE_ARRAY_FUNCTIONS(jlong, Long, 'J');
+PRIMITIVE_ARRAY_FUNCTIONS(jfloat, Float, 'F');
+PRIMITIVE_ARRAY_FUNCTIONS(jdouble, Double, 'D');
+
+static jint Check_RegisterNatives(JNIEnv* env, jclass clazz,
+ const JNINativeMethod* methods, jint nMethods)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ CHECK_CLASS(env, clazz);
+ jint result;
+ result = BASE_ENV(env)->RegisterNatives(env, clazz, methods, nMethods);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static jint Check_UnregisterNatives(JNIEnv* env, jclass clazz)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ CHECK_CLASS(env, clazz);
+ jint result;
+ result = BASE_ENV(env)->UnregisterNatives(env, clazz);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static jint Check_MonitorEnter(JNIEnv* env, jobject obj)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ CHECK_OBJECT(env, obj);
+ jint result;
+ result = BASE_ENV(env)->MonitorEnter(env, obj);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static jint Check_MonitorExit(JNIEnv* env, jobject obj)
+{
+ CHECK_ENTER(env, kFlag_Default | kFlag_ExcepOkay);
+ CHECK_OBJECT(env, obj);
+ jint result;
+ result = BASE_ENV(env)->MonitorExit(env, obj);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static jint Check_GetJavaVM(JNIEnv *env, JavaVM **vm)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ jint result;
+ result = BASE_ENV(env)->GetJavaVM(env, vm);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static void Check_GetStringRegion(JNIEnv* env, jstring str, jsize start,
+ jsize len, jchar* buf)
+{
+ CHECK_ENTER(env, kFlag_CritOkay);
+ CHECK_STRING(env, str);
+ BASE_ENV(env)->GetStringRegion(env, str, start, len, buf);
+ CHECK_EXIT(env);
+}
+
+static void Check_GetStringUTFRegion(JNIEnv* env, jstring str, jsize start,
+ jsize len, char* buf)
+{
+ CHECK_ENTER(env, kFlag_CritOkay);
+ CHECK_STRING(env, str);
+ BASE_ENV(env)->GetStringUTFRegion(env, str, start, len, buf);
+ CHECK_EXIT(env);
+}
+
+static void* Check_GetPrimitiveArrayCritical(JNIEnv* env, jarray array,
+ jboolean* isCopy)
+{
+ CHECK_ENTER(env, kFlag_CritGet);
+ CHECK_ARRAY(env, array);
+ void* result;
+ result = BASE_ENV(env)->GetPrimitiveArrayCritical(env, array, isCopy);
+ if (((JNIEnvExt*)env)->forceDataCopy && result != NULL) {
+ result = createGuardedPACopy(env, array, isCopy);
+ }
+ CHECK_EXIT(env);
+ return result;
+}
+
+static void Check_ReleasePrimitiveArrayCritical(JNIEnv* env, jarray array,
+ void* carray, jint mode)
+{
+ CHECK_ENTER(env, kFlag_CritRelease | kFlag_ExcepOkay);
+ CHECK_ARRAY(env, array);
+ CHECK_NON_NULL(env, carray);
+ CHECK_RELEASE_MODE(env, mode);
+ if (((JNIEnvExt*)env)->forceDataCopy) {
+ carray = releaseGuardedPACopy(env, array, carray, mode);
+ }
+ BASE_ENV(env)->ReleasePrimitiveArrayCritical(env, array, carray, mode);
+ CHECK_EXIT(env);
+}
+
+static const jchar* Check_GetStringCritical(JNIEnv* env, jstring string,
+ jboolean* isCopy)
+{
+ CHECK_ENTER(env, kFlag_CritGet);
+ CHECK_STRING(env, string);
+ const jchar* result;
+ result = BASE_ENV(env)->GetStringCritical(env, string, isCopy);
+ if (((JNIEnvExt*)env)->forceDataCopy && result != NULL) {
+ // TODO: fix for indirect
+ int len = dvmStringLen(string) * 2;
+ result = (const jchar*) createGuardedCopy(result, len, false);
+ if (isCopy != NULL)
+ *isCopy = JNI_TRUE;
+ }
+ CHECK_EXIT(env);
+ return result;
+}
+
+static void Check_ReleaseStringCritical(JNIEnv* env, jstring string,
+ const jchar* carray)
+{
+ CHECK_ENTER(env, kFlag_CritRelease | kFlag_ExcepOkay);
+ CHECK_STRING(env, string);
+ CHECK_NON_NULL(env, carray);
+ if (((JNIEnvExt*)env)->forceDataCopy) {
+ if (!checkGuardedCopy(carray, false)) {
+ LOGE("JNI: failed guarded copy check in ReleaseStringCritical\n");
+ abortMaybe();
+ return;
+ }
+ carray = (const jchar*) freeGuardedCopy((jchar*)carray);
+ }
+ BASE_ENV(env)->ReleaseStringCritical(env, string, carray);
+ CHECK_EXIT(env);
+}
+
+static jweak Check_NewWeakGlobalRef(JNIEnv* env, jobject obj)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ CHECK_OBJECT(env, obj);
+ jweak result;
+ result = BASE_ENV(env)->NewWeakGlobalRef(env, obj);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static void Check_DeleteWeakGlobalRef(JNIEnv* env, jweak obj)
+{
+ CHECK_ENTER(env, kFlag_Default | kFlag_ExcepOkay);
+ CHECK_OBJECT(env, obj);
+ BASE_ENV(env)->DeleteWeakGlobalRef(env, obj);
+ CHECK_EXIT(env);
+}
+
+static jboolean Check_ExceptionCheck(JNIEnv* env)
+{
+ CHECK_ENTER(env, kFlag_CritOkay | kFlag_ExcepOkay);
+ jboolean result;
+ result = BASE_ENV(env)->ExceptionCheck(env);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static jobjectRefType Check_GetObjectRefType(JNIEnv* env, jobject obj)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ CHECK_OBJECT(env, obj);
+ jobjectRefType result;
+ result = BASE_ENV(env)->GetObjectRefType(env, obj);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static jobject Check_NewDirectByteBuffer(JNIEnv* env, void* address,
+ jlong capacity)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ jobject result;
+ if (address == NULL || capacity < 0) {
+ LOGW("JNI WARNING: invalid values for address (%p) or capacity (%ld)\n",
+ address, (long) capacity);
+ abortMaybe();
+ return NULL;
+ }
+ result = BASE_ENV(env)->NewDirectByteBuffer(env, address, capacity);
+ CHECK_EXIT(env);
+ return result;
+}
+
+static void* Check_GetDirectBufferAddress(JNIEnv* env, jobject buf)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ CHECK_OBJECT(env, buf);
+ void* result = BASE_ENV(env)->GetDirectBufferAddress(env, buf);
+ CHECK_EXIT(env);
+
+ /* optional - check result vs. "safe" implementation */
+ if (kRedundantDirectBufferTest) {
+ jobject platformAddr = NULL;
+ void* checkResult = NULL;
+
+ /*
+ * Start by determining if the object supports the DirectBuffer
+ * interfaces. Note this does not guarantee that it's a direct buffer.
+ */
+ if (JNI_FALSE == (*env)->IsInstanceOf(env, buf,
+ gDvm.jclassOrgApacheHarmonyNioInternalDirectBuffer))
+ {
+ goto bail;
+ }
+
+ /*
+ * Get the PlatformAddress object.
+ *
+ * If this isn't a direct buffer, platformAddr will be NULL and/or an
+ * exception will have been thrown.
+ */
+ platformAddr = (*env)->CallObjectMethod(env, buf,
+ (jmethodID) gDvm.methOrgApacheHarmonyNioInternalDirectBuffer_getEffectiveAddress);
+
+ if ((*env)->ExceptionCheck(env)) {
+ (*env)->ExceptionClear(env);
+ platformAddr = NULL;
+ }
+ if (platformAddr == NULL) {
+ LOGV("Got request for address of non-direct buffer\n");
+ goto bail;
+ }
+
+ jclass platformAddrClass = (*env)->FindClass(env,
+ "org/apache/harmony/luni/platform/PlatformAddress");
+ jmethodID toLongMethod = (*env)->GetMethodID(env, platformAddrClass,
+ "toLong", "()J");
+ checkResult = (void*)(u4)(*env)->CallLongMethod(env, platformAddr,
+ toLongMethod);
+
+ bail:
+ if (platformAddr != NULL)
+ (*env)->DeleteLocalRef(env, platformAddr);
+
+ if (result != checkResult) {
+ LOGW("JNI WARNING: direct buffer result mismatch (%p vs %p)\n",
+ result, checkResult);
+ abortMaybe();
+ /* keep going */
+ }
+ }
+
+ return result;
+}
+
+static jlong Check_GetDirectBufferCapacity(JNIEnv* env, jobject buf)
+{
+ CHECK_ENTER(env, kFlag_Default);
+ CHECK_OBJECT(env, buf);
+ /* TODO: verify "buf" is an instance of java.nio.Buffer */
+ jlong result = BASE_ENV(env)->GetDirectBufferCapacity(env, buf);
+ CHECK_EXIT(env);
+ return result;
+}
+
+
+/*
+ * ===========================================================================
+ * JNI invocation functions
+ * ===========================================================================
+ */
+
+static jint Check_DestroyJavaVM(JavaVM* vm)
+{
+ CHECK_VMENTER(vm, false);
+ jint result;
+ result = BASE_VM(vm)->DestroyJavaVM(vm);
+ CHECK_VMEXIT(vm, false);
+ return result;
+}
+
+static jint Check_AttachCurrentThread(JavaVM* vm, JNIEnv** p_env,
+ void* thr_args)
+{
+ CHECK_VMENTER(vm, false);
+ jint result;
+ result = BASE_VM(vm)->AttachCurrentThread(vm, p_env, thr_args);
+ CHECK_VMEXIT(vm, true);
+ return result;
+}
+
+static jint Check_AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env,
+ void* thr_args)
+{
+ CHECK_VMENTER(vm, false);
+ jint result;
+ result = BASE_VM(vm)->AttachCurrentThreadAsDaemon(vm, p_env, thr_args);
+ CHECK_VMEXIT(vm, true);
+ return result;
+}
+
+static jint Check_DetachCurrentThread(JavaVM* vm)
+{
+ CHECK_VMENTER(vm, true);
+ jint result;
+ result = BASE_VM(vm)->DetachCurrentThread(vm);
+ CHECK_VMEXIT(vm, false);
+ return result;
+}
+
+static jint Check_GetEnv(JavaVM* vm, void** env, jint version)
+{
+ CHECK_VMENTER(vm, true);
+ jint result;
+ result = BASE_VM(vm)->GetEnv(vm, env, version);
+ CHECK_VMEXIT(vm, true);
+ return result;
+}
+
+
+/*
+ * ===========================================================================
+ * Function tables
+ * ===========================================================================
+ */
+
+static const struct JNINativeInterface gCheckNativeInterface = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+
+ Check_GetVersion,
+
+ Check_DefineClass,
+ Check_FindClass,
+
+ Check_FromReflectedMethod,
+ Check_FromReflectedField,
+ Check_ToReflectedMethod,
+
+ Check_GetSuperclass,
+ Check_IsAssignableFrom,
+
+ Check_ToReflectedField,
+
+ Check_Throw,
+ Check_ThrowNew,
+ Check_ExceptionOccurred,
+ Check_ExceptionDescribe,
+ Check_ExceptionClear,
+ Check_FatalError,
+
+ Check_PushLocalFrame,
+ Check_PopLocalFrame,
+
+ Check_NewGlobalRef,
+ Check_DeleteGlobalRef,
+ Check_DeleteLocalRef,
+ Check_IsSameObject,
+ Check_NewLocalRef,
+ Check_EnsureLocalCapacity,
+
+ Check_AllocObject,
+ Check_NewObject,
+ Check_NewObjectV,
+ Check_NewObjectA,
+
+ Check_GetObjectClass,
+ Check_IsInstanceOf,
+
+ Check_GetMethodID,
+
+ Check_CallObjectMethod,
+ Check_CallObjectMethodV,
+ Check_CallObjectMethodA,
+ Check_CallBooleanMethod,
+ Check_CallBooleanMethodV,
+ Check_CallBooleanMethodA,
+ Check_CallByteMethod,
+ Check_CallByteMethodV,
+ Check_CallByteMethodA,
+ Check_CallCharMethod,
+ Check_CallCharMethodV,
+ Check_CallCharMethodA,
+ Check_CallShortMethod,
+ Check_CallShortMethodV,
+ Check_CallShortMethodA,
+ Check_CallIntMethod,
+ Check_CallIntMethodV,
+ Check_CallIntMethodA,
+ Check_CallLongMethod,
+ Check_CallLongMethodV,
+ Check_CallLongMethodA,
+ Check_CallFloatMethod,
+ Check_CallFloatMethodV,
+ Check_CallFloatMethodA,
+ Check_CallDoubleMethod,
+ Check_CallDoubleMethodV,
+ Check_CallDoubleMethodA,
+ Check_CallVoidMethod,
+ Check_CallVoidMethodV,
+ Check_CallVoidMethodA,
+
+ Check_CallNonvirtualObjectMethod,
+ Check_CallNonvirtualObjectMethodV,
+ Check_CallNonvirtualObjectMethodA,
+ Check_CallNonvirtualBooleanMethod,
+ Check_CallNonvirtualBooleanMethodV,
+ Check_CallNonvirtualBooleanMethodA,
+ Check_CallNonvirtualByteMethod,
+ Check_CallNonvirtualByteMethodV,
+ Check_CallNonvirtualByteMethodA,
+ Check_CallNonvirtualCharMethod,
+ Check_CallNonvirtualCharMethodV,
+ Check_CallNonvirtualCharMethodA,
+ Check_CallNonvirtualShortMethod,
+ Check_CallNonvirtualShortMethodV,
+ Check_CallNonvirtualShortMethodA,
+ Check_CallNonvirtualIntMethod,
+ Check_CallNonvirtualIntMethodV,
+ Check_CallNonvirtualIntMethodA,
+ Check_CallNonvirtualLongMethod,
+ Check_CallNonvirtualLongMethodV,
+ Check_CallNonvirtualLongMethodA,
+ Check_CallNonvirtualFloatMethod,
+ Check_CallNonvirtualFloatMethodV,
+ Check_CallNonvirtualFloatMethodA,
+ Check_CallNonvirtualDoubleMethod,
+ Check_CallNonvirtualDoubleMethodV,
+ Check_CallNonvirtualDoubleMethodA,
+ Check_CallNonvirtualVoidMethod,
+ Check_CallNonvirtualVoidMethodV,
+ Check_CallNonvirtualVoidMethodA,
+
+ Check_GetFieldID,
+
+ Check_GetObjectField,
+ Check_GetBooleanField,
+ Check_GetByteField,
+ Check_GetCharField,
+ Check_GetShortField,
+ Check_GetIntField,
+ Check_GetLongField,
+ Check_GetFloatField,
+ Check_GetDoubleField,
+ Check_SetObjectField,
+ Check_SetBooleanField,
+ Check_SetByteField,
+ Check_SetCharField,
+ Check_SetShortField,
+ Check_SetIntField,
+ Check_SetLongField,
+ Check_SetFloatField,
+ Check_SetDoubleField,
+
+ Check_GetStaticMethodID,
+
+ Check_CallStaticObjectMethod,
+ Check_CallStaticObjectMethodV,
+ Check_CallStaticObjectMethodA,
+ Check_CallStaticBooleanMethod,
+ Check_CallStaticBooleanMethodV,
+ Check_CallStaticBooleanMethodA,
+ Check_CallStaticByteMethod,
+ Check_CallStaticByteMethodV,
+ Check_CallStaticByteMethodA,
+ Check_CallStaticCharMethod,
+ Check_CallStaticCharMethodV,
+ Check_CallStaticCharMethodA,
+ Check_CallStaticShortMethod,
+ Check_CallStaticShortMethodV,
+ Check_CallStaticShortMethodA,
+ Check_CallStaticIntMethod,
+ Check_CallStaticIntMethodV,
+ Check_CallStaticIntMethodA,
+ Check_CallStaticLongMethod,
+ Check_CallStaticLongMethodV,
+ Check_CallStaticLongMethodA,
+ Check_CallStaticFloatMethod,
+ Check_CallStaticFloatMethodV,
+ Check_CallStaticFloatMethodA,
+ Check_CallStaticDoubleMethod,
+ Check_CallStaticDoubleMethodV,
+ Check_CallStaticDoubleMethodA,
+ Check_CallStaticVoidMethod,
+ Check_CallStaticVoidMethodV,
+ Check_CallStaticVoidMethodA,
+
+ Check_GetStaticFieldID,
+
+ Check_GetStaticObjectField,
+ Check_GetStaticBooleanField,
+ Check_GetStaticByteField,
+ Check_GetStaticCharField,
+ Check_GetStaticShortField,
+ Check_GetStaticIntField,
+ Check_GetStaticLongField,
+ Check_GetStaticFloatField,
+ Check_GetStaticDoubleField,
+
+ Check_SetStaticObjectField,
+ Check_SetStaticBooleanField,
+ Check_SetStaticByteField,
+ Check_SetStaticCharField,
+ Check_SetStaticShortField,
+ Check_SetStaticIntField,
+ Check_SetStaticLongField,
+ Check_SetStaticFloatField,
+ Check_SetStaticDoubleField,
+
+ Check_NewString,
+
+ Check_GetStringLength,
+ Check_GetStringChars,
+ Check_ReleaseStringChars,
+
+ Check_NewStringUTF,
+ Check_GetStringUTFLength,
+ Check_GetStringUTFChars,
+ Check_ReleaseStringUTFChars,
+
+ Check_GetArrayLength,
+ Check_NewObjectArray,
+ Check_GetObjectArrayElement,
+ Check_SetObjectArrayElement,
+
+ Check_NewBooleanArray,
+ Check_NewByteArray,
+ Check_NewCharArray,
+ Check_NewShortArray,
+ Check_NewIntArray,
+ Check_NewLongArray,
+ Check_NewFloatArray,
+ Check_NewDoubleArray,
+
+ Check_GetBooleanArrayElements,
+ Check_GetByteArrayElements,
+ Check_GetCharArrayElements,
+ Check_GetShortArrayElements,
+ Check_GetIntArrayElements,
+ Check_GetLongArrayElements,
+ Check_GetFloatArrayElements,
+ Check_GetDoubleArrayElements,
+
+ Check_ReleaseBooleanArrayElements,
+ Check_ReleaseByteArrayElements,
+ Check_ReleaseCharArrayElements,
+ Check_ReleaseShortArrayElements,
+ Check_ReleaseIntArrayElements,
+ Check_ReleaseLongArrayElements,
+ Check_ReleaseFloatArrayElements,
+ Check_ReleaseDoubleArrayElements,
+
+ Check_GetBooleanArrayRegion,
+ Check_GetByteArrayRegion,
+ Check_GetCharArrayRegion,
+ Check_GetShortArrayRegion,
+ Check_GetIntArrayRegion,
+ Check_GetLongArrayRegion,
+ Check_GetFloatArrayRegion,
+ Check_GetDoubleArrayRegion,
+ Check_SetBooleanArrayRegion,
+ Check_SetByteArrayRegion,
+ Check_SetCharArrayRegion,
+ Check_SetShortArrayRegion,
+ Check_SetIntArrayRegion,
+ Check_SetLongArrayRegion,
+ Check_SetFloatArrayRegion,
+ Check_SetDoubleArrayRegion,
+
+ Check_RegisterNatives,
+ Check_UnregisterNatives,
+
+ Check_MonitorEnter,
+ Check_MonitorExit,
+
+ Check_GetJavaVM,
+
+ Check_GetStringRegion,
+ Check_GetStringUTFRegion,
+
+ Check_GetPrimitiveArrayCritical,
+ Check_ReleasePrimitiveArrayCritical,
+
+ Check_GetStringCritical,
+ Check_ReleaseStringCritical,
+
+ Check_NewWeakGlobalRef,
+ Check_DeleteWeakGlobalRef,
+
+ Check_ExceptionCheck,
+
+ Check_NewDirectByteBuffer,
+ Check_GetDirectBufferAddress,
+ Check_GetDirectBufferCapacity,
+
+ Check_GetObjectRefType
+};
+static const struct JNIInvokeInterface gCheckInvokeInterface = {
+ NULL,
+ NULL,
+ NULL,
+
+ Check_DestroyJavaVM,
+ Check_AttachCurrentThread,
+ Check_DetachCurrentThread,
+
+ Check_GetEnv,
+
+ Check_AttachCurrentThreadAsDaemon,
+};
+
+
+/*
+ * Replace the normal table with the checked table.
+ */
+void dvmUseCheckedJniEnv(JNIEnvExt* pEnv)
+{
+ assert(pEnv->funcTable != &gCheckNativeInterface);
+ pEnv->baseFuncTable = pEnv->funcTable;
+ pEnv->funcTable = &gCheckNativeInterface;
+}
+
+/*
+ * Replace the normal table with the checked table.
+ */
+void dvmUseCheckedJniVm(JavaVMExt* pVm)
+{
+ assert(pVm->funcTable != &gCheckInvokeInterface);
+ pVm->baseFuncTable = pVm->funcTable;
+ pVm->funcTable = &gCheckInvokeInterface;
+}
diff --git a/vm/Common.h b/vm/Common.h
new file mode 100644
index 0000000..ae0938a
--- /dev/null
+++ b/vm/Common.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Common defines for all Dalvik code.
+ */
+#ifndef _DALVIK_COMMON
+#define _DALVIK_COMMON
+
+#ifndef LOG_TAG
+# define LOG_TAG "dalvikvm"
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+
+#if !defined(NDEBUG) && defined(WITH_DALVIK_ASSERT)
+# undef assert
+# define assert(x) \
+ ((x) ? ((void)0) : (LOGE("ASSERT FAILED (%s:%d): %s\n", \
+ __FILE__, __LINE__, #x), *(int*)39=39, 0) )
+#endif
+
+#define MIN(x,y) (((x) < (y)) ? (x) : (y))
+#define MAX(x,y) (((x) > (y)) ? (x) : (y))
+
+#define LIKELY(exp) (__builtin_expect((exp) != 0, true))
+#define UNLIKELY(exp) (__builtin_expect((exp) != 0, false))
+
+/*
+ * If "very verbose" logging is enabled, make it equivalent to LOGV.
+ * Otherwise, make it disappear.
+ *
+ * Define this above the #include "Dalvik.h" to enable for only a
+ * single file.
+ */
+/* #define VERY_VERBOSE_LOG */
+#if defined(VERY_VERBOSE_LOG)
+# define LOGVV LOGV
+# define IF_LOGVV() IF_LOGV()
+#else
+# define LOGVV(...) ((void)0)
+# define IF_LOGVV() if (false)
+#endif
+
+
+/*
+ * These match the definitions in the VM specification.
+ */
+#ifdef HAVE_STDINT_H
+# include <stdint.h> /* C99 */
+typedef uint8_t u1;
+typedef uint16_t u2;
+typedef uint32_t u4;
+typedef uint64_t u8;
+typedef int8_t s1;
+typedef int16_t s2;
+typedef int32_t s4;
+typedef int64_t s8;
+#else
+typedef unsigned char u1;
+typedef unsigned short u2;
+typedef unsigned int u4;
+typedef unsigned long long u8;
+typedef signed char s1;
+typedef signed short s2;
+typedef signed int s4;
+typedef signed long long s8;
+#endif
+
+/*
+ * Storage for primitive types and object references.
+ *
+ * Some parts of the code (notably object field access) assume that values
+ * are "left aligned", i.e. given "JValue jv", "jv.i" and "*((s4*)&jv)"
+ * yield the same result. This seems to be guaranteed by gcc on big- and
+ * little-endian systems.
+ */
+typedef union JValue {
+ u1 z;
+ s1 b;
+ u2 c;
+ s2 s;
+ s4 i;
+ s8 j;
+ float f;
+ double d;
+ void* l;
+} JValue;
+
+/*
+ * The <stdbool.h> definition uses _Bool, a type known to the compiler.
+ */
+#ifdef HAVE_STDBOOL_H
+# include <stdbool.h> /* C99 */
+#else
+# ifndef __bool_true_false_are_defined
+typedef enum { false=0, true=!false } bool;
+# define __bool_true_false_are_defined 1
+# endif
+#endif
+
+#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
+
+
+#if defined(HAVE_ENDIAN_H)
+# include <endian.h>
+#else /*not HAVE_ENDIAN_H*/
+# define __BIG_ENDIAN 4321
+# define __LITTLE_ENDIAN 1234
+# if defined(HAVE_LITTLE_ENDIAN)
+# define __BYTE_ORDER __LITTLE_ENDIAN
+# else
+# define __BYTE_ORDER __BIG_ENDIAN
+# endif
+#endif /*not HAVE_ENDIAN_H*/
+
+
+#if 0
+/*
+ * Pretend we have the Android logging macros. These are replaced by the
+ * Android logging implementation.
+ */
+#define ANDROID_LOG_DEBUG 3
+#define LOGV(...) LOG_PRI(2, 0, __VA_ARGS__)
+#define LOGD(...) LOG_PRI(3, 0, __VA_ARGS__)
+#define LOGI(...) LOG_PRI(4, 0, __VA_ARGS__)
+#define LOGW(...) LOG_PRI(5, 0, __VA_ARGS__)
+#define LOGE(...) LOG_PRI(6, 0, __VA_ARGS__)
+#define MIN_LOG_LEVEL 2
+
+#define LOG_PRI(priority, tag, ...) do { \
+ if (priority >= MIN_LOG_LEVEL) { \
+ dvmFprintf(stdout, "%s:%-4d ", __FILE__, __LINE__); \
+ dvmFprintf(stdout, __VA_ARGS__); \
+ } \
+ } while(0)
+#else
+# include "utils/Log.h"
+#endif
+
+#endif /*_DALVIK_COMMON*/
diff --git a/vm/Dalvik.h b/vm/Dalvik.h
new file mode 100644
index 0000000..5779b08
--- /dev/null
+++ b/vm/Dalvik.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * All-inclusive internal header file. Include this to get everything useful.
+ */
+#ifndef _DALVIK_DALVIK
+#define _DALVIK_DALVIK
+
+#include <pthread.h>
+
+#include "Common.h"
+#include "Inlines.h"
+#include "Misc.h"
+#include "Bits.h"
+#include "libdex/SysUtil.h"
+#include "libdex/DexFile.h"
+#include "libdex/DexProto.h"
+#include "libdex/ZipArchive.h"
+#include "DvmDex.h"
+#include "RawDexFile.h"
+#include "Sync.h"
+#include "oo/Object.h"
+#include "Native.h"
+#include "native/InternalNative.h"
+
+#include "DalvikVersion.h"
+#include "Debugger.h"
+#include "Profile.h"
+#include "UtfString.h"
+#include "Intern.h"
+#include "ReferenceTable.h"
+#include "IndirectRefTable.h"
+#include "AtomicCache.h"
+#include "Thread.h"
+#include "Ddm.h"
+#include "Hash.h"
+#include "interp/Stack.h"
+#include "oo/Class.h"
+#include "oo/Resolve.h"
+#include "oo/Array.h"
+#include "Exception.h"
+#include "alloc/Alloc.h"
+#include "alloc/CardTable.h"
+#include "alloc/HeapDebug.h"
+#include "alloc/HeapWorker.h"
+#include "alloc/GC.h"
+#include "alloc/WriteBarrier.h"
+#include "oo/AccessCheck.h"
+#include "JarFile.h"
+#include "Properties.h"
+#include "jdwp/Jdwp.h"
+#include "SignalCatcher.h"
+#include "StdioConverter.h"
+#include "JniInternal.h"
+#include "LinearAlloc.h"
+#include "analysis/DexVerify.h"
+#include "analysis/DexPrepare.h"
+#include "analysis/RegisterMap.h"
+#include "Init.h"
+#include "libdex/OpCode.h"
+#include "libdex/InstrUtils.h"
+#include "AllocTracker.h"
+#include "PointerSet.h"
+#if defined(WITH_JIT)
+#include "compiler/Compiler.h"
+#endif
+#include "Globals.h"
+#include "reflect/Reflect.h"
+#include "oo/TypeCheck.h"
+#include "Atomic.h"
+#include "interp/Interp.h"
+#include "InlineNative.h"
+#include "oo/ObjectInlines.h"
+
+#endif /*_DALVIK_DALVIK*/
diff --git a/vm/DalvikVersion.h b/vm/DalvikVersion.h
new file mode 100644
index 0000000..3bc37b0
--- /dev/null
+++ b/vm/DalvikVersion.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik VM version info.
+ */
+#ifndef _DALVIK_VERSION
+#define _DALVIK_VERSION
+
+/*
+ * The version we show to tourists.
+ */
+#define DALVIK_MAJOR_VERSION 1
+#define DALVIK_MINOR_VERSION 4
+#define DALVIK_BUG_VERSION 0
+
+/*
+ * VM build number. This must change whenever something that affects the
+ * way classes load changes, e.g. field ordering or vtable layout. Changing
+ * this guarantees that the optimized form of the DEX file is regenerated.
+ */
+#define DALVIK_VM_BUILD 23
+
+#endif /*_DALVIK_VERSION*/
diff --git a/vm/Ddm.c b/vm/Ddm.c
new file mode 100644
index 0000000..774ed3f
--- /dev/null
+++ b/vm/Ddm.c
@@ -0,0 +1,606 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Handle Dalvik Debug Monitor requests and events.
+ *
+ * Remember that all DDM traffic is big-endian since it travels over the
+ * JDWP connection.
+ */
+#include "Dalvik.h"
+
+#include <fcntl.h>
+#include <errno.h>
+
+/*
+ * "buf" contains a full JDWP packet, possibly with multiple chunks. We
+ * need to process each, accumulate the replies, and ship the whole thing
+ * back.
+ *
+ * Returns "true" if we have a reply. The reply buffer is newly allocated,
+ * and includes the chunk type/length, followed by the data.
+ *
+ * TODO: we currently assume that the request and reply include a single
+ * chunk. If this becomes inconvenient we will need to adapt.
+ */
+bool dvmDdmHandlePacket(const u1* buf, int dataLen, u1** pReplyBuf,
+ int* pReplyLen)
+{
+ Thread* self = dvmThreadSelf();
+ const int kChunkHdrLen = 8;
+ ArrayObject* dataArray = NULL;
+ bool result = false;
+
+ assert(dataLen >= 0);
+
+ /*
+ * Prep DdmServer. We could throw this in gDvm.
+ */
+ ClassObject* ddmServerClass;
+ Method* dispatch;
+
+ ddmServerClass =
+ dvmFindClass("Lorg/apache/harmony/dalvik/ddmc/DdmServer;", NULL);
+ if (ddmServerClass == NULL) {
+ LOGW("Unable to find org.apache.harmony.dalvik.ddmc.DdmServer\n");
+ goto bail;
+ }
+ dispatch = dvmFindDirectMethodByDescriptor(ddmServerClass, "dispatch",
+ "(I[BII)Lorg/apache/harmony/dalvik/ddmc/Chunk;");
+ if (dispatch == NULL) {
+ LOGW("Unable to find DdmServer.dispatch\n");
+ goto bail;
+ }
+
+ /*
+ * Prep Chunk.
+ */
+ int chunkTypeOff, chunkDataOff, chunkOffsetOff, chunkLengthOff;
+ ClassObject* chunkClass;
+ chunkClass = dvmFindClass("Lorg/apache/harmony/dalvik/ddmc/Chunk;", NULL);
+ if (chunkClass == NULL) {
+ LOGW("Unable to find org.apache.harmony.dalvik.ddmc.Chunk\n");
+ goto bail;
+ }
+ chunkTypeOff = dvmFindFieldOffset(chunkClass, "type", "I");
+ chunkDataOff = dvmFindFieldOffset(chunkClass, "data", "[B");
+ chunkOffsetOff = dvmFindFieldOffset(chunkClass, "offset", "I");
+ chunkLengthOff = dvmFindFieldOffset(chunkClass, "length", "I");
+ if (chunkTypeOff < 0 || chunkDataOff < 0 ||
+ chunkOffsetOff < 0 || chunkLengthOff < 0)
+ {
+ LOGW("Unable to find all chunk fields\n");
+ goto bail;
+ }
+
+ /*
+ * The chunk handlers are written in the Java programming language, so
+ * we need to convert the buffer to a byte array.
+ */
+ dataArray = dvmAllocPrimitiveArray('B', dataLen, ALLOC_DEFAULT);
+ if (dataArray == NULL) {
+ LOGW("array alloc failed (%d)\n", dataLen);
+ dvmClearException(self);
+ goto bail;
+ }
+ memcpy(dataArray->contents, buf, dataLen);
+
+ /*
+ * Run through and find all chunks. [Currently just find the first.]
+ */
+ unsigned int offset, length, type;
+ type = get4BE((u1*)dataArray->contents + 0);
+ length = get4BE((u1*)dataArray->contents + 4);
+ offset = kChunkHdrLen;
+ if (offset+length > (unsigned int) dataLen) {
+ LOGW("WARNING: bad chunk found (len=%u pktLen=%d)\n", length, dataLen);
+ goto bail;
+ }
+
+ /*
+ * Call the handler.
+ */
+ JValue callRes;
+ dvmCallMethod(self, dispatch, NULL, &callRes, type, dataArray, offset,
+ length);
+ if (dvmCheckException(self)) {
+ LOGI("Exception thrown by dispatcher for 0x%08x\n", type);
+ dvmLogExceptionStackTrace();
+ dvmClearException(self);
+ goto bail;
+ }
+
+ Object* chunk;
+ ArrayObject* replyData;
+ chunk = (Object*) callRes.l;
+ if (chunk == NULL)
+ goto bail;
+
+ /*
+ * Pull the pieces out of the chunk. We copy the results into a
+ * newly-allocated buffer that the caller can free. We don't want to
+ * continue using the Chunk object because nothing has a reference to it.
+ * (If we do an alloc in here, we need to dvmAddTrackedAlloc it.)
+ *
+ * We could avoid this by returning type/data/offset/length and having
+ * the caller be aware of the object lifetime issues, but that
+ * integrates the JDWP code more tightly into the VM, and doesn't work
+ * if we have responses for multiple chunks.
+ *
+ * So we're pretty much stuck with copying data around multiple times.
+ */
+ type = dvmGetFieldInt(chunk, chunkTypeOff);
+ replyData = (ArrayObject*) dvmGetFieldObject(chunk, chunkDataOff);
+ offset = dvmGetFieldInt(chunk, chunkOffsetOff);
+ length = dvmGetFieldInt(chunk, chunkLengthOff);
+
+ LOGV("DDM reply: type=0x%08x data=%p offset=%d length=%d\n",
+ type, replyData, offset, length);
+
+ if (length == 0 || replyData == NULL)
+ goto bail;
+ if (offset + length > replyData->length) {
+ LOGW("WARNING: chunk off=%d len=%d exceeds reply array len %d\n",
+ offset, length, replyData->length);
+ goto bail;
+ }
+
+ u1* reply;
+ reply = (u1*) malloc(length + kChunkHdrLen);
+ if (reply == NULL) {
+ LOGW("malloc %d failed\n", length+kChunkHdrLen);
+ goto bail;
+ }
+ set4BE(reply + 0, type);
+ set4BE(reply + 4, length);
+ memcpy(reply+kChunkHdrLen, (const u1*)replyData->contents + offset, length);
+
+ *pReplyBuf = reply;
+ *pReplyLen = length + kChunkHdrLen;
+ result = true;
+
+ LOGV("dvmHandleDdm returning type=%.4s buf=%p len=%d\n",
+ (char*) reply, reply, length);
+
+bail:
+ dvmReleaseTrackedAlloc((Object*) dataArray, NULL);
+ return result;
+}
+
+/* defined in org.apache.harmony.dalvik.ddmc.DdmServer */
+#define CONNECTED 1
+#define DISCONNECTED 2
+
+/*
+ * Broadcast an event to all handlers.
+ */
+static void broadcast(int event)
+{
+ ClassObject* ddmServerClass;
+ Method* bcast;
+
+ ddmServerClass =
+ dvmFindClass("Lorg/apache/harmony/dalvik/ddmc/DdmServer;", NULL);
+ if (ddmServerClass == NULL) {
+ LOGW("Unable to find org.apache.harmony.dalvik.ddmc.DdmServer\n");
+ goto bail;
+ }
+ bcast = dvmFindDirectMethodByDescriptor(ddmServerClass, "broadcast", "(I)V");
+ if (bcast == NULL) {
+ LOGW("Unable to find DdmServer.broadcast\n");
+ goto bail;
+ }
+
+ Thread* self = dvmThreadSelf();
+
+ if (self->status != THREAD_RUNNING) {
+ LOGE("ERROR: DDM broadcast with thread status=%d\n", self->status);
+ /* try anyway? */
+ }
+
+ JValue unused;
+ dvmCallMethod(self, bcast, NULL, &unused, event);
+ if (dvmCheckException(self)) {
+ LOGI("Exception thrown by broadcast(%d)\n", event);
+ dvmLogExceptionStackTrace();
+ dvmClearException(self);
+ goto bail;
+ }
+
+bail:
+ ;
+}
+
+/*
+ * First DDM packet has arrived over JDWP. Notify the press.
+ *
+ * We can do some initialization here too.
+ */
+void dvmDdmConnected(void)
+{
+ // TODO: any init
+
+ LOGV("Broadcasting DDM connect\n");
+ broadcast(CONNECTED);
+}
+
+/*
+ * JDWP connection has dropped.
+ *
+ * Do some cleanup.
+ */
+void dvmDdmDisconnected(void)
+{
+ LOGV("Broadcasting DDM disconnect\n");
+ broadcast(DISCONNECTED);
+
+ gDvm.ddmThreadNotification = false;
+}
+
+
+/*
+ * Turn thread notification on or off.
+ */
+void dvmDdmSetThreadNotification(bool enable)
+{
+ /*
+ * We lock the thread list to avoid sending duplicate events or missing
+ * a thread change. We should be okay holding this lock while sending
+ * the messages out. (We have to hold it while accessing a live thread.)
+ */
+ dvmLockThreadList(NULL);
+ gDvm.ddmThreadNotification = enable;
+
+ if (enable) {
+ Thread* thread;
+ for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+ //LOGW("notify %d\n", thread->threadId);
+ dvmDdmSendThreadNotification(thread, true);
+ }
+ }
+
+ dvmUnlockThreadList();
+}
+
+/*
+ * Send a notification when a thread starts or stops.
+ *
+ * Because we broadcast the full set of threads when the notifications are
+ * first enabled, it's possible for "thread" to be actively executing.
+ */
+void dvmDdmSendThreadNotification(Thread* thread, bool started)
+{
+ if (!gDvm.ddmThreadNotification)
+ return;
+
+ StringObject* nameObj = NULL;
+ Object* threadObj = thread->threadObj;
+
+ if (threadObj != NULL) {
+ nameObj = (StringObject*)
+ dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_name);
+ }
+
+ int type, len;
+ u1 buf[256];
+
+ if (started) {
+ const u2* chars;
+ u2* outChars;
+ size_t stringLen;
+
+ type = CHUNK_TYPE("THCR");
+
+ if (nameObj != NULL) {
+ stringLen = dvmStringLen(nameObj);
+ chars = dvmStringChars(nameObj);
+ } else {
+ stringLen = 0;
+ chars = NULL;
+ }
+
+ /* leave room for the two integer fields */
+ if (stringLen > (sizeof(buf) - sizeof(u4)*2) / 2)
+ stringLen = (sizeof(buf) - sizeof(u4)*2) / 2;
+ len = stringLen*2 + sizeof(u4)*2;
+
+ set4BE(&buf[0x00], thread->threadId);
+ set4BE(&buf[0x04], stringLen);
+
+ /* copy the UTF-16 string, transforming to big-endian */
+ outChars = (u2*) &buf[0x08];
+ while (stringLen--)
+ set2BE((u1*) (outChars++), *chars++);
+ } else {
+ type = CHUNK_TYPE("THDE");
+
+ len = 4;
+
+ set4BE(&buf[0x00], thread->threadId);
+ }
+
+ dvmDbgDdmSendChunk(type, len, buf);
+}
+
+/*
+ * Send a notification when a thread's name changes.
+ */
+void dvmDdmSendThreadNameChange(int threadId, StringObject* newName)
+{
+ if (!gDvm.ddmThreadNotification)
+ return;
+
+ size_t stringLen = dvmStringLen(newName);
+ const u2* chars = dvmStringChars(newName);
+
+ /*
+ * Output format:
+ * (4b) thread ID
+ * (4b) stringLen
+ * (xb) string chars
+ */
+ int bufLen = 4 + 4 + (stringLen * 2);
+ u1 buf[bufLen];
+
+ set4BE(&buf[0x00], threadId);
+ set4BE(&buf[0x04], stringLen);
+ u2* outChars = (u2*) &buf[0x08];
+ while (stringLen--)
+ set2BE((u1*) (outChars++), *chars++);
+
+ dvmDbgDdmSendChunk(CHUNK_TYPE("THNM"), bufLen, buf);
+}
+
+/*
+ * Get some per-thread stats.
+ *
+ * This is currently generated by opening the appropriate "stat" file
+ * in /proc and reading the pile of stuff that comes out.
+ */
+static bool getThreadStats(pid_t pid, pid_t tid, unsigned long* pUtime,
+ unsigned long* pStime)
+{
+ /*
+ int pid;
+ char comm[128];
+ char state;
+ int ppid, pgrp, session, tty_nr, tpgid;
+ unsigned long flags, minflt, cminflt, majflt, cmajflt, utime, stime;
+ long cutime, cstime, priority, nice, zero, itrealvalue;
+ unsigned long starttime, vsize;
+ long rss;
+ unsigned long rlim, startcode, endcode, startstack, kstkesp, kstkeip;
+ unsigned long signal, blocked, sigignore, sigcatch, wchan, nswap, cnswap;
+ int exit_signal, processor;
+ unsigned long rt_priority, policy;
+
+ scanf("%d %s %c %d %d %d %d %d %lu %lu %lu %lu %lu %lu %lu %ld %ld %ld "
+ "%ld %ld %ld %lu %lu %ld %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu "
+ "%lu %lu %lu %d %d %lu %lu",
+ &pid, comm, &state, &ppid, &pgrp, &session, &tty_nr, &tpgid,
+ &flags, &minflt, &cminflt, &majflt, &cmajflt, &utime, &stime,
+ &cutime, &cstime, &priority, &nice, &zero, &itrealvalue,
+ &starttime, &vsize, &rss, &rlim, &startcode, &endcode,
+ &startstack, &kstkesp, &kstkeip, &signal, &blocked, &sigignore,
+ &sigcatch, &wchan, &nswap, &cnswap, &exit_signal, &processor,
+ &rt_priority, &policy);
+ */
+
+ char nameBuf[64];
+ int i, fd;
+
+ /*
+ * Open and read the appropriate file. This is expected to work on
+ * Linux but will fail on other platforms (e.g. Mac sim).
+ */
+ sprintf(nameBuf, "/proc/%d/task/%d/stat", (int) pid, (int) tid);
+ fd = open(nameBuf, O_RDONLY);
+ if (fd < 0) {
+ LOGV("Unable to open '%s': %s\n", nameBuf, strerror(errno));
+ return false;
+ }
+
+ char lineBuf[512]; // > 2x typical
+ int cc;
+ cc = read(fd, lineBuf, sizeof(lineBuf)-1);
+ if (cc <= 0) {
+ const char* msg = (cc == 0) ? "unexpected EOF" : strerror(errno);
+ LOGI("Unable to read '%s': %s\n", nameBuf, msg);
+ close(fd);
+ return false;
+ }
+ lineBuf[cc] = '\0';
+
+ /*
+ * Skip whitespace-separated tokens.
+ */
+ static const char* kWhitespace = " ";
+ char* cp = lineBuf;
+ for (i = 0; i < 13; i++) {
+ cp += strcspn(cp, kWhitespace); // skip token
+ cp += strspn(cp, kWhitespace); // skip whitespace
+ }
+
+ /*
+ * Grab the values we want.
+ */
+ char* endp;
+ *pUtime = strtoul(cp, &endp, 10);
+ if (endp == cp)
+ LOGI("Warning: strtoul failed on utime ('%.30s...')\n", cp);
+
+ cp += strcspn(cp, kWhitespace);
+ cp += strspn(cp, kWhitespace);
+
+ *pStime = strtoul(cp, &endp, 10);
+ if (endp == cp)
+ LOGI("Warning: strtoul failed on stime ('%.30s...')\n", cp);
+
+ close(fd);
+ return true;
+}
+
+/*
+ * Generate the contents of a THST chunk. The data encompasses all known
+ * threads.
+ *
+ * Response has:
+ * (1b) header len
+ * (1b) bytes per entry
+ * (2b) thread count
+ * Then, for each thread:
+ * (4b) threadId
+ * (1b) thread status
+ * (4b) tid
+ * (4b) utime
+ * (4b) stime
+ * (1b) is daemon?
+ *
+ * The length fields exist in anticipation of adding additional fields
+ * without wanting to break ddms or bump the full protocol version. I don't
+ * think it warrants full versioning. They might be extraneous and could
+ * be removed from a future version.
+ *
+ * Returns a new byte[] with the data inside, or NULL on failure. The
+ * caller must call dvmReleaseTrackedAlloc() on the array.
+ */
+ArrayObject* dvmDdmGenerateThreadStats(void)
+{
+ const int kHeaderLen = 4;
+ const int kBytesPerEntry = 18;
+
+ dvmLockThreadList(NULL);
+
+ Thread* thread;
+ int threadCount = 0;
+ for (thread = gDvm.threadList; thread != NULL; thread = thread->next)
+ threadCount++;
+
+ /*
+ * Create a temporary buffer. We can't perform heap allocation with
+ * the thread list lock held (could cause a GC). The output is small
+ * enough to sit on the stack.
+ */
+ int bufLen = kHeaderLen + threadCount * kBytesPerEntry;
+ u1 tmpBuf[bufLen];
+ u1* buf = tmpBuf;
+
+ set1(buf+0, kHeaderLen);
+ set1(buf+1, kBytesPerEntry);
+ set2BE(buf+2, (u2) threadCount);
+ buf += kHeaderLen;
+
+ pid_t pid = getpid();
+ for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+ unsigned long utime, stime;
+ bool isDaemon = false;
+
+ if (!getThreadStats(pid, thread->systemTid, &utime, &stime)) {
+ // failed; drop in empty values
+ utime = stime = 0;
+ }
+
+ Object* threadObj = thread->threadObj;
+ if (threadObj != NULL) {
+ isDaemon = dvmGetFieldBoolean(threadObj,
+ gDvm.offJavaLangThread_daemon);
+ }
+
+ set4BE(buf+0, thread->threadId);
+ set1(buf+4, thread->status);
+ set4BE(buf+5, thread->systemTid);
+ set4BE(buf+9, utime);
+ set4BE(buf+13, stime);
+ set1(buf+17, isDaemon);
+
+ buf += kBytesPerEntry;
+ }
+ dvmUnlockThreadList();
+
+
+ /*
+ * Create a byte array to hold the data.
+ */
+ ArrayObject* arrayObj = dvmAllocPrimitiveArray('B', bufLen, ALLOC_DEFAULT);
+ if (arrayObj != NULL)
+ memcpy(arrayObj->contents, tmpBuf, bufLen);
+ return arrayObj;
+}
+
+
+/*
+ * Find the specified thread and return its stack trace as an array of
+ * StackTraceElement objects.
+ */
+ArrayObject* dvmDdmGetStackTraceById(u4 threadId)
+{
+ Thread* self = dvmThreadSelf();
+ Thread* thread;
+ int* traceBuf;
+
+ dvmLockThreadList(self);
+
+ for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+ if (thread->threadId == threadId)
+ break;
+ }
+ if (thread == NULL) {
+ LOGI("dvmDdmGetStackTraceById: threadid=%d not found\n", threadId);
+ dvmUnlockThreadList();
+ return NULL;
+ }
+
+ /*
+ * Suspend the thread, pull out the stack trace, then resume the thread
+ * and release the thread list lock. If we're being asked to examine
+ * our own stack trace, skip the suspend/resume.
+ */
+ int stackDepth = -1;
+ if (thread != self)
+ dvmSuspendThread(thread);
+ traceBuf = dvmFillInStackTraceRaw(thread, &stackDepth);
+ if (thread != self)
+ dvmResumeThread(thread);
+ dvmUnlockThreadList();
+
+ /*
+ * Convert the raw buffer into an array of StackTraceElement.
+ */
+ ArrayObject* trace = dvmGetStackTraceRaw(traceBuf, stackDepth);
+ free(traceBuf);
+ return trace;
+}
+
+/*
+ * Gather up the allocation data and copy it into a byte[].
+ *
+ * Returns NULL on failure with an exception raised.
+ */
+ArrayObject* dvmDdmGetRecentAllocations(void)
+{
+ u1* data;
+ size_t len;
+
+ if (!dvmGenerateTrackedAllocationReport(&data, &len)) {
+ /* assume OOM */
+ dvmThrowException("Ljava/lang/OutOfMemoryError;","recent alloc native");
+ return NULL;
+ }
+
+ ArrayObject* arrayObj = dvmAllocPrimitiveArray('B', len, ALLOC_DEFAULT);
+ if (arrayObj != NULL)
+ memcpy(arrayObj->contents, data, len);
+ return arrayObj;
+}
diff --git a/vm/Ddm.h b/vm/Ddm.h
new file mode 100644
index 0000000..01f5d18
--- /dev/null
+++ b/vm/Ddm.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Dalvik Debug Monitor
+ */
+#ifndef _DALVIK_DDM
+#define _DALVIK_DDM
+
+/*
+ * Handle a packet full of DDM goodness.
+ *
+ * Returns "true" if we have anything to say in return; in which case,
+ * "*pReplyBuf" and "*pReplyLen" will also be set.
+ */
+bool dvmDdmHandlePacket(const u1* buf, int dataLen, u1** pReplyBuf,
+ int* pReplyLen);
+
+/*
+ * Deal with the DDM server connecting and disconnecting.
+ */
+void dvmDdmConnected(void);
+void dvmDdmDisconnected(void);
+
+/*
+ * Turn thread notification on or off.
+ */
+void dvmDdmSetThreadNotification(bool enable);
+
+/*
+ * If thread start/stop notification is enabled, call this when threads
+ * are created or die.
+ */
+void dvmDdmSendThreadNotification(Thread* thread, bool started);
+
+/*
+ * If thread start/stop notification is enabled, call this when the
+ * thread name changes.
+ */
+void dvmDdmSendThreadNameChange(int threadId, StringObject* newName);
+
+/*
+ * Generate a byte[] full of thread stats for a THST packet.
+ */
+ArrayObject* dvmDdmGenerateThreadStats(void);
+
+/*
+ * Let the heap know that the HPIF when value has changed.
+ *
+ * @return true iff the when value is supported by the VM.
+ */
+bool dvmDdmHandleHpifChunk(int when);
+
+/*
+ * Let the heap know that the HPSG or NHSG what/when values have changed.
+ *
+ * @param native false for an HPSG chunk, true for an NHSG chunk
+ *
+ * @return true iff the what/when values are supported by the VM.
+ */
+bool dvmDdmHandleHpsgNhsgChunk(int when, int what, bool native);
+
+/*
+ * Get an array of StackTraceElement objects for the specified thread.
+ */
+ArrayObject* dvmDdmGetStackTraceById(u4 threadId);
+
+/*
+ * Gather up recent allocation data and return it in a byte[].
+ *
+ * Returns NULL on failure with an exception raised.
+ */
+ArrayObject* dvmDdmGetRecentAllocations(void);
+
+#endif /*_DALVIK_DDM*/
diff --git a/vm/Debugger.c b/vm/Debugger.c
new file mode 100644
index 0000000..2cea85c
--- /dev/null
+++ b/vm/Debugger.c
@@ -0,0 +1,3071 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Link between JDWP and the VM. The code here only runs as a result of
+ * requests from the debugger, so speed is not essential. Maintaining
+ * isolation of the JDWP code should make it easier to maintain and reuse.
+ *
+ * Collecting all debugger-related pieces here will also allow us to #ifdef
+ * the JDWP code out of release builds.
+ */
+#include "Dalvik.h"
+
+/*
+Notes on garbage collection and object registration
+
+JDWP does not allow the debugger to assume that objects passed to it
+will not be garbage collected. It specifies explicit commands (e.g.
+ObjectReference.DisableCollection) to allow the debugger to manage
+object lifetime. It does, however, require that the VM not re-use an
+object ID unless an explicit "dispose" call has been made, and if the
+VM asks for a now-collected object we must return INVALID_OBJECT.
+
+JDWP also requires that, while the VM is suspended, no garbage collection
+occur. The JDWP docs suggest that this is obvious, because no threads
+can be running. Unfortunately it's not entirely clear how to deal
+with situations where the debugger itself allocates strings or executes
+code as part of displaying variables. The easiest way to enforce this,
+short of disabling GC whenever the debugger is connected, is to ensure
+that the debugger thread can't cause a GC: it has to expand the heap or
+fail to allocate. (Might want to make that "is debugger thread AND all
+other threads are suspended" to avoid unnecessary heap expansion by a
+poorly-timed JDWP request.)
+
+We use an "object registry" so that we can separate our internal
+representation from what we show the debugger. This allows us to
+return a registry table index instead of a pointer or handle.
+
+There are various approaches we can take to achieve correct behavior:
+
+(1) Disable garbage collection entirely while the debugger is attached.
+This is very easy, but doesn't allow extended debugging sessions on
+small devices.
+
+(2) Keep a list of all object references requested by or sent to the
+debugger, and include the list in the GC root set. This ensures that
+objects the debugger might care about don't go away. This is straightforward,
+but it can cause us to hold on to large objects and prevent finalizers from
+being executed.
+
+(3) Keep a list of what amount to weak object references. This way we
+don't interfere with the GC, and can support JDWP requests like
+"ObjectReference.IsCollected".
+
+The current implementation is #2. The set should be reasonably small and
+performance isn't critical, so a simple expanding array can be used.
+
+
+Notes on threads:
+
+The VM has a Thread struct associated with every active thread. The
+ThreadId we pass to the debugger is the ObjectId for the java/lang/Thread
+object, so to retrieve the VM's Thread struct we have to scan through the
+list looking for a match.
+
+When a thread goes away, we lock the list and free the struct. To
+avoid having the thread list updated or Thread structs freed out from
+under us, we want to acquire and hold the thread list lock while we're
+performing operations on Threads. Exceptions to this rule are noted in
+a couple of places.
+
+We can speed this up a bit by adding a Thread struct pointer to the
+java/lang/Thread object, and ensuring that both are discarded at the
+same time.
+*/
+
+#define THREAD_GROUP_ALL ((ObjectId) 0x12345) // magic, internal-only value
+
+#define kSlot0Sub 1000 // Eclipse workaround
+
+/*
+ * System init. We don't allocate the registry until first use.
+ * Make sure we do this before initializing JDWP.
+ */
+bool dvmDebuggerStartup(void)
+{
+ if (!dvmBreakpointStartup())
+ return false;
+
+ gDvm.dbgRegistry = dvmHashTableCreate(1000, NULL);
+ return (gDvm.dbgRegistry != NULL);
+}
+
+/*
+ * Free registry storage.
+ */
+void dvmDebuggerShutdown(void)
+{
+ dvmHashTableFree(gDvm.dbgRegistry);
+ gDvm.dbgRegistry = NULL;
+ dvmBreakpointShutdown();
+}
+
+
+/*
+ * Pass these through to the VM functions. Allows extended checking
+ * (e.g. "errorcheck" mutexes). If nothing else we can assert() success.
+ */
+void dvmDbgInitMutex(pthread_mutex_t* pMutex)
+{
+ dvmInitMutex(pMutex);
+}
+void dvmDbgLockMutex(pthread_mutex_t* pMutex)
+{
+ dvmLockMutex(pMutex);
+}
+void dvmDbgUnlockMutex(pthread_mutex_t* pMutex)
+{
+ dvmUnlockMutex(pMutex);
+}
+void dvmDbgInitCond(pthread_cond_t* pCond)
+{
+ pthread_cond_init(pCond, NULL);
+}
+void dvmDbgCondWait(pthread_cond_t* pCond, pthread_mutex_t* pMutex)
+{
+ int cc __attribute__ ((__unused__)) = pthread_cond_wait(pCond, pMutex);
+ assert(cc == 0);
+}
+void dvmDbgCondSignal(pthread_cond_t* pCond)
+{
+ int cc __attribute__ ((__unused__)) = pthread_cond_signal(pCond);
+ assert(cc == 0);
+}
+void dvmDbgCondBroadcast(pthread_cond_t* pCond)
+{
+ int cc __attribute__ ((__unused__)) = pthread_cond_broadcast(pCond);
+ assert(cc == 0);
+}
+
+
+/* keep track of type, in case we need to distinguish them someday */
+typedef enum RegistryType {
+ kObjectId = 0xc1, kRefTypeId
+} RegistryType;
+
+/*
+ * Hash function for object IDs. Since objects are at least 8 bytes, and
+ * could someday be allocated on 16-byte boundaries, we don't want to use
+ * the low 4 bits in our hash.
+ */
+static inline u4 registryHash(u4 val)
+{
+ return val >> 4;
+}
+
+/*
+ * (This is a dvmHashTableLookup() callback.)
+ */
+static int registryCompare(const void* obj1, const void* obj2)
+{
+ return (int) obj1 - (int) obj2;
+}
+
+
+/*
+ * Determine if an id is already in the list.
+ *
+ * If the list doesn't yet exist, this creates it.
+ *
+ * Lock the registry before calling here.
+ */
+#ifndef NDEBUG
+static bool lookupId(ObjectId id)
+{
+ void* found;
+
+ found = dvmHashTableLookup(gDvm.dbgRegistry, registryHash((u4) id),
+ (void*)(u4) id, registryCompare, false);
+ if (found == NULL)
+ return false;
+ assert(found == (void*)(u4) id);
+ return true;
+}
+#endif
+
+/*
+ * Register an object, if it hasn't already been.
+ *
+ * This is used for both ObjectId and RefTypeId. In theory we don't have
+ * to register RefTypeIds unless we're worried about classes unloading.
+ *
+ * Null references must be represented as zero, or the debugger will get
+ * very confused.
+ */
+static ObjectId registerObject(const Object* obj, RegistryType type, bool reg)
+{
+ ObjectId id;
+
+ if (obj == NULL)
+ return 0;
+
+ assert((u4) obj != 0xcccccccc);
+ assert((u4) obj > 0x100);
+
+ id = (ObjectId)(u4)obj | ((u8) type) << 32;
+ if (!reg)
+ return id;
+
+ dvmHashTableLock(gDvm.dbgRegistry);
+ if (!gDvm.debuggerConnected) {
+ /* debugger has detached while we were doing stuff? */
+ LOGI("ignoring registerObject request in thread=%d\n",
+ dvmThreadSelf()->threadId);
+ //dvmAbort();
+ goto bail;
+ }
+
+ (void) dvmHashTableLookup(gDvm.dbgRegistry, registryHash((u4) id),
+ (void*)(u4) id, registryCompare, true);
+
+bail:
+ dvmHashTableUnlock(gDvm.dbgRegistry);
+ return id;
+}
+
+/*
+ * (This is a HashForeachFunc callback.)
+ */
+static int markRef(void* data, void* arg)
+{
+ UNUSED_PARAMETER(arg);
+
+ //LOGI("dbg mark %p\n", data);
+ dvmMarkObjectNonNull(data);
+ return 0;
+}
+
+/* Mark all of the registered debugger references so the
+ * GC doesn't collect them.
+ */
+void dvmGcMarkDebuggerRefs()
+{
+ /* dvmDebuggerStartup() may not have been called before the first GC.
+ */
+ if (gDvm.dbgRegistry != NULL) {
+ dvmHashTableLock(gDvm.dbgRegistry);
+ dvmHashForeach(gDvm.dbgRegistry, markRef, NULL);
+ dvmHashTableUnlock(gDvm.dbgRegistry);
+ }
+}
+
+/*
+ * Verify that an object has been registered. If it hasn't, the debugger
+ * is asking for something we didn't send it, which means something
+ * somewhere is broken.
+ *
+ * If speed is an issue we can encode the registry index in the high
+ * four bytes. We could also just hard-wire this to "true".
+ *
+ * Note this actually takes both ObjectId and RefTypeId.
+ */
+#ifndef NDEBUG
+static bool objectIsRegistered(ObjectId id, RegistryType type)
+{
+ UNUSED_PARAMETER(type);
+
+ if (id == 0) // null reference?
+ return true;
+
+ dvmHashTableLock(gDvm.dbgRegistry);
+ bool result = lookupId(id);
+ dvmHashTableUnlock(gDvm.dbgRegistry);
+ return result;
+}
+#endif
+
+/*
+ * Convert to/from a RefTypeId.
+ *
+ * These are rarely NULL, but can be (e.g. java/lang/Object's superclass).
+ */
+static RefTypeId classObjectToRefTypeId(ClassObject* clazz)
+{
+ return (RefTypeId) registerObject((Object*) clazz, kRefTypeId, true);
+}
+#if 0
+static RefTypeId classObjectToRefTypeIdNoReg(ClassObject* clazz)
+{
+ return (RefTypeId) registerObject((Object*) clazz, kRefTypeId, false);
+}
+#endif
+static ClassObject* refTypeIdToClassObject(RefTypeId id)
+{
+ assert(objectIsRegistered(id, kRefTypeId) || !gDvm.debuggerConnected);
+ return (ClassObject*)(u4) id;
+}
+
+/*
+ * Convert to/from an ObjectId.
+ */
+static ObjectId objectToObjectId(const Object* obj)
+{
+ return registerObject(obj, kObjectId, true);
+}
+static ObjectId objectToObjectIdNoReg(const Object* obj)
+{
+ return registerObject(obj, kObjectId, false);
+}
+static Object* objectIdToObject(ObjectId id)
+{
+ assert(objectIsRegistered(id, kObjectId) || !gDvm.debuggerConnected);
+ return (Object*)(u4) id;
+}
+
+/*
+ * Register an object ID that might not have been registered previously.
+ *
+ * Normally this wouldn't happen -- the conversion to an ObjectId would
+ * have added the object to the registry -- but in some cases (e.g.
+ * throwing exceptions) we really want to do the registration late.
+ */
+void dvmDbgRegisterObjectId(ObjectId id)
+{
+ Object* obj = (Object*)(u4) id;
+ LOGV("+++ registering %p (%s)\n", obj, obj->clazz->descriptor);
+ registerObject(obj, kObjectId, true);
+}
+
+/*
+ * Convert to/from a MethodId.
+ *
+ * These IDs are only guaranteed unique within a class, so they could be
+ * an enumeration index. For now we just use the Method*.
+ */
+static MethodId methodToMethodId(const Method* meth)
+{
+ return (MethodId)(u4) meth;
+}
+static Method* methodIdToMethod(RefTypeId refTypeId, MethodId id)
+{
+ // TODO? verify "id" is actually a method in "refTypeId"
+ return (Method*)(u4) id;
+}
+
+/*
+ * Convert to/from a FieldId.
+ *
+ * These IDs are only guaranteed unique within a class, so they could be
+ * an enumeration index. For now we just use the Field*.
+ */
+static FieldId fieldToFieldId(const Field* field)
+{
+ return (FieldId)(u4) field;
+}
+static Field* fieldIdToField(RefTypeId refTypeId, FieldId id)
+{
+ // TODO? verify "id" is actually a field in "refTypeId"
+ return (Field*)(u4) id;
+}
+
+/*
+ * Convert to/from a FrameId.
+ *
+ * We just return a pointer to the stack frame.
+ */
+static FrameId frameToFrameId(const void* frame)
+{
+ return (FrameId)(u4) frame;
+}
+static void* frameIdToFrame(FrameId id)
+{
+ return (void*)(u4) id;
+}
+
+
+/*
+ * Get the invocation request state.
+ */
+DebugInvokeReq* dvmDbgGetInvokeReq(void)
+{
+ return &dvmThreadSelf()->invokeReq;
+}
+
+/*
+ * Enable the object registry, but don't enable debugging features yet.
+ *
+ * Only called from the JDWP handler thread.
+ */
+void dvmDbgConnected(void)
+{
+ assert(!gDvm.debuggerConnected);
+
+ LOGV("JDWP has attached\n");
+ assert(dvmHashTableNumEntries(gDvm.dbgRegistry) == 0);
+ gDvm.debuggerConnected = true;
+}
+
+/*
+ * Enable all debugging features, including scans for breakpoints.
+ *
+ * This is a no-op if we're already active.
+ *
+ * Only called from the JDWP handler thread.
+ */
+void dvmDbgActive(void)
+{
+ if (gDvm.debuggerActive)
+ return;
+
+ LOGI("Debugger is active\n");
+ dvmInitBreakpoints();
+ gDvm.debuggerActive = true;
+#if defined(WITH_JIT)
+ dvmCompilerStateRefresh();
+#endif
+}
+
+/*
+ * Disable debugging features.
+ *
+ * Set "debuggerConnected" to false, which disables use of the object
+ * registry.
+ *
+ * Only called from the JDWP handler thread.
+ */
+void dvmDbgDisconnected(void)
+{
+ assert(gDvm.debuggerConnected);
+
+ gDvm.debuggerActive = false;
+
+ dvmHashTableLock(gDvm.dbgRegistry);
+ gDvm.debuggerConnected = false;
+
+ LOGD("Debugger has detached; object registry had %d entries\n",
+ dvmHashTableNumEntries(gDvm.dbgRegistry));
+ //int i;
+ //for (i = 0; i < gDvm.dbgRegistryNext; i++)
+ // LOGVV("%4d: 0x%llx\n", i, gDvm.dbgRegistryTable[i]);
+
+ dvmHashTableClear(gDvm.dbgRegistry);
+ dvmHashTableUnlock(gDvm.dbgRegistry);
+#if defined(WITH_JIT)
+ dvmCompilerStateRefresh();
+#endif
+}
+
+/*
+ * Returns "true" if a debugger is connected.
+ *
+ * Does not return "true" if it's just a DDM server.
+ */
+bool dvmDbgIsDebuggerConnected(void)
+{
+ return gDvm.debuggerActive;
+}
+
+/*
+ * Get time since last debugger activity. Used when figuring out if the
+ * debugger has finished configuring us.
+ */
+s8 dvmDbgLastDebuggerActivity(void)
+{
+ return dvmJdwpLastDebuggerActivity(gDvm.jdwpState);
+}
+
+/*
+ * JDWP thread is running, don't allow GC.
+ */
+int dvmDbgThreadRunning(void)
+{
+ return dvmChangeStatus(NULL, THREAD_RUNNING);
+}
+
+/*
+ * JDWP thread is idle, allow GC.
+ */
+int dvmDbgThreadWaiting(void)
+{
+ return dvmChangeStatus(NULL, THREAD_VMWAIT);
+}
+
+/*
+ * Restore state returned by Running/Waiting calls.
+ */
+int dvmDbgThreadContinuing(int status)
+{
+ return dvmChangeStatus(NULL, status);
+}
+
+/*
+ * The debugger wants us to exit.
+ */
+void dvmDbgExit(int status)
+{
+ // TODO? invoke System.exit() to perform exit processing; ends up
+ // in System.exitInternal(), which can call JNI exit hook
+ LOGI("GC lifetime allocation: %d bytes\n", gDvm.allocProf.allocCount);
+ if (CALC_CACHE_STATS) {
+ dvmDumpAtomicCacheStats(gDvm.instanceofCache);
+ dvmDumpBootClassPath();
+ }
+#ifdef PROFILE_FIELD_ACCESS
+ dvmDumpFieldAccessCounts();
+#endif
+
+ exit(status);
+}
+
+
+/*
+ * ===========================================================================
+ * Class, Object, Array
+ * ===========================================================================
+ */
+
+/*
+ * Get the class's type descriptor from a reference type ID.
+ */
+const char* dvmDbgGetClassDescriptor(RefTypeId id)
+{
+ ClassObject* clazz;
+
+ clazz = refTypeIdToClassObject(id);
+ return clazz->descriptor;
+}
+
+/*
+ * Convert a RefTypeId to an ObjectId.
+ */
+ObjectId dvmDbgGetClassObject(RefTypeId id)
+{
+ ClassObject* clazz = refTypeIdToClassObject(id);
+ return objectToObjectId((Object*) clazz);
+}
+
+/*
+ * Return the superclass of a class (will be NULL for java/lang/Object).
+ */
+RefTypeId dvmDbgGetSuperclass(RefTypeId id)
+{
+ ClassObject* clazz = refTypeIdToClassObject(id);
+ return classObjectToRefTypeId(clazz->super);
+}
+
+/*
+ * Return a class's defining class loader.
+ */
+RefTypeId dvmDbgGetClassLoader(RefTypeId id)
+{
+ ClassObject* clazz = refTypeIdToClassObject(id);
+ return objectToObjectId(clazz->classLoader);
+}
+
+/*
+ * Return a class's access flags.
+ */
+u4 dvmDbgGetAccessFlags(RefTypeId id)
+{
+ ClassObject* clazz = refTypeIdToClassObject(id);
+ return clazz->accessFlags & JAVA_FLAGS_MASK;
+}
+
+/*
+ * Is this class an interface?
+ */
+bool dvmDbgIsInterface(RefTypeId id)
+{
+ ClassObject* clazz = refTypeIdToClassObject(id);
+ return dvmIsInterfaceClass(clazz);
+}
+
+/*
+ * dvmHashForeach callback
+ */
+static int copyRefType(void* vclazz, void* varg)
+{
+ RefTypeId** pRefType = (RefTypeId**)varg;
+ **pRefType = classObjectToRefTypeId((ClassObject*) vclazz);
+ (*pRefType)++;
+ return 0;
+}
+
+/*
+ * Get the complete list of reference classes (i.e. all classes except
+ * the primitive types).
+ *
+ * Returns a newly-allocated buffer full of RefTypeId values.
+ */
+void dvmDbgGetClassList(u4* pNumClasses, RefTypeId** pClassRefBuf)
+{
+ RefTypeId* pRefType;
+
+ dvmHashTableLock(gDvm.loadedClasses);
+ *pNumClasses = dvmHashTableNumEntries(gDvm.loadedClasses);
+ pRefType = *pClassRefBuf = malloc(sizeof(RefTypeId) * *pNumClasses);
+
+ if (dvmHashForeach(gDvm.loadedClasses, copyRefType, &pRefType) != 0) {
+ LOGW("Warning: problem getting class list\n");
+ /* not really expecting this to happen */
+ } else {
+ assert(pRefType - *pClassRefBuf == (int) *pNumClasses);
+ }
+
+ dvmHashTableUnlock(gDvm.loadedClasses);
+}
+
+/*
+ * Get the list of reference classes "visible" to the specified class
+ * loader. A class is visible to a class loader if the ClassLoader object
+ * is the defining loader or is listed as an initiating loader.
+ *
+ * Returns a newly-allocated buffer full of RefTypeId values.
+ */
+void dvmDbgGetVisibleClassList(ObjectId classLoaderId, u4* pNumClasses,
+ RefTypeId** pClassRefBuf)
+{
+ Object* classLoader;
+ int numClasses = 0, maxClasses;
+
+ classLoader = objectIdToObject(classLoaderId);
+ // I don't think classLoader can be NULL, but the spec doesn't say
+
+ LOGVV("GetVisibleList: comparing to %p\n", classLoader);
+
+ dvmHashTableLock(gDvm.loadedClasses);
+
+ /* over-allocate the return buffer */
+ maxClasses = dvmHashTableNumEntries(gDvm.loadedClasses);
+ *pClassRefBuf = malloc(sizeof(RefTypeId) * maxClasses);
+
+ /*
+ * Run through the list, looking for matches.
+ */
+ HashIter iter;
+ for (dvmHashIterBegin(gDvm.loadedClasses, &iter); !dvmHashIterDone(&iter);
+ dvmHashIterNext(&iter))
+ {
+ ClassObject* clazz = (ClassObject*) dvmHashIterData(&iter);
+
+ if (clazz->classLoader == classLoader ||
+ dvmLoaderInInitiatingList(clazz, classLoader))
+ {
+ LOGVV(" match '%s'\n", clazz->descriptor);
+ (*pClassRefBuf)[numClasses++] = classObjectToRefTypeId(clazz);
+ }
+ }
+ *pNumClasses = numClasses;
+
+ dvmHashTableUnlock(gDvm.loadedClasses);
+}
+
+/*
+ * Generate the "JNI signature" for a class, e.g. "Ljava/lang/String;".
+ *
+ * Our class descriptors are in the correct format, so we just copy that.
+ * TODO: figure out if we can avoid the copy now that we're using
+ * descriptors instead of unadorned class names.
+ *
+ * Returns a newly-allocated string.
+ */
+static char* generateJNISignature(ClassObject* clazz)
+{
+ return strdup(clazz->descriptor);
+}
+
+/*
+ * Get information about a class.
+ *
+ * If "pSignature" is not NULL, *pSignature gets the "JNI signature" of
+ * the class.
+ */
+void dvmDbgGetClassInfo(RefTypeId classId, u1* pTypeTag, u4* pStatus,
+ char** pSignature)
+{
+ ClassObject* clazz = refTypeIdToClassObject(classId);
+
+ if (clazz->descriptor[0] == '[') {
+ /* generated array class */
+ *pStatus = CS_VERIFIED | CS_PREPARED;
+ *pTypeTag = TT_ARRAY;
+ } else {
+ if (clazz->status == CLASS_ERROR)
+ *pStatus = CS_ERROR;
+ else
+ *pStatus = CS_VERIFIED | CS_PREPARED | CS_INITIALIZED;
+ if (dvmIsInterfaceClass(clazz))
+ *pTypeTag = TT_INTERFACE;
+ else
+ *pTypeTag = TT_CLASS;
+ }
+ if (pSignature != NULL)
+ *pSignature = generateJNISignature(clazz);
+}
+
+/*
+ * Search the list of loaded classes for a match.
+ */
+bool dvmDbgFindLoadedClassBySignature(const char* classDescriptor,
+ RefTypeId* pRefTypeId)
+{
+ ClassObject* clazz;
+
+ clazz = dvmFindLoadedClass(classDescriptor);
+ if (clazz != NULL) {
+ *pRefTypeId = classObjectToRefTypeId(clazz);
+ return true;
+ } else
+ return false;
+}
+
+
+/*
+ * Get an object's class and "type tag".
+ */
+void dvmDbgGetObjectType(ObjectId objectId, u1* pRefTypeTag,
+ RefTypeId* pRefTypeId)
+{
+ Object* obj = objectIdToObject(objectId);
+
+ if (dvmIsArrayClass(obj->clazz))
+ *pRefTypeTag = TT_ARRAY;
+ else if (dvmIsInterfaceClass(obj->clazz))
+ *pRefTypeTag = TT_INTERFACE;
+ else
+ *pRefTypeTag = TT_CLASS;
+ *pRefTypeId = classObjectToRefTypeId(obj->clazz);
+}
+
+/*
+ * Get a class object's "type tag".
+ */
+u1 dvmDbgGetClassObjectType(RefTypeId refTypeId)
+{
+ ClassObject* clazz = refTypeIdToClassObject(refTypeId);
+
+ if (dvmIsArrayClass(clazz))
+ return TT_ARRAY;
+ else if (dvmIsInterfaceClass(clazz))
+ return TT_INTERFACE;
+ else
+ return TT_CLASS;
+}
+
+/*
+ * Get a class' signature.
+ *
+ * Returns a newly-allocated string.
+ */
+char* dvmDbgGetSignature(RefTypeId refTypeId)
+{
+ ClassObject* clazz;
+
+ clazz = refTypeIdToClassObject(refTypeId);
+ assert(clazz != NULL);
+
+ return generateJNISignature(clazz);
+}
+
+/*
+ * Get class' source file.
+ *
+ * Returns a newly-allocated string.
+ */
+const char* dvmDbgGetSourceFile(RefTypeId refTypeId)
+{
+ ClassObject* clazz;
+
+ clazz = refTypeIdToClassObject(refTypeId);
+ assert(clazz != NULL);
+
+ return clazz->sourceFile;
+}
+
+/*
+ * Get an object's type name. Converted to a "JNI signature".
+ *
+ * Returns a newly-allocated string.
+ */
+char* dvmDbgGetObjectTypeName(ObjectId objectId)
+{
+ Object* obj = objectIdToObject(objectId);
+
+ assert(obj != NULL);
+
+ return generateJNISignature(obj->clazz);
+}
+
+/*
+ * Given a type signature (e.g. "Ljava/lang/String;"), return the JDWP
+ * "type tag".
+ *
+ * In many cases this is necessary but not sufficient. For example, if
+ * we have a NULL String object, we want to return JT_STRING. If we have
+ * a java/lang/Object that holds a String reference, we also want to
+ * return JT_STRING. See dvmDbgGetObjectTag().
+ */
+int dvmDbgGetSignatureTag(const char* type)
+{
+ /*
+ * We're not checking the class loader here (to guarantee that JT_STRING
+ * is truly the one and only String), but it probably doesn't matter
+ * for our purposes.
+ */
+ if (strcmp(type, "Ljava/lang/String;") == 0)
+ return JT_STRING;
+ else if (strcmp(type, "Ljava/lang/Class;") == 0)
+ return JT_CLASS_OBJECT;
+ else if (strcmp(type, "Ljava/lang/Thread;") == 0)
+ return JT_THREAD;
+ else if (strcmp(type, "Ljava/lang/ThreadGroup;") == 0)
+ return JT_THREAD_GROUP;
+ else if (strcmp(type, "Ljava/lang/ClassLoader;") == 0)
+ return JT_CLASS_LOADER;
+
+ switch (type[0]) {
+ case '[': return JT_ARRAY;
+ case 'B': return JT_BYTE;
+ case 'C': return JT_CHAR;
+ case 'L': return JT_OBJECT;
+ case 'F': return JT_FLOAT;
+ case 'D': return JT_DOUBLE;
+ case 'I': return JT_INT;
+ case 'J': return JT_LONG;
+ case 'S': return JT_SHORT;
+ case 'V': return JT_VOID;
+ case 'Z': return JT_BOOLEAN;
+ default:
+ LOGE("ERROR: unhandled type '%s'\n", type);
+ assert(false);
+ return -1;
+ }
+}
+
+/*
+ * Methods declared to return Object might actually be returning one
+ * of the "refined types". We need to check the object explicitly.
+ */
+static u1 resultTagFromObject(Object* obj)
+{
+ ClassObject* clazz;
+
+ if (obj == NULL)
+ return JT_OBJECT;
+
+ clazz = obj->clazz;
+
+ /*
+ * Comparing against the known classes is faster than string
+ * comparisons. It ensures that we only find the classes in the
+ * bootstrap class loader, which may or may not be what we want.
+ */
+ if (clazz == gDvm.classJavaLangString)
+ return JT_STRING;
+ else if (clazz == gDvm.classJavaLangClass)
+ return JT_CLASS_OBJECT;
+ else if (clazz == gDvm.classJavaLangThread)
+ return JT_THREAD;
+ else if (clazz == gDvm.classJavaLangThreadGroup)
+ return JT_THREAD_GROUP;
+ else if (strcmp(clazz->descriptor, "Ljava/lang/ClassLoader;") == 0)
+ return JT_CLASS_LOADER;
+ else if (clazz->descriptor[0] == '[')
+ return JT_ARRAY;
+ else
+ return JT_OBJECT;
+}
+
+/*
+ * Determine the tag for an object with a known type.
+ */
+int dvmDbgGetObjectTag(ObjectId objectId, const char* type)
+{
+ u1 tag;
+
+ tag = dvmDbgGetSignatureTag(type);
+ if (tag == JT_OBJECT && objectId != 0)
+ tag = resultTagFromObject(objectIdToObject(objectId));
+
+ return tag;
+}
+
+/*
+ * Get the widths of the specified JDWP.Tag value.
+ */
+int dvmDbgGetTagWidth(int tag)
+{
+ switch (tag) {
+ case JT_VOID:
+ return 0;
+ case JT_BYTE:
+ case JT_BOOLEAN:
+ return 1;
+ case JT_CHAR:
+ case JT_SHORT:
+ return 2;
+ case JT_FLOAT:
+ case JT_INT:
+ return 4;
+ case JT_ARRAY:
+ case JT_OBJECT:
+ case JT_STRING:
+ case JT_THREAD:
+ case JT_THREAD_GROUP:
+ case JT_CLASS_LOADER:
+ case JT_CLASS_OBJECT:
+ return sizeof(ObjectId);
+ case JT_DOUBLE:
+ case JT_LONG:
+ return 8;
+ default:
+ LOGE("ERROR: unhandled tag '%c'\n", tag);
+ assert(false);
+ return -1;
+ }
+}
+
+/*
+ * Determine whether or not a tag represents a primitive type.
+ */
+static bool isTagPrimitive(u1 tag)
+{
+ switch (tag) {
+ case JT_BYTE:
+ case JT_CHAR:
+ case JT_FLOAT:
+ case JT_DOUBLE:
+ case JT_INT:
+ case JT_LONG:
+ case JT_SHORT:
+ case JT_VOID:
+ case JT_BOOLEAN:
+ return true;
+ case JT_ARRAY:
+ case JT_OBJECT:
+ case JT_STRING:
+ case JT_CLASS_OBJECT:
+ case JT_THREAD:
+ case JT_THREAD_GROUP:
+ case JT_CLASS_LOADER:
+ return false;
+ default:
+ LOGE("ERROR: unhandled tag '%c'\n", tag);
+ assert(false);
+ return false;
+ }
+}
+
+
+/*
+ * Return the length of the specified array.
+ */
+int dvmDbgGetArrayLength(ObjectId arrayId)
+{
+ ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
+ assert(dvmIsArray(arrayObj));
+ return arrayObj->length;
+}
+
+/*
+ * Return a tag indicating the general type of elements in the array.
+ */
+int dvmDbgGetArrayElementTag(ObjectId arrayId)
+{
+ ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
+
+ assert(dvmIsArray(arrayObj));
+
+ return dvmDbgGetSignatureTag(arrayObj->obj.clazz->descriptor + 1);
+}
+
+/*
+ * Copy a series of values with the specified width, changing the byte
+ * ordering to big-endian.
+ */
+static void copyValuesToBE(u1* out, const u1* in, int count, int width)
+{
+ int i;
+
+ switch (width) {
+ case 1:
+ memcpy(out, in, count);
+ break;
+ case 2:
+ for (i = 0; i < count; i++)
+ *(((u2*) out)+i) = get2BE(in + i*2);
+ break;
+ case 4:
+ for (i = 0; i < count; i++)
+ *(((u4*) out)+i) = get4BE(in + i*4);
+ break;
+ case 8:
+ for (i = 0; i < count; i++)
+ *(((u8*) out)+i) = get8BE(in + i*8);
+ break;
+ default:
+ assert(false);
+ }
+}
+
+/*
+ * Copy a series of values with the specified with, changing the
+ * byte order from big-endian.
+ */
+static void copyValuesFromBE(u1* out, const u1* in, int count, int width)
+{
+ int i;
+
+ switch (width) {
+ case 1:
+ memcpy(out, in, count);
+ break;
+ case 2:
+ for (i = 0; i < count; i++)
+ set2BE(out + i*2, *((u2*)in + i));
+ break;
+ case 4:
+ for (i = 0; i < count; i++)
+ set4BE(out + i*4, *((u4*)in + i));
+ break;
+ case 8:
+ for (i = 0; i < count; i++)
+ set8BE(out + i*8, *((u8*)in + i));
+ break;
+ default:
+ assert(false);
+ }
+}
+
+/*
+ * Output a piece of an array to the reply buffer.
+ *
+ * Returns "false" if something looks fishy.
+ */
+bool dvmDbgOutputArray(ObjectId arrayId, int firstIndex, int count,
+ ExpandBuf* pReply)
+{
+ ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
+ const u1* data = (const u1*)arrayObj->contents;
+ u1 tag;
+
+ assert(dvmIsArray(arrayObj));
+
+ if (firstIndex + count > (int)arrayObj->length) {
+ LOGW("Request for index=%d + count=%d excceds length=%d\n",
+ firstIndex, count, arrayObj->length);
+ return false;
+ }
+
+ tag = dvmDbgGetSignatureTag(arrayObj->obj.clazz->descriptor + 1);
+
+ if (isTagPrimitive(tag)) {
+ int width = dvmDbgGetTagWidth(tag);
+ u1* outBuf;
+
+ outBuf = expandBufAddSpace(pReply, count * width);
+
+ copyValuesToBE(outBuf, data + firstIndex*width, count, width);
+ } else {
+ Object** pObjects;
+ int i;
+
+ pObjects = (Object**) data;
+ pObjects += firstIndex;
+
+ LOGV(" --> copying %d object IDs\n", count);
+ //assert(tag == JT_OBJECT); // could be object or "refined" type
+
+ for (i = 0; i < count; i++, pObjects++) {
+ u1 thisTag;
+ if (*pObjects != NULL)
+ thisTag = resultTagFromObject(*pObjects);
+ else
+ thisTag = tag;
+ expandBufAdd1(pReply, thisTag);
+ expandBufAddObjectId(pReply, objectToObjectId(*pObjects));
+ }
+ }
+
+ return true;
+}
+
+/*
+ * Set a range of elements in an array from the data in "buf".
+ */
+bool dvmDbgSetArrayElements(ObjectId arrayId, int firstIndex, int count,
+ const u1* buf)
+{
+ ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
+ u1* data = (u1*)arrayObj->contents;
+ u1 tag;
+
+ assert(dvmIsArray(arrayObj));
+
+ if (firstIndex + count > (int)arrayObj->length) {
+ LOGW("Attempt to set index=%d + count=%d excceds length=%d\n",
+ firstIndex, count, arrayObj->length);
+ return false;
+ }
+
+ tag = dvmDbgGetSignatureTag(arrayObj->obj.clazz->descriptor + 1);
+
+ if (isTagPrimitive(tag)) {
+ int width = dvmDbgGetTagWidth(tag);
+
+ LOGV(" --> setting %d '%c' width=%d\n", count, tag, width);
+
+ copyValuesFromBE(data + firstIndex*width, buf, count, width);
+ } else {
+ Object** pObjects;
+ int i;
+
+ pObjects = (Object**) data;
+ pObjects += firstIndex;
+
+ LOGV(" --> setting %d objects", count);
+
+ /* should do array type check here */
+ for (i = 0; i < count; i++) {
+ ObjectId id = dvmReadObjectId(&buf);
+ *pObjects++ = objectIdToObject(id);
+ }
+ }
+
+ return true;
+}
+
+/*
+ * Create a new string.
+ *
+ * The only place the reference will be held in the VM is in our registry.
+ */
+ObjectId dvmDbgCreateString(const char* str)
+{
+ StringObject* strObj;
+
+ strObj = dvmCreateStringFromCstr(str);
+ dvmReleaseTrackedAlloc((Object*) strObj, NULL);
+ return objectToObjectId((Object*) strObj);
+}
+
+/*
+ * Allocate a new object of the specified type.
+ *
+ * Add it to the registry to prevent it from being GCed.
+ */
+ObjectId dvmDbgCreateObject(RefTypeId classId)
+{
+ ClassObject* clazz = refTypeIdToClassObject(classId);
+ Object* newObj = dvmAllocObject(clazz, ALLOC_DEFAULT);
+ dvmReleaseTrackedAlloc(newObj, NULL);
+ return objectToObjectId(newObj);
+}
+
+/*
+ * Allocate a new array object of the specified type and length. The
+ * type is the array type, not the element type.
+ *
+ * Add it to the registry to prevent it from being GCed.
+ */
+ObjectId dvmDbgCreateArrayObject(RefTypeId arrayTypeId, u4 length)
+{
+ ClassObject* clazz = refTypeIdToClassObject(arrayTypeId);
+ Object* newObj = (Object*) dvmAllocArrayByClass(clazz, length, ALLOC_DEFAULT);
+ dvmReleaseTrackedAlloc(newObj, NULL);
+ return objectToObjectId(newObj);
+}
+
+/*
+ * Determine if "instClassId" is an instance of "classId".
+ */
+bool dvmDbgMatchType(RefTypeId instClassId, RefTypeId classId)
+{
+ ClassObject* instClazz = refTypeIdToClassObject(instClassId);
+ ClassObject* clazz = refTypeIdToClassObject(classId);
+
+ return dvmInstanceof(instClazz, clazz);
+}
+
+
+/*
+ * ===========================================================================
+ * Method and Field
+ * ===========================================================================
+ */
+
+/*
+ * Get the method name from a MethodId.
+ */
+const char* dvmDbgGetMethodName(RefTypeId refTypeId, MethodId id)
+{
+ Method* meth;
+
+ meth = methodIdToMethod(refTypeId, id);
+ return meth->name;
+}
+
+/*
+ * For ReferenceType.Fields and ReferenceType.FieldsWithGeneric:
+ * output all fields declared by the class. Inerhited fields are
+ * not included.
+ */
+void dvmDbgOutputAllFields(RefTypeId refTypeId, bool withGeneric,
+ ExpandBuf* pReply)
+{
+ static const u1 genericSignature[1] = "";
+ ClassObject* clazz;
+ Field* field;
+ u4 declared;
+ int i;
+
+ clazz = refTypeIdToClassObject(refTypeId);
+ assert(clazz != NULL);
+
+ declared = clazz->sfieldCount + clazz->ifieldCount;
+ expandBufAdd4BE(pReply, declared);
+
+ for (i = 0; i < clazz->sfieldCount; i++) {
+ field = (Field*) &clazz->sfields[i];
+
+ expandBufAddFieldId(pReply, fieldToFieldId(field));
+ expandBufAddUtf8String(pReply, (const u1*) field->name);
+ expandBufAddUtf8String(pReply, (const u1*) field->signature);
+ if (withGeneric)
+ expandBufAddUtf8String(pReply, genericSignature);
+ expandBufAdd4BE(pReply, field->accessFlags);
+ }
+ for (i = 0; i < clazz->ifieldCount; i++) {
+ field = (Field*) &clazz->ifields[i];
+
+ expandBufAddFieldId(pReply, fieldToFieldId(field));
+ expandBufAddUtf8String(pReply, (const u1*) field->name);
+ expandBufAddUtf8String(pReply, (const u1*) field->signature);
+ if (withGeneric)
+ expandBufAddUtf8String(pReply, genericSignature);
+ expandBufAdd4BE(pReply, field->accessFlags);
+ }
+}
+
+/*
+ * For ReferenceType.Methods and ReferenceType.MethodsWithGeneric:
+ * output all methods declared by the class. Inherited methods are
+ * not included.
+ */
+void dvmDbgOutputAllMethods(RefTypeId refTypeId, bool withGeneric,
+ ExpandBuf* pReply)
+{
+ DexStringCache stringCache;
+ static const u1 genericSignature[1] = "";
+ ClassObject* clazz;
+ Method* meth;
+ u4 declared;
+ int i;
+
+ dexStringCacheInit(&stringCache);
+
+ clazz = refTypeIdToClassObject(refTypeId);
+ assert(clazz != NULL);
+
+ declared = clazz->directMethodCount + clazz->virtualMethodCount;
+ expandBufAdd4BE(pReply, declared);
+
+ for (i = 0; i < clazz->directMethodCount; i++) {
+ meth = &clazz->directMethods[i];
+
+ expandBufAddMethodId(pReply, methodToMethodId(meth));
+ expandBufAddUtf8String(pReply, (const u1*) meth->name);
+
+ expandBufAddUtf8String(pReply,
+ (const u1*) dexProtoGetMethodDescriptor(&meth->prototype,
+ &stringCache));
+
+ if (withGeneric)
+ expandBufAddUtf8String(pReply, genericSignature);
+ expandBufAdd4BE(pReply, meth->accessFlags);
+ }
+ for (i = 0; i < clazz->virtualMethodCount; i++) {
+ meth = &clazz->virtualMethods[i];
+
+ expandBufAddMethodId(pReply, methodToMethodId(meth));
+ expandBufAddUtf8String(pReply, (const u1*) meth->name);
+
+ expandBufAddUtf8String(pReply,
+ (const u1*) dexProtoGetMethodDescriptor(&meth->prototype,
+ &stringCache));
+
+ if (withGeneric)
+ expandBufAddUtf8String(pReply, genericSignature);
+ expandBufAdd4BE(pReply, meth->accessFlags);
+ }
+
+ dexStringCacheRelease(&stringCache);
+}
+
+/*
+ * Output all interfaces directly implemented by the class.
+ */
+void dvmDbgOutputAllInterfaces(RefTypeId refTypeId, ExpandBuf* pReply)
+{
+ ClassObject* clazz;
+ int i, start, count;
+
+ clazz = refTypeIdToClassObject(refTypeId);
+ assert(clazz != NULL);
+
+ if (clazz->super == NULL)
+ start = 0;
+ else
+ start = clazz->super->iftableCount;
+
+ count = clazz->iftableCount - start;
+ expandBufAdd4BE(pReply, count);
+ for (i = start; i < clazz->iftableCount; i++) {
+ ClassObject* iface = clazz->iftable[i].clazz;
+ expandBufAddRefTypeId(pReply, classObjectToRefTypeId(iface));
+ }
+}
+
+typedef struct DebugCallbackContext {
+ int numItems;
+ ExpandBuf* pReply;
+ // used by locals table
+ bool withGeneric;
+} DebugCallbackContext;
+
+static int lineTablePositionsCb(void *cnxt, u4 address, u4 lineNum)
+{
+ DebugCallbackContext *pContext = (DebugCallbackContext *)cnxt;
+
+ expandBufAdd8BE(pContext->pReply, address);
+ expandBufAdd4BE(pContext->pReply, lineNum);
+ pContext->numItems++;
+
+ return 0;
+}
+
+/*
+ * For Method.LineTable: output the line table.
+ *
+ * Note we operate in Dalvik's 16-bit units rather than bytes.
+ */
+void dvmDbgOutputLineTable(RefTypeId refTypeId, MethodId methodId,
+ ExpandBuf* pReply)
+{
+ Method* method;
+ u8 start, end;
+ DebugCallbackContext context;
+
+ memset (&context, 0, sizeof(DebugCallbackContext));
+
+ method = methodIdToMethod(refTypeId, methodId);
+ if (dvmIsNativeMethod(method)) {
+ start = (u8) -1;
+ end = (u8) -1;
+ } else {
+ start = 0;
+ end = dvmGetMethodInsnsSize(method);
+ }
+
+ expandBufAdd8BE(pReply, start);
+ expandBufAdd8BE(pReply, end);
+
+ // Add numLines later
+ size_t numLinesOffset = expandBufGetLength(pReply);
+ expandBufAdd4BE(pReply, 0);
+
+ context.pReply = pReply;
+
+ dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile,
+ dvmGetMethodCode(method),
+ method->clazz->descriptor,
+ method->prototype.protoIdx,
+ method->accessFlags,
+ lineTablePositionsCb, NULL, &context);
+
+ set4BE(expandBufGetBuffer(pReply) + numLinesOffset, context.numItems);
+}
+
+/*
+ * Eclipse appears to expect that the "this" reference is in slot zero.
+ * If it's not, the "variables" display will show two copies of "this",
+ * possibly because it gets "this" from SF.ThisObject and then displays
+ * all locals with nonzero slot numbers.
+ *
+ * So, we remap the item in slot 0 to 1000, and remap "this" to zero. On
+ * SF.GetValues / SF.SetValues we map them back.
+ */
+static int tweakSlot(int slot, const char* name)
+{
+ int newSlot = slot;
+
+ if (strcmp(name, "this") == 0) // only remap "this" ptr
+ newSlot = 0;
+ else if (slot == 0) // always remap slot 0
+ newSlot = kSlot0Sub;
+
+ LOGV("untweak: %d to %d\n", slot, newSlot);
+ return newSlot;
+}
+
+/*
+ * Reverse Eclipse hack.
+ */
+static int untweakSlot(int slot, const void* framePtr)
+{
+ int newSlot = slot;
+
+ if (slot == kSlot0Sub) {
+ newSlot = 0;
+ } else if (slot == 0) {
+ const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
+ const Method* method = saveArea->method;
+ newSlot = method->registersSize - method->insSize;
+ }
+
+ LOGV("untweak: %d to %d\n", slot, newSlot);
+ return newSlot;
+}
+
+static void variableTableCb (void *cnxt, u2 reg, u4 startAddress,
+ u4 endAddress, const char *name, const char *descriptor,
+ const char *signature)
+{
+ DebugCallbackContext *pContext = (DebugCallbackContext *)cnxt;
+
+ reg = (u2) tweakSlot(reg, name);
+
+ LOGV(" %2d: %d(%d) '%s' '%s' slot=%d\n",
+ pContext->numItems, startAddress, endAddress - startAddress,
+ name, descriptor, reg);
+
+ expandBufAdd8BE(pContext->pReply, startAddress);
+ expandBufAddUtf8String(pContext->pReply, (const u1*)name);
+ expandBufAddUtf8String(pContext->pReply, (const u1*)descriptor);
+ if (pContext->withGeneric) {
+ expandBufAddUtf8String(pContext->pReply, (const u1*) signature);
+ }
+ expandBufAdd4BE(pContext->pReply, endAddress - startAddress);
+ expandBufAdd4BE(pContext->pReply, reg);
+
+ pContext->numItems++;
+}
+
+/*
+ * For Method.VariableTable[WithGeneric]: output information about local
+ * variables for the specified method.
+ */
+void dvmDbgOutputVariableTable(RefTypeId refTypeId, MethodId methodId,
+ bool withGeneric, ExpandBuf* pReply)
+{
+ Method* method;
+ DebugCallbackContext context;
+
+ memset (&context, 0, sizeof(DebugCallbackContext));
+
+ method = methodIdToMethod(refTypeId, methodId);
+
+ expandBufAdd4BE(pReply, method->insSize);
+
+ // Add numLocals later
+ size_t numLocalsOffset = expandBufGetLength(pReply);
+ expandBufAdd4BE(pReply, 0);
+
+ context.pReply = pReply;
+ context.withGeneric = withGeneric;
+ dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile,
+ dvmGetMethodCode(method),
+ method->clazz->descriptor,
+ method->prototype.protoIdx,
+ method->accessFlags,
+ NULL, variableTableCb, &context);
+
+ set4BE(expandBufGetBuffer(pReply) + numLocalsOffset, context.numItems);
+}
+
+/*
+ * Get the type tag for the field's type.
+ */
+int dvmDbgGetFieldTag(ObjectId objId, FieldId fieldId)
+{
+ Object* obj = objectIdToObject(objId);
+ RefTypeId classId = classObjectToRefTypeId(obj->clazz);
+ Field* field = fieldIdToField(classId, fieldId);
+
+ return dvmDbgGetSignatureTag(field->signature);
+}
+
+/*
+ * Get the type tag for the static field's type.
+ */
+int dvmDbgGetStaticFieldTag(RefTypeId refTypeId, FieldId fieldId)
+{
+ Field* field = fieldIdToField(refTypeId, fieldId);
+ return dvmDbgGetSignatureTag(field->signature);
+}
+
+/*
+ * Copy the value of a field into the specified buffer.
+ */
+void dvmDbgGetFieldValue(ObjectId objectId, FieldId fieldId, u1* buf,
+ int expectedLen)
+{
+ Object* obj = objectIdToObject(objectId);
+ RefTypeId classId = classObjectToRefTypeId(obj->clazz);
+ InstField* field = (InstField*) fieldIdToField(classId, fieldId);
+ Object* objVal;
+ u4 intVal;
+ u8 longVal;
+
+ switch (field->field.signature[0]) {
+ case JT_BOOLEAN:
+ assert(expectedLen == 1);
+ intVal = dvmGetFieldBoolean(obj, field->byteOffset);
+ set1(buf, intVal != 0);
+ break;
+ case JT_BYTE:
+ assert(expectedLen == 1);
+ intVal = dvmGetFieldInt(obj, field->byteOffset);
+ set1(buf, intVal);
+ break;
+ case JT_SHORT:
+ case JT_CHAR:
+ assert(expectedLen == 2);
+ intVal = dvmGetFieldInt(obj, field->byteOffset);
+ set2BE(buf, intVal);
+ break;
+ case JT_INT:
+ case JT_FLOAT:
+ assert(expectedLen == 4);
+ intVal = dvmGetFieldInt(obj, field->byteOffset);
+ set4BE(buf, intVal);
+ break;
+ case JT_ARRAY:
+ case JT_OBJECT:
+ assert(expectedLen == sizeof(ObjectId));
+ objVal = dvmGetFieldObject(obj, field->byteOffset);
+ dvmSetObjectId(buf, objectToObjectId(objVal));
+ break;
+ case JT_DOUBLE:
+ case JT_LONG:
+ assert(expectedLen == 8);
+ longVal = dvmGetFieldLong(obj, field->byteOffset);
+ set8BE(buf, longVal);
+ break;
+ default:
+ LOGE("ERROR: unhandled class type '%s'\n", field->field.signature);
+ assert(false);
+ break;
+ }
+}
+
+/*
+ * Set the value of the specified field.
+ */
+void dvmDbgSetFieldValue(ObjectId objectId, FieldId fieldId, u8 value,
+ int width)
+{
+ Object* obj = objectIdToObject(objectId);
+ RefTypeId classId = classObjectToRefTypeId(obj->clazz);
+ InstField* field = (InstField*) fieldIdToField(classId, fieldId);
+
+ switch (field->field.signature[0]) {
+ case JT_BOOLEAN:
+ assert(width == 1);
+ dvmSetFieldBoolean(obj, field->byteOffset, value != 0);
+ break;
+ case JT_BYTE:
+ assert(width == 1);
+ dvmSetFieldInt(obj, field->byteOffset, value);
+ break;
+ case JT_SHORT:
+ case JT_CHAR:
+ assert(width == 2);
+ dvmSetFieldInt(obj, field->byteOffset, value);
+ break;
+ case JT_INT:
+ case JT_FLOAT:
+ assert(width == 4);
+ dvmSetFieldInt(obj, field->byteOffset, value);
+ break;
+ case JT_ARRAY:
+ case JT_OBJECT:
+ assert(width == sizeof(ObjectId));
+ dvmSetFieldObject(obj, field->byteOffset, objectIdToObject(value));
+ break;
+ case JT_DOUBLE:
+ case JT_LONG:
+ assert(width == 8);
+ dvmSetFieldLong(obj, field->byteOffset, value);
+ break;
+ default:
+ LOGE("ERROR: unhandled class type '%s'\n", field->field.signature);
+ assert(false);
+ break;
+ }
+}
+
+/*
+ * Copy the value of a static field into the specified buffer.
+ */
+void dvmDbgGetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId, u1* buf,
+ int expectedLen)
+{
+ StaticField* sfield = (StaticField*) fieldIdToField(refTypeId, fieldId);
+ Object* objVal;
+ JValue value;
+
+ switch (sfield->field.signature[0]) {
+ case JT_BOOLEAN:
+ assert(expectedLen == 1);
+ set1(buf, dvmGetStaticFieldBoolean(sfield));
+ break;
+ case JT_BYTE:
+ assert(expectedLen == 1);
+ set1(buf, dvmGetStaticFieldByte(sfield));
+ break;
+ case JT_SHORT:
+ assert(expectedLen == 2);
+ set2BE(buf, dvmGetStaticFieldShort(sfield));
+ break;
+ case JT_CHAR:
+ assert(expectedLen == 2);
+ set2BE(buf, dvmGetStaticFieldChar(sfield));
+ break;
+ case JT_INT:
+ assert(expectedLen == 4);
+ set4BE(buf, dvmGetStaticFieldInt(sfield));
+ break;
+ case JT_FLOAT:
+ assert(expectedLen == 4);
+ value.f = dvmGetStaticFieldFloat(sfield);
+ set4BE(buf, value.i);
+ break;
+ case JT_ARRAY:
+ case JT_OBJECT:
+ assert(expectedLen == sizeof(ObjectId));
+ objVal = dvmGetStaticFieldObject(sfield);
+ dvmSetObjectId(buf, objectToObjectId(objVal));
+ break;
+ case JT_LONG:
+ assert(expectedLen == 8);
+ set8BE(buf, dvmGetStaticFieldLong(sfield));
+ break;
+ case JT_DOUBLE:
+ assert(expectedLen == 8);
+ value.d = dvmGetStaticFieldDouble(sfield);
+ set8BE(buf, value.j);
+ break;
+ default:
+ LOGE("ERROR: unhandled class type '%s'\n", sfield->field.signature);
+ assert(false);
+ break;
+ }
+}
+
+/*
+ * Set the value of a static field.
+ */
+void dvmDbgSetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId,
+ u8 rawValue, int width)
+{
+ StaticField* sfield = (StaticField*) fieldIdToField(refTypeId, fieldId);
+ Object* objVal;
+ JValue value;
+
+ value.j = rawValue;
+
+ switch (sfield->field.signature[0]) {
+ case JT_BOOLEAN:
+ assert(width == 1);
+ dvmSetStaticFieldBoolean(sfield, value.z);
+ break;
+ case JT_BYTE:
+ assert(width == 1);
+ dvmSetStaticFieldByte(sfield, value.b);
+ break;
+ case JT_SHORT:
+ assert(width == 2);
+ dvmSetStaticFieldShort(sfield, value.s);
+ break;
+ case JT_CHAR:
+ assert(width == 2);
+ dvmSetStaticFieldChar(sfield, value.c);
+ break;
+ case JT_INT:
+ assert(width == 4);
+ dvmSetStaticFieldInt(sfield, value.i);
+ break;
+ case JT_FLOAT:
+ assert(width == 4);
+ dvmSetStaticFieldFloat(sfield, value.f);
+ break;
+ case JT_ARRAY:
+ case JT_OBJECT:
+ assert(width == sizeof(ObjectId));
+ objVal = objectIdToObject(rawValue);
+ dvmSetStaticFieldObject(sfield, objVal);
+ break;
+ case JT_LONG:
+ assert(width == 8);
+ dvmSetStaticFieldLong(sfield, value.j);
+ break;
+ case JT_DOUBLE:
+ assert(width == 8);
+ dvmSetStaticFieldDouble(sfield, value.d);
+ break;
+ default:
+ LOGE("ERROR: unhandled class type '%s'\n", sfield->field.signature);
+ assert(false);
+ break;
+ }
+}
+
+/*
+ * Convert a string object to a UTF-8 string.
+ *
+ * Returns a newly-allocated string.
+ */
+char* dvmDbgStringToUtf8(ObjectId strId)
+{
+ StringObject* strObj = (StringObject*) objectIdToObject(strId);
+
+ return dvmCreateCstrFromString(strObj);
+}
+
+
+/*
+ * ===========================================================================
+ * Thread and ThreadGroup
+ * ===========================================================================
+ */
+
+/*
+ * Convert a thread object to a Thread ptr.
+ *
+ * This currently requires running through the list of threads and finding
+ * a match.
+ *
+ * IMPORTANT: grab gDvm.threadListLock before calling here.
+ */
+static Thread* threadObjToThread(Object* threadObj)
+{
+ Thread* thread;
+
+ for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+ if (thread->threadObj == threadObj)
+ break;
+ }
+
+ return thread;
+}
+
+/*
+ * Get the status and suspend state of a thread.
+ */
+bool dvmDbgGetThreadStatus(ObjectId threadId, u4* pThreadStatus,
+ u4* pSuspendStatus)
+{
+ Object* threadObj;
+ Thread* thread;
+ bool result = false;
+
+ threadObj = objectIdToObject(threadId);
+ assert(threadObj != NULL);
+
+ /* lock the thread list, so the thread doesn't vanish while we work */
+ dvmLockThreadList(NULL);
+
+ thread = threadObjToThread(threadObj);
+ if (thread == NULL)
+ goto bail;
+
+ switch (thread->status) {
+ case THREAD_ZOMBIE: *pThreadStatus = TS_ZOMBIE; break;
+ case THREAD_RUNNING: *pThreadStatus = TS_RUNNING; break;
+ case THREAD_TIMED_WAIT: *pThreadStatus = TS_SLEEPING; break;
+ case THREAD_MONITOR: *pThreadStatus = TS_MONITOR; break;
+ case THREAD_WAIT: *pThreadStatus = TS_WAIT; break;
+ case THREAD_INITIALIZING: *pThreadStatus = TS_ZOMBIE; break;
+ case THREAD_STARTING: *pThreadStatus = TS_ZOMBIE; break;
+ case THREAD_NATIVE: *pThreadStatus = TS_RUNNING; break;
+ case THREAD_VMWAIT: *pThreadStatus = TS_WAIT; break;
+ case THREAD_SUSPENDED: *pThreadStatus = TS_RUNNING; break;
+ default:
+ assert(false);
+ *pThreadStatus = THREAD_ZOMBIE;
+ break;
+ }
+
+ if (dvmIsSuspended(thread))
+ *pSuspendStatus = SUSPEND_STATUS_SUSPENDED;
+ else
+ *pSuspendStatus = 0;
+
+ result = true;
+
+bail:
+ dvmUnlockThreadList();
+ return result;
+}
+
+/*
+ * Get the thread's suspend count.
+ */
+u4 dvmDbgGetThreadSuspendCount(ObjectId threadId)
+{
+ Object* threadObj;
+ Thread* thread;
+ u4 result = 0;
+
+ threadObj = objectIdToObject(threadId);
+ assert(threadObj != NULL);
+
+ /* lock the thread list, so the thread doesn't vanish while we work */
+ dvmLockThreadList(NULL);
+
+ thread = threadObjToThread(threadObj);
+ if (thread == NULL)
+ goto bail;
+
+ result = thread->suspendCount;
+
+bail:
+ dvmUnlockThreadList();
+ return result;
+}
+
+/*
+ * Determine whether or not a thread exists in the VM's thread list.
+ *
+ * Returns "true" if the thread exists.
+ */
+bool dvmDbgThreadExists(ObjectId threadId)
+{
+ Object* threadObj;
+ Thread* thread;
+ bool result;
+
+ threadObj = objectIdToObject(threadId);
+ assert(threadObj != NULL);
+
+ /* lock the thread list, so the thread doesn't vanish while we work */
+ dvmLockThreadList(NULL);
+
+ thread = threadObjToThread(threadObj);
+ if (thread == NULL)
+ result = false;
+ else
+ result = true;
+
+ dvmUnlockThreadList();
+ return result;
+}
+
+/*
+ * Determine whether or not a thread is suspended.
+ *
+ * Returns "false" if the thread is running or doesn't exist.
+ */
+bool dvmDbgIsSuspended(ObjectId threadId)
+{
+ Object* threadObj;
+ Thread* thread;
+ bool result = false;
+
+ threadObj = objectIdToObject(threadId);
+ assert(threadObj != NULL);
+
+ /* lock the thread list, so the thread doesn't vanish while we work */
+ dvmLockThreadList(NULL);
+
+ thread = threadObjToThread(threadObj);
+ if (thread == NULL)
+ goto bail;
+
+ result = dvmIsSuspended(thread);
+
+bail:
+ dvmUnlockThreadList();
+ return result;
+}
+
+#if 0
+/*
+ * Wait until a thread suspends.
+ *
+ * We stray from the usual pattern here, and release the thread list lock
+ * before we use the Thread. This is necessary and should be safe in this
+ * circumstance; see comments in dvmWaitForSuspend().
+ */
+void dvmDbgWaitForSuspend(ObjectId threadId)
+{
+ Object* threadObj;
+ Thread* thread;
+
+ threadObj = objectIdToObject(threadId);
+ assert(threadObj != NULL);
+
+ dvmLockThreadList(NULL);
+ thread = threadObjToThread(threadObj);
+ dvmUnlockThreadList();
+
+ if (thread != NULL)
+ dvmWaitForSuspend(thread);
+}
+#endif
+
+
+/*
+ * Return the ObjectId for the "system" thread group.
+ */
+ObjectId dvmDbgGetSystemThreadGroupId(void)
+{
+ Object* groupObj = dvmGetSystemThreadGroup();
+ return objectToObjectId(groupObj);
+}
+
+/*
+ * Return the ObjectId for the "system" thread group.
+ */
+ObjectId dvmDbgGetMainThreadGroupId(void)
+{
+ Object* groupObj = dvmGetMainThreadGroup();
+ return objectToObjectId(groupObj);
+}
+
+/*
+ * Get the name of a thread.
+ *
+ * Returns a newly-allocated string.
+ */
+char* dvmDbgGetThreadName(ObjectId threadId)
+{
+ Object* threadObj;
+ StringObject* nameStr;
+ char* str;
+ char* result;
+
+ threadObj = objectIdToObject(threadId);
+ assert(threadObj != NULL);
+
+ nameStr = (StringObject*) dvmGetFieldObject(threadObj,
+ gDvm.offJavaLangThread_name);
+ str = dvmCreateCstrFromString(nameStr);
+ result = (char*) malloc(strlen(str) + 20);
+
+ /* lock the thread list, so the thread doesn't vanish while we work */
+ dvmLockThreadList(NULL);
+ Thread* thread = threadObjToThread(threadObj);
+ if (thread != NULL)
+ sprintf(result, "<%d> %s", thread->threadId, str);
+ else
+ sprintf(result, "%s", str);
+ dvmUnlockThreadList();
+
+ free(str);
+ return result;
+}
+
+/*
+ * Get a thread's group.
+ */
+ObjectId dvmDbgGetThreadGroup(ObjectId threadId)
+{
+ Object* threadObj;
+ Object* group;
+
+ threadObj = objectIdToObject(threadId);
+ assert(threadObj != NULL);
+
+ group = dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_group);
+ return objectToObjectId(group);
+}
+
+
+/*
+ * Get the name of a thread group.
+ *
+ * Returns a newly-allocated string.
+ */
+char* dvmDbgGetThreadGroupName(ObjectId threadGroupId)
+{
+ Object* threadGroup;
+ InstField* nameField;
+ StringObject* nameStr;
+
+ threadGroup = objectIdToObject(threadGroupId);
+ assert(threadGroup != NULL);
+
+ nameField = dvmFindInstanceField(gDvm.classJavaLangThreadGroup,
+ "name", "Ljava/lang/String;");
+ if (nameField == NULL) {
+ LOGE("unable to find name field in ThreadGroup\n");
+ return NULL;
+ }
+
+ nameStr = (StringObject*) dvmGetFieldObject(threadGroup,
+ nameField->byteOffset);
+ return dvmCreateCstrFromString(nameStr);
+}
+
+/*
+ * Get the parent of a thread group.
+ *
+ * Returns a newly-allocated string.
+ */
+ObjectId dvmDbgGetThreadGroupParent(ObjectId threadGroupId)
+{
+ Object* threadGroup;
+ InstField* parentField;
+ Object* parent;
+
+ threadGroup = objectIdToObject(threadGroupId);
+ assert(threadGroup != NULL);
+
+ parentField = dvmFindInstanceField(gDvm.classJavaLangThreadGroup,
+ "parent", "Ljava/lang/ThreadGroup;");
+ if (parentField == NULL) {
+ LOGE("unable to find parent field in ThreadGroup\n");
+ parent = NULL;
+ } else {
+ parent = dvmGetFieldObject(threadGroup, parentField->byteOffset);
+ }
+ return objectToObjectId(parent);
+}
+
+/*
+ * Get the list of threads in the thread group.
+ *
+ * We do this by running through the full list of threads and returning
+ * the ones that have the ThreadGroup object as their owner.
+ *
+ * If threadGroupId is set to "kAllThreads", we ignore the group field and
+ * return all threads.
+ *
+ * The caller must free "*ppThreadIds".
+ */
+void dvmDbgGetThreadGroupThreads(ObjectId threadGroupId,
+ ObjectId** ppThreadIds, u4* pThreadCount)
+{
+ Object* targetThreadGroup = NULL;
+ InstField* groupField = NULL;
+ Thread* thread;
+ int count;
+
+ if (threadGroupId != THREAD_GROUP_ALL) {
+ targetThreadGroup = objectIdToObject(threadGroupId);
+ assert(targetThreadGroup != NULL);
+ }
+
+ groupField = dvmFindInstanceField(gDvm.classJavaLangThread,
+ "group", "Ljava/lang/ThreadGroup;");
+
+ dvmLockThreadList(NULL);
+
+ thread = gDvm.threadList;
+ count = 0;
+ for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+ Object* group;
+
+ /* Skip over the JDWP support thread. Some debuggers
+ * get bent out of shape when they can't suspend and
+ * query all threads, so it's easier if we just don't
+ * tell them about us.
+ */
+ if (thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
+ continue;
+
+ /* This thread is currently being created, and isn't ready
+ * to be seen by the debugger yet.
+ */
+ if (thread->threadObj == NULL)
+ continue;
+
+ group = dvmGetFieldObject(thread->threadObj, groupField->byteOffset);
+ if (threadGroupId == THREAD_GROUP_ALL || group == targetThreadGroup)
+ count++;
+ }
+
+ *pThreadCount = count;
+
+ if (count == 0) {
+ *ppThreadIds = NULL;
+ } else {
+ ObjectId* ptr;
+ ptr = *ppThreadIds = (ObjectId*) malloc(sizeof(ObjectId) * count);
+
+ for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+ Object* group;
+
+ /* Skip over the JDWP support thread. Some debuggers
+ * get bent out of shape when they can't suspend and
+ * query all threads, so it's easier if we just don't
+ * tell them about us.
+ */
+ if (thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
+ continue;
+
+ /* This thread is currently being created, and isn't ready
+ * to be seen by the debugger yet.
+ */
+ if (thread->threadObj == NULL)
+ continue;
+
+ group = dvmGetFieldObject(thread->threadObj,groupField->byteOffset);
+ if (threadGroupId == THREAD_GROUP_ALL || group == targetThreadGroup)
+ {
+ *ptr++ = objectToObjectId(thread->threadObj);
+ count--;
+ }
+ }
+
+ assert(count == 0);
+ }
+
+ dvmUnlockThreadList();
+}
+
+/*
+ * Get all threads.
+ *
+ * The caller must free "*ppThreadIds".
+ */
+void dvmDbgGetAllThreads(ObjectId** ppThreadIds, u4* pThreadCount)
+{
+ dvmDbgGetThreadGroupThreads(THREAD_GROUP_ALL, ppThreadIds, pThreadCount);
+}
+
+
+/*
+ * Count up the #of frames on the thread's stack.
+ *
+ * Returns -1 on failure;
+ */
+int dvmDbgGetThreadFrameCount(ObjectId threadId)
+{
+ Object* threadObj;
+ Thread* thread;
+ void* framePtr;
+ u4 count = 0;
+
+ threadObj = objectIdToObject(threadId);
+
+ dvmLockThreadList(NULL);
+
+ thread = threadObjToThread(threadObj);
+ if (thread == NULL)
+ goto bail;
+
+ framePtr = thread->curFrame;
+ while (framePtr != NULL) {
+ if (!dvmIsBreakFrame(framePtr))
+ count++;
+
+ framePtr = SAVEAREA_FROM_FP(framePtr)->prevFrame;
+ }
+
+bail:
+ dvmUnlockThreadList();
+ return count;
+}
+
+/*
+ * Get info for frame N from the specified thread's stack.
+ */
+bool dvmDbgGetThreadFrame(ObjectId threadId, int num, FrameId* pFrameId,
+ JdwpLocation* pLoc)
+{
+ Object* threadObj;
+ Thread* thread;
+ void* framePtr;
+ int count;
+
+ threadObj = objectIdToObject(threadId);
+
+ dvmLockThreadList(NULL);
+
+ thread = threadObjToThread(threadObj);
+ if (thread == NULL)
+ goto bail;
+
+ framePtr = thread->curFrame;
+ count = 0;
+ while (framePtr != NULL) {
+ const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
+ const Method* method = saveArea->method;
+
+ if (!dvmIsBreakFrame(framePtr)) {
+ if (count == num) {
+ *pFrameId = frameToFrameId(framePtr);
+ if (dvmIsInterfaceClass(method->clazz))
+ pLoc->typeTag = TT_INTERFACE;
+ else
+ pLoc->typeTag = TT_CLASS;
+ pLoc->classId = classObjectToRefTypeId(method->clazz);
+ pLoc->methodId = methodToMethodId(method);
+ if (dvmIsNativeMethod(method))
+ pLoc->idx = (u8)-1;
+ else
+ pLoc->idx = saveArea->xtra.currentPc - method->insns;
+ dvmUnlockThreadList();
+ return true;
+ }
+
+ count++;
+ }
+
+ framePtr = saveArea->prevFrame;
+ }
+
+bail:
+ dvmUnlockThreadList();
+ return false;
+}
+
+/*
+ * Get the ThreadId for the current thread.
+ */
+ObjectId dvmDbgGetThreadSelfId(void)
+{
+ Thread* self = dvmThreadSelf();
+ return objectToObjectId(self->threadObj);
+}
+
+/*
+ * Suspend the VM.
+ */
+void dvmDbgSuspendVM(bool isEvent)
+{
+ dvmSuspendAllThreads(isEvent ? SUSPEND_FOR_DEBUG_EVENT : SUSPEND_FOR_DEBUG);
+}
+
+/*
+ * Resume the VM.
+ */
+void dvmDbgResumeVM()
+{
+ dvmResumeAllThreads(SUSPEND_FOR_DEBUG);
+}
+
+/*
+ * Suspend one thread (not ourselves).
+ */
+void dvmDbgSuspendThread(ObjectId threadId)
+{
+ Object* threadObj = objectIdToObject(threadId);
+ Thread* thread;
+
+ dvmLockThreadList(NULL);
+
+ thread = threadObjToThread(threadObj);
+ if (thread == NULL) {
+ /* can happen if our ThreadDeath notify crosses in the mail */
+ LOGW("WARNING: threadid=%llx obj=%p no match\n", threadId, threadObj);
+ } else {
+ dvmSuspendThread(thread);
+ }
+
+ dvmUnlockThreadList();
+}
+
+/*
+ * Resume one thread (not ourselves).
+ */
+void dvmDbgResumeThread(ObjectId threadId)
+{
+ Object* threadObj = objectIdToObject(threadId);
+ Thread* thread;
+
+ dvmLockThreadList(NULL);
+
+ thread = threadObjToThread(threadObj);
+ if (thread == NULL) {
+ LOGW("WARNING: threadid=%llx obj=%p no match\n", threadId, threadObj);
+ } else {
+ dvmResumeThread(thread);
+ }
+
+ dvmUnlockThreadList();
+}
+
+/*
+ * Suspend ourselves after sending an event to the debugger.
+ */
+void dvmDbgSuspendSelf(void)
+{
+ dvmSuspendSelf(true);
+}
+
+/*
+ * Get the "this" object for the specified frame.
+ */
+static Object* getThisObject(const u4* framePtr)
+{
+ const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
+ const Method* method = saveArea->method;
+ int argOffset = method->registersSize - method->insSize;
+ Object* thisObj;
+
+ if (method == NULL) {
+ /* this is a "break" frame? */
+ assert(false);
+ return NULL;
+ }
+
+ LOGVV(" Pulling this object for frame at %p\n", framePtr);
+ LOGVV(" Method='%s' native=%d static=%d this=%p\n",
+ method->name, dvmIsNativeMethod(method),
+ dvmIsStaticMethod(method), (Object*) framePtr[argOffset]);
+
+ /*
+ * No "this" pointer for statics. No args on the interp stack for
+ * native methods invoked directly from the VM.
+ */
+ if (dvmIsNativeMethod(method) || dvmIsStaticMethod(method))
+ thisObj = NULL;
+ else
+ thisObj = (Object*) framePtr[argOffset];
+
+ if (thisObj != NULL && !dvmIsValidObject(thisObj)) {
+ LOGW("Debugger: invalid 'this' pointer %p in %s.%s; returning NULL\n",
+ framePtr, method->clazz->descriptor, method->name);
+ thisObj = NULL;
+ }
+
+ return thisObj;
+}
+
+/*
+ * Return the "this" object for the specified frame. The thread must be
+ * suspended.
+ */
+bool dvmDbgGetThisObject(ObjectId threadId, FrameId frameId, ObjectId* pThisId)
+{
+ const u4* framePtr = frameIdToFrame(frameId);
+ Object* thisObj;
+
+ UNUSED_PARAMETER(threadId);
+
+ thisObj = getThisObject(framePtr);
+
+ *pThisId = objectToObjectId(thisObj);
+ return true;
+}
+
+/*
+ * Copy the value of a method argument or local variable into the
+ * specified buffer. The value will be preceeded with the tag.
+ */
+void dvmDbgGetLocalValue(ObjectId threadId, FrameId frameId, int slot,
+ u1 tag, u1* buf, int expectedLen)
+{
+ const u4* framePtr = frameIdToFrame(frameId);
+ Object* objVal;
+ u4 intVal;
+ u8 longVal;
+
+ UNUSED_PARAMETER(threadId);
+
+ slot = untweakSlot(slot, framePtr); // Eclipse workaround
+
+ switch (tag) {
+ case JT_BOOLEAN:
+ assert(expectedLen == 1);
+ intVal = framePtr[slot];
+ set1(buf+1, intVal != 0);
+ break;
+ case JT_BYTE:
+ assert(expectedLen == 1);
+ intVal = framePtr[slot];
+ set1(buf+1, intVal);
+ break;
+ case JT_SHORT:
+ case JT_CHAR:
+ assert(expectedLen == 2);
+ intVal = framePtr[slot];
+ set2BE(buf+1, intVal);
+ break;
+ case JT_INT:
+ case JT_FLOAT:
+ assert(expectedLen == 4);
+ intVal = framePtr[slot];
+ set4BE(buf+1, intVal);
+ break;
+ case JT_ARRAY:
+ assert(expectedLen == 8);
+ {
+ /* convert to "ObjectId" */
+ objVal = (Object*)framePtr[slot];
+ if (objVal != NULL && !dvmIsValidObject(objVal)) {
+ LOGW("JDWP: slot %d expected to hold array, %p invalid\n",
+ slot, objVal);
+ dvmAbort(); // DEBUG: make it obvious
+ objVal = NULL;
+ tag = JT_OBJECT; // JT_ARRAY not expected for NULL ref
+ }
+ dvmSetObjectId(buf+1, objectToObjectId(objVal));
+ }
+ break;
+ case JT_OBJECT:
+ assert(expectedLen == 8);
+ {
+ /* convert to "ObjectId" */
+ objVal = (Object*)framePtr[slot];
+ //char* name;
+
+ if (objVal != NULL) {
+ if (!dvmIsValidObject(objVal)) {
+ LOGW("JDWP: slot %d expected to hold object, %p invalid\n",
+ slot, objVal);
+ dvmAbort(); // DEBUG: make it obvious
+ objVal = NULL;
+ }
+ //name = generateJNISignature(objVal->clazz);
+ tag = resultTagFromObject(objVal);
+ //free(name);
+ } else {
+ tag = JT_OBJECT;
+ }
+ dvmSetObjectId(buf+1, objectToObjectId(objVal));
+ }
+ break;
+ case JT_DOUBLE:
+ case JT_LONG:
+ assert(expectedLen == 8);
+ longVal = *(u8*)(&framePtr[slot]);
+ set8BE(buf+1, longVal);
+ break;
+ default:
+ LOGE("ERROR: unhandled tag '%c'\n", tag);
+ assert(false);
+ break;
+ }
+
+ set1(buf, tag);
+}
+
+/*
+ * Copy a new value into an argument or local variable.
+ */
+void dvmDbgSetLocalValue(ObjectId threadId, FrameId frameId, int slot, u1 tag,
+ u8 value, int width)
+{
+ u4* framePtr = frameIdToFrame(frameId);
+
+ UNUSED_PARAMETER(threadId);
+
+ slot = untweakSlot(slot, framePtr); // Eclipse workaround
+
+ switch (tag) {
+ case JT_BOOLEAN:
+ assert(width == 1);
+ framePtr[slot] = (u4)value;
+ break;
+ case JT_BYTE:
+ assert(width == 1);
+ framePtr[slot] = (u4)value;
+ break;
+ case JT_SHORT:
+ case JT_CHAR:
+ assert(width == 2);
+ framePtr[slot] = (u4)value;
+ break;
+ case JT_INT:
+ case JT_FLOAT:
+ assert(width == 4);
+ framePtr[slot] = (u4)value;
+ break;
+ case JT_STRING:
+ /* The debugger calls VirtualMachine.CreateString to create a new
+ * string, then uses this to set the object reference, when you
+ * edit a String object */
+ case JT_ARRAY:
+ case JT_OBJECT:
+ assert(width == sizeof(ObjectId));
+ framePtr[slot] = (u4) objectIdToObject(value);
+ break;
+ case JT_DOUBLE:
+ case JT_LONG:
+ assert(width == 8);
+ *(u8*)(&framePtr[slot]) = value;
+ break;
+ case JT_VOID:
+ case JT_CLASS_OBJECT:
+ case JT_THREAD:
+ case JT_THREAD_GROUP:
+ case JT_CLASS_LOADER:
+ default:
+ LOGE("ERROR: unhandled tag '%c'\n", tag);
+ assert(false);
+ break;
+ }
+}
+
+
+/*
+ * ===========================================================================
+ * Debugger notification
+ * ===========================================================================
+ */
+
+/*
+ * Tell JDWP that a breakpoint address has been reached.
+ *
+ * "pcOffset" will be -1 for native methods.
+ * "thisPtr" will be NULL for static methods.
+ */
+void dvmDbgPostLocationEvent(const Method* method, int pcOffset,
+ Object* thisPtr, int eventFlags)
+{
+ JdwpLocation loc;
+
+ if (dvmIsInterfaceClass(method->clazz))
+ loc.typeTag = TT_INTERFACE;
+ else
+ loc.typeTag = TT_CLASS;
+ loc.classId = classObjectToRefTypeId(method->clazz);
+ loc.methodId = methodToMethodId(method);
+ loc.idx = pcOffset;
+
+ /*
+ * Note we use "NoReg" so we don't keep track of references that are
+ * never actually sent to the debugger. The "thisPtr" is only used to
+ * compare against registered events.
+ */
+
+ if (dvmJdwpPostLocationEvent(gDvm.jdwpState, &loc,
+ objectToObjectIdNoReg(thisPtr), eventFlags))
+ {
+ classObjectToRefTypeId(method->clazz);
+ objectToObjectId(thisPtr);
+ }
+}
+
+/*
+ * Tell JDWP that an exception has occurred.
+ */
+void dvmDbgPostException(void* throwFp, int throwRelPc, void* catchFp,
+ int catchRelPc, Object* exception)
+{
+ JdwpLocation throwLoc, catchLoc;
+ const Method* throwMeth;
+ const Method* catchMeth;
+
+ throwMeth = SAVEAREA_FROM_FP(throwFp)->method;
+ if (dvmIsInterfaceClass(throwMeth->clazz))
+ throwLoc.typeTag = TT_INTERFACE;
+ else
+ throwLoc.typeTag = TT_CLASS;
+ throwLoc.classId = classObjectToRefTypeId(throwMeth->clazz);
+ throwLoc.methodId = methodToMethodId(throwMeth);
+ throwLoc.idx = throwRelPc;
+
+ if (catchRelPc < 0) {
+ memset(&catchLoc, 0, sizeof(catchLoc));
+ } else {
+ catchMeth = SAVEAREA_FROM_FP(catchFp)->method;
+ if (dvmIsInterfaceClass(catchMeth->clazz))
+ catchLoc.typeTag = TT_INTERFACE;
+ else
+ catchLoc.typeTag = TT_CLASS;
+ catchLoc.classId = classObjectToRefTypeId(catchMeth->clazz);
+ catchLoc.methodId = methodToMethodId(catchMeth);
+ catchLoc.idx = catchRelPc;
+ }
+
+ /* need this for InstanceOnly filters */
+ Object* thisObj = getThisObject(throwFp);
+
+ /*
+ * Hand the event to the JDWP exception handler. Note we're using the
+ * "NoReg" objectID on the exception, which is not strictly correct --
+ * the exception object WILL be passed up to the debugger if the
+ * debugger is interested in the event. We do this because the current
+ * implementation of the debugger object registry never throws anything
+ * away, and some people were experiencing a fatal build up of exception
+ * objects when dealing with certain libraries.
+ */
+ dvmJdwpPostException(gDvm.jdwpState, &throwLoc,
+ objectToObjectIdNoReg(exception),
+ classObjectToRefTypeId(exception->clazz), &catchLoc,
+ objectToObjectId(thisObj));
+}
+
+/*
+ * Tell JDWP and/or DDMS that a thread has started.
+ */
+void dvmDbgPostThreadStart(Thread* thread)
+{
+ if (gDvm.debuggerActive) {
+ dvmJdwpPostThreadChange(gDvm.jdwpState,
+ objectToObjectId(thread->threadObj), true);
+ }
+ if (gDvm.ddmThreadNotification)
+ dvmDdmSendThreadNotification(thread, true);
+}
+
+/*
+ * Tell JDWP and/or DDMS that a thread has gone away.
+ */
+void dvmDbgPostThreadDeath(Thread* thread)
+{
+ if (gDvm.debuggerActive) {
+ dvmJdwpPostThreadChange(gDvm.jdwpState,
+ objectToObjectId(thread->threadObj), false);
+ }
+ if (gDvm.ddmThreadNotification)
+ dvmDdmSendThreadNotification(thread, false);
+}
+
+/*
+ * Tell JDWP that a new class has been prepared.
+ */
+void dvmDbgPostClassPrepare(ClassObject* clazz)
+{
+ int tag;
+ char* signature;
+
+ if (dvmIsInterfaceClass(clazz))
+ tag = TT_INTERFACE;
+ else
+ tag = TT_CLASS;
+
+ // TODO - we currently always send both "verified" and "prepared" since
+ // debuggers seem to like that. There might be some advantage to honesty,
+ // since the class may not yet be verified.
+ signature = generateJNISignature(clazz);
+ dvmJdwpPostClassPrepare(gDvm.jdwpState, tag, classObjectToRefTypeId(clazz),
+ signature, CS_VERIFIED | CS_PREPARED);
+ free(signature);
+}
+
+/*
+ * The JDWP event mechanism has registered an event with a LocationOnly
+ * mod. Tell the interpreter to call us if we hit the specified
+ * address.
+ */
+bool dvmDbgWatchLocation(const JdwpLocation* pLoc)
+{
+ Method* method = methodIdToMethod(pLoc->classId, pLoc->methodId);
+ assert(!dvmIsNativeMethod(method));
+ dvmAddBreakAddr(method, pLoc->idx);
+ return true; /* assume success */
+}
+
+/*
+ * An event with a LocationOnly mod has been removed.
+ */
+void dvmDbgUnwatchLocation(const JdwpLocation* pLoc)
+{
+ Method* method = methodIdToMethod(pLoc->classId, pLoc->methodId);
+ assert(!dvmIsNativeMethod(method));
+ dvmClearBreakAddr(method, pLoc->idx);
+}
+
+/*
+ * The JDWP event mechanism has registered a single-step event. Tell
+ * the interpreter about it.
+ */
+bool dvmDbgConfigureStep(ObjectId threadId, enum JdwpStepSize size,
+ enum JdwpStepDepth depth)
+{
+ Object* threadObj;
+ Thread* thread;
+ bool result = false;
+
+ threadObj = objectIdToObject(threadId);
+ assert(threadObj != NULL);
+
+ /*
+ * Get a pointer to the Thread struct for this ID. The pointer will
+ * be used strictly for comparisons against the current thread pointer
+ * after the setup is complete, so we can safely release the lock.
+ */
+ dvmLockThreadList(NULL);
+ thread = threadObjToThread(threadObj);
+
+ if (thread == NULL) {
+ LOGE("Thread for single-step not found\n");
+ goto bail;
+ }
+ if (!dvmIsSuspended(thread)) {
+ LOGE("Thread for single-step not suspended\n");
+ assert(!"non-susp step"); // I want to know if this can happen
+ goto bail;
+ }
+
+ assert(dvmIsSuspended(thread));
+ if (!dvmAddSingleStep(thread, size, depth))
+ goto bail;
+
+ result = true;
+
+bail:
+ dvmUnlockThreadList();
+ return result;
+}
+
+/*
+ * A single-step event has been removed.
+ */
+void dvmDbgUnconfigureStep(ObjectId threadId)
+{
+ UNUSED_PARAMETER(threadId);
+
+ /* right now it's global, so don't need to find Thread */
+ dvmClearSingleStep(NULL);
+}
+
+/*
+ * Invoke a method in a thread that has been stopped on a breakpoint or
+ * other debugger event. (This function is called from the JDWP thread.)
+ *
+ * Note that access control is not enforced, per spec.
+ */
+JdwpError dvmDbgInvokeMethod(ObjectId threadId, ObjectId objectId,
+ RefTypeId classId, MethodId methodId, u4 numArgs, ObjectId* argArray,
+ u4 options, u1* pResultTag, u8* pResultValue, ObjectId* pExceptObj)
+{
+ Object* threadObj = objectIdToObject(threadId);
+ Thread* targetThread;
+ JdwpError err = ERR_NONE;
+
+ dvmLockThreadList(NULL);
+
+ targetThread = threadObjToThread(threadObj);
+ if (targetThread == NULL) {
+ err = ERR_INVALID_THREAD; /* thread does not exist */
+ dvmUnlockThreadList();
+ goto bail;
+ }
+ if (!targetThread->invokeReq.ready) {
+ err = ERR_INVALID_THREAD; /* thread not stopped by event */
+ dvmUnlockThreadList();
+ goto bail;
+ }
+
+ /*
+ * We currently have a bug where we don't successfully resume the
+ * target thread if the suspend count is too deep. We're expected to
+ * require one "resume" for each "suspend", but when asked to execute
+ * a method we have to resume fully and then re-suspend it back to the
+ * same level. (The easiest way to cause this is to type "suspend"
+ * multiple times in jdb.)
+ *
+ * It's unclear what this means when the event specifies "resume all"
+ * and some threads are suspended more deeply than others. This is
+ * a rare problem, so for now we just prevent it from hanging forever
+ * by rejecting the method invocation request. Without this, we will
+ * be stuck waiting on a suspended thread.
+ */
+ if (targetThread->suspendCount > 1) {
+ LOGW("threadid=%d: suspend count on threadid=%d is %d, too deep "
+ "for method exec\n",
+ dvmThreadSelf()->threadId, targetThread->threadId,
+ targetThread->suspendCount);
+ err = ERR_THREAD_SUSPENDED; /* probably not expected here */
+ dvmUnlockThreadList();
+ goto bail;
+ }
+
+ /*
+ * TODO: ought to screen the various IDs, and verify that the argument
+ * list is valid.
+ */
+
+ targetThread->invokeReq.obj = objectIdToObject(objectId);
+ targetThread->invokeReq.thread = threadObj;
+ targetThread->invokeReq.clazz = refTypeIdToClassObject(classId);
+ targetThread->invokeReq.method = methodIdToMethod(classId, methodId);
+ targetThread->invokeReq.numArgs = numArgs;
+ targetThread->invokeReq.argArray = argArray;
+ targetThread->invokeReq.options = options;
+ targetThread->invokeReq.invokeNeeded = true;
+
+ /*
+ * This is a bit risky -- if the thread goes away we're sitting high
+ * and dry -- but we must release this before the dvmResumeAllThreads
+ * call, and it's unwise to hold it during dvmWaitForSuspend.
+ */
+ dvmUnlockThreadList();
+
+ /*
+ * We change our (JDWP thread) status, which should be THREAD_RUNNING,
+ * so the VM can suspend for a GC if the invoke request causes us to
+ * run out of memory. It's also a good idea to change it before locking
+ * the invokeReq mutex, although that should never be held for long.
+ */
+ Thread* self = dvmThreadSelf();
+ int oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
+
+ LOGV(" Transferring control to event thread\n");
+ dvmLockMutex(&targetThread->invokeReq.lock);
+
+ if ((options & INVOKE_SINGLE_THREADED) == 0) {
+ LOGV(" Resuming all threads\n");
+ dvmResumeAllThreads(SUSPEND_FOR_DEBUG_EVENT);
+ } else {
+ LOGV(" Resuming event thread only\n");
+ dvmResumeThread(targetThread);
+ }
+
+ /*
+ * Wait for the request to finish executing.
+ */
+ while (targetThread->invokeReq.invokeNeeded) {
+ pthread_cond_wait(&targetThread->invokeReq.cv,
+ &targetThread->invokeReq.lock);
+ }
+ dvmUnlockMutex(&targetThread->invokeReq.lock);
+ LOGV(" Control has returned from event thread\n");
+
+ /* wait for thread to re-suspend itself */
+ dvmWaitForSuspend(targetThread);
+
+ /*
+ * Done waiting, switch back to RUNNING.
+ */
+ dvmChangeStatus(self, oldStatus);
+
+ /*
+ * Suspend the threads. We waited for the target thread to suspend
+ * itself, so all we need to do is suspend the others.
+ *
+ * The suspendAllThreads() call will double-suspend the event thread,
+ * so we want to resume the target thread once to keep the books straight.
+ */
+ if ((options & INVOKE_SINGLE_THREADED) == 0) {
+ LOGV(" Suspending all threads\n");
+ dvmSuspendAllThreads(SUSPEND_FOR_DEBUG_EVENT);
+ LOGV(" Resuming event thread to balance the count\n");
+ dvmResumeThread(targetThread);
+ }
+
+ /*
+ * Set up the result.
+ */
+ *pResultTag = targetThread->invokeReq.resultTag;
+ if (isTagPrimitive(targetThread->invokeReq.resultTag))
+ *pResultValue = targetThread->invokeReq.resultValue.j;
+ else
+ *pResultValue = objectToObjectId(targetThread->invokeReq.resultValue.l);
+ *pExceptObj = targetThread->invokeReq.exceptObj;
+ err = targetThread->invokeReq.err;
+
+bail:
+ return err;
+}
+
+/*
+ * Determine the tag type for the return value for this method.
+ */
+static u1 resultTagFromSignature(const Method* method)
+{
+ const char* descriptor = dexProtoGetReturnType(&method->prototype);
+ return dvmDbgGetSignatureTag(descriptor);
+}
+
+/*
+ * Execute the method described by "*pReq".
+ *
+ * We're currently in VMWAIT, because we're stopped on a breakpoint. We
+ * want to switch to RUNNING while we execute.
+ */
+void dvmDbgExecuteMethod(DebugInvokeReq* pReq)
+{
+ Thread* self = dvmThreadSelf();
+ const Method* meth;
+ Object* oldExcept;
+ int oldStatus;
+
+ /*
+ * We can be called while an exception is pending in the VM. We need
+ * to preserve that across the method invocation.
+ */
+ oldExcept = dvmGetException(self);
+ dvmClearException(self);
+
+ oldStatus = dvmChangeStatus(self, THREAD_RUNNING);
+
+ /*
+ * Translate the method through the vtable, unless we're calling a
+ * direct method or the debugger wants to suppress it.
+ */
+ if ((pReq->options & INVOKE_NONVIRTUAL) != 0 || pReq->obj == NULL ||
+ dvmIsDirectMethod(pReq->method))
+ {
+ meth = pReq->method;
+ } else {
+ meth = dvmGetVirtualizedMethod(pReq->clazz, pReq->method);
+ }
+ assert(meth != NULL);
+
+ assert(sizeof(jvalue) == sizeof(u8));
+
+ IF_LOGV() {
+ char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+ LOGV("JDWP invoking method %p/%p %s.%s:%s\n",
+ pReq->method, meth, meth->clazz->descriptor, meth->name, desc);
+ free(desc);
+ }
+
+ dvmCallMethodA(self, meth, pReq->obj, false, &pReq->resultValue,
+ (jvalue*)pReq->argArray);
+ pReq->exceptObj = objectToObjectId(dvmGetException(self));
+ pReq->resultTag = resultTagFromSignature(meth);
+ if (pReq->exceptObj != 0) {
+ Object* exc = dvmGetException(self);
+ LOGD(" JDWP invocation returning with exceptObj=%p (%s)\n",
+ exc, exc->clazz->descriptor);
+ //dvmLogExceptionStackTrace();
+ dvmClearException(self);
+ /*
+ * Nothing should try to use this, but it looks like something is.
+ * Make it null to be safe.
+ */
+ pReq->resultValue.j = 0; /*0xadadadad;*/
+ } else if (pReq->resultTag == JT_OBJECT) {
+ /* if no exception thrown, examine object result more closely */
+ u1 newTag = resultTagFromObject(pReq->resultValue.l);
+ if (newTag != pReq->resultTag) {
+ LOGVV(" JDWP promoted result from %d to %d\n",
+ pReq->resultTag, newTag);
+ pReq->resultTag = newTag;
+ }
+ }
+
+ if (oldExcept != NULL)
+ dvmSetException(self, oldExcept);
+ dvmChangeStatus(self, oldStatus);
+}
+
+// for dvmAddressSetForLine
+typedef struct AddressSetContext {
+ bool lastAddressValid;
+ u4 lastAddress;
+ u4 lineNum;
+ AddressSet *pSet;
+} AddressSetContext;
+
+// for dvmAddressSetForLine
+static int addressSetCb (void *cnxt, u4 address, u4 lineNum)
+{
+ AddressSetContext *pContext = (AddressSetContext *)cnxt;
+
+ if (lineNum == pContext->lineNum) {
+ if (!pContext->lastAddressValid) {
+ // Everything from this address until the next line change is ours
+ pContext->lastAddress = address;
+ pContext->lastAddressValid = true;
+ }
+ // else, If we're already in a valid range for this lineNum,
+ // just keep going (shouldn't really happen)
+ } else if (pContext->lastAddressValid) { // and the line number is new
+ u4 i;
+ // Add everything from the last entry up until here to the set
+ for (i = pContext->lastAddress; i < address; i++) {
+ dvmAddressSetSet(pContext->pSet, i);
+ }
+
+ pContext->lastAddressValid = false;
+ }
+
+ // there may be multiple entries for a line
+ return 0;
+}
+/*
+ * Build up a set of bytecode addresses associated with a line number
+ */
+const AddressSet *dvmAddressSetForLine(const Method* method, int line)
+{
+ AddressSet *result;
+ const DexFile *pDexFile = method->clazz->pDvmDex->pDexFile;
+ u4 insnsSize = dvmGetMethodInsnsSize(method);
+ AddressSetContext context;
+
+ result = calloc(1, sizeof(AddressSet) + (insnsSize/8) + 1);
+ result->setSize = insnsSize;
+
+ memset(&context, 0, sizeof(context));
+ context.pSet = result;
+ context.lineNum = line;
+ context.lastAddressValid = false;
+
+ dexDecodeDebugInfo(pDexFile, dvmGetMethodCode(method),
+ method->clazz->descriptor,
+ method->prototype.protoIdx,
+ method->accessFlags,
+ addressSetCb, NULL, &context);
+
+ // If the line number was the last in the position table...
+ if (context.lastAddressValid) {
+ u4 i;
+ for (i = context.lastAddress; i < insnsSize; i++) {
+ dvmAddressSetSet(result, i);
+ }
+ }
+
+ return result;
+}
+
+
+/*
+ * ===========================================================================
+ * Dalvik Debug Monitor support
+ * ===========================================================================
+ */
+
+/*
+ * We have received a DDM packet over JDWP. Hand it off to the VM.
+ */
+bool dvmDbgDdmHandlePacket(const u1* buf, int dataLen, u1** pReplyBuf,
+ int* pReplyLen)
+{
+ return dvmDdmHandlePacket(buf, dataLen, pReplyBuf, pReplyLen);
+}
+
+/*
+ * First DDM packet has arrived over JDWP. Notify the press.
+ */
+void dvmDbgDdmConnected(void)
+{
+ dvmDdmConnected();
+}
+
+/*
+ * JDWP connection has dropped.
+ */
+void dvmDbgDdmDisconnected(void)
+{
+ dvmDdmDisconnected();
+}
+
+/*
+ * Send up a JDWP event packet with a DDM chunk in it.
+ */
+void dvmDbgDdmSendChunk(int type, size_t len, const u1* buf)
+{
+ assert(buf != NULL);
+ struct iovec vec[1] = { {(void*)buf, len} };
+ dvmDbgDdmSendChunkV(type, vec, 1);
+}
+
+/*
+ * Send up a JDWP event packet with a DDM chunk in it. The chunk is
+ * concatenated from multiple source buffers.
+ */
+void dvmDbgDdmSendChunkV(int type, const struct iovec* iov, int iovcnt)
+{
+ if (gDvm.jdwpState == NULL) {
+ LOGV("Debugger thread not active, ignoring DDM send (t=0x%08x)\n",
+ type);
+ return;
+ }
+
+ dvmJdwpDdmSendChunkV(gDvm.jdwpState, type, iov, iovcnt);
+}
diff --git a/vm/Debugger.h b/vm/Debugger.h
new file mode 100644
index 0000000..d722160
--- /dev/null
+++ b/vm/Debugger.h
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik-specific side of debugger support. (The JDWP code is intended to
+ * be relatively generic.)
+ */
+#ifndef _DALVIK_DEBUGGER
+#define _DALVIK_DEBUGGER
+
+#include "Common.h"
+#include "Misc.h"
+#include "jdwp/Jdwp.h"
+#include <pthread.h>
+
+/* fwd decl */
+struct Object;
+struct ClassObject;
+struct Method;
+struct Thread;
+
+/*
+ * Used by StepControl to track a set of addresses associated with
+ * a single line.
+ */
+typedef struct AddressSet {
+ u4 setSize;
+ u1 set[1];
+} AddressSet;
+
+INLINE void dvmAddressSetSet(AddressSet *pSet, u4 toSet)
+{
+ if (toSet < pSet->setSize) {
+ pSet->set[toSet/8] |= 1 << (toSet % 8);
+ }
+}
+
+INLINE bool dvmAddressSetGet(const AddressSet *pSet, u4 toGet)
+{
+ if (toGet < pSet->setSize) {
+ return (pSet->set[toGet/8] & (1 << (toGet % 8))) != 0;
+ } else {
+ return false;
+ }
+}
+
+/*
+ * Single-step management.
+ */
+typedef struct StepControl {
+ /* request */
+ enum JdwpStepSize size;
+ enum JdwpStepDepth depth;
+ struct Thread* thread; /* don't deref; for comparison only */
+
+ /* current state */
+ bool active;
+ const struct Method* method;
+ int line; /* line #; could be -1 */
+ const AddressSet* pAddressSet; /* if non-null, address set for line */
+ int frameDepth;
+} StepControl;
+
+/*
+ * Invoke-during-breakpoint support.
+ */
+typedef struct DebugInvokeReq {
+ /* boolean; only set when we're in the tail end of an event handler */
+ bool ready;
+
+ /* boolean; set if the JDWP thread wants this thread to do work */
+ bool invokeNeeded;
+
+ /* request */
+ struct Object* obj; /* not used for ClassType.InvokeMethod */
+ struct Object* thread;
+ struct ClassObject* clazz;
+ struct Method* method;
+ u4 numArgs;
+ u8* argArray; /* will be NULL if numArgs==0 */
+ u4 options;
+
+ /* result */
+ JdwpError err;
+ u1 resultTag;
+ JValue resultValue;
+ ObjectId exceptObj;
+
+ /* condition variable to wait on while the method executes */
+ pthread_mutex_t lock;
+ pthread_cond_t cv;
+} DebugInvokeReq;
+
+/* system init/shutdown */
+bool dvmDebuggerStartup(void);
+void dvmDebuggerShutdown(void);
+
+void dvmDbgInitMutex(pthread_mutex_t* pMutex);
+void dvmDbgLockMutex(pthread_mutex_t* pMutex);
+void dvmDbgUnlockMutex(pthread_mutex_t* pMutex);
+void dvmDbgInitCond(pthread_cond_t* pCond);
+void dvmDbgCondWait(pthread_cond_t* pCond, pthread_mutex_t* pMutex);
+void dvmDbgCondSignal(pthread_cond_t* pCond);
+void dvmDbgCondBroadcast(pthread_cond_t* pCond);
+
+/*
+ * Return the DebugInvokeReq for the current thread.
+ */
+DebugInvokeReq* dvmDbgGetInvokeReq(void);
+
+/*
+ * Enable/disable breakpoints and step modes. Used to provide a heads-up
+ * when the debugger attaches.
+ */
+void dvmDbgConnected(void);
+void dvmDbgActive(void);
+void dvmDbgDisconnected(void);
+
+/*
+ * Returns "true" if a debugger is connected. Returns "false" if it's
+ * just DDM.
+ */
+bool dvmDbgIsDebuggerConnected(void);
+
+/*
+ * Time, in milliseconds, since the last debugger activity. Does not
+ * include DDMS activity. Returns -1 if there has been no activity.
+ * Returns 0 if we're in the middle of handling a debugger request.
+ */
+s8 dvmDbgLastDebuggerActivity(void);
+
+/*
+ * Block/allow GC depending on what we're doing. These return the old
+ * status, which can be fed to dvmDbgThreadGoing() to restore the previous
+ * mode.
+ */
+int dvmDbgThreadRunning(void);
+int dvmDbgThreadWaiting(void);
+int dvmDbgThreadContinuing(int status);
+
+/*
+ * The debugger wants the VM to exit.
+ */
+void dvmDbgExit(int status);
+
+/*
+ * Class, Object, Array
+ */
+const char* dvmDbgGetClassDescriptor(RefTypeId id);
+ObjectId dvmDbgGetClassObject(RefTypeId id);
+RefTypeId dvmDbgGetSuperclass(RefTypeId id);
+ObjectId dvmDbgGetClassLoader(RefTypeId id);
+u4 dvmDbgGetAccessFlags(RefTypeId id);
+bool dvmDbgIsInterface(RefTypeId id);
+void dvmDbgGetClassList(u4* pNumClasses, RefTypeId** pClassRefBuf);
+void dvmDbgGetVisibleClassList(ObjectId classLoaderId, u4* pNumClasses,
+ RefTypeId** pClassRefBuf);
+void dvmDbgGetClassInfo(RefTypeId classId, u1* pTypeTag, u4* pStatus,
+ char** pSignature);
+bool dvmDbgFindLoadedClassBySignature(const char* classDescriptor,
+ RefTypeId* pRefTypeId);
+void dvmDbgGetObjectType(ObjectId objectId, u1* pRefTypeTag,
+ RefTypeId* pRefTypeId);
+u1 dvmDbgGetClassObjectType(RefTypeId refTypeId);
+char* dvmDbgGetSignature(RefTypeId refTypeId);
+const char* dvmDbgGetSourceFile(RefTypeId refTypeId);
+char* dvmDbgGetObjectTypeName(ObjectId objectId);
+int dvmDbgGetSignatureTag(const char* signature);
+int dvmDbgGetObjectTag(ObjectId objectId, const char* type);
+int dvmDbgGetTagWidth(int tag);
+
+int dvmDbgGetArrayLength(ObjectId arrayId);
+int dvmDbgGetArrayElementTag(ObjectId arrayId);
+bool dvmDbgOutputArray(ObjectId arrayId, int firstIndex, int count,
+ ExpandBuf* pReply);
+bool dvmDbgSetArrayElements(ObjectId arrayId, int firstIndex, int count,
+ const u1* buf);
+
+ObjectId dvmDbgCreateString(const char* str);
+ObjectId dvmDbgCreateObject(RefTypeId classId);
+ObjectId dvmDbgCreateArrayObject(RefTypeId arrayTypeId, u4 length);
+
+bool dvmDbgMatchType(RefTypeId instClassId, RefTypeId classId);
+
+/*
+ * Method and Field
+ */
+const char* dvmDbgGetMethodName(RefTypeId refTypeId, MethodId id);
+void dvmDbgOutputAllFields(RefTypeId refTypeId, bool withGeneric,
+ ExpandBuf* pReply);
+void dvmDbgOutputAllMethods(RefTypeId refTypeId, bool withGeneric,
+ ExpandBuf* pReply);
+void dvmDbgOutputAllInterfaces(RefTypeId refTypeId, ExpandBuf* pReply);
+void dvmDbgOutputLineTable(RefTypeId refTypeId, MethodId methodId,
+ ExpandBuf* pReply);
+void dvmDbgOutputVariableTable(RefTypeId refTypeId, MethodId id,
+ bool withGeneric, ExpandBuf* pReply);
+
+int dvmDbgGetFieldTag(ObjectId objId, FieldId fieldId);
+int dvmDbgGetStaticFieldTag(RefTypeId refTypeId, FieldId fieldId);
+void dvmDbgGetFieldValue(ObjectId objId, FieldId fieldId, u1* ptr, int width);
+void dvmDbgSetFieldValue(ObjectId objectId, FieldId fieldId, u8 value,
+ int width);
+void dvmDbgGetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId, u1* ptr,
+ int width);
+void dvmDbgSetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId,
+ u8 rawValue, int width);
+
+char* dvmDbgStringToUtf8(ObjectId strId);
+
+/*
+ * Thread, ThreadGroup, Frame
+ */
+char* dvmDbgGetThreadName(ObjectId threadId);
+ObjectId dvmDbgGetThreadGroup(ObjectId threadId);
+char* dvmDbgGetThreadGroupName(ObjectId threadGroupId);
+ObjectId dvmDbgGetThreadGroupParent(ObjectId threadGroupId);
+ObjectId dvmDbgGetSystemThreadGroupId(void);
+ObjectId dvmDbgGetMainThreadGroupId(void);
+
+bool dvmDbgGetThreadStatus(ObjectId threadId, u4* threadStatus,
+ u4* suspendStatus);
+u4 dvmDbgGetThreadSuspendCount(ObjectId threadId);
+bool dvmDbgThreadExists(ObjectId threadId);
+bool dvmDbgIsSuspended(ObjectId threadId);
+//void dvmDbgWaitForSuspend(ObjectId threadId);
+void dvmDbgGetThreadGroupThreads(ObjectId threadGroupId,
+ ObjectId** ppThreadIds, u4* pThreadCount);
+void dvmDbgGetAllThreads(ObjectId** ppThreadIds, u4* pThreadCount);
+int dvmDbgGetThreadFrameCount(ObjectId threadId);
+bool dvmDbgGetThreadFrame(ObjectId threadId, int num, FrameId* pFrameId,
+ JdwpLocation* pLoc);
+
+ObjectId dvmDbgGetThreadSelfId(void);
+void dvmDbgSuspendVM(bool isEvent);
+void dvmDbgResumeVM(void);
+void dvmDbgSuspendThread(ObjectId threadId);
+void dvmDbgResumeThread(ObjectId threadId);
+void dvmDbgSuspendSelf(void);
+
+bool dvmDbgGetThisObject(ObjectId threadId, FrameId frameId, ObjectId* pThisId);
+void dvmDbgGetLocalValue(ObjectId threadId, FrameId frameId, int slot,
+ u1 tag, u1* buf, int expectedLen);
+void dvmDbgSetLocalValue(ObjectId threadId, FrameId frameId, int slot,
+ u1 tag, u8 value, int width);
+
+
+/*
+ * Debugger notification
+ */
+void dvmDbgPostLocationEvent(const struct Method* method, int pcOffset,
+ struct Object* thisPtr, int eventFlags);
+void dvmDbgPostException(void* throwFp, int throwRelPc, void* catchFp,
+ int catchRelPc, struct Object* exception);
+void dvmDbgPostThreadStart(struct Thread* thread);
+void dvmDbgPostThreadDeath(struct Thread* thread);
+void dvmDbgPostClassPrepare(struct ClassObject* clazz);
+// FieldAccess, FieldModification
+
+/* for "eventFlags" */
+enum {
+ DBG_BREAKPOINT = 0x01,
+ DBG_SINGLE_STEP = 0x02,
+ DBG_METHOD_ENTRY = 0x04,
+ DBG_METHOD_EXIT = 0x08,
+};
+
+bool dvmDbgWatchLocation(const JdwpLocation* pLoc);
+void dvmDbgUnwatchLocation(const JdwpLocation* pLoc);
+bool dvmDbgConfigureStep(ObjectId threadId, enum JdwpStepSize size,
+ enum JdwpStepDepth depth);
+void dvmDbgUnconfigureStep(ObjectId threadId);
+
+JdwpError dvmDbgInvokeMethod(ObjectId threadId, ObjectId objectId,
+ RefTypeId classId, MethodId methodId, u4 numArgs, u8* argArray,
+ u4 options, u1* pResultTag, u8* pResultValue, ObjectId* pExceptObj);
+void dvmDbgExecuteMethod(DebugInvokeReq* pReq);
+
+/* Make an AddressSet for a line, for single stepping */
+const AddressSet *dvmAddressSetForLine(const struct Method* method, int line);
+
+/* perform "late registration" of an object ID */
+void dvmDbgRegisterObjectId(ObjectId id);
+
+/*
+ * DDM support.
+ */
+bool dvmDbgDdmHandlePacket(const u1* buf, int dataLen, u1** pReplyBuf,
+ int* pReplyLen);
+void dvmDbgDdmConnected(void);
+void dvmDbgDdmDisconnected(void);
+void dvmDbgDdmSendChunk(int type, size_t len, const u1* buf);
+void dvmDbgDdmSendChunkV(int type, const struct iovec* iov, int iovcnt);
+
+#define CHUNK_TYPE(_name) \
+ ((_name)[0] << 24 | (_name)[1] << 16 | (_name)[2] << 8 | (_name)[3])
+
+#endif /*_DALVIK_DEBUGGER*/
diff --git a/vm/Dvm.mk b/vm/Dvm.mk
new file mode 100644
index 0000000..0867ffa
--- /dev/null
+++ b/vm/Dvm.mk
@@ -0,0 +1,336 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Common definitions for host or target builds of libdvm.
+#
+# If you enable or disable optional features here, make sure you do
+# a "clean" build -- not everything depends on Dalvik.h. (See Android.mk
+# for the exact command.)
+#
+
+
+#
+# Compiler defines.
+#
+LOCAL_CFLAGS += -fstrict-aliasing -Wstrict-aliasing=2 -fno-align-jumps
+#LOCAL_CFLAGS += -DUSE_INDIRECT_REF
+LOCAL_CFLAGS += -Wall -Wextra -Wno-unused-parameter
+LOCAL_CFLAGS += -DARCH_VARIANT=\"$(dvm_arch_variant)\"
+
+#
+# Optional features. These may impact the size or performance of the VM.
+#
+
+ifeq ($(WITH_DEADLOCK_PREDICTION),true)
+ LOCAL_CFLAGS += -DWITH_DEADLOCK_PREDICTION
+ WITH_MONITOR_TRACKING := true
+endif
+ifeq ($(WITH_MONITOR_TRACKING),true)
+ LOCAL_CFLAGS += -DWITH_MONITOR_TRACKING
+endif
+
+# Make a debugging version when building the simulator (if not told
+# otherwise) and when explicitly asked.
+dvm_make_debug_vm := false
+ifeq ($(strip $(DEBUG_DALVIK_VM)),)
+ ifeq ($(dvm_simulator),true)
+ dvm_make_debug_vm := true
+ endif
+else
+ dvm_make_debug_vm := $(DEBUG_DALVIK_VM)
+endif
+
+ifeq ($(dvm_make_debug_vm),true)
+ #
+ # "Debug" profile:
+ # - debugger enabled
+ # - profiling enabled
+ # - tracked-reference verification enabled
+ # - allocation limits enabled
+ # - GDB helpers enabled
+ # - LOGV
+ # - assert()
+ #
+ LOCAL_CFLAGS += -DWITH_INSTR_CHECKS
+ LOCAL_CFLAGS += -DWITH_EXTRA_OBJECT_VALIDATION
+ LOCAL_CFLAGS += -DWITH_TRACKREF_CHECKS
+ LOCAL_CFLAGS += -DWITH_ALLOC_LIMITS
+ LOCAL_CFLAGS += -DWITH_EXTRA_GC_CHECKS=1
+ #LOCAL_CFLAGS += -DCHECK_MUTEX
+ #LOCAL_CFLAGS += -DPROFILE_FIELD_ACCESS
+ LOCAL_CFLAGS += -DDVM_SHOW_EXCEPTION=3
+ # add some extra stuff to make it easier to examine with GDB
+ LOCAL_CFLAGS += -DEASY_GDB
+ # overall config may be for a "release" build, so reconfigure these
+ LOCAL_CFLAGS += -UNDEBUG -DDEBUG=1 -DLOG_NDEBUG=1 -DWITH_DALVIK_ASSERT
+else # !dvm_make_debug_vm
+ #
+ # "Performance" profile:
+ # - all development features disabled
+ # - compiler optimizations enabled (redundant for "release" builds)
+ # - (debugging and profiling still enabled)
+ #
+ #LOCAL_CFLAGS += -DNDEBUG -DLOG_NDEBUG=1
+ # "-O2" is redundant for device (release) but useful for sim (debug)
+ #LOCAL_CFLAGS += -O2 -Winline
+ #LOCAL_CFLAGS += -DWITH_EXTRA_OBJECT_VALIDATION
+ LOCAL_CFLAGS += -DDVM_SHOW_EXCEPTION=1
+ # if you want to try with assertions on the device, add:
+ #LOCAL_CFLAGS += -UNDEBUG -DDEBUG=1 -DLOG_NDEBUG=1 -DWITH_DALVIK_ASSERT
+endif # !dvm_make_debug_vm
+
+# bug hunting: checksum and verify interpreted stack when making JNI calls
+#LOCAL_CFLAGS += -DWITH_JNI_STACK_CHECK
+
+LOCAL_SRC_FILES := \
+ AllocTracker.c \
+ Atomic.c.arm \
+ AtomicCache.c \
+ CheckJni.c \
+ Ddm.c \
+ Debugger.c \
+ DvmDex.c \
+ Exception.c \
+ Hash.c \
+ IndirectRefTable.c.arm \
+ Init.c \
+ InlineNative.c.arm \
+ Inlines.c \
+ Intern.c \
+ Jni.c \
+ JarFile.c \
+ LinearAlloc.c \
+ Misc.c.arm \
+ Native.c \
+ PointerSet.c \
+ Profile.c \
+ Properties.c \
+ RawDexFile.c \
+ ReferenceTable.c \
+ SignalCatcher.c \
+ StdioConverter.c \
+ Sync.c \
+ TestCompability.c \
+ Thread.c \
+ UtfString.c \
+ alloc/clz.c.arm \
+ alloc/Alloc.c \
+ alloc/CardTable.c \
+ alloc/HeapBitmap.c.arm \
+ alloc/HeapDebug.c \
+ alloc/HeapTable.c \
+ alloc/HeapWorker.c \
+ alloc/Heap.c.arm \
+ alloc/DdmHeap.c \
+ alloc/Verify.c \
+ alloc/Visit.c \
+ analysis/CodeVerify.c \
+ analysis/DexPrepare.c \
+ analysis/DexVerify.c \
+ analysis/Optimize.c \
+ analysis/RegisterMap.c \
+ analysis/VerifySubs.c \
+ interp/Interp.c.arm \
+ interp/Stack.c \
+ jdwp/ExpandBuf.c \
+ jdwp/JdwpAdb.c \
+ jdwp/JdwpConstants.c \
+ jdwp/JdwpEvent.c \
+ jdwp/JdwpHandler.c \
+ jdwp/JdwpMain.c \
+ jdwp/JdwpSocket.c \
+ mterp/Mterp.c.arm \
+ mterp/out/InterpC-portstd.c.arm \
+ mterp/out/InterpC-portdbg.c.arm \
+ native/InternalNative.c \
+ native/dalvik_system_DexFile.c \
+ native/dalvik_system_VMDebug.c \
+ native/dalvik_system_VMRuntime.c \
+ native/dalvik_system_VMStack.c \
+ native/dalvik_system_Zygote.c \
+ native/java_lang_Class.c \
+ native/java_lang_Object.c \
+ native/java_lang_Runtime.c \
+ native/java_lang_String.c \
+ native/java_lang_System.c \
+ native/java_lang_SystemProperties.c \
+ native/java_lang_Throwable.c \
+ native/java_lang_VMClassLoader.c \
+ native/java_lang_VMThread.c \
+ native/java_lang_reflect_AccessibleObject.c \
+ native/java_lang_reflect_Array.c \
+ native/java_lang_reflect_Constructor.c \
+ native/java_lang_reflect_Field.c \
+ native/java_lang_reflect_Method.c \
+ native/java_lang_reflect_Proxy.c \
+ native/java_security_AccessController.c \
+ native/java_util_concurrent_atomic_AtomicLong.c \
+ native/org_apache_harmony_dalvik_NativeTestTarget.c \
+ native/org_apache_harmony_dalvik_ddmc_DdmServer.c \
+ native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.c \
+ native/sun_misc_Unsafe.c \
+ oo/AccessCheck.c \
+ oo/Array.c \
+ oo/Class.c \
+ oo/Object.c \
+ oo/Resolve.c \
+ oo/TypeCheck.c \
+ reflect/Annotation.c \
+ reflect/Proxy.c \
+ reflect/Reflect.c \
+ test/AtomicTest.c.arm \
+ test/TestHash.c \
+ test/TestIndirectRefTable.c
+
+WITH_COPYING_GC := $(strip $(WITH_COPYING_GC))
+
+ifeq ($(WITH_COPYING_GC),true)
+ LOCAL_CFLAGS += -DWITH_COPYING_GC
+ LOCAL_SRC_FILES += \
+ alloc/Copying.c.arm
+else
+ LOCAL_SRC_FILES += \
+ alloc/HeapSource.c \
+ alloc/MarkSweep.c.arm
+endif
+
+WITH_JIT := $(strip $(WITH_JIT))
+
+ifeq ($(WITH_JIT),true)
+ LOCAL_CFLAGS += -DWITH_JIT
+ LOCAL_SRC_FILES += \
+ compiler/Compiler.c \
+ compiler/Frontend.c \
+ compiler/Utility.c \
+ compiler/InlineTransformation.c \
+ compiler/IntermediateRep.c \
+ compiler/Dataflow.c \
+ compiler/Loop.c \
+ compiler/Ralloc.c \
+ interp/Jit.c
+endif
+
+WITH_HPROF := $(strip $(WITH_HPROF))
+ifeq ($(WITH_HPROF),)
+ WITH_HPROF := true
+endif
+ifeq ($(WITH_HPROF),true)
+ LOCAL_SRC_FILES += \
+ hprof/Hprof.c \
+ hprof/HprofClass.c \
+ hprof/HprofHeap.c \
+ hprof/HprofOutput.c \
+ hprof/HprofString.c
+ LOCAL_CFLAGS += -DWITH_HPROF=1
+
+ ifeq ($(strip $(WITH_HPROF_STACK)),true)
+ LOCAL_SRC_FILES += \
+ hprof/HprofStack.c \
+ hprof/HprofStackFrame.c
+ LOCAL_CFLAGS += -DWITH_HPROF_STACK=1
+ endif # WITH_HPROF_STACK
+endif # WITH_HPROF
+
+LOCAL_C_INCLUDES += \
+ $(JNI_H_INCLUDE) \
+ dalvik \
+ dalvik/vm \
+ external/zlib \
+ $(KERNEL_HEADERS)
+
+
+ifeq ($(dvm_simulator),true)
+ LOCAL_LDLIBS += -lpthread -ldl
+ ifeq ($(HOST_OS),linux)
+ # need this for clock_gettime() in profiling
+ LOCAL_LDLIBS += -lrt
+ endif
+endif
+
+MTERP_ARCH_KNOWN := false
+
+ifeq ($(dvm_arch),arm)
+ #dvm_arch_variant := armv7-a
+ #LOCAL_CFLAGS += -march=armv7-a -mfloat-abi=softfp -mfpu=vfp
+ LOCAL_CFLAGS += -Werror
+ MTERP_ARCH_KNOWN := true
+ # Select architecture-specific sources (armv4t, armv5te etc.)
+ LOCAL_SRC_FILES += \
+ arch/arm/CallOldABI.S \
+ arch/arm/CallEABI.S \
+ arch/arm/HintsEABI.c \
+ mterp/out/InterpC-$(dvm_arch_variant).c.arm \
+ mterp/out/InterpAsm-$(dvm_arch_variant).S
+
+ ifeq ($(WITH_JIT),true)
+ LOCAL_SRC_FILES += \
+ compiler/codegen/arm/RallocUtil.c \
+ compiler/codegen/arm/$(dvm_arch_variant)/Codegen.c \
+ compiler/codegen/arm/$(dvm_arch_variant)/CallingConvention.S \
+ compiler/codegen/arm/Assemble.c \
+ compiler/codegen/arm/ArchUtility.c \
+ compiler/codegen/arm/LocalOptimizations.c \
+ compiler/codegen/arm/GlobalOptimizations.c \
+ compiler/template/out/CompilerTemplateAsm-$(dvm_arch_variant).S
+ endif
+endif
+
+ifeq ($(dvm_arch),x86)
+ ifeq ($(dvm_os),linux)
+ MTERP_ARCH_KNOWN := true
+ LOCAL_SRC_FILES += \
+ arch/$(dvm_arch_variant)/Call386ABI.S \
+ arch/$(dvm_arch_variant)/Hints386ABI.c \
+ mterp/out/InterpC-$(dvm_arch_variant).c \
+ mterp/out/InterpAsm-$(dvm_arch_variant).S
+ endif
+endif
+
+ifeq ($(dvm_arch),sh)
+ MTERP_ARCH_KNOWN := true
+ LOCAL_SRC_FILES += \
+ arch/sh/CallSH4ABI.S \
+ arch/generic/Hints.c \
+ mterp/out/InterpC-allstubs.c \
+ mterp/out/InterpAsm-allstubs.S
+endif
+
+ifeq ($(MTERP_ARCH_KNOWN),false)
+ # unknown architecture, try to use FFI
+ LOCAL_C_INCLUDES += external/libffi/$(dvm_os)-$(dvm_arch)
+
+ ifeq ($(dvm_os)-$(dvm_arch),darwin-x86)
+ # OSX includes libffi, so just make the linker aware of it directly.
+ LOCAL_LDLIBS += -lffi
+ else
+ LOCAL_SHARED_LIBRARIES += libffi
+ endif
+
+ LOCAL_SRC_FILES += \
+ arch/generic/Call.c \
+ arch/generic/Hints.c \
+ mterp/out/InterpC-allstubs.c
+
+ # The following symbols are usually defined in the asm file, but
+ # since we don't have an asm file in this case, we instead just
+ # peg them at 0 here, and we add an #ifdef'able define for good
+ # measure, too.
+ LOCAL_CFLAGS += -DdvmAsmInstructionStart=0 -DdvmAsmInstructionEnd=0 \
+ -DdvmAsmSisterStart=0 -DdvmAsmSisterEnd=0 -DDVM_NO_ASM_INTERP=1
+endif
+
+ifeq ($(TEST_VM_IN_ECLAIR),true)
+ LOCAL_CFLAGS += -DTEST_VM_IN_ECLAIR
+endif
diff --git a/vm/DvmDex.c b/vm/DvmDex.c
new file mode 100644
index 0000000..94422b3
--- /dev/null
+++ b/vm/DvmDex.c
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * VM-specific state associated with a DEX file.
+ */
+#include "Dalvik.h"
+
+
+/*
+ * Create auxillary data structures.
+ *
+ * We need a 4-byte pointer for every reference to a class, method, field,
+ * or string constant. Summed up over all loaded DEX files (including the
+ * whoppers in the boostrap class path), this adds up to be quite a bit
+ * of native memory.
+ *
+ * For more traditional VMs these values could be stuffed into the loaded
+ * class file constant pool area, but we don't have that luxury since our
+ * classes are memory-mapped read-only.
+ *
+ * The DEX optimizer will remove the need for some of these (e.g. we won't
+ * use the entry for virtual methods that are only called through
+ * invoke-virtual-quick), creating the possibility of some space reduction
+ * at dexopt time.
+ */
+static DvmDex* allocateAuxStructures(DexFile* pDexFile)
+{
+ DvmDex* pDvmDex;
+ const DexHeader* pHeader;
+ u4 stringCount, classCount, methodCount, fieldCount;
+
+ pDvmDex = (DvmDex*) calloc(1, sizeof(DvmDex));
+ if (pDvmDex == NULL)
+ return NULL;
+
+ pDvmDex->pDexFile = pDexFile;
+ pDvmDex->pHeader = pDexFile->pHeader;
+
+ pHeader = pDvmDex->pHeader;
+
+ stringCount = pHeader->stringIdsSize;
+ classCount = pHeader->typeIdsSize;
+ methodCount = pHeader->methodIdsSize;
+ fieldCount = pHeader->fieldIdsSize;
+
+ pDvmDex->pResStrings = (struct StringObject**)
+ calloc(stringCount, sizeof(struct StringObject*));
+
+ pDvmDex->pResClasses = (struct ClassObject**)
+ calloc(classCount, sizeof(struct ClassObject*));
+
+ pDvmDex->pResMethods = (struct Method**)
+ calloc(methodCount, sizeof(struct Method*));
+
+ pDvmDex->pResFields = (struct Field**)
+ calloc(fieldCount, sizeof(struct Field*));
+
+ LOGV("+++ DEX %p: allocateAux %d+%d+%d+%d * 4 = %d bytes\n",
+ pDvmDex, stringCount, classCount, methodCount, fieldCount,
+ (stringCount + classCount + methodCount + fieldCount) * 4);
+
+ pDvmDex->pInterfaceCache = dvmAllocAtomicCache(DEX_INTERFACE_CACHE_SIZE);
+
+ if (pDvmDex->pResStrings == NULL ||
+ pDvmDex->pResClasses == NULL ||
+ pDvmDex->pResMethods == NULL ||
+ pDvmDex->pResFields == NULL ||
+ pDvmDex->pInterfaceCache == NULL)
+ {
+ LOGE("Alloc failure in allocateAuxStructures\n");
+ free(pDvmDex->pResStrings);
+ free(pDvmDex->pResClasses);
+ free(pDvmDex->pResMethods);
+ free(pDvmDex->pResFields);
+ free(pDvmDex);
+ return NULL;
+ }
+
+ return pDvmDex;
+
+}
+
+/*
+ * Given an open optimized DEX file, map it into read-only shared memory and
+ * parse the contents.
+ *
+ * Returns nonzero on error.
+ */
+int dvmDexFileOpenFromFd(int fd, DvmDex** ppDvmDex)
+{
+ DvmDex* pDvmDex;
+ DexFile* pDexFile;
+ MemMapping memMap;
+ int parseFlags = kDexParseDefault;
+ int result = -1;
+
+ if (gDvm.verifyDexChecksum)
+ parseFlags |= kDexParseVerifyChecksum;
+
+ if (lseek(fd, 0, SEEK_SET) < 0) {
+ LOGE("lseek rewind failed\n");
+ goto bail;
+ }
+
+ if (sysMapFileInShmemWritableReadOnly(fd, &memMap) != 0) {
+ LOGE("Unable to map file\n");
+ goto bail;
+ }
+
+ pDexFile = dexFileParse(memMap.addr, memMap.length, parseFlags);
+ if (pDexFile == NULL) {
+ LOGE("DEX parse failed\n");
+ sysReleaseShmem(&memMap);
+ goto bail;
+ }
+
+ pDvmDex = allocateAuxStructures(pDexFile);
+ if (pDvmDex == NULL) {
+ dexFileFree(pDexFile);
+ sysReleaseShmem(&memMap);
+ goto bail;
+ }
+
+ /* tuck this into the DexFile so it gets released later */
+ sysCopyMap(&pDvmDex->memMap, &memMap);
+ *ppDvmDex = pDvmDex;
+ result = 0;
+
+bail:
+ return result;
+}
+
+/*
+ * Create a DexFile structure for a "partial" DEX. This is one that is in
+ * the process of being optimized. The optimization header isn't finished
+ * and we won't have any of the auxillary data tables, so we have to do
+ * the initialization slightly differently.
+ *
+ * Returns nonzero on error.
+ */
+int dvmDexFileOpenPartial(const void* addr, int len, DvmDex** ppDvmDex)
+{
+ DvmDex* pDvmDex;
+ DexFile* pDexFile;
+ int parseFlags = kDexParseDefault;
+ int result = -1;
+
+ /* -- file is incomplete, new checksum has not yet been calculated
+ if (gDvm.verifyDexChecksum)
+ parseFlags |= kDexParseVerifyChecksum;
+ */
+
+ pDexFile = dexFileParse(addr, len, parseFlags);
+ if (pDexFile == NULL) {
+ LOGE("DEX parse failed\n");
+ goto bail;
+ }
+ pDvmDex = allocateAuxStructures(pDexFile);
+ if (pDvmDex == NULL) {
+ dexFileFree(pDexFile);
+ goto bail;
+ }
+
+ *ppDvmDex = pDvmDex;
+ result = 0;
+
+bail:
+ return result;
+}
+
+/*
+ * Free up the DexFile and any associated data structures.
+ *
+ * Note we may be called with a partially-initialized DvmDex.
+ */
+void dvmDexFileFree(DvmDex* pDvmDex)
+{
+ if (pDvmDex == NULL)
+ return;
+
+ dexFileFree(pDvmDex->pDexFile);
+
+ LOGV("+++ DEX %p: freeing aux structs\n", pDvmDex);
+ free(pDvmDex->pResStrings);
+ free(pDvmDex->pResClasses);
+ free(pDvmDex->pResMethods);
+ free(pDvmDex->pResFields);
+ dvmFreeAtomicCache(pDvmDex->pInterfaceCache);
+
+ sysReleaseShmem(&pDvmDex->memMap);
+ free(pDvmDex);
+}
+
+
+/*
+ * Change the byte at the specified address to a new value. If the location
+ * already has the new value, do nothing.
+ *
+ * This requires changing the access permissions to read-write, updating
+ * the value, and then resetting the permissions.
+ *
+ * We need to ensure mutual exclusion at a page granularity to avoid a race
+ * where one threads sets read-write, another thread sets read-only, and
+ * then the first thread does a write. Since we don't do a lot of updates,
+ * and the window is small, we just use a lock across the entire DvmDex.
+ * We're only trying to make the page state change atomic; it's up to the
+ * caller to ensure that multiple threads aren't stomping on the same
+ * location (e.g. breakpoints and verifier/optimizer changes happening
+ * simultaneously).
+ *
+ * TODO: if we're back to the original state of the page, use
+ * madvise(MADV_DONTNEED) to release the private/dirty copy.
+ *
+ * Returns "true" on success.
+ */
+bool dvmDexChangeDex1(DvmDex* pDvmDex, u1* addr, u1 newVal)
+{
+ if (*addr == newVal) {
+ LOGV("+++ byte at %p is already 0x%02x\n", addr, newVal);
+ return true;
+ }
+
+ /*
+ * We're not holding this for long, so we don't bother with switching
+ * to VMWAIT.
+ */
+ dvmLockMutex(&pDvmDex->modLock);
+
+ LOGV("+++ change byte at %p from 0x%02x to 0x%02x\n", addr, *addr, newVal);
+ if (sysChangeMapAccess(addr, 1, true, &pDvmDex->memMap) != 0) {
+ LOGD("NOTE: DEX page access change (->RW) failed\n");
+ /* expected on files mounted from FAT; keep going (may crash) */
+ }
+
+ *addr = newVal;
+
+ if (sysChangeMapAccess(addr, 1, false, &pDvmDex->memMap) != 0) {
+ LOGD("NOTE: DEX page access change (->RO) failed\n");
+ /* expected on files mounted from FAT; keep going */
+ }
+
+ dvmUnlockMutex(&pDvmDex->modLock);
+
+ return true;
+}
+
+/*
+ * Change the 2-byte value at the specified address to a new value. If the
+ * location already has the new value, do nothing.
+ *
+ * Otherwise works like dvmDexChangeDex1.
+ */
+bool dvmDexChangeDex2(DvmDex* pDvmDex, u2* addr, u2 newVal)
+{
+ if (*addr == newVal) {
+ LOGV("+++ value at %p is already 0x%04x\n", addr, newVal);
+ return true;
+ }
+
+ /*
+ * We're not holding this for long, so we don't bother with switching
+ * to VMWAIT.
+ */
+ dvmLockMutex(&pDvmDex->modLock);
+
+ LOGV("+++ change 2byte at %p from 0x%04x to 0x%04x\n", addr, *addr, newVal);
+ if (sysChangeMapAccess(addr, 2, true, &pDvmDex->memMap) != 0) {
+ LOGD("NOTE: DEX page access change (->RW) failed\n");
+ /* expected on files mounted from FAT; keep going (may crash) */
+ }
+
+ *addr = newVal;
+
+ if (sysChangeMapAccess(addr, 2, false, &pDvmDex->memMap) != 0) {
+ LOGD("NOTE: DEX page access change (->RO) failed\n");
+ /* expected on files mounted from FAT; keep going */
+ }
+
+ dvmUnlockMutex(&pDvmDex->modLock);
+
+ return true;
+}
diff --git a/vm/DvmDex.h b/vm/DvmDex.h
new file mode 100644
index 0000000..f36940b
--- /dev/null
+++ b/vm/DvmDex.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * The VM wraps some additional data structures around the DexFile. These
+ * are defined here.
+ */
+#ifndef _DALVIK_DVMDEX
+#define _DALVIK_DVMDEX
+
+#include "libdex/DexFile.h"
+
+/* extern */
+struct ClassObject;
+struct HashTable;
+struct InstField;
+struct Method;
+struct StringObject;
+
+
+/*
+ * Some additional VM data structures that are associated with the DEX file.
+ */
+typedef struct DvmDex {
+ /* pointer to the DexFile we're associated with */
+ DexFile* pDexFile;
+
+ /* clone of pDexFile->pHeader (it's used frequently enough) */
+ const DexHeader* pHeader;
+
+ /* interned strings; parallel to "stringIds" */
+ struct StringObject** pResStrings;
+
+ /* resolved classes; parallel to "typeIds" */
+ struct ClassObject** pResClasses;
+
+ /* resolved methods; parallel to "methodIds" */
+ struct Method** pResMethods;
+
+ /* resolved instance fields; parallel to "fieldIds" */
+ /* (this holds both InstField and StaticField) */
+ struct Field** pResFields;
+
+ /* interface method lookup cache */
+ struct AtomicCache* pInterfaceCache;
+
+ /* shared memory region with file contents */
+ MemMapping memMap;
+
+ /* lock ensuring mutual exclusion during updates */
+ pthread_mutex_t modLock;
+} DvmDex;
+
+
+/*
+ * Given a file descriptor for an open "optimized" DEX file, map it into
+ * memory and parse the contents.
+ *
+ * On success, returns 0 and sets "*ppDvmDex" to a newly-allocated DvmDex.
+ * On failure, returns a meaningful error code [currently just -1].
+ */
+int dvmDexFileOpenFromFd(int fd, DvmDex** ppDvmDex);
+
+/*
+ * Open a partial DEX file. Only useful as part of the optimization process.
+ */
+int dvmDexFileOpenPartial(const void* addr, int len, DvmDex** ppDvmDex);
+
+/*
+ * Free a DvmDex structure, along with any associated structures.
+ */
+void dvmDexFileFree(DvmDex* pDvmDex);
+
+
+/*
+ * Change the 1- or 2-byte value at the specified address to a new value. If
+ * the location already has the new value, do nothing.
+ *
+ * This does not make any synchronization guarantees. The caller must
+ * ensure exclusivity vs. other callers.
+ *
+ * For the 2-byte call, the pointer should have 16-bit alignment.
+ *
+ * Returns "true" on success.
+ */
+bool dvmDexChangeDex1(DvmDex* pDvmDex, u1* addr, u1 newVal);
+bool dvmDexChangeDex2(DvmDex* pDvmDex, u2* addr, u2 newVal);
+
+
+/*
+ * Return the requested item if it has been resolved, or NULL if it hasn't.
+ */
+INLINE struct StringObject* dvmDexGetResolvedString(const DvmDex* pDvmDex,
+ u4 stringIdx)
+{
+ assert(stringIdx < pDvmDex->pHeader->stringIdsSize);
+ return pDvmDex->pResStrings[stringIdx];
+}
+INLINE struct ClassObject* dvmDexGetResolvedClass(const DvmDex* pDvmDex,
+ u4 classIdx)
+{
+ assert(classIdx < pDvmDex->pHeader->typeIdsSize);
+ return pDvmDex->pResClasses[classIdx];
+}
+INLINE struct Method* dvmDexGetResolvedMethod(const DvmDex* pDvmDex,
+ u4 methodIdx)
+{
+ assert(methodIdx < pDvmDex->pHeader->methodIdsSize);
+ return pDvmDex->pResMethods[methodIdx];
+}
+INLINE struct Field* dvmDexGetResolvedField(const DvmDex* pDvmDex,
+ u4 fieldIdx)
+{
+ assert(fieldIdx < pDvmDex->pHeader->fieldIdsSize);
+ return pDvmDex->pResFields[fieldIdx];
+}
+
+/*
+ * Update the resolved item table. Resolution always produces the same
+ * result, so we're not worried about atomicity here.
+ */
+INLINE void dvmDexSetResolvedString(DvmDex* pDvmDex, u4 stringIdx,
+ struct StringObject* str)
+{
+ assert(stringIdx < pDvmDex->pHeader->stringIdsSize);
+ pDvmDex->pResStrings[stringIdx] = str;
+}
+INLINE void dvmDexSetResolvedClass(DvmDex* pDvmDex, u4 classIdx,
+ struct ClassObject* clazz)
+{
+ assert(classIdx < pDvmDex->pHeader->typeIdsSize);
+ pDvmDex->pResClasses[classIdx] = clazz;
+}
+INLINE void dvmDexSetResolvedMethod(DvmDex* pDvmDex, u4 methodIdx,
+ struct Method* method)
+{
+ assert(methodIdx < pDvmDex->pHeader->methodIdsSize);
+ pDvmDex->pResMethods[methodIdx] = method;
+}
+INLINE void dvmDexSetResolvedField(DvmDex* pDvmDex, u4 fieldIdx,
+ struct Field* field)
+{
+ assert(fieldIdx < pDvmDex->pHeader->fieldIdsSize);
+ pDvmDex->pResFields[fieldIdx] = field;
+}
+
+#endif /*_DALVIK_DVMDEX*/
diff --git a/vm/Exception.c b/vm/Exception.c
new file mode 100644
index 0000000..3a73420
--- /dev/null
+++ b/vm/Exception.c
@@ -0,0 +1,1304 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Exception handling.
+ */
+#include "Dalvik.h"
+#include "libdex/DexCatch.h"
+
+#include <stdlib.h>
+
+/*
+Notes on Exception Handling
+
+We have one fairly sticky issue to deal with: creating the exception stack
+trace. The trouble is that we need the current value of the program
+counter for the method now being executed, but that's only held in a local
+variable or hardware register in the main interpreter loop.
+
+The exception mechanism requires that the current stack trace be associated
+with a Throwable at the time the Throwable is constructed. The construction
+may or may not be associated with a throw. We have three situations to
+consider:
+
+ (1) A Throwable is created with a "new Throwable" statement in the
+ application code, for immediate or deferred use with a "throw" statement.
+ (2) The VM throws an exception from within the interpreter core, e.g.
+ after an integer divide-by-zero.
+ (3) The VM throws an exception from somewhere deeper down, e.g. while
+ trying to link a class.
+
+We need to have the current value for the PC, which means that for
+situation (3) the interpreter loop must copy it to an externally-accessible
+location before handling any opcode that could cause the VM to throw
+an exception. We can't store it globally, because the various threads
+would trample each other. We can't store it in the Thread structure,
+because it'll get overwritten as soon as the Throwable constructor starts
+executing. It needs to go on the stack, but our stack frames hold the
+caller's *saved* PC, not the current PC.
+
+Situation #1 doesn't require special handling. Situation #2 could be dealt
+with by passing the PC into the exception creation function. The trick
+is to solve situation #3 in a way that adds minimal overhead to common
+operations. Making it more costly to throw an exception is acceptable.
+
+There are a few ways to deal with this:
+
+ (a) Change "savedPc" to "currentPc" in the stack frame. All of the
+ stack logic gets offset by one frame. The current PC is written
+ to the current stack frame when necessary.
+ (b) Write the current PC into the current stack frame, but without
+ replacing "savedPc". The JNI local refs pointer, which is only
+ used for native code, can be overloaded to save space.
+ (c) In dvmThrowException(), push an extra stack frame on, with the
+ current PC in it. The current PC is written into the Thread struct
+ when necessary, and copied out when the VM throws.
+ (d) Before doing something that might throw an exception, push a
+ temporary frame on with the saved PC in it.
+
+Solution (a) is the simplest, but breaks Dalvik's goal of mingling native
+and interpreted stacks.
+
+Solution (b) retains the simplicity of (a) without rearranging the stack,
+but now in some cases we're storing the PC twice, which feels wrong.
+
+Solution (c) usually works, because we push the saved PC onto the stack
+before the Throwable construction can overwrite the copy in Thread. One
+way solution (c) could break is:
+ - Interpreter saves the PC
+ - Execute some bytecode, which runs successfully (and alters the saved PC)
+ - Throw an exception before re-saving the PC (i.e in the same opcode)
+This is a risk for anything that could cause <clinit> to execute, e.g.
+executing a static method or accessing a static field. Attemping to access
+a field that doesn't exist in a class that does exist might cause this.
+It may be possible to simply bracket the dvmCallMethod*() functions to
+save/restore it.
+
+Solution (d) incurs additional overhead, but may have other benefits (e.g.
+it's easy to find the stack frames that should be removed before storage
+in the Throwable).
+
+Current plan is option (b), because it's simple, fast, and doesn't change
+the way the stack works.
+*/
+
+/* fwd */
+static bool initException(Object* exception, const char* msg, Object* cause,
+ Thread* self);
+
+
+/*
+ * Cache pointers to some of the exception classes we use locally.
+ *
+ * Note this is NOT called during dexopt optimization. Some of the fields
+ * are initialized by the verifier (dvmVerifyCodeFlow).
+ */
+bool dvmExceptionStartup(void)
+{
+ gDvm.classJavaLangThrowable =
+ dvmFindSystemClassNoInit("Ljava/lang/Throwable;");
+ gDvm.classJavaLangRuntimeException =
+ dvmFindSystemClassNoInit("Ljava/lang/RuntimeException;");
+ gDvm.classJavaLangStackOverflowError =
+ dvmFindSystemClassNoInit("Ljava/lang/StackOverflowError;");
+ gDvm.classJavaLangError =
+ dvmFindSystemClassNoInit("Ljava/lang/Error;");
+ gDvm.classJavaLangStackTraceElement =
+ dvmFindSystemClassNoInit("Ljava/lang/StackTraceElement;");
+ gDvm.classJavaLangStackTraceElementArray =
+ dvmFindArrayClass("[Ljava/lang/StackTraceElement;", NULL);
+ if (gDvm.classJavaLangThrowable == NULL ||
+ gDvm.classJavaLangStackTraceElement == NULL ||
+ gDvm.classJavaLangStackTraceElementArray == NULL)
+ {
+ LOGE("Could not find one or more essential exception classes\n");
+ return false;
+ }
+
+ /*
+ * Find the constructor. Note that, unlike other saved method lookups,
+ * we're using a Method* instead of a vtable offset. This is because
+ * constructors don't have vtable offsets. (Also, since we're creating
+ * the object in question, it's impossible for anyone to sub-class it.)
+ */
+ Method* meth;
+ meth = dvmFindDirectMethodByDescriptor(gDvm.classJavaLangStackTraceElement,
+ "<init>", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V");
+ if (meth == NULL) {
+ LOGE("Unable to find constructor for StackTraceElement\n");
+ return false;
+ }
+ gDvm.methJavaLangStackTraceElement_init = meth;
+
+ /* grab an offset for the stackData field */
+ gDvm.offJavaLangThrowable_stackState =
+ dvmFindFieldOffset(gDvm.classJavaLangThrowable,
+ "stackState", "Ljava/lang/Object;");
+ if (gDvm.offJavaLangThrowable_stackState < 0) {
+ LOGE("Unable to find Throwable.stackState\n");
+ return false;
+ }
+
+ /* and one for the message field, in case we want to show it */
+ gDvm.offJavaLangThrowable_message =
+ dvmFindFieldOffset(gDvm.classJavaLangThrowable,
+ "detailMessage", "Ljava/lang/String;");
+ if (gDvm.offJavaLangThrowable_message < 0) {
+ LOGE("Unable to find Throwable.detailMessage\n");
+ return false;
+ }
+
+ /* and one for the cause field, just 'cause */
+ gDvm.offJavaLangThrowable_cause =
+ dvmFindFieldOffset(gDvm.classJavaLangThrowable,
+ "cause", "Ljava/lang/Throwable;");
+ if (gDvm.offJavaLangThrowable_cause < 0) {
+ LOGE("Unable to find Throwable.cause\n");
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Clean up.
+ */
+void dvmExceptionShutdown(void)
+{
+ // nothing to do
+}
+
+
+/*
+ * Format the message into a small buffer and pass it along.
+ */
+void dvmThrowExceptionFmtV(const char* exceptionDescriptor, const char* fmt,
+ va_list args)
+{
+ char msgBuf[512];
+
+ vsnprintf(msgBuf, sizeof(msgBuf), fmt, args);
+ dvmThrowChainedException(exceptionDescriptor, msgBuf, NULL);
+}
+
+/*
+ * Create a Throwable and throw an exception in the current thread (where
+ * "throwing" just means "set the thread's exception pointer").
+ *
+ * "msg" and/or "cause" may be NULL.
+ *
+ * If we have a bad exception hierarchy -- something in Throwable.<init>
+ * is missing -- then every attempt to throw an exception will result
+ * in another exception. Exceptions are generally allowed to "chain"
+ * to other exceptions, so it's hard to auto-detect this problem. It can
+ * only happen if the system classes are broken, so it's probably not
+ * worth spending cycles to detect it.
+ *
+ * We do have one case to worry about: if the classpath is completely
+ * wrong, we'll go into a death spin during startup because we can't find
+ * the initial class and then we can't find NoClassDefFoundError. We have
+ * to handle this case.
+ *
+ * [Do we want to cache pointers to common exception classes?]
+ */
+void dvmThrowChainedException(const char* exceptionDescriptor, const char* msg,
+ Object* cause)
+{
+ ClassObject* excepClass;
+
+ LOGV("THROW '%s' msg='%s' cause=%s\n",
+ exceptionDescriptor, msg,
+ (cause != NULL) ? cause->clazz->descriptor : "(none)");
+
+ if (gDvm.initializing) {
+ if (++gDvm.initExceptionCount >= 2) {
+ LOGE("Too many exceptions during init (failed on '%s' '%s')\n",
+ exceptionDescriptor, msg);
+ dvmAbort();
+ }
+ }
+
+ excepClass = dvmFindSystemClass(exceptionDescriptor);
+ if (excepClass == NULL) {
+ /*
+ * We couldn't find the exception class. The attempt to find a
+ * nonexistent class should have raised an exception. If no
+ * exception is currently raised, then we're pretty clearly unable
+ * to throw ANY sort of exception, and we need to pack it in.
+ *
+ * If we were able to throw the "class load failed" exception,
+ * stick with that. Ideally we'd stuff the original exception
+ * into the "cause" field, but since we can't find it we can't
+ * do that. The exception class name should be in the "message"
+ * field.
+ */
+ if (!dvmCheckException(dvmThreadSelf())) {
+ LOGE("FATAL: unable to throw exception (failed on '%s' '%s')\n",
+ exceptionDescriptor, msg);
+ dvmAbort();
+ }
+ return;
+ }
+
+ dvmThrowChainedExceptionByClass(excepClass, msg, cause);
+}
+
+/*
+ * Start/continue throwing process now that we have a class reference.
+ */
+void dvmThrowChainedExceptionByClass(ClassObject* excepClass, const char* msg,
+ Object* cause)
+{
+ Thread* self = dvmThreadSelf();
+ Object* exception;
+
+ /* make sure the exception is initialized */
+ if (!dvmIsClassInitialized(excepClass) && !dvmInitClass(excepClass)) {
+ LOGE("ERROR: unable to initialize exception class '%s'\n",
+ excepClass->descriptor);
+ if (strcmp(excepClass->descriptor, "Ljava/lang/InternalError;") == 0)
+ dvmAbort();
+ dvmThrowChainedException("Ljava/lang/InternalError;",
+ "failed to init original exception class", cause);
+ return;
+ }
+
+ exception = dvmAllocObject(excepClass, ALLOC_DEFAULT);
+ if (exception == NULL) {
+ /*
+ * We're in a lot of trouble. We might be in the process of
+ * throwing an out-of-memory exception, in which case the
+ * pre-allocated object will have been thrown when our object alloc
+ * failed. So long as there's an exception raised, return and
+ * allow the system to try to recover. If not, something is broken
+ * and we need to bail out.
+ */
+ if (dvmCheckException(self))
+ goto bail;
+ LOGE("FATAL: unable to allocate exception '%s' '%s'\n",
+ excepClass->descriptor, msg != NULL ? msg : "(no msg)");
+ dvmAbort();
+ }
+
+ /*
+ * Init the exception.
+ */
+ if (gDvm.optimizing) {
+ /* need the exception object, but can't invoke interpreted code */
+ LOGV("Skipping init of exception %s '%s'\n",
+ excepClass->descriptor, msg);
+ } else {
+ assert(excepClass == exception->clazz);
+ if (!initException(exception, msg, cause, self)) {
+ /*
+ * Whoops. If we can't initialize the exception, we can't use
+ * it. If there's an exception already set, the constructor
+ * probably threw an OutOfMemoryError.
+ */
+ if (!dvmCheckException(self)) {
+ /*
+ * We're required to throw something, so we just
+ * throw the pre-constructed internal error.
+ */
+ self->exception = gDvm.internalErrorObj;
+ }
+ goto bail;
+ }
+ }
+
+ self->exception = exception;
+
+bail:
+ dvmReleaseTrackedAlloc(exception, self);
+}
+
+/*
+ * Throw the named exception using the dotted form of the class
+ * descriptor as the exception message, and with the specified cause.
+ */
+void dvmThrowChainedExceptionWithClassMessage(const char* exceptionDescriptor,
+ const char* messageDescriptor, Object* cause)
+{
+ char* message = dvmDescriptorToDot(messageDescriptor);
+
+ dvmThrowChainedException(exceptionDescriptor, message, cause);
+ free(message);
+}
+
+/*
+ * Like dvmThrowExceptionWithMessageFromDescriptor, but take a
+ * class object instead of a name.
+ */
+void dvmThrowExceptionByClassWithClassMessage(ClassObject* exceptionClass,
+ const char* messageDescriptor)
+{
+ char* message = dvmDescriptorToName(messageDescriptor);
+
+ dvmThrowExceptionByClass(exceptionClass, message);
+ free(message);
+}
+
+/*
+ * Find and return an exception constructor method that can take the
+ * indicated parameters, or return NULL if no such constructor exists.
+ */
+static Method* findExceptionInitMethod(ClassObject* excepClass,
+ bool hasMessage, bool hasCause)
+{
+ if (hasMessage) {
+ Method* result;
+
+ if (hasCause) {
+ result = dvmFindDirectMethodByDescriptor(
+ excepClass, "<init>",
+ "(Ljava/lang/String;Ljava/lang/Throwable;)V");
+ } else {
+ result = dvmFindDirectMethodByDescriptor(
+ excepClass, "<init>", "(Ljava/lang/String;)V");
+ }
+
+ if (result != NULL) {
+ return result;
+ }
+
+ if (hasCause) {
+ return dvmFindDirectMethodByDescriptor(
+ excepClass, "<init>",
+ "(Ljava/lang/Object;Ljava/lang/Throwable;)V");
+ } else {
+ return dvmFindDirectMethodByDescriptor(
+ excepClass, "<init>", "(Ljava/lang/Object;)V");
+ }
+ } else if (hasCause) {
+ return dvmFindDirectMethodByDescriptor(
+ excepClass, "<init>", "(Ljava/lang/Throwable;)V");
+ } else {
+ return dvmFindDirectMethodByDescriptor(excepClass, "<init>", "()V");
+ }
+}
+
+/*
+ * Initialize an exception with an appropriate constructor.
+ *
+ * "exception" is the exception object to initialize.
+ * Either or both of "msg" and "cause" may be null.
+ * "self" is dvmThreadSelf(), passed in so we don't have to look it up again.
+ *
+ * If the process of initializing the exception causes another
+ * exception (e.g., OutOfMemoryError) to be thrown, return an error
+ * and leave self->exception intact.
+ */
+static bool initException(Object* exception, const char* msg, Object* cause,
+ Thread* self)
+{
+ enum {
+ kInitUnknown,
+ kInitNoarg,
+ kInitMsg,
+ kInitMsgThrow,
+ kInitThrow
+ } initKind = kInitUnknown;
+ Method* initMethod = NULL;
+ ClassObject* excepClass = exception->clazz;
+ StringObject* msgStr = NULL;
+ bool result = false;
+ bool needInitCause = false;
+
+ assert(self != NULL);
+ assert(self->exception == NULL);
+
+ /* if we have a message, create a String */
+ if (msg == NULL)
+ msgStr = NULL;
+ else {
+ msgStr = dvmCreateStringFromCstr(msg);
+ if (msgStr == NULL) {
+ LOGW("Could not allocate message string \"%s\" while "
+ "throwing internal exception (%s)\n",
+ msg, excepClass->descriptor);
+ goto bail;
+ }
+ }
+
+ if (cause != NULL) {
+ if (!dvmInstanceof(cause->clazz, gDvm.classJavaLangThrowable)) {
+ LOGE("Tried to init exception with cause '%s'\n",
+ cause->clazz->descriptor);
+ dvmAbort();
+ }
+ }
+
+ /*
+ * The Throwable class has four public constructors:
+ * (1) Throwable()
+ * (2) Throwable(String message)
+ * (3) Throwable(String message, Throwable cause) (added in 1.4)
+ * (4) Throwable(Throwable cause) (added in 1.4)
+ *
+ * The first two are part of the original design, and most exception
+ * classes should support them. The third prototype was used by
+ * individual exceptions. e.g. ClassNotFoundException added it in 1.2.
+ * The general "cause" mechanism was added in 1.4. Some classes,
+ * such as IllegalArgumentException, initially supported the first
+ * two, but added the second two in a later release.
+ *
+ * Exceptions may be picky about how their "cause" field is initialized.
+ * If you call ClassNotFoundException(String), it may choose to
+ * initialize its "cause" field to null. Doing so prevents future
+ * calls to Throwable.initCause().
+ *
+ * So, if "cause" is not NULL, we need to look for a constructor that
+ * takes a throwable. If we can't find one, we fall back on calling
+ * #1/#2 and making a separate call to initCause(). Passing a null ref
+ * for "message" into Throwable(String, Throwable) is allowed, but we
+ * prefer to use the Throwable-only version because it has different
+ * behavior.
+ *
+ * java.lang.TypeNotPresentException is a strange case -- it has #3 but
+ * not #2. (Some might argue that the constructor is actually not #3,
+ * because it doesn't take the message string as an argument, but it
+ * has the same effect and we can work with it here.)
+ *
+ * java.lang.AssertionError is also a strange case -- it has a
+ * constructor that takes an Object, but not one that takes a String.
+ * There may be other cases like this, as well, so we generally look
+ * for an Object-taking constructor if we can't find one that takes
+ * a String.
+ */
+ if (cause == NULL) {
+ if (msgStr == NULL) {
+ initMethod = findExceptionInitMethod(excepClass, false, false);
+ initKind = kInitNoarg;
+ } else {
+ initMethod = findExceptionInitMethod(excepClass, true, false);
+ if (initMethod != NULL) {
+ initKind = kInitMsg;
+ } else {
+ /* no #2, try #3 */
+ initMethod = findExceptionInitMethod(excepClass, true, true);
+ if (initMethod != NULL) {
+ initKind = kInitMsgThrow;
+ }
+ }
+ }
+ } else {
+ if (msgStr == NULL) {
+ initMethod = findExceptionInitMethod(excepClass, false, true);
+ if (initMethod != NULL) {
+ initKind = kInitThrow;
+ } else {
+ initMethod = findExceptionInitMethod(excepClass, false, false);
+ initKind = kInitNoarg;
+ needInitCause = true;
+ }
+ } else {
+ initMethod = findExceptionInitMethod(excepClass, true, true);
+ if (initMethod != NULL) {
+ initKind = kInitMsgThrow;
+ } else {
+ initMethod = findExceptionInitMethod(excepClass, true, false);
+ initKind = kInitMsg;
+ needInitCause = true;
+ }
+ }
+ }
+
+ if (initMethod == NULL) {
+ /*
+ * We can't find the desired constructor. This can happen if a
+ * subclass of java/lang/Throwable doesn't define an expected
+ * constructor, e.g. it doesn't provide one that takes a string
+ * when a message has been provided.
+ */
+ LOGW("WARNING: exception class '%s' missing constructor "
+ "(msg='%s' kind=%d)\n",
+ excepClass->descriptor, msg, initKind);
+ assert(strcmp(excepClass->descriptor,
+ "Ljava/lang/RuntimeException;") != 0);
+ dvmThrowChainedException("Ljava/lang/RuntimeException;",
+ "re-throw on exception class missing constructor", NULL);
+ goto bail;
+ }
+
+ /*
+ * Call the constructor with the appropriate arguments.
+ */
+ JValue unused;
+ switch (initKind) {
+ case kInitNoarg:
+ LOGVV("+++ exc noarg (ic=%d)\n", needInitCause);
+ dvmCallMethod(self, initMethod, exception, &unused);
+ break;
+ case kInitMsg:
+ LOGVV("+++ exc msg (ic=%d)\n", needInitCause);
+ dvmCallMethod(self, initMethod, exception, &unused, msgStr);
+ break;
+ case kInitThrow:
+ LOGVV("+++ exc throw");
+ assert(!needInitCause);
+ dvmCallMethod(self, initMethod, exception, &unused, cause);
+ break;
+ case kInitMsgThrow:
+ LOGVV("+++ exc msg+throw");
+ assert(!needInitCause);
+ dvmCallMethod(self, initMethod, exception, &unused, msgStr, cause);
+ break;
+ default:
+ assert(false);
+ goto bail;
+ }
+
+ /*
+ * It's possible the constructor has thrown an exception. If so, we
+ * return an error and let our caller deal with it.
+ */
+ if (self->exception != NULL) {
+ LOGW("Exception thrown (%s) while throwing internal exception (%s)\n",
+ self->exception->clazz->descriptor, exception->clazz->descriptor);
+ goto bail;
+ }
+
+ /*
+ * If this exception was caused by another exception, and we weren't
+ * able to find a cause-setting constructor, set the "cause" field
+ * with an explicit call.
+ */
+ if (needInitCause) {
+ Method* initCause;
+ initCause = dvmFindVirtualMethodHierByDescriptor(excepClass, "initCause",
+ "(Ljava/lang/Throwable;)Ljava/lang/Throwable;");
+ if (initCause != NULL) {
+ dvmCallMethod(self, initCause, exception, &unused, cause);
+ if (self->exception != NULL) {
+ /* initCause() threw an exception; return an error and
+ * let the caller deal with it.
+ */
+ LOGW("Exception thrown (%s) during initCause() "
+ "of internal exception (%s)\n",
+ self->exception->clazz->descriptor,
+ exception->clazz->descriptor);
+ goto bail;
+ }
+ } else {
+ LOGW("WARNING: couldn't find initCause in '%s'\n",
+ excepClass->descriptor);
+ }
+ }
+
+
+ result = true;
+
+bail:
+ dvmReleaseTrackedAlloc((Object*) msgStr, self); // NULL is ok
+ return result;
+}
+
+
+/*
+ * Clear the pending exception and the "initExceptionCount" counter. This
+ * is used by the optimization and verification code, which has to run with
+ * "initializing" set to avoid going into a death-spin if the "class not
+ * found" exception can't be found.
+ *
+ * This can also be called when the VM is in a "normal" state, e.g. when
+ * verifying classes that couldn't be verified at optimization time. The
+ * reset of initExceptionCount should be harmless in that case.
+ */
+void dvmClearOptException(Thread* self)
+{
+ self->exception = NULL;
+ gDvm.initExceptionCount = 0;
+}
+
+/*
+ * Returns "true" if this is a "checked" exception, i.e. it's a subclass
+ * of Throwable (assumed) but not a subclass of RuntimeException or Error.
+ */
+bool dvmIsCheckedException(const Object* exception)
+{
+ if (dvmInstanceof(exception->clazz, gDvm.classJavaLangError) ||
+ dvmInstanceof(exception->clazz, gDvm.classJavaLangRuntimeException))
+ {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+/*
+ * Wrap the now-pending exception in a different exception. This is useful
+ * for reflection stuff that wants to hand a checked exception back from a
+ * method that doesn't declare it.
+ *
+ * If something fails, an (unchecked) exception related to that failure
+ * will be pending instead.
+ */
+void dvmWrapException(const char* newExcepStr)
+{
+ Thread* self = dvmThreadSelf();
+ Object* origExcep;
+ ClassObject* iteClass;
+
+ origExcep = dvmGetException(self);
+ dvmAddTrackedAlloc(origExcep, self); // don't let the GC free it
+
+ dvmClearException(self); // clear before class lookup
+ iteClass = dvmFindSystemClass(newExcepStr);
+ if (iteClass != NULL) {
+ Object* iteExcep;
+ Method* initMethod;
+
+ iteExcep = dvmAllocObject(iteClass, ALLOC_DEFAULT);
+ if (iteExcep != NULL) {
+ initMethod = dvmFindDirectMethodByDescriptor(iteClass, "<init>",
+ "(Ljava/lang/Throwable;)V");
+ if (initMethod != NULL) {
+ JValue unused;
+ dvmCallMethod(self, initMethod, iteExcep, &unused,
+ origExcep);
+
+ /* if <init> succeeded, replace the old exception */
+ if (!dvmCheckException(self))
+ dvmSetException(self, iteExcep);
+ }
+ dvmReleaseTrackedAlloc(iteExcep, NULL);
+
+ /* if initMethod doesn't exist, or failed... */
+ if (!dvmCheckException(self))
+ dvmSetException(self, origExcep);
+ } else {
+ /* leave OutOfMemoryError pending */
+ }
+ } else {
+ /* leave ClassNotFoundException pending */
+ }
+
+ assert(dvmCheckException(self));
+ dvmReleaseTrackedAlloc(origExcep, self);
+}
+
+/*
+ * Get the "cause" field from an exception.
+ *
+ * The Throwable class initializes the "cause" field to "this" to
+ * differentiate between being initialized to null and never being
+ * initialized. We check for that here and convert it to NULL.
+ */
+Object* dvmGetExceptionCause(const Object* exception)
+{
+ if (!dvmInstanceof(exception->clazz, gDvm.classJavaLangThrowable)) {
+ LOGE("Tried to get cause from object of type '%s'\n",
+ exception->clazz->descriptor);
+ dvmAbort();
+ }
+ Object* cause =
+ dvmGetFieldObject(exception, gDvm.offJavaLangThrowable_cause);
+ if (cause == exception)
+ return NULL;
+ else
+ return cause;
+}
+
+/*
+ * Print the stack trace of the current exception on stderr. This is called
+ * from the JNI ExceptionDescribe call.
+ *
+ * For consistency we just invoke the Throwable printStackTrace method,
+ * which might be overridden in the exception object.
+ *
+ * Exceptions thrown during the course of printing the stack trace are
+ * ignored.
+ */
+void dvmPrintExceptionStackTrace(void)
+{
+ Thread* self = dvmThreadSelf();
+ Object* exception;
+ Method* printMethod;
+
+ exception = self->exception;
+ if (exception == NULL)
+ return;
+
+ self->exception = NULL;
+ printMethod = dvmFindVirtualMethodHierByDescriptor(exception->clazz,
+ "printStackTrace", "()V");
+ if (printMethod != NULL) {
+ JValue unused;
+ dvmCallMethod(self, printMethod, exception, &unused);
+ } else {
+ LOGW("WARNING: could not find printStackTrace in %s\n",
+ exception->clazz->descriptor);
+ }
+
+ if (self->exception != NULL) {
+ LOGW("NOTE: exception thrown while printing stack trace: %s\n",
+ self->exception->clazz->descriptor);
+ }
+
+ self->exception = exception;
+}
+
+/*
+ * Search the method's list of exceptions for a match.
+ *
+ * Returns the offset of the catch block on success, or -1 on failure.
+ */
+static int findCatchInMethod(Thread* self, const Method* method, int relPc,
+ ClassObject* excepClass)
+{
+ /*
+ * Need to clear the exception before entry. Otherwise, dvmResolveClass
+ * might think somebody threw an exception while it was loading a class.
+ */
+ assert(!dvmCheckException(self));
+ assert(!dvmIsNativeMethod(method));
+
+ LOGVV("findCatchInMethod %s.%s excep=%s depth=%d\n",
+ method->clazz->descriptor, method->name, excepClass->descriptor,
+ dvmComputeExactFrameDepth(self->curFrame));
+
+ DvmDex* pDvmDex = method->clazz->pDvmDex;
+ const DexCode* pCode = dvmGetMethodCode(method);
+ DexCatchIterator iterator;
+
+ if (dexFindCatchHandler(&iterator, pCode, relPc)) {
+ for (;;) {
+ DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
+
+ if (handler == NULL) {
+ break;
+ }
+
+ if (handler->typeIdx == kDexNoIndex) {
+ /* catch-all */
+ LOGV("Match on catch-all block at 0x%02x in %s.%s for %s\n",
+ relPc, method->clazz->descriptor,
+ method->name, excepClass->descriptor);
+ return handler->address;
+ }
+
+ ClassObject* throwable =
+ dvmDexGetResolvedClass(pDvmDex, handler->typeIdx);
+ if (throwable == NULL) {
+ /*
+ * TODO: this behaves badly if we run off the stack
+ * while trying to throw an exception. The problem is
+ * that, if we're in a class loaded by a class loader,
+ * the call to dvmResolveClass has to ask the class
+ * loader for help resolving any previously-unresolved
+ * classes. If this particular class loader hasn't
+ * resolved StackOverflowError, it will call into
+ * interpreted code, and blow up.
+ *
+ * We currently replace the previous exception with
+ * the StackOverflowError, which means they won't be
+ * catching it *unless* they explicitly catch
+ * StackOverflowError, in which case we'll be unable
+ * to resolve the class referred to by the "catch"
+ * block.
+ *
+ * We end up getting a huge pile of warnings if we do
+ * a simple synthetic test, because this method gets
+ * called on every stack frame up the tree, and it
+ * fails every time.
+ *
+ * This eventually bails out, effectively becoming an
+ * uncatchable exception, so other than the flurry of
+ * warnings it's not really a problem. Still, we could
+ * probably handle this better.
+ */
+ throwable = dvmResolveClass(method->clazz, handler->typeIdx,
+ true);
+ if (throwable == NULL) {
+ /*
+ * We couldn't find the exception they wanted in
+ * our class files (or, perhaps, the stack blew up
+ * while we were querying a class loader). Cough
+ * up a warning, then move on to the next entry.
+ * Keep the exception status clear.
+ */
+ LOGW("Could not resolve class ref'ed in exception "
+ "catch list (class index %d, exception %s)\n",
+ handler->typeIdx,
+ (self->exception != NULL) ?
+ self->exception->clazz->descriptor : "(none)");
+ dvmClearException(self);
+ continue;
+ }
+ }
+
+ //LOGD("ADDR MATCH, check %s instanceof %s\n",
+ // excepClass->descriptor, pEntry->excepClass->descriptor);
+
+ if (dvmInstanceof(excepClass, throwable)) {
+ LOGV("Match on catch block at 0x%02x in %s.%s for %s\n",
+ relPc, method->clazz->descriptor,
+ method->name, excepClass->descriptor);
+ return handler->address;
+ }
+ }
+ }
+
+ LOGV("No matching catch block at 0x%02x in %s for %s\n",
+ relPc, method->name, excepClass->descriptor);
+ return -1;
+}
+
+/*
+ * Find a matching "catch" block. "pc" is the relative PC within the
+ * current method, indicating the offset from the start in 16-bit units.
+ *
+ * Returns the offset to the catch block, or -1 if we run up against a
+ * break frame without finding anything.
+ *
+ * The class resolution stuff we have to do while evaluating the "catch"
+ * blocks could cause an exception. The caller should clear the exception
+ * before calling here and restore it after.
+ *
+ * Sets *newFrame to the frame pointer of the frame with the catch block.
+ * If "scanOnly" is false, self->curFrame is also set to this value.
+ */
+int dvmFindCatchBlock(Thread* self, int relPc, Object* exception,
+ bool scanOnly, void** newFrame)
+{
+ void* fp = self->curFrame;
+ int catchAddr = -1;
+
+ assert(!dvmCheckException(self));
+
+ while (true) {
+ StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+ catchAddr = findCatchInMethod(self, saveArea->method, relPc,
+ exception->clazz);
+ if (catchAddr >= 0)
+ break;
+
+ /*
+ * Normally we'd check for ACC_SYNCHRONIZED methods and unlock
+ * them as we unroll. Dalvik uses what amount to generated
+ * "finally" blocks to take care of this for us.
+ */
+
+ /* output method profiling info */
+ if (!scanOnly) {
+ TRACE_METHOD_UNROLL(self, saveArea->method);
+ }
+
+ /*
+ * Move up one frame. If the next thing up is a break frame,
+ * break out now so we're left unrolled to the last method frame.
+ * We need to point there so we can roll up the JNI local refs
+ * if this was a native method.
+ */
+ assert(saveArea->prevFrame != NULL);
+ if (dvmIsBreakFrame(saveArea->prevFrame)) {
+ if (!scanOnly)
+ break; // bail with catchAddr == -1
+
+ /*
+ * We're scanning for the debugger. It needs to know if this
+ * exception is going to be caught or not, and we need to figure
+ * out if it will be caught *ever* not just between the current
+ * position and the next break frame. We can't tell what native
+ * code is going to do, so we assume it never catches exceptions.
+ *
+ * Start by finding an interpreted code frame.
+ */
+ fp = saveArea->prevFrame; // this is the break frame
+ saveArea = SAVEAREA_FROM_FP(fp);
+ fp = saveArea->prevFrame; // this may be a good one
+ while (fp != NULL) {
+ if (!dvmIsBreakFrame(fp)) {
+ saveArea = SAVEAREA_FROM_FP(fp);
+ if (!dvmIsNativeMethod(saveArea->method))
+ break;
+ }
+
+ fp = SAVEAREA_FROM_FP(fp)->prevFrame;
+ }
+ if (fp == NULL)
+ break; // bail with catchAddr == -1
+
+ /*
+ * Now fp points to the "good" frame. When the interp code
+ * invoked the native code, it saved a copy of its current PC
+ * into xtra.currentPc. Pull it out of there.
+ */
+ relPc =
+ saveArea->xtra.currentPc - SAVEAREA_FROM_FP(fp)->method->insns;
+ } else {
+ fp = saveArea->prevFrame;
+
+ /* savedPc in was-current frame goes with method in now-current */
+ relPc = saveArea->savedPc - SAVEAREA_FROM_FP(fp)->method->insns;
+ }
+ }
+
+ if (!scanOnly)
+ self->curFrame = fp;
+
+ /*
+ * The class resolution in findCatchInMethod() could cause an exception.
+ * Clear it to be safe.
+ */
+ self->exception = NULL;
+
+ *newFrame = fp;
+ return catchAddr;
+}
+
+/*
+ * We have to carry the exception's stack trace around, but in many cases
+ * it will never be examined. It makes sense to keep it in a compact,
+ * VM-specific object, rather than an array of Objects with strings.
+ *
+ * Pass in the thread whose stack we're interested in. If "thread" is
+ * not self, the thread must be suspended. This implies that the thread
+ * list lock is held, which means we can't allocate objects or we risk
+ * jamming the GC. So, we allow this function to return different formats.
+ * (This shouldn't be called directly -- see the inline functions in the
+ * header file.)
+ *
+ * If "wantObject" is true, this returns a newly-allocated Object, which is
+ * presently an array of integers, but could become something else in the
+ * future. If "wantObject" is false, return plain malloc data.
+ *
+ * NOTE: if we support class unloading, we will need to scan the class
+ * object references out of these arrays.
+ */
+void* dvmFillInStackTraceInternal(Thread* thread, bool wantObject, int* pCount)
+{
+ ArrayObject* stackData = NULL;
+ int* simpleData = NULL;
+ void* fp;
+ void* startFp;
+ int stackDepth;
+ int* intPtr;
+
+ if (pCount != NULL)
+ *pCount = 0;
+ fp = thread->curFrame;
+
+ assert(thread == dvmThreadSelf() || dvmIsSuspended(thread));
+
+ /*
+ * We're looking at a stack frame for code running below a Throwable
+ * constructor. We want to remove the Throwable methods and the
+ * superclass initializations so the user doesn't see them when they
+ * read the stack dump.
+ *
+ * TODO: this just scrapes off the top layers of Throwable. Might not do
+ * the right thing if we create an exception object or cause a VM
+ * exception while in a Throwable method.
+ */
+ while (fp != NULL) {
+ const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+ const Method* method = saveArea->method;
+
+ if (dvmIsBreakFrame(fp))
+ break;
+ if (!dvmInstanceof(method->clazz, gDvm.classJavaLangThrowable))
+ break;
+ //LOGD("EXCEP: ignoring %s.%s\n",
+ // method->clazz->descriptor, method->name);
+ fp = saveArea->prevFrame;
+ }
+ startFp = fp;
+
+ /*
+ * Compute the stack depth.
+ */
+ stackDepth = 0;
+ while (fp != NULL) {
+ const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+
+ if (!dvmIsBreakFrame(fp))
+ stackDepth++;
+
+ assert(fp != saveArea->prevFrame);
+ fp = saveArea->prevFrame;
+ }
+ //LOGD("EXCEP: stack depth is %d\n", stackDepth);
+
+ if (!stackDepth)
+ goto bail;
+
+ /*
+ * We need to store a pointer to the Method and the program counter.
+ * We have 4-byte pointers, so we use '[I'.
+ */
+ if (wantObject) {
+ assert(sizeof(Method*) == 4);
+ stackData = dvmAllocPrimitiveArray('I', stackDepth*2, ALLOC_DEFAULT);
+ if (stackData == NULL) {
+ assert(dvmCheckException(dvmThreadSelf()));
+ goto bail;
+ }
+ intPtr = (int*) stackData->contents;
+ } else {
+ /* array of ints; first entry is stack depth */
+ assert(sizeof(Method*) == sizeof(int));
+ simpleData = (int*) malloc(sizeof(int) * stackDepth*2);
+ if (simpleData == NULL)
+ goto bail;
+
+ assert(pCount != NULL);
+ intPtr = simpleData;
+ }
+ if (pCount != NULL)
+ *pCount = stackDepth;
+
+ fp = startFp;
+ while (fp != NULL) {
+ const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+ const Method* method = saveArea->method;
+
+ if (!dvmIsBreakFrame(fp)) {
+ //LOGD("EXCEP keeping %s.%s\n", method->clazz->descriptor,
+ // method->name);
+
+ *intPtr++ = (int) method;
+ if (dvmIsNativeMethod(method)) {
+ *intPtr++ = 0; /* no saved PC for native methods */
+ } else {
+ assert(saveArea->xtra.currentPc >= method->insns &&
+ saveArea->xtra.currentPc <
+ method->insns + dvmGetMethodInsnsSize(method));
+ *intPtr++ = (int) (saveArea->xtra.currentPc - method->insns);
+ }
+
+ stackDepth--; // for verification
+ }
+
+ assert(fp != saveArea->prevFrame);
+ fp = saveArea->prevFrame;
+ }
+ assert(stackDepth == 0);
+
+bail:
+ if (wantObject) {
+ dvmReleaseTrackedAlloc((Object*) stackData, dvmThreadSelf());
+ return stackData;
+ } else {
+ return simpleData;
+ }
+}
+
+
+/*
+ * Given an Object previously created by dvmFillInStackTrace(), use the
+ * contents of the saved stack trace to generate an array of
+ * java/lang/StackTraceElement objects.
+ *
+ * The returned array is not added to the "local refs" list.
+ */
+ArrayObject* dvmGetStackTrace(const Object* ostackData)
+{
+ const ArrayObject* stackData = (const ArrayObject*) ostackData;
+ const int* intVals;
+ int stackSize;
+
+ stackSize = stackData->length / 2;
+ intVals = (const int*) stackData->contents;
+ return dvmGetStackTraceRaw(intVals, stackSize);
+}
+
+/*
+ * Generate an array of StackTraceElement objects from the raw integer
+ * data encoded by dvmFillInStackTrace().
+ *
+ * "intVals" points to the first {method,pc} pair.
+ *
+ * The returned array is not added to the "local refs" list.
+ */
+ArrayObject* dvmGetStackTraceRaw(const int* intVals, int stackDepth)
+{
+ ArrayObject* steArray = NULL;
+ int i;
+
+ /* init this if we haven't yet */
+ if (!dvmIsClassInitialized(gDvm.classJavaLangStackTraceElement))
+ dvmInitClass(gDvm.classJavaLangStackTraceElement);
+
+ /* allocate a StackTraceElement array */
+ steArray = dvmAllocArray(gDvm.classJavaLangStackTraceElementArray,
+ stackDepth, kObjectArrayRefWidth, ALLOC_DEFAULT);
+ if (steArray == NULL)
+ goto bail;
+
+ /*
+ * Allocate and initialize a StackTraceElement for each stack frame.
+ * We use the standard constructor to configure the object.
+ */
+ for (i = 0; i < stackDepth; i++) {
+ Object* ste;
+ Method* meth;
+ StringObject* className;
+ StringObject* methodName;
+ StringObject* fileName;
+ int lineNumber, pc;
+ const char* sourceFile;
+ char* dotName;
+
+ ste = dvmAllocObject(gDvm.classJavaLangStackTraceElement,ALLOC_DEFAULT);
+ if (ste == NULL)
+ goto bail;
+
+ meth = (Method*) *intVals++;
+ pc = *intVals++;
+
+ if (pc == -1) // broken top frame?
+ lineNumber = 0;
+ else
+ lineNumber = dvmLineNumFromPC(meth, pc);
+
+ dotName = dvmDescriptorToDot(meth->clazz->descriptor);
+ className = dvmCreateStringFromCstr(dotName);
+ free(dotName);
+
+ methodName = dvmCreateStringFromCstr(meth->name);
+ sourceFile = dvmGetMethodSourceFile(meth);
+ if (sourceFile != NULL)
+ fileName = dvmCreateStringFromCstr(sourceFile);
+ else
+ fileName = NULL;
+
+ /*
+ * Invoke:
+ * public StackTraceElement(String declaringClass, String methodName,
+ * String fileName, int lineNumber)
+ * (where lineNumber==-2 means "native")
+ */
+ JValue unused;
+ dvmCallMethod(dvmThreadSelf(), gDvm.methJavaLangStackTraceElement_init,
+ ste, &unused, className, methodName, fileName, lineNumber);
+
+ dvmReleaseTrackedAlloc(ste, NULL);
+ dvmReleaseTrackedAlloc((Object*) className, NULL);
+ dvmReleaseTrackedAlloc((Object*) methodName, NULL);
+ dvmReleaseTrackedAlloc((Object*) fileName, NULL);
+
+ if (dvmCheckException(dvmThreadSelf()))
+ goto bail;
+
+ dvmSetObjectArrayElement(steArray, i, ste);
+ }
+
+bail:
+ dvmReleaseTrackedAlloc((Object*) steArray, NULL);
+ return steArray;
+}
+
+/*
+ * Dump the contents of a raw stack trace to the log.
+ */
+void dvmLogRawStackTrace(const int* intVals, int stackDepth)
+{
+ int i;
+
+ /*
+ * Run through the array of stack frame data.
+ */
+ for (i = 0; i < stackDepth; i++) {
+ Method* meth;
+ int lineNumber, pc;
+ const char* sourceFile;
+ char* dotName;
+
+ meth = (Method*) *intVals++;
+ pc = *intVals++;
+
+ if (pc == -1) // broken top frame?
+ lineNumber = 0;
+ else
+ lineNumber = dvmLineNumFromPC(meth, pc);
+
+ // probably don't need to do this, but it looks nicer
+ dotName = dvmDescriptorToDot(meth->clazz->descriptor);
+
+ if (dvmIsNativeMethod(meth)) {
+ LOGI("\tat %s.%s(Native Method)\n", dotName, meth->name);
+ } else {
+ LOGI("\tat %s.%s(%s:%d)\n",
+ dotName, meth->name, dvmGetMethodSourceFile(meth),
+ dvmLineNumFromPC(meth, pc));
+ }
+
+ free(dotName);
+
+ sourceFile = dvmGetMethodSourceFile(meth);
+ }
+}
+
+/*
+ * Print the direct stack trace of the given exception to the log.
+ */
+static void logStackTraceOf(Object* exception)
+{
+ const ArrayObject* stackData;
+ StringObject* messageStr;
+ int stackSize;
+ const int* intVals;
+
+ messageStr = (StringObject*) dvmGetFieldObject(exception,
+ gDvm.offJavaLangThrowable_message);
+ if (messageStr != NULL) {
+ char* cp = dvmCreateCstrFromString(messageStr);
+ LOGI("%s: %s\n", exception->clazz->descriptor, cp);
+ free(cp);
+ } else {
+ LOGI("%s:\n", exception->clazz->descriptor);
+ }
+
+ stackData = (const ArrayObject*) dvmGetFieldObject(exception,
+ gDvm.offJavaLangThrowable_stackState);
+ if (stackData == NULL) {
+ LOGI(" (no stack trace data found)\n");
+ return;
+ }
+
+ stackSize = stackData->length / 2;
+ intVals = (const int*) stackData->contents;
+
+ dvmLogRawStackTrace(intVals, stackSize);
+}
+
+/*
+ * Print the stack trace of the current thread's exception, as well as
+ * the stack traces of any chained exceptions, to the log. We extract
+ * the stored stack trace and process it internally instead of calling
+ * interpreted code.
+ */
+void dvmLogExceptionStackTrace(void)
+{
+ Object* exception = dvmThreadSelf()->exception;
+ Object* cause;
+
+ if (exception == NULL) {
+ LOGW("tried to log a null exception?\n");
+ return;
+ }
+
+ for (;;) {
+ logStackTraceOf(exception);
+ cause = dvmGetExceptionCause(exception);
+ if (cause == NULL) {
+ break;
+ }
+ LOGI("Caused by:\n");
+ exception = cause;
+ }
+}
diff --git a/vm/Exception.h b/vm/Exception.h
new file mode 100644
index 0000000..b812f73
--- /dev/null
+++ b/vm/Exception.h
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Exception handling.
+ */
+#ifndef _DALVIK_EXCEPTION
+#define _DALVIK_EXCEPTION
+
+/* initialization */
+bool dvmExceptionStartup(void);
+void dvmExceptionShutdown(void);
+
+/*
+ * Throw an exception in the current thread, by class descriptor.
+ */
+void dvmThrowChainedException(const char* exceptionDescriptor, const char* msg,
+ Object* cause);
+INLINE void dvmThrowException(const char* exceptionDescriptor,
+ const char* msg)
+{
+ dvmThrowChainedException(exceptionDescriptor, msg, NULL);
+}
+
+/*
+ * Like dvmThrowChainedException, but takes printf-style args for the message.
+ */
+void dvmThrowExceptionFmtV(const char* exceptionDescriptor, const char* fmt,
+ va_list args);
+void dvmThrowExceptionFmt(const char* exceptionDescriptor, const char* fmt, ...)
+#if defined(__GNUC__)
+ __attribute__ ((format(printf, 2, 3)))
+#endif
+ ;
+INLINE void dvmThrowExceptionFmt(const char* exceptionDescriptor,
+ const char* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ dvmThrowExceptionFmtV(exceptionDescriptor, fmt, args);
+ va_end(args);
+}
+
+/*
+ * Throw an exception in the current thread, by class object.
+ */
+void dvmThrowChainedExceptionByClass(ClassObject* exceptionClass,
+ const char* msg, Object* cause);
+INLINE void dvmThrowExceptionByClass(ClassObject* exceptionClass,
+ const char* msg)
+{
+ dvmThrowChainedExceptionByClass(exceptionClass, msg, NULL);
+}
+
+/*
+ * Throw the named exception using the name of a class as the exception
+ * message.
+ */
+void dvmThrowChainedExceptionWithClassMessage(const char* exceptionDescriptor,
+ const char* messageDescriptor, Object* cause);
+INLINE void dvmThrowExceptionWithClassMessage(const char* exceptionDescriptor,
+ const char* messageDescriptor)
+{
+ dvmThrowChainedExceptionWithClassMessage(exceptionDescriptor,
+ messageDescriptor, NULL);
+}
+
+/*
+ * Like dvmThrowExceptionWithMessageFromDescriptor, but take a
+ * class object instead of a name.
+ */
+void dvmThrowExceptionByClassWithClassMessage(ClassObject* exceptionClass,
+ const char* messageDescriptor);
+
+/*
+ * Return the exception being thrown in the current thread, or NULL if
+ * no exception is pending.
+ */
+INLINE Object* dvmGetException(Thread* self) {
+ return self->exception;
+}
+
+/*
+ * Set the exception being thrown in the current thread.
+ */
+INLINE void dvmSetException(Thread* self, Object* exception)
+{
+ assert(exception != NULL);
+ self->exception = exception;
+}
+
+/*
+ * Clear the pending exception.
+ *
+ * (We use this rather than "set(null)" because we may need to have special
+ * fixups here for StackOverflowError stuff. Calling "clear" in the code
+ * makes it obvious.)
+ */
+INLINE void dvmClearException(Thread* self) {
+ self->exception = NULL;
+}
+
+/*
+ * Clear the pending exception. Used by the optimization and verification
+ * code, which has to run with "initializing" set to avoid going into a
+ * death-spin if the "class not found" exception can't be found.
+ */
+void dvmClearOptException(Thread* self);
+
+/*
+ * Returns "true" if an exception is pending. Use this if you have a
+ * "self" pointer.
+ */
+INLINE bool dvmCheckException(Thread* self) {
+ return (self->exception != NULL);
+}
+
+/*
+ * Returns "true" if this is a "checked" exception, i.e. it's a subclass
+ * of Throwable (assumed) but not a subclass of RuntimeException or Error.
+ */
+bool dvmIsCheckedException(const Object* exception);
+
+/*
+ * Wrap the now-pending exception in a different exception.
+ *
+ * If something fails, an (unchecked) exception related to that failure
+ * will be pending instead.
+ */
+void dvmWrapException(const char* newExcepStr);
+
+/*
+ * Get the "cause" field from an exception.
+ *
+ * Returns NULL if the field is null or uninitialized.
+ */
+Object* dvmGetExceptionCause(const Object* exception);
+
+/*
+ * Print the exception stack trace on stderr. Calls the exception's
+ * print function.
+ */
+void dvmPrintExceptionStackTrace(void);
+
+/*
+ * Print the exception stack trace to the log file. The exception stack
+ * trace is computed within the VM.
+ */
+void dvmLogExceptionStackTrace(void);
+
+/*
+ * Search for a catch block that matches "exception".
+ *
+ * "*newFrame" gets a copy of the new frame pointer.
+ *
+ * If "doUnroll" is set, we unroll "thread"s stack as we go (and update
+ * self->curFrame with the same value as in *newFrame).
+ *
+ * Returns the offset to the catch code on success, or -1 if we couldn't
+ * find a catcher.
+ */
+int dvmFindCatchBlock(Thread* self, int relPc, Object* exception,
+ bool doUnroll, void** newFrame);
+
+/*
+ * Support for saving exception stack traces and converting them to
+ * usable form. Use the "FillIn" function to generate a compact array
+ * that represents the stack frames, then "GetStackTrace" to convert it
+ * to an array of StackTraceElement objects.
+ *
+ * Don't call the "Internal" form of the function directly.
+ */
+void* dvmFillInStackTraceInternal(Thread* thread, bool wantObject, int* pCount);
+/* return an [I for use by interpreted code */
+INLINE Object* dvmFillInStackTrace(Thread* thread) {
+ return (Object*) dvmFillInStackTraceInternal(thread, true, NULL);
+}
+ArrayObject* dvmGetStackTrace(const Object* stackState);
+/* return an int* and array count; caller must free() the return value */
+INLINE int* dvmFillInStackTraceRaw(Thread* thread, int* pCount) {
+ return (int*) dvmFillInStackTraceInternal(thread, false, pCount);
+}
+ArrayObject* dvmGetStackTraceRaw(const int* intVals, int stackDepth);
+
+/*
+ * Print a formatted version of a raw stack trace to the log file.
+ */
+void dvmLogRawStackTrace(const int* intVals, int stackDepth);
+
+#endif /*_DALVIK_EXCEPTION*/
diff --git a/vm/Globals.h b/vm/Globals.h
new file mode 100644
index 0000000..6ba6d5a
--- /dev/null
+++ b/vm/Globals.h
@@ -0,0 +1,877 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Variables with library scope.
+ *
+ * Prefer this over scattered static and global variables -- it's easier to
+ * view the state in a debugger, it makes clean shutdown simpler, we can
+ * trivially dump the state into a crash log, and it dodges most naming
+ * collisions that will arise when we are embedded in a larger program.
+ *
+ * If we want multiple VMs per process, this can get stuffed into TLS (or
+ * accessed through a Thread field). May need to pass it around for some
+ * of the early initialization functions.
+ */
+#ifndef _DALVIK_GLOBALS
+#define _DALVIK_GLOBALS
+
+#include <stdarg.h>
+#include <pthread.h>
+
+#define MAX_BREAKPOINTS 20 /* used for a debugger optimization */
+
+/* private structures */
+typedef struct GcHeap GcHeap;
+typedef struct BreakpointSet BreakpointSet;
+typedef struct InlineSub InlineSub;
+
+/*
+ * One of these for each -ea/-da/-esa/-dsa on the command line.
+ */
+typedef struct AssertionControl {
+ char* pkgOrClass; /* package/class string, or NULL for esa/dsa */
+ int pkgOrClassLen; /* string length, for quick compare */
+ bool enable; /* enable or disable */
+ bool isPackage; /* string ended with "..."? */
+} AssertionControl;
+
+/*
+ * Execution mode, e.g. interpreter vs. JIT.
+ */
+typedef enum ExecutionMode {
+ kExecutionModeUnknown = 0,
+ kExecutionModeInterpPortable,
+ kExecutionModeInterpFast,
+#if defined(WITH_JIT)
+ kExecutionModeJit,
+#endif
+} ExecutionMode;
+
+/*
+ * All fields are initialized to zero.
+ *
+ * Storage allocated here must be freed by a subsystem shutdown function or
+ * from within freeGlobals().
+ */
+struct DvmGlobals {
+ /*
+ * Some options from the command line or environment.
+ */
+ char* bootClassPathStr;
+ char* classPathStr;
+
+ unsigned int heapSizeStart;
+ unsigned int heapSizeMax;
+ unsigned int stackSize;
+
+ bool verboseGc;
+ bool verboseJni;
+ bool verboseClass;
+ bool verboseShutdown;
+
+ bool jdwpAllowed; // debugging allowed for this process?
+ bool jdwpConfigured; // has debugging info been provided?
+ int jdwpTransport;
+ bool jdwpServer;
+ char* jdwpHost;
+ int jdwpPort;
+ bool jdwpSuspend;
+
+ /* use wall clock as method profiler clock source? */
+ bool profilerWallClock;
+
+ /*
+ * Lock profiling threshold value in milliseconds. Acquires that
+ * exceed threshold are logged. Acquires within the threshold are
+ * logged with a probability of $\frac{time}{threshold}$ . If the
+ * threshold is unset no additional logging occurs.
+ */
+ u4 lockProfThreshold;
+
+ int (*vfprintfHook)(FILE*, const char*, va_list);
+ void (*exitHook)(int);
+ void (*abortHook)(void);
+
+ int jniGrefLimit; // 0 means no limit
+ char* jniTrace;
+ bool reduceSignals;
+ bool noQuitHandler;
+ bool verifyDexChecksum;
+ char* stackTraceFile; // for SIGQUIT-inspired output
+
+ bool logStdio;
+
+ DexOptimizerMode dexOptMode;
+ DexClassVerifyMode classVerifyMode;
+
+ bool dexOptForSmp;
+
+ /*
+ * GC option flags.
+ */
+ bool preciseGc;
+ bool preVerify;
+ bool postVerify;
+ bool generateRegisterMaps;
+ bool concurrentMarkSweep;
+ bool verifyCardTable;
+
+ int assertionCtrlCount;
+ AssertionControl* assertionCtrl;
+
+ ExecutionMode executionMode;
+
+ /*
+ * VM init management.
+ */
+ bool initializing;
+ int initExceptionCount;
+ bool optimizing;
+
+ /*
+ * java.lang.System properties set from the command line.
+ */
+ int numProps;
+ int maxProps;
+ char** propList;
+
+ /*
+ * Where the VM goes to find system classes.
+ */
+ ClassPathEntry* bootClassPath;
+ /* used by the DEX optimizer to load classes from an unfinished DEX */
+ DvmDex* bootClassPathOptExtra;
+ bool optimizingBootstrapClass;
+
+ /*
+ * Loaded classes, hashed by class name. Each entry is a ClassObject*,
+ * allocated in GC space.
+ */
+ HashTable* loadedClasses;
+
+ /*
+ * Value for the next class serial number to be assigned. This is
+ * incremented as we load classes. Failed loads and races may result
+ * in some numbers being skipped, and the serial number is not
+ * guaranteed to start at 1, so the current value should not be used
+ * as a count of loaded classes.
+ */
+ volatile int classSerialNumber;
+
+ /*
+ * Classes with a low classSerialNumber are probably in the zygote, and
+ * their InitiatingLoaderList is not used, to promote sharing. The list is
+ * kept here instead.
+ */
+ InitiatingLoaderList* initiatingLoaderList;
+
+ /*
+ * Interned strings.
+ */
+
+ /* A mutex that guards access to the interned string tables. */
+ pthread_mutex_t internLock;
+
+ /* Hash table of strings interned by the user. */
+ HashTable* internedStrings;
+
+ /* Hash table of strings interned by the class loader. */
+ HashTable* literalStrings;
+
+ /*
+ * Quick lookups for popular classes used internally.
+ */
+ ClassObject* classJavaLangClass;
+ ClassObject* classJavaLangClassArray;
+ ClassObject* classJavaLangError;
+ ClassObject* classJavaLangObject;
+ ClassObject* classJavaLangObjectArray;
+ ClassObject* classJavaLangRuntimeException;
+ ClassObject* classJavaLangString;
+ ClassObject* classJavaLangThread;
+ ClassObject* classJavaLangVMThread;
+ ClassObject* classJavaLangThreadGroup;
+ ClassObject* classJavaLangThrowable;
+ ClassObject* classJavaLangStackOverflowError;
+ ClassObject* classJavaLangStackTraceElement;
+ ClassObject* classJavaLangStackTraceElementArray;
+ ClassObject* classJavaLangAnnotationAnnotationArray;
+ ClassObject* classJavaLangAnnotationAnnotationArrayArray;
+ ClassObject* classJavaLangReflectAccessibleObject;
+ ClassObject* classJavaLangReflectConstructor;
+ ClassObject* classJavaLangReflectConstructorArray;
+ ClassObject* classJavaLangReflectField;
+ ClassObject* classJavaLangReflectFieldArray;
+ ClassObject* classJavaLangReflectMethod;
+ ClassObject* classJavaLangReflectMethodArray;
+ ClassObject* classJavaLangReflectProxy;
+ ClassObject* classJavaLangExceptionInInitializerError;
+ ClassObject* classJavaLangRefPhantomReference;
+ ClassObject* classJavaLangRefReference;
+ ClassObject* classJavaNioReadWriteDirectByteBuffer;
+ ClassObject* classJavaSecurityAccessController;
+ ClassObject* classOrgApacheHarmonyLangAnnotationAnnotationFactory;
+ ClassObject* classOrgApacheHarmonyLangAnnotationAnnotationMember;
+ ClassObject* classOrgApacheHarmonyLangAnnotationAnnotationMemberArray;
+ ClassObject* classOrgApacheHarmonyNioInternalDirectBuffer;
+ jclass jclassOrgApacheHarmonyNioInternalDirectBuffer;
+
+ /* synthetic classes for arrays of primitives */
+ ClassObject* classArrayBoolean;
+ ClassObject* classArrayChar;
+ ClassObject* classArrayFloat;
+ ClassObject* classArrayDouble;
+ ClassObject* classArrayByte;
+ ClassObject* classArrayShort;
+ ClassObject* classArrayInt;
+ ClassObject* classArrayLong;
+
+ /* method offsets - Object */
+ int voffJavaLangObject_equals;
+ int voffJavaLangObject_hashCode;
+ int voffJavaLangObject_toString;
+ int voffJavaLangObject_finalize;
+
+ /* field offsets - Class */
+ int offJavaLangClass_pd;
+
+ /* field offsets - String */
+ int javaLangStringReady; /* 0=not init, 1=ready, -1=initing */
+ int offJavaLangString_value;
+ int offJavaLangString_count;
+ int offJavaLangString_offset;
+ int offJavaLangString_hashCode;
+
+ /* field offsets - Thread */
+ int offJavaLangThread_vmThread;
+ int offJavaLangThread_group;
+ int offJavaLangThread_daemon;
+ int offJavaLangThread_name;
+ int offJavaLangThread_priority;
+
+ /* method offsets - Thread */
+ int voffJavaLangThread_run;
+
+ /* field offsets - VMThread */
+ int offJavaLangVMThread_thread;
+ int offJavaLangVMThread_vmData;
+
+ /* method offsets - ThreadGroup */
+ int voffJavaLangThreadGroup_removeThread;
+
+ /* field offsets - Throwable */
+ int offJavaLangThrowable_stackState;
+ int offJavaLangThrowable_message;
+ int offJavaLangThrowable_cause;
+
+ /* field offsets - java.lang.reflect.* */
+ int offJavaLangReflectAccessibleObject_flag;
+ int offJavaLangReflectConstructor_slot;
+ int offJavaLangReflectConstructor_declClass;
+ int offJavaLangReflectField_slot;
+ int offJavaLangReflectField_declClass;
+ int offJavaLangReflectMethod_slot;
+ int offJavaLangReflectMethod_declClass;
+
+ /* field offsets - java.lang.ref.Reference */
+ int offJavaLangRefReference_referent;
+ int offJavaLangRefReference_queue;
+ int offJavaLangRefReference_queueNext;
+ int offJavaLangRefReference_pendingNext;
+
+ /* method pointers - java.lang.ref.Reference */
+ Method* methJavaLangRefReference_enqueueInternal;
+
+ /* field offsets - java.nio.Buffer and java.nio.DirectByteBufferImpl */
+ //int offJavaNioBuffer_capacity;
+ //int offJavaNioDirectByteBufferImpl_pointer;
+
+ /* method pointers - java.security.AccessController */
+ volatile int javaSecurityAccessControllerReady;
+ Method* methJavaSecurityAccessController_doPrivileged[4];
+
+ /* constructor method pointers; no vtable involved, so use Method* */
+ Method* methJavaLangStackTraceElement_init;
+ Method* methJavaLangExceptionInInitializerError_init;
+ Method* methJavaLangRefPhantomReference_init;
+ Method* methJavaLangReflectConstructor_init;
+ Method* methJavaLangReflectField_init;
+ Method* methJavaLangReflectMethod_init;
+ Method* methOrgApacheHarmonyLangAnnotationAnnotationMember_init;
+
+ /* static method pointers - android.lang.annotation.* */
+ Method*
+ methOrgApacheHarmonyLangAnnotationAnnotationFactory_createAnnotation;
+
+ /* direct method pointers - java.lang.reflect.Proxy */
+ Method* methJavaLangReflectProxy_constructorPrototype;
+
+ /* field offsets - java.lang.reflect.Proxy */
+ int offJavaLangReflectProxy_h;
+
+ /* fake native entry point method */
+ Method* methFakeNativeEntry;
+
+ /* assorted direct buffer helpers */
+ Method* methJavaNioReadWriteDirectByteBuffer_init;
+ Method* methOrgApacheHarmonyLuniPlatformPlatformAddress_on;
+ Method* methOrgApacheHarmonyNioInternalDirectBuffer_getEffectiveAddress;
+ int offJavaNioBuffer_capacity;
+ int offJavaNioBuffer_effectiveDirectAddress;
+ int offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr;
+ int voffOrgApacheHarmonyLuniPlatformPlatformAddress_toLong;
+
+ /*
+ * VM-synthesized primitive classes, for arrays.
+ */
+ ClassObject* volatile primitiveClass[PRIM_MAX];
+
+ /*
+ * Thread list. This always has at least one element in it (main),
+ * and main is always the first entry.
+ *
+ * The threadListLock is used for several things, including the thread
+ * start condition variable. Generally speaking, you must hold the
+ * threadListLock when:
+ * - adding/removing items from the list
+ * - waiting on or signaling threadStartCond
+ * - examining the Thread struct for another thread (this is to avoid
+ * one thread freeing the Thread struct while another thread is
+ * perusing it)
+ */
+ Thread* threadList;
+ pthread_mutex_t threadListLock;
+
+ pthread_cond_t threadStartCond;
+
+ /*
+ * The thread code grabs this before suspending all threads. There
+ * are a few things that can cause a "suspend all":
+ * (1) the GC is starting;
+ * (2) the debugger has sent a "suspend all" request;
+ * (3) a thread has hit a breakpoint or exception that the debugger
+ * has marked as a "suspend all" event;
+ * (4) the SignalCatcher caught a signal that requires suspension.
+ * (5) (if implemented) the JIT needs to perform a heavyweight
+ * rearrangement of the translation cache or JitTable.
+ *
+ * Because we use "safe point" self-suspension, it is never safe to
+ * do a blocking "lock" call on this mutex -- if it has been acquired,
+ * somebody is probably trying to put you to sleep. The leading '_' is
+ * intended as a reminder that this lock is special.
+ */
+ pthread_mutex_t _threadSuspendLock;
+
+ /*
+ * Guards Thread->suspendCount for all threads, and provides the lock
+ * for the condition variable that all suspended threads sleep on
+ * (threadSuspendCountCond).
+ *
+ * This has to be separate from threadListLock because of the way
+ * threads put themselves to sleep.
+ */
+ pthread_mutex_t threadSuspendCountLock;
+
+ /*
+ * Suspended threads sleep on this. They should sleep on the condition
+ * variable until their "suspend count" is zero.
+ *
+ * Paired with "threadSuspendCountLock".
+ */
+ pthread_cond_t threadSuspendCountCond;
+
+ /*
+ * Sum of all threads' suspendCount fields. The JIT needs to know if any
+ * thread is suspended. Guarded by threadSuspendCountLock.
+ */
+ int sumThreadSuspendCount;
+
+ /*
+ * MUTEX ORDERING: when locking multiple mutexes, always grab them in
+ * this order to avoid deadlock:
+ *
+ * (1) _threadSuspendLock (use lockThreadSuspend())
+ * (2) threadListLock (use dvmLockThreadList())
+ * (3) threadSuspendCountLock (use lockThreadSuspendCount())
+ */
+
+
+ /*
+ * Thread ID bitmap. We want threads to have small integer IDs so
+ * we can use them in "thin locks".
+ */
+ BitVector* threadIdMap;
+
+ /*
+ * Manage exit conditions. The VM exits when all non-daemon threads
+ * have exited. If the main thread returns early, we need to sleep
+ * on a condition variable.
+ */
+ int nonDaemonThreadCount; /* must hold threadListLock to access */
+ //pthread_mutex_t vmExitLock;
+ pthread_cond_t vmExitCond;
+
+ /*
+ * The set of DEX files loaded by custom class loaders.
+ */
+ HashTable* userDexFiles;
+
+ /*
+ * JNI global reference table.
+ */
+#ifdef USE_INDIRECT_REF
+ IndirectRefTable jniGlobalRefTable;
+#else
+ ReferenceTable jniGlobalRefTable;
+#endif
+ pthread_mutex_t jniGlobalRefLock;
+ int jniGlobalRefHiMark;
+ int jniGlobalRefLoMark;
+
+ /*
+ * JNI pinned object table (used for primitive arrays).
+ */
+ ReferenceTable jniPinRefTable;
+ pthread_mutex_t jniPinRefLock;
+
+ /*
+ * Native shared library table.
+ */
+ HashTable* nativeLibs;
+
+ /*
+ * GC heap lock. Functions like gcMalloc() acquire this before making
+ * any changes to the heap. It is held throughout garbage collection.
+ */
+ pthread_mutex_t gcHeapLock;
+
+ /*
+ * Condition variable to queue threads waiting to retry an
+ * allocation. Signaled after a concurrent GC is completed.
+ */
+ pthread_cond_t gcHeapCond;
+
+ /* Opaque pointer representing the heap. */
+ GcHeap* gcHeap;
+
+ /* The card table base, modified as needed for marking cards. */
+ u1* biasedCardTableBase;
+
+ /*
+ * Pre-allocated throwables.
+ */
+ Object* outOfMemoryObj;
+ Object* internalErrorObj;
+ Object* noClassDefFoundErrorObj;
+
+ /* Monitor list, so we can free them */
+ /*volatile*/ Monitor* monitorList;
+
+ /* Monitor for Thread.sleep() implementation */
+ Monitor* threadSleepMon;
+
+ /* set when we create a second heap inside the zygote */
+ bool newZygoteHeapAllocated;
+
+ /*
+ * TLS keys.
+ */
+ pthread_key_t pthreadKeySelf; /* Thread*, for dvmThreadSelf */
+
+ /*
+ * JNI allows you to have multiple VMs, but we limit ourselves to 1,
+ * so "vmList" is really just a pointer to the one and only VM.
+ */
+ JavaVM* vmList;
+
+ /*
+ * Cache results of "A instanceof B".
+ */
+ AtomicCache* instanceofCache;
+
+ /* instruction width table, used for optimization and verification */
+ InstructionWidth* instrWidth;
+ /* instruction flags table, used for verification */
+ InstructionFlags* instrFlags;
+ /* instruction format table, used for verification */
+ InstructionFormat* instrFormat;
+
+ /* inline substitution table, used during optimization */
+ InlineSub* inlineSubs;
+
+ /*
+ * Bootstrap class loader linear allocator.
+ */
+ LinearAllocHdr* pBootLoaderAlloc;
+
+
+ /*
+ * Heap worker thread.
+ */
+ bool heapWorkerInitialized;
+ bool heapWorkerReady;
+ bool haltHeapWorker;
+ pthread_t heapWorkerHandle;
+ pthread_mutex_t heapWorkerLock;
+ pthread_cond_t heapWorkerCond;
+ pthread_cond_t heapWorkerIdleCond;
+ pthread_mutex_t heapWorkerListLock;
+
+ /*
+ * Compute some stats on loaded classes.
+ */
+ int numLoadedClasses;
+ int numDeclaredMethods;
+ int numDeclaredInstFields;
+ int numDeclaredStaticFields;
+
+ /* when using a native debugger, set this to suppress watchdog timers */
+ bool nativeDebuggerActive;
+
+ /*
+ * JDWP debugger support.
+ *
+ * Note "debuggerActive" is accessed from mterp, so its storage size and
+ * meaning must not be changed without updating the assembly sources.
+ */
+ bool debuggerConnected; /* debugger or DDMS is connected */
+ u1 debuggerActive; /* debugger is making requests */
+ JdwpState* jdwpState;
+
+ /*
+ * Registry of objects known to the debugger.
+ */
+ HashTable* dbgRegistry;
+
+ /*
+ * Debugger breakpoint table.
+ */
+ BreakpointSet* breakpointSet;
+
+ /*
+ * Single-step control struct. We currently only allow one thread to
+ * be single-stepping at a time, which is all that really makes sense,
+ * but it's possible we may need to expand this to be per-thread.
+ */
+ StepControl stepControl;
+
+ /*
+ * DDM features embedded in the VM.
+ */
+ bool ddmThreadNotification;
+
+ /*
+ * Zygote (partially-started process) support
+ */
+ bool zygote;
+
+ /*
+ * Used for tracking allocations that we report to DDMS. When the feature
+ * is enabled (through a DDMS request) the "allocRecords" pointer becomes
+ * non-NULL.
+ */
+ pthread_mutex_t allocTrackerLock;
+ AllocRecord* allocRecords;
+ int allocRecordHead; /* most-recently-added entry */
+ int allocRecordCount; /* #of valid entries */
+
+#ifdef WITH_ALLOC_LIMITS
+ /* set on first use of an alloc limit, never cleared */
+ bool checkAllocLimits;
+ /* allocation limit, for setGlobalAllocationLimit() regression testing */
+ int allocationLimit;
+#endif
+
+#ifdef WITH_DEADLOCK_PREDICTION
+ /* global lock on history tree accesses */
+ pthread_mutex_t deadlockHistoryLock;
+
+ enum { kDPOff=0, kDPWarn, kDPErr, kDPAbort } deadlockPredictMode;
+#endif
+
+ /*
+ * When a profiler is enabled, this is incremented. Distinct profilers
+ * include "dmtrace" method tracing, emulator method tracing, and
+ * possibly instruction counting.
+ *
+ * The purpose of this is to have a single value that the interpreter
+ * can check to see if any profiling activity is enabled.
+ */
+ volatile int activeProfilers;
+
+ /*
+ * State for method-trace profiling.
+ */
+ MethodTraceState methodTrace;
+
+ /*
+ * State for emulator tracing.
+ */
+ void* emulatorTracePage;
+ int emulatorTraceEnableCount;
+
+ /*
+ * Global state for memory allocation profiling.
+ */
+ AllocProfState allocProf;
+
+ /*
+ * Pointers to the original methods for things that have been inlined.
+ * This makes it easy for us to output method entry/exit records for
+ * the method calls we're not actually making. (Used by method
+ * profiling.)
+ */
+ Method** inlinedMethods;
+
+ /*
+ * Dalvik instruction counts (256 entries).
+ */
+ int* executedInstrCounts;
+ bool instructionCountEnableCount;
+
+ /*
+ * Signal catcher thread (for SIGQUIT).
+ */
+ pthread_t signalCatcherHandle;
+ bool haltSignalCatcher;
+
+ /*
+ * Stdout/stderr conversion thread.
+ */
+ bool haltStdioConverter;
+ bool stdioConverterReady;
+ pthread_t stdioConverterHandle;
+ pthread_mutex_t stdioConverterLock;
+ pthread_cond_t stdioConverterCond;
+
+ /*
+ * pid of the system_server process. We track it so that when system server
+ * crashes the Zygote process will be killed and restarted.
+ */
+ pid_t systemServerPid;
+
+ int kernelGroupScheduling;
+
+//#define COUNT_PRECISE_METHODS
+#ifdef COUNT_PRECISE_METHODS
+ PointerSet* preciseMethods;
+#endif
+
+ /* some RegisterMap statistics, useful during development */
+ void* registerMapStats;
+};
+
+extern struct DvmGlobals gDvm;
+
+#if defined(WITH_JIT)
+
+/*
+ * Exiting the compiled code w/o chaining will incur overhead to look up the
+ * target in the code cache which is extra work only when JIT is enabled. So
+ * we want to monitor it closely to make sure we don't have performance bugs.
+ */
+typedef enum NoChainExits {
+ kInlineCacheMiss = 0,
+ kCallsiteInterpreted,
+ kSwitchOverflow,
+ kHeavyweightMonitor,
+ kNoChainExitLast,
+} NoChainExits;
+
+/*
+ * JIT-specific global state
+ */
+struct DvmJitGlobals {
+ /*
+ * Guards writes to Dalvik PC (dPC), translated code address (codeAddr) and
+ * chain fields within the JIT hash table. Note carefully the access
+ * mechanism.
+ * Only writes are guarded, and the guarded fields must be updated in a
+ * specific order using atomic operations. Further, once a field is
+ * written it cannot be changed without halting all threads.
+ *
+ * The write order is:
+ * 1) codeAddr
+ * 2) dPC
+ * 3) chain [if necessary]
+ *
+ * This mutex also guards both read and write of curJitTableEntries.
+ */
+ pthread_mutex_t tableLock;
+
+ /* The JIT hash table. Note that for access speed, copies of this pointer
+ * are stored in each thread. */
+ struct JitEntry *pJitEntryTable;
+
+ /* Array of profile threshold counters */
+ unsigned char *pProfTable;
+
+ /* Copy of pProfTable used for temporarily disabling the Jit */
+ unsigned char *pProfTableCopy;
+
+ /* Size of JIT hash table in entries. Must be a power of 2 */
+ unsigned int jitTableSize;
+
+ /* Mask used in hash function for JitTable. Should be jitTableSize-1 */
+ unsigned int jitTableMask;
+
+ /* How many entries in the JitEntryTable are in use */
+ unsigned int jitTableEntriesUsed;
+
+ /* Bytes allocated for the code cache */
+ unsigned int codeCacheSize;
+
+ /* Trigger for trace selection */
+ unsigned short threshold;
+
+ /* JIT Compiler Control */
+ bool haltCompilerThread;
+ bool blockingMode;
+ pthread_t compilerHandle;
+ pthread_mutex_t compilerLock;
+ pthread_mutex_t compilerICPatchLock;
+ pthread_cond_t compilerQueueActivity;
+ pthread_cond_t compilerQueueEmpty;
+ volatile int compilerQueueLength;
+ int compilerHighWater;
+ int compilerWorkEnqueueIndex;
+ int compilerWorkDequeueIndex;
+ int compilerICPatchIndex;
+
+ /* JIT internal stats */
+ int compilerMaxQueued;
+ int translationChains;
+
+ /* Compiled code cache */
+ void* codeCache;
+
+ /* Bytes used by the code templates */
+ unsigned int templateSize;
+
+ /* Bytes already used in the code cache */
+ unsigned int codeCacheByteUsed;
+
+ /* Number of installed compilations in the cache */
+ unsigned int numCompilations;
+
+ /* Flag to indicate that the code cache is full */
+ bool codeCacheFull;
+
+ /* Page size - 1 */
+ unsigned int pageSizeMask;
+
+ /* Lock to change the protection type of the code cache */
+ pthread_mutex_t codeCacheProtectionLock;
+
+ /* Number of times that the code cache has been reset */
+ int numCodeCacheReset;
+
+ /* Number of times that the code cache reset request has been delayed */
+ int numCodeCacheResetDelayed;
+
+ /* true/false: compile/reject opcodes specified in the -Xjitop list */
+ bool includeSelectedOp;
+
+ /* true/false: compile/reject methods specified in the -Xjitmethod list */
+ bool includeSelectedMethod;
+
+ /* Disable JIT for selected opcodes - one bit for each opcode */
+ char opList[32];
+
+ /* Disable JIT for selected methods */
+ HashTable *methodTable;
+
+ /* Flag to dump all compiled code */
+ bool printMe;
+
+ /* Flag to count trace execution */
+ bool profile;
+
+ /* Vector to disable selected optimizations */
+ int disableOpt;
+
+ /* Table to track the overall and trace statistics of hot methods */
+ HashTable* methodStatsTable;
+
+ /* Filter method compilation blacklist with call-graph information */
+ bool checkCallGraph;
+
+ /* New translation chain has been set up */
+ volatile bool hasNewChain;
+
+#if defined(WITH_SELF_VERIFICATION)
+ /* Spin when error is detected, volatile so GDB can reset it */
+ volatile bool selfVerificationSpin;
+#endif
+
+ /* Framework or stand-alone? */
+ bool runningInAndroidFramework;
+
+ /* Framework callback happened? */
+ bool alreadyEnabledViaFramework;
+
+ /* Framework requests to disable the JIT for good */
+ bool disableJit;
+
+#if defined(SIGNATURE_BREAKPOINT)
+ /* Signature breakpoint */
+ u4 signatureBreakpointSize; // # of words
+ u4 *signatureBreakpoint; // Signature content
+#endif
+
+#if defined(WITH_JIT_TUNING)
+ /* Performance tuning counters */
+ int addrLookupsFound;
+ int addrLookupsNotFound;
+ int noChainExit[kNoChainExitLast];
+ int normalExit;
+ int puntExit;
+ int invokeMonomorphic;
+ int invokePolymorphic;
+ int invokeNative;
+ int invokeMonoGetterInlined;
+ int invokeMonoSetterInlined;
+ int invokePolyGetterInlined;
+ int invokePolySetterInlined;
+ int returnOp;
+ int icPatchInit;
+ int icPatchLockFree;
+ int icPatchQueued;
+ int icPatchRejected;
+ int icPatchDropped;
+ u8 jitTime;
+ int codeCachePatches;
+#endif
+
+ /* Place arrays at the end to ease the display in gdb sessions */
+
+ /* Work order queue for compilations */
+ CompilerWorkOrder compilerWorkQueue[COMPILER_WORK_QUEUE_SIZE];
+
+ /* Work order queue for predicted chain patching */
+ ICPatchWorkOrder compilerICPatchQueue[COMPILER_IC_PATCH_QUEUE_SIZE];
+};
+
+extern struct DvmJitGlobals gDvmJit;
+
+#if defined(WITH_JIT_TUNING)
+extern int gDvmICHitCount;
+#endif
+
+#endif
+
+#endif /*_DALVIK_GLOBALS*/
diff --git a/vm/Hash.c b/vm/Hash.c
new file mode 100644
index 0000000..7bdd92f
--- /dev/null
+++ b/vm/Hash.c
@@ -0,0 +1,418 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Hash table. The dominant calls are add and lookup, with removals
+ * happening very infrequently. We use probing, and don't worry much
+ * about tombstone removal.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+
+/* table load factor, i.e. how full can it get before we resize */
+//#define LOAD_NUMER 3 // 75%
+//#define LOAD_DENOM 4
+#define LOAD_NUMER 5 // 62.5%
+#define LOAD_DENOM 8
+//#define LOAD_NUMER 1 // 50%
+//#define LOAD_DENOM 2
+
+/*
+ * Compute the capacity needed for a table to hold "size" elements.
+ */
+size_t dvmHashSize(size_t size) {
+ return (size * LOAD_DENOM) / LOAD_NUMER +1;
+}
+
+
+/*
+ * Create and initialize a hash table.
+ */
+HashTable* dvmHashTableCreate(size_t initialSize, HashFreeFunc freeFunc)
+{
+ HashTable* pHashTable;
+
+ assert(initialSize > 0);
+
+ pHashTable = (HashTable*) malloc(sizeof(*pHashTable));
+ if (pHashTable == NULL)
+ return NULL;
+
+ dvmInitMutex(&pHashTable->lock);
+
+ pHashTable->tableSize = dexRoundUpPower2(initialSize);
+ pHashTable->numEntries = pHashTable->numDeadEntries = 0;
+ pHashTable->freeFunc = freeFunc;
+ pHashTable->pEntries =
+ (HashEntry*) malloc(pHashTable->tableSize * sizeof(HashEntry));
+ if (pHashTable->pEntries == NULL) {
+ free(pHashTable);
+ return NULL;
+ }
+
+ memset(pHashTable->pEntries, 0, pHashTable->tableSize * sizeof(HashEntry));
+ return pHashTable;
+}
+
+/*
+ * Clear out all entries.
+ */
+void dvmHashTableClear(HashTable* pHashTable)
+{
+ HashEntry* pEnt;
+ int i;
+
+ pEnt = pHashTable->pEntries;
+ for (i = 0; i < pHashTable->tableSize; i++, pEnt++) {
+ if (pEnt->data == HASH_TOMBSTONE) {
+ // nuke entry
+ pEnt->data = NULL;
+ } else if (pEnt->data != NULL) {
+ // call free func then nuke entry
+ if (pHashTable->freeFunc != NULL)
+ (*pHashTable->freeFunc)(pEnt->data);
+ pEnt->data = NULL;
+ }
+ }
+
+ pHashTable->numEntries = 0;
+ pHashTable->numDeadEntries = 0;
+}
+
+/*
+ * Free the table.
+ */
+void dvmHashTableFree(HashTable* pHashTable)
+{
+ if (pHashTable == NULL)
+ return;
+ dvmHashTableClear(pHashTable);
+ free(pHashTable->pEntries);
+ free(pHashTable);
+}
+
+#ifndef NDEBUG
+/*
+ * Count up the number of tombstone entries in the hash table.
+ */
+static int countTombStones(HashTable* pHashTable)
+{
+ int i, count;
+
+ for (count = i = 0; i < pHashTable->tableSize; i++) {
+ if (pHashTable->pEntries[i].data == HASH_TOMBSTONE)
+ count++;
+ }
+ return count;
+}
+#endif
+
+/*
+ * Resize a hash table. We do this when adding an entry increased the
+ * size of the table beyond its comfy limit.
+ *
+ * This essentially requires re-inserting all elements into the new storage.
+ *
+ * If multiple threads can access the hash table, the table's lock should
+ * have been grabbed before issuing the "lookup+add" call that led to the
+ * resize, so we don't have a synchronization problem here.
+ */
+static bool resizeHash(HashTable* pHashTable, int newSize)
+{
+ HashEntry* pNewEntries;
+ int i;
+
+ assert(countTombStones(pHashTable) == pHashTable->numDeadEntries);
+ //LOGI("before: dead=%d\n", pHashTable->numDeadEntries);
+
+ pNewEntries = (HashEntry*) calloc(newSize, sizeof(HashEntry));
+ if (pNewEntries == NULL)
+ return false;
+
+ for (i = 0; i < pHashTable->tableSize; i++) {
+ void* data = pHashTable->pEntries[i].data;
+ if (data != NULL && data != HASH_TOMBSTONE) {
+ int hashValue = pHashTable->pEntries[i].hashValue;
+ int newIdx;
+
+ /* probe for new spot, wrapping around */
+ newIdx = hashValue & (newSize-1);
+ while (pNewEntries[newIdx].data != NULL)
+ newIdx = (newIdx + 1) & (newSize-1);
+
+ pNewEntries[newIdx].hashValue = hashValue;
+ pNewEntries[newIdx].data = data;
+ }
+ }
+
+ free(pHashTable->pEntries);
+ pHashTable->pEntries = pNewEntries;
+ pHashTable->tableSize = newSize;
+ pHashTable->numDeadEntries = 0;
+
+ assert(countTombStones(pHashTable) == 0);
+ return true;
+}
+
+/*
+ * Look up an entry.
+ *
+ * We probe on collisions, wrapping around the table.
+ */
+void* dvmHashTableLookup(HashTable* pHashTable, u4 itemHash, void* item,
+ HashCompareFunc cmpFunc, bool doAdd)
+{
+ HashEntry* pEntry;
+ HashEntry* pEnd;
+ void* result = NULL;
+
+ assert(pHashTable->tableSize > 0);
+ assert(item != HASH_TOMBSTONE);
+ assert(item != NULL);
+
+ /* jump to the first entry and probe for a match */
+ pEntry = &pHashTable->pEntries[itemHash & (pHashTable->tableSize-1)];
+ pEnd = &pHashTable->pEntries[pHashTable->tableSize];
+ while (pEntry->data != NULL) {
+ if (pEntry->data != HASH_TOMBSTONE &&
+ pEntry->hashValue == itemHash &&
+ (*cmpFunc)(pEntry->data, item) == 0)
+ {
+ /* match */
+ //LOGD("+++ match on entry %d\n", pEntry - pHashTable->pEntries);
+ break;
+ }
+
+ pEntry++;
+ if (pEntry == pEnd) { /* wrap around to start */
+ if (pHashTable->tableSize == 1)
+ break; /* edge case - single-entry table */
+ pEntry = pHashTable->pEntries;
+ }
+
+ //LOGI("+++ look probing %d...\n", pEntry - pHashTable->pEntries);
+ }
+
+ if (pEntry->data == NULL) {
+ if (doAdd) {
+ pEntry->hashValue = itemHash;
+ pEntry->data = item;
+ pHashTable->numEntries++;
+
+ /*
+ * We've added an entry. See if this brings us too close to full.
+ */
+ if ((pHashTable->numEntries+pHashTable->numDeadEntries) * LOAD_DENOM
+ > pHashTable->tableSize * LOAD_NUMER)
+ {
+ if (!resizeHash(pHashTable, pHashTable->tableSize * 2)) {
+ /* don't really have a way to indicate failure */
+ LOGE("Dalvik hash resize failure\n");
+ dvmAbort();
+ }
+ /* note "pEntry" is now invalid */
+ } else {
+ //LOGW("okay %d/%d/%d\n",
+ // pHashTable->numEntries, pHashTable->tableSize,
+ // (pHashTable->tableSize * LOAD_NUMER) / LOAD_DENOM);
+ }
+
+ /* full table is bad -- search for nonexistent never halts */
+ assert(pHashTable->numEntries < pHashTable->tableSize);
+ result = item;
+ } else {
+ assert(result == NULL);
+ }
+ } else {
+ result = pEntry->data;
+ }
+
+ return result;
+}
+
+/*
+ * Remove an entry from the table.
+ *
+ * Does NOT invoke the "free" function on the item.
+ */
+bool dvmHashTableRemove(HashTable* pHashTable, u4 itemHash, void* item)
+{
+ HashEntry* pEntry;
+ HashEntry* pEnd;
+
+ assert(pHashTable->tableSize > 0);
+
+ /* jump to the first entry and probe for a match */
+ pEntry = &pHashTable->pEntries[itemHash & (pHashTable->tableSize-1)];
+ pEnd = &pHashTable->pEntries[pHashTable->tableSize];
+ while (pEntry->data != NULL) {
+ if (pEntry->data == item) {
+ //LOGI("+++ stepping on entry %d\n", pEntry - pHashTable->pEntries);
+ pEntry->data = HASH_TOMBSTONE;
+ pHashTable->numEntries--;
+ pHashTable->numDeadEntries++;
+ return true;
+ }
+
+ pEntry++;
+ if (pEntry == pEnd) { /* wrap around to start */
+ if (pHashTable->tableSize == 1)
+ break; /* edge case - single-entry table */
+ pEntry = pHashTable->pEntries;
+ }
+
+ //LOGI("+++ del probing %d...\n", pEntry - pHashTable->pEntries);
+ }
+
+ return false;
+}
+
+/*
+ * Scan every entry in the hash table and evaluate it with the specified
+ * indirect function call. If the function returns 1, remove the entry from
+ * the table.
+ *
+ * Does NOT invoke the "free" function on the item.
+ *
+ * Returning values other than 0 or 1 will abort the routine.
+ */
+int dvmHashForeachRemove(HashTable* pHashTable, HashForeachRemoveFunc func)
+{
+ int i, val;
+
+ for (i = 0; i < pHashTable->tableSize; i++) {
+ HashEntry* pEnt = &pHashTable->pEntries[i];
+
+ if (pEnt->data != NULL && pEnt->data != HASH_TOMBSTONE) {
+ val = (*func)(pEnt->data);
+ if (val == 1) {
+ pEnt->data = HASH_TOMBSTONE;
+ pHashTable->numEntries--;
+ pHashTable->numDeadEntries++;
+ }
+ else if (val != 0) {
+ return val;
+ }
+ }
+ }
+ return 0;
+}
+
+
+/*
+ * Execute a function on every entry in the hash table.
+ *
+ * If "func" returns a nonzero value, terminate early and return the value.
+ */
+int dvmHashForeach(HashTable* pHashTable, HashForeachFunc func, void* arg)
+{
+ int i, val;
+
+ for (i = 0; i < pHashTable->tableSize; i++) {
+ HashEntry* pEnt = &pHashTable->pEntries[i];
+
+ if (pEnt->data != NULL && pEnt->data != HASH_TOMBSTONE) {
+ val = (*func)(pEnt->data, arg);
+ if (val != 0)
+ return val;
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ * Look up an entry, counting the number of times we have to probe.
+ *
+ * Returns -1 if the entry wasn't found.
+ */
+static int countProbes(HashTable* pHashTable, u4 itemHash, const void* item,
+ HashCompareFunc cmpFunc)
+{
+ HashEntry* pEntry;
+ HashEntry* pEnd;
+ int count = 0;
+
+ assert(pHashTable->tableSize > 0);
+ assert(item != HASH_TOMBSTONE);
+ assert(item != NULL);
+
+ /* jump to the first entry and probe for a match */
+ pEntry = &pHashTable->pEntries[itemHash & (pHashTable->tableSize-1)];
+ pEnd = &pHashTable->pEntries[pHashTable->tableSize];
+ while (pEntry->data != NULL) {
+ if (pEntry->data != HASH_TOMBSTONE &&
+ pEntry->hashValue == itemHash &&
+ (*cmpFunc)(pEntry->data, item) == 0)
+ {
+ /* match */
+ break;
+ }
+
+ pEntry++;
+ if (pEntry == pEnd) { /* wrap around to start */
+ if (pHashTable->tableSize == 1)
+ break; /* edge case - single-entry table */
+ pEntry = pHashTable->pEntries;
+ }
+
+ count++;
+ }
+ if (pEntry->data == NULL)
+ return -1;
+
+ return count;
+}
+
+/*
+ * Evaluate the amount of probing required for the specified hash table.
+ *
+ * We do this by running through all entries in the hash table, computing
+ * the hash value and then doing a lookup.
+ *
+ * The caller should lock the table before calling here.
+ */
+void dvmHashTableProbeCount(HashTable* pHashTable, HashCalcFunc calcFunc,
+ HashCompareFunc cmpFunc)
+{
+ int numEntries, minProbe, maxProbe, totalProbe;
+ HashIter iter;
+
+ numEntries = maxProbe = totalProbe = 0;
+ minProbe = 65536*32767;
+
+ for (dvmHashIterBegin(pHashTable, &iter); !dvmHashIterDone(&iter);
+ dvmHashIterNext(&iter))
+ {
+ const void* data = (const void*)dvmHashIterData(&iter);
+ int count;
+
+ count = countProbes(pHashTable, (*calcFunc)(data), data, cmpFunc);
+
+ numEntries++;
+
+ if (count < minProbe)
+ minProbe = count;
+ if (count > maxProbe)
+ maxProbe = count;
+ totalProbe += count;
+ }
+
+ LOGI("Probe: min=%d max=%d, total=%d in %d (%d), avg=%.3f\n",
+ minProbe, maxProbe, totalProbe, numEntries, pHashTable->tableSize,
+ (float) totalProbe / (float) numEntries);
+}
diff --git a/vm/Hash.h b/vm/Hash.h
new file mode 100644
index 0000000..cfd7544
--- /dev/null
+++ b/vm/Hash.h
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * General purpose hash table, used for finding classes, methods, etc.
+ *
+ * When the number of elements reaches a certain percentage of the table's
+ * capacity, the table will be resized.
+ */
+#ifndef _DALVIK_HASH
+#define _DALVIK_HASH
+
+/* compute the hash of an item with a specific type */
+typedef u4 (*HashCompute)(const void* item);
+
+/*
+ * Compare a hash entry with a "loose" item after their hash values match.
+ * Returns { <0, 0, >0 } depending on ordering of items (same semantics
+ * as strcmp()).
+ */
+typedef int (*HashCompareFunc)(const void* tableItem, const void* looseItem);
+
+/*
+ * This function will be used to free entries in the table. This can be
+ * NULL if no free is required, free(), or a custom function.
+ */
+typedef void (*HashFreeFunc)(void* ptr);
+
+/*
+ * Used by dvmHashForeach().
+ */
+typedef int (*HashForeachFunc)(void* data, void* arg);
+
+/*
+ * Used by dvmHashForeachRemove().
+ */
+typedef int (*HashForeachRemoveFunc)(void* data);
+
+/*
+ * One entry in the hash table. "data" values are expected to be (or have
+ * the same characteristics as) valid pointers. In particular, a NULL
+ * value for "data" indicates an empty slot, and HASH_TOMBSTONE indicates
+ * a no-longer-used slot that must be stepped over during probing.
+ *
+ * Attempting to add a NULL or tombstone value is an error.
+ *
+ * When an entry is released, we will call (HashFreeFunc)(entry->data).
+ */
+typedef struct HashEntry {
+ u4 hashValue;
+ void* data;
+} HashEntry;
+
+#define HASH_TOMBSTONE ((void*) 0xcbcacccd) // invalid ptr value
+
+/*
+ * Expandable hash table.
+ *
+ * This structure should be considered opaque.
+ */
+typedef struct HashTable {
+ int tableSize; /* must be power of 2 */
+ int numEntries; /* current #of "live" entries */
+ int numDeadEntries; /* current #of tombstone entries */
+ HashEntry* pEntries; /* array on heap */
+ HashFreeFunc freeFunc;
+ pthread_mutex_t lock;
+} HashTable;
+
+/*
+ * Create and initialize a HashTable structure, using "initialSize" as
+ * a basis for the initial capacity of the table. (The actual initial
+ * table size may be adjusted upward.) If you know exactly how many
+ * elements the table will hold, pass the result from dvmHashSize() in.)
+ *
+ * Returns "false" if unable to allocate the table.
+ */
+HashTable* dvmHashTableCreate(size_t initialSize, HashFreeFunc freeFunc);
+
+/*
+ * Compute the capacity needed for a table to hold "size" elements. Use
+ * this when you know ahead of time how many elements the table will hold.
+ * Pass this value into dvmHashTableCreate() to ensure that you can add
+ * all elements without needing to reallocate the table.
+ */
+size_t dvmHashSize(size_t size);
+
+/*
+ * Clear out a hash table, freeing the contents of any used entries.
+ */
+void dvmHashTableClear(HashTable* pHashTable);
+
+/*
+ * Free a hash table. Performs a "clear" first.
+ */
+void dvmHashTableFree(HashTable* pHashTable);
+
+/*
+ * Exclusive access. Important when adding items to a table, or when
+ * doing any operations on a table that could be added to by another thread.
+ */
+INLINE void dvmHashTableLock(HashTable* pHashTable) {
+ dvmLockMutex(&pHashTable->lock);
+}
+INLINE void dvmHashTableUnlock(HashTable* pHashTable) {
+ dvmUnlockMutex(&pHashTable->lock);
+}
+
+/*
+ * Get #of entries in hash table.
+ */
+INLINE int dvmHashTableNumEntries(HashTable* pHashTable) {
+ return pHashTable->numEntries;
+}
+
+/*
+ * Get total size of hash table (for memory usage calculations).
+ */
+INLINE int dvmHashTableMemUsage(HashTable* pHashTable) {
+ return sizeof(HashTable) + pHashTable->tableSize * sizeof(HashEntry);
+}
+
+/*
+ * Look up an entry in the table, possibly adding it if it's not there.
+ *
+ * If "item" is not found, and "doAdd" is false, NULL is returned.
+ * Otherwise, a pointer to the found or added item is returned. (You can
+ * tell the difference by seeing if return value == item.)
+ *
+ * An "add" operation may cause the entire table to be reallocated. Don't
+ * forget to lock the table before calling this.
+ */
+void* dvmHashTableLookup(HashTable* pHashTable, u4 itemHash, void* item,
+ HashCompareFunc cmpFunc, bool doAdd);
+
+/*
+ * Remove an item from the hash table, given its "data" pointer. Does not
+ * invoke the "free" function; just detaches it from the table.
+ */
+bool dvmHashTableRemove(HashTable* pHashTable, u4 hash, void* item);
+
+/*
+ * Execute "func" on every entry in the hash table.
+ *
+ * If "func" returns a nonzero value, terminate early and return the value.
+ */
+int dvmHashForeach(HashTable* pHashTable, HashForeachFunc func, void* arg);
+
+/*
+ * Execute "func" on every entry in the hash table.
+ *
+ * If "func" returns 1 detach the entry from the hash table. Does not invoke
+ * the "free" function.
+ *
+ * Returning values other than 0 or 1 from "func" will abort the routine.
+ */
+int dvmHashForeachRemove(HashTable* pHashTable, HashForeachRemoveFunc func);
+
+/*
+ * An alternative to dvmHashForeach(), using an iterator.
+ *
+ * Use like this:
+ * HashIter iter;
+ * for (dvmHashIterBegin(hashTable, &iter); !dvmHashIterDone(&iter);
+ * dvmHashIterNext(&iter))
+ * {
+ * MyData* data = (MyData*)dvmHashIterData(&iter);
+ * }
+ */
+typedef struct HashIter {
+ void* data;
+ HashTable* pHashTable;
+ int idx;
+} HashIter;
+INLINE void dvmHashIterNext(HashIter* pIter) {
+ int i = pIter->idx +1;
+ int lim = pIter->pHashTable->tableSize;
+ for ( ; i < lim; i++) {
+ void* data = pIter->pHashTable->pEntries[i].data;
+ if (data != NULL && data != HASH_TOMBSTONE)
+ break;
+ }
+ pIter->idx = i;
+}
+INLINE void dvmHashIterBegin(HashTable* pHashTable, HashIter* pIter) {
+ pIter->pHashTable = pHashTable;
+ pIter->idx = -1;
+ dvmHashIterNext(pIter);
+}
+INLINE bool dvmHashIterDone(HashIter* pIter) {
+ return (pIter->idx >= pIter->pHashTable->tableSize);
+}
+INLINE void* dvmHashIterData(HashIter* pIter) {
+ assert(pIter->idx >= 0 && pIter->idx < pIter->pHashTable->tableSize);
+ return pIter->pHashTable->pEntries[pIter->idx].data;
+}
+
+
+/*
+ * Evaluate hash table performance by examining the number of times we
+ * have to probe for an entry.
+ *
+ * The caller should lock the table beforehand.
+ */
+typedef u4 (*HashCalcFunc)(const void* item);
+void dvmHashTableProbeCount(HashTable* pHashTable, HashCalcFunc calcFunc,
+ HashCompareFunc cmpFunc);
+
+#endif /*_DALVIK_HASH*/
diff --git a/vm/IndirectRefTable.c b/vm/IndirectRefTable.c
new file mode 100644
index 0000000..dadd03f
--- /dev/null
+++ b/vm/IndirectRefTable.c
@@ -0,0 +1,501 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Indirect reference table management.
+ */
+#include "Dalvik.h"
+
+/*
+ * Initialize an IndirectRefTable structure.
+ */
+bool dvmInitIndirectRefTable(IndirectRefTable* pRef, int initialCount,
+ int maxCount, IndirectRefKind kind)
+{
+ assert(initialCount > 0);
+ assert(initialCount <= maxCount);
+ assert(kind == kIndirectKindLocal || kind == kIndirectKindGlobal);
+
+ pRef->table = (Object**) malloc(initialCount * sizeof(Object*));
+ if (pRef->table == NULL)
+ return false;
+#ifndef NDEBUG
+ memset(pRef->table, 0xd1, initialCount * sizeof(Object*));
+#endif
+
+ pRef->slotData =
+ (IndirectRefSlot*) calloc(maxCount, sizeof(IndirectRefSlot));
+ if (pRef->slotData == NULL)
+ return false;
+
+ pRef->segmentState.all = IRT_FIRST_SEGMENT;
+ pRef->allocEntries = initialCount;
+ pRef->maxEntries = maxCount;
+ pRef->kind = kind;
+
+ return true;
+}
+
+/*
+ * Clears out the contents of a IndirectRefTable, freeing allocated storage.
+ */
+void dvmClearIndirectRefTable(IndirectRefTable* pRef)
+{
+ free(pRef->table);
+ pRef->table = NULL;
+ pRef->allocEntries = pRef->maxEntries = -1;
+}
+
+/*
+ * Remove one or more segments from the top. The table entry identified
+ * by "cookie" becomes the new top-most entry.
+ *
+ * Returns false if "cookie" is invalid or the table has only one segment.
+ */
+bool dvmPopIndirectRefTableSegmentCheck(IndirectRefTable* pRef, u4 cookie)
+{
+ IRTSegmentState sst;
+
+ /*
+ * The new value for "top" must be <= the current value. Otherwise
+ * this would represent an expansion of the table.
+ */
+ sst.all = cookie;
+ if (sst.parts.topIndex > pRef->segmentState.parts.topIndex) {
+ LOGE("Attempt to expand table with segment pop (%d to %d)\n",
+ pRef->segmentState.parts.topIndex, sst.parts.topIndex);
+ return false;
+ }
+ if (sst.parts.numHoles >= sst.parts.topIndex) {
+ LOGE("Absurd numHoles in cookie (%d bi=%d)\n",
+ sst.parts.numHoles, sst.parts.topIndex);
+ return false;
+ }
+
+ LOGV("IRT %p[%d]: pop, top=%d holes=%d\n",
+ pRef, pRef->kind, sst.parts.topIndex, sst.parts.numHoles);
+
+ return true;
+}
+
+/*
+ * Make sure that the entry at "idx" is correctly paired with "iref".
+ */
+static bool checkEntry(IndirectRefTable* pRef, IndirectRef iref, int idx)
+{
+ Object* obj = pRef->table[idx];
+ IndirectRef checkRef = dvmObjectToIndirectRef(pRef, obj, idx, pRef->kind);
+ if (checkRef != iref) {
+ LOGW("IRT %p[%d]: iref mismatch (req=%p vs cur=%p)\n",
+ pRef, pRef->kind, iref, checkRef);
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Update extended debug info when an entry is added.
+ *
+ * We advance the serial number, invalidating any outstanding references to
+ * this slot.
+ */
+static inline void updateSlotAdd(IndirectRefTable* pRef, Object* obj, int slot)
+{
+ if (pRef->slotData != NULL) {
+ IndirectRefSlot* pSlot = &pRef->slotData[slot];
+ pSlot->serial++;
+ //LOGI("+++ add [%d] slot %d (%p->%p), serial=%d\n",
+ // pRef->kind, slot, obj, iref, pSlot->serial);
+ pSlot->previous[pSlot->serial % kIRTPrevCount] = obj;
+ }
+}
+
+/*
+ * Update extended debug info when an entry is removed.
+ */
+static inline void updateSlotRemove(IndirectRefTable* pRef, int slot)
+{
+ if (pRef->slotData != NULL) {
+ //IndirectRefSlot* pSlot = &pRef->slotData[slot];
+ //LOGI("+++ remove [%d] slot %d, serial now %d\n",
+ // pRef->kind, slot, pSlot->serial);
+ }
+}
+
+/*
+ * Add "obj" to "pRef".
+ */
+IndirectRef dvmAddToIndirectRefTable(IndirectRefTable* pRef, u4 cookie,
+ Object* obj)
+{
+ IRTSegmentState prevState;
+ prevState.all = cookie;
+ int topIndex = pRef->segmentState.parts.topIndex;
+
+ assert(obj != NULL);
+ assert(dvmIsValidObject(obj));
+ assert(pRef->table != NULL);
+ assert(pRef->allocEntries <= pRef->maxEntries);
+ assert(pRef->segmentState.parts.numHoles >= prevState.parts.numHoles);
+
+ if (topIndex == pRef->allocEntries) {
+ /* reached end of allocated space; did we hit buffer max? */
+ if (topIndex == pRef->maxEntries) {
+ LOGW("IndirectRefTable overflow (max=%d)\n", pRef->maxEntries);
+ return NULL;
+ }
+
+ Object** newTable;
+ int newSize;
+
+ newSize = pRef->allocEntries * 2;
+ if (newSize > pRef->maxEntries)
+ newSize = pRef->maxEntries;
+ assert(newSize > pRef->allocEntries);
+
+ newTable = (Object**) realloc(pRef->table, newSize * sizeof(Object*));
+ if (newTable == NULL) {
+ LOGE("Unable to expand iref table (from %d to %d, max=%d)\n",
+ pRef->allocEntries, newSize, pRef->maxEntries);
+ return false;
+ }
+ LOGI("Growing ireftab %p from %d to %d (max=%d)\n",
+ pRef, pRef->allocEntries, newSize, pRef->maxEntries);
+
+ /* update entries; adjust "nextEntry" in case memory moved */
+ pRef->table = newTable;
+ pRef->allocEntries = newSize;
+ }
+
+ IndirectRef result;
+
+ /*
+ * We know there's enough room in the table. Now we just need to find
+ * the right spot. If there's a hole, find it and fill it; otherwise,
+ * add to the end of the list.
+ */
+ int numHoles = pRef->segmentState.parts.numHoles - prevState.parts.numHoles;
+ if (numHoles > 0) {
+ assert(topIndex > 1);
+ /* find the first hole; likely to be near the end of the list */
+ Object** pScan = &pRef->table[topIndex - 1];
+ assert(*pScan != NULL);
+ while (*--pScan != NULL) {
+ assert(pScan >= pRef->table + prevState.parts.topIndex);
+ }
+ updateSlotAdd(pRef, obj, pScan - pRef->table);
+ result = dvmObjectToIndirectRef(pRef, obj, pScan - pRef->table,
+ pRef->kind);
+ *pScan = obj;
+ pRef->segmentState.parts.numHoles--;
+ } else {
+ /* add to the end */
+ updateSlotAdd(pRef, obj, topIndex);
+ result = dvmObjectToIndirectRef(pRef, obj, topIndex, pRef->kind);
+ pRef->table[topIndex++] = obj;
+ pRef->segmentState.parts.topIndex = topIndex;
+ }
+
+ assert(result != NULL);
+ return result;
+}
+
+/*
+ * Verify that the indirect table lookup is valid.
+ *
+ * Returns "false" if something looks bad.
+ */
+bool dvmGetFromIndirectRefTableCheck(IndirectRefTable* pRef, IndirectRef iref)
+{
+ if (dvmGetIndirectRefType(iref) == kIndirectKindInvalid) {
+ LOGW("Invalid indirect reference 0x%08x\n", (u4) iref);
+ return false;
+ }
+
+ int topIndex = pRef->segmentState.parts.topIndex;
+ int idx = dvmIndirectRefToIndex(iref);
+
+ if (iref == NULL) {
+ LOGI("--- lookup on NULL iref\n");
+ return false;
+ }
+ if (idx >= topIndex) {
+ /* bad -- stale reference? */
+ LOGI("Attempt to access invalid index %d (top=%d)\n",
+ idx, topIndex);
+ return false;
+ }
+
+ Object* obj = pRef->table[idx];
+ if (obj == NULL) {
+ LOGI("Attempt to read from hole, iref=%p\n", iref);
+ return false;
+ }
+ if (!checkEntry(pRef, iref, idx))
+ return false;
+
+ return true;
+}
+
+/*
+ * Remove "obj" from "pRef". We extract the table offset bits from "iref"
+ * and zap the corresponding entry, leaving a hole if it's not at the top.
+ *
+ * If the entry is not between the current top index and the bottom index
+ * specified by the cookie, we don't remove anything. This is the behavior
+ * required by JNI's DeleteLocalRef function.
+ *
+ * Note this is NOT called when a local frame is popped. This is only used
+ * for explict single removals.
+ *
+ * Returns "false" if nothing was removed.
+ */
+bool dvmRemoveFromIndirectRefTable(IndirectRefTable* pRef, u4 cookie,
+ IndirectRef iref)
+{
+ IRTSegmentState prevState;
+ prevState.all = cookie;
+ int topIndex = pRef->segmentState.parts.topIndex;
+ int bottomIndex = prevState.parts.topIndex;
+
+ assert(pRef->table != NULL);
+ assert(pRef->allocEntries <= pRef->maxEntries);
+ assert(pRef->segmentState.parts.numHoles >= prevState.parts.numHoles);
+
+ int idx = dvmIndirectRefToIndex(iref);
+ if (idx < bottomIndex) {
+ /* wrong segment */
+ LOGV("Attempt to remove index outside index area (%d vs %d-%d)\n",
+ idx, bottomIndex, topIndex);
+ return false;
+ }
+ if (idx >= topIndex) {
+ /* bad -- stale reference? */
+ LOGI("Attempt to remove invalid index %d (bottom=%d top=%d)\n",
+ idx, bottomIndex, topIndex);
+ return false;
+ }
+
+ if (idx == topIndex-1) {
+ /*
+ * Top-most entry. Scan up and consume holes. No need to NULL
+ * out the entry, since the test vs. topIndex will catch it.
+ */
+ if (!checkEntry(pRef, iref, idx))
+ return false;
+ updateSlotRemove(pRef, idx);
+
+#ifndef NDEBUG
+ pRef->table[idx] = (IndirectRef) 0xd3d3d3d3;
+#endif
+
+ int numHoles =
+ pRef->segmentState.parts.numHoles - prevState.parts.numHoles;
+ if (numHoles != 0) {
+ while (--topIndex > bottomIndex && numHoles != 0) {
+ LOGV("+++ checking for hole at %d (cookie=0x%08x) val=%p\n",
+ topIndex-1, cookie, pRef->table[topIndex-1]);
+ if (pRef->table[topIndex-1] != NULL)
+ break;
+ LOGV("+++ ate hole at %d\n", topIndex-1);
+ numHoles--;
+ }
+ pRef->segmentState.parts.numHoles =
+ numHoles + prevState.parts.numHoles;
+ pRef->segmentState.parts.topIndex = topIndex;
+ } else {
+ pRef->segmentState.parts.topIndex = topIndex-1;
+ LOGV("+++ ate last entry %d\n", topIndex-1);
+ }
+ } else {
+ /*
+ * Not the top-most entry. This creates a hole. We NULL out the
+ * entry to prevent somebody from deleting it twice and screwing up
+ * the hole count.
+ */
+ if (pRef->table[idx] == NULL) {
+ LOGV("--- WEIRD: removing null entry %d\n", idx);
+ return false;
+ }
+ if (!checkEntry(pRef, iref, idx))
+ return false;
+ updateSlotRemove(pRef, idx);
+
+ pRef->table[idx] = NULL;
+ pRef->segmentState.parts.numHoles++;
+ LOGV("+++ left hole at %d, holes=%d\n",
+ idx, pRef->segmentState.parts.numHoles);
+ }
+
+ return true;
+}
+
+/*
+ * This is a qsort() callback. We sort Object* by class, allocation size,
+ * and then by the Object* itself.
+ */
+static int compareObject(const void* vobj1, const void* vobj2)
+{
+ Object* obj1 = *((Object**) vobj1);
+ Object* obj2 = *((Object**) vobj2);
+
+ /* ensure null references appear at the end */
+ if (obj1 == NULL) {
+ if (obj2 == NULL) {
+ return 0;
+ } else {
+ return 1;
+ }
+ } else if (obj2 == NULL) {
+ return -1;
+ }
+
+ if (obj1->clazz != obj2->clazz) {
+ return (u1*)obj1->clazz - (u1*)obj2->clazz;
+ } else {
+ int size1 = dvmObjectSizeInHeap(obj1);
+ int size2 = dvmObjectSizeInHeap(obj2);
+ if (size1 != size2) {
+ return size1 - size2;
+ } else {
+ return (u1*)obj1 - (u1*)obj2;
+ }
+ }
+}
+
+/*
+ * Log an object with some additional info.
+ *
+ * Pass in the number of additional elements that are identical to or
+ * equivalent to the original.
+ */
+static void logObject(Object* obj, int size, int identical, int equiv)
+{
+ if (obj == NULL) {
+ LOGW(" NULL reference (count=%d)\n", equiv);
+ return;
+ }
+
+ if (identical + equiv != 0) {
+ LOGW("%5d of %s %dB (%d unique)\n", identical + equiv +1,
+ obj->clazz->descriptor, size, equiv +1);
+ } else {
+ LOGW("%5d of %s %dB\n", identical + equiv +1,
+ obj->clazz->descriptor, size);
+ }
+}
+
+/*
+ * Dump the contents of a IndirectRefTable to the log.
+ */
+void dvmDumpIndirectRefTable(const IndirectRefTable* pRef, const char* descr)
+{
+ const int kLast = 10;
+ int count = dvmIndirectRefTableEntries(pRef);
+ Object** refs;
+ int i;
+
+ if (count == 0) {
+ LOGW("Reference table has no entries\n");
+ return;
+ }
+ assert(count > 0);
+
+ /*
+ * Dump the most recent N entries. If there are holes, we will show
+ * fewer than N.
+ */
+ LOGW("Last %d entries in %s reference table:\n", kLast, descr);
+ refs = pRef->table; // use unsorted list
+ int size;
+ int start = count - kLast;
+ if (start < 0)
+ start = 0;
+
+ for (i = start; i < count; i++) {
+ if (refs[i] == NULL)
+ continue;
+ size = dvmObjectSizeInHeap(refs[i]);
+ Object* ref = refs[i];
+ if (ref->clazz == gDvm.classJavaLangClass) {
+ ClassObject* clazz = (ClassObject*) ref;
+ LOGW("%5d: %p cls=%s '%s' (%d bytes)\n", i, ref,
+ (refs[i] == NULL) ? "-" : ref->clazz->descriptor,
+ clazz->descriptor, size);
+ } else {
+ LOGW("%5d: %p cls=%s (%d bytes)\n", i, ref,
+ (refs[i] == NULL) ? "-" : ref->clazz->descriptor, size);
+ }
+ }
+
+ /*
+ * Make a copy of the table, and sort it.
+ *
+ * The NULL "holes" wind up at the end, so we can strip them off easily.
+ */
+ Object** tableCopy = (Object**)malloc(sizeof(Object*) * count);
+ memcpy(tableCopy, pRef->table, sizeof(Object*) * count);
+ qsort(tableCopy, count, sizeof(Object*), compareObject);
+ refs = tableCopy; // use sorted list
+
+ if (false) {
+ int q;
+ for (q = 0; q < count; q++)
+ LOGI("%d %p\n", q, refs[q]);
+ }
+
+ int holes = 0;
+ while (refs[count-1] == NULL) {
+ count--;
+ holes++;
+ }
+
+ /*
+ * Dump uniquified table summary. While we're at it, generate a
+ * cumulative total amount of pinned memory based on the unique entries.
+ */
+ LOGW("%s reference table summary (%d entries / %d holes):\n",
+ descr, count, holes);
+ int equiv, identical, total;
+ total = equiv = identical = 0;
+ for (i = 1; i < count; i++) {
+ size = dvmObjectSizeInHeap(refs[i-1]);
+
+ if (refs[i] == refs[i-1]) {
+ /* same reference, added more than once */
+ identical++;
+ } else if (refs[i]->clazz == refs[i-1]->clazz &&
+ (int) dvmObjectSizeInHeap(refs[i]) == size)
+ {
+ /* same class / size, different object */
+ total += size;
+ equiv++;
+ } else {
+ /* different class */
+ total += size;
+ logObject(refs[i-1], size, identical, equiv);
+ equiv = identical = 0;
+ }
+ }
+
+ /* handle the last entry (everything above outputs refs[i-1]) */
+ size = (refs[count-1] == NULL) ? 0 : dvmObjectSizeInHeap(refs[count-1]);
+ total += size;
+ logObject(refs[count-1], size, identical, equiv);
+
+ LOGW("Memory held directly by native code is %d bytes\n", total);
+ free(tableCopy);
+}
diff --git a/vm/IndirectRefTable.h b/vm/IndirectRefTable.h
new file mode 100644
index 0000000..6a4db04
--- /dev/null
+++ b/vm/IndirectRefTable.h
@@ -0,0 +1,382 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DALVIK_INDIRECTREFTABLE
+#define _DALVIK_INDIRECTREFTABLE
+/*
+ * Maintain a table of indirect references. Used for local/global JNI
+ * references.
+ *
+ * The table contains object references that are part of the GC root set.
+ * When an object is added we return an IndirectRef that is not a valid
+ * pointer but can be used to find the original value in O(1) time.
+ * Conversions to and from indirect refs are performed on JNI method calls
+ * in and out of the VM, so they need to be very fast.
+ *
+ * To be efficient for JNI local variable storage, we need to provide
+ * operations that allow us to operate on segments of the table, where
+ * segments are pushed and popped as if on a stack. For example, deletion
+ * of an entry should only succeed if it appears in the current segment,
+ * and we want to be able to strip off the current segment quickly when
+ * a method returns. Additions to the table must be made in the current
+ * segment even if space is available in an earlier area.
+ *
+ * A new segment is created when we call into native code from interpreted
+ * code, or when we handle the JNI PushLocalFrame function.
+ *
+ * The GC must be able to scan the entire table quickly.
+ *
+ * In summary, these must be very fast:
+ * - adding or removing a segment
+ * - adding references to a new segment
+ * - converting an indirect reference back to an Object
+ * These can be a little slower, but must still be pretty quick:
+ * - adding references to a "mature" segment
+ * - removing individual references
+ * - scanning the entire table straight through
+ *
+ * If there's more than one segment, we don't guarantee that the table
+ * will fill completely before we fail due to lack of space. We do ensure
+ * that the current segment will pack tightly, which should satisfy JNI
+ * requirements (e.g. EnsureLocalCapacity).
+ *
+ * To make everything fit nicely in 32-bit integers, the maximum size of
+ * the table is capped at 64K.
+ *
+ * None of the table functions are synchronized.
+ */
+
+/*
+ * Indirect reference definition. This must be interchangeable with JNI's
+ * jobject, and it's convenient to let null be null, so we use void*.
+ *
+ * We need a 16-bit table index and a 2-bit reference type (global, local,
+ * weak global). Real object pointers will have zeroes in the low 2 or 3
+ * bits (4- or 8-byte alignment), so it's useful to put the ref type
+ * in the low bits and reserve zero as an invalid value.
+ *
+ * The remaining 14 bits can be used to detect stale indirect references.
+ * For example, if objects don't move, we can use a hash of the original
+ * Object* to make sure the entry hasn't been re-used. (If the Object*
+ * we find there doesn't match because of heap movement, we could do a
+ * secondary check on the preserved hash value; this implies that creating
+ * a global/local ref queries the hash value and forces it to be saved.)
+ * This is only done when CheckJNI is enabled.
+ *
+ * A more rigorous approach would be to put a serial number in the extra
+ * bits, and keep a copy of the serial number in a parallel table. This is
+ * easier when objects can move, but requires 2x the memory and additional
+ * memory accesses on add/get. It will catch additional problems, e.g.:
+ * create iref1 for obj, delete iref1, create iref2 for same obj, lookup
+ * iref1. A pattern based on object bits will miss this.
+ */
+typedef void* IndirectRef;
+
+/*
+ * Indirect reference kind, used as the two low bits of IndirectRef.
+ *
+ * For convenience these match up with enum jobjectRefType from jni.h.
+ */
+typedef enum IndirectRefKind {
+ kIndirectKindInvalid = 0,
+ kIndirectKindLocal = 1,
+ kIndirectKindGlobal = 2,
+ kIndirectKindWeakGlobal = 3
+} IndirectRefKind;
+
+/*
+ * Extended debugging structure. We keep a parallel array of these, one
+ * per slot in the table.
+ */
+#define kIRTPrevCount 4
+typedef struct IndirectRefSlot {
+ u4 serial; /* slot serial */
+ Object* previous[kIRTPrevCount];
+} IndirectRefSlot;
+
+/*
+ * Table definition.
+ *
+ * For the global reference table, the expected common operations are
+ * adding a new entry and removing a recently-added entry (usually the
+ * most-recently-added entry). For JNI local references, the common
+ * operations are adding a new entry and removing an entire table segment.
+ *
+ * If "allocEntries" is not equal to "maxEntries", the table may expand
+ * when entries are added, which means the memory may move. If you want
+ * to keep pointers into "table" rather than offsets, you must use a
+ * fixed-size table.
+ *
+ * If we delete entries from the middle of the list, we will be left with
+ * "holes". We track the number of holes so that, when adding new elements,
+ * we can quickly decide to do a trivial append or go slot-hunting.
+ *
+ * When the top-most entry is removed, any holes immediately below it are
+ * also removed. Thus, deletion of an entry may reduce "topIndex" by more
+ * than one.
+ *
+ * To get the desired behavior for JNI locals, we need to know the bottom
+ * and top of the current "segment". The top is managed internally, and
+ * the bottom is passed in as a function argument (the VM keeps it in a
+ * slot in the interpreted stack frame). When we call a native method or
+ * push a local frame, the current top index gets pushed on, and serves
+ * as the new bottom. When we pop a frame off, the value from the stack
+ * becomes the new top index, and the value stored in the previous frame
+ * becomes the new bottom.
+ *
+ * To avoid having to re-scan the table after a pop, we want to push the
+ * number of holes in the table onto the stack. Because of our 64K-entry
+ * cap, we can combine the two into a single unsigned 32-bit value.
+ * Instead of a "bottom" argument we take a "cookie", which includes the
+ * bottom index and the count of holes below the bottom.
+ *
+ * We need to minimize method call/return overhead. If we store the
+ * "cookie" externally, on the interpreted call stack, the VM can handle
+ * pushes and pops with a single 4-byte load and store. (We could also
+ * store it internally in a public structure, but the local JNI refs are
+ * logically tied to interpreted stack frames anyway.)
+ *
+ * Common alternative implementation: make IndirectRef a pointer to the
+ * actual reference slot. Instead of getting a table and doing a lookup,
+ * the lookup can be done instantly. Operations like determining the
+ * type and deleting the reference are more expensive because the table
+ * must be hunted for (i.e. you have to do a pointer comparison to see
+ * which table it's in), you can't move the table when expanding it (so
+ * realloc() is out), and tricks like serial number checking to detect
+ * stale references aren't possible (though we may be able to get similar
+ * benefits with other approaches).
+ *
+ * TODO: consider a "lastDeleteIndex" for quick hole-filling when an
+ * add immediately follows a delete; must invalidate after segment pop
+ * (which could increase the cost/complexity of method call/return).
+ * Might be worth only using it for JNI globals.
+ *
+ * TODO: may want completely different add/remove algorithms for global
+ * and local refs to improve performance. A large circular buffer might
+ * reduce the amortized cost of adding global references.
+ *
+ * TODO: if we can guarantee that the underlying storage doesn't move,
+ * e.g. by using oversized mmap regions to handle expanding tables, we may
+ * be able to avoid having to synchronize lookups. Might make sense to
+ * add a "synchronized lookup" call that takes the mutex as an argument,
+ * and either locks or doesn't lock based on internal details.
+ */
+typedef union IRTSegmentState {
+ u4 all;
+ struct {
+ u4 topIndex:16; /* index of first unused entry */
+ u4 numHoles:16; /* #of holes in entire table */
+ } parts;
+} IRTSegmentState;
+typedef struct IndirectRefTable {
+ /* semi-public - read/write by interpreter in native call handler */
+ IRTSegmentState segmentState;
+
+ /* semi-public - read-only during GC scan; pointer must not be kept */
+ Object** table; /* bottom of the stack */
+
+ /* private */
+ IndirectRefSlot* slotData; /* extended debugging info */
+ int allocEntries; /* #of entries we have space for */
+ int maxEntries; /* max #of entries allowed */
+ IndirectRefKind kind; /* bit mask, ORed into all irefs */
+
+ // TODO: want hole-filling stats (#of holes filled, total entries scanned)
+ // for performance evaluation.
+} IndirectRefTable;
+
+/* use as initial value for "cookie", and when table has only one segment */
+#define IRT_FIRST_SEGMENT 0
+
+/*
+ * (This is PRIVATE, but we want it inside other inlines in this header.)
+ *
+ * Indirectify the object.
+ *
+ * The object pointer itself is subject to relocation in some GC
+ * implementations, so we shouldn't really be using it here.
+ */
+INLINE IndirectRef dvmObjectToIndirectRef(IndirectRefTable* pRef,
+ Object* obj, u4 tableIndex, IndirectRefKind kind)
+{
+ assert(tableIndex < 65536);
+ //u4 objChunk = (((u4) obj >> 3) ^ ((u4) obj >> 19)) & 0x3fff;
+ //u4 uref = objChunk << 18 | (tableIndex << 2) | kind;
+ u4 serialChunk = pRef->slotData[tableIndex].serial;
+ u4 uref = serialChunk << 20 | (tableIndex << 2) | kind;
+ return (IndirectRef) uref;
+}
+
+/*
+ * (This is PRIVATE, but we want it inside other inlines in this header.)
+ *
+ * Extract the table index from an indirect reference.
+ */
+INLINE u4 dvmIndirectRefToIndex(IndirectRef iref)
+{
+ u4 uref = (u4) iref;
+ return (uref >> 2) & 0xffff;
+}
+
+/*
+ * Determine what kind of indirect reference this is.
+ */
+INLINE IndirectRefKind dvmGetIndirectRefType(IndirectRef iref)
+{
+ return (u4) iref & 0x03;
+}
+
+/*
+ * Initialize an IndirectRefTable.
+ *
+ * If "initialCount" != "maxCount", the table will expand as required.
+ *
+ * "kind" should be Local or Global. The Global table may also hold
+ * WeakGlobal refs.
+ *
+ * Returns "false" if table allocation fails.
+ */
+bool dvmInitIndirectRefTable(IndirectRefTable* pRef, int initialCount,
+ int maxCount, IndirectRefKind kind);
+
+/*
+ * Clear out the contents, freeing allocated storage. Does not free "pRef".
+ *
+ * You must call dvmInitReferenceTable() before you can re-use this table.
+ */
+void dvmClearIndirectRefTable(IndirectRefTable* pRef);
+
+/*
+ * Start a new segment at the top of the table.
+ *
+ * Returns an opaque 32-bit value that must be provided when the segment
+ * is to be removed.
+ *
+ * IMPORTANT: this is implemented as a single instruction in mterp, rather
+ * than a call here. You can add debugging aids for the C-language
+ * interpreters, but the basic implementation may not change.
+ */
+INLINE u4 dvmPushIndirectRefTableSegment(IndirectRefTable* pRef)
+{
+ return pRef->segmentState.all;
+}
+
+/* extra debugging checks */
+bool dvmPopIndirectRefTableSegmentCheck(IndirectRefTable* pRef, u4 cookie);
+
+/*
+ * Remove one or more segments from the top. The table entry identified
+ * by "cookie" becomes the new top-most entry.
+ *
+ * IMPORTANT: this is implemented as a single instruction in mterp, rather
+ * than a call here. You can add debugging aids for the C-language
+ * interpreters, but the basic implementation must not change.
+ */
+INLINE void dvmPopIndirectRefTableSegment(IndirectRefTable* pRef, u4 cookie)
+{
+ dvmPopIndirectRefTableSegmentCheck(pRef, cookie);
+ pRef->segmentState.all = cookie;
+}
+
+/*
+ * Return the #of entries in the entire table. This includes holes, and
+ * so may be larger than the actual number of "live" entries.
+ */
+INLINE size_t dvmIndirectRefTableEntries(const IndirectRefTable* pRef)
+{
+ return pRef->segmentState.parts.topIndex;
+}
+
+/*
+ * Returns "true" if the table is full. The table is considered full if
+ * we would need to expand it to add another entry to the current segment.
+ */
+INLINE size_t dvmIsIndirectRefTableFull(const IndirectRefTable* pRef)
+{
+ return dvmIndirectRefTableEntries(pRef) == (size_t)pRef->allocEntries;
+}
+
+/*
+ * Add a new entry. "obj" must be a valid non-NULL object reference
+ * (though it's okay if it's not fully-formed, e.g. the result from
+ * dvmMalloc doesn't have obj->clazz set).
+ *
+ * Returns NULL if the table is full (max entries reached, or alloc
+ * failed during expansion).
+ */
+IndirectRef dvmAddToIndirectRefTable(IndirectRefTable* pRef, u4 cookie,
+ Object* obj);
+
+/*
+ * Add a new entry at the end. Similar to Add but does not usually attempt
+ * to fill in holes. This is only appropriate to use right after a new
+ * segment has been pushed.
+ *
+ * (This is intended for use when calling into a native JNI method, so
+ * performance is critical.)
+ */
+INLINE IndirectRef dvmAppendToIndirectRefTable(IndirectRefTable* pRef,
+ u4 cookie, Object* obj)
+{
+ int topIndex = pRef->segmentState.parts.topIndex;
+ if (topIndex == pRef->allocEntries) {
+ /* up against alloc or max limit, call the fancy version */
+ return dvmAddToIndirectRefTable(pRef, cookie, obj);
+ } else {
+ IndirectRef result = dvmObjectToIndirectRef(pRef, obj, topIndex,
+ pRef->kind);
+ pRef->table[topIndex++] = obj;
+ pRef->segmentState.parts.topIndex = topIndex;
+ return result;
+ }
+}
+
+/* extra debugging checks */
+bool dvmGetFromIndirectRefTableCheck(IndirectRefTable* pRef, IndirectRef iref);
+
+/*
+ * Given an IndirectRef in the table, return the Object it refers to.
+ *
+ * Returns NULL if iref is invalid.
+ */
+INLINE Object* dvmGetFromIndirectRefTable(IndirectRefTable* pRef,
+ IndirectRef iref)
+{
+ if (!dvmGetFromIndirectRefTableCheck(pRef, iref))
+ return NULL;
+
+ int idx = dvmIndirectRefToIndex(iref);
+ return pRef->table[idx];
+}
+
+/*
+ * Remove an existing entry.
+ *
+ * If the entry is not between the current top index and the bottom index
+ * specified by the cookie, we don't remove anything. This is the behavior
+ * required by JNI's DeleteLocalRef function.
+ *
+ * Returns "false" if nothing was removed.
+ */
+bool dvmRemoveFromIndirectRefTable(IndirectRefTable* pRef, u4 cookie,
+ IndirectRef iref);
+
+/*
+ * Dump the contents of a reference table to the log file.
+ */
+void dvmDumpIndirectRefTable(const IndirectRefTable* pRef, const char* descr);
+
+#endif /*_DALVIK_INDIRECTREFTABLE*/
diff --git a/vm/Init.c b/vm/Init.c
new file mode 100644
index 0000000..b82dd39
--- /dev/null
+++ b/vm/Init.c
@@ -0,0 +1,1748 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik initialization, shutdown, and command-line argument processing.
+ */
+#include "Dalvik.h"
+#include "test/Test.h"
+#include "mterp/Mterp.h"
+#include "Hash.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <limits.h>
+#include <ctype.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#define kMinHeapStartSize (1*1024*1024)
+#define kMinHeapSize (2*1024*1024)
+#define kMaxHeapSize (1*1024*1024*1024)
+
+/*
+ * Register VM-agnostic native methods for system classes.
+ */
+extern int jniRegisterSystemMethods(JNIEnv* env);
+
+/* fwd */
+static bool registerSystemNatives(JNIEnv* pEnv);
+static bool dvmInitJDWP(void);
+static bool dvmInitZygote(void);
+
+
+/* global state */
+struct DvmGlobals gDvm;
+
+/* JIT-specific global state */
+#if defined(WITH_JIT)
+struct DvmJitGlobals gDvmJit;
+
+#if defined(WITH_JIT_TUNING)
+/*
+ * Track the number of hits in the inline cache for predicted chaining.
+ * Use an ugly global variable here since it is accessed in assembly code.
+ */
+int gDvmICHitCount;
+#endif
+
+#endif
+
+/*
+ * Show usage.
+ *
+ * We follow the tradition of unhyphenated compound words.
+ */
+static void dvmUsage(const char* progName)
+{
+ dvmFprintf(stderr, "%s: [options] class [argument ...]\n", progName);
+ dvmFprintf(stderr, "%s: [options] -jar file.jar [argument ...]\n",progName);
+ dvmFprintf(stderr, "\n");
+ dvmFprintf(stderr, "The following standard options are recognized:\n");
+ dvmFprintf(stderr, " -classpath classpath\n");
+ dvmFprintf(stderr, " -Dproperty=value\n");
+ dvmFprintf(stderr, " -verbose:tag ('gc', 'jni', or 'class')\n");
+ dvmFprintf(stderr, " -ea[:<package name>... |:<class name>]\n");
+ dvmFprintf(stderr, " -da[:<package name>... |:<class name>]\n");
+ dvmFprintf(stderr, " (-enableassertions, -disableassertions)\n");
+ dvmFprintf(stderr, " -esa\n");
+ dvmFprintf(stderr, " -dsa\n");
+ dvmFprintf(stderr,
+ " (-enablesystemassertions, -disablesystemassertions)\n");
+ dvmFprintf(stderr, " -showversion\n");
+ dvmFprintf(stderr, " -help\n");
+ dvmFprintf(stderr, "\n");
+ dvmFprintf(stderr, "The following extended options are recognized:\n");
+ dvmFprintf(stderr, " -Xrunjdwp:<options>\n");
+ dvmFprintf(stderr, " -Xbootclasspath:bootclasspath\n");
+ dvmFprintf(stderr, " -Xcheck:tag (e.g. 'jni')\n");
+ dvmFprintf(stderr, " -XmsN (min heap, must be multiple of 1K, >= 1MB)\n");
+ dvmFprintf(stderr, " -XmxN (max heap, must be multiple of 1K, >= 2MB)\n");
+ dvmFprintf(stderr, " -XssN (stack size, >= %dKB, <= %dKB)\n",
+ kMinStackSize / 1024, kMaxStackSize / 1024);
+ dvmFprintf(stderr, " -Xverify:{none,remote,all}\n");
+ dvmFprintf(stderr, " -Xrs\n");
+#if defined(WITH_JIT)
+ dvmFprintf(stderr,
+ " -Xint (extended to accept ':portable', ':fast' and ':jit')\n");
+#else
+ dvmFprintf(stderr,
+ " -Xint (extended to accept ':portable' and ':fast')\n");
+#endif
+ dvmFprintf(stderr, "\n");
+ dvmFprintf(stderr, "These are unique to Dalvik:\n");
+ dvmFprintf(stderr, " -Xzygote\n");
+ dvmFprintf(stderr, " -Xdexopt:{none,verified,all}\n");
+ dvmFprintf(stderr, " -Xnoquithandler\n");
+ dvmFprintf(stderr,
+ " -Xjnigreflimit:N (must be multiple of 100, >= 200)\n");
+ dvmFprintf(stderr, " -Xjniopts:{warnonly,forcecopy}\n");
+ dvmFprintf(stderr, " -Xjnitrace:substring (eg NativeClass or nativeMethod)\n");
+ dvmFprintf(stderr, " -Xdeadlockpredict:{off,warn,err,abort}\n");
+ dvmFprintf(stderr, " -Xstacktracefile:<filename>\n");
+ dvmFprintf(stderr, " -Xgc:[no]precise\n");
+ dvmFprintf(stderr, " -Xgc:[no]preverify\n");
+ dvmFprintf(stderr, " -Xgc:[no]postverify\n");
+ dvmFprintf(stderr, " -Xgc:[no]concurrent\n");
+ dvmFprintf(stderr, " -Xgc:[no]verifycardtable\n");
+ dvmFprintf(stderr, " -Xgenregmap\n");
+ dvmFprintf(stderr, " -Xcheckdexsum\n");
+#if defined(WITH_JIT)
+ dvmFprintf(stderr, " -Xincludeselectedop\n");
+ dvmFprintf(stderr, " -Xjitop:hexopvalue[-endvalue]"
+ "[,hexopvalue[-endvalue]]*\n");
+ dvmFprintf(stderr, " -Xincludeselectedmethod\n");
+ dvmFprintf(stderr, " -Xjitthreshold:decimalvalue\n");
+ dvmFprintf(stderr, " -Xjitblocking\n");
+ dvmFprintf(stderr, " -Xjitmethod:signature[,signature]* "
+ "(eg Ljava/lang/String\\;replace)\n");
+ dvmFprintf(stderr, " -Xjitcheckcg\n");
+ dvmFprintf(stderr, " -Xjitverbose\n");
+ dvmFprintf(stderr, " -Xjitprofile\n");
+ dvmFprintf(stderr, " -Xjitdisableopt\n");
+#endif
+ dvmFprintf(stderr, "\n");
+ dvmFprintf(stderr, "Configured with:"
+ " debugger"
+ " profiler"
+#ifdef WITH_MONITOR_TRACKING
+ " monitor_tracking"
+#endif
+#ifdef WITH_DEADLOCK_PREDICTION
+ " deadlock_prediction"
+#endif
+#ifdef WITH_HPROF
+ " hprof"
+#endif
+#ifdef WITH_HPROF_STACK
+ " hprof_stack"
+#endif
+#ifdef WITH_ALLOC_LIMITS
+ " alloc_limits"
+#endif
+#ifdef WITH_TRACKREF_CHECKS
+ " trackref_checks"
+#endif
+#ifdef WITH_INSTR_CHECKS
+ " instr_checks"
+#endif
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+ " extra_object_validation"
+#endif
+#ifdef WITH_EXTRA_GC_CHECKS
+ " extra_gc_checks"
+#endif
+#if !defined(NDEBUG) && defined(WITH_DALVIK_ASSERT)
+ " dalvik_assert"
+#endif
+#ifdef WITH_JNI_STACK_CHECK
+ " jni_stack_check"
+#endif
+#ifdef EASY_GDB
+ " easy_gdb"
+#endif
+#ifdef CHECK_MUTEX
+ " check_mutex"
+#endif
+#ifdef PROFILE_FIELD_ACCESS
+ " profile_field_access"
+#endif
+#if defined(WITH_JIT)
+ " jit(" ARCH_VARIANT ")"
+#endif
+#if defined(WITH_SELF_VERIFICATION)
+ " self_verification"
+#endif
+#if ANDROID_SMP != 0
+ " smp"
+#endif
+ );
+#ifdef DVM_SHOW_EXCEPTION
+ dvmFprintf(stderr, " show_exception=%d", DVM_SHOW_EXCEPTION);
+#endif
+ dvmFprintf(stderr, "\n\n");
+}
+
+/*
+ * Show helpful information on JDWP options.
+ */
+static void showJdwpHelp(void)
+{
+ dvmFprintf(stderr,
+ "Example: -Xrunjdwp:transport=dt_socket,address=8000,server=y\n");
+ dvmFprintf(stderr,
+ "Example: -Xrunjdwp:transport=dt_socket,address=localhost:6500,server=n\n");
+}
+
+/*
+ * Show version and copyright info.
+ */
+static void showVersion(void)
+{
+ dvmFprintf(stdout, "DalvikVM version %d.%d.%d\n",
+ DALVIK_MAJOR_VERSION, DALVIK_MINOR_VERSION, DALVIK_BUG_VERSION);
+ dvmFprintf(stdout,
+ "Copyright (C) 2007 The Android Open Source Project\n\n"
+ "This software is built from source code licensed under the "
+ "Apache License,\n"
+ "Version 2.0 (the \"License\"). You may obtain a copy of the "
+ "License at\n\n"
+ " http://www.apache.org/licenses/LICENSE-2.0\n\n"
+ "See the associated NOTICE file for this software for further "
+ "details.\n");
+}
+
+/*
+ * Parse a string of the form /[0-9]+[kKmMgG]?/, which is used to specify
+ * memory sizes. [kK] indicates kilobytes, [mM] megabytes, and
+ * [gG] gigabytes.
+ *
+ * "s" should point just past the "-Xm?" part of the string.
+ * "min" specifies the lowest acceptable value described by "s".
+ * "div" specifies a divisor, e.g. 1024 if the value must be a multiple
+ * of 1024.
+ *
+ * The spec says the -Xmx and -Xms options must be multiples of 1024. It
+ * doesn't say anything about -Xss.
+ *
+ * Returns 0 (a useless size) if "s" is malformed or specifies a low or
+ * non-evenly-divisible value.
+ */
+static unsigned int dvmParseMemOption(const char *s, unsigned int div)
+{
+ /* strtoul accepts a leading [+-], which we don't want,
+ * so make sure our string starts with a decimal digit.
+ */
+ if (isdigit(*s)) {
+ const char *s2;
+ unsigned int val;
+
+ val = (unsigned int)strtoul(s, (char **)&s2, 10);
+ if (s2 != s) {
+ /* s2 should be pointing just after the number.
+ * If this is the end of the string, the user
+ * has specified a number of bytes. Otherwise,
+ * there should be exactly one more character
+ * that specifies a multiplier.
+ */
+ if (*s2 != '\0') {
+ char c;
+
+ /* The remainder of the string is either a single multiplier
+ * character, or nothing to indicate that the value is in
+ * bytes.
+ */
+ c = *s2++;
+ if (*s2 == '\0') {
+ unsigned int mul;
+
+ if (c == '\0') {
+ mul = 1;
+ } else if (c == 'k' || c == 'K') {
+ mul = 1024;
+ } else if (c == 'm' || c == 'M') {
+ mul = 1024 * 1024;
+ } else if (c == 'g' || c == 'G') {
+ mul = 1024 * 1024 * 1024;
+ } else {
+ /* Unknown multiplier character.
+ */
+ return 0;
+ }
+
+ if (val <= UINT_MAX / mul) {
+ val *= mul;
+ } else {
+ /* Clamp to a multiple of 1024.
+ */
+ val = UINT_MAX & ~(1024-1);
+ }
+ } else {
+ /* There's more than one character after the
+ * numeric part.
+ */
+ return 0;
+ }
+ }
+
+ /* The man page says that a -Xm value must be
+ * a multiple of 1024.
+ */
+ if (val % div == 0) {
+ return val;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Handle one of the JDWP name/value pairs.
+ *
+ * JDWP options are:
+ * help: if specified, show help message and bail
+ * transport: may be dt_socket or dt_shmem
+ * address: for dt_socket, "host:port", or just "port" when listening
+ * server: if "y", wait for debugger to attach; if "n", attach to debugger
+ * timeout: how long to wait for debugger to connect / listen
+ *
+ * Useful with server=n (these aren't supported yet):
+ * onthrow=<exception-name>: connect to debugger when exception thrown
+ * onuncaught=y|n: connect to debugger when uncaught exception thrown
+ * launch=<command-line>: launch the debugger itself
+ *
+ * The "transport" option is required, as is "address" if server=n.
+ */
+static bool handleJdwpOption(const char* name, const char* value)
+{
+ if (strcmp(name, "transport") == 0) {
+ if (strcmp(value, "dt_socket") == 0) {
+ gDvm.jdwpTransport = kJdwpTransportSocket;
+ } else if (strcmp(value, "dt_android_adb") == 0) {
+ gDvm.jdwpTransport = kJdwpTransportAndroidAdb;
+ } else {
+ LOGE("JDWP transport '%s' not supported\n", value);
+ return false;
+ }
+ } else if (strcmp(name, "server") == 0) {
+ if (*value == 'n')
+ gDvm.jdwpServer = false;
+ else if (*value == 'y')
+ gDvm.jdwpServer = true;
+ else {
+ LOGE("JDWP option 'server' must be 'y' or 'n'\n");
+ return false;
+ }
+ } else if (strcmp(name, "suspend") == 0) {
+ if (*value == 'n')
+ gDvm.jdwpSuspend = false;
+ else if (*value == 'y')
+ gDvm.jdwpSuspend = true;
+ else {
+ LOGE("JDWP option 'suspend' must be 'y' or 'n'\n");
+ return false;
+ }
+ } else if (strcmp(name, "address") == 0) {
+ /* this is either <port> or <host>:<port> */
+ const char* colon = strchr(value, ':');
+ char* end;
+ long port;
+
+ if (colon != NULL) {
+ free(gDvm.jdwpHost);
+ gDvm.jdwpHost = (char*) malloc(colon - value +1);
+ strncpy(gDvm.jdwpHost, value, colon - value +1);
+ gDvm.jdwpHost[colon-value] = '\0';
+ value = colon + 1;
+ }
+ if (*value == '\0') {
+ LOGE("JDWP address missing port\n");
+ return false;
+ }
+ port = strtol(value, &end, 10);
+ if (*end != '\0') {
+ LOGE("JDWP address has junk in port field '%s'\n", value);
+ return false;
+ }
+ gDvm.jdwpPort = port;
+ } else if (strcmp(name, "launch") == 0 ||
+ strcmp(name, "onthrow") == 0 ||
+ strcmp(name, "oncaught") == 0 ||
+ strcmp(name, "timeout") == 0)
+ {
+ /* valid but unsupported */
+ LOGI("Ignoring JDWP option '%s'='%s'\n", name, value);
+ } else {
+ LOGI("Ignoring unrecognized JDWP option '%s'='%s'\n", name, value);
+ }
+
+ return true;
+}
+
+/*
+ * Parse the latter half of a -Xrunjdwp/-agentlib:jdwp= string, e.g.:
+ * "transport=dt_socket,address=8000,server=y,suspend=n"
+ */
+static bool parseJdwpOptions(const char* str)
+{
+ char* mangle = strdup(str);
+ char* name = mangle;
+ bool result = false;
+
+ /*
+ * Process all of the name=value pairs.
+ */
+ while (true) {
+ char* value;
+ char* comma;
+
+ value = strchr(name, '=');
+ if (value == NULL) {
+ LOGE("JDWP opts: garbage at '%s'\n", name);
+ goto bail;
+ }
+
+ comma = strchr(name, ','); // use name, not value, for safety
+ if (comma != NULL) {
+ if (comma < value) {
+ LOGE("JDWP opts: found comma before '=' in '%s'\n", mangle);
+ goto bail;
+ }
+ *comma = '\0';
+ }
+
+ *value++ = '\0'; // stomp the '='
+
+ if (!handleJdwpOption(name, value))
+ goto bail;
+
+ if (comma == NULL) {
+ /* out of options */
+ break;
+ }
+ name = comma+1;
+ }
+
+ /*
+ * Make sure the combination of arguments makes sense.
+ */
+ if (gDvm.jdwpTransport == kJdwpTransportUnknown) {
+ LOGE("JDWP opts: must specify transport\n");
+ goto bail;
+ }
+ if (!gDvm.jdwpServer && (gDvm.jdwpHost == NULL || gDvm.jdwpPort == 0)) {
+ LOGE("JDWP opts: when server=n, must specify host and port\n");
+ goto bail;
+ }
+ // transport mandatory
+ // outbound server address
+
+ gDvm.jdwpConfigured = true;
+ result = true;
+
+bail:
+ free(mangle);
+ return result;
+}
+
+/*
+ * Handle one of the four kinds of assertion arguments.
+ *
+ * "pkgOrClass" is the last part of an enable/disable line. For a package
+ * the arg looks like "-ea:com.google.fubar...", for a class it looks
+ * like "-ea:com.google.fubar.Wahoo". The string we get starts at the ':'.
+ *
+ * For system assertions (-esa/-dsa), "pkgOrClass" is NULL.
+ *
+ * Multiple instances of these arguments can be specified, e.g. you can
+ * enable assertions for a package and then disable them for one class in
+ * the package.
+ */
+static bool enableAssertions(const char* pkgOrClass, bool enable)
+{
+ AssertionControl* pCtrl = &gDvm.assertionCtrl[gDvm.assertionCtrlCount++];
+ pCtrl->enable = enable;
+
+ if (pkgOrClass == NULL) {
+ /* enable or disable for all system classes */
+ pCtrl->isPackage = false;
+ pCtrl->pkgOrClass = NULL;
+ pCtrl->pkgOrClassLen = 0;
+ } else {
+ if (*pkgOrClass == '\0') {
+ /* global enable/disable for all but system */
+ pCtrl->isPackage = false;
+ pCtrl->pkgOrClass = strdup("");
+ pCtrl->pkgOrClassLen = 0;
+ } else {
+ pCtrl->pkgOrClass = dvmDotToSlash(pkgOrClass+1); // skip ':'
+ if (pCtrl->pkgOrClass == NULL) {
+ /* can happen if class name includes an illegal '/' */
+ LOGW("Unable to process assertion arg '%s'\n", pkgOrClass);
+ return false;
+ }
+
+ int len = strlen(pCtrl->pkgOrClass);
+ if (len >= 3 && strcmp(pCtrl->pkgOrClass + len-3, "///") == 0) {
+ /* mark as package, truncate two of the three slashes */
+ pCtrl->isPackage = true;
+ *(pCtrl->pkgOrClass + len-2) = '\0';
+ pCtrl->pkgOrClassLen = len - 2;
+ } else {
+ /* just a class */
+ pCtrl->isPackage = false;
+ pCtrl->pkgOrClassLen = len;
+ }
+ }
+ }
+
+ return true;
+}
+
+/*
+ * Turn assertions on when requested to do so by the Zygote.
+ *
+ * This is a bit sketchy. We can't (easily) go back and fiddle with all
+ * of the classes that have already been initialized, so this only
+ * affects classes that have yet to be loaded. If some or all assertions
+ * have been enabled through some other means, we don't want to mess with
+ * it here, so we do nothing. Finally, we assume that there's room in
+ * "assertionCtrl" to hold at least one entry; this is guaranteed by the
+ * allocator.
+ *
+ * This must only be called from the main thread during zygote init.
+ */
+void dvmLateEnableAssertions(void)
+{
+ if (gDvm.assertionCtrl == NULL) {
+ LOGD("Not late-enabling assertions: no assertionCtrl array\n");
+ return;
+ } else if (gDvm.assertionCtrlCount != 0) {
+ LOGD("Not late-enabling assertions: some asserts already configured\n");
+ return;
+ }
+ LOGD("Late-enabling assertions\n");
+
+ /* global enable for all but system */
+ AssertionControl* pCtrl = gDvm.assertionCtrl;
+ pCtrl->pkgOrClass = strdup("");
+ pCtrl->pkgOrClassLen = 0;
+ pCtrl->isPackage = false;
+ pCtrl->enable = true;
+ gDvm.assertionCtrlCount = 1;
+}
+
+
+/*
+ * Release memory associated with the AssertionCtrl array.
+ */
+static void freeAssertionCtrl(void)
+{
+ int i;
+
+ for (i = 0; i < gDvm.assertionCtrlCount; i++)
+ free(gDvm.assertionCtrl[i].pkgOrClass);
+ free(gDvm.assertionCtrl);
+}
+
+#if defined(WITH_JIT)
+/* Parse -Xjitop to selectively turn on/off certain opcodes for JIT */
+static void processXjitop(const char *opt)
+{
+ if (opt[7] == ':') {
+ const char *startPtr = &opt[8];
+ char *endPtr = NULL;
+
+ do {
+ long startValue, endValue;
+
+ startValue = strtol(startPtr, &endPtr, 16);
+ if (startPtr != endPtr) {
+ /* Just in case value is out of range */
+ startValue &= 0xff;
+
+ if (*endPtr == '-') {
+ endValue = strtol(endPtr+1, &endPtr, 16);
+ endValue &= 0xff;
+ } else {
+ endValue = startValue;
+ }
+
+ for (; startValue <= endValue; startValue++) {
+ LOGW("Dalvik opcode %x is selected for debugging",
+ (unsigned int) startValue);
+ /* Mark the corresponding bit to 1 */
+ gDvmJit.opList[startValue >> 3] |=
+ 1 << (startValue & 0x7);
+ }
+
+ if (*endPtr == 0) {
+ break;
+ }
+
+ startPtr = endPtr + 1;
+
+ continue;
+ } else {
+ if (*endPtr != 0) {
+ dvmFprintf(stderr,
+ "Warning: Unrecognized opcode value substring "
+ "%s\n", endPtr);
+ }
+ break;
+ }
+ } while (1);
+ } else {
+ int i;
+ for (i = 0; i < 32; i++) {
+ gDvmJit.opList[i] = 0xff;
+ }
+ dvmFprintf(stderr, "Warning: select all opcodes\n");
+ }
+}
+
+/* Parse -Xjitmethod to selectively turn on/off certain methods for JIT */
+static void processXjitmethod(const char *opt)
+{
+ char *buf = strdup(&opt[12]);
+ char *start, *end;
+
+ gDvmJit.methodTable = dvmHashTableCreate(8, NULL);
+
+ start = buf;
+ /*
+ * Break comma-separated method signatures and enter them into the hash
+ * table individually.
+ */
+ do {
+ int hashValue;
+
+ end = strchr(start, ',');
+ if (end) {
+ *end = 0;
+ }
+
+ hashValue = dvmComputeUtf8Hash(start);
+
+ dvmHashTableLookup(gDvmJit.methodTable, hashValue,
+ strdup(start),
+ (HashCompareFunc) strcmp, true);
+ if (end) {
+ start = end + 1;
+ } else {
+ break;
+ }
+ } while (1);
+ free(buf);
+}
+#endif
+
+/*
+ * Process an argument vector full of options. Unlike standard C programs,
+ * argv[0] does not contain the name of the program.
+ *
+ * If "ignoreUnrecognized" is set, we ignore options starting with "-X" or "_"
+ * that we don't recognize. Otherwise, we return with an error as soon as
+ * we see anything we can't identify.
+ *
+ * Returns 0 on success, -1 on failure, and 1 for the special case of
+ * "-version" where we want to stop without showing an error message.
+ */
+static int dvmProcessOptions(int argc, const char* const argv[],
+ bool ignoreUnrecognized)
+{
+ int i;
+
+ LOGV("VM options (%d):\n", argc);
+ for (i = 0; i < argc; i++)
+ LOGV(" %d: '%s'\n", i, argv[i]);
+
+ /*
+ * Over-allocate AssertionControl array for convenience. If allocated,
+ * the array must be able to hold at least one entry, so that the
+ * zygote-time activation can do its business.
+ */
+ assert(gDvm.assertionCtrl == NULL);
+ if (argc > 0) {
+ gDvm.assertionCtrl =
+ (AssertionControl*) malloc(sizeof(AssertionControl) * argc);
+ if (gDvm.assertionCtrl == NULL)
+ return -1;
+ assert(gDvm.assertionCtrlCount == 0);
+ }
+
+ for (i = 0; i < argc; i++) {
+ if (strcmp(argv[i], "-help") == 0) {
+ /* show usage and stop */
+ return -1;
+
+ } else if (strcmp(argv[i], "-version") == 0) {
+ /* show version and stop */
+ showVersion();
+ return 1;
+ } else if (strcmp(argv[i], "-showversion") == 0) {
+ /* show version and continue */
+ showVersion();
+
+ } else if (strcmp(argv[i], "-classpath") == 0 ||
+ strcmp(argv[i], "-cp") == 0)
+ {
+ /* set classpath */
+ if (i == argc-1) {
+ dvmFprintf(stderr, "Missing classpath path list\n");
+ return -1;
+ }
+ free(gDvm.classPathStr); /* in case we have compiled-in default */
+ gDvm.classPathStr = strdup(argv[++i]);
+
+ } else if (strncmp(argv[i], "-Xbootclasspath:",
+ sizeof("-Xbootclasspath:")-1) == 0)
+ {
+ /* set bootclasspath */
+ const char* path = argv[i] + sizeof("-Xbootclasspath:")-1;
+
+ if (*path == '\0') {
+ dvmFprintf(stderr, "Missing bootclasspath path list\n");
+ return -1;
+ }
+ free(gDvm.bootClassPathStr);
+ gDvm.bootClassPathStr = strdup(path);
+
+ } else if (strncmp(argv[i], "-Xbootclasspath/a:",
+ sizeof("-Xbootclasspath/a:")-1) == 0) {
+ const char* appPath = argv[i] + sizeof("-Xbootclasspath/a:")-1;
+
+ if (*(appPath) == '\0') {
+ dvmFprintf(stderr, "Missing appending bootclasspath path list\n");
+ return -1;
+ }
+ char* allPath;
+
+ if (asprintf(&allPath, "%s:%s", gDvm.bootClassPathStr, appPath) < 0) {
+ dvmFprintf(stderr, "Can't append to bootclasspath path list\n");
+ return -1;
+ }
+ free(gDvm.bootClassPathStr);
+ gDvm.bootClassPathStr = allPath;
+
+ } else if (strncmp(argv[i], "-Xbootclasspath/p:",
+ sizeof("-Xbootclasspath/p:")-1) == 0) {
+ const char* prePath = argv[i] + sizeof("-Xbootclasspath/p:")-1;
+
+ if (*(prePath) == '\0') {
+ dvmFprintf(stderr, "Missing prepending bootclasspath path list\n");
+ return -1;
+ }
+ char* allPath;
+
+ if (asprintf(&allPath, "%s:%s", prePath, gDvm.bootClassPathStr) < 0) {
+ dvmFprintf(stderr, "Can't prepend to bootclasspath path list\n");
+ return -1;
+ }
+ free(gDvm.bootClassPathStr);
+ gDvm.bootClassPathStr = allPath;
+
+ } else if (strncmp(argv[i], "-D", 2) == 0) {
+ /* set property */
+ dvmAddCommandLineProperty(argv[i] + 2);
+
+ } else if (strcmp(argv[i], "-jar") == 0) {
+ // TODO: handle this; name of jar should be in argv[i+1]
+ dvmFprintf(stderr, "-jar not yet handled\n");
+ assert(false);
+
+ } else if (strncmp(argv[i], "-Xms", 4) == 0) {
+ unsigned int val = dvmParseMemOption(argv[i]+4, 1024);
+ if (val != 0) {
+ if (val >= kMinHeapStartSize && val <= kMaxHeapSize) {
+ gDvm.heapSizeStart = val;
+ } else {
+ dvmFprintf(stderr,
+ "Invalid -Xms '%s', range is %dKB to %dKB\n",
+ argv[i], kMinHeapStartSize/1024, kMaxHeapSize/1024);
+ return -1;
+ }
+ } else {
+ dvmFprintf(stderr, "Invalid -Xms option '%s'\n", argv[i]);
+ return -1;
+ }
+ } else if (strncmp(argv[i], "-Xmx", 4) == 0) {
+ unsigned int val = dvmParseMemOption(argv[i]+4, 1024);
+ if (val != 0) {
+ if (val >= kMinHeapSize && val <= kMaxHeapSize) {
+ gDvm.heapSizeMax = val;
+ } else {
+ dvmFprintf(stderr,
+ "Invalid -Xmx '%s', range is %dKB to %dKB\n",
+ argv[i], kMinHeapSize/1024, kMaxHeapSize/1024);
+ return -1;
+ }
+ } else {
+ dvmFprintf(stderr, "Invalid -Xmx option '%s'\n", argv[i]);
+ return -1;
+ }
+ } else if (strncmp(argv[i], "-Xss", 4) == 0) {
+ unsigned int val = dvmParseMemOption(argv[i]+4, 1);
+ if (val != 0) {
+ if (val >= kMinStackSize && val <= kMaxStackSize) {
+ gDvm.stackSize = val;
+ } else {
+ dvmFprintf(stderr, "Invalid -Xss '%s', range is %d to %d\n",
+ argv[i], kMinStackSize, kMaxStackSize);
+ return -1;
+ }
+ } else {
+ dvmFprintf(stderr, "Invalid -Xss option '%s'\n", argv[i]);
+ return -1;
+ }
+
+ } else if (strcmp(argv[i], "-verbose") == 0 ||
+ strcmp(argv[i], "-verbose:class") == 0)
+ {
+ // JNI spec says "-verbose:gc,class" is valid, but cmd line
+ // doesn't work that way; may want to support.
+ gDvm.verboseClass = true;
+ } else if (strcmp(argv[i], "-verbose:jni") == 0) {
+ gDvm.verboseJni = true;
+ } else if (strcmp(argv[i], "-verbose:gc") == 0) {
+ gDvm.verboseGc = true;
+ } else if (strcmp(argv[i], "-verbose:shutdown") == 0) {
+ gDvm.verboseShutdown = true;
+
+ } else if (strncmp(argv[i], "-enableassertions", 17) == 0) {
+ enableAssertions(argv[i] + 17, true);
+ } else if (strncmp(argv[i], "-ea", 3) == 0) {
+ enableAssertions(argv[i] + 3, true);
+ } else if (strncmp(argv[i], "-disableassertions", 18) == 0) {
+ enableAssertions(argv[i] + 18, false);
+ } else if (strncmp(argv[i], "-da", 3) == 0) {
+ enableAssertions(argv[i] + 3, false);
+ } else if (strcmp(argv[i], "-enablesystemassertions") == 0 ||
+ strcmp(argv[i], "-esa") == 0)
+ {
+ enableAssertions(NULL, true);
+ } else if (strcmp(argv[i], "-disablesystemassertions") == 0 ||
+ strcmp(argv[i], "-dsa") == 0)
+ {
+ enableAssertions(NULL, false);
+
+ } else if (strncmp(argv[i], "-Xcheck:jni", 11) == 0) {
+ /* nothing to do now -- was handled during JNI init */
+
+ } else if (strcmp(argv[i], "-Xdebug") == 0) {
+ /* accept but ignore */
+
+ } else if (strncmp(argv[i], "-Xrunjdwp:", 10) == 0 ||
+ strncmp(argv[i], "-agentlib:jdwp=", 15) == 0)
+ {
+ const char* tail;
+
+ if (argv[i][1] == 'X')
+ tail = argv[i] + 10;
+ else
+ tail = argv[i] + 15;
+
+ if (strncmp(tail, "help", 4) == 0 || !parseJdwpOptions(tail)) {
+ showJdwpHelp();
+ return 1;
+ }
+ } else if (strcmp(argv[i], "-Xrs") == 0) {
+ gDvm.reduceSignals = true;
+ } else if (strcmp(argv[i], "-Xnoquithandler") == 0) {
+ /* disables SIGQUIT handler thread while still blocking SIGQUIT */
+ /* (useful if we don't want thread but system still signals us) */
+ gDvm.noQuitHandler = true;
+ } else if (strcmp(argv[i], "-Xzygote") == 0) {
+ gDvm.zygote = true;
+#if defined(WITH_JIT)
+ gDvmJit.runningInAndroidFramework = true;
+#endif
+ } else if (strncmp(argv[i], "-Xdexopt:", 9) == 0) {
+ if (strcmp(argv[i] + 9, "none") == 0)
+ gDvm.dexOptMode = OPTIMIZE_MODE_NONE;
+ else if (strcmp(argv[i] + 9, "verified") == 0)
+ gDvm.dexOptMode = OPTIMIZE_MODE_VERIFIED;
+ else if (strcmp(argv[i] + 9, "all") == 0)
+ gDvm.dexOptMode = OPTIMIZE_MODE_ALL;
+ else {
+ dvmFprintf(stderr, "Unrecognized dexopt option '%s'\n",argv[i]);
+ return -1;
+ }
+ } else if (strncmp(argv[i], "-Xverify:", 9) == 0) {
+ if (strcmp(argv[i] + 9, "none") == 0)
+ gDvm.classVerifyMode = VERIFY_MODE_NONE;
+ else if (strcmp(argv[i] + 9, "remote") == 0)
+ gDvm.classVerifyMode = VERIFY_MODE_REMOTE;
+ else if (strcmp(argv[i] + 9, "all") == 0)
+ gDvm.classVerifyMode = VERIFY_MODE_ALL;
+ else {
+ dvmFprintf(stderr, "Unrecognized verify option '%s'\n",argv[i]);
+ return -1;
+ }
+ } else if (strncmp(argv[i], "-Xjnigreflimit:", 15) == 0) {
+ int lim = atoi(argv[i] + 15);
+ if (lim < 200 || (lim % 100) != 0) {
+ dvmFprintf(stderr, "Bad value for -Xjnigreflimit: '%s'\n",
+ argv[i]+15);
+ return -1;
+ }
+ gDvm.jniGrefLimit = lim;
+ } else if (strncmp(argv[i], "-Xjnitrace:", 11) == 0) {
+ gDvm.jniTrace = strdup(argv[i] + 11);
+ } else if (strcmp(argv[i], "-Xlog-stdio") == 0) {
+ gDvm.logStdio = true;
+
+ } else if (strncmp(argv[i], "-Xint", 5) == 0) {
+ if (argv[i][5] == ':') {
+ if (strcmp(argv[i] + 6, "portable") == 0)
+ gDvm.executionMode = kExecutionModeInterpPortable;
+ else if (strcmp(argv[i] + 6, "fast") == 0)
+ gDvm.executionMode = kExecutionModeInterpFast;
+#ifdef WITH_JIT
+ else if (strcmp(argv[i] + 6, "jit") == 0)
+ gDvm.executionMode = kExecutionModeJit;
+#endif
+ else {
+ dvmFprintf(stderr,
+ "Warning: Unrecognized interpreter mode %s\n",argv[i]);
+ /* keep going */
+ }
+ } else {
+ /* disable JIT if it was enabled by default */
+ gDvm.executionMode = kExecutionModeInterpFast;
+ }
+
+ } else if (strncmp(argv[i], "-Xlockprofthreshold:", 20) == 0) {
+ gDvm.lockProfThreshold = atoi(argv[i] + 20);
+
+#ifdef WITH_JIT
+ } else if (strncmp(argv[i], "-Xjitop", 7) == 0) {
+ processXjitop(argv[i]);
+ } else if (strncmp(argv[i], "-Xjitmethod", 11) == 0) {
+ processXjitmethod(argv[i]);
+ } else if (strncmp(argv[i], "-Xjitblocking", 13) == 0) {
+ gDvmJit.blockingMode = true;
+ } else if (strncmp(argv[i], "-Xjitthreshold:", 15) == 0) {
+ gDvmJit.threshold = atoi(argv[i] + 15);
+ } else if (strncmp(argv[i], "-Xincludeselectedop", 19) == 0) {
+ gDvmJit.includeSelectedOp = true;
+ } else if (strncmp(argv[i], "-Xincludeselectedmethod", 23) == 0) {
+ gDvmJit.includeSelectedMethod = true;
+ } else if (strncmp(argv[i], "-Xjitcheckcg", 12) == 0) {
+ gDvmJit.checkCallGraph = true;
+ /* Need to enable blocking mode due to stack crawling */
+ gDvmJit.blockingMode = true;
+ } else if (strncmp(argv[i], "-Xjitverbose", 12) == 0) {
+ gDvmJit.printMe = true;
+ } else if (strncmp(argv[i], "-Xjitprofile", 12) == 0) {
+ gDvmJit.profile = true;
+ } else if (strncmp(argv[i], "-Xjitdisableopt", 15) == 0) {
+ /* Disable selected optimizations */
+ if (argv[i][15] == ':') {
+ sscanf(argv[i] + 16, "%x", &gDvmJit.disableOpt);
+ /* Disable all optimizations */
+ } else {
+ gDvmJit.disableOpt = -1;
+ }
+#endif
+
+ } else if (strncmp(argv[i], "-Xdeadlockpredict:", 18) == 0) {
+#ifdef WITH_DEADLOCK_PREDICTION
+ if (strcmp(argv[i] + 18, "off") == 0)
+ gDvm.deadlockPredictMode = kDPOff;
+ else if (strcmp(argv[i] + 18, "warn") == 0)
+ gDvm.deadlockPredictMode = kDPWarn;
+ else if (strcmp(argv[i] + 18, "err") == 0)
+ gDvm.deadlockPredictMode = kDPErr;
+ else if (strcmp(argv[i] + 18, "abort") == 0)
+ gDvm.deadlockPredictMode = kDPAbort;
+ else {
+ dvmFprintf(stderr, "Bad value for -Xdeadlockpredict");
+ return -1;
+ }
+ if (gDvm.deadlockPredictMode != kDPOff)
+ LOGD("Deadlock prediction enabled (%s)\n", argv[i]+18);
+#endif
+
+ } else if (strncmp(argv[i], "-Xstacktracefile:", 17) == 0) {
+ gDvm.stackTraceFile = strdup(argv[i]+17);
+
+ } else if (strcmp(argv[i], "-Xgenregmap") == 0) {
+ gDvm.generateRegisterMaps = true;
+ LOGV("Register maps will be generated during verification\n");
+
+ } else if (strncmp(argv[i], "-Xgc:", 5) == 0) {
+ if (strcmp(argv[i] + 5, "precise") == 0)
+ gDvm.preciseGc = true;
+ else if (strcmp(argv[i] + 5, "noprecise") == 0)
+ gDvm.preciseGc = false;
+ else if (strcmp(argv[i] + 5, "preverify") == 0)
+ gDvm.preVerify = true;
+ else if (strcmp(argv[i] + 5, "nopreverify") == 0)
+ gDvm.preVerify = false;
+ else if (strcmp(argv[i] + 5, "postverify") == 0)
+ gDvm.postVerify = true;
+ else if (strcmp(argv[i] + 5, "nopostverify") == 0)
+ gDvm.postVerify = false;
+ else if (strcmp(argv[i] + 5, "concurrent") == 0)
+ gDvm.concurrentMarkSweep = true;
+ else if (strcmp(argv[i] + 5, "noconcurrent") == 0)
+ gDvm.concurrentMarkSweep = false;
+ else if (strcmp(argv[i] + 5, "verifycardtable") == 0)
+ gDvm.verifyCardTable = true;
+ else if (strcmp(argv[i] + 5, "noverifycardtable") == 0)
+ gDvm.verifyCardTable = false;
+ else {
+ dvmFprintf(stderr, "Bad value for -Xgc");
+ return -1;
+ }
+ LOGV("Precise GC configured %s\n", gDvm.preciseGc ? "ON" : "OFF");
+
+ } else if (strcmp(argv[i], "-Xcheckdexsum") == 0) {
+ gDvm.verifyDexChecksum = true;
+
+ } else if (strcmp(argv[i], "-Xprofile:wallclock") == 0) {
+ gDvm.profilerWallClock = true;
+
+ } else {
+ if (!ignoreUnrecognized) {
+ dvmFprintf(stderr, "Unrecognized option '%s'\n", argv[i]);
+ return -1;
+ }
+ }
+ }
+
+ if (gDvm.heapSizeStart > gDvm.heapSizeMax) {
+ dvmFprintf(stderr, "Heap start size must be <= heap max size\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Set defaults for fields altered or modified by arguments.
+ *
+ * Globals are initialized to 0 (a/k/a NULL or false).
+ */
+static void setCommandLineDefaults()
+{
+ const char* envStr;
+
+ envStr = getenv("CLASSPATH");
+ if (envStr != NULL)
+ gDvm.classPathStr = strdup(envStr);
+ else
+ gDvm.classPathStr = strdup(".");
+ envStr = getenv("BOOTCLASSPATH");
+ if (envStr != NULL)
+ gDvm.bootClassPathStr = strdup(envStr);
+ else
+ gDvm.bootClassPathStr = strdup(".");
+
+ /* Defaults overridden by -Xms and -Xmx.
+ * TODO: base these on a system or application-specific default
+ */
+ gDvm.heapSizeStart = 2 * 1024 * 1024; // Spec says 16MB; too big for us.
+ gDvm.heapSizeMax = 16 * 1024 * 1024; // Spec says 75% physical mem
+ gDvm.stackSize = kDefaultStackSize;
+
+ gDvm.concurrentMarkSweep = true;
+
+ /* gDvm.jdwpSuspend = true; */
+
+ /* allowed unless zygote config doesn't allow it */
+ gDvm.jdwpAllowed = true;
+
+ /* default verification and optimization modes */
+ gDvm.classVerifyMode = VERIFY_MODE_ALL;
+ gDvm.dexOptMode = OPTIMIZE_MODE_VERIFIED;
+
+ /*
+ * Default execution mode.
+ *
+ * This should probably interact with the mterp code somehow, e.g. if
+ * we know we're using the "desktop" build we should probably be
+ * using "portable" rather than "fast".
+ */
+#if defined(WITH_JIT)
+ gDvm.executionMode = kExecutionModeJit;
+#else
+ gDvm.executionMode = kExecutionModeInterpFast;
+#endif
+
+ /*
+ * SMP support is a compile-time define, but we may want to have
+ * dexopt target a differently-configured device.
+ */
+ gDvm.dexOptForSmp = (ANDROID_SMP != 0);
+}
+
+
+/*
+ * Handle a SIGBUS, which frequently occurs because somebody replaced an
+ * optimized DEX file out from under us.
+ */
+static void busCatcher(int signum, siginfo_t* info, void* context)
+{
+ void* addr = info->si_addr;
+
+ LOGE("Caught a SIGBUS (%d), addr=%p\n", signum, addr);
+
+ /*
+ * If we return at this point the SIGBUS just keeps happening, so we
+ * remove the signal handler and allow it to kill us. TODO: restore
+ * the original, which points to a debuggerd stub; if we don't then
+ * debuggerd won't be notified.
+ */
+ signal(SIGBUS, SIG_DFL);
+}
+
+/*
+ * Configure signals. We need to block SIGQUIT so that the signal only
+ * reaches the dump-stack-trace thread.
+ *
+ * This can be disabled with the "-Xrs" flag.
+ */
+static void blockSignals()
+{
+ sigset_t mask;
+ int cc;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGQUIT);
+ sigaddset(&mask, SIGUSR1); // used to initiate heap dump
+#if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
+ sigaddset(&mask, SIGUSR2); // used to investigate JIT internals
+#endif
+ //sigaddset(&mask, SIGPIPE);
+ cc = sigprocmask(SIG_BLOCK, &mask, NULL);
+ assert(cc == 0);
+
+ if (false) {
+ /* TODO: save the old sigaction in a global */
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_sigaction = busCatcher;
+ sa.sa_flags = SA_SIGINFO;
+ cc = sigaction(SIGBUS, &sa, NULL);
+ assert(cc == 0);
+ }
+}
+
+/*
+ * VM initialization. Pass in any options provided on the command line.
+ * Do not pass in the class name or the options for the class.
+ *
+ * Returns 0 on success.
+ */
+int dvmStartup(int argc, const char* const argv[], bool ignoreUnrecognized,
+ JNIEnv* pEnv)
+{
+ int i, cc;
+
+ assert(gDvm.initializing);
+
+ LOGV("VM init args (%d):\n", argc);
+ for (i = 0; i < argc; i++)
+ LOGV(" %d: '%s'\n", i, argv[i]);
+
+ setCommandLineDefaults();
+
+ /* prep properties storage */
+ if (!dvmPropertiesStartup(argc))
+ goto fail;
+
+ /*
+ * Process the option flags (if any).
+ */
+ cc = dvmProcessOptions(argc, argv, ignoreUnrecognized);
+ if (cc != 0) {
+ if (cc < 0) {
+ dvmFprintf(stderr, "\n");
+ dvmUsage("dalvikvm");
+ }
+ goto fail;
+ }
+
+#if WITH_EXTRA_GC_CHECKS > 1
+ /* only "portable" interp has the extra goodies */
+ if (gDvm.executionMode != kExecutionModeInterpPortable) {
+ LOGI("Switching to 'portable' interpreter for GC checks\n");
+ gDvm.executionMode = kExecutionModeInterpPortable;
+ }
+#endif
+
+ /* Configure group scheduling capabilities */
+ if (!access("/dev/cpuctl/tasks", F_OK)) {
+ LOGV("Using kernel group scheduling");
+ gDvm.kernelGroupScheduling = 1;
+ } else {
+ LOGV("Using kernel scheduler policies");
+ }
+
+ /* configure signal handling */
+ if (!gDvm.reduceSignals)
+ blockSignals();
+
+ /* verify system page size */
+ if (sysconf(_SC_PAGESIZE) != SYSTEM_PAGE_SIZE) {
+ LOGE("ERROR: expected page size %d, got %d\n",
+ SYSTEM_PAGE_SIZE, (int) sysconf(_SC_PAGESIZE));
+ goto fail;
+ }
+
+ /* mterp setup */
+ LOGV("Using executionMode %d\n", gDvm.executionMode);
+ dvmCheckAsmConstants();
+
+ /*
+ * Initialize components.
+ */
+ if (!dvmAllocTrackerStartup())
+ goto fail;
+ if (!dvmGcStartup())
+ goto fail;
+ if (!dvmThreadStartup())
+ goto fail;
+ if (!dvmInlineNativeStartup())
+ goto fail;
+ if (!dvmVerificationStartup())
+ goto fail;
+ if (!dvmRegisterMapStartup())
+ goto fail;
+ if (!dvmInstanceofStartup())
+ goto fail;
+ if (!dvmClassStartup())
+ goto fail;
+ if (!dvmThreadObjStartup())
+ goto fail;
+ if (!dvmExceptionStartup())
+ goto fail;
+ if (!dvmStringInternStartup())
+ goto fail;
+ if (!dvmNativeStartup())
+ goto fail;
+ if (!dvmInternalNativeStartup())
+ goto fail;
+ if (!dvmJniStartup())
+ goto fail;
+ if (!dvmReflectStartup())
+ goto fail;
+ if (!dvmProfilingStartup())
+ goto fail;
+
+ /* make sure we got these [can this go away?] */
+ assert(gDvm.classJavaLangClass != NULL);
+ assert(gDvm.classJavaLangObject != NULL);
+ //assert(gDvm.classJavaLangString != NULL);
+ assert(gDvm.classJavaLangThread != NULL);
+ assert(gDvm.classJavaLangVMThread != NULL);
+ assert(gDvm.classJavaLangThreadGroup != NULL);
+
+ /*
+ * Make sure these exist. If they don't, we can return a failure out
+ * of main and nip the whole thing in the bud.
+ */
+ static const char* earlyClasses[] = {
+ "Ljava/lang/InternalError;",
+ "Ljava/lang/StackOverflowError;",
+ "Ljava/lang/UnsatisfiedLinkError;",
+ "Ljava/lang/NoClassDefFoundError;",
+ NULL
+ };
+ const char** pClassName;
+ for (pClassName = earlyClasses; *pClassName != NULL; pClassName++) {
+ if (dvmFindSystemClassNoInit(*pClassName) == NULL)
+ goto fail;
+ }
+
+ /*
+ * Miscellaneous class library validation.
+ */
+ if (!dvmValidateBoxClasses())
+ goto fail;
+
+ /*
+ * Do the last bits of Thread struct initialization we need to allow
+ * JNI calls to work.
+ */
+ if (!dvmPrepMainForJni(pEnv))
+ goto fail;
+
+ /*
+ * Register the system native methods, which are registered through JNI.
+ */
+ if (!registerSystemNatives(pEnv))
+ goto fail;
+
+ /*
+ * Do some "late" initialization for the memory allocator. This may
+ * allocate storage and initialize classes.
+ */
+ if (!dvmCreateStockExceptions())
+ goto fail;
+
+ /*
+ * At this point, the VM is in a pretty good state. Finish prep on
+ * the main thread (specifically, create a java.lang.Thread object to go
+ * along with our Thread struct). Note we will probably be executing
+ * some interpreted class initializer code in here.
+ */
+ if (!dvmPrepMainThread())
+ goto fail;
+
+ /*
+ * Make sure we haven't accumulated any tracked references. The main
+ * thread should be starting with a clean slate.
+ */
+ if (dvmReferenceTableEntries(&dvmThreadSelf()->internalLocalRefTable) != 0)
+ {
+ LOGW("Warning: tracked references remain post-initialization\n");
+ dvmDumpReferenceTable(&dvmThreadSelf()->internalLocalRefTable, "MAIN");
+ }
+
+ /* general debugging setup */
+ if (!dvmDebuggerStartup())
+ goto fail;
+
+ /*
+ * Init for either zygote mode or non-zygote mode. The key difference
+ * is that we don't start any additional threads in Zygote mode.
+ */
+ if (gDvm.zygote) {
+ if (!dvmInitZygote())
+ goto fail;
+ } else {
+ if (!dvmInitAfterZygote())
+ goto fail;
+ }
+
+
+#ifndef NDEBUG
+ if (!dvmTestHash())
+ LOGE("dmvTestHash FAILED\n");
+ if (false /*noisy!*/ && !dvmTestIndirectRefTable())
+ LOGE("dvmTestIndirectRefTable FAILED\n");
+#endif
+
+ assert(!dvmCheckException(dvmThreadSelf()));
+ gDvm.initExceptionCount = 0;
+
+ return 0;
+
+fail:
+ dvmShutdown();
+ return 1;
+}
+
+/*
+ * Register java.* natives from our class libraries. We need to do
+ * this after we're ready for JNI registration calls, but before we
+ * do any class initialization.
+ *
+ * If we get this wrong, we will blow up in the ThreadGroup class init if
+ * interpreted code makes any reference to System. It will likely do this
+ * since it wants to do some java.io.File setup (e.g. for static in/out/err).
+ *
+ * We need to have gDvm.initializing raised here so that JNI FindClass
+ * won't try to use the system/application class loader.
+ */
+static bool registerSystemNatives(JNIEnv* pEnv)
+{
+ Thread* self;
+
+ /* main thread is always first in list */
+ self = gDvm.threadList;
+
+ /* must set this before allowing JNI-based method registration */
+ self->status = THREAD_NATIVE;
+
+ if (jniRegisterSystemMethods(pEnv) < 0) {
+ LOGW("jniRegisterSystemMethods failed\n");
+ return false;
+ }
+
+ /* back to run mode */
+ self->status = THREAD_RUNNING;
+
+ return true;
+}
+
+
+/*
+ * Do zygote-mode-only initialization.
+ */
+static bool dvmInitZygote(void)
+{
+ /* zygote goes into its own process group */
+ setpgid(0,0);
+
+ return true;
+}
+
+/*
+ * Do non-zygote-mode initialization. This is done during VM init for
+ * standard startup, or after a "zygote fork" when creating a new process.
+ */
+bool dvmInitAfterZygote(void)
+{
+ u8 startHeap, startQuit, startJdwp;
+ u8 endHeap, endQuit, endJdwp;
+
+ startHeap = dvmGetRelativeTimeUsec();
+
+ /*
+ * Post-zygote heap initialization, including starting
+ * the HeapWorker thread.
+ */
+ if (!dvmGcStartupAfterZygote())
+ return false;
+
+ endHeap = dvmGetRelativeTimeUsec();
+ startQuit = dvmGetRelativeTimeUsec();
+
+ /* start signal catcher thread that dumps stacks on SIGQUIT */
+ if (!gDvm.reduceSignals && !gDvm.noQuitHandler) {
+ if (!dvmSignalCatcherStartup())
+ return false;
+ }
+
+ /* start stdout/stderr copier, if requested */
+ if (gDvm.logStdio) {
+ if (!dvmStdioConverterStartup())
+ return false;
+ }
+
+ endQuit = dvmGetRelativeTimeUsec();
+ startJdwp = dvmGetRelativeTimeUsec();
+
+ /*
+ * Start JDWP thread. If the command-line debugger flags specified
+ * "suspend=y", this will pause the VM. We probably want this to
+ * come last.
+ */
+ if (!dvmInitJDWP()) {
+ LOGD("JDWP init failed; continuing anyway\n");
+ }
+
+ endJdwp = dvmGetRelativeTimeUsec();
+
+ LOGV("thread-start heap=%d quit=%d jdwp=%d total=%d usec\n",
+ (int)(endHeap-startHeap), (int)(endQuit-startQuit),
+ (int)(endJdwp-startJdwp), (int)(endJdwp-startHeap));
+
+#ifdef WITH_JIT
+ if (gDvm.executionMode == kExecutionModeJit) {
+ if (!dvmCompilerStartup())
+ return false;
+ }
+#endif
+
+ return true;
+}
+
+/*
+ * Prepare for a connection to a JDWP-compliant debugger.
+ *
+ * Note this needs to happen fairly late in the startup process, because
+ * we need to have all of the java.* native methods registered (which in
+ * turn requires JNI to be fully prepped).
+ *
+ * There are several ways to initialize:
+ * server=n
+ * We immediately try to connect to host:port. Bail on failure. On
+ * success, send VM_START (suspending the VM if "suspend=y").
+ * server=y suspend=n
+ * Passively listen for a debugger to connect. Return immediately.
+ * server=y suspend=y
+ * Wait until debugger connects. Send VM_START ASAP, suspending the
+ * VM after the message is sent.
+ *
+ * This gets more complicated with a nonzero value for "timeout".
+ */
+static bool dvmInitJDWP(void)
+{
+ assert(!gDvm.zygote);
+
+ /*
+ * Init JDWP if the debugger is enabled. This may connect out to a
+ * debugger, passively listen for a debugger, or block waiting for a
+ * debugger.
+ */
+ if (gDvm.jdwpAllowed && gDvm.jdwpConfigured) {
+ JdwpStartupParams params;
+
+ if (gDvm.jdwpHost != NULL) {
+ if (strlen(gDvm.jdwpHost) >= sizeof(params.host)-1) {
+ LOGE("ERROR: hostname too long: '%s'\n", gDvm.jdwpHost);
+ return false;
+ }
+ strcpy(params.host, gDvm.jdwpHost);
+ } else {
+ params.host[0] = '\0';
+ }
+ params.transport = gDvm.jdwpTransport;
+ params.server = gDvm.jdwpServer;
+ params.suspend = gDvm.jdwpSuspend;
+ params.port = gDvm.jdwpPort;
+
+ gDvm.jdwpState = dvmJdwpStartup(&params);
+ if (gDvm.jdwpState == NULL) {
+ LOGW("WARNING: debugger thread failed to initialize\n");
+ /* TODO: ignore? fail? need to mimic "expected" behavior */
+ }
+ }
+
+ /*
+ * If a debugger has already attached, send the "welcome" message. This
+ * may cause us to suspend all threads.
+ */
+ if (dvmJdwpIsActive(gDvm.jdwpState)) {
+ //dvmChangeStatus(NULL, THREAD_RUNNING);
+ if (!dvmJdwpPostVMStart(gDvm.jdwpState, gDvm.jdwpSuspend)) {
+ LOGW("WARNING: failed to post 'start' message to debugger\n");
+ /* keep going */
+ }
+ //dvmChangeStatus(NULL, THREAD_NATIVE);
+ }
+
+ return true;
+}
+
+/*
+ * An alternative to JNI_CreateJavaVM/dvmStartup that does the first bit
+ * of initialization and then returns with "initializing" still set. (Used
+ * by DexOpt command-line utility.)
+ *
+ * Attempting to use JNI or internal natives will fail. It's best if
+ * no bytecode gets executed, which means no <clinit>, which means no
+ * exception-throwing. We check the "initializing" flag anyway when
+ * throwing an exception, so we can insert some code that avoids chucking
+ * an exception when we're optimizing stuff.
+ *
+ * Returns 0 on success.
+ */
+int dvmPrepForDexOpt(const char* bootClassPath, DexOptimizerMode dexOptMode,
+ DexClassVerifyMode verifyMode, int dexoptFlags)
+{
+ gDvm.initializing = true;
+ gDvm.optimizing = true;
+
+ /* configure signal handling */
+ blockSignals();
+
+ /* set some defaults */
+ setCommandLineDefaults();
+ free(gDvm.bootClassPathStr);
+ gDvm.bootClassPathStr = strdup(bootClassPath);
+
+ /* set opt/verify modes */
+ gDvm.dexOptMode = dexOptMode;
+ gDvm.classVerifyMode = verifyMode;
+ gDvm.generateRegisterMaps = (dexoptFlags & DEXOPT_GEN_REGISTER_MAPS) != 0;
+ if (dexoptFlags & DEXOPT_SMP) {
+ assert((dexoptFlags & DEXOPT_UNIPROCESSOR) == 0);
+ gDvm.dexOptForSmp = true;
+ } else if (dexoptFlags & DEXOPT_UNIPROCESSOR) {
+ gDvm.dexOptForSmp = false;
+ } else {
+ gDvm.dexOptForSmp = (ANDROID_SMP != 0);
+ }
+
+ /*
+ * Initialize the heap, some basic thread control mutexes, and
+ * get the bootclasspath prepped.
+ *
+ * We can't load any classes yet because we may not yet have a source
+ * for things like java.lang.Object and java.lang.Class.
+ */
+ if (!dvmGcStartup())
+ goto fail;
+ if (!dvmThreadStartup())
+ goto fail;
+ if (!dvmInlineNativeStartup())
+ goto fail;
+ if (!dvmVerificationStartup())
+ goto fail;
+ if (!dvmRegisterMapStartup())
+ goto fail;
+ if (!dvmInstanceofStartup())
+ goto fail;
+ if (!dvmClassStartup())
+ goto fail;
+
+ /*
+ * We leave gDvm.initializing set to "true" so that, if we're not
+ * able to process the "core" classes, we don't go into a death-spin
+ * trying to throw a "class not found" exception.
+ */
+
+ return 0;
+
+fail:
+ dvmShutdown();
+ return 1;
+}
+
+
+/*
+ * All threads have stopped. Finish the shutdown procedure.
+ *
+ * We can also be called if startup fails partway through, so be prepared
+ * to deal with partially initialized data.
+ *
+ * Free any storage allocated in gGlobals.
+ *
+ * We can't dlclose() shared libs we've loaded, because it's possible a
+ * thread not associated with the VM is running code in one.
+ *
+ * This is called from the JNI DestroyJavaVM function, which can be
+ * called from any thread. (In practice, this will usually run in the
+ * same thread that started the VM, a/k/a the main thread, but we don't
+ * want to assume that.)
+ */
+void dvmShutdown(void)
+{
+ LOGV("VM shutting down\n");
+
+ if (CALC_CACHE_STATS)
+ dvmDumpAtomicCacheStats(gDvm.instanceofCache);
+
+ /*
+ * Stop our internal threads.
+ */
+ dvmGcThreadShutdown();
+
+ if (gDvm.jdwpState != NULL)
+ dvmJdwpShutdown(gDvm.jdwpState);
+ free(gDvm.jdwpHost);
+ gDvm.jdwpHost = NULL;
+ free(gDvm.jniTrace);
+ gDvm.jniTrace = NULL;
+ free(gDvm.stackTraceFile);
+ gDvm.stackTraceFile = NULL;
+
+ /* tell signal catcher to shut down if it was started */
+ dvmSignalCatcherShutdown();
+
+ /* shut down stdout/stderr conversion */
+ dvmStdioConverterShutdown();
+
+#ifdef WITH_JIT
+ if (gDvm.executionMode == kExecutionModeJit) {
+ /* shut down the compiler thread */
+ dvmCompilerShutdown();
+ }
+#endif
+
+ /*
+ * Kill any daemon threads that still exist. Actively-running threads
+ * are likely to crash the process if they continue to execute while
+ * the VM shuts down.
+ */
+ dvmSlayDaemons();
+
+ if (gDvm.verboseShutdown)
+ LOGD("VM cleaning up\n");
+
+ dvmDebuggerShutdown();
+ dvmReflectShutdown();
+ dvmProfilingShutdown();
+ dvmJniShutdown();
+ dvmStringInternShutdown();
+ dvmExceptionShutdown();
+ dvmThreadShutdown();
+ dvmClassShutdown();
+ dvmVerificationShutdown();
+ dvmRegisterMapShutdown();
+ dvmInstanceofShutdown();
+ dvmInlineNativeShutdown();
+ dvmGcShutdown();
+ dvmAllocTrackerShutdown();
+ dvmPropertiesShutdown();
+
+ /* these must happen AFTER dvmClassShutdown has walked through class data */
+ dvmNativeShutdown();
+ dvmInternalNativeShutdown();
+
+ free(gDvm.bootClassPathStr);
+ free(gDvm.classPathStr);
+
+ freeAssertionCtrl();
+
+ /*
+ * We want valgrind to report anything we forget to free as "definitely
+ * lost". If there's a pointer in the global chunk, it would be reported
+ * as "still reachable". Erasing the memory fixes this.
+ *
+ * This must be erased to zero if we want to restart the VM within this
+ * process.
+ */
+ memset(&gDvm, 0xcd, sizeof(gDvm));
+}
+
+
+/*
+ * fprintf() wrapper that calls through the JNI-specified vfprintf hook if
+ * one was specified.
+ */
+int dvmFprintf(FILE* fp, const char* format, ...)
+{
+ va_list args;
+ int result;
+
+ va_start(args, format);
+ if (gDvm.vfprintfHook != NULL)
+ result = (*gDvm.vfprintfHook)(fp, format, args);
+ else
+ result = vfprintf(fp, format, args);
+ va_end(args);
+
+ return result;
+}
+
+/*
+ * Abort the VM. We get here on fatal errors. Try very hard not to use
+ * this; whenever possible, return an error to somebody responsible.
+ */
+void dvmAbort(void)
+{
+ LOGE("VM aborting\n");
+
+ fflush(NULL); // flush all open file buffers
+
+ /* JNI-supplied abort hook gets right of first refusal */
+ if (gDvm.abortHook != NULL)
+ (*gDvm.abortHook)();
+
+ /*
+ * If we call abort(), all threads in the process receives a SIBABRT.
+ * debuggerd dumps the stack trace of the main thread, whether or not
+ * that was the thread that failed.
+ *
+ * By stuffing a value into a bogus address, we cause a segmentation
+ * fault in the current thread, and get a useful log from debuggerd.
+ * We can also trivially tell the difference between a VM crash and
+ * a deliberate abort by looking at the fault address.
+ */
+ *((char*)0xdeadd00d) = 38;
+ abort();
+
+ /* notreached */
+}
diff --git a/vm/Init.h b/vm/Init.h
new file mode 100644
index 0000000..63051a2
--- /dev/null
+++ b/vm/Init.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * VM initialization and shutdown.
+ */
+#ifndef _DALVIK_INIT
+#define _DALVIK_INIT
+
+/*
+ * Standard VM initialization, usually invoked through JNI.
+ */
+int dvmStartup(int argc, const char* const argv[], bool ignoreUnrecognized,
+ JNIEnv* pEnv);
+void dvmShutdown(void);
+bool dvmInitAfterZygote(void);
+
+/*
+ * Enable Java programming language assert statements after the Zygote fork.
+ */
+void dvmLateEnableAssertions(void);
+
+/*
+ * Partial VM initialization; only used as part of "dexopt", which may be
+ * asked to optimize a DEX file holding fundamental classes.
+ */
+int dvmPrepForDexOpt(const char* bootClassPath, DexOptimizerMode dexOptMode,
+ DexClassVerifyMode verifyMode, int dexoptFlags);
+
+/*
+ * Replacement for fprintf() when we want to send a message to the console.
+ * This defaults to fprintf(), but will use the JNI fprintf callback if
+ * one was provided.
+ */
+int dvmFprintf(FILE* fp, const char* format, ...)
+#if defined(__GNUC__)
+ __attribute__ ((format(printf, 2, 3)))
+#endif
+ ;
+
+#endif /*_DALVIK_INIT*/
diff --git a/vm/InlineNative.c b/vm/InlineNative.c
new file mode 100644
index 0000000..a3a9ac8
--- /dev/null
+++ b/vm/InlineNative.c
@@ -0,0 +1,857 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Inlined native functions. These definitions replace interpreted or
+ * native implementations at runtime; "intrinsic" might be a better word.
+ */
+#include "Dalvik.h"
+
+#include <math.h>
+
+#ifdef HAVE__MEMCMP16
+/* hand-coded assembly implementation, available on some platforms */
+//#warning "trying memcmp16"
+//#define CHECK_MEMCMP16
+/* "count" is in 16-bit units */
+extern u4 __memcmp16(const u2* s0, const u2* s1, size_t count);
+#endif
+
+/*
+ * Some notes on "inline" functions.
+ *
+ * These are NOT simply native implementations. A full method definition
+ * must still be provided. Depending on the flags passed into the VM
+ * at runtime, the original or inline version may be selected by the
+ * DEX optimizer.
+ *
+ * PLEASE DO NOT use this as the default location for native methods.
+ * The difference between this and an "internal native" static method
+ * call on a 200MHz ARM 9 is roughly 370ns vs. 700ns. The code here
+ * "secretly replaces" the other method, so you can't avoid having two
+ * implementations. Since the DEX optimizer mode can't be known ahead
+ * of time, both implementations must be correct and complete.
+ *
+ * The only stuff that really needs to be here are methods that
+ * are high-volume or must be low-overhead, e.g. certain String/Math
+ * methods and some java.util.concurrent.atomic operations.
+ *
+ * Normally, a class is loaded and initialized the first time a static
+ * method is invoked. This property is NOT preserved here. If you need
+ * to access a static field in a class, you must ensure initialization
+ * yourself (cheap/easy way is to check the resolved-methods table, and
+ * resolve the method if it hasn't been).
+ *
+ * DO NOT replace "synchronized" methods. We do not support method
+ * synchronization here.
+ *
+ * DO NOT perform any allocations or do anything that could cause a
+ * garbage collection. The method arguments are not visible to the GC
+ * and will not be pinned or updated when memory blocks move. You are
+ * allowed to allocate and throw an exception so long as you only do so
+ * immediately before returning.
+ *
+ * Remember that these functions are executing while the thread is in
+ * the "RUNNING" state, not the "NATIVE" state. If you perform a blocking
+ * operation you can stall the entire VM if the GC or debugger wants to
+ * suspend the thread. Since these are arguably native implementations
+ * rather than VM internals, prefer NATIVE to VMWAIT if you want to change
+ * the thread state.
+ *
+ * Always write results to 32-bit or 64-bit fields in "pResult", e.g. do
+ * not write boolean results to pResult->z. The interpreter expects
+ * 32 or 64 bits to be set.
+ *
+ * Inline op methods return "false" if an exception was thrown, "true" if
+ * everything went well.
+ *
+ * DO NOT provide implementations of methods that can be overridden by a
+ * subclass, as polymorphism does not work correctly. For safety you should
+ * only provide inline functions for classes/methods declared "final".
+ *
+ * It's best to avoid inlining the overridden version of a method. For
+ * example, String.hashCode() is inherited from Object.hashCode(). Code
+ * calling String.hashCode() through an Object reference will run the
+ * "slow" version, while calling it through a String reference gets
+ * the inlined version. It's best to have just one version unless there
+ * are clear performance gains.
+ *
+ * Because the actual method is not called, debugger breakpoints on these
+ * methods will not happen. (TODO: have the code here find the original
+ * method and call it when the debugger is active.) Additional steps have
+ * been taken to allow method profiling to produce correct results.
+ */
+
+
+/*
+ * ===========================================================================
+ * org.apache.harmony.dalvik.NativeTestTarget
+ * ===========================================================================
+ */
+
+/*
+ * public static void emptyInlineMethod
+ *
+ * This exists only for benchmarks.
+ */
+static bool org_apache_harmony_dalvik_NativeTestTarget_emptyInlineMethod(
+ u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+{
+ // do nothing
+ return true;
+}
+
+
+/*
+ * ===========================================================================
+ * java.lang.String
+ * ===========================================================================
+ */
+
+/*
+ * public char charAt(int index)
+ */
+static bool javaLangString_charAt(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+ JValue* pResult)
+{
+ int count, offset;
+ ArrayObject* chars;
+
+ /* null reference check on "this" */
+ if (!dvmValidateObject((Object*) arg0))
+ return false;
+
+ //LOGI("String.charAt this=0x%08x index=%d\n", arg0, arg1);
+ count = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_COUNT);
+ if ((s4) arg1 < 0 || (s4) arg1 >= count) {
+ dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL);
+ return false;
+ } else {
+ offset = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_OFFSET);
+ chars = (ArrayObject*)
+ dvmGetFieldObject((Object*) arg0, STRING_FIELDOFF_VALUE);
+
+ pResult->i = ((const u2*) chars->contents)[arg1 + offset];
+ return true;
+ }
+}
+
+#ifdef CHECK_MEMCMP16
+/*
+ * Utility function when we're evaluating alternative implementations.
+ */
+static void badMatch(StringObject* thisStrObj, StringObject* compStrObj,
+ int expectResult, int newResult, const char* compareType)
+{
+ ArrayObject* thisArray;
+ ArrayObject* compArray;
+ const char* thisStr;
+ const char* compStr;
+ int thisOffset, compOffset, thisCount, compCount;
+
+ thisCount =
+ dvmGetFieldInt((Object*) thisStrObj, STRING_FIELDOFF_COUNT);
+ compCount =
+ dvmGetFieldInt((Object*) compStrObj, STRING_FIELDOFF_COUNT);
+ thisOffset =
+ dvmGetFieldInt((Object*) thisStrObj, STRING_FIELDOFF_OFFSET);
+ compOffset =
+ dvmGetFieldInt((Object*) compStrObj, STRING_FIELDOFF_OFFSET);
+ thisArray = (ArrayObject*)
+ dvmGetFieldObject((Object*) thisStrObj, STRING_FIELDOFF_VALUE);
+ compArray = (ArrayObject*)
+ dvmGetFieldObject((Object*) compStrObj, STRING_FIELDOFF_VALUE);
+
+ thisStr = dvmCreateCstrFromString(thisStrObj);
+ compStr = dvmCreateCstrFromString(compStrObj);
+
+ LOGE("%s expected %d got %d\n", compareType, expectResult, newResult);
+ LOGE(" this (o=%d l=%d) '%s'\n", thisOffset, thisCount, thisStr);
+ LOGE(" comp (o=%d l=%d) '%s'\n", compOffset, compCount, compStr);
+ dvmPrintHexDumpEx(ANDROID_LOG_INFO, LOG_TAG,
+ ((const u2*) thisArray->contents) + thisOffset, thisCount*2,
+ kHexDumpLocal);
+ dvmPrintHexDumpEx(ANDROID_LOG_INFO, LOG_TAG,
+ ((const u2*) compArray->contents) + compOffset, compCount*2,
+ kHexDumpLocal);
+ dvmAbort();
+}
+#endif
+
+/*
+ * public int compareTo(String s)
+ */
+static bool javaLangString_compareTo(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+ JValue* pResult)
+{
+ /*
+ * Null reference check on "this". Normally this is performed during
+ * the setup of the virtual method call. We need to do it before
+ * anything else. While we're at it, check out the other string,
+ * which must also be non-null.
+ */
+ if (!dvmValidateObject((Object*) arg0) ||
+ !dvmValidateObject((Object*) arg1))
+ {
+ return false;
+ }
+
+ /* quick test for comparison with itself */
+ if (arg0 == arg1) {
+ pResult->i = 0;
+ return true;
+ }
+
+ /*
+ * This would be simpler and faster if we promoted StringObject to
+ * a full representation, lining up the C structure fields with the
+ * actual object fields.
+ */
+ int thisCount, thisOffset, compCount, compOffset;
+ ArrayObject* thisArray;
+ ArrayObject* compArray;
+ const u2* thisChars;
+ const u2* compChars;
+ int minCount, countDiff;
+
+ thisCount = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_COUNT);
+ compCount = dvmGetFieldInt((Object*) arg1, STRING_FIELDOFF_COUNT);
+ countDiff = thisCount - compCount;
+ minCount = (countDiff < 0) ? thisCount : compCount;
+ thisOffset = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_OFFSET);
+ compOffset = dvmGetFieldInt((Object*) arg1, STRING_FIELDOFF_OFFSET);
+ thisArray = (ArrayObject*)
+ dvmGetFieldObject((Object*) arg0, STRING_FIELDOFF_VALUE);
+ compArray = (ArrayObject*)
+ dvmGetFieldObject((Object*) arg1, STRING_FIELDOFF_VALUE);
+ thisChars = ((const u2*) thisArray->contents) + thisOffset;
+ compChars = ((const u2*) compArray->contents) + compOffset;
+
+#ifdef HAVE__MEMCMP16
+ /*
+ * Use assembly version, which returns the difference between the
+ * characters. The annoying part here is that 0x00e9 - 0xffff != 0x00ea,
+ * because the interpreter converts the characters to 32-bit integers
+ * *without* sign extension before it subtracts them (which makes some
+ * sense since "char" is unsigned). So what we get is the result of
+ * 0x000000e9 - 0x0000ffff, which is 0xffff00ea.
+ */
+ int otherRes = __memcmp16(thisChars, compChars, minCount);
+# ifdef CHECK_MEMCMP16
+ int i;
+ for (i = 0; i < minCount; i++) {
+ if (thisChars[i] != compChars[i]) {
+ pResult->i = (s4) thisChars[i] - (s4) compChars[i];
+ if (pResult->i != otherRes) {
+ badMatch((StringObject*) arg0, (StringObject*) arg1,
+ pResult->i, otherRes, "compareTo");
+ }
+ return true;
+ }
+ }
+# endif
+ if (otherRes != 0) {
+ pResult->i = otherRes;
+ return true;
+ }
+
+#else
+ /*
+ * Straightforward implementation, examining 16 bits at a time. Compare
+ * the characters that overlap, and if they're all the same then return
+ * the difference in lengths.
+ */
+ int i;
+ for (i = 0; i < minCount; i++) {
+ if (thisChars[i] != compChars[i]) {
+ pResult->i = (s4) thisChars[i] - (s4) compChars[i];
+ return true;
+ }
+ }
+#endif
+
+ pResult->i = countDiff;
+ return true;
+}
+
+/*
+ * public boolean equals(Object anObject)
+ */
+static bool javaLangString_equals(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+ JValue* pResult)
+{
+ /*
+ * Null reference check on "this".
+ */
+ if (!dvmValidateObject((Object*) arg0))
+ return false;
+
+ /* quick test for comparison with itself */
+ if (arg0 == arg1) {
+ pResult->i = true;
+ return true;
+ }
+
+ /*
+ * See if the other object is also a String.
+ *
+ * str.equals(null) is expected to return false, presumably based on
+ * the results of the instanceof test.
+ */
+ if (arg1 == 0 || ((Object*) arg0)->clazz != ((Object*) arg1)->clazz) {
+ pResult->i = false;
+ return true;
+ }
+
+ /*
+ * This would be simpler and faster if we promoted StringObject to
+ * a full representation, lining up the C structure fields with the
+ * actual object fields.
+ */
+ int thisCount, thisOffset, compCount, compOffset;
+ ArrayObject* thisArray;
+ ArrayObject* compArray;
+ const u2* thisChars;
+ const u2* compChars;
+
+ /* quick length check */
+ thisCount = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_COUNT);
+ compCount = dvmGetFieldInt((Object*) arg1, STRING_FIELDOFF_COUNT);
+ if (thisCount != compCount) {
+ pResult->i = false;
+ return true;
+ }
+
+ thisOffset = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_OFFSET);
+ compOffset = dvmGetFieldInt((Object*) arg1, STRING_FIELDOFF_OFFSET);
+ thisArray = (ArrayObject*)
+ dvmGetFieldObject((Object*) arg0, STRING_FIELDOFF_VALUE);
+ compArray = (ArrayObject*)
+ dvmGetFieldObject((Object*) arg1, STRING_FIELDOFF_VALUE);
+ thisChars = ((const u2*) thisArray->contents) + thisOffset;
+ compChars = ((const u2*) compArray->contents) + compOffset;
+
+#ifdef HAVE__MEMCMP16
+ pResult->i = (__memcmp16(thisChars, compChars, thisCount) == 0);
+# ifdef CHECK_MEMCMP16
+ int otherRes = (memcmp(thisChars, compChars, thisCount * 2) == 0);
+ if (pResult->i != otherRes) {
+ badMatch((StringObject*) arg0, (StringObject*) arg1,
+ otherRes, pResult->i, "equals-1");
+ }
+# endif
+#else
+ /*
+ * Straightforward implementation, examining 16 bits at a time. The
+ * direction of the loop doesn't matter, and starting at the end may
+ * give us an advantage when comparing certain types of strings (e.g.
+ * class names).
+ *
+ * We want to go forward for benchmarks against __memcmp16 so we get a
+ * meaningful comparison when the strings don't match (could also test
+ * with palindromes).
+ */
+ int i;
+ //for (i = 0; i < thisCount; i++)
+ for (i = thisCount-1; i >= 0; --i)
+ {
+ if (thisChars[i] != compChars[i]) {
+ pResult->i = false;
+ return true;
+ }
+ }
+ pResult->i = true;
+#endif
+
+ return true;
+}
+
+/*
+ * public int length()
+ */
+static bool javaLangString_length(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+ JValue* pResult)
+{
+ //LOGI("String.length this=0x%08x pResult=%p\n", arg0, pResult);
+
+ /* null reference check on "this" */
+ if (!dvmValidateObject((Object*) arg0))
+ return false;
+
+ pResult->i = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_COUNT);
+ return true;
+}
+
+/*
+ * public boolean isEmpty()
+ */
+static bool javaLangString_isEmpty(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+ JValue* pResult)
+{
+ //LOGI("String.isEmpty this=0x%08x pResult=%p\n", arg0, pResult);
+
+ /* null reference check on "this" */
+ if (!dvmValidateObject((Object*) arg0))
+ return false;
+
+ pResult->i = (dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_COUNT) == 0);
+ return true;
+}
+
+/*
+ * Determine the index of the first character matching "ch". The string
+ * to search is described by "chars", "offset", and "count".
+ *
+ * The character must be <= 0xffff. Supplementary characters are handled in
+ * Java.
+ *
+ * The "start" parameter must be clamped to [0..count].
+ *
+ * Returns -1 if no match is found.
+ */
+static inline int indexOfCommon(Object* strObj, int ch, int start)
+{
+ //if ((ch & 0xffff) != ch) /* 32-bit code point */
+ // return -1;
+
+ /* pull out the basic elements */
+ ArrayObject* charArray =
+ (ArrayObject*) dvmGetFieldObject(strObj, STRING_FIELDOFF_VALUE);
+ const u2* chars = (const u2*) charArray->contents;
+ int offset = dvmGetFieldInt(strObj, STRING_FIELDOFF_OFFSET);
+ int count = dvmGetFieldInt(strObj, STRING_FIELDOFF_COUNT);
+ //LOGI("String.indexOf(0x%08x, 0x%04x, %d) off=%d count=%d\n",
+ // (u4) strObj, ch, start, offset, count);
+
+ /* factor out the offset */
+ chars += offset;
+
+ if (start < 0)
+ start = 0;
+
+#if 0
+ /* 16-bit loop, simple */
+ while (start < count) {
+ if (chars[start] == ch)
+ return start;
+ start++;
+ }
+#else
+ /* 16-bit loop, slightly better on ARM */
+ const u2* ptr = chars + start;
+ const u2* endPtr = chars + count;
+ while (ptr < endPtr) {
+ if (*ptr++ == ch)
+ return (ptr-1) - chars;
+ }
+#endif
+
+ return -1;
+}
+
+/*
+ * public int indexOf(int c, int start)
+ *
+ * Scan forward through the string for a matching character.
+ * The character must be <= 0xffff; this method does not handle supplementary
+ * characters.
+ */
+static bool javaLangString_fastIndexOf_II(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+ JValue* pResult)
+{
+ /* null reference check on "this" */
+ if (!dvmValidateObject((Object*) arg0))
+ return false;
+
+ pResult->i = indexOfCommon((Object*) arg0, arg1, arg2);
+ return true;
+}
+
+
+/*
+ * ===========================================================================
+ * java.lang.Math
+ * ===========================================================================
+ */
+
+typedef union {
+ u4 arg;
+ float ff;
+} Convert32;
+
+typedef union {
+ u4 arg[2];
+ s8 ll;
+ double dd;
+} Convert64;
+
+/*
+ * public static int abs(int)
+ */
+static bool javaLangMath_abs_int(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+ JValue* pResult)
+{
+ s4 val = (s4) arg0;
+ pResult->i = (val >= 0) ? val : -val;
+ return true;
+}
+
+/*
+ * public static long abs(long)
+ */
+static bool javaLangMath_abs_long(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+ JValue* pResult)
+{
+ Convert64 convert;
+ convert.arg[0] = arg0;
+ convert.arg[1] = arg1;
+ s8 val = convert.ll;
+ pResult->j = (val >= 0) ? val : -val;
+ return true;
+}
+
+/*
+ * public static float abs(float)
+ */
+static bool javaLangMath_abs_float(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+ JValue* pResult)
+{
+ Convert32 convert;
+ /* clear the sign bit; assumes a fairly common fp representation */
+ convert.arg = arg0 & 0x7fffffff;
+ pResult->f = convert.ff;
+ return true;
+}
+
+/*
+ * public static double abs(double)
+ */
+static bool javaLangMath_abs_double(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+ JValue* pResult)
+{
+ Convert64 convert;
+ convert.arg[0] = arg0;
+ convert.arg[1] = arg1;
+ /* clear the sign bit in the (endian-dependent) high word */
+ convert.ll &= 0x7fffffffffffffffULL;
+ pResult->d = convert.dd;
+ return true;
+}
+
+/*
+ * public static int min(int)
+ */
+static bool javaLangMath_min_int(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+ JValue* pResult)
+{
+ pResult->i = ((s4) arg0 < (s4) arg1) ? arg0 : arg1;
+ return true;
+}
+
+/*
+ * public static int max(int)
+ */
+static bool javaLangMath_max_int(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+ JValue* pResult)
+{
+ pResult->i = ((s4) arg0 > (s4) arg1) ? arg0 : arg1;
+ return true;
+}
+
+/*
+ * public static double sqrt(double)
+ *
+ * With ARM VFP enabled, gcc turns this into an fsqrtd instruction, followed
+ * by an fcmpd of the result against itself. If it doesn't match (i.e.
+ * it's NaN), the libm sqrt() is invoked.
+ */
+static bool javaLangMath_sqrt(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+ JValue* pResult)
+{
+ Convert64 convert;
+ convert.arg[0] = arg0;
+ convert.arg[1] = arg1;
+ pResult->d = sqrt(convert.dd);
+ return true;
+}
+
+/*
+ * public static double cos(double)
+ */
+static bool javaLangMath_cos(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+ JValue* pResult)
+{
+ Convert64 convert;
+ convert.arg[0] = arg0;
+ convert.arg[1] = arg1;
+ pResult->d = cos(convert.dd);
+ return true;
+}
+
+/*
+ * public static double sin(double)
+ */
+static bool javaLangMath_sin(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+ JValue* pResult)
+{
+ Convert64 convert;
+ convert.arg[0] = arg0;
+ convert.arg[1] = arg1;
+ pResult->d = sin(convert.dd);
+ return true;
+}
+
+/*
+ * ===========================================================================
+ * java.lang.Float
+ * ===========================================================================
+ */
+
+static bool javaLangFloat_floatToIntBits(u4 arg0, u4 arg1, u4 arg2, u4 arg,
+ JValue* pResult)
+{
+ Convert32 convert;
+ convert.arg = arg0;
+ pResult->i = isnanf(convert.ff) ? 0x7fc00000 : arg0;
+ return true;
+}
+
+static bool javaLangFloat_floatToRawIntBits(u4 arg0, u4 arg1, u4 arg2, u4 arg,
+ JValue* pResult)
+{
+ pResult->i = arg0;
+ return true;
+}
+
+static bool javaLangFloat_intBitsToFloat(u4 arg0, u4 arg1, u4 arg2, u4 arg,
+ JValue* pResult)
+{
+ Convert32 convert;
+ convert.arg = arg0;
+ pResult->f = convert.ff;
+ return true;
+}
+
+/*
+ * ===========================================================================
+ * java.lang.Double
+ * ===========================================================================
+ */
+
+static bool javaLangDouble_doubleToLongBits(u4 arg0, u4 arg1, u4 arg2, u4 arg,
+ JValue* pResult)
+{
+ Convert64 convert;
+ convert.arg[0] = arg0;
+ convert.arg[1] = arg1;
+ pResult->j = isnan(convert.dd) ? 0x7ff8000000000000LL : convert.ll;
+ return true;
+}
+
+static bool javaLangDouble_doubleToRawLongBits(u4 arg0, u4 arg1, u4 arg2,
+ u4 arg, JValue* pResult)
+{
+ Convert64 convert;
+ convert.arg[0] = arg0;
+ convert.arg[1] = arg1;
+ pResult->j = convert.ll;
+ return true;
+}
+
+static bool javaLangDouble_longBitsToDouble(u4 arg0, u4 arg1, u4 arg2, u4 arg,
+ JValue* pResult)
+{
+ Convert64 convert;
+ convert.arg[0] = arg0;
+ convert.arg[1] = arg1;
+ pResult->d = convert.dd;
+ return true;
+}
+
+/*
+ * ===========================================================================
+ * Infrastructure
+ * ===========================================================================
+ */
+
+/*
+ * Table of methods.
+ *
+ * The DEX optimizer uses the class/method/signature string fields to decide
+ * which calls it can trample. The interpreter just uses the function
+ * pointer field.
+ *
+ * IMPORTANT: you must update DALVIK_VM_BUILD in DalvikVersion.h if you make
+ * changes to this table.
+ *
+ * NOTE: If present, the JIT will also need to know about changes
+ * to this table. Update the NativeInlineOps enum in InlineNative.h and
+ * the dispatch code in compiler/codegen/<target>/Codegen.c.
+ */
+const InlineOperation gDvmInlineOpsTable[] = {
+ { org_apache_harmony_dalvik_NativeTestTarget_emptyInlineMethod,
+ "Lorg/apache/harmony/dalvik/NativeTestTarget;",
+ "emptyInlineMethod", "()V" },
+
+ { javaLangString_charAt,
+ "Ljava/lang/String;", "charAt", "(I)C" },
+ { javaLangString_compareTo,
+ "Ljava/lang/String;", "compareTo", "(Ljava/lang/String;)I" },
+ { javaLangString_equals,
+ "Ljava/lang/String;", "equals", "(Ljava/lang/Object;)Z" },
+ { javaLangString_fastIndexOf_II,
+ "Ljava/lang/String;", "fastIndexOf", "(II)I" },
+ { javaLangString_isEmpty,
+ "Ljava/lang/String;", "isEmpty", "()Z" },
+ { javaLangString_length,
+ "Ljava/lang/String;", "length", "()I" },
+
+ { javaLangMath_abs_int,
+ "Ljava/lang/Math;", "abs", "(I)I" },
+ { javaLangMath_abs_long,
+ "Ljava/lang/Math;", "abs", "(J)J" },
+ { javaLangMath_abs_float,
+ "Ljava/lang/Math;", "abs", "(F)F" },
+ { javaLangMath_abs_double,
+ "Ljava/lang/Math;", "abs", "(D)D" },
+ { javaLangMath_min_int,
+ "Ljava/lang/Math;", "min", "(II)I" },
+ { javaLangMath_max_int,
+ "Ljava/lang/Math;", "max", "(II)I" },
+ { javaLangMath_sqrt,
+ "Ljava/lang/Math;", "sqrt", "(D)D" },
+ { javaLangMath_cos,
+ "Ljava/lang/Math;", "cos", "(D)D" },
+ { javaLangMath_sin,
+ "Ljava/lang/Math;", "sin", "(D)D" },
+
+ { javaLangFloat_floatToIntBits,
+ "Ljava/lang/Float;", "floatToIntBits", "(F)I" },
+ { javaLangFloat_floatToRawIntBits,
+ "Ljava/lang/Float;", "floatToRawIntBits", "(F)I" },
+ { javaLangFloat_intBitsToFloat,
+ "Ljava/lang/Float;", "intBitsToFloat", "(I)F" },
+
+ { javaLangDouble_doubleToLongBits,
+ "Ljava/lang/Double;", "doubleToLongBits", "(D)J" },
+ { javaLangDouble_doubleToRawLongBits,
+ "Ljava/lang/Double;", "doubleToRawLongBits", "(D)J" },
+ { javaLangDouble_longBitsToDouble,
+ "Ljava/lang/Double;", "longBitsToDouble", "(J)D" },
+};
+
+/*
+ * Allocate some tables.
+ */
+bool dvmInlineNativeStartup(void)
+{
+ gDvm.inlinedMethods =
+ (Method**) calloc(NELEM(gDvmInlineOpsTable), sizeof(Method*));
+ if (gDvm.inlinedMethods == NULL)
+ return false;
+
+ return true;
+}
+
+/*
+ * Free generated tables.
+ */
+void dvmInlineNativeShutdown(void)
+{
+ free(gDvm.inlinedMethods);
+}
+
+
+/*
+ * Get a pointer to the inlineops table.
+ */
+const InlineOperation* dvmGetInlineOpsTable(void)
+{
+ return gDvmInlineOpsTable;
+}
+
+/*
+ * Get the number of entries in the inlineops table.
+ */
+int dvmGetInlineOpsTableLength(void)
+{
+ return NELEM(gDvmInlineOpsTable);
+}
+
+/*
+ * Make an inline call for the "debug" interpreter, used when the debugger
+ * or profiler is active.
+ */
+bool dvmPerformInlineOp4Dbg(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+ JValue* pResult, int opIndex)
+{
+ Thread* self = dvmThreadSelf();
+ bool result;
+
+ assert(opIndex >= 0 && opIndex < NELEM(gDvmInlineOpsTable));
+
+ /*
+ * Populate the methods table on first use. It's possible the class
+ * hasn't been resolved yet, so we need to do the full "calling the
+ * method for the first time" routine. (It's probably okay to skip
+ * the access checks.)
+ *
+ * Currently assuming that we're only inlining stuff loaded by the
+ * bootstrap class loader. This is a safe assumption for many reasons.
+ */
+ Method* method = gDvm.inlinedMethods[opIndex];
+ if (method == NULL) {
+ ClassObject* clazz;
+
+ clazz = dvmFindClassNoInit(
+ gDvmInlineOpsTable[opIndex].classDescriptor, NULL);
+ if (clazz == NULL) {
+ LOGW("Warning: can't find class '%s'\n", clazz->descriptor);
+ goto skip_prof;
+ }
+ method = dvmFindDirectMethodByDescriptor(clazz,
+ gDvmInlineOpsTable[opIndex].methodName,
+ gDvmInlineOpsTable[opIndex].methodSignature);
+ if (method == NULL)
+ method = dvmFindVirtualMethodByDescriptor(clazz,
+ gDvmInlineOpsTable[opIndex].methodName,
+ gDvmInlineOpsTable[opIndex].methodSignature);
+ if (method == NULL) {
+ LOGW("Warning: can't find method %s.%s %s\n",
+ clazz->descriptor,
+ gDvmInlineOpsTable[opIndex].methodName,
+ gDvmInlineOpsTable[opIndex].methodSignature);
+ goto skip_prof;
+ }
+
+ gDvm.inlinedMethods[opIndex] = method;
+ IF_LOGV() {
+ char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+ LOGV("Registered for profile: %s.%s %s\n",
+ method->clazz->descriptor, method->name, desc);
+ free(desc);
+ }
+ }
+
+ TRACE_METHOD_ENTER(self, method);
+ result = (*gDvmInlineOpsTable[opIndex].func)(arg0, arg1, arg2, arg3,
+ pResult);
+ TRACE_METHOD_EXIT(self, method);
+ return result;
+
+skip_prof:
+ return (*gDvmInlineOpsTable[opIndex].func)(arg0, arg1, arg2, arg3, pResult);
+}
diff --git a/vm/InlineNative.h b/vm/InlineNative.h
new file mode 100644
index 0000000..a54d9bd
--- /dev/null
+++ b/vm/InlineNative.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Inlined native functions.
+ */
+#ifndef _DALVIK_INLINENATIVE
+#define _DALVIK_INLINENATIVE
+
+/* startup/shutdown */
+bool dvmInlineNativeStartup(void);
+void dvmInlineNativeShutdown(void);
+
+/*
+ * Basic 4-argument inline operation handler.
+ */
+typedef bool (*InlineOp4Func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+ JValue* pResult);
+
+/*
+ * Table of inline operations.
+ *
+ * Try to keep this at a power-of-two size, so we don't have to multiply.
+ *
+ * TODO: might be to our advantage to generate a compact jump table on
+ * the heap at runtime (or just declare two static tables, one with full
+ * info and one with just function pointers). Especially useful if we decide
+ * to support other method call forms, e.g. /range. We can also just
+ * generate assembly code that knows how many args it needs and has the
+ * target address embedded.
+ */
+typedef struct InlineOperation {
+ InlineOp4Func func; /* MUST be first entry */
+ const char* classDescriptor;
+ const char* methodName;
+ const char* methodSignature;
+} InlineOperation;
+
+/* Must be kept in sync w/ gDvmInlineOpsTable in InlineNative.c */
+typedef enum NativeInlineOps {
+ INLINE_EMPTYINLINEMETHOD = 0,
+ INLINE_STRING_CHARAT = 1,
+ INLINE_STRING_COMPARETO = 2,
+ INLINE_STRING_EQUALS = 3,
+ INLINE_STRING_FASTINDEXOF_II = 4,
+ INLINE_STRING_IS_EMPTY = 5,
+ INLINE_STRING_LENGTH = 6,
+ INLINE_MATH_ABS_INT = 7,
+ INLINE_MATH_ABS_LONG = 8,
+ INLINE_MATH_ABS_FLOAT = 9,
+ INLINE_MATH_ABS_DOUBLE = 10,
+ INLINE_MATH_MIN_INT = 11,
+ INLINE_MATH_MAX_INT = 12,
+ INLINE_MATH_SQRT = 13,
+ INLINE_MATH_COS = 14,
+ INLINE_MATH_SIN = 15,
+ INLINE_FLOAT_TO_INT_BITS = 16,
+ INLINE_FLOAT_TO_RAW_INT_BITS = 17,
+ INLINE_INT_BITS_TO_FLOAT = 18,
+ INLINE_DOUBLE_TO_LONG_BITS = 19,
+ INLINE_DOUBLE_TO_RAW_LONG_BITS = 20,
+ INLINE_LONG_BITS_TO_DOUBLE = 21,
+} NativeInlineOps;
+
+/*
+ * Get the inlineops table.
+ */
+const InlineOperation* dvmGetInlineOpsTable(void);
+int dvmGetInlineOpsTableLength(void);
+
+/*
+ * The table, exposed so we can access it with C inlines. Prefer access
+ * through dvmGetInlineOpsTable().
+ */
+extern const InlineOperation gDvmInlineOpsTable[];
+
+/*
+ * Perform the operation specified by "opIndex".
+ *
+ * We want the arguments to appear in the first 4 registers so they can
+ * be passed straight through to the handler function. Ideally on ARM
+ * they'll go into r0-r3 and stay there.
+ *
+ * Returns "true" if everything went normally, "false" if an exception
+ * was thrown.
+ */
+INLINE bool dvmPerformInlineOp4Std(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+ JValue* pResult, int opIndex)
+{
+ return (*gDvmInlineOpsTable[opIndex].func)(arg0, arg1, arg2, arg3, pResult);
+}
+
+/*
+ * Like the "std" version, but will emit profiling info.
+ */
+bool dvmPerformInlineOp4Dbg(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
+ JValue* pResult, int opIndex);
+
+#endif /*_DALVIK_INLINENATIVE*/
diff --git a/vm/Inlines.c b/vm/Inlines.c
new file mode 100644
index 0000000..f1bd621
--- /dev/null
+++ b/vm/Inlines.c
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Generate non-inline copies of inline functions declared in header files.
+ */
+
+#define _DALVIK_GEN_INLINES
+#include "Dalvik.h"
+#include "analysis/CodeVerify.h"
+#include "analysis/RegisterMap.h"
+#include "mterp/c/header.c"
+
+#undef LOG_TAG
+#include "jdwp/JdwpPriv.h"
diff --git a/vm/Inlines.h b/vm/Inlines.h
new file mode 100644
index 0000000..f1580e3
--- /dev/null
+++ b/vm/Inlines.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * In gcc, "extern inline" ensures that the copy in the header is never
+ * turned into a separate function. This prevents us from having multiple
+ * non-inline copies. However, we still need to provide a non-inline
+ * version in the library for the benefit of applications that include our
+ * headers and are built with optimizations disabled. Either that, or use
+ * the "always_inline" gcc attribute to ensure that the non-inline version
+ * is never needed.
+ *
+ * (Note C99 has different notions about what the keyword combos mean.)
+ */
+#ifndef _DALVIK_GEN_INLINES /* only defined by Inlines.c */
+# define INLINE extern __inline__
+#else
+# define INLINE
+#endif
diff --git a/vm/Intern.c b/vm/Intern.c
new file mode 100644
index 0000000..8bc38b8
--- /dev/null
+++ b/vm/Intern.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * String interning.
+ */
+#include "Dalvik.h"
+
+#include <stddef.h>
+
+/*
+ * Prep string interning.
+ */
+bool dvmStringInternStartup(void)
+{
+ dvmInitMutex(&gDvm.internLock);
+ gDvm.internedStrings = dvmHashTableCreate(256, NULL);
+ if (gDvm.internedStrings == NULL)
+ return false;
+ gDvm.literalStrings = dvmHashTableCreate(256, NULL);
+ if (gDvm.literalStrings == NULL)
+ return false;
+ return true;
+}
+
+/*
+ * Chuck the intern list.
+ *
+ * The contents of the list are StringObjects that live on the GC heap.
+ */
+void dvmStringInternShutdown(void)
+{
+ if (gDvm.internedStrings != NULL || gDvm.literalStrings != NULL) {
+ dvmDestroyMutex(&gDvm.internLock);
+ }
+ dvmHashTableFree(gDvm.internedStrings);
+ gDvm.internedStrings = NULL;
+ dvmHashTableFree(gDvm.literalStrings);
+ gDvm.literalStrings = NULL;
+}
+
+static StringObject* lookupInternedString(StringObject* strObj, bool isLiteral)
+{
+ StringObject* found;
+ u4 hash;
+
+ assert(strObj != NULL);
+ hash = dvmComputeStringHash(strObj);
+ dvmLockMutex(&gDvm.internLock);
+ if (isLiteral) {
+ /*
+ * Check the literal table for a match.
+ */
+ StringObject* literal = dvmHashTableLookup(gDvm.literalStrings,
+ hash, strObj,
+ dvmHashcmpStrings,
+ false);
+ if (literal != NULL) {
+ /*
+ * A match was found in the literal table, the easy case.
+ */
+ found = literal;
+ } else {
+ /*
+ * There is no match in the literal table, check the
+ * interned string table.
+ */
+ StringObject* interned = dvmHashTableLookup(gDvm.internedStrings,
+ hash, strObj,
+ dvmHashcmpStrings,
+ false);
+ if (interned != NULL) {
+ /*
+ * A match was found in the interned table. Move the
+ * matching string to the literal table.
+ */
+ dvmHashTableRemove(gDvm.internedStrings, hash, interned);
+ found = dvmHashTableLookup(gDvm.literalStrings,
+ hash, interned,
+ dvmHashcmpStrings,
+ true);
+ assert(found == interned);
+ } else {
+ /*
+ * No match in the literal table or the interned
+ * table. Insert into the literal table.
+ */
+ found = dvmHashTableLookup(gDvm.literalStrings,
+ hash, strObj,
+ dvmHashcmpStrings,
+ true);
+ assert(found == strObj);
+ }
+ }
+ } else {
+ /*
+ * Check the literal table for a match.
+ */
+ found = dvmHashTableLookup(gDvm.literalStrings,
+ hash, strObj,
+ dvmHashcmpStrings,
+ false);
+ if (found == NULL) {
+ /*
+ * No match was found in the literal table. Insert into
+ * the intern table.
+ */
+ found = dvmHashTableLookup(gDvm.internedStrings,
+ hash, strObj,
+ dvmHashcmpStrings,
+ true);
+ }
+ }
+ assert(found != NULL);
+ dvmUnlockMutex(&gDvm.internLock);
+ return found;
+}
+
+/*
+ * Find an entry in the interned string table.
+ *
+ * If the string doesn't already exist, the StringObject is added to
+ * the table. Otherwise, the existing entry is returned.
+ */
+StringObject* dvmLookupInternedString(StringObject* strObj)
+{
+ return lookupInternedString(strObj, false);
+}
+
+/*
+ * Same as dvmLookupInternedString(), but guarantees that the
+ * returned string is a literal.
+ */
+StringObject* dvmLookupImmortalInternedString(StringObject* strObj)
+{
+ return lookupInternedString(strObj, true);
+}
+
+/*
+ * Returns true if the object is a weak interned string. Any string
+ * interned by the user is weak.
+ */
+bool dvmIsWeakInternedString(const StringObject* strObj)
+{
+ StringObject* found;
+ u4 hash;
+
+ assert(strObj != NULL);
+ if (gDvm.internedStrings == NULL) {
+ return false;
+ }
+ dvmLockMutex(&gDvm.internLock);
+ hash = dvmComputeStringHash(strObj);
+ found = dvmHashTableLookup(gDvm.internedStrings, hash, (void*)strObj,
+ dvmHashcmpStrings, false);
+ dvmUnlockMutex(&gDvm.internLock);
+ return found == strObj;
+}
+
+static int markStringObject(void* strObj, void* arg)
+{
+ UNUSED_PARAMETER(arg);
+ dvmMarkObjectNonNull(strObj);
+ return 0;
+}
+
+/*
+ * Blacken string references from the literal string table. The
+ * literal table is a root.
+ */
+void dvmGcScanInternedStrings()
+{
+ /* It's possible for a GC to happen before dvmStringInternStartup()
+ * is called.
+ */
+ if (gDvm.literalStrings != NULL) {
+ dvmHashForeach(gDvm.literalStrings, markStringObject, NULL);
+ }
+}
+
+/*
+ * Clear white references from the intern table.
+ */
+void dvmGcDetachDeadInternedStrings(int (*isUnmarkedObject)(void *))
+{
+ /* It's possible for a GC to happen before dvmStringInternStartup()
+ * is called.
+ */
+ if (gDvm.internedStrings != NULL) {
+ dvmLockMutex(&gDvm.internLock);
+ dvmHashForeachRemove(gDvm.internedStrings, isUnmarkedObject);
+ dvmUnlockMutex(&gDvm.internLock);
+ }
+}
diff --git a/vm/Intern.h b/vm/Intern.h
new file mode 100644
index 0000000..a378fa5
--- /dev/null
+++ b/vm/Intern.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Interned strings.
+ */
+#ifndef _DALVIK_INTERN
+#define _DALVIK_INTERN
+
+bool dvmStringInternStartup(void);
+void dvmStringInternShutdown(void);
+StringObject* dvmLookupInternedString(StringObject* strObj);
+StringObject* dvmLookupImmortalInternedString(StringObject* strObj);
+bool dvmIsWeakInternedString(const StringObject* strObj);
+
+#endif /*_DALVIK_INTERN*/
diff --git a/vm/JarFile.c b/vm/JarFile.c
new file mode 100644
index 0000000..39eb8d1
--- /dev/null
+++ b/vm/JarFile.c
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Access the contents of a Jar file.
+ *
+ * This isn't actually concerned with any of the Jar-like elements; it
+ * just wants a zip archive with "classes.dex" inside. In Android the
+ * most common example is ".apk".
+ */
+#include "Dalvik.h"
+#include "libdex/OptInvocation.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <zlib.h>
+#include <fcntl.h>
+#include <errno.h>
+
+static const char* kDexInJarName = "classes.dex";
+
+/*
+ * Attempt to open a file whose name is similar to <fileName>,
+ * but with the supplied suffix. E.g.,
+ * openAlternateSuffix("Home.apk", "dex", O_RDONLY) will attempt
+ * to open "Home.dex". If the open succeeds, a pointer to a
+ * malloc()ed copy of the opened file name will be put in <*pCachedName>.
+ *
+ * <flags> is passed directly to open(). O_CREAT is not supported.
+ */
+static int openAlternateSuffix(const char *fileName, const char *suffix,
+ int flags, char **pCachedName)
+{
+ char *buf, *c;
+ size_t fileNameLen = strlen(fileName);
+ size_t suffixLen = strlen(suffix);
+ size_t bufLen = fileNameLen + suffixLen + 1;
+ int fd = -1;
+
+ buf = malloc(bufLen);
+ if (buf == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /* Copy the original filename into the buffer, find
+ * the last dot, and copy the suffix to just after it.
+ */
+ memcpy(buf, fileName, fileNameLen + 1);
+ c = strrchr(buf, '.');
+ if (c == NULL) {
+ errno = ENOENT;
+ goto bail;
+ }
+ memcpy(c + 1, suffix, suffixLen + 1);
+
+ fd = open(buf, flags);
+ if (fd >= 0) {
+ *pCachedName = buf;
+ return fd;
+ }
+ LOGV("Couldn't open %s: %s\n", buf, strerror(errno));
+bail:
+ free(buf);
+ return -1;
+}
+
+/*
+ * Checks the dependencies of the dex cache file corresponding
+ * to the jar file at the absolute path "fileName".
+ */
+DexCacheStatus dvmDexCacheStatus(const char *fileName)
+{
+ ZipArchive archive;
+ char* cachedName = NULL;
+ int fd;
+ DexCacheStatus result = DEX_CACHE_ERROR;
+ ZipEntry entry;
+
+ /* Always treat elements of the bootclasspath as up-to-date.
+ * The fact that interpreted code is running at all means that this
+ * should be true.
+ */
+ if (dvmClassPathContains(gDvm.bootClassPath, fileName)) {
+ return DEX_CACHE_OK;
+ }
+
+ //TODO: match dvmJarFileOpen()'s logic. Not super-important
+ // (the odex-first logic is only necessary for dexpreopt)
+ // but it would be nice to be consistent.
+
+ /* Try to find the dex file inside of the archive.
+ */
+ if (dexZipOpenArchive(fileName, &archive) != 0) {
+ return DEX_CACHE_BAD_ARCHIVE;
+ }
+ entry = dexZipFindEntry(&archive, kDexInJarName);
+ if (entry != NULL) {
+ bool newFile = false;
+
+ /*
+ * See if there's an up-to-date copy of the optimized dex
+ * in the cache, but don't create one if there isn't.
+ */
+ LOGV("dvmDexCacheStatus: Checking cache for %s\n", fileName);
+ cachedName = dexOptGenerateCacheFileName(fileName, kDexInJarName);
+ if (cachedName == NULL)
+ return -1;
+
+ fd = dvmOpenCachedDexFile(fileName, cachedName,
+ dexGetZipEntryModTime(&archive, entry),
+ dexGetZipEntryCrc32(&archive, entry),
+ /*isBootstrap=*/false, &newFile, /*createIfMissing=*/false);
+ LOGV("dvmOpenCachedDexFile returned fd %d\n", fd);
+ if (fd < 0) {
+ result = DEX_CACHE_STALE;
+ goto bail;
+ }
+
+ /* dvmOpenCachedDexFile locks the file as a side-effect.
+ * Unlock and close it.
+ */
+ if (!dvmUnlockCachedDexFile(fd)) {
+ /* uh oh -- this process needs to exit or we'll wedge the system */
+ LOGE("Unable to unlock DEX file\n");
+ goto bail;
+ }
+
+ /* When createIfMissing is false, dvmOpenCachedDexFile() only
+ * returns a valid fd if the cache file is up-to-date.
+ */
+ } else {
+ /*
+ * There's no dex file in the jar file. See if there's an
+ * optimized dex file living alongside the jar.
+ */
+ fd = openAlternateSuffix(fileName, "odex", O_RDONLY, &cachedName);
+ if (fd < 0) {
+ LOGI("Zip is good, but no %s inside, and no .odex "
+ "file in the same directory\n", kDexInJarName);
+ result = DEX_CACHE_BAD_ARCHIVE;
+ goto bail;
+ }
+
+ LOGV("Using alternate file (odex) for %s ...\n", fileName);
+ if (!dvmCheckOptHeaderAndDependencies(fd, false, 0, 0, true, true)) {
+ LOGE("%s odex has stale dependencies\n", fileName);
+ LOGE("odex source not available -- failing\n");
+ result = DEX_CACHE_STALE_ODEX;
+ goto bail;
+ } else {
+ LOGV("%s odex has good dependencies\n", fileName);
+ }
+ }
+ result = DEX_CACHE_OK;
+
+bail:
+ dexZipCloseArchive(&archive);
+ free(cachedName);
+ if (fd >= 0) {
+ close(fd);
+ }
+ return result;
+}
+
+/*
+ * Open a Jar file. It's okay if it's just a Zip archive without all of
+ * the Jar trimmings, but we do insist on finding "classes.dex" inside
+ * or an appropriately-named ".odex" file alongside.
+ *
+ * If "isBootstrap" is not set, the optimizer/verifier regards this DEX as
+ * being part of a different class loader.
+ */
+int dvmJarFileOpen(const char* fileName, const char* odexOutputName,
+ JarFile** ppJarFile, bool isBootstrap)
+{
+ ZipArchive archive;
+ DvmDex* pDvmDex = NULL;
+ char* cachedName = NULL;
+ bool archiveOpen = false;
+ bool locked = false;
+ int fd = -1;
+ int result = -1;
+
+ /* Even if we're not going to look at the archive, we need to
+ * open it so we can stuff it into ppJarFile.
+ */
+ if (dexZipOpenArchive(fileName, &archive) != 0)
+ goto bail;
+ archiveOpen = true;
+
+ /* If we fork/exec into dexopt, don't let it inherit the archive's fd.
+ */
+ dvmSetCloseOnExec(dexZipGetArchiveFd(&archive));
+
+ /* First, look for a ".odex" alongside the jar file. It will
+ * have the same name/path except for the extension.
+ */
+ fd = openAlternateSuffix(fileName, "odex", O_RDONLY, &cachedName);
+ if (fd >= 0) {
+ LOGV("Using alternate file (odex) for %s ...\n", fileName);
+ if (!dvmCheckOptHeaderAndDependencies(fd, false, 0, 0, true, true)) {
+ LOGE("%s odex has stale dependencies\n", fileName);
+ free(cachedName);
+ close(fd);
+ fd = -1;
+ goto tryArchive;
+ } else {
+ LOGV("%s odex has good dependencies\n", fileName);
+ //TODO: make sure that the .odex actually corresponds
+ // to the classes.dex inside the archive (if present).
+ // For typical use there will be no classes.dex.
+ }
+ } else {
+ ZipEntry entry;
+
+tryArchive:
+ /*
+ * Pre-created .odex absent or stale. Look inside the jar for a
+ * "classes.dex".
+ */
+ entry = dexZipFindEntry(&archive, kDexInJarName);
+ if (entry != NULL) {
+ bool newFile = false;
+
+ /*
+ * We've found the one we want. See if there's an up-to-date copy
+ * in the cache.
+ *
+ * On return, "fd" will be seeked just past the "opt" header.
+ *
+ * If a stale .odex file is present and classes.dex exists in
+ * the archive, this will *not* return an fd pointing to the
+ * .odex file; the fd will point into dalvik-cache like any
+ * other jar.
+ */
+ if (odexOutputName == NULL) {
+ cachedName = dexOptGenerateCacheFileName(fileName,
+ kDexInJarName);
+ if (cachedName == NULL)
+ goto bail;
+ } else {
+ cachedName = strdup(odexOutputName);
+ }
+ LOGV("dvmDexCacheStatus: Checking cache for %s (%s)\n",
+ fileName, cachedName);
+ fd = dvmOpenCachedDexFile(fileName, cachedName,
+ dexGetZipEntryModTime(&archive, entry),
+ dexGetZipEntryCrc32(&archive, entry),
+ isBootstrap, &newFile, /*createIfMissing=*/true);
+ if (fd < 0) {
+ LOGI("Unable to open or create cache for %s (%s)\n",
+ fileName, cachedName);
+ goto bail;
+ }
+ locked = true;
+
+ /*
+ * If fd points to a new file (because there was no cached version,
+ * or the cached version was stale), generate the optimized DEX.
+ * The file descriptor returned is still locked, and is positioned
+ * just past the optimization header.
+ */
+ if (newFile) {
+ u8 startWhen, extractWhen, endWhen;
+ bool result;
+ off_t dexOffset;
+
+ dexOffset = lseek(fd, 0, SEEK_CUR);
+ result = (dexOffset > 0);
+
+ if (result) {
+ startWhen = dvmGetRelativeTimeUsec();
+ result = dexZipExtractEntryToFile(&archive, entry, fd) == 0;
+ extractWhen = dvmGetRelativeTimeUsec();
+ }
+ if (result) {
+ result = dvmOptimizeDexFile(fd, dexOffset,
+ dexGetZipEntryUncompLen(&archive, entry),
+ fileName,
+ dexGetZipEntryModTime(&archive, entry),
+ dexGetZipEntryCrc32(&archive, entry),
+ isBootstrap);
+ }
+
+ if (!result) {
+ LOGE("Unable to extract+optimize DEX from '%s'\n",
+ fileName);
+ goto bail;
+ }
+
+ endWhen = dvmGetRelativeTimeUsec();
+ LOGD("DEX prep '%s': unzip in %dms, rewrite %dms\n",
+ fileName,
+ (int) (extractWhen - startWhen) / 1000,
+ (int) (endWhen - extractWhen) / 1000);
+ }
+ } else {
+ LOGI("Zip is good, but no %s inside, and no valid .odex "
+ "file in the same directory\n", kDexInJarName);
+ goto bail;
+ }
+ }
+
+ /*
+ * Map the cached version. This immediately rewinds the fd, so it
+ * doesn't have to be seeked anywhere in particular.
+ */
+ if (dvmDexFileOpenFromFd(fd, &pDvmDex) != 0) {
+ LOGI("Unable to map %s in %s\n", kDexInJarName, fileName);
+ goto bail;
+ }
+
+ if (locked) {
+ /* unlock the fd */
+ if (!dvmUnlockCachedDexFile(fd)) {
+ /* uh oh -- this process needs to exit or we'll wedge the system */
+ LOGE("Unable to unlock DEX file\n");
+ goto bail;
+ }
+ locked = false;
+ }
+
+ LOGV("Successfully opened '%s' in '%s'\n", kDexInJarName, fileName);
+
+ *ppJarFile = (JarFile*) calloc(1, sizeof(JarFile));
+ (*ppJarFile)->archive = archive;
+ (*ppJarFile)->cacheFileName = cachedName;
+ (*ppJarFile)->pDvmDex = pDvmDex;
+ cachedName = NULL; // don't free it below
+ result = 0;
+
+bail:
+ /* clean up, closing the open file */
+ if (archiveOpen && result != 0)
+ dexZipCloseArchive(&archive);
+ free(cachedName);
+ if (fd >= 0) {
+ if (locked)
+ (void) dvmUnlockCachedDexFile(fd);
+ close(fd);
+ }
+ return result;
+}
+
+/*
+ * Close a Jar file and free the struct.
+ */
+void dvmJarFileFree(JarFile* pJarFile)
+{
+ if (pJarFile == NULL)
+ return;
+
+ dvmDexFileFree(pJarFile->pDvmDex);
+ dexZipCloseArchive(&pJarFile->archive);
+ free(pJarFile->cacheFileName);
+ free(pJarFile);
+}
diff --git a/vm/JarFile.h b/vm/JarFile.h
new file mode 100644
index 0000000..36849ec
--- /dev/null
+++ b/vm/JarFile.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Decode jar/apk/zip files.
+ */
+#ifndef _DALVIK_JARFILE
+#define _DALVIK_JARFILE
+
+/*
+ * This represents an open, scanned Jar file. (It's actually for any Zip
+ * archive that happens to hold a Dex file.)
+ */
+typedef struct JarFile {
+ ZipArchive archive;
+ //MemMapping map;
+ char* cacheFileName;
+ DvmDex* pDvmDex;
+} JarFile;
+
+/*
+ * Open the Zip archive and get a list of the classfile entries.
+ *
+ * On success, returns 0 and sets "*ppJarFile" to a newly-allocated JarFile.
+ * On failure, returns a meaningful error code [currently just -1].
+ */
+int dvmJarFileOpen(const char* fileName, const char* odexOutputName,
+ JarFile** ppJarFile, bool isBootstrap);
+
+/*
+ * Free a JarFile structure, along with any associated structures.
+ */
+void dvmJarFileFree(JarFile* pJarFile);
+
+/* pry the DexFile out of a JarFile */
+INLINE DvmDex* dvmGetJarFileDex(JarFile* pJarFile) {
+ return pJarFile->pDvmDex;
+}
+
+/* get full path of optimized DEX file */
+INLINE const char* dvmGetJarFileCacheFileName(JarFile* pJarFile) {
+ return pJarFile->cacheFileName;
+}
+
+typedef enum DexCacheStatus {
+ DEX_CACHE_ERROR = -2,
+ DEX_CACHE_BAD_ARCHIVE = -1,
+ DEX_CACHE_OK = 0,
+ DEX_CACHE_STALE,
+ DEX_CACHE_STALE_ODEX,
+} DexCacheStatus;
+
+/*
+ * Checks the dependencies of the dex cache file corresponding
+ * to the jar file at the absolute path "fileName".
+ */
+DexCacheStatus dvmDexCacheStatus(const char *fileName);
+
+#endif /*_DALVIK_JARFILE*/
diff --git a/vm/Jni.c b/vm/Jni.c
new file mode 100644
index 0000000..ef0749a
--- /dev/null
+++ b/vm/Jni.c
@@ -0,0 +1,4509 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik implementation of JNI interfaces.
+ */
+#include "Dalvik.h"
+#include "JniInternal.h"
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <limits.h>
+
+/*
+Native methods and interaction with the GC
+
+All JNI methods must start by changing their thread status to
+THREAD_RUNNING, and finish by changing it back to THREAD_NATIVE before
+returning to native code. The switch to "running" triggers a thread
+suspension check.
+
+With a rudimentary GC we should be able to skip the status change for
+simple functions, e.g. IsSameObject, GetJavaVM, GetStringLength, maybe
+even access to fields with primitive types. Our options are more limited
+with a compacting GC, so we should replace JNI_ENTER with JNI_ENTER_NCGC
+or somesuch on the "lite" functions if we want to try this optimization.
+
+For performance reasons we do as little error-checking as possible here.
+For example, we don't check to make sure the correct type of Object is
+passed in when setting a field, and we don't prevent you from storing
+new values in a "final" field. Such things are best handled in the
+"check" version. For actions that are common, dangerous, and must be
+checked at runtime, such as array bounds checks, we do the tests here.
+
+
+General notes on local/global reference tracking
+
+JNI provides explicit control over natively-held references that the GC
+needs to know about. These can be local, in which case they're released
+when the native method returns into the VM, or global, which are held
+until explicitly released. (There are also weak-global references,
+which have the lifespan and visibility of global references, but the
+object they refer to may be collected.)
+
+The references can be created with explicit JNI NewLocalRef / NewGlobalRef
+calls. The former is very unusual, the latter is reasonably common
+(e.g. for caching references to class objects).
+
+Local references are most often created as a side-effect of JNI functions.
+For example, the AllocObject/NewObject functions must create local
+references to the objects returned, because nothing else in the GC root
+set has a reference to the new objects.
+
+The most common mode of operation is for a method to create zero or
+more local references and return. Explicit "local delete" operations
+are expected to be exceedingly rare, except when walking through an
+object array, and the Push/PopLocalFrame calls are expected to be used
+infrequently. For efficient operation, we want to add new local refs
+with a simple store/increment operation; to avoid infinite growth in
+pathological situations, we need to reclaim the space used by deleted
+entries.
+
+If we just want to maintain a list for the GC root set, we can use an
+expanding append-only array that compacts when objects are deleted.
+In typical situations, e.g. running through an array of objects, we will
+be deleting one of the most recently added entries, so we can minimize
+the number of elements moved (or avoid having to move any).
+
+If we want to conceal the pointer values from native code, which is
+necessary to allow the GC to move JNI-referenced objects around, then we
+have to use a more complicated indirection mechanism.
+
+The spec says, "Local references are only valid in the thread in which
+they are created. The native code must not pass local references from
+one thread to another."
+
+
+Pinned objects
+
+For some large chunks of data, notably primitive arrays and String data,
+JNI allows the VM to choose whether it wants to pin the array object or
+make a copy. We currently pin the memory for better execution performance.
+
+TODO: we're using simple root set references to pin primitive array data,
+because they have the property we need (i.e. the pointer we return is
+guaranteed valid until we explicitly release it). However, if we have a
+compacting GC and don't want to pin all memory held by all global refs,
+we need to treat these differently.
+
+
+Global reference tracking
+
+There should be a small "active" set centered around the most-recently
+added items.
+
+Because it's global, access to it has to be synchronized. Additions and
+removals require grabbing a mutex. If the table serves as an indirection
+mechanism (i.e. it's not just a list for the benefit of the garbage
+collector), reference lookups may also require grabbing a mutex.
+
+The JNI spec does not define any sort of limit, so the list must be able
+to expand to a reasonable size. It may be useful to log significant
+increases in usage to help identify resource leaks.
+
+
+Weak-global reference tracking
+
+[TBD]
+
+
+Local reference tracking
+
+The table of local references can be stored on the interpreted stack or
+in a parallel data structure (one per thread).
+
+*** Approach #1: use the interpreted stack
+
+The easiest place to tuck it is between the frame ptr and the first saved
+register, which is always in0. (See the ASCII art in Stack.h.) We can
+shift the "VM-specific goop" and frame ptr down, effectively inserting
+the JNI local refs in the space normally occupied by local variables.
+
+(Three things are accessed from the frame pointer:
+ (1) framePtr[N] is register vN, used to get at "ins" and "locals".
+ (2) framePtr - sizeof(StackSaveArea) is the VM frame goop.
+ (3) framePtr - sizeof(StackSaveArea) - numOuts is where the "outs" go.
+The only thing that isn't determined by an offset from the current FP
+is the previous frame. However, tucking things below the previous frame
+can be problematic because the "outs" of the previous frame overlap with
+the "ins" of the current frame. If the "ins" are altered they must be
+restored before we return. For a native method call, the easiest and
+safest thing to disrupt is #1, because there are no locals and the "ins"
+are all copied to the native stack.)
+
+We can implement Push/PopLocalFrame with the existing stack frame calls,
+making sure we copy some goop from the previous frame (notably the method
+ptr, so that dvmGetCurrentJNIMethod() doesn't require extra effort).
+
+We can pre-allocate the storage at the time the stack frame is first
+set up, but we have to be careful. When calling from interpreted code
+the frame ptr points directly at the arguments we're passing, but we can
+offset the args pointer when calling the native bridge.
+
+To manage the local ref collection, we need to be able to find three
+things: (1) the start of the region, (2) the end of the region, and (3)
+the next available entry. The last is only required for quick adds.
+We currently have two easily-accessible pointers, the current FP and the
+previous frame's FP. (The "stack pointer" shown in the ASCII art doesn't
+actually exist in the interpreted world.)
+
+We can't use the current FP to find the first "in", because we want to
+insert the variable-sized local refs table between them. It's awkward
+to use the previous frame's FP because native methods invoked via
+dvmCallMethod() or dvmInvokeMethod() don't have "ins", but native methods
+invoked from interpreted code do. We can either track the local refs
+table size with a field in the stack frame, or insert unnecessary items
+so that all native stack frames have "ins".
+
+Assuming we can find the region bounds, we still need pointer #3
+for an efficient implementation. This can be stored in an otherwise
+unused-for-native field in the frame goop.
+
+When we run out of room we have to make more space. If we start allocating
+locals immediately below in0 and grow downward, we will detect end-of-space
+by running into the current frame's FP. We then memmove() the goop down
+(memcpy if we guarantee the additional size is larger than the frame).
+This is nice because we only have to move sizeof(StackSaveArea) bytes
+each time.
+
+Stack walking should be okay so long as nothing tries to access the
+"ins" by an offset from the FP. In theory the "ins" could be read by
+the debugger or SIGQUIT handler looking for "this" or other arguments,
+but in practice this behavior isn't expected to work for native methods,
+so we can simply disallow it.
+
+A conservative GC can just scan the entire stack from top to bottom to find
+all references. An exact GC will need to understand the actual layout.
+
+*** Approach #2: use a parallel stack
+
+Each Thread/JNIEnv points to a reference table. The struct has
+a system-heap-allocated array of references and a pointer to the
+next-available entry ("nextEntry").
+
+Each stack frame has a pointer to what it sees as the "bottom" element
+in the array (we can double-up the "currentPc" field). This is set to
+"nextEntry" when the frame is pushed on. As local references are added
+or removed, "nextEntry" is updated.
+
+We implement Push/PopLocalFrame with actual stack frames. Before a JNI
+frame gets popped, we set "nextEntry" to the "top" pointer of the current
+frame, effectively releasing the references.
+
+The GC will scan all references from the start of the table to the
+"nextEntry" pointer.
+
+*** Comparison
+
+All approaches will return a failure result when they run out of local
+reference space. For #1 that means blowing out the stack, for #2 it's
+running out of room in the array.
+
+Compared to #1, approach #2:
+ - Needs only one pointer in the stack frame goop.
+ - Makes pre-allocating storage unnecessary.
+ - Doesn't contend with interpreted stack depth for space. In most
+ cases, if something blows out the local ref storage, it's because the
+ JNI code was misbehaving rather than called from way down.
+ - Allows the GC to do a linear scan per thread in a buffer that is 100%
+ references. The GC can be slightly less smart when scanning the stack.
+ - Will be easier to work with if we combine native and interpeted stacks.
+
+ - Isn't as clean, especially when popping frames, since we have to do
+ explicit work. Fortunately we only have to do it when popping native
+ method calls off, so it doesn't add overhead to interpreted code paths.
+ - Is awkward to expand dynamically. We'll want to pre-allocate the full
+ amount of space; this is fine, since something on the order of 1KB should
+ be plenty. The JNI spec allows us to limit this.
+ - Requires the GC to scan even more memory. With the references embedded
+ in the stack we get better locality of reference.
+
+*/
+
+/* fwd */
+static const struct JNINativeInterface gNativeInterface;
+static jobject addGlobalReference(Object* obj);
+
+#ifdef WITH_JNI_STACK_CHECK
+# define COMPUTE_STACK_SUM(_self) computeStackSum(_self);
+# define CHECK_STACK_SUM(_self) checkStackSum(_self);
+//static void computeStackSum(Thread* self);
+//static void checkStackSum(Thread* self);
+#else
+# define COMPUTE_STACK_SUM(_self) ((void)0)
+# define CHECK_STACK_SUM(_self) ((void)0)
+#endif
+
+
+/*
+ * ===========================================================================
+ * Utility functions
+ * ===========================================================================
+ */
+
+/*
+ * Entry/exit processing for all JNI calls.
+ *
+ * If TRUSTED_JNIENV is set, we get to skip the (curiously expensive)
+ * thread-local storage lookup on our Thread*. If the caller has passed
+ * the wrong JNIEnv in, we're going to be accessing unsynchronized
+ * structures from more than one thread, and things are going to fail
+ * in bizarre ways. This is only sensible if the native code has been
+ * fully exercised with CheckJNI enabled.
+ */
+#define TRUSTED_JNIENV
+#ifdef TRUSTED_JNIENV
+# define JNI_ENTER() \
+ Thread* _self = ((JNIEnvExt*)env)->self; \
+ CHECK_STACK_SUM(_self); \
+ dvmChangeStatus(_self, THREAD_RUNNING)
+#else
+# define JNI_ENTER() \
+ Thread* _self = dvmThreadSelf(); \
+ UNUSED_PARAMETER(env); \
+ CHECK_STACK_SUM(_self); \
+ dvmChangeStatus(_self, THREAD_RUNNING)
+#endif
+#define JNI_EXIT() \
+ dvmChangeStatus(_self, THREAD_NATIVE); \
+ COMPUTE_STACK_SUM(_self)
+
+#define kGlobalRefsTableInitialSize 512
+#define kGlobalRefsTableMaxSize 51200 /* arbitrary, must be < 64K */
+#define kGrefWaterInterval 100
+#define kTrackGrefUsage true
+
+#define kPinTableInitialSize 16
+#define kPinTableMaxSize 1024
+#define kPinComplainThreshold 10
+
+/*
+ * Allocate the global references table, and look up some classes for
+ * the benefit of direct buffer access.
+ */
+bool dvmJniStartup(void)
+{
+#ifdef USE_INDIRECT_REF
+ if (!dvmInitIndirectRefTable(&gDvm.jniGlobalRefTable,
+ kGlobalRefsTableInitialSize, kGlobalRefsTableMaxSize,
+ kIndirectKindGlobal))
+ return false;
+#else
+ if (!dvmInitReferenceTable(&gDvm.jniGlobalRefTable,
+ kGlobalRefsTableInitialSize, kGlobalRefsTableMaxSize))
+ return false;
+#endif
+
+ dvmInitMutex(&gDvm.jniGlobalRefLock);
+ gDvm.jniGlobalRefLoMark = 0;
+ gDvm.jniGlobalRefHiMark = kGrefWaterInterval * 2;
+
+ if (!dvmInitReferenceTable(&gDvm.jniPinRefTable,
+ kPinTableInitialSize, kPinTableMaxSize))
+ return false;
+
+ dvmInitMutex(&gDvm.jniPinRefLock);
+
+ Method* meth;
+
+ /*
+ * Grab the PhantomReference constructor.
+ */
+ gDvm.classJavaLangRefPhantomReference =
+ dvmFindSystemClassNoInit("Ljava/lang/ref/PhantomReference;");
+ if (gDvm.classJavaLangRefPhantomReference == NULL) {
+ LOGE("Unable to find PhantomReference class\n");
+ return false;
+ }
+ meth= dvmFindDirectMethodByDescriptor(gDvm.classJavaLangRefPhantomReference,
+ "<init>", "(Ljava/lang/Object;Ljava/lang/ref/ReferenceQueue;)V");
+ if (meth == NULL) {
+ LOGE("Unable to find constructor for PhantomReference\n");
+ return false;
+ }
+ gDvm.methJavaLangRefPhantomReference_init = meth;
+
+
+ /*
+ * Look up and cache pointers to some direct buffer classes, fields,
+ * and methods.
+ */
+ ClassObject* platformAddressClass =
+ dvmFindSystemClassNoInit("Lorg/apache/harmony/luni/platform/PlatformAddress;");
+ ClassObject* platformAddressFactoryClass =
+ dvmFindSystemClassNoInit("Lorg/apache/harmony/luni/platform/PlatformAddressFactory;");
+ ClassObject* directBufferClass =
+ dvmFindSystemClassNoInit("Lorg/apache/harmony/nio/internal/DirectBuffer;");
+ ClassObject* readWriteBufferClass =
+ dvmFindSystemClassNoInit("Ljava/nio/ReadWriteDirectByteBuffer;");
+ ClassObject* bufferClass =
+ dvmFindSystemClassNoInit("Ljava/nio/Buffer;");
+
+ if (platformAddressClass == NULL || platformAddressFactoryClass == NULL ||
+ directBufferClass == NULL || readWriteBufferClass == NULL ||
+ bufferClass == NULL)
+ {
+ LOGE("Unable to find internal direct buffer classes\n");
+ return false;
+ }
+ gDvm.classJavaNioReadWriteDirectByteBuffer = readWriteBufferClass;
+ gDvm.classOrgApacheHarmonyNioInternalDirectBuffer = directBufferClass;
+ /* need a global reference for extended CheckJNI tests */
+ gDvm.jclassOrgApacheHarmonyNioInternalDirectBuffer =
+ addGlobalReference((Object*) directBufferClass);
+
+ /*
+ * We need a Method* here rather than a vtable offset, because
+ * DirectBuffer is an interface class.
+ */
+ meth = dvmFindVirtualMethodByDescriptor(
+ gDvm.classOrgApacheHarmonyNioInternalDirectBuffer,
+ "getEffectiveAddress",
+ "()Lorg/apache/harmony/luni/platform/PlatformAddress;");
+ if (meth == NULL) {
+ LOGE("Unable to find PlatformAddress.getEffectiveAddress\n");
+ return false;
+ }
+ gDvm.methOrgApacheHarmonyNioInternalDirectBuffer_getEffectiveAddress = meth;
+
+ meth = dvmFindVirtualMethodByDescriptor(platformAddressClass,
+ "toLong", "()J");
+ if (meth == NULL) {
+ LOGE("Unable to find PlatformAddress.toLong\n");
+ return false;
+ }
+ gDvm.voffOrgApacheHarmonyLuniPlatformPlatformAddress_toLong =
+ meth->methodIndex;
+
+ meth = dvmFindDirectMethodByDescriptor(platformAddressFactoryClass,
+ "on",
+ "(I)Lorg/apache/harmony/luni/platform/PlatformAddress;");
+ if (meth == NULL) {
+ LOGE("Unable to find PlatformAddressFactory.on\n");
+ return false;
+ }
+ gDvm.methOrgApacheHarmonyLuniPlatformPlatformAddress_on = meth;
+
+ meth = dvmFindDirectMethodByDescriptor(readWriteBufferClass,
+ "<init>",
+ "(Lorg/apache/harmony/luni/platform/PlatformAddress;II)V");
+ if (meth == NULL) {
+ LOGE("Unable to find ReadWriteDirectByteBuffer.<init>\n");
+ return false;
+ }
+ gDvm.methJavaNioReadWriteDirectByteBuffer_init = meth;
+
+ gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr =
+ dvmFindFieldOffset(platformAddressClass, "osaddr", "I");
+ if (gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr < 0) {
+ LOGE("Unable to find PlatformAddress.osaddr\n");
+ return false;
+ }
+
+ gDvm.offJavaNioBuffer_capacity =
+ dvmFindFieldOffset(bufferClass, "capacity", "I");
+ if (gDvm.offJavaNioBuffer_capacity < 0) {
+ LOGE("Unable to find Buffer.capacity\n");
+ return false;
+ }
+
+ gDvm.offJavaNioBuffer_effectiveDirectAddress =
+ dvmFindFieldOffset(bufferClass, "effectiveDirectAddress", "I");
+ if (gDvm.offJavaNioBuffer_effectiveDirectAddress < 0) {
+ LOGE("Unable to find Buffer.effectiveDirectAddress\n");
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Free the global references table.
+ */
+void dvmJniShutdown(void)
+{
+#ifdef USE_INDIRECT_REF
+ dvmClearIndirectRefTable(&gDvm.jniGlobalRefTable);
+#else
+ dvmClearReferenceTable(&gDvm.jniGlobalRefTable);
+#endif
+ dvmClearReferenceTable(&gDvm.jniPinRefTable);
+}
+
+
+/*
+ * Find the JNIEnv associated with the current thread.
+ *
+ * Currently stored in the Thread struct. Could also just drop this into
+ * thread-local storage.
+ */
+JNIEnvExt* dvmGetJNIEnvForThread(void)
+{
+ Thread* self = dvmThreadSelf();
+ if (self == NULL)
+ return NULL;
+ return (JNIEnvExt*) dvmGetThreadJNIEnv(self);
+}
+
+/*
+ * Create a new JNIEnv struct and add it to the VM's list.
+ *
+ * "self" will be NULL for the main thread, since the VM hasn't started
+ * yet; the value will be filled in later.
+ */
+JNIEnv* dvmCreateJNIEnv(Thread* self)
+{
+ JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
+ JNIEnvExt* newEnv;
+
+ //if (self != NULL)
+ // LOGI("Ent CreateJNIEnv: threadid=%d %p\n", self->threadId, self);
+
+ assert(vm != NULL);
+
+ newEnv = (JNIEnvExt*) calloc(1, sizeof(JNIEnvExt));
+ newEnv->funcTable = &gNativeInterface;
+ newEnv->vm = vm;
+ newEnv->forceDataCopy = vm->forceDataCopy;
+ if (self != NULL) {
+ dvmSetJniEnvThreadId((JNIEnv*) newEnv, self);
+ assert(newEnv->envThreadId != 0);
+ } else {
+ /* make it obvious if we fail to initialize these later */
+ newEnv->envThreadId = 0x77777775;
+ newEnv->self = (Thread*) 0x77777779;
+ }
+ if (vm->useChecked)
+ dvmUseCheckedJniEnv(newEnv);
+
+ dvmLockMutex(&vm->envListLock);
+
+ /* insert at head of list */
+ newEnv->next = vm->envList;
+ assert(newEnv->prev == NULL);
+ if (vm->envList == NULL) // rare, but possible
+ vm->envList = newEnv;
+ else
+ vm->envList->prev = newEnv;
+ vm->envList = newEnv;
+
+ dvmUnlockMutex(&vm->envListLock);
+
+ //if (self != NULL)
+ // LOGI("Xit CreateJNIEnv: threadid=%d %p\n", self->threadId, self);
+ return (JNIEnv*) newEnv;
+}
+
+/*
+ * Remove a JNIEnv struct from the list and free it.
+ */
+void dvmDestroyJNIEnv(JNIEnv* env)
+{
+ JNIEnvExt* extEnv = (JNIEnvExt*) env;
+ JavaVMExt* vm = extEnv->vm;
+ Thread* self;
+
+ if (env == NULL)
+ return;
+
+ self = dvmThreadSelf();
+ assert(self != NULL);
+
+ //LOGI("Ent DestroyJNIEnv: threadid=%d %p\n", self->threadId, self);
+
+ dvmLockMutex(&vm->envListLock);
+
+ if (extEnv == vm->envList) {
+ assert(extEnv->prev == NULL);
+ vm->envList = extEnv->next;
+ } else {
+ assert(extEnv->prev != NULL);
+ extEnv->prev->next = extEnv->next;
+ }
+ if (extEnv->next != NULL)
+ extEnv->next->prev = extEnv->prev;
+
+ dvmUnlockMutex(&extEnv->vm->envListLock);
+
+ free(env);
+ //LOGI("Xit DestroyJNIEnv: threadid=%d %p\n", self->threadId, self);
+}
+
+
+/*
+ * Retrieve the ReferenceTable struct for the current thread.
+ *
+ * Going through "env" rather than dvmThreadSelf() is faster but will
+ * get weird if the JNI code is passing the wrong JNIEnv around.
+ */
+#ifdef USE_INDIRECT_REF
+static inline IndirectRefTable* getLocalRefTable(JNIEnv* env)
+#else
+static inline ReferenceTable* getLocalRefTable(JNIEnv* env)
+#endif
+{
+ //return &dvmThreadSelf()->jniLocalRefTable;
+ return &((JNIEnvExt*)env)->self->jniLocalRefTable;
+}
+
+#ifdef USE_INDIRECT_REF
+/*
+ * Convert an indirect reference to an Object reference. The indirect
+ * reference may be local, global, or weak-global.
+ *
+ * If "jobj" is NULL or an invalid indirect reference, this returns NULL.
+ */
+Object* dvmDecodeIndirectRef(JNIEnv* env, jobject jobj)
+{
+ if (jobj == NULL)
+ return NULL;
+
+ Object* result;
+
+ switch (dvmGetIndirectRefType(jobj)) {
+ case kIndirectKindLocal:
+ {
+ IndirectRefTable* pRefTable = getLocalRefTable(env);
+ result = dvmGetFromIndirectRefTable(pRefTable, jobj);
+ }
+ break;
+ case kIndirectKindGlobal:
+ {
+ // TODO: find a way to avoid the mutex activity here
+ IndirectRefTable* pRefTable = &gDvm.jniGlobalRefTable;
+ dvmLockMutex(&gDvm.jniGlobalRefLock);
+ result = dvmGetFromIndirectRefTable(pRefTable, jobj);
+ dvmUnlockMutex(&gDvm.jniGlobalRefLock);
+ }
+ break;
+ case kIndirectKindWeakGlobal:
+ {
+ // TODO: implement
+ LOGE("weak-global not yet supported\n");
+ result = NULL;
+ dvmAbort();
+ }
+ break;
+ case kIndirectKindInvalid:
+ default:
+ LOGW("Invalid indirect reference %p in decodeIndirectRef\n", jobj);
+ dvmAbort();
+ result = NULL;
+ break;
+ }
+
+ return result;
+}
+#else
+ /* use trivial inline in JniInternal.h for performance */
+#endif
+
+/*
+ * Add a local reference for an object to the current stack frame. When
+ * the native function returns, the reference will be discarded.
+ *
+ * We need to allow the same reference to be added multiple times.
+ *
+ * This will be called on otherwise unreferenced objects. We cannot do
+ * GC allocations here, and it's best if we don't grab a mutex.
+ *
+ * Returns the local reference (currently just the same pointer that was
+ * passed in), or NULL on failure.
+ */
+static jobject addLocalReference(JNIEnv* env, Object* obj)
+{
+ if (obj == NULL)
+ return NULL;
+
+ jobject jobj;
+
+#ifdef USE_INDIRECT_REF
+ IndirectRefTable* pRefTable = getLocalRefTable(env);
+ void* curFrame = ((JNIEnvExt*)env)->self->curFrame;
+ u4 cookie = SAVEAREA_FROM_FP(curFrame)->xtra.localRefCookie;
+
+ jobj = (jobject) dvmAddToIndirectRefTable(pRefTable, cookie, obj);
+ if (jobj == NULL) {
+ dvmDumpIndirectRefTable(pRefTable, "JNI local");
+ LOGE("Failed adding to JNI local ref table (has %d entries)\n",
+ (int) dvmIndirectRefTableEntries(pRefTable));
+ dvmDumpThread(dvmThreadSelf(), false);
+ dvmAbort(); // spec says call FatalError; this is equivalent
+ } else {
+ LOGVV("LREF add %p (%s.%s) (ent=%d)\n", obj,
+ dvmGetCurrentJNIMethod()->clazz->descriptor,
+ dvmGetCurrentJNIMethod()->name,
+ (int) dvmReferenceTableEntries(pRefTable));
+ }
+#else
+ ReferenceTable* pRefTable = getLocalRefTable(env);
+
+ if (!dvmAddToReferenceTable(pRefTable, obj)) {
+ dvmDumpReferenceTable(pRefTable, "JNI local");
+ LOGE("Failed adding to JNI local ref table (has %d entries)\n",
+ (int) dvmReferenceTableEntries(pRefTable));
+ dvmDumpThread(dvmThreadSelf(), false);
+ dvmAbort(); // spec says call FatalError; this is equivalent
+ } else {
+ LOGVV("LREF add %p (%s.%s) (ent=%d)\n", obj,
+ dvmGetCurrentJNIMethod()->clazz->descriptor,
+ dvmGetCurrentJNIMethod()->name,
+ (int) dvmReferenceTableEntries(pRefTable));
+ }
+
+ jobj = (jobject) obj;
+#endif
+
+ return jobj;
+}
+
+/*
+ * Ensure that at least "capacity" references can be held in the local
+ * refs table of the current thread.
+ */
+static bool ensureLocalCapacity(JNIEnv* env, int capacity)
+{
+#ifdef USE_INDIRECT_REF
+ IndirectRefTable* pRefTable = getLocalRefTable(env);
+ int numEntries = dvmIndirectRefTableEntries(pRefTable);
+ // TODO: this isn't quite right, since "numEntries" includes holes
+ return ((kJniLocalRefMax - numEntries) >= capacity);
+#else
+ ReferenceTable* pRefTable = getLocalRefTable(env);
+
+ return (kJniLocalRefMax - (pRefTable->nextEntry - pRefTable->table) >= capacity);
+#endif
+}
+
+/*
+ * Explicitly delete a reference from the local list.
+ */
+static void deleteLocalReference(JNIEnv* env, jobject jobj)
+{
+ if (jobj == NULL)
+ return;
+
+#ifdef USE_INDIRECT_REF
+ IndirectRefTable* pRefTable = getLocalRefTable(env);
+ Thread* self = ((JNIEnvExt*)env)->self;
+ u4 cookie = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefCookie;
+
+ if (!dvmRemoveFromIndirectRefTable(pRefTable, cookie, jobj)) {
+ /*
+ * Attempting to delete a local reference that is not in the
+ * topmost local reference frame is a no-op. DeleteLocalRef returns
+ * void and doesn't throw any exceptions, but we should probably
+ * complain about it so the user will notice that things aren't
+ * going quite the way they expect.
+ */
+ LOGW("JNI WARNING: DeleteLocalRef(%p) failed to find entry\n", jobj);
+ }
+#else
+ ReferenceTable* pRefTable = getLocalRefTable(env);
+ Thread* self = ((JNIEnvExt*)env)->self;
+ Object** bottom = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefCookie;
+
+ if (!dvmRemoveFromReferenceTable(pRefTable, bottom, (Object*) jobj)) {
+ /*
+ * Attempting to delete a local reference that is not in the
+ * topmost local reference frame is a no-op. DeleteLocalRef returns
+ * void and doesn't throw any exceptions, but we should probably
+ * complain about it so the user will notice that things aren't
+ * going quite the way they expect.
+ */
+ LOGW("JNI WARNING: DeleteLocalRef(%p) failed to find entry (valid=%d)\n",
+ jobj, dvmIsValidObject((Object*) jobj));
+ }
+#endif
+}
+
+/*
+ * Add a global reference for an object.
+ *
+ * We may add the same object more than once. Add/remove calls are paired,
+ * so it needs to appear on the list multiple times.
+ */
+static jobject addGlobalReference(Object* obj)
+{
+ if (obj == NULL)
+ return NULL;
+
+ //LOGI("adding obj=%p\n", obj);
+ //dvmDumpThread(dvmThreadSelf(), false);
+
+ if (false && ((Object*)obj)->clazz == gDvm.classJavaLangClass) {
+ ClassObject* clazz = (ClassObject*) obj;
+ LOGI("-------\n");
+ LOGI("Adding global ref on class %s\n", clazz->descriptor);
+ dvmDumpThread(dvmThreadSelf(), false);
+ }
+ if (false && ((Object*)obj)->clazz == gDvm.classJavaLangString) {
+ StringObject* strObj = (StringObject*) obj;
+ char* str = dvmCreateCstrFromString(strObj);
+ if (strcmp(str, "sync-response") == 0) {
+ LOGI("-------\n");
+ LOGI("Adding global ref on string '%s'\n", str);
+ dvmDumpThread(dvmThreadSelf(), false);
+ //dvmAbort();
+ }
+ free(str);
+ }
+ if (false && ((Object*)obj)->clazz == gDvm.classArrayByte) {
+ ArrayObject* arrayObj = (ArrayObject*) obj;
+ if (arrayObj->length == 8192 /*&&
+ dvmReferenceTableEntries(&gDvm.jniGlobalRefTable) > 400*/)
+ {
+ LOGI("Adding global ref on byte array %p (len=%d)\n",
+ arrayObj, arrayObj->length);
+ dvmDumpThread(dvmThreadSelf(), false);
+ }
+ }
+
+ jobject jobj;
+
+ dvmLockMutex(&gDvm.jniGlobalRefLock);
+
+ /*
+ * Throwing an exception on failure is problematic, because JNI code
+ * may not be expecting an exception, and things sort of cascade. We
+ * want to have a hard limit to catch leaks during debugging, but this
+ * otherwise needs to expand until memory is consumed. As a practical
+ * matter, if we have many thousands of global references, chances are
+ * we're either leaking global ref table entries or we're going to
+ * run out of space in the GC heap.
+ */
+#ifdef USE_INDIRECT_REF
+ jobj = dvmAddToIndirectRefTable(&gDvm.jniGlobalRefTable, IRT_FIRST_SEGMENT,
+ obj);
+ if (jobj == NULL) {
+ dvmDumpIndirectRefTable(&gDvm.jniGlobalRefTable, "JNI global");
+ LOGE("Failed adding to JNI global ref table (%d entries)\n",
+ (int) dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable));
+ dvmAbort();
+ }
+
+ LOGVV("GREF add %p (%s.%s)\n", obj,
+ dvmGetCurrentJNIMethod()->clazz->descriptor,
+ dvmGetCurrentJNIMethod()->name);
+
+ /* GREF usage tracking; should probably be disabled for production env */
+ if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
+ int count = dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable);
+ // TODO: adjust for "holes"
+ if (count > gDvm.jniGlobalRefHiMark) {
+ LOGD("GREF has increased to %d\n", count);
+ gDvm.jniGlobalRefHiMark += kGrefWaterInterval;
+ gDvm.jniGlobalRefLoMark += kGrefWaterInterval;
+
+ /* watch for "excessive" use; not generally appropriate */
+ if (count >= gDvm.jniGrefLimit) {
+ JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
+ if (vm->warnError) {
+ dvmDumpIndirectRefTable(&gDvm.jniGlobalRefTable,
+ "JNI global");
+ LOGE("Excessive JNI global references (%d)\n", count);
+ dvmAbort();
+ } else {
+ LOGW("Excessive JNI global references (%d)\n", count);
+ }
+ }
+ }
+ }
+#else
+ if (!dvmAddToReferenceTable(&gDvm.jniGlobalRefTable, obj)) {
+ dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global");
+ LOGE("Failed adding to JNI global ref table (%d entries)\n",
+ (int) dvmReferenceTableEntries(&gDvm.jniGlobalRefTable));
+ dvmAbort();
+ }
+ jobj = (jobject) obj;
+
+ LOGVV("GREF add %p (%s.%s)\n", obj,
+ dvmGetCurrentJNIMethod()->clazz->descriptor,
+ dvmGetCurrentJNIMethod()->name);
+
+ /* GREF usage tracking; should probably be disabled for production env */
+ if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
+ int count = dvmReferenceTableEntries(&gDvm.jniGlobalRefTable);
+ if (count > gDvm.jniGlobalRefHiMark) {
+ LOGD("GREF has increased to %d\n", count);
+ gDvm.jniGlobalRefHiMark += kGrefWaterInterval;
+ gDvm.jniGlobalRefLoMark += kGrefWaterInterval;
+
+ /* watch for "excessive" use; not generally appropriate */
+ if (count >= gDvm.jniGrefLimit) {
+ JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
+ if (vm->warnError) {
+ dvmDumpReferenceTable(&gDvm.jniGlobalRefTable,"JNI global");
+ LOGE("Excessive JNI global references (%d)\n", count);
+ dvmAbort();
+ } else {
+ LOGW("Excessive JNI global references (%d)\n", count);
+ }
+ }
+ }
+ }
+#endif
+
+ dvmUnlockMutex(&gDvm.jniGlobalRefLock);
+ return jobj;
+}
+
+/*
+ * Remove a global reference. In most cases it's the entry most recently
+ * added, which makes this pretty quick.
+ *
+ * Thought: if it's not the most recent entry, just null it out. When we
+ * fill up, do a compaction pass before we expand the list.
+ */
+static void deleteGlobalReference(jobject jobj)
+{
+ if (jobj == NULL)
+ return;
+
+ dvmLockMutex(&gDvm.jniGlobalRefLock);
+
+#ifdef USE_INDIRECT_REF
+ if (!dvmRemoveFromIndirectRefTable(&gDvm.jniGlobalRefTable,
+ IRT_FIRST_SEGMENT, jobj))
+ {
+ LOGW("JNI: DeleteGlobalRef(%p) failed to find entry\n", jobj);
+ goto bail;
+ }
+
+ if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
+ int count = dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable);
+ // TODO: not quite right, need to subtract holes
+ if (count < gDvm.jniGlobalRefLoMark) {
+ LOGD("GREF has decreased to %d\n", count);
+ gDvm.jniGlobalRefHiMark -= kGrefWaterInterval;
+ gDvm.jniGlobalRefLoMark -= kGrefWaterInterval;
+ }
+ }
+#else
+ if (!dvmRemoveFromReferenceTable(&gDvm.jniGlobalRefTable,
+ gDvm.jniGlobalRefTable.table, jobj))
+ {
+ LOGW("JNI: DeleteGlobalRef(%p) failed to find entry (valid=%d)\n",
+ jobj, dvmIsValidObject((Object*) jobj));
+ goto bail;
+ }
+
+ if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
+ int count = dvmReferenceTableEntries(&gDvm.jniGlobalRefTable);
+ if (count < gDvm.jniGlobalRefLoMark) {
+ LOGD("GREF has decreased to %d\n", count);
+ gDvm.jniGlobalRefHiMark -= kGrefWaterInterval;
+ gDvm.jniGlobalRefLoMark -= kGrefWaterInterval;
+ }
+ }
+#endif
+
+bail:
+ dvmUnlockMutex(&gDvm.jniGlobalRefLock);
+}
+
+/*
+ * We create a PhantomReference that references the object, add a
+ * global reference to it, and then flip some bits before returning it.
+ * The last step ensures that we detect it as special and that only
+ * appropriate calls will accept it.
+ *
+ * On failure, returns NULL with an exception pending.
+ */
+static jweak createWeakGlobalRef(JNIEnv* env, jobject jobj)
+{
+ if (jobj == NULL)
+ return NULL;
+
+ Thread* self = ((JNIEnvExt*)env)->self;
+ Object* obj = dvmDecodeIndirectRef(env, jobj);
+ Object* phantomObj;
+ jobject phantomRef;
+
+ /*
+ * Allocate a PhantomReference, then call the constructor to set
+ * the referent and the reference queue.
+ *
+ * We use a "magic" reference queue that the GC knows about; it behaves
+ * more like a queueless WeakReference, clearing the referent and
+ * not calling enqueue().
+ */
+ if (!dvmIsClassInitialized(gDvm.classJavaLangRefPhantomReference))
+ dvmInitClass(gDvm.classJavaLangRefPhantomReference);
+ phantomObj = dvmAllocObject(gDvm.classJavaLangRefPhantomReference,
+ ALLOC_DEFAULT);
+ if (phantomObj == NULL) {
+ assert(dvmCheckException(self));
+ LOGW("Failed on WeakGlobalRef alloc\n");
+ return NULL;
+ }
+
+ JValue unused;
+ dvmCallMethod(self, gDvm.methJavaLangRefPhantomReference_init, phantomObj,
+ &unused, obj, NULL);
+ dvmReleaseTrackedAlloc(phantomObj, self);
+
+ if (dvmCheckException(self)) {
+ LOGW("PhantomReference init failed\n");
+ return NULL;
+ }
+
+ LOGV("+++ WGR: created phantom ref %p for object %p\n", phantomObj, obj);
+
+ /*
+ * Add it to the global reference table, and mangle the pointer.
+ */
+ phantomRef = addGlobalReference(phantomObj);
+ return dvmObfuscateWeakGlobalRef(phantomRef);
+}
+
+/*
+ * Delete the global reference that's keeping the PhantomReference around.
+ * The PhantomReference will eventually be discarded by the GC.
+ */
+static void deleteWeakGlobalRef(JNIEnv* env, jweak wref)
+{
+ if (wref == NULL)
+ return;
+
+ jobject phantomRef = dvmNormalizeWeakGlobalRef(wref);
+ deleteGlobalReference(phantomRef);
+}
+
+/*
+ * Extract the referent from a PhantomReference. Used for weak global
+ * references.
+ *
+ * "jwobj" is a "mangled" WGR pointer.
+ */
+static Object* getPhantomReferent(JNIEnv* env, jweak jwobj)
+{
+ jobject jobj = dvmNormalizeWeakGlobalRef(jwobj);
+ Object* obj = dvmDecodeIndirectRef(env, jobj);
+
+ if (obj->clazz != gDvm.classJavaLangRefPhantomReference) {
+ LOGE("%p is not a phantom reference (%s)\n",
+ jwobj, obj->clazz->descriptor);
+ return NULL;
+ }
+
+ return dvmGetFieldObject(obj, gDvm.offJavaLangRefReference_referent);
+}
+
+
+/*
+ * Objects don't currently move, so we just need to create a reference
+ * that will ensure the array object isn't collected.
+ *
+ * We use a separate reference table, which is part of the GC root set.
+ */
+static void pinPrimitiveArray(ArrayObject* arrayObj)
+{
+ if (arrayObj == NULL)
+ return;
+
+ dvmLockMutex(&gDvm.jniPinRefLock);
+ if (!dvmAddToReferenceTable(&gDvm.jniPinRefTable, (Object*)arrayObj)) {
+ dvmDumpReferenceTable(&gDvm.jniPinRefTable, "JNI pinned array");
+ LOGE("Failed adding to JNI pinned array ref table (%d entries)\n",
+ (int) dvmReferenceTableEntries(&gDvm.jniPinRefTable));
+ dvmDumpThread(dvmThreadSelf(), false);
+ dvmAbort();
+ }
+
+ /*
+ * If we're watching global ref usage, also keep an eye on these.
+ *
+ * The total number of pinned primitive arrays should be pretty small.
+ * A single array should not be pinned more than once or twice; any
+ * more than that is a strong indicator that a Release function is
+ * not being called.
+ */
+ if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
+ int count = 0;
+ Object** ppObj = gDvm.jniPinRefTable.table;
+ while (ppObj < gDvm.jniPinRefTable.nextEntry) {
+ if (*ppObj++ == (Object*) arrayObj)
+ count++;
+ }
+
+ if (count > kPinComplainThreshold) {
+ LOGW("JNI: pin count on array %p (%s) is now %d\n",
+ arrayObj, arrayObj->obj.clazz->descriptor, count);
+ /* keep going */
+ }
+ }
+
+ dvmUnlockMutex(&gDvm.jniPinRefLock);
+}
+
+/*
+ * Un-pin the array object. If an object was pinned twice, it must be
+ * unpinned twice before it's free to move.
+ */
+static void unpinPrimitiveArray(ArrayObject* arrayObj)
+{
+ if (arrayObj == NULL)
+ return;
+
+ dvmLockMutex(&gDvm.jniPinRefLock);
+ if (!dvmRemoveFromReferenceTable(&gDvm.jniPinRefTable,
+ gDvm.jniPinRefTable.table, (Object*) arrayObj))
+ {
+ LOGW("JNI: unpinPrimitiveArray(%p) failed to find entry (valid=%d)\n",
+ arrayObj, dvmIsValidObject((Object*) arrayObj));
+ goto bail;
+ }
+
+bail:
+ dvmUnlockMutex(&gDvm.jniPinRefLock);
+}
+
+/*
+ * Dump the contents of the JNI reference tables to the log file.
+ *
+ * We only dump the local refs associated with the current thread.
+ */
+void dvmDumpJniReferenceTables(void)
+{
+ Thread* self = dvmThreadSelf();
+ JNIEnv* env = self->jniEnv;
+ ReferenceTable* pLocalRefs = getLocalRefTable(env);
+
+#ifdef USE_INDIRECT_REF
+ dvmDumpIndirectRefTable(pLocalRefs, "JNI local");
+ dvmDumpIndirectRefTable(&gDvm.jniGlobalRefTable, "JNI global");
+#else
+ dvmDumpReferenceTable(pLocalRefs, "JNI local");
+ dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global");
+#endif
+ dvmDumpReferenceTable(&gDvm.jniPinRefTable, "JNI pinned array");
+}
+
+/*
+ * GC helper function to mark all JNI global references.
+ *
+ * We're currently handling the "pin" table here too.
+ */
+void dvmGcMarkJniGlobalRefs()
+{
+ Object** op;
+
+ dvmLockMutex(&gDvm.jniGlobalRefLock);
+
+#ifdef USE_INDIRECT_REF
+ IndirectRefTable* pRefTable = &gDvm.jniGlobalRefTable;
+ op = pRefTable->table;
+ int numEntries = dvmIndirectRefTableEntries(pRefTable);
+ int i;
+
+ for (i = 0; i < numEntries; i++) {
+ Object* obj = *op;
+ if (obj != NULL)
+ dvmMarkObjectNonNull(obj);
+ op++;
+ }
+#else
+ op = gDvm.jniGlobalRefTable.table;
+ while ((uintptr_t)op < (uintptr_t)gDvm.jniGlobalRefTable.nextEntry) {
+ dvmMarkObjectNonNull(*(op++));
+ }
+#endif
+
+ dvmUnlockMutex(&gDvm.jniGlobalRefLock);
+
+
+ dvmLockMutex(&gDvm.jniPinRefLock);
+
+ op = gDvm.jniPinRefTable.table;
+ while ((uintptr_t)op < (uintptr_t)gDvm.jniPinRefTable.nextEntry) {
+ dvmMarkObjectNonNull(*(op++));
+ }
+
+ dvmUnlockMutex(&gDvm.jniPinRefLock);
+}
+
+
+#ifndef USE_INDIRECT_REF
+/*
+ * Determine if "obj" appears in the argument list for the native method.
+ *
+ * We use the "shorty" signature to determine which argument slots hold
+ * reference types.
+ */
+static bool findInArgList(Thread* self, Object* obj)
+{
+ const Method* meth;
+ u4* fp;
+ int i;
+
+ fp = self->curFrame;
+ while (1) {
+ /*
+ * Back up over JNI PushLocalFrame frames. This works because the
+ * previous frame on the interpreted stack is either a break frame
+ * (if we called here via native code) or an interpreted method (if
+ * we called here via the interpreter). In both cases the method
+ * pointer won't match.
+ */
+ StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+ meth = saveArea->method;
+ if (meth != SAVEAREA_FROM_FP(saveArea->prevFrame)->method)
+ break;
+ fp = saveArea->prevFrame;
+ }
+
+ LOGVV("+++ scanning %d args in %s (%s)\n",
+ meth->insSize, meth->name, meth->shorty);
+ const char* shorty = meth->shorty +1; /* skip return type char */
+ for (i = 0; i < meth->insSize; i++) {
+ if (i == 0 && !dvmIsStaticMethod(meth)) {
+ /* first arg is "this" ref, not represented in "shorty" */
+ if (fp[i] == (u4) obj)
+ return true;
+ } else {
+ /* if this is a reference type, see if it matches */
+ switch (*shorty) {
+ case 'L':
+ if (fp[i] == (u4) obj)
+ return true;
+ break;
+ case 'D':
+ case 'J':
+ i++;
+ break;
+ case '\0':
+ LOGE("Whoops! ran off the end of %s (%d)\n",
+ meth->shorty, meth->insSize);
+ break;
+ default:
+ if (fp[i] == (u4) obj)
+ LOGI("NOTE: ref %p match on arg type %c\n", obj, *shorty);
+ break;
+ }
+ shorty++;
+ }
+ }
+
+ /*
+ * For static methods, we also pass a class pointer in.
+ */
+ if (dvmIsStaticMethod(meth)) {
+ //LOGI("+++ checking class pointer in %s\n", meth->name);
+ if ((void*)obj == (void*)meth->clazz)
+ return true;
+ }
+ return false;
+}
+#endif
+
+/*
+ * Verify that a reference passed in from native code is one that the
+ * code is allowed to have.
+ *
+ * It's okay for native code to pass us a reference that:
+ * - was passed in as an argument when invoked by native code (and hence
+ * is in the JNI local refs table)
+ * - was returned to it from JNI (and is now in the local refs table)
+ * - is present in the JNI global refs table
+ *
+ * Used by -Xcheck:jni and GetObjectRefType.
+ *
+ * NOTE: in the current VM, global and local references are identical. If
+ * something is both global and local, we can't tell them apart, and always
+ * return "local".
+ */
+jobjectRefType dvmGetJNIRefType(JNIEnv* env, jobject jobj)
+{
+#ifdef USE_INDIRECT_REF
+ /*
+ * IndirectRefKind is currently defined as an exact match of
+ * jobjectRefType, so this is easy. We have to decode it to determine
+ * if it's a valid reference and not merely valid-looking.
+ */
+ Object* obj = dvmDecodeIndirectRef(env, jobj);
+
+ if (obj == NULL) {
+ /* invalid ref, or jobj was NULL */
+ return JNIInvalidRefType;
+ } else {
+ return (jobjectRefType) dvmGetIndirectRefType(jobj);
+ }
+#else
+ ReferenceTable* pRefTable = getLocalRefTable(env);
+ Thread* self = dvmThreadSelf();
+
+ if (dvmIsWeakGlobalRef(jobj)) {
+ return JNIWeakGlobalRefType;
+ }
+
+ /* check args */
+ if (findInArgList(self, jobj)) {
+ //LOGI("--- REF found %p on stack\n", jobj);
+ return JNILocalRefType;
+ }
+
+ /* check locals */
+ if (dvmFindInReferenceTable(pRefTable, pRefTable->table, jobj) != NULL) {
+ //LOGI("--- REF found %p in locals\n", jobj);
+ return JNILocalRefType;
+ }
+
+ /* check globals */
+ dvmLockMutex(&gDvm.jniGlobalRefLock);
+ if (dvmFindInReferenceTable(&gDvm.jniGlobalRefTable,
+ gDvm.jniGlobalRefTable.table, jobj))
+ {
+ //LOGI("--- REF found %p in globals\n", jobj);
+ dvmUnlockMutex(&gDvm.jniGlobalRefLock);
+ return JNIGlobalRefType;
+ }
+ dvmUnlockMutex(&gDvm.jniGlobalRefLock);
+
+ /* not found! */
+ return JNIInvalidRefType;
+#endif
+}
+
+/*
+ * Register a method that uses JNI calling conventions.
+ */
+static bool dvmRegisterJNIMethod(ClassObject* clazz, const char* methodName,
+ const char* signature, void* fnPtr)
+{
+ Method* method;
+ bool result = false;
+
+ if (fnPtr == NULL)
+ goto bail;
+
+ method = dvmFindDirectMethodByDescriptor(clazz, methodName, signature);
+ if (method == NULL)
+ method = dvmFindVirtualMethodByDescriptor(clazz, methodName, signature);
+ if (method == NULL) {
+ LOGW("ERROR: Unable to find decl for native %s.%s:%s\n",
+ clazz->descriptor, methodName, signature);
+ goto bail;
+ }
+
+ if (!dvmIsNativeMethod(method)) {
+ LOGW("Unable to register: not native: %s.%s:%s\n",
+ clazz->descriptor, methodName, signature);
+ goto bail;
+ }
+
+ if (method->nativeFunc != dvmResolveNativeMethod) {
+ /* this is allowed, but unusual */
+ LOGV("Note: %s.%s:%s was already registered\n",
+ clazz->descriptor, methodName, signature);
+ }
+
+ dvmUseJNIBridge(method, fnPtr);
+
+ LOGV("JNI-registered %s.%s:%s\n", clazz->descriptor, methodName,
+ signature);
+ result = true;
+
+bail:
+ return result;
+}
+
+/*
+ * Returns "true" if CheckJNI is enabled in the VM.
+ */
+static bool dvmIsCheckJNIEnabled(void)
+{
+ JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
+ return vm->useChecked;
+}
+
+/*
+ * Returns the appropriate JNI bridge for 'method', also taking into account
+ * the -Xcheck:jni setting.
+ */
+static DalvikBridgeFunc dvmSelectJNIBridge(const Method* method)
+{
+ enum {
+ kJNIGeneral = 0,
+ kJNISync = 1,
+ kJNIVirtualNoRef = 2,
+ kJNIStaticNoRef = 3,
+ } kind;
+ static const DalvikBridgeFunc stdFunc[] = {
+ dvmCallJNIMethod_general,
+ dvmCallJNIMethod_synchronized,
+ dvmCallJNIMethod_virtualNoRef,
+ dvmCallJNIMethod_staticNoRef
+ };
+ static const DalvikBridgeFunc checkFunc[] = {
+ dvmCheckCallJNIMethod_general,
+ dvmCheckCallJNIMethod_synchronized,
+ dvmCheckCallJNIMethod_virtualNoRef,
+ dvmCheckCallJNIMethod_staticNoRef
+ };
+
+ bool hasRefArg = false;
+
+ if (dvmIsSynchronizedMethod(method)) {
+ /* use version with synchronization; calls into general handler */
+ kind = kJNISync;
+ } else {
+ /*
+ * Do a quick scan through the "shorty" signature to see if the method
+ * takes any reference arguments.
+ */
+ const char* cp = method->shorty;
+ while (*++cp != '\0') { /* pre-incr to skip return type */
+ if (*cp == 'L') {
+ /* 'L' used for both object and array references */
+ hasRefArg = true;
+ break;
+ }
+ }
+
+ if (hasRefArg) {
+ /* use general handler to slurp up reference args */
+ kind = kJNIGeneral;
+ } else {
+ /* virtual methods have a ref in args[0] (not in signature) */
+ if (dvmIsStaticMethod(method))
+ kind = kJNIStaticNoRef;
+ else
+ kind = kJNIVirtualNoRef;
+ }
+ }
+
+ return dvmIsCheckJNIEnabled() ? checkFunc[kind] : stdFunc[kind];
+}
+
+/*
+ * Trace a call into native code.
+ */
+static void dvmTraceCallJNIMethod(const u4* args, JValue* pResult,
+ const Method* method, Thread* self)
+{
+ dvmLogNativeMethodEntry(method, args);
+ DalvikBridgeFunc bridge = dvmSelectJNIBridge(method);
+ (*bridge)(args, pResult, method, self);
+ dvmLogNativeMethodExit(method, self, *pResult);
+}
+
+/**
+ * Returns true if the -Xjnitrace setting implies we should trace 'method'.
+ */
+static bool shouldTrace(Method* method)
+{
+ return gDvm.jniTrace && strstr(method->clazz->descriptor, gDvm.jniTrace);
+}
+
+/*
+ * Point "method->nativeFunc" at the JNI bridge, and overload "method->insns"
+ * to point at the actual function.
+ */
+void dvmUseJNIBridge(Method* method, void* func)
+{
+ DalvikBridgeFunc bridge = shouldTrace(method)
+ ? dvmTraceCallJNIMethod
+ : dvmSelectJNIBridge(method);
+ dvmSetNativeFunc(method, bridge, func);
+}
+
+/*
+ * Get the method currently being executed by examining the interp stack.
+ */
+const Method* dvmGetCurrentJNIMethod(void)
+{
+ assert(dvmThreadSelf() != NULL);
+
+ void* fp = dvmThreadSelf()->curFrame;
+ const Method* meth = SAVEAREA_FROM_FP(fp)->method;
+
+ assert(meth != NULL);
+ assert(dvmIsNativeMethod(meth));
+ return meth;
+}
+
+
+/*
+ * Track a JNI MonitorEnter in the current thread.
+ *
+ * The goal is to be able to "implicitly" release all JNI-held monitors
+ * when the thread detaches.
+ *
+ * Monitors may be entered multiple times, so we add a new entry for each
+ * enter call. It would be more efficient to keep a counter. At present
+ * there's no real motivation to improve this however.
+ */
+static void trackMonitorEnter(Thread* self, Object* obj)
+{
+ static const int kInitialSize = 16;
+ ReferenceTable* refTable = &self->jniMonitorRefTable;
+
+ /* init table on first use */
+ if (refTable->table == NULL) {
+ assert(refTable->maxEntries == 0);
+
+ if (!dvmInitReferenceTable(refTable, kInitialSize, INT_MAX)) {
+ LOGE("Unable to initialize monitor tracking table\n");
+ dvmAbort();
+ }
+ }
+
+ if (!dvmAddToReferenceTable(refTable, obj)) {
+ /* ran out of memory? could throw exception instead */
+ LOGE("Unable to add entry to monitor tracking table\n");
+ dvmAbort();
+ } else {
+ LOGVV("--- added monitor %p\n", obj);
+ }
+}
+
+
+/*
+ * Track a JNI MonitorExit in the current thread.
+ */
+static void trackMonitorExit(Thread* self, Object* obj)
+{
+ ReferenceTable* pRefTable = &self->jniMonitorRefTable;
+
+ if (!dvmRemoveFromReferenceTable(pRefTable, pRefTable->table, obj)) {
+ LOGE("JNI monitor %p not found in tracking list\n", obj);
+ /* keep going? */
+ } else {
+ LOGVV("--- removed monitor %p\n", obj);
+ }
+}
+
+/*
+ * Release all monitors held by the jniMonitorRefTable list.
+ */
+void dvmReleaseJniMonitors(Thread* self)
+{
+ ReferenceTable* pRefTable = &self->jniMonitorRefTable;
+ Object** top = pRefTable->table;
+
+ if (top == NULL)
+ return;
+
+ Object** ptr = pRefTable->nextEntry;
+ while (--ptr >= top) {
+ if (!dvmUnlockObject(self, *ptr)) {
+ LOGW("Unable to unlock monitor %p at thread detach\n", *ptr);
+ } else {
+ LOGVV("--- detach-releasing monitor %p\n", *ptr);
+ }
+ }
+
+ /* zap it */
+ pRefTable->nextEntry = pRefTable->table;
+}
+
+/*
+ * Determine if the specified class can be instantiated from JNI. This
+ * is used by AllocObject / NewObject, which are documented as throwing
+ * an exception for abstract and interface classes, and not accepting
+ * array classes. We also want to reject attempts to create new Class
+ * objects, since only DefineClass should do that.
+ */
+static bool canAllocClass(ClassObject* clazz)
+{
+ if (dvmIsAbstractClass(clazz) || dvmIsInterfaceClass(clazz)) {
+ /* JNI spec defines what this throws */
+ dvmThrowExceptionFmt("Ljava/lang/InstantiationException;",
+ "Can't instantiate %s (abstract or interface)", clazz->descriptor);
+ return false;
+ } else if (dvmIsArrayClass(clazz) || clazz == gDvm.classJavaLangClass) {
+ /* spec says "must not" for arrays, ignores Class */
+ dvmThrowExceptionFmt("Ljava/lang/IllegalArgumentException;",
+ "Can't instantiate %s (array or Class) with this JNI function",
+ clazz->descriptor);
+ return false;
+ }
+
+ return true;
+}
+
+#ifdef WITH_JNI_STACK_CHECK
+/*
+ * Compute a CRC on the entire interpreted stack.
+ *
+ * Would be nice to compute it on "self" as well, but there are parts of
+ * the Thread that can be altered by other threads (e.g. prev/next pointers).
+ */
+static void computeStackSum(Thread* self)
+{
+ const u1* low = (const u1*)SAVEAREA_FROM_FP(self->curFrame);
+ u4 crc = dvmInitCrc32();
+ self->stackCrc = 0;
+ crc = dvmComputeCrc32(crc, low, self->interpStackStart - low);
+ self->stackCrc = crc;
+}
+
+/*
+ * Compute a CRC on the entire interpreted stack, and compare it to what
+ * we previously computed.
+ *
+ * We can execute JNI directly from native code without calling in from
+ * interpreted code during VM initialization and immediately after JNI
+ * thread attachment. Another opportunity exists during JNI_OnLoad. Rather
+ * than catching these cases we just ignore them here, which is marginally
+ * less accurate but reduces the amount of code we have to touch with #ifdefs.
+ */
+static void checkStackSum(Thread* self)
+{
+ const u1* low = (const u1*)SAVEAREA_FROM_FP(self->curFrame);
+ u4 stackCrc, crc;
+
+ stackCrc = self->stackCrc;
+ self->stackCrc = 0;
+ crc = dvmInitCrc32();
+ crc = dvmComputeCrc32(crc, low, self->interpStackStart - low);
+ if (crc != stackCrc) {
+ const Method* meth = dvmGetCurrentJNIMethod();
+ if (dvmComputeExactFrameDepth(self->curFrame) == 1) {
+ LOGD("JNI: bad stack CRC (0x%08x) -- okay during init\n",
+ stackCrc);
+ } else if (strcmp(meth->name, "nativeLoad") == 0 &&
+ (strcmp(meth->clazz->descriptor, "Ljava/lang/Runtime;") == 0))
+ {
+ LOGD("JNI: bad stack CRC (0x%08x) -- okay during JNI_OnLoad\n",
+ stackCrc);
+ } else {
+ LOGW("JNI: bad stack CRC (%08x vs %08x)\n", crc, stackCrc);
+ dvmAbort();
+ }
+ }
+ self->stackCrc = (u4) -1; /* make logic errors more noticeable */
+}
+#endif
+
+
+/*
+ * ===========================================================================
+ * JNI call bridge
+ * ===========================================================================
+ */
+
+/*
+ * The functions here form a bridge between interpreted code and JNI native
+ * functions. The basic task is to convert an array of primitives and
+ * references into C-style function arguments. This is architecture-specific
+ * and usually requires help from assembly code.
+ *
+ * The bridge takes four arguments: the array of parameters, a place to
+ * store the function result (if any), the method to call, and a pointer
+ * to the current thread.
+ *
+ * These functions aren't called directly from elsewhere in the VM.
+ * A pointer in the Method struct points to one of these, and when a native
+ * method is invoked the interpreter jumps to it.
+ *
+ * (The "internal native" methods are invoked the same way, but instead
+ * of calling through a bridge, the target method is called directly.)
+ *
+ * The "args" array should not be modified, but we do so anyway for
+ * performance reasons. We know that it points to the "outs" area on
+ * the current method's interpreted stack. This area is ignored by the
+ * precise GC, because there is no register map for a native method (for
+ * an interpreted method the args would be listed in the argument set).
+ * We know all of the values exist elsewhere on the interpreted stack,
+ * because the method call setup copies them right before making the call,
+ * so we don't have to worry about concealing stuff from the GC.
+ *
+ * If we don't want to modify "args", we either have to create a local
+ * copy and modify it before calling dvmPlatformInvoke, or we have to do
+ * the local reference replacement within dvmPlatformInvoke. The latter
+ * has some performance advantages, though if we can inline the local
+ * reference adds we may win when there's a lot of reference args (unless
+ * we want to code up some local ref table manipulation in assembly.
+ */
+
+/*
+ * If necessary, convert the value in pResult from a local/global reference
+ * to an object pointer.
+ */
+static inline void convertReferenceResult(JNIEnv* env, JValue* pResult,
+ const Method* method, Thread* self)
+{
+#ifdef USE_INDIRECT_REF
+ if (method->shorty[0] == 'L' && !dvmCheckException(self) &&
+ pResult->l != NULL)
+ {
+ pResult->l = dvmDecodeIndirectRef(env, pResult->l);
+ }
+#endif
+}
+
+/*
+ * General form, handles all cases.
+ */
+void dvmCallJNIMethod_general(const u4* args, JValue* pResult,
+ const Method* method, Thread* self)
+{
+ int oldStatus;
+ u4* modArgs = (u4*) args;
+ jclass staticMethodClass;
+ JNIEnv* env = self->jniEnv;
+
+ //LOGI("JNI calling %p (%s.%s:%s):\n", method->insns,
+ // method->clazz->descriptor, method->name, method->shorty);
+
+#ifdef USE_INDIRECT_REF
+ /*
+ * Walk the argument list, creating local references for appropriate
+ * arguments.
+ */
+ int idx = 0;
+ if (dvmIsStaticMethod(method)) {
+ /* add the class object we pass in */
+ staticMethodClass = addLocalReference(env, (Object*) method->clazz);
+ if (staticMethodClass == NULL) {
+ assert(dvmCheckException(self));
+ return;
+ }
+ } else {
+ /* add "this" */
+ staticMethodClass = NULL;
+ jobject thisObj = addLocalReference(env, (Object*) modArgs[0]);
+ if (thisObj == NULL) {
+ assert(dvmCheckException(self));
+ return;
+ }
+ modArgs[idx] = (u4) thisObj;
+ idx = 1;
+ }
+
+ const char* shorty = &method->shorty[1]; /* skip return type */
+ while (*shorty != '\0') {
+ switch (*shorty++) {
+ case 'L':
+ //LOGI(" local %d: 0x%08x\n", idx, modArgs[idx]);
+ if (modArgs[idx] != 0) {
+ //if (!dvmIsValidObject((Object*) modArgs[idx]))
+ // dvmAbort();
+ jobject argObj = addLocalReference(env, (Object*) modArgs[idx]);
+ if (argObj == NULL) {
+ assert(dvmCheckException(self));
+ return;
+ }
+ modArgs[idx] = (u4) argObj;
+ }
+ break;
+ case 'D':
+ case 'J':
+ idx++;
+ break;
+ default:
+ /* Z B C S I -- do nothing */
+ break;
+ }
+
+ idx++;
+ }
+#else
+ staticMethodClass = dvmIsStaticMethod(method) ?
+ (jclass) method->clazz : NULL;
+#endif
+
+ oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
+
+ ANDROID_MEMBAR_FULL(); /* guarantee ordering on method->insns */
+ assert(method->insns != NULL);
+
+ COMPUTE_STACK_SUM(self);
+ dvmPlatformInvoke(env, staticMethodClass,
+ method->jniArgInfo, method->insSize, modArgs, method->shorty,
+ (void*)method->insns, pResult);
+ CHECK_STACK_SUM(self);
+
+ dvmChangeStatus(self, oldStatus);
+
+ convertReferenceResult(env, pResult, method, self);
+}
+
+/*
+ * Handler for the unusual case of a synchronized native method.
+ *
+ * Lock the object, then call through the general function.
+ */
+void dvmCallJNIMethod_synchronized(const u4* args, JValue* pResult,
+ const Method* method, Thread* self)
+{
+ Object* lockObj;
+
+ assert(dvmIsSynchronizedMethod(method));
+
+ if (dvmIsStaticMethod(method))
+ lockObj = (Object*) method->clazz;
+ else
+ lockObj = (Object*) args[0];
+
+ LOGVV("Calling %s.%s: locking %p (%s)\n",
+ method->clazz->descriptor, method->name,
+ lockObj, lockObj->clazz->descriptor);
+
+ dvmLockObject(self, lockObj);
+ dvmCallJNIMethod_general(args, pResult, method, self);
+ dvmUnlockObject(self, lockObj);
+}
+
+/*
+ * Virtual method call, no reference arguments.
+ *
+ * We need to local-ref the "this" argument, found in args[0].
+ */
+void dvmCallJNIMethod_virtualNoRef(const u4* args, JValue* pResult,
+ const Method* method, Thread* self)
+{
+ u4* modArgs = (u4*) args;
+ int oldStatus;
+
+#ifdef USE_INDIRECT_REF
+ jobject thisObj = addLocalReference(self->jniEnv, (Object*) args[0]);
+ if (thisObj == NULL) {
+ assert(dvmCheckException(self));
+ return;
+ }
+ modArgs[0] = (u4) thisObj;
+#endif
+
+ oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
+
+ ANDROID_MEMBAR_FULL(); /* guarantee ordering on method->insns */
+
+ COMPUTE_STACK_SUM(self);
+ dvmPlatformInvoke(self->jniEnv, NULL,
+ method->jniArgInfo, method->insSize, modArgs, method->shorty,
+ (void*)method->insns, pResult);
+ CHECK_STACK_SUM(self);
+
+ dvmChangeStatus(self, oldStatus);
+
+ convertReferenceResult(self->jniEnv, pResult, method, self);
+}
+
+/*
+ * Static method call, no reference arguments.
+ *
+ * We need to local-ref the class reference.
+ */
+void dvmCallJNIMethod_staticNoRef(const u4* args, JValue* pResult,
+ const Method* method, Thread* self)
+{
+ jclass staticMethodClass;
+ int oldStatus;
+
+#ifdef USE_INDIRECT_REF
+ staticMethodClass = addLocalReference(self->jniEnv, (Object*)method->clazz);
+ if (staticMethodClass == NULL) {
+ assert(dvmCheckException(self));
+ return;
+ }
+#else
+ staticMethodClass = (jobject) method->clazz;
+#endif
+
+ oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
+
+ ANDROID_MEMBAR_FULL(); /* guarantee ordering on method->insns */
+
+ COMPUTE_STACK_SUM(self);
+ dvmPlatformInvoke(self->jniEnv, staticMethodClass,
+ method->jniArgInfo, method->insSize, args, method->shorty,
+ (void*)method->insns, pResult);
+ CHECK_STACK_SUM(self);
+
+ dvmChangeStatus(self, oldStatus);
+
+ convertReferenceResult(self->jniEnv, pResult, method, self);
+}
+
+/*
+ * Extract the return type enum from the "jniArgInfo" field.
+ */
+DalvikJniReturnType dvmGetArgInfoReturnType(int jniArgInfo)
+{
+ return (jniArgInfo & DALVIK_JNI_RETURN_MASK) >> DALVIK_JNI_RETURN_SHIFT;
+}
+
+
+/*
+ * ===========================================================================
+ * JNI implementation
+ * ===========================================================================
+ */
+
+/*
+ * Return the version of the native method interface.
+ */
+static jint GetVersion(JNIEnv* env)
+{
+ JNI_ENTER();
+ /*
+ * There is absolutely no need to toggle the mode for correct behavior.
+ * However, it does provide native code with a simple "suspend self
+ * if necessary" call.
+ */
+ JNI_EXIT();
+ return JNI_VERSION_1_6;
+}
+
+/*
+ * Create a new class from a bag of bytes.
+ *
+ * This is not currently supported within Dalvik.
+ */
+static jclass DefineClass(JNIEnv* env, const char *name, jobject loader,
+ const jbyte* buf, jsize bufLen)
+{
+ UNUSED_PARAMETER(name);
+ UNUSED_PARAMETER(loader);
+ UNUSED_PARAMETER(buf);
+ UNUSED_PARAMETER(bufLen);
+
+ JNI_ENTER();
+ LOGW("JNI DefineClass is not supported\n");
+ JNI_EXIT();
+ return NULL;
+}
+
+/*
+ * Find a class by name.
+ *
+ * We have to use the "no init" version of FindClass here, because we might
+ * be getting the class prior to registering native methods that will be
+ * used in <clinit>.
+ *
+ * We need to get the class loader associated with the current native
+ * method. If there is no native method, e.g. we're calling this from native
+ * code right after creating the VM, the spec says we need to use the class
+ * loader returned by "ClassLoader.getBaseClassLoader". There is no such
+ * method, but it's likely they meant ClassLoader.getSystemClassLoader.
+ * We can't get that until after the VM has initialized though.
+ */
+static jclass FindClass(JNIEnv* env, const char* name)
+{
+ JNI_ENTER();
+
+ const Method* thisMethod;
+ ClassObject* clazz;
+ jclass jclazz = NULL;
+ Object* loader;
+ char* descriptor = NULL;
+
+ thisMethod = dvmGetCurrentJNIMethod();
+ assert(thisMethod != NULL);
+
+ descriptor = dvmNameToDescriptor(name);
+ if (descriptor == NULL) {
+ clazz = NULL;
+ goto bail;
+ }
+
+ //Thread* self = dvmThreadSelf();
+ if (_self->classLoaderOverride != NULL) {
+ /* hack for JNI_OnLoad */
+ assert(strcmp(thisMethod->name, "nativeLoad") == 0);
+ loader = _self->classLoaderOverride;
+ } else if (thisMethod == gDvm.methFakeNativeEntry) {
+ /* start point of invocation interface */
+ if (!gDvm.initializing)
+ loader = dvmGetSystemClassLoader();
+ else
+ loader = NULL;
+ } else {
+ loader = thisMethod->clazz->classLoader;
+ }
+
+ clazz = dvmFindClassNoInit(descriptor, loader);
+ jclazz = addLocalReference(env, (Object*) clazz);
+
+bail:
+ free(descriptor);
+
+ JNI_EXIT();
+ return jclazz;
+}
+
+/*
+ * Return the superclass of a class.
+ */
+static jclass GetSuperclass(JNIEnv* env, jclass jclazz)
+{
+ JNI_ENTER();
+ jclass jsuper = NULL;
+
+ ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+ if (clazz != NULL)
+ jsuper = addLocalReference(env, (Object*)clazz->super);
+ JNI_EXIT();
+ return jsuper;
+}
+
+/*
+ * Determine whether an object of clazz1 can be safely cast to clazz2.
+ *
+ * Like IsInstanceOf, but with a pair of class objects instead of obj+class.
+ */
+static jboolean IsAssignableFrom(JNIEnv* env, jclass jclazz1, jclass jclazz2)
+{
+ JNI_ENTER();
+
+ ClassObject* clazz1 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz1);
+ ClassObject* clazz2 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz2);
+
+ jboolean result = dvmInstanceof(clazz1, clazz2);
+
+ JNI_EXIT();
+ return result;
+}
+
+/*
+ * Given a java.lang.reflect.Method or .Constructor, return a methodID.
+ */
+static jmethodID FromReflectedMethod(JNIEnv* env, jobject jmethod)
+{
+ JNI_ENTER();
+ jmethodID methodID;
+ Object* method = dvmDecodeIndirectRef(env, jmethod);
+ methodID = (jmethodID) dvmGetMethodFromReflectObj(method);
+ JNI_EXIT();
+ return methodID;
+}
+
+/*
+ * Given a java.lang.reflect.Field, return a fieldID.
+ */
+static jfieldID FromReflectedField(JNIEnv* env, jobject jfield)
+{
+ JNI_ENTER();
+ jfieldID fieldID;
+ Object* field = dvmDecodeIndirectRef(env, jfield);
+ fieldID = (jfieldID) dvmGetFieldFromReflectObj(field);
+ JNI_EXIT();
+ return fieldID;
+}
+
+/*
+ * Convert a methodID to a java.lang.reflect.Method or .Constructor.
+ *
+ * (The "isStatic" field does not appear in the spec.)
+ *
+ * Throws OutOfMemory and returns NULL on failure.
+ */
+static jobject ToReflectedMethod(JNIEnv* env, jclass jcls, jmethodID methodID,
+ jboolean isStatic)
+{
+ JNI_ENTER();
+ ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls);
+ Object* obj = dvmCreateReflectObjForMethod(clazz, (Method*) methodID);
+ dvmReleaseTrackedAlloc(obj, NULL);
+ jobject jobj = addLocalReference(env, obj);
+ JNI_EXIT();
+ return jobj;
+}
+
+/*
+ * Convert a fieldID to a java.lang.reflect.Field.
+ *
+ * (The "isStatic" field does not appear in the spec.)
+ *
+ * Throws OutOfMemory and returns NULL on failure.
+ */
+static jobject ToReflectedField(JNIEnv* env, jclass jcls, jfieldID fieldID,
+ jboolean isStatic)
+{
+ JNI_ENTER();
+ ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls);
+ Object* obj = dvmCreateReflectObjForField(clazz, (Field*) fieldID);
+ dvmReleaseTrackedAlloc(obj, NULL);
+ jobject jobj = addLocalReference(env, obj);
+ JNI_EXIT();
+ return jobj;
+}
+
+/*
+ * Take this exception and throw it.
+ */
+static jint Throw(JNIEnv* env, jthrowable jobj)
+{
+ JNI_ENTER();
+
+ jint retval;
+
+ if (jobj != NULL) {
+ Object* obj = dvmDecodeIndirectRef(env, jobj);
+ dvmSetException(_self, obj);
+ retval = JNI_OK;
+ } else {
+ retval = JNI_ERR;
+ }
+
+ JNI_EXIT();
+ return retval;
+}
+
+/*
+ * Constructs an exception object from the specified class with the message
+ * specified by "message", and throws it.
+ */
+static jint ThrowNew(JNIEnv* env, jclass jclazz, const char* message)
+{
+ JNI_ENTER();
+
+ ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+ dvmThrowExceptionByClass(clazz, message);
+ // TODO: should return failure if this didn't work (e.g. OOM)
+
+ JNI_EXIT();
+ return JNI_OK;
+}
+
+/*
+ * If an exception is being thrown, return the exception object. Otherwise,
+ * return NULL.
+ *
+ * TODO: if there is no pending exception, we should be able to skip the
+ * enter/exit checks. If we find one, we need to enter and then re-fetch
+ * the exception (in case it got moved by a compacting GC).
+ */
+static jthrowable ExceptionOccurred(JNIEnv* env)
+{
+ JNI_ENTER();
+
+ Object* exception;
+ jobject localException;
+
+ exception = dvmGetException(_self);
+ localException = addLocalReference(env, exception);
+ if (localException == NULL && exception != NULL) {
+ /*
+ * We were unable to add a new local reference, and threw a new
+ * exception. We can't return "exception", because it's not a
+ * local reference. So we have to return NULL, indicating that
+ * there was no exception, even though it's pretty much raining
+ * exceptions in here.
+ */
+ LOGW("JNI WARNING: addLocal/exception combo\n");
+ }
+
+ JNI_EXIT();
+ return localException;
+}
+
+/*
+ * Print an exception and stack trace to stderr.
+ */
+static void ExceptionDescribe(JNIEnv* env)
+{
+ JNI_ENTER();
+
+ Object* exception = dvmGetException(_self);
+ if (exception != NULL) {
+ dvmPrintExceptionStackTrace();
+ } else {
+ LOGI("Odd: ExceptionDescribe called, but no exception pending\n");
+ }
+
+ JNI_EXIT();
+}
+
+/*
+ * Clear the exception currently being thrown.
+ *
+ * TODO: we should be able to skip the enter/exit stuff.
+ */
+static void ExceptionClear(JNIEnv* env)
+{
+ JNI_ENTER();
+ dvmClearException(_self);
+ JNI_EXIT();
+}
+
+/*
+ * Kill the VM. This function does not return.
+ */
+static void FatalError(JNIEnv* env, const char* msg)
+{
+ //dvmChangeStatus(NULL, THREAD_RUNNING);
+ LOGE("JNI posting fatal error: %s\n", msg);
+ dvmAbort();
+}
+
+/*
+ * Push a new JNI frame on the stack, with a new set of locals.
+ *
+ * The new frame must have the same method pointer. (If for no other
+ * reason than FindClass needs it to get the appropriate class loader.)
+ */
+static jint PushLocalFrame(JNIEnv* env, jint capacity)
+{
+ JNI_ENTER();
+ int result = JNI_OK;
+ if (!ensureLocalCapacity(env, capacity) ||
+ !dvmPushLocalFrame(_self /*dvmThreadSelf()*/, dvmGetCurrentJNIMethod()))
+ {
+ /* yes, OutOfMemoryError, not StackOverflowError */
+ dvmClearException(_self);
+ dvmThrowException("Ljava/lang/OutOfMemoryError;",
+ "out of stack in JNI PushLocalFrame");
+ result = JNI_ERR;
+ }
+ JNI_EXIT();
+ return result;
+}
+
+/*
+ * Pop the local frame off. If "result" is not null, add it as a
+ * local reference on the now-current frame.
+ */
+static jobject PopLocalFrame(JNIEnv* env, jobject jresult)
+{
+ JNI_ENTER();
+ Object* result = dvmDecodeIndirectRef(env, jresult);
+ if (!dvmPopLocalFrame(_self /*dvmThreadSelf()*/)) {
+ LOGW("JNI WARNING: too many PopLocalFrame calls\n");
+ dvmClearException(_self);
+ dvmThrowException("Ljava/lang/RuntimeException;",
+ "too many PopLocalFrame calls");
+ }
+ jresult = addLocalReference(env, result);
+ JNI_EXIT();
+ return result;
+}
+
+/*
+ * Add a reference to the global list.
+ */
+static jobject NewGlobalRef(JNIEnv* env, jobject jobj)
+{
+ Object* obj;
+
+ JNI_ENTER();
+ if (dvmIsWeakGlobalRef(jobj))
+ obj = getPhantomReferent(env, (jweak) jobj);
+ else
+ obj = dvmDecodeIndirectRef(env, jobj);
+ jobject retval = addGlobalReference(obj);
+ JNI_EXIT();
+ return retval;
+}
+
+/*
+ * Delete a reference from the global list.
+ */
+static void DeleteGlobalRef(JNIEnv* env, jobject jglobalRef)
+{
+ JNI_ENTER();
+ deleteGlobalReference(jglobalRef);
+ JNI_EXIT();
+}
+
+
+/*
+ * Add a reference to the local list.
+ */
+static jobject NewLocalRef(JNIEnv* env, jobject jobj)
+{
+ Object* obj;
+
+ JNI_ENTER();
+ if (dvmIsWeakGlobalRef(jobj))
+ obj = getPhantomReferent(env, (jweak) jobj);
+ else
+ obj = dvmDecodeIndirectRef(env, jobj);
+ jobject retval = addLocalReference(env, obj);
+ JNI_EXIT();
+ return retval;
+}
+
+/*
+ * Delete a reference from the local list.
+ */
+static void DeleteLocalRef(JNIEnv* env, jobject jlocalRef)
+{
+ JNI_ENTER();
+ deleteLocalReference(env, jlocalRef);
+ JNI_EXIT();
+}
+
+/*
+ * Ensure that the local references table can hold at least this many
+ * references.
+ */
+static jint EnsureLocalCapacity(JNIEnv* env, jint capacity)
+{
+ JNI_ENTER();
+ bool okay = ensureLocalCapacity(env, capacity);
+ if (!okay) {
+ dvmThrowException("Ljava/lang/OutOfMemoryError;",
+ "can't ensure local reference capacity");
+ }
+ JNI_EXIT();
+ if (okay)
+ return 0;
+ else
+ return -1;
+}
+
+
+/*
+ * Determine whether two Object references refer to the same underlying object.
+ */
+static jboolean IsSameObject(JNIEnv* env, jobject jref1, jobject jref2)
+{
+ JNI_ENTER();
+ Object* obj1 = dvmDecodeIndirectRef(env, jref1);
+ Object* obj2 = dvmDecodeIndirectRef(env, jref2);
+ jboolean result = (obj1 == obj2);
+ JNI_EXIT();
+ return result;
+}
+
+/*
+ * Allocate a new object without invoking any constructors.
+ */
+static jobject AllocObject(JNIEnv* env, jclass jclazz)
+{
+ JNI_ENTER();
+
+ ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+ jobject result;
+
+ if (!canAllocClass(clazz) ||
+ (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)))
+ {
+ assert(dvmCheckException(_self));
+ result = NULL;
+ } else {
+ Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+ result = addLocalReference(env, newObj);
+ }
+
+ JNI_EXIT();
+ return result;
+}
+
+/*
+ * Allocate a new object and invoke the supplied constructor.
+ */
+static jobject NewObject(JNIEnv* env, jclass jclazz, jmethodID methodID, ...)
+{
+ JNI_ENTER();
+ ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+ jobject result;
+
+ if (!canAllocClass(clazz) ||
+ (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)))
+ {
+ assert(dvmCheckException(_self));
+ result = NULL;
+ } else {
+ Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+ result = addLocalReference(env, newObj);
+ if (newObj != NULL) {
+ JValue unused;
+ va_list args;
+ va_start(args, methodID);
+ dvmCallMethodV(_self, (Method*) methodID, newObj, true, &unused,
+ args);
+ va_end(args);
+ }
+ }
+
+ JNI_EXIT();
+ return result;
+}
+static jobject NewObjectV(JNIEnv* env, jclass jclazz, jmethodID methodID,
+ va_list args)
+{
+ JNI_ENTER();
+ ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+ jobject result;
+
+ if (!canAllocClass(clazz) ||
+ (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)))
+ {
+ assert(dvmCheckException(_self));
+ result = NULL;
+ } else {
+ Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+ result = addLocalReference(env, newObj);
+ if (newObj != NULL) {
+ JValue unused;
+ dvmCallMethodV(_self, (Method*) methodID, newObj, true, &unused,
+ args);
+ }
+ }
+
+ JNI_EXIT();
+ return result;
+}
+static jobject NewObjectA(JNIEnv* env, jclass jclazz, jmethodID methodID,
+ jvalue* args)
+{
+ JNI_ENTER();
+ ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+ jobject result;
+
+ if (!canAllocClass(clazz) ||
+ (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)))
+ {
+ assert(dvmCheckException(_self));
+ result = NULL;
+ } else {
+ Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+ result = addLocalReference(env, newObj);
+ if (newObj != NULL) {
+ JValue unused;
+ dvmCallMethodA(_self, (Method*) methodID, newObj, true, &unused,
+ args);
+ }
+ }
+
+ JNI_EXIT();
+ return result;
+}
+
+/*
+ * Returns the class of an object.
+ *
+ * JNI spec says: obj must not be NULL.
+ */
+static jclass GetObjectClass(JNIEnv* env, jobject jobj)
+{
+ JNI_ENTER();
+
+ assert(jobj != NULL);
+
+ Object* obj = dvmDecodeIndirectRef(env, jobj);
+ jclass jclazz = addLocalReference(env, (Object*) obj->clazz);
+
+ JNI_EXIT();
+ return jclazz;
+}
+
+/*
+ * Determine whether "obj" is an instance of "clazz".
+ */
+static jboolean IsInstanceOf(JNIEnv* env, jobject jobj, jclass jclazz)
+{
+ JNI_ENTER();
+
+ assert(jclazz != NULL);
+
+ jboolean result;
+
+ if (jobj == NULL) {
+ result = true;
+ } else {
+ Object* obj = dvmDecodeIndirectRef(env, jobj);
+ ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+ result = dvmInstanceof(obj->clazz, clazz);
+ }
+
+ JNI_EXIT();
+ return result;
+}
+
+/*
+ * Get a method ID for an instance method.
+ *
+ * JNI defines <init> as an instance method, but Dalvik considers it a
+ * "direct" method, so we have to special-case it here.
+ *
+ * Dalvik also puts all private methods into the "direct" list, so we
+ * really need to just search both lists.
+ */
+static jmethodID GetMethodID(JNIEnv* env, jclass jclazz, const char* name,
+ const char* sig)
+{
+ JNI_ENTER();
+
+ ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+ jmethodID id = NULL;
+
+ if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
+ assert(dvmCheckException(_self));
+ } else {
+ Method* meth;
+
+ meth = dvmFindVirtualMethodHierByDescriptor(clazz, name, sig);
+ if (meth == NULL) {
+ /* search private methods and constructors; non-hierarchical */
+ meth = dvmFindDirectMethodByDescriptor(clazz, name, sig);
+ }
+ if (meth != NULL && dvmIsStaticMethod(meth)) {
+ IF_LOGD() {
+ char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+ LOGD("GetMethodID: not returning static method %s.%s %s\n",
+ clazz->descriptor, meth->name, desc);
+ free(desc);
+ }
+ meth = NULL;
+ }
+ if (meth == NULL) {
+ LOGD("GetMethodID: method not found: %s.%s:%s\n",
+ clazz->descriptor, name, sig);
+ dvmThrowException("Ljava/lang/NoSuchMethodError;", name);
+ }
+
+ /*
+ * The method's class may not be the same as clazz, but if
+ * it isn't this must be a virtual method and the class must
+ * be a superclass (and, hence, already initialized).
+ */
+ if (meth != NULL) {
+ assert(dvmIsClassInitialized(meth->clazz) ||
+ dvmIsClassInitializing(meth->clazz));
+ }
+ id = (jmethodID) meth;
+ }
+ JNI_EXIT();
+ return id;
+}
+
+/*
+ * Get a field ID (instance fields).
+ */
+static jfieldID GetFieldID(JNIEnv* env, jclass jclazz,
+ const char* name, const char* sig)
+{
+ JNI_ENTER();
+
+ ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+ jfieldID id;
+
+ if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
+ assert(dvmCheckException(_self));
+ id = NULL;
+ } else {
+ id = (jfieldID) dvmFindInstanceFieldHier(clazz, name, sig);
+ if (id == NULL) {
+ LOGD("GetFieldID: unable to find field %s.%s:%s\n",
+ clazz->descriptor, name, sig);
+ dvmThrowException("Ljava/lang/NoSuchFieldError;", name);
+ }
+ }
+ JNI_EXIT();
+ return id;
+}
+
+/*
+ * Get the method ID for a static method in a class.
+ */
+static jmethodID GetStaticMethodID(JNIEnv* env, jclass jclazz,
+ const char* name, const char* sig)
+{
+ JNI_ENTER();
+
+ ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+ jmethodID id;
+
+ if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
+ assert(dvmCheckException(_self));
+ id = NULL;
+ } else {
+ Method* meth;
+
+ meth = dvmFindDirectMethodHierByDescriptor(clazz, name, sig);
+
+ /* make sure it's static, not virtual+private */
+ if (meth != NULL && !dvmIsStaticMethod(meth)) {
+ IF_LOGD() {
+ char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+ LOGD("GetStaticMethodID: "
+ "not returning nonstatic method %s.%s %s\n",
+ clazz->descriptor, meth->name, desc);
+ free(desc);
+ }
+ meth = NULL;
+ }
+
+ id = (jmethodID) meth;
+ if (id == NULL)
+ dvmThrowException("Ljava/lang/NoSuchMethodError;", name);
+ }
+
+ JNI_EXIT();
+ return id;
+}
+
+/*
+ * Get a field ID (static fields).
+ */
+static jfieldID GetStaticFieldID(JNIEnv* env, jclass jclazz,
+ const char* name, const char* sig)
+{
+ JNI_ENTER();
+
+ ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+ jfieldID id;
+
+ if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
+ assert(dvmCheckException(_self));
+ id = NULL;
+ } else {
+ id = (jfieldID) dvmFindStaticField(clazz, name, sig);
+ if (id == NULL)
+ dvmThrowException("Ljava/lang/NoSuchFieldError;", name);
+ }
+ JNI_EXIT();
+ return id;
+}
+
+/*
+ * Get a static field.
+ *
+ * If we get an object reference, add it to the local refs list.
+ */
+#define GET_STATIC_TYPE_FIELD(_ctype, _jname, _isref) \
+ static _ctype GetStatic##_jname##Field(JNIEnv* env, jclass jclazz, \
+ jfieldID fieldID) \
+ { \
+ UNUSED_PARAMETER(jclazz); \
+ JNI_ENTER(); \
+ StaticField* sfield = (StaticField*) fieldID; \
+ _ctype value; \
+ if (dvmIsVolatileField(&sfield->field)) { \
+ if (_isref) { /* only when _ctype==jobject */ \
+ Object* obj = dvmGetStaticFieldObjectVolatile(sfield); \
+ value = (_ctype)(u4)addLocalReference(env, obj); \
+ } else { \
+ value = dvmGetStaticField##_jname##Volatile(sfield); \
+ } \
+ } else { \
+ if (_isref) { \
+ Object* obj = dvmGetStaticFieldObject(sfield); \
+ value = (_ctype)(u4)addLocalReference(env, obj); \
+ } else { \
+ value = dvmGetStaticField##_jname(sfield); \
+ } \
+ } \
+ JNI_EXIT(); \
+ return value; \
+ }
+GET_STATIC_TYPE_FIELD(jobject, Object, true);
+GET_STATIC_TYPE_FIELD(jboolean, Boolean, false);
+GET_STATIC_TYPE_FIELD(jbyte, Byte, false);
+GET_STATIC_TYPE_FIELD(jchar, Char, false);
+GET_STATIC_TYPE_FIELD(jshort, Short, false);
+GET_STATIC_TYPE_FIELD(jint, Int, false);
+GET_STATIC_TYPE_FIELD(jlong, Long, false);
+GET_STATIC_TYPE_FIELD(jfloat, Float, false);
+GET_STATIC_TYPE_FIELD(jdouble, Double, false);
+
+/*
+ * Set a static field.
+ */
+#define SET_STATIC_TYPE_FIELD(_ctype, _jname, _isref) \
+ static void SetStatic##_jname##Field(JNIEnv* env, jclass jclazz, \
+ jfieldID fieldID, _ctype value) \
+ { \
+ UNUSED_PARAMETER(jclazz); \
+ JNI_ENTER(); \
+ StaticField* sfield = (StaticField*) fieldID; \
+ if (dvmIsVolatileField(&sfield->field)) { \
+ if (_isref) { /* only when _ctype==jobject */ \
+ Object* valObj = \
+ dvmDecodeIndirectRef(env, (jobject)(u4)value); \
+ dvmSetStaticFieldObjectVolatile(sfield, valObj); \
+ } else { \
+ dvmSetStaticField##_jname##Volatile(sfield, value); \
+ } \
+ } else { \
+ if (_isref) { \
+ Object* valObj = \
+ dvmDecodeIndirectRef(env, (jobject)(u4)value); \
+ dvmSetStaticFieldObject(sfield, valObj); \
+ } else { \
+ dvmSetStaticField##_jname(sfield, value); \
+ } \
+ } \
+ JNI_EXIT(); \
+ }
+SET_STATIC_TYPE_FIELD(jobject, Object, true);
+SET_STATIC_TYPE_FIELD(jboolean, Boolean, false);
+SET_STATIC_TYPE_FIELD(jbyte, Byte, false);
+SET_STATIC_TYPE_FIELD(jchar, Char, false);
+SET_STATIC_TYPE_FIELD(jshort, Short, false);
+SET_STATIC_TYPE_FIELD(jint, Int, false);
+SET_STATIC_TYPE_FIELD(jlong, Long, false);
+SET_STATIC_TYPE_FIELD(jfloat, Float, false);
+SET_STATIC_TYPE_FIELD(jdouble, Double, false);
+
+/*
+ * Get an instance field.
+ *
+ * If we get an object reference, add it to the local refs list.
+ */
+#define GET_TYPE_FIELD(_ctype, _jname, _isref) \
+ static _ctype Get##_jname##Field(JNIEnv* env, jobject jobj, \
+ jfieldID fieldID) \
+ { \
+ JNI_ENTER(); \
+ Object* obj = dvmDecodeIndirectRef(env, jobj); \
+ InstField* field = (InstField*) fieldID; \
+ _ctype value; \
+ if (dvmIsVolatileField(&field->field)) { \
+ if (_isref) { /* only when _ctype==jobject */ \
+ Object* valObj = \
+ dvmGetFieldObjectVolatile(obj, field->byteOffset); \
+ value = (_ctype)(u4)addLocalReference(env, valObj); \
+ } else { \
+ value = \
+ dvmGetField##_jname##Volatile(obj, field->byteOffset); \
+ } \
+ } else { \
+ if (_isref) { \
+ Object* valObj = dvmGetFieldObject(obj, field->byteOffset); \
+ value = (_ctype)(u4)addLocalReference(env, valObj); \
+ } else { \
+ value = dvmGetField##_jname(obj, field->byteOffset); \
+ } \
+ } \
+ JNI_EXIT(); \
+ return value; \
+ }
+GET_TYPE_FIELD(jobject, Object, true);
+GET_TYPE_FIELD(jboolean, Boolean, false);
+GET_TYPE_FIELD(jbyte, Byte, false);
+GET_TYPE_FIELD(jchar, Char, false);
+GET_TYPE_FIELD(jshort, Short, false);
+GET_TYPE_FIELD(jint, Int, false);
+GET_TYPE_FIELD(jlong, Long, false);
+GET_TYPE_FIELD(jfloat, Float, false);
+GET_TYPE_FIELD(jdouble, Double, false);
+
+/*
+ * Set an instance field.
+ */
+#define SET_TYPE_FIELD(_ctype, _jname, _isref) \
+ static void Set##_jname##Field(JNIEnv* env, jobject jobj, \
+ jfieldID fieldID, _ctype value) \
+ { \
+ JNI_ENTER(); \
+ Object* obj = dvmDecodeIndirectRef(env, jobj); \
+ InstField* field = (InstField*) fieldID; \
+ if (dvmIsVolatileField(&field->field)) { \
+ if (_isref) { /* only when _ctype==jobject */ \
+ Object* valObj = \
+ dvmDecodeIndirectRef(env, (jobject)(u4)value); \
+ dvmSetFieldObjectVolatile(obj, field->byteOffset, valObj); \
+ } else { \
+ dvmSetField##_jname##Volatile(obj, \
+ field->byteOffset, value); \
+ } \
+ } else { \
+ if (_isref) { \
+ Object* valObj = \
+ dvmDecodeIndirectRef(env, (jobject)(u4)value); \
+ dvmSetFieldObject(obj, field->byteOffset, valObj); \
+ } else { \
+ dvmSetField##_jname(obj, field->byteOffset, value); \
+ } \
+ } \
+ JNI_EXIT(); \
+ }
+SET_TYPE_FIELD(jobject, Object, true);
+SET_TYPE_FIELD(jboolean, Boolean, false);
+SET_TYPE_FIELD(jbyte, Byte, false);
+SET_TYPE_FIELD(jchar, Char, false);
+SET_TYPE_FIELD(jshort, Short, false);
+SET_TYPE_FIELD(jint, Int, false);
+SET_TYPE_FIELD(jlong, Long, false);
+SET_TYPE_FIELD(jfloat, Float, false);
+SET_TYPE_FIELD(jdouble, Double, false);
+
+/*
+ * Make a virtual method call.
+ *
+ * Three versions (..., va_list, jvalue[]) for each return type. If we're
+ * returning an Object, we have to add it to the local references table.
+ */
+#define CALL_VIRTUAL(_ctype, _jname, _retfail, _retok, _isref) \
+ static _ctype Call##_jname##Method(JNIEnv* env, jobject jobj, \
+ jmethodID methodID, ...) \
+ { \
+ JNI_ENTER(); \
+ Object* obj = dvmDecodeIndirectRef(env, jobj); \
+ const Method* meth; \
+ va_list args; \
+ JValue result; \
+ meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \
+ if (meth == NULL) { \
+ JNI_EXIT(); \
+ return _retfail; \
+ } \
+ va_start(args, methodID); \
+ dvmCallMethodV(_self, meth, obj, true, &result, args); \
+ va_end(args); \
+ if (_isref && !dvmCheckException(_self)) \
+ result.l = addLocalReference(env, result.l); \
+ JNI_EXIT(); \
+ return _retok; \
+ } \
+ static _ctype Call##_jname##MethodV(JNIEnv* env, jobject jobj, \
+ jmethodID methodID, va_list args) \
+ { \
+ JNI_ENTER(); \
+ Object* obj = dvmDecodeIndirectRef(env, jobj); \
+ const Method* meth; \
+ JValue result; \
+ meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \
+ if (meth == NULL) { \
+ JNI_EXIT(); \
+ return _retfail; \
+ } \
+ dvmCallMethodV(_self, meth, obj, true, &result, args); \
+ if (_isref && !dvmCheckException(_self)) \
+ result.l = addLocalReference(env, result.l); \
+ JNI_EXIT(); \
+ return _retok; \
+ } \
+ static _ctype Call##_jname##MethodA(JNIEnv* env, jobject jobj, \
+ jmethodID methodID, jvalue* args) \
+ { \
+ JNI_ENTER(); \
+ Object* obj = dvmDecodeIndirectRef(env, jobj); \
+ const Method* meth; \
+ JValue result; \
+ meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \
+ if (meth == NULL) { \
+ JNI_EXIT(); \
+ return _retfail; \
+ } \
+ dvmCallMethodA(_self, meth, obj, true, &result, args); \
+ if (_isref && !dvmCheckException(_self)) \
+ result.l = addLocalReference(env, result.l); \
+ JNI_EXIT(); \
+ return _retok; \
+ }
+CALL_VIRTUAL(jobject, Object, NULL, result.l, true);
+CALL_VIRTUAL(jboolean, Boolean, 0, result.z, false);
+CALL_VIRTUAL(jbyte, Byte, 0, result.b, false);
+CALL_VIRTUAL(jchar, Char, 0, result.c, false);
+CALL_VIRTUAL(jshort, Short, 0, result.s, false);
+CALL_VIRTUAL(jint, Int, 0, result.i, false);
+CALL_VIRTUAL(jlong, Long, 0, result.j, false);
+CALL_VIRTUAL(jfloat, Float, 0.0f, result.f, false);
+CALL_VIRTUAL(jdouble, Double, 0.0, result.d, false);
+CALL_VIRTUAL(void, Void, , , false);
+
+/*
+ * Make a "non-virtual" method call. We're still calling a virtual method,
+ * but this time we're not doing an indirection through the object's vtable.
+ * The "clazz" parameter defines which implementation of a method we want.
+ *
+ * Three versions (..., va_list, jvalue[]) for each return type.
+ */
+#define CALL_NONVIRTUAL(_ctype, _jname, _retfail, _retok, _isref) \
+ static _ctype CallNonvirtual##_jname##Method(JNIEnv* env, jobject jobj, \
+ jclass jclazz, jmethodID methodID, ...) \
+ { \
+ JNI_ENTER(); \
+ Object* obj = dvmDecodeIndirectRef(env, jobj); \
+ ClassObject* clazz = \
+ (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \
+ const Method* meth; \
+ va_list args; \
+ JValue result; \
+ meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \
+ if (meth == NULL) { \
+ JNI_EXIT(); \
+ return _retfail; \
+ } \
+ va_start(args, methodID); \
+ dvmCallMethodV(_self, meth, obj, true, &result, args); \
+ if (_isref && !dvmCheckException(_self)) \
+ result.l = addLocalReference(env, result.l); \
+ va_end(args); \
+ JNI_EXIT(); \
+ return _retok; \
+ } \
+ static _ctype CallNonvirtual##_jname##MethodV(JNIEnv* env, jobject jobj,\
+ jclass jclazz, jmethodID methodID, va_list args) \
+ { \
+ JNI_ENTER(); \
+ Object* obj = dvmDecodeIndirectRef(env, jobj); \
+ ClassObject* clazz = \
+ (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \
+ const Method* meth; \
+ JValue result; \
+ meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \
+ if (meth == NULL) { \
+ JNI_EXIT(); \
+ return _retfail; \
+ } \
+ dvmCallMethodV(_self, meth, obj, true, &result, args); \
+ if (_isref && !dvmCheckException(_self)) \
+ result.l = addLocalReference(env, result.l); \
+ JNI_EXIT(); \
+ return _retok; \
+ } \
+ static _ctype CallNonvirtual##_jname##MethodA(JNIEnv* env, jobject jobj,\
+ jclass jclazz, jmethodID methodID, jvalue* args) \
+ { \
+ JNI_ENTER(); \
+ Object* obj = dvmDecodeIndirectRef(env, jobj); \
+ ClassObject* clazz = \
+ (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \
+ const Method* meth; \
+ JValue result; \
+ meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \
+ if (meth == NULL) { \
+ JNI_EXIT(); \
+ return _retfail; \
+ } \
+ dvmCallMethodA(_self, meth, obj, true, &result, args); \
+ if (_isref && !dvmCheckException(_self)) \
+ result.l = addLocalReference(env, result.l); \
+ JNI_EXIT(); \
+ return _retok; \
+ }
+CALL_NONVIRTUAL(jobject, Object, NULL, result.l, true);
+CALL_NONVIRTUAL(jboolean, Boolean, 0, result.z, false);
+CALL_NONVIRTUAL(jbyte, Byte, 0, result.b, false);
+CALL_NONVIRTUAL(jchar, Char, 0, result.c, false);
+CALL_NONVIRTUAL(jshort, Short, 0, result.s, false);
+CALL_NONVIRTUAL(jint, Int, 0, result.i, false);
+CALL_NONVIRTUAL(jlong, Long, 0, result.j, false);
+CALL_NONVIRTUAL(jfloat, Float, 0.0f, result.f, false);
+CALL_NONVIRTUAL(jdouble, Double, 0.0, result.d, false);
+CALL_NONVIRTUAL(void, Void, , , false);
+
+
+/*
+ * Call a static method.
+ */
+#define CALL_STATIC(_ctype, _jname, _retfail, _retok, _isref) \
+ static _ctype CallStatic##_jname##Method(JNIEnv* env, jclass jclazz, \
+ jmethodID methodID, ...) \
+ { \
+ UNUSED_PARAMETER(jclazz); \
+ JNI_ENTER(); \
+ JValue result; \
+ va_list args; \
+ va_start(args, methodID); \
+ dvmCallMethodV(_self, (Method*)methodID, NULL, true, &result, args);\
+ va_end(args); \
+ if (_isref && !dvmCheckException(_self)) \
+ result.l = addLocalReference(env, result.l); \
+ JNI_EXIT(); \
+ return _retok; \
+ } \
+ static _ctype CallStatic##_jname##MethodV(JNIEnv* env, jclass jclazz, \
+ jmethodID methodID, va_list args) \
+ { \
+ UNUSED_PARAMETER(jclazz); \
+ JNI_ENTER(); \
+ JValue result; \
+ dvmCallMethodV(_self, (Method*)methodID, NULL, true, &result, args);\
+ if (_isref && !dvmCheckException(_self)) \
+ result.l = addLocalReference(env, result.l); \
+ JNI_EXIT(); \
+ return _retok; \
+ } \
+ static _ctype CallStatic##_jname##MethodA(JNIEnv* env, jclass jclazz, \
+ jmethodID methodID, jvalue* args) \
+ { \
+ UNUSED_PARAMETER(jclazz); \
+ JNI_ENTER(); \
+ JValue result; \
+ dvmCallMethodA(_self, (Method*)methodID, NULL, true, &result, args);\
+ if (_isref && !dvmCheckException(_self)) \
+ result.l = addLocalReference(env, result.l); \
+ JNI_EXIT(); \
+ return _retok; \
+ }
+CALL_STATIC(jobject, Object, NULL, result.l, true);
+CALL_STATIC(jboolean, Boolean, 0, result.z, false);
+CALL_STATIC(jbyte, Byte, 0, result.b, false);
+CALL_STATIC(jchar, Char, 0, result.c, false);
+CALL_STATIC(jshort, Short, 0, result.s, false);
+CALL_STATIC(jint, Int, 0, result.i, false);
+CALL_STATIC(jlong, Long, 0, result.j, false);
+CALL_STATIC(jfloat, Float, 0.0f, result.f, false);
+CALL_STATIC(jdouble, Double, 0.0, result.d, false);
+CALL_STATIC(void, Void, , , false);
+
+/*
+ * Create a new String from Unicode data.
+ *
+ * If "len" is zero, we will return an empty string even if "unicodeChars"
+ * is NULL. (The JNI spec is vague here.)
+ */
+static jstring NewString(JNIEnv* env, const jchar* unicodeChars, jsize len)
+{
+ JNI_ENTER();
+ jobject retval;
+
+ StringObject* jstr = dvmCreateStringFromUnicode(unicodeChars, len);
+ if (jstr == NULL) {
+ retval = NULL;
+ } else {
+ dvmReleaseTrackedAlloc((Object*) jstr, NULL);
+ retval = addLocalReference(env, (Object*) jstr);
+ }
+
+ JNI_EXIT();
+ return retval;
+}
+
+/*
+ * Return the length of a String in Unicode character units.
+ */
+static jsize GetStringLength(JNIEnv* env, jstring jstr)
+{
+ JNI_ENTER();
+
+ StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
+ jsize len = dvmStringLen(strObj);
+
+ JNI_EXIT();
+ return len;
+}
+
+
+/*
+ * Get a string's character data.
+ *
+ * The result is guaranteed to be valid until ReleaseStringChars is
+ * called, which means we have to pin it or return a copy.
+ */
+static const jchar* GetStringChars(JNIEnv* env, jstring jstr, jboolean* isCopy)
+{
+ JNI_ENTER();
+
+ StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
+ ArrayObject* strChars = dvmStringCharArray(strObj);
+
+ pinPrimitiveArray(strChars);
+
+ const u2* data = dvmStringChars(strObj);
+ if (isCopy != NULL)
+ *isCopy = JNI_FALSE;
+
+ JNI_EXIT();
+ return (jchar*)data;
+}
+
+/*
+ * Release our grip on some characters from a string.
+ */
+static void ReleaseStringChars(JNIEnv* env, jstring jstr, const jchar* chars)
+{
+ JNI_ENTER();
+ StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
+ ArrayObject* strChars = dvmStringCharArray(strObj);
+ unpinPrimitiveArray(strChars);
+ JNI_EXIT();
+}
+
+/*
+ * Create a new java.lang.String object from chars in modified UTF-8 form.
+ *
+ * The spec doesn't say how to handle a NULL string. Popular desktop VMs
+ * accept it and return a NULL pointer in response.
+ */
+static jstring NewStringUTF(JNIEnv* env, const char* bytes)
+{
+ JNI_ENTER();
+
+ jstring result;
+
+ if (bytes == NULL) {
+ result = NULL;
+ } else {
+ /* note newStr could come back NULL on OOM */
+ StringObject* newStr = dvmCreateStringFromCstr(bytes);
+ result = addLocalReference(env, (Object*) newStr);
+ dvmReleaseTrackedAlloc((Object*)newStr, NULL);
+ }
+
+ JNI_EXIT();
+ return result;
+}
+
+/*
+ * Return the length in bytes of the modified UTF-8 form of the string.
+ */
+static jsize GetStringUTFLength(JNIEnv* env, jstring jstr)
+{
+ JNI_ENTER();
+
+ StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
+ jsize len = dvmStringUtf8ByteLen(strObj);
+
+ JNI_EXIT();
+ return len;
+}
+
+/*
+ * Convert "string" to modified UTF-8 and return a pointer. The returned
+ * value must be released with ReleaseStringUTFChars.
+ *
+ * According to the JNI reference, "Returns a pointer to a UTF-8 string,
+ * or NULL if the operation fails. Returns NULL if and only if an invocation
+ * of this function has thrown an exception."
+ *
+ * The behavior here currently follows that of other open-source VMs, which
+ * quietly return NULL if "string" is NULL. We should consider throwing an
+ * NPE. (The CheckJNI code blows up if you try to pass in a NULL string,
+ * which should catch this sort of thing during development.) Certain other
+ * VMs will crash with a segmentation fault.
+ */
+static const char* GetStringUTFChars(JNIEnv* env, jstring jstr,
+ jboolean* isCopy)
+{
+ JNI_ENTER();
+ char* newStr;
+
+ if (jstr == NULL) {
+ /* this shouldn't happen; throw NPE? */
+ newStr = NULL;
+ } else {
+ if (isCopy != NULL)
+ *isCopy = JNI_TRUE;
+
+ StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
+ newStr = dvmCreateCstrFromString(strObj);
+ if (newStr == NULL) {
+ /* assume memory failure */
+ dvmThrowException("Ljava/lang/OutOfMemoryError;",
+ "native heap string alloc failed");
+ }
+ }
+
+ JNI_EXIT();
+ return newStr;
+}
+
+/*
+ * Release a string created by GetStringUTFChars().
+ */
+static void ReleaseStringUTFChars(JNIEnv* env, jstring jstr, const char* utf)
+{
+ JNI_ENTER();
+ free((char*)utf);
+ JNI_EXIT();
+}
+
+/*
+ * Return the capacity of the array.
+ */
+static jsize GetArrayLength(JNIEnv* env, jarray jarr)
+{
+ JNI_ENTER();
+
+ ArrayObject* arrObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
+ jsize length = arrObj->length;
+
+ JNI_EXIT();
+ return length;
+}
+
+/*
+ * Construct a new array that holds objects from class "elementClass".
+ */
+static jobjectArray NewObjectArray(JNIEnv* env, jsize length,
+ jclass jelementClass, jobject jinitialElement)
+{
+ JNI_ENTER();
+
+ jobjectArray newArray = NULL;
+ ClassObject* elemClassObj =
+ (ClassObject*) dvmDecodeIndirectRef(env, jelementClass);
+
+ if (elemClassObj == NULL) {
+ dvmThrowException("Ljava/lang/NullPointerException;",
+ "JNI NewObjectArray");
+ goto bail;
+ }
+
+ ArrayObject* newObj =
+ dvmAllocObjectArray(elemClassObj, length, ALLOC_DEFAULT);
+ if (newObj == NULL) {
+ assert(dvmCheckException(_self));
+ goto bail;
+ }
+ newArray = addLocalReference(env, (Object*) newObj);
+ dvmReleaseTrackedAlloc((Object*) newObj, NULL);
+
+ /*
+ * Initialize the array. Trashes "length".
+ */
+ if (jinitialElement != NULL) {
+ Object* initialElement = dvmDecodeIndirectRef(env, jinitialElement);
+ Object** arrayData = (Object**) newObj->contents;
+
+ while (length--)
+ *arrayData++ = initialElement;
+ }
+
+
+bail:
+ JNI_EXIT();
+ return newArray;
+}
+
+/*
+ * Get one element of an Object array.
+ *
+ * Add the object to the local references table in case the array goes away.
+ */
+static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray jarr,
+ jsize index)
+{
+ JNI_ENTER();
+
+ ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
+ jobject retval = NULL;
+
+ assert(arrayObj != NULL);
+
+ /* check the array bounds */
+ if (index < 0 || index >= (int) arrayObj->length) {
+ dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;",
+ arrayObj->obj.clazz->descriptor);
+ goto bail;
+ }
+
+ Object* value = ((Object**) arrayObj->contents)[index];
+ retval = addLocalReference(env, value);
+
+bail:
+ JNI_EXIT();
+ return retval;
+}
+
+/*
+ * Set one element of an Object array.
+ */
+static void SetObjectArrayElement(JNIEnv* env, jobjectArray jarr,
+ jsize index, jobject jobj)
+{
+ JNI_ENTER();
+
+ ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
+
+ assert(arrayObj != NULL);
+
+ /* check the array bounds */
+ if (index < 0 || index >= (int) arrayObj->length) {
+ dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;",
+ arrayObj->obj.clazz->descriptor);
+ goto bail;
+ }
+
+ //LOGV("JNI: set element %d in array %p to %p\n", index, array, value);
+
+ Object* obj = dvmDecodeIndirectRef(env, jobj);
+ dvmSetObjectArrayElement(arrayObj, index, obj);
+
+bail:
+ JNI_EXIT();
+}
+
+/*
+ * Create a new array of primitive elements.
+ */
+#define NEW_PRIMITIVE_ARRAY(_artype, _jname, _typechar) \
+ static _artype New##_jname##Array(JNIEnv* env, jsize length) \
+ { \
+ JNI_ENTER(); \
+ ArrayObject* arrayObj; \
+ arrayObj = dvmAllocPrimitiveArray(_typechar, length, \
+ ALLOC_DEFAULT); \
+ jarray jarr = NULL; \
+ if (arrayObj != NULL) { \
+ jarr = addLocalReference(env, (Object*) arrayObj); \
+ dvmReleaseTrackedAlloc((Object*) arrayObj, NULL); \
+ } \
+ JNI_EXIT(); \
+ return (_artype)jarr; \
+ }
+NEW_PRIMITIVE_ARRAY(jbooleanArray, Boolean, 'Z');
+NEW_PRIMITIVE_ARRAY(jbyteArray, Byte, 'B');
+NEW_PRIMITIVE_ARRAY(jcharArray, Char, 'C');
+NEW_PRIMITIVE_ARRAY(jshortArray, Short, 'S');
+NEW_PRIMITIVE_ARRAY(jintArray, Int, 'I');
+NEW_PRIMITIVE_ARRAY(jlongArray, Long, 'J');
+NEW_PRIMITIVE_ARRAY(jfloatArray, Float, 'F');
+NEW_PRIMITIVE_ARRAY(jdoubleArray, Double, 'D');
+
+/*
+ * Get a pointer to a C array of primitive elements from an array object
+ * of the matching type.
+ *
+ * In a compacting GC, we either need to return a copy of the elements or
+ * "pin" the memory. Otherwise we run the risk of native code using the
+ * buffer as the destination of e.g. a blocking read() call that wakes up
+ * during a GC.
+ */
+#define GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
+ static _ctype* Get##_jname##ArrayElements(JNIEnv* env, \
+ _ctype##Array jarr, jboolean* isCopy) \
+ { \
+ JNI_ENTER(); \
+ _ctype* data; \
+ ArrayObject* arrayObj = \
+ (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
+ pinPrimitiveArray(arrayObj); \
+ data = (_ctype*) arrayObj->contents; \
+ if (isCopy != NULL) \
+ *isCopy = JNI_FALSE; \
+ JNI_EXIT(); \
+ return data; \
+ }
+
+/*
+ * Release the storage locked down by the "get" function.
+ *
+ * The spec says, "'mode' has no effect if 'elems' is not a copy of the
+ * elements in 'array'." They apparently did not anticipate the need to
+ * un-pin memory.
+ */
+#define RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
+ static void Release##_jname##ArrayElements(JNIEnv* env, \
+ _ctype##Array jarr, _ctype* elems, jint mode) \
+ { \
+ UNUSED_PARAMETER(elems); \
+ JNI_ENTER(); \
+ if (mode != JNI_COMMIT) { \
+ ArrayObject* arrayObj = \
+ (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
+ unpinPrimitiveArray(arrayObj); \
+ } \
+ JNI_EXIT(); \
+ }
+
+/*
+ * Copy a section of a primitive array to a buffer.
+ */
+#define GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
+ static void Get##_jname##ArrayRegion(JNIEnv* env, \
+ _ctype##Array jarr, jsize start, jsize len, _ctype* buf) \
+ { \
+ JNI_ENTER(); \
+ ArrayObject* arrayObj = \
+ (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
+ _ctype* data = (_ctype*) arrayObj->contents; \
+ if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \
+ dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+ arrayObj->obj.clazz->descriptor); \
+ } else { \
+ memcpy(buf, data + start, len * sizeof(_ctype)); \
+ } \
+ JNI_EXIT(); \
+ }
+
+/*
+ * Copy a section of a primitive array from a buffer.
+ */
+#define SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
+ static void Set##_jname##ArrayRegion(JNIEnv* env, \
+ _ctype##Array jarr, jsize start, jsize len, const _ctype* buf) \
+ { \
+ JNI_ENTER(); \
+ ArrayObject* arrayObj = \
+ (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
+ _ctype* data = (_ctype*) arrayObj->contents; \
+ if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \
+ dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+ arrayObj->obj.clazz->descriptor); \
+ } else { \
+ memcpy(data + start, buf, len * sizeof(_ctype)); \
+ } \
+ JNI_EXIT(); \
+ }
+
+/*
+ * 4-in-1:
+ * Get<Type>ArrayElements
+ * Release<Type>ArrayElements
+ * Get<Type>ArrayRegion
+ * Set<Type>ArrayRegion
+ */
+#define PRIMITIVE_ARRAY_FUNCTIONS(_ctype, _jname) \
+ GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
+ RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
+ GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname); \
+ SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname);
+
+PRIMITIVE_ARRAY_FUNCTIONS(jboolean, Boolean);
+PRIMITIVE_ARRAY_FUNCTIONS(jbyte, Byte);
+PRIMITIVE_ARRAY_FUNCTIONS(jchar, Char);
+PRIMITIVE_ARRAY_FUNCTIONS(jshort, Short);
+PRIMITIVE_ARRAY_FUNCTIONS(jint, Int);
+PRIMITIVE_ARRAY_FUNCTIONS(jlong, Long);
+PRIMITIVE_ARRAY_FUNCTIONS(jfloat, Float);
+PRIMITIVE_ARRAY_FUNCTIONS(jdouble, Double);
+
+/*
+ * Register one or more native functions in one class.
+ *
+ * This can be called multiple times on the same method, allowing the
+ * caller to redefine the method implementation at will.
+ */
+static jint RegisterNatives(JNIEnv* env, jclass jclazz,
+ const JNINativeMethod* methods, jint nMethods)
+{
+ JNI_ENTER();
+
+ ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+ jint retval = JNI_OK;
+ int i;
+
+ if (gDvm.verboseJni) {
+ LOGI("[Registering JNI native methods for class %s]\n",
+ clazz->descriptor);
+ }
+
+ for (i = 0; i < nMethods; i++) {
+ if (!dvmRegisterJNIMethod(clazz, methods[i].name,
+ methods[i].signature, methods[i].fnPtr))
+ {
+ retval = JNI_ERR;
+ }
+ }
+
+ JNI_EXIT();
+ return retval;
+}
+
+/*
+ * Un-register all native methods associated with the class.
+ *
+ * The JNI docs refer to this as a way to reload/relink native libraries,
+ * and say it "should not be used in normal native code". In particular,
+ * there is no need to do this during shutdown, and you do not need to do
+ * this before redefining a method implementation with RegisterNatives.
+ *
+ * It's chiefly useful for a native "plugin"-style library that wasn't
+ * loaded with System.loadLibrary() (since there's no way to unload those).
+ * For example, the library could upgrade itself by:
+ *
+ * 1. call UnregisterNatives to unbind the old methods
+ * 2. ensure that no code is still executing inside it (somehow)
+ * 3. dlclose() the library
+ * 4. dlopen() the new library
+ * 5. use RegisterNatives to bind the methods from the new library
+ *
+ * The above can work correctly without the UnregisterNatives call, but
+ * creates a window of opportunity in which somebody might try to call a
+ * method that is pointing at unmapped memory, crashing the VM. In theory
+ * the same guards that prevent dlclose() from unmapping executing code could
+ * prevent that anyway, but with this we can be more thorough and also deal
+ * with methods that only exist in the old or new form of the library (maybe
+ * the lib wants to try the call and catch the UnsatisfiedLinkError).
+ */
+static jint UnregisterNatives(JNIEnv* env, jclass jclazz)
+{
+ JNI_ENTER();
+
+ ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+ if (gDvm.verboseJni) {
+ LOGI("[Unregistering JNI native methods for class %s]\n",
+ clazz->descriptor);
+ }
+ dvmUnregisterJNINativeMethods(clazz);
+
+ JNI_EXIT();
+ return JNI_OK;
+}
+
+/*
+ * Lock the monitor.
+ *
+ * We have to track all monitor enters and exits, so that we can undo any
+ * outstanding synchronization before the thread exits.
+ */
+static jint MonitorEnter(JNIEnv* env, jobject jobj)
+{
+ JNI_ENTER();
+ Object* obj = dvmDecodeIndirectRef(env, jobj);
+ dvmLockObject(_self, obj);
+ trackMonitorEnter(_self, obj);
+ JNI_EXIT();
+ return JNI_OK;
+}
+
+/*
+ * Unlock the monitor.
+ *
+ * Throws an IllegalMonitorStateException if the current thread
+ * doesn't own the monitor. (dvmUnlockObject() takes care of the throw.)
+ *
+ * According to the 1.6 spec, it's legal to call here with an exception
+ * pending. If this fails, we'll stomp the original exception.
+ */
+static jint MonitorExit(JNIEnv* env, jobject jobj)
+{
+ JNI_ENTER();
+ Object* obj = dvmDecodeIndirectRef(env, jobj);
+ bool success = dvmUnlockObject(_self, obj);
+ if (success)
+ trackMonitorExit(_self, obj);
+ JNI_EXIT();
+ return success ? JNI_OK : JNI_ERR;
+}
+
+/*
+ * Return the JavaVM interface associated with the current thread.
+ */
+static jint GetJavaVM(JNIEnv* env, JavaVM** vm)
+{
+ JNI_ENTER();
+ //*vm = gDvm.vmList;
+ *vm = (JavaVM*) ((JNIEnvExt*)env)->vm;
+ JNI_EXIT();
+ if (*vm == NULL)
+ return JNI_ERR;
+ else
+ return JNI_OK;
+}
+
+/*
+ * Copies "len" Unicode characters, from offset "start".
+ */
+static void GetStringRegion(JNIEnv* env, jstring jstr, jsize start, jsize len,
+ jchar* buf)
+{
+ JNI_ENTER();
+ StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
+ if (start + len > dvmStringLen(strObj))
+ dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL);
+ else
+ memcpy(buf, dvmStringChars(strObj) + start, len * sizeof(u2));
+ JNI_EXIT();
+}
+
+/*
+ * Translates "len" Unicode characters, from offset "start", into
+ * modified UTF-8 encoding.
+ */
+static void GetStringUTFRegion(JNIEnv* env, jstring jstr, jsize start,
+ jsize len, char* buf)
+{
+ JNI_ENTER();
+ StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
+ if (start + len > dvmStringLen(strObj))
+ dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL);
+ else
+ dvmCreateCstrFromStringRegion(strObj, start, len, buf);
+ JNI_EXIT();
+}
+
+/*
+ * Get a raw pointer to array data.
+ *
+ * The caller is expected to call "release" before doing any JNI calls
+ * or blocking I/O operations.
+ *
+ * We need to pin the memory or block GC.
+ */
+static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray jarr,
+ jboolean* isCopy)
+{
+ JNI_ENTER();
+ void* data;
+ ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
+ pinPrimitiveArray(arrayObj);
+ data = arrayObj->contents;
+ if (isCopy != NULL)
+ *isCopy = JNI_FALSE;
+ JNI_EXIT();
+ return data;
+}
+
+/*
+ * Release an array obtained with GetPrimitiveArrayCritical.
+ */
+static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray jarr,
+ void* carray, jint mode)
+{
+ JNI_ENTER();
+ if (mode != JNI_COMMIT) {
+ ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
+ unpinPrimitiveArray(arrayObj);
+ }
+ JNI_EXIT();
+}
+
+/*
+ * Like GetStringChars, but with restricted use.
+ */
+static const jchar* GetStringCritical(JNIEnv* env, jstring jstr,
+ jboolean* isCopy)
+{
+ JNI_ENTER();
+ StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
+ ArrayObject* strChars = dvmStringCharArray(strObj);
+
+ pinPrimitiveArray(strChars);
+
+ const u2* data = dvmStringChars(strObj);
+ if (isCopy != NULL)
+ *isCopy = JNI_FALSE;
+
+ JNI_EXIT();
+ return (jchar*)data;
+}
+
+/*
+ * Like ReleaseStringChars, but with restricted use.
+ */
+static void ReleaseStringCritical(JNIEnv* env, jstring jstr,
+ const jchar* carray)
+{
+ JNI_ENTER();
+ StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
+ ArrayObject* strChars = dvmStringCharArray(strObj);
+ unpinPrimitiveArray(strChars);
+ JNI_EXIT();
+}
+
+/*
+ * Create a new weak global reference.
+ */
+static jweak NewWeakGlobalRef(JNIEnv* env, jobject obj)
+{
+ JNI_ENTER();
+ jweak wref = createWeakGlobalRef(env, obj);
+ JNI_EXIT();
+ return wref;
+}
+
+/*
+ * Delete the specified weak global reference.
+ */
+static void DeleteWeakGlobalRef(JNIEnv* env, jweak wref)
+{
+ JNI_ENTER();
+ deleteWeakGlobalRef(env, wref);
+ JNI_EXIT();
+}
+
+/*
+ * Quick check for pending exceptions.
+ *
+ * TODO: we should be able to skip the enter/exit macros here.
+ */
+static jboolean ExceptionCheck(JNIEnv* env)
+{
+ JNI_ENTER();
+ bool result = dvmCheckException(_self);
+ JNI_EXIT();
+ return result;
+}
+
+/*
+ * Returns the type of the object referred to by "obj". It can be local,
+ * global, or weak global.
+ *
+ * In the current implementation, references can be global and local at
+ * the same time, so while the return value is accurate it may not tell
+ * the whole story.
+ */
+static jobjectRefType GetObjectRefType(JNIEnv* env, jobject jobj)
+{
+ JNI_ENTER();
+ jobjectRefType type = dvmGetJNIRefType(env, jobj);
+ JNI_EXIT();
+ return type;
+}
+
+/*
+ * Allocate and return a new java.nio.ByteBuffer for this block of memory.
+ *
+ * "address" may not be NULL, and "capacity" must be > 0. (These are only
+ * verified when CheckJNI is enabled.)
+ */
+static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity)
+{
+ JNI_ENTER();
+
+ Thread* self = _self /*dvmThreadSelf()*/;
+ Object* platformAddress = NULL;
+ JValue callResult;
+ jobject result = NULL;
+ ClassObject* tmpClazz;
+
+ tmpClazz = gDvm.methOrgApacheHarmonyLuniPlatformPlatformAddress_on->clazz;
+ if (!dvmIsClassInitialized(tmpClazz) && !dvmInitClass(tmpClazz))
+ goto bail;
+
+ /* get an instance of PlatformAddress that wraps the provided address */
+ dvmCallMethod(self,
+ gDvm.methOrgApacheHarmonyLuniPlatformPlatformAddress_on,
+ NULL, &callResult, address);
+ if (dvmGetException(self) != NULL || callResult.l == NULL)
+ goto bail;
+
+ /* don't let the GC discard it */
+ platformAddress = (Object*) callResult.l;
+ dvmAddTrackedAlloc(platformAddress, self);
+ LOGV("tracking %p for address=%p\n", platformAddress, address);
+
+ /* create an instance of java.nio.ReadWriteDirectByteBuffer */
+ tmpClazz = gDvm.classJavaNioReadWriteDirectByteBuffer;
+ if (!dvmIsClassInitialized(tmpClazz) && !dvmInitClass(tmpClazz))
+ goto bail;
+ Object* newObj = dvmAllocObject(tmpClazz, ALLOC_DONT_TRACK);
+ if (newObj != NULL) {
+ /* call the (PlatformAddress, int, int) constructor */
+ result = addLocalReference(env, newObj);
+ dvmCallMethod(self, gDvm.methJavaNioReadWriteDirectByteBuffer_init,
+ newObj, &callResult, platformAddress, (jint) capacity, (jint) 0);
+ if (dvmGetException(self) != NULL) {
+ deleteLocalReference(env, result);
+ result = NULL;
+ goto bail;
+ }
+ }
+
+bail:
+ if (platformAddress != NULL)
+ dvmReleaseTrackedAlloc(platformAddress, self);
+ JNI_EXIT();
+ return result;
+}
+
+/*
+ * Get the starting address of the buffer for the specified java.nio.Buffer.
+ *
+ * If this is not a "direct" buffer, we return NULL.
+ */
+static void* GetDirectBufferAddress(JNIEnv* env, jobject jbuf)
+{
+ JNI_ENTER();
+
+ Object* bufObj = dvmDecodeIndirectRef(env, jbuf);
+ Thread* self = _self /*dvmThreadSelf()*/;
+ void* result;
+
+ /*
+ * All Buffer objects have an effectiveDirectAddress field. If it's
+ * nonzero, we can just return that value. If not, we have to call
+ * through DirectBuffer.getEffectiveAddress(), which as a side-effect
+ * will set the effectiveDirectAddress field for direct buffers (and
+ * things that wrap direct buffers).
+ */
+ result = (void*) dvmGetFieldInt(bufObj,
+ gDvm.offJavaNioBuffer_effectiveDirectAddress);
+ if (result != NULL) {
+ //LOGI("fast path for %p\n", buf);
+ goto bail;
+ }
+
+ /*
+ * Start by determining if the object supports the DirectBuffer
+ * interfaces. Note this does not guarantee that it's a direct buffer.
+ */
+ if (!dvmInstanceof(bufObj->clazz,
+ gDvm.classOrgApacheHarmonyNioInternalDirectBuffer))
+ {
+ goto bail;
+ }
+
+ /*
+ * Get a PlatformAddress object with the effective address.
+ *
+ * If this isn't a direct buffer, the result will be NULL and/or an
+ * exception will have been thrown.
+ */
+ JValue callResult;
+ const Method* meth = dvmGetVirtualizedMethod(bufObj->clazz,
+ gDvm.methOrgApacheHarmonyNioInternalDirectBuffer_getEffectiveAddress);
+ dvmCallMethodA(self, meth, bufObj, false, &callResult, NULL);
+ if (dvmGetException(self) != NULL) {
+ dvmClearException(self);
+ callResult.l = NULL;
+ }
+
+ Object* platformAddr = callResult.l;
+ if (platformAddr == NULL) {
+ LOGV("Got request for address of non-direct buffer\n");
+ goto bail;
+ }
+
+ /*
+ * Extract the address from the PlatformAddress object. Instead of
+ * calling the toLong() method, just grab the field directly. This
+ * is faster but more fragile.
+ */
+ result = (void*) dvmGetFieldInt(platformAddr,
+ gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr);
+
+ //LOGI("slow path for %p --> %p\n", buf, result);
+
+bail:
+ JNI_EXIT();
+ return result;
+}
+
+/*
+ * Get the capacity of the buffer for the specified java.nio.Buffer.
+ *
+ * Returns -1 if the object is not a direct buffer. (We actually skip
+ * this check, since it's expensive to determine, and just return the
+ * capacity regardless.)
+ */
+static jlong GetDirectBufferCapacity(JNIEnv* env, jobject jbuf)
+{
+ JNI_ENTER();
+
+ /*
+ * The capacity is always in the Buffer.capacity field.
+ *
+ * (The "check" version should verify that this is actually a Buffer,
+ * but we're not required to do so here.)
+ */
+ Object* buf = dvmDecodeIndirectRef(env, jbuf);
+ jlong result = dvmGetFieldInt(buf, gDvm.offJavaNioBuffer_capacity);
+
+ JNI_EXIT();
+ return result;
+}
+
+
+/*
+ * ===========================================================================
+ * JNI invocation functions
+ * ===========================================================================
+ */
+
+/*
+ * Handle AttachCurrentThread{AsDaemon}.
+ *
+ * We need to make sure the VM is actually running. For example, if we start
+ * up, issue an Attach, and the VM exits almost immediately, by the time the
+ * attaching happens the VM could already be shutting down.
+ *
+ * It's hard to avoid a race condition here because we don't want to hold
+ * a lock across the entire operation. What we can do is temporarily
+ * increment the thread count to prevent a VM exit.
+ *
+ * This could potentially still have problems if a daemon thread calls here
+ * while the VM is shutting down. dvmThreadSelf() will work, since it just
+ * uses pthread TLS, but dereferencing "vm" could fail. Such is life when
+ * you shut down a VM while threads are still running inside it.
+ *
+ * Remember that some code may call this as a way to find the per-thread
+ * JNIEnv pointer. Don't do excess work for that case.
+ */
+static jint attachThread(JavaVM* vm, JNIEnv** p_env, void* thr_args,
+ bool isDaemon)
+{
+ JavaVMAttachArgs* args = (JavaVMAttachArgs*) thr_args;
+ Thread* self;
+ bool result = false;
+
+ /*
+ * Return immediately if we're already one with the VM.
+ */
+ self = dvmThreadSelf();
+ if (self != NULL) {
+ *p_env = self->jniEnv;
+ return JNI_OK;
+ }
+
+ /*
+ * No threads allowed in zygote mode.
+ */
+ if (gDvm.zygote) {
+ return JNI_ERR;
+ }
+
+ /* increment the count to keep the VM from bailing while we run */
+ dvmLockThreadList(NULL);
+ if (gDvm.nonDaemonThreadCount == 0) {
+ // dead or dying
+ LOGV("Refusing to attach thread '%s' -- VM is shutting down\n",
+ (thr_args == NULL) ? "(unknown)" : args->name);
+ dvmUnlockThreadList();
+ return JNI_ERR;
+ }
+ gDvm.nonDaemonThreadCount++;
+ dvmUnlockThreadList();
+
+ /* tweak the JavaVMAttachArgs as needed */
+ JavaVMAttachArgs argsCopy;
+ if (args == NULL) {
+ /* allow the v1.1 calling convention */
+ argsCopy.version = JNI_VERSION_1_2;
+ argsCopy.name = NULL;
+ argsCopy.group = dvmGetMainThreadGroup();
+ } else {
+ assert(args->version >= JNI_VERSION_1_2);
+
+ argsCopy.version = args->version;
+ argsCopy.name = args->name;
+ if (args->group != NULL)
+ argsCopy.group = args->group;
+ else
+ argsCopy.group = dvmGetMainThreadGroup();
+ }
+
+ result = dvmAttachCurrentThread(&argsCopy, isDaemon);
+
+ /* restore the count */
+ dvmLockThreadList(NULL);
+ gDvm.nonDaemonThreadCount--;
+ dvmUnlockThreadList();
+
+ /*
+ * Change the status to indicate that we're out in native code. This
+ * call is not guarded with state-change macros, so we have to do it
+ * by hand.
+ */
+ if (result) {
+ self = dvmThreadSelf();
+ assert(self != NULL);
+ dvmChangeStatus(self, THREAD_NATIVE);
+ *p_env = self->jniEnv;
+ return JNI_OK;
+ } else {
+ return JNI_ERR;
+ }
+}
+
+/*
+ * Attach the current thread to the VM. If the thread is already attached,
+ * this is a no-op.
+ */
+static jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args)
+{
+ return attachThread(vm, p_env, thr_args, false);
+}
+
+/*
+ * Like AttachCurrentThread, but set the "daemon" flag.
+ */
+static jint AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env,
+ void* thr_args)
+{
+ return attachThread(vm, p_env, thr_args, true);
+}
+
+/*
+ * Dissociate the current thread from the VM.
+ */
+static jint DetachCurrentThread(JavaVM* vm)
+{
+ Thread* self = dvmThreadSelf();
+
+ if (self == NULL) /* not attached, can't do anything */
+ return JNI_ERR;
+
+ /* switch to "running" to check for suspension */
+ dvmChangeStatus(self, THREAD_RUNNING);
+
+ /* detach the thread */
+ dvmDetachCurrentThread();
+
+ /* (no need to change status back -- we have no status) */
+ return JNI_OK;
+}
+
+/*
+ * If current thread is attached to VM, return the associated JNIEnv.
+ * Otherwise, stuff NULL in and return JNI_EDETACHED.
+ *
+ * JVMTI overloads this by specifying a magic value for "version", so we
+ * do want to check that here.
+ */
+static jint GetEnv(JavaVM* vm, void** env, jint version)
+{
+ Thread* self = dvmThreadSelf();
+
+ if (version < JNI_VERSION_1_1 || version > JNI_VERSION_1_6)
+ return JNI_EVERSION;
+
+ if (self == NULL) {
+ *env = NULL;
+ } else {
+ /* TODO: status change is probably unnecessary */
+ dvmChangeStatus(self, THREAD_RUNNING);
+ *env = (void*) dvmGetThreadJNIEnv(self);
+ dvmChangeStatus(self, THREAD_NATIVE);
+ }
+ if (*env == NULL)
+ return JNI_EDETACHED;
+ else
+ return JNI_OK;
+}
+
+/*
+ * Destroy the VM. This may be called from any thread.
+ *
+ * If the current thread is attached, wait until the current thread is
+ * the only non-daemon user-level thread. If the current thread is not
+ * attached, we attach it and do the processing as usual. (If the attach
+ * fails, it's probably because all the non-daemon threads have already
+ * exited and the VM doesn't want to let us back in.)
+ *
+ * TODO: we don't really deal with the situation where more than one thread
+ * has called here. One thread wins, the other stays trapped waiting on
+ * the condition variable forever. Not sure this situation is interesting
+ * in real life.
+ */
+static jint DestroyJavaVM(JavaVM* vm)
+{
+ JavaVMExt* ext = (JavaVMExt*) vm;
+ Thread* self;
+
+ if (ext == NULL)
+ return JNI_ERR;
+
+ if (gDvm.verboseShutdown)
+ LOGD("DestroyJavaVM waiting for non-daemon threads to exit\n");
+
+ /*
+ * Sleep on a condition variable until it's okay to exit.
+ */
+ self = dvmThreadSelf();
+ if (self == NULL) {
+ JNIEnv* tmpEnv;
+ if (AttachCurrentThread(vm, &tmpEnv, NULL) != JNI_OK) {
+ LOGV("Unable to reattach main for Destroy; assuming VM is "
+ "shutting down (count=%d)\n",
+ gDvm.nonDaemonThreadCount);
+ goto shutdown;
+ } else {
+ LOGV("Attached to wait for shutdown in Destroy\n");
+ }
+ }
+ dvmChangeStatus(self, THREAD_VMWAIT);
+
+ dvmLockThreadList(self);
+ gDvm.nonDaemonThreadCount--; // remove current thread from count
+
+ while (gDvm.nonDaemonThreadCount > 0)
+ pthread_cond_wait(&gDvm.vmExitCond, &gDvm.threadListLock);
+
+ dvmUnlockThreadList();
+ self = NULL;
+
+shutdown:
+ // TODO: call System.exit() to run any registered shutdown hooks
+ // (this may not return -- figure out how this should work)
+
+ if (gDvm.verboseShutdown)
+ LOGD("DestroyJavaVM shutting VM down\n");
+ dvmShutdown();
+
+ // TODO - free resources associated with JNI-attached daemon threads
+ free(ext->envList);
+ free(ext);
+
+ return JNI_OK;
+}
+
+
+/*
+ * ===========================================================================
+ * Function tables
+ * ===========================================================================
+ */
+
+static const struct JNINativeInterface gNativeInterface = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+
+ GetVersion,
+
+ DefineClass,
+ FindClass,
+
+ FromReflectedMethod,
+ FromReflectedField,
+ ToReflectedMethod,
+
+ GetSuperclass,
+ IsAssignableFrom,
+
+ ToReflectedField,
+
+ Throw,
+ ThrowNew,
+ ExceptionOccurred,
+ ExceptionDescribe,
+ ExceptionClear,
+ FatalError,
+
+ PushLocalFrame,
+ PopLocalFrame,
+
+ NewGlobalRef,
+ DeleteGlobalRef,
+ DeleteLocalRef,
+ IsSameObject,
+ NewLocalRef,
+ EnsureLocalCapacity,
+
+ AllocObject,
+ NewObject,
+ NewObjectV,
+ NewObjectA,
+
+ GetObjectClass,
+ IsInstanceOf,
+
+ GetMethodID,
+
+ CallObjectMethod,
+ CallObjectMethodV,
+ CallObjectMethodA,
+ CallBooleanMethod,
+ CallBooleanMethodV,
+ CallBooleanMethodA,
+ CallByteMethod,
+ CallByteMethodV,
+ CallByteMethodA,
+ CallCharMethod,
+ CallCharMethodV,
+ CallCharMethodA,
+ CallShortMethod,
+ CallShortMethodV,
+ CallShortMethodA,
+ CallIntMethod,
+ CallIntMethodV,
+ CallIntMethodA,
+ CallLongMethod,
+ CallLongMethodV,
+ CallLongMethodA,
+ CallFloatMethod,
+ CallFloatMethodV,
+ CallFloatMethodA,
+ CallDoubleMethod,
+ CallDoubleMethodV,
+ CallDoubleMethodA,
+ CallVoidMethod,
+ CallVoidMethodV,
+ CallVoidMethodA,
+
+ CallNonvirtualObjectMethod,
+ CallNonvirtualObjectMethodV,
+ CallNonvirtualObjectMethodA,
+ CallNonvirtualBooleanMethod,
+ CallNonvirtualBooleanMethodV,
+ CallNonvirtualBooleanMethodA,
+ CallNonvirtualByteMethod,
+ CallNonvirtualByteMethodV,
+ CallNonvirtualByteMethodA,
+ CallNonvirtualCharMethod,
+ CallNonvirtualCharMethodV,
+ CallNonvirtualCharMethodA,
+ CallNonvirtualShortMethod,
+ CallNonvirtualShortMethodV,
+ CallNonvirtualShortMethodA,
+ CallNonvirtualIntMethod,
+ CallNonvirtualIntMethodV,
+ CallNonvirtualIntMethodA,
+ CallNonvirtualLongMethod,
+ CallNonvirtualLongMethodV,
+ CallNonvirtualLongMethodA,
+ CallNonvirtualFloatMethod,
+ CallNonvirtualFloatMethodV,
+ CallNonvirtualFloatMethodA,
+ CallNonvirtualDoubleMethod,
+ CallNonvirtualDoubleMethodV,
+ CallNonvirtualDoubleMethodA,
+ CallNonvirtualVoidMethod,
+ CallNonvirtualVoidMethodV,
+ CallNonvirtualVoidMethodA,
+
+ GetFieldID,
+
+ GetObjectField,
+ GetBooleanField,
+ GetByteField,
+ GetCharField,
+ GetShortField,
+ GetIntField,
+ GetLongField,
+ GetFloatField,
+ GetDoubleField,
+ SetObjectField,
+ SetBooleanField,
+ SetByteField,
+ SetCharField,
+ SetShortField,
+ SetIntField,
+ SetLongField,
+ SetFloatField,
+ SetDoubleField,
+
+ GetStaticMethodID,
+
+ CallStaticObjectMethod,
+ CallStaticObjectMethodV,
+ CallStaticObjectMethodA,
+ CallStaticBooleanMethod,
+ CallStaticBooleanMethodV,
+ CallStaticBooleanMethodA,
+ CallStaticByteMethod,
+ CallStaticByteMethodV,
+ CallStaticByteMethodA,
+ CallStaticCharMethod,
+ CallStaticCharMethodV,
+ CallStaticCharMethodA,
+ CallStaticShortMethod,
+ CallStaticShortMethodV,
+ CallStaticShortMethodA,
+ CallStaticIntMethod,
+ CallStaticIntMethodV,
+ CallStaticIntMethodA,
+ CallStaticLongMethod,
+ CallStaticLongMethodV,
+ CallStaticLongMethodA,
+ CallStaticFloatMethod,
+ CallStaticFloatMethodV,
+ CallStaticFloatMethodA,
+ CallStaticDoubleMethod,
+ CallStaticDoubleMethodV,
+ CallStaticDoubleMethodA,
+ CallStaticVoidMethod,
+ CallStaticVoidMethodV,
+ CallStaticVoidMethodA,
+
+ GetStaticFieldID,
+
+ GetStaticObjectField,
+ GetStaticBooleanField,
+ GetStaticByteField,
+ GetStaticCharField,
+ GetStaticShortField,
+ GetStaticIntField,
+ GetStaticLongField,
+ GetStaticFloatField,
+ GetStaticDoubleField,
+
+ SetStaticObjectField,
+ SetStaticBooleanField,
+ SetStaticByteField,
+ SetStaticCharField,
+ SetStaticShortField,
+ SetStaticIntField,
+ SetStaticLongField,
+ SetStaticFloatField,
+ SetStaticDoubleField,
+
+ NewString,
+
+ GetStringLength,
+ GetStringChars,
+ ReleaseStringChars,
+
+ NewStringUTF,
+ GetStringUTFLength,
+ GetStringUTFChars,
+ ReleaseStringUTFChars,
+
+ GetArrayLength,
+ NewObjectArray,
+ GetObjectArrayElement,
+ SetObjectArrayElement,
+
+ NewBooleanArray,
+ NewByteArray,
+ NewCharArray,
+ NewShortArray,
+ NewIntArray,
+ NewLongArray,
+ NewFloatArray,
+ NewDoubleArray,
+
+ GetBooleanArrayElements,
+ GetByteArrayElements,
+ GetCharArrayElements,
+ GetShortArrayElements,
+ GetIntArrayElements,
+ GetLongArrayElements,
+ GetFloatArrayElements,
+ GetDoubleArrayElements,
+
+ ReleaseBooleanArrayElements,
+ ReleaseByteArrayElements,
+ ReleaseCharArrayElements,
+ ReleaseShortArrayElements,
+ ReleaseIntArrayElements,
+ ReleaseLongArrayElements,
+ ReleaseFloatArrayElements,
+ ReleaseDoubleArrayElements,
+
+ GetBooleanArrayRegion,
+ GetByteArrayRegion,
+ GetCharArrayRegion,
+ GetShortArrayRegion,
+ GetIntArrayRegion,
+ GetLongArrayRegion,
+ GetFloatArrayRegion,
+ GetDoubleArrayRegion,
+ SetBooleanArrayRegion,
+ SetByteArrayRegion,
+ SetCharArrayRegion,
+ SetShortArrayRegion,
+ SetIntArrayRegion,
+ SetLongArrayRegion,
+ SetFloatArrayRegion,
+ SetDoubleArrayRegion,
+
+ RegisterNatives,
+ UnregisterNatives,
+
+ MonitorEnter,
+ MonitorExit,
+
+ GetJavaVM,
+
+ GetStringRegion,
+ GetStringUTFRegion,
+
+ GetPrimitiveArrayCritical,
+ ReleasePrimitiveArrayCritical,
+
+ GetStringCritical,
+ ReleaseStringCritical,
+
+ NewWeakGlobalRef,
+ DeleteWeakGlobalRef,
+
+ ExceptionCheck,
+
+ NewDirectByteBuffer,
+ GetDirectBufferAddress,
+ GetDirectBufferCapacity,
+
+ GetObjectRefType
+};
+static const struct JNIInvokeInterface gInvokeInterface = {
+ NULL,
+ NULL,
+ NULL,
+
+ DestroyJavaVM,
+ AttachCurrentThread,
+ DetachCurrentThread,
+
+ GetEnv,
+
+ AttachCurrentThreadAsDaemon,
+};
+
+
+/*
+ * ===========================================================================
+ * VM/Env creation
+ * ===========================================================================
+ */
+
+/*
+ * Enable "checked JNI" after the VM has partially started. This must
+ * only be called in "zygote" mode, when we have one thread running.
+ *
+ * This doesn't attempt to rewrite the JNI call bridge associated with
+ * native methods, so we won't get those checks for any methods that have
+ * already been resolved.
+ */
+void dvmLateEnableCheckedJni(void)
+{
+ JNIEnvExt* extEnv;
+ JavaVMExt* extVm;
+
+ extEnv = dvmGetJNIEnvForThread();
+ if (extEnv == NULL) {
+ LOGE("dvmLateEnableCheckedJni: thread has no JNIEnv\n");
+ return;
+ }
+ extVm = extEnv->vm;
+ assert(extVm != NULL);
+
+ if (!extVm->useChecked) {
+ LOGD("Late-enabling CheckJNI\n");
+ dvmUseCheckedJniVm(extVm);
+ extVm->useChecked = true;
+ dvmUseCheckedJniEnv(extEnv);
+
+ /* currently no way to pick up jniopts features */
+ } else {
+ LOGD("Not late-enabling CheckJNI (already on)\n");
+ }
+}
+
+/*
+ * Not supported.
+ */
+jint JNI_GetDefaultJavaVMInitArgs(void* vm_args)
+{
+ return JNI_ERR;
+}
+
+/*
+ * Return a buffer full of created VMs.
+ *
+ * We always have zero or one.
+ */
+jint JNI_GetCreatedJavaVMs(JavaVM** vmBuf, jsize bufLen, jsize* nVMs)
+{
+ if (gDvm.vmList != NULL) {
+ *nVMs = 1;
+
+ if (bufLen > 0)
+ *vmBuf++ = gDvm.vmList;
+ } else {
+ *nVMs = 0;
+ }
+
+ return JNI_OK;
+}
+
+
+/*
+ * Create a new VM instance.
+ *
+ * The current thread becomes the main VM thread. We return immediately,
+ * which effectively means the caller is executing in a native method.
+ */
+jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args)
+{
+ const JavaVMInitArgs* args = (JavaVMInitArgs*) vm_args;
+ JNIEnvExt* pEnv = NULL;
+ JavaVMExt* pVM = NULL;
+ const char** argv;
+ int argc = 0;
+ int i, curOpt;
+ int result = JNI_ERR;
+ bool checkJni = false;
+ bool warnError = true;
+ bool forceDataCopy = false;
+
+ if (args->version < JNI_VERSION_1_2)
+ return JNI_EVERSION;
+
+ // TODO: don't allow creation of multiple VMs -- one per customer for now
+
+ /* zero globals; not strictly necessary the first time a VM is started */
+ memset(&gDvm, 0, sizeof(gDvm));
+
+ /*
+ * Set up structures for JNIEnv and VM.
+ */
+ //pEnv = (JNIEnvExt*) malloc(sizeof(JNIEnvExt));
+ pVM = (JavaVMExt*) malloc(sizeof(JavaVMExt));
+
+ //memset(pEnv, 0, sizeof(JNIEnvExt));
+ //pEnv->funcTable = &gNativeInterface;
+ //pEnv->vm = pVM;
+ memset(pVM, 0, sizeof(JavaVMExt));
+ pVM->funcTable = &gInvokeInterface;
+ pVM->envList = pEnv;
+ dvmInitMutex(&pVM->envListLock);
+
+ argv = (const char**) malloc(sizeof(char*) * (args->nOptions));
+ memset(argv, 0, sizeof(char*) * (args->nOptions));
+
+ curOpt = 0;
+
+ /*
+ * Convert JNI args to argv.
+ *
+ * We have to pull out vfprintf/exit/abort, because they use the
+ * "extraInfo" field to pass function pointer "hooks" in. We also
+ * look for the -Xcheck:jni stuff here.
+ */
+ for (i = 0; i < args->nOptions; i++) {
+ const char* optStr = args->options[i].optionString;
+
+ if (optStr == NULL) {
+ fprintf(stderr, "ERROR: arg %d string was null\n", i);
+ goto bail;
+ } else if (strcmp(optStr, "vfprintf") == 0) {
+ gDvm.vfprintfHook = args->options[i].extraInfo;
+ } else if (strcmp(optStr, "exit") == 0) {
+ gDvm.exitHook = args->options[i].extraInfo;
+ } else if (strcmp(optStr, "abort") == 0) {
+ gDvm.abortHook = args->options[i].extraInfo;
+ } else if (strcmp(optStr, "-Xcheck:jni") == 0) {
+ checkJni = true;
+ } else if (strncmp(optStr, "-Xjniopts:", 10) == 0) {
+ const char* jniOpts = optStr + 9;
+ while (jniOpts != NULL) {
+ jniOpts++; /* skip past ':' or ',' */
+ if (strncmp(jniOpts, "warnonly", 8) == 0) {
+ warnError = false;
+ } else if (strncmp(jniOpts, "forcecopy", 9) == 0) {
+ forceDataCopy = true;
+ } else {
+ LOGW("unknown jni opt starting at '%s'\n", jniOpts);
+ }
+ jniOpts = strchr(jniOpts, ',');
+ }
+ } else {
+ /* regular option */
+ argv[curOpt++] = optStr;
+ }
+ }
+ argc = curOpt;
+
+ if (checkJni) {
+ dvmUseCheckedJniVm(pVM);
+ pVM->useChecked = true;
+ }
+ pVM->warnError = warnError;
+ pVM->forceDataCopy = forceDataCopy;
+
+ /* set this up before initializing VM, so it can create some JNIEnvs */
+ gDvm.vmList = (JavaVM*) pVM;
+
+ /*
+ * Create an env for main thread. We need to have something set up
+ * here because some of the class initialization we do when starting
+ * up the VM will call into native code.
+ */
+ pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL);
+
+ /* initialize VM */
+ gDvm.initializing = true;
+ if (dvmStartup(argc, argv, args->ignoreUnrecognized, (JNIEnv*)pEnv) != 0) {
+ free(pEnv);
+ free(pVM);
+ goto bail;
+ }
+
+ /*
+ * Success! Return stuff to caller.
+ */
+ dvmChangeStatus(NULL, THREAD_NATIVE);
+ *p_env = (JNIEnv*) pEnv;
+ *p_vm = (JavaVM*) pVM;
+ result = JNI_OK;
+
+bail:
+ gDvm.initializing = false;
+ if (result == JNI_OK)
+ LOGV("JNI_CreateJavaVM succeeded\n");
+ else
+ LOGW("JNI_CreateJavaVM failed\n");
+ free(argv);
+ return result;
+}
diff --git a/vm/JniInternal.h b/vm/JniInternal.h
new file mode 100644
index 0000000..302dcb0
--- /dev/null
+++ b/vm/JniInternal.h
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * JNI innards, common to the regular and "checked" interfaces.
+ */
+#ifndef _DALVIK_JNIINTERNAL
+#define _DALVIK_JNIINTERNAL
+
+#include "jni.h"
+
+/* system init/shutdown */
+bool dvmJniStartup(void);
+void dvmJniShutdown(void);
+
+/*
+ * Our data structures for JNIEnv and JavaVM.
+ *
+ * Native code thinks it has a pointer to a pointer. We know better.
+ */
+struct JavaVMExt;
+
+typedef struct JNIEnvExt {
+ const struct JNINativeInterface* funcTable; /* must be first */
+
+ const struct JNINativeInterface* baseFuncTable;
+
+ /* pointer to the VM we are a part of */
+ struct JavaVMExt* vm;
+
+ u4 envThreadId;
+ Thread* self;
+
+ /* if nonzero, we are in a "critical" JNI call */
+ int critical;
+
+ /* keep a copy of this here for speed */
+ bool forceDataCopy;
+
+ struct JNIEnvExt* prev;
+ struct JNIEnvExt* next;
+} JNIEnvExt;
+
+typedef struct JavaVMExt {
+ const struct JNIInvokeInterface* funcTable; /* must be first */
+
+ const struct JNIInvokeInterface* baseFuncTable;
+
+ /* if multiple VMs are desired, add doubly-linked list stuff here */
+
+ /* per-VM feature flags */
+ bool useChecked;
+ bool warnError;
+ bool forceDataCopy;
+
+ /* head of list of JNIEnvs associated with this VM */
+ JNIEnvExt* envList;
+ pthread_mutex_t envListLock;
+} JavaVMExt;
+
+/*
+ * Native function return type; used by dvmPlatformInvoke().
+ *
+ * This is part of Method.jniArgInfo, and must fit in 3 bits.
+ * Note: Assembly code in arch/<arch>/Call<arch>.S relies on
+ * the enum values defined here.
+ */
+typedef enum DalvikJniReturnType {
+ DALVIK_JNI_RETURN_VOID = 0, /* must be zero */
+ DALVIK_JNI_RETURN_FLOAT = 1,
+ DALVIK_JNI_RETURN_DOUBLE = 2,
+ DALVIK_JNI_RETURN_S8 = 3,
+ DALVIK_JNI_RETURN_S4 = 4,
+ DALVIK_JNI_RETURN_S2 = 5,
+ DALVIK_JNI_RETURN_U2 = 6,
+ DALVIK_JNI_RETURN_S1 = 7
+} DalvikJniReturnType;
+
+#define DALVIK_JNI_NO_ARG_INFO 0x80000000
+#define DALVIK_JNI_RETURN_MASK 0x70000000
+#define DALVIK_JNI_RETURN_SHIFT 28
+#define DALVIK_JNI_COUNT_MASK 0x0f000000
+#define DALVIK_JNI_COUNT_SHIFT 24
+
+
+/*
+ * Pop the JNI local stack when we return from a native method. "saveArea"
+ * points to the StackSaveArea for the method we're leaving.
+ *
+ * (This may be implemented directly in assembly in mterp, so changes here
+ * may only affect the portable interpreter.)
+ */
+INLINE void dvmPopJniLocals(Thread* self, StackSaveArea* saveArea)
+{
+#ifdef USE_INDIRECT_REF
+ self->jniLocalRefTable.segmentState.all = saveArea->xtra.localRefCookie;
+#else
+ self->jniLocalRefTable.nextEntry = saveArea->xtra.localRefCookie;
+#endif
+}
+
+/*
+ * Set the envThreadId field.
+ */
+INLINE void dvmSetJniEnvThreadId(JNIEnv* pEnv, Thread* self)
+{
+ ((JNIEnvExt*)pEnv)->envThreadId = self->threadId;
+ ((JNIEnvExt*)pEnv)->self = self;
+}
+
+/*
+ * JNI call bridges. Not called directly.
+ *
+ * The "Check" versions are used when CheckJNI is enabled.
+ */
+void dvmCallJNIMethod_general(const u4* args, JValue* pResult,
+ const Method* method, Thread* self);
+void dvmCallJNIMethod_synchronized(const u4* args, JValue* pResult,
+ const Method* method, Thread* self);
+void dvmCallJNIMethod_virtualNoRef(const u4* args, JValue* pResult,
+ const Method* method, Thread* self);
+void dvmCallJNIMethod_staticNoRef(const u4* args, JValue* pResult,
+ const Method* method, Thread* self);
+void dvmCheckCallJNIMethod_general(const u4* args, JValue* pResult,
+ const Method* method, Thread* self);
+void dvmCheckCallJNIMethod_synchronized(const u4* args, JValue* pResult,
+ const Method* method, Thread* self);
+void dvmCheckCallJNIMethod_virtualNoRef(const u4* args, JValue* pResult,
+ const Method* method, Thread* self);
+void dvmCheckCallJNIMethod_staticNoRef(const u4* args, JValue* pResult,
+ const Method* method, Thread* self);
+
+/*
+ * Configure "method" to use the JNI bridge to call "func".
+ */
+void dvmUseJNIBridge(Method* method, void* func);
+
+
+/*
+ * Enable the "checked" versions.
+ */
+void dvmUseCheckedJniEnv(JNIEnvExt* pEnv);
+void dvmUseCheckedJniVm(JavaVMExt* pVm);
+void dvmLateEnableCheckedJni(void);
+
+/*
+ * Decode a local, global, or weak-global reference.
+ */
+#ifdef USE_INDIRECT_REF
+Object* dvmDecodeIndirectRef(JNIEnv* env, jobject jobj);
+#else
+/* use an inline to ensure this is a no-op */
+INLINE Object* dvmDecodeIndirectRef(JNIEnv* env, jobject jobj) {
+ return (Object*) jobj;
+}
+#endif
+
+/*
+ * Verify that a reference passed in from native code is valid. Returns
+ * an indication of local/global/invalid.
+ */
+jobjectRefType dvmGetJNIRefType(JNIEnv* env, jobject jobj);
+
+/*
+ * Get the last method called on the interp stack. This is the method
+ * "responsible" for calling into JNI.
+ */
+const Method* dvmGetCurrentJNIMethod(void);
+
+/*
+ * Create/destroy a JNIEnv for the current thread.
+ */
+JNIEnv* dvmCreateJNIEnv(Thread* self);
+void dvmDestroyJNIEnv(JNIEnv* env);
+
+/*
+ * Find the JNIEnv associated with the current thread.
+ */
+JNIEnvExt* dvmGetJNIEnvForThread(void);
+
+/*
+ * Extract the return type enum from the "jniArgInfo" value.
+ */
+DalvikJniReturnType dvmGetArgInfoReturnType(int jniArgInfo);
+
+/*
+ * Release all MonitorEnter-acquired locks that are still held. Called at
+ * DetachCurrentThread time.
+ */
+void dvmReleaseJniMonitors(Thread* self);
+
+/*
+ * Dump the contents of the JNI reference tables to the log file.
+ *
+ * The local ref tables associated with other threads are not included.
+ */
+void dvmDumpJniReferenceTables(void);
+
+/*
+ * This mask is applied to weak global reference values returned to
+ * native code. The goal is to create an invalid pointer that will cause
+ * a crash if misused. The mmap region for the virtual heap is typically
+ * around 0x40xxxxxx.
+ *
+ * To make weak global references easily distinguishable from other kinds
+ * of references when !USE_INDIRECT_REF, we XOR the low bits. Assuming >=
+ * 64-bit alignment of objects, this changes the low 3 bits from all clear
+ * to all set.
+ */
+#define WEAK_GLOBAL_XOR 0x9e0fffff
+
+/*
+ * "Obfuscate" a weak global reference pointer.
+ */
+INLINE jweak dvmObfuscateWeakGlobalRef(jobject jobj) {
+ return (jweak) ((u4) jobj ^ WEAK_GLOBAL_XOR);
+}
+
+/*
+ * Undo the obfuscation.
+ */
+INLINE jobject dvmNormalizeWeakGlobalRef(jweak ref) {
+ return (jobject) ((u4) ref ^ WEAK_GLOBAL_XOR);
+}
+
+/*
+ * Returns "true" if this looks like a weak global reference.
+ *
+ * Relies on the low 3 bits being set instead of clear (the latter is
+ * guaranteed by 64-bit alignment of objects).
+ */
+INLINE bool dvmIsWeakGlobalRef(jobject jobj) {
+ return (((u4) jobj & 0x07) == 0x07);
+}
+
+#endif /*_DALVIK_JNIINTERNAL*/
diff --git a/vm/LinearAlloc.c b/vm/LinearAlloc.c
new file mode 100644
index 0000000..65db79e
--- /dev/null
+++ b/vm/LinearAlloc.c
@@ -0,0 +1,704 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Linear memory allocation, tied to class loaders.
+ */
+#include "Dalvik.h"
+
+#include <sys/mman.h>
+#include <limits.h>
+#include <errno.h>
+
+//#define DISABLE_LINEAR_ALLOC
+
+// Use ashmem to name the LinearAlloc section
+#define USE_ASHMEM 1
+
+#ifdef USE_ASHMEM
+#include <cutils/ashmem.h>
+#endif /* USE_ASHMEM */
+
+/*
+Overview
+
+This is intended to be a simple, fast allocator for "write-once" storage.
+The expectation is that this will hold small allocations that don't change,
+such as parts of classes (vtables, fields, methods, interfaces). Because
+the lifetime of these items is tied to classes, which in turn are tied
+to class loaders, we associate the storage with a ClassLoader object.
+
+[ We don't yet support class unloading, and our ClassLoader implementation
+is in flux, so for now we just have a single global region and the
+"classLoader" argument is ignored. ]
+
+By storing the data here, rather than on the system heap, we reduce heap
+clutter, speed class loading, reduce the memory footprint (reduced heap
+structure overhead), and most importantly we increase the number of pages
+that remain shared between processes launched in "Zygote mode".
+
+The 4 bytes preceding each block contain the block length. This allows us
+to support "free" and "realloc" calls in a limited way. We don't free
+storage once it has been allocated, but in some circumstances it could be
+useful to erase storage to garbage values after a "free" or "realloc".
+(Bad idea if we're trying to share pages.) We need to align to 8-byte
+boundaries for some architectures, so we have a 50-50 chance of getting
+this for free in a given block.
+
+A NULL value for the "classLoader" argument refers to the bootstrap class
+loader, which is never unloaded (until the VM shuts down).
+
+Because the memory is not expected to be updated, we can use mprotect to
+guard the pages on debug builds. Handy when tracking down corruption.
+*/
+
+/* alignment for allocations; must be power of 2, and currently >= hdr_xtra */
+#define BLOCK_ALIGN 8
+
+/* default length of memory segment (worst case is probably "dexopt") */
+#define DEFAULT_MAX_LENGTH (5*1024*1024)
+
+/* leave enough space for a length word */
+#define HEADER_EXTRA 4
+
+/* overload the length word */
+#define LENGTHFLAG_FREE 0x80000000
+#define LENGTHFLAG_RW 0x40000000
+#define LENGTHFLAG_MASK (~(LENGTHFLAG_FREE|LENGTHFLAG_RW))
+
+
+/* fwd */
+static void checkAllFree(Object* classLoader);
+
+
+/*
+ * Someday, retrieve the linear alloc struct associated with a particular
+ * class loader. For now, always use the boostrap loader's instance.
+ */
+static inline LinearAllocHdr* getHeader(Object* classLoader)
+{
+ return gDvm.pBootLoaderAlloc;
+}
+
+/*
+ * Convert a pointer to memory to a pointer to the block header (which is
+ * currently just a length word).
+ */
+static inline u4* getBlockHeader(void* mem)
+{
+ return ((u4*) mem) -1;
+}
+
+/*
+ * Create a new linear allocation block.
+ */
+LinearAllocHdr* dvmLinearAllocCreate(Object* classLoader)
+{
+#ifdef DISABLE_LINEAR_ALLOC
+ return (LinearAllocHdr*) 0x12345;
+#endif
+ LinearAllocHdr* pHdr;
+
+ pHdr = (LinearAllocHdr*) malloc(sizeof(*pHdr));
+
+
+ /*
+ * "curOffset" points to the location of the next pre-block header,
+ * which means we have to advance to the next BLOCK_ALIGN address and
+ * back up.
+ *
+ * Note we leave the first page empty (see below), and start the
+ * first entry on the second page at an offset that ensures the next
+ * chunk of data will be properly aligned.
+ */
+ assert(BLOCK_ALIGN >= HEADER_EXTRA);
+ pHdr->curOffset = pHdr->firstOffset =
+ (BLOCK_ALIGN-HEADER_EXTRA) + SYSTEM_PAGE_SIZE;
+ pHdr->mapLength = DEFAULT_MAX_LENGTH;
+
+#ifdef USE_ASHMEM
+ int fd;
+
+ fd = ashmem_create_region("dalvik-LinearAlloc", DEFAULT_MAX_LENGTH);
+ if (fd < 0) {
+ LOGE("ashmem LinearAlloc failed %s", strerror(errno));
+ free(pHdr);
+ return NULL;
+ }
+
+ pHdr->mapAddr = mmap(NULL, pHdr->mapLength, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE, fd, 0);
+ if (pHdr->mapAddr == MAP_FAILED) {
+ LOGE("LinearAlloc mmap(%d) failed: %s\n", pHdr->mapLength,
+ strerror(errno));
+ free(pHdr);
+ close(fd);
+ return NULL;
+ }
+
+ close(fd);
+#else /*USE_ASHMEM*/
+ // MAP_ANON is listed as "deprecated" on Linux,
+ // but MAP_ANONYMOUS is not defined under Mac OS X.
+ pHdr->mapAddr = mmap(NULL, pHdr->mapLength, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON, -1, 0);
+ if (pHdr->mapAddr == MAP_FAILED) {
+ LOGE("LinearAlloc mmap(%d) failed: %s\n", pHdr->mapLength,
+ strerror(errno));
+ free(pHdr);
+ return NULL;
+ }
+#endif /*USE_ASHMEM*/
+
+ /* region expected to begin on a page boundary */
+ assert(((int) pHdr->mapAddr & (SYSTEM_PAGE_SIZE-1)) == 0);
+
+ /* the system should initialize newly-mapped memory to zero */
+ assert(*(u4*) (pHdr->mapAddr + pHdr->curOffset) == 0);
+
+ /*
+ * Disable access to all except starting page. We will enable pages
+ * as we use them. This helps prevent bad pointers from working. The
+ * pages start out PROT_NONE, become read/write while we access them,
+ * then go to read-only after we finish our changes.
+ *
+ * We have to make the first page readable because we have 4 pad bytes,
+ * followed by 4 length bytes, giving an initial offset of 8. The
+ * generic code below assumes that there could have been a previous
+ * allocation that wrote into those 4 pad bytes, therefore the page
+ * must have been marked readable by the previous allocation.
+ *
+ * We insert an extra page in here to force a break in the memory map
+ * so we can see ourselves more easily in "showmap". Otherwise this
+ * stuff blends into the neighboring pages. [TODO: do we still need
+ * the extra page now that we have ashmem?]
+ */
+ if (mprotect(pHdr->mapAddr, pHdr->mapLength, PROT_NONE) != 0) {
+ LOGW("LinearAlloc init mprotect failed: %s\n", strerror(errno));
+ free(pHdr);
+ return NULL;
+ }
+ if (mprotect(pHdr->mapAddr + SYSTEM_PAGE_SIZE, SYSTEM_PAGE_SIZE,
+ ENFORCE_READ_ONLY ? PROT_READ : PROT_READ|PROT_WRITE) != 0)
+ {
+ LOGW("LinearAlloc init mprotect #2 failed: %s\n", strerror(errno));
+ free(pHdr);
+ return NULL;
+ }
+
+ if (ENFORCE_READ_ONLY) {
+ /* allocate the per-page ref count */
+ int numPages = (pHdr->mapLength+SYSTEM_PAGE_SIZE-1) / SYSTEM_PAGE_SIZE;
+ pHdr->writeRefCount = calloc(numPages, sizeof(short));
+ if (pHdr->writeRefCount == NULL) {
+ free(pHdr);
+ return NULL;
+ }
+ }
+
+ dvmInitMutex(&pHdr->lock);
+
+ LOGV("LinearAlloc: created region at %p-%p\n",
+ pHdr->mapAddr, pHdr->mapAddr + pHdr->mapLength-1);
+
+ return pHdr;
+}
+
+/*
+ * Destroy a linear allocation area.
+ *
+ * We do a trivial "has everything been freed?" check before unmapping the
+ * memory and freeing the LinearAllocHdr.
+ */
+void dvmLinearAllocDestroy(Object* classLoader)
+{
+#ifdef DISABLE_LINEAR_ALLOC
+ return;
+#endif
+ LinearAllocHdr* pHdr = getHeader(classLoader);
+ if (pHdr == NULL)
+ return;
+
+ checkAllFree(classLoader);
+
+ //dvmLinearAllocDump(classLoader);
+
+ if (gDvm.verboseShutdown) {
+ LOGV("Unmapping linear allocator base=%p\n", pHdr->mapAddr);
+ LOGD("LinearAlloc %p used %d of %d (%d%%)\n",
+ classLoader, pHdr->curOffset, pHdr->mapLength,
+ (pHdr->curOffset * 100) / pHdr->mapLength);
+ }
+
+ if (munmap(pHdr->mapAddr, pHdr->mapLength) != 0) {
+ LOGW("LinearAlloc munmap(%p, %d) failed: %s\n",
+ pHdr->mapAddr, pHdr->mapLength, strerror(errno));
+ }
+ free(pHdr);
+}
+
+/*
+ * Allocate "size" bytes of storage, associated with a particular class
+ * loader.
+ *
+ * It's okay for size to be zero.
+ *
+ * We always leave "curOffset" pointing at the next place where we will
+ * store the header that precedes the returned storage.
+ *
+ * This aborts the VM on failure, so it's not necessary to check for a
+ * NULL return value.
+ */
+void* dvmLinearAlloc(Object* classLoader, size_t size)
+{
+ LinearAllocHdr* pHdr = getHeader(classLoader);
+ int startOffset, nextOffset;
+ int lastGoodOff, firstWriteOff, lastWriteOff;
+
+#ifdef DISABLE_LINEAR_ALLOC
+ return calloc(1, size);
+#endif
+
+ LOGVV("--- LinearAlloc(%p, %d)\n", classLoader, size);
+
+ /*
+ * What we'd like to do is just determine the new end-of-alloc size
+ * and atomic-swap the updated value in. The trouble is that, the
+ * first time we reach a new page, we need to call mprotect() to
+ * make the page available, and we don't want to call mprotect() on
+ * every allocation. The troubled situation is:
+ * - thread A allocs across a page boundary, but gets preempted
+ * before mprotect() completes
+ * - thread B allocs within the new page, and doesn't call mprotect()
+ */
+ dvmLockMutex(&pHdr->lock);
+
+ startOffset = pHdr->curOffset;
+ assert(((startOffset + HEADER_EXTRA) & (BLOCK_ALIGN-1)) == 0);
+
+ /*
+ * Compute the new offset. The old offset points at the address where
+ * we will store the hidden block header, so we advance past that,
+ * add the size of data they want, add another header's worth so we
+ * know we have room for that, and round up to BLOCK_ALIGN. That's
+ * the next location where we'll put user data. We then subtract the
+ * chunk header size off so we're back to the header pointer.
+ *
+ * Examples:
+ * old=12 size=3 new=((12+(4*2)+3+7) & ~7)-4 = 24-4 --> 20
+ * old=12 size=5 new=((12+(4*2)+5+7) & ~7)-4 = 32-4 --> 28
+ */
+ nextOffset = ((startOffset + HEADER_EXTRA*2 + size + (BLOCK_ALIGN-1))
+ & ~(BLOCK_ALIGN-1)) - HEADER_EXTRA;
+ LOGVV("--- old=%d size=%d new=%d\n", startOffset, size, nextOffset);
+
+ if (nextOffset > pHdr->mapLength) {
+ /*
+ * We don't have to abort here. We could fall back on the system
+ * malloc(), and have our "free" call figure out what to do. Only
+ * works if the users of these functions actually free everything
+ * they allocate.
+ */
+ LOGE("LinearAlloc exceeded capacity (%d), last=%d\n",
+ pHdr->mapLength, (int) size);
+ dvmAbort();
+ }
+
+ /*
+ * Round up "size" to encompass the entire region, including the 0-7
+ * pad bytes before the next chunk header. This way we get maximum
+ * utility out of "realloc", and when we're doing ENFORCE_READ_ONLY
+ * stuff we always treat the full extent.
+ */
+ size = nextOffset - (startOffset + HEADER_EXTRA);
+ LOGVV("--- (size now %d)\n", size);
+
+ /*
+ * See if we are starting on or have crossed into a new page. If so,
+ * call mprotect on the page(s) we're about to write to. We have to
+ * page-align the start address, but don't have to make the length a
+ * SYSTEM_PAGE_SIZE multiple (but we do it anyway).
+ *
+ * Note that "startOffset" is not the last *allocated* byte, but rather
+ * the offset of the first *unallocated* byte (which we are about to
+ * write the chunk header to). "nextOffset" is similar.
+ *
+ * If ENFORCE_READ_ONLY is enabled, we have to call mprotect even if
+ * we've written to this page before, because it might be read-only.
+ */
+ lastGoodOff = (startOffset-1) & ~(SYSTEM_PAGE_SIZE-1);
+ firstWriteOff = startOffset & ~(SYSTEM_PAGE_SIZE-1);
+ lastWriteOff = (nextOffset-1) & ~(SYSTEM_PAGE_SIZE-1);
+ LOGVV("--- lastGood=0x%04x firstWrite=0x%04x lastWrite=0x%04x\n",
+ lastGoodOff, firstWriteOff, lastWriteOff);
+ if (lastGoodOff != lastWriteOff || ENFORCE_READ_ONLY) {
+ int cc, start, len;
+
+ start = firstWriteOff;
+ assert(start <= nextOffset);
+ len = (lastWriteOff - firstWriteOff) + SYSTEM_PAGE_SIZE;
+
+ LOGVV("--- calling mprotect(start=%d len=%d RW)\n", start, len);
+ cc = mprotect(pHdr->mapAddr + start, len, PROT_READ | PROT_WRITE);
+ if (cc != 0) {
+ LOGE("LinearAlloc mprotect (+%d %d) failed: %s\n",
+ start, len, strerror(errno));
+ /* we're going to fail soon, might as do it now */
+ dvmAbort();
+ }
+ }
+
+ /* update the ref counts on the now-writable pages */
+ if (ENFORCE_READ_ONLY) {
+ int i, start, end;
+
+ start = firstWriteOff / SYSTEM_PAGE_SIZE;
+ end = lastWriteOff / SYSTEM_PAGE_SIZE;
+
+ LOGVV("--- marking pages %d-%d RW (alloc %d at %p)\n",
+ start, end, size, pHdr->mapAddr + startOffset + HEADER_EXTRA);
+ for (i = start; i <= end; i++)
+ pHdr->writeRefCount[i]++;
+ }
+
+ /* stow the size in the header */
+ if (ENFORCE_READ_ONLY)
+ *(u4*)(pHdr->mapAddr + startOffset) = size | LENGTHFLAG_RW;
+ else
+ *(u4*)(pHdr->mapAddr + startOffset) = size;
+
+ /*
+ * Update data structure.
+ */
+ pHdr->curOffset = nextOffset;
+
+ dvmUnlockMutex(&pHdr->lock);
+ return pHdr->mapAddr + startOffset + HEADER_EXTRA;
+}
+
+/*
+ * Helper function, replaces strdup().
+ */
+char* dvmLinearStrdup(Object* classLoader, const char* str)
+{
+#ifdef DISABLE_LINEAR_ALLOC
+ return strdup(str);
+#endif
+ int len = strlen(str);
+ void* mem = dvmLinearAlloc(classLoader, len+1);
+ memcpy(mem, str, len+1);
+ if (ENFORCE_READ_ONLY)
+ dvmLinearSetReadOnly(classLoader, mem);
+ return (char*) mem;
+}
+
+/*
+ * "Reallocate" a piece of memory.
+ *
+ * If the new size is <= the old size, we return the original pointer
+ * without doing anything.
+ *
+ * If the new size is > the old size, we allocate new storage, copy the
+ * old stuff over, and mark the new stuff as free.
+ */
+void* dvmLinearRealloc(Object* classLoader, void* mem, size_t newSize)
+{
+#ifdef DISABLE_LINEAR_ALLOC
+ return realloc(mem, newSize);
+#endif
+ /* make sure we have the right region (and mem != NULL) */
+ assert(mem != NULL);
+ assert(mem >= (void*) getHeader(classLoader)->mapAddr &&
+ mem < (void*) (getHeader(classLoader)->mapAddr +
+ getHeader(classLoader)->curOffset));
+
+ const u4* pLen = getBlockHeader(mem);
+ LOGV("--- LinearRealloc(%d) old=%d\n", newSize, *pLen);
+
+ /* handle size reduction case */
+ if (*pLen >= newSize) {
+ if (ENFORCE_READ_ONLY)
+ dvmLinearSetReadWrite(classLoader, mem);
+ return mem;
+ }
+
+ void* newMem;
+
+ newMem = dvmLinearAlloc(classLoader, newSize);
+ assert(newMem != NULL);
+ memcpy(newMem, mem, *pLen);
+ dvmLinearFree(classLoader, mem);
+
+ return newMem;
+}
+
+
+/*
+ * Update the read/write status of one or more pages.
+ */
+static void updatePages(Object* classLoader, void* mem, int direction)
+{
+ LinearAllocHdr* pHdr = getHeader(classLoader);
+ dvmLockMutex(&pHdr->lock);
+
+ /* make sure we have the right region */
+ assert(mem >= (void*) pHdr->mapAddr &&
+ mem < (void*) (pHdr->mapAddr + pHdr->curOffset));
+
+ u4* pLen = getBlockHeader(mem);
+ u4 len = *pLen & LENGTHFLAG_MASK;
+ int firstPage, lastPage;
+
+ firstPage = ((u1*)pLen - (u1*)pHdr->mapAddr) / SYSTEM_PAGE_SIZE;
+ lastPage = ((u1*)mem - (u1*)pHdr->mapAddr + (len-1)) / SYSTEM_PAGE_SIZE;
+ LOGVV("--- updating pages %d-%d (%d)\n", firstPage, lastPage, direction);
+
+ int i, cc;
+
+ /*
+ * Update individual pages. We could do some sort of "lazy update" to
+ * combine mprotect calls, but that's almost certainly more trouble
+ * than it's worth.
+ */
+ for (i = firstPage; i <= lastPage; i++) {
+ if (direction < 0) {
+ /*
+ * Trying to mark read-only.
+ */
+ if (i == firstPage) {
+ if ((*pLen & LENGTHFLAG_RW) == 0) {
+ LOGW("Double RO on %p\n", mem);
+ dvmAbort();
+ } else
+ *pLen &= ~LENGTHFLAG_RW;
+ }
+
+ if (pHdr->writeRefCount[i] == 0) {
+ LOGE("Can't make page %d any less writable\n", i);
+ dvmAbort();
+ }
+ pHdr->writeRefCount[i]--;
+ if (pHdr->writeRefCount[i] == 0) {
+ LOGVV("--- prot page %d RO\n", i);
+ cc = mprotect(pHdr->mapAddr + SYSTEM_PAGE_SIZE * i,
+ SYSTEM_PAGE_SIZE, PROT_READ);
+ assert(cc == 0);
+ }
+ } else {
+ /*
+ * Trying to mark writable.
+ */
+ if (pHdr->writeRefCount[i] >= 32767) {
+ LOGE("Can't make page %d any more writable\n", i);
+ dvmAbort();
+ }
+ if (pHdr->writeRefCount[i] == 0) {
+ LOGVV("--- prot page %d RW\n", i);
+ cc = mprotect(pHdr->mapAddr + SYSTEM_PAGE_SIZE * i,
+ SYSTEM_PAGE_SIZE, PROT_READ | PROT_WRITE);
+ assert(cc == 0);
+ }
+ pHdr->writeRefCount[i]++;
+
+ if (i == firstPage) {
+ if ((*pLen & LENGTHFLAG_RW) != 0) {
+ LOGW("Double RW on %p\n", mem);
+ dvmAbort();
+ } else
+ *pLen |= LENGTHFLAG_RW;
+ }
+ }
+ }
+
+ dvmUnlockMutex(&pHdr->lock);
+}
+
+/*
+ * Try to mark the pages in which a chunk of memory lives as read-only.
+ * Whether or not the pages actually change state depends on how many
+ * others are trying to access the same pages.
+ *
+ * Only call here if ENFORCE_READ_ONLY is true.
+ */
+void dvmLinearSetReadOnly(Object* classLoader, void* mem)
+{
+#ifdef DISABLE_LINEAR_ALLOC
+ return;
+#endif
+ updatePages(classLoader, mem, -1);
+}
+
+/*
+ * Make the pages on which "mem" sits read-write.
+ *
+ * This covers the header as well as the data itself. (We could add a
+ * "header-only" mode for dvmLinearFree.)
+ *
+ * Only call here if ENFORCE_READ_ONLY is true.
+ */
+void dvmLinearSetReadWrite(Object* classLoader, void* mem)
+{
+#ifdef DISABLE_LINEAR_ALLOC
+ return;
+#endif
+ updatePages(classLoader, mem, 1);
+}
+
+/*
+ * Mark an allocation as free.
+ */
+void dvmLinearFree(Object* classLoader, void* mem)
+{
+#ifdef DISABLE_LINEAR_ALLOC
+ free(mem);
+ return;
+#endif
+ if (mem == NULL)
+ return;
+
+ /* make sure we have the right region */
+ assert(mem >= (void*) getHeader(classLoader)->mapAddr &&
+ mem < (void*) (getHeader(classLoader)->mapAddr +
+ getHeader(classLoader)->curOffset));
+
+ if (ENFORCE_READ_ONLY)
+ dvmLinearSetReadWrite(classLoader, mem);
+
+ u4* pLen = getBlockHeader(mem);
+ *pLen |= LENGTHFLAG_FREE;
+
+ if (ENFORCE_READ_ONLY)
+ dvmLinearSetReadOnly(classLoader, mem);
+}
+
+/*
+ * For debugging, dump the contents of a linear alloc area.
+ *
+ * We grab the lock so that the header contents and list output are
+ * consistent.
+ */
+void dvmLinearAllocDump(Object* classLoader)
+{
+#ifdef DISABLE_LINEAR_ALLOC
+ return;
+#endif
+ LinearAllocHdr* pHdr = getHeader(classLoader);
+
+ dvmLockMutex(&pHdr->lock);
+
+ LOGI("LinearAlloc classLoader=%p\n", classLoader);
+ LOGI(" mapAddr=%p mapLength=%d firstOffset=%d\n",
+ pHdr->mapAddr, pHdr->mapLength, pHdr->firstOffset);
+ LOGI(" curOffset=%d\n", pHdr->curOffset);
+
+ int off = pHdr->firstOffset;
+ u4 rawLen, fullLen;
+
+ while (off < pHdr->curOffset) {
+ rawLen = *(u4*) (pHdr->mapAddr + off);
+ fullLen = ((HEADER_EXTRA*2 + (rawLen & LENGTHFLAG_MASK))
+ & ~(BLOCK_ALIGN-1));
+
+ LOGI(" %p (%3d): %clen=%d%s\n", pHdr->mapAddr + off + HEADER_EXTRA,
+ (int) ((off + HEADER_EXTRA) / SYSTEM_PAGE_SIZE),
+ (rawLen & LENGTHFLAG_FREE) != 0 ? '*' : ' ',
+ rawLen & LENGTHFLAG_MASK,
+ (rawLen & LENGTHFLAG_RW) != 0 ? " [RW]" : "");
+
+ off += fullLen;
+ }
+
+ if (ENFORCE_READ_ONLY) {
+ LOGI("writeRefCount map:\n");
+
+ int numPages = (pHdr->mapLength+SYSTEM_PAGE_SIZE-1) / SYSTEM_PAGE_SIZE;
+ int zstart = 0;
+ int i;
+
+ for (i = 0; i < numPages; i++) {
+ int count = pHdr->writeRefCount[i];
+
+ if (count != 0) {
+ if (zstart < i-1)
+ printf(" %d-%d: zero\n", zstart, i-1);
+ else if (zstart == i-1)
+ printf(" %d: zero\n", zstart);
+ zstart = i+1;
+ printf(" %d: %d\n", i, count);
+ }
+ }
+ if (zstart < i)
+ printf(" %d-%d: zero\n", zstart, i-1);
+ }
+
+ LOGD("LinearAlloc %p using %d of %d (%d%%)\n",
+ classLoader, pHdr->curOffset, pHdr->mapLength,
+ (pHdr->curOffset * 100) / pHdr->mapLength);
+
+ dvmUnlockMutex(&pHdr->lock);
+}
+
+/*
+ * Verify that all blocks are freed.
+ *
+ * This should only be done as we're shutting down, but there could be a
+ * daemon thread that's still trying to do something, so we grab the locks.
+ */
+static void checkAllFree(Object* classLoader)
+{
+#ifdef DISABLE_LINEAR_ALLOC
+ return;
+#endif
+ LinearAllocHdr* pHdr = getHeader(classLoader);
+
+ dvmLockMutex(&pHdr->lock);
+
+ int off = pHdr->firstOffset;
+ u4 rawLen, fullLen;
+
+ while (off < pHdr->curOffset) {
+ rawLen = *(u4*) (pHdr->mapAddr + off);
+ fullLen = ((HEADER_EXTRA*2 + (rawLen & LENGTHFLAG_MASK))
+ & ~(BLOCK_ALIGN-1));
+
+ if ((rawLen & LENGTHFLAG_FREE) == 0) {
+ LOGW("LinearAlloc %p not freed: %p len=%d\n", classLoader,
+ pHdr->mapAddr + off + HEADER_EXTRA, rawLen & LENGTHFLAG_MASK);
+ }
+
+ off += fullLen;
+ }
+
+ dvmUnlockMutex(&pHdr->lock);
+}
+
+/*
+ * Determine if [start, start+length) is contained in the in-use area of
+ * a single LinearAlloc. The full set of linear allocators is scanned.
+ *
+ * [ Since we currently only have one region, this is pretty simple. In
+ * the future we'll need to traverse a table of class loaders. ]
+ */
+bool dvmLinearAllocContains(const void* start, size_t length)
+{
+ LinearAllocHdr* pHdr = getHeader(NULL);
+
+ if (pHdr == NULL)
+ return false;
+
+ return (char*) start >= pHdr->mapAddr &&
+ ((char*)start + length) <= (pHdr->mapAddr + pHdr->curOffset);
+}
diff --git a/vm/LinearAlloc.h b/vm/LinearAlloc.h
new file mode 100644
index 0000000..aa33fe1
--- /dev/null
+++ b/vm/LinearAlloc.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Simple linear memory allocator.
+ */
+#ifndef _DALVIK_LINEARALLOC
+#define _DALVIK_LINEARALLOC
+
+/*
+ * If this is set, we create additional data structures and make many
+ * additional mprotect() calls.
+ */
+#define ENFORCE_READ_ONLY false
+
+/*
+ * Linear allocation state. We could tuck this into the start of the
+ * allocated region, but that would prevent us from sharing the rest of
+ * that first page.
+ */
+typedef struct LinearAllocHdr {
+ int curOffset; /* offset where next data goes */
+ pthread_mutex_t lock; /* controls updates to this struct */
+
+ char* mapAddr; /* start of mmap()ed region */
+ int mapLength; /* length of region */
+ int firstOffset; /* for chasing through */
+
+ short* writeRefCount; /* for ENFORCE_READ_ONLY */
+} LinearAllocHdr;
+
+
+/*
+ * Create a new alloc region.
+ */
+LinearAllocHdr* dvmLinearAllocCreate(Object* classLoader);
+
+/*
+ * Destroy a region.
+ */
+void dvmLinearAllocDestroy(Object* classLoader);
+
+/*
+ * Allocate a chunk of memory. The memory will be zeroed out.
+ *
+ * For ENFORCE_READ_ONLY, call dvmLinearReadOnly on the result.
+ */
+void* dvmLinearAlloc(Object* classLoader, size_t size);
+
+/*
+ * Reallocate a chunk. The original storage is not released, but may be
+ * erased to aid debugging.
+ *
+ * For ENFORCE_READ_ONLY, call dvmLinearReadOnly on the result. Also, the
+ * caller should probably mark the "mem" argument read-only before calling.
+ */
+void* dvmLinearRealloc(Object* classLoader, void* mem, size_t newSize);
+
+/* don't call these directly */
+void dvmLinearSetReadOnly(Object* classLoader, void* mem);
+void dvmLinearSetReadWrite(Object* classLoader, void* mem);
+
+/*
+ * Mark a chunk of memory from Alloc or Realloc as read-only. This must
+ * be done after all changes to the block of memory have been made. This
+ * actually operates on a page granularity.
+ */
+INLINE void dvmLinearReadOnly(Object* classLoader, void* mem)
+{
+ if (ENFORCE_READ_ONLY && mem != NULL)
+ dvmLinearSetReadOnly(classLoader, mem);
+}
+
+/*
+ * Make a chunk of memory writable again.
+ */
+INLINE void dvmLinearReadWrite(Object* classLoader, void* mem)
+{
+ if (ENFORCE_READ_ONLY && mem != NULL)
+ dvmLinearSetReadWrite(classLoader, mem);
+}
+
+/*
+ * Free a chunk. Does not increase available storage, but the freed area
+ * may be erased to aid debugging.
+ */
+void dvmLinearFree(Object* classLoader, void* mem);
+
+/*
+ * Helper function; allocates new storage and copies "str" into it.
+ *
+ * For ENFORCE_READ_ONLY, do *not* call dvmLinearReadOnly on the result.
+ * This is done automatically.
+ */
+char* dvmLinearStrdup(Object* classLoader, const char* str);
+
+/*
+ * Dump the contents of a linear alloc area.
+ */
+void dvmLinearAllocDump(Object* classLoader);
+
+/*
+ * Determine if [start, start+length) is contained in the in-use area of
+ * a single LinearAlloc. The full set of linear allocators is scanned.
+ */
+bool dvmLinearAllocContains(const void* start, size_t length);
+
+#endif /*_DALVIK_LINEARALLOC*/
diff --git a/vm/Misc.c b/vm/Misc.c
new file mode 100644
index 0000000..93bc7f4
--- /dev/null
+++ b/vm/Misc.c
@@ -0,0 +1,738 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Miscellaneous utility functions.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <strings.h>
+#include <ctype.h>
+#include <time.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <cutils/ashmem.h>
+#include <sys/mman.h>
+
+#define ALIGN_UP_TO_PAGE_SIZE(p) \
+ (((size_t)(p) + (SYSTEM_PAGE_SIZE - 1)) & ~(SYSTEM_PAGE_SIZE - 1))
+
+/*
+ * Print a hex dump in this format:
+ *
+01234567: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff 0123456789abcdef\n
+ *
+ * If "mode" is kHexDumpLocal, we start at offset zero, and show a full
+ * 16 bytes on the first line. If it's kHexDumpMem, we make this look
+ * like a memory dump, using the actual address, outputting a partial line
+ * if "vaddr" isn't aligned on a 16-byte boundary.
+ *
+ * "priority" and "tag" determine the values passed to the log calls.
+ *
+ * Does not use printf() or other string-formatting calls.
+ */
+void dvmPrintHexDumpEx(int priority, const char* tag, const void* vaddr,
+ size_t length, HexDumpMode mode)
+{
+ static const char gHexDigit[] = "0123456789abcdef";
+ const unsigned char* addr = vaddr;
+ char out[77]; /* exact fit */
+ unsigned int offset; /* offset to show while printing */
+ char* hex;
+ char* asc;
+ int gap;
+ //int trickle = 0;
+
+ if (mode == kHexDumpLocal)
+ offset = 0;
+ else
+ offset = (int) addr;
+
+ memset(out, ' ', sizeof(out)-1);
+ out[8] = ':';
+ out[sizeof(out)-2] = '\n';
+ out[sizeof(out)-1] = '\0';
+
+ gap = (int) offset & 0x0f;
+ while (length) {
+ unsigned int lineOffset = offset & ~0x0f;
+ int i, count;
+
+ hex = out;
+ asc = out + 59;
+
+ for (i = 0; i < 8; i++) {
+ *hex++ = gHexDigit[lineOffset >> 28];
+ lineOffset <<= 4;
+ }
+ hex++;
+ hex++;
+
+ count = ((int)length > 16-gap) ? 16-gap : (int)length; /* cap length */
+ assert(count != 0);
+ assert(count+gap <= 16);
+
+ if (gap) {
+ /* only on first line */
+ hex += gap * 3;
+ asc += gap;
+ }
+
+ for (i = gap ; i < count+gap; i++) {
+ *hex++ = gHexDigit[*addr >> 4];
+ *hex++ = gHexDigit[*addr & 0x0f];
+ hex++;
+ if (*addr >= 0x20 && *addr < 0x7f /*isprint(*addr)*/)
+ *asc++ = *addr;
+ else
+ *asc++ = '.';
+ addr++;
+ }
+ for ( ; i < 16; i++) {
+ /* erase extra stuff; only happens on last line */
+ *hex++ = ' ';
+ *hex++ = ' ';
+ hex++;
+ *asc++ = ' ';
+ }
+
+ LOG_PRI(priority, tag, "%s", out);
+#if 0 //def HAVE_ANDROID_OS
+ /*
+ * We can overrun logcat easily by writing at full speed. On the
+ * other hand, we can make Eclipse time out if we're showing
+ * packet dumps while debugging JDWP.
+ */
+ {
+ if (trickle++ == 8) {
+ trickle = 0;
+ usleep(20000);
+ }
+ }
+#endif
+
+ gap = 0;
+ length -= count;
+ offset += count;
+ }
+}
+
+
+/*
+ * Fill out a DebugOutputTarget, suitable for printing to the log.
+ */
+void dvmCreateLogOutputTarget(DebugOutputTarget* target, int priority,
+ const char* tag)
+{
+ assert(target != NULL);
+ assert(tag != NULL);
+
+ target->which = kDebugTargetLog;
+ target->data.log.priority = priority;
+ target->data.log.tag = tag;
+}
+
+/*
+ * Fill out a DebugOutputTarget suitable for printing to a file pointer.
+ */
+void dvmCreateFileOutputTarget(DebugOutputTarget* target, FILE* fp)
+{
+ assert(target != NULL);
+ assert(fp != NULL);
+
+ target->which = kDebugTargetFile;
+ target->data.file.fp = fp;
+}
+
+/*
+ * Free "target" and any associated data.
+ */
+void dvmFreeOutputTarget(DebugOutputTarget* target)
+{
+ free(target);
+}
+
+/*
+ * Print a debug message, to either a file or the log.
+ */
+void dvmPrintDebugMessage(const DebugOutputTarget* target, const char* format,
+ ...)
+{
+ va_list args;
+
+ va_start(args, format);
+
+ switch (target->which) {
+ case kDebugTargetLog:
+ LOG_PRI_VA(target->data.log.priority, target->data.log.tag,
+ format, args);
+ break;
+ case kDebugTargetFile:
+ vfprintf(target->data.file.fp, format, args);
+ break;
+ default:
+ LOGE("unexpected 'which' %d\n", target->which);
+ break;
+ }
+
+ va_end(args);
+}
+
+
+/*
+ * Allocate a bit vector with enough space to hold at least the specified
+ * number of bits.
+ */
+BitVector* dvmAllocBitVector(int startBits, bool expandable)
+{
+ BitVector* bv;
+ int count;
+
+ assert(sizeof(bv->storage[0]) == 4); /* assuming 32-bit units */
+ assert(startBits >= 0);
+
+ bv = (BitVector*) malloc(sizeof(BitVector));
+
+ count = (startBits + 31) >> 5;
+
+ bv->storageSize = count;
+ bv->expandable = expandable;
+ bv->storage = (u4*) malloc(count * sizeof(u4));
+ memset(bv->storage, 0x00, count * sizeof(u4));
+ return bv;
+}
+
+/*
+ * Free a BitVector.
+ */
+void dvmFreeBitVector(BitVector* pBits)
+{
+ if (pBits == NULL)
+ return;
+
+ free(pBits->storage);
+ free(pBits);
+}
+
+/*
+ * "Allocate" the first-available bit in the bitmap.
+ *
+ * This is not synchronized. The caller is expected to hold some sort of
+ * lock that prevents multiple threads from executing simultaneously in
+ * dvmAllocBit/dvmFreeBit.
+ */
+int dvmAllocBit(BitVector* pBits)
+{
+ int word, bit;
+
+retry:
+ for (word = 0; word < pBits->storageSize; word++) {
+ if (pBits->storage[word] != 0xffffffff) {
+ /*
+ * There are unallocated bits in this word. Return the first.
+ */
+ bit = ffs(~(pBits->storage[word])) -1;
+ assert(bit >= 0 && bit < 32);
+ pBits->storage[word] |= 1 << bit;
+ return (word << 5) | bit;
+ }
+ }
+
+ /*
+ * Ran out of space, allocate more if we're allowed to.
+ */
+ if (!pBits->expandable)
+ return -1;
+
+ pBits->storage = realloc(pBits->storage,
+ (pBits->storageSize + kBitVectorGrowth) * sizeof(u4));
+ memset(&pBits->storage[pBits->storageSize], 0x00,
+ kBitVectorGrowth * sizeof(u4));
+ pBits->storageSize += kBitVectorGrowth;
+ goto retry;
+}
+
+/*
+ * Mark the specified bit as "set".
+ *
+ * Returns "false" if the bit is outside the range of the vector and we're
+ * not allowed to expand.
+ */
+bool dvmSetBit(BitVector* pBits, int num)
+{
+ assert(num >= 0);
+ if (num >= pBits->storageSize * (int)sizeof(u4) * 8) {
+ if (!pBits->expandable)
+ return false;
+
+ int newSize = (num + 31) >> 5;
+ assert(newSize > pBits->storageSize);
+ pBits->storage = realloc(pBits->storage, newSize * sizeof(u4));
+ memset(&pBits->storage[pBits->storageSize], 0x00,
+ (newSize - pBits->storageSize) * sizeof(u4));
+ pBits->storageSize = newSize;
+ }
+
+ pBits->storage[num >> 5] |= 1 << (num & 0x1f);
+ return true;
+}
+
+/*
+ * Mark the specified bit as "clear".
+ */
+void dvmClearBit(BitVector* pBits, int num)
+{
+ assert(num >= 0 && num < (int) pBits->storageSize * (int)sizeof(u4) * 8);
+
+ pBits->storage[num >> 5] &= ~(1 << (num & 0x1f));
+}
+
+/*
+ * Mark all bits bit as "clear".
+ */
+void dvmClearAllBits(BitVector* pBits)
+{
+ int count = pBits->storageSize;
+ memset(pBits->storage, 0, count * sizeof(u4));
+}
+
+/*
+ * Determine whether or not the specified bit is set.
+ */
+bool dvmIsBitSet(const BitVector* pBits, int num)
+{
+ assert(num >= 0 && num < (int) pBits->storageSize * (int)sizeof(u4) * 8);
+
+ int val = pBits->storage[num >> 5] & (1 << (num & 0x1f));
+ return (val != 0);
+}
+
+/*
+ * Count the number of bits that are set.
+ */
+int dvmCountSetBits(const BitVector* pBits)
+{
+ int word;
+ int count = 0;
+
+ for (word = 0; word < pBits->storageSize; word++) {
+ u4 val = pBits->storage[word];
+
+ if (val != 0) {
+ if (val == 0xffffffff) {
+ count += 32;
+ } else {
+ /* count the number of '1' bits */
+ while (val != 0) {
+ val &= val - 1;
+ count++;
+ }
+ }
+ }
+ }
+
+ return count;
+}
+
+/*
+ * Copy a whole vector to the other. Only do that when the both vectors have
+ * the same size and attribute.
+ */
+bool dvmCopyBitVector(BitVector *dest, const BitVector *src)
+{
+ if (dest->storageSize != src->storageSize ||
+ dest->expandable != src->expandable)
+ return false;
+ memcpy(dest->storage, src->storage, sizeof(u4) * dest->storageSize);
+ return true;
+}
+
+/*
+ * Intersect two bit vectores and merge the result on top of the pre-existing
+ * value in the dest vector.
+ */
+bool dvmIntersectBitVectors(BitVector *dest, const BitVector *src1,
+ const BitVector *src2)
+{
+ if (dest->storageSize != src1->storageSize ||
+ dest->storageSize != src2->storageSize ||
+ dest->expandable != src1->expandable ||
+ dest->expandable != src2->expandable)
+ return false;
+
+ int i;
+ for (i = 0; i < dest->storageSize; i++) {
+ dest->storage[i] |= src1->storage[i] & src2->storage[i];
+ }
+ return true;
+}
+
+/*
+ * Return a newly-allocated string in which all occurrences of '.' have
+ * been changed to '/'. If we find a '/' in the original string, NULL
+ * is returned to avoid ambiguity.
+ */
+char* dvmDotToSlash(const char* str)
+{
+ char* newStr = strdup(str);
+ char* cp = newStr;
+
+ if (newStr == NULL)
+ return NULL;
+
+ while (*cp != '\0') {
+ if (*cp == '/') {
+ assert(false);
+ return NULL;
+ }
+ if (*cp == '.')
+ *cp = '/';
+ cp++;
+ }
+
+ return newStr;
+}
+
+/*
+ * Return a newly-allocated string for the "dot version" of the class
+ * name for the given type descriptor. That is, The initial "L" and
+ * final ";" (if any) have been removed and all occurrences of '/'
+ * have been changed to '.'.
+ */
+char* dvmDescriptorToDot(const char* str)
+{
+ size_t at = strlen(str);
+ char* newStr;
+
+ if ((at >= 2) && (str[0] == 'L') && (str[at - 1] == ';')) {
+ at -= 2; /* Two fewer chars to copy. */
+ str++; /* Skip the 'L'. */
+ }
+
+ newStr = malloc(at + 1); /* Add one for the '\0'. */
+ if (newStr == NULL)
+ return NULL;
+
+ newStr[at] = '\0';
+
+ while (at > 0) {
+ at--;
+ newStr[at] = (str[at] == '/') ? '.' : str[at];
+ }
+
+ return newStr;
+}
+
+/*
+ * Return a newly-allocated string for the type descriptor
+ * corresponding to the "dot version" of the given class name. That
+ * is, non-array names are surrounded by "L" and ";", and all
+ * occurrences of '.' are changed to '/'.
+ */
+char* dvmDotToDescriptor(const char* str)
+{
+ size_t length = strlen(str);
+ int wrapElSemi = 0;
+ char* newStr;
+ char* at;
+
+ if (str[0] != '[') {
+ length += 2; /* for "L" and ";" */
+ wrapElSemi = 1;
+ }
+
+ newStr = at = malloc(length + 1); /* + 1 for the '\0' */
+
+ if (newStr == NULL) {
+ return NULL;
+ }
+
+ if (wrapElSemi) {
+ *(at++) = 'L';
+ }
+
+ while (*str) {
+ char c = *(str++);
+ if (c == '.') {
+ c = '/';
+ }
+ *(at++) = c;
+ }
+
+ if (wrapElSemi) {
+ *(at++) = ';';
+ }
+
+ *at = '\0';
+ return newStr;
+}
+
+/*
+ * Return a newly-allocated string for the internal-form class name for
+ * the given type descriptor. That is, the initial "L" and final ";" (if
+ * any) have been removed.
+ */
+char* dvmDescriptorToName(const char* str)
+{
+ if (str[0] == 'L') {
+ size_t length = strlen(str) - 1;
+ char* newStr = malloc(length);
+
+ if (newStr == NULL) {
+ return NULL;
+ }
+
+ strlcpy(newStr, str + 1, length);
+ return newStr;
+ }
+
+ return strdup(str);
+}
+
+/*
+ * Return a newly-allocated string for the type descriptor for the given
+ * internal-form class name. That is, a non-array class name will get
+ * surrounded by "L" and ";", while array names are left as-is.
+ */
+char* dvmNameToDescriptor(const char* str)
+{
+ if (str[0] != '[') {
+ size_t length = strlen(str);
+ char* descriptor = malloc(length + 3);
+
+ if (descriptor == NULL) {
+ return NULL;
+ }
+
+ descriptor[0] = 'L';
+ strcpy(descriptor + 1, str);
+ descriptor[length + 1] = ';';
+ descriptor[length + 2] = '\0';
+
+ return descriptor;
+ }
+
+ return strdup(str);
+}
+
+/*
+ * Get a notion of the current time, in nanoseconds. This is meant for
+ * computing durations (e.g. "operation X took 52nsec"), so the result
+ * should not be used to get the current date/time.
+ */
+u8 dvmGetRelativeTimeNsec(void)
+{
+#ifdef HAVE_POSIX_CLOCKS
+ struct timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ return (u8)now.tv_sec*1000000000LL + now.tv_nsec;
+#else
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ return (u8)now.tv_sec*1000000000LL + now.tv_usec * 1000LL;
+#endif
+}
+
+/*
+ * Get the per-thread CPU time, in nanoseconds.
+ *
+ * Only useful for time deltas.
+ */
+u8 dvmGetThreadCpuTimeNsec(void)
+{
+#ifdef HAVE_POSIX_CLOCKS
+ struct timespec now;
+ clock_gettime(CLOCK_THREAD_CPUTIME_ID, &now);
+ return (u8)now.tv_sec*1000000000LL + now.tv_nsec;
+#else
+ return (u8) -1;
+#endif
+}
+
+/*
+ * Get the per-thread CPU time, in nanoseconds, for the specified thread.
+ */
+u8 dvmGetOtherThreadCpuTimeNsec(pthread_t thread)
+{
+#if 0 /*def HAVE_POSIX_CLOCKS*/
+ int clockId;
+
+ if (pthread_getcpuclockid(thread, &clockId) != 0)
+ return (u8) -1;
+
+ struct timespec now;
+ clock_gettime(clockId, &now);
+ return (u8)now.tv_sec*1000000000LL + now.tv_nsec;
+#else
+ return (u8) -1;
+#endif
+}
+
+
+/*
+ * Call this repeatedly, with successively higher values for "iteration",
+ * to sleep for a period of time not to exceed "maxTotalSleep".
+ *
+ * For example, when called with iteration==0 we will sleep for a very
+ * brief time. On the next call we will sleep for a longer time. When
+ * the sum total of all sleeps reaches "maxTotalSleep", this returns false.
+ *
+ * The initial start time value for "relStartTime" MUST come from the
+ * dvmGetRelativeTimeUsec call. On the device this must come from the
+ * monotonic clock source, not the wall clock.
+ *
+ * This should be used wherever you might be tempted to call sched_yield()
+ * in a loop. The problem with sched_yield is that, for a high-priority
+ * thread, the kernel might not actually transfer control elsewhere.
+ *
+ * Returns "false" if we were unable to sleep because our time was up.
+ */
+bool dvmIterativeSleep(int iteration, int maxTotalSleep, u8 relStartTime)
+{
+ const int minSleep = 10000;
+ u8 curTime;
+ int curDelay;
+
+ /*
+ * Get current time, and see if we've already exceeded the limit.
+ */
+ curTime = dvmGetRelativeTimeUsec();
+ if (curTime >= relStartTime + maxTotalSleep) {
+ LOGVV("exsl: sleep exceeded (start=%llu max=%d now=%llu)\n",
+ relStartTime, maxTotalSleep, curTime);
+ return false;
+ }
+
+ /*
+ * Compute current delay. We're bounded by "maxTotalSleep", so no
+ * real risk of overflow assuming "usleep" isn't returning early.
+ * (Besides, 2^30 usec is about 18 minutes by itself.)
+ *
+ * For iteration==0 we just call sched_yield(), so the first sleep
+ * at iteration==1 is actually (minSleep * 2).
+ */
+ curDelay = minSleep;
+ while (iteration-- > 0)
+ curDelay *= 2;
+ assert(curDelay > 0);
+
+ if (curTime + curDelay >= relStartTime + maxTotalSleep) {
+ LOGVV("exsl: reduced delay from %d to %d\n",
+ curDelay, (int) ((relStartTime + maxTotalSleep) - curTime));
+ curDelay = (int) ((relStartTime + maxTotalSleep) - curTime);
+ }
+
+ if (iteration == 0) {
+ LOGVV("exsl: yield\n");
+ sched_yield();
+ } else {
+ LOGVV("exsl: sleep for %d\n", curDelay);
+ usleep(curDelay);
+ }
+ return true;
+}
+
+
+/*
+ * Set the "close on exec" flag so we don't expose our file descriptors
+ * to processes launched by us.
+ */
+bool dvmSetCloseOnExec(int fd)
+{
+ int flags;
+
+ /*
+ * There's presently only one flag defined, so getting the previous
+ * value of the fd flags is probably unnecessary.
+ */
+ flags = fcntl(fd, F_GETFD);
+ if (flags < 0) {
+ LOGW("Unable to get fd flags for fd %d\n", fd);
+ return false;
+ }
+ if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
+ LOGW("Unable to set close-on-exec for fd %d\n", fd);
+ return false;
+ }
+ return true;
+}
+
+#if (!HAVE_STRLCPY)
+/* Implementation of strlcpy() for platforms that don't already have it. */
+size_t strlcpy(char *dst, const char *src, size_t size) {
+ size_t srcLength = strlen(src);
+ size_t copyLength = srcLength;
+
+ if (srcLength > (size - 1)) {
+ copyLength = size - 1;
+ }
+
+ if (size != 0) {
+ strncpy(dst, src, copyLength);
+ dst[copyLength] = '\0';
+ }
+
+ return srcLength;
+}
+#endif
+
+/*
+ * Allocates a memory region using ashmem and mmap, initialized to
+ * zero. Actual allocation rounded up to page multiple. Returns
+ * NULL on failure.
+ */
+void *dvmAllocRegion(size_t size, int prot, const char *name) {
+ void *base;
+ int fd, ret;
+
+ size = ALIGN_UP_TO_PAGE_SIZE(size);
+ fd = ashmem_create_region(name, size);
+ if (fd == -1) {
+ return NULL;
+ }
+ base = mmap(NULL, size, prot, MAP_PRIVATE, fd, 0);
+ ret = close(fd);
+ if (base == MAP_FAILED) {
+ return NULL;
+ }
+ if (ret == -1) {
+ return NULL;
+ }
+ return base;
+}
+
+/* documented in header file */
+const char* dvmPathToAbsolutePortion(const char* path) {
+ if (path == NULL) {
+ return NULL;
+ }
+
+ if (path[0] == '/') {
+ /* It's a regular absolute path. Return it. */
+ return path;
+ }
+
+ const char* sentinel = strstr(path, "/./");
+
+ if (sentinel != NULL) {
+ /* It's got the sentinel. Return a pointer to the second slash. */
+ return sentinel + 2;
+ }
+
+ return NULL;
+}
diff --git a/vm/Misc.h b/vm/Misc.h
new file mode 100644
index 0000000..fd3c531
--- /dev/null
+++ b/vm/Misc.h
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Miscellaneous utility functions.
+ */
+#ifndef _DALVIK_MISC
+#define _DALVIK_MISC
+
+#include "Inlines.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+/*
+ * Used to shut up the compiler when a parameter isn't used.
+ */
+#define UNUSED_PARAMETER(p) (void)(p)
+
+/*
+ * Floating point conversion functions. These are necessary to avoid
+ * strict-aliasing problems ("dereferencing type-punned pointer will break
+ * strict-aliasing rules"). According to the gcc info page, this usage
+ * is allowed, even with "-fstrict-aliasing".
+ *
+ * The code generated by gcc-4.1.1 appears to be much better than a
+ * type cast dereference ("int foo = *(int*)&myfloat") when the conversion
+ * function is inlined. It also allows us to take advantage of the
+ * optimizations that strict aliasing rules allow.
+ */
+INLINE float dvmU4ToFloat(u4 val) {
+ union { u4 in; float out; } conv;
+ conv.in = val;
+ return conv.out;
+}
+INLINE u4 dvmFloatToU4(float val) {
+ union { float in; u4 out; } conv;
+ conv.in = val;
+ return conv.out;
+}
+
+/*
+ * Print a hex dump to the log file.
+ *
+ * "local" mode prints a hex dump starting from offset 0 (roughly equivalent
+ * to "xxd -g1").
+ *
+ * "mem" mode shows the actual memory address, and will offset the start
+ * so that the low nibble of the address is always zero.
+ *
+ * If "tag" is NULL the default tag ("dalvikvm") will be used.
+ */
+typedef enum { kHexDumpLocal, kHexDumpMem } HexDumpMode;
+void dvmPrintHexDumpEx(int priority, const char* tag, const void* vaddr,
+ size_t length, HexDumpMode mode);
+
+/*
+ * Print a hex dump, at INFO level.
+ */
+INLINE void dvmPrintHexDump(const void* vaddr, size_t length) {
+ dvmPrintHexDumpEx(ANDROID_LOG_INFO, LOG_TAG,
+ vaddr, length, kHexDumpLocal);
+}
+
+/*
+ * Print a hex dump at VERBOSE level. This does nothing in non-debug builds.
+ */
+INLINE void dvmPrintHexDumpDbg(const void* vaddr, size_t length,const char* tag)
+{
+#if !LOG_NDEBUG
+ dvmPrintHexDumpEx(ANDROID_LOG_VERBOSE, (tag != NULL) ? tag : LOG_TAG,
+ vaddr, length, kHexDumpLocal);
+#endif
+}
+
+/*
+ * We pass one of these around when we want code to be able to write debug
+ * info to either the log or to a file (or stdout/stderr).
+ */
+typedef struct DebugOutputTarget {
+ /* where to? */
+ enum {
+ kDebugTargetUnknown = 0,
+ kDebugTargetLog,
+ kDebugTargetFile,
+ } which;
+
+ /* additional bits */
+ union {
+ struct {
+ int priority;
+ const char* tag;
+ } log;
+ struct {
+ FILE* fp;
+ } file;
+ } data;
+} DebugOutputTarget;
+
+/*
+ * Fill in a DebugOutputTarget struct.
+ */
+void dvmCreateLogOutputTarget(DebugOutputTarget* target, int priority,
+ const char* tag);
+void dvmCreateFileOutputTarget(DebugOutputTarget* target, FILE* fp);
+
+/*
+ * Print a debug message.
+ */
+void dvmPrintDebugMessage(const DebugOutputTarget* target, const char* format,
+ ...)
+#if defined(__GNUC__)
+ __attribute__ ((format(printf, 2, 3)))
+#endif
+ ;
+
+
+/*
+ * Expanding bitmap, used for tracking resources. Bits are numbered starting
+ * from zero.
+ *
+ * All operations on a BitVector are unsynchronized.
+ */
+typedef struct BitVector {
+ bool expandable; /* expand bitmap if we run out? */
+ int storageSize; /* current size, in 32-bit words */
+ u4* storage;
+} BitVector;
+
+/* allocate a bit vector with enough space to hold "startBits" bits */
+BitVector* dvmAllocBitVector(int startBits, bool expandable);
+void dvmFreeBitVector(BitVector* pBits);
+
+/*
+ * dvmAllocBit always allocates the first possible bit. If we run out of
+ * space in the bitmap, and it's not marked expandable, dvmAllocBit
+ * returns -1.
+ *
+ * dvmSetBit sets the specified bit, expanding the vector if necessary
+ * (and possible).
+ *
+ * dvmIsBitSet returns "true" if the bit is set.
+ */
+int dvmAllocBit(BitVector* pBits);
+bool dvmSetBit(BitVector* pBits, int num);
+void dvmClearBit(BitVector* pBits, int num);
+void dvmClearAllBits(BitVector* pBits);
+bool dvmIsBitSet(const BitVector* pBits, int num);
+
+/* count the number of bits that have been set */
+int dvmCountSetBits(const BitVector* pBits);
+
+/* copy one vector to the other compatible one */
+bool dvmCopyBitVector(BitVector *dest, const BitVector *src);
+
+/*
+ * Intersect two bit vectores and merge the result on top of the pre-existing
+ * value in the dest vector.
+ */
+bool dvmIntersectBitVectors(BitVector *dest, const BitVector *src1,
+ const BitVector *src2);
+
+#define kBitVectorGrowth 4 /* increase by 4 u4s when limit hit */
+
+
+/*
+ * Return a newly-allocated string in which all occurrences of '.' have
+ * been changed to '/'. If we find a '/' in the original string, NULL
+ * is returned to avoid ambiguity.
+ */
+char* dvmDotToSlash(const char* str);
+
+/*
+ * Return a newly-allocated string for the "dot version" of the class
+ * name for the given type descriptor. That is, The initial "L" and
+ * final ";" (if any) have been removed and all occurrences of '/'
+ * have been changed to '.'.
+ */
+char* dvmDescriptorToDot(const char* str);
+
+/*
+ * Return a newly-allocated string for the type descriptor
+ * corresponding to the "dot version" of the given class name. That
+ * is, non-array names are surrounde by "L" and ";", and all
+ * occurrences of '.' have been changed to '/'.
+ */
+char* dvmDotToDescriptor(const char* str);
+
+/*
+ * Return a newly-allocated string for the internal-form class name for
+ * the given type descriptor. That is, the initial "L" and final ";" (if
+ * any) have been removed.
+ */
+char* dvmDescriptorToName(const char* str);
+
+/*
+ * Return a newly-allocated string for the type descriptor for the given
+ * internal-form class name. That is, a non-array class name will get
+ * surrounded by "L" and ";", while array names are left as-is.
+ */
+char* dvmNameToDescriptor(const char* str);
+
+/*
+ * Get the current time, in nanoseconds. This is "relative" time, meaning
+ * it could be wall-clock time or a monotonic counter, and is only suitable
+ * for computing time deltas.
+ */
+u8 dvmGetRelativeTimeNsec(void);
+
+/*
+ * Get the current time, in microseconds. This is "relative" time, meaning
+ * it could be wall-clock time or a monotonic counter, and is only suitable
+ * for computing time deltas.
+ */
+INLINE u8 dvmGetRelativeTimeUsec(void) {
+ return dvmGetRelativeTimeNsec() / 1000;
+}
+
+/*
+ * Get the current time, in milliseconds. This is "relative" time,
+ * meaning it could be wall-clock time or a monotonic counter, and is
+ * only suitable for computing time deltas. The value returned from
+ * this function is a u4 and should only be used for debugging
+ * messages. TODO: make this value relative to the start-up time of
+ * the VM.
+ */
+INLINE u4 dvmGetRelativeTimeMsec(void) {
+ return (u4)(dvmGetRelativeTimeUsec() / 1000);
+}
+
+/*
+ * Get the current per-thread CPU time. This clock increases monotonically
+ * when the thread is running, but not when it's sleeping or blocked on a
+ * synchronization object.
+ *
+ * The absolute value of the clock may not be useful, so this should only
+ * be used for time deltas.
+ *
+ * If the thread CPU clock is not available, this always returns (u8)-1.
+ */
+u8 dvmGetThreadCpuTimeNsec(void);
+
+/*
+ * Per-thread CPU time, in micros.
+ */
+INLINE u8 dvmGetThreadCpuTimeUsec(void) {
+ return dvmGetThreadCpuTimeNsec() / 1000;
+}
+
+/*
+ * Like dvmGetThreadCpuTimeNsec, but for a different thread.
+ */
+u8 dvmGetOtherThreadCpuTimeNsec(pthread_t thread);
+INLINE u8 dvmGetOtherThreadCpuTimeUsec(pthread_t thread) {
+ return dvmGetOtherThreadCpuTimeNsec(thread) / 1000;
+}
+
+/*
+ * Sleep for increasingly longer periods, until "maxTotalSleep" microseconds
+ * have elapsed. Pass in the start time, which must be a value returned by
+ * dvmGetRelativeTimeUsec().
+ *
+ * Returns "false" if we were unable to sleep because our time is up.
+ */
+bool dvmIterativeSleep(int iteration, int maxTotalSleep, u8 relStartTime);
+
+/*
+ * Set the "close on exec" flag on a file descriptor.
+ */
+bool dvmSetCloseOnExec(int fd);
+
+/*
+ * Unconditionally abort the entire VM. Try not to use this.
+ *
+ * NOTE: if this is marked ((noreturn)), gcc will merge multiple dvmAbort()
+ * calls in a single function together. This is good, in that it reduces
+ * code size slightly, but also bad, because the native stack trace we
+ * get from the abort may point at the wrong call site. Best to leave
+ * it undecorated.
+ */
+void dvmAbort(void);
+
+#if (!HAVE_STRLCPY)
+/* Implementation of strlcpy() for platforms that don't already have it. */
+size_t strlcpy(char *dst, const char *src, size_t size);
+#endif
+
+/*
+ * Allocates a memory region using ashmem and mmap, initialized to
+ * zero. Actual allocation rounded up to page multiple. Returns
+ * NULL on failure.
+ */
+void *dvmAllocRegion(size_t size, int prot, const char *name);
+
+/*
+ * Returns the pointer to the "absolute path" part of the given path
+ * string, treating first (if any) instance of "/./" as a sentinel
+ * indicating the start of the absolute path. If the path isn't absolute
+ * in the usual way (i.e., starts with "/") and doesn't have the sentinel,
+ * then this returns NULL.
+ *
+ * For example:
+ * "/foo/bar/baz" returns "/foo/bar/baz"
+ * "foo/./bar/baz" returns "/bar/baz"
+ * "foo/bar/baz" returns NULL
+ *
+ * The sentinel is used specifically to aid in cross-optimization, where
+ * a host is processing dex files in a build tree, and where we don't want
+ * the build tree's directory structure to be baked into the output (such
+ * as, for example, in the dependency paths of optimized dex files).
+ */
+const char* dvmPathToAbsolutePortion(const char* path);
+
+#endif /*_DALVIK_MISC*/
diff --git a/vm/Native.c b/vm/Native.c
new file mode 100644
index 0000000..1ebef2e
--- /dev/null
+++ b/vm/Native.c
@@ -0,0 +1,874 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Native method resolution.
+ *
+ * Currently the "Dalvik native" methods are only used for internal methods.
+ * Someday we may want to export the interface as a faster but riskier
+ * alternative to JNI.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+#include <dlfcn.h>
+
+static void freeSharedLibEntry(void* ptr);
+static void* lookupSharedLibMethod(const Method* method);
+
+
+/*
+ * Initialize the native code loader.
+ */
+bool dvmNativeStartup(void)
+{
+ gDvm.nativeLibs = dvmHashTableCreate(4, freeSharedLibEntry);
+ if (gDvm.nativeLibs == NULL)
+ return false;
+
+ return true;
+}
+
+/*
+ * Free up our tables.
+ */
+void dvmNativeShutdown(void)
+{
+ dvmHashTableFree(gDvm.nativeLibs);
+ gDvm.nativeLibs = NULL;
+}
+
+
+/*
+ * Resolve a native method and invoke it.
+ *
+ * This is executed as if it were a native bridge or function. If the
+ * resolution succeeds, method->insns is replaced, and we don't go through
+ * here again unless the method is unregistered.
+ *
+ * Initializes method's class if necessary.
+ *
+ * An exception is thrown on resolution failure.
+ *
+ * (This should not be taking "const Method*", because it modifies the
+ * structure, but the declaration needs to match the DalvikBridgeFunc
+ * type definition.)
+ */
+void dvmResolveNativeMethod(const u4* args, JValue* pResult,
+ const Method* method, Thread* self)
+{
+ ClassObject* clazz = method->clazz;
+ void* func;
+
+ /*
+ * If this is a static method, it could be called before the class
+ * has been initialized.
+ */
+ if (dvmIsStaticMethod(method)) {
+ if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
+ assert(dvmCheckException(dvmThreadSelf()));
+ return;
+ }
+ } else {
+ assert(dvmIsClassInitialized(clazz) ||
+ dvmIsClassInitializing(clazz));
+ }
+
+ /* start with our internal-native methods */
+ func = dvmLookupInternalNativeMethod(method);
+ if (func != NULL) {
+ /* resolution always gets the same answer, so no race here */
+ IF_LOGVV() {
+ char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+ LOGVV("+++ resolved native %s.%s %s, invoking\n",
+ clazz->descriptor, method->name, desc);
+ free(desc);
+ }
+ if (dvmIsSynchronizedMethod(method)) {
+ LOGE("ERROR: internal-native can't be declared 'synchronized'\n");
+ LOGE("Failing on %s.%s\n", method->clazz->descriptor, method->name);
+ dvmAbort(); // harsh, but this is VM-internal problem
+ }
+ DalvikBridgeFunc dfunc = (DalvikBridgeFunc) func;
+ dvmSetNativeFunc((Method*) method, dfunc, NULL);
+ dfunc(args, pResult, method, self);
+ return;
+ }
+
+ /* now scan any DLLs we have loaded for JNI signatures */
+ func = lookupSharedLibMethod(method);
+ if (func != NULL) {
+ /* found it, point it at the JNI bridge and then call it */
+ dvmUseJNIBridge((Method*) method, func);
+ (*method->nativeFunc)(args, pResult, method, self);
+ return;
+ }
+
+ IF_LOGW() {
+ char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+ LOGW("No implementation found for native %s.%s %s\n",
+ clazz->descriptor, method->name, desc);
+ free(desc);
+ }
+
+ dvmThrowException("Ljava/lang/UnsatisfiedLinkError;", method->name);
+}
+
+
+/*
+ * ===========================================================================
+ * Native shared library support
+ * ===========================================================================
+ */
+
+// TODO? if a ClassLoader is unloaded, we need to unload all DLLs that
+// are associated with it. (Or not -- can't determine if native code
+// is still using parts of it.)
+
+typedef enum OnLoadState {
+ kOnLoadPending = 0, /* initial state, must be zero */
+ kOnLoadFailed,
+ kOnLoadOkay,
+} OnLoadState;
+
+/*
+ * We add one of these to the hash table for every library we load. The
+ * hash is on the "pathName" field.
+ */
+typedef struct SharedLib {
+ char* pathName; /* absolute path to library */
+ void* handle; /* from dlopen */
+ Object* classLoader; /* ClassLoader we are associated with */
+
+ pthread_mutex_t onLoadLock; /* guards remaining items */
+ pthread_cond_t onLoadCond; /* wait for JNI_OnLoad in other thread */
+ u4 onLoadThreadId; /* recursive invocation guard */
+ OnLoadState onLoadResult; /* result of earlier JNI_OnLoad */
+} SharedLib;
+
+/*
+ * (This is a dvmHashTableLookup callback.)
+ *
+ * Find an entry that matches the string.
+ */
+static int hashcmpNameStr(const void* ventry, const void* vname)
+{
+ const SharedLib* pLib = (const SharedLib*) ventry;
+ const char* name = (const char*) vname;
+
+ return strcmp(pLib->pathName, name);
+}
+
+/*
+ * (This is a dvmHashTableLookup callback.)
+ *
+ * Find an entry that matches the new entry.
+ *
+ * We don't compare the class loader here, because you're not allowed to
+ * have the same shared library associated with more than one CL.
+ */
+static int hashcmpSharedLib(const void* ventry, const void* vnewEntry)
+{
+ const SharedLib* pLib = (const SharedLib*) ventry;
+ const SharedLib* pNewLib = (const SharedLib*) vnewEntry;
+
+ LOGD("--- comparing %p '%s' %p '%s'\n",
+ pLib, pLib->pathName, pNewLib, pNewLib->pathName);
+ return strcmp(pLib->pathName, pNewLib->pathName);
+}
+
+/*
+ * Check to see if an entry with the same pathname already exists.
+ */
+static SharedLib* findSharedLibEntry(const char* pathName)
+{
+ u4 hash = dvmComputeUtf8Hash(pathName);
+ void* ent;
+
+ ent = dvmHashTableLookup(gDvm.nativeLibs, hash, (void*)pathName,
+ hashcmpNameStr, false);
+ return ent;
+}
+
+/*
+ * Add the new entry to the table.
+ *
+ * Returns the table entry, which will not be the same as "pLib" if the
+ * entry already exists.
+ */
+static SharedLib* addSharedLibEntry(SharedLib* pLib)
+{
+ u4 hash = dvmComputeUtf8Hash(pLib->pathName);
+
+ /*
+ * Do the lookup with the "add" flag set. If we add it, we will get
+ * our own pointer back. If somebody beat us to the punch, we'll get
+ * their pointer back instead.
+ */
+ return dvmHashTableLookup(gDvm.nativeLibs, hash, pLib, hashcmpSharedLib,
+ true);
+}
+
+/*
+ * Free up an entry. (This is a dvmHashTableFree callback.)
+ */
+static void freeSharedLibEntry(void* ptr)
+{
+ SharedLib* pLib = (SharedLib*) ptr;
+
+ /*
+ * Calling dlclose() here is somewhat dangerous, because it's possible
+ * that a thread outside the VM is still accessing the code we loaded.
+ */
+ if (false)
+ dlclose(pLib->handle);
+ free(pLib->pathName);
+ free(pLib);
+}
+
+/*
+ * Convert library name to system-dependent form, e.g. "jpeg" becomes
+ * "libjpeg.so".
+ *
+ * (Should we have this take buffer+len and avoid the alloc? It gets
+ * called very rarely.)
+ */
+char* dvmCreateSystemLibraryName(char* libName)
+{
+ char buf[256];
+ int len;
+
+ len = snprintf(buf, sizeof(buf), OS_SHARED_LIB_FORMAT_STR, libName);
+ if (len >= (int) sizeof(buf))
+ return NULL;
+ else
+ return strdup(buf);
+}
+
+/*
+ * Check the result of an earlier call to JNI_OnLoad on this library. If
+ * the call has not yet finished in another thread, wait for it.
+ */
+static bool checkOnLoadResult(SharedLib* pEntry)
+{
+ Thread* self = dvmThreadSelf();
+ if (pEntry->onLoadThreadId == self->threadId) {
+ /*
+ * Check this so we don't end up waiting for ourselves. We need
+ * to return "true" so the caller can continue.
+ */
+ LOGI("threadid=%d: recursive native library load attempt (%s)\n",
+ self->threadId, pEntry->pathName);
+ return true;
+ }
+
+ LOGV("+++ retrieving %s OnLoad status\n", pEntry->pathName);
+ bool result;
+
+ dvmLockMutex(&pEntry->onLoadLock);
+ while (pEntry->onLoadResult == kOnLoadPending) {
+ LOGD("threadid=%d: waiting for %s OnLoad status\n",
+ self->threadId, pEntry->pathName);
+ ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
+ pthread_cond_wait(&pEntry->onLoadCond, &pEntry->onLoadLock);
+ dvmChangeStatus(self, oldStatus);
+ }
+ if (pEntry->onLoadResult == kOnLoadOkay) {
+ LOGV("+++ earlier OnLoad(%s) okay\n", pEntry->pathName);
+ result = true;
+ } else {
+ LOGV("+++ earlier OnLoad(%s) failed\n", pEntry->pathName);
+ result = false;
+ }
+ dvmUnlockMutex(&pEntry->onLoadLock);
+ return result;
+}
+
+typedef int (*OnLoadFunc)(JavaVM*, void*);
+
+/*
+ * Load native code from the specified absolute pathname. Per the spec,
+ * if we've already loaded a library with the specified pathname, we
+ * return without doing anything.
+ *
+ * TODO? for better results we should absolutify the pathname. For fully
+ * correct results we should stat to get the inode and compare that. The
+ * existing implementation is fine so long as everybody is using
+ * System.loadLibrary.
+ *
+ * The library will be associated with the specified class loader. The JNI
+ * spec says we can't load the same library into more than one class loader.
+ *
+ * Returns "true" on success. On failure, sets *detail to a
+ * human-readable description of the error or NULL if no detail is
+ * available; ownership of the string is transferred to the caller.
+ */
+bool dvmLoadNativeCode(const char* pathName, Object* classLoader,
+ char** detail)
+{
+ SharedLib* pEntry;
+ void* handle;
+ bool verbose;
+
+ /* reduce noise by not chattering about system libraries */
+ verbose = !!strncmp(pathName, "/system", sizeof("/system")-1);
+ verbose = verbose && !!strncmp(pathName, "/vendor", sizeof("/vendor")-1);
+
+ if (verbose)
+ LOGD("Trying to load lib %s %p\n", pathName, classLoader);
+
+ *detail = NULL;
+
+ /*
+ * See if we've already loaded it. If we have, and the class loader
+ * matches, return successfully without doing anything.
+ */
+ pEntry = findSharedLibEntry(pathName);
+ if (pEntry != NULL) {
+ if (pEntry->classLoader != classLoader) {
+ LOGW("Shared lib '%s' already opened by CL %p; can't open in %p\n",
+ pathName, pEntry->classLoader, classLoader);
+ return false;
+ }
+ if (verbose) {
+ LOGD("Shared lib '%s' already loaded in same CL %p\n",
+ pathName, classLoader);
+ }
+ if (!checkOnLoadResult(pEntry))
+ return false;
+ return true;
+ }
+
+ /*
+ * Open the shared library. Because we're using a full path, the system
+ * doesn't have to search through LD_LIBRARY_PATH. (It may do so to
+ * resolve this library's dependencies though.)
+ *
+ * Failures here are expected when java.library.path has several entries
+ * and we have to hunt for the lib.
+ *
+ * The current version of the dynamic linker prints detailed information
+ * about dlopen() failures. Some things to check if the message is
+ * cryptic:
+ * - make sure the library exists on the device
+ * - verify that the right path is being opened (the debug log message
+ * above can help with that)
+ * - check to see if the library is valid (e.g. not zero bytes long)
+ * - check config/prelink-linux-arm.map to ensure that the library
+ * is listed and is not being overrun by the previous entry (if
+ * loading suddenly stops working on a prelinked library, this is
+ * a good one to check)
+ * - write a trivial app that calls sleep() then dlopen(), attach
+ * to it with "strace -p <pid>" while it sleeps, and watch for
+ * attempts to open nonexistent dependent shared libs
+ *
+ * This can execute slowly for a large library on a busy system, so we
+ * want to switch from RUNNING to VMWAIT while it executes. This allows
+ * the GC to ignore us.
+ */
+ Thread* self = dvmThreadSelf();
+ ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
+ handle = dlopen(pathName, RTLD_LAZY);
+ dvmChangeStatus(self, oldStatus);
+
+ if (handle == NULL) {
+ *detail = strdup(dlerror());
+ return false;
+ }
+
+ /* create a new entry */
+ SharedLib* pNewEntry;
+ pNewEntry = (SharedLib*) calloc(1, sizeof(SharedLib));
+ pNewEntry->pathName = strdup(pathName);
+ pNewEntry->handle = handle;
+ pNewEntry->classLoader = classLoader;
+ dvmInitMutex(&pNewEntry->onLoadLock);
+ pthread_cond_init(&pNewEntry->onLoadCond, NULL);
+ pNewEntry->onLoadThreadId = self->threadId;
+
+ /* try to add it to the list */
+ SharedLib* pActualEntry = addSharedLibEntry(pNewEntry);
+
+ if (pNewEntry != pActualEntry) {
+ LOGI("WOW: we lost a race to add a shared lib (%s CL=%p)\n",
+ pathName, classLoader);
+ freeSharedLibEntry(pNewEntry);
+ return checkOnLoadResult(pActualEntry);
+ } else {
+ if (verbose)
+ LOGD("Added shared lib %s %p\n", pathName, classLoader);
+
+ bool result = true;
+ void* vonLoad;
+ int version;
+
+ vonLoad = dlsym(handle, "JNI_OnLoad");
+ if (vonLoad == NULL) {
+ LOGD("No JNI_OnLoad found in %s %p, skipping init\n",
+ pathName, classLoader);
+ } else {
+ /*
+ * Call JNI_OnLoad. We have to override the current class
+ * loader, which will always be "null" since the stuff at the
+ * top of the stack is around Runtime.loadLibrary(). (See
+ * the comments in the JNI FindClass function.)
+ */
+ OnLoadFunc func = vonLoad;
+ Object* prevOverride = self->classLoaderOverride;
+
+ self->classLoaderOverride = classLoader;
+ oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
+ LOGV("+++ calling JNI_OnLoad(%s)\n", pathName);
+ version = (*func)(gDvm.vmList, NULL);
+ dvmChangeStatus(self, oldStatus);
+ self->classLoaderOverride = prevOverride;
+
+ if (version != JNI_VERSION_1_2 && version != JNI_VERSION_1_4 &&
+ version != JNI_VERSION_1_6)
+ {
+ LOGW("JNI_OnLoad returned bad version (%d) in %s %p\n",
+ version, pathName, classLoader);
+ /*
+ * It's unwise to call dlclose() here, but we can mark it
+ * as bad and ensure that future load attempts will fail.
+ *
+ * We don't know how far JNI_OnLoad got, so there could
+ * be some partially-initialized stuff accessible through
+ * newly-registered native method calls. We could try to
+ * unregister them, but that doesn't seem worthwhile.
+ */
+ result = false;
+ } else {
+ LOGV("+++ finished JNI_OnLoad %s\n", pathName);
+ }
+ }
+
+ if (result)
+ pNewEntry->onLoadResult = kOnLoadOkay;
+ else
+ pNewEntry->onLoadResult = kOnLoadFailed;
+
+ pNewEntry->onLoadThreadId = 0;
+
+ /*
+ * Broadcast a wakeup to anybody sleeping on the condition variable.
+ */
+ dvmLockMutex(&pNewEntry->onLoadLock);
+ pthread_cond_broadcast(&pNewEntry->onLoadCond);
+ dvmUnlockMutex(&pNewEntry->onLoadLock);
+ return result;
+ }
+}
+
+
+/*
+ * Un-register JNI native methods.
+ *
+ * There are two relevant fields in struct Method, "nativeFunc" and
+ * "insns". The former holds a function pointer to a "bridge" function
+ * (or, for internal native, the actual implementation). The latter holds
+ * a pointer to the actual JNI method.
+ *
+ * The obvious approach is to reset both fields to their initial state
+ * (nativeFunc points at dvmResolveNativeMethod, insns holds NULL), but
+ * that creates some unpleasant race conditions. In particular, if another
+ * thread is executing inside the call bridge for the method in question,
+ * and we reset insns to NULL, the VM will crash. (See the comments above
+ * dvmSetNativeFunc() for additional commentary.)
+ *
+ * We can't rely on being able to update two 32-bit fields in one atomic
+ * operation (e.g. no 64-bit atomic ops on ARMv5TE), so we want to change
+ * only one field. It turns out we can simply reset nativeFunc to its
+ * initial state, leaving insns alone, because dvmResolveNativeMethod
+ * ignores "insns" entirely.
+ *
+ * When the method is re-registered, both fields will be updated, but
+ * dvmSetNativeFunc guarantees that "insns" is updated first. This means
+ * we shouldn't be in a situation where we have a "live" call bridge and
+ * a stale implementation pointer.
+ */
+static void unregisterJNINativeMethods(Method* methods, size_t count)
+{
+ while (count != 0) {
+ count--;
+
+ Method* meth = &methods[count];
+ if (!dvmIsNativeMethod(meth))
+ continue;
+ if (dvmIsAbstractMethod(meth)) /* avoid abstract method stubs */
+ continue;
+
+ /*
+ * Strictly speaking this ought to test the function pointer against
+ * the various JNI bridge functions to ensure that we only undo
+ * methods that were registered through JNI. In practice, any
+ * native method with a non-NULL "insns" is a registered JNI method.
+ *
+ * If we inadvertently unregister an internal-native, it'll get
+ * re-resolved on the next call; unregistering an unregistered
+ * JNI method is a no-op. So we don't really need to test for
+ * anything.
+ */
+
+ LOGD("Unregistering JNI method %s.%s:%s\n",
+ meth->clazz->descriptor, meth->name, meth->shorty);
+ dvmSetNativeFunc(meth, dvmResolveNativeMethod, NULL);
+ }
+}
+
+/*
+ * Un-register all JNI native methods from a class.
+ */
+void dvmUnregisterJNINativeMethods(ClassObject* clazz)
+{
+ unregisterJNINativeMethods(clazz->directMethods, clazz->directMethodCount);
+ unregisterJNINativeMethods(clazz->virtualMethods, clazz->virtualMethodCount);
+}
+
+
+/*
+ * ===========================================================================
+ * Signature-based method lookup
+ * ===========================================================================
+ */
+
+/*
+ * Create the pre-mangled form of the class+method string.
+ *
+ * Returns a newly-allocated string, and sets "*pLen" to the length.
+ */
+static char* createJniNameString(const char* classDescriptor,
+ const char* methodName, int* pLen)
+{
+ char* result;
+ size_t descriptorLength = strlen(classDescriptor);
+
+ *pLen = 4 + descriptorLength + strlen(methodName);
+
+ result = malloc(*pLen +1);
+ if (result == NULL)
+ return NULL;
+
+ /*
+ * Add one to classDescriptor to skip the "L", and then replace
+ * the final ";" with a "/" after the sprintf() call.
+ */
+ sprintf(result, "Java/%s%s", classDescriptor + 1, methodName);
+ result[5 + (descriptorLength - 2)] = '/';
+
+ return result;
+}
+
+/*
+ * Returns a newly-allocated, mangled copy of "str".
+ *
+ * "str" is a "modified UTF-8" string. We convert it to UTF-16 first to
+ * make life simpler.
+ */
+static char* mangleString(const char* str, int len)
+{
+ u2* utf16 = NULL;
+ char* mangle = NULL;
+ int charLen;
+
+ //LOGI("mangling '%s' %d\n", str, len);
+
+ assert(str[len] == '\0');
+
+ charLen = dvmUtf8Len(str);
+ utf16 = (u2*) malloc(sizeof(u2) * charLen);
+ if (utf16 == NULL)
+ goto bail;
+
+ dvmConvertUtf8ToUtf16(utf16, str);
+
+ /*
+ * Compute the length of the mangled string.
+ */
+ int i, mangleLen = 0;
+
+ for (i = 0; i < charLen; i++) {
+ u2 ch = utf16[i];
+
+ if (ch == '$' || ch > 127) {
+ mangleLen += 6;
+ } else {
+ switch (ch) {
+ case '_':
+ case ';':
+ case '[':
+ mangleLen += 2;
+ break;
+ default:
+ mangleLen++;
+ break;
+ }
+ }
+ }
+
+ char* cp;
+
+ mangle = (char*) malloc(mangleLen +1);
+ if (mangle == NULL)
+ goto bail;
+
+ for (i = 0, cp = mangle; i < charLen; i++) {
+ u2 ch = utf16[i];
+
+ if (ch == '$' || ch > 127) {
+ sprintf(cp, "_0%04x", ch);
+ cp += 6;
+ } else {
+ switch (ch) {
+ case '_':
+ *cp++ = '_';
+ *cp++ = '1';
+ break;
+ case ';':
+ *cp++ = '_';
+ *cp++ = '2';
+ break;
+ case '[':
+ *cp++ = '_';
+ *cp++ = '3';
+ break;
+ case '/':
+ *cp++ = '_';
+ break;
+ default:
+ *cp++ = (char) ch;
+ break;
+ }
+ }
+ }
+
+ *cp = '\0';
+
+bail:
+ free(utf16);
+ return mangle;
+}
+
+/*
+ * Create the mangled form of the parameter types.
+ */
+static char* createMangledSignature(const DexProto* proto)
+{
+ DexStringCache sigCache;
+ const char* interim;
+ char* result;
+
+ dexStringCacheInit(&sigCache);
+ interim = dexProtoGetParameterDescriptors(proto, &sigCache);
+ result = mangleString(interim, strlen(interim));
+ dexStringCacheRelease(&sigCache);
+
+ return result;
+}
+
+/*
+ * (This is a dvmHashForeach callback.)
+ *
+ * Search for a matching method in this shared library.
+ *
+ * TODO: we may want to skip libraries for which JNI_OnLoad failed.
+ */
+static int findMethodInLib(void* vlib, void* vmethod)
+{
+ const SharedLib* pLib = (const SharedLib*) vlib;
+ const Method* meth = (const Method*) vmethod;
+ char* preMangleCM = NULL;
+ char* mangleCM = NULL;
+ char* mangleSig = NULL;
+ char* mangleCMSig = NULL;
+ void* func = NULL;
+ int len;
+
+ if (meth->clazz->classLoader != pLib->classLoader) {
+ LOGV("+++ not scanning '%s' for '%s' (wrong CL)\n",
+ pLib->pathName, meth->name);
+ return 0;
+ } else
+ LOGV("+++ scanning '%s' for '%s'\n", pLib->pathName, meth->name);
+
+ /*
+ * First, we try it without the signature.
+ */
+ preMangleCM =
+ createJniNameString(meth->clazz->descriptor, meth->name, &len);
+ if (preMangleCM == NULL)
+ goto bail;
+
+ mangleCM = mangleString(preMangleCM, len);
+ if (mangleCM == NULL)
+ goto bail;
+
+ LOGV("+++ calling dlsym(%s)\n", mangleCM);
+ func = dlsym(pLib->handle, mangleCM);
+ if (func == NULL) {
+ mangleSig =
+ createMangledSignature(&meth->prototype);
+ if (mangleSig == NULL)
+ goto bail;
+
+ mangleCMSig = (char*) malloc(strlen(mangleCM) + strlen(mangleSig) +3);
+ if (mangleCMSig == NULL)
+ goto bail;
+
+ sprintf(mangleCMSig, "%s__%s", mangleCM, mangleSig);
+
+ LOGV("+++ calling dlsym(%s)\n", mangleCMSig);
+ func = dlsym(pLib->handle, mangleCMSig);
+ if (func != NULL) {
+ LOGV("Found '%s' with dlsym\n", mangleCMSig);
+ }
+ } else {
+ LOGV("Found '%s' with dlsym\n", mangleCM);
+ }
+
+bail:
+ free(preMangleCM);
+ free(mangleCM);
+ free(mangleSig);
+ free(mangleCMSig);
+ return (int) func;
+}
+
+/*
+ * See if the requested method lives in any of the currently-loaded
+ * shared libraries. We do this by checking each of them for the expected
+ * method signature.
+ */
+static void* lookupSharedLibMethod(const Method* method)
+{
+ if (gDvm.nativeLibs == NULL) {
+ LOGE("Unexpected init state: nativeLibs not ready\n");
+ dvmAbort();
+ }
+ return (void*) dvmHashForeach(gDvm.nativeLibs, findMethodInLib,
+ (void*) method);
+}
+
+
+static void appendValue(char type, const JValue value, char* buf, size_t n,
+ bool appendComma)
+{
+ size_t len = strlen(buf);
+ if (len >= n - 32) { // 32 should be longer than anything we could append.
+ buf[len - 1] = '.';
+ buf[len - 2] = '.';
+ buf[len - 3] = '.';
+ return;
+ }
+ char* p = buf + len;
+ switch (type) {
+ case 'B':
+ if (value.b >= 0 && value.b < 10) {
+ sprintf(p, "%d", value.b);
+ } else {
+ sprintf(p, "0x%x (%d)", value.b, value.b);
+ }
+ break;
+ case 'C':
+ if (value.c < 0x7f && value.c >= ' ') {
+ sprintf(p, "U+%x ('%c')", value.c, value.c);
+ } else {
+ sprintf(p, "U+%x", value.c);
+ }
+ break;
+ case 'D':
+ sprintf(p, "%g", value.d);
+ break;
+ case 'F':
+ sprintf(p, "%g", value.f);
+ break;
+ case 'I':
+ sprintf(p, "%d", value.i);
+ break;
+ case 'L':
+ sprintf(p, "0x%x", value.i);
+ break;
+ case 'J':
+ sprintf(p, "%lld", value.j);
+ break;
+ case 'S':
+ sprintf(p, "%d", value.s);
+ break;
+ case 'V':
+ strcpy(p, "void");
+ break;
+ case 'Z':
+ strcpy(p, value.z ? "true" : "false");
+ break;
+ default:
+ sprintf(p, "unknown type '%c'", type);
+ break;
+ }
+
+ if (appendComma) {
+ strcat(p, ", ");
+ }
+}
+
+#define LOGI_NATIVE(...) LOG(LOG_INFO, LOG_TAG "-native", __VA_ARGS__)
+
+void dvmLogNativeMethodEntry(const Method* method, const u4* args)
+{
+ char thisString[32] = { 0 };
+ const u4* sp = args; // &args[method->registersSize - method->insSize];
+ if (!dvmIsStaticMethod(method)) {
+ sprintf(thisString, "this=0x%08x ", *sp++);
+ }
+
+ char argsString[128]= { 0 };
+ const char* desc = &method->shorty[1];
+ while (*desc != '\0') {
+ char argType = *desc++;
+ JValue value;
+ if (argType == 'D' || argType == 'J') {
+ value.j = dvmGetArgLong(sp, 0);
+ sp += 2;
+ } else {
+ value.i = *sp++;
+ }
+ appendValue(argType, value, argsString, sizeof(argsString),
+ *desc != '\0');
+ }
+
+ char* signature = dexProtoCopyMethodDescriptor(&method->prototype);
+ LOGI_NATIVE("-> %s.%s%s %s(%s)", method->clazz->descriptor, method->name,
+ signature, thisString, argsString);
+ free(signature);
+}
+
+void dvmLogNativeMethodExit(const Method* method, Thread* self,
+ const JValue returnValue)
+{
+ char* signature = dexProtoCopyMethodDescriptor(&method->prototype);
+ if (dvmCheckException(self)) {
+ Object* exception = dvmGetException(self);
+ LOGI_NATIVE("<- %s.%s%s threw %s", method->clazz->descriptor,
+ method->name, signature, exception->clazz->descriptor);
+ } else {
+ char returnValueString[128] = { 0 };
+ char returnType = method->shorty[0];
+ appendValue(returnType, returnValue,
+ returnValueString, sizeof(returnValueString), false);
+ LOGI_NATIVE("<- %s.%s%s returned %s", method->clazz->descriptor,
+ method->name, signature, returnValueString);
+ }
+ free(signature);
+}
diff --git a/vm/Native.h b/vm/Native.h
new file mode 100644
index 0000000..f60ccce
--- /dev/null
+++ b/vm/Native.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Dalvik's native call interface.
+ *
+ * You should follow the JNI function naming conventions, but prefix with
+ * "Dalvik_" instead of "Java_".
+ */
+#ifndef _DALVIK_NATIVE
+#define _DALVIK_NATIVE
+
+/*
+ * Method description; equivalent to a JNI struct.
+ */
+typedef struct DalvikNativeMethod {
+ const char* name;
+ const char* signature;
+ DalvikNativeFunc fnPtr;
+} DalvikNativeMethod;
+
+/*
+ * All methods for one class. The last "methodInfo" has a NULL "name".
+ */
+typedef struct DalvikNativeClass {
+ const char* classDescriptor;
+ const DalvikNativeMethod* methodInfo;
+ u4 classDescriptorHash; /* initialized at runtime */
+} DalvikNativeClass;
+
+
+/* init/shutdown */
+bool dvmNativeStartup(void);
+void dvmNativeShutdown(void);
+
+
+/*
+ * Convert argc/argv into a function call. This is platform-specific.
+ */
+void dvmPlatformInvoke(void* pEnv, ClassObject* clazz, int argInfo, int argc,
+ const u4* argv, const char* signature, void* func, JValue* pResult);
+
+/*
+ * Generate hints to speed native calls. This is platform specific.
+ */
+u4 dvmPlatformInvokeHints(const DexProto* proto);
+
+/*
+ * Convert a short library name ("jpeg") to a system-dependent name
+ * ("libjpeg.so"). Returns a newly-allocated string.
+ */
+char* dvmCreateSystemLibraryName(char* libName);
+//void dvmLoadNativeLibrary(StringObject* libNameObj, Object* classLoader);
+bool dvmLoadNativeCode(const char* fileName, Object* classLoader,
+ char** detail);
+
+
+/*
+ * Resolve a native method. This uses the same prototype as a
+ * DalvikBridgeFunc, because it takes the place of the actual function
+ * until the first time that it's invoked.
+ *
+ * Causes the method's class to be initialized.
+ *
+ * Throws an exception and returns NULL on failure.
+ */
+void dvmResolveNativeMethod(const u4* args, JValue* pResult,
+ const Method* method, struct Thread* self);
+
+/*
+ * Unregister all JNI native methods associated with a class.
+ */
+void dvmUnregisterJNINativeMethods(ClassObject* clazz);
+
+//#define GET_ARG_LONG(_args, _elem) (*(s8*)(&(_args)[_elem]))
+#define GET_ARG_LONG(_args, _elem) dvmGetArgLong(_args, _elem)
+
+/*
+ * Helpful function for accessing long integers in "u4* args".
+ *
+ * We can't just return *(s8*)(&args[elem]), because that breaks if our
+ * architecture requires 64-bit alignment of 64-bit values.
+ *
+ * Big/little endian shouldn't matter here -- ordering of words within a
+ * long seems consistent across our supported platforms.
+ */
+INLINE s8 dvmGetArgLong(const u4* args, int elem)
+{
+#if 0
+ union { u4 parts[2]; s8 whole; } conv;
+ conv.parts[0] = args[elem];
+ conv.parts[1] = args[elem+1];
+ return conv.whole;
+#else
+ /* with gcc's optimizer, memcpy() turns into simpler assignments */
+ s8 val;
+ memcpy(&val, &args[elem], 8);
+ return val;
+#endif
+}
+
+/*
+ * Used to implement -Xjnitrace.
+ */
+struct Thread;
+void dvmLogNativeMethodEntry(const Method* method, const u4* newFp);
+void dvmLogNativeMethodExit(const Method* method, struct Thread* self,
+ const JValue retval);
+
+#endif /*_DALVIK_NATIVE*/
diff --git a/vm/PointerSet.c b/vm/PointerSet.c
new file mode 100644
index 0000000..1d2e814
--- /dev/null
+++ b/vm/PointerSet.c
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Maintain an expanding set of unique pointer values.
+ */
+#include "Dalvik.h"
+
+/*
+ * Sorted, expanding list of pointers.
+ */
+struct PointerSet {
+ u2 alloc;
+ u2 count;
+ const void** list;
+};
+
+/*
+ * Verify that the set is in sorted order.
+ */
+#ifndef NDEBUG
+static bool verifySorted(PointerSet* pSet)
+{
+ const void* last = NULL;
+ int i;
+
+ for (i = 0; i < pSet->count; i++) {
+ const void* cur = pSet->list[i];
+ if (cur < last)
+ return false;
+ last = cur;
+ }
+
+ return true;
+}
+#endif
+
+/*
+ * Allocate a new PointerSet.
+ *
+ * Returns NULL on failure.
+ */
+PointerSet* dvmPointerSetAlloc(int initialSize)
+{
+ PointerSet* pSet = calloc(1, sizeof(PointerSet));
+ if (pSet != NULL) {
+ if (initialSize > 0) {
+ pSet->list = malloc(sizeof(const void*) * initialSize);
+ if (pSet->list == NULL) {
+ free(pSet);
+ return NULL;
+ }
+ pSet->alloc = initialSize;
+ }
+ }
+
+ return pSet;
+}
+
+/*
+ * Free up a PointerSet.
+ */
+void dvmPointerSetFree(PointerSet* pSet)
+{
+ if (pSet == NULL)
+ return;
+
+ if (pSet->list != NULL) {
+ free(pSet->list);
+ pSet->list = NULL;
+ }
+ free(pSet);
+}
+
+/*
+ * Clear the contents of a pointer set.
+ */
+void dvmPointerSetClear(PointerSet* pSet)
+{
+ pSet->count = 0;
+}
+
+/*
+ * Get the number of pointers currently stored in the list.
+ */
+int dvmPointerSetGetCount(const PointerSet* pSet)
+{
+ return pSet->count;
+}
+
+/*
+ * Get the Nth entry from the list.
+ */
+const void* dvmPointerSetGetEntry(const PointerSet* pSet, int i)
+{
+ return pSet->list[i];
+}
+
+/*
+ * Insert a new entry into the list. If it already exists, this returns
+ * without doing anything.
+ *
+ * Returns "true" if the value was added.
+ */
+bool dvmPointerSetAddEntry(PointerSet* pSet, const void* ptr)
+{
+ int nearby;
+
+ if (dvmPointerSetHas(pSet, ptr, &nearby))
+ return false;
+
+ /* ensure we have space to add one more */
+ if (pSet->count == pSet->alloc) {
+ /* time to expand */
+ const void** newList;
+
+ if (pSet->alloc == 0)
+ pSet->alloc = 4;
+ else
+ pSet->alloc *= 2;
+ LOGVV("expanding %p to %d\n", pSet, pSet->alloc);
+ newList = realloc(pSet->list, pSet->alloc * sizeof(const void*));
+ if (newList == NULL) {
+ LOGE("Failed expanding ptr set (alloc=%d)\n", pSet->alloc);
+ dvmAbort();
+ }
+ pSet->list = newList;
+ }
+
+ if (pSet->count == 0) {
+ /* empty list */
+ assert(nearby == 0);
+ } else {
+ /*
+ * Determine the insertion index. The binary search might have
+ * terminated "above" or "below" the value.
+ */
+ if (nearby != 0 && ptr < pSet->list[nearby-1]) {
+ //LOGD("nearby-1=%d %p, inserting %p at -1\n",
+ // nearby-1, pSet->list[nearby-1], ptr);
+ nearby--;
+ } else if (ptr < pSet->list[nearby]) {
+ //LOGD("nearby=%d %p, inserting %p at +0\n",
+ // nearby, pSet->list[nearby], ptr);
+ } else {
+ //LOGD("nearby+1=%d %p, inserting %p at +1\n",
+ // nearby+1, pSet->list[nearby+1], ptr);
+ nearby++;
+ }
+
+ /*
+ * Move existing values, if necessary.
+ */
+ if (nearby != pSet->count) {
+ /* shift up */
+ memmove(&pSet->list[nearby+1], &pSet->list[nearby],
+ (pSet->count - nearby) * sizeof(pSet->list[0]));
+ }
+ }
+
+ pSet->list[nearby] = ptr;
+ pSet->count++;
+
+ assert(verifySorted(pSet));
+ return true;
+}
+
+/*
+ * Returns "true" if the element was successfully removed.
+ */
+bool dvmPointerSetRemoveEntry(PointerSet* pSet, const void* ptr)
+{
+ int where;
+
+ if (!dvmPointerSetHas(pSet, ptr, &where))
+ return false;
+
+ if (where != pSet->count-1) {
+ /* shift down */
+ memmove(&pSet->list[where], &pSet->list[where+1],
+ (pSet->count-1 - where) * sizeof(pSet->list[0]));
+ }
+
+ pSet->count--;
+ pSet->list[pSet->count] = (const void*) 0xdecadead; // debug
+ return true;
+}
+
+/*
+ * Returns the index if "ptr" appears in the list. If it doesn't appear,
+ * this returns a negative index for a nearby element.
+ */
+bool dvmPointerSetHas(const PointerSet* pSet, const void* ptr, int* pIndex)
+{
+ int hi, lo, mid;
+
+ lo = mid = 0;
+ hi = pSet->count-1;
+
+ /* array is sorted, use a binary search */
+ while (lo <= hi) {
+ mid = (lo + hi) / 2;
+ const void* listVal = pSet->list[mid];
+
+ if (ptr > listVal) {
+ lo = mid + 1;
+ } else if (ptr < listVal) {
+ hi = mid - 1;
+ } else /* listVal == ptr */ {
+ if (pIndex != NULL)
+ *pIndex = mid;
+ return true;
+ }
+ }
+
+ if (pIndex != NULL)
+ *pIndex = mid;
+ return false;
+}
+
+/*
+ * Compute the intersection of the set and the array of pointers passed in.
+ *
+ * Any pointer in "pSet" that does not appear in "ptrArray" is removed.
+ */
+void dvmPointerSetIntersect(PointerSet* pSet, const void** ptrArray, int count)
+{
+ int i, j;
+
+ for (i = 0; i < pSet->count; i++) {
+ for (j = 0; j < count; j++) {
+ if (pSet->list[i] == ptrArray[j]) {
+ /* match, keep this one */
+ break;
+ }
+ }
+
+ if (j == count) {
+ /* no match, remove entry */
+ if (i != pSet->count-1) {
+ /* shift down */
+ memmove(&pSet->list[i], &pSet->list[i+1],
+ (pSet->count-1 - i) * sizeof(pSet->list[0]));
+ }
+
+ pSet->count--;
+ pSet->list[pSet->count] = (const void*) 0xdecadead; // debug
+ i--; /* adjust loop counter */
+ }
+ }
+}
+
+/*
+ * Print the list contents to stdout. For debugging.
+ */
+void dvmPointerSetDump(const PointerSet* pSet)
+{
+ int i;
+ for (i = 0; i < pSet->count; i++)
+ printf(" %p", pSet->list[i]);
+}
diff --git a/vm/PointerSet.h b/vm/PointerSet.h
new file mode 100644
index 0000000..ffc0635
--- /dev/null
+++ b/vm/PointerSet.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Maintain an expanding set of unique pointer values. The set is
+ * kept in sorted order.
+ */
+#ifndef _DALVIK_POINTERSET
+#define _DALVIK_POINTERSET
+
+struct PointerSet; /* private */
+typedef struct PointerSet PointerSet;
+
+/*
+ * Allocate a new PointerSet.
+ *
+ * Returns NULL on failure.
+ */
+PointerSet* dvmPointerSetAlloc(int initialSize);
+
+/*
+ * Free up a PointerSet.
+ */
+void dvmPointerSetFree(PointerSet* pSet);
+
+/*
+ * Clear the contents of a pointer set.
+ */
+void dvmPointerSetClear(PointerSet* pSet);
+
+/*
+ * Get the number of pointers currently stored in the list.
+ */
+int dvmPointerSetGetCount(const PointerSet* pSet);
+
+/*
+ * Get the Nth entry from the list.
+ */
+const void* dvmPointerSetGetEntry(const PointerSet* pSet, int i);
+
+/*
+ * Insert a new entry into the list. If it already exists, this returns
+ * without doing anything.
+ *
+ * Returns "true" if the pointer was added.
+ */
+bool dvmPointerSetAddEntry(PointerSet* pSet, const void* ptr);
+
+/*
+ * Returns "true" if the element was successfully removed.
+ */
+bool dvmPointerSetRemoveEntry(PointerSet* pSet, const void* ptr);
+
+/*
+ * Returns "true" if the value appears, "false" otherwise. If "pIndex" is
+ * non-NULL, it will receive the matching index or the index of a nearby
+ * element.
+ */
+bool dvmPointerSetHas(const PointerSet* pSet, const void* ptr, int* pIndex);
+
+/*
+ * Find an entry in the set. Returns the index, or -1 if not found.
+ */
+INLINE int dvmPointerSetFind(const PointerSet* pSet, const void* ptr) {
+ int idx;
+ if (!dvmPointerSetHas(pSet, ptr, &idx))
+ idx = -1;
+ return idx;
+}
+
+/*
+ * Compute the intersection of the set and the array of pointers passed in.
+ *
+ * Any pointer in "pSet" that does not appear in "ptrArray" is removed.
+ */
+void dvmPointerSetIntersect(PointerSet* pSet, const void** ptrArray, int count);
+
+/*
+ * Print the list contents to stdout. For debugging.
+ */
+void dvmPointerSetDump(const PointerSet* pSet);
+
+#endif /*_DALVIK_POINTERSET*/
diff --git a/vm/Profile.c b/vm/Profile.c
new file mode 100644
index 0000000..d5dcc36
--- /dev/null
+++ b/vm/Profile.c
@@ -0,0 +1,895 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Android's method call profiling goodies.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <sched.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <cutils/open_memstream.h>
+
+#ifdef HAVE_ANDROID_OS
+# define UPDATE_MAGIC_PAGE 1
+#endif
+
+/*
+ * File format:
+ * header
+ * record 0
+ * record 1
+ * ...
+ *
+ * Header format:
+ * u4 magic ('SLOW')
+ * u2 version
+ * u2 offset to data
+ * u8 start date/time in usec
+ *
+ * Record format:
+ * u1 thread ID
+ * u4 method ID | method action
+ * u4 time delta since start, in usec
+ *
+ * 32 bits of microseconds is 70 minutes.
+ *
+ * All values are stored in little-endian order.
+ */
+#define TRACE_REC_SIZE 9
+#define TRACE_MAGIC 0x574f4c53
+#define TRACE_HEADER_LEN 32
+
+#define FILL_PATTERN 0xeeeeeeee
+
+
+/*
+ * Get the wall-clock date/time, in usec.
+ */
+static inline u8 getTimeInUsec()
+{
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ return tv.tv_sec * 1000000LL + tv.tv_usec;
+}
+
+/*
+ * Get the current time, in microseconds.
+ *
+ * This can mean one of two things. In "global clock" mode, we get the
+ * same time across all threads. If we use CLOCK_THREAD_CPUTIME_ID, we
+ * get a per-thread CPU usage timer. The latter is better, but a bit
+ * more complicated to implement.
+ */
+static inline u8 getClock()
+{
+#if defined(HAVE_POSIX_CLOCKS)
+ if (!gDvm.profilerWallClock) {
+ struct timespec tm;
+
+ clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tm);
+ if (!(tm.tv_nsec >= 0 && tm.tv_nsec < 1*1000*1000*1000)) {
+ LOGE("bad nsec: %ld\n", tm.tv_nsec);
+ dvmAbort();
+ }
+
+ return tm.tv_sec * 1000000LL + tm.tv_nsec / 1000;
+ } else
+#endif
+ {
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ return tv.tv_sec * 1000000LL + tv.tv_usec;
+ }
+}
+
+/*
+ * Write little-endian data.
+ */
+static inline void storeShortLE(u1* buf, u2 val)
+{
+ *buf++ = (u1) val;
+ *buf++ = (u1) (val >> 8);
+}
+static inline void storeIntLE(u1* buf, u4 val)
+{
+ *buf++ = (u1) val;
+ *buf++ = (u1) (val >> 8);
+ *buf++ = (u1) (val >> 16);
+ *buf++ = (u1) (val >> 24);
+}
+static inline void storeLongLE(u1* buf, u8 val)
+{
+ *buf++ = (u1) val;
+ *buf++ = (u1) (val >> 8);
+ *buf++ = (u1) (val >> 16);
+ *buf++ = (u1) (val >> 24);
+ *buf++ = (u1) (val >> 32);
+ *buf++ = (u1) (val >> 40);
+ *buf++ = (u1) (val >> 48);
+ *buf++ = (u1) (val >> 56);
+}
+
+/*
+ * Boot-time init.
+ */
+bool dvmProfilingStartup(void)
+{
+ /*
+ * Initialize "dmtrace" method profiling.
+ */
+ memset(&gDvm.methodTrace, 0, sizeof(gDvm.methodTrace));
+ dvmInitMutex(&gDvm.methodTrace.startStopLock);
+ pthread_cond_init(&gDvm.methodTrace.threadExitCond, NULL);
+
+ ClassObject* clazz =
+ dvmFindClassNoInit("Ldalvik/system/VMDebug;", NULL);
+ assert(clazz != NULL);
+ gDvm.methodTrace.gcMethod =
+ dvmFindDirectMethodByDescriptor(clazz, "startGC", "()V");
+ gDvm.methodTrace.classPrepMethod =
+ dvmFindDirectMethodByDescriptor(clazz, "startClassPrep", "()V");
+ if (gDvm.methodTrace.gcMethod == NULL ||
+ gDvm.methodTrace.classPrepMethod == NULL)
+ {
+ LOGE("Unable to find startGC or startClassPrep\n");
+ return false;
+ }
+
+ assert(!dvmCheckException(dvmThreadSelf()));
+
+ /*
+ * Allocate storage for instruction counters.
+ */
+ gDvm.executedInstrCounts = (int*) malloc(kNumDalvikInstructions * sizeof(int));
+ if (gDvm.executedInstrCounts == NULL)
+ return false;
+ memset(gDvm.executedInstrCounts, 0, kNumDalvikInstructions * sizeof(int));
+
+#ifdef UPDATE_MAGIC_PAGE
+ /*
+ * If we're running on the emulator, there's a magic page into which
+ * we can put interpreted method information. This allows interpreted
+ * methods to show up in the emulator's code traces.
+ *
+ * We could key this off of the "ro.kernel.qemu" property, but there's
+ * no real harm in doing this on a real device.
+ */
+ int fd = open("/dev/qemu_trace", O_RDWR);
+ if (fd < 0) {
+ LOGV("Unable to open /dev/qemu_trace\n");
+ } else {
+ gDvm.emulatorTracePage = mmap(0, SYSTEM_PAGE_SIZE, PROT_READ|PROT_WRITE,
+ MAP_SHARED, fd, 0);
+ close(fd);
+ if (gDvm.emulatorTracePage == MAP_FAILED) {
+ LOGE("Unable to mmap /dev/qemu_trace\n");
+ gDvm.emulatorTracePage = NULL;
+ } else {
+ *(u4*) gDvm.emulatorTracePage = 0;
+ }
+ }
+#else
+ assert(gDvm.emulatorTracePage == NULL);
+#endif
+
+ return true;
+}
+
+/*
+ * Free up profiling resources.
+ */
+void dvmProfilingShutdown(void)
+{
+#ifdef UPDATE_MAGIC_PAGE
+ if (gDvm.emulatorTracePage != NULL)
+ munmap(gDvm.emulatorTracePage, SYSTEM_PAGE_SIZE);
+#endif
+ free(gDvm.executedInstrCounts);
+}
+
+/*
+ * Update the "active profilers" count.
+ *
+ * "count" should be +1 or -1.
+ */
+static void updateActiveProfilers(int count)
+{
+ int oldValue, newValue;
+
+ do {
+ oldValue = gDvm.activeProfilers;
+ newValue = oldValue + count;
+ if (newValue < 0) {
+ LOGE("Can't have %d active profilers\n", newValue);
+ dvmAbort();
+ }
+ } while (android_atomic_release_cas(oldValue, newValue,
+ &gDvm.activeProfilers) != 0);
+
+ LOGD("+++ active profiler count now %d\n", newValue);
+#if defined(WITH_JIT)
+ dvmCompilerStateRefresh();
+#endif
+}
+
+
+/*
+ * Reset the "cpuClockBase" field in all threads.
+ */
+static void resetCpuClockBase(void)
+{
+ Thread* thread;
+
+ dvmLockThreadList(NULL);
+ for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+ thread->cpuClockBaseSet = false;
+ thread->cpuClockBase = 0;
+ }
+ dvmUnlockThreadList();
+}
+
+/*
+ * Dump the thread list to the specified file.
+ */
+static void dumpThreadList(FILE* fp)
+{
+ Thread* thread;
+
+ dvmLockThreadList(NULL);
+ for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+ char* name = dvmGetThreadName(thread);
+
+ fprintf(fp, "%d\t%s\n", thread->threadId, name);
+ free(name);
+ }
+ dvmUnlockThreadList();
+}
+
+/*
+ * This is a dvmHashForeach callback.
+ */
+static int dumpMarkedMethods(void* vclazz, void* vfp)
+{
+ DexStringCache stringCache;
+ ClassObject* clazz = (ClassObject*) vclazz;
+ FILE* fp = (FILE*) vfp;
+ Method* meth;
+ char* name;
+ int i;
+
+ dexStringCacheInit(&stringCache);
+
+ for (i = 0; i < clazz->virtualMethodCount; i++) {
+ meth = &clazz->virtualMethods[i];
+ if (meth->inProfile) {
+ name = dvmDescriptorToName(meth->clazz->descriptor);
+ fprintf(fp, "0x%08x\t%s\t%s\t%s\t%s\t%d\n", (int) meth,
+ name, meth->name,
+ dexProtoGetMethodDescriptor(&meth->prototype, &stringCache),
+ dvmGetMethodSourceFile(meth), dvmLineNumFromPC(meth, 0));
+ meth->inProfile = false;
+ free(name);
+ }
+ }
+
+ for (i = 0; i < clazz->directMethodCount; i++) {
+ meth = &clazz->directMethods[i];
+ if (meth->inProfile) {
+ name = dvmDescriptorToName(meth->clazz->descriptor);
+ fprintf(fp, "0x%08x\t%s\t%s\t%s\t%s\t%d\n", (int) meth,
+ name, meth->name,
+ dexProtoGetMethodDescriptor(&meth->prototype, &stringCache),
+ dvmGetMethodSourceFile(meth), dvmLineNumFromPC(meth, 0));
+ meth->inProfile = false;
+ free(name);
+ }
+ }
+
+ dexStringCacheRelease(&stringCache);
+
+ return 0;
+}
+
+/*
+ * Dump the list of "marked" methods to the specified file.
+ */
+static void dumpMethodList(FILE* fp)
+{
+ dvmHashTableLock(gDvm.loadedClasses);
+ dvmHashForeach(gDvm.loadedClasses, dumpMarkedMethods, (void*) fp);
+ dvmHashTableUnlock(gDvm.loadedClasses);
+}
+
+/*
+ * Start method tracing. Method tracing is global to the VM (i.e. we
+ * trace all threads).
+ *
+ * This opens the output file (if an already open fd has not been supplied,
+ * and we're not going direct to DDMS) and allocates the data buffer. This
+ * takes ownership of the file descriptor, closing it on completion.
+ *
+ * On failure, we throw an exception and return.
+ */
+void dvmMethodTraceStart(const char* traceFileName, int traceFd, int bufferSize,
+ int flags, bool directToDdms)
+{
+ MethodTraceState* state = &gDvm.methodTrace;
+
+ assert(bufferSize > 0);
+
+ dvmLockMutex(&state->startStopLock);
+ while (state->traceEnabled != 0) {
+ LOGI("TRACE start requested, but already in progress; stopping\n");
+ dvmUnlockMutex(&state->startStopLock);
+ dvmMethodTraceStop();
+ dvmLockMutex(&state->startStopLock);
+ }
+ updateActiveProfilers(1);
+ LOGI("TRACE STARTED: '%s' %dKB\n", traceFileName, bufferSize / 1024);
+
+ /*
+ * Allocate storage and open files.
+ *
+ * We don't need to initialize the buffer, but doing so might remove
+ * some fault overhead if the pages aren't mapped until touched.
+ */
+ state->buf = (u1*) malloc(bufferSize);
+ if (state->buf == NULL) {
+ dvmThrowException("Ljava/lang/InternalError;", "buffer alloc failed");
+ goto fail;
+ }
+ if (!directToDdms) {
+ if (traceFd < 0) {
+ state->traceFile = fopen(traceFileName, "w");
+ } else {
+ state->traceFile = fdopen(traceFd, "w");
+ }
+ if (state->traceFile == NULL) {
+ int err = errno;
+ LOGE("Unable to open trace file '%s': %s\n",
+ traceFileName, strerror(err));
+ dvmThrowExceptionFmt("Ljava/lang/RuntimeException;",
+ "Unable to open trace file '%s': %s",
+ traceFileName, strerror(err));
+ goto fail;
+ }
+ }
+ traceFd = -1;
+ memset(state->buf, (char)FILL_PATTERN, bufferSize);
+
+ state->directToDdms = directToDdms;
+ state->bufferSize = bufferSize;
+ state->overflow = false;
+
+ /*
+ * Enable alloc counts if we've been requested to do so.
+ */
+ state->flags = flags;
+ if ((flags & TRACE_ALLOC_COUNTS) != 0)
+ dvmStartAllocCounting();
+
+ /* reset our notion of the start time for all CPU threads */
+ resetCpuClockBase();
+
+ state->startWhen = getTimeInUsec();
+
+ /*
+ * Output the header.
+ */
+ memset(state->buf, 0, TRACE_HEADER_LEN);
+ storeIntLE(state->buf + 0, TRACE_MAGIC);
+ storeShortLE(state->buf + 4, TRACE_VERSION);
+ storeShortLE(state->buf + 6, TRACE_HEADER_LEN);
+ storeLongLE(state->buf + 8, state->startWhen);
+ state->curOffset = TRACE_HEADER_LEN;
+
+ /*
+ * Set the "enabled" flag. Once we do this, threads will wait to be
+ * signaled before exiting, so we have to make sure we wake them up.
+ */
+ android_atomic_release_store(true, &state->traceEnabled);
+ dvmUnlockMutex(&state->startStopLock);
+ return;
+
+fail:
+ updateActiveProfilers(-1);
+ if (state->traceFile != NULL) {
+ fclose(state->traceFile);
+ state->traceFile = NULL;
+ }
+ if (state->buf != NULL) {
+ free(state->buf);
+ state->buf = NULL;
+ }
+ if (traceFd >= 0)
+ close(traceFd);
+ dvmUnlockMutex(&state->startStopLock);
+}
+
+/*
+ * Run through the data buffer and pull out the methods that were visited.
+ * Set a mark so that we know which ones to output.
+ */
+static void markTouchedMethods(int endOffset)
+{
+ u1* ptr = gDvm.methodTrace.buf + TRACE_HEADER_LEN;
+ u1* end = gDvm.methodTrace.buf + endOffset;
+ unsigned int methodVal;
+ Method* method;
+
+ while (ptr < end) {
+ methodVal = *(ptr+1) | (*(ptr+2) << 8) | (*(ptr+3) << 16)
+ | (*(ptr+4) << 24);
+ method = (Method*) METHOD_ID(methodVal);
+
+ method->inProfile = true;
+ ptr += TRACE_REC_SIZE;
+ }
+}
+
+/*
+ * Compute the amount of overhead in a clock call, in nsec.
+ *
+ * This value is going to vary depending on what else is going on in the
+ * system. When examined across several runs a pattern should emerge.
+ */
+static u4 getClockOverhead(void)
+{
+ u8 calStart, calElapsed;
+ int i;
+
+ calStart = getClock();
+ for (i = 1000 * 4; i > 0; i--) {
+ getClock();
+ getClock();
+ getClock();
+ getClock();
+ getClock();
+ getClock();
+ getClock();
+ getClock();
+ }
+
+ calElapsed = getClock() - calStart;
+ return (int) (calElapsed / (8*4));
+}
+
+/*
+ * Returns "true" if method tracing is currently active.
+ */
+bool dvmIsMethodTraceActive(void)
+{
+ const MethodTraceState* state = &gDvm.methodTrace;
+ return state->traceEnabled;
+}
+
+/*
+ * Stop method tracing. We write the buffer to disk and generate a key
+ * file so we can interpret it.
+ */
+void dvmMethodTraceStop(void)
+{
+ MethodTraceState* state = &gDvm.methodTrace;
+ u8 elapsed;
+
+ /*
+ * We need this to prevent somebody from starting a new trace while
+ * we're in the process of stopping the old.
+ */
+ dvmLockMutex(&state->startStopLock);
+
+ if (!state->traceEnabled) {
+ /* somebody already stopped it, or it was never started */
+ LOGD("TRACE stop requested, but not running\n");
+ dvmUnlockMutex(&state->startStopLock);
+ return;
+ } else {
+ updateActiveProfilers(-1);
+ }
+
+ /* compute elapsed time */
+ elapsed = getTimeInUsec() - state->startWhen;
+
+ /*
+ * Globally disable it, and allow other threads to notice. We want
+ * to stall here for at least as long as dvmMethodTraceAdd needs
+ * to finish. There's no real risk though -- it will take a while to
+ * write the data to disk, and we don't clear the buffer pointer until
+ * after that completes.
+ */
+ state->traceEnabled = false;
+ ANDROID_MEMBAR_FULL();
+ sched_yield();
+ usleep(250 * 1000);
+
+ if ((state->flags & TRACE_ALLOC_COUNTS) != 0)
+ dvmStopAllocCounting();
+
+ /*
+ * It's possible under some circumstances for a thread to have advanced
+ * the data pointer but not written the method value. It's possible
+ * (though less likely) for the data pointer to be advanced, or partial
+ * data written, while we're doing work here.
+ *
+ * To avoid seeing partially-written data, we grab state->curOffset here,
+ * and use our local copy from here on. We then scan through what's
+ * already written. If we see the fill pattern in what should be the
+ * method pointer, we cut things off early. (If we don't, we'll fail
+ * when we dereference the pointer.)
+ *
+ * There's a theoretical possibility of interrupting another thread
+ * after it has partially written the method pointer, in which case
+ * we'll likely crash when we dereference it. The possibility of
+ * this actually happening should be at or near zero. Fixing it
+ * completely could be done by writing the thread number last and
+ * using a sentinel value to indicate a partially-written record,
+ * but that requires memory barriers.
+ */
+ int finalCurOffset = state->curOffset;
+
+ if (finalCurOffset > TRACE_HEADER_LEN) {
+ u4 fillVal = METHOD_ID(FILL_PATTERN);
+ u1* scanPtr = state->buf + TRACE_HEADER_LEN;
+
+ while (scanPtr < state->buf + finalCurOffset) {
+ u4 methodVal = scanPtr[1] | (scanPtr[2] << 8) | (scanPtr[3] << 16)
+ | (scanPtr[4] << 24);
+ if (METHOD_ID(methodVal) == fillVal) {
+ u1* scanBase = state->buf + TRACE_HEADER_LEN;
+ LOGW("Found unfilled record at %d (of %d)\n",
+ (scanPtr - scanBase) / TRACE_REC_SIZE,
+ (finalCurOffset - TRACE_HEADER_LEN) / TRACE_REC_SIZE);
+ finalCurOffset = scanPtr - state->buf;
+ break;
+ }
+
+ scanPtr += TRACE_REC_SIZE;
+ }
+ }
+
+ LOGI("TRACE STOPPED%s: writing %d records\n",
+ state->overflow ? " (NOTE: overflowed buffer)" : "",
+ (finalCurOffset - TRACE_HEADER_LEN) / TRACE_REC_SIZE);
+ if (gDvm.debuggerActive) {
+ LOGW("WARNING: a debugger is active; method-tracing results "
+ "will be skewed\n");
+ }
+
+ /*
+ * Do a quick calibration test to see how expensive our clock call is.
+ */
+ u4 clockNsec = getClockOverhead();
+
+ markTouchedMethods(finalCurOffset);
+
+ char* memStreamPtr;
+ size_t memStreamSize;
+ if (state->directToDdms) {
+ assert(state->traceFile == NULL);
+ state->traceFile = open_memstream(&memStreamPtr, &memStreamSize);
+ if (state->traceFile == NULL) {
+ /* not expected */
+ LOGE("Unable to open memstream\n");
+ dvmAbort();
+ }
+ }
+ assert(state->traceFile != NULL);
+
+ fprintf(state->traceFile, "%cversion\n", TOKEN_CHAR);
+ fprintf(state->traceFile, "%d\n", TRACE_VERSION);
+ fprintf(state->traceFile, "data-file-overflow=%s\n",
+ state->overflow ? "true" : "false");
+#if defined(HAVE_POSIX_CLOCKS)
+ if (!gDvm.profilerWallClock) {
+ fprintf(state->traceFile, "clock=thread-cpu\n");
+ } else
+#endif
+ {
+ fprintf(state->traceFile, "clock=wall\n");
+ }
+ fprintf(state->traceFile, "elapsed-time-usec=%llu\n", elapsed);
+ fprintf(state->traceFile, "num-method-calls=%d\n",
+ (finalCurOffset - TRACE_HEADER_LEN) / TRACE_REC_SIZE);
+ fprintf(state->traceFile, "clock-call-overhead-nsec=%d\n", clockNsec);
+ fprintf(state->traceFile, "vm=dalvik\n");
+ if ((state->flags & TRACE_ALLOC_COUNTS) != 0) {
+ fprintf(state->traceFile, "alloc-count=%d\n",
+ gDvm.allocProf.allocCount);
+ fprintf(state->traceFile, "alloc-size=%d\n",
+ gDvm.allocProf.allocSize);
+ fprintf(state->traceFile, "gc-count=%d\n",
+ gDvm.allocProf.gcCount);
+ }
+ fprintf(state->traceFile, "%cthreads\n", TOKEN_CHAR);
+ dumpThreadList(state->traceFile);
+ fprintf(state->traceFile, "%cmethods\n", TOKEN_CHAR);
+ dumpMethodList(state->traceFile);
+ fprintf(state->traceFile, "%cend\n", TOKEN_CHAR);
+
+ if (state->directToDdms) {
+ /*
+ * Data is in two places: memStreamPtr and state->buf. Send
+ * the whole thing to DDMS, wrapped in an MPSE packet.
+ */
+ fflush(state->traceFile);
+
+ struct iovec iov[2];
+ iov[0].iov_base = memStreamPtr;
+ iov[0].iov_len = memStreamSize;
+ iov[1].iov_base = state->buf;
+ iov[1].iov_len = finalCurOffset;
+ dvmDbgDdmSendChunkV(CHUNK_TYPE("MPSE"), iov, 2);
+ } else {
+ /* append the profiling data */
+ if (fwrite(state->buf, finalCurOffset, 1, state->traceFile) != 1) {
+ int err = errno;
+ LOGE("trace fwrite(%d) failed: %s\n",
+ finalCurOffset, strerror(err));
+ dvmThrowExceptionFmt("Ljava/lang/RuntimeException;",
+ "Trace data write failed: %s", strerror(err));
+ }
+ }
+
+ /* done! */
+ free(state->buf);
+ state->buf = NULL;
+ fclose(state->traceFile);
+ state->traceFile = NULL;
+
+ /* wake any threads that were waiting for profiling to complete */
+ dvmBroadcastCond(&state->threadExitCond);
+ dvmUnlockMutex(&state->startStopLock);
+}
+
+
+/*
+ * We just did something with a method. Emit a record.
+ *
+ * Multiple threads may be banging on this all at once. We use atomic ops
+ * rather than mutexes for speed.
+ */
+void dvmMethodTraceAdd(Thread* self, const Method* method, int action)
+{
+ MethodTraceState* state = &gDvm.methodTrace;
+ u4 clockDiff, methodVal;
+ int oldOffset, newOffset;
+ u1* ptr;
+
+ /*
+ * We can only access the per-thread CPU clock from within the
+ * thread, so we have to initialize the base time on the first use.
+ * (Looks like pthread_getcpuclockid(thread, &id) will do what we
+ * want, but it doesn't appear to be defined on the device.)
+ */
+ if (!self->cpuClockBaseSet) {
+ self->cpuClockBase = getClock();
+ self->cpuClockBaseSet = true;
+ //LOGI("thread base id=%d 0x%llx\n",
+ // self->threadId, self->cpuClockBase);
+ }
+
+ /*
+ * Advance "curOffset" atomically.
+ */
+ do {
+ oldOffset = state->curOffset;
+ newOffset = oldOffset + TRACE_REC_SIZE;
+ if (newOffset > state->bufferSize) {
+ state->overflow = true;
+ return;
+ }
+ } while (android_atomic_release_cas(oldOffset, newOffset,
+ &state->curOffset) != 0);
+
+ //assert(METHOD_ACTION((u4) method) == 0);
+
+ u8 now = getClock();
+ clockDiff = (u4) (now - self->cpuClockBase);
+
+ methodVal = METHOD_COMBINE((u4) method, action);
+
+ /*
+ * Write data into "oldOffset".
+ */
+ ptr = state->buf + oldOffset;
+ *ptr++ = self->threadId;
+ *ptr++ = (u1) methodVal;
+ *ptr++ = (u1) (methodVal >> 8);
+ *ptr++ = (u1) (methodVal >> 16);
+ *ptr++ = (u1) (methodVal >> 24);
+ *ptr++ = (u1) clockDiff;
+ *ptr++ = (u1) (clockDiff >> 8);
+ *ptr++ = (u1) (clockDiff >> 16);
+ *ptr++ = (u1) (clockDiff >> 24);
+}
+
+/*
+ * We just did something with a method. Emit a record by setting a value
+ * in a magic memory location.
+ */
+void dvmEmitEmulatorTrace(const Method* method, int action)
+{
+#ifdef UPDATE_MAGIC_PAGE
+ /*
+ * We store the address of the Dalvik bytecodes to the memory-mapped
+ * trace page for normal Java methods. We also trace calls to native
+ * functions by storing the address of the native function to the
+ * trace page.
+ * Abstract methods don't have any bytecodes, so we don't trace them.
+ * (Abstract methods are never called, but in Dalvik they can be
+ * because we do a "late trap" to a native method to generate the
+ * abstract method exception.)
+ */
+ if (dvmIsAbstractMethod(method))
+ return;
+
+ u4* pMagic = (u4*) gDvm.emulatorTracePage;
+ u4 addr;
+
+ if (dvmIsNativeMethod(method)) {
+ /*
+ * The "action" parameter is one of:
+ * 0 = ENTER
+ * 1 = EXIT
+ * 2 = UNROLL
+ * To help the trace tools reconstruct the runtime stack containing
+ * a mix of Java plus native methods, we add 4 to the action if this
+ * is a native method.
+ */
+ action += 4;
+
+ /*
+ * Get the address of the native function.
+ * This isn't the right address -- how do I get it?
+ * Fortunately, the trace tools can get by without the address, but
+ * it would be nice to fix this.
+ */
+ addr = (u4) method->nativeFunc;
+ } else {
+ /*
+ * The dexlist output shows the &DexCode.insns offset value, which
+ * is offset from the start of the base DEX header. Method.insns
+ * is the absolute address, effectively offset from the start of
+ * the optimized DEX header. We either need to return the
+ * optimized DEX base file address offset by the right amount, or
+ * take the "real" address and subtract off the size of the
+ * optimized DEX header.
+ *
+ * Would be nice to factor this out at dexlist time, but we can't count
+ * on having access to the correct optimized DEX file.
+ */
+ assert(method->insns != NULL);
+ const DexOptHeader* pOptHdr = method->clazz->pDvmDex->pDexFile->pOptHeader;
+ addr = (u4) method->insns - pOptHdr->dexOffset;
+ }
+
+ *(pMagic+action) = addr;
+ LOGVV("Set %p = 0x%08x (%s.%s)\n",
+ pMagic+action, addr, method->clazz->descriptor, method->name);
+#endif
+}
+
+/*
+ * The GC calls this when it's about to start. We add a marker to the
+ * trace output so the tool can exclude the GC cost from the results.
+ */
+void dvmMethodTraceGCBegin(void)
+{
+ TRACE_METHOD_ENTER(dvmThreadSelf(), gDvm.methodTrace.gcMethod);
+}
+void dvmMethodTraceGCEnd(void)
+{
+ TRACE_METHOD_EXIT(dvmThreadSelf(), gDvm.methodTrace.gcMethod);
+}
+
+/*
+ * The class loader calls this when it's loading or initializing a class.
+ */
+void dvmMethodTraceClassPrepBegin(void)
+{
+ TRACE_METHOD_ENTER(dvmThreadSelf(), gDvm.methodTrace.classPrepMethod);
+}
+void dvmMethodTraceClassPrepEnd(void)
+{
+ TRACE_METHOD_EXIT(dvmThreadSelf(), gDvm.methodTrace.classPrepMethod);
+}
+
+
+/*
+ * Enable emulator trace info.
+ */
+void dvmEmulatorTraceStart(void)
+{
+ /* If we could not map the emulator trace page, then do not enable tracing */
+ if (gDvm.emulatorTracePage == NULL)
+ return;
+
+ updateActiveProfilers(1);
+
+ /* in theory we should make this an atomic inc; in practice not important */
+ gDvm.emulatorTraceEnableCount++;
+ if (gDvm.emulatorTraceEnableCount == 1)
+ LOGD("--- emulator method traces enabled\n");
+}
+
+/*
+ * Disable emulator trace info.
+ */
+void dvmEmulatorTraceStop(void)
+{
+ if (gDvm.emulatorTraceEnableCount == 0) {
+ LOGE("ERROR: emulator tracing not enabled\n");
+ return;
+ }
+ updateActiveProfilers(-1);
+ /* in theory we should make this an atomic inc; in practice not important */
+ gDvm.emulatorTraceEnableCount--;
+ if (gDvm.emulatorTraceEnableCount == 0)
+ LOGD("--- emulator method traces disabled\n");
+}
+
+
+/*
+ * Start instruction counting.
+ */
+void dvmStartInstructionCounting()
+{
+ updateActiveProfilers(1);
+ /* in theory we should make this an atomic inc; in practice not important */
+ gDvm.instructionCountEnableCount++;
+}
+
+/*
+ * Start instruction counting.
+ */
+void dvmStopInstructionCounting()
+{
+ if (gDvm.instructionCountEnableCount == 0) {
+ LOGE("ERROR: instruction counting not enabled\n");
+ dvmAbort();
+ }
+ updateActiveProfilers(-1);
+ gDvm.instructionCountEnableCount--;
+}
+
+
+/*
+ * Start alloc counting. Note this doesn't affect the "active profilers"
+ * count, since the interpreter loop is not involved.
+ */
+void dvmStartAllocCounting(void)
+{
+ gDvm.allocProf.enabled = true;
+}
+
+/*
+ * Stop alloc counting.
+ */
+void dvmStopAllocCounting(void)
+{
+ gDvm.allocProf.enabled = false;
+}
diff --git a/vm/Profile.h b/vm/Profile.h
new file mode 100644
index 0000000..08bbf61
--- /dev/null
+++ b/vm/Profile.h
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Android's method call profiling goodies.
+ */
+#ifndef _DALVIK_PROFILE
+#define _DALVIK_PROFILE
+
+#ifndef NOT_VM /* for utilities that sneakily include this file */
+
+#include <stdio.h>
+
+/* External allocations are hackish enough that it's worthwhile
+ * separating them for possible removal later.
+ */
+#define PROFILE_EXTERNAL_ALLOCATIONS 1
+
+struct Thread; // extern
+
+
+/* boot init */
+bool dvmProfilingStartup(void);
+void dvmProfilingShutdown(void);
+
+/*
+ * Method trace state. This is currently global. In theory we could make
+ * most of this per-thread.
+ */
+typedef struct MethodTraceState {
+ /* these are set during VM init */
+ Method* gcMethod;
+ Method* classPrepMethod;
+
+ /* active state */
+ pthread_mutex_t startStopLock;
+ pthread_cond_t threadExitCond;
+ FILE* traceFile;
+ bool directToDdms;
+ int bufferSize;
+ int flags;
+
+ int traceEnabled;
+ u1* buf;
+ volatile int curOffset;
+ u8 startWhen;
+ int overflow;
+} MethodTraceState;
+
+/*
+ * Memory allocation profiler state. This is used both globally and
+ * per-thread.
+ *
+ * If you add a field here, zero it out in dvmStartAllocCounting().
+ */
+typedef struct AllocProfState {
+ bool enabled; // is allocation tracking enabled?
+
+ int allocCount; // #of objects allocated
+ int allocSize; // cumulative size of objects
+
+ int failedAllocCount; // #of times an allocation failed
+ int failedAllocSize; // cumulative size of failed allocations
+
+ int freeCount; // #of objects freed
+ int freeSize; // cumulative size of freed objects
+
+ int gcCount; // #of times an allocation triggered a GC
+
+ int classInitCount; // #of initialized classes
+ u8 classInitTime; // cumulative time spent in class init (nsec)
+
+#if PROFILE_EXTERNAL_ALLOCATIONS
+ int externalAllocCount; // #of calls to dvmTrackExternalAllocation()
+ int externalAllocSize; // #of bytes passed to ...ExternalAllocation()
+
+ int failedExternalAllocCount; // #of times an allocation failed
+ int failedExternalAllocSize; // cumulative size of failed allocations
+
+ int externalFreeCount; // #of calls to dvmTrackExternalFree()
+ int externalFreeSize; // #of bytes passed to ...ExternalFree()
+#endif // PROFILE_EXTERNAL_ALLOCATIONS
+} AllocProfState;
+
+
+/*
+ * Start/stop method tracing.
+ */
+void dvmMethodTraceStart(const char* traceFileName, int traceFd, int bufferSize,
+ int flags, bool directToDdms);
+bool dvmIsMethodTraceActive(void);
+void dvmMethodTraceStop(void);
+
+/*
+ * Start/stop emulator tracing.
+ */
+void dvmEmulatorTraceStart(void);
+void dvmEmulatorTraceStop(void);
+
+/*
+ * Start/stop Dalvik instruction counting.
+ */
+void dvmStartInstructionCounting();
+void dvmStopInstructionCounting();
+
+/*
+ * Bit flags for dvmMethodTraceStart "flags" argument. These must match
+ * the values in android.os.Debug.
+ */
+enum {
+ TRACE_ALLOC_COUNTS = 0x01,
+};
+
+/*
+ * Call these when a method enters or exits.
+ */
+#define TRACE_METHOD_ENTER(_self, _method) \
+ do { \
+ if (gDvm.activeProfilers != 0) { \
+ if (gDvm.methodTrace.traceEnabled) \
+ dvmMethodTraceAdd(_self, _method, METHOD_TRACE_ENTER); \
+ if (gDvm.emulatorTraceEnableCount != 0) \
+ dvmEmitEmulatorTrace(_method, METHOD_TRACE_ENTER); \
+ } \
+ } while(0);
+#define TRACE_METHOD_EXIT(_self, _method) \
+ do { \
+ if (gDvm.activeProfilers != 0) { \
+ if (gDvm.methodTrace.traceEnabled) \
+ dvmMethodTraceAdd(_self, _method, METHOD_TRACE_EXIT); \
+ if (gDvm.emulatorTraceEnableCount != 0) \
+ dvmEmitEmulatorTrace(_method, METHOD_TRACE_EXIT); \
+ } \
+ } while(0);
+#define TRACE_METHOD_UNROLL(_self, _method) \
+ do { \
+ if (gDvm.activeProfilers != 0) { \
+ if (gDvm.methodTrace.traceEnabled) \
+ dvmMethodTraceAdd(_self, _method, METHOD_TRACE_UNROLL); \
+ if (gDvm.emulatorTraceEnableCount != 0) \
+ dvmEmitEmulatorTrace(_method, METHOD_TRACE_UNROLL); \
+ } \
+ } while(0);
+
+void dvmMethodTraceAdd(struct Thread* self, const Method* method, int action);
+void dvmEmitEmulatorTrace(const Method* method, int action);
+
+void dvmMethodTraceGCBegin(void);
+void dvmMethodTraceGCEnd(void);
+void dvmMethodTraceClassPrepBegin(void);
+void dvmMethodTraceClassPrepEnd(void);
+
+/*
+ * Start/stop alloc counting.
+ */
+void dvmStartAllocCounting(void);
+void dvmStopAllocCounting(void);
+
+#endif
+
+
+/*
+ * Enumeration for the two "action" bits.
+ */
+enum {
+ METHOD_TRACE_ENTER = 0x00, // method entry
+ METHOD_TRACE_EXIT = 0x01, // method exit
+ METHOD_TRACE_UNROLL = 0x02, // method exited by exception unrolling
+ // 0x03 currently unused
+};
+
+#define TOKEN_CHAR '*'
+#define TRACE_VERSION 1
+
+/*
+ * Common definitions, shared with the dump tool.
+ */
+#define METHOD_ACTION_MASK 0x03 /* two bits */
+#define METHOD_ID(_method) ((_method) & (~METHOD_ACTION_MASK))
+#define METHOD_ACTION(_method) (((unsigned int)(_method)) & METHOD_ACTION_MASK)
+#define METHOD_COMBINE(_method, _action) ((_method) | (_action))
+
+#endif /*_DALVIK_PROFILE*/
diff --git a/vm/Properties.c b/vm/Properties.c
new file mode 100644
index 0000000..288085b
--- /dev/null
+++ b/vm/Properties.c
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Set up values for System.getProperties().
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+#include <sys/utsname.h>
+#include <limits.h>
+#include <unistd.h>
+
+/*
+ * Create some storage for properties read from the command line.
+ */
+bool dvmPropertiesStartup(int maxProps)
+{
+ gDvm.maxProps = maxProps;
+ if (maxProps > 0) {
+ gDvm.propList = (char**) malloc(maxProps * sizeof(char*));
+ if (gDvm.propList == NULL)
+ return false;
+ }
+ gDvm.numProps = 0;
+
+ return true;
+}
+
+/*
+ * Clean up.
+ */
+void dvmPropertiesShutdown(void)
+{
+ int i;
+
+ for (i = 0; i < gDvm.numProps; i++)
+ free(gDvm.propList[i]);
+ free(gDvm.propList);
+ gDvm.propList = NULL;
+}
+
+/*
+ * Add a property specified on the command line. "argStr" has the form
+ * "name=value". "name" must have nonzero length.
+ *
+ * Returns "true" if argStr appears valid.
+ */
+bool dvmAddCommandLineProperty(const char* argStr)
+{
+ char* mangle;
+ char* equals;
+
+ mangle = strdup(argStr);
+ equals = strchr(mangle, '=');
+ if (equals == NULL || equals == mangle) {
+ free(mangle);
+ return false;
+ }
+ *equals = '\0';
+
+ assert(gDvm.numProps < gDvm.maxProps);
+ gDvm.propList[gDvm.numProps++] = mangle;
+
+ return true;
+}
+
+
+/*
+ * Find the "put" method for this class.
+ *
+ * Returns NULL and throws an exception if not found.
+ */
+static Method* getPut(ClassObject* clazz)
+{
+ Method* put;
+
+ put = dvmFindVirtualMethodHierByDescriptor(clazz, "setProperty",
+ "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;");
+ if (put == NULL) {
+ dvmThrowException("Ljava/lang/RuntimeException;",
+ "could not find setProperty(String,String) in Properties");
+ /* fall through to return */
+ }
+ return put;
+}
+
+/*
+ * Set the value of the property.
+ */
+static void setProperty(Object* propObj, Method* put, const char* key,
+ const char* value)
+{
+ StringObject* keyStr;
+ StringObject* valueStr;
+
+ if (value == NULL) {
+ /* unclear what to do; probably want to create prop w/ empty string */
+ value = "";
+ }
+
+ keyStr = dvmCreateStringFromCstr(key);
+ valueStr = dvmCreateStringFromCstr(value);
+ if (keyStr == NULL || valueStr == NULL) {
+ LOGW("setProperty string creation failed\n");
+ goto bail;
+ }
+
+ JValue unused;
+ dvmCallMethod(dvmThreadSelf(), put, propObj, &unused, keyStr, valueStr);
+
+bail:
+ dvmReleaseTrackedAlloc((Object*) keyStr, NULL);
+ dvmReleaseTrackedAlloc((Object*) valueStr, NULL);
+}
+
+/*
+ * Create the VM-default system properties.
+ *
+ * We can do them here, or do them in interpreted code with lots of native
+ * methods to get bits and pieces. This is a bit smaller.
+ */
+void dvmCreateDefaultProperties(Object* propObj)
+{
+ Method* put = getPut(propObj->clazz);
+
+ if (put == NULL)
+ return;
+
+ struct utsname info;
+ uname(&info);
+
+ /* constant strings that are used multiple times below */
+ const char *projectUrl = "http://www.android.com/";
+ const char *projectName = "The Android Project";
+
+ /*
+ * These are listed in the docs.
+ */
+
+ setProperty(propObj, put, "java.boot.class.path", gDvm.bootClassPathStr);
+ setProperty(propObj, put, "java.class.path", gDvm.classPathStr);
+ setProperty(propObj, put, "java.class.version", "46.0");
+ setProperty(propObj, put, "java.compiler", "");
+ setProperty(propObj, put, "java.ext.dirs", "");
+
+ if (getenv("JAVA_HOME") != NULL) {
+ setProperty(propObj, put, "java.home", getenv("JAVA_HOME"));
+ } else {
+ setProperty(propObj, put, "java.home", "/system");
+ }
+
+ setProperty(propObj, put, "java.io.tmpdir", "/tmp");
+ setProperty(propObj, put, "java.library.path", getenv("LD_LIBRARY_PATH"));
+
+ setProperty(propObj, put, "java.net.preferIPv6Addresses", "true");
+
+ setProperty(propObj, put, "java.vendor", projectName);
+ setProperty(propObj, put, "java.vendor.url", projectUrl);
+ setProperty(propObj, put, "java.version", "0");
+ setProperty(propObj, put, "java.vm.name", "Dalvik");
+ setProperty(propObj, put, "java.vm.specification.name",
+ "Dalvik Virtual Machine Specification");
+ setProperty(propObj, put, "java.vm.specification.vendor", projectName);
+ setProperty(propObj, put, "java.vm.specification.version", "0.9");
+ setProperty(propObj, put, "java.vm.vendor", projectName);
+
+ char tmpBuf[64];
+ sprintf(tmpBuf, "%d.%d.%d",
+ DALVIK_MAJOR_VERSION, DALVIK_MINOR_VERSION, DALVIK_BUG_VERSION);
+ setProperty(propObj, put, "java.vm.version", tmpBuf);
+
+ setProperty(propObj, put, "java.specification.name",
+ "Dalvik Core Library");
+ setProperty(propObj, put, "java.specification.vendor", projectName);
+ setProperty(propObj, put, "java.specification.version", "0.9");
+
+ setProperty(propObj, put, "os.arch", info.machine);
+ setProperty(propObj, put, "os.name", info.sysname);
+ setProperty(propObj, put, "os.version", info.release);
+ setProperty(propObj, put, "user.home", getenv("HOME"));
+ setProperty(propObj, put, "user.name", getenv("USER"));
+
+ char path[PATH_MAX];
+ setProperty(propObj, put, "user.dir", getcwd(path, sizeof(path)));
+
+ setProperty(propObj, put, "file.separator", "/");
+ setProperty(propObj, put, "line.separator", "\n");
+ setProperty(propObj, put, "path.separator", ":");
+
+ /*
+ * These show up elsewhere, so do them here too.
+ */
+ setProperty(propObj, put, "java.runtime.name", "Android Runtime");
+ setProperty(propObj, put, "java.runtime.version", "0.9");
+ setProperty(propObj, put, "java.vm.vendor.url", projectUrl);
+
+ setProperty(propObj, put, "file.encoding", "UTF-8");
+ setProperty(propObj, put, "user.language", "en");
+ setProperty(propObj, put, "user.region", "US");
+
+ /*
+ * These are unique to Android/Dalvik.
+ */
+ setProperty(propObj, put, "android.vm.dexfile", "true");
+}
+
+/*
+ * Add anything specified on the command line.
+ */
+void dvmSetCommandLineProperties(Object* propObj)
+{
+ Method* put = getPut(propObj->clazz);
+ int i;
+
+ if (put == NULL)
+ return;
+
+ for (i = 0; i < gDvm.numProps; i++) {
+ const char* value;
+
+ /* value starts after the end of the key string */
+ for (value = gDvm.propList[i]; *value != '\0'; value++)
+ ;
+ setProperty(propObj, put, gDvm.propList[i], value+1);
+ }
+}
+
+/*
+ * Get a property by calling System.getProperty(key).
+ *
+ * Returns a newly-allocated string, or NULL on failure or key not found.
+ * (Unexpected failures will also raise an exception.)
+ */
+char* dvmGetProperty(const char* key)
+{
+ ClassObject* system;
+ Method* getProp;
+ StringObject* keyObj = NULL;
+ StringObject* valueObj;
+ char* result = NULL;
+
+ assert(key != NULL);
+
+ system = dvmFindSystemClass("Ljava/lang/System;");
+ if (system == NULL)
+ goto bail;
+
+ getProp = dvmFindDirectMethodByDescriptor(system, "getProperty",
+ "(Ljava/lang/String;)Ljava/lang/String;");
+ if (getProp == NULL) {
+ LOGW("Could not find getProperty(String) in java.lang.System\n");
+ goto bail;
+ }
+
+ keyObj = dvmCreateStringFromCstr(key);
+ if (keyObj == NULL)
+ goto bail;
+
+ JValue val;
+ dvmCallMethod(dvmThreadSelf(), getProp, NULL, &val, keyObj);
+ valueObj = (StringObject*) val.l;
+ if (valueObj == NULL)
+ goto bail;
+
+ result = dvmCreateCstrFromString(valueObj);
+ /* fall through with result */
+
+bail:
+ dvmReleaseTrackedAlloc((Object*)keyObj, NULL);
+ return result;
+}
diff --git a/vm/Properties.h b/vm/Properties.h
new file mode 100644
index 0000000..f7f2f03
--- /dev/null
+++ b/vm/Properties.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Support for System.getProperties().
+ */
+#ifndef _DALVIK_PROPERTIES
+#define _DALVIK_PROPERTIES
+
+/*
+ * Initialization.
+ */
+bool dvmPropertiesStartup(int maxProps);
+void dvmPropertiesShutdown(void);
+
+/* add "-D" option to list */
+bool dvmAddCommandLineProperty(const char* argStr);
+
+/* called during property initialization */
+void dvmCreateDefaultProperties(Object* propObj);
+void dvmSetCommandLineProperties(Object* propObj);
+
+char* dvmGetProperty(const char* key);
+
+#endif /*_DALVIK_PROPERTIES*/
diff --git a/vm/README.txt b/vm/README.txt
new file mode 100644
index 0000000..1be1a63
--- /dev/null
+++ b/vm/README.txt
@@ -0,0 +1,19 @@
+Dalvik Virtual Machine
+
+
+Source code rules of the road:
+
+- All externally-visible function names must start with "dvm" to avoid
+namespace clashes. Use static functions when possible.
+
+- Do not create static variables (globally or locally). Do not create
+global variables. Keep everything with non-local lifespan in "gDvm",
+defined in Globals.h, so that all global VM state is in one place.
+
+- Use "startup" and "shutdown" functions to clean up gDvm. The VM must
+exit cleanly in valgrind.
+
+- The primary target is ARM Linux. Others are secondary, but must still
+work correctly.
+
+- Use of gcc-specific and C99 constructs is allowed.
diff --git a/vm/RawDexFile.c b/vm/RawDexFile.c
new file mode 100644
index 0000000..3402ff4
--- /dev/null
+++ b/vm/RawDexFile.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Open an unoptimized DEX file.
+ */
+#include "Dalvik.h"
+
+/*
+ * Open an unoptimized DEX file. This finds the optimized version in the
+ * cache, constructing it if necessary.
+ */
+int dvmRawDexFileOpen(const char* fileName, const char* odexOutputName,
+ RawDexFile** ppRawDexFile, bool isBootstrap)
+{
+ // TODO - should be very similar to what JarFile does
+ return -1;
+}
+
+/*
+ * Close a RawDexFile and free the struct.
+ */
+void dvmRawDexFileFree(RawDexFile* pRawDexFile)
+{
+ if (pRawDexFile == NULL)
+ return;
+
+ dvmDexFileFree(pRawDexFile->pDvmDex);
+ free(pRawDexFile->cacheFileName);
+ free(pRawDexFile);
+}
diff --git a/vm/RawDexFile.h b/vm/RawDexFile.h
new file mode 100644
index 0000000..b7c5b33
--- /dev/null
+++ b/vm/RawDexFile.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * This represents a "raw" unswapped, unoptimized DEX file. We don't open
+ * them directly, except to create the optimized version that we tuck in
+ * the cache area.
+ */
+#ifndef _DALVIK_RAWDEXFILE
+#define _DALVIK_RAWDEXFILE
+
+/*
+ * Structure representing a "raw" DEX file, in its unswapped unoptimized
+ * state.
+ */
+typedef struct RawDexFile {
+ char* cacheFileName;
+ DvmDex* pDvmDex;
+} RawDexFile;
+
+/*
+ * Open a raw ".dex" file, optimize it, and load it.
+ *
+ * On success, returns 0 and sets "*ppDexFile" to a newly-allocated DexFile.
+ * On failure, returns a meaningful error code [currently just -1].
+ */
+int dvmRawDexFileOpen(const char* fileName, const char* odexOutputName,
+ RawDexFile** ppDexFile, bool isBootstrap);
+
+/*
+ * Free a RawDexFile structure, along with any associated structures.
+ */
+void dvmRawDexFileFree(RawDexFile* pRawDexFile);
+
+/*
+ * Pry the DexFile out of a RawDexFile.
+ */
+INLINE DvmDex* dvmGetRawDexFileDex(RawDexFile* pRawDexFile) {
+ return pRawDexFile->pDvmDex;
+}
+
+/* get full path of optimized DEX file */
+INLINE const char* dvmGetRawDexFileCacheFileName(RawDexFile* pRawDexFile) {
+ return pRawDexFile->cacheFileName;
+}
+
+#endif /*_DALVIK_RAWDEXFILE*/
diff --git a/vm/ReconfigureDvm.mk b/vm/ReconfigureDvm.mk
new file mode 100644
index 0000000..13138f8
--- /dev/null
+++ b/vm/ReconfigureDvm.mk
@@ -0,0 +1,36 @@
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+include $(CLEAR_VARS)
+
+# Variables used in the included Dvm.mk.
+dvm_os := $(TARGET_OS)
+dvm_arch := $(TARGET_ARCH)
+dvm_arch_variant := $(TARGET_ARCH_VARIANT)
+dvm_simulator := $(TARGET_SIMULATOR)
+
+include $(LOCAL_PATH)/Dvm.mk
+
+LOCAL_SHARED_LIBRARIES += liblog libcutils libnativehelper libz
+ifneq ($(TARGET_SIMULATOR),true)
+LOCAL_SHARED_LIBRARIES += libdl
+endif # !TARGET_SIMULATOR
+
+LOCAL_STATIC_LIBRARIES += libdex
+
+# Don't prelink by default
+LOCAL_PRELINK_MODULE := false
+
+# Don't install on any build by default
+LOCAL_MODULE_TAGS := optional
diff --git a/vm/ReferenceTable.c b/vm/ReferenceTable.c
new file mode 100644
index 0000000..8984d5f
--- /dev/null
+++ b/vm/ReferenceTable.c
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Reference table management.
+ */
+#include "Dalvik.h"
+
+/*
+ * Initialize a ReferenceTable structure.
+ */
+bool dvmInitReferenceTable(ReferenceTable* pRef, int initialCount,
+ int maxCount)
+{
+ assert(initialCount > 0);
+ assert(initialCount <= maxCount);
+
+ pRef->table = (Object**) malloc(initialCount * sizeof(Object*));
+ if (pRef->table == NULL)
+ return false;
+#ifndef NDEBUG
+ memset(pRef->table, 0xdd, initialCount * sizeof(Object*));
+#endif
+ pRef->nextEntry = pRef->table;
+ pRef->allocEntries = initialCount;
+ pRef->maxEntries = maxCount;
+
+ return true;
+}
+
+/*
+ * Clears out the contents of a ReferenceTable, freeing allocated storage.
+ */
+void dvmClearReferenceTable(ReferenceTable* pRef)
+{
+ free(pRef->table);
+ pRef->table = pRef->nextEntry = NULL;
+ pRef->allocEntries = pRef->maxEntries = -1;
+}
+
+/*
+ * Add "obj" to "pRef".
+ */
+bool dvmAddToReferenceTable(ReferenceTable* pRef, Object* obj)
+{
+ assert(dvmIsValidObject(obj));
+ assert(obj != NULL);
+ assert(pRef->table != NULL);
+ assert(pRef->allocEntries <= pRef->maxEntries);
+
+ if (pRef->nextEntry == pRef->table + pRef->allocEntries) {
+ /* reached end of allocated space; did we hit buffer max? */
+ if (pRef->nextEntry == pRef->table + pRef->maxEntries) {
+ LOGW("ReferenceTable overflow (max=%d)\n", pRef->maxEntries);
+ return false;
+ }
+
+ Object** newTable;
+ int newSize;
+
+ newSize = pRef->allocEntries * 2;
+ if (newSize > pRef->maxEntries)
+ newSize = pRef->maxEntries;
+ assert(newSize > pRef->allocEntries);
+
+ newTable = (Object**) realloc(pRef->table, newSize * sizeof(Object*));
+ if (newTable == NULL) {
+ LOGE("Unable to expand ref table (from %d to %d %d-byte entries)\n",
+ pRef->allocEntries, newSize, sizeof(Object*));
+ return false;
+ }
+ LOGVV("Growing %p from %d to %d\n", pRef, pRef->allocEntries, newSize);
+
+ /* update entries; adjust "nextEntry" in case memory moved */
+ pRef->nextEntry = newTable + (pRef->nextEntry - pRef->table);
+ pRef->table = newTable;
+ pRef->allocEntries = newSize;
+ }
+
+ *pRef->nextEntry++ = obj;
+ return true;
+}
+
+/*
+ * Returns NULL if not found.
+ */
+Object** dvmFindInReferenceTable(const ReferenceTable* pRef, Object** bottom,
+ Object* obj)
+{
+ Object** ptr;
+
+ ptr = pRef->nextEntry;
+ while (--ptr >= bottom) {
+ if (*ptr == obj)
+ return ptr;
+ }
+ return NULL;
+}
+
+/*
+ * Remove "obj" from "pRef". We start at the end of the list (where the
+ * most-recently-added element is), and stop searching for a match after
+ * examining the element at "bottom".
+ *
+ * Most of the time "obj" is at or near the end of the list. If not, we
+ * compact it down.
+ */
+bool dvmRemoveFromReferenceTable(ReferenceTable* pRef, Object** bottom,
+ Object* obj)
+{
+ Object** ptr;
+
+ assert(pRef->table != NULL);
+
+ /*
+ * Scan from the most-recently-added entry up to the bottom entry for
+ * this frame.
+ */
+ ptr = dvmFindInReferenceTable(pRef, bottom, obj);
+ if (ptr == NULL)
+ return false;
+
+ /*
+ * Delete the entry.
+ */
+ pRef->nextEntry--;
+ int moveCount = pRef->nextEntry - ptr;
+ if (moveCount != 0) {
+ /* remove from middle, slide the rest down */
+ memmove(ptr, ptr+1, moveCount * sizeof(Object*));
+ //LOGV("LREF delete %p, shift %d down\n", obj, moveCount);
+ } else {
+ /* last entry, falls off the end */
+ //LOGV("LREF delete %p from end\n", obj);
+ }
+
+ return true;
+}
+
+/*
+ * This is a qsort() callback. We sort Object* by class, allocation size,
+ * and then by the Object* itself.
+ */
+static int compareObject(const void* vobj1, const void* vobj2)
+{
+ Object* obj1 = *((Object**) vobj1);
+ Object* obj2 = *((Object**) vobj2);
+
+ if (obj1 == NULL || obj2 == NULL)
+ return (u1*)obj1 - (u1*)obj2;
+
+ if (obj1->clazz != obj2->clazz) {
+ return (u1*)obj1->clazz - (u1*)obj2->clazz;
+ } else {
+ int size1 = dvmObjectSizeInHeap(obj1);
+ int size2 = dvmObjectSizeInHeap(obj2);
+ if (size1 != size2) {
+ return size1 - size2;
+ } else {
+ return (u1*)obj1 - (u1*)obj2;
+ }
+ }
+}
+
+/*
+ * Log an object with some additional info.
+ *
+ * Pass in the number of additional elements that are identical to or
+ * equivalent to the original.
+ */
+static void logObject(Object* obj, int size, int identical, int equiv)
+{
+ if (obj == NULL) {
+ LOGW(" NULL reference (count=%d)\n", equiv);
+ return;
+ }
+
+ /* handle "raw" dvmMalloc case */
+ const char* descriptor =
+ (obj->clazz != NULL) ? obj->clazz->descriptor : "(raw)";
+
+ if (identical + equiv != 0) {
+ LOGW("%5d of %s %dB (%d unique)\n", identical + equiv +1,
+ descriptor, size, equiv +1);
+ } else {
+ LOGW("%5d of %s %dB\n", identical + equiv +1, descriptor, size);
+ }
+}
+
+/*
+ * Dump the contents of a ReferenceTable to the log.
+ *
+ * The caller should lock any external sync before calling.
+ *
+ * (This was originally written to be tolerant of null entries in the table.
+ * I don't think that can happen anymore.)
+ */
+void dvmDumpReferenceTable(const ReferenceTable* pRef, const char* descr)
+{
+ const int kLast = 10;
+ int count = dvmReferenceTableEntries(pRef);
+ Object** refs;
+ int i;
+
+ if (count == 0) {
+ LOGW("%s reference table has no entries\n", descr);
+ return;
+ }
+ assert(count > 0);
+
+ /*
+ * Dump the most recent N entries.
+ */
+ LOGW("Last %d entries in %s reference table:\n", kLast, descr);
+ refs = pRef->table; // use unsorted list
+ int size;
+ int start = count - kLast;
+ if (start < 0)
+ start = 0;
+
+ for (i = start; i < count; i++) {
+ size = (refs[i] == NULL) ? 0 : dvmObjectSizeInHeap(refs[i]);
+ Object* ref = refs[i];
+ if (ref->clazz == gDvm.classJavaLangClass) {
+ ClassObject* clazz = (ClassObject*) ref;
+ LOGW("%5d: %p cls=%s '%s' (%d bytes)\n", i, ref,
+ (refs[i] == NULL) ? "-" : ref->clazz->descriptor,
+ clazz->descriptor, size);
+ } else if (ref->clazz == NULL) {
+ /* should only be possible right after a plain dvmMalloc() */
+ LOGW("%5d: %p cls=(raw) (%d bytes)\n", i, ref, size);
+ } else {
+ LOGW("%5d: %p cls=%s (%d bytes)\n", i, ref,
+ (refs[i] == NULL) ? "-" : ref->clazz->descriptor, size);
+ }
+ }
+
+ /*
+ * Make a copy of the table, and sort it.
+ */
+ Object** tableCopy = (Object**)malloc(sizeof(Object*) * count);
+ memcpy(tableCopy, pRef->table, sizeof(Object*) * count);
+ qsort(tableCopy, count, sizeof(Object*), compareObject);
+ refs = tableCopy; // use sorted list
+
+ /*
+ * Dump uniquified table summary. While we're at it, generate a
+ * cumulative total amount of pinned memory based on the unique entries.
+ */
+ LOGW("%s reference table summary (%d entries):\n", descr, count);
+ int equiv, identical, total;
+ total = equiv = identical = 0;
+ for (i = 1; i < count; i++) {
+ size = (refs[i-1] == NULL) ? 0 : dvmObjectSizeInHeap(refs[i-1]);
+
+ if (refs[i] == refs[i-1]) {
+ /* same reference, added more than once */
+ identical++;
+ } else if (refs[i]->clazz == refs[i-1]->clazz &&
+ (int) dvmObjectSizeInHeap(refs[i]) == size)
+ {
+ /* same class / size, different object */
+ total += size;
+ equiv++;
+ } else {
+ /* different class */
+ total += size;
+ logObject(refs[i-1], size, identical, equiv);
+ equiv = identical = 0;
+ }
+ }
+
+ /* handle the last entry (everything above outputs refs[i-1]) */
+ size = (refs[count-1] == NULL) ? 0 : dvmObjectSizeInHeap(refs[count-1]);
+ total += size;
+ logObject(refs[count-1], size, identical, equiv);
+
+ LOGW("Memory held directly by tracked refs is %d bytes\n", total);
+ free(tableCopy);
+}
diff --git a/vm/ReferenceTable.h b/vm/ReferenceTable.h
new file mode 100644
index 0000000..d6e2d70
--- /dev/null
+++ b/vm/ReferenceTable.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Maintain a table of references. Used for internal local references,
+ * JNI locals, JNI globals, and GC heap references.
+ *
+ * None of the table functions are synchronized.
+ */
+#ifndef _DALVIK_REFERENCETABLE
+#define _DALVIK_REFERENCETABLE
+
+/*
+ * Table definition.
+ *
+ * The expected common operations are adding a new entry and removing a
+ * recently-added entry (usually the most-recently-added entry).
+ *
+ * If "allocEntries" is not equal to "maxEntries", the table may expand when
+ * entries are added, which means the memory may move. If you want to keep
+ * pointers into "table" rather than offsets, use a fixed-size table.
+ *
+ * (This structure is still somewhat transparent; direct access to
+ * table/nextEntry is allowed.)
+ */
+typedef struct ReferenceTable {
+ Object** nextEntry; /* top of the list */
+ Object** table; /* bottom of the list */
+
+ int allocEntries; /* #of entries we have space for */
+ int maxEntries; /* max #of entries allowed */
+} ReferenceTable;
+
+/*
+ * Initialize a ReferenceTable.
+ *
+ * If "initialCount" != "maxCount", the table will expand as required.
+ *
+ * Returns "false" if table allocation fails.
+ */
+bool dvmInitReferenceTable(ReferenceTable* pRef, int initialCount,
+ int maxCount);
+
+/*
+ * Clears out the contents of a ReferenceTable, freeing allocated storage.
+ * Does not free "pRef".
+ *
+ * You must call dvmInitReferenceTable() before you can re-use this table.
+ */
+void dvmClearReferenceTable(ReferenceTable* pRef);
+
+/*
+ * Return the #of entries currently stored in the ReferenceTable.
+ */
+INLINE size_t dvmReferenceTableEntries(const ReferenceTable* pRef)
+{
+ return pRef->nextEntry - pRef->table;
+}
+
+/*
+ * Returns "true" if the table is full. The table is considered full if
+ * we would need to expand it to add another entry.
+ */
+INLINE size_t dvmIsReferenceTableFull(const ReferenceTable* pRef)
+{
+ return dvmReferenceTableEntries(pRef) == (size_t)pRef->allocEntries;
+}
+
+/*
+ * Add a new entry. "obj" must be a valid non-NULL object reference
+ * (though it's okay if it's not fully-formed, e.g. the result from
+ * dvmMalloc doesn't have obj->clazz set).
+ *
+ * Returns "false" if the table is full.
+ */
+bool dvmAddToReferenceTable(ReferenceTable* pRef, Object* obj);
+
+/*
+ * Determine if "obj" is present in "pRef". Stops searching when we hit
+ * "bottom". To include the entire table, pass in "pRef->table" as the
+ * bottom.
+ *
+ * Returns NULL if "obj" was not found.
+ */
+Object** dvmFindInReferenceTable(const ReferenceTable* pRef, Object** bottom,
+ Object* obj);
+
+/*
+ * Remove an existing entry.
+ *
+ * We stop searching for a match after examining the element at "bottom".
+ * This is useful when entries are associated with a stack frame.
+ *
+ * Returns "false" if the entry was not found.
+ */
+bool dvmRemoveFromReferenceTable(ReferenceTable* pRef, Object** bottom,
+ Object* obj);
+
+/*
+ * Dump the contents of a reference table to the log file.
+ */
+void dvmDumpReferenceTable(const ReferenceTable* pRef, const char* descr);
+
+#endif /*_DALVIK_REFERENCETABLE*/
diff --git a/vm/SignalCatcher.c b/vm/SignalCatcher.c
new file mode 100644
index 0000000..d270b6f
--- /dev/null
+++ b/vm/SignalCatcher.c
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This is a thread that catches signals and does something useful. For
+ * example, when a SIGQUIT (Ctrl-\) arrives, suspend the VM and dump the
+ * status of all threads.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <pthread.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <cutils/open_memstream.h>
+
+static void* signalCatcherThreadStart(void* arg);
+
+/*
+ * Crank up the signal catcher thread.
+ *
+ * Returns immediately.
+ */
+bool dvmSignalCatcherStartup(void)
+{
+ gDvm.haltSignalCatcher = false;
+
+ if (!dvmCreateInternalThread(&gDvm.signalCatcherHandle,
+ "Signal Catcher", signalCatcherThreadStart, NULL))
+ return false;
+
+ return true;
+}
+
+/*
+ * Shut down the signal catcher thread if it was started.
+ *
+ * Since we know the thread is just sitting around waiting for signals
+ * to arrive, send it one.
+ */
+void dvmSignalCatcherShutdown(void)
+{
+ gDvm.haltSignalCatcher = true;
+ if (gDvm.signalCatcherHandle == 0) // not started yet
+ return;
+
+ pthread_kill(gDvm.signalCatcherHandle, SIGQUIT);
+
+ pthread_join(gDvm.signalCatcherHandle, NULL);
+ LOGV("signal catcher has shut down\n");
+}
+
+
+/*
+ * Print the name of the current process, if we can get it.
+ */
+static void printProcessName(const DebugOutputTarget* target)
+{
+ int fd = -1;
+
+ fd = open("/proc/self/cmdline", O_RDONLY, 0);
+ if (fd < 0)
+ goto bail;
+
+ char tmpBuf[256];
+ ssize_t actual;
+
+ actual = read(fd, tmpBuf, sizeof(tmpBuf)-1);
+ if (actual <= 0)
+ goto bail;
+
+ tmpBuf[actual] = '\0';
+ dvmPrintDebugMessage(target, "Cmd line: %s\n", tmpBuf);
+
+bail:
+ if (fd >= 0)
+ close(fd);
+}
+
+/*
+ * Dump the stack traces for all threads to the supplied file, putting
+ * a timestamp header on it.
+ */
+static void logThreadStacks(FILE* fp)
+{
+ DebugOutputTarget target;
+
+ dvmCreateFileOutputTarget(&target, fp);
+
+ pid_t pid = getpid();
+ time_t now = time(NULL);
+ struct tm* ptm;
+#ifdef HAVE_LOCALTIME_R
+ struct tm tmbuf;
+ ptm = localtime_r(&now, &tmbuf);
+#else
+ ptm = localtime(&now);
+#endif
+ dvmPrintDebugMessage(&target,
+ "\n\n----- pid %d at %04d-%02d-%02d %02d:%02d:%02d -----\n",
+ pid, ptm->tm_year + 1900, ptm->tm_mon+1, ptm->tm_mday,
+ ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
+ printProcessName(&target);
+ dvmPrintDebugMessage(&target, "\n");
+ dvmDumpAllThreadsEx(&target, true);
+ fprintf(fp, "----- end %d -----\n", pid);
+}
+
+
+/*
+ * Respond to a SIGQUIT by dumping the thread stacks. Optionally dump
+ * a few other things while we're at it.
+ *
+ * Thread stacks can either go to the log or to a file designated for holding
+ * ANR traces. If we're writing to a file, we want to do it in one shot,
+ * so we can use a single O_APPEND write instead of contending for exclusive
+ * access with flock(). There may be an advantage in resuming the VM
+ * before doing the file write, so we don't stall the VM if disk I/O is
+ * bottlenecked.
+ *
+ * If JIT tuning is compiled in, dump compiler stats as well.
+ */
+static void handleSigQuit(void)
+{
+ char* traceBuf = NULL;
+ size_t traceLen;
+
+ dvmSuspendAllThreads(SUSPEND_FOR_STACK_DUMP);
+
+ dvmDumpLoaderStats("sig");
+
+ if (gDvm.stackTraceFile == NULL) {
+ /* just dump to log */
+ DebugOutputTarget target;
+ dvmCreateLogOutputTarget(&target, ANDROID_LOG_INFO, LOG_TAG);
+ dvmDumpAllThreadsEx(&target, true);
+ } else {
+ /* write to memory buffer */
+ FILE* memfp = open_memstream(&traceBuf, &traceLen);
+ if (memfp == NULL) {
+ LOGE("Unable to create memstream for stack traces\n");
+ traceBuf = NULL; /* make sure it didn't touch this */
+ /* continue on */
+ } else {
+ logThreadStacks(memfp);
+ fclose(memfp);
+ }
+ }
+
+#if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
+ dvmCompilerDumpStats();
+#endif
+
+ if (false) {
+ dvmLockMutex(&gDvm.jniGlobalRefLock);
+ dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global");
+ dvmUnlockMutex(&gDvm.jniGlobalRefLock);
+ }
+ if (false) dvmDumpTrackedAllocations(true);
+
+ dvmResumeAllThreads(SUSPEND_FOR_STACK_DUMP);
+
+ if (traceBuf != NULL) {
+ /*
+ * We don't know how long it will take to do the disk I/O, so put us
+ * into VMWAIT for the duration.
+ */
+ int oldStatus = dvmChangeStatus(dvmThreadSelf(), THREAD_VMWAIT);
+
+ /*
+ * Open the stack trace output file, creating it if necessary. It
+ * needs to be world-writable so other processes can write to it.
+ */
+ int fd = open(gDvm.stackTraceFile, O_WRONLY | O_APPEND | O_CREAT, 0666);
+ if (fd < 0) {
+ LOGE("Unable to open stack trace file '%s': %s\n",
+ gDvm.stackTraceFile, strerror(errno));
+ } else {
+ ssize_t actual = write(fd, traceBuf, traceLen);
+ if (actual != (ssize_t) traceLen) {
+ LOGE("Failed to write stack traces to %s (%d of %zd): %s\n",
+ gDvm.stackTraceFile, (int) actual, traceLen,
+ strerror(errno));
+ } else {
+ LOGI("Wrote stack traces to '%s'\n", gDvm.stackTraceFile);
+ }
+ close(fd);
+ }
+
+ free(traceBuf);
+ dvmChangeStatus(dvmThreadSelf(), oldStatus);
+ }
+}
+
+/*
+ * Respond to a SIGUSR1 by forcing a GC.
+ */
+static void handleSigUsr1(void)
+{
+ LOGI("SIGUSR1 forcing GC (no HPROF)\n");
+ dvmCollectGarbage(false);
+}
+
+#if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
+/*
+ * Respond to a SIGUSR2 by dumping some JIT stats and possibly resetting
+ * the code cache.
+ */
+static void handleSigUsr2(void)
+{
+ static int codeCacheResetCount = 0;
+ if ((--codeCacheResetCount & 7) == 0) {
+ gDvmJit.codeCacheFull = true;
+ } else {
+ dvmCompilerDumpStats();
+ /* Stress-test unchain all */
+ dvmJitUnchainAll();
+ LOGD("Send %d more signals to rest the code cache",
+ codeCacheResetCount & 7);
+ }
+}
+#endif
+
+/*
+ * Sleep in sigwait() until a signal arrives.
+ */
+static void* signalCatcherThreadStart(void* arg)
+{
+ Thread* self = dvmThreadSelf();
+ sigset_t mask;
+ int cc;
+
+ UNUSED_PARAMETER(arg);
+
+ LOGV("Signal catcher thread started (threadid=%d)\n", self->threadId);
+
+ /* set up mask with signals we want to handle */
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGQUIT);
+ sigaddset(&mask, SIGUSR1);
+#if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
+ sigaddset(&mask, SIGUSR2);
+#endif
+
+ while (true) {
+ int rcvd;
+
+ dvmChangeStatus(self, THREAD_VMWAIT);
+
+ /*
+ * Signals for sigwait() must be blocked but not ignored. We
+ * block signals like SIGQUIT for all threads, so the condition
+ * is met. When the signal hits, we wake up, without any signal
+ * handlers being invoked.
+ *
+ * When running under GDB we occasionally return from sigwait()
+ * with EINTR (e.g. when other threads exit).
+ */
+loop:
+ cc = sigwait(&mask, &rcvd);
+ if (cc != 0) {
+ if (cc == EINTR) {
+ //LOGV("sigwait: EINTR\n");
+ goto loop;
+ }
+ assert(!"bad result from sigwait");
+ }
+
+ if (!gDvm.haltSignalCatcher) {
+ LOGI("threadid=%d: reacting to signal %d\n",
+ dvmThreadSelf()->threadId, rcvd);
+ }
+
+ /* set our status to RUNNING, self-suspending if GC in progress */
+ dvmChangeStatus(self, THREAD_RUNNING);
+
+ if (gDvm.haltSignalCatcher)
+ break;
+
+ switch (rcvd) {
+ case SIGQUIT:
+ handleSigQuit();
+ break;
+ case SIGUSR1:
+ handleSigUsr1();
+ break;
+#if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
+ case SIGUSR2:
+ handleSigUsr2();
+ break;
+#endif
+ default:
+ LOGE("unexpected signal %d\n", rcvd);
+ break;
+ }
+ }
+
+ return NULL;
+}
diff --git a/vm/SignalCatcher.h b/vm/SignalCatcher.h
new file mode 100644
index 0000000..ece052c
--- /dev/null
+++ b/vm/SignalCatcher.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Signal catcher thread.
+ */
+#ifndef _DALVIK_SIGNALCATCHER
+#define _DALVIK_SIGNALCATCHER
+
+bool dvmSignalCatcherStartup(void);
+void dvmSignalCatcherShutdown(void);
+
+#endif /*_DALVIK_SIGNALCATCHER*/
diff --git a/vm/StdioConverter.c b/vm/StdioConverter.c
new file mode 100644
index 0000000..6a4d845
--- /dev/null
+++ b/vm/StdioConverter.c
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Thread that reads from stdout/stderr and converts them to log messages.
+ * (Sort of a hack.)
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#define kFilenoStdout 1
+#define kFilenoStderr 2
+
+/*
+ * Hold our replacement stdout/stderr.
+ */
+typedef struct StdPipes {
+ int stdoutPipe[2];
+ int stderrPipe[2];
+} StdPipes;
+
+#define kMaxLine 512
+
+/*
+ * Hold some data.
+ */
+typedef struct BufferedData {
+ char buf[kMaxLine+1];
+ int count;
+} BufferedData;
+
+// fwd
+static void* stdioConverterThreadStart(void* arg);
+static bool readAndLog(int fd, BufferedData* data, const char* tag);
+
+
+/*
+ * Crank up the stdout/stderr converter thread.
+ *
+ * Returns immediately.
+ */
+bool dvmStdioConverterStartup(void)
+{
+ StdPipes* pipeStorage;
+
+ gDvm.haltStdioConverter = false;
+
+ dvmInitMutex(&gDvm.stdioConverterLock);
+ pthread_cond_init(&gDvm.stdioConverterCond, NULL);
+
+ pipeStorage = (StdPipes*) malloc(sizeof(StdPipes));
+ if (pipeStorage == NULL)
+ return false;
+
+ if (pipe(pipeStorage->stdoutPipe) != 0) {
+ LOGW("pipe failed: %s\n", strerror(errno));
+ return false;
+ }
+ if (pipe(pipeStorage->stderrPipe) != 0) {
+ LOGW("pipe failed: %s\n", strerror(errno));
+ return false;
+ }
+
+ if (dup2(pipeStorage->stdoutPipe[1], kFilenoStdout) != kFilenoStdout) {
+ LOGW("dup2(1) failed: %s\n", strerror(errno));
+ return false;
+ }
+ close(pipeStorage->stdoutPipe[1]);
+ pipeStorage->stdoutPipe[1] = -1;
+#ifdef HAVE_ANDROID_OS
+ /* don't redirect stderr on sim -- logs get written there! */
+ /* (don't need this on the sim anyway) */
+ if (dup2(pipeStorage->stderrPipe[1], kFilenoStderr) != kFilenoStderr) {
+ LOGW("dup2(2) failed: %d %s\n", errno, strerror(errno));
+ return false;
+ }
+ close(pipeStorage->stderrPipe[1]);
+ pipeStorage->stderrPipe[1] = -1;
+#endif
+
+
+ /*
+ * Create the thread.
+ */
+ dvmLockMutex(&gDvm.stdioConverterLock);
+
+ if (!dvmCreateInternalThread(&gDvm.stdioConverterHandle,
+ "Stdio Converter", stdioConverterThreadStart, pipeStorage))
+ {
+ free(pipeStorage);
+ return false;
+ }
+ /* new thread owns pipeStorage */
+
+ while (!gDvm.stdioConverterReady) {
+ dvmWaitCond(&gDvm.stdioConverterCond, &gDvm.stdioConverterLock);
+ }
+ dvmUnlockMutex(&gDvm.stdioConverterLock);
+
+ return true;
+}
+
+/*
+ * Shut down the stdio converter thread if it was started.
+ *
+ * Since we know the thread is just sitting around waiting for something
+ * to arrive on stdout, print something.
+ */
+void dvmStdioConverterShutdown(void)
+{
+ gDvm.haltStdioConverter = true;
+ if (gDvm.stdioConverterHandle == 0) // not started, or still starting
+ return;
+
+ /* print something to wake it up */
+ printf("Shutting down\n");
+ fflush(stdout);
+
+ LOGD("Joining stdio converter...\n");
+ pthread_join(gDvm.stdioConverterHandle, NULL);
+}
+
+/*
+ * Select on stdout/stderr pipes, waiting for activity.
+ *
+ * DO NOT use printf from here.
+ */
+static void* stdioConverterThreadStart(void* arg)
+{
+ StdPipes* pipeStorage = (StdPipes*) arg;
+ BufferedData* stdoutData;
+ BufferedData* stderrData;
+ int cc;
+
+ /* tell the main thread that we're ready */
+ dvmLockMutex(&gDvm.stdioConverterLock);
+ gDvm.stdioConverterReady = true;
+ cc = pthread_cond_signal(&gDvm.stdioConverterCond);
+ assert(cc == 0);
+ dvmUnlockMutex(&gDvm.stdioConverterLock);
+
+ /* we never do anything that affects the rest of the VM */
+ dvmChangeStatus(NULL, THREAD_VMWAIT);
+
+ /*
+ * Allocate read buffers.
+ */
+ stdoutData = (BufferedData*) malloc(sizeof(*stdoutData));
+ stderrData = (BufferedData*) malloc(sizeof(*stderrData));
+ stdoutData->count = stderrData->count = 0;
+
+ /*
+ * Read until shutdown time.
+ */
+ while (!gDvm.haltStdioConverter) {
+ fd_set readfds;
+ int maxFd, fdCount;
+
+ FD_ZERO(&readfds);
+ FD_SET(pipeStorage->stdoutPipe[0], &readfds);
+ FD_SET(pipeStorage->stderrPipe[0], &readfds);
+ maxFd = MAX(pipeStorage->stdoutPipe[0], pipeStorage->stderrPipe[0]);
+
+ fdCount = select(maxFd+1, &readfds, NULL, NULL, NULL);
+
+ if (fdCount < 0) {
+ if (errno != EINTR) {
+ LOGE("select on stdout/stderr failed\n");
+ break;
+ }
+ LOGD("Got EINTR, ignoring\n");
+ } else if (fdCount == 0) {
+ LOGD("WEIRD: select returned zero\n");
+ } else {
+ bool err = false;
+ if (FD_ISSET(pipeStorage->stdoutPipe[0], &readfds)) {
+ err |= !readAndLog(pipeStorage->stdoutPipe[0], stdoutData,
+ "stdout");
+ }
+ if (FD_ISSET(pipeStorage->stderrPipe[0], &readfds)) {
+ err |= !readAndLog(pipeStorage->stderrPipe[0], stderrData,
+ "stderr");
+ }
+
+ /* probably EOF; give up */
+ if (err) {
+ LOGW("stdio converter got read error; shutting it down\n");
+ break;
+ }
+ }
+ }
+
+ close(pipeStorage->stdoutPipe[0]);
+ close(pipeStorage->stderrPipe[0]);
+
+ free(pipeStorage);
+ free(stdoutData);
+ free(stderrData);
+
+ /* change back for shutdown sequence */
+ dvmChangeStatus(NULL, THREAD_RUNNING);
+ return NULL;
+}
+
+/*
+ * Data is pending on "fd". Read as much as will fit in "data", then
+ * write out any full lines and compact "data".
+ */
+static bool readAndLog(int fd, BufferedData* data, const char* tag)
+{
+ ssize_t actual;
+ size_t want;
+
+ assert(data->count < kMaxLine);
+
+ want = kMaxLine - data->count;
+ actual = read(fd, data->buf + data->count, want);
+ if (actual <= 0) {
+ LOGW("read %s: (%d,%d) failed (%d): %s\n",
+ tag, fd, want, (int)actual, strerror(errno));
+ return false;
+ } else {
+ //LOGI("read %s: %d at %d\n", tag, actual, data->count);
+ }
+ data->count += actual;
+
+ /*
+ * Got more data, look for an EOL. We expect LF or CRLF, but will
+ * try to handle a standalone CR.
+ */
+ char* cp = data->buf;
+ const char* start = data->buf;
+ int i = data->count;
+ for (i = data->count; i > 0; i--, cp++) {
+ if (*cp == '\n' || (*cp == '\r' && i != 0 && *(cp+1) != '\n')) {
+ *cp = '\0';
+ //LOGW("GOT %d at %d '%s'\n", cp - start, start - data->buf, start);
+ LOG(LOG_INFO, tag, "%s", start);
+ start = cp+1;
+ }
+ }
+
+ /*
+ * See if we overflowed. If so, cut it off.
+ */
+ if (start == data->buf && data->count == kMaxLine) {
+ data->buf[kMaxLine] = '\0';
+ LOG(LOG_INFO, tag, "%s!", start);
+ start = cp + kMaxLine;
+ }
+
+ /*
+ * Update "data" if we consumed some output. If there's anything left
+ * in the buffer, it's because we didn't see an EOL and need to keep
+ * reading until we see one.
+ */
+ if (start != data->buf) {
+ if (start >= data->buf + data->count) {
+ /* consumed all available */
+ data->count = 0;
+ } else {
+ /* some left over */
+ int remaining = data->count - (start - data->buf);
+ memmove(data->buf, start, remaining);
+ data->count = remaining;
+ }
+ }
+
+ return true;
+}
diff --git a/vm/StdioConverter.h b/vm/StdioConverter.h
new file mode 100644
index 0000000..ffbf807
--- /dev/null
+++ b/vm/StdioConverter.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Stdout/stderr conversion thread.
+ */
+#ifndef _DALVIK_STDOUTCONVERTER
+#define _DALVIK_STDOUTCONVERTER
+
+bool dvmStdioConverterStartup(void);
+void dvmStdioConverterShutdown(void);
+
+#endif /*_DALVIK_STDOUTCONVERTER*/
diff --git a/vm/Sync.c b/vm/Sync.c
new file mode 100644
index 0000000..b771b1c
--- /dev/null
+++ b/vm/Sync.c
@@ -0,0 +1,2129 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Fundamental synchronization mechanisms.
+ *
+ * The top part of the file has operations on "monitor" structs; the
+ * next part has the native calls on objects.
+ *
+ * The current implementation uses "thin locking" to avoid allocating
+ * an Object's full Monitor struct until absolutely necessary (i.e.,
+ * during contention or a call to wait()).
+ *
+ * TODO: make improvements to thin locking
+ * We may be able to improve performance and reduce memory requirements by:
+ * - reverting to a thin lock once the Monitor is no longer necessary
+ * - using a pool of monitor objects, with some sort of recycling scheme
+ *
+ * TODO: recycle native-level monitors when objects are garbage collected.
+ */
+#include "Dalvik.h"
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <time.h>
+#include <sys/time.h>
+#include <errno.h>
+
+#define LOG_THIN LOGV
+
+#ifdef WITH_DEADLOCK_PREDICTION /* fwd */
+static const char* kStartBanner =
+ "<-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#";
+static const char* kEndBanner =
+ "#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#->";
+
+/*
+ * Unsorted, expanding list of objects.
+ *
+ * This is very similar to PointerSet (which came into existence after this),
+ * but these are unsorted, uniqueness is not enforced by the "add" function,
+ * and the base object isn't allocated on the heap.
+ */
+typedef struct ExpandingObjectList {
+ u2 alloc;
+ u2 count;
+ Object** list;
+} ExpandingObjectList;
+
+/* fwd */
+static void updateDeadlockPrediction(Thread* self, Object* obj);
+static void removeCollectedObject(Object* obj);
+static void expandObjClear(ExpandingObjectList* pList);
+#endif
+
+/*
+ * Every Object has a monitor associated with it, but not every Object is
+ * actually locked. Even the ones that are locked do not need a
+ * full-fledged monitor until a) there is actual contention or b) wait()
+ * is called on the Object.
+ *
+ * For Dalvik, we have implemented a scheme similar to the one described
+ * in Bacon et al.'s "Thin locks: featherweight synchronization for Java"
+ * (ACM 1998). Things are even easier for us, though, because we have
+ * a full 32 bits to work with.
+ *
+ * The two states of an Object's lock are referred to as "thin" and
+ * "fat". A lock may transition from the "thin" state to the "fat"
+ * state and this transition is referred to as inflation. Once a lock
+ * has been inflated it remains in the "fat" state indefinitely.
+ *
+ * The lock value itself is stored in Object.lock. The LSB of the
+ * lock encodes its state. When cleared, the lock is in the "thin"
+ * state and its bits are formatted as follows:
+ *
+ * [31 ---- 19] [18 ---- 3] [2 ---- 1] [0]
+ * lock count thread id hash state 0
+ *
+ * When set, the lock is in the "fat" state and its bits are formatted
+ * as follows:
+ *
+ * [31 ---- 3] [2 ---- 1] [0]
+ * pointer hash state 1
+ *
+ * For an in-depth description of the mechanics of thin-vs-fat locking,
+ * read the paper referred to above.
+ */
+
+/*
+ * Monitors provide:
+ * - mutually exclusive access to resources
+ * - a way for multiple threads to wait for notification
+ *
+ * In effect, they fill the role of both mutexes and condition variables.
+ *
+ * Only one thread can own the monitor at any time. There may be several
+ * threads waiting on it (the wait call unlocks it). One or more waiting
+ * threads may be getting interrupted or notified at any given time.
+ *
+ * TODO: the various members of monitor are not SMP-safe.
+ */
+struct Monitor {
+ Thread* owner; /* which thread currently owns the lock? */
+ int lockCount; /* owner's recursive lock depth */
+ Object* obj; /* what object are we part of [debug only] */
+
+ Thread* waitSet; /* threads currently waiting on this monitor */
+
+ pthread_mutex_t lock;
+
+ Monitor* next;
+
+ /*
+ * Who last acquired this monitor, when lock sampling is enabled.
+ * Even when enabled, ownerFileName may be NULL.
+ */
+ char* ownerFileName;
+ u4 ownerLineNumber;
+
+#ifdef WITH_DEADLOCK_PREDICTION
+ /*
+ * Objects that have been locked immediately after this one in the
+ * past. We use an expanding flat array, allocated on first use, to
+ * minimize allocations. Deletions from the list, expected to be
+ * infrequent, are crunched down.
+ */
+ ExpandingObjectList historyChildren;
+
+ /*
+ * We also track parents. This isn't strictly necessary, but it makes
+ * the cleanup at GC time significantly faster.
+ */
+ ExpandingObjectList historyParents;
+
+ /* used during cycle detection */
+ bool historyMark;
+
+ /* stack trace, established the first time we locked the object */
+ int historyStackDepth;
+ int* historyRawStackTrace;
+#endif
+};
+
+
+/*
+ * Create and initialize a monitor.
+ */
+Monitor* dvmCreateMonitor(Object* obj)
+{
+ Monitor* mon;
+
+ mon = (Monitor*) calloc(1, sizeof(Monitor));
+ if (mon == NULL) {
+ LOGE("Unable to allocate monitor\n");
+ dvmAbort();
+ }
+ if (((u4)mon & 7) != 0) {
+ LOGE("Misaligned monitor: %p\n", mon);
+ dvmAbort();
+ }
+ mon->obj = obj;
+ dvmInitMutex(&mon->lock);
+
+ /* replace the head of the list with the new monitor */
+ do {
+ mon->next = gDvm.monitorList;
+ } while (android_atomic_release_cas((int32_t)mon->next, (int32_t)mon,
+ (int32_t*)(void*)&gDvm.monitorList) != 0);
+
+ return mon;
+}
+
+/*
+ * Free the monitor list. Only used when shutting the VM down.
+ */
+void dvmFreeMonitorList(void)
+{
+ Monitor* mon;
+ Monitor* nextMon;
+
+ mon = gDvm.monitorList;
+ while (mon != NULL) {
+ nextMon = mon->next;
+
+#ifdef WITH_DEADLOCK_PREDICTION
+ expandObjClear(&mon->historyChildren);
+ expandObjClear(&mon->historyParents);
+ free(mon->historyRawStackTrace);
+#endif
+ free(mon);
+ mon = nextMon;
+ }
+}
+
+/*
+ * Log some info about our monitors.
+ */
+void dvmDumpMonitorInfo(const char* msg)
+{
+#if QUIET_ZYGOTE_MONITOR
+ if (gDvm.zygote) {
+ return;
+ }
+#endif
+
+ int totalCount;
+ int liveCount;
+
+ totalCount = liveCount = 0;
+ Monitor* mon = gDvm.monitorList;
+ while (mon != NULL) {
+ totalCount++;
+ if (mon->obj != NULL)
+ liveCount++;
+ mon = mon->next;
+ }
+
+ LOGD("%s: monitor list has %d entries (%d live)\n",
+ msg, totalCount, liveCount);
+}
+
+/*
+ * Get the object that a monitor is part of.
+ */
+Object* dvmGetMonitorObject(Monitor* mon)
+{
+ if (mon == NULL)
+ return NULL;
+ else
+ return mon->obj;
+}
+
+/*
+ * Returns the thread id of the thread owning the given lock.
+ */
+static u4 lockOwner(Object* obj)
+{
+ Thread *owner;
+ u4 lock;
+
+ assert(obj != NULL);
+ /*
+ * Since we're reading the lock value multiple times, latch it so
+ * that it doesn't change out from under us if we get preempted.
+ */
+ lock = obj->lock;
+ if (LW_SHAPE(lock) == LW_SHAPE_THIN) {
+ return LW_LOCK_OWNER(lock);
+ } else {
+ owner = LW_MONITOR(lock)->owner;
+ return owner ? owner->threadId : 0;
+ }
+}
+
+/*
+ * Get the thread that holds the lock on the specified object. The
+ * object may be unlocked, thin-locked, or fat-locked.
+ *
+ * The caller must lock the thread list before calling here.
+ */
+Thread* dvmGetObjectLockHolder(Object* obj)
+{
+ u4 threadId = lockOwner(obj);
+
+ if (threadId == 0)
+ return NULL;
+ return dvmGetThreadByThreadId(threadId);
+}
+
+/*
+ * Checks whether the given thread holds the given
+ * objects's lock.
+ */
+bool dvmHoldsLock(Thread* thread, Object* obj)
+{
+ if (thread == NULL || obj == NULL) {
+ return false;
+ } else {
+ return thread->threadId == lockOwner(obj);
+ }
+}
+
+/*
+ * Free the monitor associated with an object and make the object's lock
+ * thin again. This is called during garbage collection.
+ */
+static void freeObjectMonitor(Object* obj)
+{
+ Monitor *mon;
+
+ assert(LW_SHAPE(obj->lock) == LW_SHAPE_FAT);
+
+#ifdef WITH_DEADLOCK_PREDICTION
+ if (gDvm.deadlockPredictMode != kDPOff)
+ removeCollectedObject(obj);
+#endif
+
+ mon = LW_MONITOR(obj->lock);
+ obj->lock = DVM_LOCK_INITIAL_THIN_VALUE;
+
+ /* This lock is associated with an object
+ * that's being swept. The only possible way
+ * anyone could be holding this lock would be
+ * if some JNI code locked but didn't unlock
+ * the object, in which case we've got some bad
+ * native code somewhere.
+ */
+ assert(pthread_mutex_trylock(&mon->lock) == 0);
+ assert(pthread_mutex_unlock(&mon->lock) == 0);
+ dvmDestroyMutex(&mon->lock);
+#ifdef WITH_DEADLOCK_PREDICTION
+ expandObjClear(&mon->historyChildren);
+ expandObjClear(&mon->historyParents);
+ free(mon->historyRawStackTrace);
+#endif
+ free(mon);
+}
+
+/*
+ * Frees monitor objects belonging to unmarked objects.
+ */
+void dvmSweepMonitorList(Monitor** mon, int (*isUnmarkedObject)(void*))
+{
+ Monitor handle;
+ Monitor *prev, *curr;
+ Object *obj;
+
+ assert(mon != NULL);
+ assert(isUnmarkedObject != NULL);
+#ifdef WITH_DEADLOCK_PREDICTION
+ dvmDumpMonitorInfo("before monitor sweep");
+#endif
+ prev = &handle;
+ prev->next = curr = *mon;
+ while (curr != NULL) {
+ obj = curr->obj;
+ if (obj != NULL && (*isUnmarkedObject)(obj) != 0) {
+ prev->next = curr = curr->next;
+ freeObjectMonitor(obj);
+ } else {
+ prev = curr;
+ curr = curr->next;
+ }
+ }
+ *mon = handle.next;
+#ifdef WITH_DEADLOCK_PREDICTION
+ dvmDumpMonitorInfo("after monitor sweep");
+#endif
+}
+
+static char *logWriteInt(char *dst, int value)
+{
+ *dst++ = EVENT_TYPE_INT;
+ set4LE((u1 *)dst, value);
+ return dst + 4;
+}
+
+static char *logWriteString(char *dst, const char *value, size_t len)
+{
+ *dst++ = EVENT_TYPE_STRING;
+ len = len < 32 ? len : 32;
+ set4LE((u1 *)dst, len);
+ dst += 4;
+ memcpy(dst, value, len);
+ return dst + len;
+}
+
+#define EVENT_LOG_TAG_dvm_lock_sample 20003
+
+static void logContentionEvent(Thread *self, u4 waitMs, u4 samplePercent,
+ const char *ownerFileName, u4 ownerLineNumber)
+{
+ const StackSaveArea *saveArea;
+ const Method *meth;
+ u4 relativePc;
+ char eventBuffer[174];
+ const char *fileName;
+ char procName[33], *selfName;
+ char *cp;
+ size_t len;
+ int fd;
+
+ saveArea = SAVEAREA_FROM_FP(self->curFrame);
+ meth = saveArea->method;
+ cp = eventBuffer;
+
+ /* Emit the event list length, 1 byte. */
+ *cp++ = 9;
+
+ /* Emit the process name, <= 37 bytes. */
+ fd = open("/proc/self/cmdline", O_RDONLY);
+ memset(procName, 0, sizeof(procName));
+ read(fd, procName, sizeof(procName) - 1);
+ close(fd);
+ len = strlen(procName);
+ cp = logWriteString(cp, procName, len);
+
+ /* Emit the main thread status, 5 bytes. */
+ bool isMainThread = (self->systemTid == getpid());
+ cp = logWriteInt(cp, isMainThread);
+
+ /* Emit self thread name string, <= 37 bytes. */
+ selfName = dvmGetThreadName(self);
+ cp = logWriteString(cp, selfName, strlen(selfName));
+ free(selfName);
+
+ /* Emit the wait time, 5 bytes. */
+ cp = logWriteInt(cp, waitMs);
+
+ /* Emit the source code file name, <= 37 bytes. */
+ fileName = dvmGetMethodSourceFile(meth);
+ if (fileName == NULL) fileName = "";
+ cp = logWriteString(cp, fileName, strlen(fileName));
+
+ /* Emit the source code line number, 5 bytes. */
+ relativePc = saveArea->xtra.currentPc - saveArea->method->insns;
+ cp = logWriteInt(cp, dvmLineNumFromPC(meth, relativePc));
+
+ /* Emit the lock owner source code file name, <= 37 bytes. */
+ if (ownerFileName == NULL) {
+ ownerFileName = "";
+ } else if (strcmp(fileName, ownerFileName) == 0) {
+ /* Common case, so save on log space. */
+ ownerFileName = "-";
+ }
+ cp = logWriteString(cp, ownerFileName, strlen(ownerFileName));
+
+ /* Emit the source code line number, 5 bytes. */
+ cp = logWriteInt(cp, ownerLineNumber);
+
+ /* Emit the sample percentage, 5 bytes. */
+ cp = logWriteInt(cp, samplePercent);
+
+ assert((size_t)(cp - eventBuffer) <= sizeof(eventBuffer));
+ android_btWriteLog(EVENT_LOG_TAG_dvm_lock_sample,
+ EVENT_TYPE_LIST,
+ eventBuffer,
+ (size_t)(cp - eventBuffer));
+}
+
+/*
+ * Lock a monitor.
+ */
+static void lockMonitor(Thread* self, Monitor* mon)
+{
+ ThreadStatus oldStatus;
+ u4 waitThreshold, samplePercent;
+ u8 waitStart, waitEnd, waitMs;
+
+ if (mon->owner == self) {
+ mon->lockCount++;
+ return;
+ }
+ if (dvmTryLockMutex(&mon->lock) != 0) {
+ oldStatus = dvmChangeStatus(self, THREAD_MONITOR);
+ waitThreshold = gDvm.lockProfThreshold;
+ if (waitThreshold) {
+ waitStart = dvmGetRelativeTimeUsec();
+ }
+ const char* currentOwnerFileName = mon->ownerFileName;
+ u4 currentOwnerLineNumber = mon->ownerLineNumber;
+
+ dvmLockMutex(&mon->lock);
+ if (waitThreshold) {
+ waitEnd = dvmGetRelativeTimeUsec();
+ }
+ dvmChangeStatus(self, oldStatus);
+ if (waitThreshold) {
+ waitMs = (waitEnd - waitStart) / 1000;
+ if (waitMs >= waitThreshold) {
+ samplePercent = 100;
+ } else {
+ samplePercent = 100 * waitMs / waitThreshold;
+ }
+ if (samplePercent != 0 && ((u4)rand() % 100 < samplePercent)) {
+ logContentionEvent(self, waitMs, samplePercent,
+ currentOwnerFileName, currentOwnerLineNumber);
+ }
+ }
+ }
+ mon->owner = self;
+ assert(mon->lockCount == 0);
+
+ // When debugging, save the current monitor holder for future
+ // acquisition failures to use in sampled logging.
+ if (gDvm.lockProfThreshold > 0) {
+ const StackSaveArea *saveArea;
+ const Method *meth;
+ mon->ownerLineNumber = 0;
+ if (self->curFrame == NULL) {
+ mon->ownerFileName = "no_frame";
+ } else if ((saveArea = SAVEAREA_FROM_FP(self->curFrame)) == NULL) {
+ mon->ownerFileName = "no_save_area";
+ } else if ((meth = saveArea->method) == NULL) {
+ mon->ownerFileName = "no_method";
+ } else {
+ u4 relativePc = saveArea->xtra.currentPc - saveArea->method->insns;
+ mon->ownerFileName = (char*) dvmGetMethodSourceFile(meth);
+ if (mon->ownerFileName == NULL) {
+ mon->ownerFileName = "no_method_file";
+ } else {
+ mon->ownerLineNumber = dvmLineNumFromPC(meth, relativePc);
+ }
+ }
+ }
+}
+
+/*
+ * Try to lock a monitor.
+ *
+ * Returns "true" on success.
+ */
+#ifdef WITH_COPYING_GC
+static bool tryLockMonitor(Thread* self, Monitor* mon)
+{
+ if (mon->owner == self) {
+ mon->lockCount++;
+ return true;
+ } else {
+ if (dvmTryLockMutex(&mon->lock) == 0) {
+ mon->owner = self;
+ assert(mon->lockCount == 0);
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
+#endif
+
+/*
+ * Unlock a monitor.
+ *
+ * Returns true if the unlock succeeded.
+ * If the unlock failed, an exception will be pending.
+ */
+static bool unlockMonitor(Thread* self, Monitor* mon)
+{
+ assert(self != NULL);
+ assert(mon != NULL);
+ if (mon->owner == self) {
+ /*
+ * We own the monitor, so nobody else can be in here.
+ */
+ if (mon->lockCount == 0) {
+ mon->owner = NULL;
+ mon->ownerFileName = "unlocked";
+ mon->ownerLineNumber = 0;
+ dvmUnlockMutex(&mon->lock);
+ } else {
+ mon->lockCount--;
+ }
+ } else {
+ /*
+ * We don't own this, so we're not allowed to unlock it.
+ * The JNI spec says that we should throw IllegalMonitorStateException
+ * in this case.
+ */
+ dvmThrowException("Ljava/lang/IllegalMonitorStateException;",
+ "unlock of unowned monitor");
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Checks the wait set for circular structure. Returns 0 if the list
+ * is not circular. Otherwise, returns 1. Used only by asserts.
+ */
+#ifndef NDEBUG
+static int waitSetCheck(Monitor *mon)
+{
+ Thread *fast, *slow;
+ size_t n;
+
+ assert(mon != NULL);
+ fast = slow = mon->waitSet;
+ n = 0;
+ for (;;) {
+ if (fast == NULL) return 0;
+ if (fast->waitNext == NULL) return 0;
+ if (fast == slow && n > 0) return 1;
+ n += 2;
+ fast = fast->waitNext->waitNext;
+ slow = slow->waitNext;
+ }
+}
+#endif
+
+/*
+ * Links a thread into a monitor's wait set. The monitor lock must be
+ * held by the caller of this routine.
+ */
+static void waitSetAppend(Monitor *mon, Thread *thread)
+{
+ Thread *elt;
+
+ assert(mon != NULL);
+ assert(mon->owner == dvmThreadSelf());
+ assert(thread != NULL);
+ assert(thread->waitNext == NULL);
+ assert(waitSetCheck(mon) == 0);
+ if (mon->waitSet == NULL) {
+ mon->waitSet = thread;
+ return;
+ }
+ elt = mon->waitSet;
+ while (elt->waitNext != NULL) {
+ elt = elt->waitNext;
+ }
+ elt->waitNext = thread;
+}
+
+/*
+ * Unlinks a thread from a monitor's wait set. The monitor lock must
+ * be held by the caller of this routine.
+ */
+static void waitSetRemove(Monitor *mon, Thread *thread)
+{
+ Thread *elt;
+
+ assert(mon != NULL);
+ assert(mon->owner == dvmThreadSelf());
+ assert(thread != NULL);
+ assert(waitSetCheck(mon) == 0);
+ if (mon->waitSet == NULL) {
+ return;
+ }
+ if (mon->waitSet == thread) {
+ mon->waitSet = thread->waitNext;
+ thread->waitNext = NULL;
+ return;
+ }
+ elt = mon->waitSet;
+ while (elt->waitNext != NULL) {
+ if (elt->waitNext == thread) {
+ elt->waitNext = thread->waitNext;
+ thread->waitNext = NULL;
+ return;
+ }
+ elt = elt->waitNext;
+ }
+}
+
+/*
+ * Converts the given relative waiting time into an absolute time.
+ */
+void absoluteTime(s8 msec, s4 nsec, struct timespec *ts)
+{
+ s8 endSec;
+
+#ifdef HAVE_TIMEDWAIT_MONOTONIC
+ clock_gettime(CLOCK_MONOTONIC, ts);
+#else
+ {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ ts->tv_sec = tv.tv_sec;
+ ts->tv_nsec = tv.tv_usec * 1000;
+ }
+#endif
+ endSec = ts->tv_sec + msec / 1000;
+ if (endSec >= 0x7fffffff) {
+ LOGV("NOTE: end time exceeds epoch\n");
+ endSec = 0x7ffffffe;
+ }
+ ts->tv_sec = endSec;
+ ts->tv_nsec = (ts->tv_nsec + (msec % 1000) * 1000000) + nsec;
+
+ /* catch rollover */
+ if (ts->tv_nsec >= 1000000000L) {
+ ts->tv_sec++;
+ ts->tv_nsec -= 1000000000L;
+ }
+}
+
+int dvmRelativeCondWait(pthread_cond_t* cond, pthread_mutex_t* mutex,
+ s8 msec, s4 nsec)
+{
+ int ret;
+ struct timespec ts;
+ absoluteTime(msec, nsec, &ts);
+#if defined(HAVE_TIMEDWAIT_MONOTONIC)
+ ret = pthread_cond_timedwait_monotonic(cond, mutex, &ts);
+#else
+ ret = pthread_cond_timedwait(cond, mutex, &ts);
+#endif
+ assert(ret == 0 || ret == ETIMEDOUT);
+ return ret;
+}
+
+/*
+ * Wait on a monitor until timeout, interrupt, or notification. Used for
+ * Object.wait() and (somewhat indirectly) Thread.sleep() and Thread.join().
+ *
+ * If another thread calls Thread.interrupt(), we throw InterruptedException
+ * and return immediately if one of the following are true:
+ * - blocked in wait(), wait(long), or wait(long, int) methods of Object
+ * - blocked in join(), join(long), or join(long, int) methods of Thread
+ * - blocked in sleep(long), or sleep(long, int) methods of Thread
+ * Otherwise, we set the "interrupted" flag.
+ *
+ * Checks to make sure that "nsec" is in the range 0-999999
+ * (i.e. fractions of a millisecond) and throws the appropriate
+ * exception if it isn't.
+ *
+ * The spec allows "spurious wakeups", and recommends that all code using
+ * Object.wait() do so in a loop. This appears to derive from concerns
+ * about pthread_cond_wait() on multiprocessor systems. Some commentary
+ * on the web casts doubt on whether these can/should occur.
+ *
+ * Since we're allowed to wake up "early", we clamp extremely long durations
+ * to return at the end of the 32-bit time epoch.
+ */
+static void waitMonitor(Thread* self, Monitor* mon, s8 msec, s4 nsec,
+ bool interruptShouldThrow)
+{
+ struct timespec ts;
+ bool wasInterrupted = false;
+ bool timed;
+ int ret;
+ char *savedFileName;
+ u4 savedLineNumber;
+
+ assert(self != NULL);
+ assert(mon != NULL);
+
+ /* Make sure that we hold the lock. */
+ if (mon->owner != self) {
+ dvmThrowException("Ljava/lang/IllegalMonitorStateException;",
+ "object not locked by thread before wait()");
+ return;
+ }
+
+ /*
+ * Enforce the timeout range.
+ */
+ if (msec < 0 || nsec < 0 || nsec > 999999) {
+ dvmThrowException("Ljava/lang/IllegalArgumentException;",
+ "timeout arguments out of range");
+ return;
+ }
+
+ /*
+ * Compute absolute wakeup time, if necessary.
+ */
+ if (msec == 0 && nsec == 0) {
+ timed = false;
+ } else {
+ absoluteTime(msec, nsec, &ts);
+ timed = true;
+ }
+
+ /*
+ * Add ourselves to the set of threads waiting on this monitor, and
+ * release our hold. We need to let it go even if we're a few levels
+ * deep in a recursive lock, and we need to restore that later.
+ *
+ * We append to the wait set ahead of clearing the count and owner
+ * fields so the subroutine can check that the calling thread owns
+ * the monitor. Aside from that, the order of member updates is
+ * not order sensitive as we hold the pthread mutex.
+ */
+ waitSetAppend(mon, self);
+ int prevLockCount = mon->lockCount;
+ mon->lockCount = 0;
+ mon->owner = NULL;
+ savedFileName = mon->ownerFileName;
+ mon->ownerFileName = NULL;
+ savedLineNumber = mon->ownerLineNumber;
+ mon->ownerLineNumber = 0;
+
+ /*
+ * Update thread status. If the GC wakes up, it'll ignore us, knowing
+ * that we won't touch any references in this state, and we'll check
+ * our suspend mode before we transition out.
+ */
+ if (timed)
+ dvmChangeStatus(self, THREAD_TIMED_WAIT);
+ else
+ dvmChangeStatus(self, THREAD_WAIT);
+
+ dvmLockMutex(&self->waitMutex);
+
+ /*
+ * Set waitMonitor to the monitor object we will be waiting on.
+ * When waitMonitor is non-NULL a notifying or interrupting thread
+ * must signal the thread's waitCond to wake it up.
+ */
+ assert(self->waitMonitor == NULL);
+ self->waitMonitor = mon;
+
+ /*
+ * Handle the case where the thread was interrupted before we called
+ * wait().
+ */
+ if (self->interrupted) {
+ wasInterrupted = true;
+ self->waitMonitor = NULL;
+ dvmUnlockMutex(&self->waitMutex);
+ goto done;
+ }
+
+ /*
+ * Release the monitor lock and wait for a notification or
+ * a timeout to occur.
+ */
+ dvmUnlockMutex(&mon->lock);
+
+ if (!timed) {
+ ret = pthread_cond_wait(&self->waitCond, &self->waitMutex);
+ assert(ret == 0);
+ } else {
+#ifdef HAVE_TIMEDWAIT_MONOTONIC
+ ret = pthread_cond_timedwait_monotonic(&self->waitCond, &self->waitMutex, &ts);
+#else
+ ret = pthread_cond_timedwait(&self->waitCond, &self->waitMutex, &ts);
+#endif
+ assert(ret == 0 || ret == ETIMEDOUT);
+ }
+ if (self->interrupted) {
+ wasInterrupted = true;
+ }
+
+ self->interrupted = false;
+ self->waitMonitor = NULL;
+
+ dvmUnlockMutex(&self->waitMutex);
+
+ /* Reacquire the monitor lock. */
+ lockMonitor(self, mon);
+
+done:
+ /*
+ * We remove our thread from wait set after restoring the count
+ * and owner fields so the subroutine can check that the calling
+ * thread owns the monitor. Aside from that, the order of member
+ * updates is not order sensitive as we hold the pthread mutex.
+ */
+ mon->owner = self;
+ mon->lockCount = prevLockCount;
+ mon->ownerFileName = savedFileName;
+ mon->ownerLineNumber = savedLineNumber;
+ waitSetRemove(mon, self);
+
+ /* set self->status back to THREAD_RUNNING, and self-suspend if needed */
+ dvmChangeStatus(self, THREAD_RUNNING);
+
+ if (wasInterrupted) {
+ /*
+ * We were interrupted while waiting, or somebody interrupted an
+ * un-interruptible thread earlier and we're bailing out immediately.
+ *
+ * The doc sayeth: "The interrupted status of the current thread is
+ * cleared when this exception is thrown."
+ */
+ self->interrupted = false;
+ if (interruptShouldThrow)
+ dvmThrowException("Ljava/lang/InterruptedException;", NULL);
+ }
+}
+
+/*
+ * Notify one thread waiting on this monitor.
+ */
+static void notifyMonitor(Thread* self, Monitor* mon)
+{
+ Thread* thread;
+
+ assert(self != NULL);
+ assert(mon != NULL);
+
+ /* Make sure that we hold the lock. */
+ if (mon->owner != self) {
+ dvmThrowException("Ljava/lang/IllegalMonitorStateException;",
+ "object not locked by thread before notify()");
+ return;
+ }
+ /* Signal the first waiting thread in the wait set. */
+ while (mon->waitSet != NULL) {
+ thread = mon->waitSet;
+ mon->waitSet = thread->waitNext;
+ thread->waitNext = NULL;
+ dvmLockMutex(&thread->waitMutex);
+ /* Check to see if the thread is still waiting. */
+ if (thread->waitMonitor != NULL) {
+ pthread_cond_signal(&thread->waitCond);
+ dvmUnlockMutex(&thread->waitMutex);
+ return;
+ }
+ dvmUnlockMutex(&thread->waitMutex);
+ }
+}
+
+/*
+ * Notify all threads waiting on this monitor.
+ */
+static void notifyAllMonitor(Thread* self, Monitor* mon)
+{
+ Thread* thread;
+
+ assert(self != NULL);
+ assert(mon != NULL);
+
+ /* Make sure that we hold the lock. */
+ if (mon->owner != self) {
+ dvmThrowException("Ljava/lang/IllegalMonitorStateException;",
+ "object not locked by thread before notifyAll()");
+ return;
+ }
+ /* Signal all threads in the wait set. */
+ while (mon->waitSet != NULL) {
+ thread = mon->waitSet;
+ mon->waitSet = thread->waitNext;
+ thread->waitNext = NULL;
+ dvmLockMutex(&thread->waitMutex);
+ /* Check to see if the thread is still waiting. */
+ if (thread->waitMonitor != NULL) {
+ pthread_cond_signal(&thread->waitCond);
+ }
+ dvmUnlockMutex(&thread->waitMutex);
+ }
+}
+
+/*
+ * Changes the shape of a monitor from thin to fat, preserving the
+ * internal lock state. The calling thread must own the lock.
+ */
+static void inflateMonitor(Thread *self, Object *obj)
+{
+ Monitor *mon;
+ u4 thin;
+
+ assert(self != NULL);
+ assert(obj != NULL);
+ assert(LW_SHAPE(obj->lock) == LW_SHAPE_THIN);
+ assert(LW_LOCK_OWNER(obj->lock) == self->threadId);
+ /* Allocate and acquire a new monitor. */
+ mon = dvmCreateMonitor(obj);
+ lockMonitor(self, mon);
+ /* Propagate the lock state. */
+ thin = obj->lock;
+ mon->lockCount = LW_LOCK_COUNT(thin);
+ thin &= LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT;
+ thin |= (u4)mon | LW_SHAPE_FAT;
+ /* Publish the updated lock word. */
+ android_atomic_release_store(thin, (int32_t *)&obj->lock);
+}
+
+/*
+ * Implements monitorenter for "synchronized" stuff.
+ *
+ * This does not fail or throw an exception (unless deadlock prediction
+ * is enabled and set to "err" mode).
+ */
+void dvmLockObject(Thread* self, Object *obj)
+{
+ volatile u4 *thinp;
+ ThreadStatus oldStatus;
+ useconds_t sleepDelay;
+ const useconds_t maxSleepDelay = 1 << 20;
+ u4 thin, newThin, threadId;
+
+ assert(self != NULL);
+ assert(obj != NULL);
+ threadId = self->threadId;
+ thinp = &obj->lock;
+retry:
+ thin = *thinp;
+ if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
+ /*
+ * The lock is a thin lock. The owner field is used to
+ * determine the acquire method, ordered by cost.
+ */
+ if (LW_LOCK_OWNER(thin) == threadId) {
+ /*
+ * The calling thread owns the lock. Increment the
+ * value of the recursion count field.
+ */
+ obj->lock += 1 << LW_LOCK_COUNT_SHIFT;
+ if (LW_LOCK_COUNT(obj->lock) == LW_LOCK_COUNT_MASK) {
+ /*
+ * The reacquisition limit has been reached. Inflate
+ * the lock so the next acquire will not overflow the
+ * recursion count field.
+ */
+ inflateMonitor(self, obj);
+ }
+ } else if (LW_LOCK_OWNER(thin) == 0) {
+ /*
+ * The lock is unowned. Install the thread id of the
+ * calling thread into the owner field. This is the
+ * common case. In performance critical code the JIT
+ * will have tried this before calling out to the VM.
+ */
+ newThin = thin | (threadId << LW_LOCK_OWNER_SHIFT);
+ if (android_atomic_acquire_cas(thin, newThin,
+ (int32_t*)thinp) != 0) {
+ /*
+ * The acquire failed. Try again.
+ */
+ goto retry;
+ }
+ } else {
+ LOG_THIN("(%d) spin on lock %p: %#x (%#x) %#x",
+ threadId, &obj->lock, 0, *thinp, thin);
+ /*
+ * The lock is owned by another thread. Notify the VM
+ * that we are about to wait.
+ */
+ oldStatus = dvmChangeStatus(self, THREAD_MONITOR);
+ /*
+ * Spin until the thin lock is released or inflated.
+ */
+ sleepDelay = 0;
+ for (;;) {
+ thin = *thinp;
+ /*
+ * Check the shape of the lock word. Another thread
+ * may have inflated the lock while we were waiting.
+ */
+ if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
+ if (LW_LOCK_OWNER(thin) == 0) {
+ /*
+ * The lock has been released. Install the
+ * thread id of the calling thread into the
+ * owner field.
+ */
+ newThin = thin | (threadId << LW_LOCK_OWNER_SHIFT);
+ if (android_atomic_acquire_cas(thin, newThin,
+ (int32_t *)thinp) == 0) {
+ /*
+ * The acquire succeed. Break out of the
+ * loop and proceed to inflate the lock.
+ */
+ break;
+ }
+ } else {
+ /*
+ * The lock has not been released. Yield so
+ * the owning thread can run.
+ */
+ if (sleepDelay == 0) {
+ sched_yield();
+ sleepDelay = 1000;
+ } else {
+ usleep(sleepDelay);
+ if (sleepDelay < maxSleepDelay / 2) {
+ sleepDelay *= 2;
+ }
+ }
+ }
+ } else {
+ /*
+ * The thin lock was inflated by another thread.
+ * Let the VM know we are no longer waiting and
+ * try again.
+ */
+ LOG_THIN("(%d) lock %p surprise-fattened",
+ threadId, &obj->lock);
+ dvmChangeStatus(self, oldStatus);
+ goto retry;
+ }
+ }
+ LOG_THIN("(%d) spin on lock done %p: %#x (%#x) %#x",
+ threadId, &obj->lock, 0, *thinp, thin);
+ /*
+ * We have acquired the thin lock. Let the VM know that
+ * we are no longer waiting.
+ */
+ dvmChangeStatus(self, oldStatus);
+ /*
+ * Fatten the lock.
+ */
+ inflateMonitor(self, obj);
+ LOG_THIN("(%d) lock %p fattened", threadId, &obj->lock);
+ }
+ } else {
+ /*
+ * The lock is a fat lock.
+ */
+ assert(LW_MONITOR(obj->lock) != NULL);
+ lockMonitor(self, LW_MONITOR(obj->lock));
+ }
+#ifdef WITH_DEADLOCK_PREDICTION
+ /*
+ * See if we were allowed to grab the lock at this time. We do it
+ * *after* acquiring the lock, rather than before, so that we can
+ * freely update the Monitor struct. This seems counter-intuitive,
+ * but our goal is deadlock *prediction* not deadlock *prevention*.
+ * (If we actually deadlock, the situation is easy to diagnose from
+ * a thread dump, so there's no point making a special effort to do
+ * the checks before the lock is held.)
+ *
+ * This needs to happen before we add the object to the thread's
+ * monitor list, so we can tell the difference between first-lock and
+ * re-lock.
+ *
+ * It's also important that we do this while in THREAD_RUNNING, so
+ * that we don't interfere with cleanup operations in the GC.
+ */
+ if (gDvm.deadlockPredictMode != kDPOff) {
+ if (self->status != THREAD_RUNNING) {
+ LOGE("Bad thread status (%d) in DP\n", self->status);
+ dvmDumpThread(self, false);
+ dvmAbort();
+ }
+ assert(!dvmCheckException(self));
+ updateDeadlockPrediction(self, obj);
+ if (dvmCheckException(self)) {
+ /*
+ * If we're throwing an exception here, we need to free the
+ * lock. We add the object to the thread's monitor list so the
+ * "unlock" code can remove it.
+ */
+ dvmAddToMonitorList(self, obj, false);
+ dvmUnlockObject(self, obj);
+ LOGV("--- unlocked, pending is '%s'\n",
+ dvmGetException(self)->clazz->descriptor);
+ }
+ }
+
+ /*
+ * Add the locked object, and the current stack trace, to the list
+ * held by the Thread object. If deadlock prediction isn't on,
+ * don't capture the stack trace.
+ */
+ dvmAddToMonitorList(self, obj, gDvm.deadlockPredictMode != kDPOff);
+#elif defined(WITH_MONITOR_TRACKING)
+ /*
+ * Add the locked object to the list held by the Thread object.
+ */
+ dvmAddToMonitorList(self, obj, false);
+#endif
+}
+
+/*
+ * Implements monitorexit for "synchronized" stuff.
+ *
+ * On failure, throws an exception and returns "false".
+ */
+bool dvmUnlockObject(Thread* self, Object *obj)
+{
+ u4 thin;
+
+ assert(self != NULL);
+ assert(self->status == THREAD_RUNNING);
+ assert(obj != NULL);
+ /*
+ * Cache the lock word as its value can change while we are
+ * examining its state.
+ */
+ thin = obj->lock;
+ if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
+ /*
+ * The lock is thin. We must ensure that the lock is owned
+ * by the given thread before unlocking it.
+ */
+ if (LW_LOCK_OWNER(thin) == self->threadId) {
+ /*
+ * We are the lock owner. It is safe to update the lock
+ * without CAS as lock ownership guards the lock itself.
+ */
+ if (LW_LOCK_COUNT(thin) == 0) {
+ /*
+ * The lock was not recursively acquired, the common
+ * case. Unlock by clearing all bits except for the
+ * hash state.
+ */
+ obj->lock &= (LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT);
+ } else {
+ /*
+ * The object was recursively acquired. Decrement the
+ * lock recursion count field.
+ */
+ obj->lock -= 1 << LW_LOCK_COUNT_SHIFT;
+ }
+ } else {
+ /*
+ * We do not own the lock. The JVM spec requires that we
+ * throw an exception in this case.
+ */
+ dvmThrowException("Ljava/lang/IllegalMonitorStateException;",
+ "unlock of unowned monitor");
+ return false;
+ }
+ } else {
+ /*
+ * The lock is fat. We must check to see if unlockMonitor has
+ * raised any exceptions before continuing.
+ */
+ assert(LW_MONITOR(obj->lock) != NULL);
+ if (!unlockMonitor(self, LW_MONITOR(obj->lock))) {
+ /*
+ * An exception has been raised. Do not fall through.
+ */
+ return false;
+ }
+ }
+
+#ifdef WITH_MONITOR_TRACKING
+ /*
+ * Remove the object from the Thread's list.
+ */
+ dvmRemoveFromMonitorList(self, obj);
+#endif
+
+ return true;
+}
+
+/*
+ * Object.wait(). Also called for class init.
+ */
+void dvmObjectWait(Thread* self, Object *obj, s8 msec, s4 nsec,
+ bool interruptShouldThrow)
+{
+ Monitor* mon;
+ u4 thin = obj->lock;
+
+ /* If the lock is still thin, we need to fatten it.
+ */
+ if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
+ /* Make sure that 'self' holds the lock.
+ */
+ if (LW_LOCK_OWNER(thin) != self->threadId) {
+ dvmThrowException("Ljava/lang/IllegalMonitorStateException;",
+ "object not locked by thread before wait()");
+ return;
+ }
+
+ /* This thread holds the lock. We need to fatten the lock
+ * so 'self' can block on it. Don't update the object lock
+ * field yet, because 'self' needs to acquire the lock before
+ * any other thread gets a chance.
+ */
+ inflateMonitor(self, obj);
+ LOG_THIN("(%d) lock %p fattened by wait() to count %d",
+ self->threadId, &obj->lock, mon->lockCount);
+ }
+ mon = LW_MONITOR(obj->lock);
+ waitMonitor(self, mon, msec, nsec, interruptShouldThrow);
+}
+
+/*
+ * Object.notify().
+ */
+void dvmObjectNotify(Thread* self, Object *obj)
+{
+ u4 thin = obj->lock;
+
+ /* If the lock is still thin, there aren't any waiters;
+ * waiting on an object forces lock fattening.
+ */
+ if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
+ /* Make sure that 'self' holds the lock.
+ */
+ if (LW_LOCK_OWNER(thin) != self->threadId) {
+ dvmThrowException("Ljava/lang/IllegalMonitorStateException;",
+ "object not locked by thread before notify()");
+ return;
+ }
+
+ /* no-op; there are no waiters to notify.
+ */
+ } else {
+ /* It's a fat lock.
+ */
+ notifyMonitor(self, LW_MONITOR(thin));
+ }
+}
+
+/*
+ * Object.notifyAll().
+ */
+void dvmObjectNotifyAll(Thread* self, Object *obj)
+{
+ u4 thin = obj->lock;
+
+ /* If the lock is still thin, there aren't any waiters;
+ * waiting on an object forces lock fattening.
+ */
+ if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
+ /* Make sure that 'self' holds the lock.
+ */
+ if (LW_LOCK_OWNER(thin) != self->threadId) {
+ dvmThrowException("Ljava/lang/IllegalMonitorStateException;",
+ "object not locked by thread before notifyAll()");
+ return;
+ }
+
+ /* no-op; there are no waiters to notify.
+ */
+ } else {
+ /* It's a fat lock.
+ */
+ notifyAllMonitor(self, LW_MONITOR(thin));
+ }
+}
+
+/*
+ * This implements java.lang.Thread.sleep(long msec, int nsec).
+ *
+ * The sleep is interruptible by other threads, which means we can't just
+ * plop into an OS sleep call. (We probably could if we wanted to send
+ * signals around and rely on EINTR, but that's inefficient and relies
+ * on native code respecting our signal mask.)
+ *
+ * We have to do all of this stuff for Object.wait() as well, so it's
+ * easiest to just sleep on a private Monitor.
+ *
+ * It appears that we want sleep(0,0) to go through the motions of sleeping
+ * for a very short duration, rather than just returning.
+ */
+void dvmThreadSleep(u8 msec, u4 nsec)
+{
+ Thread* self = dvmThreadSelf();
+ Monitor* mon = gDvm.threadSleepMon;
+
+ /* sleep(0,0) wakes up immediately, wait(0,0) means wait forever; adjust */
+ if (msec == 0 && nsec == 0)
+ nsec++;
+
+ lockMonitor(self, mon);
+ waitMonitor(self, mon, msec, nsec, true);
+ unlockMonitor(self, mon);
+}
+
+/*
+ * Implement java.lang.Thread.interrupt().
+ */
+void dvmThreadInterrupt(Thread* thread)
+{
+ assert(thread != NULL);
+
+ dvmLockMutex(&thread->waitMutex);
+
+ /*
+ * If the interrupted flag is already set no additional action is
+ * required.
+ */
+ if (thread->interrupted == true) {
+ dvmUnlockMutex(&thread->waitMutex);
+ return;
+ }
+
+ /*
+ * Raise the "interrupted" flag. This will cause it to bail early out
+ * of the next wait() attempt, if it's not currently waiting on
+ * something.
+ */
+ thread->interrupted = true;
+
+ /*
+ * Is the thread waiting?
+ *
+ * Note that fat vs. thin doesn't matter here; waitMonitor
+ * is only set when a thread actually waits on a monitor,
+ * which implies that the monitor has already been fattened.
+ */
+ if (thread->waitMonitor != NULL) {
+ pthread_cond_signal(&thread->waitCond);
+ }
+
+ dvmUnlockMutex(&thread->waitMutex);
+}
+
+#ifndef WITH_COPYING_GC
+u4 dvmIdentityHashCode(Object *obj)
+{
+ return (u4)obj;
+}
+#else
+/*
+ * Returns the identity hash code of the given object.
+ */
+u4 dvmIdentityHashCode(Object *obj)
+{
+ Thread *self, *thread;
+ volatile u4 *lw;
+ size_t size;
+ u4 lock, owner, hashState;
+
+ if (obj == NULL) {
+ /*
+ * Null is defined to have an identity hash code of 0.
+ */
+ return 0;
+ }
+ lw = &obj->lock;
+retry:
+ hashState = LW_HASH_STATE(*lw);
+ if (hashState == LW_HASH_STATE_HASHED) {
+ /*
+ * The object has been hashed but has not had its hash code
+ * relocated by the garbage collector. Use the raw object
+ * address.
+ */
+ return (u4)obj >> 3;
+ } else if (hashState == LW_HASH_STATE_HASHED_AND_MOVED) {
+ /*
+ * The object has been hashed and its hash code has been
+ * relocated by the collector. Use the value of the naturally
+ * aligned word following the instance data.
+ */
+ assert(obj->clazz != gDvm.classJavaLangClass);
+ if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISARRAY)) {
+ size = dvmArrayObjectSize((ArrayObject *)obj);
+ size = (size + 2) & ~2;
+ } else {
+ size = obj->clazz->objectSize;
+ }
+ return *(u4 *)(((char *)obj) + size);
+ } else if (hashState == LW_HASH_STATE_UNHASHED) {
+ /*
+ * The object has never been hashed. Change the hash state to
+ * hashed and use the raw object address.
+ */
+ self = dvmThreadSelf();
+ if (self->threadId == lockOwner(obj)) {
+ /*
+ * We already own the lock so we can update the hash state
+ * directly.
+ */
+ *lw |= (LW_HASH_STATE_HASHED << LW_HASH_STATE_SHIFT);
+ return (u4)obj >> 3;
+ }
+ /*
+ * We do not own the lock. Try acquiring the lock. Should
+ * this fail, we must suspend the owning thread.
+ */
+ if (LW_SHAPE(*lw) == LW_SHAPE_THIN) {
+ /*
+ * If the lock is thin assume it is unowned. We simulate
+ * an acquire, update, and release with a single CAS.
+ */
+ lock = DVM_LOCK_INITIAL_THIN_VALUE;
+ lock |= (LW_HASH_STATE_HASHED << LW_HASH_STATE_SHIFT);
+ if (android_atomic_acquire_cas(
+ (int32_t)DVM_LOCK_INITIAL_THIN_VALUE,
+ (int32_t)lock,
+ (int32_t *)lw) == 0) {
+ /*
+ * A new lockword has been installed with a hash state
+ * of hashed. Use the raw object address.
+ */
+ return (u4)obj >> 3;
+ }
+ } else {
+ if (tryLockMonitor(self, LW_MONITOR(*lw))) {
+ /*
+ * The monitor lock has been acquired. Change the
+ * hash state to hashed and use the raw object
+ * address.
+ */
+ *lw |= (LW_HASH_STATE_HASHED << LW_HASH_STATE_SHIFT);
+ unlockMonitor(self, LW_MONITOR(*lw));
+ return (u4)obj >> 3;
+ }
+ }
+ /*
+ * At this point we have failed to acquire the lock. We must
+ * identify the owning thread and suspend it.
+ */
+ dvmLockThreadList(self);
+ /*
+ * Cache the lock word as its value can change between
+ * determining its shape and retrieving its owner.
+ */
+ lock = *lw;
+ if (LW_SHAPE(lock) == LW_SHAPE_THIN) {
+ /*
+ * Find the thread with the corresponding thread id.
+ */
+ owner = LW_LOCK_OWNER(lock);
+ assert(owner != self->threadId);
+ /*
+ * If the lock has no owner do not bother scanning the
+ * thread list and fall through to the failure handler.
+ */
+ thread = owner ? gDvm.threadList : NULL;
+ while (thread != NULL) {
+ if (thread->threadId == owner) {
+ break;
+ }
+ thread = thread->next;
+ }
+ } else {
+ thread = LW_MONITOR(lock)->owner;
+ }
+ /*
+ * If thread is NULL the object has been released since the
+ * thread list lock was acquired. Try again.
+ */
+ if (thread == NULL) {
+ dvmUnlockThreadList();
+ goto retry;
+ }
+ /*
+ * Wait for the owning thread to suspend.
+ */
+ dvmSuspendThread(thread);
+ if (dvmHoldsLock(thread, obj)) {
+ /*
+ * The owning thread has been suspended. We can safely
+ * change the hash state to hashed.
+ */
+ *lw |= (LW_HASH_STATE_HASHED << LW_HASH_STATE_SHIFT);
+ dvmResumeThread(thread);
+ dvmUnlockThreadList();
+ return (u4)obj >> 3;
+ }
+ /*
+ * The wrong thread has been suspended. Try again.
+ */
+ dvmResumeThread(thread);
+ dvmUnlockThreadList();
+ goto retry;
+ }
+ LOGE("object %p has an unknown hash state %#x", obj, hashState);
+ dvmDumpThread(dvmThreadSelf(), false);
+ dvmAbort();
+ return 0; /* Quiet the compiler. */
+}
+#endif /* WITH_COPYING_GC */
+
+#ifdef WITH_DEADLOCK_PREDICTION
+/*
+ * ===========================================================================
+ * Deadlock prediction
+ * ===========================================================================
+ */
+/*
+The idea is to predict the possibility of deadlock by recording the order
+in which monitors are acquired. If we see an attempt to acquire a lock
+out of order, we can identify the locks and offending code.
+
+To make this work, we need to keep track of the locks held by each thread,
+and create history trees for each lock. When a thread tries to acquire
+a new lock, we walk through the "history children" of the lock, looking
+for a match with locks the thread already holds. If we find a match,
+it means the thread has made a request that could result in a deadlock.
+
+To support recursive locks, we always allow re-locking a currently-held
+lock, and maintain a recursion depth count.
+
+An ASCII-art example, where letters represent Objects:
+
+ A
+ /|\
+ / | \
+ B | D
+ \ |
+ \|
+ C
+
+The above is the tree we'd have after handling Object synchronization
+sequences "ABC", "AC", "AD". A has three children, {B, C, D}. C is also
+a child of B. (The lines represent pointers between parent and child.
+Every node can have multiple parents and multiple children.)
+
+If we hold AC, and want to lock B, we recursively search through B's
+children to see if A or C appears. It does, so we reject the attempt.
+(A straightforward way to implement it: add a link from C to B, then
+determine whether the graph starting at B contains a cycle.)
+
+If we hold AC and want to lock D, we would succeed, creating a new link
+from C to D.
+
+The lock history and a stack trace is attached to the Object's Monitor
+struct, which means we need to fatten every Object we lock (thin locking
+is effectively disabled). If we don't need the stack trace we can
+avoid fattening the leaf nodes, only fattening objects that need to hold
+history trees.
+
+Updates to Monitor structs are only allowed for the thread that holds
+the Monitor, so we actually do most of our deadlock prediction work after
+the lock has been acquired.
+
+When an object with a monitor is GCed, we need to remove it from the
+history trees. There are two basic approaches:
+ (1) For through the entire set of known monitors, search all child
+ lists for the object in question. This is rather slow, resulting
+ in GC passes that take upwards of 10 seconds to complete.
+ (2) Maintain "parent" pointers in each node. Remove the entries as
+ required. This requires additional storage and maintenance for
+ every operation, but is significantly faster at GC time.
+For each GCed object, we merge all of the object's children into each of
+the object's parents.
+*/
+
+#if !defined(WITH_MONITOR_TRACKING)
+# error "WITH_DEADLOCK_PREDICTION requires WITH_MONITOR_TRACKING"
+#endif
+
+/*
+ * Clear out the contents of an ExpandingObjectList, freeing any
+ * dynamic allocations.
+ */
+static void expandObjClear(ExpandingObjectList* pList)
+{
+ if (pList->list != NULL) {
+ free(pList->list);
+ pList->list = NULL;
+ }
+ pList->alloc = pList->count = 0;
+}
+
+/*
+ * Get the number of objects currently stored in the list.
+ */
+static inline int expandBufGetCount(const ExpandingObjectList* pList)
+{
+ return pList->count;
+}
+
+/*
+ * Get the Nth entry from the list.
+ */
+static inline Object* expandBufGetEntry(const ExpandingObjectList* pList,
+ int i)
+{
+ return pList->list[i];
+}
+
+/*
+ * Add a new entry to the list.
+ *
+ * We don't check for or try to enforce uniqueness. It's expected that
+ * the higher-level code does this for us.
+ */
+static void expandObjAddEntry(ExpandingObjectList* pList, Object* obj)
+{
+ if (pList->count == pList->alloc) {
+ /* time to expand */
+ Object** newList;
+
+ if (pList->alloc == 0)
+ pList->alloc = 4;
+ else
+ pList->alloc *= 2;
+ LOGVV("expanding %p to %d\n", pList, pList->alloc);
+ newList = realloc(pList->list, pList->alloc * sizeof(Object*));
+ if (newList == NULL) {
+ LOGE("Failed expanding DP object list (alloc=%d)\n", pList->alloc);
+ dvmAbort();
+ }
+ pList->list = newList;
+ }
+
+ pList->list[pList->count++] = obj;
+}
+
+/*
+ * Returns "true" if the element was successfully removed.
+ */
+static bool expandObjRemoveEntry(ExpandingObjectList* pList, Object* obj)
+{
+ int i;
+
+ for (i = pList->count-1; i >= 0; i--) {
+ if (pList->list[i] == obj)
+ break;
+ }
+ if (i < 0)
+ return false;
+
+ if (i != pList->count-1) {
+ /*
+ * The order of elements is not important, so we just copy the
+ * last entry into the new slot.
+ */
+ //memmove(&pList->list[i], &pList->list[i+1],
+ // (pList->count-1 - i) * sizeof(pList->list[0]));
+ pList->list[i] = pList->list[pList->count-1];
+ }
+
+ pList->count--;
+ pList->list[pList->count] = (Object*) 0xdecadead;
+ return true;
+}
+
+/*
+ * Returns "true" if "obj" appears in the list.
+ */
+static bool expandObjHas(const ExpandingObjectList* pList, Object* obj)
+{
+ int i;
+
+ for (i = 0; i < pList->count; i++) {
+ if (pList->list[i] == obj)
+ return true;
+ }
+ return false;
+}
+
+/*
+ * Print the list contents to stdout. For debugging.
+ */
+static void expandObjDump(const ExpandingObjectList* pList)
+{
+ int i;
+ for (i = 0; i < pList->count; i++)
+ printf(" %p", pList->list[i]);
+}
+
+/*
+ * Check for duplicate entries. Returns the index of the first instance
+ * of the duplicated value, or -1 if no duplicates were found.
+ */
+static int expandObjCheckForDuplicates(const ExpandingObjectList* pList)
+{
+ int i, j;
+ for (i = 0; i < pList->count-1; i++) {
+ for (j = i + 1; j < pList->count; j++) {
+ if (pList->list[i] == pList->list[j]) {
+ return i;
+ }
+ }
+ }
+
+ return -1;
+}
+
+
+/*
+ * Determine whether "child" appears in the list of objects associated
+ * with the Monitor in "parent". If "parent" is a thin lock, we return
+ * false immediately.
+ */
+static bool objectInChildList(const Object* parent, Object* child)
+{
+ u4 lock = parent->lock;
+ if (!IS_LOCK_FAT(&lock)) {
+ //LOGI("on thin\n");
+ return false;
+ }
+
+ return expandObjHas(&LW_MONITOR(lock)->historyChildren, child);
+}
+
+/*
+ * Print the child list.
+ */
+static void dumpKids(Object* parent)
+{
+ Monitor* mon = LW_MONITOR(parent->lock);
+
+ printf("Children of %p:", parent);
+ expandObjDump(&mon->historyChildren);
+ printf("\n");
+}
+
+/*
+ * Add "child" to the list of children in "parent", and add "parent" to
+ * the list of parents in "child".
+ */
+static void linkParentToChild(Object* parent, Object* child)
+{
+ //assert(LW_MONITOR(parent->lock)->owner == dvmThreadSelf()); // !owned for merge
+ assert(IS_LOCK_FAT(&parent->lock));
+ assert(IS_LOCK_FAT(&child->lock));
+ assert(parent != child);
+ Monitor* mon;
+
+ mon = LW_MONITOR(parent->lock);
+ assert(!expandObjHas(&mon->historyChildren, child));
+ expandObjAddEntry(&mon->historyChildren, child);
+
+ mon = LW_MONITOR(child->lock);
+ assert(!expandObjHas(&mon->historyParents, parent));
+ expandObjAddEntry(&mon->historyParents, parent);
+}
+
+
+/*
+ * Remove "child" from the list of children in "parent".
+ */
+static void unlinkParentFromChild(Object* parent, Object* child)
+{
+ //assert(LW_MONITOR(parent->lock)->owner == dvmThreadSelf()); // !owned for GC
+ assert(IS_LOCK_FAT(&parent->lock));
+ assert(IS_LOCK_FAT(&child->lock));
+ assert(parent != child);
+ Monitor* mon;
+
+ mon = LW_MONITOR(parent->lock);
+ if (!expandObjRemoveEntry(&mon->historyChildren, child)) {
+ LOGW("WARNING: child %p not found in parent %p\n", child, parent);
+ }
+ assert(!expandObjHas(&mon->historyChildren, child));
+ assert(expandObjCheckForDuplicates(&mon->historyChildren) < 0);
+
+ mon = LW_MONITOR(child->lock);
+ if (!expandObjRemoveEntry(&mon->historyParents, parent)) {
+ LOGW("WARNING: parent %p not found in child %p\n", parent, child);
+ }
+ assert(!expandObjHas(&mon->historyParents, parent));
+ assert(expandObjCheckForDuplicates(&mon->historyParents) < 0);
+}
+
+
+/*
+ * Log the monitors held by the current thread. This is done as part of
+ * flagging an error.
+ */
+static void logHeldMonitors(Thread* self)
+{
+ char* name = NULL;
+
+ name = dvmGetThreadName(self);
+ LOGW("Monitors currently held by thread (threadid=%d '%s')\n",
+ self->threadId, name);
+ LOGW("(most-recently-acquired on top):\n");
+ free(name);
+
+ LockedObjectData* lod = self->pLockedObjects;
+ while (lod != NULL) {
+ LOGW("--- object %p[%d] (%s)\n",
+ lod->obj, lod->recursionCount, lod->obj->clazz->descriptor);
+ dvmLogRawStackTrace(lod->rawStackTrace, lod->stackDepth);
+
+ lod = lod->next;
+ }
+}
+
+/*
+ * Recursively traverse the object hierarchy starting at "obj". We mark
+ * ourselves on entry and clear the mark on exit. If we ever encounter
+ * a marked object, we have a cycle.
+ *
+ * Returns "true" if all is well, "false" if we found a cycle.
+ */
+static bool traverseTree(Thread* self, const Object* obj)
+{
+ assert(IS_LOCK_FAT(&obj->lock));
+ Monitor* mon = LW_MONITOR(obj->lock);
+
+ /*
+ * Have we been here before?
+ */
+ if (mon->historyMark) {
+ int* rawStackTrace;
+ int stackDepth;
+
+ LOGW("%s\n", kStartBanner);
+ LOGW("Illegal lock attempt:\n");
+ LOGW("--- object %p (%s)\n", obj, obj->clazz->descriptor);
+
+ rawStackTrace = dvmFillInStackTraceRaw(self, &stackDepth);
+ dvmLogRawStackTrace(rawStackTrace, stackDepth);
+ free(rawStackTrace);
+
+ LOGW(" ");
+ logHeldMonitors(self);
+
+ LOGW(" ");
+ LOGW("Earlier, the following lock order (from last to first) was\n");
+ LOGW("established -- stack trace is from first successful lock):\n");
+ return false;
+ }
+ mon->historyMark = true;
+
+ /*
+ * Examine the children. We do NOT hold these locks, so they might
+ * very well transition from thin to fat or change ownership while
+ * we work.
+ *
+ * NOTE: we rely on the fact that they cannot revert from fat to thin
+ * while we work. This is currently a safe assumption.
+ *
+ * We can safely ignore thin-locked children, because by definition
+ * they have no history and are leaf nodes. In the current
+ * implementation we always fatten the locks to provide a place to
+ * hang the stack trace.
+ */
+ ExpandingObjectList* pList = &mon->historyChildren;
+ int i;
+ for (i = expandBufGetCount(pList)-1; i >= 0; i--) {
+ const Object* child = expandBufGetEntry(pList, i);
+ u4 lock = child->lock;
+ if (!IS_LOCK_FAT(&lock))
+ continue;
+ if (!traverseTree(self, child)) {
+ LOGW("--- object %p (%s)\n", obj, obj->clazz->descriptor);
+ dvmLogRawStackTrace(mon->historyRawStackTrace,
+ mon->historyStackDepth);
+ mon->historyMark = false;
+ return false;
+ }
+ }
+
+ mon->historyMark = false;
+
+ return true;
+}
+
+/*
+ * Update the deadlock prediction tree, based on the current thread
+ * acquiring "acqObj". This must be called before the object is added to
+ * the thread's list of held monitors.
+ *
+ * If the thread already holds the lock (recursion), or this is a known
+ * lock configuration, we return without doing anything. Otherwise, we add
+ * a link from the most-recently-acquired lock in this thread to "acqObj"
+ * after ensuring that the parent lock is "fat".
+ *
+ * This MUST NOT be called while a GC is in progress in another thread,
+ * because we assume exclusive access to history trees in owned monitors.
+ */
+static void updateDeadlockPrediction(Thread* self, Object* acqObj)
+{
+ LockedObjectData* lod;
+ LockedObjectData* mrl;
+
+ /*
+ * Quick check for recursive access.
+ */
+ lod = dvmFindInMonitorList(self, acqObj);
+ if (lod != NULL) {
+ LOGV("+++ DP: recursive %p\n", acqObj);
+ return;
+ }
+
+ /*
+ * Make the newly-acquired object's monitor "fat". In some ways this
+ * isn't strictly necessary, but we need the GC to tell us when
+ * "interesting" objects go away, and right now the only way to make
+ * an object look interesting is to give it a monitor.
+ *
+ * This also gives us a place to hang a stack trace.
+ *
+ * Our thread holds the lock, so we're allowed to rewrite the lock
+ * without worrying that something will change out from under us.
+ */
+ if (!IS_LOCK_FAT(&acqObj->lock)) {
+ LOGVV("fattening lockee %p (recur=%d)\n",
+ acqObj, LW_LOCK_COUNT(acqObj->lock.thin));
+ inflateMonitor(self, acqObj);
+ }
+
+ /* if we don't have a stack trace for this monitor, establish one */
+ if (LW_MONITOR(acqObj->lock)->historyRawStackTrace == NULL) {
+ Monitor* mon = LW_MONITOR(acqObj->lock);
+ mon->historyRawStackTrace = dvmFillInStackTraceRaw(self,
+ &mon->historyStackDepth);
+ }
+
+ /*
+ * We need to examine and perhaps modify the most-recently-locked
+ * monitor. We own that, so there's no risk of another thread
+ * stepping on us.
+ *
+ * Retrieve the most-recently-locked entry from our thread.
+ */
+ mrl = self->pLockedObjects;
+ if (mrl == NULL)
+ return; /* no other locks held */
+
+ /*
+ * Do a quick check to see if "acqObj" is a direct descendant. We can do
+ * this without holding the global lock because of our assertion that
+ * a GC is not running in parallel -- nobody except the GC can
+ * modify a history list in a Monitor they don't own, and we own "mrl".
+ * (There might be concurrent *reads*, but no concurrent *writes.)
+ *
+ * If we find it, this is a known good configuration, and we're done.
+ */
+ if (objectInChildList(mrl->obj, acqObj))
+ return;
+
+ /*
+ * "mrl" is going to need to have a history tree. If it's currently
+ * a thin lock, we make it fat now. The thin lock might have a
+ * nonzero recursive lock count, which we need to carry over.
+ *
+ * Our thread holds the lock, so we're allowed to rewrite the lock
+ * without worrying that something will change out from under us.
+ */
+ if (!IS_LOCK_FAT(&mrl->obj->lock)) {
+ LOGVV("fattening parent %p f/b/o child %p (recur=%d)\n",
+ mrl->obj, acqObj, LW_LOCK_COUNT(mrl->obj->lock));
+ inflateMonitor(self, mrl->obj);
+ }
+
+ /*
+ * We haven't seen this configuration before. We need to scan down
+ * acqObj's tree to see if any of the monitors in self->pLockedObjects
+ * appear. We grab a global lock before traversing or updating the
+ * history list.
+ *
+ * If we find a match for any of our held locks, we know that the lock
+ * has previously been acquired *after* acqObj, and we throw an error.
+ *
+ * The easiest way to do this is to create a link from "mrl" to "acqObj"
+ * and do a recursive traversal, marking nodes as we cross them. If
+ * we cross one a second time, we have a cycle and can throw an error.
+ * (We do the flag-clearing traversal before adding the new link, so
+ * that we're guaranteed to terminate.)
+ *
+ * If "acqObj" is a thin lock, it has no history, and we can create a
+ * link to it without additional checks. [ We now guarantee that it's
+ * always fat. ]
+ */
+ bool failed = false;
+ dvmLockMutex(&gDvm.deadlockHistoryLock);
+ linkParentToChild(mrl->obj, acqObj);
+ if (!traverseTree(self, acqObj)) {
+ LOGW("%s\n", kEndBanner);
+ failed = true;
+
+ /* remove the entry so we're still okay when in "warning" mode */
+ unlinkParentFromChild(mrl->obj, acqObj);
+ }
+ dvmUnlockMutex(&gDvm.deadlockHistoryLock);
+
+ if (failed) {
+ switch (gDvm.deadlockPredictMode) {
+ case kDPErr:
+ dvmThrowException("Ldalvik/system/PotentialDeadlockError;", NULL);
+ break;
+ case kDPAbort:
+ LOGE("Aborting due to potential deadlock\n");
+ dvmAbort();
+ break;
+ default:
+ /* warn only */
+ break;
+ }
+ }
+}
+
+/*
+ * We're removing "child" from existence. We want to pull all of
+ * child's children into "parent", filtering out duplicates. This is
+ * called during the GC.
+ *
+ * This does not modify "child", which might have multiple parents.
+ */
+static void mergeChildren(Object* parent, const Object* child)
+{
+ Monitor* mon;
+ int i;
+
+ assert(IS_LOCK_FAT(&child->lock));
+ mon = LW_MONITOR(child->lock);
+ ExpandingObjectList* pList = &mon->historyChildren;
+
+ for (i = expandBufGetCount(pList)-1; i >= 0; i--) {
+ Object* grandChild = expandBufGetEntry(pList, i);
+
+ if (!objectInChildList(parent, grandChild)) {
+ LOGVV("+++ migrating %p link to %p\n", grandChild, parent);
+ linkParentToChild(parent, grandChild);
+ } else {
+ LOGVV("+++ parent %p already links to %p\n", parent, grandChild);
+ }
+ }
+}
+
+/*
+ * An object with a fat lock is being collected during a GC pass. We
+ * want to remove it from any lock history trees that it is a part of.
+ *
+ * This may require updating the history trees in several monitors. The
+ * monitor semantics guarantee that no other thread will be accessing
+ * the history trees at the same time.
+ */
+static void removeCollectedObject(Object* obj)
+{
+ Monitor* mon;
+
+ LOGVV("+++ collecting %p\n", obj);
+
+ /*
+ * For every parent of this object:
+ * - merge all of our children into the parent's child list (creates
+ * a two-way link between parent and child)
+ * - remove ourselves from the parent's child list
+ */
+ ExpandingObjectList* pList;
+ int i;
+
+ assert(IS_LOCK_FAT(&obj->lock));
+ mon = LW_MONITOR(obj->lock);
+ pList = &mon->historyParents;
+ for (i = expandBufGetCount(pList)-1; i >= 0; i--) {
+ Object* parent = expandBufGetEntry(pList, i);
+ Monitor* parentMon = LW_MONITOR(parent->lock);
+
+ if (!expandObjRemoveEntry(&parentMon->historyChildren, obj)) {
+ LOGW("WARNING: child %p not found in parent %p\n", obj, parent);
+ }
+ assert(!expandObjHas(&parentMon->historyChildren, obj));
+
+ mergeChildren(parent, obj);
+ }
+
+ /*
+ * For every child of this object:
+ * - remove ourselves from the child's parent list
+ */
+ pList = &mon->historyChildren;
+ for (i = expandBufGetCount(pList)-1; i >= 0; i--) {
+ Object* child = expandBufGetEntry(pList, i);
+ Monitor* childMon = LW_MONITOR(child->lock);
+
+ if (!expandObjRemoveEntry(&childMon->historyParents, obj)) {
+ LOGW("WARNING: parent %p not found in child %p\n", obj, child);
+ }
+ assert(!expandObjHas(&childMon->historyParents, obj));
+ }
+}
+
+#endif /*WITH_DEADLOCK_PREDICTION*/
diff --git a/vm/Sync.h b/vm/Sync.h
new file mode 100644
index 0000000..1a168b9
--- /dev/null
+++ b/vm/Sync.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Object synchronization functions.
+ */
+#ifndef _DALVIK_SYNC
+#define _DALVIK_SYNC
+
+/*
+ * Monitor shape field. Used to distinguish immediate thin locks from
+ * indirecting fat locks.
+ */
+#define LW_SHAPE_THIN 0
+#define LW_SHAPE_FAT 1
+#define LW_SHAPE_MASK 0x1
+#define LW_SHAPE(x) ((x) & LW_SHAPE_MASK)
+
+/*
+ * Hash state field. Used to signify that an object has had its
+ * identity hash code exposed or relocated.
+ */
+#define LW_HASH_STATE_UNHASHED 0
+#define LW_HASH_STATE_HASHED 1
+#define LW_HASH_STATE_HASHED_AND_MOVED 3
+#define LW_HASH_STATE_MASK 0x3
+#define LW_HASH_STATE_SHIFT 1
+#define LW_HASH_STATE(x) (((x) >> LW_HASH_STATE_SHIFT) & LW_HASH_STATE_MASK)
+
+/*
+ * Monitor accessor. Extracts a monitor structure pointer from a fat
+ * lock. Performs no error checking.
+ */
+#define LW_MONITOR(x) \
+ ((Monitor*)((x) & ~((LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT) | \
+ LW_SHAPE_MASK)))
+
+/*
+ * Lock owner field. Contains the thread id of the thread currently
+ * holding the lock.
+ */
+#define LW_LOCK_OWNER_MASK 0xffff
+#define LW_LOCK_OWNER_SHIFT 3
+#define LW_LOCK_OWNER(x) (((x) >> LW_LOCK_OWNER_SHIFT) & LW_LOCK_OWNER_MASK)
+
+/*
+ * Lock recursion count field. Contains a count of the numer of times
+ * a lock has been recursively acquired.
+ */
+#define LW_LOCK_COUNT_MASK 0x1fff
+#define LW_LOCK_COUNT_SHIFT 19
+#define LW_LOCK_COUNT(x) (((x) >> LW_LOCK_COUNT_SHIFT) & LW_LOCK_COUNT_MASK)
+
+struct Object;
+struct Monitor;
+struct Thread;
+typedef struct Monitor Monitor;
+
+#define QUIET_ZYGOTE_MONITOR 1
+
+/*
+ * Initialize a Lock to the proper starting value.
+ * This is necessary for thin locking.
+ */
+#define DVM_LOCK_INITIAL_THIN_VALUE (0)
+
+#define DVM_LOCK_INIT(lock) \
+ do { *(lock) = DVM_LOCK_INITIAL_THIN_VALUE; } while (0)
+
+/*
+ * Returns true if the lock has been fattened.
+ */
+#define IS_LOCK_FAT(lock) (LW_SHAPE(*(lock)) == LW_SHAPE_FAT)
+
+/*
+ * Acquire the object's monitor.
+ */
+void dvmLockObject(struct Thread* self, struct Object* obj);
+
+/* Returns true if the unlock succeeded.
+ * If the unlock failed, an exception will be pending.
+ */
+bool dvmUnlockObject(struct Thread* self, struct Object* obj);
+
+/*
+ * Implementations of some java/lang/Object calls.
+ */
+void dvmObjectWait(struct Thread* self, struct Object* obj,
+ s8 timeout, s4 nanos, bool interruptShouldThrow);
+void dvmObjectNotify(struct Thread* self, struct Object* obj);
+void dvmObjectNotifyAll(struct Thread* self, struct Object* obj);
+
+/*
+ * Implementation of System.identityHashCode().
+ */
+u4 dvmIdentityHashCode(struct Object* obj);
+
+/*
+ * Implementation of Thread.sleep().
+ */
+void dvmThreadSleep(u8 msec, u4 nsec);
+
+/*
+ * Implementation of Thread.interrupt().
+ *
+ * Interrupt a thread. If it's waiting on a monitor, wake it up.
+ */
+void dvmThreadInterrupt(struct Thread* thread);
+
+/* create a new Monitor struct */
+Monitor* dvmCreateMonitor(struct Object* obj);
+
+/*
+ * Frees unmarked monitors from the monitor list. The given callback
+ * routine should return a non-zero value when passed a pointer to an
+ * unmarked object.
+ */
+void dvmSweepMonitorList(Monitor** mon, int (*isUnmarkedObject)(void*));
+
+/* free monitor list */
+void dvmFreeMonitorList(void);
+
+/*
+ * Get the object a monitor is part of.
+ *
+ * Returns NULL if "mon" is NULL or the monitor is not part of an object
+ * (which should only happen for Thread.sleep() in the current implementation).
+ */
+struct Object* dvmGetMonitorObject(Monitor* mon);
+
+/*
+ * Get the thread that holds the lock on the specified object. The
+ * object may be unlocked, thin-locked, or fat-locked.
+ *
+ * The caller must lock the thread list before calling here.
+ */
+struct Thread* dvmGetObjectLockHolder(struct Object* obj);
+
+/*
+ * Checks whether the object is held by the specified thread.
+ */
+bool dvmHoldsLock(struct Thread* thread, struct Object* obj);
+
+/*
+ * Relative timed wait on condition
+ */
+int dvmRelativeCondWait(pthread_cond_t* cond, pthread_mutex_t* mutex,
+ s8 msec, s4 nsec);
+
+/*
+ * Debug.
+ */
+void dvmDumpMonitorInfo(const char* msg);
+
+#endif /*_DALVIK_SYNC*/
diff --git a/vm/TestCompability.c b/vm/TestCompability.c
new file mode 100644
index 0000000..d447db3
--- /dev/null
+++ b/vm/TestCompability.c
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <Dalvik.h>
+
+#if defined(TEST_VM_IN_ECLAIR)
+FILE *open_memstream(char **ptr, size_t *sizeloc)
+{
+ LOGE("Fake open_memstream entered");
+ dvmAbort();
+ return NULL;
+}
+#endif
diff --git a/vm/Thread.c b/vm/Thread.c
new file mode 100644
index 0000000..c9b6311
--- /dev/null
+++ b/vm/Thread.c
@@ -0,0 +1,4219 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Thread support.
+ */
+#include "Dalvik.h"
+
+#include "utils/threads.h" // need Android thread priorities
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/resource.h>
+#include <sys/mman.h>
+#include <signal.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#if defined(HAVE_PRCTL)
+#include <sys/prctl.h>
+#endif
+
+#if defined(WITH_SELF_VERIFICATION)
+#include "interp/Jit.h" // need for self verification
+#endif
+
+
+/* desktop Linux needs a little help with gettid() */
+#if defined(HAVE_GETTID) && !defined(HAVE_ANDROID_OS)
+#define __KERNEL__
+# include <linux/unistd.h>
+#ifdef _syscall0
+_syscall0(pid_t,gettid)
+#else
+pid_t gettid() { return syscall(__NR_gettid);}
+#endif
+#undef __KERNEL__
+#endif
+
+// Change this to enable logging on cgroup errors
+#define ENABLE_CGROUP_ERR_LOGGING 0
+
+// change this to LOGV/LOGD to debug thread activity
+#define LOG_THREAD LOGVV
+
+/*
+Notes on Threading
+
+All threads are native pthreads. All threads, except the JDWP debugger
+thread, are visible to code running in the VM and to the debugger. (We
+don't want the debugger to try to manipulate the thread that listens for
+instructions from the debugger.) Internal VM threads are in the "system"
+ThreadGroup, all others are in the "main" ThreadGroup, per convention.
+
+The GC only runs when all threads have been suspended. Threads are
+expected to suspend themselves, using a "safe point" mechanism. We check
+for a suspend request at certain points in the main interpreter loop,
+and on requests coming in from native code (e.g. all JNI functions).
+Certain debugger events may inspire threads to self-suspend.
+
+Native methods must use JNI calls to modify object references to avoid
+clashes with the GC. JNI doesn't provide a way for native code to access
+arrays of objects as such -- code must always get/set individual entries --
+so it should be possible to fully control access through JNI.
+
+Internal native VM threads, such as the finalizer thread, must explicitly
+check for suspension periodically. In most cases they will be sound
+asleep on a condition variable, and won't notice the suspension anyway.
+
+Threads may be suspended by the GC, debugger, or the SIGQUIT listener
+thread. The debugger may suspend or resume individual threads, while the
+GC always suspends all threads. Each thread has a "suspend count" that
+is incremented on suspend requests and decremented on resume requests.
+When the count is zero, the thread is runnable. This allows us to fulfill
+a debugger requirement: if the debugger suspends a thread, the thread is
+not allowed to run again until the debugger resumes it (or disconnects,
+in which case we must resume all debugger-suspended threads).
+
+Paused threads sleep on a condition variable, and are awoken en masse.
+Certain "slow" VM operations, such as starting up a new thread, will be
+done in a separate "VMWAIT" state, so that the rest of the VM doesn't
+freeze up waiting for the operation to finish. Threads must check for
+pending suspension when leaving VMWAIT.
+
+Because threads suspend themselves while interpreting code or when native
+code makes JNI calls, there is no risk of suspending while holding internal
+VM locks. All threads can enter a suspended (or native-code-only) state.
+Also, we don't have to worry about object references existing solely
+in hardware registers.
+
+We do, however, have to worry about objects that were allocated internally
+and aren't yet visible to anything else in the VM. If we allocate an
+object, and then go to sleep on a mutex after changing to a non-RUNNING
+state (e.g. while trying to allocate a second object), the first object
+could be garbage-collected out from under us while we sleep. To manage
+this, we automatically add all allocated objects to an internal object
+tracking list, and only remove them when we know we won't be suspended
+before the object appears in the GC root set.
+
+The debugger may choose to suspend or resume a single thread, which can
+lead to application-level deadlocks; this is expected behavior. The VM
+will only check for suspension of single threads when the debugger is
+active (the java.lang.Thread calls for this are deprecated and hence are
+not supported). Resumption of a single thread is handled by decrementing
+the thread's suspend count and sending a broadcast signal to the condition
+variable. (This will cause all threads to wake up and immediately go back
+to sleep, which isn't tremendously efficient, but neither is having the
+debugger attached.)
+
+The debugger is not allowed to resume threads suspended by the GC. This
+is trivially enforced by ignoring debugger requests while the GC is running
+(the JDWP thread is suspended during GC).
+
+The VM maintains a Thread struct for every pthread known to the VM. There
+is a java/lang/Thread object associated with every Thread. At present,
+there is no safe way to go from a Thread object to a Thread struct except by
+locking and scanning the list; this is necessary because the lifetimes of
+the two are not closely coupled. We may want to change this behavior,
+though at present the only performance impact is on the debugger (see
+threadObjToThread()). See also notes about dvmDetachCurrentThread().
+*/
+/*
+Alternate implementation (signal-based):
+
+Threads run without safe points -- zero overhead. The VM uses a signal
+(e.g. pthread_kill(SIGUSR1)) to notify threads of suspension or resumption.
+
+The trouble with using signals to suspend threads is that it means a thread
+can be in the middle of an operation when garbage collection starts.
+To prevent some sticky situations, we have to introduce critical sections
+to the VM code.
+
+Critical sections temporarily block suspension for a given thread.
+The thread must move to a non-blocked state (and self-suspend) after
+finishing its current task. If the thread blocks on a resource held
+by a suspended thread, we're hosed.
+
+One approach is to require that no blocking operations, notably
+acquisition of mutexes, can be performed within a critical section.
+This is too limiting. For example, if thread A gets suspended while
+holding the thread list lock, it will prevent the GC or debugger from
+being able to safely access the thread list. We need to wrap the critical
+section around the entire operation (enter critical, get lock, do stuff,
+release lock, exit critical).
+
+A better approach is to declare that certain resources can only be held
+within critical sections. A thread that enters a critical section and
+then gets blocked on the thread list lock knows that the thread it is
+waiting for is also in a critical section, and will release the lock
+before suspending itself. Eventually all threads will complete their
+operations and self-suspend. For this to work, the VM must:
+
+ (1) Determine the set of resources that may be accessed from the GC or
+ debugger threads. The mutexes guarding those go into the "critical
+ resource set" (CRS).
+ (2) Ensure that no resource in the CRS can be acquired outside of a
+ critical section. This can be verified with an assert().
+ (3) Ensure that only resources in the CRS can be held while in a critical
+ section. This is harder to enforce.
+
+If any of these conditions are not met, deadlock can ensue when grabbing
+resources in the GC or debugger (#1) or waiting for threads to suspend
+(#2,#3). (You won't actually deadlock in the GC, because if the semantics
+above are followed you don't need to lock anything in the GC. The risk is
+rather that the GC will access data structures in an intermediate state.)
+
+This approach requires more care and awareness in the VM than
+safe-pointing. Because the GC and debugger are fairly intrusive, there
+really aren't any internal VM resources that aren't shared. Thus, the
+enter/exit critical calls can be added to internal mutex wrappers, which
+makes it easy to get #1 and #2 right.
+
+An ordering should be established for all locks to avoid deadlocks.
+
+Monitor locks, which are also implemented with pthread calls, should not
+cause any problems here. Threads fighting over such locks will not be in
+critical sections and can be suspended freely.
+
+This can get tricky if we ever need exclusive access to VM and non-VM
+resources at the same time. It's not clear if this is a real concern.
+
+There are (at least) two ways to handle the incoming signals:
+
+ (a) Always accept signals. If we're in a critical section, the signal
+ handler just returns without doing anything (the "suspend level"
+ should have been incremented before the signal was sent). Otherwise,
+ if the "suspend level" is nonzero, we go to sleep.
+ (b) Block signals in critical sections. This ensures that we can't be
+ interrupted in a critical section, but requires pthread_sigmask()
+ calls on entry and exit.
+
+This is a choice between blocking the message and blocking the messenger.
+Because UNIX signals are unreliable (you can only know that you have been
+signaled, not whether you were signaled once or 10 times), the choice is
+not significant for correctness. The choice depends on the efficiency
+of pthread_sigmask() and the desire to actually block signals. Either way,
+it is best to ensure that there is only one indication of "blocked";
+having two (i.e. block signals and set a flag, then only send a signal
+if the flag isn't set) can lead to race conditions.
+
+The signal handler must take care to copy registers onto the stack (via
+setjmp), so that stack scans find all references. Because we have to scan
+native stacks, "exact" GC is not possible with this approach.
+
+Some other concerns with flinging signals around:
+ - Odd interactions with some debuggers (e.g. gdb on the Mac)
+ - Restrictions on some standard library calls during GC (e.g. don't
+ use printf on stdout to print GC debug messages)
+*/
+
+#define kMaxThreadId ((1 << 16) - 1)
+#define kMainThreadId 1
+
+
+static Thread* allocThread(int interpStackSize);
+static bool prepareThread(Thread* thread);
+static void setThreadSelf(Thread* thread);
+static void unlinkThread(Thread* thread);
+static void freeThread(Thread* thread);
+static void assignThreadId(Thread* thread);
+static bool createFakeEntryFrame(Thread* thread);
+static bool createFakeRunFrame(Thread* thread);
+static void* interpThreadStart(void* arg);
+static void* internalThreadStart(void* arg);
+static void threadExitUncaughtException(Thread* thread, Object* group);
+static void threadExitCheck(void* arg);
+static void waitForThreadSuspend(Thread* self, Thread* thread);
+static int getThreadPriorityFromSystem(void);
+
+/*
+ * The JIT needs to know if any thread is suspended. We do this by
+ * maintaining a global sum of all threads' suspend counts. All suspendCount
+ * updates should go through this after aquiring threadSuspendCountLock.
+ */
+static inline void dvmAddToThreadSuspendCount(int *pSuspendCount, int delta)
+{
+ *pSuspendCount += delta;
+ gDvm.sumThreadSuspendCount += delta;
+}
+
+/*
+ * Initialize thread list and main thread's environment. We need to set
+ * up some basic stuff so that dvmThreadSelf() will work when we start
+ * loading classes (e.g. to check for exceptions).
+ */
+bool dvmThreadStartup(void)
+{
+ Thread* thread;
+
+ /* allocate a TLS slot */
+ if (pthread_key_create(&gDvm.pthreadKeySelf, threadExitCheck) != 0) {
+ LOGE("ERROR: pthread_key_create failed\n");
+ return false;
+ }
+
+ /* test our pthread lib */
+ if (pthread_getspecific(gDvm.pthreadKeySelf) != NULL)
+ LOGW("WARNING: newly-created pthread TLS slot is not NULL\n");
+
+ /* prep thread-related locks and conditions */
+ dvmInitMutex(&gDvm.threadListLock);
+ pthread_cond_init(&gDvm.threadStartCond, NULL);
+ //dvmInitMutex(&gDvm.vmExitLock);
+ pthread_cond_init(&gDvm.vmExitCond, NULL);
+ dvmInitMutex(&gDvm._threadSuspendLock);
+ dvmInitMutex(&gDvm.threadSuspendCountLock);
+ pthread_cond_init(&gDvm.threadSuspendCountCond, NULL);
+#ifdef WITH_DEADLOCK_PREDICTION
+ dvmInitMutex(&gDvm.deadlockHistoryLock);
+#endif
+
+ /*
+ * Dedicated monitor for Thread.sleep().
+ * TODO: change this to an Object* so we don't have to expose this
+ * call, and we interact better with JDWP monitor calls. Requires
+ * deferring the object creation to much later (e.g. final "main"
+ * thread prep) or until first use.
+ */
+ gDvm.threadSleepMon = dvmCreateMonitor(NULL);
+
+ gDvm.threadIdMap = dvmAllocBitVector(kMaxThreadId, false);
+
+ thread = allocThread(gDvm.stackSize);
+ if (thread == NULL)
+ return false;
+
+ /* switch mode for when we run initializers */
+ thread->status = THREAD_RUNNING;
+
+ /*
+ * We need to assign the threadId early so we can lock/notify
+ * object monitors. We'll set the "threadObj" field later.
+ */
+ prepareThread(thread);
+ gDvm.threadList = thread;
+
+#ifdef COUNT_PRECISE_METHODS
+ gDvm.preciseMethods = dvmPointerSetAlloc(200);
+#endif
+
+ return true;
+}
+
+/*
+ * We're a little farther up now, and can load some basic classes.
+ *
+ * We're far enough along that we can poke at java.lang.Thread and friends,
+ * but should not assume that static initializers have run (or cause them
+ * to do so). That means no object allocations yet.
+ */
+bool dvmThreadObjStartup(void)
+{
+ /*
+ * Cache the locations of these classes. It's likely that we're the
+ * first to reference them, so they're being loaded now.
+ */
+ gDvm.classJavaLangThread =
+ dvmFindSystemClassNoInit("Ljava/lang/Thread;");
+ gDvm.classJavaLangVMThread =
+ dvmFindSystemClassNoInit("Ljava/lang/VMThread;");
+ gDvm.classJavaLangThreadGroup =
+ dvmFindSystemClassNoInit("Ljava/lang/ThreadGroup;");
+ if (gDvm.classJavaLangThread == NULL ||
+ gDvm.classJavaLangThreadGroup == NULL ||
+ gDvm.classJavaLangThreadGroup == NULL)
+ {
+ LOGE("Could not find one or more essential thread classes\n");
+ return false;
+ }
+
+ /*
+ * Cache field offsets. This makes things a little faster, at the
+ * expense of hard-coding non-public field names into the VM.
+ */
+ gDvm.offJavaLangThread_vmThread =
+ dvmFindFieldOffset(gDvm.classJavaLangThread,
+ "vmThread", "Ljava/lang/VMThread;");
+ gDvm.offJavaLangThread_group =
+ dvmFindFieldOffset(gDvm.classJavaLangThread,
+ "group", "Ljava/lang/ThreadGroup;");
+ gDvm.offJavaLangThread_daemon =
+ dvmFindFieldOffset(gDvm.classJavaLangThread, "daemon", "Z");
+ gDvm.offJavaLangThread_name =
+ dvmFindFieldOffset(gDvm.classJavaLangThread,
+ "name", "Ljava/lang/String;");
+ gDvm.offJavaLangThread_priority =
+ dvmFindFieldOffset(gDvm.classJavaLangThread, "priority", "I");
+
+ if (gDvm.offJavaLangThread_vmThread < 0 ||
+ gDvm.offJavaLangThread_group < 0 ||
+ gDvm.offJavaLangThread_daemon < 0 ||
+ gDvm.offJavaLangThread_name < 0 ||
+ gDvm.offJavaLangThread_priority < 0)
+ {
+ LOGE("Unable to find all fields in java.lang.Thread\n");
+ return false;
+ }
+
+ gDvm.offJavaLangVMThread_thread =
+ dvmFindFieldOffset(gDvm.classJavaLangVMThread,
+ "thread", "Ljava/lang/Thread;");
+ gDvm.offJavaLangVMThread_vmData =
+ dvmFindFieldOffset(gDvm.classJavaLangVMThread, "vmData", "I");
+ if (gDvm.offJavaLangVMThread_thread < 0 ||
+ gDvm.offJavaLangVMThread_vmData < 0)
+ {
+ LOGE("Unable to find all fields in java.lang.VMThread\n");
+ return false;
+ }
+
+ /*
+ * Cache the vtable offset for "run()".
+ *
+ * We don't want to keep the Method* because then we won't find see
+ * methods defined in subclasses.
+ */
+ Method* meth;
+ meth = dvmFindVirtualMethodByDescriptor(gDvm.classJavaLangThread, "run", "()V");
+ if (meth == NULL) {
+ LOGE("Unable to find run() in java.lang.Thread\n");
+ return false;
+ }
+ gDvm.voffJavaLangThread_run = meth->methodIndex;
+
+ /*
+ * Cache vtable offsets for ThreadGroup methods.
+ */
+ meth = dvmFindVirtualMethodByDescriptor(gDvm.classJavaLangThreadGroup,
+ "removeThread", "(Ljava/lang/Thread;)V");
+ if (meth == NULL) {
+ LOGE("Unable to find removeThread(Thread) in java.lang.ThreadGroup\n");
+ return false;
+ }
+ gDvm.voffJavaLangThreadGroup_removeThread = meth->methodIndex;
+
+ return true;
+}
+
+/*
+ * All threads should be stopped by now. Clean up some thread globals.
+ */
+void dvmThreadShutdown(void)
+{
+ if (gDvm.threadList != NULL) {
+ /*
+ * If we walk through the thread list and try to free the
+ * lingering thread structures (which should only be for daemon
+ * threads), the daemon threads may crash if they execute before
+ * the process dies. Let them leak.
+ */
+ freeThread(gDvm.threadList);
+ gDvm.threadList = NULL;
+ }
+
+ dvmFreeBitVector(gDvm.threadIdMap);
+
+ dvmFreeMonitorList();
+
+ pthread_key_delete(gDvm.pthreadKeySelf);
+}
+
+
+/*
+ * Grab the suspend count global lock.
+ */
+static inline void lockThreadSuspendCount(void)
+{
+ /*
+ * Don't try to change to VMWAIT here. When we change back to RUNNING
+ * we have to check for a pending suspend, which results in grabbing
+ * this lock recursively. Doesn't work with "fast" pthread mutexes.
+ *
+ * This lock is always held for very brief periods, so as long as
+ * mutex ordering is respected we shouldn't stall.
+ */
+ dvmLockMutex(&gDvm.threadSuspendCountLock);
+}
+
+/*
+ * Release the suspend count global lock.
+ */
+static inline void unlockThreadSuspendCount(void)
+{
+ dvmUnlockMutex(&gDvm.threadSuspendCountLock);
+}
+
+/*
+ * Grab the thread list global lock.
+ *
+ * This is held while "suspend all" is trying to make everybody stop. If
+ * the shutdown is in progress, and somebody tries to grab the lock, they'll
+ * have to wait for the GC to finish. Therefore it's important that the
+ * thread not be in RUNNING mode.
+ *
+ * We don't have to check to see if we should be suspended once we have
+ * the lock. Nobody can suspend all threads without holding the thread list
+ * lock while they do it, so by definition there isn't a GC in progress.
+ *
+ * This function deliberately avoids the use of dvmChangeStatus(),
+ * which could grab threadSuspendCountLock. To avoid deadlock, threads
+ * are required to grab the thread list lock before the thread suspend
+ * count lock. (See comment in DvmGlobals.)
+ *
+ * TODO: consider checking for suspend after acquiring the lock, and
+ * backing off if set. As stated above, it can't happen during normal
+ * execution, but it *can* happen during shutdown when daemon threads
+ * are being suspended.
+ */
+void dvmLockThreadList(Thread* self)
+{
+ ThreadStatus oldStatus;
+
+ if (self == NULL) /* try to get it from TLS */
+ self = dvmThreadSelf();
+
+ if (self != NULL) {
+ oldStatus = self->status;
+ self->status = THREAD_VMWAIT;
+ } else {
+ /* happens during VM shutdown */
+ //LOGW("NULL self in dvmLockThreadList\n");
+ oldStatus = -1; // shut up gcc
+ }
+
+ dvmLockMutex(&gDvm.threadListLock);
+
+ if (self != NULL)
+ self->status = oldStatus;
+}
+
+/*
+ * Release the thread list global lock.
+ */
+void dvmUnlockThreadList(void)
+{
+ dvmUnlockMutex(&gDvm.threadListLock);
+}
+
+/*
+ * Convert SuspendCause to a string.
+ */
+static const char* getSuspendCauseStr(SuspendCause why)
+{
+ switch (why) {
+ case SUSPEND_NOT: return "NOT?";
+ case SUSPEND_FOR_GC: return "gc";
+ case SUSPEND_FOR_DEBUG: return "debug";
+ case SUSPEND_FOR_DEBUG_EVENT: return "debug-event";
+ case SUSPEND_FOR_STACK_DUMP: return "stack-dump";
+ case SUSPEND_FOR_VERIFY: return "verify";
+#if defined(WITH_JIT)
+ case SUSPEND_FOR_TBL_RESIZE: return "table-resize";
+ case SUSPEND_FOR_IC_PATCH: return "inline-cache-patch";
+ case SUSPEND_FOR_CC_RESET: return "reset-code-cache";
+ case SUSPEND_FOR_REFRESH: return "refresh jit status";
+#endif
+ default: return "UNKNOWN";
+ }
+}
+
+/*
+ * Grab the "thread suspend" lock. This is required to prevent the
+ * GC and the debugger from simultaneously suspending all threads.
+ *
+ * If we fail to get the lock, somebody else is trying to suspend all
+ * threads -- including us. If we go to sleep on the lock we'll deadlock
+ * the VM. Loop until we get it or somebody puts us to sleep.
+ */
+static void lockThreadSuspend(const char* who, SuspendCause why)
+{
+ const int kSpinSleepTime = 3*1000*1000; /* 3s */
+ u8 startWhen = 0; // init req'd to placate gcc
+ int sleepIter = 0;
+ int cc;
+
+ do {
+ cc = dvmTryLockMutex(&gDvm._threadSuspendLock);
+ if (cc != 0) {
+ Thread* self = dvmThreadSelf();
+
+ if (!dvmCheckSuspendPending(self)) {
+ /*
+ * Could be that a resume-all is in progress, and something
+ * grabbed the CPU when the wakeup was broadcast. The thread
+ * performing the resume hasn't had a chance to release the
+ * thread suspend lock. (We release before the broadcast,
+ * so this should be a narrow window.)
+ *
+ * Could be we hit the window as a suspend was started,
+ * and the lock has been grabbed but the suspend counts
+ * haven't been incremented yet.
+ *
+ * Could be an unusual JNI thread-attach thing.
+ *
+ * Could be the debugger telling us to resume at roughly
+ * the same time we're posting an event.
+ *
+ * Could be two app threads both want to patch predicted
+ * chaining cells around the same time.
+ */
+ LOGI("threadid=%d ODD: want thread-suspend lock (%s:%s),"
+ " it's held, no suspend pending\n",
+ self->threadId, who, getSuspendCauseStr(why));
+ } else {
+ /* we suspended; reset timeout */
+ sleepIter = 0;
+ }
+
+ /* give the lock-holder a chance to do some work */
+ if (sleepIter == 0)
+ startWhen = dvmGetRelativeTimeUsec();
+ if (!dvmIterativeSleep(sleepIter++, kSpinSleepTime, startWhen)) {
+ LOGE("threadid=%d: couldn't get thread-suspend lock (%s:%s),"
+ " bailing\n",
+ self->threadId, who, getSuspendCauseStr(why));
+ /* threads are not suspended, thread dump could crash */
+ dvmDumpAllThreads(false);
+ dvmAbort();
+ }
+ }
+ } while (cc != 0);
+ assert(cc == 0);
+}
+
+/*
+ * Release the "thread suspend" lock.
+ */
+static inline void unlockThreadSuspend(void)
+{
+ dvmUnlockMutex(&gDvm._threadSuspendLock);
+}
+
+
+/*
+ * Kill any daemon threads that still exist. All of ours should be
+ * stopped, so these should be Thread objects or JNI-attached threads
+ * started by the application. Actively-running threads are likely
+ * to crash the process if they continue to execute while the VM
+ * shuts down, so we really need to kill or suspend them. (If we want
+ * the VM to restart within this process, we need to kill them, but that
+ * leaves open the possibility of orphaned resources.)
+ *
+ * Waiting for the thread to suspend may be unwise at this point, but
+ * if one of these is wedged in a critical section then we probably
+ * would've locked up on the last GC attempt.
+ *
+ * It's possible for this function to get called after a failed
+ * initialization, so be careful with assumptions about the environment.
+ *
+ * This will be called from whatever thread calls DestroyJavaVM, usually
+ * but not necessarily the main thread. It's likely, but not guaranteed,
+ * that the current thread has already been cleaned up.
+ */
+void dvmSlayDaemons(void)
+{
+ Thread* self = dvmThreadSelf(); // may be null
+ Thread* target;
+ int threadId = 0;
+ bool doWait = false;
+
+ dvmLockThreadList(self);
+
+ if (self != NULL)
+ threadId = self->threadId;
+
+ target = gDvm.threadList;
+ while (target != NULL) {
+ if (target == self) {
+ target = target->next;
+ continue;
+ }
+
+ if (!dvmGetFieldBoolean(target->threadObj,
+ gDvm.offJavaLangThread_daemon))
+ {
+ /* should never happen; suspend it with the rest */
+ LOGW("threadid=%d: non-daemon id=%d still running at shutdown?!\n",
+ threadId, target->threadId);
+ }
+
+ char* threadName = dvmGetThreadName(target);
+ LOGD("threadid=%d: suspending daemon id=%d name='%s'\n",
+ threadId, target->threadId, threadName);
+ free(threadName);
+
+ /* mark as suspended */
+ lockThreadSuspendCount();
+ dvmAddToThreadSuspendCount(&target->suspendCount, 1);
+ unlockThreadSuspendCount();
+ doWait = true;
+
+ target = target->next;
+ }
+
+ //dvmDumpAllThreads(false);
+
+ /*
+ * Unlock the thread list, relocking it later if necessary. It's
+ * possible a thread is in VMWAIT after calling dvmLockThreadList,
+ * and that function *doesn't* check for pending suspend after
+ * acquiring the lock. We want to let them finish their business
+ * and see the pending suspend before we continue here.
+ *
+ * There's no guarantee of mutex fairness, so this might not work.
+ * (The alternative is to have dvmLockThreadList check for suspend
+ * after acquiring the lock and back off, something we should consider.)
+ */
+ dvmUnlockThreadList();
+
+ if (doWait) {
+ bool complained = false;
+
+ usleep(200 * 1000);
+
+ dvmLockThreadList(self);
+
+ /*
+ * Sleep for a bit until the threads have suspended. We're trying
+ * to exit, so don't wait for too long.
+ */
+ int i;
+ for (i = 0; i < 10; i++) {
+ bool allSuspended = true;
+
+ target = gDvm.threadList;
+ while (target != NULL) {
+ if (target == self) {
+ target = target->next;
+ continue;
+ }
+
+ if (target->status == THREAD_RUNNING) {
+ if (!complained)
+ LOGD("threadid=%d not ready yet\n", target->threadId);
+ allSuspended = false;
+ /* keep going so we log each running daemon once */
+ }
+
+ target = target->next;
+ }
+
+ if (allSuspended) {
+ LOGD("threadid=%d: all daemons have suspended\n", threadId);
+ break;
+ } else {
+ if (!complained) {
+ complained = true;
+ LOGD("threadid=%d: waiting briefly for daemon suspension\n",
+ threadId);
+ }
+ }
+
+ usleep(200 * 1000);
+ }
+ dvmUnlockThreadList();
+ }
+
+#if 0 /* bad things happen if they come out of JNI or "spuriously" wake up */
+ /*
+ * Abandon the threads and recover their resources.
+ */
+ target = gDvm.threadList;
+ while (target != NULL) {
+ Thread* nextTarget = target->next;
+ unlinkThread(target);
+ freeThread(target);
+ target = nextTarget;
+ }
+#endif
+
+ //dvmDumpAllThreads(true);
+}
+
+
+/*
+ * Finish preparing the parts of the Thread struct required to support
+ * JNI registration.
+ */
+bool dvmPrepMainForJni(JNIEnv* pEnv)
+{
+ Thread* self;
+
+ /* main thread is always first in list at this point */
+ self = gDvm.threadList;
+ assert(self->threadId == kMainThreadId);
+
+ /* create a "fake" JNI frame at the top of the main thread interp stack */
+ if (!createFakeEntryFrame(self))
+ return false;
+
+ /* fill these in, since they weren't ready at dvmCreateJNIEnv time */
+ dvmSetJniEnvThreadId(pEnv, self);
+ dvmSetThreadJNIEnv(self, (JNIEnv*) pEnv);
+
+ return true;
+}
+
+
+/*
+ * Finish preparing the main thread, allocating some objects to represent
+ * it. As part of doing so, we finish initializing Thread and ThreadGroup.
+ * This will execute some interpreted code (e.g. class initializers).
+ */
+bool dvmPrepMainThread(void)
+{
+ Thread* thread;
+ Object* groupObj;
+ Object* threadObj;
+ Object* vmThreadObj;
+ StringObject* threadNameStr;
+ Method* init;
+ JValue unused;
+
+ LOGV("+++ finishing prep on main VM thread\n");
+
+ /* main thread is always first in list at this point */
+ thread = gDvm.threadList;
+ assert(thread->threadId == kMainThreadId);
+
+ /*
+ * Make sure the classes are initialized. We have to do this before
+ * we create an instance of them.
+ */
+ if (!dvmInitClass(gDvm.classJavaLangClass)) {
+ LOGE("'Class' class failed to initialize\n");
+ return false;
+ }
+ if (!dvmInitClass(gDvm.classJavaLangThreadGroup) ||
+ !dvmInitClass(gDvm.classJavaLangThread) ||
+ !dvmInitClass(gDvm.classJavaLangVMThread))
+ {
+ LOGE("thread classes failed to initialize\n");
+ return false;
+ }
+
+ groupObj = dvmGetMainThreadGroup();
+ if (groupObj == NULL)
+ return false;
+
+ /*
+ * Allocate and construct a Thread with the internal-creation
+ * constructor.
+ */
+ threadObj = dvmAllocObject(gDvm.classJavaLangThread, ALLOC_DEFAULT);
+ if (threadObj == NULL) {
+ LOGE("unable to allocate main thread object\n");
+ return false;
+ }
+ dvmReleaseTrackedAlloc(threadObj, NULL);
+
+ threadNameStr = dvmCreateStringFromCstr("main");
+ if (threadNameStr == NULL)
+ return false;
+ dvmReleaseTrackedAlloc((Object*)threadNameStr, NULL);
+
+ init = dvmFindDirectMethodByDescriptor(gDvm.classJavaLangThread, "<init>",
+ "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V");
+ assert(init != NULL);
+ dvmCallMethod(thread, init, threadObj, &unused, groupObj, threadNameStr,
+ THREAD_NORM_PRIORITY, false);
+ if (dvmCheckException(thread)) {
+ LOGE("exception thrown while constructing main thread object\n");
+ return false;
+ }
+
+ /*
+ * Allocate and construct a VMThread.
+ */
+ vmThreadObj = dvmAllocObject(gDvm.classJavaLangVMThread, ALLOC_DEFAULT);
+ if (vmThreadObj == NULL) {
+ LOGE("unable to allocate main vmthread object\n");
+ return false;
+ }
+ dvmReleaseTrackedAlloc(vmThreadObj, NULL);
+
+ init = dvmFindDirectMethodByDescriptor(gDvm.classJavaLangVMThread, "<init>",
+ "(Ljava/lang/Thread;)V");
+ dvmCallMethod(thread, init, vmThreadObj, &unused, threadObj);
+ if (dvmCheckException(thread)) {
+ LOGE("exception thrown while constructing main vmthread object\n");
+ return false;
+ }
+
+ /* set the VMThread.vmData field to our Thread struct */
+ assert(gDvm.offJavaLangVMThread_vmData != 0);
+ dvmSetFieldInt(vmThreadObj, gDvm.offJavaLangVMThread_vmData, (u4)thread);
+
+ /*
+ * Stuff the VMThread back into the Thread. From this point on, other
+ * Threads will see that this Thread is running (at least, they would,
+ * if there were any).
+ */
+ dvmSetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread,
+ vmThreadObj);
+
+ thread->threadObj = threadObj;
+
+ /*
+ * Set the context class loader. This invokes a ClassLoader method,
+ * which could conceivably call Thread.currentThread(), so we want the
+ * Thread to be fully configured before we do this.
+ */
+ Object* systemLoader = dvmGetSystemClassLoader();
+ if (systemLoader == NULL) {
+ LOGW("WARNING: system class loader is NULL (setting main ctxt)\n");
+ /* keep going */
+ }
+ int ctxtClassLoaderOffset = dvmFindFieldOffset(gDvm.classJavaLangThread,
+ "contextClassLoader", "Ljava/lang/ClassLoader;");
+ if (ctxtClassLoaderOffset < 0) {
+ LOGE("Unable to find contextClassLoader field in Thread\n");
+ return false;
+ }
+ dvmSetFieldObject(threadObj, ctxtClassLoaderOffset, systemLoader);
+
+ /*
+ * Finish our thread prep.
+ */
+
+ /* include self in non-daemon threads (mainly for AttachCurrentThread) */
+ gDvm.nonDaemonThreadCount++;
+
+ return true;
+}
+
+
+/*
+ * Alloc and initialize a Thread struct.
+ *
+ * Does not create any objects, just stuff on the system (malloc) heap.
+ */
+static Thread* allocThread(int interpStackSize)
+{
+ Thread* thread;
+ u1* stackBottom;
+
+ thread = (Thread*) calloc(1, sizeof(Thread));
+ if (thread == NULL)
+ return NULL;
+
+#if defined(WITH_SELF_VERIFICATION)
+ if (dvmSelfVerificationShadowSpaceAlloc(thread) == NULL)
+ return NULL;
+#endif
+
+ assert(interpStackSize >= kMinStackSize && interpStackSize <=kMaxStackSize);
+
+ thread->status = THREAD_INITIALIZING;
+ thread->suspendCount = 0;
+
+#ifdef WITH_ALLOC_LIMITS
+ thread->allocLimit = -1;
+#endif
+
+ /*
+ * Allocate and initialize the interpreted code stack. We essentially
+ * "lose" the alloc pointer, which points at the bottom of the stack,
+ * but we can get it back later because we know how big the stack is.
+ *
+ * The stack must be aligned on a 4-byte boundary.
+ */
+#ifdef MALLOC_INTERP_STACK
+ stackBottom = (u1*) malloc(interpStackSize);
+ if (stackBottom == NULL) {
+#if defined(WITH_SELF_VERIFICATION)
+ dvmSelfVerificationShadowSpaceFree(thread);
+#endif
+ free(thread);
+ return NULL;
+ }
+ memset(stackBottom, 0xc5, interpStackSize); // stop valgrind complaints
+#else
+ stackBottom = mmap(NULL, interpStackSize, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON, -1, 0);
+ if (stackBottom == MAP_FAILED) {
+#if defined(WITH_SELF_VERIFICATION)
+ dvmSelfVerificationShadowSpaceFree(thread);
+#endif
+ free(thread);
+ return NULL;
+ }
+#endif
+
+ assert(((u4)stackBottom & 0x03) == 0); // looks like our malloc ensures this
+ thread->interpStackSize = interpStackSize;
+ thread->interpStackStart = stackBottom + interpStackSize;
+ thread->interpStackEnd = stackBottom + STACK_OVERFLOW_RESERVE;
+
+ /* give the thread code a chance to set things up */
+ dvmInitInterpStack(thread, interpStackSize);
+
+ return thread;
+}
+
+/*
+ * Get a meaningful thread ID. At present this only has meaning under Linux,
+ * where getpid() and gettid() sometimes agree and sometimes don't depending
+ * on your thread model (try "export LD_ASSUME_KERNEL=2.4.19").
+ */
+pid_t dvmGetSysThreadId(void)
+{
+#ifdef HAVE_GETTID
+ return gettid();
+#else
+ return getpid();
+#endif
+}
+
+/*
+ * Finish initialization of a Thread struct.
+ *
+ * This must be called while executing in the new thread, but before the
+ * thread is added to the thread list.
+ *
+ * NOTE: The threadListLock must be held by the caller (needed for
+ * assignThreadId()).
+ */
+static bool prepareThread(Thread* thread)
+{
+ assignThreadId(thread);
+ thread->handle = pthread_self();
+ thread->systemTid = dvmGetSysThreadId();
+
+ //LOGI("SYSTEM TID IS %d (pid is %d)\n", (int) thread->systemTid,
+ // (int) getpid());
+ /*
+ * If we were called by dvmAttachCurrentThread, the self value is
+ * already correctly established as "thread".
+ */
+ setThreadSelf(thread);
+
+ LOGV("threadid=%d: interp stack at %p\n",
+ thread->threadId, thread->interpStackStart - thread->interpStackSize);
+
+ /*
+ * Initialize invokeReq.
+ */
+ dvmInitMutex(&thread->invokeReq.lock);
+ pthread_cond_init(&thread->invokeReq.cv, NULL);
+
+ /*
+ * Initialize our reference tracking tables.
+ *
+ * Most threads won't use jniMonitorRefTable, so we clear out the
+ * structure but don't call the init function (which allocs storage).
+ */
+#ifdef USE_INDIRECT_REF
+ if (!dvmInitIndirectRefTable(&thread->jniLocalRefTable,
+ kJniLocalRefMin, kJniLocalRefMax, kIndirectKindLocal))
+ return false;
+#else
+ /*
+ * The JNI local ref table *must* be fixed-size because we keep pointers
+ * into the table in our stack frames.
+ */
+ if (!dvmInitReferenceTable(&thread->jniLocalRefTable,
+ kJniLocalRefMax, kJniLocalRefMax))
+ return false;
+#endif
+ if (!dvmInitReferenceTable(&thread->internalLocalRefTable,
+ kInternalRefDefault, kInternalRefMax))
+ return false;
+
+ memset(&thread->jniMonitorRefTable, 0, sizeof(thread->jniMonitorRefTable));
+
+ pthread_cond_init(&thread->waitCond, NULL);
+ dvmInitMutex(&thread->waitMutex);
+
+ return true;
+}
+
+/*
+ * Remove a thread from the internal list.
+ * Clear out the links to make it obvious that the thread is
+ * no longer on the list. Caller must hold gDvm.threadListLock.
+ */
+static void unlinkThread(Thread* thread)
+{
+ LOG_THREAD("threadid=%d: removing from list\n", thread->threadId);
+ if (thread == gDvm.threadList) {
+ assert(thread->prev == NULL);
+ gDvm.threadList = thread->next;
+ } else {
+ assert(thread->prev != NULL);
+ thread->prev->next = thread->next;
+ }
+ if (thread->next != NULL)
+ thread->next->prev = thread->prev;
+ thread->prev = thread->next = NULL;
+}
+
+/*
+ * Free a Thread struct, and all the stuff allocated within.
+ */
+static void freeThread(Thread* thread)
+{
+ if (thread == NULL)
+ return;
+
+ /* thread->threadId is zero at this point */
+ LOGVV("threadid=%d: freeing\n", thread->threadId);
+
+ if (thread->interpStackStart != NULL) {
+ u1* interpStackBottom;
+
+ interpStackBottom = thread->interpStackStart;
+ interpStackBottom -= thread->interpStackSize;
+#ifdef MALLOC_INTERP_STACK
+ free(interpStackBottom);
+#else
+ if (munmap(interpStackBottom, thread->interpStackSize) != 0)
+ LOGW("munmap(thread stack) failed\n");
+#endif
+ }
+
+#ifdef USE_INDIRECT_REF
+ dvmClearIndirectRefTable(&thread->jniLocalRefTable);
+#else
+ dvmClearReferenceTable(&thread->jniLocalRefTable);
+#endif
+ dvmClearReferenceTable(&thread->internalLocalRefTable);
+ if (&thread->jniMonitorRefTable.table != NULL)
+ dvmClearReferenceTable(&thread->jniMonitorRefTable);
+
+#if defined(WITH_SELF_VERIFICATION)
+ dvmSelfVerificationShadowSpaceFree(thread);
+#endif
+ free(thread);
+}
+
+/*
+ * Like pthread_self(), but on a Thread*.
+ */
+Thread* dvmThreadSelf(void)
+{
+ return (Thread*) pthread_getspecific(gDvm.pthreadKeySelf);
+}
+
+/*
+ * Explore our sense of self. Stuffs the thread pointer into TLS.
+ */
+static void setThreadSelf(Thread* thread)
+{
+ int cc;
+
+ cc = pthread_setspecific(gDvm.pthreadKeySelf, thread);
+ if (cc != 0) {
+ /*
+ * Sometimes this fails under Bionic with EINVAL during shutdown.
+ * This can happen if the timing is just right, e.g. a thread
+ * fails to attach during shutdown, but the "fail" path calls
+ * here to ensure we clean up after ourselves.
+ */
+ if (thread != NULL) {
+ LOGE("pthread_setspecific(%p) failed, err=%d\n", thread, cc);
+ dvmAbort(); /* the world is fundamentally hosed */
+ }
+ }
+}
+
+/*
+ * This is associated with the pthreadKeySelf key. It's called by the
+ * pthread library when a thread is exiting and the "self" pointer in TLS
+ * is non-NULL, meaning the VM hasn't had a chance to clean up. In normal
+ * operation this will not be called.
+ *
+ * This is mainly of use to ensure that we don't leak resources if, for
+ * example, a thread attaches itself to us with AttachCurrentThread and
+ * then exits without notifying the VM.
+ *
+ * We could do the detach here instead of aborting, but this will lead to
+ * portability problems. Other implementations do not do this check and
+ * will simply be unaware that the thread has exited, leading to resource
+ * leaks (and, if this is a non-daemon thread, an infinite hang when the
+ * VM tries to shut down).
+ *
+ * Because some implementations may want to use the pthread destructor
+ * to initiate the detach, and the ordering of destructors is not defined,
+ * we want to iterate a couple of times to give those a chance to run.
+ */
+static void threadExitCheck(void* arg)
+{
+ const int kMaxCount = 2;
+
+ Thread* self = (Thread*) arg;
+ assert(self != NULL);
+
+ LOGV("threadid=%d: threadExitCheck(%p) count=%d\n",
+ self->threadId, arg, self->threadExitCheckCount);
+
+ if (self->status == THREAD_ZOMBIE) {
+ LOGW("threadid=%d: Weird -- shouldn't be in threadExitCheck\n",
+ self->threadId);
+ return;
+ }
+
+ if (self->threadExitCheckCount < kMaxCount) {
+ /*
+ * Spin a couple of times to let other destructors fire.
+ */
+ LOGD("threadid=%d: thread exiting, not yet detached (count=%d)\n",
+ self->threadId, self->threadExitCheckCount);
+ self->threadExitCheckCount++;
+ int cc = pthread_setspecific(gDvm.pthreadKeySelf, self);
+ if (cc != 0) {
+ LOGE("threadid=%d: unable to re-add thread to TLS\n",
+ self->threadId);
+ dvmAbort();
+ }
+ } else {
+ LOGE("threadid=%d: native thread exited without detaching\n",
+ self->threadId);
+ dvmAbort();
+ }
+}
+
+
+/*
+ * Assign the threadId. This needs to be a small integer so that our
+ * "thin" locks fit in a small number of bits.
+ *
+ * We reserve zero for use as an invalid ID.
+ *
+ * This must be called with threadListLock held.
+ */
+static void assignThreadId(Thread* thread)
+{
+ /*
+ * Find a small unique integer. threadIdMap is a vector of
+ * kMaxThreadId bits; dvmAllocBit() returns the index of a
+ * bit, meaning that it will always be < kMaxThreadId.
+ */
+ int num = dvmAllocBit(gDvm.threadIdMap);
+ if (num < 0) {
+ LOGE("Ran out of thread IDs\n");
+ dvmAbort(); // TODO: make this a non-fatal error result
+ }
+
+ thread->threadId = num + 1;
+
+ assert(thread->threadId != 0);
+ assert(thread->threadId != DVM_LOCK_INITIAL_THIN_VALUE);
+}
+
+/*
+ * Give back the thread ID.
+ */
+static void releaseThreadId(Thread* thread)
+{
+ assert(thread->threadId > 0);
+ dvmClearBit(gDvm.threadIdMap, thread->threadId - 1);
+ thread->threadId = 0;
+}
+
+
+/*
+ * Add a stack frame that makes it look like the native code in the main
+ * thread was originally invoked from interpreted code. This gives us a
+ * place to hang JNI local references. The VM spec says (v2 5.2) that the
+ * VM begins by executing "main" in a class, so in a way this brings us
+ * closer to the spec.
+ */
+static bool createFakeEntryFrame(Thread* thread)
+{
+ assert(thread->threadId == kMainThreadId); // main thread only
+
+ /* find the method on first use */
+ if (gDvm.methFakeNativeEntry == NULL) {
+ ClassObject* nativeStart;
+ Method* mainMeth;
+
+ nativeStart = dvmFindSystemClassNoInit(
+ "Ldalvik/system/NativeStart;");
+ if (nativeStart == NULL) {
+ LOGE("Unable to find dalvik.system.NativeStart class\n");
+ return false;
+ }
+
+ /*
+ * Because we are creating a frame that represents application code, we
+ * want to stuff the application class loader into the method's class
+ * loader field, even though we're using the system class loader to
+ * load it. This makes life easier over in JNI FindClass (though it
+ * could bite us in other ways).
+ *
+ * Unfortunately this is occurring too early in the initialization,
+ * of necessity coming before JNI is initialized, and we're not quite
+ * ready to set up the application class loader.
+ *
+ * So we save a pointer to the method in gDvm.methFakeNativeEntry
+ * and check it in FindClass. The method is private so nobody else
+ * can call it.
+ */
+ //nativeStart->classLoader = dvmGetSystemClassLoader();
+
+ mainMeth = dvmFindDirectMethodByDescriptor(nativeStart,
+ "main", "([Ljava/lang/String;)V");
+ if (mainMeth == NULL) {
+ LOGE("Unable to find 'main' in dalvik.system.NativeStart\n");
+ return false;
+ }
+
+ gDvm.methFakeNativeEntry = mainMeth;
+ }
+
+ if (!dvmPushJNIFrame(thread, gDvm.methFakeNativeEntry))
+ return false;
+
+ /*
+ * Null out the "String[] args" argument.
+ */
+ assert(gDvm.methFakeNativeEntry->registersSize == 1);
+ u4* framePtr = (u4*) thread->curFrame;
+ framePtr[0] = 0;
+
+ return true;
+}
+
+
+/*
+ * Add a stack frame that makes it look like the native thread has been
+ * executing interpreted code. This gives us a place to hang JNI local
+ * references.
+ */
+static bool createFakeRunFrame(Thread* thread)
+{
+ ClassObject* nativeStart;
+ Method* runMeth;
+
+ /*
+ * TODO: cache this result so we don't have to dig for it every time
+ * somebody attaches a thread to the VM. Also consider changing this
+ * to a static method so we don't have a null "this" pointer in the
+ * "ins" on the stack. (Does it really need to look like a Runnable?)
+ */
+ nativeStart = dvmFindSystemClassNoInit("Ldalvik/system/NativeStart;");
+ if (nativeStart == NULL) {
+ LOGE("Unable to find dalvik.system.NativeStart class\n");
+ return false;
+ }
+
+ runMeth = dvmFindVirtualMethodByDescriptor(nativeStart, "run", "()V");
+ if (runMeth == NULL) {
+ LOGE("Unable to find 'run' in dalvik.system.NativeStart\n");
+ return false;
+ }
+
+ if (!dvmPushJNIFrame(thread, runMeth))
+ return false;
+
+ /*
+ * Provide a NULL 'this' argument. The method we've put at the top of
+ * the stack looks like a virtual call to run() in a Runnable class.
+ * (If we declared the method static, it wouldn't take any arguments
+ * and we wouldn't have to do this.)
+ */
+ assert(runMeth->registersSize == 1);
+ u4* framePtr = (u4*) thread->curFrame;
+ framePtr[0] = 0;
+
+ return true;
+}
+
+/*
+ * Helper function to set the name of the current thread
+ */
+static void setThreadName(const char *threadName)
+{
+ int hasAt = 0;
+ int hasDot = 0;
+ const char *s = threadName;
+ while (*s) {
+ if (*s == '.') hasDot = 1;
+ else if (*s == '@') hasAt = 1;
+ s++;
+ }
+ int len = s - threadName;
+ if (len < 15 || hasAt || !hasDot) {
+ s = threadName;
+ } else {
+ s = threadName + len - 15;
+ }
+#if defined(HAVE_ANDROID_PTHREAD_SETNAME_NP)
+ /* pthread_setname_np fails rather than truncating long strings */
+ char buf[16]; // MAX_TASK_COMM_LEN=16 is hard-coded into bionic
+ strncpy(buf, s, sizeof(buf)-1);
+ buf[sizeof(buf)-1] = '\0';
+ int err = pthread_setname_np(pthread_self(), buf);
+ if (err != 0) {
+ LOGW("Unable to set the name of current thread to '%s': %s\n",
+ buf, strerror(err));
+ }
+#elif defined(HAVE_PRCTL)
+ prctl(PR_SET_NAME, (unsigned long) s, 0, 0, 0);
+#else
+ LOGD("No way to set current thread's name (%s)\n", s);
+#endif
+}
+
+/*
+ * Create a thread as a result of java.lang.Thread.start().
+ *
+ * We do have to worry about some concurrency problems, e.g. programs
+ * that try to call Thread.start() on the same object from multiple threads.
+ * (This will fail for all but one, but we have to make sure that it succeeds
+ * for exactly one.)
+ *
+ * Some of the complexity here arises from our desire to mimic the
+ * Thread vs. VMThread class decomposition we inherited. We've been given
+ * a Thread, and now we need to create a VMThread and then populate both
+ * objects. We also need to create one of our internal Thread objects.
+ *
+ * Pass in a stack size of 0 to get the default.
+ *
+ * The "threadObj" reference must be pinned by the caller to prevent the GC
+ * from moving it around (e.g. added to the tracked allocation list).
+ */
+bool dvmCreateInterpThread(Object* threadObj, int reqStackSize)
+{
+ pthread_attr_t threadAttr;
+ pthread_t threadHandle;
+ Thread* self;
+ Thread* newThread = NULL;
+ Object* vmThreadObj = NULL;
+ int stackSize;
+
+ assert(threadObj != NULL);
+
+ if(gDvm.zygote) {
+ // Allow the sampling profiler thread. We shut it down before forking.
+ StringObject* nameStr = (StringObject*) dvmGetFieldObject(threadObj,
+ gDvm.offJavaLangThread_name);
+ char* threadName = dvmCreateCstrFromString(nameStr);
+ bool profilerThread = strcmp(threadName, "SamplingProfiler") == 0;
+ free(threadName);
+ if (!profilerThread) {
+ dvmThrowException("Ljava/lang/IllegalStateException;",
+ "No new threads in -Xzygote mode");
+
+ goto fail;
+ }
+ }
+
+ self = dvmThreadSelf();
+ if (reqStackSize == 0)
+ stackSize = gDvm.stackSize;
+ else if (reqStackSize < kMinStackSize)
+ stackSize = kMinStackSize;
+ else if (reqStackSize > kMaxStackSize)
+ stackSize = kMaxStackSize;
+ else
+ stackSize = reqStackSize;
+
+ pthread_attr_init(&threadAttr);
+ pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED);
+
+ /*
+ * To minimize the time spent in the critical section, we allocate the
+ * vmThread object here.
+ */
+ vmThreadObj = dvmAllocObject(gDvm.classJavaLangVMThread, ALLOC_DEFAULT);
+ if (vmThreadObj == NULL)
+ goto fail;
+
+ newThread = allocThread(stackSize);
+ if (newThread == NULL)
+ goto fail;
+ newThread->threadObj = threadObj;
+
+ assert(newThread->status == THREAD_INITIALIZING);
+
+ /*
+ * We need to lock out other threads while we test and set the
+ * "vmThread" field in java.lang.Thread, because we use that to determine
+ * if this thread has been started before. We use the thread list lock
+ * because it's handy and we're going to need to grab it again soon
+ * anyway.
+ */
+ dvmLockThreadList(self);
+
+ if (dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread) != NULL) {
+ dvmUnlockThreadList();
+ dvmThrowException("Ljava/lang/IllegalThreadStateException;",
+ "thread has already been started");
+ goto fail;
+ }
+
+ /*
+ * There are actually three data structures: Thread (object), VMThread
+ * (object), and Thread (C struct). All of them point to at least one
+ * other.
+ *
+ * As soon as "VMThread.vmData" is assigned, other threads can start
+ * making calls into us (e.g. setPriority).
+ */
+ dvmSetFieldInt(vmThreadObj, gDvm.offJavaLangVMThread_vmData, (u4)newThread);
+ dvmSetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread, vmThreadObj);
+
+ /*
+ * Thread creation might take a while, so release the lock.
+ */
+ dvmUnlockThreadList();
+
+ ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
+ int cc = pthread_create(&threadHandle, &threadAttr, interpThreadStart,
+ newThread);
+ oldStatus = dvmChangeStatus(self, oldStatus);
+
+ if (cc != 0) {
+ /*
+ * Failure generally indicates that we have exceeded system
+ * resource limits. VirtualMachineError is probably too severe,
+ * so use OutOfMemoryError.
+ */
+ LOGE("Thread creation failed (err=%s)\n", strerror(errno));
+
+ dvmSetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread, NULL);
+
+ dvmThrowException("Ljava/lang/OutOfMemoryError;",
+ "thread creation failed");
+ goto fail;
+ }
+
+ /*
+ * We need to wait for the thread to start. Otherwise, depending on
+ * the whims of the OS scheduler, we could return and the code in our
+ * thread could try to do operations on the new thread before it had
+ * finished starting.
+ *
+ * The new thread will lock the thread list, change its state to
+ * THREAD_STARTING, broadcast to gDvm.threadStartCond, and then sleep
+ * on gDvm.threadStartCond (which uses the thread list lock). This
+ * thread (the parent) will either see that the thread is already ready
+ * after we grab the thread list lock, or will be awakened from the
+ * condition variable on the broadcast.
+ *
+ * We don't want to stall the rest of the VM while the new thread
+ * starts, which can happen if the GC wakes up at the wrong moment.
+ * So, we change our own status to VMWAIT, and self-suspend if
+ * necessary after we finish adding the new thread.
+ *
+ *
+ * We have to deal with an odd race with the GC/debugger suspension
+ * mechanism when creating a new thread. The information about whether
+ * or not a thread should be suspended is contained entirely within
+ * the Thread struct; this is usually cleaner to deal with than having
+ * one or more globally-visible suspension flags. The trouble is that
+ * we could create the thread while the VM is trying to suspend all
+ * threads. The suspend-count won't be nonzero for the new thread,
+ * so dvmChangeStatus(THREAD_RUNNING) won't cause a suspension.
+ *
+ * The easiest way to deal with this is to prevent the new thread from
+ * running until the parent says it's okay. This results in the
+ * following (correct) sequence of events for a "badly timed" GC
+ * (where '-' is us, 'o' is the child, and '+' is some other thread):
+ *
+ * - call pthread_create()
+ * - lock thread list
+ * - put self into THREAD_VMWAIT so GC doesn't wait for us
+ * - sleep on condition var (mutex = thread list lock) until child starts
+ * + GC triggered by another thread
+ * + thread list locked; suspend counts updated; thread list unlocked
+ * + loop waiting for all runnable threads to suspend
+ * + success, start GC
+ * o child thread wakes, signals condition var to wake parent
+ * o child waits for parent ack on condition variable
+ * - we wake up, locking thread list
+ * - add child to thread list
+ * - unlock thread list
+ * - change our state back to THREAD_RUNNING; GC causes us to suspend
+ * + GC finishes; all threads in thread list are resumed
+ * - lock thread list
+ * - set child to THREAD_VMWAIT, and signal it to start
+ * - unlock thread list
+ * o child resumes
+ * o child changes state to THREAD_RUNNING
+ *
+ * The above shows the GC starting up during thread creation, but if
+ * it starts anywhere after VMThread.create() is called it will
+ * produce the same series of events.
+ *
+ * Once the child is in the thread list, it will be suspended and
+ * resumed like any other thread. In the above scenario the resume-all
+ * code will try to resume the new thread, which was never actually
+ * suspended, and try to decrement the child's thread suspend count to -1.
+ * We can catch this in the resume-all code.
+ *
+ * Bouncing back and forth between threads like this adds a small amount
+ * of scheduler overhead to thread startup.
+ *
+ * One alternative to having the child wait for the parent would be
+ * to have the child inherit the parents' suspension count. This
+ * would work for a GC, since we can safely assume that the parent
+ * thread didn't cause it, but we must only do so if the parent suspension
+ * was caused by a suspend-all. If the parent was being asked to
+ * suspend singly by the debugger, the child should not inherit the value.
+ *
+ * We could also have a global "new thread suspend count" that gets
+ * picked up by new threads before changing state to THREAD_RUNNING.
+ * This would be protected by the thread list lock and set by a
+ * suspend-all.
+ */
+ dvmLockThreadList(self);
+ assert(self->status == THREAD_RUNNING);
+ self->status = THREAD_VMWAIT;
+ while (newThread->status != THREAD_STARTING)
+ pthread_cond_wait(&gDvm.threadStartCond, &gDvm.threadListLock);
+
+ LOG_THREAD("threadid=%d: adding to list\n", newThread->threadId);
+ newThread->next = gDvm.threadList->next;
+ if (newThread->next != NULL)
+ newThread->next->prev = newThread;
+ newThread->prev = gDvm.threadList;
+ gDvm.threadList->next = newThread;
+
+ if (!dvmGetFieldBoolean(threadObj, gDvm.offJavaLangThread_daemon))
+ gDvm.nonDaemonThreadCount++; // guarded by thread list lock
+
+ dvmUnlockThreadList();
+
+ /* change status back to RUNNING, self-suspending if necessary */
+ dvmChangeStatus(self, THREAD_RUNNING);
+
+ /*
+ * Tell the new thread to start.
+ *
+ * We must hold the thread list lock before messing with another thread.
+ * In the general case we would also need to verify that newThread was
+ * still in the thread list, but in our case the thread has not started
+ * executing user code and therefore has not had a chance to exit.
+ *
+ * We move it to VMWAIT, and it then shifts itself to RUNNING, which
+ * comes with a suspend-pending check.
+ */
+ dvmLockThreadList(self);
+
+ assert(newThread->status == THREAD_STARTING);
+ newThread->status = THREAD_VMWAIT;
+ pthread_cond_broadcast(&gDvm.threadStartCond);
+
+ dvmUnlockThreadList();
+
+ dvmReleaseTrackedAlloc(vmThreadObj, NULL);
+ return true;
+
+fail:
+ freeThread(newThread);
+ dvmReleaseTrackedAlloc(vmThreadObj, NULL);
+ return false;
+}
+
+/*
+ * pthread entry function for threads started from interpreted code.
+ */
+static void* interpThreadStart(void* arg)
+{
+ Thread* self = (Thread*) arg;
+
+ char *threadName = dvmGetThreadName(self);
+ setThreadName(threadName);
+ free(threadName);
+
+ /*
+ * Finish initializing the Thread struct.
+ */
+ dvmLockThreadList(self);
+ prepareThread(self);
+
+ LOG_THREAD("threadid=%d: created from interp\n", self->threadId);
+
+ /*
+ * Change our status and wake our parent, who will add us to the
+ * thread list and advance our state to VMWAIT.
+ */
+ self->status = THREAD_STARTING;
+ pthread_cond_broadcast(&gDvm.threadStartCond);
+
+ /*
+ * Wait until the parent says we can go. Assuming there wasn't a
+ * suspend pending, this will happen immediately. When it completes,
+ * we're full-fledged citizens of the VM.
+ *
+ * We have to use THREAD_VMWAIT here rather than THREAD_RUNNING
+ * because the pthread_cond_wait below needs to reacquire a lock that
+ * suspend-all is also interested in. If we get unlucky, the parent could
+ * change us to THREAD_RUNNING, then a GC could start before we get
+ * signaled, and suspend-all will grab the thread list lock and then
+ * wait for us to suspend. We'll be in the tail end of pthread_cond_wait
+ * trying to get the lock.
+ */
+ while (self->status != THREAD_VMWAIT)
+ pthread_cond_wait(&gDvm.threadStartCond, &gDvm.threadListLock);
+
+ dvmUnlockThreadList();
+
+ /*
+ * Add a JNI context.
+ */
+ self->jniEnv = dvmCreateJNIEnv(self);
+
+ /*
+ * Change our state so the GC will wait for us from now on. If a GC is
+ * in progress this call will suspend us.
+ */
+ dvmChangeStatus(self, THREAD_RUNNING);
+
+ /*
+ * Notify the debugger & DDM. The debugger notification may cause
+ * us to suspend ourselves (and others).
+ */
+ if (gDvm.debuggerConnected)
+ dvmDbgPostThreadStart(self);
+
+ /*
+ * Set the system thread priority according to the Thread object's
+ * priority level. We don't usually need to do this, because both the
+ * Thread object and system thread priorities inherit from parents. The
+ * tricky case is when somebody creates a Thread object, calls
+ * setPriority(), and then starts the thread. We could manage this with
+ * a "needs priority update" flag to avoid the redundant call.
+ */
+ int priority = dvmGetFieldInt(self->threadObj,
+ gDvm.offJavaLangThread_priority);
+ dvmChangeThreadPriority(self, priority);
+
+ /*
+ * Execute the "run" method.
+ *
+ * At this point our stack is empty, so somebody who comes looking for
+ * stack traces right now won't have much to look at. This is normal.
+ */
+ Method* run = self->threadObj->clazz->vtable[gDvm.voffJavaLangThread_run];
+ JValue unused;
+
+ LOGV("threadid=%d: calling run()\n", self->threadId);
+ assert(strcmp(run->name, "run") == 0);
+ dvmCallMethod(self, run, self->threadObj, &unused);
+ LOGV("threadid=%d: exiting\n", self->threadId);
+
+ /*
+ * Remove the thread from various lists, report its death, and free
+ * its resources.
+ */
+ dvmDetachCurrentThread();
+
+ return NULL;
+}
+
+/*
+ * The current thread is exiting with an uncaught exception. The
+ * Java programming language allows the application to provide a
+ * thread-exit-uncaught-exception handler for the VM, for a specific
+ * Thread, and for all threads in a ThreadGroup.
+ *
+ * Version 1.5 added the per-thread handler. We need to call
+ * "uncaughtException" in the handler object, which is either the
+ * ThreadGroup object or the Thread-specific handler.
+ */
+static void threadExitUncaughtException(Thread* self, Object* group)
+{
+ Object* exception;
+ Object* handlerObj;
+ Method* uncaughtHandler = NULL;
+ InstField* threadHandler;
+
+ LOGW("threadid=%d: thread exiting with uncaught exception (group=%p)\n",
+ self->threadId, group);
+ assert(group != NULL);
+
+ /*
+ * Get a pointer to the exception, then clear out the one in the
+ * thread. We don't want to have it set when executing interpreted code.
+ */
+ exception = dvmGetException(self);
+ dvmAddTrackedAlloc(exception, self);
+ dvmClearException(self);
+
+ /*
+ * Get the Thread's "uncaughtHandler" object. Use it if non-NULL;
+ * else use "group" (which is an instance of UncaughtExceptionHandler).
+ */
+ threadHandler = dvmFindInstanceField(gDvm.classJavaLangThread,
+ "uncaughtHandler", "Ljava/lang/Thread$UncaughtExceptionHandler;");
+ if (threadHandler == NULL) {
+ LOGW("WARNING: no 'uncaughtHandler' field in java/lang/Thread\n");
+ goto bail;
+ }
+ handlerObj = dvmGetFieldObject(self->threadObj, threadHandler->byteOffset);
+ if (handlerObj == NULL)
+ handlerObj = group;
+
+ /*
+ * Find the "uncaughtHandler" field in this object.
+ */
+ uncaughtHandler = dvmFindVirtualMethodHierByDescriptor(handlerObj->clazz,
+ "uncaughtException", "(Ljava/lang/Thread;Ljava/lang/Throwable;)V");
+
+ if (uncaughtHandler != NULL) {
+ //LOGI("+++ calling %s.uncaughtException\n",
+ // handlerObj->clazz->descriptor);
+ JValue unused;
+ dvmCallMethod(self, uncaughtHandler, handlerObj, &unused,
+ self->threadObj, exception);
+ } else {
+ /* restore it and dump a stack trace */
+ LOGW("WARNING: no 'uncaughtException' method in class %s\n",
+ handlerObj->clazz->descriptor);
+ dvmSetException(self, exception);
+ dvmLogExceptionStackTrace();
+ }
+
+bail:
+#if defined(WITH_JIT)
+ /* Remove this thread's suspendCount from global suspendCount sum */
+ lockThreadSuspendCount();
+ dvmAddToThreadSuspendCount(&self->suspendCount, -self->suspendCount);
+ unlockThreadSuspendCount();
+#endif
+ dvmReleaseTrackedAlloc(exception, self);
+}
+
+
+/*
+ * Create an internal VM thread, for things like JDWP and finalizers.
+ *
+ * The easiest way to do this is create a new thread and then use the
+ * JNI AttachCurrentThread implementation.
+ *
+ * This does not return until after the new thread has begun executing.
+ */
+bool dvmCreateInternalThread(pthread_t* pHandle, const char* name,
+ InternalThreadStart func, void* funcArg)
+{
+ InternalStartArgs* pArgs;
+ Object* systemGroup;
+ pthread_attr_t threadAttr;
+ volatile Thread* newThread = NULL;
+ volatile int createStatus = 0;
+
+ systemGroup = dvmGetSystemThreadGroup();
+ if (systemGroup == NULL)
+ return false;
+
+ pArgs = (InternalStartArgs*) malloc(sizeof(*pArgs));
+ pArgs->func = func;
+ pArgs->funcArg = funcArg;
+ pArgs->name = strdup(name); // storage will be owned by new thread
+ pArgs->group = systemGroup;
+ pArgs->isDaemon = true;
+ pArgs->pThread = &newThread;
+ pArgs->pCreateStatus = &createStatus;
+
+ pthread_attr_init(&threadAttr);
+ //pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED);
+
+ if (pthread_create(pHandle, &threadAttr, internalThreadStart,
+ pArgs) != 0)
+ {
+ LOGE("internal thread creation failed\n");
+ free(pArgs->name);
+ free(pArgs);
+ return false;
+ }
+
+ /*
+ * Wait for the child to start. This gives us an opportunity to make
+ * sure that the thread started correctly, and allows our caller to
+ * assume that the thread has started running.
+ *
+ * Because we aren't holding a lock across the thread creation, it's
+ * possible that the child will already have completed its
+ * initialization. Because the child only adjusts "createStatus" while
+ * holding the thread list lock, the initial condition on the "while"
+ * loop will correctly avoid the wait if this occurs.
+ *
+ * It's also possible that we'll have to wait for the thread to finish
+ * being created, and as part of allocating a Thread object it might
+ * need to initiate a GC. We switch to VMWAIT while we pause.
+ */
+ Thread* self = dvmThreadSelf();
+ ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
+ dvmLockThreadList(self);
+ while (createStatus == 0)
+ pthread_cond_wait(&gDvm.threadStartCond, &gDvm.threadListLock);
+
+ if (newThread == NULL) {
+ LOGW("internal thread create failed (createStatus=%d)\n", createStatus);
+ assert(createStatus < 0);
+ /* don't free pArgs -- if pthread_create succeeded, child owns it */
+ dvmUnlockThreadList();
+ dvmChangeStatus(self, oldStatus);
+ return false;
+ }
+
+ /* thread could be in any state now (except early init states) */
+ //assert(newThread->status == THREAD_RUNNING);
+
+ dvmUnlockThreadList();
+ dvmChangeStatus(self, oldStatus);
+
+ return true;
+}
+
+/*
+ * pthread entry function for internally-created threads.
+ *
+ * We are expected to free "arg" and its contents. If we're a daemon
+ * thread, and we get cancelled abruptly when the VM shuts down, the
+ * storage won't be freed. If this becomes a concern we can make a copy
+ * on the stack.
+ */
+static void* internalThreadStart(void* arg)
+{
+ InternalStartArgs* pArgs = (InternalStartArgs*) arg;
+ JavaVMAttachArgs jniArgs;
+
+ jniArgs.version = JNI_VERSION_1_2;
+ jniArgs.name = pArgs->name;
+ jniArgs.group = pArgs->group;
+
+ setThreadName(pArgs->name);
+
+ /* use local jniArgs as stack top */
+ if (dvmAttachCurrentThread(&jniArgs, pArgs->isDaemon)) {
+ /*
+ * Tell the parent of our success.
+ *
+ * threadListLock is the mutex for threadStartCond.
+ */
+ dvmLockThreadList(dvmThreadSelf());
+ *pArgs->pCreateStatus = 1;
+ *pArgs->pThread = dvmThreadSelf();
+ pthread_cond_broadcast(&gDvm.threadStartCond);
+ dvmUnlockThreadList();
+
+ LOG_THREAD("threadid=%d: internal '%s'\n",
+ dvmThreadSelf()->threadId, pArgs->name);
+
+ /* execute */
+ (*pArgs->func)(pArgs->funcArg);
+
+ /* detach ourselves */
+ dvmDetachCurrentThread();
+ } else {
+ /*
+ * Tell the parent of our failure. We don't have a Thread struct,
+ * so we can't be suspended, so we don't need to enter a critical
+ * section.
+ */
+ dvmLockThreadList(dvmThreadSelf());
+ *pArgs->pCreateStatus = -1;
+ assert(*pArgs->pThread == NULL);
+ pthread_cond_broadcast(&gDvm.threadStartCond);
+ dvmUnlockThreadList();
+
+ assert(*pArgs->pThread == NULL);
+ }
+
+ free(pArgs->name);
+ free(pArgs);
+ return NULL;
+}
+
+/*
+ * Attach the current thread to the VM.
+ *
+ * Used for internally-created threads and JNI's AttachCurrentThread.
+ */
+bool dvmAttachCurrentThread(const JavaVMAttachArgs* pArgs, bool isDaemon)
+{
+ Thread* self = NULL;
+ Object* threadObj = NULL;
+ Object* vmThreadObj = NULL;
+ StringObject* threadNameStr = NULL;
+ Method* init;
+ bool ok, ret;
+
+ /* allocate thread struct, and establish a basic sense of self */
+ self = allocThread(gDvm.stackSize);
+ if (self == NULL)
+ goto fail;
+ setThreadSelf(self);
+
+ /*
+ * Finish our thread prep. We need to do this before adding ourselves
+ * to the thread list or invoking any interpreted code. prepareThread()
+ * requires that we hold the thread list lock.
+ */
+ dvmLockThreadList(self);
+ ok = prepareThread(self);
+ dvmUnlockThreadList();
+ if (!ok)
+ goto fail;
+
+ self->jniEnv = dvmCreateJNIEnv(self);
+ if (self->jniEnv == NULL)
+ goto fail;
+
+ /*
+ * Create a "fake" JNI frame at the top of the main thread interp stack.
+ * It isn't really necessary for the internal threads, but it gives
+ * the debugger something to show. It is essential for the JNI-attached
+ * threads.
+ */
+ if (!createFakeRunFrame(self))
+ goto fail;
+
+ /*
+ * The native side of the thread is ready; add it to the list. Once
+ * it's on the list the thread is visible to the JDWP code and the GC.
+ */
+ LOG_THREAD("threadid=%d: adding to list (attached)\n", self->threadId);
+
+ dvmLockThreadList(self);
+
+ self->next = gDvm.threadList->next;
+ if (self->next != NULL)
+ self->next->prev = self;
+ self->prev = gDvm.threadList;
+ gDvm.threadList->next = self;
+ if (!isDaemon)
+ gDvm.nonDaemonThreadCount++;
+
+ dvmUnlockThreadList();
+
+ /*
+ * Switch state from initializing to running.
+ *
+ * It's possible that a GC began right before we added ourselves
+ * to the thread list, and is still going. That means our thread
+ * suspend count won't reflect the fact that we should be suspended.
+ * To deal with this, we transition to VMWAIT, pulse the heap lock,
+ * and then advance to RUNNING. That will ensure that we stall until
+ * the GC completes.
+ *
+ * Once we're in RUNNING, we're like any other thread in the VM (except
+ * for the lack of an initialized threadObj). We're then free to
+ * allocate and initialize objects.
+ */
+ assert(self->status == THREAD_INITIALIZING);
+ dvmChangeStatus(self, THREAD_VMWAIT);
+ dvmLockMutex(&gDvm.gcHeapLock);
+ dvmUnlockMutex(&gDvm.gcHeapLock);
+ dvmChangeStatus(self, THREAD_RUNNING);
+
+ /*
+ * Create Thread and VMThread objects.
+ */
+ threadObj = dvmAllocObject(gDvm.classJavaLangThread, ALLOC_DEFAULT);
+ vmThreadObj = dvmAllocObject(gDvm.classJavaLangVMThread, ALLOC_DEFAULT);
+ if (threadObj == NULL || vmThreadObj == NULL)
+ goto fail_unlink;
+
+ /*
+ * This makes threadObj visible to the GC. We still have it in the
+ * tracked allocation table, so it can't move around on us.
+ */
+ self->threadObj = threadObj;
+ dvmSetFieldInt(vmThreadObj, gDvm.offJavaLangVMThread_vmData, (u4)self);
+
+ /*
+ * Create a string for the thread name.
+ */
+ if (pArgs->name != NULL) {
+ threadNameStr = dvmCreateStringFromCstr(pArgs->name);
+ if (threadNameStr == NULL) {
+ assert(dvmCheckException(dvmThreadSelf()));
+ goto fail_unlink;
+ }
+ }
+
+ init = dvmFindDirectMethodByDescriptor(gDvm.classJavaLangThread, "<init>",
+ "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V");
+ if (init == NULL) {
+ assert(dvmCheckException(self));
+ goto fail_unlink;
+ }
+
+ /*
+ * Now we're ready to run some interpreted code.
+ *
+ * We need to construct the Thread object and set the VMThread field.
+ * Setting VMThread tells interpreted code that we're alive.
+ *
+ * Call the (group, name, priority, daemon) constructor on the Thread.
+ * This sets the thread's name and adds it to the specified group, and
+ * provides values for priority and daemon (which are normally inherited
+ * from the current thread).
+ */
+ JValue unused;
+ dvmCallMethod(self, init, threadObj, &unused, (Object*)pArgs->group,
+ threadNameStr, getThreadPriorityFromSystem(), isDaemon);
+ if (dvmCheckException(self)) {
+ LOGE("exception thrown while constructing attached thread object\n");
+ goto fail_unlink;
+ }
+
+ /*
+ * Set the VMThread field, which tells interpreted code that we're alive.
+ *
+ * The risk of a thread start collision here is very low; somebody
+ * would have to be deliberately polling the ThreadGroup list and
+ * trying to start threads against anything it sees, which would
+ * generally cause problems for all thread creation. However, for
+ * correctness we test "vmThread" before setting it.
+ *
+ * TODO: this still has a race, it's just smaller. Not sure this is
+ * worth putting effort into fixing. Need to hold a lock while
+ * fiddling with the field, or maybe initialize the Thread object in a
+ * way that ensures another thread can't call start() on it.
+ */
+ if (dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread) != NULL) {
+ LOGW("WOW: thread start hijack\n");
+ dvmThrowException("Ljava/lang/IllegalThreadStateException;",
+ "thread has already been started");
+ /* We don't want to free anything associated with the thread
+ * because someone is obviously interested in it. Just let
+ * it go and hope it will clean itself up when its finished.
+ * This case should never happen anyway.
+ *
+ * Since we're letting it live, we need to finish setting it up.
+ * We just have to let the caller know that the intended operation
+ * has failed.
+ *
+ * [ This seems strange -- stepping on the vmThread object that's
+ * already present seems like a bad idea. TODO: figure this out. ]
+ */
+ ret = false;
+ } else {
+ ret = true;
+ }
+ dvmSetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread, vmThreadObj);
+
+ /* we can now safely un-pin these */
+ dvmReleaseTrackedAlloc(threadObj, self);
+ dvmReleaseTrackedAlloc(vmThreadObj, self);
+ dvmReleaseTrackedAlloc((Object*)threadNameStr, self);
+
+ LOG_THREAD("threadid=%d: attached from native, name=%s\n",
+ self->threadId, pArgs->name);
+
+ /* tell the debugger & DDM */
+ if (gDvm.debuggerConnected)
+ dvmDbgPostThreadStart(self);
+
+ return ret;
+
+fail_unlink:
+ dvmLockThreadList(self);
+ unlinkThread(self);
+ if (!isDaemon)
+ gDvm.nonDaemonThreadCount--;
+ dvmUnlockThreadList();
+ /* fall through to "fail" */
+fail:
+ dvmReleaseTrackedAlloc(threadObj, self);
+ dvmReleaseTrackedAlloc(vmThreadObj, self);
+ dvmReleaseTrackedAlloc((Object*)threadNameStr, self);
+ if (self != NULL) {
+ if (self->jniEnv != NULL) {
+ dvmDestroyJNIEnv(self->jniEnv);
+ self->jniEnv = NULL;
+ }
+ freeThread(self);
+ }
+ setThreadSelf(NULL);
+ return false;
+}
+
+/*
+ * Detach the thread from the various data structures, notify other threads
+ * that are waiting to "join" it, and free up all heap-allocated storage.
+ *
+ * Used for all threads.
+ *
+ * When we get here the interpreted stack should be empty. The JNI 1.6 spec
+ * requires us to enforce this for the DetachCurrentThread call, probably
+ * because it also says that DetachCurrentThread causes all monitors
+ * associated with the thread to be released. (Because the stack is empty,
+ * we only have to worry about explicit JNI calls to MonitorEnter.)
+ *
+ * THOUGHT:
+ * We might want to avoid freeing our internal Thread structure until the
+ * associated Thread/VMThread objects get GCed. Our Thread is impossible to
+ * get to once the thread shuts down, but there is a small possibility of
+ * an operation starting in another thread before this thread halts, and
+ * finishing much later (perhaps the thread got stalled by a weird OS bug).
+ * We don't want something like Thread.isInterrupted() crawling through
+ * freed storage. Can do with a Thread finalizer, or by creating a
+ * dedicated ThreadObject class for java/lang/Thread and moving all of our
+ * state into that.
+ */
+void dvmDetachCurrentThread(void)
+{
+ Thread* self = dvmThreadSelf();
+ Object* vmThread;
+ Object* group;
+
+ /*
+ * Make sure we're not detaching a thread that's still running. (This
+ * could happen with an explicit JNI detach call.)
+ *
+ * A thread created by interpreted code will finish with a depth of
+ * zero, while a JNI-attached thread will have the synthetic "stack
+ * starter" native method at the top.
+ */
+ int curDepth = dvmComputeExactFrameDepth(self->curFrame);
+ if (curDepth != 0) {
+ bool topIsNative = false;
+
+ if (curDepth == 1) {
+ /* not expecting a lingering break frame; just look at curFrame */
+ assert(!dvmIsBreakFrame(self->curFrame));
+ StackSaveArea* ssa = SAVEAREA_FROM_FP(self->curFrame);
+ if (dvmIsNativeMethod(ssa->method))
+ topIsNative = true;
+ }
+
+ if (!topIsNative) {
+ LOGE("ERROR: detaching thread with interp frames (count=%d)\n",
+ curDepth);
+ dvmDumpThread(self, false);
+ dvmAbort();
+ }
+ }
+
+ group = dvmGetFieldObject(self->threadObj, gDvm.offJavaLangThread_group);
+ LOG_THREAD("threadid=%d: detach (group=%p)\n", self->threadId, group);
+
+ /*
+ * Release any held monitors. Since there are no interpreted stack
+ * frames, the only thing left are the monitors held by JNI MonitorEnter
+ * calls.
+ */
+ dvmReleaseJniMonitors(self);
+
+ /*
+ * Do some thread-exit uncaught exception processing if necessary.
+ */
+ if (dvmCheckException(self))
+ threadExitUncaughtException(self, group);
+
+ /*
+ * Remove the thread from the thread group.
+ */
+ if (group != NULL) {
+ Method* removeThread =
+ group->clazz->vtable[gDvm.voffJavaLangThreadGroup_removeThread];
+ JValue unused;
+ dvmCallMethod(self, removeThread, group, &unused, self->threadObj);
+ }
+
+ /*
+ * Clear the vmThread reference in the Thread object. Interpreted code
+ * will now see that this Thread is not running. As this may be the
+ * only reference to the VMThread object that the VM knows about, we
+ * have to create an internal reference to it first.
+ */
+ vmThread = dvmGetFieldObject(self->threadObj,
+ gDvm.offJavaLangThread_vmThread);
+ dvmAddTrackedAlloc(vmThread, self);
+ dvmSetFieldObject(self->threadObj, gDvm.offJavaLangThread_vmThread, NULL);
+
+ /* clear out our struct Thread pointer, since it's going away */
+ dvmSetFieldObject(vmThread, gDvm.offJavaLangVMThread_vmData, NULL);
+
+ /*
+ * Tell the debugger & DDM. This may cause the current thread or all
+ * threads to suspend.
+ *
+ * The JDWP spec is somewhat vague about when this happens, other than
+ * that it's issued by the dying thread, which may still appear in
+ * an "all threads" listing.
+ */
+ if (gDvm.debuggerConnected)
+ dvmDbgPostThreadDeath(self);
+
+ /*
+ * Thread.join() is implemented as an Object.wait() on the VMThread
+ * object. Signal anyone who is waiting.
+ */
+ dvmLockObject(self, vmThread);
+ dvmObjectNotifyAll(self, vmThread);
+ dvmUnlockObject(self, vmThread);
+
+ dvmReleaseTrackedAlloc(vmThread, self);
+ vmThread = NULL;
+
+ /*
+ * We're done manipulating objects, so it's okay if the GC runs in
+ * parallel with us from here out. It's important to do this if
+ * profiling is enabled, since we can wait indefinitely.
+ */
+ android_atomic_release_store(THREAD_VMWAIT, &self->status);
+
+ /*
+ * If we're doing method trace profiling, we don't want threads to exit,
+ * because if they do we'll end up reusing thread IDs. This complicates
+ * analysis and makes it impossible to have reasonable output in the
+ * "threads" section of the "key" file.
+ *
+ * We need to do this after Thread.join() completes, or other threads
+ * could get wedged. Since self->threadObj is still valid, the Thread
+ * object will not get GCed even though we're no longer in the ThreadGroup
+ * list (which is important since the profiling thread needs to get
+ * the thread's name).
+ */
+ MethodTraceState* traceState = &gDvm.methodTrace;
+
+ dvmLockMutex(&traceState->startStopLock);
+ if (traceState->traceEnabled) {
+ LOGI("threadid=%d: waiting for method trace to finish\n",
+ self->threadId);
+ while (traceState->traceEnabled) {
+ dvmWaitCond(&traceState->threadExitCond,
+ &traceState->startStopLock);
+ }
+ }
+ dvmUnlockMutex(&traceState->startStopLock);
+
+ dvmLockThreadList(self);
+
+ /*
+ * Lose the JNI context.
+ */
+ dvmDestroyJNIEnv(self->jniEnv);
+ self->jniEnv = NULL;
+
+ self->status = THREAD_ZOMBIE;
+
+ /*
+ * Remove ourselves from the internal thread list.
+ */
+ unlinkThread(self);
+
+ /*
+ * If we're the last one standing, signal anybody waiting in
+ * DestroyJavaVM that it's okay to exit.
+ */
+ if (!dvmGetFieldBoolean(self->threadObj, gDvm.offJavaLangThread_daemon)) {
+ gDvm.nonDaemonThreadCount--; // guarded by thread list lock
+
+ if (gDvm.nonDaemonThreadCount == 0) {
+ int cc;
+
+ LOGV("threadid=%d: last non-daemon thread\n", self->threadId);
+ //dvmDumpAllThreads(false);
+ // cond var guarded by threadListLock, which we already hold
+ cc = pthread_cond_signal(&gDvm.vmExitCond);
+ assert(cc == 0);
+ }
+ }
+
+ LOGV("threadid=%d: bye!\n", self->threadId);
+ releaseThreadId(self);
+ dvmUnlockThreadList();
+
+ setThreadSelf(NULL);
+
+ freeThread(self);
+}
+
+
+/*
+ * Suspend a single thread. Do not use to suspend yourself.
+ *
+ * This is used primarily for debugger/DDMS activity. Does not return
+ * until the thread has suspended or is in a "safe" state (e.g. executing
+ * native code outside the VM).
+ *
+ * The thread list lock should be held before calling here -- it's not
+ * entirely safe to hang on to a Thread* from another thread otherwise.
+ * (We'd need to grab it here anyway to avoid clashing with a suspend-all.)
+ */
+void dvmSuspendThread(Thread* thread)
+{
+ assert(thread != NULL);
+ assert(thread != dvmThreadSelf());
+ //assert(thread->handle != dvmJdwpGetDebugThread(gDvm.jdwpState));
+
+ lockThreadSuspendCount();
+ dvmAddToThreadSuspendCount(&thread->suspendCount, 1);
+ thread->dbgSuspendCount++;
+
+ LOG_THREAD("threadid=%d: suspend++, now=%d\n",
+ thread->threadId, thread->suspendCount);
+ unlockThreadSuspendCount();
+
+ waitForThreadSuspend(dvmThreadSelf(), thread);
+}
+
+/*
+ * Reduce the suspend count of a thread. If it hits zero, tell it to
+ * resume.
+ *
+ * Used primarily for debugger/DDMS activity. The thread in question
+ * might have been suspended singly or as part of a suspend-all operation.
+ *
+ * The thread list lock should be held before calling here -- it's not
+ * entirely safe to hang on to a Thread* from another thread otherwise.
+ * (We'd need to grab it here anyway to avoid clashing with a suspend-all.)
+ */
+void dvmResumeThread(Thread* thread)
+{
+ assert(thread != NULL);
+ assert(thread != dvmThreadSelf());
+ //assert(thread->handle != dvmJdwpGetDebugThread(gDvm.jdwpState));
+
+ lockThreadSuspendCount();
+ if (thread->suspendCount > 0) {
+ dvmAddToThreadSuspendCount(&thread->suspendCount, -1);
+ thread->dbgSuspendCount--;
+ } else {
+ LOG_THREAD("threadid=%d: suspendCount already zero\n",
+ thread->threadId);
+ }
+
+ LOG_THREAD("threadid=%d: suspend--, now=%d\n",
+ thread->threadId, thread->suspendCount);
+
+ if (thread->suspendCount == 0) {
+ dvmBroadcastCond(&gDvm.threadSuspendCountCond);
+ }
+
+ unlockThreadSuspendCount();
+}
+
+/*
+ * Suspend yourself, as a result of debugger activity.
+ */
+void dvmSuspendSelf(bool jdwpActivity)
+{
+ Thread* self = dvmThreadSelf();
+
+ /* debugger thread must not suspend itself due to debugger activity! */
+ assert(gDvm.jdwpState != NULL);
+ if (self->handle == dvmJdwpGetDebugThread(gDvm.jdwpState)) {
+ assert(false);
+ return;
+ }
+
+ /*
+ * Collisions with other suspends aren't really interesting. We want
+ * to ensure that we're the only one fiddling with the suspend count
+ * though.
+ */
+ lockThreadSuspendCount();
+ dvmAddToThreadSuspendCount(&self->suspendCount, 1);
+ self->dbgSuspendCount++;
+
+ /*
+ * Suspend ourselves.
+ */
+ assert(self->suspendCount > 0);
+ self->status = THREAD_SUSPENDED;
+ LOG_THREAD("threadid=%d: self-suspending (dbg)\n", self->threadId);
+
+ /*
+ * Tell JDWP that we've completed suspension. The JDWP thread can't
+ * tell us to resume before we're fully asleep because we hold the
+ * suspend count lock.
+ *
+ * If we got here via waitForDebugger(), don't do this part.
+ */
+ if (jdwpActivity) {
+ //LOGI("threadid=%d: clearing wait-for-event (my handle=%08x)\n",
+ // self->threadId, (int) self->handle);
+ dvmJdwpClearWaitForEventThread(gDvm.jdwpState);
+ }
+
+ while (self->suspendCount != 0) {
+ dvmWaitCond(&gDvm.threadSuspendCountCond,
+ &gDvm.threadSuspendCountLock);
+ if (self->suspendCount != 0) {
+ /*
+ * The condition was signaled but we're still suspended. This
+ * can happen if the debugger lets go while a SIGQUIT thread
+ * dump event is pending (assuming SignalCatcher was resumed for
+ * just long enough to try to grab the thread-suspend lock).
+ */
+ LOGD("threadid=%d: still suspended after undo (sc=%d dc=%d)\n",
+ self->threadId, self->suspendCount, self->dbgSuspendCount);
+ }
+ }
+ assert(self->suspendCount == 0 && self->dbgSuspendCount == 0);
+ self->status = THREAD_RUNNING;
+ LOG_THREAD("threadid=%d: self-reviving (dbg), status=%d\n",
+ self->threadId, self->status);
+
+ unlockThreadSuspendCount();
+}
+
+
+#ifdef HAVE_GLIBC
+# define NUM_FRAMES 20
+# include <execinfo.h>
+/*
+ * glibc-only stack dump function. Requires link with "--export-dynamic".
+ *
+ * TODO: move this into libs/cutils and make it work for all platforms.
+ */
+static void printBackTrace(void)
+{
+ void* array[NUM_FRAMES];
+ size_t size;
+ char** strings;
+ size_t i;
+
+ size = backtrace(array, NUM_FRAMES);
+ strings = backtrace_symbols(array, size);
+
+ LOGW("Obtained %zd stack frames.\n", size);
+
+ for (i = 0; i < size; i++)
+ LOGW("%s\n", strings[i]);
+
+ free(strings);
+}
+#else
+static void printBackTrace(void) {}
+#endif
+
+/*
+ * Dump the state of the current thread and that of another thread that
+ * we think is wedged.
+ */
+static void dumpWedgedThread(Thread* thread)
+{
+ dvmDumpThread(dvmThreadSelf(), false);
+ printBackTrace();
+
+ // dumping a running thread is risky, but could be useful
+ dvmDumpThread(thread, true);
+
+ // stop now and get a core dump
+ //abort();
+}
+
+/*
+ * If the thread is running at below-normal priority, temporarily elevate
+ * it to "normal".
+ *
+ * Returns zero if no changes were made. Otherwise, returns bit flags
+ * indicating what was changed, storing the previous values in the
+ * provided locations.
+ */
+int dvmRaiseThreadPriorityIfNeeded(Thread* thread, int* pSavedThreadPrio,
+ SchedPolicy* pSavedThreadPolicy)
+{
+ errno = 0;
+ *pSavedThreadPrio = getpriority(PRIO_PROCESS, thread->systemTid);
+ if (errno != 0) {
+ LOGW("Unable to get priority for threadid=%d sysTid=%d\n",
+ thread->threadId, thread->systemTid);
+ return 0;
+ }
+ if (get_sched_policy(thread->systemTid, pSavedThreadPolicy) != 0) {
+ LOGW("Unable to get policy for threadid=%d sysTid=%d\n",
+ thread->threadId, thread->systemTid);
+ return 0;
+ }
+
+ int changeFlags = 0;
+
+ /*
+ * Change the priority if we're in the background group.
+ */
+ if (*pSavedThreadPolicy == SP_BACKGROUND) {
+ if (set_sched_policy(thread->systemTid, SP_FOREGROUND) != 0) {
+ LOGW("Couldn't set fg policy on tid %d\n", thread->systemTid);
+ } else {
+ changeFlags |= kChangedPolicy;
+ LOGD("Temporarily moving tid %d to fg (was %d)\n",
+ thread->systemTid, *pSavedThreadPolicy);
+ }
+ }
+
+ /*
+ * getpriority() returns the "nice" value, so larger numbers indicate
+ * lower priority, with 0 being normal.
+ */
+ if (*pSavedThreadPrio > 0) {
+ const int kHigher = 0;
+ if (setpriority(PRIO_PROCESS, thread->systemTid, kHigher) != 0) {
+ LOGW("Couldn't raise priority on tid %d to %d\n",
+ thread->systemTid, kHigher);
+ } else {
+ changeFlags |= kChangedPriority;
+ LOGD("Temporarily raised priority on tid %d (%d -> %d)\n",
+ thread->systemTid, *pSavedThreadPrio, kHigher);
+ }
+ }
+
+ return changeFlags;
+}
+
+/*
+ * Reset the priority values for the thread in question.
+ */
+void dvmResetThreadPriority(Thread* thread, int changeFlags,
+ int savedThreadPrio, SchedPolicy savedThreadPolicy)
+{
+ if ((changeFlags & kChangedPolicy) != 0) {
+ if (set_sched_policy(thread->systemTid, savedThreadPolicy) != 0) {
+ LOGW("NOTE: couldn't reset tid %d to (%d)\n",
+ thread->systemTid, savedThreadPolicy);
+ } else {
+ LOGD("Restored policy of %d to %d\n",
+ thread->systemTid, savedThreadPolicy);
+ }
+ }
+
+ if ((changeFlags & kChangedPriority) != 0) {
+ if (setpriority(PRIO_PROCESS, thread->systemTid, savedThreadPrio) != 0)
+ {
+ LOGW("NOTE: couldn't reset priority on thread %d to %d\n",
+ thread->systemTid, savedThreadPrio);
+ } else {
+ LOGD("Restored priority on %d to %d\n",
+ thread->systemTid, savedThreadPrio);
+ }
+ }
+}
+
+/*
+ * Wait for another thread to see the pending suspension and stop running.
+ * It can either suspend itself or go into a non-running state such as
+ * VMWAIT or NATIVE in which it cannot interact with the GC.
+ *
+ * If we're running at a higher priority, sched_yield() may not do anything,
+ * so we need to sleep for "long enough" to guarantee that the other
+ * thread has a chance to finish what it's doing. Sleeping for too short
+ * a period (e.g. less than the resolution of the sleep clock) might cause
+ * the scheduler to return immediately, so we want to start with a
+ * "reasonable" value and expand.
+ *
+ * This does not return until the other thread has stopped running.
+ * Eventually we time out and the VM aborts.
+ *
+ * This does not try to detect the situation where two threads are
+ * waiting for each other to suspend. In normal use this is part of a
+ * suspend-all, which implies that the suspend-all lock is held, or as
+ * part of a debugger action in which the JDWP thread is always the one
+ * doing the suspending. (We may need to re-evaluate this now that
+ * getThreadStackTrace is implemented as suspend-snapshot-resume.)
+ *
+ * TODO: track basic stats about time required to suspend VM.
+ */
+#define FIRST_SLEEP (250*1000) /* 0.25s */
+#define MORE_SLEEP (750*1000) /* 0.75s */
+static void waitForThreadSuspend(Thread* self, Thread* thread)
+{
+ const int kMaxRetries = 10;
+ int spinSleepTime = FIRST_SLEEP;
+ bool complained = false;
+ int priChangeFlags = 0;
+ int savedThreadPrio = -500;
+ SchedPolicy savedThreadPolicy = SP_FOREGROUND;
+
+ int sleepIter = 0;
+ int retryCount = 0;
+ u8 startWhen = 0; // init req'd to placate gcc
+ u8 firstStartWhen = 0;
+
+ while (thread->status == THREAD_RUNNING) {
+ if (sleepIter == 0) { // get current time on first iteration
+ startWhen = dvmGetRelativeTimeUsec();
+ if (firstStartWhen == 0) // first iteration of first attempt
+ firstStartWhen = startWhen;
+
+ /*
+ * After waiting for a bit, check to see if the target thread is
+ * running at a reduced priority. If so, bump it up temporarily
+ * to give it more CPU time.
+ */
+ if (retryCount == 2) {
+ assert(thread->systemTid != 0);
+ priChangeFlags = dvmRaiseThreadPriorityIfNeeded(thread,
+ &savedThreadPrio, &savedThreadPolicy);
+ }
+ }
+
+#if defined (WITH_JIT)
+ /*
+ * If we're still waiting after the first timeout, unchain all
+ * translations iff:
+ * 1) There are new chains formed since the last unchain
+ * 2) The top VM frame of the running thread is running JIT'ed code
+ */
+ if (gDvmJit.pJitEntryTable && retryCount > 0 &&
+ gDvmJit.hasNewChain && thread->inJitCodeCache) {
+ LOGD("JIT unchain all for threadid=%d", thread->threadId);
+ dvmJitUnchainAll();
+ }
+#endif
+
+ /*
+ * Sleep briefly. The iterative sleep call returns false if we've
+ * exceeded the total time limit for this round of sleeping.
+ */
+ if (!dvmIterativeSleep(sleepIter++, spinSleepTime, startWhen)) {
+ if (spinSleepTime != FIRST_SLEEP) {
+ LOGW("threadid=%d: spin on suspend #%d threadid=%d (pcf=%d)\n",
+ self->threadId, retryCount,
+ thread->threadId, priChangeFlags);
+ if (retryCount > 1) {
+ /* stack trace logging is slow; skip on first iter */
+ dumpWedgedThread(thread);
+ }
+ complained = true;
+ }
+
+ // keep going; could be slow due to valgrind
+ sleepIter = 0;
+ spinSleepTime = MORE_SLEEP;
+
+ if (retryCount++ == kMaxRetries) {
+ LOGE("Fatal spin-on-suspend, dumping threads\n");
+ dvmDumpAllThreads(false);
+
+ /* log this after -- long traces will scroll off log */
+ LOGE("threadid=%d: stuck on threadid=%d, giving up\n",
+ self->threadId, thread->threadId);
+
+ /* try to get a debuggerd dump from the spinning thread */
+ dvmNukeThread(thread);
+ /* abort the VM */
+ dvmAbort();
+ }
+ }
+ }
+
+ if (complained) {
+ LOGW("threadid=%d: spin on suspend resolved in %lld msec\n",
+ self->threadId,
+ (dvmGetRelativeTimeUsec() - firstStartWhen) / 1000);
+ //dvmDumpThread(thread, false); /* suspended, so dump is safe */
+ }
+ if (priChangeFlags != 0) {
+ dvmResetThreadPriority(thread, priChangeFlags, savedThreadPrio,
+ savedThreadPolicy);
+ }
+}
+
+/*
+ * Suspend all threads except the current one. This is used by the GC,
+ * the debugger, and by any thread that hits a "suspend all threads"
+ * debugger event (e.g. breakpoint or exception).
+ *
+ * If thread N hits a "suspend all threads" breakpoint, we don't want it
+ * to suspend the JDWP thread. For the GC, we do, because the debugger can
+ * create objects and even execute arbitrary code. The "why" argument
+ * allows the caller to say why the suspension is taking place.
+ *
+ * This can be called when a global suspend has already happened, due to
+ * various debugger gymnastics, so keeping an "everybody is suspended" flag
+ * doesn't work.
+ *
+ * DO NOT grab any locks before calling here. We grab & release the thread
+ * lock and suspend lock here (and we're not using recursive threads), and
+ * we might have to self-suspend if somebody else beats us here.
+ *
+ * We know the current thread is in the thread list, because we attach the
+ * thread before doing anything that could cause VM suspension (like object
+ * allocation).
+ */
+void dvmSuspendAllThreads(SuspendCause why)
+{
+ Thread* self = dvmThreadSelf();
+ Thread* thread;
+
+ assert(why != 0);
+
+ /*
+ * Start by grabbing the thread suspend lock. If we can't get it, most
+ * likely somebody else is in the process of performing a suspend or
+ * resume, so lockThreadSuspend() will cause us to self-suspend.
+ *
+ * We keep the lock until all other threads are suspended.
+ */
+ lockThreadSuspend("susp-all", why);
+
+ LOG_THREAD("threadid=%d: SuspendAll starting\n", self->threadId);
+
+ /*
+ * This is possible if the current thread was in VMWAIT mode when a
+ * suspend-all happened, and then decided to do its own suspend-all.
+ * This can happen when a couple of threads have simultaneous events
+ * of interest to the debugger.
+ */
+ //assert(self->suspendCount == 0);
+
+ /*
+ * Increment everybody's suspend count (except our own).
+ */
+ dvmLockThreadList(self);
+
+ lockThreadSuspendCount();
+ for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+ if (thread == self)
+ continue;
+
+ /* debugger events don't suspend JDWP thread */
+ if ((why == SUSPEND_FOR_DEBUG || why == SUSPEND_FOR_DEBUG_EVENT) &&
+ thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
+ continue;
+
+ dvmAddToThreadSuspendCount(&thread->suspendCount, 1);
+ if (why == SUSPEND_FOR_DEBUG || why == SUSPEND_FOR_DEBUG_EVENT)
+ thread->dbgSuspendCount++;
+ }
+ unlockThreadSuspendCount();
+
+ /*
+ * Wait for everybody in THREAD_RUNNING state to stop. Other states
+ * indicate the code is either running natively or sleeping quietly.
+ * Any attempt to transition back to THREAD_RUNNING will cause a check
+ * for suspension, so it should be impossible for anything to execute
+ * interpreted code or modify objects (assuming native code plays nicely).
+ *
+ * It's also okay if the thread transitions to a non-RUNNING state.
+ *
+ * Note we released the threadSuspendCountLock before getting here,
+ * so if another thread is fiddling with its suspend count (perhaps
+ * self-suspending for the debugger) it won't block while we're waiting
+ * in here.
+ */
+ for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+ if (thread == self)
+ continue;
+
+ /* debugger events don't suspend JDWP thread */
+ if ((why == SUSPEND_FOR_DEBUG || why == SUSPEND_FOR_DEBUG_EVENT) &&
+ thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
+ continue;
+
+ /* wait for the other thread to see the pending suspend */
+ waitForThreadSuspend(self, thread);
+
+ LOG_THREAD("threadid=%d: threadid=%d status=%d sc=%d dc=%d\n",
+ self->threadId,
+ thread->threadId, thread->status, thread->suspendCount,
+ thread->dbgSuspendCount);
+ }
+
+ dvmUnlockThreadList();
+ unlockThreadSuspend();
+
+ LOG_THREAD("threadid=%d: SuspendAll complete\n", self->threadId);
+}
+
+/*
+ * Resume all threads that are currently suspended.
+ *
+ * The "why" must match with the previous suspend.
+ */
+void dvmResumeAllThreads(SuspendCause why)
+{
+ Thread* self = dvmThreadSelf();
+ Thread* thread;
+ int cc;
+
+ lockThreadSuspend("res-all", why); /* one suspend/resume at a time */
+ LOG_THREAD("threadid=%d: ResumeAll starting\n", self->threadId);
+
+ /*
+ * Decrement the suspend counts for all threads. No need for atomic
+ * writes, since nobody should be moving until we decrement the count.
+ * We do need to hold the thread list because of JNI attaches.
+ */
+ dvmLockThreadList(self);
+ lockThreadSuspendCount();
+ for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+ if (thread == self)
+ continue;
+
+ /* debugger events don't suspend JDWP thread */
+ if ((why == SUSPEND_FOR_DEBUG || why == SUSPEND_FOR_DEBUG_EVENT) &&
+ thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
+ {
+ continue;
+ }
+
+ if (thread->suspendCount > 0) {
+ dvmAddToThreadSuspendCount(&thread->suspendCount, -1);
+ if (why == SUSPEND_FOR_DEBUG || why == SUSPEND_FOR_DEBUG_EVENT)
+ thread->dbgSuspendCount--;
+ } else {
+ LOG_THREAD("threadid=%d: suspendCount already zero\n",
+ thread->threadId);
+ }
+ }
+ unlockThreadSuspendCount();
+ dvmUnlockThreadList();
+
+ /*
+ * In some ways it makes sense to continue to hold the thread-suspend
+ * lock while we issue the wakeup broadcast. It allows us to complete
+ * one operation before moving on to the next, which simplifies the
+ * thread activity debug traces.
+ *
+ * This approach caused us some difficulty under Linux, because the
+ * condition variable broadcast not only made the threads runnable,
+ * but actually caused them to execute, and it was a while before
+ * the thread performing the wakeup had an opportunity to release the
+ * thread-suspend lock.
+ *
+ * This is a problem because, when a thread tries to acquire that
+ * lock, it times out after 3 seconds. If at some point the thread
+ * is told to suspend, the clock resets; but since the VM is still
+ * theoretically mid-resume, there's no suspend pending. If, for
+ * example, the GC was waking threads up while the SIGQUIT handler
+ * was trying to acquire the lock, we would occasionally time out on
+ * a busy system and SignalCatcher would abort.
+ *
+ * We now perform the unlock before the wakeup broadcast. The next
+ * suspend can't actually start until the broadcast completes and
+ * returns, because we're holding the thread-suspend-count lock, but the
+ * suspending thread is now able to make progress and we avoid the abort.
+ *
+ * (Technically there is a narrow window between when we release
+ * the thread-suspend lock and grab the thread-suspend-count lock.
+ * This could cause us to send a broadcast to threads with nonzero
+ * suspend counts, but this is expected and they'll all just fall
+ * right back to sleep. It's probably safe to grab the suspend-count
+ * lock before releasing thread-suspend, since we're still following
+ * the correct order of acquisition, but it feels weird.)
+ */
+
+ LOG_THREAD("threadid=%d: ResumeAll waking others\n", self->threadId);
+ unlockThreadSuspend();
+
+ /*
+ * Broadcast a notification to all suspended threads, some or all of
+ * which may choose to wake up. No need to wait for them.
+ */
+ lockThreadSuspendCount();
+ cc = pthread_cond_broadcast(&gDvm.threadSuspendCountCond);
+ assert(cc == 0);
+ unlockThreadSuspendCount();
+
+ LOG_THREAD("threadid=%d: ResumeAll complete\n", self->threadId);
+}
+
+/*
+ * Undo any debugger suspensions. This is called when the debugger
+ * disconnects.
+ */
+void dvmUndoDebuggerSuspensions(void)
+{
+ Thread* self = dvmThreadSelf();
+ Thread* thread;
+ int cc;
+
+ lockThreadSuspend("undo", SUSPEND_FOR_DEBUG);
+ LOG_THREAD("threadid=%d: UndoDebuggerSusp starting\n", self->threadId);
+
+ /*
+ * Decrement the suspend counts for all threads. No need for atomic
+ * writes, since nobody should be moving until we decrement the count.
+ * We do need to hold the thread list because of JNI attaches.
+ */
+ dvmLockThreadList(self);
+ lockThreadSuspendCount();
+ for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+ if (thread == self)
+ continue;
+
+ /* debugger events don't suspend JDWP thread */
+ if (thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState)) {
+ assert(thread->dbgSuspendCount == 0);
+ continue;
+ }
+
+ assert(thread->suspendCount >= thread->dbgSuspendCount);
+ dvmAddToThreadSuspendCount(&thread->suspendCount,
+ -thread->dbgSuspendCount);
+ thread->dbgSuspendCount = 0;
+ }
+ unlockThreadSuspendCount();
+ dvmUnlockThreadList();
+
+ /*
+ * Broadcast a notification to all suspended threads, some or all of
+ * which may choose to wake up. No need to wait for them.
+ */
+ lockThreadSuspendCount();
+ cc = pthread_cond_broadcast(&gDvm.threadSuspendCountCond);
+ assert(cc == 0);
+ unlockThreadSuspendCount();
+
+ unlockThreadSuspend();
+
+ LOG_THREAD("threadid=%d: UndoDebuggerSusp complete\n", self->threadId);
+}
+
+/*
+ * Determine if a thread is suspended.
+ *
+ * As with all operations on foreign threads, the caller should hold
+ * the thread list lock before calling.
+ *
+ * If the thread is suspending or waking, these fields could be changing
+ * out from under us (or the thread could change state right after we
+ * examine it), making this generally unreliable. This is chiefly
+ * intended for use by the debugger.
+ */
+bool dvmIsSuspended(const Thread* thread)
+{
+ /*
+ * The thread could be:
+ * (1) Running happily. status is RUNNING, suspendCount is zero.
+ * Return "false".
+ * (2) Pending suspend. status is RUNNING, suspendCount is nonzero.
+ * Return "false".
+ * (3) Suspended. suspendCount is nonzero, and status is !RUNNING.
+ * Return "true".
+ * (4) Waking up. suspendCount is zero, status is SUSPENDED
+ * Return "false" (since it could change out from under us, unless
+ * we hold suspendCountLock).
+ */
+
+ return (thread->suspendCount != 0 && thread->status != THREAD_RUNNING);
+}
+
+/*
+ * Wait until another thread self-suspends. This is specifically for
+ * synchronization between the JDWP thread and a thread that has decided
+ * to suspend itself after sending an event to the debugger.
+ *
+ * Threads that encounter "suspend all" events work as well -- the thread
+ * in question suspends everybody else and then itself.
+ *
+ * We can't hold a thread lock here or in the caller, because we could
+ * get here just before the to-be-waited-for-thread issues a "suspend all".
+ * There's an opportunity for badness if the thread we're waiting for exits
+ * and gets cleaned up, but since the thread in question is processing a
+ * debugger event, that's not really a possibility. (To avoid deadlock,
+ * it's important that we not be in THREAD_RUNNING while we wait.)
+ */
+void dvmWaitForSuspend(Thread* thread)
+{
+ Thread* self = dvmThreadSelf();
+
+ LOG_THREAD("threadid=%d: waiting for threadid=%d to sleep\n",
+ self->threadId, thread->threadId);
+
+ assert(thread->handle != dvmJdwpGetDebugThread(gDvm.jdwpState));
+ assert(thread != self);
+ assert(self->status != THREAD_RUNNING);
+
+ waitForThreadSuspend(self, thread);
+
+ LOG_THREAD("threadid=%d: threadid=%d is now asleep\n",
+ self->threadId, thread->threadId);
+}
+
+/*
+ * Check to see if we need to suspend ourselves. If so, go to sleep on
+ * a condition variable.
+ *
+ * Returns "true" if we suspended ourselves.
+ */
+static bool fullSuspendCheck(Thread* self)
+{
+ assert(self != NULL);
+ assert(self->suspendCount >= 0);
+
+ /*
+ * Grab gDvm.threadSuspendCountLock. This gives us exclusive write
+ * access to self->suspendCount.
+ */
+ lockThreadSuspendCount(); /* grab gDvm.threadSuspendCountLock */
+
+ bool needSuspend = (self->suspendCount != 0);
+ if (needSuspend) {
+ LOG_THREAD("threadid=%d: self-suspending\n", self->threadId);
+ ThreadStatus oldStatus = self->status; /* should be RUNNING */
+ self->status = THREAD_SUSPENDED;
+
+ while (self->suspendCount != 0) {
+ /*
+ * Wait for wakeup signal, releasing lock. The act of releasing
+ * and re-acquiring the lock provides the memory barriers we
+ * need for correct behavior on SMP.
+ */
+ dvmWaitCond(&gDvm.threadSuspendCountCond,
+ &gDvm.threadSuspendCountLock);
+ }
+ assert(self->suspendCount == 0 && self->dbgSuspendCount == 0);
+ self->status = oldStatus;
+ LOG_THREAD("threadid=%d: self-reviving, status=%d\n",
+ self->threadId, self->status);
+ }
+
+ unlockThreadSuspendCount();
+
+ return needSuspend;
+}
+
+/*
+ * Check to see if a suspend is pending. If so, suspend the current
+ * thread, and return "true" after we have been resumed.
+ */
+bool dvmCheckSuspendPending(Thread* self)
+{
+ assert(self != NULL);
+ if (self->suspendCount == 0) {
+ return false;
+ } else {
+ return fullSuspendCheck(self);
+ }
+}
+
+/*
+ * Update our status.
+ *
+ * The "self" argument, which may be NULL, is accepted as an optimization.
+ *
+ * Returns the old status.
+ */
+ThreadStatus dvmChangeStatus(Thread* self, ThreadStatus newStatus)
+{
+ ThreadStatus oldStatus;
+
+ if (self == NULL)
+ self = dvmThreadSelf();
+
+ LOGVV("threadid=%d: (status %d -> %d)\n",
+ self->threadId, self->status, newStatus);
+
+ oldStatus = self->status;
+
+ if (newStatus == THREAD_RUNNING) {
+ /*
+ * Change our status to THREAD_RUNNING. The transition requires
+ * that we check for pending suspension, because the VM considers
+ * us to be "asleep" in all other states, and another thread could
+ * be performing a GC now.
+ *
+ * The order of operations is very significant here. One way to
+ * do this wrong is:
+ *
+ * GCing thread Our thread (in NATIVE)
+ * ------------ ----------------------
+ * check suspend count (== 0)
+ * dvmSuspendAllThreads()
+ * grab suspend-count lock
+ * increment all suspend counts
+ * release suspend-count lock
+ * check thread state (== NATIVE)
+ * all are suspended, begin GC
+ * set state to RUNNING
+ * (continue executing)
+ *
+ * We can correct this by grabbing the suspend-count lock and
+ * performing both of our operations (check suspend count, set
+ * state) while holding it, now we need to grab a mutex on every
+ * transition to RUNNING.
+ *
+ * What we do instead is change the order of operations so that
+ * the transition to RUNNING happens first. If we then detect
+ * that the suspend count is nonzero, we switch to SUSPENDED.
+ *
+ * Appropriate compiler and memory barriers are required to ensure
+ * that the operations are observed in the expected order.
+ *
+ * This does create a small window of opportunity where a GC in
+ * progress could observe what appears to be a running thread (if
+ * it happens to look between when we set to RUNNING and when we
+ * switch to SUSPENDED). At worst this only affects assertions
+ * and thread logging. (We could work around it with some sort
+ * of intermediate "pre-running" state that is generally treated
+ * as equivalent to running, but that doesn't seem worthwhile.)
+ *
+ * We can also solve this by combining the "status" and "suspend
+ * count" fields into a single 32-bit value. This trades the
+ * store/load barrier on transition to RUNNING for an atomic RMW
+ * op on all transitions and all suspend count updates (also, all
+ * accesses to status or the thread count require bit-fiddling).
+ * It also eliminates the brief transition through RUNNING when
+ * the thread is supposed to be suspended. This is possibly faster
+ * on SMP and slightly more correct, but less convenient.
+ */
+ assert(oldStatus != THREAD_RUNNING);
+ android_atomic_acquire_store(newStatus, &self->status);
+ if (self->suspendCount != 0) {
+ fullSuspendCheck(self);
+ }
+ } else {
+ /*
+ * Not changing to THREAD_RUNNING. No additional work required.
+ *
+ * We use a releasing store to ensure that, if we were RUNNING,
+ * any updates we previously made to objects on the managed heap
+ * will be observed before the state change.
+ */
+ assert(newStatus != THREAD_SUSPENDED);
+ android_atomic_release_store(newStatus, &self->status);
+ }
+
+ return oldStatus;
+}
+
+/*
+ * Get a statically defined thread group from a field in the ThreadGroup
+ * Class object. Expected arguments are "mMain" and "mSystem".
+ */
+static Object* getStaticThreadGroup(const char* fieldName)
+{
+ StaticField* groupField;
+ Object* groupObj;
+
+ groupField = dvmFindStaticField(gDvm.classJavaLangThreadGroup,
+ fieldName, "Ljava/lang/ThreadGroup;");
+ if (groupField == NULL) {
+ LOGE("java.lang.ThreadGroup does not have an '%s' field\n", fieldName);
+ dvmThrowException("Ljava/lang/IncompatibleClassChangeError;", NULL);
+ return NULL;
+ }
+ groupObj = dvmGetStaticFieldObject(groupField);
+ if (groupObj == NULL) {
+ LOGE("java.lang.ThreadGroup.%s not initialized\n", fieldName);
+ dvmThrowException("Ljava/lang/InternalError;", NULL);
+ return NULL;
+ }
+
+ return groupObj;
+}
+Object* dvmGetSystemThreadGroup(void)
+{
+ return getStaticThreadGroup("mSystem");
+}
+Object* dvmGetMainThreadGroup(void)
+{
+ return getStaticThreadGroup("mMain");
+}
+
+/*
+ * Given a VMThread object, return the associated Thread*.
+ *
+ * NOTE: if the thread detaches, the struct Thread will disappear, and
+ * we will be touching invalid data. For safety, lock the thread list
+ * before calling this.
+ */
+Thread* dvmGetThreadFromThreadObject(Object* vmThreadObj)
+{
+ int vmData;
+
+ vmData = dvmGetFieldInt(vmThreadObj, gDvm.offJavaLangVMThread_vmData);
+
+ if (false) {
+ Thread* thread = gDvm.threadList;
+ while (thread != NULL) {
+ if ((Thread*)vmData == thread)
+ break;
+
+ thread = thread->next;
+ }
+
+ if (thread == NULL) {
+ LOGW("WARNING: vmThreadObj=%p has thread=%p, not in thread list\n",
+ vmThreadObj, (Thread*)vmData);
+ vmData = 0;
+ }
+ }
+
+ return (Thread*) vmData;
+}
+
+/*
+ * Given a pthread handle, return the associated Thread*.
+ * Caller must hold the thread list lock.
+ *
+ * Returns NULL if the thread was not found.
+ */
+Thread* dvmGetThreadByHandle(pthread_t handle)
+{
+ Thread* thread;
+ for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+ if (thread->handle == handle)
+ break;
+ }
+ return thread;
+}
+
+/*
+ * Given a threadId, return the associated Thread*.
+ * Caller must hold the thread list lock.
+ *
+ * Returns NULL if the thread was not found.
+ */
+Thread* dvmGetThreadByThreadId(u4 threadId)
+{
+ Thread* thread;
+ for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+ if (thread->threadId == threadId)
+ break;
+ }
+ return thread;
+}
+
+
+/*
+ * Conversion map for "nice" values.
+ *
+ * We use Android thread priority constants to be consistent with the rest
+ * of the system. In some cases adjacent entries may overlap.
+ */
+static const int kNiceValues[10] = {
+ ANDROID_PRIORITY_LOWEST, /* 1 (MIN_PRIORITY) */
+ ANDROID_PRIORITY_BACKGROUND + 6,
+ ANDROID_PRIORITY_BACKGROUND + 3,
+ ANDROID_PRIORITY_BACKGROUND,
+ ANDROID_PRIORITY_NORMAL, /* 5 (NORM_PRIORITY) */
+ ANDROID_PRIORITY_NORMAL - 2,
+ ANDROID_PRIORITY_NORMAL - 4,
+ ANDROID_PRIORITY_URGENT_DISPLAY + 3,
+ ANDROID_PRIORITY_URGENT_DISPLAY + 2,
+ ANDROID_PRIORITY_URGENT_DISPLAY /* 10 (MAX_PRIORITY) */
+};
+
+/*
+ * Change the priority of a system thread to match that of the Thread object.
+ *
+ * We map a priority value from 1-10 to Linux "nice" values, where lower
+ * numbers indicate higher priority.
+ */
+void dvmChangeThreadPriority(Thread* thread, int newPriority)
+{
+ pid_t pid = thread->systemTid;
+ int newNice;
+
+ if (newPriority < 1 || newPriority > 10) {
+ LOGW("bad priority %d\n", newPriority);
+ newPriority = 5;
+ }
+ newNice = kNiceValues[newPriority-1];
+
+ if (newNice >= ANDROID_PRIORITY_BACKGROUND) {
+ set_sched_policy(dvmGetSysThreadId(), SP_BACKGROUND);
+ } else if (getpriority(PRIO_PROCESS, pid) >= ANDROID_PRIORITY_BACKGROUND) {
+ set_sched_policy(dvmGetSysThreadId(), SP_FOREGROUND);
+ }
+
+ if (setpriority(PRIO_PROCESS, pid, newNice) != 0) {
+ char* str = dvmGetThreadName(thread);
+ LOGI("setPriority(%d) '%s' to prio=%d(n=%d) failed: %s\n",
+ pid, str, newPriority, newNice, strerror(errno));
+ free(str);
+ } else {
+ LOGV("setPriority(%d) to prio=%d(n=%d)\n",
+ pid, newPriority, newNice);
+ }
+}
+
+/*
+ * Get the thread priority for the current thread by querying the system.
+ * This is useful when attaching a thread through JNI.
+ *
+ * Returns a value from 1 to 10 (compatible with java.lang.Thread values).
+ */
+static int getThreadPriorityFromSystem(void)
+{
+ int i, sysprio, jprio;
+
+ errno = 0;
+ sysprio = getpriority(PRIO_PROCESS, 0);
+ if (sysprio == -1 && errno != 0) {
+ LOGW("getpriority() failed: %s\n", strerror(errno));
+ return THREAD_NORM_PRIORITY;
+ }
+
+ jprio = THREAD_MIN_PRIORITY;
+ for (i = 0; i < NELEM(kNiceValues); i++) {
+ if (sysprio >= kNiceValues[i])
+ break;
+ jprio++;
+ }
+ if (jprio > THREAD_MAX_PRIORITY)
+ jprio = THREAD_MAX_PRIORITY;
+
+ return jprio;
+}
+
+
+/*
+ * Return true if the thread is on gDvm.threadList.
+ * Caller should not hold gDvm.threadListLock.
+ */
+bool dvmIsOnThreadList(const Thread* thread)
+{
+ bool ret = false;
+
+ dvmLockThreadList(NULL);
+ if (thread == gDvm.threadList) {
+ ret = true;
+ } else {
+ ret = thread->prev != NULL || thread->next != NULL;
+ }
+ dvmUnlockThreadList();
+
+ return ret;
+}
+
+/*
+ * Dump a thread to the log file -- just calls dvmDumpThreadEx() with an
+ * output target.
+ */
+void dvmDumpThread(Thread* thread, bool isRunning)
+{
+ DebugOutputTarget target;
+
+ dvmCreateLogOutputTarget(&target, ANDROID_LOG_INFO, LOG_TAG);
+ dvmDumpThreadEx(&target, thread, isRunning);
+}
+
+/*
+ * Try to get the scheduler group.
+ *
+ * The data from /proc/<pid>/cgroup looks (something) like:
+ * 2:cpu:/bg_non_interactive
+ * 1:cpuacct:/
+ *
+ * We return the part on the "cpu" line after the '/', which will be an
+ * empty string for the default cgroup. If the string is longer than
+ * "bufLen", the string will be truncated.
+ *
+ * On error, -1 is returned, and an error description will be stored in
+ * the buffer.
+ */
+static int getSchedulerGroup(int tid, char* buf, size_t bufLen)
+{
+#ifdef HAVE_ANDROID_OS
+ char pathBuf[32];
+ char lineBuf[256];
+ FILE *fp;
+
+ snprintf(pathBuf, sizeof(pathBuf), "/proc/%d/cgroup", tid);
+ if ((fp = fopen(pathBuf, "r")) == NULL) {
+ snprintf(buf, bufLen, "[fopen-error:%d]", errno);
+ return -1;
+ }
+
+ while (fgets(lineBuf, sizeof(lineBuf) -1, fp) != NULL) {
+ char* subsys;
+ char* grp;
+ size_t len;
+
+ /* Junk the first field */
+ subsys = strchr(lineBuf, ':');
+ if (subsys == NULL) {
+ goto out_bad_data;
+ }
+
+ if (strncmp(subsys, ":cpu:", 5) != 0) {
+ /* Not the subsys we're looking for */
+ continue;
+ }
+
+ grp = strchr(subsys, '/');
+ if (grp == NULL) {
+ goto out_bad_data;
+ }
+ grp++; /* Drop the leading '/' */
+
+ len = strlen(grp);
+ grp[len-1] = '\0'; /* Drop the trailing '\n' */
+
+ if (bufLen <= len) {
+ len = bufLen - 1;
+ }
+ strncpy(buf, grp, len);
+ buf[len] = '\0';
+ fclose(fp);
+ return 0;
+ }
+
+ snprintf(buf, bufLen, "[no-cpu-subsys]");
+ fclose(fp);
+ return -1;
+
+out_bad_data:
+ LOGE("Bad cgroup data {%s}", lineBuf);
+ snprintf(buf, bufLen, "[data-parse-failed]");
+ fclose(fp);
+ return -1;
+
+#else
+ snprintf(buf, bufLen, "[n/a]");
+ return -1;
+#endif
+}
+
+/*
+ * Convert ThreadStatus to a string.
+ */
+const char* dvmGetThreadStatusStr(ThreadStatus status)
+{
+ switch (status) {
+ case THREAD_ZOMBIE: return "ZOMBIE";
+ case THREAD_RUNNING: return "RUNNABLE";
+ case THREAD_TIMED_WAIT: return "TIMED_WAIT";
+ case THREAD_MONITOR: return "MONITOR";
+ case THREAD_WAIT: return "WAIT";
+ case THREAD_INITIALIZING: return "INITIALIZING";
+ case THREAD_STARTING: return "STARTING";
+ case THREAD_NATIVE: return "NATIVE";
+ case THREAD_VMWAIT: return "VMWAIT";
+ case THREAD_SUSPENDED: return "SUSPENDED";
+ default: return "UNKNOWN";
+ }
+}
+
+/*
+ * Print information about the specified thread.
+ *
+ * Works best when the thread in question is "self" or has been suspended.
+ * When dumping a separate thread that's still running, set "isRunning" to
+ * use a more cautious thread dump function.
+ */
+void dvmDumpThreadEx(const DebugOutputTarget* target, Thread* thread,
+ bool isRunning)
+{
+ Object* threadObj;
+ Object* groupObj;
+ StringObject* nameStr;
+ char* threadName = NULL;
+ char* groupName = NULL;
+ char schedulerGroupBuf[32];
+ bool isDaemon;
+ int priority; // java.lang.Thread priority
+ int policy; // pthread policy
+ struct sched_param sp; // pthread scheduling parameters
+ char schedstatBuf[64]; // contents of /proc/[pid]/task/[tid]/schedstat
+ int schedstatFd;
+
+ /*
+ * Get the java.lang.Thread object. This function gets called from
+ * some weird debug contexts, so it's possible that there's a GC in
+ * progress on some other thread. To decrease the chances of the
+ * thread object being moved out from under us, we add the reference
+ * to the tracked allocation list, which pins it in place.
+ *
+ * If threadObj is NULL, the thread is still in the process of being
+ * attached to the VM, and there's really nothing interesting to
+ * say about it yet.
+ */
+ threadObj = thread->threadObj;
+ if (threadObj == NULL) {
+ LOGI("Can't dump thread %d: threadObj not set\n", thread->threadId);
+ return;
+ }
+ dvmAddTrackedAlloc(threadObj, NULL);
+
+ nameStr = (StringObject*) dvmGetFieldObject(threadObj,
+ gDvm.offJavaLangThread_name);
+ threadName = dvmCreateCstrFromString(nameStr);
+
+ priority = dvmGetFieldInt(threadObj, gDvm.offJavaLangThread_priority);
+ isDaemon = dvmGetFieldBoolean(threadObj, gDvm.offJavaLangThread_daemon);
+
+ if (pthread_getschedparam(pthread_self(), &policy, &sp) != 0) {
+ LOGW("Warning: pthread_getschedparam failed\n");
+ policy = -1;
+ sp.sched_priority = -1;
+ }
+ if (getSchedulerGroup(thread->systemTid, schedulerGroupBuf,
+ sizeof(schedulerGroupBuf)) == 0 &&
+ schedulerGroupBuf[0] == '\0') {
+ strcpy(schedulerGroupBuf, "default");
+ }
+
+ /* a null value for group is not expected, but deal with it anyway */
+ groupObj = (Object*) dvmGetFieldObject(threadObj,
+ gDvm.offJavaLangThread_group);
+ if (groupObj != NULL) {
+ int offset = dvmFindFieldOffset(gDvm.classJavaLangThreadGroup,
+ "name", "Ljava/lang/String;");
+ if (offset < 0) {
+ LOGW("Unable to find 'name' field in ThreadGroup\n");
+ } else {
+ nameStr = (StringObject*) dvmGetFieldObject(groupObj, offset);
+ groupName = dvmCreateCstrFromString(nameStr);
+ }
+ }
+ if (groupName == NULL)
+ groupName = strdup("(null; initializing?)");
+
+ dvmPrintDebugMessage(target,
+ "\"%s\"%s prio=%d tid=%d %s%s\n",
+ threadName, isDaemon ? " daemon" : "",
+ priority, thread->threadId, dvmGetThreadStatusStr(thread->status),
+#if defined(WITH_JIT)
+ thread->inJitCodeCache ? " JIT" : ""
+#else
+ ""
+#endif
+ );
+ dvmPrintDebugMessage(target,
+ " | group=\"%s\" sCount=%d dsCount=%d obj=%p self=%p\n",
+ groupName, thread->suspendCount, thread->dbgSuspendCount,
+ thread->threadObj, thread);
+ dvmPrintDebugMessage(target,
+ " | sysTid=%d nice=%d sched=%d/%d cgrp=%s handle=%d\n",
+ thread->systemTid, getpriority(PRIO_PROCESS, thread->systemTid),
+ policy, sp.sched_priority, schedulerGroupBuf, (int)thread->handle);
+
+ snprintf(schedstatBuf, sizeof(schedstatBuf), "/proc/%d/task/%d/schedstat",
+ getpid(), thread->systemTid);
+ schedstatFd = open(schedstatBuf, O_RDONLY);
+ if (schedstatFd >= 0) {
+ int bytes;
+ bytes = read(schedstatFd, schedstatBuf, sizeof(schedstatBuf) - 1);
+ close(schedstatFd);
+ if (bytes > 1) {
+ schedstatBuf[bytes-1] = 0; // trailing newline
+ dvmPrintDebugMessage(target, " | schedstat=( %s )\n", schedstatBuf);
+ }
+ }
+
+#ifdef WITH_MONITOR_TRACKING
+ if (!isRunning) {
+ LockedObjectData* lod = thread->pLockedObjects;
+ if (lod != NULL)
+ dvmPrintDebugMessage(target, " | monitors held:\n");
+ else
+ dvmPrintDebugMessage(target, " | monitors held: <none>\n");
+ while (lod != NULL) {
+ Object* obj = lod->obj;
+ if (obj->clazz == gDvm.classJavaLangClass) {
+ ClassObject* clazz = (ClassObject*) obj;
+ dvmPrintDebugMessage(target, " > %p[%d] (%s object for class %s)\n",
+ obj, lod->recursionCount, obj->clazz->descriptor,
+ clazz->descriptor);
+ } else {
+ dvmPrintDebugMessage(target, " > %p[%d] (%s)\n",
+ obj, lod->recursionCount, obj->clazz->descriptor);
+ }
+ lod = lod->next;
+ }
+ }
+#endif
+
+ if (isRunning)
+ dvmDumpRunningThreadStack(target, thread);
+ else
+ dvmDumpThreadStack(target, thread);
+
+ dvmReleaseTrackedAlloc(threadObj, NULL);
+ free(threadName);
+ free(groupName);
+}
+
+/*
+ * Get the name of a thread.
+ *
+ * For correctness, the caller should hold the thread list lock to ensure
+ * that the thread doesn't go away mid-call.
+ *
+ * Returns a newly-allocated string, or NULL if the Thread doesn't have a name.
+ */
+char* dvmGetThreadName(Thread* thread)
+{
+ StringObject* nameObj;
+
+ if (thread->threadObj == NULL) {
+ LOGW("threadObj is NULL, name not available\n");
+ return strdup("-unknown-");
+ }
+
+ nameObj = (StringObject*)
+ dvmGetFieldObject(thread->threadObj, gDvm.offJavaLangThread_name);
+ return dvmCreateCstrFromString(nameObj);
+}
+
+/*
+ * Dump all threads to the log file -- just calls dvmDumpAllThreadsEx() with
+ * an output target.
+ */
+void dvmDumpAllThreads(bool grabLock)
+{
+ DebugOutputTarget target;
+
+ dvmCreateLogOutputTarget(&target, ANDROID_LOG_INFO, LOG_TAG);
+ dvmDumpAllThreadsEx(&target, grabLock);
+}
+
+/*
+ * Print information about all known threads. Assumes they have been
+ * suspended (or are in a non-interpreting state, e.g. WAIT or NATIVE).
+ *
+ * If "grabLock" is true, we grab the thread lock list. This is important
+ * to do unless the caller already holds the lock.
+ */
+void dvmDumpAllThreadsEx(const DebugOutputTarget* target, bool grabLock)
+{
+ Thread* thread;
+
+ dvmPrintDebugMessage(target, "DALVIK THREADS:\n");
+
+#ifdef HAVE_ANDROID_OS
+ dvmPrintDebugMessage(target,
+ "(mutexes: tll=%x tsl=%x tscl=%x ghl=%x hwl=%x hwll=%x)\n",
+ gDvm.threadListLock.value,
+ gDvm._threadSuspendLock.value,
+ gDvm.threadSuspendCountLock.value,
+ gDvm.gcHeapLock.value,
+ gDvm.heapWorkerLock.value,
+ gDvm.heapWorkerListLock.value);
+#endif
+
+ if (grabLock)
+ dvmLockThreadList(dvmThreadSelf());
+
+ thread = gDvm.threadList;
+ while (thread != NULL) {
+ dvmDumpThreadEx(target, thread, false);
+
+ /* verify link */
+ assert(thread->next == NULL || thread->next->prev == thread);
+
+ thread = thread->next;
+ }
+
+ if (grabLock)
+ dvmUnlockThreadList();
+}
+
+/*
+ * Nuke the target thread from orbit.
+ *
+ * The idea is to send a "crash" signal to the target thread so that
+ * debuggerd will take notice and dump an appropriate stack trace.
+ * Because of the way debuggerd works, we have to throw the same signal
+ * at it twice.
+ *
+ * This does not necessarily cause the entire process to stop, but once a
+ * thread has been nuked the rest of the system is likely to be unstable.
+ * This returns so that some limited set of additional operations may be
+ * performed, but it's advisable (and expected) to call dvmAbort soon.
+ * (This is NOT a way to simply cancel a thread.)
+ */
+void dvmNukeThread(Thread* thread)
+{
+ int killResult;
+
+ /* suppress the heapworker watchdog to assist anyone using a debugger */
+ gDvm.nativeDebuggerActive = true;
+
+ /*
+ * Send the signals, separated by a brief interval to allow debuggerd
+ * to work its magic. An uncommon signal like SIGFPE or SIGSTKFLT
+ * can be used instead of SIGSEGV to avoid making it look like the
+ * code actually crashed at the current point of execution.
+ *
+ * (Observed behavior: with SIGFPE, debuggerd will dump the target
+ * thread and then the thread that calls dvmAbort. With SIGSEGV,
+ * you don't get the second stack trace; possibly something in the
+ * kernel decides that a signal has already been sent and it's time
+ * to just kill the process. The position in the current thread is
+ * generally known, so the second dump is not useful.)
+ *
+ * The target thread can continue to execute between the two signals.
+ * (The first just causes debuggerd to attach to it.)
+ */
+ LOGD("threadid=%d: sending two SIGSTKFLTs to threadid=%d (tid=%d) to"
+ " cause debuggerd dump\n",
+ dvmThreadSelf()->threadId, thread->threadId, thread->systemTid);
+ killResult = pthread_kill(thread->handle, SIGSTKFLT);
+ if (killResult != 0) {
+ LOGD("NOTE: pthread_kill #1 failed: %s\n", strerror(killResult));
+ }
+ usleep(2 * 1000 * 1000); // TODO: timed-wait until debuggerd attaches
+ killResult = pthread_kill(thread->handle, SIGSTKFLT);
+ if (killResult != 0) {
+ LOGD("NOTE: pthread_kill #2 failed: %s\n", strerror(killResult));
+ }
+ LOGD("Sent, pausing to let debuggerd run\n");
+ usleep(8 * 1000 * 1000); // TODO: timed-wait until debuggerd finishes
+
+ /* ignore SIGSEGV so the eventual dmvAbort() doesn't notify debuggerd */
+ signal(SIGSEGV, SIG_IGN);
+ LOGD("Continuing\n");
+}
+
+#ifdef WITH_MONITOR_TRACKING
+/*
+ * Count up the #of locked objects in the current thread.
+ */
+static int getThreadObjectCount(const Thread* self)
+{
+ LockedObjectData* lod;
+ int count = 0;
+
+ lod = self->pLockedObjects;
+ while (lod != NULL) {
+ count++;
+ lod = lod->next;
+ }
+ return count;
+}
+
+/*
+ * Add the object to the thread's locked object list if it doesn't already
+ * exist. The most recently added object is the most likely to be released
+ * next, so we insert at the head of the list.
+ *
+ * If it already exists, we increase the recursive lock count.
+ *
+ * The object's lock may be thin or fat.
+ */
+void dvmAddToMonitorList(Thread* self, Object* obj, bool withTrace)
+{
+ LockedObjectData* newLod;
+ LockedObjectData* lod;
+ int* trace;
+ int depth;
+
+ lod = self->pLockedObjects;
+ while (lod != NULL) {
+ if (lod->obj == obj) {
+ lod->recursionCount++;
+ LOGV("+++ +recursive lock %p -> %d\n", obj, lod->recursionCount);
+ return;
+ }
+ lod = lod->next;
+ }
+
+ newLod = (LockedObjectData*) calloc(1, sizeof(LockedObjectData));
+ if (newLod == NULL) {
+ LOGE("malloc failed on %d bytes\n", sizeof(LockedObjectData));
+ return;
+ }
+ newLod->obj = obj;
+ newLod->recursionCount = 0;
+
+ if (withTrace) {
+ trace = dvmFillInStackTraceRaw(self, &depth);
+ newLod->rawStackTrace = trace;
+ newLod->stackDepth = depth;
+ }
+
+ newLod->next = self->pLockedObjects;
+ self->pLockedObjects = newLod;
+
+ LOGV("+++ threadid=%d: added %p, now %d\n",
+ self->threadId, newLod, getThreadObjectCount(self));
+}
+
+/*
+ * Remove the object from the thread's locked object list. If the entry
+ * has a nonzero recursion count, we just decrement the count instead.
+ */
+void dvmRemoveFromMonitorList(Thread* self, Object* obj)
+{
+ LockedObjectData* lod;
+ LockedObjectData* prevLod;
+
+ lod = self->pLockedObjects;
+ prevLod = NULL;
+ while (lod != NULL) {
+ if (lod->obj == obj) {
+ if (lod->recursionCount > 0) {
+ lod->recursionCount--;
+ LOGV("+++ -recursive lock %p -> %d\n",
+ obj, lod->recursionCount);
+ return;
+ } else {
+ break;
+ }
+ }
+ prevLod = lod;
+ lod = lod->next;
+ }
+
+ if (lod == NULL) {
+ LOGW("BUG: object %p not found in thread's lock list\n", obj);
+ return;
+ }
+ if (prevLod == NULL) {
+ /* first item in list */
+ assert(self->pLockedObjects == lod);
+ self->pLockedObjects = lod->next;
+ } else {
+ /* middle/end of list */
+ prevLod->next = lod->next;
+ }
+
+ LOGV("+++ threadid=%d: removed %p, now %d\n",
+ self->threadId, lod, getThreadObjectCount(self));
+ free(lod->rawStackTrace);
+ free(lod);
+}
+
+/*
+ * If the specified object is already in the thread's locked object list,
+ * return the LockedObjectData struct. Otherwise return NULL.
+ */
+LockedObjectData* dvmFindInMonitorList(const Thread* self, const Object* obj)
+{
+ LockedObjectData* lod;
+
+ lod = self->pLockedObjects;
+ while (lod != NULL) {
+ if (lod->obj == obj)
+ return lod;
+ lod = lod->next;
+ }
+ return NULL;
+}
+#endif /*WITH_MONITOR_TRACKING*/
+
+
+/*
+ * GC helper functions
+ */
+
+/*
+ * Add the contents of the registers from the interpreted call stack.
+ */
+static void gcScanInterpStackReferences(Thread *thread)
+{
+ const u4 *framePtr;
+#if WITH_EXTRA_GC_CHECKS > 1
+ bool first = true;
+#endif
+
+ framePtr = (const u4 *)thread->curFrame;
+ while (framePtr != NULL) {
+ const StackSaveArea *saveArea;
+ const Method *method;
+
+ saveArea = SAVEAREA_FROM_FP(framePtr);
+ method = saveArea->method;
+ if (method != NULL) {
+#ifdef COUNT_PRECISE_METHODS
+ /* the GC is running, so no lock required */
+ if (dvmPointerSetAddEntry(gDvm.preciseMethods, method))
+ LOGI("PGC: added %s.%s %p\n",
+ method->clazz->descriptor, method->name, method);
+#endif
+#if WITH_EXTRA_GC_CHECKS > 1
+ /*
+ * May also want to enable the memset() in the "invokeMethod"
+ * goto target in the portable interpreter. That sets the stack
+ * to a pattern that makes referring to uninitialized data
+ * very obvious.
+ */
+
+ if (first) {
+ /*
+ * First frame, isn't native, check the "alternate" saved PC
+ * as a sanity check.
+ *
+ * It seems like we could check the second frame if the first
+ * is native, since the PCs should be the same. It turns out
+ * this doesn't always work. The problem is that we could
+ * have calls in the sequence:
+ * interp method #2
+ * native method
+ * interp method #1
+ *
+ * and then GC while in the native method after returning
+ * from interp method #2. The currentPc on the stack is
+ * for interp method #1, but thread->currentPc2 is still
+ * set for the last thing interp method #2 did.
+ *
+ * This can also happen in normal execution:
+ * - sget-object on not-yet-loaded class
+ * - class init updates currentPc2
+ * - static field init is handled by parsing annotations;
+ * static String init requires creation of a String object,
+ * which can cause a GC
+ *
+ * Essentially, any pattern that involves executing
+ * interpreted code and then causes an allocation without
+ * executing instructions in the original method will hit
+ * this. These are rare enough that the test still has
+ * some value.
+ */
+ if (saveArea->xtra.currentPc != thread->currentPc2) {
+ LOGW("PGC: savedPC(%p) != current PC(%p), %s.%s ins=%p\n",
+ saveArea->xtra.currentPc, thread->currentPc2,
+ method->clazz->descriptor, method->name, method->insns);
+ if (saveArea->xtra.currentPc != NULL)
+ LOGE(" pc inst = 0x%04x\n", *saveArea->xtra.currentPc);
+ if (thread->currentPc2 != NULL)
+ LOGE(" pc2 inst = 0x%04x\n", *thread->currentPc2);
+ dvmDumpThread(thread, false);
+ }
+ } else {
+ /*
+ * It's unusual, but not impossible, for a non-first frame
+ * to be at something other than a method invocation. For
+ * example, if we do a new-instance on a nonexistent class,
+ * we'll have a lot of class loader activity on the stack
+ * above the frame with the "new" operation. Could also
+ * happen while we initialize a Throwable when an instruction
+ * fails.
+ *
+ * So there's not much we can do here to verify the PC,
+ * except to verify that it's a GC point.
+ */
+ }
+ assert(saveArea->xtra.currentPc != NULL);
+#endif
+
+ const RegisterMap* pMap;
+ const u1* regVector;
+ int i;
+
+ Method* nonConstMethod = (Method*) method; // quiet gcc
+ pMap = dvmGetExpandedRegisterMap(nonConstMethod);
+ if (pMap != NULL) {
+ /* found map, get registers for this address */
+ int addr = saveArea->xtra.currentPc - method->insns;
+ regVector = dvmRegisterMapGetLine(pMap, addr);
+ if (regVector == NULL) {
+ LOGW("PGC: map but no entry for %s.%s addr=0x%04x\n",
+ method->clazz->descriptor, method->name, addr);
+ } else {
+ LOGV("PGC: found map for %s.%s 0x%04x (t=%d)\n",
+ method->clazz->descriptor, method->name, addr,
+ thread->threadId);
+ }
+ } else {
+ /*
+ * No map found. If precise GC is disabled this is
+ * expected -- we don't create pointers to the map data even
+ * if it's present -- but if it's enabled it means we're
+ * unexpectedly falling back on a conservative scan, so it's
+ * worth yelling a little.
+ */
+ if (gDvm.preciseGc) {
+ LOGVV("PGC: no map for %s.%s\n",
+ method->clazz->descriptor, method->name);
+ }
+ regVector = NULL;
+ }
+
+ if (regVector == NULL) {
+ /* conservative scan */
+ for (i = method->registersSize - 1; i >= 0; i--) {
+ u4 rval = *framePtr++;
+ if (rval != 0 && (rval & 0x3) == 0) {
+ dvmMarkIfObject((Object *)rval);
+ }
+ }
+ } else {
+ /*
+ * Precise scan. v0 is at the lowest address on the
+ * interpreted stack, and is the first bit in the register
+ * vector, so we can walk through the register map and
+ * memory in the same direction.
+ *
+ * A '1' bit indicates a live reference.
+ */
+ u2 bits = 1 << 1;
+ for (i = method->registersSize - 1; i >= 0; i--) {
+ u4 rval = *framePtr++;
+
+ bits >>= 1;
+ if (bits == 1) {
+ /* set bit 9 so we can tell when we're empty */
+ bits = *regVector++ | 0x0100;
+ LOGVV("loaded bits: 0x%02x\n", bits & 0xff);
+ }
+
+ if (rval != 0 && (bits & 0x01) != 0) {
+ /*
+ * Non-null, register marked as live reference. This
+ * should always be a valid object.
+ */
+#if WITH_EXTRA_GC_CHECKS > 0
+ if ((rval & 0x3) != 0 ||
+ !dvmIsValidObject((Object*) rval))
+ {
+ /* this is very bad */
+ LOGE("PGC: invalid ref in reg %d: 0x%08x\n",
+ method->registersSize-1 - i, rval);
+ LOGE("PGC: %s.%s addr 0x%04x\n",
+ method->clazz->descriptor, method->name,
+ saveArea->xtra.currentPc - method->insns);
+ } else
+#endif
+ {
+ dvmMarkObjectNonNull((Object *)rval);
+ }
+ } else {
+ /*
+ * Null or non-reference, do nothing at all.
+ */
+#if WITH_EXTRA_GC_CHECKS > 1
+ if (dvmIsValidObject((Object*) rval)) {
+ /* this is normal, but we feel chatty */
+ LOGD("PGC: ignoring valid ref in reg %d: 0x%08x\n",
+ method->registersSize-1 - i, rval);
+ }
+#endif
+ }
+ }
+ dvmReleaseRegisterMapLine(pMap, regVector);
+ }
+ }
+
+#if WITH_EXTRA_GC_CHECKS > 1
+ first = false;
+#endif
+
+ /* Don't fall into an infinite loop if things get corrupted.
+ */
+ assert((uintptr_t)saveArea->prevFrame > (uintptr_t)framePtr ||
+ saveArea->prevFrame == NULL);
+ framePtr = saveArea->prevFrame;
+ }
+}
+
+static void gcScanReferenceTable(ReferenceTable *refTable)
+{
+ Object **op;
+
+ //TODO: these asserts are overkill; turn them off when things stablize.
+ assert(refTable != NULL);
+ assert(refTable->table != NULL);
+ assert(refTable->nextEntry != NULL);
+ assert((uintptr_t)refTable->nextEntry >= (uintptr_t)refTable->table);
+ assert(refTable->nextEntry - refTable->table <= refTable->maxEntries);
+
+ op = refTable->table;
+ while ((uintptr_t)op < (uintptr_t)refTable->nextEntry) {
+ dvmMarkObjectNonNull(*(op++));
+ }
+}
+
+#ifdef USE_INDIRECT_REF
+static void gcScanIndirectRefTable(IndirectRefTable* pRefTable)
+{
+ Object** op = pRefTable->table;
+ int numEntries = dvmIndirectRefTableEntries(pRefTable);
+ int i;
+
+ for (i = 0; i < numEntries; i++) {
+ Object* obj = *op;
+ if (obj != NULL)
+ dvmMarkObjectNonNull(obj);
+ op++;
+ }
+}
+#endif
+
+/*
+ * Scan a Thread and mark any objects it references.
+ */
+static void gcScanThread(Thread *thread)
+{
+ assert(thread != NULL);
+
+ /*
+ * The target thread must be suspended or in a state where it can't do
+ * any harm (e.g. in Object.wait()). The only exception is the current
+ * thread, which will still be active and in the "running" state.
+ *
+ * It's possible to encounter a false-positive here because a thread
+ * transitioning to running from (say) vmwait or native will briefly
+ * set their status to running before switching to suspended. This
+ * is highly unlikely, but does mean that we don't want to abort if
+ * the situation arises.
+ */
+ if (thread->status == THREAD_RUNNING && thread != dvmThreadSelf()) {
+ Thread* self = dvmThreadSelf();
+ LOGW("threadid=%d: Warning: GC scanning a running thread (%d)\n",
+ self->threadId, thread->threadId);
+ dvmDumpThread(thread, true);
+ LOGW("Found by:\n");
+ dvmDumpThread(self, false);
+
+ /* continue anyway */
+ }
+
+ HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_THREAD_OBJECT, thread->threadId);
+
+ dvmMarkObject(thread->threadObj); // could be NULL, when constructing
+
+ HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_NATIVE_STACK, thread->threadId);
+
+ dvmMarkObject(thread->exception); // usually NULL
+ gcScanReferenceTable(&thread->internalLocalRefTable);
+
+ HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_JNI_LOCAL, thread->threadId);
+
+#ifdef USE_INDIRECT_REF
+ gcScanIndirectRefTable(&thread->jniLocalRefTable);
+#else
+ gcScanReferenceTable(&thread->jniLocalRefTable);
+#endif
+
+ if (thread->jniMonitorRefTable.table != NULL) {
+ HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_JNI_MONITOR, thread->threadId);
+
+ gcScanReferenceTable(&thread->jniMonitorRefTable);
+ }
+
+ HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_JAVA_FRAME, thread->threadId);
+
+ gcScanInterpStackReferences(thread);
+
+ HPROF_CLEAR_GC_SCAN_STATE();
+}
+
+static void gcScanAllThreads()
+{
+ Thread *thread;
+
+ /* Lock the thread list so we can safely use the
+ * next/prev pointers.
+ */
+ dvmLockThreadList(dvmThreadSelf());
+
+ for (thread = gDvm.threadList; thread != NULL;
+ thread = thread->next)
+ {
+ /* We need to scan our own stack, so don't special-case
+ * the current thread.
+ */
+ gcScanThread(thread);
+ }
+
+ dvmUnlockThreadList();
+}
+
+void dvmGcScanRootThreadGroups()
+{
+ /* We scan the VM's list of threads instead of going
+ * through the actual ThreadGroups, but it should be
+ * equivalent.
+ *
+ * This assumes that the ThreadGroup class object is in
+ * the root set, which should always be true; it's
+ * loaded by the built-in class loader, which is part
+ * of the root set.
+ */
+ gcScanAllThreads();
+}
diff --git a/vm/Thread.h b/vm/Thread.h
new file mode 100644
index 0000000..e336dda
--- /dev/null
+++ b/vm/Thread.h
@@ -0,0 +1,563 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * VM thread support.
+ */
+#ifndef _DALVIK_THREAD
+#define _DALVIK_THREAD
+
+#include "jni.h"
+
+#include <errno.h>
+#include <cutils/sched_policy.h>
+
+
+#if defined(CHECK_MUTEX) && !defined(__USE_UNIX98)
+/* glibc lacks this unless you #define __USE_UNIX98 */
+int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type);
+enum { PTHREAD_MUTEX_ERRORCHECK = PTHREAD_MUTEX_ERRORCHECK_NP };
+#endif
+
+#ifdef WITH_MONITOR_TRACKING
+struct LockedObjectData;
+#endif
+
+/*
+ * Current status; these map to JDWP constants, so don't rearrange them.
+ * (If you do alter this, update the strings in dvmDumpThread and the
+ * conversion table in VMThread.java.)
+ *
+ * Note that "suspended" is orthogonal to these values (so says JDWP).
+ */
+typedef enum ThreadStatus {
+ THREAD_UNDEFINED = -1, /* makes enum compatible with int32_t */
+
+ /* these match up with JDWP values */
+ THREAD_ZOMBIE = 0, /* TERMINATED */
+ THREAD_RUNNING = 1, /* RUNNABLE or running now */
+ THREAD_TIMED_WAIT = 2, /* TIMED_WAITING in Object.wait() */
+ THREAD_MONITOR = 3, /* BLOCKED on a monitor */
+ THREAD_WAIT = 4, /* WAITING in Object.wait() */
+ /* non-JDWP states */
+ THREAD_INITIALIZING = 5, /* allocated, not yet running */
+ THREAD_STARTING = 6, /* started, not yet on thread list */
+ THREAD_NATIVE = 7, /* off in a JNI native method */
+ THREAD_VMWAIT = 8, /* waiting on a VM resource */
+ THREAD_SUSPENDED = 9, /* suspended, usually by GC or debugger */
+} ThreadStatus;
+
+/* thread priorities, from java.lang.Thread */
+enum {
+ THREAD_MIN_PRIORITY = 1,
+ THREAD_NORM_PRIORITY = 5,
+ THREAD_MAX_PRIORITY = 10,
+};
+
+
+/* initialization */
+bool dvmThreadStartup(void);
+bool dvmThreadObjStartup(void);
+void dvmThreadShutdown(void);
+void dvmSlayDaemons(void);
+
+
+#define kJniLocalRefMin 32
+#define kJniLocalRefMax 512 /* arbitrary; should be plenty */
+#define kInternalRefDefault 32 /* equally arbitrary */
+#define kInternalRefMax 4096 /* mainly a sanity check */
+
+#define kMinStackSize (512 + STACK_OVERFLOW_RESERVE)
+#define kDefaultStackSize (12*1024) /* three 4K pages */
+#define kMaxStackSize (256*1024 + STACK_OVERFLOW_RESERVE)
+
+/*
+ * Our per-thread data.
+ *
+ * These are allocated on the system heap.
+ */
+typedef struct Thread {
+ /* small unique integer; useful for "thin" locks and debug messages */
+ u4 threadId;
+
+ /*
+ * Thread's current status. Can only be changed by the thread itself
+ * (i.e. don't mess with this from other threads).
+ */
+ volatile ThreadStatus status;
+
+ /*
+ * This is the number of times the thread has been suspended. When the
+ * count drops to zero, the thread resumes.
+ *
+ * "dbgSuspendCount" is the portion of the suspend count that the
+ * debugger is responsible for. This has to be tracked separately so
+ * that we can recover correctly if the debugger abruptly disconnects
+ * (suspendCount -= dbgSuspendCount). The debugger should not be able
+ * to resume GC-suspended threads, because we ignore the debugger while
+ * a GC is in progress.
+ *
+ * Both of these are guarded by gDvm.threadSuspendCountLock.
+ *
+ * (We could store both of these in the same 32-bit, using 16-bit
+ * halves, to make atomic ops possible. In practice, you only need
+ * to read suspendCount, and we need to hold a mutex when making
+ * changes, so there's no need to merge them. Note the non-debug
+ * component will rarely be other than 1 or 0 -- not sure it's even
+ * possible with the way mutexes are currently used.)
+ */
+ int suspendCount;
+ int dbgSuspendCount;
+
+ /* thread handle, as reported by pthread_self() */
+ pthread_t handle;
+
+ /* thread ID, only useful under Linux */
+ pid_t systemTid;
+
+ /* start (high addr) of interp stack (subtract size to get malloc addr) */
+ u1* interpStackStart;
+
+ /* current limit of stack; flexes for StackOverflowError */
+ const u1* interpStackEnd;
+
+ /* interpreter stack size; our stacks are fixed-length */
+ int interpStackSize;
+ bool stackOverflowed;
+
+ /* FP of bottom-most (currently executing) stack frame on interp stack */
+ void* curFrame;
+
+ /* current exception, or NULL if nothing pending */
+ Object* exception;
+
+ /* the java/lang/Thread that we are associated with */
+ Object* threadObj;
+
+ /* the JNIEnv pointer associated with this thread */
+ JNIEnv* jniEnv;
+
+ /* internal reference tracking */
+ ReferenceTable internalLocalRefTable;
+
+#if defined(WITH_JIT)
+ /*
+ * Whether the current top VM frame is in the interpreter or JIT cache:
+ * NULL : in the interpreter
+ * non-NULL: entry address of the JIT'ed code (the actual value doesn't
+ * matter)
+ */
+ void* inJitCodeCache;
+#if defined(WITH_SELF_VERIFICATION)
+ /* Buffer for register state during self verification */
+ struct ShadowSpace* shadowSpace;
+#endif
+#endif
+
+ /* JNI local reference tracking */
+#ifdef USE_INDIRECT_REF
+ IndirectRefTable jniLocalRefTable;
+#else
+ ReferenceTable jniLocalRefTable;
+#endif
+
+ /* JNI native monitor reference tracking (initialized on first use) */
+ ReferenceTable jniMonitorRefTable;
+
+ /* hack to make JNI_OnLoad work right */
+ Object* classLoaderOverride;
+
+ /* mutex to guard the interrupted and the waitMonitor members */
+ pthread_mutex_t waitMutex;
+
+ /* pointer to the monitor lock we're currently waiting on */
+ /* guarded by waitMutex */
+ /* TODO: consider changing this to Object* for better JDWP interaction */
+ Monitor* waitMonitor;
+
+ /* thread "interrupted" status; stays raised until queried or thrown */
+ /* guarded by waitMutex */
+ bool interrupted;
+
+ /* links to the next thread in the wait set this thread is part of */
+ struct Thread* waitNext;
+
+ /* object to sleep on while we are waiting for a monitor */
+ pthread_cond_t waitCond;
+
+ /*
+ * Set to true when the thread is in the process of throwing an
+ * OutOfMemoryError.
+ */
+ bool throwingOOME;
+
+ /* links to rest of thread list; grab global lock before traversing */
+ struct Thread* prev;
+ struct Thread* next;
+
+ /* used by threadExitCheck when a thread exits without detaching */
+ int threadExitCheckCount;
+
+ /* JDWP invoke-during-breakpoint support */
+ DebugInvokeReq invokeReq;
+
+#ifdef WITH_MONITOR_TRACKING
+ /* objects locked by this thread; most recent is at head of list */
+ struct LockedObjectData* pLockedObjects;
+#endif
+
+#ifdef WITH_ALLOC_LIMITS
+ /* allocation limit, for Debug.setAllocationLimit() regression testing */
+ int allocLimit;
+#endif
+
+ /* base time for per-thread CPU timing (used by method profiling) */
+ bool cpuClockBaseSet;
+ u8 cpuClockBase;
+
+ /* memory allocation profiling state */
+ AllocProfState allocProf;
+
+#ifdef WITH_JNI_STACK_CHECK
+ u4 stackCrc;
+#endif
+
+#if WITH_EXTRA_GC_CHECKS > 1
+ /* PC, saved on every instruction; redundant with StackSaveArea */
+ const u2* currentPc2;
+#endif
+} Thread;
+
+/* start point for an internal thread; mimics pthread args */
+typedef void* (*InternalThreadStart)(void* arg);
+
+/* args for internal thread creation */
+typedef struct InternalStartArgs {
+ /* inputs */
+ InternalThreadStart func;
+ void* funcArg;
+ char* name;
+ Object* group;
+ bool isDaemon;
+ /* result */
+ volatile Thread** pThread;
+ volatile int* pCreateStatus;
+} InternalStartArgs;
+
+/* finish init */
+bool dvmPrepMainForJni(JNIEnv* pEnv);
+bool dvmPrepMainThread(void);
+
+/* utility function to get the tid */
+pid_t dvmGetSysThreadId(void);
+
+/*
+ * Get our Thread* from TLS.
+ *
+ * Returns NULL if this isn't a thread that the VM is aware of.
+ */
+Thread* dvmThreadSelf(void);
+
+/* grab the thread list global lock */
+void dvmLockThreadList(Thread* self);
+/* release the thread list global lock */
+void dvmUnlockThreadList(void);
+
+/*
+ * Thread suspend/resume, used by the GC and debugger.
+ */
+typedef enum SuspendCause {
+ SUSPEND_NOT = 0,
+ SUSPEND_FOR_GC,
+ SUSPEND_FOR_DEBUG,
+ SUSPEND_FOR_DEBUG_EVENT,
+ SUSPEND_FOR_STACK_DUMP,
+ SUSPEND_FOR_DEX_OPT,
+ SUSPEND_FOR_VERIFY,
+#if defined(WITH_JIT)
+ SUSPEND_FOR_TBL_RESIZE, // jit-table resize
+ SUSPEND_FOR_IC_PATCH, // polymorphic callsite inline-cache patch
+ SUSPEND_FOR_CC_RESET, // code-cache reset
+ SUSPEND_FOR_REFRESH, // Reload data cached in interpState
+#endif
+} SuspendCause;
+void dvmSuspendThread(Thread* thread);
+void dvmSuspendSelf(bool jdwpActivity);
+void dvmResumeThread(Thread* thread);
+void dvmSuspendAllThreads(SuspendCause why);
+void dvmResumeAllThreads(SuspendCause why);
+void dvmUndoDebuggerSuspensions(void);
+
+/*
+ * Check suspend state. Grab threadListLock before calling.
+ */
+bool dvmIsSuspended(const Thread* thread);
+
+/*
+ * Wait until a thread has suspended. (Used by debugger support.)
+ */
+void dvmWaitForSuspend(Thread* thread);
+
+/*
+ * Check to see if we should be suspended now. If so, suspend ourselves
+ * by sleeping on a condition variable.
+ */
+bool dvmCheckSuspendPending(Thread* self);
+
+/*
+ * Fast test for use in the interpreter. Returns "true" if our suspend
+ * count is nonzero.
+ */
+INLINE bool dvmCheckSuspendQuick(Thread* self) {
+ return (self->suspendCount != 0);
+}
+
+/*
+ * Used when changing thread state. Threads may only change their own.
+ * The "self" argument, which may be NULL, is accepted as an optimization.
+ *
+ * If you're calling this before waiting on a resource (e.g. THREAD_WAIT
+ * or THREAD_MONITOR), do so in the same function as the wait -- this records
+ * the current stack depth for the GC.
+ *
+ * If you're changing to THREAD_RUNNING, this will check for suspension.
+ *
+ * Returns the old status.
+ */
+ThreadStatus dvmChangeStatus(Thread* self, ThreadStatus newStatus);
+
+/*
+ * Initialize a mutex.
+ */
+INLINE void dvmInitMutex(pthread_mutex_t* pMutex)
+{
+#ifdef CHECK_MUTEX
+ pthread_mutexattr_t attr;
+ int cc;
+
+ pthread_mutexattr_init(&attr);
+ cc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK_NP);
+ assert(cc == 0);
+ pthread_mutex_init(pMutex, &attr);
+ pthread_mutexattr_destroy(&attr);
+#else
+ pthread_mutex_init(pMutex, NULL); // default=PTHREAD_MUTEX_FAST_NP
+#endif
+}
+
+/*
+ * Grab a plain mutex.
+ */
+INLINE void dvmLockMutex(pthread_mutex_t* pMutex)
+{
+ int cc __attribute__ ((__unused__)) = pthread_mutex_lock(pMutex);
+ assert(cc == 0);
+}
+
+/*
+ * Try grabbing a plain mutex. Returns 0 if successful.
+ */
+INLINE int dvmTryLockMutex(pthread_mutex_t* pMutex)
+{
+ int cc = pthread_mutex_trylock(pMutex);
+ assert(cc == 0 || cc == EBUSY);
+ return cc;
+}
+
+/*
+ * Unlock pthread mutex.
+ */
+INLINE void dvmUnlockMutex(pthread_mutex_t* pMutex)
+{
+ int cc __attribute__ ((__unused__)) = pthread_mutex_unlock(pMutex);
+ assert(cc == 0);
+}
+
+/*
+ * Destroy a mutex.
+ */
+INLINE void dvmDestroyMutex(pthread_mutex_t* pMutex)
+{
+ int cc __attribute__ ((__unused__)) = pthread_mutex_destroy(pMutex);
+ assert(cc == 0);
+}
+
+INLINE void dvmBroadcastCond(pthread_cond_t* pCond)
+{
+ int cc __attribute__ ((__unused__)) = pthread_cond_broadcast(pCond);
+ assert(cc == 0);
+}
+
+INLINE void dvmSignalCond(pthread_cond_t* pCond)
+{
+ int cc __attribute__ ((__unused__)) = pthread_cond_signal(pCond);
+ assert(cc == 0);
+}
+
+INLINE void dvmWaitCond(pthread_cond_t* pCond, pthread_mutex_t* pMutex)
+{
+ int cc __attribute__ ((__unused__)) = pthread_cond_wait(pCond, pMutex);
+ assert(cc == 0);
+}
+
+/*
+ * Create a thread as a result of java.lang.Thread.start().
+ */
+bool dvmCreateInterpThread(Object* threadObj, int reqStackSize);
+
+/*
+ * Create a thread internal to the VM. It's visible to interpreted code,
+ * but found in the "system" thread group rather than "main".
+ */
+bool dvmCreateInternalThread(pthread_t* pHandle, const char* name,
+ InternalThreadStart func, void* funcArg);
+
+/*
+ * Attach or detach the current thread from the VM.
+ */
+bool dvmAttachCurrentThread(const JavaVMAttachArgs* pArgs, bool isDaemon);
+void dvmDetachCurrentThread(void);
+
+/*
+ * Get the "main" or "system" thread group.
+ */
+Object* dvmGetMainThreadGroup(void);
+Object* dvmGetSystemThreadGroup(void);
+
+/*
+ * Given a java/lang/VMThread object, return our Thread.
+ */
+Thread* dvmGetThreadFromThreadObject(Object* vmThreadObj);
+
+/*
+ * Given a pthread handle, return the associated Thread*.
+ * Caller must hold the thread list lock.
+ *
+ * Returns NULL if the thread was not found.
+ */
+Thread* dvmGetThreadByHandle(pthread_t handle);
+
+/*
+ * Given a thread ID, return the associated Thread*.
+ * Caller must hold the thread list lock.
+ *
+ * Returns NULL if the thread was not found.
+ */
+Thread* dvmGetThreadByThreadId(u4 threadId);
+
+/*
+ * Sleep in a thread. Returns when the sleep timer returns or the thread
+ * is interrupted.
+ */
+void dvmThreadSleep(u8 msec, u4 nsec);
+
+/*
+ * Get the name of a thread. (For safety, hold the thread list lock.)
+ */
+char* dvmGetThreadName(Thread* thread);
+
+/*
+ * Convert ThreadStatus to a string.
+ */
+const char* dvmGetThreadStatusStr(ThreadStatus status);
+
+/*
+ * Return true if a thread is on the internal list. If it is, the
+ * thread is part of the GC's root set.
+ */
+bool dvmIsOnThreadList(const Thread* thread);
+
+/*
+ * Get/set the JNIEnv field.
+ */
+INLINE JNIEnv* dvmGetThreadJNIEnv(Thread* self) { return self->jniEnv; }
+INLINE void dvmSetThreadJNIEnv(Thread* self, JNIEnv* env) { self->jniEnv = env;}
+
+/*
+ * Update the priority value of the underlying pthread.
+ */
+void dvmChangeThreadPriority(Thread* thread, int newPriority);
+
+/* "change flags" values for raise/reset thread priority calls */
+#define kChangedPriority 0x01
+#define kChangedPolicy 0x02
+
+/*
+ * If necessary, raise the thread's priority to nice=0 cgroup=fg.
+ *
+ * Returns bit flags indicating changes made (zero if nothing was done).
+ */
+int dvmRaiseThreadPriorityIfNeeded(Thread* thread, int* pSavedThreadPrio,
+ SchedPolicy* pSavedThreadPolicy);
+
+/*
+ * Drop the thread priority to what it was before an earlier call to
+ * dvmRaiseThreadPriorityIfNeeded().
+ */
+void dvmResetThreadPriority(Thread* thread, int changeFlags,
+ int savedThreadPrio, SchedPolicy savedThreadPolicy);
+
+/*
+ * Debug: dump information about a single thread.
+ */
+void dvmDumpThread(Thread* thread, bool isRunning);
+void dvmDumpThreadEx(const DebugOutputTarget* target, Thread* thread,
+ bool isRunning);
+
+/*
+ * Debug: dump information about all threads.
+ */
+void dvmDumpAllThreads(bool grabLock);
+void dvmDumpAllThreadsEx(const DebugOutputTarget* target, bool grabLock);
+
+/*
+ * Debug: kill a thread to get a debuggerd stack trace. Leaves the VM
+ * in an uncertain state.
+ */
+void dvmNukeThread(Thread* thread);
+
+#ifdef WITH_MONITOR_TRACKING
+/*
+ * Track locks held by the current thread, along with the stack trace at
+ * the point the lock was acquired.
+ *
+ * At any given time the number of locks held across the VM should be
+ * fairly small, so there's no reason not to generate and store the entire
+ * stack trace.
+ */
+typedef struct LockedObjectData {
+ /* the locked object */
+ struct Object* obj;
+
+ /* number of times it has been locked recursively (zero-based ref count) */
+ int recursionCount;
+
+ /* stack trace at point of initial acquire */
+ u4 stackDepth;
+ int* rawStackTrace;
+
+ struct LockedObjectData* next;
+} LockedObjectData;
+
+/*
+ * Add/remove/find objects from the thread's monitor list.
+ */
+void dvmAddToMonitorList(Thread* self, Object* obj, bool withTrace);
+void dvmRemoveFromMonitorList(Thread* self, Object* obj);
+LockedObjectData* dvmFindInMonitorList(const Thread* self, const Object* obj);
+#endif
+
+#endif /*_DALVIK_THREAD*/
diff --git a/vm/UtfString.c b/vm/UtfString.c
new file mode 100644
index 0000000..f560dac
--- /dev/null
+++ b/vm/UtfString.c
@@ -0,0 +1,523 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * UTF-8 and Unicode string manipulation, plus java/lang/String convenience
+ * functions.
+ *
+ * In most cases we populate the fields in the String object directly,
+ * rather than going through an instance field lookup.
+ */
+#include "Dalvik.h"
+#include <stdlib.h>
+
+/*
+ * Initialize string globals.
+ *
+ * This isn't part of the VM init sequence because it's hard to get the
+ * timing right -- we need it to happen after java/lang/String has been
+ * loaded, but before anybody wants to use a string. It's easiest to
+ * just initialize it on first use.
+ *
+ * In some unusual circumstances (e.g. trying to throw an exception because
+ * String implements java/lang/CharSequence, but CharSequence doesn't exist)
+ * we can try to create an exception string internally before anything has
+ * really tried to use String. In that case we basically self-destruct.
+ *
+ * We're expecting to be essentially single-threaded at this point.
+ * We employ atomics to ensure everything is observed correctly, and also
+ * to guarantee that we do detect a problem if our assumption is wrong.
+ */
+static bool stringStartup()
+{
+ if (gDvm.javaLangStringReady < 0) {
+ LOGE("ERROR: reentrant string initialization\n");
+ assert(false);
+ return false;
+ }
+
+ if (android_atomic_acquire_cas(0, -1, &gDvm.javaLangStringReady) != 0) {
+ LOGE("ERROR: initial string-ready state not 0 (%d)\n",
+ gDvm.javaLangStringReady);
+ return false;
+ }
+
+ if (gDvm.classJavaLangString == NULL)
+ gDvm.classJavaLangString =
+ dvmFindSystemClassNoInit("Ljava/lang/String;");
+
+ gDvm.offJavaLangString_value =
+ dvmFindFieldOffset(gDvm.classJavaLangString, "value", "[C");
+ gDvm.offJavaLangString_count =
+ dvmFindFieldOffset(gDvm.classJavaLangString, "count", "I");
+ gDvm.offJavaLangString_offset =
+ dvmFindFieldOffset(gDvm.classJavaLangString, "offset", "I");
+ gDvm.offJavaLangString_hashCode =
+ dvmFindFieldOffset(gDvm.classJavaLangString, "hashCode", "I");
+
+ if (gDvm.offJavaLangString_value < 0 ||
+ gDvm.offJavaLangString_count < 0 ||
+ gDvm.offJavaLangString_offset < 0 ||
+ gDvm.offJavaLangString_hashCode < 0)
+ {
+ LOGE("VM-required field missing from java/lang/String\n");
+ return false;
+ }
+
+ bool badValue = false;
+ if (gDvm.offJavaLangString_value != STRING_FIELDOFF_VALUE) {
+ LOGE("InlineNative: String.value offset = %d, expected %d\n",
+ gDvm.offJavaLangString_value, STRING_FIELDOFF_VALUE);
+ badValue = true;
+ }
+ if (gDvm.offJavaLangString_count != STRING_FIELDOFF_COUNT) {
+ LOGE("InlineNative: String.count offset = %d, expected %d\n",
+ gDvm.offJavaLangString_count, STRING_FIELDOFF_COUNT);
+ badValue = true;
+ }
+ if (gDvm.offJavaLangString_offset != STRING_FIELDOFF_OFFSET) {
+ LOGE("InlineNative: String.offset offset = %d, expected %d\n",
+ gDvm.offJavaLangString_offset, STRING_FIELDOFF_OFFSET);
+ badValue = true;
+ }
+ if (gDvm.offJavaLangString_hashCode != STRING_FIELDOFF_HASHCODE) {
+ LOGE("InlineNative: String.hashCode offset = %d, expected %d\n",
+ gDvm.offJavaLangString_hashCode, STRING_FIELDOFF_HASHCODE);
+ badValue = true;
+ }
+ if (badValue)
+ return false;
+
+ android_atomic_release_store(1, &gDvm.javaLangStringReady);
+
+ return true;
+}
+
+/*
+ * Discard heap-allocated storage.
+ */
+void dvmStringShutdown()
+{
+ // currently unused
+}
+
+/*
+ * Compute a hash code on a UTF-8 string, for use with internal hash tables.
+ *
+ * This may or may not yield the same results as the java/lang/String
+ * computeHashCode() function. (To make sure this doesn't get abused,
+ * I'm initializing the hash code to 1 so they *don't* match up.)
+ *
+ * It would be more correct to invoke dexGetUtf16FromUtf8() here and compute
+ * the hash with the result. That way, if something encoded the same
+ * character in two different ways, the hash value would be the same. For
+ * our purposes that isn't necessary.
+ */
+u4 dvmComputeUtf8Hash(const char* utf8Str)
+{
+ u4 hash = 1;
+
+ while (*utf8Str != '\0')
+ hash = hash * 31 + *utf8Str++;
+
+ return hash;
+}
+
+/*
+ * Like "strlen", but for strings encoded with "modified" UTF-8.
+ *
+ * The value returned is the number of characters, which may or may not
+ * be the same as the number of bytes.
+ *
+ * (If this needs optimizing, try: mask against 0xa0, shift right 5,
+ * get increment {1-3} from table of 8 values.)
+ */
+int dvmUtf8Len(const char* utf8Str)
+{
+ int ic, len = 0;
+
+ while ((ic = *utf8Str++) != '\0') {
+ len++;
+ if ((ic & 0x80) != 0) {
+ /* two- or three-byte encoding */
+ utf8Str++;
+ if ((ic & 0x20) != 0) {
+ /* three-byte encoding */
+ utf8Str++;
+ }
+ }
+ }
+
+ return len;
+}
+
+/*
+ * Convert a "modified" UTF-8 string to UTF-16.
+ */
+void dvmConvertUtf8ToUtf16(u2* utf16Str, const char* utf8Str)
+{
+ while (*utf8Str != '\0')
+ *utf16Str++ = dexGetUtf16FromUtf8(&utf8Str);
+}
+
+/*
+ * Given a UTF-16 string, compute the length of the corresponding UTF-8
+ * string in bytes.
+ */
+static int utf16_utf8ByteLen(const u2* utf16Str, int len)
+{
+ int utf8Len = 0;
+
+ while (len--) {
+ unsigned int uic = *utf16Str++;
+
+ /*
+ * The most common case is (uic > 0 && uic <= 0x7f).
+ */
+ if (uic == 0 || uic > 0x7f) {
+ if (uic > 0x07ff)
+ utf8Len += 3;
+ else /*(uic > 0x7f || uic == 0) */
+ utf8Len += 2;
+ } else
+ utf8Len++;
+ }
+ return utf8Len;
+}
+
+/*
+ * Convert a UTF-16 string to UTF-8.
+ *
+ * Make sure you allocate "utf8Str" with the result of utf16_utf8ByteLen(),
+ * not just "len".
+ */
+static void convertUtf16ToUtf8(char* utf8Str, const u2* utf16Str, int len)
+{
+ assert(len >= 0);
+
+ while (len--) {
+ unsigned int uic = *utf16Str++;
+
+ /*
+ * The most common case is (uic > 0 && uic <= 0x7f).
+ */
+ if (uic == 0 || uic > 0x7f) {
+ if (uic > 0x07ff) {
+ *utf8Str++ = (uic >> 12) | 0xe0;
+ *utf8Str++ = ((uic >> 6) & 0x3f) | 0x80;
+ *utf8Str++ = (uic & 0x3f) | 0x80;
+ } else /*(uic > 0x7f || uic == 0)*/ {
+ *utf8Str++ = (uic >> 6) | 0xc0;
+ *utf8Str++ = (uic & 0x3f) | 0x80;
+ }
+ } else {
+ *utf8Str++ = uic;
+ }
+ }
+
+ *utf8Str = '\0';
+}
+
+/*
+ * Use the java/lang/String.computeHashCode() algorithm.
+ */
+static inline u4 dvmComputeUtf16Hash(const u2* utf16Str, int len)
+{
+ u4 hash = 0;
+
+ while (len--)
+ hash = hash * 31 + *utf16Str++;
+
+ return hash;
+}
+u4 dvmComputeStringHash(const StringObject* strObj) {
+ ArrayObject* chars = (ArrayObject*) dvmGetFieldObject((Object*) strObj,
+ STRING_FIELDOFF_VALUE);
+ int offset, len;
+
+ len = dvmGetFieldInt((Object*) strObj, STRING_FIELDOFF_COUNT);
+ offset = dvmGetFieldInt((Object*) strObj, STRING_FIELDOFF_OFFSET);
+
+ return dvmComputeUtf16Hash((u2*) chars->contents + offset, len);
+}
+
+/*
+ * Create a new java/lang/String object, using the string data in "utf8Str".
+ *
+ * The caller must call dvmReleaseTrackedAlloc() on the return value.
+ *
+ * Returns NULL and throws an exception on failure.
+ */
+StringObject* dvmCreateStringFromCstr(const char* utf8Str)
+{
+ assert(utf8Str != NULL);
+ return dvmCreateStringFromCstrAndLength(utf8Str, dvmUtf8Len(utf8Str));
+}
+
+/*
+ * Create a java/lang/String from a C string, given its UTF-16 length
+ * (number of UTF-16 code points).
+ *
+ * The caller must call dvmReleaseTrackedAlloc() on the return value.
+ *
+ * Returns NULL and throws an exception on failure.
+ */
+StringObject* dvmCreateStringFromCstrAndLength(const char* utf8Str,
+ u4 utf16Length)
+{
+ StringObject* newObj;
+ ArrayObject* chars;
+ u4 hashCode = 0;
+
+ //LOGV("Creating String from '%s'\n", utf8Str);
+ assert(utf8Str != NULL);
+
+ if (gDvm.javaLangStringReady <= 0) {
+ if (!stringStartup())
+ return NULL;
+ }
+
+ /* init before alloc */
+ if (!dvmIsClassInitialized(gDvm.classJavaLangString) &&
+ !dvmInitClass(gDvm.classJavaLangString))
+ {
+ return NULL;
+ }
+
+ newObj = (StringObject*) dvmAllocObject(gDvm.classJavaLangString,
+ ALLOC_DEFAULT);
+ if (newObj == NULL)
+ return NULL;
+
+ chars = dvmAllocPrimitiveArray('C', utf16Length, ALLOC_DEFAULT);
+ if (chars == NULL) {
+ dvmReleaseTrackedAlloc((Object*) newObj, NULL);
+ return NULL;
+ }
+ dvmConvertUtf8ToUtf16((u2*)chars->contents, utf8Str);
+ hashCode = dvmComputeUtf16Hash((u2*) chars->contents, utf16Length);
+
+ dvmSetFieldObject((Object*)newObj, STRING_FIELDOFF_VALUE,
+ (Object*)chars);
+ dvmReleaseTrackedAlloc((Object*) chars, NULL);
+ dvmSetFieldInt((Object*)newObj, STRING_FIELDOFF_COUNT, utf16Length);
+ dvmSetFieldInt((Object*)newObj, STRING_FIELDOFF_HASHCODE, hashCode);
+ /* leave offset set to zero */
+
+ /* debugging stuff */
+ //dvmDumpObject((Object*)newObj);
+ //printHexDumpEx(ANDROID_LOG_DEBUG, chars->contents, utf16Length * 2,
+ // kHexDumpMem);
+
+ /* caller may need to dvmReleaseTrackedAlloc(newObj) */
+ return newObj;
+}
+
+/*
+ * Create a new java/lang/String object, using the Unicode data.
+ */
+StringObject* dvmCreateStringFromUnicode(const u2* unichars, int len)
+{
+ StringObject* newObj;
+ ArrayObject* chars;
+ u4 hashCode = 0;
+
+ /* we allow a null pointer if the length is zero */
+ assert(len == 0 || unichars != NULL);
+
+ if (gDvm.javaLangStringReady <= 0) {
+ if (!stringStartup())
+ return NULL;
+ }
+
+ /* init before alloc */
+ if (!dvmIsClassInitialized(gDvm.classJavaLangString) &&
+ !dvmInitClass(gDvm.classJavaLangString))
+ {
+ return NULL;
+ }
+
+ newObj = (StringObject*) dvmAllocObject(gDvm.classJavaLangString,
+ ALLOC_DEFAULT);
+ if (newObj == NULL)
+ return NULL;
+
+ chars = dvmAllocPrimitiveArray('C', len, ALLOC_DEFAULT);
+ if (chars == NULL) {
+ dvmReleaseTrackedAlloc((Object*) newObj, NULL);
+ return NULL;
+ }
+ if (len > 0)
+ memcpy(chars->contents, unichars, len * sizeof(u2));
+ hashCode = dvmComputeUtf16Hash((u2*) chars->contents, len);
+
+ dvmSetFieldObject((Object*)newObj, STRING_FIELDOFF_VALUE,
+ (Object*)chars);
+ dvmReleaseTrackedAlloc((Object*) chars, NULL);
+ dvmSetFieldInt((Object*)newObj, STRING_FIELDOFF_COUNT, len);
+ dvmSetFieldInt((Object*)newObj, STRING_FIELDOFF_HASHCODE, hashCode);
+ /* leave offset set to zero */
+
+ /* debugging stuff */
+ //dvmDumpObject((Object*)newObj);
+ //printHexDumpEx(ANDROID_LOG_DEBUG, chars->contents, len*2, kHexDumpMem);
+
+ /* caller must dvmReleaseTrackedAlloc(newObj) */
+ return newObj;
+}
+
+/*
+ * Create a new C string from a java/lang/String object.
+ *
+ * Returns NULL if the object is NULL.
+ */
+char* dvmCreateCstrFromString(StringObject* jstr)
+{
+ char* newStr;
+ ArrayObject* chars;
+ int len, byteLen, offset;
+ const u2* data;
+
+ assert(gDvm.javaLangStringReady > 0);
+
+ if (jstr == NULL)
+ return NULL;
+
+ len = dvmGetFieldInt((Object*) jstr, STRING_FIELDOFF_COUNT);
+ offset = dvmGetFieldInt((Object*) jstr, STRING_FIELDOFF_OFFSET);
+ chars = (ArrayObject*) dvmGetFieldObject((Object*) jstr,
+ STRING_FIELDOFF_VALUE);
+ data = (const u2*) chars->contents + offset;
+ assert(offset + len <= (int) chars->length);
+
+ byteLen = utf16_utf8ByteLen(data, len);
+ newStr = (char*) malloc(byteLen+1);
+ if (newStr == NULL)
+ return NULL;
+ convertUtf16ToUtf8(newStr, data, len);
+
+ return newStr;
+}
+
+/*
+ * Create a UTF-8 C string from a region of a java/lang/String. (Used by
+ * the JNI GetStringUTFRegion call.)
+ */
+void dvmCreateCstrFromStringRegion(StringObject* jstr, int start, int len,
+ char* buf)
+{
+ const u2* data;
+
+ data = dvmStringChars(jstr) + start;
+ convertUtf16ToUtf8(buf, data, len);
+}
+
+/*
+ * Compute the length, in modified UTF-8, of a java/lang/String object.
+ *
+ * Does not include the terminating null byte.
+ */
+int dvmStringUtf8ByteLen(StringObject* jstr)
+{
+ ArrayObject* chars;
+ int len, offset;
+ const u2* data;
+
+ assert(gDvm.javaLangStringReady > 0);
+
+ if (jstr == NULL)
+ return 0; // should we throw something? assert?
+
+ len = dvmGetFieldInt((Object*) jstr, STRING_FIELDOFF_COUNT);
+ offset = dvmGetFieldInt((Object*) jstr, STRING_FIELDOFF_OFFSET);
+ chars = (ArrayObject*) dvmGetFieldObject((Object*) jstr,
+ STRING_FIELDOFF_VALUE);
+ data = (const u2*) chars->contents + offset;
+ assert(offset + len <= (int) chars->length);
+
+ return utf16_utf8ByteLen(data, len);
+}
+
+/*
+ * Get the string's length.
+ */
+int dvmStringLen(StringObject* jstr)
+{
+ return dvmGetFieldInt((Object*) jstr, STRING_FIELDOFF_COUNT);
+}
+
+/*
+ * Get the char[] object from the String.
+ */
+ArrayObject* dvmStringCharArray(StringObject* jstr)
+{
+ return (ArrayObject*) dvmGetFieldObject((Object*) jstr,
+ STRING_FIELDOFF_VALUE);
+}
+
+/*
+ * Get the string's data.
+ */
+const u2* dvmStringChars(StringObject* jstr)
+{
+ ArrayObject* chars;
+ int offset;
+
+ offset = dvmGetFieldInt((Object*) jstr, STRING_FIELDOFF_OFFSET);
+ chars = (ArrayObject*) dvmGetFieldObject((Object*) jstr,
+ STRING_FIELDOFF_VALUE);
+ return (const u2*) chars->contents + offset;
+}
+
+
+/*
+ * Compare two String objects.
+ *
+ * This is a dvmHashTableLookup() callback. The function has already
+ * compared their hash values; we need to do a full compare to ensure
+ * that the strings really match.
+ */
+int dvmHashcmpStrings(const void* vstrObj1, const void* vstrObj2)
+{
+ const StringObject* strObj1 = (const StringObject*) vstrObj1;
+ const StringObject* strObj2 = (const StringObject*) vstrObj2;
+ ArrayObject* chars1;
+ ArrayObject* chars2;
+ int len1, len2, offset1, offset2;
+
+ assert(gDvm.javaLangStringReady > 0);
+
+ /* get offset and length into char array; all values are in 16-bit units */
+ len1 = dvmGetFieldInt((Object*) strObj1, STRING_FIELDOFF_COUNT);
+ offset1 = dvmGetFieldInt((Object*) strObj1, STRING_FIELDOFF_OFFSET);
+ len2 = dvmGetFieldInt((Object*) strObj2, STRING_FIELDOFF_COUNT);
+ offset2 = dvmGetFieldInt((Object*) strObj2, STRING_FIELDOFF_OFFSET);
+ if (len1 != len2)
+ return len1 - len2;
+
+ chars1 = (ArrayObject*) dvmGetFieldObject((Object*) strObj1,
+ STRING_FIELDOFF_VALUE);
+ chars2 = (ArrayObject*) dvmGetFieldObject((Object*) strObj2,
+ STRING_FIELDOFF_VALUE);
+
+ /* damage here actually indicates a broken java/lang/String */
+ assert(offset1 + len1 <= (int) chars1->length);
+ assert(offset2 + len2 <= (int) chars2->length);
+
+ return memcmp((const u2*) chars1->contents + offset1,
+ (const u2*) chars2->contents + offset2,
+ len1 * sizeof(u2));
+}
diff --git a/vm/UtfString.h b/vm/UtfString.h
new file mode 100644
index 0000000..b291f5a
--- /dev/null
+++ b/vm/UtfString.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * UTF-8 and Unicode string manipulation functions, plus convenience
+ * functions for working with java/lang/String.
+ */
+#ifndef _DALVIK_STRING
+#define _DALVIK_STRING
+
+/*
+ * (This is private to UtfString.c, but we cheat a bit and also use it
+ * for InlineNative.c. Not really worth creating a separate header.)
+ *
+ * We can avoid poking around in gDvm by hard-coding the expected values of
+ * the String field offsets. This will be annoying if String is in flux
+ * or the VM field layout is changing, so we use defines here to make it
+ * easy to switch back to the gDvm version.
+ *
+ * The values are checked for correctness during startup.
+ */
+//#define USE_GLOBAL_STRING_DEFS
+#ifdef USE_GLOBAL_STRING_DEFS
+# define STRING_FIELDOFF_VALUE gDvm.offJavaLangString_value
+# define STRING_FIELDOFF_OFFSET gDvm.offJavaLangString_offset
+# define STRING_FIELDOFF_COUNT gDvm.offJavaLangString_count
+# define STRING_FIELDOFF_HASHCODE gDvm.offJavaLangString_hashCode
+#else
+# define STRING_FIELDOFF_VALUE 8
+# define STRING_FIELDOFF_HASHCODE 12
+# define STRING_FIELDOFF_OFFSET 16
+# define STRING_FIELDOFF_COUNT 20
+#endif
+
+/*
+ * Hash function for modified UTF-8 strings.
+ */
+u4 dvmComputeUtf8Hash(const char* str);
+
+/*
+ * Hash function for string objects.
+ */
+u4 dvmComputeStringHash(const StringObject* strObj);
+
+/*
+ * Create a java/lang/String from a C string.
+ *
+ * The caller must call dvmReleaseTrackedAlloc() on the return value.
+ *
+ * Returns NULL and throws an exception on failure.
+ */
+StringObject* dvmCreateStringFromCstr(const char* utf8Str);
+
+/*
+ * Create a java/lang/String from a C string, given its UTF-16 length
+ * (number of UTF-16 code points).
+ *
+ * The caller must call dvmReleaseTrackedAlloc() on the return value.
+ *
+ * Returns NULL and throws an exception on failure.
+ */
+StringObject* dvmCreateStringFromCstrAndLength(const char* utf8Str,
+ u4 utf16Length);
+
+/*
+ * Compute the number of characters in a "modified UTF-8" string. This will
+ * match the result from strlen() so long as there are no multi-byte chars.
+ */
+int dvmUtf8Len(const char* utf8Str);
+
+/*
+ * Convert a UTF-8 string to UTF-16. "utf16Str" must have enough room
+ * to hold the output.
+ */
+void dvmConvertUtf8ToUtf16(u2* utf16Str, const char* utf8Str);
+
+/*
+ * Create a java/lang/String from a Unicode string.
+ *
+ * The caller must call dvmReleaseTrackedAlloc() on the return value.
+ */
+StringObject* dvmCreateStringFromUnicode(const u2* unichars, int len);
+
+/*
+ * Create a UTF-8 C string from a java/lang/String. Caller must free
+ * the result.
+ *
+ * Returns NULL if "jstr" is NULL.
+ */
+char* dvmCreateCstrFromString(StringObject* jstr);
+
+/*
+ * Create a UTF-8 C string from a region of a java/lang/String. (Used by
+ * the JNI GetStringUTFRegion call.)
+ */
+void dvmCreateCstrFromStringRegion(StringObject* jstr, int start, int len,
+ char* buf);
+
+/*
+ * Compute the length in bytes of the modified UTF-8 representation of a
+ * string.
+ */
+int dvmStringUtf8ByteLen(StringObject* jstr);
+
+/*
+ * Get the length in Unicode characters of a string.
+ */
+int dvmStringLen(StringObject* jstr);
+
+/*
+ * Get the char[] object from the String.
+ */
+ArrayObject* dvmStringCharArray(StringObject* jstr);
+
+/*
+ * Get a pointer to the Unicode data.
+ */
+const u2* dvmStringChars(StringObject* jstr);
+
+/*
+ * Compare two string objects. (This is a dvmHashTableLookup() callback.)
+ */
+int dvmHashcmpStrings(const void* vstrObj1, const void* vstrObj2);
+
+#endif /*_DALVIK_STRING*/
diff --git a/vm/alloc/Alloc.c b/vm/alloc/Alloc.c
new file mode 100644
index 0000000..7b56a35
--- /dev/null
+++ b/vm/alloc/Alloc.c
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Garbage-collecting memory allocator.
+ */
+#include "Dalvik.h"
+#include "alloc/Heap.h"
+#include "alloc/HeapInternal.h"
+#include "alloc/HeapSource.h"
+
+#if WITH_HPROF && WITH_HPROF_STACK
+#include "hprof/Hprof.h"
+#endif
+
+
+/*
+ * Initialize the GC universe.
+ *
+ * We're currently using a memory-mapped arena to keep things off of the
+ * main heap. This needs to be replaced with something real.
+ */
+bool dvmGcStartup(void)
+{
+ dvmInitMutex(&gDvm.gcHeapLock);
+
+ return dvmHeapStartup();
+}
+
+/*
+ * Post-zygote heap initialization, including starting
+ * the HeapWorker thread.
+ */
+bool dvmGcStartupAfterZygote(void)
+{
+ if (!dvmHeapWorkerStartup()) {
+ return false;
+ }
+ return dvmHeapStartupAfterZygote();
+}
+
+/*
+ * Shutdown the threads internal to the garbage collector.
+ */
+void dvmGcThreadShutdown(void)
+{
+ dvmHeapWorkerShutdown();
+ dvmHeapThreadShutdown();
+}
+
+/*
+ * Shut the GC down.
+ */
+void dvmGcShutdown(void)
+{
+ //TODO: grab and destroy the lock
+ dvmHeapShutdown();
+}
+
+/*
+ * Do any last-minute preparation before we call fork() for the first time.
+ */
+bool dvmGcPreZygoteFork(void)
+{
+ return dvmHeapSourceStartupBeforeFork();
+}
+
+/*
+ * Create a "stock instance" of an exception class.
+ */
+static Object* createStockException(const char* descriptor, const char* msg)
+{
+ Thread* self = dvmThreadSelf();
+ StringObject* msgStr = NULL;
+ ClassObject* clazz;
+ Method* init;
+ Object* obj;
+
+ /* find class, initialize if necessary */
+ clazz = dvmFindSystemClass(descriptor);
+ if (clazz == NULL) {
+ LOGE("Unable to find %s\n", descriptor);
+ return NULL;
+ }
+
+ init = dvmFindDirectMethodByDescriptor(clazz, "<init>",
+ "(Ljava/lang/String;)V");
+ if (init == NULL) {
+ LOGE("Unable to find String-arg constructor for %s\n", descriptor);
+ return NULL;
+ }
+
+ obj = dvmAllocObject(clazz, ALLOC_DEFAULT);
+ if (obj == NULL)
+ return NULL;
+
+ if (msg == NULL) {
+ msgStr = NULL;
+ } else {
+ msgStr = dvmCreateStringFromCstr(msg);
+ if (msgStr == NULL) {
+ LOGW("Could not allocate message string \"%s\"\n", msg);
+ dvmReleaseTrackedAlloc(obj, self);
+ return NULL;
+ }
+ }
+
+ JValue unused;
+ dvmCallMethod(self, init, obj, &unused, msgStr);
+ if (dvmCheckException(self)) {
+ dvmReleaseTrackedAlloc((Object*) msgStr, self);
+ dvmReleaseTrackedAlloc(obj, self);
+ return NULL;
+ }
+
+ dvmReleaseTrackedAlloc((Object*) msgStr, self); // okay if msgStr NULL
+ return obj;
+}
+
+/*
+ * Create some "stock" exceptions. These can be thrown when the system is
+ * too screwed up to allocate and initialize anything, or when we don't
+ * need a meaningful stack trace.
+ *
+ * We can't do this during the initial startup because we need to execute
+ * the constructors.
+ */
+bool dvmCreateStockExceptions(void)
+{
+ /*
+ * Pre-allocate some throwables. These need to be explicitly added
+ * to the GC's root set (see dvmHeapMarkRootSet()).
+ */
+ gDvm.outOfMemoryObj = createStockException("Ljava/lang/OutOfMemoryError;",
+ "[memory exhausted]");
+ dvmReleaseTrackedAlloc(gDvm.outOfMemoryObj, NULL);
+ gDvm.internalErrorObj = createStockException("Ljava/lang/InternalError;",
+ "[pre-allocated]");
+ dvmReleaseTrackedAlloc(gDvm.internalErrorObj, NULL);
+ gDvm.noClassDefFoundErrorObj =
+ createStockException("Ljava/lang/NoClassDefFoundError;",
+ "[generic]");
+ dvmReleaseTrackedAlloc(gDvm.noClassDefFoundErrorObj, NULL);
+
+ if (gDvm.outOfMemoryObj == NULL || gDvm.internalErrorObj == NULL ||
+ gDvm.noClassDefFoundErrorObj == NULL)
+ {
+ LOGW("Unable to create stock exceptions\n");
+ return false;
+ }
+
+ return true;
+}
+
+
+/*
+ * Create an instance of the specified class.
+ *
+ * Returns NULL and throws an exception on failure.
+ */
+Object* dvmAllocObject(ClassObject* clazz, int flags)
+{
+ Object* newObj;
+
+ assert(dvmIsClassInitialized(clazz) || dvmIsClassInitializing(clazz));
+
+ if (IS_CLASS_FLAG_SET(clazz, CLASS_ISFINALIZABLE)) {
+ flags |= ALLOC_FINALIZABLE;
+ }
+
+ /* allocate on GC heap; memory is zeroed out */
+ newObj = dvmMalloc(clazz->objectSize, flags);
+ if (newObj != NULL) {
+ DVM_OBJECT_INIT(newObj, clazz);
+#if WITH_HPROF && WITH_HPROF_STACK
+ hprofFillInStackTrace(newObj);
+#endif
+ dvmTrackAllocation(clazz, clazz->objectSize);
+ }
+
+ return newObj;
+}
+
+/*
+ * Create a copy of an object, for Object.clone().
+ *
+ * We use the size actually allocated, rather than obj->clazz->objectSize,
+ * because the latter doesn't work for array objects.
+ */
+Object* dvmCloneObject(Object* obj)
+{
+ Object* copy;
+ int size;
+ int flags;
+
+ assert(dvmIsValidObject(obj));
+
+ /* Class.java shouldn't let us get here (java.lang.Class is final
+ * and does not implement Clonable), but make extra sure.
+ * A memcpy() clone will wreak havoc on a ClassObject's "innards".
+ */
+ assert(obj->clazz != gDvm.classJavaLangClass);
+
+ if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISFINALIZABLE))
+ flags = ALLOC_DEFAULT | ALLOC_FINALIZABLE;
+ else
+ flags = ALLOC_DEFAULT;
+
+ if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISARRAY)) {
+ size = dvmArrayObjectSize((ArrayObject *)obj);
+ } else {
+ size = obj->clazz->objectSize;
+ }
+
+ copy = dvmMalloc(size, flags);
+ if (copy == NULL)
+ return NULL;
+#if WITH_HPROF && WITH_HPROF_STACK
+ hprofFillInStackTrace(copy);
+ dvmTrackAllocation(obj->clazz, size);
+#endif
+
+ memcpy(copy, obj, size);
+ DVM_LOCK_INIT(&copy->lock);
+ dvmWriteBarrierObject(copy);
+
+ return copy;
+}
+
+
+/*
+ * Track an object that was allocated internally and isn't yet part of the
+ * VM root set.
+ *
+ * We could do this per-thread or globally. If it's global we don't have
+ * to do the thread lookup but we do have to synchronize access to the list.
+ *
+ * NOTE: "obj" is not a fully-formed object; in particular, obj->clazz will
+ * usually be NULL since we're being called from dvmMalloc().
+ */
+void dvmAddTrackedAlloc(Object* obj, Thread* self)
+{
+ if (self == NULL)
+ self = dvmThreadSelf();
+
+ assert(self != NULL);
+ if (!dvmAddToReferenceTable(&self->internalLocalRefTable, obj)) {
+ LOGE("threadid=%d: unable to add %p to internal ref table\n",
+ self->threadId, obj);
+ dvmDumpThread(self, false);
+ dvmAbort();
+ }
+}
+
+/*
+ * Stop tracking an object.
+ *
+ * We allow attempts to delete NULL "obj" so that callers don't have to wrap
+ * calls with "if != NULL".
+ */
+void dvmReleaseTrackedAlloc(Object* obj, Thread* self)
+{
+ if (obj == NULL)
+ return;
+
+ if (self == NULL)
+ self = dvmThreadSelf();
+ assert(self != NULL);
+
+ if (!dvmRemoveFromReferenceTable(&self->internalLocalRefTable,
+ self->internalLocalRefTable.table, obj))
+ {
+ LOGE("threadid=%d: failed to remove %p from internal ref table\n",
+ self->threadId, obj);
+ dvmAbort();
+ }
+}
+
+
+/*
+ * Explicitly initiate garbage collection.
+ */
+void dvmCollectGarbage(bool collectSoftReferences)
+{
+ dvmLockHeap();
+ while (gDvm.gcHeap->gcRunning) {
+ dvmWaitForConcurrentGcToComplete();
+ }
+ dvmCollectGarbageInternal(collectSoftReferences, GC_EXPLICIT);
+ dvmUnlockHeap();
+}
+
+typedef struct {
+ const ClassObject *clazz;
+ size_t count;
+} CountContext;
+
+static void countInstancesOfClassCallback(void *ptr, void *arg)
+{
+ CountContext *ctx = arg;
+ const Object *obj = ptr;
+
+ assert(ctx != NULL);
+ if (obj->clazz == ctx->clazz) {
+ ctx->count += 1;
+ }
+}
+
+size_t dvmCountInstancesOfClass(const ClassObject *clazz)
+{
+ CountContext ctx = { clazz, 0 };
+ HeapBitmap *bitmap = dvmHeapSourceGetLiveBits();
+ dvmLockHeap();
+ dvmHeapBitmapWalk(bitmap, countInstancesOfClassCallback, &ctx);
+ dvmUnlockHeap();
+ return ctx.count;
+}
+
+static void countAssignableInstancesOfClassCallback(void *ptr, void *arg)
+{
+ CountContext *ctx = arg;
+ const Object *obj = ptr;
+
+ assert(ctx != NULL);
+ if (dvmInstanceof(obj->clazz, ctx->clazz)) {
+ ctx->count += 1;
+ }
+}
+
+size_t dvmCountAssignableInstancesOfClass(const ClassObject *clazz)
+{
+ CountContext ctx = { clazz, 0 };
+ HeapBitmap *bitmap = dvmHeapSourceGetLiveBits();
+ dvmLockHeap();
+ dvmHeapBitmapWalk(bitmap, countAssignableInstancesOfClassCallback, &ctx);
+ dvmUnlockHeap();
+ return ctx.count;
+}
diff --git a/vm/alloc/Alloc.h b/vm/alloc/Alloc.h
new file mode 100644
index 0000000..fd9c633
--- /dev/null
+++ b/vm/alloc/Alloc.h
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Garbage-collecting allocator.
+ */
+#ifndef _DALVIK_ALLOC_ALLOC
+#define _DALVIK_ALLOC_ALLOC
+
+#include <stddef.h>
+
+/*
+ * Initialization.
+ */
+bool dvmGcStartup(void);
+bool dvmCreateStockExceptions(void);
+bool dvmGcStartupAfterZygote(void);
+void dvmGcShutdown(void);
+void dvmGcThreadShutdown(void);
+
+/*
+ * Do any last-minute preparation before we call fork() for the first time.
+ */
+bool dvmGcPreZygoteFork(void);
+
+/*
+ * Basic allocation function.
+ *
+ * The new object will be added to the "tracked alloc" table unless
+ * flags is ALLOC_DONT_TRACK.
+ *
+ * Returns NULL and throws an exception on failure.
+ */
+void* dvmMalloc(size_t size, int flags);
+
+/*
+ * Allocate a new object.
+ *
+ * The new object will be added to the "tracked alloc" table unless
+ * flags is ALLOC_DONT_TRACK.
+ *
+ * Returns NULL and throws an exception on failure.
+ */
+Object* dvmAllocObject(ClassObject* clazz, int flags);
+
+/* flags for dvmMalloc */
+enum {
+ ALLOC_DEFAULT = 0x00,
+ ALLOC_DONT_TRACK = 0x01, /* don't add to internal tracking list */
+ ALLOC_FINALIZABLE = 0x02, /* call finalize() before freeing */
+};
+
+/*
+ * Call when a request is so far off that we can't call dvmMalloc(). Throws
+ * an exception with the specified message.
+ */
+void dvmThrowBadAllocException(const char* msg);
+
+/*
+ * Track an object reference that is currently only visible internally.
+ * This is called automatically by dvmMalloc() unless ALLOC_DONT_TRACK
+ * is set.
+ *
+ * The "self" argument is allowed as an optimization; it may be NULL.
+ */
+void dvmAddTrackedAlloc(Object* obj, Thread* self);
+
+/*
+ * Remove an object from the internal tracking list.
+ *
+ * Does nothing if "obj" is NULL.
+ *
+ * The "self" argument is allowed as an optimization; it may be NULL.
+ */
+void dvmReleaseTrackedAlloc(Object* obj, Thread* self);
+
+/*
+ * Returns true iff <obj> points to a valid allocated object.
+ */
+bool dvmIsValidObject(const Object* obj);
+
+/*
+ * Create a copy of an object.
+ *
+ * The new object will be added to the "tracked alloc" table.
+ */
+Object* dvmCloneObject(Object* obj);
+
+/*
+ * Validate the object pointer. Returns "false" and throws an exception if
+ * "obj" is null or invalid.
+ *
+ * This may be used in performance critical areas as a null-pointer check;
+ * anything else here should be for debug builds only. In particular, for
+ * "release" builds we want to skip the call to dvmIsValidObject() -- the
+ * classfile validation will screen out code that puts invalid data into
+ * object reference registers.
+ */
+INLINE int dvmValidateObject(Object* obj)
+{
+ if (obj == NULL) {
+ dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+ return false;
+ }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+ if (!dvmIsValidObject(obj)) {
+ dvmAbort();
+ dvmThrowException("Ljava/lang/InternalError;",
+ "VM detected invalid object ptr");
+ return false;
+ }
+#endif
+#ifndef NDEBUG
+ /* check for heap corruption */
+ if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+ dvmAbort();
+ dvmThrowException("Ljava/lang/InternalError;",
+ "VM detected invalid object class ptr");
+ return false;
+ }
+#endif
+ return true;
+}
+
+/*
+ * Determine the exact number of GC heap bytes used by an object. (Internal
+ * to heap code except for debugging.)
+ */
+size_t dvmObjectSizeInHeap(const Object* obj);
+
+/*
+ * Gets the current ideal heap utilization, represented as a number
+ * between zero and one.
+ */
+float dvmGetTargetHeapUtilization(void);
+
+/*
+ * Sets the new ideal heap utilization, represented as a number
+ * between zero and one.
+ */
+void dvmSetTargetHeapUtilization(float newTarget);
+
+/*
+ * If set is true, sets the new minimum heap size to size; always
+ * returns the current (or previous) size. If size is zero,
+ * removes the current minimum constraint (if present).
+ */
+size_t dvmMinimumHeapSize(size_t size, bool set);
+
+/*
+ * Updates the internal count of externally-allocated memory. If there's
+ * enough room for that memory, returns true. If not, returns false and
+ * does not update the count.
+ *
+ * May cause a GC as a side-effect.
+ */
+bool dvmTrackExternalAllocation(size_t n);
+
+/*
+ * Reduces the internal count of externally-allocated memory.
+ */
+void dvmTrackExternalFree(size_t n);
+
+/*
+ * Returns the number of externally-allocated bytes being tracked by
+ * dvmTrackExternalAllocation/Free().
+ */
+size_t dvmGetExternalBytesAllocated(void);
+
+/*
+ * Returns a count of the direct instances of a class.
+ */
+size_t dvmCountInstancesOfClass(const ClassObject *clazz);
+
+/*
+ * Returns a count of the instances of a class and its subclasses.
+ */
+size_t dvmCountAssignableInstancesOfClass(const ClassObject *clazz);
+
+#endif /*_DALVIK_ALLOC_ALLOC*/
diff --git a/vm/alloc/CardTable.c b/vm/alloc/CardTable.c
new file mode 100644
index 0000000..9e3677b
--- /dev/null
+++ b/vm/alloc/CardTable.c
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/mman.h> /* for PROT_* */
+
+#include "Dalvik.h"
+#include "alloc/HeapSource.h"
+#include "alloc/Visit.h"
+
+/*
+ * Maintain a card table from the the write barrier. All writes of
+ * non-NULL values to heap addresses should go through an entry in
+ * WriteBarrier, and from there to here.
+ *
+ * The heap is divided into "cards" of GC_CARD_SIZE bytes, as
+ * determined by GC_CARD_SHIFT. The card table contains one byte of
+ * data per card, to be used by the GC. The value of the byte will be
+ * one of GC_CARD_CLEAN or GC_CARD_DIRTY.
+ *
+ * After any store of a non-NULL object pointer into a heap object,
+ * code is obliged to mark the card dirty. The setters in
+ * ObjectInlines.h [such as dvmSetFieldObject] do this for you. The
+ * JIT and fast interpreters also contain code to mark cards as dirty.
+ *
+ * The card table's base [the "biased card table"] gets set to a
+ * rather strange value. In order to keep the JIT from having to
+ * fabricate or load GC_DIRTY_CARD to store into the card table,
+ * biased base is within the mmap allocation at a point where it's low
+ * byte is equal to GC_DIRTY_CARD. See dvmCardTableStartup for details.
+ */
+
+/*
+ * Initializes the card table; must be called before any other
+ * dvmCardTable*() functions.
+ */
+bool dvmCardTableStartup(void)
+{
+ size_t length;
+ void *allocBase;
+ u1 *biasedBase;
+ GcHeap *gcHeap = gDvm.gcHeap;
+ void *heapBase = dvmHeapSourceGetBase();
+ assert(gcHeap != NULL);
+ assert(heapBase != NULL);
+
+ /* Set up the card table */
+ length = gDvm.heapSizeMax / GC_CARD_SIZE;
+ /* Allocate an extra 256 bytes to allow fixed low-byte of base */
+ allocBase = dvmAllocRegion(length + 0x100, PROT_READ | PROT_WRITE,
+ "dalvik-card-table");
+ if (allocBase == NULL) {
+ return false;
+ }
+ gcHeap->cardTableBase = allocBase;
+ gcHeap->cardTableLength = length;
+ /* All zeros is the correct initial value; all clean. */
+ assert(GC_CARD_CLEAN == 0);
+
+ biasedBase = (u1 *)((uintptr_t)allocBase -
+ ((uintptr_t)heapBase >> GC_CARD_SHIFT));
+ if (((uintptr_t)biasedBase & 0xff) != GC_CARD_DIRTY) {
+ int offset = GC_CARD_DIRTY - ((uintptr_t)biasedBase & 0xff);
+ biasedBase += offset + (offset < 0 ? 0x100 : 0);
+ }
+ assert(((uintptr_t)biasedBase & 0xff) == GC_CARD_DIRTY);
+ gDvm.biasedCardTableBase = biasedBase;
+
+ return true;
+}
+
+/*
+ * Tears down the entire CardTable.
+ */
+void dvmCardTableShutdown()
+{
+ gDvm.biasedCardTableBase = NULL;
+ munmap(gDvm.gcHeap->cardTableBase, gDvm.gcHeap->cardTableLength);
+}
+
+void dvmClearCardTable(void)
+{
+ assert(gDvm.gcHeap->cardTableBase != NULL);
+ memset(gDvm.gcHeap->cardTableBase, GC_CARD_CLEAN, gDvm.gcHeap->cardTableLength);
+}
+
+/*
+ * Returns true iff the address is within the bounds of the card table.
+ */
+bool dvmIsValidCard(const u1 *cardAddr)
+{
+ GcHeap *h = gDvm.gcHeap;
+ return cardAddr >= h->cardTableBase &&
+ cardAddr < &h->cardTableBase[h->cardTableLength];
+}
+
+/*
+ * Returns the address of the relevent byte in the card table, given
+ * an address on the heap.
+ */
+u1 *dvmCardFromAddr(const void *addr)
+{
+ u1 *biasedBase = gDvm.biasedCardTableBase;
+ u1 *cardAddr = biasedBase + ((uintptr_t)addr >> GC_CARD_SHIFT);
+ assert(dvmIsValidCard(cardAddr));
+ return cardAddr;
+}
+
+/*
+ * Returns the first address in the heap which maps to this card.
+ */
+void *dvmAddrFromCard(const u1 *cardAddr)
+{
+ assert(dvmIsValidCard(cardAddr));
+ uintptr_t offset = cardAddr - gDvm.biasedCardTableBase;
+ return (void *)(offset << GC_CARD_SHIFT);
+}
+
+/*
+ * Dirties the card for the given address.
+ */
+void dvmMarkCard(const void *addr)
+{
+ u1 *cardAddr = dvmCardFromAddr(addr);
+ *cardAddr = GC_CARD_DIRTY;
+}
+
+/*
+ * Returns true if the object is on a dirty card.
+ */
+static bool isObjectDirty(const Object *obj)
+{
+ assert(obj != NULL);
+ assert(dvmIsValidObject(obj));
+ u1 *card = dvmCardFromAddr(obj);
+ return *card == GC_CARD_DIRTY;
+}
+
+/*
+ * Context structure for verifying the card table.
+ */
+typedef struct {
+ HeapBitmap *markBits;
+ size_t whiteRefs;
+} WhiteReferenceCounter;
+
+/*
+ * Visitor that counts white referents.
+ */
+static void countWhiteReferenceVisitor(void *addr, void *arg)
+{
+ WhiteReferenceCounter *ctx;
+ Object *obj;
+
+ assert(addr != NULL);
+ assert(arg != NULL);
+ obj = *(Object **)addr;
+ if (obj == NULL) {
+ return;
+ }
+ assert(dvmIsValidObject(obj));
+ ctx = arg;
+ if (dvmHeapBitmapIsObjectBitSet(ctx->markBits, obj)) {
+ return;
+ }
+ ctx->whiteRefs += 1;
+}
+
+/*
+ * Returns true if the given object is a reference object and the
+ * just the referent is unmarked.
+ */
+static bool isReferentUnmarked(const Object *obj,
+ const WhiteReferenceCounter* ctx)
+{
+ assert(obj != NULL);
+ assert(obj->clazz != NULL);
+ assert(ctx != NULL);
+ if (ctx->whiteRefs != 1) {
+ return false;
+ } else if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISREFERENCE)) {
+ size_t offset = gDvm.offJavaLangRefReference_referent;
+ const Object *referent = dvmGetFieldObject(obj, offset);
+ return !dvmHeapBitmapIsObjectBitSet(ctx->markBits, referent);
+ } else {
+ return false;
+ }
+}
+
+/*
+ * Returns true if the given object is a string and has been interned
+ * by the user.
+ */
+static bool isWeakInternedString(const Object *obj)
+{
+ assert(obj != NULL);
+ if (obj->clazz == gDvm.classJavaLangString) {
+ return dvmIsWeakInternedString((StringObject *)obj);
+ } else {
+ return false;
+ }
+}
+
+/*
+ * Returns true if the given object has been pushed on the mark stack
+ * by root marking.
+ */
+static bool isPushedOnMarkStack(const Object *obj)
+{
+ GcMarkContext *ctx = &gDvm.gcHeap->markContext;
+ const Object **ptr;
+
+ for (ptr = ctx->stack.top; ptr != ctx->stack.base; ++ptr) {
+ if (*ptr == obj) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/*
+ * Callback applied to marked objects. If the object is gray and on
+ * an unmarked card an error is logged and the VM is aborted. Card
+ * table verification occurs between root marking and weak reference
+ * processing. We treat objects marked from the roots and weak
+ * references specially as it is permissible for these objects to be
+ * gray and on an unmarked card.
+ */
+static void verifyCardTableCallback(void *ptr, void *arg)
+{
+ Object *obj = ptr;
+ WhiteReferenceCounter ctx = { arg, 0 };
+
+ dvmVisitObject(countWhiteReferenceVisitor, obj, &ctx);
+ if (ctx.whiteRefs == 0) {
+ return;
+ } else if (isObjectDirty(obj)) {
+ return;
+ } else if (isReferentUnmarked(obj, &ctx)) {
+ return;
+ } else if (isWeakInternedString(obj)) {
+ return;
+ } else if (isPushedOnMarkStack(obj)) {
+ return;
+ } else {
+ LOGE("Verify failed, object %p is gray and on an unmarked card", obj);
+ dvmDumpObject(obj);
+ dvmAbort();
+ }
+}
+
+/*
+ * Verifies that gray objects are on a dirty card.
+ */
+void dvmVerifyCardTable(void)
+{
+ HeapBitmap *markBits = gDvm.gcHeap->markContext.bitmap;
+ dvmHeapBitmapWalk(markBits, verifyCardTableCallback, markBits);
+}
diff --git a/vm/alloc/CardTable.h b/vm/alloc/CardTable.h
new file mode 100644
index 0000000..1a6db11
--- /dev/null
+++ b/vm/alloc/CardTable.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Maintain a card table from the the write barrier. All writes of
+ * non-NULL values to heap addresses should go through an entry in
+ * WriteBarrier, and from there to here.
+ */
+
+#ifndef _DALVIK_ALLOC_CARDTABLE
+#define _DALVIK_ALLOC_CARDTABLE
+
+#define GC_CARD_SHIFT 7
+#define GC_CARD_SIZE (1 << GC_CARD_SHIFT)
+#define GC_CARD_CLEAN 0
+#define GC_CARD_DIRTY 0x70
+
+/*
+ * Initializes the card table; must be called before any other
+ * dvmCardTable*() functions.
+ */
+bool dvmCardTableStartup(void);
+
+/*
+ * Tears down the entire CardTable structure.
+ */
+void dvmCardTableShutdown(void);
+
+/*
+ * Resets all of the bytes in the card table to clean.
+ */
+void dvmClearCardTable(void);
+
+/*
+ * Returns the address of the relevent byte in the card table, given
+ * an address on the heap.
+ */
+u1 *dvmCardFromAddr(const void *addr);
+
+/*
+ * Returns the first address in the heap which maps to this card.
+ */
+void *dvmAddrFromCard(const u1 *card);
+
+/*
+ * Set the card associated with the given address to GC_CARD_DIRTY.
+ */
+void dvmMarkCard(const void *addr);
+
+/*
+ * Verifies that all gray objects are on a dirty card.
+ */
+void dvmVerifyCardTable(void);
+
+#endif /*_DALVIK_ALLOC_CARDTABLE*/
diff --git a/vm/alloc/Copying.c b/vm/alloc/Copying.c
new file mode 100644
index 0000000..940d9f5
--- /dev/null
+++ b/vm/alloc/Copying.c
@@ -0,0 +1,2360 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <limits.h>
+#include <sys/mman.h>
+
+#include "Dalvik.h"
+#include "alloc/Heap.h"
+#include "alloc/HeapBitmap.h"
+#include "alloc/HeapInternal.h"
+#include "alloc/HeapSource.h"
+#include "alloc/Verify.h"
+#include "alloc/clz.h"
+
+/*
+ * A "mostly copying", generational, garbage collector.
+ *
+ * TODO: we allocate our own contiguous tract of page frames to back
+ * object allocations. To cooperate with other heaps active in the
+ * virtual machine we need to move the responsibility of allocating
+ * pages someplace outside of this code.
+ *
+ * The other major data structures that maintain the state of the heap
+ * are the block space table and the block queue.
+ *
+ * The block space table records the state of a block. We must track
+ * whether a block is:
+ *
+ * - Free or allocated in some space.
+ *
+ * - If the block holds part of a large object allocation, whether the
+ * block is the initial or a continued block of the allocation.
+ *
+ * - Whether the block is pinned, that is to say whether at least one
+ * object in the block must remain stationary. Only needed during a
+ * GC.
+ *
+ * - Which space the object belongs to. At present this means
+ * from-space or to-space.
+ *
+ * The block queue is used during garbage collection. Unlike Cheney's
+ * algorithm, from-space and to-space are not contiguous. Therefore,
+ * one cannot maintain the state of the copy with just two pointers.
+ * The block queue exists to thread lists of blocks from the various
+ * spaces together.
+ *
+ * Additionally, we record the free space frontier of the heap, as
+ * well as the address of the first object within a block, which is
+ * required to copy objects following a large object (not currently
+ * implemented). This is stored in the heap source structure. This
+ * should be moved elsewhere to support in-line allocations from Java
+ * threads.
+ *
+ * Allocation requests are satisfied by reserving storage from one or
+ * more contiguous blocks. Objects that are small enough to fit
+ * inside a block are packed together within a block. Objects that
+ * are larger than a block are allocated from contiguous sequences of
+ * blocks. When half the available blocks are filled, a garbage
+ * collection occurs. We "flip" spaces (exchange from- and to-space),
+ * copy live objects into to space, and perform pointer adjustment.
+ *
+ * Copying is made more complicated by the requirement that some
+ * objects must not be moved. This property is known as "pinning".
+ * These objects must be dealt with specially. We use Bartlett's
+ * scheme; blocks containing such objects are grayed (promoted) at the
+ * start of a garbage collection. By virtue of this trick, tracing
+ * from the roots proceeds as usual but all objects on those pages are
+ * considered promoted and therefore not moved.
+ *
+ * TODO: there is sufficient information within the garbage collector
+ * to implement Attardi's scheme for evacuating unpinned objects from
+ * a page that is otherwise pinned. This would eliminate false
+ * retention caused by the large pinning granularity.
+ *
+ * We need a scheme for medium and large objects. Ignore that for
+ * now, we can return to this later.
+ *
+ * Eventually we need to worry about promoting objects out of the
+ * copy-collected heap (tenuring) into a less volatile space. Copying
+ * may not always be the best policy for such spaces. We should
+ * consider a variant of mark, sweep, compact.
+ *
+ * The block scheme allows us to use VM page faults to maintain a
+ * write barrier. Consider having a special leaf state for a page.
+ *
+ * Bibliography:
+ *
+ * C. J. Cheney. 1970. A non-recursive list compacting
+ * algorithm. CACM. 13-11 pp677--678.
+ *
+ * Joel F. Bartlett. 1988. Compacting Garbage Collection with
+ * Ambiguous Roots. Digital Equipment Corporation.
+ *
+ * Joel F. Bartlett. 1989. Mostly-Copying Garbage Collection Picks Up
+ * Generations and C++. Digital Equipment Corporation.
+ *
+ * G. May Yip. 1991. Incremental, Generational Mostly-Copying Garbage
+ * Collection in Uncooperative Environments. Digital Equipment
+ * Corporation.
+ *
+ * Giuseppe Attardi, Tito Flagella. 1994. A Customisable Memory
+ * Management Framework. TR-94-010
+ *
+ * Giuseppe Attardi, Tito Flagella, Pietro Iglio. 1998. A customisable
+ * memory management framework for C++. Software -- Practice and
+ * Experience. 28(11), 1143-1183.
+ *
+ */
+
+#define ARRAYSIZE(x) (sizeof(x) / sizeof(x[0]))
+
+#if 0
+#define LOG_ALLOC LOGI
+#define LOG_PIN LOGI
+#define LOG_PROM LOGI
+#define LOG_REF LOGI
+#define LOG_SCAV LOGI
+#define LOG_TRAN LOGI
+#define LOG_VER LOGI
+#else
+#define LOG_ALLOC(...) ((void)0)
+#define LOG_PIN(...) ((void)0)
+#define LOG_PROM(...) ((void)0)
+#define LOG_REF(...) ((void)0)
+#define LOG_SCAV(...) ((void)0)
+#define LOG_TRAN(...) ((void)0)
+#define LOG_VER(...) ((void)0)
+#endif
+
+static void enqueueBlock(HeapSource *heapSource, size_t block);
+static void scavengeReference(Object **obj);
+static bool toSpaceContains(const void *addr);
+static bool fromSpaceContains(const void *addr);
+static size_t sumHeapBitmap(const HeapBitmap *bitmap);
+static size_t objectSize(const Object *obj);
+static void scavengeDataObject(Object *obj);
+static void scavengeBlockQueue(void);
+
+/*
+ * We use 512-byte blocks.
+ */
+enum { BLOCK_SHIFT = 9 };
+enum { BLOCK_SIZE = 1 << BLOCK_SHIFT };
+
+/*
+ * Space identifiers, stored into the blockSpace array.
+ */
+enum {
+ BLOCK_FREE = 0,
+ BLOCK_FROM_SPACE = 1,
+ BLOCK_TO_SPACE = 2,
+ BLOCK_CONTINUED = 7
+};
+
+/*
+ * Alignment for all allocations, in bytes.
+ */
+enum { ALLOC_ALIGNMENT = 8 };
+
+/*
+ * Sentinel value for the queue end.
+ */
+#define QUEUE_TAIL (~(size_t)0)
+
+struct HeapSource {
+
+ /* The base address of backing store. */
+ u1 *blockBase;
+
+ /* Total number of blocks available for allocation. */
+ size_t totalBlocks;
+ size_t allocBlocks;
+
+ /*
+ * The scavenger work queue. Implemented as an array of index
+ * values into the queue.
+ */
+ size_t *blockQueue;
+
+ /*
+ * Base and limit blocks. Basically the shifted start address of
+ * the block. We convert blocks to a relative number when
+ * indexing in the block queue. TODO: make the block queue base
+ * relative rather than the index into the block queue.
+ */
+ size_t baseBlock, limitBlock;
+
+ size_t queueHead;
+ size_t queueTail;
+ size_t queueSize;
+
+ /* The space of the current block 0 (free), 1 or 2. */
+ char *blockSpace;
+
+ /* Start of free space in the current block. */
+ u1 *allocPtr;
+ /* Exclusive limit of free space in the current block. */
+ u1 *allocLimit;
+
+ HeapBitmap allocBits;
+
+ /*
+ * The starting size of the heap. This value is the same as the
+ * value provided to the -Xms flag.
+ */
+ size_t minimumSize;
+
+ /*
+ * The maximum size of the heap. This value is the same as the
+ * -Xmx flag.
+ */
+ size_t maximumSize;
+
+ /*
+ * The current, committed size of the heap. At present, this is
+ * equivalent to the maximumSize.
+ */
+ size_t currentSize;
+
+ size_t bytesAllocated;
+};
+
+static unsigned long alignDown(unsigned long x, unsigned long n)
+{
+ return x & -n;
+}
+
+static unsigned long alignUp(unsigned long x, unsigned long n)
+{
+ return alignDown(x + (n - 1), n);
+}
+
+static void describeBlocks(const HeapSource *heapSource)
+{
+ size_t i;
+
+ for (i = 0; i < heapSource->totalBlocks; ++i) {
+ if ((i % 32) == 0) putchar('\n');
+ printf("%d ", heapSource->blockSpace[i]);
+ }
+ putchar('\n');
+}
+
+/*
+ * Virtual memory interface.
+ */
+
+static void *virtualAlloc(size_t length)
+{
+ void *addr;
+ int flags, prot;
+
+ flags = MAP_PRIVATE | MAP_ANONYMOUS;
+ prot = PROT_READ | PROT_WRITE;
+ addr = mmap(NULL, length, prot, flags, -1, 0);
+ if (addr == MAP_FAILED) {
+ LOGE_HEAP("mmap: %s", strerror(errno));
+ addr = NULL;
+ }
+ return addr;
+}
+
+static void virtualFree(void *addr, size_t length)
+{
+ int res;
+
+ assert(addr != NULL);
+ assert((uintptr_t)addr % SYSTEM_PAGE_SIZE == 0);
+ res = munmap(addr, length);
+ if (res == -1) {
+ LOGE_HEAP("munmap: %s", strerror(errno));
+ }
+}
+
+#ifndef NDEBUG
+static int isValidAddress(const HeapSource *heapSource, const u1 *addr)
+{
+ size_t block;
+
+ block = (uintptr_t)addr >> BLOCK_SHIFT;
+ return heapSource->baseBlock <= block &&
+ heapSource->limitBlock > block;
+}
+#endif
+
+/*
+ * Iterate over the block map looking for a contiguous run of free
+ * blocks.
+ */
+static void *allocateBlocks(HeapSource *heapSource, size_t blocks)
+{
+ void *addr;
+ size_t allocBlocks, totalBlocks;
+ size_t i, j;
+
+ allocBlocks = heapSource->allocBlocks;
+ totalBlocks = heapSource->totalBlocks;
+ /* Check underflow. */
+ assert(blocks != 0);
+ /* Check overflow. */
+ if (allocBlocks + blocks > totalBlocks / 2) {
+ return NULL;
+ }
+ /* Scan block map. */
+ for (i = 0; i < totalBlocks; ++i) {
+ /* Check fit. */
+ for (j = 0; j < blocks; ++j) { /* runs over totalBlocks */
+ if (heapSource->blockSpace[i+j] != BLOCK_FREE) {
+ break;
+ }
+ }
+ /* No fit? */
+ if (j != blocks) {
+ i += j;
+ continue;
+ }
+ /* Fit, allocate. */
+ heapSource->blockSpace[i] = BLOCK_TO_SPACE; /* why to-space? */
+ for (j = 1; j < blocks; ++j) {
+ heapSource->blockSpace[i+j] = BLOCK_CONTINUED;
+ }
+ heapSource->allocBlocks += blocks;
+ addr = &heapSource->blockBase[i*BLOCK_SIZE];
+ memset(addr, 0, blocks*BLOCK_SIZE);
+ /* Collecting? */
+ if (heapSource->queueHead != QUEUE_TAIL) {
+ LOG_ALLOC("allocateBlocks allocBlocks=%zu,block#=%zu", heapSource->allocBlocks, i);
+ /*
+ * This allocated was on behalf of the transporter when it
+ * shaded a white object gray. We enqueue the block so
+ * the scavenger can further shade the gray objects black.
+ */
+ enqueueBlock(heapSource, i);
+ }
+
+ return addr;
+ }
+ /* Insufficient space, fail. */
+ LOGE("Insufficient space, %zu blocks, %zu blocks allocated and %zu bytes allocated",
+ heapSource->totalBlocks,
+ heapSource->allocBlocks,
+ heapSource->bytesAllocated);
+ return NULL;
+}
+
+/* Converts an absolute address to a relative block number. */
+static size_t addressToBlock(const HeapSource *heapSource, const void *addr)
+{
+ assert(heapSource != NULL);
+ assert(isValidAddress(heapSource, addr));
+ return (((uintptr_t)addr) >> BLOCK_SHIFT) - heapSource->baseBlock;
+}
+
+/* Converts a relative block number to an absolute address. */
+static u1 *blockToAddress(const HeapSource *heapSource, size_t block)
+{
+ u1 *addr;
+
+ addr = (u1 *) (((uintptr_t) heapSource->baseBlock + block) * BLOCK_SIZE);
+ assert(isValidAddress(heapSource, addr));
+ return addr;
+}
+
+static void clearBlock(HeapSource *heapSource, size_t block)
+{
+ u1 *addr;
+ size_t i;
+
+ assert(heapSource != NULL);
+ assert(block < heapSource->totalBlocks);
+ addr = heapSource->blockBase + block*BLOCK_SIZE;
+ memset(addr, 0xCC, BLOCK_SIZE);
+ for (i = 0; i < BLOCK_SIZE; i += 8) {
+ dvmHeapBitmapClearObjectBit(&heapSource->allocBits, addr + i);
+ }
+}
+
+static void clearFromSpace(HeapSource *heapSource)
+{
+ size_t i, count;
+
+ assert(heapSource != NULL);
+ i = count = 0;
+ while (i < heapSource->totalBlocks) {
+ if (heapSource->blockSpace[i] != BLOCK_FROM_SPACE) {
+ ++i;
+ continue;
+ }
+ heapSource->blockSpace[i] = BLOCK_FREE;
+ clearBlock(heapSource, i);
+ ++i;
+ ++count;
+ while (i < heapSource->totalBlocks &&
+ heapSource->blockSpace[i] == BLOCK_CONTINUED) {
+ heapSource->blockSpace[i] = BLOCK_FREE;
+ clearBlock(heapSource, i);
+ ++i;
+ ++count;
+ }
+ }
+ LOG_SCAV("freed %zu blocks (%zu bytes)", count, count*BLOCK_SIZE);
+}
+
+/*
+ * Appends the given block to the block queue. The block queue is
+ * processed in-order by the scavenger.
+ */
+static void enqueueBlock(HeapSource *heapSource, size_t block)
+{
+ assert(heapSource != NULL);
+ assert(block < heapSource->totalBlocks);
+ if (heapSource->queueHead != QUEUE_TAIL) {
+ heapSource->blockQueue[heapSource->queueTail] = block;
+ } else {
+ heapSource->queueHead = block;
+ }
+ heapSource->blockQueue[block] = QUEUE_TAIL;
+ heapSource->queueTail = block;
+ ++heapSource->queueSize;
+}
+
+/*
+ * Grays all objects within the block corresponding to the given
+ * address.
+ */
+static void promoteBlockByAddr(HeapSource *heapSource, const void *addr)
+{
+ size_t block;
+
+ block = addressToBlock(heapSource, (const u1 *)addr);
+ if (heapSource->blockSpace[block] != BLOCK_TO_SPACE) {
+ // LOG_PROM("promoting block %zu %d @ %p", block, heapSource->blockSpace[block], obj);
+ heapSource->blockSpace[block] = BLOCK_TO_SPACE;
+ enqueueBlock(heapSource, block);
+ /* TODO(cshapiro): count continued blocks?*/
+ heapSource->allocBlocks += 1;
+ } else {
+ // LOG_PROM("NOT promoting block %zu %d @ %p", block, heapSource->blockSpace[block], obj);
+ }
+}
+
+GcHeap *dvmHeapSourceStartup(size_t startSize, size_t absoluteMaxSize)
+{
+ GcHeap* gcHeap;
+ HeapSource *heapSource;
+
+ assert(startSize <= absoluteMaxSize);
+
+ heapSource = malloc(sizeof(*heapSource));
+ assert(heapSource != NULL);
+ memset(heapSource, 0, sizeof(*heapSource));
+
+ heapSource->minimumSize = alignUp(startSize, BLOCK_SIZE);
+ heapSource->maximumSize = alignUp(absoluteMaxSize, BLOCK_SIZE);
+
+ heapSource->currentSize = heapSource->maximumSize;
+
+ /* Allocate underlying storage for blocks. */
+ heapSource->blockBase = virtualAlloc(heapSource->maximumSize);
+ assert(heapSource->blockBase != NULL);
+ heapSource->baseBlock = (uintptr_t) heapSource->blockBase >> BLOCK_SHIFT;
+ heapSource->limitBlock = ((uintptr_t) heapSource->blockBase + heapSource->maximumSize) >> BLOCK_SHIFT;
+
+ heapSource->allocBlocks = 0;
+ heapSource->totalBlocks = (heapSource->limitBlock - heapSource->baseBlock);
+
+ assert(heapSource->totalBlocks = heapSource->maximumSize / BLOCK_SIZE);
+
+ {
+ size_t size = sizeof(heapSource->blockQueue[0]);
+ heapSource->blockQueue = malloc(heapSource->totalBlocks*size);
+ assert(heapSource->blockQueue != NULL);
+ memset(heapSource->blockQueue, 0xCC, heapSource->totalBlocks*size);
+ heapSource->queueHead = QUEUE_TAIL;
+ }
+
+ /* Byte indicating space residence or free status of block. */
+ {
+ size_t size = sizeof(heapSource->blockSpace[0]);
+ heapSource->blockSpace = malloc(heapSource->totalBlocks*size);
+ assert(heapSource->blockSpace != NULL);
+ memset(heapSource->blockSpace, 0, heapSource->totalBlocks*size);
+ }
+
+ dvmHeapBitmapInit(&heapSource->allocBits,
+ heapSource->blockBase,
+ heapSource->maximumSize,
+ "blockBase");
+
+ /* Initialize allocation pointers. */
+ heapSource->allocPtr = allocateBlocks(heapSource, 1);
+ heapSource->allocLimit = heapSource->allocPtr + BLOCK_SIZE;
+
+ gcHeap = malloc(sizeof(*gcHeap));
+ assert(gcHeap != NULL);
+ memset(gcHeap, 0, sizeof(*gcHeap));
+ gcHeap->heapSource = heapSource;
+
+ return gcHeap;
+}
+
+/*
+ * Perform any required heap initializations after forking from the
+ * zygote process. This is a no-op for the time being. Eventually
+ * this will demarcate the shared region of the heap.
+ */
+bool dvmHeapSourceStartupAfterZygote(void)
+{
+ return true;
+}
+
+bool dvmHeapSourceStartupBeforeFork(void)
+{
+ assert(!"implemented");
+ return false;
+}
+
+void dvmHeapSourceShutdown(GcHeap **gcHeap)
+{
+ if (*gcHeap == NULL || (*gcHeap)->heapSource == NULL)
+ return;
+ free((*gcHeap)->heapSource->blockQueue);
+ free((*gcHeap)->heapSource->blockSpace);
+ virtualFree((*gcHeap)->heapSource->blockBase,
+ (*gcHeap)->heapSource->maximumSize);
+ free((*gcHeap)->heapSource);
+ (*gcHeap)->heapSource = NULL;
+ free(*gcHeap);
+ *gcHeap = NULL;
+}
+
+size_t dvmHeapSourceGetValue(enum HeapSourceValueSpec spec,
+ size_t perHeapStats[],
+ size_t arrayLen)
+{
+ HeapSource *heapSource;
+ size_t value;
+
+ heapSource = gDvm.gcHeap->heapSource;
+ switch (spec) {
+ case HS_EXTERNAL_BYTES_ALLOCATED:
+ value = 0;
+ break;
+ case HS_EXTERNAL_LIMIT:
+ value = 0;
+ break;
+ case HS_FOOTPRINT:
+ value = heapSource->maximumSize;
+ break;
+ case HS_ALLOWED_FOOTPRINT:
+ value = heapSource->maximumSize;
+ break;
+ case HS_BYTES_ALLOCATED:
+ value = heapSource->bytesAllocated;
+ break;
+ case HS_OBJECTS_ALLOCATED:
+ value = sumHeapBitmap(&heapSource->allocBits);
+ break;
+ default:
+ assert(!"implemented");
+ value = 0;
+ }
+ if (perHeapStats) {
+ *perHeapStats = value;
+ }
+ return value;
+}
+
+/*
+ * Performs a shallow copy of the allocation bitmap into the given
+ * vector of heap bitmaps.
+ */
+void dvmHeapSourceGetObjectBitmaps(HeapBitmap objBits[], HeapBitmap markBits[],
+ size_t numHeaps)
+{
+ assert(!"implemented");
+}
+
+HeapBitmap *dvmHeapSourceGetLiveBits(void)
+{
+ return &gDvm.gcHeap->heapSource->allocBits;
+}
+
+/*
+ * Allocate the specified number of bytes from the heap. The
+ * allocation cursor points into a block of free storage. If the
+ * given allocation fits in the remaining space of the block, we
+ * advance the cursor and return a pointer to the free storage. If
+ * the allocation cannot fit in the current block but is smaller than
+ * a block we request a new block and allocate from it instead. If
+ * the allocation is larger than a block we must allocate from a span
+ * of contiguous blocks.
+ */
+void *dvmHeapSourceAlloc(size_t length)
+{
+ HeapSource *heapSource;
+ unsigned char *addr;
+ size_t aligned, available, blocks;
+
+ heapSource = gDvm.gcHeap->heapSource;
+ assert(heapSource != NULL);
+ assert(heapSource->allocPtr != NULL);
+ assert(heapSource->allocLimit != NULL);
+
+ aligned = alignUp(length, ALLOC_ALIGNMENT);
+ available = heapSource->allocLimit - heapSource->allocPtr;
+
+ /* Try allocating inside the current block. */
+ if (aligned <= available) {
+ addr = heapSource->allocPtr;
+ heapSource->allocPtr += aligned;
+ heapSource->bytesAllocated += aligned;
+ dvmHeapBitmapSetObjectBit(&heapSource->allocBits, addr);
+ return addr;
+ }
+
+ /* Try allocating in a new block. */
+ if (aligned <= BLOCK_SIZE) {
+ addr = allocateBlocks(heapSource, 1);
+ if (addr != NULL) {
+ heapSource->allocLimit = addr + BLOCK_SIZE;
+ heapSource->allocPtr = addr + aligned;
+ heapSource->bytesAllocated += aligned;
+ dvmHeapBitmapSetObjectBit(&heapSource->allocBits, addr);
+ /* TODO(cshapiro): pad out the current block. */
+ }
+ return addr;
+ }
+
+ /* Try allocating in a span of blocks. */
+ blocks = alignUp(aligned, BLOCK_SIZE) / BLOCK_SIZE;
+
+ addr = allocateBlocks(heapSource, blocks);
+ /* Propagate failure upward. */
+ if (addr != NULL) {
+ heapSource->bytesAllocated += aligned;
+ dvmHeapBitmapSetObjectBit(&heapSource->allocBits, addr);
+ /* TODO(cshapiro): pad out free space in the last block. */
+ }
+ return addr;
+}
+
+void *dvmHeapSourceAllocAndGrow(size_t size)
+{
+ return dvmHeapSourceAlloc(size);
+}
+
+/* TODO: refactor along with dvmHeapSourceAlloc */
+void *allocateGray(size_t size)
+{
+ HeapSource *heapSource;
+ void *addr;
+ size_t block;
+
+ /* TODO: add a check that we are in a GC. */
+ heapSource = gDvm.gcHeap->heapSource;
+ addr = dvmHeapSourceAlloc(size);
+ assert(addr != NULL);
+ block = addressToBlock(heapSource, (const u1 *)addr);
+ if (heapSource->queueHead == QUEUE_TAIL) {
+ /*
+ * Forcibly append the underlying block to the queue. This
+ * condition occurs when referents are transported following
+ * the initial trace.
+ */
+ enqueueBlock(heapSource, block);
+ LOG_PROM("forced promoting block %zu %d @ %p", block, heapSource->blockSpace[block], addr);
+ }
+ return addr;
+}
+
+bool dvmHeapSourceContainsAddress(const void *ptr)
+{
+ HeapSource *heapSource = gDvm.gcHeap->heapSource;
+ return dvmHeapBitmapCoversAddress(&heapSource->allocBits, ptr);
+}
+
+/*
+ * Returns true if the given address is within the heap and points to
+ * the header of a live object.
+ */
+bool dvmHeapSourceContains(const void *addr)
+{
+ HeapSource *heapSource;
+ HeapBitmap *bitmap;
+
+ heapSource = gDvm.gcHeap->heapSource;
+ bitmap = &heapSource->allocBits;
+ if (!dvmHeapBitmapCoversAddress(bitmap, addr)) {
+ return false;
+ } else {
+ return dvmHeapBitmapIsObjectBitSet(bitmap, addr);
+ }
+}
+
+bool dvmHeapSourceGetPtrFlag(const void *ptr, enum HeapSourcePtrFlag flag)
+{
+ assert(!"implemented");
+ return false;
+}
+
+size_t dvmHeapSourceChunkSize(const void *ptr)
+{
+ assert(!"implemented");
+ return 0;
+}
+
+size_t dvmHeapSourceFootprint(void)
+{
+ assert(!"implemented");
+ return 0;
+}
+
+/*
+ * Returns the "ideal footprint" which appears to be the number of
+ * bytes currently committed to the heap. This starts out at the
+ * start size of the heap and grows toward the maximum size.
+ */
+size_t dvmHeapSourceGetIdealFootprint(void)
+{
+ return gDvm.gcHeap->heapSource->currentSize;
+}
+
+float dvmGetTargetHeapUtilization(void)
+{
+ return 0.5f;
+}
+
+void dvmSetTargetHeapUtilization(float newTarget)
+{
+ assert(newTarget > 0.0f && newTarget < 1.0f);
+}
+
+size_t dvmMinimumHeapSize(size_t size, bool set)
+{
+ return gDvm.gcHeap->heapSource->minimumSize;
+}
+
+/*
+ * Expands the size of the heap after a collection. At present we
+ * commit the pages for maximum size of the heap so this routine is
+ * just a no-op. Eventually, we will either allocate or commit pages
+ * on an as-need basis.
+ */
+void dvmHeapSourceGrowForUtilization(void)
+{
+ /* do nothing */
+}
+
+void dvmHeapSourceTrim(size_t bytesTrimmed[], size_t arrayLen)
+{
+ /* do nothing */
+}
+
+void dvmHeapSourceWalk(void (*callback)(const void *chunkptr, size_t chunklen,
+ const void *userptr, size_t userlen,
+ void *arg),
+ void *arg)
+{
+ assert(!"implemented");
+}
+
+size_t dvmHeapSourceGetNumHeaps(void)
+{
+ return 1;
+}
+
+bool dvmTrackExternalAllocation(size_t n)
+{
+ /* do nothing */
+ return true;
+}
+
+void dvmTrackExternalFree(size_t n)
+{
+ /* do nothing */
+}
+
+size_t dvmGetExternalBytesAllocated(void)
+{
+ assert(!"implemented");
+ return 0;
+}
+
+void dvmHeapSourceFlip(void)
+{
+ HeapSource *heapSource;
+ size_t i;
+
+ heapSource = gDvm.gcHeap->heapSource;
+
+ /* Reset the block queue. */
+ heapSource->allocBlocks = 0;
+ heapSource->queueSize = 0;
+ heapSource->queueHead = QUEUE_TAIL;
+
+ /* TODO(cshapiro): pad the current (prev) block. */
+
+ heapSource->allocPtr = NULL;
+ heapSource->allocLimit = NULL;
+
+ /* Whiten all allocated blocks. */
+ for (i = 0; i < heapSource->totalBlocks; ++i) {
+ if (heapSource->blockSpace[i] == BLOCK_TO_SPACE) {
+ heapSource->blockSpace[i] = BLOCK_FROM_SPACE;
+ }
+ }
+}
+
+static void room(size_t *alloc, size_t *avail, size_t *total)
+{
+ HeapSource *heapSource;
+
+ heapSource = gDvm.gcHeap->heapSource;
+ *total = heapSource->totalBlocks*BLOCK_SIZE;
+ *alloc = heapSource->allocBlocks*BLOCK_SIZE;
+ *avail = *total - *alloc;
+}
+
+static bool isSpaceInternal(u1 *addr, int space)
+{
+ HeapSource *heapSource;
+ u1 *base, *limit;
+ size_t offset;
+ char space2;
+
+ heapSource = gDvm.gcHeap->heapSource;
+ base = heapSource->blockBase;
+ assert(addr >= base);
+ limit = heapSource->blockBase + heapSource->maximumSize;
+ assert(addr < limit);
+ offset = addr - base;
+ space2 = heapSource->blockSpace[offset >> BLOCK_SHIFT];
+ return space == space2;
+}
+
+static bool fromSpaceContains(const void *addr)
+{
+ return isSpaceInternal((u1 *)addr, BLOCK_FROM_SPACE);
+}
+
+static bool toSpaceContains(const void *addr)
+{
+ return isSpaceInternal((u1 *)addr, BLOCK_TO_SPACE);
+}
+
+/*
+ * Notifies the collector that the object at the given address must
+ * remain stationary during the current collection.
+ */
+static void pinObject(const Object *obj)
+{
+ promoteBlockByAddr(gDvm.gcHeap->heapSource, obj);
+}
+
+static size_t sumHeapBitmap(const HeapBitmap *bitmap)
+{
+ size_t i, sum;
+
+ sum = 0;
+ for (i = 0; i < bitmap->bitsLen >> 2; ++i) {
+ sum += CLZ(bitmap->bits[i]);
+ }
+ return sum;
+}
+
+/*
+ * Miscellaneous functionality.
+ */
+
+static int isForward(const void *addr)
+{
+ return (uintptr_t)addr & 0x1;
+}
+
+static void setForward(const void *toObj, void *fromObj)
+{
+ *(unsigned long *)fromObj = (uintptr_t)toObj | 0x1;
+}
+
+static void* getForward(const void *fromObj)
+{
+ return (void *)((uintptr_t)fromObj & ~0x1);
+}
+
+/* Beware, uses the same encoding as a forwarding pointers! */
+static int isPermanentString(const StringObject *obj) {
+ return (uintptr_t)obj & 0x1;
+}
+
+static void* getPermanentString(const StringObject *obj)
+{
+ return (void *)((uintptr_t)obj & ~0x1);
+}
+
+
+/*
+ * Scavenging and transporting routines follow. A transporter grays
+ * an object. A scavenger blackens an object. We define these
+ * routines for each fundamental object type. Dispatch is performed
+ * in scavengeObject.
+ */
+
+/*
+ * Class object scavenging.
+ */
+static void scavengeClassObject(ClassObject *obj)
+{
+ int i;
+
+ LOG_SCAV("scavengeClassObject(obj=%p)", obj);
+ assert(obj != NULL);
+ assert(obj->obj.clazz != NULL);
+ assert(obj->obj.clazz->descriptor != NULL);
+ assert(!strcmp(obj->obj.clazz->descriptor, "Ljava/lang/Class;"));
+ assert(obj->descriptor != NULL);
+ LOG_SCAV("scavengeClassObject: descriptor='%s',vtableCount=%zu",
+ obj->descriptor, obj->vtableCount);
+ /* Delegate class object and instance field scavenging. */
+ scavengeDataObject((Object *)obj);
+ /* Scavenge the array element class object. */
+ if (IS_CLASS_FLAG_SET(obj, CLASS_ISARRAY)) {
+ scavengeReference((Object **)(void *)&obj->elementClass);
+ }
+ /* Scavenge the superclass. */
+ scavengeReference((Object **)(void *)&obj->super);
+ /* Scavenge the class loader. */
+ scavengeReference(&obj->classLoader);
+ /* Scavenge static fields. */
+ for (i = 0; i < obj->sfieldCount; ++i) {
+ char ch = obj->sfields[i].field.signature[0];
+ if (ch == '[' || ch == 'L') {
+ scavengeReference((Object **)(void *)&obj->sfields[i].value.l);
+ }
+ }
+ /* Scavenge interface class objects. */
+ for (i = 0; i < obj->interfaceCount; ++i) {
+ scavengeReference((Object **) &obj->interfaces[i]);
+ }
+}
+
+/*
+ * Array object scavenging.
+ */
+static size_t scavengeArrayObject(ArrayObject *array)
+{
+ size_t i, length;
+
+ LOG_SCAV("scavengeArrayObject(array=%p)", array);
+ /* Scavenge the class object. */
+ assert(toSpaceContains(array));
+ assert(array != NULL);
+ assert(array->obj.clazz != NULL);
+ scavengeReference((Object **) array);
+ length = dvmArrayObjectSize(array);
+ /* Scavenge the array contents. */
+ if (IS_CLASS_FLAG_SET(array->obj.clazz, CLASS_ISOBJECTARRAY)) {
+ Object **contents = (Object **)array->contents;
+ for (i = 0; i < array->length; ++i) {
+ scavengeReference(&contents[i]);
+ }
+ }
+ return length;
+}
+
+/*
+ * Reference object scavenging.
+ */
+
+static int getReferenceFlags(const Object *obj)
+{
+ int flags;
+
+ flags = CLASS_ISREFERENCE |
+ CLASS_ISWEAKREFERENCE |
+ CLASS_ISPHANTOMREFERENCE;
+ return GET_CLASS_FLAG_GROUP(obj->clazz, flags);
+}
+
+static int isSoftReference(const Object *obj)
+{
+ return getReferenceFlags(obj) == CLASS_ISREFERENCE;
+}
+
+static int isWeakReference(const Object *obj)
+{
+ return getReferenceFlags(obj) & CLASS_ISWEAKREFERENCE;
+}
+
+#ifndef NDEBUG
+static bool isPhantomReference(const Object *obj)
+{
+ return getReferenceFlags(obj) & CLASS_ISPHANTOMREFERENCE;
+}
+#endif
+
+/*
+ * Returns true if the reference was registered with a reference queue
+ * but has not yet been appended to it.
+ */
+static bool isReferenceEnqueuable(const Object *ref)
+{
+ Object *queue, *queueNext;
+
+ queue = dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queue);
+ queueNext = dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queueNext);
+ if (queue == NULL || queueNext != NULL) {
+ /*
+ * There is no queue, or the reference has already
+ * been enqueued. The Reference.enqueue() method
+ * will do nothing even if we call it.
+ */
+ return false;
+ }
+
+ /*
+ * We need to call enqueue(), but if we called it from
+ * here we'd probably deadlock. Schedule a call.
+ */
+ return true;
+}
+
+/*
+ * Schedules a reference to be appended to its reference queue.
+ */
+static void enqueueReference(Object *ref)
+{
+ assert(ref != NULL);
+ assert(dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queue) != NULL);
+ assert(dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queueNext) == NULL);
+ if (!dvmHeapAddRefToLargeTable(&gDvm.gcHeap->referenceOperations, ref)) {
+ LOGE("no room for any more reference operations");
+ dvmAbort();
+ }
+}
+
+/*
+ * Sets the referent field of a reference object to NULL.
+ */
+static void clearReference(Object *obj)
+{
+ dvmSetFieldObject(obj, gDvm.offJavaLangRefReference_referent, NULL);
+}
+
+/*
+ * Clears reference objects with white referents.
+ */
+void clearWhiteReferences(Object **list)
+{
+ size_t referentOffset, queueNextOffset;
+ bool doSignal;
+
+ queueNextOffset = gDvm.offJavaLangRefReference_queueNext;
+ referentOffset = gDvm.offJavaLangRefReference_referent;
+ doSignal = false;
+ while (*list != NULL) {
+ Object *ref = *list;
+ JValue *field = dvmFieldPtr(ref, referentOffset);
+ Object *referent = field->l;
+ *list = dvmGetFieldObject(ref, queueNextOffset);
+ dvmSetFieldObject(ref, queueNextOffset, NULL);
+ assert(referent != NULL);
+ if (isForward(referent->clazz)) {
+ field->l = referent = getForward(referent->clazz);
+ continue;
+ }
+ if (fromSpaceContains(referent)) {
+ /* Referent is white, clear it. */
+ clearReference(ref);
+ if (isReferenceEnqueuable(ref)) {
+ enqueueReference(ref);
+ doSignal = true;
+ }
+ }
+ }
+ /*
+ * If we cleared a reference with a reference queue we must notify
+ * the heap worker to append the reference.
+ */
+ if (doSignal) {
+ dvmSignalHeapWorker(false);
+ }
+ assert(*list == NULL);
+}
+
+/*
+ * Blackens referents subject to the soft reference preservation
+ * policy.
+ */
+void preserveSoftReferences(Object **list)
+{
+ Object *ref;
+ Object *prev, *next;
+ size_t referentOffset, queueNextOffset;
+ unsigned counter;
+ bool white;
+
+ queueNextOffset = gDvm.offJavaLangRefReference_queueNext;
+ referentOffset = gDvm.offJavaLangRefReference_referent;
+ counter = 0;
+ prev = next = NULL;
+ ref = *list;
+ while (ref != NULL) {
+ JValue *field = dvmFieldPtr(ref, referentOffset);
+ Object *referent = field->l;
+ next = dvmGetFieldObject(ref, queueNextOffset);
+ assert(referent != NULL);
+ if (isForward(referent->clazz)) {
+ /* Referent is black. */
+ field->l = referent = getForward(referent->clazz);
+ white = false;
+ } else {
+ white = fromSpaceContains(referent);
+ }
+ if (!white && ((++counter) & 1)) {
+ /* Referent is white and biased toward saving, gray it. */
+ scavengeReference((Object **)(void *)&field->l);
+ white = true;
+ }
+ if (white) {
+ /* Referent is black, unlink it. */
+ if (prev != NULL) {
+ dvmSetFieldObject(ref, queueNextOffset, NULL);
+ dvmSetFieldObject(prev, queueNextOffset, next);
+ }
+ } else {
+ /* Referent is white, skip over it. */
+ prev = ref;
+ }
+ ref = next;
+ }
+ /*
+ * Restart the trace with the newly gray references added to the
+ * root set.
+ */
+ scavengeBlockQueue();
+}
+
+void processFinalizableReferences(void)
+{
+ HeapRefTable newPendingRefs;
+ LargeHeapRefTable *finRefs = gDvm.gcHeap->finalizableRefs;
+ Object **ref;
+ Object **lastRef;
+ size_t totalPendCount;
+
+ /*
+ * All strongly, reachable objects are black.
+ * Any white finalizable objects need to be finalized.
+ */
+
+ /* Create a table that the new pending refs will
+ * be added to.
+ */
+ if (!dvmHeapInitHeapRefTable(&newPendingRefs)) {
+ //TODO: mark all finalizable refs and hope that
+ // we can schedule them next time. Watch out,
+ // because we may be expecting to free up space
+ // by calling finalizers.
+ LOG_REF("no room for pending finalizations\n");
+ dvmAbort();
+ }
+
+ /*
+ * Walk through finalizableRefs and move any white references to
+ * the list of new pending refs.
+ */
+ totalPendCount = 0;
+ while (finRefs != NULL) {
+ Object **gapRef;
+ size_t newPendCount = 0;
+
+ gapRef = ref = finRefs->refs.table;
+ lastRef = finRefs->refs.nextEntry;
+ while (ref < lastRef) {
+ if (fromSpaceContains(*ref)) {
+ if (!dvmHeapAddToHeapRefTable(&newPendingRefs, *ref)) {
+ //TODO: add the current table and allocate
+ // a new, smaller one.
+ LOG_REF("no room for any more pending finalizations: %zd\n",
+ dvmHeapNumHeapRefTableEntries(&newPendingRefs));
+ dvmAbort();
+ }
+ newPendCount++;
+ } else {
+ /* This ref is black, so will remain on finalizableRefs.
+ */
+ if (newPendCount > 0) {
+ /* Copy it up to fill the holes.
+ */
+ *gapRef++ = *ref;
+ } else {
+ /* No holes yet; don't bother copying.
+ */
+ gapRef++;
+ }
+ }
+ ref++;
+ }
+ finRefs->refs.nextEntry = gapRef;
+ //TODO: if the table is empty when we're done, free it.
+ totalPendCount += newPendCount;
+ finRefs = finRefs->next;
+ }
+ LOG_REF("%zd finalizers triggered.\n", totalPendCount);
+ if (totalPendCount == 0) {
+ /* No objects required finalization.
+ * Free the empty temporary table.
+ */
+ dvmClearReferenceTable(&newPendingRefs);
+ return;
+ }
+
+ /* Add the new pending refs to the main list.
+ */
+ if (!dvmHeapAddTableToLargeTable(&gDvm.gcHeap->pendingFinalizationRefs,
+ &newPendingRefs))
+ {
+ LOG_REF("can't insert new pending finalizations\n");
+ dvmAbort();
+ }
+
+ //TODO: try compacting the main list with a memcpy loop
+
+ /* Blacken the refs we just moved; we don't want them or their
+ * children to get swept yet.
+ */
+ ref = newPendingRefs.table;
+ lastRef = newPendingRefs.nextEntry;
+ assert(ref < lastRef);
+ HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_FINALIZING, 0);
+ while (ref < lastRef) {
+ scavengeReference(ref);
+ ref++;
+ }
+ HPROF_CLEAR_GC_SCAN_STATE();
+ scavengeBlockQueue();
+ dvmSignalHeapWorker(false);
+}
+
+/*
+ * If a reference points to from-space and has been forwarded, we snap
+ * the pointer to its new to-space address. If the reference points
+ * to an unforwarded from-space address we must enqueue the reference
+ * for later processing. TODO: implement proper reference processing
+ * and move the referent scavenging elsewhere.
+ */
+static void scavengeReferenceObject(Object *obj)
+{
+ Object *referent;
+ Object **queue;
+ size_t referentOffset, queueNextOffset;
+
+ assert(obj != NULL);
+ LOG_SCAV("scavengeReferenceObject(obj=%p),'%s'", obj, obj->clazz->descriptor);
+ scavengeDataObject(obj);
+ referentOffset = gDvm.offJavaLangRefReference_referent;
+ referent = dvmGetFieldObject(obj, referentOffset);
+ if (referent == NULL || toSpaceContains(referent)) {
+ return;
+ }
+ if (isSoftReference(obj)) {
+ queue = &gDvm.gcHeap->softReferences;
+ } else if (isWeakReference(obj)) {
+ queue = &gDvm.gcHeap->weakReferences;
+ } else {
+ assert(isPhantomReference(obj));
+ queue = &gDvm.gcHeap->phantomReferences;
+ }
+ queueNextOffset = gDvm.offJavaLangRefReference_queueNext;
+ dvmSetFieldObject(obj, queueNextOffset, *queue);
+ *queue = obj;
+ LOG_SCAV("scavengeReferenceObject: enqueueing %p", obj);
+}
+
+/*
+ * Data object scavenging.
+ */
+static void scavengeDataObject(Object *obj)
+{
+ ClassObject *clazz;
+ int i;
+
+ // LOG_SCAV("scavengeDataObject(obj=%p)", obj);
+ assert(obj != NULL);
+ assert(obj->clazz != NULL);
+ assert(obj->clazz->objectSize != 0);
+ assert(toSpaceContains(obj));
+ /* Scavenge the class object. */
+ clazz = obj->clazz;
+ scavengeReference((Object **) obj);
+ /* Scavenge instance fields. */
+ if (clazz->refOffsets != CLASS_WALK_SUPER) {
+ size_t refOffsets = clazz->refOffsets;
+ while (refOffsets != 0) {
+ size_t rshift = CLZ(refOffsets);
+ size_t offset = CLASS_OFFSET_FROM_CLZ(rshift);
+ Object **ref = (Object **)((u1 *)obj + offset);
+ scavengeReference(ref);
+ refOffsets &= ~(CLASS_HIGH_BIT >> rshift);
+ }
+ } else {
+ for (; clazz != NULL; clazz = clazz->super) {
+ InstField *field = clazz->ifields;
+ for (i = 0; i < clazz->ifieldRefCount; ++i, ++field) {
+ size_t offset = field->byteOffset;
+ Object **ref = (Object **)((u1 *)obj + offset);
+ scavengeReference(ref);
+ }
+ }
+ }
+}
+
+static Object *transportObject(const Object *fromObj)
+{
+ Object *toObj;
+ size_t allocSize, copySize;
+
+ LOG_TRAN("transportObject(fromObj=%p) allocBlocks=%zu",
+ fromObj,
+ gDvm.gcHeap->heapSource->allocBlocks);
+ assert(fromObj != NULL);
+ assert(fromSpaceContains(fromObj));
+ allocSize = copySize = objectSize(fromObj);
+ if (LW_HASH_STATE(fromObj->lock) != LW_HASH_STATE_UNHASHED) {
+ /*
+ * The object has been hashed or hashed and moved. We must
+ * reserve an additional word for a hash code.
+ */
+ allocSize += sizeof(u4);
+ }
+ if (LW_HASH_STATE(fromObj->lock) == LW_HASH_STATE_HASHED_AND_MOVED) {
+ /*
+ * The object has its hash code allocated. Ensure the hash
+ * code is copied along with the instance data.
+ */
+ copySize += sizeof(u4);
+ }
+ /* TODO(cshapiro): don't copy, re-map large data objects. */
+ assert(copySize <= allocSize);
+ toObj = allocateGray(allocSize);
+ assert(toObj != NULL);
+ assert(toSpaceContains(toObj));
+ memcpy(toObj, fromObj, copySize);
+ if (LW_HASH_STATE(fromObj->lock) == LW_HASH_STATE_HASHED) {
+ /*
+ * The object has had its hash code exposed. Append it to the
+ * instance and set a bit so we know to look for it there.
+ */
+ *(u4 *)(((char *)toObj) + copySize) = (u4)fromObj >> 3;
+ toObj->lock |= LW_HASH_STATE_HASHED_AND_MOVED << LW_HASH_STATE_SHIFT;
+ }
+ LOG_TRAN("transportObject: from %p/%zu to %p/%zu (%zu,%zu) %s",
+ fromObj, addressToBlock(gDvm.gcHeap->heapSource,fromObj),
+ toObj, addressToBlock(gDvm.gcHeap->heapSource,toObj),
+ copySize, allocSize, copySize < allocSize ? "DIFFERENT" : "");
+ return toObj;
+}
+
+/*
+ * Generic reference scavenging.
+ */
+
+/*
+ * Given a reference to an object, the scavenge routine will gray the
+ * reference. Any objects pointed to by the scavenger object will be
+ * transported to new space and a forwarding pointer will be installed
+ * in the header of the object.
+ */
+
+/*
+ * Blacken the given pointer. If the pointer is in from space, it is
+ * transported to new space. If the object has a forwarding pointer
+ * installed it has already been transported and the referent is
+ * snapped to the new address.
+ */
+static void scavengeReference(Object **obj)
+{
+ ClassObject *clazz;
+ Object *fromObj, *toObj;
+
+ assert(obj);
+
+ if (*obj == NULL) return;
+
+ assert(dvmIsValidObject(*obj));
+
+ /* The entire block is black. */
+ if (toSpaceContains(*obj)) {
+ LOG_SCAV("scavengeReference skipping pinned object @ %p", *obj);
+ return;
+ }
+ LOG_SCAV("scavengeReference(*obj=%p)", *obj);
+
+ assert(fromSpaceContains(*obj));
+
+ clazz = (*obj)->clazz;
+
+ if (isForward(clazz)) {
+ // LOG_SCAV("forwarding %p @ %p to %p", *obj, obj, (void *)((uintptr_t)clazz & ~0x1));
+ *obj = (Object *)getForward(clazz);
+ return;
+ }
+ fromObj = *obj;
+ if (clazz == NULL) {
+ // LOG_SCAV("scavangeReference %p has a NULL class object", fromObj);
+ assert(!"implemented");
+ toObj = NULL;
+ } else {
+ toObj = transportObject(fromObj);
+ }
+ setForward(toObj, fromObj);
+ *obj = (Object *)toObj;
+}
+
+/*
+ * Generic object scavenging.
+ */
+static void scavengeObject(Object *obj)
+{
+ ClassObject *clazz;
+
+ assert(obj != NULL);
+ assert(obj->clazz != NULL);
+ assert(!((uintptr_t)obj->clazz & 0x1));
+ clazz = obj->clazz;
+ if (clazz == gDvm.classJavaLangClass) {
+ scavengeClassObject((ClassObject *)obj);
+ } else if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) {
+ scavengeArrayObject((ArrayObject *)obj);
+ } else if (IS_CLASS_FLAG_SET(clazz, CLASS_ISREFERENCE)) {
+ scavengeReferenceObject(obj);
+ } else {
+ scavengeDataObject(obj);
+ }
+}
+
+/*
+ * External root scavenging routines.
+ */
+
+static void pinHashTableEntries(HashTable *table)
+{
+ HashEntry *entry;
+ void *obj;
+ int i;
+
+ LOG_PIN(">>> pinHashTableEntries(table=%p)", table);
+ if (table == NULL) {
+ return;
+ }
+ dvmHashTableLock(table);
+ for (i = 0; i < table->tableSize; ++i) {
+ entry = &table->pEntries[i];
+ obj = entry->data;
+ if (obj == NULL || obj == HASH_TOMBSTONE) {
+ continue;
+ }
+ pinObject(entry->data);
+ }
+ dvmHashTableUnlock(table);
+ LOG_PIN("<<< pinHashTableEntries(table=%p)", table);
+}
+
+static void pinPrimitiveClasses(void)
+{
+ size_t length;
+ size_t i;
+
+ length = ARRAYSIZE(gDvm.primitiveClass);
+ for (i = 0; i < length; i++) {
+ if (gDvm.primitiveClass[i] != NULL) {
+ pinObject((Object *)gDvm.primitiveClass[i]);
+ }
+ }
+}
+
+/*
+ * Scavenge interned strings. Permanent interned strings will have
+ * been pinned and are therefore ignored. Non-permanent strings that
+ * have been forwarded are snapped. All other entries are removed.
+ */
+static void scavengeInternedStrings(void)
+{
+ HashTable *table;
+ HashEntry *entry;
+ Object *obj;
+ int i;
+
+ table = gDvm.internedStrings;
+ if (table == NULL) {
+ return;
+ }
+ dvmHashTableLock(table);
+ for (i = 0; i < table->tableSize; ++i) {
+ entry = &table->pEntries[i];
+ obj = (Object *)entry->data;
+ if (obj == NULL || obj == HASH_TOMBSTONE) {
+ continue;
+ } else if (!isPermanentString((StringObject *)obj)) {
+ // LOG_SCAV("entry->data=%p", entry->data);
+ LOG_SCAV(">>> string obj=%p", entry->data);
+ /* TODO(cshapiro): detach white string objects */
+ scavengeReference((Object **)(void *)&entry->data);
+ LOG_SCAV("<<< string obj=%p", entry->data);
+ }
+ }
+ dvmHashTableUnlock(table);
+}
+
+static void pinInternedStrings(void)
+{
+ HashTable *table;
+ HashEntry *entry;
+ Object *obj;
+ int i;
+
+ table = gDvm.internedStrings;
+ if (table == NULL) {
+ return;
+ }
+ dvmHashTableLock(table);
+ for (i = 0; i < table->tableSize; ++i) {
+ entry = &table->pEntries[i];
+ obj = (Object *)entry->data;
+ if (obj == NULL || obj == HASH_TOMBSTONE) {
+ continue;
+ } else if (isPermanentString((StringObject *)obj)) {
+ obj = (Object *)getPermanentString((StringObject*)obj);
+ LOG_PROM(">>> pin string obj=%p", obj);
+ pinObject(obj);
+ LOG_PROM("<<< pin string obj=%p", obj);
+ }
+ }
+ dvmHashTableUnlock(table);
+}
+
+/*
+ * At present, reference tables contain references that must not be
+ * moved by the collector. Instead of scavenging each reference in
+ * the table we pin each referenced object.
+ */
+static void pinReferenceTable(const ReferenceTable *table)
+{
+ Object **entry;
+
+ assert(table != NULL);
+ assert(table->table != NULL);
+ assert(table->nextEntry != NULL);
+ for (entry = table->table; entry < table->nextEntry; ++entry) {
+ assert(entry != NULL);
+ assert(!isForward(*entry));
+ pinObject(*entry);
+ }
+}
+
+static void scavengeLargeHeapRefTable(LargeHeapRefTable *table)
+{
+ for (; table != NULL; table = table->next) {
+ Object **ref = table->refs.table;
+ for (; ref < table->refs.nextEntry; ++ref) {
+ scavengeReference(ref);
+ }
+ }
+}
+
+/* This code was copied from Thread.c */
+static void scavengeThreadStack(Thread *thread)
+{
+ const u4 *framePtr;
+#if WITH_EXTRA_GC_CHECKS > 1
+ bool first = true;
+#endif
+
+ framePtr = (const u4 *)thread->curFrame;
+ while (framePtr != NULL) {
+ const StackSaveArea *saveArea;
+ const Method *method;
+
+ saveArea = SAVEAREA_FROM_FP(framePtr);
+ method = saveArea->method;
+ if (method != NULL && !dvmIsNativeMethod(method)) {
+#ifdef COUNT_PRECISE_METHODS
+ /* the GC is running, so no lock required */
+ if (dvmPointerSetAddEntry(gDvm.preciseMethods, method))
+ LOG_SCAV("PGC: added %s.%s %p\n",
+ method->clazz->descriptor, method->name, method);
+#endif
+#if WITH_EXTRA_GC_CHECKS > 1
+ /*
+ * May also want to enable the memset() in the "invokeMethod"
+ * goto target in the portable interpreter. That sets the stack
+ * to a pattern that makes referring to uninitialized data
+ * very obvious.
+ */
+
+ if (first) {
+ /*
+ * First frame, isn't native, check the "alternate" saved PC
+ * as a sanity check.
+ *
+ * It seems like we could check the second frame if the first
+ * is native, since the PCs should be the same. It turns out
+ * this doesn't always work. The problem is that we could
+ * have calls in the sequence:
+ * interp method #2
+ * native method
+ * interp method #1
+ *
+ * and then GC while in the native method after returning
+ * from interp method #2. The currentPc on the stack is
+ * for interp method #1, but thread->currentPc2 is still
+ * set for the last thing interp method #2 did.
+ *
+ * This can also happen in normal execution:
+ * - sget-object on not-yet-loaded class
+ * - class init updates currentPc2
+ * - static field init is handled by parsing annotations;
+ * static String init requires creation of a String object,
+ * which can cause a GC
+ *
+ * Essentially, any pattern that involves executing
+ * interpreted code and then causes an allocation without
+ * executing instructions in the original method will hit
+ * this. These are rare enough that the test still has
+ * some value.
+ */
+ if (saveArea->xtra.currentPc != thread->currentPc2) {
+ LOGW("PGC: savedPC(%p) != current PC(%p), %s.%s ins=%p\n",
+ saveArea->xtra.currentPc, thread->currentPc2,
+ method->clazz->descriptor, method->name, method->insns);
+ if (saveArea->xtra.currentPc != NULL)
+ LOGE(" pc inst = 0x%04x\n", *saveArea->xtra.currentPc);
+ if (thread->currentPc2 != NULL)
+ LOGE(" pc2 inst = 0x%04x\n", *thread->currentPc2);
+ dvmDumpThread(thread, false);
+ }
+ } else {
+ /*
+ * It's unusual, but not impossible, for a non-first frame
+ * to be at something other than a method invocation. For
+ * example, if we do a new-instance on a nonexistent class,
+ * we'll have a lot of class loader activity on the stack
+ * above the frame with the "new" operation. Could also
+ * happen while we initialize a Throwable when an instruction
+ * fails.
+ *
+ * So there's not much we can do here to verify the PC,
+ * except to verify that it's a GC point.
+ */
+ }
+ assert(saveArea->xtra.currentPc != NULL);
+#endif
+
+ const RegisterMap* pMap;
+ const u1* regVector;
+ int i;
+
+ Method* nonConstMethod = (Method*) method; // quiet gcc
+ pMap = dvmGetExpandedRegisterMap(nonConstMethod);
+
+ //LOG_SCAV("PGC: %s.%s\n", method->clazz->descriptor, method->name);
+
+ if (pMap != NULL) {
+ /* found map, get registers for this address */
+ int addr = saveArea->xtra.currentPc - method->insns;
+ regVector = dvmRegisterMapGetLine(pMap, addr);
+ /*
+ if (regVector == NULL) {
+ LOG_SCAV("PGC: map but no entry for %s.%s addr=0x%04x\n",
+ method->clazz->descriptor, method->name, addr);
+ } else {
+ LOG_SCAV("PGC: found map for %s.%s 0x%04x (t=%d)\n",
+ method->clazz->descriptor, method->name, addr,
+ thread->threadId);
+ }
+ */
+ } else {
+ /*
+ * No map found. If precise GC is disabled this is
+ * expected -- we don't create pointers to the map data even
+ * if it's present -- but if it's enabled it means we're
+ * unexpectedly falling back on a conservative scan, so it's
+ * worth yelling a little.
+ */
+ if (gDvm.preciseGc) {
+ LOG_SCAV("PGC: no map for %s.%s\n", method->clazz->descriptor, method->name);
+ }
+ regVector = NULL;
+ }
+ if (regVector == NULL) {
+ /*
+ * There are no roots to scavenge. Skip over the entire frame.
+ */
+ framePtr += method->registersSize;
+ } else {
+ /*
+ * Precise scan. v0 is at the lowest address on the
+ * interpreted stack, and is the first bit in the register
+ * vector, so we can walk through the register map and
+ * memory in the same direction.
+ *
+ * A '1' bit indicates a live reference.
+ */
+ u2 bits = 1 << 1;
+ for (i = method->registersSize - 1; i >= 0; i--) {
+ u4 rval = *framePtr;
+
+ bits >>= 1;
+ if (bits == 1) {
+ /* set bit 9 so we can tell when we're empty */
+ bits = *regVector++ | 0x0100;
+ }
+
+ if (rval != 0 && (bits & 0x01) != 0) {
+ /*
+ * Non-null, register marked as live reference. This
+ * should always be a valid object.
+ */
+#if WITH_EXTRA_GC_CHECKS > 0
+ if ((rval & 0x3) != 0 || !dvmIsValidObject((Object*) rval)) {
+ /* this is very bad */
+ LOGE("PGC: invalid ref in reg %d: 0x%08x\n",
+ method->registersSize-1 - i, rval);
+ } else
+#endif
+ {
+
+ // LOG_SCAV("stack reference %u@%p", *framePtr, framePtr);
+ /* dvmMarkObjectNonNull((Object *)rval); */
+ scavengeReference((Object **) framePtr);
+ }
+ } else {
+ /*
+ * Null or non-reference, do nothing at all.
+ */
+#if WITH_EXTRA_GC_CHECKS > 1
+ if (dvmIsValidObject((Object*) rval)) {
+ /* this is normal, but we feel chatty */
+ LOGD("PGC: ignoring valid ref in reg %d: 0x%08x\n",
+ method->registersSize-1 - i, rval);
+ }
+#endif
+ }
+ ++framePtr;
+ }
+ dvmReleaseRegisterMapLine(pMap, regVector);
+ }
+ }
+ /* else this is a break frame and there is nothing to gray, or
+ * this is a native method and the registers are just the "ins",
+ * copied from various registers in the caller's set.
+ */
+
+#if WITH_EXTRA_GC_CHECKS > 1
+ first = false;
+#endif
+
+ /* Don't fall into an infinite loop if things get corrupted.
+ */
+ assert((uintptr_t)saveArea->prevFrame > (uintptr_t)framePtr ||
+ saveArea->prevFrame == NULL);
+ framePtr = saveArea->prevFrame;
+ }
+}
+
+static void scavengeThread(Thread *thread)
+{
+ // LOG_SCAV("scavengeThread(thread=%p)", thread);
+
+ // LOG_SCAV("Scavenging threadObj=%p", thread->threadObj);
+ scavengeReference(&thread->threadObj);
+
+ // LOG_SCAV("Scavenging exception=%p", thread->exception);
+ scavengeReference(&thread->exception);
+
+ scavengeThreadStack(thread);
+}
+
+static void scavengeThreadList(void)
+{
+ Thread *thread;
+
+ dvmLockThreadList(dvmThreadSelf());
+ thread = gDvm.threadList;
+ while (thread) {
+ scavengeThread(thread);
+ thread = thread->next;
+ }
+ dvmUnlockThreadList();
+}
+
+static void pinThreadStack(const Thread *thread)
+{
+ const u4 *framePtr;
+ const StackSaveArea *saveArea;
+ Method *method;
+ const char *shorty;
+ Object *obj;
+ int i;
+
+ saveArea = NULL;
+ framePtr = (const u4 *)thread->curFrame;
+ for (; framePtr != NULL; framePtr = saveArea->prevFrame) {
+ saveArea = SAVEAREA_FROM_FP(framePtr);
+ method = (Method *)saveArea->method;
+ if (method != NULL && dvmIsNativeMethod(method)) {
+ /*
+ * This is native method, pin its arguments.
+ *
+ * For purposes of graying references, we don't need to do
+ * anything here, because all of the native "ins" were copied
+ * from registers in the caller's stack frame and won't be
+ * changed (an interpreted method can freely use registers
+ * with parameters like any other register, but natives don't
+ * work that way).
+ *
+ * However, we need to ensure that references visible to
+ * native methods don't move around. We can do a precise scan
+ * of the arguments by examining the method signature.
+ */
+ LOG_PIN("+++ native scan %s.%s\n",
+ method->clazz->descriptor, method->name);
+ assert(method->registersSize == method->insSize);
+ if (!dvmIsStaticMethod(method)) {
+ /* grab the "this" pointer */
+ obj = (Object *)*framePtr++;
+ if (obj == NULL) {
+ /*
+ * This can happen for the "fake" entry frame inserted
+ * for threads created outside the VM. There's no actual
+ * call so there's no object. If we changed the fake
+ * entry method to be declared "static" then this
+ * situation should never occur.
+ */
+ } else {
+ assert(dvmIsValidObject(obj));
+ pinObject(obj);
+ }
+ }
+ shorty = method->shorty+1; // skip return value
+ for (i = method->registersSize - 1; i >= 0; i--, framePtr++) {
+ switch (*shorty++) {
+ case 'L':
+ obj = (Object *)*framePtr;
+ if (obj != NULL) {
+ assert(dvmIsValidObject(obj));
+ pinObject(obj);
+ }
+ break;
+ case 'D':
+ case 'J':
+ framePtr++;
+ break;
+ default:
+ /* 32-bit non-reference value */
+ obj = (Object *)*framePtr; // debug, remove
+ if (dvmIsValidObject(obj)) { // debug, remove
+ /* if we see a lot of these, our scan might be off */
+ LOG_PIN("+++ did NOT pin obj %p\n", obj);
+ }
+ break;
+ }
+ }
+ } else if (method != NULL && !dvmIsNativeMethod(method)) {
+ const RegisterMap* pMap = dvmGetExpandedRegisterMap(method);
+ const u1* regVector = NULL;
+
+ LOGI("conservative : %s.%s\n", method->clazz->descriptor, method->name);
+
+ if (pMap != NULL) {
+ int addr = saveArea->xtra.currentPc - method->insns;
+ regVector = dvmRegisterMapGetLine(pMap, addr);
+ }
+ if (regVector == NULL) {
+ /*
+ * No register info for this frame, conservatively pin.
+ */
+ for (i = 0; i < method->registersSize; ++i) {
+ u4 regValue = framePtr[i];
+ if (regValue != 0 && (regValue & 0x3) == 0 && dvmIsValidObject((Object *)regValue)) {
+ pinObject((Object *)regValue);
+ }
+ }
+ }
+ }
+ /*
+ * Don't fall into an infinite loop if things get corrupted.
+ */
+ assert((uintptr_t)saveArea->prevFrame > (uintptr_t)framePtr ||
+ saveArea->prevFrame == NULL);
+ }
+}
+
+static void pinThread(const Thread *thread)
+{
+ assert(thread != NULL);
+ LOG_PIN("pinThread(thread=%p)", thread);
+
+ LOG_PIN("Pin native method arguments");
+ pinThreadStack(thread);
+
+ LOG_PIN("Pin internalLocalRefTable");
+ pinReferenceTable(&thread->internalLocalRefTable);
+
+ LOG_PIN("Pin jniLocalRefTable");
+ pinReferenceTable(&thread->jniLocalRefTable);
+
+ /* Can the check be pushed into the promote routine? */
+ if (thread->jniMonitorRefTable.table) {
+ LOG_PIN("Pin jniMonitorRefTable");
+ pinReferenceTable(&thread->jniMonitorRefTable);
+ }
+}
+
+static void pinThreadList(void)
+{
+ Thread *thread;
+
+ dvmLockThreadList(dvmThreadSelf());
+ thread = gDvm.threadList;
+ while (thread) {
+ pinThread(thread);
+ thread = thread->next;
+ }
+ dvmUnlockThreadList();
+}
+
+/*
+ * Heap block scavenging.
+ */
+
+/*
+ * Scavenge objects in the current block. Scavenging terminates when
+ * the pointer reaches the highest address in the block or when a run
+ * of zero words that continues to the highest address is reached.
+ */
+static void scavengeBlock(HeapSource *heapSource, size_t block)
+{
+ u1 *cursor;
+ u1 *end;
+ size_t size;
+
+ LOG_SCAV("scavengeBlock(heapSource=%p,block=%zu)", heapSource, block);
+
+ assert(heapSource != NULL);
+ assert(block < heapSource->totalBlocks);
+ assert(heapSource->blockSpace[block] == BLOCK_TO_SPACE);
+
+ cursor = blockToAddress(heapSource, block);
+ end = cursor + BLOCK_SIZE;
+ LOG_SCAV("scavengeBlock start=%p, end=%p", cursor, end);
+
+ /* Parse and scavenge the current block. */
+ size = 0;
+ while (cursor < end) {
+ u4 word = *(u4 *)cursor;
+ if (word != 0) {
+ scavengeObject((Object *)cursor);
+ size = objectSize((Object *)cursor);
+ size = alignUp(size, ALLOC_ALIGNMENT);
+ cursor += size;
+ } else {
+ /* Check for padding. */
+ while (*(u4 *)cursor == 0) {
+ cursor += 4;
+ if (cursor == end) break;
+ }
+ /* Punt if something went wrong. */
+ assert(cursor == end);
+ }
+ }
+}
+
+static size_t objectSize(const Object *obj)
+{
+ size_t size;
+
+ assert(obj != NULL);
+ assert(obj->clazz != NULL);
+ if (obj->clazz == gDvm.classJavaLangClass) {
+ size = dvmClassObjectSize((ClassObject *)obj);
+ } else if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISARRAY)) {
+ size = dvmArrayObjectSize((ArrayObject *)obj);
+ } else {
+ assert(obj->clazz->objectSize != 0);
+ size = obj->clazz->objectSize;
+ }
+ if (LW_HASH_STATE(obj->lock) == LW_HASH_STATE_HASHED_AND_MOVED) {
+ size += sizeof(u4);
+ }
+ return size;
+}
+
+static void verifyBlock(HeapSource *heapSource, size_t block)
+{
+ u1 *cursor;
+ u1 *end;
+ size_t size;
+
+ // LOG_VER("verifyBlock(heapSource=%p,block=%zu)", heapSource, block);
+
+ assert(heapSource != NULL);
+ assert(block < heapSource->totalBlocks);
+ assert(heapSource->blockSpace[block] == BLOCK_TO_SPACE);
+
+ cursor = blockToAddress(heapSource, block);
+ end = cursor + BLOCK_SIZE;
+ // LOG_VER("verifyBlock start=%p, end=%p", cursor, end);
+
+ /* Parse and scavenge the current block. */
+ size = 0;
+ while (cursor < end) {
+ u4 word = *(u4 *)cursor;
+ if (word != 0) {
+ dvmVerifyObject((Object *)cursor);
+ size = objectSize((Object *)cursor);
+ size = alignUp(size, ALLOC_ALIGNMENT);
+ cursor += size;
+ } else {
+ /* Check for padding. */
+ while (*(unsigned long *)cursor == 0) {
+ cursor += 4;
+ if (cursor == end) break;
+ }
+ /* Punt if something went wrong. */
+ assert(cursor == end);
+ }
+ }
+}
+
+static void describeBlockQueue(const HeapSource *heapSource)
+{
+ size_t block, count;
+ char space;
+
+ block = heapSource->queueHead;
+ count = 0;
+ LOG_SCAV(">>> describeBlockQueue(heapSource=%p)", heapSource);
+ /* Count the number of blocks enqueued. */
+ while (block != QUEUE_TAIL) {
+ block = heapSource->blockQueue[block];
+ ++count;
+ }
+ LOG_SCAV("blockQueue %zu elements, enqueued %zu",
+ count, heapSource->queueSize);
+ block = heapSource->queueHead;
+ while (block != QUEUE_TAIL) {
+ space = heapSource->blockSpace[block];
+ LOG_SCAV("block=%zu@%p,space=%zu", block, blockToAddress(heapSource,block), space);
+ block = heapSource->blockQueue[block];
+ }
+
+ LOG_SCAV("<<< describeBlockQueue(heapSource=%p)", heapSource);
+}
+
+/*
+ * Blackens promoted objects.
+ */
+static void scavengeBlockQueue(void)
+{
+ HeapSource *heapSource;
+ size_t block;
+
+ LOG_SCAV(">>> scavengeBlockQueue()");
+ heapSource = gDvm.gcHeap->heapSource;
+ describeBlockQueue(heapSource);
+ while (heapSource->queueHead != QUEUE_TAIL) {
+ block = heapSource->queueHead;
+ LOG_SCAV("Dequeueing block %zu\n", block);
+ scavengeBlock(heapSource, block);
+ heapSource->queueHead = heapSource->blockQueue[block];
+ LOG_SCAV("New queue head is %zu\n", heapSource->queueHead);
+ }
+ LOG_SCAV("<<< scavengeBlockQueue()");
+}
+
+/*
+ * Scan the block list and verify all blocks that are marked as being
+ * in new space. This should be parametrized so we can invoke this
+ * routine outside of the context of a collection.
+ */
+static void verifyNewSpace(void)
+{
+ HeapSource *heapSource;
+ size_t i;
+ size_t c0, c1, c2, c7;
+
+ c0 = c1 = c2 = c7 = 0;
+ heapSource = gDvm.gcHeap->heapSource;
+ for (i = 0; i < heapSource->totalBlocks; ++i) {
+ switch (heapSource->blockSpace[i]) {
+ case BLOCK_FREE: ++c0; break;
+ case BLOCK_TO_SPACE: ++c1; break;
+ case BLOCK_FROM_SPACE: ++c2; break;
+ case BLOCK_CONTINUED: ++c7; break;
+ default: assert(!"reached");
+ }
+ }
+ LOG_VER("Block Demographics: "
+ "Free=%zu,ToSpace=%zu,FromSpace=%zu,Continued=%zu",
+ c0, c1, c2, c7);
+ for (i = 0; i < heapSource->totalBlocks; ++i) {
+ if (heapSource->blockSpace[i] != BLOCK_TO_SPACE) {
+ continue;
+ }
+ verifyBlock(heapSource, i);
+ }
+}
+
+static void scavengeGlobals(void)
+{
+ scavengeReference((Object **)(void *)&gDvm.classJavaLangClass);
+ scavengeReference((Object **)(void *)&gDvm.classJavaLangClassArray);
+ scavengeReference((Object **)(void *)&gDvm.classJavaLangError);
+ scavengeReference((Object **)(void *)&gDvm.classJavaLangObject);
+ scavengeReference((Object **)(void *)&gDvm.classJavaLangObjectArray);
+ scavengeReference((Object **)(void *)&gDvm.classJavaLangRuntimeException);
+ scavengeReference((Object **)(void *)&gDvm.classJavaLangString);
+ scavengeReference((Object **)(void *)&gDvm.classJavaLangThread);
+ scavengeReference((Object **)(void *)&gDvm.classJavaLangVMThread);
+ scavengeReference((Object **)(void *)&gDvm.classJavaLangThreadGroup);
+ scavengeReference((Object **)(void *)&gDvm.classJavaLangThrowable);
+ scavengeReference((Object **)(void *)&gDvm.classJavaLangStackTraceElement);
+ scavengeReference((Object **)(void *)&gDvm.classJavaLangStackTraceElementArray);
+ scavengeReference((Object **)(void *)&gDvm.classJavaLangAnnotationAnnotationArray);
+ scavengeReference((Object **)(void *)&gDvm.classJavaLangAnnotationAnnotationArrayArray);
+ scavengeReference((Object **)(void *)&gDvm.classJavaLangReflectAccessibleObject);
+ scavengeReference((Object **)(void *)&gDvm.classJavaLangReflectConstructor);
+ scavengeReference((Object **)(void *)&gDvm.classJavaLangReflectConstructorArray);
+ scavengeReference((Object **)(void *)&gDvm.classJavaLangReflectField);
+ scavengeReference((Object **)(void *)&gDvm.classJavaLangReflectFieldArray);
+ scavengeReference((Object **)(void *)&gDvm.classJavaLangReflectMethod);
+ scavengeReference((Object **)(void *)&gDvm.classJavaLangReflectMethodArray);
+ scavengeReference((Object **)(void *)&gDvm.classJavaLangReflectProxy);
+ scavengeReference((Object **)(void *)&gDvm.classJavaLangExceptionInInitializerError);
+ scavengeReference((Object **)(void *)&gDvm.classJavaLangRefReference);
+ scavengeReference((Object **)(void *)&gDvm.classJavaNioReadWriteDirectByteBuffer);
+ scavengeReference((Object **)(void *)&gDvm.classJavaSecurityAccessController);
+ scavengeReference((Object **)(void *)&gDvm.classOrgApacheHarmonyLangAnnotationAnnotationFactory);
+ scavengeReference((Object **)(void *)&gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMember);
+ scavengeReference((Object **)(void *)&gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMemberArray);
+ scavengeReference((Object **)(void *)&gDvm.classOrgApacheHarmonyNioInternalDirectBuffer);
+ scavengeReference((Object **)(void *)&gDvm.classArrayBoolean);
+ scavengeReference((Object **)(void *)&gDvm.classArrayChar);
+ scavengeReference((Object **)(void *)&gDvm.classArrayFloat);
+ scavengeReference((Object **)(void *)&gDvm.classArrayDouble);
+ scavengeReference((Object **)(void *)&gDvm.classArrayByte);
+ scavengeReference((Object **)(void *)&gDvm.classArrayShort);
+ scavengeReference((Object **)(void *)&gDvm.classArrayInt);
+ scavengeReference((Object **)(void *)&gDvm.classArrayLong);
+}
+
+void describeHeap(void)
+{
+ HeapSource *heapSource;
+
+ heapSource = gDvm.gcHeap->heapSource;
+ describeBlocks(heapSource);
+}
+
+/*
+ * The collection interface. Collection has a few distinct phases.
+ * The first is flipping AKA condemning AKA whitening the heap. The
+ * second is to promote all objects which are pointed to by pinned or
+ * ambiguous references. The third phase is tracing from the stacks,
+ * registers and various globals. Lastly, a verification of the heap
+ * is performed. The last phase should be optional.
+ */
+void dvmScavengeRoots(void) /* Needs a new name badly */
+{
+ GcHeap *gcHeap;
+
+ {
+ size_t alloc, unused, total;
+
+ room(&alloc, &unused, &total);
+ LOG_SCAV("BEFORE GC: %zu alloc, %zu free, %zu total.",
+ alloc, unused, total);
+ }
+
+ gcHeap = gDvm.gcHeap;
+ dvmHeapSourceFlip();
+
+ /*
+ * Promote blocks with stationary objects.
+ */
+ pinThreadList();
+ pinReferenceTable(&gDvm.jniGlobalRefTable);
+ pinReferenceTable(&gDvm.jniPinRefTable);
+ pinHashTableEntries(gDvm.loadedClasses);
+ pinHashTableEntries(gDvm.dbgRegistry);
+ pinPrimitiveClasses();
+ pinInternedStrings();
+
+ // describeBlocks(gcHeap->heapSource);
+
+ /*
+ * Create first, open new-space page right here.
+ */
+
+ /* Reset allocation to an unallocated block. */
+ gDvm.gcHeap->heapSource->allocPtr = allocateBlocks(gDvm.gcHeap->heapSource, 1);
+ gDvm.gcHeap->heapSource->allocLimit = gDvm.gcHeap->heapSource->allocPtr + BLOCK_SIZE;
+ /*
+ * Hack: promote the empty block allocated above. If the
+ * promotions that occurred above did not actually gray any
+ * objects, the block queue may be empty. We must force a
+ * promotion to be safe.
+ */
+ promoteBlockByAddr(gDvm.gcHeap->heapSource, gDvm.gcHeap->heapSource->allocPtr);
+
+ /*
+ * Scavenge blocks and relocate movable objects.
+ */
+
+ LOG_SCAV("Scavenging gDvm.threadList");
+ scavengeThreadList();
+
+ LOG_SCAV("Scavenging gDvm.gcHeap->referenceOperations");
+ scavengeLargeHeapRefTable(gcHeap->referenceOperations);
+
+ LOG_SCAV("Scavenging gDvm.gcHeap->pendingFinalizationRefs");
+ scavengeLargeHeapRefTable(gcHeap->pendingFinalizationRefs);
+
+ LOG_SCAV("Scavenging random global stuff");
+ scavengeReference(&gDvm.outOfMemoryObj);
+ scavengeReference(&gDvm.internalErrorObj);
+ scavengeReference(&gDvm.noClassDefFoundErrorObj);
+
+ // LOG_SCAV("Scavenging gDvm.internedString");
+ scavengeInternedStrings();
+
+ LOG_SCAV("Root scavenge has completed.");
+
+ scavengeBlockQueue();
+
+ LOG_SCAV("Re-snap global class pointers.");
+ scavengeGlobals();
+
+ LOG_SCAV("New space scavenge has completed.");
+
+ /*
+ * Process reference objects in strength order.
+ */
+
+ LOG_REF("Processing soft references...");
+ preserveSoftReferences(&gDvm.gcHeap->softReferences);
+ clearWhiteReferences(&gDvm.gcHeap->softReferences);
+
+ LOG_REF("Processing weak references...");
+ clearWhiteReferences(&gDvm.gcHeap->weakReferences);
+
+ LOG_REF("Finding finalizations...");
+ processFinalizableReferences();
+
+ LOG_REF("Processing f-reachable soft references...");
+ clearWhiteReferences(&gDvm.gcHeap->softReferences);
+
+ LOG_REF("Processing f-reachable weak references...");
+ clearWhiteReferences(&gDvm.gcHeap->weakReferences);
+
+ LOG_REF("Processing phantom references...");
+ clearWhiteReferences(&gDvm.gcHeap->phantomReferences);
+
+ /*
+ * Verify the stack and heap.
+ */
+ dvmVerifyRoots();
+ verifyNewSpace();
+
+ //describeBlocks(gcHeap->heapSource);
+
+ clearFromSpace(gcHeap->heapSource);
+
+ {
+ size_t alloc, rem, total;
+
+ room(&alloc, &rem, &total);
+ LOG_SCAV("AFTER GC: %zu alloc, %zu free, %zu total.", alloc, rem, total);
+ }
+}
+
+/*
+ * Interface compatibility routines.
+ */
+
+void dvmClearWhiteRefs(Object **list)
+{
+ /* do nothing */
+ assert(*list == NULL);
+}
+
+void dvmHandleSoftRefs(Object **list)
+{
+ /* do nothing */
+ assert(*list == NULL);
+}
+
+bool dvmHeapBeginMarkStep(GcMode mode)
+{
+ /* do nothing */
+ return true;
+}
+
+void dvmHeapFinishMarkStep(void)
+{
+ /* do nothing */
+}
+
+void dvmHeapMarkRootSet(void)
+{
+ /* do nothing */
+}
+
+void dvmHeapScanMarkedObjects(void)
+{
+ dvmScavengeRoots();
+}
+
+void dvmHeapScheduleFinalizations(void)
+{
+ /* do nothing */
+}
+
+void dvmHeapSweepUnmarkedObjects(GcMode mode, int *numFreed, size_t *sizeFreed)
+{
+ *numFreed = 0;
+ *sizeFreed = 0;
+ /* do nothing */
+}
+
+void dvmMarkObjectNonNull(const Object *obj)
+{
+ assert(!"implemented");
+}
+
+void dvmMarkDirtyObjects(void)
+{
+ assert(!"implemented");
+}
+
+void dvmHeapSourceThreadShutdown(void)
+{
+ /* do nothing */
+}
diff --git a/vm/alloc/DdmHeap.c b/vm/alloc/DdmHeap.c
new file mode 100644
index 0000000..4cb5cae
--- /dev/null
+++ b/vm/alloc/DdmHeap.c
@@ -0,0 +1,497 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * DDM-related heap functions
+ */
+#include <sys/time.h>
+#include <time.h>
+
+#include "Dalvik.h"
+#include "alloc/Heap.h"
+#include "alloc/HeapInternal.h"
+#include "alloc/DdmHeap.h"
+#include "alloc/HeapSource.h"
+
+#define DEFAULT_HEAP_ID 1
+
+enum HpifWhen {
+ HPIF_WHEN_NEVER = 0,
+ HPIF_WHEN_NOW = 1,
+ HPIF_WHEN_NEXT_GC = 2,
+ HPIF_WHEN_EVERY_GC = 3
+};
+
+/*
+ * Chunk HPIF (client --> server)
+ *
+ * Heap Info. General information about the heap,
+ * suitable for a summary display.
+ *
+ * [u4]: number of heaps
+ *
+ * For each heap:
+ * [u4]: heap ID
+ * [u8]: timestamp in ms since Unix epoch
+ * [u1]: capture reason (same as 'when' value from server)
+ * [u4]: max heap size in bytes (-Xmx)
+ * [u4]: current heap size in bytes
+ * [u4]: current number of bytes allocated
+ * [u4]: current number of objects allocated
+ */
+#define HPIF_SIZE(numHeaps) \
+ (sizeof(u4) + (numHeaps) * (5 * sizeof(u4) + sizeof(u1) + sizeof(u8)))
+void
+dvmDdmSendHeapInfo(int reason, bool shouldLock)
+{
+ struct timeval now;
+ u8 nowMs;
+ u1 *buf, *b;
+
+ buf = (u1 *)malloc(HPIF_SIZE(1));
+ if (buf == NULL) {
+ return;
+ }
+ b = buf;
+
+ /* If there's a one-shot 'when', reset it.
+ */
+ if (reason == gDvm.gcHeap->ddmHpifWhen) {
+ if (shouldLock && ! dvmLockHeap()) {
+ LOGW("%s(): can't lock heap to clear when\n", __func__);
+ goto skip_when;
+ }
+ if (reason == gDvm.gcHeap->ddmHpifWhen) {
+ if (gDvm.gcHeap->ddmHpifWhen == HPIF_WHEN_NEXT_GC) {
+ gDvm.gcHeap->ddmHpifWhen = HPIF_WHEN_NEVER;
+ }
+ }
+ if (shouldLock) {
+ dvmUnlockHeap();
+ }
+ }
+skip_when:
+
+ /* The current time, in milliseconds since 0:00 GMT, 1/1/70.
+ */
+ if (gettimeofday(&now, NULL) < 0) {
+ nowMs = 0;
+ } else {
+ nowMs = (u8)now.tv_sec * 1000 + now.tv_usec / 1000;
+ }
+
+ /* number of heaps */
+ set4BE(b, 1); b += 4;
+
+ /* For each heap (of which there is one) */
+ {
+ /* heap ID */
+ set4BE(b, DEFAULT_HEAP_ID); b += 4;
+
+ /* timestamp */
+ set8BE(b, nowMs); b += 8;
+
+ /* 'when' value */
+ *b++ = (u1)reason;
+
+ /* max allowed heap size in bytes */
+ set4BE(b, gDvm.heapSizeMax); b += 4;
+
+ /* current heap size in bytes */
+ set4BE(b, dvmHeapSourceGetValue(HS_FOOTPRINT, NULL, 0)); b += 4;
+
+ /* number of bytes allocated */
+ set4BE(b, dvmHeapSourceGetValue(HS_BYTES_ALLOCATED, NULL, 0)); b += 4;
+
+ /* number of objects allocated */
+ set4BE(b, dvmHeapSourceGetValue(HS_OBJECTS_ALLOCATED, NULL, 0)); b += 4;
+ }
+ assert((intptr_t)b == (intptr_t)buf + (intptr_t)HPIF_SIZE(1));
+
+ dvmDbgDdmSendChunk(CHUNK_TYPE("HPIF"), b - buf, buf);
+}
+
+bool
+dvmDdmHandleHpifChunk(int when)
+{
+ switch (when) {
+ case HPIF_WHEN_NOW:
+ dvmDdmSendHeapInfo(when, true);
+ break;
+ case HPIF_WHEN_NEVER:
+ case HPIF_WHEN_NEXT_GC:
+ case HPIF_WHEN_EVERY_GC:
+ if (dvmLockHeap()) {
+ gDvm.gcHeap->ddmHpifWhen = when;
+ dvmUnlockHeap();
+ } else {
+ LOGI("%s(): can't lock heap to set when\n", __func__);
+ return false;
+ }
+ break;
+ default:
+ LOGI("%s(): bad when value 0x%08x\n", __func__, when);
+ return false;
+ }
+
+ return true;
+}
+
+enum HpsgSolidity {
+ SOLIDITY_FREE = 0,
+ SOLIDITY_HARD = 1,
+ SOLIDITY_SOFT = 2,
+ SOLIDITY_WEAK = 3,
+ SOLIDITY_PHANTOM = 4,
+ SOLIDITY_FINALIZABLE = 5,
+ SOLIDITY_SWEEP = 6,
+};
+
+enum HpsgKind {
+ KIND_OBJECT = 0,
+ KIND_CLASS_OBJECT = 1,
+ KIND_ARRAY_1 = 2,
+ KIND_ARRAY_2 = 3,
+ KIND_ARRAY_4 = 4,
+ KIND_ARRAY_8 = 5,
+ KIND_UNKNOWN = 6,
+ KIND_NATIVE = 7,
+};
+
+#define HPSG_PARTIAL (1<<7)
+#define HPSG_STATE(solidity, kind) \
+ ((u1)((((kind) & 0x7) << 3) | ((solidity) & 0x7)))
+
+typedef struct HeapChunkContext {
+ u1 *buf;
+ u1 *p;
+ u1 *pieceLenField;
+ size_t bufLen;
+ size_t totalAllocationUnits;
+ int type;
+ bool merge;
+ bool needHeader;
+} HeapChunkContext;
+
+#define ALLOCATION_UNIT_SIZE 8
+
+static void
+flush_hpsg_chunk(HeapChunkContext *ctx)
+{
+ /* Patch the "length of piece" field.
+ */
+ assert(ctx->buf <= ctx->pieceLenField &&
+ ctx->pieceLenField <= ctx->p);
+ set4BE(ctx->pieceLenField, ctx->totalAllocationUnits);
+
+ /* Send the chunk.
+ */
+ dvmDbgDdmSendChunk(ctx->type, ctx->p - ctx->buf, ctx->buf);
+
+ /* Reset the context.
+ */
+ ctx->p = ctx->buf;
+ ctx->totalAllocationUnits = 0;
+ ctx->needHeader = true;
+ ctx->pieceLenField = NULL;
+}
+
+static void
+heap_chunk_callback(const void *chunkptr, size_t chunklen,
+ const void *userptr, size_t userlen, void *arg)
+{
+ HeapChunkContext *ctx = (HeapChunkContext *)arg;
+ u1 state;
+
+ UNUSED_PARAMETER(userlen);
+
+ assert((chunklen & (ALLOCATION_UNIT_SIZE-1)) == 0);
+
+ /* Make sure there's enough room left in the buffer.
+ * We need to use two bytes for every fractional 256
+ * allocation units used by the chunk.
+ */
+ {
+ size_t needed = (((chunklen/ALLOCATION_UNIT_SIZE + 255) / 256) * 2);
+ size_t bytesLeft = ctx->bufLen - (size_t)(ctx->p - ctx->buf);
+ if (bytesLeft < needed) {
+ flush_hpsg_chunk(ctx);
+ }
+
+ bytesLeft = ctx->bufLen - (size_t)(ctx->p - ctx->buf);
+ if (bytesLeft < needed) {
+ LOGW("chunk is too big to transmit (chunklen=%zd, %zd bytes)\n",
+ chunklen, needed);
+ return;
+ }
+ }
+
+//TODO: notice when there's a gap and start a new heap, or at least a new range.
+ if (ctx->needHeader) {
+ /*
+ * Start a new HPSx chunk.
+ */
+
+ /* [u4]: heap ID */
+ set4BE(ctx->p, DEFAULT_HEAP_ID); ctx->p += 4;
+
+ /* [u1]: size of allocation unit, in bytes */
+ *ctx->p++ = 8;
+
+ /* [u4]: virtual address of segment start */
+ set4BE(ctx->p, (uintptr_t)chunkptr); ctx->p += 4;
+
+ /* [u4]: offset of this piece (relative to the virtual address) */
+ set4BE(ctx->p, 0); ctx->p += 4;
+
+ /* [u4]: length of piece, in allocation units
+ * We won't know this until we're done, so save the offset
+ * and stuff in a dummy value.
+ */
+ ctx->pieceLenField = ctx->p;
+ set4BE(ctx->p, 0x55555555); ctx->p += 4;
+
+ ctx->needHeader = false;
+ }
+
+ /* Determine the type of this chunk.
+ */
+ if (userptr == NULL) {
+ /* It's a free chunk.
+ */
+ state = HPSG_STATE(SOLIDITY_FREE, 0);
+ } else {
+ const Object *obj = userptr;
+ /* If we're looking at the native heap, we'll just return
+ * (SOLIDITY_HARD, KIND_NATIVE) for all allocated chunks
+ */
+ bool native = ctx->type == CHUNK_TYPE("NHSG");
+
+ /* It's an allocated chunk. Figure out what it is.
+ */
+//TODO: if ctx.merge, see if this chunk is different from the last chunk.
+// If it's the same, we should combine them.
+ if (!native && dvmIsValidObject(obj)) {
+ ClassObject *clazz = obj->clazz;
+ if (clazz == NULL) {
+ /* The object was probably just created
+ * but hasn't been initialized yet.
+ */
+ state = HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT);
+ } else if (clazz == gDvm.classJavaLangClass) {
+ state = HPSG_STATE(SOLIDITY_HARD, KIND_CLASS_OBJECT);
+ } else if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) {
+ if (IS_CLASS_FLAG_SET(clazz, CLASS_ISOBJECTARRAY)) {
+ state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4);
+ } else {
+ switch (clazz->elementClass->primitiveType) {
+ case PRIM_BOOLEAN:
+ case PRIM_BYTE:
+ state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_1);
+ break;
+ case PRIM_CHAR:
+ case PRIM_SHORT:
+ state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_2);
+ break;
+ case PRIM_INT:
+ case PRIM_FLOAT:
+ state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4);
+ break;
+ case PRIM_DOUBLE:
+ case PRIM_LONG:
+ state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_8);
+ break;
+ default:
+ assert(!"Unknown GC heap object type");
+ state = HPSG_STATE(SOLIDITY_HARD, KIND_UNKNOWN);
+ break;
+ }
+ }
+ } else {
+ state = HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT);
+ }
+ } else {
+ obj = NULL; // it's not actually an object
+ state = HPSG_STATE(SOLIDITY_HARD, KIND_NATIVE);
+ }
+ }
+
+ /* Write out the chunk description.
+ */
+ chunklen /= ALLOCATION_UNIT_SIZE; // convert to allocation units
+ ctx->totalAllocationUnits += chunklen;
+ while (chunklen > 256) {
+ *ctx->p++ = state | HPSG_PARTIAL;
+ *ctx->p++ = 255; // length - 1
+ chunklen -= 256;
+ }
+ *ctx->p++ = state;
+ *ctx->p++ = chunklen - 1;
+}
+
+enum HpsgWhen {
+ HPSG_WHEN_NEVER = 0,
+ HPSG_WHEN_EVERY_GC = 1,
+};
+enum HpsgWhat {
+ HPSG_WHAT_MERGED_OBJECTS = 0,
+ HPSG_WHAT_DISTINCT_OBJECTS = 1,
+};
+
+/*
+ * Maximum chunk size. Obtain this from the formula:
+ *
+ * (((maximum_heap_size / ALLOCATION_UNIT_SIZE) + 255) / 256) * 2
+ */
+#define HPSx_CHUNK_SIZE (16384 - 16)
+
+void dlmalloc_walk_heap(void(*)(const void*, size_t, const void*, size_t, void*),void*);
+
+static void
+walkHeap(bool merge, bool native)
+{
+ HeapChunkContext ctx;
+
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.bufLen = HPSx_CHUNK_SIZE;
+ ctx.buf = (u1 *)malloc(ctx.bufLen);
+ if (ctx.buf == NULL) {
+ return;
+ }
+
+ ctx.merge = merge;
+ if (native) {
+ ctx.type = CHUNK_TYPE("NHSG");
+ } else {
+ if (ctx.merge) {
+ ctx.type = CHUNK_TYPE("HPSG");
+ } else {
+ ctx.type = CHUNK_TYPE("HPSO");
+ }
+ }
+
+ ctx.p = ctx.buf;
+ ctx.needHeader = true;
+ if (native) {
+ dlmalloc_walk_heap(heap_chunk_callback, (void *)&ctx);
+ } else {
+ dvmHeapSourceWalk(heap_chunk_callback, (void *)&ctx);
+ }
+ if (ctx.p > ctx.buf) {
+ flush_hpsg_chunk(&ctx);
+ }
+
+ free(ctx.buf);
+}
+
+void
+dvmDdmSendHeapSegments(bool shouldLock, bool native)
+{
+ u1 heapId[sizeof(u4)];
+ GcHeap *gcHeap = gDvm.gcHeap;
+ int when, what;
+ bool merge;
+
+ /* Don't even grab the lock if there's nothing to do when we're called.
+ */
+ if (!native) {
+ when = gcHeap->ddmHpsgWhen;
+ what = gcHeap->ddmHpsgWhat;
+ if (when == HPSG_WHEN_NEVER) {
+ return;
+ }
+ } else {
+ when = gcHeap->ddmNhsgWhen;
+ what = gcHeap->ddmNhsgWhat;
+ if (when == HPSG_WHEN_NEVER) {
+ return;
+ }
+ }
+ if (shouldLock && !dvmLockHeap()) {
+ LOGW("Can't lock heap for DDM HPSx dump\n");
+ return;
+ }
+
+ /* Figure out what kind of chunks we'll be sending.
+ */
+ if (what == HPSG_WHAT_MERGED_OBJECTS) {
+ merge = true;
+ } else if (what == HPSG_WHAT_DISTINCT_OBJECTS) {
+ merge = false;
+ } else {
+ assert(!"bad HPSG.what value");
+ return;
+ }
+
+ /* First, send a heap start chunk.
+ */
+ set4BE(heapId, DEFAULT_HEAP_ID);
+ dvmDbgDdmSendChunk(native ? CHUNK_TYPE("NHST") : CHUNK_TYPE("HPST"),
+ sizeof(u4), heapId);
+
+ /* Send a series of heap segment chunks.
+ */
+ walkHeap(merge, native);
+
+ /* Finally, send a heap end chunk.
+ */
+ dvmDbgDdmSendChunk(native ? CHUNK_TYPE("NHEN") : CHUNK_TYPE("HPEN"),
+ sizeof(u4), heapId);
+
+ if (shouldLock) {
+ dvmUnlockHeap();
+ }
+}
+
+bool
+dvmDdmHandleHpsgNhsgChunk(int when, int what, bool native)
+{
+ LOGI("dvmDdmHandleHpsgChunk(when %d, what %d, heap %d)\n", when, what,
+ native);
+ switch (when) {
+ case HPSG_WHEN_NEVER:
+ case HPSG_WHEN_EVERY_GC:
+ break;
+ default:
+ LOGI("%s(): bad when value 0x%08x\n", __func__, when);
+ return false;
+ }
+
+ switch (what) {
+ case HPSG_WHAT_MERGED_OBJECTS:
+ case HPSG_WHAT_DISTINCT_OBJECTS:
+ break;
+ default:
+ LOGI("%s(): bad what value 0x%08x\n", __func__, what);
+ return false;
+ }
+
+ if (dvmLockHeap()) {
+ if (!native) {
+ gDvm.gcHeap->ddmHpsgWhen = when;
+ gDvm.gcHeap->ddmHpsgWhat = what;
+ } else {
+ gDvm.gcHeap->ddmNhsgWhen = when;
+ gDvm.gcHeap->ddmNhsgWhat = what;
+ }
+//TODO: if what says we should dump immediately, signal (or do) it from here
+ dvmUnlockHeap();
+ } else {
+ LOGI("%s(): can't lock heap to set when/what\n", __func__);
+ return false;
+ }
+
+ return true;
+}
diff --git a/vm/alloc/DdmHeap.h b/vm/alloc/DdmHeap.h
new file mode 100644
index 0000000..c3e11dc
--- /dev/null
+++ b/vm/alloc/DdmHeap.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * DDM-specific internal heap functions.
+ */
+#ifndef _DALVIK_ALLOC_DDMHEAP
+#define _DALVIK_ALLOC_DDMHEAP
+
+/*
+ * Sends the current heap info to the DDM server.
+ * Should be called after a GC when gcHeap->ddmHpifWhen
+ * is non-zero.
+ */
+void dvmDdmSendHeapInfo(int reason, bool shouldLock);
+
+/*
+ * Walks through the heap and sends a series of
+ * HPST/NHST, HPSG/HPSO/NHSG, and HPEN/NHEN chunks that describe
+ * the contents of the GC or native heap.
+ *
+ * @param shouldLock If true, grab the heap lock. If false,
+ * the heap lock must already be held.
+ * @param heap If false, dump the GC heap; if true, dump the
+ * native heap.
+ */
+void dvmDdmSendHeapSegments(bool shouldLock, bool native);
+
+#endif // _DALVIK_ALLOC_DDMHEAP
diff --git a/vm/alloc/Float12.h b/vm/alloc/Float12.h
new file mode 100644
index 0000000..324cc51
--- /dev/null
+++ b/vm/alloc/Float12.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DALVIK_FLOAT12_H
+#define _DALVIK_FLOAT12_H
+
+/* Encodes a 32-bit number in 12 bits with +/-1.5% error,
+ * though the majority (80%) are within +/-0.25%.
+ *
+ * The encoding looks like:
+ *
+ * EEEMMMMM MMMMMMMM MMMMMMMM
+ * 76543210 76543210 76543210
+ *
+ * where EEE is a base-16 exponent and MMMM is the mantissa.
+ * The output value is (MMMM * 16^EEE), or (MMMM << (EEE * 4)).
+ *
+ * TODO: do this in a less brain-dead way. I'm sure we can do
+ * it without all of these loops.
+ */
+inline unsigned short intToFloat12(unsigned int val)
+{
+ int oval = val;
+ int shift = 0;
+
+ /* Shift off the precision we don't care about.
+ * Don't round here; it biases the values too high
+ * (such that the encoded value is always greater
+ * than the actual value)
+ */
+ unsigned int pval = val;
+ while (val > 0x1ff) {
+ pval = val;
+ val >>= 1;
+ shift++;
+ }
+ if (shift > 0 && (pval & 1)) {
+ /* Round based on the last bit we shifted off.
+ */
+ val++;
+ if (val > 0x1ff) {
+ val = (val + 1) >> 1;
+ shift++;
+ }
+ }
+
+ /* Shift off enough bits to create a valid exponent.
+ * Since we care about the bits we're losing, be sure
+ * to round them.
+ */
+ while (shift % 4 != 0) {
+ val = (val + 1) >> 1;
+ shift++;
+ }
+
+ /* In the end, only round by the most-significant lost bit.
+ * This centers the values around the closest match.
+ * All of the rounding we did above guarantees that this
+ * round won't overflow past 0x1ff.
+ */
+ if (shift > 0) {
+ val = ((oval >> (shift - 1)) + 1) >> 1;
+ }
+
+ val |= (shift / 4) << 9;
+ return val;
+}
+
+inline unsigned int float12ToInt(unsigned short f12)
+{
+ return (f12 & 0x1ff) << ((f12 >> 9) * 4);
+}
+
+#if 0 // testing
+
+#include <stdio.h>
+int main(int argc, char *argv[])
+{
+ if (argc != 3) {
+ fprintf(stderr, "usage: %s <min> <max>\n", argv[0]);
+ return 1;
+ }
+
+ unsigned int min = atoi(argv[1]);
+ unsigned int max = atoi(argv[2]);
+ if (min > max) {
+ int t = min;
+ max = min;
+ min = t;
+ } else if (min == max) {
+ max++;
+ }
+
+ while (min < max) {
+ unsigned int out;
+ unsigned short sf;
+
+ sf = intToFloat12(min);
+ out = float12ToInt(sf);
+// printf("%d 0x%03x / 0x%03x %d (%d)\n", min, min, sf, out, (int)min - (int)out);
+ printf("%6.6f %d %d\n", ((float)(int)(min - out)) / (float)(int)min, min, out);
+ if (min <= 8192) {
+ min++;
+ } else if (min < 10000) {
+ min += 10;
+ } else if (min < 100000) {
+ min += 1000;
+ } else {
+ min += 10000;
+ }
+ }
+ return 0;
+}
+
+#endif // testing
+
+#endif // _DALVIK_FLOAT12_H
diff --git a/vm/alloc/GC.h b/vm/alloc/GC.h
new file mode 100644
index 0000000..62e9aa6
--- /dev/null
+++ b/vm/alloc/GC.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Garbage collector
+ */
+#ifndef _DALVIK_ALLOC_GC
+#define _DALVIK_ALLOC_GC
+
+/*
+ * Initiate garbage collection.
+ *
+ * This usually happens automatically, but can also be caused by Runtime.gc().
+ */
+void dvmCollectGarbage(bool collectSoftRefs);
+
+/****
+ **** NOTE: The functions after this point will (should) only be called
+ **** during GC.
+ ****/
+
+/*
+ * Functions that mark an object.
+ *
+ * Currently implemented in Heap.c.
+ */
+
+/*
+ * Mark an object and schedule it to be scanned for
+ * references to other objects.
+ *
+ * @param obj must be a valid object
+ */
+void dvmMarkObjectNonNull(const Object *obj);
+
+/*
+ * Mark an object and schedule it to be scanned for
+ * references to other objects.
+ *
+ * @param obj must be a valid object or NULL
+ */
+#define dvmMarkObject(obj) \
+ do { \
+ Object *DMO_obj_ = (Object *)(obj); \
+ if (DMO_obj_ != NULL) { \
+ dvmMarkObjectNonNull(DMO_obj_); \
+ } \
+ } while (false)
+
+/*
+ * If obj points to a valid object, mark it and
+ * schedule it to be scanned for references to other
+ * objects.
+ *
+ * @param obj any pointer that may be an Object, or NULL
+TODO: check for alignment, too (would require knowledge of heap chunks)
+ */
+#define dvmMarkIfObject(obj) \
+ do { \
+ Object *DMIO_obj_ = (Object *)(obj); \
+ if (DMIO_obj_ != NULL && dvmIsValidObject(DMIO_obj_)) { \
+ dvmMarkObjectNonNull(DMIO_obj_); \
+ } \
+ } while (false)
+
+/*
+ * Functions that handle scanning various objects for references.
+ */
+
+/*
+ * Mark all class objects loaded by the root class loader;
+ * most of these are the java.* classes.
+ *
+ * Currently implemented in Class.c.
+ */
+void dvmGcScanRootClassLoader(void);
+
+/*
+ * Mark all root ThreadGroup objects, guaranteeing that
+ * all live Thread objects will eventually be scanned.
+ *
+ * NOTE: this is a misnomer, because the current implementation
+ * actually only scans the internal list of VM threads, which
+ * will mark all VM-reachable Thread objects. Someone else
+ * must scan the root class loader, which will mark java/lang/ThreadGroup.
+ * The ThreadGroup class object has static members pointing to
+ * the root ThreadGroups, and these will be marked as a side-effect
+ * of marking the class object.
+ *
+ * Currently implemented in Thread.c.
+ */
+void dvmGcScanRootThreadGroups(void);
+
+/*
+ * Mark all interned string objects.
+ *
+ * Currently implemented in Intern.c.
+ */
+void dvmGcScanInternedStrings(void);
+
+/*
+ * Remove any unmarked interned string objects from the table.
+ *
+ * Currently implemented in Intern.c.
+ */
+void dvmGcDetachDeadInternedStrings(int (*isUnmarkedObject)(void *));
+
+/*
+ * Mark all primitive class objects.
+ *
+ * Currently implemented in Array.c.
+ */
+void dvmGcScanPrimitiveClasses(void);
+
+/*
+ * Mark all JNI global references.
+ *
+ * Currently implemented in JNI.c.
+ */
+void dvmGcMarkJniGlobalRefs(void);
+
+/*
+ * Mark all debugger references.
+ *
+ * Currently implemented in Debugger.c.
+ */
+void dvmGcMarkDebuggerRefs(void);
+
+/*
+ * Optional heap profiling.
+ */
+#if WITH_HPROF && !defined(_DALVIK_HPROF_HPROF)
+#include "hprof/Hprof.h"
+#define HPROF_SET_GC_SCAN_STATE(tag_, thread_) \
+ dvmHeapSetHprofGcScanState((tag_), (thread_))
+#define HPROF_CLEAR_GC_SCAN_STATE() \
+ dvmHeapSetHprofGcScanState(0, 0)
+#else
+#define HPROF_SET_GC_SCAN_STATE(tag_, thread_) do {} while (false)
+#define HPROF_CLEAR_GC_SCAN_STATE() do {} while (false)
+#endif
+
+#endif // _DALVIK_ALLOC_GC
diff --git a/vm/alloc/Heap.c b/vm/alloc/Heap.c
new file mode 100644
index 0000000..843ee38
--- /dev/null
+++ b/vm/alloc/Heap.c
@@ -0,0 +1,1007 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Garbage-collecting memory allocator.
+ */
+#include "Dalvik.h"
+#include "alloc/HeapBitmap.h"
+#include "alloc/Verify.h"
+#include "alloc/HeapTable.h"
+#include "alloc/Heap.h"
+#include "alloc/HeapInternal.h"
+#include "alloc/DdmHeap.h"
+#include "alloc/HeapSource.h"
+#include "alloc/MarkSweep.h"
+#include "alloc/Visit.h"
+
+#include "utils/threads.h" // need Android thread priorities
+#define kInvalidPriority 10000
+
+#include <cutils/sched_policy.h>
+
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <limits.h>
+#include <errno.h>
+
+static const char* GcReasonStr[] = {
+ [GC_FOR_MALLOC] = "GC_FOR_MALLOC",
+ [GC_CONCURRENT] = "GC_CONCURRENT",
+ [GC_EXPLICIT] = "GC_EXPLICIT",
+ [GC_EXTERNAL_ALLOC] = "GC_EXTERNAL_ALLOC",
+ [GC_HPROF_DUMP_HEAP] = "GC_HPROF_DUMP_HEAP"
+};
+
+/*
+ * Initialize the GC heap.
+ *
+ * Returns true if successful, false otherwise.
+ */
+bool dvmHeapStartup()
+{
+ GcHeap *gcHeap;
+
+#if defined(WITH_ALLOC_LIMITS)
+ gDvm.checkAllocLimits = false;
+ gDvm.allocationLimit = -1;
+#endif
+
+ gcHeap = dvmHeapSourceStartup(gDvm.heapSizeStart, gDvm.heapSizeMax);
+ if (gcHeap == NULL) {
+ return false;
+ }
+ gcHeap->heapWorkerCurrentObject = NULL;
+ gcHeap->heapWorkerCurrentMethod = NULL;
+ gcHeap->heapWorkerInterpStartTime = 0LL;
+ gcHeap->ddmHpifWhen = 0;
+ gcHeap->ddmHpsgWhen = 0;
+ gcHeap->ddmHpsgWhat = 0;
+ gcHeap->ddmNhsgWhen = 0;
+ gcHeap->ddmNhsgWhat = 0;
+#if WITH_HPROF
+ gcHeap->hprofDumpOnGc = false;
+ gcHeap->hprofContext = NULL;
+#endif
+ gDvm.gcHeap = gcHeap;
+
+ /* Set up the lists and lock we'll use for finalizable
+ * and reference objects.
+ */
+ dvmInitMutex(&gDvm.heapWorkerListLock);
+ gcHeap->finalizableRefs = NULL;
+ gcHeap->pendingFinalizationRefs = NULL;
+ gcHeap->referenceOperations = NULL;
+
+ if (!dvmCardTableStartup()) {
+ LOGE_HEAP("card table startup failed.");
+ return false;
+ }
+
+ /* Initialize the HeapWorker locks and other state
+ * that the GC uses.
+ */
+ dvmInitializeHeapWorkerState();
+
+ return true;
+}
+
+bool dvmHeapStartupAfterZygote(void)
+{
+ return dvmHeapSourceStartupAfterZygote();
+}
+
+void dvmHeapShutdown()
+{
+//TODO: make sure we're locked
+ if (gDvm.gcHeap != NULL) {
+ dvmCardTableShutdown();
+ /* Tables are allocated on the native heap; they need to be
+ * cleaned up explicitly. The process may stick around, so we
+ * don't want to leak any native memory.
+ */
+ dvmHeapFreeLargeTable(gDvm.gcHeap->finalizableRefs);
+ gDvm.gcHeap->finalizableRefs = NULL;
+
+ dvmHeapFreeLargeTable(gDvm.gcHeap->pendingFinalizationRefs);
+ gDvm.gcHeap->pendingFinalizationRefs = NULL;
+
+ dvmHeapFreeLargeTable(gDvm.gcHeap->referenceOperations);
+ gDvm.gcHeap->referenceOperations = NULL;
+
+ /* Destroy the heap. Any outstanding pointers will point to
+ * unmapped memory (unless/until someone else maps it). This
+ * frees gDvm.gcHeap as a side-effect.
+ */
+ dvmHeapSourceShutdown(&gDvm.gcHeap);
+ }
+}
+
+/*
+ * Shutdown any threads internal to the heap.
+ */
+void dvmHeapThreadShutdown(void)
+{
+ dvmHeapSourceThreadShutdown();
+}
+
+/*
+ * We've been asked to allocate something we can't, e.g. an array so
+ * large that (length * elementWidth) is larger than 2^31.
+ *
+ * _The Java Programming Language_, 4th edition, says, "you can be sure
+ * that all SoftReferences to softly reachable objects will be cleared
+ * before an OutOfMemoryError is thrown."
+ *
+ * It's unclear whether that holds for all situations where an OOM can
+ * be thrown, or just in the context of an allocation that fails due
+ * to lack of heap space. For simplicity we just throw the exception.
+ *
+ * (OOM due to actually running out of space is handled elsewhere.)
+ */
+void dvmThrowBadAllocException(const char* msg)
+{
+ dvmThrowException("Ljava/lang/OutOfMemoryError;", msg);
+}
+
+/*
+ * Grab the lock, but put ourselves into THREAD_VMWAIT if it looks like
+ * we're going to have to wait on the mutex.
+ */
+bool dvmLockHeap()
+{
+ if (dvmTryLockMutex(&gDvm.gcHeapLock) != 0) {
+ Thread *self;
+ ThreadStatus oldStatus;
+
+ self = dvmThreadSelf();
+ oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
+ dvmLockMutex(&gDvm.gcHeapLock);
+ dvmChangeStatus(self, oldStatus);
+ }
+
+ return true;
+}
+
+void dvmUnlockHeap()
+{
+ dvmUnlockMutex(&gDvm.gcHeapLock);
+}
+
+/* Pop an object from the list of pending finalizations and
+ * reference clears/enqueues, and return the object.
+ * The caller must call dvmReleaseTrackedAlloc()
+ * on the object when finished.
+ *
+ * Typically only called by the heap worker thread.
+ */
+Object *dvmGetNextHeapWorkerObject(HeapWorkerOperation *op)
+{
+ Object *obj;
+ GcHeap *gcHeap = gDvm.gcHeap;
+
+ assert(op != NULL);
+
+ dvmLockMutex(&gDvm.heapWorkerListLock);
+
+ obj = dvmHeapGetNextObjectFromLargeTable(&gcHeap->referenceOperations);
+ if (obj != NULL) {
+ *op = WORKER_ENQUEUE;
+ } else {
+ obj = dvmHeapGetNextObjectFromLargeTable(
+ &gcHeap->pendingFinalizationRefs);
+ if (obj != NULL) {
+ *op = WORKER_FINALIZE;
+ }
+ }
+
+ if (obj != NULL) {
+ /* Don't let the GC collect the object until the
+ * worker thread is done with it.
+ */
+ dvmAddTrackedAlloc(obj, NULL);
+ }
+
+ dvmUnlockMutex(&gDvm.heapWorkerListLock);
+
+ return obj;
+}
+
+/* Do a full garbage collection, which may grow the
+ * heap as a side-effect if the live set is large.
+ */
+static void gcForMalloc(bool collectSoftReferences)
+{
+ if (gDvm.allocProf.enabled) {
+ Thread* self = dvmThreadSelf();
+ gDvm.allocProf.gcCount++;
+ if (self != NULL) {
+ self->allocProf.gcCount++;
+ }
+ }
+ /* This may adjust the soft limit as a side-effect.
+ */
+ LOGD_HEAP("dvmMalloc initiating GC%s\n",
+ collectSoftReferences ? "(collect SoftReferences)" : "");
+ dvmCollectGarbageInternal(collectSoftReferences, GC_FOR_MALLOC);
+}
+
+/* Try as hard as possible to allocate some memory.
+ */
+static void *tryMalloc(size_t size)
+{
+ void *ptr;
+
+ /* Don't try too hard if there's no way the allocation is
+ * going to succeed. We have to collect SoftReferences before
+ * throwing an OOME, though.
+ */
+ if (size >= gDvm.heapSizeMax) {
+ LOGW_HEAP("dvmMalloc(%zu/0x%08zx): "
+ "someone's allocating a huge buffer\n", size, size);
+ ptr = NULL;
+ goto collect_soft_refs;
+ }
+
+//TODO: figure out better heuristics
+// There will be a lot of churn if someone allocates a bunch of
+// big objects in a row, and we hit the frag case each time.
+// A full GC for each.
+// Maybe we grow the heap in bigger leaps
+// Maybe we skip the GC if the size is large and we did one recently
+// (number of allocations ago) (watch for thread effects)
+// DeflateTest allocs a bunch of ~128k buffers w/in 0-5 allocs of each other
+// (or, at least, there are only 0-5 objects swept each time)
+
+ ptr = dvmHeapSourceAlloc(size);
+ if (ptr != NULL) {
+ return ptr;
+ }
+
+ /*
+ * The allocation failed. If the GC is running, block until it
+ * completes and retry.
+ */
+ if (gDvm.gcHeap->gcRunning) {
+ /*
+ * The GC is concurrently tracing the heap. Release the heap
+ * lock, wait for the GC to complete, and retrying allocating.
+ */
+ dvmWaitForConcurrentGcToComplete();
+ ptr = dvmHeapSourceAlloc(size);
+ if (ptr != NULL) {
+ return ptr;
+ }
+ }
+ /*
+ * Another failure. Our thread was starved or there may be too
+ * many live objects. Try a foreground GC. This will have no
+ * effect if the concurrent GC is already running.
+ */
+ gcForMalloc(false);
+ ptr = dvmHeapSourceAlloc(size);
+ if (ptr != NULL) {
+ return ptr;
+ }
+
+ /* Even that didn't work; this is an exceptional state.
+ * Try harder, growing the heap if necessary.
+ */
+ ptr = dvmHeapSourceAllocAndGrow(size);
+ if (ptr != NULL) {
+ size_t newHeapSize;
+
+ newHeapSize = dvmHeapSourceGetIdealFootprint();
+//TODO: may want to grow a little bit more so that the amount of free
+// space is equal to the old free space + the utilization slop for
+// the new allocation.
+ LOGI_HEAP("Grow heap (frag case) to "
+ "%zu.%03zuMB for %zu-byte allocation\n",
+ FRACTIONAL_MB(newHeapSize), size);
+ return ptr;
+ }
+
+ /* Most allocations should have succeeded by now, so the heap
+ * is really full, really fragmented, or the requested size is
+ * really big. Do another GC, collecting SoftReferences this
+ * time. The VM spec requires that all SoftReferences have
+ * been collected and cleared before throwing an OOME.
+ */
+//TODO: wait for the finalizers from the previous GC to finish
+collect_soft_refs:
+ LOGI_HEAP("Forcing collection of SoftReferences for %zu-byte allocation\n",
+ size);
+ gcForMalloc(true);
+ ptr = dvmHeapSourceAllocAndGrow(size);
+ if (ptr != NULL) {
+ return ptr;
+ }
+//TODO: maybe wait for finalizers and try one last time
+
+ LOGE_HEAP("Out of memory on a %zd-byte allocation.\n", size);
+//TODO: tell the HeapSource to dump its state
+ dvmDumpThread(dvmThreadSelf(), false);
+
+ return NULL;
+}
+
+/* Throw an OutOfMemoryError if there's a thread to attach it to.
+ * Avoid recursing.
+ *
+ * The caller must not be holding the heap lock, or else the allocations
+ * in dvmThrowException() will deadlock.
+ */
+static void throwOOME()
+{
+ Thread *self;
+
+ if ((self = dvmThreadSelf()) != NULL) {
+ /* If the current (failing) dvmMalloc() happened as part of thread
+ * creation/attachment before the thread became part of the root set,
+ * we can't rely on the thread-local trackedAlloc table, so
+ * we can't keep track of a real allocated OOME object. But, since
+ * the thread is in the process of being created, it won't have
+ * a useful stack anyway, so we may as well make things easier
+ * by throwing the (stackless) pre-built OOME.
+ */
+ if (dvmIsOnThreadList(self) && !self->throwingOOME) {
+ /* Let ourselves know that we tried to throw an OOM
+ * error in the normal way in case we run out of
+ * memory trying to allocate it inside dvmThrowException().
+ */
+ self->throwingOOME = true;
+
+ /* Don't include a description string;
+ * one fewer allocation.
+ */
+ dvmThrowException("Ljava/lang/OutOfMemoryError;", NULL);
+ } else {
+ /*
+ * This thread has already tried to throw an OutOfMemoryError,
+ * which probably means that we're running out of memory
+ * while recursively trying to throw.
+ *
+ * To avoid any more allocation attempts, "throw" a pre-built
+ * OutOfMemoryError object (which won't have a useful stack trace).
+ *
+ * Note that since this call can't possibly allocate anything,
+ * we don't care about the state of self->throwingOOME
+ * (which will usually already be set).
+ */
+ dvmSetException(self, gDvm.outOfMemoryObj);
+ }
+ /* We're done with the possible recursion.
+ */
+ self->throwingOOME = false;
+ }
+}
+
+/*
+ * Allocate storage on the GC heap. We guarantee 8-byte alignment.
+ *
+ * The new storage is zeroed out.
+ *
+ * Note that, in rare cases, this could get called while a GC is in
+ * progress. If a non-VM thread tries to attach itself through JNI,
+ * it will need to allocate some objects. If this becomes annoying to
+ * deal with, we can block it at the source, but holding the allocation
+ * mutex should be enough.
+ *
+ * In rare circumstances (JNI AttachCurrentThread) we can be called
+ * from a non-VM thread.
+ *
+ * Use ALLOC_DONT_TRACK when we either don't want to track an allocation
+ * (because it's being done for the interpreter "new" operation and will
+ * be part of the root set immediately) or we can't (because this allocation
+ * is for a brand new thread).
+ *
+ * Returns NULL and throws an exception on failure.
+ *
+ * TODO: don't do a GC if the debugger thinks all threads are suspended
+ */
+void* dvmMalloc(size_t size, int flags)
+{
+ GcHeap *gcHeap = gDvm.gcHeap;
+ void *ptr;
+
+#if defined(WITH_ALLOC_LIMITS)
+ /*
+ * See if they've exceeded the allocation limit for this thread.
+ *
+ * A limit value of -1 means "no limit".
+ *
+ * This is enabled at compile time because it requires us to do a
+ * TLS lookup for the Thread pointer. This has enough of a performance
+ * impact that we don't want to do it if we don't have to. (Now that
+ * we're using gDvm.checkAllocLimits we may want to reconsider this,
+ * but it's probably still best to just compile the check out of
+ * production code -- one less thing to hit on every allocation.)
+ */
+ if (gDvm.checkAllocLimits) {
+ Thread* self = dvmThreadSelf();
+ if (self != NULL) {
+ int count = self->allocLimit;
+ if (count > 0) {
+ self->allocLimit--;
+ } else if (count == 0) {
+ /* fail! */
+ assert(!gDvm.initializing);
+ self->allocLimit = -1;
+ dvmThrowException("Ldalvik/system/AllocationLimitError;",
+ "thread allocation limit exceeded");
+ return NULL;
+ }
+ }
+ }
+
+ if (gDvm.allocationLimit >= 0) {
+ assert(!gDvm.initializing);
+ gDvm.allocationLimit = -1;
+ dvmThrowException("Ldalvik/system/AllocationLimitError;",
+ "global allocation limit exceeded");
+ return NULL;
+ }
+#endif
+
+ dvmLockHeap();
+
+ /* Try as hard as possible to allocate some memory.
+ */
+ ptr = tryMalloc(size);
+ if (ptr != NULL) {
+ /* We've got the memory.
+ */
+ if ((flags & ALLOC_FINALIZABLE) != 0) {
+ /* This object is an instance of a class that
+ * overrides finalize(). Add it to the finalizable list.
+ */
+ if (!dvmHeapAddRefToLargeTable(&gcHeap->finalizableRefs,
+ (Object *)ptr))
+ {
+ LOGE_HEAP("dvmMalloc(): no room for any more "
+ "finalizable objects\n");
+ dvmAbort();
+ }
+ }
+
+ if (gDvm.allocProf.enabled) {
+ Thread* self = dvmThreadSelf();
+ gDvm.allocProf.allocCount++;
+ gDvm.allocProf.allocSize += size;
+ if (self != NULL) {
+ self->allocProf.allocCount++;
+ self->allocProf.allocSize += size;
+ }
+ }
+ } else {
+ /* The allocation failed.
+ */
+
+ if (gDvm.allocProf.enabled) {
+ Thread* self = dvmThreadSelf();
+ gDvm.allocProf.failedAllocCount++;
+ gDvm.allocProf.failedAllocSize += size;
+ if (self != NULL) {
+ self->allocProf.failedAllocCount++;
+ self->allocProf.failedAllocSize += size;
+ }
+ }
+ }
+
+ dvmUnlockHeap();
+
+ if (ptr != NULL) {
+ /*
+ * If caller hasn't asked us not to track it, add it to the
+ * internal tracking list.
+ */
+ if ((flags & ALLOC_DONT_TRACK) == 0) {
+ dvmAddTrackedAlloc(ptr, NULL);
+ }
+ } else {
+ /*
+ * The allocation failed; throw an OutOfMemoryError.
+ */
+ throwOOME();
+ }
+
+ return ptr;
+}
+
+/*
+ * Returns true iff <obj> points to a valid allocated object.
+ */
+bool dvmIsValidObject(const Object* obj)
+{
+ /* Don't bother if it's NULL or not 8-byte aligned.
+ */
+ if (obj != NULL && ((uintptr_t)obj & (8-1)) == 0) {
+ /* Even if the heap isn't locked, this shouldn't return
+ * any false negatives. The only mutation that could
+ * be happening is allocation, which means that another
+ * thread could be in the middle of a read-modify-write
+ * to add a new bit for a new object. However, that
+ * RMW will have completed by the time any other thread
+ * could possibly see the new pointer, so there is no
+ * danger of dvmIsValidObject() being called on a valid
+ * pointer whose bit isn't set.
+ *
+ * Freeing will only happen during the sweep phase, which
+ * only happens while the heap is locked.
+ */
+ return dvmHeapSourceContains(obj);
+ }
+ return false;
+}
+
+size_t dvmObjectSizeInHeap(const Object *obj)
+{
+ return dvmHeapSourceChunkSize(obj);
+}
+
+static void verifyRootsAndHeap(void)
+{
+ dvmVerifyRoots();
+ dvmVerifyBitmap(dvmHeapSourceGetLiveBits());
+}
+
+/*
+ * Initiate garbage collection.
+ *
+ * NOTES:
+ * - If we don't hold gDvm.threadListLock, it's possible for a thread to
+ * be added to the thread list while we work. The thread should NOT
+ * start executing, so this is only interesting when we start chasing
+ * thread stacks. (Before we do so, grab the lock.)
+ *
+ * We are not allowed to GC when the debugger has suspended the VM, which
+ * is awkward because debugger requests can cause allocations. The easiest
+ * way to enforce this is to refuse to GC on an allocation made by the
+ * JDWP thread -- we have to expand the heap or fail.
+ */
+void dvmCollectGarbageInternal(bool clearSoftRefs, GcReason reason)
+{
+ GcHeap *gcHeap = gDvm.gcHeap;
+ u4 rootSuspend, rootSuspendTime, rootStart, rootEnd;
+ u4 dirtySuspend, dirtyStart, dirtyEnd;
+ u4 totalTime;
+ size_t numObjectsFreed, numBytesFreed;
+ size_t currAllocated, currFootprint;
+ size_t extAllocated, extLimit;
+ size_t percentFree;
+ GcMode gcMode;
+ int oldThreadPriority = kInvalidPriority;
+
+ /* The heap lock must be held.
+ */
+
+ if (gcHeap->gcRunning) {
+ LOGW_HEAP("Attempted recursive GC\n");
+ return;
+ }
+
+ gcMode = (reason == GC_FOR_MALLOC) ? GC_PARTIAL : GC_FULL;
+ gcHeap->gcRunning = true;
+
+ rootSuspend = dvmGetRelativeTimeMsec();
+ dvmSuspendAllThreads(SUSPEND_FOR_GC);
+ rootStart = dvmGetRelativeTimeMsec();
+ rootSuspendTime = rootStart - rootSuspend;
+
+ /*
+ * If we are not marking concurrently raise the priority of the
+ * thread performing the garbage collection.
+ */
+ if (reason != GC_CONCURRENT) {
+ /* Get the priority (the "nice" value) of the current thread. The
+ * getpriority() call can legitimately return -1, so we have to
+ * explicitly test errno.
+ */
+ errno = 0;
+ int priorityResult = getpriority(PRIO_PROCESS, 0);
+ if (errno != 0) {
+ LOGI_HEAP("getpriority(self) failed: %s\n", strerror(errno));
+ } else if (priorityResult > ANDROID_PRIORITY_NORMAL) {
+ /* Current value is numerically greater than "normal", which
+ * in backward UNIX terms means lower priority.
+ */
+
+ if (priorityResult >= ANDROID_PRIORITY_BACKGROUND) {
+ set_sched_policy(dvmGetSysThreadId(), SP_FOREGROUND);
+ }
+
+ if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_NORMAL) != 0) {
+ LOGI_HEAP("Unable to elevate priority from %d to %d\n",
+ priorityResult, ANDROID_PRIORITY_NORMAL);
+ } else {
+ /* priority elevated; save value so we can restore it later */
+ LOGD_HEAP("Elevating priority from %d to %d\n",
+ priorityResult, ANDROID_PRIORITY_NORMAL);
+ oldThreadPriority = priorityResult;
+ }
+ }
+ }
+
+ /* Wait for the HeapWorker thread to block.
+ * (It may also already be suspended in interp code,
+ * in which case it's not holding heapWorkerLock.)
+ */
+ dvmLockMutex(&gDvm.heapWorkerLock);
+
+ /* Make sure that the HeapWorker thread hasn't become
+ * wedged inside interp code. If it has, this call will
+ * print a message and abort the VM.
+ */
+ dvmAssertHeapWorkerThreadRunning();
+
+ /* Lock the pendingFinalizationRefs list.
+ *
+ * Acquire the lock after suspending so the finalizer
+ * thread can't block in the RUNNING state while
+ * we try to suspend.
+ */
+ dvmLockMutex(&gDvm.heapWorkerListLock);
+
+ if (gDvm.preVerify) {
+ LOGV_HEAP("Verifying roots and heap before GC");
+ verifyRootsAndHeap();
+ }
+
+ dvmMethodTraceGCBegin();
+
+#if WITH_HPROF
+
+/* Set DUMP_HEAP_ON_DDMS_UPDATE to 1 to enable heap dumps
+ * whenever DDMS requests a heap update (HPIF chunk).
+ * The output files will appear in /data/misc, which must
+ * already exist.
+ * You must define "WITH_HPROF := true" in your buildspec.mk
+ * and recompile libdvm for this to work.
+ *
+ * To enable stack traces for each allocation, define
+ * "WITH_HPROF_STACK := true" in buildspec.mk. This option slows down
+ * allocations and also requires 8 additional bytes per object on the
+ * GC heap.
+ */
+#define DUMP_HEAP_ON_DDMS_UPDATE 0
+#if DUMP_HEAP_ON_DDMS_UPDATE
+ gcHeap->hprofDumpOnGc |= (gcHeap->ddmHpifWhen != 0);
+#endif
+
+ if (gcHeap->hprofDumpOnGc) {
+ char nameBuf[128];
+
+ gcHeap->hprofResult = -1;
+
+ if (gcHeap->hprofFileName == NULL) {
+ /* no filename was provided; invent one */
+ sprintf(nameBuf, "/data/misc/heap-dump-tm%d-pid%d.hprof",
+ (int) time(NULL), (int) getpid());
+ gcHeap->hprofFileName = nameBuf;
+ }
+ gcHeap->hprofContext = hprofStartup(gcHeap->hprofFileName,
+ gcHeap->hprofFd, gcHeap->hprofDirectToDdms);
+ if (gcHeap->hprofContext != NULL) {
+ hprofStartHeapDump(gcHeap->hprofContext);
+ }
+ gcHeap->hprofDumpOnGc = false;
+ gcHeap->hprofFileName = NULL;
+ }
+#endif
+
+ /* Set up the marking context.
+ */
+ if (!dvmHeapBeginMarkStep(gcMode)) {
+ LOGE_HEAP("dvmHeapBeginMarkStep failed; aborting\n");
+ dvmAbort();
+ }
+
+ /* Mark the set of objects that are strongly reachable from the roots.
+ */
+ LOGD_HEAP("Marking...");
+ dvmHeapMarkRootSet();
+
+ /* dvmHeapScanMarkedObjects() will build the lists of known
+ * instances of the Reference classes.
+ */
+ gcHeap->softReferences = NULL;
+ gcHeap->weakReferences = NULL;
+ gcHeap->phantomReferences = NULL;
+
+ if (reason == GC_CONCURRENT) {
+ /*
+ * Resume threads while tracing from the roots. We unlock the
+ * heap to allow mutator threads to allocate from free space.
+ */
+ rootEnd = dvmGetRelativeTimeMsec();
+ dvmClearCardTable();
+ dvmUnlockHeap();
+ dvmResumeAllThreads(SUSPEND_FOR_GC);
+ }
+
+ /* Recursively mark any objects that marked objects point to strongly.
+ * If we're not collecting soft references, soft-reachable
+ * objects will also be marked.
+ */
+ LOGD_HEAP("Recursing...");
+ dvmHeapScanMarkedObjects();
+
+ if (reason == GC_CONCURRENT) {
+ /*
+ * Re-acquire the heap lock and perform the final thread
+ * suspension.
+ */
+ dvmLockHeap();
+ dirtySuspend = dvmGetRelativeTimeMsec();
+ dvmSuspendAllThreads(SUSPEND_FOR_GC);
+ dirtyStart = dvmGetRelativeTimeMsec();
+ /*
+ * As no barrier intercepts root updates, we conservatively
+ * assume all roots may be gray and re-mark them.
+ */
+ dvmHeapReMarkRootSet();
+ /*
+ * With the exception of reference objects and weak interned
+ * strings, all gray objects should now be on dirty cards.
+ */
+ if (gDvm.verifyCardTable) {
+ dvmVerifyCardTable();
+ }
+ /*
+ * Recursively mark gray objects pointed to by the roots or by
+ * heap objects dirtied during the concurrent mark.
+ */
+ dvmHeapReScanMarkedObjects();
+ }
+
+ /* All strongly-reachable objects have now been marked.
+ */
+ LOGD_HEAP("Handling soft references...");
+ if (!clearSoftRefs) {
+ dvmHandleSoftRefs(&gcHeap->softReferences);
+ }
+ dvmClearWhiteRefs(&gcHeap->softReferences);
+
+ LOGD_HEAP("Handling weak references...");
+ dvmClearWhiteRefs(&gcHeap->weakReferences);
+
+ /* Once all weak-reachable objects have been taken
+ * care of, any remaining unmarked objects can be finalized.
+ */
+ LOGD_HEAP("Finding finalizations...");
+ dvmHeapScheduleFinalizations();
+
+ LOGD_HEAP("Handling f-reachable soft references...");
+ dvmClearWhiteRefs(&gcHeap->softReferences);
+
+ LOGD_HEAP("Handling f-reachable weak references...");
+ dvmClearWhiteRefs(&gcHeap->weakReferences);
+
+ /* Any remaining objects that are not pending finalization
+ * could be phantom-reachable. This will mark any phantom-reachable
+ * objects, as well as enqueue their references.
+ */
+ LOGD_HEAP("Handling phantom references...");
+ dvmClearWhiteRefs(&gcHeap->phantomReferences);
+
+#if defined(WITH_JIT)
+ /*
+ * Patching a chaining cell is very cheap as it only updates 4 words. It's
+ * the overhead of stopping all threads and synchronizing the I/D cache
+ * that makes it expensive.
+ *
+ * Therefore we batch those work orders in a queue and go through them
+ * when threads are suspended for GC.
+ */
+ dvmCompilerPerformSafePointChecks();
+#endif
+
+ LOGD_HEAP("Sweeping...");
+
+ dvmHeapSweepSystemWeaks();
+
+ /*
+ * Live objects have a bit set in the mark bitmap, swap the mark
+ * and live bitmaps. The sweep can proceed concurrently viewing
+ * the new live bitmap as the old mark bitmap, and vice versa.
+ */
+ dvmHeapSourceSwapBitmaps();
+
+ if (gDvm.postVerify) {
+ LOGV_HEAP("Verifying roots and heap after GC");
+ verifyRootsAndHeap();
+ }
+
+ if (reason == GC_CONCURRENT) {
+ dirtyEnd = dvmGetRelativeTimeMsec();
+ dvmUnlockHeap();
+ dvmResumeAllThreads(SUSPEND_FOR_GC);
+ }
+ dvmHeapSweepUnmarkedObjects(gcMode, reason == GC_CONCURRENT,
+ &numObjectsFreed, &numBytesFreed);
+ LOGD_HEAP("Cleaning up...");
+ dvmHeapFinishMarkStep();
+ if (reason == GC_CONCURRENT) {
+ dvmLockHeap();
+ }
+
+ LOGD_HEAP("Done.");
+
+ /* Now's a good time to adjust the heap size, since
+ * we know what our utilization is.
+ *
+ * This doesn't actually resize any memory;
+ * it just lets the heap grow more when necessary.
+ */
+ if (reason != GC_EXTERNAL_ALLOC) {
+ dvmHeapSourceGrowForUtilization();
+ }
+
+ currAllocated = dvmHeapSourceGetValue(HS_BYTES_ALLOCATED, NULL, 0);
+ currFootprint = dvmHeapSourceGetValue(HS_FOOTPRINT, NULL, 0);
+
+#if WITH_HPROF
+ if (gcHeap->hprofContext != NULL) {
+ hprofFinishHeapDump(gcHeap->hprofContext);
+//TODO: write a HEAP_SUMMARY record
+ if (hprofShutdown(gcHeap->hprofContext))
+ gcHeap->hprofResult = 0; /* indicate success */
+ gcHeap->hprofContext = NULL;
+ }
+#endif
+
+ /* Now that we've freed up the GC heap, return any large
+ * free chunks back to the system. They'll get paged back
+ * in the next time they're used. Don't do it immediately,
+ * though; if the process is still allocating a bunch of
+ * memory, we'll be taking a ton of page faults that we don't
+ * necessarily need to.
+ *
+ * Cancel any old scheduled trims, and schedule a new one.
+ */
+ dvmScheduleHeapSourceTrim(5); // in seconds
+
+ dvmMethodTraceGCEnd();
+ LOGV_HEAP("GC finished");
+
+ gcHeap->gcRunning = false;
+
+ LOGV_HEAP("Resuming threads");
+ dvmUnlockMutex(&gDvm.heapWorkerListLock);
+ dvmUnlockMutex(&gDvm.heapWorkerLock);
+
+ if (reason == GC_CONCURRENT) {
+ /*
+ * Wake-up any threads that blocked after a failed allocation
+ * request.
+ */
+ dvmBroadcastCond(&gDvm.gcHeapCond);
+ }
+
+ if (reason != GC_CONCURRENT) {
+ dirtyEnd = dvmGetRelativeTimeMsec();
+ dvmResumeAllThreads(SUSPEND_FOR_GC);
+ if (oldThreadPriority != kInvalidPriority) {
+ if (setpriority(PRIO_PROCESS, 0, oldThreadPriority) != 0) {
+ LOGW_HEAP("Unable to reset priority to %d: %s\n",
+ oldThreadPriority, strerror(errno));
+ } else {
+ LOGD_HEAP("Reset priority to %d\n", oldThreadPriority);
+ }
+
+ if (oldThreadPriority >= ANDROID_PRIORITY_BACKGROUND) {
+ set_sched_policy(dvmGetSysThreadId(), SP_BACKGROUND);
+ }
+ }
+ }
+
+ extAllocated = dvmHeapSourceGetValue(HS_EXTERNAL_BYTES_ALLOCATED, NULL, 0);
+ extLimit = dvmHeapSourceGetValue(HS_EXTERNAL_LIMIT, NULL, 0);
+ percentFree = 100 - (size_t)(100.0f * (float)currAllocated / currFootprint);
+ if (reason != GC_CONCURRENT) {
+ u4 markSweepTime = dirtyEnd - rootStart;
+ bool isSmall = numBytesFreed > 0 && numBytesFreed < 1024;
+ totalTime = rootSuspendTime + markSweepTime;
+ LOGD("%s freed %s%zdK, %d%% free %zdK/%zdK, external %zdK/%zdK, "
+ "paused %ums",
+ GcReasonStr[reason],
+ isSmall ? "<" : "",
+ numBytesFreed ? MAX(numBytesFreed / 1024, 1) : 0,
+ percentFree,
+ currAllocated / 1024, currFootprint / 1024,
+ extAllocated / 1024, extLimit / 1024,
+ markSweepTime);
+ } else {
+ u4 rootTime = rootEnd - rootStart;
+ u4 dirtySuspendTime = dirtyStart - dirtySuspend;
+ u4 dirtyTime = dirtyEnd - dirtyStart;
+ bool isSmall = numBytesFreed > 0 && numBytesFreed < 1024;
+ totalTime = rootSuspendTime + rootTime + dirtySuspendTime + dirtyTime;
+ LOGD("%s freed %s%zdK, %d%% free %zdK/%zdK, external %zdK/%zdK, "
+ "paused %ums+%ums",
+ GcReasonStr[reason],
+ isSmall ? "<" : "",
+ numBytesFreed ? MAX(numBytesFreed / 1024, 1) : 0,
+ percentFree,
+ currAllocated / 1024, currFootprint / 1024,
+ extAllocated / 1024, extLimit / 1024,
+ rootTime, dirtyTime);
+ }
+ dvmLogGcStats(numObjectsFreed, numBytesFreed, totalTime);
+ if (gcHeap->ddmHpifWhen != 0) {
+ LOGD_HEAP("Sending VM heap info to DDM\n");
+ dvmDdmSendHeapInfo(gcHeap->ddmHpifWhen, false);
+ }
+ if (gcHeap->ddmHpsgWhen != 0) {
+ LOGD_HEAP("Dumping VM heap to DDM\n");
+ dvmDdmSendHeapSegments(false, false);
+ }
+ if (gcHeap->ddmNhsgWhen != 0) {
+ LOGD_HEAP("Dumping native heap to DDM\n");
+ dvmDdmSendHeapSegments(false, true);
+ }
+}
+
+void dvmWaitForConcurrentGcToComplete(void)
+{
+ Thread *self = dvmThreadSelf();
+ ThreadStatus oldStatus;
+ assert(self != NULL);
+ oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
+ dvmWaitCond(&gDvm.gcHeapCond, &gDvm.gcHeapLock);
+ dvmChangeStatus(self, oldStatus);
+}
+
+#if WITH_HPROF
+/*
+ * Perform garbage collection, writing heap information to the specified file.
+ *
+ * If "fd" is >= 0, the output will be written to that file descriptor.
+ * Otherwise, "fileName" is used to create an output file.
+ *
+ * If "fileName" is NULL, a suitable name will be generated automatically.
+ * (TODO: remove this when the SIGUSR1 feature goes away)
+ *
+ * If "directToDdms" is set, the other arguments are ignored, and data is
+ * sent directly to DDMS.
+ *
+ * Returns 0 on success, or an error code on failure.
+ */
+int hprofDumpHeap(const char* fileName, int fd, bool directToDdms)
+{
+ int result;
+
+ dvmLockMutex(&gDvm.gcHeapLock);
+
+ gDvm.gcHeap->hprofDumpOnGc = true;
+ gDvm.gcHeap->hprofFileName = fileName;
+ gDvm.gcHeap->hprofFd = fd;
+ gDvm.gcHeap->hprofDirectToDdms = directToDdms;
+ dvmCollectGarbageInternal(false, GC_HPROF_DUMP_HEAP);
+ result = gDvm.gcHeap->hprofResult;
+
+ dvmUnlockMutex(&gDvm.gcHeapLock);
+
+ return result;
+}
+
+void dvmHeapSetHprofGcScanState(hprof_heap_tag_t state, u4 threadSerialNumber)
+{
+ if (gDvm.gcHeap->hprofContext != NULL) {
+ hprofSetGcScanState(gDvm.gcHeap->hprofContext, state,
+ threadSerialNumber);
+ }
+}
+#endif
diff --git a/vm/alloc/Heap.h b/vm/alloc/Heap.h
new file mode 100644
index 0000000..ea0510f
--- /dev/null
+++ b/vm/alloc/Heap.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Internal heap functions
+ */
+#ifndef _DALVIK_ALLOC_HEAP
+#define _DALVIK_ALLOC_HEAP
+
+/*
+ * Initialize the GC heap.
+ *
+ * Returns true if successful, false otherwise.
+ */
+bool dvmHeapStartup(void);
+
+/*
+ * Initialization that needs to wait until after leaving zygote mode.
+ * This needs to be called before the first allocation or GC that
+ * happens after forking.
+ */
+bool dvmHeapStartupAfterZygote(void);
+
+/*
+ * Tear down the GC heap.
+ *
+ * Frees all memory allocated via dvmMalloc() as
+ * a side-effect.
+ */
+void dvmHeapShutdown(void);
+
+/*
+ * Stops any threads internal to the garbage collector. Called before
+ * the heap itself is shutdown.
+ */
+void dvmHeapThreadShutdown(void);
+
+#if 0 // needs to be in Alloc.h so debug code can find it.
+/*
+ * Returns a number of bytes greater than or
+ * equal to the size of the named object in the heap.
+ *
+ * Specifically, it returns the size of the heap
+ * chunk which contains the object.
+ */
+size_t dvmObjectSizeInHeap(const Object *obj);
+#endif
+
+typedef enum {
+ /* GC all heaps. */
+ GC_FULL,
+ /* GC just the first heap. */
+ GC_PARTIAL
+} GcMode;
+
+typedef enum {
+ /* Not enough space for an "ordinary" Object to be allocated. */
+ GC_FOR_MALLOC,
+ /* Automatic GC triggered by exceeding a heap occupancy threshold. */
+ GC_CONCURRENT,
+ /* Explicit GC via Runtime.gc(), VMRuntime.gc(), or SIGUSR1. */
+ GC_EXPLICIT,
+ /* GC to try to reduce heap footprint to allow more non-GC'ed memory. */
+ GC_EXTERNAL_ALLOC,
+ /* GC to dump heap contents to a file, only used under WITH_HPROF */
+ GC_HPROF_DUMP_HEAP
+} GcReason;
+
+/*
+ * Run the garbage collector without doing any locking.
+ */
+void dvmCollectGarbageInternal(bool clearSoftRefs, GcReason reason);
+
+/*
+ * Blocks the until the GC thread signals the completion of a
+ * concurrent GC.
+ */
+void dvmWaitForConcurrentGcToComplete(void);
+
+#endif // _DALVIK_ALLOC_HEAP
diff --git a/vm/alloc/HeapBitmap.c b/vm/alloc/HeapBitmap.c
new file mode 100644
index 0000000..08d5976
--- /dev/null
+++ b/vm/alloc/HeapBitmap.c
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "HeapBitmap.h"
+#include "clz.h"
+#include <sys/mman.h> /* for PROT_* */
+
+/*
+ * Initialize a HeapBitmap so that it points to a bitmap large
+ * enough to cover a heap at <base> of <maxSize> bytes, where
+ * objects are guaranteed to be HB_OBJECT_ALIGNMENT-aligned.
+ */
+bool
+dvmHeapBitmapInit(HeapBitmap *hb, const void *base, size_t maxSize,
+ const char *name)
+{
+ void *bits;
+ size_t bitsLen;
+
+ assert(hb != NULL);
+ assert(name != NULL);
+ bitsLen = HB_OFFSET_TO_INDEX(maxSize) * sizeof(*hb->bits);
+ bits = dvmAllocRegion(bitsLen, PROT_READ | PROT_WRITE, name);
+ if (bits == NULL) {
+ LOGE("Could not mmap %zd-byte ashmem region '%s'", bitsLen, name);
+ return false;
+ }
+ hb->bits = bits;
+ hb->bitsLen = hb->allocLen = bitsLen;
+ hb->base = (uintptr_t)base;
+ hb->max = hb->base - 1;
+ return true;
+}
+
+/*
+ * Clean up any resources associated with the bitmap.
+ */
+void
+dvmHeapBitmapDelete(HeapBitmap *hb)
+{
+ assert(hb != NULL);
+
+ if (hb->bits != NULL) {
+ munmap((char *)hb->bits, hb->allocLen);
+ }
+ memset(hb, 0, sizeof(*hb));
+}
+
+/*
+ * Fill the bitmap with zeroes. Returns the bitmap's memory to
+ * the system as a side-effect.
+ */
+void
+dvmHeapBitmapZero(HeapBitmap *hb)
+{
+ assert(hb != NULL);
+
+ if (hb->bits != NULL) {
+ /* This returns the memory to the system.
+ * Successive page faults will return zeroed memory.
+ */
+ madvise(hb->bits, hb->bitsLen, MADV_DONTNEED);
+ hb->max = hb->base - 1;
+ }
+}
+
+/*
+ * Walk through the bitmaps in increasing address order, and find the
+ * object pointers that correspond to garbage objects. Call
+ * <callback> zero or more times with lists of these object pointers.
+ *
+ * The callback is permitted to increase the bitmap's max; the walk
+ * will use the updated max as a terminating condition,
+ */
+void dvmHeapBitmapSweepWalk(const HeapBitmap *liveHb, const HeapBitmap *markHb,
+ BitmapSweepCallback *callback, void *callbackArg)
+{
+ static const size_t kPointerBufSize = 128;
+ void *pointerBuf[kPointerBufSize];
+ void **pb = pointerBuf;
+ size_t index;
+ size_t i;
+
+#define FLUSH_POINTERBUF() \
+ do { \
+ (*callback)(pb - pointerBuf, (void **)pointerBuf, \
+ callbackArg); \
+ pb = pointerBuf; \
+ } while (false)
+
+#define DECODE_BITS(hb_, bits_, update_index_) \
+ do { \
+ if (UNLIKELY(bits_ != 0)) { \
+ static const unsigned long kHighBit = \
+ (unsigned long)1 << (HB_BITS_PER_WORD - 1); \
+ const uintptr_t ptrBase = HB_INDEX_TO_OFFSET(i) + hb_->base; \
+/*TODO: hold onto ptrBase so we can shrink max later if possible */ \
+/*TODO: see if this is likely or unlikely */ \
+ while (bits_ != 0) { \
+ const int rshift = CLZ(bits_); \
+ bits_ &= ~(kHighBit >> rshift); \
+ *pb++ = (void *)(ptrBase + rshift * HB_OBJECT_ALIGNMENT); \
+ } \
+ /* Make sure that there are always enough slots available */ \
+ /* for an entire word of 1s. */ \
+ if (kPointerBufSize - (pb - pointerBuf) < HB_BITS_PER_WORD) { \
+ FLUSH_POINTERBUF(); \
+ if (update_index_) { \
+ /* The callback may have caused hb_->max to grow. */ \
+ index = HB_OFFSET_TO_INDEX(hb_->max - hb_->base); \
+ } \
+ } \
+ } \
+ } while (false)
+
+ assert(liveHb != NULL);
+ assert(liveHb->bits != NULL);
+ assert(markHb != NULL);
+ assert(markHb->bits != NULL);
+ assert(callback != NULL);
+
+ if (liveHb->base != markHb->base) {
+ LOGW("dvmHeapBitmapSweepWalk: bitmaps cover different heaps (%zd != %zd)",
+ liveHb->base, markHb->base);
+ return;
+ }
+ if (liveHb->bitsLen != markHb->bitsLen) {
+ LOGW("dvmHeapBitmapSweepWalk: size of bitmaps differ (%zd != %zd)",
+ liveHb->bitsLen, markHb->bitsLen);
+ return;
+ }
+ if (liveHb->max < liveHb->base && markHb->max < markHb->base) {
+ /* Easy case; both are obviously empty.
+ */
+ return;
+ }
+
+ /* First, walk along the section of the bitmaps that may be the same.
+ */
+ if (liveHb->max >= liveHb->base && markHb->max >= markHb->base) {
+ unsigned long *live, *mark;
+ uintptr_t offset;
+
+ offset = ((liveHb->max < markHb->max) ? liveHb->max : markHb->max) - liveHb->base;
+//TODO: keep track of which (and whether) one is longer for later
+ index = HB_OFFSET_TO_INDEX(offset);
+
+ live = liveHb->bits;
+ mark = markHb->bits;
+ for (i = 0; i <= index; i++) {
+//TODO: unroll this. pile up a few in locals?
+ unsigned long garbage = live[i] & ~mark[i];
+ DECODE_BITS(liveHb, garbage, false);
+//BUG: if the callback was called, either max could have changed.
+ }
+ /* The next index to look at.
+ */
+ index++;
+ } else {
+ /* One of the bitmaps is empty.
+ */
+ index = 0;
+ }
+
+ /* If one bitmap's max is larger, walk through the rest of the
+ * set bits.
+ */
+const HeapBitmap *longHb;
+unsigned long *p;
+//TODO: may be the same size, in which case this is wasted work
+ longHb = (liveHb->max > markHb->max) ? liveHb : markHb;
+ i = index;
+ index = HB_OFFSET_TO_INDEX(longHb->max - longHb->base);
+ p = longHb->bits + i;
+ for (/* i = i */; i <= index; i++) {
+//TODO: unroll this
+ unsigned long bits = *p++;
+ DECODE_BITS(longHb, bits, true);
+ }
+
+ if (pb > pointerBuf) {
+ FLUSH_POINTERBUF();
+ }
+#undef FLUSH_POINTERBUF
+#undef DECODE_BITS
+}
diff --git a/vm/alloc/HeapBitmap.h b/vm/alloc/HeapBitmap.h
new file mode 100644
index 0000000..bb09020
--- /dev/null
+++ b/vm/alloc/HeapBitmap.h
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _DALVIK_HEAP_BITMAP
+#define _DALVIK_HEAP_BITMAP
+
+#include <limits.h>
+#include <stdint.h>
+#include "clz.h"
+
+#define HB_OBJECT_ALIGNMENT 8
+#define HB_BITS_PER_WORD (sizeof(unsigned long) * CHAR_BIT)
+
+/* <offset> is the difference from .base to a pointer address.
+ * <index> is the index of .bits that contains the bit representing
+ * <offset>.
+ */
+#define HB_OFFSET_TO_INDEX(offset_) \
+ ((uintptr_t)(offset_) / HB_OBJECT_ALIGNMENT / HB_BITS_PER_WORD)
+#define HB_INDEX_TO_OFFSET(index_) \
+ ((uintptr_t)(index_) * HB_OBJECT_ALIGNMENT * HB_BITS_PER_WORD)
+
+#define HB_OFFSET_TO_BYTE_INDEX(offset_) \
+ (HB_OFFSET_TO_INDEX(offset_) * sizeof(*((HeapBitmap *)0)->bits))
+
+/* Pack the bits in backwards so they come out in address order
+ * when using CLZ.
+ */
+#define HB_OFFSET_TO_MASK(offset_) \
+ (1 << \
+ (31-(((uintptr_t)(offset_) / HB_OBJECT_ALIGNMENT) % HB_BITS_PER_WORD)))
+
+/* Return the maximum offset (exclusive) that <hb> can represent.
+ */
+#define HB_MAX_OFFSET(hb_) \
+ HB_INDEX_TO_OFFSET((hb_)->bitsLen / sizeof(*(hb_)->bits))
+
+#define HB_INLINE_PROTO(p) \
+ static inline p __attribute__((always_inline)); \
+ static inline p
+
+typedef struct {
+ /* The bitmap data, which points to an mmap()ed area of zeroed
+ * anonymous memory.
+ */
+ unsigned long *bits;
+
+ /* The size of the used memory pointed to by bits, in bytes. This
+ * value changes when the bitmap is shrunk.
+ */
+ size_t bitsLen;
+
+ /* The real size of the memory pointed to by bits. This is the
+ * number of bytes we requested from the allocator and does not
+ * change.
+ */
+ size_t allocLen;
+
+ /* The base address, which corresponds to the first bit in
+ * the bitmap.
+ */
+ uintptr_t base;
+
+ /* The highest pointer value ever returned by an allocation
+ * from this heap. I.e., the highest address that may correspond
+ * to a set bit. If there are no bits set, (max < base).
+ */
+ uintptr_t max;
+} HeapBitmap;
+
+typedef void BitmapCallback(void *addr, void *arg);
+typedef void BitmapScanCallback(void *addr, void *finger, void *arg);
+typedef void BitmapSweepCallback(size_t numPtrs, void **ptrs, void *arg);
+
+/*
+ * Initialize a HeapBitmap so that it points to a bitmap large
+ * enough to cover a heap at <base> of <maxSize> bytes, where
+ * objects are guaranteed to be HB_OBJECT_ALIGNMENT-aligned.
+ */
+bool dvmHeapBitmapInit(HeapBitmap *hb, const void *base, size_t maxSize,
+ const char *name);
+
+/*
+ * Clean up any resources associated with the bitmap.
+ */
+void dvmHeapBitmapDelete(HeapBitmap *hb);
+
+/*
+ * Fill the bitmap with zeroes. Returns the bitmap's memory to
+ * the system as a side-effect.
+ */
+void dvmHeapBitmapZero(HeapBitmap *hb);
+
+/*
+ * Visits set bits in address order. The callback is not permitted to
+ * change the bitmap bits or max during the traversal.
+ */
+HB_INLINE_PROTO(
+ void
+ dvmHeapBitmapWalk(const HeapBitmap *bitmap,
+ BitmapCallback *callback, void *arg)
+)
+{
+ assert(bitmap != NULL);
+ assert(bitmap->bits != NULL);
+ assert(callback != NULL);
+ uintptr_t end = HB_OFFSET_TO_INDEX(bitmap->max - bitmap->base);
+ uintptr_t i;
+ for (i = 0; i <= end; ++i) {
+ unsigned long word = bitmap->bits[i];
+ if (UNLIKELY(word != 0)) {
+ unsigned long highBit = 1 << (HB_BITS_PER_WORD - 1);
+ uintptr_t ptrBase = HB_INDEX_TO_OFFSET(i) + bitmap->base;
+ while (word != 0) {
+ const int shift = CLZ(word);
+ void *addr = (void *)(ptrBase + shift * HB_OBJECT_ALIGNMENT);
+ (*callback)(addr, arg);
+ word &= ~(highBit >> shift);
+ }
+ }
+ }
+}
+
+/*
+ * Similar to dvmHeapBitmapWalk but the callback routine is permitted
+ * to change the bitmap bits and max during traversal. Used by the
+ * the root marking scan exclusively.
+ *
+ * The callback is invoked with a finger argument. The finger is a
+ * pointer to an address not yet visited by the traversal. If the
+ * callback sets a bit for an address at or above the finger, this
+ * address will be visited by the traversal. If the callback sets a
+ * bit for an address below the finger, this address will not be
+ * visited.
+ */
+HB_INLINE_PROTO(
+ void
+ dvmHeapBitmapScanWalk(HeapBitmap *bitmap,
+ BitmapScanCallback *callback, void *arg)
+)
+{
+ assert(bitmap != NULL);
+ assert(bitmap->bits != NULL);
+ assert(callback != NULL);
+ uintptr_t end = HB_OFFSET_TO_INDEX(bitmap->max - bitmap->base);
+ uintptr_t i;
+ for (i = 0; i <= end; ++i) {
+ unsigned long word = bitmap->bits[i];
+ if (UNLIKELY(word != 0)) {
+ unsigned long highBit = 1 << (HB_BITS_PER_WORD - 1);
+ uintptr_t ptrBase = HB_INDEX_TO_OFFSET(i) + bitmap->base;
+ void *finger = (void *)(HB_INDEX_TO_OFFSET(i + 1) + bitmap->base);
+ while (word != 0) {
+ const int shift = CLZ(word);
+ void *addr = (void *)(ptrBase + shift * HB_OBJECT_ALIGNMENT);
+ (*callback)(addr, finger, arg);
+ word &= ~(highBit >> shift);
+ }
+ end = HB_OFFSET_TO_INDEX(bitmap->max - bitmap->base);
+ }
+ }
+}
+
+/*
+ * Walk through the bitmaps in increasing address order, and find the
+ * object pointers that correspond to garbage objects. Call
+ * <callback> zero or more times with lists of these object pointers.
+ *
+ * The callback is permitted to increase the bitmap's max; the walk
+ * will use the updated max as a terminating condition.
+ */
+void dvmHeapBitmapSweepWalk(const HeapBitmap *liveHb, const HeapBitmap *markHb,
+ BitmapSweepCallback *callback, void *callbackArg);
+
+/*
+ * Return true iff <obj> is within the range of pointers that this
+ * bitmap could potentially cover, even if a bit has not been set
+ * for it.
+ */
+HB_INLINE_PROTO(
+ bool
+ dvmHeapBitmapCoversAddress(const HeapBitmap *hb, const void *obj)
+)
+{
+ assert(hb != NULL);
+
+ if (obj != NULL) {
+ const uintptr_t offset = (uintptr_t)obj - hb->base;
+ const size_t index = HB_OFFSET_TO_INDEX(offset);
+ return index < hb->bitsLen / sizeof(*hb->bits);
+ }
+ return false;
+}
+
+/*
+ * Internal function; do not call directly.
+ */
+HB_INLINE_PROTO(
+ unsigned long
+ _heapBitmapModifyObjectBit(HeapBitmap *hb, const void *obj,
+ bool setBit, bool returnOld)
+)
+{
+ const uintptr_t offset = (uintptr_t)obj - hb->base;
+ const size_t index = HB_OFFSET_TO_INDEX(offset);
+ const unsigned long mask = HB_OFFSET_TO_MASK(offset);
+
+ assert(hb->bits != NULL);
+ assert((uintptr_t)obj >= hb->base);
+ assert(index < hb->bitsLen / sizeof(*hb->bits));
+
+ if (setBit) {
+ if ((uintptr_t)obj > hb->max) {
+ hb->max = (uintptr_t)obj;
+ }
+ if (returnOld) {
+ unsigned long *p = hb->bits + index;
+ const unsigned long word = *p;
+ *p |= mask;
+ return word & mask;
+ } else {
+ hb->bits[index] |= mask;
+ }
+ } else {
+ hb->bits[index] &= ~mask;
+ }
+ return false;
+}
+
+/*
+ * Sets the bit corresponding to <obj>, and returns the previous value
+ * of that bit (as zero or non-zero). Does no range checking to see if
+ * <obj> is outside of the coverage of the bitmap.
+ *
+ * NOTE: casting this value to a bool is dangerous, because higher
+ * set bits will be lost.
+ */
+HB_INLINE_PROTO(
+ unsigned long
+ dvmHeapBitmapSetAndReturnObjectBit(HeapBitmap *hb, const void *obj)
+)
+{
+ return _heapBitmapModifyObjectBit(hb, obj, true, true);
+}
+
+/*
+ * Sets the bit corresponding to <obj>, and widens the range of seen
+ * pointers if necessary. Does no range checking.
+ */
+HB_INLINE_PROTO(
+ void
+ dvmHeapBitmapSetObjectBit(HeapBitmap *hb, const void *obj)
+)
+{
+ (void)_heapBitmapModifyObjectBit(hb, obj, true, false);
+}
+
+/*
+ * Clears the bit corresponding to <obj>. Does no range checking.
+ */
+HB_INLINE_PROTO(
+ void
+ dvmHeapBitmapClearObjectBit(HeapBitmap *hb, const void *obj)
+)
+{
+ (void)_heapBitmapModifyObjectBit(hb, obj, false, false);
+}
+
+/*
+ * Returns the current value of the bit corresponding to <obj>,
+ * as zero or non-zero. Does no range checking.
+ *
+ * NOTE: casting this value to a bool is dangerous, because higher
+ * set bits will be lost.
+ */
+HB_INLINE_PROTO(
+ unsigned long
+ dvmHeapBitmapIsObjectBitSet(const HeapBitmap *hb, const void *obj)
+)
+{
+ assert(dvmHeapBitmapCoversAddress(hb, obj));
+ assert(hb->bits != NULL);
+ assert((uintptr_t)obj >= hb->base);
+
+ if ((uintptr_t)obj <= hb->max) {
+ const uintptr_t offset = (uintptr_t)obj - hb->base;
+ return hb->bits[HB_OFFSET_TO_INDEX(offset)] & HB_OFFSET_TO_MASK(offset);
+ } else {
+ return 0;
+ }
+}
+
+#undef HB_INLINE_PROTO
+
+#endif // _DALVIK_HEAP_BITMAP
diff --git a/vm/alloc/HeapDebug.c b/vm/alloc/HeapDebug.c
new file mode 100644
index 0000000..cb8fb48
--- /dev/null
+++ b/vm/alloc/HeapDebug.c
@@ -0,0 +1,401 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <stdlib.h>
+
+#include "Dalvik.h"
+#include "HeapInternal.h"
+#include "HeapSource.h"
+#include "Float12.h"
+
+int dvmGetHeapDebugInfo(HeapDebugInfoType info)
+{
+ switch (info) {
+ case kVirtualHeapSize:
+ return (int)dvmHeapSourceGetValue(HS_FOOTPRINT, NULL, 0);
+ case kVirtualHeapAllocated:
+ return (int)dvmHeapSourceGetValue(HS_BYTES_ALLOCATED, NULL, 0);
+ default:
+ return -1;
+ }
+}
+
+/* Looks up the cmdline for the process and tries to find
+ * the most descriptive five characters, then inserts the
+ * short name into the provided event value.
+ */
+#define PROC_NAME_LEN 5
+static void insertProcessName(long long *ep)
+{
+ static bool foundRealName = false;
+ static char name[PROC_NAME_LEN] = { 'X', 'X', 'X', 'X', 'X' };
+ long long event = *ep;
+
+ if (!foundRealName) {
+ int fd = open("/proc/self/cmdline", O_RDONLY);
+ if (fd > 0) {
+ char buf[128];
+ ssize_t n = read(fd, buf, sizeof(buf) - 1);
+ close(fd);
+ if (n > 0) {
+ memset(name, 0, sizeof(name));
+ if (n <= PROC_NAME_LEN) {
+ // The whole name fits.
+ memcpy(name, buf, n);
+ } else {
+ /* We need to truncate. The name will look something
+ * like "com.android.home". Favor the characters
+ * immediately following the last dot.
+ */
+ buf[n] = '\0';
+ char *dot = strrchr(buf, '.');
+ if (dot == NULL) {
+ /* Or, look for a slash, in case it's something like
+ * "/system/bin/runtime".
+ */
+ dot = strrchr(buf, '/');
+ }
+ if (dot != NULL) {
+ dot++; // Skip the dot
+ size_t dotlen = strlen(dot);
+ if (dotlen < PROC_NAME_LEN) {
+ /* Use all available characters. We know that
+ * n > PROC_NAME_LEN from the check above.
+ */
+ dot -= PROC_NAME_LEN - dotlen;
+ }
+ strncpy(name, dot, PROC_NAME_LEN);
+ } else {
+ // No dot; just use the leading characters.
+ memcpy(name, buf, PROC_NAME_LEN);
+ }
+ }
+ if (strcmp(buf, "zygote") != 0) {
+ /* If the process is no longer called "zygote",
+ * cache this name.
+ */
+ foundRealName = true;
+ }
+ }
+ }
+ }
+
+ event &= ~(0xffffffffffLL << 24);
+ event |= (long long)name[0] << 56;
+ event |= (long long)name[1] << 48;
+ event |= (long long)name[2] << 40;
+ event |= (long long)name[3] << 32;
+ event |= (long long)name[4] << 24;
+
+ *ep = event;
+}
+
+// See device/data/etc/event-log-tags
+#define EVENT_LOG_TAG_dvm_gc_info 20001
+#define EVENT_LOG_TAG_dvm_gc_madvise_info 20002
+
+void dvmLogGcStats(size_t numFreed, size_t sizeFreed, size_t gcTimeMs)
+{
+ size_t perHeapActualSize[HEAP_SOURCE_MAX_HEAP_COUNT],
+ perHeapAllowedSize[HEAP_SOURCE_MAX_HEAP_COUNT],
+ perHeapNumAllocated[HEAP_SOURCE_MAX_HEAP_COUNT],
+ perHeapSizeAllocated[HEAP_SOURCE_MAX_HEAP_COUNT];
+ unsigned char eventBuf[1 + (1 + sizeof(long long)) * 4];
+ size_t actualSize, allowedSize, numAllocated, sizeAllocated;
+ size_t softLimit = dvmHeapSourceGetIdealFootprint();
+ size_t nHeaps = dvmHeapSourceGetNumHeaps();
+
+ /* Enough to quiet down gcc for unitialized variable check */
+ perHeapActualSize[0] = perHeapAllowedSize[0] = perHeapNumAllocated[0] =
+ perHeapSizeAllocated[0] = 0;
+ actualSize = dvmHeapSourceGetValue(HS_FOOTPRINT, perHeapActualSize,
+ HEAP_SOURCE_MAX_HEAP_COUNT);
+ allowedSize = dvmHeapSourceGetValue(HS_ALLOWED_FOOTPRINT,
+ perHeapAllowedSize, HEAP_SOURCE_MAX_HEAP_COUNT);
+ numAllocated = dvmHeapSourceGetValue(HS_OBJECTS_ALLOCATED,
+ perHeapNumAllocated, HEAP_SOURCE_MAX_HEAP_COUNT);
+ sizeAllocated = dvmHeapSourceGetValue(HS_BYTES_ALLOCATED,
+ perHeapSizeAllocated, HEAP_SOURCE_MAX_HEAP_COUNT);
+
+ /*
+ * Construct the the first 64-bit value to write to the log.
+ * Global information:
+ *
+ * [63 ] Must be zero
+ * [62-24] ASCII process identifier
+ * [23-12] GC time in ms
+ * [11- 0] Bytes freed
+ *
+ */
+ long long event0;
+ event0 = 0LL << 63 |
+ (long long)intToFloat12(gcTimeMs) << 12 |
+ (long long)intToFloat12(sizeFreed);
+ insertProcessName(&event0);
+
+ /*
+ * Aggregated heap stats:
+ *
+ * [63-62] 10
+ * [61-60] Reserved; must be zero
+ * [59-48] Objects freed
+ * [47-36] Actual size (current footprint)
+ * [35-24] Allowed size (current hard max)
+ * [23-12] Objects allocated
+ * [11- 0] Bytes allocated
+ */
+ long long event1;
+ event1 = 2LL << 62 |
+ (long long)intToFloat12(numFreed) << 48 |
+ (long long)intToFloat12(actualSize) << 36 |
+ (long long)intToFloat12(allowedSize) << 24 |
+ (long long)intToFloat12(numAllocated) << 12 |
+ (long long)intToFloat12(sizeAllocated);
+
+ /*
+ * Report the current state of the zygote heap(s).
+ *
+ * The active heap is always heap[0]. We can be in one of three states
+ * at present:
+ *
+ * (1) Still in the zygote. Zygote using heap[0].
+ * (2) In the zygote, when the first child is started. We created a
+ * new heap just before the first fork() call, so the original
+ * "zygote heap" is now heap[1], and we have a small heap[0] for
+ * anything we do from here on.
+ * (3) In an app process. The app gets a new heap[0], and can also
+ * see the two zygote heaps [1] and [2] (probably unwise to
+ * assume any specific ordering).
+ *
+ * So if nHeaps == 1, we want the stats from heap[0]; else we want
+ * the sum of the values from heap[1] to heap[nHeaps-1].
+ *
+ *
+ * Zygote heap stats (except for the soft limit, which belongs to the
+ * active heap):
+ *
+ * [63-62] 11
+ * [61-60] Reserved; must be zero
+ * [59-48] Soft Limit (for the active heap)
+ * [47-36] Actual size (current footprint)
+ * [35-24] Allowed size (current hard max)
+ * [23-12] Objects allocated
+ * [11- 0] Bytes allocated
+ */
+ long long event2;
+ size_t zActualSize, zAllowedSize, zNumAllocated, zSizeAllocated;
+ int firstHeap = (nHeaps == 1) ? 0 : 1;
+ size_t hh;
+
+ zActualSize = zAllowedSize = zNumAllocated = zSizeAllocated = 0;
+ for (hh = firstHeap; hh < nHeaps; hh++) {
+ zActualSize += perHeapActualSize[hh];
+ zAllowedSize += perHeapAllowedSize[hh];
+ zNumAllocated += perHeapNumAllocated[hh];
+ zSizeAllocated += perHeapSizeAllocated[hh];
+ }
+ event2 = 3LL << 62 |
+ (long long)intToFloat12(softLimit) << 48 |
+ (long long)intToFloat12(zActualSize) << 36 |
+ (long long)intToFloat12(zAllowedSize) << 24 |
+ (long long)intToFloat12(zNumAllocated) << 12 |
+ (long long)intToFloat12(zSizeAllocated);
+
+ /*
+ * Report the current external allocation stats and the native heap
+ * summary.
+ *
+ * [63-48] Reserved; must be zero (TODO: put new data in these slots)
+ * [47-36] dlmalloc_footprint
+ * [35-24] mallinfo: total allocated space
+ * [23-12] External byte limit
+ * [11- 0] External bytes allocated
+ */
+ long long event3;
+ size_t externalLimit, externalBytesAllocated;
+ size_t uordblks, footprint;
+
+#if 0
+ /*
+ * This adds 2-5msec to the GC cost on a DVT, or about 2-3% of the cost
+ * of a GC, so it's not horribly expensive but it's not free either.
+ */
+ extern size_t dlmalloc_footprint(void);
+ struct mallinfo mi;
+ //u8 start, end;
+
+ //start = dvmGetRelativeTimeNsec();
+ mi = mallinfo();
+ uordblks = mi.uordblks;
+ footprint = dlmalloc_footprint();
+ //end = dvmGetRelativeTimeNsec();
+ //LOGD("mallinfo+footprint took %dusec; used=%zd footprint=%zd\n",
+ // (int)((end - start) / 1000), mi.uordblks, footprint);
+#else
+ uordblks = footprint = 0;
+#endif
+
+ externalLimit =
+ dvmHeapSourceGetValue(HS_EXTERNAL_LIMIT, NULL, 0);
+ externalBytesAllocated =
+ dvmHeapSourceGetValue(HS_EXTERNAL_BYTES_ALLOCATED, NULL, 0);
+ event3 =
+ (long long)intToFloat12(footprint) << 36 |
+ (long long)intToFloat12(uordblks) << 24 |
+ (long long)intToFloat12(externalLimit) << 12 |
+ (long long)intToFloat12(externalBytesAllocated);
+
+ /* Build the event data.
+ * [ 0: 0] item count (4)
+ * [ 1: 1] EVENT_TYPE_LONG
+ * [ 2: 9] event0
+ * [10:10] EVENT_TYPE_LONG
+ * [11:18] event1
+ * [19:19] EVENT_TYPE_LONG
+ * [20:27] event2
+ * [28:28] EVENT_TYPE_LONG
+ * [29:36] event2
+ */
+ unsigned char *c = eventBuf;
+ *c++ = 4;
+ *c++ = EVENT_TYPE_LONG;
+ memcpy(c, &event0, sizeof(event0));
+ c += sizeof(event0);
+ *c++ = EVENT_TYPE_LONG;
+ memcpy(c, &event1, sizeof(event1));
+ c += sizeof(event1);
+ *c++ = EVENT_TYPE_LONG;
+ memcpy(c, &event2, sizeof(event2));
+ c += sizeof(event2);
+ *c++ = EVENT_TYPE_LONG;
+ memcpy(c, &event3, sizeof(event3));
+
+ (void) android_btWriteLog(EVENT_LOG_TAG_dvm_gc_info, EVENT_TYPE_LIST,
+ eventBuf, sizeof(eventBuf));
+}
+
+void dvmLogMadviseStats(size_t madvisedSizes[], size_t arrayLen)
+{
+ unsigned char eventBuf[1 + (1 + sizeof(int)) * 2];
+ size_t total, zyg;
+ size_t firstHeap, i;
+ size_t nHeaps = dvmHeapSourceGetNumHeaps();
+
+ assert(arrayLen >= nHeaps);
+
+ firstHeap = nHeaps > 1 ? 1 : 0;
+ total = 0;
+ zyg = 0;
+ for (i = 0; i < nHeaps; i++) {
+ total += madvisedSizes[i];
+ if (i >= firstHeap) {
+ zyg += madvisedSizes[i];
+ }
+ }
+
+ /* Build the event data.
+ * [ 0: 0] item count (2)
+ * [ 1: 1] EVENT_TYPE_INT
+ * [ 2: 5] total madvise byte count
+ * [ 6: 6] EVENT_TYPE_INT
+ * [ 7:10] zygote heap madvise byte count
+ */
+ unsigned char *c = eventBuf;
+ *c++ = 2;
+ *c++ = EVENT_TYPE_INT;
+ memcpy(c, &total, sizeof(total));
+ c += sizeof(total);
+ *c++ = EVENT_TYPE_INT;
+ memcpy(c, &zyg, sizeof(zyg));
+ c += sizeof(zyg);
+
+ (void) android_btWriteLog(EVENT_LOG_TAG_dvm_gc_madvise_info,
+ EVENT_TYPE_LIST, eventBuf, sizeof(eventBuf));
+}
+
+#if 0
+#include <errno.h>
+#include <stdio.h>
+
+typedef struct HeapDumpContext {
+ FILE *fp;
+ void *chunkStart;
+ size_t chunkLen;
+ bool chunkFree;
+} HeapDumpContext;
+
+static void
+dump_context(const HeapDumpContext *ctx)
+{
+ fprintf(ctx->fp, "0x%08x %12.12zd %s\n", (uintptr_t)ctx->chunkStart,
+ ctx->chunkLen, ctx->chunkFree ? "FREE" : "USED");
+}
+
+static void
+heap_chunk_callback(const void *chunkptr, size_t chunklen,
+ const void *userptr, size_t userlen, void *arg)
+{
+ HeapDumpContext *ctx = (HeapDumpContext *)arg;
+ bool chunkFree = (userptr == NULL);
+
+ if (chunkFree != ctx->chunkFree ||
+ ((char *)ctx->chunkStart + ctx->chunkLen) != chunkptr)
+ {
+ /* The new chunk is of a different type or isn't
+ * contiguous with the current chunk. Dump the
+ * old one and start a new one.
+ */
+ if (ctx->chunkStart != NULL) {
+ /* It's not the first chunk. */
+ dump_context(ctx);
+ }
+ ctx->chunkStart = (void *)chunkptr;
+ ctx->chunkLen = chunklen;
+ ctx->chunkFree = chunkFree;
+ } else {
+ /* Extend the current chunk.
+ */
+ ctx->chunkLen += chunklen;
+ }
+}
+
+/* Dumps free and used ranges, as text, to the named file.
+ */
+void dvmDumpHeapToFile(const char *fileName)
+{
+ HeapDumpContext ctx;
+ FILE *fp;
+
+ fp = fopen(fileName, "w+");
+ if (fp == NULL) {
+ LOGE("Can't open %s for writing: %s\n", fileName, strerror(errno));
+ return;
+ }
+ LOGW("Dumping heap to %s...\n", fileName);
+
+ fprintf(fp, "==== Dalvik heap dump ====\n");
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.fp = fp;
+ dvmHeapSourceWalk(heap_chunk_callback, (void *)&ctx);
+ dump_context(&ctx);
+ fprintf(fp, "==== end heap dump ====\n");
+
+ LOGW("Dumped heap to %s.\n", fileName);
+
+ fclose(fp);
+}
+#endif
diff --git a/vm/alloc/HeapDebug.h b/vm/alloc/HeapDebug.h
new file mode 100644
index 0000000..19f4b45
--- /dev/null
+++ b/vm/alloc/HeapDebug.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _DALVIK_HEAPDEBUG
+#define _DALVIK_HEAPDEBUG
+
+typedef enum HeapDebugInfoType {
+ kVirtualHeapSize = 0,
+ kNativeHeapSize = 1,
+ kVirtualHeapAllocated = 2,
+ kNativeHeapAllocated = 3,
+} HeapDebugInfoType;
+
+/* Return the specified value.
+ * Returns -1 if the type is unknown.
+ */
+int dvmGetHeapDebugInfo(HeapDebugInfoType info);
+
+#endif // _DALVIK_HEAPDEBUG
diff --git a/vm/alloc/HeapInternal.h b/vm/alloc/HeapInternal.h
new file mode 100644
index 0000000..0298f84
--- /dev/null
+++ b/vm/alloc/HeapInternal.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Types and macros used internally by the heap.
+ */
+#ifndef _DALVIK_ALLOC_HEAP_INTERNAL
+#define _DALVIK_ALLOC_HEAP_INTERNAL
+
+#include <time.h> // for struct timespec
+
+#include "HeapTable.h"
+#include "MarkSweep.h"
+
+struct GcHeap {
+ HeapSource *heapSource;
+
+ /* List of heap objects that will require finalization when
+ * collected. I.e., instance objects
+ *
+ * a) whose class definitions override java.lang.Object.finalize()
+ *
+ * *** AND ***
+ *
+ * b) that have never been finalized.
+ *
+ * Note that this does not exclude non-garbage objects; this
+ * is not the list of pending finalizations, but of objects that
+ * potentially have finalization in their futures.
+ */
+ LargeHeapRefTable *finalizableRefs;
+
+ /* The list of objects that need to have finalize() called
+ * on themselves. These references are part of the root set.
+ *
+ * This table is protected by gDvm.heapWorkerListLock, which must
+ * be acquired after the heap lock.
+ */
+ LargeHeapRefTable *pendingFinalizationRefs;
+
+ /* Linked lists of subclass instances of java/lang/ref/Reference
+ * that we find while recursing. The "next" pointers are hidden
+ * in the objects' <code>int Reference.vmData</code> fields.
+ * These lists are cleared and rebuilt each time the GC runs.
+ */
+ Object *softReferences;
+ Object *weakReferences;
+ Object *phantomReferences;
+
+ /* The list of Reference objects that need to be cleared and/or
+ * enqueued. The bottom two bits of the object pointers indicate
+ * whether they should be cleared and/or enqueued.
+ *
+ * This table is protected by gDvm.heapWorkerListLock, which must
+ * be acquired after the heap lock.
+ */
+ LargeHeapRefTable *referenceOperations;
+
+ /* If non-null, the method that the HeapWorker is currently
+ * executing.
+ */
+ Object *heapWorkerCurrentObject;
+ Method *heapWorkerCurrentMethod;
+
+ /* If heapWorkerCurrentObject is non-null, this gives the time when
+ * HeapWorker started executing that method. The time value must come
+ * from dvmGetRelativeTimeUsec().
+ *
+ * The "Cpu" entry tracks the per-thread CPU timer (when available).
+ */
+ u8 heapWorkerInterpStartTime;
+ u8 heapWorkerInterpCpuStartTime;
+
+ /* If any fields are non-zero, indicates the next (absolute) time that
+ * the HeapWorker thread should call dvmHeapSourceTrim().
+ */
+ struct timespec heapWorkerNextTrim;
+
+ /* The current state of the mark step.
+ * Only valid during a GC.
+ */
+ GcMarkContext markContext;
+
+ /* GC's card table */
+ u1* cardTableBase;
+ size_t cardTableLength;
+
+ /* Is the GC running? Used to avoid recursive calls to GC.
+ */
+ bool gcRunning;
+
+ /*
+ * Debug control values
+ */
+
+ int ddmHpifWhen;
+ int ddmHpsgWhen;
+ int ddmHpsgWhat;
+ int ddmNhsgWhen;
+ int ddmNhsgWhat;
+
+#if WITH_HPROF
+ bool hprofDumpOnGc;
+ const char* hprofFileName;
+ int hprofFd;
+ hprof_context_t *hprofContext;
+ int hprofResult;
+ bool hprofDirectToDdms;
+#endif
+};
+
+bool dvmLockHeap(void);
+void dvmUnlockHeap(void);
+void dvmLogGcStats(size_t numFreed, size_t sizeFreed, size_t gcTimeMs);
+void dvmLogMadviseStats(size_t madvisedSizes[], size_t arrayLen);
+
+/*
+ * Logging helpers
+ */
+
+#define HEAP_LOG_TAG LOG_TAG "-heap"
+
+#if LOG_NDEBUG
+#define LOGV_HEAP(...) ((void)0)
+#define LOGD_HEAP(...) ((void)0)
+#else
+#define LOGV_HEAP(...) LOG(LOG_VERBOSE, HEAP_LOG_TAG, __VA_ARGS__)
+#define LOGD_HEAP(...) LOG(LOG_DEBUG, HEAP_LOG_TAG, __VA_ARGS__)
+#endif
+#define LOGI_HEAP(...) LOG(LOG_INFO, HEAP_LOG_TAG, __VA_ARGS__)
+#define LOGW_HEAP(...) LOG(LOG_WARN, HEAP_LOG_TAG, __VA_ARGS__)
+#define LOGE_HEAP(...) LOG(LOG_ERROR, HEAP_LOG_TAG, __VA_ARGS__)
+
+#define QUIET_ZYGOTE_GC 1
+#if QUIET_ZYGOTE_GC
+#undef LOGI_HEAP
+#define LOGI_HEAP(...) \
+ do { \
+ if (!gDvm.zygote) { \
+ LOG(LOG_INFO, HEAP_LOG_TAG, __VA_ARGS__); \
+ } \
+ } while (false)
+#endif
+
+#define FRACTIONAL_MB(n) (n) / (1024 * 1024), \
+ ((((n) % (1024 * 1024)) / 1024) * 1000) / 1024
+#define FRACTIONAL_PCT(n,max) ((n) * 100) / (max), \
+ (((n) * 1000) / (max)) % 10
+
+#endif // _DALVIK_ALLOC_HEAP_INTERNAL
diff --git a/vm/alloc/HeapSource.c b/vm/alloc/HeapSource.c
new file mode 100644
index 0000000..a18def5
--- /dev/null
+++ b/vm/alloc/HeapSource.c
@@ -0,0 +1,1891 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cutils/mspace.h>
+#include <stdint.h> // for SIZE_MAX
+#include <sys/mman.h>
+#include <errno.h>
+
+#include "Dalvik.h"
+#include "alloc/Heap.h"
+#include "alloc/HeapInternal.h"
+#include "alloc/HeapSource.h"
+#include "alloc/HeapBitmap.h"
+
+// TODO: find a real header file for these.
+extern int dlmalloc_trim(size_t);
+extern void dlmalloc_walk_free_pages(void(*)(void*, void*, void*), void*);
+
+static void snapIdealFootprint(void);
+static void setIdealFootprint(size_t max);
+
+#define ALIGN_UP_TO_PAGE_SIZE(p) \
+ (((size_t)(p) + (SYSTEM_PAGE_SIZE - 1)) & ~(SYSTEM_PAGE_SIZE - 1))
+#define ALIGN_DOWN_TO_PAGE_SIZE(p) \
+ ((size_t)(p) & ~(SYSTEM_PAGE_SIZE - 1))
+
+#define HEAP_UTILIZATION_MAX 1024
+#define DEFAULT_HEAP_UTILIZATION 512 // Range 1..HEAP_UTILIZATION_MAX
+#define HEAP_IDEAL_FREE (2 * 1024 * 1024)
+#define HEAP_MIN_FREE (HEAP_IDEAL_FREE / 4)
+
+/* Start a concurrent collection when free memory falls under this
+ * many bytes.
+ */
+#define CONCURRENT_START (128 << 10)
+
+/* The next GC will not be concurrent when free memory after a GC is
+ * under this many bytes.
+ */
+#define CONCURRENT_MIN_FREE (CONCURRENT_START + (128 << 10))
+
+#define HS_BOILERPLATE() \
+ do { \
+ assert(gDvm.gcHeap != NULL); \
+ assert(gDvm.gcHeap->heapSource != NULL); \
+ assert(gHs == gDvm.gcHeap->heapSource); \
+ } while (0)
+
+#define DEBUG_HEAP_SOURCE 0
+#if DEBUG_HEAP_SOURCE
+#define HSTRACE(...) LOG(LOG_INFO, LOG_TAG "-hs", __VA_ARGS__)
+#else
+#define HSTRACE(...) /**/
+#endif
+
+/*
+=======================================================
+=======================================================
+=======================================================
+
+How will this be used?
+allocating/freeing: Heap.c just wants to say "alloc(n)" and get a ptr
+ - if allocating in large doesn't work, try allocating from small
+Heap.c will use HeapSource.h; HeapSource.c will do the right thing
+ between small and large
+ - some operations should be abstracted; put in a structure
+
+How do we manage the size trade-offs?
+- keep mspace max footprint clamped to actual footprint
+- if small-alloc returns null, adjust large vs. small ratio
+ - give small all available slack and retry
+ - success or fail, snap back to actual footprint and give rest to large
+
+managed as "small actual" + "large actual" + "delta to allowed total footprint"
+- when allocating from one source or the other, give the delta to the
+ active source, but snap back afterwards
+- that may not work so great for a gc heap, because small will always consume.
+ - but we need to use the memory, and the current max is the amount we
+ need to fill before a GC.
+
+Find a way to permanently steal pages from the middle of the heap
+ - segment tricks?
+
+Allocate String and char[] in a separate heap?
+
+Maybe avoid growing small heap, even if there's slack? Look at
+live ratio of small heap after a gc; scale it based on that.
+
+=======================================================
+=======================================================
+=======================================================
+*/
+
+typedef struct {
+ /* The mspace to allocate from.
+ */
+ mspace msp;
+
+ /* The largest size that this heap is allowed to grow to.
+ */
+ size_t absoluteMaxSize;
+
+ /* Number of bytes allocated from this mspace for objects,
+ * including any overhead. This value is NOT exact, and
+ * should only be used as an input for certain heuristics.
+ */
+ size_t bytesAllocated;
+
+ /* Number of bytes allocated from this mspace at which a
+ * concurrent garbage collection will be started.
+ */
+ size_t concurrentStartBytes;
+
+ /* Number of objects currently allocated from this mspace.
+ */
+ size_t objectsAllocated;
+
+ /*
+ * The lowest address of this heap, inclusive.
+ */
+ char *base;
+
+ /*
+ * The highest address of this heap, exclusive.
+ */
+ char *limit;
+} Heap;
+
+struct HeapSource {
+ /* Target ideal heap utilization ratio; range 1..HEAP_UTILIZATION_MAX
+ */
+ size_t targetUtilization;
+
+ /* Requested minimum heap size, or zero if there is no minimum.
+ */
+ size_t minimumSize;
+
+ /* The starting heap size.
+ */
+ size_t startSize;
+
+ /* The largest that the heap source as a whole is allowed to grow.
+ */
+ size_t absoluteMaxSize;
+
+ /* The desired max size of the heap source as a whole.
+ */
+ size_t idealSize;
+
+ /* The maximum number of bytes allowed to be allocated from the
+ * active heap before a GC is forced. This is used to "shrink" the
+ * heap in lieu of actual compaction.
+ */
+ size_t softLimit;
+
+ /* The heaps; heaps[0] is always the active heap,
+ * which new objects should be allocated from.
+ */
+ Heap heaps[HEAP_SOURCE_MAX_HEAP_COUNT];
+
+ /* The current number of heaps.
+ */
+ size_t numHeaps;
+
+ /* External allocation count.
+ */
+ size_t externalBytesAllocated;
+
+ /* The maximum number of external bytes that may be allocated.
+ */
+ size_t externalLimit;
+
+ /* True if zygote mode was active when the HeapSource was created.
+ */
+ bool sawZygote;
+
+ /*
+ * The base address of the virtual memory reservation.
+ */
+ char *heapBase;
+
+ /*
+ * The length in bytes of the virtual memory reservation.
+ */
+ size_t heapLength;
+
+ /*
+ * The live object bitmap.
+ */
+ HeapBitmap liveBits;
+
+ /*
+ * The mark bitmap.
+ */
+ HeapBitmap markBits;
+
+ /*
+ * State for the GC daemon.
+ */
+ bool hasGcThread;
+ pthread_t gcThread;
+ bool gcThreadShutdown;
+ pthread_mutex_t gcThreadMutex;
+ pthread_cond_t gcThreadCond;
+};
+
+#define hs2heap(hs_) (&((hs_)->heaps[0]))
+
+/*
+ * Returns true iff a soft limit is in effect for the active heap.
+ */
+static inline bool
+softLimited(const HeapSource *hs)
+{
+ /* softLimit will be either SIZE_MAX or the limit for the
+ * active mspace. idealSize can be greater than softLimit
+ * if there is more than one heap. If there is only one
+ * heap, a non-SIZE_MAX softLimit should always be the same
+ * as idealSize.
+ */
+ return hs->softLimit <= hs->idealSize;
+}
+
+/*
+ * Returns approximately the maximum number of bytes allowed to be
+ * allocated from the active heap before a GC is forced.
+ */
+static size_t
+getAllocLimit(const HeapSource *hs)
+{
+ if (softLimited(hs)) {
+ return hs->softLimit;
+ } else {
+ return mspace_max_allowed_footprint(hs2heap(hs)->msp);
+ }
+}
+
+/*
+ * Returns the current footprint of all heaps. If includeActive
+ * is false, don't count the heap at index 0.
+ */
+static inline size_t
+oldHeapOverhead(const HeapSource *hs, bool includeActive)
+{
+ size_t footprint = 0;
+ size_t i;
+
+ if (includeActive) {
+ i = 0;
+ } else {
+ i = 1;
+ }
+ for (/* i = i */; i < hs->numHeaps; i++) {
+//TODO: include size of bitmaps? If so, don't use bitsLen, listen to .max
+ footprint += mspace_footprint(hs->heaps[i].msp);
+ }
+ return footprint;
+}
+
+/*
+ * Returns the heap that <ptr> could have come from, or NULL
+ * if it could not have come from any heap.
+ */
+static inline Heap *
+ptr2heap(const HeapSource *hs, const void *ptr)
+{
+ const size_t numHeaps = hs->numHeaps;
+ size_t i;
+
+//TODO: unroll this to HEAP_SOURCE_MAX_HEAP_COUNT
+ if (ptr != NULL) {
+ for (i = 0; i < numHeaps; i++) {
+ const Heap *const heap = &hs->heaps[i];
+
+ if ((const char *)ptr >= heap->base && (const char *)ptr < heap->limit) {
+ return (Heap *)heap;
+ }
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Functions to update heapSource->bytesAllocated when an object
+ * is allocated or freed. mspace_usable_size() will give
+ * us a much more accurate picture of heap utilization than
+ * the requested byte sizes would.
+ *
+ * These aren't exact, and should not be treated as such.
+ */
+static void countAllocation(Heap *heap, const void *ptr, bool isObj)
+{
+ HeapSource *hs;
+
+ assert(heap->bytesAllocated < mspace_footprint(heap->msp));
+
+ heap->bytesAllocated += mspace_usable_size(heap->msp, ptr) +
+ HEAP_SOURCE_CHUNK_OVERHEAD;
+ if (isObj) {
+ heap->objectsAllocated++;
+ hs = gDvm.gcHeap->heapSource;
+ dvmHeapBitmapSetObjectBit(&hs->liveBits, ptr);
+ }
+
+ assert(heap->bytesAllocated < mspace_footprint(heap->msp));
+}
+
+static void countFree(Heap *heap, const void *ptr, size_t *numBytes)
+{
+ HeapSource *hs;
+ size_t delta;
+
+ delta = mspace_usable_size(heap->msp, ptr) + HEAP_SOURCE_CHUNK_OVERHEAD;
+ assert(delta > 0);
+ if (delta < heap->bytesAllocated) {
+ heap->bytesAllocated -= delta;
+ } else {
+ heap->bytesAllocated = 0;
+ }
+ hs = gDvm.gcHeap->heapSource;
+ dvmHeapBitmapClearObjectBit(&hs->liveBits, ptr);
+ if (heap->objectsAllocated > 0) {
+ heap->objectsAllocated--;
+ }
+ *numBytes += delta;
+}
+
+static HeapSource *gHs = NULL;
+
+static mspace
+createMspace(void *base, size_t startSize, size_t absoluteMaxSize)
+{
+ mspace msp;
+
+ /* Create an unlocked dlmalloc mspace to use as
+ * a small-object heap source.
+ *
+ * We start off reserving heapSizeStart/2 bytes but
+ * letting the heap grow to heapSizeStart. This saves
+ * memory in the case where a process uses even less
+ * than the starting size.
+ */
+ LOGV_HEAP("Creating VM heap of size %u\n", startSize);
+ errno = 0;
+ msp = create_contiguous_mspace_with_base(startSize/2,
+ absoluteMaxSize, /*locked=*/false, base);
+ if (msp != NULL) {
+ /* Don't let the heap grow past the starting size without
+ * our intervention.
+ */
+ mspace_set_max_allowed_footprint(msp, startSize);
+ } else {
+ /* There's no guarantee that errno has meaning when the call
+ * fails, but it often does.
+ */
+ LOGE_HEAP("Can't create VM heap of size (%u,%u): %s\n",
+ startSize/2, absoluteMaxSize, strerror(errno));
+ }
+
+ return msp;
+}
+
+static bool
+addNewHeap(HeapSource *hs, mspace msp, size_t mspAbsoluteMaxSize)
+{
+ Heap heap;
+
+ if (hs->numHeaps >= HEAP_SOURCE_MAX_HEAP_COUNT) {
+ LOGE("Attempt to create too many heaps (%zd >= %zd)\n",
+ hs->numHeaps, HEAP_SOURCE_MAX_HEAP_COUNT);
+ dvmAbort();
+ return false;
+ }
+
+ memset(&heap, 0, sizeof(heap));
+
+ if (msp != NULL) {
+ heap.msp = msp;
+ heap.absoluteMaxSize = mspAbsoluteMaxSize;
+ heap.concurrentStartBytes = SIZE_MAX;
+ heap.base = hs->heapBase;
+ heap.limit = hs->heapBase + heap.absoluteMaxSize;
+ } else {
+ void *sbrk0 = contiguous_mspace_sbrk0(hs->heaps[0].msp);
+ char *base = (char *)ALIGN_UP_TO_PAGE_SIZE(sbrk0);
+ size_t overhead = base - hs->heaps[0].base;
+
+ assert(((size_t)hs->heaps[0].base & (SYSTEM_PAGE_SIZE - 1)) == 0);
+ if (overhead + HEAP_MIN_FREE >= hs->absoluteMaxSize) {
+ LOGE_HEAP("No room to create any more heaps "
+ "(%zd overhead, %zd max)\n",
+ overhead, hs->absoluteMaxSize);
+ return false;
+ }
+ hs->heaps[0].absoluteMaxSize = overhead;
+ hs->heaps[0].limit = base;
+ heap.absoluteMaxSize = hs->absoluteMaxSize - overhead;
+ heap.msp = createMspace(base, HEAP_MIN_FREE, heap.absoluteMaxSize);
+ heap.concurrentStartBytes = HEAP_MIN_FREE - CONCURRENT_START;
+ heap.base = base;
+ heap.limit = heap.base + heap.absoluteMaxSize;
+ if (heap.msp == NULL) {
+ return false;
+ }
+ }
+
+ /* Don't let the soon-to-be-old heap grow any further.
+ */
+ if (hs->numHeaps > 0) {
+ mspace msp = hs->heaps[0].msp;
+ mspace_set_max_allowed_footprint(msp, mspace_footprint(msp));
+ }
+
+ /* Put the new heap in the list, at heaps[0].
+ * Shift existing heaps down.
+ */
+ memmove(&hs->heaps[1], &hs->heaps[0], hs->numHeaps * sizeof(hs->heaps[0]));
+ hs->heaps[0] = heap;
+ hs->numHeaps++;
+
+ return true;
+}
+
+/*
+ * The garbage collection daemon. Initiates a concurrent collection
+ * when signaled.
+ */
+static void *gcDaemonThread(void* arg)
+{
+ dvmChangeStatus(NULL, THREAD_VMWAIT);
+ dvmLockMutex(&gHs->gcThreadMutex);
+ while (gHs->gcThreadShutdown != true) {
+ dvmWaitCond(&gHs->gcThreadCond, &gHs->gcThreadMutex);
+ dvmLockHeap();
+ dvmChangeStatus(NULL, THREAD_RUNNING);
+ dvmCollectGarbageInternal(false, GC_CONCURRENT);
+ dvmChangeStatus(NULL, THREAD_VMWAIT);
+ dvmUnlockHeap();
+ }
+ dvmChangeStatus(NULL, THREAD_RUNNING);
+ return NULL;
+}
+
+static bool gcDaemonStartup(void)
+{
+ dvmInitMutex(&gHs->gcThreadMutex);
+ pthread_cond_init(&gHs->gcThreadCond, NULL);
+ gHs->gcThreadShutdown = false;
+ gHs->hasGcThread = dvmCreateInternalThread(&gHs->gcThread, "GC",
+ gcDaemonThread, NULL);
+ return gHs->hasGcThread;
+}
+
+static void gcDaemonShutdown(void)
+{
+ if (gHs->hasGcThread) {
+ dvmLockMutex(&gHs->gcThreadMutex);
+ gHs->gcThreadShutdown = true;
+ dvmSignalCond(&gHs->gcThreadCond);
+ dvmUnlockMutex(&gHs->gcThreadMutex);
+ pthread_join(gHs->gcThread, NULL);
+ }
+}
+
+/*
+ * Initializes the heap source; must be called before any other
+ * dvmHeapSource*() functions. Returns a GcHeap structure
+ * allocated from the heap source.
+ */
+GcHeap *
+dvmHeapSourceStartup(size_t startSize, size_t absoluteMaxSize)
+{
+ GcHeap *gcHeap;
+ HeapSource *hs;
+ mspace msp;
+ size_t length;
+ void *base;
+
+ assert(gHs == NULL);
+
+ if (startSize > absoluteMaxSize) {
+ LOGE("Bad heap parameters (start=%d, max=%d)\n",
+ startSize, absoluteMaxSize);
+ return NULL;
+ }
+
+ /*
+ * Allocate a contiguous region of virtual memory to subdivided
+ * among the heaps managed by the garbage collector.
+ */
+ length = ALIGN_UP_TO_PAGE_SIZE(absoluteMaxSize);
+ base = dvmAllocRegion(length, PROT_NONE, "dalvik-heap");
+ if (base == NULL) {
+ return NULL;
+ }
+
+ /* Create an unlocked dlmalloc mspace to use as
+ * the small object heap source.
+ */
+ msp = createMspace(base, startSize, absoluteMaxSize);
+ if (msp == NULL) {
+ goto fail;
+ }
+
+ /* Allocate a descriptor from the heap we just created.
+ */
+ gcHeap = mspace_malloc(msp, sizeof(*gcHeap));
+ if (gcHeap == NULL) {
+ LOGE_HEAP("Can't allocate heap descriptor\n");
+ goto fail;
+ }
+ memset(gcHeap, 0, sizeof(*gcHeap));
+
+ hs = mspace_malloc(msp, sizeof(*hs));
+ if (hs == NULL) {
+ LOGE_HEAP("Can't allocate heap source\n");
+ goto fail;
+ }
+ memset(hs, 0, sizeof(*hs));
+
+ hs->targetUtilization = DEFAULT_HEAP_UTILIZATION;
+ hs->minimumSize = 0;
+ hs->startSize = startSize;
+ hs->absoluteMaxSize = absoluteMaxSize;
+ hs->idealSize = startSize;
+ hs->softLimit = SIZE_MAX; // no soft limit at first
+ hs->numHeaps = 0;
+ hs->sawZygote = gDvm.zygote;
+ hs->hasGcThread = false;
+ hs->heapBase = base;
+ hs->heapLength = length;
+ if (!addNewHeap(hs, msp, absoluteMaxSize)) {
+ LOGE_HEAP("Can't add initial heap\n");
+ goto fail;
+ }
+ if (!dvmHeapBitmapInit(&hs->liveBits, base, length, "dalvik-bitmap-1")) {
+ LOGE_HEAP("Can't create liveBits\n");
+ goto fail;
+ }
+ if (!dvmHeapBitmapInit(&hs->markBits, base, length, "dalvik-bitmap-2")) {
+ LOGE_HEAP("Can't create markBits\n");
+ dvmHeapBitmapDelete(&hs->liveBits);
+ goto fail;
+ }
+
+ gcHeap->markContext.bitmap = &hs->markBits;
+ gcHeap->heapSource = hs;
+
+ countAllocation(hs2heap(hs), gcHeap, false);
+ countAllocation(hs2heap(hs), hs, false);
+
+ gHs = hs;
+ return gcHeap;
+
+fail:
+ munmap(base, length);
+ return NULL;
+}
+
+bool dvmHeapSourceStartupAfterZygote(void)
+{
+ return gDvm.concurrentMarkSweep ? gcDaemonStartup() : true;
+}
+
+/*
+ * This is called while in zygote mode, right before we fork() for the
+ * first time. We create a heap for all future zygote process allocations,
+ * in an attempt to avoid touching pages in the zygote heap. (This would
+ * probably be unnecessary if we had a compacting GC -- the source of our
+ * troubles is small allocations filling in the gaps from larger ones.)
+ */
+bool
+dvmHeapSourceStartupBeforeFork()
+{
+ HeapSource *hs = gHs; // use a local to avoid the implicit "volatile"
+
+ HS_BOILERPLATE();
+
+ assert(gDvm.zygote);
+
+ if (!gDvm.newZygoteHeapAllocated) {
+ /* Create a new heap for post-fork zygote allocations. We only
+ * try once, even if it fails.
+ */
+ LOGV("Splitting out new zygote heap\n");
+ gDvm.newZygoteHeapAllocated = true;
+ return addNewHeap(hs, NULL, 0);
+ }
+ return true;
+}
+
+void dvmHeapSourceThreadShutdown(void)
+{
+ if (gDvm.gcHeap != NULL && gDvm.concurrentMarkSweep) {
+ gcDaemonShutdown();
+ }
+}
+
+/*
+ * Tears down the entire GcHeap structure and all of the substructures
+ * attached to it. This call has the side effect of setting the given
+ * gcHeap pointer and gHs to NULL.
+ */
+void
+dvmHeapSourceShutdown(GcHeap **gcHeap)
+{
+ if (*gcHeap != NULL && (*gcHeap)->heapSource != NULL) {
+ HeapSource *hs;
+
+ hs = (*gcHeap)->heapSource;
+
+ assert((char *)*gcHeap >= hs->heapBase);
+ assert((char *)*gcHeap < hs->heapBase + hs->heapLength);
+
+ dvmHeapBitmapDelete(&hs->liveBits);
+ dvmHeapBitmapDelete(&hs->markBits);
+
+ munmap(hs->heapBase, hs->heapLength);
+ gHs = NULL;
+ *gcHeap = NULL;
+ }
+}
+
+/*
+ * Gets the begining of the allocation for the HeapSource.
+ */
+void *dvmHeapSourceGetBase(void)
+{
+ return gHs->heapBase;
+}
+
+/*
+ * Returns the requested value. If the per-heap stats are requested, fill
+ * them as well.
+ *
+ * Caller must hold the heap lock.
+ */
+size_t
+dvmHeapSourceGetValue(enum HeapSourceValueSpec spec, size_t perHeapStats[],
+ size_t arrayLen)
+{
+ HeapSource *hs = gHs;
+ size_t value = 0;
+ size_t total = 0;
+ size_t i;
+
+ HS_BOILERPLATE();
+
+ switch (spec) {
+ case HS_EXTERNAL_BYTES_ALLOCATED:
+ return hs->externalBytesAllocated;
+ case HS_EXTERNAL_LIMIT:
+ return hs->externalLimit;
+ default:
+ // look at all heaps.
+ ;
+ }
+
+ assert(arrayLen >= hs->numHeaps || perHeapStats == NULL);
+ for (i = 0; i < hs->numHeaps; i++) {
+ Heap *const heap = &hs->heaps[i];
+
+ switch (spec) {
+ case HS_FOOTPRINT:
+ value = mspace_footprint(heap->msp);
+ break;
+ case HS_ALLOWED_FOOTPRINT:
+ value = mspace_max_allowed_footprint(heap->msp);
+ break;
+ case HS_BYTES_ALLOCATED:
+ value = heap->bytesAllocated;
+ break;
+ case HS_OBJECTS_ALLOCATED:
+ value = heap->objectsAllocated;
+ break;
+ default:
+ // quiet gcc
+ break;
+ }
+ if (perHeapStats) {
+ perHeapStats[i] = value;
+ }
+ total += value;
+ }
+ return total;
+}
+
+static void aliasBitmap(HeapBitmap *dst, HeapBitmap *src,
+ uintptr_t base, uintptr_t max) {
+ size_t offset;
+
+ dst->base = base;
+ dst->max = max;
+ dst->bitsLen = HB_OFFSET_TO_BYTE_INDEX(max - base) + sizeof(dst->bits);
+ /* The exclusive limit from bitsLen is greater than the inclusive max. */
+ assert(base + HB_MAX_OFFSET(dst) > max);
+ /* The exclusive limit is at most one word of bits beyond max. */
+ assert((base + HB_MAX_OFFSET(dst)) - max <=
+ HB_OBJECT_ALIGNMENT * HB_BITS_PER_WORD);
+ dst->allocLen = dst->bitsLen;
+ offset = base - src->base;
+ assert(HB_OFFSET_TO_MASK(offset) == 1 << 31);
+ dst->bits = &src->bits[HB_OFFSET_TO_INDEX(offset)];
+}
+
+/*
+ * Initializes a vector of object and mark bits to the object and mark
+ * bits of each heap. The bits are aliased to the heapsource
+ * object and mark bitmaps. This routine is used by the sweep code
+ * which needs to free each object in the correct heap.
+ */
+void dvmHeapSourceGetObjectBitmaps(HeapBitmap liveBits[], HeapBitmap markBits[],
+ size_t numHeaps)
+{
+ HeapSource *hs = gHs;
+ uintptr_t base, max;
+ size_t i;
+
+ HS_BOILERPLATE();
+
+ assert(numHeaps == hs->numHeaps);
+ for (i = 0; i < hs->numHeaps; ++i) {
+ base = (uintptr_t)hs->heaps[i].base;
+ /* -1 because limit is exclusive but max is inclusive. */
+ max = MIN((uintptr_t)hs->heaps[i].limit - 1, hs->markBits.max);
+ aliasBitmap(&liveBits[i], &hs->liveBits, base, max);
+ aliasBitmap(&markBits[i], &hs->markBits, base, max);
+ }
+}
+
+/*
+ * Get the bitmap representing all live objects.
+ */
+HeapBitmap *dvmHeapSourceGetLiveBits(void)
+{
+ HS_BOILERPLATE();
+
+ return &gHs->liveBits;
+}
+
+void dvmHeapSourceSwapBitmaps(void)
+{
+ HeapBitmap tmp;
+
+ tmp = gHs->liveBits;
+ gHs->liveBits = gHs->markBits;
+ gHs->markBits = tmp;
+}
+
+void dvmHeapSourceZeroMarkBitmap(void)
+{
+ HS_BOILERPLATE();
+
+ dvmHeapBitmapZero(&gHs->markBits);
+}
+
+void dvmMarkImmuneObjects(const char *immuneLimit)
+{
+ char *dst, *src;
+ size_t i, index, length;
+
+ /*
+ * Copy the contents of the live bit vector for immune object
+ * range into the mark bit vector.
+ */
+ /* The only values generated by dvmHeapSourceGetImmuneLimit() */
+ assert(immuneLimit == gHs->heaps[0].base ||
+ immuneLimit == NULL);
+ assert(gHs->liveBits.base == gHs->markBits.base);
+ assert(gHs->liveBits.bitsLen == gHs->markBits.bitsLen);
+ /* heap[0] is never immune */
+ assert(gHs->heaps[0].base >= immuneLimit);
+ assert(gHs->heaps[0].limit > immuneLimit);
+
+ for (i = 1; i < gHs->numHeaps; ++i) {
+ if (gHs->heaps[i].base < immuneLimit) {
+ assert(gHs->heaps[i].limit <= immuneLimit);
+ /* Compute the number of words to copy in the bitmap. */
+ index = HB_OFFSET_TO_INDEX(
+ (uintptr_t)gHs->heaps[i].base - gHs->liveBits.base);
+ /* Compute the starting offset in the live and mark bits. */
+ src = (char *)(gHs->liveBits.bits + index);
+ dst = (char *)(gHs->markBits.bits + index);
+ /* Compute the number of bytes of the live bitmap to copy. */
+ length = HB_OFFSET_TO_BYTE_INDEX(
+ gHs->heaps[i].limit - gHs->heaps[i].base);
+ /* Do the copy. */
+ memcpy(dst, src, length);
+ /* Make sure max points to the address of the highest set bit. */
+ if (gHs->markBits.max < (uintptr_t)gHs->heaps[i].limit) {
+ gHs->markBits.max = (uintptr_t)gHs->heaps[i].limit;
+ }
+ }
+ }
+}
+
+/*
+ * Allocates <n> bytes of zeroed data.
+ */
+void *
+dvmHeapSourceAlloc(size_t n)
+{
+ HeapSource *hs = gHs;
+ Heap *heap;
+ void *ptr;
+
+ HS_BOILERPLATE();
+ heap = hs2heap(hs);
+ if (heap->bytesAllocated + n > hs->softLimit) {
+ /*
+ * This allocation would push us over the soft limit; act as
+ * if the heap is full.
+ */
+ LOGV_HEAP("softLimit of %zd.%03zdMB hit for %zd-byte allocation\n",
+ FRACTIONAL_MB(hs->softLimit), n);
+ return NULL;
+ }
+ ptr = mspace_calloc(heap->msp, 1, n);
+ if (ptr == NULL) {
+ return NULL;
+ }
+ countAllocation(heap, ptr, true);
+ /*
+ * Check to see if a concurrent GC should be initiated.
+ */
+ if (gDvm.gcHeap->gcRunning || !hs->hasGcThread) {
+ /*
+ * The garbage collector thread is already running or has yet
+ * to be started. Do nothing.
+ */
+ return ptr;
+ }
+ if (heap->bytesAllocated > heap->concurrentStartBytes) {
+ /*
+ * We have exceeded the allocation threshold. Wake up the
+ * garbage collector.
+ */
+ dvmSignalCond(&gHs->gcThreadCond);
+ }
+ return ptr;
+}
+
+/* Remove any hard limits, try to allocate, and shrink back down.
+ * Last resort when trying to allocate an object.
+ */
+static void *
+heapAllocAndGrow(HeapSource *hs, Heap *heap, size_t n)
+{
+ void *ptr;
+ size_t max;
+
+ /* Grow as much as possible, but don't let the real footprint
+ * plus external allocations go over the absolute max.
+ */
+ max = heap->absoluteMaxSize;
+ if (max > hs->externalBytesAllocated) {
+ max -= hs->externalBytesAllocated;
+
+ mspace_set_max_allowed_footprint(heap->msp, max);
+ ptr = dvmHeapSourceAlloc(n);
+
+ /* Shrink back down as small as possible. Our caller may
+ * readjust max_allowed to a more appropriate value.
+ */
+ mspace_set_max_allowed_footprint(heap->msp,
+ mspace_footprint(heap->msp));
+ } else {
+ ptr = NULL;
+ }
+
+ return ptr;
+}
+
+/*
+ * Allocates <n> bytes of zeroed data, growing as much as possible
+ * if necessary.
+ */
+void *
+dvmHeapSourceAllocAndGrow(size_t n)
+{
+ HeapSource *hs = gHs;
+ Heap *heap;
+ void *ptr;
+ size_t oldIdealSize;
+
+ HS_BOILERPLATE();
+ heap = hs2heap(hs);
+
+ ptr = dvmHeapSourceAlloc(n);
+ if (ptr != NULL) {
+ return ptr;
+ }
+
+ oldIdealSize = hs->idealSize;
+ if (softLimited(hs)) {
+ /* We're soft-limited. Try removing the soft limit to
+ * see if we can allocate without actually growing.
+ */
+ hs->softLimit = SIZE_MAX;
+ ptr = dvmHeapSourceAlloc(n);
+ if (ptr != NULL) {
+ /* Removing the soft limit worked; fix things up to
+ * reflect the new effective ideal size.
+ */
+ snapIdealFootprint();
+ return ptr;
+ }
+ // softLimit intentionally left at SIZE_MAX.
+ }
+
+ /* We're not soft-limited. Grow the heap to satisfy the request.
+ * If this call fails, no footprints will have changed.
+ */
+ ptr = heapAllocAndGrow(hs, heap, n);
+ if (ptr != NULL) {
+ /* The allocation succeeded. Fix up the ideal size to
+ * reflect any footprint modifications that had to happen.
+ */
+ snapIdealFootprint();
+ } else {
+ /* We just couldn't do it. Restore the original ideal size,
+ * fixing up softLimit if necessary.
+ */
+ setIdealFootprint(oldIdealSize);
+ }
+ return ptr;
+}
+
+/*
+ * Frees the first numPtrs objects in the ptrs list and returns the
+ * amount of reclaimed storage. The list must contain addresses all in
+ * the same mspace, and must be in increasing order. This implies that
+ * there are no duplicates, and no entries are NULL.
+ */
+size_t dvmHeapSourceFreeList(size_t numPtrs, void **ptrs)
+{
+ Heap *heap;
+ size_t numBytes;
+
+ HS_BOILERPLATE();
+
+ if (numPtrs == 0) {
+ return 0;
+ }
+
+ assert(ptrs != NULL);
+ assert(*ptrs != NULL);
+ heap = ptr2heap(gHs, *ptrs);
+ numBytes = 0;
+ if (heap != NULL) {
+ mspace *msp = heap->msp;
+ // Calling mspace_free on shared heaps disrupts sharing too
+ // much. For heap[0] -- the 'active heap' -- we call
+ // mspace_free, but on the other heaps we only do some
+ // accounting.
+ if (heap == gHs->heaps) {
+ // mspace_merge_objects takes two allocated objects, and
+ // if the second immediately follows the first, will merge
+ // them, returning a larger object occupying the same
+ // memory. This is a local operation, and doesn't require
+ // dlmalloc to manipulate any freelists. It's pretty
+ // inexpensive compared to free().
+
+ // ptrs is an array of objects all in memory order, and if
+ // client code has been allocating lots of short-lived
+ // objects, this is likely to contain runs of objects all
+ // now garbage, and thus highly amenable to this optimization.
+
+ // Unroll the 0th iteration around the loop below,
+ // countFree ptrs[0] and initializing merged.
+ assert(ptrs[0] != NULL);
+ assert(ptr2heap(gHs, ptrs[0]) == heap);
+ countFree(heap, ptrs[0], &numBytes);
+ void *merged = ptrs[0];
+
+ size_t i;
+ for (i = 1; i < numPtrs; i++) {
+ assert(merged != NULL);
+ assert(ptrs[i] != NULL);
+ assert((intptr_t)merged < (intptr_t)ptrs[i]);
+ assert(ptr2heap(gHs, ptrs[i]) == heap);
+ countFree(heap, ptrs[i], &numBytes);
+ // Try to merge. If it works, merged now includes the
+ // memory of ptrs[i]. If it doesn't, free merged, and
+ // see if ptrs[i] starts a new run of adjacent
+ // objects to merge.
+ if (mspace_merge_objects(msp, merged, ptrs[i]) == NULL) {
+ mspace_free(msp, merged);
+ merged = ptrs[i];
+ }
+ }
+ assert(merged != NULL);
+ mspace_free(msp, merged);
+ } else {
+ // This is not an 'active heap'. Only do the accounting.
+ size_t i;
+ for (i = 0; i < numPtrs; i++) {
+ assert(ptrs[i] != NULL);
+ assert(ptr2heap(gHs, ptrs[i]) == heap);
+ countFree(heap, ptrs[i], &numBytes);
+ }
+ }
+ }
+ return numBytes;
+}
+
+/*
+ * Returns true iff <ptr> is in the heap source.
+ */
+bool
+dvmHeapSourceContainsAddress(const void *ptr)
+{
+ HS_BOILERPLATE();
+
+ return (dvmHeapBitmapCoversAddress(&gHs->liveBits, ptr));
+}
+
+/*
+ * Returns true iff <ptr> was allocated from the heap source.
+ */
+bool
+dvmHeapSourceContains(const void *ptr)
+{
+ HS_BOILERPLATE();
+
+ if (dvmHeapSourceContainsAddress(ptr)) {
+ return dvmHeapBitmapIsObjectBitSet(&gHs->liveBits, ptr) != 0;
+ }
+ return false;
+}
+
+/*
+ * Returns the value of the requested flag.
+ */
+bool
+dvmHeapSourceGetPtrFlag(const void *ptr, enum HeapSourcePtrFlag flag)
+{
+ if (ptr == NULL) {
+ return false;
+ }
+
+ if (flag == HS_CONTAINS) {
+ return dvmHeapSourceContains(ptr);
+ } else if (flag == HS_ALLOCATED_IN_ZYGOTE) {
+ HeapSource *hs = gHs;
+
+ HS_BOILERPLATE();
+
+ if (hs->sawZygote) {
+ Heap *heap;
+
+ heap = ptr2heap(hs, ptr);
+ if (heap != NULL) {
+ /* If the object is not in the active heap, we assume that
+ * it was allocated as part of zygote.
+ */
+ return heap != hs->heaps;
+ }
+ }
+ /* The pointer is outside of any known heap, or we are not
+ * running in zygote mode.
+ */
+ return false;
+ }
+
+ return false;
+}
+
+/*
+ * Returns the number of usable bytes in an allocated chunk; the size
+ * may be larger than the size passed to dvmHeapSourceAlloc().
+ */
+size_t
+dvmHeapSourceChunkSize(const void *ptr)
+{
+ Heap *heap;
+
+ HS_BOILERPLATE();
+
+ heap = ptr2heap(gHs, ptr);
+ if (heap != NULL) {
+ return mspace_usable_size(heap->msp, ptr);
+ }
+ return 0;
+}
+
+/*
+ * Returns the number of bytes that the heap source has allocated
+ * from the system using sbrk/mmap, etc.
+ *
+ * Caller must hold the heap lock.
+ */
+size_t
+dvmHeapSourceFootprint()
+{
+ HS_BOILERPLATE();
+
+//TODO: include size of bitmaps?
+ return oldHeapOverhead(gHs, true);
+}
+
+/*
+ * Return the real bytes used by old heaps and external memory
+ * plus the soft usage of the current heap. When a soft limit
+ * is in effect, this is effectively what it's compared against
+ * (though, in practice, it only looks at the current heap).
+ */
+static size_t
+getSoftFootprint(bool includeActive)
+{
+ HeapSource *hs = gHs;
+ size_t ret;
+
+ HS_BOILERPLATE();
+
+ ret = oldHeapOverhead(hs, false) + hs->externalBytesAllocated;
+ if (includeActive) {
+ ret += hs->heaps[0].bytesAllocated;
+ }
+
+ return ret;
+}
+
+/*
+ * Gets the maximum number of bytes that the heap source is allowed
+ * to allocate from the system.
+ */
+size_t
+dvmHeapSourceGetIdealFootprint()
+{
+ HeapSource *hs = gHs;
+
+ HS_BOILERPLATE();
+
+ return hs->idealSize;
+}
+
+/*
+ * Sets the soft limit, handling any necessary changes to the allowed
+ * footprint of the active heap.
+ */
+static void
+setSoftLimit(HeapSource *hs, size_t softLimit)
+{
+ /* Compare against the actual footprint, rather than the
+ * max_allowed, because the heap may not have grown all the
+ * way to the allowed size yet.
+ */
+ mspace msp = hs->heaps[0].msp;
+ size_t currentHeapSize = mspace_footprint(msp);
+ if (softLimit < currentHeapSize) {
+ /* Don't let the heap grow any more, and impose a soft limit.
+ */
+ mspace_set_max_allowed_footprint(msp, currentHeapSize);
+ hs->softLimit = softLimit;
+ } else {
+ /* Let the heap grow to the requested max, and remove any
+ * soft limit, if set.
+ */
+ mspace_set_max_allowed_footprint(msp, softLimit);
+ hs->softLimit = SIZE_MAX;
+ }
+}
+
+/*
+ * Sets the maximum number of bytes that the heap source is allowed
+ * to allocate from the system. Clamps to the appropriate maximum
+ * value.
+ */
+static void
+setIdealFootprint(size_t max)
+{
+ HeapSource *hs = gHs;
+#if DEBUG_HEAP_SOURCE
+ HeapSource oldHs = *hs;
+ mspace msp = hs->heaps[0].msp;
+ size_t oldAllowedFootprint =
+ mspace_max_allowed_footprint(msp);
+#endif
+
+ HS_BOILERPLATE();
+
+ if (max > hs->absoluteMaxSize) {
+ LOGI_HEAP("Clamp target GC heap from %zd.%03zdMB to %u.%03uMB\n",
+ FRACTIONAL_MB(max),
+ FRACTIONAL_MB(hs->absoluteMaxSize));
+ max = hs->absoluteMaxSize;
+ } else if (max < hs->minimumSize) {
+ max = hs->minimumSize;
+ }
+
+ /* Convert max into a size that applies to the active heap.
+ * Old heaps and external allocations will count against the ideal size.
+ */
+ size_t overhead = getSoftFootprint(false);
+ size_t activeMax;
+ if (overhead < max) {
+ activeMax = max - overhead;
+ } else {
+ activeMax = 0;
+ }
+
+ setSoftLimit(hs, activeMax);
+ hs->idealSize = max;
+
+ HSTRACE("IDEAL %zd->%zd (%d), soft %zd->%zd (%d), allowed %zd->%zd (%d), "
+ "ext %zd\n",
+ oldHs.idealSize, hs->idealSize, hs->idealSize - oldHs.idealSize,
+ oldHs.softLimit, hs->softLimit, hs->softLimit - oldHs.softLimit,
+ oldAllowedFootprint, mspace_max_allowed_footprint(msp),
+ mspace_max_allowed_footprint(msp) - oldAllowedFootprint,
+ hs->externalBytesAllocated);
+
+}
+
+/*
+ * Make the ideal footprint equal to the current footprint.
+ */
+static void
+snapIdealFootprint()
+{
+ HS_BOILERPLATE();
+
+ setIdealFootprint(getSoftFootprint(true));
+}
+
+/*
+ * Gets the current ideal heap utilization, represented as a number
+ * between zero and one.
+ */
+float dvmGetTargetHeapUtilization()
+{
+ HeapSource *hs = gHs;
+
+ HS_BOILERPLATE();
+
+ return (float)hs->targetUtilization / (float)HEAP_UTILIZATION_MAX;
+}
+
+/*
+ * Sets the new ideal heap utilization, represented as a number
+ * between zero and one.
+ */
+void dvmSetTargetHeapUtilization(float newTarget)
+{
+ HeapSource *hs = gHs;
+
+ HS_BOILERPLATE();
+
+ /* Clamp it to a reasonable range.
+ */
+ // TODO: This may need some tuning.
+ if (newTarget < 0.2) {
+ newTarget = 0.2;
+ } else if (newTarget > 0.8) {
+ newTarget = 0.8;
+ }
+
+ hs->targetUtilization =
+ (size_t)(newTarget * (float)HEAP_UTILIZATION_MAX);
+ LOGV("Set heap target utilization to %zd/%d (%f)\n",
+ hs->targetUtilization, HEAP_UTILIZATION_MAX, newTarget);
+}
+
+/*
+ * If set is true, sets the new minimum heap size to size; always
+ * returns the current (or previous) size. If size is negative,
+ * removes the current minimum constraint (if present).
+ */
+size_t
+dvmMinimumHeapSize(size_t size, bool set)
+{
+ HeapSource *hs = gHs;
+ size_t oldMinimumSize;
+
+ /* gHs caches an entry in gDvm.gcHeap; we need to hold the
+ * heap lock if we're going to look at it. We also need the
+ * lock for the call to setIdealFootprint().
+ */
+ dvmLockHeap();
+
+ HS_BOILERPLATE();
+
+ oldMinimumSize = hs->minimumSize;
+
+ if (set) {
+ /* Don't worry about external allocations right now.
+ * setIdealFootprint() will take them into account when
+ * minimumSize is used, and it's better to hold onto the
+ * intended minimumSize than to clamp it arbitrarily based
+ * on the current allocations.
+ */
+ if (size > hs->absoluteMaxSize) {
+ size = hs->absoluteMaxSize;
+ }
+ hs->minimumSize = size;
+ if (size > hs->idealSize) {
+ /* Force a snap to the minimum value, which we just set
+ * and which setIdealFootprint() will take into consideration.
+ */
+ setIdealFootprint(hs->idealSize);
+ }
+ /* Otherwise we'll just keep it in mind the next time
+ * setIdealFootprint() is called.
+ */
+ }
+
+ dvmUnlockHeap();
+
+ return oldMinimumSize;
+}
+
+/*
+ * Given the size of a live set, returns the ideal heap size given
+ * the current target utilization and MIN/MAX values.
+ *
+ * targetUtilization is in the range 1..HEAP_UTILIZATION_MAX.
+ */
+static size_t
+getUtilizationTarget(size_t liveSize, size_t targetUtilization)
+{
+ size_t targetSize;
+
+ /* Use the current target utilization ratio to determine the
+ * ideal heap size based on the size of the live set.
+ */
+ targetSize = (liveSize / targetUtilization) * HEAP_UTILIZATION_MAX;
+
+ /* Cap the amount of free space, though, so we don't end up
+ * with, e.g., 8MB of free space when the live set size hits 8MB.
+ */
+ if (targetSize > liveSize + HEAP_IDEAL_FREE) {
+ targetSize = liveSize + HEAP_IDEAL_FREE;
+ } else if (targetSize < liveSize + HEAP_MIN_FREE) {
+ targetSize = liveSize + HEAP_MIN_FREE;
+ }
+ return targetSize;
+}
+
+/*
+ * Given the current contents of the active heap, increase the allowed
+ * heap footprint to match the target utilization ratio. This
+ * should only be called immediately after a full mark/sweep.
+ */
+void dvmHeapSourceGrowForUtilization()
+{
+ HeapSource *hs = gHs;
+ Heap *heap;
+ size_t targetHeapSize;
+ size_t currentHeapUsed;
+ size_t oldIdealSize;
+ size_t newHeapMax;
+ size_t overhead;
+ size_t freeBytes;
+
+ HS_BOILERPLATE();
+ heap = hs2heap(hs);
+
+ /* Use the current target utilization ratio to determine the
+ * ideal heap size based on the size of the live set.
+ * Note that only the active heap plays any part in this.
+ *
+ * Avoid letting the old heaps influence the target free size,
+ * because they may be full of objects that aren't actually
+ * in the working set. Just look at the allocated size of
+ * the current heap.
+ */
+ currentHeapUsed = heap->bytesAllocated;
+#define LET_EXTERNAL_INFLUENCE_UTILIZATION 1
+#if LET_EXTERNAL_INFLUENCE_UTILIZATION
+ /* This is a hack to deal with the side-effects of moving
+ * bitmap data out of the Dalvik heap. Since the amount
+ * of free space after a GC scales with the size of the
+ * live set, many apps expected the large free space that
+ * appeared along with megabytes' worth of bitmaps. When
+ * the bitmaps were removed, the free size shrank significantly,
+ * and apps started GCing constantly. This makes it so the
+ * post-GC free space is the same size it would have been
+ * if the bitmaps were still in the Dalvik heap.
+ */
+ currentHeapUsed += hs->externalBytesAllocated;
+#endif
+ targetHeapSize =
+ getUtilizationTarget(currentHeapUsed, hs->targetUtilization);
+#if LET_EXTERNAL_INFLUENCE_UTILIZATION
+ currentHeapUsed -= hs->externalBytesAllocated;
+ targetHeapSize -= hs->externalBytesAllocated;
+#endif
+
+ /* The ideal size includes the old heaps; add overhead so that
+ * it can be immediately subtracted again in setIdealFootprint().
+ * If the target heap size would exceed the max, setIdealFootprint()
+ * will clamp it to a legal value.
+ */
+ overhead = getSoftFootprint(false);
+ oldIdealSize = hs->idealSize;
+ setIdealFootprint(targetHeapSize + overhead);
+
+ freeBytes = getAllocLimit(hs);
+ if (freeBytes < CONCURRENT_MIN_FREE) {
+ /* Not enough free memory to allow a concurrent GC. */
+ heap->concurrentStartBytes = SIZE_MAX;
+ } else {
+ heap->concurrentStartBytes = freeBytes - CONCURRENT_START;
+ }
+ newHeapMax = mspace_max_allowed_footprint(heap->msp);
+ if (softLimited(hs)) {
+ LOGD_HEAP("GC old usage %zd.%zd%%; now "
+ "%zd.%03zdMB used / %zd.%03zdMB soft max "
+ "(%zd.%03zdMB over, "
+ "%zd.%03zdMB ext, "
+ "%zd.%03zdMB real max)\n",
+ FRACTIONAL_PCT(currentHeapUsed, oldIdealSize),
+ FRACTIONAL_MB(currentHeapUsed),
+ FRACTIONAL_MB(hs->softLimit),
+ FRACTIONAL_MB(overhead),
+ FRACTIONAL_MB(hs->externalBytesAllocated),
+ FRACTIONAL_MB(newHeapMax));
+ } else {
+ LOGD_HEAP("GC old usage %zd.%zd%%; now "
+ "%zd.%03zdMB used / %zd.%03zdMB real max "
+ "(%zd.%03zdMB over, "
+ "%zd.%03zdMB ext)\n",
+ FRACTIONAL_PCT(currentHeapUsed, oldIdealSize),
+ FRACTIONAL_MB(currentHeapUsed),
+ FRACTIONAL_MB(newHeapMax),
+ FRACTIONAL_MB(overhead),
+ FRACTIONAL_MB(hs->externalBytesAllocated));
+ }
+}
+
+/*
+ * Return free pages to the system.
+ * TODO: move this somewhere else, especially the native heap part.
+ */
+
+static void releasePagesInRange(void *start, void *end, void *nbytes)
+{
+ /* Linux requires that the madvise() start address is page-aligned.
+ * We also align the end address.
+ */
+ start = (void *)ALIGN_UP_TO_PAGE_SIZE(start);
+ end = (void *)((size_t)end & ~(SYSTEM_PAGE_SIZE - 1));
+ if (start < end) {
+ size_t length = (char *)end - (char *)start;
+ madvise(start, length, MADV_DONTNEED);
+ *(size_t *)nbytes += length;
+ }
+}
+
+/*
+ * Return unused memory to the system if possible.
+ */
+void
+dvmHeapSourceTrim(size_t bytesTrimmed[], size_t arrayLen)
+{
+ HeapSource *hs = gHs;
+ size_t nativeBytes, heapBytes;
+ size_t i;
+
+ HS_BOILERPLATE();
+
+ assert(arrayLen >= hs->numHeaps);
+
+ heapBytes = 0;
+ for (i = 0; i < hs->numHeaps; i++) {
+ Heap *heap = &hs->heaps[i];
+
+ /* Return the wilderness chunk to the system.
+ */
+ mspace_trim(heap->msp, 0);
+
+ /* Return any whole free pages to the system.
+ */
+ bytesTrimmed[i] = 0;
+ mspace_walk_free_pages(heap->msp, releasePagesInRange,
+ &bytesTrimmed[i]);
+ heapBytes += bytesTrimmed[i];
+ }
+
+ /* Same for the native heap.
+ */
+ dlmalloc_trim(0);
+ nativeBytes = 0;
+ dlmalloc_walk_free_pages(releasePagesInRange, &nativeBytes);
+
+ LOGD_HEAP("madvised %zd (GC) + %zd (native) = %zd total bytes\n",
+ heapBytes, nativeBytes, heapBytes + nativeBytes);
+}
+
+/*
+ * Walks over the heap source and passes every allocated and
+ * free chunk to the callback.
+ */
+void
+dvmHeapSourceWalk(void(*callback)(const void *chunkptr, size_t chunklen,
+ const void *userptr, size_t userlen,
+ void *arg),
+ void *arg)
+{
+ HeapSource *hs = gHs;
+ size_t i;
+
+ HS_BOILERPLATE();
+
+ /* Walk the heaps from oldest to newest.
+ */
+//TODO: do this in address order
+ for (i = hs->numHeaps; i > 0; --i) {
+ mspace_walk_heap(hs->heaps[i-1].msp, callback, arg);
+ }
+}
+
+/*
+ * Gets the number of heaps available in the heap source.
+ *
+ * Caller must hold the heap lock, because gHs caches a field
+ * in gDvm.gcHeap.
+ */
+size_t
+dvmHeapSourceGetNumHeaps()
+{
+ HeapSource *hs = gHs;
+
+ HS_BOILERPLATE();
+
+ return hs->numHeaps;
+}
+
+
+/*
+ * External allocation tracking
+ *
+ * In some situations, memory outside of the heap is tied to the
+ * lifetime of objects in the heap. Since that memory is kept alive
+ * by heap objects, it should provide memory pressure that can influence
+ * GCs.
+ */
+
+/*
+ * Returns true if the requested number of bytes can be allocated from
+ * available storage.
+ */
+static bool externalBytesAvailable(const HeapSource *hs, size_t numBytes)
+{
+ const Heap *heap;
+ size_t currentHeapSize, newHeapSize;
+
+ /* Make sure that this allocation is even possible.
+ * Don't let the external size plus the actual heap size
+ * go over the absolute max. This essentially treats
+ * external allocations as part of the active heap.
+ *
+ * Note that this will fail "mysteriously" if there's
+ * a small softLimit but a large heap footprint.
+ */
+ heap = hs2heap(hs);
+ currentHeapSize = mspace_max_allowed_footprint(heap->msp);
+ newHeapSize = currentHeapSize + hs->externalBytesAllocated + numBytes;
+ if (newHeapSize <= heap->absoluteMaxSize) {
+ return true;
+ }
+ HSTRACE("externalBytesAvailable(): "
+ "footprint %zu + extAlloc %zu + n %zu >= max %zu (space for %zu)\n",
+ currentHeapSize, hs->externalBytesAllocated, numBytes,
+ heap->absoluteMaxSize,
+ heap->absoluteMaxSize -
+ (currentHeapSize + hs->externalBytesAllocated));
+ return false;
+}
+
+#define EXTERNAL_TARGET_UTILIZATION 820 // 80%
+
+/*
+ * Tries to update the internal count of externally-allocated memory.
+ * If there's enough room for that memory, returns true. If not, returns
+ * false and does not update the count.
+ *
+ * The caller must ensure externalBytesAvailable(hs, n) == true.
+ */
+static bool
+externalAlloc(HeapSource *hs, size_t n, bool grow)
+{
+ assert(hs->externalLimit >= hs->externalBytesAllocated);
+
+ HSTRACE("externalAlloc(%zd%s)\n", n, grow ? ", grow" : "");
+ assert(externalBytesAvailable(hs, n)); // The caller must ensure this.
+
+ /* External allocations have their own "free space" that they
+ * can allocate from without causing a GC.
+ */
+ if (hs->externalBytesAllocated + n <= hs->externalLimit) {
+ hs->externalBytesAllocated += n;
+#if PROFILE_EXTERNAL_ALLOCATIONS
+ if (gDvm.allocProf.enabled) {
+ Thread* self = dvmThreadSelf();
+ gDvm.allocProf.externalAllocCount++;
+ gDvm.allocProf.externalAllocSize += n;
+ if (self != NULL) {
+ self->allocProf.externalAllocCount++;
+ self->allocProf.externalAllocSize += n;
+ }
+ }
+#endif
+ return true;
+ }
+ if (!grow) {
+ return false;
+ }
+
+ /* GROW */
+ hs->externalBytesAllocated += n;
+ hs->externalLimit = getUtilizationTarget(
+ hs->externalBytesAllocated, EXTERNAL_TARGET_UTILIZATION);
+ HSTRACE("EXTERNAL grow limit to %zd\n", hs->externalLimit);
+ return true;
+}
+
+static void
+gcForExternalAlloc(bool collectSoftReferences)
+{
+ if (gDvm.allocProf.enabled) {
+ Thread* self = dvmThreadSelf();
+ gDvm.allocProf.gcCount++;
+ if (self != NULL) {
+ self->allocProf.gcCount++;
+ }
+ }
+ dvmCollectGarbageInternal(collectSoftReferences, GC_EXTERNAL_ALLOC);
+}
+
+/*
+ * Returns true if there is enough unused storage to perform an
+ * external allocation of the specified size. If there insufficient
+ * free storage we try to releasing memory from external allocations
+ * and trimming the heap.
+ */
+static bool externalAllocPossible(const HeapSource *hs, size_t n)
+{
+ size_t bytesTrimmed[HEAP_SOURCE_MAX_HEAP_COUNT];
+
+ /*
+ * If there is sufficient space return immediately.
+ */
+ if (externalBytesAvailable(hs, n)) {
+ return true;
+ }
+ /*
+ * There is insufficient space. Wait for the garbage collector to
+ * become inactive before proceeding.
+ */
+ while (gDvm.gcHeap->gcRunning) {
+ dvmWaitForConcurrentGcToComplete();
+ }
+ /*
+ * The heap may have grown or become trimmed while we were
+ * waiting.
+ */
+ if (externalBytesAvailable(hs, n)) {
+ return true;
+ }
+ /*
+ * Try a garbage collection that clears soft references. This may
+ * make trimming more effective.
+ */
+ gcForExternalAlloc(true);
+ if (externalBytesAvailable(hs, n)) {
+ return true;
+ }
+ /*
+ * Try trimming the mspace to reclaim unused pages.
+ */
+ dvmHeapSourceTrim(bytesTrimmed, NELEM(bytesTrimmed));
+ snapIdealFootprint();
+ if (externalBytesAvailable(hs, n)) {
+ return true;
+ }
+ /*
+ * Nothing worked, return an error.
+ */
+ return false;
+}
+
+/*
+ * Updates the internal count of externally-allocated memory. If there's
+ * enough room for that memory, returns true. If not, returns false and
+ * does not update the count.
+ *
+ * May cause a GC as a side-effect.
+ */
+bool
+dvmTrackExternalAllocation(size_t n)
+{
+ HeapSource *hs = gHs;
+ bool ret = false;
+
+ /* gHs caches an entry in gDvm.gcHeap; we need to hold the
+ * heap lock if we're going to look at it.
+ */
+ dvmLockHeap();
+
+ HS_BOILERPLATE();
+ assert(hs->externalLimit >= hs->externalBytesAllocated);
+
+ /*
+ * The externalAlloc calls require the externalAllocPossible
+ * invariant to be established.
+ */
+ if (!externalAllocPossible(hs, n)) {
+ LOGE_HEAP("%zd-byte external allocation "
+ "too large for this process.", n);
+ goto out;
+ }
+
+ /* Try "allocating" using the existing "free space".
+ */
+ HSTRACE("EXTERNAL alloc %zu (%zu < %zu)\n",
+ n, hs->externalBytesAllocated, hs->externalLimit);
+ if (externalAlloc(hs, n, false)) {
+ ret = true;
+ goto out;
+ }
+ /*
+ * Wait until garbage collector is quiescent before proceeding.
+ */
+ while (gDvm.gcHeap->gcRunning) {
+ dvmWaitForConcurrentGcToComplete();
+ }
+ /*
+ * Re-establish the invariant if it was lost while we were
+ * waiting.
+ */
+ if (!externalAllocPossible(hs, n)) {
+ LOGE_HEAP("%zd-byte external allocation "
+ "too large for this process.", n);
+ goto out;
+ }
+ /* The "allocation" failed. Free up some space by doing
+ * a full garbage collection. This may grow the heap source
+ * if the live set is sufficiently large.
+ */
+ HSTRACE("EXTERNAL alloc %zd: GC 1\n", n);
+ gcForExternalAlloc(false); // don't collect SoftReferences
+ if (externalAlloc(hs, n, false)) {
+ ret = true;
+ goto out;
+ }
+
+ /* Even that didn't work; this is an exceptional state.
+ * Try harder, growing the heap source if necessary.
+ */
+ HSTRACE("EXTERNAL alloc %zd: frag\n", n);
+ ret = externalAlloc(hs, n, true);
+ if (ret) {
+ goto out;
+ }
+
+ /* We couldn't even grow enough to satisfy the request.
+ * Try one last GC, collecting SoftReferences this time.
+ */
+ HSTRACE("EXTERNAL alloc %zd: GC 2\n", n);
+ gcForExternalAlloc(true); // collect SoftReferences
+ ret = externalAlloc(hs, n, true);
+ if (!ret) {
+ LOGE_HEAP("Out of external memory on a %zu-byte allocation.\n", n);
+ }
+
+#if PROFILE_EXTERNAL_ALLOCATIONS
+ if (gDvm.allocProf.enabled) {
+ Thread* self = dvmThreadSelf();
+ gDvm.allocProf.failedExternalAllocCount++;
+ gDvm.allocProf.failedExternalAllocSize += n;
+ if (self != NULL) {
+ self->allocProf.failedExternalAllocCount++;
+ self->allocProf.failedExternalAllocSize += n;
+ }
+ }
+#endif
+
+out:
+ dvmUnlockHeap();
+
+ return ret;
+}
+
+/*
+ * Reduces the internal count of externally-allocated memory.
+ */
+void
+dvmTrackExternalFree(size_t n)
+{
+ HeapSource *hs = gHs;
+ size_t newExternalLimit;
+ size_t oldExternalBytesAllocated;
+
+ HSTRACE("EXTERNAL free %zu (%zu < %zu)\n",
+ n, hs->externalBytesAllocated, hs->externalLimit);
+
+ /* gHs caches an entry in gDvm.gcHeap; we need to hold the
+ * heap lock if we're going to look at it.
+ */
+ dvmLockHeap();
+
+ HS_BOILERPLATE();
+ assert(hs->externalLimit >= hs->externalBytesAllocated);
+
+ oldExternalBytesAllocated = hs->externalBytesAllocated;
+ if (n <= hs->externalBytesAllocated) {
+ hs->externalBytesAllocated -= n;
+ } else {
+ n = hs->externalBytesAllocated;
+ hs->externalBytesAllocated = 0;
+ }
+
+#if PROFILE_EXTERNAL_ALLOCATIONS
+ if (gDvm.allocProf.enabled) {
+ Thread* self = dvmThreadSelf();
+ gDvm.allocProf.externalFreeCount++;
+ gDvm.allocProf.externalFreeSize += n;
+ if (self != NULL) {
+ self->allocProf.externalFreeCount++;
+ self->allocProf.externalFreeSize += n;
+ }
+ }
+#endif
+
+ /* Shrink as quickly as we can.
+ */
+ newExternalLimit = getUtilizationTarget(
+ hs->externalBytesAllocated, EXTERNAL_TARGET_UTILIZATION);
+ if (newExternalLimit < oldExternalBytesAllocated) {
+ /* Make sure that the remaining free space is at least
+ * big enough to allocate something of the size that was
+ * just freed. This makes it more likely that
+ * externalFree(N); externalAlloc(N);
+ * will work without causing a GC.
+ */
+ HSTRACE("EXTERNAL free preserved %zu extra free bytes\n",
+ oldExternalBytesAllocated - newExternalLimit);
+ newExternalLimit = oldExternalBytesAllocated;
+ }
+ if (newExternalLimit < hs->externalLimit) {
+ hs->externalLimit = newExternalLimit;
+ }
+
+ dvmUnlockHeap();
+}
+
+/*
+ * Returns the number of externally-allocated bytes being tracked by
+ * dvmTrackExternalAllocation/Free().
+ */
+size_t
+dvmGetExternalBytesAllocated()
+{
+ const HeapSource *hs = gHs;
+ size_t ret;
+
+ /* gHs caches an entry in gDvm.gcHeap; we need to hold the
+ * heap lock if we're going to look at it. We also need the
+ * lock for the call to setIdealFootprint().
+ */
+ dvmLockHeap();
+ HS_BOILERPLATE();
+ ret = hs->externalBytesAllocated;
+ dvmUnlockHeap();
+
+ return ret;
+}
+
+void *dvmHeapSourceGetImmuneLimit(GcMode mode)
+{
+ if (mode == GC_PARTIAL) {
+ return hs2heap(gHs)->base;
+ } else {
+ return NULL;
+ }
+}
diff --git a/vm/alloc/HeapSource.h b/vm/alloc/HeapSource.h
new file mode 100644
index 0000000..84b5794
--- /dev/null
+++ b/vm/alloc/HeapSource.h
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _DALVIK_HEAP_SOURCE
+#define _DALVIK_HEAP_SOURCE
+
+#include "alloc/Heap.h"
+#include "alloc/HeapInternal.h" // for GcHeap
+
+/* dlmalloc uses one size_t per allocated chunk.
+ */
+#define HEAP_SOURCE_CHUNK_OVERHEAD (1 * sizeof (size_t))
+#define HEAP_SOURCE_WORST_CHUNK_OVERHEAD (32 * sizeof (size_t))
+
+/* The largest number of separate heaps we can handle.
+ */
+#define HEAP_SOURCE_MAX_HEAP_COUNT 2
+
+/*
+ * Initializes the heap source; must be called before any other
+ * dvmHeapSource*() functions.
+ */
+GcHeap *dvmHeapSourceStartup(size_t startSize, size_t absoluteMaxSize);
+
+/*
+ * If the HeapSource was created while in zygote mode, this
+ * will create a new heap for post-zygote allocations.
+ * Having a separate heap should maximize the number of pages
+ * that a given app_process shares with the zygote process.
+ */
+bool dvmHeapSourceStartupAfterZygote(void);
+
+/*
+ * If the HeapSource was created while in zygote mode, this
+ * will create an additional zygote heap before the first fork().
+ * Having a separate heap should reduce the number of shared
+ * pages subsequently touched by the zygote process.
+ */
+bool dvmHeapSourceStartupBeforeFork(void);
+
+/*
+ * Shutdown any threads internal to the heap source. This should be
+ * called before the heap source itself is shutdown.
+ */
+void dvmHeapSourceThreadShutdown(void);
+
+/*
+ * Tears down the heap source and frees any resources associated with it.
+ */
+void dvmHeapSourceShutdown(GcHeap **gcHeap);
+
+/*
+ * Initializes a vector of object and mark bits to the object and mark
+ * bits of each heap.
+ */
+void dvmHeapSourceGetObjectBitmaps(HeapBitmap liveBits[], HeapBitmap markBits[],
+ size_t numHeaps);
+
+/*
+ * Get the bitmap representing all live objects.
+ */
+HeapBitmap *dvmHeapSourceGetLiveBits(void);
+
+/*
+ * Gets the begining of the allocation for the HeapSource.
+ */
+void *dvmHeapSourceGetBase(void);
+
+/*
+ * Returns the requested value. If the per-heap stats are requested, fill
+ * them as well.
+ */
+enum HeapSourceValueSpec {
+ HS_FOOTPRINT,
+ HS_ALLOWED_FOOTPRINT,
+ HS_BYTES_ALLOCATED,
+ HS_OBJECTS_ALLOCATED,
+ HS_EXTERNAL_BYTES_ALLOCATED,
+ HS_EXTERNAL_LIMIT
+};
+size_t dvmHeapSourceGetValue(enum HeapSourceValueSpec spec,
+ size_t perHeapStats[], size_t arrayLen);
+
+/*
+ * Allocates <n> bytes of zeroed data.
+ */
+void *dvmHeapSourceAlloc(size_t n);
+
+/*
+ * Allocates <n> bytes of zeroed data, growing up to absoluteMaxSize
+ * if necessary.
+ */
+void *dvmHeapSourceAllocAndGrow(size_t n);
+
+/*
+ * Frees the first numPtrs objects in the ptrs list and returns the
+ * amount of reclaimed storage. The list must contain addresses all
+ * in the same mspace, and must be in increasing order. This implies
+ * that there are no duplicates, and no entries are NULL.
+ */
+size_t dvmHeapSourceFreeList(size_t numPtrs, void **ptrs);
+
+/*
+ * Returns true iff <ptr> was allocated from the heap source.
+ */
+bool dvmHeapSourceContains(const void *ptr);
+
+/*
+ * Returns true iff <ptr> is within the address space managed by heap source.
+ */
+bool dvmHeapSourceContainsAddress(const void *ptr);
+
+/*
+ * Returns the value of the requested flag.
+ */
+enum HeapSourcePtrFlag {
+ HS_CONTAINS, // identical to dvmHeapSourceContains()
+ HS_ALLOCATED_IN_ZYGOTE
+};
+bool dvmHeapSourceGetPtrFlag(const void *ptr, enum HeapSourcePtrFlag flag);
+
+/*
+ * Returns the number of usable bytes in an allocated chunk; the size
+ * may be larger than the size passed to dvmHeapSourceAlloc().
+ */
+size_t dvmHeapSourceChunkSize(const void *ptr);
+
+/*
+ * Returns the number of bytes that the heap source has allocated
+ * from the system using sbrk/mmap, etc.
+ */
+size_t dvmHeapSourceFootprint(void);
+
+/*
+ * Gets the maximum number of bytes that the heap source is allowed
+ * to allocate from the system.
+ */
+size_t dvmHeapSourceGetIdealFootprint(void);
+
+/*
+ * Given the current contents of the heap, increase the allowed
+ * heap footprint to match the target utilization ratio. This
+ * should only be called immediately after a full mark/sweep.
+ */
+void dvmHeapSourceGrowForUtilization(void);
+
+/*
+ * Return unused memory to the system if possible. If <bytesTrimmed>
+ * is non-NULL, the number of bytes returned to the system is written to it.
+ */
+void dvmHeapSourceTrim(size_t bytesTrimmed[], size_t arrayLen);
+
+/*
+ * Walks over the heap source and passes every allocated and
+ * free chunk to the callback.
+ */
+void dvmHeapSourceWalk(void(*callback)(const void *chunkptr, size_t chunklen,
+ const void *userptr, size_t userlen,
+ void *arg),
+ void *arg);
+/*
+ * Gets the number of heaps available in the heap source.
+ */
+size_t dvmHeapSourceGetNumHeaps(void);
+
+/*
+ * Exchanges the mark and object bitmaps.
+ */
+void dvmHeapSourceSwapBitmaps(void);
+
+/*
+ * Zeroes the mark bitmap.
+ */
+void dvmHeapSourceZeroMarkBitmap(void);
+
+/*
+ * Marks all objects inside the immune region of the heap. Addresses
+ * at or above this pointer are threatened, addresses below this
+ * pointer are immune.
+ */
+void dvmMarkImmuneObjects(const char *immuneLimit);
+
+/*
+ * Returns a pointer that demarcates the threatened region of the
+ * heap. Addresses at or above this pointer are threatened, addresses
+ * below this pointer are immune.
+ */
+void *dvmHeapSourceGetImmuneLimit(GcMode mode);
+
+#endif // _DALVIK_HEAP_SOURCE
diff --git a/vm/alloc/HeapTable.c b/vm/alloc/HeapTable.c
new file mode 100644
index 0000000..e9f2729
--- /dev/null
+++ b/vm/alloc/HeapTable.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "alloc/HeapTable.h"
+#include "alloc/HeapInternal.h"
+
+#include <limits.h> // for INT_MAX
+
+static const int kLargeHeapRefTableNElems = 1024;
+static const int kFinalizableRefDefault = 128;
+
+void dvmHeapHeapTableFree(void *ptr)
+{
+ free(ptr);
+}
+
+#define heapRefTableIsFull(refs) \
+ dvmIsReferenceTableFull(refs)
+
+bool dvmHeapInitHeapRefTable(HeapRefTable *refs)
+{
+ memset(refs, 0, sizeof(*refs));
+ return dvmInitReferenceTable(refs, kFinalizableRefDefault, INT_MAX);
+}
+
+/* Frees the array inside the HeapRefTable, not the HeapRefTable itself.
+ */
+void dvmHeapFreeHeapRefTable(HeapRefTable *refs)
+{
+ dvmClearReferenceTable(refs);
+}
+
+/*
+ * Large, non-contiguous reference tables
+ */
+
+bool dvmHeapAddRefToLargeTable(LargeHeapRefTable **tableP, Object *ref)
+{
+ LargeHeapRefTable *table;
+
+ assert(tableP != NULL);
+ assert(ref != NULL);
+
+ /* Make sure that a table with a free slot is
+ * at the head of the list.
+ */
+ if (*tableP != NULL) {
+ table = *tableP;
+ LargeHeapRefTable *prevTable;
+
+ /* Find an empty slot for this reference.
+ */
+ prevTable = NULL;
+ while (table != NULL && heapRefTableIsFull(&table->refs)) {
+ prevTable = table;
+ table = table->next;
+ }
+ if (table != NULL) {
+ if (prevTable != NULL) {
+ /* Move the table to the head of the list.
+ */
+ prevTable->next = table->next;
+ table->next = *tableP;
+ *tableP = table;
+ }
+ /* else it's already at the head. */
+
+ goto insert;
+ }
+ /* else all tables are already full;
+ * fall through to the alloc case.
+ */
+ }
+
+ /* Allocate a new table.
+ */
+ table = calloc(1, sizeof(LargeHeapRefTable));
+ if (table == NULL) {
+ LOGE_HEAP("Can't allocate a new large ref table\n");
+ return false;
+ }
+ if (!dvmInitReferenceTable(&table->refs,
+ kLargeHeapRefTableNElems,
+ INT_MAX)) {
+ LOGE_HEAP("Can't initialize a new large ref table\n");
+ dvmHeapHeapTableFree(table);
+ return false;
+ }
+
+ /* Stick it at the head.
+ */
+ table->next = *tableP;
+ *tableP = table;
+
+insert:
+ /* Insert the reference.
+ */
+ assert(table == *tableP);
+ assert(table != NULL);
+ assert(!heapRefTableIsFull(&table->refs));
+ *table->refs.nextEntry++ = ref;
+
+ return true;
+}
+
+bool dvmHeapAddTableToLargeTable(LargeHeapRefTable **tableP, HeapRefTable *refs)
+{
+ LargeHeapRefTable *table;
+
+ /* Allocate a node.
+ */
+ table = calloc(1, sizeof(LargeHeapRefTable));
+ if (table == NULL) {
+ LOGE_HEAP("Can't allocate a new large ref table\n");
+ return false;
+ }
+ table->refs = *refs;
+
+ /* Insert the table into the list.
+ */
+ table->next = *tableP;
+ *tableP = table;
+
+ return true;
+}
+
+/* Frees everything associated with the LargeHeapRefTable.
+ */
+void dvmHeapFreeLargeTable(LargeHeapRefTable *table)
+{
+ while (table != NULL) {
+ LargeHeapRefTable *next = table->next;
+ dvmHeapFreeHeapRefTable(&table->refs);
+ dvmHeapHeapTableFree(table);
+ table = next;
+ }
+}
+
+Object *dvmHeapGetNextObjectFromLargeTable(LargeHeapRefTable **pTable)
+{
+ LargeHeapRefTable *table;
+ Object *obj;
+
+ assert(pTable != NULL);
+
+ obj = NULL;
+ table = *pTable;
+ if (table != NULL) {
+ HeapRefTable *refs = &table->refs;
+
+ /* We should never have an empty table node in the list.
+ */
+ assert(dvmReferenceTableEntries(refs) != 0);
+
+ /* Remove and return the last entry in the list.
+ */
+ obj = *--refs->nextEntry;
+
+ /* If this was the last entry in the table node,
+ * free it and patch up the list.
+ */
+ if (refs->nextEntry == refs->table) {
+ *pTable = table->next;
+ dvmClearReferenceTable(refs);
+ dvmHeapHeapTableFree(table);
+ }
+ }
+
+ return obj;
+}
+
+void dvmHeapMarkLargeTableRefs(LargeHeapRefTable *table)
+{
+ while (table != NULL) {
+ Object **ref, **lastRef;
+
+ ref = table->refs.table;
+ lastRef = table->refs.nextEntry;
+ while (ref < lastRef) {
+ dvmMarkObjectNonNull(*ref++);
+ }
+ table = table->next;
+ }
+}
diff --git a/vm/alloc/HeapTable.h b/vm/alloc/HeapTable.h
new file mode 100644
index 0000000..55851b9
--- /dev/null
+++ b/vm/alloc/HeapTable.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _DALVIK_ALLOC_HEAP_TABLE
+#define _DALVIK_ALLOC_HEAP_TABLE
+
+#include "ReferenceTable.h"
+
+typedef ReferenceTable HeapRefTable;
+typedef struct LargeHeapRefTable LargeHeapRefTable;
+typedef struct HeapSource HeapSource;
+
+struct LargeHeapRefTable {
+ LargeHeapRefTable *next;
+ HeapRefTable refs;
+};
+
+bool dvmHeapInitHeapRefTable(HeapRefTable *refs);
+void dvmHeapFreeHeapRefTable(HeapRefTable *refs);
+void dvmHeapFreeLargeTable(LargeHeapRefTable *table);
+void dvmHeapHeapTableFree(void *ptr);
+bool dvmHeapAddRefToLargeTable(LargeHeapRefTable **tableP, Object *ref);
+void dvmHeapMarkLargeTableRefs(LargeHeapRefTable *table);
+bool dvmHeapAddTableToLargeTable(LargeHeapRefTable **tableP,
+ HeapRefTable *refs);
+Object *dvmHeapGetNextObjectFromLargeTable(LargeHeapRefTable **pTable);
+
+#define dvmHeapAddToHeapRefTable(refs, ptr) \
+ dvmAddToReferenceTable((refs), (ptr))
+
+#define dvmHeapNumHeapRefTableEntries(refs) \
+ dvmReferenceTableEntries(refs)
+
+#define dvmHeapRemoveFromHeapRefTable(refs, ptr) \
+ dvmRemoveFromReferenceTable((refs), (refs)->table, (ptr))
+
+#endif // _DALVIK_ALLOC_HEAP_TABLE
diff --git a/vm/alloc/HeapWorker.c b/vm/alloc/HeapWorker.c
new file mode 100644
index 0000000..42b8942
--- /dev/null
+++ b/vm/alloc/HeapWorker.c
@@ -0,0 +1,542 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * An async worker thread to handle certain heap operations that need
+ * to be done in a separate thread to avoid synchronization problems.
+ * HeapWorkers and reference enqueuing are handled by this thread.
+ * The VM does all clearing.
+ */
+#include "Dalvik.h"
+#include "HeapInternal.h"
+
+#include <sys/time.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <signal.h>
+#include <errno.h> // for ETIMEDOUT, etc.
+
+static void* heapWorkerThreadStart(void* arg);
+
+/*
+ * Initialize any HeapWorker state that Heap.c
+ * cares about. This lets the GC start before the
+ * HeapWorker thread is initialized.
+ */
+void dvmInitializeHeapWorkerState()
+{
+ assert(!gDvm.heapWorkerInitialized);
+
+ dvmInitMutex(&gDvm.heapWorkerLock);
+ pthread_cond_init(&gDvm.heapWorkerCond, NULL);
+ pthread_cond_init(&gDvm.heapWorkerIdleCond, NULL);
+
+ gDvm.heapWorkerInitialized = true;
+}
+
+/*
+ * Crank up the heap worker thread.
+ *
+ * Does not return until the thread is ready for business.
+ */
+bool dvmHeapWorkerStartup(void)
+{
+ assert(!gDvm.haltHeapWorker);
+ assert(!gDvm.heapWorkerReady);
+ assert(gDvm.heapWorkerHandle == 0);
+ assert(gDvm.heapWorkerInitialized);
+
+ /* use heapWorkerLock/heapWorkerCond to communicate readiness */
+ dvmLockMutex(&gDvm.heapWorkerLock);
+
+//BUG: If a GC happens in here or in the new thread while we hold the lock,
+// the GC will deadlock when trying to acquire heapWorkerLock.
+ if (!dvmCreateInternalThread(&gDvm.heapWorkerHandle,
+ "HeapWorker", heapWorkerThreadStart, NULL))
+ {
+ dvmUnlockMutex(&gDvm.heapWorkerLock);
+ return false;
+ }
+
+ /*
+ * Wait for the heap worker to come up. We know the thread was created,
+ * so this should not get stuck.
+ */
+ while (!gDvm.heapWorkerReady) {
+ dvmWaitCond(&gDvm.heapWorkerCond, &gDvm.heapWorkerLock);
+ }
+
+ dvmUnlockMutex(&gDvm.heapWorkerLock);
+ return true;
+}
+
+/*
+ * Shut down the heap worker thread if it was started.
+ */
+void dvmHeapWorkerShutdown(void)
+{
+ void* threadReturn;
+
+ /* note: assuming that (pthread_t)0 is not a valid thread handle */
+ if (gDvm.heapWorkerHandle != 0) {
+ gDvm.haltHeapWorker = true;
+ dvmSignalHeapWorker(true);
+
+ /*
+ * We may not want to wait for the heapWorkers to complete. It's
+ * a good idea to do so, in case they're holding some sort of OS
+ * resource that doesn't get reclaimed when the process exits
+ * (e.g. an open temp file).
+ */
+ if (pthread_join(gDvm.heapWorkerHandle, &threadReturn) != 0)
+ LOGW("HeapWorker thread join failed\n");
+ else if (gDvm.verboseShutdown)
+ LOGD("HeapWorker thread has shut down\n");
+
+ gDvm.heapWorkerReady = false;
+ }
+}
+
+/* Make sure that the HeapWorker thread hasn't spent an inordinate
+ * amount of time inside a finalizer.
+ *
+ * Aborts the VM if the thread appears to be wedged.
+ *
+ * The caller must hold the heapWorkerLock to guarantee an atomic
+ * read of the watchdog values.
+ */
+void dvmAssertHeapWorkerThreadRunning()
+{
+ if (gDvm.gcHeap->heapWorkerCurrentObject != NULL) {
+ static const u8 HEAP_WORKER_WATCHDOG_TIMEOUT = 10*1000*1000LL; // 10sec
+
+ u8 heapWorkerInterpStartTime = gDvm.gcHeap->heapWorkerInterpStartTime;
+ u8 now = dvmGetRelativeTimeUsec();
+ u8 delta = now - heapWorkerInterpStartTime;
+
+ if (delta > HEAP_WORKER_WATCHDOG_TIMEOUT &&
+ (gDvm.debuggerActive || gDvm.nativeDebuggerActive))
+ {
+ /*
+ * Debugger suspension can block the thread indefinitely. For
+ * best results we should reset this explicitly whenever the
+ * HeapWorker thread is resumed. Unfortunately this is also
+ * affected by native debuggers, and we have no visibility
+ * into how they're manipulating us. So, we ignore the
+ * watchdog and just reset the timer.
+ */
+ LOGI("Debugger is attached -- suppressing HeapWorker watchdog\n");
+ gDvm.gcHeap->heapWorkerInterpStartTime = now; /* reset timer */
+ } else if (delta > HEAP_WORKER_WATCHDOG_TIMEOUT) {
+ /*
+ * Before we give up entirely, see if maybe we're just not
+ * getting any CPU time because we're stuck in a background
+ * process group. If we successfully move the thread into the
+ * foreground we'll just leave it there (it doesn't do anything
+ * if the process isn't GCing).
+ */
+ dvmLockThreadList(NULL);
+ Thread* thread = dvmGetThreadByHandle(gDvm.heapWorkerHandle);
+ dvmUnlockThreadList();
+
+ if (thread != NULL) {
+ int priChangeFlags, threadPrio;
+ SchedPolicy threadPolicy;
+ priChangeFlags = dvmRaiseThreadPriorityIfNeeded(thread,
+ &threadPrio, &threadPolicy);
+ if (priChangeFlags != 0) {
+ LOGI("HeapWorker watchdog expired, raising priority"
+ " and retrying\n");
+ gDvm.gcHeap->heapWorkerInterpStartTime = now;
+ return;
+ }
+ }
+
+ char* desc = dexProtoCopyMethodDescriptor(
+ &gDvm.gcHeap->heapWorkerCurrentMethod->prototype);
+ LOGE("HeapWorker is wedged: %lldms spent inside %s.%s%s\n",
+ delta / 1000,
+ gDvm.gcHeap->heapWorkerCurrentObject->clazz->descriptor,
+ gDvm.gcHeap->heapWorkerCurrentMethod->name, desc);
+ free(desc);
+ dvmDumpAllThreads(true);
+
+ /* try to get a debuggerd dump from the target thread */
+ dvmNukeThread(thread);
+
+ /* abort the VM */
+ dvmAbort();
+ } else if (delta > HEAP_WORKER_WATCHDOG_TIMEOUT / 2) {
+ char* desc = dexProtoCopyMethodDescriptor(
+ &gDvm.gcHeap->heapWorkerCurrentMethod->prototype);
+ LOGW("HeapWorker may be wedged: %lldms spent inside %s.%s%s\n",
+ delta / 1000,
+ gDvm.gcHeap->heapWorkerCurrentObject->clazz->descriptor,
+ gDvm.gcHeap->heapWorkerCurrentMethod->name, desc);
+ free(desc);
+ }
+ }
+}
+
+/*
+ * Acquires a mutex, transitioning to the VMWAIT state if the mutex is
+ * held. This allows the thread to suspend while it waits for another
+ * thread to release the mutex.
+ */
+static void lockMutex(pthread_mutex_t *mu)
+{
+ Thread *self;
+ ThreadStatus oldStatus;
+
+ assert(mu != NULL);
+ if (dvmTryLockMutex(mu) != 0) {
+ self = dvmThreadSelf();
+ assert(self != NULL);
+ oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
+ dvmLockMutex(mu);
+ dvmChangeStatus(self, oldStatus);
+ }
+}
+
+static void callMethod(Thread *self, Object *obj, Method *method)
+{
+ JValue unused;
+
+ /* Keep track of the method we're about to call and
+ * the current time so that other threads can detect
+ * when this thread wedges and provide useful information.
+ */
+ gDvm.gcHeap->heapWorkerInterpStartTime = dvmGetRelativeTimeUsec();
+ gDvm.gcHeap->heapWorkerInterpCpuStartTime = dvmGetThreadCpuTimeUsec();
+ gDvm.gcHeap->heapWorkerCurrentMethod = method;
+ gDvm.gcHeap->heapWorkerCurrentObject = obj;
+
+ /* Call the method.
+ *
+ * Don't hold the lock when executing interpreted
+ * code. It may suspend, and the GC needs to grab
+ * heapWorkerLock.
+ */
+ dvmUnlockMutex(&gDvm.heapWorkerLock);
+ if (false) {
+ /* Log entry/exit; this will likely flood the log enough to
+ * cause "logcat" to drop entries.
+ */
+ char tmpTag[16];
+ sprintf(tmpTag, "HW%d", self->systemTid);
+ LOG(LOG_DEBUG, tmpTag, "Call %s\n", method->clazz->descriptor);
+ dvmCallMethod(self, method, obj, &unused);
+ LOG(LOG_DEBUG, tmpTag, " done\n");
+ } else {
+ dvmCallMethod(self, method, obj, &unused);
+ }
+ /*
+ * Reacquire the heap worker lock in a suspend-friendly way.
+ */
+ lockMutex(&gDvm.heapWorkerLock);
+
+ gDvm.gcHeap->heapWorkerCurrentObject = NULL;
+ gDvm.gcHeap->heapWorkerCurrentMethod = NULL;
+ gDvm.gcHeap->heapWorkerInterpStartTime = 0LL;
+
+ /* Exceptions thrown during these calls interrupt
+ * the method, but are otherwise ignored.
+ */
+ if (dvmCheckException(self)) {
+#if DVM_SHOW_EXCEPTION >= 1
+ LOGI("Uncaught exception thrown by finalizer (will be discarded):\n");
+ dvmLogExceptionStackTrace();
+#endif
+ dvmClearException(self);
+ }
+}
+
+/* Process all enqueued heap work, including finalizers and reference
+ * enqueueing. Clearing has already been done by the VM.
+ *
+ * Caller must hold gDvm.heapWorkerLock.
+ */
+static void doHeapWork(Thread *self)
+{
+ Object *obj;
+ HeapWorkerOperation op;
+ int numFinalizersCalled, numReferencesEnqueued;
+
+ assert(gDvm.voffJavaLangObject_finalize >= 0);
+ assert(gDvm.methJavaLangRefReference_enqueueInternal != NULL);
+
+ numFinalizersCalled = 0;
+ numReferencesEnqueued = 0;
+ while ((obj = dvmGetNextHeapWorkerObject(&op)) != NULL) {
+ Method *method = NULL;
+
+ /* Make sure the object hasn't been collected since
+ * being scheduled.
+ */
+ assert(dvmIsValidObject(obj));
+
+ /* Call the appropriate method(s).
+ */
+ if (op == WORKER_FINALIZE) {
+ numFinalizersCalled++;
+ method = obj->clazz->vtable[gDvm.voffJavaLangObject_finalize];
+ assert(dvmCompareNameDescriptorAndMethod("finalize", "()V",
+ method) == 0);
+ assert(method->clazz != gDvm.classJavaLangObject);
+ callMethod(self, obj, method);
+ } else {
+ assert(op == WORKER_ENQUEUE);
+ assert(dvmGetFieldObject(
+ obj, gDvm.offJavaLangRefReference_queue) != NULL);
+ assert(dvmGetFieldObject(
+ obj, gDvm.offJavaLangRefReference_queueNext) == NULL);
+ numReferencesEnqueued++;
+ callMethod(self, obj,
+ gDvm.methJavaLangRefReference_enqueueInternal);
+ }
+
+ /* Let the GC collect the object.
+ */
+ dvmReleaseTrackedAlloc(obj, self);
+ }
+ LOGV("Called %d finalizers\n", numFinalizersCalled);
+ LOGV("Enqueued %d references\n", numReferencesEnqueued);
+}
+
+/*
+ * The heap worker thread sits quietly until the GC tells it there's work
+ * to do.
+ */
+static void* heapWorkerThreadStart(void* arg)
+{
+ Thread *self = dvmThreadSelf();
+
+ UNUSED_PARAMETER(arg);
+
+ LOGV("HeapWorker thread started (threadid=%d)\n", self->threadId);
+
+ /* tell the main thread that we're ready */
+ lockMutex(&gDvm.heapWorkerLock);
+ gDvm.heapWorkerReady = true;
+ dvmSignalCond(&gDvm.heapWorkerCond);
+ dvmUnlockMutex(&gDvm.heapWorkerLock);
+
+ lockMutex(&gDvm.heapWorkerLock);
+ while (!gDvm.haltHeapWorker) {
+ struct timespec trimtime;
+ bool timedwait = false;
+
+ /* We're done running interpreted code for now. */
+ dvmChangeStatus(NULL, THREAD_VMWAIT);
+
+ /* Signal anyone who wants to know when we're done. */
+ dvmBroadcastCond(&gDvm.heapWorkerIdleCond);
+
+ /* Trim the heap if we were asked to. */
+ trimtime = gDvm.gcHeap->heapWorkerNextTrim;
+ if (trimtime.tv_sec != 0 && trimtime.tv_nsec != 0) {
+ struct timespec now;
+
+#ifdef HAVE_TIMEDWAIT_MONOTONIC
+ clock_gettime(CLOCK_MONOTONIC, &now); // relative time
+#else
+ struct timeval tvnow;
+ gettimeofday(&tvnow, NULL); // absolute time
+ now.tv_sec = tvnow.tv_sec;
+ now.tv_nsec = tvnow.tv_usec * 1000;
+#endif
+
+ if (trimtime.tv_sec < now.tv_sec ||
+ (trimtime.tv_sec == now.tv_sec &&
+ trimtime.tv_nsec <= now.tv_nsec))
+ {
+ size_t madvisedSizes[HEAP_SOURCE_MAX_HEAP_COUNT];
+
+ /*
+ * Acquire the gcHeapLock. The requires releasing the
+ * heapWorkerLock before the gcHeapLock is acquired.
+ * It is possible that the gcHeapLock may be acquired
+ * during a concurrent GC in which case heapWorkerLock
+ * is held by the GC and we are unable to make forward
+ * progress. We avoid deadlock by releasing the
+ * gcHeapLock and then waiting to be signaled when the
+ * GC completes. There is no guarantee that the next
+ * time we are run will coincide with GC inactivity so
+ * the check and wait must be performed within a loop.
+ */
+ dvmUnlockMutex(&gDvm.heapWorkerLock);
+ dvmLockHeap();
+ while (gDvm.gcHeap->gcRunning) {
+ dvmWaitForConcurrentGcToComplete();
+ }
+ dvmLockMutex(&gDvm.heapWorkerLock);
+
+ memset(madvisedSizes, 0, sizeof(madvisedSizes));
+ dvmHeapSourceTrim(madvisedSizes, HEAP_SOURCE_MAX_HEAP_COUNT);
+ dvmLogMadviseStats(madvisedSizes, HEAP_SOURCE_MAX_HEAP_COUNT);
+
+ dvmUnlockHeap();
+
+ trimtime.tv_sec = 0;
+ trimtime.tv_nsec = 0;
+ gDvm.gcHeap->heapWorkerNextTrim = trimtime;
+ } else {
+ timedwait = true;
+ }
+ }
+
+ /* sleep until signaled */
+ if (timedwait) {
+ int cc __attribute__ ((__unused__));
+#ifdef HAVE_TIMEDWAIT_MONOTONIC
+ cc = pthread_cond_timedwait_monotonic(&gDvm.heapWorkerCond,
+ &gDvm.heapWorkerLock, &trimtime);
+#else
+ cc = pthread_cond_timedwait(&gDvm.heapWorkerCond,
+ &gDvm.heapWorkerLock, &trimtime);
+#endif
+ assert(cc == 0 || cc == ETIMEDOUT);
+ } else {
+ dvmWaitCond(&gDvm.heapWorkerCond, &gDvm.heapWorkerLock);
+ }
+
+ /*
+ * Return to the running state before doing heap work. This
+ * will block if the GC has initiated a suspend. We release
+ * the heapWorkerLock beforehand for the GC to make progress
+ * and wait to be signaled after the GC completes. There is
+ * no guarantee that the next time we are run will coincide
+ * with GC inactivity so the check and wait must be performed
+ * within a loop.
+ */
+ dvmUnlockMutex(&gDvm.heapWorkerLock);
+ dvmChangeStatus(NULL, THREAD_RUNNING);
+ dvmLockHeap();
+ while (gDvm.gcHeap->gcRunning) {
+ dvmWaitForConcurrentGcToComplete();
+ }
+ dvmLockMutex(&gDvm.heapWorkerLock);
+ dvmUnlockHeap();
+ LOGV("HeapWorker is awake\n");
+
+ /* Process any events in the queue.
+ */
+ doHeapWork(self);
+ }
+ dvmUnlockMutex(&gDvm.heapWorkerLock);
+
+ if (gDvm.verboseShutdown)
+ LOGD("HeapWorker thread shutting down\n");
+ return NULL;
+}
+
+/*
+ * Wake up the heap worker to let it know that there's work to be done.
+ */
+void dvmSignalHeapWorker(bool shouldLock)
+{
+ if (shouldLock) {
+ dvmLockMutex(&gDvm.heapWorkerLock);
+ }
+
+ dvmSignalCond(&gDvm.heapWorkerCond);
+
+ if (shouldLock) {
+ dvmUnlockMutex(&gDvm.heapWorkerLock);
+ }
+}
+
+/*
+ * Block until all pending heap worker work has finished.
+ */
+void dvmWaitForHeapWorkerIdle()
+{
+ assert(gDvm.heapWorkerReady);
+
+ dvmChangeStatus(NULL, THREAD_VMWAIT);
+
+ dvmLockMutex(&gDvm.heapWorkerLock);
+
+ /* Wake up the heap worker and wait for it to finish. */
+ //TODO(http://b/issue?id=699704): This will deadlock if
+ // called from finalize(), enqueue(), or clear(). We
+ // need to detect when this is called from the HeapWorker
+ // context and just give up.
+ dvmSignalHeapWorker(false);
+ dvmWaitCond(&gDvm.heapWorkerIdleCond, &gDvm.heapWorkerLock);
+
+ dvmUnlockMutex(&gDvm.heapWorkerLock);
+
+ dvmChangeStatus(NULL, THREAD_RUNNING);
+}
+
+/*
+ * Do not return until any pending heap work has finished. This may
+ * or may not happen in the context of the calling thread.
+ * No exceptions will escape.
+ */
+void dvmRunFinalizationSync()
+{
+ if (gDvm.zygote) {
+ assert(!gDvm.heapWorkerReady);
+
+ /* When in zygote mode, there is no heap worker.
+ * Do the work in the current thread.
+ */
+ dvmLockMutex(&gDvm.heapWorkerLock);
+ doHeapWork(dvmThreadSelf());
+ dvmUnlockMutex(&gDvm.heapWorkerLock);
+ } else {
+ /* Outside of zygote mode, we can just ask the
+ * heap worker thread to do the work.
+ */
+ dvmWaitForHeapWorkerIdle();
+ }
+}
+
+/*
+ * Requests that dvmHeapSourceTrim() be called no sooner
+ * than timeoutSec seconds from now. If timeoutSec
+ * is zero, any pending trim is cancelled.
+ *
+ * Caller must hold heapWorkerLock.
+ */
+void dvmScheduleHeapSourceTrim(size_t timeoutSec)
+{
+ GcHeap *gcHeap = gDvm.gcHeap;
+ struct timespec timeout;
+
+ if (timeoutSec == 0) {
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 0;
+ /* Don't wake up the thread just to tell it to cancel.
+ * If it wakes up naturally, we can avoid the extra
+ * context switch.
+ */
+ } else {
+#ifdef HAVE_TIMEDWAIT_MONOTONIC
+ clock_gettime(CLOCK_MONOTONIC, &timeout);
+ timeout.tv_sec += timeoutSec;
+#else
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ timeout.tv_sec = now.tv_sec + timeoutSec;
+ timeout.tv_nsec = now.tv_usec * 1000;
+#endif
+ dvmSignalHeapWorker(false);
+ }
+ gcHeap->heapWorkerNextTrim = timeout;
+}
diff --git a/vm/alloc/HeapWorker.h b/vm/alloc/HeapWorker.h
new file mode 100644
index 0000000..45587ff
--- /dev/null
+++ b/vm/alloc/HeapWorker.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Manage async heap tasks.
+ */
+#ifndef _DALVIK_ALLOC_HEAP_WORKER
+#define _DALVIK_ALLOC_HEAP_WORKER
+
+/*
+ * Initialize any HeapWorker state that Heap.c
+ * cares about. This lets the GC start before the
+ * HeapWorker thread is initialized.
+ */
+void dvmInitializeHeapWorkerState(void);
+
+/*
+ * Initialization. Starts/stops the worker thread.
+ */
+bool dvmHeapWorkerStartup(void);
+void dvmHeapWorkerShutdown(void);
+
+/*
+ * Tell the worker thread to wake up and do work.
+ * If shouldLock is false, the caller must have already
+ * acquired gDvm.heapWorkerLock.
+ */
+void dvmSignalHeapWorker(bool shouldLock);
+
+/*
+ * Block until all pending heap worker work has finished.
+ */
+void dvmWaitForHeapWorkerIdle(void);
+
+/*
+ * Does not return until any pending finalizers have been called.
+ * This may or may not happen in the context of the calling thread.
+ * No exceptions will escape.
+ *
+ * Used by zygote, which doesn't have a HeapWorker thread.
+ */
+void dvmRunFinalizationSync(void);
+
+/*
+ * Requests that dvmHeapSourceTrim() be called no sooner
+ * than timeoutSec seconds from now. If timeoutSec
+ * is zero, any pending trim is cancelled.
+ *
+ * Caller must hold heapWorkerLock.
+ */
+void dvmScheduleHeapSourceTrim(size_t timeoutSec);
+
+/* Make sure that the HeapWorker thread hasn't spent an inordinate
+ * amount of time inside interpreted code.
+ *
+ * Aborts the VM if the thread appears to be wedged.
+ *
+ * The caller must hold the heapWorkerLock.
+ */
+void dvmAssertHeapWorkerThreadRunning();
+
+/*
+ * The type of operation for HeapWorker to perform on an object.
+ */
+typedef enum HeapWorkerOperation {
+ WORKER_FINALIZE = 0,
+ WORKER_ENQUEUE = 1,
+} HeapWorkerOperation;
+
+/*
+ * Called by the worker thread to get the next object
+ * to finalize/enqueue/clear. Implemented in Heap.c.
+ *
+ * @param op The operation to perform on the returned object.
+ * Must be non-NULL.
+ * @return The object to operate on, or NULL.
+ */
+Object *dvmGetNextHeapWorkerObject(HeapWorkerOperation *op);
+
+#endif /*_DALVIK_ALLOC_HEAP_WORKER*/
diff --git a/vm/alloc/MarkSweep.c b/vm/alloc/MarkSweep.c
new file mode 100644
index 0000000..62a4ccc
--- /dev/null
+++ b/vm/alloc/MarkSweep.c
@@ -0,0 +1,986 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "alloc/clz.h"
+#include "alloc/HeapBitmap.h"
+#include "alloc/HeapInternal.h"
+#include "alloc/HeapSource.h"
+#include "alloc/MarkSweep.h"
+#include "alloc/Visit.h"
+#include <limits.h> // for ULONG_MAX
+#include <sys/mman.h> // for madvise(), mmap()
+#include <errno.h>
+
+#define GC_LOG_TAG LOG_TAG "-gc"
+
+#if LOG_NDEBUG
+#define LOGV_GC(...) ((void)0)
+#define LOGD_GC(...) ((void)0)
+#else
+#define LOGV_GC(...) LOG(LOG_VERBOSE, GC_LOG_TAG, __VA_ARGS__)
+#define LOGD_GC(...) LOG(LOG_DEBUG, GC_LOG_TAG, __VA_ARGS__)
+#endif
+
+#define LOGI_GC(...) LOG(LOG_INFO, GC_LOG_TAG, __VA_ARGS__)
+#define LOGW_GC(...) LOG(LOG_WARN, GC_LOG_TAG, __VA_ARGS__)
+#define LOGE_GC(...) LOG(LOG_ERROR, GC_LOG_TAG, __VA_ARGS__)
+
+#define LOG_SCAN(...) LOGV_GC("SCAN: " __VA_ARGS__)
+
+#define ALIGN_UP(x, n) (((size_t)(x) + (n) - 1) & ~((n) - 1))
+#define ALIGN_UP_TO_PAGE_SIZE(p) ALIGN_UP(p, SYSTEM_PAGE_SIZE)
+
+/* Do not cast the result of this to a boolean; the only set bit
+ * may be > 1<<8.
+ */
+static inline long isMarked(const void *obj, const GcMarkContext *ctx)
+{
+ return dvmHeapBitmapIsObjectBitSet(ctx->bitmap, obj);
+}
+
+static bool createMarkStack(GcMarkStack *stack)
+{
+ const Object **limit;
+ const char *name;
+ size_t size;
+
+ /* Create a stack big enough for the worst possible case,
+ * where the heap is perfectly full of the smallest object.
+ * TODO: be better about memory usage; use a smaller stack with
+ * overflow detection and recovery.
+ */
+ size = dvmHeapSourceGetIdealFootprint() * sizeof(Object*) /
+ (sizeof(Object) + HEAP_SOURCE_CHUNK_OVERHEAD);
+ size = ALIGN_UP_TO_PAGE_SIZE(size);
+ name = "dalvik-mark-stack";
+ limit = dvmAllocRegion(size, PROT_READ | PROT_WRITE, name);
+ if (limit == NULL) {
+ LOGE_GC("Could not mmap %zd-byte ashmem region '%s'", size, name);
+ return false;
+ }
+ stack->limit = limit;
+ stack->base = (const Object **)((uintptr_t)limit + size);
+ stack->top = stack->base;
+ return true;
+}
+
+static void destroyMarkStack(GcMarkStack *stack)
+{
+ munmap((char *)stack->limit,
+ (uintptr_t)stack->base - (uintptr_t)stack->limit);
+ memset(stack, 0, sizeof(*stack));
+}
+
+#define MARK_STACK_PUSH(stack, obj) \
+ do { \
+ *--(stack).top = (obj); \
+ } while (false)
+
+bool dvmHeapBeginMarkStep(GcMode mode)
+{
+ GcMarkContext *ctx = &gDvm.gcHeap->markContext;
+
+ if (!createMarkStack(&ctx->stack)) {
+ return false;
+ }
+ ctx->finger = NULL;
+ ctx->immuneLimit = dvmHeapSourceGetImmuneLimit(mode);
+ return true;
+}
+
+static long setAndReturnMarkBit(GcMarkContext *ctx, const void *obj)
+{
+ return dvmHeapBitmapSetAndReturnObjectBit(ctx->bitmap, obj);
+}
+
+static void markObjectNonNull(const Object *obj, GcMarkContext *ctx,
+ bool checkFinger)
+{
+ assert(ctx != NULL);
+ assert(obj != NULL);
+ assert(dvmIsValidObject(obj));
+
+ if (obj < (Object *)ctx->immuneLimit) {
+ assert(isMarked(obj, ctx));
+ return;
+ }
+ if (!setAndReturnMarkBit(ctx, obj)) {
+ /* This object was not previously marked.
+ */
+ if (checkFinger && (void *)obj < ctx->finger) {
+ /* This object will need to go on the mark stack.
+ */
+ MARK_STACK_PUSH(ctx->stack, obj);
+ }
+
+#if WITH_HPROF
+ if (gDvm.gcHeap->hprofContext != NULL) {
+ hprofMarkRootObject(gDvm.gcHeap->hprofContext, obj, 0);
+ }
+#endif
+ }
+}
+
+/* Used to mark objects when recursing. Recursion is done by moving
+ * the finger across the bitmaps in address order and marking child
+ * objects. Any newly-marked objects whose addresses are lower than
+ * the finger won't be visited by the bitmap scan, so those objects
+ * need to be added to the mark stack.
+ */
+static void markObject(const Object *obj, GcMarkContext *ctx)
+{
+ if (obj != NULL) {
+ markObjectNonNull(obj, ctx, true);
+ }
+}
+
+/* If the object hasn't already been marked, mark it and
+ * schedule it to be scanned for references.
+ *
+ * obj may not be NULL. The macro dvmMarkObject() should
+ * be used in situations where a reference may be NULL.
+ *
+ * This function may only be called when marking the root
+ * set. When recursing, use the internal markObject().
+ */
+void dvmMarkObjectNonNull(const Object *obj)
+{
+ assert(obj != NULL);
+ markObjectNonNull(obj, &gDvm.gcHeap->markContext, false);
+}
+
+/* Mark the set of root objects.
+ *
+ * Things we need to scan:
+ * - System classes defined by root classloader
+ * - For each thread:
+ * - Interpreted stack, from top to "curFrame"
+ * - Dalvik registers (args + local vars)
+ * - JNI local references
+ * - Automatic VM local references (TrackedAlloc)
+ * - Associated Thread/VMThread object
+ * - ThreadGroups (could track & start with these instead of working
+ * upward from Threads)
+ * - Exception currently being thrown, if present
+ * - JNI global references
+ * - Interned string table
+ * - Primitive classes
+ * - Special objects
+ * - gDvm.outOfMemoryObj
+ * - Objects allocated with ALLOC_NO_GC
+ * - Objects pending finalization (but not yet finalized)
+ * - Objects in debugger object registry
+ *
+ * Don't need:
+ * - Native stack (for in-progress stuff in the VM)
+ * - The TrackedAlloc stuff watches all native VM references.
+ */
+void dvmHeapMarkRootSet()
+{
+ GcHeap *gcHeap = gDvm.gcHeap;
+
+ HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_STICKY_CLASS, 0);
+
+ LOG_SCAN("immune objects");
+ dvmMarkImmuneObjects(gcHeap->markContext.immuneLimit);
+
+ LOG_SCAN("root class loader\n");
+ dvmGcScanRootClassLoader();
+ LOG_SCAN("primitive classes\n");
+ dvmGcScanPrimitiveClasses();
+
+ /* dvmGcScanRootThreadGroups() sets a bunch of
+ * different scan states internally.
+ */
+ HPROF_CLEAR_GC_SCAN_STATE();
+
+ LOG_SCAN("root thread groups\n");
+ dvmGcScanRootThreadGroups();
+
+ HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_INTERNED_STRING, 0);
+
+ LOG_SCAN("interned strings\n");
+ dvmGcScanInternedStrings();
+
+ HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_JNI_GLOBAL, 0);
+
+ LOG_SCAN("JNI global refs\n");
+ dvmGcMarkJniGlobalRefs();
+
+ HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_REFERENCE_CLEANUP, 0);
+
+ LOG_SCAN("pending reference operations\n");
+ dvmHeapMarkLargeTableRefs(gcHeap->referenceOperations);
+
+ HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_FINALIZING, 0);
+
+ LOG_SCAN("pending finalizations\n");
+ dvmHeapMarkLargeTableRefs(gcHeap->pendingFinalizationRefs);
+
+ HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_DEBUGGER, 0);
+
+ LOG_SCAN("debugger refs\n");
+ dvmGcMarkDebuggerRefs();
+
+ HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_VM_INTERNAL, 0);
+
+ /* Mark any special objects we have sitting around.
+ */
+ LOG_SCAN("special objects\n");
+ dvmMarkObjectNonNull(gDvm.outOfMemoryObj);
+ dvmMarkObjectNonNull(gDvm.internalErrorObj);
+ dvmMarkObjectNonNull(gDvm.noClassDefFoundErrorObj);
+//TODO: scan object references sitting in gDvm; use pointer begin & end
+
+ HPROF_CLEAR_GC_SCAN_STATE();
+}
+
+/*
+ * Callback applied to root references. If the root location contains
+ * a white reference it is pushed on the mark stack and grayed.
+ */
+static void markObjectVisitor(void *addr, void *arg)
+{
+ Object *obj;
+
+ assert(addr != NULL);
+ assert(arg != NULL);
+ obj = *(Object **)addr;
+ if (obj != NULL) {
+ markObjectNonNull(obj, arg, true);
+ }
+}
+
+/*
+ * Grays all references in the roots.
+ */
+void dvmHeapReMarkRootSet(void)
+{
+ GcMarkContext *ctx = &gDvm.gcHeap->markContext;
+ assert(ctx->finger == (void *)ULONG_MAX);
+ dvmVisitRoots(markObjectVisitor, ctx);
+}
+
+/*
+ * Nothing past this point is allowed to use dvmMarkObject() or
+ * dvmMarkObjectNonNull(), which are for root-marking only.
+ * Scanning/recursion must use markObject(), which takes the finger
+ * into account.
+ */
+#undef dvmMarkObject
+#define dvmMarkObject __dont_use_dvmMarkObject__
+#define dvmMarkObjectNonNull __dont_use_dvmMarkObjectNonNull__
+
+/*
+ * Scans instance fields.
+ */
+static void scanInstanceFields(const Object *obj, GcMarkContext *ctx)
+{
+ assert(obj != NULL);
+ assert(obj->clazz != NULL);
+ assert(ctx != NULL);
+
+ if (obj->clazz->refOffsets != CLASS_WALK_SUPER) {
+ unsigned int refOffsets = obj->clazz->refOffsets;
+ while (refOffsets != 0) {
+ const int rshift = CLZ(refOffsets);
+ refOffsets &= ~(CLASS_HIGH_BIT >> rshift);
+ markObject(dvmGetFieldObject((Object*)obj,
+ CLASS_OFFSET_FROM_CLZ(rshift)), ctx);
+ }
+ } else {
+ ClassObject *clazz;
+ int i;
+ for (clazz = obj->clazz; clazz != NULL; clazz = clazz->super) {
+ InstField *field = clazz->ifields;
+ for (i = 0; i < clazz->ifieldRefCount; ++i, ++field) {
+ void *addr = BYTE_OFFSET((Object *)obj, field->byteOffset);
+ markObject(((JValue *)addr)->l, ctx);
+ }
+ }
+ }
+}
+
+/*
+ * Scans the header, static field references, and interface
+ * pointers of a class object.
+ */
+static void scanClassObject(const ClassObject *obj, GcMarkContext *ctx)
+{
+ int i;
+
+ assert(obj != NULL);
+ assert(obj->obj.clazz == gDvm.classJavaLangClass);
+ assert(ctx != NULL);
+
+ markObject((Object *)obj->obj.clazz, ctx);
+ if (IS_CLASS_FLAG_SET(obj, CLASS_ISARRAY)) {
+ markObject((Object *)obj->elementClass, ctx);
+ }
+ /* Do super and the interfaces contain Objects and not dex idx values? */
+ if (obj->status > CLASS_IDX) {
+ markObject((Object *)obj->super, ctx);
+ }
+ markObject(obj->classLoader, ctx);
+ /* Scan static field references. */
+ for (i = 0; i < obj->sfieldCount; ++i) {
+ char ch = obj->sfields[i].field.signature[0];
+ if (ch == '[' || ch == 'L') {
+ markObject(obj->sfields[i].value.l, ctx);
+ }
+ }
+ /* Scan the instance fields. */
+ scanInstanceFields((const Object *)obj, ctx);
+ /* Scan interface references. */
+ if (obj->status > CLASS_IDX) {
+ for (i = 0; i < obj->interfaceCount; ++i) {
+ markObject((Object *)obj->interfaces[i], ctx);
+ }
+ }
+}
+
+/*
+ * Scans the header of all array objects. If the array object is
+ * specialized to a reference type, scans the array data as well.
+ */
+static void scanArrayObject(const ArrayObject *obj, GcMarkContext *ctx)
+{
+ size_t i;
+
+ assert(obj != NULL);
+ assert(obj->obj.clazz != NULL);
+ assert(ctx != NULL);
+ /* Scan the class object reference. */
+ markObject((Object *)obj->obj.clazz, ctx);
+ if (IS_CLASS_FLAG_SET(obj->obj.clazz, CLASS_ISOBJECTARRAY)) {
+ /* Scan the array contents. */
+ Object **contents = (Object **)obj->contents;
+ for (i = 0; i < obj->length; ++i) {
+ markObject(contents[i], ctx);
+ }
+ }
+}
+
+/*
+ * Returns class flags relating to Reference subclasses.
+ */
+static int referenceClassFlags(const Object *obj)
+{
+ int flags = CLASS_ISREFERENCE |
+ CLASS_ISWEAKREFERENCE |
+ CLASS_ISPHANTOMREFERENCE;
+ return GET_CLASS_FLAG_GROUP(obj->clazz, flags);
+}
+
+/*
+ * Returns true if the object derives from SoftReference.
+ */
+static bool isSoftReference(const Object *obj)
+{
+ return referenceClassFlags(obj) == CLASS_ISREFERENCE;
+}
+
+/*
+ * Returns true if the object derives from WeakReference.
+ */
+static bool isWeakReference(const Object *obj)
+{
+ return referenceClassFlags(obj) & CLASS_ISWEAKREFERENCE;
+}
+
+/*
+ * Returns true if the object derives from PhantomReference.
+ */
+static bool isPhantomReference(const Object *obj)
+{
+ return referenceClassFlags(obj) & CLASS_ISPHANTOMREFERENCE;
+}
+
+/*
+ * Adds a reference to the tail of a circular queue of references.
+ */
+static void enqueuePendingReference(Object *ref, Object **list)
+{
+ size_t offset;
+
+ assert(ref != NULL);
+ assert(list != NULL);
+ offset = gDvm.offJavaLangRefReference_pendingNext;
+ if (*list == NULL) {
+ dvmSetFieldObject(ref, offset, ref);
+ *list = ref;
+ } else {
+ Object *head = dvmGetFieldObject(*list, offset);
+ dvmSetFieldObject(ref, offset, head);
+ dvmSetFieldObject(*list, offset, ref);
+ }
+}
+
+/*
+ * Removes the reference at the head of a circular queue of
+ * references.
+ */
+static Object *dequeuePendingReference(Object **list)
+{
+ Object *ref, *head;
+ size_t offset;
+
+ assert(list != NULL);
+ assert(*list != NULL);
+ offset = gDvm.offJavaLangRefReference_pendingNext;
+ head = dvmGetFieldObject(*list, offset);
+ if (*list == head) {
+ ref = *list;
+ *list = NULL;
+ } else {
+ Object *next = dvmGetFieldObject(head, offset);
+ dvmSetFieldObject(*list, offset, next);
+ ref = head;
+ }
+ dvmSetFieldObject(ref, offset, NULL);
+ return ref;
+}
+
+/*
+ * Process the "referent" field in a java.lang.ref.Reference. If the
+ * referent has not yet been marked, put it on the appropriate list in
+ * the gcHeap for later processing.
+ */
+static void delayReferenceReferent(Object *obj, GcMarkContext *ctx)
+{
+ GcHeap *gcHeap = gDvm.gcHeap;
+ Object *pending, *referent;
+ size_t pendingNextOffset, referentOffset;
+
+ assert(obj != NULL);
+ assert(obj->clazz != NULL);
+ assert(IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISREFERENCE));
+ assert(ctx != NULL);
+ pendingNextOffset = gDvm.offJavaLangRefReference_pendingNext;
+ referentOffset = gDvm.offJavaLangRefReference_referent;
+ pending = dvmGetFieldObject(obj, pendingNextOffset);
+ referent = dvmGetFieldObject(obj, referentOffset);
+ if (pending == NULL && referent != NULL && !isMarked(referent, ctx)) {
+ Object **list = NULL;
+ if (isSoftReference(obj)) {
+ list = &gcHeap->softReferences;
+ } else if (isWeakReference(obj)) {
+ list = &gcHeap->weakReferences;
+ } else if (isPhantomReference(obj)) {
+ list = &gcHeap->phantomReferences;
+ }
+ assert(list != NULL);
+ enqueuePendingReference(obj, list);
+ }
+}
+
+/*
+ * Scans the header and field references of a data object.
+ */
+static void scanDataObject(DataObject *obj, GcMarkContext *ctx)
+{
+ assert(obj != NULL);
+ assert(obj->obj.clazz != NULL);
+ assert(ctx != NULL);
+ /* Scan the class object. */
+ markObject((Object *)obj->obj.clazz, ctx);
+ /* Scan the instance fields. */
+ scanInstanceFields((const Object *)obj, ctx);
+ if (IS_CLASS_FLAG_SET(obj->obj.clazz, CLASS_ISREFERENCE)) {
+ delayReferenceReferent((Object *)obj, ctx);
+ }
+}
+
+/*
+ * Scans an object reference. Determines the type of the reference
+ * and dispatches to a specialized scanning routine.
+ */
+static void scanObject(const Object *obj, GcMarkContext *ctx)
+{
+ assert(obj != NULL);
+ assert(ctx != NULL);
+ assert(obj->clazz != NULL);
+#if WITH_HPROF
+ if (gDvm.gcHeap->hprofContext != NULL) {
+ hprofDumpHeapObject(gDvm.gcHeap->hprofContext, obj);
+ }
+#endif
+ /* Dispatch a type-specific scan routine. */
+ if (obj->clazz == gDvm.classJavaLangClass) {
+ scanClassObject((ClassObject *)obj, ctx);
+ } else if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISARRAY)) {
+ scanArrayObject((ArrayObject *)obj, ctx);
+ } else {
+ scanDataObject((DataObject *)obj, ctx);
+ }
+}
+
+static void
+processMarkStack(GcMarkContext *ctx)
+{
+ const Object **const base = ctx->stack.base;
+
+ /* Scan anything that's on the mark stack.
+ * We can't use the bitmaps anymore, so use
+ * a finger that points past the end of them.
+ */
+ ctx->finger = (void *)ULONG_MAX;
+ while (ctx->stack.top != base) {
+ scanObject(*ctx->stack.top++, ctx);
+ }
+}
+
+static size_t objectSize(const Object *obj)
+{
+ assert(dvmIsValidObject(obj));
+ assert(dvmIsValidObject((Object *)obj->clazz));
+ if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISARRAY)) {
+ return dvmArrayObjectSize((ArrayObject *)obj);
+ } else if (obj->clazz == gDvm.classJavaLangClass) {
+ return dvmClassObjectSize((ClassObject *)obj);
+ } else {
+ return obj->clazz->objectSize;
+ }
+}
+
+/*
+ * Scans forward to the header of the next marked object between start
+ * and limit. Returns NULL if no marked objects are in that region.
+ */
+static Object *nextGrayObject(u1 *base, u1 *limit, HeapBitmap *markBits)
+{
+ u1 *ptr;
+
+ assert(base < limit);
+ assert(limit - base <= GC_CARD_SIZE);
+ for (ptr = base; ptr < limit; ptr += HB_OBJECT_ALIGNMENT) {
+ if (dvmHeapBitmapIsObjectBitSet(markBits, ptr))
+ return (Object *)ptr;
+ }
+ return NULL;
+}
+
+/*
+ * Scan the card table looking for objects that have been grayed by
+ * the mutator.
+ */
+static void scanGrayObjects(GcMarkContext *ctx)
+{
+ GcHeap *h = gDvm.gcHeap;
+ HeapBitmap *markBits, *liveBits;
+ u1 *card, *baseCard, *limitCard;
+ size_t footprint;
+
+ markBits = ctx->bitmap;
+ liveBits = dvmHeapSourceGetLiveBits();
+ footprint = dvmHeapSourceGetValue(HS_FOOTPRINT, NULL, 0);
+ baseCard = &h->cardTableBase[0];
+ limitCard = dvmCardFromAddr((u1 *)dvmHeapSourceGetBase() + footprint);
+ assert(limitCard <= &h->cardTableBase[h->cardTableLength]);
+ for (card = baseCard; card != limitCard; ++card) {
+ if (*card == GC_CARD_DIRTY) {
+ /*
+ * The card is dirty. Scan all of the objects that
+ * intersect with the card address.
+ */
+ u1 *addr = dvmAddrFromCard(card);
+ /*
+ * Scan through all black objects that start on the
+ * current card.
+ */
+ u1 *limit = addr + GC_CARD_SIZE;
+ u1 *next = addr;
+ while (next < limit) {
+ Object *obj = nextGrayObject(next, limit, markBits);
+ if (obj == NULL)
+ break;
+ scanObject(obj, ctx);
+ next = (u1*)obj + ALIGN_UP(objectSize(obj), HB_OBJECT_ALIGNMENT);
+ }
+ }
+ }
+}
+
+/*
+ * Callback for scanning each object in the bitmap. The finger is set
+ * to the address corresponding to the lowest address in the next word
+ * of bits in the bitmap.
+ */
+static void scanBitmapCallback(void *addr, void *finger, void *arg)
+{
+ GcMarkContext *ctx = arg;
+ ctx->finger = (void *)finger;
+ scanObject(addr, ctx);
+}
+
+/* Given bitmaps with the root set marked, find and mark all
+ * reachable objects. When this returns, the entire set of
+ * live objects will be marked and the mark stack will be empty.
+ */
+void dvmHeapScanMarkedObjects(void)
+{
+ GcMarkContext *ctx = &gDvm.gcHeap->markContext;
+
+ assert(ctx->finger == NULL);
+
+ /* The bitmaps currently have bits set for the root set.
+ * Walk across the bitmaps and scan each object.
+ */
+ dvmHeapBitmapScanWalk(ctx->bitmap, scanBitmapCallback, ctx);
+
+ /* We've walked the mark bitmaps. Scan anything that's
+ * left on the mark stack.
+ */
+ processMarkStack(ctx);
+
+ LOG_SCAN("done with marked objects\n");
+}
+
+void dvmHeapReScanMarkedObjects(void)
+{
+ GcMarkContext *ctx = &gDvm.gcHeap->markContext;
+
+ /*
+ * The finger must have been set to the maximum value to ensure
+ * that gray objects will be pushed onto the mark stack.
+ */
+ assert(ctx->finger == (void *)ULONG_MAX);
+ scanGrayObjects(ctx);
+ processMarkStack(ctx);
+}
+
+/*
+ * Clear the referent field.
+ */
+static void clearReference(Object *reference)
+{
+ size_t offset = gDvm.offJavaLangRefReference_referent;
+ dvmSetFieldObject(reference, offset, NULL);
+}
+
+/*
+ * Returns true if the reference was registered with a reference queue
+ * and has not yet been enqueued.
+ */
+static bool isEnqueuable(const Object *reference)
+{
+ Object *queue = dvmGetFieldObject(reference,
+ gDvm.offJavaLangRefReference_queue);
+ Object *queueNext = dvmGetFieldObject(reference,
+ gDvm.offJavaLangRefReference_queueNext);
+ return queue != NULL && queueNext == NULL;
+}
+
+/*
+ * Schedules a reference to be appended to its reference queue.
+ */
+static void enqueueReference(Object *ref)
+{
+ assert(ref != NULL);
+ assert(dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queue) != NULL);
+ assert(dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queueNext) == NULL);
+ if (!dvmHeapAddRefToLargeTable(&gDvm.gcHeap->referenceOperations, ref)) {
+ LOGE_HEAP("enqueueReference(): no room for any more "
+ "reference operations\n");
+ dvmAbort();
+ }
+}
+
+/*
+ * Walks the reference list marking any references subject to the
+ * reference clearing policy. References with a black referent are
+ * removed from the list. References with white referents biased
+ * toward saving are blackened and also removed from the list.
+ */
+void dvmHandleSoftRefs(Object **list)
+{
+ GcMarkContext *ctx;
+ Object *ref, *referent;
+ Object *clear;
+ size_t referentOffset;
+ size_t counter;
+ bool marked;
+
+ ctx = &gDvm.gcHeap->markContext;
+ referentOffset = gDvm.offJavaLangRefReference_referent;
+ clear = NULL;
+ counter = 0;
+ while (*list != NULL) {
+ ref = dequeuePendingReference(list);
+ referent = dvmGetFieldObject(ref, referentOffset);
+ if (referent == NULL) {
+ /* Referent was cleared by the user during marking. */
+ continue;
+ }
+ marked = isMarked(referent, ctx);
+ if (!marked && ((++counter) & 1)) {
+ /* Referent is white and biased toward saving, mark it. */
+ markObject(referent, ctx);
+ marked = true;
+ }
+ if (!marked) {
+ /* Referent is white, queue it for clearing. */
+ enqueuePendingReference(ref, &clear);
+ }
+ }
+ *list = clear;
+ /*
+ * Restart the mark with the newly black references added to the
+ * root set.
+ */
+ processMarkStack(ctx);
+}
+
+/*
+ * Unlink the reference list clearing references objects with white
+ * referents. Cleared references registered to a reference queue are
+ * scheduled for appending by the heap worker thread.
+ */
+void dvmClearWhiteRefs(Object **list)
+{
+ GcMarkContext *ctx;
+ Object *ref, *referent;
+ size_t referentOffset;
+ bool doSignal;
+
+ ctx = &gDvm.gcHeap->markContext;
+ referentOffset = gDvm.offJavaLangRefReference_referent;
+ doSignal = false;
+ while (*list != NULL) {
+ ref = dequeuePendingReference(list);
+ referent = dvmGetFieldObject(ref, referentOffset);
+ if (referent != NULL && !isMarked(referent, ctx)) {
+ /* Referent is white, clear it. */
+ clearReference(ref);
+ if (isEnqueuable(ref)) {
+ enqueueReference(ref);
+ doSignal = true;
+ }
+ }
+ }
+ /*
+ * If we cleared a reference with a reference queue we must notify
+ * the heap worker to append the reference.
+ */
+ if (doSignal) {
+ dvmSignalHeapWorker(false);
+ }
+ assert(*list == NULL);
+}
+
+/* Find unreachable objects that need to be finalized,
+ * and schedule them for finalization.
+ */
+void dvmHeapScheduleFinalizations()
+{
+ HeapRefTable newPendingRefs;
+ LargeHeapRefTable *finRefs = gDvm.gcHeap->finalizableRefs;
+ Object **ref;
+ Object **lastRef;
+ size_t totalPendCount;
+ GcMarkContext *ctx = &gDvm.gcHeap->markContext;
+
+ /*
+ * All reachable objects have been marked.
+ * Any unmarked finalizable objects need to be finalized.
+ */
+
+ /* Create a table that the new pending refs will
+ * be added to.
+ */
+ if (!dvmHeapInitHeapRefTable(&newPendingRefs)) {
+ //TODO: mark all finalizable refs and hope that
+ // we can schedule them next time. Watch out,
+ // because we may be expecting to free up space
+ // by calling finalizers.
+ LOGE_GC("dvmHeapScheduleFinalizations(): no room for "
+ "pending finalizations\n");
+ dvmAbort();
+ }
+
+ /* Walk through finalizableRefs and move any unmarked references
+ * to the list of new pending refs.
+ */
+ totalPendCount = 0;
+ while (finRefs != NULL) {
+ Object **gapRef;
+ size_t newPendCount = 0;
+
+ gapRef = ref = finRefs->refs.table;
+ lastRef = finRefs->refs.nextEntry;
+ while (ref < lastRef) {
+ if (!isMarked(*ref, ctx)) {
+ if (!dvmHeapAddToHeapRefTable(&newPendingRefs, *ref)) {
+ //TODO: add the current table and allocate
+ // a new, smaller one.
+ LOGE_GC("dvmHeapScheduleFinalizations(): "
+ "no room for any more pending finalizations: %zd\n",
+ dvmHeapNumHeapRefTableEntries(&newPendingRefs));
+ dvmAbort();
+ }
+ newPendCount++;
+ } else {
+ /* This ref is marked, so will remain on finalizableRefs.
+ */
+ if (newPendCount > 0) {
+ /* Copy it up to fill the holes.
+ */
+ *gapRef++ = *ref;
+ } else {
+ /* No holes yet; don't bother copying.
+ */
+ gapRef++;
+ }
+ }
+ ref++;
+ }
+ finRefs->refs.nextEntry = gapRef;
+ //TODO: if the table is empty when we're done, free it.
+ totalPendCount += newPendCount;
+ finRefs = finRefs->next;
+ }
+ LOGD_GC("dvmHeapScheduleFinalizations(): %zd finalizers triggered.\n",
+ totalPendCount);
+ if (totalPendCount == 0) {
+ /* No objects required finalization.
+ * Free the empty temporary table.
+ */
+ dvmClearReferenceTable(&newPendingRefs);
+ return;
+ }
+
+ /* Add the new pending refs to the main list.
+ */
+ if (!dvmHeapAddTableToLargeTable(&gDvm.gcHeap->pendingFinalizationRefs,
+ &newPendingRefs))
+ {
+ LOGE_GC("dvmHeapScheduleFinalizations(): can't insert new "
+ "pending finalizations\n");
+ dvmAbort();
+ }
+
+ //TODO: try compacting the main list with a memcpy loop
+
+ /* Mark the refs we just moved; we don't want them or their
+ * children to get swept yet.
+ */
+ ref = newPendingRefs.table;
+ lastRef = newPendingRefs.nextEntry;
+ assert(ref < lastRef);
+ HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_FINALIZING, 0);
+ while (ref < lastRef) {
+ assert(*ref != NULL);
+ markObject(*ref, ctx);
+ ref++;
+ }
+ HPROF_CLEAR_GC_SCAN_STATE();
+ processMarkStack(ctx);
+ dvmSignalHeapWorker(false);
+}
+
+void dvmHeapFinishMarkStep()
+{
+ GcMarkContext *ctx;
+
+ ctx = &gDvm.gcHeap->markContext;
+
+ /* The mark bits are now not needed.
+ */
+ dvmHeapSourceZeroMarkBitmap();
+
+ /* Clean up everything else associated with the marking process.
+ */
+ destroyMarkStack(&ctx->stack);
+
+ ctx->finger = NULL;
+}
+
+typedef struct {
+ size_t numObjects;
+ size_t numBytes;
+ bool isConcurrent;
+} SweepContext;
+
+static void sweepBitmapCallback(size_t numPtrs, void **ptrs, void *arg)
+{
+ SweepContext *ctx = arg;
+
+ if (ctx->isConcurrent) {
+ dvmLockHeap();
+ }
+ ctx->numBytes += dvmHeapSourceFreeList(numPtrs, ptrs);
+ ctx->numObjects += numPtrs;
+ if (ctx->isConcurrent) {
+ dvmUnlockHeap();
+ }
+}
+
+/*
+ * Returns true if the given object is unmarked. This assumes that
+ * the bitmaps have not yet been swapped.
+ */
+static int isUnmarkedObject(void *object)
+{
+ return !isMarked((void *)((uintptr_t)object & ~(HB_OBJECT_ALIGNMENT-1)),
+ &gDvm.gcHeap->markContext);
+}
+
+/*
+ * Process all the internal system structures that behave like
+ * weakly-held objects.
+ */
+void dvmHeapSweepSystemWeaks(void)
+{
+ dvmGcDetachDeadInternedStrings(isUnmarkedObject);
+ dvmSweepMonitorList(&gDvm.monitorList, isUnmarkedObject);
+}
+
+/*
+ * Walk through the list of objects that haven't been marked and free
+ * them. Assumes the bitmaps have been swapped.
+ */
+void dvmHeapSweepUnmarkedObjects(GcMode mode, bool isConcurrent,
+ size_t *numObjects, size_t *numBytes)
+{
+ HeapBitmap currMark[HEAP_SOURCE_MAX_HEAP_COUNT];
+ HeapBitmap currLive[HEAP_SOURCE_MAX_HEAP_COUNT];
+ SweepContext ctx;
+ size_t numBitmaps, numSweepBitmaps;
+ size_t i;
+
+ numBitmaps = dvmHeapSourceGetNumHeaps();
+ dvmHeapSourceGetObjectBitmaps(currLive, currMark, numBitmaps);
+ if (mode == GC_PARTIAL) {
+ numSweepBitmaps = 1;
+ assert((uintptr_t)gDvm.gcHeap->markContext.immuneLimit == currLive[0].base);
+ } else {
+ numSweepBitmaps = numBitmaps;
+ }
+ ctx.numObjects = ctx.numBytes = 0;
+ ctx.isConcurrent = isConcurrent;
+ for (i = 0; i < numSweepBitmaps; i++) {
+ HeapBitmap* prevLive = &currMark[i];
+ HeapBitmap* prevMark = &currLive[i];
+ dvmHeapBitmapSweepWalk(prevLive, prevMark, sweepBitmapCallback, &ctx);
+ }
+ *numObjects = ctx.numObjects;
+ *numBytes = ctx.numBytes;
+ if (gDvm.allocProf.enabled) {
+ gDvm.allocProf.freeCount += ctx.numObjects;
+ gDvm.allocProf.freeSize += ctx.numBytes;
+ }
+}
diff --git a/vm/alloc/MarkSweep.h b/vm/alloc/MarkSweep.h
new file mode 100644
index 0000000..ffdfbd5
--- /dev/null
+++ b/vm/alloc/MarkSweep.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _DALVIK_ALLOC_MARK_SWEEP
+#define _DALVIK_ALLOC_MARK_SWEEP
+
+#include "alloc/HeapBitmap.h"
+#include "alloc/HeapSource.h"
+
+/* Downward-growing stack for better cache read behavior.
+ */
+typedef struct {
+ /* Lowest address (inclusive)
+ */
+ const Object **limit;
+
+ /* Current top of the stack (inclusive)
+ */
+ const Object **top;
+
+ /* Highest address (exclusive)
+ */
+ const Object **base;
+} GcMarkStack;
+
+/* This is declared publicly so that it can be included in gDvm.gcHeap.
+ */
+typedef struct {
+ HeapBitmap *bitmap;
+ GcMarkStack stack;
+ const char *immuneLimit;
+ const void *finger; // only used while scanning/recursing.
+} GcMarkContext;
+
+bool dvmHeapBeginMarkStep(GcMode mode);
+void dvmHeapMarkRootSet(void);
+void dvmHeapReMarkRootSet(void);
+void dvmHeapScanMarkedObjects(void);
+void dvmHeapReScanMarkedObjects(void);
+void dvmHandleSoftRefs(Object **list);
+void dvmClearWhiteRefs(Object **list);
+void dvmHeapScheduleFinalizations(void);
+void dvmHeapFinishMarkStep(void);
+void dvmHeapSweepSystemWeaks(void);
+void dvmHeapSweepUnmarkedObjects(GcMode mode, bool isConcurrent,
+ size_t *numObjects, size_t *numBytes);
+
+#endif // _DALVIK_ALLOC_MARK_SWEEP
diff --git a/vm/alloc/TEST/HeapBitmapTest/Makefile b/vm/alloc/TEST/HeapBitmapTest/Makefile
new file mode 100644
index 0000000..fe31b24
--- /dev/null
+++ b/vm/alloc/TEST/HeapBitmapTest/Makefile
@@ -0,0 +1,28 @@
+.PHONY: all
+all: runtest
+
+$(shell mkdir -p out)
+
+CC := gcc
+CFLAGS := -g -Wall -Werror
+#CFLAGS += -O2
+
+out/main.o: main.c ../../HeapBitmap.h
+ $(CC) $(CFLAGS) -c $< -o $@ -I ../..
+
+out/HeapBitmap.o: ../../HeapBitmap.c ../../HeapBitmap.h ../../clz.h include/cutils/ashmem.h include/Dalvik.h
+ $(CC) $(CFLAGS) -c $< -o $@ -I ../.. -I include
+
+out/clz.o: ../../clz.c ../../clz.h
+ $(CC) $(CFLAGS) -c $< -o $@ -I ../..
+
+out/hbtest: out/main.o out/HeapBitmap.o out/clz.o
+ $(CC) $^ -o $@
+
+.PHONY: runtest
+runtest: out/hbtest
+ out/hbtest
+
+.PHONY: clean
+clean:
+ rm -rf out
diff --git a/vm/alloc/TEST/HeapBitmapTest/include/Dalvik.h b/vm/alloc/TEST/HeapBitmapTest/include/Dalvik.h
new file mode 100644
index 0000000..4c9f608
--- /dev/null
+++ b/vm/alloc/TEST/HeapBitmapTest/include/Dalvik.h
@@ -0,0 +1,18 @@
+#ifndef DALVIK_H_
+#define DALVIK_H_
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+
+#define LOGW(...) printf("W/" __VA_ARGS__)
+#define LOGE(...) printf("E/" __VA_ARGS__)
+
+inline void dvmAbort(void) {
+ exit(1);
+}
+
+#endif // DALVIK_H_
diff --git a/vm/alloc/TEST/HeapBitmapTest/include/cutils/ashmem.h b/vm/alloc/TEST/HeapBitmapTest/include/cutils/ashmem.h
new file mode 100644
index 0000000..8680c77
--- /dev/null
+++ b/vm/alloc/TEST/HeapBitmapTest/include/cutils/ashmem.h
@@ -0,0 +1,14 @@
+#ifndef ASHMEM_H_
+#define ASHMEM_H_
+
+#include <fcntl.h>
+
+#define ASHMEM_NAME_LEN 128
+
+inline int
+ashmem_create_region(const char *name, size_t len)
+{
+ return open("/dev/zero", O_RDWR);
+}
+
+#endif
diff --git a/vm/alloc/TEST/HeapBitmapTest/main.c b/vm/alloc/TEST/HeapBitmapTest/main.c
new file mode 100644
index 0000000..10fa7f8
--- /dev/null
+++ b/vm/alloc/TEST/HeapBitmapTest/main.c
@@ -0,0 +1,496 @@
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <assert.h>
+#include <string.h>
+#include <unistd.h>
+#define __attribute(x) /* disable inlining */
+#include "HeapBitmap.h"
+#undef __attribute
+
+#define PAGE_SIZE 4096
+#define HEAP_BASE ((void *)0x10000)
+#define HEAP_SIZE (5 * PAGE_SIZE + 888)
+
+#define VERBOSE 1
+#if VERBOSE
+#define TRACE(...) printf(__VA_ARGS__)
+#else
+#define TRACE(...) /**/
+#endif
+
+void
+test_init()
+{
+ HeapBitmap hb;
+ bool ok;
+
+ memset(&hb, 0x55, sizeof(hb));
+
+ ok = dvmHeapBitmapInit(&hb, HEAP_BASE, HEAP_SIZE, "test");
+ assert(ok);
+
+ assert(hb.bits != NULL);
+ assert(hb.bitsLen >= HB_OFFSET_TO_INDEX(HEAP_SIZE));
+ assert(hb.base == (uintptr_t)HEAP_BASE);
+ assert(hb.max < hb.base);
+
+ /* Make sure hb.bits is mapped.
+ */
+ *hb.bits = 0x55;
+ assert(*hb.bits = 0x55);
+ *hb.bits = 0;
+
+#define TEST_UNMAP 0
+#if TEST_UNMAP
+ /* Hold onto this to make sure it's unmapped later.
+ */
+ unsigned long int *bits = hb.bits;
+#endif
+
+ dvmHeapBitmapDelete(&hb);
+
+ assert(hb.bits == NULL);
+ assert(hb.bitsLen == 0);
+ assert(hb.base == 0);
+ assert(hb.max == 0);
+
+#if TEST_UNMAP
+ /* This pointer shouldn't be mapped anymore.
+ */
+ *bits = 0x55;
+ assert(!"Should have segfaulted");
+#endif
+}
+
+bool is_zeroed(const HeapBitmap *hb)
+{
+ int i;
+
+ for (i = 0; i < hb->bitsLen / sizeof (*hb->bits); i++) {
+ if (hb->bits[i] != 0L) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void assert_empty(const HeapBitmap *hb)
+{
+ assert(hb->bits != NULL);
+ assert(hb->bitsLen >= HB_OFFSET_TO_INDEX(HEAP_SIZE));
+ assert(hb->base == (uintptr_t)HEAP_BASE);
+ assert(hb->max < hb->base);
+
+ assert(is_zeroed(hb));
+
+ assert(!dvmHeapBitmapMayContainObject(hb,
+ HEAP_BASE));
+ assert(!dvmHeapBitmapMayContainObject(hb,
+ HEAP_BASE + HB_OBJECT_ALIGNMENT));
+ assert(!dvmHeapBitmapMayContainObject(hb,
+ HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+ assert(!dvmHeapBitmapMayContainObject(hb,
+ HEAP_BASE + HEAP_SIZE));
+
+ assert(!dvmHeapBitmapIsObjectBitSet(hb,
+ HEAP_BASE));
+ assert(!dvmHeapBitmapIsObjectBitSet(hb,
+ HEAP_BASE + HB_OBJECT_ALIGNMENT));
+ assert(!dvmHeapBitmapIsObjectBitSet(hb,
+ HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+}
+
+void
+test_bits()
+{
+ HeapBitmap hb;
+ bool ok;
+
+ ok = dvmHeapBitmapInit(&hb, HEAP_BASE, HEAP_SIZE, "test");
+ assert(ok);
+
+ assert_empty(&hb);
+
+ /* Set the lowest address.
+ */
+ dvmHeapBitmapSetObjectBit(&hb, HEAP_BASE);
+ assert(dvmHeapBitmapMayContainObject(&hb,
+ HEAP_BASE));
+ assert(!dvmHeapBitmapMayContainObject(&hb,
+ HEAP_BASE + HB_OBJECT_ALIGNMENT));
+ assert(!dvmHeapBitmapMayContainObject(&hb,
+ HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+ assert(!dvmHeapBitmapMayContainObject(&hb,
+ HEAP_BASE + HEAP_SIZE));
+
+ assert(dvmHeapBitmapIsObjectBitSet(&hb,
+ HEAP_BASE));
+ assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+ HEAP_BASE + HB_OBJECT_ALIGNMENT));
+ assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+ HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+
+ /* Set the highest address.
+ */
+ dvmHeapBitmapSetObjectBit(&hb, HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT);
+ assert(dvmHeapBitmapMayContainObject(&hb,
+ HEAP_BASE));
+ assert(dvmHeapBitmapMayContainObject(&hb,
+ HEAP_BASE + HB_OBJECT_ALIGNMENT));
+ assert(dvmHeapBitmapMayContainObject(&hb,
+ HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+ assert(!dvmHeapBitmapMayContainObject(&hb,
+ HEAP_BASE + HEAP_SIZE));
+
+ assert(dvmHeapBitmapIsObjectBitSet(&hb,
+ HEAP_BASE));
+ assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+ HEAP_BASE + HB_OBJECT_ALIGNMENT));
+ assert(dvmHeapBitmapIsObjectBitSet(&hb,
+ HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+
+ /* Clear the lowest address.
+ */
+ dvmHeapBitmapClearObjectBit(&hb, HEAP_BASE);
+ assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+ HEAP_BASE));
+ assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+ HEAP_BASE + HB_OBJECT_ALIGNMENT));
+ assert(dvmHeapBitmapIsObjectBitSet(&hb,
+ HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+ assert(!is_zeroed(&hb));
+
+ /* Clear the highest address.
+ */
+ dvmHeapBitmapClearObjectBit(&hb,
+ HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT);
+ assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+ HEAP_BASE));
+ assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+ HEAP_BASE + HB_OBJECT_ALIGNMENT));
+ assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+ HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+ assert(is_zeroed(&hb));
+
+ /* Clean up.
+ */
+ dvmHeapBitmapDelete(&hb);
+}
+
+void
+test_clear()
+{
+ HeapBitmap hb;
+ bool ok;
+
+ ok = dvmHeapBitmapInit(&hb, HEAP_BASE, HEAP_SIZE, "test");
+ assert(ok);
+ assert_empty(&hb);
+
+ /* Set the highest address.
+ */
+ dvmHeapBitmapSetObjectBit(&hb, HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT);
+ assert(!is_zeroed(&hb));
+
+ /* Clear the bitmap.
+ */
+ dvmHeapBitmapZero(&hb);
+ assert_empty(&hb);
+
+ /* Clean up.
+ */
+ dvmHeapBitmapDelete(&hb);
+}
+
+void
+test_modify()
+{
+ HeapBitmap hb;
+ bool ok;
+ unsigned long bit;
+
+ ok = dvmHeapBitmapInit(&hb, HEAP_BASE, HEAP_SIZE, "test");
+ assert(ok);
+ assert_empty(&hb);
+
+ /* Set the lowest address.
+ */
+ bit = dvmHeapBitmapSetAndReturnObjectBit(&hb, HEAP_BASE);
+ assert(bit == 0);
+ assert(dvmHeapBitmapMayContainObject(&hb,
+ HEAP_BASE));
+ assert(!dvmHeapBitmapMayContainObject(&hb,
+ HEAP_BASE + HB_OBJECT_ALIGNMENT));
+ assert(!dvmHeapBitmapMayContainObject(&hb,
+ HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+ assert(!dvmHeapBitmapMayContainObject(&hb,
+ HEAP_BASE + HEAP_SIZE));
+
+ assert(dvmHeapBitmapIsObjectBitSet(&hb,
+ HEAP_BASE));
+ assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+ HEAP_BASE + HB_OBJECT_ALIGNMENT));
+ assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+ HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+
+ /* Set the lowest address again.
+ */
+ bit = dvmHeapBitmapSetAndReturnObjectBit(&hb, HEAP_BASE);
+ assert(bit != 0);
+ assert(dvmHeapBitmapMayContainObject(&hb,
+ HEAP_BASE));
+ assert(!dvmHeapBitmapMayContainObject(&hb,
+ HEAP_BASE + HB_OBJECT_ALIGNMENT));
+ assert(!dvmHeapBitmapMayContainObject(&hb,
+ HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+ assert(!dvmHeapBitmapMayContainObject(&hb,
+ HEAP_BASE + HEAP_SIZE));
+
+ assert(dvmHeapBitmapIsObjectBitSet(&hb,
+ HEAP_BASE));
+ assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+ HEAP_BASE + HB_OBJECT_ALIGNMENT));
+ assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+ HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+
+ /* Set the highest address.
+ */
+ bit = dvmHeapBitmapSetAndReturnObjectBit(&hb,
+ HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT);
+ assert(bit == 0);
+ assert(dvmHeapBitmapMayContainObject(&hb,
+ HEAP_BASE));
+ assert(dvmHeapBitmapMayContainObject(&hb,
+ HEAP_BASE + HB_OBJECT_ALIGNMENT));
+ assert(dvmHeapBitmapMayContainObject(&hb,
+ HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+ assert(!dvmHeapBitmapMayContainObject(&hb,
+ HEAP_BASE + HEAP_SIZE));
+
+ assert(dvmHeapBitmapIsObjectBitSet(&hb,
+ HEAP_BASE));
+ assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+ HEAP_BASE + HB_OBJECT_ALIGNMENT));
+ assert(dvmHeapBitmapIsObjectBitSet(&hb,
+ HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+
+ /* Set the highest address again.
+ */
+ bit = dvmHeapBitmapSetAndReturnObjectBit(&hb,
+ HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT);
+ assert(bit != 0);
+ assert(dvmHeapBitmapMayContainObject(&hb,
+ HEAP_BASE));
+ assert(dvmHeapBitmapMayContainObject(&hb,
+ HEAP_BASE + HB_OBJECT_ALIGNMENT));
+ assert(dvmHeapBitmapMayContainObject(&hb,
+ HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+ assert(!dvmHeapBitmapMayContainObject(&hb,
+ HEAP_BASE + HEAP_SIZE));
+
+ assert(dvmHeapBitmapIsObjectBitSet(&hb,
+ HEAP_BASE));
+ assert(!dvmHeapBitmapIsObjectBitSet(&hb,
+ HEAP_BASE + HB_OBJECT_ALIGNMENT));
+ assert(dvmHeapBitmapIsObjectBitSet(&hb,
+ HEAP_BASE + HEAP_SIZE - HB_OBJECT_ALIGNMENT));
+
+ /* Clean up.
+ */
+ dvmHeapBitmapDelete(&hb);
+}
+
+/*
+ * xor test support functions
+ */
+
+static void *gCallbackArg = NULL;
+
+#define NUM_XOR_PTRS 128
+static size_t gNumPtrs;
+static void *gXorPtrs[NUM_XOR_PTRS];
+static bool gClearedPtrs[NUM_XOR_PTRS];
+static bool gSeenPtrs[NUM_XOR_PTRS];
+
+bool
+xorCallback(size_t numPtrs, void **ptrs, const void *finger, void *arg)
+{
+ assert(numPtrs > 0);
+ assert(ptrs != NULL);
+ assert(arg == gCallbackArg);
+
+size_t i;
+ for (i = 0; i < numPtrs; i++) {
+ assert(ptrs[i] < finger);
+ printf("callback: 0x%08x ( < 0x%08x )\n",
+ (uintptr_t)ptrs[i], (uintptr_t)finger);
+ }
+
+ return true;
+}
+
+bool
+seenAndClearedMatch()
+{
+ size_t i;
+ for (i = 0; i < gNumPtrs; i++) {
+ if (gClearedPtrs[i] != gSeenPtrs[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void
+run_xor(ssize_t offset, size_t step)
+{
+ assert(step != 0);
+ assert(step < HEAP_SIZE);
+
+ /* Figure out the range.
+ */
+uintptr_t base;
+uintptr_t top;
+ if (offset >= 0) {
+ base = (uintptr_t)HEAP_BASE + offset;
+ } else {
+ base = (uintptr_t)HEAP_BASE + (uintptr_t)HEAP_SIZE + offset;
+ }
+ if (base < (uintptr_t)HEAP_BASE) {
+ base = (uintptr_t)HEAP_BASE;
+ } else if (base > (uintptr_t)(HEAP_BASE + HEAP_SIZE)) {
+ base = (uintptr_t)(HEAP_BASE + HEAP_SIZE);
+ } else {
+ base = (base + HB_OBJECT_ALIGNMENT - 1) & ~(HB_OBJECT_ALIGNMENT - 1);
+ }
+ step *= HB_OBJECT_ALIGNMENT;
+ top = base + step * NUM_XOR_PTRS;
+ if (top > (uintptr_t)(HEAP_BASE + HEAP_SIZE)) {
+ top = (uintptr_t)(HEAP_BASE + HEAP_SIZE);
+ }
+
+ /* Create the pointers.
+ */
+ gNumPtrs = 0;
+ memset(gXorPtrs, 0, sizeof(gXorPtrs));
+ memset(gClearedPtrs, 0, sizeof(gClearedPtrs));
+ memset(gSeenPtrs, 0, sizeof(gSeenPtrs));
+
+uintptr_t addr;
+void **p = gXorPtrs;
+ for (addr = base; addr < top; addr += step) {
+ *p++ = (void *)addr;
+ gNumPtrs++;
+ }
+ assert(seenAndClearedMatch());
+
+ /* Set up the bitmaps.
+ */
+HeapBitmap hb1, hb2;
+bool ok;
+
+ ok = dvmHeapBitmapInit(&hb1, HEAP_BASE, HEAP_SIZE, "test1");
+ assert(ok);
+ ok = dvmHeapBitmapInitFromTemplate(&hb2, &hb1, "test2");
+ assert(ok);
+
+ /* Walk two empty bitmaps.
+ */
+TRACE("walk 0\n");
+ ok = dvmHeapBitmapXorWalk(&hb1, &hb2, xorCallback, gCallbackArg);
+ assert(ok);
+ assert(seenAndClearedMatch());
+
+ /* Walk one empty bitmap.
+ */
+TRACE("walk 1\n");
+ dvmHeapBitmapSetObjectBit(&hb1, (void *)base);
+ ok = dvmHeapBitmapXorWalk(&hb1, &hb2, xorCallback, gCallbackArg);
+ assert(ok);
+
+ /* Make the bitmaps match.
+ */
+TRACE("walk 2\n");
+ dvmHeapBitmapSetObjectBit(&hb2, (void *)base);
+ ok = dvmHeapBitmapXorWalk(&hb1, &hb2, xorCallback, gCallbackArg);
+ assert(ok);
+
+ /* Clear the bitmaps.
+ */
+ dvmHeapBitmapZero(&hb1);
+ assert_empty(&hb1);
+ dvmHeapBitmapZero(&hb2);
+ assert_empty(&hb2);
+
+ /* Set the pointers we created in one of the bitmaps,
+ * then visit them.
+ */
+size_t i;
+ for (i = 0; i < gNumPtrs; i++) {
+ dvmHeapBitmapSetObjectBit(&hb1, gXorPtrs[i]);
+ }
+TRACE("walk 3\n");
+ ok = dvmHeapBitmapXorWalk(&hb1, &hb2, xorCallback, gCallbackArg);
+ assert(ok);
+
+ /* Set every third pointer in the other bitmap, and visit again.
+ */
+ for (i = 0; i < gNumPtrs; i += 3) {
+ dvmHeapBitmapSetObjectBit(&hb2, gXorPtrs[i]);
+ }
+TRACE("walk 4\n");
+ ok = dvmHeapBitmapXorWalk(&hb1, &hb2, xorCallback, gCallbackArg);
+ assert(ok);
+
+ /* Set every other pointer in the other bitmap, and visit again.
+ */
+ for (i = 0; i < gNumPtrs; i += 2) {
+ dvmHeapBitmapSetObjectBit(&hb2, gXorPtrs[i]);
+ }
+TRACE("walk 5\n");
+ ok = dvmHeapBitmapXorWalk(&hb1, &hb2, xorCallback, gCallbackArg);
+ assert(ok);
+
+ /* Walk just one bitmap.
+ */
+TRACE("walk 6\n");
+ ok = dvmHeapBitmapWalk(&hb2, xorCallback, gCallbackArg);
+ assert(ok);
+
+//xxx build an expect list for the callback
+//xxx test where max points to beginning, middle, and end of a word
+
+ /* Clean up.
+ */
+ dvmHeapBitmapDelete(&hb1);
+ dvmHeapBitmapDelete(&hb2);
+}
+
+void
+test_xor()
+{
+ run_xor(0, 1);
+ run_xor(100, 34);
+}
+
+int main(int argc, char *argv[])
+{
+ printf("test_init...\n");
+ test_init();
+
+ printf("test_bits...\n");
+ test_bits();
+
+ printf("test_clear...\n");
+ test_clear();
+
+ printf("test_modify...\n");
+ test_modify();
+
+ printf("test_xor...\n");
+ test_xor();
+
+ printf("done.\n");
+ return 0;
+}
diff --git a/vm/alloc/Verify.c b/vm/alloc/Verify.c
new file mode 100644
index 0000000..48168dd
--- /dev/null
+++ b/vm/alloc/Verify.c
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "alloc/HeapBitmap.h"
+#include "alloc/HeapSource.h"
+#include "alloc/Verify.h"
+#include "alloc/Visit.h"
+
+static void dumpReferencesVisitor(void *pObj, void *arg)
+{
+ Object *obj = *(Object **)pObj;
+ Object *lookingFor = *(Object **)arg;
+ if (lookingFor != NULL && lookingFor == obj) {
+ *(Object **)arg = NULL;
+ }
+}
+
+static void dumpReferencesCallback(void *ptr, void *arg)
+{
+ Object *obj = arg;
+ if (ptr == obj) {
+ LOGD("skipping %p == %p", ptr, obj);
+ return;
+ }
+ dvmVisitObject(dumpReferencesVisitor, ptr, &obj);
+ if (obj == NULL) {
+ LOGD("Found %p in the heap @ %p", arg, ptr);
+ dvmDumpObject(ptr);
+ }
+}
+
+static void dumpReferencesRootVisitor(void *ptr, void *arg)
+{
+ Object *obj = *(Object **)ptr;
+ Object *lookingFor = *(Object **)arg;
+ if (obj == lookingFor) {
+ LOGD("Found %p in a root @ %p", arg, ptr);
+ }
+}
+
+/*
+ * Searches the roots and heap for object references.
+ */
+static void dumpReferences(const Object *obj)
+{
+ HeapBitmap *bitmap = dvmHeapSourceGetLiveBits();
+ void *arg = (void *)obj;
+ dvmVisitRoots(dumpReferencesRootVisitor, arg);
+ dvmHeapBitmapWalk(bitmap, dumpReferencesCallback, arg);
+}
+
+/*
+ * Checks that the given reference points to a valid object.
+ */
+static void verifyReference(void *addr, void *arg)
+{
+ Object *obj;
+ bool isValid;
+
+ assert(addr != NULL);
+ obj = *(Object **)addr;
+ if (obj == NULL) {
+ isValid = true;
+ } else {
+ isValid = dvmIsValidObject(obj);
+ }
+ if (!isValid) {
+ Object **parent = arg;
+ if (*parent != NULL) {
+ LOGE("Verify of object %p failed", *parent);
+ dvmDumpObject(*parent);
+ *parent = NULL;
+ }
+ LOGE("Verify of reference %p @ %p failed", obj, addr);
+ dvmDumpObject(obj);
+ }
+}
+
+/*
+ * Verifies an object reference.
+ */
+void dvmVerifyObject(const Object *obj)
+{
+ Object *arg = (Object *)obj;
+ dvmVisitObject(verifyReference, (Object *)obj, &arg);
+ if (arg == NULL) {
+ dumpReferences(obj);
+ dvmAbort();
+ }
+}
+
+/*
+ * Helper function to call dvmVerifyObject from a bitmap walker.
+ */
+static void verifyBitmapCallback(void *ptr, void *arg)
+{
+ dvmVerifyObject(ptr);
+}
+
+/*
+ * Verifies the object references in a heap bitmap. Assumes the VM is
+ * suspended.
+ */
+void dvmVerifyBitmap(const HeapBitmap *bitmap)
+{
+ dvmHeapBitmapWalk(bitmap, verifyBitmapCallback, NULL);
+}
+
+/*
+ * Verifies references in the roots.
+ */
+void dvmVerifyRoots(void)
+{
+ dvmVisitRoots(verifyReference, NULL);
+}
diff --git a/vm/alloc/Verify.h b/vm/alloc/Verify.h
new file mode 100644
index 0000000..56d8958
--- /dev/null
+++ b/vm/alloc/Verify.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DALVIK_ALLOC_VERIFY
+#define _DALVIK_ALLOC_VERIFY
+
+/*
+ * Verifies an object reference.
+ */
+void dvmVerifyObject(const Object *obj);
+
+/*
+ * Verifies the object references in a heap bitmap. Assumes the VM is
+ * suspended.
+ */
+void dvmVerifyBitmap(const HeapBitmap *bitmap);
+
+/*
+ * Verifies the contents of various global roots.
+ */
+void dvmVerifyRoots(void);
+
+#endif /* _DALVIK_ALLOC_VERIFY */
diff --git a/vm/alloc/Visit.c b/vm/alloc/Visit.c
new file mode 100644
index 0000000..0af27da
--- /dev/null
+++ b/vm/alloc/Visit.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "alloc/clz.h"
+#include "alloc/HeapInternal.h"
+#include "alloc/Visit.h"
+#include "alloc/VisitInlines.h"
+
+/*
+ * Visits all of the reference locations in an object.
+ */
+void dvmVisitObject(Visitor *visitor, Object *obj, void *arg)
+{
+ assert(visitor != NULL);
+ assert(obj != NULL);
+ assert(obj->clazz != NULL);
+ visitObject(visitor, obj, arg);
+}
+
+/*
+ * Applies a verification function to all present values in the hash table.
+ */
+static void visitHashTable(Visitor *visitor, HashTable *table, void *arg)
+{
+ int i;
+
+ assert(visitor != NULL);
+ assert(table != NULL);
+ dvmHashTableLock(table);
+ for (i = 0; i < table->tableSize; ++i) {
+ HashEntry *entry = &table->pEntries[i];
+ if (entry->data != NULL && entry->data != HASH_TOMBSTONE) {
+ (*visitor)(&entry->data, arg);
+ }
+ }
+ dvmHashTableUnlock(table);
+}
+
+/*
+ * Visits all entries in the reference table.
+ */
+static void visitReferenceTable(Visitor *visitor, const ReferenceTable *table,
+ void *arg)
+{
+ Object **entry;
+
+ assert(visitor != NULL);
+ assert(table != NULL);
+ for (entry = table->table; entry < table->nextEntry; ++entry) {
+ assert(entry != NULL);
+ (*visitor)(entry, arg);
+ }
+}
+
+/*
+ * Visits a large heap reference table. These objects are list heads.
+ * As such, it is valid for table to be NULL.
+ */
+static void visitLargeHeapRefTable(Visitor *visitor, LargeHeapRefTable *table,
+ void *arg)
+{
+ assert(visitor != NULL);
+ for (; table != NULL; table = table->next) {
+ visitReferenceTable(visitor, &table->refs, arg);
+ }
+}
+
+/*
+ * Visits all stack slots. TODO: visit native methods.
+ */
+static void visitThreadStack(Visitor *visitor, Thread *thread, void *arg)
+{
+ const StackSaveArea *saveArea;
+ u4 *framePtr;
+
+ assert(visitor != NULL);
+ assert(thread != NULL);
+ framePtr = (u4 *)thread->curFrame;
+ for (; framePtr != NULL; framePtr = saveArea->prevFrame) {
+ Method *method;
+ saveArea = SAVEAREA_FROM_FP(framePtr);
+ method = (Method *)saveArea->method;
+ if (method != NULL && !dvmIsNativeMethod(method)) {
+ const RegisterMap* pMap = dvmGetExpandedRegisterMap(method);
+ const u1* regVector = NULL;
+ size_t i;
+
+ if (pMap != NULL) {
+ /* found map, get registers for this address */
+ int addr = saveArea->xtra.currentPc - method->insns;
+ regVector = dvmRegisterMapGetLine(pMap, addr);
+ }
+ if (regVector == NULL) {
+ /*
+ * Either there was no register map or there is no
+ * info for the current PC. Perform a conservative
+ * scan.
+ */
+ for (i = 0; i < method->registersSize; ++i) {
+ if (dvmIsValidObject((Object *)framePtr[i])) {
+ (*visitor)(&framePtr[i], arg);
+ }
+ }
+ } else {
+ /*
+ * Precise scan. v0 is at the lowest address on the
+ * interpreted stack, and is the first bit in the
+ * register vector, so we can walk through the
+ * register map and memory in the same direction.
+ *
+ * A '1' bit indicates a live reference.
+ */
+ u2 bits = 1 << 1;
+ for (i = 0; i < method->registersSize; ++i) {
+ bits >>= 1;
+ if (bits == 1) {
+ /* set bit 9 so we can tell when we're empty */
+ bits = *regVector++ | 0x0100;
+ }
+ if ((bits & 0x1) != 0) {
+ /*
+ * Register is marked as live, it's a valid root.
+ */
+ (*visitor)(&framePtr[i], arg);
+ }
+ }
+ dvmReleaseRegisterMapLine(pMap, regVector);
+ }
+ }
+ /*
+ * Don't fall into an infinite loop if things get corrupted.
+ */
+ assert((uintptr_t)saveArea->prevFrame > (uintptr_t)framePtr ||
+ saveArea->prevFrame == NULL);
+ }
+}
+
+/*
+ * Visits all roots associated with a thread.
+ */
+static void visitThread(Visitor *visitor, Thread *thread, void *arg)
+{
+ assert(visitor != NULL);
+ assert(thread != NULL);
+ (*visitor)(&thread->threadObj, arg);
+ (*visitor)(&thread->exception, arg);
+ visitReferenceTable(visitor, &thread->internalLocalRefTable, arg);
+ visitReferenceTable(visitor, &thread->jniLocalRefTable, arg);
+ if (thread->jniMonitorRefTable.table) {
+ visitReferenceTable(visitor, &thread->jniMonitorRefTable, arg);
+ }
+ visitThreadStack(visitor, thread, arg);
+}
+
+/*
+ * Visits all threads on the thread list.
+ */
+static void visitThreads(Visitor *visitor, void *arg)
+{
+ Thread *thread;
+
+ assert(visitor != NULL);
+ dvmLockThreadList(dvmThreadSelf());
+ thread = gDvm.threadList;
+ while (thread) {
+ visitThread(visitor, thread, arg);
+ thread = thread->next;
+ }
+ dvmUnlockThreadList();
+}
+
+/*
+ * Visits roots. TODO: visit all roots.
+ */
+void dvmVisitRoots(Visitor *visitor, void *arg)
+{
+ assert(visitor != NULL);
+ visitHashTable(visitor, gDvm.loadedClasses, arg);
+ visitHashTable(visitor, gDvm.dbgRegistry, arg);
+ visitHashTable(visitor, gDvm.internedStrings, arg);
+ visitHashTable(visitor, gDvm.literalStrings, arg);
+ visitReferenceTable(visitor, &gDvm.jniGlobalRefTable, arg);
+ visitReferenceTable(visitor, &gDvm.jniPinRefTable, arg);
+ visitLargeHeapRefTable(visitor, gDvm.gcHeap->referenceOperations, arg);
+ visitLargeHeapRefTable(visitor, gDvm.gcHeap->pendingFinalizationRefs, arg);
+ visitThreads(visitor, arg);
+ (*visitor)(&gDvm.outOfMemoryObj, arg);
+ (*visitor)(&gDvm.internalErrorObj, arg);
+ (*visitor)(&gDvm.noClassDefFoundErrorObj, arg);
+ /* TODO: visit cached global references. */
+}
diff --git a/vm/alloc/Visit.h b/vm/alloc/Visit.h
new file mode 100644
index 0000000..488c721
--- /dev/null
+++ b/vm/alloc/Visit.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DALVIK_ALLOC_VISIT
+#define _DALVIK_ALLOC_VISIT
+
+#include "Dalvik.h"
+
+/*
+ * Callback invoked with the address of a reference and a user
+ * supplied context argument.
+ */
+typedef void Visitor(void *addr, void *arg);
+
+/*
+ * Visits references in an object.
+ */
+void dvmVisitObject(Visitor *visitor, Object *obj, void *arg);
+
+/*
+ * Visits references in the root set.
+ */
+void dvmVisitRoots(Visitor *visitor, void *arg);
+
+#endif /* _DALVIK_ALLOC_VISIT */
diff --git a/vm/alloc/VisitInlines.h b/vm/alloc/VisitInlines.h
new file mode 100644
index 0000000..84a456a
--- /dev/null
+++ b/vm/alloc/VisitInlines.h
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DALVIK_ALLOC_VISITINLINES
+#define _DALVIK_ALLOC_VISITINLINES
+
+/*
+ * Visits the instance fields of a class or data object.
+ */
+static void visitFields(Visitor *visitor, Object *obj, void *arg)
+{
+ assert(visitor != NULL);
+ assert(obj != NULL);
+ assert(obj->clazz != NULL);
+ if (obj->clazz->refOffsets != CLASS_WALK_SUPER) {
+ size_t refOffsets = obj->clazz->refOffsets;
+ while (refOffsets != 0) {
+ size_t rshift = CLZ(refOffsets);
+ size_t offset = CLASS_OFFSET_FROM_CLZ(rshift);
+ Object **ref = BYTE_OFFSET(obj, offset);
+ (*visitor)(ref, arg);
+ refOffsets &= ~(CLASS_HIGH_BIT >> rshift);
+ }
+ } else {
+ ClassObject *clazz;
+ for (clazz = obj->clazz; clazz != NULL; clazz = clazz->super) {
+ InstField *field = clazz->ifields;
+ int i;
+ for (i = 0; i < clazz->ifieldRefCount; ++i, ++field) {
+ size_t offset = field->byteOffset;
+ Object **ref = BYTE_OFFSET(obj, offset);
+ (*visitor)(ref, arg);
+ }
+ }
+ }
+}
+
+/*
+ * Visits the static fields of a class object.
+ */
+static void visitStaticFields(Visitor *visitor, ClassObject *clazz,
+ void *arg)
+{
+ int i;
+
+ assert(visitor != NULL);
+ assert(clazz != NULL);
+ for (i = 0; i < clazz->sfieldCount; ++i) {
+ char ch = clazz->sfields[i].field.signature[0];
+ if (ch == '[' || ch == 'L') {
+ (*visitor)(&clazz->sfields[i].value.l, arg);
+ }
+ }
+}
+
+/*
+ * Visit the interfaces of a class object.
+ */
+static void visitInterfaces(Visitor *visitor, ClassObject *clazz,
+ void *arg)
+{
+ int i;
+
+ assert(visitor != NULL);
+ assert(clazz != NULL);
+ for (i = 0; i < clazz->interfaceCount; ++i) {
+ (*visitor)(&clazz->interfaces[i], arg);
+ }
+}
+
+/*
+ * Visits all the references stored in a class object instance.
+ */
+static void visitClassObject(Visitor *visitor, Object *obj, void *arg)
+{
+ ClassObject *classObj;
+ ClassStatus status;
+
+ assert(visitor != NULL);
+ assert(obj != NULL);
+ assert(obj->clazz != NULL);
+ assert(!strcmp(obj->clazz->descriptor, "Ljava/lang/Class;"));
+ classObj = (ClassObject *)obj;
+ (*visitor)(&obj->clazz, arg);
+ if (IS_CLASS_FLAG_SET(classObj, CLASS_ISARRAY)) {
+ (*visitor)(&classObj->elementClass, arg);
+ }
+ status = classObj->status;
+ if (status > CLASS_IDX) {
+ (*visitor)(&classObj->super, arg);
+ }
+ (*visitor)(&classObj->classLoader, arg);
+ visitFields(visitor, obj, arg);
+ visitStaticFields(visitor, classObj, arg);
+ if (status > CLASS_IDX) {
+ visitInterfaces(visitor, classObj, arg);
+ }
+}
+
+/*
+ * Visits the class object and, if the array is typed as an object
+ * array, all of the array elements.
+ */
+static void visitArrayObject(Visitor *visitor, Object *obj, void *arg)
+{
+ assert(visitor != NULL);
+ assert(obj != NULL);
+ assert(obj->clazz != NULL);
+ (*visitor)(&obj->clazz, arg);
+ if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISOBJECTARRAY)) {
+ ArrayObject *array = (ArrayObject *)obj;
+ Object **contents = (Object **)array->contents;
+ size_t i;
+ for (i = 0; i < array->length; ++i) {
+ (*visitor)(&contents[i], arg);
+ }
+ }
+}
+
+/*
+ * Visits the class object and reference typed instance fields of a
+ * data object.
+ */
+static void visitDataObject(Visitor *visitor, Object *obj, void *arg)
+{
+ assert(visitor != NULL);
+ assert(obj != NULL);
+ assert(obj->clazz != NULL);
+ (*visitor)(&obj->clazz, arg);
+ visitFields(visitor, obj, arg);
+}
+
+/*
+ * Like visitDataObject, but visits the hidden referent field that
+ * belongings to the subclasses of java.lang.Reference.
+ */
+static void visitReferenceObject(Visitor *visitor, Object *obj, void *arg)
+{
+ assert(visitor != NULL);
+ assert(obj != NULL);
+ assert(obj->clazz != NULL);
+ visitDataObject(visitor, obj, arg);
+ size_t offset = gDvm.offJavaLangRefReference_referent;
+ Object **ref = BYTE_OFFSET(obj, offset);
+ (*visitor)(ref, arg);
+}
+
+/*
+ * Visits all of the reference stored in an object.
+ */
+static void visitObject(Visitor *visitor, Object *obj, void *arg)
+{
+ assert(visitor != NULL);
+ assert(obj != NULL);
+ assert(obj->clazz != NULL);
+ if (obj->clazz == gDvm.classJavaLangClass) {
+ visitClassObject(visitor, obj, arg);
+ } else if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISARRAY)) {
+ visitArrayObject(visitor, obj, arg);
+ } else if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISREFERENCE)) {
+ visitReferenceObject(visitor, obj, arg);
+ } else {
+ visitDataObject(visitor, obj, arg);
+ }
+}
+
+#endif /* _DALVIK_ALLOC_VISITINLINES */
diff --git a/vm/alloc/WriteBarrier.h b/vm/alloc/WriteBarrier.h
new file mode 100644
index 0000000..dd8f129
--- /dev/null
+++ b/vm/alloc/WriteBarrier.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DALVIK_ALLOC_WRITEBARRIER
+#define _DALVIK_ALLOC_WRITEBARRIER
+
+/*
+ * Note writes to the heap. These functions must be called if a field
+ * of an Object in the heap changes, and before any GC safe-point. The
+ * call is not needed if NULL is stored in the field.
+ */
+
+/*
+ * The address within the Object has been written, and perhaps changed.
+ */
+INLINE void dvmWriteBarrierField(const Object *obj, void *addr)
+{
+ dvmMarkCard(obj);
+}
+
+/*
+ * All of the Object may have changed.
+ */
+INLINE void dvmWriteBarrierObject(const Object *obj)
+{
+ dvmMarkCard(obj);
+}
+
+/*
+ * Some or perhaps all of the array indexes in the Array, greater than
+ * or equal to start and strictly less than end, have been written,
+ * and perhaps changed.
+ */
+INLINE void dvmWriteBarrierArray(const ArrayObject *obj,
+ size_t start, size_t end)
+{
+ dvmMarkCard((Object *)obj);
+}
+
+#endif /* _DALVIK_ALLOC_WRITEBARRIER */
diff --git a/vm/alloc/clz.c b/vm/alloc/clz.c
new file mode 100644
index 0000000..3488975
--- /dev/null
+++ b/vm/alloc/clz.c
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "clz.h"
+
+/*
+ * Implementation of CLZ; intended to mimic gcc's __builtin_clz.
+ *
+ * Returns the number of leading zero bits, starting at the most
+ * significant bit position. If the argument is zero, the result is
+ * undefined.
+ *
+ * (For best results, this file should always be compiled for ARM, not THUMB.)
+ */
+int dvmClzImpl(unsigned int x)
+{
+#ifdef HAVE_BUILTIN_CLZ
+ /*
+ * This file was compiled with flags that allow it to use the built-in
+ * CLZ (e.g. ARM mode for ARMv5 or later).
+ */
+ return __builtin_clz(x);
+#else
+ /*
+ * Built-in version not available.
+ */
+ if (!x) return 32;
+ int e = 31;
+ if (x&0xFFFF0000) { e -=16; x >>=16; }
+ if (x&0x0000FF00) { e -= 8; x >>= 8; }
+ if (x&0x000000F0) { e -= 4; x >>= 4; }
+ if (x&0x0000000C) { e -= 2; x >>= 2; }
+ if (x&0x00000002) { e -= 1; }
+ return e;
+#endif
+}
diff --git a/vm/alloc/clz.h b/vm/alloc/clz.h
new file mode 100644
index 0000000..77fa6d4
--- /dev/null
+++ b/vm/alloc/clz.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Implementation of clz(), which returns the number of leading zero bits,
+ * starting at the most significant bit position. If the argument is zero,
+ * the result is undefined.
+ *
+ * On some platforms, gcc provides a __builtin_clz() function that uses
+ * an optimized implementation (e.g. the CLZ instruction on ARM).
+ *
+ * This gets a little tricky for ARM, because it's only available in ARMv5
+ * and above, and even on ARMv5 it's not available for THUMB code. So we
+ * need to tailor this for every source file.
+ */
+#ifndef _DALVIK_CLZ
+
+#if defined(__arm__) && !defined(__thumb__)
+# include <machine/cpu-features.h>
+# if defined(__ARM_HAVE_CLZ)
+# define CLZ(x) __builtin_clz(x)
+# define HAVE_BUILTIN_CLZ
+# endif
+#endif
+
+#ifndef HAVE_BUILTIN_CLZ
+# define CLZ(x) dvmClzImpl(x)
+int dvmClzImpl(unsigned int x);
+#endif
+
+#endif // _DALVIK_CLZ
diff --git a/vm/analysis/CodeVerify.c b/vm/analysis/CodeVerify.c
new file mode 100644
index 0000000..a7d634e
--- /dev/null
+++ b/vm/analysis/CodeVerify.c
@@ -0,0 +1,5782 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik bytecode structural verifier. The only public entry point
+ * (except for a few shared utility functions) is dvmVerifyCodeFlow().
+ *
+ * TODO: might benefit from a signature-->class lookup cache. Could avoid
+ * some string-peeling and wouldn't need to compute hashes.
+ *
+ * TODO: we do too much stuff in here that could be done in the static
+ * verification pass. It's convenient, because we have all of the
+ * necessary information, but it's more efficient to do it over in
+ * DexVerify.c because in here we may have to process instructions
+ * multiple times.
+ */
+#include "Dalvik.h"
+#include "analysis/CodeVerify.h"
+#include "analysis/Optimize.h"
+#include "analysis/RegisterMap.h"
+#include "libdex/DexCatch.h"
+#include "libdex/InstrUtils.h"
+
+#include <stddef.h>
+
+
+/*
+ * We don't need to store the register data for many instructions, because
+ * we either only need it at branch points (for verification) or GC points
+ * and branches (for verification + type-precise register analysis).
+ */
+typedef enum RegisterTrackingMode {
+ kTrackRegsBranches,
+ kTrackRegsGcPoints,
+ kTrackRegsAll
+} RegisterTrackingMode;
+
+/*
+ * Set this to enable dead code scanning. This is not required, but it's
+ * very useful when testing changes to the verifier (to make sure we're not
+ * skipping over stuff) and for checking the optimized output from "dx".
+ * The only reason not to do it is that it slightly increases the time
+ * required to perform verification.
+ */
+#define DEAD_CODE_SCAN true
+
+static bool gDebugVerbose = false; // TODO: remove this
+
+#if 0
+int gDvm__totalInstr = 0;
+int gDvm__gcInstr = 0;
+int gDvm__gcData = 0;
+int gDvm__gcSimpleData = 0;
+#endif
+
+/*
+ * Selectively enable verbose debug logging -- use this to activate
+ * dumpRegTypes() calls for all instructions in the specified method.
+ */
+static inline bool doVerboseLogging(const Method* meth) {
+ return false; /* COMMENT OUT to enable verbose debugging */
+
+ const char* cd = "Landroid/net/http/Request;";
+ const char* mn = "readResponse";
+ const char* sg = "(Landroid/net/http/AndroidHttpClientConnection;)V";
+ return (strcmp(meth->clazz->descriptor, cd) == 0 &&
+ dvmCompareNameDescriptorAndMethod(mn, sg, meth) == 0);
+}
+
+#define SHOW_REG_DETAILS (0 /*| DRT_SHOW_REF_TYPES | DRT_SHOW_LOCALS*/)
+
+/*
+ * We need an extra "pseudo register" to hold the return type briefly. It
+ * can be category 1 or 2, so we need two slots.
+ */
+#define kExtraRegs 2
+#define RESULT_REGISTER(_insnRegCount) (_insnRegCount)
+
+/*
+ * Big fat collection of registers.
+ */
+typedef struct RegisterTable {
+ /*
+ * Array of RegType arrays, one per address in the method. We only
+ * set the pointers for certain addresses, based on what we're trying
+ * to accomplish.
+ */
+ RegType** addrRegs;
+
+ /*
+ * Number of registers we track for each instruction. This is equal
+ * to the method's declared "registersSize" plus kExtraRegs.
+ */
+ int insnRegCountPlus;
+
+ /*
+ * A single large alloc, with all of the storage needed for addrRegs.
+ */
+ RegType* regAlloc;
+} RegisterTable;
+
+
+/* fwd */
+#ifndef NDEBUG
+static void checkMergeTab(void);
+#endif
+static bool isInitMethod(const Method* meth);
+static RegType getInvocationThis(const RegType* insnRegs,\
+ const int insnRegCount, const DecodedInstruction* pDecInsn,
+ VerifyError* pFailure);
+static void verifyRegisterType(const RegType* insnRegs, const int insnRegCount,\
+ u4 vsrc, RegType checkType, VerifyError* pFailure);
+static bool doCodeVerification(const Method* meth, InsnFlags* insnFlags,\
+ RegisterTable* regTable, UninitInstanceMap* uninitMap);
+static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags,\
+ RegisterTable* regTable, RegType* workRegs, int insnIdx,
+ UninitInstanceMap* uninitMap, int* pStartGuess);
+static ClassObject* findCommonSuperclass(ClassObject* c1, ClassObject* c2);
+static void dumpRegTypes(const Method* meth, const InsnFlags* insnFlags,\
+ const RegType* addrRegs, int addr, const char* addrName,
+ const UninitInstanceMap* uninitMap, int displayFlags);
+
+/* bit values for dumpRegTypes() "displayFlags" */
+enum {
+ DRT_SIMPLE = 0,
+ DRT_SHOW_REF_TYPES = 0x01,
+ DRT_SHOW_LOCALS = 0x02,
+};
+
+
+/*
+ * ===========================================================================
+ * RegType and UninitInstanceMap utility functions
+ * ===========================================================================
+ */
+
+#define __ kRegTypeUnknown
+#define _U kRegTypeUninit
+#define _X kRegTypeConflict
+#define _F kRegTypeFloat
+#define _0 kRegTypeZero
+#define _1 kRegTypeOne
+#define _Z kRegTypeBoolean
+#define _b kRegTypePosByte
+#define _B kRegTypeByte
+#define _s kRegTypePosShort
+#define _S kRegTypeShort
+#define _C kRegTypeChar
+#define _I kRegTypeInteger
+#define _J kRegTypeLongLo
+#define _j kRegTypeLongHi
+#define _D kRegTypeDoubleLo
+#define _d kRegTypeDoubleHi
+
+/*
+ * Merge result table for primitive values. The table is symmetric along
+ * the diagonal.
+ *
+ * Note that 32-bit int/float do not merge into 64-bit long/double. This
+ * is a register merge, not a widening conversion. Only the "implicit"
+ * widening within a category, e.g. byte to short, is allowed.
+ *
+ * Because Dalvik does not draw a distinction between int and float, we
+ * have to allow free exchange between 32-bit int/float and 64-bit
+ * long/double.
+ *
+ * Note that Uninit+Uninit=Uninit. This holds true because we only
+ * use this when the RegType value is exactly equal to kRegTypeUninit, which
+ * can only happen for the zeroeth entry in the table.
+ *
+ * "Unknown" never merges with anything known. The only time a register
+ * transitions from "unknown" to "known" is when we're executing code
+ * for the first time, and we handle that with a simple copy.
+ */
+const char gDvmMergeTab[kRegTypeMAX][kRegTypeMAX] =
+{
+ /* chk: _ U X F 0 1 Z b B s S C I J j D d */
+ { /*_*/ __,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X },
+ { /*U*/ _X,_U,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X },
+ { /*X*/ _X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X },
+ { /*F*/ _X,_X,_X,_F,_F,_F,_F,_F,_F,_F,_F,_F,_F,_X,_X,_X,_X },
+ { /*0*/ _X,_X,_X,_F,_0,_Z,_Z,_b,_B,_s,_S,_C,_I,_X,_X,_X,_X },
+ { /*1*/ _X,_X,_X,_F,_Z,_1,_Z,_b,_B,_s,_S,_C,_I,_X,_X,_X,_X },
+ { /*Z*/ _X,_X,_X,_F,_Z,_Z,_Z,_b,_B,_s,_S,_C,_I,_X,_X,_X,_X },
+ { /*b*/ _X,_X,_X,_F,_b,_b,_b,_b,_B,_s,_S,_C,_I,_X,_X,_X,_X },
+ { /*B*/ _X,_X,_X,_F,_B,_B,_B,_B,_B,_S,_S,_I,_I,_X,_X,_X,_X },
+ { /*s*/ _X,_X,_X,_F,_s,_s,_s,_s,_S,_s,_S,_C,_I,_X,_X,_X,_X },
+ { /*S*/ _X,_X,_X,_F,_S,_S,_S,_S,_S,_S,_S,_I,_I,_X,_X,_X,_X },
+ { /*C*/ _X,_X,_X,_F,_C,_C,_C,_C,_I,_C,_I,_C,_I,_X,_X,_X,_X },
+ { /*I*/ _X,_X,_X,_F,_I,_I,_I,_I,_I,_I,_I,_I,_I,_X,_X,_X,_X },
+ { /*J*/ _X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_J,_X,_J,_X },
+ { /*j*/ _X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_j,_X,_j },
+ { /*D*/ _X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_J,_X,_D,_X },
+ { /*d*/ _X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_j,_X,_d },
+};
+
+#undef __
+#undef _U
+#undef _X
+#undef _F
+#undef _0
+#undef _1
+#undef _Z
+#undef _b
+#undef _B
+#undef _s
+#undef _S
+#undef _C
+#undef _I
+#undef _J
+#undef _j
+#undef _D
+#undef _d
+
+#ifndef NDEBUG
+/*
+ * Verify symmetry in the conversion table.
+ */
+static void checkMergeTab(void)
+{
+ int i, j;
+
+ for (i = 0; i < kRegTypeMAX; i++) {
+ for (j = i; j < kRegTypeMAX; j++) {
+ if (gDvmMergeTab[i][j] != gDvmMergeTab[j][i]) {
+ LOGE("Symmetry violation: %d,%d vs %d,%d\n", i, j, j, i);
+ dvmAbort();
+ }
+ }
+ }
+}
+#endif
+
+/*
+ * Determine whether we can convert "srcType" to "checkType", where
+ * "checkType" is one of the category-1 non-reference types.
+ *
+ * 32-bit int and float are interchangeable.
+ */
+static bool canConvertTo1nr(RegType srcType, RegType checkType)
+{
+ static const char convTab
+ [kRegType1nrEND-kRegType1nrSTART+1][kRegType1nrEND-kRegType1nrSTART+1] =
+ {
+ /* chk: F 0 1 Z b B s S C I */
+ { /*F*/ 1, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
+ { /*0*/ 1, 1, 0, 1, 1, 1, 1, 1, 1, 1 },
+ { /*1*/ 1, 0, 1, 1, 1, 1, 1, 1, 1, 1 },
+ { /*Z*/ 1, 0, 0, 1, 1, 1, 1, 1, 1, 1 },
+ { /*b*/ 1, 0, 0, 0, 1, 1, 1, 1, 1, 1 },
+ { /*B*/ 1, 0, 0, 0, 0, 1, 0, 1, 0, 1 },
+ { /*s*/ 1, 0, 0, 0, 0, 0, 1, 1, 1, 1 },
+ { /*S*/ 1, 0, 0, 0, 0, 0, 0, 1, 0, 1 },
+ { /*C*/ 1, 0, 0, 0, 0, 0, 0, 0, 1, 1 },
+ { /*I*/ 1, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
+ };
+
+ assert(checkType >= kRegType1nrSTART && checkType <= kRegType1nrEND);
+#if 0
+ if (checkType < kRegType1nrSTART || checkType > kRegType1nrEND) {
+ LOG_VFY("Unexpected checkType %d (srcType=%d)\n", checkType, srcType);
+ assert(false);
+ return false;
+ }
+#endif
+
+ //printf("convTab[%d][%d] = %d\n", srcType, checkType,
+ // convTab[srcType-kRegType1nrSTART][checkType-kRegType1nrSTART]);
+ if (srcType >= kRegType1nrSTART && srcType <= kRegType1nrEND)
+ return (bool) convTab[srcType-kRegType1nrSTART][checkType-kRegType1nrSTART];
+
+ return false;
+}
+
+/*
+ * Determine whether the types are compatible. In Dalvik, 64-bit doubles
+ * and longs are interchangeable.
+ */
+static bool canConvertTo2(RegType srcType, RegType checkType)
+{
+ return ((srcType == kRegTypeLongLo || srcType == kRegTypeDoubleLo) &&
+ (checkType == kRegTypeLongLo || checkType == kRegTypeDoubleLo));
+}
+
+/*
+ * Determine whether or not "instrType" and "targetType" are compatible,
+ * for purposes of getting or setting a value in a field or array. The
+ * idea is that an instruction with a category 1nr type (say, aget-short
+ * or iput-boolean) is accessing a static field, instance field, or array
+ * entry, and we want to make sure sure that the operation is legal.
+ *
+ * At a minimum, source and destination must have the same width. We
+ * further refine this to assert that "short" and "char" are not
+ * compatible, because the sign-extension is different on the "get"
+ * operations. As usual, "float" and "int" are interoperable.
+ *
+ * We're not considering the actual contents of the register, so we'll
+ * never get "pseudo-types" like kRegTypeZero or kRegTypePosShort. We
+ * could get kRegTypeUnknown in "targetType" if a field or array class
+ * lookup failed. Category 2 types and references are checked elsewhere.
+ */
+static bool checkFieldArrayStore1nr(RegType instrType, RegType targetType)
+{
+ if (instrType == targetType)
+ return true; /* quick positive; most common case */
+
+ if ((instrType == kRegTypeInteger && targetType == kRegTypeFloat) ||
+ (instrType == kRegTypeFloat && targetType == kRegTypeInteger))
+ {
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Convert a VM PrimitiveType enum value to the equivalent RegType value.
+ */
+static RegType primitiveTypeToRegType(PrimitiveType primType)
+{
+ static const struct {
+ RegType regType; /* type equivalent */
+ PrimitiveType primType; /* verification */
+ } convTab[] = {
+ /* must match order of enum in Object.h */
+ { kRegTypeBoolean, PRIM_BOOLEAN },
+ { kRegTypeChar, PRIM_CHAR },
+ { kRegTypeFloat, PRIM_FLOAT },
+ { kRegTypeDoubleLo, PRIM_DOUBLE },
+ { kRegTypeByte, PRIM_BYTE },
+ { kRegTypeShort, PRIM_SHORT },
+ { kRegTypeInteger, PRIM_INT },
+ { kRegTypeLongLo, PRIM_LONG },
+ // PRIM_VOID
+ };
+
+ if (primType < 0 || primType > (int) (sizeof(convTab) / sizeof(convTab[0])))
+ {
+ assert(false);
+ return kRegTypeUnknown;
+ }
+
+ assert(convTab[primType].primType == primType);
+ return convTab[primType].regType;
+}
+
+/*
+ * Create a new uninitialized instance map.
+ *
+ * The map is allocated and populated with address entries. The addresses
+ * appear in ascending order to allow binary searching.
+ *
+ * Very few methods have 10 or more new-instance instructions; the
+ * majority have 0 or 1. Occasionally a static initializer will have 200+.
+ */
+UninitInstanceMap* dvmCreateUninitInstanceMap(const Method* meth,
+ const InsnFlags* insnFlags, int newInstanceCount)
+{
+ const int insnsSize = dvmGetMethodInsnsSize(meth);
+ const u2* insns = meth->insns;
+ UninitInstanceMap* uninitMap;
+ bool isInit = false;
+ int idx, addr;
+
+ if (isInitMethod(meth)) {
+ newInstanceCount++;
+ isInit = true;
+ }
+
+ /*
+ * Allocate the header and map as a single unit.
+ *
+ * TODO: consider having a static instance so we can avoid allocations.
+ * I don't think the verifier is guaranteed to be single-threaded when
+ * running in the VM (rather than dexopt), so that must be taken into
+ * account.
+ */
+ int size = offsetof(UninitInstanceMap, map) +
+ newInstanceCount * sizeof(uninitMap->map[0]);
+ uninitMap = calloc(1, size);
+ if (uninitMap == NULL)
+ return NULL;
+ uninitMap->numEntries = newInstanceCount;
+
+ idx = 0;
+ if (isInit) {
+ uninitMap->map[idx++].addr = kUninitThisArgAddr;
+ }
+
+ /*
+ * Run through and find the new-instance instructions.
+ */
+ for (addr = 0; addr < insnsSize; /**/) {
+ int width = dvmInsnGetWidth(insnFlags, addr);
+
+ if ((*insns & 0xff) == OP_NEW_INSTANCE)
+ uninitMap->map[idx++].addr = addr;
+
+ addr += width;
+ insns += width;
+ }
+
+ assert(idx == newInstanceCount);
+ return uninitMap;
+}
+
+/*
+ * Free the map.
+ */
+void dvmFreeUninitInstanceMap(UninitInstanceMap* uninitMap)
+{
+ free(uninitMap);
+}
+
+/*
+ * Set the class object associated with the instruction at "addr".
+ *
+ * Returns the map slot index, or -1 if the address isn't listed in the map
+ * (shouldn't happen) or if a class is already associated with the address
+ * (bad bytecode).
+ *
+ * Entries, once set, do not change -- a given address can only allocate
+ * one type of object.
+ */
+int dvmSetUninitInstance(UninitInstanceMap* uninitMap, int addr,
+ ClassObject* clazz)
+{
+ int idx;
+
+ assert(clazz != NULL);
+
+ /* TODO: binary search when numEntries > 8 */
+ for (idx = uninitMap->numEntries - 1; idx >= 0; idx--) {
+ if (uninitMap->map[idx].addr == addr) {
+ if (uninitMap->map[idx].clazz != NULL &&
+ uninitMap->map[idx].clazz != clazz)
+ {
+ LOG_VFY("VFY: addr %d already set to %p, not setting to %p\n",
+ addr, uninitMap->map[idx].clazz, clazz);
+ return -1; // already set to something else??
+ }
+ uninitMap->map[idx].clazz = clazz;
+ return idx;
+ }
+ }
+
+ LOG_VFY("VFY: addr %d not found in uninit map\n", addr);
+ assert(false); // shouldn't happen
+ return -1;
+}
+
+/*
+ * Get the class object at the specified index.
+ */
+ClassObject* dvmGetUninitInstance(const UninitInstanceMap* uninitMap, int idx)
+{
+ assert(idx >= 0 && idx < uninitMap->numEntries);
+ return uninitMap->map[idx].clazz;
+}
+
+/* determine if "type" is actually an object reference (init/uninit/zero) */
+static inline bool regTypeIsReference(RegType type) {
+ return (type > kRegTypeMAX || type == kRegTypeUninit ||
+ type == kRegTypeZero);
+}
+
+/* determine if "type" is an uninitialized object reference */
+static inline bool regTypeIsUninitReference(RegType type) {
+ return ((type & kRegTypeUninitMask) == kRegTypeUninit);
+}
+
+/* convert the initialized reference "type" to a ClassObject pointer */
+/* (does not expect uninit ref types or "zero") */
+static ClassObject* regTypeInitializedReferenceToClass(RegType type)
+{
+ assert(regTypeIsReference(type) && type != kRegTypeZero);
+ if ((type & 0x01) == 0) {
+ return (ClassObject*) type;
+ } else {
+ //LOG_VFY("VFY: attempted to use uninitialized reference\n");
+ return NULL;
+ }
+}
+
+/* extract the index into the uninitialized instance map table */
+static inline int regTypeToUninitIndex(RegType type) {
+ assert(regTypeIsUninitReference(type));
+ return (type & ~kRegTypeUninitMask) >> kRegTypeUninitShift;
+}
+
+/* convert the reference "type" to a ClassObject pointer */
+static ClassObject* regTypeReferenceToClass(RegType type,
+ const UninitInstanceMap* uninitMap)
+{
+ assert(regTypeIsReference(type) && type != kRegTypeZero);
+ if (regTypeIsUninitReference(type)) {
+ assert(uninitMap != NULL);
+ return dvmGetUninitInstance(uninitMap, regTypeToUninitIndex(type));
+ } else {
+ return (ClassObject*) type;
+ }
+}
+
+/* convert the ClassObject pointer to an (initialized) register type */
+static inline RegType regTypeFromClass(ClassObject* clazz) {
+ return (u4) clazz;
+}
+
+/* return the RegType for the uninitialized reference in slot "uidx" */
+static RegType regTypeFromUninitIndex(int uidx) {
+ return (u4) (kRegTypeUninit | (uidx << kRegTypeUninitShift));
+}
+
+
+/*
+ * ===========================================================================
+ * Signature operations
+ * ===========================================================================
+ */
+
+/*
+ * Is this method a constructor?
+ */
+static bool isInitMethod(const Method* meth)
+{
+ return (*meth->name == '<' && strcmp(meth->name+1, "init>") == 0);
+}
+
+/*
+ * Is this method a class initializer?
+ */
+#if 0
+static bool isClassInitMethod(const Method* meth)
+{
+ return (*meth->name == '<' && strcmp(meth->name+1, "clinit>") == 0);
+}
+#endif
+
+/*
+ * Look up a class reference given as a simple string descriptor.
+ *
+ * If we can't find it, return a generic substitute when possible.
+ */
+static ClassObject* lookupClassByDescriptor(const Method* meth,
+ const char* pDescriptor, VerifyError* pFailure)
+{
+ /*
+ * The javac compiler occasionally puts references to nonexistent
+ * classes in signatures. For example, if you have a non-static
+ * inner class with no constructor, the compiler provides
+ * a private <init> for you. Constructing the class
+ * requires <init>(parent), but the outer class can't call
+ * that because the method is private. So the compiler
+ * generates a package-scope <init>(parent,bogus) method that
+ * just calls the regular <init> (the "bogus" part being necessary
+ * to distinguish the signature of the synthetic method).
+ * Treating the bogus class as an instance of java.lang.Object
+ * allows the verifier to process the class successfully.
+ */
+
+ //LOGI("Looking up '%s'\n", typeStr);
+ ClassObject* clazz;
+ clazz = dvmFindClassNoInit(pDescriptor, meth->clazz->classLoader);
+ if (clazz == NULL) {
+ dvmClearOptException(dvmThreadSelf());
+ if (strchr(pDescriptor, '$') != NULL) {
+ LOGV("VFY: unable to find class referenced in signature (%s)\n",
+ pDescriptor);
+ } else {
+ LOG_VFY("VFY: unable to find class referenced in signature (%s)\n",
+ pDescriptor);
+ }
+
+ if (pDescriptor[0] == '[') {
+ /* We are looking at an array descriptor. */
+
+ /*
+ * There should never be a problem loading primitive arrays.
+ */
+ if (pDescriptor[1] != 'L' && pDescriptor[1] != '[') {
+ LOG_VFY("VFY: invalid char in signature in '%s'\n",
+ pDescriptor);
+ *pFailure = VERIFY_ERROR_GENERIC;
+ }
+
+ /*
+ * Try to continue with base array type. This will let
+ * us pass basic stuff (e.g. get array len) that wouldn't
+ * fly with an Object. This is NOT correct if the
+ * missing type is a primitive array, but we should never
+ * have a problem loading those. (I'm not convinced this
+ * is correct or even useful. Just use Object here?)
+ */
+ clazz = dvmFindClassNoInit("[Ljava/lang/Object;",
+ meth->clazz->classLoader);
+ } else if (pDescriptor[0] == 'L') {
+ /*
+ * We are looking at a non-array reference descriptor;
+ * try to continue with base reference type.
+ */
+ clazz = gDvm.classJavaLangObject;
+ } else {
+ /* We are looking at a primitive type. */
+ LOG_VFY("VFY: invalid char in signature in '%s'\n", pDescriptor);
+ *pFailure = VERIFY_ERROR_GENERIC;
+ }
+
+ if (clazz == NULL) {
+ *pFailure = VERIFY_ERROR_GENERIC;
+ }
+ }
+
+ if (dvmIsPrimitiveClass(clazz)) {
+ LOG_VFY("VFY: invalid use of primitive type '%s'\n", pDescriptor);
+ *pFailure = VERIFY_ERROR_GENERIC;
+ clazz = NULL;
+ }
+
+ return clazz;
+}
+
+/*
+ * Look up a class reference in a signature. Could be an arg or the
+ * return value.
+ *
+ * Advances "*pSig" to the last character in the signature (that is, to
+ * the ';').
+ *
+ * NOTE: this is also expected to verify the signature.
+ */
+static ClassObject* lookupSignatureClass(const Method* meth, const char** pSig,
+ VerifyError* pFailure)
+{
+ const char* sig = *pSig;
+ const char* endp = sig;
+
+ assert(sig != NULL && *sig == 'L');
+
+ while (*++endp != ';' && *endp != '\0')
+ ;
+ if (*endp != ';') {
+ LOG_VFY("VFY: bad signature component '%s' (missing ';')\n", sig);
+ *pFailure = VERIFY_ERROR_GENERIC;
+ return NULL;
+ }
+
+ endp++; /* Advance past the ';'. */
+ int typeLen = endp - sig;
+ char typeStr[typeLen+1]; /* +1 for the '\0' */
+ memcpy(typeStr, sig, typeLen);
+ typeStr[typeLen] = '\0';
+
+ *pSig = endp - 1; /* - 1 so that *pSig points at, not past, the ';' */
+
+ return lookupClassByDescriptor(meth, typeStr, pFailure);
+}
+
+/*
+ * Look up an array class reference in a signature. Could be an arg or the
+ * return value.
+ *
+ * Advances "*pSig" to the last character in the signature.
+ *
+ * NOTE: this is also expected to verify the signature.
+ */
+static ClassObject* lookupSignatureArrayClass(const Method* meth,
+ const char** pSig, VerifyError* pFailure)
+{
+ const char* sig = *pSig;
+ const char* endp = sig;
+
+ assert(sig != NULL && *sig == '[');
+
+ /* find the end */
+ while (*++endp == '[' && *endp != '\0')
+ ;
+
+ if (*endp == 'L') {
+ while (*++endp != ';' && *endp != '\0')
+ ;
+ if (*endp != ';') {
+ LOG_VFY("VFY: bad signature component '%s' (missing ';')\n", sig);
+ *pFailure = VERIFY_ERROR_GENERIC;
+ return NULL;
+ }
+ }
+
+ int typeLen = endp - sig +1;
+ char typeStr[typeLen+1];
+ memcpy(typeStr, sig, typeLen);
+ typeStr[typeLen] = '\0';
+
+ *pSig = endp;
+
+ return lookupClassByDescriptor(meth, typeStr, pFailure);
+}
+
+/*
+ * Set the register types for the first instruction in the method based on
+ * the method signature.
+ *
+ * This has the side-effect of validating the signature.
+ *
+ * Returns "true" on success.
+ */
+static bool setTypesFromSignature(const Method* meth, RegType* regTypes,
+ UninitInstanceMap* uninitMap)
+{
+ DexParameterIterator iterator;
+ int actualArgs, expectedArgs, argStart;
+ VerifyError failure = VERIFY_ERROR_NONE;
+
+ dexParameterIteratorInit(&iterator, &meth->prototype);
+ argStart = meth->registersSize - meth->insSize;
+ expectedArgs = meth->insSize; /* long/double count as two */
+ actualArgs = 0;
+
+ assert(argStart >= 0); /* should have been verified earlier */
+
+ /*
+ * Include the "this" pointer.
+ */
+ if (!dvmIsStaticMethod(meth)) {
+ /*
+ * If this is a constructor for a class other than java.lang.Object,
+ * mark the first ("this") argument as uninitialized. This restricts
+ * field access until the superclass constructor is called.
+ */
+ if (isInitMethod(meth) && meth->clazz != gDvm.classJavaLangObject) {
+ int uidx = dvmSetUninitInstance(uninitMap, kUninitThisArgAddr,
+ meth->clazz);
+ assert(uidx == 0);
+ regTypes[argStart + actualArgs] = regTypeFromUninitIndex(uidx);
+ } else {
+ regTypes[argStart + actualArgs] = regTypeFromClass(meth->clazz);
+ }
+ actualArgs++;
+ }
+
+ for (;;) {
+ const char* descriptor = dexParameterIteratorNextDescriptor(&iterator);
+
+ if (descriptor == NULL) {
+ break;
+ }
+
+ if (actualArgs >= expectedArgs) {
+ LOG_VFY("VFY: expected %d args, found more (%s)\n",
+ expectedArgs, descriptor);
+ goto bad_sig;
+ }
+
+ switch (*descriptor) {
+ case 'L':
+ case '[':
+ /*
+ * We assume that reference arguments are initialized. The
+ * only way it could be otherwise (assuming the caller was
+ * verified) is if the current method is <init>, but in that
+ * case it's effectively considered initialized the instant
+ * we reach here (in the sense that we can return without
+ * doing anything or call virtual methods).
+ */
+ {
+ ClassObject* clazz =
+ lookupClassByDescriptor(meth, descriptor, &failure);
+ if (!VERIFY_OK(failure))
+ goto bad_sig;
+ regTypes[argStart + actualArgs] = regTypeFromClass(clazz);
+ }
+ actualArgs++;
+ break;
+ case 'Z':
+ regTypes[argStart + actualArgs] = kRegTypeBoolean;
+ actualArgs++;
+ break;
+ case 'C':
+ regTypes[argStart + actualArgs] = kRegTypeChar;
+ actualArgs++;
+ break;
+ case 'B':
+ regTypes[argStart + actualArgs] = kRegTypeByte;
+ actualArgs++;
+ break;
+ case 'I':
+ regTypes[argStart + actualArgs] = kRegTypeInteger;
+ actualArgs++;
+ break;
+ case 'S':
+ regTypes[argStart + actualArgs] = kRegTypeShort;
+ actualArgs++;
+ break;
+ case 'F':
+ regTypes[argStart + actualArgs] = kRegTypeFloat;
+ actualArgs++;
+ break;
+ case 'D':
+ regTypes[argStart + actualArgs] = kRegTypeDoubleLo;
+ regTypes[argStart + actualArgs +1] = kRegTypeDoubleHi;
+ actualArgs += 2;
+ break;
+ case 'J':
+ regTypes[argStart + actualArgs] = kRegTypeLongLo;
+ regTypes[argStart + actualArgs +1] = kRegTypeLongHi;
+ actualArgs += 2;
+ break;
+ default:
+ LOG_VFY("VFY: unexpected signature type char '%c'\n", *descriptor);
+ goto bad_sig;
+ }
+ }
+
+ if (actualArgs != expectedArgs) {
+ LOG_VFY("VFY: expected %d args, found %d\n", expectedArgs, actualArgs);
+ goto bad_sig;
+ }
+
+ const char* descriptor = dexProtoGetReturnType(&meth->prototype);
+
+ /*
+ * Validate return type. We don't do the type lookup; just want to make
+ * sure that it has the right format. Only major difference from the
+ * method argument format is that 'V' is supported.
+ */
+ switch (*descriptor) {
+ case 'I':
+ case 'C':
+ case 'S':
+ case 'B':
+ case 'Z':
+ case 'V':
+ case 'F':
+ case 'D':
+ case 'J':
+ if (*(descriptor+1) != '\0')
+ goto bad_sig;
+ break;
+ case '[':
+ /* single/multi, object/primitive */
+ while (*++descriptor == '[')
+ ;
+ if (*descriptor == 'L') {
+ while (*++descriptor != ';' && *descriptor != '\0')
+ ;
+ if (*descriptor != ';')
+ goto bad_sig;
+ } else {
+ if (*(descriptor+1) != '\0')
+ goto bad_sig;
+ }
+ break;
+ case 'L':
+ /* could be more thorough here, but shouldn't be required */
+ while (*++descriptor != ';' && *descriptor != '\0')
+ ;
+ if (*descriptor != ';')
+ goto bad_sig;
+ break;
+ default:
+ goto bad_sig;
+ }
+
+ return true;
+
+//fail:
+// LOG_VFY_METH(meth, "VFY: bad sig\n");
+// return false;
+
+bad_sig:
+ {
+ char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+ LOG_VFY("VFY: bad signature '%s' for %s.%s\n",
+ desc, meth->clazz->descriptor, meth->name);
+ free(desc);
+ }
+ return false;
+}
+
+/*
+ * Return the register type for the method. We can't just use the
+ * already-computed DalvikJniReturnType, because if it's a reference type
+ * we need to do the class lookup.
+ *
+ * Returned references are assumed to be initialized.
+ *
+ * Returns kRegTypeUnknown for "void".
+ */
+static RegType getMethodReturnType(const Method* meth)
+{
+ RegType type;
+ const char* descriptor = dexProtoGetReturnType(&meth->prototype);
+
+ switch (*descriptor) {
+ case 'I':
+ type = kRegTypeInteger;
+ break;
+ case 'C':
+ type = kRegTypeChar;
+ break;
+ case 'S':
+ type = kRegTypeShort;
+ break;
+ case 'B':
+ type = kRegTypeByte;
+ break;
+ case 'Z':
+ type = kRegTypeBoolean;
+ break;
+ case 'V':
+ type = kRegTypeUnknown;
+ break;
+ case 'F':
+ type = kRegTypeFloat;
+ break;
+ case 'D':
+ type = kRegTypeDoubleLo;
+ break;
+ case 'J':
+ type = kRegTypeLongLo;
+ break;
+ case 'L':
+ case '[':
+ {
+ VerifyError failure = VERIFY_ERROR_NONE;
+ ClassObject* clazz =
+ lookupClassByDescriptor(meth, descriptor, &failure);
+ assert(VERIFY_OK(failure));
+ type = regTypeFromClass(clazz);
+ }
+ break;
+ default:
+ /* we verified signature return type earlier, so this is impossible */
+ assert(false);
+ type = kRegTypeConflict;
+ break;
+ }
+
+ return type;
+}
+
+/*
+ * Convert a single-character signature value (i.e. a primitive type) to
+ * the corresponding RegType. This is intended for access to object fields
+ * holding primitive types.
+ *
+ * Returns kRegTypeUnknown for objects, arrays, and void.
+ */
+static RegType primSigCharToRegType(char sigChar)
+{
+ RegType type;
+
+ switch (sigChar) {
+ case 'I':
+ type = kRegTypeInteger;
+ break;
+ case 'C':
+ type = kRegTypeChar;
+ break;
+ case 'S':
+ type = kRegTypeShort;
+ break;
+ case 'B':
+ type = kRegTypeByte;
+ break;
+ case 'Z':
+ type = kRegTypeBoolean;
+ break;
+ case 'F':
+ type = kRegTypeFloat;
+ break;
+ case 'D':
+ type = kRegTypeDoubleLo;
+ break;
+ case 'J':
+ type = kRegTypeLongLo;
+ break;
+ case 'V':
+ case 'L':
+ case '[':
+ type = kRegTypeUnknown;
+ break;
+ default:
+ assert(false);
+ type = kRegTypeUnknown;
+ break;
+ }
+
+ return type;
+}
+
+/*
+ * See if the method matches the MethodType.
+ */
+static bool isCorrectInvokeKind(MethodType methodType, Method* resMethod)
+{
+ switch (methodType) {
+ case METHOD_DIRECT:
+ return dvmIsDirectMethod(resMethod);
+ case METHOD_STATIC:
+ return dvmIsStaticMethod(resMethod);
+ case METHOD_VIRTUAL:
+ case METHOD_INTERFACE:
+ return !dvmIsDirectMethod(resMethod);
+ default:
+ return false;
+ }
+}
+
+/*
+ * Verify the arguments to a method. We're executing in "method", making
+ * a call to the method reference in vB.
+ *
+ * If this is a "direct" invoke, we allow calls to <init>. For calls to
+ * <init>, the first argument may be an uninitialized reference. Otherwise,
+ * calls to anything starting with '<' will be rejected, as will any
+ * uninitialized reference arguments.
+ *
+ * For non-static method calls, this will verify that the method call is
+ * appropriate for the "this" argument.
+ *
+ * The method reference is in vBBBB. The "isRange" parameter determines
+ * whether we use 0-4 "args" values or a range of registers defined by
+ * vAA and vCCCC.
+ *
+ * Widening conversions on integers and references are allowed, but
+ * narrowing conversions are not.
+ *
+ * Returns the resolved method on success, NULL on failure (with *pFailure
+ * set appropriately).
+ */
+static Method* verifyInvocationArgs(const Method* meth, const RegType* insnRegs,
+ const int insnRegCount, const DecodedInstruction* pDecInsn,
+ UninitInstanceMap* uninitMap, MethodType methodType, bool isRange,
+ bool isSuper, VerifyError* pFailure)
+{
+ Method* resMethod;
+ char* sigOriginal = NULL;
+
+ /*
+ * Resolve the method. This could be an abstract or concrete method
+ * depending on what sort of call we're making.
+ */
+ if (methodType == METHOD_INTERFACE) {
+ resMethod = dvmOptResolveInterfaceMethod(meth->clazz, pDecInsn->vB);
+ } else {
+ resMethod = dvmOptResolveMethod(meth->clazz, pDecInsn->vB, methodType,
+ pFailure);
+ }
+ if (resMethod == NULL) {
+ /* failed; print a meaningful failure message */
+ DexFile* pDexFile = meth->clazz->pDvmDex->pDexFile;
+ const DexMethodId* pMethodId;
+ const char* methodName;
+ char* methodDesc;
+ const char* classDescriptor;
+
+ pMethodId = dexGetMethodId(pDexFile, pDecInsn->vB);
+ methodName = dexStringById(pDexFile, pMethodId->nameIdx);
+ methodDesc = dexCopyDescriptorFromMethodId(pDexFile, pMethodId);
+ classDescriptor = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
+
+ if (!gDvm.optimizing) {
+ char* dotMissingClass = dvmDescriptorToDot(classDescriptor);
+ char* dotMethClass = dvmDescriptorToDot(meth->clazz->descriptor);
+ //char* curMethodDesc =
+ // dexProtoCopyMethodDescriptor(&meth->prototype);
+
+ LOGI("Could not find method %s.%s, referenced from method %s.%s\n",
+ dotMissingClass, methodName/*, methodDesc*/,
+ dotMethClass, meth->name/*, curMethodDesc*/);
+
+ free(dotMissingClass);
+ free(dotMethClass);
+ //free(curMethodDesc);
+ }
+
+ LOG_VFY("VFY: unable to resolve %s method %u: %s.%s %s\n",
+ dvmMethodTypeStr(methodType), pDecInsn->vB,
+ classDescriptor, methodName, methodDesc);
+ free(methodDesc);
+ if (VERIFY_OK(*pFailure)) /* not set for interface resolve */
+ *pFailure = VERIFY_ERROR_NO_METHOD;
+ goto fail;
+ }
+
+ /*
+ * Only time you can explicitly call a method starting with '<' is when
+ * making a "direct" invocation on "<init>". There are additional
+ * restrictions but we don't enforce them here.
+ */
+ if (resMethod->name[0] == '<') {
+ if (methodType != METHOD_DIRECT || !isInitMethod(resMethod)) {
+ LOG_VFY("VFY: invalid call to %s.%s\n",
+ resMethod->clazz->descriptor, resMethod->name);
+ goto bad_sig;
+ }
+ }
+
+ /*
+ * See if the method type implied by the invoke instruction matches the
+ * access flags for the target method.
+ */
+ if (!isCorrectInvokeKind(methodType, resMethod)) {
+ LOG_VFY("VFY: invoke type does not match method type of %s.%s\n",
+ resMethod->clazz->descriptor, resMethod->name);
+ goto fail;
+ }
+
+ /*
+ * If we're using invoke-super(method), make sure that the executing
+ * method's class' superclass has a vtable entry for the target method.
+ */
+ if (isSuper) {
+ assert(methodType == METHOD_VIRTUAL);
+ ClassObject* super = meth->clazz->super;
+ if (super == NULL || resMethod->methodIndex > super->vtableCount) {
+ char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
+ LOG_VFY("VFY: invalid invoke-super from %s.%s to super %s.%s %s\n",
+ meth->clazz->descriptor, meth->name,
+ (super == NULL) ? "-" : super->descriptor,
+ resMethod->name, desc);
+ free(desc);
+ *pFailure = VERIFY_ERROR_NO_METHOD;
+ goto fail;
+ }
+ }
+
+ /*
+ * We use vAA as our expected arg count, rather than resMethod->insSize,
+ * because we need to match the call to the signature. Also, we might
+ * might be calling through an abstract method definition (which doesn't
+ * have register count values).
+ */
+ sigOriginal = dexProtoCopyMethodDescriptor(&resMethod->prototype);
+ const char* sig = sigOriginal;
+ int expectedArgs = pDecInsn->vA;
+ int actualArgs = 0;
+
+ if (!isRange && expectedArgs > 5) {
+ LOG_VFY("VFY: invalid arg count in non-range invoke (%d)\n",
+ pDecInsn->vA);
+ goto fail;
+ }
+ if (expectedArgs > meth->outsSize) {
+ LOG_VFY("VFY: invalid arg count (%d) exceeds outsSize (%d)\n",
+ expectedArgs, meth->outsSize);
+ goto fail;
+ }
+
+ if (*sig++ != '(')
+ goto bad_sig;
+
+ /*
+ * Check the "this" argument, which must be an instance of the class
+ * that declared the method. For an interface class, we don't do the
+ * full interface merge, so we can't do a rigorous check here (which
+ * is okay since we have to do it at runtime).
+ */
+ if (!dvmIsStaticMethod(resMethod)) {
+ ClassObject* actualThisRef;
+ RegType actualArgType;
+
+ actualArgType = getInvocationThis(insnRegs, insnRegCount, pDecInsn,
+ pFailure);
+ if (!VERIFY_OK(*pFailure))
+ goto fail;
+
+ if (regTypeIsUninitReference(actualArgType) && resMethod->name[0] != '<')
+ {
+ LOG_VFY("VFY: 'this' arg must be initialized\n");
+ goto fail;
+ }
+ if (methodType != METHOD_INTERFACE && actualArgType != kRegTypeZero) {
+ actualThisRef = regTypeReferenceToClass(actualArgType, uninitMap);
+ if (!dvmInstanceof(actualThisRef, resMethod->clazz)) {
+ LOG_VFY("VFY: 'this' arg '%s' not instance of '%s'\n",
+ actualThisRef->descriptor,
+ resMethod->clazz->descriptor);
+ goto fail;
+ }
+ }
+ actualArgs++;
+ }
+
+ /*
+ * Process the target method's signature. This signature may or may not
+ * have been verified, so we can't assume it's properly formed.
+ */
+ while (*sig != '\0' && *sig != ')') {
+ if (actualArgs >= expectedArgs) {
+ LOG_VFY("VFY: expected %d args, found more (%c)\n",
+ expectedArgs, *sig);
+ goto bad_sig;
+ }
+
+ u4 getReg;
+ if (isRange)
+ getReg = pDecInsn->vC + actualArgs;
+ else
+ getReg = pDecInsn->arg[actualArgs];
+
+ switch (*sig) {
+ case 'L':
+ {
+ ClassObject* clazz = lookupSignatureClass(meth, &sig, pFailure);
+ if (!VERIFY_OK(*pFailure))
+ goto bad_sig;
+ verifyRegisterType(insnRegs, insnRegCount, getReg,
+ regTypeFromClass(clazz), pFailure);
+ if (!VERIFY_OK(*pFailure)) {
+ LOG_VFY("VFY: bad arg %d (into %s)\n",
+ actualArgs, clazz->descriptor);
+ goto bad_sig;
+ }
+ }
+ actualArgs++;
+ break;
+ case '[':
+ {
+ ClassObject* clazz =
+ lookupSignatureArrayClass(meth, &sig, pFailure);
+ if (!VERIFY_OK(*pFailure))
+ goto bad_sig;
+ verifyRegisterType(insnRegs, insnRegCount, getReg,
+ regTypeFromClass(clazz), pFailure);
+ if (!VERIFY_OK(*pFailure)) {
+ LOG_VFY("VFY: bad arg %d (into %s)\n",
+ actualArgs, clazz->descriptor);
+ goto bad_sig;
+ }
+ }
+ actualArgs++;
+ break;
+ case 'Z':
+ verifyRegisterType(insnRegs, insnRegCount, getReg,
+ kRegTypeBoolean, pFailure);
+ actualArgs++;
+ break;
+ case 'C':
+ verifyRegisterType(insnRegs, insnRegCount, getReg,
+ kRegTypeChar, pFailure);
+ actualArgs++;
+ break;
+ case 'B':
+ verifyRegisterType(insnRegs, insnRegCount, getReg,
+ kRegTypeByte, pFailure);
+ actualArgs++;
+ break;
+ case 'I':
+ verifyRegisterType(insnRegs, insnRegCount, getReg,
+ kRegTypeInteger, pFailure);
+ actualArgs++;
+ break;
+ case 'S':
+ verifyRegisterType(insnRegs, insnRegCount, getReg,
+ kRegTypeShort, pFailure);
+ actualArgs++;
+ break;
+ case 'F':
+ verifyRegisterType(insnRegs, insnRegCount, getReg,
+ kRegTypeFloat, pFailure);
+ actualArgs++;
+ break;
+ case 'D':
+ verifyRegisterType(insnRegs, insnRegCount, getReg,
+ kRegTypeDoubleLo, pFailure);
+ actualArgs += 2;
+ break;
+ case 'J':
+ verifyRegisterType(insnRegs, insnRegCount, getReg,
+ kRegTypeLongLo, pFailure);
+ actualArgs += 2;
+ break;
+ default:
+ LOG_VFY("VFY: invocation target: bad signature type char '%c'\n",
+ *sig);
+ goto bad_sig;
+ }
+
+ sig++;
+ }
+ if (*sig != ')') {
+ char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
+ LOG_VFY("VFY: invocation target: bad signature '%s'\n", desc);
+ free(desc);
+ goto bad_sig;
+ }
+
+ if (actualArgs != expectedArgs) {
+ LOG_VFY("VFY: expected %d args, found %d\n", expectedArgs, actualArgs);
+ goto bad_sig;
+ }
+
+ free(sigOriginal);
+ return resMethod;
+
+bad_sig:
+ if (resMethod != NULL) {
+ char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
+ LOG_VFY("VFY: rejecting call to %s.%s %s\n",
+ resMethod->clazz->descriptor, resMethod->name, desc);
+ free(desc);
+ }
+
+fail:
+ free(sigOriginal);
+ if (*pFailure == VERIFY_ERROR_NONE)
+ *pFailure = VERIFY_ERROR_GENERIC;
+ return NULL;
+}
+
+/*
+ * Get the class object for the type of data stored in a field. This isn't
+ * stored in the Field struct, so we have to recover it from the signature.
+ *
+ * This only works for reference types. Don't call this for primitive types.
+ *
+ * If we can't find the class, we return java.lang.Object, so that
+ * verification can continue if a field is only accessed in trivial ways.
+ */
+static ClassObject* getFieldClass(const Method* meth, const Field* field)
+{
+ ClassObject* fieldClass;
+ const char* signature = field->signature;
+
+ if ((*signature == 'L') || (*signature == '[')) {
+ fieldClass = dvmFindClassNoInit(signature,
+ meth->clazz->classLoader);
+ } else {
+ return NULL;
+ }
+
+ if (fieldClass == NULL) {
+ dvmClearOptException(dvmThreadSelf());
+ LOGV("VFY: unable to find class '%s' for field %s.%s, trying Object\n",
+ field->signature, meth->clazz->descriptor, field->name);
+ fieldClass = gDvm.classJavaLangObject;
+ } else {
+ assert(!dvmIsPrimitiveClass(fieldClass));
+ }
+ return fieldClass;
+}
+
+
+/*
+ * ===========================================================================
+ * Register operations
+ * ===========================================================================
+ */
+
+/*
+ * Get the type of register N, verifying that the register is valid.
+ *
+ * Sets "*pFailure" appropriately if the register number is out of range.
+ */
+static inline RegType getRegisterType(const RegType* insnRegs,
+ const int insnRegCount, u4 vsrc, VerifyError* pFailure)
+{
+ if (vsrc >= (u4) insnRegCount) {
+ *pFailure = VERIFY_ERROR_GENERIC;
+ return kRegTypeUnknown;
+ } else {
+ return insnRegs[vsrc];
+ }
+}
+
+/*
+ * Get the value from a register, and cast it to a ClassObject. Sets
+ * "*pFailure" if something fails.
+ *
+ * This fails if the register holds an uninitialized class.
+ *
+ * If the register holds kRegTypeZero, this returns a NULL pointer.
+ */
+static ClassObject* getClassFromRegister(const RegType* insnRegs,
+ const int insnRegCount, u4 vsrc, VerifyError* pFailure)
+{
+ ClassObject* clazz = NULL;
+ RegType type;
+
+ /* get the element type of the array held in vsrc */
+ type = getRegisterType(insnRegs, insnRegCount, vsrc, pFailure);
+ if (!VERIFY_OK(*pFailure))
+ goto bail;
+
+ /* if "always zero", we allow it to fail at runtime */
+ if (type == kRegTypeZero)
+ goto bail;
+
+ if (!regTypeIsReference(type)) {
+ LOG_VFY("VFY: tried to get class from non-ref register v%d (type=%d)\n",
+ vsrc, type);
+ *pFailure = VERIFY_ERROR_GENERIC;
+ goto bail;
+ }
+ if (regTypeIsUninitReference(type)) {
+ LOG_VFY("VFY: register %u holds uninitialized reference\n", vsrc);
+ *pFailure = VERIFY_ERROR_GENERIC;
+ goto bail;
+ }
+
+ clazz = regTypeInitializedReferenceToClass(type);
+
+bail:
+ return clazz;
+}
+
+/*
+ * Get the "this" pointer from a non-static method invocation. This
+ * returns the RegType so the caller can decide whether it needs the
+ * reference to be initialized or not. (Can also return kRegTypeZero
+ * if the reference can only be zero at this point.)
+ *
+ * The argument count is in vA, and the first argument is in vC, for both
+ * "simple" and "range" versions. We just need to make sure vA is >= 1
+ * and then return vC.
+ */
+static RegType getInvocationThis(const RegType* insnRegs,
+ const int insnRegCount, const DecodedInstruction* pDecInsn,
+ VerifyError* pFailure)
+{
+ RegType thisType = kRegTypeUnknown;
+
+ if (pDecInsn->vA < 1) {
+ LOG_VFY("VFY: invoke lacks 'this'\n");
+ *pFailure = VERIFY_ERROR_GENERIC;
+ goto bail;
+ }
+
+ /* get the element type of the array held in vsrc */
+ thisType = getRegisterType(insnRegs, insnRegCount, pDecInsn->vC, pFailure);
+ if (!VERIFY_OK(*pFailure)) {
+ LOG_VFY("VFY: failed to get 'this' from register %u\n", pDecInsn->vC);
+ goto bail;
+ }
+
+ if (!regTypeIsReference(thisType)) {
+ LOG_VFY("VFY: tried to get class from non-ref register v%d (type=%d)\n",
+ pDecInsn->vC, thisType);
+ *pFailure = VERIFY_ERROR_GENERIC;
+ goto bail;
+ }
+
+bail:
+ return thisType;
+}
+
+/*
+ * Set the type of register N, verifying that the register is valid. If
+ * "newType" is the "Lo" part of a 64-bit value, register N+1 will be
+ * set to "newType+1".
+ *
+ * Sets "*pFailure" if the register number is out of range.
+ */
+static void setRegisterType(RegType* insnRegs, const int insnRegCount,
+ u4 vdst, RegType newType, VerifyError* pFailure)
+{
+ //LOGD("set-reg v%u = %d\n", vdst, newType);
+ switch (newType) {
+ case kRegTypeUnknown:
+ case kRegTypeBoolean:
+ case kRegTypeOne:
+ case kRegTypeByte:
+ case kRegTypePosByte:
+ case kRegTypeShort:
+ case kRegTypePosShort:
+ case kRegTypeChar:
+ case kRegTypeInteger:
+ case kRegTypeFloat:
+ case kRegTypeZero:
+ if (vdst >= (u4) insnRegCount) {
+ *pFailure = VERIFY_ERROR_GENERIC;
+ } else {
+ insnRegs[vdst] = newType;
+ }
+ break;
+ case kRegTypeLongLo:
+ case kRegTypeDoubleLo:
+ if (vdst+1 >= (u4) insnRegCount) {
+ *pFailure = VERIFY_ERROR_GENERIC;
+ } else {
+ insnRegs[vdst] = newType;
+ insnRegs[vdst+1] = newType+1;
+ }
+ break;
+ case kRegTypeLongHi:
+ case kRegTypeDoubleHi:
+ /* should never set these explicitly */
+ *pFailure = VERIFY_ERROR_GENERIC;
+ break;
+
+ case kRegTypeUninit:
+ default:
+ if (regTypeIsReference(newType)) {
+ if (vdst >= (u4) insnRegCount) {
+ *pFailure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ insnRegs[vdst] = newType;
+
+ /*
+ * In most circumstances we won't see a reference to a primitive
+ * class here (e.g. "D"), since that would mean the object in the
+ * register is actually a primitive type. It can happen as the
+ * result of an assumed-successful check-cast instruction in
+ * which the second argument refers to a primitive class. (In
+ * practice, such an instruction will always throw an exception.)
+ *
+ * This is not an issue for instructions like const-class, where
+ * the object in the register is a java.lang.Class instance.
+ */
+ break;
+ }
+ /* bad - fall through */
+
+ case kRegTypeConflict: // should only be set during a merge
+ LOG_VFY("Unexpected set type %d\n", newType);
+ assert(false);
+ *pFailure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+}
+
+/*
+ * Verify that the contents of the specified register have the specified
+ * type (or can be converted to it through an implicit widening conversion).
+ *
+ * In theory we could use this to modify the type of the source register,
+ * e.g. a generic 32-bit constant, once used as a float, would thereafter
+ * remain a float. There is no compelling reason to require this though.
+ *
+ * If "vsrc" is a reference, both it and the "vsrc" register must be
+ * initialized ("vsrc" may be Zero). This will verify that the value in
+ * the register is an instance of checkType, or if checkType is an
+ * interface, verify that the register implements checkType.
+ */
+static void verifyRegisterType(const RegType* insnRegs, const int insnRegCount,
+ u4 vsrc, RegType checkType, VerifyError* pFailure)
+{
+ if (vsrc >= (u4) insnRegCount) {
+ *pFailure = VERIFY_ERROR_GENERIC;
+ return;
+ }
+
+ RegType srcType = insnRegs[vsrc];
+
+ //LOGD("check-reg v%u = %d\n", vsrc, checkType);
+ switch (checkType) {
+ case kRegTypeFloat:
+ case kRegTypeBoolean:
+ case kRegTypePosByte:
+ case kRegTypeByte:
+ case kRegTypePosShort:
+ case kRegTypeShort:
+ case kRegTypeChar:
+ case kRegTypeInteger:
+ if (!canConvertTo1nr(srcType, checkType)) {
+ LOG_VFY("VFY: register1 v%u type %d, wanted %d\n",
+ vsrc, srcType, checkType);
+ *pFailure = VERIFY_ERROR_GENERIC;
+ }
+ break;
+ case kRegTypeLongLo:
+ case kRegTypeDoubleLo:
+ if (vsrc+1 >= (u4) insnRegCount) {
+ LOG_VFY("VFY: register2 v%u out of range (%d)\n",
+ vsrc, insnRegCount);
+ *pFailure = VERIFY_ERROR_GENERIC;
+ } else if (insnRegs[vsrc+1] != srcType+1) {
+ LOG_VFY("VFY: register2 v%u-%u values %d,%d\n",
+ vsrc, vsrc+1, insnRegs[vsrc], insnRegs[vsrc+1]);
+ *pFailure = VERIFY_ERROR_GENERIC;
+ } else if (!canConvertTo2(srcType, checkType)) {
+ LOG_VFY("VFY: register2 v%u type %d, wanted %d\n",
+ vsrc, srcType, checkType);
+ *pFailure = VERIFY_ERROR_GENERIC;
+ }
+ break;
+
+ case kRegTypeLongHi:
+ case kRegTypeDoubleHi:
+ case kRegTypeZero:
+ case kRegTypeOne:
+ case kRegTypeUnknown:
+ case kRegTypeConflict:
+ /* should never be checking for these explicitly */
+ assert(false);
+ *pFailure = VERIFY_ERROR_GENERIC;
+ return;
+ case kRegTypeUninit:
+ default:
+ /* make sure checkType is initialized reference */
+ if (!regTypeIsReference(checkType)) {
+ LOG_VFY("VFY: unexpected check type %d\n", checkType);
+ assert(false);
+ *pFailure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ if (regTypeIsUninitReference(checkType)) {
+ LOG_VFY("VFY: uninitialized ref not expected as reg check\n");
+ *pFailure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ /* make sure srcType is initialized reference or always-NULL */
+ if (!regTypeIsReference(srcType)) {
+ LOG_VFY("VFY: register1 v%u type %d, wanted ref\n", vsrc, srcType);
+ *pFailure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ if (regTypeIsUninitReference(srcType)) {
+ LOG_VFY("VFY: register1 v%u holds uninitialized ref\n", vsrc);
+ *pFailure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ /* if the register isn't Zero, make sure it's an instance of check */
+ if (srcType != kRegTypeZero) {
+ ClassObject* srcClass = regTypeInitializedReferenceToClass(srcType);
+ ClassObject* checkClass = regTypeInitializedReferenceToClass(checkType);
+ assert(srcClass != NULL);
+ assert(checkClass != NULL);
+
+ if (dvmIsInterfaceClass(checkClass)) {
+ /*
+ * All objects implement all interfaces as far as the
+ * verifier is concerned. The runtime has to sort it out.
+ * See comments above findCommonSuperclass.
+ */
+ /*
+ if (srcClass != checkClass &&
+ !dvmImplements(srcClass, checkClass))
+ {
+ LOG_VFY("VFY: %s does not implement %s\n",
+ srcClass->descriptor, checkClass->descriptor);
+ *pFailure = VERIFY_ERROR_GENERIC;
+ }
+ */
+ } else {
+ if (!dvmInstanceof(srcClass, checkClass)) {
+ LOG_VFY("VFY: %s is not instance of %s\n",
+ srcClass->descriptor, checkClass->descriptor);
+ *pFailure = VERIFY_ERROR_GENERIC;
+ }
+ }
+ }
+ break;
+ }
+}
+
+/*
+ * Set the type of the "result" register. Mostly this exists to expand
+ * "insnRegCount" to encompass the result register.
+ */
+static void setResultRegisterType(RegType* insnRegs, const int insnRegCount,
+ RegType newType, VerifyError* pFailure)
+{
+ setRegisterType(insnRegs, insnRegCount + kExtraRegs,
+ RESULT_REGISTER(insnRegCount), newType, pFailure);
+}
+
+
+/*
+ * Update all registers holding "uninitType" to instead hold the
+ * corresponding initialized reference type. This is called when an
+ * appropriate <init> method is invoked -- all copies of the reference
+ * must be marked as initialized.
+ */
+static void markRefsAsInitialized(RegType* insnRegs, int insnRegCount,
+ UninitInstanceMap* uninitMap, RegType uninitType, VerifyError* pFailure)
+{
+ ClassObject* clazz;
+ RegType initType;
+ int i, changed;
+
+ clazz = dvmGetUninitInstance(uninitMap, regTypeToUninitIndex(uninitType));
+ if (clazz == NULL) {
+ LOGE("VFY: unable to find type=0x%x (idx=%d)\n",
+ uninitType, regTypeToUninitIndex(uninitType));
+ *pFailure = VERIFY_ERROR_GENERIC;
+ return;
+ }
+ initType = regTypeFromClass(clazz);
+
+ changed = 0;
+ for (i = 0; i < insnRegCount; i++) {
+ if (insnRegs[i] == uninitType) {
+ insnRegs[i] = initType;
+ changed++;
+ }
+ }
+ //LOGD("VFY: marked %d registers as initialized\n", changed);
+ assert(changed > 0);
+
+ return;
+}
+
+/*
+ * We're creating a new instance of class C at address A. Any registers
+ * holding instances previously created at address A must be initialized
+ * by now. If not, we mark them as "conflict" to prevent them from being
+ * used (otherwise, markRefsAsInitialized would mark the old ones and the
+ * new ones at the same time).
+ */
+static void markUninitRefsAsInvalid(RegType* insnRegs, int insnRegCount,
+ UninitInstanceMap* uninitMap, RegType uninitType)
+{
+ int i, changed;
+
+ changed = 0;
+ for (i = 0; i < insnRegCount; i++) {
+ if (insnRegs[i] == uninitType) {
+ insnRegs[i] = kRegTypeConflict;
+ changed++;
+ }
+ }
+
+ //if (changed)
+ // LOGD("VFY: marked %d uninitialized registers as invalid\n", changed);
+}
+
+/*
+ * Find the start of the register set for the specified instruction in
+ * the current method.
+ */
+static inline RegType* getRegisterLine(const RegisterTable* regTable,
+ int insnIdx)
+{
+ return regTable->addrRegs[insnIdx];
+}
+
+/*
+ * Copy a bunch of registers.
+ */
+static inline void copyRegisters(RegType* dst, const RegType* src,
+ int numRegs)
+{
+ memcpy(dst, src, numRegs * sizeof(RegType));
+}
+
+/*
+ * Compare a bunch of registers.
+ *
+ * Returns 0 if they match. Using this for a sort is unwise, since the
+ * value can change based on machine endianness.
+ */
+static inline int compareRegisters(const RegType* src1, const RegType* src2,
+ int numRegs)
+{
+ return memcmp(src1, src2, numRegs * sizeof(RegType));
+}
+
+/*
+ * Register type categories, for type checking.
+ *
+ * The spec says category 1 includes boolean, byte, char, short, int, float,
+ * reference, and returnAddress. Category 2 includes long and double.
+ *
+ * We treat object references separately, so we have "category1nr". We
+ * don't support jsr/ret, so there is no "returnAddress" type.
+ */
+typedef enum TypeCategory {
+ kTypeCategoryUnknown = 0,
+ kTypeCategory1nr, // byte, char, int, float, boolean
+ kTypeCategory2, // long, double
+ kTypeCategoryRef, // object reference
+} TypeCategory;
+
+/*
+ * See if "type" matches "cat". All we're really looking for here is that
+ * we're not mixing and matching 32-bit and 64-bit quantities, and we're
+ * not mixing references with numerics. (For example, the arguments to
+ * "a < b" could be integers of different sizes, but they must both be
+ * integers. Dalvik is less specific about int vs. float, so we treat them
+ * as equivalent here.)
+ *
+ * For category 2 values, "type" must be the "low" half of the value.
+ *
+ * Sets "*pFailure" if something looks wrong.
+ */
+static void checkTypeCategory(RegType type, TypeCategory cat,
+ VerifyError* pFailure)
+{
+ switch (cat) {
+ case kTypeCategory1nr:
+ switch (type) {
+ case kRegTypeFloat:
+ case kRegTypeZero:
+ case kRegTypeOne:
+ case kRegTypeBoolean:
+ case kRegTypePosByte:
+ case kRegTypeByte:
+ case kRegTypePosShort:
+ case kRegTypeShort:
+ case kRegTypeChar:
+ case kRegTypeInteger:
+ break;
+ default:
+ *pFailure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ break;
+
+ case kTypeCategory2:
+ switch (type) {
+ case kRegTypeLongLo:
+ case kRegTypeDoubleLo:
+ break;
+ default:
+ *pFailure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ break;
+
+ case kTypeCategoryRef:
+ if (type != kRegTypeZero && !regTypeIsReference(type))
+ *pFailure = VERIFY_ERROR_GENERIC;
+ break;
+
+ default:
+ assert(false);
+ *pFailure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+}
+
+/*
+ * For a category 2 register pair, verify that "typeh" is the appropriate
+ * high part for "typel".
+ *
+ * Does not verify that "typel" is in fact the low part of a 64-bit
+ * register pair.
+ */
+static void checkWidePair(RegType typel, RegType typeh, VerifyError* pFailure)
+{
+ if ((typeh != typel+1))
+ *pFailure = VERIFY_ERROR_GENERIC;
+}
+
+/*
+ * Implement category-1 "move" instructions. Copy a 32-bit value from
+ * "vsrc" to "vdst".
+ *
+ * "insnRegCount" is the number of registers available. The "vdst" and
+ * "vsrc" values are checked against this.
+ */
+static void copyRegister1(RegType* insnRegs, int insnRegCount, u4 vdst,
+ u4 vsrc, TypeCategory cat, VerifyError* pFailure)
+{
+ RegType type = getRegisterType(insnRegs, insnRegCount, vsrc, pFailure);
+ if (VERIFY_OK(*pFailure))
+ checkTypeCategory(type, cat, pFailure);
+ if (VERIFY_OK(*pFailure))
+ setRegisterType(insnRegs, insnRegCount, vdst, type, pFailure);
+
+ if (!VERIFY_OK(*pFailure)) {
+ LOG_VFY("VFY: copy1 v%u<-v%u type=%d cat=%d\n", vdst, vsrc, type, cat);
+ }
+}
+
+/*
+ * Implement category-2 "move" instructions. Copy a 64-bit value from
+ * "vsrc" to "vdst". This copies both halves of the register.
+ */
+static void copyRegister2(RegType* insnRegs, int insnRegCount, u4 vdst,
+ u4 vsrc, VerifyError* pFailure)
+{
+ RegType typel = getRegisterType(insnRegs, insnRegCount, vsrc, pFailure);
+ RegType typeh = getRegisterType(insnRegs, insnRegCount, vsrc+1, pFailure);
+ if (VERIFY_OK(*pFailure)) {
+ checkTypeCategory(typel, kTypeCategory2, pFailure);
+ checkWidePair(typel, typeh, pFailure);
+ }
+ if (VERIFY_OK(*pFailure))
+ setRegisterType(insnRegs, insnRegCount, vdst, typel, pFailure);
+
+ if (!VERIFY_OK(*pFailure)) {
+ LOG_VFY("VFY: copy2 v%u<-v%u type=%d/%d\n", vdst, vsrc, typel, typeh);
+ }
+}
+
+/*
+ * Implement "move-result". Copy the category-1 value from the result
+ * register to another register, and reset the result register.
+ *
+ * We can't just call copyRegister1 with an altered insnRegCount,
+ * because that would affect the test on "vdst" as well.
+ */
+static void copyResultRegister1(RegType* insnRegs, const int insnRegCount,
+ u4 vdst, TypeCategory cat, VerifyError* pFailure)
+{
+ RegType type;
+ u4 vsrc;
+
+ vsrc = RESULT_REGISTER(insnRegCount);
+ type = getRegisterType(insnRegs, insnRegCount + kExtraRegs, vsrc, pFailure);
+ if (VERIFY_OK(*pFailure))
+ checkTypeCategory(type, cat, pFailure);
+ if (VERIFY_OK(*pFailure)) {
+ setRegisterType(insnRegs, insnRegCount, vdst, type, pFailure);
+ insnRegs[vsrc] = kRegTypeUnknown;
+ }
+
+ if (!VERIFY_OK(*pFailure)) {
+ LOG_VFY("VFY: copyRes1 v%u<-v%u cat=%d type=%d\n",
+ vdst, vsrc, cat, type);
+ }
+}
+
+/*
+ * Implement "move-result-wide". Copy the category-2 value from the result
+ * register to another register, and reset the result register.
+ *
+ * We can't just call copyRegister2 with an altered insnRegCount,
+ * because that would affect the test on "vdst" as well.
+ */
+static void copyResultRegister2(RegType* insnRegs, const int insnRegCount,
+ u4 vdst, VerifyError* pFailure)
+{
+ RegType typel, typeh;
+ u4 vsrc;
+
+ vsrc = RESULT_REGISTER(insnRegCount);
+ typel = getRegisterType(insnRegs, insnRegCount + kExtraRegs, vsrc,
+ pFailure);
+ typeh = getRegisterType(insnRegs, insnRegCount + kExtraRegs, vsrc+1,
+ pFailure);
+ if (VERIFY_OK(*pFailure)) {
+ checkTypeCategory(typel, kTypeCategory2, pFailure);
+ checkWidePair(typel, typeh, pFailure);
+ }
+ if (VERIFY_OK(*pFailure)) {
+ setRegisterType(insnRegs, insnRegCount, vdst, typel, pFailure);
+ insnRegs[vsrc] = kRegTypeUnknown;
+ insnRegs[vsrc+1] = kRegTypeUnknown;
+ }
+
+ if (!VERIFY_OK(*pFailure)) {
+ LOG_VFY("VFY: copyRes2 v%u<-v%u type=%d/%d\n",
+ vdst, vsrc, typel, typeh);
+ }
+}
+
+/*
+ * Verify types for a simple two-register instruction (e.g. "neg-int").
+ * "dstType" is stored into vA, and "srcType" is verified against vB.
+ */
+static void checkUnop(RegType* insnRegs, const int insnRegCount,
+ DecodedInstruction* pDecInsn, RegType dstType, RegType srcType,
+ VerifyError* pFailure)
+{
+ verifyRegisterType(insnRegs, insnRegCount, pDecInsn->vB, srcType, pFailure);
+ setRegisterType(insnRegs, insnRegCount, pDecInsn->vA, dstType, pFailure);
+}
+
+/*
+ * We're performing an operation like "and-int/2addr" that can be
+ * performed on booleans as well as integers. We get no indication of
+ * boolean-ness, but we can infer it from the types of the arguments.
+ *
+ * Assumes we've already validated reg1/reg2.
+ *
+ * TODO: consider generalizing this. The key principle is that the
+ * result of a bitwise operation can only be as wide as the widest of
+ * the operands. You can safely AND/OR/XOR two chars together and know
+ * you still have a char, so it's reasonable for the compiler or "dx"
+ * to skip the int-to-char instruction. (We need to do this for boolean
+ * because there is no int-to-boolean operation.)
+ *
+ * Returns true if both args are Boolean, Zero, or One.
+ */
+static bool upcastBooleanOp(RegType* insnRegs, const int insnRegCount,
+ u4 reg1, u4 reg2)
+{
+ RegType type1, type2;
+
+ type1 = insnRegs[reg1];
+ type2 = insnRegs[reg2];
+
+ if ((type1 == kRegTypeBoolean || type1 == kRegTypeZero ||
+ type1 == kRegTypeOne) &&
+ (type2 == kRegTypeBoolean || type2 == kRegTypeZero ||
+ type2 == kRegTypeOne))
+ {
+ return true;
+ }
+ return false;
+}
+
+/*
+ * Verify types for A two-register instruction with a literal constant
+ * (e.g. "add-int/lit8"). "dstType" is stored into vA, and "srcType" is
+ * verified against vB.
+ *
+ * If "checkBooleanOp" is set, we use the constant value in vC.
+ */
+static void checkLitop(RegType* insnRegs, const int insnRegCount,
+ DecodedInstruction* pDecInsn, RegType dstType, RegType srcType,
+ bool checkBooleanOp, VerifyError* pFailure)
+{
+ verifyRegisterType(insnRegs, insnRegCount, pDecInsn->vB, srcType, pFailure);
+ if (VERIFY_OK(*pFailure) && checkBooleanOp) {
+ assert(dstType == kRegTypeInteger);
+ /* check vB with the call, then check the constant manually */
+ if (upcastBooleanOp(insnRegs, insnRegCount, pDecInsn->vB, pDecInsn->vB)
+ && (pDecInsn->vC == 0 || pDecInsn->vC == 1))
+ {
+ dstType = kRegTypeBoolean;
+ }
+ }
+ setRegisterType(insnRegs, insnRegCount, pDecInsn->vA, dstType, pFailure);
+}
+
+/*
+ * Verify types for a simple three-register instruction (e.g. "add-int").
+ * "dstType" is stored into vA, and "srcType1"/"srcType2" are verified
+ * against vB/vC.
+ */
+static void checkBinop(RegType* insnRegs, const int insnRegCount,
+ DecodedInstruction* pDecInsn, RegType dstType, RegType srcType1,
+ RegType srcType2, bool checkBooleanOp, VerifyError* pFailure)
+{
+ verifyRegisterType(insnRegs, insnRegCount, pDecInsn->vB, srcType1,
+ pFailure);
+ verifyRegisterType(insnRegs, insnRegCount, pDecInsn->vC, srcType2,
+ pFailure);
+ if (VERIFY_OK(*pFailure) && checkBooleanOp) {
+ assert(dstType == kRegTypeInteger);
+ if (upcastBooleanOp(insnRegs, insnRegCount, pDecInsn->vB, pDecInsn->vC))
+ dstType = kRegTypeBoolean;
+ }
+ setRegisterType(insnRegs, insnRegCount, pDecInsn->vA, dstType, pFailure);
+}
+
+/*
+ * Verify types for a binary "2addr" operation. "srcType1"/"srcType2"
+ * are verified against vA/vB, then "dstType" is stored into vA.
+ */
+static void checkBinop2addr(RegType* insnRegs, const int insnRegCount,
+ DecodedInstruction* pDecInsn, RegType dstType, RegType srcType1,
+ RegType srcType2, bool checkBooleanOp, VerifyError* pFailure)
+{
+ verifyRegisterType(insnRegs, insnRegCount, pDecInsn->vA, srcType1,
+ pFailure);
+ verifyRegisterType(insnRegs, insnRegCount, pDecInsn->vB, srcType2,
+ pFailure);
+ if (VERIFY_OK(*pFailure) && checkBooleanOp) {
+ assert(dstType == kRegTypeInteger);
+ if (upcastBooleanOp(insnRegs, insnRegCount, pDecInsn->vA, pDecInsn->vB))
+ dstType = kRegTypeBoolean;
+ }
+ setRegisterType(insnRegs, insnRegCount, pDecInsn->vA, dstType, pFailure);
+}
+
+/*
+ * Treat right-shifting as a narrowing conversion when possible.
+ *
+ * For example, right-shifting an int 24 times results in a value that can
+ * be treated as a byte.
+ *
+ * Things get interesting when contemplating sign extension. Right-
+ * shifting an integer by 16 yields a value that can be represented in a
+ * "short" but not a "char", but an unsigned right shift by 16 yields a
+ * value that belongs in a char rather than a short. (Consider what would
+ * happen if the result of the shift were cast to a char or short and then
+ * cast back to an int. If sign extension, or the lack thereof, causes
+ * a change in the 32-bit representation, then the conversion was lossy.)
+ *
+ * A signed right shift by 17 on an integer results in a short. An unsigned
+ * right shfit by 17 on an integer results in a posshort, which can be
+ * assigned to a short or a char.
+ *
+ * An unsigned right shift on a short can actually expand the result into
+ * a 32-bit integer. For example, 0xfffff123 >>> 8 becomes 0x00fffff1,
+ * which can't be represented in anything smaller than an int.
+ *
+ * javac does not generate code that takes advantage of this, but some
+ * of the code optimizers do. It's generally a peephole optimization
+ * that replaces a particular sequence, e.g. (bipush 24, ishr, i2b) is
+ * replaced by (bipush 24, ishr). Knowing that shifting a short 8 times
+ * to the right yields a byte is really more than we need to handle the
+ * code that's out there, but support is not much more complex than just
+ * handling integer.
+ *
+ * Right-shifting never yields a boolean value.
+ *
+ * Returns the new register type.
+ */
+static RegType adjustForRightShift(RegType* workRegs, const int insnRegCount,
+ int reg, unsigned int shiftCount, bool isUnsignedShift,
+ VerifyError* pFailure)
+{
+ RegType srcType = getRegisterType(workRegs, insnRegCount, reg, pFailure);
+ RegType newType;
+
+ /* no-op */
+ if (shiftCount == 0)
+ return srcType;
+
+ /* safe defaults */
+ if (isUnsignedShift)
+ newType = kRegTypeInteger;
+ else
+ newType = srcType;
+
+ if (shiftCount >= 32) {
+ LOG_VFY("Got unexpectedly large shift count %u\n", shiftCount);
+ /* fail? */
+ return newType;
+ }
+
+ switch (srcType) {
+ case kRegTypeInteger: /* 32-bit signed value */
+ case kRegTypeFloat: /* (allowed; treat same as int) */
+ if (isUnsignedShift) {
+ if (shiftCount > 24)
+ newType = kRegTypePosByte;
+ else if (shiftCount >= 16)
+ newType = kRegTypeChar;
+ } else {
+ if (shiftCount >= 24)
+ newType = kRegTypeByte;
+ else if (shiftCount >= 16)
+ newType = kRegTypeShort;
+ }
+ break;
+ case kRegTypeShort: /* 16-bit signed value */
+ if (isUnsignedShift) {
+ /* default (kRegTypeInteger) is correct */
+ } else {
+ if (shiftCount >= 8)
+ newType = kRegTypeByte;
+ }
+ break;
+ case kRegTypePosShort: /* 15-bit unsigned value */
+ if (shiftCount >= 8)
+ newType = kRegTypePosByte;
+ break;
+ case kRegTypeChar: /* 16-bit unsigned value */
+ if (shiftCount > 8)
+ newType = kRegTypePosByte;
+ break;
+ case kRegTypeByte: /* 8-bit signed value */
+ /* defaults (u=kRegTypeInteger / s=srcType) are correct */
+ break;
+ case kRegTypePosByte: /* 7-bit unsigned value */
+ /* always use newType=srcType */
+ newType = srcType;
+ break;
+ case kRegTypeZero: /* 1-bit unsigned value */
+ case kRegTypeOne:
+ case kRegTypeBoolean:
+ /* unnecessary? */
+ newType = kRegTypeZero;
+ break;
+ default:
+ /* long, double, references; shouldn't be here! */
+ assert(false);
+ break;
+ }
+
+ if (newType != srcType) {
+ LOGVV("narrowing: %d(%d) --> %d to %d\n",
+ shiftCount, isUnsignedShift, srcType, newType);
+ } else {
+ LOGVV("not narrowed: %d(%d) --> %d\n",
+ shiftCount, isUnsignedShift, srcType);
+ }
+ return newType;
+}
+
+
+/*
+ * ===========================================================================
+ * Register merge
+ * ===========================================================================
+ */
+
+/*
+ * Compute the "class depth" of a class. This is the distance from the
+ * class to the top of the tree, chasing superclass links. java.lang.Object
+ * has a class depth of 0.
+ */
+static int getClassDepth(ClassObject* clazz)
+{
+ int depth = 0;
+
+ while (clazz->super != NULL) {
+ clazz = clazz->super;
+ depth++;
+ }
+ return depth;
+}
+
+/*
+ * Given two classes, walk up the superclass tree to find a common
+ * ancestor. (Called from findCommonSuperclass().)
+ *
+ * TODO: consider caching the class depth in the class object so we don't
+ * have to search for it here.
+ */
+static ClassObject* digForSuperclass(ClassObject* c1, ClassObject* c2)
+{
+ int depth1, depth2;
+
+ depth1 = getClassDepth(c1);
+ depth2 = getClassDepth(c2);
+
+ if (gDebugVerbose) {
+ LOGVV("COMMON: %s(%d) + %s(%d)\n",
+ c1->descriptor, depth1, c2->descriptor, depth2);
+ }
+
+ /* pull the deepest one up */
+ if (depth1 > depth2) {
+ while (depth1 > depth2) {
+ c1 = c1->super;
+ depth1--;
+ }
+ } else {
+ while (depth2 > depth1) {
+ c2 = c2->super;
+ depth2--;
+ }
+ }
+
+ /* walk up in lock-step */
+ while (c1 != c2) {
+ c1 = c1->super;
+ c2 = c2->super;
+
+ assert(c1 != NULL && c2 != NULL);
+ }
+
+ if (gDebugVerbose) {
+ LOGVV(" : --> %s\n", c1->descriptor);
+ }
+ return c1;
+}
+
+/*
+ * Merge two array classes. We can't use the general "walk up to the
+ * superclass" merge because the superclass of an array is always Object.
+ * We want String[] + Integer[] = Object[]. This works for higher dimensions
+ * as well, e.g. String[][] + Integer[][] = Object[][].
+ *
+ * If Foo1 and Foo2 are subclasses of Foo, Foo1[] + Foo2[] = Foo[].
+ *
+ * If Class implements Type, Class[] + Type[] = Type[].
+ *
+ * If the dimensions don't match, we want to convert to an array of Object
+ * with the least dimension, e.g. String[][] + String[][][][] = Object[][].
+ *
+ * This gets a little awkward because we may have to ask the VM to create
+ * a new array type with the appropriate element and dimensions. However, we
+ * shouldn't be doing this often.
+ */
+static ClassObject* findCommonArraySuperclass(ClassObject* c1, ClassObject* c2)
+{
+ ClassObject* arrayClass = NULL;
+ ClassObject* commonElem;
+ int i, numDims;
+
+ assert(c1->arrayDim > 0);
+ assert(c2->arrayDim > 0);
+
+ if (c1->arrayDim == c2->arrayDim) {
+ //commonElem = digForSuperclass(c1->elementClass, c2->elementClass);
+ commonElem = findCommonSuperclass(c1->elementClass, c2->elementClass);
+ numDims = c1->arrayDim;
+ } else {
+ if (c1->arrayDim < c2->arrayDim)
+ numDims = c1->arrayDim;
+ else
+ numDims = c2->arrayDim;
+ commonElem = c1->super; // == java.lang.Object
+ }
+
+ /* walk from the element to the (multi-)dimensioned array type */
+ for (i = 0; i < numDims; i++) {
+ arrayClass = dvmFindArrayClassForElement(commonElem);
+ commonElem = arrayClass;
+ }
+
+ LOGVV("ArrayMerge '%s' + '%s' --> '%s'\n",
+ c1->descriptor, c2->descriptor, arrayClass->descriptor);
+ return arrayClass;
+}
+
+/*
+ * Find the first common superclass of the two classes. We're not
+ * interested in common interfaces.
+ *
+ * The easiest way to do this for concrete classes is to compute the "class
+ * depth" of each, move up toward the root of the deepest one until they're
+ * at the same depth, then walk both up to the root until they match.
+ *
+ * If both classes are arrays of non-primitive types, we need to merge
+ * based on array depth and element type.
+ *
+ * If one class is an interface, we check to see if the other class/interface
+ * (or one of its predecessors) implements the interface. If so, we return
+ * the interface; otherwise, we return Object.
+ *
+ * NOTE: we continue the tradition of "lazy interface handling". To wit,
+ * suppose we have three classes:
+ * One implements Fancy, Free
+ * Two implements Fancy, Free
+ * Three implements Free
+ * where Fancy and Free are unrelated interfaces. The code requires us
+ * to merge One into Two. Ideally we'd use a common interface, which
+ * gives us a choice between Fancy and Free, and no guidance on which to
+ * use. If we use Free, we'll be okay when Three gets merged in, but if
+ * we choose Fancy, we're hosed. The "ideal" solution is to create a
+ * set of common interfaces and carry that around, merging further references
+ * into it. This is a pain. The easy solution is to simply boil them
+ * down to Objects and let the runtime invokeinterface call fail, which
+ * is what we do.
+ */
+static ClassObject* findCommonSuperclass(ClassObject* c1, ClassObject* c2)
+{
+ assert(!dvmIsPrimitiveClass(c1) && !dvmIsPrimitiveClass(c2));
+
+ if (c1 == c2)
+ return c1;
+
+ if (dvmIsInterfaceClass(c1) && dvmImplements(c2, c1)) {
+ if (gDebugVerbose)
+ LOGVV("COMMON/I1: %s + %s --> %s\n",
+ c1->descriptor, c2->descriptor, c1->descriptor);
+ return c1;
+ }
+ if (dvmIsInterfaceClass(c2) && dvmImplements(c1, c2)) {
+ if (gDebugVerbose)
+ LOGVV("COMMON/I2: %s + %s --> %s\n",
+ c1->descriptor, c2->descriptor, c2->descriptor);
+ return c2;
+ }
+
+ if (dvmIsArrayClass(c1) && dvmIsArrayClass(c2) &&
+ !dvmIsPrimitiveClass(c1->elementClass) &&
+ !dvmIsPrimitiveClass(c2->elementClass))
+ {
+ return findCommonArraySuperclass(c1, c2);
+ }
+
+ return digForSuperclass(c1, c2);
+}
+
+/*
+ * Merge two RegType values.
+ *
+ * Sets "*pChanged" to "true" if the result doesn't match "type1".
+ */
+static RegType mergeTypes(RegType type1, RegType type2, bool* pChanged)
+{
+ RegType result;
+
+ /*
+ * Check for trivial case so we don't have to hit memory.
+ */
+ if (type1 == type2)
+ return type1;
+
+ /*
+ * Use the table if we can, and reject any attempts to merge something
+ * from the table with a reference type.
+ *
+ * The uninitialized table entry at index zero *will* show up as a
+ * simple kRegTypeUninit value. Since this cannot be merged with
+ * anything but itself, the rules do the right thing.
+ */
+ if (type1 < kRegTypeMAX) {
+ if (type2 < kRegTypeMAX) {
+ result = gDvmMergeTab[type1][type2];
+ } else {
+ /* simple + reference == conflict, usually */
+ if (type1 == kRegTypeZero)
+ result = type2;
+ else
+ result = kRegTypeConflict;
+ }
+ } else {
+ if (type2 < kRegTypeMAX) {
+ /* reference + simple == conflict, usually */
+ if (type2 == kRegTypeZero)
+ result = type1;
+ else
+ result = kRegTypeConflict;
+ } else {
+ /* merging two references */
+ if (regTypeIsUninitReference(type1) ||
+ regTypeIsUninitReference(type2))
+ {
+ /* can't merge uninit with anything but self */
+ result = kRegTypeConflict;
+ } else {
+ ClassObject* clazz1 = regTypeInitializedReferenceToClass(type1);
+ ClassObject* clazz2 = regTypeInitializedReferenceToClass(type2);
+ ClassObject* mergedClass;
+
+ mergedClass = findCommonSuperclass(clazz1, clazz2);
+ assert(mergedClass != NULL);
+ result = regTypeFromClass(mergedClass);
+ }
+ }
+ }
+
+ if (result != type1)
+ *pChanged = true;
+ return result;
+}
+
+/*
+ * Control can transfer to "nextInsn".
+ *
+ * Merge the registers from "workRegs" into "regTypes" at "nextInsn", and
+ * set the "changed" flag on the target address if the registers have changed.
+ */
+static void updateRegisters(const Method* meth, InsnFlags* insnFlags,
+ RegisterTable* regTable, int nextInsn, const RegType* workRegs)
+{
+ RegType* targetRegs = getRegisterLine(regTable, nextInsn);
+ const int insnRegCount = meth->registersSize;
+
+#if 0
+ if (!dvmInsnIsBranchTarget(insnFlags, nextInsn)) {
+ LOGE("insnFlags[0x%x]=0x%08x\n", nextInsn, insnFlags[nextInsn]);
+ LOGE(" In %s.%s %s\n",
+ meth->clazz->descriptor, meth->name, meth->descriptor);
+ assert(false);
+ }
+#endif
+
+ if (!dvmInsnIsVisitedOrChanged(insnFlags, nextInsn)) {
+ /*
+ * We haven't processed this instruction before, and we haven't
+ * touched the registers here, so there's nothing to "merge". Copy
+ * the registers over and mark it as changed. (This is the only
+ * way a register can transition out of "unknown", so this is not
+ * just an optimization.)
+ */
+ LOGVV("COPY into 0x%04x\n", nextInsn);
+ copyRegisters(targetRegs, workRegs, insnRegCount + kExtraRegs);
+ dvmInsnSetChanged(insnFlags, nextInsn, true);
+ } else {
+ if (gDebugVerbose) {
+ LOGVV("MERGE into 0x%04x\n", nextInsn);
+ //dumpRegTypes(meth, insnFlags, targetRegs, 0, "targ", NULL, 0);
+ //dumpRegTypes(meth, insnFlags, workRegs, 0, "work", NULL, 0);
+ }
+ /* merge registers, set Changed only if different */
+ bool changed = false;
+ int i;
+
+ for (i = 0; i < insnRegCount + kExtraRegs; i++) {
+ targetRegs[i] = mergeTypes(targetRegs[i], workRegs[i], &changed);
+ }
+
+ if (gDebugVerbose) {
+ //LOGI(" RESULT (changed=%d)\n", changed);
+ //dumpRegTypes(meth, insnFlags, targetRegs, 0, "rslt", NULL, 0);
+ }
+
+ if (changed)
+ dvmInsnSetChanged(insnFlags, nextInsn, true);
+ }
+}
+
+
+/*
+ * ===========================================================================
+ * Utility functions
+ * ===========================================================================
+ */
+
+/*
+ * Look up an instance field, specified by "fieldIdx", that is going to be
+ * accessed in object "objType". This resolves the field and then verifies
+ * that the class containing the field is an instance of the reference in
+ * "objType".
+ *
+ * It is possible for "objType" to be kRegTypeZero, meaning that we might
+ * have a null reference. This is a runtime problem, so we allow it,
+ * skipping some of the type checks.
+ *
+ * In general, "objType" must be an initialized reference. However, we
+ * allow it to be uninitialized if this is an "<init>" method and the field
+ * is declared within the "objType" class.
+ *
+ * Returns an InstField on success, returns NULL and sets "*pFailure"
+ * on failure.
+ */
+static InstField* getInstField(const Method* meth,
+ const UninitInstanceMap* uninitMap, RegType objType, int fieldIdx,
+ VerifyError* pFailure)
+{
+ InstField* instField = NULL;
+ ClassObject* objClass;
+ bool mustBeLocal = false;
+
+ if (!regTypeIsReference(objType)) {
+ LOG_VFY("VFY: attempt to access field in non-reference type %d\n",
+ objType);
+ *pFailure = VERIFY_ERROR_GENERIC;
+ goto bail;
+ }
+
+ instField = dvmOptResolveInstField(meth->clazz, fieldIdx, pFailure);
+ if (instField == NULL) {
+ LOG_VFY("VFY: unable to resolve instance field %u\n", fieldIdx);
+ assert(!VERIFY_OK(*pFailure));
+ goto bail;
+ }
+
+ if (objType == kRegTypeZero)
+ goto bail;
+
+ /*
+ * Access to fields in uninitialized objects is allowed if this is
+ * the <init> method for the object and the field in question is
+ * declared by this class.
+ */
+ objClass = regTypeReferenceToClass(objType, uninitMap);
+ assert(objClass != NULL);
+ if (regTypeIsUninitReference(objType)) {
+ if (!isInitMethod(meth) || meth->clazz != objClass) {
+ LOG_VFY("VFY: attempt to access field via uninitialized ref\n");
+ *pFailure = VERIFY_ERROR_GENERIC;
+ goto bail;
+ }
+ mustBeLocal = true;
+ }
+
+ if (!dvmInstanceof(objClass, instField->field.clazz)) {
+ LOG_VFY("VFY: invalid field access (field %s.%s, through %s ref)\n",
+ instField->field.clazz->descriptor, instField->field.name,
+ objClass->descriptor);
+ *pFailure = VERIFY_ERROR_NO_FIELD;
+ goto bail;
+ }
+
+ if (mustBeLocal) {
+ /* for uninit ref, make sure it's defined by this class, not super */
+ if (instField < objClass->ifields ||
+ instField >= objClass->ifields + objClass->ifieldCount)
+ {
+ LOG_VFY("VFY: invalid constructor field access (field %s in %s)\n",
+ instField->field.name, objClass->descriptor);
+ *pFailure = VERIFY_ERROR_GENERIC;
+ goto bail;
+ }
+ }
+
+bail:
+ return instField;
+}
+
+/*
+ * Look up a static field.
+ *
+ * Returns a StaticField on success, returns NULL and sets "*pFailure"
+ * on failure.
+ */
+static StaticField* getStaticField(const Method* meth, int fieldIdx,
+ VerifyError* pFailure)
+{
+ StaticField* staticField;
+
+ staticField = dvmOptResolveStaticField(meth->clazz, fieldIdx, pFailure);
+ if (staticField == NULL) {
+ DexFile* pDexFile = meth->clazz->pDvmDex->pDexFile;
+ const DexFieldId* pFieldId;
+
+ pFieldId = dexGetFieldId(pDexFile, fieldIdx);
+
+ LOG_VFY("VFY: unable to resolve static field %u (%s) in %s\n", fieldIdx,
+ dexStringById(pDexFile, pFieldId->nameIdx),
+ dexStringByTypeIdx(pDexFile, pFieldId->classIdx));
+ assert(!VERIFY_OK(*pFailure));
+ goto bail;
+ }
+
+bail:
+ return staticField;
+}
+
+/*
+ * If "field" is marked "final", make sure this is the either <clinit>
+ * or <init> as appropriate.
+ *
+ * Sets "*pFailure" on failure.
+ */
+static void checkFinalFieldAccess(const Method* meth, const Field* field,
+ VerifyError* pFailure)
+{
+ if (!dvmIsFinalField(field))
+ return;
+
+ /* make sure we're in the same class */
+ if (meth->clazz != field->clazz) {
+ LOG_VFY_METH(meth, "VFY: can't modify final field %s.%s\n",
+ field->clazz->descriptor, field->name);
+ *pFailure = VERIFY_ERROR_ACCESS_FIELD;
+ return;
+ }
+
+ /*
+ * The VM spec descriptions of putfield and putstatic say that
+ * IllegalAccessError is only thrown when the instructions appear
+ * outside the declaring class. Our earlier attempts to restrict
+ * final field modification to constructors are, therefore, wrong.
+ */
+#if 0
+ /* make sure we're in the right kind of constructor */
+ if (dvmIsStaticField(field)) {
+ if (!isClassInitMethod(meth)) {
+ LOG_VFY_METH(meth,
+ "VFY: can't modify final static field outside <clinit>\n");
+ *pFailure = VERIFY_ERROR_GENERIC;
+ }
+ } else {
+ if (!isInitMethod(meth)) {
+ LOG_VFY_METH(meth,
+ "VFY: can't modify final field outside <init>\n");
+ *pFailure = VERIFY_ERROR_GENERIC;
+ }
+ }
+#endif
+}
+
+/*
+ * Make sure that the register type is suitable for use as an array index.
+ *
+ * Sets "*pFailure" if not.
+ */
+static void checkArrayIndexType(const Method* meth, RegType regType,
+ VerifyError* pFailure)
+{
+ if (VERIFY_OK(*pFailure)) {
+ /*
+ * The 1nr types are interchangeable at this level. We could
+ * do something special if we can definitively identify it as a
+ * float, but there's no real value in doing so.
+ */
+ checkTypeCategory(regType, kTypeCategory1nr, pFailure);
+ if (!VERIFY_OK(*pFailure)) {
+ LOG_VFY_METH(meth, "Invalid reg type for array index (%d)\n",
+ regType);
+ }
+ }
+}
+
+/*
+ * Check constraints on constructor return. Specifically, make sure that
+ * the "this" argument got initialized.
+ *
+ * The "this" argument to <init> uses code offset kUninitThisArgAddr, which
+ * puts it at the start of the list in slot 0. If we see a register with
+ * an uninitialized slot 0 reference, we know it somehow didn't get
+ * initialized.
+ *
+ * Returns "true" if all is well.
+ */
+static bool checkConstructorReturn(const Method* meth, const RegType* insnRegs,
+ const int insnRegCount)
+{
+ int i;
+
+ if (!isInitMethod(meth))
+ return true;
+
+ RegType uninitThis = regTypeFromUninitIndex(kUninitThisArgSlot);
+
+ for (i = 0; i < insnRegCount; i++) {
+ if (insnRegs[i] == uninitThis) {
+ LOG_VFY("VFY: <init> returning without calling superclass init\n");
+ return false;
+ }
+ }
+ return true;
+}
+
+/*
+ * Verify that the target instruction is not "move-exception". It's important
+ * that the only way to execute a move-exception is as the first instruction
+ * of an exception handler.
+ *
+ * Returns "true" if all is well, "false" if the target instruction is
+ * move-exception.
+ */
+static bool checkMoveException(const Method* meth, int insnIdx,
+ const char* logNote)
+{
+ assert(insnIdx >= 0 && insnIdx < (int)dvmGetMethodInsnsSize(meth));
+
+ if ((meth->insns[insnIdx] & 0xff) == OP_MOVE_EXCEPTION) {
+ LOG_VFY("VFY: invalid use of move-exception\n");
+ return false;
+ }
+ return true;
+}
+
+/*
+ * For the "move-exception" instruction at "insnIdx", which must be at an
+ * exception handler address, determine the first common superclass of
+ * all exceptions that can land here. (For javac output, we're probably
+ * looking at multiple spans of bytecode covered by one "try" that lands
+ * at an exception-specific "catch", but in general the handler could be
+ * shared for multiple exceptions.)
+ *
+ * Returns NULL if no matching exception handler can be found, or if the
+ * exception is not a subclass of Throwable.
+ */
+static ClassObject* getCaughtExceptionType(const Method* meth, int insnIdx,
+ VerifyError* pFailure)
+{
+ VerifyError localFailure;
+ const DexCode* pCode;
+ DexFile* pDexFile;
+ ClassObject* commonSuper = NULL;
+ bool foundPossibleHandler = false;
+ u4 handlersSize;
+ u4 offset;
+ u4 i;
+
+ pDexFile = meth->clazz->pDvmDex->pDexFile;
+ pCode = dvmGetMethodCode(meth);
+
+ if (pCode->triesSize != 0) {
+ handlersSize = dexGetHandlersSize(pCode);
+ offset = dexGetFirstHandlerOffset(pCode);
+ } else {
+ handlersSize = 0;
+ offset = 0;
+ }
+
+ for (i = 0; i < handlersSize; i++) {
+ DexCatchIterator iterator;
+ dexCatchIteratorInit(&iterator, pCode, offset);
+
+ for (;;) {
+ const DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
+
+ if (handler == NULL) {
+ break;
+ }
+
+ if (handler->address == (u4) insnIdx) {
+ ClassObject* clazz;
+ foundPossibleHandler = true;
+
+ if (handler->typeIdx == kDexNoIndex)
+ clazz = gDvm.classJavaLangThrowable;
+ else
+ clazz = dvmOptResolveClass(meth->clazz, handler->typeIdx,
+ &localFailure);
+
+ if (clazz == NULL) {
+ LOG_VFY("VFY: unable to resolve exception class %u (%s)\n",
+ handler->typeIdx,
+ dexStringByTypeIdx(pDexFile, handler->typeIdx));
+ /* TODO: do we want to keep going? If we don't fail
+ * this we run the risk of having a non-Throwable
+ * introduced at runtime. However, that won't pass
+ * an instanceof test, so is essentially harmless. */
+ } else {
+ if (commonSuper == NULL)
+ commonSuper = clazz;
+ else
+ commonSuper = findCommonSuperclass(clazz, commonSuper);
+ }
+ }
+ }
+
+ offset = dexCatchIteratorGetEndOffset(&iterator, pCode);
+ }
+
+ if (commonSuper == NULL) {
+ /* no catch blocks, or no catches with classes we can find */
+ LOG_VFY_METH(meth,
+ "VFY: unable to find exception handler at addr 0x%x\n", insnIdx);
+ *pFailure = VERIFY_ERROR_GENERIC;
+ } else {
+ // TODO: verify the class is an instance of Throwable?
+ }
+
+ return commonSuper;
+}
+
+/*
+ * Initialize the RegisterTable.
+ *
+ * Every instruction address can have a different set of information about
+ * what's in which register, but for verification purposes we only need to
+ * store it at branch target addresses (because we merge into that).
+ *
+ * By zeroing out the storage we are effectively initializing the register
+ * information to kRegTypeUnknown.
+ */
+static bool initRegisterTable(const Method* meth, const InsnFlags* insnFlags,
+ RegisterTable* regTable, RegisterTrackingMode trackRegsFor)
+{
+ const int insnsSize = dvmGetMethodInsnsSize(meth);
+ int i;
+
+ regTable->insnRegCountPlus = meth->registersSize + kExtraRegs;
+ regTable->addrRegs = (RegType**) calloc(insnsSize, sizeof(RegType*));
+ if (regTable->addrRegs == NULL)
+ return false;
+
+ assert(insnsSize > 0);
+
+ /*
+ * "All" means "every address that holds the start of an instruction".
+ * "Branches" and "GcPoints" mean just those addresses.
+ *
+ * "GcPoints" fills about half the addresses, "Branches" about 15%.
+ */
+ int interestingCount = 0;
+ //int insnCount = 0;
+
+ for (i = 0; i < insnsSize; i++) {
+ bool interesting;
+
+ switch (trackRegsFor) {
+ case kTrackRegsAll:
+ interesting = dvmInsnIsOpcode(insnFlags, i);
+ break;
+ case kTrackRegsGcPoints:
+ interesting = dvmInsnIsGcPoint(insnFlags, i) ||
+ dvmInsnIsBranchTarget(insnFlags, i);
+ break;
+ case kTrackRegsBranches:
+ interesting = dvmInsnIsBranchTarget(insnFlags, i);
+ break;
+ default:
+ dvmAbort();
+ return false;
+ }
+
+ if (interesting)
+ interestingCount++;
+
+ /* count instructions, for display only */
+ //if (dvmInsnIsOpcode(insnFlags, i))
+ // insnCount++;
+ }
+
+ regTable->regAlloc = (RegType*)
+ calloc(regTable->insnRegCountPlus * interestingCount, sizeof(RegType));
+ if (regTable->regAlloc == NULL)
+ return false;
+
+ RegType* regPtr = regTable->regAlloc;
+ for (i = 0; i < insnsSize; i++) {
+ bool interesting;
+
+ switch (trackRegsFor) {
+ case kTrackRegsAll:
+ interesting = dvmInsnIsOpcode(insnFlags, i);
+ break;
+ case kTrackRegsGcPoints:
+ interesting = dvmInsnIsGcPoint(insnFlags, i) ||
+ dvmInsnIsBranchTarget(insnFlags, i);
+ break;
+ case kTrackRegsBranches:
+ interesting = dvmInsnIsBranchTarget(insnFlags, i);
+ break;
+ default:
+ dvmAbort();
+ return false;
+ }
+
+ if (interesting) {
+ regTable->addrRegs[i] = regPtr;
+ regPtr += regTable->insnRegCountPlus;
+ }
+ }
+
+ //LOGD("Tracking registers for %d, total %d of %d(%d) (%d%%)\n",
+ // TRACK_REGS_FOR, interestingCount, insnCount, insnsSize,
+ // (interestingCount*100) / insnCount);
+
+ assert(regPtr - regTable->regAlloc ==
+ regTable->insnRegCountPlus * interestingCount);
+ assert(regTable->addrRegs[0] != NULL);
+ return true;
+}
+
+
+/*
+ * Verify that the arguments in a filled-new-array instruction are valid.
+ *
+ * "resClass" is the class refered to by pDecInsn->vB.
+ */
+static void verifyFilledNewArrayRegs(const Method* meth,
+ const RegType* insnRegs, const int insnRegCount,
+ const DecodedInstruction* pDecInsn, ClassObject* resClass, bool isRange,
+ VerifyError* pFailure)
+{
+ u4 argCount = pDecInsn->vA;
+ RegType expectedType;
+ PrimitiveType elemType;
+ unsigned int ui;
+
+ assert(dvmIsArrayClass(resClass));
+ elemType = resClass->elementClass->primitiveType;
+ if (elemType == PRIM_NOT) {
+ expectedType = regTypeFromClass(resClass->elementClass);
+ } else {
+ expectedType = primitiveTypeToRegType(elemType);
+ }
+ //LOGI("filled-new-array: %s -> %d\n", resClass->descriptor, expectedType);
+
+ /*
+ * Verify each register. If "argCount" is bad, verifyRegisterType()
+ * will run off the end of the list and fail. It's legal, if silly,
+ * for argCount to be zero.
+ */
+ for (ui = 0; ui < argCount; ui++) {
+ u4 getReg;
+
+ if (isRange)
+ getReg = pDecInsn->vC + ui;
+ else
+ getReg = pDecInsn->arg[ui];
+
+ verifyRegisterType(insnRegs, insnRegCount, getReg, expectedType,
+ pFailure);
+ if (!VERIFY_OK(*pFailure)) {
+ LOG_VFY("VFY: filled-new-array arg %u(%u) not valid\n", ui, getReg);
+ return;
+ }
+ }
+}
+
+
+/*
+ * Replace an instruction with "throw-verification-error". This allows us to
+ * defer error reporting until the code path is first used.
+ *
+ * This is expected to be called during "just in time" verification, not
+ * from within dexopt. (Verification failures in dexopt will result in
+ * postponement of verification to first use of the class.)
+ *
+ * The throw-verification-error instruction requires two code units. Some
+ * of the replaced instructions require three; the third code unit will
+ * receive a "nop". The instruction's length will be left unchanged
+ * in "insnFlags".
+ *
+ * The verifier explicitly locks out breakpoint activity, so there should
+ * be no clashes with the debugger.
+ *
+ * Returns "true" on success.
+ */
+static bool replaceFailingInstruction(const Method* meth, InsnFlags* insnFlags,
+ int insnIdx, VerifyError failure)
+{
+ VerifyErrorRefType refType;
+ const u2* oldInsns = meth->insns + insnIdx;
+ u2 oldInsn = *oldInsns;
+ bool result = false;
+
+ if (gDvm.optimizing)
+ LOGD("Weird: RFI during dexopt?");
+
+ //LOGD(" was 0x%04x\n", oldInsn);
+ u2* newInsns = (u2*) meth->insns + insnIdx;
+
+ /*
+ * Generate the new instruction out of the old.
+ *
+ * First, make sure this is an instruction we're expecting to stomp on.
+ */
+ switch (oldInsn & 0xff) {
+ case OP_CONST_CLASS: // insn[1] == class ref, 2 bytes
+ case OP_CHECK_CAST:
+ case OP_INSTANCE_OF:
+ case OP_NEW_INSTANCE:
+ case OP_NEW_ARRAY:
+ case OP_FILLED_NEW_ARRAY: // insn[1] == class ref, 3 bytes
+ case OP_FILLED_NEW_ARRAY_RANGE:
+ refType = VERIFY_ERROR_REF_CLASS;
+ break;
+
+ case OP_IGET: // insn[1] == field ref, 2 bytes
+ case OP_IGET_BOOLEAN:
+ case OP_IGET_BYTE:
+ case OP_IGET_CHAR:
+ case OP_IGET_SHORT:
+ case OP_IGET_WIDE:
+ case OP_IGET_OBJECT:
+ case OP_IPUT:
+ case OP_IPUT_BOOLEAN:
+ case OP_IPUT_BYTE:
+ case OP_IPUT_CHAR:
+ case OP_IPUT_SHORT:
+ case OP_IPUT_WIDE:
+ case OP_IPUT_OBJECT:
+ case OP_SGET:
+ case OP_SGET_BOOLEAN:
+ case OP_SGET_BYTE:
+ case OP_SGET_CHAR:
+ case OP_SGET_SHORT:
+ case OP_SGET_WIDE:
+ case OP_SGET_OBJECT:
+ case OP_SPUT:
+ case OP_SPUT_BOOLEAN:
+ case OP_SPUT_BYTE:
+ case OP_SPUT_CHAR:
+ case OP_SPUT_SHORT:
+ case OP_SPUT_WIDE:
+ case OP_SPUT_OBJECT:
+ refType = VERIFY_ERROR_REF_FIELD;
+ break;
+
+ case OP_INVOKE_VIRTUAL: // insn[1] == method ref, 3 bytes
+ case OP_INVOKE_VIRTUAL_RANGE:
+ case OP_INVOKE_SUPER:
+ case OP_INVOKE_SUPER_RANGE:
+ case OP_INVOKE_DIRECT:
+ case OP_INVOKE_DIRECT_RANGE:
+ case OP_INVOKE_STATIC:
+ case OP_INVOKE_STATIC_RANGE:
+ case OP_INVOKE_INTERFACE:
+ case OP_INVOKE_INTERFACE_RANGE:
+ refType = VERIFY_ERROR_REF_METHOD;
+ break;
+
+ default:
+ /* could handle this in a generic way, but this is probably safer */
+ LOG_VFY("GLITCH: verifier asked to replace opcode 0x%02x\n",
+ oldInsn & 0xff);
+ goto bail;
+ }
+
+ /* write a NOP over the third code unit, if necessary */
+ int width = dvmInsnGetWidth(insnFlags, insnIdx);
+ switch (width) {
+ case 2:
+ /* nothing to do */
+ break;
+ case 3:
+ dvmDexChangeDex2(meth->clazz->pDvmDex, newInsns+2, OP_NOP);
+ //newInsns[2] = OP_NOP;
+ break;
+ default:
+ /* whoops */
+ LOGE("ERROR: stomped a %d-unit instruction with a verifier error\n",
+ width);
+ dvmAbort();
+ }
+
+ /* encode the opcode, with the failure code in the high byte */
+ u2 newVal = OP_THROW_VERIFICATION_ERROR |
+ (failure << 8) | (refType << (8 + kVerifyErrorRefTypeShift));
+ //newInsns[0] = newVal;
+ dvmDexChangeDex2(meth->clazz->pDvmDex, newInsns, newVal);
+
+ result = true;
+
+bail:
+ return result;
+}
+
+
+/*
+ * ===========================================================================
+ * Entry point and driver loop
+ * ===========================================================================
+ */
+
+/*
+ * Entry point for the detailed code-flow analysis.
+ */
+bool dvmVerifyCodeFlow(VerifierData* vdata)
+{
+ bool result = false;
+ const Method* meth = vdata->method;
+ const int insnsSize = vdata->insnsSize;
+ const bool generateRegisterMap = gDvm.generateRegisterMaps;
+ RegisterTable regTable;
+
+ memset(&regTable, 0, sizeof(regTable));
+
+#ifndef NDEBUG
+ checkMergeTab(); // only need to do this if table gets updated
+#endif
+
+ /*
+ * We rely on these for verification of const-class, const-string,
+ * and throw instructions. Make sure we have them.
+ */
+ if (gDvm.classJavaLangClass == NULL)
+ gDvm.classJavaLangClass =
+ dvmFindSystemClassNoInit("Ljava/lang/Class;");
+ if (gDvm.classJavaLangString == NULL)
+ gDvm.classJavaLangString =
+ dvmFindSystemClassNoInit("Ljava/lang/String;");
+ if (gDvm.classJavaLangThrowable == NULL) {
+ gDvm.classJavaLangThrowable =
+ dvmFindSystemClassNoInit("Ljava/lang/Throwable;");
+ gDvm.offJavaLangThrowable_cause =
+ dvmFindFieldOffset(gDvm.classJavaLangThrowable,
+ "cause", "Ljava/lang/Throwable;");
+ }
+ if (gDvm.classJavaLangObject == NULL)
+ gDvm.classJavaLangObject =
+ dvmFindSystemClassNoInit("Ljava/lang/Object;");
+
+ if (meth->registersSize * insnsSize > 4*1024*1024) {
+ LOG_VFY_METH(meth,
+ "VFY: warning: method is huge (regs=%d insnsSize=%d)\n",
+ meth->registersSize, insnsSize);
+ /* might be bogus data, might be some huge generated method */
+ }
+
+ /*
+ * Create register lists, and initialize them to "Unknown". If we're
+ * also going to create the register map, we need to retain the
+ * register lists for a larger set of addresses.
+ */
+ if (!initRegisterTable(meth, vdata->insnFlags, &regTable,
+ generateRegisterMap ? kTrackRegsGcPoints : kTrackRegsBranches))
+ goto bail;
+
+ vdata->addrRegs = NULL; /* don't set this until we need it */
+
+ /*
+ * Initialize the types of the registers that correspond to the
+ * method arguments. We can determine this from the method signature.
+ */
+ if (!setTypesFromSignature(meth, regTable.addrRegs[0], vdata->uninitMap))
+ goto bail;
+
+ /*
+ * Run the verifier.
+ */
+ if (!doCodeVerification(meth, vdata->insnFlags, &regTable, vdata->uninitMap))
+ goto bail;
+
+ /*
+ * Generate a register map.
+ */
+ if (generateRegisterMap) {
+ vdata->addrRegs = regTable.addrRegs;
+
+ RegisterMap* pMap = dvmGenerateRegisterMapV(vdata);
+ if (pMap != NULL) {
+ /*
+ * Tuck it into the Method struct. It will either get used
+ * directly or, if we're in dexopt, will be packed up and
+ * appended to the DEX file.
+ */
+ dvmSetRegisterMap((Method*)meth, pMap);
+ }
+ }
+
+ /*
+ * Success.
+ */
+ result = true;
+
+bail:
+ free(regTable.addrRegs);
+ free(regTable.regAlloc);
+ return result;
+}
+
+/*
+ * Grind through the instructions.
+ *
+ * The basic strategy is as outlined in v3 4.11.1.2: set the "changed" bit
+ * on the first instruction, process it (setting additional "changed" bits),
+ * and repeat until there are no more.
+ *
+ * v3 4.11.1.1
+ * - (N/A) operand stack is always the same size
+ * - operand stack [registers] contain the correct types of values
+ * - local variables [registers] contain the correct types of values
+ * - methods are invoked with the appropriate arguments
+ * - fields are assigned using values of appropriate types
+ * - opcodes have the correct type values in operand registers
+ * - there is never an uninitialized class instance in a local variable in
+ * code protected by an exception handler (operand stack is okay, because
+ * the operand stack is discarded when an exception is thrown) [can't
+ * know what's a local var w/o the debug info -- should fall out of
+ * register typing]
+ *
+ * v3 4.11.1.2
+ * - execution cannot fall off the end of the code
+ *
+ * (We also do many of the items described in the "static checks" sections,
+ * because it's easier to do them here.)
+ *
+ * We need an array of RegType values, one per register, for every
+ * instruction. In theory this could become quite large -- up to several
+ * megabytes for a monster function. For self-preservation we reject
+ * anything that requires more than a certain amount of memory. (Typical
+ * "large" should be on the order of 4K code units * 8 registers.) This
+ * will likely have to be adjusted.
+ *
+ *
+ * The spec forbids backward branches when there's an uninitialized reference
+ * in a register. The idea is to prevent something like this:
+ * loop:
+ * move r1, r0
+ * new-instance r0, MyClass
+ * ...
+ * if-eq rN, loop // once
+ * initialize r0
+ *
+ * This leaves us with two different instances, both allocated by the
+ * same instruction, but only one is initialized. The scheme outlined in
+ * v3 4.11.1.4 wouldn't catch this, so they work around it by preventing
+ * backward branches. We achieve identical results without restricting
+ * code reordering by specifying that you can't execute the new-instance
+ * instruction if a register contains an uninitialized instance created
+ * by that same instrutcion.
+ */
+static bool doCodeVerification(const Method* meth, InsnFlags* insnFlags,
+ RegisterTable* regTable, UninitInstanceMap* uninitMap)
+{
+ const int insnsSize = dvmGetMethodInsnsSize(meth);
+ RegType workRegs[meth->registersSize + kExtraRegs];
+ bool result = false;
+ bool debugVerbose = false;
+ int insnIdx, startGuess;
+
+ /*
+ * Begin by marking the first instruction as "changed".
+ */
+ dvmInsnSetChanged(insnFlags, 0, true);
+
+ if (doVerboseLogging(meth)) {
+ IF_LOGI() {
+ char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+ LOGI("Now verifying: %s.%s %s (ins=%d regs=%d)\n",
+ meth->clazz->descriptor, meth->name, desc,
+ meth->insSize, meth->registersSize);
+ LOGI(" ------ [0 4 8 12 16 20 24 28 32 36\n");
+ free(desc);
+ }
+ debugVerbose = true;
+ gDebugVerbose = true;
+ } else {
+ gDebugVerbose = false;
+ }
+
+ startGuess = 0;
+
+ /*
+ * Continue until no instructions are marked "changed".
+ */
+ while (true) {
+ /*
+ * Find the first marked one. Use "startGuess" as a way to find
+ * one quickly.
+ */
+ for (insnIdx = startGuess; insnIdx < insnsSize; insnIdx++) {
+ if (dvmInsnIsChanged(insnFlags, insnIdx))
+ break;
+ }
+
+ if (insnIdx == insnsSize) {
+ if (startGuess != 0) {
+ /* try again, starting from the top */
+ startGuess = 0;
+ continue;
+ } else {
+ /* all flags are clear */
+ break;
+ }
+ }
+
+ /*
+ * We carry the working set of registers from instruction to
+ * instruction. If this address can be the target of a branch
+ * (or throw) instruction, or if we're skipping around chasing
+ * "changed" flags, we need to load the set of registers from
+ * the table.
+ *
+ * Because we always prefer to continue on to the next instruction,
+ * we should never have a situation where we have a stray
+ * "changed" flag set on an instruction that isn't a branch target.
+ */
+ if (dvmInsnIsBranchTarget(insnFlags, insnIdx)) {
+ RegType* insnRegs = getRegisterLine(regTable, insnIdx);
+ assert(insnRegs != NULL);
+ copyRegisters(workRegs, insnRegs, meth->registersSize + kExtraRegs);
+
+ if (debugVerbose) {
+ dumpRegTypes(meth, insnFlags, workRegs, insnIdx, NULL,uninitMap,
+ SHOW_REG_DETAILS);
+ }
+
+ } else {
+ if (debugVerbose) {
+ dumpRegTypes(meth, insnFlags, workRegs, insnIdx, NULL,uninitMap,
+ SHOW_REG_DETAILS);
+ }
+
+#ifndef NDEBUG
+ /*
+ * Sanity check: retrieve the stored register line (assuming
+ * a full table) and make sure it actually matches.
+ */
+ RegType* insnRegs = getRegisterLine(regTable, insnIdx);
+ if (insnRegs != NULL &&
+ compareRegisters(workRegs, insnRegs,
+ meth->registersSize + kExtraRegs) != 0)
+ {
+ char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+ LOG_VFY("HUH? workRegs diverged in %s.%s %s\n",
+ meth->clazz->descriptor, meth->name, desc);
+ free(desc);
+ dumpRegTypes(meth, insnFlags, workRegs, 0, "work",
+ uninitMap, DRT_SHOW_REF_TYPES | DRT_SHOW_LOCALS);
+ dumpRegTypes(meth, insnFlags, insnRegs, 0, "insn",
+ uninitMap, DRT_SHOW_REF_TYPES | DRT_SHOW_LOCALS);
+ }
+#endif
+ }
+
+ //LOGI("process %s.%s %s %d\n",
+ // meth->clazz->descriptor, meth->name, meth->descriptor, insnIdx);
+ if (!verifyInstruction(meth, insnFlags, regTable, workRegs, insnIdx,
+ uninitMap, &startGuess))
+ {
+ //LOGD("+++ %s bailing at %d\n", meth->name, insnIdx);
+ goto bail;
+ }
+
+#if 0
+ {
+ static const int gcMask = kInstrCanBranch | kInstrCanSwitch |
+ kInstrCanThrow | kInstrCanReturn;
+ OpCode opCode = *(meth->insns + insnIdx) & 0xff;
+ int flags = dexGetInstrFlags(gDvm.instrFlags, opCode);
+
+ /* 8, 16, 32, or 32*n -bit regs */
+ int regWidth = (meth->registersSize + 7) / 8;
+ if (regWidth == 3)
+ regWidth = 4;
+ if (regWidth > 4) {
+ regWidth = ((regWidth + 3) / 4) * 4;
+ if (false) {
+ LOGW("WOW: %d regs -> %d %s.%s\n",
+ meth->registersSize, regWidth,
+ meth->clazz->descriptor, meth->name);
+ //x = true;
+ }
+ }
+
+ if ((flags & gcMask) != 0) {
+ /* this is a potential GC point */
+ gDvm__gcInstr++;
+
+ if (insnsSize < 256)
+ gDvm__gcData += 1;
+ else
+ gDvm__gcData += 2;
+ gDvm__gcData += regWidth;
+ }
+ gDvm__gcSimpleData += regWidth;
+
+ gDvm__totalInstr++;
+ }
+#endif
+
+ /*
+ * Clear "changed" and mark as visited.
+ */
+ dvmInsnSetVisited(insnFlags, insnIdx, true);
+ dvmInsnSetChanged(insnFlags, insnIdx, false);
+ }
+
+ if (DEAD_CODE_SCAN && !IS_METHOD_FLAG_SET(meth, METHOD_ISWRITABLE)) {
+ /*
+ * Scan for dead code. There's nothing "evil" about dead code
+ * (besides the wasted space), but it indicates a flaw somewhere
+ * down the line, possibly in the verifier.
+ *
+ * If we've rewritten "always throw" instructions into the stream,
+ * we are almost certainly going to have some dead code.
+ */
+ int deadStart = -1;
+ for (insnIdx = 0; insnIdx < insnsSize;
+ insnIdx += dvmInsnGetWidth(insnFlags, insnIdx))
+ {
+ /*
+ * Switch-statement data doesn't get "visited" by scanner. It
+ * may or may not be preceded by a padding NOP.
+ */
+ int instr = meth->insns[insnIdx];
+ if (instr == kPackedSwitchSignature ||
+ instr == kSparseSwitchSignature ||
+ instr == kArrayDataSignature ||
+ (instr == OP_NOP &&
+ (meth->insns[insnIdx+1] == kPackedSwitchSignature ||
+ meth->insns[insnIdx+1] == kSparseSwitchSignature ||
+ meth->insns[insnIdx+1] == kArrayDataSignature)))
+ {
+ dvmInsnSetVisited(insnFlags, insnIdx, true);
+ }
+
+ if (!dvmInsnIsVisited(insnFlags, insnIdx)) {
+ if (deadStart < 0)
+ deadStart = insnIdx;
+ } else if (deadStart >= 0) {
+ IF_LOGD() {
+ char* desc =
+ dexProtoCopyMethodDescriptor(&meth->prototype);
+ LOGD("VFY: dead code 0x%04x-%04x in %s.%s %s\n",
+ deadStart, insnIdx-1,
+ meth->clazz->descriptor, meth->name, desc);
+ free(desc);
+ }
+
+ deadStart = -1;
+ }
+ }
+ if (deadStart >= 0) {
+ IF_LOGD() {
+ char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+ LOGD("VFY: dead code 0x%04x-%04x in %s.%s %s\n",
+ deadStart, insnIdx-1,
+ meth->clazz->descriptor, meth->name, desc);
+ free(desc);
+ }
+ }
+ }
+
+ result = true;
+
+bail:
+ return result;
+}
+
+
+/*
+ * Perform verification for a single instruction.
+ *
+ * This requires fully decoding the instruction to determine the effect
+ * it has on registers.
+ *
+ * Finds zero or more following instructions and sets the "changed" flag
+ * if execution at that point needs to be (re-)evaluated. Register changes
+ * are merged into "regTypes" at the target addresses. Does not set or
+ * clear any other flags in "insnFlags".
+ *
+ * This may alter meth->insns if we need to replace an instruction with
+ * throw-verification-error.
+ */
+static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags,
+ RegisterTable* regTable, RegType* workRegs, int insnIdx,
+ UninitInstanceMap* uninitMap, int* pStartGuess)
+{
+ const int insnsSize = dvmGetMethodInsnsSize(meth);
+ const u2* insns = meth->insns + insnIdx;
+ bool result = false;
+
+ /*
+ * Once we finish decoding the instruction, we need to figure out where
+ * we can go from here. There are three possible ways to transfer
+ * control to another statement:
+ *
+ * (1) Continue to the next instruction. Applies to all but
+ * unconditional branches, method returns, and exception throws.
+ * (2) Branch to one or more possible locations. Applies to branches
+ * and switch statements.
+ * (3) Exception handlers. Applies to any instruction that can
+ * throw an exception that is handled by an encompassing "try"
+ * block.
+ *
+ * We can also return, in which case there is no successor instruction
+ * from this point.
+ *
+ * The behavior can be determined from the InstructionFlags.
+ */
+
+ const DexFile* pDexFile = meth->clazz->pDvmDex->pDexFile;
+ RegType entryRegs[meth->registersSize + kExtraRegs];
+ ClassObject* resClass;
+ int branchTarget = 0;
+ const int insnRegCount = meth->registersSize;
+ RegType tmpType;
+ DecodedInstruction decInsn;
+ bool justSetResult = false;
+ VerifyError failure = VERIFY_ERROR_NONE;
+
+#ifndef NDEBUG
+ memset(&decInsn, 0x81, sizeof(decInsn));
+#endif
+ dexDecodeInstruction(gDvm.instrFormat, insns, &decInsn);
+
+ int nextFlags = dexGetInstrFlags(gDvm.instrFlags, decInsn.opCode);
+
+ /*
+ * Make a copy of the previous register state. If the instruction
+ * throws an exception, we merge *this* into the destination rather
+ * than workRegs, because we don't want the result from the "successful"
+ * code path (e.g. a check-cast that "improves" a type) to be visible
+ * to the exception handler.
+ */
+ if ((nextFlags & kInstrCanThrow) != 0 && dvmInsnIsInTry(insnFlags, insnIdx))
+ {
+ copyRegisters(entryRegs, workRegs, meth->registersSize + kExtraRegs);
+ } else {
+#ifndef NDEBUG
+ memset(entryRegs, 0xdd,
+ (meth->registersSize + kExtraRegs) * sizeof(RegType));
+#endif
+ }
+
+ switch (decInsn.opCode) {
+ case OP_NOP:
+ /*
+ * A "pure" NOP has no effect on anything. Data tables start with
+ * a signature that looks like a NOP; if we see one of these in
+ * the course of executing code then we have a problem.
+ */
+ if (decInsn.vA != 0) {
+ LOG_VFY("VFY: encountered data table in instruction stream\n");
+ failure = VERIFY_ERROR_GENERIC;
+ }
+ break;
+
+ case OP_MOVE:
+ case OP_MOVE_FROM16:
+ case OP_MOVE_16:
+ copyRegister1(workRegs, insnRegCount, decInsn.vA, decInsn.vB,
+ kTypeCategory1nr, &failure);
+ break;
+ case OP_MOVE_WIDE:
+ case OP_MOVE_WIDE_FROM16:
+ case OP_MOVE_WIDE_16:
+ copyRegister2(workRegs, insnRegCount, decInsn.vA, decInsn.vB, &failure);
+ break;
+ case OP_MOVE_OBJECT:
+ case OP_MOVE_OBJECT_FROM16:
+ case OP_MOVE_OBJECT_16:
+ copyRegister1(workRegs, insnRegCount, decInsn.vA, decInsn.vB,
+ kTypeCategoryRef, &failure);
+ break;
+
+ /*
+ * The move-result instructions copy data out of a "pseudo-register"
+ * with the results from the last method invocation. In practice we
+ * might want to hold the result in an actual CPU register, so the
+ * Dalvik spec requires that these only appear immediately after an
+ * invoke or filled-new-array.
+ *
+ * These calls invalidate the "result" register. (This is now
+ * redundant with the reset done below, but it can make the debug info
+ * easier to read in some cases.)
+ */
+ case OP_MOVE_RESULT:
+ copyResultRegister1(workRegs, insnRegCount, decInsn.vA,
+ kTypeCategory1nr, &failure);
+ break;
+ case OP_MOVE_RESULT_WIDE:
+ copyResultRegister2(workRegs, insnRegCount, decInsn.vA, &failure);
+ break;
+ case OP_MOVE_RESULT_OBJECT:
+ copyResultRegister1(workRegs, insnRegCount, decInsn.vA,
+ kTypeCategoryRef, &failure);
+ break;
+
+ case OP_MOVE_EXCEPTION:
+ /*
+ * This statement can only appear as the first instruction in an
+ * exception handler (though not all exception handlers need to
+ * have one of these). We verify that as part of extracting the
+ * exception type from the catch block list.
+ *
+ * "resClass" will hold the closest common superclass of all
+ * exceptions that can be handled here.
+ */
+ resClass = getCaughtExceptionType(meth, insnIdx, &failure);
+ if (resClass == NULL) {
+ assert(!VERIFY_OK(failure));
+ } else {
+ setRegisterType(workRegs, insnRegCount, decInsn.vA,
+ regTypeFromClass(resClass), &failure);
+ }
+ break;
+
+ case OP_RETURN_VOID:
+ if (!checkConstructorReturn(meth, workRegs, insnRegCount)) {
+ failure = VERIFY_ERROR_GENERIC;
+ } else if (getMethodReturnType(meth) != kRegTypeUnknown) {
+ LOG_VFY("VFY: return-void not expected\n");
+ failure = VERIFY_ERROR_GENERIC;
+ }
+ break;
+ case OP_RETURN:
+ if (!checkConstructorReturn(meth, workRegs, insnRegCount)) {
+ failure = VERIFY_ERROR_GENERIC;
+ } else {
+ /* check the method signature */
+ RegType returnType = getMethodReturnType(meth);
+ checkTypeCategory(returnType, kTypeCategory1nr, &failure);
+ if (!VERIFY_OK(failure))
+ LOG_VFY("VFY: return-32 not expected\n");
+
+ /* check the register contents */
+ returnType = getRegisterType(workRegs, insnRegCount, decInsn.vA,
+ &failure);
+ checkTypeCategory(returnType, kTypeCategory1nr, &failure);
+ if (!VERIFY_OK(failure))
+ LOG_VFY("VFY: return-32 on invalid register v%d\n", decInsn.vA);
+ }
+ break;
+ case OP_RETURN_WIDE:
+ if (!checkConstructorReturn(meth, workRegs, insnRegCount)) {
+ failure = VERIFY_ERROR_GENERIC;
+ } else {
+ RegType returnType, returnTypeHi;
+
+ /* check the method signature */
+ returnType = getMethodReturnType(meth);
+ checkTypeCategory(returnType, kTypeCategory2, &failure);
+ if (!VERIFY_OK(failure))
+ LOG_VFY("VFY: return-wide not expected\n");
+
+ /* check the register contents */
+ returnType = getRegisterType(workRegs, insnRegCount, decInsn.vA,
+ &failure);
+ returnTypeHi = getRegisterType(workRegs, insnRegCount,
+ decInsn.vA +1, &failure);
+ if (VERIFY_OK(failure)) {
+ checkTypeCategory(returnType, kTypeCategory2, &failure);
+ checkWidePair(returnType, returnTypeHi, &failure);
+ }
+ if (!VERIFY_OK(failure)) {
+ LOG_VFY("VFY: return-wide on invalid register pair v%d\n",
+ decInsn.vA);
+ }
+ }
+ break;
+ case OP_RETURN_OBJECT:
+ if (!checkConstructorReturn(meth, workRegs, insnRegCount)) {
+ failure = VERIFY_ERROR_GENERIC;
+ } else {
+ RegType returnType = getMethodReturnType(meth);
+ checkTypeCategory(returnType, kTypeCategoryRef, &failure);
+ if (!VERIFY_OK(failure)) {
+ LOG_VFY("VFY: return-object not expected\n");
+ break;
+ }
+
+ /* returnType is the *expected* return type, not register value */
+ assert(returnType != kRegTypeZero);
+ assert(!regTypeIsUninitReference(returnType));
+
+ /*
+ * Verify that the reference in vAA is an instance of the type
+ * in "returnType". The Zero type is allowed here. If the
+ * method is declared to return an interface, then any
+ * initialized reference is acceptable.
+ *
+ * Note getClassFromRegister fails if the register holds an
+ * uninitialized reference, so we do not allow them to be
+ * returned.
+ */
+ ClassObject* declClass;
+
+ declClass = regTypeInitializedReferenceToClass(returnType);
+ resClass = getClassFromRegister(workRegs, insnRegCount,
+ decInsn.vA, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+ if (resClass != NULL) {
+ if (!dvmIsInterfaceClass(declClass) &&
+ !dvmInstanceof(resClass, declClass))
+ {
+ LOG_VFY("VFY: returning %s (cl=%p), declared %s (cl=%p)\n",
+ resClass->descriptor, resClass->classLoader,
+ declClass->descriptor, declClass->classLoader);
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ }
+ }
+ break;
+
+ case OP_CONST_4:
+ case OP_CONST_16:
+ case OP_CONST:
+ /* could be boolean, int, float, or a null reference */
+ setRegisterType(workRegs, insnRegCount, decInsn.vA,
+ dvmDetermineCat1Const((s4)decInsn.vB), &failure);
+ break;
+ case OP_CONST_HIGH16:
+ /* could be boolean, int, float, or a null reference */
+ setRegisterType(workRegs, insnRegCount, decInsn.vA,
+ dvmDetermineCat1Const((s4) decInsn.vB << 16), &failure);
+ break;
+ case OP_CONST_WIDE_16:
+ case OP_CONST_WIDE_32:
+ case OP_CONST_WIDE:
+ case OP_CONST_WIDE_HIGH16:
+ /* could be long or double; default to long and allow conversion */
+ setRegisterType(workRegs, insnRegCount, decInsn.vA,
+ kRegTypeLongLo, &failure);
+ break;
+ case OP_CONST_STRING:
+ case OP_CONST_STRING_JUMBO:
+ assert(gDvm.classJavaLangString != NULL);
+ setRegisterType(workRegs, insnRegCount, decInsn.vA,
+ regTypeFromClass(gDvm.classJavaLangString), &failure);
+ break;
+ case OP_CONST_CLASS:
+ assert(gDvm.classJavaLangClass != NULL);
+ /* make sure we can resolve the class; access check is important */
+ resClass = dvmOptResolveClass(meth->clazz, decInsn.vB, &failure);
+ if (resClass == NULL) {
+ const char* badClassDesc = dexStringByTypeIdx(pDexFile, decInsn.vB);
+ dvmLogUnableToResolveClass(badClassDesc, meth);
+ LOG_VFY("VFY: unable to resolve const-class %d (%s) in %s\n",
+ decInsn.vB, badClassDesc, meth->clazz->descriptor);
+ assert(failure != VERIFY_ERROR_GENERIC);
+ } else {
+ setRegisterType(workRegs, insnRegCount, decInsn.vA,
+ regTypeFromClass(gDvm.classJavaLangClass), &failure);
+ }
+ break;
+
+ case OP_MONITOR_ENTER:
+ case OP_MONITOR_EXIT:
+ tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vA, &failure);
+ if (VERIFY_OK(failure)) {
+ if (!regTypeIsReference(tmpType)) {
+ LOG_VFY("VFY: monitor op on non-object\n");
+ failure = VERIFY_ERROR_GENERIC;
+ }
+ }
+ break;
+
+ case OP_CHECK_CAST:
+ /*
+ * If this instruction succeeds, we will promote register vA to
+ * the type in vB. (This could be a demotion -- not expected, so
+ * we don't try to address it.)
+ *
+ * If it fails, an exception is thrown, which we deal with later
+ * by ignoring the update to decInsn.vA when branching to a handler.
+ */
+ resClass = dvmOptResolveClass(meth->clazz, decInsn.vB, &failure);
+ if (resClass == NULL) {
+ const char* badClassDesc = dexStringByTypeIdx(pDexFile, decInsn.vB);
+ dvmLogUnableToResolveClass(badClassDesc, meth);
+ LOG_VFY("VFY: unable to resolve check-cast %d (%s) in %s\n",
+ decInsn.vB, badClassDesc, meth->clazz->descriptor);
+ assert(failure != VERIFY_ERROR_GENERIC);
+ } else {
+ RegType origType;
+
+ origType = getRegisterType(workRegs, insnRegCount, decInsn.vA,
+ &failure);
+ if (!VERIFY_OK(failure))
+ break;
+ if (!regTypeIsReference(origType)) {
+ LOG_VFY("VFY: check-cast on non-reference in v%u\n",decInsn.vA);
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ setRegisterType(workRegs, insnRegCount, decInsn.vA,
+ regTypeFromClass(resClass), &failure);
+ }
+ break;
+ case OP_INSTANCE_OF:
+ /* make sure we're checking a reference type */
+ tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vB, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+ if (!regTypeIsReference(tmpType)) {
+ LOG_VFY("VFY: vB not a reference (%d)\n", tmpType);
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ /* make sure we can resolve the class; access check is important */
+ resClass = dvmOptResolveClass(meth->clazz, decInsn.vC, &failure);
+ if (resClass == NULL) {
+ const char* badClassDesc = dexStringByTypeIdx(pDexFile, decInsn.vC);
+ dvmLogUnableToResolveClass(badClassDesc, meth);
+ LOG_VFY("VFY: unable to resolve instanceof %d (%s) in %s\n",
+ decInsn.vC, badClassDesc, meth->clazz->descriptor);
+ assert(failure != VERIFY_ERROR_GENERIC);
+ } else {
+ /* result is boolean */
+ setRegisterType(workRegs, insnRegCount, decInsn.vA,
+ kRegTypeBoolean, &failure);
+ }
+ break;
+
+ case OP_ARRAY_LENGTH:
+ resClass = getClassFromRegister(workRegs, insnRegCount,
+ decInsn.vB, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+ if (resClass != NULL && !dvmIsArrayClass(resClass)) {
+ LOG_VFY("VFY: array-length on non-array\n");
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ setRegisterType(workRegs, insnRegCount, decInsn.vA, kRegTypeInteger,
+ &failure);
+ break;
+
+ case OP_NEW_INSTANCE:
+ resClass = dvmOptResolveClass(meth->clazz, decInsn.vB, &failure);
+ if (resClass == NULL) {
+ const char* badClassDesc = dexStringByTypeIdx(pDexFile, decInsn.vB);
+ dvmLogUnableToResolveClass(badClassDesc, meth);
+ LOG_VFY("VFY: unable to resolve new-instance %d (%s) in %s\n",
+ decInsn.vB, badClassDesc, meth->clazz->descriptor);
+ assert(failure != VERIFY_ERROR_GENERIC);
+ } else {
+ RegType uninitType;
+
+ /* can't create an instance of an interface or abstract class */
+ if (dvmIsAbstractClass(resClass) || dvmIsInterfaceClass(resClass)) {
+ LOG_VFY("VFY: new-instance on interface or abstract class %s\n",
+ resClass->descriptor);
+ failure = VERIFY_ERROR_INSTANTIATION;
+ break;
+ }
+
+ /* add resolved class to uninit map if not already there */
+ int uidx = dvmSetUninitInstance(uninitMap, insnIdx, resClass);
+ assert(uidx >= 0);
+ uninitType = regTypeFromUninitIndex(uidx);
+
+ /*
+ * Any registers holding previous allocations from this address
+ * that have not yet been initialized must be marked invalid.
+ */
+ markUninitRefsAsInvalid(workRegs, insnRegCount, uninitMap,
+ uninitType);
+
+ /* add the new uninitialized reference to the register ste */
+ setRegisterType(workRegs, insnRegCount, decInsn.vA,
+ uninitType, &failure);
+ }
+ break;
+ case OP_NEW_ARRAY:
+ resClass = dvmOptResolveClass(meth->clazz, decInsn.vC, &failure);
+ if (resClass == NULL) {
+ const char* badClassDesc = dexStringByTypeIdx(pDexFile, decInsn.vC);
+ dvmLogUnableToResolveClass(badClassDesc, meth);
+ LOG_VFY("VFY: unable to resolve new-array %d (%s) in %s\n",
+ decInsn.vC, badClassDesc, meth->clazz->descriptor);
+ assert(failure != VERIFY_ERROR_GENERIC);
+ } else if (!dvmIsArrayClass(resClass)) {
+ LOG_VFY("VFY: new-array on non-array class\n");
+ failure = VERIFY_ERROR_GENERIC;
+ } else {
+ /* make sure "size" register is valid type */
+ verifyRegisterType(workRegs, insnRegCount, decInsn.vB,
+ kRegTypeInteger, &failure);
+ /* set register type to array class */
+ setRegisterType(workRegs, insnRegCount, decInsn.vA,
+ regTypeFromClass(resClass), &failure);
+ }
+ break;
+ case OP_FILLED_NEW_ARRAY:
+ case OP_FILLED_NEW_ARRAY_RANGE:
+ resClass = dvmOptResolveClass(meth->clazz, decInsn.vB, &failure);
+ if (resClass == NULL) {
+ const char* badClassDesc = dexStringByTypeIdx(pDexFile, decInsn.vB);
+ dvmLogUnableToResolveClass(badClassDesc, meth);
+ LOG_VFY("VFY: unable to resolve filled-array %d (%s) in %s\n",
+ decInsn.vB, badClassDesc, meth->clazz->descriptor);
+ assert(failure != VERIFY_ERROR_GENERIC);
+ } else if (!dvmIsArrayClass(resClass)) {
+ LOG_VFY("VFY: filled-new-array on non-array class\n");
+ failure = VERIFY_ERROR_GENERIC;
+ } else {
+ bool isRange = (decInsn.opCode == OP_FILLED_NEW_ARRAY_RANGE);
+
+ /* check the arguments to the instruction */
+ verifyFilledNewArrayRegs(meth, workRegs, insnRegCount, &decInsn,
+ resClass, isRange, &failure);
+ /* filled-array result goes into "result" register */
+ setResultRegisterType(workRegs, insnRegCount,
+ regTypeFromClass(resClass), &failure);
+ justSetResult = true;
+ }
+ break;
+
+ case OP_CMPL_FLOAT:
+ case OP_CMPG_FLOAT:
+ verifyRegisterType(workRegs, insnRegCount, decInsn.vB, kRegTypeFloat,
+ &failure);
+ verifyRegisterType(workRegs, insnRegCount, decInsn.vC, kRegTypeFloat,
+ &failure);
+ setRegisterType(workRegs, insnRegCount, decInsn.vA, kRegTypeBoolean,
+ &failure);
+ break;
+ case OP_CMPL_DOUBLE:
+ case OP_CMPG_DOUBLE:
+ verifyRegisterType(workRegs, insnRegCount, decInsn.vB, kRegTypeDoubleLo,
+ &failure);
+ verifyRegisterType(workRegs, insnRegCount, decInsn.vC, kRegTypeDoubleLo,
+ &failure);
+ setRegisterType(workRegs, insnRegCount, decInsn.vA, kRegTypeBoolean,
+ &failure);
+ break;
+ case OP_CMP_LONG:
+ verifyRegisterType(workRegs, insnRegCount, decInsn.vB, kRegTypeLongLo,
+ &failure);
+ verifyRegisterType(workRegs, insnRegCount, decInsn.vC, kRegTypeLongLo,
+ &failure);
+ setRegisterType(workRegs, insnRegCount, decInsn.vA, kRegTypeBoolean,
+ &failure);
+ break;
+
+ case OP_THROW:
+ resClass = getClassFromRegister(workRegs, insnRegCount,
+ decInsn.vA, &failure);
+ if (VERIFY_OK(failure) && resClass != NULL) {
+ if (!dvmInstanceof(resClass, gDvm.classJavaLangThrowable)) {
+ LOG_VFY("VFY: thrown class %s not instanceof Throwable\n",
+ resClass->descriptor);
+ failure = VERIFY_ERROR_GENERIC;
+ }
+ }
+ break;
+
+ case OP_GOTO:
+ case OP_GOTO_16:
+ case OP_GOTO_32:
+ /* no effect on or use of registers */
+ break;
+
+ case OP_PACKED_SWITCH:
+ case OP_SPARSE_SWITCH:
+ /* verify that vAA is an integer, or can be converted to one */
+ verifyRegisterType(workRegs, insnRegCount, decInsn.vA,
+ kRegTypeInteger, &failure);
+ break;
+
+ case OP_FILL_ARRAY_DATA:
+ {
+ RegType valueType;
+ const u2 *arrayData;
+ u2 elemWidth;
+
+ /* Similar to the verification done for APUT */
+ resClass = getClassFromRegister(workRegs, insnRegCount,
+ decInsn.vA, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+
+ /* resClass can be null if the reg type is Zero */
+ if (resClass == NULL)
+ break;
+
+ if (!dvmIsArrayClass(resClass) || resClass->arrayDim != 1 ||
+ resClass->elementClass->primitiveType == PRIM_NOT ||
+ resClass->elementClass->primitiveType == PRIM_VOID)
+ {
+ LOG_VFY("VFY: invalid fill-array-data on %s\n",
+ resClass->descriptor);
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ valueType = primitiveTypeToRegType(
+ resClass->elementClass->primitiveType);
+ assert(valueType != kRegTypeUnknown);
+
+ /*
+ * Now verify if the element width in the table matches the element
+ * width declared in the array
+ */
+ arrayData = insns + (insns[1] | (((s4)insns[2]) << 16));
+ if (arrayData[0] != kArrayDataSignature) {
+ LOG_VFY("VFY: invalid magic for array-data\n");
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ switch (resClass->elementClass->primitiveType) {
+ case PRIM_BOOLEAN:
+ case PRIM_BYTE:
+ elemWidth = 1;
+ break;
+ case PRIM_CHAR:
+ case PRIM_SHORT:
+ elemWidth = 2;
+ break;
+ case PRIM_FLOAT:
+ case PRIM_INT:
+ elemWidth = 4;
+ break;
+ case PRIM_DOUBLE:
+ case PRIM_LONG:
+ elemWidth = 8;
+ break;
+ default:
+ elemWidth = 0;
+ break;
+ }
+
+ /*
+ * Since we don't compress the data in Dex, expect to see equal
+ * width of data stored in the table and expected from the array
+ * class.
+ */
+ if (arrayData[1] != elemWidth) {
+ LOG_VFY("VFY: array-data size mismatch (%d vs %d)\n",
+ arrayData[1], elemWidth);
+ failure = VERIFY_ERROR_GENERIC;
+ }
+ }
+ break;
+
+ case OP_IF_EQ:
+ case OP_IF_NE:
+ {
+ RegType type1, type2;
+
+ type1 = getRegisterType(workRegs, insnRegCount, decInsn.vA,
+ &failure);
+ type2 = getRegisterType(workRegs, insnRegCount, decInsn.vB,
+ &failure);
+ if (!VERIFY_OK(failure))
+ break;
+
+ /* both references? */
+ if (regTypeIsReference(type1) && regTypeIsReference(type2))
+ break;
+
+ /* both category-1nr? */
+ checkTypeCategory(type1, kTypeCategory1nr, &failure);
+ checkTypeCategory(type2, kTypeCategory1nr, &failure);
+ if (!VERIFY_OK(failure)) {
+ LOG_VFY("VFY: args to if-eq/if-ne must both be refs or cat1\n");
+ break;
+ }
+ }
+ break;
+ case OP_IF_LT:
+ case OP_IF_GE:
+ case OP_IF_GT:
+ case OP_IF_LE:
+ tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vA, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+ checkTypeCategory(tmpType, kTypeCategory1nr, &failure);
+ if (!VERIFY_OK(failure)) {
+ LOG_VFY("VFY: args to 'if' must be cat-1nr\n");
+ break;
+ }
+ tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vB, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+ checkTypeCategory(tmpType, kTypeCategory1nr, &failure);
+ if (!VERIFY_OK(failure)) {
+ LOG_VFY("VFY: args to 'if' must be cat-1nr\n");
+ break;
+ }
+ break;
+ case OP_IF_EQZ:
+ case OP_IF_NEZ:
+ tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vA, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+ if (regTypeIsReference(tmpType))
+ break;
+ checkTypeCategory(tmpType, kTypeCategory1nr, &failure);
+ if (!VERIFY_OK(failure))
+ LOG_VFY("VFY: expected cat-1 arg to if\n");
+ break;
+ case OP_IF_LTZ:
+ case OP_IF_GEZ:
+ case OP_IF_GTZ:
+ case OP_IF_LEZ:
+ tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vA, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+ checkTypeCategory(tmpType, kTypeCategory1nr, &failure);
+ if (!VERIFY_OK(failure))
+ LOG_VFY("VFY: expected cat-1 arg to if\n");
+ break;
+
+ case OP_AGET:
+ tmpType = kRegTypeInteger;
+ goto aget_1nr_common;
+ case OP_AGET_BOOLEAN:
+ tmpType = kRegTypeBoolean;
+ goto aget_1nr_common;
+ case OP_AGET_BYTE:
+ tmpType = kRegTypeByte;
+ goto aget_1nr_common;
+ case OP_AGET_CHAR:
+ tmpType = kRegTypeChar;
+ goto aget_1nr_common;
+ case OP_AGET_SHORT:
+ tmpType = kRegTypeShort;
+ goto aget_1nr_common;
+aget_1nr_common:
+ {
+ RegType srcType, indexType;
+
+ indexType = getRegisterType(workRegs, insnRegCount, decInsn.vC,
+ &failure);
+ checkArrayIndexType(meth, indexType, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+
+ resClass = getClassFromRegister(workRegs, insnRegCount,
+ decInsn.vB, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+ if (resClass != NULL) {
+ /* verify the class */
+ if (!dvmIsArrayClass(resClass) || resClass->arrayDim != 1 ||
+ resClass->elementClass->primitiveType == PRIM_NOT)
+ {
+ LOG_VFY("VFY: invalid aget-1nr target %s\n",
+ resClass->descriptor);
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ /* make sure array type matches instruction */
+ srcType = primitiveTypeToRegType(
+ resClass->elementClass->primitiveType);
+
+ if (!checkFieldArrayStore1nr(tmpType, srcType)) {
+ LOG_VFY("VFY: invalid aget-1nr, array type=%d with"
+ " inst type=%d (on %s)\n",
+ srcType, tmpType, resClass->descriptor);
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ }
+ setRegisterType(workRegs, insnRegCount, decInsn.vA,
+ tmpType, &failure);
+ }
+ break;
+
+ case OP_AGET_WIDE:
+ {
+ RegType dstType, indexType;
+
+ indexType = getRegisterType(workRegs, insnRegCount, decInsn.vC,
+ &failure);
+ checkArrayIndexType(meth, indexType, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+
+ resClass = getClassFromRegister(workRegs, insnRegCount,
+ decInsn.vB, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+ if (resClass != NULL) {
+ /* verify the class */
+ if (!dvmIsArrayClass(resClass) || resClass->arrayDim != 1 ||
+ resClass->elementClass->primitiveType == PRIM_NOT)
+ {
+ LOG_VFY("VFY: invalid aget-wide target %s\n",
+ resClass->descriptor);
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ /* try to refine "dstType" */
+ switch (resClass->elementClass->primitiveType) {
+ case PRIM_LONG:
+ dstType = kRegTypeLongLo;
+ break;
+ case PRIM_DOUBLE:
+ dstType = kRegTypeDoubleLo;
+ break;
+ default:
+ LOG_VFY("VFY: invalid aget-wide on %s\n",
+ resClass->descriptor);
+ dstType = kRegTypeUnknown;
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ } else {
+ /*
+ * Null array ref; this code path will fail at runtime. We
+ * know this is either long or double, and we don't really
+ * discriminate between those during verification, so we
+ * call it a long.
+ */
+ dstType = kRegTypeLongLo;
+ }
+ setRegisterType(workRegs, insnRegCount, decInsn.vA,
+ dstType, &failure);
+ }
+ break;
+
+ case OP_AGET_OBJECT:
+ {
+ RegType dstType, indexType;
+
+ indexType = getRegisterType(workRegs, insnRegCount, decInsn.vC,
+ &failure);
+ checkArrayIndexType(meth, indexType, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+
+ /* get the class of the array we're pulling an object from */
+ resClass = getClassFromRegister(workRegs, insnRegCount,
+ decInsn.vB, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+ if (resClass != NULL) {
+ ClassObject* elementClass;
+
+ assert(resClass != NULL);
+ if (!dvmIsArrayClass(resClass)) {
+ LOG_VFY("VFY: aget-object on non-array class\n");
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ assert(resClass->elementClass != NULL);
+
+ /*
+ * Find the element class. resClass->elementClass indicates
+ * the basic type, which won't be what we want for a
+ * multi-dimensional array.
+ */
+ if (resClass->descriptor[1] == '[') {
+ assert(resClass->arrayDim > 1);
+ elementClass = dvmFindArrayClass(&resClass->descriptor[1],
+ resClass->classLoader);
+ } else if (resClass->descriptor[1] == 'L') {
+ assert(resClass->arrayDim == 1);
+ elementClass = resClass->elementClass;
+ } else {
+ LOG_VFY("VFY: aget-object on non-ref array class (%s)\n",
+ resClass->descriptor);
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ dstType = regTypeFromClass(elementClass);
+ } else {
+ /*
+ * The array reference is NULL, so the current code path will
+ * throw an exception. For proper merging with later code
+ * paths, and correct handling of "if-eqz" tests on the
+ * result of the array get, we want to treat this as a null
+ * reference.
+ */
+ dstType = kRegTypeZero;
+ }
+ setRegisterType(workRegs, insnRegCount, decInsn.vA,
+ dstType, &failure);
+ }
+ break;
+ case OP_APUT:
+ tmpType = kRegTypeInteger;
+ goto aput_1nr_common;
+ case OP_APUT_BOOLEAN:
+ tmpType = kRegTypeBoolean;
+ goto aput_1nr_common;
+ case OP_APUT_BYTE:
+ tmpType = kRegTypeByte;
+ goto aput_1nr_common;
+ case OP_APUT_CHAR:
+ tmpType = kRegTypeChar;
+ goto aput_1nr_common;
+ case OP_APUT_SHORT:
+ tmpType = kRegTypeShort;
+ goto aput_1nr_common;
+aput_1nr_common:
+ {
+ RegType srcType, dstType, indexType;
+
+ indexType = getRegisterType(workRegs, insnRegCount, decInsn.vC,
+ &failure);
+ checkArrayIndexType(meth, indexType, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+
+ /* make sure the source register has the correct type */
+ srcType = getRegisterType(workRegs, insnRegCount, decInsn.vA,
+ &failure);
+ if (!canConvertTo1nr(srcType, tmpType)) {
+ LOG_VFY("VFY: invalid reg type %d on aput instr (need %d)\n",
+ srcType, tmpType);
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ resClass = getClassFromRegister(workRegs, insnRegCount,
+ decInsn.vB, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+
+ /* resClass can be null if the reg type is Zero */
+ if (resClass == NULL)
+ break;
+
+ if (!dvmIsArrayClass(resClass) || resClass->arrayDim != 1 ||
+ resClass->elementClass->primitiveType == PRIM_NOT)
+ {
+ LOG_VFY("VFY: invalid aput-1nr on %s\n", resClass->descriptor);
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ /* verify that instruction matches array */
+ dstType = primitiveTypeToRegType(
+ resClass->elementClass->primitiveType);
+ assert(dstType != kRegTypeUnknown);
+
+ if (!checkFieldArrayStore1nr(tmpType, dstType)) {
+ LOG_VFY("VFY: invalid aput-1nr on %s (inst=%d dst=%d)\n",
+ resClass->descriptor, tmpType, dstType);
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ }
+ break;
+ case OP_APUT_WIDE:
+ tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vC,
+ &failure);
+ checkArrayIndexType(meth, tmpType, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+
+ tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vA, &failure);
+ if (VERIFY_OK(failure)) {
+ RegType typeHi =
+ getRegisterType(workRegs, insnRegCount, decInsn.vA+1, &failure);
+ checkTypeCategory(tmpType, kTypeCategory2, &failure);
+ checkWidePair(tmpType, typeHi, &failure);
+ }
+ if (!VERIFY_OK(failure))
+ break;
+
+ resClass = getClassFromRegister(workRegs, insnRegCount,
+ decInsn.vB, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+ if (resClass != NULL) {
+ /* verify the class and try to refine "dstType" */
+ if (!dvmIsArrayClass(resClass) || resClass->arrayDim != 1 ||
+ resClass->elementClass->primitiveType == PRIM_NOT)
+ {
+ LOG_VFY("VFY: invalid aput-wide on %s\n",
+ resClass->descriptor);
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ switch (resClass->elementClass->primitiveType) {
+ case PRIM_LONG:
+ case PRIM_DOUBLE:
+ /* these are okay */
+ break;
+ default:
+ LOG_VFY("VFY: invalid aput-wide on %s\n",
+ resClass->descriptor);
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ }
+ break;
+ case OP_APUT_OBJECT:
+ tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vC,
+ &failure);
+ checkArrayIndexType(meth, tmpType, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+
+ /* get the ref we're storing; Zero is okay, Uninit is not */
+ resClass = getClassFromRegister(workRegs, insnRegCount,
+ decInsn.vA, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+ if (resClass != NULL) {
+ ClassObject* arrayClass;
+ ClassObject* elementClass;
+
+ /*
+ * Get the array class. If the array ref is null, we won't
+ * have type information (and we'll crash at runtime with a
+ * null pointer exception).
+ */
+ arrayClass = getClassFromRegister(workRegs, insnRegCount,
+ decInsn.vB, &failure);
+
+ if (arrayClass != NULL) {
+ /* see if the array holds a compatible type */
+ if (!dvmIsArrayClass(arrayClass)) {
+ LOG_VFY("VFY: invalid aput-object on %s\n",
+ arrayClass->descriptor);
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ /*
+ * Find the element class. resClass->elementClass indicates
+ * the basic type, which won't be what we want for a
+ * multi-dimensional array.
+ *
+ * All we want to check here is that the element type is a
+ * reference class. We *don't* check instanceof here, because
+ * you can still put a String into a String[] after the latter
+ * has been cast to an Object[].
+ */
+ if (arrayClass->descriptor[1] == '[') {
+ assert(arrayClass->arrayDim > 1);
+ elementClass = dvmFindArrayClass(&arrayClass->descriptor[1],
+ arrayClass->classLoader);
+ } else {
+ assert(arrayClass->arrayDim == 1);
+ elementClass = arrayClass->elementClass;
+ }
+ if (elementClass->primitiveType != PRIM_NOT) {
+ LOG_VFY("VFY: invalid aput-object of %s into %s\n",
+ resClass->descriptor, arrayClass->descriptor);
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ }
+ }
+ break;
+
+ case OP_IGET:
+ case OP_IGET_VOLATILE:
+ tmpType = kRegTypeInteger;
+ goto iget_1nr_common;
+ case OP_IGET_BOOLEAN:
+ tmpType = kRegTypeBoolean;
+ goto iget_1nr_common;
+ case OP_IGET_BYTE:
+ tmpType = kRegTypeByte;
+ goto iget_1nr_common;
+ case OP_IGET_CHAR:
+ tmpType = kRegTypeChar;
+ goto iget_1nr_common;
+ case OP_IGET_SHORT:
+ tmpType = kRegTypeShort;
+ goto iget_1nr_common;
+iget_1nr_common:
+ {
+ InstField* instField;
+ RegType objType, fieldType;
+
+ objType = getRegisterType(workRegs, insnRegCount, decInsn.vB,
+ &failure);
+ if (!VERIFY_OK(failure))
+ break;
+ instField = getInstField(meth, uninitMap, objType, decInsn.vC,
+ &failure);
+ if (!VERIFY_OK(failure))
+ break;
+
+ /* make sure the field's type is compatible with expectation */
+ fieldType = primSigCharToRegType(instField->field.signature[0]);
+ if (fieldType == kRegTypeUnknown ||
+ !checkFieldArrayStore1nr(tmpType, fieldType))
+ {
+ LOG_VFY("VFY: invalid iget-1nr of %s.%s (inst=%d field=%d)\n",
+ instField->field.clazz->descriptor,
+ instField->field.name, tmpType, fieldType);
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ setRegisterType(workRegs, insnRegCount, decInsn.vA, tmpType,
+ &failure);
+ }
+ break;
+ case OP_IGET_WIDE:
+ case OP_IGET_WIDE_VOLATILE:
+ {
+ RegType dstType;
+ InstField* instField;
+ RegType objType;
+
+ objType = getRegisterType(workRegs, insnRegCount, decInsn.vB,
+ &failure);
+ if (!VERIFY_OK(failure))
+ break;
+ instField = getInstField(meth, uninitMap, objType, decInsn.vC,
+ &failure);
+ if (!VERIFY_OK(failure))
+ break;
+ /* check the type, which should be prim */
+ switch (instField->field.signature[0]) {
+ case 'D':
+ dstType = kRegTypeDoubleLo;
+ break;
+ case 'J':
+ dstType = kRegTypeLongLo;
+ break;
+ default:
+ LOG_VFY("VFY: invalid iget-wide of %s.%s\n",
+ instField->field.clazz->descriptor,
+ instField->field.name);
+ dstType = kRegTypeUnknown;
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ if (VERIFY_OK(failure)) {
+ setRegisterType(workRegs, insnRegCount, decInsn.vA,
+ dstType, &failure);
+ }
+ }
+ break;
+ case OP_IGET_OBJECT:
+ case OP_IGET_OBJECT_VOLATILE:
+ {
+ ClassObject* fieldClass;
+ InstField* instField;
+ RegType objType;
+
+ objType = getRegisterType(workRegs, insnRegCount, decInsn.vB,
+ &failure);
+ if (!VERIFY_OK(failure))
+ break;
+ instField = getInstField(meth, uninitMap, objType, decInsn.vC,
+ &failure);
+ if (!VERIFY_OK(failure))
+ break;
+ fieldClass = getFieldClass(meth, &instField->field);
+ if (fieldClass == NULL) {
+ /* class not found or primitive type */
+ LOG_VFY("VFY: unable to recover field class from '%s'\n",
+ instField->field.signature);
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ if (VERIFY_OK(failure)) {
+ assert(!dvmIsPrimitiveClass(fieldClass));
+ setRegisterType(workRegs, insnRegCount, decInsn.vA,
+ regTypeFromClass(fieldClass), &failure);
+ }
+ }
+ break;
+ case OP_IPUT:
+ case OP_IPUT_VOLATILE:
+ tmpType = kRegTypeInteger;
+ goto iput_1nr_common;
+ case OP_IPUT_BOOLEAN:
+ tmpType = kRegTypeBoolean;
+ goto iput_1nr_common;
+ case OP_IPUT_BYTE:
+ tmpType = kRegTypeByte;
+ goto iput_1nr_common;
+ case OP_IPUT_CHAR:
+ tmpType = kRegTypeChar;
+ goto iput_1nr_common;
+ case OP_IPUT_SHORT:
+ tmpType = kRegTypeShort;
+ goto iput_1nr_common;
+iput_1nr_common:
+ {
+ RegType srcType, fieldType, objType;
+ InstField* instField;
+
+ srcType = getRegisterType(workRegs, insnRegCount, decInsn.vA,
+ &failure);
+
+ /*
+ * javac generates synthetic functions that write byte values
+ * into boolean fields.
+ */
+ if (tmpType == kRegTypeBoolean && srcType == kRegTypeByte)
+ srcType = kRegTypeBoolean;
+
+ /* make sure the source register has the correct type */
+ if (!canConvertTo1nr(srcType, tmpType)) {
+ LOG_VFY("VFY: invalid reg type %d on iput instr (need %d)\n",
+ srcType, tmpType);
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ objType = getRegisterType(workRegs, insnRegCount, decInsn.vB,
+ &failure);
+ if (!VERIFY_OK(failure))
+ break;
+ instField = getInstField(meth, uninitMap, objType, decInsn.vC,
+ &failure);
+ if (!VERIFY_OK(failure))
+ break;
+ checkFinalFieldAccess(meth, &instField->field, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+
+ /* get type of field we're storing into */
+ fieldType = primSigCharToRegType(instField->field.signature[0]);
+ if (fieldType == kRegTypeUnknown ||
+ !checkFieldArrayStore1nr(tmpType, fieldType))
+ {
+ LOG_VFY("VFY: invalid iput-1nr of %s.%s (inst=%d field=%d)\n",
+ instField->field.clazz->descriptor,
+ instField->field.name, tmpType, fieldType);
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ }
+ break;
+ case OP_IPUT_WIDE:
+ case OP_IPUT_WIDE_VOLATILE:
+ tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vA, &failure);
+ if (VERIFY_OK(failure)) {
+ RegType typeHi =
+ getRegisterType(workRegs, insnRegCount, decInsn.vA+1, &failure);
+ checkTypeCategory(tmpType, kTypeCategory2, &failure);
+ checkWidePair(tmpType, typeHi, &failure);
+ }
+ if (VERIFY_OK(failure)) {
+ InstField* instField;
+ RegType objType;
+
+ objType = getRegisterType(workRegs, insnRegCount, decInsn.vB,
+ &failure);
+ if (!VERIFY_OK(failure))
+ break;
+ instField = getInstField(meth, uninitMap, objType, decInsn.vC,
+ &failure);
+ if (!VERIFY_OK(failure))
+ break;
+ checkFinalFieldAccess(meth, &instField->field, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+
+ /* check the type, which should be prim */
+ switch (instField->field.signature[0]) {
+ case 'D':
+ case 'J':
+ /* these are okay (and interchangeable) */
+ break;
+ default:
+ LOG_VFY("VFY: invalid iput-wide of %s.%s\n",
+ instField->field.clazz->descriptor,
+ instField->field.name);
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ }
+ break;
+ case OP_IPUT_OBJECT:
+ case OP_IPUT_OBJECT_VOLATILE:
+ {
+ ClassObject* fieldClass;
+ ClassObject* valueClass;
+ InstField* instField;
+ RegType objType, valueType;
+
+ objType = getRegisterType(workRegs, insnRegCount, decInsn.vB,
+ &failure);
+ if (!VERIFY_OK(failure))
+ break;
+ instField = getInstField(meth, uninitMap, objType, decInsn.vC,
+ &failure);
+ if (!VERIFY_OK(failure))
+ break;
+ checkFinalFieldAccess(meth, &instField->field, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+
+ fieldClass = getFieldClass(meth, &instField->field);
+ if (fieldClass == NULL) {
+ LOG_VFY("VFY: unable to recover field class from '%s'\n",
+ instField->field.signature);
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ valueType = getRegisterType(workRegs, insnRegCount, decInsn.vA,
+ &failure);
+ if (!VERIFY_OK(failure))
+ break;
+ if (!regTypeIsReference(valueType)) {
+ LOG_VFY("VFY: storing non-ref v%d into ref field '%s' (%s)\n",
+ decInsn.vA, instField->field.name,
+ fieldClass->descriptor);
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ if (valueType != kRegTypeZero) {
+ valueClass = regTypeInitializedReferenceToClass(valueType);
+ if (valueClass == NULL) {
+ LOG_VFY("VFY: storing uninit ref v%d into ref field\n",
+ decInsn.vA);
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ /* allow if field is any interface or field is base class */
+ if (!dvmIsInterfaceClass(fieldClass) &&
+ !dvmInstanceof(valueClass, fieldClass))
+ {
+ LOG_VFY("VFY: storing type '%s' into field type '%s' (%s.%s)\n",
+ valueClass->descriptor, fieldClass->descriptor,
+ instField->field.clazz->descriptor,
+ instField->field.name);
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ }
+ }
+ break;
+
+ case OP_SGET:
+ case OP_SGET_VOLATILE:
+ tmpType = kRegTypeInteger;
+ goto sget_1nr_common;
+ case OP_SGET_BOOLEAN:
+ tmpType = kRegTypeBoolean;
+ goto sget_1nr_common;
+ case OP_SGET_BYTE:
+ tmpType = kRegTypeByte;
+ goto sget_1nr_common;
+ case OP_SGET_CHAR:
+ tmpType = kRegTypeChar;
+ goto sget_1nr_common;
+ case OP_SGET_SHORT:
+ tmpType = kRegTypeShort;
+ goto sget_1nr_common;
+sget_1nr_common:
+ {
+ StaticField* staticField;
+ RegType fieldType;
+
+ staticField = getStaticField(meth, decInsn.vB, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+
+ /*
+ * Make sure the field's type is compatible with expectation.
+ * We can get ourselves into trouble if we mix & match loads
+ * and stores with different widths, so rather than just checking
+ * "canConvertTo1nr" we require that the field types have equal
+ * widths. (We can't generally require an exact type match,
+ * because e.g. "int" and "float" are interchangeable.)
+ */
+ fieldType = primSigCharToRegType(staticField->field.signature[0]);
+ if (!checkFieldArrayStore1nr(tmpType, fieldType)) {
+ LOG_VFY("VFY: invalid sget-1nr of %s.%s (inst=%d actual=%d)\n",
+ staticField->field.clazz->descriptor,
+ staticField->field.name, tmpType, fieldType);
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ setRegisterType(workRegs, insnRegCount, decInsn.vA, tmpType,
+ &failure);
+ }
+ break;
+ case OP_SGET_WIDE:
+ case OP_SGET_WIDE_VOLATILE:
+ {
+ StaticField* staticField;
+ RegType dstType;
+
+ staticField = getStaticField(meth, decInsn.vB, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+ /* check the type, which should be prim */
+ switch (staticField->field.signature[0]) {
+ case 'D':
+ dstType = kRegTypeDoubleLo;
+ break;
+ case 'J':
+ dstType = kRegTypeLongLo;
+ break;
+ default:
+ LOG_VFY("VFY: invalid sget-wide of %s.%s\n",
+ staticField->field.clazz->descriptor,
+ staticField->field.name);
+ dstType = kRegTypeUnknown;
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ if (VERIFY_OK(failure)) {
+ setRegisterType(workRegs, insnRegCount, decInsn.vA,
+ dstType, &failure);
+ }
+ }
+ break;
+ case OP_SGET_OBJECT:
+ case OP_SGET_OBJECT_VOLATILE:
+ {
+ StaticField* staticField;
+ ClassObject* fieldClass;
+
+ staticField = getStaticField(meth, decInsn.vB, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+ fieldClass = getFieldClass(meth, &staticField->field);
+ if (fieldClass == NULL) {
+ LOG_VFY("VFY: unable to recover field class from '%s'\n",
+ staticField->field.signature);
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ if (dvmIsPrimitiveClass(fieldClass)) {
+ LOG_VFY("VFY: attempt to get prim field with sget-object\n");
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ setRegisterType(workRegs, insnRegCount, decInsn.vA,
+ regTypeFromClass(fieldClass), &failure);
+ }
+ break;
+ case OP_SPUT:
+ case OP_SPUT_VOLATILE:
+ tmpType = kRegTypeInteger;
+ goto sput_1nr_common;
+ case OP_SPUT_BOOLEAN:
+ tmpType = kRegTypeBoolean;
+ goto sput_1nr_common;
+ case OP_SPUT_BYTE:
+ tmpType = kRegTypeByte;
+ goto sput_1nr_common;
+ case OP_SPUT_CHAR:
+ tmpType = kRegTypeChar;
+ goto sput_1nr_common;
+ case OP_SPUT_SHORT:
+ tmpType = kRegTypeShort;
+ goto sput_1nr_common;
+sput_1nr_common:
+ {
+ RegType srcType, fieldType;
+ StaticField* staticField;
+
+ srcType = getRegisterType(workRegs, insnRegCount, decInsn.vA,
+ &failure);
+
+ /*
+ * javac generates synthetic functions that write byte values
+ * into boolean fields.
+ */
+ if (tmpType == kRegTypeBoolean && srcType == kRegTypeByte)
+ srcType = kRegTypeBoolean;
+
+ /* make sure the source register has the correct type */
+ if (!canConvertTo1nr(srcType, tmpType)) {
+ LOG_VFY("VFY: invalid reg type %d on sput instr (need %d)\n",
+ srcType, tmpType);
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ staticField = getStaticField(meth, decInsn.vB, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+ checkFinalFieldAccess(meth, &staticField->field, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+
+ /*
+ * Get type of field we're storing into. We know that the
+ * contents of the register match the instruction, but we also
+ * need to ensure that the instruction matches the field type.
+ * Using e.g. sput-short to write into a 32-bit integer field
+ * can lead to trouble if we do 16-bit writes.
+ */
+ fieldType = primSigCharToRegType(staticField->field.signature[0]);
+ if (!checkFieldArrayStore1nr(tmpType, fieldType)) {
+ LOG_VFY("VFY: invalid sput-1nr of %s.%s (inst=%d actual=%d)\n",
+ staticField->field.clazz->descriptor,
+ staticField->field.name, tmpType, fieldType);
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ }
+ break;
+ case OP_SPUT_WIDE:
+ case OP_SPUT_WIDE_VOLATILE:
+ tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vA, &failure);
+ if (VERIFY_OK(failure)) {
+ RegType typeHi =
+ getRegisterType(workRegs, insnRegCount, decInsn.vA+1, &failure);
+ checkTypeCategory(tmpType, kTypeCategory2, &failure);
+ checkWidePair(tmpType, typeHi, &failure);
+ }
+ if (VERIFY_OK(failure)) {
+ StaticField* staticField;
+
+ staticField = getStaticField(meth, decInsn.vB, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+ checkFinalFieldAccess(meth, &staticField->field, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+
+ /* check the type, which should be prim */
+ switch (staticField->field.signature[0]) {
+ case 'D':
+ case 'J':
+ /* these are okay */
+ break;
+ default:
+ LOG_VFY("VFY: invalid sput-wide of %s.%s\n",
+ staticField->field.clazz->descriptor,
+ staticField->field.name);
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ }
+ break;
+ case OP_SPUT_OBJECT:
+ case OP_SPUT_OBJECT_VOLATILE:
+ {
+ ClassObject* fieldClass;
+ ClassObject* valueClass;
+ StaticField* staticField;
+ RegType valueType;
+
+ staticField = getStaticField(meth, decInsn.vB, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+ checkFinalFieldAccess(meth, &staticField->field, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+
+ fieldClass = getFieldClass(meth, &staticField->field);
+ if (fieldClass == NULL) {
+ LOG_VFY("VFY: unable to recover field class from '%s'\n",
+ staticField->field.signature);
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ valueType = getRegisterType(workRegs, insnRegCount, decInsn.vA,
+ &failure);
+ if (!VERIFY_OK(failure))
+ break;
+ if (!regTypeIsReference(valueType)) {
+ LOG_VFY("VFY: storing non-ref v%d into ref field '%s' (%s)\n",
+ decInsn.vA, staticField->field.name,
+ fieldClass->descriptor);
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ if (valueType != kRegTypeZero) {
+ valueClass = regTypeInitializedReferenceToClass(valueType);
+ if (valueClass == NULL) {
+ LOG_VFY("VFY: storing uninit ref v%d into ref field\n",
+ decInsn.vA);
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ /* allow if field is any interface or field is base class */
+ if (!dvmIsInterfaceClass(fieldClass) &&
+ !dvmInstanceof(valueClass, fieldClass))
+ {
+ LOG_VFY("VFY: storing type '%s' into field type '%s' (%s.%s)\n",
+ valueClass->descriptor, fieldClass->descriptor,
+ staticField->field.clazz->descriptor,
+ staticField->field.name);
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ }
+ }
+ break;
+
+ case OP_INVOKE_VIRTUAL:
+ case OP_INVOKE_VIRTUAL_RANGE:
+ case OP_INVOKE_SUPER:
+ case OP_INVOKE_SUPER_RANGE:
+ {
+ Method* calledMethod;
+ RegType returnType;
+ bool isRange;
+ bool isSuper;
+
+ isRange = (decInsn.opCode == OP_INVOKE_VIRTUAL_RANGE ||
+ decInsn.opCode == OP_INVOKE_SUPER_RANGE);
+ isSuper = (decInsn.opCode == OP_INVOKE_SUPER ||
+ decInsn.opCode == OP_INVOKE_SUPER_RANGE);
+
+ calledMethod = verifyInvocationArgs(meth, workRegs, insnRegCount,
+ &decInsn, uninitMap, METHOD_VIRTUAL, isRange,
+ isSuper, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+ returnType = getMethodReturnType(calledMethod);
+ setResultRegisterType(workRegs, insnRegCount, returnType, &failure);
+ justSetResult = true;
+ }
+ break;
+ case OP_INVOKE_DIRECT:
+ case OP_INVOKE_DIRECT_RANGE:
+ {
+ RegType returnType;
+ Method* calledMethod;
+ bool isRange;
+
+ isRange = (decInsn.opCode == OP_INVOKE_DIRECT_RANGE);
+ calledMethod = verifyInvocationArgs(meth, workRegs, insnRegCount,
+ &decInsn, uninitMap, METHOD_DIRECT, isRange,
+ false, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+
+ /*
+ * Some additional checks when calling <init>. We know from
+ * the invocation arg check that the "this" argument is an
+ * instance of calledMethod->clazz. Now we further restrict
+ * that to require that calledMethod->clazz is the same as
+ * this->clazz or this->super, allowing the latter only if
+ * the "this" argument is the same as the "this" argument to
+ * this method (which implies that we're in <init> ourselves).
+ */
+ if (isInitMethod(calledMethod)) {
+ RegType thisType;
+ thisType = getInvocationThis(workRegs, insnRegCount,
+ &decInsn, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+
+ /* no null refs allowed (?) */
+ if (thisType == kRegTypeZero) {
+ LOG_VFY("VFY: unable to initialize null ref\n");
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ ClassObject* thisClass;
+
+ thisClass = regTypeReferenceToClass(thisType, uninitMap);
+ assert(thisClass != NULL);
+
+ /* must be in same class or in superclass */
+ if (calledMethod->clazz == thisClass->super) {
+ if (thisClass != meth->clazz) {
+ LOG_VFY("VFY: invoke-direct <init> on super only "
+ "allowed for 'this' in <init>");
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ } else if (calledMethod->clazz != thisClass) {
+ LOG_VFY("VFY: invoke-direct <init> must be on current "
+ "class or super\n");
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ /* arg must be an uninitialized reference */
+ if (!regTypeIsUninitReference(thisType)) {
+ LOG_VFY("VFY: can only initialize the uninitialized\n");
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ /*
+ * Replace the uninitialized reference with an initialized
+ * one, and clear the entry in the uninit map. We need to
+ * do this for all registers that have the same object
+ * instance in them, not just the "this" register.
+ */
+ markRefsAsInitialized(workRegs, insnRegCount, uninitMap,
+ thisType, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+ }
+ returnType = getMethodReturnType(calledMethod);
+ setResultRegisterType(workRegs, insnRegCount,
+ returnType, &failure);
+ justSetResult = true;
+ }
+ break;
+ case OP_INVOKE_STATIC:
+ case OP_INVOKE_STATIC_RANGE:
+ {
+ RegType returnType;
+ Method* calledMethod;
+ bool isRange;
+
+ isRange = (decInsn.opCode == OP_INVOKE_STATIC_RANGE);
+ calledMethod = verifyInvocationArgs(meth, workRegs, insnRegCount,
+ &decInsn, uninitMap, METHOD_STATIC, isRange,
+ false, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+
+ returnType = getMethodReturnType(calledMethod);
+ setResultRegisterType(workRegs, insnRegCount, returnType, &failure);
+ justSetResult = true;
+ }
+ break;
+ case OP_INVOKE_INTERFACE:
+ case OP_INVOKE_INTERFACE_RANGE:
+ {
+ RegType /*thisType,*/ returnType;
+ Method* absMethod;
+ bool isRange;
+
+ isRange = (decInsn.opCode == OP_INVOKE_INTERFACE_RANGE);
+ absMethod = verifyInvocationArgs(meth, workRegs, insnRegCount,
+ &decInsn, uninitMap, METHOD_INTERFACE, isRange,
+ false, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+
+#if 0 /* can't do this here, fails on dalvik test 052-verifier-fun */
+ /*
+ * Get the type of the "this" arg, which should always be an
+ * interface class. Because we don't do a full merge on
+ * interface classes, this might have reduced to Object.
+ */
+ thisType = getInvocationThis(workRegs, insnRegCount,
+ &decInsn, &failure);
+ if (!VERIFY_OK(failure))
+ break;
+
+ if (thisType == kRegTypeZero) {
+ /* null pointer always passes (and always fails at runtime) */
+ } else {
+ ClassObject* thisClass;
+
+ thisClass = regTypeInitializedReferenceToClass(thisType);
+ if (thisClass == NULL) {
+ LOG_VFY("VFY: interface call on uninitialized\n");
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ /*
+ * Either "thisClass" needs to be the interface class that
+ * defined absMethod, or absMethod's class needs to be one
+ * of the interfaces implemented by "thisClass". (Or, if
+ * we couldn't complete the merge, this will be Object.)
+ */
+ if (thisClass != absMethod->clazz &&
+ thisClass != gDvm.classJavaLangObject &&
+ !dvmImplements(thisClass, absMethod->clazz))
+ {
+ LOG_VFY("VFY: unable to match absMethod '%s' with %s interfaces\n",
+ absMethod->name, thisClass->descriptor);
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ }
+#endif
+
+ /*
+ * We don't have an object instance, so we can't find the
+ * concrete method. However, all of the type information is
+ * in the abstract method, so we're good.
+ */
+ returnType = getMethodReturnType(absMethod);
+ setResultRegisterType(workRegs, insnRegCount, returnType, &failure);
+ justSetResult = true;
+ }
+ break;
+
+ case OP_NEG_INT:
+ case OP_NOT_INT:
+ checkUnop(workRegs, insnRegCount, &decInsn,
+ kRegTypeInteger, kRegTypeInteger, &failure);
+ break;
+ case OP_NEG_LONG:
+ case OP_NOT_LONG:
+ checkUnop(workRegs, insnRegCount, &decInsn,
+ kRegTypeLongLo, kRegTypeLongLo, &failure);
+ break;
+ case OP_NEG_FLOAT:
+ checkUnop(workRegs, insnRegCount, &decInsn,
+ kRegTypeFloat, kRegTypeFloat, &failure);
+ break;
+ case OP_NEG_DOUBLE:
+ checkUnop(workRegs, insnRegCount, &decInsn,
+ kRegTypeDoubleLo, kRegTypeDoubleLo, &failure);
+ break;
+ case OP_INT_TO_LONG:
+ checkUnop(workRegs, insnRegCount, &decInsn,
+ kRegTypeLongLo, kRegTypeInteger, &failure);
+ break;
+ case OP_INT_TO_FLOAT:
+ checkUnop(workRegs, insnRegCount, &decInsn,
+ kRegTypeFloat, kRegTypeInteger, &failure);
+ break;
+ case OP_INT_TO_DOUBLE:
+ checkUnop(workRegs, insnRegCount, &decInsn,
+ kRegTypeDoubleLo, kRegTypeInteger, &failure);
+ break;
+ case OP_LONG_TO_INT:
+ checkUnop(workRegs, insnRegCount, &decInsn,
+ kRegTypeInteger, kRegTypeLongLo, &failure);
+ break;
+ case OP_LONG_TO_FLOAT:
+ checkUnop(workRegs, insnRegCount, &decInsn,
+ kRegTypeFloat, kRegTypeLongLo, &failure);
+ break;
+ case OP_LONG_TO_DOUBLE:
+ checkUnop(workRegs, insnRegCount, &decInsn,
+ kRegTypeDoubleLo, kRegTypeLongLo, &failure);
+ break;
+ case OP_FLOAT_TO_INT:
+ checkUnop(workRegs, insnRegCount, &decInsn,
+ kRegTypeInteger, kRegTypeFloat, &failure);
+ break;
+ case OP_FLOAT_TO_LONG:
+ checkUnop(workRegs, insnRegCount, &decInsn,
+ kRegTypeLongLo, kRegTypeFloat, &failure);
+ break;
+ case OP_FLOAT_TO_DOUBLE:
+ checkUnop(workRegs, insnRegCount, &decInsn,
+ kRegTypeDoubleLo, kRegTypeFloat, &failure);
+ break;
+ case OP_DOUBLE_TO_INT:
+ checkUnop(workRegs, insnRegCount, &decInsn,
+ kRegTypeInteger, kRegTypeDoubleLo, &failure);
+ break;
+ case OP_DOUBLE_TO_LONG:
+ checkUnop(workRegs, insnRegCount, &decInsn,
+ kRegTypeLongLo, kRegTypeDoubleLo, &failure);
+ break;
+ case OP_DOUBLE_TO_FLOAT:
+ checkUnop(workRegs, insnRegCount, &decInsn,
+ kRegTypeFloat, kRegTypeDoubleLo, &failure);
+ break;
+ case OP_INT_TO_BYTE:
+ checkUnop(workRegs, insnRegCount, &decInsn,
+ kRegTypeByte, kRegTypeInteger, &failure);
+ break;
+ case OP_INT_TO_CHAR:
+ checkUnop(workRegs, insnRegCount, &decInsn,
+ kRegTypeChar, kRegTypeInteger, &failure);
+ break;
+ case OP_INT_TO_SHORT:
+ checkUnop(workRegs, insnRegCount, &decInsn,
+ kRegTypeShort, kRegTypeInteger, &failure);
+ break;
+
+ case OP_ADD_INT:
+ case OP_SUB_INT:
+ case OP_MUL_INT:
+ case OP_REM_INT:
+ case OP_DIV_INT:
+ case OP_SHL_INT:
+ case OP_SHR_INT:
+ case OP_USHR_INT:
+ checkBinop(workRegs, insnRegCount, &decInsn,
+ kRegTypeInteger, kRegTypeInteger, kRegTypeInteger, false, &failure);
+ break;
+ case OP_AND_INT:
+ case OP_OR_INT:
+ case OP_XOR_INT:
+ checkBinop(workRegs, insnRegCount, &decInsn,
+ kRegTypeInteger, kRegTypeInteger, kRegTypeInteger, true, &failure);
+ break;
+ case OP_ADD_LONG:
+ case OP_SUB_LONG:
+ case OP_MUL_LONG:
+ case OP_DIV_LONG:
+ case OP_REM_LONG:
+ case OP_AND_LONG:
+ case OP_OR_LONG:
+ case OP_XOR_LONG:
+ checkBinop(workRegs, insnRegCount, &decInsn,
+ kRegTypeLongLo, kRegTypeLongLo, kRegTypeLongLo, false, &failure);
+ break;
+ case OP_SHL_LONG:
+ case OP_SHR_LONG:
+ case OP_USHR_LONG:
+ /* shift distance is Int, making these different from other binops */
+ checkBinop(workRegs, insnRegCount, &decInsn,
+ kRegTypeLongLo, kRegTypeLongLo, kRegTypeInteger, false, &failure);
+ break;
+ case OP_ADD_FLOAT:
+ case OP_SUB_FLOAT:
+ case OP_MUL_FLOAT:
+ case OP_DIV_FLOAT:
+ case OP_REM_FLOAT:
+ checkBinop(workRegs, insnRegCount, &decInsn,
+ kRegTypeFloat, kRegTypeFloat, kRegTypeFloat, false, &failure);
+ break;
+ case OP_ADD_DOUBLE:
+ case OP_SUB_DOUBLE:
+ case OP_MUL_DOUBLE:
+ case OP_DIV_DOUBLE:
+ case OP_REM_DOUBLE:
+ checkBinop(workRegs, insnRegCount, &decInsn,
+ kRegTypeDoubleLo, kRegTypeDoubleLo, kRegTypeDoubleLo, false,
+ &failure);
+ break;
+ case OP_ADD_INT_2ADDR:
+ case OP_SUB_INT_2ADDR:
+ case OP_MUL_INT_2ADDR:
+ case OP_REM_INT_2ADDR:
+ case OP_SHL_INT_2ADDR:
+ case OP_SHR_INT_2ADDR:
+ case OP_USHR_INT_2ADDR:
+ checkBinop2addr(workRegs, insnRegCount, &decInsn,
+ kRegTypeInteger, kRegTypeInteger, kRegTypeInteger, false, &failure);
+ break;
+ case OP_AND_INT_2ADDR:
+ case OP_OR_INT_2ADDR:
+ case OP_XOR_INT_2ADDR:
+ checkBinop2addr(workRegs, insnRegCount, &decInsn,
+ kRegTypeInteger, kRegTypeInteger, kRegTypeInteger, true, &failure);
+ break;
+ case OP_DIV_INT_2ADDR:
+ checkBinop2addr(workRegs, insnRegCount, &decInsn,
+ kRegTypeInteger, kRegTypeInteger, kRegTypeInteger, false, &failure);
+ break;
+ case OP_ADD_LONG_2ADDR:
+ case OP_SUB_LONG_2ADDR:
+ case OP_MUL_LONG_2ADDR:
+ case OP_DIV_LONG_2ADDR:
+ case OP_REM_LONG_2ADDR:
+ case OP_AND_LONG_2ADDR:
+ case OP_OR_LONG_2ADDR:
+ case OP_XOR_LONG_2ADDR:
+ checkBinop2addr(workRegs, insnRegCount, &decInsn,
+ kRegTypeLongLo, kRegTypeLongLo, kRegTypeLongLo, false, &failure);
+ break;
+ case OP_SHL_LONG_2ADDR:
+ case OP_SHR_LONG_2ADDR:
+ case OP_USHR_LONG_2ADDR:
+ checkBinop2addr(workRegs, insnRegCount, &decInsn,
+ kRegTypeLongLo, kRegTypeLongLo, kRegTypeInteger, false, &failure);
+ break;
+ case OP_ADD_FLOAT_2ADDR:
+ case OP_SUB_FLOAT_2ADDR:
+ case OP_MUL_FLOAT_2ADDR:
+ case OP_DIV_FLOAT_2ADDR:
+ case OP_REM_FLOAT_2ADDR:
+ checkBinop2addr(workRegs, insnRegCount, &decInsn,
+ kRegTypeFloat, kRegTypeFloat, kRegTypeFloat, false, &failure);
+ break;
+ case OP_ADD_DOUBLE_2ADDR:
+ case OP_SUB_DOUBLE_2ADDR:
+ case OP_MUL_DOUBLE_2ADDR:
+ case OP_DIV_DOUBLE_2ADDR:
+ case OP_REM_DOUBLE_2ADDR:
+ checkBinop2addr(workRegs, insnRegCount, &decInsn,
+ kRegTypeDoubleLo, kRegTypeDoubleLo, kRegTypeDoubleLo, false,
+ &failure);
+ break;
+ case OP_ADD_INT_LIT16:
+ case OP_RSUB_INT:
+ case OP_MUL_INT_LIT16:
+ case OP_DIV_INT_LIT16:
+ case OP_REM_INT_LIT16:
+ checkLitop(workRegs, insnRegCount, &decInsn,
+ kRegTypeInteger, kRegTypeInteger, false, &failure);
+ break;
+ case OP_AND_INT_LIT16:
+ case OP_OR_INT_LIT16:
+ case OP_XOR_INT_LIT16:
+ checkLitop(workRegs, insnRegCount, &decInsn,
+ kRegTypeInteger, kRegTypeInteger, true, &failure);
+ break;
+ case OP_ADD_INT_LIT8:
+ case OP_RSUB_INT_LIT8:
+ case OP_MUL_INT_LIT8:
+ case OP_DIV_INT_LIT8:
+ case OP_REM_INT_LIT8:
+ case OP_SHL_INT_LIT8:
+ checkLitop(workRegs, insnRegCount, &decInsn,
+ kRegTypeInteger, kRegTypeInteger, false, &failure);
+ break;
+ case OP_SHR_INT_LIT8:
+ tmpType = adjustForRightShift(workRegs, insnRegCount,
+ decInsn.vB, decInsn.vC, false, &failure);
+ checkLitop(workRegs, insnRegCount, &decInsn,
+ tmpType, kRegTypeInteger, false, &failure);
+ break;
+ case OP_USHR_INT_LIT8:
+ tmpType = adjustForRightShift(workRegs, insnRegCount,
+ decInsn.vB, decInsn.vC, true, &failure);
+ checkLitop(workRegs, insnRegCount, &decInsn,
+ tmpType, kRegTypeInteger, false, &failure);
+ break;
+ case OP_AND_INT_LIT8:
+ case OP_OR_INT_LIT8:
+ case OP_XOR_INT_LIT8:
+ checkLitop(workRegs, insnRegCount, &decInsn,
+ kRegTypeInteger, kRegTypeInteger, true, &failure);
+ break;
+
+ /*
+ * This falls into the general category of "optimized" instructions,
+ * which don't generally appear during verification. Because it's
+ * inserted in the course of verification, we can expect to see it here.
+ */
+ case OP_THROW_VERIFICATION_ERROR:
+ break;
+
+ /*
+ * Verifying "quickened" instructions is tricky, because we have
+ * discarded the original field/method information. The byte offsets
+ * and vtable indices only have meaning in the context of an object
+ * instance.
+ *
+ * If a piece of code declares a local reference variable, assigns
+ * null to it, and then issues a virtual method call on it, we
+ * cannot evaluate the method call during verification. This situation
+ * isn't hard to handle, since we know the call will always result in an
+ * NPE, and the arguments and return value don't matter. Any code that
+ * depends on the result of the method call is inaccessible, so the
+ * fact that we can't fully verify anything that comes after the bad
+ * call is not a problem.
+ *
+ * We must also consider the case of multiple code paths, only some of
+ * which involve a null reference. We can completely verify the method
+ * if we sidestep the results of executing with a null reference.
+ * For example, if on the first pass through the code we try to do a
+ * virtual method invocation through a null ref, we have to skip the
+ * method checks and have the method return a "wildcard" type (which
+ * merges with anything to become that other thing). The move-result
+ * will tell us if it's a reference, single-word numeric, or double-word
+ * value. We continue to perform the verification, and at the end of
+ * the function any invocations that were never fully exercised are
+ * marked as null-only.
+ *
+ * We would do something similar for the field accesses. The field's
+ * type, once known, can be used to recover the width of short integers.
+ * If the object reference was null, the field-get returns the "wildcard"
+ * type, which is acceptable for any operation.
+ */
+ case OP_EXECUTE_INLINE:
+ case OP_EXECUTE_INLINE_RANGE:
+ case OP_INVOKE_DIRECT_EMPTY:
+ case OP_IGET_QUICK:
+ case OP_IGET_WIDE_QUICK:
+ case OP_IGET_OBJECT_QUICK:
+ case OP_IPUT_QUICK:
+ case OP_IPUT_WIDE_QUICK:
+ case OP_IPUT_OBJECT_QUICK:
+ case OP_INVOKE_VIRTUAL_QUICK:
+ case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+ case OP_INVOKE_SUPER_QUICK:
+ case OP_INVOKE_SUPER_QUICK_RANGE:
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+
+ /* these should never appear during verification */
+ case OP_UNUSED_3E:
+ case OP_UNUSED_3F:
+ case OP_UNUSED_40:
+ case OP_UNUSED_41:
+ case OP_UNUSED_42:
+ case OP_UNUSED_43:
+ case OP_UNUSED_73:
+ case OP_UNUSED_79:
+ case OP_UNUSED_7A:
+ case OP_BREAKPOINT:
+ case OP_UNUSED_F1:
+ case OP_UNUSED_FF:
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+
+ /*
+ * DO NOT add a "default" clause here. Without it the compiler will
+ * complain if an instruction is missing (which is desirable).
+ */
+ }
+
+ if (!VERIFY_OK(failure)) {
+ if (failure == VERIFY_ERROR_GENERIC || gDvm.optimizing) {
+ /* immediate failure, reject class */
+ LOG_VFY_METH(meth, "VFY: rejecting opcode 0x%02x at 0x%04x\n",
+ decInsn.opCode, insnIdx);
+ goto bail;
+ } else {
+ /* replace opcode and continue on */
+ LOGD("VFY: replacing opcode 0x%02x at 0x%04x\n",
+ decInsn.opCode, insnIdx);
+ if (!replaceFailingInstruction(meth, insnFlags, insnIdx, failure)) {
+ LOG_VFY_METH(meth, "VFY: rejecting opcode 0x%02x at 0x%04x\n",
+ decInsn.opCode, insnIdx);
+ goto bail;
+ }
+ /* IMPORTANT: meth->insns may have been changed */
+ insns = meth->insns + insnIdx;
+
+ /* continue on as if we just handled a throw-verification-error */
+ failure = VERIFY_ERROR_NONE;
+ nextFlags = kInstrCanThrow;
+ }
+ }
+
+ /*
+ * If we didn't just set the result register, clear it out. This
+ * ensures that you can only use "move-result" immediately after the
+ * result is set. (We could check this statically, but it's not
+ * expensive and it makes our debugging output cleaner.)
+ */
+ if (!justSetResult) {
+ int reg = RESULT_REGISTER(insnRegCount);
+ workRegs[reg] = workRegs[reg+1] = kRegTypeUnknown;
+ }
+
+ /*
+ * Handle "continue". Tag the next consecutive instruction.
+ */
+ if ((nextFlags & kInstrCanContinue) != 0) {
+ int insnWidth = dvmInsnGetWidth(insnFlags, insnIdx);
+ if (insnIdx+insnWidth >= insnsSize) {
+ LOG_VFY_METH(meth,
+ "VFY: execution can walk off end of code area (from 0x%x)\n",
+ insnIdx);
+ goto bail;
+ }
+
+ /*
+ * The only way to get to a move-exception instruction is to get
+ * thrown there. Make sure the next instruction isn't one.
+ */
+ if (!checkMoveException(meth, insnIdx+insnWidth, "next"))
+ goto bail;
+
+ if (getRegisterLine(regTable, insnIdx+insnWidth) != NULL) {
+ /*
+ * Merge registers into what we have for the next instruction,
+ * and set the "changed" flag if needed.
+ */
+ updateRegisters(meth, insnFlags, regTable, insnIdx+insnWidth,
+ workRegs);
+ } else {
+ /*
+ * We're not recording register data for the next instruction,
+ * so we don't know what the prior state was. We have to
+ * assume that something has changed and re-evaluate it.
+ */
+ dvmInsnSetChanged(insnFlags, insnIdx+insnWidth, true);
+ }
+ }
+
+ /*
+ * Handle "branch". Tag the branch target.
+ *
+ * NOTE: instructions like OP_EQZ provide information about the state
+ * of the register when the branch is taken or not taken. For example,
+ * somebody could get a reference field, check it for zero, and if the
+ * branch is taken immediately store that register in a boolean field
+ * since the value is known to be zero. We do not currently account for
+ * that, and will reject the code.
+ */
+ if ((nextFlags & kInstrCanBranch) != 0) {
+ bool isConditional;
+
+ if (!dvmGetBranchTarget(meth, insnFlags, insnIdx, &branchTarget,
+ &isConditional))
+ {
+ /* should never happen after static verification */
+ LOG_VFY_METH(meth, "VFY: bad branch at %d\n", insnIdx);
+ goto bail;
+ }
+ assert(isConditional || (nextFlags & kInstrCanContinue) == 0);
+ assert(!isConditional || (nextFlags & kInstrCanContinue) != 0);
+
+ if (!checkMoveException(meth, insnIdx+branchTarget, "branch"))
+ goto bail;
+
+ /* update branch target, set "changed" if appropriate */
+ updateRegisters(meth, insnFlags, regTable, insnIdx+branchTarget,
+ workRegs);
+ }
+
+ /*
+ * Handle "switch". Tag all possible branch targets.
+ *
+ * We've already verified that the table is structurally sound, so we
+ * just need to walk through and tag the targets.
+ */
+ if ((nextFlags & kInstrCanSwitch) != 0) {
+ int offsetToSwitch = insns[1] | (((s4)insns[2]) << 16);
+ const u2* switchInsns = insns + offsetToSwitch;
+ int switchCount = switchInsns[1];
+ int offsetToTargets, targ;
+
+ if ((*insns & 0xff) == OP_PACKED_SWITCH) {
+ /* 0=sig, 1=count, 2/3=firstKey */
+ offsetToTargets = 4;
+ } else {
+ /* 0=sig, 1=count, 2..count*2 = keys */
+ assert((*insns & 0xff) == OP_SPARSE_SWITCH);
+ offsetToTargets = 2 + 2*switchCount;
+ }
+
+ /* verify each switch target */
+ for (targ = 0; targ < switchCount; targ++) {
+ int offset, absOffset;
+
+ /* offsets are 32-bit, and only partly endian-swapped */
+ offset = switchInsns[offsetToTargets + targ*2] |
+ (((s4) switchInsns[offsetToTargets + targ*2 +1]) << 16);
+ absOffset = insnIdx + offset;
+
+ assert(absOffset >= 0 && absOffset < insnsSize);
+
+ if (!checkMoveException(meth, absOffset, "switch"))
+ goto bail;
+
+ updateRegisters(meth, insnFlags, regTable, absOffset, workRegs);
+ }
+ }
+
+ /*
+ * Handle instructions that can throw and that are sitting in a
+ * "try" block. (If they're not in a "try" block when they throw,
+ * control transfers out of the method.)
+ */
+ if ((nextFlags & kInstrCanThrow) != 0 && dvmInsnIsInTry(insnFlags, insnIdx))
+ {
+ const DexCode* pCode = dvmGetMethodCode(meth);
+ DexCatchIterator iterator;
+
+ if (dexFindCatchHandler(&iterator, pCode, insnIdx)) {
+ for (;;) {
+ DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
+
+ if (handler == NULL) {
+ break;
+ }
+
+ /* note we use entryRegs, not workRegs */
+ updateRegisters(meth, insnFlags, regTable, handler->address,
+ entryRegs);
+ }
+ }
+ }
+
+ /*
+ * Update startGuess. Advance to the next instruction of that's
+ * possible, otherwise use the branch target if one was found. If
+ * neither of those exists we're in a return or throw; leave startGuess
+ * alone and let the caller sort it out.
+ */
+ if ((nextFlags & kInstrCanContinue) != 0) {
+ *pStartGuess = insnIdx + dvmInsnGetWidth(insnFlags, insnIdx);
+ } else if ((nextFlags & kInstrCanBranch) != 0) {
+ /* we're still okay if branchTarget is zero */
+ *pStartGuess = insnIdx + branchTarget;
+ }
+
+ assert(*pStartGuess >= 0 && *pStartGuess < insnsSize &&
+ dvmInsnGetWidth(insnFlags, *pStartGuess) != 0);
+
+ result = true;
+
+bail:
+ return result;
+}
+
+
+/*
+ * callback function used in dumpRegTypes to print local vars
+ * valid at a given address.
+ */
+static void logLocalsCb(void *cnxt, u2 reg, u4 startAddress, u4 endAddress,
+ const char *name, const char *descriptor,
+ const char *signature)
+{
+ int addr = *((int *)cnxt);
+
+ if (addr >= (int) startAddress && addr < (int) endAddress)
+ {
+ LOGI(" %2d: '%s' %s\n", reg, name, descriptor);
+ }
+}
+
+/*
+ * Dump the register types for the specifed address to the log file.
+ */
+static void dumpRegTypes(const Method* meth, const InsnFlags* insnFlags,
+ const RegType* addrRegs, int addr, const char* addrName,
+ const UninitInstanceMap* uninitMap, int displayFlags)
+{
+ int regCount = meth->registersSize;
+ int fullRegCount = regCount + kExtraRegs;
+ bool branchTarget = dvmInsnIsBranchTarget(insnFlags, addr);
+ int i;
+
+ assert(addr >= 0 && addr < (int) dvmGetMethodInsnsSize(meth));
+
+ int regCharSize = fullRegCount + (fullRegCount-1)/4 + 2 +1;
+ char regChars[regCharSize +1];
+ memset(regChars, ' ', regCharSize);
+ regChars[0] = '[';
+ if (regCount == 0)
+ regChars[1] = ']';
+ else
+ regChars[1 + (regCount-1) + (regCount-1)/4 +1] = ']';
+ regChars[regCharSize] = '\0';
+
+ //const RegType* addrRegs = getRegisterLine(regTable, addr);
+
+ for (i = 0; i < regCount + kExtraRegs; i++) {
+ char tch;
+
+ switch (addrRegs[i]) {
+ case kRegTypeUnknown: tch = '.'; break;
+ case kRegTypeConflict: tch = 'X'; break;
+ case kRegTypeFloat: tch = 'F'; break;
+ case kRegTypeZero: tch = '0'; break;
+ case kRegTypeOne: tch = '1'; break;
+ case kRegTypeBoolean: tch = 'Z'; break;
+ case kRegTypePosByte: tch = 'b'; break;
+ case kRegTypeByte: tch = 'B'; break;
+ case kRegTypePosShort: tch = 's'; break;
+ case kRegTypeShort: tch = 'S'; break;
+ case kRegTypeChar: tch = 'C'; break;
+ case kRegTypeInteger: tch = 'I'; break;
+ case kRegTypeLongLo: tch = 'J'; break;
+ case kRegTypeLongHi: tch = 'j'; break;
+ case kRegTypeDoubleLo: tch = 'D'; break;
+ case kRegTypeDoubleHi: tch = 'd'; break;
+ default:
+ if (regTypeIsReference(addrRegs[i])) {
+ if (regTypeIsUninitReference(addrRegs[i]))
+ tch = 'U';
+ else
+ tch = 'L';
+ } else {
+ tch = '*';
+ assert(false);
+ }
+ break;
+ }
+
+ if (i < regCount)
+ regChars[1 + i + (i/4)] = tch;
+ else
+ regChars[1 + i + (i/4) + 2] = tch;
+ }
+
+ if (addr == 0 && addrName != NULL)
+ LOGI("%c%s %s\n", branchTarget ? '>' : ' ', addrName, regChars);
+ else
+ LOGI("%c0x%04x %s\n", branchTarget ? '>' : ' ', addr, regChars);
+
+ if (displayFlags & DRT_SHOW_REF_TYPES) {
+ for (i = 0; i < regCount + kExtraRegs; i++) {
+ if (regTypeIsReference(addrRegs[i]) && addrRegs[i] != kRegTypeZero)
+ {
+ ClassObject* clazz;
+
+ clazz = regTypeReferenceToClass(addrRegs[i], uninitMap);
+ assert(dvmValidateObject((Object*)clazz));
+ if (i < regCount) {
+ LOGI(" %2d: 0x%08x %s%s\n",
+ i, addrRegs[i],
+ regTypeIsUninitReference(addrRegs[i]) ? "[U]" : "",
+ clazz->descriptor);
+ } else {
+ LOGI(" RS: 0x%08x %s%s\n",
+ addrRegs[i],
+ regTypeIsUninitReference(addrRegs[i]) ? "[U]" : "",
+ clazz->descriptor);
+ }
+ }
+ }
+ }
+ if (displayFlags & DRT_SHOW_LOCALS) {
+ dexDecodeDebugInfo(meth->clazz->pDvmDex->pDexFile,
+ dvmGetMethodCode(meth),
+ meth->clazz->descriptor,
+ meth->prototype.protoIdx,
+ meth->accessFlags,
+ NULL, logLocalsCb, &addr);
+ }
+}
diff --git a/vm/analysis/CodeVerify.h b/vm/analysis/CodeVerify.h
new file mode 100644
index 0000000..a7ddc95
--- /dev/null
+++ b/vm/analysis/CodeVerify.h
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik bytecode verifier.
+ */
+#ifndef _DALVIK_CODEVERIFY
+#define _DALVIK_CODEVERIFY
+
+#include "analysis/VerifySubs.h"
+
+
+/*
+ * Enumeration for register type values. The "hi" piece of a 64-bit value
+ * MUST immediately follow the "lo" piece in the enumeration, so we can check
+ * that hi==lo+1.
+ *
+ * Assignment of constants:
+ * [-MAXINT,-32768) : integer
+ * [-32768,-128) : short
+ * [-128,0) : byte
+ * 0 : zero
+ * 1 : one
+ * [2,128) : posbyte
+ * [128,32768) : posshort
+ * [32768,65536) : char
+ * [65536,MAXINT] : integer
+ *
+ * Allowed "implicit" widening conversions:
+ * zero -> boolean, posbyte, byte, posshort, short, char, integer, ref (null)
+ * one -> boolean, posbyte, byte, posshort, short, char, integer
+ * boolean -> posbyte, byte, posshort, short, char, integer
+ * posbyte -> posshort, short, integer, char
+ * byte -> short, integer
+ * posshort -> integer, char
+ * short -> integer
+ * char -> integer
+ *
+ * In addition, all of the above can convert to "float".
+ *
+ * We're more careful with integer values than the spec requires. The
+ * motivation is to restrict byte/char/short to the correct range of values.
+ * For example, if a method takes a byte argument, we don't want to allow
+ * the code to load the constant "1024" and pass it in.
+ */
+enum {
+ kRegTypeUnknown = 0, /* initial state; use value=0 so calloc works */
+ kRegTypeUninit = 1, /* MUST be odd to distinguish from pointer */
+ kRegTypeConflict, /* merge clash makes this reg's type unknowable */
+
+ /*
+ * Category-1nr types. The order of these is chiseled into a couple
+ * of tables, so don't add, remove, or reorder if you can avoid it.
+ */
+#define kRegType1nrSTART kRegTypeFloat
+ kRegTypeFloat,
+ kRegTypeZero, /* 32-bit 0, could be Boolean, Int, Float, or Ref */
+ kRegTypeOne, /* 32-bit 1, could be Boolean, Int, Float */
+ kRegTypeBoolean, /* must be 0 or 1 */
+ kRegTypePosByte, /* byte, known positive (can become char) */
+ kRegTypeByte,
+ kRegTypePosShort, /* short, known positive (can become char) */
+ kRegTypeShort,
+ kRegTypeChar,
+ kRegTypeInteger,
+#define kRegType1nrEND kRegTypeInteger
+
+ kRegTypeLongLo, /* lower-numbered register; endian-independent */
+ kRegTypeLongHi,
+ kRegTypeDoubleLo,
+ kRegTypeDoubleHi,
+
+ /*
+ * Enumeration max; this is used with "full" (32-bit) RegType values.
+ *
+ * Anything larger than this is a ClassObject or uninit ref. Mask off
+ * all but the low 8 bits; if you're left with kRegTypeUninit, pull
+ * the uninit index out of the high 24. Because kRegTypeUninit has an
+ * odd value, there is no risk of a particular ClassObject pointer bit
+ * pattern being confused for it (assuming our class object allocator
+ * uses word alignment).
+ */
+ kRegTypeMAX
+};
+#define kRegTypeUninitMask 0xff
+#define kRegTypeUninitShift 8
+
+/*
+ * RegType holds information about the type of data held in a register.
+ * For most types it's a simple enum. For reference types it holds a
+ * pointer to the ClassObject, and for uninitialized references it holds
+ * an index into the UninitInstanceMap.
+ */
+typedef u4 RegType;
+
+/*
+ * Table that maps uninitialized instances to classes, based on the
+ * address of the new-instance instruction.
+ */
+typedef struct UninitInstanceMap {
+ int numEntries;
+ struct {
+ int addr; /* code offset, or -1 for method arg ("this") */
+ ClassObject* clazz; /* class created at this address */
+ } map[1];
+} UninitInstanceMap;
+#define kUninitThisArgAddr (-1)
+#define kUninitThisArgSlot 0
+
+/*
+ * Various bits of data generated by the verifier, wrapped up in a package
+ * for ease of use by the register map generator.
+ */
+typedef struct VerifierData {
+ /*
+ * The method we're working on.
+ */
+ const Method* method;
+
+ /*
+ * Number of code units of instructions in the method. A cache of the
+ * value calculated by dvmGetMethodInsnsSize().
+ */
+ u4 insnsSize;
+
+ /*
+ * Number of registers we track for each instruction. This is equal
+ * to the method's declared "registersSize". (Does not include the
+ * pending return value.)
+ */
+ u4 insnRegCount;
+
+ /*
+ * Instruction widths and flags, one entry per code unit.
+ */
+ InsnFlags* insnFlags;
+
+ /*
+ * Uninitialized instance map, used for tracking the movement of
+ * objects that have been allocated but not initialized.
+ */
+ UninitInstanceMap* uninitMap;
+
+ /*
+ * Array of SRegType arrays, one entry per code unit. We only need
+ * entries for code units that hold the start of an "interesting"
+ * instruction. For register map generation, we're only interested
+ * in GC points.
+ */
+ RegType** addrRegs;
+} VerifierData;
+
+
+/* table with static merge logic for primitive types */
+extern const char gDvmMergeTab[kRegTypeMAX][kRegTypeMAX];
+
+
+/*
+ * Returns "true" if the flags indicate that this address holds the start
+ * of an instruction.
+ */
+INLINE bool dvmInsnIsOpcode(const InsnFlags* insnFlags, int addr) {
+ return (insnFlags[addr] & kInsnFlagWidthMask) != 0;
+}
+
+/*
+ * Extract the unsigned 16-bit instruction width from "flags".
+ */
+INLINE int dvmInsnGetWidth(const InsnFlags* insnFlags, int addr) {
+ return insnFlags[addr] & kInsnFlagWidthMask;
+}
+
+/*
+ * Changed?
+ */
+INLINE bool dvmInsnIsChanged(const InsnFlags* insnFlags, int addr) {
+ return (insnFlags[addr] & kInsnFlagChanged) != 0;
+}
+INLINE void dvmInsnSetChanged(InsnFlags* insnFlags, int addr, bool changed)
+{
+ if (changed)
+ insnFlags[addr] |= kInsnFlagChanged;
+ else
+ insnFlags[addr] &= ~kInsnFlagChanged;
+}
+
+/*
+ * Visited?
+ */
+INLINE bool dvmInsnIsVisited(const InsnFlags* insnFlags, int addr) {
+ return (insnFlags[addr] & kInsnFlagVisited) != 0;
+}
+INLINE void dvmInsnSetVisited(InsnFlags* insnFlags, int addr, bool changed)
+{
+ if (changed)
+ insnFlags[addr] |= kInsnFlagVisited;
+ else
+ insnFlags[addr] &= ~kInsnFlagVisited;
+}
+
+/*
+ * Visited or changed?
+ */
+INLINE bool dvmInsnIsVisitedOrChanged(const InsnFlags* insnFlags, int addr) {
+ return (insnFlags[addr] & (kInsnFlagVisited|kInsnFlagChanged)) != 0;
+}
+
+/*
+ * In a "try" block?
+ */
+INLINE bool dvmInsnIsInTry(const InsnFlags* insnFlags, int addr) {
+ return (insnFlags[addr] & kInsnFlagInTry) != 0;
+}
+INLINE void dvmInsnSetInTry(InsnFlags* insnFlags, int addr, bool inTry)
+{
+ assert(inTry);
+ //if (inTry)
+ insnFlags[addr] |= kInsnFlagInTry;
+ //else
+ // insnFlags[addr] &= ~kInsnFlagInTry;
+}
+
+/*
+ * Instruction is a branch target or exception handler?
+ */
+INLINE bool dvmInsnIsBranchTarget(const InsnFlags* insnFlags, int addr) {
+ return (insnFlags[addr] & kInsnFlagBranchTarget) != 0;
+}
+INLINE void dvmInsnSetBranchTarget(InsnFlags* insnFlags, int addr,
+ bool isBranch)
+{
+ assert(isBranch);
+ //if (isBranch)
+ insnFlags[addr] |= kInsnFlagBranchTarget;
+ //else
+ // insnFlags[addr] &= ~kInsnFlagBranchTarget;
+}
+
+/*
+ * Instruction is a GC point?
+ */
+INLINE bool dvmInsnIsGcPoint(const InsnFlags* insnFlags, int addr) {
+ return (insnFlags[addr] & kInsnFlagGcPoint) != 0;
+}
+INLINE void dvmInsnSetGcPoint(InsnFlags* insnFlags, int addr,
+ bool isGcPoint)
+{
+ assert(isGcPoint);
+ //if (isGcPoint)
+ insnFlags[addr] |= kInsnFlagGcPoint;
+ //else
+ // insnFlags[addr] &= ~kInsnFlagGcPoint;
+}
+
+
+/*
+ * Create a new UninitInstanceMap.
+ */
+UninitInstanceMap* dvmCreateUninitInstanceMap(const Method* meth,
+ const InsnFlags* insnFlags, int newInstanceCount);
+
+/*
+ * Release the storage associated with an UninitInstanceMap.
+ */
+void dvmFreeUninitInstanceMap(UninitInstanceMap* uninitMap);
+
+/*
+ * Associate a class with an address. Returns the map slot index, or -1
+ * if the address isn't listed in the map (shouldn't happen) or if a
+ * different class is already associated with the address (shouldn't
+ * happen either).
+ */
+//int dvmSetUninitInstance(UninitInstanceMap* uninitMap, int addr,
+// ClassObject* clazz);
+
+/*
+ * Return the class associated with an uninitialized reference. Pass in
+ * the map index.
+ */
+//ClassObject* dvmGetUninitInstance(const UninitInstanceMap* uninitMap, int idx);
+
+/*
+ * Clear the class associated with an uninitialized reference. Pass in
+ * the map index.
+ */
+//void dvmClearUninitInstance(UninitInstanceMap* uninitMap, int idx);
+
+
+/*
+ * Verify bytecode in "meth". "insnFlags" should be populated with
+ * instruction widths and "in try" flags.
+ */
+bool dvmVerifyCodeFlow(VerifierData* vdata);
+
+#endif /*_DALVIK_CODEVERIFY*/
diff --git a/vm/analysis/DexPrepare.c b/vm/analysis/DexPrepare.c
new file mode 100644
index 0000000..c00810c
--- /dev/null
+++ b/vm/analysis/DexPrepare.c
@@ -0,0 +1,1446 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Prepare a DEX file for use by the VM. Depending upon the VM options
+ * we will attempt to verify and/or optimize the code, possibly appending
+ * register maps.
+ *
+ * TODO: the format of the optimized header is currently "whatever we
+ * happen to write", since the VM that writes it is by definition the same
+ * as the VM that reads it. Still, it should be better documented and
+ * more rigorously structured.
+ */
+#include "Dalvik.h"
+#include "libdex/OptInvocation.h"
+#include "analysis/RegisterMap.h"
+#include "analysis/Optimize.h"
+
+#include <zlib.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <errno.h>
+
+
+/* fwd */
+static bool rewriteDex(u1* addr, int len, u4* pHeaderFlags,
+ DexClassLookup** ppClassLookup);
+static bool loadAllClasses(DvmDex* pDvmDex);
+static void verifyAndOptimizeClasses(DexFile* pDexFile, bool doVerify,
+ bool doOpt);
+static void verifyAndOptimizeClass(DexFile* pDexFile, ClassObject* clazz,
+ const DexClassDef* pClassDef, bool doVerify, bool doOpt);
+static void updateChecksum(u1* addr, int len, DexHeader* pHeader);
+static int writeDependencies(int fd, u4 modWhen, u4 crc);
+static bool writeOptData(int fd, const DexClassLookup* pClassLookup,\
+ const RegisterMapBuilder* pRegMapBuilder);
+static bool computeFileChecksum(int fd, off_t start, size_t length, u4* pSum);
+
+
+/*
+ * Return the fd of an open file in the DEX file cache area. If the cache
+ * file doesn't exist or is out of date, this will remove the old entry,
+ * create a new one (writing only the file header), and return with the
+ * "new file" flag set.
+ *
+ * It's possible to execute from an unoptimized DEX file directly,
+ * assuming the byte ordering and structure alignment is correct, but
+ * disadvantageous because some significant optimizations are not possible.
+ * It's not generally possible to do the same from an uncompressed Jar
+ * file entry, because we have to guarantee 32-bit alignment in the
+ * memory-mapped file.
+ *
+ * For a Jar/APK file (a zip archive with "classes.dex" inside), "modWhen"
+ * and "crc32" come from the Zip directory entry. For a stand-alone DEX
+ * file, it's the modification date of the file and the Adler32 from the
+ * DEX header (which immediately follows the magic). If these don't
+ * match what's stored in the opt header, we reject the file immediately.
+ *
+ * On success, the file descriptor will be positioned just past the "opt"
+ * file header, and will be locked with flock. "*pCachedName" will point
+ * to newly-allocated storage.
+ */
+int dvmOpenCachedDexFile(const char* fileName, const char* cacheFileName,
+ u4 modWhen, u4 crc, bool isBootstrap, bool* pNewFile, bool createIfMissing)
+{
+ int fd, cc;
+ struct stat fdStat, fileStat;
+ bool readOnly = false;
+
+ *pNewFile = false;
+
+retry:
+ /*
+ * Try to open the cache file. If we've been asked to,
+ * create it if it doesn't exist.
+ */
+ fd = createIfMissing ? open(cacheFileName, O_CREAT|O_RDWR, 0644) : -1;
+ if (fd < 0) {
+ fd = open(cacheFileName, O_RDONLY, 0);
+ if (fd < 0) {
+ if (createIfMissing) {
+ LOGE("Can't open dex cache '%s': %s\n",
+ cacheFileName, strerror(errno));
+ }
+ return fd;
+ }
+ readOnly = true;
+ }
+
+ /*
+ * Grab an exclusive lock on the cache file. If somebody else is
+ * working on it, we'll block here until they complete. Because
+ * we're waiting on an external resource, we go into VMWAIT mode.
+ */
+ int oldStatus;
+ LOGV("DexOpt: locking cache file %s (fd=%d, boot=%d)\n",
+ cacheFileName, fd, isBootstrap);
+ oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
+ cc = flock(fd, LOCK_EX | LOCK_NB);
+ if (cc != 0) {
+ LOGD("DexOpt: sleeping on flock(%s)\n", cacheFileName);
+ cc = flock(fd, LOCK_EX);
+ }
+ dvmChangeStatus(NULL, oldStatus);
+ if (cc != 0) {
+ LOGE("Can't lock dex cache '%s': %d\n", cacheFileName, cc);
+ close(fd);
+ return -1;
+ }
+ LOGV("DexOpt: locked cache file\n");
+
+ /*
+ * Check to see if the fd we opened and locked matches the file in
+ * the filesystem. If they don't, then somebody else unlinked ours
+ * and created a new file, and we need to use that one instead. (If
+ * we caught them between the unlink and the create, we'll get an
+ * ENOENT from the file stat.)
+ */
+ cc = fstat(fd, &fdStat);
+ if (cc != 0) {
+ LOGE("Can't stat open file '%s'\n", cacheFileName);
+ LOGVV("DexOpt: unlocking cache file %s\n", cacheFileName);
+ goto close_fail;
+ }
+ cc = stat(cacheFileName, &fileStat);
+ if (cc != 0 ||
+ fdStat.st_dev != fileStat.st_dev || fdStat.st_ino != fileStat.st_ino)
+ {
+ LOGD("DexOpt: our open cache file is stale; sleeping and retrying\n");
+ LOGVV("DexOpt: unlocking cache file %s\n", cacheFileName);
+ flock(fd, LOCK_UN);
+ close(fd);
+ usleep(250 * 1000); /* if something is hosed, don't peg machine */
+ goto retry;
+ }
+
+ /*
+ * We have the correct file open and locked. If the file size is zero,
+ * then it was just created by us, and we want to fill in some fields
+ * in the "opt" header and set "*pNewFile". Otherwise, we want to
+ * verify that the fields in the header match our expectations, and
+ * reset the file if they don't.
+ */
+ if (fdStat.st_size == 0) {
+ if (readOnly) {
+ LOGW("DexOpt: file has zero length and isn't writable\n");
+ goto close_fail;
+ }
+ cc = dexOptCreateEmptyHeader(fd);
+ if (cc != 0)
+ goto close_fail;
+ *pNewFile = true;
+ LOGV("DexOpt: successfully initialized new cache file\n");
+ } else {
+ bool expectVerify, expectOpt;
+
+ if (gDvm.classVerifyMode == VERIFY_MODE_NONE)
+ expectVerify = false;
+ else if (gDvm.classVerifyMode == VERIFY_MODE_REMOTE)
+ expectVerify = !isBootstrap;
+ else /*if (gDvm.classVerifyMode == VERIFY_MODE_ALL)*/
+ expectVerify = true;
+
+ if (gDvm.dexOptMode == OPTIMIZE_MODE_NONE)
+ expectOpt = false;
+ else if (gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED)
+ expectOpt = expectVerify;
+ else /*if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)*/
+ expectOpt = true;
+
+ LOGV("checking deps, expecting vfy=%d opt=%d\n",
+ expectVerify, expectOpt);
+
+ if (!dvmCheckOptHeaderAndDependencies(fd, true, modWhen, crc,
+ expectVerify, expectOpt))
+ {
+ if (readOnly) {
+ /*
+ * We could unlink and rewrite the file if we own it or
+ * the "sticky" bit isn't set on the directory. However,
+ * we're not able to truncate it, which spoils things. So,
+ * give up now.
+ */
+ if (createIfMissing) {
+ LOGW("Cached DEX '%s' (%s) is stale and not writable\n",
+ fileName, cacheFileName);
+ }
+ goto close_fail;
+ }
+
+ /*
+ * If we truncate the existing file before unlinking it, any
+ * process that has it mapped will fail when it tries to touch
+ * the pages.
+ *
+ * This is very important. The zygote process will have the
+ * boot DEX files (core, framework, etc.) mapped early. If
+ * (say) core.dex gets updated, and somebody launches an app
+ * that uses App.dex, then App.dex gets reoptimized because it's
+ * dependent upon the boot classes. However, dexopt will be
+ * using the *new* core.dex to do the optimizations, while the
+ * app will actually be running against the *old* core.dex
+ * because it starts from zygote.
+ *
+ * Even without zygote, it's still possible for a class loader
+ * to pull in an APK that was optimized against an older set
+ * of DEX files. We must ensure that everything fails when a
+ * boot DEX gets updated, and for general "why aren't my
+ * changes doing anything" purposes its best if we just make
+ * everything crash when a DEX they're using gets updated.
+ */
+ LOGD("ODEX file is stale or bad; removing and retrying (%s)\n",
+ cacheFileName);
+ if (ftruncate(fd, 0) != 0) {
+ LOGW("Warning: unable to truncate cache file '%s': %s\n",
+ cacheFileName, strerror(errno));
+ /* keep going */
+ }
+ if (unlink(cacheFileName) != 0) {
+ LOGW("Warning: unable to remove cache file '%s': %d %s\n",
+ cacheFileName, errno, strerror(errno));
+ /* keep going; permission failure should probably be fatal */
+ }
+ LOGVV("DexOpt: unlocking cache file %s\n", cacheFileName);
+ flock(fd, LOCK_UN);
+ close(fd);
+ goto retry;
+ } else {
+ LOGV("DexOpt: good deps in cache file\n");
+ }
+ }
+
+ assert(fd >= 0);
+ return fd;
+
+close_fail:
+ flock(fd, LOCK_UN);
+ close(fd);
+ return -1;
+}
+
+/*
+ * Unlock the file descriptor.
+ *
+ * Returns "true" on success.
+ */
+bool dvmUnlockCachedDexFile(int fd)
+{
+ LOGVV("DexOpt: unlocking cache file fd=%d\n", fd);
+ return (flock(fd, LOCK_UN) == 0);
+}
+
+
+/*
+ * Given a descriptor for a file with DEX data in it, produce an
+ * optimized version.
+ *
+ * The file pointed to by "fd" is expected to be a locked shared resource
+ * (or private); we make no efforts to enforce multi-process correctness
+ * here.
+ *
+ * "fileName" is only used for debug output. "modWhen" and "crc" are stored
+ * in the dependency set.
+ *
+ * The "isBootstrap" flag determines how the optimizer and verifier handle
+ * package-scope access checks. When optimizing, we only load the bootstrap
+ * class DEX files and the target DEX, so the flag determines whether the
+ * target DEX classes are given a (synthetic) non-NULL classLoader pointer.
+ * This only really matters if the target DEX contains classes that claim to
+ * be in the same package as bootstrap classes.
+ *
+ * The optimizer will need to load every class in the target DEX file.
+ * This is generally undesirable, so we start a subprocess to do the
+ * work and wait for it to complete.
+ *
+ * Returns "true" on success. All data will have been written to "fd".
+ */
+bool dvmOptimizeDexFile(int fd, off_t dexOffset, long dexLength,
+ const char* fileName, u4 modWhen, u4 crc, bool isBootstrap)
+{
+ const char* lastPart = strrchr(fileName, '/');
+ if (lastPart != NULL)
+ lastPart++;
+ else
+ lastPart = fileName;
+
+ LOGD("DexOpt: --- BEGIN '%s' (bootstrap=%d) ---\n", lastPart, isBootstrap);
+
+ pid_t pid;
+
+ /*
+ * This could happen if something in our bootclasspath, which we thought
+ * was all optimized, got rejected.
+ */
+ if (gDvm.optimizing) {
+ LOGW("Rejecting recursive optimization attempt on '%s'\n", fileName);
+ return false;
+ }
+
+ pid = fork();
+ if (pid == 0) {
+ static const int kUseValgrind = 0;
+ static const char* kDexOptBin = "/bin/dexopt";
+ static const char* kValgrinder = "/usr/bin/valgrind";
+ static const int kFixedArgCount = 10;
+ static const int kValgrindArgCount = 5;
+ static const int kMaxIntLen = 12; // '-'+10dig+'\0' -OR- 0x+8dig
+ int bcpSize = dvmGetBootPathSize();
+ int argc = kFixedArgCount + bcpSize
+ + (kValgrindArgCount * kUseValgrind);
+ char* argv[argc+1]; // last entry is NULL
+ char values[argc][kMaxIntLen];
+ char* execFile;
+ char* androidRoot;
+ int flags;
+
+ /* change process groups, so we don't clash with ProcessManager */
+ setpgid(0, 0);
+
+ /* full path to optimizer */
+ androidRoot = getenv("ANDROID_ROOT");
+ if (androidRoot == NULL) {
+ LOGW("ANDROID_ROOT not set, defaulting to /system\n");
+ androidRoot = "/system";
+ }
+ execFile = malloc(strlen(androidRoot) + strlen(kDexOptBin) + 1);
+ strcpy(execFile, androidRoot);
+ strcat(execFile, kDexOptBin);
+
+ /*
+ * Create arg vector.
+ */
+ int curArg = 0;
+
+ if (kUseValgrind) {
+ /* probably shouldn't ship the hard-coded path */
+ argv[curArg++] = (char*)kValgrinder;
+ argv[curArg++] = "--tool=memcheck";
+ argv[curArg++] = "--leak-check=yes"; // check for leaks too
+ argv[curArg++] = "--leak-resolution=med"; // increase from 2 to 4
+ argv[curArg++] = "--num-callers=16"; // default is 12
+ assert(curArg == kValgrindArgCount);
+ }
+ argv[curArg++] = execFile;
+
+ argv[curArg++] = "--dex";
+
+ sprintf(values[2], "%d", DALVIK_VM_BUILD);
+ argv[curArg++] = values[2];
+
+ sprintf(values[3], "%d", fd);
+ argv[curArg++] = values[3];
+
+ sprintf(values[4], "%d", (int) dexOffset);
+ argv[curArg++] = values[4];
+
+ sprintf(values[5], "%d", (int) dexLength);
+ argv[curArg++] = values[5];
+
+ argv[curArg++] = (char*)fileName;
+
+ sprintf(values[7], "%d", (int) modWhen);
+ argv[curArg++] = values[7];
+
+ sprintf(values[8], "%d", (int) crc);
+ argv[curArg++] = values[8];
+
+ flags = 0;
+ if (gDvm.dexOptMode != OPTIMIZE_MODE_NONE) {
+ flags |= DEXOPT_OPT_ENABLED;
+ if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)
+ flags |= DEXOPT_OPT_ALL;
+ }
+ if (gDvm.classVerifyMode != VERIFY_MODE_NONE) {
+ flags |= DEXOPT_VERIFY_ENABLED;
+ if (gDvm.classVerifyMode == VERIFY_MODE_ALL)
+ flags |= DEXOPT_VERIFY_ALL;
+ }
+ if (isBootstrap)
+ flags |= DEXOPT_IS_BOOTSTRAP;
+ if (gDvm.generateRegisterMaps)
+ flags |= DEXOPT_GEN_REGISTER_MAPS;
+ sprintf(values[9], "%d", flags);
+ argv[curArg++] = values[9];
+
+ assert(((!kUseValgrind && curArg == kFixedArgCount) ||
+ ((kUseValgrind && curArg == kFixedArgCount+kValgrindArgCount))));
+
+ ClassPathEntry* cpe;
+ for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
+ argv[curArg++] = cpe->fileName;
+ }
+ assert(curArg == argc);
+
+ argv[curArg] = NULL;
+
+ if (kUseValgrind)
+ execv(kValgrinder, argv);
+ else
+ execv(execFile, argv);
+
+ LOGE("execv '%s'%s failed: %s\n", execFile,
+ kUseValgrind ? " [valgrind]" : "", strerror(errno));
+ exit(1);
+ } else {
+ LOGV("DexOpt: waiting for verify+opt, pid=%d\n", (int) pid);
+ int status;
+ pid_t gotPid;
+ int oldStatus;
+
+ /*
+ * Wait for the optimization process to finish. We go into VMWAIT
+ * mode here so GC suspension won't have to wait for us.
+ */
+ oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
+ while (true) {
+ gotPid = waitpid(pid, &status, 0);
+ if (gotPid == -1 && errno == EINTR) {
+ LOGD("waitpid interrupted, retrying\n");
+ } else {
+ break;
+ }
+ }
+ dvmChangeStatus(NULL, oldStatus);
+ if (gotPid != pid) {
+ LOGE("waitpid failed: wanted %d, got %d: %s\n",
+ (int) pid, (int) gotPid, strerror(errno));
+ return false;
+ }
+
+ if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
+ LOGD("DexOpt: --- END '%s' (success) ---\n", lastPart);
+ return true;
+ } else {
+ LOGW("DexOpt: --- END '%s' --- status=0x%04x, process failed\n",
+ lastPart, status);
+ return false;
+ }
+ }
+}
+
+/*
+ * Do the actual optimization. This is executed in the dexopt process.
+ *
+ * For best use of disk/memory, we want to extract once and perform
+ * optimizations in place. If the file has to expand or contract
+ * to match local structure padding/alignment expectations, we want
+ * to do the rewrite as part of the extract, rather than extracting
+ * into a temp file and slurping it back out. (The structure alignment
+ * is currently correct for all platforms, and this isn't expected to
+ * change, so we should be okay with having it already extracted.)
+ *
+ * Returns "true" on success.
+ */
+bool dvmContinueOptimization(int fd, off_t dexOffset, long dexLength,
+ const char* fileName, u4 modWhen, u4 crc, bool isBootstrap)
+{
+ DexClassLookup* pClassLookup = NULL;
+ RegisterMapBuilder* pRegMapBuilder = NULL;
+ u4 headerFlags = 0;
+
+ assert(gDvm.optimizing);
+
+ LOGV("Continuing optimization (%s, isb=%d, vfy=%d, opt=%d)\n",
+ fileName, isBootstrap, doVerify, doOpt);
+
+ assert(dexOffset >= 0);
+
+ /* quick test so we don't blow up on empty file */
+ if (dexLength < (int) sizeof(DexHeader)) {
+ LOGE("too small to be DEX\n");
+ return false;
+ }
+ if (dexOffset < (int) sizeof(DexOptHeader)) {
+ LOGE("not enough room for opt header\n");
+ return false;
+ }
+
+ bool result = false;
+
+ /*
+ * Drop this into a global so we don't have to pass it around. We could
+ * also add a field to DexFile, but since it only pertains to DEX
+ * creation that probably doesn't make sense.
+ */
+ gDvm.optimizingBootstrapClass = isBootstrap;
+
+ {
+ /*
+ * Map the entire file (so we don't have to worry about page
+ * alignment). The expectation is that the output file contains
+ * our DEX data plus room for a small header.
+ */
+ bool success;
+ void* mapAddr;
+ mapAddr = mmap(NULL, dexOffset + dexLength, PROT_READ|PROT_WRITE,
+ MAP_SHARED, fd, 0);
+ if (mapAddr == MAP_FAILED) {
+ LOGE("unable to mmap DEX cache: %s\n", strerror(errno));
+ goto bail;
+ }
+
+ /*
+ * Rewrite the file. Byte reordering, structure realigning,
+ * class verification, and bytecode optimization are all performed
+ * here.
+ *
+ * In theory the file could change size and bits could shift around.
+ * In practice this would be annoying to deal with, so the file
+ * layout is designed so that it can always be rewritten in place.
+ *
+ * This sets "headerFlags" and creates the class lookup table as
+ * part of doing the processing.
+ */
+ success = rewriteDex(((u1*) mapAddr) + dexOffset, dexLength,
+ &headerFlags, &pClassLookup);
+
+ if (success) {
+ DvmDex* pDvmDex = NULL;
+ u1* dexAddr = ((u1*) mapAddr) + dexOffset;
+
+ if (dvmDexFileOpenPartial(dexAddr, dexLength, &pDvmDex) != 0) {
+ LOGE("Unable to create DexFile\n");
+ success = false;
+ } else {
+ /*
+ * If configured to do so, generate register map output
+ * for all verified classes. The register maps were
+ * generated during verification, and will now be serialized.
+ */
+ if (gDvm.generateRegisterMaps) {
+ pRegMapBuilder = dvmGenerateRegisterMaps(pDvmDex);
+ if (pRegMapBuilder == NULL) {
+ LOGE("Failed generating register maps\n");
+ success = false;
+ }
+ }
+
+ DexHeader* pHeader = (DexHeader*)pDvmDex->pHeader;
+ updateChecksum(dexAddr, dexLength, pHeader);
+
+ dvmDexFileFree(pDvmDex);
+ }
+ }
+
+ /* unmap the read-write version, forcing writes to disk */
+ if (msync(mapAddr, dexOffset + dexLength, MS_SYNC) != 0) {
+ LOGW("msync failed: %s\n", strerror(errno));
+ // weird, but keep going
+ }
+#if 1
+ /*
+ * This causes clean shutdown to fail, because we have loaded classes
+ * that point into it. For the optimizer this isn't a problem,
+ * because it's more efficient for the process to simply exit.
+ * Exclude this code when doing clean shutdown for valgrind.
+ */
+ if (munmap(mapAddr, dexOffset + dexLength) != 0) {
+ LOGE("munmap failed: %s\n", strerror(errno));
+ goto bail;
+ }
+#endif
+
+ if (!success)
+ goto bail;
+ }
+
+ /* get start offset, and adjust deps start for 64-bit alignment */
+ off_t depsOffset, optOffset, endOffset, adjOffset;
+ int depsLength, optLength;
+ u4 optChecksum;
+
+ depsOffset = lseek(fd, 0, SEEK_END);
+ if (depsOffset < 0) {
+ LOGE("lseek to EOF failed: %s\n", strerror(errno));
+ goto bail;
+ }
+ adjOffset = (depsOffset + 7) & ~(0x07);
+ if (adjOffset != depsOffset) {
+ LOGV("Adjusting deps start from %d to %d\n",
+ (int) depsOffset, (int) adjOffset);
+ depsOffset = adjOffset;
+ lseek(fd, depsOffset, SEEK_SET);
+ }
+
+ /*
+ * Append the dependency list.
+ */
+ if (writeDependencies(fd, modWhen, crc) != 0) {
+ LOGW("Failed writing dependencies\n");
+ goto bail;
+ }
+
+ /* compute deps length, then adjust opt start for 64-bit alignment */
+ optOffset = lseek(fd, 0, SEEK_END);
+ depsLength = optOffset - depsOffset;
+
+ adjOffset = (optOffset + 7) & ~(0x07);
+ if (adjOffset != optOffset) {
+ LOGV("Adjusting opt start from %d to %d\n",
+ (int) optOffset, (int) adjOffset);
+ optOffset = adjOffset;
+ lseek(fd, optOffset, SEEK_SET);
+ }
+
+ /*
+ * Append any optimized pre-computed data structures.
+ */
+ if (!writeOptData(fd, pClassLookup, pRegMapBuilder)) {
+ LOGW("Failed writing opt data\n");
+ goto bail;
+ }
+
+ endOffset = lseek(fd, 0, SEEK_END);
+ optLength = endOffset - optOffset;
+
+ /* compute checksum from start of deps to end of opt area */
+ if (!computeFileChecksum(fd, depsOffset,
+ (optOffset+optLength) - depsOffset, &optChecksum))
+ {
+ goto bail;
+ }
+
+ /*
+ * Output the "opt" header with all values filled in and a correct
+ * magic number.
+ */
+ DexOptHeader optHdr;
+ memset(&optHdr, 0xff, sizeof(optHdr));
+ memcpy(optHdr.magic, DEX_OPT_MAGIC, 4);
+ memcpy(optHdr.magic+4, DEX_OPT_MAGIC_VERS, 4);
+ optHdr.dexOffset = (u4) dexOffset;
+ optHdr.dexLength = (u4) dexLength;
+ optHdr.depsOffset = (u4) depsOffset;
+ optHdr.depsLength = (u4) depsLength;
+ optHdr.optOffset = (u4) optOffset;
+ optHdr.optLength = (u4) optLength;
+
+ optHdr.flags = headerFlags;
+ optHdr.checksum = optChecksum;
+
+ fsync(fd); /* ensure previous writes go before header is written */
+
+ lseek(fd, 0, SEEK_SET);
+ if (sysWriteFully(fd, &optHdr, sizeof(optHdr), "DexOpt opt header") != 0)
+ goto bail;
+
+ LOGV("Successfully wrote DEX header\n");
+ result = true;
+
+ //dvmRegisterMapDumpStats();
+
+bail:
+ dvmFreeRegisterMapBuilder(pRegMapBuilder);
+ free(pClassLookup);
+ return result;
+}
+
+
+/*
+ * Perform in-place rewrites on a memory-mapped DEX file.
+ *
+ * This happens in a short-lived child process, so we can go nutty with
+ * loading classes and allocating memory.
+ */
+static bool rewriteDex(u1* addr, int len, u4* pHeaderFlags,
+ DexClassLookup** ppClassLookup)
+{
+ u8 prepWhen, loadWhen, verifyOptWhen;
+ DvmDex* pDvmDex = NULL;
+ bool doVerify, doOpt;
+ bool result = false;
+
+ *pHeaderFlags = 0;
+
+ /* if the DEX is in the wrong byte order, swap it now */
+ if (dexSwapAndVerify(addr, len) != 0)
+ goto bail;
+#if __BYTE_ORDER != __LITTLE_ENDIAN
+ *pHeaderFlags |= DEX_OPT_FLAG_BIG;
+#endif
+
+ if (gDvm.classVerifyMode == VERIFY_MODE_NONE)
+ doVerify = false;
+ else if (gDvm.classVerifyMode == VERIFY_MODE_REMOTE)
+ doVerify = !gDvm.optimizingBootstrapClass;
+ else /*if (gDvm.classVerifyMode == VERIFY_MODE_ALL)*/
+ doVerify = true;
+
+ if (gDvm.dexOptMode == OPTIMIZE_MODE_NONE)
+ doOpt = false;
+ else if (gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED)
+ doOpt = doVerify;
+ else /*if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)*/
+ doOpt = true;
+
+ /* TODO: decide if this is actually useful */
+ if (doVerify)
+ *pHeaderFlags |= DEX_FLAG_VERIFIED;
+ if (doOpt)
+ *pHeaderFlags |= DEX_OPT_FLAG_FIELDS | DEX_OPT_FLAG_INVOCATIONS;
+
+ /*
+ * Now that the DEX file can be read directly, create a DexFile struct
+ * for it.
+ */
+ if (dvmDexFileOpenPartial(addr, len, &pDvmDex) != 0) {
+ LOGE("Unable to create DexFile\n");
+ goto bail;
+ }
+
+ /*
+ * Create the class lookup table. This will eventually be appended
+ * to the end of the .odex.
+ */
+ *ppClassLookup = dexCreateClassLookup(pDvmDex->pDexFile);
+ if (*ppClassLookup == NULL)
+ goto bail;
+
+ /*
+ * If we're not going to attempt to verify or optimize the classes,
+ * there's no value in loading them, so bail out early.
+ */
+ if (!doVerify && !doOpt) {
+ result = true;
+ goto bail;
+ }
+
+ /* this is needed for the next part */
+ pDvmDex->pDexFile->pClassLookup = *ppClassLookup;
+
+ prepWhen = dvmGetRelativeTimeUsec();
+
+ /*
+ * Load all classes found in this DEX file. If they fail to load for
+ * some reason, they won't get verified (which is as it should be).
+ */
+ if (!loadAllClasses(pDvmDex))
+ goto bail;
+ loadWhen = dvmGetRelativeTimeUsec();
+
+ /*
+ * Verify and optimize all classes in the DEX file (command-line
+ * options permitting).
+ *
+ * This is best-effort, so there's really no way for dexopt to
+ * fail at this point.
+ */
+ verifyAndOptimizeClasses(pDvmDex->pDexFile, doVerify, doOpt);
+ verifyOptWhen = dvmGetRelativeTimeUsec();
+
+ const char* msgStr = "???";
+ if (doVerify && doOpt)
+ msgStr = "verify+opt";
+ else if (doVerify)
+ msgStr = "verify";
+ else if (doOpt)
+ msgStr = "opt";
+ LOGD("DexOpt: load %dms, %s %dms\n",
+ (int) (loadWhen - prepWhen) / 1000,
+ msgStr,
+ (int) (verifyOptWhen - loadWhen) / 1000);
+
+ result = true;
+
+bail:
+ /* free up storage */
+ dvmDexFileFree(pDvmDex);
+
+ return result;
+}
+
+/*
+ * Try to load all classes in the specified DEX. If they have some sort
+ * of broken dependency, e.g. their superclass lives in a different DEX
+ * that wasn't previously loaded into the bootstrap class path, loading
+ * will fail. This is the desired behavior.
+ *
+ * We have no notion of class loader at this point, so we load all of
+ * the classes with the bootstrap class loader. It turns out this has
+ * exactly the behavior we want, and has no ill side effects because we're
+ * running in a separate process and anything we load here will be forgotten.
+ *
+ * We set the CLASS_MULTIPLE_DEFS flag here if we see multiple definitions.
+ * This works because we only call here as part of optimization / pre-verify,
+ * not during verification as part of loading a class into a running VM.
+ *
+ * This returns "false" if the world is too screwed up to do anything
+ * useful at all.
+ */
+static bool loadAllClasses(DvmDex* pDvmDex)
+{
+ u4 count = pDvmDex->pDexFile->pHeader->classDefsSize;
+ u4 idx;
+ int loaded = 0;
+
+ LOGV("DexOpt: +++ trying to load %d classes\n", count);
+
+ dvmSetBootPathExtraDex(pDvmDex);
+
+ /*
+ * We have some circularity issues with Class and Object that are most
+ * easily avoided by ensuring that Object is never the first thing we
+ * try to find. Take care of that here. (We only need to do this when
+ * loading classes from the DEX file that contains Object, and only
+ * when Object comes first in the list, but it costs very little to
+ * do it in all cases.)
+ */
+ if (dvmFindSystemClass("Ljava/lang/Class;") == NULL) {
+ LOGE("ERROR: java.lang.Class does not exist!\n");
+ return false;
+ }
+
+ for (idx = 0; idx < count; idx++) {
+ const DexClassDef* pClassDef;
+ const char* classDescriptor;
+ ClassObject* newClass;
+
+ pClassDef = dexGetClassDef(pDvmDex->pDexFile, idx);
+ classDescriptor =
+ dexStringByTypeIdx(pDvmDex->pDexFile, pClassDef->classIdx);
+
+ LOGV("+++ loading '%s'", classDescriptor);
+ //newClass = dvmDefineClass(pDexFile, classDescriptor,
+ // NULL);
+ newClass = dvmFindSystemClassNoInit(classDescriptor);
+ if (newClass == NULL) {
+ LOGV("DexOpt: failed loading '%s'\n", classDescriptor);
+ dvmClearOptException(dvmThreadSelf());
+ } else if (newClass->pDvmDex != pDvmDex) {
+ /*
+ * We don't load the new one, and we tag the first one found
+ * with the "multiple def" flag so the resolver doesn't try
+ * to make it available.
+ */
+ LOGD("DexOpt: '%s' has an earlier definition; blocking out\n",
+ classDescriptor);
+ SET_CLASS_FLAG(newClass, CLASS_MULTIPLE_DEFS);
+ } else {
+ loaded++;
+ }
+ }
+ LOGV("DexOpt: +++ successfully loaded %d classes\n", loaded);
+
+ dvmSetBootPathExtraDex(NULL);
+ return true;
+}
+
+/*
+ * Verify and/or optimize all classes that were successfully loaded from
+ * this DEX file.
+ */
+static void verifyAndOptimizeClasses(DexFile* pDexFile, bool doVerify,
+ bool doOpt)
+{
+ u4 count = pDexFile->pHeader->classDefsSize;
+ u4 idx;
+
+ /*
+ * Create a data structure for use by the bytecode optimizer. We
+ * stuff it into a global so we don't have to pass it around as
+ * a function argument.
+ *
+ * We could create this at VM startup, but there's no need to do so
+ * unless we're optimizing, which means we're in dexopt, and we're
+ * only going to call here once.
+ */
+ if (doOpt) {
+ gDvm.inlineSubs = dvmCreateInlineSubsTable();
+ if (gDvm.inlineSubs == NULL)
+ return;
+ }
+
+ for (idx = 0; idx < count; idx++) {
+ const DexClassDef* pClassDef;
+ const char* classDescriptor;
+ ClassObject* clazz;
+
+ pClassDef = dexGetClassDef(pDexFile, idx);
+ classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
+
+ /* all classes are loaded into the bootstrap class loader */
+ clazz = dvmLookupClass(classDescriptor, NULL, false);
+ if (clazz != NULL) {
+ verifyAndOptimizeClass(pDexFile, clazz, pClassDef, doVerify, doOpt);
+
+ } else {
+ // TODO: log when in verbose mode
+ LOGV("DexOpt: not optimizing unavailable class '%s'\n",
+ classDescriptor);
+ }
+ }
+
+ if (gDvm.inlineSubs != NULL) {
+ dvmFreeInlineSubsTable(gDvm.inlineSubs);
+ gDvm.inlineSubs = NULL;
+ }
+}
+
+/*
+ * Verify and/or optimize a specific class.
+ */
+static void verifyAndOptimizeClass(DexFile* pDexFile, ClassObject* clazz,
+ const DexClassDef* pClassDef, bool doVerify, bool doOpt)
+{
+ const char* classDescriptor;
+ bool verified = false;
+
+ classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
+
+ /*
+ * First, try to verify it.
+ */
+ if (doVerify) {
+ if (clazz->pDvmDex->pDexFile != pDexFile) {
+ LOGD("DexOpt: not verifying '%s': multiple definitions\n",
+ classDescriptor);
+ } else {
+ if (dvmVerifyClass(clazz)) {
+ /*
+ * Set the "is preverified" flag in the DexClassDef. We
+ * do it here, rather than in the ClassObject structure,
+ * because the DexClassDef is part of the odex file.
+ */
+ assert((clazz->accessFlags & JAVA_FLAGS_MASK) ==
+ pClassDef->accessFlags);
+ ((DexClassDef*)pClassDef)->accessFlags |= CLASS_ISPREVERIFIED;
+ verified = true;
+ } else {
+ // TODO: log when in verbose mode
+ LOGV("DexOpt: '%s' failed verification\n", classDescriptor);
+ }
+ }
+ }
+
+ if (doOpt) {
+ if (!verified && gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED) {
+ LOGV("DexOpt: not optimizing '%s': not verified\n",
+ classDescriptor);
+ } else {
+ dvmOptimizeClass(clazz, false);
+
+ /* set the flag whether or not we actually changed anything */
+ ((DexClassDef*)pClassDef)->accessFlags |= CLASS_ISOPTIMIZED;
+ }
+ }
+}
+
+
+/*
+ * Get the cache file name from a ClassPathEntry.
+ */
+static const char* getCacheFileName(const ClassPathEntry* cpe)
+{
+ switch (cpe->kind) {
+ case kCpeJar:
+ return dvmGetJarFileCacheFileName((JarFile*) cpe->ptr);
+ case kCpeDex:
+ return dvmGetRawDexFileCacheFileName((RawDexFile*) cpe->ptr);
+ default:
+ LOGE("DexOpt: unexpected cpe kind %d\n", cpe->kind);
+ dvmAbort();
+ return NULL;
+ }
+}
+
+/*
+ * Get the SHA-1 signature.
+ */
+static const u1* getSignature(const ClassPathEntry* cpe)
+{
+ DvmDex* pDvmDex;
+
+ switch (cpe->kind) {
+ case kCpeJar:
+ pDvmDex = dvmGetJarFileDex((JarFile*) cpe->ptr);
+ break;
+ case kCpeDex:
+ pDvmDex = dvmGetRawDexFileDex((RawDexFile*) cpe->ptr);
+ break;
+ default:
+ LOGE("unexpected cpe kind %d\n", cpe->kind);
+ dvmAbort();
+ pDvmDex = NULL; // make gcc happy
+ }
+
+ assert(pDvmDex != NULL);
+ return pDvmDex->pDexFile->pHeader->signature;
+}
+
+
+/*
+ * Dependency layout:
+ * 4b Source file modification time, in seconds since 1970 UTC
+ * 4b CRC-32 from Zip entry, or Adler32 from source DEX header
+ * 4b Dalvik VM build number
+ * 4b Number of dependency entries that follow
+ * Dependency entries:
+ * 4b Name length (including terminating null)
+ * var Full path of cache entry (null terminated)
+ * 20b SHA-1 signature from source DEX file
+ *
+ * If this changes, update DEX_OPT_MAGIC_VERS.
+ */
+static const size_t kMinDepSize = 4 * 4;
+static const size_t kMaxDepSize = 4 * 4 + 2048; // sanity check
+
+/*
+ * Read the "opt" header, verify it, then read the dependencies section
+ * and verify that data as well.
+ *
+ * If "sourceAvail" is "true", this will verify that "modWhen" and "crc"
+ * match up with what is stored in the header. If they don't, we reject
+ * the file so that it can be recreated from the updated original. If
+ * "sourceAvail" isn't set, e.g. for a .odex file, we ignore these arguments.
+ *
+ * On successful return, the file will be seeked immediately past the
+ * "opt" header.
+ */
+bool dvmCheckOptHeaderAndDependencies(int fd, bool sourceAvail, u4 modWhen,
+ u4 crc, bool expectVerify, bool expectOpt)
+{
+ DexOptHeader optHdr;
+ u1* depData = NULL;
+ const u1* magic;
+ off_t posn;
+ int result = false;
+ ssize_t actual;
+
+ /*
+ * Start at the start. The "opt" header, when present, will always be
+ * the first thing in the file.
+ */
+ if (lseek(fd, 0, SEEK_SET) != 0) {
+ LOGE("DexOpt: failed to seek to start of file: %s\n", strerror(errno));
+ goto bail;
+ }
+
+ /*
+ * Read and do trivial verification on the opt header. The header is
+ * always in host byte order.
+ */
+ actual = read(fd, &optHdr, sizeof(optHdr));
+ if (actual < 0) {
+ LOGE("DexOpt: failed reading opt header: %s\n", strerror(errno));
+ goto bail;
+ } else if (actual != sizeof(optHdr)) {
+ LOGE("DexOpt: failed reading opt header (got %d of %zd)\n",
+ (int) actual, sizeof(optHdr));
+ goto bail;
+ }
+
+ magic = optHdr.magic;
+ if (memcmp(magic, DEX_MAGIC, 4) == 0) {
+ /* somebody probably pointed us at the wrong file */
+ LOGD("DexOpt: expected optimized DEX, found unoptimized\n");
+ goto bail;
+ } else if (memcmp(magic, DEX_OPT_MAGIC, 4) != 0) {
+ /* not a DEX file, or previous attempt was interrupted */
+ LOGD("DexOpt: incorrect opt magic number (0x%02x %02x %02x %02x)\n",
+ magic[0], magic[1], magic[2], magic[3]);
+ goto bail;
+ }
+ if (memcmp(magic+4, DEX_OPT_MAGIC_VERS, 4) != 0) {
+ LOGW("DexOpt: stale opt version (0x%02x %02x %02x %02x)\n",
+ magic[4], magic[5], magic[6], magic[7]);
+ goto bail;
+ }
+ if (optHdr.depsLength < kMinDepSize || optHdr.depsLength > kMaxDepSize) {
+ LOGW("DexOpt: weird deps length %d, bailing\n", optHdr.depsLength);
+ goto bail;
+ }
+
+ /*
+ * Do the header flags match up with what we want?
+ *
+ * This is useful because it allows us to automatically regenerate
+ * a file when settings change (e.g. verification is now mandatory),
+ * but can cause difficulties if the bootstrap classes we depend upon
+ * were handled differently than the current options specify. We get
+ * upset because they're not verified or optimized, but we're not able
+ * to regenerate them because the installer won't let us.
+ *
+ * (This is also of limited value when !sourceAvail.)
+ *
+ * So, for now, we essentially ignore "expectVerify" and "expectOpt"
+ * by limiting the match mask.
+ *
+ * The only thing we really can't handle is incorrect byte-ordering.
+ */
+ const u4 matchMask = DEX_OPT_FLAG_BIG;
+ u4 expectedFlags = 0;
+#if __BYTE_ORDER != __LITTLE_ENDIAN
+ expectedFlags |= DEX_OPT_FLAG_BIG;
+#endif
+ if (expectVerify)
+ expectedFlags |= DEX_FLAG_VERIFIED;
+ if (expectOpt)
+ expectedFlags |= DEX_OPT_FLAG_FIELDS | DEX_OPT_FLAG_INVOCATIONS;
+ if ((expectedFlags & matchMask) != (optHdr.flags & matchMask)) {
+ LOGI("DexOpt: header flag mismatch (0x%02x vs 0x%02x, mask=0x%02x)\n",
+ expectedFlags, optHdr.flags, matchMask);
+ goto bail;
+ }
+
+ posn = lseek(fd, optHdr.depsOffset, SEEK_SET);
+ if (posn < 0) {
+ LOGW("DexOpt: seek to deps failed: %s\n", strerror(errno));
+ goto bail;
+ }
+
+ /*
+ * Read all of the dependency stuff into memory.
+ */
+ depData = (u1*) malloc(optHdr.depsLength);
+ if (depData == NULL) {
+ LOGW("DexOpt: unable to allocate %d bytes for deps\n",
+ optHdr.depsLength);
+ goto bail;
+ }
+ actual = read(fd, depData, optHdr.depsLength);
+ if (actual < 0) {
+ LOGW("DexOpt: failed reading deps: %s\n", strerror(errno));
+ goto bail;
+ } else if (actual != (ssize_t) optHdr.depsLength) {
+ LOGW("DexOpt: failed reading deps: got %d of %d\n",
+ (int) actual, optHdr.depsLength);
+ goto bail;
+ }
+
+ /*
+ * Verify simple items.
+ */
+ const u1* ptr;
+ u4 val;
+
+ ptr = depData;
+ val = read4LE(&ptr);
+ if (sourceAvail && val != modWhen) {
+ LOGI("DexOpt: source file mod time mismatch (%08x vs %08x)\n",
+ val, modWhen);
+ goto bail;
+ }
+ val = read4LE(&ptr);
+ if (sourceAvail && val != crc) {
+ LOGI("DexOpt: source file CRC mismatch (%08x vs %08x)\n", val, crc);
+ goto bail;
+ }
+ val = read4LE(&ptr);
+ if (val != DALVIK_VM_BUILD) {
+ LOGD("DexOpt: VM build version mismatch (%d vs %d)\n",
+ val, DALVIK_VM_BUILD);
+ goto bail;
+ }
+
+ /*
+ * Verify dependencies on other cached DEX files. It must match
+ * exactly with what is currently defined in the bootclasspath.
+ */
+ ClassPathEntry* cpe;
+ u4 numDeps;
+
+ numDeps = read4LE(&ptr);
+ LOGV("+++ DexOpt: numDeps = %d\n", numDeps);
+ for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
+ const char* cacheFileName =
+ dvmPathToAbsolutePortion(getCacheFileName(cpe));
+ assert(cacheFileName != NULL); /* guaranteed by Class.c */
+
+ const u1* signature = getSignature(cpe);
+ size_t len = strlen(cacheFileName) +1;
+ u4 storedStrLen;
+
+ if (numDeps == 0) {
+ /* more entries in bootclasspath than in deps list */
+ LOGI("DexOpt: not all deps represented\n");
+ goto bail;
+ }
+
+ storedStrLen = read4LE(&ptr);
+ if (len != storedStrLen ||
+ strcmp(cacheFileName, (const char*) ptr) != 0)
+ {
+ LOGI("DexOpt: mismatch dep name: '%s' vs. '%s'\n",
+ cacheFileName, ptr);
+ goto bail;
+ }
+
+ ptr += storedStrLen;
+
+ if (memcmp(signature, ptr, kSHA1DigestLen) != 0) {
+ LOGI("DexOpt: mismatch dep signature for '%s'\n", cacheFileName);
+ goto bail;
+ }
+ ptr += kSHA1DigestLen;
+
+ LOGV("DexOpt: dep match on '%s'\n", cacheFileName);
+
+ numDeps--;
+ }
+
+ if (numDeps != 0) {
+ /* more entries in deps list than in classpath */
+ LOGI("DexOpt: Some deps went away\n");
+ goto bail;
+ }
+
+ // consumed all data and no more?
+ if (ptr != depData + optHdr.depsLength) {
+ LOGW("DexOpt: Spurious dep data? %d vs %d\n",
+ (int) (ptr - depData), optHdr.depsLength);
+ assert(false);
+ }
+
+ result = true;
+
+bail:
+ free(depData);
+ return result;
+}
+
+/*
+ * Write the dependency info to "fd" at the current file position.
+ */
+static int writeDependencies(int fd, u4 modWhen, u4 crc)
+{
+ u1* buf = NULL;
+ int result = -1;
+ ssize_t bufLen;
+ ClassPathEntry* cpe;
+ int numDeps;
+
+ /*
+ * Count up the number of completed entries in the bootclasspath.
+ */
+ numDeps = 0;
+ bufLen = 0;
+ for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
+ const char* cacheFileName =
+ dvmPathToAbsolutePortion(getCacheFileName(cpe));
+ assert(cacheFileName != NULL); /* guaranteed by Class.c */
+
+ LOGV("+++ DexOpt: found dep '%s'\n", cacheFileName);
+
+ numDeps++;
+ bufLen += strlen(cacheFileName) +1;
+ }
+
+ bufLen += 4*4 + numDeps * (4+kSHA1DigestLen);
+
+ buf = malloc(bufLen);
+
+ set4LE(buf+0, modWhen);
+ set4LE(buf+4, crc);
+ set4LE(buf+8, DALVIK_VM_BUILD);
+ set4LE(buf+12, numDeps);
+
+ // TODO: do we want to add dvmGetInlineOpsTableLength() here? Won't
+ // help us if somebody replaces an existing entry, but it'd catch
+ // additions/removals.
+
+ u1* ptr = buf + 4*4;
+ for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
+ const char* cacheFileName =
+ dvmPathToAbsolutePortion(getCacheFileName(cpe));
+ assert(cacheFileName != NULL); /* guaranteed by Class.c */
+
+ const u1* signature = getSignature(cpe);
+ int len = strlen(cacheFileName) +1;
+
+ if (ptr + 4 + len + kSHA1DigestLen > buf + bufLen) {
+ LOGE("DexOpt: overran buffer\n");
+ dvmAbort();
+ }
+
+ set4LE(ptr, len);
+ ptr += 4;
+ memcpy(ptr, cacheFileName, len);
+ ptr += len;
+ memcpy(ptr, signature, kSHA1DigestLen);
+ ptr += kSHA1DigestLen;
+ }
+
+ assert(ptr == buf + bufLen);
+
+ result = sysWriteFully(fd, buf, bufLen, "DexOpt dep info");
+
+ free(buf);
+ return result;
+}
+
+
+/*
+ * Write a block of data in "chunk" format.
+ *
+ * The chunk header fields are always in "native" byte order. If "size"
+ * is not a multiple of 8 bytes, the data area is padded out.
+ */
+static bool writeChunk(int fd, u4 type, const void* data, size_t size)
+{
+ union { /* save a syscall by grouping these together */
+ char raw[8];
+ struct {
+ u4 type;
+ u4 size;
+ } ts;
+ } header;
+
+ assert(sizeof(header) == 8);
+
+ LOGV("Writing chunk, type=%.4s size=%d\n", (char*) &type, size);
+
+ header.ts.type = type;
+ header.ts.size = (u4) size;
+ if (sysWriteFully(fd, &header, sizeof(header),
+ "DexOpt opt chunk header write") != 0)
+ {
+ return false;
+ }
+
+ if (size > 0) {
+ if (sysWriteFully(fd, data, size, "DexOpt opt chunk write") != 0)
+ return false;
+ }
+
+ /* if necessary, pad to 64-bit alignment */
+ if ((size & 7) != 0) {
+ int padSize = 8 - (size & 7);
+ LOGV("size was %d, inserting %d pad bytes\n", size, padSize);
+ lseek(fd, padSize, SEEK_CUR);
+ }
+
+ assert( ((int)lseek(fd, 0, SEEK_CUR) & 7) == 0);
+
+ return true;
+}
+
+/*
+ * Write opt data.
+ *
+ * We have different pieces, some of which may be optional. To make the
+ * most effective use of space, we use a "chunk" format, with a 4-byte
+ * type and a 4-byte length. We guarantee 64-bit alignment for the data,
+ * so it can be used directly when the file is mapped for reading.
+ */
+static bool writeOptData(int fd, const DexClassLookup* pClassLookup,
+ const RegisterMapBuilder* pRegMapBuilder)
+{
+ /* pre-computed class lookup hash table */
+ if (!writeChunk(fd, (u4) kDexChunkClassLookup,
+ pClassLookup, pClassLookup->size))
+ {
+ return false;
+ }
+
+ /* register maps (optional) */
+ if (pRegMapBuilder != NULL) {
+ if (!writeChunk(fd, (u4) kDexChunkRegisterMaps,
+ pRegMapBuilder->data, pRegMapBuilder->size))
+ {
+ return false;
+ }
+ }
+
+ /* write the end marker */
+ if (!writeChunk(fd, (u4) kDexChunkEnd, NULL, 0)) {
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Compute a checksum on a piece of an open file.
+ *
+ * File will be positioned at end of checksummed area.
+ *
+ * Returns "true" on success.
+ */
+static bool computeFileChecksum(int fd, off_t start, size_t length, u4* pSum)
+{
+ unsigned char readBuf[8192];
+ ssize_t actual;
+ uLong adler;
+
+ if (lseek(fd, start, SEEK_SET) != start) {
+ LOGE("Unable to seek to start of checksum area (%ld): %s\n",
+ (long) start, strerror(errno));
+ return false;
+ }
+
+ adler = adler32(0L, Z_NULL, 0);
+
+ while (length != 0) {
+ size_t wanted = (length < sizeof(readBuf)) ? length : sizeof(readBuf);
+ actual = read(fd, readBuf, wanted);
+ if (actual <= 0) {
+ LOGE("Read failed (%d) while computing checksum (len=%zu): %s\n",
+ (int) actual, length, strerror(errno));
+ return false;
+ }
+
+ adler = adler32(adler, readBuf, actual);
+
+ length -= actual;
+ }
+
+ *pSum = adler;
+ return true;
+}
+
+/*
+ * Update the Adler-32 checksum stored in the DEX file. This covers the
+ * swapped and optimized DEX data, but does not include the opt header
+ * or optimized data.
+ */
+static void updateChecksum(u1* addr, int len, DexHeader* pHeader)
+{
+ /*
+ * Rewrite the checksum. We leave the SHA-1 signature alone.
+ */
+ uLong adler = adler32(0L, Z_NULL, 0);
+ const int nonSum = sizeof(pHeader->magic) + sizeof(pHeader->checksum);
+
+ adler = adler32(adler, addr + nonSum, len - nonSum);
+ pHeader->checksum = adler;
+}
diff --git a/vm/analysis/DexPrepare.h b/vm/analysis/DexPrepare.h
new file mode 100644
index 0000000..bfa5fb5
--- /dev/null
+++ b/vm/analysis/DexPrepare.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * DEX preparation declarations.
+ */
+#ifndef _DALVIK_DEXPREPARE
+#define _DALVIK_DEXPREPARE
+
+/*
+ * Global DEX optimizer control. Determines the circumstances in which we
+ * try to rewrite instructions in the DEX file.
+ */
+typedef enum DexOptimizerMode {
+ OPTIMIZE_MODE_UNKNOWN = 0,
+ OPTIMIZE_MODE_NONE, /* never optimize */
+ OPTIMIZE_MODE_VERIFIED, /* only optimize verified classes (default) */
+ OPTIMIZE_MODE_ALL /* optimize all classes */
+} DexOptimizerMode;
+
+/* some additional bit flags for dexopt */
+enum DexoptFlags {
+ DEXOPT_OPT_ENABLED = 1, /* optimizations enabled? */
+ DEXOPT_OPT_ALL = 1 << 1, /* optimize when verify fails? */
+ DEXOPT_VERIFY_ENABLED = 1 << 2, /* verification enabled? */
+ DEXOPT_VERIFY_ALL = 1 << 3, /* verify bootstrap classes? */
+ DEXOPT_IS_BOOTSTRAP = 1 << 4, /* is dex in bootstrap class path? */
+ DEXOPT_GEN_REGISTER_MAPS = 1 << 5, /* generate register maps during vfy */
+ DEXOPT_UNIPROCESSOR = 1 << 6, /* specify uniprocessor target */
+ DEXOPT_SMP = 1 << 7 /* specify SMP target */
+};
+
+/*
+ * An enumeration of problems that can turn up during verification.
+ */
+typedef enum VerifyError {
+ VERIFY_ERROR_NONE = 0, /* no error; must be zero */
+ VERIFY_ERROR_GENERIC, /* VerifyError */
+
+ VERIFY_ERROR_NO_CLASS, /* NoClassDefFoundError */
+ VERIFY_ERROR_NO_FIELD, /* NoSuchFieldError */
+ VERIFY_ERROR_NO_METHOD, /* NoSuchMethodError */
+ VERIFY_ERROR_ACCESS_CLASS, /* IllegalAccessError */
+ VERIFY_ERROR_ACCESS_FIELD, /* IllegalAccessError */
+ VERIFY_ERROR_ACCESS_METHOD, /* IllegalAccessError */
+ VERIFY_ERROR_CLASS_CHANGE, /* IncompatibleClassChangeError */
+ VERIFY_ERROR_INSTANTIATION, /* InstantiationError */
+} VerifyError;
+
+/*
+ * Identifies the type of reference in the instruction that generated the
+ * verify error (e.g. VERIFY_ERROR_ACCESS_CLASS could come from a method,
+ * field, or class reference).
+ *
+ * This must fit in two bits.
+ */
+typedef enum VerifyErrorRefType {
+ VERIFY_ERROR_REF_CLASS = 0,
+ VERIFY_ERROR_REF_FIELD = 1,
+ VERIFY_ERROR_REF_METHOD = 2,
+} VerifyErrorRefType;
+
+#define kVerifyErrorRefTypeShift 6
+
+#define VERIFY_OK(_failure) ((_failure) == VERIFY_ERROR_NONE)
+
+/*
+ * Given the full path to a DEX or Jar file, and (if appropriate) the name
+ * within the Jar, open the optimized version from the cache.
+ *
+ * If "*pNewFile" is set, a new file has been created with only a stub
+ * "opt" header, and the caller is expected to fill in the blanks.
+ *
+ * Returns the file descriptor, locked and seeked past the "opt" header.
+ */
+int dvmOpenCachedDexFile(const char* fileName, const char* cachedFile,
+ u4 modWhen, u4 crc, bool isBootstrap, bool* pNewFile, bool createIfMissing);
+
+/*
+ * Unlock the specified file descriptor. Use in conjunction with
+ * dvmOpenCachedDexFile().
+ *
+ * Returns true on success.
+ */
+bool dvmUnlockCachedDexFile(int fd);
+
+/*
+ * Verify the contents of the "opt" header, and check the DEX file's
+ * dependencies on its source zip (if available).
+ */
+bool dvmCheckOptHeaderAndDependencies(int fd, bool sourceAvail, u4 modWhen,
+ u4 crc, bool expectVerify, bool expectOpt);
+
+/*
+ * Optimize a DEX file. The file must start with the "opt" header, followed
+ * by the plain DEX data. It must be mmap()able.
+ *
+ * "fileName" is only used for debug output.
+ */
+bool dvmOptimizeDexFile(int fd, off_t dexOffset, long dexLen,
+ const char* fileName, u4 modWhen, u4 crc, bool isBootstrap);
+
+/*
+ * Continue the optimization process on the other side of a fork/exec.
+ */
+bool dvmContinueOptimization(int fd, off_t dexOffset, long dexLength,
+ const char* fileName, u4 modWhen, u4 crc, bool isBootstrap);
+
+#endif /*_DALVIK_DEXPREPARE*/
diff --git a/vm/analysis/DexVerify.c b/vm/analysis/DexVerify.c
new file mode 100644
index 0000000..b934481
--- /dev/null
+++ b/vm/analysis/DexVerify.c
@@ -0,0 +1,697 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik classfile verification. This file contains the verifier entry
+ * points and the static constraint checks.
+ */
+#include "Dalvik.h"
+#include "analysis/CodeVerify.h"
+
+
+/* fwd */
+static bool verifyMethod(Method* meth);
+static bool verifyInstructions(VerifierData* vdata);
+
+
+/*
+ * Initialize some things we need for verification.
+ */
+bool dvmVerificationStartup(void)
+{
+ gDvm.instrWidth = dexCreateInstrWidthTable();
+ gDvm.instrFormat = dexCreateInstrFormatTable();
+ gDvm.instrFlags = dexCreateInstrFlagsTable();
+ if (gDvm.instrWidth == NULL || gDvm.instrFormat == NULL ||
+ gDvm.instrFlags == NULL)
+ {
+ LOGE("Unable to create instruction tables\n");
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Free up some things we needed for verification.
+ */
+void dvmVerificationShutdown(void)
+{
+ free(gDvm.instrWidth);
+ free(gDvm.instrFormat);
+ free(gDvm.instrFlags);
+}
+
+
+/*
+ * Verify a class.
+ *
+ * By the time we get here, the value of gDvm.classVerifyMode should already
+ * have been factored in. If you want to call into the verifier even
+ * though verification is disabled, that's your business.
+ *
+ * Returns "true" on success.
+ */
+bool dvmVerifyClass(ClassObject* clazz)
+{
+ int i;
+
+ if (dvmIsClassVerified(clazz)) {
+ LOGD("Ignoring duplicate verify attempt on %s\n", clazz->descriptor);
+ return true;
+ }
+
+ for (i = 0; i < clazz->directMethodCount; i++) {
+ if (!verifyMethod(&clazz->directMethods[i])) {
+ LOG_VFY("Verifier rejected class %s\n", clazz->descriptor);
+ return false;
+ }
+ }
+ for (i = 0; i < clazz->virtualMethodCount; i++) {
+ if (!verifyMethod(&clazz->virtualMethods[i])) {
+ LOG_VFY("Verifier rejected class %s\n", clazz->descriptor);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+/*
+ * Perform verification on a single method.
+ *
+ * We do this in three passes:
+ * (1) Walk through all code units, determining instruction locations,
+ * widths, and other characteristics.
+ * (2) Walk through all code units, performing static checks on
+ * operands.
+ * (3) Iterate through the method, checking type safety and looking
+ * for code flow problems.
+ *
+ * Some checks may be bypassed depending on the verification mode. We can't
+ * turn this stuff off completely if we want to do "exact" GC.
+ *
+ * TODO: cite source?
+ * Confirmed here:
+ * - code array must not be empty
+ * - (N/A) code_length must be less than 65536
+ * Confirmed by dvmComputeCodeWidths():
+ * - opcode of first instruction begins at index 0
+ * - only documented instructions may appear
+ * - each instruction follows the last
+ * - last byte of last instruction is at (code_length-1)
+ */
+static bool verifyMethod(Method* meth)
+{
+ bool result = false;
+ int newInstanceCount;
+
+ /*
+ * Verifier state blob. Various values will be cached here so we
+ * can avoid expensive lookups and pass fewer arguments around.
+ */
+ VerifierData vdata;
+#if 1 // ndef NDEBUG
+ memset(&vdata, 0x99, sizeof(vdata));
+#endif
+
+ vdata.method = meth;
+ vdata.insnsSize = dvmGetMethodInsnsSize(meth);
+ vdata.insnRegCount = meth->registersSize;
+ vdata.insnFlags = NULL;
+ vdata.uninitMap = NULL;
+
+ /*
+ * If there aren't any instructions, make sure that's expected, then
+ * exit successfully. Note: for native methods, meth->insns gets set
+ * to a native function pointer on first call, so don't use that as
+ * an indicator.
+ */
+ if (vdata.insnsSize == 0) {
+ if (!dvmIsNativeMethod(meth) && !dvmIsAbstractMethod(meth)) {
+ LOG_VFY_METH(meth,
+ "VFY: zero-length code in concrete non-native method\n");
+ goto bail;
+ }
+
+ goto success;
+ }
+
+ /*
+ * Sanity-check the register counts. ins + locals = registers, so make
+ * sure that ins <= registers.
+ */
+ if (meth->insSize > meth->registersSize) {
+ LOG_VFY_METH(meth, "VFY: bad register counts (ins=%d regs=%d)\n",
+ meth->insSize, meth->registersSize);
+ goto bail;
+ }
+
+ /*
+ * Allocate and populate an array to hold instruction data.
+ *
+ * TODO: Consider keeping a reusable pre-allocated array sitting
+ * around for smaller methods.
+ */
+ vdata.insnFlags = (InsnFlags*)
+ calloc(dvmGetMethodInsnsSize(meth), sizeof(InsnFlags));
+ if (vdata.insnFlags == NULL)
+ goto bail;
+
+ /*
+ * Compute the width of each instruction and store the result in insnFlags.
+ * Count up the #of occurrences of new-instance instructions while we're
+ * at it.
+ */
+ if (!dvmComputeCodeWidths(meth, vdata.insnFlags, &newInstanceCount))
+ goto bail;
+
+ /*
+ * Allocate a map to hold the classes of uninitialized instances.
+ */
+ vdata.uninitMap = dvmCreateUninitInstanceMap(meth, vdata.insnFlags,
+ newInstanceCount);
+ if (vdata.uninitMap == NULL)
+ goto bail;
+
+ /*
+ * Set the "in try" flags for all instructions guarded by a "try" block.
+ */
+ if (!dvmSetTryFlags(meth, vdata.insnFlags))
+ goto bail;
+
+ /*
+ * Perform static instruction verification.
+ */
+ if (!verifyInstructions(&vdata))
+ goto bail;
+
+ /*
+ * Do code-flow analysis. Do this after verifying the branch targets
+ * so we don't need to worry about it here.
+ *
+ * If there are no registers, we don't need to do much in the way of
+ * analysis, but we still need to verify that nothing actually tries
+ * to use a register.
+ */
+ if (!dvmVerifyCodeFlow(&vdata)) {
+ //LOGD("+++ %s failed code flow\n", meth->name);
+ goto bail;
+ }
+
+success:
+ result = true;
+
+bail:
+ dvmFreeUninitInstanceMap(vdata.uninitMap);
+ free(vdata.insnFlags);
+ return result;
+}
+
+
+/*
+ * Verify an array data table. "curOffset" is the offset of the fill-array-data
+ * instruction.
+ */
+static bool checkArrayData(const Method* meth, int curOffset)
+{
+ const int insnCount = dvmGetMethodInsnsSize(meth);
+ const u2* insns = meth->insns + curOffset;
+ const u2* arrayData;
+ int valueCount, valueWidth, tableSize;
+ int offsetToArrayData;
+
+ assert(curOffset >= 0 && curOffset < insnCount);
+
+ /* make sure the start of the array data table is in range */
+ offsetToArrayData = insns[1] | (((s4)insns[2]) << 16);
+ if (curOffset + offsetToArrayData < 0 ||
+ curOffset + offsetToArrayData + 2 >= insnCount)
+ {
+ LOG_VFY_METH(meth,
+ "VFY: invalid array data start: at %d, data offset %d, count %d\n",
+ curOffset, offsetToArrayData, insnCount);
+ return false;
+ }
+
+ /* offset to array data table is a relative branch-style offset */
+ arrayData = insns + offsetToArrayData;
+
+ /* make sure the table is 32-bit aligned */
+ if ((((u4) arrayData) & 0x03) != 0) {
+ LOG_VFY_METH(meth,
+ "VFY: unaligned array data table: at %d, data offset %d\n",
+ curOffset, offsetToArrayData);
+ return false;
+ }
+
+ valueWidth = arrayData[1];
+ valueCount = *(u4*)(&arrayData[2]);
+
+ tableSize = 4 + (valueWidth * valueCount + 1) / 2;
+
+ /* make sure the end of the switch is in range */
+ if (curOffset + offsetToArrayData + tableSize > insnCount) {
+ LOG_VFY_METH(meth,
+ "VFY: invalid array data end: at %d, data offset %d, end %d, "
+ "count %d\n",
+ curOffset, offsetToArrayData,
+ curOffset + offsetToArrayData + tableSize,
+ insnCount);
+ return false;
+ }
+
+ return true;
+}
+
+
+/*
+ * Decode the current instruction.
+ */
+static void decodeInstruction(const Method* meth, int insnIdx,
+ DecodedInstruction* pDecInsn)
+{
+ dexDecodeInstruction(gDvm.instrFormat, meth->insns + insnIdx, pDecInsn);
+}
+
+
+/*
+ * Perform static checks on a "new-instance" instruction. Specifically,
+ * make sure the class reference isn't for an array class.
+ *
+ * We don't need the actual class, just a pointer to the class name.
+ */
+static bool checkNewInstance(const Method* meth, int insnIdx)
+{
+ DvmDex* pDvmDex = meth->clazz->pDvmDex;
+ DecodedInstruction decInsn;
+ const char* classDescriptor;
+ u4 idx;
+
+ decodeInstruction(meth, insnIdx, &decInsn);
+ idx = decInsn.vB; // 2nd item
+ if (idx >= pDvmDex->pHeader->typeIdsSize) {
+ LOG_VFY_METH(meth, "VFY: bad type index %d (max %d)\n",
+ idx, pDvmDex->pHeader->typeIdsSize);
+ return false;
+ }
+
+ classDescriptor = dexStringByTypeIdx(pDvmDex->pDexFile, idx);
+ if (classDescriptor[0] != 'L') {
+ LOG_VFY_METH(meth, "VFY: can't call new-instance on type '%s'\n",
+ classDescriptor);
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Perform static checks on a "new-array" instruction. Specifically, make
+ * sure they aren't creating an array of arrays that causes the number of
+ * dimensions to exceed 255.
+ */
+static bool checkNewArray(const Method* meth, int insnIdx)
+{
+ DvmDex* pDvmDex = meth->clazz->pDvmDex;
+ DecodedInstruction decInsn;
+ const char* classDescriptor;
+ u4 idx;
+
+ decodeInstruction(meth, insnIdx, &decInsn);
+ idx = decInsn.vC; // 3rd item
+ if (idx >= pDvmDex->pHeader->typeIdsSize) {
+ LOG_VFY_METH(meth, "VFY: bad type index %d (max %d)\n",
+ idx, pDvmDex->pHeader->typeIdsSize);
+ return false;
+ }
+
+ classDescriptor = dexStringByTypeIdx(pDvmDex->pDexFile, idx);
+
+ int bracketCount = 0;
+ const char* cp = classDescriptor;
+ while (*cp++ == '[')
+ bracketCount++;
+
+ if (bracketCount == 0) {
+ /* The given class must be an array type. */
+ LOG_VFY_METH(meth, "VFY: can't new-array class '%s' (not an array)\n",
+ classDescriptor);
+ return false;
+ } else if (bracketCount > 255) {
+ /* It is illegal to create an array of more than 255 dimensions. */
+ LOG_VFY_METH(meth, "VFY: can't new-array class '%s' (exceeds limit)\n",
+ classDescriptor);
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Perform static checks on an instruction that takes a class constant.
+ * Ensure that the class index is in the valid range.
+ */
+static bool checkTypeIndex(const Method* meth, int insnIdx, bool useB)
+{
+ DvmDex* pDvmDex = meth->clazz->pDvmDex;
+ DecodedInstruction decInsn;
+ u4 idx;
+
+ decodeInstruction(meth, insnIdx, &decInsn);
+ if (useB)
+ idx = decInsn.vB;
+ else
+ idx = decInsn.vC;
+ if (idx >= pDvmDex->pHeader->typeIdsSize) {
+ LOG_VFY_METH(meth, "VFY: bad type index %d (max %d)\n",
+ idx, pDvmDex->pHeader->typeIdsSize);
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Perform static checks on a field get or set instruction. All we do
+ * here is ensure that the field index is in the valid range.
+ */
+static bool checkFieldIndex(const Method* meth, int insnIdx, bool useB)
+{
+ DvmDex* pDvmDex = meth->clazz->pDvmDex;
+ DecodedInstruction decInsn;
+ u4 idx;
+
+ decodeInstruction(meth, insnIdx, &decInsn);
+ if (useB)
+ idx = decInsn.vB;
+ else
+ idx = decInsn.vC;
+ if (idx >= pDvmDex->pHeader->fieldIdsSize) {
+ LOG_VFY_METH(meth,
+ "VFY: bad field index %d (max %d) at offset 0x%04x\n",
+ idx, pDvmDex->pHeader->fieldIdsSize, insnIdx);
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Perform static checks on a method invocation instruction. All we do
+ * here is ensure that the method index is in the valid range.
+ */
+static bool checkMethodIndex(const Method* meth, int insnIdx)
+{
+ DvmDex* pDvmDex = meth->clazz->pDvmDex;
+ DecodedInstruction decInsn;
+
+ decodeInstruction(meth, insnIdx, &decInsn);
+ if (decInsn.vB >= pDvmDex->pHeader->methodIdsSize) {
+ LOG_VFY_METH(meth, "VFY: bad method index %d (max %d)\n",
+ decInsn.vB, pDvmDex->pHeader->methodIdsSize);
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Perform static checks on a string constant instruction. All we do
+ * here is ensure that the string index is in the valid range.
+ */
+static bool checkStringIndex(const Method* meth, int insnIdx)
+{
+ DvmDex* pDvmDex = meth->clazz->pDvmDex;
+ DecodedInstruction decInsn;
+
+ decodeInstruction(meth, insnIdx, &decInsn);
+ if (decInsn.vB >= pDvmDex->pHeader->stringIdsSize) {
+ LOG_VFY_METH(meth, "VFY: bad string index %d (max %d)\n",
+ decInsn.vB, pDvmDex->pHeader->stringIdsSize);
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Perform static verification on instructions.
+ *
+ * As a side effect, this sets the "branch target" flags in InsnFlags.
+ *
+ * "(CF)" items are handled during code-flow analysis.
+ *
+ * v3 4.10.1
+ * - target of each jump and branch instruction must be valid
+ * - targets of switch statements must be valid
+ * - (CF) operands referencing constant pool entries must be valid
+ * - (CF) operands of getfield, putfield, getstatic, putstatic must be valid
+ * - (new) verify operands of "quick" field ops
+ * - (CF) operands of method invocation instructions must be valid
+ * - (new) verify operands of "quick" method invoke ops
+ * - (CF) only invoke-direct can call a method starting with '<'
+ * - (CF) <clinit> must never be called explicitly
+ * - (CF) operands of instanceof, checkcast, new (and variants) must be valid
+ * - new-array[-type] limited to 255 dimensions
+ * - can't use "new" on an array class
+ * - (?) limit dimensions in multi-array creation
+ * - (CF) local variable load/store register values must be in valid range
+ *
+ * v3 4.11.1.2
+ * - branches must be within the bounds of the code array
+ * - targets of all control-flow instructions are the start of an instruction
+ * - (CF) register accesses fall within range of allocated registers
+ * - (N/A) access to constant pool must be of appropriate type
+ * - (CF) code does not end in the middle of an instruction
+ * - (CF) execution cannot fall off the end of the code
+ * - (earlier) for each exception handler, the "try" area must begin and
+ * end at the start of an instruction (end can be at the end of the code)
+ * - (earlier) for each exception handler, the handler must start at a valid
+ * instruction
+ *
+ * TODO: move some of the "CF" items in here for better performance (the
+ * code-flow analysis sometimes has to process the same instruction several
+ * times).
+ */
+static bool verifyInstructions(VerifierData* vdata)
+{
+ const Method* meth = vdata->method;
+ InsnFlags* insnFlags = vdata->insnFlags;
+ const size_t insnCount = vdata->insnsSize;
+ const u2* insns = meth->insns;
+ int i;
+
+ /* the start of the method is a "branch target" */
+ dvmInsnSetBranchTarget(insnFlags, 0, true);
+
+ for (i = 0; i < (int) insnCount; /**/) {
+ /*
+ * These types of instructions can be GC points. To support precise
+ * GC, all such instructions must export the PC in the interpreter,
+ * or the GC won't be able to identify the current PC for the thread.
+ */
+ static const int gcMask = kInstrCanBranch | kInstrCanSwitch |
+ kInstrCanThrow | kInstrCanReturn;
+
+ int width = dvmInsnGetWidth(insnFlags, i);
+ OpCode opcode = *insns & 0xff;
+ InstructionFlags opFlags = dexGetInstrFlags(gDvm.instrFlags, opcode);
+
+ if ((opFlags & gcMask) != 0) {
+ /*
+ * This instruction is probably a GC point. Branch instructions
+ * only qualify if they go backward, so we need to check the
+ * offset.
+ */
+ int offset = -1;
+ bool unused;
+ if (dvmGetBranchTarget(meth, insnFlags, i, &offset, &unused)) {
+ if (offset <= 0) {
+ dvmInsnSetGcPoint(insnFlags, i, true);
+ }
+ } else {
+ /* not a branch target */
+ dvmInsnSetGcPoint(insnFlags, i, true);
+ }
+ }
+
+ switch (opcode) {
+ case OP_NOP:
+ /* plain no-op or switch table data; nothing to do here */
+ break;
+
+ case OP_CONST_STRING:
+ case OP_CONST_STRING_JUMBO:
+ if (!checkStringIndex(meth, i))
+ return false;
+ break;
+
+ case OP_CONST_CLASS:
+ case OP_CHECK_CAST:
+ if (!checkTypeIndex(meth, i, true))
+ return false;
+ break;
+ case OP_INSTANCE_OF:
+ if (!checkTypeIndex(meth, i, false))
+ return false;
+ break;
+
+ case OP_PACKED_SWITCH:
+ case OP_SPARSE_SWITCH:
+ /* verify the associated table */
+ if (!dvmCheckSwitchTargets(meth, insnFlags, i))
+ return false;
+ break;
+
+ case OP_FILL_ARRAY_DATA:
+ /* verify the associated table */
+ if (!checkArrayData(meth, i))
+ return false;
+ break;
+
+ case OP_GOTO:
+ case OP_GOTO_16:
+ case OP_IF_EQ:
+ case OP_IF_NE:
+ case OP_IF_LT:
+ case OP_IF_GE:
+ case OP_IF_GT:
+ case OP_IF_LE:
+ case OP_IF_EQZ:
+ case OP_IF_NEZ:
+ case OP_IF_LTZ:
+ case OP_IF_GEZ:
+ case OP_IF_GTZ:
+ case OP_IF_LEZ:
+ /* check the destination */
+ if (!dvmCheckBranchTarget(meth, insnFlags, i, false))
+ return false;
+ break;
+ case OP_GOTO_32:
+ /* check the destination; self-branch is okay */
+ if (!dvmCheckBranchTarget(meth, insnFlags, i, true))
+ return false;
+ break;
+
+ case OP_NEW_INSTANCE:
+ if (!checkNewInstance(meth, i))
+ return false;
+ break;
+
+ case OP_NEW_ARRAY:
+ if (!checkNewArray(meth, i))
+ return false;
+ break;
+
+ case OP_FILLED_NEW_ARRAY:
+ if (!checkTypeIndex(meth, i, true))
+ return false;
+ break;
+ case OP_FILLED_NEW_ARRAY_RANGE:
+ if (!checkTypeIndex(meth, i, true))
+ return false;
+ break;
+
+ case OP_IGET:
+ case OP_IGET_WIDE:
+ case OP_IGET_OBJECT:
+ case OP_IGET_BOOLEAN:
+ case OP_IGET_BYTE:
+ case OP_IGET_CHAR:
+ case OP_IGET_SHORT:
+ case OP_IPUT:
+ case OP_IPUT_WIDE:
+ case OP_IPUT_OBJECT:
+ case OP_IPUT_BOOLEAN:
+ case OP_IPUT_BYTE:
+ case OP_IPUT_CHAR:
+ case OP_IPUT_SHORT:
+ /* check the field index */
+ if (!checkFieldIndex(meth, i, false))
+ return false;
+ break;
+ case OP_SGET:
+ case OP_SGET_WIDE:
+ case OP_SGET_OBJECT:
+ case OP_SGET_BOOLEAN:
+ case OP_SGET_BYTE:
+ case OP_SGET_CHAR:
+ case OP_SGET_SHORT:
+ case OP_SPUT:
+ case OP_SPUT_WIDE:
+ case OP_SPUT_OBJECT:
+ case OP_SPUT_BOOLEAN:
+ case OP_SPUT_BYTE:
+ case OP_SPUT_CHAR:
+ case OP_SPUT_SHORT:
+ /* check the field index */
+ if (!checkFieldIndex(meth, i, true))
+ return false;
+ break;
+
+ case OP_INVOKE_VIRTUAL:
+ case OP_INVOKE_SUPER:
+ case OP_INVOKE_DIRECT:
+ case OP_INVOKE_STATIC:
+ case OP_INVOKE_INTERFACE:
+ case OP_INVOKE_VIRTUAL_RANGE:
+ case OP_INVOKE_SUPER_RANGE:
+ case OP_INVOKE_DIRECT_RANGE:
+ case OP_INVOKE_STATIC_RANGE:
+ case OP_INVOKE_INTERFACE_RANGE:
+ /* check the method index */
+ if (!checkMethodIndex(meth, i))
+ return false;
+ break;
+
+ case OP_EXECUTE_INLINE:
+ case OP_INVOKE_DIRECT_EMPTY:
+ case OP_IGET_QUICK:
+ case OP_IGET_WIDE_QUICK:
+ case OP_IGET_OBJECT_QUICK:
+ case OP_IPUT_QUICK:
+ case OP_IPUT_WIDE_QUICK:
+ case OP_IPUT_OBJECT_QUICK:
+ case OP_INVOKE_VIRTUAL_QUICK:
+ case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+ case OP_INVOKE_SUPER_QUICK:
+ case OP_INVOKE_SUPER_QUICK_RANGE:
+ LOG_VFY("VFY: not expecting optimized instructions\n");
+ return false;
+ break;
+
+ default:
+ /* nothing to do */
+ break;
+ }
+
+ assert(width > 0);
+ i += width;
+ insns += width;
+ }
+
+ /* make sure the last instruction ends at the end of the insn area */
+ if (i != (int) insnCount) {
+ LOG_VFY_METH(meth,
+ "VFY: code did not end when expected (end at %d, count %d)\n",
+ i, insnCount);
+ return false;
+ }
+
+ return true;
+}
diff --git a/vm/analysis/DexVerify.h b/vm/analysis/DexVerify.h
new file mode 100644
index 0000000..ab2af52
--- /dev/null
+++ b/vm/analysis/DexVerify.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik classfile verification.
+ */
+#ifndef _DALVIK_DEXVERIFY
+#define _DALVIK_DEXVERIFY
+
+/*
+ * Global verification mode. These must be in order from least verification
+ * to most. If we're using "exact GC", we may need to perform some of
+ * the verification steps anyway.
+ */
+typedef enum {
+ VERIFY_MODE_UNKNOWN = 0,
+ VERIFY_MODE_NONE,
+ VERIFY_MODE_REMOTE,
+ VERIFY_MODE_ALL
+} DexClassVerifyMode;
+
+bool dvmVerificationStartup(void);
+void dvmVerificationShutdown(void);
+
+/*
+ * Perform verification on all classes loaded from this DEX file. If
+ * enabled, it must happen before optimization.
+ */
+bool dvmVerifyAllClasses(DexFile* pDexFile);
+
+/*
+ * Verify a single class.
+ */
+bool dvmVerifyClass(ClassObject* clazz);
+
+/*
+ * Release the storage associated with a RegisterMap.
+ */
+void dvmFreeRegisterMap(RegisterMap* pMap);
+
+#endif /*_DALVIK_DEXVERIFY*/
diff --git a/vm/analysis/Optimize.c b/vm/analysis/Optimize.c
new file mode 100644
index 0000000..7ad4e45
--- /dev/null
+++ b/vm/analysis/Optimize.c
@@ -0,0 +1,1108 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Perform some simple bytecode optimizations, chiefly "quickening" of
+ * opcodes.
+ */
+#include "Dalvik.h"
+#include "libdex/InstrUtils.h"
+
+#include <zlib.h>
+
+#include <stdlib.h>
+
+/*
+ * Virtual/direct calls to "method" are replaced with an execute-inline
+ * instruction with index "idx".
+ */
+struct InlineSub {
+ Method* method;
+ int inlineIdx;
+};
+
+
+/* fwd */
+static void optimizeMethod(Method* method, bool essentialOnly);
+static bool rewriteInstField(Method* method, u2* insns, OpCode quickOpc,
+ OpCode volatileOpc);
+static bool rewriteStaticField(Method* method, u2* insns, OpCode volatileOpc);
+static bool rewriteVirtualInvoke(Method* method, u2* insns, OpCode newOpc);
+static bool rewriteEmptyDirectInvoke(Method* method, u2* insns);
+static bool rewriteExecuteInline(Method* method, u2* insns,
+ MethodType methodType);
+static bool rewriteExecuteInlineRange(Method* method, u2* insns,
+ MethodType methodType);
+
+
+/*
+ * Create a table of inline substitutions.
+ *
+ * TODO: this is currently just a linear array. We will want to put this
+ * into a hash table as the list size increases.
+ */
+InlineSub* dvmCreateInlineSubsTable(void)
+{
+ const InlineOperation* ops = dvmGetInlineOpsTable();
+ const int count = dvmGetInlineOpsTableLength();
+ InlineSub* table;
+ Method* method;
+ ClassObject* clazz;
+ int i, tableIndex;
+
+ /*
+ * Allocate for optimism: one slot per entry, plus an end-of-list marker.
+ */
+ table = malloc(sizeof(InlineSub) * (count+1));
+
+ tableIndex = 0;
+ for (i = 0; i < count; i++) {
+ clazz = dvmFindClassNoInit(ops[i].classDescriptor, NULL);
+ if (clazz == NULL) {
+ LOGV("DexOpt: can't inline for class '%s': not found\n",
+ ops[i].classDescriptor);
+ dvmClearOptException(dvmThreadSelf());
+ } else {
+ /*
+ * Method could be virtual or direct. Try both. Don't use
+ * the "hier" versions.
+ */
+ method = dvmFindDirectMethodByDescriptor(clazz, ops[i].methodName,
+ ops[i].methodSignature);
+ if (method == NULL)
+ method = dvmFindVirtualMethodByDescriptor(clazz, ops[i].methodName,
+ ops[i].methodSignature);
+ if (method == NULL) {
+ LOGW("DexOpt: can't inline %s.%s %s: method not found\n",
+ ops[i].classDescriptor, ops[i].methodName,
+ ops[i].methodSignature);
+ } else {
+ if (!dvmIsFinalClass(clazz) && !dvmIsFinalMethod(method)) {
+ LOGW("DexOpt: WARNING: inline op on non-final class/method "
+ "%s.%s\n",
+ clazz->descriptor, method->name);
+ /* fail? */
+ }
+ if (dvmIsSynchronizedMethod(method) ||
+ dvmIsDeclaredSynchronizedMethod(method))
+ {
+ LOGW("DexOpt: WARNING: inline op on synchronized method "
+ "%s.%s\n",
+ clazz->descriptor, method->name);
+ /* fail? */
+ }
+
+ table[tableIndex].method = method;
+ table[tableIndex].inlineIdx = i;
+ tableIndex++;
+
+ LOGV("DexOpt: will inline %d: %s.%s %s\n", i,
+ ops[i].classDescriptor, ops[i].methodName,
+ ops[i].methodSignature);
+ }
+ }
+ }
+
+ /* mark end of table */
+ table[tableIndex].method = NULL;
+ LOGV("DexOpt: inline table has %d entries\n", tableIndex);
+
+ return table;
+}
+
+/*
+ * Release inline sub data structure.
+ */
+void dvmFreeInlineSubsTable(InlineSub* inlineSubs)
+{
+ free(inlineSubs);
+}
+
+
+/*
+ * Optimize the specified class.
+ *
+ * If "essentialOnly" is true, we only do essential optimizations. For
+ * example, accesses to volatile 64-bit fields must be replaced with
+ * "-wide-volatile" instructions or the program could behave incorrectly.
+ * (Skipping non-essential optimizations makes us a little bit faster, and
+ * more importantly avoids dirtying DEX pages.)
+ */
+void dvmOptimizeClass(ClassObject* clazz, bool essentialOnly)
+{
+ int i;
+
+ for (i = 0; i < clazz->directMethodCount; i++) {
+ optimizeMethod(&clazz->directMethods[i], essentialOnly);
+ }
+ for (i = 0; i < clazz->virtualMethodCount; i++) {
+ optimizeMethod(&clazz->virtualMethods[i], essentialOnly);
+ }
+}
+
+/*
+ * Optimize instructions in a method.
+ *
+ * This does a single pass through the code, examining each instruction.
+ *
+ * This is not expected to fail if the class was successfully verified.
+ * The only significant failure modes occur when an "essential" update fails,
+ * but we can't generally identify those: if we can't look up a field,
+ * we can't know if the field access was supposed to be handled as volatile.
+ *
+ * Instead, we give it our best effort, and hope for the best. For 100%
+ * reliability, only optimize a class after verification succeeds.
+ */
+static void optimizeMethod(Method* method, bool essentialOnly)
+{
+ u4 insnsSize;
+ u2* insns;
+ u2 inst;
+
+ if (!gDvm.optimizing && !essentialOnly) {
+ /* unexpected; will force copy-on-write of a lot of pages */
+ LOGD("NOTE: doing full bytecode optimization outside dexopt\n");
+ }
+
+ if (dvmIsNativeMethod(method) || dvmIsAbstractMethod(method))
+ return;
+
+ insns = (u2*) method->insns;
+ assert(insns != NULL);
+ insnsSize = dvmGetMethodInsnsSize(method);
+
+ while (insnsSize > 0) {
+ OpCode quickOpc, volatileOpc = OP_NOP;
+ int width;
+ bool notMatched = false;
+
+ inst = *insns & 0xff;
+
+ /* "essential" substitutions, always checked */
+ switch (inst) {
+ case OP_IGET:
+ case OP_IGET_BOOLEAN:
+ case OP_IGET_BYTE:
+ case OP_IGET_CHAR:
+ case OP_IGET_SHORT:
+ quickOpc = OP_IGET_QUICK;
+ if (gDvm.dexOptForSmp)
+ volatileOpc = OP_IGET_VOLATILE;
+ goto rewrite_inst_field;
+ case OP_IGET_WIDE:
+ quickOpc = OP_IGET_WIDE_QUICK;
+ volatileOpc = OP_IGET_WIDE_VOLATILE;
+ goto rewrite_inst_field;
+ case OP_IGET_OBJECT:
+ quickOpc = OP_IGET_OBJECT_QUICK;
+ if (gDvm.dexOptForSmp)
+ volatileOpc = OP_IGET_OBJECT_VOLATILE;
+ goto rewrite_inst_field;
+ case OP_IPUT:
+ case OP_IPUT_BOOLEAN:
+ case OP_IPUT_BYTE:
+ case OP_IPUT_CHAR:
+ case OP_IPUT_SHORT:
+ quickOpc = OP_IPUT_QUICK;
+ if (gDvm.dexOptForSmp)
+ volatileOpc = OP_IPUT_VOLATILE;
+ goto rewrite_inst_field;
+ case OP_IPUT_WIDE:
+ quickOpc = OP_IPUT_WIDE_QUICK;
+ volatileOpc = OP_IPUT_WIDE_VOLATILE;
+ goto rewrite_inst_field;
+ case OP_IPUT_OBJECT:
+ quickOpc = OP_IPUT_OBJECT_QUICK;
+ if (gDvm.dexOptForSmp)
+ volatileOpc = OP_IPUT_OBJECT_VOLATILE;
+rewrite_inst_field:
+ if (essentialOnly)
+ quickOpc = OP_NOP;
+ if (quickOpc != OP_NOP || volatileOpc != OP_NOP)
+ rewriteInstField(method, insns, quickOpc, volatileOpc);
+ break;
+
+ case OP_SGET_WIDE:
+ volatileOpc = OP_SGET_WIDE_VOLATILE;
+ goto rewrite_static_field;
+ case OP_SPUT_WIDE:
+ volatileOpc = OP_SPUT_WIDE_VOLATILE;
+rewrite_static_field:
+ rewriteStaticField(method, insns, volatileOpc);
+ break;
+ default:
+ notMatched = true;
+ break;
+ }
+
+ if (notMatched && gDvm.dexOptForSmp) {
+ /* additional "essential" substitutions for an SMP device */
+ switch (inst) {
+ case OP_SGET:
+ case OP_SGET_BOOLEAN:
+ case OP_SGET_BYTE:
+ case OP_SGET_CHAR:
+ case OP_SGET_SHORT:
+ volatileOpc = OP_SGET_VOLATILE;
+ goto rewrite_static_field2;
+ case OP_SGET_OBJECT:
+ volatileOpc = OP_SGET_OBJECT_VOLATILE;
+ goto rewrite_static_field2;
+ case OP_SPUT:
+ case OP_SPUT_BOOLEAN:
+ case OP_SPUT_BYTE:
+ case OP_SPUT_CHAR:
+ case OP_SPUT_SHORT:
+ volatileOpc = OP_SPUT_VOLATILE;
+ goto rewrite_static_field2;
+ case OP_SPUT_OBJECT:
+ volatileOpc = OP_SPUT_OBJECT_VOLATILE;
+rewrite_static_field2:
+ rewriteStaticField(method, insns, volatileOpc);
+ notMatched = false;
+ break;
+ default:
+ assert(notMatched);
+ break;
+ }
+ }
+
+ /* non-essential substitutions */
+ if (notMatched && !essentialOnly) {
+ switch (inst) {
+ case OP_INVOKE_VIRTUAL:
+ if (!rewriteExecuteInline(method, insns, METHOD_VIRTUAL)) {
+ rewriteVirtualInvoke(method, insns,
+ OP_INVOKE_VIRTUAL_QUICK);
+ }
+ break;
+ case OP_INVOKE_VIRTUAL_RANGE:
+ if (!rewriteExecuteInlineRange(method, insns, METHOD_VIRTUAL)) {
+ rewriteVirtualInvoke(method, insns,
+ OP_INVOKE_VIRTUAL_QUICK_RANGE);
+ }
+ break;
+ case OP_INVOKE_SUPER:
+ rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK);
+ break;
+ case OP_INVOKE_SUPER_RANGE:
+ rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK_RANGE);
+ break;
+
+ case OP_INVOKE_DIRECT:
+ if (!rewriteExecuteInline(method, insns, METHOD_DIRECT)) {
+ rewriteEmptyDirectInvoke(method, insns);
+ }
+ break;
+ case OP_INVOKE_DIRECT_RANGE:
+ rewriteExecuteInlineRange(method, insns, METHOD_DIRECT);
+ break;
+
+ case OP_INVOKE_STATIC:
+ rewriteExecuteInline(method, insns, METHOD_STATIC);
+ break;
+ case OP_INVOKE_STATIC_RANGE:
+ rewriteExecuteInlineRange(method, insns, METHOD_STATIC);
+ break;
+
+ default:
+ /* nothing to do for this instruction */
+ ;
+ }
+ }
+
+ width = dexGetInstrOrTableWidthAbs(gDvm.instrWidth, insns);
+ assert(width > 0);
+
+ insns += width;
+ insnsSize -= width;
+ }
+
+ assert(insnsSize == 0);
+}
+
+/*
+ * Update a 16-bit code unit in "meth".
+ */
+static inline void updateCode(const Method* meth, u2* ptr, u2 newVal)
+{
+ if (gDvm.optimizing) {
+ /* dexopt time, alter the output directly */
+ *ptr = newVal;
+ } else {
+ /* runtime, toggle the page read/write status */
+ dvmDexChangeDex2(meth->clazz->pDvmDex, ptr, newVal);
+ }
+}
+
+/*
+ * If "referrer" and "resClass" don't come from the same DEX file, and
+ * the DEX we're working on is not destined for the bootstrap class path,
+ * tweak the class loader so package-access checks work correctly.
+ *
+ * Only do this if we're doing pre-verification or optimization.
+ */
+static void tweakLoader(ClassObject* referrer, ClassObject* resClass)
+{
+ if (!gDvm.optimizing)
+ return;
+ assert(referrer->classLoader == NULL);
+ assert(resClass->classLoader == NULL);
+
+ if (!gDvm.optimizingBootstrapClass) {
+ /* class loader for an array class comes from element type */
+ if (dvmIsArrayClass(resClass))
+ resClass = resClass->elementClass;
+ if (referrer->pDvmDex != resClass->pDvmDex)
+ resClass->classLoader = (Object*) 0xdead3333;
+ }
+}
+
+/*
+ * Undo the effects of tweakLoader.
+ */
+static void untweakLoader(ClassObject* referrer, ClassObject* resClass)
+{
+ if (!gDvm.optimizing || gDvm.optimizingBootstrapClass)
+ return;
+
+ if (dvmIsArrayClass(resClass))
+ resClass = resClass->elementClass;
+ resClass->classLoader = NULL;
+}
+
+
+/*
+ * Alternate version of dvmResolveClass for use with verification and
+ * optimization. Performs access checks on every resolve, and refuses
+ * to acknowledge the existence of classes defined in more than one DEX
+ * file.
+ *
+ * Exceptions caused by failures are cleared before returning.
+ *
+ * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
+ */
+ClassObject* dvmOptResolveClass(ClassObject* referrer, u4 classIdx,
+ VerifyError* pFailure)
+{
+ DvmDex* pDvmDex = referrer->pDvmDex;
+ ClassObject* resClass;
+
+ /*
+ * Check the table first. If not there, do the lookup by name.
+ */
+ resClass = dvmDexGetResolvedClass(pDvmDex, classIdx);
+ if (resClass == NULL) {
+ const char* className = dexStringByTypeIdx(pDvmDex->pDexFile, classIdx);
+ if (className[0] != '\0' && className[1] == '\0') {
+ /* primitive type */
+ resClass = dvmFindPrimitiveClass(className[0]);
+ } else {
+ resClass = dvmFindClassNoInit(className, referrer->classLoader);
+ }
+ if (resClass == NULL) {
+ /* not found, exception should be raised */
+ LOGV("DexOpt: class %d (%s) not found\n",
+ classIdx,
+ dexStringByTypeIdx(pDvmDex->pDexFile, classIdx));
+ if (pFailure != NULL) {
+ /* dig through the wrappers to find the original failure */
+ Object* excep = dvmGetException(dvmThreadSelf());
+ while (true) {
+ Object* cause = dvmGetExceptionCause(excep);
+ if (cause == NULL)
+ break;
+ excep = cause;
+ }
+ if (strcmp(excep->clazz->descriptor,
+ "Ljava/lang/IncompatibleClassChangeError;") == 0)
+ {
+ *pFailure = VERIFY_ERROR_CLASS_CHANGE;
+ } else {
+ *pFailure = VERIFY_ERROR_NO_CLASS;
+ }
+ }
+ dvmClearOptException(dvmThreadSelf());
+ return NULL;
+ }
+
+ /*
+ * Add it to the resolved table so we're faster on the next lookup.
+ */
+ dvmDexSetResolvedClass(pDvmDex, classIdx, resClass);
+ }
+
+ /* multiple definitions? */
+ if (IS_CLASS_FLAG_SET(resClass, CLASS_MULTIPLE_DEFS)) {
+ LOGI("DexOpt: not resolving ambiguous class '%s'\n",
+ resClass->descriptor);
+ if (pFailure != NULL)
+ *pFailure = VERIFY_ERROR_NO_CLASS;
+ return NULL;
+ }
+
+ /* access allowed? */
+ tweakLoader(referrer, resClass);
+ bool allowed = dvmCheckClassAccess(referrer, resClass);
+ untweakLoader(referrer, resClass);
+ if (!allowed) {
+ LOGW("DexOpt: resolve class illegal access: %s -> %s\n",
+ referrer->descriptor, resClass->descriptor);
+ if (pFailure != NULL)
+ *pFailure = VERIFY_ERROR_ACCESS_CLASS;
+ return NULL;
+ }
+
+ return resClass;
+}
+
+/*
+ * Alternate version of dvmResolveInstField().
+ *
+ * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
+ */
+InstField* dvmOptResolveInstField(ClassObject* referrer, u4 ifieldIdx,
+ VerifyError* pFailure)
+{
+ DvmDex* pDvmDex = referrer->pDvmDex;
+ InstField* resField;
+
+ resField = (InstField*) dvmDexGetResolvedField(pDvmDex, ifieldIdx);
+ if (resField == NULL) {
+ const DexFieldId* pFieldId;
+ ClassObject* resClass;
+
+ pFieldId = dexGetFieldId(pDvmDex->pDexFile, ifieldIdx);
+
+ /*
+ * Find the field's class.
+ */
+ resClass = dvmOptResolveClass(referrer, pFieldId->classIdx, pFailure);
+ if (resClass == NULL) {
+ //dvmClearOptException(dvmThreadSelf());
+ assert(!dvmCheckException(dvmThreadSelf()));
+ if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
+ return NULL;
+ }
+
+ resField = (InstField*)dvmFindFieldHier(resClass,
+ dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx),
+ dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx));
+ if (resField == NULL) {
+ LOGD("DexOpt: couldn't find field %s.%s\n",
+ resClass->descriptor,
+ dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
+ if (pFailure != NULL)
+ *pFailure = VERIFY_ERROR_NO_FIELD;
+ return NULL;
+ }
+ if (dvmIsStaticField(&resField->field)) {
+ LOGD("DexOpt: wanted instance, got static for field %s.%s\n",
+ resClass->descriptor,
+ dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
+ if (pFailure != NULL)
+ *pFailure = VERIFY_ERROR_CLASS_CHANGE;
+ return NULL;
+ }
+
+ /*
+ * Add it to the resolved table so we're faster on the next lookup.
+ */
+ dvmDexSetResolvedField(pDvmDex, ifieldIdx, (Field*) resField);
+ }
+
+ /* access allowed? */
+ tweakLoader(referrer, resField->field.clazz);
+ bool allowed = dvmCheckFieldAccess(referrer, (Field*)resField);
+ untweakLoader(referrer, resField->field.clazz);
+ if (!allowed) {
+ LOGI("DexOpt: access denied from %s to field %s.%s\n",
+ referrer->descriptor, resField->field.clazz->descriptor,
+ resField->field.name);
+ if (pFailure != NULL)
+ *pFailure = VERIFY_ERROR_ACCESS_FIELD;
+ return NULL;
+ }
+
+ return resField;
+}
+
+/*
+ * Alternate version of dvmResolveStaticField().
+ *
+ * Does not force initialization of the resolved field's class.
+ *
+ * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
+ */
+StaticField* dvmOptResolveStaticField(ClassObject* referrer, u4 sfieldIdx,
+ VerifyError* pFailure)
+{
+ DvmDex* pDvmDex = referrer->pDvmDex;
+ StaticField* resField;
+
+ resField = (StaticField*)dvmDexGetResolvedField(pDvmDex, sfieldIdx);
+ if (resField == NULL) {
+ const DexFieldId* pFieldId;
+ ClassObject* resClass;
+
+ pFieldId = dexGetFieldId(pDvmDex->pDexFile, sfieldIdx);
+
+ /*
+ * Find the field's class.
+ */
+ resClass = dvmOptResolveClass(referrer, pFieldId->classIdx, pFailure);
+ if (resClass == NULL) {
+ //dvmClearOptException(dvmThreadSelf());
+ assert(!dvmCheckException(dvmThreadSelf()));
+ if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
+ return NULL;
+ }
+
+ resField = (StaticField*)dvmFindFieldHier(resClass,
+ dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx),
+ dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx));
+ if (resField == NULL) {
+ LOGD("DexOpt: couldn't find static field\n");
+ if (pFailure != NULL)
+ *pFailure = VERIFY_ERROR_NO_FIELD;
+ return NULL;
+ }
+ if (!dvmIsStaticField(&resField->field)) {
+ LOGD("DexOpt: wanted static, got instance for field %s.%s\n",
+ resClass->descriptor,
+ dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
+ if (pFailure != NULL)
+ *pFailure = VERIFY_ERROR_CLASS_CHANGE;
+ return NULL;
+ }
+
+ /*
+ * Add it to the resolved table so we're faster on the next lookup.
+ *
+ * We can only do this if we're in "dexopt", because the presence
+ * of a valid value in the resolution table implies that the class
+ * containing the static field has been initialized.
+ */
+ if (gDvm.optimizing)
+ dvmDexSetResolvedField(pDvmDex, sfieldIdx, (Field*) resField);
+ }
+
+ /* access allowed? */
+ tweakLoader(referrer, resField->field.clazz);
+ bool allowed = dvmCheckFieldAccess(referrer, (Field*)resField);
+ untweakLoader(referrer, resField->field.clazz);
+ if (!allowed) {
+ LOGI("DexOpt: access denied from %s to field %s.%s\n",
+ referrer->descriptor, resField->field.clazz->descriptor,
+ resField->field.name);
+ if (pFailure != NULL)
+ *pFailure = VERIFY_ERROR_ACCESS_FIELD;
+ return NULL;
+ }
+
+ return resField;
+}
+
+
+/*
+ * Rewrite an iget/iput instruction. These all have the form:
+ * op vA, vB, field@CCCC
+ *
+ * Where vA holds the value, vB holds the object reference, and CCCC is
+ * the field reference constant pool offset. For a non-volatile field,
+ * we want to replace the opcode with "quickOpc" and replace CCCC with
+ * the byte offset from the start of the object. For a volatile field,
+ * we just want to replace the opcode with "volatileOpc".
+ *
+ * If "volatileOpc" is OP_NOP we don't check to see if it's a volatile
+ * field. If "quickOpc" is OP_NOP, and this is a non-volatile field,
+ * we don't do anything.
+ *
+ * "method" is the referring method.
+ */
+static bool rewriteInstField(Method* method, u2* insns, OpCode quickOpc,
+ OpCode volatileOpc)
+{
+ ClassObject* clazz = method->clazz;
+ u2 fieldIdx = insns[1];
+ InstField* instField;
+
+ instField = dvmOptResolveInstField(clazz, fieldIdx, NULL);
+ if (instField == NULL) {
+ LOGI("DexOpt: unable to optimize instance field ref "
+ "0x%04x at 0x%02x in %s.%s\n",
+ fieldIdx, (int) (insns - method->insns), clazz->descriptor,
+ method->name);
+ return false;
+ }
+
+ if (instField->byteOffset >= 65536) {
+ LOGI("DexOpt: field offset exceeds 64K (%d)\n", instField->byteOffset);
+ return false;
+ }
+
+ if (volatileOpc != OP_NOP && dvmIsVolatileField(&instField->field)) {
+ updateCode(method, insns, (insns[0] & 0xff00) | (u2) volatileOpc);
+ LOGV("DexOpt: rewrote ifield access %s.%s --> volatile\n",
+ instField->field.clazz->descriptor, instField->field.name);
+ } else if (quickOpc != OP_NOP) {
+ updateCode(method, insns, (insns[0] & 0xff00) | (u2) quickOpc);
+ updateCode(method, insns+1, (u2) instField->byteOffset);
+ LOGV("DexOpt: rewrote ifield access %s.%s --> %d\n",
+ instField->field.clazz->descriptor, instField->field.name,
+ instField->byteOffset);
+ } else {
+ LOGV("DexOpt: no rewrite of ifield access %s.%s\n",
+ instField->field.clazz->descriptor, instField->field.name);
+ }
+
+ return true;
+}
+
+/*
+ * Rewrite an sget/sput instruction. These all have the form:
+ * op vAA, field@BBBB
+ *
+ * Where vAA holds the value, and BBBB is the field reference constant
+ * pool offset. There is no "quick" form of static field accesses, so
+ * this is only useful for volatile fields.
+ *
+ * "method" is the referring method.
+ */
+static bool rewriteStaticField(Method* method, u2* insns, OpCode volatileOpc)
+{
+ ClassObject* clazz = method->clazz;
+ u2 fieldIdx = insns[1];
+ StaticField* staticField;
+
+ assert(volatileOpc != OP_NOP);
+
+ staticField = dvmOptResolveStaticField(clazz, fieldIdx, NULL);
+ if (staticField == NULL) {
+ LOGI("DexOpt: unable to optimize static field ref "
+ "0x%04x at 0x%02x in %s.%s\n",
+ fieldIdx, (int) (insns - method->insns), clazz->descriptor,
+ method->name);
+ return false;
+ }
+
+ if (dvmIsVolatileField(&staticField->field)) {
+ updateCode(method, insns, (insns[0] & 0xff00) | (u2) volatileOpc);
+ LOGV("DexOpt: rewrote sfield access %s.%s --> volatile\n",
+ staticField->field.clazz->descriptor, staticField->field.name);
+ }
+
+ return true;
+}
+
+/*
+ * Alternate version of dvmResolveMethod().
+ *
+ * Doesn't throw exceptions, and checks access on every lookup.
+ *
+ * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
+ */
+Method* dvmOptResolveMethod(ClassObject* referrer, u4 methodIdx,
+ MethodType methodType, VerifyError* pFailure)
+{
+ DvmDex* pDvmDex = referrer->pDvmDex;
+ Method* resMethod;
+
+ assert(methodType == METHOD_DIRECT ||
+ methodType == METHOD_VIRTUAL ||
+ methodType == METHOD_STATIC);
+
+ LOGVV("--- resolving method %u (referrer=%s)\n", methodIdx,
+ referrer->descriptor);
+
+ resMethod = dvmDexGetResolvedMethod(pDvmDex, methodIdx);
+ if (resMethod == NULL) {
+ const DexMethodId* pMethodId;
+ ClassObject* resClass;
+
+ pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx);
+
+ resClass = dvmOptResolveClass(referrer, pMethodId->classIdx, pFailure);
+ if (resClass == NULL) {
+ /*
+ * Can't find the class that the method is a part of, or don't
+ * have permission to access the class.
+ */
+ LOGV("DexOpt: can't find called method's class (?.%s)\n",
+ dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx));
+ if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
+ return NULL;
+ }
+ if (dvmIsInterfaceClass(resClass)) {
+ /* method is part of an interface; this is wrong method for that */
+ LOGW("DexOpt: method is in an interface\n");
+ if (pFailure != NULL)
+ *pFailure = VERIFY_ERROR_GENERIC;
+ return NULL;
+ }
+
+ /*
+ * We need to chase up the class hierarchy to find methods defined
+ * in super-classes. (We only want to check the current class
+ * if we're looking for a constructor.)
+ */
+ DexProto proto;
+ dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId);
+
+ if (methodType == METHOD_DIRECT) {
+ resMethod = dvmFindDirectMethod(resClass,
+ dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), &proto);
+ } else {
+ /* METHOD_STATIC or METHOD_VIRTUAL */
+ resMethod = dvmFindMethodHier(resClass,
+ dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), &proto);
+ }
+
+ if (resMethod == NULL) {
+ LOGV("DexOpt: couldn't find method '%s'\n",
+ dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx));
+ if (pFailure != NULL)
+ *pFailure = VERIFY_ERROR_NO_METHOD;
+ return NULL;
+ }
+ if (methodType == METHOD_STATIC) {
+ if (!dvmIsStaticMethod(resMethod)) {
+ LOGD("DexOpt: wanted static, got instance for method %s.%s\n",
+ resClass->descriptor, resMethod->name);
+ if (pFailure != NULL)
+ *pFailure = VERIFY_ERROR_CLASS_CHANGE;
+ return NULL;
+ }
+ } else if (methodType == METHOD_VIRTUAL) {
+ if (dvmIsStaticMethod(resMethod)) {
+ LOGD("DexOpt: wanted instance, got static for method %s.%s\n",
+ resClass->descriptor, resMethod->name);
+ if (pFailure != NULL)
+ *pFailure = VERIFY_ERROR_CLASS_CHANGE;
+ return NULL;
+ }
+ }
+
+ /* see if this is a pure-abstract method */
+ if (dvmIsAbstractMethod(resMethod) && !dvmIsAbstractClass(resClass)) {
+ LOGW("DexOpt: pure-abstract method '%s' in %s\n",
+ dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx),
+ resClass->descriptor);
+ if (pFailure != NULL)
+ *pFailure = VERIFY_ERROR_GENERIC;
+ return NULL;
+ }
+
+ /*
+ * Add it to the resolved table so we're faster on the next lookup.
+ *
+ * We can only do this for static methods if we're not in "dexopt",
+ * because the presence of a valid value in the resolution table
+ * implies that the class containing the static field has been
+ * initialized.
+ */
+ if (methodType != METHOD_STATIC || gDvm.optimizing)
+ dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod);
+ }
+
+ LOGVV("--- found method %d (%s.%s)\n",
+ methodIdx, resMethod->clazz->descriptor, resMethod->name);
+
+ /* access allowed? */
+ tweakLoader(referrer, resMethod->clazz);
+ bool allowed = dvmCheckMethodAccess(referrer, resMethod);
+ untweakLoader(referrer, resMethod->clazz);
+ if (!allowed) {
+ IF_LOGI() {
+ char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
+ LOGI("DexOpt: illegal method access (call %s.%s %s from %s)\n",
+ resMethod->clazz->descriptor, resMethod->name, desc,
+ referrer->descriptor);
+ free(desc);
+ }
+ if (pFailure != NULL)
+ *pFailure = VERIFY_ERROR_ACCESS_METHOD;
+ return NULL;
+ }
+
+ return resMethod;
+}
+
+/*
+ * Rewrite invoke-virtual, invoke-virtual/range, invoke-super, and
+ * invoke-super/range. These all have the form:
+ * op vAA, meth@BBBB, reg stuff @CCCC
+ *
+ * We want to replace the method constant pool index BBBB with the
+ * vtable index.
+ */
+static bool rewriteVirtualInvoke(Method* method, u2* insns, OpCode newOpc)
+{
+ ClassObject* clazz = method->clazz;
+ Method* baseMethod;
+ u2 methodIdx = insns[1];
+
+ baseMethod = dvmOptResolveMethod(clazz, methodIdx, METHOD_VIRTUAL, NULL);
+ if (baseMethod == NULL) {
+ LOGD("DexOpt: unable to optimize virt call 0x%04x at 0x%02x in %s.%s\n",
+ methodIdx,
+ (int) (insns - method->insns), clazz->descriptor,
+ method->name);
+ return false;
+ }
+
+ assert((insns[0] & 0xff) == OP_INVOKE_VIRTUAL ||
+ (insns[0] & 0xff) == OP_INVOKE_VIRTUAL_RANGE ||
+ (insns[0] & 0xff) == OP_INVOKE_SUPER ||
+ (insns[0] & 0xff) == OP_INVOKE_SUPER_RANGE);
+
+ /*
+ * Note: Method->methodIndex is a u2 and is range checked during the
+ * initial load.
+ */
+ updateCode(method, insns, (insns[0] & 0xff00) | (u2) newOpc);
+ updateCode(method, insns+1, baseMethod->methodIndex);
+
+ //LOGI("DexOpt: rewrote call to %s.%s --> %s.%s\n",
+ // method->clazz->descriptor, method->name,
+ // baseMethod->clazz->descriptor, baseMethod->name);
+
+ return true;
+}
+
+/*
+ * Rewrite invoke-direct, which has the form:
+ * op vAA, meth@BBBB, reg stuff @CCCC
+ *
+ * There isn't a lot we can do to make this faster, but in some situations
+ * we can make it go away entirely.
+ *
+ * This must only be used when the invoked method does nothing and has
+ * no return value (the latter being very important for verification).
+ */
+static bool rewriteEmptyDirectInvoke(Method* method, u2* insns)
+{
+ ClassObject* clazz = method->clazz;
+ Method* calledMethod;
+ u2 methodIdx = insns[1];
+
+ calledMethod = dvmOptResolveMethod(clazz, methodIdx, METHOD_DIRECT, NULL);
+ if (calledMethod == NULL) {
+ LOGD("DexOpt: unable to opt direct call 0x%04x at 0x%02x in %s.%s\n",
+ methodIdx,
+ (int) (insns - method->insns), clazz->descriptor,
+ method->name);
+ return false;
+ }
+
+ /* TODO: verify that java.lang.Object() is actually empty! */
+ if (calledMethod->clazz == gDvm.classJavaLangObject &&
+ dvmCompareNameDescriptorAndMethod("<init>", "()V", calledMethod) == 0)
+ {
+ /*
+ * Replace with "empty" instruction. DO NOT disturb anything
+ * else about it, as we want it to function the same as
+ * OP_INVOKE_DIRECT when debugging is enabled.
+ */
+ assert((insns[0] & 0xff) == OP_INVOKE_DIRECT);
+ updateCode(method, insns,
+ (insns[0] & 0xff00) | (u2) OP_INVOKE_DIRECT_EMPTY);
+
+ //LOGI("DexOpt: marked-empty call to %s.%s --> %s.%s\n",
+ // method->clazz->descriptor, method->name,
+ // calledMethod->clazz->descriptor, calledMethod->name);
+ }
+
+ return true;
+}
+
+/*
+ * Resolve an interface method reference.
+ *
+ * No method access check here -- interface methods are always public.
+ *
+ * Returns NULL if the method was not found. Does not throw an exception.
+ */
+Method* dvmOptResolveInterfaceMethod(ClassObject* referrer, u4 methodIdx)
+{
+ DvmDex* pDvmDex = referrer->pDvmDex;
+ Method* resMethod;
+ int i;
+
+ LOGVV("--- resolving interface method %d (referrer=%s)\n",
+ methodIdx, referrer->descriptor);
+
+ resMethod = dvmDexGetResolvedMethod(pDvmDex, methodIdx);
+ if (resMethod == NULL) {
+ const DexMethodId* pMethodId;
+ ClassObject* resClass;
+
+ pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx);
+
+ resClass = dvmOptResolveClass(referrer, pMethodId->classIdx, NULL);
+ if (resClass == NULL) {
+ /* can't find the class that the method is a part of */
+ dvmClearOptException(dvmThreadSelf());
+ return NULL;
+ }
+ if (!dvmIsInterfaceClass(resClass)) {
+ /* whoops */
+ LOGI("Interface method not part of interface class\n");
+ return NULL;
+ }
+
+ const char* methodName =
+ dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx);
+ DexProto proto;
+ dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId);
+
+ LOGVV("+++ looking for '%s' '%s' in resClass='%s'\n",
+ methodName, methodSig, resClass->descriptor);
+ resMethod = dvmFindVirtualMethod(resClass, methodName, &proto);
+ if (resMethod == NULL) {
+ /* scan superinterfaces and superclass interfaces */
+ LOGVV("+++ did not resolve immediately\n");
+ for (i = 0; i < resClass->iftableCount; i++) {
+ resMethod = dvmFindVirtualMethod(resClass->iftable[i].clazz,
+ methodName, &proto);
+ if (resMethod != NULL)
+ break;
+ }
+
+ if (resMethod == NULL) {
+ LOGVV("+++ unable to resolve method %s\n", methodName);
+ return NULL;
+ }
+ } else {
+ LOGVV("+++ resolved immediately: %s (%s %d)\n", resMethod->name,
+ resMethod->clazz->descriptor, (u4) resMethod->methodIndex);
+ }
+
+ /* we're expecting this to be abstract */
+ if (!dvmIsAbstractMethod(resMethod)) {
+ char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
+ LOGW("Found non-abstract interface method %s.%s %s\n",
+ resMethod->clazz->descriptor, resMethod->name, desc);
+ free(desc);
+ return NULL;
+ }
+
+ /*
+ * Add it to the resolved table so we're faster on the next lookup.
+ */
+ dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod);
+ }
+
+ LOGVV("--- found interface method %d (%s.%s)\n",
+ methodIdx, resMethod->clazz->descriptor, resMethod->name);
+
+ /* interface methods are always public; no need to check access */
+
+ return resMethod;
+}
+
+/*
+ * See if the method being called can be rewritten as an inline operation.
+ * Works for invoke-virtual, invoke-direct, and invoke-static.
+ *
+ * Returns "true" if we replace it.
+ */
+static bool rewriteExecuteInline(Method* method, u2* insns,
+ MethodType methodType)
+{
+ const InlineSub* inlineSubs = gDvm.inlineSubs;
+ ClassObject* clazz = method->clazz;
+ Method* calledMethod;
+ u2 methodIdx = insns[1];
+
+ //return false;
+
+ calledMethod = dvmOptResolveMethod(clazz, methodIdx, methodType, NULL);
+ if (calledMethod == NULL) {
+ LOGV("+++ DexOpt inline: can't find %d\n", methodIdx);
+ return false;
+ }
+
+ while (inlineSubs->method != NULL) {
+ /*
+ if (extra) {
+ LOGI("comparing %p vs %p %s.%s %s\n",
+ inlineSubs->method, calledMethod,
+ inlineSubs->method->clazz->descriptor,
+ inlineSubs->method->name,
+ inlineSubs->method->signature);
+ }
+ */
+ if (inlineSubs->method == calledMethod) {
+ assert((insns[0] & 0xff) == OP_INVOKE_DIRECT ||
+ (insns[0] & 0xff) == OP_INVOKE_STATIC ||
+ (insns[0] & 0xff) == OP_INVOKE_VIRTUAL);
+ updateCode(method, insns,
+ (insns[0] & 0xff00) | (u2) OP_EXECUTE_INLINE);
+ updateCode(method, insns+1, (u2) inlineSubs->inlineIdx);
+
+ //LOGI("DexOpt: execute-inline %s.%s --> %s.%s\n",
+ // method->clazz->descriptor, method->name,
+ // calledMethod->clazz->descriptor, calledMethod->name);
+ return true;
+ }
+
+ inlineSubs++;
+ }
+
+ return false;
+}
+
+/*
+ * See if the method being called can be rewritten as an inline operation.
+ * Works for invoke-virtual/range, invoke-direct/range, and invoke-static/range.
+ *
+ * Returns "true" if we replace it.
+ */
+static bool rewriteExecuteInlineRange(Method* method, u2* insns,
+ MethodType methodType)
+{
+ const InlineSub* inlineSubs = gDvm.inlineSubs;
+ ClassObject* clazz = method->clazz;
+ Method* calledMethod;
+ u2 methodIdx = insns[1];
+
+ calledMethod = dvmOptResolveMethod(clazz, methodIdx, methodType, NULL);
+ if (calledMethod == NULL) {
+ LOGV("+++ DexOpt inline/range: can't find %d\n", methodIdx);
+ return false;
+ }
+
+ while (inlineSubs->method != NULL) {
+ if (inlineSubs->method == calledMethod) {
+ assert((insns[0] & 0xff) == OP_INVOKE_DIRECT_RANGE ||
+ (insns[0] & 0xff) == OP_INVOKE_STATIC_RANGE ||
+ (insns[0] & 0xff) == OP_INVOKE_VIRTUAL_RANGE);
+ updateCode(method, insns,
+ (insns[0] & 0xff00) | (u2) OP_EXECUTE_INLINE_RANGE);
+ updateCode(method, insns+1, (u2) inlineSubs->inlineIdx);
+
+ //LOGI("DexOpt: execute-inline/range %s.%s --> %s.%s\n",
+ // method->clazz->descriptor, method->name,
+ // calledMethod->clazz->descriptor, calledMethod->name);
+ return true;
+ }
+
+ inlineSubs++;
+ }
+
+ return false;
+}
diff --git a/vm/analysis/Optimize.h b/vm/analysis/Optimize.h
new file mode 100644
index 0000000..30f7eef
--- /dev/null
+++ b/vm/analysis/Optimize.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Bytecode optimization declarations.
+ */
+#ifndef _DALVIK_OPTIMIZE
+#define _DALVIK_OPTIMIZE
+
+/*
+ * Prep data structures.
+ */
+InlineSub* dvmCreateInlineSubsTable(void);
+void dvmFreeInlineSubsTable(InlineSub* inlineSubs);
+
+/*
+ * Entry point from DEX preparation.
+ */
+void dvmOptimizeClass(ClassObject* clazz, bool essentialOnly);
+
+/*
+ * Abbreviated resolution functions, for use by optimization and verification
+ * code.
+ */
+ClassObject* dvmOptResolveClass(ClassObject* referrer, u4 classIdx,
+ VerifyError* pFailure);
+Method* dvmOptResolveMethod(ClassObject* referrer, u4 methodIdx,
+ MethodType methodType, VerifyError* pFailure);
+Method* dvmOptResolveInterfaceMethod(ClassObject* referrer, u4 methodIdx);
+InstField* dvmOptResolveInstField(ClassObject* referrer, u4 ifieldIdx,
+ VerifyError* pFailure);
+StaticField* dvmOptResolveStaticField(ClassObject* referrer, u4 sfieldIdx,
+ VerifyError* pFailure);
+
+#endif /*_DALVIK_OPTIMIZE*/
diff --git a/vm/analysis/RegisterMap.c b/vm/analysis/RegisterMap.c
new file mode 100644
index 0000000..f7d92cd
--- /dev/null
+++ b/vm/analysis/RegisterMap.c
@@ -0,0 +1,3272 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This code generate "register maps" for Dalvik bytecode. In a stack-based
+ * VM we might call these "stack maps". They are used to increase the
+ * precision in the garbage collector when scanning references in the
+ * interpreter thread stacks.
+ */
+#include "Dalvik.h"
+#include "analysis/CodeVerify.h"
+#include "analysis/RegisterMap.h"
+#include "libdex/DexCatch.h"
+#include "libdex/InstrUtils.h"
+#include "libdex/Leb128.h"
+
+#include <stddef.h>
+
+/* double-check the compression */
+#define REGISTER_MAP_VERIFY false
+
+/* verbose logging */
+#define REGISTER_MAP_VERBOSE false
+
+//#define REGISTER_MAP_STATS
+
+// fwd
+static void outputTypeVector(const RegType* regs, int insnRegCount, u1* data);
+static bool verifyMap(VerifierData* vdata, const RegisterMap* pMap);
+static int compareMaps(const RegisterMap* pMap1, const RegisterMap* pMap2);
+
+#ifdef REGISTER_MAP_STATS
+static void computeMapStats(RegisterMap* pMap, const Method* method);
+#endif
+static RegisterMap* compressMapDifferential(const RegisterMap* pMap,\
+ const Method* meth);
+static RegisterMap* uncompressMapDifferential(const RegisterMap* pMap);
+
+#ifdef REGISTER_MAP_STATS
+/*
+ * Generate some statistics on the register maps we create and use.
+ */
+#define kMaxGcPointGap 50
+#define kUpdatePosnMinRegs 24
+#define kNumUpdatePosns 8
+#define kMaxDiffBits 20
+typedef struct MapStats {
+ /*
+ * Buckets measuring the distance between GC points. This tells us how
+ * many bits we need to encode the advancing program counter. We ignore
+ * some of the "long tail" entries.
+ */
+ int gcPointGap[kMaxGcPointGap];
+
+ /*
+ * Number of gaps. Equal to (number of gcPoints - number of methods),
+ * since the computation isn't including the initial gap.
+ */
+ int gcGapCount;
+
+ /*
+ * Number of gaps.
+ */
+ int totalGcPointCount;
+
+ /*
+ * For larger methods (>= 24 registers), measure in which octant register
+ * updates occur. This should help us understand whether register
+ * changes tend to cluster in the low regs even for large methods.
+ */
+ int updatePosn[kNumUpdatePosns];
+
+ /*
+ * For all methods, count up the number of changes to registers < 16
+ * and >= 16.
+ */
+ int updateLT16;
+ int updateGE16;
+
+ /*
+ * Histogram of the number of bits that differ between adjacent entries.
+ */
+ int numDiffBits[kMaxDiffBits];
+
+
+ /*
+ * Track the number of expanded maps, and the heap space required to
+ * hold them.
+ */
+ int numExpandedMaps;
+ int totalExpandedMapSize;
+} MapStats;
+#endif
+
+/*
+ * Prepare some things.
+ */
+bool dvmRegisterMapStartup(void)
+{
+#ifdef REGISTER_MAP_STATS
+ MapStats* pStats = calloc(1, sizeof(MapStats));
+ gDvm.registerMapStats = pStats;
+#endif
+ return true;
+}
+
+/*
+ * Clean up.
+ */
+void dvmRegisterMapShutdown(void)
+{
+#ifdef REGISTER_MAP_STATS
+ free(gDvm.registerMapStats);
+#endif
+}
+
+/*
+ * Write stats to log file.
+ */
+void dvmRegisterMapDumpStats(void)
+{
+#ifdef REGISTER_MAP_STATS
+ MapStats* pStats = (MapStats*) gDvm.registerMapStats;
+ int i, end;
+
+ for (end = kMaxGcPointGap-1; end >= 0; end--) {
+ if (pStats->gcPointGap[end] != 0)
+ break;
+ }
+
+ LOGI("Register Map gcPointGap stats (diff count=%d, total=%d):\n",
+ pStats->gcGapCount, pStats->totalGcPointCount);
+ assert(pStats->gcPointGap[0] == 0);
+ for (i = 1; i <= end; i++) {
+ LOGI(" %2d %d\n", i, pStats->gcPointGap[i]);
+ }
+
+
+ for (end = kMaxDiffBits-1; end >= 0; end--) {
+ if (pStats->numDiffBits[end] != 0)
+ break;
+ }
+
+ LOGI("Register Map bit difference stats:\n");
+ for (i = 0; i <= end; i++) {
+ LOGI(" %2d %d\n", i, pStats->numDiffBits[i]);
+ }
+
+
+ LOGI("Register Map update position stats (lt16=%d ge16=%d):\n",
+ pStats->updateLT16, pStats->updateGE16);
+ for (i = 0; i < kNumUpdatePosns; i++) {
+ LOGI(" %2d %d\n", i, pStats->updatePosn[i]);
+ }
+#endif
+}
+
+
+/*
+ * ===========================================================================
+ * Map generation
+ * ===========================================================================
+ */
+
+/*
+ * Generate the register map for a method that has just been verified
+ * (i.e. we're doing this as part of verification).
+ *
+ * For type-precise determination we have all the data we need, so we
+ * just need to encode it in some clever fashion.
+ *
+ * Returns a pointer to a newly-allocated RegisterMap, or NULL on failure.
+ */
+RegisterMap* dvmGenerateRegisterMapV(VerifierData* vdata)
+{
+ static const int kHeaderSize = offsetof(RegisterMap, data);
+ RegisterMap* pMap = NULL;
+ RegisterMap* pResult = NULL;
+ RegisterMapFormat format;
+ u1 regWidth;
+ u1* mapData;
+ int i, bytesForAddr, gcPointCount;
+ int bufSize;
+
+ if (vdata->method->registersSize >= 2048) {
+ LOGE("ERROR: register map can't handle %d registers\n",
+ vdata->method->registersSize);
+ goto bail;
+ }
+ regWidth = (vdata->method->registersSize + 7) / 8;
+
+ /*
+ * Decide if we need 8 or 16 bits to hold the address. Strictly speaking
+ * we only need 16 bits if we actually encode an address >= 256 -- if
+ * the method has a section at the end without GC points (e.g. array
+ * data) we don't need to count it. The situation is unusual, and
+ * detecting it requires scanning the entire method, so we don't bother.
+ */
+ if (vdata->insnsSize < 256) {
+ format = kRegMapFormatCompact8;
+ bytesForAddr = 1;
+ } else {
+ format = kRegMapFormatCompact16;
+ bytesForAddr = 2;
+ }
+
+ /*
+ * Count up the number of GC point instructions.
+ *
+ * NOTE: this does not automatically include the first instruction,
+ * since we don't count method entry as a GC point.
+ */
+ gcPointCount = 0;
+ for (i = 0; i < (int) vdata->insnsSize; i++) {
+ if (dvmInsnIsGcPoint(vdata->insnFlags, i))
+ gcPointCount++;
+ }
+ if (gcPointCount >= 65536) {
+ /* we could handle this, but in practice we don't get near this */
+ LOGE("ERROR: register map can't handle %d gc points in one method\n",
+ gcPointCount);
+ goto bail;
+ }
+
+ /*
+ * Allocate a buffer to hold the map data.
+ */
+ bufSize = kHeaderSize + gcPointCount * (bytesForAddr + regWidth);
+
+ LOGV("+++ grm: %s.%s (adr=%d gpc=%d rwd=%d bsz=%d)\n",
+ vdata->method->clazz->descriptor, vdata->method->name,
+ bytesForAddr, gcPointCount, regWidth, bufSize);
+
+ pMap = (RegisterMap*) malloc(bufSize);
+ dvmRegisterMapSetFormat(pMap, format);
+ dvmRegisterMapSetOnHeap(pMap, true);
+ dvmRegisterMapSetRegWidth(pMap, regWidth);
+ dvmRegisterMapSetNumEntries(pMap, gcPointCount);
+
+ /*
+ * Populate it.
+ */
+ mapData = pMap->data;
+ for (i = 0; i < (int) vdata->insnsSize; i++) {
+ if (dvmInsnIsGcPoint(vdata->insnFlags, i)) {
+ assert(vdata->addrRegs[i] != NULL);
+ if (format == kRegMapFormatCompact8) {
+ *mapData++ = i;
+ } else /*kRegMapFormatCompact16*/ {
+ *mapData++ = i & 0xff;
+ *mapData++ = i >> 8;
+ }
+ outputTypeVector(vdata->addrRegs[i], vdata->insnRegCount, mapData);
+ mapData += regWidth;
+ }
+ }
+
+ LOGV("mapData=%p pMap=%p bufSize=%d\n", mapData, pMap, bufSize);
+ assert(mapData - (const u1*) pMap == bufSize);
+
+ if (REGISTER_MAP_VERIFY && !verifyMap(vdata, pMap))
+ goto bail;
+#ifdef REGISTER_MAP_STATS
+ computeMapStats(pMap, vdata->method);
+#endif
+
+ /*
+ * Try to compress the map.
+ */
+ RegisterMap* pCompMap;
+
+ pCompMap = compressMapDifferential(pMap, vdata->method);
+ if (pCompMap != NULL) {
+ if (REGISTER_MAP_VERIFY) {
+ /*
+ * Expand the compressed map we just created, and compare it
+ * to the original. Abort the VM if it doesn't match up.
+ */
+ RegisterMap* pUncompMap;
+ pUncompMap = uncompressMapDifferential(pCompMap);
+ if (pUncompMap == NULL) {
+ LOGE("Map failed to uncompress - %s.%s\n",
+ vdata->method->clazz->descriptor,
+ vdata->method->name);
+ free(pCompMap);
+ /* bad - compression is broken or we're out of memory */
+ dvmAbort();
+ } else {
+ if (compareMaps(pMap, pUncompMap) != 0) {
+ LOGE("Map comparison failed - %s.%s\n",
+ vdata->method->clazz->descriptor,
+ vdata->method->name);
+ free(pCompMap);
+ /* bad - compression is broken */
+ dvmAbort();
+ }
+
+ /* verify succeeded */
+ free(pUncompMap);
+ }
+ }
+
+ if (REGISTER_MAP_VERBOSE) {
+ LOGD("Good compress on %s.%s\n",
+ vdata->method->clazz->descriptor,
+ vdata->method->name);
+ }
+ free(pMap);
+ pMap = pCompMap;
+ } else {
+ if (REGISTER_MAP_VERBOSE) {
+ LOGD("Unable to compress %s.%s (ent=%d rw=%d)\n",
+ vdata->method->clazz->descriptor,
+ vdata->method->name,
+ dvmRegisterMapGetNumEntries(pMap),
+ dvmRegisterMapGetRegWidth(pMap));
+ }
+ }
+
+ pResult = pMap;
+
+bail:
+ return pResult;
+}
+
+/*
+ * Release the storage held by a RegisterMap.
+ */
+void dvmFreeRegisterMap(RegisterMap* pMap)
+{
+ if (pMap == NULL)
+ return;
+
+ assert(dvmRegisterMapGetOnHeap(pMap));
+ free(pMap);
+}
+
+/*
+ * Determine if the RegType value is a reference type.
+ *
+ * Ordinarily we include kRegTypeZero in the "is it a reference"
+ * check. There's no value in doing so here, because we know
+ * the register can't hold anything but zero.
+ */
+static inline bool isReferenceType(RegType type)
+{
+ return (type > kRegTypeMAX || type == kRegTypeUninit);
+}
+
+/*
+ * Given a line of registers, output a bit vector that indicates whether
+ * or not the register holds a reference type (which could be null).
+ *
+ * We use '1' to indicate it's a reference, '0' for anything else (numeric
+ * value, uninitialized data, merge conflict). Register 0 will be found
+ * in the low bit of the first byte.
+ */
+static void outputTypeVector(const RegType* regs, int insnRegCount, u1* data)
+{
+ u1 val = 0;
+ int i;
+
+ for (i = 0; i < insnRegCount; i++) {
+ RegType type = *regs++;
+ val >>= 1;
+ if (isReferenceType(type))
+ val |= 0x80; /* set hi bit */
+
+ if ((i & 0x07) == 7)
+ *data++ = val;
+ }
+ if ((i & 0x07) != 0) {
+ /* flush bits from last byte */
+ val >>= 8 - (i & 0x07);
+ *data++ = val;
+ }
+}
+
+/*
+ * Print the map as a series of binary strings.
+ *
+ * Pass in method->registersSize if known, or -1 if not.
+ */
+static void dumpRegisterMap(const RegisterMap* pMap, int registersSize)
+{
+ const u1* rawMap = pMap->data;
+ const RegisterMapFormat format = dvmRegisterMapGetFormat(pMap);
+ const int numEntries = dvmRegisterMapGetNumEntries(pMap);
+ const int regWidth = dvmRegisterMapGetRegWidth(pMap);
+ int addrWidth;
+
+ switch (format) {
+ case kRegMapFormatCompact8:
+ addrWidth = 1;
+ break;
+ case kRegMapFormatCompact16:
+ addrWidth = 2;
+ break;
+ default:
+ /* can't happen */
+ LOGE("Can only dump Compact8 / Compact16 maps (not %d)\n", format);
+ return;
+ }
+
+ if (registersSize < 0)
+ registersSize = 8 * regWidth;
+ assert(registersSize <= regWidth * 8);
+
+ int ent;
+ for (ent = 0; ent < numEntries; ent++) {
+ int i, addr;
+
+ addr = *rawMap++;
+ if (addrWidth > 1)
+ addr |= (*rawMap++) << 8;
+
+ const u1* dataStart = rawMap;
+ u1 val = 0;
+
+ /* create binary string */
+ char outBuf[registersSize +1];
+ for (i = 0; i < registersSize; i++) {
+ val >>= 1;
+ if ((i & 0x07) == 0)
+ val = *rawMap++;
+
+ outBuf[i] = '0' + (val & 0x01);
+ }
+ outBuf[i] = '\0';
+
+ /* back up and create hex dump */
+ char hexBuf[regWidth * 3 +1];
+ char* cp = hexBuf;
+ rawMap = dataStart;
+ for (i = 0; i < regWidth; i++) {
+ sprintf(cp, " %02x", *rawMap++);
+ cp += 3;
+ }
+ hexBuf[i * 3] = '\0';
+
+ LOGD(" %04x %s %s\n", addr, outBuf, hexBuf);
+ }
+}
+
+/*
+ * Double-check the map.
+ *
+ * We run through all of the data in the map, and compare it to the original.
+ * Only works on uncompressed data.
+ */
+static bool verifyMap(VerifierData* vdata, const RegisterMap* pMap)
+{
+ const u1* rawMap = pMap->data;
+ const RegisterMapFormat format = dvmRegisterMapGetFormat(pMap);
+ const int numEntries = dvmRegisterMapGetNumEntries(pMap);
+ int ent;
+ bool dumpMap = false;
+
+ if (false) {
+ const char* cd = "Landroid/net/http/Request;";
+ const char* mn = "readResponse";
+ if (strcmp(vdata->method->clazz->descriptor, cd) == 0 &&
+ strcmp(vdata->method->name, mn) == 0)
+ {
+ char* desc;
+ desc = dexProtoCopyMethodDescriptor(&vdata->method->prototype);
+ LOGI("Map for %s.%s %s\n", vdata->method->clazz->descriptor,
+ vdata->method->name, desc);
+ free(desc);
+
+ dumpMap = true;
+ }
+ }
+
+ if ((vdata->method->registersSize + 7) / 8 != pMap->regWidth) {
+ LOGE("GLITCH: registersSize=%d, regWidth=%d\n",
+ vdata->method->registersSize, pMap->regWidth);
+ return false;
+ }
+
+ for (ent = 0; ent < numEntries; ent++) {
+ int addr;
+
+ switch (format) {
+ case kRegMapFormatCompact8:
+ addr = *rawMap++;
+ break;
+ case kRegMapFormatCompact16:
+ addr = *rawMap++;
+ addr |= (*rawMap++) << 8;
+ break;
+ default:
+ /* shouldn't happen */
+ LOGE("GLITCH: bad format (%d)", format);
+ dvmAbort();
+ }
+
+ const RegType* regs = vdata->addrRegs[addr];
+ if (regs == NULL) {
+ LOGE("GLITCH: addr %d has no data\n", addr);
+ return false;
+ }
+
+ u1 val = 0;
+ int i;
+
+ for (i = 0; i < vdata->method->registersSize; i++) {
+ bool bitIsRef, regIsRef;
+
+ val >>= 1;
+ if ((i & 0x07) == 0) {
+ /* load next byte of data */
+ val = *rawMap++;
+ }
+
+ bitIsRef = val & 0x01;
+
+ RegType type = regs[i];
+ regIsRef = isReferenceType(type);
+
+ if (bitIsRef != regIsRef) {
+ LOGE("GLITCH: addr %d reg %d: bit=%d reg=%d(%d)\n",
+ addr, i, bitIsRef, regIsRef, type);
+ return false;
+ }
+ }
+
+ /* rawMap now points to the address field of the next entry */
+ }
+
+ if (dumpMap)
+ dumpRegisterMap(pMap, vdata->method->registersSize);
+
+ return true;
+}
+
+
+/*
+ * ===========================================================================
+ * DEX generation & parsing
+ * ===========================================================================
+ */
+
+/*
+ * Advance "ptr" to ensure 32-bit alignment.
+ */
+static inline u1* align32(u1* ptr)
+{
+ return (u1*) (((int) ptr + 3) & ~0x03);
+}
+
+/*
+ * Compute the size, in bytes, of a register map.
+ */
+static size_t computeRegisterMapSize(const RegisterMap* pMap)
+{
+ static const int kHeaderSize = offsetof(RegisterMap, data);
+ u1 format = dvmRegisterMapGetFormat(pMap);
+ u2 numEntries = dvmRegisterMapGetNumEntries(pMap);
+
+ assert(pMap != NULL);
+
+ switch (format) {
+ case kRegMapFormatNone:
+ return 1;
+ case kRegMapFormatCompact8:
+ return kHeaderSize + (1 + pMap->regWidth) * numEntries;
+ case kRegMapFormatCompact16:
+ return kHeaderSize + (2 + pMap->regWidth) * numEntries;
+ case kRegMapFormatDifferential:
+ {
+ /* kHeaderSize + decoded ULEB128 length */
+ const u1* ptr = pMap->data;
+ int len = readUnsignedLeb128(&ptr);
+ return len + (ptr - (u1*) pMap);
+ }
+ default:
+ LOGE("Bad register map format %d\n", format);
+ dvmAbort();
+ return 0;
+ }
+}
+
+/*
+ * Output the map for a single method, if it has one.
+ *
+ * Abstract and native methods have no map. All others are expected to
+ * have one, since we know the class verified successfully.
+ *
+ * This strips the "allocated on heap" flag from the format byte, so that
+ * direct-mapped maps are correctly identified as such.
+ */
+static bool writeMapForMethod(const Method* meth, u1** pPtr)
+{
+ if (meth->registerMap == NULL) {
+ if (!dvmIsAbstractMethod(meth) && !dvmIsNativeMethod(meth)) {
+ LOGW("Warning: no map available for %s.%s\n",
+ meth->clazz->descriptor, meth->name);
+ /* weird, but keep going */
+ }
+ *(*pPtr)++ = kRegMapFormatNone;
+ return true;
+ }
+
+ /* serialize map into the buffer */
+ size_t mapSize = computeRegisterMapSize(meth->registerMap);
+ memcpy(*pPtr, meth->registerMap, mapSize);
+
+ /* strip the "on heap" flag out of the format byte, which is always first */
+ assert(**pPtr == meth->registerMap->format);
+ **pPtr &= ~(kRegMapFormatOnHeap);
+
+ *pPtr += mapSize;
+
+ return true;
+}
+
+/*
+ * Write maps for all methods in the specified class to the buffer, which
+ * can hold at most "length" bytes. "*pPtr" will be advanced past the end
+ * of the data we write.
+ */
+static bool writeMapsAllMethods(DvmDex* pDvmDex, const ClassObject* clazz,
+ u1** pPtr, size_t length)
+{
+ RegisterMapMethodPool* pMethodPool;
+ u1* ptr = *pPtr;
+ int i, methodCount;
+
+ /* artificial limit */
+ if (clazz->virtualMethodCount + clazz->directMethodCount >= 65536) {
+ LOGE("Too many methods in %s\n", clazz->descriptor);
+ return false;
+ }
+
+ pMethodPool = (RegisterMapMethodPool*) ptr;
+ ptr += offsetof(RegisterMapMethodPool, methodData);
+ methodCount = 0;
+
+ /*
+ * Run through all methods, direct then virtual. The class loader will
+ * traverse them in the same order. (We could split them into two
+ * distinct pieces, but there doesn't appear to be any value in doing
+ * so other than that it makes class loading slightly less fragile.)
+ *
+ * The class loader won't know about miranda methods at the point
+ * where it parses this, so we omit those.
+ *
+ * TODO: consider omitting all native/abstract definitions. Should be
+ * safe, though we lose the ability to sanity-check against the
+ * method counts in the DEX file.
+ */
+ for (i = 0; i < clazz->directMethodCount; i++) {
+ const Method* meth = &clazz->directMethods[i];
+ if (dvmIsMirandaMethod(meth))
+ continue;
+ if (!writeMapForMethod(&clazz->directMethods[i], &ptr)) {
+ return false;
+ }
+ methodCount++;
+ //ptr = align32(ptr);
+ }
+
+ for (i = 0; i < clazz->virtualMethodCount; i++) {
+ const Method* meth = &clazz->virtualMethods[i];
+ if (dvmIsMirandaMethod(meth))
+ continue;
+ if (!writeMapForMethod(&clazz->virtualMethods[i], &ptr)) {
+ return false;
+ }
+ methodCount++;
+ //ptr = align32(ptr);
+ }
+
+ pMethodPool->methodCount = methodCount;
+
+ *pPtr = ptr;
+ return true;
+}
+
+/*
+ * Write maps for all classes to the specified buffer, which can hold at
+ * most "length" bytes.
+ *
+ * Returns the actual length used, or 0 on failure.
+ */
+static size_t writeMapsAllClasses(DvmDex* pDvmDex, u1* basePtr, size_t length)
+{
+ DexFile* pDexFile = pDvmDex->pDexFile;
+ u4 count = pDexFile->pHeader->classDefsSize;
+ RegisterMapClassPool* pClassPool;
+ u4* offsetTable;
+ u1* ptr = basePtr;
+ u4 idx;
+
+ assert(gDvm.optimizing);
+
+ pClassPool = (RegisterMapClassPool*) ptr;
+ ptr += offsetof(RegisterMapClassPool, classDataOffset);
+ offsetTable = (u4*) ptr;
+ ptr += count * sizeof(u4);
+
+ pClassPool->numClasses = count;
+
+ /*
+ * We want an entry for every class, loaded or not.
+ */
+ for (idx = 0; idx < count; idx++) {
+ const DexClassDef* pClassDef;
+ const char* classDescriptor;
+ ClassObject* clazz;
+
+ pClassDef = dexGetClassDef(pDexFile, idx);
+ classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
+
+ /*
+ * All classes have been loaded into the bootstrap class loader.
+ * If we can find it, and it was successfully pre-verified, we
+ * run through its methods and add the register maps.
+ *
+ * If it wasn't pre-verified then we know it can't have any
+ * register maps. Classes that can't be loaded or failed
+ * verification get an empty slot in the index.
+ */
+ clazz = NULL;
+ if ((pClassDef->accessFlags & CLASS_ISPREVERIFIED) != 0)
+ clazz = dvmLookupClass(classDescriptor, NULL, false);
+
+ if (clazz != NULL) {
+ offsetTable[idx] = ptr - basePtr;
+ LOGVV("%d -> offset %d (%p-%p)\n",
+ idx, offsetTable[idx], ptr, basePtr);
+
+ if (!writeMapsAllMethods(pDvmDex, clazz, &ptr,
+ length - (ptr - basePtr)))
+ {
+ return 0;
+ }
+
+ ptr = align32(ptr);
+ LOGVV("Size %s (%d+%d methods): %d\n", clazz->descriptor,
+ clazz->directMethodCount, clazz->virtualMethodCount,
+ (ptr - basePtr) - offsetTable[idx]);
+ } else {
+ LOGV("%4d NOT mapadding '%s'\n", idx, classDescriptor);
+ assert(offsetTable[idx] == 0);
+ }
+ }
+
+ if (ptr - basePtr >= (int)length) {
+ /* a bit late */
+ LOGE("Buffer overrun\n");
+ dvmAbort();
+ }
+
+ return ptr - basePtr;
+}
+
+/*
+ * Generate a register map set for all verified classes in "pDvmDex".
+ */
+RegisterMapBuilder* dvmGenerateRegisterMaps(DvmDex* pDvmDex)
+{
+ RegisterMapBuilder* pBuilder;
+
+ pBuilder = (RegisterMapBuilder*) calloc(1, sizeof(RegisterMapBuilder));
+ if (pBuilder == NULL)
+ return NULL;
+
+ /*
+ * We have a couple of options here:
+ * (1) Compute the size of the output, and malloc a buffer.
+ * (2) Create a "large-enough" anonymous mmap region.
+ *
+ * The nice thing about option #2 is that we don't have to traverse
+ * all of the classes and methods twice. The risk is that we might
+ * not make the region large enough. Since the pages aren't mapped
+ * until used we can allocate a semi-absurd amount of memory without
+ * worrying about the effect on the rest of the system.
+ *
+ * The basic encoding on the largest jar file requires about 1MB of
+ * storage. We map out 4MB here. (TODO: guarantee that the last
+ * page of the mapping is marked invalid, so we reliably fail if
+ * we overrun.)
+ */
+ if (sysCreatePrivateMap(4 * 1024 * 1024, &pBuilder->memMap) != 0) {
+ free(pBuilder);
+ return NULL;
+ }
+
+ /*
+ * Create the maps.
+ */
+ size_t actual = writeMapsAllClasses(pDvmDex, (u1*)pBuilder->memMap.addr,
+ pBuilder->memMap.length);
+ if (actual == 0) {
+ dvmFreeRegisterMapBuilder(pBuilder);
+ return NULL;
+ }
+
+ LOGV("TOTAL size of register maps: %d\n", actual);
+
+ pBuilder->data = pBuilder->memMap.addr;
+ pBuilder->size = actual;
+ return pBuilder;
+}
+
+/*
+ * Free the builder.
+ */
+void dvmFreeRegisterMapBuilder(RegisterMapBuilder* pBuilder)
+{
+ if (pBuilder == NULL)
+ return;
+
+ sysReleaseShmem(&pBuilder->memMap);
+ free(pBuilder);
+}
+
+
+/*
+ * Find the data for the specified class.
+ *
+ * If there's no register map data, or none for this class, we return NULL.
+ */
+const void* dvmRegisterMapGetClassData(const DexFile* pDexFile, u4 classIdx,
+ u4* pNumMaps)
+{
+ const RegisterMapClassPool* pClassPool;
+ const RegisterMapMethodPool* pMethodPool;
+
+ pClassPool = (const RegisterMapClassPool*) pDexFile->pRegisterMapPool;
+ if (pClassPool == NULL)
+ return NULL;
+
+ if (classIdx >= pClassPool->numClasses) {
+ LOGE("bad class index (%d vs %d)\n", classIdx, pClassPool->numClasses);
+ dvmAbort();
+ }
+
+ u4 classOffset = pClassPool->classDataOffset[classIdx];
+ if (classOffset == 0) {
+ LOGV("+++ no map for classIdx=%d\n", classIdx);
+ return NULL;
+ }
+
+ pMethodPool =
+ (const RegisterMapMethodPool*) (((u1*) pClassPool) + classOffset);
+ if (pNumMaps != NULL)
+ *pNumMaps = pMethodPool->methodCount;
+ return pMethodPool->methodData;
+}
+
+/*
+ * This advances "*pPtr" and returns its original value.
+ */
+const RegisterMap* dvmRegisterMapGetNext(const void** pPtr)
+{
+ const RegisterMap* pMap = *pPtr;
+
+ *pPtr = /*align32*/(((u1*) pMap) + computeRegisterMapSize(pMap));
+ LOGVV("getNext: %p -> %p (f=0x%x w=%d e=%d)\n",
+ pMap, *pPtr, pMap->format, pMap->regWidth,
+ dvmRegisterMapGetNumEntries(pMap));
+ return pMap;
+}
+
+
+/*
+ * ===========================================================================
+ * Utility functions
+ * ===========================================================================
+ */
+
+/*
+ * Return the data for the specified address, or NULL if not found.
+ *
+ * The result must be released with dvmReleaseRegisterMapLine().
+ */
+const u1* dvmRegisterMapGetLine(const RegisterMap* pMap, int addr)
+{
+ int addrWidth, lineWidth;
+ u1 format = dvmRegisterMapGetFormat(pMap);
+ u2 numEntries = dvmRegisterMapGetNumEntries(pMap);
+
+ assert(numEntries > 0);
+
+ switch (format) {
+ case kRegMapFormatNone:
+ return NULL;
+ case kRegMapFormatCompact8:
+ addrWidth = 1;
+ break;
+ case kRegMapFormatCompact16:
+ addrWidth = 2;
+ break;
+ default:
+ LOGE("Unknown format %d\n", format);
+ dvmAbort();
+ return NULL;
+ }
+
+ lineWidth = addrWidth + pMap->regWidth;
+
+ /*
+ * Find the appropriate entry. Many maps are very small, some are very
+ * large.
+ */
+ static const int kSearchThreshold = 8;
+ const u1* data = NULL;
+ int lineAddr;
+
+ if (numEntries < kSearchThreshold) {
+ int i;
+ data = pMap->data;
+ for (i = numEntries; i > 0; i--) {
+ lineAddr = data[0];
+ if (addrWidth > 1)
+ lineAddr |= data[1] << 8;
+ if (lineAddr == addr)
+ return data + addrWidth;
+
+ data += lineWidth;
+ }
+ assert(data == pMap->data + lineWidth * numEntries);
+ } else {
+ int hi, lo, mid;
+
+ lo = 0;
+ hi = numEntries -1;
+
+ while (hi >= lo) {
+ mid = (hi + lo) / 2;
+ data = pMap->data + lineWidth * mid;
+
+ lineAddr = data[0];
+ if (addrWidth > 1)
+ lineAddr |= data[1] << 8;
+
+ if (addr > lineAddr) {
+ lo = mid + 1;
+ } else if (addr < lineAddr) {
+ hi = mid - 1;
+ } else {
+ return data + addrWidth;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Compare two register maps.
+ *
+ * Returns 0 if they're equal, nonzero if not.
+ */
+static int compareMaps(const RegisterMap* pMap1, const RegisterMap* pMap2)
+{
+ size_t size1, size2;
+
+ size1 = computeRegisterMapSize(pMap1);
+ size2 = computeRegisterMapSize(pMap2);
+ if (size1 != size2) {
+ LOGI("compareMaps: size mismatch (%zd vs %zd)\n", size1, size2);
+ return -1;
+ }
+
+ if (memcmp(pMap1, pMap2, size1) != 0) {
+ LOGI("compareMaps: content mismatch\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Get the expanded form of the register map associated with the method.
+ *
+ * If the map is already in one of the uncompressed formats, we return
+ * immediately. Otherwise, we expand the map and replace method's register
+ * map pointer, freeing it if it was allocated on the heap.
+ *
+ * NOTE: this function is not synchronized; external locking is mandatory
+ * (unless we're in the zygote, where single-threaded access is guaranteed).
+ */
+const RegisterMap* dvmGetExpandedRegisterMap0(Method* method)
+{
+ const RegisterMap* curMap = method->registerMap;
+ RegisterMap* newMap;
+
+ if (curMap == NULL)
+ return NULL;
+
+ /* sanity check to ensure this isn't called w/o external locking */
+ /* (if we use this at a time other than during GC, fix/remove this test) */
+ if (true) {
+ if (!gDvm.zygote && dvmTryLockMutex(&gDvm.gcHeapLock) == 0) {
+ LOGE("GLITCH: dvmGetExpandedRegisterMap not called at GC time\n");
+ dvmAbort();
+ }
+ }
+
+ RegisterMapFormat format = dvmRegisterMapGetFormat(curMap);
+ switch (format) {
+ case kRegMapFormatCompact8:
+ case kRegMapFormatCompact16:
+ if (REGISTER_MAP_VERBOSE) {
+ if (dvmRegisterMapGetOnHeap(curMap)) {
+ LOGD("RegMap: already expanded: %s.%s\n",
+ method->clazz->descriptor, method->name);
+ } else {
+ LOGD("RegMap: stored w/o compression: %s.%s\n",
+ method->clazz->descriptor, method->name);
+ }
+ }
+ return curMap;
+ case kRegMapFormatDifferential:
+ newMap = uncompressMapDifferential(curMap);
+ break;
+ default:
+ LOGE("Unknown format %d in dvmGetExpandedRegisterMap\n", format);
+ dvmAbort();
+ newMap = NULL; // make gcc happy
+ }
+
+ if (newMap == NULL) {
+ LOGE("Map failed to uncompress (fmt=%d) %s.%s\n",
+ format, method->clazz->descriptor, method->name);
+ return NULL;
+ }
+
+#ifdef REGISTER_MAP_STATS
+ /*
+ * Gather and display some stats.
+ */
+ {
+ MapStats* pStats = (MapStats*) gDvm.registerMapStats;
+ pStats->numExpandedMaps++;
+ pStats->totalExpandedMapSize += computeRegisterMapSize(newMap);
+ LOGD("RMAP: count=%d size=%d\n",
+ pStats->numExpandedMaps, pStats->totalExpandedMapSize);
+ }
+#endif
+
+ IF_LOGV() {
+ char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+ LOGV("Expanding map -> %s.%s:%s\n",
+ method->clazz->descriptor, method->name, desc);
+ free(desc);
+ }
+
+ /*
+ * Update method, and free compressed map if it was sitting on the heap.
+ */
+ dvmSetRegisterMap(method, newMap);
+
+ if (dvmRegisterMapGetOnHeap(curMap))
+ dvmFreeRegisterMap((RegisterMap*) curMap);
+
+ return newMap;
+}
+
+
+/*
+ * ===========================================================================
+ * Map compression
+ * ===========================================================================
+ */
+
+/*
+Notes on map compression
+
+The idea is to create a compressed form that will be uncompressed before
+use, with the output possibly saved in a cache. This means we can use an
+approach that is unsuited for random access if we choose.
+
+In the event that a map simply does not work with our compression scheme,
+it's reasonable to store the map without compression. In the future we
+may want to have more than one compression scheme, and try each in turn,
+retaining the best. (We certainly want to keep the uncompressed form if it
+turns out to be smaller or even slightly larger than the compressed form.)
+
+Each entry consists of an address and a bit vector. Adjacent entries are
+strongly correlated, suggesting differential encoding.
+
+
+Ideally we would avoid outputting adjacent entries with identical
+bit vectors. However, the register values at a given address do not
+imply anything about the set of valid registers at subsequent addresses.
+We therefore cannot omit an entry.
+
+ If the thread stack has a PC at an address without a corresponding
+ entry in the register map, we must conservatively scan the registers in
+ that thread. This can happen when single-stepping in the debugger,
+ because the debugger is allowed to invoke arbitrary methods when
+ a thread is stopped at a breakpoint. If we can guarantee that a GC
+ thread scan will never happen while the debugger has that thread stopped,
+ then we can lift this restriction and simply omit entries that don't
+ change the bit vector from its previous state.
+
+Each entry advances the address value by at least 1 (measured in 16-bit
+"code units"). Looking at the bootclasspath entries, advancing by 2 units
+is most common. Advances by 1 unit are far less common than advances by
+2 units, but more common than 5, and things fall off rapidly. Gaps of
+up to 220 code units appear in some computationally intensive bits of code,
+but are exceedingly rare.
+
+If we sum up the number of transitions in a couple of ranges in framework.jar:
+ [1,4]: 188998 of 218922 gaps (86.3%)
+ [1,7]: 211647 of 218922 gaps (96.7%)
+Using a 3-bit delta, with one value reserved as an escape code, should
+yield good results for the address.
+
+These results would change dramatically if we reduced the set of GC
+points by e.g. removing instructions like integer divide that are only
+present because they can throw and cause an allocation.
+
+We also need to include an "initial gap", because the first few instructions
+in a method may not be GC points.
+
+
+By observation, many entries simply repeat the previous bit vector, or
+change only one or two bits. (This is with type-precise information;
+the rate of change of bits will be different if live-precise information
+is factored in).
+
+Looking again at adjacent entries in framework.jar:
+ 0 bits changed: 63.0%
+ 1 bit changed: 32.2%
+After that it falls off rapidly, e.g. the number of entries with 2 bits
+changed is usually less than 1/10th of the number of entries with 1 bit
+changed. A solution that allows us to encode 0- or 1- bit changes
+efficiently will do well.
+
+We still need to handle cases where a large number of bits change. We
+probably want a way to drop in a full copy of the bit vector when it's
+smaller than the representation of multiple bit changes.
+
+
+The bit-change information can be encoded as an index that tells the
+decoder to toggle the state. We want to encode the index in as few bits
+as possible, but we need to allow for fairly wide vectors (e.g. we have a
+method with 175 registers). We can deal with this in a couple of ways:
+(1) use an encoding that assumes few registers and has an escape code
+for larger numbers of registers; or (2) use different encodings based
+on how many total registers the method has. The choice depends to some
+extent on whether methods with large numbers of registers tend to modify
+the first 16 regs more often than the others.
+
+The last N registers hold method arguments. If the bytecode is expected
+to be examined in a debugger, "dx" ensures that the contents of these
+registers won't change. Depending upon the encoding format, we may be
+able to take advantage of this. We still have to encode the initial
+state, but we know we'll never have to output a bit change for the last
+N registers.
+
+Considering only methods with 16 or more registers, the "target octant"
+for register changes looks like this:
+ [ 43.1%, 16.4%, 6.5%, 6.2%, 7.4%, 8.8%, 9.7%, 1.8% ]
+As expected, there are fewer changes at the end of the list where the
+arguments are kept, and more changes at the start of the list because
+register values smaller than 16 can be used in compact Dalvik instructions
+and hence are favored for frequently-used values. In general, the first
+octant is considerably more active than later entries, the last octant
+is much less active, and the rest are all about the same.
+
+Looking at all bit changes in all methods, 94% are to registers 0-15. The
+encoding will benefit greatly by favoring the low-numbered registers.
+
+
+Some of the smaller methods have identical maps, and space could be
+saved by simply including a pointer to an earlier definition. This would
+be best accomplished by specifying a "pointer" format value, followed by
+a 3-byte (or ULEB128) offset. Implementing this would probably involve
+generating a hash value for each register map and maintaining a hash table.
+
+In some cases there are repeating patterns in the bit vector that aren't
+adjacent. These could benefit from a dictionary encoding. This doesn't
+really become useful until the methods reach a certain size though,
+and managing the dictionary may incur more overhead than we want.
+
+Large maps can be compressed significantly. The trouble is that, when
+we need to use them, we have to uncompress them onto the heap. We may
+get a better trade-off between storage size and heap usage by refusing to
+compress large maps, so that they can be memory mapped and used directly.
+(OTOH, only about 2% of the maps will ever actually be used.)
+
+
+----- differential format -----
+
+// common header
++00 1B format
++01 1B regWidth
++02 2B numEntries (little-endian)
++04 nB length in bytes of the data that follows, in ULEB128 format
+ (not strictly necessary; allows determination of size w/o full parse)
++05+ 1B initial address (0-127), high bit set if max addr >= 256
++06+ nB initial value for bit vector
+
+// for each entry
++00: CCCCBAAA
+
+ AAA: address difference. Values from 0 to 6 indicate an increment of 1
+ to 7. A value of 7 indicates that the address difference is large,
+ and the next byte is a ULEB128-encoded difference value.
+
+ B: determines the meaning of CCCC.
+
+ CCCC: if B is 0, this is the number of the bit to toggle (0-15).
+ If B is 1, this is a count of the number of changed bits (1-14). A value
+ of 0 means that no bits were changed, and a value of 15 indicates
+ that enough bits were changed that it required less space to output
+ the entire bit vector.
+
++01: (optional) ULEB128-encoded address difference
+
++01+: (optional) one or more ULEB128-encoded bit numbers, OR the entire
+ bit vector.
+
+The most common situation is an entry whose address has changed by 2-4
+code units, has no changes or just a single bit change, and the changed
+register is less than 16. We should therefore be able to encode a large
+number of entries with a single byte, which is half the size of the
+Compact8 encoding method.
+*/
+
+/*
+ * Compute some stats on an uncompressed register map.
+ */
+#ifdef REGISTER_MAP_STATS
+static void computeMapStats(RegisterMap* pMap, const Method* method)
+{
+ MapStats* pStats = (MapStats*) gDvm.registerMapStats;
+ const u1 format = dvmRegisterMapGetFormat(pMap);
+ const u2 numEntries = dvmRegisterMapGetNumEntries(pMap);
+ const u1* rawMap = pMap->data;
+ const u1* prevData = NULL;
+ int ent, addr, prevAddr = -1;
+
+ for (ent = 0; ent < numEntries; ent++) {
+ switch (format) {
+ case kRegMapFormatCompact8:
+ addr = *rawMap++;
+ break;
+ case kRegMapFormatCompact16:
+ addr = *rawMap++;
+ addr |= (*rawMap++) << 8;
+ break;
+ default:
+ /* shouldn't happen */
+ LOGE("GLITCH: bad format (%d)", format);
+ dvmAbort();
+ }
+
+ const u1* dataStart = rawMap;
+
+ pStats->totalGcPointCount++;
+
+ /*
+ * Gather "gap size" stats, i.e. the difference in addresses between
+ * successive GC points.
+ */
+ if (prevData != NULL) {
+ assert(prevAddr >= 0);
+ int addrDiff = addr - prevAddr;
+
+ if (addrDiff < 0) {
+ LOGE("GLITCH: address went backward (0x%04x->0x%04x, %s.%s)\n",
+ prevAddr, addr, method->clazz->descriptor, method->name);
+ } else if (addrDiff > kMaxGcPointGap) {
+ if (REGISTER_MAP_VERBOSE) {
+ LOGI("HEY: addrDiff is %d, max %d (0x%04x->0x%04x %s.%s)\n",
+ addrDiff, kMaxGcPointGap, prevAddr, addr,
+ method->clazz->descriptor, method->name);
+ }
+ /* skip this one */
+ } else {
+ pStats->gcPointGap[addrDiff]++;
+ }
+ pStats->gcGapCount++;
+
+
+ /*
+ * Compare bit vectors in adjacent entries. We want to count
+ * up the number of bits that differ (to see if we frequently
+ * change 0 or 1 bits) and get a sense for which part of the
+ * vector changes the most often (near the start, middle, end).
+ *
+ * We only do the vector position quantization if we have at
+ * least 16 registers in the method.
+ */
+ int numDiff = 0;
+ float div = (float) kNumUpdatePosns / method->registersSize;
+ int regByte;
+ for (regByte = 0; regByte < pMap->regWidth; regByte++) {
+ int prev, cur, bit;
+
+ prev = prevData[regByte];
+ cur = dataStart[regByte];
+
+ for (bit = 0; bit < 8; bit++) {
+ if (((prev >> bit) & 1) != ((cur >> bit) & 1)) {
+ numDiff++;
+
+ int bitNum = regByte * 8 + bit;
+
+ if (bitNum < 16)
+ pStats->updateLT16++;
+ else
+ pStats->updateGE16++;
+
+ if (method->registersSize < 16)
+ continue;
+
+ if (bitNum >= method->registersSize) {
+ /* stuff off the end should be zero in both */
+ LOGE("WEIRD: bit=%d (%d/%d), prev=%02x cur=%02x\n",
+ bit, regByte, method->registersSize,
+ prev, cur);
+ assert(false);
+ }
+ int idx = (int) (bitNum * div);
+ if (!(idx >= 0 && idx < kNumUpdatePosns)) {
+ LOGE("FAIL: bitNum=%d (of %d) div=%.3f idx=%d\n",
+ bitNum, method->registersSize, div, idx);
+ assert(false);
+ }
+ pStats->updatePosn[idx]++;
+ }
+ }
+ }
+
+ if (numDiff > kMaxDiffBits) {
+ if (REGISTER_MAP_VERBOSE) {
+ LOGI("WOW: numDiff is %d, max %d\n", numDiff, kMaxDiffBits);
+ }
+ } else {
+ pStats->numDiffBits[numDiff]++;
+ }
+ }
+
+ /* advance to start of next line */
+ rawMap += pMap->regWidth;
+
+ prevAddr = addr;
+ prevData = dataStart;
+ }
+}
+#endif
+
+/*
+ * Compute the difference between two bit vectors.
+ *
+ * If "lebOutBuf" is non-NULL, we output the bit indices in ULEB128 format
+ * as we go. Otherwise, we just generate the various counts.
+ *
+ * The bit vectors are compared byte-by-byte, so any unused bits at the
+ * end must be zero.
+ *
+ * Returns the number of bytes required to hold the ULEB128 output.
+ *
+ * If "pFirstBitChanged" or "pNumBitsChanged" are non-NULL, they will
+ * receive the index of the first changed bit and the number of changed
+ * bits, respectively.
+ */
+static int computeBitDiff(const u1* bits1, const u1* bits2, int byteWidth,
+ int* pFirstBitChanged, int* pNumBitsChanged, u1* lebOutBuf)
+{
+ int numBitsChanged = 0;
+ int firstBitChanged = -1;
+ int lebSize = 0;
+ int byteNum;
+
+ /*
+ * Run through the vectors, first comparing them at the byte level. This
+ * will yield a fairly quick result if nothing has changed between them.
+ */
+ for (byteNum = 0; byteNum < byteWidth; byteNum++) {
+ u1 byte1 = *bits1++;
+ u1 byte2 = *bits2++;
+ if (byte1 != byte2) {
+ /*
+ * Walk through the byte, identifying the changed bits.
+ */
+ int bitNum;
+ for (bitNum = 0; bitNum < 8; bitNum++) {
+ if (((byte1 >> bitNum) & 0x01) != ((byte2 >> bitNum) & 0x01)) {
+ int bitOffset = (byteNum << 3) + bitNum;
+
+ if (firstBitChanged < 0)
+ firstBitChanged = bitOffset;
+ numBitsChanged++;
+
+ if (lebOutBuf == NULL) {
+ lebSize += unsignedLeb128Size(bitOffset);
+ } else {
+ u1* curBuf = lebOutBuf;
+ lebOutBuf = writeUnsignedLeb128(lebOutBuf, bitOffset);
+ lebSize += lebOutBuf - curBuf;
+ }
+ }
+ }
+ }
+ }
+
+ if (numBitsChanged > 0)
+ assert(firstBitChanged >= 0);
+
+ if (pFirstBitChanged != NULL)
+ *pFirstBitChanged = firstBitChanged;
+ if (pNumBitsChanged != NULL)
+ *pNumBitsChanged = numBitsChanged;
+
+ return lebSize;
+}
+
+/*
+ * Compress the register map with differential encoding.
+ *
+ * "meth" is only needed for debug output.
+ *
+ * On success, returns a newly-allocated RegisterMap. If the map is not
+ * compatible for some reason, or fails to get smaller, this will return NULL.
+ */
+static RegisterMap* compressMapDifferential(const RegisterMap* pMap,
+ const Method* meth)
+{
+ RegisterMap* pNewMap = NULL;
+ int origSize = computeRegisterMapSize(pMap);
+ u1* tmpBuf = NULL;
+ u1* tmpPtr;
+ int addrWidth, regWidth, numEntries;
+ bool debug = false;
+
+ if (false &&
+ strcmp(meth->clazz->descriptor, "Landroid/text/StaticLayout;") == 0 &&
+ strcmp(meth->name, "generate") == 0)
+ {
+ debug = true;
+ }
+
+ u1 format = dvmRegisterMapGetFormat(pMap);
+ switch (format) {
+ case kRegMapFormatCompact8:
+ addrWidth = 1;
+ break;
+ case kRegMapFormatCompact16:
+ addrWidth = 2;
+ break;
+ default:
+ LOGE("ERROR: can't compress map with format=%d\n", format);
+ goto bail;
+ }
+
+ regWidth = dvmRegisterMapGetRegWidth(pMap);
+ numEntries = dvmRegisterMapGetNumEntries(pMap);
+
+ if (debug) {
+ LOGI("COMPRESS: %s.%s aw=%d rw=%d ne=%d\n",
+ meth->clazz->descriptor, meth->name,
+ addrWidth, regWidth, numEntries);
+ dumpRegisterMap(pMap, -1);
+ }
+
+ if (numEntries <= 1) {
+ LOGV("Can't compress map with 0 or 1 entries\n");
+ goto bail;
+ }
+
+ /*
+ * We don't know how large the compressed data will be. It's possible
+ * for it to expand and become larger than the original. The header
+ * itself is variable-sized, so we generate everything into a temporary
+ * buffer and then copy it to form-fitting storage once we know how big
+ * it will be (and that it's smaller than the original).
+ *
+ * If we use a size that is equal to the size of the input map plus
+ * a value longer than a single entry can possibly expand to, we need
+ * only check for overflow at the end of each entry. The worst case
+ * for a single line is (1 + <ULEB8 address> + <full copy of vector>).
+ * Addresses are 16 bits, so that's (1 + 3 + regWidth).
+ *
+ * The initial address offset and bit vector will take up less than
+ * or equal to the amount of space required when uncompressed -- large
+ * initial offsets are rejected.
+ */
+ tmpBuf = (u1*) malloc(origSize + (1 + 3 + regWidth));
+ if (tmpBuf == NULL)
+ goto bail;
+
+ tmpPtr = tmpBuf;
+
+ const u1* mapData = pMap->data;
+ const u1* prevBits;
+ u2 addr, prevAddr;
+
+ addr = *mapData++;
+ if (addrWidth > 1)
+ addr |= (*mapData++) << 8;
+
+ if (addr >= 128) {
+ LOGV("Can't compress map with starting address >= 128\n");
+ goto bail;
+ }
+
+ /*
+ * Start by writing the initial address and bit vector data. The high
+ * bit of the initial address is used to indicate the required address
+ * width (which the decoder can't otherwise determine without parsing
+ * the compressed data).
+ */
+ *tmpPtr++ = addr | (addrWidth > 1 ? 0x80 : 0x00);
+ memcpy(tmpPtr, mapData, regWidth);
+
+ prevBits = mapData;
+ prevAddr = addr;
+
+ tmpPtr += regWidth;
+ mapData += regWidth;
+
+ /*
+ * Loop over all following entries.
+ */
+ int entry;
+ for (entry = 1; entry < numEntries; entry++) {
+ int addrDiff;
+ u1 key;
+
+ /*
+ * Pull out the address and figure out how to encode it.
+ */
+ addr = *mapData++;
+ if (addrWidth > 1)
+ addr |= (*mapData++) << 8;
+
+ if (debug)
+ LOGI(" addr=0x%04x ent=%d (aw=%d)\n", addr, entry, addrWidth);
+
+ addrDiff = addr - prevAddr;
+ assert(addrDiff > 0);
+ if (addrDiff < 8) {
+ /* small difference, encode in 3 bits */
+ key = addrDiff -1; /* set 00000AAA */
+ if (debug)
+ LOGI(" : small %d, key=0x%02x\n", addrDiff, key);
+ } else {
+ /* large difference, output escape code */
+ key = 0x07; /* escape code for AAA */
+ if (debug)
+ LOGI(" : large %d, key=0x%02x\n", addrDiff, key);
+ }
+
+ int numBitsChanged, firstBitChanged, lebSize;
+
+ lebSize = computeBitDiff(prevBits, mapData, regWidth,
+ &firstBitChanged, &numBitsChanged, NULL);
+
+ if (debug) {
+ LOGI(" : diff fbc=%d nbc=%d ls=%d (rw=%d)\n",
+ firstBitChanged, numBitsChanged, lebSize, regWidth);
+ }
+
+ if (numBitsChanged == 0) {
+ /* set B to 1 and CCCC to zero to indicate no bits were changed */
+ key |= 0x08;
+ if (debug) LOGI(" : no bits changed\n");
+ } else if (numBitsChanged == 1 && firstBitChanged < 16) {
+ /* set B to 0 and CCCC to the index of the changed bit */
+ key |= firstBitChanged << 4;
+ if (debug) LOGI(" : 1 low bit changed\n");
+ } else if (numBitsChanged < 15 && lebSize < regWidth) {
+ /* set B to 1 and CCCC to the number of bits */
+ key |= 0x08 | (numBitsChanged << 4);
+ if (debug) LOGI(" : some bits changed\n");
+ } else {
+ /* set B to 1 and CCCC to 0x0f so we store the entire vector */
+ key |= 0x08 | 0xf0;
+ if (debug) LOGI(" : encode original\n");
+ }
+
+ /*
+ * Encode output. Start with the key, follow with the address
+ * diff (if it didn't fit in 3 bits), then the changed bit info.
+ */
+ *tmpPtr++ = key;
+ if ((key & 0x07) == 0x07)
+ tmpPtr = writeUnsignedLeb128(tmpPtr, addrDiff);
+
+ if ((key & 0x08) != 0) {
+ int bitCount = key >> 4;
+ if (bitCount == 0) {
+ /* nothing changed, no additional output required */
+ } else if (bitCount == 15) {
+ /* full vector is most compact representation */
+ memcpy(tmpPtr, mapData, regWidth);
+ tmpPtr += regWidth;
+ } else {
+ /* write bit indices in LEB128 format */
+ (void) computeBitDiff(prevBits, mapData, regWidth,
+ NULL, NULL, tmpPtr);
+ tmpPtr += lebSize;
+ }
+ } else {
+ /* single-bit changed, value encoded in key byte */
+ }
+
+ prevBits = mapData;
+ prevAddr = addr;
+ mapData += regWidth;
+
+ /*
+ * See if we've run past the original size.
+ */
+ if (tmpPtr - tmpBuf >= origSize) {
+ if (debug) {
+ LOGD("Compressed size >= original (%d vs %d): %s.%s\n",
+ tmpPtr - tmpBuf, origSize,
+ meth->clazz->descriptor, meth->name);
+ }
+ goto bail;
+ }
+ }
+
+ /*
+ * Create a RegisterMap with the contents.
+ *
+ * TODO: consider using a threshold other than merely ">=". We would
+ * get poorer compression but potentially use less native heap space.
+ */
+ static const int kHeaderSize = offsetof(RegisterMap, data);
+ int newDataSize = tmpPtr - tmpBuf;
+ int newMapSize;
+
+ newMapSize = kHeaderSize + unsignedLeb128Size(newDataSize) + newDataSize;
+ if (newMapSize >= origSize) {
+ if (debug) {
+ LOGD("Final comp size >= original (%d vs %d): %s.%s\n",
+ newMapSize, origSize, meth->clazz->descriptor, meth->name);
+ }
+ goto bail;
+ }
+
+ pNewMap = (RegisterMap*) malloc(newMapSize);
+ if (pNewMap == NULL)
+ goto bail;
+ dvmRegisterMapSetFormat(pNewMap, kRegMapFormatDifferential);
+ dvmRegisterMapSetOnHeap(pNewMap, true);
+ dvmRegisterMapSetRegWidth(pNewMap, regWidth);
+ dvmRegisterMapSetNumEntries(pNewMap, numEntries);
+
+ tmpPtr = pNewMap->data;
+ tmpPtr = writeUnsignedLeb128(tmpPtr, newDataSize);
+ memcpy(tmpPtr, tmpBuf, newDataSize);
+
+ if (REGISTER_MAP_VERBOSE) {
+ LOGD("Compression successful (%d -> %d) from aw=%d rw=%d ne=%d\n",
+ computeRegisterMapSize(pMap), computeRegisterMapSize(pNewMap),
+ addrWidth, regWidth, numEntries);
+ }
+
+bail:
+ free(tmpBuf);
+ return pNewMap;
+}
+
+/*
+ * Toggle the value of the "idx"th bit in "ptr".
+ */
+static inline void toggleBit(u1* ptr, int idx)
+{
+ ptr[idx >> 3] ^= 1 << (idx & 0x07);
+}
+
+/*
+ * Expand a compressed map to an uncompressed form.
+ *
+ * Returns a newly-allocated RegisterMap on success, or NULL on failure.
+ *
+ * TODO: consider using the linear allocator or a custom allocator with
+ * LRU replacement for these instead of the native heap.
+ */
+static RegisterMap* uncompressMapDifferential(const RegisterMap* pMap)
+{
+ RegisterMap* pNewMap = NULL;
+ static const int kHeaderSize = offsetof(RegisterMap, data);
+ u1 format = dvmRegisterMapGetFormat(pMap);
+ RegisterMapFormat newFormat;
+ int regWidth, numEntries, newAddrWidth, newMapSize;
+
+ if (format != kRegMapFormatDifferential) {
+ LOGE("Not differential (%d)\n", format);
+ goto bail;
+ }
+
+ regWidth = dvmRegisterMapGetRegWidth(pMap);
+ numEntries = dvmRegisterMapGetNumEntries(pMap);
+
+ /* get the data size; we can check this at the end */
+ const u1* srcPtr = pMap->data;
+ int expectedSrcLen = readUnsignedLeb128(&srcPtr);
+ const u1* srcStart = srcPtr;
+
+ /* get the initial address and the 16-bit address flag */
+ int addr = *srcPtr & 0x7f;
+ if ((*srcPtr & 0x80) == 0) {
+ newFormat = kRegMapFormatCompact8;
+ newAddrWidth = 1;
+ } else {
+ newFormat = kRegMapFormatCompact16;
+ newAddrWidth = 2;
+ }
+ srcPtr++;
+
+ /* now we know enough to allocate the new map */
+ if (REGISTER_MAP_VERBOSE) {
+ LOGI("Expanding to map aw=%d rw=%d ne=%d\n",
+ newAddrWidth, regWidth, numEntries);
+ }
+ newMapSize = kHeaderSize + (newAddrWidth + regWidth) * numEntries;
+ pNewMap = (RegisterMap*) malloc(newMapSize);
+ if (pNewMap == NULL)
+ goto bail;
+
+ dvmRegisterMapSetFormat(pNewMap, newFormat);
+ dvmRegisterMapSetOnHeap(pNewMap, true);
+ dvmRegisterMapSetRegWidth(pNewMap, regWidth);
+ dvmRegisterMapSetNumEntries(pNewMap, numEntries);
+
+ /*
+ * Write the start address and initial bits to the new map.
+ */
+ u1* dstPtr = pNewMap->data;
+
+ *dstPtr++ = addr & 0xff;
+ if (newAddrWidth > 1)
+ *dstPtr++ = (u1) (addr >> 8);
+
+ memcpy(dstPtr, srcPtr, regWidth);
+
+ int prevAddr = addr;
+ const u1* prevBits = dstPtr; /* point at uncompressed data */
+
+ dstPtr += regWidth;
+ srcPtr += regWidth;
+
+ /*
+ * Walk through, uncompressing one line at a time.
+ */
+ int entry;
+ for (entry = 1; entry < numEntries; entry++) {
+ int addrDiff;
+ u1 key;
+
+ key = *srcPtr++;
+
+ /* get the address */
+ if ((key & 0x07) == 7) {
+ /* address diff follows in ULEB128 */
+ addrDiff = readUnsignedLeb128(&srcPtr);
+ } else {
+ addrDiff = (key & 0x07) +1;
+ }
+
+ addr = prevAddr + addrDiff;
+ *dstPtr++ = addr & 0xff;
+ if (newAddrWidth > 1)
+ *dstPtr++ = (u1) (addr >> 8);
+
+ /* unpack the bits */
+ if ((key & 0x08) != 0) {
+ int bitCount = (key >> 4);
+ if (bitCount == 0) {
+ /* no bits changed, just copy previous */
+ memcpy(dstPtr, prevBits, regWidth);
+ } else if (bitCount == 15) {
+ /* full copy of bit vector is present; ignore prevBits */
+ memcpy(dstPtr, srcPtr, regWidth);
+ srcPtr += regWidth;
+ } else {
+ /* copy previous bits and modify listed indices */
+ memcpy(dstPtr, prevBits, regWidth);
+ while (bitCount--) {
+ int bitIndex = readUnsignedLeb128(&srcPtr);
+ toggleBit(dstPtr, bitIndex);
+ }
+ }
+ } else {
+ /* copy previous bits and modify the specified one */
+ memcpy(dstPtr, prevBits, regWidth);
+
+ /* one bit, from 0-15 inclusive, was changed */
+ toggleBit(dstPtr, key >> 4);
+ }
+
+ prevAddr = addr;
+ prevBits = dstPtr;
+ dstPtr += regWidth;
+ }
+
+ if (dstPtr - (u1*) pNewMap != newMapSize) {
+ LOGE("ERROR: output %d bytes, expected %d\n",
+ dstPtr - (u1*) pNewMap, newMapSize);
+ goto bail;
+ }
+
+ if (srcPtr - srcStart != expectedSrcLen) {
+ LOGE("ERROR: consumed %d bytes, expected %d\n",
+ srcPtr - srcStart, expectedSrcLen);
+ goto bail;
+ }
+
+ if (REGISTER_MAP_VERBOSE) {
+ LOGD("Expansion successful (%d -> %d)\n",
+ computeRegisterMapSize(pMap), computeRegisterMapSize(pNewMap));
+ }
+
+ return pNewMap;
+
+bail:
+ free(pNewMap);
+ return NULL;
+}
+
+
+/*
+ * ===========================================================================
+ * Just-in-time generation
+ * ===========================================================================
+ */
+
+#if 0 /* incomplete implementation; may be removed entirely in the future */
+
+/*
+Notes on just-in-time RegisterMap generation
+
+Generating RegisterMap tables as part of verification is convenient because
+we generate most of what we need to know as part of doing the verify.
+The negative aspect of doing it this way is that we must store the
+result in the DEX file (if we're verifying ahead of time) or in memory
+(if verifying during class load) for every concrete non-native method,
+even if we never actually need the map during a GC.
+
+A simple but compact encoding of register map data increases the size of
+optimized DEX files by about 25%, so size considerations are important.
+
+We can instead generate the RegisterMap at the point where it is needed.
+In a typical application we only need to convert about 2% of the loaded
+methods, and we can generate type-precise roots reasonably quickly because
+(a) we know the method has already been verified and hence can make a
+lot of assumptions, and (b) we don't care what type of object a register
+holds, just whether or not it holds a reference, and hence can skip a
+lot of class resolution gymnastics.
+
+There are a couple of problems with this approach however. First, to
+get good performance we really want an implementation that is largely
+independent from the verifier, which means some duplication of effort.
+Second, we're dealing with post-dexopt code, which contains "quickened"
+instructions. We can't process those without either tracking type
+information (which slows us down) or storing additional data in the DEX
+file that allows us to reconstruct the original instructions (adds ~5%
+to the size of the ODEX).
+
+
+Implementation notes...
+
+Both type-precise and live-precise information can be generated knowing
+only whether or not a register holds a reference. We don't need to
+know what kind of reference or whether the object has been initialized.
+Not only can we skip many of the fancy steps in the verifier, we can
+initialize from simpler sources, e.g. the initial registers and return
+type are set from the "shorty" signature rather than the full signature.
+
+The short-term storage needs for just-in-time register map generation can
+be much lower because we can use a 1-byte SRegType instead of a 4-byte
+RegType. On the other hand, if we're not doing type-precise analysis
+in the verifier we only need to store register contents at every branch
+target, rather than every GC point (which are much more frequent).
+
+Whether it happens in the verifier or independently, because this is done
+with native heap allocations that may be difficult to return to the system,
+an effort should be made to minimize memory use.
+*/
+
+/*
+ * This is like RegType in the verifier, but simplified. It holds a value
+ * from the reg type enum, or kRegTypeReference.
+ */
+typedef u1 SRegType;
+#define kRegTypeReference kRegTypeMAX
+
+/*
+ * We need an extra "pseudo register" to hold the return type briefly. It
+ * can be category 1 or 2, so we need two slots.
+ */
+#define kExtraRegs 2
+#define RESULT_REGISTER(_insnRegCountPlus) (_insnRegCountPlus - kExtraRegs)
+
+/*
+ * Working state.
+ */
+typedef struct WorkState {
+ /*
+ * The method we're working on.
+ */
+ const Method* method;
+
+ /*
+ * Number of instructions in the method.
+ */
+ int insnsSize;
+
+ /*
+ * Number of registers we track for each instruction. This is equal
+ * to the method's declared "registersSize" plus kExtraRegs.
+ */
+ int insnRegCountPlus;
+
+ /*
+ * Instruction widths and flags, one entry per code unit.
+ */
+ InsnFlags* insnFlags;
+
+ /*
+ * Array of SRegType arrays, one entry per code unit. We only need
+ * to create an entry when an instruction starts at this address.
+ * We can further reduce this to instructions that are GC points.
+ *
+ * We could just go ahead and allocate one per code unit, but for
+ * larger methods that can represent a significant bit of short-term
+ * storage.
+ */
+ SRegType** addrRegs;
+
+ /*
+ * A single large alloc, with all of the storage needed for addrRegs.
+ */
+ SRegType* regAlloc;
+} WorkState;
+
+// fwd
+static bool generateMap(WorkState* pState, RegisterMap* pMap);
+static bool analyzeMethod(WorkState* pState);
+static bool handleInstruction(WorkState* pState, SRegType* workRegs,\
+ int insnIdx, int* pStartGuess);
+static void updateRegisters(WorkState* pState, int nextInsn,\
+ const SRegType* workRegs);
+
+
+/*
+ * Set instruction flags.
+ */
+static bool setInsnFlags(WorkState* pState, int* pGcPointCount)
+{
+ const Method* meth = pState->method;
+ InsnFlags* insnFlags = pState->insnFlags;
+ int insnsSize = pState->insnsSize;
+ const u2* insns = meth->insns;
+ int gcPointCount = 0;
+ int offset;
+
+ /* set the widths */
+ if (!dvmComputeCodeWidths(meth, pState->insnFlags, NULL))
+ return false;
+
+ /* mark "try" regions and exception handler branch targets */
+ if (!dvmSetTryFlags(meth, pState->insnFlags))
+ return false;
+
+ /* the start of the method is a "branch target" */
+ dvmInsnSetBranchTarget(insnFlags, 0, true);
+
+ /*
+ * Run through the instructions, looking for switches and branches.
+ * Mark their targets.
+ *
+ * We don't really need to "check" these instructions -- the verifier
+ * already did that -- but the additional overhead isn't significant
+ * enough to warrant making a second copy of the "Check" function.
+ *
+ * Mark and count GC points while we're at it.
+ */
+ for (offset = 0; offset < insnsSize; offset++) {
+ static int gcMask = kInstrCanBranch | kInstrCanSwitch |
+ kInstrCanThrow | kInstrCanReturn;
+ u1 opcode = insns[offset] & 0xff;
+ InstructionFlags opFlags = dexGetInstrFlags(gDvm.instrFlags, opcode);
+
+ if (opFlags & kInstrCanBranch) {
+ if (!dvmCheckBranchTarget(meth, insnFlags, offset, true))
+ return false;
+ }
+ if (opFlags & kInstrCanSwitch) {
+ if (!dvmCheckSwitchTargets(meth, insnFlags, offset))
+ return false;
+ }
+
+ if ((opFlags & gcMask) != 0) {
+ dvmInsnSetGcPoint(pState->insnFlags, offset, true);
+ gcPointCount++;
+ }
+ }
+
+ *pGcPointCount = gcPointCount;
+ return true;
+}
+
+/*
+ * Generate the register map for a method.
+ *
+ * Returns a pointer to newly-allocated storage.
+ */
+RegisterMap* dvmGenerateRegisterMap(const Method* meth)
+{
+ WorkState* pState = NULL;
+ RegisterMap* pMap = NULL;
+ RegisterMap* result = NULL;
+ SRegType* regPtr;
+
+ pState = (WorkState*) calloc(1, sizeof(WorkState));
+ if (pState == NULL)
+ goto bail;
+
+ pMap = (RegisterMap*) calloc(1, sizeof(RegisterMap));
+ if (pMap == NULL)
+ goto bail;
+
+ pState->method = meth;
+ pState->insnsSize = dvmGetMethodInsnsSize(meth);
+ pState->insnRegCountPlus = meth->registersSize + kExtraRegs;
+
+ pState->insnFlags = calloc(sizeof(InsnFlags), pState->insnsSize);
+ pState->addrRegs = calloc(sizeof(SRegType*), pState->insnsSize);
+
+ /*
+ * Set flags on instructions, and calculate the number of code units
+ * that happen to be GC points.
+ */
+ int gcPointCount;
+ if (!setInsnFlags(pState, &gcPointCount))
+ goto bail;
+
+ if (gcPointCount == 0) {
+ /* the method doesn't allocate or call, and never returns? unlikely */
+ LOG_VFY_METH(meth, "Found do-nothing method\n");
+ goto bail;
+ }
+
+ pState->regAlloc = (SRegType*)
+ calloc(sizeof(SRegType), pState->insnsSize * gcPointCount);
+ regPtr = pState->regAlloc;
+
+ /*
+ * For each instruction that is a GC point, set a pointer into the
+ * regAlloc buffer.
+ */
+ int offset;
+ for (offset = 0; offset < pState->insnsSize; offset++) {
+ if (dvmInsnIsGcPoint(pState->insnFlags, offset)) {
+ pState->addrRegs[offset] = regPtr;
+ regPtr += pState->insnRegCountPlus;
+ }
+ }
+ assert(regPtr - pState->regAlloc == pState->insnsSize * gcPointCount);
+ assert(pState->addrRegs[0] != NULL);
+
+ /*
+ * Compute the register map.
+ */
+ if (!generateMap(pState, pMap))
+ goto bail;
+
+ /* success */
+ result = pMap;
+ pMap = NULL;
+
+bail:
+ if (pState != NULL) {
+ free(pState->insnFlags);
+ free(pState->addrRegs);
+ free(pState->regAlloc);
+ free(pState);
+ }
+ if (pMap != NULL)
+ dvmFreeRegisterMap(pMap);
+ return result;
+}
+
+/*
+ * Release the storage associated with a RegisterMap.
+ */
+void dvmFreeRegisterMap(RegisterMap* pMap)
+{
+ if (pMap == NULL)
+ return;
+}
+
+
+/*
+ * Create the RegisterMap using the provided state.
+ */
+static bool generateMap(WorkState* pState, RegisterMap* pMap)
+{
+ bool result = false;
+
+ /*
+ * Analyze the method and store the results in WorkState.
+ */
+ if (!analyzeMethod(pState))
+ goto bail;
+
+ /*
+ * Convert the analyzed data into a RegisterMap.
+ */
+ // TODO
+
+ result = true;
+
+bail:
+ return result;
+}
+
+/*
+ * Set the register types for the method arguments. We can pull the values
+ * out of the "shorty" signature.
+ */
+static bool setTypesFromSignature(WorkState* pState)
+{
+ const Method* meth = pState->method;
+ int argReg = meth->registersSize - meth->insSize; /* first arg */
+ SRegType* pRegs = pState->addrRegs[0];
+ SRegType* pCurReg = &pRegs[argReg];
+ const char* ccp;
+
+ /*
+ * Include "this" pointer, if appropriate.
+ */
+ if (!dvmIsStaticMethod(meth)) {
+ *pCurReg++ = kRegTypeReference;
+ }
+
+ ccp = meth->shorty +1; /* skip first byte, which holds return type */
+ while (*ccp != 0) {
+ switch (*ccp) {
+ case 'L':
+ //case '[':
+ *pCurReg++ = kRegTypeReference;
+ break;
+ case 'Z':
+ *pCurReg++ = kRegTypeBoolean;
+ break;
+ case 'C':
+ *pCurReg++ = kRegTypeChar;
+ break;
+ case 'B':
+ *pCurReg++ = kRegTypeByte;
+ break;
+ case 'I':
+ *pCurReg++ = kRegTypeInteger;
+ break;
+ case 'S':
+ *pCurReg++ = kRegTypeShort;
+ break;
+ case 'F':
+ *pCurReg++ = kRegTypeFloat;
+ break;
+ case 'D':
+ *pCurReg++ = kRegTypeDoubleLo;
+ *pCurReg++ = kRegTypeDoubleHi;
+ break;
+ case 'J':
+ *pCurReg++ = kRegTypeLongLo;
+ *pCurReg++ = kRegTypeLongHi;
+ break;
+ default:
+ assert(false);
+ return false;
+ }
+ }
+
+ assert(pCurReg - pRegs == meth->insSize);
+ return true;
+}
+
+/*
+ * Find the start of the register set for the specified instruction in
+ * the current method.
+ */
+static inline SRegType* getRegisterLine(const WorkState* pState, int insnIdx)
+{
+ return pState->addrRegs[insnIdx];
+}
+
+/*
+ * Copy a set of registers.
+ */
+static inline void copyRegisters(SRegType* dst, const SRegType* src,
+ int numRegs)
+{
+ memcpy(dst, src, numRegs * sizeof(SRegType));
+}
+
+/*
+ * Compare a set of registers. Returns 0 if they match.
+ */
+static inline int compareRegisters(const SRegType* src1, const SRegType* src2,
+ int numRegs)
+{
+ return memcmp(src1, src2, numRegs * sizeof(SRegType));
+}
+
+/*
+ * Run through the instructions repeatedly until we have exercised all
+ * possible paths.
+ */
+static bool analyzeMethod(WorkState* pState)
+{
+ const Method* meth = pState->method;
+ SRegType workRegs[pState->insnRegCountPlus];
+ InsnFlags* insnFlags = pState->insnFlags;
+ int insnsSize = pState->insnsSize;
+ int insnIdx, startGuess;
+ bool result = false;
+
+ /*
+ * Initialize the types of the registers that correspond to method
+ * arguments.
+ */
+ if (!setTypesFromSignature(pState))
+ goto bail;
+
+ /*
+ * Mark the first instruction as "changed".
+ */
+ dvmInsnSetChanged(insnFlags, 0, true);
+ startGuess = 0;
+
+ if (true) {
+ IF_LOGI() {
+ char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+ LOGI("Now mapping: %s.%s %s (ins=%d regs=%d)\n",
+ meth->clazz->descriptor, meth->name, desc,
+ meth->insSize, meth->registersSize);
+ LOGI(" ------ [0 4 8 12 16 20 24 28 32 36\n");
+ free(desc);
+ }
+ }
+
+ /*
+ * Continue until no instructions are marked "changed".
+ */
+ while (true) {
+ /*
+ * Find the first marked one. Use "startGuess" as a way to find
+ * one quickly.
+ */
+ for (insnIdx = startGuess; insnIdx < insnsSize; insnIdx++) {
+ if (dvmInsnIsChanged(insnFlags, insnIdx))
+ break;
+ }
+
+ if (insnIdx == insnsSize) {
+ if (startGuess != 0) {
+ /* try again, starting from the top */
+ startGuess = 0;
+ continue;
+ } else {
+ /* all flags are clear */
+ break;
+ }
+ }
+
+ /*
+ * We carry the working set of registers from instruction to
+ * instruction. If this address can be the target of a branch
+ * (or throw) instruction, or if we're skipping around chasing
+ * "changed" flags, we need to load the set of registers from
+ * the table.
+ *
+ * Because we always prefer to continue on to the next instruction,
+ * we should never have a situation where we have a stray
+ * "changed" flag set on an instruction that isn't a branch target.
+ */
+ if (dvmInsnIsBranchTarget(insnFlags, insnIdx)) {
+ SRegType* insnRegs = getRegisterLine(pState, insnIdx);
+ assert(insnRegs != NULL);
+ copyRegisters(workRegs, insnRegs, pState->insnRegCountPlus);
+
+ } else {
+#ifndef NDEBUG
+ /*
+ * Sanity check: retrieve the stored register line (assuming
+ * a full table) and make sure it actually matches.
+ */
+ SRegType* insnRegs = getRegisterLine(pState, insnIdx);
+ if (insnRegs != NULL &&
+ compareRegisters(workRegs, insnRegs,
+ pState->insnRegCountPlus) != 0)
+ {
+ char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+ LOG_VFY("HUH? workRegs diverged in %s.%s %s\n",
+ meth->clazz->descriptor, meth->name, desc);
+ free(desc);
+ }
+#endif
+ }
+
+ /*
+ * Update the register sets altered by this instruction.
+ */
+ if (!handleInstruction(pState, workRegs, insnIdx, &startGuess)) {
+ goto bail;
+ }
+
+ dvmInsnSetVisited(insnFlags, insnIdx, true);
+ dvmInsnSetChanged(insnFlags, insnIdx, false);
+ }
+
+ // TODO - add dead code scan to help validate this code?
+
+ result = true;
+
+bail:
+ return result;
+}
+
+/*
+ * Get a pointer to the method being invoked.
+ *
+ * Returns NULL on failure.
+ */
+static Method* getInvokedMethod(const Method* meth,
+ const DecodedInstruction* pDecInsn, MethodType methodType)
+{
+ Method* resMethod;
+ char* sigOriginal = NULL;
+
+ /*
+ * Resolve the method. This could be an abstract or concrete method
+ * depending on what sort of call we're making.
+ */
+ if (methodType == METHOD_INTERFACE) {
+ resMethod = dvmOptResolveInterfaceMethod(meth->clazz, pDecInsn->vB);
+ } else {
+ resMethod = dvmOptResolveMethod(meth->clazz, pDecInsn->vB, methodType);
+ }
+ if (resMethod == NULL) {
+ /* failed; print a meaningful failure message */
+ DexFile* pDexFile = meth->clazz->pDvmDex->pDexFile;
+ const DexMethodId* pMethodId;
+ const char* methodName;
+ char* methodDesc;
+ const char* classDescriptor;
+
+ pMethodId = dexGetMethodId(pDexFile, pDecInsn->vB);
+ methodName = dexStringById(pDexFile, pMethodId->nameIdx);
+ methodDesc = dexCopyDescriptorFromMethodId(pDexFile, pMethodId);
+ classDescriptor = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
+
+ LOG_VFY("VFY: unable to resolve %s method %u: %s.%s %s\n",
+ dvmMethodTypeStr(methodType), pDecInsn->vB,
+ classDescriptor, methodName, methodDesc);
+ free(methodDesc);
+ return NULL;
+ }
+
+ return resMethod;
+}
+
+/*
+ * Return the register type for the method. Since we don't care about
+ * the actual type, we can just look at the "shorty" signature.
+ *
+ * Returns kRegTypeUnknown for "void".
+ */
+static SRegType getMethodReturnType(const Method* meth)
+{
+ SRegType type;
+
+ switch (meth->shorty[0]) {
+ case 'I':
+ type = kRegTypeInteger;
+ break;
+ case 'C':
+ type = kRegTypeChar;
+ break;
+ case 'S':
+ type = kRegTypeShort;
+ break;
+ case 'B':
+ type = kRegTypeByte;
+ break;
+ case 'Z':
+ type = kRegTypeBoolean;
+ break;
+ case 'V':
+ type = kRegTypeUnknown;
+ break;
+ case 'F':
+ type = kRegTypeFloat;
+ break;
+ case 'D':
+ type = kRegTypeDoubleLo;
+ break;
+ case 'J':
+ type = kRegTypeLongLo;
+ break;
+ case 'L':
+ //case '[':
+ type = kRegTypeReference;
+ break;
+ default:
+ /* we verified signature return type earlier, so this is impossible */
+ assert(false);
+ type = kRegTypeConflict;
+ break;
+ }
+
+ return type;
+}
+
+/*
+ * Copy a category 1 register.
+ */
+static inline void copyRegister1(SRegType* insnRegs, u4 vdst, u4 vsrc)
+{
+ insnRegs[vdst] = insnRegs[vsrc];
+}
+
+/*
+ * Copy a category 2 register. Note the source and destination may overlap.
+ */
+static inline void copyRegister2(SRegType* insnRegs, u4 vdst, u4 vsrc)
+{
+ //memmove(&insnRegs[vdst], &insnRegs[vsrc], sizeof(SRegType) * 2);
+ SRegType r1 = insnRegs[vsrc];
+ SRegType r2 = insnRegs[vsrc+1];
+ insnRegs[vdst] = r1;
+ insnRegs[vdst+1] = r2;
+}
+
+/*
+ * Set the type of a category 1 register.
+ */
+static inline void setRegisterType(SRegType* insnRegs, u4 vdst, SRegType type)
+{
+ insnRegs[vdst] = type;
+}
+
+/*
+ * Decode the specified instruction and update the register info.
+ */
+static bool handleInstruction(WorkState* pState, SRegType* workRegs,
+ int insnIdx, int* pStartGuess)
+{
+ const Method* meth = pState->method;
+ const u2* insns = meth->insns + insnIdx;
+ InsnFlags* insnFlags = pState->insnFlags;
+ bool result = false;
+
+ /*
+ * Once we finish decoding the instruction, we need to figure out where
+ * we can go from here. There are three possible ways to transfer
+ * control to another statement:
+ *
+ * (1) Continue to the next instruction. Applies to all but
+ * unconditional branches, method returns, and exception throws.
+ * (2) Branch to one or more possible locations. Applies to branches
+ * and switch statements.
+ * (3) Exception handlers. Applies to any instruction that can
+ * throw an exception that is handled by an encompassing "try"
+ * block. (We simplify this to be any instruction that can
+ * throw any exception.)
+ *
+ * We can also return, in which case there is no successor instruction
+ * from this point.
+ *
+ * The behavior can be determined from the InstrFlags.
+ */
+ DecodedInstruction decInsn;
+ SRegType entryRegs[pState->insnRegCountPlus];
+ const int insnRegCountPlus = pState->insnRegCountPlus;
+ bool justSetResult = false;
+ int branchTarget = 0;
+ SRegType tmpType;
+
+ dexDecodeInstruction(gDvm.instrFormat, insns, &decInsn);
+ const int nextFlags = dexGetInstrFlags(gDvm.instrFlags, decInsn.opCode);
+
+ /*
+ * Make a copy of the previous register state. If the instruction
+ * throws an exception, we merge *this* into the destination rather
+ * than workRegs, because we don't want the result from the "successful"
+ * code path (e.g. a check-cast that "improves" a type) to be visible
+ * to the exception handler.
+ */
+ if ((nextFlags & kInstrCanThrow) != 0 && dvmInsnIsInTry(insnFlags, insnIdx))
+ {
+ copyRegisters(entryRegs, workRegs, insnRegCountPlus);
+ }
+
+ switch (decInsn.opCode) {
+ case OP_NOP:
+ break;
+
+ case OP_MOVE:
+ case OP_MOVE_FROM16:
+ case OP_MOVE_16:
+ case OP_MOVE_OBJECT:
+ case OP_MOVE_OBJECT_FROM16:
+ case OP_MOVE_OBJECT_16:
+ copyRegister1(workRegs, decInsn.vA, decInsn.vB);
+ break;
+ case OP_MOVE_WIDE:
+ case OP_MOVE_WIDE_FROM16:
+ case OP_MOVE_WIDE_16:
+ copyRegister2(workRegs, decInsn.vA, decInsn.vB);
+ break;
+
+ /*
+ * The move-result instructions copy data out of a "pseudo-register"
+ * with the results from the last method invocation. In practice we
+ * might want to hold the result in an actual CPU register, so the
+ * Dalvik spec requires that these only appear immediately after an
+ * invoke or filled-new-array.
+ *
+ * These calls invalidate the "result" register. (This is now
+ * redundant with the reset done below, but it can make the debug info
+ * easier to read in some cases.)
+ */
+ case OP_MOVE_RESULT:
+ case OP_MOVE_RESULT_OBJECT:
+ copyRegister1(workRegs, decInsn.vA, RESULT_REGISTER(insnRegCountPlus));
+ break;
+ case OP_MOVE_RESULT_WIDE:
+ copyRegister2(workRegs, decInsn.vA, RESULT_REGISTER(insnRegCountPlus));
+ break;
+
+ case OP_MOVE_EXCEPTION:
+ /*
+ * This statement can only appear as the first instruction in an
+ * exception handler (though not all exception handlers need to
+ * have one of these). We verify that as part of extracting the
+ * exception type from the catch block list.
+ */
+ setRegisterType(workRegs, decInsn.vA, kRegTypeReference);
+ break;
+
+ case OP_RETURN_VOID:
+ case OP_RETURN:
+ case OP_RETURN_WIDE:
+ case OP_RETURN_OBJECT:
+ break;
+
+ case OP_CONST_4:
+ case OP_CONST_16:
+ case OP_CONST:
+ /* could be boolean, int, float, or a null reference */
+ setRegisterType(workRegs, decInsn.vA,
+ dvmDetermineCat1Const((s4)decInsn.vB));
+ break;
+ case OP_CONST_HIGH16:
+ /* could be boolean, int, float, or a null reference */
+ setRegisterType(workRegs, decInsn.vA,
+ dvmDetermineCat1Const((s4) decInsn.vB << 16));
+ break;
+ case OP_CONST_WIDE_16:
+ case OP_CONST_WIDE_32:
+ case OP_CONST_WIDE:
+ case OP_CONST_WIDE_HIGH16:
+ /* could be long or double; default to long and allow conversion */
+ setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo);
+ break;
+ case OP_CONST_STRING:
+ case OP_CONST_STRING_JUMBO:
+ case OP_CONST_CLASS:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeReference);
+ break;
+
+ case OP_MONITOR_ENTER:
+ case OP_MONITOR_EXIT:
+ break;
+
+ case OP_CHECK_CAST:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeReference);
+ break;
+ case OP_INSTANCE_OF:
+ /* result is boolean */
+ setRegisterType(workRegs, decInsn.vA, kRegTypeBoolean);
+ break;
+
+ case OP_ARRAY_LENGTH:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeInteger);
+ break;
+
+ case OP_NEW_INSTANCE:
+ case OP_NEW_ARRAY:
+ /* add the new uninitialized reference to the register ste */
+ setRegisterType(workRegs, decInsn.vA, kRegTypeReference);
+ break;
+ case OP_FILLED_NEW_ARRAY:
+ case OP_FILLED_NEW_ARRAY_RANGE:
+ setRegisterType(workRegs, RESULT_REGISTER(insnRegCountPlus),
+ kRegTypeReference);
+ justSetResult = true;
+ break;
+
+ case OP_CMPL_FLOAT:
+ case OP_CMPG_FLOAT:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeBoolean);
+ break;
+ case OP_CMPL_DOUBLE:
+ case OP_CMPG_DOUBLE:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeBoolean);
+ break;
+ case OP_CMP_LONG:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeBoolean);
+ break;
+
+ case OP_THROW:
+ case OP_GOTO:
+ case OP_GOTO_16:
+ case OP_GOTO_32:
+ case OP_PACKED_SWITCH:
+ case OP_SPARSE_SWITCH:
+ break;
+
+ case OP_FILL_ARRAY_DATA:
+ break;
+
+ case OP_IF_EQ:
+ case OP_IF_NE:
+ case OP_IF_LT:
+ case OP_IF_GE:
+ case OP_IF_GT:
+ case OP_IF_LE:
+ case OP_IF_EQZ:
+ case OP_IF_NEZ:
+ case OP_IF_LTZ:
+ case OP_IF_GEZ:
+ case OP_IF_GTZ:
+ case OP_IF_LEZ:
+ break;
+
+ case OP_AGET:
+ tmpType = kRegTypeInteger;
+ goto aget_1nr_common;
+ case OP_AGET_BOOLEAN:
+ tmpType = kRegTypeBoolean;
+ goto aget_1nr_common;
+ case OP_AGET_BYTE:
+ tmpType = kRegTypeByte;
+ goto aget_1nr_common;
+ case OP_AGET_CHAR:
+ tmpType = kRegTypeChar;
+ goto aget_1nr_common;
+ case OP_AGET_SHORT:
+ tmpType = kRegTypeShort;
+ goto aget_1nr_common;
+aget_1nr_common:
+ setRegisterType(workRegs, decInsn.vA, tmpType);
+ break;
+
+ case OP_AGET_WIDE:
+ /*
+ * We know this is either long or double, and we don't really
+ * discriminate between those during verification, so we
+ * call it a long.
+ */
+ setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo);
+ break;
+
+ case OP_AGET_OBJECT:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeReference);
+ break;
+
+ case OP_APUT:
+ case OP_APUT_BOOLEAN:
+ case OP_APUT_BYTE:
+ case OP_APUT_CHAR:
+ case OP_APUT_SHORT:
+ case OP_APUT_WIDE:
+ case OP_APUT_OBJECT:
+ break;
+
+ case OP_IGET:
+ tmpType = kRegTypeInteger;
+ goto iget_1nr_common;
+ case OP_IGET_BOOLEAN:
+ tmpType = kRegTypeBoolean;
+ goto iget_1nr_common;
+ case OP_IGET_BYTE:
+ tmpType = kRegTypeByte;
+ goto iget_1nr_common;
+ case OP_IGET_CHAR:
+ tmpType = kRegTypeChar;
+ goto iget_1nr_common;
+ case OP_IGET_SHORT:
+ tmpType = kRegTypeShort;
+ goto iget_1nr_common;
+iget_1nr_common:
+ setRegisterType(workRegs, decInsn.vA, tmpType);
+ break;
+
+ case OP_IGET_WIDE:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo);
+ break;
+
+ case OP_IGET_OBJECT:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeReference);
+ break;
+
+ case OP_IPUT:
+ case OP_IPUT_BOOLEAN:
+ case OP_IPUT_BYTE:
+ case OP_IPUT_CHAR:
+ case OP_IPUT_SHORT:
+ case OP_IPUT_WIDE:
+ case OP_IPUT_OBJECT:
+ break;
+
+ case OP_SGET:
+ tmpType = kRegTypeInteger;
+ goto sget_1nr_common;
+ case OP_SGET_BOOLEAN:
+ tmpType = kRegTypeBoolean;
+ goto sget_1nr_common;
+ case OP_SGET_BYTE:
+ tmpType = kRegTypeByte;
+ goto sget_1nr_common;
+ case OP_SGET_CHAR:
+ tmpType = kRegTypeChar;
+ goto sget_1nr_common;
+ case OP_SGET_SHORT:
+ tmpType = kRegTypeShort;
+ goto sget_1nr_common;
+sget_1nr_common:
+ setRegisterType(workRegs, decInsn.vA, tmpType);
+ break;
+
+ case OP_SGET_WIDE:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo);
+ break;
+
+ case OP_SGET_OBJECT:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeReference);
+ break;
+
+ case OP_SPUT:
+ case OP_SPUT_BOOLEAN:
+ case OP_SPUT_BYTE:
+ case OP_SPUT_CHAR:
+ case OP_SPUT_SHORT:
+ case OP_SPUT_WIDE:
+ case OP_SPUT_OBJECT:
+ break;
+
+ case OP_INVOKE_VIRTUAL:
+ case OP_INVOKE_VIRTUAL_RANGE:
+ case OP_INVOKE_SUPER:
+ case OP_INVOKE_SUPER_RANGE:
+ {
+ Method* calledMethod;
+
+ calledMethod = getInvokedMethod(meth, &decInsn, METHOD_VIRTUAL);
+ if (calledMethod == NULL)
+ goto bail;
+ setRegisterType(workRegs, RESULT_REGISTER(insnRegCountPlus),
+ getMethodReturnType(calledMethod));
+ justSetResult = true;
+ }
+ break;
+ case OP_INVOKE_DIRECT:
+ case OP_INVOKE_DIRECT_RANGE:
+ {
+ Method* calledMethod;
+
+ calledMethod = getInvokedMethod(meth, &decInsn, METHOD_DIRECT);
+ if (calledMethod == NULL)
+ goto bail;
+ setRegisterType(workRegs, RESULT_REGISTER(insnRegCountPlus),
+ getMethodReturnType(calledMethod));
+ justSetResult = true;
+ }
+ break;
+ case OP_INVOKE_STATIC:
+ case OP_INVOKE_STATIC_RANGE:
+ {
+ Method* calledMethod;
+
+ calledMethod = getInvokedMethod(meth, &decInsn, METHOD_STATIC);
+ if (calledMethod == NULL)
+ goto bail;
+ setRegisterType(workRegs, RESULT_REGISTER(insnRegCountPlus),
+ getMethodReturnType(calledMethod));
+ justSetResult = true;
+ }
+ break;
+ case OP_INVOKE_INTERFACE:
+ case OP_INVOKE_INTERFACE_RANGE:
+ {
+ Method* absMethod;
+
+ absMethod = getInvokedMethod(meth, &decInsn, METHOD_INTERFACE);
+ if (absMethod == NULL)
+ goto bail;
+ setRegisterType(workRegs, RESULT_REGISTER(insnRegCountPlus),
+ getMethodReturnType(absMethod));
+ justSetResult = true;
+ }
+ break;
+
+ case OP_NEG_INT:
+ case OP_NOT_INT:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeInteger);
+ break;
+ case OP_NEG_LONG:
+ case OP_NOT_LONG:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo);
+ break;
+ case OP_NEG_FLOAT:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeFloat);
+ break;
+ case OP_NEG_DOUBLE:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeDoubleLo);
+ break;
+ case OP_INT_TO_LONG:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo);
+ break;
+ case OP_INT_TO_FLOAT:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeFloat);
+ break;
+ case OP_INT_TO_DOUBLE:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeDoubleLo);
+ break;
+ case OP_LONG_TO_INT:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeInteger);
+ break;
+ case OP_LONG_TO_FLOAT:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeFloat);
+ break;
+ case OP_LONG_TO_DOUBLE:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeDoubleLo);
+ break;
+ case OP_FLOAT_TO_INT:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeInteger);
+ break;
+ case OP_FLOAT_TO_LONG:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo);
+ break;
+ case OP_FLOAT_TO_DOUBLE:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeDoubleLo);
+ break;
+ case OP_DOUBLE_TO_INT:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeInteger);
+ break;
+ case OP_DOUBLE_TO_LONG:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo);
+ break;
+ case OP_DOUBLE_TO_FLOAT:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeFloat);
+ break;
+ case OP_INT_TO_BYTE:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeByte);
+ break;
+ case OP_INT_TO_CHAR:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeChar);
+ break;
+ case OP_INT_TO_SHORT:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeShort);
+ break;
+
+ case OP_ADD_INT:
+ case OP_SUB_INT:
+ case OP_MUL_INT:
+ case OP_REM_INT:
+ case OP_DIV_INT:
+ case OP_SHL_INT:
+ case OP_SHR_INT:
+ case OP_USHR_INT:
+ case OP_AND_INT:
+ case OP_OR_INT:
+ case OP_XOR_INT:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeInteger);
+ break;
+ case OP_ADD_LONG:
+ case OP_SUB_LONG:
+ case OP_MUL_LONG:
+ case OP_DIV_LONG:
+ case OP_REM_LONG:
+ case OP_AND_LONG:
+ case OP_OR_LONG:
+ case OP_XOR_LONG:
+ case OP_SHL_LONG:
+ case OP_SHR_LONG:
+ case OP_USHR_LONG:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo);
+ break;
+ case OP_ADD_FLOAT:
+ case OP_SUB_FLOAT:
+ case OP_MUL_FLOAT:
+ case OP_DIV_FLOAT:
+ case OP_REM_FLOAT:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeFloat);
+ break;
+ case OP_ADD_DOUBLE:
+ case OP_SUB_DOUBLE:
+ case OP_MUL_DOUBLE:
+ case OP_DIV_DOUBLE:
+ case OP_REM_DOUBLE:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeDoubleLo);
+ break;
+ case OP_ADD_INT_2ADDR:
+ case OP_SUB_INT_2ADDR:
+ case OP_MUL_INT_2ADDR:
+ case OP_REM_INT_2ADDR:
+ case OP_SHL_INT_2ADDR:
+ case OP_SHR_INT_2ADDR:
+ case OP_USHR_INT_2ADDR:
+ case OP_AND_INT_2ADDR:
+ case OP_OR_INT_2ADDR:
+ case OP_XOR_INT_2ADDR:
+ case OP_DIV_INT_2ADDR:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeInteger);
+ break;
+ case OP_ADD_LONG_2ADDR:
+ case OP_SUB_LONG_2ADDR:
+ case OP_MUL_LONG_2ADDR:
+ case OP_DIV_LONG_2ADDR:
+ case OP_REM_LONG_2ADDR:
+ case OP_AND_LONG_2ADDR:
+ case OP_OR_LONG_2ADDR:
+ case OP_XOR_LONG_2ADDR:
+ case OP_SHL_LONG_2ADDR:
+ case OP_SHR_LONG_2ADDR:
+ case OP_USHR_LONG_2ADDR:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo);
+ break;
+ case OP_ADD_FLOAT_2ADDR:
+ case OP_SUB_FLOAT_2ADDR:
+ case OP_MUL_FLOAT_2ADDR:
+ case OP_DIV_FLOAT_2ADDR:
+ case OP_REM_FLOAT_2ADDR:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeFloat);
+ break;
+ case OP_ADD_DOUBLE_2ADDR:
+ case OP_SUB_DOUBLE_2ADDR:
+ case OP_MUL_DOUBLE_2ADDR:
+ case OP_DIV_DOUBLE_2ADDR:
+ case OP_REM_DOUBLE_2ADDR:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeDoubleLo);
+ break;
+ case OP_ADD_INT_LIT16:
+ case OP_RSUB_INT:
+ case OP_MUL_INT_LIT16:
+ case OP_DIV_INT_LIT16:
+ case OP_REM_INT_LIT16:
+ case OP_AND_INT_LIT16:
+ case OP_OR_INT_LIT16:
+ case OP_XOR_INT_LIT16:
+ case OP_ADD_INT_LIT8:
+ case OP_RSUB_INT_LIT8:
+ case OP_MUL_INT_LIT8:
+ case OP_DIV_INT_LIT8:
+ case OP_REM_INT_LIT8:
+ case OP_SHL_INT_LIT8:
+ case OP_SHR_INT_LIT8:
+ case OP_USHR_INT_LIT8:
+ case OP_AND_INT_LIT8:
+ case OP_OR_INT_LIT8:
+ case OP_XOR_INT_LIT8:
+ setRegisterType(workRegs, decInsn.vA, kRegTypeInteger);
+ break;
+
+
+ /*
+ * See comments in analysis/CodeVerify.c re: why some of these are
+ * annoying to deal with. It's worse in this implementation, because
+ * we're not keeping any information about the classes held in each
+ * reference register.
+ *
+ * Handling most of these would require retaining the field/method
+ * reference info that we discarded when the instructions were
+ * quickened. This is feasible but not currently supported.
+ */
+ case OP_EXECUTE_INLINE:
+ case OP_EXECUTE_INLINE_RANGE:
+ case OP_INVOKE_DIRECT_EMPTY:
+ case OP_IGET_QUICK:
+ case OP_IGET_WIDE_QUICK:
+ case OP_IGET_OBJECT_QUICK:
+ case OP_IPUT_QUICK:
+ case OP_IPUT_WIDE_QUICK:
+ case OP_IPUT_OBJECT_QUICK:
+ case OP_IGET_WIDE_VOLATILE:
+ case OP_IPUT_WIDE_VOLATILE:
+ case OP_SGET_WIDE_VOLATILE:
+ case OP_SPUT_WIDE_VOLATILE:
+ case OP_INVOKE_VIRTUAL_QUICK:
+ case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+ case OP_INVOKE_SUPER_QUICK:
+ case OP_INVOKE_SUPER_QUICK_RANGE:
+ dvmAbort(); // not implemented, shouldn't be here
+ break;
+
+
+ /* these should never appear */
+ case OP_UNUSED_3E:
+ case OP_UNUSED_3F:
+ case OP_UNUSED_40:
+ case OP_UNUSED_41:
+ case OP_UNUSED_42:
+ case OP_UNUSED_43:
+ case OP_UNUSED_73:
+ case OP_UNUSED_79:
+ case OP_UNUSED_7A:
+ case OP_UNUSED_E3:
+ case OP_UNUSED_E4:
+ case OP_UNUSED_E5:
+ case OP_UNUSED_E6:
+ case OP_UNUSED_E7:
+ case OP_BREAKPOINT:
+ case OP_UNUSED_ED:
+ case OP_UNUSED_F1:
+ case OP_UNUSED_FC:
+ case OP_UNUSED_FD:
+ case OP_UNUSED_FE:
+ case OP_UNUSED_FF:
+ dvmAbort();
+ break;
+
+ /*
+ * DO NOT add a "default" clause here. Without it the compiler will
+ * complain if an instruction is missing (which is desirable).
+ */
+ }
+
+
+ /*
+ * If we didn't just set the result register, clear it out. This
+ * isn't so important here, but does help ensure that our output matches
+ * the verifier.
+ */
+ if (!justSetResult) {
+ int reg = RESULT_REGISTER(pState->insnRegCountPlus);
+ workRegs[reg] = workRegs[reg+1] = kRegTypeUnknown;
+ }
+
+ /*
+ * Handle "continue". Tag the next consecutive instruction.
+ */
+ if ((nextFlags & kInstrCanContinue) != 0) {
+ int insnWidth = dvmInsnGetWidth(insnFlags, insnIdx);
+
+ /*
+ * We want to update the registers and set the "changed" flag on the
+ * next instruction (if necessary). We aren't storing register
+ * changes for all addresses, so for non-GC-point targets we just
+ * compare "entry" vs. "work" to see if we've changed anything.
+ */
+ if (getRegisterLine(pState, insnIdx+insnWidth) != NULL) {
+ updateRegisters(pState, insnIdx+insnWidth, workRegs);
+ } else {
+ /* if not yet visited, or regs were updated, set "changed" */
+ if (!dvmInsnIsVisited(insnFlags, insnIdx+insnWidth) ||
+ compareRegisters(workRegs, entryRegs,
+ pState->insnRegCountPlus) != 0)
+ {
+ dvmInsnSetChanged(insnFlags, insnIdx+insnWidth, true);
+ }
+ }
+ }
+
+ /*
+ * Handle "branch". Tag the branch target.
+ */
+ if ((nextFlags & kInstrCanBranch) != 0) {
+ bool isConditional;
+
+ dvmGetBranchTarget(meth, insnFlags, insnIdx, &branchTarget,
+ &isConditional);
+ assert(isConditional || (nextFlags & kInstrCanContinue) == 0);
+ assert(!isConditional || (nextFlags & kInstrCanContinue) != 0);
+
+ updateRegisters(pState, insnIdx+branchTarget, workRegs);
+ }
+
+ /*
+ * Handle "switch". Tag all possible branch targets.
+ */
+ if ((nextFlags & kInstrCanSwitch) != 0) {
+ int offsetToSwitch = insns[1] | (((s4)insns[2]) << 16);
+ const u2* switchInsns = insns + offsetToSwitch;
+ int switchCount = switchInsns[1];
+ int offsetToTargets, targ;
+
+ if ((*insns & 0xff) == OP_PACKED_SWITCH) {
+ /* 0=sig, 1=count, 2/3=firstKey */
+ offsetToTargets = 4;
+ } else {
+ /* 0=sig, 1=count, 2..count*2 = keys */
+ assert((*insns & 0xff) == OP_SPARSE_SWITCH);
+ offsetToTargets = 2 + 2*switchCount;
+ }
+
+ /* verify each switch target */
+ for (targ = 0; targ < switchCount; targ++) {
+ int offset, absOffset;
+
+ /* offsets are 32-bit, and only partly endian-swapped */
+ offset = switchInsns[offsetToTargets + targ*2] |
+ (((s4) switchInsns[offsetToTargets + targ*2 +1]) << 16);
+ absOffset = insnIdx + offset;
+ assert(absOffset >= 0 && absOffset < pState->insnsSize);
+
+ updateRegisters(pState, absOffset, workRegs);
+ }
+ }
+
+ /*
+ * Handle instructions that can throw and that are sitting in a
+ * "try" block. (If they're not in a "try" block when they throw,
+ * control transfers out of the method.)
+ */
+ if ((nextFlags & kInstrCanThrow) != 0 && dvmInsnIsInTry(insnFlags, insnIdx))
+ {
+ DexFile* pDexFile = meth->clazz->pDvmDex->pDexFile;
+ const DexCode* pCode = dvmGetMethodCode(meth);
+ DexCatchIterator iterator;
+
+ if (dexFindCatchHandler(&iterator, pCode, insnIdx)) {
+ while (true) {
+ DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
+ if (handler == NULL)
+ break;
+
+ /* note we use entryRegs, not workRegs */
+ updateRegisters(pState, handler->address, entryRegs);
+ }
+ }
+ }
+
+ /*
+ * Update startGuess. Advance to the next instruction of that's
+ * possible, otherwise use the branch target if one was found. If
+ * neither of those exists we're in a return or throw; leave startGuess
+ * alone and let the caller sort it out.
+ */
+ if ((nextFlags & kInstrCanContinue) != 0) {
+ *pStartGuess = insnIdx + dvmInsnGetWidth(insnFlags, insnIdx);
+ } else if ((nextFlags & kInstrCanBranch) != 0) {
+ /* we're still okay if branchTarget is zero */
+ *pStartGuess = insnIdx + branchTarget;
+ }
+
+ assert(*pStartGuess >= 0 && *pStartGuess < pState->insnsSize &&
+ dvmInsnGetWidth(insnFlags, *pStartGuess) != 0);
+
+ result = true;
+
+bail:
+ return result;
+}
+
+
+/*
+ * Merge two SRegType values.
+ *
+ * Sets "*pChanged" to "true" if the result doesn't match "type1".
+ */
+static SRegType mergeTypes(SRegType type1, SRegType type2, bool* pChanged)
+{
+ SRegType result;
+
+ /*
+ * Check for trivial case so we don't have to hit memory.
+ */
+ if (type1 == type2)
+ return type1;
+
+ /*
+ * Use the table if we can, and reject any attempts to merge something
+ * from the table with a reference type.
+ *
+ * The uninitialized table entry at index zero *will* show up as a
+ * simple kRegTypeUninit value. Since this cannot be merged with
+ * anything but itself, the rules do the right thing.
+ */
+ if (type1 < kRegTypeMAX) {
+ if (type2 < kRegTypeMAX) {
+ result = gDvmMergeTab[type1][type2];
+ } else {
+ /* simple + reference == conflict, usually */
+ if (type1 == kRegTypeZero)
+ result = type2;
+ else
+ result = kRegTypeConflict;
+ }
+ } else {
+ if (type2 < kRegTypeMAX) {
+ /* reference + simple == conflict, usually */
+ if (type2 == kRegTypeZero)
+ result = type1;
+ else
+ result = kRegTypeConflict;
+ } else {
+ /* merging two references */
+ assert(type1 == type2);
+ result = type1;
+ }
+ }
+
+ if (result != type1)
+ *pChanged = true;
+ return result;
+}
+
+/*
+ * Control can transfer to "nextInsn".
+ *
+ * Merge the registers from "workRegs" into "addrRegs" at "nextInsn", and
+ * set the "changed" flag on the target address if the registers have changed.
+ */
+static void updateRegisters(WorkState* pState, int nextInsn,
+ const SRegType* workRegs)
+{
+ const Method* meth = pState->method;
+ InsnFlags* insnFlags = pState->insnFlags;
+ const int insnRegCountPlus = pState->insnRegCountPlus;
+ SRegType* targetRegs = getRegisterLine(pState, nextInsn);
+
+ if (!dvmInsnIsVisitedOrChanged(insnFlags, nextInsn)) {
+ /*
+ * We haven't processed this instruction before, and we haven't
+ * touched the registers here, so there's nothing to "merge". Copy
+ * the registers over and mark it as changed. (This is the only
+ * way a register can transition out of "unknown", so this is not
+ * just an optimization.)
+ */
+ LOGVV("COPY into 0x%04x\n", nextInsn);
+ copyRegisters(targetRegs, workRegs, insnRegCountPlus);
+ dvmInsnSetChanged(insnFlags, nextInsn, true);
+ } else {
+ /* merge registers, set Changed only if different */
+ LOGVV("MERGE into 0x%04x\n", nextInsn);
+ bool changed = false;
+ int i;
+
+ for (i = 0; i < insnRegCountPlus; i++) {
+ targetRegs[i] = mergeTypes(targetRegs[i], workRegs[i], &changed);
+ }
+
+ if (changed)
+ dvmInsnSetChanged(insnFlags, nextInsn, true);
+ }
+}
+
+#endif /*#if 0*/
diff --git a/vm/analysis/RegisterMap.h b/vm/analysis/RegisterMap.h
new file mode 100644
index 0000000..7897d45
--- /dev/null
+++ b/vm/analysis/RegisterMap.h
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Declaration of register map data structure and related functions.
+ *
+ * These structures should be treated as opaque through most of the VM.
+ */
+#ifndef _DALVIK_REGISTERMAP
+#define _DALVIK_REGISTERMAP
+
+#include "analysis/VerifySubs.h"
+#include "analysis/CodeVerify.h"
+
+/*
+ * Format enumeration for RegisterMap data area.
+ */
+typedef enum RegisterMapFormat {
+ kRegMapFormatUnknown = 0,
+ kRegMapFormatNone, /* indicates no map data follows */
+ kRegMapFormatCompact8, /* compact layout, 8-bit addresses */
+ kRegMapFormatCompact16, /* compact layout, 16-bit addresses */
+ kRegMapFormatDifferential, /* compressed, differential encoding */
+
+ kRegMapFormatOnHeap = 0x80, /* bit flag, indicates allocation on heap */
+} RegisterMapFormat;
+
+/*
+ * This is a single variable-size structure. It may be allocated on the
+ * heap or mapped out of a (post-dexopt) DEX file.
+ *
+ * 32-bit alignment of the structure is NOT guaranteed. This makes it a
+ * little awkward to deal with as a structure; to avoid accidents we use
+ * only byte types. Multi-byte values are little-endian.
+ *
+ * Size of (format==FormatNone): 1 byte
+ * Size of (format==FormatCompact8): 4 + (1 + regWidth) * numEntries
+ * Size of (format==FormatCompact16): 4 + (2 + regWidth) * numEntries
+ */
+struct RegisterMap {
+ /* header */
+ u1 format; /* enum RegisterMapFormat; MUST be first entry */
+ u1 regWidth; /* bytes per register line, 1+ */
+ u1 numEntries[2]; /* number of entries */
+
+ /* raw data starts here; need not be aligned */
+ u1 data[1];
+};
+
+bool dvmRegisterMapStartup(void);
+void dvmRegisterMapShutdown(void);
+
+/*
+ * Get the format.
+ */
+INLINE RegisterMapFormat dvmRegisterMapGetFormat(const RegisterMap* pMap) {
+ return pMap->format & ~(kRegMapFormatOnHeap);
+}
+
+/*
+ * Set the format.
+ */
+INLINE void dvmRegisterMapSetFormat(RegisterMap* pMap, RegisterMapFormat format)
+{
+ pMap->format &= kRegMapFormatOnHeap;
+ pMap->format |= format;
+}
+
+/*
+ * Get the "on heap" flag.
+ */
+INLINE bool dvmRegisterMapGetOnHeap(const RegisterMap* pMap) {
+ return (pMap->format & kRegMapFormatOnHeap) != 0;
+}
+
+/*
+ * Get the register bit vector width, in bytes.
+ */
+INLINE u1 dvmRegisterMapGetRegWidth(const RegisterMap* pMap) {
+ return pMap->regWidth;
+}
+
+/*
+ * Set the register bit vector width, in bytes.
+ */
+INLINE void dvmRegisterMapSetRegWidth(RegisterMap* pMap, int regWidth) {
+ pMap->regWidth = regWidth;
+}
+
+/*
+ * Set the "on heap" flag.
+ */
+INLINE void dvmRegisterMapSetOnHeap(RegisterMap* pMap, bool val) {
+ if (val)
+ pMap->format |= kRegMapFormatOnHeap;
+ else
+ pMap->format &= ~(kRegMapFormatOnHeap);
+}
+
+/*
+ * Get the number of entries in this map.
+ */
+INLINE u2 dvmRegisterMapGetNumEntries(const RegisterMap* pMap) {
+ return pMap->numEntries[0] | (pMap->numEntries[1] << 8);
+}
+
+/*
+ * Set the number of entries in this map.
+ */
+INLINE void dvmRegisterMapSetNumEntries(RegisterMap* pMap, u2 numEntries) {
+ pMap->numEntries[0] = (u1) numEntries;
+ pMap->numEntries[1] = numEntries >> 8;
+}
+
+/*
+ * Retrieve the bit vector for the specified address. This is a pointer
+ * to the bit data from an uncompressed map, or to a temporary copy of
+ * data from a compressed map.
+ *
+ * The caller must call dvmReleaseRegisterMapLine() with the result.
+ *
+ * Returns NULL if not found.
+ */
+const u1* dvmRegisterMapGetLine(const RegisterMap* pMap, int addr);
+
+/*
+ * Release "data".
+ *
+ * If "pMap" points to a compressed map from which we have expanded a
+ * single line onto the heap, this will free "data"; otherwise, it does
+ * nothing.
+ *
+ * TODO: decide if this is still a useful concept.
+ */
+INLINE void dvmReleaseRegisterMapLine(const RegisterMap* pMap, const u1* data)
+{}
+
+
+/*
+ * A pool of register maps for methods associated with a single class.
+ *
+ * Each entry is a 4-byte method index followed by the 32-bit-aligned
+ * RegisterMap. The size of the RegisterMap is determined by parsing
+ * the map. The lack of an index reduces random access speed, but we
+ * should be doing that rarely (during class load) and it saves space.
+ *
+ * These structures are 32-bit aligned.
+ */
+typedef struct RegisterMapMethodPool {
+ u2 methodCount; /* chiefly used as a sanity check */
+
+ /* stream of per-method data starts here */
+ u4 methodData[1];
+} RegisterMapMethodPool;
+
+/*
+ * Header for the memory-mapped RegisterMap pool in the DEX file.
+ *
+ * The classDataOffset table provides offsets from the start of the
+ * RegisterMapPool structure. There is one entry per class (including
+ * interfaces, which can have static initializers).
+ *
+ * The offset points to a RegisterMapMethodPool.
+ *
+ * These structures are 32-bit aligned.
+ */
+typedef struct RegisterMapClassPool {
+ u4 numClasses;
+
+ /* offset table starts here, 32-bit aligned; offset==0 means no data */
+ u4 classDataOffset[1];
+} RegisterMapClassPool;
+
+/*
+ * Find the register maps for this class. (Used during class loading.)
+ * If "pNumMaps" is non-NULL, it will return the number of maps in the set.
+ *
+ * Returns NULL if none is available.
+ */
+const void* dvmRegisterMapGetClassData(const DexFile* pDexFile, u4 classIdx,
+ u4* pNumMaps);
+
+/*
+ * Get the register map for the next method. "*pPtr" will be advanced past
+ * the end of the map. (Used during class loading.)
+ *
+ * This should initially be called with the result from
+ * dvmRegisterMapGetClassData().
+ */
+const RegisterMap* dvmRegisterMapGetNext(const void** pPtr);
+
+/*
+ * This holds some meta-data while we construct the set of register maps
+ * for a DEX file.
+ *
+ * In particular, it keeps track of our temporary mmap region so we can
+ * free it later.
+ */
+typedef struct RegisterMapBuilder {
+ /* public */
+ void* data;
+ size_t size;
+
+ /* private */
+ MemMapping memMap;
+} RegisterMapBuilder;
+
+/*
+ * Generate a register map set for all verified classes in "pDvmDex".
+ */
+RegisterMapBuilder* dvmGenerateRegisterMaps(DvmDex* pDvmDex);
+
+/*
+ * Free the builder.
+ */
+void dvmFreeRegisterMapBuilder(RegisterMapBuilder* pBuilder);
+
+/*
+ * Generate the register map for a method that has just been verified
+ * (i.e. we're doing this as part of verification).
+ *
+ * Returns a pointer to a newly-allocated RegisterMap, or NULL on failure.
+ */
+RegisterMap* dvmGenerateRegisterMapV(VerifierData* vdata);
+
+/*
+ * Get the expanded form of the register map associated with the specified
+ * method. May update method->registerMap, possibly freeing the previous
+ * map.
+ *
+ * Returns NULL on failure (e.g. unable to expand map).
+ *
+ * NOTE: this function is not synchronized; external locking is mandatory.
+ * (This is expected to be called at GC time.)
+ */
+const RegisterMap* dvmGetExpandedRegisterMap0(Method* method);
+INLINE const RegisterMap* dvmGetExpandedRegisterMap(Method* method)
+{
+ const RegisterMap* curMap = method->registerMap;
+ if (curMap == NULL)
+ return NULL;
+ RegisterMapFormat format = dvmRegisterMapGetFormat(curMap);
+ if (format == kRegMapFormatCompact8 || format == kRegMapFormatCompact16) {
+ return curMap;
+ } else {
+ return dvmGetExpandedRegisterMap0(method);
+ }
+}
+
+/* dump stats gathered during register map creation process */
+void dvmRegisterMapDumpStats(void);
+
+#endif /*_DALVIK_REGISTERMAP*/
diff --git a/vm/analysis/VerifySubs.c b/vm/analysis/VerifySubs.c
new file mode 100644
index 0000000..8334a0c
--- /dev/null
+++ b/vm/analysis/VerifySubs.c
@@ -0,0 +1,457 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik verification subroutines.
+ */
+#include "Dalvik.h"
+#include "analysis/CodeVerify.h"
+#include "libdex/DexCatch.h"
+#include "libdex/InstrUtils.h"
+
+
+/*
+ * Compute the width of the instruction at each address in the instruction
+ * stream. Addresses that are in the middle of an instruction, or that
+ * are part of switch table data, are not set (so the caller should probably
+ * initialize "insnFlags" to zero).
+ *
+ * If "pNewInstanceCount" is not NULL, it will be set to the number of
+ * new-instance instructions in the method.
+ *
+ * Performs some static checks, notably:
+ * - opcode of first instruction begins at index 0
+ * - only documented instructions may appear
+ * - each instruction follows the last
+ * - last byte of last instruction is at (code_length-1)
+ *
+ * Logs an error and returns "false" on failure.
+ */
+bool dvmComputeCodeWidths(const Method* meth, InsnFlags* insnFlags,
+ int* pNewInstanceCount)
+{
+ size_t insnCount = dvmGetMethodInsnsSize(meth);
+ const u2* insns = meth->insns;
+ bool result = false;
+ int newInstanceCount = 0;
+ int i;
+
+
+ for (i = 0; i < (int) insnCount; /**/) {
+ size_t width = dexGetInstrOrTableWidthAbs(gDvm.instrWidth, insns);
+ if (width == 0) {
+ LOG_VFY_METH(meth,
+ "VFY: invalid post-opt instruction (0x%04x)\n", *insns);
+ goto bail;
+ }
+
+ if ((*insns & 0xff) == OP_NEW_INSTANCE)
+ newInstanceCount++;
+
+ if (width > 65535) {
+ LOG_VFY_METH(meth, "VFY: insane width %d\n", width);
+ goto bail;
+ }
+
+ insnFlags[i] |= width;
+ i += width;
+ insns += width;
+ }
+ if (i != (int) dvmGetMethodInsnsSize(meth)) {
+ LOG_VFY_METH(meth, "VFY: code did not end where expected (%d vs. %d)\n",
+ i, dvmGetMethodInsnsSize(meth));
+ goto bail;
+ }
+
+ result = true;
+ if (pNewInstanceCount != NULL)
+ *pNewInstanceCount = newInstanceCount;
+
+bail:
+ return result;
+}
+
+/*
+ * Set the "in try" flags for all instructions protected by "try" statements.
+ * Also sets the "branch target" flags for exception handlers.
+ *
+ * Call this after widths have been set in "insnFlags".
+ *
+ * Returns "false" if something in the exception table looks fishy, but
+ * we're expecting the exception table to be somewhat sane.
+ */
+bool dvmSetTryFlags(const Method* meth, InsnFlags* insnFlags)
+{
+ u4 insnsSize = dvmGetMethodInsnsSize(meth);
+ const DexCode* pCode = dvmGetMethodCode(meth);
+ u4 triesSize = pCode->triesSize;
+ const DexTry* pTries;
+ u4 handlersSize;
+ u4 offset;
+ u4 i;
+
+ if (triesSize == 0) {
+ return true;
+ }
+
+ pTries = dexGetTries(pCode);
+ handlersSize = dexGetHandlersSize(pCode);
+
+ for (i = 0; i < triesSize; i++) {
+ const DexTry* pTry = &pTries[i];
+ u4 start = pTry->startAddr;
+ u4 end = start + pTry->insnCount;
+ u4 addr;
+
+ if ((start >= end) || (start >= insnsSize) || (end > insnsSize)) {
+ LOG_VFY_METH(meth,
+ "VFY: bad exception entry: startAddr=%d endAddr=%d (size=%d)\n",
+ start, end, insnsSize);
+ return false;
+ }
+
+ if (dvmInsnGetWidth(insnFlags, start) == 0) {
+ LOG_VFY_METH(meth,
+ "VFY: 'try' block starts inside an instruction (%d)\n",
+ start);
+ return false;
+ }
+
+ for (addr = start; addr < end;
+ addr += dvmInsnGetWidth(insnFlags, addr))
+ {
+ assert(dvmInsnGetWidth(insnFlags, addr) != 0);
+ dvmInsnSetInTry(insnFlags, addr, true);
+ }
+ }
+
+ /* Iterate over each of the handlers to verify target addresses. */
+ offset = dexGetFirstHandlerOffset(pCode);
+ for (i = 0; i < handlersSize; i++) {
+ DexCatchIterator iterator;
+ dexCatchIteratorInit(&iterator, pCode, offset);
+
+ for (;;) {
+ DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
+ u4 addr;
+
+ if (handler == NULL) {
+ break;
+ }
+
+ addr = handler->address;
+ if (dvmInsnGetWidth(insnFlags, addr) == 0) {
+ LOG_VFY_METH(meth,
+ "VFY: exception handler starts at bad address (%d)\n",
+ addr);
+ return false;
+ }
+
+ dvmInsnSetBranchTarget(insnFlags, addr, true);
+ }
+
+ offset = dexCatchIteratorGetEndOffset(&iterator, pCode);
+ }
+
+ return true;
+}
+
+/*
+ * Verify a switch table. "curOffset" is the offset of the switch
+ * instruction.
+ */
+bool dvmCheckSwitchTargets(const Method* meth, InsnFlags* insnFlags,
+ int curOffset)
+{
+ const s4 insnCount = dvmGetMethodInsnsSize(meth);
+ const u2* insns = meth->insns + curOffset;
+ const u2* switchInsns;
+ u2 expectedSignature;
+ u4 switchCount, tableSize;
+ s4 offsetToSwitch, offsetToKeys, offsetToTargets;
+ s4 offset, absOffset;
+ u4 targ;
+
+ assert(curOffset >= 0 && curOffset < insnCount);
+
+ /* make sure the start of the switch is in range */
+ offsetToSwitch = insns[1] | ((s4) insns[2]) << 16;
+ if (curOffset + offsetToSwitch < 0 ||
+ curOffset + offsetToSwitch + 2 >= insnCount)
+ {
+ LOG_VFY_METH(meth,
+ "VFY: invalid switch start: at %d, switch offset %d, count %d\n",
+ curOffset, offsetToSwitch, insnCount);
+ return false;
+ }
+
+ /* offset to switch table is a relative branch-style offset */
+ switchInsns = insns + offsetToSwitch;
+
+ /* make sure the table is 32-bit aligned */
+ if ((((u4) switchInsns) & 0x03) != 0) {
+ LOG_VFY_METH(meth,
+ "VFY: unaligned switch table: at %d, switch offset %d\n",
+ curOffset, offsetToSwitch);
+ return false;
+ }
+
+ switchCount = switchInsns[1];
+
+ if ((*insns & 0xff) == OP_PACKED_SWITCH) {
+ /* 0=sig, 1=count, 2/3=firstKey */
+ offsetToTargets = 4;
+ offsetToKeys = -1;
+ expectedSignature = kPackedSwitchSignature;
+ } else {
+ /* 0=sig, 1=count, 2..count*2 = keys */
+ offsetToKeys = 2;
+ offsetToTargets = 2 + 2*switchCount;
+ expectedSignature = kSparseSwitchSignature;
+ }
+ tableSize = offsetToTargets + switchCount*2;
+
+ if (switchInsns[0] != expectedSignature) {
+ LOG_VFY_METH(meth,
+ "VFY: wrong signature for switch table (0x%04x, wanted 0x%04x)\n",
+ switchInsns[0], expectedSignature);
+ return false;
+ }
+
+ /* make sure the end of the switch is in range */
+ if (curOffset + offsetToSwitch + tableSize > (u4) insnCount) {
+ LOG_VFY_METH(meth,
+ "VFY: invalid switch end: at %d, switch offset %d, end %d, count %d\n",
+ curOffset, offsetToSwitch, curOffset + offsetToSwitch + tableSize,
+ insnCount);
+ return false;
+ }
+
+ /* for a sparse switch, verify the keys are in ascending order */
+ if (offsetToKeys > 0 && switchCount > 1) {
+ s4 lastKey;
+
+ lastKey = switchInsns[offsetToKeys] |
+ (switchInsns[offsetToKeys+1] << 16);
+ for (targ = 1; targ < switchCount; targ++) {
+ s4 key = (s4) switchInsns[offsetToKeys + targ*2] |
+ (s4) (switchInsns[offsetToKeys + targ*2 +1] << 16);
+ if (key <= lastKey) {
+ LOG_VFY_METH(meth,
+ "VFY: invalid packed switch: last key=%d, this=%d\n",
+ lastKey, key);
+ return false;
+ }
+
+ lastKey = key;
+ }
+ }
+
+ /* verify each switch target */
+ for (targ = 0; targ < switchCount; targ++) {
+ offset = (s4) switchInsns[offsetToTargets + targ*2] |
+ (s4) (switchInsns[offsetToTargets + targ*2 +1] << 16);
+ absOffset = curOffset + offset;
+
+ if (absOffset < 0 || absOffset >= insnCount ||
+ !dvmInsnIsOpcode(insnFlags, absOffset))
+ {
+ LOG_VFY_METH(meth,
+ "VFY: invalid switch target %d (-> 0x%x) at 0x%x[%d]\n",
+ offset, absOffset, curOffset, targ);
+ return false;
+ }
+ dvmInsnSetBranchTarget(insnFlags, absOffset, true);
+ }
+
+ return true;
+}
+
+/*
+ * Verify that the target of a branch instruction is valid.
+ *
+ * We don't expect code to jump directly into an exception handler, but
+ * it's valid to do so as long as the target isn't a "move-exception"
+ * instruction. We verify that in a later stage.
+ *
+ * The VM spec doesn't forbid an instruction from branching to itself,
+ * but the Dalvik spec declares that only certain instructions can do so.
+ */
+bool dvmCheckBranchTarget(const Method* meth, InsnFlags* insnFlags,
+ int curOffset, bool selfOkay)
+{
+ const int insnCount = dvmGetMethodInsnsSize(meth);
+ int offset, absOffset;
+ bool isConditional;
+
+ if (!dvmGetBranchTarget(meth, insnFlags, curOffset, &offset,
+ &isConditional))
+ return false;
+
+ if (!selfOkay && offset == 0) {
+ LOG_VFY_METH(meth, "VFY: branch offset of zero not allowed at 0x%x\n",
+ curOffset);
+ return false;
+ }
+
+ /*
+ * Check for 32-bit overflow. This isn't strictly necessary if we can
+ * depend on the VM to have identical "wrap-around" behavior, but
+ * it's unwise to depend on that.
+ */
+ if (((s8) curOffset + (s8) offset) != (s8)(curOffset + offset)) {
+ LOG_VFY_METH(meth, "VFY: branch target overflow 0x%x +%d\n",
+ curOffset, offset);
+ return false;
+ }
+ absOffset = curOffset + offset;
+ if (absOffset < 0 || absOffset >= insnCount ||
+ !dvmInsnIsOpcode(insnFlags, absOffset))
+ {
+ LOG_VFY_METH(meth,
+ "VFY: invalid branch target %d (-> 0x%x) at 0x%x\n",
+ offset, absOffset, curOffset);
+ return false;
+ }
+ dvmInsnSetBranchTarget(insnFlags, absOffset, true);
+
+ return true;
+}
+
+
+/*
+ * Output a code verifier warning message. For the pre-verifier it's not
+ * a big deal if something fails (and it may even be expected), but if
+ * we're doing just-in-time verification it's significant.
+ */
+void dvmLogVerifyFailure(const Method* meth, const char* format, ...)
+{
+ va_list ap;
+ int logLevel;
+
+ if (gDvm.optimizing) {
+ return;
+ //logLevel = ANDROID_LOG_DEBUG;
+ } else {
+ logLevel = ANDROID_LOG_WARN;
+ }
+
+ va_start(ap, format);
+ LOG_PRI_VA(logLevel, LOG_TAG, format, ap);
+ if (meth != NULL) {
+ char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+ LOG_PRI(logLevel, LOG_TAG, "VFY: rejected %s.%s %s\n",
+ meth->clazz->descriptor, meth->name, desc);
+ free(desc);
+ }
+}
+
+/*
+ * Show a relatively human-readable message describing the failure to
+ * resolve a class.
+ *
+ * TODO: this is somewhat misleading when resolution fails because of
+ * illegal access rather than nonexistent class.
+ */
+void dvmLogUnableToResolveClass(const char* missingClassDescr,
+ const Method* meth)
+{
+ if (gDvm.optimizing)
+ return;
+
+ char* dotMissingClass = dvmDescriptorToDot(missingClassDescr);
+ char* dotFromClass = dvmDescriptorToDot(meth->clazz->descriptor);
+ //char* methodDescr = dexProtoCopyMethodDescriptor(&meth->prototype);
+
+ LOGE("Could not find class '%s', referenced from method %s.%s\n",
+ dotMissingClass, dotFromClass, meth->name/*, methodDescr*/);
+
+ free(dotMissingClass);
+ free(dotFromClass);
+ //free(methodDescr);
+}
+
+/*
+ * Extract the relative offset from a branch instruction.
+ *
+ * Returns "false" on failure (e.g. this isn't a branch instruction).
+ */
+bool dvmGetBranchTarget(const Method* meth, InsnFlags* insnFlags,
+ int curOffset, int* pOffset, bool* pConditional)
+{
+ const u2* insns = meth->insns + curOffset;
+
+ switch (*insns & 0xff) {
+ case OP_GOTO:
+ *pOffset = ((s2) *insns) >> 8;
+ *pConditional = false;
+ break;
+ case OP_GOTO_32:
+ *pOffset = insns[1] | (((u4) insns[2]) << 16);
+ *pConditional = false;
+ break;
+ case OP_GOTO_16:
+ *pOffset = (s2) insns[1];
+ *pConditional = false;
+ break;
+ case OP_IF_EQ:
+ case OP_IF_NE:
+ case OP_IF_LT:
+ case OP_IF_GE:
+ case OP_IF_GT:
+ case OP_IF_LE:
+ case OP_IF_EQZ:
+ case OP_IF_NEZ:
+ case OP_IF_LTZ:
+ case OP_IF_GEZ:
+ case OP_IF_GTZ:
+ case OP_IF_LEZ:
+ *pOffset = (s2) insns[1];
+ *pConditional = true;
+ break;
+ default:
+ return false;
+ break;
+ }
+
+ return true;
+}
+
+/*
+ * Given a 32-bit constant, return the most-restricted RegType enum entry
+ * that can hold the value.
+ */
+char dvmDetermineCat1Const(s4 value)
+{
+ if (value < -32768)
+ return kRegTypeInteger;
+ else if (value < -128)
+ return kRegTypeShort;
+ else if (value < 0)
+ return kRegTypeByte;
+ else if (value == 0)
+ return kRegTypeZero;
+ else if (value == 1)
+ return kRegTypeOne;
+ else if (value < 128)
+ return kRegTypePosByte;
+ else if (value < 32768)
+ return kRegTypePosShort;
+ else if (value < 65536)
+ return kRegTypeChar;
+ else
+ return kRegTypeInteger;
+}
diff --git a/vm/analysis/VerifySubs.h b/vm/analysis/VerifySubs.h
new file mode 100644
index 0000000..9e05e9d
--- /dev/null
+++ b/vm/analysis/VerifySubs.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik bytecode verification subroutines.
+ */
+#ifndef _DALVIK_VERIFYSUBS
+#define _DALVIK_VERIFYSUBS
+
+/*
+ * InsnFlags is a 32-bit integer with the following layout:
+ * 0-15 instruction length (or 0 if this address doesn't hold an opcode)
+ * 16-31 single bit flags:
+ * InTry: in "try" block; exceptions thrown here may be caught locally
+ * BranchTarget: other instructions can branch to this instruction
+ * GcPoint: this instruction is a GC safe point
+ * Visited: verifier has examined this instruction at least once
+ * Changed: set/cleared as bytecode verifier runs
+ */
+typedef u4 InsnFlags;
+
+#define kInsnFlagWidthMask 0x0000ffff
+#define kInsnFlagInTry (1 << 16)
+#define kInsnFlagBranchTarget (1 << 17)
+#define kInsnFlagGcPoint (1 << 18)
+#define kInsnFlagVisited (1 << 30)
+#define kInsnFlagChanged (1 << 31)
+
+/* add opcode widths to InsnFlags */
+bool dvmComputeCodeWidths(const Method* meth, InsnFlags* insnFlags,
+ int* pNewInstanceCount);
+
+/* set the "in try" flag for sections of code wrapped with a "try" block */
+bool dvmSetTryFlags(const Method* meth, InsnFlags* insnFlags);
+
+/* check switch targets and set the "branch target" flag for destinations */
+bool dvmCheckSwitchTargets(const Method* meth, InsnFlags* insnFlags,
+ int curOffset);
+
+/* verify branch target and set "branch target" flag on the destination */
+bool dvmCheckBranchTarget(const Method* meth, InsnFlags* insnFlags,
+ int curOffset, bool selfOkay);
+
+/* verification failure reporting */
+#define LOG_VFY(...) dvmLogVerifyFailure(NULL, __VA_ARGS__)
+#define LOG_VFY_METH(_meth, ...) dvmLogVerifyFailure(_meth, __VA_ARGS__)
+
+/* log verification failure with optional method info */
+void dvmLogVerifyFailure(const Method* meth, const char* format, ...)
+#if defined(__GNUC__)
+ __attribute__ ((format(printf, 2, 3)))
+#endif
+ ;
+
+/* log verification failure due to resolution trouble */
+void dvmLogUnableToResolveClass(const char* missingClassDescr,
+ const Method* meth);
+
+/* extract the relative branch target from a branch instruction */
+bool dvmGetBranchTarget(const Method* meth, InsnFlags* insnFlags,
+ int curOffset, int* pOffset, bool* pConditional);
+
+/* return a RegType enumeration value that "value" just fits into */
+char dvmDetermineCat1Const(s4 value);
+
+#endif /*_DALVIK_VERIFYSUBS*/
diff --git a/vm/arch/arm/CallEABI.S b/vm/arch/arm/CallEABI.S
new file mode 100644
index 0000000..e0d4f5c
--- /dev/null
+++ b/vm/arch/arm/CallEABI.S
@@ -0,0 +1,424 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * JNI method invocation. This is used to call a C/C++ JNI method. The
+ * argument list has to be pushed onto the native stack according to
+ * local calling conventions.
+ *
+ * This version supports the "new" ARM EABI.
+ */
+
+#include <machine/cpu-features.h>
+
+#ifdef __ARM_EABI__
+
+#ifdef EXTENDED_EABI_DEBUG
+# define DBG
+#else
+# define DBG @
+#endif
+
+
+/*
+Function prototype:
+
+void dvmPlatformInvoke(void* pEnv, ClassObject* clazz, int argInfo, int argc,
+ const u4* argv, const char* signature, void* func, JValue* pReturn)
+
+The method we are calling has the form:
+
+ return_type func(JNIEnv* pEnv, ClassObject* clazz, ...)
+ -or-
+ return_type func(JNIEnv* pEnv, Object* this, ...)
+
+We receive a collection of 32-bit values which correspond to arguments from
+the interpreter (e.g. float occupies one, double occupies two). It's up to
+us to convert these into local calling conventions.
+*/
+
+/*
+ARM EABI notes:
+
+r0-r3 hold first 4 args to a method
+r9 is given special treatment in some situations, but not for us
+r10 (sl) seems to be generally available
+r11 (fp) is used by gcc (unless -fomit-frame-pointer is set)
+r12 (ip) is scratch -- not preserved across method calls
+r13 (sp) should be managed carefully in case a signal arrives
+r14 (lr) must be preserved
+r15 (pc) can be tinkered with directly
+
+r0 holds returns of <= 4 bytes
+r0-r1 hold returns of 8 bytes, low word in r0
+
+Callee must save/restore r4+ (except r12) if it modifies them.
+
+Stack is "full descending". Only the arguments that don't fit in the first 4
+registers are placed on the stack. "sp" points at the first stacked argument
+(i.e. the 5th arg).
+
+VFP: single-precision results in s0, double-precision results in d0.
+
+In the EABI, "sp" must be 64-bit aligned on entry to a function, and any
+64-bit quantities (long long, double) must be 64-bit aligned. This means
+we have to scan the method signature, identify arguments that must be
+padded, and fix them up appropriately.
+*/
+
+ .text
+ .align 2
+ .global dvmPlatformInvoke
+ .type dvmPlatformInvoke, %function
+
+/*
+ * On entry:
+ * r0 JNIEnv (can be left alone)
+ * r1 clazz (NULL for virtual method calls, non-NULL for static)
+ * r2 arg info
+ * r3 argc (number of 32-bit values in argv)
+ * [sp] argv
+ * [sp,#4] short signature
+ * [sp,#8] func
+ * [sp,#12] pReturn
+ *
+ * For a virtual method call, the "this" reference is in argv[0].
+ *
+ * argInfo (32-bit int) layout:
+ * SRRRLLLL FFFFFFFF FFFFFFFF FFFFFFFF
+ *
+ * S - if set, do things the hard way (scan the signature)
+ * R - return type enumeration, really only important for hardware FP
+ * L - number of double-words of storage required on stack (0-30 words)
+ * F - pad flag -- if set, write a pad word to the stack
+ *
+ * With this arrangement we can efficiently push up to 24 words of arguments
+ * onto the stack. Anything requiring more than that -- which should happen
+ * rarely to never -- can do the slow signature scan.
+ *
+ * (We could pack the Fs more efficiently -- we know we never push two pads
+ * in a row, and the first word can never be a pad -- but there's really
+ * no need for it.)
+ *
+ * TODO: could reduce register-saving overhead for "fast" case, since we
+ * don't use a couple of registers. Another thought is to rearrange the
+ * arguments such that r0/r1 get passed in on the stack, allowing us to
+ * use r0/r1 freely here and then load them with a single ldm. Might be
+ * faster than saving/restoring other registers so that we can leave r0/r1
+ * undisturbed.
+ *
+ * NOTE: if the called function has more than 4 words of arguments, gdb
+ * will not be able to unwind the stack past this method. The only way
+ * around this is to convince gdb to respect an explicit frame pointer.
+ */
+dvmPlatformInvoke:
+ .fnstart
+ @ Save regs. Same style as gcc with "-fomit-frame-pointer" -- we don't
+ @ disturb "fp" in case somebody else wants it. Copy "sp" to r4 and use
+ @ that to access local vars.
+ @
+ @ On entry to a function, "sp" must be 64-bit aligned. This means
+ @ we have to adjust sp manually if we push an odd number of regs here
+ @ (both here and when exiting). Easier to just push an even number
+ @ of registers.
+ mov ip, sp @ ip<- original stack pointer
+ .save {r4, r5, r6, r7, r8, r9, ip, lr}
+ stmfd sp!, {r4, r5, r6, r7, r8, r9, ip, lr}
+
+ mov r4, ip @ r4<- original stack pointer
+
+ @ Ensure 64-bit alignment. EABI guarantees sp is aligned on entry, make
+ @ sure we're aligned properly now.
+DBG tst sp, #4 @ 64-bit aligned?
+DBG bne dvmAbort
+
+ cmp r1, #0 @ Is this a static method?
+ ldr r9, [r4] @ r9<- argv
+
+ @ Not static: set r1 to *argv++ ("this"), and set argc--.
+ @
+ @ Note the "this" pointer is not included in the method signature.
+ ldreq r1, [r9], #4
+ subeq r3, r3, #1
+
+ @ Do we have arg padding flags in "argInfo"? (just need to check hi bit)
+ teq r2, #0
+ bmi .Lno_arg_info
+
+ /*
+ * "Fast" path.
+ *
+ * Make room on the stack for the arguments and copy them over,
+ * inserting pad words when appropriate.
+ *
+ * Currently:
+ * r0 don't touch
+ * r1 don't touch
+ * r2 arg info
+ * r3 argc
+ * r4 original stack pointer
+ * r5-r8 (available)
+ * r9 argv
+ */
+.Lhave_arg_info:
+ @ Expand the stack by the specified amount. We want to extract the
+ @ count of double-words from r2, multiply it by 8, and subtract that
+ @ from the stack pointer.
+ and ip, r2, #0x0f000000 @ ip<- double-words required
+ mov r5, r2, lsr #28 @ r5<- return type
+ sub sp, sp, ip, lsr #21 @ shift right 24, then left 3
+ mov r8, sp @ r8<- sp (arg copy dest)
+
+ @ Stick argv in r7 and advance it past the argv values that will be
+ @ held in r2-r3. It's possible r3 will hold a pad, so check the
+ @ bit in r2. We do this by ignoring the first bit (which would
+ @ indicate a pad in r2) and shifting the second into the carry flag.
+ @ If the carry is set, r3 will hold a pad, so we adjust argv less.
+ @
+ @ (This is harmless if argc==0)
+ mov r7, r9
+ movs r2, r2, lsr #2
+ addcc r7, r7, #8 @ skip past 2 words, for r2 and r3
+ subcc r3, r3, #2
+ addcs r7, r7, #4 @ skip past 1 word, for r2
+ subcs r3, r3, #1
+
+.Lfast_copy_loop:
+ @ if (--argc < 0) goto invoke
+ subs r3, r3, #1
+ bmi .Lcopy_done @ NOTE: expects original argv in r9
+
+.Lfast_copy_loop2:
+ @ Get pad flag into carry bit. If it's set, we don't pull a value
+ @ out of argv.
+ movs r2, r2, lsr #1
+
+ ldrcc ip, [r7], #4 @ ip = *r7++ (pull from argv)
+ strcc ip, [r8], #4 @ *r8++ = ip (write to stack)
+ bcc .Lfast_copy_loop
+
+DBG movcs ip, #-3 @ DEBUG DEBUG - make pad word obvious
+DBG strcs ip, [r8] @ DEBUG DEBUG
+ add r8, r8, #4 @ if pad, just advance ip without store
+ b .Lfast_copy_loop2 @ don't adjust argc after writing pad
+
+
+
+.Lcopy_done:
+ /*
+ * Currently:
+ * r0-r3 args (JNIEnv*, thisOrClass, arg0, arg1)
+ * r4 original saved sp
+ * r5 return type (enum DalvikJniReturnType)
+ * r9 original argv
+ *
+ * The stack copy is complete. Grab the first two words off of argv
+ * and tuck them into r2/r3. If the first arg is 32-bit and the second
+ * arg is 64-bit, then r3 "holds" a pad word and the load is unnecessary
+ * but harmless.
+ *
+ * If there are 0 or 1 arg words in argv, we will be loading uninitialized
+ * data into the registers, but since nothing tries to use it it's also
+ * harmless (assuming argv[0] and argv[1] point to valid memory, which
+ * is a reasonable assumption for Dalvik's interpreted stacks).
+ *
+ */
+ ldmia r9, {r2-r3} @ r2/r3<- argv[0]/argv[1]
+
+ @ call the method
+ ldr ip, [r4, #8] @ func
+#ifdef __ARM_HAVE_BLX
+ blx ip
+#else
+ mov lr, pc
+ bx ip
+#endif
+
+ @ We're back, result is in r0 or (for long/double) r0-r1.
+ @
+ @ In theory, we need to use the "return type" arg to figure out what
+ @ we have and how to return it. However, unless we have an FPU,
+ @ all we need to do is copy r0-r1 into the JValue union.
+ @
+ @ Thought: could redefine DalvikJniReturnType such that single-word
+ @ and double-word values occupy different ranges; simple comparison
+ @ allows us to choose between str and stm. Probably not worthwhile.
+ @
+ cmp r5, #0 @ DALVIK_JNI_RETURN_VOID?
+ ldrne ip, [r4, #12] @ pReturn
+ stmneia ip, {r0-r1} @ pReturn->j <- r0/r1
+
+ @ Restore the registers we saved and return (restores lr into pc, and
+ @ the initial stack pointer into sp).
+#ifdef __ARM_HAVE_PC_INTERWORK
+ ldmdb r4, {r4, r5, r6, r7, r8, r9, sp, pc}
+#else
+ ldmdb r4, {r4, r5, r6, r7, r8, r9, sp, lr}
+ bx lr
+#endif
+ .fnend
+
+
+
+ /*
+ * "Slow" path.
+ * Walk through the argument list, counting up the number of 32-bit words
+ * required to contain it. Then walk through it a second time, copying
+ * values out to the stack. (We could pre-compute the size to save
+ * ourselves a trip, but we'd have to store that somewhere -- this is
+ * sufficiently unlikely that it's not worthwhile.)
+ *
+ * Try not to make any assumptions about the number of args -- I think
+ * the class file format allows up to 64K words (need to verify that).
+ *
+ * Currently:
+ * r0 don't touch
+ * r1 don't touch
+ * r2 (available)
+ * r3 argc
+ * r4 original stack pointer
+ * r5-r8 (available)
+ * r9 argv
+ */
+.Lno_arg_info:
+ mov r5, r2, lsr #28 @ r5<- return type
+ ldr r6, [r4, #4] @ r6<- short signature
+ mov r2, #0 @ r2<- word count, init to zero
+
+.Lcount_loop:
+ ldrb ip, [r6], #1 @ ip<- *signature++
+ cmp ip, #0 @ end?
+ beq .Lcount_done @ all done, bail
+ add r2, r2, #1 @ count++
+ cmp ip, #'D' @ look for 'D' or 'J', which are 64-bit
+ cmpne ip, #'J'
+ bne .Lcount_loop
+
+ @ 64-bit value, insert padding if we're not aligned
+ tst r2, #1 @ odd after initial incr?
+ addne r2, #1 @ no, add 1 more to cover 64 bits
+ addeq r2, #2 @ yes, treat prev as pad, incr 2 now
+ b .Lcount_loop
+.Lcount_done:
+
+ @ We have the padded-out word count in r2. We subtract 2 from it
+ @ because we don't push the first two arg words on the stack (they're
+ @ destined for r2/r3). Pushing them on and popping them off would be
+ @ simpler but slower.
+ subs r2, r2, #2 @ subtract 2 (for contents of r2/r3)
+ movmis r2, #0 @ if negative, peg at zero, set Z-flag
+ beq .Lcopy_done @ zero args, skip stack copy
+
+DBG tst sp, #7 @ DEBUG - make sure sp is aligned now
+DBG bne dvmAbort @ DEBUG
+
+ @ Set up to copy from r7 to r8. We copy from the second arg to the
+ @ last arg, which means reading and writing to ascending addresses.
+ sub sp, sp, r2, asl #2 @ sp<- sp - r2*4
+ bic sp, #4 @ subtract another 4 ifn
+ mov r7, r9 @ r7<- argv
+ mov r8, sp @ r8<- sp
+
+ @ We need to copy words from [r7] to [r8]. We walk forward through
+ @ the signature again, "copying" pad words when appropriate, storing
+ @ upward into the stack.
+ ldr r6, [r4, #4] @ r6<- signature
+ add r7, r7, #8 @ r7<- r7+8 (assume argv 0/1 in r2/r3)
+
+ @ Eat first arg or two, for the stuff that goes into r2/r3.
+ ldrb ip, [r6], #1 @ ip<- *signature++
+ cmp ip, #'D'
+ cmpne ip, #'J'
+ beq .Lstack_copy_loop @ 64-bit arg fills r2+r3
+
+ @ First arg was 32-bit, check the next
+ ldrb ip, [r6], #1 @ ip<- *signature++
+ cmp r6, #'D'
+ cmpne r6, #'J'
+ subeq r7, #4 @ r7<- r7-4 (take it back - pad word)
+ beq .Lstack_copy_loop2 @ start with char we already have
+
+ @ Two 32-bit args, fall through and start with next arg
+
+.Lstack_copy_loop:
+ ldrb ip, [r6], #1 @ ip<- *signature++
+.Lstack_copy_loop2:
+ cmp ip, #0 @ end of shorty?
+ beq .Lcopy_done @ yes
+
+ cmp ip, #'D'
+ cmpne ip, #'J'
+ beq .Lcopy64
+
+ @ Copy a 32-bit value. [r8] is initially at the end of the stack. We
+ @ use "full descending" stacks, so we store into [r8] and incr as we
+ @ move toward the end of the arg list.
+.Lcopy32:
+ ldr ip, [r7], #4
+ str ip, [r8], #4
+ b .Lstack_copy_loop
+
+.Lcopy64:
+ @ Copy a 64-bit value. If necessary, leave a hole in the stack to
+ @ ensure alignment. We know the [r8] output area is 64-bit aligned,
+ @ so we can just mask the address.
+ add r8, r8, #7 @ r8<- (r8+7) & ~7
+ ldr ip, [r7], #4
+ bic r8, r8, #7
+ ldr r2, [r7], #4
+ str ip, [r8], #4
+ str r2, [r8], #4
+ b .Lstack_copy_loop
+
+
+
+#if 0
+
+/*
+ * Spit out a "we were here", preserving all registers. (The attempt
+ * to save ip won't work, but we need to save an even number of
+ * registers for EABI 64-bit stack alignment.)
+ */
+ .macro SQUEAK num
+common_squeak\num:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ ldr r0, strSqueak
+ mov r1, #\num
+ bl printf
+#ifdef __ARM_HAVE_PC_INTERWORK
+ ldmfd sp!, {r0, r1, r2, r3, ip, pc}
+#else
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+#endif
+ .endm
+
+ SQUEAK 0
+ SQUEAK 1
+ SQUEAK 2
+ SQUEAK 3
+ SQUEAK 4
+ SQUEAK 5
+
+strSqueak:
+ .word .LstrSqueak
+.LstrSqueak:
+ .asciz "<%d>"
+
+ .align 2
+
+#endif
+
+#endif /*__ARM_EABI__*/
diff --git a/vm/arch/arm/CallOldABI.S b/vm/arch/arm/CallOldABI.S
new file mode 100644
index 0000000..2463d3c
--- /dev/null
+++ b/vm/arch/arm/CallOldABI.S
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * JNI method invocation. This is used to call a C/C++ JNI method. The
+ * argument list has to be pushed onto the native stack according to
+ * local calling conventions.
+ *
+ * This version supports the "old" ARM ABI.
+ */
+
+#include <machine/cpu-features.h>
+
+#ifndef __ARM_EABI__
+
+/*
+Function prototype:
+
+void dvmPlatformInvoke(void* pEnv, ClassObject* clazz, int argInfo, int argc,
+ const u4* argv, const char* signature, void* func, JValue* pReturn)
+
+The method we are calling has the form:
+
+ return_type func(JNIEnv* pEnv, ClassObject* clazz, ...)
+ -or-
+ return_type func(JNIEnv* pEnv, Object* this, ...)
+
+We receive a collection of 32-bit values which correspond to arguments from
+the interpreter (e.g. float occupies one, double occupies two). It's up to
+us to convert these into local calling conventions.
+ */
+
+/*
+ARM ABI notes:
+
+r0-r3 hold first 4 args to a method
+r9 is given special treatment in some situations, but not for us
+r10 (sl) seems to be generally available
+r11 (fp) is used by gcc
+r12 (ip) is scratch -- not preserved across method calls
+r13 (sp) should be managed carefully in case a signal arrives
+r14 (lr) must be preserved
+r15 (pc) can be tinkered with directly
+
+r0 holds returns <= 4 bytes
+r0-r1 hold returns of 5-8 bytes, low word in r0
+
+Stack is "full descending". Only the arguments that don't fit in the first 4
+registers are placed on the stack. "sp" points at the first stacked argument
+(i.e. the 5th arg).
+
+VFP: single-precision results in s0, double-precision results in d0.
+
+Happily we don't have to do anything special here -- the args from the
+interpreter work directly as C/C++ args on ARM (with the "classic" ABI).
+*/
+
+ .text
+ .align 2
+ .global dvmPlatformInvoke
+ .type dvmPlatformInvoke, %function
+
+/*
+On entry:
+ r0 JNIEnv
+ r1 clazz (NULL for virtual method calls, non-NULL for static)
+ r2 arg info (ignored)
+ r3 argc
+ [sp] argv
+ [sp,#4] signature (ignored)
+ [sp,#8] func
+ [sp,#12] pReturn
+*/
+dvmPlatformInvoke:
+ @ Standard gcc stack frame setup. We don't need to push the original
+ @ sp or the current pc if "-fomit-frame-pointer" is in use for the
+ @ rest of the code. If we don't plan to use a debugger we can speed
+ @ this up a little.
+ mov ip, sp
+ stmfd sp!, {r4, r5, r6, fp, ip, lr, pc}
+ sub fp, ip, #4 @ set up fp, same way gdb does
+
+ @ We need to push a variable number of arguments onto the stack.
+ @ Rather than keep a count and pop them off after, we just hold on to
+ @ the stack pointers.
+ @
+ @ In theory we don't need to keep sp -- we can do an ldmdb instead of
+ @ an ldmia -- but we're doing the gcc frame trick where we push the
+ @ pc on with stmfd and don't pop it off.
+ mov r4, ip
+ mov r5, sp
+
+ @ argc is already in a scratch register (r3). Put argv into one. Note
+ @ argv can't go into r0-r3 because we need to use it to load those.
+ ldr ip, [r4, #0] @ ip <-- argv
+
+ @ Is this a static method?
+ cmp r1, #0
+
+ @ No: set r1 to *argv++, and set argc--.
+ @ (r0=pEnv, r1=this)
+ ldreq r1, [ip], #4
+ subeq r3, r3, #1
+
+ @ While we still have the use of r2/r3, copy excess args from argv
+ @ to the stack. We need to push the last item in argv first, and we
+ @ want the first two items in argv to end up in r2/r3.
+ subs r3, r3, #2
+ ble .Lno_copy
+
+ @ If there are N args, we want to skip 0 and 1, and push (N-1)..2. We
+ @ have N-2 in r3. If we set argv=argv+1, we can count from N-2 to 1
+ @ inclusive and get the right set of args.
+ add r6, ip, #4
+
+.Lcopy:
+ @ *--sp = argv[count]
+ ldr r2, [r6, r3, lsl #2]
+ str r2, [sp, #-4]!
+ subs r3, r3, #1
+ bne .Lcopy
+
+.Lno_copy:
+ @ Load the last two args. These are coming out of the interpreted stack,
+ @ and the VM preserves an overflow region at the bottom, so it should be
+ @ safe to load two items out of argv even if we're at the end.
+ ldr r2, [ip]
+ ldr r3, [ip, #4]
+
+ @ Show time. Tuck the pc into lr and load the pc from the method
+ @ address supplied by the caller. The value for "pc" is offset by 8
+ @ due to instruction prefetching.
+ @
+#ifdef __ARM_HAVE_PC_INTERWORK
+ mov lr, pc
+ ldr pc, [r4, #8]
+#else
+ ldr ip, [r4, #8]
+ blx ip
+#endif
+
+ @ We're back, result is in r0 or (for long/double) r0-r1.
+ @
+ @ In theory, we need to use the "return type" arg to figure out what
+ @ we have and how to return it. However, unless we have an FPU,
+ @ all we need to do is copy r0-r1 into the JValue union.
+ ldr ip, [r4, #12]
+ stmia ip, {r0-r1}
+
+#ifdef __ARM_HAVE_PC_INTERWORK
+ @ Restore the registers we saved and return. Note this remaps stuff,
+ @ so that "sp" comes from "ip", "pc" comes from "lr", and the "pc"
+ @ we pushed on evaporates when we restore "sp".
+ ldmfd r5, {r4, r5, r6, fp, sp, pc}
+#else
+ ldmfd r5, {r4, r5, r6, fp, sp, lr}
+ bx lr
+#endif
+
+#endif /*__ARM_EABI__*/
diff --git a/vm/arch/arm/HintsEABI.c b/vm/arch/arm/HintsEABI.c
new file mode 100644
index 0000000..3e27e5a
--- /dev/null
+++ b/vm/arch/arm/HintsEABI.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Target-specific optimization and run-time hints
+ */
+
+
+#include "Dalvik.h"
+#include "libdex/DexClass.h"
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <sys/stat.h>
+
+
+/*
+ * The class loader will associate with each method a 32-bit info word
+ * (jniArgInfo) to support JNI calls. The high order 4 bits of this word
+ * are the same for all targets, while the lower 28 are used for hints to
+ * allow accelerated JNI bridge transfers.
+ *
+ * jniArgInfo (32-bit int) layout:
+ *
+ * SRRRHHHH HHHHHHHH HHHHHHHH HHHHHHHH
+ *
+ * S - if set, ignore the hints and do things the hard way (scan signature)
+ * R - return-type enumeration
+ * H - target-specific hints (see below for details)
+ *
+ * This function produces arm-specific hints - specifically a description
+ * of padding required to keep all 64-bit parameters properly aligned.
+ *
+ * ARM JNI hint format
+ *
+ * LLLL FFFFFFFF FFFFFFFF FFFFFFFF
+ *
+ * L - number of double-words of storage required on the stack (0-30 words)
+ * F - pad flag -- if set, write a pad word to the stack before copying
+ * the next 32 bits
+ *
+ * If there are too many arguments to construct valid hints, this function will
+ * return a result with the S bit set.
+ */
+u4 dvmPlatformInvokeHints(const DexProto* proto)
+{
+ const char* sig = dexProtoGetShorty(proto);
+ int padFlags, jniHints;
+ char sigByte;
+ int stackOffset, padMask;
+
+ stackOffset = padFlags = 0;
+ padMask = 0x00000001;
+
+ /* Skip past the return type */
+ sig++;
+
+ while (true) {
+ sigByte = *(sig++);
+
+ if (sigByte == '\0')
+ break;
+
+ if (sigByte == 'D' || sigByte == 'J') {
+ if ((stackOffset & 1) != 0) {
+ padFlags |= padMask;
+ stackOffset++;
+ padMask <<= 1;
+ }
+ stackOffset += 2;
+ padMask <<= 2;
+ } else {
+ stackOffset++;
+ padMask <<= 1;
+ }
+ }
+
+ jniHints = 0;
+
+ if (stackOffset > DALVIK_JNI_COUNT_SHIFT) {
+ /* too big for "fast" version */
+ jniHints = DALVIK_JNI_NO_ARG_INFO;
+ } else {
+ assert((padFlags & (0xffffffff << DALVIK_JNI_COUNT_SHIFT)) == 0);
+ stackOffset -= 2; // r2/r3 holds first two items
+ if (stackOffset < 0)
+ stackOffset = 0;
+ jniHints |= ((stackOffset+1) / 2) << DALVIK_JNI_COUNT_SHIFT;
+ jniHints |= padFlags;
+ }
+
+ return jniHints;
+}
diff --git a/vm/arch/generic/Call.c b/vm/arch/generic/Call.c
new file mode 100644
index 0000000..a39b761
--- /dev/null
+++ b/vm/arch/generic/Call.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * This uses the FFI (Foreign Function Interface) library to abstract away
+ * the system-dependent stuff. The FFI code is slower than a custom
+ * assembly version, but has the distinct advantage of having been
+ * written already for several platforms.
+ */
+#include "Dalvik.h"
+#include "ffi.h"
+
+/*
+ * Convert a signature type character to an FFI type.
+ */
+static ffi_type* getFfiType(char sigType)
+{
+ switch (sigType) {
+ case 'V': return &ffi_type_void;
+ case 'F': return &ffi_type_float;
+ case 'D': return &ffi_type_double;
+ case 'J': return &ffi_type_sint64;
+ case '[':
+ case 'L': return &ffi_type_pointer;
+ default: return &ffi_type_uint32;
+ }
+}
+
+/*
+ * Call "func" with the specified arguments.
+ *
+ * The second argument to JNI native functions is either the object (the
+ * "this" pointer) or, for static functions, a pointer to the class object.
+ * The Dalvik instructions will push "this" into argv[0], but it's up to
+ * us to insert the class object.
+ *
+ * Because there is no such thing in as a null "this" pointer, we use
+ * the non-NULL state of "clazz" to determine whether or not it's static.
+ *
+ * For maximum efficiency we should compute the CIF once and save it with
+ * the method. However, this requires storing the data with every native
+ * method. Since the goal is to have custom assembly versions of this
+ * on the platforms where performance matters, I'm recomputing the CIF on
+ * every call.
+ */
+void dvmPlatformInvoke(void* pEnv, ClassObject* clazz, int argInfo, int argc,
+ const u4* argv, const char* shorty, void* func, JValue* pReturn)
+{
+ const int kMaxArgs = argc+2; /* +1 for env, maybe +1 for clazz */
+ ffi_cif cif;
+ ffi_type* types[kMaxArgs];
+ void* values[kMaxArgs];
+ ffi_type* retType;
+ const char* sig;
+ char sigByte;
+ int srcArg, dstArg;
+
+ types[0] = &ffi_type_pointer;
+ values[0] = &pEnv;
+
+ types[1] = &ffi_type_pointer;
+ if (clazz != NULL) {
+ values[1] = &clazz;
+ srcArg = 0;
+ } else {
+ values[1] = (void*) argv++;
+ srcArg = 1;
+ }
+ dstArg = 2;
+
+ /*
+ * Scan the types out of the short signature. Use them to fill out the
+ * "types" array. Store the start address of the argument in "values".
+ */
+ retType = getFfiType(*shorty);
+ while ((sigByte = *++shorty) != '\0') {
+ types[dstArg] = getFfiType(sigByte);
+ values[dstArg++] = (void*) argv++;
+ if (sigByte == 'D' || sigByte == 'J')
+ argv++;
+ }
+
+ /*
+ * Prep the CIF (Call InterFace object).
+ */
+ if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, dstArg, retType, types) != FFI_OK) {
+ LOGE("ffi_prep_cif failed\n");
+ dvmAbort();
+ }
+
+ ffi_call(&cif, FFI_FN(func), pReturn, values);
+}
diff --git a/vm/arch/generic/Hints.c b/vm/arch/generic/Hints.c
new file mode 100644
index 0000000..7b08aeb
--- /dev/null
+++ b/vm/arch/generic/Hints.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Target-specific optimization and run-time hints
+ */
+
+
+#include "Dalvik.h"
+#include "libdex/DexClass.h"
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <sys/stat.h>
+
+
+/*
+ * The class loader will associate with each method a 32-bit info word
+ * (jniArgInfo) to support JNI calls. The high order 4 bits of this word
+ * are the same for all targets, while the lower 28 are used for hints to
+ * allow accelerated JNI bridge transfers.
+ *
+ * jniArgInfo (32-bit int) layout:
+ *
+ * SRRRHHHH HHHHHHHH HHHHHHHH HHHHHHHH
+ *
+ * S - if set, ignore the hints and do things the hard way (scan signature)
+ * R - return-type enumeration
+ * H - target-specific hints
+ *
+ * This function is a placeholder/template and should be duplicated in the
+ * appropriate arch/<target>/ directory for new target ports. The hints
+ * field should be defined and constructed in conjunction with
+ * dvmPlatformInvoke.
+
+ * If valid hints can't be constructed, this function should return a negative
+ * value. In that case, the caller will set the S bit in the jniArgInfo word
+ * and convert the arguments the slow way.
+ */
+u4 dvmPlatformInvokeHints( const DexProto* proto)
+{
+ /* No hints for generic target - force argument walk at run-time */
+ return DALVIK_JNI_NO_ARG_INFO;
+}
diff --git a/vm/arch/sh/CallSH4ABI.S b/vm/arch/sh/CallSH4ABI.S
new file mode 100644
index 0000000..f8b2ddc
--- /dev/null
+++ b/vm/arch/sh/CallSH4ABI.S
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Invoking JNI native method via SH4 ABI.
+ * This inplementation follows the spec found in following URL.
+ * http://www.ecos.sourceware.org/docs-1.3.1/ref/gnupro-ref/sh/SH_ch01.html#pgfId-461254
+
+ * This version supports SH4A little endian.
+ */
+ .text
+ .align 4
+ .type dvmPlatformInvoke, #function
+ .globl dvmPlatformInvoke
+
+/*
+ * @param r4 void* pEnv (used as scrach after invoking method)
+ * @param r5 ClassObject* clazz
+ * @param r6 int argInfo
+ * @param r7 int argc
+ * @param r15[0] const u4 * argv
+ * @param r15[1] const char * shorty
+ * @param r15[2] void * func
+ * @param r15[3] JValue * pReturn
+ *
+ * @remark r0,r1 Scratch before invoking method.
+ * Return value after invoking method.
+ * @remark r2 shorty pointer
+ * @remark r3 argv pointer before invoking method.
+ * pReturn after invoking method.
+ * @remark r8-11 Don't touch.
+ * @remark r12 status of r5-7
+ * @remark r13 status of fr4-11
+ * @remark r14 Keep stack pointer.
+ */
+dvmPlatformInvoke:
+ ## save preserved regsiters
+ mov.l r14, @-r15
+ mov r15, r14
+ add #4, r14 /* r14 = original r15 = stack pointer */
+ mov.l r13, @-r15
+ mov.l r12, @-r15
+ sts.l pr, @-r15
+
+ # fetch arguments
+ mov.l @r14, r3 /* argv */
+ mov.l @(4,r14), r2 /* shorty for argumnets */
+ mov #1, r0 /* shorty's 1st byte specify ret value type. */
+ add r0, r2
+
+### initialize local variables
+
+ ## r12 ... status of r6, and r7
+ ## bit 1 << 0 : if r6 is available, it contains 1.
+ ## bit 1 << 1 : if r7 is available, it contains 1.
+ ## Note : r4 is always used to pass pEnv.
+ ## r5 is always used for clazz or object
+ mov #3, r12 /* b0000-0111 : r5-7 avialble. */
+
+ ## r13 ... status of fr4-fr11
+ ## bit 1 << 0 : if fr4 is available, it contains 1.
+ ## bit 1 << 1 : if fr5 is available, it contains 1.
+ ## ...
+ ## bit 1 << 7 : if fr11 is available, it contains 1.
+ mov #0xFF, r13 /* b1111-1111 : fr4-11 avialble. */
+
+### put arguments
+
+ ## ... keep pEnv in r4 as is.
+
+ ## check clazz
+ mov #0, r0
+ cmp/eq r0, r5
+ bf arg_loop /* if r5 has clazz, keep it as is */
+ mov.l @r3+, r5 /* put object arg in r5 */
+
+ ## other args
+arg_loop:
+one_arg_handled:
+ mov.b @r2+, r0
+ cmp/eq #0, r0 /* if (*shorty == '\0) */
+ bf process_one_arg
+ bra arg_end /* no argument left */
+ nop
+
+process_one_arg:
+
+ ## check arg type
+
+ cmp/eq #'F', r0
+ bt jfloat_arg
+
+ cmp/eq #'D', r0
+ bt jdouble_arg
+
+ cmp/eq #'J', r0
+ bt jlong_arg
+
+ ## other 32bit arg types
+ mov r12, r0
+ cmp/eq #0, r0
+ bt put_32bit_on_stack /* r6-7 not available */
+
+ tst #1, r0
+ bt j32_arg_1
+ mov.l @r3+, r6 /* put one arg in r6 */
+ mov #1, r0 /* r6 is not available now. */
+ not r0, r0
+ and r0, r12
+ bra one_arg_handled
+ nop
+j32_arg_1:
+ tst #2, r0
+ bt j32_arg_fatal_error
+ mov.l @r3+, r7 /* put one arg in r7 */
+ mov #2, r0 /* r7 is not available now. */
+ not r0, r0
+ and r0, r12
+ bra one_arg_handled
+ nop
+
+j32_arg_fatal_error:
+ bra j32_arg_fatal_error
+ nop
+
+jlong_arg:
+ mov r12, r0
+ cmp/eq #0, r0
+ bt put_64bit_on_stack /* r6-7 not available */
+
+ and #3, r0
+ cmp/eq #3, r0
+ bf put_64bit_on_stack /* consequent two registers not available. */
+ mov.l @r3+, r6 /* put one arg in r6 and r7 */
+ mov.l @r3+, r7
+ mov #3, r0 /* r6 and r7 are not available now. */
+ not r0, r0
+ and r0, r12
+ bra one_arg_handled
+ nop
+
+ # utility routines are placed here make short range jumps available.
+put_32bit_on_stack:
+ mov.l @r3+, r0
+ mov.l r0, @-r15
+ bra one_arg_handled
+ nop
+
+put_64bit_on_stack:
+ mov.l @r3+, r0
+ mov.l r0, @-r15 /* Pay attention that the endianness is */
+ mov.l @r3+, r0 /* once reversed. It is corrected when the */
+ mov.l r0, @-r15 /* arguments on stack are revesred before */
+ bra one_arg_handled /* jni call */
+ nop
+
+jdouble_arg:
+ mov r13, r0
+ cmp/eq #0, r0
+ bt put_64bit_on_stack /* fr4-11 not available */
+
+ and #3, r0
+ cmp/eq #3, r0
+ bf jdouble_arg_1
+
+ fmov.s @r3+, fr5 /* put one arg to drX */
+ fmov.s @r3+, fr4
+ mov #3, r0 /* fr4-frX not available now. */
+ not r0, r0
+ and r0, r13
+ bra one_arg_handled
+ nop
+
+jdouble_arg_1:
+ mov r13, r0
+ and #12, r0
+ cmp/eq #12, r0
+ bf jdouble_arg_2
+
+ fmov.s @r3+, fr7 /* put one arg to drX */
+ fmov.s @r3+, fr6
+ mov #15, r0 /* fr4-frX not available now. */
+ not r0, r0
+ and r0, r13
+ bra one_arg_handled
+ nop
+
+jdouble_arg_2:
+ mov r13, r0
+ and #48, r0
+ cmp/eq #48, r0
+ bf jdouble_arg_3
+ fmov.s @r3+, fr9 /* put one arg to drX */
+ fmov.s @r3+, fr8
+ mov #63, r0 /* fr4-frX not available now. */
+ not r0, r0
+ and r0, r13
+ bra one_arg_handled
+ nop
+
+jdouble_arg_3:
+ mov r13, r0
+ and #192, r0
+ cmp/eq #192, r0
+ bf put_64bit_on_stack
+ fmov.s @r3+, fr11 /* put one arg to drX */
+ fmov.s @r3+, fr10
+ mov #0, r13 /* fr4-fr11 all not available now. */
+ bra one_arg_handled
+ nop
+
+jfloat_arg:
+ mov r13, r0
+ cmp/eq #0, r0
+ bt put_32bit_on_stack /* fr4-11 not available */
+
+ tst #2, r0
+ bt jfloat_arg_1
+ fmov.s @r3+, fr5 /* put one arg to frX */
+ mov #2, r0 /* frX not available now. */
+ not r0, r0
+ and r0, r13
+ bra one_arg_handled
+ nop
+
+jfloat_arg_1:
+ tst #1, r0
+ bt jfloat_arg_2
+ fmov.s @r3+, fr4 /* put one arg to frX */
+ mov #1, r0 /* frX not available now. */
+ not r0, r0
+ and r0, r13
+ bra one_arg_handled
+ nop
+
+jfloat_arg_2:
+ tst #8, r0
+ bt jfloat_arg_3
+ fmov.s @r3+, fr7 /* put one arg to frX */
+ mov #8, r0 /* frX not available now. */
+ not r0, r0
+ and r0, r13
+ bra one_arg_handled
+ nop
+
+jfloat_arg_3:
+ tst #4, r0
+ bt jfloat_arg_4
+ fmov.s @r3+, fr6 /* put one arg to frX */
+ mov #4, r0 /* frX not available now. */
+ not r0, r0
+ and r0, r13
+ bra one_arg_handled
+ nop
+
+jfloat_arg_4:
+ tst #32, r0
+ bt jfloat_arg_5
+ fmov.s @r3+, fr9 /* put one arg to frX */
+ mov #32, r0 /* frX not available now. */
+ not r0, r0
+ and r0, r13
+ bra one_arg_handled
+ nop
+
+jfloat_arg_5:
+ tst #16, r0
+ bt jfloat_arg_6
+ fmov.s @r3+, fr8 /* put one arg to frX */
+ mov #16, r0 /* frX not available now. */
+ not r0, r0
+ and r0, r13
+ bra one_arg_handled
+ nop
+
+jfloat_arg_6:
+ tst #128, r0
+ bt jfloat_arg_7
+ fmov.s @r3+, fr11 /* put one arg to frX */
+ mov #127, r0 /* frX not available now. */
+ not r0, r0
+ and r0, r13
+ bra one_arg_handled
+ nop
+
+jfloat_arg_7:
+ tst #64, r0
+ bt jfloat_fatal_error
+ fmov.s @r3+, fr10 /* put one arg to frX */
+ mov #64, r0 /* frX not available now. */
+ not r0, r0
+ and r0, r13
+ bra one_arg_handled
+ nop
+
+jfloat_fatal_error:
+ bra jfloat_fatal_error:
+ nop
+
+arg_end:
+
+
+### reverse the variables on stack
+ mov r14, r12 /* points to first arg on stack */
+ add #-20, r12
+ mov r15, r13 /* points to last arg on stack */
+arg_rev_loop:
+ cmp/hs r12, r13 /* When r13 >= r12 (unsigned), 1->T */
+ bt arg_rev_end
+ mov.l @r12, r0
+ mov.l @r13, r1
+ mov.l r0, @r13
+ mov.l r1, @r12
+ add #-4, r12
+ add #4, r13
+ bra arg_rev_loop
+ nop
+
+arg_rev_end:
+
+### invoke the JNI function.
+
+ mov.l @(8,r14), r0
+ jsr @r0
+ nop
+
+### pass the return value
+
+ /*
+ * r0 and r1 keep return value.
+ */
+
+ ## fetch data
+ mov.l @(4,r14), r2 /* reload shorty */
+ mov.b @r2, r2 /* first byte specifyes return value type. */
+ mov.l @(12,r14), r3 /* pReturn */
+
+ ## check return value types
+
+ mov #'V', r4
+ cmp/eq r4, r2
+ bt end
+
+ mov #'F', r4
+ cmp/eq r4, r2
+ bt jfloat_ret
+
+ mov #'D', r4
+ cmp/eq r4, r2
+ bt jdouble_ret
+
+ mov #'J', r4
+ cmp/eq r4, r2
+ bt jlong_ret
+
+ ## fall-through for other 32 bit java types.
+
+ ## load return values
+j32_ret:
+ bra end
+ mov.l r0, @r3 /* delay slot */
+
+jfloat_ret:
+ bra end
+ fmov.s fr0, @r3 /* delay slot */
+
+jdouble_ret:
+ fmov.s fr1, @r3
+ mov #4, r0
+ bra end
+ fmov.s fr0, @(r0,r3) /* delay slot */
+
+jlong_ret:
+ mov.l r0, @r3
+ bra end
+ mov.l r1, @(4,r3) /* delay slot */
+
+end:
+ ## restore preserved registers
+ mov r14, r15
+ add #-16, r15
+ lds.l @r15+, pr
+ mov.l @r15+, r12
+ mov.l @r15+, r13
+ mov.l @r15+, r14
+
+ rts /* dvmPlatformInvoke returns void. */
+ nop
diff --git a/vm/arch/x86-atom/Call386ABI.S b/vm/arch/x86-atom/Call386ABI.S
new file mode 100644
index 0000000..f996168
--- /dev/null
+++ b/vm/arch/x86-atom/Call386ABI.S
@@ -0,0 +1,189 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: CallABI.S
+ *
+ * Code: facilitates call to native code C and C++ routines.
+ *
+ */
+
+ /*
+ * Function prototype:
+ *
+ * void dvmPlatformInvoke(void* pEnv, ClassObject* clazz, int argInfo, int argc,
+ * const u4* argv, const char* signature, void* func, JValue* pReturn)
+ *
+ * The method we are calling has the form:
+ *
+ * return_type func(JNIEnv* pEnv, ClassObject* clazz, ...)
+ * -or-
+ * return_type func(JNIEnv* pEnv, Object* this, ...)
+ *
+ * We receive a collection of 32-bit values which correspond to arguments from
+ * the interpreter (e.g. float occupies one, double occupies two). It's up to
+ * us to convert these into local calling conventions.
+ */
+
+ /*
+ * On entry:
+ * 4(%sp) JNIEnv (can be left alone)
+ * 8(%esp) clazz (NULL for virtual method calls, non-NULL for static)
+ * 12(%esp) arg info
+ * 16(%esp) argc (number of 32-bit values in argv)
+ * 20(%esp) argv
+ * 24(%esp) short signature
+ * 28(%esp) func
+ * 32(%esp) pReturn
+ *
+ * For a virtual method call, the "this" reference is in argv[0].
+ *
+ * argInfo (32-bit int) layout:
+ *
+ * SRRRHHHH HHHHHHHH HHHHHHHH HHHHHHHH
+ *
+ * S - if set, argInfo hints are invalid
+ * R - return type enumeration (see jniInternal.h)
+ * VOID -> 0
+ * FLOAT -> 1
+ * DOUBLE -> 2
+ * S8 -> 3
+ * S4 -> 4
+ * H - target-specific hints (see below for details)
+ *
+ * IA32 ABI JNI hint format
+ *
+ * ZZZZ ZZZZZZZZ AAAAAAAA AAAAAAAA
+ *
+ * Z - reserved
+ * A - size of the variable argument block in 32-bit words
+ */
+
+ .text
+ .align 4
+ .global dvmPlatformInvoke
+ .type dvmPlatformInvoke, %function
+
+
+dvmPlatformInvoke:
+CallABI_ENTER:
+
+ /*
+ * Save registers.
+ */
+
+ movl %ebp, -4(%esp)
+ movl %ebx, -8(%esp) # save %ebx
+ movl %esi, -12(%esp) # save %esi
+ movl %edi, -16(%esp) # save %edi
+ lea (%esp), %ebp
+
+ /*
+ * Check if argInfo is valid. Is always valid so should remove this check?
+ */
+
+ movzwl 12(%ebp), %ecx # %ecx<- argsize in words
+ movl 12(%ebp), %ebx # %ebx<- argInfo
+
+ shl $2, %ecx # %ecx<- argsize in bytes
+ subl %ecx, %esp # %esp<- expanded for arg region
+
+ /*
+ * Prepare for 16 byte alignment
+ */
+
+ and $0xfffffff0, %esp
+ subl $24, %esp
+
+
+ movl 8(%ebp), %eax # %eax<- clazz
+ cmpl $0, %eax # Check virtual or static
+ movl 4(%ebp), %ecx # %ecx<- JNIEnv
+ movl 20(%ebp), %esi # %esi<- argV
+ jne 1f # Branch if static
+ movl (%esi), %eax # get the this pointer
+ addl $4, %esi # %esi<- update past this
+
+1:
+ movl %ecx, -8(%esp) # push JNIEnv as arg #1
+ movl %eax, -4(%esp) # push clazz or this as arg #2
+ lea -8(%esp), %esp
+
+ /*
+ * Copy arguments
+ */
+
+ movzwl %bx, %ecx # %ecx<- %bx; argsize in words
+ lea 8(%esp), %edi # %edi<- stack location for arguments
+ cld
+ rep movsl # move %ecx arguments to 8(%esp)
+ call *28(%ebp)
+ sarl $28, %ebx # %ebx<- SRRR (low 4 bits)
+ je CallABI_EXIT # exit call
+ cmpl $2, %ebx
+ movl 32(%ebp), %ecx # %ecx<- return pointer
+ je 2f # handle double return
+ jl 1f # handle float return
+
+ /*
+ * If a native function returns a result smaller than 8-bytes
+ * then higher bytes may contain garbage.
+ * This code does type-checking based on size of return result.
+ * We zero higher bytes instead of allowing the garbage to go through.
+ */
+
+ cmpl $3,%ebx
+ je S8
+ cmpl $4,%ebx
+ je S4
+ cmpl $7,%ebx
+ je S1
+ cmpl $6,%ebx
+ jne S2
+U2:
+ movzwl %ax, %eax
+ movl %eax, (%ecx) # save 32-bit return
+ jmp CallABI_EXIT # exit call
+
+S1:
+ movsbl %al, %eax
+ movl %eax, (%ecx) # save 32-bit return
+ jmp CallABI_EXIT # exit call
+S2:
+ movswl %ax, %eax
+ movl %eax, (%ecx) # save 32-bit return
+ jmp CallABI_EXIT # exit call
+S4:
+ cltd
+ movl %eax, (%ecx) # save 32-bit return
+ jmp CallABI_EXIT # exit call
+S8:
+ movl %edx, 4(%ecx) # save 64-bit return
+ movl %eax, (%ecx) # save 32-bit return
+ jmp CallABI_EXIT # exit call
+
+2:
+ fstpl (%ecx) # save double return
+ jmp CallABI_EXIT # exit call
+1:
+ fstps (%ecx) # save float return
+
+CallABI_EXIT:
+ lea (%ebp), %esp
+ movl -16(%ebp), %edi # restore %edi
+ movl -12(%ebp), %esi # restore %esi
+ movl -8(%ebp), %ebx # restore %ebx
+ movl -4(%ebp), %ebp # restore caller base pointer
+ ret # return
diff --git a/vm/arch/x86-atom/Hints386ABI.c b/vm/arch/x86-atom/Hints386ABI.c
new file mode 100644
index 0000000..dd2fc69
--- /dev/null
+++ b/vm/arch/x86-atom/Hints386ABI.c
@@ -0,0 +1,79 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * The class loader will associate with each method a 32-bit info word
+ * (jniArgInfo) to support JNI calls. The high order 4 bits of this word
+ * are the same for all targets, while the lower 28 are used for hints to
+ * allow accelerated JNI bridge transfers.
+ *
+ * jniArgInfo (32-bit int) layout:
+ *
+ * SRRRHHHH HHHHHHHH HHHHHHHH HHHHHHHH
+ *
+ * S - if set, ignore the hints and do things the hard way (scan signature)
+ * R - return-type enumeration
+ * H - target-specific hints (see below for details)
+ *
+ * This function produces IA32-specific hints for the standard 32-bit 386 ABI.
+ * All arguments have 32-bit alignment. Padding is not an issue.
+ *
+ * IA32 ABI JNI hint format
+ *
+ * ZZZZ ZZZZZZZZ AAAAAAAA AAAAAAAA
+ *
+ * Z - reserved, must be 0
+ * A - size of variable argument block in 32-bit words (note - does not
+ * include JNIEnv or clazz)
+ *
+ * For the 386 ABI, valid hints should always be generated.
+ */
+
+
+#include "Dalvik.h"
+#include "libdex/DexClass.h"
+#include <stdlib.h>
+#include <stddef.h>
+#include <sys/stat.h>
+
+u4 dvmPlatformInvokeHints(const DexProto* proto) {
+
+const char* sig = dexProtoGetShorty(proto);
+unsigned int wordCount = 0;
+char sigByte;
+
+ while (1) {
+
+ /*
+ * Move past return type; dereference sigByte
+ */
+
+ sigByte = *(++sig);
+ if (sigByte == '\0') { break; }
+ ++wordCount;
+
+ if (sigByte == 'D' || sigByte == 'J') {
+ ++wordCount;
+ }
+ }
+
+/*
+ * Check for Dex file limitation and return
+ */
+
+ if (wordCount > 0xFFFF) { return DALVIK_JNI_NO_ARG_INFO; }
+ return wordCount;
+
+}
diff --git a/vm/arch/x86/Call386ABI.S b/vm/arch/x86/Call386ABI.S
new file mode 100644
index 0000000..c98876c
--- /dev/null
+++ b/vm/arch/x86/Call386ABI.S
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * JNI method invocation. This is used to call a C/C++ JNI method. The
+ * argument list has to be pushed onto the native stack according to
+ * local calling conventions.
+ *
+ * This version supports 32-bit x86
+ */
+
+/*
+Function prototype:
+
+void dvmPlatformInvoke(void* pEnv, ClassObject* clazz, int argInfo, int argc,
+ const u4* argv, const char* signature, void* func, JValue* pReturn)
+
+The method we are calling has the form:
+
+ return_type func(JNIEnv* pEnv, ClassObject* clazz, ...)
+ -or-
+ return_type func(JNIEnv* pEnv, Object* this, ...)
+
+We receive a collection of 32-bit values which correspond to arguments from
+the interpreter (e.g. float occupies one, double occupies two). It's up to
+us to convert these into local calling conventions.
+*/
+
+/*
+x86 notes:
+
+The native code expects arguments on the stack, pushed from right to left.
+This matches what Dalvik is passing here.
+
+EAX, EDX and ECX are scratch.
+
+4-byte alignment is required for long long and double, so we won't pad
+
+Non-FP return types <= 4 bytes come back in EAX
+Non-FP return types of 8 bytes come back in EAX:EDX, with lsw in EAX.
+Float and double returned on top of FP stack.
+
+*/
+
+ .text
+ .global dvmPlatformInvoke
+ .type dvmPlatformInvoke, @function
+
+/*
+ * On entry:
+ * [ 8] arg0 JNIEnv (can be left alone)
+ * [12] arg1 clazz (NULL for virtual method calls, non-NULL for static)
+ * [16] arg2 arg info
+ * [20] arg3 argc
+ * [24] arg4 argv
+ * [28] arg5 short signature
+ * [32] arg6 func
+ * [36] arg7 pReturn
+ *
+ * For a virtual method call, the "this" reference is in argv[0].
+ *
+ * argInfo (32-bit int) layout:
+ * SRRRZZZZ ZZZZZZZZ AAAAAAAA AAAAAAAA
+ *
+ * Z - reserved
+ * S - if set, argInfo hints are invalid
+ * R - return type enumeration (see jniInternal.h)
+ * VOID -> 0
+ * FLOAT -> 1
+ * DOUBLE -> 2
+ * S8 -> 3
+ * S4 -> 4
+ * A - size of the variable argument block in 32-bit words
+ *
+ */
+dvmPlatformInvoke:
+/* Establish the frame pointer, spill & align to 16b */
+ pushl %ebp
+ movl %esp,%ebp
+ pushl %edi
+ pushl %esi
+ pushl %ebx
+ subl $12,%esp
+/* For 386 ABI, argInfo hints should always be valid. Abort if not. */
+ movl 16(%ebp),%ebx
+ testl %ebx,%ebx
+ js dvmAbort
+/* Get the size of the variable region and grow (preserving alignment) */
+ movl %ebx,%ecx
+ leal 12(,%ecx,4),%ecx
+ andl $0x0003FFF0,%ecx
+ subl %ecx,%esp
+/* Handle this/class */
+ movl 8(%ebp),%ecx
+ movl 12(%ebp),%eax
+ movl 24(%ebp),%esi
+ testl %eax,%eax
+ jne isClass
+ movl (%esi),%eax
+ addl $4,%esi
+isClass:
+ pushl %eax
+ pushl %ecx
+/* Now, copy the variable arguments region */
+ movl %ebx,%ecx
+ andl $0x0000FFFF,%ecx
+ leal 8(%esp),%edi
+ cld
+ rep
+ movsd
+/* Ready to go - call the native code */
+ call *32(%ebp)
+/* Store the result. */
+ sarl $28,%ebx
+ /* Is void? */
+ testl %ebx,%ebx
+ je cleanUpAndExit
+ movl 36(%ebp),%ecx
+ /* Is FP? */
+ cmpl $2,%ebx
+ jle isFP
+ cmpl $4,%ebx /* smaller than 32-bits? */
+ jg isSmall
+storeRetval:
+ /* Blindly storing 64-bits won't hurt 32-bit case */
+ movl %eax,(%ecx)
+ movl %edx,4(%ecx)
+ jmp cleanUpAndExit
+isSmall:
+ cmpl $7,%ebx /* S1? */
+ jne checkShort
+ movsbl %al,%eax
+ movl %eax,(%ecx)
+ jmp cleanUpAndExit
+checkShort:
+ cmpl $6,%ebx /* U2? */
+ jne isSignedShort
+ movzwl %ax,%eax
+ movl %eax,(%ecx)
+ jmp cleanUpAndExit
+isSignedShort:
+ /* Must be S2 */
+ movswl %ax,%eax
+ movl %eax,(%ecx)
+ jmp cleanUpAndExit
+isFP:
+ /* Is Float? */
+ cmpl $1,%ebx
+ je saveFloat
+ fstpl (%ecx)
+ jmp cleanUpAndExit
+saveFloat:
+ fstps (%ecx)
+cleanUpAndExit:
+ leal -12(%ebp),%esp
+ pop %ebx
+ pop %esi
+ pop %edi
+ pop %ebp
+ ret
+ .size dvmPlatformInvoke, .-dvmPlatformInvoke
+ .section .note.GNU-stack,"",@progbits
diff --git a/vm/arch/x86/Hints386ABI.c b/vm/arch/x86/Hints386ABI.c
new file mode 100644
index 0000000..2a0a1d8
--- /dev/null
+++ b/vm/arch/x86/Hints386ABI.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Target-specific optimization and run-time hints
+ */
+
+
+#include "Dalvik.h"
+#include "libdex/DexClass.h"
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <sys/stat.h>
+
+
+/*
+ * The class loader will associate with each method a 32-bit info word
+ * (jniArgInfo) to support JNI calls. The high order 4 bits of this word
+ * are the same for all targets, while the lower 28 are used for hints to
+ * allow accelerated JNI bridge transfers.
+ *
+ * jniArgInfo (32-bit int) layout:
+ *
+ * SRRRHHHH HHHHHHHH HHHHHHHH HHHHHHHH
+ *
+ * S - if set, ignore the hints and do things the hard way (scan signature)
+ * R - return-type enumeration
+ * H - target-specific hints (see below for details)
+ *
+ * This function produces x86-specific hints for the standard 32-bit 386 ABI.
+ * Note that the JNI requirements are very close to the 386 runtime model. In
+ * particular, natural datatype alignments do not apply to passed arguments.
+ * All arguments have 32-bit alignment. As a result, we don't have to worry
+ * about padding - just total size. The only tricky bit is that floating point
+ * return values come back on the FP stack.
+ *
+ *
+ * 386 ABI JNI hint format
+ *
+ * ZZZZ ZZZZZZZZ AAAAAAAA AAAAAAAA
+ *
+ * Z - reserved, must be 0
+ * A - size of variable argument block in 32-bit words (note - does not
+ * include JNIEnv or clazz)
+ *
+ * For the 386 ABI, valid hints should always be generated.
+ */
+u4 dvmPlatformInvokeHints( const DexProto* proto)
+{
+ const char* sig = dexProtoGetShorty(proto);
+ unsigned int jniHints, wordCount;
+ char sigByte;
+
+ wordCount = 0;
+ while (true) {
+ sigByte = *(sig++);
+
+ if (sigByte == '\0')
+ break;
+
+ wordCount++;
+
+ if (sigByte == 'D' || sigByte == 'J') {
+ wordCount++;
+ }
+ }
+
+ if (wordCount > 0xFFFF) {
+ /* Invalid - Dex file limitation */
+ jniHints = DALVIK_JNI_NO_ARG_INFO;
+ } else {
+ jniHints = wordCount;
+ }
+
+ return jniHints;
+}
diff --git a/vm/compiler/Compiler.c b/vm/compiler/Compiler.c
new file mode 100644
index 0000000..60f060c
--- /dev/null
+++ b/vm/compiler/Compiler.c
@@ -0,0 +1,755 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/mman.h>
+#include <errno.h>
+#include <cutils/ashmem.h>
+
+#include "Dalvik.h"
+#include "interp/Jit.h"
+#include "CompilerInternals.h"
+
+static inline bool workQueueLength(void)
+{
+ return gDvmJit.compilerQueueLength;
+}
+
+static CompilerWorkOrder workDequeue(void)
+{
+ assert(gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkDequeueIndex].kind
+ != kWorkOrderInvalid);
+ CompilerWorkOrder work =
+ gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkDequeueIndex];
+ gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkDequeueIndex++].kind =
+ kWorkOrderInvalid;
+ if (gDvmJit.compilerWorkDequeueIndex == COMPILER_WORK_QUEUE_SIZE) {
+ gDvmJit.compilerWorkDequeueIndex = 0;
+ }
+ gDvmJit.compilerQueueLength--;
+ if (gDvmJit.compilerQueueLength == 0) {
+ dvmSignalCond(&gDvmJit.compilerQueueEmpty);
+ }
+
+ /* Remember the high water mark of the queue length */
+ if (gDvmJit.compilerQueueLength > gDvmJit.compilerMaxQueued)
+ gDvmJit.compilerMaxQueued = gDvmJit.compilerQueueLength;
+
+ return work;
+}
+
+/*
+ * Attempt to enqueue a work order, returning true if successful.
+ * This routine will not block, but simply return if it couldn't
+ * aquire the lock or if the queue is full.
+ *
+ * NOTE: Make sure that the caller frees the info pointer if the return value
+ * is false.
+ */
+bool dvmCompilerWorkEnqueue(const u2 *pc, WorkOrderKind kind, void* info)
+{
+ int cc;
+ int i;
+ int numWork;
+ bool result = true;
+
+ if (dvmTryLockMutex(&gDvmJit.compilerLock)) {
+ return false; // Couldn't acquire the lock
+ }
+
+ /*
+ * Return if queue or code cache is full.
+ */
+ if (gDvmJit.compilerQueueLength == COMPILER_WORK_QUEUE_SIZE ||
+ gDvmJit.codeCacheFull == true) {
+ result = false;
+ goto unlockAndExit;
+ }
+
+ for (numWork = gDvmJit.compilerQueueLength,
+ i = gDvmJit.compilerWorkDequeueIndex;
+ numWork > 0;
+ numWork--) {
+ /* Already enqueued */
+ if (gDvmJit.compilerWorkQueue[i++].pc == pc)
+ goto unlockAndExit;
+ /* Wrap around */
+ if (i == COMPILER_WORK_QUEUE_SIZE)
+ i = 0;
+ }
+
+ CompilerWorkOrder *newOrder =
+ &gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkEnqueueIndex];
+ newOrder->pc = pc;
+ newOrder->kind = kind;
+ newOrder->info = info;
+ newOrder->result.methodCompilationAborted = NULL;
+ newOrder->result.codeAddress = NULL;
+ newOrder->result.discardResult =
+ (kind == kWorkOrderTraceDebug) ? true : false;
+ newOrder->result.requestingThread = dvmThreadSelf();
+
+ gDvmJit.compilerWorkEnqueueIndex++;
+ if (gDvmJit.compilerWorkEnqueueIndex == COMPILER_WORK_QUEUE_SIZE)
+ gDvmJit.compilerWorkEnqueueIndex = 0;
+ gDvmJit.compilerQueueLength++;
+ cc = pthread_cond_signal(&gDvmJit.compilerQueueActivity);
+ assert(cc == 0);
+
+unlockAndExit:
+ dvmUnlockMutex(&gDvmJit.compilerLock);
+ return result;
+}
+
+/* Block until the queue length is 0, or there is a pending suspend request */
+void dvmCompilerDrainQueue(void)
+{
+ Thread *self = dvmThreadSelf();
+
+ dvmLockMutex(&gDvmJit.compilerLock);
+ while (workQueueLength() != 0 && !gDvmJit.haltCompilerThread &&
+ self->suspendCount == 0) {
+ /*
+ * Use timed wait here - more than one mutator threads may be blocked
+ * but the compiler thread will only signal once when the queue is
+ * emptied. Furthermore, the compiler thread may have been shutdown
+ * so the blocked thread may never get the wakeup signal.
+ */
+ dvmRelativeCondWait(&gDvmJit.compilerQueueEmpty, &gDvmJit.compilerLock, 1000, 0);
+ }
+ dvmUnlockMutex(&gDvmJit.compilerLock);
+}
+
+bool dvmCompilerSetupCodeCache(void)
+{
+ extern void dvmCompilerTemplateStart(void);
+ extern void dmvCompilerTemplateEnd(void);
+ int fd;
+
+ /* Allocate the code cache */
+ fd = ashmem_create_region("dalvik-jit-code-cache", gDvmJit.codeCacheSize);
+ if (fd < 0) {
+ LOGE("Could not create %u-byte ashmem region for the JIT code cache",
+ gDvmJit.codeCacheSize);
+ return false;
+ }
+ gDvmJit.codeCache = mmap(NULL, gDvmJit.codeCacheSize,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_PRIVATE , fd, 0);
+ close(fd);
+ if (gDvmJit.codeCache == MAP_FAILED) {
+ LOGE("Failed to mmap the JIT code cache: %s\n", strerror(errno));
+ return false;
+ }
+
+ gDvmJit.pageSizeMask = getpagesize() - 1;
+
+ /* This can be found through "dalvik-jit-code-cache" in /proc/<pid>/maps */
+ // LOGD("Code cache starts at %p", gDvmJit.codeCache);
+
+ /* Copy the template code into the beginning of the code cache */
+ int templateSize = (intptr_t) dmvCompilerTemplateEnd -
+ (intptr_t) dvmCompilerTemplateStart;
+ memcpy((void *) gDvmJit.codeCache,
+ (void *) dvmCompilerTemplateStart,
+ templateSize);
+
+ /*
+ * Work around a CPU bug by keeping the 32-bit ARM handler code in its own
+ * page.
+ */
+ if (dvmCompilerInstructionSet() == DALVIK_JIT_THUMB2) {
+ templateSize = (templateSize + 4095) & ~4095;
+ }
+
+ gDvmJit.templateSize = templateSize;
+ gDvmJit.codeCacheByteUsed = templateSize;
+
+ /* Only flush the part in the code cache that is being used now */
+ cacheflush((intptr_t) gDvmJit.codeCache,
+ (intptr_t) gDvmJit.codeCache + templateSize, 0);
+
+ int result = mprotect(gDvmJit.codeCache, gDvmJit.codeCacheSize,
+ PROTECT_CODE_CACHE_ATTRS);
+
+ if (result == -1) {
+ LOGE("Failed to remove the write permission for the code cache");
+ dvmAbort();
+ }
+
+ return true;
+}
+
+static void crawlDalvikStack(Thread *thread, bool print)
+{
+ void *fp = thread->curFrame;
+ StackSaveArea* saveArea = NULL;
+ int stackLevel = 0;
+
+ if (print) {
+ LOGD("Crawling tid %d (%s / %p %s)", thread->systemTid,
+ dvmGetThreadStatusStr(thread->status),
+ thread->inJitCodeCache,
+ thread->inJitCodeCache ? "jit" : "interp");
+ }
+ /* Crawl the Dalvik stack frames to clear the returnAddr field */
+ while (fp != NULL) {
+ saveArea = SAVEAREA_FROM_FP(fp);
+
+ if (print) {
+ if (dvmIsBreakFrame(fp)) {
+ LOGD(" #%d: break frame (%p)",
+ stackLevel, saveArea->returnAddr);
+ }
+ else {
+ LOGD(" #%d: %s.%s%s (%p)",
+ stackLevel,
+ saveArea->method->clazz->descriptor,
+ saveArea->method->name,
+ dvmIsNativeMethod(saveArea->method) ?
+ " (native)" : "",
+ saveArea->returnAddr);
+ }
+ }
+ stackLevel++;
+ saveArea->returnAddr = NULL;
+ assert(fp != saveArea->prevFrame);
+ fp = saveArea->prevFrame;
+ }
+ /* Make sure the stack is fully unwound to the bottom */
+ assert(saveArea == NULL ||
+ (u1 *) (saveArea+1) == thread->interpStackStart);
+}
+
+static void resetCodeCache(void)
+{
+ Thread* thread;
+ u8 startTime = dvmGetRelativeTimeUsec();
+ int inJit = 0;
+ int byteUsed = gDvmJit.codeCacheByteUsed;
+
+ /* If any thread is found stuck in the JIT state, don't reset the cache */
+ for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+ /*
+ * Crawl the stack to wipe out the returnAddr field so that
+ * 1) the soon-to-be-deleted code in the JIT cache won't be used
+ * 2) or the thread stuck in the JIT land will soon return
+ * to the interpreter land
+ */
+ crawlDalvikStack(thread, false);
+ if (thread->inJitCodeCache) {
+ inJit++;
+ }
+ }
+
+ if (inJit) {
+ LOGD("JIT code cache reset delayed (%d bytes %d/%d)",
+ gDvmJit.codeCacheByteUsed, gDvmJit.numCodeCacheReset,
+ ++gDvmJit.numCodeCacheResetDelayed);
+ return;
+ }
+
+ /* Lock the mutex to clean up the work queue */
+ dvmLockMutex(&gDvmJit.compilerLock);
+
+ /* Drain the work queue to free the work orders */
+ while (workQueueLength()) {
+ CompilerWorkOrder work = workDequeue();
+ free(work.info);
+ }
+
+ /* Reset the JitEntry table contents to the initial unpopulated state */
+ dvmJitResetTable();
+
+ UNPROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
+ /*
+ * Wipe out the code cache content to force immediate crashes if
+ * stale JIT'ed code is invoked.
+ */
+ memset((char *) gDvmJit.codeCache + gDvmJit.templateSize,
+ 0,
+ gDvmJit.codeCacheByteUsed - gDvmJit.templateSize);
+ cacheflush((intptr_t) gDvmJit.codeCache,
+ (intptr_t) gDvmJit.codeCache + gDvmJit.codeCacheByteUsed, 0);
+
+ PROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
+
+ /* Reset the current mark of used bytes to the end of template code */
+ gDvmJit.codeCacheByteUsed = gDvmJit.templateSize;
+ gDvmJit.numCompilations = 0;
+
+ /* Reset the work queue */
+ memset(gDvmJit.compilerWorkQueue, 0,
+ sizeof(CompilerWorkOrder) * COMPILER_WORK_QUEUE_SIZE);
+ gDvmJit.compilerWorkEnqueueIndex = gDvmJit.compilerWorkDequeueIndex = 0;
+ gDvmJit.compilerQueueLength = 0;
+
+ /* Reset the IC patch work queue */
+ dvmLockMutex(&gDvmJit.compilerICPatchLock);
+ gDvmJit.compilerICPatchIndex = 0;
+ dvmUnlockMutex(&gDvmJit.compilerICPatchLock);
+
+ /* All clear now */
+ gDvmJit.codeCacheFull = false;
+
+ dvmUnlockMutex(&gDvmJit.compilerLock);
+
+ LOGD("JIT code cache reset in %lld ms (%d bytes %d/%d)",
+ (dvmGetRelativeTimeUsec() - startTime) / 1000,
+ byteUsed, ++gDvmJit.numCodeCacheReset,
+ gDvmJit.numCodeCacheResetDelayed);
+}
+
+/*
+ * Perform actions that are only safe when all threads are suspended. Currently
+ * we do:
+ * 1) Check if the code cache is full. If so reset it and restart populating it
+ * from scratch.
+ * 2) Patch predicted chaining cells by consuming recorded work orders.
+ */
+void dvmCompilerPerformSafePointChecks(void)
+{
+ if (gDvmJit.codeCacheFull) {
+ resetCodeCache();
+ }
+ dvmCompilerPatchInlineCache();
+}
+
+bool compilerThreadStartup(void)
+{
+ JitEntry *pJitTable = NULL;
+ unsigned char *pJitProfTable = NULL;
+ unsigned int i;
+
+ if (!dvmCompilerArchInit())
+ goto fail;
+
+ /*
+ * Setup the code cache if we have not inherited a valid code cache
+ * from the zygote.
+ */
+ if (gDvmJit.codeCache == NULL) {
+ if (!dvmCompilerSetupCodeCache())
+ goto fail;
+ }
+
+ /* Allocate the initial arena block */
+ if (dvmCompilerHeapInit() == false) {
+ goto fail;
+ }
+
+ dvmLockMutex(&gDvmJit.compilerLock);
+
+ /* Track method-level compilation statistics */
+ gDvmJit.methodStatsTable = dvmHashTableCreate(32, NULL);
+
+#if defined(WITH_JIT_TUNING)
+ gDvm.verboseShutdown = true;
+#endif
+
+ dvmUnlockMutex(&gDvmJit.compilerLock);
+
+ /* Set up the JitTable */
+
+ /* Power of 2? */
+ assert(gDvmJit.jitTableSize &&
+ !(gDvmJit.jitTableSize & (gDvmJit.jitTableSize - 1)));
+
+ dvmInitMutex(&gDvmJit.tableLock);
+ dvmLockMutex(&gDvmJit.tableLock);
+ pJitTable = (JitEntry*)
+ calloc(gDvmJit.jitTableSize, sizeof(*pJitTable));
+ if (!pJitTable) {
+ LOGE("jit table allocation failed\n");
+ dvmUnlockMutex(&gDvmJit.tableLock);
+ goto fail;
+ }
+ /*
+ * NOTE: the profile table must only be allocated once, globally.
+ * Profiling is turned on and off by nulling out gDvm.pJitProfTable
+ * and then restoring its original value. However, this action
+ * is not syncronized for speed so threads may continue to hold
+ * and update the profile table after profiling has been turned
+ * off by null'ng the global pointer. Be aware.
+ */
+ pJitProfTable = (unsigned char *)malloc(JIT_PROF_SIZE);
+ if (!pJitProfTable) {
+ LOGE("jit prof table allocation failed\n");
+ dvmUnlockMutex(&gDvmJit.tableLock);
+ goto fail;
+ }
+ memset(pJitProfTable, gDvmJit.threshold, JIT_PROF_SIZE);
+ for (i=0; i < gDvmJit.jitTableSize; i++) {
+ pJitTable[i].u.info.chain = gDvmJit.jitTableSize;
+ }
+ /* Is chain field wide enough for termination pattern? */
+ assert(pJitTable[0].u.info.chain == gDvmJit.jitTableSize);
+
+ gDvmJit.pJitEntryTable = pJitTable;
+ gDvmJit.jitTableMask = gDvmJit.jitTableSize - 1;
+ gDvmJit.jitTableEntriesUsed = 0;
+ gDvmJit.compilerHighWater =
+ COMPILER_WORK_QUEUE_SIZE - (COMPILER_WORK_QUEUE_SIZE/4);
+ /*
+ * If the VM is launched with wait-on-the-debugger, we will need to hide
+ * the profile table here
+ */
+ gDvmJit.pProfTable = dvmDebuggerOrProfilerActive() ? NULL : pJitProfTable;
+ gDvmJit.pProfTableCopy = pJitProfTable;
+ dvmUnlockMutex(&gDvmJit.tableLock);
+
+ /* Signal running threads to refresh their cached pJitTable pointers */
+ dvmSuspendAllThreads(SUSPEND_FOR_REFRESH);
+ dvmResumeAllThreads(SUSPEND_FOR_REFRESH);
+
+ /* Enable signature breakpoints by customizing the following code */
+#if defined(SIGNATURE_BREAKPOINT)
+ /*
+ * Suppose one sees the following native crash in the bugreport:
+ * I/DEBUG ( 1638): Build fingerprint: 'unknown'
+ * I/DEBUG ( 1638): pid: 2468, tid: 2507 >>> com.google.android.gallery3d
+ * I/DEBUG ( 1638): signal 11 (SIGSEGV), fault addr 00001400
+ * I/DEBUG ( 1638): r0 44ea7190 r1 44e4f7b8 r2 44ebc710 r3 00000000
+ * I/DEBUG ( 1638): r4 00000a00 r5 41862dec r6 4710dc10 r7 00000280
+ * I/DEBUG ( 1638): r8 ad010f40 r9 46a37a12 10 001116b0 fp 42a78208
+ * I/DEBUG ( 1638): ip 00000090 sp 4710dbc8 lr ad060e67 pc 46b90682
+ * cpsr 00000030
+ * I/DEBUG ( 1638): #00 pc 46b90682 /dev/ashmem/dalvik-jit-code-cache
+ * I/DEBUG ( 1638): #01 pc 00060e62 /system/lib/libdvm.so
+ *
+ * I/DEBUG ( 1638): code around pc:
+ * I/DEBUG ( 1638): 46b90660 6888d01c 34091dcc d2174287 4a186b68
+ * I/DEBUG ( 1638): 46b90670 d0052800 68006809 28004790 6b68d00e
+ * I/DEBUG ( 1638): 46b90680 512000bc 37016eaf 6ea866af 6f696028
+ * I/DEBUG ( 1638): 46b90690 682a6069 429a686b e003da08 6df1480b
+ * I/DEBUG ( 1638): 46b906a0 1c2d4788 47806d70 46a378fa 47806d70
+ *
+ * Clearly it is a JIT bug. To find out which translation contains the
+ * offending code, the content of the memory dump around the faulting PC
+ * can be pasted into the gDvmJit.signatureBreakpoint[] array and next time
+ * when a similar compilation is being created, the JIT compiler replay the
+ * trace in the verbose mode and one can investigate the instruction
+ * sequence in details.
+ *
+ * The length of the signature may need additional experiments to determine.
+ * The rule of thumb is don't include PC-relative instructions in the
+ * signature since it may be affected by the alignment of the compiled code.
+ * However, a signature that's too short might increase the chance of false
+ * positive matches. Using gdbjithelper to disassembly the memory content
+ * first might be a good companion approach.
+ *
+ * For example, if the next 4 words starting from 46b90680 is pasted into
+ * the data structure:
+ */
+
+ gDvmJit.signatureBreakpointSize = 4;
+ gDvmJit.signatureBreakpoint =
+ malloc(sizeof(u4) * gDvmJit.signatureBreakpointSize);
+ gDvmJit.signatureBreakpoint[0] = 0x512000bc;
+ gDvmJit.signatureBreakpoint[1] = 0x37016eaf;
+ gDvmJit.signatureBreakpoint[2] = 0x6ea866af;
+ gDvmJit.signatureBreakpoint[3] = 0x6f696028;
+
+ /*
+ * The following log will be printed when a match is found in subsequent
+ * testings:
+ *
+ * D/dalvikvm( 2468): Signature match starting from offset 0x34 (4 words)
+ * D/dalvikvm( 2468): --------
+ * D/dalvikvm( 2468): Compiler: Building trace for computeVisibleItems,
+ * offset 0x1f7
+ * D/dalvikvm( 2468): 0x46a37a12: 0x0090 add-int v42, v5, v26
+ * D/dalvikvm( 2468): 0x46a37a16: 0x004d aput-object v13, v14, v42
+ * D/dalvikvm( 2468): 0x46a37a1a: 0x0028 goto, (#0), (#0)
+ * D/dalvikvm( 2468): 0x46a3794e: 0x00d8 add-int/lit8 v26, v26, (#1)
+ * D/dalvikvm( 2468): 0x46a37952: 0x0028 goto, (#0), (#0)
+ * D/dalvikvm( 2468): 0x46a378ee: 0x0002 move/from16 v0, v26, (#0)
+ * D/dalvikvm( 2468): 0x46a378f2: 0x0002 move/from16 v1, v29, (#0)
+ * D/dalvikvm( 2468): 0x46a378f6: 0x0035 if-ge v0, v1, (#10)
+ * D/dalvikvm( 2468): TRACEINFO (554): 0x46a37624
+ * Lcom/cooliris/media/GridLayer;computeVisibleItems 0x1f7 14 of 934, 8
+ * blocks
+ * :
+ * :
+ * D/dalvikvm( 2468): 0x20 (0020): ldr r0, [r5, #52]
+ * D/dalvikvm( 2468): 0x22 (0022): ldr r2, [pc, #96]
+ * D/dalvikvm( 2468): 0x24 (0024): cmp r0, #0
+ * D/dalvikvm( 2468): 0x26 (0026): beq 0x00000034
+ * D/dalvikvm( 2468): 0x28 (0028): ldr r1, [r1, #0]
+ * D/dalvikvm( 2468): 0x2a (002a): ldr r0, [r0, #0]
+ * D/dalvikvm( 2468): 0x2c (002c): blx r2
+ * D/dalvikvm( 2468): 0x2e (002e): cmp r0, #0
+ * D/dalvikvm( 2468): 0x30 (0030): beq 0x00000050
+ * D/dalvikvm( 2468): 0x32 (0032): ldr r0, [r5, #52]
+ * D/dalvikvm( 2468): 0x34 (0034): lsls r4, r7, #2
+ * D/dalvikvm( 2468): 0x36 (0036): str r0, [r4, r4]
+ * D/dalvikvm( 2468): -------- dalvik offset: 0x01fb @ goto, (#0), (#0)
+ * D/dalvikvm( 2468): L0x0195:
+ * D/dalvikvm( 2468): -------- dalvik offset: 0x0195 @ add-int/lit8 v26,
+ * v26, (#1)
+ * D/dalvikvm( 2468): 0x38 (0038): ldr r7, [r5, #104]
+ * D/dalvikvm( 2468): 0x3a (003a): adds r7, r7, #1
+ * D/dalvikvm( 2468): 0x3c (003c): str r7, [r5, #104]
+ * D/dalvikvm( 2468): -------- dalvik offset: 0x0197 @ goto, (#0), (#0)
+ * D/dalvikvm( 2468): L0x0165:
+ * D/dalvikvm( 2468): -------- dalvik offset: 0x0165 @ move/from16 v0, v26,
+ * (#0)
+ * D/dalvikvm( 2468): 0x3e (003e): ldr r0, [r5, #104]
+ * D/dalvikvm( 2468): 0x40 (0040): str r0, [r5, #0]
+ *
+ * The "str r0, [r4, r4]" is indeed the culprit of the native crash.
+ */
+#endif
+
+ return true;
+
+fail:
+ return false;
+
+}
+
+static void *compilerThreadStart(void *arg)
+{
+ dvmChangeStatus(NULL, THREAD_VMWAIT);
+
+ /*
+ * If we're not running stand-alone, wait a little before
+ * recieving translation requests on the assumption that process start
+ * up code isn't worth compiling. We'll resume when the framework
+ * signals us that the first screen draw has happened, or the timer
+ * below expires (to catch daemons).
+ *
+ * There is a theoretical race between the callback to
+ * VMRuntime.startJitCompiation and when the compiler thread reaches this
+ * point. In case the callback happens earlier, in order not to permanently
+ * hold the system_server (which is not using the timed wait) in
+ * interpreter-only mode we bypass the delay here.
+ */
+ if (gDvmJit.runningInAndroidFramework &&
+ !gDvmJit.alreadyEnabledViaFramework) {
+ /*
+ * If the current VM instance is the system server (detected by having
+ * 0 in gDvm.systemServerPid), we will use the indefinite wait on the
+ * conditional variable to determine whether to start the JIT or not.
+ * If the system server detects that the whole system is booted in
+ * safe mode, the conditional variable will never be signaled and the
+ * system server will remain in the interpreter-only mode. All
+ * subsequent apps will be started with the --enable-safemode flag
+ * explicitly appended.
+ */
+ if (gDvm.systemServerPid == 0) {
+ dvmLockMutex(&gDvmJit.compilerLock);
+ pthread_cond_wait(&gDvmJit.compilerQueueActivity,
+ &gDvmJit.compilerLock);
+ dvmUnlockMutex(&gDvmJit.compilerLock);
+ LOGD("JIT started for system_server");
+ } else {
+ dvmLockMutex(&gDvmJit.compilerLock);
+ /*
+ * TUNING: experiment with the delay & perhaps make it
+ * target-specific
+ */
+ dvmRelativeCondWait(&gDvmJit.compilerQueueActivity,
+ &gDvmJit.compilerLock, 3000, 0);
+ dvmUnlockMutex(&gDvmJit.compilerLock);
+ }
+ if (gDvmJit.haltCompilerThread) {
+ return NULL;
+ }
+ }
+
+ compilerThreadStartup();
+
+ dvmLockMutex(&gDvmJit.compilerLock);
+ /*
+ * Since the compiler thread will not touch any objects on the heap once
+ * being created, we just fake its state as VMWAIT so that it can be a
+ * bit late when there is suspend request pending.
+ */
+ while (!gDvmJit.haltCompilerThread) {
+ if (workQueueLength() == 0) {
+ int cc;
+ cc = pthread_cond_signal(&gDvmJit.compilerQueueEmpty);
+ assert(cc == 0);
+ pthread_cond_wait(&gDvmJit.compilerQueueActivity,
+ &gDvmJit.compilerLock);
+ continue;
+ } else {
+ do {
+ CompilerWorkOrder work = workDequeue();
+ dvmUnlockMutex(&gDvmJit.compilerLock);
+#if defined(WITH_JIT_TUNING)
+ u8 startTime = dvmGetRelativeTimeUsec();
+#endif
+ /*
+ * Check whether there is a suspend request on me. This
+ * is necessary to allow a clean shutdown.
+ *
+ * However, in the blocking stress testing mode, let the
+ * compiler thread continue doing compilations to unblock
+ * other requesting threads. This may occasionally cause
+ * shutdown from proceeding cleanly in the standalone invocation
+ * of the vm but this should be acceptable.
+ */
+ if (!gDvmJit.blockingMode)
+ dvmCheckSuspendPending(dvmThreadSelf());
+ /* Is JitTable filling up? */
+ if (gDvmJit.jitTableEntriesUsed >
+ (gDvmJit.jitTableSize - gDvmJit.jitTableSize/4)) {
+ bool resizeFail =
+ dvmJitResizeJitTable(gDvmJit.jitTableSize * 2);
+ /*
+ * If the jit table is full, consider it's time to reset
+ * the code cache too.
+ */
+ gDvmJit.codeCacheFull |= resizeFail;
+ }
+ if (gDvmJit.haltCompilerThread) {
+ LOGD("Compiler shutdown in progress - discarding request");
+ } else if (!gDvmJit.codeCacheFull) {
+ bool compileOK = false;
+ jmp_buf jmpBuf;
+ work.bailPtr = &jmpBuf;
+ bool aborted = setjmp(jmpBuf);
+ if (!aborted) {
+ compileOK = dvmCompilerDoWork(&work);
+ }
+ if (aborted || !compileOK) {
+ dvmCompilerArenaReset();
+ } else if (!work.result.discardResult &&
+ work.result.codeAddress) {
+ /* Make sure that proper code addr is installed */
+ assert(work.result.codeAddress != NULL);
+ dvmJitSetCodeAddr(work.pc, work.result.codeAddress,
+ work.result.instructionSet);
+ }
+ }
+ free(work.info);
+#if defined(WITH_JIT_TUNING)
+ gDvmJit.jitTime += dvmGetRelativeTimeUsec() - startTime;
+#endif
+ dvmLockMutex(&gDvmJit.compilerLock);
+ } while (workQueueLength() != 0);
+ }
+ }
+ pthread_cond_signal(&gDvmJit.compilerQueueEmpty);
+ dvmUnlockMutex(&gDvmJit.compilerLock);
+
+ /*
+ * As part of detaching the thread we need to call into Java code to update
+ * the ThreadGroup, and we should not be in VMWAIT state while executing
+ * interpreted code.
+ */
+ dvmChangeStatus(NULL, THREAD_RUNNING);
+
+ if (gDvm.verboseShutdown)
+ LOGD("Compiler thread shutting down\n");
+ return NULL;
+}
+
+bool dvmCompilerStartup(void)
+{
+
+ dvmInitMutex(&gDvmJit.compilerLock);
+ dvmInitMutex(&gDvmJit.compilerICPatchLock);
+ dvmInitMutex(&gDvmJit.codeCacheProtectionLock);
+ dvmLockMutex(&gDvmJit.compilerLock);
+ pthread_cond_init(&gDvmJit.compilerQueueActivity, NULL);
+ pthread_cond_init(&gDvmJit.compilerQueueEmpty, NULL);
+
+ /* Reset the work queue */
+ gDvmJit.compilerWorkEnqueueIndex = gDvmJit.compilerWorkDequeueIndex = 0;
+ gDvmJit.compilerQueueLength = 0;
+ dvmUnlockMutex(&gDvmJit.compilerLock);
+
+ /*
+ * Defer rest of initialization until we're sure JIT'ng makes sense. Launch
+ * the compiler thread, which will do the real initialization if and
+ * when it is signalled to do so.
+ */
+ return dvmCreateInternalThread(&gDvmJit.compilerHandle, "Compiler",
+ compilerThreadStart, NULL);
+}
+
+void dvmCompilerShutdown(void)
+{
+ void *threadReturn;
+
+ /* Disable new translation requests */
+ gDvmJit.pProfTable = NULL;
+ gDvmJit.pProfTableCopy = NULL;
+
+ if (gDvm.verboseShutdown) {
+ dvmCompilerDumpStats();
+ while (gDvmJit.compilerQueueLength)
+ sleep(5);
+ }
+
+ if (gDvmJit.compilerHandle) {
+
+ gDvmJit.haltCompilerThread = true;
+
+ dvmLockMutex(&gDvmJit.compilerLock);
+ pthread_cond_signal(&gDvmJit.compilerQueueActivity);
+ dvmUnlockMutex(&gDvmJit.compilerLock);
+
+ if (pthread_join(gDvmJit.compilerHandle, &threadReturn) != 0)
+ LOGW("Compiler thread join failed\n");
+ else if (gDvm.verboseShutdown)
+ LOGD("Compiler thread has shut down\n");
+ }
+
+ /* Break loops within the translation cache */
+ dvmJitUnchainAll();
+
+ /*
+ * NOTE: our current implementatation doesn't allow for the compiler
+ * thread to be restarted after it exits here. We aren't freeing
+ * the JitTable or the ProfTable because threads which still may be
+ * running or in the process of shutting down may hold references to
+ * them.
+ */
+}
+
+void dvmCompilerStateRefresh()
+{
+ bool jitActive;
+ bool jitActivate;
+ bool needUnchain = false;
+
+ /*
+ * The tableLock might not be initialized yet by the compiler thread if
+ * debugger is attached from the very beginning of the VM launch. If
+ * pProfTableCopy is NULL, the lock is not initialized yet and we don't
+ * need to refresh anything either.
+ */
+ if (gDvmJit.pProfTableCopy == NULL) {
+ return;
+ }
+
+ dvmLockMutex(&gDvmJit.tableLock);
+ jitActive = gDvmJit.pProfTable != NULL;
+ jitActivate = !(gDvm.debuggerActive || (gDvm.activeProfilers > 0));
+
+ if (jitActivate && !jitActive) {
+ gDvmJit.pProfTable = gDvmJit.pProfTableCopy;
+ } else if (!jitActivate && jitActive) {
+ gDvmJit.pProfTable = NULL;
+ needUnchain = true;
+ }
+ dvmUnlockMutex(&gDvmJit.tableLock);
+ if (needUnchain)
+ dvmJitUnchainAll();
+}
diff --git a/vm/compiler/Compiler.h b/vm/compiler/Compiler.h
new file mode 100644
index 0000000..739d517
--- /dev/null
+++ b/vm/compiler/Compiler.h
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <Thread.h>
+#include <setjmp.h>
+
+#ifndef _DALVIK_VM_COMPILER
+#define _DALVIK_VM_COMPILER
+
+/*
+ * Uncomment the following to enable JIT signature breakpoint
+ * #define SIGNATURE_BREAKPOINT
+ */
+
+#define MAX_JIT_RUN_LEN 64
+#define COMPILER_WORK_QUEUE_SIZE 100
+#define COMPILER_IC_PATCH_QUEUE_SIZE 64
+
+/* Architectural-independent parameters for predicted chains */
+#define PREDICTED_CHAIN_CLAZZ_INIT 0
+#define PREDICTED_CHAIN_METHOD_INIT 0
+#define PREDICTED_CHAIN_COUNTER_INIT 0
+/* A fake value which will avoid initialization and won't match any class */
+#define PREDICTED_CHAIN_FAKE_CLAZZ 0xdeadc001
+/* Has to be positive */
+#define PREDICTED_CHAIN_COUNTER_AVOID 0x7fffffff
+/* Rechain after this many misses - shared globally and has to be positive */
+#define PREDICTED_CHAIN_COUNTER_RECHAIN 8192
+
+#define COMPILER_TRACED(X)
+#define COMPILER_TRACEE(X)
+#define COMPILER_TRACE_CHAINING(X)
+
+/* Macro to change the permissions applied to a chunk of the code cache */
+#if !defined(WITH_JIT_TUNING)
+#define PROTECT_CODE_CACHE_ATTRS (PROT_READ | PROT_EXEC)
+#define UNPROTECT_CODE_CACHE_ATTRS (PROT_READ | PROT_EXEC | PROT_WRITE)
+#else
+/* When doing JIT profiling always grant the write permission */
+#define PROTECT_CODE_CACHE_ATTRS (PROT_READ | PROT_EXEC | \
+ (gDvmJit.profile ? PROT_WRITE : 0))
+#define UNPROTECT_CODE_CACHE_ATTRS (PROT_READ | PROT_EXEC | PROT_WRITE)
+#endif
+
+/* Acquire the lock before removing PROT_WRITE from the specified mem region */
+#define UNPROTECT_CODE_CACHE(addr, size) \
+ { \
+ dvmLockMutex(&gDvmJit.codeCacheProtectionLock); \
+ mprotect((void *) (((intptr_t) (addr)) & ~gDvmJit.pageSizeMask), \
+ (size) + (((intptr_t) (addr)) & gDvmJit.pageSizeMask), \
+ (UNPROTECT_CODE_CACHE_ATTRS)); \
+ }
+
+/* Add the PROT_WRITE to the specified memory region then release the lock */
+#define PROTECT_CODE_CACHE(addr, size) \
+ { \
+ mprotect((void *) (((intptr_t) (addr)) & ~gDvmJit.pageSizeMask), \
+ (size) + (((intptr_t) (addr)) & gDvmJit.pageSizeMask), \
+ (PROTECT_CODE_CACHE_ATTRS)); \
+ dvmUnlockMutex(&gDvmJit.codeCacheProtectionLock); \
+ }
+
+#define SINGLE_STEP_OP(opcode) \
+ (gDvmJit.includeSelectedOp != \
+ ((gDvmJit.opList[opcode >> 3] & (1 << (opcode & 0x7))) != 0))
+
+typedef enum JitInstructionSetType {
+ DALVIK_JIT_NONE = 0,
+ DALVIK_JIT_ARM,
+ DALVIK_JIT_THUMB,
+ DALVIK_JIT_THUMB2,
+ DALVIK_JIT_THUMB2EE,
+ DALVIK_JIT_X86
+} JitInstructionSetType;
+
+/* Description of a compiled trace. */
+typedef struct JitTranslationInfo {
+ void *codeAddress;
+ JitInstructionSetType instructionSet;
+ bool discardResult; // Used for debugging divergence and IC patching
+ bool methodCompilationAborted; // Cannot compile the whole method
+ Thread *requestingThread; // For debugging purpose
+} JitTranslationInfo;
+
+typedef enum WorkOrderKind {
+ kWorkOrderInvalid = 0, // Should never see by the backend
+ kWorkOrderMethod = 1, // Work is to compile a whole method
+ kWorkOrderTrace = 2, // Work is to compile code fragment(s)
+ kWorkOrderTraceDebug = 3, // Work is to compile/debug code fragment(s)
+} WorkOrderKind;
+
+typedef struct CompilerWorkOrder {
+ const u2* pc;
+ WorkOrderKind kind;
+ void* info;
+ JitTranslationInfo result;
+ jmp_buf *bailPtr;
+} CompilerWorkOrder;
+
+/* Chain cell for predicted method invocation */
+typedef struct PredictedChainingCell {
+ u4 branch; /* Branch to chained destination */
+ const ClassObject *clazz; /* key for prediction */
+ const Method *method; /* to lookup native PC from dalvik PC */
+ const ClassObject *stagedClazz; /* possible next key for prediction */
+} PredictedChainingCell;
+
+/* Work order for inline cache patching */
+typedef struct ICPatchWorkOrder {
+ PredictedChainingCell *cellAddr; /* Address to be patched */
+ PredictedChainingCell cellContent; /* content of the new cell */
+} ICPatchWorkOrder;
+
+/* States of the dbg interpreter when serving a JIT-related request */
+typedef enum JitState {
+ /* Entering states in the debug interpreter */
+ kJitNot = 0, // Non-JIT related reasons */
+ kJitTSelectRequest = 1, // Request a trace (subject to filtering)
+ kJitTSelectRequestHot = 2, // Request a hot trace (bypass the filter)
+ kJitSelfVerification = 3, // Self Verification Mode
+
+ /* Operational states in the debug interpreter */
+ kJitTSelect = 4, // Actively selecting a trace
+ kJitTSelectEnd = 5, // Done with the trace - wrap it up
+ kJitSingleStep = 6, // Single step interpretation
+ kJitSingleStepEnd = 7, // Done with single step, ready return to mterp
+ kJitDone = 8, // Ready to leave the debug interpreter
+} JitState;
+
+#if defined(WITH_SELF_VERIFICATION)
+typedef enum SelfVerificationState {
+ kSVSIdle = 0, // Idle
+ kSVSStart = 1, // Shadow space set up, running compiled code
+ kSVSPunt = 2, // Exiting compiled code by punting
+ kSVSSingleStep = 3, // Exiting compiled code by single stepping
+ kSVSNoProfile = 4, // Exiting compiled code and don't collect profiles
+ kSVSTraceSelect = 5, // Exiting compiled code and compile the next pc
+ kSVSNormal = 6, // Exiting compiled code normally
+ kSVSNoChain = 7, // Exiting compiled code by no chain
+ kSVSBackwardBranch = 8, // Exiting compiled code with backward branch trace
+ kSVSDebugInterp = 9, // Normal state restored, running debug interpreter
+} SelfVerificationState;
+#endif
+
+typedef enum JitHint {
+ kJitHintNone = 0,
+ kJitHintTaken = 1, // Last inst in run was taken branch
+ kJitHintNotTaken = 2, // Last inst in run was not taken branch
+ kJitHintNoBias = 3, // Last inst in run was unbiased branch
+} jitHint;
+
+/*
+ * Element of a Jit trace description. If the isCode bit is set, it describes
+ * a contiguous sequence of Dalvik byte codes.
+ */
+typedef struct {
+ unsigned isCode:1; // If set denotes code fragments
+ unsigned numInsts:8; // Number of Byte codes in run
+ unsigned runEnd:1; // Run ends with last byte code
+ jitHint hint:6; // Hint to apply to final code of run
+ u2 startOffset; // Starting offset for trace run
+} JitCodeDesc;
+
+/*
+ * A complete list of trace runs passed to the compiler looks like the
+ * following:
+ * frag1
+ * frag2
+ * frag3
+ * meta1
+ * meta2
+ * frag4
+ *
+ * frags 1-4 have the "isCode" field set, and metas 1-2 are plain pointers or
+ * pointers to auxiliary data structures as long as the LSB is null.
+ * The meaning of the meta content is loosely defined. It is usually the code
+ * fragment right before the first meta field (frag3 in this case) to
+ * understand and parse them. Frag4 could be a dummy one with 0 "numInsts" but
+ * the "runEnd" field set.
+ *
+ * For example, if a trace run contains a method inlining target, the class
+ * type of "this" and the currently resolved method pointer are two instances
+ * of meta information stored there.
+ */
+typedef union {
+ JitCodeDesc frag;
+ void* meta;
+} JitTraceRun;
+
+/*
+ * Trace description as will appear in the translation cache. Note
+ * flexible array at end, as these will be of variable size. To
+ * conserve space in the translation cache, total length of JitTraceRun
+ * array must be recomputed via seqential scan if needed.
+ */
+typedef struct {
+ const Method* method;
+ JitTraceRun trace[0]; // Variable-length trace descriptors
+} JitTraceDescription;
+
+typedef enum JitMethodAttributes {
+ kIsCallee = 0, /* Code is part of a callee (invoked by a hot trace) */
+ kIsHot, /* Code is part of a hot trace */
+ kIsLeaf, /* Method is leaf */
+ kIsEmpty, /* Method is empty */
+ kIsThrowFree, /* Method doesn't throw */
+ kIsGetter, /* Method fits the getter pattern */
+ kIsSetter, /* Method fits the setter pattern */
+} JitMethodAttributes;
+
+#define METHOD_IS_CALLEE (1 << kIsCallee)
+#define METHOD_IS_HOT (1 << kIsHot)
+#define METHOD_IS_LEAF (1 << kIsLeaf)
+#define METHOD_IS_EMPTY (1 << kIsEmpty)
+#define METHOD_IS_THROW_FREE (1 << kIsThrowFree)
+#define METHOD_IS_GETTER (1 << kIsGetter)
+#define METHOD_IS_SETTER (1 << kIsSetter)
+
+/* Vectors to provide optimization hints */
+typedef enum JitOptimizationHints {
+ kJitOptNoLoop = 0, // Disable loop formation/optimization
+} JitOptimizationHints;
+
+#define JIT_OPT_NO_LOOP (1 << kJitOptNoLoop)
+
+typedef struct CompilerMethodStats {
+ const Method *method; // Used as hash entry signature
+ int dalvikSize; // # of bytes for dalvik bytecodes
+ int compiledDalvikSize; // # of compiled dalvik bytecodes
+ int nativeSize; // # of bytes for produced native code
+ int attributes; // attribute vector
+} CompilerMethodStats;
+
+struct CompilationUnit;
+struct BasicBlock;
+struct SSARepresentation;
+struct GrowableList;
+struct JitEntry;
+struct MIR;
+
+bool dvmCompilerSetupCodeCache(void);
+bool dvmCompilerArchInit(void);
+void dvmCompilerArchDump(void);
+bool dvmCompilerStartup(void);
+void dvmCompilerShutdown(void);
+bool dvmCompilerWorkEnqueue(const u2* pc, WorkOrderKind kind, void* info);
+void *dvmCheckCodeCache(void *method);
+CompilerMethodStats *dvmCompilerAnalyzeMethodBody(const Method *method,
+ bool isCallee);
+bool dvmCompilerCanIncludeThisInstruction(const Method *method,
+ const DecodedInstruction *insn);
+bool dvmCompileMethod(struct CompilationUnit *cUnit, const Method *method,
+ JitTranslationInfo *info);
+bool dvmCompileTrace(JitTraceDescription *trace, int numMaxInsts,
+ JitTranslationInfo *info, jmp_buf *bailPtr, int optHints);
+void dvmCompilerDumpStats(void);
+void dvmCompilerDrainQueue(void);
+void dvmJitUnchainAll(void);
+void dvmCompilerSortAndPrintTraceProfiles(void);
+void dvmCompilerPerformSafePointChecks(void);
+void dvmCompilerInlineMIR(struct CompilationUnit *cUnit);
+void dvmInitializeSSAConversion(struct CompilationUnit *cUnit);
+int dvmConvertSSARegToDalvik(struct CompilationUnit *cUnit, int ssaReg);
+bool dvmCompilerLoopOpt(struct CompilationUnit *cUnit);
+void dvmCompilerNonLoopAnalysis(struct CompilationUnit *cUnit);
+void dvmCompilerFindLiveIn(struct CompilationUnit *cUnit,
+ struct BasicBlock *bb);
+void dvmCompilerDoSSAConversion(struct CompilationUnit *cUnit,
+ struct BasicBlock *bb);
+void dvmCompilerDoConstantPropagation(struct CompilationUnit *cUnit,
+ struct BasicBlock *bb);
+void dvmCompilerFindInductionVariables(struct CompilationUnit *cUnit,
+ struct BasicBlock *bb);
+char *dvmCompilerGetDalvikDisassembly(DecodedInstruction *insn, char *note);
+char *dvmCompilerGetSSAString(struct CompilationUnit *cUnit,
+ struct SSARepresentation *ssaRep);
+void dvmCompilerDataFlowAnalysisDispatcher(struct CompilationUnit *cUnit,
+ void (*func)(struct CompilationUnit *, struct BasicBlock *));
+void dvmCompilerStateRefresh(void);
+JitTraceDescription *dvmCopyTraceDescriptor(const u2 *pc,
+ const struct JitEntry *desc);
+void *dvmCompilerGetInterpretTemplate();
+#endif /* _DALVIK_VM_COMPILER */
diff --git a/vm/compiler/CompilerIR.h b/vm/compiler/CompilerIR.h
new file mode 100644
index 0000000..82b97e5
--- /dev/null
+++ b/vm/compiler/CompilerIR.h
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DALVIK_VM_COMPILER_IR
+#define _DALVIK_VM_COMPILER_IR
+
+#include "codegen/Optimizer.h"
+
+typedef enum RegisterClass {
+ kCoreReg,
+ kFPReg,
+ kAnyReg,
+} RegisterClass;
+
+typedef enum RegLocationType {
+ kLocDalvikFrame = 0,
+ kLocPhysReg,
+ kLocRetval, // Return region in interpState
+ kLocSpill,
+} RegLocationType;
+
+typedef struct RegLocation {
+ RegLocationType location:2;
+ unsigned wide:1;
+ unsigned fp:1; // Hint for float/double
+ u1 lowReg:6; // First physical register
+ u1 highReg:6; // 2nd physical register (if wide)
+ s2 sRegLow; // SSA name for low Dalvik word
+} RegLocation;
+
+#define INVALID_SREG (-1)
+#define INVALID_REG (-1)
+
+typedef enum BBType {
+ /* For coding convenience reasons chaining cell types should appear first */
+ kChainingCellNormal = 0,
+ kChainingCellHot,
+ kChainingCellInvokeSingleton,
+ kChainingCellInvokePredicted,
+ kChainingCellBackwardBranch,
+ kChainingCellGap,
+ /* Don't insert new fields between Gap and Last */
+ kChainingCellLast = kChainingCellGap + 1,
+ kMethodEntryBlock,
+ kTraceEntryBlock,
+ kDalvikByteCode,
+ kTraceExitBlock,
+ kMethodExitBlock,
+ kPCReconstruction,
+ kExceptionHandling,
+} BBType;
+
+typedef struct ChainCellCounts {
+ union {
+ u1 count[kChainingCellLast]; /* include one more space for the gap # */
+ u4 dummyForAlignment;
+ } u;
+} ChainCellCounts;
+
+typedef struct LIR {
+ int offset;
+ struct LIR *next;
+ struct LIR *prev;
+ struct LIR *target;
+} LIR;
+
+enum ExtendedMIROpcode {
+ kMirOpFirst = 256,
+ kMirOpPhi = kMirOpFirst,
+ kMirOpNullNRangeUpCheck,
+ kMirOpNullNRangeDownCheck,
+ kMirOpLowerBound,
+ kMirOpPunt,
+ kMirOpCheckInlinePrediction, // Gen checks for predicted inlining
+ kMirOpLast,
+};
+
+struct SSARepresentation;
+
+typedef enum {
+ kMIRIgnoreNullCheck = 0,
+ kMIRNullCheckOnly,
+ kMIRIgnoreRangeCheck,
+ kMIRRangeCheckOnly,
+ kMIRInlined, // Invoke is inlined (ie dead)
+ kMIRInlinedPred, // Invoke is inlined via prediction
+ kMIRCallee, // Instruction is inlined from callee
+} MIROptimizationFlagPositons;
+
+#define MIR_IGNORE_NULL_CHECK (1 << kMIRIgnoreNullCheck)
+#define MIR_NULL_CHECK_ONLY (1 << kMIRNullCheckOnly)
+#define MIR_IGNORE_RANGE_CHECK (1 << kMIRIgnoreRangeCheck)
+#define MIR_RANGE_CHECK_ONLY (1 << kMIRRangeCheckOnly)
+#define MIR_INLINED (1 << kMIRInlined)
+#define MIR_INLINED_PRED (1 << kMIRInlinedPred)
+#define MIR_CALLEE (1 << kMIRCallee)
+
+typedef struct CallsiteInfo {
+ const ClassObject *clazz;
+ const Method *method;
+ LIR *misPredBranchOver;
+} CallsiteInfo;
+
+typedef struct MIR {
+ DecodedInstruction dalvikInsn;
+ unsigned int width;
+ unsigned int offset;
+ struct MIR *prev;
+ struct MIR *next;
+ struct SSARepresentation *ssaRep;
+ int OptimizationFlags;
+ int seqNum;
+ union {
+ // Used by the inlined insn from the callee to find the mother method
+ const Method *calleeMethod;
+ // Used by the inlined invoke to find the class and method pointers
+ CallsiteInfo *callsiteInfo;
+ } meta;
+} MIR;
+
+struct BasicBlockDataFlow;
+
+typedef struct BasicBlock {
+ int id;
+ int visited;
+ unsigned int startOffset;
+ const Method *containingMethod; // For blocks from the callee
+ BBType blockType;
+ bool needFallThroughBranch; // For blocks ended due to length limit
+ bool isFallThroughFromInvoke; // True means the block needs alignment
+ MIR *firstMIRInsn;
+ MIR *lastMIRInsn;
+ struct BasicBlock *fallThrough;
+ struct BasicBlock *taken;
+ struct BasicBlock *next; // Serial link for book keeping purposes
+ struct BasicBlockDataFlow *dataFlowInfo;
+} BasicBlock;
+
+struct LoopAnalysis;
+struct RegisterPool;
+
+typedef enum AssemblerStatus {
+ kSuccess,
+ kRetryAll,
+ kRetryHalve
+} AssemblerStatus;
+
+typedef struct CompilationUnit {
+ int numInsts;
+ int numBlocks;
+ BasicBlock **blockList;
+ const Method *method;
+ const JitTraceDescription *traceDesc;
+ LIR *firstLIRInsn;
+ LIR *lastLIRInsn;
+ LIR *wordList;
+ LIR *chainCellOffsetLIR;
+ GrowableList pcReconstructionList;
+ int headerSize; // bytes before the first code ptr
+ int dataOffset; // starting offset of literal pool
+ int totalSize; // header + code size
+ AssemblerStatus assemblerStatus; // Success or fix and retry
+ int assemblerRetries; // How many times tried to fix assembly
+ unsigned char *codeBuffer;
+ void *baseAddr;
+ bool printMe;
+ bool allSingleStep;
+ bool executionCount; // Add code to count trace executions
+ bool hasLoop; // Contains a loop
+ bool hasInvoke; // Contains an invoke instruction
+ bool heapMemOp; // Mark mem ops for self verification
+ bool wholeMethod;
+ int numChainingCells[kChainingCellGap];
+ LIR *firstChainingLIR[kChainingCellGap];
+ LIR *chainingCellBottom;
+ struct RegisterPool *regPool;
+ int optRound; // round number to tell an LIR's age
+ jmp_buf *bailPtr;
+ JitInstructionSetType instructionSet;
+ /* Number of total regs used in the whole cUnit after SSA transformation */
+ int numSSARegs;
+ /* Map SSA reg i to the Dalvik[15..0]/Sub[31..16] pair. */
+ GrowableList *ssaToDalvikMap;
+
+ /* The following are new data structures to support SSA representations */
+ /* Map original Dalvik reg i to the SSA[15..0]/Sub[31..16] pair */
+ int *dalvikToSSAMap; // length == method->registersSize
+ BitVector *isConstantV; // length == numSSAReg
+ int *constantValues; // length == numSSAReg
+
+ /* Data structure for loop analysis and optimizations */
+ struct LoopAnalysis *loopAnalysis;
+
+ /* Map SSA names to location */
+ RegLocation *regLocation;
+ int sequenceNumber;
+
+ /*
+ * Set to the Dalvik PC of the switch instruction if it has more than
+ * MAX_CHAINED_SWITCH_CASES cases.
+ */
+ const u2 *switchOverflowPad;
+} CompilationUnit;
+
+#if defined(WITH_SELF_VERIFICATION)
+#define HEAP_ACCESS_SHADOW(_state) cUnit->heapMemOp = _state
+#else
+#define HEAP_ACCESS_SHADOW(_state)
+#endif
+
+BasicBlock *dvmCompilerNewBB(BBType blockType);
+
+void dvmCompilerAppendMIR(BasicBlock *bb, MIR *mir);
+
+void dvmCompilerPrependMIR(BasicBlock *bb, MIR *mir);
+
+void dvmCompilerInsertMIRAfter(BasicBlock *bb, MIR *currentMIR, MIR *newMIR);
+
+void dvmCompilerAppendLIR(CompilationUnit *cUnit, LIR *lir);
+
+void dvmCompilerInsertLIRBefore(LIR *currentLIR, LIR *newLIR);
+
+void dvmCompilerInsertLIRAfter(LIR *currentLIR, LIR *newLIR);
+
+void dvmCompilerAbort(CompilationUnit *cUnit);
+
+/* Debug Utilities */
+void dvmCompilerDumpCompilationUnit(CompilationUnit *cUnit);
+
+#endif /* _DALVIK_VM_COMPILER_IR */
diff --git a/vm/compiler/CompilerInternals.h b/vm/compiler/CompilerInternals.h
new file mode 100644
index 0000000..9a30b34
--- /dev/null
+++ b/vm/compiler/CompilerInternals.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DALVIK_VM_COMPILER_INTERNAL
+#define _DALVIK_VM_COMPILER_INTERNAL
+
+#include "Dalvik.h"
+#include "CompilerUtility.h"
+#include "codegen/CompilerCodegen.h"
+#include "interp/Jit.h"
+
+#endif /* _DALVIK_VM_COMPILER_INTERNAL */
diff --git a/vm/compiler/CompilerUtility.h b/vm/compiler/CompilerUtility.h
new file mode 100644
index 0000000..551edb8
--- /dev/null
+++ b/vm/compiler/CompilerUtility.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DALVIK_VM_COMPILER_UTILITY
+#define _DALVIK_VM_COMPILER_UTILITY
+
+#include "Dalvik.h"
+
+/* Each arena page has some overhead, so take a few bytes off 8k */
+#define ARENA_DEFAULT_SIZE 8100
+
+/* Allocate the initial memory block for arena-based allocation */
+bool dvmCompilerHeapInit(void);
+
+typedef struct ArenaMemBlock {
+ size_t blockSize;
+ size_t bytesAllocated;
+ struct ArenaMemBlock *next;
+ char ptr[0];
+} ArenaMemBlock;
+
+void *dvmCompilerNew(size_t size, bool zero);
+
+void dvmCompilerArenaReset(void);
+
+typedef struct GrowableList {
+ size_t numAllocated;
+ size_t numUsed;
+ void **elemList;
+} GrowableList;
+
+#define GET_ELEM_N(LIST, TYPE, N) (((TYPE*) LIST->elemList)[N])
+
+struct LIR;
+
+void dvmInitGrowableList(GrowableList *gList, size_t initLength);
+void dvmInsertGrowableList(GrowableList *gList, void *elem);
+BitVector* dvmCompilerAllocBitVector(int startBits, bool expandable);
+bool dvmCompilerSetBit(BitVector* pBits, int num);
+void dvmDebugBitVector(char *msg, const BitVector *bv, int length);
+void dvmDumpLIRInsn(struct LIR *lir, unsigned char *baseAddr);
+void dvmDumpResourceMask(struct LIR *lir, u8 mask, const char *prefix);
+
+#endif /* _DALVIK_COMPILER_UTILITY */
diff --git a/vm/compiler/Dataflow.c b/vm/compiler/Dataflow.c
new file mode 100644
index 0000000..89c5b35
--- /dev/null
+++ b/vm/compiler/Dataflow.c
@@ -0,0 +1,1459 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "Dataflow.h"
+#include "Loop.h"
+#include "libdex/OpCodeNames.h"
+
+/*
+ * Main table containing data flow attributes for each bytecode. The first
+ * 256 entries are for Dalvik bytecode instructions, where extended opcode at
+ * the MIR level are appended afterwards.
+ *
+ * TODO - many optimization flags are incomplete - they will only limit the
+ * scope of optimizations but will not cause mis-optimizations.
+ */
+int dvmCompilerDataFlowAttributes[kMirOpLast] = {
+ // 00 OP_NOP
+ DF_NOP,
+
+ // 01 OP_MOVE vA, vB
+ DF_DA | DF_UB | DF_IS_MOVE,
+
+ // 02 OP_MOVE_FROM16 vAA, vBBBB
+ DF_DA | DF_UB | DF_IS_MOVE,
+
+ // 03 OP_MOVE_16 vAAAA, vBBBB
+ DF_DA | DF_UB | DF_IS_MOVE,
+
+ // 04 OP_MOVE_WIDE vA, vB
+ DF_DA_WIDE | DF_UB_WIDE | DF_IS_MOVE,
+
+ // 05 OP_MOVE_WIDE_FROM16 vAA, vBBBB
+ DF_DA_WIDE | DF_UB_WIDE | DF_IS_MOVE,
+
+ // 06 OP_MOVE_WIDE_16 vAAAA, vBBBB
+ DF_DA_WIDE | DF_UB_WIDE | DF_IS_MOVE,
+
+ // 07 OP_MOVE_OBJECT vA, vB
+ DF_DA | DF_UB | DF_IS_MOVE,
+
+ // 08 OP_MOVE_OBJECT_FROM16 vAA, vBBBB
+ DF_DA | DF_UB | DF_IS_MOVE,
+
+ // 09 OP_MOVE_OBJECT_16 vAAAA, vBBBB
+ DF_DA | DF_UB | DF_IS_MOVE,
+
+ // 0A OP_MOVE_RESULT vAA
+ DF_DA,
+
+ // 0B OP_MOVE_RESULT_WIDE vAA
+ DF_DA_WIDE,
+
+ // 0C OP_MOVE_RESULT_OBJECT vAA
+ DF_DA,
+
+ // 0D OP_MOVE_EXCEPTION vAA
+ DF_DA,
+
+ // 0E OP_RETURN_VOID
+ DF_NOP,
+
+ // 0F OP_RETURN vAA
+ DF_UA,
+
+ // 10 OP_RETURN_WIDE vAA
+ DF_UA_WIDE,
+
+ // 11 OP_RETURN_OBJECT vAA
+ DF_UA,
+
+ // 12 OP_CONST_4 vA, #+B
+ DF_DA | DF_SETS_CONST,
+
+ // 13 OP_CONST_16 vAA, #+BBBB
+ DF_DA | DF_SETS_CONST,
+
+ // 14 OP_CONST vAA, #+BBBBBBBB
+ DF_DA | DF_SETS_CONST,
+
+ // 15 OP_CONST_HIGH16 VAA, #+BBBB0000
+ DF_DA | DF_SETS_CONST,
+
+ // 16 OP_CONST_WIDE_16 vAA, #+BBBB
+ DF_DA_WIDE | DF_SETS_CONST,
+
+ // 17 OP_CONST_WIDE_32 vAA, #+BBBBBBBB
+ DF_DA_WIDE | DF_SETS_CONST,
+
+ // 18 OP_CONST_WIDE vAA, #+BBBBBBBBBBBBBBBB
+ DF_DA_WIDE | DF_SETS_CONST,
+
+ // 19 OP_CONST_WIDE_HIGH16 vAA, #+BBBB000000000000
+ DF_DA_WIDE | DF_SETS_CONST,
+
+ // 1A OP_CONST_STRING vAA, string@BBBB
+ DF_DA,
+
+ // 1B OP_CONST_STRING_JUMBO vAA, string@BBBBBBBB
+ DF_DA,
+
+ // 1C OP_CONST_CLASS vAA, type@BBBB
+ DF_DA,
+
+ // 1D OP_MONITOR_ENTER vAA
+ DF_UA,
+
+ // 1E OP_MONITOR_EXIT vAA
+ DF_UA,
+
+ // 1F OP_CHECK_CAST vAA, type@BBBB
+ DF_UA,
+
+ // 20 OP_INSTANCE_OF vA, vB, type@CCCC
+ DF_DA | DF_UB,
+
+ // 21 OP_ARRAY_LENGTH vA, vB
+ DF_DA | DF_UB,
+
+ // 22 OP_NEW_INSTANCE vAA, type@BBBB
+ DF_DA,
+
+ // 23 OP_NEW_ARRAY vA, vB, type@CCCC
+ DF_DA | DF_UB,
+
+ // 24 OP_FILLED_NEW_ARRAY {vD, vE, vF, vG, vA}
+ DF_FORMAT_35C,
+
+ // 25 OP_FILLED_NEW_ARRAY_RANGE {vCCCC .. vNNNN}, type@BBBB
+ DF_FORMAT_3RC,
+
+ // 26 OP_FILL_ARRAY_DATA vAA, +BBBBBBBB
+ DF_UA,
+
+ // 27 OP_THROW vAA
+ DF_UA,
+
+ // 28 OP_GOTO
+ DF_NOP,
+
+ // 29 OP_GOTO_16
+ DF_NOP,
+
+ // 2A OP_GOTO_32
+ DF_NOP,
+
+ // 2B OP_PACKED_SWITCH vAA, +BBBBBBBB
+ DF_UA,
+
+ // 2C OP_SPARSE_SWITCH vAA, +BBBBBBBB
+ DF_UA,
+
+ // 2D OP_CMPL_FLOAT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_FP_B | DF_FP_C,
+
+ // 2E OP_CMPG_FLOAT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_FP_B | DF_FP_C,
+
+ // 2F OP_CMPL_DOUBLE vAA, vBB, vCC
+ DF_DA | DF_UB_WIDE | DF_UC_WIDE | DF_FP_B | DF_FP_C,
+
+ // 30 OP_CMPG_DOUBLE vAA, vBB, vCC
+ DF_DA | DF_UB_WIDE | DF_UC_WIDE | DF_FP_B | DF_FP_C,
+
+ // 31 OP_CMP_LONG vAA, vBB, vCC
+ DF_DA | DF_UB_WIDE | DF_UC_WIDE,
+
+ // 32 OP_IF_EQ vA, vB, +CCCC
+ DF_UA | DF_UB,
+
+ // 33 OP_IF_NE vA, vB, +CCCC
+ DF_UA | DF_UB,
+
+ // 34 OP_IF_LT vA, vB, +CCCC
+ DF_UA | DF_UB,
+
+ // 35 OP_IF_GE vA, vB, +CCCC
+ DF_UA | DF_UB,
+
+ // 36 OP_IF_GT vA, vB, +CCCC
+ DF_UA | DF_UB,
+
+ // 37 OP_IF_LE vA, vB, +CCCC
+ DF_UA | DF_UB,
+
+
+ // 38 OP_IF_EQZ vAA, +BBBB
+ DF_UA,
+
+ // 39 OP_IF_NEZ vAA, +BBBB
+ DF_UA,
+
+ // 3A OP_IF_LTZ vAA, +BBBB
+ DF_UA,
+
+ // 3B OP_IF_GEZ vAA, +BBBB
+ DF_UA,
+
+ // 3C OP_IF_GTZ vAA, +BBBB
+ DF_UA,
+
+ // 3D OP_IF_LEZ vAA, +BBBB
+ DF_UA,
+
+ // 3E OP_UNUSED_3E
+ DF_NOP,
+
+ // 3F OP_UNUSED_3F
+ DF_NOP,
+
+ // 40 OP_UNUSED_40
+ DF_NOP,
+
+ // 41 OP_UNUSED_41
+ DF_NOP,
+
+ // 42 OP_UNUSED_42
+ DF_NOP,
+
+ // 43 OP_UNUSED_43
+ DF_NOP,
+
+ // 44 OP_AGET vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_0 | DF_IS_GETTER,
+
+ // 45 OP_AGET_WIDE vAA, vBB, vCC
+ DF_DA_WIDE | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_0 | DF_IS_GETTER,
+
+ // 46 OP_AGET_OBJECT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_0 | DF_IS_GETTER,
+
+ // 47 OP_AGET_BOOLEAN vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_0 | DF_IS_GETTER,
+
+ // 48 OP_AGET_BYTE vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_0 | DF_IS_GETTER,
+
+ // 49 OP_AGET_CHAR vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_0 | DF_IS_GETTER,
+
+ // 4A OP_AGET_SHORT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_0 | DF_IS_GETTER,
+
+ // 4B OP_APUT vAA, vBB, vCC
+ DF_UA | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_1 | DF_IS_SETTER,
+
+ // 4C OP_APUT_WIDE vAA, vBB, vCC
+ DF_UA_WIDE | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_2 | DF_IS_SETTER,
+
+ // 4D OP_APUT_OBJECT vAA, vBB, vCC
+ DF_UA | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_1 | DF_IS_SETTER,
+
+ // 4E OP_APUT_BOOLEAN vAA, vBB, vCC
+ DF_UA | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_1 | DF_IS_SETTER,
+
+ // 4F OP_APUT_BYTE vAA, vBB, vCC
+ DF_UA | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_1 | DF_IS_SETTER,
+
+ // 50 OP_APUT_CHAR vAA, vBB, vCC
+ DF_UA | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_1 | DF_IS_SETTER,
+
+ // 51 OP_APUT_SHORT vAA, vBB, vCC
+ DF_UA | DF_UB | DF_UC | DF_NULL_N_RANGE_CHECK_1 | DF_IS_SETTER,
+
+ // 52 OP_IGET vA, vB, field@CCCC
+ DF_DA | DF_UB | DF_IS_GETTER,
+
+ // 53 OP_IGET_WIDE vA, vB, field@CCCC
+ DF_DA_WIDE | DF_UB | DF_IS_GETTER,
+
+ // 54 OP_IGET_OBJECT vA, vB, field@CCCC
+ DF_DA | DF_UB | DF_IS_GETTER,
+
+ // 55 OP_IGET_BOOLEAN vA, vB, field@CCCC
+ DF_DA | DF_UB | DF_IS_GETTER,
+
+ // 56 OP_IGET_BYTE vA, vB, field@CCCC
+ DF_DA | DF_UB | DF_IS_GETTER,
+
+ // 57 OP_IGET_CHAR vA, vB, field@CCCC
+ DF_DA | DF_UB | DF_IS_GETTER,
+
+ // 58 OP_IGET_SHORT vA, vB, field@CCCC
+ DF_DA | DF_UB | DF_IS_GETTER,
+
+ // 59 OP_IPUT vA, vB, field@CCCC
+ DF_UA | DF_UB | DF_IS_SETTER,
+
+ // 5A OP_IPUT_WIDE vA, vB, field@CCCC
+ DF_UA_WIDE | DF_UB | DF_IS_SETTER,
+
+ // 5B OP_IPUT_OBJECT vA, vB, field@CCCC
+ DF_UA | DF_UB | DF_IS_SETTER,
+
+ // 5C OP_IPUT_BOOLEAN vA, vB, field@CCCC
+ DF_UA | DF_UB | DF_IS_SETTER,
+
+ // 5D OP_IPUT_BYTE vA, vB, field@CCCC
+ DF_UA | DF_UB | DF_IS_SETTER,
+
+ // 5E OP_IPUT_CHAR vA, vB, field@CCCC
+ DF_UA | DF_UB | DF_IS_SETTER,
+
+ // 5F OP_IPUT_SHORT vA, vB, field@CCCC
+ DF_UA | DF_UB | DF_IS_SETTER,
+
+ // 60 OP_SGET vAA, field@BBBB
+ DF_DA | DF_IS_GETTER,
+
+ // 61 OP_SGET_WIDE vAA, field@BBBB
+ DF_DA_WIDE | DF_IS_GETTER,
+
+ // 62 OP_SGET_OBJECT vAA, field@BBBB
+ DF_DA | DF_IS_GETTER,
+
+ // 63 OP_SGET_BOOLEAN vAA, field@BBBB
+ DF_DA | DF_IS_GETTER,
+
+ // 64 OP_SGET_BYTE vAA, field@BBBB
+ DF_DA | DF_IS_GETTER,
+
+ // 65 OP_SGET_CHAR vAA, field@BBBB
+ DF_DA | DF_IS_GETTER,
+
+ // 66 OP_SGET_SHORT vAA, field@BBBB
+ DF_DA | DF_IS_GETTER,
+
+ // 67 OP_SPUT vAA, field@BBBB
+ DF_UA | DF_IS_SETTER,
+
+ // 68 OP_SPUT_WIDE vAA, field@BBBB
+ DF_UA_WIDE | DF_IS_SETTER,
+
+ // 69 OP_SPUT_OBJECT vAA, field@BBBB
+ DF_UA | DF_IS_SETTER,
+
+ // 6A OP_SPUT_BOOLEAN vAA, field@BBBB
+ DF_UA | DF_IS_SETTER,
+
+ // 6B OP_SPUT_BYTE vAA, field@BBBB
+ DF_UA | DF_IS_SETTER,
+
+ // 6C OP_SPUT_CHAR vAA, field@BBBB
+ DF_UA | DF_IS_SETTER,
+
+ // 6D OP_SPUT_SHORT vAA, field@BBBB
+ DF_UA | DF_IS_SETTER,
+
+ // 6E OP_INVOKE_VIRTUAL {vD, vE, vF, vG, vA}
+ DF_FORMAT_35C,
+
+ // 6F OP_INVOKE_SUPER {vD, vE, vF, vG, vA}
+ DF_FORMAT_35C,
+
+ // 70 OP_INVOKE_DIRECT {vD, vE, vF, vG, vA}
+ DF_FORMAT_35C,
+
+ // 71 OP_INVOKE_STATIC {vD, vE, vF, vG, vA}
+ DF_FORMAT_35C,
+
+ // 72 OP_INVOKE_INTERFACE {vD, vE, vF, vG, vA}
+ DF_FORMAT_35C,
+
+ // 73 OP_UNUSED_73
+ DF_NOP,
+
+ // 74 OP_INVOKE_VIRTUAL_RANGE {vCCCC .. vNNNN}
+ DF_FORMAT_3RC,
+
+ // 75 OP_INVOKE_SUPER_RANGE {vCCCC .. vNNNN}
+ DF_FORMAT_3RC,
+
+ // 76 OP_INVOKE_DIRECT_RANGE {vCCCC .. vNNNN}
+ DF_FORMAT_3RC,
+
+ // 77 OP_INVOKE_STATIC_RANGE {vCCCC .. vNNNN}
+ DF_FORMAT_3RC,
+
+ // 78 OP_INVOKE_INTERFACE_RANGE {vCCCC .. vNNNN}
+ DF_FORMAT_3RC,
+
+ // 79 OP_UNUSED_79
+ DF_NOP,
+
+ // 7A OP_UNUSED_7A
+ DF_NOP,
+
+ // 7B OP_NEG_INT vA, vB
+ DF_DA | DF_UB,
+
+ // 7C OP_NOT_INT vA, vB
+ DF_DA | DF_UB,
+
+ // 7D OP_NEG_LONG vA, vB
+ DF_DA_WIDE | DF_UB_WIDE,
+
+ // 7E OP_NOT_LONG vA, vB
+ DF_DA_WIDE | DF_UB_WIDE,
+
+ // 7F OP_NEG_FLOAT vA, vB
+ DF_DA | DF_UB | DF_FP_A | DF_FP_B,
+
+ // 80 OP_NEG_DOUBLE vA, vB
+ DF_DA_WIDE | DF_UB_WIDE | DF_FP_A | DF_FP_B,
+
+ // 81 OP_INT_TO_LONG vA, vB
+ DF_DA_WIDE | DF_UB,
+
+ // 82 OP_INT_TO_FLOAT vA, vB
+ DF_DA | DF_UB | DF_FP_A,
+
+ // 83 OP_INT_TO_DOUBLE vA, vB
+ DF_DA_WIDE | DF_UB | DF_FP_A,
+
+ // 84 OP_LONG_TO_INT vA, vB
+ DF_DA | DF_UB_WIDE,
+
+ // 85 OP_LONG_TO_FLOAT vA, vB
+ DF_DA | DF_UB_WIDE | DF_FP_A,
+
+ // 86 OP_LONG_TO_DOUBLE vA, vB
+ DF_DA_WIDE | DF_UB_WIDE | DF_FP_A,
+
+ // 87 OP_FLOAT_TO_INT vA, vB
+ DF_DA | DF_UB | DF_FP_B,
+
+ // 88 OP_FLOAT_TO_LONG vA, vB
+ DF_DA_WIDE | DF_UB | DF_FP_B,
+
+ // 89 OP_FLOAT_TO_DOUBLE vA, vB
+ DF_DA_WIDE | DF_UB | DF_FP_A | DF_FP_B,
+
+ // 8A OP_DOUBLE_TO_INT vA, vB
+ DF_DA | DF_UB_WIDE | DF_FP_B,
+
+ // 8B OP_DOUBLE_TO_LONG vA, vB
+ DF_DA_WIDE | DF_UB_WIDE | DF_FP_B,
+
+ // 8C OP_DOUBLE_TO_FLOAT vA, vB
+ DF_DA | DF_UB_WIDE | DF_FP_A | DF_FP_B,
+
+ // 8D OP_INT_TO_BYTE vA, vB
+ DF_DA | DF_UB,
+
+ // 8E OP_INT_TO_CHAR vA, vB
+ DF_DA | DF_UB,
+
+ // 8F OP_INT_TO_SHORT vA, vB
+ DF_DA | DF_UB,
+
+ // 90 OP_ADD_INT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_IS_LINEAR,
+
+ // 91 OP_SUB_INT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_IS_LINEAR,
+
+ // 92 OP_MUL_INT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC,
+
+ // 93 OP_DIV_INT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC,
+
+ // 94 OP_REM_INT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC,
+
+ // 95 OP_AND_INT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC,
+
+ // 96 OP_OR_INT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC,
+
+ // 97 OP_XOR_INT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC,
+
+ // 98 OP_SHL_INT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC,
+
+ // 99 OP_SHR_INT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC,
+
+ // 9A OP_USHR_INT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC,
+
+ // 9B OP_ADD_LONG vAA, vBB, vCC
+ DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE,
+
+ // 9C OP_SUB_LONG vAA, vBB, vCC
+ DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE,
+
+ // 9D OP_MUL_LONG vAA, vBB, vCC
+ DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE,
+
+ // 9E OP_DIV_LONG vAA, vBB, vCC
+ DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE,
+
+ // 9F OP_REM_LONG vAA, vBB, vCC
+ DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE,
+
+ // A0 OP_AND_LONG vAA, vBB, vCC
+ DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE,
+
+ // A1 OP_OR_LONG vAA, vBB, vCC
+ DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE,
+
+ // A2 OP_XOR_LONG vAA, vBB, vCC
+ DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE,
+
+ // A3 OP_SHL_LONG vAA, vBB, vCC
+ DF_DA_WIDE | DF_UB_WIDE | DF_UC,
+
+ // A4 OP_SHR_LONG vAA, vBB, vCC
+ DF_DA_WIDE | DF_UB_WIDE | DF_UC,
+
+ // A5 OP_USHR_LONG vAA, vBB, vCC
+ DF_DA_WIDE | DF_UB_WIDE | DF_UC,
+
+ // A6 OP_ADD_FLOAT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C,
+
+ // A7 OP_SUB_FLOAT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C,
+
+ // A8 OP_MUL_FLOAT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C,
+
+ // A9 OP_DIV_FLOAT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C,
+
+ // AA OP_REM_FLOAT vAA, vBB, vCC
+ DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C,
+
+ // AB OP_ADD_DOUBLE vAA, vBB, vCC
+ DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE | DF_FP_A | DF_FP_B | DF_FP_C,
+
+ // AC OP_SUB_DOUBLE vAA, vBB, vCC
+ DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE | DF_FP_A | DF_FP_B | DF_FP_C,
+
+ // AD OP_MUL_DOUBLE vAA, vBB, vCC
+ DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE | DF_FP_A | DF_FP_B | DF_FP_C,
+
+ // AE OP_DIV_DOUBLE vAA, vBB, vCC
+ DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE | DF_FP_A | DF_FP_B | DF_FP_C,
+
+ // AF OP_REM_DOUBLE vAA, vBB, vCC
+ DF_DA_WIDE | DF_UB_WIDE | DF_UC_WIDE | DF_FP_A | DF_FP_B | DF_FP_C,
+
+ // B0 OP_ADD_INT_2ADDR vA, vB
+ DF_DA | DF_UA | DF_UB,
+
+ // B1 OP_SUB_INT_2ADDR vA, vB
+ DF_DA | DF_UA | DF_UB,
+
+ // B2 OP_MUL_INT_2ADDR vA, vB
+ DF_DA | DF_UA | DF_UB,
+
+ // B3 OP_DIV_INT_2ADDR vA, vB
+ DF_DA | DF_UA | DF_UB,
+
+ // B4 OP_REM_INT_2ADDR vA, vB
+ DF_DA | DF_UA | DF_UB,
+
+ // B5 OP_AND_INT_2ADDR vA, vB
+ DF_DA | DF_UA | DF_UB,
+
+ // B6 OP_OR_INT_2ADDR vA, vB
+ DF_DA | DF_UA | DF_UB,
+
+ // B7 OP_XOR_INT_2ADDR vA, vB
+ DF_DA | DF_UA | DF_UB,
+
+ // B8 OP_SHL_INT_2ADDR vA, vB
+ DF_DA | DF_UA | DF_UB,
+
+ // B9 OP_SHR_INT_2ADDR vA, vB
+ DF_DA | DF_UA | DF_UB,
+
+ // BA OP_USHR_INT_2ADDR vA, vB
+ DF_DA | DF_UA | DF_UB,
+
+ // BB OP_ADD_LONG_2ADDR vA, vB
+ DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE,
+
+ // BC OP_SUB_LONG_2ADDR vA, vB
+ DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE,
+
+ // BD OP_MUL_LONG_2ADDR vA, vB
+ DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE,
+
+ // BE OP_DIV_LONG_2ADDR vA, vB
+ DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE,
+
+ // BF OP_REM_LONG_2ADDR vA, vB
+ DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE,
+
+ // C0 OP_AND_LONG_2ADDR vA, vB
+ DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE,
+
+ // C1 OP_OR_LONG_2ADDR vA, vB
+ DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE,
+
+ // C2 OP_XOR_LONG_2ADDR vA, vB
+ DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE,
+
+ // C3 OP_SHL_LONG_2ADDR vA, vB
+ DF_DA_WIDE | DF_UA_WIDE | DF_UB,
+
+ // C4 OP_SHR_LONG_2ADDR vA, vB
+ DF_DA_WIDE | DF_UA_WIDE | DF_UB,
+
+ // C5 OP_USHR_LONG_2ADDR vA, vB
+ DF_DA_WIDE | DF_UA_WIDE | DF_UB,
+
+ // C6 OP_ADD_FLOAT_2ADDR vA, vB
+ DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B,
+
+ // C7 OP_SUB_FLOAT_2ADDR vA, vB
+ DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B,
+
+ // C8 OP_MUL_FLOAT_2ADDR vA, vB
+ DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B,
+
+ // C9 OP_DIV_FLOAT_2ADDR vA, vB
+ DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B,
+
+ // CA OP_REM_FLOAT_2ADDR vA, vB
+ DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B,
+
+ // CB OP_ADD_DOUBLE_2ADDR vA, vB
+ DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE | DF_FP_A | DF_FP_B,
+
+ // CC OP_SUB_DOUBLE_2ADDR vA, vB
+ DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE | DF_FP_A | DF_FP_B,
+
+ // CD OP_MUL_DOUBLE_2ADDR vA, vB
+ DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE | DF_FP_A | DF_FP_B,
+
+ // CE OP_DIV_DOUBLE_2ADDR vA, vB
+ DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE | DF_FP_A | DF_FP_B,
+
+ // CF OP_REM_DOUBLE_2ADDR vA, vB
+ DF_DA_WIDE | DF_UA_WIDE | DF_UB_WIDE | DF_FP_A | DF_FP_B,
+
+ // D0 OP_ADD_INT_LIT16 vA, vB, #+CCCC
+ DF_DA | DF_UB,
+
+ // D1 OP_RSUB_INT vA, vB, #+CCCC
+ DF_DA | DF_UB,
+
+ // D2 OP_MUL_INT_LIT16 vA, vB, #+CCCC
+ DF_DA | DF_UB,
+
+ // D3 OP_DIV_INT_LIT16 vA, vB, #+CCCC
+ DF_DA | DF_UB,
+
+ // D4 OP_REM_INT_LIT16 vA, vB, #+CCCC
+ DF_DA | DF_UB,
+
+ // D5 OP_AND_INT_LIT16 vA, vB, #+CCCC
+ DF_DA | DF_UB,
+
+ // D6 OP_OR_INT_LIT16 vA, vB, #+CCCC
+ DF_DA | DF_UB,
+
+ // D7 OP_XOR_INT_LIT16 vA, vB, #+CCCC
+ DF_DA | DF_UB,
+
+ // D8 OP_ADD_INT_LIT8 vAA, vBB, #+CC
+ DF_DA | DF_UB | DF_IS_LINEAR,
+
+ // D9 OP_RSUB_INT_LIT8 vAA, vBB, #+CC
+ DF_DA | DF_UB,
+
+ // DA OP_MUL_INT_LIT8 vAA, vBB, #+CC
+ DF_DA | DF_UB,
+
+ // DB OP_DIV_INT_LIT8 vAA, vBB, #+CC
+ DF_DA | DF_UB,
+
+ // DC OP_REM_INT_LIT8 vAA, vBB, #+CC
+ DF_DA | DF_UB,
+
+ // DD OP_AND_INT_LIT8 vAA, vBB, #+CC
+ DF_DA | DF_UB,
+
+ // DE OP_OR_INT_LIT8 vAA, vBB, #+CC
+ DF_DA | DF_UB,
+
+ // DF OP_XOR_INT_LIT8 vAA, vBB, #+CC
+ DF_DA | DF_UB,
+
+ // E0 OP_SHL_INT_LIT8 vAA, vBB, #+CC
+ DF_DA | DF_UB,
+
+ // E1 OP_SHR_INT_LIT8 vAA, vBB, #+CC
+ DF_DA | DF_UB,
+
+ // E2 OP_USHR_INT_LIT8 vAA, vBB, #+CC
+ DF_DA | DF_UB,
+
+ // E3 OP_IGET_VOLATILE
+ DF_DA | DF_UB,
+
+ // E4 OP_IPUT_VOLATILE
+ DF_UA | DF_UB,
+
+ // E5 OP_SGET_VOLATILE
+ DF_DA,
+
+ // E6 OP_SPUT_VOLATILE
+ DF_UA,
+
+ // E7 OP_IGET_OBJECT_VOLATILE
+ DF_DA | DF_UB,
+
+ // E8 OP_IGET_WIDE_VOLATILE
+ DF_DA_WIDE | DF_UB,
+
+ // E9 OP_IPUT_WIDE_VOLATILE
+ DF_UA_WIDE | DF_UB,
+
+ // EA OP_SGET_WIDE_VOLATILE
+ DF_DA_WIDE,
+
+ // EB OP_SPUT_WIDE_VOLATILE
+ DF_UA_WIDE,
+
+ // EC OP_BREAKPOINT
+ DF_NOP,
+
+ // ED OP_THROW_VERIFICATION_ERROR
+ DF_NOP,
+
+ // EE OP_EXECUTE_INLINE
+ DF_FORMAT_35C,
+
+ // EF OP_EXECUTE_INLINE_RANGE
+ DF_FORMAT_3RC,
+
+ // F0 OP_INVOKE_DIRECT_EMPTY
+ DF_NOP,
+
+ // F1 OP_UNUSED_F1
+ DF_NOP,
+
+ // F2 OP_IGET_QUICK
+ DF_DA | DF_UB | DF_IS_GETTER,
+
+ // F3 OP_IGET_WIDE_QUICK
+ DF_DA_WIDE | DF_UB | DF_IS_GETTER,
+
+ // F4 OP_IGET_OBJECT_QUICK
+ DF_DA | DF_UB | DF_IS_GETTER,
+
+ // F5 OP_IPUT_QUICK
+ DF_UA | DF_UB | DF_IS_SETTER,
+
+ // F6 OP_IPUT_WIDE_QUICK
+ DF_UA_WIDE | DF_UB | DF_IS_SETTER,
+
+ // F7 OP_IPUT_OBJECT_QUICK
+ DF_UA | DF_UB | DF_IS_SETTER,
+
+ // F8 OP_INVOKE_VIRTUAL_QUICK
+ DF_FORMAT_35C,
+
+ // F9 OP_INVOKE_VIRTUAL_QUICK_RANGE
+ DF_FORMAT_3RC,
+
+ // FA OP_INVOKE_SUPER_QUICK
+ DF_FORMAT_35C,
+
+ // FB OP_INVOKE_SUPER_QUICK_RANGE
+ DF_FORMAT_3RC,
+
+ // FC OP_IPUT_OBJECT_VOLATILE
+ DF_UA | DF_UB,
+
+ // FD OP_SGET_OBJECT_VOLATILE
+ DF_DA,
+
+ // FE OP_SPUT_OBJECT_VOLATILE
+ DF_UA,
+
+ // FF OP_UNUSED_FF
+ DF_NOP,
+
+ // Beginning of extended MIR opcodes
+ // 100 OP_MIR_PHI
+ DF_PHI | DF_DA,
+
+ /*
+ * For extended MIR inserted at the MIR2LIR stage, it is okay to have
+ * undefined values here.
+ */
+};
+
+/* Return the Dalvik register/subscript pair of a given SSA register */
+int dvmConvertSSARegToDalvik(CompilationUnit *cUnit, int ssaReg)
+{
+ return GET_ELEM_N(cUnit->ssaToDalvikMap, int, ssaReg);
+}
+
+/*
+ * Utility function to convert encoded SSA register value into Dalvik register
+ * and subscript pair. Each SSA register can be used to index the
+ * ssaToDalvikMap list to get the subscript[31..16]/dalvik_reg[15..0] mapping.
+ */
+char *dvmCompilerGetDalvikDisassembly(DecodedInstruction *insn,
+ char *note)
+{
+ char buffer[256];
+ int opcode = insn->opCode;
+ int dfAttributes = dvmCompilerDataFlowAttributes[opcode];
+ char *ret;
+
+ buffer[0] = 0;
+ strcpy(buffer, dexGetOpcodeName(opcode));
+
+ if (note)
+ strcat(buffer, note);
+
+ if (dfAttributes & DF_FORMAT_35C) {
+ unsigned int i;
+ for (i = 0; i < insn->vA; i++) {
+ if (i != 0) strcat(buffer, ",");
+ snprintf(buffer + strlen(buffer), 256, " v%d", insn->arg[i]);
+ }
+ }
+ else if (dfAttributes & DF_FORMAT_3RC) {
+ snprintf(buffer + strlen(buffer), 256,
+ " v%d..v%d", insn->vC, insn->vC + insn->vA - 1);
+ }
+ else {
+ if (dfAttributes & DF_A_IS_REG) {
+ snprintf(buffer + strlen(buffer), 256, " v%d", insn->vA);
+ }
+ if (dfAttributes & DF_B_IS_REG) {
+ snprintf(buffer + strlen(buffer), 256, ", v%d", insn->vB);
+ }
+ else {
+ snprintf(buffer + strlen(buffer), 256, ", (#%d)", insn->vB);
+ }
+ if (dfAttributes & DF_C_IS_REG) {
+ snprintf(buffer + strlen(buffer), 256, ", v%d", insn->vC);
+ }
+ else {
+ snprintf(buffer + strlen(buffer), 256, ", (#%d)", insn->vC);
+ }
+ }
+ int length = strlen(buffer) + 1;
+ ret = dvmCompilerNew(length, false);
+ memcpy(ret, buffer, length);
+ return ret;
+}
+
+/*
+ * Utility function to convert encoded SSA register value into Dalvik register
+ * and subscript pair. Each SSA register can be used to index the
+ * ssaToDalvikMap list to get the subscript[31..16]/dalvik_reg[15..0] mapping.
+ */
+char *dvmCompilerGetSSAString(CompilationUnit *cUnit, SSARepresentation *ssaRep)
+{
+ char buffer[256];
+ char *ret;
+ int i;
+
+ buffer[0] = 0;
+ for (i = 0; i < ssaRep->numDefs; i++) {
+ int ssa2DalvikValue = dvmConvertSSARegToDalvik(cUnit, ssaRep->defs[i]);
+
+ sprintf(buffer + strlen(buffer), "s%d(v%d_%d) ",
+ ssaRep->defs[i], DECODE_REG(ssa2DalvikValue),
+ DECODE_SUB(ssa2DalvikValue));
+ }
+
+ if (ssaRep->numDefs) {
+ strcat(buffer, "<- ");
+ }
+
+ for (i = 0; i < ssaRep->numUses; i++) {
+ int ssa2DalvikValue = dvmConvertSSARegToDalvik(cUnit, ssaRep->uses[i]);
+ int len = strlen(buffer);
+
+ if (snprintf(buffer + len, 250 - len, "s%d(v%d_%d) ",
+ ssaRep->uses[i], DECODE_REG(ssa2DalvikValue),
+ DECODE_SUB(ssa2DalvikValue)) >= (250 - len)) {
+ strcat(buffer, "...");
+ break;
+ }
+ }
+
+ int length = strlen(buffer) + 1;
+ ret = dvmCompilerNew(length, false);
+ memcpy(ret, buffer, length);
+ return ret;
+}
+
+/* Any register that is used before being defined is considered live-in */
+static inline void handleLiveInUse(BitVector *useV, BitVector *defV,
+ BitVector *liveInV, int dalvikRegId)
+{
+ dvmCompilerSetBit(useV, dalvikRegId);
+ if (!dvmIsBitSet(defV, dalvikRegId)) {
+ dvmCompilerSetBit(liveInV, dalvikRegId);
+ }
+}
+
+/* Mark a reg as being defined */
+static inline void handleLiveInDef(BitVector *defV, int dalvikRegId)
+{
+ dvmCompilerSetBit(defV, dalvikRegId);
+}
+
+/*
+ * Find out live-in variables for natural loops. Variables that are live-in in
+ * the main loop body are considered to be defined in the entry block.
+ */
+void dvmCompilerFindLiveIn(CompilationUnit *cUnit, BasicBlock *bb)
+{
+ MIR *mir;
+ BitVector *useV, *defV, *liveInV;
+
+ if (bb->blockType != kDalvikByteCode &&
+ bb->blockType != kTraceEntryBlock) {
+ return;
+ }
+
+ useV = bb->dataFlowInfo->useV =
+ dvmCompilerAllocBitVector(cUnit->method->registersSize, false);
+ defV = bb->dataFlowInfo->defV =
+ dvmCompilerAllocBitVector(cUnit->method->registersSize, false);
+ liveInV = bb->dataFlowInfo->liveInV =
+ dvmCompilerAllocBitVector(cUnit->method->registersSize, false);
+
+ for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
+ int dfAttributes =
+ dvmCompilerDataFlowAttributes[mir->dalvikInsn.opCode];
+ DecodedInstruction *dInsn = &mir->dalvikInsn;
+
+ if (dfAttributes & DF_HAS_USES) {
+ if (dfAttributes & DF_UA) {
+ handleLiveInUse(useV, defV, liveInV, dInsn->vA);
+ } else if (dfAttributes & DF_UA_WIDE) {
+ handleLiveInUse(useV, defV, liveInV, dInsn->vA);
+ handleLiveInUse(useV, defV, liveInV, dInsn->vA+1);
+ }
+ if (dfAttributes & DF_UB) {
+ handleLiveInUse(useV, defV, liveInV, dInsn->vB);
+ } else if (dfAttributes & DF_UB_WIDE) {
+ handleLiveInUse(useV, defV, liveInV, dInsn->vB);
+ handleLiveInUse(useV, defV, liveInV, dInsn->vB+1);
+ }
+ if (dfAttributes & DF_UC) {
+ handleLiveInUse(useV, defV, liveInV, dInsn->vC);
+ } else if (dfAttributes & DF_UC_WIDE) {
+ handleLiveInUse(useV, defV, liveInV, dInsn->vC);
+ handleLiveInUse(useV, defV, liveInV, dInsn->vC+1);
+ }
+ }
+ if (dfAttributes & DF_HAS_DEFS) {
+ handleLiveInDef(defV, dInsn->vA);
+ if (dfAttributes & DF_DA_WIDE) {
+ handleLiveInDef(defV, dInsn->vA+1);
+ }
+ }
+ }
+}
+
+/* Find out the latest SSA register for a given Dalvik register */
+static void handleSSAUse(CompilationUnit *cUnit, int *uses, int dalvikReg,
+ int regIndex)
+{
+ int encodedValue = cUnit->dalvikToSSAMap[dalvikReg];
+ int ssaReg = DECODE_REG(encodedValue);
+ uses[regIndex] = ssaReg;
+}
+
+/* Setup a new SSA register for a given Dalvik register */
+static void handleSSADef(CompilationUnit *cUnit, int *defs, int dalvikReg,
+ int regIndex)
+{
+ int encodedValue = cUnit->dalvikToSSAMap[dalvikReg];
+ int ssaReg = cUnit->numSSARegs++;
+ /* Bump up the subscript */
+ int dalvikSub = DECODE_SUB(encodedValue) + 1;
+ int newD2SMapping = ENCODE_REG_SUB(ssaReg, dalvikSub);
+
+ cUnit->dalvikToSSAMap[dalvikReg] = newD2SMapping;
+
+ int newS2DMapping = ENCODE_REG_SUB(dalvikReg, dalvikSub);
+ dvmInsertGrowableList(cUnit->ssaToDalvikMap, (void *) newS2DMapping);
+
+ defs[regIndex] = ssaReg;
+}
+
+/* Loop up new SSA names for format_35c instructions */
+static void dataFlowSSAFormat35C(CompilationUnit *cUnit, MIR *mir)
+{
+ DecodedInstruction *dInsn = &mir->dalvikInsn;
+ int numUses = dInsn->vA;
+ int i;
+
+ mir->ssaRep->numUses = numUses;
+ mir->ssaRep->uses = dvmCompilerNew(sizeof(int) * numUses, false);
+
+ for (i = 0; i < numUses; i++) {
+ handleSSAUse(cUnit, mir->ssaRep->uses, dInsn->arg[i], i);
+ }
+}
+
+/* Loop up new SSA names for format_3rc instructions */
+static void dataFlowSSAFormat3RC(CompilationUnit *cUnit, MIR *mir)
+{
+ DecodedInstruction *dInsn = &mir->dalvikInsn;
+ int numUses = dInsn->vA;
+ int i;
+
+ mir->ssaRep->numUses = numUses;
+ mir->ssaRep->uses = dvmCompilerNew(sizeof(int) * numUses, false);
+
+ for (i = 0; i < numUses; i++) {
+ handleSSAUse(cUnit, mir->ssaRep->uses, dInsn->vC+i, i);
+ }
+}
+
+/* Entry function to convert a block into SSA representation */
+void dvmCompilerDoSSAConversion(CompilationUnit *cUnit, BasicBlock *bb)
+{
+ MIR *mir;
+
+ if (bb->blockType != kDalvikByteCode && bb->blockType != kTraceEntryBlock) {
+ return;
+ }
+
+ for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
+ mir->ssaRep = dvmCompilerNew(sizeof(SSARepresentation), true);
+
+ int dfAttributes =
+ dvmCompilerDataFlowAttributes[mir->dalvikInsn.opCode];
+
+ int numUses = 0;
+
+ if (dfAttributes & DF_FORMAT_35C) {
+ dataFlowSSAFormat35C(cUnit, mir);
+ continue;
+ }
+
+ if (dfAttributes & DF_FORMAT_3RC) {
+ dataFlowSSAFormat3RC(cUnit, mir);
+ continue;
+ }
+
+ if (dfAttributes & DF_HAS_USES) {
+ if (dfAttributes & DF_UA) {
+ numUses++;
+ } else if (dfAttributes & DF_UA_WIDE) {
+ numUses += 2;
+ }
+ if (dfAttributes & DF_UB) {
+ numUses++;
+ } else if (dfAttributes & DF_UB_WIDE) {
+ numUses += 2;
+ }
+ if (dfAttributes & DF_UC) {
+ numUses++;
+ } else if (dfAttributes & DF_UC_WIDE) {
+ numUses += 2;
+ }
+ }
+
+ if (numUses) {
+ mir->ssaRep->numUses = numUses;
+ mir->ssaRep->uses = dvmCompilerNew(sizeof(int) * numUses, false);
+ mir->ssaRep->fpUse = dvmCompilerNew(sizeof(bool) * numUses, false);
+ }
+
+ int numDefs = 0;
+
+ if (dfAttributes & DF_HAS_DEFS) {
+ numDefs++;
+ if (dfAttributes & DF_DA_WIDE) {
+ numDefs++;
+ }
+ }
+
+ if (numDefs) {
+ mir->ssaRep->numDefs = numDefs;
+ mir->ssaRep->defs = dvmCompilerNew(sizeof(int) * numDefs, false);
+ mir->ssaRep->fpDef = dvmCompilerNew(sizeof(bool) * numDefs, false);
+ }
+
+ DecodedInstruction *dInsn = &mir->dalvikInsn;
+
+ if (dfAttributes & DF_HAS_USES) {
+ numUses = 0;
+ if (dfAttributes & DF_UA) {
+ mir->ssaRep->fpUse[numUses] = dfAttributes & DF_FP_A;
+ handleSSAUse(cUnit, mir->ssaRep->uses, dInsn->vA, numUses++);
+ } else if (dfAttributes & DF_UA_WIDE) {
+ mir->ssaRep->fpUse[numUses] = dfAttributes & DF_FP_A;
+ handleSSAUse(cUnit, mir->ssaRep->uses, dInsn->vA, numUses++);
+ mir->ssaRep->fpUse[numUses] = dfAttributes & DF_FP_A;
+ handleSSAUse(cUnit, mir->ssaRep->uses, dInsn->vA+1, numUses++);
+ }
+ if (dfAttributes & DF_UB) {
+ mir->ssaRep->fpUse[numUses] = dfAttributes & DF_FP_B;
+ handleSSAUse(cUnit, mir->ssaRep->uses, dInsn->vB, numUses++);
+ } else if (dfAttributes & DF_UB_WIDE) {
+ mir->ssaRep->fpUse[numUses] = dfAttributes & DF_FP_B;
+ handleSSAUse(cUnit, mir->ssaRep->uses, dInsn->vB, numUses++);
+ mir->ssaRep->fpUse[numUses] = dfAttributes & DF_FP_B;
+ handleSSAUse(cUnit, mir->ssaRep->uses, dInsn->vB+1, numUses++);
+ }
+ if (dfAttributes & DF_UC) {
+ mir->ssaRep->fpUse[numUses] = dfAttributes & DF_FP_C;
+ handleSSAUse(cUnit, mir->ssaRep->uses, dInsn->vC, numUses++);
+ } else if (dfAttributes & DF_UC_WIDE) {
+ mir->ssaRep->fpUse[numUses] = dfAttributes & DF_FP_C;
+ handleSSAUse(cUnit, mir->ssaRep->uses, dInsn->vC, numUses++);
+ mir->ssaRep->fpUse[numUses] = dfAttributes & DF_FP_C;
+ handleSSAUse(cUnit, mir->ssaRep->uses, dInsn->vC+1, numUses++);
+ }
+ }
+ if (dfAttributes & DF_HAS_DEFS) {
+ mir->ssaRep->fpDef[0] = dfAttributes & DF_FP_A;
+ handleSSADef(cUnit, mir->ssaRep->defs, dInsn->vA, 0);
+ if (dfAttributes & DF_DA_WIDE) {
+ mir->ssaRep->fpDef[1] = dfAttributes & DF_FP_A;
+ handleSSADef(cUnit, mir->ssaRep->defs, dInsn->vA+1, 1);
+ }
+ }
+ }
+
+ bb->dataFlowInfo->dalvikToSSAMap =
+ dvmCompilerNew(sizeof(int) * cUnit->method->registersSize, false);
+
+ /* Take a snapshot of Dalvik->SSA mapping at the end of each block */
+ memcpy(bb->dataFlowInfo->dalvikToSSAMap, cUnit->dalvikToSSAMap,
+ sizeof(int) * cUnit->method->registersSize);
+}
+
+/* Setup a constant value for opcodes thare have the DF_SETS_CONST attribute */
+static void setConstant(CompilationUnit *cUnit, int ssaReg, int value)
+{
+ dvmSetBit(cUnit->isConstantV, ssaReg);
+ cUnit->constantValues[ssaReg] = value;
+}
+
+void dvmCompilerDoConstantPropagation(CompilationUnit *cUnit, BasicBlock *bb)
+{
+ MIR *mir;
+ BitVector *isConstantV = cUnit->isConstantV;
+
+ for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
+ int dfAttributes =
+ dvmCompilerDataFlowAttributes[mir->dalvikInsn.opCode];
+
+ DecodedInstruction *dInsn = &mir->dalvikInsn;
+
+ if (!(dfAttributes & DF_HAS_DEFS)) continue;
+
+ /* Handle instructions that set up constants directly */
+ if (dfAttributes & DF_SETS_CONST) {
+ if (dfAttributes & DF_DA) {
+ switch (dInsn->opCode) {
+ case OP_CONST_4:
+ case OP_CONST_16:
+ case OP_CONST:
+ setConstant(cUnit, mir->ssaRep->defs[0], dInsn->vB);
+ break;
+ case OP_CONST_HIGH16:
+ setConstant(cUnit, mir->ssaRep->defs[0],
+ dInsn->vB << 16);
+ break;
+ default:
+ break;
+ }
+ } else if (dfAttributes & DF_DA_WIDE) {
+ switch (dInsn->opCode) {
+ case OP_CONST_WIDE_16:
+ case OP_CONST_WIDE_32:
+ setConstant(cUnit, mir->ssaRep->defs[0], dInsn->vB);
+ setConstant(cUnit, mir->ssaRep->defs[1], 0);
+ break;
+ case OP_CONST_WIDE:
+ setConstant(cUnit, mir->ssaRep->defs[0],
+ (int) dInsn->vB_wide);
+ setConstant(cUnit, mir->ssaRep->defs[1],
+ (int) (dInsn->vB_wide >> 32));
+ break;
+ case OP_CONST_WIDE_HIGH16:
+ setConstant(cUnit, mir->ssaRep->defs[0], 0);
+ setConstant(cUnit, mir->ssaRep->defs[1],
+ dInsn->vB << 16);
+ break;
+ default:
+ break;
+ }
+ }
+ /* Handle instructions that set up constants directly */
+ } else if (dfAttributes & DF_IS_MOVE) {
+ int i;
+
+ for (i = 0; i < mir->ssaRep->numUses; i++) {
+ if (!dvmIsBitSet(isConstantV, mir->ssaRep->uses[i])) break;
+ }
+ /* Move a register holding a constant to another register */
+ if (i == mir->ssaRep->numUses) {
+ setConstant(cUnit, mir->ssaRep->defs[0],
+ cUnit->constantValues[mir->ssaRep->uses[0]]);
+ if (dfAttributes & DF_DA_WIDE) {
+ setConstant(cUnit, mir->ssaRep->defs[1],
+ cUnit->constantValues[mir->ssaRep->uses[1]]);
+ }
+ }
+ }
+ }
+ /* TODO: implement code to handle arithmetic operations */
+}
+
+void dvmCompilerFindInductionVariables(struct CompilationUnit *cUnit,
+ struct BasicBlock *bb)
+{
+ BitVector *isIndVarV = cUnit->loopAnalysis->isIndVarV;
+ BitVector *isConstantV = cUnit->isConstantV;
+ GrowableList *ivList = cUnit->loopAnalysis->ivList;
+ MIR *mir;
+
+ if (bb->blockType != kDalvikByteCode &&
+ bb->blockType != kTraceEntryBlock) {
+ return;
+ }
+
+ /* If the bb doesn't have a phi it cannot contain an induction variable */
+ if (bb->firstMIRInsn == NULL ||
+ bb->firstMIRInsn->dalvikInsn.opCode != kMirOpPhi) {
+ return;
+ }
+
+ /* Find basic induction variable first */
+ for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
+ int dfAttributes =
+ dvmCompilerDataFlowAttributes[mir->dalvikInsn.opCode];
+
+ if (!(dfAttributes & DF_IS_LINEAR)) continue;
+
+ /*
+ * For a basic induction variable:
+ * 1) use[0] should belong to the output of a phi node
+ * 2) def[0] should belong to the input of the same phi node
+ * 3) the value added/subtracted is a constant
+ */
+ MIR *phi;
+ for (phi = bb->firstMIRInsn; phi; phi = phi->next) {
+ if (phi->dalvikInsn.opCode != kMirOpPhi) break;
+
+ if (phi->ssaRep->defs[0] == mir->ssaRep->uses[0] &&
+ phi->ssaRep->uses[1] == mir->ssaRep->defs[0]) {
+ bool deltaIsConstant = false;
+ int deltaValue;
+
+ switch (mir->dalvikInsn.opCode) {
+ case OP_ADD_INT:
+ if (dvmIsBitSet(isConstantV,
+ mir->ssaRep->uses[1])) {
+ deltaValue =
+ cUnit->constantValues[mir->ssaRep->uses[1]];
+ deltaIsConstant = true;
+ }
+ break;
+ case OP_SUB_INT:
+ if (dvmIsBitSet(isConstantV,
+ mir->ssaRep->uses[1])) {
+ deltaValue =
+ -cUnit->constantValues[mir->ssaRep->uses[1]];
+ deltaIsConstant = true;
+ }
+ break;
+ case OP_ADD_INT_LIT8:
+ deltaValue = mir->dalvikInsn.vC;
+ deltaIsConstant = true;
+ break;
+ default:
+ break;
+ }
+ if (deltaIsConstant) {
+ dvmSetBit(isIndVarV, mir->ssaRep->uses[0]);
+ InductionVariableInfo *ivInfo =
+ dvmCompilerNew(sizeof(InductionVariableInfo),
+ false);
+
+ ivInfo->ssaReg = mir->ssaRep->uses[0];
+ ivInfo->basicSSAReg = mir->ssaRep->uses[0];
+ ivInfo->m = 1; // always 1 to basic iv
+ ivInfo->c = 0; // N/A to basic iv
+ ivInfo->inc = deltaValue;
+ dvmInsertGrowableList(ivList, (void *) ivInfo);
+ cUnit->loopAnalysis->numBasicIV++;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Find dependent induction variable now */
+ for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
+ int dfAttributes =
+ dvmCompilerDataFlowAttributes[mir->dalvikInsn.opCode];
+
+ if (!(dfAttributes & DF_IS_LINEAR)) continue;
+
+ /* Skip already identified induction variables */
+ if (dvmIsBitSet(isIndVarV, mir->ssaRep->defs[0])) continue;
+
+ /*
+ * For a dependent induction variable:
+ * 1) use[0] should be an induction variable (basic/dependent)
+ * 2) operand2 should be a constant
+ */
+ if (dvmIsBitSet(isIndVarV, mir->ssaRep->uses[0])) {
+ int srcDalvikReg = dvmConvertSSARegToDalvik(cUnit,
+ mir->ssaRep->uses[0]);
+ int dstDalvikReg = dvmConvertSSARegToDalvik(cUnit,
+ mir->ssaRep->defs[0]);
+
+ bool cIsConstant = false;
+ int c = 0;
+
+ switch (mir->dalvikInsn.opCode) {
+ case OP_ADD_INT:
+ if (dvmIsBitSet(isConstantV,
+ mir->ssaRep->uses[1])) {
+ c = cUnit->constantValues[mir->ssaRep->uses[1]];
+ cIsConstant = true;
+ }
+ break;
+ case OP_SUB_INT:
+ if (dvmIsBitSet(isConstantV,
+ mir->ssaRep->uses[1])) {
+ c = -cUnit->constantValues[mir->ssaRep->uses[1]];
+ cIsConstant = true;
+ }
+ break;
+ case OP_ADD_INT_LIT8:
+ c = mir->dalvikInsn.vC;
+ cIsConstant = true;
+ break;
+ default:
+ break;
+ }
+
+ /* Ignore the update to the basic induction variable itself */
+ if (DECODE_REG(srcDalvikReg) == DECODE_REG(dstDalvikReg)) {
+ cUnit->loopAnalysis->ssaBIV = mir->ssaRep->defs[0];
+ cIsConstant = false;
+ }
+
+ if (cIsConstant) {
+ unsigned int i;
+ dvmSetBit(isIndVarV, mir->ssaRep->defs[0]);
+ InductionVariableInfo *ivInfo =
+ dvmCompilerNew(sizeof(InductionVariableInfo),
+ false);
+ InductionVariableInfo *ivInfoOld = NULL ;
+
+ for (i = 0; i < ivList->numUsed; i++) {
+ ivInfoOld = ivList->elemList[i];
+ if (ivInfoOld->ssaReg == mir->ssaRep->uses[0]) break;
+ }
+
+ /* Guaranteed to find an element */
+ assert(i < ivList->numUsed);
+
+ ivInfo->ssaReg = mir->ssaRep->defs[0];
+ ivInfo->basicSSAReg = ivInfoOld->basicSSAReg;
+ ivInfo->m = ivInfoOld->m;
+ ivInfo->c = c + ivInfoOld->c;
+ ivInfo->inc = ivInfoOld->inc;
+ dvmInsertGrowableList(ivList, (void *) ivInfo);
+ }
+ }
+ }
+}
+
+/* Setup the basic data structures for SSA conversion */
+void dvmInitializeSSAConversion(CompilationUnit *cUnit)
+{
+ int i;
+ int numDalvikReg = cUnit->method->registersSize;
+
+ cUnit->ssaToDalvikMap = dvmCompilerNew(sizeof(GrowableList), false);
+ dvmInitGrowableList(cUnit->ssaToDalvikMap, numDalvikReg);
+
+ /*
+ * Initial number of SSA registers is equal to the number of Dalvik
+ * registers.
+ */
+ cUnit->numSSARegs = numDalvikReg;
+
+ /*
+ * Initialize the SSA2Dalvik map list. For the first numDalvikReg elements,
+ * the subscript is 0 so we use the ENCODE_REG_SUB macro to encode the value
+ * into "(0 << 16) | i"
+ */
+ for (i = 0; i < numDalvikReg; i++) {
+ dvmInsertGrowableList(cUnit->ssaToDalvikMap,
+ (void *) ENCODE_REG_SUB(i, 0));
+ }
+
+ /*
+ * Initialize the DalvikToSSAMap map. The low 16 bit is the SSA register id,
+ * while the high 16 bit is the current subscript. The original Dalvik
+ * register N is mapped to SSA register N with subscript 0.
+ */
+ cUnit->dalvikToSSAMap = dvmCompilerNew(sizeof(int) * numDalvikReg, false);
+ for (i = 0; i < numDalvikReg; i++) {
+ cUnit->dalvikToSSAMap[i] = i;
+ }
+
+ /*
+ * Allocate the BasicBlockDataFlow structure for the entry and code blocks
+ */
+ for (i = 0; i < cUnit->numBlocks; i++) {
+ BasicBlock *bb = cUnit->blockList[i];
+ if (bb->blockType == kDalvikByteCode ||
+ bb->blockType == kTraceEntryBlock) {
+ bb->dataFlowInfo = dvmCompilerNew(sizeof(BasicBlockDataFlow), true);
+ }
+ }
+}
+
+void dvmCompilerDataFlowAnalysisDispatcher(CompilationUnit *cUnit,
+ void (*func)(CompilationUnit *, BasicBlock *))
+{
+ int i;
+ for (i = 0; i < cUnit->numBlocks; i++) {
+ BasicBlock *bb = cUnit->blockList[i];
+ (*func)(cUnit, bb);
+ }
+}
+
+/* Main entry point to do SSA conversion for non-loop traces */
+void dvmCompilerNonLoopAnalysis(CompilationUnit *cUnit)
+{
+ dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerDoSSAConversion);
+}
diff --git a/vm/compiler/Dataflow.h b/vm/compiler/Dataflow.h
new file mode 100644
index 0000000..f3d3984
--- /dev/null
+++ b/vm/compiler/Dataflow.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DALVIK_VM_DATAFLOW
+#define _DALVIK_VM_DATAFLOW
+
+#include "Dalvik.h"
+#include "CompilerInternals.h"
+
+typedef enum DataFlowAttributePos {
+ kUA = 0,
+ kUB,
+ kUC,
+ kUAWide,
+ kUBWide,
+ kUCWide,
+ kDA,
+ kDAWide,
+ kIsMove,
+ kIsLinear,
+ kSetsConst,
+ kFormat35c,
+ kFormat3rc,
+ kPhi,
+ kNullNRangeCheck0,
+ kNullNRangeCheck1,
+ kNullNRangeCheck2,
+ kFPA,
+ kFPB,
+ kFPC,
+ kGetter,
+ kSetter,
+} DataFlowAttributes;
+
+#define DF_NOP 0
+#define DF_UA (1 << kUA)
+#define DF_UB (1 << kUB)
+#define DF_UC (1 << kUC)
+#define DF_UA_WIDE (1 << kUAWide)
+#define DF_UB_WIDE (1 << kUBWide)
+#define DF_UC_WIDE (1 << kUCWide)
+#define DF_DA (1 << kDA)
+#define DF_DA_WIDE (1 << kDAWide)
+#define DF_IS_MOVE (1 << kIsMove)
+#define DF_IS_LINEAR (1 << kIsLinear)
+#define DF_SETS_CONST (1 << kSetsConst)
+#define DF_FORMAT_35C (1 << kFormat35c)
+#define DF_FORMAT_3RC (1 << kFormat3rc)
+#define DF_PHI (1 << kPhi)
+#define DF_NULL_N_RANGE_CHECK_0 (1 << kNullNRangeCheck0)
+#define DF_NULL_N_RANGE_CHECK_1 (1 << kNullNRangeCheck1)
+#define DF_NULL_N_RANGE_CHECK_2 (1 << kNullNRangeCheck2)
+#define DF_FP_A (1 << kFPA)
+#define DF_FP_B (1 << kFPB)
+#define DF_FP_C (1 << kFPC)
+#define DF_IS_GETTER (1 << kGetter)
+#define DF_IS_SETTER (1 << kSetter)
+
+#define DF_HAS_USES (DF_UA | DF_UB | DF_UC | DF_UA_WIDE | \
+ DF_UB_WIDE | DF_UC_WIDE)
+
+#define DF_HAS_DEFS (DF_DA | DF_DA_WIDE)
+
+#define DF_HAS_NR_CHECKS (DF_NULL_N_RANGE_CHECK_0 | \
+ DF_NULL_N_RANGE_CHECK_1 | \
+ DF_NULL_N_RANGE_CHECK_2)
+
+#define DF_A_IS_REG (DF_UA | DF_UA_WIDE | DF_DA | DF_DA_WIDE)
+#define DF_B_IS_REG (DF_UB | DF_UB_WIDE)
+#define DF_C_IS_REG (DF_UC | DF_UC_WIDE)
+#define DF_IS_GETTER_OR_SETTER (DF_IS_GETTER | DF_IS_SETTER)
+
+extern int dvmCompilerDataFlowAttributes[kMirOpLast];
+
+typedef struct BasicBlockDataFlow {
+ BitVector *useV;
+ BitVector *defV;
+ BitVector *liveInV;
+ BitVector *phiV;
+ int *dalvikToSSAMap;
+} BasicBlockDataFlow;
+
+typedef struct SSARepresentation {
+ int numUses;
+ int *uses;
+ bool *fpUse;
+ int numDefs;
+ int *defs;
+ bool *fpDef;
+} SSARepresentation;
+
+typedef struct InductionVariableInfo {
+ int ssaReg;
+ int basicSSAReg;
+ int m;
+ int c;
+ int inc;
+} InductionVariableInfo;
+
+typedef struct ArrayAccessInfo {
+ int arrayReg;
+ int ivReg;
+ int maxC; // For DIV - will affect upper bound checking
+ int minC; // For DIV - will affect lower bound checking
+} ArrayAccessInfo;
+
+#define ENCODE_REG_SUB(r,s) ((s<<16) | r)
+#define DECODE_REG(v) (v & 0xffff)
+#define DECODE_SUB(v) (((unsigned int) v) >> 16)
+
+#endif /* _DALVIK_VM_DATAFLOW */
diff --git a/vm/compiler/Frontend.c b/vm/compiler/Frontend.c
new file mode 100644
index 0000000..525ec72
--- /dev/null
+++ b/vm/compiler/Frontend.c
@@ -0,0 +1,1313 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "libdex/OpCode.h"
+#include "interp/Jit.h"
+#include "CompilerInternals.h"
+#include "Dataflow.h"
+
+/*
+ * Parse an instruction, return the length of the instruction
+ */
+static inline int parseInsn(const u2 *codePtr, DecodedInstruction *decInsn,
+ bool printMe)
+{
+ u2 instr = *codePtr;
+ OpCode opcode = instr & 0xff;
+ int insnWidth;
+
+ // Don't parse instruction data
+ if (opcode == OP_NOP && instr != 0) {
+ return 0;
+ } else {
+ insnWidth = gDvm.instrWidth[opcode];
+ if (insnWidth < 0) {
+ insnWidth = -insnWidth;
+ }
+ }
+
+ dexDecodeInstruction(gDvm.instrFormat, codePtr, decInsn);
+ if (printMe) {
+ char *decodedString = dvmCompilerGetDalvikDisassembly(decInsn, NULL);
+ LOGD("%p: %#06x %s\n", codePtr, opcode, decodedString);
+ }
+ return insnWidth;
+}
+
+#define UNKNOWN_TARGET 0xffffffff
+
+/*
+ * Identify block-ending instructions and collect supplemental information
+ * regarding the following instructions.
+ */
+static inline bool findBlockBoundary(const Method *caller, MIR *insn,
+ unsigned int curOffset,
+ unsigned int *target, bool *isInvoke,
+ const Method **callee)
+{
+ switch (insn->dalvikInsn.opCode) {
+ /* Target is not compile-time constant */
+ case OP_RETURN_VOID:
+ case OP_RETURN:
+ case OP_RETURN_WIDE:
+ case OP_RETURN_OBJECT:
+ case OP_THROW:
+ *target = UNKNOWN_TARGET;
+ break;
+ case OP_INVOKE_VIRTUAL:
+ case OP_INVOKE_VIRTUAL_RANGE:
+ case OP_INVOKE_INTERFACE:
+ case OP_INVOKE_INTERFACE_RANGE:
+ case OP_INVOKE_VIRTUAL_QUICK:
+ case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+ *isInvoke = true;
+ break;
+ case OP_INVOKE_SUPER:
+ case OP_INVOKE_SUPER_RANGE: {
+ int mIndex = caller->clazz->pDvmDex->
+ pResMethods[insn->dalvikInsn.vB]->methodIndex;
+ const Method *calleeMethod =
+ caller->clazz->super->vtable[mIndex];
+
+ if (calleeMethod && !dvmIsNativeMethod(calleeMethod)) {
+ *target = (unsigned int) calleeMethod->insns;
+ }
+ *isInvoke = true;
+ *callee = calleeMethod;
+ break;
+ }
+ case OP_INVOKE_STATIC:
+ case OP_INVOKE_STATIC_RANGE: {
+ const Method *calleeMethod =
+ caller->clazz->pDvmDex->pResMethods[insn->dalvikInsn.vB];
+
+ if (calleeMethod && !dvmIsNativeMethod(calleeMethod)) {
+ *target = (unsigned int) calleeMethod->insns;
+ }
+ *isInvoke = true;
+ *callee = calleeMethod;
+ break;
+ }
+ case OP_INVOKE_SUPER_QUICK:
+ case OP_INVOKE_SUPER_QUICK_RANGE: {
+ const Method *calleeMethod =
+ caller->clazz->super->vtable[insn->dalvikInsn.vB];
+
+ if (calleeMethod && !dvmIsNativeMethod(calleeMethod)) {
+ *target = (unsigned int) calleeMethod->insns;
+ }
+ *isInvoke = true;
+ *callee = calleeMethod;
+ break;
+ }
+ case OP_INVOKE_DIRECT:
+ case OP_INVOKE_DIRECT_RANGE: {
+ const Method *calleeMethod =
+ caller->clazz->pDvmDex->pResMethods[insn->dalvikInsn.vB];
+ if (calleeMethod && !dvmIsNativeMethod(calleeMethod)) {
+ *target = (unsigned int) calleeMethod->insns;
+ }
+ *isInvoke = true;
+ *callee = calleeMethod;
+ break;
+ }
+ case OP_GOTO:
+ case OP_GOTO_16:
+ case OP_GOTO_32:
+ *target = curOffset + (int) insn->dalvikInsn.vA;
+ break;
+
+ case OP_IF_EQ:
+ case OP_IF_NE:
+ case OP_IF_LT:
+ case OP_IF_GE:
+ case OP_IF_GT:
+ case OP_IF_LE:
+ *target = curOffset + (int) insn->dalvikInsn.vC;
+ break;
+
+ case OP_IF_EQZ:
+ case OP_IF_NEZ:
+ case OP_IF_LTZ:
+ case OP_IF_GEZ:
+ case OP_IF_GTZ:
+ case OP_IF_LEZ:
+ *target = curOffset + (int) insn->dalvikInsn.vB;
+ break;
+
+ default:
+ return false;
+ }
+ return true;
+}
+
+static inline bool isGoto(MIR *insn)
+{
+ switch (insn->dalvikInsn.opCode) {
+ case OP_GOTO:
+ case OP_GOTO_16:
+ case OP_GOTO_32:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/*
+ * Identify unconditional branch instructions
+ */
+static inline bool isUnconditionalBranch(MIR *insn)
+{
+ switch (insn->dalvikInsn.opCode) {
+ case OP_RETURN_VOID:
+ case OP_RETURN:
+ case OP_RETURN_WIDE:
+ case OP_RETURN_OBJECT:
+ return true;
+ default:
+ return isGoto(insn);
+ }
+}
+
+/*
+ * dvmHashTableLookup() callback
+ */
+static int compareMethod(const CompilerMethodStats *m1,
+ const CompilerMethodStats *m2)
+{
+ return (int) m1->method - (int) m2->method;
+}
+
+/*
+ * Analyze the body of the method to collect high-level information regarding
+ * inlining:
+ * - is empty method?
+ * - is getter/setter?
+ * - can throw exception?
+ *
+ * Currently the inliner only handles getters and setters. When its capability
+ * becomes more sophisticated more information will be retrieved here.
+ */
+static int analyzeInlineTarget(DecodedInstruction *dalvikInsn, int attributes,
+ int offset)
+{
+ int flags = dexGetInstrFlags(gDvm.instrFlags, dalvikInsn->opCode);
+ int dalvikOpCode = dalvikInsn->opCode;
+
+ if ((flags & kInstrInvoke) &&
+ (dalvikOpCode != OP_INVOKE_DIRECT_EMPTY)) {
+ attributes &= ~METHOD_IS_LEAF;
+ }
+
+ if (!(flags & kInstrCanReturn)) {
+ if (!(dvmCompilerDataFlowAttributes[dalvikOpCode] &
+ DF_IS_GETTER)) {
+ attributes &= ~METHOD_IS_GETTER;
+ }
+ if (!(dvmCompilerDataFlowAttributes[dalvikOpCode] &
+ DF_IS_SETTER)) {
+ attributes &= ~METHOD_IS_SETTER;
+ }
+ }
+
+ /*
+ * The expected instruction sequence is setter will never return value and
+ * getter will also do. Clear the bits if the behavior is discovered
+ * otherwise.
+ */
+ if (flags & kInstrCanReturn) {
+ if (dalvikOpCode == OP_RETURN_VOID) {
+ attributes &= ~METHOD_IS_GETTER;
+ }
+ else {
+ attributes &= ~METHOD_IS_SETTER;
+ }
+ }
+
+ if (flags & kInstrCanThrow) {
+ attributes &= ~METHOD_IS_THROW_FREE;
+ }
+
+ if (offset == 0 && dalvikOpCode == OP_RETURN_VOID) {
+ attributes |= METHOD_IS_EMPTY;
+ }
+
+ /*
+ * Check if this opcode is selected for single stepping.
+ * If so, don't inline the callee as there is no stack frame for the
+ * interpreter to single-step through the instruction.
+ */
+ if (SINGLE_STEP_OP(dalvikOpCode)) {
+ attributes &= ~(METHOD_IS_GETTER | METHOD_IS_SETTER);
+ }
+
+ return attributes;
+}
+
+/*
+ * Analyze each method whose traces are ever compiled. Collect a variety of
+ * statistics like the ratio of exercised vs overall code and code bloat
+ * ratios. If isCallee is true, also analyze each instruction in more details
+ * to see if it is suitable for inlining.
+ */
+CompilerMethodStats *dvmCompilerAnalyzeMethodBody(const Method *method,
+ bool isCallee)
+{
+ const DexCode *dexCode = dvmGetMethodCode(method);
+ const u2 *codePtr = dexCode->insns;
+ const u2 *codeEnd = dexCode->insns + dexCode->insnsSize;
+ int insnSize = 0;
+ int hashValue = dvmComputeUtf8Hash(method->name);
+
+ CompilerMethodStats dummyMethodEntry; // For hash table lookup
+ CompilerMethodStats *realMethodEntry; // For hash table storage
+
+ /* For lookup only */
+ dummyMethodEntry.method = method;
+ realMethodEntry = dvmHashTableLookup(gDvmJit.methodStatsTable, hashValue,
+ &dummyMethodEntry,
+ (HashCompareFunc) compareMethod,
+ false);
+
+ /* This method has never been analyzed before - create an entry */
+ if (realMethodEntry == NULL) {
+ realMethodEntry =
+ (CompilerMethodStats *) calloc(1, sizeof(CompilerMethodStats));
+ realMethodEntry->method = method;
+
+ dvmHashTableLookup(gDvmJit.methodStatsTable, hashValue,
+ realMethodEntry,
+ (HashCompareFunc) compareMethod,
+ true);
+ }
+
+ /* This method is invoked as a callee and has been analyzed - just return */
+ if ((isCallee == true) && (realMethodEntry->attributes & METHOD_IS_CALLEE))
+ return realMethodEntry;
+
+ /*
+ * Similarly, return if this method has been compiled before as a hot
+ * method already.
+ */
+ if ((isCallee == false) &&
+ (realMethodEntry->attributes & METHOD_IS_HOT))
+ return realMethodEntry;
+
+ int attributes;
+
+ /* Method hasn't been analyzed for the desired purpose yet */
+ if (isCallee) {
+ /* Aggressively set the attributes until proven otherwise */
+ attributes = METHOD_IS_LEAF | METHOD_IS_THROW_FREE | METHOD_IS_CALLEE |
+ METHOD_IS_GETTER | METHOD_IS_SETTER;
+ } else {
+ attributes = METHOD_IS_HOT;
+ }
+
+ /* Count the number of instructions */
+ while (codePtr < codeEnd) {
+ DecodedInstruction dalvikInsn;
+ int width = parseInsn(codePtr, &dalvikInsn, false);
+
+ /* Terminate when the data section is seen */
+ if (width == 0)
+ break;
+
+ if (isCallee) {
+ attributes = analyzeInlineTarget(&dalvikInsn, attributes, insnSize);
+ }
+
+ insnSize += width;
+ codePtr += width;
+ }
+
+ /*
+ * Only handle simple getters/setters with one instruction followed by
+ * return
+ */
+ if ((attributes & (METHOD_IS_GETTER | METHOD_IS_SETTER)) &&
+ (insnSize != 3)) {
+ attributes &= ~(METHOD_IS_GETTER | METHOD_IS_SETTER);
+ }
+
+ realMethodEntry->dalvikSize = insnSize * 2;
+ realMethodEntry->attributes |= attributes;
+
+#if 0
+ /* Uncomment the following to explore various callee patterns */
+ if (attributes & METHOD_IS_THROW_FREE) {
+ LOGE("%s%s is inlinable%s", method->clazz->descriptor, method->name,
+ (attributes & METHOD_IS_EMPTY) ? " empty" : "");
+ }
+
+ if (attributes & METHOD_IS_LEAF) {
+ LOGE("%s%s is leaf %d%s", method->clazz->descriptor, method->name,
+ insnSize, insnSize < 5 ? " (small)" : "");
+ }
+
+ if (attributes & (METHOD_IS_GETTER | METHOD_IS_SETTER)) {
+ LOGE("%s%s is %s", method->clazz->descriptor, method->name,
+ attributes & METHOD_IS_GETTER ? "getter": "setter");
+ }
+ if (attributes ==
+ (METHOD_IS_LEAF | METHOD_IS_THROW_FREE | METHOD_IS_CALLEE)) {
+ LOGE("%s%s is inlinable non setter/getter", method->clazz->descriptor,
+ method->name);
+ }
+#endif
+
+ return realMethodEntry;
+}
+
+/*
+ * Crawl the stack of the thread that requesed compilation to see if any of the
+ * ancestors are on the blacklist.
+ */
+bool filterMethodByCallGraph(Thread *thread, const char *curMethodName)
+{
+ /* Crawl the Dalvik stack frames and compare the method name*/
+ StackSaveArea *ssaPtr = ((StackSaveArea *) thread->curFrame) - 1;
+ while (ssaPtr != ((StackSaveArea *) NULL) - 1) {
+ const Method *method = ssaPtr->method;
+ if (method) {
+ int hashValue = dvmComputeUtf8Hash(method->name);
+ bool found =
+ dvmHashTableLookup(gDvmJit.methodTable, hashValue,
+ (char *) method->name,
+ (HashCompareFunc) strcmp, false) !=
+ NULL;
+ if (found) {
+ LOGD("Method %s (--> %s) found on the JIT %s list",
+ method->name, curMethodName,
+ gDvmJit.includeSelectedMethod ? "white" : "black");
+ return true;
+ }
+
+ }
+ ssaPtr = ((StackSaveArea *) ssaPtr->prevFrame) - 1;
+ };
+ return false;
+}
+
+/*
+ * Main entry point to start trace compilation. Basic blocks are constructed
+ * first and they will be passed to the codegen routines to convert Dalvik
+ * bytecode into machine code.
+ */
+bool dvmCompileTrace(JitTraceDescription *desc, int numMaxInsts,
+ JitTranslationInfo *info, jmp_buf *bailPtr,
+ int optHints)
+{
+ const DexCode *dexCode = dvmGetMethodCode(desc->method);
+ const JitTraceRun* currRun = &desc->trace[0];
+ unsigned int curOffset = currRun->frag.startOffset;
+ unsigned int numInsts = currRun->frag.numInsts;
+ const u2 *codePtr = dexCode->insns + curOffset;
+ int traceSize = 0; // # of half-words
+ const u2 *startCodePtr = codePtr;
+ BasicBlock *startBB, *curBB, *lastBB;
+ int numBlocks = 0;
+ static int compilationId;
+ CompilationUnit cUnit;
+#if defined(WITH_JIT_TUNING)
+ CompilerMethodStats *methodStats;
+#endif
+
+ /* If we've already compiled this trace, just return success */
+ if (dvmJitGetCodeAddr(startCodePtr) && !info->discardResult) {
+ /*
+ * Make sure the codeAddress is NULL so that it won't clobber the
+ * existing entry.
+ */
+ info->codeAddress = NULL;
+ return true;
+ }
+
+ compilationId++;
+ memset(&cUnit, 0, sizeof(CompilationUnit));
+
+#if defined(WITH_JIT_TUNING)
+ /* Locate the entry to store compilation statistics for this method */
+ methodStats = dvmCompilerAnalyzeMethodBody(desc->method, false);
+#endif
+
+ /* Set the recover buffer pointer */
+ cUnit.bailPtr = bailPtr;
+
+ /* Initialize the printMe flag */
+ cUnit.printMe = gDvmJit.printMe;
+
+ /* Initialize the profile flag */
+ cUnit.executionCount = gDvmJit.profile;
+
+ /* Setup the method */
+ cUnit.method = desc->method;
+
+ /* Initialize the PC reconstruction list */
+ dvmInitGrowableList(&cUnit.pcReconstructionList, 8);
+
+ /* Identify traces that we don't want to compile */
+ if (gDvmJit.methodTable) {
+ int len = strlen(desc->method->clazz->descriptor) +
+ strlen(desc->method->name) + 1;
+ char *fullSignature = dvmCompilerNew(len, true);
+ strcpy(fullSignature, desc->method->clazz->descriptor);
+ strcat(fullSignature, desc->method->name);
+
+ int hashValue = dvmComputeUtf8Hash(fullSignature);
+
+ /*
+ * Doing three levels of screening to see whether we want to skip
+ * compiling this method
+ */
+
+ /* First, check the full "class;method" signature */
+ bool methodFound =
+ dvmHashTableLookup(gDvmJit.methodTable, hashValue,
+ fullSignature, (HashCompareFunc) strcmp,
+ false) !=
+ NULL;
+
+ /* Full signature not found - check the enclosing class */
+ if (methodFound == false) {
+ int hashValue = dvmComputeUtf8Hash(desc->method->clazz->descriptor);
+ methodFound =
+ dvmHashTableLookup(gDvmJit.methodTable, hashValue,
+ (char *) desc->method->clazz->descriptor,
+ (HashCompareFunc) strcmp, false) !=
+ NULL;
+ /* Enclosing class not found - check the method name */
+ if (methodFound == false) {
+ int hashValue = dvmComputeUtf8Hash(desc->method->name);
+ methodFound =
+ dvmHashTableLookup(gDvmJit.methodTable, hashValue,
+ (char *) desc->method->name,
+ (HashCompareFunc) strcmp, false) !=
+ NULL;
+
+ /*
+ * Debug by call-graph is enabled. Check if the debug list
+ * covers any methods on the VM stack.
+ */
+ if (methodFound == false && gDvmJit.checkCallGraph == true) {
+ methodFound =
+ filterMethodByCallGraph(info->requestingThread,
+ desc->method->name);
+ }
+ }
+ }
+
+ /*
+ * Under the following conditions, the trace will be *conservatively*
+ * compiled by only containing single-step instructions to and from the
+ * interpreter.
+ * 1) If includeSelectedMethod == false, the method matches the full or
+ * partial signature stored in the hash table.
+ *
+ * 2) If includeSelectedMethod == true, the method does not match the
+ * full and partial signature stored in the hash table.
+ */
+ if (gDvmJit.includeSelectedMethod != methodFound) {
+ cUnit.allSingleStep = true;
+ } else {
+ /* Compile the trace as normal */
+
+ /* Print the method we cherry picked */
+ if (gDvmJit.includeSelectedMethod == true) {
+ cUnit.printMe = true;
+ }
+ }
+ }
+
+ /* Allocate the entry block */
+ lastBB = startBB = curBB = dvmCompilerNewBB(kTraceEntryBlock);
+ curBB->startOffset = curOffset;
+ curBB->id = numBlocks++;
+
+ curBB = dvmCompilerNewBB(kDalvikByteCode);
+ curBB->startOffset = curOffset;
+ curBB->id = numBlocks++;
+
+ /* Make the first real dalvik block the fallthrough of the entry block */
+ startBB->fallThrough = curBB;
+ lastBB->next = curBB;
+ lastBB = curBB;
+
+ if (cUnit.printMe) {
+ LOGD("--------\nCompiler: Building trace for %s, offset 0x%x\n",
+ desc->method->name, curOffset);
+ }
+
+ /*
+ * Analyze the trace descriptor and include up to the maximal number
+ * of Dalvik instructions into the IR.
+ */
+ while (1) {
+ MIR *insn;
+ int width;
+ insn = dvmCompilerNew(sizeof(MIR), true);
+ insn->offset = curOffset;
+ width = parseInsn(codePtr, &insn->dalvikInsn, cUnit.printMe);
+
+ /* The trace should never incude instruction data */
+ assert(width);
+ insn->width = width;
+ traceSize += width;
+ dvmCompilerAppendMIR(curBB, insn);
+ cUnit.numInsts++;
+
+ int flags = dexGetInstrFlags(gDvm.instrFlags, insn->dalvikInsn.opCode);
+
+ if ((flags & kInstrInvoke) &&
+ (insn->dalvikInsn.opCode != OP_INVOKE_DIRECT_EMPTY)) {
+ assert(numInsts == 1);
+ CallsiteInfo *callsiteInfo =
+ dvmCompilerNew(sizeof(CallsiteInfo), true);
+ callsiteInfo->clazz = currRun[1].meta;
+ callsiteInfo->method = currRun[2].meta;
+ insn->meta.callsiteInfo = callsiteInfo;
+ }
+
+ /* Instruction limit reached - terminate the trace here */
+ if (cUnit.numInsts >= numMaxInsts) {
+ break;
+ }
+ if (--numInsts == 0) {
+ if (currRun->frag.runEnd) {
+ break;
+ } else {
+ /* Advance to the next trace description (ie non-meta info) */
+ do {
+ currRun++;
+ } while (!currRun->frag.isCode);
+
+ /* Dummy end-of-run marker seen */
+ if (currRun->frag.numInsts == 0) {
+ break;
+ }
+
+ curBB = dvmCompilerNewBB(kDalvikByteCode);
+ lastBB->next = curBB;
+ lastBB = curBB;
+ curBB->id = numBlocks++;
+ curOffset = currRun->frag.startOffset;
+ numInsts = currRun->frag.numInsts;
+ curBB->startOffset = curOffset;
+ codePtr = dexCode->insns + curOffset;
+ }
+ } else {
+ curOffset += width;
+ codePtr += width;
+ }
+ }
+
+#if defined(WITH_JIT_TUNING)
+ /* Convert # of half-word to bytes */
+ methodStats->compiledDalvikSize += traceSize * 2;
+#endif
+
+ /*
+ * Now scan basic blocks containing real code to connect the
+ * taken/fallthrough links. Also create chaining cells for code not included
+ * in the trace.
+ */
+ for (curBB = startBB; curBB; curBB = curBB->next) {
+ MIR *lastInsn = curBB->lastMIRInsn;
+ /* Skip empty blocks */
+ if (lastInsn == NULL) {
+ continue;
+ }
+ curOffset = lastInsn->offset;
+ unsigned int targetOffset = curOffset;
+ unsigned int fallThroughOffset = curOffset + lastInsn->width;
+ bool isInvoke = false;
+ const Method *callee = NULL;
+
+ findBlockBoundary(desc->method, curBB->lastMIRInsn, curOffset,
+ &targetOffset, &isInvoke, &callee);
+
+ /* Link the taken and fallthrough blocks */
+ BasicBlock *searchBB;
+
+ int flags = dexGetInstrFlags(gDvm.instrFlags,
+ lastInsn->dalvikInsn.opCode);
+
+ if (flags & kInstrInvoke) {
+ cUnit.hasInvoke = true;
+ }
+
+ /* No backward branch in the trace - start searching the next BB */
+ for (searchBB = curBB->next; searchBB; searchBB = searchBB->next) {
+ if (targetOffset == searchBB->startOffset) {
+ curBB->taken = searchBB;
+ }
+ if (fallThroughOffset == searchBB->startOffset) {
+ curBB->fallThrough = searchBB;
+
+ /*
+ * Fallthrough block of an invoke instruction needs to be
+ * aligned to 4-byte boundary (alignment instruction to be
+ * inserted later.
+ */
+ if (flags & kInstrInvoke) {
+ searchBB->isFallThroughFromInvoke = true;
+ }
+ }
+ }
+
+ /*
+ * Some blocks are ended by non-control-flow-change instructions,
+ * currently only due to trace length constraint. In this case we need
+ * to generate an explicit branch at the end of the block to jump to
+ * the chaining cell.
+ *
+ * NOTE: INVOKE_DIRECT_EMPTY is actually not an invoke but a nop
+ */
+ curBB->needFallThroughBranch =
+ ((flags & (kInstrCanBranch | kInstrCanSwitch | kInstrCanReturn |
+ kInstrInvoke)) == 0) ||
+ (lastInsn->dalvikInsn.opCode == OP_INVOKE_DIRECT_EMPTY);
+
+ /* Only form a loop if JIT_OPT_NO_LOOP is not set */
+ if (curBB->taken == NULL &&
+ curBB->fallThrough == NULL &&
+ flags == (kInstrCanBranch | kInstrCanContinue) &&
+ fallThroughOffset == startBB->startOffset &&
+ JIT_OPT_NO_LOOP != (optHints & JIT_OPT_NO_LOOP)) {
+ BasicBlock *loopBranch = curBB;
+ BasicBlock *exitBB;
+ BasicBlock *exitChainingCell;
+
+ if (cUnit.printMe) {
+ LOGD("Natural loop detected!");
+ }
+ exitBB = dvmCompilerNewBB(kTraceExitBlock);
+ lastBB->next = exitBB;
+ lastBB = exitBB;
+
+ exitBB->startOffset = targetOffset;
+ exitBB->id = numBlocks++;
+ exitBB->needFallThroughBranch = true;
+
+ loopBranch->taken = exitBB;
+#if defined(WITH_SELF_VERIFICATION)
+ BasicBlock *backwardCell =
+ dvmCompilerNewBB(kChainingCellBackwardBranch);
+ lastBB->next = backwardCell;
+ lastBB = backwardCell;
+
+ backwardCell->startOffset = startBB->startOffset;
+ backwardCell->id = numBlocks++;
+ loopBranch->fallThrough = backwardCell;
+#elif defined(WITH_JIT_TUNING)
+ if (gDvmJit.profile) {
+ BasicBlock *backwardCell =
+ dvmCompilerNewBB(kChainingCellBackwardBranch);
+ lastBB->next = backwardCell;
+ lastBB = backwardCell;
+
+ backwardCell->startOffset = startBB->startOffset;
+ backwardCell->id = numBlocks++;
+ loopBranch->fallThrough = backwardCell;
+ } else {
+ loopBranch->fallThrough = startBB->next;
+ }
+#else
+ loopBranch->fallThrough = startBB->next;
+#endif
+
+ /* Create the chaining cell as the fallthrough of the exit block */
+ exitChainingCell = dvmCompilerNewBB(kChainingCellNormal);
+ lastBB->next = exitChainingCell;
+ lastBB = exitChainingCell;
+
+ exitChainingCell->startOffset = targetOffset;
+ exitChainingCell->id = numBlocks++;
+
+ exitBB->fallThrough = exitChainingCell;
+
+ cUnit.hasLoop = true;
+ }
+
+ if (lastInsn->dalvikInsn.opCode == OP_PACKED_SWITCH ||
+ lastInsn->dalvikInsn.opCode == OP_SPARSE_SWITCH) {
+ int i;
+ const u2 *switchData = desc->method->insns + lastInsn->offset +
+ lastInsn->dalvikInsn.vB;
+ int size = switchData[1];
+ int maxChains = MIN(size, MAX_CHAINED_SWITCH_CASES);
+
+ /*
+ * Generate the landing pad for cases whose ranks are higher than
+ * MAX_CHAINED_SWITCH_CASES. The code will re-enter the interpreter
+ * through the NoChain point.
+ */
+ if (maxChains != size) {
+ cUnit.switchOverflowPad =
+ desc->method->insns + lastInsn->offset;
+ }
+
+ s4 *targets = (s4 *) (switchData + 2 +
+ (lastInsn->dalvikInsn.opCode == OP_PACKED_SWITCH ?
+ 2 : size * 2));
+
+ /* One chaining cell for the first MAX_CHAINED_SWITCH_CASES cases */
+ for (i = 0; i < maxChains; i++) {
+ BasicBlock *caseChain = dvmCompilerNewBB(kChainingCellNormal);
+ lastBB->next = caseChain;
+ lastBB = caseChain;
+
+ caseChain->startOffset = lastInsn->offset + targets[i];
+ caseChain->id = numBlocks++;
+ }
+
+ /* One more chaining cell for the default case */
+ BasicBlock *caseChain = dvmCompilerNewBB(kChainingCellNormal);
+ lastBB->next = caseChain;
+ lastBB = caseChain;
+
+ caseChain->startOffset = lastInsn->offset + lastInsn->width;
+ caseChain->id = numBlocks++;
+ /* Fallthrough block not included in the trace */
+ } else if (!isUnconditionalBranch(lastInsn) &&
+ curBB->fallThrough == NULL) {
+ /*
+ * If the chaining cell is after an invoke or
+ * instruction that cannot change the control flow, request a hot
+ * chaining cell.
+ */
+ if (isInvoke || curBB->needFallThroughBranch) {
+ lastBB->next = dvmCompilerNewBB(kChainingCellHot);
+ } else {
+ lastBB->next = dvmCompilerNewBB(kChainingCellNormal);
+ }
+ lastBB = lastBB->next;
+ lastBB->id = numBlocks++;
+ lastBB->startOffset = fallThroughOffset;
+ curBB->fallThrough = lastBB;
+ }
+ /* Target block not included in the trace */
+ if (curBB->taken == NULL &&
+ (isGoto(lastInsn) || isInvoke ||
+ (targetOffset != UNKNOWN_TARGET && targetOffset != curOffset))) {
+ BasicBlock *newBB;
+ if (isInvoke) {
+ /* Monomorphic callee */
+ if (callee) {
+ newBB = dvmCompilerNewBB(kChainingCellInvokeSingleton);
+ newBB->startOffset = 0;
+ newBB->containingMethod = callee;
+ /* Will resolve at runtime */
+ } else {
+ newBB = dvmCompilerNewBB(kChainingCellInvokePredicted);
+ newBB->startOffset = 0;
+ }
+ /* For unconditional branches, request a hot chaining cell */
+ } else {
+#if !defined(WITH_SELF_VERIFICATION)
+ newBB = dvmCompilerNewBB(flags & kInstrUnconditional ?
+ kChainingCellHot :
+ kChainingCellNormal);
+ newBB->startOffset = targetOffset;
+#else
+ /* Handle branches that branch back into the block */
+ if (targetOffset >= curBB->firstMIRInsn->offset &&
+ targetOffset <= curBB->lastMIRInsn->offset) {
+ newBB = dvmCompilerNewBB(kChainingCellBackwardBranch);
+ } else {
+ newBB = dvmCompilerNewBB(flags & kInstrUnconditional ?
+ kChainingCellHot :
+ kChainingCellNormal);
+ }
+ newBB->startOffset = targetOffset;
+#endif
+ }
+ newBB->id = numBlocks++;
+ curBB->taken = newBB;
+ lastBB->next = newBB;
+ lastBB = newBB;
+ }
+ }
+
+ /* Now create a special block to host PC reconstruction code */
+ lastBB->next = dvmCompilerNewBB(kPCReconstruction);
+ lastBB = lastBB->next;
+ lastBB->id = numBlocks++;
+
+ /* And one final block that publishes the PC and raise the exception */
+ lastBB->next = dvmCompilerNewBB(kExceptionHandling);
+ lastBB = lastBB->next;
+ lastBB->id = numBlocks++;
+
+ if (cUnit.printMe) {
+ char* signature = dexProtoCopyMethodDescriptor(&desc->method->prototype);
+ LOGD("TRACEINFO (%d): 0x%08x %s%s.%s 0x%x %d of %d, %d blocks",
+ compilationId,
+ (intptr_t) desc->method->insns,
+ desc->method->clazz->descriptor,
+ desc->method->name,
+ signature,
+ desc->trace[0].frag.startOffset,
+ traceSize,
+ dexCode->insnsSize,
+ numBlocks);
+ free(signature);
+ }
+
+ BasicBlock **blockList;
+
+ cUnit.traceDesc = desc;
+ cUnit.numBlocks = numBlocks;
+ blockList = cUnit.blockList =
+ dvmCompilerNew(sizeof(BasicBlock *) * numBlocks, true);
+
+ int i;
+
+ for (i = 0, curBB = startBB; i < numBlocks; i++) {
+ blockList[i] = curBB;
+ curBB = curBB->next;
+ }
+ /* Make sure all blocks are added to the cUnit */
+ assert(curBB == NULL);
+
+ /* Set the instruction set to use (NOTE: later components may change it) */
+ cUnit.instructionSet = dvmCompilerInstructionSet();
+
+ /* Inline transformation @ the MIR level */
+ if (cUnit.hasInvoke && !(gDvmJit.disableOpt & (1 << kMethodInlining))) {
+ dvmCompilerInlineMIR(&cUnit);
+ }
+
+ /* Preparation for SSA conversion */
+ dvmInitializeSSAConversion(&cUnit);
+
+ if (cUnit.hasLoop) {
+ /*
+ * Loop is not optimizable (for example lack of a single induction
+ * variable), punt and recompile the trace with loop optimization
+ * disabled.
+ */
+ bool loopOpt = dvmCompilerLoopOpt(&cUnit);
+ if (loopOpt == false) {
+ if (cUnit.printMe) {
+ LOGD("Loop is not optimizable - retry codegen");
+ }
+ /* Reset the compiler resource pool */
+ dvmCompilerArenaReset();
+ return dvmCompileTrace(desc, cUnit.numInsts, info, bailPtr,
+ optHints | JIT_OPT_NO_LOOP);
+ }
+ }
+ else {
+ dvmCompilerNonLoopAnalysis(&cUnit);
+ }
+
+ dvmCompilerInitializeRegAlloc(&cUnit); // Needs to happen after SSA naming
+
+ if (cUnit.printMe) {
+ dvmCompilerDumpCompilationUnit(&cUnit);
+ }
+
+ /* Allocate Registers */
+ dvmCompilerRegAlloc(&cUnit);
+
+ /* Convert MIR to LIR, etc. */
+ dvmCompilerMIR2LIR(&cUnit);
+
+ /* Convert LIR into machine code. Loop for recoverable retries */
+ do {
+ dvmCompilerAssembleLIR(&cUnit, info);
+ cUnit.assemblerRetries++;
+ if (cUnit.printMe && cUnit.assemblerStatus != kSuccess)
+ LOGD("Assembler abort #%d on %d",cUnit.assemblerRetries,
+ cUnit.assemblerStatus);
+ } while (cUnit.assemblerStatus == kRetryAll);
+
+ if (cUnit.printMe) {
+ dvmCompilerCodegenDump(&cUnit);
+ LOGD("End %s%s, %d Dalvik instructions",
+ desc->method->clazz->descriptor, desc->method->name,
+ cUnit.numInsts);
+ }
+
+ /* Reset the compiler resource pool */
+ dvmCompilerArenaReset();
+
+ if (cUnit.assemblerStatus == kRetryHalve) {
+ /* Halve the instruction count and start from the top */
+ return dvmCompileTrace(desc, cUnit.numInsts / 2, info, bailPtr,
+ optHints);
+ }
+
+ assert(cUnit.assemblerStatus == kSuccess);
+#if defined(WITH_JIT_TUNING)
+ methodStats->nativeSize += cUnit.totalSize;
+#endif
+ return info->codeAddress != NULL;
+}
+
+/*
+ * Since we are including instructions from possibly a cold method into the
+ * current trace, we need to make sure that all the associated information
+ * with the callee is properly initialized. If not, we punt on this inline
+ * target.
+ *
+ * TODO: volatile instructions will be handled later.
+ */
+bool dvmCompilerCanIncludeThisInstruction(const Method *method,
+ const DecodedInstruction *insn)
+{
+ switch (insn->opCode) {
+ case OP_NEW_INSTANCE:
+ case OP_CHECK_CAST: {
+ ClassObject *classPtr = (void*)
+ (method->clazz->pDvmDex->pResClasses[insn->vB]);
+
+ /* Class hasn't been initialized yet */
+ if (classPtr == NULL) {
+ return false;
+ }
+ return true;
+ }
+ case OP_SGET_OBJECT:
+ case OP_SGET_BOOLEAN:
+ case OP_SGET_CHAR:
+ case OP_SGET_BYTE:
+ case OP_SGET_SHORT:
+ case OP_SGET:
+ case OP_SGET_WIDE:
+ case OP_SPUT_OBJECT:
+ case OP_SPUT_BOOLEAN:
+ case OP_SPUT_CHAR:
+ case OP_SPUT_BYTE:
+ case OP_SPUT_SHORT:
+ case OP_SPUT:
+ case OP_SPUT_WIDE: {
+ void *fieldPtr = (void*)
+ (method->clazz->pDvmDex->pResFields[insn->vB]);
+
+ if (fieldPtr == NULL) {
+ return false;
+ }
+ return true;
+ }
+ case OP_INVOKE_SUPER:
+ case OP_INVOKE_SUPER_RANGE: {
+ int mIndex = method->clazz->pDvmDex->
+ pResMethods[insn->vB]->methodIndex;
+ const Method *calleeMethod = method->clazz->super->vtable[mIndex];
+ if (calleeMethod == NULL) {
+ return false;
+ }
+ return true;
+ }
+ case OP_INVOKE_SUPER_QUICK:
+ case OP_INVOKE_SUPER_QUICK_RANGE: {
+ const Method *calleeMethod = method->clazz->super->vtable[insn->vB];
+ if (calleeMethod == NULL) {
+ return false;
+ }
+ return true;
+ }
+ case OP_INVOKE_STATIC:
+ case OP_INVOKE_STATIC_RANGE:
+ case OP_INVOKE_DIRECT:
+ case OP_INVOKE_DIRECT_RANGE: {
+ const Method *calleeMethod =
+ method->clazz->pDvmDex->pResMethods[insn->vB];
+ if (calleeMethod == NULL) {
+ return false;
+ }
+ return true;
+ }
+ case OP_CONST_CLASS: {
+ void *classPtr = (void*)
+ (method->clazz->pDvmDex->pResClasses[insn->vB]);
+
+ if (classPtr == NULL) {
+ return false;
+ }
+ return true;
+ }
+ case OP_CONST_STRING_JUMBO:
+ case OP_CONST_STRING: {
+ void *strPtr = (void*)
+ (method->clazz->pDvmDex->pResStrings[insn->vB]);
+
+ if (strPtr == NULL) {
+ return false;
+ }
+ return true;
+ }
+ default:
+ return true;
+ }
+}
+
+/*
+ * Similar to dvmCompileTrace, but the entity processed here is the whole
+ * method.
+ *
+ * TODO: implementation will be revisited when the trace builder can provide
+ * whole-method traces.
+ */
+bool dvmCompileMethod(CompilationUnit *cUnit, const Method *method,
+ JitTranslationInfo *info)
+{
+ const DexCode *dexCode = dvmGetMethodCode(method);
+ const u2 *codePtr = dexCode->insns;
+ const u2 *codeEnd = dexCode->insns + dexCode->insnsSize;
+ int blockID = 0;
+ unsigned int curOffset = 0;
+
+ /* If we've already compiled this trace, just return success */
+ if (dvmJitGetCodeAddr(codePtr) && !info->discardResult) {
+ return true;
+ }
+
+ /* Doing method-based compilation */
+ cUnit->wholeMethod = true;
+
+ BasicBlock *firstBlock = dvmCompilerNewBB(kDalvikByteCode);
+ firstBlock->id = blockID++;
+
+ /* Allocate the bit-vector to track the beginning of basic blocks */
+ BitVector *bbStartAddr = dvmCompilerAllocBitVector(dexCode->insnsSize+1,
+ false);
+ dvmCompilerSetBit(bbStartAddr, 0);
+
+ int numInvokeTargets = 0;
+
+ /*
+ * Sequentially go through every instruction first and put them in a single
+ * basic block. Identify block boundaries at the mean time.
+ */
+ while (codePtr < codeEnd) {
+ MIR *insn = dvmCompilerNew(sizeof(MIR), true);
+ insn->offset = curOffset;
+ int width = parseInsn(codePtr, &insn->dalvikInsn, false);
+ bool isInvoke = false;
+ const Method *callee;
+ insn->width = width;
+
+ /* Terminate when the data section is seen */
+ if (width == 0)
+ break;
+
+ if (!dvmCompilerCanIncludeThisInstruction(cUnit->method,
+ &insn->dalvikInsn)) {
+ return false;
+ }
+
+ dvmCompilerAppendMIR(firstBlock, insn);
+ /*
+ * Check whether this is a block ending instruction and whether it
+ * suggests the start of a new block
+ */
+ unsigned int target = curOffset;
+
+ /*
+ * If findBlockBoundary returns true, it means the current instruction
+ * is terminating the current block. If it is a branch, the target
+ * address will be recorded in target.
+ */
+ if (findBlockBoundary(method, insn, curOffset, &target, &isInvoke,
+ &callee)) {
+ dvmCompilerSetBit(bbStartAddr, curOffset + width);
+ /* Each invoke needs a chaining cell block */
+ if (isInvoke) {
+ numInvokeTargets++;
+ }
+ /* A branch will end the current block */
+ else if (target != curOffset && target != UNKNOWN_TARGET) {
+ dvmCompilerSetBit(bbStartAddr, target);
+ }
+ }
+
+ codePtr += width;
+ /* each bit represents 16-bit quantity */
+ curOffset += width;
+ }
+
+ /*
+ * The number of blocks will be equal to the number of bits set to 1 in the
+ * bit vector minus 1, because the bit representing the location after the
+ * last instruction is set to one.
+ *
+ * We also add additional blocks for invoke chaining and the number is
+ * denoted by numInvokeTargets.
+ */
+ int numBlocks = dvmCountSetBits(bbStartAddr);
+ if (dvmIsBitSet(bbStartAddr, dexCode->insnsSize)) {
+ numBlocks--;
+ }
+
+ BasicBlock **blockList;
+ blockList = cUnit->blockList =
+ dvmCompilerNew(sizeof(BasicBlock *) * (numBlocks + numInvokeTargets),
+ true);
+
+ /*
+ * Register the first block onto the list and start splitting it into
+ * sub-blocks.
+ */
+ blockList[0] = firstBlock;
+ cUnit->numBlocks = 1;
+
+ int i;
+ for (i = 0; i < numBlocks; i++) {
+ MIR *insn;
+ BasicBlock *curBB = blockList[i];
+ curOffset = curBB->lastMIRInsn->offset;
+
+ for (insn = curBB->firstMIRInsn->next; insn; insn = insn->next) {
+ /* Found the beginning of a new block, see if it is created yet */
+ if (dvmIsBitSet(bbStartAddr, insn->offset)) {
+ int j;
+ for (j = 0; j < cUnit->numBlocks; j++) {
+ if (blockList[j]->firstMIRInsn->offset == insn->offset)
+ break;
+ }
+
+ /* Block not split yet - do it now */
+ if (j == cUnit->numBlocks) {
+ BasicBlock *newBB = dvmCompilerNewBB(kDalvikByteCode);
+ newBB->id = blockID++;
+ newBB->firstMIRInsn = insn;
+ newBB->startOffset = insn->offset;
+ newBB->lastMIRInsn = curBB->lastMIRInsn;
+ curBB->lastMIRInsn = insn->prev;
+ insn->prev->next = NULL;
+ insn->prev = NULL;
+
+ /*
+ * If the insn is not an unconditional branch, set up the
+ * fallthrough link.
+ */
+ if (!isUnconditionalBranch(curBB->lastMIRInsn)) {
+ curBB->fallThrough = newBB;
+ }
+
+ /*
+ * Fallthrough block of an invoke instruction needs to be
+ * aligned to 4-byte boundary (alignment instruction to be
+ * inserted later.
+ */
+ if (dexGetInstrFlags(gDvm.instrFlags,
+ curBB->lastMIRInsn->dalvikInsn.opCode) &
+ kInstrInvoke) {
+ newBB->isFallThroughFromInvoke = true;
+ }
+
+ /* enqueue the new block */
+ blockList[cUnit->numBlocks++] = newBB;
+ break;
+ }
+ }
+ }
+ }
+
+ if (numBlocks != cUnit->numBlocks) {
+ LOGE("Expect %d vs %d basic blocks\n", numBlocks, cUnit->numBlocks);
+ dvmCompilerAbort(cUnit);
+ }
+
+ /* Connect the basic blocks through the taken links */
+ for (i = 0; i < numBlocks; i++) {
+ BasicBlock *curBB = blockList[i];
+ MIR *insn = curBB->lastMIRInsn;
+ unsigned int target = insn->offset;
+ bool isInvoke = false;
+ const Method *callee = NULL;
+
+ findBlockBoundary(method, insn, target, &target, &isInvoke, &callee);
+
+ /* Found a block ended on a branch (not invoke) */
+ if (isInvoke == false && target != insn->offset) {
+ int j;
+ /* Forward branch */
+ if (target > insn->offset) {
+ j = i + 1;
+ } else {
+ /* Backward branch */
+ j = 0;
+ }
+ for (; j < numBlocks; j++) {
+ if (blockList[j]->firstMIRInsn->offset == target) {
+ curBB->taken = blockList[j];
+ break;
+ }
+ }
+ }
+
+ if (isInvoke) {
+ BasicBlock *newBB;
+ /* Monomorphic callee */
+ if (callee) {
+ newBB = dvmCompilerNewBB(kChainingCellInvokeSingleton);
+ newBB->startOffset = 0;
+ newBB->containingMethod = callee;
+ /* Will resolve at runtime */
+ } else {
+ newBB = dvmCompilerNewBB(kChainingCellInvokePredicted);
+ newBB->startOffset = 0;
+ }
+ newBB->id = blockID++;
+ curBB->taken = newBB;
+ /* enqueue the new block */
+ blockList[cUnit->numBlocks++] = newBB;
+ }
+ }
+
+ if (cUnit->numBlocks != numBlocks + numInvokeTargets) {
+ LOGE("Expect %d vs %d total blocks\n", numBlocks + numInvokeTargets,
+ cUnit->numBlocks);
+ dvmCompilerDumpCompilationUnit(cUnit);
+ dvmCompilerAbort(cUnit);
+ }
+
+ /* Set the instruction set to use (NOTE: later components may change it) */
+ cUnit->instructionSet = dvmCompilerInstructionSet();
+
+ /* Preparation for SSA conversion */
+ dvmInitializeSSAConversion(cUnit);
+
+ /* SSA analysis */
+ dvmCompilerNonLoopAnalysis(cUnit);
+
+ /* Needs to happen after SSA naming */
+ dvmCompilerInitializeRegAlloc(cUnit);
+
+ /* Allocate Registers */
+ dvmCompilerRegAlloc(cUnit);
+
+ /* Convert MIR to LIR, etc. */
+ dvmCompilerMIR2LIR(cUnit);
+
+ /* Convert LIR into machine code. */
+ dvmCompilerAssembleLIR(cUnit, info);
+
+ if (cUnit->assemblerStatus != kSuccess) {
+ return false;
+ }
+
+ dvmCompilerDumpCompilationUnit(cUnit);
+
+ dvmCompilerArenaReset();
+
+ return info->codeAddress != NULL;
+}
diff --git a/vm/compiler/InlineTransformation.c b/vm/compiler/InlineTransformation.c
new file mode 100644
index 0000000..ce45b8b
--- /dev/null
+++ b/vm/compiler/InlineTransformation.c
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "Dataflow.h"
+#include "libdex/OpCodeNames.h"
+
+/* Convert the reg id from the callee to the original id passed by the caller */
+static inline u4 convertRegId(const DecodedInstruction *invoke,
+ const Method *calleeMethod,
+ int calleeRegId, bool isRange)
+{
+ /* The order in the original arg passing list */
+ int rank = calleeRegId -
+ (calleeMethod->registersSize - calleeMethod->insSize);
+ assert(rank >= 0);
+ if (!isRange) {
+ return invoke->arg[rank];
+ } else {
+ return invoke->vC + rank;
+ }
+}
+
+static void inlineGetter(CompilationUnit *cUnit,
+ const Method *calleeMethod,
+ MIR *invokeMIR,
+ BasicBlock *invokeBB,
+ bool isPredicted,
+ bool isRange)
+{
+ BasicBlock *moveResultBB = invokeBB->fallThrough;
+ MIR *moveResultMIR = moveResultBB->firstMIRInsn;
+ MIR *newGetterMIR = dvmCompilerNew(sizeof(MIR), true);
+ DecodedInstruction getterInsn;
+
+ dexDecodeInstruction(gDvm.instrFormat, calleeMethod->insns, &getterInsn);
+
+ if (!dvmCompilerCanIncludeThisInstruction(calleeMethod, &getterInsn))
+ return;
+
+ /*
+ * Some getters (especially invoked through interface) are not followed
+ * by a move result.
+ */
+ if ((moveResultMIR == NULL) ||
+ (moveResultMIR->dalvikInsn.opCode != OP_MOVE_RESULT &&
+ moveResultMIR->dalvikInsn.opCode != OP_MOVE_RESULT_OBJECT &&
+ moveResultMIR->dalvikInsn.opCode != OP_MOVE_RESULT_WIDE)) {
+ return;
+ }
+
+ int dfFlags = dvmCompilerDataFlowAttributes[getterInsn.opCode];
+
+ /* Expecting vA to be the destination register */
+ if (dfFlags & (DF_UA | DF_UA_WIDE)) {
+ LOGE("opcode %d has DF_UA set (not expected)", getterInsn.opCode);
+ dvmAbort();
+ }
+
+ if (dfFlags & DF_UB) {
+ getterInsn.vB = convertRegId(&invokeMIR->dalvikInsn, calleeMethod,
+ getterInsn.vB, isRange);
+ }
+
+ if (dfFlags & DF_UC) {
+ getterInsn.vC = convertRegId(&invokeMIR->dalvikInsn, calleeMethod,
+ getterInsn.vC, isRange);
+ }
+
+ getterInsn.vA = moveResultMIR->dalvikInsn.vA;
+
+ /* Now setup the Dalvik instruction with converted src/dst registers */
+ newGetterMIR->dalvikInsn = getterInsn;
+
+ newGetterMIR->width = gDvm.instrWidth[getterInsn.opCode];
+
+ newGetterMIR->OptimizationFlags |= MIR_CALLEE;
+
+ /*
+ * If the getter instruction is about to raise any exception, punt to the
+ * interpreter and re-execute the invoke.
+ */
+ newGetterMIR->offset = invokeMIR->offset;
+
+ newGetterMIR->meta.calleeMethod = calleeMethod;
+
+ dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, newGetterMIR);
+
+ if (isPredicted) {
+ MIR *invokeMIRSlow = dvmCompilerNew(sizeof(MIR), true);
+ *invokeMIRSlow = *invokeMIR;
+ invokeMIR->dalvikInsn.opCode = kMirOpCheckInlinePrediction;
+
+ /* Use vC to denote the first argument (ie this) */
+ if (!isRange) {
+ invokeMIR->dalvikInsn.vC = invokeMIRSlow->dalvikInsn.arg[0];
+ }
+
+ moveResultMIR->OptimizationFlags |= MIR_INLINED_PRED;
+
+ dvmCompilerInsertMIRAfter(invokeBB, newGetterMIR, invokeMIRSlow);
+ invokeMIRSlow->OptimizationFlags |= MIR_INLINED_PRED;
+#if defined(WITH_JIT_TUNING)
+ gDvmJit.invokePolyGetterInlined++;
+#endif
+ } else {
+ invokeMIR->OptimizationFlags |= MIR_INLINED;
+ moveResultMIR->OptimizationFlags |= MIR_INLINED;
+#if defined(WITH_JIT_TUNING)
+ gDvmJit.invokeMonoGetterInlined++;
+#endif
+ }
+
+ return;
+}
+
+static void inlineSetter(CompilationUnit *cUnit,
+ const Method *calleeMethod,
+ MIR *invokeMIR,
+ BasicBlock *invokeBB,
+ bool isPredicted,
+ bool isRange)
+{
+ MIR *newSetterMIR = dvmCompilerNew(sizeof(MIR), true);
+ DecodedInstruction setterInsn;
+
+ dexDecodeInstruction(gDvm.instrFormat, calleeMethod->insns, &setterInsn);
+
+ if (!dvmCompilerCanIncludeThisInstruction(calleeMethod, &setterInsn))
+ return;
+
+ int dfFlags = dvmCompilerDataFlowAttributes[setterInsn.opCode];
+
+ if (dfFlags & (DF_UA | DF_UA_WIDE)) {
+ setterInsn.vA = convertRegId(&invokeMIR->dalvikInsn, calleeMethod,
+ setterInsn.vA, isRange);
+
+ }
+
+ if (dfFlags & DF_UB) {
+ setterInsn.vB = convertRegId(&invokeMIR->dalvikInsn, calleeMethod,
+ setterInsn.vB, isRange);
+
+ }
+
+ if (dfFlags & DF_UC) {
+ setterInsn.vC = convertRegId(&invokeMIR->dalvikInsn, calleeMethod,
+ setterInsn.vC, isRange);
+ }
+
+ /* Now setup the Dalvik instruction with converted src/dst registers */
+ newSetterMIR->dalvikInsn = setterInsn;
+
+ newSetterMIR->width = gDvm.instrWidth[setterInsn.opCode];
+
+ newSetterMIR->OptimizationFlags |= MIR_CALLEE;
+
+ /*
+ * If the setter instruction is about to raise any exception, punt to the
+ * interpreter and re-execute the invoke.
+ */
+ newSetterMIR->offset = invokeMIR->offset;
+
+ newSetterMIR->meta.calleeMethod = calleeMethod;
+
+ dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, newSetterMIR);
+
+ if (isPredicted) {
+ MIR *invokeMIRSlow = dvmCompilerNew(sizeof(MIR), true);
+ *invokeMIRSlow = *invokeMIR;
+ invokeMIR->dalvikInsn.opCode = kMirOpCheckInlinePrediction;
+
+ /* Use vC to denote the first argument (ie this) */
+ if (!isRange) {
+ invokeMIR->dalvikInsn.vC = invokeMIRSlow->dalvikInsn.arg[0];
+ }
+
+ dvmCompilerInsertMIRAfter(invokeBB, newSetterMIR, invokeMIRSlow);
+ invokeMIRSlow->OptimizationFlags |= MIR_INLINED_PRED;
+#if defined(WITH_JIT_TUNING)
+ gDvmJit.invokePolySetterInlined++;
+#endif
+ } else {
+ /*
+ * The invoke becomes no-op so it needs an explicit branch to jump to
+ * the chaining cell.
+ */
+ invokeBB->needFallThroughBranch = true;
+ invokeMIR->OptimizationFlags |= MIR_INLINED;
+#if defined(WITH_JIT_TUNING)
+ gDvmJit.invokeMonoSetterInlined++;
+#endif
+ }
+
+ return;
+}
+
+static void tryInlineSingletonCallsite(CompilationUnit *cUnit,
+ const Method *calleeMethod,
+ MIR *invokeMIR,
+ BasicBlock *invokeBB,
+ bool isRange)
+{
+ /* Not a Java method */
+ if (dvmIsNativeMethod(calleeMethod)) return;
+
+ CompilerMethodStats *methodStats =
+ dvmCompilerAnalyzeMethodBody(calleeMethod, true);
+
+ /* Empty callee - do nothing */
+ if (methodStats->attributes & METHOD_IS_EMPTY) {
+ /* The original invoke instruction is effectively turned into NOP */
+ invokeMIR->OptimizationFlags |= MIR_INLINED;
+ /*
+ * Need to insert an explicit branch to catch the falling knife (into
+ * the PC reconstruction or chaining cell).
+ */
+ invokeBB->needFallThroughBranch = true;
+ return;
+ }
+
+ if (methodStats->attributes & METHOD_IS_GETTER) {
+ inlineGetter(cUnit, calleeMethod, invokeMIR, invokeBB, false, isRange);
+ return;
+ } else if (methodStats->attributes & METHOD_IS_SETTER) {
+ inlineSetter(cUnit, calleeMethod, invokeMIR, invokeBB, false, isRange);
+ return;
+ }
+}
+
+static void inlineEmptyVirtualCallee(CompilationUnit *cUnit,
+ const Method *calleeMethod,
+ MIR *invokeMIR,
+ BasicBlock *invokeBB)
+{
+ MIR *invokeMIRSlow = dvmCompilerNew(sizeof(MIR), true);
+ *invokeMIRSlow = *invokeMIR;
+ invokeMIR->dalvikInsn.opCode = kMirOpCheckInlinePrediction;
+
+ dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, invokeMIRSlow);
+ invokeMIRSlow->OptimizationFlags |= MIR_INLINED_PRED;
+}
+
+static void tryInlineVirtualCallsite(CompilationUnit *cUnit,
+ const Method *calleeMethod,
+ MIR *invokeMIR,
+ BasicBlock *invokeBB,
+ bool isRange)
+{
+ /* Not a Java method */
+ if (dvmIsNativeMethod(calleeMethod)) return;
+
+ CompilerMethodStats *methodStats =
+ dvmCompilerAnalyzeMethodBody(calleeMethod, true);
+
+ /* Empty callee - do nothing by checking the clazz pointer */
+ if (methodStats->attributes & METHOD_IS_EMPTY) {
+ inlineEmptyVirtualCallee(cUnit, calleeMethod, invokeMIR, invokeBB);
+ return;
+ }
+
+ if (methodStats->attributes & METHOD_IS_GETTER) {
+ inlineGetter(cUnit, calleeMethod, invokeMIR, invokeBB, true, isRange);
+ return;
+ } else if (methodStats->attributes & METHOD_IS_SETTER) {
+ inlineSetter(cUnit, calleeMethod, invokeMIR, invokeBB, true, isRange);
+ return;
+ }
+}
+
+
+void dvmCompilerInlineMIR(CompilationUnit *cUnit)
+{
+ int i;
+ bool isRange = false;
+
+ /*
+ * Analyze the basic block containing an invoke to see if it can be inlined
+ */
+ for (i = 0; i < cUnit->numBlocks; i++) {
+ BasicBlock *bb = cUnit->blockList[i];
+ if (bb->blockType != kDalvikByteCode)
+ continue;
+ MIR *lastMIRInsn = bb->lastMIRInsn;
+ int opCode = lastMIRInsn->dalvikInsn.opCode;
+ int flags = dexGetInstrFlags(gDvm.instrFlags, opCode);
+
+ /* No invoke - continue */
+ if ((flags & kInstrInvoke) == 0)
+ continue;
+
+ /* Not a real invoke - continue */
+ if (opCode == OP_INVOKE_DIRECT_EMPTY)
+ continue;
+
+ /*
+ * If the invoke itself is selected for single stepping, don't bother
+ * to inline it.
+ */
+ if (SINGLE_STEP_OP(opCode))
+ continue;
+
+ const Method *calleeMethod;
+
+ switch (opCode) {
+ case OP_INVOKE_SUPER:
+ case OP_INVOKE_DIRECT:
+ case OP_INVOKE_STATIC:
+ case OP_INVOKE_SUPER_QUICK:
+ calleeMethod = lastMIRInsn->meta.callsiteInfo->method;
+ break;
+ case OP_INVOKE_SUPER_RANGE:
+ case OP_INVOKE_DIRECT_RANGE:
+ case OP_INVOKE_STATIC_RANGE:
+ case OP_INVOKE_SUPER_QUICK_RANGE:
+ isRange = true;
+ calleeMethod = lastMIRInsn->meta.callsiteInfo->method;
+ break;
+ default:
+ calleeMethod = NULL;
+ break;
+ }
+
+ if (calleeMethod) {
+ tryInlineSingletonCallsite(cUnit, calleeMethod, lastMIRInsn, bb,
+ isRange);
+ return;
+ }
+
+ switch (opCode) {
+ case OP_INVOKE_VIRTUAL:
+ case OP_INVOKE_VIRTUAL_QUICK:
+ case OP_INVOKE_INTERFACE:
+ isRange = false;
+ calleeMethod = lastMIRInsn->meta.callsiteInfo->method;
+ break;
+ case OP_INVOKE_VIRTUAL_RANGE:
+ case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+ case OP_INVOKE_INTERFACE_RANGE:
+ isRange = true;
+ calleeMethod = lastMIRInsn->meta.callsiteInfo->method;
+ break;
+ default:
+ break;
+ }
+
+ if (calleeMethod) {
+ tryInlineVirtualCallsite(cUnit, calleeMethod, lastMIRInsn, bb,
+ isRange);
+ return;
+ }
+ }
+}
diff --git a/vm/compiler/IntermediateRep.c b/vm/compiler/IntermediateRep.c
new file mode 100644
index 0000000..825a690
--- /dev/null
+++ b/vm/compiler/IntermediateRep.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "CompilerInternals.h"
+
+/* Allocate a new basic block */
+BasicBlock *dvmCompilerNewBB(BBType blockType)
+{
+ BasicBlock *bb = dvmCompilerNew(sizeof(BasicBlock), true);
+ bb->blockType = blockType;
+ return bb;
+}
+
+/* Insert an MIR instruction to the end of a basic block */
+void dvmCompilerAppendMIR(BasicBlock *bb, MIR *mir)
+{
+ if (bb->firstMIRInsn == NULL) {
+ assert(bb->lastMIRInsn == NULL);
+ bb->lastMIRInsn = bb->firstMIRInsn = mir;
+ mir->prev = mir->next = NULL;
+ } else {
+ bb->lastMIRInsn->next = mir;
+ mir->prev = bb->lastMIRInsn;
+ mir->next = NULL;
+ bb->lastMIRInsn = mir;
+ }
+}
+
+/* Insert an MIR instruction to the head of a basic block */
+void dvmCompilerPrependMIR(BasicBlock *bb, MIR *mir)
+{
+ if (bb->firstMIRInsn == NULL) {
+ assert(bb->lastMIRInsn == NULL);
+ bb->lastMIRInsn = bb->firstMIRInsn = mir;
+ mir->prev = mir->next = NULL;
+ } else {
+ bb->firstMIRInsn->prev = mir;
+ mir->next = bb->firstMIRInsn;
+ mir->prev = NULL;
+ bb->firstMIRInsn = mir;
+ }
+}
+
+/* Insert an MIR instruction after the specified MIR */
+void dvmCompilerInsertMIRAfter(BasicBlock *bb, MIR *currentMIR, MIR *newMIR)
+{
+ newMIR->prev = currentMIR;
+ newMIR->next = currentMIR->next;
+ currentMIR->next = newMIR;
+
+ if (newMIR->next) {
+ /* Is not the last MIR in the block */
+ newMIR->next->prev = newMIR;
+ } else {
+ /* Is the last MIR in the block */
+ bb->lastMIRInsn = newMIR;
+ }
+}
+
+/*
+ * Append an LIR instruction to the LIR list maintained by a compilation
+ * unit
+ */
+void dvmCompilerAppendLIR(CompilationUnit *cUnit, LIR *lir)
+{
+ if (cUnit->firstLIRInsn == NULL) {
+ assert(cUnit->lastLIRInsn == NULL);
+ cUnit->lastLIRInsn = cUnit->firstLIRInsn = lir;
+ lir->prev = lir->next = NULL;
+ } else {
+ cUnit->lastLIRInsn->next = lir;
+ lir->prev = cUnit->lastLIRInsn;
+ lir->next = NULL;
+ cUnit->lastLIRInsn = lir;
+ }
+}
+
+/*
+ * Insert an LIR instruction before the current instruction, which cannot be the
+ * first instruction.
+ *
+ * prevLIR <-> newLIR <-> currentLIR
+ */
+void dvmCompilerInsertLIRBefore(LIR *currentLIR, LIR *newLIR)
+{
+ assert(currentLIR->prev != NULL);
+ LIR *prevLIR = currentLIR->prev;
+
+ prevLIR->next = newLIR;
+ newLIR->prev = prevLIR;
+ newLIR->next = currentLIR;
+ currentLIR->prev = newLIR;
+}
+
+/*
+ * Insert an LIR instruction after the current instruction, which cannot be the
+ * first instruction.
+ *
+ * currentLIR -> newLIR -> oldNext
+ */
+void dvmCompilerInsertLIRAfter(LIR *currentLIR, LIR *newLIR)
+{
+ newLIR->prev = currentLIR;
+ newLIR->next = currentLIR->next;
+ currentLIR->next = newLIR;
+ newLIR->next->prev = newLIR;
+}
diff --git a/vm/compiler/Loop.c b/vm/compiler/Loop.c
new file mode 100644
index 0000000..918d955
--- /dev/null
+++ b/vm/compiler/Loop.c
@@ -0,0 +1,526 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "CompilerInternals.h"
+#include "Dataflow.h"
+#include "Loop.h"
+
+#define DEBUG_LOOP(X)
+
+/*
+ * Given the current simple natural loops, the phi node placement can be
+ * determined in the following fashion:
+ * entry (B0)
+ * +---v v
+ * | loop body (B1)
+ * | v
+ * | loop back (B2)
+ * +---+ v
+ * exit (B3)
+ *
+ * 1) Add live-ins of B1 to B0 as defs
+ * 2) The intersect of defs(B0)/defs(B1) and defs(B2)/def(B0) are the variables
+ * that need PHI nodes in B1.
+ */
+static void handlePhiPlacement(CompilationUnit *cUnit)
+{
+ BasicBlock *entry = cUnit->blockList[0];
+ BasicBlock *loopBody = cUnit->blockList[1];
+ BasicBlock *loopBranch = cUnit->blockList[2];
+ dvmCopyBitVector(entry->dataFlowInfo->defV,
+ loopBody->dataFlowInfo->liveInV);
+
+ BitVector *phiV = dvmCompilerAllocBitVector(cUnit->method->registersSize,
+ false);
+ dvmIntersectBitVectors(phiV, entry->dataFlowInfo->defV,
+ loopBody->dataFlowInfo->defV);
+ dvmIntersectBitVectors(phiV, entry->dataFlowInfo->defV,
+ loopBranch->dataFlowInfo->defV);
+
+ /* Insert the PHI MIRs */
+ int i;
+ for (i = 0; i < cUnit->method->registersSize; i++) {
+ if (!dvmIsBitSet(phiV, i)) {
+ continue;
+ }
+ MIR *phi = dvmCompilerNew(sizeof(MIR), true);
+ phi->dalvikInsn.opCode = kMirOpPhi;
+ phi->dalvikInsn.vA = i;
+ dvmCompilerPrependMIR(loopBody, phi);
+ }
+}
+
+static void fillPhiNodeContents(CompilationUnit *cUnit)
+{
+ BasicBlock *entry = cUnit->blockList[0];
+ BasicBlock *loopBody = cUnit->blockList[1];
+ BasicBlock *loopBranch = cUnit->blockList[2];
+ MIR *mir;
+
+ for (mir = loopBody->firstMIRInsn; mir; mir = mir->next) {
+ if (mir->dalvikInsn.opCode != kMirOpPhi) break;
+ int dalvikReg = mir->dalvikInsn.vA;
+
+ mir->ssaRep->numUses = 2;
+ mir->ssaRep->uses = dvmCompilerNew(sizeof(int) * 2, false);
+ mir->ssaRep->uses[0] =
+ DECODE_REG(entry->dataFlowInfo->dalvikToSSAMap[dalvikReg]);
+ mir->ssaRep->uses[1] =
+ DECODE_REG(loopBranch->dataFlowInfo->dalvikToSSAMap[dalvikReg]);
+ }
+
+
+}
+
+#if 0
+/* Debugging routines */
+static void dumpConstants(CompilationUnit *cUnit)
+{
+ int i;
+ for (i = 0; i < cUnit->numSSARegs; i++) {
+ if (dvmIsBitSet(cUnit->isConstantV, i)) {
+ int subNReg = dvmConvertSSARegToDalvik(cUnit, i);
+ LOGE("s%d(v%d_%d) has %d", i,
+ DECODE_REG(subNReg), DECODE_SUB(subNReg),
+ cUnit->constantValues[i]);
+ }
+ }
+}
+
+static void dumpIVList(CompilationUnit *cUnit)
+{
+ unsigned int i;
+ GrowableList *ivList = cUnit->loopAnalysis->ivList;
+ int *ssaToDalvikMap = (int *) cUnit->ssaToDalvikMap->elemList;
+
+ for (i = 0; i < ivList->numUsed; i++) {
+ InductionVariableInfo *ivInfo = ivList->elemList[i];
+ /* Basic IV */
+ if (ivInfo->ssaReg == ivInfo->basicSSAReg) {
+ LOGE("BIV %d: s%d(v%d) + %d", i,
+ ivInfo->ssaReg,
+ ssaToDalvikMap[ivInfo->ssaReg] & 0xffff,
+ ivInfo->inc);
+ /* Dependent IV */
+ } else {
+ LOGE("DIV %d: s%d(v%d) = %d * s%d(v%d) + %d", i,
+ ivInfo->ssaReg,
+ ssaToDalvikMap[ivInfo->ssaReg] & 0xffff,
+ ivInfo->m,
+ ivInfo->basicSSAReg,
+ ssaToDalvikMap[ivInfo->basicSSAReg] & 0xffff,
+ ivInfo->c);
+ }
+ }
+}
+
+static void dumpHoistedChecks(CompilationUnit *cUnit)
+{
+ LoopAnalysis *loopAnalysis = cUnit->loopAnalysis;
+ unsigned int i;
+
+ for (i = 0; i < loopAnalysis->arrayAccessInfo->numUsed; i++) {
+ ArrayAccessInfo *arrayAccessInfo =
+ GET_ELEM_N(loopAnalysis->arrayAccessInfo,
+ ArrayAccessInfo*, i);
+ int arrayReg = DECODE_REG(
+ dvmConvertSSARegToDalvik(cUnit, arrayAccessInfo->arrayReg));
+ int idxReg = DECODE_REG(
+ dvmConvertSSARegToDalvik(cUnit, arrayAccessInfo->ivReg));
+ LOGE("Array access %d", i);
+ LOGE(" arrayReg %d", arrayReg);
+ LOGE(" idxReg %d", idxReg);
+ LOGE(" endReg %d", loopAnalysis->endConditionReg);
+ LOGE(" maxC %d", arrayAccessInfo->maxC);
+ LOGE(" minC %d", arrayAccessInfo->minC);
+ LOGE(" opcode %d", loopAnalysis->loopBranchOpcode);
+ }
+}
+
+#endif
+
+/*
+ * A loop is considered optimizable if:
+ * 1) It has one basic induction variable
+ * 2) The loop back branch compares the BIV with a constant
+ * 3) If it is a count-up loop, the condition is GE/GT, or LE/LT/LEZ/LTZ for
+ * a count-down loop.
+ *
+ * Return false if the loop is not optimizable.
+ */
+static bool isLoopOptimizable(CompilationUnit *cUnit)
+{
+ unsigned int i;
+ BasicBlock *loopBranch = cUnit->blockList[2];
+ LoopAnalysis *loopAnalysis = cUnit->loopAnalysis;
+
+ if (loopAnalysis->numBasicIV != 1) return false;
+ for (i = 0; i < loopAnalysis->ivList->numUsed; i++) {
+ InductionVariableInfo *ivInfo;
+
+ ivInfo = GET_ELEM_N(loopAnalysis->ivList, InductionVariableInfo*, i);
+ /* Count up or down loop? */
+ if (ivInfo->ssaReg == ivInfo->basicSSAReg) {
+ /* Infinite loop */
+ if (ivInfo->inc == 0) {
+ return false;
+ }
+ loopAnalysis->isCountUpLoop = ivInfo->inc > 0;
+ break;
+ }
+ }
+
+ MIR *branch = loopBranch->lastMIRInsn;
+ OpCode opCode = branch->dalvikInsn.opCode;
+
+ /*
+ * If the instruction is not accessing the IV as the first operand, return
+ * false.
+ */
+ if (branch->ssaRep->numUses == 0 || branch->ssaRep->numDefs != 0) {
+ return false;
+ }
+
+ /*
+ * If the first operand of the comparison is not the basic induction
+ * variable, return false.
+ */
+ if (branch->ssaRep->uses[0] != loopAnalysis->ssaBIV) {
+ return false;
+ }
+
+ if (loopAnalysis->isCountUpLoop) {
+ /*
+ * If the condition op is not > or >=, this is not an optimization
+ * candidate.
+ */
+ if (opCode != OP_IF_GT && opCode != OP_IF_GE) {
+ return false;
+ }
+ /*
+ * If the comparison is not between the BIV and a loop invariant,
+ * return false. endReg is loop invariant if one of the following is
+ * true:
+ * - It is not defined in the loop (ie DECODE_SUB returns 0)
+ * - It is reloaded with a constant
+ */
+ int endReg = dvmConvertSSARegToDalvik(cUnit, branch->ssaRep->uses[1]);
+ if (DECODE_SUB(endReg) != 0 &&
+ !dvmIsBitSet(cUnit->isConstantV, branch->ssaRep->uses[1])) {
+ return false;
+ }
+ loopAnalysis->endConditionReg = DECODE_REG(endReg);
+ } else {
+ /*
+ * If the condition op is not < or <=, this is not an optimization
+ * candidate.
+ */
+ if (opCode == OP_IF_LT || opCode == OP_IF_LE) {
+ /*
+ * If the comparison is not between the BIV and a loop invariant,
+ * return false.
+ */
+ int endReg = dvmConvertSSARegToDalvik(cUnit,
+ branch->ssaRep->uses[1]);
+
+ if (DECODE_SUB(endReg) != 0) {
+ return false;
+ }
+ loopAnalysis->endConditionReg = DECODE_REG(endReg);
+ } else if (opCode != OP_IF_LTZ && opCode != OP_IF_LEZ) {
+ return false;
+ }
+ }
+ loopAnalysis->loopBranchOpcode = opCode;
+ return true;
+}
+
+/*
+ * Record the upper and lower bound information for range checks for each
+ * induction variable. If array A is accessed by index "i+5", the upper and
+ * lower bound will be len(A)-5 and -5, respectively.
+ */
+static void updateRangeCheckInfo(CompilationUnit *cUnit, int arrayReg,
+ int idxReg)
+{
+ InductionVariableInfo *ivInfo;
+ LoopAnalysis *loopAnalysis = cUnit->loopAnalysis;
+ unsigned int i, j;
+
+ for (i = 0; i < loopAnalysis->ivList->numUsed; i++) {
+ ivInfo = GET_ELEM_N(loopAnalysis->ivList, InductionVariableInfo*, i);
+ if (ivInfo->ssaReg == idxReg) {
+ ArrayAccessInfo *arrayAccessInfo = NULL;
+ for (j = 0; j < loopAnalysis->arrayAccessInfo->numUsed; j++) {
+ ArrayAccessInfo *existingArrayAccessInfo =
+ GET_ELEM_N(loopAnalysis->arrayAccessInfo,
+ ArrayAccessInfo*,
+ j);
+ if (existingArrayAccessInfo->arrayReg == arrayReg) {
+ if (ivInfo->c > existingArrayAccessInfo->maxC) {
+ existingArrayAccessInfo->maxC = ivInfo->c;
+ }
+ if (ivInfo->c < existingArrayAccessInfo->minC) {
+ existingArrayAccessInfo->minC = ivInfo->c;
+ }
+ arrayAccessInfo = existingArrayAccessInfo;
+ break;
+ }
+ }
+ if (arrayAccessInfo == NULL) {
+ arrayAccessInfo =
+ dvmCompilerNew(sizeof(ArrayAccessInfo), false);
+ arrayAccessInfo->ivReg = ivInfo->basicSSAReg;
+ arrayAccessInfo->arrayReg = arrayReg;
+ arrayAccessInfo->maxC = (ivInfo->c > 0) ? ivInfo->c : 0;
+ arrayAccessInfo->minC = (ivInfo->c < 0) ? ivInfo->c : 0;
+ dvmInsertGrowableList(loopAnalysis->arrayAccessInfo,
+ arrayAccessInfo);
+ }
+ break;
+ }
+ }
+}
+
+/* Returns true if the loop body cannot throw any exceptions */
+static bool doLoopBodyCodeMotion(CompilationUnit *cUnit)
+{
+ BasicBlock *loopBody = cUnit->blockList[1];
+ MIR *mir;
+ bool loopBodyCanThrow = false;
+
+ for (mir = loopBody->firstMIRInsn; mir; mir = mir->next) {
+ DecodedInstruction *dInsn = &mir->dalvikInsn;
+ int dfAttributes =
+ dvmCompilerDataFlowAttributes[mir->dalvikInsn.opCode];
+
+ /* Skip extended MIR instructions */
+ if (dInsn->opCode > 255) continue;
+
+ int instrFlags = dexGetInstrFlags(gDvm.instrFlags, dInsn->opCode);
+
+ /* Instruction is clean */
+ if ((instrFlags & kInstrCanThrow) == 0) continue;
+
+ /*
+ * Currently we can only optimize away null and range checks. Punt on
+ * instructions that can throw due to other exceptions.
+ */
+ if (!(dfAttributes & DF_HAS_NR_CHECKS)) {
+ loopBodyCanThrow = true;
+ continue;
+ }
+
+ /*
+ * This comparison is redundant now, but we will have more than one
+ * group of flags to check soon.
+ */
+ if (dfAttributes & DF_HAS_NR_CHECKS) {
+ /*
+ * Check if the null check is applied on a loop invariant register?
+ * If the register's SSA id is less than the number of Dalvik
+ * registers, then it is loop invariant.
+ */
+ int refIdx;
+ switch (dfAttributes & DF_HAS_NR_CHECKS) {
+ case DF_NULL_N_RANGE_CHECK_0:
+ refIdx = 0;
+ break;
+ case DF_NULL_N_RANGE_CHECK_1:
+ refIdx = 1;
+ break;
+ case DF_NULL_N_RANGE_CHECK_2:
+ refIdx = 2;
+ break;
+ default:
+ refIdx = 0;
+ LOGE("Jit: bad case in doLoopBodyCodeMotion");
+ dvmCompilerAbort(cUnit);
+ }
+
+ int useIdx = refIdx + 1;
+ int subNRegArray =
+ dvmConvertSSARegToDalvik(cUnit, mir->ssaRep->uses[refIdx]);
+ int arraySub = DECODE_SUB(subNRegArray);
+
+ /*
+ * If the register is never updated in the loop (ie subscript == 0),
+ * it is an optimization candidate.
+ */
+ if (arraySub != 0) {
+ loopBodyCanThrow = true;
+ continue;
+ }
+
+ /*
+ * Then check if the range check can be hoisted out of the loop if
+ * it is basic or dependent induction variable.
+ */
+ if (dvmIsBitSet(cUnit->loopAnalysis->isIndVarV,
+ mir->ssaRep->uses[useIdx])) {
+ mir->OptimizationFlags |=
+ MIR_IGNORE_RANGE_CHECK | MIR_IGNORE_NULL_CHECK;
+ updateRangeCheckInfo(cUnit, mir->ssaRep->uses[refIdx],
+ mir->ssaRep->uses[useIdx]);
+ }
+ }
+ }
+
+ return !loopBodyCanThrow;
+}
+
+static void genHoistedChecks(CompilationUnit *cUnit)
+{
+ unsigned int i;
+ BasicBlock *entry = cUnit->blockList[0];
+ LoopAnalysis *loopAnalysis = cUnit->loopAnalysis;
+ int globalMaxC = 0;
+ int globalMinC = 0;
+ /* Should be loop invariant */
+ int idxReg = 0;
+
+ for (i = 0; i < loopAnalysis->arrayAccessInfo->numUsed; i++) {
+ ArrayAccessInfo *arrayAccessInfo =
+ GET_ELEM_N(loopAnalysis->arrayAccessInfo,
+ ArrayAccessInfo*, i);
+ int arrayReg = DECODE_REG(
+ dvmConvertSSARegToDalvik(cUnit, arrayAccessInfo->arrayReg));
+ idxReg = DECODE_REG(
+ dvmConvertSSARegToDalvik(cUnit, arrayAccessInfo->ivReg));
+
+ MIR *rangeCheckMIR = dvmCompilerNew(sizeof(MIR), true);
+ rangeCheckMIR->dalvikInsn.opCode = (loopAnalysis->isCountUpLoop) ?
+ kMirOpNullNRangeUpCheck : kMirOpNullNRangeDownCheck;
+ rangeCheckMIR->dalvikInsn.vA = arrayReg;
+ rangeCheckMIR->dalvikInsn.vB = idxReg;
+ rangeCheckMIR->dalvikInsn.vC = loopAnalysis->endConditionReg;
+ rangeCheckMIR->dalvikInsn.arg[0] = arrayAccessInfo->maxC;
+ rangeCheckMIR->dalvikInsn.arg[1] = arrayAccessInfo->minC;
+ rangeCheckMIR->dalvikInsn.arg[2] = loopAnalysis->loopBranchOpcode;
+ dvmCompilerAppendMIR(entry, rangeCheckMIR);
+ if (arrayAccessInfo->maxC > globalMaxC) {
+ globalMaxC = arrayAccessInfo->maxC;
+ }
+ if (arrayAccessInfo->minC < globalMinC) {
+ globalMinC = arrayAccessInfo->minC;
+ }
+ }
+
+ if (loopAnalysis->arrayAccessInfo->numUsed != 0) {
+ if (loopAnalysis->isCountUpLoop) {
+ MIR *boundCheckMIR = dvmCompilerNew(sizeof(MIR), true);
+ boundCheckMIR->dalvikInsn.opCode = kMirOpLowerBound;
+ boundCheckMIR->dalvikInsn.vA = idxReg;
+ boundCheckMIR->dalvikInsn.vB = globalMinC;
+ dvmCompilerAppendMIR(entry, boundCheckMIR);
+ } else {
+ if (loopAnalysis->loopBranchOpcode == OP_IF_LT ||
+ loopAnalysis->loopBranchOpcode == OP_IF_LE) {
+ MIR *boundCheckMIR = dvmCompilerNew(sizeof(MIR), true);
+ boundCheckMIR->dalvikInsn.opCode = kMirOpLowerBound;
+ boundCheckMIR->dalvikInsn.vA = loopAnalysis->endConditionReg;
+ boundCheckMIR->dalvikInsn.vB = globalMinC;
+ /*
+ * If the end condition is ">" in the source, the check in the
+ * Dalvik bytecode is OP_IF_LE. In this case add 1 back to the
+ * constant field to reflect the fact that the smallest index
+ * value is "endValue + constant + 1".
+ */
+ if (loopAnalysis->loopBranchOpcode == OP_IF_LE) {
+ boundCheckMIR->dalvikInsn.vB++;
+ }
+ dvmCompilerAppendMIR(entry, boundCheckMIR);
+ } else if (loopAnalysis->loopBranchOpcode == OP_IF_LTZ) {
+ /* Array index will fall below 0 */
+ if (globalMinC < 0) {
+ MIR *boundCheckMIR = dvmCompilerNew(sizeof(MIR), true);
+ boundCheckMIR->dalvikInsn.opCode = kMirOpPunt;
+ dvmCompilerAppendMIR(entry, boundCheckMIR);
+ }
+ } else if (loopAnalysis->loopBranchOpcode == OP_IF_LEZ) {
+ /* Array index will fall below 0 */
+ if (globalMinC < -1) {
+ MIR *boundCheckMIR = dvmCompilerNew(sizeof(MIR), true);
+ boundCheckMIR->dalvikInsn.opCode = kMirOpPunt;
+ dvmCompilerAppendMIR(entry, boundCheckMIR);
+ }
+ } else {
+ LOGE("Jit: bad case in genHoistedChecks");
+ dvmCompilerAbort(cUnit);
+ }
+ }
+
+ }
+}
+
+/*
+ * Main entry point to do loop optimization.
+ * Return false if sanity checks for loop formation/optimization failed.
+ */
+bool dvmCompilerLoopOpt(CompilationUnit *cUnit)
+{
+ LoopAnalysis *loopAnalysis = dvmCompilerNew(sizeof(LoopAnalysis), true);
+
+ assert(cUnit->blockList[0]->blockType == kTraceEntryBlock);
+ assert(cUnit->blockList[2]->blockType == kDalvikByteCode);
+ assert(cUnit->blockList[3]->blockType == kTraceExitBlock);
+
+ cUnit->loopAnalysis = loopAnalysis;
+ /*
+ * Find live-in variables to the loop body so that we can fake their
+ * definitions in the entry block.
+ */
+ dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerFindLiveIn);
+
+ /* Insert phi nodes to the loop body */
+ handlePhiPlacement(cUnit);
+
+ dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerDoSSAConversion);
+ fillPhiNodeContents(cUnit);
+
+ /* Constant propagation */
+ cUnit->isConstantV = dvmAllocBitVector(cUnit->numSSARegs, false);
+ cUnit->constantValues = dvmCompilerNew(sizeof(int) * cUnit->numSSARegs,
+ true);
+ dvmCompilerDataFlowAnalysisDispatcher(cUnit,
+ dvmCompilerDoConstantPropagation);
+ DEBUG_LOOP(dumpConstants(cUnit);)
+
+ /* Find induction variables - basic and dependent */
+ loopAnalysis->ivList = dvmCompilerNew(sizeof(GrowableList), true);
+ dvmInitGrowableList(loopAnalysis->ivList, 4);
+ loopAnalysis->isIndVarV = dvmAllocBitVector(cUnit->numSSARegs, false);
+ dvmCompilerDataFlowAnalysisDispatcher(cUnit,
+ dvmCompilerFindInductionVariables);
+ DEBUG_LOOP(dumpIVList(cUnit);)
+
+ /* If the loop turns out to be non-optimizable, return early */
+ if (!isLoopOptimizable(cUnit))
+ return false;
+
+ loopAnalysis->arrayAccessInfo = dvmCompilerNew(sizeof(GrowableList), true);
+ dvmInitGrowableList(loopAnalysis->arrayAccessInfo, 4);
+ loopAnalysis->bodyIsClean = doLoopBodyCodeMotion(cUnit);
+ DEBUG_LOOP(dumpHoistedChecks(cUnit);)
+
+ /*
+ * Convert the array access information into extended MIR code in the loop
+ * header.
+ */
+ genHoistedChecks(cUnit);
+ return true;
+}
diff --git a/vm/compiler/Loop.h b/vm/compiler/Loop.h
new file mode 100644
index 0000000..0de2baf
--- /dev/null
+++ b/vm/compiler/Loop.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DALVIK_VM_LOOP
+#define _DALVIK_VM_LOOP
+
+#include "Dalvik.h"
+#include "CompilerInternals.h"
+
+typedef struct LoopAnalysis {
+ BitVector *isIndVarV; // length == numSSAReg
+ GrowableList *ivList; // induction variables
+ GrowableList *arrayAccessInfo; // hoisted checks for array accesses
+ int numBasicIV; // number of basic induction variables
+ int ssaBIV; // basic IV in SSA name
+ bool isCountUpLoop; // count up or down loop
+ OpCode loopBranchOpcode; // OP_IF_XXX for the loop back branch
+ int endConditionReg; // vB in "vA op vB"
+ LIR *branchToBody; // branch over to the body from entry
+ LIR *branchToPCR; // branch over to the PCR cell
+ bool bodyIsClean; // loop body cannot throw any exceptions
+} LoopAnalysis;
+
+#endif /* _DALVIK_VM_LOOP */
diff --git a/vm/compiler/Ralloc.c b/vm/compiler/Ralloc.c
new file mode 100644
index 0000000..f227527
--- /dev/null
+++ b/vm/compiler/Ralloc.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "CompilerInternals.h"
+#include "Dataflow.h"
+
+typedef struct LiveRange {
+ int ssaName;
+ bool active;
+ int first;
+ int last;
+} LiveRange;
+
+int computeLiveRange(LiveRange *list, BasicBlock *bb, int seqNum)
+{
+ MIR *mir;
+ int i;
+
+ if (bb->blockType != kDalvikByteCode &&
+ bb->blockType != kTraceEntryBlock)
+ return seqNum;
+
+ for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
+ SSARepresentation *ssaRep = mir->ssaRep;
+ mir->seqNum = seqNum;
+ if (ssaRep) {
+ for (i=0; i< ssaRep->numUses; i++) {
+ int reg = ssaRep->uses[i];
+ list[reg].first = MIN(list[reg].first, seqNum);
+ list[reg].active = true;
+ }
+ for (i=0; i< ssaRep->numDefs; i++) {
+ int reg = ssaRep->defs[i];
+ list[reg].last = MAX(list[reg].last, seqNum + 1);
+ list[reg].active = true;
+ }
+ seqNum += 2;
+ }
+ }
+ return seqNum;
+}
+
+/*
+ * Quick & dirty - make FP usage sticky. This is strictly a hint - local
+ * code generation will handle misses. It might be worthwhile to collaborate
+ * with dx/dexopt to avoid reusing the same Dalvik temp for values of
+ * different types.
+ */
+static void inferTypes(CompilationUnit *cUnit, BasicBlock *bb)
+{
+ MIR *mir;
+ if (bb->blockType != kDalvikByteCode &&
+ bb->blockType != kTraceEntryBlock)
+ return;
+
+ for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
+ SSARepresentation *ssaRep = mir->ssaRep;
+ if (ssaRep) {
+ int i;
+ for (i=0; ssaRep->fpUse && i< ssaRep->numUses; i++) {
+ if (ssaRep->fpUse[i])
+ cUnit->regLocation[ssaRep->uses[i]].fp = true;
+ }
+ for (i=0; ssaRep->fpDef && i< ssaRep->numDefs; i++) {
+ if (ssaRep->fpDef[i])
+ cUnit->regLocation[ssaRep->defs[i]].fp = true;
+ }
+ }
+ }
+}
+
+/*
+ * Determine whether to use simple or aggressive register allocation. In
+ * general, loops and full methods will get aggressive.
+ */
+static bool simpleTrace(CompilationUnit *cUnit)
+{
+ //TODO: flesh out
+ return true;
+}
+
+/*
+ * Target-independent register allocation. Requires target-dependent
+ * helper functions and assumes free list, temp list and spill region.
+ * Uses a variant of linear scan and produces a mapping between SSA names
+ * and location. Location may be original Dalvik register, hardware
+ * register or spill location.
+ *
+ * Method:
+ * 0. Allocate the structure to hold the SSA name life ranges
+ * 1. Number each MIR instruction, counting by 2.
+ * +0 -> The "read" of the operands
+ * +1 -> The definition of the target resource
+ * 2. Compute live ranges for all SSA names *not* including the
+ * subscript 0 original Dalvik names. Phi functions ignored
+ * at this point.
+ * 3. Sort the live range list by lowest range start.
+ * 4. Process and remove all Phi functions.
+ * o If there is no live range collisions among all operands and
+ * the target of a Phi function, collapse operands and target
+ * and rewrite using target SSA name.
+ * o If there is a collision, introduce copies.
+ * 5. Allocate in order of increasing live range start.
+ */
+static const RegLocation freshLoc = {kLocDalvikFrame, 0, 0, INVALID_REG,
+ INVALID_REG, INVALID_SREG};
+void dvmCompilerRegAlloc(CompilationUnit *cUnit)
+{
+ int i;
+ int seqNum = 0;
+ LiveRange *ranges;
+ RegLocation *loc;
+
+ /* Allocate the location map */
+ loc = (RegLocation*)dvmCompilerNew(cUnit->numSSARegs * sizeof(*loc), true);
+ for (i=0; i< cUnit->numSSARegs; i++) {
+ loc[i] = freshLoc;
+ loc[i].sRegLow = i;
+ }
+ cUnit->regLocation = loc;
+
+ /* Do type inference pass */
+ for (i=0; i < cUnit->numBlocks; i++) {
+ inferTypes(cUnit, cUnit->blockList[i]);
+ }
+
+ if (simpleTrace(cUnit)) {
+ /*
+ * Just rename everything back to subscript 0 names and don't do
+ * any explicit promotion. Local allocator will opportunistically
+ * promote on the fly.
+ */
+ for (i=0; i < cUnit->numSSARegs; i++) {
+ cUnit->regLocation[i].sRegLow =
+ DECODE_REG(dvmConvertSSARegToDalvik(cUnit, loc[i].sRegLow));
+ }
+ } else {
+ // Compute live ranges
+ ranges = dvmCompilerNew(cUnit->numSSARegs * sizeof(*ranges), true);
+ for (i=0; i < cUnit->numSSARegs; i++)
+ ranges[i].active = false;
+ seqNum = computeLiveRange(ranges, cUnit->blockList[i], seqNum);
+ //TODO: phi squash & linear scan promotion
+ }
+}
diff --git a/vm/compiler/Utility.c b/vm/compiler/Utility.c
new file mode 100644
index 0000000..711d4cf
--- /dev/null
+++ b/vm/compiler/Utility.c
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "CompilerInternals.h"
+
+static ArenaMemBlock *arenaHead, *currentArena;
+static int numArenaBlocks;
+
+/* Allocate the initial memory block for arena-based allocation */
+bool dvmCompilerHeapInit(void)
+{
+ assert(arenaHead == NULL);
+ arenaHead =
+ (ArenaMemBlock *) malloc(sizeof(ArenaMemBlock) + ARENA_DEFAULT_SIZE);
+ if (arenaHead == NULL) {
+ LOGE("No memory left to create compiler heap memory\n");
+ return false;
+ }
+ arenaHead->blockSize = ARENA_DEFAULT_SIZE;
+ currentArena = arenaHead;
+ currentArena->bytesAllocated = 0;
+ currentArena->next = NULL;
+ numArenaBlocks = 1;
+
+ return true;
+}
+
+/* Arena-based malloc for compilation tasks */
+void * dvmCompilerNew(size_t size, bool zero)
+{
+ size = (size + 3) & ~3;
+retry:
+ /* Normal case - space is available in the current page */
+ if (size + currentArena->bytesAllocated <= currentArena->blockSize) {
+ void *ptr;
+ ptr = &currentArena->ptr[currentArena->bytesAllocated];
+ currentArena->bytesAllocated += size;
+ if (zero) {
+ memset(ptr, 0, size);
+ }
+ return ptr;
+ } else {
+ /*
+ * See if there are previously allocated arena blocks before the last
+ * reset
+ */
+ if (currentArena->next) {
+ currentArena = currentArena->next;
+ goto retry;
+ }
+
+ size_t blockSize = (size < ARENA_DEFAULT_SIZE) ?
+ ARENA_DEFAULT_SIZE : size;
+ /* Time to allocate a new arena */
+ ArenaMemBlock *newArena = (ArenaMemBlock *)
+ malloc(sizeof(ArenaMemBlock) + blockSize);
+ if (newArena == NULL) {
+ LOGE("Arena allocation failure");
+ dvmAbort();
+ }
+ newArena->blockSize = blockSize;
+ newArena->bytesAllocated = 0;
+ newArena->next = NULL;
+ currentArena->next = newArena;
+ currentArena = newArena;
+ numArenaBlocks++;
+ if (numArenaBlocks > 10)
+ LOGI("Total arena pages for JIT: %d", numArenaBlocks);
+ goto retry;
+ }
+ return NULL;
+}
+
+/* Reclaim all the arena blocks allocated so far */
+void dvmCompilerArenaReset(void)
+{
+ ArenaMemBlock *block;
+
+ for (block = arenaHead; block; block = block->next) {
+ block->bytesAllocated = 0;
+ }
+ currentArena = arenaHead;
+}
+
+/* Growable List initialization */
+void dvmInitGrowableList(GrowableList *gList, size_t initLength)
+{
+ gList->numAllocated = initLength;
+ gList->numUsed = 0;
+ gList->elemList = (void **) dvmCompilerNew(sizeof(void *) * initLength,
+ true);
+}
+
+/* Expand the capacity of a growable list */
+static void expandGrowableList(GrowableList *gList)
+{
+ int newLength = gList->numAllocated;
+ if (newLength < 128) {
+ newLength <<= 1;
+ } else {
+ newLength += 128;
+ }
+ void *newArray = dvmCompilerNew(sizeof(void *) * newLength, true);
+ memcpy(newArray, gList->elemList, sizeof(void *) * gList->numAllocated);
+ gList->numAllocated = newLength;
+ gList->elemList = newArray;
+}
+
+/* Insert a new element into the growable list */
+void dvmInsertGrowableList(GrowableList *gList, void *elem)
+{
+ assert(gList->numAllocated != 0);
+ if (gList->numUsed == gList->numAllocated) {
+ expandGrowableList(gList);
+ }
+ gList->elemList[gList->numUsed++] = elem;
+}
+
+/* Debug Utility - dump a compilation unit */
+void dvmCompilerDumpCompilationUnit(CompilationUnit *cUnit)
+{
+ int i;
+ BasicBlock *bb;
+ char *blockTypeNames[] = {
+ "Normal Chaining Cell",
+ "Hot Chaining Cell",
+ "Singleton Chaining Cell",
+ "Predicted Chaining Cell",
+ "Backward Branch",
+ "Chaining Cell Gap",
+ "N/A",
+ "Method Entry Block",
+ "Trace Entry Block",
+ "Code Block",
+ "Trace Exit Block",
+ "Method Exit Block",
+ "PC Reconstruction",
+ "Exception Handling",
+ };
+
+ LOGD("Compiling %s %s", cUnit->method->clazz->descriptor,
+ cUnit->method->name);
+ LOGD("%d insns", dvmGetMethodInsnsSize(cUnit->method));
+ LOGD("%d blocks in total", cUnit->numBlocks);
+
+ for (i = 0; i < cUnit->numBlocks; i++) {
+ bb = cUnit->blockList[i];
+ LOGD("Block %d (%s) (insn %04x - %04x%s)\n",
+ bb->id,
+ blockTypeNames[bb->blockType],
+ bb->startOffset,
+ bb->lastMIRInsn ? bb->lastMIRInsn->offset : bb->startOffset,
+ bb->lastMIRInsn ? "" : " empty");
+ if (bb->taken) {
+ LOGD(" Taken branch: block %d (%04x)\n",
+ bb->taken->id, bb->taken->startOffset);
+ }
+ if (bb->fallThrough) {
+ LOGD(" Fallthrough : block %d (%04x)\n",
+ bb->fallThrough->id, bb->fallThrough->startOffset);
+ }
+ }
+}
+
+/*
+ * dvmHashForeach callback.
+ */
+static int dumpMethodStats(void *compilerMethodStats, void *totalMethodStats)
+{
+ CompilerMethodStats *methodStats =
+ (CompilerMethodStats *) compilerMethodStats;
+ CompilerMethodStats *totalStats =
+ (CompilerMethodStats *) totalMethodStats;
+
+ totalStats->dalvikSize += methodStats->dalvikSize;
+ totalStats->compiledDalvikSize += methodStats->compiledDalvikSize;
+ totalStats->nativeSize += methodStats->nativeSize;
+
+ /* Enable the following when fine-tuning the JIT performance */
+#if 0
+ int limit = (methodStats->dalvikSize >> 2) * 3;
+
+ /* If over 3/4 of the Dalvik code is compiled, print something */
+ if (methodStats->compiledDalvikSize >= limit) {
+ LOGD("Method stats: %s%s, %d/%d (compiled/total Dalvik), %d (native)",
+ methodStats->method->clazz->descriptor,
+ methodStats->method->name,
+ methodStats->compiledDalvikSize,
+ methodStats->dalvikSize,
+ methodStats->nativeSize);
+ }
+#endif
+ return 0;
+}
+
+/*
+ * Dump the current stats of the compiler, including number of bytes used in
+ * the code cache, arena size, and work queue length, and various JIT stats.
+ */
+void dvmCompilerDumpStats(void)
+{
+ CompilerMethodStats totalMethodStats;
+
+ memset(&totalMethodStats, 0, sizeof(CompilerMethodStats));
+ LOGD("%d compilations using %d + %d bytes",
+ gDvmJit.numCompilations,
+ gDvmJit.templateSize,
+ gDvmJit.codeCacheByteUsed - gDvmJit.templateSize);
+ LOGD("Compiler arena uses %d blocks (%d bytes each)",
+ numArenaBlocks, ARENA_DEFAULT_SIZE);
+ LOGD("Compiler work queue length is %d/%d", gDvmJit.compilerQueueLength,
+ gDvmJit.compilerMaxQueued);
+ dvmJitStats();
+ dvmCompilerArchDump();
+ if (gDvmJit.methodStatsTable) {
+ dvmHashForeach(gDvmJit.methodStatsTable, dumpMethodStats,
+ &totalMethodStats);
+ LOGD("Code size stats: %d/%d (compiled/total Dalvik), %d (native)",
+ totalMethodStats.compiledDalvikSize,
+ totalMethodStats.dalvikSize,
+ totalMethodStats.nativeSize);
+ }
+}
+
+/*
+ * Allocate a bit vector with enough space to hold at least the specified
+ * number of bits.
+ *
+ * NOTE: this is the sister implementation of dvmAllocBitVector. In this version
+ * memory is allocated from the compiler arena.
+ */
+BitVector* dvmCompilerAllocBitVector(int startBits, bool expandable)
+{
+ BitVector* bv;
+ int count;
+
+ assert(sizeof(bv->storage[0]) == 4); /* assuming 32-bit units */
+ assert(startBits >= 0);
+
+ bv = (BitVector*) dvmCompilerNew(sizeof(BitVector), false);
+
+ count = (startBits + 31) >> 5;
+
+ bv->storageSize = count;
+ bv->expandable = expandable;
+ bv->storage = (u4*) dvmCompilerNew(count * sizeof(u4), true);
+ return bv;
+}
+
+/*
+ * Mark the specified bit as "set".
+ *
+ * Returns "false" if the bit is outside the range of the vector and we're
+ * not allowed to expand.
+ *
+ * NOTE: this is the sister implementation of dvmSetBit. In this version
+ * memory is allocated from the compiler arena.
+ */
+bool dvmCompilerSetBit(BitVector *pBits, int num)
+{
+ assert(num >= 0);
+ if (num >= pBits->storageSize * (int)sizeof(u4) * 8) {
+ if (!pBits->expandable)
+ return false;
+
+ int newSize = (num + 31) >> 5;
+ assert(newSize > pBits->storageSize);
+ u4 *newStorage = dvmCompilerNew(newSize * sizeof(u4), false);
+ memcpy(newStorage, pBits->storage, pBits->storageSize * sizeof(u4));
+ memset(&newStorage[pBits->storageSize], 0,
+ (newSize - pBits->storageSize) * sizeof(u4));
+ pBits->storage = newStorage;
+ pBits->storageSize = newSize;
+ }
+
+ pBits->storage[num >> 5] |= 1 << (num & 0x1f);
+ return true;
+}
+
+void dvmDebugBitVector(char *msg, const BitVector *bv, int length)
+{
+ int i;
+
+ LOGE("%s", msg);
+ for (i = 0; i < length; i++) {
+ if (dvmIsBitSet(bv, i)) {
+ LOGE("Bit %d is set", i);
+ }
+ }
+}
+
+void dvmCompilerAbort(CompilationUnit *cUnit)
+{
+ LOGE("Jit: aborting trace compilation, reverting to interpreter");
+ /* Force a traceback in debug builds */
+ assert(0);
+ /*
+ * Abort translation and force to interpret-only for this trace
+ * Matching setjmp in compiler thread work loop in Compiler.c.
+ */
+ longjmp(*cUnit->bailPtr, 1);
+}
diff --git a/vm/compiler/codegen/CompilerCodegen.h b/vm/compiler/codegen/CompilerCodegen.h
new file mode 100644
index 0000000..7379d50
--- /dev/null
+++ b/vm/compiler/codegen/CompilerCodegen.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DALVIK_VM_COMPILERCODEGEN_H_
+#define _DALVIK_VM_COMPILERCODEGEN_H_
+
+#include "compiler/CompilerIR.h"
+
+/* Maximal number of switch cases to have inline chains */
+#define MAX_CHAINED_SWITCH_CASES 64
+
+/* Work unit is architecture dependent */
+bool dvmCompilerDoWork(CompilerWorkOrder *work);
+
+/* Lower middle-level IR to low-level IR */
+void dvmCompilerMIR2LIR(CompilationUnit *cUnit);
+
+/* Assemble LIR into machine code */
+void dvmCompilerAssembleLIR(CompilationUnit *cUnit, JitTranslationInfo *info);
+
+/* Patch inline cache content for polymorphic callsites */
+bool dvmJitPatchInlineCache(void *cellPtr, void *contentPtr);
+
+/* Implemented in the codegen/<target>/ArchUtility.c */
+void dvmCompilerCodegenDump(CompilationUnit *cUnit);
+
+/* Implemented in the codegen/<target>/Assembler.c */
+void* dvmJitChain(void *tgtAddr, u4* branchAddr);
+u4* dvmJitUnchain(void *codeAddr);
+void dvmJitUnchainAll(void);
+void dvmCompilerPatchInlineCache(void);
+
+/* Implemented in codegen/<target>/Ralloc.c */
+void dvmCompilerRegAlloc(CompilationUnit *cUnit);
+
+/* Implemented in codegen/<target>/Thumb<version>Util.c */
+void dvmCompilerInitializeRegAlloc(CompilationUnit *cUnit);
+
+/* Implemented in codegen/<target>/<target_variant>/ArchVariant.c */
+JitInstructionSetType dvmCompilerInstructionSet(void);
+
+/*
+ * Implemented in codegen/<target>/<target_variant>/ArchVariant.c
+ * Architecture-specific initializations and checks
+ */
+bool dvmCompilerArchVariantInit(void);
+
+/* Implemented in codegen/<target>/<target_variant>/ArchVariant.c */
+int dvmCompilerTargetOptHint(int key);
+
+/* Implemented in codegen/<target>/<target_variant>/ArchVariant.c */
+void dvmCompilerGenMemBarrier(CompilationUnit *cUnit);
+
+#endif /* _DALVIK_VM_COMPILERCODEGEN_H_ */
diff --git a/vm/compiler/codegen/Optimizer.h b/vm/compiler/codegen/Optimizer.h
new file mode 100644
index 0000000..d42fe87
--- /dev/null
+++ b/vm/compiler/codegen/Optimizer.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DALVIK_VM_COMPILER_OPTIMIZATION_H
+#define _DALVIK_VM_COMPILER_OPTIMIZATION_H
+
+#include "Dalvik.h"
+
+/*
+ * If the corresponding bit is set in gDvmJit.disableOpt, the selected
+ * optimization will be suppressed.
+ */
+typedef enum optControlVector {
+ kLoadStoreElimination = 0,
+ kLoadHoisting,
+ kTrackLiveTemps,
+ kSuppressLoads,
+ kMethodInlining,
+} optControlVector;
+
+/* Forward declarations */
+struct CompilationUnit;
+struct LIR;
+
+void dvmCompilerApplyLocalOptimizations(struct CompilationUnit *cUnit,
+ struct LIR *head,
+ struct LIR *tail);
+
+void dvmCompilerApplyGlobalOptimizations(struct CompilationUnit *cUnit);
+
+#endif /* _DALVIK_VM_COMPILER_OPTIMIZATION_H */
diff --git a/vm/compiler/codegen/arm/ArchUtility.c b/vm/compiler/codegen/arm/ArchUtility.c
new file mode 100644
index 0000000..2e68459
--- /dev/null
+++ b/vm/compiler/codegen/arm/ArchUtility.c
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "../../CompilerInternals.h"
+#include "libdex/OpCodeNames.h"
+#include "ArmLIR.h"
+
+static char *shiftNames[4] = {
+ "lsl",
+ "lsr",
+ "asr",
+ "ror"};
+
+/* Decode and print a ARM register name */
+static char * decodeRegList(int vector, char *buf)
+{
+ int i;
+ bool printed = false;
+ buf[0] = 0;
+ for (i = 0; i < 8; i++, vector >>= 1) {
+ if (vector & 0x1) {
+ if (printed) {
+ sprintf(buf + strlen(buf), ", r%d", i);
+ } else {
+ printed = true;
+ sprintf(buf, "r%d", i);
+ }
+ }
+ }
+ return buf;
+}
+
+static int expandImmediate(int value)
+{
+ int mode = (value & 0xf00) >> 8;
+ u4 bits = value & 0xff;
+ switch(mode) {
+ case 0:
+ return bits;
+ case 1:
+ return (bits << 16) | bits;
+ case 2:
+ return (bits << 24) | (bits << 8);
+ case 3:
+ return (bits << 24) | (bits << 16) | (bits << 8) | bits;
+ default:
+ break;
+ }
+ bits = (bits | 0x80) << 24;
+ return bits >> (((value & 0xf80) >> 7) - 8);
+}
+
+/*
+ * Interpret a format string and build a string no longer than size
+ * See format key in Assemble.c.
+ */
+static void buildInsnString(char *fmt, ArmLIR *lir, char* buf,
+ unsigned char *baseAddr, int size)
+{
+ int i;
+ char *bufEnd = &buf[size-1];
+ char *fmtEnd = &fmt[strlen(fmt)];
+ char tbuf[256];
+ char *name;
+ char nc;
+ while (fmt < fmtEnd) {
+ int operand;
+ if (*fmt == '!') {
+ fmt++;
+ assert(fmt < fmtEnd);
+ nc = *fmt++;
+ if (nc=='!') {
+ strcpy(tbuf, "!");
+ } else {
+ assert(fmt < fmtEnd);
+ assert((unsigned)(nc-'0') < 4);
+ operand = lir->operands[nc-'0'];
+ switch(*fmt++) {
+ case 'H':
+ if (operand != 0) {
+ sprintf(tbuf, ", %s %d",shiftNames[operand & 0x3],
+ operand >> 2);
+ } else {
+ strcpy(tbuf,"");
+ }
+ break;
+ case 'B':
+ switch (operand) {
+ case kSY:
+ name = "sy";
+ break;
+ case kST:
+ name = "st";
+ break;
+ case kISH:
+ name = "ish";
+ break;
+ case kISHST:
+ name = "ishst";
+ break;
+ case kNSH:
+ name = "nsh";
+ break;
+ case kNSHST:
+ name = "shst";
+ break;
+ default:
+ name = "DecodeError";
+ break;
+ }
+ strcpy(tbuf, name);
+ break;
+ case 'b':
+ strcpy(tbuf,"0000");
+ for (i=3; i>= 0; i--) {
+ tbuf[i] += operand & 1;
+ operand >>= 1;
+ }
+ break;
+ case 'n':
+ operand = ~expandImmediate(operand);
+ sprintf(tbuf,"%d [0x%x]", operand, operand);
+ break;
+ case 'm':
+ operand = expandImmediate(operand);
+ sprintf(tbuf,"%d [0x%x]", operand, operand);
+ break;
+ case 's':
+ sprintf(tbuf,"s%d",operand & FP_REG_MASK);
+ break;
+ case 'S':
+ sprintf(tbuf,"d%d",(operand & FP_REG_MASK) >> 1);
+ break;
+ case 'h':
+ sprintf(tbuf,"%04x", operand);
+ break;
+ case 'M':
+ case 'd':
+ sprintf(tbuf,"%d", operand);
+ break;
+ case 'E':
+ sprintf(tbuf,"%d", operand*4);
+ break;
+ case 'F':
+ sprintf(tbuf,"%d", operand*2);
+ break;
+ case 'c':
+ switch (operand) {
+ case kArmCondEq:
+ strcpy(tbuf, "eq");
+ break;
+ case kArmCondNe:
+ strcpy(tbuf, "ne");
+ break;
+ case kArmCondLt:
+ strcpy(tbuf, "lt");
+ break;
+ case kArmCondGe:
+ strcpy(tbuf, "ge");
+ break;
+ case kArmCondGt:
+ strcpy(tbuf, "gt");
+ break;
+ case kArmCondLe:
+ strcpy(tbuf, "le");
+ break;
+ case kArmCondCs:
+ strcpy(tbuf, "cs");
+ break;
+ case kArmCondMi:
+ strcpy(tbuf, "mi");
+ break;
+ default:
+ strcpy(tbuf, "");
+ break;
+ }
+ break;
+ case 't':
+ sprintf(tbuf,"0x%08x",
+ (int) baseAddr + lir->generic.offset + 4 +
+ (operand << 1));
+ break;
+ case 'u': {
+ int offset_1 = lir->operands[0];
+ int offset_2 = NEXT_LIR(lir)->operands[0];
+ intptr_t target =
+ ((((intptr_t) baseAddr + lir->generic.offset + 4) &
+ ~3) + (offset_1 << 21 >> 9) + (offset_2 << 1)) &
+ 0xfffffffc;
+ sprintf(tbuf, "%p", (void *) target);
+ break;
+ }
+
+ /* Nothing to print for BLX_2 */
+ case 'v':
+ strcpy(tbuf, "see above");
+ break;
+ case 'R':
+ decodeRegList(operand, tbuf);
+ break;
+ default:
+ strcpy(tbuf,"DecodeError");
+ break;
+ }
+ if (buf+strlen(tbuf) <= bufEnd) {
+ strcpy(buf, tbuf);
+ buf += strlen(tbuf);
+ } else {
+ break;
+ }
+ }
+ } else {
+ *buf++ = *fmt++;
+ }
+ if (buf == bufEnd)
+ break;
+ }
+ *buf = 0;
+}
+
+void dvmDumpResourceMask(LIR *lir, u8 mask, const char *prefix)
+{
+ char buf[256];
+ buf[0] = 0;
+ ArmLIR *armLIR = (ArmLIR *) lir;
+
+ if (mask == ENCODE_ALL) {
+ strcpy(buf, "all");
+ } else {
+ char num[8];
+ int i;
+
+ for (i = 0; i < kRegEnd; i++) {
+ if (mask & (1ULL << i)) {
+ sprintf(num, "%d ", i);
+ strcat(buf, num);
+ }
+ }
+
+ if (mask & ENCODE_CCODE) {
+ strcat(buf, "cc ");
+ }
+ if (mask & ENCODE_FP_STATUS) {
+ strcat(buf, "fpcc ");
+ }
+ if (armLIR && (mask & ENCODE_DALVIK_REG)) {
+ sprintf(buf + strlen(buf), "dr%d%s", armLIR->aliasInfo & 0xffff,
+ (armLIR->aliasInfo & 0x80000000) ? "(+1)" : "");
+ }
+ }
+ if (buf[0]) {
+ LOGD("%s: %s", prefix, buf);
+ }
+}
+
+/*
+ * Debugging macros
+ */
+#define DUMP_RESOURCE_MASK(X)
+#define DUMP_SSA_REP(X)
+
+/* Pretty-print a LIR instruction */
+void dvmDumpLIRInsn(LIR *arg, unsigned char *baseAddr)
+{
+ ArmLIR *lir = (ArmLIR *) arg;
+ char buf[256];
+ char opName[256];
+ int offset = lir->generic.offset;
+ int dest = lir->operands[0];
+ const bool dumpNop = false;
+
+ /* Handle pseudo-ops individually, and all regular insns as a group */
+ switch(lir->opCode) {
+ case kArmChainingCellBottom:
+ LOGD("-------- end of chaining cells (0x%04x)\n", offset);
+ break;
+ case kArmPseudoBarrier:
+ LOGD("-------- BARRIER");
+ break;
+ case kArmPseudoExtended:
+ LOGD("-------- %s\n", (char *) dest);
+ break;
+ case kArmPseudoSSARep:
+ DUMP_SSA_REP(LOGD("-------- %s\n", (char *) dest));
+ break;
+ case kArmPseudoTargetLabel:
+ break;
+ case kArmPseudoChainingCellBackwardBranch:
+ LOGD("-------- chaining cell (backward branch): 0x%04x\n", dest);
+ break;
+ case kArmPseudoChainingCellNormal:
+ LOGD("-------- chaining cell (normal): 0x%04x\n", dest);
+ break;
+ case kArmPseudoChainingCellHot:
+ LOGD("-------- chaining cell (hot): 0x%04x\n", dest);
+ break;
+ case kArmPseudoChainingCellInvokePredicted:
+ LOGD("-------- chaining cell (predicted)\n");
+ break;
+ case kArmPseudoChainingCellInvokeSingleton:
+ LOGD("-------- chaining cell (invoke singleton): %s/%p\n",
+ ((Method *)dest)->name,
+ ((Method *)dest)->insns);
+ break;
+ case kArmPseudoEntryBlock:
+ LOGD("-------- entry offset: 0x%04x\n", dest);
+ break;
+ case kArmPseudoDalvikByteCodeBoundary:
+ LOGD("-------- dalvik offset: 0x%04x @ %s\n", dest,
+ (char *) lir->operands[1]);
+ break;
+ case kArmPseudoExitBlock:
+ LOGD("-------- exit offset: 0x%04x\n", dest);
+ break;
+ case kArmPseudoPseudoAlign4:
+ LOGD("%p (%04x): .align4\n", baseAddr + offset, offset);
+ break;
+ case kArmPseudoPCReconstructionCell:
+ LOGD("-------- reconstruct dalvik PC : 0x%04x @ +0x%04x\n", dest,
+ lir->operands[1]);
+ break;
+ case kArmPseudoPCReconstructionBlockLabel:
+ /* Do nothing */
+ break;
+ case kArmPseudoEHBlockLabel:
+ LOGD("Exception_Handling:\n");
+ break;
+ case kArmPseudoNormalBlockLabel:
+ LOGD("L%#06x:\n", dest);
+ break;
+ default:
+ if (lir->isNop && !dumpNop) {
+ break;
+ }
+ buildInsnString(EncodingMap[lir->opCode].name, lir, opName,
+ baseAddr, 256);
+ buildInsnString(EncodingMap[lir->opCode].fmt, lir, buf, baseAddr,
+ 256);
+ LOGD("%p (%04x): %-8s%s%s\n",
+ baseAddr + offset, offset, opName, buf,
+ lir->isNop ? "(nop)" : "");
+ break;
+ }
+
+ if (lir->useMask && (!lir->isNop || dumpNop)) {
+ DUMP_RESOURCE_MASK(dvmDumpResourceMask((LIR *) lir,
+ lir->useMask, "use"));
+ }
+ if (lir->defMask && (!lir->isNop || dumpNop)) {
+ DUMP_RESOURCE_MASK(dvmDumpResourceMask((LIR *) lir,
+ lir->defMask, "def"));
+ }
+}
+
+/* Dump instructions and constant pool contents */
+void dvmCompilerCodegenDump(CompilationUnit *cUnit)
+{
+ LOGD("Dumping LIR insns\n");
+ LIR *lirInsn;
+ ArmLIR *armLIR;
+
+ LOGD("installed code is at %p\n", cUnit->baseAddr);
+ LOGD("total size is %d bytes\n", cUnit->totalSize);
+ for (lirInsn = cUnit->firstLIRInsn; lirInsn; lirInsn = lirInsn->next) {
+ dvmDumpLIRInsn(lirInsn, cUnit->baseAddr);
+ }
+ for (lirInsn = cUnit->wordList; lirInsn; lirInsn = lirInsn->next) {
+ armLIR = (ArmLIR *) lirInsn;
+ LOGD("%p (%04x): .word (0x%x)\n",
+ (char*)cUnit->baseAddr + armLIR->generic.offset,
+ armLIR->generic.offset,
+ armLIR->operands[0]);
+ }
+}
diff --git a/vm/compiler/codegen/arm/ArmLIR.h b/vm/compiler/codegen/arm/ArmLIR.h
new file mode 100644
index 0000000..24f5240
--- /dev/null
+++ b/vm/compiler/codegen/arm/ArmLIR.h
@@ -0,0 +1,789 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "compiler/CompilerInternals.h"
+
+#ifndef _DALVIK_VM_COMPILER_CODEGEN_ARM_ARMLIR_H
+#define _DALVIK_VM_COMPILER_CODEGEN_ARM_ARMLIR_H
+
+/*
+ * r0, r1, r2, r3 are always scratch
+ * r4 (rPC) is scratch for Jit, but most be restored when resuming interp
+ * r5 (rFP) is reserved [holds Dalvik frame pointer]
+ * r6 (rGLUE) is reserved [holds current &interpState]
+ * r7 (rINST) is scratch for Jit
+ * r8 (rIBASE) is scratch for Jit, but must be restored when resuming interp
+ * r9 is reserved
+ * r10 is always scratch
+ * r11 (fp) used by gcc unless -fomit-frame-pointer set [available for jit?]
+ * r12 is always scratch
+ * r13 (sp) is reserved
+ * r14 (lr) is scratch for Jit
+ * r15 (pc) is reserved
+ *
+ * Preserved across C calls: r4, r5, r6, r7, r8, r10, r11
+ * Trashed across C calls: r0, r1, r2, r3, r12, r14
+ *
+ * Floating pointer registers
+ * s0-s31
+ * d0-d15, where d0={s0,s1}, d1={s2,s3}, ... , d15={s30,s31}
+ *
+ * s16-s31 (d8-d15) preserved across C calls
+ * s0-s15 (d0-d7) trashed across C calls
+ *
+ * For Thumb code use:
+ * r0, r1, r2, r3 to hold operands/results
+ * r4, r7 for temps
+ *
+ * For Thumb2 code use:
+ * r0, r1, r2, r3, r8, r9, r10, r11, r12, r14 for operands/results
+ * r4, r7 for temps
+ * s16-s31/d8-d15 for operands/results
+ * s0-s15/d0-d7 for temps
+ *
+ * When transitioning from code cache to interp:
+ * restore rIBASE
+ * restore rPC
+ * restore r11?
+ */
+
+/* Offset to distingish FP regs */
+#define FP_REG_OFFSET 32
+/* Offset to distinguish DP FP regs */
+#define FP_DOUBLE 64
+/* Reg types */
+#define REGTYPE(x) (x & (FP_REG_OFFSET | FP_DOUBLE))
+#define FPREG(x) ((x & FP_REG_OFFSET) == FP_REG_OFFSET)
+#define LOWREG(x) ((x & 0x7) == x)
+#define DOUBLEREG(x) ((x & FP_DOUBLE) == FP_DOUBLE)
+#define SINGLEREG(x) (FPREG(x) && !DOUBLEREG(x))
+/*
+ * Note: the low register of a floating point pair is sufficient to
+ * create the name of a double, but require both names to be passed to
+ * allow for asserts to verify that the pair is consecutive if significant
+ * rework is done in this area. Also, it is a good reminder in the calling
+ * code that reg locations always describe doubles as a pair of singles.
+ */
+#define S2D(x,y) ((x) | FP_DOUBLE)
+/* Mask to strip off fp flags */
+#define FP_REG_MASK (FP_REG_OFFSET-1)
+/* non-existent Dalvik register */
+#define vNone (-1)
+/* non-existant physical register */
+#define rNone (-1)
+
+/* RegisterLocation templates return values (r0, or r0/r1) */
+#define LOC_C_RETURN {kLocPhysReg, 0, 0, r0, 0, -1}
+#define LOC_C_RETURN_WIDE {kLocPhysReg, 1, 0, r0, r1, -1}
+/* RegisterLocation templates for interpState->retVal; */
+#define LOC_DALVIK_RETURN_VAL {kLocRetval, 0, 0, 0, 0, -1}
+#define LOC_DALVIK_RETURN_VAL_WIDE {kLocRetval, 1, 0, 0, 0, -1}
+
+ /*
+ * Data structure tracking the mapping between a Dalvik register (pair) and a
+ * native register (pair). The idea is to reuse the previously loaded value
+ * if possible, otherwise to keep the value in a native register as long as
+ * possible.
+ */
+typedef struct RegisterInfo {
+ int reg; // Reg number
+ bool inUse; // Has it been allocated?
+ bool pair; // Part of a register pair?
+ int partner; // If pair, other reg of pair
+ bool live; // Is there an associated SSA name?
+ bool dirty; // If live, is it dirty?
+ int sReg; // Name of live value
+ struct LIR *defStart; // Starting inst in last def sequence
+ struct LIR *defEnd; // Ending inst in last def sequence
+} RegisterInfo;
+
+typedef struct RegisterPool {
+ BitVector *nullCheckedRegs; // Track which registers have been null-checked
+ int numCoreTemps;
+ RegisterInfo *coreTemps;
+ int nextCoreTemp;
+ int numFPTemps;
+ RegisterInfo *FPTemps;
+ int nextFPTemp;
+ int numCoreRegs;
+ RegisterInfo *coreRegs;
+ int numFPRegs;
+ RegisterInfo *FPRegs;
+} RegisterPool;
+
+typedef enum ResourceEncodingPos {
+ kGPReg0 = 0,
+ kRegSP = 13,
+ kRegLR = 14,
+ kRegPC = 15,
+ kFPReg0 = 16,
+ kRegEnd = 48,
+ kCCode = kRegEnd,
+ kFPStatus,
+ kDalvikReg,
+ kLiteral,
+ kFrameRef,
+ kHeapRef,
+ kLitPoolRef
+} ResourceEncodingPos;
+
+#define ENCODE_REG_LIST(N) ((u8) N)
+#define ENCODE_REG_SP (1ULL << kRegSP)
+#define ENCODE_REG_LR (1ULL << kRegLR)
+#define ENCODE_REG_PC (1ULL << kRegPC)
+#define ENCODE_CCODE (1ULL << kCCode)
+#define ENCODE_FP_STATUS (1ULL << kFPStatus)
+
+ /* Must alias */
+#define ENCODE_DALVIK_REG (1ULL << kDalvikReg)
+#define ENCODE_LITERAL (1ULL << kLiteral)
+
+ /* May alias */
+#define ENCODE_FRAME_REF (1ULL << kFrameRef)
+#define ENCODE_HEAP_REF (1ULL << kHeapRef)
+#define ENCODE_LITPOOL_REF (1ULL << kLitPoolRef)
+
+#define ENCODE_ALL (~0ULL)
+#define ENCODE_MEM_DEF (ENCODE_FRAME_REF | ENCODE_HEAP_REF)
+#define ENCODE_MEM_USE (ENCODE_FRAME_REF | ENCODE_HEAP_REF \
+ | ENCODE_LITPOOL_REF)
+
+#define DECODE_ALIAS_INFO_REG(X) (X & 0xffff)
+#define DECODE_ALIAS_INFO_WIDE(X) ((X & 0x80000000) ? 1 : 0)
+
+typedef enum OpSize {
+ kWord,
+ kLong,
+ kSingle,
+ kDouble,
+ kUnsignedHalf,
+ kSignedHalf,
+ kUnsignedByte,
+ kSignedByte,
+} OpSize;
+
+typedef enum OpKind {
+ kOpMov,
+ kOpMvn,
+ kOpCmp,
+ kOpLsl,
+ kOpLsr,
+ kOpAsr,
+ kOpRor,
+ kOpNot,
+ kOpAnd,
+ kOpOr,
+ kOpXor,
+ kOpNeg,
+ kOpAdd,
+ kOpAdc,
+ kOpSub,
+ kOpSbc,
+ kOpRsub,
+ kOpMul,
+ kOpDiv,
+ kOpRem,
+ kOpBic,
+ kOpCmn,
+ kOpTst,
+ kOpBkpt,
+ kOpBlx,
+ kOpPush,
+ kOpPop,
+ kOp2Char,
+ kOp2Short,
+ kOp2Byte,
+ kOpCondBr,
+ kOpUncondBr,
+} OpKind;
+
+typedef enum NativeRegisterPool {
+ r0 = 0,
+ r1 = 1,
+ r2 = 2,
+ r3 = 3,
+ r4PC = 4,
+ rFP = 5,
+ rGLUE = 6,
+ r7 = 7,
+ r8 = 8,
+ r9 = 9,
+ r10 = 10,
+ r11 = 11,
+ r12 = 12,
+ r13 = 13,
+ rlr = 14,
+ rpc = 15,
+ fr0 = 0 + FP_REG_OFFSET,
+ fr1 = 1 + FP_REG_OFFSET,
+ fr2 = 2 + FP_REG_OFFSET,
+ fr3 = 3 + FP_REG_OFFSET,
+ fr4 = 4 + FP_REG_OFFSET,
+ fr5 = 5 + FP_REG_OFFSET,
+ fr6 = 6 + FP_REG_OFFSET,
+ fr7 = 7 + FP_REG_OFFSET,
+ fr8 = 8 + FP_REG_OFFSET,
+ fr9 = 9 + FP_REG_OFFSET,
+ fr10 = 10 + FP_REG_OFFSET,
+ fr11 = 11 + FP_REG_OFFSET,
+ fr12 = 12 + FP_REG_OFFSET,
+ fr13 = 13 + FP_REG_OFFSET,
+ fr14 = 14 + FP_REG_OFFSET,
+ fr15 = 15 + FP_REG_OFFSET,
+ fr16 = 16 + FP_REG_OFFSET,
+ fr17 = 17 + FP_REG_OFFSET,
+ fr18 = 18 + FP_REG_OFFSET,
+ fr19 = 19 + FP_REG_OFFSET,
+ fr20 = 20 + FP_REG_OFFSET,
+ fr21 = 21 + FP_REG_OFFSET,
+ fr22 = 22 + FP_REG_OFFSET,
+ fr23 = 23 + FP_REG_OFFSET,
+ fr24 = 24 + FP_REG_OFFSET,
+ fr25 = 25 + FP_REG_OFFSET,
+ fr26 = 26 + FP_REG_OFFSET,
+ fr27 = 27 + FP_REG_OFFSET,
+ fr28 = 28 + FP_REG_OFFSET,
+ fr29 = 29 + FP_REG_OFFSET,
+ fr30 = 30 + FP_REG_OFFSET,
+ fr31 = 31 + FP_REG_OFFSET,
+ dr0 = fr0 + FP_DOUBLE,
+ dr1 = fr2 + FP_DOUBLE,
+ dr2 = fr4 + FP_DOUBLE,
+ dr3 = fr6 + FP_DOUBLE,
+ dr4 = fr8 + FP_DOUBLE,
+ dr5 = fr10 + FP_DOUBLE,
+ dr6 = fr12 + FP_DOUBLE,
+ dr7 = fr14 + FP_DOUBLE,
+ dr8 = fr16 + FP_DOUBLE,
+ dr9 = fr18 + FP_DOUBLE,
+ dr10 = fr20 + FP_DOUBLE,
+ dr11 = fr22 + FP_DOUBLE,
+ dr12 = fr24 + FP_DOUBLE,
+ dr13 = fr26 + FP_DOUBLE,
+ dr14 = fr28 + FP_DOUBLE,
+ dr15 = fr30 + FP_DOUBLE,
+} NativeRegisterPool;
+
+/* Shift encodings */
+typedef enum ArmShiftEncodings {
+ kArmLsl = 0x0,
+ kArmLsr = 0x1,
+ kArmAsr = 0x2,
+ kArmRor = 0x3
+} ArmShiftEncodings;
+
+/* Thumb condition encodings */
+typedef enum ArmConditionCode {
+ kArmCondEq = 0x0, /* 0000 */
+ kArmCondNe = 0x1, /* 0001 */
+ kArmCondCs = 0x2, /* 0010 */
+ kArmCondCc = 0x3, /* 0011 */
+ kArmCondMi = 0x4, /* 0100 */
+ kArmCondPl = 0x5, /* 0101 */
+ kArmCondVs = 0x6, /* 0110 */
+ kArmCondVc = 0x7, /* 0111 */
+ kArmCondHi = 0x8, /* 1000 */
+ kArmCondLs = 0x9, /* 1001 */
+ kArmCondGe = 0xa, /* 1010 */
+ kArmCondLt = 0xb, /* 1011 */
+ kArmCondGt = 0xc, /* 1100 */
+ kArmCondLe = 0xd, /* 1101 */
+ kArmCondAl = 0xe, /* 1110 */
+ kArmCondNv = 0xf, /* 1111 */
+} ArmConditionCode;
+
+#define isPseudoOpCode(opCode) ((int)(opCode) < 0)
+
+/*
+ * The following enum defines the list of supported Thumb instructions by the
+ * assembler. Their corresponding snippet positions will be defined in
+ * Assemble.c.
+ */
+typedef enum ArmOpCode {
+ kArmChainingCellBottom = -18,
+ kArmPseudoBarrier = -17,
+ kArmPseudoExtended = -16,
+ kArmPseudoSSARep = -15,
+ kArmPseudoEntryBlock = -14,
+ kArmPseudoExitBlock = -13,
+ kArmPseudoTargetLabel = -12,
+ kArmPseudoChainingCellBackwardBranch = -11,
+ kArmPseudoChainingCellHot = -10,
+ kArmPseudoChainingCellInvokePredicted = -9,
+ kArmPseudoChainingCellInvokeSingleton = -8,
+ kArmPseudoChainingCellNormal = -7,
+ kArmPseudoDalvikByteCodeBoundary = -6,
+ kArmPseudoPseudoAlign4 = -5,
+ kArmPseudoPCReconstructionCell = -4,
+ kArmPseudoPCReconstructionBlockLabel = -3,
+ kArmPseudoEHBlockLabel = -2,
+ kArmPseudoNormalBlockLabel = -1,
+ /************************************************************************/
+ kArm16BitData, /* DATA [0] rd[15..0] */
+ kThumbAdcRR, /* adc [0100000101] rm[5..3] rd[2..0] */
+ kThumbAddRRI3, /* add(1) [0001110] imm_3[8..6] rn[5..3] rd[2..0]*/
+ kThumbAddRI8, /* add(2) [00110] rd[10..8] imm_8[7..0] */
+ kThumbAddRRR, /* add(3) [0001100] rm[8..6] rn[5..3] rd[2..0] */
+ kThumbAddRRLH, /* add(4) [01000100] H12[01] rm[5..3] rd[2..0] */
+ kThumbAddRRHL, /* add(4) [01001000] H12[10] rm[5..3] rd[2..0] */
+ kThumbAddRRHH, /* add(4) [01001100] H12[11] rm[5..3] rd[2..0] */
+ kThumbAddPcRel, /* add(5) [10100] rd[10..8] imm_8[7..0] */
+ kThumbAddSpRel, /* add(6) [10101] rd[10..8] imm_8[7..0] */
+ kThumbAddSpI7, /* add(7) [101100000] imm_7[6..0] */
+ kThumbAndRR, /* and [0100000000] rm[5..3] rd[2..0] */
+ kThumbAsrRRI5, /* asr(1) [00010] imm_5[10..6] rm[5..3] rd[2..0] */
+ kThumbAsrRR, /* asr(2) [0100000100] rs[5..3] rd[2..0] */
+ kThumbBCond, /* b(1) [1101] cond[11..8] offset_8[7..0] */
+ kThumbBUncond, /* b(2) [11100] offset_11[10..0] */
+ kThumbBicRR, /* bic [0100001110] rm[5..3] rd[2..0] */
+ kThumbBkpt, /* bkpt [10111110] imm_8[7..0] */
+ kThumbBlx1, /* blx(1) [111] H[10] offset_11[10..0] */
+ kThumbBlx2, /* blx(1) [111] H[01] offset_11[10..0] */
+ kThumbBl1, /* blx(1) [111] H[10] offset_11[10..0] */
+ kThumbBl2, /* blx(1) [111] H[11] offset_11[10..0] */
+ kThumbBlxR, /* blx(2) [010001111] rm[6..3] [000] */
+ kThumbBx, /* bx [010001110] H2[6..6] rm[5..3] SBZ[000] */
+ kThumbCmnRR, /* cmn [0100001011] rm[5..3] rd[2..0] */
+ kThumbCmpRI8, /* cmp(1) [00101] rn[10..8] imm_8[7..0] */
+ kThumbCmpRR, /* cmp(2) [0100001010] rm[5..3] rd[2..0] */
+ kThumbCmpLH, /* cmp(3) [01000101] H12[01] rm[5..3] rd[2..0] */
+ kThumbCmpHL, /* cmp(3) [01000110] H12[10] rm[5..3] rd[2..0] */
+ kThumbCmpHH, /* cmp(3) [01000111] H12[11] rm[5..3] rd[2..0] */
+ kThumbEorRR, /* eor [0100000001] rm[5..3] rd[2..0] */
+ kThumbLdmia, /* ldmia [11001] rn[10..8] reglist [7..0] */
+ kThumbLdrRRI5, /* ldr(1) [01101] imm_5[10..6] rn[5..3] rd[2..0] */
+ kThumbLdrRRR, /* ldr(2) [0101100] rm[8..6] rn[5..3] rd[2..0] */
+ kThumbLdrPcRel, /* ldr(3) [01001] rd[10..8] imm_8[7..0] */
+ kThumbLdrSpRel, /* ldr(4) [10011] rd[10..8] imm_8[7..0] */
+ kThumbLdrbRRI5, /* ldrb(1) [01111] imm_5[10..6] rn[5..3] rd[2..0] */
+ kThumbLdrbRRR, /* ldrb(2) [0101110] rm[8..6] rn[5..3] rd[2..0] */
+ kThumbLdrhRRI5, /* ldrh(1) [10001] imm_5[10..6] rn[5..3] rd[2..0] */
+ kThumbLdrhRRR, /* ldrh(2) [0101101] rm[8..6] rn[5..3] rd[2..0] */
+ kThumbLdrsbRRR, /* ldrsb [0101011] rm[8..6] rn[5..3] rd[2..0] */
+ kThumbLdrshRRR, /* ldrsh [0101111] rm[8..6] rn[5..3] rd[2..0] */
+ kThumbLslRRI5, /* lsl(1) [00000] imm_5[10..6] rm[5..3] rd[2..0] */
+ kThumbLslRR, /* lsl(2) [0100000010] rs[5..3] rd[2..0] */
+ kThumbLsrRRI5, /* lsr(1) [00001] imm_5[10..6] rm[5..3] rd[2..0] */
+ kThumbLsrRR, /* lsr(2) [0100000011] rs[5..3] rd[2..0] */
+ kThumbMovImm, /* mov(1) [00100] rd[10..8] imm_8[7..0] */
+ kThumbMovRR, /* mov(2) [0001110000] rn[5..3] rd[2..0] */
+ kThumbMovRR_H2H, /* mov(3) [01000111] H12[11] rm[5..3] rd[2..0] */
+ kThumbMovRR_H2L, /* mov(3) [01000110] H12[01] rm[5..3] rd[2..0] */
+ kThumbMovRR_L2H, /* mov(3) [01000101] H12[10] rm[5..3] rd[2..0] */
+ kThumbMul, /* mul [0100001101] rm[5..3] rd[2..0] */
+ kThumbMvn, /* mvn [0100001111] rm[5..3] rd[2..0] */
+ kThumbNeg, /* neg [0100001001] rm[5..3] rd[2..0] */
+ kThumbOrr, /* orr [0100001100] rm[5..3] rd[2..0] */
+ kThumbPop, /* pop [1011110] r[8..8] rl[7..0] */
+ kThumbPush, /* push [1011010] r[8..8] rl[7..0] */
+ kThumbRorRR, /* ror [0100000111] rs[5..3] rd[2..0] */
+ kThumbSbc, /* sbc [0100000110] rm[5..3] rd[2..0] */
+ kThumbStmia, /* stmia [11000] rn[10..8] reglist [7.. 0] */
+ kThumbStrRRI5, /* str(1) [01100] imm_5[10..6] rn[5..3] rd[2..0] */
+ kThumbStrRRR, /* str(2) [0101000] rm[8..6] rn[5..3] rd[2..0] */
+ kThumbStrSpRel, /* str(3) [10010] rd[10..8] imm_8[7..0] */
+ kThumbStrbRRI5, /* strb(1) [01110] imm_5[10..6] rn[5..3] rd[2..0] */
+ kThumbStrbRRR, /* strb(2) [0101010] rm[8..6] rn[5..3] rd[2..0] */
+ kThumbStrhRRI5, /* strh(1) [10000] imm_5[10..6] rn[5..3] rd[2..0] */
+ kThumbStrhRRR, /* strh(2) [0101001] rm[8..6] rn[5..3] rd[2..0] */
+ kThumbSubRRI3, /* sub(1) [0001111] imm_3[8..6] rn[5..3] rd[2..0]*/
+ kThumbSubRI8, /* sub(2) [00111] rd[10..8] imm_8[7..0] */
+ kThumbSubRRR, /* sub(3) [0001101] rm[8..6] rn[5..3] rd[2..0] */
+ kThumbSubSpI7, /* sub(4) [101100001] imm_7[6..0] */
+ kThumbSwi, /* swi [11011111] imm_8[7..0] */
+ kThumbTst, /* tst [0100001000] rm[5..3] rn[2..0] */
+ kThumb2Vldrs, /* vldr low sx [111011011001] rn[19..16] rd[15-12]
+ [1010] imm_8[7..0] */
+ kThumb2Vldrd, /* vldr low dx [111011011001] rn[19..16] rd[15-12]
+ [1011] imm_8[7..0] */
+ kThumb2Vmuls, /* vmul vd, vn, vm [111011100010] rn[19..16]
+ rd[15-12] [10100000] rm[3..0] */
+ kThumb2Vmuld, /* vmul vd, vn, vm [111011100010] rn[19..16]
+ rd[15-12] [10110000] rm[3..0] */
+ kThumb2Vstrs, /* vstr low sx [111011011000] rn[19..16] rd[15-12]
+ [1010] imm_8[7..0] */
+ kThumb2Vstrd, /* vstr low dx [111011011000] rn[19..16] rd[15-12]
+ [1011] imm_8[7..0] */
+ kThumb2Vsubs, /* vsub vd, vn, vm [111011100011] rn[19..16]
+ rd[15-12] [10100040] rm[3..0] */
+ kThumb2Vsubd, /* vsub vd, vn, vm [111011100011] rn[19..16]
+ rd[15-12] [10110040] rm[3..0] */
+ kThumb2Vadds, /* vadd vd, vn, vm [111011100011] rn[19..16]
+ rd[15-12] [10100000] rm[3..0] */
+ kThumb2Vaddd, /* vadd vd, vn, vm [111011100011] rn[19..16]
+ rd[15-12] [10110000] rm[3..0] */
+ kThumb2Vdivs, /* vdiv vd, vn, vm [111011101000] rn[19..16]
+ rd[15-12] [10100000] rm[3..0] */
+ kThumb2Vdivd, /* vdiv vd, vn, vm [111011101000] rn[19..16]
+ rd[15-12] [10110000] rm[3..0] */
+ kThumb2VcvtIF, /* vcvt.F32 vd, vm [1110111010111000] vd[15..12]
+ [10101100] vm[3..0] */
+ kThumb2VcvtID, /* vcvt.F64 vd, vm [1110111010111000] vd[15..12]
+ [10111100] vm[3..0] */
+ kThumb2VcvtFI, /* vcvt.S32.F32 vd, vm [1110111010111101] vd[15..12]
+ [10101100] vm[3..0] */
+ kThumb2VcvtDI, /* vcvt.S32.F32 vd, vm [1110111010111101] vd[15..12]
+ [10111100] vm[3..0] */
+ kThumb2VcvtFd, /* vcvt.F64.F32 vd, vm [1110111010110111] vd[15..12]
+ [10101100] vm[3..0] */
+ kThumb2VcvtDF, /* vcvt.F32.F64 vd, vm [1110111010110111] vd[15..12]
+ [10111100] vm[3..0] */
+ kThumb2Vsqrts, /* vsqrt.f32 vd, vm [1110111010110001] vd[15..12]
+ [10101100] vm[3..0] */
+ kThumb2Vsqrtd, /* vsqrt.f64 vd, vm [1110111010110001] vd[15..12]
+ [10111100] vm[3..0] */
+ kThumb2MovImmShift, /* mov(T2) rd, #<const> [11110] i [00001001111]
+ imm3 rd[11..8] imm8 */
+ kThumb2MovImm16, /* mov(T3) rd, #<const> [11110] i [0010100] imm4 [0]
+ imm3 rd[11..8] imm8 */
+ kThumb2StrRRI12, /* str(Imm,T3) rd,[rn,#imm12] [111110001100]
+ rn[19..16] rt[15..12] imm12[11..0] */
+ kThumb2LdrRRI12, /* str(Imm,T3) rd,[rn,#imm12] [111110001100]
+ rn[19..16] rt[15..12] imm12[11..0] */
+ kThumb2StrRRI8Predec, /* str(Imm,T4) rd,[rn,#-imm8] [111110000100]
+ rn[19..16] rt[15..12] [1100] imm[7..0]*/
+ kThumb2LdrRRI8Predec, /* ldr(Imm,T4) rd,[rn,#-imm8] [111110000101]
+ rn[19..16] rt[15..12] [1100] imm[7..0]*/
+ kThumb2Cbnz, /* cbnz rd,<label> [101110] i [1] imm5[7..3]
+ rn[2..0] */
+ kThumb2Cbz, /* cbn rd,<label> [101100] i [1] imm5[7..3]
+ rn[2..0] */
+ kThumb2AddRRI12, /* add rd, rn, #imm12 [11110] i [100000] rn[19..16]
+ [0] imm3[14..12] rd[11..8] imm8[7..0] */
+ kThumb2MovRR, /* mov rd, rm [11101010010011110000] rd[11..8]
+ [0000] rm[3..0] */
+ kThumb2Vmovs, /* vmov.f32 vd, vm [111011101] D [110000]
+ vd[15..12] 101001] M [0] vm[3..0] */
+ kThumb2Vmovd, /* vmov.f64 vd, vm [111011101] D [110000]
+ vd[15..12] 101101] M [0] vm[3..0] */
+ kThumb2Ldmia, /* ldmia [111010001001[ rn[19..16] mask[15..0] */
+ kThumb2Stmia, /* stmia [111010001000[ rn[19..16] mask[15..0] */
+ kThumb2AddRRR, /* add [111010110000] rn[19..16] [0000] rd[11..8]
+ [0000] rm[3..0] */
+ kThumb2SubRRR, /* sub [111010111010] rn[19..16] [0000] rd[11..8]
+ [0000] rm[3..0] */
+ kThumb2SbcRRR, /* sbc [111010110110] rn[19..16] [0000] rd[11..8]
+ [0000] rm[3..0] */
+ kThumb2CmpRR, /* cmp [111010111011] rn[19..16] [0000] [1111]
+ [0000] rm[3..0] */
+ kThumb2SubRRI12, /* sub rd, rn, #imm12 [11110] i [01010] rn[19..16]
+ [0] imm3[14..12] rd[11..8] imm8[7..0] */
+ kThumb2MvnImmShift, /* mov(T2) rd, #<const> [11110] i [00011011110]
+ imm3 rd[11..8] imm8 */
+ kThumb2Sel, /* sel rd, rn, rm [111110101010] rn[19-16] rd[11-8]
+ rm[3-0] */
+ kThumb2Ubfx, /* ubfx rd,rn,#lsb,#width [111100111100] rn[19..16]
+ [0] imm3[14-12] rd[11-8] w[4-0] */
+ kThumb2Sbfx, /* ubfx rd,rn,#lsb,#width [111100110100] rn[19..16]
+ [0] imm3[14-12] rd[11-8] w[4-0] */
+ kThumb2LdrRRR, /* ldr rt,[rn,rm,LSL #imm] [111110000101] rn[19-16]
+ rt[15-12] [000000] imm[5-4] rm[3-0] */
+ kThumb2LdrhRRR, /* ldrh rt,[rn,rm,LSL #imm] [111110000101] rn[19-16]
+ rt[15-12] [000000] imm[5-4] rm[3-0] */
+ kThumb2LdrshRRR, /* ldrsh rt,[rn,rm,LSL #imm] [111110000101] rn[19-16]
+ rt[15-12] [000000] imm[5-4] rm[3-0] */
+ kThumb2LdrbRRR, /* ldrb rt,[rn,rm,LSL #imm] [111110000101] rn[19-16]
+ rt[15-12] [000000] imm[5-4] rm[3-0] */
+ kThumb2LdrsbRRR, /* ldrsb rt,[rn,rm,LSL #imm] [111110000101] rn[19-16]
+ rt[15-12] [000000] imm[5-4] rm[3-0] */
+ kThumb2StrRRR, /* str rt,[rn,rm,LSL #imm] [111110000100] rn[19-16]
+ rt[15-12] [000000] imm[5-4] rm[3-0] */
+ kThumb2StrhRRR, /* str rt,[rn,rm,LSL #imm] [111110000010] rn[19-16]
+ rt[15-12] [000000] imm[5-4] rm[3-0] */
+ kThumb2StrbRRR, /* str rt,[rn,rm,LSL #imm] [111110000000] rn[19-16]
+ rt[15-12] [000000] imm[5-4] rm[3-0] */
+ kThumb2LdrhRRI12, /* ldrh rt,[rn,#imm12] [111110001011]
+ rt[15..12] rn[19..16] imm12[11..0] */
+ kThumb2LdrshRRI12, /* ldrsh rt,[rn,#imm12] [111110011011]
+ rt[15..12] rn[19..16] imm12[11..0] */
+ kThumb2LdrbRRI12, /* ldrb rt,[rn,#imm12] [111110001001]
+ rt[15..12] rn[19..16] imm12[11..0] */
+ kThumb2LdrsbRRI12, /* ldrsb rt,[rn,#imm12] [111110011001]
+ rt[15..12] rn[19..16] imm12[11..0] */
+ kThumb2StrhRRI12, /* strh rt,[rn,#imm12] [111110001010]
+ rt[15..12] rn[19..16] imm12[11..0] */
+ kThumb2StrbRRI12, /* strb rt,[rn,#imm12] [111110001000]
+ rt[15..12] rn[19..16] imm12[11..0] */
+ kThumb2Pop, /* pop [1110100010111101] list[15-0]*/
+ kThumb2Push, /* push [1110100010101101] list[15-0]*/
+ kThumb2CmpRI8, /* cmp rn, #<const> [11110] i [011011] rn[19-16] [0]
+ imm3 [1111] imm8[7..0] */
+ kThumb2AdcRRR, /* adc [111010110101] rn[19..16] [0000] rd[11..8]
+ [0000] rm[3..0] */
+ kThumb2AndRRR, /* and [111010100000] rn[19..16] [0000] rd[11..8]
+ [0000] rm[3..0] */
+ kThumb2BicRRR, /* bic [111010100010] rn[19..16] [0000] rd[11..8]
+ [0000] rm[3..0] */
+ kThumb2CmnRR, /* cmn [111010110001] rn[19..16] [0000] [1111]
+ [0000] rm[3..0] */
+ kThumb2EorRRR, /* eor [111010101000] rn[19..16] [0000] rd[11..8]
+ [0000] rm[3..0] */
+ kThumb2MulRRR, /* mul [111110110000] rn[19..16] [1111] rd[11..8]
+ [0000] rm[3..0] */
+ kThumb2MnvRR, /* mvn [11101010011011110] rd[11-8] [0000]
+ rm[3..0] */
+ kThumb2RsubRRI8, /* rsub [111100011100] rn[19..16] [0000] rd[11..8]
+ imm8[7..0] */
+ kThumb2NegRR, /* actually rsub rd, rn, #0 */
+ kThumb2OrrRRR, /* orr [111010100100] rn[19..16] [0000] rd[11..8]
+ [0000] rm[3..0] */
+ kThumb2TstRR, /* tst [111010100001] rn[19..16] [0000] [1111]
+ [0000] rm[3..0] */
+ kThumb2LslRRR, /* lsl [111110100000] rn[19..16] [1111] rd[11..8]
+ [0000] rm[3..0] */
+ kThumb2LsrRRR, /* lsr [111110100010] rn[19..16] [1111] rd[11..8]
+ [0000] rm[3..0] */
+ kThumb2AsrRRR, /* asr [111110100100] rn[19..16] [1111] rd[11..8]
+ [0000] rm[3..0] */
+ kThumb2RorRRR, /* ror [111110100110] rn[19..16] [1111] rd[11..8]
+ [0000] rm[3..0] */
+ kThumb2LslRRI5, /* lsl [11101010010011110] imm[14.12] rd[11..8]
+ [00] rm[3..0] */
+ kThumb2LsrRRI5, /* lsr [11101010010011110] imm[14.12] rd[11..8]
+ [01] rm[3..0] */
+ kThumb2AsrRRI5, /* asr [11101010010011110] imm[14.12] rd[11..8]
+ [10] rm[3..0] */
+ kThumb2RorRRI5, /* ror [11101010010011110] imm[14.12] rd[11..8]
+ [11] rm[3..0] */
+ kThumb2BicRRI8, /* bic [111100000010] rn[19..16] [0] imm3
+ rd[11..8] imm8 */
+ kThumb2AndRRI8, /* bic [111100000000] rn[19..16] [0] imm3
+ rd[11..8] imm8 */
+ kThumb2OrrRRI8, /* orr [111100000100] rn[19..16] [0] imm3
+ rd[11..8] imm8 */
+ kThumb2EorRRI8, /* eor [111100001000] rn[19..16] [0] imm3
+ rd[11..8] imm8 */
+ kThumb2AddRRI8, /* add [111100001000] rn[19..16] [0] imm3
+ rd[11..8] imm8 */
+ kThumb2AdcRRI8, /* adc [111100010101] rn[19..16] [0] imm3
+ rd[11..8] imm8 */
+ kThumb2SubRRI8, /* sub [111100011011] rn[19..16] [0] imm3
+ rd[11..8] imm8 */
+ kThumb2SbcRRI8, /* sbc [111100010111] rn[19..16] [0] imm3
+ rd[11..8] imm8 */
+ kThumb2It, /* it [10111111] firstcond[7-4] mask[3-0] */
+ kThumb2Fmstat, /* fmstat [11101110111100011111101000010000] */
+ kThumb2Vcmpd, /* vcmp [111011101] D [11011] rd[15-12] [1011]
+ E [1] M [0] rm[3-0] */
+ kThumb2Vcmps, /* vcmp [111011101] D [11010] rd[15-12] [1011]
+ E [1] M [0] rm[3-0] */
+ kThumb2LdrPcRel12, /* ldr rd,[pc,#imm12] [1111100011011111] rt[15-12]
+ imm12[11-0] */
+ kThumb2BCond, /* b<c> [1110] S cond[25-22] imm6[21-16] [10]
+ J1 [0] J2 imm11[10..0] */
+ kThumb2Vmovd_RR, /* vmov [111011101] D [110000] vd[15-12 [101101]
+ M [0] vm[3-0] */
+ kThumb2Vmovs_RR, /* vmov [111011101] D [110000] vd[15-12 [101001]
+ M [0] vm[3-0] */
+ kThumb2Fmrs, /* vmov [111011100000] vn[19-16] rt[15-12] [1010]
+ N [0010000] */
+ kThumb2Fmsr, /* vmov [111011100001] vn[19-16] rt[15-12] [1010]
+ N [0010000] */
+ kThumb2Fmrrd, /* vmov [111011000100] rt2[19-16] rt[15-12]
+ [101100] M [1] vm[3-0] */
+ kThumb2Fmdrr, /* vmov [111011000101] rt2[19-16] rt[15-12]
+ [101100] M [1] vm[3-0] */
+ kThumb2Vabsd, /* vabs.f64 [111011101] D [110000] rd[15-12]
+ [1011110] M [0] vm[3-0] */
+ kThumb2Vabss, /* vabs.f32 [111011101] D [110000] rd[15-12]
+ [1010110] M [0] vm[3-0] */
+ kThumb2Vnegd, /* vneg.f64 [111011101] D [110000] rd[15-12]
+ [1011110] M [0] vm[3-0] */
+ kThumb2Vnegs, /* vneg.f32 [111011101] D [110000] rd[15-12]
+ [1010110] M [0] vm[3-0] */
+ kThumb2Vmovs_IMM8, /* vmov.f32 [111011101] D [11] imm4h[19-16] vd[15-12]
+ [10100000] imm4l[3-0] */
+ kThumb2Vmovd_IMM8, /* vmov.f64 [111011101] D [11] imm4h[19-16] vd[15-12]
+ [10110000] imm4l[3-0] */
+ kThumb2Mla, /* mla [111110110000] rn[19-16] ra[15-12] rd[7-4]
+ [0000] rm[3-0] */
+ kThumb2Umull, /* umull [111110111010] rn[19-16], rdlo[15-12]
+ rdhi[11-8] [0000] rm[3-0] */
+ kThumb2Ldrex, /* ldrex [111010000101] rn[19-16] rt[11-8] [1111]
+ imm8[7-0] */
+ kThumb2Strex, /* strex [111010000100] rn[19-16] rt[11-8] rd[11-8]
+ imm8[7-0] */
+ kThumb2Clrex, /* clrex [111100111011111110000111100101111] */
+ kThumb2Bfi, /* bfi [111100110110] rn[19-16] [0] imm3[14-12]
+ rd[11-8] imm2[7-6] [0] msb[4-0] */
+ kThumb2Bfc, /* bfc [11110011011011110] [0] imm3[14-12]
+ rd[11-8] imm2[7-6] [0] msb[4-0] */
+ kThumb2Dmb, /* dmb [1111001110111111100011110101] option[3-0] */
+
+ kArmLast,
+} ArmOpCode;
+
+/* DMB option encodings */
+typedef enum ArmOpDmbOptions {
+ kSY = 0xf,
+ kST = 0xe,
+ kISH = 0xb,
+ kISHST = 0xa,
+ kNSH = 0x7,
+ kNSHST = 0x6
+} ArmOpDmbOptions;
+
+/* Bit flags describing the behavior of each native opcode */
+typedef enum ArmOpFeatureFlags {
+ kIsBranch = 0,
+ kRegDef0,
+ kRegDef1,
+ kRegDefSP,
+ kRegDefLR,
+ kRegDefList0,
+ kRegDefList1,
+ kRegUse0,
+ kRegUse1,
+ kRegUse2,
+ kRegUse3,
+ kRegUseSP,
+ kRegUsePC,
+ kRegUseList0,
+ kRegUseList1,
+ kNoOperand,
+ kIsUnaryOp,
+ kIsBinaryOp,
+ kIsTertiaryOp,
+ kIsQuadOp,
+ kIsIT,
+ kSetsCCodes,
+ kUsesCCodes,
+ kMemLoad,
+ kMemStore,
+} ArmOpFeatureFlags;
+
+#define IS_LOAD (1 << kMemLoad)
+#define IS_STORE (1 << kMemStore)
+#define IS_BRANCH (1 << kIsBranch)
+#define REG_DEF0 (1 << kRegDef0)
+#define REG_DEF1 (1 << kRegDef1)
+#define REG_DEF_SP (1 << kRegDefSP)
+#define REG_DEF_LR (1 << kRegDefLR)
+#define REG_DEF_LIST0 (1 << kRegDefList0)
+#define REG_DEF_LIST1 (1 << kRegDefList1)
+#define REG_USE0 (1 << kRegUse0)
+#define REG_USE1 (1 << kRegUse1)
+#define REG_USE2 (1 << kRegUse2)
+#define REG_USE3 (1 << kRegUse3)
+#define REG_USE_SP (1 << kRegUseSP)
+#define REG_USE_PC (1 << kRegUsePC)
+#define REG_USE_LIST0 (1 << kRegUseList0)
+#define REG_USE_LIST1 (1 << kRegUseList1)
+#define NO_OPERAND (1 << kNoOperand)
+#define IS_UNARY_OP (1 << kIsUnaryOp)
+#define IS_BINARY_OP (1 << kIsBinaryOp)
+#define IS_TERTIARY_OP (1 << kIsTertiaryOp)
+#define IS_QUAD_OP (1 << kIsQuadOp)
+#define IS_IT (1 << kIsIT)
+#define SETS_CCODES (1 << kSetsCCodes)
+#define USES_CCODES (1 << kUsesCCodes)
+
+/* Common combo register usage patterns */
+#define REG_USE01 (REG_USE0 | REG_USE1)
+#define REG_USE012 (REG_USE01 | REG_USE2)
+#define REG_USE12 (REG_USE1 | REG_USE2)
+#define REG_DEF0_USE0 (REG_DEF0 | REG_USE0)
+#define REG_DEF0_USE1 (REG_DEF0 | REG_USE1)
+#define REG_DEF0_USE01 (REG_DEF0 | REG_USE01)
+#define REG_DEF0_USE12 (REG_DEF0 | REG_USE12)
+#define REG_DEF01_USE2 (REG_DEF0 | REG_DEF1 | REG_USE2)
+
+/* Instruction assembly fieldLoc kind */
+typedef enum ArmEncodingKind {
+ kFmtUnused,
+ kFmtBitBlt, /* Bit string using end/start */
+ kFmtDfp, /* Double FP reg */
+ kFmtSfp, /* Single FP reg */
+ kFmtModImm, /* Shifted 8-bit immed using [26,14..12,7..0] */
+ kFmtImm16, /* Zero-extended immed using [26,19..16,14..12,7..0] */
+ kFmtImm6, /* Encoded branch target using [9,7..3]0 */
+ kFmtImm12, /* Zero-extended immediate using [26,14..12,7..0] */
+ kFmtShift, /* Shift descriptor, [14..12,7..4] */
+ kFmtLsb, /* least significant bit using [14..12][7..6] */
+ kFmtBWidth, /* bit-field width, encoded as width-1 */
+ kFmtShift5, /* Shift count, [14..12,7..6] */
+ kFmtBrOffset, /* Signed extended [26,11,13,21-16,10-0]:0 */
+ kFmtFPImm, /* Encoded floating point immediate */
+} ArmEncodingKind;
+
+/* Struct used to define the snippet positions for each Thumb opcode */
+typedef struct ArmEncodingMap {
+ u4 skeleton;
+ struct {
+ ArmEncodingKind kind;
+ int end; /* end for kFmtBitBlt, 1-bit slice end for FP regs */
+ int start; /* start for kFmtBitBlt, 4-bit slice end for FP regs */
+ } fieldLoc[4];
+ ArmOpCode opCode;
+ int flags;
+ char *name;
+ char* fmt;
+ int size;
+} ArmEncodingMap;
+
+/* Keys for target-specific scheduling and other optimization hints */
+typedef enum ArmTargetOptHints {
+ kMaxHoistDistance,
+} ArmTargetOptHints;
+
+extern ArmEncodingMap EncodingMap[kArmLast];
+
+/*
+ * Each instance of this struct holds a pseudo or real LIR instruction:
+ * - pseudo ones (eg labels and marks) and will be discarded by the assembler.
+ * - real ones will be assembled into Thumb instructions.
+ *
+ * Machine resources are encoded into a 64-bit vector, where the encodings are
+ * as following:
+ * - [ 0..15]: general purpose registers including PC, SP, and LR
+ * - [16..47]: floating-point registers where d0 is expanded to s[01] and s0
+ * starts at bit 16
+ * - [48]: IT block
+ * - [49]: integer condition code
+ * - [50]: floatint-point status word
+ */
+typedef struct ArmLIR {
+ LIR generic;
+ ArmOpCode opCode;
+ int operands[4]; // [0..3] = [dest, src1, src2, extra]
+ bool isNop; // LIR is optimized away
+ bool branchInsertSV;// mark for insertion of branch before this instruction,
+ // used to identify mem ops for self verification mode
+ int age; // default is 0, set lazily by the optimizer
+ int size; // 16-bit unit size (1 for thumb, 1 or 2 for thumb2)
+ int aliasInfo; // For Dalvik register access & litpool disambiguation
+ u8 useMask; // Resource mask for use
+ u8 defMask; // Resource mask for def
+} ArmLIR;
+
+/* Init values when a predicted chain is initially assembled */
+/* E7FE is branch to self */
+#define PREDICTED_CHAIN_BX_PAIR_INIT 0xe7fe
+
+/* Utility macros to traverse the LIR/ArmLIR list */
+#define NEXT_LIR(lir) ((ArmLIR *) lir->generic.next)
+#define PREV_LIR(lir) ((ArmLIR *) lir->generic.prev)
+
+#define NEXT_LIR_LVALUE(lir) (lir)->generic.next
+#define PREV_LIR_LVALUE(lir) (lir)->generic.prev
+
+#define CHAIN_CELL_OFFSET_TAG 0xcdab
+
+#define CHAIN_CELL_NORMAL_SIZE 12
+#define CHAIN_CELL_PREDICTED_SIZE 16
+
+#endif /* _DALVIK_VM_COMPILER_CODEGEN_ARM_ARMLIR_H */
diff --git a/vm/compiler/codegen/arm/Assemble.c b/vm/compiler/codegen/arm/Assemble.c
new file mode 100644
index 0000000..4f54f1e
--- /dev/null
+++ b/vm/compiler/codegen/arm/Assemble.c
@@ -0,0 +1,2651 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "libdex/OpCode.h"
+#include "libdex/OpCodeNames.h"
+
+#include "../../CompilerInternals.h"
+#include "ArmLIR.h"
+#include "Codegen.h"
+#include <unistd.h> /* for cacheflush */
+#include <sys/mman.h> /* for protection change */
+
+#define MAX_ASSEMBLER_RETRIES 10
+
+/*
+ * opcode: ArmOpCode enum
+ * skeleton: pre-designated bit-pattern for this opcode
+ * k0: key to applying ds/de
+ * ds: dest start bit position
+ * de: dest end bit position
+ * k1: key to applying s1s/s1e
+ * s1s: src1 start bit position
+ * s1e: src1 end bit position
+ * k2: key to applying s2s/s2e
+ * s2s: src2 start bit position
+ * s2e: src2 end bit position
+ * operands: number of operands (for sanity check purposes)
+ * name: mnemonic name
+ * fmt: for pretty-printing
+ */
+#define ENCODING_MAP(opcode, skeleton, k0, ds, de, k1, s1s, s1e, k2, s2s, s2e, \
+ k3, k3s, k3e, flags, name, fmt, size) \
+ {skeleton, {{k0, ds, de}, {k1, s1s, s1e}, {k2, s2s, s2e}, \
+ {k3, k3s, k3e}}, opcode, flags, name, fmt, size}
+
+/* Instruction dump string format keys: !pf, where "!" is the start
+ * of the key, "p" is which numeric operand to use and "f" is the
+ * print format.
+ *
+ * [p]ositions:
+ * 0 -> operands[0] (dest)
+ * 1 -> operands[1] (src1)
+ * 2 -> operands[2] (src2)
+ * 3 -> operands[3] (extra)
+ *
+ * [f]ormats:
+ * h -> 4-digit hex
+ * d -> decimal
+ * E -> decimal*4
+ * F -> decimal*2
+ * c -> branch condition (beq, bne, etc.)
+ * t -> pc-relative target
+ * u -> 1st half of bl[x] target
+ * v -> 2nd half ob bl[x] target
+ * R -> register list
+ * s -> single precision floating point register
+ * S -> double precision floating point register
+ * m -> Thumb2 modified immediate
+ * n -> complimented Thumb2 modified immediate
+ * M -> Thumb2 16-bit zero-extended immediate
+ * b -> 4-digit binary
+ * B -> dmb option string (sy, st, ish, ishst, nsh, hshst)
+ * H -> operand shift
+ *
+ * [!] escape. To insert "!", use "!!"
+ */
+/* NOTE: must be kept in sync with enum ArmOpcode from ArmLIR.h */
+ArmEncodingMap EncodingMap[kArmLast] = {
+ ENCODING_MAP(kArm16BitData, 0x0000,
+ kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP, "data", "0x!0h(!0d)", 1),
+ ENCODING_MAP(kThumbAdcRR, 0x4140,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES | USES_CCODES,
+ "adcs", "r!0d, r!1d", 1),
+ ENCODING_MAP(kThumbAddRRI3, 0x1c00,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "adds", "r!0d, r!1d, #!2d", 1),
+ ENCODING_MAP(kThumbAddRI8, 0x3000,
+ kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE0 | SETS_CCODES,
+ "adds", "r!0d, r!0d, #!1d", 1),
+ ENCODING_MAP(kThumbAddRRR, 0x1800,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE12 | SETS_CCODES,
+ "adds", "r!0d, r!1d, r!2d", 1),
+ ENCODING_MAP(kThumbAddRRLH, 0x4440,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE01,
+ "add", "r!0d, r!1d", 1),
+ ENCODING_MAP(kThumbAddRRHL, 0x4480,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE01,
+ "add", "r!0d, r!1d", 1),
+ ENCODING_MAP(kThumbAddRRHH, 0x44c0,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE01,
+ "add", "r!0d, r!1d", 1),
+ ENCODING_MAP(kThumbAddPcRel, 0xa000,
+ kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | IS_BRANCH,
+ "add", "r!0d, pc, #!1E", 1),
+ ENCODING_MAP(kThumbAddSpRel, 0xa800,
+ kFmtBitBlt, 10, 8, kFmtUnused, -1, -1, kFmtBitBlt, 7, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF_SP | REG_USE_SP,
+ "add", "r!0d, sp, #!2E", 1),
+ ENCODING_MAP(kThumbAddSpI7, 0xb000,
+ kFmtBitBlt, 6, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP | REG_DEF_SP | REG_USE_SP,
+ "add", "sp, #!0d*4", 1),
+ ENCODING_MAP(kThumbAndRR, 0x4000,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+ "ands", "r!0d, r!1d", 1),
+ ENCODING_MAP(kThumbAsrRRI5, 0x1000,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "asrs", "r!0d, r!1d, #!2d", 1),
+ ENCODING_MAP(kThumbAsrRR, 0x4100,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+ "asrs", "r!0d, r!1d", 1),
+ ENCODING_MAP(kThumbBCond, 0xd000,
+ kFmtBitBlt, 7, 0, kFmtBitBlt, 11, 8, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | USES_CCODES,
+ "b!1c", "!0t", 1),
+ ENCODING_MAP(kThumbBUncond, 0xe000,
+ kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, NO_OPERAND | IS_BRANCH,
+ "b", "!0t", 1),
+ ENCODING_MAP(kThumbBicRR, 0x4380,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+ "bics", "r!0d, r!1d", 1),
+ ENCODING_MAP(kThumbBkpt, 0xbe00,
+ kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH,
+ "bkpt", "!0d", 1),
+ ENCODING_MAP(kThumbBlx1, 0xf000,
+ kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | REG_DEF_LR,
+ "blx_1", "!0u", 1),
+ ENCODING_MAP(kThumbBlx2, 0xe800,
+ kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | REG_DEF_LR,
+ "blx_2", "!0v", 1),
+ ENCODING_MAP(kThumbBl1, 0xf000,
+ kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_DEF_LR,
+ "bl_1", "!0u", 1),
+ ENCODING_MAP(kThumbBl2, 0xf800,
+ kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_DEF_LR,
+ "bl_2", "!0v", 1),
+ ENCODING_MAP(kThumbBlxR, 0x4780,
+ kFmtBitBlt, 6, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_UNARY_OP | REG_USE0 | IS_BRANCH | REG_DEF_LR,
+ "blx", "r!0d", 1),
+ ENCODING_MAP(kThumbBx, 0x4700,
+ kFmtBitBlt, 6, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH,
+ "bx", "r!0d", 1),
+ ENCODING_MAP(kThumbCmnRR, 0x42c0,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES,
+ "cmn", "r!0d, r!1d", 1),
+ ENCODING_MAP(kThumbCmpRI8, 0x2800,
+ kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE0 | SETS_CCODES,
+ "cmp", "r!0d, #!1d", 1),
+ ENCODING_MAP(kThumbCmpRR, 0x4280,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES,
+ "cmp", "r!0d, r!1d", 1),
+ ENCODING_MAP(kThumbCmpLH, 0x4540,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES,
+ "cmp", "r!0d, r!1d", 1),
+ ENCODING_MAP(kThumbCmpHL, 0x4580,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES,
+ "cmp", "r!0d, r!1d", 1),
+ ENCODING_MAP(kThumbCmpHH, 0x45c0,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES,
+ "cmp", "r!0d, r!1d", 1),
+ ENCODING_MAP(kThumbEorRR, 0x4040,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+ "eors", "r!0d, r!1d", 1),
+ ENCODING_MAP(kThumbLdmia, 0xc800,
+ kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE0 | REG_DEF_LIST1 | IS_LOAD,
+ "ldmia", "r!0d!!, <!1R>", 1),
+ ENCODING_MAP(kThumbLdrRRI5, 0x6800,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+ "ldr", "r!0d, [r!1d, #!2E]", 1),
+ ENCODING_MAP(kThumbLdrRRR, 0x5800,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD,
+ "ldr", "r!0d, [r!1d, r!2d]", 1),
+ ENCODING_MAP(kThumbLdrPcRel, 0x4800,
+ kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0 | REG_USE_PC
+ | IS_LOAD, "ldr", "r!0d, [pc, #!1E]", 1),
+ ENCODING_MAP(kThumbLdrSpRel, 0x9800,
+ kFmtBitBlt, 10, 8, kFmtUnused, -1, -1, kFmtBitBlt, 7, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0 | REG_USE_SP
+ | IS_LOAD, "ldr", "r!0d, [sp, #!2E]", 1),
+ ENCODING_MAP(kThumbLdrbRRI5, 0x7800,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+ "ldrb", "r!0d, [r!1d, #2d]", 1),
+ ENCODING_MAP(kThumbLdrbRRR, 0x5c00,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD,
+ "ldrb", "r!0d, [r!1d, r!2d]", 1),
+ ENCODING_MAP(kThumbLdrhRRI5, 0x8800,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+ "ldrh", "r!0d, [r!1d, #!2F]", 1),
+ ENCODING_MAP(kThumbLdrhRRR, 0x5a00,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD,
+ "ldrh", "r!0d, [r!1d, r!2d]", 1),
+ ENCODING_MAP(kThumbLdrsbRRR, 0x5600,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD,
+ "ldrsb", "r!0d, [r!1d, r!2d]", 1),
+ ENCODING_MAP(kThumbLdrshRRR, 0x5e00,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD,
+ "ldrsh", "r!0d, [r!1d, r!2d]", 1),
+ ENCODING_MAP(kThumbLslRRI5, 0x0000,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "lsls", "r!0d, r!1d, #!2d", 1),
+ ENCODING_MAP(kThumbLslRR, 0x4080,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+ "lsls", "r!0d, r!1d", 1),
+ ENCODING_MAP(kThumbLsrRRI5, 0x0800,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "lsrs", "r!0d, r!1d, #!2d", 1),
+ ENCODING_MAP(kThumbLsrRR, 0x40c0,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+ "lsrs", "r!0d, r!1d", 1),
+ ENCODING_MAP(kThumbMovImm, 0x2000,
+ kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0 | SETS_CCODES,
+ "movs", "r!0d, #!1d", 1),
+ ENCODING_MAP(kThumbMovRR, 0x1c00,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "movs", "r!0d, r!1d", 1),
+ ENCODING_MAP(kThumbMovRR_H2H, 0x46c0,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "mov", "r!0d, r!1d", 1),
+ ENCODING_MAP(kThumbMovRR_H2L, 0x4640,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "mov", "r!0d, r!1d", 1),
+ ENCODING_MAP(kThumbMovRR_L2H, 0x4680,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "mov", "r!0d, r!1d", 1),
+ ENCODING_MAP(kThumbMul, 0x4340,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+ "muls", "r!0d, r!1d", 1),
+ ENCODING_MAP(kThumbMvn, 0x43c0,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "mvns", "r!0d, r!1d", 1),
+ ENCODING_MAP(kThumbNeg, 0x4240,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "negs", "r!0d, r!1d", 1),
+ ENCODING_MAP(kThumbOrr, 0x4300,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+ "orrs", "r!0d, r!1d", 1),
+ ENCODING_MAP(kThumbPop, 0xbc00,
+ kFmtBitBlt, 8, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_DEF_LIST0
+ | IS_LOAD, "pop", "<!0R>", 1),
+ ENCODING_MAP(kThumbPush, 0xb400,
+ kFmtBitBlt, 8, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_USE_LIST0
+ | IS_STORE, "push", "<!0R>", 1),
+ ENCODING_MAP(kThumbRorRR, 0x41c0,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
+ "rors", "r!0d, r!1d", 1),
+ ENCODING_MAP(kThumbSbc, 0x4180,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE01 | USES_CCODES | SETS_CCODES,
+ "sbcs", "r!0d, r!1d", 1),
+ ENCODING_MAP(kThumbStmia, 0xc000,
+ kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0 | REG_USE0 | REG_USE_LIST1 | IS_STORE,
+ "stmia", "r!0d!!, <!1R>", 1),
+ ENCODING_MAP(kThumbStrRRI5, 0x6000,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+ "str", "r!0d, [r!1d, #!2E]", 1),
+ ENCODING_MAP(kThumbStrRRR, 0x5000,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE012 | IS_STORE,
+ "str", "r!0d, [r!1d, r!2d]", 1),
+ ENCODING_MAP(kThumbStrSpRel, 0x9000,
+ kFmtBitBlt, 10, 8, kFmtUnused, -1, -1, kFmtBitBlt, 7, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE0 | REG_USE_SP
+ | IS_STORE, "str", "r!0d, [sp, #!2E]", 1),
+ ENCODING_MAP(kThumbStrbRRI5, 0x7000,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+ "strb", "r!0d, [r!1d, #!2d]", 1),
+ ENCODING_MAP(kThumbStrbRRR, 0x5400,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE012 | IS_STORE,
+ "strb", "r!0d, [r!1d, r!2d]", 1),
+ ENCODING_MAP(kThumbStrhRRI5, 0x8000,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+ "strh", "r!0d, [r!1d, #!2F]", 1),
+ ENCODING_MAP(kThumbStrhRRR, 0x5200,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE012 | IS_STORE,
+ "strh", "r!0d, [r!1d, r!2d]", 1),
+ ENCODING_MAP(kThumbSubRRI3, 0x1e00,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "subs", "r!0d, r!1d, #!2d]", 1),
+ ENCODING_MAP(kThumbSubRI8, 0x3800,
+ kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE0 | SETS_CCODES,
+ "subs", "r!0d, #!1d", 1),
+ ENCODING_MAP(kThumbSubRRR, 0x1a00,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE12 | SETS_CCODES,
+ "subs", "r!0d, r!1d, r!2d", 1),
+ ENCODING_MAP(kThumbSubSpI7, 0xb080,
+ kFmtBitBlt, 6, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_UNARY_OP | REG_DEF_SP | REG_USE_SP,
+ "sub", "sp, #!0d", 1),
+ ENCODING_MAP(kThumbSwi, 0xdf00,
+ kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH,
+ "swi", "!0d", 1),
+ ENCODING_MAP(kThumbTst, 0x4200,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP | REG_USE01 | SETS_CCODES,
+ "tst", "r!0d, r!1d", 1),
+ ENCODING_MAP(kThumb2Vldrs, 0xed900a00,
+ kFmtSfp, 22, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+ "vldr", "!0s, [r!1d, #!2E]", 2),
+ ENCODING_MAP(kThumb2Vldrd, 0xed900b00,
+ kFmtDfp, 22, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+ "vldr", "!0S, [r!1d, #!2E]", 2),
+ ENCODING_MAP(kThumb2Vmuls, 0xee200a00,
+ kFmtSfp, 22, 12, kFmtSfp, 7, 16, kFmtSfp, 5, 0,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE12,
+ "vmuls", "!0s, !1s, !2s", 2),
+ ENCODING_MAP(kThumb2Vmuld, 0xee200b00,
+ kFmtDfp, 22, 12, kFmtDfp, 7, 16, kFmtDfp, 5, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "vmuld", "!0S, !1S, !2S", 2),
+ ENCODING_MAP(kThumb2Vstrs, 0xed800a00,
+ kFmtSfp, 22, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+ "vstr", "!0s, [r!1d, #!2E]", 2),
+ ENCODING_MAP(kThumb2Vstrd, 0xed800b00,
+ kFmtDfp, 22, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+ "vstr", "!0S, [r!1d, #!2E]", 2),
+ ENCODING_MAP(kThumb2Vsubs, 0xee300a40,
+ kFmtSfp, 22, 12, kFmtSfp, 7, 16, kFmtSfp, 5, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "vsub", "!0s, !1s, !2s", 2),
+ ENCODING_MAP(kThumb2Vsubd, 0xee300b40,
+ kFmtDfp, 22, 12, kFmtDfp, 7, 16, kFmtDfp, 5, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "vsub", "!0S, !1S, !2S", 2),
+ ENCODING_MAP(kThumb2Vadds, 0xee300a00,
+ kFmtSfp, 22, 12, kFmtSfp, 7, 16, kFmtSfp, 5, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "vadd", "!0s, !1s, !2s", 2),
+ ENCODING_MAP(kThumb2Vaddd, 0xee300b00,
+ kFmtDfp, 22, 12, kFmtDfp, 7, 16, kFmtDfp, 5, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "vadd", "!0S, !1S, !2S", 2),
+ ENCODING_MAP(kThumb2Vdivs, 0xee800a00,
+ kFmtSfp, 22, 12, kFmtSfp, 7, 16, kFmtSfp, 5, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "vdivs", "!0s, !1s, !2s", 2),
+ ENCODING_MAP(kThumb2Vdivd, 0xee800b00,
+ kFmtDfp, 22, 12, kFmtDfp, 7, 16, kFmtDfp, 5, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "vdivd", "!0S, !1S, !2S", 2),
+ ENCODING_MAP(kThumb2VcvtIF, 0xeeb80ac0,
+ kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vcvt.f32", "!0s, !1s", 2),
+ ENCODING_MAP(kThumb2VcvtID, 0xeeb80bc0,
+ kFmtDfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vcvt.f64", "!0S, !1s", 2),
+ ENCODING_MAP(kThumb2VcvtFI, 0xeebd0ac0,
+ kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vcvt.s32.f32 ", "!0s, !1s", 2),
+ ENCODING_MAP(kThumb2VcvtDI, 0xeebd0bc0,
+ kFmtSfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vcvt.s32.f64 ", "!0s, !1S", 2),
+ ENCODING_MAP(kThumb2VcvtFd, 0xeeb70ac0,
+ kFmtDfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vcvt.f64.f32 ", "!0S, !1s", 2),
+ ENCODING_MAP(kThumb2VcvtDF, 0xeeb70bc0,
+ kFmtSfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vcvt.f32.f64 ", "!0s, !1S", 2),
+ ENCODING_MAP(kThumb2Vsqrts, 0xeeb10ac0,
+ kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vsqrt.f32 ", "!0s, !1s", 2),
+ ENCODING_MAP(kThumb2Vsqrtd, 0xeeb10bc0,
+ kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vsqrt.f64 ", "!0S, !1S", 2),
+ ENCODING_MAP(kThumb2MovImmShift, 0xf04f0000, /* no setflags encoding */
+ kFmtBitBlt, 11, 8, kFmtModImm, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
+ "mov", "r!0d, #!1m", 2),
+ ENCODING_MAP(kThumb2MovImm16, 0xf2400000,
+ kFmtBitBlt, 11, 8, kFmtImm16, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
+ "mov", "r!0d, #!1M", 2),
+ ENCODING_MAP(kThumb2StrRRI12, 0xf8c00000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+ "str", "r!0d, [r!1d, #!2d]", 2),
+ ENCODING_MAP(kThumb2LdrRRI12, 0xf8d00000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+ "ldr", "r!0d, [r!1d, #!2d]", 2),
+ ENCODING_MAP(kThumb2StrRRI8Predec, 0xf8400c00,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 8, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+ "str", "r!0d, [r!1d, #-!2d]", 2),
+ ENCODING_MAP(kThumb2LdrRRI8Predec, 0xf8500c00,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 8, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+ "ldr", "r!0d, [r!1d, #-!2d]", 2),
+ ENCODING_MAP(kThumb2Cbnz, 0xb900, /* Note: does not affect flags */
+ kFmtBitBlt, 2, 0, kFmtImm6, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE0 | IS_BRANCH,
+ "cbnz", "r!0d,!1t", 1),
+ ENCODING_MAP(kThumb2Cbz, 0xb100, /* Note: does not affect flags */
+ kFmtBitBlt, 2, 0, kFmtImm6, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE0 | IS_BRANCH,
+ "cbz", "r!0d,!1t", 1),
+ ENCODING_MAP(kThumb2AddRRI12, 0xf2000000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtImm12, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1,/* Note: doesn't affect flags */
+ "add", "r!0d,r!1d,#!2d", 2),
+ ENCODING_MAP(kThumb2MovRR, 0xea4f0000, /* no setflags encoding */
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "mov", "r!0d, r!1d", 2),
+ ENCODING_MAP(kThumb2Vmovs, 0xeeb00a40,
+ kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vmov.f32 ", " !0s, !1s", 2),
+ ENCODING_MAP(kThumb2Vmovd, 0xeeb00b40,
+ kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vmov.f64 ", " !0S, !1S", 2),
+ ENCODING_MAP(kThumb2Ldmia, 0xe8900000,
+ kFmtBitBlt, 19, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE0 | REG_DEF_LIST1 | IS_LOAD,
+ "ldmia", "r!0d!!, <!1R>", 2),
+ ENCODING_MAP(kThumb2Stmia, 0xe8800000,
+ kFmtBitBlt, 19, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE0 | REG_USE_LIST1 | IS_STORE,
+ "stmia", "r!0d!!, <!1R>", 2),
+ ENCODING_MAP(kThumb2AddRRR, 0xeb100000, /* setflags encoding */
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtShift, -1, -1,
+ IS_QUAD_OP | REG_DEF0_USE12 | SETS_CCODES,
+ "adds", "r!0d, r!1d, r!2d!3H", 2),
+ ENCODING_MAP(kThumb2SubRRR, 0xebb00000, /* setflags enconding */
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtShift, -1, -1,
+ IS_QUAD_OP | REG_DEF0_USE12 | SETS_CCODES,
+ "subs", "r!0d, r!1d, r!2d!3H", 2),
+ ENCODING_MAP(kThumb2SbcRRR, 0xeb700000, /* setflags encoding */
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtShift, -1, -1,
+ IS_QUAD_OP | REG_DEF0_USE12 | USES_CCODES | SETS_CCODES,
+ "sbcs", "r!0d, r!1d, r!2d!3H", 2),
+ ENCODING_MAP(kThumb2CmpRR, 0xebb00f00,
+ kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtShift, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_USE01 | SETS_CCODES,
+ "cmp", "r!0d, r!1d", 2),
+ ENCODING_MAP(kThumb2SubRRI12, 0xf2a00000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtImm12, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1,/* Note: doesn't affect flags */
+ "sub", "r!0d,r!1d,#!2d", 2),
+ ENCODING_MAP(kThumb2MvnImmShift, 0xf06f0000, /* no setflags encoding */
+ kFmtBitBlt, 11, 8, kFmtModImm, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
+ "mvn", "r!0d, #!1n", 2),
+ ENCODING_MAP(kThumb2Sel, 0xfaa0f080,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE12 | USES_CCODES,
+ "sel", "r!0d, r!1d, r!2d", 2),
+ ENCODING_MAP(kThumb2Ubfx, 0xf3c00000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtLsb, -1, -1,
+ kFmtBWidth, 4, 0, IS_QUAD_OP | REG_DEF0_USE1,
+ "ubfx", "r!0d, r!1d, #!2d, #!3d", 2),
+ ENCODING_MAP(kThumb2Sbfx, 0xf3400000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtLsb, -1, -1,
+ kFmtBWidth, 4, 0, IS_QUAD_OP | REG_DEF0_USE1,
+ "sbfx", "r!0d, r!1d, #!2d, #!3d", 2),
+ ENCODING_MAP(kThumb2LdrRRR, 0xf8500000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD,
+ "ldr", "r!0d, [r!1d, r!2d, LSL #!3d]", 2),
+ ENCODING_MAP(kThumb2LdrhRRR, 0xf8300000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD,
+ "ldrh", "r!0d, [r!1d, r!2d, LSL #!3d]", 2),
+ ENCODING_MAP(kThumb2LdrshRRR, 0xf9300000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD,
+ "ldrsh", "r!0d, [r!1d, r!2d, LSL #!3d]", 2),
+ ENCODING_MAP(kThumb2LdrbRRR, 0xf8100000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD,
+ "ldrb", "r!0d, [r!1d, r!2d, LSL #!3d]", 2),
+ ENCODING_MAP(kThumb2LdrsbRRR, 0xf9100000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD,
+ "ldrsb", "r!0d, [r!1d, r!2d, LSL #!3d]", 2),
+ ENCODING_MAP(kThumb2StrRRR, 0xf8400000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_USE012 | IS_STORE,
+ "str", "r!0d, [r!1d, r!2d, LSL #!3d]", 2),
+ ENCODING_MAP(kThumb2StrhRRR, 0xf8200000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_USE012 | IS_STORE,
+ "strh", "r!0d, [r!1d, r!2d, LSL #!3d]", 2),
+ ENCODING_MAP(kThumb2StrbRRR, 0xf8000000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_USE012 | IS_STORE,
+ "strb", "r!0d, [r!1d, r!2d, LSL #!3d]", 2),
+ ENCODING_MAP(kThumb2LdrhRRI12, 0xf8b00000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+ "ldrh", "r!0d, [r!1d, #!2d]", 2),
+ ENCODING_MAP(kThumb2LdrshRRI12, 0xf9b00000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+ "ldrsh", "r!0d, [r!1d, #!2d]", 2),
+ ENCODING_MAP(kThumb2LdrbRRI12, 0xf8900000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+ "ldrb", "r!0d, [r!1d, #!2d]", 2),
+ ENCODING_MAP(kThumb2LdrsbRRI12, 0xf9900000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+ "ldrsb", "r!0d, [r!1d, #!2d]", 2),
+ ENCODING_MAP(kThumb2StrhRRI12, 0xf8a00000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+ "strh", "r!0d, [r!1d, #!2d]", 2),
+ ENCODING_MAP(kThumb2StrbRRI12, 0xf8800000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
+ "strb", "r!0d, [r!1d, #!2d]", 2),
+ ENCODING_MAP(kThumb2Pop, 0xe8bd0000,
+ kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_DEF_LIST0
+ | IS_LOAD, "pop", "<!0R>", 2),
+ ENCODING_MAP(kThumb2Push, 0xe8ad0000,
+ kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_USE_LIST0
+ | IS_STORE, "push", "<!0R>", 2),
+ ENCODING_MAP(kThumb2CmpRI8, 0xf1b00f00,
+ kFmtBitBlt, 19, 16, kFmtModImm, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_USE0 | SETS_CCODES,
+ "cmp", "r!0d, #!1m", 2),
+ ENCODING_MAP(kThumb2AdcRRR, 0xeb500000, /* setflags encoding */
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtShift, -1, -1,
+ IS_QUAD_OP | REG_DEF0_USE12 | SETS_CCODES,
+ "adcs", "r!0d, r!1d, r!2d!3H", 2),
+ ENCODING_MAP(kThumb2AndRRR, 0xea000000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12,
+ "and", "r!0d, r!1d, r!2d!3H", 2),
+ ENCODING_MAP(kThumb2BicRRR, 0xea200000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12,
+ "bic", "r!0d, r!1d, r!2d!3H", 2),
+ ENCODING_MAP(kThumb2CmnRR, 0xeb000000,
+ kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtShift, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "cmn", "r!0d, r!1d, shift !2d", 2),
+ ENCODING_MAP(kThumb2EorRRR, 0xea800000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12,
+ "eor", "r!0d, r!1d, r!2d!3H", 2),
+ ENCODING_MAP(kThumb2MulRRR, 0xfb00f000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "mul", "r!0d, r!1d, r!2d", 2),
+ ENCODING_MAP(kThumb2MnvRR, 0xea6f0000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift, -1, -1,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+ "mvn", "r!0d, r!1d, shift !2d", 2),
+ ENCODING_MAP(kThumb2RsubRRI8, 0xf1d00000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "rsb", "r!0d,r!1d,#!2m", 2),
+ ENCODING_MAP(kThumb2NegRR, 0xf1d00000, /* instance of rsub */
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "neg", "r!0d,r!1d", 2),
+ ENCODING_MAP(kThumb2OrrRRR, 0xea400000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12,
+ "orr", "r!0d, r!1d, r!2d!3H", 2),
+ ENCODING_MAP(kThumb2TstRR, 0xea100f00,
+ kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtShift, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_USE01 | SETS_CCODES,
+ "tst", "r!0d, r!1d, shift !2d", 2),
+ ENCODING_MAP(kThumb2LslRRR, 0xfa00f000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "lsl", "r!0d, r!1d, r!2d", 2),
+ ENCODING_MAP(kThumb2LsrRRR, 0xfa20f000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "lsr", "r!0d, r!1d, r!2d", 2),
+ ENCODING_MAP(kThumb2AsrRRR, 0xfa40f000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "asr", "r!0d, r!1d, r!2d", 2),
+ ENCODING_MAP(kThumb2RorRRR, 0xfa60f000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "ror", "r!0d, r!1d, r!2d", 2),
+ ENCODING_MAP(kThumb2LslRRI5, 0xea4f0000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift5, -1, -1,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+ "lsl", "r!0d, r!1d, #!2d", 2),
+ ENCODING_MAP(kThumb2LsrRRI5, 0xea4f0010,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift5, -1, -1,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+ "lsr", "r!0d, r!1d, #!2d", 2),
+ ENCODING_MAP(kThumb2AsrRRI5, 0xea4f0020,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift5, -1, -1,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+ "asr", "r!0d, r!1d, #!2d", 2),
+ ENCODING_MAP(kThumb2RorRRI5, 0xea4f0030,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift5, -1, -1,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+ "ror", "r!0d, r!1d, #!2d", 2),
+ ENCODING_MAP(kThumb2BicRRI8, 0xf0200000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+ "bic", "r!0d, r!1d, #!2m", 2),
+ ENCODING_MAP(kThumb2AndRRI8, 0xf0000000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+ "and", "r!0d, r!1d, #!2m", 2),
+ ENCODING_MAP(kThumb2OrrRRI8, 0xf0400000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+ "orr", "r!0d, r!1d, #!2m", 2),
+ ENCODING_MAP(kThumb2EorRRI8, 0xf0800000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
+ "eor", "r!0d, r!1d, #!2m", 2),
+ ENCODING_MAP(kThumb2AddRRI8, 0xf1100000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "adds", "r!0d, r!1d, #!2m", 2),
+ ENCODING_MAP(kThumb2AdcRRI8, 0xf1500000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES | USES_CCODES,
+ "adcs", "r!0d, r!1d, #!2m", 2),
+ ENCODING_MAP(kThumb2SubRRI8, 0xf1b00000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
+ "subs", "r!0d, r!1d, #!2m", 2),
+ ENCODING_MAP(kThumb2SbcRRI8, 0xf1700000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES | USES_CCODES,
+ "sbcs", "r!0d, r!1d, #!2m", 2),
+ ENCODING_MAP(kThumb2It, 0xbf00,
+ kFmtBitBlt, 7, 4, kFmtBitBlt, 3, 0, kFmtModImm, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | IS_IT | USES_CCODES,
+ "it:!1b", "!0c", 1),
+ ENCODING_MAP(kThumb2Fmstat, 0xeef1fa10,
+ kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, NO_OPERAND | SETS_CCODES,
+ "fmstat", "", 2),
+ ENCODING_MAP(kThumb2Vcmpd, 0xeeb40b40,
+ kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01,
+ "vcmp.f64", "!0S, !1S", 2),
+ ENCODING_MAP(kThumb2Vcmps, 0xeeb40a40,
+ kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01,
+ "vcmp.f32", "!0s, !1s", 2),
+ ENCODING_MAP(kThumb2LdrPcRel12, 0xf8df0000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0 | REG_USE_PC | IS_LOAD,
+ "ldr", "r!0d, [rpc, #!1d]", 2),
+ ENCODING_MAP(kThumb2BCond, 0xf0008000,
+ kFmtBrOffset, -1, -1, kFmtBitBlt, 25, 22, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | IS_BRANCH | USES_CCODES,
+ "b!1c", "!0t", 2),
+ ENCODING_MAP(kThumb2Vmovd_RR, 0xeeb00b40,
+ kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vmov.f64", "!0S, !1S", 2),
+ ENCODING_MAP(kThumb2Vmovs_RR, 0xeeb00a40,
+ kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vmov.f32", "!0s, !1s", 2),
+ ENCODING_MAP(kThumb2Fmrs, 0xee100a10,
+ kFmtBitBlt, 15, 12, kFmtSfp, 7, 16, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "fmrs", "r!0d, !1s", 2),
+ ENCODING_MAP(kThumb2Fmsr, 0xee000a10,
+ kFmtSfp, 7, 16, kFmtBitBlt, 15, 12, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "fmsr", "!0s, r!1d", 2),
+ ENCODING_MAP(kThumb2Fmrrd, 0xec500b10,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtDfp, 5, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF01_USE2,
+ "fmrrd", "r!0d, r!1d, !2S", 2),
+ ENCODING_MAP(kThumb2Fmdrr, 0xec400b10,
+ kFmtDfp, 5, 0, kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "fmdrr", "!0S, r!1d, r!2d", 2),
+ ENCODING_MAP(kThumb2Vabsd, 0xeeb00bc0,
+ kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vabs.f64", "!0S, !1S", 2),
+ ENCODING_MAP(kThumb2Vabss, 0xeeb00ac0,
+ kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vabs.f32", "!0s, !1s", 2),
+ ENCODING_MAP(kThumb2Vnegd, 0xeeb10b40,
+ kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vneg.f64", "!0S, !1S", 2),
+ ENCODING_MAP(kThumb2Vnegs, 0xeeb10a40,
+ kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
+ "vneg.f32", "!0s, !1s", 2),
+ ENCODING_MAP(kThumb2Vmovs_IMM8, 0xeeb00a00,
+ kFmtSfp, 22, 12, kFmtFPImm, 16, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
+ "vmov.f32", "!0s, #0x!1h", 2),
+ ENCODING_MAP(kThumb2Vmovd_IMM8, 0xeeb00b00,
+ kFmtDfp, 22, 12, kFmtFPImm, 16, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
+ "vmov.f64", "!0S, #0x!1h", 2),
+ ENCODING_MAP(kThumb2Mla, 0xfb000000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtBitBlt, 15, 12,
+ IS_QUAD_OP | REG_DEF0 | REG_USE1 | REG_USE2 | REG_USE3,
+ "mla", "r!0d, r!1d, r!2d, r!3d", 2),
+ ENCODING_MAP(kThumb2Umull, 0xfba00000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16,
+ kFmtBitBlt, 3, 0,
+ IS_QUAD_OP | REG_DEF0 | REG_DEF1 | REG_USE2 | REG_USE3,
+ "umull", "r!0d, r!1d, r!2d, r!3d", 2),
+ ENCODING_MAP(kThumb2Ldrex, 0xe8500f00,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
+ "ldrex", "r!0d, [r!1d, #!2E]", 2),
+ ENCODING_MAP(kThumb2Strex, 0xe8400000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16,
+ kFmtBitBlt, 7, 0, IS_QUAD_OP | REG_DEF0_USE12 | IS_STORE,
+ "strex", "r!0d,r!1d, [r!2d, #!2E]", 2),
+ ENCODING_MAP(kThumb2Clrex, 0xf3bf8f2f,
+ kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, NO_OPERAND,
+ "clrex", "", 2),
+ ENCODING_MAP(kThumb2Bfi, 0xf3600000,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtShift5, -1, -1,
+ kFmtBitBlt, 4, 0, IS_QUAD_OP | REG_DEF0_USE1,
+ "bfi", "r!0d,r!1d,#!2d,#!3d", 2),
+ ENCODING_MAP(kThumb2Bfc, 0xf36f0000,
+ kFmtBitBlt, 11, 8, kFmtShift5, -1, -1, kFmtBitBlt, 4, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0,
+ "bfc", "r!0d,#!1d,#!2d", 2),
+ ENCODING_MAP(kThumb2Dmb, 0xf3bf8f50,
+ kFmtBitBlt, 3, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP,
+ "dmb","#!0B",2),
+};
+
+/*
+ * The fake NOP of moving r0 to r0 actually will incur data stalls if r0 is
+ * not ready. Since r5 (rFP) is not updated often, it is less likely to
+ * generate unnecessary stall cycles.
+ */
+#define PADDING_MOV_R5_R5 0x1C2D
+
+/* Track the number of times that the code cache is patched */
+#if defined(WITH_JIT_TUNING)
+#define UPDATE_CODE_CACHE_PATCHES() (gDvmJit.codeCachePatches++)
+#else
+#define UPDATE_CODE_CACHE_PATCHES()
+#endif
+
+/* Write the numbers in the literal pool to the codegen stream */
+static void installDataContent(CompilationUnit *cUnit)
+{
+ int *dataPtr = (int *) ((char *) cUnit->baseAddr + cUnit->dataOffset);
+ ArmLIR *dataLIR = (ArmLIR *) cUnit->wordList;
+ while (dataLIR) {
+ *dataPtr++ = dataLIR->operands[0];
+ dataLIR = NEXT_LIR(dataLIR);
+ }
+}
+
+/* Returns the size of a Jit trace description */
+static int jitTraceDescriptionSize(const JitTraceDescription *desc)
+{
+ int runCount;
+ /* Trace end is always of non-meta type (ie isCode == true) */
+ for (runCount = 0; ; runCount++) {
+ if (desc->trace[runCount].frag.isCode &&
+ desc->trace[runCount].frag.runEnd)
+ break;
+ }
+ return sizeof(JitTraceDescription) + ((runCount+1) * sizeof(JitTraceRun));
+}
+
+/*
+ * Assemble the LIR into binary instruction format. Note that we may
+ * discover that pc-relative displacements may not fit the selected
+ * instruction. In those cases we will try to substitute a new code
+ * sequence or request that the trace be shortened and retried.
+ */
+static AssemblerStatus assembleInstructions(CompilationUnit *cUnit,
+ intptr_t startAddr)
+{
+ short *bufferAddr = (short *) cUnit->codeBuffer;
+ ArmLIR *lir;
+
+ for (lir = (ArmLIR *) cUnit->firstLIRInsn; lir; lir = NEXT_LIR(lir)) {
+ if (lir->opCode < 0) {
+ if ((lir->opCode == kArmPseudoPseudoAlign4) &&
+ /* 1 means padding is needed */
+ (lir->operands[0] == 1)) {
+ *bufferAddr++ = PADDING_MOV_R5_R5;
+ }
+ continue;
+ }
+
+ if (lir->isNop) {
+ continue;
+ }
+
+ if (lir->opCode == kThumbLdrPcRel ||
+ lir->opCode == kThumb2LdrPcRel12 ||
+ lir->opCode == kThumbAddPcRel ||
+ ((lir->opCode == kThumb2Vldrs) && (lir->operands[1] == rpc))) {
+ ArmLIR *lirTarget = (ArmLIR *) lir->generic.target;
+ intptr_t pc = (lir->generic.offset + 4) & ~3;
+ intptr_t target = lirTarget->generic.offset;
+ int delta = target - pc;
+ if (delta & 0x3) {
+ LOGE("PC-rel distance is not multiples of 4: %d\n", delta);
+ dvmCompilerAbort(cUnit);
+ }
+ if ((lir->opCode == kThumb2LdrPcRel12) && (delta > 4091)) {
+ return kRetryHalve;
+ } else if (delta > 1020) {
+ return kRetryHalve;
+ }
+ if (lir->opCode == kThumb2Vldrs) {
+ lir->operands[2] = delta >> 2;
+ } else {
+ lir->operands[1] = (lir->opCode == kThumb2LdrPcRel12) ?
+ delta : delta >> 2;
+ }
+ } else if (lir->opCode == kThumb2Cbnz || lir->opCode == kThumb2Cbz) {
+ ArmLIR *targetLIR = (ArmLIR *) lir->generic.target;
+ intptr_t pc = lir->generic.offset + 4;
+ intptr_t target = targetLIR->generic.offset;
+ int delta = target - pc;
+ if (delta > 126 || delta < 0) {
+ /* Convert to cmp rx,#0 / b[eq/ne] tgt pair */
+ ArmLIR *newInst = dvmCompilerNew(sizeof(ArmLIR), true);
+ /* Make new branch instruction and insert after */
+ newInst->opCode = kThumbBCond;
+ newInst->operands[0] = 0;
+ newInst->operands[1] = (lir->opCode == kThumb2Cbz) ?
+ kArmCondEq : kArmCondNe;
+ newInst->generic.target = lir->generic.target;
+ dvmCompilerSetupResourceMasks(newInst);
+ dvmCompilerInsertLIRAfter((LIR *)lir, (LIR *)newInst);
+ /* Convert the cb[n]z to a cmp rx, #0 ] */
+ lir->opCode = kThumbCmpRI8;
+ /* operand[0] is src1 in both cb[n]z & CmpRI8 */
+ lir->operands[1] = 0;
+ lir->generic.target = 0;
+ dvmCompilerSetupResourceMasks(lir);
+ return kRetryAll;
+ } else {
+ lir->operands[1] = delta >> 1;
+ }
+ } else if (lir->opCode == kThumbBCond ||
+ lir->opCode == kThumb2BCond) {
+ ArmLIR *targetLIR = (ArmLIR *) lir->generic.target;
+ intptr_t pc = lir->generic.offset + 4;
+ intptr_t target = targetLIR->generic.offset;
+ int delta = target - pc;
+ if ((lir->opCode == kThumbBCond) && (delta > 254 || delta < -256)) {
+ return kRetryHalve;
+ }
+ lir->operands[0] = delta >> 1;
+ } else if (lir->opCode == kThumbBUncond) {
+ ArmLIR *targetLIR = (ArmLIR *) lir->generic.target;
+ intptr_t pc = lir->generic.offset + 4;
+ intptr_t target = targetLIR->generic.offset;
+ int delta = target - pc;
+ if (delta > 2046 || delta < -2048) {
+ LOGE("Unconditional branch distance out of range: %d\n", delta);
+ dvmCompilerAbort(cUnit);
+ }
+ lir->operands[0] = delta >> 1;
+ } else if (lir->opCode == kThumbBlx1) {
+ assert(NEXT_LIR(lir)->opCode == kThumbBlx2);
+ /* curPC is Thumb */
+ intptr_t curPC = (startAddr + lir->generic.offset + 4) & ~3;
+ intptr_t target = lir->operands[1];
+
+ /* Match bit[1] in target with base */
+ if (curPC & 0x2) {
+ target |= 0x2;
+ }
+ int delta = target - curPC;
+ assert((delta >= -(1<<22)) && (delta <= ((1<<22)-2)));
+
+ lir->operands[0] = (delta >> 12) & 0x7ff;
+ NEXT_LIR(lir)->operands[0] = (delta>> 1) & 0x7ff;
+ }
+
+ ArmEncodingMap *encoder = &EncodingMap[lir->opCode];
+ u4 bits = encoder->skeleton;
+ int i;
+ for (i = 0; i < 4; i++) {
+ u4 operand;
+ u4 value;
+ operand = lir->operands[i];
+ switch(encoder->fieldLoc[i].kind) {
+ case kFmtUnused:
+ break;
+ case kFmtFPImm:
+ value = ((operand & 0xF0) >> 4) << encoder->fieldLoc[i].end;
+ value |= (operand & 0x0F) << encoder->fieldLoc[i].start;
+ bits |= value;
+ break;
+ case kFmtBrOffset:
+ value = ((operand & 0x80000) >> 19) << 26;
+ value |= ((operand & 0x40000) >> 18) << 11;
+ value |= ((operand & 0x20000) >> 17) << 13;
+ value |= ((operand & 0x1f800) >> 11) << 16;
+ value |= (operand & 0x007ff);
+ bits |= value;
+ break;
+ case kFmtShift5:
+ value = ((operand & 0x1c) >> 2) << 12;
+ value |= (operand & 0x03) << 6;
+ bits |= value;
+ break;
+ case kFmtShift:
+ value = ((operand & 0x70) >> 4) << 12;
+ value |= (operand & 0x0f) << 4;
+ bits |= value;
+ break;
+ case kFmtBWidth:
+ value = operand - 1;
+ bits |= value;
+ break;
+ case kFmtLsb:
+ value = ((operand & 0x1c) >> 2) << 12;
+ value |= (operand & 0x03) << 6;
+ bits |= value;
+ break;
+ case kFmtImm6:
+ value = ((operand & 0x20) >> 5) << 9;
+ value |= (operand & 0x1f) << 3;
+ bits |= value;
+ break;
+ case kFmtBitBlt:
+ value = (operand << encoder->fieldLoc[i].start) &
+ ((1 << (encoder->fieldLoc[i].end + 1)) - 1);
+ bits |= value;
+ break;
+ case kFmtDfp: {
+ assert(DOUBLEREG(operand));
+ assert((operand & 0x1) == 0);
+ int regName = (operand & FP_REG_MASK) >> 1;
+ /* Snag the 1-bit slice and position it */
+ value = ((regName & 0x10) >> 4) <<
+ encoder->fieldLoc[i].end;
+ /* Extract and position the 4-bit slice */
+ value |= (regName & 0x0f) <<
+ encoder->fieldLoc[i].start;
+ bits |= value;
+ break;
+ }
+ case kFmtSfp:
+ assert(SINGLEREG(operand));
+ /* Snag the 1-bit slice and position it */
+ value = (operand & 0x1) <<
+ encoder->fieldLoc[i].end;
+ /* Extract and position the 4-bit slice */
+ value |= ((operand & 0x1e) >> 1) <<
+ encoder->fieldLoc[i].start;
+ bits |= value;
+ break;
+ case kFmtImm12:
+ case kFmtModImm:
+ value = ((operand & 0x800) >> 11) << 26;
+ value |= ((operand & 0x700) >> 8) << 12;
+ value |= operand & 0x0ff;
+ bits |= value;
+ break;
+ case kFmtImm16:
+ value = ((operand & 0x0800) >> 11) << 26;
+ value |= ((operand & 0xf000) >> 12) << 16;
+ value |= ((operand & 0x0700) >> 8) << 12;
+ value |= operand & 0x0ff;
+ bits |= value;
+ break;
+ default:
+ assert(0);
+ }
+ }
+ if (encoder->size == 2) {
+ *bufferAddr++ = (bits >> 16) & 0xffff;
+ }
+ *bufferAddr++ = bits & 0xffff;
+ }
+ return kSuccess;
+}
+
+#if defined(SIGNATURE_BREAKPOINT)
+/* Inspect the assembled instruction stream to find potential matches */
+static void matchSignatureBreakpoint(const CompilationUnit *cUnit,
+ unsigned int size)
+{
+ unsigned int i, j;
+ u4 *ptr = (u4 *) cUnit->codeBuffer;
+
+ for (i = 0; i < size - gDvmJit.signatureBreakpointSize + 1; i++) {
+ if (ptr[i] == gDvmJit.signatureBreakpoint[0]) {
+ for (j = 1; j < gDvmJit.signatureBreakpointSize; j++) {
+ if (ptr[i+j] != gDvmJit.signatureBreakpoint[j]) {
+ break;
+ }
+ }
+ if (j == gDvmJit.signatureBreakpointSize) {
+ LOGD("Signature match starting from offset %#x (%d words)",
+ i*4, gDvmJit.signatureBreakpointSize);
+ int descSize = jitTraceDescriptionSize(cUnit->traceDesc);
+ JitTraceDescription *newCopy =
+ (JitTraceDescription *) malloc(descSize);
+ memcpy(newCopy, cUnit->traceDesc, descSize);
+ dvmCompilerWorkEnqueue(NULL, kWorkOrderTraceDebug, newCopy);
+ break;
+ }
+ }
+ }
+}
+#endif
+
+/*
+ * Translation layout in the code cache. Note that the codeAddress pointer
+ * in JitTable will point directly to the code body (field codeAddress). The
+ * chain cell offset codeAddress - 2, and (if present) executionCount is at
+ * codeAddress - 6.
+ *
+ * +----------------------------+
+ * | Execution count | -> [Optional] 4 bytes
+ * +----------------------------+
+ * +--| Offset to chain cell counts| -> 2 bytes
+ * | +----------------------------+
+ * | | Code body | -> Start address for translation
+ * | | | variable in 2-byte chunks
+ * | . . (JitTable's codeAddress points here)
+ * | . .
+ * | | |
+ * | +----------------------------+
+ * | | Chaining Cells | -> 12/16 bytes each, must be 4 byte aligned
+ * | . .
+ * | . .
+ * | | |
+ * | +----------------------------+
+ * | | Gap for large switch stmt | -> # cases >= MAX_CHAINED_SWITCH_CASES
+ * | +----------------------------+
+ * +->| Chaining cell counts | -> 8 bytes, chain cell counts by type
+ * +----------------------------+
+ * | Trace description | -> variable sized
+ * . .
+ * | |
+ * +----------------------------+
+ * | Literal pool | -> 4-byte aligned, variable size
+ * . .
+ * . .
+ * | |
+ * +----------------------------+
+ *
+ * Go over each instruction in the list and calculate the offset from the top
+ * before sending them off to the assembler. If out-of-range branch distance is
+ * seen rearrange the instructions a bit to correct it.
+ */
+void dvmCompilerAssembleLIR(CompilationUnit *cUnit, JitTranslationInfo *info)
+{
+ LIR *lir;
+ ArmLIR *armLIR;
+ int offset = 0;
+ int i;
+ ChainCellCounts chainCellCounts;
+ int descSize =
+ cUnit->wholeMethod ? 0 : jitTraceDescriptionSize(cUnit->traceDesc);
+ int chainingCellGap;
+
+ info->instructionSet = cUnit->instructionSet;
+
+ /* Beginning offset needs to allow space for chain cell offset */
+ for (armLIR = (ArmLIR *) cUnit->firstLIRInsn;
+ armLIR;
+ armLIR = NEXT_LIR(armLIR)) {
+ armLIR->generic.offset = offset;
+ if (armLIR->opCode >= 0 && !armLIR->isNop) {
+ armLIR->size = EncodingMap[armLIR->opCode].size * 2;
+ offset += armLIR->size;
+ } else if (armLIR->opCode == kArmPseudoPseudoAlign4) {
+ if (offset & 0x2) {
+ offset += 2;
+ armLIR->operands[0] = 1;
+ } else {
+ armLIR->operands[0] = 0;
+ }
+ }
+ /* Pseudo opcodes don't consume space */
+ }
+
+ /* Const values have to be word aligned */
+ offset = (offset + 3) & ~3;
+
+ /*
+ * Get the gap (# of u4) between the offset of chaining cell count and
+ * the bottom of real chaining cells. If the translation has chaining
+ * cells, the gap is guaranteed to be multiples of 4.
+ */
+ chainingCellGap = (offset - cUnit->chainingCellBottom->offset) >> 2;
+
+ /* Add space for chain cell counts & trace description */
+ u4 chainCellOffset = offset;
+ ArmLIR *chainCellOffsetLIR = (ArmLIR *) cUnit->chainCellOffsetLIR;
+ assert(chainCellOffsetLIR);
+ assert(chainCellOffset < 0x10000);
+ assert(chainCellOffsetLIR->opCode == kArm16BitData &&
+ chainCellOffsetLIR->operands[0] == CHAIN_CELL_OFFSET_TAG);
+
+ /*
+ * Replace the CHAIN_CELL_OFFSET_TAG with the real value. If trace
+ * profiling is enabled, subtract 4 (occupied by the counter word) from
+ * the absolute offset as the value stored in chainCellOffsetLIR is the
+ * delta from &chainCellOffsetLIR to &ChainCellCounts.
+ */
+ chainCellOffsetLIR->operands[0] =
+ gDvmJit.profile ? (chainCellOffset - 4) : chainCellOffset;
+
+ offset += sizeof(chainCellCounts) + descSize;
+
+ assert((offset & 0x3) == 0); /* Should still be word aligned */
+
+ /* Set up offsets for literals */
+ cUnit->dataOffset = offset;
+
+ for (lir = cUnit->wordList; lir; lir = lir->next) {
+ lir->offset = offset;
+ offset += 4;
+ }
+
+ cUnit->totalSize = offset;
+
+ if (gDvmJit.codeCacheByteUsed + cUnit->totalSize > gDvmJit.codeCacheSize) {
+ gDvmJit.codeCacheFull = true;
+ cUnit->baseAddr = NULL;
+ return;
+ }
+
+ /* Allocate enough space for the code block */
+ cUnit->codeBuffer = dvmCompilerNew(chainCellOffset, true);
+ if (cUnit->codeBuffer == NULL) {
+ LOGE("Code buffer allocation failure\n");
+ cUnit->baseAddr = NULL;
+ return;
+ }
+
+ /*
+ * Attempt to assemble the trace. Note that assembleInstructions
+ * may rewrite the code sequence and request a retry.
+ */
+ cUnit->assemblerStatus = assembleInstructions(cUnit,
+ (intptr_t) gDvmJit.codeCache + gDvmJit.codeCacheByteUsed);
+
+ switch(cUnit->assemblerStatus) {
+ case kSuccess:
+ break;
+ case kRetryAll:
+ if (cUnit->assemblerRetries < MAX_ASSEMBLER_RETRIES) {
+ /* Restore pristine chain cell marker on retry */
+ chainCellOffsetLIR->operands[0] = CHAIN_CELL_OFFSET_TAG;
+ return;
+ }
+ /* Too many retries - reset and try cutting the trace in half */
+ cUnit->assemblerRetries = 0;
+ cUnit->assemblerStatus = kRetryHalve;
+ return;
+ case kRetryHalve:
+ return;
+ default:
+ LOGE("Unexpected assembler status: %d", cUnit->assemblerStatus);
+ dvmAbort();
+ }
+
+#if defined(SIGNATURE_BREAKPOINT)
+ if (info->discardResult == false && gDvmJit.signatureBreakpoint != NULL &&
+ chainCellOffset/4 >= gDvmJit.signatureBreakpointSize) {
+ matchSignatureBreakpoint(cUnit, chainCellOffset/4);
+ }
+#endif
+
+ /* Don't go all the way if the goal is just to get the verbose output */
+ if (info->discardResult) return;
+
+ cUnit->baseAddr = (char *) gDvmJit.codeCache + gDvmJit.codeCacheByteUsed;
+ gDvmJit.codeCacheByteUsed += offset;
+
+ UNPROTECT_CODE_CACHE(cUnit->baseAddr, offset);
+
+ /* Install the code block */
+ memcpy((char*)cUnit->baseAddr, cUnit->codeBuffer, chainCellOffset);
+ gDvmJit.numCompilations++;
+
+ /* Install the chaining cell counts */
+ for (i=0; i< kChainingCellGap; i++) {
+ chainCellCounts.u.count[i] = cUnit->numChainingCells[i];
+ }
+
+ /* Set the gap number in the chaining cell count structure */
+ chainCellCounts.u.count[kChainingCellGap] = chainingCellGap;
+
+ memcpy((char*)cUnit->baseAddr + chainCellOffset, &chainCellCounts,
+ sizeof(chainCellCounts));
+
+ /* Install the trace description */
+ memcpy((char*)cUnit->baseAddr + chainCellOffset + sizeof(chainCellCounts),
+ cUnit->traceDesc, descSize);
+
+ /* Write the literals directly into the code cache */
+ installDataContent(cUnit);
+
+ /* Flush dcache and invalidate the icache to maintain coherence */
+ cacheflush((long)cUnit->baseAddr,
+ (long)((char *) cUnit->baseAddr + offset), 0);
+ UPDATE_CODE_CACHE_PATCHES();
+
+ PROTECT_CODE_CACHE(cUnit->baseAddr, offset);
+
+ /* Record code entry point and instruction set */
+ info->codeAddress = (char*)cUnit->baseAddr + cUnit->headerSize;
+ /* If applicable, mark low bit to denote thumb */
+ if (info->instructionSet != DALVIK_JIT_ARM)
+ info->codeAddress = (char*)info->codeAddress + 1;
+}
+
+/*
+ * Returns the skeleton bit pattern associated with an opcode. All
+ * variable fields are zeroed.
+ */
+static u4 getSkeleton(ArmOpCode op)
+{
+ return EncodingMap[op].skeleton;
+}
+
+static u4 assembleChainingBranch(int branchOffset, bool thumbTarget)
+{
+ u4 thumb1, thumb2;
+
+ if (!thumbTarget) {
+ thumb1 = (getSkeleton(kThumbBlx1) | ((branchOffset>>12) & 0x7ff));
+ thumb2 = (getSkeleton(kThumbBlx2) | ((branchOffset>> 1) & 0x7ff));
+ } else if ((branchOffset < -2048) | (branchOffset > 2046)) {
+ thumb1 = (getSkeleton(kThumbBl1) | ((branchOffset>>12) & 0x7ff));
+ thumb2 = (getSkeleton(kThumbBl2) | ((branchOffset>> 1) & 0x7ff));
+ } else {
+ thumb1 = (getSkeleton(kThumbBUncond) | ((branchOffset>> 1) & 0x7ff));
+ thumb2 = getSkeleton(kThumbOrr); /* nop -> or r0, r0 */
+ }
+
+ return thumb2<<16 | thumb1;
+}
+
+/*
+ * Perform translation chain operation.
+ * For ARM, we'll use a pair of thumb instructions to generate
+ * an unconditional chaining branch of up to 4MB in distance.
+ * Use a BL, because the generic "interpret" translation needs
+ * the link register to find the dalvik pc of teh target.
+ * 111HHooooooooooo
+ * Where HH is 10 for the 1st inst, and 11 for the second and
+ * the "o" field is each instruction's 11-bit contribution to the
+ * 22-bit branch offset.
+ * If the target is nearby, use a single-instruction bl.
+ * If one or more threads is suspended, don't chain.
+ */
+void* dvmJitChain(void* tgtAddr, u4* branchAddr)
+{
+ int baseAddr = (u4) branchAddr + 4;
+ int branchOffset = (int) tgtAddr - baseAddr;
+ u4 newInst;
+ bool thumbTarget;
+
+ /*
+ * Only chain translations when there is no urge to ask all threads to
+ * suspend themselves via the interpreter.
+ */
+ if ((gDvmJit.pProfTable != NULL) && (gDvm.sumThreadSuspendCount == 0) &&
+ (gDvmJit.codeCacheFull == false)) {
+ assert((branchOffset >= -(1<<22)) && (branchOffset <= ((1<<22)-2)));
+
+ gDvmJit.translationChains++;
+
+ COMPILER_TRACE_CHAINING(
+ LOGD("Jit Runtime: chaining 0x%x to 0x%x\n",
+ (int) branchAddr, (int) tgtAddr & -2));
+
+ /*
+ * NOTE: normally, all translations are Thumb[2] mode, with
+ * a single exception: the default TEMPLATE_INTERPRET
+ * pseudo-translation. If the need ever arises to
+ * mix Arm & Thumb[2] translations, the following code should be
+ * generalized.
+ */
+ thumbTarget = (tgtAddr != dvmCompilerGetInterpretTemplate());
+
+ newInst = assembleChainingBranch(branchOffset, thumbTarget);
+
+ /*
+ * The second half-word instruction of the chaining cell must
+ * either be a nop (which represents initial state), or is the
+ * same exact branch halfword that we are trying to install.
+ */
+ assert( ((*branchAddr >> 16) == getSkeleton(kThumbOrr)) ||
+ ((*branchAddr >> 16) == (newInst >> 16)));
+
+ UNPROTECT_CODE_CACHE(branchAddr, sizeof(*branchAddr));
+
+ *branchAddr = newInst;
+ cacheflush((long)branchAddr, (long)branchAddr + 4, 0);
+ UPDATE_CODE_CACHE_PATCHES();
+
+ PROTECT_CODE_CACHE(branchAddr, sizeof(*branchAddr));
+
+ gDvmJit.hasNewChain = true;
+ }
+
+ return tgtAddr;
+}
+
+#if !defined(WITH_SELF_VERIFICATION)
+/*
+ * Attempt to enqueue a work order to patch an inline cache for a predicted
+ * chaining cell for virtual/interface calls.
+ */
+static void inlineCachePatchEnqueue(PredictedChainingCell *cellAddr,
+ PredictedChainingCell *newContent)
+{
+ /*
+ * Make sure only one thread gets here since updating the cell (ie fast
+ * path and queueing the request (ie the queued path) have to be done
+ * in an atomic fashion.
+ */
+ dvmLockMutex(&gDvmJit.compilerICPatchLock);
+
+ /* Fast path for uninitialized chaining cell */
+ if (cellAddr->clazz == NULL &&
+ cellAddr->branch == PREDICTED_CHAIN_BX_PAIR_INIT) {
+
+ UNPROTECT_CODE_CACHE(cellAddr, sizeof(*cellAddr));
+
+ cellAddr->method = newContent->method;
+ cellAddr->branch = newContent->branch;
+ /*
+ * The update order matters - make sure clazz is updated last since it
+ * will bring the uninitialized chaining cell to life.
+ */
+ android_atomic_release_store((int32_t)newContent->clazz,
+ (void*) &cellAddr->clazz);
+ cacheflush((intptr_t) cellAddr, (intptr_t) (cellAddr+1), 0);
+ UPDATE_CODE_CACHE_PATCHES();
+
+ PROTECT_CODE_CACHE(cellAddr, sizeof(*cellAddr));
+
+#if defined(WITH_JIT_TUNING)
+ gDvmJit.icPatchInit++;
+#endif
+ /* Check if this is a frequently missed clazz */
+ } else if (cellAddr->stagedClazz != newContent->clazz) {
+ /* Not proven to be frequent yet - build up the filter cache */
+ UNPROTECT_CODE_CACHE(cellAddr, sizeof(*cellAddr));
+
+ cellAddr->stagedClazz = newContent->clazz;
+
+ UPDATE_CODE_CACHE_PATCHES();
+ PROTECT_CODE_CACHE(cellAddr, sizeof(*cellAddr));
+
+#if defined(WITH_JIT_TUNING)
+ gDvmJit.icPatchRejected++;
+#endif
+ /*
+ * Different classes but same method implementation - it is safe to just
+ * patch the class value without the need to stop the world.
+ */
+ } else if (cellAddr->method == newContent->method) {
+ UNPROTECT_CODE_CACHE(cellAddr, sizeof(*cellAddr));
+
+ cellAddr->clazz = newContent->clazz;
+ /* No need to flush the cache here since the branch is not patched */
+ UPDATE_CODE_CACHE_PATCHES();
+
+ PROTECT_CODE_CACHE(cellAddr, sizeof(*cellAddr));
+
+#if defined(WITH_JIT_TUNING)
+ gDvmJit.icPatchLockFree++;
+#endif
+ /*
+ * Cannot patch the chaining cell inline - queue it until the next safe
+ * point.
+ */
+ } else if (gDvmJit.compilerICPatchIndex < COMPILER_IC_PATCH_QUEUE_SIZE) {
+ int index = gDvmJit.compilerICPatchIndex++;
+ gDvmJit.compilerICPatchQueue[index].cellAddr = cellAddr;
+ gDvmJit.compilerICPatchQueue[index].cellContent = *newContent;
+#if defined(WITH_JIT_TUNING)
+ gDvmJit.icPatchQueued++;
+#endif
+ } else {
+ /* Queue is full - just drop this patch request */
+#if defined(WITH_JIT_TUNING)
+ gDvmJit.icPatchDropped++;
+#endif
+ }
+
+ dvmUnlockMutex(&gDvmJit.compilerICPatchLock);
+}
+#endif
+
+/*
+ * This method is called from the invoke templates for virtual and interface
+ * methods to speculatively setup a chain to the callee. The templates are
+ * written in assembly and have setup method, cell, and clazz at r0, r2, and
+ * r3 respectively, so there is a unused argument in the list. Upon return one
+ * of the following three results may happen:
+ * 1) Chain is not setup because the callee is native. Reset the rechain
+ * count to a big number so that it will take a long time before the next
+ * rechain attempt to happen.
+ * 2) Chain is not setup because the callee has not been created yet. Reset
+ * the rechain count to a small number and retry in the near future.
+ * 3) Ask all other threads to stop before patching this chaining cell.
+ * This is required because another thread may have passed the class check
+ * but hasn't reached the chaining cell yet to follow the chain. If we
+ * patch the content before halting the other thread, there could be a
+ * small window for race conditions to happen that it may follow the new
+ * but wrong chain to invoke a different method.
+ */
+const Method *dvmJitToPatchPredictedChain(const Method *method,
+ InterpState *interpState,
+ PredictedChainingCell *cell,
+ const ClassObject *clazz)
+{
+ int newRechainCount = PREDICTED_CHAIN_COUNTER_RECHAIN;
+#if defined(WITH_SELF_VERIFICATION)
+ newRechainCount = PREDICTED_CHAIN_COUNTER_AVOID;
+ goto done;
+#else
+ if (dvmIsNativeMethod(method)) {
+ UNPROTECT_CODE_CACHE(cell, sizeof(*cell));
+
+ /*
+ * Put a non-zero/bogus value in the clazz field so that it won't
+ * trigger immediate patching and will continue to fail to match with
+ * a real clazz pointer.
+ */
+ cell->clazz = (void *) PREDICTED_CHAIN_FAKE_CLAZZ;
+
+ UPDATE_CODE_CACHE_PATCHES();
+ PROTECT_CODE_CACHE(cell, sizeof(*cell));
+ goto done;
+ }
+ int tgtAddr = (int) dvmJitGetCodeAddr(method->insns);
+
+ /*
+ * Compilation not made yet for the callee. Reset the counter to a small
+ * value and come back to check soon.
+ */
+ if ((tgtAddr == 0) ||
+ ((void*)tgtAddr == dvmCompilerGetInterpretTemplate())) {
+ COMPILER_TRACE_CHAINING(
+ LOGD("Jit Runtime: predicted chain %p to method %s%s delayed",
+ cell, method->clazz->descriptor, method->name));
+ goto done;
+ }
+
+ PredictedChainingCell newCell;
+
+ if (cell->clazz == NULL) {
+ newRechainCount = interpState->icRechainCount;
+ }
+
+ int baseAddr = (int) cell + 4; // PC is cur_addr + 4
+ int branchOffset = tgtAddr - baseAddr;
+
+ newCell.branch = assembleChainingBranch(branchOffset, true);
+ newCell.clazz = clazz;
+ newCell.method = method;
+
+ /*
+ * Enter the work order to the queue and the chaining cell will be patched
+ * the next time a safe point is entered.
+ *
+ * If the enqueuing fails reset the rechain count to a normal value so that
+ * it won't get indefinitely delayed.
+ */
+ inlineCachePatchEnqueue(cell, &newCell);
+#endif
+done:
+ interpState->icRechainCount = newRechainCount;
+ return method;
+}
+
+/*
+ * Patch the inline cache content based on the content passed from the work
+ * order.
+ */
+void dvmCompilerPatchInlineCache(void)
+{
+ int i;
+ PredictedChainingCell *minAddr, *maxAddr;
+
+ /* Nothing to be done */
+ if (gDvmJit.compilerICPatchIndex == 0) return;
+
+ /*
+ * Since all threads are already stopped we don't really need to acquire
+ * the lock. But race condition can be easily introduced in the future w/o
+ * paying attention so we still acquire the lock here.
+ */
+ dvmLockMutex(&gDvmJit.compilerICPatchLock);
+
+ UNPROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
+
+ //LOGD("Number of IC patch work orders: %d", gDvmJit.compilerICPatchIndex);
+
+ /* Initialize the min/max address range */
+ minAddr = (PredictedChainingCell *)
+ ((char *) gDvmJit.codeCache + gDvmJit.codeCacheSize);
+ maxAddr = (PredictedChainingCell *) gDvmJit.codeCache;
+
+ for (i = 0; i < gDvmJit.compilerICPatchIndex; i++) {
+ PredictedChainingCell *cellAddr =
+ gDvmJit.compilerICPatchQueue[i].cellAddr;
+ PredictedChainingCell *cellContent =
+ &gDvmJit.compilerICPatchQueue[i].cellContent;
+
+ COMPILER_TRACE_CHAINING(
+ LOGD("Jit Runtime: predicted chain %p from %s to %s (%s) "
+ "patched",
+ cellAddr,
+ cellAddr->clazz->descriptor,
+ cellContent->clazz->descriptor,
+ cellContent->method->name));
+
+ /* Patch the chaining cell */
+ *cellAddr = *cellContent;
+ minAddr = (cellAddr < minAddr) ? cellAddr : minAddr;
+ maxAddr = (cellAddr > maxAddr) ? cellAddr : maxAddr;
+ }
+
+ /* Then synchronize the I/D cache */
+ cacheflush((long) minAddr, (long) (maxAddr+1), 0);
+ UPDATE_CODE_CACHE_PATCHES();
+
+ PROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
+
+ gDvmJit.compilerICPatchIndex = 0;
+ dvmUnlockMutex(&gDvmJit.compilerICPatchLock);
+}
+
+/*
+ * Unchain a trace given the starting address of the translation
+ * in the code cache. Refer to the diagram in dvmCompilerAssembleLIR.
+ * Returns the address following the last cell unchained. Note that
+ * the incoming codeAddr is a thumb code address, and therefore has
+ * the low bit set.
+ */
+u4* dvmJitUnchain(void* codeAddr)
+{
+ u2* pChainCellOffset = (u2*)((char*)codeAddr - 3);
+ u2 chainCellOffset = *pChainCellOffset;
+ ChainCellCounts *pChainCellCounts =
+ (ChainCellCounts*)((char*)codeAddr + chainCellOffset - 3);
+ int cellSize;
+ u4* pChainCells;
+ u4* pStart;
+ u4 newInst;
+ int i,j;
+ PredictedChainingCell *predChainCell;
+
+ /* Get total count of chain cells */
+ for (i = 0, cellSize = 0; i < kChainingCellGap; i++) {
+ if (i != kChainingCellInvokePredicted) {
+ cellSize += pChainCellCounts->u.count[i] * (CHAIN_CELL_NORMAL_SIZE >> 2);
+ } else {
+ cellSize += pChainCellCounts->u.count[i] *
+ (CHAIN_CELL_PREDICTED_SIZE >> 2);
+ }
+ }
+
+ if (cellSize == 0)
+ return (u4 *) pChainCellCounts;
+
+ /* Locate the beginning of the chain cell region */
+ pStart = pChainCells = ((u4 *) pChainCellCounts) - cellSize -
+ pChainCellCounts->u.count[kChainingCellGap];
+
+ /* The cells are sorted in order - walk through them and reset */
+ for (i = 0; i < kChainingCellGap; i++) {
+ int elemSize = CHAIN_CELL_NORMAL_SIZE >> 2; /* In 32-bit words */
+ if (i == kChainingCellInvokePredicted) {
+ elemSize = CHAIN_CELL_PREDICTED_SIZE >> 2;
+ }
+
+ for (j = 0; j < pChainCellCounts->u.count[i]; j++) {
+ switch(i) {
+ case kChainingCellNormal:
+ case kChainingCellHot:
+ case kChainingCellInvokeSingleton:
+ case kChainingCellBackwardBranch:
+ /*
+ * Replace the 1st half-word of the cell with an
+ * unconditional branch, leaving the 2nd half-word
+ * untouched. This avoids problems with a thread
+ * that is suspended between the two halves when
+ * this unchaining takes place.
+ */
+ newInst = *pChainCells;
+ newInst &= 0xFFFF0000;
+ newInst |= getSkeleton(kThumbBUncond); /* b offset is 0 */
+ *pChainCells = newInst;
+ break;
+ case kChainingCellInvokePredicted:
+ predChainCell = (PredictedChainingCell *) pChainCells;
+ /*
+ * There could be a race on another mutator thread to use
+ * this particular predicted cell and the check has passed
+ * the clazz comparison. So we cannot safely wipe the
+ * method and branch but it is safe to clear the clazz,
+ * which serves as the key.
+ */
+ predChainCell->clazz = PREDICTED_CHAIN_CLAZZ_INIT;
+ break;
+ default:
+ LOGE("Unexpected chaining type: %d", i);
+ dvmAbort(); // dvmAbort OK here - can't safely recover
+ }
+ COMPILER_TRACE_CHAINING(
+ LOGD("Jit Runtime: unchaining 0x%x", (int)pChainCells));
+ pChainCells += elemSize; /* Advance by a fixed number of words */
+ }
+ }
+ return pChainCells;
+}
+
+/* Unchain all translation in the cache. */
+void dvmJitUnchainAll()
+{
+ u4* lowAddress = NULL;
+ u4* highAddress = NULL;
+ unsigned int i;
+ if (gDvmJit.pJitEntryTable != NULL) {
+ COMPILER_TRACE_CHAINING(LOGD("Jit Runtime: unchaining all"));
+ dvmLockMutex(&gDvmJit.tableLock);
+
+ UNPROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
+
+ for (i = 0; i < gDvmJit.jitTableSize; i++) {
+ if (gDvmJit.pJitEntryTable[i].dPC &&
+ gDvmJit.pJitEntryTable[i].codeAddress &&
+ (gDvmJit.pJitEntryTable[i].codeAddress !=
+ dvmCompilerGetInterpretTemplate())) {
+ u4* lastAddress;
+ lastAddress =
+ dvmJitUnchain(gDvmJit.pJitEntryTable[i].codeAddress);
+ if (lowAddress == NULL ||
+ (u4*)gDvmJit.pJitEntryTable[i].codeAddress < lowAddress)
+ lowAddress = lastAddress;
+ if (lastAddress > highAddress)
+ highAddress = lastAddress;
+ }
+ }
+ cacheflush((long)lowAddress, (long)highAddress, 0);
+ UPDATE_CODE_CACHE_PATCHES();
+
+ PROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
+
+ dvmUnlockMutex(&gDvmJit.tableLock);
+ gDvmJit.translationChains = 0;
+ }
+ gDvmJit.hasNewChain = false;
+}
+
+typedef struct jitProfileAddrToLine {
+ u4 lineNum;
+ u4 bytecodeOffset;
+} jitProfileAddrToLine;
+
+
+/* Callback function to track the bytecode offset/line number relationiship */
+static int addrToLineCb (void *cnxt, u4 bytecodeOffset, u4 lineNum)
+{
+ jitProfileAddrToLine *addrToLine = (jitProfileAddrToLine *) cnxt;
+
+ /* Best match so far for this offset */
+ if (addrToLine->bytecodeOffset >= bytecodeOffset) {
+ addrToLine->lineNum = lineNum;
+ }
+ return 0;
+}
+
+static char *getTraceBase(const JitEntry *p)
+{
+ return (char*)p->codeAddress -
+ (6 + (p->u.info.instructionSet == DALVIK_JIT_ARM ? 0 : 1));
+}
+
+/* Dumps profile info for a single trace */
+static int dumpTraceProfile(JitEntry *p, bool silent, bool reset,
+ unsigned long sum)
+{
+ ChainCellCounts* pCellCounts;
+ char* traceBase;
+ u4* pExecutionCount;
+ u4 executionCount;
+ u2* pCellOffset;
+ JitTraceDescription *desc;
+ const Method* method;
+ int idx;
+
+ traceBase = getTraceBase(p);
+
+ if (p->codeAddress == NULL) {
+ if (!silent)
+ LOGD("TRACEPROFILE 0x%08x 0 NULL 0 0", (int)traceBase);
+ return 0;
+ }
+ if (p->codeAddress == dvmCompilerGetInterpretTemplate()) {
+ if (!silent)
+ LOGD("TRACEPROFILE 0x%08x 0 INTERPRET_ONLY 0 0", (int)traceBase);
+ return 0;
+ }
+
+ pExecutionCount = (u4*) (traceBase);
+ executionCount = *pExecutionCount;
+ if (reset) {
+ *pExecutionCount =0;
+ }
+ if (silent) {
+ return executionCount;
+ }
+ pCellOffset = (u2*) (traceBase + 4);
+ pCellCounts = (ChainCellCounts*) ((char *)pCellOffset + *pCellOffset);
+ desc = (JitTraceDescription*) ((char*)pCellCounts + sizeof(*pCellCounts));
+ method = desc->method;
+ char *methodDesc = dexProtoCopyMethodDescriptor(&method->prototype);
+ jitProfileAddrToLine addrToLine = {0, desc->trace[0].frag.startOffset};
+
+ /*
+ * We may end up decoding the debug information for the same method
+ * multiple times, but the tradeoff is we don't need to allocate extra
+ * space to store the addr/line mapping. Since this is a debugging feature
+ * and done infrequently so the slower but simpler mechanism should work
+ * just fine.
+ */
+ dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile,
+ dvmGetMethodCode(method),
+ method->clazz->descriptor,
+ method->prototype.protoIdx,
+ method->accessFlags,
+ addrToLineCb, NULL, &addrToLine);
+
+ LOGD("TRACEPROFILE 0x%08x % 10d %5.2f%% [%#x(+%d), %d] %s%s;%s",
+ (int)traceBase,
+ executionCount,
+ ((float ) executionCount) / sum * 100.0,
+ desc->trace[0].frag.startOffset,
+ desc->trace[0].frag.numInsts,
+ addrToLine.lineNum,
+ method->clazz->descriptor, method->name, methodDesc);
+ free(methodDesc);
+
+ /* Find the last fragment (ie runEnd is set) */
+ for (idx = 0;
+ desc->trace[idx].frag.isCode && !desc->trace[idx].frag.runEnd;
+ idx++) {
+ }
+
+ /*
+ * runEnd must comes with a JitCodeDesc frag. If isCode is false it must
+ * be a meta info field (only used by callsite info for now).
+ */
+ if (!desc->trace[idx].frag.isCode) {
+ const Method *method = desc->trace[idx+1].meta;
+ char *methodDesc = dexProtoCopyMethodDescriptor(&method->prototype);
+ /* Print the callee info in the trace */
+ LOGD(" -> %s%s;%s", method->clazz->descriptor, method->name,
+ methodDesc);
+ }
+
+ return executionCount;
+}
+
+/* Create a copy of the trace descriptor of an existing compilation */
+JitTraceDescription *dvmCopyTraceDescriptor(const u2 *pc,
+ const JitEntry *knownEntry)
+{
+ const JitEntry *jitEntry = knownEntry ? knownEntry : dvmFindJitEntry(pc);
+ if (jitEntry == NULL) return NULL;
+
+ /* Find out the startint point */
+ char *traceBase = getTraceBase(jitEntry);
+
+ /* Then find out the starting point of the chaining cell */
+ u2 *pCellOffset = (u2*) (traceBase + 4);
+ ChainCellCounts *pCellCounts =
+ (ChainCellCounts*) ((char *)pCellOffset + *pCellOffset);
+
+ /* From there we can find out the starting point of the trace descriptor */
+ JitTraceDescription *desc =
+ (JitTraceDescription*) ((char*)pCellCounts + sizeof(*pCellCounts));
+
+ /* Now make a copy and return */
+ int descSize = jitTraceDescriptionSize(desc);
+ JitTraceDescription *newCopy = (JitTraceDescription *) malloc(descSize);
+ memcpy(newCopy, desc, descSize);
+ return newCopy;
+}
+
+/* Handy function to retrieve the profile count */
+static inline int getProfileCount(const JitEntry *entry)
+{
+ if (entry->dPC == 0 || entry->codeAddress == 0 ||
+ entry->codeAddress == dvmCompilerGetInterpretTemplate())
+ return 0;
+
+ u4 *pExecutionCount = (u4 *) getTraceBase(entry);
+
+ return *pExecutionCount;
+}
+
+
+/* qsort callback function */
+static int sortTraceProfileCount(const void *entry1, const void *entry2)
+{
+ const JitEntry *jitEntry1 = entry1;
+ const JitEntry *jitEntry2 = entry2;
+
+ int count1 = getProfileCount(jitEntry1);
+ int count2 = getProfileCount(jitEntry2);
+ return (count1 == count2) ? 0 : ((count1 > count2) ? -1 : 1);
+}
+
+/* Sort the trace profile counts and dump them */
+void dvmCompilerSortAndPrintTraceProfiles()
+{
+ JitEntry *sortedEntries;
+ int numTraces = 0;
+ unsigned long sum = 0;
+ unsigned int i;
+
+ /* Make sure that the table is not changing */
+ dvmLockMutex(&gDvmJit.tableLock);
+
+ /* Sort the entries by descending order */
+ sortedEntries = malloc(sizeof(JitEntry) * gDvmJit.jitTableSize);
+ if (sortedEntries == NULL)
+ goto done;
+ memcpy(sortedEntries, gDvmJit.pJitEntryTable,
+ sizeof(JitEntry) * gDvmJit.jitTableSize);
+ qsort(sortedEntries, gDvmJit.jitTableSize, sizeof(JitEntry),
+ sortTraceProfileCount);
+
+ /* Analyze the sorted entries */
+ for (i=0; i < gDvmJit.jitTableSize; i++) {
+ if (sortedEntries[i].dPC != 0) {
+ sum += dumpTraceProfile(&sortedEntries[i],
+ true /* silent */,
+ false /* reset */,
+ 0);
+ numTraces++;
+ }
+ }
+ if (numTraces == 0)
+ numTraces = 1;
+ if (sum == 0) {
+ sum = 1;
+ }
+
+ LOGD("JIT: Average execution count -> %d",(int)(sum / numTraces));
+
+ /* Dump the sorted entries. The count of each trace will be reset to 0. */
+ for (i=0; i < gDvmJit.jitTableSize; i++) {
+ if (sortedEntries[i].dPC != 0) {
+ dumpTraceProfile(&sortedEntries[i],
+ false /* silent */,
+ true /* reset */,
+ sum);
+ }
+ }
+
+ for (i=0; i < gDvmJit.jitTableSize && i < 10; i++) {
+ /* Stip interpreter stubs */
+ if (sortedEntries[i].codeAddress == dvmCompilerGetInterpretTemplate()) {
+ continue;
+ }
+ JitTraceDescription* desc =
+ dvmCopyTraceDescriptor(NULL, &sortedEntries[i]);
+ dvmCompilerWorkEnqueue(sortedEntries[i].dPC,
+ kWorkOrderTraceDebug, desc);
+ }
+
+ free(sortedEntries);
+done:
+ dvmUnlockMutex(&gDvmJit.tableLock);
+ return;
+}
+
+#if defined(WITH_SELF_VERIFICATION)
+/*
+ * The following are used to keep compiled loads and stores from modifying
+ * memory during self verification mode.
+ *
+ * Stores do not modify memory. Instead, the address and value pair are stored
+ * into heapSpace. Addresses within heapSpace are unique. For accesses smaller
+ * than a word, the word containing the address is loaded first before being
+ * updated.
+ *
+ * Loads check heapSpace first and return data from there if an entry exists.
+ * Otherwise, data is loaded from memory as usual.
+ */
+
+/* Used to specify sizes of memory operations */
+enum {
+ kSVByte,
+ kSVSignedByte,
+ kSVHalfword,
+ kSVSignedHalfword,
+ kSVWord,
+ kSVDoubleword,
+ kSVVariable,
+};
+
+/* Load the value of a decoded register from the stack */
+static int selfVerificationMemRegLoad(int* sp, int reg)
+{
+ return *(sp + reg);
+}
+
+/* Load the value of a decoded doubleword register from the stack */
+static s8 selfVerificationMemRegLoadDouble(int* sp, int reg)
+{
+ return *((s8*)(sp + reg));
+}
+
+/* Store the value of a decoded register out to the stack */
+static void selfVerificationMemRegStore(int* sp, int data, int reg)
+{
+ *(sp + reg) = data;
+}
+
+/* Store the value of a decoded doubleword register out to the stack */
+static void selfVerificationMemRegStoreDouble(int* sp, s8 data, int reg)
+{
+ *((s8*)(sp + reg)) = data;
+}
+
+/*
+ * Load the specified size of data from the specified address, checking
+ * heapSpace first if Self Verification mode wrote to it previously, and
+ * falling back to actual memory otherwise.
+ */
+static int selfVerificationLoad(int addr, int size)
+{
+ Thread *self = dvmThreadSelf();
+ ShadowSpace *shadowSpace = self->shadowSpace;
+ ShadowHeap *heapSpacePtr;
+
+ int data;
+ int maskedAddr = addr & 0xFFFFFFFC;
+ int alignment = addr & 0x3;
+
+ for (heapSpacePtr = shadowSpace->heapSpace;
+ heapSpacePtr != shadowSpace->heapSpaceTail; heapSpacePtr++) {
+ if (heapSpacePtr->addr == maskedAddr) {
+ addr = ((unsigned int) &(heapSpacePtr->data)) | alignment;
+ break;
+ }
+ }
+
+ switch (size) {
+ case kSVByte:
+ data = *((u1*) addr);
+ break;
+ case kSVSignedByte:
+ data = *((s1*) addr);
+ break;
+ case kSVHalfword:
+ data = *((u2*) addr);
+ break;
+ case kSVSignedHalfword:
+ data = *((s2*) addr);
+ break;
+ case kSVWord:
+ data = *((u4*) addr);
+ break;
+ default:
+ LOGE("*** ERROR: BAD SIZE IN selfVerificationLoad: %d", size);
+ data = 0;
+ dvmAbort();
+ }
+
+ //LOGD("*** HEAP LOAD: Addr: 0x%x Data: 0x%x Size: %d", addr, data, size);
+ return data;
+}
+
+/* Like selfVerificationLoad, but specifically for doublewords */
+static s8 selfVerificationLoadDoubleword(int addr)
+{
+ Thread *self = dvmThreadSelf();
+ ShadowSpace* shadowSpace = self->shadowSpace;
+ ShadowHeap* heapSpacePtr;
+
+ int addr2 = addr+4;
+ unsigned int data = *((unsigned int*) addr);
+ unsigned int data2 = *((unsigned int*) addr2);
+
+ for (heapSpacePtr = shadowSpace->heapSpace;
+ heapSpacePtr != shadowSpace->heapSpaceTail; heapSpacePtr++) {
+ if (heapSpacePtr->addr == addr) {
+ data = heapSpacePtr->data;
+ } else if (heapSpacePtr->addr == addr2) {
+ data2 = heapSpacePtr->data;
+ }
+ }
+
+ //LOGD("*** HEAP LOAD DOUBLEWORD: Addr: 0x%x Data: 0x%x Data2: 0x%x",
+ // addr, data, data2);
+ return (((s8) data2) << 32) | data;
+}
+
+/*
+ * Handles a store of a specified size of data to a specified address.
+ * This gets logged as an addr/data pair in heapSpace instead of modifying
+ * memory. Addresses in heapSpace are unique, and accesses smaller than a
+ * word pull the entire word from memory first before updating.
+ */
+static void selfVerificationStore(int addr, int data, int size)
+{
+ Thread *self = dvmThreadSelf();
+ ShadowSpace *shadowSpace = self->shadowSpace;
+ ShadowHeap *heapSpacePtr;
+
+ int maskedAddr = addr & 0xFFFFFFFC;
+ int alignment = addr & 0x3;
+
+ //LOGD("*** HEAP STORE: Addr: 0x%x Data: 0x%x Size: %d", addr, data, size);
+
+ for (heapSpacePtr = shadowSpace->heapSpace;
+ heapSpacePtr != shadowSpace->heapSpaceTail; heapSpacePtr++) {
+ if (heapSpacePtr->addr == maskedAddr) break;
+ }
+
+ if (heapSpacePtr == shadowSpace->heapSpaceTail) {
+ heapSpacePtr->addr = maskedAddr;
+ heapSpacePtr->data = *((unsigned int*) maskedAddr);
+ shadowSpace->heapSpaceTail++;
+ }
+
+ addr = ((unsigned int) &(heapSpacePtr->data)) | alignment;
+ switch (size) {
+ case kSVByte:
+ *((u1*) addr) = data;
+ break;
+ case kSVSignedByte:
+ *((s1*) addr) = data;
+ break;
+ case kSVHalfword:
+ *((u2*) addr) = data;
+ break;
+ case kSVSignedHalfword:
+ *((s2*) addr) = data;
+ break;
+ case kSVWord:
+ *((u4*) addr) = data;
+ break;
+ default:
+ LOGE("*** ERROR: BAD SIZE IN selfVerificationSave: %d", size);
+ dvmAbort();
+ }
+}
+
+/* Like selfVerificationStore, but specifically for doublewords */
+static void selfVerificationStoreDoubleword(int addr, s8 double_data)
+{
+ Thread *self = dvmThreadSelf();
+ ShadowSpace *shadowSpace = self->shadowSpace;
+ ShadowHeap *heapSpacePtr;
+
+ int addr2 = addr+4;
+ int data = double_data;
+ int data2 = double_data >> 32;
+ bool store1 = false, store2 = false;
+
+ //LOGD("*** HEAP STORE DOUBLEWORD: Addr: 0x%x Data: 0x%x, Data2: 0x%x",
+ // addr, data, data2);
+
+ for (heapSpacePtr = shadowSpace->heapSpace;
+ heapSpacePtr != shadowSpace->heapSpaceTail; heapSpacePtr++) {
+ if (heapSpacePtr->addr == addr) {
+ heapSpacePtr->data = data;
+ store1 = true;
+ } else if (heapSpacePtr->addr == addr2) {
+ heapSpacePtr->data = data2;
+ store2 = true;
+ }
+ }
+
+ if (!store1) {
+ shadowSpace->heapSpaceTail->addr = addr;
+ shadowSpace->heapSpaceTail->data = data;
+ shadowSpace->heapSpaceTail++;
+ }
+ if (!store2) {
+ shadowSpace->heapSpaceTail->addr = addr2;
+ shadowSpace->heapSpaceTail->data = data2;
+ shadowSpace->heapSpaceTail++;
+ }
+}
+
+/*
+ * Decodes the memory instruction at the address specified in the link
+ * register. All registers (r0-r12,lr) and fp registers (d0-d15) are stored
+ * consecutively on the stack beginning at the specified stack pointer.
+ * Calls the proper Self Verification handler for the memory instruction and
+ * updates the link register to point past the decoded memory instruction.
+ */
+void dvmSelfVerificationMemOpDecode(int lr, int* sp)
+{
+ enum {
+ kMemOpLdrPcRel = 0x09, // ldr(3) [01001] rd[10..8] imm_8[7..0]
+ kMemOpRRR = 0x0A, // Full opcode is 7 bits
+ kMemOp2Single = 0x0A, // Used for Vstrs and Vldrs
+ kMemOpRRR2 = 0x0B, // Full opcode is 7 bits
+ kMemOp2Double = 0x0B, // Used for Vstrd and Vldrd
+ kMemOpStrRRI5 = 0x0C, // str(1) [01100] imm_5[10..6] rn[5..3] rd[2..0]
+ kMemOpLdrRRI5 = 0x0D, // ldr(1) [01101] imm_5[10..6] rn[5..3] rd[2..0]
+ kMemOpStrbRRI5 = 0x0E, // strb(1) [01110] imm_5[10..6] rn[5..3] rd[2..0]
+ kMemOpLdrbRRI5 = 0x0F, // ldrb(1) [01111] imm_5[10..6] rn[5..3] rd[2..0]
+ kMemOpStrhRRI5 = 0x10, // strh(1) [10000] imm_5[10..6] rn[5..3] rd[2..0]
+ kMemOpLdrhRRI5 = 0x11, // ldrh(1) [10001] imm_5[10..6] rn[5..3] rd[2..0]
+ kMemOpLdrSpRel = 0x13, // ldr(4) [10011] rd[10..8] imm_8[7..0]
+ kMemOpStmia = 0x18, // stmia [11000] rn[10..8] reglist [7..0]
+ kMemOpLdmia = 0x19, // ldmia [11001] rn[10..8] reglist [7..0]
+ kMemOpStrRRR = 0x28, // str(2) [0101000] rm[8..6] rn[5..3] rd[2..0]
+ kMemOpStrhRRR = 0x29, // strh(2) [0101001] rm[8..6] rn[5..3] rd[2..0]
+ kMemOpStrbRRR = 0x2A, // strb(2) [0101010] rm[8..6] rn[5..3] rd[2..0]
+ kMemOpLdrsbRRR = 0x2B, // ldrsb [0101011] rm[8..6] rn[5..3] rd[2..0]
+ kMemOpLdrRRR = 0x2C, // ldr(2) [0101100] rm[8..6] rn[5..3] rd[2..0]
+ kMemOpLdrhRRR = 0x2D, // ldrh(2) [0101101] rm[8..6] rn[5..3] rd[2..0]
+ kMemOpLdrbRRR = 0x2E, // ldrb(2) [0101110] rm[8..6] rn[5..3] rd[2..0]
+ kMemOpLdrshRRR = 0x2F, // ldrsh [0101111] rm[8..6] rn[5..3] rd[2..0]
+ kMemOp2Stmia = 0xE88, // stmia [111010001000[ rn[19..16] mask[15..0]
+ kMemOp2Ldmia = 0xE89, // ldmia [111010001001[ rn[19..16] mask[15..0]
+ kMemOp2Stmia2 = 0xE8A, // stmia [111010001010[ rn[19..16] mask[15..0]
+ kMemOp2Ldmia2 = 0xE8B, // ldmia [111010001011[ rn[19..16] mask[15..0]
+ kMemOp2Vstr = 0xED8, // Used for Vstrs and Vstrd
+ kMemOp2Vldr = 0xED9, // Used for Vldrs and Vldrd
+ kMemOp2Vstr2 = 0xEDC, // Used for Vstrs and Vstrd
+ kMemOp2Vldr2 = 0xEDD, // Used for Vstrs and Vstrd
+ kMemOp2StrbRRR = 0xF80, /* str rt,[rn,rm,LSL #imm] [111110000000]
+ rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+ kMemOp2LdrbRRR = 0xF81, /* ldrb rt,[rn,rm,LSL #imm] [111110000001]
+ rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+ kMemOp2StrhRRR = 0xF82, /* str rt,[rn,rm,LSL #imm] [111110000010]
+ rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+ kMemOp2LdrhRRR = 0xF83, /* ldrh rt,[rn,rm,LSL #imm] [111110000011]
+ rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+ kMemOp2StrRRR = 0xF84, /* str rt,[rn,rm,LSL #imm] [111110000100]
+ rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+ kMemOp2LdrRRR = 0xF85, /* ldr rt,[rn,rm,LSL #imm] [111110000101]
+ rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+ kMemOp2StrbRRI12 = 0xF88, /* strb rt,[rn,#imm12] [111110001000]
+ rt[15..12] rn[19..16] imm12[11..0] */
+ kMemOp2LdrbRRI12 = 0xF89, /* ldrb rt,[rn,#imm12] [111110001001]
+ rt[15..12] rn[19..16] imm12[11..0] */
+ kMemOp2StrhRRI12 = 0xF8A, /* strh rt,[rn,#imm12] [111110001010]
+ rt[15..12] rn[19..16] imm12[11..0] */
+ kMemOp2LdrhRRI12 = 0xF8B, /* ldrh rt,[rn,#imm12] [111110001011]
+ rt[15..12] rn[19..16] imm12[11..0] */
+ kMemOp2StrRRI12 = 0xF8C, /* str(Imm,T3) rd,[rn,#imm12] [111110001100]
+ rn[19..16] rt[15..12] imm12[11..0] */
+ kMemOp2LdrRRI12 = 0xF8D, /* ldr(Imm,T3) rd,[rn,#imm12] [111110001101]
+ rn[19..16] rt[15..12] imm12[11..0] */
+ kMemOp2LdrsbRRR = 0xF91, /* ldrsb rt,[rn,rm,LSL #imm] [111110010001]
+ rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+ kMemOp2LdrshRRR = 0xF93, /* ldrsh rt,[rn,rm,LSL #imm] [111110010011]
+ rn[19-16] rt[15-12] [000000] imm[5-4] rm[3-0] */
+ kMemOp2LdrsbRRI12 = 0xF99, /* ldrsb rt,[rn,#imm12] [111110011001]
+ rt[15..12] rn[19..16] imm12[11..0] */
+ kMemOp2LdrshRRI12 = 0xF9B, /* ldrsh rt,[rn,#imm12] [111110011011]
+ rt[15..12] rn[19..16] imm12[11..0] */
+ kMemOp2 = 0xE000, // top 3 bits set indicates Thumb2
+ };
+
+ int addr, offset, data;
+ long long double_data;
+ int size = kSVWord;
+ bool store = false;
+ unsigned int *lr_masked = (unsigned int *) (lr & 0xFFFFFFFE);
+ unsigned int insn = *lr_masked;
+
+ int old_lr;
+ old_lr = selfVerificationMemRegLoad(sp, 13);
+
+ if ((insn & kMemOp2) == kMemOp2) {
+ insn = (insn << 16) | (insn >> 16);
+ //LOGD("*** THUMB2 - Addr: 0x%x Insn: 0x%x", lr, insn);
+
+ int opcode12 = (insn >> 20) & 0xFFF;
+ int opcode4 = (insn >> 8) & 0xF;
+ int imm2 = (insn >> 4) & 0x3;
+ int imm8 = insn & 0xFF;
+ int imm12 = insn & 0xFFF;
+ int rd = (insn >> 12) & 0xF;
+ int rm = insn & 0xF;
+ int rn = (insn >> 16) & 0xF;
+ int rt = (insn >> 12) & 0xF;
+ bool wBack = true;
+
+ // Update the link register
+ selfVerificationMemRegStore(sp, old_lr+4, 13);
+
+ // Determine whether the mem op is a store or load
+ switch (opcode12) {
+ case kMemOp2Stmia:
+ case kMemOp2Stmia2:
+ case kMemOp2Vstr:
+ case kMemOp2Vstr2:
+ case kMemOp2StrbRRR:
+ case kMemOp2StrhRRR:
+ case kMemOp2StrRRR:
+ case kMemOp2StrbRRI12:
+ case kMemOp2StrhRRI12:
+ case kMemOp2StrRRI12:
+ store = true;
+ }
+
+ // Determine the size of the mem access
+ switch (opcode12) {
+ case kMemOp2StrbRRR:
+ case kMemOp2LdrbRRR:
+ case kMemOp2StrbRRI12:
+ case kMemOp2LdrbRRI12:
+ size = kSVByte;
+ break;
+ case kMemOp2LdrsbRRR:
+ case kMemOp2LdrsbRRI12:
+ size = kSVSignedByte;
+ break;
+ case kMemOp2StrhRRR:
+ case kMemOp2LdrhRRR:
+ case kMemOp2StrhRRI12:
+ case kMemOp2LdrhRRI12:
+ size = kSVHalfword;
+ break;
+ case kMemOp2LdrshRRR:
+ case kMemOp2LdrshRRI12:
+ size = kSVSignedHalfword;
+ break;
+ case kMemOp2Vstr:
+ case kMemOp2Vstr2:
+ case kMemOp2Vldr:
+ case kMemOp2Vldr2:
+ if (opcode4 == kMemOp2Double) size = kSVDoubleword;
+ break;
+ case kMemOp2Stmia:
+ case kMemOp2Ldmia:
+ case kMemOp2Stmia2:
+ case kMemOp2Ldmia2:
+ size = kSVVariable;
+ break;
+ }
+
+ // Load the value of the address
+ addr = selfVerificationMemRegLoad(sp, rn);
+
+ // Figure out the offset
+ switch (opcode12) {
+ case kMemOp2Vstr:
+ case kMemOp2Vstr2:
+ case kMemOp2Vldr:
+ case kMemOp2Vldr2:
+ offset = imm8 << 2;
+ if (opcode4 == kMemOp2Single) {
+ rt = rd << 1;
+ if (insn & 0x400000) rt |= 0x1;
+ } else if (opcode4 == kMemOp2Double) {
+ if (insn & 0x400000) rt |= 0x10;
+ rt = rt << 1;
+ } else {
+ LOGE("*** ERROR: UNRECOGNIZED VECTOR MEM OP: %x", opcode4);
+ dvmAbort();
+ }
+ rt += 14;
+ break;
+ case kMemOp2StrbRRR:
+ case kMemOp2LdrbRRR:
+ case kMemOp2StrhRRR:
+ case kMemOp2LdrhRRR:
+ case kMemOp2StrRRR:
+ case kMemOp2LdrRRR:
+ case kMemOp2LdrsbRRR:
+ case kMemOp2LdrshRRR:
+ offset = selfVerificationMemRegLoad(sp, rm) << imm2;
+ break;
+ case kMemOp2StrbRRI12:
+ case kMemOp2LdrbRRI12:
+ case kMemOp2StrhRRI12:
+ case kMemOp2LdrhRRI12:
+ case kMemOp2StrRRI12:
+ case kMemOp2LdrRRI12:
+ case kMemOp2LdrsbRRI12:
+ case kMemOp2LdrshRRI12:
+ offset = imm12;
+ break;
+ case kMemOp2Stmia:
+ case kMemOp2Ldmia:
+ wBack = false;
+ case kMemOp2Stmia2:
+ case kMemOp2Ldmia2:
+ offset = 0;
+ break;
+ default:
+ LOGE("*** ERROR: UNRECOGNIZED THUMB2 MEM OP: %x", opcode12);
+ offset = 0;
+ dvmAbort();
+ }
+
+ // Handle the decoded mem op accordingly
+ if (store) {
+ if (size == kSVVariable) {
+ LOGD("*** THUMB2 STMIA CURRENTLY UNUSED (AND UNTESTED)");
+ int i;
+ int regList = insn & 0xFFFF;
+ for (i = 0; i < 16; i++) {
+ if (regList & 0x1) {
+ data = selfVerificationMemRegLoad(sp, i);
+ selfVerificationStore(addr, data, kSVWord);
+ addr += 4;
+ }
+ regList = regList >> 1;
+ }
+ if (wBack) selfVerificationMemRegStore(sp, addr, rn);
+ } else if (size == kSVDoubleword) {
+ double_data = selfVerificationMemRegLoadDouble(sp, rt);
+ selfVerificationStoreDoubleword(addr+offset, double_data);
+ } else {
+ data = selfVerificationMemRegLoad(sp, rt);
+ selfVerificationStore(addr+offset, data, size);
+ }
+ } else {
+ if (size == kSVVariable) {
+ LOGD("*** THUMB2 LDMIA CURRENTLY UNUSED (AND UNTESTED)");
+ int i;
+ int regList = insn & 0xFFFF;
+ for (i = 0; i < 16; i++) {
+ if (regList & 0x1) {
+ data = selfVerificationLoad(addr, kSVWord);
+ selfVerificationMemRegStore(sp, data, i);
+ addr += 4;
+ }
+ regList = regList >> 1;
+ }
+ if (wBack) selfVerificationMemRegStore(sp, addr, rn);
+ } else if (size == kSVDoubleword) {
+ double_data = selfVerificationLoadDoubleword(addr+offset);
+ selfVerificationMemRegStoreDouble(sp, double_data, rt);
+ } else {
+ data = selfVerificationLoad(addr+offset, size);
+ selfVerificationMemRegStore(sp, data, rt);
+ }
+ }
+ } else {
+ //LOGD("*** THUMB - Addr: 0x%x Insn: 0x%x", lr, insn);
+
+ // Update the link register
+ selfVerificationMemRegStore(sp, old_lr+2, 13);
+
+ int opcode5 = (insn >> 11) & 0x1F;
+ int opcode7 = (insn >> 9) & 0x7F;
+ int imm = (insn >> 6) & 0x1F;
+ int rd = (insn >> 8) & 0x7;
+ int rm = (insn >> 6) & 0x7;
+ int rn = (insn >> 3) & 0x7;
+ int rt = insn & 0x7;
+
+ // Determine whether the mem op is a store or load
+ switch (opcode5) {
+ case kMemOpRRR:
+ switch (opcode7) {
+ case kMemOpStrRRR:
+ case kMemOpStrhRRR:
+ case kMemOpStrbRRR:
+ store = true;
+ }
+ break;
+ case kMemOpStrRRI5:
+ case kMemOpStrbRRI5:
+ case kMemOpStrhRRI5:
+ case kMemOpStmia:
+ store = true;
+ }
+
+ // Determine the size of the mem access
+ switch (opcode5) {
+ case kMemOpRRR:
+ case kMemOpRRR2:
+ switch (opcode7) {
+ case kMemOpStrbRRR:
+ case kMemOpLdrbRRR:
+ size = kSVByte;
+ break;
+ case kMemOpLdrsbRRR:
+ size = kSVSignedByte;
+ break;
+ case kMemOpStrhRRR:
+ case kMemOpLdrhRRR:
+ size = kSVHalfword;
+ break;
+ case kMemOpLdrshRRR:
+ size = kSVSignedHalfword;
+ break;
+ }
+ break;
+ case kMemOpStrbRRI5:
+ case kMemOpLdrbRRI5:
+ size = kSVByte;
+ break;
+ case kMemOpStrhRRI5:
+ case kMemOpLdrhRRI5:
+ size = kSVHalfword;
+ break;
+ case kMemOpStmia:
+ case kMemOpLdmia:
+ size = kSVVariable;
+ break;
+ }
+
+ // Load the value of the address
+ if (opcode5 == kMemOpLdrPcRel)
+ addr = selfVerificationMemRegLoad(sp, 4);
+ else if (opcode5 == kMemOpStmia || opcode5 == kMemOpLdmia)
+ addr = selfVerificationMemRegLoad(sp, rd);
+ else
+ addr = selfVerificationMemRegLoad(sp, rn);
+
+ // Figure out the offset
+ switch (opcode5) {
+ case kMemOpLdrPcRel:
+ offset = (insn & 0xFF) << 2;
+ rt = rd;
+ break;
+ case kMemOpRRR:
+ case kMemOpRRR2:
+ offset = selfVerificationMemRegLoad(sp, rm);
+ break;
+ case kMemOpStrRRI5:
+ case kMemOpLdrRRI5:
+ offset = imm << 2;
+ break;
+ case kMemOpStrhRRI5:
+ case kMemOpLdrhRRI5:
+ offset = imm << 1;
+ break;
+ case kMemOpStrbRRI5:
+ case kMemOpLdrbRRI5:
+ offset = imm;
+ break;
+ case kMemOpStmia:
+ case kMemOpLdmia:
+ offset = 0;
+ break;
+ default:
+ LOGE("*** ERROR: UNRECOGNIZED THUMB MEM OP: %x", opcode5);
+ offset = 0;
+ dvmAbort();
+ }
+
+ // Handle the decoded mem op accordingly
+ if (store) {
+ if (size == kSVVariable) {
+ int i;
+ int regList = insn & 0xFF;
+ for (i = 0; i < 8; i++) {
+ if (regList & 0x1) {
+ data = selfVerificationMemRegLoad(sp, i);
+ selfVerificationStore(addr, data, kSVWord);
+ addr += 4;
+ }
+ regList = regList >> 1;
+ }
+ selfVerificationMemRegStore(sp, addr, rd);
+ } else {
+ data = selfVerificationMemRegLoad(sp, rt);
+ selfVerificationStore(addr+offset, data, size);
+ }
+ } else {
+ if (size == kSVVariable) {
+ bool wBack = true;
+ int i;
+ int regList = insn & 0xFF;
+ for (i = 0; i < 8; i++) {
+ if (regList & 0x1) {
+ if (i == rd) wBack = false;
+ data = selfVerificationLoad(addr, kSVWord);
+ selfVerificationMemRegStore(sp, data, i);
+ addr += 4;
+ }
+ regList = regList >> 1;
+ }
+ if (wBack) selfVerificationMemRegStore(sp, addr, rd);
+ } else {
+ data = selfVerificationLoad(addr+offset, size);
+ selfVerificationMemRegStore(sp, data, rt);
+ }
+ }
+ }
+}
+#endif
diff --git a/vm/compiler/codegen/arm/CalloutHelper.h b/vm/compiler/codegen/arm/CalloutHelper.h
new file mode 100644
index 0000000..d6eb421
--- /dev/null
+++ b/vm/compiler/codegen/arm/CalloutHelper.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+
+#ifndef _DALVIK_VM_COMPILER_CODEGEN_ARM_CALLOUT_HELPER_H
+#define _DALVIK_VM_COMPILER_CODEGEN_ARM_CALLOUT_HELPER_H
+
+/*
+ * Declare/comment prototypes of all native callout functions invoked by the
+ * JIT'ed code here and use the LOAD_FUNC_ADDR macro to load the address into
+ * a register. In this way we have a centralized place to find out all native
+ * helper functions and we can grep for LOAD_FUNC_ADDR to find out all the
+ * callsites.
+ */
+
+/* Load a statically compiled function address as a constant */
+#define LOAD_FUNC_ADDR(cUnit, reg, addr) loadConstant(cUnit, reg, addr)
+
+/* Conversions */
+float __aeabi_i2f(int op1); // OP_INT_TO_FLOAT
+int __aeabi_f2iz(float op1); // OP_FLOAT_TO_INT
+float __aeabi_d2f(double op1); // OP_DOUBLE_TO_FLOAT
+double __aeabi_f2d(float op1); // OP_FLOAT_TO_DOUBLE
+double __aeabi_i2d(int op1); // OP_INT_TO_DOUBLE
+int __aeabi_d2iz(double op1); // OP_DOUBLE_TO_INT
+float __aeabi_l2f(long op1); // OP_LONG_TO_FLOAT
+double __aeabi_l2d(long op1); // OP_LONG_TO_DOUBLE
+s8 dvmJitf2l(float op1); // OP_FLOAT_TO_LONG
+s8 dvmJitd2l(double op1); // OP_DOUBLE_TO_LONG
+
+/* Single-precision FP arithmetics */
+float __aeabi_fadd(float a, float b); // OP_ADD_FLOAT[_2ADDR]
+float __aeabi_fsub(float a, float b); // OP_SUB_FLOAT[_2ADDR]
+float __aeabi_fdiv(float a, float b); // OP_DIV_FLOAT[_2ADDR]
+float __aeabi_fmul(float a, float b); // OP_MUL_FLOAT[_2ADDR]
+float fmodf(float a, float b); // OP_REM_FLOAT[_2ADDR]
+
+/* Double-precision FP arithmetics */
+double __aeabi_dadd(double a, double b); // OP_ADD_DOUBLE[_2ADDR]
+double __aeabi_dsub(double a, double b); // OP_SUB_DOUBLE[_2ADDR]
+double __aeabi_ddiv(double a, double b); // OP_DIV_DOUBLE[_2ADDR]
+double __aeabi_dmul(double a, double b); // OP_MUL_DOUBLE[_2ADDR]
+double fmod(double a, double b); // OP_REM_DOUBLE[_2ADDR]
+
+/* Integer arithmetics */
+int __aeabi_idivmod(int op1, int op2); // OP_REM_INT[_2ADDR|_LIT8|_LIT16]
+int __aeabi_idiv(int op1, int op2); // OP_DIV_INT[_2ADDR|_LIT8|_LIT16]
+
+/* Long long arithmetics - OP_REM_LONG[_2ADDR] & OP_DIV_LONG[_2ADDR] */
+long long __aeabi_ldivmod(long long op1, long long op2);
+
+/* Originally declared in Sync.h */
+bool dvmUnlockObject(struct Thread* self, struct Object* obj); //OP_MONITOR_EXIT
+
+/* Originally declared in oo/TypeCheck.h */
+bool dvmCanPutArrayElement(const ClassObject* elemClass, // OP_APUT_OBJECT
+ const ClassObject* arrayClass);
+int dvmInstanceofNonTrivial(const ClassObject* instance, // OP_CHECK_CAST &&
+ const ClassObject* clazz); // OP_INSTANCE_OF
+
+/* Originally declared in oo/Array.h */
+ArrayObject* dvmAllocArrayByClass(ClassObject* arrayClass, // OP_NEW_ARRAY
+ size_t length, int allocFlags);
+
+/* Originally declared in interp/InterpDefs.h */
+bool dvmInterpHandleFillArrayData(ArrayObject* arrayObject,// OP_FILL_ARRAY_DATA
+ const u2* arrayData);
+
+/*
+ * Switch dispatch offset calculation for OP_PACKED_SWITCH & OP_SPARSE_SWITCH
+ * Used in CodegenDriver.c
+ * static s8 findPackedSwitchIndex(const u2* switchData, int testVal, int pc);
+ * static s8 findSparseSwitchIndex(const u2* switchData, int testVal, int pc);
+ */
+
+/*
+ * Resolve interface callsites - OP_INVOKE_INTERFACE & OP_INVOKE_INTERFACE_RANGE
+ *
+ * Originally declared in mterp/common/FindInterface.h and only comment it here
+ * due to the INLINE attribute.
+ *
+ * INLINE Method* dvmFindInterfaceMethodInCache(ClassObject* thisClass,
+ * u4 methodIdx, const Method* method, DvmDex* methodClassDex)
+ */
+
+/* Originally declared in alloc/Alloc.h */
+Object* dvmAllocObject(ClassObject* clazz, int flags); // OP_NEW_INSTANCE
+
+/*
+ * Functions declared in gDvmInlineOpsTable[] are used for
+ * OP_EXECUTE_INLINE & OP_EXECUTE_INLINE_RANGE.
+ *
+ * org_apache_harmony_dalvik_NativeTestTarget_emptyInlineMethod
+ * javaLangString_charAt
+ * javaLangString_compareTo
+ * javaLangString_equals
+ * javaLangString_indexOf_I
+ * javaLangString_indexOf_II
+ * javaLangString_length
+ * javaLangMath_abs_int
+ * javaLangMath_abs_long
+ * javaLangMath_abs_float
+ * javaLangMath_abs_double
+ * javaLangMath_min_int
+ * javaLangMath_max_int
+ * javaLangMath_sqrt
+ * javaLangMath_cos
+ * javaLangMath_sin
+ */
+double sqrt(double x); // INLINE_MATH_SQRT
+
+/*
+ * The following functions are invoked through the compiler templates (declared
+ * in compiler/template/armv5te/footer.S:
+ *
+ * __aeabi_cdcmple // CMPG_DOUBLE
+ * __aeabi_cfcmple // CMPG_FLOAT
+ * dvmLockObject // MONITOR_ENTER
+ */
+
+#endif /* _DALVIK_VM_COMPILER_CODEGEN_ARM_CALLOUT_HELPER_H */
diff --git a/vm/compiler/codegen/arm/Codegen.h b/vm/compiler/codegen/arm/Codegen.h
new file mode 100644
index 0000000..be74e3f
--- /dev/null
+++ b/vm/compiler/codegen/arm/Codegen.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file contains register alloction support and is intended to be
+ * included by:
+ *
+ * Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+#include "compiler/CompilerIR.h"
+#include "CalloutHelper.h"
+
+#if defined(_CODEGEN_C)
+/*
+ * loadConstant() sometimes needs to add a small imm to a pre-existing constant
+ */
+static ArmLIR *opRegImm(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
+ int value);
+static ArmLIR *opRegReg(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
+ int rSrc2);
+
+/* Forward decalraton the portable versions due to circular dependency */
+static bool genArithOpFloatPortable(CompilationUnit *cUnit, MIR *mir,
+ RegLocation rlDest, RegLocation rlSrc1,
+ RegLocation rlSrc2);
+
+static bool genArithOpDoublePortable(CompilationUnit *cUnit, MIR *mir,
+ RegLocation rlDest, RegLocation rlSrc1,
+ RegLocation rlSrc2);
+
+static bool genConversionPortable(CompilationUnit *cUnit, MIR *mir);
+
+#if defined(WITH_DEADLOCK_PREDICTION) || defined(WITH_MONITOR_TRACKING) || \
+ defined(__ARM_ARCH_5__)
+static void genMonitorPortable(CompilationUnit *cUnit, MIR *mir);
+#endif
+
+static void genInterpSingleStep(CompilationUnit *cUnit, MIR *mir);
+
+#endif
+
+
+#if defined(WITH_SELF_VERIFICATION)
+/* Self Verification memory instruction decoder */
+void dvmSelfVerificationMemOpDecode(int lr, int* sp);
+#endif
+
+/*
+ * Architecture-dependent register allocation routines implemented in
+ * Thumb[2]/Ralloc.c
+ */
+extern int dvmCompilerAllocTypedTempPair(CompilationUnit *cUnit,
+ bool fpHint, int regClass);
+
+extern int dvmCompilerAllocTypedTemp(CompilationUnit *cUnit, bool fpHint,
+ int regClass);
+
+extern ArmLIR* dvmCompilerRegCopyNoInsert(CompilationUnit *cUnit, int rDest,
+ int rSrc);
+
+extern ArmLIR* dvmCompilerRegCopy(CompilationUnit *cUnit, int rDest, int rSrc);
+
+extern void dvmCompilerRegCopyWide(CompilationUnit *cUnit, int destLo,
+ int destHi, int srcLo, int srcHi);
+
+extern void dvmCompilerSetupResourceMasks(ArmLIR *lir);
+
+extern void dvmCompilerFlushRegImpl(CompilationUnit *cUnit, int rBase,
+ int displacement, int rSrc, OpSize size);
+
+extern void dvmCompilerFlushRegWideImpl(CompilationUnit *cUnit, int rBase,
+ int displacement, int rSrcLo,
+ int rSrcHi);
diff --git a/vm/compiler/codegen/arm/CodegenCommon.c b/vm/compiler/codegen/arm/CodegenCommon.c
new file mode 100644
index 0000000..d8854ba
--- /dev/null
+++ b/vm/compiler/codegen/arm/CodegenCommon.c
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file contains codegen and support common to all supported
+ * ARM variants. It is included by:
+ *
+ * Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ * which combines this common code with specific support found in the
+ * applicable directory below this one.
+ */
+
+#include "compiler/Loop.h"
+
+/* Array holding the entry offset of each template relative to the first one */
+static intptr_t templateEntryOffsets[TEMPLATE_LAST_MARK];
+
+/* Track exercised opcodes */
+static int opcodeCoverage[256];
+
+static void setMemRefType(ArmLIR *lir, bool isLoad, int memType)
+{
+ u8 *maskPtr;
+ u8 mask;
+ assert( EncodingMap[lir->opCode].flags & (IS_LOAD | IS_STORE));
+ if (isLoad) {
+ maskPtr = &lir->useMask;
+ mask = ENCODE_MEM_USE;
+ } else {
+ maskPtr = &lir->defMask;
+ mask = ENCODE_MEM_DEF;
+ }
+ /* Clear out the memref flags */
+ *maskPtr &= ~mask;
+ /* ..and then add back the one we need */
+ switch(memType) {
+ case kLiteral:
+ assert(isLoad);
+ *maskPtr |= (ENCODE_LITERAL | ENCODE_LITPOOL_REF);
+ break;
+ case kDalvikReg:
+ *maskPtr |= (ENCODE_DALVIK_REG | ENCODE_FRAME_REF);
+ break;
+ case kHeapRef:
+ *maskPtr |= ENCODE_HEAP_REF;
+ break;
+ default:
+ LOGE("Jit: invalid memref kind - %d", memType);
+ assert(0); // Bail if debug build, set worst-case in the field
+ *maskPtr |= ENCODE_ALL;
+ }
+}
+
+/*
+ * Mark load/store instructions that access Dalvik registers through rFP +
+ * offset.
+ */
+static void annotateDalvikRegAccess(ArmLIR *lir, int regId, bool isLoad)
+{
+ setMemRefType(lir, isLoad, kDalvikReg);
+
+ /*
+ * Store the Dalvik register id in aliasInfo. Mark he MSB if it is a 64-bit
+ * access.
+ */
+ lir->aliasInfo = regId;
+ if (DOUBLEREG(lir->operands[0])) {
+ lir->aliasInfo |= 0x80000000;
+ }
+}
+
+/*
+ * Decode the register id and mark the corresponding bit(s).
+ */
+static inline void setupRegMask(u8 *mask, int reg)
+{
+ u8 seed;
+ int shift;
+ int regId = reg & 0x1f;
+
+ /*
+ * Each double register is equal to a pair of single-precision FP registers
+ */
+ seed = DOUBLEREG(reg) ? 3 : 1;
+ /* FP register starts at bit position 16 */
+ shift = FPREG(reg) ? kFPReg0 : 0;
+ /* Expand the double register id into single offset */
+ shift += regId;
+ *mask |= seed << shift;
+}
+
+/*
+ * Set up the proper fields in the resource mask
+ */
+static void setupResourceMasks(ArmLIR *lir)
+{
+ int opCode = lir->opCode;
+ int flags;
+
+ if (opCode <= 0) {
+ lir->useMask = lir->defMask = 0;
+ return;
+ }
+
+ flags = EncodingMap[lir->opCode].flags;
+
+ /* Set up the mask for resources that are updated */
+ if (flags & (IS_LOAD | IS_STORE)) {
+ /* Default to heap - will catch specialized classes later */
+ setMemRefType(lir, flags & IS_LOAD, kHeapRef);
+ }
+
+ if (flags & IS_BRANCH) {
+ lir->defMask |= ENCODE_REG_PC;
+ lir->useMask |= ENCODE_REG_PC;
+ }
+
+ if (flags & REG_DEF0) {
+ setupRegMask(&lir->defMask, lir->operands[0]);
+ }
+
+ if (flags & REG_DEF1) {
+ setupRegMask(&lir->defMask, lir->operands[1]);
+ }
+
+ if (flags & REG_DEF_SP) {
+ lir->defMask |= ENCODE_REG_SP;
+ }
+
+ if (flags & REG_DEF_LR) {
+ lir->defMask |= ENCODE_REG_LR;
+ }
+
+ if (flags & REG_DEF_LIST0) {
+ lir->defMask |= ENCODE_REG_LIST(lir->operands[0]);
+ }
+
+ if (flags & REG_DEF_LIST1) {
+ lir->defMask |= ENCODE_REG_LIST(lir->operands[1]);
+ }
+
+ if (flags & SETS_CCODES) {
+ lir->defMask |= ENCODE_CCODE;
+ }
+
+ /* Conservatively treat the IT block */
+ if (flags & IS_IT) {
+ lir->defMask = ENCODE_ALL;
+ }
+
+ /* Set up the mask for resources that are used */
+ if (flags & IS_BRANCH) {
+ lir->useMask |= ENCODE_REG_PC;
+ }
+
+ if (flags & (REG_USE0 | REG_USE1 | REG_USE2 | REG_USE3)) {
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ if (flags & (1 << (kRegUse0 + i))) {
+ setupRegMask(&lir->useMask, lir->operands[i]);
+ }
+ }
+ }
+
+ if (flags & REG_USE_PC) {
+ lir->useMask |= ENCODE_REG_PC;
+ }
+
+ if (flags & REG_USE_SP) {
+ lir->useMask |= ENCODE_REG_SP;
+ }
+
+ if (flags & REG_USE_LIST0) {
+ lir->useMask |= ENCODE_REG_LIST(lir->operands[0]);
+ }
+
+ if (flags & REG_USE_LIST1) {
+ lir->useMask |= ENCODE_REG_LIST(lir->operands[1]);
+ }
+
+ if (flags & USES_CCODES) {
+ lir->useMask |= ENCODE_CCODE;
+ }
+}
+
+/*
+ * The following are building blocks to construct low-level IRs with 0 - 4
+ * operands.
+ */
+static ArmLIR *newLIR0(CompilationUnit *cUnit, ArmOpCode opCode)
+{
+ ArmLIR *insn = dvmCompilerNew(sizeof(ArmLIR), true);
+ assert(isPseudoOpCode(opCode) || (EncodingMap[opCode].flags & NO_OPERAND));
+ insn->opCode = opCode;
+ setupResourceMasks(insn);
+ dvmCompilerAppendLIR(cUnit, (LIR *) insn);
+ return insn;
+}
+
+static ArmLIR *newLIR1(CompilationUnit *cUnit, ArmOpCode opCode,
+ int dest)
+{
+ ArmLIR *insn = dvmCompilerNew(sizeof(ArmLIR), true);
+ assert(isPseudoOpCode(opCode) || (EncodingMap[opCode].flags & IS_UNARY_OP));
+ insn->opCode = opCode;
+ insn->operands[0] = dest;
+ setupResourceMasks(insn);
+ dvmCompilerAppendLIR(cUnit, (LIR *) insn);
+ return insn;
+}
+
+static ArmLIR *newLIR2(CompilationUnit *cUnit, ArmOpCode opCode,
+ int dest, int src1)
+{
+ ArmLIR *insn = dvmCompilerNew(sizeof(ArmLIR), true);
+ assert(isPseudoOpCode(opCode) ||
+ (EncodingMap[opCode].flags & IS_BINARY_OP));
+ insn->opCode = opCode;
+ insn->operands[0] = dest;
+ insn->operands[1] = src1;
+ setupResourceMasks(insn);
+ dvmCompilerAppendLIR(cUnit, (LIR *) insn);
+ return insn;
+}
+
+static ArmLIR *newLIR3(CompilationUnit *cUnit, ArmOpCode opCode,
+ int dest, int src1, int src2)
+{
+ ArmLIR *insn = dvmCompilerNew(sizeof(ArmLIR), true);
+ if (!(EncodingMap[opCode].flags & IS_TERTIARY_OP)) {
+ LOGE("Bad LIR3: %s[%d]",EncodingMap[opCode].name,opCode);
+ }
+ assert(isPseudoOpCode(opCode) ||
+ (EncodingMap[opCode].flags & IS_TERTIARY_OP));
+ insn->opCode = opCode;
+ insn->operands[0] = dest;
+ insn->operands[1] = src1;
+ insn->operands[2] = src2;
+ setupResourceMasks(insn);
+ dvmCompilerAppendLIR(cUnit, (LIR *) insn);
+ return insn;
+}
+
+#if defined(_ARMV7_A) || defined(_ARMV7_A_NEON)
+static ArmLIR *newLIR4(CompilationUnit *cUnit, ArmOpCode opCode,
+ int dest, int src1, int src2, int info)
+{
+ ArmLIR *insn = dvmCompilerNew(sizeof(ArmLIR), true);
+ assert(isPseudoOpCode(opCode) ||
+ (EncodingMap[opCode].flags & IS_QUAD_OP));
+ insn->opCode = opCode;
+ insn->operands[0] = dest;
+ insn->operands[1] = src1;
+ insn->operands[2] = src2;
+ insn->operands[3] = info;
+ setupResourceMasks(insn);
+ dvmCompilerAppendLIR(cUnit, (LIR *) insn);
+ return insn;
+}
+#endif
+
+/*
+ * If the next instruction is a move-result or move-result-long,
+ * return the target Dalvik sReg[s] and convert the next to a
+ * nop. Otherwise, return INVALID_SREG. Used to optimize method inlining.
+ */
+static RegLocation inlinedTarget(CompilationUnit *cUnit, MIR *mir,
+ bool fpHint)
+{
+ if (mir->next &&
+ ((mir->next->dalvikInsn.opCode == OP_MOVE_RESULT) ||
+ (mir->next->dalvikInsn.opCode == OP_MOVE_RESULT_OBJECT))) {
+ mir->next->dalvikInsn.opCode = OP_NOP;
+ return dvmCompilerGetDest(cUnit, mir->next, 0);
+ } else {
+ RegLocation res = LOC_DALVIK_RETURN_VAL;
+ res.fp = fpHint;
+ return res;
+ }
+}
+
+/*
+ * Search the existing constants in the literal pool for an exact or close match
+ * within specified delta (greater or equal to 0).
+ */
+static ArmLIR *scanLiteralPool(CompilationUnit *cUnit, int value,
+ unsigned int delta)
+{
+ LIR *dataTarget = cUnit->wordList;
+ while (dataTarget) {
+ if (((unsigned) (value - ((ArmLIR *) dataTarget)->operands[0])) <=
+ delta)
+ return (ArmLIR *) dataTarget;
+ dataTarget = dataTarget->next;
+ }
+ return NULL;
+}
+
+/*
+ * The following are building blocks to insert constants into the pool or
+ * instruction streams.
+ */
+
+/* Add a 32-bit constant either in the constant pool or mixed with code */
+static ArmLIR *addWordData(CompilationUnit *cUnit, int value, bool inPlace)
+{
+ /* Add the constant to the literal pool */
+ if (!inPlace) {
+ ArmLIR *newValue = dvmCompilerNew(sizeof(ArmLIR), true);
+ newValue->operands[0] = value;
+ newValue->generic.next = cUnit->wordList;
+ cUnit->wordList = (LIR *) newValue;
+ return newValue;
+ } else {
+ /* Add the constant in the middle of code stream */
+ newLIR1(cUnit, kArm16BitData, (value & 0xffff));
+ newLIR1(cUnit, kArm16BitData, (value >> 16));
+ }
+ return NULL;
+}
+
+static RegLocation inlinedTargetWide(CompilationUnit *cUnit, MIR *mir,
+ bool fpHint)
+{
+ if (mir->next &&
+ (mir->next->dalvikInsn.opCode == OP_MOVE_RESULT_WIDE)) {
+ mir->next->dalvikInsn.opCode = OP_NOP;
+ return dvmCompilerGetDestWide(cUnit, mir->next, 0, 1);
+ } else {
+ RegLocation res = LOC_DALVIK_RETURN_VAL_WIDE;
+ res.fp = fpHint;
+ return res;
+ }
+}
+
+
+/*
+ * Generate an kArmPseudoBarrier marker to indicate the boundary of special
+ * blocks.
+ */
+static void genBarrier(CompilationUnit *cUnit)
+{
+ ArmLIR *barrier = newLIR0(cUnit, kArmPseudoBarrier);
+ /* Mark all resources as being clobbered */
+ barrier->defMask = -1;
+}
+
+/* Create the PC reconstruction slot if not already done */
+extern ArmLIR *genCheckCommon(CompilationUnit *cUnit, int dOffset,
+ ArmLIR *branch,
+ ArmLIR *pcrLabel)
+{
+ /* Forget all def info (because we might rollback here. Bug #2367397 */
+ dvmCompilerResetDefTracking(cUnit);
+
+ /* Set up the place holder to reconstruct this Dalvik PC */
+ if (pcrLabel == NULL) {
+ int dPC = (int) (cUnit->method->insns + dOffset);
+ pcrLabel = dvmCompilerNew(sizeof(ArmLIR), true);
+ pcrLabel->opCode = kArmPseudoPCReconstructionCell;
+ pcrLabel->operands[0] = dPC;
+ pcrLabel->operands[1] = dOffset;
+ /* Insert the place holder to the growable list */
+ dvmInsertGrowableList(&cUnit->pcReconstructionList, pcrLabel);
+ }
+ /* Branch to the PC reconstruction code */
+ branch->generic.target = (LIR *) pcrLabel;
+ return pcrLabel;
+}
diff --git a/vm/compiler/codegen/arm/CodegenDriver.c b/vm/compiler/codegen/arm/CodegenDriver.c
new file mode 100644
index 0000000..011679b
--- /dev/null
+++ b/vm/compiler/codegen/arm/CodegenDriver.c
@@ -0,0 +1,4482 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file contains codegen and support common to all supported
+ * ARM variants. It is included by:
+ *
+ * Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ * which combines this common code with specific support found in the
+ * applicable directory below this one.
+ */
+
+/*
+ * Mark garbage collection card. Skip if the value we're storing is null.
+ */
+static void markCard(CompilationUnit *cUnit, int valReg, int tgtAddrReg)
+{
+ int regCardBase = dvmCompilerAllocTemp(cUnit);
+ int regCardNo = dvmCompilerAllocTemp(cUnit);
+ opRegImm(cUnit, kOpCmp, valReg, 0); /* storing null? */
+ ArmLIR *branchOver = opCondBranch(cUnit, kArmCondEq);
+ loadWordDisp(cUnit, rGLUE, offsetof(InterpState, cardTable),
+ regCardBase);
+ opRegRegImm(cUnit, kOpLsr, regCardNo, tgtAddrReg, GC_CARD_SHIFT);
+ storeBaseIndexed(cUnit, regCardBase, regCardNo, regCardBase, 0,
+ kUnsignedByte);
+ ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+ target->defMask = ENCODE_ALL;
+ branchOver->generic.target = (LIR *)target;
+ dvmCompilerFreeTemp(cUnit, regCardBase);
+ dvmCompilerFreeTemp(cUnit, regCardNo);
+}
+
+static bool genConversionCall(CompilationUnit *cUnit, MIR *mir, void *funct,
+ int srcSize, int tgtSize)
+{
+ /*
+ * Don't optimize the register usage since it calls out to template
+ * functions
+ */
+ RegLocation rlSrc;
+ RegLocation rlDest;
+ dvmCompilerFlushAllRegs(cUnit); /* Send everything to home location */
+ if (srcSize == 1) {
+ rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+ loadValueDirectFixed(cUnit, rlSrc, r0);
+ } else {
+ rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+ loadValueDirectWideFixed(cUnit, rlSrc, r0, r1);
+ }
+ LOAD_FUNC_ADDR(cUnit, r2, (int)funct);
+ opReg(cUnit, kOpBlx, r2);
+ dvmCompilerClobberCallRegs(cUnit);
+ if (tgtSize == 1) {
+ RegLocation rlResult;
+ rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+ rlResult = dvmCompilerGetReturn(cUnit);
+ storeValue(cUnit, rlDest, rlResult);
+ } else {
+ RegLocation rlResult;
+ rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+ rlResult = dvmCompilerGetReturnWide(cUnit);
+ storeValueWide(cUnit, rlDest, rlResult);
+ }
+ return false;
+}
+
+static bool genArithOpFloatPortable(CompilationUnit *cUnit, MIR *mir,
+ RegLocation rlDest, RegLocation rlSrc1,
+ RegLocation rlSrc2)
+{
+ RegLocation rlResult;
+ void* funct;
+
+ switch (mir->dalvikInsn.opCode) {
+ case OP_ADD_FLOAT_2ADDR:
+ case OP_ADD_FLOAT:
+ funct = (void*) __aeabi_fadd;
+ break;
+ case OP_SUB_FLOAT_2ADDR:
+ case OP_SUB_FLOAT:
+ funct = (void*) __aeabi_fsub;
+ break;
+ case OP_DIV_FLOAT_2ADDR:
+ case OP_DIV_FLOAT:
+ funct = (void*) __aeabi_fdiv;
+ break;
+ case OP_MUL_FLOAT_2ADDR:
+ case OP_MUL_FLOAT:
+ funct = (void*) __aeabi_fmul;
+ break;
+ case OP_REM_FLOAT_2ADDR:
+ case OP_REM_FLOAT:
+ funct = (void*) fmodf;
+ break;
+ case OP_NEG_FLOAT: {
+ genNegFloat(cUnit, rlDest, rlSrc1);
+ return false;
+ }
+ default:
+ return true;
+ }
+ dvmCompilerFlushAllRegs(cUnit); /* Send everything to home location */
+ loadValueDirectFixed(cUnit, rlSrc1, r0);
+ loadValueDirectFixed(cUnit, rlSrc2, r1);
+ LOAD_FUNC_ADDR(cUnit, r2, (int)funct);
+ opReg(cUnit, kOpBlx, r2);
+ dvmCompilerClobberCallRegs(cUnit);
+ rlResult = dvmCompilerGetReturn(cUnit);
+ storeValue(cUnit, rlDest, rlResult);
+ return false;
+}
+
+static bool genArithOpDoublePortable(CompilationUnit *cUnit, MIR *mir,
+ RegLocation rlDest, RegLocation rlSrc1,
+ RegLocation rlSrc2)
+{
+ RegLocation rlResult;
+ void* funct;
+
+ switch (mir->dalvikInsn.opCode) {
+ case OP_ADD_DOUBLE_2ADDR:
+ case OP_ADD_DOUBLE:
+ funct = (void*) __aeabi_dadd;
+ break;
+ case OP_SUB_DOUBLE_2ADDR:
+ case OP_SUB_DOUBLE:
+ funct = (void*) __aeabi_dsub;
+ break;
+ case OP_DIV_DOUBLE_2ADDR:
+ case OP_DIV_DOUBLE:
+ funct = (void*) __aeabi_ddiv;
+ break;
+ case OP_MUL_DOUBLE_2ADDR:
+ case OP_MUL_DOUBLE:
+ funct = (void*) __aeabi_dmul;
+ break;
+ case OP_REM_DOUBLE_2ADDR:
+ case OP_REM_DOUBLE:
+ funct = (void*) fmod;
+ break;
+ case OP_NEG_DOUBLE: {
+ genNegDouble(cUnit, rlDest, rlSrc1);
+ return false;
+ }
+ default:
+ return true;
+ }
+ dvmCompilerFlushAllRegs(cUnit); /* Send everything to home location */
+ LOAD_FUNC_ADDR(cUnit, rlr, (int)funct);
+ loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
+ loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
+ opReg(cUnit, kOpBlx, rlr);
+ dvmCompilerClobberCallRegs(cUnit);
+ rlResult = dvmCompilerGetReturnWide(cUnit);
+ storeValueWide(cUnit, rlDest, rlResult);
+ return false;
+}
+
+static bool genConversionPortable(CompilationUnit *cUnit, MIR *mir)
+{
+ OpCode opCode = mir->dalvikInsn.opCode;
+
+ switch (opCode) {
+ case OP_INT_TO_FLOAT:
+ return genConversionCall(cUnit, mir, (void*)__aeabi_i2f, 1, 1);
+ case OP_FLOAT_TO_INT:
+ return genConversionCall(cUnit, mir, (void*)__aeabi_f2iz, 1, 1);
+ case OP_DOUBLE_TO_FLOAT:
+ return genConversionCall(cUnit, mir, (void*)__aeabi_d2f, 2, 1);
+ case OP_FLOAT_TO_DOUBLE:
+ return genConversionCall(cUnit, mir, (void*)__aeabi_f2d, 1, 2);
+ case OP_INT_TO_DOUBLE:
+ return genConversionCall(cUnit, mir, (void*)__aeabi_i2d, 1, 2);
+ case OP_DOUBLE_TO_INT:
+ return genConversionCall(cUnit, mir, (void*)__aeabi_d2iz, 2, 1);
+ case OP_FLOAT_TO_LONG:
+ return genConversionCall(cUnit, mir, (void*)dvmJitf2l, 1, 2);
+ case OP_LONG_TO_FLOAT:
+ return genConversionCall(cUnit, mir, (void*)__aeabi_l2f, 2, 1);
+ case OP_DOUBLE_TO_LONG:
+ return genConversionCall(cUnit, mir, (void*)dvmJitd2l, 2, 2);
+ case OP_LONG_TO_DOUBLE:
+ return genConversionCall(cUnit, mir, (void*)__aeabi_l2d, 2, 2);
+ default:
+ return true;
+ }
+ return false;
+}
+
+#if defined(WITH_SELF_VERIFICATION)
+static void selfVerificationBranchInsert(LIR *currentLIR, ArmOpCode opCode,
+ int dest, int src1)
+{
+ ArmLIR *insn = dvmCompilerNew(sizeof(ArmLIR), true);
+ insn->opCode = opCode;
+ insn->operands[0] = dest;
+ insn->operands[1] = src1;
+ setupResourceMasks(insn);
+ dvmCompilerInsertLIRBefore(currentLIR, (LIR *) insn);
+}
+
+static void selfVerificationBranchInsertPass(CompilationUnit *cUnit)
+{
+ ArmLIR *thisLIR;
+ TemplateOpCode opCode = TEMPLATE_MEM_OP_DECODE;
+
+ for (thisLIR = (ArmLIR *) cUnit->firstLIRInsn;
+ thisLIR != (ArmLIR *) cUnit->lastLIRInsn;
+ thisLIR = NEXT_LIR(thisLIR)) {
+ if (thisLIR->branchInsertSV) {
+ /* Branch to mem op decode template */
+ selfVerificationBranchInsert((LIR *) thisLIR, kThumbBlx1,
+ (int) gDvmJit.codeCache + templateEntryOffsets[opCode],
+ (int) gDvmJit.codeCache + templateEntryOffsets[opCode]);
+ selfVerificationBranchInsert((LIR *) thisLIR, kThumbBlx2,
+ (int) gDvmJit.codeCache + templateEntryOffsets[opCode],
+ (int) gDvmJit.codeCache + templateEntryOffsets[opCode]);
+ }
+ }
+}
+#endif
+
+/* Generate conditional branch instructions */
+static ArmLIR *genConditionalBranch(CompilationUnit *cUnit,
+ ArmConditionCode cond,
+ ArmLIR *target)
+{
+ ArmLIR *branch = opCondBranch(cUnit, cond);
+ branch->generic.target = (LIR *) target;
+ return branch;
+}
+
+/* Generate a unconditional branch to go to the interpreter */
+static inline ArmLIR *genTrap(CompilationUnit *cUnit, int dOffset,
+ ArmLIR *pcrLabel)
+{
+ ArmLIR *branch = opNone(cUnit, kOpUncondBr);
+ return genCheckCommon(cUnit, dOffset, branch, pcrLabel);
+}
+
+/* Load a wide field from an object instance */
+static void genIGetWide(CompilationUnit *cUnit, MIR *mir, int fieldOffset)
+{
+ RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 0);
+ RegLocation rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+ RegLocation rlResult;
+ rlObj = loadValue(cUnit, rlObj, kCoreReg);
+ int regPtr = dvmCompilerAllocTemp(cUnit);
+
+ assert(rlDest.wide);
+
+ genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
+ NULL);/* null object? */
+ opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset);
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true);
+
+ HEAP_ACCESS_SHADOW(true);
+ loadPair(cUnit, regPtr, rlResult.lowReg, rlResult.highReg);
+ HEAP_ACCESS_SHADOW(false);
+
+ dvmCompilerFreeTemp(cUnit, regPtr);
+ storeValueWide(cUnit, rlDest, rlResult);
+}
+
+/* Store a wide field to an object instance */
+static void genIPutWide(CompilationUnit *cUnit, MIR *mir, int fieldOffset)
+{
+ RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+ RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 2);
+ rlObj = loadValue(cUnit, rlObj, kCoreReg);
+ int regPtr;
+ rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg);
+ genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
+ NULL);/* null object? */
+ regPtr = dvmCompilerAllocTemp(cUnit);
+ opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset);
+
+ HEAP_ACCESS_SHADOW(true);
+ storePair(cUnit, regPtr, rlSrc.lowReg, rlSrc.highReg);
+ HEAP_ACCESS_SHADOW(false);
+
+ dvmCompilerFreeTemp(cUnit, regPtr);
+}
+
+/*
+ * Load a field from an object instance
+ *
+ */
+static void genIGet(CompilationUnit *cUnit, MIR *mir, OpSize size,
+ int fieldOffset, bool isVolatile)
+{
+ RegLocation rlResult;
+ RegisterClass regClass = dvmCompilerRegClassBySize(size);
+ RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 0);
+ RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+ rlObj = loadValue(cUnit, rlObj, kCoreReg);
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, regClass, true);
+ genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
+ NULL);/* null object? */
+
+ HEAP_ACCESS_SHADOW(true);
+ loadBaseDisp(cUnit, mir, rlObj.lowReg, fieldOffset, rlResult.lowReg,
+ size, rlObj.sRegLow);
+ HEAP_ACCESS_SHADOW(false);
+ if (isVolatile) {
+ dvmCompilerGenMemBarrier(cUnit);
+ }
+
+ storeValue(cUnit, rlDest, rlResult);
+}
+
+/*
+ * Store a field to an object instance
+ *
+ */
+static void genIPut(CompilationUnit *cUnit, MIR *mir, OpSize size,
+ int fieldOffset, bool isObject, bool isVolatile)
+{
+ RegisterClass regClass = dvmCompilerRegClassBySize(size);
+ RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+ RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 1);
+ rlObj = loadValue(cUnit, rlObj, kCoreReg);
+ rlSrc = loadValue(cUnit, rlSrc, regClass);
+ genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
+ NULL);/* null object? */
+
+ if (isVolatile) {
+ dvmCompilerGenMemBarrier(cUnit);
+ }
+ HEAP_ACCESS_SHADOW(true);
+ storeBaseDisp(cUnit, rlObj.lowReg, fieldOffset, rlSrc.lowReg, size);
+ HEAP_ACCESS_SHADOW(false);
+ if (isObject) {
+ /* NOTE: marking card based on object head */
+ markCard(cUnit, rlSrc.lowReg, rlObj.lowReg);
+ }
+}
+
+
+/*
+ * Generate array load
+ */
+static void genArrayGet(CompilationUnit *cUnit, MIR *mir, OpSize size,
+ RegLocation rlArray, RegLocation rlIndex,
+ RegLocation rlDest, int scale)
+{
+ RegisterClass regClass = dvmCompilerRegClassBySize(size);
+ int lenOffset = offsetof(ArrayObject, length);
+ int dataOffset = offsetof(ArrayObject, contents);
+ RegLocation rlResult;
+ rlArray = loadValue(cUnit, rlArray, kCoreReg);
+ rlIndex = loadValue(cUnit, rlIndex, kCoreReg);
+ int regPtr;
+
+ /* null object? */
+ ArmLIR * pcrLabel = NULL;
+
+ if (!(mir->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) {
+ pcrLabel = genNullCheck(cUnit, rlArray.sRegLow,
+ rlArray.lowReg, mir->offset, NULL);
+ }
+
+ regPtr = dvmCompilerAllocTemp(cUnit);
+
+ if (!(mir->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
+ int regLen = dvmCompilerAllocTemp(cUnit);
+ /* Get len */
+ loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);
+ /* regPtr -> array data */
+ opRegRegImm(cUnit, kOpAdd, regPtr, rlArray.lowReg, dataOffset);
+ genBoundsCheck(cUnit, rlIndex.lowReg, regLen, mir->offset,
+ pcrLabel);
+ dvmCompilerFreeTemp(cUnit, regLen);
+ } else {
+ /* regPtr -> array data */
+ opRegRegImm(cUnit, kOpAdd, regPtr, rlArray.lowReg, dataOffset);
+ }
+ if ((size == kLong) || (size == kDouble)) {
+ if (scale) {
+ int rNewIndex = dvmCompilerAllocTemp(cUnit);
+ opRegRegImm(cUnit, kOpLsl, rNewIndex, rlIndex.lowReg, scale);
+ opRegReg(cUnit, kOpAdd, regPtr, rNewIndex);
+ dvmCompilerFreeTemp(cUnit, rNewIndex);
+ } else {
+ opRegReg(cUnit, kOpAdd, regPtr, rlIndex.lowReg);
+ }
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, regClass, true);
+
+ HEAP_ACCESS_SHADOW(true);
+ loadPair(cUnit, regPtr, rlResult.lowReg, rlResult.highReg);
+ HEAP_ACCESS_SHADOW(false);
+
+ dvmCompilerFreeTemp(cUnit, regPtr);
+ storeValueWide(cUnit, rlDest, rlResult);
+ } else {
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, regClass, true);
+
+ HEAP_ACCESS_SHADOW(true);
+ loadBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlResult.lowReg,
+ scale, size);
+ HEAP_ACCESS_SHADOW(false);
+
+ dvmCompilerFreeTemp(cUnit, regPtr);
+ storeValue(cUnit, rlDest, rlResult);
+ }
+}
+
+/*
+ * Generate array store
+ *
+ */
+static void genArrayPut(CompilationUnit *cUnit, MIR *mir, OpSize size,
+ RegLocation rlArray, RegLocation rlIndex,
+ RegLocation rlSrc, int scale)
+{
+ RegisterClass regClass = dvmCompilerRegClassBySize(size);
+ int lenOffset = offsetof(ArrayObject, length);
+ int dataOffset = offsetof(ArrayObject, contents);
+
+ int regPtr;
+ rlArray = loadValue(cUnit, rlArray, kCoreReg);
+ rlIndex = loadValue(cUnit, rlIndex, kCoreReg);
+
+ if (dvmCompilerIsTemp(cUnit, rlArray.lowReg)) {
+ dvmCompilerClobber(cUnit, rlArray.lowReg);
+ regPtr = rlArray.lowReg;
+ } else {
+ regPtr = dvmCompilerAllocTemp(cUnit);
+ genRegCopy(cUnit, regPtr, rlArray.lowReg);
+ }
+
+ /* null object? */
+ ArmLIR * pcrLabel = NULL;
+
+ if (!(mir->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) {
+ pcrLabel = genNullCheck(cUnit, rlArray.sRegLow, rlArray.lowReg,
+ mir->offset, NULL);
+ }
+
+ if (!(mir->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
+ int regLen = dvmCompilerAllocTemp(cUnit);
+ //NOTE: max live temps(4) here.
+ /* Get len */
+ loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);
+ /* regPtr -> array data */
+ opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
+ genBoundsCheck(cUnit, rlIndex.lowReg, regLen, mir->offset,
+ pcrLabel);
+ dvmCompilerFreeTemp(cUnit, regLen);
+ } else {
+ /* regPtr -> array data */
+ opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
+ }
+ /* at this point, regPtr points to array, 2 live temps */
+ if ((size == kLong) || (size == kDouble)) {
+ //TODO: need specific wide routine that can handle fp regs
+ if (scale) {
+ int rNewIndex = dvmCompilerAllocTemp(cUnit);
+ opRegRegImm(cUnit, kOpLsl, rNewIndex, rlIndex.lowReg, scale);
+ opRegReg(cUnit, kOpAdd, regPtr, rNewIndex);
+ dvmCompilerFreeTemp(cUnit, rNewIndex);
+ } else {
+ opRegReg(cUnit, kOpAdd, regPtr, rlIndex.lowReg);
+ }
+ rlSrc = loadValueWide(cUnit, rlSrc, regClass);
+
+ HEAP_ACCESS_SHADOW(true);
+ storePair(cUnit, regPtr, rlSrc.lowReg, rlSrc.highReg);
+ HEAP_ACCESS_SHADOW(false);
+
+ dvmCompilerFreeTemp(cUnit, regPtr);
+ } else {
+ rlSrc = loadValue(cUnit, rlSrc, regClass);
+
+ HEAP_ACCESS_SHADOW(true);
+ storeBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlSrc.lowReg,
+ scale, size);
+ HEAP_ACCESS_SHADOW(false);
+ }
+}
+
+/*
+ * Generate array object store
+ * Must use explicit register allocation here because of
+ * call-out to dvmCanPutArrayElement
+ */
+static void genArrayObjectPut(CompilationUnit *cUnit, MIR *mir,
+ RegLocation rlArray, RegLocation rlIndex,
+ RegLocation rlSrc, int scale)
+{
+ int lenOffset = offsetof(ArrayObject, length);
+ int dataOffset = offsetof(ArrayObject, contents);
+
+ dvmCompilerFlushAllRegs(cUnit);
+
+ int regLen = r0;
+ int regPtr = r4PC; /* Preserved across call */
+ int regArray = r1;
+ int regIndex = r7; /* Preserved across call */
+
+ loadValueDirectFixed(cUnit, rlArray, regArray);
+ loadValueDirectFixed(cUnit, rlIndex, regIndex);
+
+ /* null object? */
+ ArmLIR * pcrLabel = NULL;
+
+ if (!(mir->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) {
+ pcrLabel = genNullCheck(cUnit, rlArray.sRegLow, regArray,
+ mir->offset, NULL);
+ }
+
+ if (!(mir->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
+ /* Get len */
+ loadWordDisp(cUnit, regArray, lenOffset, regLen);
+ /* regPtr -> array data */
+ opRegRegImm(cUnit, kOpAdd, regPtr, regArray, dataOffset);
+ genBoundsCheck(cUnit, regIndex, regLen, mir->offset,
+ pcrLabel);
+ } else {
+ /* regPtr -> array data */
+ opRegRegImm(cUnit, kOpAdd, regPtr, regArray, dataOffset);
+ }
+
+ /* Get object to store */
+ loadValueDirectFixed(cUnit, rlSrc, r0);
+ LOAD_FUNC_ADDR(cUnit, r2, (int)dvmCanPutArrayElement);
+
+ /* Are we storing null? If so, avoid check */
+ opRegImm(cUnit, kOpCmp, r0, 0);
+ ArmLIR *branchOver = opCondBranch(cUnit, kArmCondEq);
+
+ /* Make sure the types are compatible */
+ loadWordDisp(cUnit, regArray, offsetof(Object, clazz), r1);
+ loadWordDisp(cUnit, r0, offsetof(Object, clazz), r0);
+ opReg(cUnit, kOpBlx, r2);
+ dvmCompilerClobberCallRegs(cUnit);
+
+ /*
+ * Using fixed registers here, and counting on r4 and r7 being
+ * preserved across the above call. Tell the register allocation
+ * utilities about the regs we are using directly
+ */
+ dvmCompilerLockTemp(cUnit, regPtr); // r4PC
+ dvmCompilerLockTemp(cUnit, regIndex); // r7
+ dvmCompilerLockTemp(cUnit, r0);
+ dvmCompilerLockTemp(cUnit, r1);
+
+ /* Bad? - roll back and re-execute if so */
+ genRegImmCheck(cUnit, kArmCondEq, r0, 0, mir->offset, pcrLabel);
+
+ /* Resume here - must reload element & array, regPtr & index preserved */
+ loadValueDirectFixed(cUnit, rlSrc, r0);
+ loadValueDirectFixed(cUnit, rlArray, r1);
+
+ ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+ target->defMask = ENCODE_ALL;
+ branchOver->generic.target = (LIR *) target;
+
+ HEAP_ACCESS_SHADOW(true);
+ storeBaseIndexed(cUnit, regPtr, regIndex, r0,
+ scale, kWord);
+ HEAP_ACCESS_SHADOW(false);
+
+ dvmCompilerFreeTemp(cUnit, regPtr);
+ dvmCompilerFreeTemp(cUnit, regIndex);
+
+ /* NOTE: marking card here based on object head */
+ markCard(cUnit, r0, r1);
+}
+
+static bool genShiftOpLong(CompilationUnit *cUnit, MIR *mir,
+ RegLocation rlDest, RegLocation rlSrc1,
+ RegLocation rlShift)
+{
+ /*
+ * Don't mess with the regsiters here as there is a particular calling
+ * convention to the out-of-line handler.
+ */
+ RegLocation rlResult;
+
+ loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
+ loadValueDirect(cUnit, rlShift, r2);
+ switch( mir->dalvikInsn.opCode) {
+ case OP_SHL_LONG:
+ case OP_SHL_LONG_2ADDR:
+ genDispatchToHandler(cUnit, TEMPLATE_SHL_LONG);
+ break;
+ case OP_SHR_LONG:
+ case OP_SHR_LONG_2ADDR:
+ genDispatchToHandler(cUnit, TEMPLATE_SHR_LONG);
+ break;
+ case OP_USHR_LONG:
+ case OP_USHR_LONG_2ADDR:
+ genDispatchToHandler(cUnit, TEMPLATE_USHR_LONG);
+ break;
+ default:
+ return true;
+ }
+ rlResult = dvmCompilerGetReturnWide(cUnit);
+ storeValueWide(cUnit, rlDest, rlResult);
+ return false;
+}
+
+static bool genArithOpLong(CompilationUnit *cUnit, MIR *mir,
+ RegLocation rlDest, RegLocation rlSrc1,
+ RegLocation rlSrc2)
+{
+ RegLocation rlResult;
+ OpKind firstOp = kOpBkpt;
+ OpKind secondOp = kOpBkpt;
+ bool callOut = false;
+ void *callTgt;
+ int retReg = r0;
+
+ switch (mir->dalvikInsn.opCode) {
+ case OP_NOT_LONG:
+ rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+ opRegReg(cUnit, kOpMvn, rlResult.lowReg, rlSrc2.lowReg);
+ opRegReg(cUnit, kOpMvn, rlResult.highReg, rlSrc2.highReg);
+ storeValueWide(cUnit, rlDest, rlResult);
+ return false;
+ break;
+ case OP_ADD_LONG:
+ case OP_ADD_LONG_2ADDR:
+ firstOp = kOpAdd;
+ secondOp = kOpAdc;
+ break;
+ case OP_SUB_LONG:
+ case OP_SUB_LONG_2ADDR:
+ firstOp = kOpSub;
+ secondOp = kOpSbc;
+ break;
+ case OP_MUL_LONG:
+ case OP_MUL_LONG_2ADDR:
+ genMulLong(cUnit, rlDest, rlSrc1, rlSrc2);
+ return false;
+ case OP_DIV_LONG:
+ case OP_DIV_LONG_2ADDR:
+ callOut = true;
+ retReg = r0;
+ callTgt = (void*)__aeabi_ldivmod;
+ break;
+ /* NOTE - result is in r2/r3 instead of r0/r1 */
+ case OP_REM_LONG:
+ case OP_REM_LONG_2ADDR:
+ callOut = true;
+ callTgt = (void*)__aeabi_ldivmod;
+ retReg = r2;
+ break;
+ case OP_AND_LONG_2ADDR:
+ case OP_AND_LONG:
+ firstOp = kOpAnd;
+ secondOp = kOpAnd;
+ break;
+ case OP_OR_LONG:
+ case OP_OR_LONG_2ADDR:
+ firstOp = kOpOr;
+ secondOp = kOpOr;
+ break;
+ case OP_XOR_LONG:
+ case OP_XOR_LONG_2ADDR:
+ firstOp = kOpXor;
+ secondOp = kOpXor;
+ break;
+ case OP_NEG_LONG: {
+ //TUNING: can improve this using Thumb2 code
+ int tReg = dvmCompilerAllocTemp(cUnit);
+ rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+ loadConstantNoClobber(cUnit, tReg, 0);
+ opRegRegReg(cUnit, kOpSub, rlResult.lowReg,
+ tReg, rlSrc2.lowReg);
+ opRegReg(cUnit, kOpSbc, tReg, rlSrc2.highReg);
+ genRegCopy(cUnit, rlResult.highReg, tReg);
+ storeValueWide(cUnit, rlDest, rlResult);
+ return false;
+ }
+ default:
+ LOGE("Invalid long arith op");
+ dvmCompilerAbort(cUnit);
+ }
+ if (!callOut) {
+ genLong3Addr(cUnit, mir, firstOp, secondOp, rlDest, rlSrc1, rlSrc2);
+ } else {
+ // Adjust return regs in to handle case of rem returning r2/r3
+ dvmCompilerFlushAllRegs(cUnit); /* Send everything to home location */
+ loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
+ LOAD_FUNC_ADDR(cUnit, rlr, (int) callTgt);
+ loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
+ opReg(cUnit, kOpBlx, rlr);
+ dvmCompilerClobberCallRegs(cUnit);
+ if (retReg == r0)
+ rlResult = dvmCompilerGetReturnWide(cUnit);
+ else
+ rlResult = dvmCompilerGetReturnWideAlt(cUnit);
+ storeValueWide(cUnit, rlDest, rlResult);
+ }
+ return false;
+}
+
+static bool genArithOpInt(CompilationUnit *cUnit, MIR *mir,
+ RegLocation rlDest, RegLocation rlSrc1,
+ RegLocation rlSrc2)
+{
+ OpKind op = kOpBkpt;
+ bool callOut = false;
+ bool checkZero = false;
+ bool unary = false;
+ int retReg = r0;
+ void *callTgt;
+ RegLocation rlResult;
+ bool shiftOp = false;
+
+ switch (mir->dalvikInsn.opCode) {
+ case OP_NEG_INT:
+ op = kOpNeg;
+ unary = true;
+ break;
+ case OP_NOT_INT:
+ op = kOpMvn;
+ unary = true;
+ break;
+ case OP_ADD_INT:
+ case OP_ADD_INT_2ADDR:
+ op = kOpAdd;
+ break;
+ case OP_SUB_INT:
+ case OP_SUB_INT_2ADDR:
+ op = kOpSub;
+ break;
+ case OP_MUL_INT:
+ case OP_MUL_INT_2ADDR:
+ op = kOpMul;
+ break;
+ case OP_DIV_INT:
+ case OP_DIV_INT_2ADDR:
+ callOut = true;
+ checkZero = true;
+ callTgt = __aeabi_idiv;
+ retReg = r0;
+ break;
+ /* NOTE: returns in r1 */
+ case OP_REM_INT:
+ case OP_REM_INT_2ADDR:
+ callOut = true;
+ checkZero = true;
+ callTgt = __aeabi_idivmod;
+ retReg = r1;
+ break;
+ case OP_AND_INT:
+ case OP_AND_INT_2ADDR:
+ op = kOpAnd;
+ break;
+ case OP_OR_INT:
+ case OP_OR_INT_2ADDR:
+ op = kOpOr;
+ break;
+ case OP_XOR_INT:
+ case OP_XOR_INT_2ADDR:
+ op = kOpXor;
+ break;
+ case OP_SHL_INT:
+ case OP_SHL_INT_2ADDR:
+ shiftOp = true;
+ op = kOpLsl;
+ break;
+ case OP_SHR_INT:
+ case OP_SHR_INT_2ADDR:
+ shiftOp = true;
+ op = kOpAsr;
+ break;
+ case OP_USHR_INT:
+ case OP_USHR_INT_2ADDR:
+ shiftOp = true;
+ op = kOpLsr;
+ break;
+ default:
+ LOGE("Invalid word arith op: 0x%x(%d)",
+ mir->dalvikInsn.opCode, mir->dalvikInsn.opCode);
+ dvmCompilerAbort(cUnit);
+ }
+ if (!callOut) {
+ rlSrc1 = loadValue(cUnit, rlSrc1, kCoreReg);
+ if (unary) {
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+ opRegReg(cUnit, op, rlResult.lowReg,
+ rlSrc1.lowReg);
+ } else {
+ rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg);
+ if (shiftOp) {
+ int tReg = dvmCompilerAllocTemp(cUnit);
+ opRegRegImm(cUnit, kOpAnd, tReg, rlSrc2.lowReg, 31);
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+ opRegRegReg(cUnit, op, rlResult.lowReg,
+ rlSrc1.lowReg, tReg);
+ dvmCompilerFreeTemp(cUnit, tReg);
+ } else {
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+ opRegRegReg(cUnit, op, rlResult.lowReg,
+ rlSrc1.lowReg, rlSrc2.lowReg);
+ }
+ }
+ storeValue(cUnit, rlDest, rlResult);
+ } else {
+ RegLocation rlResult;
+ dvmCompilerFlushAllRegs(cUnit); /* Send everything to home location */
+ loadValueDirectFixed(cUnit, rlSrc2, r1);
+ LOAD_FUNC_ADDR(cUnit, r2, (int) callTgt);
+ loadValueDirectFixed(cUnit, rlSrc1, r0);
+ if (checkZero) {
+ genNullCheck(cUnit, rlSrc2.sRegLow, r1, mir->offset, NULL);
+ }
+ opReg(cUnit, kOpBlx, r2);
+ dvmCompilerClobberCallRegs(cUnit);
+ if (retReg == r0)
+ rlResult = dvmCompilerGetReturn(cUnit);
+ else
+ rlResult = dvmCompilerGetReturnAlt(cUnit);
+ storeValue(cUnit, rlDest, rlResult);
+ }
+ return false;
+}
+
+static bool genArithOp(CompilationUnit *cUnit, MIR *mir)
+{
+ OpCode opCode = mir->dalvikInsn.opCode;
+ RegLocation rlDest;
+ RegLocation rlSrc1;
+ RegLocation rlSrc2;
+ /* Deduce sizes of operands */
+ if (mir->ssaRep->numUses == 2) {
+ rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 0);
+ rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 1);
+ } else if (mir->ssaRep->numUses == 3) {
+ rlSrc1 = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+ rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 2);
+ } else {
+ rlSrc1 = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+ rlSrc2 = dvmCompilerGetSrcWide(cUnit, mir, 2, 3);
+ assert(mir->ssaRep->numUses == 4);
+ }
+ if (mir->ssaRep->numDefs == 1) {
+ rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+ } else {
+ assert(mir->ssaRep->numDefs == 2);
+ rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+ }
+
+ if ((opCode >= OP_ADD_LONG_2ADDR) && (opCode <= OP_XOR_LONG_2ADDR)) {
+ return genArithOpLong(cUnit,mir, rlDest, rlSrc1, rlSrc2);
+ }
+ if ((opCode >= OP_ADD_LONG) && (opCode <= OP_XOR_LONG)) {
+ return genArithOpLong(cUnit,mir, rlDest, rlSrc1, rlSrc2);
+ }
+ if ((opCode >= OP_SHL_LONG_2ADDR) && (opCode <= OP_USHR_LONG_2ADDR)) {
+ return genShiftOpLong(cUnit,mir, rlDest, rlSrc1, rlSrc2);
+ }
+ if ((opCode >= OP_SHL_LONG) && (opCode <= OP_USHR_LONG)) {
+ return genShiftOpLong(cUnit,mir, rlDest, rlSrc1, rlSrc2);
+ }
+ if ((opCode >= OP_ADD_INT_2ADDR) && (opCode <= OP_USHR_INT_2ADDR)) {
+ return genArithOpInt(cUnit,mir, rlDest, rlSrc1, rlSrc2);
+ }
+ if ((opCode >= OP_ADD_INT) && (opCode <= OP_USHR_INT)) {
+ return genArithOpInt(cUnit,mir, rlDest, rlSrc1, rlSrc2);
+ }
+ if ((opCode >= OP_ADD_FLOAT_2ADDR) && (opCode <= OP_REM_FLOAT_2ADDR)) {
+ return genArithOpFloat(cUnit,mir, rlDest, rlSrc1, rlSrc2);
+ }
+ if ((opCode >= OP_ADD_FLOAT) && (opCode <= OP_REM_FLOAT)) {
+ return genArithOpFloat(cUnit, mir, rlDest, rlSrc1, rlSrc2);
+ }
+ if ((opCode >= OP_ADD_DOUBLE_2ADDR) && (opCode <= OP_REM_DOUBLE_2ADDR)) {
+ return genArithOpDouble(cUnit,mir, rlDest, rlSrc1, rlSrc2);
+ }
+ if ((opCode >= OP_ADD_DOUBLE) && (opCode <= OP_REM_DOUBLE)) {
+ return genArithOpDouble(cUnit,mir, rlDest, rlSrc1, rlSrc2);
+ }
+ return true;
+}
+
+/* Generate unconditional branch instructions */
+static ArmLIR *genUnconditionalBranch(CompilationUnit *cUnit, ArmLIR *target)
+{
+ ArmLIR *branch = opNone(cUnit, kOpUncondBr);
+ branch->generic.target = (LIR *) target;
+ return branch;
+}
+
+/* Perform the actual operation for OP_RETURN_* */
+static void genReturnCommon(CompilationUnit *cUnit, MIR *mir)
+{
+ genDispatchToHandler(cUnit, TEMPLATE_RETURN);
+#if defined(WITH_JIT_TUNING)
+ gDvmJit.returnOp++;
+#endif
+ int dPC = (int) (cUnit->method->insns + mir->offset);
+ /* Insert branch, but defer setting of target */
+ ArmLIR *branch = genUnconditionalBranch(cUnit, NULL);
+ /* Set up the place holder to reconstruct this Dalvik PC */
+ ArmLIR *pcrLabel = dvmCompilerNew(sizeof(ArmLIR), true);
+ pcrLabel->opCode = kArmPseudoPCReconstructionCell;
+ pcrLabel->operands[0] = dPC;
+ pcrLabel->operands[1] = mir->offset;
+ /* Insert the place holder to the growable list */
+ dvmInsertGrowableList(&cUnit->pcReconstructionList, pcrLabel);
+ /* Branch to the PC reconstruction code */
+ branch->generic.target = (LIR *) pcrLabel;
+}
+
+static void genProcessArgsNoRange(CompilationUnit *cUnit, MIR *mir,
+ DecodedInstruction *dInsn,
+ ArmLIR **pcrLabel)
+{
+ unsigned int i;
+ unsigned int regMask = 0;
+ RegLocation rlArg;
+ int numDone = 0;
+
+ /*
+ * Load arguments to r0..r4. Note that these registers may contain
+ * live values, so we clobber them immediately after loading to prevent
+ * them from being used as sources for subsequent loads.
+ */
+ dvmCompilerLockAllTemps(cUnit);
+ for (i = 0; i < dInsn->vA; i++) {
+ regMask |= 1 << i;
+ rlArg = dvmCompilerGetSrc(cUnit, mir, numDone++);
+ loadValueDirectFixed(cUnit, rlArg, i);
+ }
+ if (regMask) {
+ /* Up to 5 args are pushed on top of FP - sizeofStackSaveArea */
+ opRegRegImm(cUnit, kOpSub, r7, rFP,
+ sizeof(StackSaveArea) + (dInsn->vA << 2));
+ /* generate null check */
+ if (pcrLabel) {
+ *pcrLabel = genNullCheck(cUnit, dvmCompilerSSASrc(mir, 0), r0,
+ mir->offset, NULL);
+ }
+ storeMultiple(cUnit, r7, regMask);
+ }
+}
+
+static void genProcessArgsRange(CompilationUnit *cUnit, MIR *mir,
+ DecodedInstruction *dInsn,
+ ArmLIR **pcrLabel)
+{
+ int srcOffset = dInsn->vC << 2;
+ int numArgs = dInsn->vA;
+ int regMask;
+
+ /*
+ * Note: here, all promoted registers will have been flushed
+ * back to the Dalvik base locations, so register usage restrictins
+ * are lifted. All parms loaded from original Dalvik register
+ * region - even though some might conceivably have valid copies
+ * cached in a preserved register.
+ */
+ dvmCompilerLockAllTemps(cUnit);
+
+ /*
+ * r4PC : &rFP[vC]
+ * r7: &newFP[0]
+ */
+ opRegRegImm(cUnit, kOpAdd, r4PC, rFP, srcOffset);
+ /* load [r0 .. min(numArgs,4)] */
+ regMask = (1 << ((numArgs < 4) ? numArgs : 4)) - 1;
+ /*
+ * Protect the loadMultiple instruction from being reordered with other
+ * Dalvik stack accesses.
+ */
+ loadMultiple(cUnit, r4PC, regMask);
+
+ opRegRegImm(cUnit, kOpSub, r7, rFP,
+ sizeof(StackSaveArea) + (numArgs << 2));
+ /* generate null check */
+ if (pcrLabel) {
+ *pcrLabel = genNullCheck(cUnit, dvmCompilerSSASrc(mir, 0), r0,
+ mir->offset, NULL);
+ }
+
+ /*
+ * Handle remaining 4n arguments:
+ * store previously loaded 4 values and load the next 4 values
+ */
+ if (numArgs >= 8) {
+ ArmLIR *loopLabel = NULL;
+ /*
+ * r0 contains "this" and it will be used later, so push it to the stack
+ * first. Pushing r5 (rFP) is just for stack alignment purposes.
+ */
+ opImm(cUnit, kOpPush, (1 << r0 | 1 << rFP));
+ /* No need to generate the loop structure if numArgs <= 11 */
+ if (numArgs > 11) {
+ loadConstant(cUnit, 5, ((numArgs - 4) >> 2) << 2);
+ loopLabel = newLIR0(cUnit, kArmPseudoTargetLabel);
+ loopLabel->defMask = ENCODE_ALL;
+ }
+ storeMultiple(cUnit, r7, regMask);
+ /*
+ * Protect the loadMultiple instruction from being reordered with other
+ * Dalvik stack accesses.
+ */
+ loadMultiple(cUnit, r4PC, regMask);
+ /* No need to generate the loop structure if numArgs <= 11 */
+ if (numArgs > 11) {
+ opRegImm(cUnit, kOpSub, rFP, 4);
+ genConditionalBranch(cUnit, kArmCondNe, loopLabel);
+ }
+ }
+
+ /* Save the last batch of loaded values */
+ storeMultiple(cUnit, r7, regMask);
+
+ /* Generate the loop epilogue - don't use r0 */
+ if ((numArgs > 4) && (numArgs % 4)) {
+ regMask = ((1 << (numArgs & 0x3)) - 1) << 1;
+ /*
+ * Protect the loadMultiple instruction from being reordered with other
+ * Dalvik stack accesses.
+ */
+ loadMultiple(cUnit, r4PC, regMask);
+ }
+ if (numArgs >= 8)
+ opImm(cUnit, kOpPop, (1 << r0 | 1 << rFP));
+
+ /* Save the modulo 4 arguments */
+ if ((numArgs > 4) && (numArgs % 4)) {
+ storeMultiple(cUnit, r7, regMask);
+ }
+}
+
+/*
+ * Generate code to setup the call stack then jump to the chaining cell if it
+ * is not a native method.
+ */
+static void genInvokeSingletonCommon(CompilationUnit *cUnit, MIR *mir,
+ BasicBlock *bb, ArmLIR *labelList,
+ ArmLIR *pcrLabel,
+ const Method *calleeMethod)
+{
+ /*
+ * Note: all Dalvik register state should be flushed to
+ * memory by the point, so register usage restrictions no
+ * longer apply. All temp & preserved registers may be used.
+ */
+ dvmCompilerLockAllTemps(cUnit);
+ ArmLIR *retChainingCell = &labelList[bb->fallThrough->id];
+
+ /* r1 = &retChainingCell */
+ dvmCompilerLockTemp(cUnit, r1);
+ ArmLIR *addrRetChain = opRegRegImm(cUnit, kOpAdd, r1, rpc, 0);
+ /* r4PC = dalvikCallsite */
+ loadConstant(cUnit, r4PC,
+ (int) (cUnit->method->insns + mir->offset));
+ addrRetChain->generic.target = (LIR *) retChainingCell;
+ /*
+ * r0 = calleeMethod (loaded upon calling genInvokeSingletonCommon)
+ * r1 = &ChainingCell
+ * r4PC = callsiteDPC
+ */
+ if (dvmIsNativeMethod(calleeMethod)) {
+ genDispatchToHandler(cUnit, TEMPLATE_INVOKE_METHOD_NATIVE);
+#if defined(WITH_JIT_TUNING)
+ gDvmJit.invokeNative++;
+#endif
+ } else {
+ genDispatchToHandler(cUnit, TEMPLATE_INVOKE_METHOD_CHAIN);
+#if defined(WITH_JIT_TUNING)
+ gDvmJit.invokeMonomorphic++;
+#endif
+ /* Branch to the chaining cell */
+ genUnconditionalBranch(cUnit, &labelList[bb->taken->id]);
+ }
+ /* Handle exceptions using the interpreter */
+ genTrap(cUnit, mir->offset, pcrLabel);
+}
+
+/*
+ * Generate code to check the validity of a predicted chain and take actions
+ * based on the result.
+ *
+ * 0x426a99aa : ldr r4, [pc, #72] --> r4 <- dalvikPC of this invoke
+ * 0x426a99ac : add r1, pc, #32 --> r1 <- &retChainingCell
+ * 0x426a99ae : add r2, pc, #40 --> r2 <- &predictedChainingCell
+ * 0x426a99b0 : blx_1 0x426a918c --+ TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN
+ * 0x426a99b2 : blx_2 see above --+
+ * 0x426a99b4 : b 0x426a99d8 --> off to the predicted chain
+ * 0x426a99b6 : b 0x426a99c8 --> punt to the interpreter
+ * 0x426a99b8 : ldr r0, [r7, #44] --> r0 <- this->class->vtable[methodIdx]
+ * 0x426a99ba : cmp r1, #0 --> compare r1 (rechain count) against 0
+ * 0x426a99bc : bgt 0x426a99c2 --> >=0? don't rechain
+ * 0x426a99be : ldr r7, [r6, #96] --+ dvmJitToPatchPredictedChain
+ * 0x426a99c0 : blx r7 --+
+ * 0x426a99c2 : add r1, pc, #12 --> r1 <- &retChainingCell
+ * 0x426a99c4 : blx_1 0x426a9098 --+ TEMPLATE_INVOKE_METHOD_NO_OPT
+ * 0x426a99c6 : blx_2 see above --+
+ */
+static void genInvokeVirtualCommon(CompilationUnit *cUnit, MIR *mir,
+ int methodIndex,
+ ArmLIR *retChainingCell,
+ ArmLIR *predChainingCell,
+ ArmLIR *pcrLabel)
+{
+ /*
+ * Note: all Dalvik register state should be flushed to
+ * memory by the point, so register usage restrictions no
+ * longer apply. Lock temps to prevent them from being
+ * allocated by utility routines.
+ */
+ dvmCompilerLockAllTemps(cUnit);
+
+ /* "this" is already left in r0 by genProcessArgs* */
+
+ /* r4PC = dalvikCallsite */
+ loadConstant(cUnit, r4PC,
+ (int) (cUnit->method->insns + mir->offset));
+
+ /* r1 = &retChainingCell */
+ ArmLIR *addrRetChain = opRegRegImm(cUnit, kOpAdd, r1, rpc, 0);
+ addrRetChain->generic.target = (LIR *) retChainingCell;
+
+ /* r2 = &predictedChainingCell */
+ ArmLIR *predictedChainingCell = opRegRegImm(cUnit, kOpAdd, r2, rpc, 0);
+ predictedChainingCell->generic.target = (LIR *) predChainingCell;
+
+ genDispatchToHandler(cUnit, TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN);
+
+ /* return through lr - jump to the chaining cell */
+ genUnconditionalBranch(cUnit, predChainingCell);
+
+ /*
+ * null-check on "this" may have been eliminated, but we still need a PC-
+ * reconstruction label for stack overflow bailout.
+ */
+ if (pcrLabel == NULL) {
+ int dPC = (int) (cUnit->method->insns + mir->offset);
+ pcrLabel = dvmCompilerNew(sizeof(ArmLIR), true);
+ pcrLabel->opCode = kArmPseudoPCReconstructionCell;
+ pcrLabel->operands[0] = dPC;
+ pcrLabel->operands[1] = mir->offset;
+ /* Insert the place holder to the growable list */
+ dvmInsertGrowableList(&cUnit->pcReconstructionList, pcrLabel);
+ }
+
+ /* return through lr+2 - punt to the interpreter */
+ genUnconditionalBranch(cUnit, pcrLabel);
+
+ /*
+ * return through lr+4 - fully resolve the callee method.
+ * r1 <- count
+ * r2 <- &predictedChainCell
+ * r3 <- this->class
+ * r4 <- dPC
+ * r7 <- this->class->vtable
+ */
+
+ /* r0 <- calleeMethod */
+ loadWordDisp(cUnit, r7, methodIndex * 4, r0);
+
+ /* Check if rechain limit is reached */
+ opRegImm(cUnit, kOpCmp, r1, 0);
+
+ ArmLIR *bypassRechaining = opCondBranch(cUnit, kArmCondGt);
+
+ loadWordDisp(cUnit, rGLUE, offsetof(InterpState,
+ jitToInterpEntries.dvmJitToPatchPredictedChain), r7);
+
+ genRegCopy(cUnit, r1, rGLUE);
+
+ /*
+ * r0 = calleeMethod
+ * r2 = &predictedChainingCell
+ * r3 = class
+ *
+ * &returnChainingCell has been loaded into r1 but is not needed
+ * when patching the chaining cell and will be clobbered upon
+ * returning so it will be reconstructed again.
+ */
+ opReg(cUnit, kOpBlx, r7);
+
+ /* r1 = &retChainingCell */
+ addrRetChain = opRegRegImm(cUnit, kOpAdd, r1, rpc, 0);
+ addrRetChain->generic.target = (LIR *) retChainingCell;
+
+ bypassRechaining->generic.target = (LIR *) addrRetChain;
+ /*
+ * r0 = calleeMethod,
+ * r1 = &ChainingCell,
+ * r4PC = callsiteDPC,
+ */
+ genDispatchToHandler(cUnit, TEMPLATE_INVOKE_METHOD_NO_OPT);
+#if defined(WITH_JIT_TUNING)
+ gDvmJit.invokePolymorphic++;
+#endif
+ /* Handle exceptions using the interpreter */
+ genTrap(cUnit, mir->offset, pcrLabel);
+}
+
+/* Geneate a branch to go back to the interpreter */
+static void genPuntToInterp(CompilationUnit *cUnit, unsigned int offset)
+{
+ /* r0 = dalvik pc */
+ dvmCompilerFlushAllRegs(cUnit);
+ loadConstant(cUnit, r0, (int) (cUnit->method->insns + offset));
+ loadWordDisp(cUnit, rGLUE, offsetof(InterpState,
+ jitToInterpEntries.dvmJitToInterpPunt), r1);
+ opReg(cUnit, kOpBlx, r1);
+}
+
+/*
+ * Attempt to single step one instruction using the interpreter and return
+ * to the compiled code for the next Dalvik instruction
+ */
+static void genInterpSingleStep(CompilationUnit *cUnit, MIR *mir)
+{
+ int flags = dexGetInstrFlags(gDvm.instrFlags, mir->dalvikInsn.opCode);
+ int flagsToCheck = kInstrCanBranch | kInstrCanSwitch | kInstrCanReturn |
+ kInstrCanThrow;
+
+ //If already optimized out, just ignore
+ if (mir->dalvikInsn.opCode == OP_NOP)
+ return;
+
+ //Ugly, but necessary. Flush all Dalvik regs so Interp can find them
+ dvmCompilerFlushAllRegs(cUnit);
+
+ if ((mir->next == NULL) || (flags & flagsToCheck)) {
+ genPuntToInterp(cUnit, mir->offset);
+ return;
+ }
+ int entryAddr = offsetof(InterpState,
+ jitToInterpEntries.dvmJitToInterpSingleStep);
+ loadWordDisp(cUnit, rGLUE, entryAddr, r2);
+ /* r0 = dalvik pc */
+ loadConstant(cUnit, r0, (int) (cUnit->method->insns + mir->offset));
+ /* r1 = dalvik pc of following instruction */
+ loadConstant(cUnit, r1, (int) (cUnit->method->insns + mir->next->offset));
+ opReg(cUnit, kOpBlx, r2);
+}
+
+#if defined(WITH_DEADLOCK_PREDICTION) || defined(WITH_MONITOR_TRACKING) || \
+ defined(_ARMV5TE) || defined(_ARMV5TE_VFP)
+/*
+ * To prevent a thread in a monitor wait from blocking the Jit from
+ * resetting the code cache, heavyweight monitor lock will not
+ * be allowed to return to an existing translation. Instead, we will
+ * handle them by branching to a handler, which will in turn call the
+ * runtime lock routine and then branch directly back to the
+ * interpreter main loop. Given the high cost of the heavyweight
+ * lock operation, this additional cost should be slight (especially when
+ * considering that we expect the vast majority of lock operations to
+ * use the fast-path thin lock bypass).
+ */
+static void genMonitorPortable(CompilationUnit *cUnit, MIR *mir)
+{
+ bool isEnter = (mir->dalvikInsn.opCode == OP_MONITOR_ENTER);
+ genExportPC(cUnit, mir);
+ dvmCompilerFlushAllRegs(cUnit); /* Send everything to home location */
+ RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+ loadValueDirectFixed(cUnit, rlSrc, r1);
+ loadWordDisp(cUnit, rGLUE, offsetof(InterpState, self), r0);
+ genNullCheck(cUnit, rlSrc.sRegLow, r1, mir->offset, NULL);
+ if (isEnter) {
+ /* Get dPC of next insn */
+ loadConstant(cUnit, r4PC, (int)(cUnit->method->insns + mir->offset +
+ dexGetInstrWidthAbs(gDvm.instrWidth, OP_MONITOR_ENTER)));
+#if defined(WITH_DEADLOCK_PREDICTION)
+ genDispatchToHandler(cUnit, TEMPLATE_MONITOR_ENTER_DEBUG);
+#else
+ genDispatchToHandler(cUnit, TEMPLATE_MONITOR_ENTER);
+#endif
+ } else {
+ LOAD_FUNC_ADDR(cUnit, r2, (int)dvmUnlockObject);
+ /* Do the call */
+ opReg(cUnit, kOpBlx, r2);
+ opRegImm(cUnit, kOpCmp, r0, 0); /* Did we throw? */
+ ArmLIR *branchOver = opCondBranch(cUnit, kArmCondNe);
+ loadConstant(cUnit, r0,
+ (int) (cUnit->method->insns + mir->offset +
+ dexGetInstrWidthAbs(gDvm.instrWidth, OP_MONITOR_EXIT)));
+ genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON);
+ ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+ target->defMask = ENCODE_ALL;
+ branchOver->generic.target = (LIR *) target;
+ dvmCompilerClobberCallRegs(cUnit);
+ }
+}
+#endif
+
+/*
+ * The following are the first-level codegen routines that analyze the format
+ * of each bytecode then either dispatch special purpose codegen routines
+ * or produce corresponding Thumb instructions directly.
+ */
+
+static bool handleFmt10t_Fmt20t_Fmt30t(CompilationUnit *cUnit, MIR *mir,
+ BasicBlock *bb, ArmLIR *labelList)
+{
+ /* For OP_GOTO, OP_GOTO_16, and OP_GOTO_32 */
+ genUnconditionalBranch(cUnit, &labelList[bb->taken->id]);
+ return false;
+}
+
+static bool handleFmt10x(CompilationUnit *cUnit, MIR *mir)
+{
+ OpCode dalvikOpCode = mir->dalvikInsn.opCode;
+ if ((dalvikOpCode >= OP_UNUSED_3E) && (dalvikOpCode <= OP_UNUSED_43)) {
+ LOGE("Codegen: got unused opcode 0x%x\n",dalvikOpCode);
+ return true;
+ }
+ switch (dalvikOpCode) {
+ case OP_RETURN_VOID:
+ genReturnCommon(cUnit,mir);
+ break;
+ case OP_UNUSED_73:
+ case OP_UNUSED_79:
+ case OP_UNUSED_7A:
+ case OP_UNUSED_F1:
+ case OP_UNUSED_FF:
+ LOGE("Codegen: got unused opcode 0x%x\n",dalvikOpCode);
+ return true;
+ case OP_NOP:
+ break;
+ default:
+ return true;
+ }
+ return false;
+}
+
+static bool handleFmt11n_Fmt31i(CompilationUnit *cUnit, MIR *mir)
+{
+ RegLocation rlDest;
+ RegLocation rlResult;
+ if (mir->ssaRep->numDefs == 2) {
+ rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+ } else {
+ rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+ }
+
+ switch (mir->dalvikInsn.opCode) {
+ case OP_CONST:
+ case OP_CONST_4: {
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true);
+ loadConstantNoClobber(cUnit, rlResult.lowReg, mir->dalvikInsn.vB);
+ storeValue(cUnit, rlDest, rlResult);
+ break;
+ }
+ case OP_CONST_WIDE_32: {
+ //TUNING: single routine to load constant pair for support doubles
+ //TUNING: load 0/-1 separately to avoid load dependency
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+ loadConstantNoClobber(cUnit, rlResult.lowReg, mir->dalvikInsn.vB);
+ opRegRegImm(cUnit, kOpAsr, rlResult.highReg,
+ rlResult.lowReg, 31);
+ storeValueWide(cUnit, rlDest, rlResult);
+ break;
+ }
+ default:
+ return true;
+ }
+ return false;
+}
+
+static bool handleFmt21h(CompilationUnit *cUnit, MIR *mir)
+{
+ RegLocation rlDest;
+ RegLocation rlResult;
+ if (mir->ssaRep->numDefs == 2) {
+ rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+ } else {
+ rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+ }
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true);
+
+ switch (mir->dalvikInsn.opCode) {
+ case OP_CONST_HIGH16: {
+ loadConstantNoClobber(cUnit, rlResult.lowReg,
+ mir->dalvikInsn.vB << 16);
+ storeValue(cUnit, rlDest, rlResult);
+ break;
+ }
+ case OP_CONST_WIDE_HIGH16: {
+ loadConstantValueWide(cUnit, rlResult.lowReg, rlResult.highReg,
+ 0, mir->dalvikInsn.vB << 16);
+ storeValueWide(cUnit, rlDest, rlResult);
+ break;
+ }
+ default:
+ return true;
+ }
+ return false;
+}
+
+static bool handleFmt20bc(CompilationUnit *cUnit, MIR *mir)
+{
+ /* For OP_THROW_VERIFICATION_ERROR */
+ genInterpSingleStep(cUnit, mir);
+ return false;
+}
+
+static bool handleFmt21c_Fmt31c(CompilationUnit *cUnit, MIR *mir)
+{
+ RegLocation rlResult;
+ RegLocation rlDest;
+ RegLocation rlSrc;
+
+ switch (mir->dalvikInsn.opCode) {
+ case OP_CONST_STRING_JUMBO:
+ case OP_CONST_STRING: {
+ void *strPtr = (void*)
+ (cUnit->method->clazz->pDvmDex->pResStrings[mir->dalvikInsn.vB]);
+
+ if (strPtr == NULL) {
+ LOGE("Unexpected null string");
+ dvmAbort();
+ }
+
+ rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+ loadConstantNoClobber(cUnit, rlResult.lowReg, (int) strPtr );
+ storeValue(cUnit, rlDest, rlResult);
+ break;
+ }
+ case OP_CONST_CLASS: {
+ void *classPtr = (void*)
+ (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vB]);
+
+ if (classPtr == NULL) {
+ LOGE("Unexpected null class");
+ dvmAbort();
+ }
+
+ rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+ loadConstantNoClobber(cUnit, rlResult.lowReg, (int) classPtr );
+ storeValue(cUnit, rlDest, rlResult);
+ break;
+ }
+ case OP_SGET_VOLATILE:
+ case OP_SGET_OBJECT_VOLATILE:
+ case OP_SGET_OBJECT:
+ case OP_SGET_BOOLEAN:
+ case OP_SGET_CHAR:
+ case OP_SGET_BYTE:
+ case OP_SGET_SHORT:
+ case OP_SGET: {
+ int valOffset = offsetof(StaticField, value);
+ int tReg = dvmCompilerAllocTemp(cUnit);
+ bool isVolatile;
+ const Method *method = (mir->OptimizationFlags & MIR_CALLEE) ?
+ mir->meta.calleeMethod : cUnit->method;
+ void *fieldPtr = (void*)
+ (method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]);
+
+ if (fieldPtr == NULL) {
+ LOGE("Unexpected null static field");
+ dvmAbort();
+ }
+
+ isVolatile = (mir->dalvikInsn.opCode == OP_SGET_VOLATILE) ||
+ (mir->dalvikInsn.opCode == OP_SGET_OBJECT_VOLATILE) ||
+ dvmIsVolatileField(fieldPtr);
+
+ rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true);
+ loadConstant(cUnit, tReg, (int) fieldPtr + valOffset);
+
+ if (isVolatile) {
+ dvmCompilerGenMemBarrier(cUnit);
+ }
+ HEAP_ACCESS_SHADOW(true);
+ loadWordDisp(cUnit, tReg, 0, rlResult.lowReg);
+ HEAP_ACCESS_SHADOW(false);
+
+ storeValue(cUnit, rlDest, rlResult);
+ break;
+ }
+ case OP_SGET_WIDE: {
+ int valOffset = offsetof(StaticField, value);
+ const Method *method = (mir->OptimizationFlags & MIR_CALLEE) ?
+ mir->meta.calleeMethod : cUnit->method;
+ void *fieldPtr = (void*)
+ (method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]);
+
+ if (fieldPtr == NULL) {
+ LOGE("Unexpected null static field");
+ dvmAbort();
+ }
+
+ int tReg = dvmCompilerAllocTemp(cUnit);
+ rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true);
+ loadConstant(cUnit, tReg, (int) fieldPtr + valOffset);
+
+ HEAP_ACCESS_SHADOW(true);
+ loadPair(cUnit, tReg, rlResult.lowReg, rlResult.highReg);
+ HEAP_ACCESS_SHADOW(false);
+
+ storeValueWide(cUnit, rlDest, rlResult);
+ break;
+ }
+ case OP_SPUT_OBJECT:
+ case OP_SPUT_OBJECT_VOLATILE:
+ case OP_SPUT_VOLATILE:
+ case OP_SPUT_BOOLEAN:
+ case OP_SPUT_CHAR:
+ case OP_SPUT_BYTE:
+ case OP_SPUT_SHORT:
+ case OP_SPUT: {
+ int valOffset = offsetof(StaticField, value);
+ int tReg = dvmCompilerAllocTemp(cUnit);
+ int objHead;
+ bool isVolatile;
+ bool isSputObject;
+ const Method *method = (mir->OptimizationFlags & MIR_CALLEE) ?
+ mir->meta.calleeMethod : cUnit->method;
+ void *fieldPtr = (void*)
+ (method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]);
+
+ isVolatile = (mir->dalvikInsn.opCode == OP_SPUT_VOLATILE) ||
+ (mir->dalvikInsn.opCode == OP_SPUT_OBJECT_VOLATILE) ||
+ dvmIsVolatileField(fieldPtr);
+
+ isSputObject = (mir->dalvikInsn.opCode == OP_SPUT_OBJECT) ||
+ (mir->dalvikInsn.opCode == OP_SPUT_OBJECT_VOLATILE);
+
+ if (fieldPtr == NULL) {
+ LOGE("Unexpected null static field");
+ dvmAbort();
+ }
+
+ rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+ rlSrc = loadValue(cUnit, rlSrc, kAnyReg);
+ loadConstant(cUnit, tReg, (int) fieldPtr);
+ if (isSputObject) {
+ objHead = dvmCompilerAllocTemp(cUnit);
+ loadWordDisp(cUnit, tReg, offsetof(Field, clazz), objHead);
+ }
+ HEAP_ACCESS_SHADOW(true);
+ storeWordDisp(cUnit, tReg, valOffset ,rlSrc.lowReg);
+ dvmCompilerFreeTemp(cUnit, tReg);
+ HEAP_ACCESS_SHADOW(false);
+ if (isVolatile) {
+ dvmCompilerGenMemBarrier(cUnit);
+ }
+ if (isSputObject) {
+ /* NOTE: marking card based sfield->clazz */
+ markCard(cUnit, rlSrc.lowReg, objHead);
+ dvmCompilerFreeTemp(cUnit, objHead);
+ }
+
+ break;
+ }
+ case OP_SPUT_WIDE: {
+ int tReg = dvmCompilerAllocTemp(cUnit);
+ int valOffset = offsetof(StaticField, value);
+ const Method *method = (mir->OptimizationFlags & MIR_CALLEE) ?
+ mir->meta.calleeMethod : cUnit->method;
+ void *fieldPtr = (void*)
+ (method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]);
+
+ if (fieldPtr == NULL) {
+ LOGE("Unexpected null static field");
+ dvmAbort();
+ }
+
+ rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+ rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg);
+ loadConstant(cUnit, tReg, (int) fieldPtr + valOffset);
+
+ HEAP_ACCESS_SHADOW(true);
+ storePair(cUnit, tReg, rlSrc.lowReg, rlSrc.highReg);
+ HEAP_ACCESS_SHADOW(false);
+ break;
+ }
+ case OP_NEW_INSTANCE: {
+ /*
+ * Obey the calling convention and don't mess with the register
+ * usage.
+ */
+ ClassObject *classPtr = (void*)
+ (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vB]);
+
+ if (classPtr == NULL) {
+ LOGE("Unexpected null class");
+ dvmAbort();
+ }
+
+ /*
+ * If it is going to throw, it should not make to the trace to begin
+ * with. However, Alloc might throw, so we need to genExportPC()
+ */
+ assert((classPtr->accessFlags & (ACC_INTERFACE|ACC_ABSTRACT)) == 0);
+ dvmCompilerFlushAllRegs(cUnit); /* Everything to home location */
+ genExportPC(cUnit, mir);
+ LOAD_FUNC_ADDR(cUnit, r2, (int)dvmAllocObject);
+ loadConstant(cUnit, r0, (int) classPtr);
+ loadConstant(cUnit, r1, ALLOC_DONT_TRACK);
+ opReg(cUnit, kOpBlx, r2);
+ dvmCompilerClobberCallRegs(cUnit);
+ /* generate a branch over if allocation is successful */
+ opRegImm(cUnit, kOpCmp, r0, 0); /* NULL? */
+ ArmLIR *branchOver = opCondBranch(cUnit, kArmCondNe);
+ /*
+ * OOM exception needs to be thrown here and cannot re-execute
+ */
+ loadConstant(cUnit, r0,
+ (int) (cUnit->method->insns + mir->offset));
+ genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON);
+ /* noreturn */
+
+ ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+ target->defMask = ENCODE_ALL;
+ branchOver->generic.target = (LIR *) target;
+ rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+ rlResult = dvmCompilerGetReturn(cUnit);
+ storeValue(cUnit, rlDest, rlResult);
+ break;
+ }
+ case OP_CHECK_CAST: {
+ /*
+ * Obey the calling convention and don't mess with the register
+ * usage.
+ */
+ ClassObject *classPtr =
+ (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vB]);
+ /*
+ * Note: It is possible that classPtr is NULL at this point,
+ * even though this instruction has been successfully interpreted.
+ * If the previous interpretation had a null source, the
+ * interpreter would not have bothered to resolve the clazz.
+ * Bail out to the interpreter in this case, and log it
+ * so that we can tell if it happens frequently.
+ */
+ if (classPtr == NULL) {
+ LOGVV("null clazz in OP_CHECK_CAST, single-stepping");
+ genInterpSingleStep(cUnit, mir);
+ return false;
+ }
+ dvmCompilerFlushAllRegs(cUnit); /* Everything to home location */
+ loadConstant(cUnit, r1, (int) classPtr );
+ rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+ rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+ opRegImm(cUnit, kOpCmp, rlSrc.lowReg, 0); /* Null? */
+ ArmLIR *branch1 = opCondBranch(cUnit, kArmCondEq);
+ /*
+ * rlSrc.lowReg now contains object->clazz. Note that
+ * it could have been allocated r0, but we're okay so long
+ * as we don't do anything desctructive until r0 is loaded
+ * with clazz.
+ */
+ /* r0 now contains object->clazz */
+ loadWordDisp(cUnit, rlSrc.lowReg, offsetof(Object, clazz), r0);
+ LOAD_FUNC_ADDR(cUnit, r2, (int)dvmInstanceofNonTrivial);
+ opRegReg(cUnit, kOpCmp, r0, r1);
+ ArmLIR *branch2 = opCondBranch(cUnit, kArmCondEq);
+ opReg(cUnit, kOpBlx, r2);
+ dvmCompilerClobberCallRegs(cUnit);
+ /*
+ * If null, check cast failed - punt to the interpreter. Because
+ * interpreter will be the one throwing, we don't need to
+ * genExportPC() here.
+ */
+ genZeroCheck(cUnit, r0, mir->offset, NULL);
+ /* check cast passed - branch target here */
+ ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+ target->defMask = ENCODE_ALL;
+ branch1->generic.target = (LIR *)target;
+ branch2->generic.target = (LIR *)target;
+ break;
+ }
+ case OP_SGET_WIDE_VOLATILE:
+ case OP_SPUT_WIDE_VOLATILE:
+ genInterpSingleStep(cUnit, mir);
+ break;
+ default:
+ return true;
+ }
+ return false;
+}
+
+/*
+ * A typical example of inlined getter/setter from a monomorphic callsite:
+ *
+ * D/dalvikvm( 289): -------- dalvik offset: 0x0000 @ invoke-static (I)
+ * D/dalvikvm( 289): -------- dalvik offset: 0x0000 @ sget-object (C) v0, ...
+ * D/dalvikvm( 289): 0x4427fc22 (0002): ldr r0, [pc, #56]
+ * D/dalvikvm( 289): 0x4427fc24 (0004): ldr r1, [r0, #0]
+ * D/dalvikvm( 289): 0x4427fc26 (0006): str r1, [r5, #0]
+ * D/dalvikvm( 289): 0x4427fc28 (0008): .align4
+ * D/dalvikvm( 289): L0x0003:
+ * D/dalvikvm( 289): -------- dalvik offset: 0x0003 @ move-result-object (I) v0
+ *
+ * Note the invoke-static and move-result-object with the (I) notation are
+ * turned into no-op.
+ */
+static bool handleFmt11x(CompilationUnit *cUnit, MIR *mir)
+{
+ OpCode dalvikOpCode = mir->dalvikInsn.opCode;
+ RegLocation rlResult;
+ switch (dalvikOpCode) {
+ case OP_MOVE_EXCEPTION: {
+ int offset = offsetof(InterpState, self);
+ int exOffset = offsetof(Thread, exception);
+ int selfReg = dvmCompilerAllocTemp(cUnit);
+ int resetReg = dvmCompilerAllocTemp(cUnit);
+ RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+ loadWordDisp(cUnit, rGLUE, offset, selfReg);
+ loadConstant(cUnit, resetReg, 0);
+ loadWordDisp(cUnit, selfReg, exOffset, rlResult.lowReg);
+ storeWordDisp(cUnit, selfReg, exOffset, resetReg);
+ storeValue(cUnit, rlDest, rlResult);
+ break;
+ }
+ case OP_MOVE_RESULT:
+ case OP_MOVE_RESULT_OBJECT: {
+ /* An inlined move result is effectively no-op */
+ if (mir->OptimizationFlags & MIR_INLINED)
+ break;
+ RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+ RegLocation rlSrc = LOC_DALVIK_RETURN_VAL;
+ rlSrc.fp = rlDest.fp;
+ storeValue(cUnit, rlDest, rlSrc);
+ break;
+ }
+ case OP_MOVE_RESULT_WIDE: {
+ /* An inlined move result is effectively no-op */
+ if (mir->OptimizationFlags & MIR_INLINED)
+ break;
+ RegLocation rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+ RegLocation rlSrc = LOC_DALVIK_RETURN_VAL_WIDE;
+ rlSrc.fp = rlDest.fp;
+ storeValueWide(cUnit, rlDest, rlSrc);
+ break;
+ }
+ case OP_RETURN_WIDE: {
+ RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+ RegLocation rlDest = LOC_DALVIK_RETURN_VAL_WIDE;
+ rlDest.fp = rlSrc.fp;
+ storeValueWide(cUnit, rlDest, rlSrc);
+ genReturnCommon(cUnit,mir);
+ break;
+ }
+ case OP_RETURN:
+ case OP_RETURN_OBJECT: {
+ RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+ RegLocation rlDest = LOC_DALVIK_RETURN_VAL;
+ rlDest.fp = rlSrc.fp;
+ storeValue(cUnit, rlDest, rlSrc);
+ genReturnCommon(cUnit,mir);
+ break;
+ }
+ case OP_MONITOR_EXIT:
+ case OP_MONITOR_ENTER:
+#if defined(WITH_DEADLOCK_PREDICTION) || defined(WITH_MONITOR_TRACKING)
+ genMonitorPortable(cUnit, mir);
+#else
+ genMonitor(cUnit, mir);
+#endif
+ break;
+ case OP_THROW: {
+ genInterpSingleStep(cUnit, mir);
+ break;
+ }
+ default:
+ return true;
+ }
+ return false;
+}
+
+static bool handleFmt12x(CompilationUnit *cUnit, MIR *mir)
+{
+ OpCode opCode = mir->dalvikInsn.opCode;
+ RegLocation rlDest;
+ RegLocation rlSrc;
+ RegLocation rlResult;
+
+ if ( (opCode >= OP_ADD_INT_2ADDR) && (opCode <= OP_REM_DOUBLE_2ADDR)) {
+ return genArithOp( cUnit, mir );
+ }
+
+ if (mir->ssaRep->numUses == 2)
+ rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+ else
+ rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+ if (mir->ssaRep->numDefs == 2)
+ rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+ else
+ rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+
+ switch (opCode) {
+ case OP_DOUBLE_TO_INT:
+ case OP_INT_TO_FLOAT:
+ case OP_FLOAT_TO_INT:
+ case OP_DOUBLE_TO_FLOAT:
+ case OP_FLOAT_TO_DOUBLE:
+ case OP_INT_TO_DOUBLE:
+ case OP_FLOAT_TO_LONG:
+ case OP_LONG_TO_FLOAT:
+ case OP_DOUBLE_TO_LONG:
+ case OP_LONG_TO_DOUBLE:
+ return genConversion(cUnit, mir);
+ case OP_NEG_INT:
+ case OP_NOT_INT:
+ return genArithOpInt(cUnit, mir, rlDest, rlSrc, rlSrc);
+ case OP_NEG_LONG:
+ case OP_NOT_LONG:
+ return genArithOpLong(cUnit, mir, rlDest, rlSrc, rlSrc);
+ case OP_NEG_FLOAT:
+ return genArithOpFloat(cUnit, mir, rlDest, rlSrc, rlSrc);
+ case OP_NEG_DOUBLE:
+ return genArithOpDouble(cUnit, mir, rlDest, rlSrc, rlSrc);
+ case OP_MOVE_WIDE:
+ storeValueWide(cUnit, rlDest, rlSrc);
+ break;
+ case OP_INT_TO_LONG:
+ rlSrc = dvmCompilerUpdateLoc(cUnit, rlSrc);
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+ //TUNING: shouldn't loadValueDirect already check for phys reg?
+ if (rlSrc.location == kLocPhysReg) {
+ genRegCopy(cUnit, rlResult.lowReg, rlSrc.lowReg);
+ } else {
+ loadValueDirect(cUnit, rlSrc, rlResult.lowReg);
+ }
+ opRegRegImm(cUnit, kOpAsr, rlResult.highReg,
+ rlResult.lowReg, 31);
+ storeValueWide(cUnit, rlDest, rlResult);
+ break;
+ case OP_LONG_TO_INT:
+ rlSrc = dvmCompilerUpdateLocWide(cUnit, rlSrc);
+ rlSrc = dvmCompilerWideToNarrow(cUnit, rlSrc);
+ // Intentional fallthrough
+ case OP_MOVE:
+ case OP_MOVE_OBJECT:
+ storeValue(cUnit, rlDest, rlSrc);
+ break;
+ case OP_INT_TO_BYTE:
+ rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+ opRegReg(cUnit, kOp2Byte, rlResult.lowReg, rlSrc.lowReg);
+ storeValue(cUnit, rlDest, rlResult);
+ break;
+ case OP_INT_TO_SHORT:
+ rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+ opRegReg(cUnit, kOp2Short, rlResult.lowReg, rlSrc.lowReg);
+ storeValue(cUnit, rlDest, rlResult);
+ break;
+ case OP_INT_TO_CHAR:
+ rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+ opRegReg(cUnit, kOp2Char, rlResult.lowReg, rlSrc.lowReg);
+ storeValue(cUnit, rlDest, rlResult);
+ break;
+ case OP_ARRAY_LENGTH: {
+ int lenOffset = offsetof(ArrayObject, length);
+ rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+ genNullCheck(cUnit, rlSrc.sRegLow, rlSrc.lowReg,
+ mir->offset, NULL);
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+ loadWordDisp(cUnit, rlSrc.lowReg, lenOffset,
+ rlResult.lowReg);
+ storeValue(cUnit, rlDest, rlResult);
+ break;
+ }
+ default:
+ return true;
+ }
+ return false;
+}
+
+static bool handleFmt21s(CompilationUnit *cUnit, MIR *mir)
+{
+ OpCode dalvikOpCode = mir->dalvikInsn.opCode;
+ RegLocation rlDest;
+ RegLocation rlResult;
+ int BBBB = mir->dalvikInsn.vB;
+ if (dalvikOpCode == OP_CONST_WIDE_16) {
+ rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+ loadConstantNoClobber(cUnit, rlResult.lowReg, BBBB);
+ //TUNING: do high separately to avoid load dependency
+ opRegRegImm(cUnit, kOpAsr, rlResult.highReg, rlResult.lowReg, 31);
+ storeValueWide(cUnit, rlDest, rlResult);
+ } else if (dalvikOpCode == OP_CONST_16) {
+ rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true);
+ loadConstantNoClobber(cUnit, rlResult.lowReg, BBBB);
+ storeValue(cUnit, rlDest, rlResult);
+ } else
+ return true;
+ return false;
+}
+
+/* Compare agaist zero */
+static bool handleFmt21t(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb,
+ ArmLIR *labelList)
+{
+ OpCode dalvikOpCode = mir->dalvikInsn.opCode;
+ ArmConditionCode cond;
+ RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+ rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+ opRegImm(cUnit, kOpCmp, rlSrc.lowReg, 0);
+
+//TUNING: break this out to allow use of Thumb2 CB[N]Z
+ switch (dalvikOpCode) {
+ case OP_IF_EQZ:
+ cond = kArmCondEq;
+ break;
+ case OP_IF_NEZ:
+ cond = kArmCondNe;
+ break;
+ case OP_IF_LTZ:
+ cond = kArmCondLt;
+ break;
+ case OP_IF_GEZ:
+ cond = kArmCondGe;
+ break;
+ case OP_IF_GTZ:
+ cond = kArmCondGt;
+ break;
+ case OP_IF_LEZ:
+ cond = kArmCondLe;
+ break;
+ default:
+ cond = 0;
+ LOGE("Unexpected opcode (%d) for Fmt21t\n", dalvikOpCode);
+ dvmCompilerAbort(cUnit);
+ }
+ genConditionalBranch(cUnit, cond, &labelList[bb->taken->id]);
+ /* This mostly likely will be optimized away in a later phase */
+ genUnconditionalBranch(cUnit, &labelList[bb->fallThrough->id]);
+ return false;
+}
+
+static bool isPowerOfTwo(int x)
+{
+ return (x & (x - 1)) == 0;
+}
+
+// Returns true if no more than two bits are set in 'x'.
+static bool isPopCountLE2(unsigned int x)
+{
+ x &= x - 1;
+ return (x & (x - 1)) == 0;
+}
+
+// Returns the index of the lowest set bit in 'x'.
+static int lowestSetBit(unsigned int x) {
+ int bit_posn = 0;
+ while ((x & 0xf) == 0) {
+ bit_posn += 4;
+ x >>= 4;
+ }
+ while ((x & 1) == 0) {
+ bit_posn++;
+ x >>= 1;
+ }
+ return bit_posn;
+}
+
+// Returns true if it added instructions to 'cUnit' to divide 'rlSrc' by 'lit'
+// and store the result in 'rlDest'.
+static bool handleEasyDivide(CompilationUnit *cUnit, OpCode dalvikOpCode,
+ RegLocation rlSrc, RegLocation rlDest, int lit)
+{
+ if (lit < 2 || !isPowerOfTwo(lit)) {
+ return false;
+ }
+ int k = lowestSetBit(lit);
+ if (k >= 30) {
+ // Avoid special cases.
+ return false;
+ }
+ bool div = (dalvikOpCode == OP_DIV_INT_LIT8 || dalvikOpCode == OP_DIV_INT_LIT16);
+ rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+ RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+ if (div) {
+ int tReg = dvmCompilerAllocTemp(cUnit);
+ if (lit == 2) {
+ // Division by 2 is by far the most common division by constant.
+ opRegRegImm(cUnit, kOpLsr, tReg, rlSrc.lowReg, 32 - k);
+ opRegRegReg(cUnit, kOpAdd, tReg, tReg, rlSrc.lowReg);
+ opRegRegImm(cUnit, kOpAsr, rlResult.lowReg, tReg, k);
+ } else {
+ opRegRegImm(cUnit, kOpAsr, tReg, rlSrc.lowReg, 31);
+ opRegRegImm(cUnit, kOpLsr, tReg, tReg, 32 - k);
+ opRegRegReg(cUnit, kOpAdd, tReg, tReg, rlSrc.lowReg);
+ opRegRegImm(cUnit, kOpAsr, rlResult.lowReg, tReg, k);
+ }
+ } else {
+ int cReg = dvmCompilerAllocTemp(cUnit);
+ loadConstant(cUnit, cReg, lit - 1);
+ int tReg1 = dvmCompilerAllocTemp(cUnit);
+ int tReg2 = dvmCompilerAllocTemp(cUnit);
+ if (lit == 2) {
+ opRegRegImm(cUnit, kOpLsr, tReg1, rlSrc.lowReg, 32 - k);
+ opRegRegReg(cUnit, kOpAdd, tReg2, tReg1, rlSrc.lowReg);
+ opRegRegReg(cUnit, kOpAnd, tReg2, tReg2, cReg);
+ opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg2, tReg1);
+ } else {
+ opRegRegImm(cUnit, kOpAsr, tReg1, rlSrc.lowReg, 31);
+ opRegRegImm(cUnit, kOpLsr, tReg1, tReg1, 32 - k);
+ opRegRegReg(cUnit, kOpAdd, tReg2, tReg1, rlSrc.lowReg);
+ opRegRegReg(cUnit, kOpAnd, tReg2, tReg2, cReg);
+ opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg2, tReg1);
+ }
+ }
+ storeValue(cUnit, rlDest, rlResult);
+ return true;
+}
+
+// Returns true if it added instructions to 'cUnit' to multiply 'rlSrc' by 'lit'
+// and store the result in 'rlDest'.
+static bool handleEasyMultiply(CompilationUnit *cUnit,
+ RegLocation rlSrc, RegLocation rlDest, int lit)
+{
+ // Can we simplify this multiplication?
+ bool powerOfTwo = false;
+ bool popCountLE2 = false;
+ bool powerOfTwoMinusOne = false;
+ if (lit < 2) {
+ // Avoid special cases.
+ return false;
+ } else if (isPowerOfTwo(lit)) {
+ powerOfTwo = true;
+ } else if (isPopCountLE2(lit)) {
+ popCountLE2 = true;
+ } else if (isPowerOfTwo(lit + 1)) {
+ powerOfTwoMinusOne = true;
+ } else {
+ return false;
+ }
+ rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+ RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+ if (powerOfTwo) {
+ // Shift.
+ opRegRegImm(cUnit, kOpLsl, rlResult.lowReg, rlSrc.lowReg,
+ lowestSetBit(lit));
+ } else if (popCountLE2) {
+ // Shift and add and shift.
+ int firstBit = lowestSetBit(lit);
+ int secondBit = lowestSetBit(lit ^ (1 << firstBit));
+ genMultiplyByTwoBitMultiplier(cUnit, rlSrc, rlResult, lit,
+ firstBit, secondBit);
+ } else {
+ // Reverse subtract: (src << (shift + 1)) - src.
+ assert(powerOfTwoMinusOne);
+ // TODO: rsb dst, src, src lsl#lowestSetBit(lit + 1)
+ int tReg = dvmCompilerAllocTemp(cUnit);
+ opRegRegImm(cUnit, kOpLsl, tReg, rlSrc.lowReg, lowestSetBit(lit + 1));
+ opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg, rlSrc.lowReg);
+ }
+ storeValue(cUnit, rlDest, rlResult);
+ return true;
+}
+
+static bool handleFmt22b_Fmt22s(CompilationUnit *cUnit, MIR *mir)
+{
+ OpCode dalvikOpCode = mir->dalvikInsn.opCode;
+ RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+ RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+ RegLocation rlResult;
+ int lit = mir->dalvikInsn.vC;
+ OpKind op = 0; /* Make gcc happy */
+ int shiftOp = false;
+ bool isDiv = false;
+
+ switch (dalvikOpCode) {
+ case OP_RSUB_INT_LIT8:
+ case OP_RSUB_INT: {
+ int tReg;
+ //TUNING: add support for use of Arm rsub op
+ rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+ tReg = dvmCompilerAllocTemp(cUnit);
+ loadConstant(cUnit, tReg, lit);
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+ opRegRegReg(cUnit, kOpSub, rlResult.lowReg,
+ tReg, rlSrc.lowReg);
+ storeValue(cUnit, rlDest, rlResult);
+ return false;
+ break;
+ }
+
+ case OP_ADD_INT_LIT8:
+ case OP_ADD_INT_LIT16:
+ op = kOpAdd;
+ break;
+ case OP_MUL_INT_LIT8:
+ case OP_MUL_INT_LIT16: {
+ if (handleEasyMultiply(cUnit, rlSrc, rlDest, lit)) {
+ return false;
+ }
+ op = kOpMul;
+ break;
+ }
+ case OP_AND_INT_LIT8:
+ case OP_AND_INT_LIT16:
+ op = kOpAnd;
+ break;
+ case OP_OR_INT_LIT8:
+ case OP_OR_INT_LIT16:
+ op = kOpOr;
+ break;
+ case OP_XOR_INT_LIT8:
+ case OP_XOR_INT_LIT16:
+ op = kOpXor;
+ break;
+ case OP_SHL_INT_LIT8:
+ lit &= 31;
+ shiftOp = true;
+ op = kOpLsl;
+ break;
+ case OP_SHR_INT_LIT8:
+ lit &= 31;
+ shiftOp = true;
+ op = kOpAsr;
+ break;
+ case OP_USHR_INT_LIT8:
+ lit &= 31;
+ shiftOp = true;
+ op = kOpLsr;
+ break;
+
+ case OP_DIV_INT_LIT8:
+ case OP_DIV_INT_LIT16:
+ case OP_REM_INT_LIT8:
+ case OP_REM_INT_LIT16:
+ if (lit == 0) {
+ /* Let the interpreter deal with div by 0 */
+ genInterpSingleStep(cUnit, mir);
+ return false;
+ }
+ if (handleEasyDivide(cUnit, dalvikOpCode, rlSrc, rlDest, lit)) {
+ return false;
+ }
+ dvmCompilerFlushAllRegs(cUnit); /* Everything to home location */
+ loadValueDirectFixed(cUnit, rlSrc, r0);
+ dvmCompilerClobber(cUnit, r0);
+ if ((dalvikOpCode == OP_DIV_INT_LIT8) ||
+ (dalvikOpCode == OP_DIV_INT_LIT16)) {
+ LOAD_FUNC_ADDR(cUnit, r2, (int)__aeabi_idiv);
+ isDiv = true;
+ } else {
+ LOAD_FUNC_ADDR(cUnit, r2, (int)__aeabi_idivmod);
+ isDiv = false;
+ }
+ loadConstant(cUnit, r1, lit);
+ opReg(cUnit, kOpBlx, r2);
+ dvmCompilerClobberCallRegs(cUnit);
+ if (isDiv)
+ rlResult = dvmCompilerGetReturn(cUnit);
+ else
+ rlResult = dvmCompilerGetReturnAlt(cUnit);
+ storeValue(cUnit, rlDest, rlResult);
+ return false;
+ break;
+ default:
+ return true;
+ }
+ rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+ // Avoid shifts by literal 0 - no support in Thumb. Change to copy
+ if (shiftOp && (lit == 0)) {
+ genRegCopy(cUnit, rlResult.lowReg, rlSrc.lowReg);
+ } else {
+ opRegRegImm(cUnit, op, rlResult.lowReg, rlSrc.lowReg, lit);
+ }
+ storeValue(cUnit, rlDest, rlResult);
+ return false;
+}
+
+static bool handleFmt22c(CompilationUnit *cUnit, MIR *mir)
+{
+ OpCode dalvikOpCode = mir->dalvikInsn.opCode;
+ int fieldOffset = -1;
+ bool isVolatile = false;
+ switch (dalvikOpCode) {
+ /*
+ * Wide volatiles currently handled via single step.
+ * Add them here if generating in-line code.
+ * case OP_IGET_WIDE_VOLATILE:
+ * case OP_IPUT_WIDE_VOLATILE:
+ */
+ case OP_IGET:
+ case OP_IGET_VOLATILE:
+ case OP_IGET_WIDE:
+ case OP_IGET_OBJECT:
+ case OP_IGET_OBJECT_VOLATILE:
+ case OP_IGET_BOOLEAN:
+ case OP_IGET_BYTE:
+ case OP_IGET_CHAR:
+ case OP_IGET_SHORT:
+ case OP_IPUT:
+ case OP_IPUT_VOLATILE:
+ case OP_IPUT_WIDE:
+ case OP_IPUT_OBJECT:
+ case OP_IPUT_OBJECT_VOLATILE:
+ case OP_IPUT_BOOLEAN:
+ case OP_IPUT_BYTE:
+ case OP_IPUT_CHAR:
+ case OP_IPUT_SHORT: {
+ const Method *method = (mir->OptimizationFlags & MIR_CALLEE) ?
+ mir->meta.calleeMethod : cUnit->method;
+ Field *fieldPtr =
+ method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vC];
+
+ if (fieldPtr == NULL) {
+ LOGE("Unexpected null instance field");
+ dvmAbort();
+ }
+ isVolatile = dvmIsVolatileField(fieldPtr);
+ fieldOffset = ((InstField *)fieldPtr)->byteOffset;
+ break;
+ }
+ default:
+ break;
+ }
+
+ switch (dalvikOpCode) {
+ case OP_NEW_ARRAY: {
+ // Generates a call - use explicit registers
+ RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+ RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+ RegLocation rlResult;
+ void *classPtr = (void*)
+ (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vC]);
+
+ if (classPtr == NULL) {
+ LOGE("Unexpected null class");
+ dvmAbort();
+ }
+
+ dvmCompilerFlushAllRegs(cUnit); /* Everything to home location */
+ genExportPC(cUnit, mir);
+ loadValueDirectFixed(cUnit, rlSrc, r1); /* Len */
+ loadConstant(cUnit, r0, (int) classPtr );
+ LOAD_FUNC_ADDR(cUnit, r3, (int)dvmAllocArrayByClass);
+ /*
+ * "len < 0": bail to the interpreter to re-execute the
+ * instruction
+ */
+ genRegImmCheck(cUnit, kArmCondMi, r1, 0, mir->offset, NULL);
+ loadConstant(cUnit, r2, ALLOC_DONT_TRACK);
+ opReg(cUnit, kOpBlx, r3);
+ dvmCompilerClobberCallRegs(cUnit);
+ /* generate a branch over if allocation is successful */
+ opRegImm(cUnit, kOpCmp, r0, 0); /* NULL? */
+ ArmLIR *branchOver = opCondBranch(cUnit, kArmCondNe);
+ /*
+ * OOM exception needs to be thrown here and cannot re-execute
+ */
+ loadConstant(cUnit, r0,
+ (int) (cUnit->method->insns + mir->offset));
+ genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON);
+ /* noreturn */
+
+ ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+ target->defMask = ENCODE_ALL;
+ branchOver->generic.target = (LIR *) target;
+ rlResult = dvmCompilerGetReturn(cUnit);
+ storeValue(cUnit, rlDest, rlResult);
+ break;
+ }
+ case OP_INSTANCE_OF: {
+ // May generate a call - use explicit registers
+ RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+ RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+ RegLocation rlResult;
+ ClassObject *classPtr =
+ (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vC]);
+ /*
+ * Note: It is possible that classPtr is NULL at this point,
+ * even though this instruction has been successfully interpreted.
+ * If the previous interpretation had a null source, the
+ * interpreter would not have bothered to resolve the clazz.
+ * Bail out to the interpreter in this case, and log it
+ * so that we can tell if it happens frequently.
+ */
+ if (classPtr == NULL) {
+ LOGD("null clazz in OP_INSTANCE_OF, single-stepping");
+ genInterpSingleStep(cUnit, mir);
+ break;
+ }
+ dvmCompilerFlushAllRegs(cUnit); /* Everything to home location */
+ loadValueDirectFixed(cUnit, rlSrc, r0); /* Ref */
+ loadConstant(cUnit, r2, (int) classPtr );
+//TUNING: compare to 0 primative to allow use of CB[N]Z
+ opRegImm(cUnit, kOpCmp, r0, 0); /* NULL? */
+ /* When taken r0 has NULL which can be used for store directly */
+ ArmLIR *branch1 = opCondBranch(cUnit, kArmCondEq);
+ /* r1 now contains object->clazz */
+ loadWordDisp(cUnit, r0, offsetof(Object, clazz), r1);
+ /* r1 now contains object->clazz */
+ LOAD_FUNC_ADDR(cUnit, r3, (int)dvmInstanceofNonTrivial);
+ loadConstant(cUnit, r0, 1); /* Assume true */
+ opRegReg(cUnit, kOpCmp, r1, r2);
+ ArmLIR *branch2 = opCondBranch(cUnit, kArmCondEq);
+ genRegCopy(cUnit, r0, r1);
+ genRegCopy(cUnit, r1, r2);
+ opReg(cUnit, kOpBlx, r3);
+ dvmCompilerClobberCallRegs(cUnit);
+ /* branch target here */
+ ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+ target->defMask = ENCODE_ALL;
+ rlResult = dvmCompilerGetReturn(cUnit);
+ storeValue(cUnit, rlDest, rlResult);
+ branch1->generic.target = (LIR *)target;
+ branch2->generic.target = (LIR *)target;
+ break;
+ }
+ case OP_IGET_WIDE:
+ genIGetWide(cUnit, mir, fieldOffset);
+ break;
+ case OP_IGET_VOLATILE:
+ case OP_IGET_OBJECT_VOLATILE:
+ isVolatile = true;
+ // NOTE: intentional fallthrough
+ case OP_IGET:
+ case OP_IGET_OBJECT:
+ case OP_IGET_BOOLEAN:
+ case OP_IGET_BYTE:
+ case OP_IGET_CHAR:
+ case OP_IGET_SHORT:
+ genIGet(cUnit, mir, kWord, fieldOffset, isVolatile);
+ break;
+ case OP_IPUT_WIDE:
+ genIPutWide(cUnit, mir, fieldOffset);
+ break;
+ case OP_IPUT:
+ case OP_IPUT_SHORT:
+ case OP_IPUT_CHAR:
+ case OP_IPUT_BYTE:
+ case OP_IPUT_BOOLEAN:
+ genIPut(cUnit, mir, kWord, fieldOffset, false, isVolatile);
+ break;
+ case OP_IPUT_VOLATILE:
+ case OP_IPUT_OBJECT_VOLATILE:
+ isVolatile = true;
+ // NOTE: intentional fallthrough
+ case OP_IPUT_OBJECT:
+ genIPut(cUnit, mir, kWord, fieldOffset, true, isVolatile);
+ break;
+ case OP_IGET_WIDE_VOLATILE:
+ case OP_IPUT_WIDE_VOLATILE:
+ genInterpSingleStep(cUnit, mir);
+ break;
+ default:
+ return true;
+ }
+ return false;
+}
+
+static bool handleFmt22cs(CompilationUnit *cUnit, MIR *mir)
+{
+ OpCode dalvikOpCode = mir->dalvikInsn.opCode;
+ int fieldOffset = mir->dalvikInsn.vC;
+ switch (dalvikOpCode) {
+ case OP_IGET_QUICK:
+ case OP_IGET_OBJECT_QUICK:
+ genIGet(cUnit, mir, kWord, fieldOffset, false);
+ break;
+ case OP_IPUT_QUICK:
+ genIPut(cUnit, mir, kWord, fieldOffset, false, false);
+ break;
+ case OP_IPUT_OBJECT_QUICK:
+ genIPut(cUnit, mir, kWord, fieldOffset, true, false);
+ break;
+ case OP_IGET_WIDE_QUICK:
+ genIGetWide(cUnit, mir, fieldOffset);
+ break;
+ case OP_IPUT_WIDE_QUICK:
+ genIPutWide(cUnit, mir, fieldOffset);
+ break;
+ default:
+ return true;
+ }
+ return false;
+
+}
+
+/* Compare agaist zero */
+static bool handleFmt22t(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb,
+ ArmLIR *labelList)
+{
+ OpCode dalvikOpCode = mir->dalvikInsn.opCode;
+ ArmConditionCode cond;
+ RegLocation rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 0);
+ RegLocation rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 1);
+
+ rlSrc1 = loadValue(cUnit, rlSrc1, kCoreReg);
+ rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg);
+ opRegReg(cUnit, kOpCmp, rlSrc1.lowReg, rlSrc2.lowReg);
+
+ switch (dalvikOpCode) {
+ case OP_IF_EQ:
+ cond = kArmCondEq;
+ break;
+ case OP_IF_NE:
+ cond = kArmCondNe;
+ break;
+ case OP_IF_LT:
+ cond = kArmCondLt;
+ break;
+ case OP_IF_GE:
+ cond = kArmCondGe;
+ break;
+ case OP_IF_GT:
+ cond = kArmCondGt;
+ break;
+ case OP_IF_LE:
+ cond = kArmCondLe;
+ break;
+ default:
+ cond = 0;
+ LOGE("Unexpected opcode (%d) for Fmt22t\n", dalvikOpCode);
+ dvmCompilerAbort(cUnit);
+ }
+ genConditionalBranch(cUnit, cond, &labelList[bb->taken->id]);
+ /* This mostly likely will be optimized away in a later phase */
+ genUnconditionalBranch(cUnit, &labelList[bb->fallThrough->id]);
+ return false;
+}
+
+static bool handleFmt22x_Fmt32x(CompilationUnit *cUnit, MIR *mir)
+{
+ OpCode opCode = mir->dalvikInsn.opCode;
+
+ switch (opCode) {
+ case OP_MOVE_16:
+ case OP_MOVE_OBJECT_16:
+ case OP_MOVE_FROM16:
+ case OP_MOVE_OBJECT_FROM16: {
+ storeValue(cUnit, dvmCompilerGetDest(cUnit, mir, 0),
+ dvmCompilerGetSrc(cUnit, mir, 0));
+ break;
+ }
+ case OP_MOVE_WIDE_16:
+ case OP_MOVE_WIDE_FROM16: {
+ storeValueWide(cUnit, dvmCompilerGetDestWide(cUnit, mir, 0, 1),
+ dvmCompilerGetSrcWide(cUnit, mir, 0, 1));
+ break;
+ }
+ default:
+ return true;
+ }
+ return false;
+}
+
+static bool handleFmt23x(CompilationUnit *cUnit, MIR *mir)
+{
+ OpCode opCode = mir->dalvikInsn.opCode;
+ RegLocation rlSrc1;
+ RegLocation rlSrc2;
+ RegLocation rlDest;
+
+ if ( (opCode >= OP_ADD_INT) && (opCode <= OP_REM_DOUBLE)) {
+ return genArithOp( cUnit, mir );
+ }
+
+ /* APUTs have 3 sources and no targets */
+ if (mir->ssaRep->numDefs == 0) {
+ if (mir->ssaRep->numUses == 3) {
+ rlDest = dvmCompilerGetSrc(cUnit, mir, 0);
+ rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 1);
+ rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 2);
+ } else {
+ assert(mir->ssaRep->numUses == 4);
+ rlDest = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+ rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 2);
+ rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 3);
+ }
+ } else {
+ /* Two sources and 1 dest. Deduce the operand sizes */
+ if (mir->ssaRep->numUses == 4) {
+ rlSrc1 = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+ rlSrc2 = dvmCompilerGetSrcWide(cUnit, mir, 2, 3);
+ } else {
+ assert(mir->ssaRep->numUses == 2);
+ rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 0);
+ rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 1);
+ }
+ if (mir->ssaRep->numDefs == 2) {
+ rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+ } else {
+ assert(mir->ssaRep->numDefs == 1);
+ rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+ }
+ }
+
+
+ switch (opCode) {
+ case OP_CMPL_FLOAT:
+ case OP_CMPG_FLOAT:
+ case OP_CMPL_DOUBLE:
+ case OP_CMPG_DOUBLE:
+ return genCmpFP(cUnit, mir, rlDest, rlSrc1, rlSrc2);
+ case OP_CMP_LONG:
+ genCmpLong(cUnit, mir, rlDest, rlSrc1, rlSrc2);
+ break;
+ case OP_AGET_WIDE:
+ genArrayGet(cUnit, mir, kLong, rlSrc1, rlSrc2, rlDest, 3);
+ break;
+ case OP_AGET:
+ case OP_AGET_OBJECT:
+ genArrayGet(cUnit, mir, kWord, rlSrc1, rlSrc2, rlDest, 2);
+ break;
+ case OP_AGET_BOOLEAN:
+ genArrayGet(cUnit, mir, kUnsignedByte, rlSrc1, rlSrc2, rlDest, 0);
+ break;
+ case OP_AGET_BYTE:
+ genArrayGet(cUnit, mir, kSignedByte, rlSrc1, rlSrc2, rlDest, 0);
+ break;
+ case OP_AGET_CHAR:
+ genArrayGet(cUnit, mir, kUnsignedHalf, rlSrc1, rlSrc2, rlDest, 1);
+ break;
+ case OP_AGET_SHORT:
+ genArrayGet(cUnit, mir, kSignedHalf, rlSrc1, rlSrc2, rlDest, 1);
+ break;
+ case OP_APUT_WIDE:
+ genArrayPut(cUnit, mir, kLong, rlSrc1, rlSrc2, rlDest, 3);
+ break;
+ case OP_APUT:
+ genArrayPut(cUnit, mir, kWord, rlSrc1, rlSrc2, rlDest, 2);
+ break;
+ case OP_APUT_OBJECT:
+ genArrayObjectPut(cUnit, mir, rlSrc1, rlSrc2, rlDest, 2);
+ break;
+ case OP_APUT_SHORT:
+ case OP_APUT_CHAR:
+ genArrayPut(cUnit, mir, kUnsignedHalf, rlSrc1, rlSrc2, rlDest, 1);
+ break;
+ case OP_APUT_BYTE:
+ case OP_APUT_BOOLEAN:
+ genArrayPut(cUnit, mir, kUnsignedByte, rlSrc1, rlSrc2, rlDest, 0);
+ break;
+ default:
+ return true;
+ }
+ return false;
+}
+
+/*
+ * Find the matching case.
+ *
+ * return values:
+ * r0 (low 32-bit): pc of the chaining cell corresponding to the resolved case,
+ * including default which is placed at MIN(size, MAX_CHAINED_SWITCH_CASES).
+ * r1 (high 32-bit): the branch offset of the matching case (only for indexes
+ * above MAX_CHAINED_SWITCH_CASES).
+ *
+ * Instructions around the call are:
+ *
+ * mov r2, pc
+ * blx &findPackedSwitchIndex
+ * mov pc, r0
+ * .align4
+ * chaining cell for case 0 [12 bytes]
+ * chaining cell for case 1 [12 bytes]
+ * :
+ * chaining cell for case MIN(size, MAX_CHAINED_SWITCH_CASES)-1 [12 bytes]
+ * chaining cell for case default [8 bytes]
+ * noChain exit
+ */
+static s8 findPackedSwitchIndex(const u2* switchData, int testVal, int pc)
+{
+ int size;
+ int firstKey;
+ const int *entries;
+ int index;
+ int jumpIndex;
+ int caseDPCOffset = 0;
+ /* In Thumb mode pc is 4 ahead of the "mov r2, pc" instruction */
+ int chainingPC = (pc + 4) & ~3;
+
+ /*
+ * Packed switch data format:
+ * ushort ident = 0x0100 magic value
+ * ushort size number of entries in the table
+ * int first_key first (and lowest) switch case value
+ * int targets[size] branch targets, relative to switch opcode
+ *
+ * Total size is (4+size*2) 16-bit code units.
+ */
+ size = switchData[1];
+ assert(size > 0);
+
+ firstKey = switchData[2];
+ firstKey |= switchData[3] << 16;
+
+
+ /* The entries are guaranteed to be aligned on a 32-bit boundary;
+ * we can treat them as a native int array.
+ */
+ entries = (const int*) &switchData[4];
+ assert(((u4)entries & 0x3) == 0);
+
+ index = testVal - firstKey;
+
+ /* Jump to the default cell */
+ if (index < 0 || index >= size) {
+ jumpIndex = MIN(size, MAX_CHAINED_SWITCH_CASES);
+ /* Jump to the non-chaining exit point */
+ } else if (index >= MAX_CHAINED_SWITCH_CASES) {
+ jumpIndex = MAX_CHAINED_SWITCH_CASES + 1;
+ caseDPCOffset = entries[index];
+ /* Jump to the inline chaining cell */
+ } else {
+ jumpIndex = index;
+ }
+
+ chainingPC += jumpIndex * CHAIN_CELL_NORMAL_SIZE;
+ return (((s8) caseDPCOffset) << 32) | (u8) chainingPC;
+}
+
+/* See comments for findPackedSwitchIndex */
+static s8 findSparseSwitchIndex(const u2* switchData, int testVal, int pc)
+{
+ int size;
+ const int *keys;
+ const int *entries;
+ int chainingPC = (pc + 4) & ~3;
+ int i;
+
+ /*
+ * Sparse switch data format:
+ * ushort ident = 0x0200 magic value
+ * ushort size number of entries in the table; > 0
+ * int keys[size] keys, sorted low-to-high; 32-bit aligned
+ * int targets[size] branch targets, relative to switch opcode
+ *
+ * Total size is (2+size*4) 16-bit code units.
+ */
+
+ size = switchData[1];
+ assert(size > 0);
+
+ /* The keys are guaranteed to be aligned on a 32-bit boundary;
+ * we can treat them as a native int array.
+ */
+ keys = (const int*) &switchData[2];
+ assert(((u4)keys & 0x3) == 0);
+
+ /* The entries are guaranteed to be aligned on a 32-bit boundary;
+ * we can treat them as a native int array.
+ */
+ entries = keys + size;
+ assert(((u4)entries & 0x3) == 0);
+
+ /*
+ * Run through the list of keys, which are guaranteed to
+ * be sorted low-to-high.
+ *
+ * Most tables have 3-4 entries. Few have more than 10. A binary
+ * search here is probably not useful.
+ */
+ for (i = 0; i < size; i++) {
+ int k = keys[i];
+ if (k == testVal) {
+ /* MAX_CHAINED_SWITCH_CASES + 1 is the start of the overflow case */
+ int jumpIndex = (i < MAX_CHAINED_SWITCH_CASES) ?
+ i : MAX_CHAINED_SWITCH_CASES + 1;
+ chainingPC += jumpIndex * CHAIN_CELL_NORMAL_SIZE;
+ return (((s8) entries[i]) << 32) | (u8) chainingPC;
+ } else if (k > testVal) {
+ break;
+ }
+ }
+ return chainingPC + MIN(size, MAX_CHAINED_SWITCH_CASES) *
+ CHAIN_CELL_NORMAL_SIZE;
+}
+
+static bool handleFmt31t(CompilationUnit *cUnit, MIR *mir)
+{
+ OpCode dalvikOpCode = mir->dalvikInsn.opCode;
+ switch (dalvikOpCode) {
+ case OP_FILL_ARRAY_DATA: {
+ RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+ // Making a call - use explicit registers
+ dvmCompilerFlushAllRegs(cUnit); /* Everything to home location */
+ genExportPC(cUnit, mir);
+ loadValueDirectFixed(cUnit, rlSrc, r0);
+ LOAD_FUNC_ADDR(cUnit, r2, (int)dvmInterpHandleFillArrayData);
+ loadConstant(cUnit, r1,
+ (int) (cUnit->method->insns + mir->offset + mir->dalvikInsn.vB));
+ opReg(cUnit, kOpBlx, r2);
+ dvmCompilerClobberCallRegs(cUnit);
+ /* generate a branch over if successful */
+ opRegImm(cUnit, kOpCmp, r0, 0); /* NULL? */
+ ArmLIR *branchOver = opCondBranch(cUnit, kArmCondNe);
+ loadConstant(cUnit, r0,
+ (int) (cUnit->method->insns + mir->offset));
+ genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON);
+ ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+ target->defMask = ENCODE_ALL;
+ branchOver->generic.target = (LIR *) target;
+ break;
+ }
+ /*
+ * Compute the goto target of up to
+ * MIN(switchSize, MAX_CHAINED_SWITCH_CASES) + 1 chaining cells.
+ * See the comment before findPackedSwitchIndex for the code layout.
+ */
+ case OP_PACKED_SWITCH:
+ case OP_SPARSE_SWITCH: {
+ RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+ dvmCompilerFlushAllRegs(cUnit); /* Everything to home location */
+ loadValueDirectFixed(cUnit, rlSrc, r1);
+ dvmCompilerLockAllTemps(cUnit);
+ if (dalvikOpCode == OP_PACKED_SWITCH) {
+ LOAD_FUNC_ADDR(cUnit, r4PC, (int)findPackedSwitchIndex);
+ } else {
+ LOAD_FUNC_ADDR(cUnit, r4PC, (int)findSparseSwitchIndex);
+ }
+ /* r0 <- Addr of the switch data */
+ loadConstant(cUnit, r0,
+ (int) (cUnit->method->insns + mir->offset + mir->dalvikInsn.vB));
+ /* r2 <- pc of the instruction following the blx */
+ opRegReg(cUnit, kOpMov, r2, rpc);
+ opReg(cUnit, kOpBlx, r4PC);
+ dvmCompilerClobberCallRegs(cUnit);
+ /* pc <- computed goto target */
+ opRegReg(cUnit, kOpMov, rpc, r0);
+ break;
+ }
+ default:
+ return true;
+ }
+ return false;
+}
+
+/*
+ * See the example of predicted inlining listed before the
+ * genValidationForPredictedInline function. The function here takes care the
+ * branch over at 0x4858de78 and the misprediction target at 0x4858de7a.
+ */
+static void genLandingPadForMispredictedCallee(CompilationUnit *cUnit, MIR *mir,
+ BasicBlock *bb,
+ ArmLIR *labelList)
+{
+ BasicBlock *fallThrough = bb->fallThrough;
+
+ /* Bypass the move-result block if there is one */
+ if (fallThrough->firstMIRInsn) {
+ assert(fallThrough->firstMIRInsn->OptimizationFlags & MIR_INLINED_PRED);
+ fallThrough = fallThrough->fallThrough;
+ }
+ /* Generate a branch over if the predicted inlining is correct */
+ genUnconditionalBranch(cUnit, &labelList[fallThrough->id]);
+
+ /* Reset the register state */
+ dvmCompilerResetRegPool(cUnit);
+ dvmCompilerClobberAllRegs(cUnit);
+ dvmCompilerResetNullCheck(cUnit);
+
+ /* Target for the slow invoke path */
+ ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+ target->defMask = ENCODE_ALL;
+ /* Hook up the target to the verification branch */
+ mir->meta.callsiteInfo->misPredBranchOver->target = (LIR *) target;
+}
+
+static bool handleFmt35c_3rc(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb,
+ ArmLIR *labelList)
+{
+ ArmLIR *retChainingCell = NULL;
+ ArmLIR *pcrLabel = NULL;
+
+ /* An invoke with the MIR_INLINED is effectively a no-op */
+ if (mir->OptimizationFlags & MIR_INLINED)
+ return false;
+
+ if (bb->fallThrough != NULL)
+ retChainingCell = &labelList[bb->fallThrough->id];
+
+ DecodedInstruction *dInsn = &mir->dalvikInsn;
+ switch (mir->dalvikInsn.opCode) {
+ /*
+ * calleeMethod = this->clazz->vtable[
+ * method->clazz->pDvmDex->pResMethods[BBBB]->methodIndex
+ * ]
+ */
+ case OP_INVOKE_VIRTUAL:
+ case OP_INVOKE_VIRTUAL_RANGE: {
+ ArmLIR *predChainingCell = &labelList[bb->taken->id];
+ int methodIndex =
+ cUnit->method->clazz->pDvmDex->pResMethods[dInsn->vB]->
+ methodIndex;
+
+ /*
+ * If the invoke has non-null misPredBranchOver, we need to generate
+ * the non-inlined version of the invoke here to handle the
+ * mispredicted case.
+ */
+ if (mir->meta.callsiteInfo->misPredBranchOver) {
+ genLandingPadForMispredictedCallee(cUnit, mir, bb, labelList);
+ }
+
+ if (mir->dalvikInsn.opCode == OP_INVOKE_VIRTUAL)
+ genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel);
+ else
+ genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel);
+
+ genInvokeVirtualCommon(cUnit, mir, methodIndex,
+ retChainingCell,
+ predChainingCell,
+ pcrLabel);
+ break;
+ }
+ /*
+ * calleeMethod = method->clazz->super->vtable[method->clazz->pDvmDex
+ * ->pResMethods[BBBB]->methodIndex]
+ */
+ case OP_INVOKE_SUPER:
+ case OP_INVOKE_SUPER_RANGE: {
+ /* Grab the method ptr directly from what the interpreter sees */
+ const Method *calleeMethod = mir->meta.callsiteInfo->method;
+ assert(calleeMethod == cUnit->method->clazz->super->vtable[
+ cUnit->method->clazz->pDvmDex->
+ pResMethods[dInsn->vB]->methodIndex]);
+
+ if (mir->dalvikInsn.opCode == OP_INVOKE_SUPER)
+ genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel);
+ else
+ genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel);
+
+ /* r0 = calleeMethod */
+ loadConstant(cUnit, r0, (int) calleeMethod);
+
+ genInvokeSingletonCommon(cUnit, mir, bb, labelList, pcrLabel,
+ calleeMethod);
+ break;
+ }
+ /* calleeMethod = method->clazz->pDvmDex->pResMethods[BBBB] */
+ case OP_INVOKE_DIRECT:
+ case OP_INVOKE_DIRECT_RANGE: {
+ /* Grab the method ptr directly from what the interpreter sees */
+ const Method *calleeMethod = mir->meta.callsiteInfo->method;
+ assert(calleeMethod ==
+ cUnit->method->clazz->pDvmDex->pResMethods[dInsn->vB]);
+
+ if (mir->dalvikInsn.opCode == OP_INVOKE_DIRECT)
+ genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel);
+ else
+ genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel);
+
+ /* r0 = calleeMethod */
+ loadConstant(cUnit, r0, (int) calleeMethod);
+
+ genInvokeSingletonCommon(cUnit, mir, bb, labelList, pcrLabel,
+ calleeMethod);
+ break;
+ }
+ /* calleeMethod = method->clazz->pDvmDex->pResMethods[BBBB] */
+ case OP_INVOKE_STATIC:
+ case OP_INVOKE_STATIC_RANGE: {
+ /* Grab the method ptr directly from what the interpreter sees */
+ const Method *calleeMethod = mir->meta.callsiteInfo->method;
+ assert(calleeMethod ==
+ cUnit->method->clazz->pDvmDex->pResMethods[dInsn->vB]);
+
+ if (mir->dalvikInsn.opCode == OP_INVOKE_STATIC)
+ genProcessArgsNoRange(cUnit, mir, dInsn,
+ NULL /* no null check */);
+ else
+ genProcessArgsRange(cUnit, mir, dInsn,
+ NULL /* no null check */);
+
+ /* r0 = calleeMethod */
+ loadConstant(cUnit, r0, (int) calleeMethod);
+
+ genInvokeSingletonCommon(cUnit, mir, bb, labelList, pcrLabel,
+ calleeMethod);
+ break;
+ }
+ /*
+ * calleeMethod = dvmFindInterfaceMethodInCache(this->clazz,
+ * BBBB, method, method->clazz->pDvmDex)
+ *
+ * The following is an example of generated code for
+ * "invoke-interface v0"
+ *
+ * -------- dalvik offset: 0x0008 @ invoke-interface v0
+ * 0x47357e36 : ldr r0, [r5, #0] --+
+ * 0x47357e38 : sub r7,r5,#24 |
+ * 0x47357e3c : cmp r0, #0 | genProcessArgsNoRange
+ * 0x47357e3e : beq 0x47357e82 |
+ * 0x47357e40 : stmia r7, <r0> --+
+ * 0x47357e42 : ldr r4, [pc, #120] --> r4 <- dalvikPC of this invoke
+ * 0x47357e44 : add r1, pc, #64 --> r1 <- &retChainingCell
+ * 0x47357e46 : add r2, pc, #72 --> r2 <- &predictedChainingCell
+ * 0x47357e48 : blx_1 0x47348190 --+ TEMPLATE_INVOKE_METHOD_
+ * 0x47357e4a : blx_2 see above --+ PREDICTED_CHAIN
+ * 0x47357e4c : b 0x47357e90 --> off to the predicted chain
+ * 0x47357e4e : b 0x47357e82 --> punt to the interpreter
+ * 0x47357e50 : mov r8, r1 --+
+ * 0x47357e52 : mov r9, r2 |
+ * 0x47357e54 : ldr r2, [pc, #96] |
+ * 0x47357e56 : mov r10, r3 |
+ * 0x47357e58 : movs r0, r3 | dvmFindInterfaceMethodInCache
+ * 0x47357e5a : ldr r3, [pc, #88] |
+ * 0x47357e5c : ldr r7, [pc, #80] |
+ * 0x47357e5e : mov r1, #1452 |
+ * 0x47357e62 : blx r7 --+
+ * 0x47357e64 : cmp r0, #0 --> calleeMethod == NULL?
+ * 0x47357e66 : bne 0x47357e6e --> branch over the throw if !r0
+ * 0x47357e68 : ldr r0, [pc, #80] --> load Dalvik PC of the invoke
+ * 0x47357e6a : blx_1 0x47348494 --+ TEMPLATE_THROW_EXCEPTION_
+ * 0x47357e6c : blx_2 see above --+ COMMON
+ * 0x47357e6e : mov r1, r8 --> r1 <- &retChainingCell
+ * 0x47357e70 : cmp r1, #0 --> compare against 0
+ * 0x47357e72 : bgt 0x47357e7c --> >=0? don't rechain
+ * 0x47357e74 : ldr r7, [r6, #108] --+
+ * 0x47357e76 : mov r2, r9 | dvmJitToPatchPredictedChain
+ * 0x47357e78 : mov r3, r10 |
+ * 0x47357e7a : blx r7 --+
+ * 0x47357e7c : add r1, pc, #8 --> r1 <- &retChainingCell
+ * 0x47357e7e : blx_1 0x4734809c --+ TEMPLATE_INVOKE_METHOD_NO_OPT
+ * 0x47357e80 : blx_2 see above --+
+ * -------- reconstruct dalvik PC : 0x425719dc @ +0x0008
+ * 0x47357e82 : ldr r0, [pc, #56]
+ * Exception_Handling:
+ * 0x47357e84 : ldr r1, [r6, #92]
+ * 0x47357e86 : blx r1
+ * 0x47357e88 : .align4
+ * -------- chaining cell (hot): 0x000b
+ * 0x47357e88 : ldr r0, [r6, #104]
+ * 0x47357e8a : blx r0
+ * 0x47357e8c : data 0x19e2(6626)
+ * 0x47357e8e : data 0x4257(16983)
+ * 0x47357e90 : .align4
+ * -------- chaining cell (predicted)
+ * 0x47357e90 : data 0xe7fe(59390) --> will be patched into bx
+ * 0x47357e92 : data 0x0000(0)
+ * 0x47357e94 : data 0x0000(0) --> class
+ * 0x47357e96 : data 0x0000(0)
+ * 0x47357e98 : data 0x0000(0) --> method
+ * 0x47357e9a : data 0x0000(0)
+ * 0x47357e9c : data 0x0000(0) --> rechain count
+ * 0x47357e9e : data 0x0000(0)
+ * -------- end of chaining cells (0x006c)
+ * 0x47357eb0 : .word (0xad03e369)
+ * 0x47357eb4 : .word (0x28a90)
+ * 0x47357eb8 : .word (0x41a63394)
+ * 0x47357ebc : .word (0x425719dc)
+ */
+ case OP_INVOKE_INTERFACE:
+ case OP_INVOKE_INTERFACE_RANGE: {
+ ArmLIR *predChainingCell = &labelList[bb->taken->id];
+
+ /*
+ * If the invoke has non-null misPredBranchOver, we need to generate
+ * the non-inlined version of the invoke here to handle the
+ * mispredicted case.
+ */
+ if (mir->meta.callsiteInfo->misPredBranchOver) {
+ genLandingPadForMispredictedCallee(cUnit, mir, bb, labelList);
+ }
+
+ if (mir->dalvikInsn.opCode == OP_INVOKE_INTERFACE)
+ genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel);
+ else
+ genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel);
+
+ /* "this" is already left in r0 by genProcessArgs* */
+
+ /* r4PC = dalvikCallsite */
+ loadConstant(cUnit, r4PC,
+ (int) (cUnit->method->insns + mir->offset));
+
+ /* r1 = &retChainingCell */
+ ArmLIR *addrRetChain =
+ opRegRegImm(cUnit, kOpAdd, r1, rpc, 0);
+ addrRetChain->generic.target = (LIR *) retChainingCell;
+
+ /* r2 = &predictedChainingCell */
+ ArmLIR *predictedChainingCell =
+ opRegRegImm(cUnit, kOpAdd, r2, rpc, 0);
+ predictedChainingCell->generic.target = (LIR *) predChainingCell;
+
+ genDispatchToHandler(cUnit, TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN);
+
+ /* return through lr - jump to the chaining cell */
+ genUnconditionalBranch(cUnit, predChainingCell);
+
+ /*
+ * null-check on "this" may have been eliminated, but we still need
+ * a PC-reconstruction label for stack overflow bailout.
+ */
+ if (pcrLabel == NULL) {
+ int dPC = (int) (cUnit->method->insns + mir->offset);
+ pcrLabel = dvmCompilerNew(sizeof(ArmLIR), true);
+ pcrLabel->opCode = kArmPseudoPCReconstructionCell;
+ pcrLabel->operands[0] = dPC;
+ pcrLabel->operands[1] = mir->offset;
+ /* Insert the place holder to the growable list */
+ dvmInsertGrowableList(&cUnit->pcReconstructionList, pcrLabel);
+ }
+
+ /* return through lr+2 - punt to the interpreter */
+ genUnconditionalBranch(cUnit, pcrLabel);
+
+ /*
+ * return through lr+4 - fully resolve the callee method.
+ * r1 <- count
+ * r2 <- &predictedChainCell
+ * r3 <- this->class
+ * r4 <- dPC
+ * r7 <- this->class->vtable
+ */
+
+ /* Save count, &predictedChainCell, and class to high regs first */
+ genRegCopy(cUnit, r8, r1);
+ genRegCopy(cUnit, r9, r2);
+ genRegCopy(cUnit, r10, r3);
+
+ /* r0 now contains this->clazz */
+ genRegCopy(cUnit, r0, r3);
+
+ /* r1 = BBBB */
+ loadConstant(cUnit, r1, dInsn->vB);
+
+ /* r2 = method (caller) */
+ loadConstant(cUnit, r2, (int) cUnit->method);
+
+ /* r3 = pDvmDex */
+ loadConstant(cUnit, r3, (int) cUnit->method->clazz->pDvmDex);
+
+ LOAD_FUNC_ADDR(cUnit, r7,
+ (intptr_t) dvmFindInterfaceMethodInCache);
+ opReg(cUnit, kOpBlx, r7);
+ /* r0 = calleeMethod (returned from dvmFindInterfaceMethodInCache */
+
+ dvmCompilerClobberCallRegs(cUnit);
+ /* generate a branch over if the interface method is resolved */
+ opRegImm(cUnit, kOpCmp, r0, 0); /* NULL? */
+ ArmLIR *branchOver = opCondBranch(cUnit, kArmCondNe);
+ /*
+ * calleeMethod == NULL -> throw
+ */
+ loadConstant(cUnit, r0,
+ (int) (cUnit->method->insns + mir->offset));
+ genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON);
+ /* noreturn */
+
+ ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+ target->defMask = ENCODE_ALL;
+ branchOver->generic.target = (LIR *) target;
+
+ genRegCopy(cUnit, r1, r8);
+
+ /* Check if rechain limit is reached */
+ opRegImm(cUnit, kOpCmp, r1, 0);
+
+ ArmLIR *bypassRechaining = opCondBranch(cUnit, kArmCondGt);
+
+ loadWordDisp(cUnit, rGLUE, offsetof(InterpState,
+ jitToInterpEntries.dvmJitToPatchPredictedChain), r7);
+
+ genRegCopy(cUnit, r1, rGLUE);
+ genRegCopy(cUnit, r2, r9);
+ genRegCopy(cUnit, r3, r10);
+
+ /*
+ * r0 = calleeMethod
+ * r2 = &predictedChainingCell
+ * r3 = class
+ *
+ * &returnChainingCell has been loaded into r1 but is not needed
+ * when patching the chaining cell and will be clobbered upon
+ * returning so it will be reconstructed again.
+ */
+ opReg(cUnit, kOpBlx, r7);
+
+ /* r1 = &retChainingCell */
+ addrRetChain = opRegRegImm(cUnit, kOpAdd, r1, rpc, 0);
+ addrRetChain->generic.target = (LIR *) retChainingCell;
+
+ bypassRechaining->generic.target = (LIR *) addrRetChain;
+
+ /*
+ * r0 = this, r1 = calleeMethod,
+ * r1 = &ChainingCell,
+ * r4PC = callsiteDPC,
+ */
+ genDispatchToHandler(cUnit, TEMPLATE_INVOKE_METHOD_NO_OPT);
+#if defined(WITH_JIT_TUNING)
+ gDvmJit.invokePolymorphic++;
+#endif
+ /* Handle exceptions using the interpreter */
+ genTrap(cUnit, mir->offset, pcrLabel);
+ break;
+ }
+ /* NOP */
+ case OP_INVOKE_DIRECT_EMPTY: {
+ return false;
+ }
+ case OP_FILLED_NEW_ARRAY:
+ case OP_FILLED_NEW_ARRAY_RANGE: {
+ /* Just let the interpreter deal with these */
+ genInterpSingleStep(cUnit, mir);
+ break;
+ }
+ default:
+ return true;
+ }
+ return false;
+}
+
+static bool handleFmt35ms_3rms(CompilationUnit *cUnit, MIR *mir,
+ BasicBlock *bb, ArmLIR *labelList)
+{
+ ArmLIR *retChainingCell = &labelList[bb->fallThrough->id];
+ ArmLIR *predChainingCell = &labelList[bb->taken->id];
+ ArmLIR *pcrLabel = NULL;
+
+ /* An invoke with the MIR_INLINED is effectively a no-op */
+ if (mir->OptimizationFlags & MIR_INLINED)
+ return false;
+
+ DecodedInstruction *dInsn = &mir->dalvikInsn;
+ switch (mir->dalvikInsn.opCode) {
+ /* calleeMethod = this->clazz->vtable[BBBB] */
+ case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+ case OP_INVOKE_VIRTUAL_QUICK: {
+ int methodIndex = dInsn->vB;
+
+ /*
+ * If the invoke has non-null misPredBranchOver, we need to generate
+ * the non-inlined version of the invoke here to handle the
+ * mispredicted case.
+ */
+ if (mir->meta.callsiteInfo->misPredBranchOver) {
+ genLandingPadForMispredictedCallee(cUnit, mir, bb, labelList);
+ }
+
+ if (mir->dalvikInsn.opCode == OP_INVOKE_VIRTUAL_QUICK)
+ genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel);
+ else
+ genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel);
+
+ genInvokeVirtualCommon(cUnit, mir, methodIndex,
+ retChainingCell,
+ predChainingCell,
+ pcrLabel);
+ break;
+ }
+ /* calleeMethod = method->clazz->super->vtable[BBBB] */
+ case OP_INVOKE_SUPER_QUICK:
+ case OP_INVOKE_SUPER_QUICK_RANGE: {
+ /* Grab the method ptr directly from what the interpreter sees */
+ const Method *calleeMethod = mir->meta.callsiteInfo->method;
+ assert(calleeMethod ==
+ cUnit->method->clazz->super->vtable[dInsn->vB]);
+
+ if (mir->dalvikInsn.opCode == OP_INVOKE_SUPER_QUICK)
+ genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel);
+ else
+ genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel);
+
+ /* r0 = calleeMethod */
+ loadConstant(cUnit, r0, (int) calleeMethod);
+
+ genInvokeSingletonCommon(cUnit, mir, bb, labelList, pcrLabel,
+ calleeMethod);
+ break;
+ }
+ default:
+ return true;
+ }
+ return false;
+}
+
+/*
+ * This operation is complex enough that we'll do it partly inline
+ * and partly with a handler. NOTE: the handler uses hardcoded
+ * values for string object offsets and must be revisitied if the
+ * layout changes.
+ */
+static bool genInlinedCompareTo(CompilationUnit *cUnit, MIR *mir)
+{
+#if defined(USE_GLOBAL_STRING_DEFS)
+ return false;
+#else
+ ArmLIR *rollback;
+ RegLocation rlThis = dvmCompilerGetSrc(cUnit, mir, 0);
+ RegLocation rlComp = dvmCompilerGetSrc(cUnit, mir, 1);
+
+ loadValueDirectFixed(cUnit, rlThis, r0);
+ loadValueDirectFixed(cUnit, rlComp, r1);
+ /* Test objects for NULL */
+ rollback = genNullCheck(cUnit, rlThis.sRegLow, r0, mir->offset, NULL);
+ genNullCheck(cUnit, rlComp.sRegLow, r1, mir->offset, rollback);
+ /*
+ * TUNING: we could check for object pointer equality before invoking
+ * handler. Unclear whether the gain would be worth the added code size
+ * expansion.
+ */
+ genDispatchToHandler(cUnit, TEMPLATE_STRING_COMPARETO);
+ storeValue(cUnit, inlinedTarget(cUnit, mir, false),
+ dvmCompilerGetReturn(cUnit));
+ return true;
+#endif
+}
+
+static bool genInlinedFastIndexOf(CompilationUnit *cUnit, MIR *mir)
+{
+#if defined(USE_GLOBAL_STRING_DEFS)
+ return false;
+#else
+ RegLocation rlThis = dvmCompilerGetSrc(cUnit, mir, 0);
+ RegLocation rlChar = dvmCompilerGetSrc(cUnit, mir, 1);
+
+ loadValueDirectFixed(cUnit, rlThis, r0);
+ loadValueDirectFixed(cUnit, rlChar, r1);
+ RegLocation rlStart = dvmCompilerGetSrc(cUnit, mir, 2);
+ loadValueDirectFixed(cUnit, rlStart, r2);
+ /* Test objects for NULL */
+ genNullCheck(cUnit, rlThis.sRegLow, r0, mir->offset, NULL);
+ genDispatchToHandler(cUnit, TEMPLATE_STRING_INDEXOF);
+ storeValue(cUnit, inlinedTarget(cUnit, mir, false),
+ dvmCompilerGetReturn(cUnit));
+ return true;
+#endif
+}
+
+// Generates an inlined String.isEmpty or String.length.
+static bool genInlinedStringIsEmptyOrLength(CompilationUnit *cUnit, MIR *mir,
+ bool isEmpty)
+{
+ // dst = src.length();
+ RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 0);
+ RegLocation rlDest = inlinedTarget(cUnit, mir, false);
+ rlObj = loadValue(cUnit, rlObj, kCoreReg);
+ RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+ genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset, NULL);
+ loadWordDisp(cUnit, rlObj.lowReg, gDvm.offJavaLangString_count,
+ rlResult.lowReg);
+ if (isEmpty) {
+ // dst = (dst == 0);
+ int tReg = dvmCompilerAllocTemp(cUnit);
+ opRegReg(cUnit, kOpNeg, tReg, rlResult.lowReg);
+ opRegRegReg(cUnit, kOpAdc, rlResult.lowReg, rlResult.lowReg, tReg);
+ }
+ storeValue(cUnit, rlDest, rlResult);
+ return false;
+}
+
+static bool genInlinedStringLength(CompilationUnit *cUnit, MIR *mir)
+{
+ return genInlinedStringIsEmptyOrLength(cUnit, mir, false);
+}
+
+static bool genInlinedStringIsEmpty(CompilationUnit *cUnit, MIR *mir)
+{
+ return genInlinedStringIsEmptyOrLength(cUnit, mir, true);
+}
+
+static bool genInlinedStringCharAt(CompilationUnit *cUnit, MIR *mir)
+{
+ int contents = offsetof(ArrayObject, contents);
+ RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 0);
+ RegLocation rlIdx = dvmCompilerGetSrc(cUnit, mir, 1);
+ RegLocation rlDest = inlinedTarget(cUnit, mir, false);
+ RegLocation rlResult;
+ rlObj = loadValue(cUnit, rlObj, kCoreReg);
+ rlIdx = loadValue(cUnit, rlIdx, kCoreReg);
+ int regMax = dvmCompilerAllocTemp(cUnit);
+ int regOff = dvmCompilerAllocTemp(cUnit);
+ int regPtr = dvmCompilerAllocTemp(cUnit);
+ ArmLIR *pcrLabel = genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg,
+ mir->offset, NULL);
+ loadWordDisp(cUnit, rlObj.lowReg, gDvm.offJavaLangString_count, regMax);
+ loadWordDisp(cUnit, rlObj.lowReg, gDvm.offJavaLangString_offset, regOff);
+ loadWordDisp(cUnit, rlObj.lowReg, gDvm.offJavaLangString_value, regPtr);
+ genBoundsCheck(cUnit, rlIdx.lowReg, regMax, mir->offset, pcrLabel);
+ dvmCompilerFreeTemp(cUnit, regMax);
+ opRegImm(cUnit, kOpAdd, regPtr, contents);
+ opRegReg(cUnit, kOpAdd, regOff, rlIdx.lowReg);
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+ loadBaseIndexed(cUnit, regPtr, regOff, rlResult.lowReg, 1, kUnsignedHalf);
+ storeValue(cUnit, rlDest, rlResult);
+ return false;
+}
+
+static bool genInlinedAbsInt(CompilationUnit *cUnit, MIR *mir)
+{
+ RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+ rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+ RegLocation rlDest = inlinedTarget(cUnit, mir, false);
+ RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+ int signReg = dvmCompilerAllocTemp(cUnit);
+ /*
+ * abs(x) = y<=x>>31, (x+y)^y.
+ * Thumb2's IT block also yields 3 instructions, but imposes
+ * scheduling constraints.
+ */
+ opRegRegImm(cUnit, kOpAsr, signReg, rlSrc.lowReg, 31);
+ opRegRegReg(cUnit, kOpAdd, rlResult.lowReg, rlSrc.lowReg, signReg);
+ opRegReg(cUnit, kOpXor, rlResult.lowReg, signReg);
+ storeValue(cUnit, rlDest, rlResult);
+ return false;
+}
+
+static bool genInlinedAbsLong(CompilationUnit *cUnit, MIR *mir)
+{
+ RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+ RegLocation rlDest = inlinedTargetWide(cUnit, mir, false);
+ rlSrc = loadValueWide(cUnit, rlSrc, kCoreReg);
+ RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+ int signReg = dvmCompilerAllocTemp(cUnit);
+ /*
+ * abs(x) = y<=x>>31, (x+y)^y.
+ * Thumb2 IT block allows slightly shorter sequence,
+ * but introduces a scheduling barrier. Stick with this
+ * mechanism for now.
+ */
+ opRegRegImm(cUnit, kOpAsr, signReg, rlSrc.highReg, 31);
+ opRegRegReg(cUnit, kOpAdd, rlResult.lowReg, rlSrc.lowReg, signReg);
+ opRegRegReg(cUnit, kOpAdc, rlResult.highReg, rlSrc.highReg, signReg);
+ opRegReg(cUnit, kOpXor, rlResult.lowReg, signReg);
+ opRegReg(cUnit, kOpXor, rlResult.highReg, signReg);
+ storeValueWide(cUnit, rlDest, rlResult);
+ return false;
+}
+
+static bool genInlinedIntFloatConversion(CompilationUnit *cUnit, MIR *mir)
+{
+ // Just move from source to destination...
+ RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+ RegLocation rlDest = inlinedTarget(cUnit, mir, false);
+ storeValue(cUnit, rlDest, rlSrc);
+ return false;
+}
+
+static bool genInlinedLongDoubleConversion(CompilationUnit *cUnit, MIR *mir)
+{
+ // Just move from source to destination...
+ RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+ RegLocation rlDest = inlinedTargetWide(cUnit, mir, false);
+ storeValueWide(cUnit, rlDest, rlSrc);
+ return false;
+}
+
+/*
+ * NOTE: Handles both range and non-range versions (arguments
+ * have already been normalized by this point).
+ */
+static bool handleExecuteInline(CompilationUnit *cUnit, MIR *mir)
+{
+ DecodedInstruction *dInsn = &mir->dalvikInsn;
+ switch( mir->dalvikInsn.opCode) {
+ case OP_EXECUTE_INLINE_RANGE:
+ case OP_EXECUTE_INLINE: {
+ unsigned int i;
+ const InlineOperation* inLineTable = dvmGetInlineOpsTable();
+ int offset = offsetof(InterpState, retval);
+ int operation = dInsn->vB;
+ switch (operation) {
+ case INLINE_EMPTYINLINEMETHOD:
+ return false; /* Nop */
+ case INLINE_STRING_LENGTH:
+ return genInlinedStringLength(cUnit, mir);
+ case INLINE_STRING_IS_EMPTY:
+ return genInlinedStringIsEmpty(cUnit, mir);
+ case INLINE_MATH_ABS_INT:
+ return genInlinedAbsInt(cUnit, mir);
+ case INLINE_MATH_ABS_LONG:
+ return genInlinedAbsLong(cUnit, mir);
+ case INLINE_MATH_MIN_INT:
+ return genInlinedMinMaxInt(cUnit, mir, true);
+ case INLINE_MATH_MAX_INT:
+ return genInlinedMinMaxInt(cUnit, mir, false);
+ case INLINE_STRING_CHARAT:
+ return genInlinedStringCharAt(cUnit, mir);
+ case INLINE_MATH_SQRT:
+ if (genInlineSqrt(cUnit, mir))
+ return false;
+ else
+ break; /* Handle with C routine */
+ case INLINE_MATH_ABS_FLOAT:
+ if (genInlinedAbsFloat(cUnit, mir))
+ return false;
+ else
+ break;
+ case INLINE_MATH_ABS_DOUBLE:
+ if (genInlinedAbsDouble(cUnit, mir))
+ return false;
+ else
+ break;
+ case INLINE_STRING_COMPARETO:
+ if (genInlinedCompareTo(cUnit, mir))
+ return false;
+ else
+ break;
+ case INLINE_STRING_FASTINDEXOF_II:
+ if (genInlinedFastIndexOf(cUnit, mir))
+ return false;
+ else
+ break;
+ case INLINE_FLOAT_TO_RAW_INT_BITS:
+ case INLINE_INT_BITS_TO_FLOAT:
+ return genInlinedIntFloatConversion(cUnit, mir);
+ case INLINE_DOUBLE_TO_RAW_LONG_BITS:
+ case INLINE_LONG_BITS_TO_DOUBLE:
+ return genInlinedLongDoubleConversion(cUnit, mir);
+ case INLINE_STRING_EQUALS:
+ case INLINE_MATH_COS:
+ case INLINE_MATH_SIN:
+ case INLINE_FLOAT_TO_INT_BITS:
+ case INLINE_DOUBLE_TO_LONG_BITS:
+ break; /* Handle with C routine */
+ default:
+ dvmCompilerAbort(cUnit);
+ }
+ dvmCompilerFlushAllRegs(cUnit); /* Everything to home location */
+ dvmCompilerClobberCallRegs(cUnit);
+ dvmCompilerClobber(cUnit, r4PC);
+ dvmCompilerClobber(cUnit, r7);
+ opRegRegImm(cUnit, kOpAdd, r4PC, rGLUE, offset);
+ opImm(cUnit, kOpPush, (1<<r4PC) | (1<<r7));
+ LOAD_FUNC_ADDR(cUnit, r4PC, (int)inLineTable[operation].func);
+ genExportPC(cUnit, mir);
+ for (i=0; i < dInsn->vA; i++) {
+ loadValueDirect(cUnit, dvmCompilerGetSrc(cUnit, mir, i), i);
+ }
+ opReg(cUnit, kOpBlx, r4PC);
+ opRegImm(cUnit, kOpAdd, r13, 8);
+ opRegImm(cUnit, kOpCmp, r0, 0); /* NULL? */
+ ArmLIR *branchOver = opCondBranch(cUnit, kArmCondNe);
+ loadConstant(cUnit, r0,
+ (int) (cUnit->method->insns + mir->offset));
+ genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON);
+ ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+ target->defMask = ENCODE_ALL;
+ branchOver->generic.target = (LIR *) target;
+ break;
+ }
+ default:
+ return true;
+ }
+ return false;
+}
+
+static bool handleFmt51l(CompilationUnit *cUnit, MIR *mir)
+{
+ //TUNING: We're using core regs here - not optimal when target is a double
+ RegLocation rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+ RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+ loadConstantNoClobber(cUnit, rlResult.lowReg,
+ mir->dalvikInsn.vB_wide & 0xFFFFFFFFUL);
+ loadConstantNoClobber(cUnit, rlResult.highReg,
+ (mir->dalvikInsn.vB_wide>>32) & 0xFFFFFFFFUL);
+ storeValueWide(cUnit, rlDest, rlResult);
+ return false;
+}
+
+/*
+ * The following are special processing routines that handle transfer of
+ * controls between compiled code and the interpreter. Certain VM states like
+ * Dalvik PC and special-purpose registers are reconstructed here.
+ */
+
+/*
+ * Insert a
+ * b .+4
+ * nop
+ * pair at the beginning of a chaining cell. This serves as the
+ * switch branch that selects between reverting to the interpreter or
+ * not. Once the cell is chained to a translation, the cell will
+ * contain a 32-bit branch. Subsequent chain/unchain operations will
+ * then only alter that first 16-bits - the "b .+4" for unchaining,
+ * and the restoration of the first half of the 32-bit branch for
+ * rechaining.
+ */
+static void insertChainingSwitch(CompilationUnit *cUnit)
+{
+ ArmLIR *branch = newLIR0(cUnit, kThumbBUncond);
+ newLIR2(cUnit, kThumbOrr, r0, r0);
+ ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+ target->defMask = ENCODE_ALL;
+ branch->generic.target = (LIR *) target;
+}
+
+/* Chaining cell for code that may need warmup. */
+static void handleNormalChainingCell(CompilationUnit *cUnit,
+ unsigned int offset)
+{
+ /*
+ * Use raw instruction constructors to guarantee that the generated
+ * instructions fit the predefined cell size.
+ */
+ insertChainingSwitch(cUnit);
+ newLIR3(cUnit, kThumbLdrRRI5, r0, rGLUE,
+ offsetof(InterpState,
+ jitToInterpEntries.dvmJitToInterpNormal) >> 2);
+ newLIR1(cUnit, kThumbBlxR, r0);
+ addWordData(cUnit, (int) (cUnit->method->insns + offset), true);
+}
+
+/*
+ * Chaining cell for instructions that immediately following already translated
+ * code.
+ */
+static void handleHotChainingCell(CompilationUnit *cUnit,
+ unsigned int offset)
+{
+ /*
+ * Use raw instruction constructors to guarantee that the generated
+ * instructions fit the predefined cell size.
+ */
+ insertChainingSwitch(cUnit);
+ newLIR3(cUnit, kThumbLdrRRI5, r0, rGLUE,
+ offsetof(InterpState,
+ jitToInterpEntries.dvmJitToInterpTraceSelect) >> 2);
+ newLIR1(cUnit, kThumbBlxR, r0);
+ addWordData(cUnit, (int) (cUnit->method->insns + offset), true);
+}
+
+#if defined(WITH_SELF_VERIFICATION) || defined(WITH_JIT_TUNING)
+/* Chaining cell for branches that branch back into the same basic block */
+static void handleBackwardBranchChainingCell(CompilationUnit *cUnit,
+ unsigned int offset)
+{
+ /*
+ * Use raw instruction constructors to guarantee that the generated
+ * instructions fit the predefined cell size.
+ */
+ insertChainingSwitch(cUnit);
+#if defined(WITH_SELF_VERIFICATION)
+ newLIR3(cUnit, kThumbLdrRRI5, r0, rGLUE,
+ offsetof(InterpState,
+ jitToInterpEntries.dvmJitToInterpBackwardBranch) >> 2);
+#else
+ newLIR3(cUnit, kThumbLdrRRI5, r0, rGLUE,
+ offsetof(InterpState, jitToInterpEntries.dvmJitToInterpNormal) >> 2);
+#endif
+ newLIR1(cUnit, kThumbBlxR, r0);
+ addWordData(cUnit, (int) (cUnit->method->insns + offset), true);
+}
+
+#endif
+/* Chaining cell for monomorphic method invocations. */
+static void handleInvokeSingletonChainingCell(CompilationUnit *cUnit,
+ const Method *callee)
+{
+ /*
+ * Use raw instruction constructors to guarantee that the generated
+ * instructions fit the predefined cell size.
+ */
+ insertChainingSwitch(cUnit);
+ newLIR3(cUnit, kThumbLdrRRI5, r0, rGLUE,
+ offsetof(InterpState,
+ jitToInterpEntries.dvmJitToInterpTraceSelect) >> 2);
+ newLIR1(cUnit, kThumbBlxR, r0);
+ addWordData(cUnit, (int) (callee->insns), true);
+}
+
+/* Chaining cell for monomorphic method invocations. */
+static void handleInvokePredictedChainingCell(CompilationUnit *cUnit)
+{
+
+ /* Should not be executed in the initial state */
+ addWordData(cUnit, PREDICTED_CHAIN_BX_PAIR_INIT, true);
+ /* To be filled: class */
+ addWordData(cUnit, PREDICTED_CHAIN_CLAZZ_INIT, true);
+ /* To be filled: method */
+ addWordData(cUnit, PREDICTED_CHAIN_METHOD_INIT, true);
+ /*
+ * Rechain count. The initial value of 0 here will trigger chaining upon
+ * the first invocation of this callsite.
+ */
+ addWordData(cUnit, PREDICTED_CHAIN_COUNTER_INIT, true);
+}
+
+/* Load the Dalvik PC into r0 and jump to the specified target */
+static void handlePCReconstruction(CompilationUnit *cUnit,
+ ArmLIR *targetLabel)
+{
+ ArmLIR **pcrLabel =
+ (ArmLIR **) cUnit->pcReconstructionList.elemList;
+ int numElems = cUnit->pcReconstructionList.numUsed;
+ int i;
+ for (i = 0; i < numElems; i++) {
+ dvmCompilerAppendLIR(cUnit, (LIR *) pcrLabel[i]);
+ /* r0 = dalvik PC */
+ loadConstant(cUnit, r0, pcrLabel[i]->operands[0]);
+ genUnconditionalBranch(cUnit, targetLabel);
+ }
+}
+
+static char *extendedMIROpNames[kMirOpLast - kMirOpFirst] = {
+ "kMirOpPhi",
+ "kMirOpNullNRangeUpCheck",
+ "kMirOpNullNRangeDownCheck",
+ "kMirOpLowerBound",
+ "kMirOpPunt",
+ "kMirOpCheckInlinePrediction",
+};
+
+/*
+ * vA = arrayReg;
+ * vB = idxReg;
+ * vC = endConditionReg;
+ * arg[0] = maxC
+ * arg[1] = minC
+ * arg[2] = loopBranchConditionCode
+ */
+static void genHoistedChecksForCountUpLoop(CompilationUnit *cUnit, MIR *mir)
+{
+ /*
+ * NOTE: these synthesized blocks don't have ssa names assigned
+ * for Dalvik registers. However, because they dominate the following
+ * blocks we can simply use the Dalvik name w/ subscript 0 as the
+ * ssa name.
+ */
+ DecodedInstruction *dInsn = &mir->dalvikInsn;
+ const int lenOffset = offsetof(ArrayObject, length);
+ const int maxC = dInsn->arg[0];
+ int regLength;
+ RegLocation rlArray = cUnit->regLocation[mir->dalvikInsn.vA];
+ RegLocation rlIdxEnd = cUnit->regLocation[mir->dalvikInsn.vC];
+
+ /* regArray <- arrayRef */
+ rlArray = loadValue(cUnit, rlArray, kCoreReg);
+ rlIdxEnd = loadValue(cUnit, rlIdxEnd, kCoreReg);
+ genRegImmCheck(cUnit, kArmCondEq, rlArray.lowReg, 0, 0,
+ (ArmLIR *) cUnit->loopAnalysis->branchToPCR);
+
+ /* regLength <- len(arrayRef) */
+ regLength = dvmCompilerAllocTemp(cUnit);
+ loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLength);
+
+ int delta = maxC;
+ /*
+ * If the loop end condition is ">=" instead of ">", then the largest value
+ * of the index is "endCondition - 1".
+ */
+ if (dInsn->arg[2] == OP_IF_GE) {
+ delta--;
+ }
+
+ if (delta) {
+ int tReg = dvmCompilerAllocTemp(cUnit);
+ opRegRegImm(cUnit, kOpAdd, tReg, rlIdxEnd.lowReg, delta);
+ rlIdxEnd.lowReg = tReg;
+ dvmCompilerFreeTemp(cUnit, tReg);
+ }
+ /* Punt if "regIdxEnd < len(Array)" is false */
+ genRegRegCheck(cUnit, kArmCondGe, rlIdxEnd.lowReg, regLength, 0,
+ (ArmLIR *) cUnit->loopAnalysis->branchToPCR);
+}
+
+/*
+ * vA = arrayReg;
+ * vB = idxReg;
+ * vC = endConditionReg;
+ * arg[0] = maxC
+ * arg[1] = minC
+ * arg[2] = loopBranchConditionCode
+ */
+static void genHoistedChecksForCountDownLoop(CompilationUnit *cUnit, MIR *mir)
+{
+ DecodedInstruction *dInsn = &mir->dalvikInsn;
+ const int lenOffset = offsetof(ArrayObject, length);
+ const int regLength = dvmCompilerAllocTemp(cUnit);
+ const int maxC = dInsn->arg[0];
+ RegLocation rlArray = cUnit->regLocation[mir->dalvikInsn.vA];
+ RegLocation rlIdxInit = cUnit->regLocation[mir->dalvikInsn.vB];
+
+ /* regArray <- arrayRef */
+ rlArray = loadValue(cUnit, rlArray, kCoreReg);
+ rlIdxInit = loadValue(cUnit, rlIdxInit, kCoreReg);
+ genRegImmCheck(cUnit, kArmCondEq, rlArray.lowReg, 0, 0,
+ (ArmLIR *) cUnit->loopAnalysis->branchToPCR);
+
+ /* regLength <- len(arrayRef) */
+ loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLength);
+
+ if (maxC) {
+ int tReg = dvmCompilerAllocTemp(cUnit);
+ opRegRegImm(cUnit, kOpAdd, tReg, rlIdxInit.lowReg, maxC);
+ rlIdxInit.lowReg = tReg;
+ dvmCompilerFreeTemp(cUnit, tReg);
+ }
+
+ /* Punt if "regIdxInit < len(Array)" is false */
+ genRegRegCheck(cUnit, kArmCondGe, rlIdxInit.lowReg, regLength, 0,
+ (ArmLIR *) cUnit->loopAnalysis->branchToPCR);
+}
+
+/*
+ * vA = idxReg;
+ * vB = minC;
+ */
+static void genHoistedLowerBoundCheck(CompilationUnit *cUnit, MIR *mir)
+{
+ DecodedInstruction *dInsn = &mir->dalvikInsn;
+ const int minC = dInsn->vB;
+ RegLocation rlIdx = cUnit->regLocation[mir->dalvikInsn.vA];
+
+ /* regIdx <- initial index value */
+ rlIdx = loadValue(cUnit, rlIdx, kCoreReg);
+
+ /* Punt if "regIdxInit + minC >= 0" is false */
+ genRegImmCheck(cUnit, kArmCondLt, rlIdx.lowReg, -minC, 0,
+ (ArmLIR *) cUnit->loopAnalysis->branchToPCR);
+}
+
+/*
+ * vC = this
+ *
+ * A predicted inlining target looks like the following, where instructions
+ * between 0x4858de66 and 0x4858de72 are checking if the predicted class
+ * matches "this", and the verificaion code is generated by this routine.
+ *
+ * (C) means the instruction is inlined from the callee, and (PI) means the
+ * instruction is the predicted inlined invoke, whose corresponding
+ * instructions are still generated to handle the mispredicted case.
+ *
+ * D/dalvikvm( 86): -------- kMirOpCheckInlinePrediction
+ * D/dalvikvm( 86): 0x4858de66 (0002): ldr r0, [r5, #68]
+ * D/dalvikvm( 86): 0x4858de68 (0004): ldr r1, [pc, #140]
+ * D/dalvikvm( 86): 0x4858de6a (0006): cmp r0, #0
+ * D/dalvikvm( 86): 0x4858de6c (0008): beq 0x4858deb2
+ * D/dalvikvm( 86): 0x4858de6e (000a): ldr r2, [r0, #0]
+ * D/dalvikvm( 86): 0x4858de70 (000c): cmp r1, r2
+ * D/dalvikvm( 86): 0x4858de72 (000e): bne 0x4858de7a
+ * D/dalvikvm( 86): -------- dalvik offset: 0x004c @ +iget-object-quick (C)
+ * v4, v17, (#8)
+ * D/dalvikvm( 86): 0x4858de74 (0010): ldr r3, [r0, #8]
+ * D/dalvikvm( 86): 0x4858de76 (0012): str r3, [r5, #16]
+ * D/dalvikvm( 86): -------- dalvik offset: 0x004c @
+ * +invoke-virtual-quick/range (PI) v17..v17
+ * D/dalvikvm( 86): 0x4858de78 (0014): b 0x4858debc
+ * D/dalvikvm( 86): 0x4858de7a (0016): add r4,r5,#68
+ * D/dalvikvm( 86): -------- BARRIER
+ * D/dalvikvm( 86): 0x4858de7e (001a): ldmia r4, <r0>
+ * D/dalvikvm( 86): -------- BARRIER
+ * D/dalvikvm( 86): 0x4858de80 (001c): sub r7,r5,#24
+ * D/dalvikvm( 86): 0x4858de84 (0020): cmp r0, #0
+ * D/dalvikvm( 86): 0x4858de86 (0022): beq 0x4858deb6
+ * D/dalvikvm( 86): -------- BARRIER
+ * D/dalvikvm( 86): 0x4858de88 (0024): stmia r7, <r0>
+ * D/dalvikvm( 86): -------- BARRIER
+ * D/dalvikvm( 86): 0x4858de8a (0026): ldr r4, [pc, #104]
+ * D/dalvikvm( 86): 0x4858de8c (0028): add r1, pc, #28
+ * D/dalvikvm( 86): 0x4858de8e (002a): add r2, pc, #56
+ * D/dalvikvm( 86): 0x4858de90 (002c): blx_1 0x48589198
+ * D/dalvikvm( 86): 0x4858de92 (002e): blx_2 see above
+ * D/dalvikvm( 86): 0x4858de94 (0030): b 0x4858dec8
+ * D/dalvikvm( 86): 0x4858de96 (0032): b 0x4858deb6
+ * D/dalvikvm( 86): 0x4858de98 (0034): ldr r0, [r7, #72]
+ * D/dalvikvm( 86): 0x4858de9a (0036): cmp r1, #0
+ * D/dalvikvm( 86): 0x4858de9c (0038): bgt 0x4858dea4
+ * D/dalvikvm( 86): 0x4858de9e (003a): ldr r7, [r6, #116]
+ * D/dalvikvm( 86): 0x4858dea0 (003c): movs r1, r6
+ * D/dalvikvm( 86): 0x4858dea2 (003e): blx r7
+ * D/dalvikvm( 86): 0x4858dea4 (0040): add r1, pc, #4
+ * D/dalvikvm( 86): 0x4858dea6 (0042): blx_1 0x485890a0
+ * D/dalvikvm( 86): 0x4858dea8 (0044): blx_2 see above
+ * D/dalvikvm( 86): 0x4858deaa (0046): b 0x4858deb6
+ * D/dalvikvm( 86): 0x4858deac (0048): .align4
+ * D/dalvikvm( 86): L0x004f:
+ * D/dalvikvm( 86): -------- dalvik offset: 0x004f @ move-result-object (PI)
+ * v4, (#0), (#0)
+ * D/dalvikvm( 86): 0x4858deac (0048): ldr r4, [r6, #8]
+ * D/dalvikvm( 86): 0x4858deae (004a): str r4, [r5, #16]
+ * D/dalvikvm( 86): 0x4858deb0 (004c): b 0x4858debc
+ * D/dalvikvm( 86): -------- reconstruct dalvik PC : 0x42beefcc @ +0x004c
+ * D/dalvikvm( 86): 0x4858deb2 (004e): ldr r0, [pc, #64]
+ * D/dalvikvm( 86): 0x4858deb4 (0050): b 0x4858deb8
+ * D/dalvikvm( 86): -------- reconstruct dalvik PC : 0x42beefcc @ +0x004c
+ * D/dalvikvm( 86): 0x4858deb6 (0052): ldr r0, [pc, #60]
+ * D/dalvikvm( 86): Exception_Handling:
+ * D/dalvikvm( 86): 0x4858deb8 (0054): ldr r1, [r6, #100]
+ * D/dalvikvm( 86): 0x4858deba (0056): blx r1
+ * D/dalvikvm( 86): 0x4858debc (0058): .align4
+ * D/dalvikvm( 86): -------- chaining cell (hot): 0x0050
+ * D/dalvikvm( 86): 0x4858debc (0058): b 0x4858dec0
+ * D/dalvikvm( 86): 0x4858debe (005a): orrs r0, r0
+ * D/dalvikvm( 86): 0x4858dec0 (005c): ldr r0, [r6, #112]
+ * D/dalvikvm( 86): 0x4858dec2 (005e): blx r0
+ * D/dalvikvm( 86): 0x4858dec4 (0060): data 0xefd4(61396)
+ * D/dalvikvm( 86): 0x4858dec6 (0062): data 0x42be(17086)
+ * D/dalvikvm( 86): 0x4858dec8 (0064): .align4
+ * D/dalvikvm( 86): -------- chaining cell (predicted)
+ * D/dalvikvm( 86): 0x4858dec8 (0064): data 0xe7fe(59390)
+ * D/dalvikvm( 86): 0x4858deca (0066): data 0x0000(0)
+ * D/dalvikvm( 86): 0x4858decc (0068): data 0x0000(0)
+ * D/dalvikvm( 86): 0x4858dece (006a): data 0x0000(0)
+ * :
+ */
+static void genValidationForPredictedInline(CompilationUnit *cUnit, MIR *mir)
+{
+ CallsiteInfo *callsiteInfo = mir->meta.callsiteInfo;
+ RegLocation rlThis = cUnit->regLocation[mir->dalvikInsn.vC];
+
+ rlThis = loadValue(cUnit, rlThis, kCoreReg);
+ int regPredictedClass = dvmCompilerAllocTemp(cUnit);
+ loadConstant(cUnit, regPredictedClass, (int) callsiteInfo->clazz);
+ genNullCheck(cUnit, rlThis.sRegLow, rlThis.lowReg, mir->offset,
+ NULL);/* null object? */
+ int regActualClass = dvmCompilerAllocTemp(cUnit);
+ loadWordDisp(cUnit, rlThis.lowReg, offsetof(Object, clazz), regActualClass);
+ opRegReg(cUnit, kOpCmp, regPredictedClass, regActualClass);
+ /*
+ * Set the misPredBranchOver target so that it will be generated when the
+ * code for the non-optimized invoke is generated.
+ */
+ callsiteInfo->misPredBranchOver = (LIR *) opCondBranch(cUnit, kArmCondNe);
+}
+
+/* Extended MIR instructions like PHI */
+static void handleExtendedMIR(CompilationUnit *cUnit, MIR *mir)
+{
+ int opOffset = mir->dalvikInsn.opCode - kMirOpFirst;
+ char *msg = dvmCompilerNew(strlen(extendedMIROpNames[opOffset]) + 1,
+ false);
+ strcpy(msg, extendedMIROpNames[opOffset]);
+ newLIR1(cUnit, kArmPseudoExtended, (int) msg);
+
+ switch (mir->dalvikInsn.opCode) {
+ case kMirOpPhi: {
+ char *ssaString = dvmCompilerGetSSAString(cUnit, mir->ssaRep);
+ newLIR1(cUnit, kArmPseudoSSARep, (int) ssaString);
+ break;
+ }
+ case kMirOpNullNRangeUpCheck: {
+ genHoistedChecksForCountUpLoop(cUnit, mir);
+ break;
+ }
+ case kMirOpNullNRangeDownCheck: {
+ genHoistedChecksForCountDownLoop(cUnit, mir);
+ break;
+ }
+ case kMirOpLowerBound: {
+ genHoistedLowerBoundCheck(cUnit, mir);
+ break;
+ }
+ case kMirOpPunt: {
+ genUnconditionalBranch(cUnit,
+ (ArmLIR *) cUnit->loopAnalysis->branchToPCR);
+ break;
+ }
+ case kMirOpCheckInlinePrediction: {
+ genValidationForPredictedInline(cUnit, mir);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+/*
+ * Create a PC-reconstruction cell for the starting offset of this trace.
+ * Since the PCR cell is placed near the end of the compiled code which is
+ * usually out of range for a conditional branch, we put two branches (one
+ * branch over to the loop body and one layover branch to the actual PCR) at the
+ * end of the entry block.
+ */
+static void setupLoopEntryBlock(CompilationUnit *cUnit, BasicBlock *entry,
+ ArmLIR *bodyLabel)
+{
+ /* Set up the place holder to reconstruct this Dalvik PC */
+ ArmLIR *pcrLabel = dvmCompilerNew(sizeof(ArmLIR), true);
+ pcrLabel->opCode = kArmPseudoPCReconstructionCell;
+ pcrLabel->operands[0] =
+ (int) (cUnit->method->insns + entry->startOffset);
+ pcrLabel->operands[1] = entry->startOffset;
+ /* Insert the place holder to the growable list */
+ dvmInsertGrowableList(&cUnit->pcReconstructionList, pcrLabel);
+
+ /*
+ * Next, create two branches - one branch over to the loop body and the
+ * other branch to the PCR cell to punt.
+ */
+ ArmLIR *branchToBody = dvmCompilerNew(sizeof(ArmLIR), true);
+ branchToBody->opCode = kThumbBUncond;
+ branchToBody->generic.target = (LIR *) bodyLabel;
+ setupResourceMasks(branchToBody);
+ cUnit->loopAnalysis->branchToBody = (LIR *) branchToBody;
+
+ ArmLIR *branchToPCR = dvmCompilerNew(sizeof(ArmLIR), true);
+ branchToPCR->opCode = kThumbBUncond;
+ branchToPCR->generic.target = (LIR *) pcrLabel;
+ setupResourceMasks(branchToPCR);
+ cUnit->loopAnalysis->branchToPCR = (LIR *) branchToPCR;
+}
+
+#if defined(WITH_SELF_VERIFICATION)
+static bool selfVerificationPuntOps(MIR *mir)
+{
+ DecodedInstruction *decInsn = &mir->dalvikInsn;
+ OpCode op = decInsn->opCode;
+
+ /*
+ * All opcodes that can throw exceptions and use the
+ * TEMPLATE_THROW_EXCEPTION_COMMON template should be excluded in the trace
+ * under self-verification mode.
+ */
+ return (op == OP_MONITOR_ENTER || op == OP_MONITOR_EXIT ||
+ op == OP_NEW_INSTANCE || op == OP_NEW_ARRAY ||
+ op == OP_CHECK_CAST || op == OP_MOVE_EXCEPTION ||
+ op == OP_FILL_ARRAY_DATA || op == OP_EXECUTE_INLINE ||
+ op == OP_EXECUTE_INLINE_RANGE);
+}
+#endif
+
+void dvmCompilerMIR2LIR(CompilationUnit *cUnit)
+{
+ /* Used to hold the labels of each block */
+ ArmLIR *labelList =
+ dvmCompilerNew(sizeof(ArmLIR) * cUnit->numBlocks, true);
+ GrowableList chainingListByType[kChainingCellGap];
+ int i;
+
+ /*
+ * Initialize various types chaining lists.
+ */
+ for (i = 0; i < kChainingCellGap; i++) {
+ dvmInitGrowableList(&chainingListByType[i], 2);
+ }
+
+ BasicBlock **blockList = cUnit->blockList;
+
+ if (cUnit->executionCount) {
+ /*
+ * Reserve 6 bytes at the beginning of the trace
+ * +----------------------------+
+ * | execution count (4 bytes) |
+ * +----------------------------+
+ * | chain cell offset (2 bytes)|
+ * +----------------------------+
+ * ...and then code to increment the execution
+ * count:
+ * mov r0, pc @ move adr of "mov r0,pc" + 4 to r0
+ * sub r0, #10 @ back up to addr of executionCount
+ * ldr r1, [r0]
+ * add r1, #1
+ * str r1, [r0]
+ */
+ newLIR1(cUnit, kArm16BitData, 0);
+ newLIR1(cUnit, kArm16BitData, 0);
+ cUnit->chainCellOffsetLIR =
+ (LIR *) newLIR1(cUnit, kArm16BitData, CHAIN_CELL_OFFSET_TAG);
+ cUnit->headerSize = 6;
+ /* Thumb instruction used directly here to ensure correct size */
+ newLIR2(cUnit, kThumbMovRR_H2L, r0, rpc);
+ newLIR2(cUnit, kThumbSubRI8, r0, 10);
+ newLIR3(cUnit, kThumbLdrRRI5, r1, r0, 0);
+ newLIR2(cUnit, kThumbAddRI8, r1, 1);
+ newLIR3(cUnit, kThumbStrRRI5, r1, r0, 0);
+ } else {
+ /* Just reserve 2 bytes for the chain cell offset */
+ cUnit->chainCellOffsetLIR =
+ (LIR *) newLIR1(cUnit, kArm16BitData, CHAIN_CELL_OFFSET_TAG);
+ cUnit->headerSize = 2;
+ }
+
+ /* Handle the content in each basic block */
+ for (i = 0; i < cUnit->numBlocks; i++) {
+ blockList[i]->visited = true;
+ MIR *mir;
+
+ labelList[i].operands[0] = blockList[i]->startOffset;
+
+ if (blockList[i]->blockType >= kChainingCellGap) {
+ if (blockList[i]->isFallThroughFromInvoke == true) {
+ /* Align this block first since it is a return chaining cell */
+ newLIR0(cUnit, kArmPseudoPseudoAlign4);
+ }
+ /*
+ * Append the label pseudo LIR first. Chaining cells will be handled
+ * separately afterwards.
+ */
+ dvmCompilerAppendLIR(cUnit, (LIR *) &labelList[i]);
+ }
+
+ if (blockList[i]->blockType == kTraceEntryBlock) {
+ labelList[i].opCode = kArmPseudoEntryBlock;
+ if (blockList[i]->firstMIRInsn == NULL) {
+ continue;
+ } else {
+ setupLoopEntryBlock(cUnit, blockList[i],
+ &labelList[blockList[i]->fallThrough->id]);
+ }
+ } else if (blockList[i]->blockType == kTraceExitBlock) {
+ labelList[i].opCode = kArmPseudoExitBlock;
+ goto gen_fallthrough;
+ } else if (blockList[i]->blockType == kDalvikByteCode) {
+ labelList[i].opCode = kArmPseudoNormalBlockLabel;
+ /* Reset the register state */
+ dvmCompilerResetRegPool(cUnit);
+ dvmCompilerClobberAllRegs(cUnit);
+ dvmCompilerResetNullCheck(cUnit);
+ } else {
+ switch (blockList[i]->blockType) {
+ case kChainingCellNormal:
+ labelList[i].opCode = kArmPseudoChainingCellNormal;
+ /* handle the codegen later */
+ dvmInsertGrowableList(
+ &chainingListByType[kChainingCellNormal], (void *) i);
+ break;
+ case kChainingCellInvokeSingleton:
+ labelList[i].opCode =
+ kArmPseudoChainingCellInvokeSingleton;
+ labelList[i].operands[0] =
+ (int) blockList[i]->containingMethod;
+ /* handle the codegen later */
+ dvmInsertGrowableList(
+ &chainingListByType[kChainingCellInvokeSingleton],
+ (void *) i);
+ break;
+ case kChainingCellInvokePredicted:
+ labelList[i].opCode =
+ kArmPseudoChainingCellInvokePredicted;
+ /* handle the codegen later */
+ dvmInsertGrowableList(
+ &chainingListByType[kChainingCellInvokePredicted],
+ (void *) i);
+ break;
+ case kChainingCellHot:
+ labelList[i].opCode =
+ kArmPseudoChainingCellHot;
+ /* handle the codegen later */
+ dvmInsertGrowableList(
+ &chainingListByType[kChainingCellHot],
+ (void *) i);
+ break;
+ case kPCReconstruction:
+ /* Make sure exception handling block is next */
+ labelList[i].opCode =
+ kArmPseudoPCReconstructionBlockLabel;
+ assert (i == cUnit->numBlocks - 2);
+ handlePCReconstruction(cUnit, &labelList[i+1]);
+ break;
+ case kExceptionHandling:
+ labelList[i].opCode = kArmPseudoEHBlockLabel;
+ if (cUnit->pcReconstructionList.numUsed) {
+ loadWordDisp(cUnit, rGLUE, offsetof(InterpState,
+ jitToInterpEntries.dvmJitToInterpPunt),
+ r1);
+ opReg(cUnit, kOpBlx, r1);
+ }
+ break;
+#if defined(WITH_SELF_VERIFICATION) || defined(WITH_JIT_TUNING)
+ case kChainingCellBackwardBranch:
+ labelList[i].opCode =
+ kArmPseudoChainingCellBackwardBranch;
+ /* handle the codegen later */
+ dvmInsertGrowableList(
+ &chainingListByType[kChainingCellBackwardBranch],
+ (void *) i);
+ break;
+#endif
+ default:
+ break;
+ }
+ continue;
+ }
+
+ ArmLIR *headLIR = NULL;
+
+ for (mir = blockList[i]->firstMIRInsn; mir; mir = mir->next) {
+
+ dvmCompilerResetRegPool(cUnit);
+ if (gDvmJit.disableOpt & (1 << kTrackLiveTemps)) {
+ dvmCompilerClobberAllRegs(cUnit);
+ }
+
+ if (gDvmJit.disableOpt & (1 << kSuppressLoads)) {
+ dvmCompilerResetDefTracking(cUnit);
+ }
+
+ if (mir->dalvikInsn.opCode >= kMirOpFirst) {
+ handleExtendedMIR(cUnit, mir);
+ continue;
+ }
+
+
+ OpCode dalvikOpCode = mir->dalvikInsn.opCode;
+ InstructionFormat dalvikFormat =
+ dexGetInstrFormat(gDvm.instrFormat, dalvikOpCode);
+ char *note;
+ if (mir->OptimizationFlags & MIR_INLINED) {
+ note = " (I)";
+ } else if (mir->OptimizationFlags & MIR_INLINED_PRED) {
+ note = " (PI)";
+ } else if (mir->OptimizationFlags & MIR_CALLEE) {
+ note = " (C)";
+ } else {
+ note = NULL;
+ }
+
+ ArmLIR *boundaryLIR =
+ newLIR2(cUnit, kArmPseudoDalvikByteCodeBoundary,
+ mir->offset,
+ (int) dvmCompilerGetDalvikDisassembly(&mir->dalvikInsn,
+ note));
+ if (mir->ssaRep) {
+ char *ssaString = dvmCompilerGetSSAString(cUnit, mir->ssaRep);
+ newLIR1(cUnit, kArmPseudoSSARep, (int) ssaString);
+ }
+
+ /* Remember the first LIR for this block */
+ if (headLIR == NULL) {
+ headLIR = boundaryLIR;
+ /* Set the first boundaryLIR as a scheduling barrier */
+ headLIR->defMask = ENCODE_ALL;
+ }
+
+ bool notHandled;
+ /*
+ * Debugging: screen the opcode first to see if it is in the
+ * do[-not]-compile list
+ */
+ bool singleStepMe = SINGLE_STEP_OP(dalvikOpCode);
+#if defined(WITH_SELF_VERIFICATION)
+ if (singleStepMe == false) {
+ singleStepMe = selfVerificationPuntOps(mir);
+ }
+#endif
+ if (singleStepMe || cUnit->allSingleStep) {
+ notHandled = false;
+ genInterpSingleStep(cUnit, mir);
+ } else {
+ opcodeCoverage[dalvikOpCode]++;
+ switch (dalvikFormat) {
+ case kFmt10t:
+ case kFmt20t:
+ case kFmt30t:
+ notHandled = handleFmt10t_Fmt20t_Fmt30t(cUnit,
+ mir, blockList[i], labelList);
+ break;
+ case kFmt10x:
+ notHandled = handleFmt10x(cUnit, mir);
+ break;
+ case kFmt11n:
+ case kFmt31i:
+ notHandled = handleFmt11n_Fmt31i(cUnit, mir);
+ break;
+ case kFmt11x:
+ notHandled = handleFmt11x(cUnit, mir);
+ break;
+ case kFmt12x:
+ notHandled = handleFmt12x(cUnit, mir);
+ break;
+ case kFmt20bc:
+ notHandled = handleFmt20bc(cUnit, mir);
+ break;
+ case kFmt21c:
+ case kFmt31c:
+ notHandled = handleFmt21c_Fmt31c(cUnit, mir);
+ break;
+ case kFmt21h:
+ notHandled = handleFmt21h(cUnit, mir);
+ break;
+ case kFmt21s:
+ notHandled = handleFmt21s(cUnit, mir);
+ break;
+ case kFmt21t:
+ notHandled = handleFmt21t(cUnit, mir, blockList[i],
+ labelList);
+ break;
+ case kFmt22b:
+ case kFmt22s:
+ notHandled = handleFmt22b_Fmt22s(cUnit, mir);
+ break;
+ case kFmt22c:
+ notHandled = handleFmt22c(cUnit, mir);
+ break;
+ case kFmt22cs:
+ notHandled = handleFmt22cs(cUnit, mir);
+ break;
+ case kFmt22t:
+ notHandled = handleFmt22t(cUnit, mir, blockList[i],
+ labelList);
+ break;
+ case kFmt22x:
+ case kFmt32x:
+ notHandled = handleFmt22x_Fmt32x(cUnit, mir);
+ break;
+ case kFmt23x:
+ notHandled = handleFmt23x(cUnit, mir);
+ break;
+ case kFmt31t:
+ notHandled = handleFmt31t(cUnit, mir);
+ break;
+ case kFmt3rc:
+ case kFmt35c:
+ notHandled = handleFmt35c_3rc(cUnit, mir, blockList[i],
+ labelList);
+ break;
+ case kFmt3rms:
+ case kFmt35ms:
+ notHandled = handleFmt35ms_3rms(cUnit, mir,blockList[i],
+ labelList);
+ break;
+ case kFmt3inline:
+ case kFmt3rinline:
+ notHandled = handleExecuteInline(cUnit, mir);
+ break;
+ case kFmt51l:
+ notHandled = handleFmt51l(cUnit, mir);
+ break;
+ default:
+ notHandled = true;
+ break;
+ }
+ }
+ if (notHandled) {
+ LOGE("%#06x: Opcode 0x%x (%s) / Fmt %d not handled\n",
+ mir->offset,
+ dalvikOpCode, dexGetOpcodeName(dalvikOpCode),
+ dalvikFormat);
+ dvmCompilerAbort(cUnit);
+ break;
+ }
+ }
+
+ if (blockList[i]->blockType == kTraceEntryBlock) {
+ dvmCompilerAppendLIR(cUnit,
+ (LIR *) cUnit->loopAnalysis->branchToBody);
+ dvmCompilerAppendLIR(cUnit,
+ (LIR *) cUnit->loopAnalysis->branchToPCR);
+ }
+
+ if (headLIR) {
+ /*
+ * Eliminate redundant loads/stores and delay stores into later
+ * slots
+ */
+ dvmCompilerApplyLocalOptimizations(cUnit, (LIR *) headLIR,
+ cUnit->lastLIRInsn);
+ }
+
+gen_fallthrough:
+ /*
+ * Check if the block is terminated due to trace length constraint -
+ * insert an unconditional branch to the chaining cell.
+ */
+ if (blockList[i]->needFallThroughBranch) {
+ genUnconditionalBranch(cUnit,
+ &labelList[blockList[i]->fallThrough->id]);
+ }
+
+ }
+
+ /* Handle the chaining cells in predefined order */
+ for (i = 0; i < kChainingCellGap; i++) {
+ size_t j;
+ int *blockIdList = (int *) chainingListByType[i].elemList;
+
+ cUnit->numChainingCells[i] = chainingListByType[i].numUsed;
+
+ /* No chaining cells of this type */
+ if (cUnit->numChainingCells[i] == 0)
+ continue;
+
+ /* Record the first LIR for a new type of chaining cell */
+ cUnit->firstChainingLIR[i] = (LIR *) &labelList[blockIdList[0]];
+
+ for (j = 0; j < chainingListByType[i].numUsed; j++) {
+ int blockId = blockIdList[j];
+
+ /* Align this chaining cell first */
+ newLIR0(cUnit, kArmPseudoPseudoAlign4);
+
+ /* Insert the pseudo chaining instruction */
+ dvmCompilerAppendLIR(cUnit, (LIR *) &labelList[blockId]);
+
+
+ switch (blockList[blockId]->blockType) {
+ case kChainingCellNormal:
+ handleNormalChainingCell(cUnit,
+ blockList[blockId]->startOffset);
+ break;
+ case kChainingCellInvokeSingleton:
+ handleInvokeSingletonChainingCell(cUnit,
+ blockList[blockId]->containingMethod);
+ break;
+ case kChainingCellInvokePredicted:
+ handleInvokePredictedChainingCell(cUnit);
+ break;
+ case kChainingCellHot:
+ handleHotChainingCell(cUnit,
+ blockList[blockId]->startOffset);
+ break;
+#if defined(WITH_SELF_VERIFICATION) || defined(WITH_JIT_TUNING)
+ case kChainingCellBackwardBranch:
+ handleBackwardBranchChainingCell(cUnit,
+ blockList[blockId]->startOffset);
+ break;
+#endif
+ default:
+ LOGE("Bad blocktype %d", blockList[blockId]->blockType);
+ dvmCompilerAbort(cUnit);
+ }
+ }
+ }
+
+ /* Mark the bottom of chaining cells */
+ cUnit->chainingCellBottom = (LIR *) newLIR0(cUnit, kArmChainingCellBottom);
+
+ /*
+ * Generate the branch to the dvmJitToInterpNoChain entry point at the end
+ * of all chaining cells for the overflow cases.
+ */
+ if (cUnit->switchOverflowPad) {
+ loadConstant(cUnit, r0, (int) cUnit->switchOverflowPad);
+ loadWordDisp(cUnit, rGLUE, offsetof(InterpState,
+ jitToInterpEntries.dvmJitToInterpNoChain), r2);
+ opRegReg(cUnit, kOpAdd, r1, r1);
+ opRegRegReg(cUnit, kOpAdd, r4PC, r0, r1);
+#if defined(WITH_JIT_TUNING)
+ loadConstant(cUnit, r0, kSwitchOverflow);
+#endif
+ opReg(cUnit, kOpBlx, r2);
+ }
+
+ dvmCompilerApplyGlobalOptimizations(cUnit);
+
+#if defined(WITH_SELF_VERIFICATION)
+ selfVerificationBranchInsertPass(cUnit);
+#endif
+}
+
+/* Accept the work and start compiling */
+bool dvmCompilerDoWork(CompilerWorkOrder *work)
+{
+ bool res;
+
+ if (gDvmJit.codeCacheFull) {
+ return false;
+ }
+
+ switch (work->kind) {
+ case kWorkOrderTrace:
+ /* Start compilation with maximally allowed trace length */
+ res = dvmCompileTrace(work->info, JIT_MAX_TRACE_LEN, &work->result,
+ work->bailPtr, 0 /* no hints */);
+ break;
+ case kWorkOrderTraceDebug: {
+ bool oldPrintMe = gDvmJit.printMe;
+ gDvmJit.printMe = true;
+ /* Start compilation with maximally allowed trace length */
+ res = dvmCompileTrace(work->info, JIT_MAX_TRACE_LEN, &work->result,
+ work->bailPtr, 0 /* no hints */);
+ gDvmJit.printMe = oldPrintMe;
+ break;
+ }
+ default:
+ res = false;
+ LOGE("Jit: unknown work order type");
+ assert(0); // Bail if debug build, discard otherwise
+ }
+ return res;
+}
+
+/* Architectural-specific debugging helpers go here */
+void dvmCompilerArchDump(void)
+{
+ /* Print compiled opcode in this VM instance */
+ int i, start, streak;
+ char buf[1024];
+
+ streak = i = 0;
+ buf[0] = 0;
+ while (opcodeCoverage[i] == 0 && i < 256) {
+ i++;
+ }
+ if (i == 256) {
+ return;
+ }
+ for (start = i++, streak = 1; i < 256; i++) {
+ if (opcodeCoverage[i]) {
+ streak++;
+ } else {
+ if (streak == 1) {
+ sprintf(buf+strlen(buf), "%x,", start);
+ } else {
+ sprintf(buf+strlen(buf), "%x-%x,", start, start + streak - 1);
+ }
+ streak = 0;
+ while (opcodeCoverage[i] == 0 && i < 256) {
+ i++;
+ }
+ if (i < 256) {
+ streak = 1;
+ start = i;
+ }
+ }
+ }
+ if (streak) {
+ if (streak == 1) {
+ sprintf(buf+strlen(buf), "%x", start);
+ } else {
+ sprintf(buf+strlen(buf), "%x-%x", start, start + streak - 1);
+ }
+ }
+ if (strlen(buf)) {
+ LOGD("dalvik.vm.jit.op = %s", buf);
+ }
+}
+
+/* Common initialization routine for an architecture family */
+bool dvmCompilerArchInit()
+{
+ int i;
+
+ for (i = 0; i < kArmLast; i++) {
+ if (EncodingMap[i].opCode != i) {
+ LOGE("Encoding order for %s is wrong: expecting %d, seeing %d",
+ EncodingMap[i].name, i, EncodingMap[i].opCode);
+ dvmAbort(); // OK to dvmAbort - build error
+ }
+ }
+
+ return dvmCompilerArchVariantInit();
+}
+
+void *dvmCompilerGetInterpretTemplate()
+{
+ return (void*) ((int)gDvmJit.codeCache +
+ templateEntryOffsets[TEMPLATE_INTERPRET]);
+}
+
+/* Needed by the Assembler */
+void dvmCompilerSetupResourceMasks(ArmLIR *lir)
+{
+ setupResourceMasks(lir);
+}
+
+/* Needed by the ld/st optmizatons */
+ArmLIR* dvmCompilerRegCopyNoInsert(CompilationUnit *cUnit, int rDest, int rSrc)
+{
+ return genRegCopyNoInsert(cUnit, rDest, rSrc);
+}
+
+/* Needed by the register allocator */
+ArmLIR* dvmCompilerRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
+{
+ return genRegCopy(cUnit, rDest, rSrc);
+}
+
+/* Needed by the register allocator */
+void dvmCompilerRegCopyWide(CompilationUnit *cUnit, int destLo, int destHi,
+ int srcLo, int srcHi)
+{
+ genRegCopyWide(cUnit, destLo, destHi, srcLo, srcHi);
+}
+
+void dvmCompilerFlushRegImpl(CompilationUnit *cUnit, int rBase,
+ int displacement, int rSrc, OpSize size)
+{
+ storeBaseDisp(cUnit, rBase, displacement, rSrc, size);
+}
+
+void dvmCompilerFlushRegWideImpl(CompilationUnit *cUnit, int rBase,
+ int displacement, int rSrcLo, int rSrcHi)
+{
+ storeBaseDispWide(cUnit, rBase, displacement, rSrcLo, rSrcHi);
+}
diff --git a/vm/compiler/codegen/arm/CodegenFactory.c b/vm/compiler/codegen/arm/CodegenFactory.c
new file mode 100644
index 0000000..157bd1f
--- /dev/null
+++ b/vm/compiler/codegen/arm/CodegenFactory.c
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file contains codegen and support common to all supported
+ * ARM variants. It is included by:
+ *
+ * Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ * which combines this common code with specific support found in the
+ * applicable directory below this one.
+ */
+
+
+/* Load a word at base + displacement. Displacement must be word multiple */
+static ArmLIR *loadWordDisp(CompilationUnit *cUnit, int rBase, int displacement,
+ int rDest)
+{
+ return loadBaseDisp(cUnit, NULL, rBase, displacement, rDest, kWord,
+ INVALID_SREG);
+}
+
+static ArmLIR *storeWordDisp(CompilationUnit *cUnit, int rBase,
+ int displacement, int rSrc)
+{
+ return storeBaseDisp(cUnit, rBase, displacement, rSrc, kWord);
+}
+
+/*
+ * Load a Dalvik register into a physical register. Take care when
+ * using this routine, as it doesn't perform any bookkeeping regarding
+ * register liveness. That is the responsibility of the caller.
+ */
+static void loadValueDirect(CompilationUnit *cUnit, RegLocation rlSrc,
+ int reg1)
+{
+ rlSrc = dvmCompilerUpdateLoc(cUnit, rlSrc);
+ if (rlSrc.location == kLocPhysReg) {
+ genRegCopy(cUnit, reg1, rlSrc.lowReg);
+ } else if (rlSrc.location == kLocRetval) {
+ loadWordDisp(cUnit, rGLUE, offsetof(InterpState, retval), reg1);
+ } else {
+ assert(rlSrc.location == kLocDalvikFrame);
+ loadWordDisp(cUnit, rFP, dvmCompilerS2VReg(cUnit, rlSrc.sRegLow) << 2,
+ reg1);
+ }
+}
+
+/*
+ * Similar to loadValueDirect, but clobbers and allocates the target
+ * register. Should be used when loading to a fixed register (for example,
+ * loading arguments to an out of line call.
+ */
+static void loadValueDirectFixed(CompilationUnit *cUnit, RegLocation rlSrc,
+ int reg1)
+{
+ dvmCompilerClobber(cUnit, reg1);
+ dvmCompilerMarkInUse(cUnit, reg1);
+ loadValueDirect(cUnit, rlSrc, reg1);
+}
+
+/*
+ * Load a Dalvik register pair into a physical register[s]. Take care when
+ * using this routine, as it doesn't perform any bookkeeping regarding
+ * register liveness. That is the responsibility of the caller.
+ */
+static void loadValueDirectWide(CompilationUnit *cUnit, RegLocation rlSrc,
+ int regLo, int regHi)
+{
+ rlSrc = dvmCompilerUpdateLocWide(cUnit, rlSrc);
+ if (rlSrc.location == kLocPhysReg) {
+ genRegCopyWide(cUnit, regLo, regHi, rlSrc.lowReg, rlSrc.highReg);
+ } else if (rlSrc.location == kLocRetval) {
+ loadBaseDispWide(cUnit, NULL, rGLUE, offsetof(InterpState, retval),
+ regLo, regHi, INVALID_SREG);
+ } else {
+ assert(rlSrc.location == kLocDalvikFrame);
+ loadBaseDispWide(cUnit, NULL, rFP,
+ dvmCompilerS2VReg(cUnit, rlSrc.sRegLow) << 2,
+ regLo, regHi, INVALID_SREG);
+ }
+}
+
+/*
+ * Similar to loadValueDirect, but clobbers and allocates the target
+ * registers. Should be used when loading to a fixed registers (for example,
+ * loading arguments to an out of line call.
+ */
+static void loadValueDirectWideFixed(CompilationUnit *cUnit, RegLocation rlSrc,
+ int regLo, int regHi)
+{
+ dvmCompilerClobber(cUnit, regLo);
+ dvmCompilerClobber(cUnit, regHi);
+ dvmCompilerMarkInUse(cUnit, regLo);
+ dvmCompilerMarkInUse(cUnit, regHi);
+ loadValueDirectWide(cUnit, rlSrc, regLo, regHi);
+}
+
+static RegLocation loadValue(CompilationUnit *cUnit, RegLocation rlSrc,
+ RegisterClass opKind)
+{
+ rlSrc = dvmCompilerEvalLoc(cUnit, rlSrc, opKind, false);
+ if (rlSrc.location == kLocDalvikFrame) {
+ loadValueDirect(cUnit, rlSrc, rlSrc.lowReg);
+ rlSrc.location = kLocPhysReg;
+ dvmCompilerMarkLive(cUnit, rlSrc.lowReg, rlSrc.sRegLow);
+ } else if (rlSrc.location == kLocRetval) {
+ loadWordDisp(cUnit, rGLUE, offsetof(InterpState, retval), rlSrc.lowReg);
+ rlSrc.location = kLocPhysReg;
+ dvmCompilerClobber(cUnit, rlSrc.lowReg);
+ }
+ return rlSrc;
+}
+
+static void storeValue(CompilationUnit *cUnit, RegLocation rlDest,
+ RegLocation rlSrc)
+{
+ LIR *defStart;
+ LIR *defEnd;
+ assert(!rlDest.wide);
+ assert(!rlSrc.wide);
+ dvmCompilerKillNullCheckedLoc(cUnit, rlDest);
+ rlSrc = dvmCompilerUpdateLoc(cUnit, rlSrc);
+ rlDest = dvmCompilerUpdateLoc(cUnit, rlDest);
+ if (rlSrc.location == kLocPhysReg) {
+ if (dvmCompilerIsLive(cUnit, rlSrc.lowReg) ||
+ (rlDest.location == kLocPhysReg)) {
+ // Src is live or Dest has assigned reg.
+ rlDest = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, false);
+ genRegCopy(cUnit, rlDest.lowReg, rlSrc.lowReg);
+ } else {
+ // Just re-assign the registers. Dest gets Src's regs
+ rlDest.lowReg = rlSrc.lowReg;
+ dvmCompilerClobber(cUnit, rlSrc.lowReg);
+ }
+ } else {
+ // Load Src either into promoted Dest or temps allocated for Dest
+ rlDest = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, false);
+ loadValueDirect(cUnit, rlSrc, rlDest.lowReg);
+ }
+
+ // Dest is now live and dirty (until/if we flush it to home location)
+ dvmCompilerMarkLive(cUnit, rlDest.lowReg, rlDest.sRegLow);
+ dvmCompilerMarkDirty(cUnit, rlDest.lowReg);
+
+
+ if (rlDest.location == kLocRetval) {
+ storeBaseDisp(cUnit, rGLUE, offsetof(InterpState, retval),
+ rlDest.lowReg, kWord);
+ dvmCompilerClobber(cUnit, rlDest.lowReg);
+ } else {
+ dvmCompilerResetDefLoc(cUnit, rlDest);
+ if (dvmCompilerLiveOut(cUnit, rlDest.sRegLow)) {
+ defStart = (LIR *)cUnit->lastLIRInsn;
+ int vReg = dvmCompilerS2VReg(cUnit, rlDest.sRegLow);
+ storeBaseDisp(cUnit, rFP, vReg << 2, rlDest.lowReg, kWord);
+ dvmCompilerMarkClean(cUnit, rlDest.lowReg);
+ defEnd = (LIR *)cUnit->lastLIRInsn;
+ dvmCompilerMarkDef(cUnit, rlDest, defStart, defEnd);
+ }
+ }
+}
+
+static RegLocation loadValueWide(CompilationUnit *cUnit, RegLocation rlSrc,
+ RegisterClass opKind)
+{
+ assert(rlSrc.wide);
+ rlSrc = dvmCompilerEvalLoc(cUnit, rlSrc, opKind, false);
+ if (rlSrc.location == kLocDalvikFrame) {
+ loadValueDirectWide(cUnit, rlSrc, rlSrc.lowReg, rlSrc.highReg);
+ rlSrc.location = kLocPhysReg;
+ dvmCompilerMarkLive(cUnit, rlSrc.lowReg, rlSrc.sRegLow);
+ dvmCompilerMarkLive(cUnit, rlSrc.highReg,
+ dvmCompilerSRegHi(rlSrc.sRegLow));
+ } else if (rlSrc.location == kLocRetval) {
+ loadBaseDispWide(cUnit, NULL, rGLUE, offsetof(InterpState, retval),
+ rlSrc.lowReg, rlSrc.highReg, INVALID_SREG);
+ rlSrc.location = kLocPhysReg;
+ dvmCompilerClobber(cUnit, rlSrc.lowReg);
+ dvmCompilerClobber(cUnit, rlSrc.highReg);
+ }
+ return rlSrc;
+}
+
+static void storeValueWide(CompilationUnit *cUnit, RegLocation rlDest,
+ RegLocation rlSrc)
+{
+ LIR *defStart;
+ LIR *defEnd;
+ assert(FPREG(rlSrc.lowReg)==FPREG(rlSrc.highReg));
+ assert(rlDest.wide);
+ assert(rlSrc.wide);
+ dvmCompilerKillNullCheckedLoc(cUnit, rlDest);
+ if (rlSrc.location == kLocPhysReg) {
+ if (dvmCompilerIsLive(cUnit, rlSrc.lowReg) ||
+ dvmCompilerIsLive(cUnit, rlSrc.highReg) ||
+ (rlDest.location == kLocPhysReg)) {
+ // Src is live or Dest has assigned reg.
+ rlDest = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, false);
+ genRegCopyWide(cUnit, rlDest.lowReg, rlDest.highReg,
+ rlSrc.lowReg, rlSrc.highReg);
+ } else {
+ // Just re-assign the registers. Dest gets Src's regs
+ rlDest.lowReg = rlSrc.lowReg;
+ rlDest.highReg = rlSrc.highReg;
+ dvmCompilerClobber(cUnit, rlSrc.lowReg);
+ dvmCompilerClobber(cUnit, rlSrc.highReg);
+ }
+ } else {
+ // Load Src either into promoted Dest or temps allocated for Dest
+ rlDest = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, false);
+ loadValueDirectWide(cUnit, rlSrc, rlDest.lowReg,
+ rlDest.highReg);
+ }
+
+ // Dest is now live and dirty (until/if we flush it to home location)
+ dvmCompilerMarkLive(cUnit, rlDest.lowReg, rlDest.sRegLow);
+ dvmCompilerMarkLive(cUnit, rlDest.highReg,
+ dvmCompilerSRegHi(rlDest.sRegLow));
+ dvmCompilerMarkDirty(cUnit, rlDest.lowReg);
+ dvmCompilerMarkDirty(cUnit, rlDest.highReg);
+ dvmCompilerMarkPair(cUnit, rlDest.lowReg, rlDest.highReg);
+
+
+ if (rlDest.location == kLocRetval) {
+ storeBaseDispWide(cUnit, rGLUE, offsetof(InterpState, retval),
+ rlDest.lowReg, rlDest.highReg);
+ dvmCompilerClobber(cUnit, rlDest.lowReg);
+ dvmCompilerClobber(cUnit, rlDest.highReg);
+ } else {
+ dvmCompilerResetDefLocWide(cUnit, rlDest);
+ if (dvmCompilerLiveOut(cUnit, rlDest.sRegLow) ||
+ dvmCompilerLiveOut(cUnit, dvmCompilerSRegHi(rlDest.sRegLow))) {
+ defStart = (LIR *)cUnit->lastLIRInsn;
+ int vReg = dvmCompilerS2VReg(cUnit, rlDest.sRegLow);
+ assert((vReg+1) == dvmCompilerS2VReg(cUnit,
+ dvmCompilerSRegHi(rlDest.sRegLow)));
+ storeBaseDispWide(cUnit, rFP, vReg << 2, rlDest.lowReg,
+ rlDest.highReg);
+ dvmCompilerMarkClean(cUnit, rlDest.lowReg);
+ dvmCompilerMarkClean(cUnit, rlDest.highReg);
+ defEnd = (LIR *)cUnit->lastLIRInsn;
+ dvmCompilerMarkDefWide(cUnit, rlDest, defStart, defEnd);
+ }
+ }
+}
+/*
+ * Perform null-check on a register. sReg is the ssa register being checked,
+ * and mReg is the machine register holding the actual value. If internal state
+ * indicates that sReg has been checked before the check request is ignored.
+ */
+static ArmLIR *genNullCheck(CompilationUnit *cUnit, int sReg, int mReg,
+ int dOffset, ArmLIR *pcrLabel)
+{
+ /* This particular Dalvik register has been null-checked */
+ if (dvmIsBitSet(cUnit->regPool->nullCheckedRegs, sReg)) {
+ return pcrLabel;
+ }
+ dvmSetBit(cUnit->regPool->nullCheckedRegs, sReg);
+ return genRegImmCheck(cUnit, kArmCondEq, mReg, 0, dOffset, pcrLabel);
+}
+
+
+
+/*
+ * Perform a "reg cmp reg" operation and jump to the PCR region if condition
+ * satisfies.
+ */
+static ArmLIR *genRegRegCheck(CompilationUnit *cUnit,
+ ArmConditionCode cond,
+ int reg1, int reg2, int dOffset,
+ ArmLIR *pcrLabel)
+{
+ ArmLIR *res;
+ res = opRegReg(cUnit, kOpCmp, reg1, reg2);
+ ArmLIR *branch = opCondBranch(cUnit, cond);
+ genCheckCommon(cUnit, dOffset, branch, pcrLabel);
+ return res;
+}
+
+/*
+ * Perform zero-check on a register. Similar to genNullCheck but the value being
+ * checked does not have a corresponding Dalvik register.
+ */
+static ArmLIR *genZeroCheck(CompilationUnit *cUnit, int mReg,
+ int dOffset, ArmLIR *pcrLabel)
+{
+ return genRegImmCheck(cUnit, kArmCondEq, mReg, 0, dOffset, pcrLabel);
+}
+
+/* Perform bound check on two registers */
+static ArmLIR *genBoundsCheck(CompilationUnit *cUnit, int rIndex,
+ int rBound, int dOffset, ArmLIR *pcrLabel)
+{
+ return genRegRegCheck(cUnit, kArmCondCs, rIndex, rBound, dOffset,
+ pcrLabel);
+}
+
+/*
+ * Jump to the out-of-line handler in ARM mode to finish executing the
+ * remaining of more complex instructions.
+ */
+static void genDispatchToHandler(CompilationUnit *cUnit, TemplateOpCode opCode)
+{
+ /*
+ * NOTE - In practice BLX only needs one operand, but since the assembler
+ * may abort itself and retry due to other out-of-range conditions we
+ * cannot really use operand[0] to store the absolute target address since
+ * it may get clobbered by the final relative offset. Therefore,
+ * we fake BLX_1 is a two operand instruction and the absolute target
+ * address is stored in operand[1].
+ */
+ dvmCompilerClobberHandlerRegs(cUnit);
+ newLIR2(cUnit, kThumbBlx1,
+ (int) gDvmJit.codeCache + templateEntryOffsets[opCode],
+ (int) gDvmJit.codeCache + templateEntryOffsets[opCode]);
+ newLIR2(cUnit, kThumbBlx2,
+ (int) gDvmJit.codeCache + templateEntryOffsets[opCode],
+ (int) gDvmJit.codeCache + templateEntryOffsets[opCode]);
+}
diff --git a/vm/compiler/codegen/arm/FP/Thumb2VFP.c b/vm/compiler/codegen/arm/FP/Thumb2VFP.c
new file mode 100644
index 0000000..b5bcf99
--- /dev/null
+++ b/vm/compiler/codegen/arm/FP/Thumb2VFP.c
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+static bool genArithOpFloat(CompilationUnit *cUnit, MIR *mir,
+ RegLocation rlDest, RegLocation rlSrc1,
+ RegLocation rlSrc2)
+{
+ int op = kThumbBkpt;
+ RegLocation rlResult;
+
+ /*
+ * Don't attempt to optimize register usage since these opcodes call out to
+ * the handlers.
+ */
+ switch (mir->dalvikInsn.opCode) {
+ case OP_ADD_FLOAT_2ADDR:
+ case OP_ADD_FLOAT:
+ op = kThumb2Vadds;
+ break;
+ case OP_SUB_FLOAT_2ADDR:
+ case OP_SUB_FLOAT:
+ op = kThumb2Vsubs;
+ break;
+ case OP_DIV_FLOAT_2ADDR:
+ case OP_DIV_FLOAT:
+ op = kThumb2Vdivs;
+ break;
+ case OP_MUL_FLOAT_2ADDR:
+ case OP_MUL_FLOAT:
+ op = kThumb2Vmuls;
+ break;
+ case OP_REM_FLOAT_2ADDR:
+ case OP_REM_FLOAT:
+ case OP_NEG_FLOAT: {
+ return genArithOpFloatPortable(cUnit, mir, rlDest, rlSrc1,
+ rlSrc2);
+ }
+ default:
+ return true;
+ }
+ rlSrc1 = loadValue(cUnit, rlSrc1, kFPReg);
+ rlSrc2 = loadValue(cUnit, rlSrc2, kFPReg);
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
+ newLIR3(cUnit, op, rlResult.lowReg, rlSrc1.lowReg, rlSrc2.lowReg);
+ storeValue(cUnit, rlDest, rlResult);
+ return false;
+}
+
+static bool genArithOpDouble(CompilationUnit *cUnit, MIR *mir,
+ RegLocation rlDest, RegLocation rlSrc1,
+ RegLocation rlSrc2)
+{
+ int op = kThumbBkpt;
+ RegLocation rlResult;
+
+ switch (mir->dalvikInsn.opCode) {
+ case OP_ADD_DOUBLE_2ADDR:
+ case OP_ADD_DOUBLE:
+ op = kThumb2Vaddd;
+ break;
+ case OP_SUB_DOUBLE_2ADDR:
+ case OP_SUB_DOUBLE:
+ op = kThumb2Vsubd;
+ break;
+ case OP_DIV_DOUBLE_2ADDR:
+ case OP_DIV_DOUBLE:
+ op = kThumb2Vdivd;
+ break;
+ case OP_MUL_DOUBLE_2ADDR:
+ case OP_MUL_DOUBLE:
+ op = kThumb2Vmuld;
+ break;
+ case OP_REM_DOUBLE_2ADDR:
+ case OP_REM_DOUBLE:
+ case OP_NEG_DOUBLE: {
+ return genArithOpDoublePortable(cUnit, mir, rlDest, rlSrc1,
+ rlSrc2);
+ }
+ default:
+ return true;
+ }
+
+ rlSrc1 = loadValueWide(cUnit, rlSrc1, kFPReg);
+ assert(rlSrc1.wide);
+ rlSrc2 = loadValueWide(cUnit, rlSrc2, kFPReg);
+ assert(rlSrc2.wide);
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
+ assert(rlDest.wide);
+ assert(rlResult.wide);
+ newLIR3(cUnit, op, S2D(rlResult.lowReg, rlResult.highReg),
+ S2D(rlSrc1.lowReg, rlSrc1.highReg),
+ S2D(rlSrc2.lowReg, rlSrc2.highReg));
+ storeValueWide(cUnit, rlDest, rlResult);
+ return false;
+}
+
+static bool genConversion(CompilationUnit *cUnit, MIR *mir)
+{
+ OpCode opCode = mir->dalvikInsn.opCode;
+ int op = kThumbBkpt;
+ bool longSrc = false;
+ bool longDest = false;
+ int srcReg;
+ RegLocation rlSrc;
+ RegLocation rlDest;
+ RegLocation rlResult;
+
+ switch (opCode) {
+ case OP_INT_TO_FLOAT:
+ longSrc = false;
+ longDest = false;
+ op = kThumb2VcvtIF;
+ break;
+ case OP_FLOAT_TO_INT:
+ longSrc = false;
+ longDest = false;
+ op = kThumb2VcvtFI;
+ break;
+ case OP_DOUBLE_TO_FLOAT:
+ longSrc = true;
+ longDest = false;
+ op = kThumb2VcvtDF;
+ break;
+ case OP_FLOAT_TO_DOUBLE:
+ longSrc = false;
+ longDest = true;
+ op = kThumb2VcvtFd;
+ break;
+ case OP_INT_TO_DOUBLE:
+ longSrc = false;
+ longDest = true;
+ op = kThumb2VcvtID;
+ break;
+ case OP_DOUBLE_TO_INT:
+ longSrc = true;
+ longDest = false;
+ op = kThumb2VcvtDI;
+ break;
+ case OP_LONG_TO_DOUBLE:
+ case OP_FLOAT_TO_LONG:
+ case OP_LONG_TO_FLOAT:
+ case OP_DOUBLE_TO_LONG:
+ return genConversionPortable(cUnit, mir);
+ default:
+ return true;
+ }
+ if (longSrc) {
+ rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+ rlSrc = loadValueWide(cUnit, rlSrc, kFPReg);
+ srcReg = S2D(rlSrc.lowReg, rlSrc.highReg);
+ } else {
+ rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+ rlSrc = loadValue(cUnit, rlSrc, kFPReg);
+ srcReg = rlSrc.lowReg;
+ }
+ if (longDest) {
+ rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
+ newLIR2(cUnit, op, S2D(rlResult.lowReg, rlResult.highReg), srcReg);
+ storeValueWide(cUnit, rlDest, rlResult);
+ } else {
+ rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
+ newLIR2(cUnit, op, rlResult.lowReg, srcReg);
+ storeValue(cUnit, rlDest, rlResult);
+ }
+ return false;
+}
+
+static bool genInlineSqrt(CompilationUnit *cUnit, MIR *mir)
+{
+ ArmLIR *branch;
+ RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+ RegLocation rlDest = inlinedTargetWide(cUnit, mir, true);
+ rlSrc = loadValueWide(cUnit, rlSrc, kFPReg);
+ RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
+ newLIR2(cUnit, kThumb2Vsqrtd, S2D(rlResult.lowReg, rlResult.highReg),
+ S2D(rlSrc.lowReg, rlSrc.highReg));
+ newLIR2(cUnit, kThumb2Vcmpd, S2D(rlResult.lowReg, rlResult.highReg),
+ S2D(rlResult.lowReg, rlResult.highReg));
+ newLIR0(cUnit, kThumb2Fmstat);
+ branch = newLIR2(cUnit, kThumbBCond, 0, kArmCondEq);
+ dvmCompilerClobberCallRegs(cUnit);
+ LOAD_FUNC_ADDR(cUnit, r2, (int)sqrt);
+ newLIR3(cUnit, kThumb2Fmrrd, r0, r1, S2D(rlSrc.lowReg, rlSrc.highReg));
+ newLIR1(cUnit, kThumbBlxR, r2);
+ newLIR3(cUnit, kThumb2Fmdrr, S2D(rlResult.lowReg, rlResult.highReg),
+ r0, r1);
+ ArmLIR *label = newLIR0(cUnit, kArmPseudoTargetLabel);
+ label->defMask = ENCODE_ALL;
+ branch->generic.target = (LIR *)label;
+ storeValueWide(cUnit, rlDest, rlResult);
+ return true;
+}
+
+static bool genCmpFP(CompilationUnit *cUnit, MIR *mir, RegLocation rlDest,
+ RegLocation rlSrc1, RegLocation rlSrc2)
+{
+ bool isDouble;
+ int defaultResult;
+ RegLocation rlResult;
+
+ switch(mir->dalvikInsn.opCode) {
+ case OP_CMPL_FLOAT:
+ isDouble = false;
+ defaultResult = -1;
+ break;
+ case OP_CMPG_FLOAT:
+ isDouble = false;
+ defaultResult = 1;
+ break;
+ case OP_CMPL_DOUBLE:
+ isDouble = true;
+ defaultResult = -1;
+ break;
+ case OP_CMPG_DOUBLE:
+ isDouble = true;
+ defaultResult = 1;
+ break;
+ default:
+ return true;
+ }
+ if (isDouble) {
+ rlSrc1 = loadValueWide(cUnit, rlSrc1, kFPReg);
+ rlSrc2 = loadValueWide(cUnit, rlSrc2, kFPReg);
+ dvmCompilerClobberSReg(cUnit, rlDest.sRegLow);
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+ loadConstant(cUnit, rlResult.lowReg, defaultResult);
+ newLIR2(cUnit, kThumb2Vcmpd, S2D(rlSrc1.lowReg, r1Src2.highReg),
+ S2D(rlSrc2.lowReg, rlSrc2.highReg));
+ } else {
+ rlSrc1 = loadValue(cUnit, rlSrc1, kFPReg);
+ rlSrc2 = loadValue(cUnit, rlSrc2, kFPReg);
+ dvmCompilerClobberSReg(cUnit, rlDest.sRegLow);
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+ loadConstant(cUnit, rlResult.lowReg, defaultResult);
+ newLIR2(cUnit, kThumb2Vcmps, rlSrc1.lowReg, rlSrc2.lowReg);
+ }
+ assert(!FPREG(rlResult.lowReg));
+ newLIR0(cUnit, kThumb2Fmstat);
+ genIT(cUnit, (defaultResult == -1) ? kArmCondGt : kArmCondMi, "");
+ newLIR2(cUnit, kThumb2MovImmShift, rlResult.lowReg,
+ modifiedImmediate(-defaultResult)); // Must not alter ccodes
+ genIT(cUnit, kArmCondEq, "");
+ loadConstant(cUnit, rlResult.lowReg, 0);
+ storeValue(cUnit, rlDest, rlResult);
+ return false;
+}
diff --git a/vm/compiler/codegen/arm/FP/ThumbPortableFP.c b/vm/compiler/codegen/arm/FP/ThumbPortableFP.c
new file mode 100644
index 0000000..957b4d4
--- /dev/null
+++ b/vm/compiler/codegen/arm/FP/ThumbPortableFP.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Forward decalraton the portable versions due to circular dependency */
+static bool genArithOpFloatPortable(CompilationUnit *cUnit, MIR *mir,
+ RegLocation rlDest, RegLocation rlSrc1,
+ RegLocation rlSrc2);
+
+static bool genArithOpDoublePortable(CompilationUnit *cUnit, MIR *mir,
+ RegLocation rlDest, RegLocation rlSrc1,
+ RegLocation rlSrc2);
+
+static bool genConversionPortable(CompilationUnit *cUnit, MIR *mir);
+
+static bool genConversion(CompilationUnit *cUnit, MIR *mir)
+{
+ return genConversionPortable(cUnit, mir);
+}
+
+static bool genArithOpFloat(CompilationUnit *cUnit, MIR *mir,
+ RegLocation rlDest, RegLocation rlSrc1,
+ RegLocation rlSrc2)
+{
+ return genArithOpFloatPortable(cUnit, mir, rlDest, rlSrc1, rlSrc2);
+}
+
+static bool genArithOpDouble(CompilationUnit *cUnit, MIR *mir,
+ RegLocation rlDest, RegLocation rlSrc1,
+ RegLocation rlSrc2)
+{
+ return genArithOpDoublePortable(cUnit, mir, rlDest, rlSrc1, rlSrc2);
+}
+
+static bool genInlineSqrt(CompilationUnit *cUnit, MIR *mir)
+{
+ return false; /* punt to C handler */
+}
+
+static bool genCmpFP(CompilationUnit *cUnit, MIR *mir, RegLocation rlDest,
+ RegLocation rlSrc1, RegLocation rlSrc2)
+{
+ RegLocation rlResult = LOC_C_RETURN;
+ /*
+ * Don't attempt to optimize register usage since these opcodes call out to
+ * the handlers.
+ */
+ switch (mir->dalvikInsn.opCode) {
+ case OP_CMPL_FLOAT:
+ loadValueDirectFixed(cUnit, rlSrc1, r0);
+ loadValueDirectFixed(cUnit, rlSrc2, r1);
+ genDispatchToHandler(cUnit, TEMPLATE_CMPL_FLOAT);
+ storeValue(cUnit, rlDest, rlResult);
+ break;
+ case OP_CMPG_FLOAT:
+ loadValueDirectFixed(cUnit, rlSrc1, r0);
+ loadValueDirectFixed(cUnit, rlSrc2, r1);
+ genDispatchToHandler(cUnit, TEMPLATE_CMPG_FLOAT);
+ storeValue(cUnit, rlDest, rlResult);
+ break;
+ case OP_CMPL_DOUBLE:
+ loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
+ loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
+ genDispatchToHandler(cUnit, TEMPLATE_CMPL_DOUBLE);
+ storeValue(cUnit, rlDest, rlResult);
+ break;
+ case OP_CMPG_DOUBLE:
+ loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
+ loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
+ genDispatchToHandler(cUnit, TEMPLATE_CMPG_DOUBLE);
+ storeValue(cUnit, rlDest, rlResult);
+ break;
+ default:
+ return true;
+ }
+ return false;
+}
diff --git a/vm/compiler/codegen/arm/FP/ThumbVFP.c b/vm/compiler/codegen/arm/FP/ThumbVFP.c
new file mode 100644
index 0000000..16d3ff7
--- /dev/null
+++ b/vm/compiler/codegen/arm/FP/ThumbVFP.c
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file is included by Codegen-armv5te-vfp.c, and implements architecture
+ * variant-specific code.
+ */
+
+extern void dvmCompilerFlushRegWideForV5TEVFP(CompilationUnit *cUnit,
+ int reg1, int reg2);
+extern void dvmCompilerFlushRegForV5TEVFP(CompilationUnit *cUnit, int reg);
+
+/*
+ * Take the address of a Dalvik register and store it into rDest.
+ * Clobber any live values associated either with the Dalvik value
+ * or the target register and lock the target fixed register.
+ */
+static void loadValueAddressDirect(CompilationUnit *cUnit, RegLocation rlSrc,
+ int rDest)
+{
+ rlSrc = rlSrc.wide ? dvmCompilerUpdateLocWide(cUnit, rlSrc) :
+ dvmCompilerUpdateLoc(cUnit, rlSrc);
+ if (rlSrc.location == kLocPhysReg) {
+ if (rlSrc.wide) {
+ dvmCompilerFlushRegWideForV5TEVFP(cUnit, rlSrc.lowReg,
+ rlSrc.highReg);
+ } else {
+ dvmCompilerFlushRegForV5TEVFP(cUnit, rlSrc.lowReg);
+ }
+ }
+ dvmCompilerClobber(cUnit, rDest);
+ dvmCompilerLockTemp(cUnit, rDest);
+ opRegRegImm(cUnit, kOpAdd, rDest, rFP,
+ dvmCompilerS2VReg(cUnit, rlSrc.sRegLow) << 2);
+}
+
+static bool genInlineSqrt(CompilationUnit *cUnit, MIR *mir)
+{
+ RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+ RegLocation rlResult = LOC_C_RETURN_WIDE;
+ RegLocation rlDest = LOC_DALVIK_RETURN_VAL_WIDE;
+ loadValueAddressDirect(cUnit, rlSrc, r2);
+ genDispatchToHandler(cUnit, TEMPLATE_SQRT_DOUBLE_VFP);
+ storeValueWide(cUnit, rlDest, rlResult);
+ return false;
+}
+
+/*
+ * TUNING: On some implementations, it is quicker to pass addresses
+ * to the handlers rather than load the operands into core registers
+ * and then move the values to FP regs in the handlers. Other implementations
+ * may prefer passing data in registers (and the latter approach would
+ * yeild cleaner register handling - avoiding the requirement that operands
+ * be flushed to memory prior to the call).
+ */
+static bool genArithOpFloat(CompilationUnit *cUnit, MIR *mir,
+ RegLocation rlDest, RegLocation rlSrc1,
+ RegLocation rlSrc2)
+{
+ TemplateOpCode opCode;
+
+ /*
+ * Don't attempt to optimize register usage since these opcodes call out to
+ * the handlers.
+ */
+ switch (mir->dalvikInsn.opCode) {
+ case OP_ADD_FLOAT_2ADDR:
+ case OP_ADD_FLOAT:
+ opCode = TEMPLATE_ADD_FLOAT_VFP;
+ break;
+ case OP_SUB_FLOAT_2ADDR:
+ case OP_SUB_FLOAT:
+ opCode = TEMPLATE_SUB_FLOAT_VFP;
+ break;
+ case OP_DIV_FLOAT_2ADDR:
+ case OP_DIV_FLOAT:
+ opCode = TEMPLATE_DIV_FLOAT_VFP;
+ break;
+ case OP_MUL_FLOAT_2ADDR:
+ case OP_MUL_FLOAT:
+ opCode = TEMPLATE_MUL_FLOAT_VFP;
+ break;
+ case OP_REM_FLOAT_2ADDR:
+ case OP_REM_FLOAT:
+ case OP_NEG_FLOAT: {
+ return genArithOpFloatPortable(cUnit, mir, rlDest, rlSrc1, rlSrc2);
+ }
+ default:
+ return true;
+ }
+ loadValueAddressDirect(cUnit, rlDest, r0);
+ loadValueAddressDirect(cUnit, rlSrc1, r1);
+ loadValueAddressDirect(cUnit, rlSrc2, r2);
+ genDispatchToHandler(cUnit, opCode);
+ rlDest = dvmCompilerUpdateLoc(cUnit, rlDest);
+ if (rlDest.location == kLocPhysReg) {
+ dvmCompilerClobber(cUnit, rlDest.lowReg);
+ }
+ return false;
+}
+
+static bool genArithOpDouble(CompilationUnit *cUnit, MIR *mir,
+ RegLocation rlDest, RegLocation rlSrc1,
+ RegLocation rlSrc2)
+{
+ TemplateOpCode opCode;
+
+ switch (mir->dalvikInsn.opCode) {
+ case OP_ADD_DOUBLE_2ADDR:
+ case OP_ADD_DOUBLE:
+ opCode = TEMPLATE_ADD_DOUBLE_VFP;
+ break;
+ case OP_SUB_DOUBLE_2ADDR:
+ case OP_SUB_DOUBLE:
+ opCode = TEMPLATE_SUB_DOUBLE_VFP;
+ break;
+ case OP_DIV_DOUBLE_2ADDR:
+ case OP_DIV_DOUBLE:
+ opCode = TEMPLATE_DIV_DOUBLE_VFP;
+ break;
+ case OP_MUL_DOUBLE_2ADDR:
+ case OP_MUL_DOUBLE:
+ opCode = TEMPLATE_MUL_DOUBLE_VFP;
+ break;
+ case OP_REM_DOUBLE_2ADDR:
+ case OP_REM_DOUBLE:
+ case OP_NEG_DOUBLE: {
+ return genArithOpDoublePortable(cUnit, mir, rlDest, rlSrc1,
+ rlSrc2);
+ }
+ default:
+ return true;
+ }
+ loadValueAddressDirect(cUnit, rlDest, r0);
+ loadValueAddressDirect(cUnit, rlSrc1, r1);
+ loadValueAddressDirect(cUnit, rlSrc2, r2);
+ genDispatchToHandler(cUnit, opCode);
+ rlDest = dvmCompilerUpdateLocWide(cUnit, rlDest);
+ if (rlDest.location == kLocPhysReg) {
+ dvmCompilerClobber(cUnit, rlDest.lowReg);
+ dvmCompilerClobber(cUnit, rlDest.highReg);
+ }
+ return false;
+}
+
+static bool genConversion(CompilationUnit *cUnit, MIR *mir)
+{
+ OpCode opCode = mir->dalvikInsn.opCode;
+ bool longSrc = false;
+ bool longDest = false;
+ RegLocation rlSrc;
+ RegLocation rlDest;
+ TemplateOpCode template;
+ switch (opCode) {
+ case OP_INT_TO_FLOAT:
+ longSrc = false;
+ longDest = false;
+ template = TEMPLATE_INT_TO_FLOAT_VFP;
+ break;
+ case OP_FLOAT_TO_INT:
+ longSrc = false;
+ longDest = false;
+ template = TEMPLATE_FLOAT_TO_INT_VFP;
+ break;
+ case OP_DOUBLE_TO_FLOAT:
+ longSrc = true;
+ longDest = false;
+ template = TEMPLATE_DOUBLE_TO_FLOAT_VFP;
+ break;
+ case OP_FLOAT_TO_DOUBLE:
+ longSrc = false;
+ longDest = true;
+ template = TEMPLATE_FLOAT_TO_DOUBLE_VFP;
+ break;
+ case OP_INT_TO_DOUBLE:
+ longSrc = false;
+ longDest = true;
+ template = TEMPLATE_INT_TO_DOUBLE_VFP;
+ break;
+ case OP_DOUBLE_TO_INT:
+ longSrc = true;
+ longDest = false;
+ template = TEMPLATE_DOUBLE_TO_INT_VFP;
+ break;
+ case OP_LONG_TO_DOUBLE:
+ case OP_FLOAT_TO_LONG:
+ case OP_LONG_TO_FLOAT:
+ case OP_DOUBLE_TO_LONG:
+ return genConversionPortable(cUnit, mir);
+ default:
+ return true;
+ }
+
+ if (longSrc) {
+ rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+ } else {
+ rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+ }
+
+ if (longDest) {
+ rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
+ } else {
+ rlDest = dvmCompilerGetDest(cUnit, mir, 0);
+ }
+ loadValueAddressDirect(cUnit, rlDest, r0);
+ loadValueAddressDirect(cUnit, rlSrc, r1);
+ genDispatchToHandler(cUnit, template);
+ if (rlDest.wide) {
+ rlDest = dvmCompilerUpdateLocWide(cUnit, rlDest);
+ dvmCompilerClobber(cUnit, rlDest.highReg);
+ } else {
+ rlDest = dvmCompilerUpdateLoc(cUnit, rlDest);
+ }
+ dvmCompilerClobber(cUnit, rlDest.lowReg);
+ return false;
+}
+
+static bool genCmpFP(CompilationUnit *cUnit, MIR *mir, RegLocation rlDest,
+ RegLocation rlSrc1, RegLocation rlSrc2)
+{
+ TemplateOpCode template;
+ RegLocation rlResult = dvmCompilerGetReturn(cUnit);
+ bool wide = true;
+
+ switch(mir->dalvikInsn.opCode) {
+ case OP_CMPL_FLOAT:
+ template = TEMPLATE_CMPL_FLOAT_VFP;
+ wide = false;
+ break;
+ case OP_CMPG_FLOAT:
+ template = TEMPLATE_CMPG_FLOAT_VFP;
+ wide = false;
+ break;
+ case OP_CMPL_DOUBLE:
+ template = TEMPLATE_CMPL_DOUBLE_VFP;
+ break;
+ case OP_CMPG_DOUBLE:
+ template = TEMPLATE_CMPG_DOUBLE_VFP;
+ break;
+ default:
+ return true;
+ }
+ loadValueAddressDirect(cUnit, rlSrc1, r0);
+ loadValueAddressDirect(cUnit, rlSrc2, r1);
+ genDispatchToHandler(cUnit, template);
+ storeValue(cUnit, rlDest, rlResult);
+ return false;
+}
diff --git a/vm/compiler/codegen/arm/GlobalOptimizations.c b/vm/compiler/codegen/arm/GlobalOptimizations.c
new file mode 100644
index 0000000..1cfa32b
--- /dev/null
+++ b/vm/compiler/codegen/arm/GlobalOptimizations.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "vm/compiler/CompilerInternals.h"
+#include "ArmLIR.h"
+
+/*
+ * Identify unconditional branches that jump to the immediate successor of the
+ * branch itself.
+ */
+static void applyRedundantBranchElimination(CompilationUnit *cUnit)
+{
+ ArmLIR *thisLIR;
+
+ for (thisLIR = (ArmLIR *) cUnit->firstLIRInsn;
+ thisLIR != (ArmLIR *) cUnit->lastLIRInsn;
+ thisLIR = NEXT_LIR(thisLIR)) {
+
+ /* Branch to the next instruction */
+ if (thisLIR->opCode == kThumbBUncond) {
+ ArmLIR *nextLIR = thisLIR;
+
+ while (true) {
+ nextLIR = NEXT_LIR(nextLIR);
+
+ /*
+ * Is the branch target the next instruction?
+ */
+ if (nextLIR == (ArmLIR *) thisLIR->generic.target) {
+ thisLIR->isNop = true;
+ break;
+ }
+
+ /*
+ * Found real useful stuff between the branch and the target
+ */
+ if (!isPseudoOpCode(nextLIR->opCode))
+ break;
+ }
+ }
+ }
+}
+
+void dvmCompilerApplyGlobalOptimizations(CompilationUnit *cUnit)
+{
+ applyRedundantBranchElimination(cUnit);
+}
diff --git a/vm/compiler/codegen/arm/LocalOptimizations.c b/vm/compiler/codegen/arm/LocalOptimizations.c
new file mode 100644
index 0000000..724fdb7
--- /dev/null
+++ b/vm/compiler/codegen/arm/LocalOptimizations.c
@@ -0,0 +1,512 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "vm/compiler/CompilerInternals.h"
+#include "ArmLIR.h"
+#include "Codegen.h"
+
+#define DEBUG_OPT(X)
+
+ArmLIR* dvmCompilerGenCopy(CompilationUnit *cUnit, int rDest, int rSrc);
+
+/* Is this a Dalvik register access? */
+static inline bool isDalvikLoad(ArmLIR *lir)
+{
+ return (lir->useMask != ENCODE_ALL) && (lir->useMask & ENCODE_DALVIK_REG);
+}
+
+/* Is this a load from the literal pool? */
+static inline bool isLiteralLoad(ArmLIR *lir)
+{
+ return (lir->useMask != ENCODE_ALL) && (lir->useMask & ENCODE_LITERAL);
+}
+
+static inline bool isDalvikStore(ArmLIR *lir)
+{
+ return (lir->defMask != ENCODE_ALL) && (lir->defMask & ENCODE_DALVIK_REG);
+}
+
+static inline bool isDalvikRegisterClobbered(ArmLIR *lir1, ArmLIR *lir2)
+{
+ int reg1Lo = DECODE_ALIAS_INFO_REG(lir1->aliasInfo);
+ int reg1Hi = reg1Lo + DECODE_ALIAS_INFO_WIDE(lir1->aliasInfo);
+ int reg2Lo = DECODE_ALIAS_INFO_REG(lir2->aliasInfo);
+ int reg2Hi = reg2Lo + DECODE_ALIAS_INFO_WIDE(lir2->aliasInfo);
+
+ return (reg1Lo == reg2Lo) || (reg1Lo == reg2Hi) || (reg1Hi == reg2Lo);
+}
+
+#if 0
+/* Debugging utility routine */
+static void dumpDependentInsnPair(ArmLIR *thisLIR, ArmLIR *checkLIR,
+ const char *optimization)
+{
+ LOGD("************ %s ************", optimization);
+ dvmDumpLIRInsn((LIR *) thisLIR, 0);
+ dvmDumpLIRInsn((LIR *) checkLIR, 0);
+}
+#endif
+
+/*
+ * Perform a pass of top-down walk to
+ * 1) Eliminate redundant loads and stores
+ * 2) Sink stores to latest possible slot
+ */
+static void applyLoadStoreElimination(CompilationUnit *cUnit,
+ ArmLIR *headLIR,
+ ArmLIR *tailLIR)
+{
+ ArmLIR *thisLIR;
+
+ cUnit->optRound++;
+ for (thisLIR = headLIR;
+ thisLIR != tailLIR;
+ thisLIR = NEXT_LIR(thisLIR)) {
+ /* Skip newly added instructions */
+ if (thisLIR->age >= cUnit->optRound) {
+ continue;
+ }
+ if (isDalvikStore(thisLIR)) {
+ int nativeRegId = thisLIR->operands[0];
+ ArmLIR *checkLIR;
+ int sinkDistance = 0;
+ /*
+ * Add r15 (pc) to the mask to prevent this instruction
+ * from sinking past branch instructions. Unset the Dalvik register
+ * bit when checking with native resource constraints.
+ */
+ u8 stopMask = (ENCODE_REG_PC | thisLIR->useMask) &
+ ~ENCODE_DALVIK_REG;
+
+ for (checkLIR = NEXT_LIR(thisLIR);
+ checkLIR != tailLIR;
+ checkLIR = NEXT_LIR(checkLIR)) {
+
+ /* Check if a Dalvik register load is redundant */
+ if (isDalvikLoad(checkLIR) &&
+ (checkLIR->aliasInfo == thisLIR->aliasInfo) &&
+ (REGTYPE(checkLIR->operands[0]) == REGTYPE(nativeRegId))) {
+ /* Insert a move to replace the load */
+ if (checkLIR->operands[0] != nativeRegId) {
+ ArmLIR *moveLIR;
+ moveLIR = dvmCompilerRegCopyNoInsert(
+ cUnit, checkLIR->operands[0], nativeRegId);
+ /*
+ * Insert the converted checkLIR instruction after the
+ * the original checkLIR since the optimization is
+ * scannng in the top-down order and the new instruction
+ * will need to be checked.
+ */
+ dvmCompilerInsertLIRAfter((LIR *) checkLIR,
+ (LIR *) moveLIR);
+ }
+ checkLIR->isNop = true;
+ continue;
+
+ /*
+ * Found a true output dependency - nuke the previous store.
+ * The register type doesn't matter here.
+ */
+ } else if (isDalvikStore(checkLIR) &&
+ (checkLIR->aliasInfo == thisLIR->aliasInfo)) {
+ thisLIR->isNop = true;
+ break;
+ /* Find out the latest slot that the store can be sunk into */
+ } else {
+ /* Last instruction reached */
+ bool stopHere = (NEXT_LIR(checkLIR) == tailLIR);
+
+ /* Store data is clobbered */
+ stopHere |= ((stopMask & checkLIR->defMask) != 0);
+
+ /* Store data partially clobbers the Dalvik register */
+ if (stopHere == false &&
+ ((checkLIR->useMask | checkLIR->defMask) &
+ ENCODE_DALVIK_REG)) {
+ stopHere = isDalvikRegisterClobbered(thisLIR, checkLIR);
+ }
+
+ /* Found a new place to put the store - move it here */
+ if (stopHere == true) {
+ DEBUG_OPT(dumpDependentInsnPair(thisLIR, checkLIR,
+ "SINK STORE"));
+ /* The store can be sunk for at least one cycle */
+ if (sinkDistance != 0) {
+ ArmLIR *newStoreLIR =
+ dvmCompilerNew(sizeof(ArmLIR), true);
+ *newStoreLIR = *thisLIR;
+ newStoreLIR->age = cUnit->optRound;
+ /*
+ * Stop point found - insert *before* the checkLIR
+ * since the instruction list is scanned in the
+ * top-down order.
+ */
+ dvmCompilerInsertLIRBefore((LIR *) checkLIR,
+ (LIR *) newStoreLIR);
+ thisLIR->isNop = true;
+ }
+ break;
+ }
+
+ /*
+ * Saw a real instruction that the store can be sunk after
+ */
+ if (!isPseudoOpCode(checkLIR->opCode)) {
+ sinkDistance++;
+ }
+ }
+ }
+ }
+ }
+}
+
+static void applyLoadHoisting(CompilationUnit *cUnit,
+ ArmLIR *headLIR,
+ ArmLIR *tailLIR)
+{
+ ArmLIR *thisLIR;
+ /*
+ * Don't want to hoist in front of first load following a barrier (or
+ * first instruction of the block.
+ */
+ bool firstLoad = true;
+ int maxHoist = dvmCompilerTargetOptHint(kMaxHoistDistance);
+
+ cUnit->optRound++;
+ for (thisLIR = headLIR;
+ thisLIR != tailLIR;
+ thisLIR = NEXT_LIR(thisLIR)) {
+ /* Skip newly added instructions */
+ if (thisLIR->age >= cUnit->optRound ||
+ thisLIR->isNop == true) {
+ continue;
+ }
+
+ if (firstLoad && (EncodingMap[thisLIR->opCode].flags & IS_LOAD)) {
+ /*
+ * Ensure nothing will be hoisted in front of this load because
+ * it's result will likely be needed soon.
+ */
+ thisLIR->defMask |= ENCODE_MEM_USE;
+ firstLoad = false;
+ }
+
+ firstLoad |= (thisLIR->defMask == ENCODE_ALL);
+
+ if (isDalvikLoad(thisLIR)) {
+ int dRegId = DECODE_ALIAS_INFO_REG(thisLIR->aliasInfo);
+ int nativeRegId = thisLIR->operands[0];
+ ArmLIR *checkLIR;
+ int hoistDistance = 0;
+ u8 stopUseMask = (ENCODE_REG_PC | thisLIR->useMask);
+ u8 stopDefMask = thisLIR->defMask;
+ u8 checkResult;
+
+ /* First check if the load can be completely elinimated */
+ for (checkLIR = PREV_LIR(thisLIR);
+ checkLIR != headLIR;
+ checkLIR = PREV_LIR(checkLIR)) {
+
+ if (checkLIR->isNop) continue;
+
+ /*
+ * Check if the Dalvik register is previously accessed
+ * with exactly the same type.
+ */
+ if ((isDalvikLoad(checkLIR) || isDalvikStore(checkLIR)) &&
+ (checkLIR->aliasInfo == thisLIR->aliasInfo) &&
+ (checkLIR->operands[0] == nativeRegId)) {
+ /*
+ * If it is previously accessed but with a different type,
+ * the search will terminate later at the point checking
+ * for partially overlapping stores.
+ */
+ thisLIR->isNop = true;
+ break;
+ }
+
+ /*
+ * No earlier use/def can reach this load if:
+ * 1) Head instruction is reached
+ */
+ if (checkLIR == headLIR) {
+ break;
+ }
+
+ checkResult = (stopUseMask | stopDefMask) & checkLIR->defMask;
+
+ /*
+ * If both instructions are verified Dalvik accesses, clear the
+ * may- and must-alias bits to detect true resource
+ * dependencies.
+ */
+ if (checkResult & ENCODE_DALVIK_REG) {
+ checkResult &= ~(ENCODE_DALVIK_REG | ENCODE_FRAME_REF);
+ }
+
+ /*
+ * 2) load target register is clobbered
+ * 3) A branch is seen (stopUseMask has the PC bit set).
+ */
+ if (checkResult) {
+ break;
+ }
+
+ /* Store data partially clobbers the Dalvik register */
+ if (isDalvikStore(checkLIR) &&
+ isDalvikRegisterClobbered(thisLIR, checkLIR)) {
+ break;
+ }
+ }
+
+ /* The load has been eliminated */
+ if (thisLIR->isNop) continue;
+
+ /*
+ * The load cannot be eliminated. See if it can be hoisted to an
+ * earlier spot.
+ */
+ for (checkLIR = PREV_LIR(thisLIR);
+ /* empty by intention */;
+ checkLIR = PREV_LIR(checkLIR)) {
+
+ if (checkLIR->isNop) continue;
+
+ /*
+ * Check if the "thisLIR" load is redundant
+ * NOTE: At one point, we also triggered if the checkLIR
+ * instruction was a load. However, that tended to insert
+ * a load/use dependency because the full scheduler is
+ * not yet complete. When it is, we chould also trigger
+ * on loads.
+ */
+ if (isDalvikStore(checkLIR) &&
+ (checkLIR->aliasInfo == thisLIR->aliasInfo) &&
+ (REGTYPE(checkLIR->operands[0]) == REGTYPE(nativeRegId))) {
+ /* Insert a move to replace the load */
+ if (checkLIR->operands[0] != nativeRegId) {
+ ArmLIR *moveLIR;
+ moveLIR = dvmCompilerRegCopyNoInsert(
+ cUnit, nativeRegId, checkLIR->operands[0]);
+ /*
+ * Convert *thisLIR* load into a move
+ */
+ dvmCompilerInsertLIRAfter((LIR *) checkLIR,
+ (LIR *) moveLIR);
+ }
+ thisLIR->isNop = true;
+ break;
+
+ /* Find out if the load can be yanked past the checkLIR */
+ } else {
+ /* Last instruction reached */
+ bool stopHere = (checkLIR == headLIR);
+
+ /* Base address is clobbered by checkLIR */
+ checkResult = stopUseMask & checkLIR->defMask;
+ if (checkResult & ENCODE_DALVIK_REG) {
+ checkResult &= ~(ENCODE_DALVIK_REG | ENCODE_FRAME_REF);
+ }
+ stopHere |= (checkResult != 0);
+
+ /* Load target clobbers use/def in checkLIR */
+ checkResult = stopDefMask &
+ (checkLIR->useMask | checkLIR->defMask);
+ if (checkResult & ENCODE_DALVIK_REG) {
+ checkResult &= ~(ENCODE_DALVIK_REG | ENCODE_FRAME_REF);
+ }
+ stopHere |= (checkResult != 0);
+
+ /* Store data partially clobbers the Dalvik register */
+ if (stopHere == false &&
+ (checkLIR->defMask & ENCODE_DALVIK_REG)) {
+ stopHere = isDalvikRegisterClobbered(thisLIR, checkLIR);
+ }
+
+ /*
+ * Stop at an earlier Dalvik load if the offset of checkLIR
+ * is not less than thisLIR
+ *
+ * Experiments show that doing
+ *
+ * ldr r1, [r5, #16]
+ * ldr r0, [r5, #20]
+ *
+ * is much faster than
+ *
+ * ldr r0, [r5, #20]
+ * ldr r1, [r5, #16]
+ */
+ if (isDalvikLoad(checkLIR)) {
+ int dRegId2 =
+ DECODE_ALIAS_INFO_REG(checkLIR->aliasInfo);
+ if (dRegId2 <= dRegId) {
+ stopHere = true;
+ }
+ }
+
+ /* Don't go too far */
+ stopHere |= (hoistDistance >= maxHoist);
+
+ /* Found a new place to put the load - move it here */
+ if (stopHere == true) {
+ DEBUG_OPT(dumpDependentInsnPair(thisLIR, checkLIR,
+ "HOIST LOAD"));
+ /* The load can be hoisted for at least one cycle */
+ if (hoistDistance != 0) {
+ ArmLIR *newLoadLIR =
+ dvmCompilerNew(sizeof(ArmLIR), true);
+ *newLoadLIR = *thisLIR;
+ newLoadLIR->age = cUnit->optRound;
+ /*
+ * Stop point found - insert *after* the checkLIR
+ * since the instruction list is scanned in the
+ * bottom-up order.
+ */
+ dvmCompilerInsertLIRAfter((LIR *) checkLIR,
+ (LIR *) newLoadLIR);
+ thisLIR->isNop = true;
+ }
+ break;
+ }
+
+ /*
+ * Saw a real instruction that hosting the load is
+ * beneficial
+ */
+ if (!isPseudoOpCode(checkLIR->opCode)) {
+ hoistDistance++;
+ }
+ }
+ }
+ } else if (isLiteralLoad(thisLIR)) {
+ int litVal = thisLIR->aliasInfo;
+ int nativeRegId = thisLIR->operands[0];
+ ArmLIR *checkLIR;
+ int hoistDistance = 0;
+ u8 stopUseMask = (ENCODE_REG_PC | thisLIR->useMask) &
+ ~ENCODE_LITPOOL_REF;
+ u8 stopDefMask = thisLIR->defMask & ~ENCODE_LITPOOL_REF;
+
+ /* First check if the load can be completely elinimated */
+ for (checkLIR = PREV_LIR(thisLIR);
+ checkLIR != headLIR;
+ checkLIR = PREV_LIR(checkLIR)) {
+
+ if (checkLIR->isNop) continue;
+
+ /* Reloading same literal into same tgt reg? Eliminate if so */
+ if (isLiteralLoad(checkLIR) &&
+ (checkLIR->aliasInfo == litVal) &&
+ (checkLIR->operands[0] == nativeRegId)) {
+ thisLIR->isNop = true;
+ break;
+ }
+
+ /*
+ * No earlier use/def can reach this load if:
+ * 1) Head instruction is reached
+ * 2) load target register is clobbered
+ * 3) A branch is seen (stopUseMask has the PC bit set).
+ */
+ if ((checkLIR == headLIR) ||
+ (stopUseMask | stopDefMask) & checkLIR->defMask) {
+ break;
+ }
+ }
+
+ /* The load has been eliminated */
+ if (thisLIR->isNop) continue;
+
+ /*
+ * The load cannot be eliminated. See if it can be hoisted to an
+ * earlier spot.
+ */
+ for (checkLIR = PREV_LIR(thisLIR);
+ /* empty by intention */;
+ checkLIR = PREV_LIR(checkLIR)) {
+
+ if (checkLIR->isNop) continue;
+
+ /*
+ * TUNING: once a full scheduler exists, check here
+ * for conversion of a redundant load into a copy similar
+ * to the way redundant loads are handled above.
+ */
+
+ /* Find out if the load can be yanked past the checkLIR */
+
+ /* Last instruction reached */
+ bool stopHere = (checkLIR == headLIR);
+
+ /* Base address is clobbered by checkLIR */
+ stopHere |= ((stopUseMask & checkLIR->defMask) != 0);
+
+ /* Load target clobbers use/def in checkLIR */
+ stopHere |= ((stopDefMask &
+ (checkLIR->useMask | checkLIR->defMask)) != 0);
+
+ /* Avoid re-ordering literal pool loads */
+ stopHere |= isLiteralLoad(checkLIR);
+
+ /* Don't go too far */
+ stopHere |= (hoistDistance >= maxHoist);
+
+ /* Found a new place to put the load - move it here */
+ if (stopHere == true) {
+ DEBUG_OPT(dumpDependentInsnPair(thisLIR, checkLIR,
+ "HOIST LOAD"));
+ /* The store can be hoisted for at least one cycle */
+ if (hoistDistance != 0) {
+ ArmLIR *newLoadLIR =
+ dvmCompilerNew(sizeof(ArmLIR), true);
+ *newLoadLIR = *thisLIR;
+ newLoadLIR->age = cUnit->optRound;
+ /*
+ * Insertion is guaranteed to succeed since checkLIR
+ * is never the first LIR on the list
+ */
+ dvmCompilerInsertLIRAfter((LIR *) checkLIR,
+ (LIR *) newLoadLIR);
+ thisLIR->isNop = true;
+ }
+ break;
+ }
+
+ /*
+ * Saw a real instruction that hosting the load is
+ * beneficial
+ */
+ if (!isPseudoOpCode(checkLIR->opCode)) {
+ hoistDistance++;
+ }
+ }
+ }
+ }
+}
+
+void dvmCompilerApplyLocalOptimizations(CompilationUnit *cUnit, LIR *headLIR,
+ LIR *tailLIR)
+{
+ if (!(gDvmJit.disableOpt & (1 << kLoadStoreElimination))) {
+ applyLoadStoreElimination(cUnit, (ArmLIR *) headLIR,
+ (ArmLIR *) tailLIR);
+ }
+ if (!(gDvmJit.disableOpt & (1 << kLoadHoisting))) {
+ applyLoadHoisting(cUnit, (ArmLIR *) headLIR, (ArmLIR *) tailLIR);
+ }
+}
diff --git a/vm/compiler/codegen/arm/README.txt b/vm/compiler/codegen/arm/README.txt
new file mode 100644
index 0000000..a49eef8
--- /dev/null
+++ b/vm/compiler/codegen/arm/README.txt
@@ -0,0 +1,48 @@
+The codegen file for the ARM-based JIT is composed by files broken by
+functionality hierarchies. The goal is to separate architectural dependent
+and independent components to facilitate maintenance and future extension.
+
+For example, the codegen file for armv7-a is assembled by the following
+components:
+
+--
+
+/* Architectural independent building blocks */
+#include "../CodegenCommon.c"
+
+/* Thumb2-specific factory utilities */
+#include "../Thumb2/Factory.c"
+/* Factory utilities dependent on arch-specific features */
+#include "../CodegenFactory.c"
+
+/* Thumb2-specific codegen routines */
+#include "../Thumb2/Gen.c"
+/* Thumb2+VFP codegen routines */
+#include "../FP/Thumb2VFP.c"
+
+/* Thumb2-specific register allocation */
+#include "../Thumb2/Ralloc.c"
+
+/* MIR2LIR dispatcher and architectural independent codegen routines */
+#include "../CodegenDriver.c"
+
+/* Architecture manifest */
+#include "ArchVariant.c"
+
+--
+
+For the Thumb/Thumb2 directories, each contain the followin three files:
+
+- Factory.c (low-level routines for instruction selections)
+- Gen.c (invoke the ISA-specific instruction selection routines)
+- Ralloc.c (arch-dependent register pools)
+
+The FP directory contains FP-specific codegen routines depending on
+Thumb/Thumb2/VFP/PortableFP:
+
+- Thumb2VFP.c
+- ThumbVFP.c
+- ThumbPortableFP.c
+
+In this way the dependency between generic and specific code tied to
+particular architectures can be explicitly represented.
diff --git a/vm/compiler/codegen/arm/Ralloc.h b/vm/compiler/codegen/arm/Ralloc.h
new file mode 100644
index 0000000..cc3e605
--- /dev/null
+++ b/vm/compiler/codegen/arm/Ralloc.h
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file contains register alloction support and is intended to be
+ * included by:
+ *
+ * Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+#include "compiler/CompilerUtility.h"
+#include "compiler/CompilerIR.h"
+#include "compiler/Dataflow.h"
+#include "compiler/codegen/arm/ArmLIR.h"
+
+/*
+ * Return most flexible allowed register class based on size.
+ * Bug: 2813841
+ * Must use a core register for data types narrower than word (due
+ * to possible unaligned load/store.
+ */
+static inline RegisterClass dvmCompilerRegClassBySize(OpSize size)
+{
+ return (size == kUnsignedHalf ||
+ size == kSignedHalf ||
+ size == kUnsignedByte ||
+ size == kSignedByte ) ? kCoreReg : kAnyReg;
+}
+
+static inline int dvmCompilerS2VReg(CompilationUnit *cUnit, int sReg)
+{
+ assert(sReg != INVALID_SREG);
+ return DECODE_REG(dvmConvertSSARegToDalvik(cUnit, sReg));
+}
+
+/* Reset the tracker to unknown state */
+static inline void dvmCompilerResetNullCheck(CompilationUnit *cUnit)
+{
+ dvmClearAllBits(cUnit->regPool->nullCheckedRegs);
+}
+
+/*
+ * Get the "real" sreg number associated with an sReg slot. In general,
+ * sReg values passed through codegen are the SSA names created by
+ * dataflow analysis and refer to slot numbers in the cUnit->regLocation
+ * array. However, renaming is accomplished by simply replacing RegLocation
+ * entries in the cUnit->reglocation[] array. Therefore, when location
+ * records for operands are first created, we need to ask the locRecord
+ * identified by the dataflow pass what it's new name is.
+ */
+
+static inline int dvmCompilerSRegHi(int lowSreg) {
+ return (lowSreg == INVALID_SREG) ? INVALID_SREG : lowSreg + 1;
+}
+
+
+static inline bool dvmCompilerLiveOut(CompilationUnit *cUnit, int sReg)
+{
+ //TODO: fully implement
+ return true;
+}
+
+static inline int dvmCompilerSSASrc(MIR *mir, int num)
+{
+ assert(mir->ssaRep->numUses > num);
+ return mir->ssaRep->uses[num];
+}
+
+extern RegLocation dvmCompilerEvalLoc(CompilationUnit *cUnit, RegLocation loc,
+ int regClass, bool update);
+/* Mark a temp register as dead. Does not affect allocation state. */
+extern void dvmCompilerClobber(CompilationUnit *cUnit, int reg);
+
+extern RegLocation dvmCompilerUpdateLoc(CompilationUnit *cUnit,
+ RegLocation loc);
+
+/* see comments for updateLoc */
+extern RegLocation dvmCompilerUpdateLocWide(CompilationUnit *cUnit,
+ RegLocation loc);
+
+/* Clobber all of the temps that might be used by a handler. */
+extern void dvmCompilerClobberHandlerRegs(CompilationUnit *cUnit);
+
+extern void dvmCompilerMarkLive(CompilationUnit *cUnit, int reg, int sReg);
+
+extern void dvmCompilerMarkDirty(CompilationUnit *cUnit, int reg);
+
+extern void dvmCompilerMarkPair(CompilationUnit *cUnit, int lowReg,
+ int highReg);
+
+extern void dvmCompilerMarkClean(CompilationUnit *cUnit, int reg);
+
+extern void dvmCompilerResetDef(CompilationUnit *cUnit, int reg);
+
+extern void dvmCompilerResetDefLoc(CompilationUnit *cUnit, RegLocation rl);
+
+/* Set up temp & preserved register pools specialized by target */
+extern void dvmCompilerInitPool(RegisterInfo *regs, int *regNums, int num);
+
+/*
+ * Mark the beginning and end LIR of a def sequence. Note that
+ * on entry start points to the LIR prior to the beginning of the
+ * sequence.
+ */
+extern void dvmCompilerMarkDef(CompilationUnit *cUnit, RegLocation rl,
+ LIR *start, LIR *finish);
+/*
+ * Mark the beginning and end LIR of a def sequence. Note that
+ * on entry start points to the LIR prior to the beginning of the
+ * sequence.
+ */
+extern void dvmCompilerMarkDefWide(CompilationUnit *cUnit, RegLocation rl,
+ LIR *start, LIR *finish);
+
+extern RegLocation dvmCompilerGetSrcWide(CompilationUnit *cUnit, MIR *mir,
+ int low, int high);
+
+extern RegLocation dvmCompilerGetDestWide(CompilationUnit *cUnit, MIR *mir,
+ int low, int high);
+// Get the LocRecord associated with an SSA name use.
+extern RegLocation dvmCompilerGetSrc(CompilationUnit *cUnit, MIR *mir, int num);
+
+// Get the LocRecord associated with an SSA name def.
+extern RegLocation dvmCompilerGetDest(CompilationUnit *cUnit, MIR *mir,
+ int num);
+
+extern RegLocation dvmCompilerGetReturnWide(CompilationUnit *cUnit);
+
+/* Clobber all regs that might be used by an external C call */
+extern void dvmCompilerClobberCallRegs(CompilationUnit *cUnit);
+
+extern RegisterInfo *dvmCompilerIsTemp(CompilationUnit *cUnit, int reg);
+
+extern void dvmCompilerMarkInUse(CompilationUnit *cUnit, int reg);
+
+extern int dvmCompilerAllocTemp(CompilationUnit *cUnit);
+
+extern int dvmCompilerAllocTempFloat(CompilationUnit *cUnit);
+
+//REDO: too many assumptions.
+extern int dvmCompilerAllocTempDouble(CompilationUnit *cUnit);
+
+extern void dvmCompilerFreeTemp(CompilationUnit *cUnit, int reg);
+
+extern void dvmCompilerResetDefLocWide(CompilationUnit *cUnit, RegLocation rl);
+
+extern void dvmCompilerResetDefTracking(CompilationUnit *cUnit);
+
+/* Kill the corresponding bit in the null-checked register list */
+extern void dvmCompilerKillNullCheckedLoc(CompilationUnit *cUnit,
+ RegLocation loc);
+
+//FIXME - this needs to also check the preserved pool.
+extern RegisterInfo *dvmCompilerIsLive(CompilationUnit *cUnit, int reg);
+
+/* To be used when explicitly managing register use */
+extern void dvmCompilerLockAllTemps(CompilationUnit *cUnit);
+
+extern void dvmCompilerFlushAllRegs(CompilationUnit *cUnit);
+
+extern RegLocation dvmCompilerGetReturnWideAlt(CompilationUnit *cUnit);
+
+extern RegLocation dvmCompilerGetReturn(CompilationUnit *cUnit);
+
+extern RegLocation dvmCompilerGetReturnAlt(CompilationUnit *cUnit);
+
+/* Clobber any temp associated with an sReg. Could be in either class */
+extern void dvmCompilerClobberSReg(CompilationUnit *cUnit, int sReg);
+
+/* Return a temp if one is available, -1 otherwise */
+extern int dvmCompilerAllocFreeTemp(CompilationUnit *cUnit);
+
+/*
+ * Similar to dvmCompilerAllocTemp(), but forces the allocation of a specific
+ * register. No check is made to see if the register was previously
+ * allocated. Use with caution.
+ */
+extern void dvmCompilerLockTemp(CompilationUnit *cUnit, int reg);
+
+extern RegLocation dvmCompilerWideToNarrow(CompilationUnit *cUnit,
+ RegLocation rl);
+
+/*
+ * Free all allocated temps in the temp pools. Note that this does
+ * not affect the "liveness" of a temp register, which will stay
+ * live until it is either explicitly killed or reallocated.
+ */
+extern void dvmCompilerResetRegPool(CompilationUnit *cUnit);
+
+extern void dvmCompilerClobberAllRegs(CompilationUnit *cUnit);
+
+extern void dvmCompilerResetDefTracking(CompilationUnit *cUnit);
diff --git a/vm/compiler/codegen/arm/RallocUtil.c b/vm/compiler/codegen/arm/RallocUtil.c
new file mode 100644
index 0000000..e917995
--- /dev/null
+++ b/vm/compiler/codegen/arm/RallocUtil.c
@@ -0,0 +1,1010 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file contains register alloction support and is intended to be
+ * included by:
+ *
+ * Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+#include "compiler/CompilerUtility.h"
+#include "compiler/CompilerIR.h"
+#include "compiler/Dataflow.h"
+#include "ArmLIR.h"
+#include "Codegen.h"
+#include "Ralloc.h"
+
+/*
+ * Register usage for 16-bit Thumb systems:
+ * r0-r3: Temp/argument
+ * lr(r14): Temp for translations, return address for handlers
+ * rGLUE(r6): Pointer to InterpState
+ * rFP(r5): Dalvik frame pointer
+ * r4, r7: Temp for translations
+ * r8, r9, r10: Temp preserved across C calls
+ * r11, ip(r12): Temp not preserved across C calls
+ *
+ * Register usage for 32-bit Thumb systems:
+ * r0-r3: Temp/argument
+ * lr(r14): Temp for translations, return address for handlers
+ * rGLUE(r6): Pointer to InterpState
+ * rFP(r5): Dalvik frame pointer
+ * r4, r7: Temp for translations
+ * r8, r9, r10 Temp preserved across C calls
+ * r11, ip(r12): Temp not preserved across C calls
+ * fp0-fp15: Hot temps, not preserved across C calls
+ * fp16-fp31: Promotion pool
+ *
+ */
+
+#define SREG(c, s) ((c)->regLocation[(s)].sRegLow)
+/*
+ * Get the "real" sreg number associated with an sReg slot. In general,
+ * sReg values passed through codegen are the SSA names created by
+ * dataflow analysis and refer to slot numbers in the cUnit->regLocation
+ * array. However, renaming is accomplished by simply replacing RegLocation
+ * entries in the cUnit->reglocation[] array. Therefore, when location
+ * records for operands are first created, we need to ask the locRecord
+ * identified by the dataflow pass what it's new name is.
+ */
+
+/*
+ * Free all allocated temps in the temp pools. Note that this does
+ * not affect the "liveness" of a temp register, which will stay
+ * live until it is either explicitly killed or reallocated.
+ */
+extern void dvmCompilerResetRegPool(CompilationUnit *cUnit)
+{
+ int i;
+ for (i=0; i < cUnit->regPool->numCoreTemps; i++) {
+ cUnit->regPool->coreTemps[i].inUse = false;
+ }
+ for (i=0; i < cUnit->regPool->numFPTemps; i++) {
+ cUnit->regPool->FPTemps[i].inUse = false;
+ }
+}
+
+ /* Set up temp & preserved register pools specialized by target */
+extern void dvmCompilerInitPool(RegisterInfo *regs, int *regNums, int num)
+{
+ int i;
+ for (i=0; i < num; i++) {
+ regs[i].reg = regNums[i];
+ regs[i].inUse = false;
+ regs[i].pair = false;
+ regs[i].live = false;
+ regs[i].dirty = false;
+ regs[i].sReg = INVALID_SREG;
+ }
+}
+
+static void dumpRegPool(RegisterInfo *p, int numRegs)
+{
+ int i;
+ LOGE("================================================");
+ for (i=0; i < numRegs; i++ ){
+ LOGE("R[%d]: U:%d, P:%d, part:%d, LV:%d, D:%d, SR:%d, ST:%x, EN:%x",
+ p[i].reg, p[i].inUse, p[i].pair, p[i].partner, p[i].live,
+ p[i].dirty, p[i].sReg,(int)p[i].defStart, (int)p[i].defEnd);
+ }
+ LOGE("================================================");
+}
+
+static RegisterInfo *getRegInfo(CompilationUnit *cUnit, int reg)
+{
+ int numTemps = cUnit->regPool->numCoreTemps;
+ RegisterInfo *p = cUnit->regPool->coreTemps;
+ int i;
+ for (i=0; i< numTemps; i++) {
+ if (p[i].reg == reg) {
+ return &p[i];
+ }
+ }
+ p = cUnit->regPool->FPTemps;
+ numTemps = cUnit->regPool->numFPTemps;
+ for (i=0; i< numTemps; i++) {
+ if (p[i].reg == reg) {
+ return &p[i];
+ }
+ }
+ LOGE("Tried to get info on a non-existant temp: r%d",reg);
+ dvmCompilerAbort(cUnit);
+ return NULL;
+}
+
+static void flushRegWide(CompilationUnit *cUnit, int reg1, int reg2)
+{
+ RegisterInfo *info1 = getRegInfo(cUnit, reg1);
+ RegisterInfo *info2 = getRegInfo(cUnit, reg2);
+ assert(info1 && info2 && info1->pair && info2->pair &&
+ (info1->partner == info2->reg) &&
+ (info2->partner == info1->reg));
+ if ((info1->live && info1->dirty) || (info2->live && info2->dirty)) {
+ info1->dirty = false;
+ info2->dirty = false;
+ if (dvmCompilerS2VReg(cUnit, info2->sReg) <
+ dvmCompilerS2VReg(cUnit, info1->sReg))
+ info1 = info2;
+ dvmCompilerFlushRegWideImpl(cUnit, rFP,
+ dvmCompilerS2VReg(cUnit, info1->sReg) << 2,
+ info1->reg, info1->partner);
+ }
+}
+
+static void flushReg(CompilationUnit *cUnit, int reg)
+{
+ RegisterInfo *info = getRegInfo(cUnit, reg);
+ if (info->live && info->dirty) {
+ info->dirty = false;
+ dvmCompilerFlushRegImpl(cUnit, rFP,
+ dvmCompilerS2VReg(cUnit, info->sReg) << 2,
+ reg, kWord);
+ }
+}
+
+/* return true if found reg to clobber */
+static bool clobberRegBody(CompilationUnit *cUnit, RegisterInfo *p,
+ int numTemps, int reg)
+{
+ int i;
+ for (i=0; i< numTemps; i++) {
+ if (p[i].reg == reg) {
+ if (p[i].live && p[i].dirty) {
+ if (p[i].pair) {
+ flushRegWide(cUnit, p[i].reg, p[i].partner);
+ } else {
+ flushReg(cUnit, p[i].reg);
+ }
+ }
+ p[i].live = false;
+ p[i].sReg = INVALID_SREG;
+ p[i].defStart = NULL;
+ p[i].defEnd = NULL;
+ if (p[i].pair) {
+ p[i].pair = false;
+ /* partners should be in same pool */
+ clobberRegBody(cUnit, p, numTemps, p[i].partner);
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+/* Mark a temp register as dead. Does not affect allocation state. */
+void dvmCompilerClobber(CompilationUnit *cUnit, int reg)
+{
+ if (!clobberRegBody(cUnit, cUnit->regPool->coreTemps,
+ cUnit->regPool->numCoreTemps, reg)) {
+ clobberRegBody(cUnit, cUnit->regPool->FPTemps,
+ cUnit->regPool->numFPTemps, reg);
+ }
+}
+
+static void clobberSRegBody(RegisterInfo *p, int numTemps, int sReg)
+{
+ int i;
+ for (i=0; i< numTemps; i++) {
+ if (p[i].sReg == sReg) {
+ p[i].live = false;
+ p[i].defStart = NULL;
+ p[i].defEnd = NULL;
+ }
+ }
+}
+
+/* Clobber any temp associated with an sReg. Could be in either class */
+extern void dvmCompilerClobberSReg(CompilationUnit *cUnit, int sReg)
+{
+ clobberSRegBody(cUnit->regPool->coreTemps, cUnit->regPool->numCoreTemps,
+ sReg);
+ clobberSRegBody(cUnit->regPool->FPTemps, cUnit->regPool->numFPTemps,
+ sReg);
+}
+
+static int allocTempBody(CompilationUnit *cUnit, RegisterInfo *p, int numTemps,
+ int *nextTemp, bool required)
+{
+ int i;
+ int next = *nextTemp;
+ for (i=0; i< numTemps; i++) {
+ if (next >= numTemps)
+ next = 0;
+ if (!p[next].inUse && !p[next].live) {
+ dvmCompilerClobber(cUnit, p[next].reg);
+ p[next].inUse = true;
+ p[next].pair = false;
+ *nextTemp = next + 1;
+ return p[next].reg;
+ }
+ next++;
+ }
+ next = *nextTemp;
+ for (i=0; i< numTemps; i++) {
+ if (next >= numTemps)
+ next = 0;
+ if (!p[next].inUse) {
+ dvmCompilerClobber(cUnit, p[next].reg);
+ p[next].inUse = true;
+ p[next].pair = false;
+ *nextTemp = next + 1;
+ return p[next].reg;
+ }
+ next++;
+ }
+ if (required) {
+ LOGE("No free temp registers");
+ dvmCompilerAbort(cUnit);
+ }
+ return -1; // No register available
+}
+
+//REDO: too many assumptions.
+extern int dvmCompilerAllocTempDouble(CompilationUnit *cUnit)
+{
+ RegisterInfo *p = cUnit->regPool->FPTemps;
+ int numTemps = cUnit->regPool->numFPTemps;
+ int next = cUnit->regPool->nextFPTemp;
+ int i;
+
+ for (i=0; i < numTemps; i+=2) {
+ /* Cleanup - not all targets need aligned regs */
+ if (next & 1)
+ next++;
+ if (next >= numTemps)
+ next = 0;
+ if ((!p[next].inUse && !p[next].live) &&
+ (!p[next+1].inUse && !p[next+1].live)) {
+ dvmCompilerClobber(cUnit, p[next].reg);
+ dvmCompilerClobber(cUnit, p[next+1].reg);
+ p[next].inUse = true;
+ p[next+1].inUse = true;
+ assert((p[next].reg+1) == p[next+1].reg);
+ assert((p[next].reg & 0x1) == 0);
+ cUnit->regPool->nextFPTemp += 2;
+ return p[next].reg;
+ }
+ next += 2;
+ }
+ next = cUnit->regPool->nextFPTemp;
+ for (i=0; i < numTemps; i+=2) {
+ if (next >= numTemps)
+ next = 0;
+ if (!p[next].inUse && !p[next+1].inUse) {
+ dvmCompilerClobber(cUnit, p[next].reg);
+ dvmCompilerClobber(cUnit, p[next+1].reg);
+ p[next].inUse = true;
+ p[next+1].inUse = true;
+ assert((p[next].reg+1) == p[next+1].reg);
+ assert((p[next].reg & 0x1) == 0);
+ cUnit->regPool->nextFPTemp += 2;
+ return p[next].reg;
+ }
+ next += 2;
+ }
+ LOGE("No free temp registers");
+ dvmCompilerAbort(cUnit);
+ return -1;
+}
+
+/* Return a temp if one is available, -1 otherwise */
+extern int dvmCompilerAllocFreeTemp(CompilationUnit *cUnit)
+{
+ return allocTempBody(cUnit, cUnit->regPool->coreTemps,
+ cUnit->regPool->numCoreTemps,
+ &cUnit->regPool->nextCoreTemp, true);
+}
+
+extern int dvmCompilerAllocTemp(CompilationUnit *cUnit)
+{
+ return allocTempBody(cUnit, cUnit->regPool->coreTemps,
+ cUnit->regPool->numCoreTemps,
+ &cUnit->regPool->nextCoreTemp, true);
+}
+
+extern int dvmCompilerAllocTempFloat(CompilationUnit *cUnit)
+{
+ return allocTempBody(cUnit, cUnit->regPool->FPTemps,
+ cUnit->regPool->numFPTemps,
+ &cUnit->regPool->nextFPTemp, true);
+}
+
+static RegisterInfo *allocLiveBody(RegisterInfo *p, int numTemps, int sReg)
+{
+ int i;
+ if (sReg == -1)
+ return NULL;
+ for (i=0; i < numTemps; i++) {
+ if (p[i].live && (p[i].sReg == sReg)) {
+ p[i].inUse = true;
+ return &p[i];
+ }
+ }
+ return NULL;
+}
+
+static RegisterInfo *allocLive(CompilationUnit *cUnit, int sReg,
+ int regClass)
+{
+ RegisterInfo *res = NULL;
+ switch(regClass) {
+ case kAnyReg:
+ res = allocLiveBody(cUnit->regPool->FPTemps,
+ cUnit->regPool->numFPTemps, sReg);
+ if (res)
+ break;
+ /* Intentional fallthrough */
+ case kCoreReg:
+ res = allocLiveBody(cUnit->regPool->coreTemps,
+ cUnit->regPool->numCoreTemps, sReg);
+ break;
+ case kFPReg:
+ res = allocLiveBody(cUnit->regPool->FPTemps,
+ cUnit->regPool->numFPTemps, sReg);
+ break;
+ default:
+ LOGE("Invalid register type");
+ dvmCompilerAbort(cUnit);
+ }
+ return res;
+}
+
+extern void dvmCompilerFreeTemp(CompilationUnit *cUnit, int reg)
+{
+ RegisterInfo *p = cUnit->regPool->coreTemps;
+ int numTemps = cUnit->regPool->numCoreTemps;
+ int i;
+ for (i=0; i< numTemps; i++) {
+ if (p[i].reg == reg) {
+ p[i].inUse = false;
+ p[i].pair = false;
+ return;
+ }
+ }
+ p = cUnit->regPool->FPTemps;
+ numTemps = cUnit->regPool->numFPTemps;
+ for (i=0; i< numTemps; i++) {
+ if (p[i].reg == reg) {
+ p[i].inUse = false;
+ p[i].pair = false;
+ return;
+ }
+ }
+ LOGE("Tried to free a non-existant temp: r%d",reg);
+ dvmCompilerAbort(cUnit);
+}
+
+/*
+ * FIXME - this needs to also check the preserved pool once we start
+ * start using preserved registers.
+ */
+extern RegisterInfo *dvmCompilerIsLive(CompilationUnit *cUnit, int reg)
+{
+ RegisterInfo *p = cUnit->regPool->coreTemps;
+ int numTemps = cUnit->regPool->numCoreTemps;
+ int i;
+ for (i=0; i< numTemps; i++) {
+ if (p[i].reg == reg) {
+ return p[i].live ? &p[i] : NULL;
+ }
+ }
+ p = cUnit->regPool->FPTemps;
+ numTemps = cUnit->regPool->numFPTemps;
+ for (i=0; i< numTemps; i++) {
+ if (p[i].reg == reg) {
+ return p[i].live ? &p[i] : NULL;
+ }
+ }
+ return NULL;
+}
+
+extern RegisterInfo *dvmCompilerIsTemp(CompilationUnit *cUnit, int reg)
+{
+ RegisterInfo *p = cUnit->regPool->coreTemps;
+ int numTemps = cUnit->regPool->numCoreTemps;
+ int i;
+ for (i=0; i< numTemps; i++) {
+ if (p[i].reg == reg) {
+ return &p[i];
+ }
+ }
+ p = cUnit->regPool->FPTemps;
+ numTemps = cUnit->regPool->numFPTemps;
+ for (i=0; i< numTemps; i++) {
+ if (p[i].reg == reg) {
+ return &p[i];
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Similar to dvmCompilerAllocTemp(), but forces the allocation of a specific
+ * register. No check is made to see if the register was previously
+ * allocated. Use with caution.
+ */
+extern void dvmCompilerLockTemp(CompilationUnit *cUnit, int reg)
+{
+ RegisterInfo *p = cUnit->regPool->coreTemps;
+ int numTemps = cUnit->regPool->numCoreTemps;
+ int i;
+ for (i=0; i< numTemps; i++) {
+ if (p[i].reg == reg) {
+ p[i].inUse = true;
+ p[i].live = false;
+ return;
+ }
+ }
+ p = cUnit->regPool->FPTemps;
+ numTemps = cUnit->regPool->numFPTemps;
+ for (i=0; i< numTemps; i++) {
+ if (p[i].reg == reg) {
+ p[i].inUse = true;
+ p[i].live = false;
+ return;
+ }
+ }
+ LOGE("Tried to lock a non-existant temp: r%d",reg);
+ dvmCompilerAbort(cUnit);
+}
+
+/* Clobber all regs that might be used by an external C call */
+extern void dvmCompilerClobberCallRegs(CompilationUnit *cUnit)
+{
+ dvmCompilerClobber(cUnit, r0);
+ dvmCompilerClobber(cUnit, r1);
+ dvmCompilerClobber(cUnit, r2);
+ dvmCompilerClobber(cUnit, r3);
+ dvmCompilerClobber(cUnit, r9); // Need to do this?, be conservative
+ dvmCompilerClobber(cUnit, r11);
+ dvmCompilerClobber(cUnit, r12);
+ dvmCompilerClobber(cUnit, rlr);
+}
+
+/* Clobber all of the temps that might be used by a handler. */
+extern void dvmCompilerClobberHandlerRegs(CompilationUnit *cUnit)
+{
+ //TUNING: reduce the set of regs used by handlers. Only a few need lots.
+ dvmCompilerClobberCallRegs(cUnit);
+ dvmCompilerClobber(cUnit, r4PC);
+ dvmCompilerClobber(cUnit, r7);
+ dvmCompilerClobber(cUnit, r8);
+ dvmCompilerClobber(cUnit, r9);
+ dvmCompilerClobber(cUnit, r10);
+}
+
+extern void dvmCompilerResetDef(CompilationUnit *cUnit, int reg)
+{
+ RegisterInfo *p = getRegInfo(cUnit, reg);
+ p->defStart = NULL;
+ p->defEnd = NULL;
+}
+
+static void nullifyRange(CompilationUnit *cUnit, LIR *start, LIR *finish,
+ int sReg1, int sReg2)
+{
+ if (start && finish) {
+ LIR *p;
+ assert(sReg1 == sReg2);
+ for (p = start; ;p = p->next) {
+ ((ArmLIR *)p)->isNop = true;
+ if (p == finish)
+ break;
+ }
+ }
+}
+
+/*
+ * Mark the beginning and end LIR of a def sequence. Note that
+ * on entry start points to the LIR prior to the beginning of the
+ * sequence.
+ */
+extern void dvmCompilerMarkDef(CompilationUnit *cUnit, RegLocation rl,
+ LIR *start, LIR *finish)
+{
+ assert(!rl.wide);
+ assert(start && start->next);
+ assert(finish);
+ RegisterInfo *p = getRegInfo(cUnit, rl.lowReg);
+ p->defStart = start->next;
+ p->defEnd = finish;
+}
+
+/*
+ * Mark the beginning and end LIR of a def sequence. Note that
+ * on entry start points to the LIR prior to the beginning of the
+ * sequence.
+ */
+extern void dvmCompilerMarkDefWide(CompilationUnit *cUnit, RegLocation rl,
+ LIR *start, LIR *finish)
+{
+ assert(rl.wide);
+ assert(start && start->next);
+ assert(finish);
+ RegisterInfo *p = getRegInfo(cUnit, rl.lowReg);
+ dvmCompilerResetDef(cUnit, rl.highReg); // Only track low of pair
+ p->defStart = start->next;
+ p->defEnd = finish;
+}
+
+extern RegLocation dvmCompilerWideToNarrow(CompilationUnit *cUnit,
+ RegLocation rl)
+{
+ assert(rl.wide);
+ if (rl.location == kLocPhysReg) {
+ RegisterInfo *infoLo = getRegInfo(cUnit, rl.lowReg);
+ RegisterInfo *infoHi = getRegInfo(cUnit, rl.highReg);
+ if (!infoLo->pair) {
+ dumpRegPool(cUnit->regPool->coreTemps,
+ cUnit->regPool->numCoreTemps);
+ assert(infoLo->pair);
+ }
+ if (!infoHi->pair) {
+ dumpRegPool(cUnit->regPool->coreTemps,
+ cUnit->regPool->numCoreTemps);
+ assert(infoHi->pair);
+ }
+ assert(infoLo->pair);
+ assert(infoHi->pair);
+ assert(infoLo->partner == infoHi->reg);
+ assert(infoHi->partner == infoLo->reg);
+ infoLo->pair = false;
+ infoHi->pair = false;
+ infoLo->defStart = NULL;
+ infoLo->defEnd = NULL;
+ infoHi->defStart = NULL;
+ infoHi->defEnd = NULL;
+ }
+ rl.wide = false;
+ return rl;
+}
+
+extern void dvmCompilerResetDefLoc(CompilationUnit *cUnit, RegLocation rl)
+{
+ assert(!rl.wide);
+ if (!(gDvmJit.disableOpt & (1 << kSuppressLoads))) {
+ RegisterInfo *p = getRegInfo(cUnit, rl.lowReg);
+ assert(!p->pair);
+ nullifyRange(cUnit, p->defStart, p->defEnd,
+ p->sReg, rl.sRegLow);
+ }
+ dvmCompilerResetDef(cUnit, rl.lowReg);
+}
+
+extern void dvmCompilerResetDefLocWide(CompilationUnit *cUnit, RegLocation rl)
+{
+ assert(rl.wide);
+ if (!(gDvmJit.disableOpt & (1 << kSuppressLoads))) {
+ RegisterInfo *p = getRegInfo(cUnit, rl.lowReg);
+ assert(p->pair);
+ nullifyRange(cUnit, p->defStart, p->defEnd,
+ p->sReg, rl.sRegLow);
+ }
+ dvmCompilerResetDef(cUnit, rl.lowReg);
+ dvmCompilerResetDef(cUnit, rl.highReg);
+}
+
+extern void dvmCompilerResetDefTracking(CompilationUnit *cUnit)
+{
+ int i;
+ for (i=0; i< cUnit->regPool->numCoreTemps; i++) {
+ dvmCompilerResetDef(cUnit, cUnit->regPool->coreTemps[i].reg);
+ }
+ for (i=0; i< cUnit->regPool->numFPTemps; i++) {
+ dvmCompilerResetDef(cUnit, cUnit->regPool->FPTemps[i].reg);
+ }
+}
+
+extern void dvmCompilerClobberAllRegs(CompilationUnit *cUnit)
+{
+ int i;
+ for (i=0; i< cUnit->regPool->numCoreTemps; i++) {
+ dvmCompilerClobber(cUnit, cUnit->regPool->coreTemps[i].reg);
+ }
+ for (i=0; i< cUnit->regPool->numFPTemps; i++) {
+ dvmCompilerClobber(cUnit, cUnit->regPool->FPTemps[i].reg);
+ }
+}
+
+/* To be used when explicitly managing register use */
+extern void dvmCompilerLockAllTemps(CompilationUnit *cUnit)
+{
+ int i;
+ for (i=0; i< cUnit->regPool->numCoreTemps; i++) {
+ dvmCompilerLockTemp(cUnit, cUnit->regPool->coreTemps[i].reg);
+ }
+}
+
+// Make sure nothing is live and dirty
+static void flushAllRegsBody(CompilationUnit *cUnit, RegisterInfo *info,
+ int numRegs)
+{
+ int i;
+ for (i=0; i < numRegs; i++) {
+ if (info[i].live && info[i].dirty) {
+ if (info[i].pair) {
+ flushRegWide(cUnit, info[i].reg, info[i].partner);
+ } else {
+ flushReg(cUnit, info[i].reg);
+ }
+ }
+ }
+}
+
+extern void dvmCompilerFlushAllRegs(CompilationUnit *cUnit)
+{
+ flushAllRegsBody(cUnit, cUnit->regPool->coreTemps,
+ cUnit->regPool->numCoreTemps);
+ flushAllRegsBody(cUnit, cUnit->regPool->FPTemps,
+ cUnit->regPool->numFPTemps);
+ dvmCompilerClobberAllRegs(cUnit);
+}
+
+
+//TUNING: rewrite all of this reg stuff. Probably use an attribute table
+static bool regClassMatches(int regClass, int reg)
+{
+ if (regClass == kAnyReg) {
+ return true;
+ } else if (regClass == kCoreReg) {
+ return !FPREG(reg);
+ } else {
+ return FPREG(reg);
+ }
+}
+
+extern void dvmCompilerMarkLive(CompilationUnit *cUnit, int reg, int sReg)
+{
+ RegisterInfo *info = getRegInfo(cUnit, reg);
+ if ((info->reg == reg) && (info->sReg == sReg) && info->live) {
+ return; /* already live */
+ } else if (sReg != INVALID_SREG) {
+ dvmCompilerClobberSReg(cUnit, sReg);
+ info->live = true;
+ } else {
+ /* Can't be live if no associated sReg */
+ info->live = false;
+ }
+ info->sReg = sReg;
+}
+
+extern void dvmCompilerMarkPair(CompilationUnit *cUnit, int lowReg, int highReg)
+{
+ RegisterInfo *infoLo = getRegInfo(cUnit, lowReg);
+ RegisterInfo *infoHi = getRegInfo(cUnit, highReg);
+ infoLo->pair = infoHi->pair = true;
+ infoLo->partner = highReg;
+ infoHi->partner = lowReg;
+}
+
+extern void dvmCompilerMarkClean(CompilationUnit *cUnit, int reg)
+{
+ RegisterInfo *info = getRegInfo(cUnit, reg);
+ info->dirty = false;
+}
+
+extern void dvmCompilerMarkDirty(CompilationUnit *cUnit, int reg)
+{
+ RegisterInfo *info = getRegInfo(cUnit, reg);
+ info->dirty = true;
+}
+
+extern void dvmCompilerMarkInUse(CompilationUnit *cUnit, int reg)
+{
+ RegisterInfo *info = getRegInfo(cUnit, reg);
+ info->inUse = true;
+}
+
+void copyRegInfo(CompilationUnit *cUnit, int newReg, int oldReg)
+{
+ RegisterInfo *newInfo = getRegInfo(cUnit, newReg);
+ RegisterInfo *oldInfo = getRegInfo(cUnit, oldReg);
+ *newInfo = *oldInfo;
+ newInfo->reg = newReg;
+}
+
+/*
+ * Return an updated location record with current in-register status.
+ * If the value lives in live temps, reflect that fact. No code
+ * is generated. The the live value is part of an older pair,
+ * clobber both low and high.
+ * TUNING: clobbering both is a bit heavy-handed, but the alternative
+ * is a bit complex when dealing with FP regs. Examine code to see
+ * if it's worthwhile trying to be more clever here.
+ */
+extern RegLocation dvmCompilerUpdateLoc(CompilationUnit *cUnit, RegLocation loc)
+{
+ assert(!loc.wide);
+ if (loc.location == kLocDalvikFrame) {
+ RegisterInfo *infoLo = allocLive(cUnit, loc.sRegLow, kAnyReg);
+ if (infoLo) {
+ if (infoLo->pair) {
+ dvmCompilerClobber(cUnit, infoLo->reg);
+ dvmCompilerClobber(cUnit, infoLo->partner);
+ } else {
+ loc.lowReg = infoLo->reg;
+ loc.location = kLocPhysReg;
+ }
+ }
+ }
+
+ return loc;
+}
+
+/* see comments for updateLoc */
+extern RegLocation dvmCompilerUpdateLocWide(CompilationUnit *cUnit,
+ RegLocation loc)
+{
+ assert(loc.wide);
+ if (loc.location == kLocDalvikFrame) {
+ // Are the dalvik regs already live in physical registers?
+ RegisterInfo *infoLo = allocLive(cUnit, loc.sRegLow, kAnyReg);
+ RegisterInfo *infoHi = allocLive(cUnit,
+ dvmCompilerSRegHi(loc.sRegLow), kAnyReg);
+ bool match = true;
+ match = match && (infoLo != NULL);
+ match = match && (infoHi != NULL);
+ // Are they both core or both FP?
+ match = match && (FPREG(infoLo->reg) == FPREG(infoHi->reg));
+ // If a pair of floating point singles, are they properly aligned?
+ if (match && FPREG(infoLo->reg)) {
+ match &= ((infoLo->reg & 0x1) == 0);
+ match &= ((infoHi->reg - infoLo->reg) == 1);
+ }
+ // If previously used as a pair, it is the same pair?
+ if (match && (infoLo->pair || infoHi->pair)) {
+ match = (infoLo->pair == infoHi->pair);
+ match &= ((infoLo->reg == infoHi->partner) &&
+ (infoHi->reg == infoLo->partner));
+ }
+ if (match) {
+ // Can reuse - update the register usage info
+ loc.lowReg = infoLo->reg;
+ loc.highReg = infoHi->reg;
+ loc.location = kLocPhysReg;
+ dvmCompilerMarkPair(cUnit, loc.lowReg, loc.highReg);
+ assert(!FPREG(loc.lowReg) || ((loc.lowReg & 0x1) == 0));
+ return loc;
+ }
+ // Can't easily reuse - clobber any overlaps
+ if (infoLo) {
+ dvmCompilerClobber(cUnit, infoLo->reg);
+ if (infoLo->pair)
+ dvmCompilerClobber(cUnit, infoLo->partner);
+ }
+ if (infoHi) {
+ dvmCompilerClobber(cUnit, infoHi->reg);
+ if (infoHi->pair)
+ dvmCompilerClobber(cUnit, infoHi->partner);
+ }
+ }
+
+ return loc;
+}
+
+static RegLocation evalLocWide(CompilationUnit *cUnit, RegLocation loc,
+ int regClass, bool update)
+{
+ assert(loc.wide);
+ int newRegs;
+ int lowReg;
+ int highReg;
+
+ loc = dvmCompilerUpdateLocWide(cUnit, loc);
+
+ /* If already in registers, we can assume proper form. Right reg class? */
+ if (loc.location == kLocPhysReg) {
+ assert(FPREG(loc.lowReg) == FPREG(loc.highReg));
+ assert(!FPREG(loc.lowReg) || ((loc.lowReg & 0x1) == 0));
+ if (!regClassMatches(regClass, loc.lowReg)) {
+ /* Wrong register class. Reallocate and copy */
+ newRegs = dvmCompilerAllocTypedTempPair(cUnit, loc.fp, regClass);
+ lowReg = newRegs & 0xff;
+ highReg = (newRegs >> 8) & 0xff;
+ dvmCompilerRegCopyWide(cUnit, lowReg, highReg, loc.lowReg,
+ loc.highReg);
+ copyRegInfo(cUnit, lowReg, loc.lowReg);
+ copyRegInfo(cUnit, highReg, loc.highReg);
+ dvmCompilerClobber(cUnit, loc.lowReg);
+ dvmCompilerClobber(cUnit, loc.highReg);
+ loc.lowReg = lowReg;
+ loc.highReg = highReg;
+ dvmCompilerMarkPair(cUnit, loc.lowReg, loc.highReg);
+ assert(!FPREG(loc.lowReg) || ((loc.lowReg & 0x1) == 0));
+ }
+ return loc;
+ }
+
+ assert((loc.location != kLocRetval) || (loc.sRegLow == INVALID_SREG));
+ assert((loc.location != kLocRetval) ||
+ (dvmCompilerSRegHi(loc.sRegLow) == INVALID_SREG));
+
+ newRegs = dvmCompilerAllocTypedTempPair(cUnit, loc.fp, regClass);
+ loc.lowReg = newRegs & 0xff;
+ loc.highReg = (newRegs >> 8) & 0xff;
+
+ dvmCompilerMarkPair(cUnit, loc.lowReg, loc.highReg);
+ if (update) {
+ loc.location = kLocPhysReg;
+ dvmCompilerMarkLive(cUnit, loc.lowReg, loc.sRegLow);
+ dvmCompilerMarkLive(cUnit, loc.highReg, dvmCompilerSRegHi(loc.sRegLow));
+ }
+ assert(!FPREG(loc.lowReg) || ((loc.lowReg & 0x1) == 0));
+ return loc;
+}
+
+extern RegLocation dvmCompilerEvalLoc(CompilationUnit *cUnit, RegLocation loc,
+ int regClass, bool update)
+{
+ int newReg;
+ if (loc.wide)
+ return evalLocWide(cUnit, loc, regClass, update);
+ loc = dvmCompilerUpdateLoc(cUnit, loc);
+
+ if (loc.location == kLocPhysReg) {
+ if (!regClassMatches(regClass, loc.lowReg)) {
+ /* Wrong register class. Realloc, copy and transfer ownership */
+ newReg = dvmCompilerAllocTypedTemp(cUnit, loc.fp, regClass);
+ dvmCompilerRegCopy(cUnit, newReg, loc.lowReg);
+ copyRegInfo(cUnit, newReg, loc.lowReg);
+ dvmCompilerClobber(cUnit, loc.lowReg);
+ loc.lowReg = newReg;
+ }
+ return loc;
+ }
+
+ assert((loc.location != kLocRetval) || (loc.sRegLow == INVALID_SREG));
+
+ newReg = dvmCompilerAllocTypedTemp(cUnit, loc.fp, regClass);
+ loc.lowReg = newReg;
+
+ if (update) {
+ loc.location = kLocPhysReg;
+ dvmCompilerMarkLive(cUnit, loc.lowReg, loc.sRegLow);
+ }
+ return loc;
+}
+
+static inline int getDestSSAName(MIR *mir, int num)
+{
+ assert(mir->ssaRep->numDefs > num);
+ return mir->ssaRep->defs[num];
+}
+
+// Get the LocRecord associated with an SSA name use.
+extern RegLocation dvmCompilerGetSrc(CompilationUnit *cUnit, MIR *mir, int num)
+{
+ RegLocation loc = cUnit->regLocation[
+ SREG(cUnit, dvmCompilerSSASrc(mir, num))];
+ loc.fp = cUnit->regLocation[dvmCompilerSSASrc(mir, num)].fp;
+ loc.wide = false;
+ return loc;
+}
+
+// Get the LocRecord associated with an SSA name def.
+extern RegLocation dvmCompilerGetDest(CompilationUnit *cUnit, MIR *mir,
+ int num)
+{
+ RegLocation loc = cUnit->regLocation[SREG(cUnit, getDestSSAName(mir, num))];
+ loc.fp = cUnit->regLocation[getDestSSAName(mir, num)].fp;
+ loc.wide = false;
+ return loc;
+}
+
+static RegLocation getLocWide(CompilationUnit *cUnit, MIR *mir,
+ int low, int high, bool isSrc)
+{
+ RegLocation lowLoc;
+ RegLocation highLoc;
+ /* Copy loc record for low word and patch in data from high word */
+ if (isSrc) {
+ lowLoc = dvmCompilerGetSrc(cUnit, mir, low);
+ highLoc = dvmCompilerGetSrc(cUnit, mir, high);
+ } else {
+ lowLoc = dvmCompilerGetDest(cUnit, mir, low);
+ highLoc = dvmCompilerGetDest(cUnit, mir, high);
+ }
+ /* Avoid this case by either promoting both or neither. */
+ assert(lowLoc.location == highLoc.location);
+ if (lowLoc.location == kLocPhysReg) {
+ /* This case shouldn't happen if we've named correctly */
+ assert(lowLoc.fp == highLoc.fp);
+ }
+ lowLoc.wide = true;
+ lowLoc.highReg = highLoc.lowReg;
+ return lowLoc;
+}
+
+extern RegLocation dvmCompilerGetDestWide(CompilationUnit *cUnit, MIR *mir,
+ int low, int high)
+{
+ return getLocWide(cUnit, mir, low, high, false);
+}
+
+extern RegLocation dvmCompilerGetSrcWide(CompilationUnit *cUnit, MIR *mir,
+ int low, int high)
+{
+ return getLocWide(cUnit, mir, low, high, true);
+}
+
+extern RegLocation dvmCompilerGetReturnWide(CompilationUnit *cUnit)
+{
+ RegLocation res = LOC_C_RETURN_WIDE;
+ dvmCompilerClobber(cUnit, r0);
+ dvmCompilerClobber(cUnit, r1);
+ dvmCompilerMarkInUse(cUnit, r0);
+ dvmCompilerMarkInUse(cUnit, r1);
+ dvmCompilerMarkPair(cUnit, res.lowReg, res.highReg);
+ return res;
+}
+
+extern RegLocation dvmCompilerGetReturnWideAlt(CompilationUnit *cUnit)
+{
+ RegLocation res = LOC_C_RETURN_WIDE;
+ res.lowReg = r2;
+ res.highReg = r3;
+ dvmCompilerClobber(cUnit, r2);
+ dvmCompilerClobber(cUnit, r3);
+ dvmCompilerMarkInUse(cUnit, r2);
+ dvmCompilerMarkInUse(cUnit, r3);
+ dvmCompilerMarkPair(cUnit, res.lowReg, res.highReg);
+ return res;
+}
+
+extern RegLocation dvmCompilerGetReturn(CompilationUnit *cUnit)
+{
+ RegLocation res = LOC_C_RETURN;
+ dvmCompilerClobber(cUnit, r0);
+ dvmCompilerMarkInUse(cUnit, r0);
+ return res;
+}
+
+extern RegLocation dvmCompilerGetReturnAlt(CompilationUnit *cUnit)
+{
+ RegLocation res = LOC_C_RETURN;
+ res.lowReg = r1;
+ dvmCompilerClobber(cUnit, r1);
+ dvmCompilerMarkInUse(cUnit, r1);
+ return res;
+}
+
+/* Kill the corresponding bit in the null-checked register list */
+extern void dvmCompilerKillNullCheckedLoc(CompilationUnit *cUnit,
+ RegLocation loc)
+{
+ if (loc.location != kLocRetval) {
+ assert(loc.sRegLow != INVALID_SREG);
+ dvmClearBit(cUnit->regPool->nullCheckedRegs, loc.sRegLow);
+ if (loc.wide) {
+ assert(dvmCompilerSRegHi(loc.sRegLow) != INVALID_SREG);
+ dvmClearBit(cUnit->regPool->nullCheckedRegs,
+ dvmCompilerSRegHi(loc.sRegLow));
+ }
+ }
+}
+
+extern void dvmCompilerFlushRegWideForV5TEVFP(CompilationUnit *cUnit,
+ int reg1, int reg2)
+{
+ flushRegWide(cUnit, reg1, reg2);
+}
+
+extern void dvmCompilerFlushRegForV5TEVFP(CompilationUnit *cUnit, int reg)
+{
+ flushReg(cUnit, reg);
+}
diff --git a/vm/compiler/codegen/arm/Thumb/Factory.c b/vm/compiler/codegen/arm/Thumb/Factory.c
new file mode 100644
index 0000000..85f612e
--- /dev/null
+++ b/vm/compiler/codegen/arm/Thumb/Factory.c
@@ -0,0 +1,879 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file contains codegen for the Thumb ISA and is intended to be
+ * includes by:
+ *
+ * Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+static int coreTemps[] = {r0, r1, r2, r3, r4PC, r7};
+
+static void storePair(CompilationUnit *cUnit, int base, int lowReg,
+ int highReg);
+static void loadPair(CompilationUnit *cUnit, int base, int lowReg, int highReg);
+static ArmLIR *loadWordDisp(CompilationUnit *cUnit, int rBase, int displacement,
+ int rDest);
+static ArmLIR *storeWordDisp(CompilationUnit *cUnit, int rBase,
+ int displacement, int rSrc);
+static ArmLIR *genRegRegCheck(CompilationUnit *cUnit,
+ ArmConditionCode cond,
+ int reg1, int reg2, int dOffset,
+ ArmLIR *pcrLabel);
+
+
+/*
+ * Load a immediate using a shortcut if possible; otherwise
+ * grab from the per-translation literal pool. If target is
+ * a high register, build constant into a low register and copy.
+ *
+ * No additional register clobbering operation performed. Use this version when
+ * 1) rDest is freshly returned from dvmCompilerAllocTemp or
+ * 2) The codegen is under fixed register usage
+ */
+static ArmLIR *loadConstantNoClobber(CompilationUnit *cUnit, int rDest,
+ int value)
+{
+ ArmLIR *res;
+ int tDest = LOWREG(rDest) ? rDest : dvmCompilerAllocTemp(cUnit);
+ /* See if the value can be constructed cheaply */
+ if ((value >= 0) && (value <= 255)) {
+ res = newLIR2(cUnit, kThumbMovImm, tDest, value);
+ if (rDest != tDest) {
+ opRegReg(cUnit, kOpMov, rDest, tDest);
+ dvmCompilerFreeTemp(cUnit, tDest);
+ }
+ return res;
+ } else if ((value & 0xFFFFFF00) == 0xFFFFFF00) {
+ res = newLIR2(cUnit, kThumbMovImm, tDest, ~value);
+ newLIR2(cUnit, kThumbMvn, tDest, tDest);
+ if (rDest != tDest) {
+ opRegReg(cUnit, kOpMov, rDest, tDest);
+ dvmCompilerFreeTemp(cUnit, tDest);
+ }
+ return res;
+ }
+ /* No shortcut - go ahead and use literal pool */
+ ArmLIR *dataTarget = scanLiteralPool(cUnit, value, 255);
+ if (dataTarget == NULL) {
+ dataTarget = addWordData(cUnit, value, false);
+ }
+ ArmLIR *loadPcRel = dvmCompilerNew(sizeof(ArmLIR), true);
+ loadPcRel->opCode = kThumbLdrPcRel;
+ loadPcRel->generic.target = (LIR *) dataTarget;
+ loadPcRel->operands[0] = tDest;
+ setupResourceMasks(loadPcRel);
+ /*
+ * Special case for literal loads with a link register target.
+ * Self-cosim mode will insert calls prior to heap references
+ * after optimization, and those will destroy r14. The easy
+ * workaround is to treat literal loads into r14 as heap references
+ * to prevent them from being hoisted. Use of r14 in this manner
+ * is currently rare. Revist if that changes.
+ */
+ if (rDest != rlr)
+ setMemRefType(loadPcRel, true, kLiteral);
+ loadPcRel->aliasInfo = dataTarget->operands[0];
+ res = loadPcRel;
+ dvmCompilerAppendLIR(cUnit, (LIR *) loadPcRel);
+
+ /*
+ * To save space in the constant pool, we use the ADD_RRI8 instruction to
+ * add up to 255 to an existing constant value.
+ */
+ if (dataTarget->operands[0] != value) {
+ newLIR2(cUnit, kThumbAddRI8, tDest, value - dataTarget->operands[0]);
+ }
+ if (rDest != tDest) {
+ opRegReg(cUnit, kOpMov, rDest, tDest);
+ dvmCompilerFreeTemp(cUnit, tDest);
+ }
+ return res;
+}
+
+/*
+ * Load an immediate value into a fixed or temp register. Target
+ * register is clobbered, and marked inUse.
+ */
+static ArmLIR *loadConstant(CompilationUnit *cUnit, int rDest, int value)
+{
+ if (dvmCompilerIsTemp(cUnit, rDest)) {
+ dvmCompilerClobber(cUnit, rDest);
+ dvmCompilerMarkInUse(cUnit, rDest);
+ }
+ return loadConstantNoClobber(cUnit, rDest, value);
+}
+
+static ArmLIR *opNone(CompilationUnit *cUnit, OpKind op)
+{
+ ArmOpCode opCode = kThumbBkpt;
+ switch (op) {
+ case kOpUncondBr:
+ opCode = kThumbBUncond;
+ break;
+ default:
+ LOGE("Jit: bad case in opNone");
+ dvmCompilerAbort(cUnit);
+ }
+ return newLIR0(cUnit, opCode);
+}
+
+static ArmLIR *opCondBranch(CompilationUnit *cUnit, ArmConditionCode cc)
+{
+ return newLIR2(cUnit, kThumbBCond, 0 /* offset to be patched */, cc);
+}
+
+static ArmLIR *opImm(CompilationUnit *cUnit, OpKind op, int value)
+{
+ ArmOpCode opCode = kThumbBkpt;
+ switch (op) {
+ case kOpPush:
+ opCode = kThumbPush;
+ break;
+ case kOpPop:
+ opCode = kThumbPop;
+ break;
+ default:
+ LOGE("Jit: bad case in opCondBranch");
+ dvmCompilerAbort(cUnit);
+ }
+ return newLIR1(cUnit, opCode, value);
+}
+
+static ArmLIR *opReg(CompilationUnit *cUnit, OpKind op, int rDestSrc)
+{
+ ArmOpCode opCode = kThumbBkpt;
+ switch (op) {
+ case kOpBlx:
+ opCode = kThumbBlxR;
+ break;
+ default:
+ LOGE("Jit: bad case in opReg");
+ dvmCompilerAbort(cUnit);
+ }
+ return newLIR1(cUnit, opCode, rDestSrc);
+}
+
+static ArmLIR *opRegImm(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
+ int value)
+{
+ ArmLIR *res;
+ bool neg = (value < 0);
+ int absValue = (neg) ? -value : value;
+ bool shortForm = (absValue & 0xff) == absValue;
+ ArmOpCode opCode = kThumbBkpt;
+ switch (op) {
+ case kOpAdd:
+ if ( !neg && (rDestSrc1 == 13) && (value <= 508)) { /* sp */
+ assert((value & 0x3) == 0);
+ return newLIR1(cUnit, kThumbAddSpI7, value >> 2);
+ } else if (shortForm) {
+ opCode = (neg) ? kThumbSubRI8 : kThumbAddRI8;
+ } else
+ opCode = kThumbAddRRR;
+ break;
+ case kOpSub:
+ if (!neg && (rDestSrc1 == 13) && (value <= 508)) { /* sp */
+ assert((value & 0x3) == 0);
+ return newLIR1(cUnit, kThumbSubSpI7, value >> 2);
+ } else if (shortForm) {
+ opCode = (neg) ? kThumbAddRI8 : kThumbSubRI8;
+ } else
+ opCode = kThumbSubRRR;
+ break;
+ case kOpCmp:
+ if (neg)
+ shortForm = false;
+ if (LOWREG(rDestSrc1) && shortForm) {
+ opCode = kThumbCmpRI8;
+ } else if (LOWREG(rDestSrc1)) {
+ opCode = kThumbCmpRR;
+ } else {
+ shortForm = false;
+ opCode = kThumbCmpHL;
+ }
+ break;
+ default:
+ LOGE("Jit: bad case in opRegImm");
+ dvmCompilerAbort(cUnit);
+ break;
+ }
+ if (shortForm)
+ res = newLIR2(cUnit, opCode, rDestSrc1, absValue);
+ else {
+ int rScratch = dvmCompilerAllocTemp(cUnit);
+ res = loadConstant(cUnit, rScratch, value);
+ if (op == kOpCmp)
+ newLIR2(cUnit, opCode, rDestSrc1, rScratch);
+ else
+ newLIR3(cUnit, opCode, rDestSrc1, rDestSrc1, rScratch);
+ }
+ return res;
+}
+
+static ArmLIR *opRegRegReg(CompilationUnit *cUnit, OpKind op, int rDest,
+ int rSrc1, int rSrc2)
+{
+ ArmOpCode opCode = kThumbBkpt;
+ switch (op) {
+ case kOpAdd:
+ opCode = kThumbAddRRR;
+ break;
+ case kOpSub:
+ opCode = kThumbSubRRR;
+ break;
+ default:
+ if (rDest == rSrc1) {
+ return opRegReg(cUnit, op, rDest, rSrc2);
+ } else if (rDest == rSrc2) {
+ assert(dvmCompilerIsTemp(cUnit, rSrc1));
+ dvmCompilerClobber(cUnit, rSrc1);
+ opRegReg(cUnit, op, rSrc1, rSrc2);
+ return opRegReg(cUnit, kOpMov, rDest, rSrc1);
+ } else {
+ opRegReg(cUnit, kOpMov, rDest, rSrc1);
+ return opRegReg(cUnit, op, rDest, rSrc2);
+ }
+ break;
+ }
+ return newLIR3(cUnit, opCode, rDest, rSrc1, rSrc2);
+}
+
+static ArmLIR *opRegRegImm(CompilationUnit *cUnit, OpKind op, int rDest,
+ int rSrc1, int value)
+{
+ ArmLIR *res;
+ bool neg = (value < 0);
+ int absValue = (neg) ? -value : value;
+ ArmOpCode opCode = kThumbBkpt;
+ bool shortForm = (absValue & 0x7) == absValue;
+ switch(op) {
+ case kOpAdd:
+ if (rDest == rSrc1)
+ return opRegImm(cUnit, op, rDest, value);
+ if ((rSrc1 == 13) && (value <= 1020)) { /* sp */
+ assert((value & 0x3) == 0);
+ shortForm = true;
+ opCode = kThumbAddSpRel;
+ value >>= 2;
+ } else if ((rSrc1 == 15) && (value <= 1020)) { /* pc */
+ assert((value & 0x3) == 0);
+ shortForm = true;
+ opCode = kThumbAddPcRel;
+ value >>= 2;
+ } else if (shortForm) {
+ opCode = (neg) ? kThumbSubRRI3 : kThumbAddRRI3;
+ } else if ((absValue > 0) && (absValue <= (255 + 7))) {
+ /* Two shots - 1st handle the 7 */
+ opCode = (neg) ? kThumbSubRRI3 : kThumbAddRRI3;
+ res = newLIR3(cUnit, opCode, rDest, rSrc1, 7);
+ opCode = (neg) ? kThumbSubRI8 : kThumbAddRI8;
+ newLIR2(cUnit, opCode, rDest, absValue - 7);
+ return res;
+ } else
+ opCode = kThumbAddRRR;
+ break;
+
+ case kOpSub:
+ if (rDest == rSrc1)
+ return opRegImm(cUnit, op, rDest, value);
+ if (shortForm) {
+ opCode = (neg) ? kThumbAddRRI3 : kThumbSubRRI3;
+ } else if ((absValue > 0) && (absValue <= (255 + 7))) {
+ /* Two shots - 1st handle the 7 */
+ opCode = (neg) ? kThumbAddRRI3 : kThumbSubRRI3;
+ res = newLIR3(cUnit, opCode, rDest, rSrc1, 7);
+ opCode = (neg) ? kThumbAddRI8 : kThumbSubRI8;
+ newLIR2(cUnit, opCode, rDest, absValue - 7);
+ return res;
+ } else
+ opCode = kThumbSubRRR;
+ break;
+ case kOpLsl:
+ shortForm = (!neg && value <= 31);
+ opCode = kThumbLslRRI5;
+ break;
+ case kOpLsr:
+ shortForm = (!neg && value <= 31);
+ opCode = kThumbLsrRRI5;
+ break;
+ case kOpAsr:
+ shortForm = (!neg && value <= 31);
+ opCode = kThumbAsrRRI5;
+ break;
+ case kOpMul:
+ case kOpAnd:
+ case kOpOr:
+ case kOpXor:
+ if (rDest == rSrc1) {
+ int rScratch = dvmCompilerAllocTemp(cUnit);
+ res = loadConstant(cUnit, rScratch, value);
+ opRegReg(cUnit, op, rDest, rScratch);
+ } else {
+ res = loadConstant(cUnit, rDest, value);
+ opRegReg(cUnit, op, rDest, rSrc1);
+ }
+ return res;
+ default:
+ LOGE("Jit: bad case in opRegRegImm");
+ dvmCompilerAbort(cUnit);
+ break;
+ }
+ if (shortForm)
+ res = newLIR3(cUnit, opCode, rDest, rSrc1, absValue);
+ else {
+ if (rDest != rSrc1) {
+ res = loadConstant(cUnit, rDest, value);
+ newLIR3(cUnit, opCode, rDest, rSrc1, rDest);
+ } else {
+ int rScratch = dvmCompilerAllocTemp(cUnit);
+ res = loadConstant(cUnit, rScratch, value);
+ newLIR3(cUnit, opCode, rDest, rSrc1, rScratch);
+ }
+ }
+ return res;
+}
+
+static ArmLIR *opRegReg(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
+ int rSrc2)
+{
+ ArmLIR *res;
+ ArmOpCode opCode = kThumbBkpt;
+ switch (op) {
+ case kOpAdc:
+ opCode = kThumbAdcRR;
+ break;
+ case kOpAnd:
+ opCode = kThumbAndRR;
+ break;
+ case kOpBic:
+ opCode = kThumbBicRR;
+ break;
+ case kOpCmn:
+ opCode = kThumbCmnRR;
+ break;
+ case kOpCmp:
+ opCode = kThumbCmpRR;
+ break;
+ case kOpXor:
+ opCode = kThumbEorRR;
+ break;
+ case kOpMov:
+ if (LOWREG(rDestSrc1) && LOWREG(rSrc2))
+ opCode = kThumbMovRR;
+ else if (!LOWREG(rDestSrc1) && !LOWREG(rSrc2))
+ opCode = kThumbMovRR_H2H;
+ else if (LOWREG(rDestSrc1))
+ opCode = kThumbMovRR_H2L;
+ else
+ opCode = kThumbMovRR_L2H;
+ break;
+ case kOpMul:
+ opCode = kThumbMul;
+ break;
+ case kOpMvn:
+ opCode = kThumbMvn;
+ break;
+ case kOpNeg:
+ opCode = kThumbNeg;
+ break;
+ case kOpOr:
+ opCode = kThumbOrr;
+ break;
+ case kOpSbc:
+ opCode = kThumbSbc;
+ break;
+ case kOpTst:
+ opCode = kThumbTst;
+ break;
+ case kOpLsl:
+ opCode = kThumbLslRR;
+ break;
+ case kOpLsr:
+ opCode = kThumbLsrRR;
+ break;
+ case kOpAsr:
+ opCode = kThumbAsrRR;
+ break;
+ case kOpRor:
+ opCode = kThumbRorRR;
+ case kOpAdd:
+ case kOpSub:
+ return opRegRegReg(cUnit, op, rDestSrc1, rDestSrc1, rSrc2);
+ case kOp2Byte:
+ res = opRegRegImm(cUnit, kOpLsl, rDestSrc1, rSrc2, 24);
+ opRegRegImm(cUnit, kOpAsr, rDestSrc1, rDestSrc1, 24);
+ return res;
+ case kOp2Short:
+ res = opRegRegImm(cUnit, kOpLsl, rDestSrc1, rSrc2, 16);
+ opRegRegImm(cUnit, kOpAsr, rDestSrc1, rDestSrc1, 16);
+ return res;
+ case kOp2Char:
+ res = opRegRegImm(cUnit, kOpLsl, rDestSrc1, rSrc2, 16);
+ opRegRegImm(cUnit, kOpLsr, rDestSrc1, rDestSrc1, 16);
+ return res;
+ default:
+ LOGE("Jit: bad case in opRegReg");
+ dvmCompilerAbort(cUnit);
+ break;
+ }
+ return newLIR2(cUnit, opCode, rDestSrc1, rSrc2);
+}
+
+static ArmLIR *loadConstantValueWide(CompilationUnit *cUnit, int rDestLo,
+ int rDestHi, int valLo, int valHi)
+{
+ ArmLIR *res;
+ res = loadConstantNoClobber(cUnit, rDestLo, valLo);
+ loadConstantNoClobber(cUnit, rDestHi, valHi);
+ return res;
+}
+
+/* Load value from base + scaled index. */
+static ArmLIR *loadBaseIndexed(CompilationUnit *cUnit, int rBase,
+ int rIndex, int rDest, int scale, OpSize size)
+{
+ ArmLIR *first = NULL;
+ ArmLIR *res;
+ ArmOpCode opCode = kThumbBkpt;
+ int rNewIndex = rIndex;
+ if (scale) {
+ // Scale the index, but can't trash the original.
+ rNewIndex = dvmCompilerAllocTemp(cUnit);
+ first = opRegRegImm(cUnit, kOpLsl, rNewIndex, rIndex, scale);
+ }
+ switch (size) {
+ case kWord:
+ opCode = kThumbLdrRRR;
+ break;
+ case kUnsignedHalf:
+ opCode = kThumbLdrhRRR;
+ break;
+ case kSignedHalf:
+ opCode = kThumbLdrshRRR;
+ break;
+ case kUnsignedByte:
+ opCode = kThumbLdrbRRR;
+ break;
+ case kSignedByte:
+ opCode = kThumbLdrsbRRR;
+ break;
+ default:
+ LOGE("Jit: bad case in loadBaseIndexed");
+ dvmCompilerAbort(cUnit);
+ }
+ res = newLIR3(cUnit, opCode, rDest, rBase, rNewIndex);
+#if defined(WITH_SELF_VERIFICATION)
+ if (cUnit->heapMemOp)
+ res->branchInsertSV = true;
+#endif
+ if (scale)
+ dvmCompilerFreeTemp(cUnit, rNewIndex);
+ return (first) ? first : res;
+}
+
+/* store value base base + scaled index. */
+static ArmLIR *storeBaseIndexed(CompilationUnit *cUnit, int rBase,
+ int rIndex, int rSrc, int scale, OpSize size)
+{
+ ArmLIR *first = NULL;
+ ArmLIR *res;
+ ArmOpCode opCode = kThumbBkpt;
+ int rNewIndex = rIndex;
+ if (scale) {
+ rNewIndex = dvmCompilerAllocTemp(cUnit);
+ first = opRegRegImm(cUnit, kOpLsl, rNewIndex, rIndex, scale);
+ }
+ switch (size) {
+ case kWord:
+ opCode = kThumbStrRRR;
+ break;
+ case kUnsignedHalf:
+ case kSignedHalf:
+ opCode = kThumbStrhRRR;
+ break;
+ case kUnsignedByte:
+ case kSignedByte:
+ opCode = kThumbStrbRRR;
+ break;
+ default:
+ LOGE("Jit: bad case in storeBaseIndexed");
+ dvmCompilerAbort(cUnit);
+ }
+ res = newLIR3(cUnit, opCode, rSrc, rBase, rNewIndex);
+#if defined(WITH_SELF_VERIFICATION)
+ if (cUnit->heapMemOp)
+ res->branchInsertSV = true;
+#endif
+ if (scale)
+ dvmCompilerFreeTemp(cUnit, rNewIndex);
+ return (first) ? first : res;
+}
+
+static ArmLIR *loadMultiple(CompilationUnit *cUnit, int rBase, int rMask)
+{
+ ArmLIR *res;
+ genBarrier(cUnit);
+ res = newLIR2(cUnit, kThumbLdmia, rBase, rMask);
+#if defined(WITH_SELF_VERIFICATION)
+ if (cUnit->heapMemOp)
+ res->branchInsertSV = true;
+#endif
+ genBarrier(cUnit);
+ return res;
+}
+
+static ArmLIR *storeMultiple(CompilationUnit *cUnit, int rBase, int rMask)
+{
+ ArmLIR *res;
+ genBarrier(cUnit);
+ res = newLIR2(cUnit, kThumbStmia, rBase, rMask);
+#if defined(WITH_SELF_VERIFICATION)
+ if (cUnit->heapMemOp)
+ res->branchInsertSV = true;
+#endif
+ genBarrier(cUnit);
+ return res;
+}
+
+static ArmLIR *loadBaseDispBody(CompilationUnit *cUnit, MIR *mir, int rBase,
+ int displacement, int rDest, int rDestHi,
+ OpSize size, int sReg)
+/*
+ * Load value from base + displacement. Optionally perform null check
+ * on base (which must have an associated sReg and MIR). If not
+ * performing null check, incoming MIR can be null. IMPORTANT: this
+ * code must not allocate any new temps. If a new register is needed
+ * and base and dest are the same, spill some other register to
+ * rlp and then restore.
+ */
+{
+ ArmLIR *res;
+ ArmLIR *load = NULL;
+ ArmLIR *load2 = NULL;
+ ArmOpCode opCode = kThumbBkpt;
+ bool shortForm = false;
+ int encodedDisp = displacement;
+ bool pair = false;
+
+ switch (size) {
+ case kLong:
+ case kDouble:
+ pair = true;
+ if ((displacement < 124) && (displacement >= 0)) {
+ assert((displacement & 0x3) == 0);
+ shortForm = true;
+ encodedDisp >>= 2;
+ opCode = kThumbLdrRRI5;
+ } else {
+ opCode = kThumbLdrRRR;
+ }
+ break;
+ case kWord:
+ if (LOWREG(rDest) && (rBase == rpc) &&
+ (displacement <= 1020) && (displacement >= 0)) {
+ shortForm = true;
+ encodedDisp >>= 2;
+ opCode = kThumbLdrPcRel;
+ } else if (LOWREG(rDest) && (rBase == r13) &&
+ (displacement <= 1020) && (displacement >= 0)) {
+ shortForm = true;
+ encodedDisp >>= 2;
+ opCode = kThumbLdrSpRel;
+ } else if (displacement < 128 && displacement >= 0) {
+ assert((displacement & 0x3) == 0);
+ shortForm = true;
+ encodedDisp >>= 2;
+ opCode = kThumbLdrRRI5;
+ } else {
+ opCode = kThumbLdrRRR;
+ }
+ break;
+ case kUnsignedHalf:
+ if (displacement < 64 && displacement >= 0) {
+ assert((displacement & 0x1) == 0);
+ shortForm = true;
+ encodedDisp >>= 1;
+ opCode = kThumbLdrhRRI5;
+ } else {
+ opCode = kThumbLdrhRRR;
+ }
+ break;
+ case kSignedHalf:
+ opCode = kThumbLdrshRRR;
+ break;
+ case kUnsignedByte:
+ if (displacement < 32 && displacement >= 0) {
+ shortForm = true;
+ opCode = kThumbLdrbRRI5;
+ } else {
+ opCode = kThumbLdrbRRR;
+ }
+ break;
+ case kSignedByte:
+ opCode = kThumbLdrsbRRR;
+ break;
+ default:
+ LOGE("Jit: bad case in loadBaseIndexedBody");
+ dvmCompilerAbort(cUnit);
+ }
+ if (shortForm) {
+ load = res = newLIR3(cUnit, opCode, rDest, rBase, encodedDisp);
+ if (pair) {
+ load2 = newLIR3(cUnit, opCode, rDestHi, rBase, encodedDisp+1);
+ }
+ } else {
+ if (pair) {
+ int rTmp = dvmCompilerAllocFreeTemp(cUnit);
+ res = opRegRegImm(cUnit, kOpAdd, rTmp, rBase, displacement);
+ load = newLIR3(cUnit, kThumbLdrRRI5, rDest, rTmp, 0);
+ load2 = newLIR3(cUnit, kThumbLdrRRI5, rDestHi, rTmp, 1);
+ dvmCompilerFreeTemp(cUnit, rTmp);
+ } else {
+ int rTmp = (rBase == rDest) ? dvmCompilerAllocFreeTemp(cUnit)
+ : rDest;
+ res = loadConstant(cUnit, rTmp, displacement);
+ load = newLIR3(cUnit, opCode, rDest, rBase, rTmp);
+ if (rBase == rFP)
+ annotateDalvikRegAccess(load, displacement >> 2,
+ true /* isLoad */);
+ if (rTmp != rDest)
+ dvmCompilerFreeTemp(cUnit, rTmp);
+ }
+ }
+ if (rBase == rFP) {
+ if (load != NULL)
+ annotateDalvikRegAccess(load, displacement >> 2,
+ true /* isLoad */);
+ if (load2 != NULL)
+ annotateDalvikRegAccess(load2, (displacement >> 2) + 1,
+ true /* isLoad */);
+ }
+#if defined(WITH_SELF_VERIFICATION)
+ if (load != NULL && cUnit->heapMemOp)
+ load->branchInsertSV = true;
+ if (load2 != NULL && cUnit->heapMemOp)
+ load2->branchInsertSV = true;
+#endif
+ return res;
+}
+
+static ArmLIR *loadBaseDisp(CompilationUnit *cUnit, MIR *mir, int rBase,
+ int displacement, int rDest, OpSize size,
+ int sReg)
+{
+ return loadBaseDispBody(cUnit, mir, rBase, displacement, rDest, -1,
+ size, sReg);
+}
+
+static ArmLIR *loadBaseDispWide(CompilationUnit *cUnit, MIR *mir, int rBase,
+ int displacement, int rDestLo, int rDestHi,
+ int sReg)
+{
+ return loadBaseDispBody(cUnit, mir, rBase, displacement, rDestLo, rDestHi,
+ kLong, sReg);
+}
+
+static ArmLIR *storeBaseDispBody(CompilationUnit *cUnit, int rBase,
+ int displacement, int rSrc, int rSrcHi,
+ OpSize size)
+{
+ ArmLIR *res;
+ ArmLIR *store = NULL;
+ ArmLIR *store2 = NULL;
+ ArmOpCode opCode = kThumbBkpt;
+ bool shortForm = false;
+ int encodedDisp = displacement;
+ bool pair = false;
+
+ switch (size) {
+ case kLong:
+ case kDouble:
+ pair = true;
+ if ((displacement < 124) && (displacement >= 0)) {
+ assert((displacement & 0x3) == 0);
+ pair = true;
+ shortForm = true;
+ encodedDisp >>= 2;
+ opCode = kThumbStrRRI5;
+ } else {
+ opCode = kThumbStrRRR;
+ }
+ break;
+ case kWord:
+ if (displacement < 128 && displacement >= 0) {
+ assert((displacement & 0x3) == 0);
+ shortForm = true;
+ encodedDisp >>= 2;
+ opCode = kThumbStrRRI5;
+ } else {
+ opCode = kThumbStrRRR;
+ }
+ break;
+ case kUnsignedHalf:
+ case kSignedHalf:
+ if (displacement < 64 && displacement >= 0) {
+ assert((displacement & 0x1) == 0);
+ shortForm = true;
+ encodedDisp >>= 1;
+ opCode = kThumbStrhRRI5;
+ } else {
+ opCode = kThumbStrhRRR;
+ }
+ break;
+ case kUnsignedByte:
+ case kSignedByte:
+ if (displacement < 32 && displacement >= 0) {
+ shortForm = true;
+ opCode = kThumbStrbRRI5;
+ } else {
+ opCode = kThumbStrbRRR;
+ }
+ break;
+ default:
+ LOGE("Jit: bad case in storeBaseIndexedBody");
+ dvmCompilerAbort(cUnit);
+ }
+ if (shortForm) {
+ store = res = newLIR3(cUnit, opCode, rSrc, rBase, encodedDisp);
+ if (pair) {
+ store2 = newLIR3(cUnit, opCode, rSrcHi, rBase, encodedDisp + 1);
+ }
+ } else {
+ int rScratch = dvmCompilerAllocTemp(cUnit);
+ if (pair) {
+ res = opRegRegImm(cUnit, kOpAdd, rScratch, rBase, displacement);
+ store = newLIR3(cUnit, kThumbStrRRI5, rSrc, rScratch, 0);
+ store2 = newLIR3(cUnit, kThumbStrRRI5, rSrcHi, rScratch, 1);
+ } else {
+ res = loadConstant(cUnit, rScratch, displacement);
+ store = newLIR3(cUnit, opCode, rSrc, rBase, rScratch);
+ }
+ dvmCompilerFreeTemp(cUnit, rScratch);
+ }
+ if (rBase == rFP) {
+ if (store != NULL)
+ annotateDalvikRegAccess(store, displacement >> 2,
+ false /* isLoad */);
+ if (store2 != NULL)
+ annotateDalvikRegAccess(store2, (displacement >> 2) + 1,
+ false /* isLoad */);
+ }
+#if defined(WITH_SELF_VERIFICATION)
+ if (store != NULL && cUnit->heapMemOp)
+ store->branchInsertSV = true;
+ if (store2 != NULL && cUnit->heapMemOp)
+ store2->branchInsertSV = true;
+#endif
+ return res;
+}
+
+static ArmLIR *storeBaseDisp(CompilationUnit *cUnit, int rBase,
+ int displacement, int rSrc, OpSize size)
+{
+ return storeBaseDispBody(cUnit, rBase, displacement, rSrc, -1, size);
+}
+
+static ArmLIR *storeBaseDispWide(CompilationUnit *cUnit, int rBase,
+ int displacement, int rSrcLo, int rSrcHi)
+{
+ return storeBaseDispBody(cUnit, rBase, displacement, rSrcLo, rSrcHi, kLong);
+}
+
+static void storePair(CompilationUnit *cUnit, int base, int lowReg, int highReg)
+{
+ if (lowReg < highReg) {
+ storeMultiple(cUnit, base, (1 << lowReg) | (1 << highReg));
+ } else {
+ storeWordDisp(cUnit, base, 0, lowReg);
+ storeWordDisp(cUnit, base, 4, highReg);
+ }
+}
+
+static void loadPair(CompilationUnit *cUnit, int base, int lowReg, int highReg)
+{
+ if (lowReg < highReg) {
+ loadMultiple(cUnit, base, (1 << lowReg) | (1 << highReg));
+ } else {
+ loadWordDisp(cUnit, base, 0 , lowReg);
+ loadWordDisp(cUnit, base, 4 , highReg);
+ }
+}
+
+static ArmLIR* genRegCopyNoInsert(CompilationUnit *cUnit, int rDest, int rSrc)
+{
+ ArmLIR* res;
+ ArmOpCode opCode;
+ res = dvmCompilerNew(sizeof(ArmLIR), true);
+ if (LOWREG(rDest) && LOWREG(rSrc))
+ opCode = kThumbMovRR;
+ else if (!LOWREG(rDest) && !LOWREG(rSrc))
+ opCode = kThumbMovRR_H2H;
+ else if (LOWREG(rDest))
+ opCode = kThumbMovRR_H2L;
+ else
+ opCode = kThumbMovRR_L2H;
+
+ res->operands[0] = rDest;
+ res->operands[1] = rSrc;
+ res->opCode = opCode;
+ setupResourceMasks(res);
+ if (rDest == rSrc) {
+ res->isNop = true;
+ }
+ return res;
+}
+
+static ArmLIR* genRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
+{
+ ArmLIR *res = genRegCopyNoInsert(cUnit, rDest, rSrc);
+ dvmCompilerAppendLIR(cUnit, (LIR*)res);
+ return res;
+}
+
+static void genRegCopyWide(CompilationUnit *cUnit, int destLo, int destHi,
+ int srcLo, int srcHi)
+{
+ // Handle overlap
+ if (srcHi == destLo) {
+ genRegCopy(cUnit, destHi, srcHi);
+ genRegCopy(cUnit, destLo, srcLo);
+ } else {
+ genRegCopy(cUnit, destLo, srcLo);
+ genRegCopy(cUnit, destHi, srcHi);
+ }
+}
+
+static inline ArmLIR *genRegImmCheck(CompilationUnit *cUnit,
+ ArmConditionCode cond, int reg,
+ int checkValue, int dOffset,
+ ArmLIR *pcrLabel)
+{
+ int tReg;
+ ArmLIR *res;
+ if ((checkValue & 0xff) != checkValue) {
+ tReg = dvmCompilerAllocTemp(cUnit);
+ loadConstant(cUnit, tReg, checkValue);
+ res = genRegRegCheck(cUnit, cond, reg, tReg, dOffset, pcrLabel);
+ dvmCompilerFreeTemp(cUnit, tReg);
+ return res;
+ }
+ newLIR2(cUnit, kThumbCmpRI8, reg, checkValue);
+ ArmLIR *branch = newLIR2(cUnit, kThumbBCond, 0, cond);
+ return genCheckCommon(cUnit, dOffset, branch, pcrLabel);
+}
diff --git a/vm/compiler/codegen/arm/Thumb/Gen.c b/vm/compiler/codegen/arm/Thumb/Gen.c
new file mode 100644
index 0000000..37cc18d
--- /dev/null
+++ b/vm/compiler/codegen/arm/Thumb/Gen.c
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file contains codegen for the Thumb ISA and is intended to be
+ * includes by:
+ *
+ * Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+/*
+ * Perform a "reg cmp imm" operation and jump to the PCR region if condition
+ * satisfies.
+ */
+static void genNegFloat(CompilationUnit *cUnit, RegLocation rlDest,
+ RegLocation rlSrc)
+{
+ RegLocation rlResult;
+ rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+ opRegRegImm(cUnit, kOpAdd, rlResult.lowReg,
+ rlSrc.lowReg, 0x80000000);
+ storeValue(cUnit, rlDest, rlResult);
+}
+
+static void genNegDouble(CompilationUnit *cUnit, RegLocation rlDest,
+ RegLocation rlSrc)
+{
+ RegLocation rlResult;
+ rlSrc = loadValueWide(cUnit, rlSrc, kCoreReg);
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+ opRegRegImm(cUnit, kOpAdd, rlResult.highReg, rlSrc.highReg,
+ 0x80000000);
+ genRegCopy(cUnit, rlResult.lowReg, rlSrc.lowReg);
+ storeValueWide(cUnit, rlDest, rlResult);
+}
+
+static void genMulLong(CompilationUnit *cUnit, RegLocation rlDest,
+ RegLocation rlSrc1, RegLocation rlSrc2)
+{
+ RegLocation rlResult;
+ loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
+ loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
+ genDispatchToHandler(cUnit, TEMPLATE_MUL_LONG);
+ rlResult = dvmCompilerGetReturnWide(cUnit);
+ storeValueWide(cUnit, rlDest, rlResult);
+}
+
+static bool partialOverlap(int sreg1, int sreg2)
+{
+ return abs(sreg1 - sreg2) == 1;
+}
+
+static void genLong3Addr(CompilationUnit *cUnit, MIR *mir, OpKind firstOp,
+ OpKind secondOp, RegLocation rlDest,
+ RegLocation rlSrc1, RegLocation rlSrc2)
+{
+ RegLocation rlResult;
+ if (partialOverlap(rlSrc1.sRegLow,rlSrc2.sRegLow) ||
+ partialOverlap(rlSrc1.sRegLow,rlDest.sRegLow) ||
+ partialOverlap(rlSrc2.sRegLow,rlDest.sRegLow)) {
+ // Rare case - not enough registers to properly handle
+ genInterpSingleStep(cUnit, mir);
+ } else if (rlDest.sRegLow == rlSrc1.sRegLow) {
+ // Already 2-operand
+ rlResult = loadValueWide(cUnit, rlDest, kCoreReg);
+ rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
+ opRegReg(cUnit, firstOp, rlResult.lowReg, rlSrc2.lowReg);
+ opRegReg(cUnit, secondOp, rlResult.highReg, rlSrc2.highReg);
+ storeValueWide(cUnit, rlDest, rlResult);
+ } else if (rlDest.sRegLow == rlSrc2.sRegLow) {
+ // Bad case - must use/clobber Src1 and reassign Dest
+ rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
+ rlResult = loadValueWide(cUnit, rlDest, kCoreReg);
+ opRegReg(cUnit, firstOp, rlSrc1.lowReg, rlResult.lowReg);
+ opRegReg(cUnit, secondOp, rlSrc1.highReg, rlResult.highReg);
+ // Old reg assignments are now invalid
+ dvmCompilerClobber(cUnit, rlResult.lowReg);
+ dvmCompilerClobber(cUnit, rlResult.highReg);
+ dvmCompilerClobber(cUnit, rlSrc1.lowReg);
+ dvmCompilerClobber(cUnit, rlSrc1.highReg);
+ rlDest.location = kLocDalvikFrame;
+ assert(rlSrc1.location == kLocPhysReg);
+ // Reassign registers - rlDest will now get rlSrc1's old regs
+ storeValueWide(cUnit, rlDest, rlSrc1);
+ } else {
+ // Copy Src1 to Dest
+ rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, false);
+ loadValueDirectWide(cUnit, rlSrc1, rlResult.lowReg,
+ rlResult.highReg);
+ rlResult.location = kLocPhysReg;
+ opRegReg(cUnit, firstOp, rlResult.lowReg, rlSrc2.lowReg);
+ opRegReg(cUnit, secondOp, rlResult.highReg, rlSrc2.highReg);
+ storeValueWide(cUnit, rlDest, rlResult);
+ }
+}
+
+void dvmCompilerInitializeRegAlloc(CompilationUnit *cUnit)
+{
+ int numTemps = sizeof(coreTemps)/sizeof(int);
+ RegisterPool *pool = dvmCompilerNew(sizeof(*pool), true);
+ cUnit->regPool = pool;
+ pool->numCoreTemps = numTemps;
+ pool->coreTemps =
+ dvmCompilerNew(numTemps * sizeof(*pool->coreTemps), true);
+ pool->numFPTemps = 0;
+ pool->FPTemps = NULL;
+ pool->numCoreRegs = 0;
+ pool->coreRegs = NULL;
+ pool->numFPRegs = 0;
+ pool->FPRegs = NULL;
+ dvmCompilerInitPool(pool->coreTemps, coreTemps, pool->numCoreTemps);
+ dvmCompilerInitPool(pool->FPTemps, NULL, 0);
+ dvmCompilerInitPool(pool->coreRegs, NULL, 0);
+ dvmCompilerInitPool(pool->FPRegs, NULL, 0);
+ pool->nullCheckedRegs =
+ dvmCompilerAllocBitVector(cUnit->numSSARegs, false);
+}
+
+/* Export the Dalvik PC assicated with an instruction to the StackSave area */
+static ArmLIR *genExportPC(CompilationUnit *cUnit, MIR *mir)
+{
+ ArmLIR *res;
+ int rDPC = dvmCompilerAllocTemp(cUnit);
+ int rAddr = dvmCompilerAllocTemp(cUnit);
+ int offset = offsetof(StackSaveArea, xtra.currentPc);
+ res = loadConstant(cUnit, rDPC, (int) (cUnit->method->insns + mir->offset));
+ newLIR2(cUnit, kThumbMovRR, rAddr, rFP);
+ newLIR2(cUnit, kThumbSubRI8, rAddr, sizeof(StackSaveArea) - offset);
+ storeWordDisp( cUnit, rAddr, 0, rDPC);
+ return res;
+}
+
+static void genMonitor(CompilationUnit *cUnit, MIR *mir)
+{
+ genMonitorPortable(cUnit, mir);
+}
+
+static void genCmpLong(CompilationUnit *cUnit, MIR *mir, RegLocation rlDest,
+ RegLocation rlSrc1, RegLocation rlSrc2)
+{
+ RegLocation rlResult;
+ loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
+ loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
+ genDispatchToHandler(cUnit, TEMPLATE_CMP_LONG);
+ rlResult = dvmCompilerGetReturn(cUnit);
+ storeValue(cUnit, rlDest, rlResult);
+}
+
+static bool genInlinedAbsFloat(CompilationUnit *cUnit, MIR *mir)
+{
+ int offset = offsetof(InterpState, retval);
+ RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+ int reg0 = loadValue(cUnit, rlSrc, kCoreReg).lowReg;
+ int signMask = dvmCompilerAllocTemp(cUnit);
+ loadConstant(cUnit, signMask, 0x7fffffff);
+ newLIR2(cUnit, kThumbAndRR, reg0, signMask);
+ dvmCompilerFreeTemp(cUnit, signMask);
+ storeWordDisp(cUnit, rGLUE, offset, reg0);
+ //TUNING: rewrite this to not clobber
+ dvmCompilerClobber(cUnit, reg0);
+ return true;
+}
+
+static bool genInlinedAbsDouble(CompilationUnit *cUnit, MIR *mir)
+{
+ int offset = offsetof(InterpState, retval);
+ RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+ RegLocation regSrc = loadValueWide(cUnit, rlSrc, kCoreReg);
+ int reglo = regSrc.lowReg;
+ int reghi = regSrc.highReg;
+ int signMask = dvmCompilerAllocTemp(cUnit);
+ loadConstant(cUnit, signMask, 0x7fffffff);
+ storeWordDisp(cUnit, rGLUE, offset, reglo);
+ newLIR2(cUnit, kThumbAndRR, reghi, signMask);
+ dvmCompilerFreeTemp(cUnit, signMask);
+ storeWordDisp(cUnit, rGLUE, offset + 4, reghi);
+ //TUNING: rewrite this to not clobber
+ dvmCompilerClobber(cUnit, reghi);
+ return true;
+}
+
+/* No select in thumb, so we need to branch. Thumb2 will do better */
+static bool genInlinedMinMaxInt(CompilationUnit *cUnit, MIR *mir, bool isMin)
+{
+ int offset = offsetof(InterpState, retval);
+ RegLocation rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 0);
+ RegLocation rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 1);
+ int reg0 = loadValue(cUnit, rlSrc1, kCoreReg).lowReg;
+ int reg1 = loadValue(cUnit, rlSrc2, kCoreReg).lowReg;
+ newLIR2(cUnit, kThumbCmpRR, reg0, reg1);
+ ArmLIR *branch1 = newLIR2(cUnit, kThumbBCond, 2,
+ isMin ? kArmCondLt : kArmCondGt);
+ newLIR2(cUnit, kThumbMovRR, reg0, reg1);
+ ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+ target->defMask = ENCODE_ALL;
+ newLIR3(cUnit, kThumbStrRRI5, reg0, rGLUE, offset >> 2);
+ branch1->generic.target = (LIR *)target;
+ //TUNING: rewrite this to not clobber
+ dvmCompilerClobber(cUnit,reg0);
+ return false;
+}
+
+static void genMultiplyByTwoBitMultiplier(CompilationUnit *cUnit,
+ RegLocation rlSrc, RegLocation rlResult, int lit,
+ int firstBit, int secondBit)
+{
+ // We can't implement "add src, src, src, lsl#shift" on Thumb, so we have
+ // to do a regular multiply.
+ opRegRegImm(cUnit, kOpMul, rlResult.lowReg, rlSrc.lowReg, lit);
+}
diff --git a/vm/compiler/codegen/arm/Thumb/Ralloc.c b/vm/compiler/codegen/arm/Thumb/Ralloc.c
new file mode 100644
index 0000000..6769972
--- /dev/null
+++ b/vm/compiler/codegen/arm/Thumb/Ralloc.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file contains codegen for the Thumb ISA and is intended to be
+ * includes by:
+ *
+ * Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+/*
+ * Alloc a pair of core registers, or a double. Low reg in low byte,
+ * high reg in next byte.
+ */
+int dvmCompilerAllocTypedTempPair(CompilationUnit *cUnit, bool fpHint,
+ int regClass)
+{
+ int highReg;
+ int lowReg;
+ int res = 0;
+ lowReg = dvmCompilerAllocTemp(cUnit);
+ highReg = dvmCompilerAllocTemp(cUnit);
+ res = (lowReg & 0xff) | ((highReg & 0xff) << 8);
+ return res;
+}
+
+int dvmCompilerAllocTypedTemp(CompilationUnit *cUnit, bool fpHint, int regClass)
+{
+ return dvmCompilerAllocTemp(cUnit);
+}
diff --git a/vm/compiler/codegen/arm/Thumb2/Factory.c b/vm/compiler/codegen/arm/Thumb2/Factory.c
new file mode 100644
index 0000000..2bf2940
--- /dev/null
+++ b/vm/compiler/codegen/arm/Thumb2/Factory.c
@@ -0,0 +1,1210 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file contains codegen for the Thumb ISA and is intended to be
+ * includes by:
+ *
+ * Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+static int coreTemps[] = {r0, r1, r2, r3, r4PC, r7, r8, r9, r10, r11, r12};
+static int fpTemps[] = {fr16, fr17, fr18, fr19, fr20, fr21, fr22, fr23,
+ fr24, fr25, fr26, fr27, fr28, fr29, fr30, fr31};
+
+static int encodeImmSingle(int value)
+{
+ int res;
+ int bitA = (value & 0x80000000) >> 31;
+ int notBitB = (value & 0x40000000) >> 30;
+ int bitB = (value & 0x20000000) >> 29;
+ int bSmear = (value & 0x3e000000) >> 25;
+ int slice = (value & 0x01f80000) >> 19;
+ int zeroes = (value & 0x0007ffff);
+ if (zeroes != 0)
+ return -1;
+ if (bitB) {
+ if ((notBitB != 0) || (bSmear != 0x1f))
+ return -1;
+ } else {
+ if ((notBitB != 1) || (bSmear != 0x0))
+ return -1;
+ }
+ res = (bitA << 7) | (bitB << 6) | slice;
+ return res;
+}
+
+static ArmLIR *loadFPConstantValue(CompilationUnit *cUnit, int rDest,
+ int value)
+{
+ int encodedImm = encodeImmSingle(value);
+ assert(SINGLEREG(rDest));
+ if (encodedImm >= 0) {
+ return newLIR2(cUnit, kThumb2Vmovs_IMM8, rDest, encodedImm);
+ }
+ ArmLIR *dataTarget = scanLiteralPool(cUnit, value, 0);
+ if (dataTarget == NULL) {
+ dataTarget = addWordData(cUnit, value, false);
+ }
+ ArmLIR *loadPcRel = dvmCompilerNew(sizeof(ArmLIR), true);
+ loadPcRel->opCode = kThumb2Vldrs;
+ loadPcRel->generic.target = (LIR *) dataTarget;
+ loadPcRel->operands[0] = rDest;
+ loadPcRel->operands[1] = rpc;
+ setupResourceMasks(loadPcRel);
+ // Self-cosim workaround.
+ if (rDest != rlr)
+ setMemRefType(loadPcRel, true, kLiteral);
+ loadPcRel->aliasInfo = dataTarget->operands[0];
+ dvmCompilerAppendLIR(cUnit, (LIR *) loadPcRel);
+ return loadPcRel;
+}
+
+static int leadingZeros(u4 val)
+{
+ u4 alt;
+ int n;
+ int count;
+
+ count = 16;
+ n = 32;
+ do {
+ alt = val >> count;
+ if (alt != 0) {
+ n = n - count;
+ val = alt;
+ }
+ count >>= 1;
+ } while (count);
+ return n - val;
+}
+
+/*
+ * Determine whether value can be encoded as a Thumb2 modified
+ * immediate. If not, return -1. If so, return i:imm3:a:bcdefgh form.
+ */
+static int modifiedImmediate(u4 value)
+{
+ int zLeading;
+ int zTrailing;
+ u4 b0 = value & 0xff;
+
+ /* Note: case of value==0 must use 0:000:0:0000000 encoding */
+ if (value <= 0xFF)
+ return b0; // 0:000:a:bcdefgh
+ if (value == ((b0 << 16) | b0))
+ return (0x1 << 8) | b0; /* 0:001:a:bcdefgh */
+ if (value == ((b0 << 24) | (b0 << 16) | (b0 << 8) | b0))
+ return (0x3 << 8) | b0; /* 0:011:a:bcdefgh */
+ b0 = (value >> 8) & 0xff;
+ if (value == ((b0 << 24) | (b0 << 8)))
+ return (0x2 << 8) | b0; /* 0:010:a:bcdefgh */
+ /* Can we do it with rotation? */
+ zLeading = leadingZeros(value);
+ zTrailing = 32 - leadingZeros(~value & (value - 1));
+ /* A run of eight or fewer active bits? */
+ if ((zLeading + zTrailing) < 24)
+ return -1; /* No - bail */
+ /* left-justify the constant, discarding msb (known to be 1) */
+ value <<= zLeading + 1;
+ /* Create bcdefgh */
+ value >>= 25;
+ /* Put it all together */
+ return value | ((0x8 + zLeading) << 7); /* [01000..11111]:bcdefgh */
+}
+
+/*
+ * Load a immediate using a shortcut if possible; otherwise
+ * grab from the per-translation literal pool.
+ *
+ * No additional register clobbering operation performed. Use this version when
+ * 1) rDest is freshly returned from dvmCompilerAllocTemp or
+ * 2) The codegen is under fixed register usage
+ */
+static ArmLIR *loadConstantNoClobber(CompilationUnit *cUnit, int rDest,
+ int value)
+{
+ ArmLIR *res;
+ int modImm;
+
+ if (FPREG(rDest)) {
+ return loadFPConstantValue(cUnit, rDest, value);
+ }
+
+ /* See if the value can be constructed cheaply */
+ if (LOWREG(rDest) && (value >= 0) && (value <= 255)) {
+ return newLIR2(cUnit, kThumbMovImm, rDest, value);
+ }
+ /* Check Modified immediate special cases */
+ modImm = modifiedImmediate(value);
+ if (modImm >= 0) {
+ res = newLIR2(cUnit, kThumb2MovImmShift, rDest, modImm);
+ return res;
+ }
+ modImm = modifiedImmediate(~value);
+ if (modImm >= 0) {
+ res = newLIR2(cUnit, kThumb2MvnImmShift, rDest, modImm);
+ return res;
+ }
+ /* 16-bit immediate? */
+ if ((value & 0xffff) == value) {
+ res = newLIR2(cUnit, kThumb2MovImm16, rDest, value);
+ return res;
+ }
+ /* No shortcut - go ahead and use literal pool */
+ ArmLIR *dataTarget = scanLiteralPool(cUnit, value, 0);
+ if (dataTarget == NULL) {
+ dataTarget = addWordData(cUnit, value, false);
+ }
+ ArmLIR *loadPcRel = dvmCompilerNew(sizeof(ArmLIR), true);
+ loadPcRel->opCode = kThumb2LdrPcRel12;
+ loadPcRel->generic.target = (LIR *) dataTarget;
+ loadPcRel->operands[0] = rDest;
+ setupResourceMasks(loadPcRel);
+ /*
+ * Special case for literal loads with a link register target.
+ * Self-cosim mode will insert calls prior to heap references
+ * after optimization, and those will destroy r14. The easy
+ * workaround is to treat literal loads into r14 as heap references
+ * to prevent them from being hoisted. Use of r14 in this manner
+ * is currently rare. Revisit if that changes.
+ */
+ if (rDest != rlr)
+ setMemRefType(loadPcRel, true, kLiteral);
+ loadPcRel->aliasInfo = dataTarget->operands[0];
+ res = loadPcRel;
+ dvmCompilerAppendLIR(cUnit, (LIR *) loadPcRel);
+
+ /*
+ * To save space in the constant pool, we use the ADD_RRI8 instruction to
+ * add up to 255 to an existing constant value.
+ */
+ if (dataTarget->operands[0] != value) {
+ opRegImm(cUnit, kOpAdd, rDest, value - dataTarget->operands[0]);
+ }
+ return res;
+}
+
+/*
+ * Load an immediate value into a fixed or temp register. Target
+ * register is clobbered, and marked inUse.
+ */
+static ArmLIR *loadConstant(CompilationUnit *cUnit, int rDest, int value)
+{
+ if (dvmCompilerIsTemp(cUnit, rDest)) {
+ dvmCompilerClobber(cUnit, rDest);
+ dvmCompilerMarkInUse(cUnit, rDest);
+ }
+ return loadConstantNoClobber(cUnit, rDest, value);
+}
+
+static ArmLIR *opNone(CompilationUnit *cUnit, OpKind op)
+{
+ ArmOpCode opCode = kThumbBkpt;
+ switch (op) {
+ case kOpUncondBr:
+ opCode = kThumbBUncond;
+ break;
+ default:
+ assert(0);
+ }
+ return newLIR0(cUnit, opCode);
+}
+
+static ArmLIR *opCondBranch(CompilationUnit *cUnit, ArmConditionCode cc)
+{
+ return newLIR2(cUnit, kThumb2BCond, 0 /* offset to be patched */, cc);
+}
+
+static ArmLIR *opImm(CompilationUnit *cUnit, OpKind op, int value)
+{
+ ArmOpCode opCode = kThumbBkpt;
+ switch (op) {
+ case kOpPush:
+ opCode = ((value & 0xff00) != 0) ? kThumb2Push : kThumbPush;
+ break;
+ case kOpPop:
+ opCode = ((value & 0xff00) != 0) ? kThumb2Pop : kThumbPop;
+ break;
+ default:
+ assert(0);
+ }
+ return newLIR1(cUnit, opCode, value);
+}
+
+static ArmLIR *opReg(CompilationUnit *cUnit, OpKind op, int rDestSrc)
+{
+ ArmOpCode opCode = kThumbBkpt;
+ switch (op) {
+ case kOpBlx:
+ opCode = kThumbBlxR;
+ break;
+ default:
+ assert(0);
+ }
+ return newLIR1(cUnit, opCode, rDestSrc);
+}
+
+static ArmLIR *opRegRegShift(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
+ int rSrc2, int shift)
+{
+ bool thumbForm = ((shift == 0) && LOWREG(rDestSrc1) && LOWREG(rSrc2));
+ ArmOpCode opCode = kThumbBkpt;
+ switch (op) {
+ case kOpAdc:
+ opCode = (thumbForm) ? kThumbAdcRR : kThumb2AdcRRR;
+ break;
+ case kOpAnd:
+ opCode = (thumbForm) ? kThumbAndRR : kThumb2AndRRR;
+ break;
+ case kOpBic:
+ opCode = (thumbForm) ? kThumbBicRR : kThumb2BicRRR;
+ break;
+ case kOpCmn:
+ assert(shift == 0);
+ opCode = (thumbForm) ? kThumbCmnRR : kThumb2CmnRR;
+ break;
+ case kOpCmp:
+ if (thumbForm)
+ opCode = kThumbCmpRR;
+ else if ((shift == 0) && !LOWREG(rDestSrc1) && !LOWREG(rSrc2))
+ opCode = kThumbCmpHH;
+ else if ((shift == 0) && LOWREG(rDestSrc1))
+ opCode = kThumbCmpLH;
+ else if (shift == 0)
+ opCode = kThumbCmpHL;
+ else
+ opCode = kThumb2CmpRR;
+ break;
+ case kOpXor:
+ opCode = (thumbForm) ? kThumbEorRR : kThumb2EorRRR;
+ break;
+ case kOpMov:
+ assert(shift == 0);
+ if (LOWREG(rDestSrc1) && LOWREG(rSrc2))
+ opCode = kThumbMovRR;
+ else if (!LOWREG(rDestSrc1) && !LOWREG(rSrc2))
+ opCode = kThumbMovRR_H2H;
+ else if (LOWREG(rDestSrc1))
+ opCode = kThumbMovRR_H2L;
+ else
+ opCode = kThumbMovRR_L2H;
+ break;
+ case kOpMul:
+ assert(shift == 0);
+ opCode = (thumbForm) ? kThumbMul : kThumb2MulRRR;
+ break;
+ case kOpMvn:
+ opCode = (thumbForm) ? kThumbMvn : kThumb2MnvRR;
+ break;
+ case kOpNeg:
+ assert(shift == 0);
+ opCode = (thumbForm) ? kThumbNeg : kThumb2NegRR;
+ break;
+ case kOpOr:
+ opCode = (thumbForm) ? kThumbOrr : kThumb2OrrRRR;
+ break;
+ case kOpSbc:
+ opCode = (thumbForm) ? kThumbSbc : kThumb2SbcRRR;
+ break;
+ case kOpTst:
+ opCode = (thumbForm) ? kThumbTst : kThumb2TstRR;
+ break;
+ case kOpLsl:
+ assert(shift == 0);
+ opCode = (thumbForm) ? kThumbLslRR : kThumb2LslRRR;
+ break;
+ case kOpLsr:
+ assert(shift == 0);
+ opCode = (thumbForm) ? kThumbLsrRR : kThumb2LsrRRR;
+ break;
+ case kOpAsr:
+ assert(shift == 0);
+ opCode = (thumbForm) ? kThumbAsrRR : kThumb2AsrRRR;
+ break;
+ case kOpRor:
+ assert(shift == 0);
+ opCode = (thumbForm) ? kThumbRorRR : kThumb2RorRRR;
+ break;
+ case kOpAdd:
+ opCode = (thumbForm) ? kThumbAddRRR : kThumb2AddRRR;
+ break;
+ case kOpSub:
+ opCode = (thumbForm) ? kThumbSubRRR : kThumb2SubRRR;
+ break;
+ case kOp2Byte:
+ assert(shift == 0);
+ return newLIR4(cUnit, kThumb2Sbfx, rDestSrc1, rSrc2, 0, 8);
+ case kOp2Short:
+ assert(shift == 0);
+ return newLIR4(cUnit, kThumb2Sbfx, rDestSrc1, rSrc2, 0, 16);
+ case kOp2Char:
+ assert(shift == 0);
+ return newLIR4(cUnit, kThumb2Ubfx, rDestSrc1, rSrc2, 0, 16);
+ default:
+ assert(0);
+ break;
+ }
+ assert(opCode >= 0);
+ if (EncodingMap[opCode].flags & IS_BINARY_OP)
+ return newLIR2(cUnit, opCode, rDestSrc1, rSrc2);
+ else if (EncodingMap[opCode].flags & IS_TERTIARY_OP) {
+ if (EncodingMap[opCode].fieldLoc[2].kind == kFmtShift)
+ return newLIR3(cUnit, opCode, rDestSrc1, rSrc2, shift);
+ else
+ return newLIR3(cUnit, opCode, rDestSrc1, rDestSrc1, rSrc2);
+ } else if (EncodingMap[opCode].flags & IS_QUAD_OP)
+ return newLIR4(cUnit, opCode, rDestSrc1, rDestSrc1, rSrc2, shift);
+ else {
+ assert(0);
+ return NULL;
+ }
+}
+
+static ArmLIR *opRegReg(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
+ int rSrc2)
+{
+ return opRegRegShift(cUnit, op, rDestSrc1, rSrc2, 0);
+}
+
+static ArmLIR *opRegRegRegShift(CompilationUnit *cUnit, OpKind op,
+ int rDest, int rSrc1, int rSrc2, int shift)
+{
+ ArmOpCode opCode = kThumbBkpt;
+ bool thumbForm = (shift == 0) && LOWREG(rDest) && LOWREG(rSrc1) &&
+ LOWREG(rSrc2);
+ switch (op) {
+ case kOpAdd:
+ opCode = (thumbForm) ? kThumbAddRRR : kThumb2AddRRR;
+ break;
+ case kOpSub:
+ opCode = (thumbForm) ? kThumbSubRRR : kThumb2SubRRR;
+ break;
+ case kOpAdc:
+ opCode = kThumb2AdcRRR;
+ break;
+ case kOpAnd:
+ opCode = kThumb2AndRRR;
+ break;
+ case kOpBic:
+ opCode = kThumb2BicRRR;
+ break;
+ case kOpXor:
+ opCode = kThumb2EorRRR;
+ break;
+ case kOpMul:
+ assert(shift == 0);
+ opCode = kThumb2MulRRR;
+ break;
+ case kOpOr:
+ opCode = kThumb2OrrRRR;
+ break;
+ case kOpSbc:
+ opCode = kThumb2SbcRRR;
+ break;
+ case kOpLsl:
+ assert(shift == 0);
+ opCode = kThumb2LslRRR;
+ break;
+ case kOpLsr:
+ assert(shift == 0);
+ opCode = kThumb2LsrRRR;
+ break;
+ case kOpAsr:
+ assert(shift == 0);
+ opCode = kThumb2AsrRRR;
+ break;
+ case kOpRor:
+ assert(shift == 0);
+ opCode = kThumb2RorRRR;
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ assert(opCode >= 0);
+ if (EncodingMap[opCode].flags & IS_QUAD_OP)
+ return newLIR4(cUnit, opCode, rDest, rSrc1, rSrc2, shift);
+ else {
+ assert(EncodingMap[opCode].flags & IS_TERTIARY_OP);
+ return newLIR3(cUnit, opCode, rDest, rSrc1, rSrc2);
+ }
+}
+
+static ArmLIR *opRegRegReg(CompilationUnit *cUnit, OpKind op, int rDest,
+ int rSrc1, int rSrc2)
+{
+ return opRegRegRegShift(cUnit, op, rDest, rSrc1, rSrc2, 0);
+}
+
+static ArmLIR *opRegRegImm(CompilationUnit *cUnit, OpKind op, int rDest,
+ int rSrc1, int value)
+{
+ ArmLIR *res;
+ bool neg = (value < 0);
+ int absValue = (neg) ? -value : value;
+ ArmOpCode opCode = kThumbBkpt;
+ ArmOpCode altOpCode = kThumbBkpt;
+ bool allLowRegs = (LOWREG(rDest) && LOWREG(rSrc1));
+ int modImm = modifiedImmediate(value);
+ int modImmNeg = modifiedImmediate(-value);
+
+ switch(op) {
+ case kOpLsl:
+ if (allLowRegs)
+ return newLIR3(cUnit, kThumbLslRRI5, rDest, rSrc1, value);
+ else
+ return newLIR3(cUnit, kThumb2LslRRI5, rDest, rSrc1, value);
+ case kOpLsr:
+ if (allLowRegs)
+ return newLIR3(cUnit, kThumbLsrRRI5, rDest, rSrc1, value);
+ else
+ return newLIR3(cUnit, kThumb2LsrRRI5, rDest, rSrc1, value);
+ case kOpAsr:
+ if (allLowRegs)
+ return newLIR3(cUnit, kThumbAsrRRI5, rDest, rSrc1, value);
+ else
+ return newLIR3(cUnit, kThumb2AsrRRI5, rDest, rSrc1, value);
+ case kOpRor:
+ return newLIR3(cUnit, kThumb2RorRRI5, rDest, rSrc1, value);
+ case kOpAdd:
+ if (LOWREG(rDest) && (rSrc1 == 13) &&
+ (value <= 1020) && ((value & 0x3)==0)) {
+ return newLIR3(cUnit, kThumbAddSpRel, rDest, rSrc1,
+ value >> 2);
+ } else if (LOWREG(rDest) && (rSrc1 == rpc) &&
+ (value <= 1020) && ((value & 0x3)==0)) {
+ return newLIR3(cUnit, kThumbAddPcRel, rDest, rSrc1,
+ value >> 2);
+ }
+ opCode = kThumb2AddRRI8;
+ altOpCode = kThumb2AddRRR;
+ // Note: intentional fallthrough
+ case kOpSub:
+ if (allLowRegs && ((absValue & 0x7) == absValue)) {
+ if (op == kOpAdd)
+ opCode = (neg) ? kThumbSubRRI3 : kThumbAddRRI3;
+ else
+ opCode = (neg) ? kThumbAddRRI3 : kThumbSubRRI3;
+ return newLIR3(cUnit, opCode, rDest, rSrc1, absValue);
+ } else if ((absValue & 0xff) == absValue) {
+ if (op == kOpAdd)
+ opCode = (neg) ? kThumb2SubRRI12 : kThumb2AddRRI12;
+ else
+ opCode = (neg) ? kThumb2AddRRI12 : kThumb2SubRRI12;
+ return newLIR3(cUnit, opCode, rDest, rSrc1, absValue);
+ }
+ if (modImmNeg >= 0) {
+ op = (op == kOpAdd) ? kOpSub : kOpAdd;
+ modImm = modImmNeg;
+ }
+ if (op == kOpSub) {
+ opCode = kThumb2SubRRI8;
+ altOpCode = kThumb2SubRRR;
+ }
+ break;
+ case kOpAdc:
+ opCode = kThumb2AdcRRI8;
+ altOpCode = kThumb2AdcRRR;
+ break;
+ case kOpSbc:
+ opCode = kThumb2SbcRRI8;
+ altOpCode = kThumb2SbcRRR;
+ break;
+ case kOpOr:
+ opCode = kThumb2OrrRRI8;
+ altOpCode = kThumb2OrrRRR;
+ break;
+ case kOpAnd:
+ opCode = kThumb2AndRRI8;
+ altOpCode = kThumb2AndRRR;
+ break;
+ case kOpXor:
+ opCode = kThumb2EorRRI8;
+ altOpCode = kThumb2EorRRR;
+ break;
+ case kOpMul:
+ //TUNING: power of 2, shift & add
+ modImm = -1;
+ altOpCode = kThumb2MulRRR;
+ break;
+ case kOpCmp: {
+ int modImm = modifiedImmediate(value);
+ ArmLIR *res;
+ if (modImm >= 0) {
+ res = newLIR2(cUnit, kThumb2CmpRI8, rSrc1, modImm);
+ } else {
+ int rTmp = dvmCompilerAllocTemp(cUnit);
+ res = loadConstant(cUnit, rTmp, value);
+ opRegReg(cUnit, kOpCmp, rSrc1, rTmp);
+ dvmCompilerFreeTemp(cUnit, rTmp);
+ }
+ return res;
+ }
+ default:
+ assert(0);
+ }
+
+ if (modImm >= 0) {
+ return newLIR3(cUnit, opCode, rDest, rSrc1, modImm);
+ } else {
+ int rScratch = dvmCompilerAllocTemp(cUnit);
+ loadConstant(cUnit, rScratch, value);
+ if (EncodingMap[altOpCode].flags & IS_QUAD_OP)
+ res = newLIR4(cUnit, altOpCode, rDest, rSrc1, rScratch, 0);
+ else
+ res = newLIR3(cUnit, altOpCode, rDest, rSrc1, rScratch);
+ dvmCompilerFreeTemp(cUnit, rScratch);
+ return res;
+ }
+}
+
+/* Handle Thumb-only variants here - otherwise punt to opRegRegImm */
+static ArmLIR *opRegImm(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
+ int value)
+{
+ bool neg = (value < 0);
+ int absValue = (neg) ? -value : value;
+ bool shortForm = (((absValue & 0xff) == absValue) && LOWREG(rDestSrc1));
+ ArmOpCode opCode = kThumbBkpt;
+ switch (op) {
+ case kOpAdd:
+ if ( !neg && (rDestSrc1 == 13) && (value <= 508)) { /* sp */
+ assert((value & 0x3) == 0);
+ return newLIR1(cUnit, kThumbAddSpI7, value >> 2);
+ } else if (shortForm) {
+ opCode = (neg) ? kThumbSubRI8 : kThumbAddRI8;
+ }
+ break;
+ case kOpSub:
+ if (!neg && (rDestSrc1 == 13) && (value <= 508)) { /* sp */
+ assert((value & 0x3) == 0);
+ return newLIR1(cUnit, kThumbSubSpI7, value >> 2);
+ } else if (shortForm) {
+ opCode = (neg) ? kThumbAddRI8 : kThumbSubRI8;
+ }
+ break;
+ case kOpCmp:
+ if (LOWREG(rDestSrc1) && shortForm)
+ opCode = (shortForm) ? kThumbCmpRI8 : kThumbCmpRR;
+ else if (LOWREG(rDestSrc1))
+ opCode = kThumbCmpRR;
+ else {
+ shortForm = false;
+ opCode = kThumbCmpHL;
+ }
+ break;
+ default:
+ /* Punt to opRegRegImm - if bad case catch it there */
+ shortForm = false;
+ break;
+ }
+ if (shortForm)
+ return newLIR2(cUnit, opCode, rDestSrc1, absValue);
+ else {
+ return opRegRegImm(cUnit, op, rDestSrc1, rDestSrc1, value);
+ }
+}
+
+/*
+ * Determine whether value can be encoded as a Thumb2 floating point
+ * immediate. If not, return -1. If so return encoded 8-bit value.
+ */
+static int encodeImmDoubleHigh(int value)
+{
+ int res;
+ int bitA = (value & 0x80000000) >> 31;
+ int notBitB = (value & 0x40000000) >> 30;
+ int bitB = (value & 0x20000000) >> 29;
+ int bSmear = (value & 0x3fc00000) >> 22;
+ int slice = (value & 0x003f0000) >> 16;
+ int zeroes = (value & 0x0000ffff);
+ if (zeroes != 0)
+ return -1;
+ if (bitB) {
+ if ((notBitB != 0) || (bSmear != 0x1f))
+ return -1;
+ } else {
+ if ((notBitB != 1) || (bSmear != 0x0))
+ return -1;
+ }
+ res = (bitA << 7) | (bitB << 6) | slice;
+ return res;
+}
+
+static int encodeImmDouble(int valLo, int valHi)
+{
+ int res = -1;
+ if (valLo == 0)
+ res = encodeImmDoubleHigh(valHi);
+ return res;
+}
+
+static ArmLIR *loadConstantValueWide(CompilationUnit *cUnit, int rDestLo,
+ int rDestHi, int valLo, int valHi)
+{
+ int encodedImm = encodeImmDouble(valLo, valHi);
+ ArmLIR *res;
+ if (FPREG(rDestLo) && (encodedImm >= 0)) {
+ res = newLIR2(cUnit, kThumb2Vmovd_IMM8, S2D(rDestLo, rDestHi),
+ encodedImm);
+ } else {
+ res = loadConstantNoClobber(cUnit, rDestLo, valLo);
+ loadConstantNoClobber(cUnit, rDestHi, valHi);
+ }
+ return res;
+}
+
+static int encodeShift(int code, int amount) {
+ return ((amount & 0x1f) << 2) | code;
+}
+
+static ArmLIR *loadBaseIndexed(CompilationUnit *cUnit, int rBase,
+ int rIndex, int rDest, int scale, OpSize size)
+{
+ bool allLowRegs = LOWREG(rBase) && LOWREG(rIndex) && LOWREG(rDest);
+ ArmLIR *load;
+ ArmOpCode opCode = kThumbBkpt;
+ bool thumbForm = (allLowRegs && (scale == 0));
+ int regPtr;
+
+ if (FPREG(rDest)) {
+ assert(SINGLEREG(rDest));
+ assert((size == kWord) || (size == kSingle));
+ opCode = kThumb2Vldrs;
+ size = kSingle;
+ } else {
+ if (size == kSingle)
+ size = kWord;
+ }
+
+ switch (size) {
+ case kSingle:
+ regPtr = dvmCompilerAllocTemp(cUnit);
+ if (scale) {
+ newLIR4(cUnit, kThumb2AddRRR, regPtr, rBase, rIndex,
+ encodeShift(kArmLsl, scale));
+ } else {
+ opRegRegReg(cUnit, kOpAdd, regPtr, rBase, rIndex);
+ }
+ load = newLIR3(cUnit, opCode, rDest, regPtr, 0);
+#if defined(WITH_SELF_VERIFICATION)
+ if (cUnit->heapMemOp)
+ load->branchInsertSV = true;
+#endif
+ return load;
+ case kWord:
+ opCode = (thumbForm) ? kThumbLdrRRR : kThumb2LdrRRR;
+ break;
+ case kUnsignedHalf:
+ opCode = (thumbForm) ? kThumbLdrhRRR : kThumb2LdrhRRR;
+ break;
+ case kSignedHalf:
+ opCode = (thumbForm) ? kThumbLdrshRRR : kThumb2LdrshRRR;
+ break;
+ case kUnsignedByte:
+ opCode = (thumbForm) ? kThumbLdrbRRR : kThumb2LdrbRRR;
+ break;
+ case kSignedByte:
+ opCode = (thumbForm) ? kThumbLdrsbRRR : kThumb2LdrsbRRR;
+ break;
+ default:
+ assert(0);
+ }
+ if (thumbForm)
+ load = newLIR3(cUnit, opCode, rDest, rBase, rIndex);
+ else
+ load = newLIR4(cUnit, opCode, rDest, rBase, rIndex, scale);
+
+#if defined(WITH_SELF_VERIFICATION)
+ if (cUnit->heapMemOp)
+ load->branchInsertSV = true;
+#endif
+ return load;
+}
+
+static ArmLIR *storeBaseIndexed(CompilationUnit *cUnit, int rBase,
+ int rIndex, int rSrc, int scale, OpSize size)
+{
+ bool allLowRegs = LOWREG(rBase) && LOWREG(rIndex) && LOWREG(rSrc);
+ ArmLIR *store;
+ ArmOpCode opCode = kThumbBkpt;
+ bool thumbForm = (allLowRegs && (scale == 0));
+ int regPtr;
+
+ if (FPREG(rSrc)) {
+ assert(SINGLEREG(rSrc));
+ assert((size == kWord) || (size == kSingle));
+ opCode = kThumb2Vstrs;
+ size = kSingle;
+ } else {
+ if (size == kSingle)
+ size = kWord;
+ }
+
+ switch (size) {
+ case kSingle:
+ regPtr = dvmCompilerAllocTemp(cUnit);
+ if (scale) {
+ newLIR4(cUnit, kThumb2AddRRR, regPtr, rBase, rIndex,
+ encodeShift(kArmLsl, scale));
+ } else {
+ opRegRegReg(cUnit, kOpAdd, regPtr, rBase, rIndex);
+ }
+ store = newLIR3(cUnit, opCode, rSrc, regPtr, 0);
+#if defined(WITH_SELF_VERIFICATION)
+ if (cUnit->heapMemOp)
+ store->branchInsertSV = true;
+#endif
+ return store;
+ case kWord:
+ opCode = (thumbForm) ? kThumbStrRRR : kThumb2StrRRR;
+ break;
+ case kUnsignedHalf:
+ case kSignedHalf:
+ opCode = (thumbForm) ? kThumbStrhRRR : kThumb2StrhRRR;
+ break;
+ case kUnsignedByte:
+ case kSignedByte:
+ opCode = (thumbForm) ? kThumbStrbRRR : kThumb2StrbRRR;
+ break;
+ default:
+ assert(0);
+ }
+ if (thumbForm)
+ store = newLIR3(cUnit, opCode, rSrc, rBase, rIndex);
+ else
+ store = newLIR4(cUnit, opCode, rSrc, rBase, rIndex, scale);
+
+#if defined(WITH_SELF_VERIFICATION)
+ if (cUnit->heapMemOp)
+ store->branchInsertSV = true;
+#endif
+ return store;
+}
+
+/*
+ * Load value from base + displacement. Optionally perform null check
+ * on base (which must have an associated sReg and MIR). If not
+ * performing null check, incoming MIR can be null.
+ */
+static ArmLIR *loadBaseDispBody(CompilationUnit *cUnit, MIR *mir, int rBase,
+ int displacement, int rDest, int rDestHi,
+ OpSize size, int sReg)
+{
+ ArmLIR *res, *load;
+ ArmOpCode opCode = kThumbBkpt;
+ bool shortForm = false;
+ bool thumb2Form = (displacement < 4092 && displacement >= 0);
+ bool allLowRegs = (LOWREG(rBase) && LOWREG(rDest));
+ int encodedDisp = displacement;
+
+ switch (size) {
+ case kDouble:
+ case kLong:
+ if (FPREG(rDest)) {
+ if (SINGLEREG(rDest)) {
+ assert(FPREG(rDestHi));
+ rDest = S2D(rDest, rDestHi);
+ }
+ opCode = kThumb2Vldrd;
+ if (displacement <= 1020) {
+ shortForm = true;
+ encodedDisp >>= 2;
+ }
+ break;
+ } else {
+ res = loadBaseDispBody(cUnit, mir, rBase, displacement, rDest,
+ -1, kWord, sReg);
+ loadBaseDispBody(cUnit, NULL, rBase, displacement + 4, rDestHi,
+ -1, kWord, INVALID_SREG);
+ return res;
+ }
+ case kSingle:
+ case kWord:
+ if (FPREG(rDest)) {
+ opCode = kThumb2Vldrs;
+ if (displacement <= 1020) {
+ shortForm = true;
+ encodedDisp >>= 2;
+ }
+ break;
+ }
+ if (LOWREG(rDest) && (rBase == rpc) &&
+ (displacement <= 1020) && (displacement >= 0)) {
+ shortForm = true;
+ encodedDisp >>= 2;
+ opCode = kThumbLdrPcRel;
+ } else if (LOWREG(rDest) && (rBase == r13) &&
+ (displacement <= 1020) && (displacement >= 0)) {
+ shortForm = true;
+ encodedDisp >>= 2;
+ opCode = kThumbLdrSpRel;
+ } else if (allLowRegs && displacement < 128 && displacement >= 0) {
+ assert((displacement & 0x3) == 0);
+ shortForm = true;
+ encodedDisp >>= 2;
+ opCode = kThumbLdrRRI5;
+ } else if (thumb2Form) {
+ shortForm = true;
+ opCode = kThumb2LdrRRI12;
+ }
+ break;
+ case kUnsignedHalf:
+ if (allLowRegs && displacement < 64 && displacement >= 0) {
+ assert((displacement & 0x1) == 0);
+ shortForm = true;
+ encodedDisp >>= 1;
+ opCode = kThumbLdrhRRI5;
+ } else if (displacement < 4092 && displacement >= 0) {
+ shortForm = true;
+ opCode = kThumb2LdrhRRI12;
+ }
+ break;
+ case kSignedHalf:
+ if (thumb2Form) {
+ shortForm = true;
+ opCode = kThumb2LdrshRRI12;
+ }
+ break;
+ case kUnsignedByte:
+ if (allLowRegs && displacement < 32 && displacement >= 0) {
+ shortForm = true;
+ opCode = kThumbLdrbRRI5;
+ } else if (thumb2Form) {
+ shortForm = true;
+ opCode = kThumb2LdrbRRI12;
+ }
+ break;
+ case kSignedByte:
+ if (thumb2Form) {
+ shortForm = true;
+ opCode = kThumb2LdrsbRRI12;
+ }
+ break;
+ default:
+ assert(0);
+ }
+
+ if (shortForm) {
+ load = res = newLIR3(cUnit, opCode, rDest, rBase, encodedDisp);
+ } else {
+ int regOffset = dvmCompilerAllocTemp(cUnit);
+ res = loadConstant(cUnit, regOffset, encodedDisp);
+ load = loadBaseIndexed(cUnit, rBase, regOffset, rDest, 0, size);
+ dvmCompilerFreeTemp(cUnit, regOffset);
+ }
+
+ if (rBase == rFP) {
+ annotateDalvikRegAccess(load, displacement >> 2, true /* isLoad */);
+ }
+#if defined(WITH_SELF_VERIFICATION)
+ if (cUnit->heapMemOp)
+ load->branchInsertSV = true;
+#endif
+ return res;
+}
+
+static ArmLIR *loadBaseDisp(CompilationUnit *cUnit, MIR *mir, int rBase,
+ int displacement, int rDest, OpSize size,
+ int sReg)
+{
+ return loadBaseDispBody(cUnit, mir, rBase, displacement, rDest, -1,
+ size, sReg);
+}
+
+static ArmLIR *loadBaseDispWide(CompilationUnit *cUnit, MIR *mir, int rBase,
+ int displacement, int rDestLo, int rDestHi,
+ int sReg)
+{
+ return loadBaseDispBody(cUnit, mir, rBase, displacement, rDestLo, rDestHi,
+ kLong, sReg);
+}
+
+
+static ArmLIR *storeBaseDispBody(CompilationUnit *cUnit, int rBase,
+ int displacement, int rSrc, int rSrcHi,
+ OpSize size)
+{
+ ArmLIR *res, *store;
+ ArmOpCode opCode = kThumbBkpt;
+ bool shortForm = false;
+ bool thumb2Form = (displacement < 4092 && displacement >= 0);
+ bool allLowRegs = (LOWREG(rBase) && LOWREG(rSrc));
+ int encodedDisp = displacement;
+
+ switch (size) {
+ case kLong:
+ case kDouble:
+ if (!FPREG(rSrc)) {
+ res = storeBaseDispBody(cUnit, rBase, displacement, rSrc,
+ -1, kWord);
+ storeBaseDispBody(cUnit, rBase, displacement + 4, rSrcHi,
+ -1, kWord);
+ return res;
+ }
+ if (SINGLEREG(rSrc)) {
+ assert(FPREG(rSrcHi));
+ rSrc = S2D(rSrc, rSrcHi);
+ }
+ opCode = kThumb2Vstrd;
+ if (displacement <= 1020) {
+ shortForm = true;
+ encodedDisp >>= 2;
+ }
+ break;
+ case kSingle:
+ case kWord:
+ if (FPREG(rSrc)) {
+ assert(SINGLEREG(rSrc));
+ opCode = kThumb2Vstrs;
+ if (displacement <= 1020) {
+ shortForm = true;
+ encodedDisp >>= 2;
+ }
+ break;
+ }
+ if (allLowRegs && displacement < 128 && displacement >= 0) {
+ assert((displacement & 0x3) == 0);
+ shortForm = true;
+ encodedDisp >>= 2;
+ opCode = kThumbStrRRI5;
+ } else if (thumb2Form) {
+ shortForm = true;
+ opCode = kThumb2StrRRI12;
+ }
+ break;
+ case kUnsignedHalf:
+ case kSignedHalf:
+ if (allLowRegs && displacement < 64 && displacement >= 0) {
+ assert((displacement & 0x1) == 0);
+ shortForm = true;
+ encodedDisp >>= 1;
+ opCode = kThumbStrhRRI5;
+ } else if (thumb2Form) {
+ shortForm = true;
+ opCode = kThumb2StrhRRI12;
+ }
+ break;
+ case kUnsignedByte:
+ case kSignedByte:
+ if (allLowRegs && displacement < 32 && displacement >= 0) {
+ shortForm = true;
+ opCode = kThumbStrbRRI5;
+ } else if (thumb2Form) {
+ shortForm = true;
+ opCode = kThumb2StrbRRI12;
+ }
+ break;
+ default:
+ assert(0);
+ }
+ if (shortForm) {
+ store = res = newLIR3(cUnit, opCode, rSrc, rBase, encodedDisp);
+ } else {
+ int rScratch = dvmCompilerAllocTemp(cUnit);
+ res = loadConstant(cUnit, rScratch, encodedDisp);
+ store = storeBaseIndexed(cUnit, rBase, rScratch, rSrc, 0, size);
+ dvmCompilerFreeTemp(cUnit, rScratch);
+ }
+
+ if (rBase == rFP) {
+ annotateDalvikRegAccess(store, displacement >> 2, false /* isLoad */);
+ }
+#if defined(WITH_SELF_VERIFICATION)
+ if (cUnit->heapMemOp)
+ store->branchInsertSV = true;
+#endif
+ return res;
+}
+
+static ArmLIR *storeBaseDisp(CompilationUnit *cUnit, int rBase,
+ int displacement, int rSrc, OpSize size)
+{
+ return storeBaseDispBody(cUnit, rBase, displacement, rSrc, -1, size);
+}
+
+static ArmLIR *storeBaseDispWide(CompilationUnit *cUnit, int rBase,
+ int displacement, int rSrcLo, int rSrcHi)
+{
+ return storeBaseDispBody(cUnit, rBase, displacement, rSrcLo, rSrcHi, kLong);
+}
+
+static ArmLIR *loadMultiple(CompilationUnit *cUnit, int rBase, int rMask)
+{
+ ArmLIR *res;
+ genBarrier(cUnit);
+ if (LOWREG(rBase) && ((rMask & 0xff)==rMask)) {
+ res = newLIR2(cUnit, kThumbLdmia, rBase, rMask);
+ } else {
+ res = newLIR2(cUnit, kThumb2Ldmia, rBase, rMask);
+ }
+#if defined(WITH_SELF_VERIFICATION)
+ if (cUnit->heapMemOp)
+ res->branchInsertSV = true;
+#endif
+ genBarrier(cUnit);
+ return res;
+}
+
+static ArmLIR *storeMultiple(CompilationUnit *cUnit, int rBase, int rMask)
+{
+ ArmLIR *res;
+ genBarrier(cUnit);
+ if (LOWREG(rBase) && ((rMask & 0xff)==rMask)) {
+ res = newLIR2(cUnit, kThumbStmia, rBase, rMask);
+ } else {
+ res = newLIR2(cUnit, kThumb2Stmia, rBase, rMask);
+ }
+#if defined(WITH_SELF_VERIFICATION)
+ if (cUnit->heapMemOp)
+ res->branchInsertSV = true;
+#endif
+ genBarrier(cUnit);
+ return res;
+}
+
+static void storePair(CompilationUnit *cUnit, int base, int lowReg, int highReg)
+{
+ storeBaseDispWide(cUnit, base, 0, lowReg, highReg);
+}
+
+static void loadPair(CompilationUnit *cUnit, int base, int lowReg, int highReg)
+{
+ loadBaseDispWide(cUnit, NULL, base, 0, lowReg, highReg, INVALID_SREG);
+}
+
+
+/*
+ * Perform a "reg cmp imm" operation and jump to the PCR region if condition
+ * satisfies.
+ */
+static ArmLIR *genRegImmCheck(CompilationUnit *cUnit,
+ ArmConditionCode cond, int reg,
+ int checkValue, int dOffset,
+ ArmLIR *pcrLabel)
+{
+ ArmLIR *branch;
+ int modImm;
+ if ((LOWREG(reg)) && (checkValue == 0) &&
+ ((cond == kArmCondEq) || (cond == kArmCondNe))) {
+ branch = newLIR2(cUnit,
+ (cond == kArmCondEq) ? kThumb2Cbz : kThumb2Cbnz,
+ reg, 0);
+ } else {
+ modImm = modifiedImmediate(checkValue);
+ if (LOWREG(reg) && ((checkValue & 0xff) == checkValue)) {
+ newLIR2(cUnit, kThumbCmpRI8, reg, checkValue);
+ } else if (modImm >= 0) {
+ newLIR2(cUnit, kThumb2CmpRI8, reg, modImm);
+ } else {
+ int tReg = dvmCompilerAllocTemp(cUnit);
+ loadConstant(cUnit, tReg, checkValue);
+ opRegReg(cUnit, kOpCmp, reg, tReg);
+ }
+ branch = newLIR2(cUnit, kThumbBCond, 0, cond);
+ }
+ return genCheckCommon(cUnit, dOffset, branch, pcrLabel);
+}
+
+static ArmLIR *fpRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
+{
+ ArmLIR* res = dvmCompilerNew(sizeof(ArmLIR), true);
+ res->operands[0] = rDest;
+ res->operands[1] = rSrc;
+ if (rDest == rSrc) {
+ res->isNop = true;
+ } else {
+ assert(DOUBLEREG(rDest) == DOUBLEREG(rSrc));
+ if (DOUBLEREG(rDest)) {
+ res->opCode = kThumb2Vmovd;
+ } else {
+ if (SINGLEREG(rDest)) {
+ res->opCode = SINGLEREG(rSrc) ? kThumb2Vmovs : kThumb2Fmsr;
+ } else {
+ assert(SINGLEREG(rSrc));
+ res->opCode = kThumb2Fmrs;
+ }
+ }
+ res->operands[0] = rDest;
+ res->operands[1] = rSrc;
+ }
+ setupResourceMasks(res);
+ return res;
+}
+
+static ArmLIR* genRegCopyNoInsert(CompilationUnit *cUnit, int rDest, int rSrc)
+{
+ ArmLIR* res;
+ ArmOpCode opCode;
+ if (FPREG(rDest) || FPREG(rSrc))
+ return fpRegCopy(cUnit, rDest, rSrc);
+ res = dvmCompilerNew(sizeof(ArmLIR), true);
+ if (LOWREG(rDest) && LOWREG(rSrc))
+ opCode = kThumbMovRR;
+ else if (!LOWREG(rDest) && !LOWREG(rSrc))
+ opCode = kThumbMovRR_H2H;
+ else if (LOWREG(rDest))
+ opCode = kThumbMovRR_H2L;
+ else
+ opCode = kThumbMovRR_L2H;
+
+ res->operands[0] = rDest;
+ res->operands[1] = rSrc;
+ res->opCode = opCode;
+ setupResourceMasks(res);
+ if (rDest == rSrc) {
+ res->isNop = true;
+ }
+ return res;
+}
+
+static ArmLIR* genRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
+{
+ ArmLIR *res = genRegCopyNoInsert(cUnit, rDest, rSrc);
+ dvmCompilerAppendLIR(cUnit, (LIR*)res);
+ return res;
+}
+
+static void genRegCopyWide(CompilationUnit *cUnit, int destLo, int destHi,
+ int srcLo, int srcHi)
+{
+ bool destFP = FPREG(destLo) && FPREG(destHi);
+ bool srcFP = FPREG(srcLo) && FPREG(srcHi);
+ assert(FPREG(srcLo) == FPREG(srcHi));
+ assert(FPREG(destLo) == FPREG(destHi));
+ if (destFP) {
+ if (srcFP) {
+ genRegCopy(cUnit, S2D(destLo, destHi), S2D(srcLo, srcHi));
+ } else {
+ newLIR3(cUnit, kThumb2Fmdrr, S2D(destLo, destHi), srcLo, srcHi);
+ }
+ } else {
+ if (srcFP) {
+ newLIR3(cUnit, kThumb2Fmrrd, destLo, destHi, S2D(srcLo, srcHi));
+ } else {
+ // Handle overlap
+ if (srcHi == destLo) {
+ genRegCopy(cUnit, destHi, srcHi);
+ genRegCopy(cUnit, destLo, srcLo);
+ } else {
+ genRegCopy(cUnit, destLo, srcLo);
+ genRegCopy(cUnit, destHi, srcHi);
+ }
+ }
+ }
+}
diff --git a/vm/compiler/codegen/arm/Thumb2/Gen.c b/vm/compiler/codegen/arm/Thumb2/Gen.c
new file mode 100644
index 0000000..3632388
--- /dev/null
+++ b/vm/compiler/codegen/arm/Thumb2/Gen.c
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file contains codegen for the Thumb ISA and is intended to be
+ * includes by:
+ *
+ * Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+static void genNegFloat(CompilationUnit *cUnit, RegLocation rlDest,
+ RegLocation rlSrc)
+{
+ RegLocation rlResult;
+ rlSrc = loadValue(cUnit, rlSrc, kFPReg);
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
+ newLIR2(cUnit, kThumb2Vnegs, rlResult.lowReg, rlSrc.lowReg);
+ storeValue(cUnit, rlDest, rlResult);
+}
+
+static void genNegDouble(CompilationUnit *cUnit, RegLocation rlDest,
+ RegLocation rlSrc)
+{
+ RegLocation rlResult;
+ rlSrc = loadValueWide(cUnit, rlSrc, kFPReg);
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
+ newLIR2(cUnit, kThumb2Vnegd, S2D(rlResult.lowReg, rlResult.highReg),
+ S2D(rlSrc.lowReg, rlSrc.highReg));
+ storeValueWide(cUnit, rlDest, rlResult);
+}
+
+/*
+ * To avoid possible conflicts, we use a lot of temps here. Note that
+ * our usage of Thumb2 instruction forms avoids the problems with register
+ * reuse for multiply instructions prior to arm6.
+ */
+static void genMulLong(CompilationUnit *cUnit, RegLocation rlDest,
+ RegLocation rlSrc1, RegLocation rlSrc2)
+{
+ RegLocation rlResult;
+ int resLo = dvmCompilerAllocTemp(cUnit);
+ int resHi = dvmCompilerAllocTemp(cUnit);
+ int tmp1 = dvmCompilerAllocTemp(cUnit);
+
+ rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
+ rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
+
+ newLIR3(cUnit, kThumb2MulRRR, tmp1, rlSrc2.lowReg, rlSrc1.highReg);
+ newLIR4(cUnit, kThumb2Umull, resLo, resHi, rlSrc2.lowReg, rlSrc1.lowReg);
+ newLIR4(cUnit, kThumb2Mla, tmp1, rlSrc1.lowReg, rlSrc2.highReg, tmp1);
+ newLIR4(cUnit, kThumb2AddRRR, resHi, tmp1, resHi, 0);
+ dvmCompilerFreeTemp(cUnit, tmp1);
+
+ rlResult = dvmCompilerGetReturnWide(cUnit); // Just as a template, will patch
+ rlResult.lowReg = resLo;
+ rlResult.highReg = resHi;
+ storeValueWide(cUnit, rlDest, rlResult);
+}
+
+static void genLong3Addr(CompilationUnit *cUnit, MIR *mir, OpKind firstOp,
+ OpKind secondOp, RegLocation rlDest,
+ RegLocation rlSrc1, RegLocation rlSrc2)
+{
+ RegLocation rlResult;
+ rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
+ rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
+ rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+ opRegRegReg(cUnit, firstOp, rlResult.lowReg, rlSrc1.lowReg, rlSrc2.lowReg);
+ opRegRegReg(cUnit, secondOp, rlResult.highReg, rlSrc1.highReg,
+ rlSrc2.highReg);
+ storeValueWide(cUnit, rlDest, rlResult);
+}
+
+void dvmCompilerInitializeRegAlloc(CompilationUnit *cUnit)
+{
+ int numTemps = sizeof(coreTemps)/sizeof(int);
+ int numFPTemps = sizeof(fpTemps)/sizeof(int);
+ RegisterPool *pool = dvmCompilerNew(sizeof(*pool), true);
+ cUnit->regPool = pool;
+ pool->numCoreTemps = numTemps;
+ pool->coreTemps =
+ dvmCompilerNew(numTemps * sizeof(*cUnit->regPool->coreTemps), true);
+ pool->numFPTemps = numFPTemps;
+ pool->FPTemps =
+ dvmCompilerNew(numFPTemps * sizeof(*cUnit->regPool->FPTemps), true);
+ pool->numCoreRegs = 0;
+ pool->coreRegs = NULL;
+ pool->numFPRegs = 0;
+ pool->FPRegs = NULL;
+ dvmCompilerInitPool(pool->coreTemps, coreTemps, pool->numCoreTemps);
+ dvmCompilerInitPool(pool->FPTemps, fpTemps, pool->numFPTemps);
+ dvmCompilerInitPool(pool->coreRegs, NULL, 0);
+ dvmCompilerInitPool(pool->FPRegs, NULL, 0);
+ pool->nullCheckedRegs =
+ dvmCompilerAllocBitVector(cUnit->numSSARegs, false);
+}
+
+/*
+ * Generate a Thumb2 IT instruction, which can nullify up to
+ * four subsequent instructions based on a condition and its
+ * inverse. The condition applies to the first instruction, which
+ * is executed if the condition is met. The string "guide" consists
+ * of 0 to 3 chars, and applies to the 2nd through 4th instruction.
+ * A "T" means the instruction is executed if the condition is
+ * met, and an "E" means the instruction is executed if the condition
+ * is not met.
+ */
+static ArmLIR *genIT(CompilationUnit *cUnit, ArmConditionCode code,
+ char *guide)
+{
+ int mask;
+ int condBit = code & 1;
+ int altBit = condBit ^ 1;
+ int mask3 = 0;
+ int mask2 = 0;
+ int mask1 = 0;
+
+ //Note: case fallthroughs intentional
+ switch(strlen(guide)) {
+ case 3:
+ mask1 = (guide[2] == 'T') ? condBit : altBit;
+ case 2:
+ mask2 = (guide[1] == 'T') ? condBit : altBit;
+ case 1:
+ mask3 = (guide[0] == 'T') ? condBit : altBit;
+ break;
+ case 0:
+ break;
+ default:
+ LOGE("Jit: bad case in genIT");
+ dvmCompilerAbort(cUnit);
+ }
+ mask = (mask3 << 3) | (mask2 << 2) | (mask1 << 1) |
+ (1 << (3 - strlen(guide)));
+ return newLIR2(cUnit, kThumb2It, code, mask);
+}
+
+/* Export the Dalvik PC assicated with an instruction to the StackSave area */
+static ArmLIR *genExportPC(CompilationUnit *cUnit, MIR *mir)
+{
+ ArmLIR *res;
+ int offset = offsetof(StackSaveArea, xtra.currentPc);
+ int rDPC = dvmCompilerAllocTemp(cUnit);
+ res = loadConstant(cUnit, rDPC, (int) (cUnit->method->insns + mir->offset));
+ newLIR3(cUnit, kThumb2StrRRI8Predec, rDPC, rFP,
+ sizeof(StackSaveArea) - offset);
+ dvmCompilerFreeTemp(cUnit, rDPC);
+ return res;
+}
+
+/*
+ * Handle simple case (thin lock) inline. If it's complicated, bail
+ * out to the heavyweight lock/unlock routines. We'll use dedicated
+ * registers here in order to be in the right position in case we
+ * to bail to dvm[Lock/Unlock]Object(self, object)
+ *
+ * r0 -> self pointer [arg0 for dvm[Lock/Unlock]Object
+ * r1 -> object [arg1 for dvm[Lock/Unlock]Object
+ * r2 -> intial contents of object->lock, later result of strex
+ * r3 -> self->threadId
+ * r7 -> temp to hold new lock value [unlock only]
+ * r4 -> allow to be used by utilities as general temp
+ *
+ * The result of the strex is 0 if we acquire the lock.
+ *
+ * See comments in Sync.c for the layout of the lock word.
+ * Of particular interest to this code is the test for the
+ * simple case - which we handle inline. For monitor enter, the
+ * simple case is thin lock, held by no-one. For monitor exit,
+ * the simple case is thin lock, held by the unlocking thread with
+ * a recurse count of 0.
+ *
+ * A minor complication is that there is a field in the lock word
+ * unrelated to locking: the hash state. This field must be ignored, but
+ * preserved.
+ *
+ */
+static void genMonitorEnter(CompilationUnit *cUnit, MIR *mir)
+{
+ RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+ ArmLIR *target;
+ ArmLIR *hopTarget;
+ ArmLIR *branch;
+ ArmLIR *hopBranch;
+
+ assert(LW_SHAPE_THIN == 0);
+ loadValueDirectFixed(cUnit, rlSrc, r1); // Get obj
+ dvmCompilerLockAllTemps(cUnit); // Prepare for explicit register usage
+ dvmCompilerFreeTemp(cUnit, r4PC); // Free up r4 for general use
+ loadWordDisp(cUnit, rGLUE, offsetof(InterpState, self), r0); // Get self
+ genNullCheck(cUnit, rlSrc.sRegLow, r1, mir->offset, NULL);
+ loadWordDisp(cUnit, r0, offsetof(Thread, threadId), r3); // Get threadId
+ newLIR3(cUnit, kThumb2Ldrex, r2, r1,
+ offsetof(Object, lock) >> 2); // Get object->lock
+ opRegImm(cUnit, kOpLsl, r3, LW_LOCK_OWNER_SHIFT); // Align owner
+ // Is lock unheld on lock or held by us (==threadId) on unlock?
+ newLIR4(cUnit, kThumb2Bfi, r3, r2, 0, LW_LOCK_OWNER_SHIFT - 1);
+ newLIR3(cUnit, kThumb2Bfc, r2, LW_HASH_STATE_SHIFT,
+ LW_LOCK_OWNER_SHIFT - 1);
+ hopBranch = newLIR2(cUnit, kThumb2Cbnz, r2, 0);
+ newLIR4(cUnit, kThumb2Strex, r2, r3, r1, offsetof(Object, lock) >> 2);
+ dvmCompilerGenMemBarrier(cUnit);
+ branch = newLIR2(cUnit, kThumb2Cbz, r2, 0);
+
+ hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
+ hopTarget->defMask = ENCODE_ALL;
+ hopBranch->generic.target = (LIR *)hopTarget;
+
+ // Clear the lock
+ ArmLIR *inst = newLIR0(cUnit, kThumb2Clrex);
+ // ...and make it a scheduling barrier
+ inst->defMask = ENCODE_ALL;
+
+ // Export PC (part 1)
+ loadConstant(cUnit, r3, (int) (cUnit->method->insns + mir->offset));
+
+ /* Get dPC of next insn */
+ loadConstant(cUnit, r4PC, (int)(cUnit->method->insns + mir->offset +
+ dexGetInstrWidthAbs(gDvm.instrWidth, OP_MONITOR_ENTER)));
+ // Export PC (part 2)
+ newLIR3(cUnit, kThumb2StrRRI8Predec, r3, rFP,
+ sizeof(StackSaveArea) -
+ offsetof(StackSaveArea, xtra.currentPc));
+ /* Call template, and don't return */
+ genDispatchToHandler(cUnit, TEMPLATE_MONITOR_ENTER);
+ // Resume here
+ target = newLIR0(cUnit, kArmPseudoTargetLabel);
+ target->defMask = ENCODE_ALL;
+ branch->generic.target = (LIR *)target;
+}
+
+/*
+ * For monitor unlock, we don't have to use ldrex/strex. Once
+ * we've determined that the lock is thin and that we own it with
+ * a zero recursion count, it's safe to punch it back to the
+ * initial, unlock thin state with a store word.
+ */
+static void genMonitorExit(CompilationUnit *cUnit, MIR *mir)
+{
+ RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+ ArmLIR *target;
+ ArmLIR *branch;
+ ArmLIR *hopTarget;
+ ArmLIR *hopBranch;
+
+ assert(LW_SHAPE_THIN == 0);
+ loadValueDirectFixed(cUnit, rlSrc, r1); // Get obj
+ dvmCompilerLockAllTemps(cUnit); // Prepare for explicit register usage
+ dvmCompilerFreeTemp(cUnit, r4PC); // Free up r4 for general use
+ loadWordDisp(cUnit, rGLUE, offsetof(InterpState, self), r0); // Get self
+ genNullCheck(cUnit, rlSrc.sRegLow, r1, mir->offset, NULL);
+ loadWordDisp(cUnit, r1, offsetof(Object, lock), r2); // Get object->lock
+ loadWordDisp(cUnit, r0, offsetof(Thread, threadId), r3); // Get threadId
+ // Is lock unheld on lock or held by us (==threadId) on unlock?
+ opRegRegImm(cUnit, kOpAnd, r7, r2,
+ (LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT));
+ opRegImm(cUnit, kOpLsl, r3, LW_LOCK_OWNER_SHIFT); // Align owner
+ newLIR3(cUnit, kThumb2Bfc, r2, LW_HASH_STATE_SHIFT,
+ LW_LOCK_OWNER_SHIFT - 1);
+ opRegReg(cUnit, kOpSub, r2, r3);
+ hopBranch = opCondBranch(cUnit, kArmCondNe);
+ dvmCompilerGenMemBarrier(cUnit);
+ storeWordDisp(cUnit, r1, offsetof(Object, lock), r7);
+ branch = opNone(cUnit, kOpUncondBr);
+
+ hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
+ hopTarget->defMask = ENCODE_ALL;
+ hopBranch->generic.target = (LIR *)hopTarget;
+
+ // Export PC (part 1)
+ loadConstant(cUnit, r3, (int) (cUnit->method->insns + mir->offset));
+
+ LOAD_FUNC_ADDR(cUnit, r7, (int)dvmUnlockObject);
+ // Export PC (part 2)
+ newLIR3(cUnit, kThumb2StrRRI8Predec, r3, rFP,
+ sizeof(StackSaveArea) -
+ offsetof(StackSaveArea, xtra.currentPc));
+ opReg(cUnit, kOpBlx, r7);
+ opRegImm(cUnit, kOpCmp, r0, 0); /* Did we throw? */
+ ArmLIR *branchOver = opCondBranch(cUnit, kArmCondNe);
+ loadConstant(cUnit, r0,
+ (int) (cUnit->method->insns + mir->offset +
+ dexGetInstrWidthAbs(gDvm.instrWidth, OP_MONITOR_EXIT)));
+ genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON);
+
+ // Resume here
+ target = newLIR0(cUnit, kArmPseudoTargetLabel);
+ target->defMask = ENCODE_ALL;
+ branch->generic.target = (LIR *)target;
+ branchOver->generic.target = (LIR *) target;
+}
+
+static void genMonitor(CompilationUnit *cUnit, MIR *mir)
+{
+ if (mir->dalvikInsn.opCode == OP_MONITOR_ENTER)
+ genMonitorEnter(cUnit, mir);
+ else
+ genMonitorExit(cUnit, mir);
+}
+
+/*
+ * 64-bit 3way compare function.
+ * mov r7, #-1
+ * cmp op1hi, op2hi
+ * blt done
+ * bgt flip
+ * sub r7, op1lo, op2lo (treat as unsigned)
+ * beq done
+ * ite hi
+ * mov(hi) r7, #-1
+ * mov(!hi) r7, #1
+ * flip:
+ * neg r7
+ * done:
+ */
+static void genCmpLong(CompilationUnit *cUnit, MIR *mir,
+ RegLocation rlDest, RegLocation rlSrc1,
+ RegLocation rlSrc2)
+{
+ RegLocation rlTemp = LOC_C_RETURN; // Just using as template, will change
+ ArmLIR *target1;
+ ArmLIR *target2;
+ rlSrc1 = loadValueWide(cUnit, rlSrc1, kCoreReg);
+ rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg);
+ rlTemp.lowReg = dvmCompilerAllocTemp(cUnit);
+ loadConstant(cUnit, rlTemp.lowReg, -1);
+ opRegReg(cUnit, kOpCmp, rlSrc1.highReg, rlSrc2.highReg);
+ ArmLIR *branch1 = opCondBranch(cUnit, kArmCondLt);
+ ArmLIR *branch2 = opCondBranch(cUnit, kArmCondGt);
+ opRegRegReg(cUnit, kOpSub, rlTemp.lowReg, rlSrc1.lowReg, rlSrc2.lowReg);
+ ArmLIR *branch3 = opCondBranch(cUnit, kArmCondEq);
+
+ genIT(cUnit, kArmCondHi, "E");
+ newLIR2(cUnit, kThumb2MovImmShift, rlTemp.lowReg, modifiedImmediate(-1));
+ loadConstant(cUnit, rlTemp.lowReg, 1);
+ genBarrier(cUnit);
+
+ target2 = newLIR0(cUnit, kArmPseudoTargetLabel);
+ target2->defMask = -1;
+ opRegReg(cUnit, kOpNeg, rlTemp.lowReg, rlTemp.lowReg);
+
+ target1 = newLIR0(cUnit, kArmPseudoTargetLabel);
+ target1->defMask = -1;
+
+ storeValue(cUnit, rlDest, rlTemp);
+
+ branch1->generic.target = (LIR *)target1;
+ branch2->generic.target = (LIR *)target2;
+ branch3->generic.target = branch1->generic.target;
+}
+
+static bool genInlinedAbsFloat(CompilationUnit *cUnit, MIR *mir)
+{
+ RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
+ RegLocation rlDest = inlinedTarget(cUnit, mir, true);
+ rlSrc = loadValue(cUnit, rlSrc, kFPReg);
+ RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
+ newLIR2(cUnit, kThumb2Vabss, rlResult.lowReg, rlSrc.lowReg);
+ storeValue(cUnit, rlDest, rlResult);
+ return true;
+}
+
+static bool genInlinedAbsDouble(CompilationUnit *cUnit, MIR *mir)
+{
+ RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
+ RegLocation rlDest = inlinedTargetWide(cUnit, mir, true);
+ rlSrc = loadValueWide(cUnit, rlSrc, kFPReg);
+ RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
+ newLIR2(cUnit, kThumb2Vabsd, S2D(rlResult.lowReg, rlResult.highReg),
+ S2D(rlSrc.lowReg, rlSrc.highReg));
+ storeValueWide(cUnit, rlDest, rlResult);
+ return true;
+}
+
+static bool genInlinedMinMaxInt(CompilationUnit *cUnit, MIR *mir, bool isMin)
+{
+ RegLocation rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 0);
+ RegLocation rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 1);
+ rlSrc1 = loadValue(cUnit, rlSrc1, kCoreReg);
+ rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg);
+ RegLocation rlDest = inlinedTarget(cUnit, mir, false);
+ RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true);
+ opRegReg(cUnit, kOpCmp, rlSrc1.lowReg, rlSrc2.lowReg);
+ genIT(cUnit, (isMin) ? kArmCondGt : kArmCondLt, "E");
+ opRegReg(cUnit, kOpMov, rlResult.lowReg, rlSrc2.lowReg);
+ opRegReg(cUnit, kOpMov, rlResult.lowReg, rlSrc1.lowReg);
+ genBarrier(cUnit);
+ storeValue(cUnit, rlDest, rlResult);
+ return false;
+}
+
+static void genMultiplyByTwoBitMultiplier(CompilationUnit *cUnit,
+ RegLocation rlSrc, RegLocation rlResult, int lit,
+ int firstBit, int secondBit)
+{
+ opRegRegRegShift(cUnit, kOpAdd, rlResult.lowReg, rlSrc.lowReg, rlSrc.lowReg,
+ encodeShift(kArmLsl, secondBit - firstBit));
+ if (firstBit != 0) {
+ opRegRegImm(cUnit, kOpLsl, rlResult.lowReg, rlResult.lowReg, firstBit);
+ }
+}
diff --git a/vm/compiler/codegen/arm/Thumb2/Ralloc.c b/vm/compiler/codegen/arm/Thumb2/Ralloc.c
new file mode 100644
index 0000000..6adfd62
--- /dev/null
+++ b/vm/compiler/codegen/arm/Thumb2/Ralloc.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file contains codegen for the Thumb ISA and is intended to be
+ * includes by:
+ *
+ * Codegen-$(TARGET_ARCH_VARIANT).c
+ *
+ */
+
+/* Stress mode for testing: if defined will reverse corereg/floatreg hint */
+//#define REGCLASS_STRESS_MODE
+
+/*
+ * Alloc a pair of core registers, or a double. Low reg in low byte,
+ * high reg in next byte.
+ */
+int dvmCompilerAllocTypedTempPair(CompilationUnit *cUnit,
+ bool fpHint, int regClass)
+{
+ int highReg;
+ int lowReg;
+ int res = 0;
+
+#if defined(REGCLASS_STRESS_MODE)
+ fpHint = !fpHint;
+#endif
+
+ if (((regClass == kAnyReg) && fpHint) || (regClass == kFPReg)) {
+ lowReg = dvmCompilerAllocTempDouble(cUnit);
+ highReg = lowReg + 1;
+ } else {
+ lowReg = dvmCompilerAllocTemp(cUnit);
+ highReg = dvmCompilerAllocTemp(cUnit);
+ }
+ res = (lowReg & 0xff) | ((highReg & 0xff) << 8);
+ return res;
+}
+
+int dvmCompilerAllocTypedTemp(CompilationUnit *cUnit, bool fpHint,
+ int regClass)
+{
+#if defined(REGCLASS_STRESS_MODE)
+ fpHint = !fpHint;
+#endif
+ if (((regClass == kAnyReg) && fpHint) || (regClass == kFPReg))
+ return dvmCompilerAllocTempFloat(cUnit);
+ return dvmCompilerAllocTemp(cUnit);
+}
diff --git a/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.c b/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.c
new file mode 100644
index 0000000..6511eac
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file is included by Codegen-armv5te-vfp.c, and implements architecture
+ * variant-specific code.
+ */
+
+/*
+ * Determine the initial instruction set to be used for this trace.
+ * Later components may decide to change this.
+ */
+JitInstructionSetType dvmCompilerInstructionSet(void)
+{
+ return DALVIK_JIT_THUMB;
+}
+
+/* Architecture-specific initializations and checks go here */
+bool dvmCompilerArchVariantInit(void)
+{
+ /* First, declare dvmCompiler_TEMPLATE_XXX for each template */
+#define JIT_TEMPLATE(X) extern void dvmCompiler_TEMPLATE_##X();
+#include "../../../template/armv5te-vfp/TemplateOpList.h"
+#undef JIT_TEMPLATE
+
+ int i = 0;
+ extern void dvmCompilerTemplateStart(void);
+
+ /*
+ * Then, populate the templateEntryOffsets array with the offsets from the
+ * the dvmCompilerTemplateStart symbol for each template.
+ */
+#define JIT_TEMPLATE(X) templateEntryOffsets[i++] = \
+ (intptr_t) dvmCompiler_TEMPLATE_##X - (intptr_t) dvmCompilerTemplateStart;
+#include "../../../template/armv5te-vfp/TemplateOpList.h"
+#undef JIT_TEMPLATE
+
+ /* Target-specific configuration */
+ gDvmJit.jitTableSize = 1 << 9; // 512
+ gDvmJit.jitTableMask = gDvmJit.jitTableSize - 1;
+ gDvmJit.threshold = 200;
+ gDvmJit.codeCacheSize = 512*1024;
+
+#if defined(WITH_SELF_VERIFICATION)
+ /* Force into blocking mode */
+ gDvmJit.blockingMode = true;
+ gDvm.nativeDebuggerActive = true;
+#endif
+
+ /* Codegen-specific assumptions */
+ assert(offsetof(ClassObject, vtable) < 128 &&
+ (offsetof(ClassObject, vtable) & 0x3) == 0);
+ assert(offsetof(ArrayObject, length) < 128 &&
+ (offsetof(ArrayObject, length) & 0x3) == 0);
+ assert(offsetof(ArrayObject, contents) < 256);
+
+ /* Up to 5 args are pushed on top of FP - sizeofStackSaveArea */
+ assert(sizeof(StackSaveArea) < 236);
+
+ /*
+ * EA is calculated by doing "Rn + imm5 << 2", make sure that the last
+ * offset from the struct is less than 128.
+ */
+ assert((offsetof(InterpState, jitToInterpEntries) +
+ sizeof(struct JitToInterpEntries)) <= 128);
+ return true;
+}
+
+int dvmCompilerTargetOptHint(int key)
+{
+ int res;
+ switch (key) {
+ case kMaxHoistDistance:
+ res = 2;
+ break;
+ default:
+ LOGE("Unknown target optimization hint key: %d",key);
+ res = 0;
+ }
+ return res;
+}
+
+void dvmCompilerGenMemBarrier(CompilationUnit *cUnit)
+{
+#if ANDROID_SMP != 0
+#error armv5+smp not supported
+#endif
+}
diff --git a/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.h b/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.h
new file mode 100644
index 0000000..9f862e8
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV5TE_VFP_ARCHVARIANT_H
+#define _DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV5TE_VFP_ARCHVARIANT_H
+
+/* Create the TemplateOpcode enum */
+#define JIT_TEMPLATE(X) TEMPLATE_##X,
+typedef enum {
+#include "../../../template/armv5te-vfp/TemplateOpList.h"
+/*
+ * For example,
+ * TEMPLATE_CMP_LONG,
+ * TEMPLATE_RETURN,
+ * ...
+ */
+ TEMPLATE_LAST_MARK,
+} TemplateOpCode;
+#undef JIT_TEMPLATE
+
+#endif /* _DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV5TE_VFP_ARCHVARIANT_H */
diff --git a/vm/compiler/codegen/arm/armv5te-vfp/CallingConvention.S b/vm/compiler/codegen/arm/armv5te-vfp/CallingConvention.S
new file mode 100644
index 0000000..4f12395
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv5te-vfp/CallingConvention.S
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Save & restore for callee-save FP registers.
+ * On entry:
+ * r0 : pointer to save area of JIT_CALLEE_SAVE_WORD_SIZE
+ */
+ .text
+ .align 2
+ .global dvmJitCalleeSave
+ .type dvmJitCalleeSave, %function
+dvmJitCalleeSave:
+ vstmia r0, {d8-d15}
+ bx lr
+
+ .global dvmJitCalleeRestore
+ .type dvmJitCalleeRestore, %function
+dvmJitCalleeRestore:
+ vldmia r0, {d8-d15}
+ bx lr
diff --git a/vm/compiler/codegen/arm/armv5te-vfp/Codegen.c b/vm/compiler/codegen/arm/armv5te-vfp/Codegen.c
new file mode 100644
index 0000000..eda243f
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv5te-vfp/Codegen.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define _CODEGEN_C
+#define _ARMV5TE_VFP
+
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "libdex/OpCode.h"
+#include "libdex/OpCodeNames.h"
+#include "compiler/CompilerInternals.h"
+#include "compiler/codegen/arm/ArmLIR.h"
+#include "mterp/common/FindInterface.h"
+#include "compiler/codegen/arm/Ralloc.h"
+#include "compiler/codegen/arm/Codegen.h"
+#include "compiler/Loop.h"
+#include "ArchVariant.h"
+
+/* Architectural independent building blocks */
+#include "../CodegenCommon.c"
+
+/* Thumb-specific factory utilities */
+#include "../Thumb/Factory.c"
+/* Factory utilities dependent on arch-specific features */
+#include "../CodegenFactory.c"
+
+/* Thumb-specific codegen routines */
+#include "../Thumb/Gen.c"
+/* Thumb+VFP codegen routines */
+#include "../FP/ThumbVFP.c"
+
+/* Thumb-specific register allocation */
+#include "../Thumb/Ralloc.c"
+
+/* MIR2LIR dispatcher and architectural independent codegen routines */
+#include "../CodegenDriver.c"
+
+/* Architecture manifest */
+#include "ArchVariant.c"
diff --git a/vm/compiler/codegen/arm/armv5te/ArchVariant.c b/vm/compiler/codegen/arm/armv5te/ArchVariant.c
new file mode 100644
index 0000000..814f410
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv5te/ArchVariant.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This file is included by Codegen-armv5te.c, and implements architecture
+ * variant-specific code.
+ */
+
+/*
+ * Determine the initial instruction set to be used for this trace.
+ * Later components may decide to change this.
+ */
+JitInstructionSetType dvmCompilerInstructionSet(void)
+{
+ return DALVIK_JIT_THUMB;
+}
+
+/* Architecture-specific initializations and checks go here */
+bool dvmCompilerArchVariantInit(void)
+{
+ /* First, declare dvmCompiler_TEMPLATE_XXX for each template */
+#define JIT_TEMPLATE(X) extern void dvmCompiler_TEMPLATE_##X();
+#include "../../../template/armv5te/TemplateOpList.h"
+#undef JIT_TEMPLATE
+
+ int i = 0;
+ extern void dvmCompilerTemplateStart(void);
+
+ /*
+ * Then, populate the templateEntryOffsets array with the offsets from the
+ * the dvmCompilerTemplateStart symbol for each template.
+ */
+#define JIT_TEMPLATE(X) templateEntryOffsets[i++] = \
+ (intptr_t) dvmCompiler_TEMPLATE_##X - (intptr_t) dvmCompilerTemplateStart;
+#include "../../../template/armv5te/TemplateOpList.h"
+#undef JIT_TEMPLATE
+
+ /* Target-specific configuration */
+ gDvmJit.jitTableSize = 1 << 9; // 512
+ gDvmJit.jitTableMask = gDvmJit.jitTableSize - 1;
+ gDvmJit.threshold = 200;
+ gDvmJit.codeCacheSize = 512*1024;
+
+#if defined(WITH_SELF_VERIFICATION)
+ /* Force into blocking mode */
+ gDvmJit.blockingMode = true;
+ gDvm.nativeDebuggerActive = true;
+#endif
+
+ /* Codegen-specific assumptions */
+ assert(offsetof(ClassObject, vtable) < 128 &&
+ (offsetof(ClassObject, vtable) & 0x3) == 0);
+ assert(offsetof(ArrayObject, length) < 128 &&
+ (offsetof(ArrayObject, length) & 0x3) == 0);
+ assert(offsetof(ArrayObject, contents) < 256);
+
+ /* Up to 5 args are pushed on top of FP - sizeofStackSaveArea */
+ assert(sizeof(StackSaveArea) < 236);
+
+ /*
+ * EA is calculated by doing "Rn + imm5 << 2", make sure that the last
+ * offset from the struct is less than 128.
+ */
+ assert((offsetof(InterpState, jitToInterpEntries) +
+ sizeof(struct JitToInterpEntries)) <= 128);
+ return true;
+}
+
+int dvmCompilerTargetOptHint(int key)
+{
+ int res;
+ switch (key) {
+ case kMaxHoistDistance:
+ res = 2;
+ break;
+ default:
+ LOGE("Unknown target optimization hint key: %d",key);
+ res = 0;
+ }
+ return res;
+}
+
+void dvmCompilerGenMemBarrier(CompilationUnit *cUnit)
+{
+#if ANDROID_SMP != 0
+#error armv5+smp not supported
+#endif
+}
diff --git a/vm/compiler/codegen/arm/armv5te/ArchVariant.h b/vm/compiler/codegen/arm/armv5te/ArchVariant.h
new file mode 100644
index 0000000..6420df7
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv5te/ArchVariant.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV5TE_ARCHVARIANT_H
+#define _DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV5TE_ARCHVARIANT_H
+
+/* Create the TemplateOpcode enum */
+#define JIT_TEMPLATE(X) TEMPLATE_##X,
+typedef enum {
+#include "../../../template/armv5te/TemplateOpList.h"
+/*
+ * For example,
+ * TEMPLATE_CMP_LONG,
+ * TEMPLATE_RETURN,
+ * ...
+ */
+ TEMPLATE_LAST_MARK,
+} TemplateOpCode;
+#undef JIT_TEMPLATE
+
+#endif /* _DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV5TE_ARCHVARIANT_H */
diff --git a/vm/compiler/codegen/arm/armv5te/CallingConvention.S b/vm/compiler/codegen/arm/armv5te/CallingConvention.S
new file mode 100644
index 0000000..0cbc64f
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv5te/CallingConvention.S
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Save & restore for callee-save FP registers.
+ * On entry:
+ * r0 : pointer to save area of JIT_CALLEE_SAVE_WORD_SIZE
+ */
+ .text
+ .align 2
+ .global dvmJitCalleeSave
+ .type dvmJitCalleeSave, %function
+dvmJitCalleeSave:
+ bx lr
+
+ .global dvmJitCalleeRestore
+ .type dvmJitCalleeRestore, %function
+dvmJitCalleeRestore:
+ bx lr
diff --git a/vm/compiler/codegen/arm/armv5te/Codegen.c b/vm/compiler/codegen/arm/armv5te/Codegen.c
new file mode 100644
index 0000000..f953390
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv5te/Codegen.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define _CODEGEN_C
+#define _ARMV5TE
+
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "libdex/OpCode.h"
+#include "libdex/OpCodeNames.h"
+#include "compiler/CompilerInternals.h"
+#include "compiler/codegen/arm/ArmLIR.h"
+#include "mterp/common/FindInterface.h"
+#include "compiler/codegen/arm/Ralloc.h"
+#include "compiler/codegen/arm/Codegen.h"
+#include "compiler/Loop.h"
+#include "ArchVariant.h"
+
+/* Architectural independent building blocks */
+#include "../CodegenCommon.c"
+
+/* Architectural independent building blocks */
+#include "../Thumb/Factory.c"
+/* Factory utilities dependent on arch-specific features */
+#include "../CodegenFactory.c"
+
+/* Thumb-specific codegen routines */
+#include "../Thumb/Gen.c"
+/* Thumb+Portable FP codegen routines */
+#include "../FP/ThumbPortableFP.c"
+
+/* Thumb-specific register allocation */
+#include "../Thumb/Ralloc.c"
+
+/* MIR2LIR dispatcher and architectural independent codegen routines */
+#include "../CodegenDriver.c"
+
+/* Architecture manifest */
+#include "ArchVariant.c"
diff --git a/vm/compiler/codegen/arm/armv7-a-neon/ArchVariant.c b/vm/compiler/codegen/arm/armv7-a-neon/ArchVariant.c
new file mode 100644
index 0000000..f1727c6
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv7-a-neon/ArchVariant.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Determine the initial instruction set to be used for this trace.
+ * Later components may decide to change this.
+ */
+JitInstructionSetType dvmCompilerInstructionSet(void)
+{
+ return DALVIK_JIT_THUMB2;
+}
+
+/* Architecture-specific initializations and checks go here */
+bool dvmCompilerArchVariantInit(void)
+{
+ /* First, declare dvmCompiler_TEMPLATE_XXX for each template */
+#define JIT_TEMPLATE(X) extern void dvmCompiler_TEMPLATE_##X();
+#include "../../../template/armv5te-vfp/TemplateOpList.h"
+#undef JIT_TEMPLATE
+
+ int i = 0;
+ extern void dvmCompilerTemplateStart(void);
+
+ /*
+ * Then, populate the templateEntryOffsets array with the offsets from the
+ * the dvmCompilerTemplateStart symbol for each template.
+ */
+#define JIT_TEMPLATE(X) templateEntryOffsets[i++] = \
+ (intptr_t) dvmCompiler_TEMPLATE_##X - (intptr_t) dvmCompilerTemplateStart;
+#include "../../../template/armv5te-vfp/TemplateOpList.h"
+#undef JIT_TEMPLATE
+
+ /* Target-specific configuration */
+ gDvmJit.jitTableSize = 1 << 12; // 4096
+ gDvmJit.jitTableMask = gDvmJit.jitTableSize - 1;
+ gDvmJit.threshold = 40;
+ gDvmJit.codeCacheSize = 1024*1024;
+
+#if defined(WITH_SELF_VERIFICATION)
+ /* Force into blocking */
+ gDvmJit.blockingMode = true;
+ gDvm.nativeDebuggerActive = true;
+#endif
+
+ /* Codegen-specific assumptions */
+ assert(offsetof(ClassObject, vtable) < 128 &&
+ (offsetof(ClassObject, vtable) & 0x3) == 0);
+ assert(offsetof(ArrayObject, length) < 128 &&
+ (offsetof(ArrayObject, length) & 0x3) == 0);
+ assert(offsetof(ArrayObject, contents) < 256);
+
+ /* Up to 5 args are pushed on top of FP - sizeofStackSaveArea */
+ assert(sizeof(StackSaveArea) < 236);
+
+ /*
+ * EA is calculated by doing "Rn + imm5 << 2", make sure that the last
+ * offset from the struct is less than 128.
+ */
+ assert((offsetof(InterpState, jitToInterpEntries) +
+ sizeof(struct JitToInterpEntries)) <= 128);
+ return true;
+}
+
+int dvmCompilerTargetOptHint(int key)
+{
+ int res;
+ switch (key) {
+ case kMaxHoistDistance:
+ res = 7;
+ break;
+ default:
+ LOGE("Unknown target optimization hint key: %d",key);
+ res = 0;
+ }
+ return res;
+}
+
+void dvmCompilerGenMemBarrier(CompilationUnit *cUnit)
+{
+#if ANDROID_SMP != 0
+ ArmLIR *dmb = newLIR1(cUnit, kThumb2Dmb, kSY); // Full system DMB
+ dmb->defMask = ENCODE_ALL;
+#endif
+}
diff --git a/vm/compiler/codegen/arm/armv7-a-neon/ArchVariant.h b/vm/compiler/codegen/arm/armv7-a-neon/ArchVariant.h
new file mode 100644
index 0000000..9f862e8
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv7-a-neon/ArchVariant.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV5TE_VFP_ARCHVARIANT_H
+#define _DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV5TE_VFP_ARCHVARIANT_H
+
+/* Create the TemplateOpcode enum */
+#define JIT_TEMPLATE(X) TEMPLATE_##X,
+typedef enum {
+#include "../../../template/armv5te-vfp/TemplateOpList.h"
+/*
+ * For example,
+ * TEMPLATE_CMP_LONG,
+ * TEMPLATE_RETURN,
+ * ...
+ */
+ TEMPLATE_LAST_MARK,
+} TemplateOpCode;
+#undef JIT_TEMPLATE
+
+#endif /* _DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV5TE_VFP_ARCHVARIANT_H */
diff --git a/vm/compiler/codegen/arm/armv7-a-neon/CallingConvention.S b/vm/compiler/codegen/arm/armv7-a-neon/CallingConvention.S
new file mode 100644
index 0000000..4f12395
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv7-a-neon/CallingConvention.S
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Save & restore for callee-save FP registers.
+ * On entry:
+ * r0 : pointer to save area of JIT_CALLEE_SAVE_WORD_SIZE
+ */
+ .text
+ .align 2
+ .global dvmJitCalleeSave
+ .type dvmJitCalleeSave, %function
+dvmJitCalleeSave:
+ vstmia r0, {d8-d15}
+ bx lr
+
+ .global dvmJitCalleeRestore
+ .type dvmJitCalleeRestore, %function
+dvmJitCalleeRestore:
+ vldmia r0, {d8-d15}
+ bx lr
diff --git a/vm/compiler/codegen/arm/armv7-a-neon/Codegen.c b/vm/compiler/codegen/arm/armv7-a-neon/Codegen.c
new file mode 100644
index 0000000..1edc25b
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv7-a-neon/Codegen.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define _CODEGEN_C
+#define _ARMV7_A_NEON
+
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "libdex/OpCode.h"
+#include "libdex/OpCodeNames.h"
+#include "compiler/CompilerInternals.h"
+#include "compiler/codegen/arm/ArmLIR.h"
+#include "mterp/common/FindInterface.h"
+#include "compiler/codegen/arm/Ralloc.h"
+#include "compiler/codegen/arm/Codegen.h"
+#include "compiler/Loop.h"
+#include "ArchVariant.h"
+
+/* Architectural independent building blocks */
+#include "../CodegenCommon.c"
+
+/* Thumb2-specific factory utilities */
+#include "../Thumb2/Factory.c"
+/* Factory utilities dependent on arch-specific features */
+#include "../CodegenFactory.c"
+
+/* Thumb2-specific codegen routines */
+#include "../Thumb2/Gen.c"
+/* Thumb2+VFP codegen routines */
+#include "../FP/Thumb2VFP.c"
+
+/* Thumb2-specific register allocation */
+#include "../Thumb2/Ralloc.c"
+
+/* MIR2LIR dispatcher and architectural independent codegen routines */
+#include "../CodegenDriver.c"
+
+/* Architecture manifest */
+#include "ArchVariant.c"
diff --git a/vm/compiler/codegen/arm/armv7-a/ArchVariant.c b/vm/compiler/codegen/arm/armv7-a/ArchVariant.c
new file mode 100644
index 0000000..f1727c6
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv7-a/ArchVariant.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Determine the initial instruction set to be used for this trace.
+ * Later components may decide to change this.
+ */
+JitInstructionSetType dvmCompilerInstructionSet(void)
+{
+ return DALVIK_JIT_THUMB2;
+}
+
+/* Architecture-specific initializations and checks go here */
+bool dvmCompilerArchVariantInit(void)
+{
+ /* First, declare dvmCompiler_TEMPLATE_XXX for each template */
+#define JIT_TEMPLATE(X) extern void dvmCompiler_TEMPLATE_##X();
+#include "../../../template/armv5te-vfp/TemplateOpList.h"
+#undef JIT_TEMPLATE
+
+ int i = 0;
+ extern void dvmCompilerTemplateStart(void);
+
+ /*
+ * Then, populate the templateEntryOffsets array with the offsets from the
+ * the dvmCompilerTemplateStart symbol for each template.
+ */
+#define JIT_TEMPLATE(X) templateEntryOffsets[i++] = \
+ (intptr_t) dvmCompiler_TEMPLATE_##X - (intptr_t) dvmCompilerTemplateStart;
+#include "../../../template/armv5te-vfp/TemplateOpList.h"
+#undef JIT_TEMPLATE
+
+ /* Target-specific configuration */
+ gDvmJit.jitTableSize = 1 << 12; // 4096
+ gDvmJit.jitTableMask = gDvmJit.jitTableSize - 1;
+ gDvmJit.threshold = 40;
+ gDvmJit.codeCacheSize = 1024*1024;
+
+#if defined(WITH_SELF_VERIFICATION)
+ /* Force into blocking */
+ gDvmJit.blockingMode = true;
+ gDvm.nativeDebuggerActive = true;
+#endif
+
+ /* Codegen-specific assumptions */
+ assert(offsetof(ClassObject, vtable) < 128 &&
+ (offsetof(ClassObject, vtable) & 0x3) == 0);
+ assert(offsetof(ArrayObject, length) < 128 &&
+ (offsetof(ArrayObject, length) & 0x3) == 0);
+ assert(offsetof(ArrayObject, contents) < 256);
+
+ /* Up to 5 args are pushed on top of FP - sizeofStackSaveArea */
+ assert(sizeof(StackSaveArea) < 236);
+
+ /*
+ * EA is calculated by doing "Rn + imm5 << 2", make sure that the last
+ * offset from the struct is less than 128.
+ */
+ assert((offsetof(InterpState, jitToInterpEntries) +
+ sizeof(struct JitToInterpEntries)) <= 128);
+ return true;
+}
+
+int dvmCompilerTargetOptHint(int key)
+{
+ int res;
+ switch (key) {
+ case kMaxHoistDistance:
+ res = 7;
+ break;
+ default:
+ LOGE("Unknown target optimization hint key: %d",key);
+ res = 0;
+ }
+ return res;
+}
+
+void dvmCompilerGenMemBarrier(CompilationUnit *cUnit)
+{
+#if ANDROID_SMP != 0
+ ArmLIR *dmb = newLIR1(cUnit, kThumb2Dmb, kSY); // Full system DMB
+ dmb->defMask = ENCODE_ALL;
+#endif
+}
diff --git a/vm/compiler/codegen/arm/armv7-a/ArchVariant.h b/vm/compiler/codegen/arm/armv7-a/ArchVariant.h
new file mode 100644
index 0000000..9f862e8
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv7-a/ArchVariant.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV5TE_VFP_ARCHVARIANT_H
+#define _DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV5TE_VFP_ARCHVARIANT_H
+
+/* Create the TemplateOpcode enum */
+#define JIT_TEMPLATE(X) TEMPLATE_##X,
+typedef enum {
+#include "../../../template/armv5te-vfp/TemplateOpList.h"
+/*
+ * For example,
+ * TEMPLATE_CMP_LONG,
+ * TEMPLATE_RETURN,
+ * ...
+ */
+ TEMPLATE_LAST_MARK,
+} TemplateOpCode;
+#undef JIT_TEMPLATE
+
+#endif /* _DALVIK_VM_COMPILER_CODEGEN_ARM_ARMV5TE_VFP_ARCHVARIANT_H */
diff --git a/vm/compiler/codegen/arm/armv7-a/CallingConvention.S b/vm/compiler/codegen/arm/armv7-a/CallingConvention.S
new file mode 100644
index 0000000..4f12395
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv7-a/CallingConvention.S
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Save & restore for callee-save FP registers.
+ * On entry:
+ * r0 : pointer to save area of JIT_CALLEE_SAVE_WORD_SIZE
+ */
+ .text
+ .align 2
+ .global dvmJitCalleeSave
+ .type dvmJitCalleeSave, %function
+dvmJitCalleeSave:
+ vstmia r0, {d8-d15}
+ bx lr
+
+ .global dvmJitCalleeRestore
+ .type dvmJitCalleeRestore, %function
+dvmJitCalleeRestore:
+ vldmia r0, {d8-d15}
+ bx lr
diff --git a/vm/compiler/codegen/arm/armv7-a/Codegen.c b/vm/compiler/codegen/arm/armv7-a/Codegen.c
new file mode 100644
index 0000000..1514d55
--- /dev/null
+++ b/vm/compiler/codegen/arm/armv7-a/Codegen.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define _CODEGEN_C
+#define _ARMV7_A
+
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "libdex/OpCode.h"
+#include "libdex/OpCodeNames.h"
+#include "compiler/CompilerInternals.h"
+#include "compiler/codegen/arm/ArmLIR.h"
+#include "mterp/common/FindInterface.h"
+#include "compiler/codegen/arm/Ralloc.h"
+#include "compiler/codegen/arm/Codegen.h"
+#include "compiler/Loop.h"
+#include "ArchVariant.h"
+
+/* Architectural independent building blocks */
+#include "../CodegenCommon.c"
+
+/* Thumb2-specific factory utilities */
+#include "../Thumb2/Factory.c"
+/* Factory utilities dependent on arch-specific features */
+#include "../CodegenFactory.c"
+
+/* Thumb2-specific codegen routines */
+#include "../Thumb2/Gen.c"
+/* Thumb2+VFP codegen routines */
+#include "../FP/Thumb2VFP.c"
+
+/* Thumb2-specific register allocation */
+#include "../Thumb2/Ralloc.c"
+
+/* MIR2LIR dispatcher and architectural independent codegen routines */
+#include "../CodegenDriver.c"
+
+/* Architecture manifest */
+#include "ArchVariant.c"
diff --git a/vm/compiler/template/Makefile-template b/vm/compiler/template/Makefile-template
new file mode 100644
index 0000000..9203183
--- /dev/null
+++ b/vm/compiler/template/Makefile-template
@@ -0,0 +1,49 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Makefile for the Dalvik modular interpreter. This is not currently
+# integrated into the build system.
+#
+
+SHELL := /bin/sh
+
+# Build system has TARGET_ARCH=arm, but we need the exact architecture.
+# The base assumption for an ARM platform is ARMv5TE, but we may want to
+# support older ARMv4 devices, or use special features from ARMv6 or VFP.
+# The simulator build is "desktop".
+#
+# To generate sources for all targets:
+# for arch in desktop armv5te; do TARGET_ARCH_EXT=$arch make -f Makefile-mterp; done
+#
+#TARGET_ARCH_EXT := armv5te
+
+OUTPUT_DIR := out
+
+# Accumulate all possible dependencies for the generated files in a very
+# conservative fashion. If it's not one of the generated files in "out",
+# assume it's a dependency.
+SOURCE_DEPS := \
+ $(shell find . -path ./$(OUTPUT_DIR) -prune -o -type f -print)
+
+# Source files generated by the script. There's always one C and one
+# assembly file, though in practice one or the other could be empty.
+GEN_SOURCES := \
+ $(OUTPUT_DIR)/CompilerTemplateAsm-$(TARGET_ARCH_EXT).S
+
+target: $(GEN_SOURCES)
+
+$(GEN_SOURCES): $(SOURCE_DEPS)
+ @mkdir -p out
+ ./gen-template.py $(TARGET_ARCH_EXT) $(OUTPUT_DIR)
diff --git a/vm/compiler/template/README.txt b/vm/compiler/template/README.txt
new file mode 100644
index 0000000..fced412
--- /dev/null
+++ b/vm/compiler/template/README.txt
@@ -0,0 +1 @@
+See README.txt under dalvik/vm/mterp for details.
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_ADD_DOUBLE_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_ADD_DOUBLE_VFP.S
new file mode 100644
index 0000000..51693fa
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_ADD_DOUBLE_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/fbinopWide.S" {"instr":"faddd d2, d0, d1"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_ADD_FLOAT_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_ADD_FLOAT_VFP.S
new file mode 100644
index 0000000..ad1e122
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_ADD_FLOAT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/fbinop.S" {"instr":"fadds s2, s0, s1"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_CMPG_DOUBLE_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_CMPG_DOUBLE_VFP.S
new file mode 100644
index 0000000..992c894
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_CMPG_DOUBLE_VFP.S
@@ -0,0 +1,33 @@
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * int compare(x, y) {
+ * if (x == y) {
+ * return 0;
+ * } else if (x < y) {
+ * return -1;
+ * } else if (x > y) {
+ * return 1;
+ * } else {
+ * return 1;
+ * }
+ * }
+ *
+ * On entry:
+ * r0 = &op1 [vBB]
+ * r1 = &op2 [vCC]
+ */
+ /* op vAA, vBB, vCC */
+ fldd d0, [r0] @ d0<- vBB
+ fldd d1, [r1] @ d1<- vCC
+ fcmpd d0, d1 @ compare (vBB, vCC)
+ mov r0, #1 @ r0<- 1 (default)
+ fmstat @ export status flags
+ mvnmi r0, #0 @ (less than) r0<- -1
+ moveq r0, #0 @ (equal) r0<- 0
+ bx lr
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_CMPG_FLOAT_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_CMPG_FLOAT_VFP.S
new file mode 100644
index 0000000..0510ef6
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_CMPG_FLOAT_VFP.S
@@ -0,0 +1,32 @@
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * int compare(x, y) {
+ * if (x == y) {
+ * return 0;
+ * } else if (x < y) {
+ * return -1;
+ * } else if (x > y) {
+ * return 1;
+ * } else {
+ * return 1;
+ * }
+ * }
+ * On entry:
+ * r0 = &op1 [vBB]
+ * r1 = &op2 [vCC]
+ */
+ /* op vAA, vBB, vCC */
+ flds s0, [r0] @ d0<- vBB
+ flds s1, [r1] @ d1<- vCC
+ fcmps s0, s1 @ compare (vBB, vCC)
+ mov r0, #1 @ r0<- 1 (default)
+ fmstat @ export status flags
+ mvnmi r0, #0 @ (less than) r0<- -1
+ moveq r0, #0 @ (equal) r0<- 0
+ bx lr
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_CMPL_DOUBLE_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_CMPL_DOUBLE_VFP.S
new file mode 100644
index 0000000..7241af1
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_CMPL_DOUBLE_VFP.S
@@ -0,0 +1,32 @@
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * int compare(x, y) {
+ * if (x == y) {
+ * return 0;
+ * } else if (x > y) {
+ * return 1;
+ * } else if (x < y) {
+ * return -1;
+ * } else {
+ * return -1;
+ * }
+ * }
+ * On entry:
+ * r0 = &op1 [vBB]
+ * r1 = &op2 [vCC]
+ */
+ /* op vAA, vBB, vCC */
+ fldd d0, [r0] @ d0<- vBB
+ fldd d1, [r1] @ d1<- vCC
+ fcmped d0, d1 @ compare (vBB, vCC)
+ mvn r0, #0 @ r0<- -1 (default)
+ fmstat @ export status flags
+ movgt r0, #1 @ (greater than) r0<- 1
+ moveq r0, #0 @ (equal) r0<- 0
+ bx lr
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_CMPL_FLOAT_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_CMPL_FLOAT_VFP.S
new file mode 100644
index 0000000..bdb42d6
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_CMPL_FLOAT_VFP.S
@@ -0,0 +1,32 @@
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * int compare(x, y) {
+ * if (x == y) {
+ * return 0;
+ * } else if (x > y) {
+ * return 1;
+ * } else if (x < y) {
+ * return -1;
+ * } else {
+ * return -1;
+ * }
+ * }
+ * On entry:
+ * r0 = &op1 [vBB]
+ * r1 = &op2 [vCC]
+ */
+ /* op vAA, vBB, vCC */
+ flds s0, [r0] @ d0<- vBB
+ flds s1, [r1] @ d1<- vCC
+ fcmps s0, s1 @ compare (vBB, vCC)
+ mvn r0, #0 @ r0<- -1 (default)
+ fmstat @ export status flags
+ movgt r0, #1 @ (greater than) r0<- 1
+ moveq r0, #0 @ (equal) r0<- 0
+ bx lr
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_DIV_DOUBLE_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_DIV_DOUBLE_VFP.S
new file mode 100644
index 0000000..8fa58b8
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_DIV_DOUBLE_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/fbinopWide.S" {"instr":"fdivd d2, d0, d1"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_DIV_FLOAT_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_DIV_FLOAT_VFP.S
new file mode 100644
index 0000000..fc125ce
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_DIV_FLOAT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/fbinop.S" {"instr":"fdivs s2, s0, s1"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_DOUBLE_TO_FLOAT_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_DOUBLE_TO_FLOAT_VFP.S
new file mode 100644
index 0000000..dba3b08
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_DOUBLE_TO_FLOAT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/funopNarrower.S" {"instr":"fcvtsd s0, d0"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_DOUBLE_TO_INT_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_DOUBLE_TO_INT_VFP.S
new file mode 100644
index 0000000..4d910aa
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_DOUBLE_TO_INT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/funopNarrower.S" {"instr":"ftosizd s0, d0"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_FLOAT_TO_DOUBLE_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_FLOAT_TO_DOUBLE_VFP.S
new file mode 100644
index 0000000..a5157dd
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_FLOAT_TO_DOUBLE_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/funopWider.S" {"instr":"fcvtds d0, s0"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_FLOAT_TO_INT_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_FLOAT_TO_INT_VFP.S
new file mode 100644
index 0000000..90900aa
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_FLOAT_TO_INT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/funop.S" {"instr":"ftosizs s1, s0"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_INT_TO_DOUBLE_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_INT_TO_DOUBLE_VFP.S
new file mode 100644
index 0000000..c9f4fd6
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_INT_TO_DOUBLE_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/funopWider.S" {"instr":"fsitod d0, s0"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_INT_TO_FLOAT_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_INT_TO_FLOAT_VFP.S
new file mode 100644
index 0000000..a8f57b5
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_INT_TO_FLOAT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/funop.S" {"instr":"fsitos s1, s0"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_MEM_OP_DECODE.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_MEM_OP_DECODE.S
new file mode 100644
index 0000000..21e23a9
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_MEM_OP_DECODE.S
@@ -0,0 +1,19 @@
+#if defined(WITH_SELF_VERIFICATION)
+ /*
+ * This handler encapsulates heap memory ops for selfVerification mode.
+ *
+ * The call to the handler is inserted prior to a heap memory operation.
+ * This handler then calls a function to decode the memory op, and process
+ * it accordingly. Afterwards, the handler changes the return address to
+ * skip the memory op so it never gets executed.
+ */
+ vpush {d0-d15} @ save out all fp registers
+ push {r0-r12,lr} @ save out all registers
+ mov r0, lr @ arg0 <- link register
+ mov r1, sp @ arg1 <- stack pointer
+ ldr r2, .LdvmSelfVerificationMemOpDecode @ defined in footer.S
+ blx r2 @ decode and handle the mem op
+ pop {r0-r12,lr} @ restore all registers
+ vpop {d0-d15} @ restore all fp registers
+ bx lr @ return to compiled code
+#endif
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_MUL_DOUBLE_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_MUL_DOUBLE_VFP.S
new file mode 100644
index 0000000..459e796
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_MUL_DOUBLE_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/fbinopWide.S" {"instr":"fmuld d2, d0, d1"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_MUL_FLOAT_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_MUL_FLOAT_VFP.S
new file mode 100644
index 0000000..301fa84
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_MUL_FLOAT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/fbinop.S" {"instr":"fmuls s2, s0, s1"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_RESTORE_STATE.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_RESTORE_STATE.S
new file mode 100644
index 0000000..ec80139
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_RESTORE_STATE.S
@@ -0,0 +1,11 @@
+ /*
+ * This handler restores state following a selfVerification memory access.
+ * On entry:
+ * r0 - offset from rGLUE to the 1st element of the coreRegs save array.
+ */
+ add r0, r0, rGLUE @ pointer to heapArgSpace.coreRegs[0]
+ add r0, #64 @ pointer to heapArgSpace.fpRegs[0]
+ vldmia r0, {d0-d15}
+ sub r0, #64 @ pointer to heapArgSpace.coreRegs[0]
+ ldmia r0, {r0-r12}
+ bx lr
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_SAVE_STATE.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_SAVE_STATE.S
new file mode 100644
index 0000000..1bd02c8
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_SAVE_STATE.S
@@ -0,0 +1,23 @@
+ /*
+ * This handler performs a register save for selfVerification mode.
+ * On entry:
+ * Top of stack + 4: r7 value to save
+ * Top of stack + 0: r0 value to save
+ * r0 - offset from rGLUE to the beginning of the heapArgSpace record
+ * r7 - the value of regMap
+ *
+ * The handler must save regMap, r0-r12 and then return with r0-r12
+ * with their original values (note that this means r0 and r7 must take
+ * the values on the stack - not the ones in those registers on entry.
+ * Finally, the two registers previously pushed must be popped.
+ */
+ add r0, r0, rGLUE @ pointer to heapArgSpace
+ stmia r0!, {r7} @ save regMap
+ ldr r7, [r13, #0] @ recover r0 value
+ stmia r0!, {r7} @ save r0
+ ldr r7, [r13, #4] @ recover r7 value
+ stmia r0!, {r1-r12}
+ add r0, #12 @ move to start of FP save regio
+ vstmia r0, {d0-d15}
+ pop {r0, r7} @ recover r0, r7
+ bx lr
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_SQRT_DOUBLE_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_SQRT_DOUBLE_VFP.S
new file mode 100644
index 0000000..1c6bb46
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_SQRT_DOUBLE_VFP.S
@@ -0,0 +1,23 @@
+%verify "executed"
+ /*
+ * 64-bit floating point vfp sqrt operation.
+ * If the result is a NaN, bail out to library code to do
+ * the right thing.
+ *
+ * On entry:
+ * r2 src addr of op1
+ * On exit:
+ * r0,r1 = res
+ */
+ fldd d0, [r2]
+ fsqrtd d1, d0
+ fcmpd d1, d1
+ fmstat
+ fmrrd r0, r1, d1
+ bxeq lr @ Result OK - return
+ ldr r2, .Lsqrt
+ fmrrd r0, r1, d0 @ reload orig operand
+ bx r2 @ tail call to sqrt library routine
+
+.Lsqrt:
+ .word sqrt
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_SUB_DOUBLE_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_SUB_DOUBLE_VFP.S
new file mode 100644
index 0000000..8fa20a0
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_SUB_DOUBLE_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/fbinopWide.S" {"instr":"fsubd d2, d0, d1"}
diff --git a/vm/compiler/template/armv5te-vfp/TEMPLATE_SUB_FLOAT_VFP.S b/vm/compiler/template/armv5te-vfp/TEMPLATE_SUB_FLOAT_VFP.S
new file mode 100644
index 0000000..5e17e51
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TEMPLATE_SUB_FLOAT_VFP.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te-vfp/fbinop.S" {"instr":"fsubs s2, s0, s1"}
diff --git a/vm/compiler/template/armv5te-vfp/TemplateOpList.h b/vm/compiler/template/armv5te-vfp/TemplateOpList.h
new file mode 100644
index 0000000..d991bed
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/TemplateOpList.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik opcode list that uses additional templates to complete JIT execution.
+ */
+#ifndef JIT_TEMPLATE
+#define JIT_TEMPLATE(X)
+#endif
+
+JIT_TEMPLATE(CMP_LONG)
+JIT_TEMPLATE(RETURN)
+JIT_TEMPLATE(INVOKE_METHOD_NO_OPT)
+JIT_TEMPLATE(INVOKE_METHOD_CHAIN)
+JIT_TEMPLATE(INVOKE_METHOD_PREDICTED_CHAIN)
+JIT_TEMPLATE(INVOKE_METHOD_NATIVE)
+JIT_TEMPLATE(MUL_LONG)
+JIT_TEMPLATE(SHL_LONG)
+JIT_TEMPLATE(SHR_LONG)
+JIT_TEMPLATE(USHR_LONG)
+JIT_TEMPLATE(ADD_FLOAT_VFP)
+JIT_TEMPLATE(SUB_FLOAT_VFP)
+JIT_TEMPLATE(MUL_FLOAT_VFP)
+JIT_TEMPLATE(DIV_FLOAT_VFP)
+JIT_TEMPLATE(ADD_DOUBLE_VFP)
+JIT_TEMPLATE(SUB_DOUBLE_VFP)
+JIT_TEMPLATE(MUL_DOUBLE_VFP)
+JIT_TEMPLATE(DIV_DOUBLE_VFP)
+JIT_TEMPLATE(DOUBLE_TO_FLOAT_VFP)
+JIT_TEMPLATE(DOUBLE_TO_INT_VFP)
+JIT_TEMPLATE(FLOAT_TO_DOUBLE_VFP)
+JIT_TEMPLATE(FLOAT_TO_INT_VFP)
+JIT_TEMPLATE(INT_TO_DOUBLE_VFP)
+JIT_TEMPLATE(INT_TO_FLOAT_VFP)
+JIT_TEMPLATE(CMPG_DOUBLE_VFP)
+JIT_TEMPLATE(CMPL_DOUBLE_VFP)
+JIT_TEMPLATE(CMPG_FLOAT_VFP)
+JIT_TEMPLATE(CMPL_FLOAT_VFP)
+JIT_TEMPLATE(SQRT_DOUBLE_VFP)
+JIT_TEMPLATE(THROW_EXCEPTION_COMMON)
+JIT_TEMPLATE(MEM_OP_DECODE)
+JIT_TEMPLATE(STRING_COMPARETO)
+JIT_TEMPLATE(STRING_INDEXOF)
+JIT_TEMPLATE(INTERPRET)
+JIT_TEMPLATE(MONITOR_ENTER)
+JIT_TEMPLATE(MONITOR_ENTER_DEBUG)
diff --git a/vm/compiler/template/armv5te-vfp/fbinop.S b/vm/compiler/template/armv5te-vfp/fbinop.S
new file mode 100644
index 0000000..3bc4b52
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/fbinop.S
@@ -0,0 +1,14 @@
+ /*
+ * Generic 32-bit floating point operation. Provide an "instr" line that
+ * specifies an instruction that performs s2 = s0 op s1.
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = op1 address
+ * r2 = op2 address
+ */
+ flds s0,[r1]
+ flds s1,[r2]
+ $instr
+ fsts s2,[r0]
+ bx lr
diff --git a/vm/compiler/template/armv5te-vfp/fbinopWide.S b/vm/compiler/template/armv5te-vfp/fbinopWide.S
new file mode 100644
index 0000000..3774646
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/fbinopWide.S
@@ -0,0 +1,14 @@
+ /*
+ * Generic 64-bit floating point operation. Provide an "instr" line that
+ * specifies an instruction that performs s2 = s0 op s1.
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = op1 address
+ * r2 = op2 address
+ */
+ fldd d0,[r1]
+ fldd d1,[r2]
+ $instr
+ fstd d2,[r0]
+ bx lr
diff --git a/vm/compiler/template/armv5te-vfp/funop.S b/vm/compiler/template/armv5te-vfp/funop.S
new file mode 100644
index 0000000..8409c28
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/funop.S
@@ -0,0 +1,15 @@
+ /*
+ * Generic 32bit-to-32bit floating point unary operation. Provide an
+ * "instr" line that specifies an instruction that performs "s1 = op s0".
+ *
+ * For: float-to-int, int-to-float
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = src dalvik register address
+ */
+ /* unop vA, vB */
+ flds s0, [r1] @ s0<- vB
+ $instr @ s1<- op s0
+ fsts s1, [r0] @ vA<- s1
+ bx lr
diff --git a/vm/compiler/template/armv5te-vfp/funopNarrower.S b/vm/compiler/template/armv5te-vfp/funopNarrower.S
new file mode 100644
index 0000000..8566fca
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/funopNarrower.S
@@ -0,0 +1,15 @@
+ /*
+ * Generic 64bit-to-32bit floating point unary operation. Provide an
+ * "instr" line that specifies an instruction that performs "s0 = op d0".
+ *
+ * For: double-to-int, double-to-float
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = src dalvik register address
+ */
+ /* unop vA, vB */
+ fldd d0, [r1] @ d0<- vB
+ $instr @ s0<- op d0
+ fsts s0, [r0] @ vA<- s0
+ bx lr
diff --git a/vm/compiler/template/armv5te-vfp/funopWider.S b/vm/compiler/template/armv5te-vfp/funopWider.S
new file mode 100644
index 0000000..dbe745c
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/funopWider.S
@@ -0,0 +1,15 @@
+ /*
+ * Generic 32bit-to-64bit floating point unary operation. Provide an
+ * "instr" line that specifies an instruction that performs "d0 = op s0".
+ *
+ * For: int-to-double, float-to-double
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = src dalvik register address
+ */
+ /* unop vA, vB */
+ flds s0, [r1] @ s0<- vB
+ $instr @ d0<- op s0
+ fstd d0, [r0] @ vA<- d0
+ bx lr
diff --git a/vm/compiler/template/armv5te-vfp/platform.S b/vm/compiler/template/armv5te-vfp/platform.S
new file mode 100644
index 0000000..880e875
--- /dev/null
+++ b/vm/compiler/template/armv5te-vfp/platform.S
@@ -0,0 +1,16 @@
+/*
+ * ===========================================================================
+ * CPU-version-specific defines and utility
+ * ===========================================================================
+ */
+
+/*
+ * Macro for "MOV LR,PC / LDR PC,xxx", which is not allowed pre-ARMv5.
+ * Jump to subroutine.
+ *
+ * May modify IP and LR.
+ */
+.macro LDR_PC_LR source
+ mov lr, pc
+ ldr pc, \source
+.endm
diff --git a/vm/compiler/template/armv5te/TEMPLATE_CMPG_DOUBLE.S b/vm/compiler/template/armv5te/TEMPLATE_CMPG_DOUBLE.S
new file mode 100644
index 0000000..f18f6d3
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_CMPG_DOUBLE.S
@@ -0,0 +1 @@
+%include "armv5te/TEMPLATE_CMPL_DOUBLE.S" { "naninst":"mov r0, #1" }
diff --git a/vm/compiler/template/armv5te/TEMPLATE_CMPG_FLOAT.S b/vm/compiler/template/armv5te/TEMPLATE_CMPG_FLOAT.S
new file mode 100644
index 0000000..02887e5
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_CMPG_FLOAT.S
@@ -0,0 +1 @@
+%include "armv5te/TEMPLATE_CMPL_FLOAT.S" { "naninst":"mov r0, #1" }
diff --git a/vm/compiler/template/armv5te/TEMPLATE_CMPL_DOUBLE.S b/vm/compiler/template/armv5te/TEMPLATE_CMPL_DOUBLE.S
new file mode 100644
index 0000000..15988f6
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_CMPL_DOUBLE.S
@@ -0,0 +1,36 @@
+%default { "naninst":"mvn r0, #0" }
+ /*
+ * For the JIT: incoming arguments in r0-r1, r2-r3
+ * result in r0
+ *
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+ * on what value we'd like to return when one of the operands is NaN.
+ *
+ * See OP_CMPL_FLOAT for an explanation.
+ *
+ * For: cmpl-double, cmpg-double
+ */
+ /* op vAA, vBB, vCC */
+ push {r0-r3} @ save operands
+ mov r11, lr @ save return address
+ LDR_PC_LR ".L__aeabi_cdcmple" @ PIC way of "bl __aeabi_cdcmple"
+ bhi .L${opcode}_gt_or_nan @ C set and Z clear, disambiguate
+ mvncc r0, #0 @ (less than) r1<- -1
+ moveq r0, #0 @ (equal) r1<- 0, trumps less than
+ add sp, #16 @ drop unused operands
+ bx r11
+
+ @ Test for NaN with a second comparison. EABI forbids testing bit
+ @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+ @ make the library call.
+.L${opcode}_gt_or_nan:
+ pop {r2-r3} @ restore operands in reverse order
+ pop {r0-r1} @ restore operands in reverse order
+ LDR_PC_LR ".L__aeabi_cdcmple" @ r0<- Z set if eq, C clear if <
+ movcc r0, #1 @ (greater than) r1<- 1
+ bxcc r11
+ $naninst @ r1<- 1 or -1 for NaN
+ bx r11
diff --git a/vm/compiler/template/armv5te/TEMPLATE_CMPL_FLOAT.S b/vm/compiler/template/armv5te/TEMPLATE_CMPL_FLOAT.S
new file mode 100644
index 0000000..eb1c7e1
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_CMPL_FLOAT.S
@@ -0,0 +1,54 @@
+%default { "naninst":"mvn r0, #0" }
+ /*
+ * For the JIT: incoming arguments in r0-r1, r2-r3
+ * result in r0
+ *
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+ * on what value we'd like to return when one of the operands is NaN.
+ *
+ * The operation we're implementing is:
+ * if (x == y)
+ * return 0;
+ * else if (x < y)
+ * return -1;
+ * else if (x > y)
+ * return 1;
+ * else
+ * return {-1,1}; // one or both operands was NaN
+ *
+ * The straightforward implementation requires 3 calls to functions
+ * that return a result in r0. We can do it with two calls if our
+ * EABI library supports __aeabi_cfcmple (only one if we want to check
+ * for NaN directly):
+ * check x <= y
+ * if <, return -1
+ * if ==, return 0
+ * check y <= x
+ * if <, return 1
+ * return {-1,1}
+ *
+ * for: cmpl-float, cmpg-float
+ */
+ /* op vAA, vBB, vCC */
+ mov r9, r0 @ Save copies - we may need to redo
+ mov r10, r1
+ mov r11, lr @ save return address
+ LDR_PC_LR ".L__aeabi_cfcmple" @ cmp <=: C clear if <, Z set if eq
+ bhi .L${opcode}_gt_or_nan @ C set and Z clear, disambiguate
+ mvncc r0, #0 @ (less than) r0<- -1
+ moveq r0, #0 @ (equal) r0<- 0, trumps less than
+ bx r11
+ @ Test for NaN with a second comparison. EABI forbids testing bit
+ @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+ @ make the library call.
+.L${opcode}_gt_or_nan:
+ mov r0, r10 @ restore in reverse order
+ mov r1, r9
+ LDR_PC_LR ".L__aeabi_cfcmple" @ r0<- Z set if eq, C clear if <
+ movcc r0, #1 @ (greater than) r1<- 1
+ bxcc r11
+ $naninst @ r1<- 1 or -1 for NaN
+ bx r11
diff --git a/vm/compiler/template/armv5te/TEMPLATE_CMP_LONG.S b/vm/compiler/template/armv5te/TEMPLATE_CMP_LONG.S
new file mode 100644
index 0000000..e5e8196
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_CMP_LONG.S
@@ -0,0 +1,33 @@
+ /*
+ * Compare two 64-bit values. Puts 0, 1, or -1 into the destination
+ * register based on the results of the comparison.
+ *
+ * We load the full values with LDM, but in practice many values could
+ * be resolved by only looking at the high word. This could be made
+ * faster or slower by splitting the LDM into a pair of LDRs.
+ *
+ * If we just wanted to set condition flags, we could do this:
+ * subs ip, r0, r2
+ * sbcs ip, r1, r3
+ * subeqs ip, r0, r2
+ * Leaving { <0, 0, >0 } in ip. However, we have to set it to a specific
+ * integer value, which we can do with 2 conditional mov/mvn instructions
+ * (set 1, set -1; if they're equal we already have 0 in ip), giving
+ * us a constant 5-cycle path plus a branch at the end to the
+ * instruction epilogue code. The multi-compare approach below needs
+ * 2 or 3 cycles + branch if the high word doesn't match, 6 + branch
+ * in the worst case (the 64-bit values are equal).
+ */
+ /* cmp-long vAA, vBB, vCC */
+ cmp r1, r3 @ compare (vBB+1, vCC+1)
+ blt .L${opcode}_less @ signed compare on high part
+ bgt .L${opcode}_greater
+ subs r0, r0, r2 @ r0<- r0 - r2
+ bxeq lr
+ bhi .L${opcode}_greater @ unsigned compare on low part
+.L${opcode}_less:
+ mvn r0, #0 @ r0<- -1
+ bx lr
+.L${opcode}_greater:
+ mov r0, #1 @ r0<- 1
+ bx lr
diff --git a/vm/compiler/template/armv5te/TEMPLATE_INTERPRET.S b/vm/compiler/template/armv5te/TEMPLATE_INTERPRET.S
new file mode 100644
index 0000000..2b0c730
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_INTERPRET.S
@@ -0,0 +1,23 @@
+ /*
+ * This handler transfers control to the interpeter without performing
+ * any lookups. It may be called either as part of a normal chaining
+ * operation, or from the transition code in header.S. We distinquish
+ * the two cases by looking at the link register. If called from a
+ * translation chain, it will point to the chaining Dalvik PC -3.
+ * On entry:
+ * lr - if NULL:
+ * r1 - the Dalvik PC to begin interpretation.
+ * else
+ * [lr, #3] contains Dalvik PC to begin interpretation
+ * rGLUE - pointer to interpState
+ * rFP - Dalvik frame pointer
+ */
+ cmp lr, #0
+ ldrne r1,[lr, #3]
+ ldr r2, .LinterpPunt
+ mov r0, r1 @ set Dalvik PC
+ bx r2
+ @ doesn't return
+
+.LinterpPunt:
+ .word dvmJitToInterpPunt
diff --git a/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S
new file mode 100644
index 0000000..a137d22
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S
@@ -0,0 +1,45 @@
+ /*
+ * For monomorphic callsite, setup the Dalvik frame and return to the
+ * Thumb code through the link register to transfer control to the callee
+ * method through a dedicated chaining cell.
+ */
+ @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+ @ methodToCall is guaranteed to be non-native
+.LinvokeChain:
+ ldrh r7, [r0, #offMethod_registersSize] @ r7<- methodToCall->regsSize
+ ldrh r2, [r0, #offMethod_outsSize] @ r2<- methodToCall->outsSize
+ ldr r9, [rGLUE, #offGlue_interpStackEnd] @ r9<- interpStackEnd
+ ldr r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+ add r3, r1, #1 @ Thumb addr is odd
+ SAVEAREA_FROM_FP(r1, rFP) @ r1<- stack save area
+ sub r1, r1, r7, lsl #2 @ r1<- newFp (old savearea - regsSize)
+ SAVEAREA_FROM_FP(r10, r1) @ r10<- stack save area
+ add r12, lr, #2 @ setup the punt-to-interp address
+ sub r10, r10, r2, lsl #2 @ r10<- bottom (newsave - outsSize)
+ ldr r8, [r8] @ r8<- suspendCount (int)
+ cmp r10, r9 @ bottom < interpStackEnd?
+ bxlo r12 @ return to raise stack overflow excep.
+ @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+ ldr r9, [r0, #offMethod_clazz] @ r9<- method->clazz
+ str rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+ str rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+ ldr rPC, [r0, #offMethod_insns] @ rPC<- methodToCall->insns
+
+
+ @ set up newSaveArea
+ str rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+ str r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+ str r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+ cmp r8, #0 @ suspendCount != 0
+ bxne r12 @ bail to the interpreter
+
+ ldr r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+ ldr r2, [rGLUE, #offGlue_self] @ r2<- glue->self
+
+ @ Update "glue" values for the new method
+ str r0, [rGLUE, #offGlue_method] @ glue->method = methodToCall
+ str r3, [rGLUE, #offGlue_methodClassDex] @ glue->methodClassDex = ...
+ mov rFP, r1 @ fp = newFp
+ str rFP, [r2, #offThread_curFrame] @ self->curFrame = newFp
+
+ bx lr @ return to the callee-chaining cell
diff --git a/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S
new file mode 100644
index 0000000..2557863
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S
@@ -0,0 +1,70 @@
+ @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+ ldrh r7, [r0, #offMethod_registersSize] @ r7<- methodToCall->regsSize
+ ldr r9, [rGLUE, #offGlue_interpStackEnd] @ r9<- interpStackEnd
+ ldr r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+ add r3, r1, #1 @ Thumb addr is odd
+ SAVEAREA_FROM_FP(r1, rFP) @ r1<- stack save area
+ sub r1, r1, r7, lsl #2 @ r1<- newFp (old savearea - regsSize)
+ SAVEAREA_FROM_FP(r10, r1) @ r10<- stack save area
+ ldr r8, [r8] @ r3<- suspendCount (int)
+ cmp r10, r9 @ bottom < interpStackEnd?
+ bxlo lr @ return to raise stack overflow excep.
+ @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+ str rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+ str rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+ ldr rPC, [r0, #offMethod_insns] @ rPC<- methodToCall->insns
+
+
+ @ set up newSaveArea
+ str rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+ str r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+ ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
+ str r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+ cmp r8, #0 @ suspendCount != 0
+ ldr r8, [r0, #offMethod_nativeFunc] @ r8<- method->nativeFunc
+#if !defined(WITH_SELF_VERIFICATION)
+ bxne lr @ bail to the interpreter
+#else
+ bx lr @ bail to interpreter unconditionally
+#endif
+
+ @ go ahead and transfer control to the native code
+ ldr r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
+ mov r2, #0
+ str r1, [r3, #offThread_curFrame] @ self->curFrame = newFp
+ str r2, [r3, #offThread_inJitCodeCache] @ not in the jit code cache
+ str r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+ @ newFp->localRefCookie=top
+ mov r9, r3 @ r9<- glue->self (preserve)
+ SAVEAREA_FROM_FP(r10, r1) @ r10<- new stack save area
+
+ mov r2, r0 @ r2<- methodToCall
+ mov r0, r1 @ r0<- newFP
+ add r1, rGLUE, #offGlue_retval @ r1<- &retval
+
+ blx r8 @ off to the native code
+
+ @ native return; r9=self, r10=newSaveArea
+ @ equivalent to dvmPopJniLocals
+ ldr r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+ ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+ ldr r1, [r9, #offThread_exception] @ check for exception
+ str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
+ cmp r1, #0 @ null?
+ str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
+ ldr r0, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+
+ @ r0 = dalvikCallsitePC
+ bne .LhandleException @ no, handle exception
+
+ str r2, [r9, #offThread_inJitCodeCache] @ set the mode properly
+ cmp r2, #0 @ return chaining cell still exists?
+ bxne r2 @ yes - go ahead
+
+ @ continue executing the next instruction through the interpreter
+ ldr r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+ add rPC, r0, #6 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+ mov r0, #kCallsiteInterpreted
+#endif
+ mov pc, r1
diff --git a/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S
new file mode 100644
index 0000000..5be6978
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S
@@ -0,0 +1,54 @@
+ /*
+ * For polymorphic callsites - setup the Dalvik frame and load Dalvik PC
+ * into rPC then jump to dvmJitToInterpNoChain to dispatch the
+ * runtime-resolved callee.
+ */
+ @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+ ldrh r7, [r0, #offMethod_registersSize] @ r7<- methodToCall->regsSize
+ ldrh r2, [r0, #offMethod_outsSize] @ r2<- methodToCall->outsSize
+ ldr r9, [rGLUE, #offGlue_interpStackEnd] @ r9<- interpStackEnd
+ ldr r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+ add r3, r1, #1 @ Thumb addr is odd
+ SAVEAREA_FROM_FP(r1, rFP) @ r1<- stack save area
+ sub r1, r1, r7, lsl #2 @ r1<- newFp (old savearea - regsSize)
+ SAVEAREA_FROM_FP(r10, r1) @ r10<- stack save area
+ sub r10, r10, r2, lsl #2 @ r10<- bottom (newsave - outsSize)
+ ldr r8, [r8] @ r8<- suspendCount (int)
+ cmp r10, r9 @ bottom < interpStackEnd?
+ bxlo lr @ return to raise stack overflow excep.
+ @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+ ldr r9, [r0, #offMethod_clazz] @ r9<- method->clazz
+ ldr r10, [r0, #offMethod_accessFlags] @ r10<- methodToCall->accessFlags
+ str rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+ str rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+ ldr rPC, [r0, #offMethod_insns] @ rPC<- methodToCall->insns
+
+
+ @ set up newSaveArea
+ str rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+ str r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+ str r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+ cmp r8, #0 @ suspendCount != 0
+ bxne lr @ bail to the interpreter
+ tst r10, #ACC_NATIVE
+#if !defined(WITH_SELF_VERIFICATION)
+ bne .LinvokeNative
+#else
+ bxne lr @ bail to the interpreter
+#endif
+
+ ldr r10, .LdvmJitToInterpTraceSelectNoChain
+ ldr r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+ ldr r2, [rGLUE, #offGlue_self] @ r2<- glue->self
+
+ @ Update "glue" values for the new method
+ str r0, [rGLUE, #offGlue_method] @ glue->method = methodToCall
+ str r3, [rGLUE, #offGlue_methodClassDex] @ glue->methodClassDex = ...
+ mov rFP, r1 @ fp = newFp
+ str rFP, [r2, #offThread_curFrame] @ self->curFrame = newFp
+
+ @ Start executing the callee
+#if defined(WITH_JIT_TUNING)
+ mov r0, #kInlineCacheMiss
+#endif
+ mov pc, r10 @ dvmJitToInterpTraceSelectNoChain
diff --git a/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S
new file mode 100644
index 0000000..c3085b9
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S
@@ -0,0 +1,50 @@
+ /*
+ * For polymorphic callsite, check whether the cached class pointer matches
+ * the current one. If so setup the Dalvik frame and return to the
+ * Thumb code through the link register to transfer control to the callee
+ * method through a dedicated chaining cell.
+ *
+ * The predicted chaining cell is declared in ArmLIR.h with the
+ * following layout:
+ *
+ * typedef struct PredictedChainingCell {
+ * u4 branch;
+ * const ClassObject *clazz;
+ * const Method *method;
+ * u4 counter;
+ * } PredictedChainingCell;
+ *
+ * Upon returning to the callsite:
+ * - lr : to branch to the chaining cell
+ * - lr+2: to punt to the interpreter
+ * - lr+4: to fully resolve the callee and may rechain.
+ * r3 <- class
+ * r9 <- counter
+ */
+ @ r0 = this, r1 = returnCell, r2 = predictedChainCell, rPC = dalvikCallsite
+ ldr r3, [r0, #offObject_clazz] @ r3 <- this->class
+ ldr r8, [r2, #4] @ r8 <- predictedChainCell->clazz
+ ldr r0, [r2, #8] @ r0 <- predictedChainCell->method
+ ldr r9, [rGLUE, #offGlue_icRechainCount] @ r1 <- shared rechainCount
+ cmp r3, r8 @ predicted class == actual class?
+#if defined(WITH_JIT_TUNING)
+ ldr r7, .LdvmICHitCount
+ ldreq r10, [r7, #0]
+ add r10, r10, #1
+ streq r10, [r7, #0]
+#endif
+ beq .LinvokeChain @ predicted chain is valid
+ ldr r7, [r3, #offClassObject_vtable] @ r7 <- this->class->vtable
+ cmp r8, #0 @ initialized class or not
+ moveq r1, #0
+ subne r1, r9, #1 @ count--
+ strne r1, [rGLUE, #offGlue_icRechainCount] @ write back to InterpState
+ add lr, lr, #4 @ return to fully-resolve landing pad
+ /*
+ * r1 <- count
+ * r2 <- &predictedChainCell
+ * r3 <- this->class
+ * r4 <- dPC
+ * r7 <- this->class->vtable
+ */
+ bx lr
diff --git a/vm/compiler/template/armv5te/TEMPLATE_MEM_OP_DECODE.S b/vm/compiler/template/armv5te/TEMPLATE_MEM_OP_DECODE.S
new file mode 100644
index 0000000..ecd4eaa
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_MEM_OP_DECODE.S
@@ -0,0 +1,17 @@
+#if defined(WITH_SELF_VERIFICATION)
+ /*
+ * This handler encapsulates heap memory ops for selfVerification mode.
+ *
+ * The call to the handler is inserted prior to a heap memory operation.
+ * This handler then calls a function to decode the memory op, and process
+ * it accordingly. Afterwards, the handler changes the return address to
+ * skip the memory op so it never gets executed.
+ */
+ push {r0-r12,lr} @ save out all registers
+ mov r0, lr @ arg0 <- link register
+ mov r1, sp @ arg1 <- stack pointer
+ ldr r2, .LdvmSelfVerificationMemOpDecode @ defined in footer.S
+ blx r2 @ decode and handle the mem op
+ pop {r0-r12,lr} @ restore all registers
+ bx lr @ return to compiled code
+#endif
diff --git a/vm/compiler/template/armv5te/TEMPLATE_MONITOR_ENTER.S b/vm/compiler/template/armv5te/TEMPLATE_MONITOR_ENTER.S
new file mode 100644
index 0000000..8e7f728
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_MONITOR_ENTER.S
@@ -0,0 +1,25 @@
+ /*
+ * Call out to the runtime to lock an object. Because this thread
+ * may have been suspended in THREAD_MONITOR state and the Jit's
+ * translation cache subsequently cleared, we cannot return directly.
+ * Instead, unconditionally transition to the interpreter to resume.
+ *
+ * On entry:
+ * r0 - self pointer
+ * r1 - the object (which has already been null-checked by the caller
+ * r4 - the Dalvik PC of the following instruction.
+ */
+ ldr r2, .LdvmLockObject
+ mov r3, #0 @ Record that we're not returning
+ str r3, [r0, #offThread_inJitCodeCache]
+ blx r2 @ dvmLockObject(self, obj)
+ @ refresh Jit's on/off status
+ ldr r0, [rGLUE, #offGlue_ppJitProfTable]
+ ldr r0, [r0]
+ ldr r2, .LdvmJitToInterpNoChain
+ str r0, [rGLUE, #offGlue_pJitProfTable]
+ @ Bail to interpreter - no chain [note - r4 still contains rPC]
+#if defined(WITH_JIT_TUNING)
+ mov r0, #kHeavyweightMonitor
+#endif
+ bx r2
diff --git a/vm/compiler/template/armv5te/TEMPLATE_MONITOR_ENTER_DEBUG.S b/vm/compiler/template/armv5te/TEMPLATE_MONITOR_ENTER_DEBUG.S
new file mode 100644
index 0000000..5cf26e7
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_MONITOR_ENTER_DEBUG.S
@@ -0,0 +1,32 @@
+ /*
+ * To support deadlock prediction, this version of MONITOR_ENTER
+ * will always call the heavyweight dvmLockObject, check for an
+ * exception and then bail out to the interpreter.
+ *
+ * On entry:
+ * r0 - self pointer
+ * r1 - the object (which has already been null-checked by the caller
+ * r4 - the Dalvik PC of the following instruction.
+ *
+ */
+ ldr r2, .LdvmLockObject
+ mov r3, #0 @ Record that we're not returning
+ str r3, [r0, #offThread_inJitCodeCache]
+ blx r2 @ dvmLockObject(self, obj)
+ @ refresh Jit's on/off status & test for exception
+ ldr r0, [rGLUE, #offGlue_ppJitProfTable]
+ ldr r1, [rGLUE, #offGlue_self]
+ ldr r0, [r0]
+ ldr r1, [r1, #offThread_exception]
+ str r0, [rGLUE, #offGlue_pJitProfTable]
+ cmp r1, #0
+ beq 1f
+ ldr r2, .LhandleException
+ sub r0, r4, #2 @ roll dPC back to this monitor instruction
+ bx r2
+1:
+ @ Bail to interpreter - no chain [note - r4 still contains rPC]
+#if defined(WITH_JIT_TUNING)
+ mov r0, #kHeavyweightMonitor
+#endif
+ ldr pc, .LdvmJitToInterpNoChain
diff --git a/vm/compiler/template/armv5te/TEMPLATE_MUL_LONG.S b/vm/compiler/template/armv5te/TEMPLATE_MUL_LONG.S
new file mode 100644
index 0000000..8a9b115
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_MUL_LONG.S
@@ -0,0 +1,28 @@
+ /*
+ * Signed 64-bit integer multiply.
+ *
+ * For JIT: op1 in r0/r1, op2 in r2/r3, return in r0/r1
+ *
+ * Consider WXxYZ (r1r0 x r3r2) with a long multiply:
+ * WX
+ * x YZ
+ * --------
+ * ZW ZX
+ * YW YX
+ *
+ * The low word of the result holds ZX, the high word holds
+ * (ZW+YX) + (the high overflow from ZX). YW doesn't matter because
+ * it doesn't fit in the low 64 bits.
+ *
+ * Unlike most ARM math operations, multiply instructions have
+ * restrictions on using the same register more than once (Rd and Rm
+ * cannot be the same).
+ */
+ /* mul-long vAA, vBB, vCC */
+ mul ip, r2, r1 @ ip<- ZxW
+ umull r9, r10, r2, r0 @ r9/r10 <- ZxX
+ mla r2, r0, r3, ip @ r2<- YxX + (ZxW)
+ add r10, r2, r10 @ r10<- r10 + low(ZxW + (YxX))
+ mov r0,r9
+ mov r1,r10
+ bx lr
diff --git a/vm/compiler/template/armv5te/TEMPLATE_RESTORE_STATE.S b/vm/compiler/template/armv5te/TEMPLATE_RESTORE_STATE.S
new file mode 100644
index 0000000..e3719db
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_RESTORE_STATE.S
@@ -0,0 +1,8 @@
+ /*
+ * This handler restores state following a selfVerification memory access.
+ * On entry:
+ * r0 - offset from rGLUE to the 1st element of the coreRegs save array.
+ */
+ add r0, r0, rGLUE @ pointer to heapArgSpace.coreRegs[0]
+ ldmia r0, {r0-r12}
+ bx lr
diff --git a/vm/compiler/template/armv5te/TEMPLATE_RETURN.S b/vm/compiler/template/armv5te/TEMPLATE_RETURN.S
new file mode 100644
index 0000000..b7ab971
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_RETURN.S
@@ -0,0 +1,50 @@
+ /*
+ * Unwind a frame from the Dalvik stack for compiled OP_RETURN_XXX.
+ * If the stored value in returnAddr
+ * is non-zero, the caller is compiled by the JIT thus return to the
+ * address in the code cache following the invoke instruction. Otherwise
+ * return to the special dvmJitToInterpNoChain entry point.
+ */
+ SAVEAREA_FROM_FP(r0, rFP) @ r0<- saveArea (old)
+ ldr r10, [r0, #offStackSaveArea_prevFrame] @ r10<- saveArea->prevFrame
+ ldr r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+ ldr rPC, [r0, #offStackSaveArea_savedPc] @ rPC<- saveArea->savedPc
+#if !defined(WITH_SELF_VERIFICATION)
+ ldr r9, [r0, #offStackSaveArea_returnAddr] @ r9<- chaining cell ret
+#else
+ mov r9, #0 @ disable chaining
+#endif
+ ldr r2, [r10, #(offStackSaveArea_method - sizeofStackSaveArea)]
+ @ r2<- method we're returning to
+ ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
+ cmp r2, #0 @ break frame?
+#if !defined(WITH_SELF_VERIFICATION)
+ beq 1f @ bail to interpreter
+#else
+ blxeq lr @ punt to interpreter and compare state
+#endif
+ ldr r1, .LdvmJitToInterpNoChainNoProfile @ defined in footer.S
+ mov rFP, r10 @ publish new FP
+ ldrne r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+ ldr r8, [r8] @ r8<- suspendCount
+
+ str r2, [rGLUE, #offGlue_method]@ glue->method = newSave->method
+ ldr r0, [r10, #offClassObject_pDvmDex] @ r0<- method->clazz->pDvmDex
+ str rFP, [r3, #offThread_curFrame] @ self->curFrame = fp
+ add rPC, rPC, #6 @ publish new rPC (advance 6 bytes)
+ str r0, [rGLUE, #offGlue_methodClassDex]
+ cmp r8, #0 @ check the suspendCount
+ movne r9, #0 @ clear the chaining cell address
+ str r9, [r3, #offThread_inJitCodeCache] @ in code cache or not
+ cmp r9, #0 @ chaining cell exists?
+ blxne r9 @ jump to the chaining cell
+#if defined(WITH_JIT_TUNING)
+ mov r0, #kCallsiteInterpreted
+#endif
+ mov pc, r1 @ callsite is interpreted
+1:
+ stmia rGLUE, {rPC, rFP} @ SAVE_PC_FP_TO_GLUE()
+ ldr r2, .LdvmMterpStdBail @ defined in footer.S
+ mov r1, #0 @ changeInterp = false
+ mov r0, rGLUE @ Expecting rGLUE in r0
+ blx r2 @ exit the interpreter
diff --git a/vm/compiler/template/armv5te/TEMPLATE_SAVE_STATE.S b/vm/compiler/template/armv5te/TEMPLATE_SAVE_STATE.S
new file mode 100644
index 0000000..df2d1e6
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_SAVE_STATE.S
@@ -0,0 +1,21 @@
+ /*
+ * This handler performs a register save for selfVerification mode.
+ * On entry:
+ * Top of stack + 4: r7 value to save
+ * Top of stack + 0: r0 value to save
+ * r0 - offset from rGLUE to the beginning of the heapArgSpace record
+ * r7 - the value of regMap
+ *
+ * The handler must save regMap, r0-r12 and then return with r0-r12
+ * with their original values (note that this means r0 and r7 must take
+ * the values on the stack - not the ones in those registers on entry.
+ * Finally, the two registers previously pushed must be popped.
+ */
+ add r0, r0, rGLUE @ pointer to heapArgSpace
+ stmia r0!, {r7} @ save regMap
+ ldr r7, [r13, #0] @ recover r0 value
+ stmia r0!, {r7} @ save r0
+ ldr r7, [r13, #4] @ recover r7 value
+ stmia r0!, {r1-r12}
+ pop {r0, r7} @ recover r0, r7
+ bx lr
diff --git a/vm/compiler/template/armv5te/TEMPLATE_SHL_LONG.S b/vm/compiler/template/armv5te/TEMPLATE_SHL_LONG.S
new file mode 100644
index 0000000..532f8a4
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_SHL_LONG.S
@@ -0,0 +1,15 @@
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to ignore all but the low
+ * 6 bits.
+ */
+ /* shl-long vAA, vBB, vCC */
+ and r2, r2, #63 @ r2<- r2 & 0x3f
+ mov r1, r1, asl r2 @ r1<- r1 << r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32)
+ mov r0, r0, asl r2 @ r0<- r0 << r2
+ bx lr
diff --git a/vm/compiler/template/armv5te/TEMPLATE_SHR_LONG.S b/vm/compiler/template/armv5te/TEMPLATE_SHR_LONG.S
new file mode 100644
index 0000000..c737840
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_SHR_LONG.S
@@ -0,0 +1,15 @@
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to ignore all but the low
+ * 6 bits.
+ */
+ /* shr-long vAA, vBB, vCC */
+ and r2, r2, #63 @ r0<- r0 & 0x3f
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32)
+ mov r1, r1, asr r2 @ r1<- r1 >> r2
+ bx lr
diff --git a/vm/compiler/template/armv5te/TEMPLATE_STRING_COMPARETO.S b/vm/compiler/template/armv5te/TEMPLATE_STRING_COMPARETO.S
new file mode 100644
index 0000000..54bde47
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_STRING_COMPARETO.S
@@ -0,0 +1,133 @@
+ /*
+ * String's compareTo.
+ *
+ * Requires r0/r1 to have been previously checked for null. Will
+ * return negative if this's string is < comp, 0 if they are the
+ * same and positive if >.
+ *
+ * IMPORTANT NOTE:
+ *
+ * This code relies on hard-coded offsets for string objects, and must be
+ * kept in sync with definitions in UtfString.h. See asm-constants.h
+ *
+ * On entry:
+ * r0: this object pointer
+ * r1: comp object pointer
+ *
+ */
+
+ mov r2, r0 @ this to r2, opening up r0 for return value
+ subs r0, r2, r1 @ Same?
+ bxeq lr
+
+ ldr r4, [r2, #STRING_FIELDOFF_OFFSET]
+ ldr r9, [r1, #STRING_FIELDOFF_OFFSET]
+ ldr r7, [r2, #STRING_FIELDOFF_COUNT]
+ ldr r10, [r1, #STRING_FIELDOFF_COUNT]
+ ldr r2, [r2, #STRING_FIELDOFF_VALUE]
+ ldr r1, [r1, #STRING_FIELDOFF_VALUE]
+
+ /*
+ * At this point, we have:
+ * value: r2/r1
+ * offset: r4/r9
+ * count: r7/r10
+ * We're going to compute
+ * r11 <- countDiff
+ * r10 <- minCount
+ */
+ subs r11, r7, r10
+ movls r10, r7
+
+ /* Now, build pointers to the string data */
+ add r2, r2, r4, lsl #1
+ add r1, r1, r9, lsl #1
+ /*
+ * Note: data pointers point to previous element so we can use pre-index
+ * mode with base writeback.
+ */
+ add r2, #16-2 @ offset to contents[-1]
+ add r1, #16-2 @ offset to contents[-1]
+
+ /*
+ * At this point we have:
+ * r2: *this string data
+ * r1: *comp string data
+ * r10: iteration count for comparison
+ * r11: value to return if the first part of the string is equal
+ * r0: reserved for result
+ * r3, r4, r7, r8, r9, r12 available for loading string data
+ */
+
+ subs r10, #2
+ blt do_remainder2
+
+ /*
+ * Unroll the first two checks so we can quickly catch early mismatch
+ * on long strings (but preserve incoming alignment)
+ */
+
+ ldrh r3, [r2, #2]!
+ ldrh r4, [r1, #2]!
+ ldrh r7, [r2, #2]!
+ ldrh r8, [r1, #2]!
+ subs r0, r3, r4
+ subeqs r0, r7, r8
+ bxne lr
+ cmp r10, #28
+ bgt do_memcmp16
+ subs r10, #3
+ blt do_remainder
+
+loopback_triple:
+ ldrh r3, [r2, #2]!
+ ldrh r4, [r1, #2]!
+ ldrh r7, [r2, #2]!
+ ldrh r8, [r1, #2]!
+ ldrh r9, [r2, #2]!
+ ldrh r12,[r1, #2]!
+ subs r0, r3, r4
+ subeqs r0, r7, r8
+ subeqs r0, r9, r12
+ bxne lr
+ subs r10, #3
+ bge loopback_triple
+
+do_remainder:
+ adds r10, #3
+ beq returnDiff
+
+loopback_single:
+ ldrh r3, [r2, #2]!
+ ldrh r4, [r1, #2]!
+ subs r0, r3, r4
+ bxne lr
+ subs r10, #1
+ bne loopback_single
+
+returnDiff:
+ mov r0, r11
+ bx lr
+
+do_remainder2:
+ adds r10, #2
+ bne loopback_single
+ mov r0, r11
+ bx lr
+
+ /* Long string case */
+do_memcmp16:
+ mov r4, lr
+ ldr lr, .Lmemcmp16
+ mov r7, r11
+ add r0, r2, #2
+ add r1, r1, #2
+ mov r2, r10
+ blx lr
+ cmp r0, #0
+ bxne r4
+ mov r0, r7
+ bx r4
+
+.Lmemcmp16:
+ .word __memcmp16
diff --git a/vm/compiler/template/armv5te/TEMPLATE_STRING_INDEXOF.S b/vm/compiler/template/armv5te/TEMPLATE_STRING_INDEXOF.S
new file mode 100644
index 0000000..bdfdf28
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_STRING_INDEXOF.S
@@ -0,0 +1,112 @@
+ /*
+ * String's indexOf.
+ *
+ * Requires r0 to have been previously checked for null. Will
+ * return index of match of r1 in r0.
+ *
+ * IMPORTANT NOTE:
+ *
+ * This code relies on hard-coded offsets for string objects, and must be
+ * kept in sync wth definitions in UtfString.h See asm-constants.h
+ *
+ * On entry:
+ * r0: string object pointer
+ * r1: char to match
+ * r2: Starting offset in string data
+ */
+
+ ldr r7, [r0, #STRING_FIELDOFF_OFFSET]
+ ldr r8, [r0, #STRING_FIELDOFF_COUNT]
+ ldr r0, [r0, #STRING_FIELDOFF_VALUE]
+
+ /*
+ * At this point, we have:
+ * r0: object pointer
+ * r1: char to match
+ * r2: starting offset
+ * r7: offset
+ * r8: string length
+ */
+
+ /* Build pointer to start of string data */
+ add r0, #16
+ add r0, r0, r7, lsl #1
+
+ /* Save a copy of starting data in r7 */
+ mov r7, r0
+
+ /* Clamp start to [0..count] */
+ cmp r2, #0
+ movlt r2, #0
+ cmp r2, r8
+ movgt r2, r8
+
+ /* Build pointer to start of data to compare and pre-bias */
+ add r0, r0, r2, lsl #1
+ sub r0, #2
+
+ /* Compute iteration count */
+ sub r8, r2
+
+ /*
+ * At this point we have:
+ * r0: start of data to test
+ * r1: chat to compare
+ * r8: iteration count
+ * r7: original start of string
+ * r3, r4, r9, r10, r11, r12 available for loading string data
+ */
+
+ subs r8, #4
+ blt indexof_remainder
+
+indexof_loop4:
+ ldrh r3, [r0, #2]!
+ ldrh r4, [r0, #2]!
+ ldrh r10, [r0, #2]!
+ ldrh r11, [r0, #2]!
+ cmp r3, r1
+ beq match_0
+ cmp r4, r1
+ beq match_1
+ cmp r10, r1
+ beq match_2
+ cmp r11, r1
+ beq match_3
+ subs r8, #4
+ bge indexof_loop4
+
+indexof_remainder:
+ adds r8, #4
+ beq indexof_nomatch
+
+indexof_loop1:
+ ldrh r3, [r0, #2]!
+ cmp r3, r1
+ beq match_3
+ subs r8, #1
+ bne indexof_loop1
+
+indexof_nomatch:
+ mov r0, #-1
+ bx lr
+
+match_0:
+ sub r0, #6
+ sub r0, r7
+ asr r0, r0, #1
+ bx lr
+match_1:
+ sub r0, #4
+ sub r0, r7
+ asr r0, r0, #1
+ bx lr
+match_2:
+ sub r0, #2
+ sub r0, r7
+ asr r0, r0, #1
+ bx lr
+match_3:
+ sub r0, r7
+ asr r0, r0, #1
+ bx lr
diff --git a/vm/compiler/template/armv5te/TEMPLATE_THROW_EXCEPTION_COMMON.S b/vm/compiler/template/armv5te/TEMPLATE_THROW_EXCEPTION_COMMON.S
new file mode 100644
index 0000000..b737798
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_THROW_EXCEPTION_COMMON.S
@@ -0,0 +1,6 @@
+ /*
+ * Throw an exception from JIT'ed code.
+ * On entry:
+ * r0 Dalvik PC that raises the exception
+ */
+ b .LhandleException
diff --git a/vm/compiler/template/armv5te/TEMPLATE_USHR_LONG.S b/vm/compiler/template/armv5te/TEMPLATE_USHR_LONG.S
new file mode 100644
index 0000000..8a48df2
--- /dev/null
+++ b/vm/compiler/template/armv5te/TEMPLATE_USHR_LONG.S
@@ -0,0 +1,15 @@
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to ignore all but the low
+ * 6 bits.
+ */
+ /* ushr-long vAA, vBB, vCC */
+ and r2, r2, #63 @ r0<- r0 & 0x3f
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32)
+ mov r1, r1, lsr r2 @ r1<- r1 >>> r2
+ bx lr
diff --git a/vm/compiler/template/armv5te/TemplateOpList.h b/vm/compiler/template/armv5te/TemplateOpList.h
new file mode 100644
index 0000000..e81383c
--- /dev/null
+++ b/vm/compiler/template/armv5te/TemplateOpList.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik opcode list that uses additional templates to complete JIT execution.
+ */
+#ifndef JIT_TEMPLATE
+#define JIT_TEMPLATE(X)
+#endif
+
+JIT_TEMPLATE(CMP_LONG)
+JIT_TEMPLATE(RETURN)
+JIT_TEMPLATE(INVOKE_METHOD_NO_OPT)
+JIT_TEMPLATE(INVOKE_METHOD_CHAIN)
+JIT_TEMPLATE(INVOKE_METHOD_PREDICTED_CHAIN)
+JIT_TEMPLATE(INVOKE_METHOD_NATIVE)
+JIT_TEMPLATE(CMPG_DOUBLE)
+JIT_TEMPLATE(CMPL_DOUBLE)
+JIT_TEMPLATE(CMPG_FLOAT)
+JIT_TEMPLATE(CMPL_FLOAT)
+JIT_TEMPLATE(MUL_LONG)
+JIT_TEMPLATE(SHL_LONG)
+JIT_TEMPLATE(SHR_LONG)
+JIT_TEMPLATE(USHR_LONG)
+JIT_TEMPLATE(THROW_EXCEPTION_COMMON)
+JIT_TEMPLATE(MEM_OP_DECODE)
+JIT_TEMPLATE(STRING_COMPARETO)
+JIT_TEMPLATE(STRING_INDEXOF)
+JIT_TEMPLATE(INTERPRET)
+JIT_TEMPLATE(MONITOR_ENTER)
+JIT_TEMPLATE(MONITOR_ENTER_DEBUG)
diff --git a/vm/compiler/template/armv5te/footer.S b/vm/compiler/template/armv5te/footer.S
new file mode 100644
index 0000000..73fc3d7
--- /dev/null
+++ b/vm/compiler/template/armv5te/footer.S
@@ -0,0 +1,107 @@
+/*
+ * ===========================================================================
+ * Common subroutines and data
+ * ===========================================================================
+ */
+
+ .text
+ .align 2
+.LinvokeNative:
+ @ Prep for the native call
+ @ r1 = newFP, r0 = methodToCall
+ ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
+ mov r2, #0
+ ldr r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
+ str r2, [r3, #offThread_inJitCodeCache] @ not in jit code cache
+ str r1, [r3, #offThread_curFrame] @ self->curFrame = newFp
+ str r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+ @ newFp->localRefCookie=top
+ mov r9, r3 @ r9<- glue->self (preserve)
+ SAVEAREA_FROM_FP(r10, r1) @ r10<- new stack save area
+
+ mov r2, r0 @ r2<- methodToCall
+ mov r0, r1 @ r0<- newFP
+ add r1, rGLUE, #offGlue_retval @ r1<- &retval
+
+ LDR_PC_LR "[r2, #offMethod_nativeFunc]"
+
+ @ Refresh Jit's on/off status
+ ldr r3, [rGLUE, #offGlue_ppJitProfTable]
+
+ @ native return; r9=self, r10=newSaveArea
+ @ equivalent to dvmPopJniLocals
+ ldr r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+ ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+ ldr r1, [r9, #offThread_exception] @ check for exception
+ ldr r3, [r3] @ r1 <- pointer to Jit profile table
+ str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
+ cmp r1, #0 @ null?
+ str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
+ ldr r0, [r10, #offStackSaveArea_savedPc] @ reload rPC
+ str r3, [rGLUE, #offGlue_pJitProfTable] @ cache current JitProfTable
+
+ @ r0 = dalvikCallsitePC
+ bne .LhandleException @ no, handle exception
+
+ str r2, [r9, #offThread_inJitCodeCache] @ set the new mode
+ cmp r2, #0 @ return chaining cell still exists?
+ bxne r2 @ yes - go ahead
+
+ @ continue executing the next instruction through the interpreter
+ ldr r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+ add rPC, r0, #6 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+ mov r0, #kCallsiteInterpreted
+#endif
+ mov pc, r1
+
+/*
+ * On entry:
+ * r0 Faulting Dalvik PC
+ */
+.LhandleException:
+#if defined(WITH_SELF_VERIFICATION)
+ ldr pc, .LdeadFood @ should not see this under self-verification mode
+.LdeadFood:
+ .word 0xdeadf00d
+#endif
+ ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
+ mov r2, #0
+ str r2, [r3, #offThread_inJitCodeCache] @ in interpreter land
+ ldr r1, .LdvmMterpCommonExceptionThrown @ PIC way of getting &func
+ ldr rIBASE, .LdvmAsmInstructionStart @ same as above
+ mov rPC, r0 @ reload the faulting Dalvik address
+ mov pc, r1 @ branch to dvmMterpCommonExceptionThrown
+
+ .align 2
+.LdvmAsmInstructionStart:
+ .word dvmAsmInstructionStart
+.LdvmJitToInterpNoChainNoProfile:
+ .word dvmJitToInterpNoChainNoProfile
+.LdvmJitToInterpTraceSelectNoChain:
+ .word dvmJitToInterpTraceSelectNoChain
+.LdvmJitToInterpNoChain:
+ .word dvmJitToInterpNoChain
+.LdvmMterpStdBail:
+ .word dvmMterpStdBail
+.LdvmMterpCommonExceptionThrown:
+ .word dvmMterpCommonExceptionThrown
+.LdvmLockObject:
+ .word dvmLockObject
+#if defined(WITH_JIT_TUNING)
+.LdvmICHitCount:
+ .word gDvmICHitCount
+#endif
+#if defined(WITH_SELF_VERIFICATION)
+.LdvmSelfVerificationMemOpDecode:
+ .word dvmSelfVerificationMemOpDecode
+#endif
+.L__aeabi_cdcmple:
+ .word __aeabi_cdcmple
+.L__aeabi_cfcmple:
+ .word __aeabi_cfcmple
+
+ .global dmvCompilerTemplateEnd
+dmvCompilerTemplateEnd:
+
+#endif /* WITH_JIT */
diff --git a/vm/compiler/template/armv5te/header.S b/vm/compiler/template/armv5te/header.S
new file mode 100644
index 0000000..e6b3362
--- /dev/null
+++ b/vm/compiler/template/armv5te/header.S
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if defined(WITH_JIT)
+
+/*
+ * ARMv5 definitions and declarations.
+ */
+
+/*
+ARM EABI general notes:
+
+r0-r3 hold first 4 args to a method; they are not preserved across method calls
+r4-r8 are available for general use
+r9 is given special treatment in some situations, but not for us
+r10 (sl) seems to be generally available
+r11 (fp) is used by gcc (unless -fomit-frame-pointer is set)
+r12 (ip) is scratch -- not preserved across method calls
+r13 (sp) should be managed carefully in case a signal arrives
+r14 (lr) must be preserved
+r15 (pc) can be tinkered with directly
+
+r0 holds returns of <= 4 bytes
+r0-r1 hold returns of 8 bytes, low word in r0
+
+Callee must save/restore r4+ (except r12) if it modifies them.
+
+Stack is "full descending". Only the arguments that don't fit in the first 4
+registers are placed on the stack. "sp" points at the first stacked argument
+(i.e. the 5th arg).
+
+VFP: single-precision results in s0, double-precision results in d0.
+
+In the EABI, "sp" must be 64-bit aligned on entry to a function, and any
+64-bit quantities (long long, double) must be 64-bit aligned.
+*/
+
+/*
+JIT and ARM notes:
+
+The following registers have fixed assignments:
+
+ reg nick purpose
+ r5 rFP interpreted frame pointer, used for accessing locals and args
+ r6 rGLUE MterpGlue pointer
+
+The following registers have fixed assignments in mterp but are scratch
+registers in compiled code
+
+ reg nick purpose
+ r4 rPC interpreted program counter, used for fetching instructions
+ r7 rINST first 16-bit code unit of current instruction
+ r8 rIBASE interpreted instruction base pointer, used for computed goto
+
+Macros are provided for common operations. Each macro MUST emit only
+one instruction to make instruction-counting easier. They MUST NOT alter
+unspecified registers or condition codes.
+*/
+
+/* single-purpose registers, given names for clarity */
+#define rPC r4
+#define rFP r5
+#define rGLUE r6
+#define rINST r7
+#define rIBASE r8
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+#define SAVEAREA_FROM_FP(_reg, _fpreg) \
+ sub _reg, _fpreg, #sizeofStackSaveArea
+
+#define EXPORT_PC() \
+ str rPC, [rFP, #(-sizeofStackSaveArea + offStackSaveArea_currentPc)]
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../../../mterp/common/asm-constants.h"
diff --git a/vm/compiler/template/armv5te/platform.S b/vm/compiler/template/armv5te/platform.S
new file mode 100644
index 0000000..880e875
--- /dev/null
+++ b/vm/compiler/template/armv5te/platform.S
@@ -0,0 +1,16 @@
+/*
+ * ===========================================================================
+ * CPU-version-specific defines and utility
+ * ===========================================================================
+ */
+
+/*
+ * Macro for "MOV LR,PC / LDR PC,xxx", which is not allowed pre-ARMv5.
+ * Jump to subroutine.
+ *
+ * May modify IP and LR.
+ */
+.macro LDR_PC_LR source
+ mov lr, pc
+ ldr pc, \source
+.endm
diff --git a/vm/compiler/template/armv7-a-neon/TemplateOpList.h b/vm/compiler/template/armv7-a-neon/TemplateOpList.h
new file mode 100644
index 0000000..d991bed
--- /dev/null
+++ b/vm/compiler/template/armv7-a-neon/TemplateOpList.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik opcode list that uses additional templates to complete JIT execution.
+ */
+#ifndef JIT_TEMPLATE
+#define JIT_TEMPLATE(X)
+#endif
+
+JIT_TEMPLATE(CMP_LONG)
+JIT_TEMPLATE(RETURN)
+JIT_TEMPLATE(INVOKE_METHOD_NO_OPT)
+JIT_TEMPLATE(INVOKE_METHOD_CHAIN)
+JIT_TEMPLATE(INVOKE_METHOD_PREDICTED_CHAIN)
+JIT_TEMPLATE(INVOKE_METHOD_NATIVE)
+JIT_TEMPLATE(MUL_LONG)
+JIT_TEMPLATE(SHL_LONG)
+JIT_TEMPLATE(SHR_LONG)
+JIT_TEMPLATE(USHR_LONG)
+JIT_TEMPLATE(ADD_FLOAT_VFP)
+JIT_TEMPLATE(SUB_FLOAT_VFP)
+JIT_TEMPLATE(MUL_FLOAT_VFP)
+JIT_TEMPLATE(DIV_FLOAT_VFP)
+JIT_TEMPLATE(ADD_DOUBLE_VFP)
+JIT_TEMPLATE(SUB_DOUBLE_VFP)
+JIT_TEMPLATE(MUL_DOUBLE_VFP)
+JIT_TEMPLATE(DIV_DOUBLE_VFP)
+JIT_TEMPLATE(DOUBLE_TO_FLOAT_VFP)
+JIT_TEMPLATE(DOUBLE_TO_INT_VFP)
+JIT_TEMPLATE(FLOAT_TO_DOUBLE_VFP)
+JIT_TEMPLATE(FLOAT_TO_INT_VFP)
+JIT_TEMPLATE(INT_TO_DOUBLE_VFP)
+JIT_TEMPLATE(INT_TO_FLOAT_VFP)
+JIT_TEMPLATE(CMPG_DOUBLE_VFP)
+JIT_TEMPLATE(CMPL_DOUBLE_VFP)
+JIT_TEMPLATE(CMPG_FLOAT_VFP)
+JIT_TEMPLATE(CMPL_FLOAT_VFP)
+JIT_TEMPLATE(SQRT_DOUBLE_VFP)
+JIT_TEMPLATE(THROW_EXCEPTION_COMMON)
+JIT_TEMPLATE(MEM_OP_DECODE)
+JIT_TEMPLATE(STRING_COMPARETO)
+JIT_TEMPLATE(STRING_INDEXOF)
+JIT_TEMPLATE(INTERPRET)
+JIT_TEMPLATE(MONITOR_ENTER)
+JIT_TEMPLATE(MONITOR_ENTER_DEBUG)
diff --git a/vm/compiler/template/armv7-a/TemplateOpList.h b/vm/compiler/template/armv7-a/TemplateOpList.h
new file mode 100644
index 0000000..d991bed
--- /dev/null
+++ b/vm/compiler/template/armv7-a/TemplateOpList.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik opcode list that uses additional templates to complete JIT execution.
+ */
+#ifndef JIT_TEMPLATE
+#define JIT_TEMPLATE(X)
+#endif
+
+JIT_TEMPLATE(CMP_LONG)
+JIT_TEMPLATE(RETURN)
+JIT_TEMPLATE(INVOKE_METHOD_NO_OPT)
+JIT_TEMPLATE(INVOKE_METHOD_CHAIN)
+JIT_TEMPLATE(INVOKE_METHOD_PREDICTED_CHAIN)
+JIT_TEMPLATE(INVOKE_METHOD_NATIVE)
+JIT_TEMPLATE(MUL_LONG)
+JIT_TEMPLATE(SHL_LONG)
+JIT_TEMPLATE(SHR_LONG)
+JIT_TEMPLATE(USHR_LONG)
+JIT_TEMPLATE(ADD_FLOAT_VFP)
+JIT_TEMPLATE(SUB_FLOAT_VFP)
+JIT_TEMPLATE(MUL_FLOAT_VFP)
+JIT_TEMPLATE(DIV_FLOAT_VFP)
+JIT_TEMPLATE(ADD_DOUBLE_VFP)
+JIT_TEMPLATE(SUB_DOUBLE_VFP)
+JIT_TEMPLATE(MUL_DOUBLE_VFP)
+JIT_TEMPLATE(DIV_DOUBLE_VFP)
+JIT_TEMPLATE(DOUBLE_TO_FLOAT_VFP)
+JIT_TEMPLATE(DOUBLE_TO_INT_VFP)
+JIT_TEMPLATE(FLOAT_TO_DOUBLE_VFP)
+JIT_TEMPLATE(FLOAT_TO_INT_VFP)
+JIT_TEMPLATE(INT_TO_DOUBLE_VFP)
+JIT_TEMPLATE(INT_TO_FLOAT_VFP)
+JIT_TEMPLATE(CMPG_DOUBLE_VFP)
+JIT_TEMPLATE(CMPL_DOUBLE_VFP)
+JIT_TEMPLATE(CMPG_FLOAT_VFP)
+JIT_TEMPLATE(CMPL_FLOAT_VFP)
+JIT_TEMPLATE(SQRT_DOUBLE_VFP)
+JIT_TEMPLATE(THROW_EXCEPTION_COMMON)
+JIT_TEMPLATE(MEM_OP_DECODE)
+JIT_TEMPLATE(STRING_COMPARETO)
+JIT_TEMPLATE(STRING_INDEXOF)
+JIT_TEMPLATE(INTERPRET)
+JIT_TEMPLATE(MONITOR_ENTER)
+JIT_TEMPLATE(MONITOR_ENTER_DEBUG)
diff --git a/vm/compiler/template/config-armv5te b/vm/compiler/template/config-armv5te
new file mode 100644
index 0000000..668df1b
--- /dev/null
+++ b/vm/compiler/template/config-armv5te
@@ -0,0 +1,45 @@
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Configuration for ARMv5TE architecture targets.
+#
+
+# file header and basic definitions
+#import c/header.c
+import armv5te/header.S
+
+# C pre-processor defines for stub C instructions
+#import cstubs/stubdefs.c
+
+# highly-platform-specific defs
+import armv5te/platform.S
+
+# common defs for the C helpers; include this before the instruction handlers
+#import c/opcommon.c
+
+# opcode list; argument to op-start is default directory
+op-start armv5te
+
+op-end
+
+# "helper" code for C; include if you use any of the C stubs (this generates
+# object code, so it's normally excluded)
+##import c/gotoTargets.c
+
+# end of defs; include this when cstubs/stubdefs.c is included
+#import cstubs/enddefs.c
+
+# common subroutines for asm
+import armv5te/footer.S
diff --git a/vm/compiler/template/config-armv5te-vfp b/vm/compiler/template/config-armv5te-vfp
new file mode 100644
index 0000000..1b02261
--- /dev/null
+++ b/vm/compiler/template/config-armv5te-vfp
@@ -0,0 +1,62 @@
+
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Configuration for ARMv5TE architecture targets.
+#
+
+# file header and basic definitions
+#import c/header.c
+import armv5te/header.S
+
+# C pre-processor defines for stub C instructions
+#import cstubs/stubdefs.c
+
+# highly-platform-specific defs
+import armv5te-vfp/platform.S
+
+# common defs for the C helpers; include this before the instruction handlers
+#import c/opcommon.c
+
+# opcode list; argument to op-start is default directory
+op-start armv5te-vfp
+ op TEMPLATE_CMP_LONG armv5te
+ op TEMPLATE_INVOKE_METHOD_CHAIN armv5te
+ op TEMPLATE_INVOKE_METHOD_NATIVE armv5te
+ op TEMPLATE_INVOKE_METHOD_NO_OPT armv5te
+ op TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN armv5te
+ op TEMPLATE_MUL_LONG armv5te
+ op TEMPLATE_RETURN armv5te
+ op TEMPLATE_SHL_LONG armv5te
+ op TEMPLATE_SHR_LONG armv5te
+ op TEMPLATE_USHR_LONG armv5te
+ op TEMPLATE_THROW_EXCEPTION_COMMON armv5te
+ op TEMPLATE_STRING_COMPARETO armv5te
+ op TEMPLATE_STRING_INDEXOF armv5te
+ op TEMPLATE_INTERPRET armv5te
+ op TEMPLATE_MONITOR_ENTER armv5te
+ op TEMPLATE_MONITOR_ENTER_DEBUG armv5te
+
+op-end
+
+# "helper" code for C; include if you use any of the C stubs (this generates
+# object code, so it's normally excluded)
+##import c/gotoTargets.c
+
+# end of defs; include this when cstubs/stubdefs.c is included
+#import cstubs/enddefs.c
+
+# common subroutines for asm
+import armv5te/footer.S
diff --git a/vm/compiler/template/config-armv7-a b/vm/compiler/template/config-armv7-a
new file mode 100644
index 0000000..be7af31
--- /dev/null
+++ b/vm/compiler/template/config-armv7-a
@@ -0,0 +1,61 @@
+
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Configuration for ARMv7-a architecture targets.
+#
+
+# file header and basic definitions
+#import c/header.c
+import armv5te/header.S
+
+# C pre-processor defines for stub C instructions
+#import cstubs/stubdefs.c
+
+# highly-platform-specific defs
+import armv5te-vfp/platform.S
+
+# common defs for the C helpers; include this before the instruction handlers
+#import c/opcommon.c
+
+# opcode list; argument to op-start is default directory
+op-start armv5te-vfp
+ op TEMPLATE_CMP_LONG armv5te
+ op TEMPLATE_INVOKE_METHOD_CHAIN armv5te
+ op TEMPLATE_INVOKE_METHOD_NATIVE armv5te
+ op TEMPLATE_INVOKE_METHOD_NO_OPT armv5te
+ op TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN armv5te
+ op TEMPLATE_MUL_LONG armv5te
+ op TEMPLATE_RETURN armv5te
+ op TEMPLATE_SHL_LONG armv5te
+ op TEMPLATE_SHR_LONG armv5te
+ op TEMPLATE_USHR_LONG armv5te
+ op TEMPLATE_THROW_EXCEPTION_COMMON armv5te
+ op TEMPLATE_STRING_COMPARETO armv5te
+ op TEMPLATE_STRING_INDEXOF armv5te
+ op TEMPLATE_INTERPRET armv5te
+ op TEMPLATE_MONITOR_ENTER armv5te
+ op TEMPLATE_MONITOR_ENTER_DEBUG armv5te
+op-end
+
+# "helper" code for C; include if you use any of the C stubs (this generates
+# object code, so it's normally excluded)
+##import c/gotoTargets.c
+
+# end of defs; include this when cstubs/stubdefs.c is included
+#import cstubs/enddefs.c
+
+# common subroutines for asm
+import armv5te/footer.S
diff --git a/vm/compiler/template/config-armv7-a-neon b/vm/compiler/template/config-armv7-a-neon
new file mode 100644
index 0000000..be7af31
--- /dev/null
+++ b/vm/compiler/template/config-armv7-a-neon
@@ -0,0 +1,61 @@
+
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Configuration for ARMv7-a architecture targets.
+#
+
+# file header and basic definitions
+#import c/header.c
+import armv5te/header.S
+
+# C pre-processor defines for stub C instructions
+#import cstubs/stubdefs.c
+
+# highly-platform-specific defs
+import armv5te-vfp/platform.S
+
+# common defs for the C helpers; include this before the instruction handlers
+#import c/opcommon.c
+
+# opcode list; argument to op-start is default directory
+op-start armv5te-vfp
+ op TEMPLATE_CMP_LONG armv5te
+ op TEMPLATE_INVOKE_METHOD_CHAIN armv5te
+ op TEMPLATE_INVOKE_METHOD_NATIVE armv5te
+ op TEMPLATE_INVOKE_METHOD_NO_OPT armv5te
+ op TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN armv5te
+ op TEMPLATE_MUL_LONG armv5te
+ op TEMPLATE_RETURN armv5te
+ op TEMPLATE_SHL_LONG armv5te
+ op TEMPLATE_SHR_LONG armv5te
+ op TEMPLATE_USHR_LONG armv5te
+ op TEMPLATE_THROW_EXCEPTION_COMMON armv5te
+ op TEMPLATE_STRING_COMPARETO armv5te
+ op TEMPLATE_STRING_INDEXOF armv5te
+ op TEMPLATE_INTERPRET armv5te
+ op TEMPLATE_MONITOR_ENTER armv5te
+ op TEMPLATE_MONITOR_ENTER_DEBUG armv5te
+op-end
+
+# "helper" code for C; include if you use any of the C stubs (this generates
+# object code, so it's normally excluded)
+##import c/gotoTargets.c
+
+# end of defs; include this when cstubs/stubdefs.c is included
+#import cstubs/enddefs.c
+
+# common subroutines for asm
+import armv5te/footer.S
diff --git a/vm/compiler/template/gen-template.py b/vm/compiler/template/gen-template.py
new file mode 100755
index 0000000..8a1ba0c
--- /dev/null
+++ b/vm/compiler/template/gen-template.py
@@ -0,0 +1,422 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Using instructions from an architecture-specific config file, generate C
+# and assembly source files for the Dalvik JIT.
+#
+
+import sys, string, re, time
+from string import Template
+
+interp_defs_file = "TemplateOpList.h" # need opcode list
+
+handler_size_bits = -1000
+handler_size_bytes = -1000
+in_op_start = 0 # 0=not started, 1=started, 2=ended
+default_op_dir = None
+opcode_locations = {}
+asm_stub_text = []
+label_prefix = ".L" # use ".L" to hide labels from gdb
+
+
+# Exception class.
+class DataParseError(SyntaxError):
+ "Failure when parsing data file"
+
+#
+# Set any omnipresent substitution values.
+#
+def getGlobalSubDict():
+ return { "handler_size_bits":handler_size_bits,
+ "handler_size_bytes":handler_size_bytes }
+
+#
+# Parse arch config file --
+# Set handler_size_bytes to the value of tokens[1], and handler_size_bits to
+# log2(handler_size_bytes). Throws an exception if "bytes" is not a power
+# of two.
+#
+def setHandlerSize(tokens):
+ global handler_size_bits, handler_size_bytes
+ if len(tokens) != 2:
+ raise DataParseError("handler-size requires one argument")
+ if handler_size_bits != -1000:
+ raise DataParseError("handler-size may only be set once")
+
+ # compute log2(n), and make sure n is a power of 2
+ handler_size_bytes = bytes = int(tokens[1])
+ bits = -1
+ while bytes > 0:
+ bytes //= 2 # halve with truncating division
+ bits += 1
+
+ if handler_size_bytes == 0 or handler_size_bytes != (1 << bits):
+ raise DataParseError("handler-size (%d) must be power of 2 and > 0" \
+ % orig_bytes)
+ handler_size_bits = bits
+
+#
+# Parse arch config file --
+# Copy a file in to the C or asm output file.
+#
+def importFile(tokens):
+ if len(tokens) != 2:
+ raise DataParseError("import requires one argument")
+ source = tokens[1]
+ if source.endswith(".S"):
+ appendSourceFile(tokens[1], getGlobalSubDict(), asm_fp, None)
+ else:
+ raise DataParseError("don't know how to import %s (expecting .c/.S)"
+ % source)
+
+#
+# Parse arch config file --
+# Copy a file in to the C or asm output file.
+#
+def setAsmStub(tokens):
+ global asm_stub_text
+ if len(tokens) != 2:
+ raise DataParseError("import requires one argument")
+ try:
+ stub_fp = open(tokens[1])
+ asm_stub_text = stub_fp.readlines()
+ except IOError, err:
+ stub_fp.close()
+ raise DataParseError("unable to load asm-stub: %s" % str(err))
+ stub_fp.close()
+
+#
+# Parse arch config file --
+# Start of opcode list.
+#
+def opStart(tokens):
+ global in_op_start
+ global default_op_dir
+ if len(tokens) != 2:
+ raise DataParseError("opStart takes a directory name argument")
+ if in_op_start != 0:
+ raise DataParseError("opStart can only be specified once")
+ default_op_dir = tokens[1]
+ in_op_start = 1
+
+#
+# Parse arch config file --
+# Set location of a single opcode's source file.
+#
+def opEntry(tokens):
+ #global opcode_locations
+ if len(tokens) != 3:
+ raise DataParseError("op requires exactly two arguments")
+ if in_op_start != 1:
+ raise DataParseError("op statements must be between opStart/opEnd")
+ try:
+ index = opcodes.index(tokens[1])
+ except ValueError:
+ raise DataParseError("unknown opcode %s" % tokens[1])
+ opcode_locations[tokens[1]] = tokens[2]
+
+#
+# Parse arch config file --
+# End of opcode list; emit instruction blocks.
+#
+def opEnd(tokens):
+ global in_op_start
+ if len(tokens) != 1:
+ raise DataParseError("opEnd takes no arguments")
+ if in_op_start != 1:
+ raise DataParseError("opEnd must follow opStart, and only appear once")
+ in_op_start = 2
+
+ loadAndEmitOpcodes()
+
+
+#
+# Extract an ordered list of instructions from the VM sources. We use the
+# "goto table" definition macro, which has exactly 256 entries.
+#
+def getOpcodeList():
+ opcodes = []
+ opcode_fp = open("%s/%s" % (target_arch, interp_defs_file))
+ opcode_re = re.compile(r"^JIT_TEMPLATE\((\w+)\)", re.DOTALL)
+ for line in opcode_fp:
+ match = opcode_re.match(line)
+ if not match:
+ continue
+ opcodes.append("TEMPLATE_" + match.group(1))
+ opcode_fp.close()
+
+ return opcodes
+
+
+#
+# Load and emit opcodes for all 256 instructions.
+#
+def loadAndEmitOpcodes():
+ sister_list = []
+
+ # point dvmAsmInstructionStart at the first handler or stub
+ asm_fp.write("\n .global dvmCompilerTemplateStart\n")
+ asm_fp.write(" .type dvmCompilerTemplateStart, %function\n")
+ asm_fp.write(" .text\n\n")
+ asm_fp.write("dvmCompilerTemplateStart:\n\n")
+
+ for i in xrange(len(opcodes)):
+ op = opcodes[i]
+
+ if opcode_locations.has_key(op):
+ location = opcode_locations[op]
+ else:
+ location = default_op_dir
+
+ loadAndEmitAsm(location, i, sister_list)
+
+ # Use variable sized handlers now
+ # asm_fp.write("\n .balign %d\n" % handler_size_bytes)
+ asm_fp.write(" .size dvmCompilerTemplateStart, .-dvmCompilerTemplateStart\n")
+
+#
+# Load an assembly fragment and emit it.
+#
+def loadAndEmitAsm(location, opindex, sister_list):
+ op = opcodes[opindex]
+ source = "%s/%s.S" % (location, op)
+ dict = getGlobalSubDict()
+ dict.update({ "opcode":op, "opnum":opindex })
+ print " emit %s --> asm" % source
+
+ emitAsmHeader(asm_fp, dict)
+ appendSourceFile(source, dict, asm_fp, sister_list)
+
+#
+# Output the alignment directive and label for an assembly piece.
+#
+def emitAsmHeader(outfp, dict):
+ outfp.write("/* ------------------------------ */\n")
+ # The alignment directive ensures that the handler occupies
+ # at least the correct amount of space. We don't try to deal
+ # with overflow here.
+ outfp.write(" .balign 4\n")
+ # Emit a label so that gdb will say the right thing. We prepend an
+ # underscore so the symbol name doesn't clash with the OpCode enum.
+ template_name = "dvmCompiler_%(opcode)s" % dict
+ outfp.write(" .global %s\n" % template_name);
+ outfp.write("%s:\n" % template_name);
+
+#
+# Output a generic instruction stub that updates the "glue" struct and
+# calls the C implementation.
+#
+def emitAsmStub(outfp, dict):
+ emitAsmHeader(outfp, dict)
+ for line in asm_stub_text:
+ templ = Template(line)
+ outfp.write(templ.substitute(dict))
+
+#
+# Append the file specified by "source" to the open "outfp". Each line will
+# be template-replaced using the substitution dictionary "dict".
+#
+# If the first line of the file starts with "%" it is taken as a directive.
+# A "%include" line contains a filename and, optionally, a Python-style
+# dictionary declaration with substitution strings. (This is implemented
+# with recursion.)
+#
+# If "sister_list" is provided, and we find a line that contains only "&",
+# all subsequent lines from the file will be appended to sister_list instead
+# of copied to the output.
+#
+# This may modify "dict".
+#
+def appendSourceFile(source, dict, outfp, sister_list):
+ outfp.write("/* File: %s */\n" % source)
+ infp = open(source, "r")
+ in_sister = False
+ for line in infp:
+ if line.startswith("%include"):
+ # Parse the "include" line
+ tokens = line.strip().split(' ', 2)
+ if len(tokens) < 2:
+ raise DataParseError("malformed %%include in %s" % source)
+
+ alt_source = tokens[1].strip("\"")
+ if alt_source == source:
+ raise DataParseError("self-referential %%include in %s"
+ % source)
+
+ new_dict = dict.copy()
+ if len(tokens) == 3:
+ new_dict.update(eval(tokens[2]))
+ #print " including src=%s dict=%s" % (alt_source, new_dict)
+ appendSourceFile(alt_source, new_dict, outfp, sister_list)
+ continue
+
+ elif line.startswith("%default"):
+ # copy keywords into dictionary
+ tokens = line.strip().split(' ', 1)
+ if len(tokens) < 2:
+ raise DataParseError("malformed %%default in %s" % source)
+ defaultValues = eval(tokens[1])
+ for entry in defaultValues:
+ dict.setdefault(entry, defaultValues[entry])
+ continue
+
+ elif line.startswith("%verify"):
+ # more to come, someday
+ continue
+
+ elif line.startswith("%break") and sister_list != None:
+ # allow more than one %break, ignoring all following the first
+ if not in_sister:
+ in_sister = True
+ sister_list.append("\n/* continuation for %(opcode)s */\n"%dict)
+ continue
+
+ # perform keyword substitution if a dictionary was provided
+ if dict != None:
+ templ = Template(line)
+ try:
+ subline = templ.substitute(dict)
+ except KeyError, err:
+ raise DataParseError("keyword substitution failed in %s: %s"
+ % (source, str(err)))
+ except:
+ print "ERROR: substitution failed: " + line
+ raise
+ else:
+ subline = line
+
+ # write output to appropriate file
+ if in_sister:
+ sister_list.append(subline)
+ else:
+ outfp.write(subline)
+ outfp.write("\n")
+ infp.close()
+
+#
+# Emit a C-style section header comment.
+#
+def emitSectionComment(str, fp):
+ equals = "========================================" \
+ "==================================="
+
+ fp.write("\n/*\n * %s\n * %s\n * %s\n */\n" %
+ (equals, str, equals))
+
+
+#
+# ===========================================================================
+# "main" code
+#
+
+#
+# Check args.
+#
+if len(sys.argv) != 3:
+ print "Usage: %s target-arch output-dir" % sys.argv[0]
+ sys.exit(2)
+
+target_arch = sys.argv[1]
+output_dir = sys.argv[2]
+
+#
+# Extract opcode list.
+#
+opcodes = getOpcodeList()
+#for op in opcodes:
+# print " %s" % op
+
+#
+# Open config file.
+#
+try:
+ config_fp = open("config-%s" % target_arch)
+except:
+ print "Unable to open config file 'config-%s'" % target_arch
+ sys.exit(1)
+
+#
+# Open and prepare output files.
+#
+try:
+ asm_fp = open("%s/CompilerTemplateAsm-%s.S" % (output_dir, target_arch), "w")
+except:
+ print "Unable to open output files"
+ print "Make sure directory '%s' exists and existing files are writable" \
+ % output_dir
+ # Ideally we'd remove the files to avoid confusing "make", but if they
+ # failed to open we probably won't be able to remove them either.
+ sys.exit(1)
+
+print "Generating %s" % (asm_fp.name)
+
+file_header = """/*
+ * This file was generated automatically by gen-template.py for '%s'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+""" % (target_arch)
+
+asm_fp.write(file_header)
+
+#
+# Process the config file.
+#
+failed = False
+try:
+ for line in config_fp:
+ line = line.strip() # remove CRLF, leading spaces
+ tokens = line.split(' ') # tokenize
+ #print "%d: %s" % (len(tokens), tokens)
+ if len(tokens[0]) == 0:
+ #print " blank"
+ pass
+ elif tokens[0][0] == '#':
+ #print " comment"
+ pass
+ else:
+ if tokens[0] == "handler-size":
+ setHandlerSize(tokens)
+ elif tokens[0] == "import":
+ importFile(tokens)
+ elif tokens[0] == "asm-stub":
+ setAsmStub(tokens)
+ elif tokens[0] == "op-start":
+ opStart(tokens)
+ elif tokens[0] == "op-end":
+ opEnd(tokens)
+ elif tokens[0] == "op":
+ opEntry(tokens)
+ else:
+ raise DataParseError, "unrecognized command '%s'" % tokens[0]
+except DataParseError, err:
+ print "Failed: " + str(err)
+ # TODO: remove output files so "make" doesn't get confused
+ failed = True
+ asm_fp.close()
+ c_fp = asm_fp = None
+
+config_fp.close()
+
+#
+# Done!
+#
+if asm_fp:
+ asm_fp.close()
+
+sys.exit(failed)
diff --git a/vm/compiler/template/out/CompilerTemplateAsm-armv5te-vfp.S b/vm/compiler/template/out/CompilerTemplateAsm-armv5te-vfp.S
new file mode 100644
index 0000000..60664fa
--- /dev/null
+++ b/vm/compiler/template/out/CompilerTemplateAsm-armv5te-vfp.S
@@ -0,0 +1,1544 @@
+/*
+ * This file was generated automatically by gen-template.py for 'armv5te-vfp'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: armv5te/header.S */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if defined(WITH_JIT)
+
+/*
+ * ARMv5 definitions and declarations.
+ */
+
+/*
+ARM EABI general notes:
+
+r0-r3 hold first 4 args to a method; they are not preserved across method calls
+r4-r8 are available for general use
+r9 is given special treatment in some situations, but not for us
+r10 (sl) seems to be generally available
+r11 (fp) is used by gcc (unless -fomit-frame-pointer is set)
+r12 (ip) is scratch -- not preserved across method calls
+r13 (sp) should be managed carefully in case a signal arrives
+r14 (lr) must be preserved
+r15 (pc) can be tinkered with directly
+
+r0 holds returns of <= 4 bytes
+r0-r1 hold returns of 8 bytes, low word in r0
+
+Callee must save/restore r4+ (except r12) if it modifies them.
+
+Stack is "full descending". Only the arguments that don't fit in the first 4
+registers are placed on the stack. "sp" points at the first stacked argument
+(i.e. the 5th arg).
+
+VFP: single-precision results in s0, double-precision results in d0.
+
+In the EABI, "sp" must be 64-bit aligned on entry to a function, and any
+64-bit quantities (long long, double) must be 64-bit aligned.
+*/
+
+/*
+JIT and ARM notes:
+
+The following registers have fixed assignments:
+
+ reg nick purpose
+ r5 rFP interpreted frame pointer, used for accessing locals and args
+ r6 rGLUE MterpGlue pointer
+
+The following registers have fixed assignments in mterp but are scratch
+registers in compiled code
+
+ reg nick purpose
+ r4 rPC interpreted program counter, used for fetching instructions
+ r7 rINST first 16-bit code unit of current instruction
+ r8 rIBASE interpreted instruction base pointer, used for computed goto
+
+Macros are provided for common operations. Each macro MUST emit only
+one instruction to make instruction-counting easier. They MUST NOT alter
+unspecified registers or condition codes.
+*/
+
+/* single-purpose registers, given names for clarity */
+#define rPC r4
+#define rFP r5
+#define rGLUE r6
+#define rINST r7
+#define rIBASE r8
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+#define SAVEAREA_FROM_FP(_reg, _fpreg) \
+ sub _reg, _fpreg, #sizeofStackSaveArea
+
+#define EXPORT_PC() \
+ str rPC, [rFP, #(-sizeofStackSaveArea + offStackSaveArea_currentPc)]
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../../../mterp/common/asm-constants.h"
+
+/* File: armv5te-vfp/platform.S */
+/*
+ * ===========================================================================
+ * CPU-version-specific defines and utility
+ * ===========================================================================
+ */
+
+/*
+ * Macro for "MOV LR,PC / LDR PC,xxx", which is not allowed pre-ARMv5.
+ * Jump to subroutine.
+ *
+ * May modify IP and LR.
+ */
+.macro LDR_PC_LR source
+ mov lr, pc
+ ldr pc, \source
+.endm
+
+
+ .global dvmCompilerTemplateStart
+ .type dvmCompilerTemplateStart, %function
+ .text
+
+dvmCompilerTemplateStart:
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_CMP_LONG
+dvmCompiler_TEMPLATE_CMP_LONG:
+/* File: armv5te/TEMPLATE_CMP_LONG.S */
+ /*
+ * Compare two 64-bit values. Puts 0, 1, or -1 into the destination
+ * register based on the results of the comparison.
+ *
+ * We load the full values with LDM, but in practice many values could
+ * be resolved by only looking at the high word. This could be made
+ * faster or slower by splitting the LDM into a pair of LDRs.
+ *
+ * If we just wanted to set condition flags, we could do this:
+ * subs ip, r0, r2
+ * sbcs ip, r1, r3
+ * subeqs ip, r0, r2
+ * Leaving { <0, 0, >0 } in ip. However, we have to set it to a specific
+ * integer value, which we can do with 2 conditional mov/mvn instructions
+ * (set 1, set -1; if they're equal we already have 0 in ip), giving
+ * us a constant 5-cycle path plus a branch at the end to the
+ * instruction epilogue code. The multi-compare approach below needs
+ * 2 or 3 cycles + branch if the high word doesn't match, 6 + branch
+ * in the worst case (the 64-bit values are equal).
+ */
+ /* cmp-long vAA, vBB, vCC */
+ cmp r1, r3 @ compare (vBB+1, vCC+1)
+ blt .LTEMPLATE_CMP_LONG_less @ signed compare on high part
+ bgt .LTEMPLATE_CMP_LONG_greater
+ subs r0, r0, r2 @ r0<- r0 - r2
+ bxeq lr
+ bhi .LTEMPLATE_CMP_LONG_greater @ unsigned compare on low part
+.LTEMPLATE_CMP_LONG_less:
+ mvn r0, #0 @ r0<- -1
+ bx lr
+.LTEMPLATE_CMP_LONG_greater:
+ mov r0, #1 @ r0<- 1
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_RETURN
+dvmCompiler_TEMPLATE_RETURN:
+/* File: armv5te/TEMPLATE_RETURN.S */
+ /*
+ * Unwind a frame from the Dalvik stack for compiled OP_RETURN_XXX.
+ * If the stored value in returnAddr
+ * is non-zero, the caller is compiled by the JIT thus return to the
+ * address in the code cache following the invoke instruction. Otherwise
+ * return to the special dvmJitToInterpNoChain entry point.
+ */
+ SAVEAREA_FROM_FP(r0, rFP) @ r0<- saveArea (old)
+ ldr r10, [r0, #offStackSaveArea_prevFrame] @ r10<- saveArea->prevFrame
+ ldr r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+ ldr rPC, [r0, #offStackSaveArea_savedPc] @ rPC<- saveArea->savedPc
+#if !defined(WITH_SELF_VERIFICATION)
+ ldr r9, [r0, #offStackSaveArea_returnAddr] @ r9<- chaining cell ret
+#else
+ mov r9, #0 @ disable chaining
+#endif
+ ldr r2, [r10, #(offStackSaveArea_method - sizeofStackSaveArea)]
+ @ r2<- method we're returning to
+ ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
+ cmp r2, #0 @ break frame?
+#if !defined(WITH_SELF_VERIFICATION)
+ beq 1f @ bail to interpreter
+#else
+ blxeq lr @ punt to interpreter and compare state
+#endif
+ ldr r1, .LdvmJitToInterpNoChainNoProfile @ defined in footer.S
+ mov rFP, r10 @ publish new FP
+ ldrne r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+ ldr r8, [r8] @ r8<- suspendCount
+
+ str r2, [rGLUE, #offGlue_method]@ glue->method = newSave->method
+ ldr r0, [r10, #offClassObject_pDvmDex] @ r0<- method->clazz->pDvmDex
+ str rFP, [r3, #offThread_curFrame] @ self->curFrame = fp
+ add rPC, rPC, #6 @ publish new rPC (advance 6 bytes)
+ str r0, [rGLUE, #offGlue_methodClassDex]
+ cmp r8, #0 @ check the suspendCount
+ movne r9, #0 @ clear the chaining cell address
+ str r9, [r3, #offThread_inJitCodeCache] @ in code cache or not
+ cmp r9, #0 @ chaining cell exists?
+ blxne r9 @ jump to the chaining cell
+#if defined(WITH_JIT_TUNING)
+ mov r0, #kCallsiteInterpreted
+#endif
+ mov pc, r1 @ callsite is interpreted
+1:
+ stmia rGLUE, {rPC, rFP} @ SAVE_PC_FP_TO_GLUE()
+ ldr r2, .LdvmMterpStdBail @ defined in footer.S
+ mov r1, #0 @ changeInterp = false
+ mov r0, rGLUE @ Expecting rGLUE in r0
+ blx r2 @ exit the interpreter
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S */
+ /*
+ * For polymorphic callsites - setup the Dalvik frame and load Dalvik PC
+ * into rPC then jump to dvmJitToInterpNoChain to dispatch the
+ * runtime-resolved callee.
+ */
+ @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+ ldrh r7, [r0, #offMethod_registersSize] @ r7<- methodToCall->regsSize
+ ldrh r2, [r0, #offMethod_outsSize] @ r2<- methodToCall->outsSize
+ ldr r9, [rGLUE, #offGlue_interpStackEnd] @ r9<- interpStackEnd
+ ldr r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+ add r3, r1, #1 @ Thumb addr is odd
+ SAVEAREA_FROM_FP(r1, rFP) @ r1<- stack save area
+ sub r1, r1, r7, lsl #2 @ r1<- newFp (old savearea - regsSize)
+ SAVEAREA_FROM_FP(r10, r1) @ r10<- stack save area
+ sub r10, r10, r2, lsl #2 @ r10<- bottom (newsave - outsSize)
+ ldr r8, [r8] @ r8<- suspendCount (int)
+ cmp r10, r9 @ bottom < interpStackEnd?
+ bxlo lr @ return to raise stack overflow excep.
+ @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+ ldr r9, [r0, #offMethod_clazz] @ r9<- method->clazz
+ ldr r10, [r0, #offMethod_accessFlags] @ r10<- methodToCall->accessFlags
+ str rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+ str rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+ ldr rPC, [r0, #offMethod_insns] @ rPC<- methodToCall->insns
+
+
+ @ set up newSaveArea
+ str rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+ str r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+ str r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+ cmp r8, #0 @ suspendCount != 0
+ bxne lr @ bail to the interpreter
+ tst r10, #ACC_NATIVE
+#if !defined(WITH_SELF_VERIFICATION)
+ bne .LinvokeNative
+#else
+ bxne lr @ bail to the interpreter
+#endif
+
+ ldr r10, .LdvmJitToInterpTraceSelectNoChain
+ ldr r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+ ldr r2, [rGLUE, #offGlue_self] @ r2<- glue->self
+
+ @ Update "glue" values for the new method
+ str r0, [rGLUE, #offGlue_method] @ glue->method = methodToCall
+ str r3, [rGLUE, #offGlue_methodClassDex] @ glue->methodClassDex = ...
+ mov rFP, r1 @ fp = newFp
+ str rFP, [r2, #offThread_curFrame] @ self->curFrame = newFp
+
+ @ Start executing the callee
+#if defined(WITH_JIT_TUNING)
+ mov r0, #kInlineCacheMiss
+#endif
+ mov pc, r10 @ dvmJitToInterpTraceSelectNoChain
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN
+dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S */
+ /*
+ * For monomorphic callsite, setup the Dalvik frame and return to the
+ * Thumb code through the link register to transfer control to the callee
+ * method through a dedicated chaining cell.
+ */
+ @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+ @ methodToCall is guaranteed to be non-native
+.LinvokeChain:
+ ldrh r7, [r0, #offMethod_registersSize] @ r7<- methodToCall->regsSize
+ ldrh r2, [r0, #offMethod_outsSize] @ r2<- methodToCall->outsSize
+ ldr r9, [rGLUE, #offGlue_interpStackEnd] @ r9<- interpStackEnd
+ ldr r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+ add r3, r1, #1 @ Thumb addr is odd
+ SAVEAREA_FROM_FP(r1, rFP) @ r1<- stack save area
+ sub r1, r1, r7, lsl #2 @ r1<- newFp (old savearea - regsSize)
+ SAVEAREA_FROM_FP(r10, r1) @ r10<- stack save area
+ add r12, lr, #2 @ setup the punt-to-interp address
+ sub r10, r10, r2, lsl #2 @ r10<- bottom (newsave - outsSize)
+ ldr r8, [r8] @ r8<- suspendCount (int)
+ cmp r10, r9 @ bottom < interpStackEnd?
+ bxlo r12 @ return to raise stack overflow excep.
+ @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+ ldr r9, [r0, #offMethod_clazz] @ r9<- method->clazz
+ str rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+ str rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+ ldr rPC, [r0, #offMethod_insns] @ rPC<- methodToCall->insns
+
+
+ @ set up newSaveArea
+ str rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+ str r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+ str r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+ cmp r8, #0 @ suspendCount != 0
+ bxne r12 @ bail to the interpreter
+
+ ldr r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+ ldr r2, [rGLUE, #offGlue_self] @ r2<- glue->self
+
+ @ Update "glue" values for the new method
+ str r0, [rGLUE, #offGlue_method] @ glue->method = methodToCall
+ str r3, [rGLUE, #offGlue_methodClassDex] @ glue->methodClassDex = ...
+ mov rFP, r1 @ fp = newFp
+ str rFP, [r2, #offThread_curFrame] @ self->curFrame = newFp
+
+ bx lr @ return to the callee-chaining cell
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN
+dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S */
+ /*
+ * For polymorphic callsite, check whether the cached class pointer matches
+ * the current one. If so setup the Dalvik frame and return to the
+ * Thumb code through the link register to transfer control to the callee
+ * method through a dedicated chaining cell.
+ *
+ * The predicted chaining cell is declared in ArmLIR.h with the
+ * following layout:
+ *
+ * typedef struct PredictedChainingCell {
+ * u4 branch;
+ * const ClassObject *clazz;
+ * const Method *method;
+ * u4 counter;
+ * } PredictedChainingCell;
+ *
+ * Upon returning to the callsite:
+ * - lr : to branch to the chaining cell
+ * - lr+2: to punt to the interpreter
+ * - lr+4: to fully resolve the callee and may rechain.
+ * r3 <- class
+ * r9 <- counter
+ */
+ @ r0 = this, r1 = returnCell, r2 = predictedChainCell, rPC = dalvikCallsite
+ ldr r3, [r0, #offObject_clazz] @ r3 <- this->class
+ ldr r8, [r2, #4] @ r8 <- predictedChainCell->clazz
+ ldr r0, [r2, #8] @ r0 <- predictedChainCell->method
+ ldr r9, [rGLUE, #offGlue_icRechainCount] @ r1 <- shared rechainCount
+ cmp r3, r8 @ predicted class == actual class?
+#if defined(WITH_JIT_TUNING)
+ ldr r7, .LdvmICHitCount
+ ldreq r10, [r7, #0]
+ add r10, r10, #1
+ streq r10, [r7, #0]
+#endif
+ beq .LinvokeChain @ predicted chain is valid
+ ldr r7, [r3, #offClassObject_vtable] @ r7 <- this->class->vtable
+ cmp r8, #0 @ initialized class or not
+ moveq r1, #0
+ subne r1, r9, #1 @ count--
+ strne r1, [rGLUE, #offGlue_icRechainCount] @ write back to InterpState
+ add lr, lr, #4 @ return to fully-resolve landing pad
+ /*
+ * r1 <- count
+ * r2 <- &predictedChainCell
+ * r3 <- this->class
+ * r4 <- dPC
+ * r7 <- this->class->vtable
+ */
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S */
+ @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+ ldrh r7, [r0, #offMethod_registersSize] @ r7<- methodToCall->regsSize
+ ldr r9, [rGLUE, #offGlue_interpStackEnd] @ r9<- interpStackEnd
+ ldr r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+ add r3, r1, #1 @ Thumb addr is odd
+ SAVEAREA_FROM_FP(r1, rFP) @ r1<- stack save area
+ sub r1, r1, r7, lsl #2 @ r1<- newFp (old savearea - regsSize)
+ SAVEAREA_FROM_FP(r10, r1) @ r10<- stack save area
+ ldr r8, [r8] @ r3<- suspendCount (int)
+ cmp r10, r9 @ bottom < interpStackEnd?
+ bxlo lr @ return to raise stack overflow excep.
+ @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+ str rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+ str rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+ ldr rPC, [r0, #offMethod_insns] @ rPC<- methodToCall->insns
+
+
+ @ set up newSaveArea
+ str rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+ str r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+ ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
+ str r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+ cmp r8, #0 @ suspendCount != 0
+ ldr r8, [r0, #offMethod_nativeFunc] @ r8<- method->nativeFunc
+#if !defined(WITH_SELF_VERIFICATION)
+ bxne lr @ bail to the interpreter
+#else
+ bx lr @ bail to interpreter unconditionally
+#endif
+
+ @ go ahead and transfer control to the native code
+ ldr r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
+ mov r2, #0
+ str r1, [r3, #offThread_curFrame] @ self->curFrame = newFp
+ str r2, [r3, #offThread_inJitCodeCache] @ not in the jit code cache
+ str r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+ @ newFp->localRefCookie=top
+ mov r9, r3 @ r9<- glue->self (preserve)
+ SAVEAREA_FROM_FP(r10, r1) @ r10<- new stack save area
+
+ mov r2, r0 @ r2<- methodToCall
+ mov r0, r1 @ r0<- newFP
+ add r1, rGLUE, #offGlue_retval @ r1<- &retval
+
+ blx r8 @ off to the native code
+
+ @ native return; r9=self, r10=newSaveArea
+ @ equivalent to dvmPopJniLocals
+ ldr r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+ ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+ ldr r1, [r9, #offThread_exception] @ check for exception
+ str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
+ cmp r1, #0 @ null?
+ str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
+ ldr r0, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+
+ @ r0 = dalvikCallsitePC
+ bne .LhandleException @ no, handle exception
+
+ str r2, [r9, #offThread_inJitCodeCache] @ set the mode properly
+ cmp r2, #0 @ return chaining cell still exists?
+ bxne r2 @ yes - go ahead
+
+ @ continue executing the next instruction through the interpreter
+ ldr r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+ add rPC, r0, #6 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+ mov r0, #kCallsiteInterpreted
+#endif
+ mov pc, r1
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_MUL_LONG
+dvmCompiler_TEMPLATE_MUL_LONG:
+/* File: armv5te/TEMPLATE_MUL_LONG.S */
+ /*
+ * Signed 64-bit integer multiply.
+ *
+ * For JIT: op1 in r0/r1, op2 in r2/r3, return in r0/r1
+ *
+ * Consider WXxYZ (r1r0 x r3r2) with a long multiply:
+ * WX
+ * x YZ
+ * --------
+ * ZW ZX
+ * YW YX
+ *
+ * The low word of the result holds ZX, the high word holds
+ * (ZW+YX) + (the high overflow from ZX). YW doesn't matter because
+ * it doesn't fit in the low 64 bits.
+ *
+ * Unlike most ARM math operations, multiply instructions have
+ * restrictions on using the same register more than once (Rd and Rm
+ * cannot be the same).
+ */
+ /* mul-long vAA, vBB, vCC */
+ mul ip, r2, r1 @ ip<- ZxW
+ umull r9, r10, r2, r0 @ r9/r10 <- ZxX
+ mla r2, r0, r3, ip @ r2<- YxX + (ZxW)
+ add r10, r2, r10 @ r10<- r10 + low(ZxW + (YxX))
+ mov r0,r9
+ mov r1,r10
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_SHL_LONG
+dvmCompiler_TEMPLATE_SHL_LONG:
+/* File: armv5te/TEMPLATE_SHL_LONG.S */
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to ignore all but the low
+ * 6 bits.
+ */
+ /* shl-long vAA, vBB, vCC */
+ and r2, r2, #63 @ r2<- r2 & 0x3f
+ mov r1, r1, asl r2 @ r1<- r1 << r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32)
+ mov r0, r0, asl r2 @ r0<- r0 << r2
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_SHR_LONG
+dvmCompiler_TEMPLATE_SHR_LONG:
+/* File: armv5te/TEMPLATE_SHR_LONG.S */
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to ignore all but the low
+ * 6 bits.
+ */
+ /* shr-long vAA, vBB, vCC */
+ and r2, r2, #63 @ r0<- r0 & 0x3f
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32)
+ mov r1, r1, asr r2 @ r1<- r1 >> r2
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_USHR_LONG
+dvmCompiler_TEMPLATE_USHR_LONG:
+/* File: armv5te/TEMPLATE_USHR_LONG.S */
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to ignore all but the low
+ * 6 bits.
+ */
+ /* ushr-long vAA, vBB, vCC */
+ and r2, r2, #63 @ r0<- r0 & 0x3f
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32)
+ mov r1, r1, lsr r2 @ r1<- r1 >>> r2
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_ADD_FLOAT_VFP
+dvmCompiler_TEMPLATE_ADD_FLOAT_VFP:
+/* File: armv5te-vfp/TEMPLATE_ADD_FLOAT_VFP.S */
+/* File: armv5te-vfp/fbinop.S */
+ /*
+ * Generic 32-bit floating point operation. Provide an "instr" line that
+ * specifies an instruction that performs s2 = s0 op s1.
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = op1 address
+ * r2 = op2 address
+ */
+ flds s0,[r1]
+ flds s1,[r2]
+ fadds s2, s0, s1
+ fsts s2,[r0]
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_SUB_FLOAT_VFP
+dvmCompiler_TEMPLATE_SUB_FLOAT_VFP:
+/* File: armv5te-vfp/TEMPLATE_SUB_FLOAT_VFP.S */
+/* File: armv5te-vfp/fbinop.S */
+ /*
+ * Generic 32-bit floating point operation. Provide an "instr" line that
+ * specifies an instruction that performs s2 = s0 op s1.
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = op1 address
+ * r2 = op2 address
+ */
+ flds s0,[r1]
+ flds s1,[r2]
+ fsubs s2, s0, s1
+ fsts s2,[r0]
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_MUL_FLOAT_VFP
+dvmCompiler_TEMPLATE_MUL_FLOAT_VFP:
+/* File: armv5te-vfp/TEMPLATE_MUL_FLOAT_VFP.S */
+/* File: armv5te-vfp/fbinop.S */
+ /*
+ * Generic 32-bit floating point operation. Provide an "instr" line that
+ * specifies an instruction that performs s2 = s0 op s1.
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = op1 address
+ * r2 = op2 address
+ */
+ flds s0,[r1]
+ flds s1,[r2]
+ fmuls s2, s0, s1
+ fsts s2,[r0]
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_DIV_FLOAT_VFP
+dvmCompiler_TEMPLATE_DIV_FLOAT_VFP:
+/* File: armv5te-vfp/TEMPLATE_DIV_FLOAT_VFP.S */
+/* File: armv5te-vfp/fbinop.S */
+ /*
+ * Generic 32-bit floating point operation. Provide an "instr" line that
+ * specifies an instruction that performs s2 = s0 op s1.
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = op1 address
+ * r2 = op2 address
+ */
+ flds s0,[r1]
+ flds s1,[r2]
+ fdivs s2, s0, s1
+ fsts s2,[r0]
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_ADD_DOUBLE_VFP
+dvmCompiler_TEMPLATE_ADD_DOUBLE_VFP:
+/* File: armv5te-vfp/TEMPLATE_ADD_DOUBLE_VFP.S */
+/* File: armv5te-vfp/fbinopWide.S */
+ /*
+ * Generic 64-bit floating point operation. Provide an "instr" line that
+ * specifies an instruction that performs s2 = s0 op s1.
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = op1 address
+ * r2 = op2 address
+ */
+ fldd d0,[r1]
+ fldd d1,[r2]
+ faddd d2, d0, d1
+ fstd d2,[r0]
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_SUB_DOUBLE_VFP
+dvmCompiler_TEMPLATE_SUB_DOUBLE_VFP:
+/* File: armv5te-vfp/TEMPLATE_SUB_DOUBLE_VFP.S */
+/* File: armv5te-vfp/fbinopWide.S */
+ /*
+ * Generic 64-bit floating point operation. Provide an "instr" line that
+ * specifies an instruction that performs s2 = s0 op s1.
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = op1 address
+ * r2 = op2 address
+ */
+ fldd d0,[r1]
+ fldd d1,[r2]
+ fsubd d2, d0, d1
+ fstd d2,[r0]
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_MUL_DOUBLE_VFP
+dvmCompiler_TEMPLATE_MUL_DOUBLE_VFP:
+/* File: armv5te-vfp/TEMPLATE_MUL_DOUBLE_VFP.S */
+/* File: armv5te-vfp/fbinopWide.S */
+ /*
+ * Generic 64-bit floating point operation. Provide an "instr" line that
+ * specifies an instruction that performs s2 = s0 op s1.
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = op1 address
+ * r2 = op2 address
+ */
+ fldd d0,[r1]
+ fldd d1,[r2]
+ fmuld d2, d0, d1
+ fstd d2,[r0]
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_DIV_DOUBLE_VFP
+dvmCompiler_TEMPLATE_DIV_DOUBLE_VFP:
+/* File: armv5te-vfp/TEMPLATE_DIV_DOUBLE_VFP.S */
+/* File: armv5te-vfp/fbinopWide.S */
+ /*
+ * Generic 64-bit floating point operation. Provide an "instr" line that
+ * specifies an instruction that performs s2 = s0 op s1.
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = op1 address
+ * r2 = op2 address
+ */
+ fldd d0,[r1]
+ fldd d1,[r2]
+ fdivd d2, d0, d1
+ fstd d2,[r0]
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_DOUBLE_TO_FLOAT_VFP
+dvmCompiler_TEMPLATE_DOUBLE_TO_FLOAT_VFP:
+/* File: armv5te-vfp/TEMPLATE_DOUBLE_TO_FLOAT_VFP.S */
+/* File: armv5te-vfp/funopNarrower.S */
+ /*
+ * Generic 64bit-to-32bit floating point unary operation. Provide an
+ * "instr" line that specifies an instruction that performs "s0 = op d0".
+ *
+ * For: double-to-int, double-to-float
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = src dalvik register address
+ */
+ /* unop vA, vB */
+ fldd d0, [r1] @ d0<- vB
+ fcvtsd s0, d0 @ s0<- op d0
+ fsts s0, [r0] @ vA<- s0
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_DOUBLE_TO_INT_VFP
+dvmCompiler_TEMPLATE_DOUBLE_TO_INT_VFP:
+/* File: armv5te-vfp/TEMPLATE_DOUBLE_TO_INT_VFP.S */
+/* File: armv5te-vfp/funopNarrower.S */
+ /*
+ * Generic 64bit-to-32bit floating point unary operation. Provide an
+ * "instr" line that specifies an instruction that performs "s0 = op d0".
+ *
+ * For: double-to-int, double-to-float
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = src dalvik register address
+ */
+ /* unop vA, vB */
+ fldd d0, [r1] @ d0<- vB
+ ftosizd s0, d0 @ s0<- op d0
+ fsts s0, [r0] @ vA<- s0
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_FLOAT_TO_DOUBLE_VFP
+dvmCompiler_TEMPLATE_FLOAT_TO_DOUBLE_VFP:
+/* File: armv5te-vfp/TEMPLATE_FLOAT_TO_DOUBLE_VFP.S */
+/* File: armv5te-vfp/funopWider.S */
+ /*
+ * Generic 32bit-to-64bit floating point unary operation. Provide an
+ * "instr" line that specifies an instruction that performs "d0 = op s0".
+ *
+ * For: int-to-double, float-to-double
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = src dalvik register address
+ */
+ /* unop vA, vB */
+ flds s0, [r1] @ s0<- vB
+ fcvtds d0, s0 @ d0<- op s0
+ fstd d0, [r0] @ vA<- d0
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_FLOAT_TO_INT_VFP
+dvmCompiler_TEMPLATE_FLOAT_TO_INT_VFP:
+/* File: armv5te-vfp/TEMPLATE_FLOAT_TO_INT_VFP.S */
+/* File: armv5te-vfp/funop.S */
+ /*
+ * Generic 32bit-to-32bit floating point unary operation. Provide an
+ * "instr" line that specifies an instruction that performs "s1 = op s0".
+ *
+ * For: float-to-int, int-to-float
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = src dalvik register address
+ */
+ /* unop vA, vB */
+ flds s0, [r1] @ s0<- vB
+ ftosizs s1, s0 @ s1<- op s0
+ fsts s1, [r0] @ vA<- s1
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_INT_TO_DOUBLE_VFP
+dvmCompiler_TEMPLATE_INT_TO_DOUBLE_VFP:
+/* File: armv5te-vfp/TEMPLATE_INT_TO_DOUBLE_VFP.S */
+/* File: armv5te-vfp/funopWider.S */
+ /*
+ * Generic 32bit-to-64bit floating point unary operation. Provide an
+ * "instr" line that specifies an instruction that performs "d0 = op s0".
+ *
+ * For: int-to-double, float-to-double
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = src dalvik register address
+ */
+ /* unop vA, vB */
+ flds s0, [r1] @ s0<- vB
+ fsitod d0, s0 @ d0<- op s0
+ fstd d0, [r0] @ vA<- d0
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_INT_TO_FLOAT_VFP
+dvmCompiler_TEMPLATE_INT_TO_FLOAT_VFP:
+/* File: armv5te-vfp/TEMPLATE_INT_TO_FLOAT_VFP.S */
+/* File: armv5te-vfp/funop.S */
+ /*
+ * Generic 32bit-to-32bit floating point unary operation. Provide an
+ * "instr" line that specifies an instruction that performs "s1 = op s0".
+ *
+ * For: float-to-int, int-to-float
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = src dalvik register address
+ */
+ /* unop vA, vB */
+ flds s0, [r1] @ s0<- vB
+ fsitos s1, s0 @ s1<- op s0
+ fsts s1, [r0] @ vA<- s1
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_CMPG_DOUBLE_VFP
+dvmCompiler_TEMPLATE_CMPG_DOUBLE_VFP:
+/* File: armv5te-vfp/TEMPLATE_CMPG_DOUBLE_VFP.S */
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * int compare(x, y) {
+ * if (x == y) {
+ * return 0;
+ * } else if (x < y) {
+ * return -1;
+ * } else if (x > y) {
+ * return 1;
+ * } else {
+ * return 1;
+ * }
+ * }
+ *
+ * On entry:
+ * r0 = &op1 [vBB]
+ * r1 = &op2 [vCC]
+ */
+ /* op vAA, vBB, vCC */
+ fldd d0, [r0] @ d0<- vBB
+ fldd d1, [r1] @ d1<- vCC
+ fcmpd d0, d1 @ compare (vBB, vCC)
+ mov r0, #1 @ r0<- 1 (default)
+ fmstat @ export status flags
+ mvnmi r0, #0 @ (less than) r0<- -1
+ moveq r0, #0 @ (equal) r0<- 0
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_CMPL_DOUBLE_VFP
+dvmCompiler_TEMPLATE_CMPL_DOUBLE_VFP:
+/* File: armv5te-vfp/TEMPLATE_CMPL_DOUBLE_VFP.S */
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * int compare(x, y) {
+ * if (x == y) {
+ * return 0;
+ * } else if (x > y) {
+ * return 1;
+ * } else if (x < y) {
+ * return -1;
+ * } else {
+ * return -1;
+ * }
+ * }
+ * On entry:
+ * r0 = &op1 [vBB]
+ * r1 = &op2 [vCC]
+ */
+ /* op vAA, vBB, vCC */
+ fldd d0, [r0] @ d0<- vBB
+ fldd d1, [r1] @ d1<- vCC
+ fcmped d0, d1 @ compare (vBB, vCC)
+ mvn r0, #0 @ r0<- -1 (default)
+ fmstat @ export status flags
+ movgt r0, #1 @ (greater than) r0<- 1
+ moveq r0, #0 @ (equal) r0<- 0
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_CMPG_FLOAT_VFP
+dvmCompiler_TEMPLATE_CMPG_FLOAT_VFP:
+/* File: armv5te-vfp/TEMPLATE_CMPG_FLOAT_VFP.S */
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * int compare(x, y) {
+ * if (x == y) {
+ * return 0;
+ * } else if (x < y) {
+ * return -1;
+ * } else if (x > y) {
+ * return 1;
+ * } else {
+ * return 1;
+ * }
+ * }
+ * On entry:
+ * r0 = &op1 [vBB]
+ * r1 = &op2 [vCC]
+ */
+ /* op vAA, vBB, vCC */
+ flds s0, [r0] @ d0<- vBB
+ flds s1, [r1] @ d1<- vCC
+ fcmps s0, s1 @ compare (vBB, vCC)
+ mov r0, #1 @ r0<- 1 (default)
+ fmstat @ export status flags
+ mvnmi r0, #0 @ (less than) r0<- -1
+ moveq r0, #0 @ (equal) r0<- 0
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_CMPL_FLOAT_VFP
+dvmCompiler_TEMPLATE_CMPL_FLOAT_VFP:
+/* File: armv5te-vfp/TEMPLATE_CMPL_FLOAT_VFP.S */
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * int compare(x, y) {
+ * if (x == y) {
+ * return 0;
+ * } else if (x > y) {
+ * return 1;
+ * } else if (x < y) {
+ * return -1;
+ * } else {
+ * return -1;
+ * }
+ * }
+ * On entry:
+ * r0 = &op1 [vBB]
+ * r1 = &op2 [vCC]
+ */
+ /* op vAA, vBB, vCC */
+ flds s0, [r0] @ d0<- vBB
+ flds s1, [r1] @ d1<- vCC
+ fcmps s0, s1 @ compare (vBB, vCC)
+ mvn r0, #0 @ r0<- -1 (default)
+ fmstat @ export status flags
+ movgt r0, #1 @ (greater than) r0<- 1
+ moveq r0, #0 @ (equal) r0<- 0
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_SQRT_DOUBLE_VFP
+dvmCompiler_TEMPLATE_SQRT_DOUBLE_VFP:
+/* File: armv5te-vfp/TEMPLATE_SQRT_DOUBLE_VFP.S */
+ /*
+ * 64-bit floating point vfp sqrt operation.
+ * If the result is a NaN, bail out to library code to do
+ * the right thing.
+ *
+ * On entry:
+ * r2 src addr of op1
+ * On exit:
+ * r0,r1 = res
+ */
+ fldd d0, [r2]
+ fsqrtd d1, d0
+ fcmpd d1, d1
+ fmstat
+ fmrrd r0, r1, d1
+ bxeq lr @ Result OK - return
+ ldr r2, .Lsqrt
+ fmrrd r0, r1, d0 @ reload orig operand
+ bx r2 @ tail call to sqrt library routine
+
+.Lsqrt:
+ .word sqrt
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_THROW_EXCEPTION_COMMON
+dvmCompiler_TEMPLATE_THROW_EXCEPTION_COMMON:
+/* File: armv5te/TEMPLATE_THROW_EXCEPTION_COMMON.S */
+ /*
+ * Throw an exception from JIT'ed code.
+ * On entry:
+ * r0 Dalvik PC that raises the exception
+ */
+ b .LhandleException
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_MEM_OP_DECODE
+dvmCompiler_TEMPLATE_MEM_OP_DECODE:
+/* File: armv5te-vfp/TEMPLATE_MEM_OP_DECODE.S */
+#if defined(WITH_SELF_VERIFICATION)
+ /*
+ * This handler encapsulates heap memory ops for selfVerification mode.
+ *
+ * The call to the handler is inserted prior to a heap memory operation.
+ * This handler then calls a function to decode the memory op, and process
+ * it accordingly. Afterwards, the handler changes the return address to
+ * skip the memory op so it never gets executed.
+ */
+ vpush {d0-d15} @ save out all fp registers
+ push {r0-r12,lr} @ save out all registers
+ mov r0, lr @ arg0 <- link register
+ mov r1, sp @ arg1 <- stack pointer
+ ldr r2, .LdvmSelfVerificationMemOpDecode @ defined in footer.S
+ blx r2 @ decode and handle the mem op
+ pop {r0-r12,lr} @ restore all registers
+ vpop {d0-d15} @ restore all fp registers
+ bx lr @ return to compiled code
+#endif
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_STRING_COMPARETO
+dvmCompiler_TEMPLATE_STRING_COMPARETO:
+/* File: armv5te/TEMPLATE_STRING_COMPARETO.S */
+ /*
+ * String's compareTo.
+ *
+ * Requires r0/r1 to have been previously checked for null. Will
+ * return negative if this's string is < comp, 0 if they are the
+ * same and positive if >.
+ *
+ * IMPORTANT NOTE:
+ *
+ * This code relies on hard-coded offsets for string objects, and must be
+ * kept in sync with definitions in UtfString.h. See asm-constants.h
+ *
+ * On entry:
+ * r0: this object pointer
+ * r1: comp object pointer
+ *
+ */
+
+ mov r2, r0 @ this to r2, opening up r0 for return value
+ subs r0, r2, r1 @ Same?
+ bxeq lr
+
+ ldr r4, [r2, #STRING_FIELDOFF_OFFSET]
+ ldr r9, [r1, #STRING_FIELDOFF_OFFSET]
+ ldr r7, [r2, #STRING_FIELDOFF_COUNT]
+ ldr r10, [r1, #STRING_FIELDOFF_COUNT]
+ ldr r2, [r2, #STRING_FIELDOFF_VALUE]
+ ldr r1, [r1, #STRING_FIELDOFF_VALUE]
+
+ /*
+ * At this point, we have:
+ * value: r2/r1
+ * offset: r4/r9
+ * count: r7/r10
+ * We're going to compute
+ * r11 <- countDiff
+ * r10 <- minCount
+ */
+ subs r11, r7, r10
+ movls r10, r7
+
+ /* Now, build pointers to the string data */
+ add r2, r2, r4, lsl #1
+ add r1, r1, r9, lsl #1
+ /*
+ * Note: data pointers point to previous element so we can use pre-index
+ * mode with base writeback.
+ */
+ add r2, #16-2 @ offset to contents[-1]
+ add r1, #16-2 @ offset to contents[-1]
+
+ /*
+ * At this point we have:
+ * r2: *this string data
+ * r1: *comp string data
+ * r10: iteration count for comparison
+ * r11: value to return if the first part of the string is equal
+ * r0: reserved for result
+ * r3, r4, r7, r8, r9, r12 available for loading string data
+ */
+
+ subs r10, #2
+ blt do_remainder2
+
+ /*
+ * Unroll the first two checks so we can quickly catch early mismatch
+ * on long strings (but preserve incoming alignment)
+ */
+
+ ldrh r3, [r2, #2]!
+ ldrh r4, [r1, #2]!
+ ldrh r7, [r2, #2]!
+ ldrh r8, [r1, #2]!
+ subs r0, r3, r4
+ subeqs r0, r7, r8
+ bxne lr
+ cmp r10, #28
+ bgt do_memcmp16
+ subs r10, #3
+ blt do_remainder
+
+loopback_triple:
+ ldrh r3, [r2, #2]!
+ ldrh r4, [r1, #2]!
+ ldrh r7, [r2, #2]!
+ ldrh r8, [r1, #2]!
+ ldrh r9, [r2, #2]!
+ ldrh r12,[r1, #2]!
+ subs r0, r3, r4
+ subeqs r0, r7, r8
+ subeqs r0, r9, r12
+ bxne lr
+ subs r10, #3
+ bge loopback_triple
+
+do_remainder:
+ adds r10, #3
+ beq returnDiff
+
+loopback_single:
+ ldrh r3, [r2, #2]!
+ ldrh r4, [r1, #2]!
+ subs r0, r3, r4
+ bxne lr
+ subs r10, #1
+ bne loopback_single
+
+returnDiff:
+ mov r0, r11
+ bx lr
+
+do_remainder2:
+ adds r10, #2
+ bne loopback_single
+ mov r0, r11
+ bx lr
+
+ /* Long string case */
+do_memcmp16:
+ mov r4, lr
+ ldr lr, .Lmemcmp16
+ mov r7, r11
+ add r0, r2, #2
+ add r1, r1, #2
+ mov r2, r10
+ blx lr
+ cmp r0, #0
+ bxne r4
+ mov r0, r7
+ bx r4
+
+.Lmemcmp16:
+ .word __memcmp16
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_STRING_INDEXOF
+dvmCompiler_TEMPLATE_STRING_INDEXOF:
+/* File: armv5te/TEMPLATE_STRING_INDEXOF.S */
+ /*
+ * String's indexOf.
+ *
+ * Requires r0 to have been previously checked for null. Will
+ * return index of match of r1 in r0.
+ *
+ * IMPORTANT NOTE:
+ *
+ * This code relies on hard-coded offsets for string objects, and must be
+ * kept in sync wth definitions in UtfString.h See asm-constants.h
+ *
+ * On entry:
+ * r0: string object pointer
+ * r1: char to match
+ * r2: Starting offset in string data
+ */
+
+ ldr r7, [r0, #STRING_FIELDOFF_OFFSET]
+ ldr r8, [r0, #STRING_FIELDOFF_COUNT]
+ ldr r0, [r0, #STRING_FIELDOFF_VALUE]
+
+ /*
+ * At this point, we have:
+ * r0: object pointer
+ * r1: char to match
+ * r2: starting offset
+ * r7: offset
+ * r8: string length
+ */
+
+ /* Build pointer to start of string data */
+ add r0, #16
+ add r0, r0, r7, lsl #1
+
+ /* Save a copy of starting data in r7 */
+ mov r7, r0
+
+ /* Clamp start to [0..count] */
+ cmp r2, #0
+ movlt r2, #0
+ cmp r2, r8
+ movgt r2, r8
+
+ /* Build pointer to start of data to compare and pre-bias */
+ add r0, r0, r2, lsl #1
+ sub r0, #2
+
+ /* Compute iteration count */
+ sub r8, r2
+
+ /*
+ * At this point we have:
+ * r0: start of data to test
+ * r1: chat to compare
+ * r8: iteration count
+ * r7: original start of string
+ * r3, r4, r9, r10, r11, r12 available for loading string data
+ */
+
+ subs r8, #4
+ blt indexof_remainder
+
+indexof_loop4:
+ ldrh r3, [r0, #2]!
+ ldrh r4, [r0, #2]!
+ ldrh r10, [r0, #2]!
+ ldrh r11, [r0, #2]!
+ cmp r3, r1
+ beq match_0
+ cmp r4, r1
+ beq match_1
+ cmp r10, r1
+ beq match_2
+ cmp r11, r1
+ beq match_3
+ subs r8, #4
+ bge indexof_loop4
+
+indexof_remainder:
+ adds r8, #4
+ beq indexof_nomatch
+
+indexof_loop1:
+ ldrh r3, [r0, #2]!
+ cmp r3, r1
+ beq match_3
+ subs r8, #1
+ bne indexof_loop1
+
+indexof_nomatch:
+ mov r0, #-1
+ bx lr
+
+match_0:
+ sub r0, #6
+ sub r0, r7
+ asr r0, r0, #1
+ bx lr
+match_1:
+ sub r0, #4
+ sub r0, r7
+ asr r0, r0, #1
+ bx lr
+match_2:
+ sub r0, #2
+ sub r0, r7
+ asr r0, r0, #1
+ bx lr
+match_3:
+ sub r0, r7
+ asr r0, r0, #1
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_INTERPRET
+dvmCompiler_TEMPLATE_INTERPRET:
+/* File: armv5te/TEMPLATE_INTERPRET.S */
+ /*
+ * This handler transfers control to the interpeter without performing
+ * any lookups. It may be called either as part of a normal chaining
+ * operation, or from the transition code in header.S. We distinquish
+ * the two cases by looking at the link register. If called from a
+ * translation chain, it will point to the chaining Dalvik PC -3.
+ * On entry:
+ * lr - if NULL:
+ * r1 - the Dalvik PC to begin interpretation.
+ * else
+ * [lr, #3] contains Dalvik PC to begin interpretation
+ * rGLUE - pointer to interpState
+ * rFP - Dalvik frame pointer
+ */
+ cmp lr, #0
+ ldrne r1,[lr, #3]
+ ldr r2, .LinterpPunt
+ mov r0, r1 @ set Dalvik PC
+ bx r2
+ @ doesn't return
+
+.LinterpPunt:
+ .word dvmJitToInterpPunt
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_MONITOR_ENTER
+dvmCompiler_TEMPLATE_MONITOR_ENTER:
+/* File: armv5te/TEMPLATE_MONITOR_ENTER.S */
+ /*
+ * Call out to the runtime to lock an object. Because this thread
+ * may have been suspended in THREAD_MONITOR state and the Jit's
+ * translation cache subsequently cleared, we cannot return directly.
+ * Instead, unconditionally transition to the interpreter to resume.
+ *
+ * On entry:
+ * r0 - self pointer
+ * r1 - the object (which has already been null-checked by the caller
+ * r4 - the Dalvik PC of the following instruction.
+ */
+ ldr r2, .LdvmLockObject
+ mov r3, #0 @ Record that we're not returning
+ str r3, [r0, #offThread_inJitCodeCache]
+ blx r2 @ dvmLockObject(self, obj)
+ @ refresh Jit's on/off status
+ ldr r0, [rGLUE, #offGlue_ppJitProfTable]
+ ldr r0, [r0]
+ ldr r2, .LdvmJitToInterpNoChain
+ str r0, [rGLUE, #offGlue_pJitProfTable]
+ @ Bail to interpreter - no chain [note - r4 still contains rPC]
+#if defined(WITH_JIT_TUNING)
+ mov r0, #kHeavyweightMonitor
+#endif
+ bx r2
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_MONITOR_ENTER_DEBUG
+dvmCompiler_TEMPLATE_MONITOR_ENTER_DEBUG:
+/* File: armv5te/TEMPLATE_MONITOR_ENTER_DEBUG.S */
+ /*
+ * To support deadlock prediction, this version of MONITOR_ENTER
+ * will always call the heavyweight dvmLockObject, check for an
+ * exception and then bail out to the interpreter.
+ *
+ * On entry:
+ * r0 - self pointer
+ * r1 - the object (which has already been null-checked by the caller
+ * r4 - the Dalvik PC of the following instruction.
+ *
+ */
+ ldr r2, .LdvmLockObject
+ mov r3, #0 @ Record that we're not returning
+ str r3, [r0, #offThread_inJitCodeCache]
+ blx r2 @ dvmLockObject(self, obj)
+ @ refresh Jit's on/off status & test for exception
+ ldr r0, [rGLUE, #offGlue_ppJitProfTable]
+ ldr r1, [rGLUE, #offGlue_self]
+ ldr r0, [r0]
+ ldr r1, [r1, #offThread_exception]
+ str r0, [rGLUE, #offGlue_pJitProfTable]
+ cmp r1, #0
+ beq 1f
+ ldr r2, .LhandleException
+ sub r0, r4, #2 @ roll dPC back to this monitor instruction
+ bx r2
+1:
+ @ Bail to interpreter - no chain [note - r4 still contains rPC]
+#if defined(WITH_JIT_TUNING)
+ mov r0, #kHeavyweightMonitor
+#endif
+ ldr pc, .LdvmJitToInterpNoChain
+
+ .size dvmCompilerTemplateStart, .-dvmCompilerTemplateStart
+/* File: armv5te/footer.S */
+/*
+ * ===========================================================================
+ * Common subroutines and data
+ * ===========================================================================
+ */
+
+ .text
+ .align 2
+.LinvokeNative:
+ @ Prep for the native call
+ @ r1 = newFP, r0 = methodToCall
+ ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
+ mov r2, #0
+ ldr r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
+ str r2, [r3, #offThread_inJitCodeCache] @ not in jit code cache
+ str r1, [r3, #offThread_curFrame] @ self->curFrame = newFp
+ str r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+ @ newFp->localRefCookie=top
+ mov r9, r3 @ r9<- glue->self (preserve)
+ SAVEAREA_FROM_FP(r10, r1) @ r10<- new stack save area
+
+ mov r2, r0 @ r2<- methodToCall
+ mov r0, r1 @ r0<- newFP
+ add r1, rGLUE, #offGlue_retval @ r1<- &retval
+
+ LDR_PC_LR "[r2, #offMethod_nativeFunc]"
+
+ @ Refresh Jit's on/off status
+ ldr r3, [rGLUE, #offGlue_ppJitProfTable]
+
+ @ native return; r9=self, r10=newSaveArea
+ @ equivalent to dvmPopJniLocals
+ ldr r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+ ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+ ldr r1, [r9, #offThread_exception] @ check for exception
+ ldr r3, [r3] @ r1 <- pointer to Jit profile table
+ str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
+ cmp r1, #0 @ null?
+ str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
+ ldr r0, [r10, #offStackSaveArea_savedPc] @ reload rPC
+ str r3, [rGLUE, #offGlue_pJitProfTable] @ cache current JitProfTable
+
+ @ r0 = dalvikCallsitePC
+ bne .LhandleException @ no, handle exception
+
+ str r2, [r9, #offThread_inJitCodeCache] @ set the new mode
+ cmp r2, #0 @ return chaining cell still exists?
+ bxne r2 @ yes - go ahead
+
+ @ continue executing the next instruction through the interpreter
+ ldr r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+ add rPC, r0, #6 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+ mov r0, #kCallsiteInterpreted
+#endif
+ mov pc, r1
+
+/*
+ * On entry:
+ * r0 Faulting Dalvik PC
+ */
+.LhandleException:
+#if defined(WITH_SELF_VERIFICATION)
+ ldr pc, .LdeadFood @ should not see this under self-verification mode
+.LdeadFood:
+ .word 0xdeadf00d
+#endif
+ ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
+ mov r2, #0
+ str r2, [r3, #offThread_inJitCodeCache] @ in interpreter land
+ ldr r1, .LdvmMterpCommonExceptionThrown @ PIC way of getting &func
+ ldr rIBASE, .LdvmAsmInstructionStart @ same as above
+ mov rPC, r0 @ reload the faulting Dalvik address
+ mov pc, r1 @ branch to dvmMterpCommonExceptionThrown
+
+ .align 2
+.LdvmAsmInstructionStart:
+ .word dvmAsmInstructionStart
+.LdvmJitToInterpNoChainNoProfile:
+ .word dvmJitToInterpNoChainNoProfile
+.LdvmJitToInterpTraceSelectNoChain:
+ .word dvmJitToInterpTraceSelectNoChain
+.LdvmJitToInterpNoChain:
+ .word dvmJitToInterpNoChain
+.LdvmMterpStdBail:
+ .word dvmMterpStdBail
+.LdvmMterpCommonExceptionThrown:
+ .word dvmMterpCommonExceptionThrown
+.LdvmLockObject:
+ .word dvmLockObject
+#if defined(WITH_JIT_TUNING)
+.LdvmICHitCount:
+ .word gDvmICHitCount
+#endif
+#if defined(WITH_SELF_VERIFICATION)
+.LdvmSelfVerificationMemOpDecode:
+ .word dvmSelfVerificationMemOpDecode
+#endif
+.L__aeabi_cdcmple:
+ .word __aeabi_cdcmple
+.L__aeabi_cfcmple:
+ .word __aeabi_cfcmple
+
+ .global dmvCompilerTemplateEnd
+dmvCompilerTemplateEnd:
+
+#endif /* WITH_JIT */
+
diff --git a/vm/compiler/template/out/CompilerTemplateAsm-armv5te.S b/vm/compiler/template/out/CompilerTemplateAsm-armv5te.S
new file mode 100644
index 0000000..ccdbcca
--- /dev/null
+++ b/vm/compiler/template/out/CompilerTemplateAsm-armv5te.S
@@ -0,0 +1,1267 @@
+/*
+ * This file was generated automatically by gen-template.py for 'armv5te'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: armv5te/header.S */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if defined(WITH_JIT)
+
+/*
+ * ARMv5 definitions and declarations.
+ */
+
+/*
+ARM EABI general notes:
+
+r0-r3 hold first 4 args to a method; they are not preserved across method calls
+r4-r8 are available for general use
+r9 is given special treatment in some situations, but not for us
+r10 (sl) seems to be generally available
+r11 (fp) is used by gcc (unless -fomit-frame-pointer is set)
+r12 (ip) is scratch -- not preserved across method calls
+r13 (sp) should be managed carefully in case a signal arrives
+r14 (lr) must be preserved
+r15 (pc) can be tinkered with directly
+
+r0 holds returns of <= 4 bytes
+r0-r1 hold returns of 8 bytes, low word in r0
+
+Callee must save/restore r4+ (except r12) if it modifies them.
+
+Stack is "full descending". Only the arguments that don't fit in the first 4
+registers are placed on the stack. "sp" points at the first stacked argument
+(i.e. the 5th arg).
+
+VFP: single-precision results in s0, double-precision results in d0.
+
+In the EABI, "sp" must be 64-bit aligned on entry to a function, and any
+64-bit quantities (long long, double) must be 64-bit aligned.
+*/
+
+/*
+JIT and ARM notes:
+
+The following registers have fixed assignments:
+
+ reg nick purpose
+ r5 rFP interpreted frame pointer, used for accessing locals and args
+ r6 rGLUE MterpGlue pointer
+
+The following registers have fixed assignments in mterp but are scratch
+registers in compiled code
+
+ reg nick purpose
+ r4 rPC interpreted program counter, used for fetching instructions
+ r7 rINST first 16-bit code unit of current instruction
+ r8 rIBASE interpreted instruction base pointer, used for computed goto
+
+Macros are provided for common operations. Each macro MUST emit only
+one instruction to make instruction-counting easier. They MUST NOT alter
+unspecified registers or condition codes.
+*/
+
+/* single-purpose registers, given names for clarity */
+#define rPC r4
+#define rFP r5
+#define rGLUE r6
+#define rINST r7
+#define rIBASE r8
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+#define SAVEAREA_FROM_FP(_reg, _fpreg) \
+ sub _reg, _fpreg, #sizeofStackSaveArea
+
+#define EXPORT_PC() \
+ str rPC, [rFP, #(-sizeofStackSaveArea + offStackSaveArea_currentPc)]
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../../../mterp/common/asm-constants.h"
+
+/* File: armv5te/platform.S */
+/*
+ * ===========================================================================
+ * CPU-version-specific defines and utility
+ * ===========================================================================
+ */
+
+/*
+ * Macro for "MOV LR,PC / LDR PC,xxx", which is not allowed pre-ARMv5.
+ * Jump to subroutine.
+ *
+ * May modify IP and LR.
+ */
+.macro LDR_PC_LR source
+ mov lr, pc
+ ldr pc, \source
+.endm
+
+
+ .global dvmCompilerTemplateStart
+ .type dvmCompilerTemplateStart, %function
+ .text
+
+dvmCompilerTemplateStart:
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_CMP_LONG
+dvmCompiler_TEMPLATE_CMP_LONG:
+/* File: armv5te/TEMPLATE_CMP_LONG.S */
+ /*
+ * Compare two 64-bit values. Puts 0, 1, or -1 into the destination
+ * register based on the results of the comparison.
+ *
+ * We load the full values with LDM, but in practice many values could
+ * be resolved by only looking at the high word. This could be made
+ * faster or slower by splitting the LDM into a pair of LDRs.
+ *
+ * If we just wanted to set condition flags, we could do this:
+ * subs ip, r0, r2
+ * sbcs ip, r1, r3
+ * subeqs ip, r0, r2
+ * Leaving { <0, 0, >0 } in ip. However, we have to set it to a specific
+ * integer value, which we can do with 2 conditional mov/mvn instructions
+ * (set 1, set -1; if they're equal we already have 0 in ip), giving
+ * us a constant 5-cycle path plus a branch at the end to the
+ * instruction epilogue code. The multi-compare approach below needs
+ * 2 or 3 cycles + branch if the high word doesn't match, 6 + branch
+ * in the worst case (the 64-bit values are equal).
+ */
+ /* cmp-long vAA, vBB, vCC */
+ cmp r1, r3 @ compare (vBB+1, vCC+1)
+ blt .LTEMPLATE_CMP_LONG_less @ signed compare on high part
+ bgt .LTEMPLATE_CMP_LONG_greater
+ subs r0, r0, r2 @ r0<- r0 - r2
+ bxeq lr
+ bhi .LTEMPLATE_CMP_LONG_greater @ unsigned compare on low part
+.LTEMPLATE_CMP_LONG_less:
+ mvn r0, #0 @ r0<- -1
+ bx lr
+.LTEMPLATE_CMP_LONG_greater:
+ mov r0, #1 @ r0<- 1
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_RETURN
+dvmCompiler_TEMPLATE_RETURN:
+/* File: armv5te/TEMPLATE_RETURN.S */
+ /*
+ * Unwind a frame from the Dalvik stack for compiled OP_RETURN_XXX.
+ * If the stored value in returnAddr
+ * is non-zero, the caller is compiled by the JIT thus return to the
+ * address in the code cache following the invoke instruction. Otherwise
+ * return to the special dvmJitToInterpNoChain entry point.
+ */
+ SAVEAREA_FROM_FP(r0, rFP) @ r0<- saveArea (old)
+ ldr r10, [r0, #offStackSaveArea_prevFrame] @ r10<- saveArea->prevFrame
+ ldr r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+ ldr rPC, [r0, #offStackSaveArea_savedPc] @ rPC<- saveArea->savedPc
+#if !defined(WITH_SELF_VERIFICATION)
+ ldr r9, [r0, #offStackSaveArea_returnAddr] @ r9<- chaining cell ret
+#else
+ mov r9, #0 @ disable chaining
+#endif
+ ldr r2, [r10, #(offStackSaveArea_method - sizeofStackSaveArea)]
+ @ r2<- method we're returning to
+ ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
+ cmp r2, #0 @ break frame?
+#if !defined(WITH_SELF_VERIFICATION)
+ beq 1f @ bail to interpreter
+#else
+ blxeq lr @ punt to interpreter and compare state
+#endif
+ ldr r1, .LdvmJitToInterpNoChainNoProfile @ defined in footer.S
+ mov rFP, r10 @ publish new FP
+ ldrne r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+ ldr r8, [r8] @ r8<- suspendCount
+
+ str r2, [rGLUE, #offGlue_method]@ glue->method = newSave->method
+ ldr r0, [r10, #offClassObject_pDvmDex] @ r0<- method->clazz->pDvmDex
+ str rFP, [r3, #offThread_curFrame] @ self->curFrame = fp
+ add rPC, rPC, #6 @ publish new rPC (advance 6 bytes)
+ str r0, [rGLUE, #offGlue_methodClassDex]
+ cmp r8, #0 @ check the suspendCount
+ movne r9, #0 @ clear the chaining cell address
+ str r9, [r3, #offThread_inJitCodeCache] @ in code cache or not
+ cmp r9, #0 @ chaining cell exists?
+ blxne r9 @ jump to the chaining cell
+#if defined(WITH_JIT_TUNING)
+ mov r0, #kCallsiteInterpreted
+#endif
+ mov pc, r1 @ callsite is interpreted
+1:
+ stmia rGLUE, {rPC, rFP} @ SAVE_PC_FP_TO_GLUE()
+ ldr r2, .LdvmMterpStdBail @ defined in footer.S
+ mov r1, #0 @ changeInterp = false
+ mov r0, rGLUE @ Expecting rGLUE in r0
+ blx r2 @ exit the interpreter
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S */
+ /*
+ * For polymorphic callsites - setup the Dalvik frame and load Dalvik PC
+ * into rPC then jump to dvmJitToInterpNoChain to dispatch the
+ * runtime-resolved callee.
+ */
+ @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+ ldrh r7, [r0, #offMethod_registersSize] @ r7<- methodToCall->regsSize
+ ldrh r2, [r0, #offMethod_outsSize] @ r2<- methodToCall->outsSize
+ ldr r9, [rGLUE, #offGlue_interpStackEnd] @ r9<- interpStackEnd
+ ldr r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+ add r3, r1, #1 @ Thumb addr is odd
+ SAVEAREA_FROM_FP(r1, rFP) @ r1<- stack save area
+ sub r1, r1, r7, lsl #2 @ r1<- newFp (old savearea - regsSize)
+ SAVEAREA_FROM_FP(r10, r1) @ r10<- stack save area
+ sub r10, r10, r2, lsl #2 @ r10<- bottom (newsave - outsSize)
+ ldr r8, [r8] @ r8<- suspendCount (int)
+ cmp r10, r9 @ bottom < interpStackEnd?
+ bxlo lr @ return to raise stack overflow excep.
+ @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+ ldr r9, [r0, #offMethod_clazz] @ r9<- method->clazz
+ ldr r10, [r0, #offMethod_accessFlags] @ r10<- methodToCall->accessFlags
+ str rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+ str rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+ ldr rPC, [r0, #offMethod_insns] @ rPC<- methodToCall->insns
+
+
+ @ set up newSaveArea
+ str rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+ str r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+ str r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+ cmp r8, #0 @ suspendCount != 0
+ bxne lr @ bail to the interpreter
+ tst r10, #ACC_NATIVE
+#if !defined(WITH_SELF_VERIFICATION)
+ bne .LinvokeNative
+#else
+ bxne lr @ bail to the interpreter
+#endif
+
+ ldr r10, .LdvmJitToInterpTraceSelectNoChain
+ ldr r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+ ldr r2, [rGLUE, #offGlue_self] @ r2<- glue->self
+
+ @ Update "glue" values for the new method
+ str r0, [rGLUE, #offGlue_method] @ glue->method = methodToCall
+ str r3, [rGLUE, #offGlue_methodClassDex] @ glue->methodClassDex = ...
+ mov rFP, r1 @ fp = newFp
+ str rFP, [r2, #offThread_curFrame] @ self->curFrame = newFp
+
+ @ Start executing the callee
+#if defined(WITH_JIT_TUNING)
+ mov r0, #kInlineCacheMiss
+#endif
+ mov pc, r10 @ dvmJitToInterpTraceSelectNoChain
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN
+dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S */
+ /*
+ * For monomorphic callsite, setup the Dalvik frame and return to the
+ * Thumb code through the link register to transfer control to the callee
+ * method through a dedicated chaining cell.
+ */
+ @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+ @ methodToCall is guaranteed to be non-native
+.LinvokeChain:
+ ldrh r7, [r0, #offMethod_registersSize] @ r7<- methodToCall->regsSize
+ ldrh r2, [r0, #offMethod_outsSize] @ r2<- methodToCall->outsSize
+ ldr r9, [rGLUE, #offGlue_interpStackEnd] @ r9<- interpStackEnd
+ ldr r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+ add r3, r1, #1 @ Thumb addr is odd
+ SAVEAREA_FROM_FP(r1, rFP) @ r1<- stack save area
+ sub r1, r1, r7, lsl #2 @ r1<- newFp (old savearea - regsSize)
+ SAVEAREA_FROM_FP(r10, r1) @ r10<- stack save area
+ add r12, lr, #2 @ setup the punt-to-interp address
+ sub r10, r10, r2, lsl #2 @ r10<- bottom (newsave - outsSize)
+ ldr r8, [r8] @ r8<- suspendCount (int)
+ cmp r10, r9 @ bottom < interpStackEnd?
+ bxlo r12 @ return to raise stack overflow excep.
+ @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+ ldr r9, [r0, #offMethod_clazz] @ r9<- method->clazz
+ str rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+ str rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+ ldr rPC, [r0, #offMethod_insns] @ rPC<- methodToCall->insns
+
+
+ @ set up newSaveArea
+ str rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+ str r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+ str r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+ cmp r8, #0 @ suspendCount != 0
+ bxne r12 @ bail to the interpreter
+
+ ldr r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+ ldr r2, [rGLUE, #offGlue_self] @ r2<- glue->self
+
+ @ Update "glue" values for the new method
+ str r0, [rGLUE, #offGlue_method] @ glue->method = methodToCall
+ str r3, [rGLUE, #offGlue_methodClassDex] @ glue->methodClassDex = ...
+ mov rFP, r1 @ fp = newFp
+ str rFP, [r2, #offThread_curFrame] @ self->curFrame = newFp
+
+ bx lr @ return to the callee-chaining cell
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN
+dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S */
+ /*
+ * For polymorphic callsite, check whether the cached class pointer matches
+ * the current one. If so setup the Dalvik frame and return to the
+ * Thumb code through the link register to transfer control to the callee
+ * method through a dedicated chaining cell.
+ *
+ * The predicted chaining cell is declared in ArmLIR.h with the
+ * following layout:
+ *
+ * typedef struct PredictedChainingCell {
+ * u4 branch;
+ * const ClassObject *clazz;
+ * const Method *method;
+ * u4 counter;
+ * } PredictedChainingCell;
+ *
+ * Upon returning to the callsite:
+ * - lr : to branch to the chaining cell
+ * - lr+2: to punt to the interpreter
+ * - lr+4: to fully resolve the callee and may rechain.
+ * r3 <- class
+ * r9 <- counter
+ */
+ @ r0 = this, r1 = returnCell, r2 = predictedChainCell, rPC = dalvikCallsite
+ ldr r3, [r0, #offObject_clazz] @ r3 <- this->class
+ ldr r8, [r2, #4] @ r8 <- predictedChainCell->clazz
+ ldr r0, [r2, #8] @ r0 <- predictedChainCell->method
+ ldr r9, [rGLUE, #offGlue_icRechainCount] @ r1 <- shared rechainCount
+ cmp r3, r8 @ predicted class == actual class?
+#if defined(WITH_JIT_TUNING)
+ ldr r7, .LdvmICHitCount
+ ldreq r10, [r7, #0]
+ add r10, r10, #1
+ streq r10, [r7, #0]
+#endif
+ beq .LinvokeChain @ predicted chain is valid
+ ldr r7, [r3, #offClassObject_vtable] @ r7 <- this->class->vtable
+ cmp r8, #0 @ initialized class or not
+ moveq r1, #0
+ subne r1, r9, #1 @ count--
+ strne r1, [rGLUE, #offGlue_icRechainCount] @ write back to InterpState
+ add lr, lr, #4 @ return to fully-resolve landing pad
+ /*
+ * r1 <- count
+ * r2 <- &predictedChainCell
+ * r3 <- this->class
+ * r4 <- dPC
+ * r7 <- this->class->vtable
+ */
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S */
+ @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+ ldrh r7, [r0, #offMethod_registersSize] @ r7<- methodToCall->regsSize
+ ldr r9, [rGLUE, #offGlue_interpStackEnd] @ r9<- interpStackEnd
+ ldr r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+ add r3, r1, #1 @ Thumb addr is odd
+ SAVEAREA_FROM_FP(r1, rFP) @ r1<- stack save area
+ sub r1, r1, r7, lsl #2 @ r1<- newFp (old savearea - regsSize)
+ SAVEAREA_FROM_FP(r10, r1) @ r10<- stack save area
+ ldr r8, [r8] @ r3<- suspendCount (int)
+ cmp r10, r9 @ bottom < interpStackEnd?
+ bxlo lr @ return to raise stack overflow excep.
+ @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+ str rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+ str rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+ ldr rPC, [r0, #offMethod_insns] @ rPC<- methodToCall->insns
+
+
+ @ set up newSaveArea
+ str rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+ str r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+ ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
+ str r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+ cmp r8, #0 @ suspendCount != 0
+ ldr r8, [r0, #offMethod_nativeFunc] @ r8<- method->nativeFunc
+#if !defined(WITH_SELF_VERIFICATION)
+ bxne lr @ bail to the interpreter
+#else
+ bx lr @ bail to interpreter unconditionally
+#endif
+
+ @ go ahead and transfer control to the native code
+ ldr r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
+ mov r2, #0
+ str r1, [r3, #offThread_curFrame] @ self->curFrame = newFp
+ str r2, [r3, #offThread_inJitCodeCache] @ not in the jit code cache
+ str r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+ @ newFp->localRefCookie=top
+ mov r9, r3 @ r9<- glue->self (preserve)
+ SAVEAREA_FROM_FP(r10, r1) @ r10<- new stack save area
+
+ mov r2, r0 @ r2<- methodToCall
+ mov r0, r1 @ r0<- newFP
+ add r1, rGLUE, #offGlue_retval @ r1<- &retval
+
+ blx r8 @ off to the native code
+
+ @ native return; r9=self, r10=newSaveArea
+ @ equivalent to dvmPopJniLocals
+ ldr r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+ ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+ ldr r1, [r9, #offThread_exception] @ check for exception
+ str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
+ cmp r1, #0 @ null?
+ str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
+ ldr r0, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+
+ @ r0 = dalvikCallsitePC
+ bne .LhandleException @ no, handle exception
+
+ str r2, [r9, #offThread_inJitCodeCache] @ set the mode properly
+ cmp r2, #0 @ return chaining cell still exists?
+ bxne r2 @ yes - go ahead
+
+ @ continue executing the next instruction through the interpreter
+ ldr r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+ add rPC, r0, #6 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+ mov r0, #kCallsiteInterpreted
+#endif
+ mov pc, r1
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_CMPG_DOUBLE
+dvmCompiler_TEMPLATE_CMPG_DOUBLE:
+/* File: armv5te/TEMPLATE_CMPG_DOUBLE.S */
+/* File: armv5te/TEMPLATE_CMPL_DOUBLE.S */
+ /*
+ * For the JIT: incoming arguments in r0-r1, r2-r3
+ * result in r0
+ *
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+ * on what value we'd like to return when one of the operands is NaN.
+ *
+ * See OP_CMPL_FLOAT for an explanation.
+ *
+ * For: cmpl-double, cmpg-double
+ */
+ /* op vAA, vBB, vCC */
+ push {r0-r3} @ save operands
+ mov r11, lr @ save return address
+ LDR_PC_LR ".L__aeabi_cdcmple" @ PIC way of "bl __aeabi_cdcmple"
+ bhi .LTEMPLATE_CMPG_DOUBLE_gt_or_nan @ C set and Z clear, disambiguate
+ mvncc r0, #0 @ (less than) r1<- -1
+ moveq r0, #0 @ (equal) r1<- 0, trumps less than
+ add sp, #16 @ drop unused operands
+ bx r11
+
+ @ Test for NaN with a second comparison. EABI forbids testing bit
+ @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+ @ make the library call.
+.LTEMPLATE_CMPG_DOUBLE_gt_or_nan:
+ pop {r2-r3} @ restore operands in reverse order
+ pop {r0-r1} @ restore operands in reverse order
+ LDR_PC_LR ".L__aeabi_cdcmple" @ r0<- Z set if eq, C clear if <
+ movcc r0, #1 @ (greater than) r1<- 1
+ bxcc r11
+ mov r0, #1 @ r1<- 1 or -1 for NaN
+ bx r11
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_CMPL_DOUBLE
+dvmCompiler_TEMPLATE_CMPL_DOUBLE:
+/* File: armv5te/TEMPLATE_CMPL_DOUBLE.S */
+ /*
+ * For the JIT: incoming arguments in r0-r1, r2-r3
+ * result in r0
+ *
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+ * on what value we'd like to return when one of the operands is NaN.
+ *
+ * See OP_CMPL_FLOAT for an explanation.
+ *
+ * For: cmpl-double, cmpg-double
+ */
+ /* op vAA, vBB, vCC */
+ push {r0-r3} @ save operands
+ mov r11, lr @ save return address
+ LDR_PC_LR ".L__aeabi_cdcmple" @ PIC way of "bl __aeabi_cdcmple"
+ bhi .LTEMPLATE_CMPL_DOUBLE_gt_or_nan @ C set and Z clear, disambiguate
+ mvncc r0, #0 @ (less than) r1<- -1
+ moveq r0, #0 @ (equal) r1<- 0, trumps less than
+ add sp, #16 @ drop unused operands
+ bx r11
+
+ @ Test for NaN with a second comparison. EABI forbids testing bit
+ @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+ @ make the library call.
+.LTEMPLATE_CMPL_DOUBLE_gt_or_nan:
+ pop {r2-r3} @ restore operands in reverse order
+ pop {r0-r1} @ restore operands in reverse order
+ LDR_PC_LR ".L__aeabi_cdcmple" @ r0<- Z set if eq, C clear if <
+ movcc r0, #1 @ (greater than) r1<- 1
+ bxcc r11
+ mvn r0, #0 @ r1<- 1 or -1 for NaN
+ bx r11
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_CMPG_FLOAT
+dvmCompiler_TEMPLATE_CMPG_FLOAT:
+/* File: armv5te/TEMPLATE_CMPG_FLOAT.S */
+/* File: armv5te/TEMPLATE_CMPL_FLOAT.S */
+ /*
+ * For the JIT: incoming arguments in r0-r1, r2-r3
+ * result in r0
+ *
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+ * on what value we'd like to return when one of the operands is NaN.
+ *
+ * The operation we're implementing is:
+ * if (x == y)
+ * return 0;
+ * else if (x < y)
+ * return -1;
+ * else if (x > y)
+ * return 1;
+ * else
+ * return {-1,1}; // one or both operands was NaN
+ *
+ * The straightforward implementation requires 3 calls to functions
+ * that return a result in r0. We can do it with two calls if our
+ * EABI library supports __aeabi_cfcmple (only one if we want to check
+ * for NaN directly):
+ * check x <= y
+ * if <, return -1
+ * if ==, return 0
+ * check y <= x
+ * if <, return 1
+ * return {-1,1}
+ *
+ * for: cmpl-float, cmpg-float
+ */
+ /* op vAA, vBB, vCC */
+ mov r9, r0 @ Save copies - we may need to redo
+ mov r10, r1
+ mov r11, lr @ save return address
+ LDR_PC_LR ".L__aeabi_cfcmple" @ cmp <=: C clear if <, Z set if eq
+ bhi .LTEMPLATE_CMPG_FLOAT_gt_or_nan @ C set and Z clear, disambiguate
+ mvncc r0, #0 @ (less than) r0<- -1
+ moveq r0, #0 @ (equal) r0<- 0, trumps less than
+ bx r11
+ @ Test for NaN with a second comparison. EABI forbids testing bit
+ @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+ @ make the library call.
+.LTEMPLATE_CMPG_FLOAT_gt_or_nan:
+ mov r0, r10 @ restore in reverse order
+ mov r1, r9
+ LDR_PC_LR ".L__aeabi_cfcmple" @ r0<- Z set if eq, C clear if <
+ movcc r0, #1 @ (greater than) r1<- 1
+ bxcc r11
+ mov r0, #1 @ r1<- 1 or -1 for NaN
+ bx r11
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_CMPL_FLOAT
+dvmCompiler_TEMPLATE_CMPL_FLOAT:
+/* File: armv5te/TEMPLATE_CMPL_FLOAT.S */
+ /*
+ * For the JIT: incoming arguments in r0-r1, r2-r3
+ * result in r0
+ *
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+ * on what value we'd like to return when one of the operands is NaN.
+ *
+ * The operation we're implementing is:
+ * if (x == y)
+ * return 0;
+ * else if (x < y)
+ * return -1;
+ * else if (x > y)
+ * return 1;
+ * else
+ * return {-1,1}; // one or both operands was NaN
+ *
+ * The straightforward implementation requires 3 calls to functions
+ * that return a result in r0. We can do it with two calls if our
+ * EABI library supports __aeabi_cfcmple (only one if we want to check
+ * for NaN directly):
+ * check x <= y
+ * if <, return -1
+ * if ==, return 0
+ * check y <= x
+ * if <, return 1
+ * return {-1,1}
+ *
+ * for: cmpl-float, cmpg-float
+ */
+ /* op vAA, vBB, vCC */
+ mov r9, r0 @ Save copies - we may need to redo
+ mov r10, r1
+ mov r11, lr @ save return address
+ LDR_PC_LR ".L__aeabi_cfcmple" @ cmp <=: C clear if <, Z set if eq
+ bhi .LTEMPLATE_CMPL_FLOAT_gt_or_nan @ C set and Z clear, disambiguate
+ mvncc r0, #0 @ (less than) r0<- -1
+ moveq r0, #0 @ (equal) r0<- 0, trumps less than
+ bx r11
+ @ Test for NaN with a second comparison. EABI forbids testing bit
+ @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+ @ make the library call.
+.LTEMPLATE_CMPL_FLOAT_gt_or_nan:
+ mov r0, r10 @ restore in reverse order
+ mov r1, r9
+ LDR_PC_LR ".L__aeabi_cfcmple" @ r0<- Z set if eq, C clear if <
+ movcc r0, #1 @ (greater than) r1<- 1
+ bxcc r11
+ mvn r0, #0 @ r1<- 1 or -1 for NaN
+ bx r11
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_MUL_LONG
+dvmCompiler_TEMPLATE_MUL_LONG:
+/* File: armv5te/TEMPLATE_MUL_LONG.S */
+ /*
+ * Signed 64-bit integer multiply.
+ *
+ * For JIT: op1 in r0/r1, op2 in r2/r3, return in r0/r1
+ *
+ * Consider WXxYZ (r1r0 x r3r2) with a long multiply:
+ * WX
+ * x YZ
+ * --------
+ * ZW ZX
+ * YW YX
+ *
+ * The low word of the result holds ZX, the high word holds
+ * (ZW+YX) + (the high overflow from ZX). YW doesn't matter because
+ * it doesn't fit in the low 64 bits.
+ *
+ * Unlike most ARM math operations, multiply instructions have
+ * restrictions on using the same register more than once (Rd and Rm
+ * cannot be the same).
+ */
+ /* mul-long vAA, vBB, vCC */
+ mul ip, r2, r1 @ ip<- ZxW
+ umull r9, r10, r2, r0 @ r9/r10 <- ZxX
+ mla r2, r0, r3, ip @ r2<- YxX + (ZxW)
+ add r10, r2, r10 @ r10<- r10 + low(ZxW + (YxX))
+ mov r0,r9
+ mov r1,r10
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_SHL_LONG
+dvmCompiler_TEMPLATE_SHL_LONG:
+/* File: armv5te/TEMPLATE_SHL_LONG.S */
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to ignore all but the low
+ * 6 bits.
+ */
+ /* shl-long vAA, vBB, vCC */
+ and r2, r2, #63 @ r2<- r2 & 0x3f
+ mov r1, r1, asl r2 @ r1<- r1 << r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32)
+ mov r0, r0, asl r2 @ r0<- r0 << r2
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_SHR_LONG
+dvmCompiler_TEMPLATE_SHR_LONG:
+/* File: armv5te/TEMPLATE_SHR_LONG.S */
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to ignore all but the low
+ * 6 bits.
+ */
+ /* shr-long vAA, vBB, vCC */
+ and r2, r2, #63 @ r0<- r0 & 0x3f
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32)
+ mov r1, r1, asr r2 @ r1<- r1 >> r2
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_USHR_LONG
+dvmCompiler_TEMPLATE_USHR_LONG:
+/* File: armv5te/TEMPLATE_USHR_LONG.S */
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to ignore all but the low
+ * 6 bits.
+ */
+ /* ushr-long vAA, vBB, vCC */
+ and r2, r2, #63 @ r0<- r0 & 0x3f
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32)
+ mov r1, r1, lsr r2 @ r1<- r1 >>> r2
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_THROW_EXCEPTION_COMMON
+dvmCompiler_TEMPLATE_THROW_EXCEPTION_COMMON:
+/* File: armv5te/TEMPLATE_THROW_EXCEPTION_COMMON.S */
+ /*
+ * Throw an exception from JIT'ed code.
+ * On entry:
+ * r0 Dalvik PC that raises the exception
+ */
+ b .LhandleException
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_MEM_OP_DECODE
+dvmCompiler_TEMPLATE_MEM_OP_DECODE:
+/* File: armv5te/TEMPLATE_MEM_OP_DECODE.S */
+#if defined(WITH_SELF_VERIFICATION)
+ /*
+ * This handler encapsulates heap memory ops for selfVerification mode.
+ *
+ * The call to the handler is inserted prior to a heap memory operation.
+ * This handler then calls a function to decode the memory op, and process
+ * it accordingly. Afterwards, the handler changes the return address to
+ * skip the memory op so it never gets executed.
+ */
+ push {r0-r12,lr} @ save out all registers
+ mov r0, lr @ arg0 <- link register
+ mov r1, sp @ arg1 <- stack pointer
+ ldr r2, .LdvmSelfVerificationMemOpDecode @ defined in footer.S
+ blx r2 @ decode and handle the mem op
+ pop {r0-r12,lr} @ restore all registers
+ bx lr @ return to compiled code
+#endif
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_STRING_COMPARETO
+dvmCompiler_TEMPLATE_STRING_COMPARETO:
+/* File: armv5te/TEMPLATE_STRING_COMPARETO.S */
+ /*
+ * String's compareTo.
+ *
+ * Requires r0/r1 to have been previously checked for null. Will
+ * return negative if this's string is < comp, 0 if they are the
+ * same and positive if >.
+ *
+ * IMPORTANT NOTE:
+ *
+ * This code relies on hard-coded offsets for string objects, and must be
+ * kept in sync with definitions in UtfString.h. See asm-constants.h
+ *
+ * On entry:
+ * r0: this object pointer
+ * r1: comp object pointer
+ *
+ */
+
+ mov r2, r0 @ this to r2, opening up r0 for return value
+ subs r0, r2, r1 @ Same?
+ bxeq lr
+
+ ldr r4, [r2, #STRING_FIELDOFF_OFFSET]
+ ldr r9, [r1, #STRING_FIELDOFF_OFFSET]
+ ldr r7, [r2, #STRING_FIELDOFF_COUNT]
+ ldr r10, [r1, #STRING_FIELDOFF_COUNT]
+ ldr r2, [r2, #STRING_FIELDOFF_VALUE]
+ ldr r1, [r1, #STRING_FIELDOFF_VALUE]
+
+ /*
+ * At this point, we have:
+ * value: r2/r1
+ * offset: r4/r9
+ * count: r7/r10
+ * We're going to compute
+ * r11 <- countDiff
+ * r10 <- minCount
+ */
+ subs r11, r7, r10
+ movls r10, r7
+
+ /* Now, build pointers to the string data */
+ add r2, r2, r4, lsl #1
+ add r1, r1, r9, lsl #1
+ /*
+ * Note: data pointers point to previous element so we can use pre-index
+ * mode with base writeback.
+ */
+ add r2, #16-2 @ offset to contents[-1]
+ add r1, #16-2 @ offset to contents[-1]
+
+ /*
+ * At this point we have:
+ * r2: *this string data
+ * r1: *comp string data
+ * r10: iteration count for comparison
+ * r11: value to return if the first part of the string is equal
+ * r0: reserved for result
+ * r3, r4, r7, r8, r9, r12 available for loading string data
+ */
+
+ subs r10, #2
+ blt do_remainder2
+
+ /*
+ * Unroll the first two checks so we can quickly catch early mismatch
+ * on long strings (but preserve incoming alignment)
+ */
+
+ ldrh r3, [r2, #2]!
+ ldrh r4, [r1, #2]!
+ ldrh r7, [r2, #2]!
+ ldrh r8, [r1, #2]!
+ subs r0, r3, r4
+ subeqs r0, r7, r8
+ bxne lr
+ cmp r10, #28
+ bgt do_memcmp16
+ subs r10, #3
+ blt do_remainder
+
+loopback_triple:
+ ldrh r3, [r2, #2]!
+ ldrh r4, [r1, #2]!
+ ldrh r7, [r2, #2]!
+ ldrh r8, [r1, #2]!
+ ldrh r9, [r2, #2]!
+ ldrh r12,[r1, #2]!
+ subs r0, r3, r4
+ subeqs r0, r7, r8
+ subeqs r0, r9, r12
+ bxne lr
+ subs r10, #3
+ bge loopback_triple
+
+do_remainder:
+ adds r10, #3
+ beq returnDiff
+
+loopback_single:
+ ldrh r3, [r2, #2]!
+ ldrh r4, [r1, #2]!
+ subs r0, r3, r4
+ bxne lr
+ subs r10, #1
+ bne loopback_single
+
+returnDiff:
+ mov r0, r11
+ bx lr
+
+do_remainder2:
+ adds r10, #2
+ bne loopback_single
+ mov r0, r11
+ bx lr
+
+ /* Long string case */
+do_memcmp16:
+ mov r4, lr
+ ldr lr, .Lmemcmp16
+ mov r7, r11
+ add r0, r2, #2
+ add r1, r1, #2
+ mov r2, r10
+ blx lr
+ cmp r0, #0
+ bxne r4
+ mov r0, r7
+ bx r4
+
+.Lmemcmp16:
+ .word __memcmp16
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_STRING_INDEXOF
+dvmCompiler_TEMPLATE_STRING_INDEXOF:
+/* File: armv5te/TEMPLATE_STRING_INDEXOF.S */
+ /*
+ * String's indexOf.
+ *
+ * Requires r0 to have been previously checked for null. Will
+ * return index of match of r1 in r0.
+ *
+ * IMPORTANT NOTE:
+ *
+ * This code relies on hard-coded offsets for string objects, and must be
+ * kept in sync wth definitions in UtfString.h See asm-constants.h
+ *
+ * On entry:
+ * r0: string object pointer
+ * r1: char to match
+ * r2: Starting offset in string data
+ */
+
+ ldr r7, [r0, #STRING_FIELDOFF_OFFSET]
+ ldr r8, [r0, #STRING_FIELDOFF_COUNT]
+ ldr r0, [r0, #STRING_FIELDOFF_VALUE]
+
+ /*
+ * At this point, we have:
+ * r0: object pointer
+ * r1: char to match
+ * r2: starting offset
+ * r7: offset
+ * r8: string length
+ */
+
+ /* Build pointer to start of string data */
+ add r0, #16
+ add r0, r0, r7, lsl #1
+
+ /* Save a copy of starting data in r7 */
+ mov r7, r0
+
+ /* Clamp start to [0..count] */
+ cmp r2, #0
+ movlt r2, #0
+ cmp r2, r8
+ movgt r2, r8
+
+ /* Build pointer to start of data to compare and pre-bias */
+ add r0, r0, r2, lsl #1
+ sub r0, #2
+
+ /* Compute iteration count */
+ sub r8, r2
+
+ /*
+ * At this point we have:
+ * r0: start of data to test
+ * r1: chat to compare
+ * r8: iteration count
+ * r7: original start of string
+ * r3, r4, r9, r10, r11, r12 available for loading string data
+ */
+
+ subs r8, #4
+ blt indexof_remainder
+
+indexof_loop4:
+ ldrh r3, [r0, #2]!
+ ldrh r4, [r0, #2]!
+ ldrh r10, [r0, #2]!
+ ldrh r11, [r0, #2]!
+ cmp r3, r1
+ beq match_0
+ cmp r4, r1
+ beq match_1
+ cmp r10, r1
+ beq match_2
+ cmp r11, r1
+ beq match_3
+ subs r8, #4
+ bge indexof_loop4
+
+indexof_remainder:
+ adds r8, #4
+ beq indexof_nomatch
+
+indexof_loop1:
+ ldrh r3, [r0, #2]!
+ cmp r3, r1
+ beq match_3
+ subs r8, #1
+ bne indexof_loop1
+
+indexof_nomatch:
+ mov r0, #-1
+ bx lr
+
+match_0:
+ sub r0, #6
+ sub r0, r7
+ asr r0, r0, #1
+ bx lr
+match_1:
+ sub r0, #4
+ sub r0, r7
+ asr r0, r0, #1
+ bx lr
+match_2:
+ sub r0, #2
+ sub r0, r7
+ asr r0, r0, #1
+ bx lr
+match_3:
+ sub r0, r7
+ asr r0, r0, #1
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_INTERPRET
+dvmCompiler_TEMPLATE_INTERPRET:
+/* File: armv5te/TEMPLATE_INTERPRET.S */
+ /*
+ * This handler transfers control to the interpeter without performing
+ * any lookups. It may be called either as part of a normal chaining
+ * operation, or from the transition code in header.S. We distinquish
+ * the two cases by looking at the link register. If called from a
+ * translation chain, it will point to the chaining Dalvik PC -3.
+ * On entry:
+ * lr - if NULL:
+ * r1 - the Dalvik PC to begin interpretation.
+ * else
+ * [lr, #3] contains Dalvik PC to begin interpretation
+ * rGLUE - pointer to interpState
+ * rFP - Dalvik frame pointer
+ */
+ cmp lr, #0
+ ldrne r1,[lr, #3]
+ ldr r2, .LinterpPunt
+ mov r0, r1 @ set Dalvik PC
+ bx r2
+ @ doesn't return
+
+.LinterpPunt:
+ .word dvmJitToInterpPunt
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_MONITOR_ENTER
+dvmCompiler_TEMPLATE_MONITOR_ENTER:
+/* File: armv5te/TEMPLATE_MONITOR_ENTER.S */
+ /*
+ * Call out to the runtime to lock an object. Because this thread
+ * may have been suspended in THREAD_MONITOR state and the Jit's
+ * translation cache subsequently cleared, we cannot return directly.
+ * Instead, unconditionally transition to the interpreter to resume.
+ *
+ * On entry:
+ * r0 - self pointer
+ * r1 - the object (which has already been null-checked by the caller
+ * r4 - the Dalvik PC of the following instruction.
+ */
+ ldr r2, .LdvmLockObject
+ mov r3, #0 @ Record that we're not returning
+ str r3, [r0, #offThread_inJitCodeCache]
+ blx r2 @ dvmLockObject(self, obj)
+ @ refresh Jit's on/off status
+ ldr r0, [rGLUE, #offGlue_ppJitProfTable]
+ ldr r0, [r0]
+ ldr r2, .LdvmJitToInterpNoChain
+ str r0, [rGLUE, #offGlue_pJitProfTable]
+ @ Bail to interpreter - no chain [note - r4 still contains rPC]
+#if defined(WITH_JIT_TUNING)
+ mov r0, #kHeavyweightMonitor
+#endif
+ bx r2
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_MONITOR_ENTER_DEBUG
+dvmCompiler_TEMPLATE_MONITOR_ENTER_DEBUG:
+/* File: armv5te/TEMPLATE_MONITOR_ENTER_DEBUG.S */
+ /*
+ * To support deadlock prediction, this version of MONITOR_ENTER
+ * will always call the heavyweight dvmLockObject, check for an
+ * exception and then bail out to the interpreter.
+ *
+ * On entry:
+ * r0 - self pointer
+ * r1 - the object (which has already been null-checked by the caller
+ * r4 - the Dalvik PC of the following instruction.
+ *
+ */
+ ldr r2, .LdvmLockObject
+ mov r3, #0 @ Record that we're not returning
+ str r3, [r0, #offThread_inJitCodeCache]
+ blx r2 @ dvmLockObject(self, obj)
+ @ refresh Jit's on/off status & test for exception
+ ldr r0, [rGLUE, #offGlue_ppJitProfTable]
+ ldr r1, [rGLUE, #offGlue_self]
+ ldr r0, [r0]
+ ldr r1, [r1, #offThread_exception]
+ str r0, [rGLUE, #offGlue_pJitProfTable]
+ cmp r1, #0
+ beq 1f
+ ldr r2, .LhandleException
+ sub r0, r4, #2 @ roll dPC back to this monitor instruction
+ bx r2
+1:
+ @ Bail to interpreter - no chain [note - r4 still contains rPC]
+#if defined(WITH_JIT_TUNING)
+ mov r0, #kHeavyweightMonitor
+#endif
+ ldr pc, .LdvmJitToInterpNoChain
+
+ .size dvmCompilerTemplateStart, .-dvmCompilerTemplateStart
+/* File: armv5te/footer.S */
+/*
+ * ===========================================================================
+ * Common subroutines and data
+ * ===========================================================================
+ */
+
+ .text
+ .align 2
+.LinvokeNative:
+ @ Prep for the native call
+ @ r1 = newFP, r0 = methodToCall
+ ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
+ mov r2, #0
+ ldr r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
+ str r2, [r3, #offThread_inJitCodeCache] @ not in jit code cache
+ str r1, [r3, #offThread_curFrame] @ self->curFrame = newFp
+ str r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+ @ newFp->localRefCookie=top
+ mov r9, r3 @ r9<- glue->self (preserve)
+ SAVEAREA_FROM_FP(r10, r1) @ r10<- new stack save area
+
+ mov r2, r0 @ r2<- methodToCall
+ mov r0, r1 @ r0<- newFP
+ add r1, rGLUE, #offGlue_retval @ r1<- &retval
+
+ LDR_PC_LR "[r2, #offMethod_nativeFunc]"
+
+ @ Refresh Jit's on/off status
+ ldr r3, [rGLUE, #offGlue_ppJitProfTable]
+
+ @ native return; r9=self, r10=newSaveArea
+ @ equivalent to dvmPopJniLocals
+ ldr r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+ ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+ ldr r1, [r9, #offThread_exception] @ check for exception
+ ldr r3, [r3] @ r1 <- pointer to Jit profile table
+ str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
+ cmp r1, #0 @ null?
+ str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
+ ldr r0, [r10, #offStackSaveArea_savedPc] @ reload rPC
+ str r3, [rGLUE, #offGlue_pJitProfTable] @ cache current JitProfTable
+
+ @ r0 = dalvikCallsitePC
+ bne .LhandleException @ no, handle exception
+
+ str r2, [r9, #offThread_inJitCodeCache] @ set the new mode
+ cmp r2, #0 @ return chaining cell still exists?
+ bxne r2 @ yes - go ahead
+
+ @ continue executing the next instruction through the interpreter
+ ldr r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+ add rPC, r0, #6 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+ mov r0, #kCallsiteInterpreted
+#endif
+ mov pc, r1
+
+/*
+ * On entry:
+ * r0 Faulting Dalvik PC
+ */
+.LhandleException:
+#if defined(WITH_SELF_VERIFICATION)
+ ldr pc, .LdeadFood @ should not see this under self-verification mode
+.LdeadFood:
+ .word 0xdeadf00d
+#endif
+ ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
+ mov r2, #0
+ str r2, [r3, #offThread_inJitCodeCache] @ in interpreter land
+ ldr r1, .LdvmMterpCommonExceptionThrown @ PIC way of getting &func
+ ldr rIBASE, .LdvmAsmInstructionStart @ same as above
+ mov rPC, r0 @ reload the faulting Dalvik address
+ mov pc, r1 @ branch to dvmMterpCommonExceptionThrown
+
+ .align 2
+.LdvmAsmInstructionStart:
+ .word dvmAsmInstructionStart
+.LdvmJitToInterpNoChainNoProfile:
+ .word dvmJitToInterpNoChainNoProfile
+.LdvmJitToInterpTraceSelectNoChain:
+ .word dvmJitToInterpTraceSelectNoChain
+.LdvmJitToInterpNoChain:
+ .word dvmJitToInterpNoChain
+.LdvmMterpStdBail:
+ .word dvmMterpStdBail
+.LdvmMterpCommonExceptionThrown:
+ .word dvmMterpCommonExceptionThrown
+.LdvmLockObject:
+ .word dvmLockObject
+#if defined(WITH_JIT_TUNING)
+.LdvmICHitCount:
+ .word gDvmICHitCount
+#endif
+#if defined(WITH_SELF_VERIFICATION)
+.LdvmSelfVerificationMemOpDecode:
+ .word dvmSelfVerificationMemOpDecode
+#endif
+.L__aeabi_cdcmple:
+ .word __aeabi_cdcmple
+.L__aeabi_cfcmple:
+ .word __aeabi_cfcmple
+
+ .global dmvCompilerTemplateEnd
+dmvCompilerTemplateEnd:
+
+#endif /* WITH_JIT */
+
diff --git a/vm/compiler/template/out/CompilerTemplateAsm-armv7-a-neon.S b/vm/compiler/template/out/CompilerTemplateAsm-armv7-a-neon.S
new file mode 100644
index 0000000..e520056
--- /dev/null
+++ b/vm/compiler/template/out/CompilerTemplateAsm-armv7-a-neon.S
@@ -0,0 +1,1544 @@
+/*
+ * This file was generated automatically by gen-template.py for 'armv7-a-neon'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: armv5te/header.S */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if defined(WITH_JIT)
+
+/*
+ * ARMv5 definitions and declarations.
+ */
+
+/*
+ARM EABI general notes:
+
+r0-r3 hold first 4 args to a method; they are not preserved across method calls
+r4-r8 are available for general use
+r9 is given special treatment in some situations, but not for us
+r10 (sl) seems to be generally available
+r11 (fp) is used by gcc (unless -fomit-frame-pointer is set)
+r12 (ip) is scratch -- not preserved across method calls
+r13 (sp) should be managed carefully in case a signal arrives
+r14 (lr) must be preserved
+r15 (pc) can be tinkered with directly
+
+r0 holds returns of <= 4 bytes
+r0-r1 hold returns of 8 bytes, low word in r0
+
+Callee must save/restore r4+ (except r12) if it modifies them.
+
+Stack is "full descending". Only the arguments that don't fit in the first 4
+registers are placed on the stack. "sp" points at the first stacked argument
+(i.e. the 5th arg).
+
+VFP: single-precision results in s0, double-precision results in d0.
+
+In the EABI, "sp" must be 64-bit aligned on entry to a function, and any
+64-bit quantities (long long, double) must be 64-bit aligned.
+*/
+
+/*
+JIT and ARM notes:
+
+The following registers have fixed assignments:
+
+ reg nick purpose
+ r5 rFP interpreted frame pointer, used for accessing locals and args
+ r6 rGLUE MterpGlue pointer
+
+The following registers have fixed assignments in mterp but are scratch
+registers in compiled code
+
+ reg nick purpose
+ r4 rPC interpreted program counter, used for fetching instructions
+ r7 rINST first 16-bit code unit of current instruction
+ r8 rIBASE interpreted instruction base pointer, used for computed goto
+
+Macros are provided for common operations. Each macro MUST emit only
+one instruction to make instruction-counting easier. They MUST NOT alter
+unspecified registers or condition codes.
+*/
+
+/* single-purpose registers, given names for clarity */
+#define rPC r4
+#define rFP r5
+#define rGLUE r6
+#define rINST r7
+#define rIBASE r8
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+#define SAVEAREA_FROM_FP(_reg, _fpreg) \
+ sub _reg, _fpreg, #sizeofStackSaveArea
+
+#define EXPORT_PC() \
+ str rPC, [rFP, #(-sizeofStackSaveArea + offStackSaveArea_currentPc)]
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../../../mterp/common/asm-constants.h"
+
+/* File: armv5te-vfp/platform.S */
+/*
+ * ===========================================================================
+ * CPU-version-specific defines and utility
+ * ===========================================================================
+ */
+
+/*
+ * Macro for "MOV LR,PC / LDR PC,xxx", which is not allowed pre-ARMv5.
+ * Jump to subroutine.
+ *
+ * May modify IP and LR.
+ */
+.macro LDR_PC_LR source
+ mov lr, pc
+ ldr pc, \source
+.endm
+
+
+ .global dvmCompilerTemplateStart
+ .type dvmCompilerTemplateStart, %function
+ .text
+
+dvmCompilerTemplateStart:
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_CMP_LONG
+dvmCompiler_TEMPLATE_CMP_LONG:
+/* File: armv5te/TEMPLATE_CMP_LONG.S */
+ /*
+ * Compare two 64-bit values. Puts 0, 1, or -1 into the destination
+ * register based on the results of the comparison.
+ *
+ * We load the full values with LDM, but in practice many values could
+ * be resolved by only looking at the high word. This could be made
+ * faster or slower by splitting the LDM into a pair of LDRs.
+ *
+ * If we just wanted to set condition flags, we could do this:
+ * subs ip, r0, r2
+ * sbcs ip, r1, r3
+ * subeqs ip, r0, r2
+ * Leaving { <0, 0, >0 } in ip. However, we have to set it to a specific
+ * integer value, which we can do with 2 conditional mov/mvn instructions
+ * (set 1, set -1; if they're equal we already have 0 in ip), giving
+ * us a constant 5-cycle path plus a branch at the end to the
+ * instruction epilogue code. The multi-compare approach below needs
+ * 2 or 3 cycles + branch if the high word doesn't match, 6 + branch
+ * in the worst case (the 64-bit values are equal).
+ */
+ /* cmp-long vAA, vBB, vCC */
+ cmp r1, r3 @ compare (vBB+1, vCC+1)
+ blt .LTEMPLATE_CMP_LONG_less @ signed compare on high part
+ bgt .LTEMPLATE_CMP_LONG_greater
+ subs r0, r0, r2 @ r0<- r0 - r2
+ bxeq lr
+ bhi .LTEMPLATE_CMP_LONG_greater @ unsigned compare on low part
+.LTEMPLATE_CMP_LONG_less:
+ mvn r0, #0 @ r0<- -1
+ bx lr
+.LTEMPLATE_CMP_LONG_greater:
+ mov r0, #1 @ r0<- 1
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_RETURN
+dvmCompiler_TEMPLATE_RETURN:
+/* File: armv5te/TEMPLATE_RETURN.S */
+ /*
+ * Unwind a frame from the Dalvik stack for compiled OP_RETURN_XXX.
+ * If the stored value in returnAddr
+ * is non-zero, the caller is compiled by the JIT thus return to the
+ * address in the code cache following the invoke instruction. Otherwise
+ * return to the special dvmJitToInterpNoChain entry point.
+ */
+ SAVEAREA_FROM_FP(r0, rFP) @ r0<- saveArea (old)
+ ldr r10, [r0, #offStackSaveArea_prevFrame] @ r10<- saveArea->prevFrame
+ ldr r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+ ldr rPC, [r0, #offStackSaveArea_savedPc] @ rPC<- saveArea->savedPc
+#if !defined(WITH_SELF_VERIFICATION)
+ ldr r9, [r0, #offStackSaveArea_returnAddr] @ r9<- chaining cell ret
+#else
+ mov r9, #0 @ disable chaining
+#endif
+ ldr r2, [r10, #(offStackSaveArea_method - sizeofStackSaveArea)]
+ @ r2<- method we're returning to
+ ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
+ cmp r2, #0 @ break frame?
+#if !defined(WITH_SELF_VERIFICATION)
+ beq 1f @ bail to interpreter
+#else
+ blxeq lr @ punt to interpreter and compare state
+#endif
+ ldr r1, .LdvmJitToInterpNoChainNoProfile @ defined in footer.S
+ mov rFP, r10 @ publish new FP
+ ldrne r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+ ldr r8, [r8] @ r8<- suspendCount
+
+ str r2, [rGLUE, #offGlue_method]@ glue->method = newSave->method
+ ldr r0, [r10, #offClassObject_pDvmDex] @ r0<- method->clazz->pDvmDex
+ str rFP, [r3, #offThread_curFrame] @ self->curFrame = fp
+ add rPC, rPC, #6 @ publish new rPC (advance 6 bytes)
+ str r0, [rGLUE, #offGlue_methodClassDex]
+ cmp r8, #0 @ check the suspendCount
+ movne r9, #0 @ clear the chaining cell address
+ str r9, [r3, #offThread_inJitCodeCache] @ in code cache or not
+ cmp r9, #0 @ chaining cell exists?
+ blxne r9 @ jump to the chaining cell
+#if defined(WITH_JIT_TUNING)
+ mov r0, #kCallsiteInterpreted
+#endif
+ mov pc, r1 @ callsite is interpreted
+1:
+ stmia rGLUE, {rPC, rFP} @ SAVE_PC_FP_TO_GLUE()
+ ldr r2, .LdvmMterpStdBail @ defined in footer.S
+ mov r1, #0 @ changeInterp = false
+ mov r0, rGLUE @ Expecting rGLUE in r0
+ blx r2 @ exit the interpreter
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S */
+ /*
+ * For polymorphic callsites - setup the Dalvik frame and load Dalvik PC
+ * into rPC then jump to dvmJitToInterpNoChain to dispatch the
+ * runtime-resolved callee.
+ */
+ @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+ ldrh r7, [r0, #offMethod_registersSize] @ r7<- methodToCall->regsSize
+ ldrh r2, [r0, #offMethod_outsSize] @ r2<- methodToCall->outsSize
+ ldr r9, [rGLUE, #offGlue_interpStackEnd] @ r9<- interpStackEnd
+ ldr r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+ add r3, r1, #1 @ Thumb addr is odd
+ SAVEAREA_FROM_FP(r1, rFP) @ r1<- stack save area
+ sub r1, r1, r7, lsl #2 @ r1<- newFp (old savearea - regsSize)
+ SAVEAREA_FROM_FP(r10, r1) @ r10<- stack save area
+ sub r10, r10, r2, lsl #2 @ r10<- bottom (newsave - outsSize)
+ ldr r8, [r8] @ r8<- suspendCount (int)
+ cmp r10, r9 @ bottom < interpStackEnd?
+ bxlo lr @ return to raise stack overflow excep.
+ @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+ ldr r9, [r0, #offMethod_clazz] @ r9<- method->clazz
+ ldr r10, [r0, #offMethod_accessFlags] @ r10<- methodToCall->accessFlags
+ str rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+ str rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+ ldr rPC, [r0, #offMethod_insns] @ rPC<- methodToCall->insns
+
+
+ @ set up newSaveArea
+ str rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+ str r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+ str r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+ cmp r8, #0 @ suspendCount != 0
+ bxne lr @ bail to the interpreter
+ tst r10, #ACC_NATIVE
+#if !defined(WITH_SELF_VERIFICATION)
+ bne .LinvokeNative
+#else
+ bxne lr @ bail to the interpreter
+#endif
+
+ ldr r10, .LdvmJitToInterpTraceSelectNoChain
+ ldr r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+ ldr r2, [rGLUE, #offGlue_self] @ r2<- glue->self
+
+ @ Update "glue" values for the new method
+ str r0, [rGLUE, #offGlue_method] @ glue->method = methodToCall
+ str r3, [rGLUE, #offGlue_methodClassDex] @ glue->methodClassDex = ...
+ mov rFP, r1 @ fp = newFp
+ str rFP, [r2, #offThread_curFrame] @ self->curFrame = newFp
+
+ @ Start executing the callee
+#if defined(WITH_JIT_TUNING)
+ mov r0, #kInlineCacheMiss
+#endif
+ mov pc, r10 @ dvmJitToInterpTraceSelectNoChain
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN
+dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S */
+ /*
+ * For monomorphic callsite, setup the Dalvik frame and return to the
+ * Thumb code through the link register to transfer control to the callee
+ * method through a dedicated chaining cell.
+ */
+ @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+ @ methodToCall is guaranteed to be non-native
+.LinvokeChain:
+ ldrh r7, [r0, #offMethod_registersSize] @ r7<- methodToCall->regsSize
+ ldrh r2, [r0, #offMethod_outsSize] @ r2<- methodToCall->outsSize
+ ldr r9, [rGLUE, #offGlue_interpStackEnd] @ r9<- interpStackEnd
+ ldr r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+ add r3, r1, #1 @ Thumb addr is odd
+ SAVEAREA_FROM_FP(r1, rFP) @ r1<- stack save area
+ sub r1, r1, r7, lsl #2 @ r1<- newFp (old savearea - regsSize)
+ SAVEAREA_FROM_FP(r10, r1) @ r10<- stack save area
+ add r12, lr, #2 @ setup the punt-to-interp address
+ sub r10, r10, r2, lsl #2 @ r10<- bottom (newsave - outsSize)
+ ldr r8, [r8] @ r8<- suspendCount (int)
+ cmp r10, r9 @ bottom < interpStackEnd?
+ bxlo r12 @ return to raise stack overflow excep.
+ @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+ ldr r9, [r0, #offMethod_clazz] @ r9<- method->clazz
+ str rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+ str rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+ ldr rPC, [r0, #offMethod_insns] @ rPC<- methodToCall->insns
+
+
+ @ set up newSaveArea
+ str rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+ str r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+ str r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+ cmp r8, #0 @ suspendCount != 0
+ bxne r12 @ bail to the interpreter
+
+ ldr r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+ ldr r2, [rGLUE, #offGlue_self] @ r2<- glue->self
+
+ @ Update "glue" values for the new method
+ str r0, [rGLUE, #offGlue_method] @ glue->method = methodToCall
+ str r3, [rGLUE, #offGlue_methodClassDex] @ glue->methodClassDex = ...
+ mov rFP, r1 @ fp = newFp
+ str rFP, [r2, #offThread_curFrame] @ self->curFrame = newFp
+
+ bx lr @ return to the callee-chaining cell
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN
+dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S */
+ /*
+ * For polymorphic callsite, check whether the cached class pointer matches
+ * the current one. If so setup the Dalvik frame and return to the
+ * Thumb code through the link register to transfer control to the callee
+ * method through a dedicated chaining cell.
+ *
+ * The predicted chaining cell is declared in ArmLIR.h with the
+ * following layout:
+ *
+ * typedef struct PredictedChainingCell {
+ * u4 branch;
+ * const ClassObject *clazz;
+ * const Method *method;
+ * u4 counter;
+ * } PredictedChainingCell;
+ *
+ * Upon returning to the callsite:
+ * - lr : to branch to the chaining cell
+ * - lr+2: to punt to the interpreter
+ * - lr+4: to fully resolve the callee and may rechain.
+ * r3 <- class
+ * r9 <- counter
+ */
+ @ r0 = this, r1 = returnCell, r2 = predictedChainCell, rPC = dalvikCallsite
+ ldr r3, [r0, #offObject_clazz] @ r3 <- this->class
+ ldr r8, [r2, #4] @ r8 <- predictedChainCell->clazz
+ ldr r0, [r2, #8] @ r0 <- predictedChainCell->method
+ ldr r9, [rGLUE, #offGlue_icRechainCount] @ r1 <- shared rechainCount
+ cmp r3, r8 @ predicted class == actual class?
+#if defined(WITH_JIT_TUNING)
+ ldr r7, .LdvmICHitCount
+ ldreq r10, [r7, #0]
+ add r10, r10, #1
+ streq r10, [r7, #0]
+#endif
+ beq .LinvokeChain @ predicted chain is valid
+ ldr r7, [r3, #offClassObject_vtable] @ r7 <- this->class->vtable
+ cmp r8, #0 @ initialized class or not
+ moveq r1, #0
+ subne r1, r9, #1 @ count--
+ strne r1, [rGLUE, #offGlue_icRechainCount] @ write back to InterpState
+ add lr, lr, #4 @ return to fully-resolve landing pad
+ /*
+ * r1 <- count
+ * r2 <- &predictedChainCell
+ * r3 <- this->class
+ * r4 <- dPC
+ * r7 <- this->class->vtable
+ */
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S */
+ @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+ ldrh r7, [r0, #offMethod_registersSize] @ r7<- methodToCall->regsSize
+ ldr r9, [rGLUE, #offGlue_interpStackEnd] @ r9<- interpStackEnd
+ ldr r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+ add r3, r1, #1 @ Thumb addr is odd
+ SAVEAREA_FROM_FP(r1, rFP) @ r1<- stack save area
+ sub r1, r1, r7, lsl #2 @ r1<- newFp (old savearea - regsSize)
+ SAVEAREA_FROM_FP(r10, r1) @ r10<- stack save area
+ ldr r8, [r8] @ r3<- suspendCount (int)
+ cmp r10, r9 @ bottom < interpStackEnd?
+ bxlo lr @ return to raise stack overflow excep.
+ @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+ str rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+ str rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+ ldr rPC, [r0, #offMethod_insns] @ rPC<- methodToCall->insns
+
+
+ @ set up newSaveArea
+ str rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+ str r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+ ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
+ str r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+ cmp r8, #0 @ suspendCount != 0
+ ldr r8, [r0, #offMethod_nativeFunc] @ r8<- method->nativeFunc
+#if !defined(WITH_SELF_VERIFICATION)
+ bxne lr @ bail to the interpreter
+#else
+ bx lr @ bail to interpreter unconditionally
+#endif
+
+ @ go ahead and transfer control to the native code
+ ldr r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
+ mov r2, #0
+ str r1, [r3, #offThread_curFrame] @ self->curFrame = newFp
+ str r2, [r3, #offThread_inJitCodeCache] @ not in the jit code cache
+ str r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+ @ newFp->localRefCookie=top
+ mov r9, r3 @ r9<- glue->self (preserve)
+ SAVEAREA_FROM_FP(r10, r1) @ r10<- new stack save area
+
+ mov r2, r0 @ r2<- methodToCall
+ mov r0, r1 @ r0<- newFP
+ add r1, rGLUE, #offGlue_retval @ r1<- &retval
+
+ blx r8 @ off to the native code
+
+ @ native return; r9=self, r10=newSaveArea
+ @ equivalent to dvmPopJniLocals
+ ldr r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+ ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+ ldr r1, [r9, #offThread_exception] @ check for exception
+ str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
+ cmp r1, #0 @ null?
+ str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
+ ldr r0, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+
+ @ r0 = dalvikCallsitePC
+ bne .LhandleException @ no, handle exception
+
+ str r2, [r9, #offThread_inJitCodeCache] @ set the mode properly
+ cmp r2, #0 @ return chaining cell still exists?
+ bxne r2 @ yes - go ahead
+
+ @ continue executing the next instruction through the interpreter
+ ldr r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+ add rPC, r0, #6 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+ mov r0, #kCallsiteInterpreted
+#endif
+ mov pc, r1
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_MUL_LONG
+dvmCompiler_TEMPLATE_MUL_LONG:
+/* File: armv5te/TEMPLATE_MUL_LONG.S */
+ /*
+ * Signed 64-bit integer multiply.
+ *
+ * For JIT: op1 in r0/r1, op2 in r2/r3, return in r0/r1
+ *
+ * Consider WXxYZ (r1r0 x r3r2) with a long multiply:
+ * WX
+ * x YZ
+ * --------
+ * ZW ZX
+ * YW YX
+ *
+ * The low word of the result holds ZX, the high word holds
+ * (ZW+YX) + (the high overflow from ZX). YW doesn't matter because
+ * it doesn't fit in the low 64 bits.
+ *
+ * Unlike most ARM math operations, multiply instructions have
+ * restrictions on using the same register more than once (Rd and Rm
+ * cannot be the same).
+ */
+ /* mul-long vAA, vBB, vCC */
+ mul ip, r2, r1 @ ip<- ZxW
+ umull r9, r10, r2, r0 @ r9/r10 <- ZxX
+ mla r2, r0, r3, ip @ r2<- YxX + (ZxW)
+ add r10, r2, r10 @ r10<- r10 + low(ZxW + (YxX))
+ mov r0,r9
+ mov r1,r10
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_SHL_LONG
+dvmCompiler_TEMPLATE_SHL_LONG:
+/* File: armv5te/TEMPLATE_SHL_LONG.S */
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to ignore all but the low
+ * 6 bits.
+ */
+ /* shl-long vAA, vBB, vCC */
+ and r2, r2, #63 @ r2<- r2 & 0x3f
+ mov r1, r1, asl r2 @ r1<- r1 << r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32)
+ mov r0, r0, asl r2 @ r0<- r0 << r2
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_SHR_LONG
+dvmCompiler_TEMPLATE_SHR_LONG:
+/* File: armv5te/TEMPLATE_SHR_LONG.S */
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to ignore all but the low
+ * 6 bits.
+ */
+ /* shr-long vAA, vBB, vCC */
+ and r2, r2, #63 @ r0<- r0 & 0x3f
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32)
+ mov r1, r1, asr r2 @ r1<- r1 >> r2
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_USHR_LONG
+dvmCompiler_TEMPLATE_USHR_LONG:
+/* File: armv5te/TEMPLATE_USHR_LONG.S */
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to ignore all but the low
+ * 6 bits.
+ */
+ /* ushr-long vAA, vBB, vCC */
+ and r2, r2, #63 @ r0<- r0 & 0x3f
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32)
+ mov r1, r1, lsr r2 @ r1<- r1 >>> r2
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_ADD_FLOAT_VFP
+dvmCompiler_TEMPLATE_ADD_FLOAT_VFP:
+/* File: armv5te-vfp/TEMPLATE_ADD_FLOAT_VFP.S */
+/* File: armv5te-vfp/fbinop.S */
+ /*
+ * Generic 32-bit floating point operation. Provide an "instr" line that
+ * specifies an instruction that performs s2 = s0 op s1.
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = op1 address
+ * r2 = op2 address
+ */
+ flds s0,[r1]
+ flds s1,[r2]
+ fadds s2, s0, s1
+ fsts s2,[r0]
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_SUB_FLOAT_VFP
+dvmCompiler_TEMPLATE_SUB_FLOAT_VFP:
+/* File: armv5te-vfp/TEMPLATE_SUB_FLOAT_VFP.S */
+/* File: armv5te-vfp/fbinop.S */
+ /*
+ * Generic 32-bit floating point operation. Provide an "instr" line that
+ * specifies an instruction that performs s2 = s0 op s1.
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = op1 address
+ * r2 = op2 address
+ */
+ flds s0,[r1]
+ flds s1,[r2]
+ fsubs s2, s0, s1
+ fsts s2,[r0]
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_MUL_FLOAT_VFP
+dvmCompiler_TEMPLATE_MUL_FLOAT_VFP:
+/* File: armv5te-vfp/TEMPLATE_MUL_FLOAT_VFP.S */
+/* File: armv5te-vfp/fbinop.S */
+ /*
+ * Generic 32-bit floating point operation. Provide an "instr" line that
+ * specifies an instruction that performs s2 = s0 op s1.
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = op1 address
+ * r2 = op2 address
+ */
+ flds s0,[r1]
+ flds s1,[r2]
+ fmuls s2, s0, s1
+ fsts s2,[r0]
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_DIV_FLOAT_VFP
+dvmCompiler_TEMPLATE_DIV_FLOAT_VFP:
+/* File: armv5te-vfp/TEMPLATE_DIV_FLOAT_VFP.S */
+/* File: armv5te-vfp/fbinop.S */
+ /*
+ * Generic 32-bit floating point operation. Provide an "instr" line that
+ * specifies an instruction that performs s2 = s0 op s1.
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = op1 address
+ * r2 = op2 address
+ */
+ flds s0,[r1]
+ flds s1,[r2]
+ fdivs s2, s0, s1
+ fsts s2,[r0]
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_ADD_DOUBLE_VFP
+dvmCompiler_TEMPLATE_ADD_DOUBLE_VFP:
+/* File: armv5te-vfp/TEMPLATE_ADD_DOUBLE_VFP.S */
+/* File: armv5te-vfp/fbinopWide.S */
+ /*
+ * Generic 64-bit floating point operation. Provide an "instr" line that
+ * specifies an instruction that performs s2 = s0 op s1.
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = op1 address
+ * r2 = op2 address
+ */
+ fldd d0,[r1]
+ fldd d1,[r2]
+ faddd d2, d0, d1
+ fstd d2,[r0]
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_SUB_DOUBLE_VFP
+dvmCompiler_TEMPLATE_SUB_DOUBLE_VFP:
+/* File: armv5te-vfp/TEMPLATE_SUB_DOUBLE_VFP.S */
+/* File: armv5te-vfp/fbinopWide.S */
+ /*
+ * Generic 64-bit floating point operation. Provide an "instr" line that
+ * specifies an instruction that performs s2 = s0 op s1.
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = op1 address
+ * r2 = op2 address
+ */
+ fldd d0,[r1]
+ fldd d1,[r2]
+ fsubd d2, d0, d1
+ fstd d2,[r0]
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_MUL_DOUBLE_VFP
+dvmCompiler_TEMPLATE_MUL_DOUBLE_VFP:
+/* File: armv5te-vfp/TEMPLATE_MUL_DOUBLE_VFP.S */
+/* File: armv5te-vfp/fbinopWide.S */
+ /*
+ * Generic 64-bit floating point operation. Provide an "instr" line that
+ * specifies an instruction that performs s2 = s0 op s1.
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = op1 address
+ * r2 = op2 address
+ */
+ fldd d0,[r1]
+ fldd d1,[r2]
+ fmuld d2, d0, d1
+ fstd d2,[r0]
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_DIV_DOUBLE_VFP
+dvmCompiler_TEMPLATE_DIV_DOUBLE_VFP:
+/* File: armv5te-vfp/TEMPLATE_DIV_DOUBLE_VFP.S */
+/* File: armv5te-vfp/fbinopWide.S */
+ /*
+ * Generic 64-bit floating point operation. Provide an "instr" line that
+ * specifies an instruction that performs s2 = s0 op s1.
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = op1 address
+ * r2 = op2 address
+ */
+ fldd d0,[r1]
+ fldd d1,[r2]
+ fdivd d2, d0, d1
+ fstd d2,[r0]
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_DOUBLE_TO_FLOAT_VFP
+dvmCompiler_TEMPLATE_DOUBLE_TO_FLOAT_VFP:
+/* File: armv5te-vfp/TEMPLATE_DOUBLE_TO_FLOAT_VFP.S */
+/* File: armv5te-vfp/funopNarrower.S */
+ /*
+ * Generic 64bit-to-32bit floating point unary operation. Provide an
+ * "instr" line that specifies an instruction that performs "s0 = op d0".
+ *
+ * For: double-to-int, double-to-float
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = src dalvik register address
+ */
+ /* unop vA, vB */
+ fldd d0, [r1] @ d0<- vB
+ fcvtsd s0, d0 @ s0<- op d0
+ fsts s0, [r0] @ vA<- s0
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_DOUBLE_TO_INT_VFP
+dvmCompiler_TEMPLATE_DOUBLE_TO_INT_VFP:
+/* File: armv5te-vfp/TEMPLATE_DOUBLE_TO_INT_VFP.S */
+/* File: armv5te-vfp/funopNarrower.S */
+ /*
+ * Generic 64bit-to-32bit floating point unary operation. Provide an
+ * "instr" line that specifies an instruction that performs "s0 = op d0".
+ *
+ * For: double-to-int, double-to-float
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = src dalvik register address
+ */
+ /* unop vA, vB */
+ fldd d0, [r1] @ d0<- vB
+ ftosizd s0, d0 @ s0<- op d0
+ fsts s0, [r0] @ vA<- s0
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_FLOAT_TO_DOUBLE_VFP
+dvmCompiler_TEMPLATE_FLOAT_TO_DOUBLE_VFP:
+/* File: armv5te-vfp/TEMPLATE_FLOAT_TO_DOUBLE_VFP.S */
+/* File: armv5te-vfp/funopWider.S */
+ /*
+ * Generic 32bit-to-64bit floating point unary operation. Provide an
+ * "instr" line that specifies an instruction that performs "d0 = op s0".
+ *
+ * For: int-to-double, float-to-double
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = src dalvik register address
+ */
+ /* unop vA, vB */
+ flds s0, [r1] @ s0<- vB
+ fcvtds d0, s0 @ d0<- op s0
+ fstd d0, [r0] @ vA<- d0
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_FLOAT_TO_INT_VFP
+dvmCompiler_TEMPLATE_FLOAT_TO_INT_VFP:
+/* File: armv5te-vfp/TEMPLATE_FLOAT_TO_INT_VFP.S */
+/* File: armv5te-vfp/funop.S */
+ /*
+ * Generic 32bit-to-32bit floating point unary operation. Provide an
+ * "instr" line that specifies an instruction that performs "s1 = op s0".
+ *
+ * For: float-to-int, int-to-float
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = src dalvik register address
+ */
+ /* unop vA, vB */
+ flds s0, [r1] @ s0<- vB
+ ftosizs s1, s0 @ s1<- op s0
+ fsts s1, [r0] @ vA<- s1
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_INT_TO_DOUBLE_VFP
+dvmCompiler_TEMPLATE_INT_TO_DOUBLE_VFP:
+/* File: armv5te-vfp/TEMPLATE_INT_TO_DOUBLE_VFP.S */
+/* File: armv5te-vfp/funopWider.S */
+ /*
+ * Generic 32bit-to-64bit floating point unary operation. Provide an
+ * "instr" line that specifies an instruction that performs "d0 = op s0".
+ *
+ * For: int-to-double, float-to-double
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = src dalvik register address
+ */
+ /* unop vA, vB */
+ flds s0, [r1] @ s0<- vB
+ fsitod d0, s0 @ d0<- op s0
+ fstd d0, [r0] @ vA<- d0
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_INT_TO_FLOAT_VFP
+dvmCompiler_TEMPLATE_INT_TO_FLOAT_VFP:
+/* File: armv5te-vfp/TEMPLATE_INT_TO_FLOAT_VFP.S */
+/* File: armv5te-vfp/funop.S */
+ /*
+ * Generic 32bit-to-32bit floating point unary operation. Provide an
+ * "instr" line that specifies an instruction that performs "s1 = op s0".
+ *
+ * For: float-to-int, int-to-float
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = src dalvik register address
+ */
+ /* unop vA, vB */
+ flds s0, [r1] @ s0<- vB
+ fsitos s1, s0 @ s1<- op s0
+ fsts s1, [r0] @ vA<- s1
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_CMPG_DOUBLE_VFP
+dvmCompiler_TEMPLATE_CMPG_DOUBLE_VFP:
+/* File: armv5te-vfp/TEMPLATE_CMPG_DOUBLE_VFP.S */
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * int compare(x, y) {
+ * if (x == y) {
+ * return 0;
+ * } else if (x < y) {
+ * return -1;
+ * } else if (x > y) {
+ * return 1;
+ * } else {
+ * return 1;
+ * }
+ * }
+ *
+ * On entry:
+ * r0 = &op1 [vBB]
+ * r1 = &op2 [vCC]
+ */
+ /* op vAA, vBB, vCC */
+ fldd d0, [r0] @ d0<- vBB
+ fldd d1, [r1] @ d1<- vCC
+ fcmpd d0, d1 @ compare (vBB, vCC)
+ mov r0, #1 @ r0<- 1 (default)
+ fmstat @ export status flags
+ mvnmi r0, #0 @ (less than) r0<- -1
+ moveq r0, #0 @ (equal) r0<- 0
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_CMPL_DOUBLE_VFP
+dvmCompiler_TEMPLATE_CMPL_DOUBLE_VFP:
+/* File: armv5te-vfp/TEMPLATE_CMPL_DOUBLE_VFP.S */
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * int compare(x, y) {
+ * if (x == y) {
+ * return 0;
+ * } else if (x > y) {
+ * return 1;
+ * } else if (x < y) {
+ * return -1;
+ * } else {
+ * return -1;
+ * }
+ * }
+ * On entry:
+ * r0 = &op1 [vBB]
+ * r1 = &op2 [vCC]
+ */
+ /* op vAA, vBB, vCC */
+ fldd d0, [r0] @ d0<- vBB
+ fldd d1, [r1] @ d1<- vCC
+ fcmped d0, d1 @ compare (vBB, vCC)
+ mvn r0, #0 @ r0<- -1 (default)
+ fmstat @ export status flags
+ movgt r0, #1 @ (greater than) r0<- 1
+ moveq r0, #0 @ (equal) r0<- 0
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_CMPG_FLOAT_VFP
+dvmCompiler_TEMPLATE_CMPG_FLOAT_VFP:
+/* File: armv5te-vfp/TEMPLATE_CMPG_FLOAT_VFP.S */
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * int compare(x, y) {
+ * if (x == y) {
+ * return 0;
+ * } else if (x < y) {
+ * return -1;
+ * } else if (x > y) {
+ * return 1;
+ * } else {
+ * return 1;
+ * }
+ * }
+ * On entry:
+ * r0 = &op1 [vBB]
+ * r1 = &op2 [vCC]
+ */
+ /* op vAA, vBB, vCC */
+ flds s0, [r0] @ d0<- vBB
+ flds s1, [r1] @ d1<- vCC
+ fcmps s0, s1 @ compare (vBB, vCC)
+ mov r0, #1 @ r0<- 1 (default)
+ fmstat @ export status flags
+ mvnmi r0, #0 @ (less than) r0<- -1
+ moveq r0, #0 @ (equal) r0<- 0
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_CMPL_FLOAT_VFP
+dvmCompiler_TEMPLATE_CMPL_FLOAT_VFP:
+/* File: armv5te-vfp/TEMPLATE_CMPL_FLOAT_VFP.S */
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * int compare(x, y) {
+ * if (x == y) {
+ * return 0;
+ * } else if (x > y) {
+ * return 1;
+ * } else if (x < y) {
+ * return -1;
+ * } else {
+ * return -1;
+ * }
+ * }
+ * On entry:
+ * r0 = &op1 [vBB]
+ * r1 = &op2 [vCC]
+ */
+ /* op vAA, vBB, vCC */
+ flds s0, [r0] @ d0<- vBB
+ flds s1, [r1] @ d1<- vCC
+ fcmps s0, s1 @ compare (vBB, vCC)
+ mvn r0, #0 @ r0<- -1 (default)
+ fmstat @ export status flags
+ movgt r0, #1 @ (greater than) r0<- 1
+ moveq r0, #0 @ (equal) r0<- 0
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_SQRT_DOUBLE_VFP
+dvmCompiler_TEMPLATE_SQRT_DOUBLE_VFP:
+/* File: armv5te-vfp/TEMPLATE_SQRT_DOUBLE_VFP.S */
+ /*
+ * 64-bit floating point vfp sqrt operation.
+ * If the result is a NaN, bail out to library code to do
+ * the right thing.
+ *
+ * On entry:
+ * r2 src addr of op1
+ * On exit:
+ * r0,r1 = res
+ */
+ fldd d0, [r2]
+ fsqrtd d1, d0
+ fcmpd d1, d1
+ fmstat
+ fmrrd r0, r1, d1
+ bxeq lr @ Result OK - return
+ ldr r2, .Lsqrt
+ fmrrd r0, r1, d0 @ reload orig operand
+ bx r2 @ tail call to sqrt library routine
+
+.Lsqrt:
+ .word sqrt
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_THROW_EXCEPTION_COMMON
+dvmCompiler_TEMPLATE_THROW_EXCEPTION_COMMON:
+/* File: armv5te/TEMPLATE_THROW_EXCEPTION_COMMON.S */
+ /*
+ * Throw an exception from JIT'ed code.
+ * On entry:
+ * r0 Dalvik PC that raises the exception
+ */
+ b .LhandleException
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_MEM_OP_DECODE
+dvmCompiler_TEMPLATE_MEM_OP_DECODE:
+/* File: armv5te-vfp/TEMPLATE_MEM_OP_DECODE.S */
+#if defined(WITH_SELF_VERIFICATION)
+ /*
+ * This handler encapsulates heap memory ops for selfVerification mode.
+ *
+ * The call to the handler is inserted prior to a heap memory operation.
+ * This handler then calls a function to decode the memory op, and process
+ * it accordingly. Afterwards, the handler changes the return address to
+ * skip the memory op so it never gets executed.
+ */
+ vpush {d0-d15} @ save out all fp registers
+ push {r0-r12,lr} @ save out all registers
+ mov r0, lr @ arg0 <- link register
+ mov r1, sp @ arg1 <- stack pointer
+ ldr r2, .LdvmSelfVerificationMemOpDecode @ defined in footer.S
+ blx r2 @ decode and handle the mem op
+ pop {r0-r12,lr} @ restore all registers
+ vpop {d0-d15} @ restore all fp registers
+ bx lr @ return to compiled code
+#endif
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_STRING_COMPARETO
+dvmCompiler_TEMPLATE_STRING_COMPARETO:
+/* File: armv5te/TEMPLATE_STRING_COMPARETO.S */
+ /*
+ * String's compareTo.
+ *
+ * Requires r0/r1 to have been previously checked for null. Will
+ * return negative if this's string is < comp, 0 if they are the
+ * same and positive if >.
+ *
+ * IMPORTANT NOTE:
+ *
+ * This code relies on hard-coded offsets for string objects, and must be
+ * kept in sync with definitions in UtfString.h. See asm-constants.h
+ *
+ * On entry:
+ * r0: this object pointer
+ * r1: comp object pointer
+ *
+ */
+
+ mov r2, r0 @ this to r2, opening up r0 for return value
+ subs r0, r2, r1 @ Same?
+ bxeq lr
+
+ ldr r4, [r2, #STRING_FIELDOFF_OFFSET]
+ ldr r9, [r1, #STRING_FIELDOFF_OFFSET]
+ ldr r7, [r2, #STRING_FIELDOFF_COUNT]
+ ldr r10, [r1, #STRING_FIELDOFF_COUNT]
+ ldr r2, [r2, #STRING_FIELDOFF_VALUE]
+ ldr r1, [r1, #STRING_FIELDOFF_VALUE]
+
+ /*
+ * At this point, we have:
+ * value: r2/r1
+ * offset: r4/r9
+ * count: r7/r10
+ * We're going to compute
+ * r11 <- countDiff
+ * r10 <- minCount
+ */
+ subs r11, r7, r10
+ movls r10, r7
+
+ /* Now, build pointers to the string data */
+ add r2, r2, r4, lsl #1
+ add r1, r1, r9, lsl #1
+ /*
+ * Note: data pointers point to previous element so we can use pre-index
+ * mode with base writeback.
+ */
+ add r2, #16-2 @ offset to contents[-1]
+ add r1, #16-2 @ offset to contents[-1]
+
+ /*
+ * At this point we have:
+ * r2: *this string data
+ * r1: *comp string data
+ * r10: iteration count for comparison
+ * r11: value to return if the first part of the string is equal
+ * r0: reserved for result
+ * r3, r4, r7, r8, r9, r12 available for loading string data
+ */
+
+ subs r10, #2
+ blt do_remainder2
+
+ /*
+ * Unroll the first two checks so we can quickly catch early mismatch
+ * on long strings (but preserve incoming alignment)
+ */
+
+ ldrh r3, [r2, #2]!
+ ldrh r4, [r1, #2]!
+ ldrh r7, [r2, #2]!
+ ldrh r8, [r1, #2]!
+ subs r0, r3, r4
+ subeqs r0, r7, r8
+ bxne lr
+ cmp r10, #28
+ bgt do_memcmp16
+ subs r10, #3
+ blt do_remainder
+
+loopback_triple:
+ ldrh r3, [r2, #2]!
+ ldrh r4, [r1, #2]!
+ ldrh r7, [r2, #2]!
+ ldrh r8, [r1, #2]!
+ ldrh r9, [r2, #2]!
+ ldrh r12,[r1, #2]!
+ subs r0, r3, r4
+ subeqs r0, r7, r8
+ subeqs r0, r9, r12
+ bxne lr
+ subs r10, #3
+ bge loopback_triple
+
+do_remainder:
+ adds r10, #3
+ beq returnDiff
+
+loopback_single:
+ ldrh r3, [r2, #2]!
+ ldrh r4, [r1, #2]!
+ subs r0, r3, r4
+ bxne lr
+ subs r10, #1
+ bne loopback_single
+
+returnDiff:
+ mov r0, r11
+ bx lr
+
+do_remainder2:
+ adds r10, #2
+ bne loopback_single
+ mov r0, r11
+ bx lr
+
+ /* Long string case */
+do_memcmp16:
+ mov r4, lr
+ ldr lr, .Lmemcmp16
+ mov r7, r11
+ add r0, r2, #2
+ add r1, r1, #2
+ mov r2, r10
+ blx lr
+ cmp r0, #0
+ bxne r4
+ mov r0, r7
+ bx r4
+
+.Lmemcmp16:
+ .word __memcmp16
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_STRING_INDEXOF
+dvmCompiler_TEMPLATE_STRING_INDEXOF:
+/* File: armv5te/TEMPLATE_STRING_INDEXOF.S */
+ /*
+ * String's indexOf.
+ *
+ * Requires r0 to have been previously checked for null. Will
+ * return index of match of r1 in r0.
+ *
+ * IMPORTANT NOTE:
+ *
+ * This code relies on hard-coded offsets for string objects, and must be
+ * kept in sync wth definitions in UtfString.h See asm-constants.h
+ *
+ * On entry:
+ * r0: string object pointer
+ * r1: char to match
+ * r2: Starting offset in string data
+ */
+
+ ldr r7, [r0, #STRING_FIELDOFF_OFFSET]
+ ldr r8, [r0, #STRING_FIELDOFF_COUNT]
+ ldr r0, [r0, #STRING_FIELDOFF_VALUE]
+
+ /*
+ * At this point, we have:
+ * r0: object pointer
+ * r1: char to match
+ * r2: starting offset
+ * r7: offset
+ * r8: string length
+ */
+
+ /* Build pointer to start of string data */
+ add r0, #16
+ add r0, r0, r7, lsl #1
+
+ /* Save a copy of starting data in r7 */
+ mov r7, r0
+
+ /* Clamp start to [0..count] */
+ cmp r2, #0
+ movlt r2, #0
+ cmp r2, r8
+ movgt r2, r8
+
+ /* Build pointer to start of data to compare and pre-bias */
+ add r0, r0, r2, lsl #1
+ sub r0, #2
+
+ /* Compute iteration count */
+ sub r8, r2
+
+ /*
+ * At this point we have:
+ * r0: start of data to test
+ * r1: chat to compare
+ * r8: iteration count
+ * r7: original start of string
+ * r3, r4, r9, r10, r11, r12 available for loading string data
+ */
+
+ subs r8, #4
+ blt indexof_remainder
+
+indexof_loop4:
+ ldrh r3, [r0, #2]!
+ ldrh r4, [r0, #2]!
+ ldrh r10, [r0, #2]!
+ ldrh r11, [r0, #2]!
+ cmp r3, r1
+ beq match_0
+ cmp r4, r1
+ beq match_1
+ cmp r10, r1
+ beq match_2
+ cmp r11, r1
+ beq match_3
+ subs r8, #4
+ bge indexof_loop4
+
+indexof_remainder:
+ adds r8, #4
+ beq indexof_nomatch
+
+indexof_loop1:
+ ldrh r3, [r0, #2]!
+ cmp r3, r1
+ beq match_3
+ subs r8, #1
+ bne indexof_loop1
+
+indexof_nomatch:
+ mov r0, #-1
+ bx lr
+
+match_0:
+ sub r0, #6
+ sub r0, r7
+ asr r0, r0, #1
+ bx lr
+match_1:
+ sub r0, #4
+ sub r0, r7
+ asr r0, r0, #1
+ bx lr
+match_2:
+ sub r0, #2
+ sub r0, r7
+ asr r0, r0, #1
+ bx lr
+match_3:
+ sub r0, r7
+ asr r0, r0, #1
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_INTERPRET
+dvmCompiler_TEMPLATE_INTERPRET:
+/* File: armv5te/TEMPLATE_INTERPRET.S */
+ /*
+ * This handler transfers control to the interpeter without performing
+ * any lookups. It may be called either as part of a normal chaining
+ * operation, or from the transition code in header.S. We distinquish
+ * the two cases by looking at the link register. If called from a
+ * translation chain, it will point to the chaining Dalvik PC -3.
+ * On entry:
+ * lr - if NULL:
+ * r1 - the Dalvik PC to begin interpretation.
+ * else
+ * [lr, #3] contains Dalvik PC to begin interpretation
+ * rGLUE - pointer to interpState
+ * rFP - Dalvik frame pointer
+ */
+ cmp lr, #0
+ ldrne r1,[lr, #3]
+ ldr r2, .LinterpPunt
+ mov r0, r1 @ set Dalvik PC
+ bx r2
+ @ doesn't return
+
+.LinterpPunt:
+ .word dvmJitToInterpPunt
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_MONITOR_ENTER
+dvmCompiler_TEMPLATE_MONITOR_ENTER:
+/* File: armv5te/TEMPLATE_MONITOR_ENTER.S */
+ /*
+ * Call out to the runtime to lock an object. Because this thread
+ * may have been suspended in THREAD_MONITOR state and the Jit's
+ * translation cache subsequently cleared, we cannot return directly.
+ * Instead, unconditionally transition to the interpreter to resume.
+ *
+ * On entry:
+ * r0 - self pointer
+ * r1 - the object (which has already been null-checked by the caller
+ * r4 - the Dalvik PC of the following instruction.
+ */
+ ldr r2, .LdvmLockObject
+ mov r3, #0 @ Record that we're not returning
+ str r3, [r0, #offThread_inJitCodeCache]
+ blx r2 @ dvmLockObject(self, obj)
+ @ refresh Jit's on/off status
+ ldr r0, [rGLUE, #offGlue_ppJitProfTable]
+ ldr r0, [r0]
+ ldr r2, .LdvmJitToInterpNoChain
+ str r0, [rGLUE, #offGlue_pJitProfTable]
+ @ Bail to interpreter - no chain [note - r4 still contains rPC]
+#if defined(WITH_JIT_TUNING)
+ mov r0, #kHeavyweightMonitor
+#endif
+ bx r2
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_MONITOR_ENTER_DEBUG
+dvmCompiler_TEMPLATE_MONITOR_ENTER_DEBUG:
+/* File: armv5te/TEMPLATE_MONITOR_ENTER_DEBUG.S */
+ /*
+ * To support deadlock prediction, this version of MONITOR_ENTER
+ * will always call the heavyweight dvmLockObject, check for an
+ * exception and then bail out to the interpreter.
+ *
+ * On entry:
+ * r0 - self pointer
+ * r1 - the object (which has already been null-checked by the caller
+ * r4 - the Dalvik PC of the following instruction.
+ *
+ */
+ ldr r2, .LdvmLockObject
+ mov r3, #0 @ Record that we're not returning
+ str r3, [r0, #offThread_inJitCodeCache]
+ blx r2 @ dvmLockObject(self, obj)
+ @ refresh Jit's on/off status & test for exception
+ ldr r0, [rGLUE, #offGlue_ppJitProfTable]
+ ldr r1, [rGLUE, #offGlue_self]
+ ldr r0, [r0]
+ ldr r1, [r1, #offThread_exception]
+ str r0, [rGLUE, #offGlue_pJitProfTable]
+ cmp r1, #0
+ beq 1f
+ ldr r2, .LhandleException
+ sub r0, r4, #2 @ roll dPC back to this monitor instruction
+ bx r2
+1:
+ @ Bail to interpreter - no chain [note - r4 still contains rPC]
+#if defined(WITH_JIT_TUNING)
+ mov r0, #kHeavyweightMonitor
+#endif
+ ldr pc, .LdvmJitToInterpNoChain
+
+ .size dvmCompilerTemplateStart, .-dvmCompilerTemplateStart
+/* File: armv5te/footer.S */
+/*
+ * ===========================================================================
+ * Common subroutines and data
+ * ===========================================================================
+ */
+
+ .text
+ .align 2
+.LinvokeNative:
+ @ Prep for the native call
+ @ r1 = newFP, r0 = methodToCall
+ ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
+ mov r2, #0
+ ldr r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
+ str r2, [r3, #offThread_inJitCodeCache] @ not in jit code cache
+ str r1, [r3, #offThread_curFrame] @ self->curFrame = newFp
+ str r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+ @ newFp->localRefCookie=top
+ mov r9, r3 @ r9<- glue->self (preserve)
+ SAVEAREA_FROM_FP(r10, r1) @ r10<- new stack save area
+
+ mov r2, r0 @ r2<- methodToCall
+ mov r0, r1 @ r0<- newFP
+ add r1, rGLUE, #offGlue_retval @ r1<- &retval
+
+ LDR_PC_LR "[r2, #offMethod_nativeFunc]"
+
+ @ Refresh Jit's on/off status
+ ldr r3, [rGLUE, #offGlue_ppJitProfTable]
+
+ @ native return; r9=self, r10=newSaveArea
+ @ equivalent to dvmPopJniLocals
+ ldr r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+ ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+ ldr r1, [r9, #offThread_exception] @ check for exception
+ ldr r3, [r3] @ r1 <- pointer to Jit profile table
+ str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
+ cmp r1, #0 @ null?
+ str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
+ ldr r0, [r10, #offStackSaveArea_savedPc] @ reload rPC
+ str r3, [rGLUE, #offGlue_pJitProfTable] @ cache current JitProfTable
+
+ @ r0 = dalvikCallsitePC
+ bne .LhandleException @ no, handle exception
+
+ str r2, [r9, #offThread_inJitCodeCache] @ set the new mode
+ cmp r2, #0 @ return chaining cell still exists?
+ bxne r2 @ yes - go ahead
+
+ @ continue executing the next instruction through the interpreter
+ ldr r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+ add rPC, r0, #6 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+ mov r0, #kCallsiteInterpreted
+#endif
+ mov pc, r1
+
+/*
+ * On entry:
+ * r0 Faulting Dalvik PC
+ */
+.LhandleException:
+#if defined(WITH_SELF_VERIFICATION)
+ ldr pc, .LdeadFood @ should not see this under self-verification mode
+.LdeadFood:
+ .word 0xdeadf00d
+#endif
+ ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
+ mov r2, #0
+ str r2, [r3, #offThread_inJitCodeCache] @ in interpreter land
+ ldr r1, .LdvmMterpCommonExceptionThrown @ PIC way of getting &func
+ ldr rIBASE, .LdvmAsmInstructionStart @ same as above
+ mov rPC, r0 @ reload the faulting Dalvik address
+ mov pc, r1 @ branch to dvmMterpCommonExceptionThrown
+
+ .align 2
+.LdvmAsmInstructionStart:
+ .word dvmAsmInstructionStart
+.LdvmJitToInterpNoChainNoProfile:
+ .word dvmJitToInterpNoChainNoProfile
+.LdvmJitToInterpTraceSelectNoChain:
+ .word dvmJitToInterpTraceSelectNoChain
+.LdvmJitToInterpNoChain:
+ .word dvmJitToInterpNoChain
+.LdvmMterpStdBail:
+ .word dvmMterpStdBail
+.LdvmMterpCommonExceptionThrown:
+ .word dvmMterpCommonExceptionThrown
+.LdvmLockObject:
+ .word dvmLockObject
+#if defined(WITH_JIT_TUNING)
+.LdvmICHitCount:
+ .word gDvmICHitCount
+#endif
+#if defined(WITH_SELF_VERIFICATION)
+.LdvmSelfVerificationMemOpDecode:
+ .word dvmSelfVerificationMemOpDecode
+#endif
+.L__aeabi_cdcmple:
+ .word __aeabi_cdcmple
+.L__aeabi_cfcmple:
+ .word __aeabi_cfcmple
+
+ .global dmvCompilerTemplateEnd
+dmvCompilerTemplateEnd:
+
+#endif /* WITH_JIT */
+
diff --git a/vm/compiler/template/out/CompilerTemplateAsm-armv7-a.S b/vm/compiler/template/out/CompilerTemplateAsm-armv7-a.S
new file mode 100644
index 0000000..87a0691
--- /dev/null
+++ b/vm/compiler/template/out/CompilerTemplateAsm-armv7-a.S
@@ -0,0 +1,1544 @@
+/*
+ * This file was generated automatically by gen-template.py for 'armv7-a'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: armv5te/header.S */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if defined(WITH_JIT)
+
+/*
+ * ARMv5 definitions and declarations.
+ */
+
+/*
+ARM EABI general notes:
+
+r0-r3 hold first 4 args to a method; they are not preserved across method calls
+r4-r8 are available for general use
+r9 is given special treatment in some situations, but not for us
+r10 (sl) seems to be generally available
+r11 (fp) is used by gcc (unless -fomit-frame-pointer is set)
+r12 (ip) is scratch -- not preserved across method calls
+r13 (sp) should be managed carefully in case a signal arrives
+r14 (lr) must be preserved
+r15 (pc) can be tinkered with directly
+
+r0 holds returns of <= 4 bytes
+r0-r1 hold returns of 8 bytes, low word in r0
+
+Callee must save/restore r4+ (except r12) if it modifies them.
+
+Stack is "full descending". Only the arguments that don't fit in the first 4
+registers are placed on the stack. "sp" points at the first stacked argument
+(i.e. the 5th arg).
+
+VFP: single-precision results in s0, double-precision results in d0.
+
+In the EABI, "sp" must be 64-bit aligned on entry to a function, and any
+64-bit quantities (long long, double) must be 64-bit aligned.
+*/
+
+/*
+JIT and ARM notes:
+
+The following registers have fixed assignments:
+
+ reg nick purpose
+ r5 rFP interpreted frame pointer, used for accessing locals and args
+ r6 rGLUE MterpGlue pointer
+
+The following registers have fixed assignments in mterp but are scratch
+registers in compiled code
+
+ reg nick purpose
+ r4 rPC interpreted program counter, used for fetching instructions
+ r7 rINST first 16-bit code unit of current instruction
+ r8 rIBASE interpreted instruction base pointer, used for computed goto
+
+Macros are provided for common operations. Each macro MUST emit only
+one instruction to make instruction-counting easier. They MUST NOT alter
+unspecified registers or condition codes.
+*/
+
+/* single-purpose registers, given names for clarity */
+#define rPC r4
+#define rFP r5
+#define rGLUE r6
+#define rINST r7
+#define rIBASE r8
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+#define SAVEAREA_FROM_FP(_reg, _fpreg) \
+ sub _reg, _fpreg, #sizeofStackSaveArea
+
+#define EXPORT_PC() \
+ str rPC, [rFP, #(-sizeofStackSaveArea + offStackSaveArea_currentPc)]
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../../../mterp/common/asm-constants.h"
+
+/* File: armv5te-vfp/platform.S */
+/*
+ * ===========================================================================
+ * CPU-version-specific defines and utility
+ * ===========================================================================
+ */
+
+/*
+ * Macro for "MOV LR,PC / LDR PC,xxx", which is not allowed pre-ARMv5.
+ * Jump to subroutine.
+ *
+ * May modify IP and LR.
+ */
+.macro LDR_PC_LR source
+ mov lr, pc
+ ldr pc, \source
+.endm
+
+
+ .global dvmCompilerTemplateStart
+ .type dvmCompilerTemplateStart, %function
+ .text
+
+dvmCompilerTemplateStart:
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_CMP_LONG
+dvmCompiler_TEMPLATE_CMP_LONG:
+/* File: armv5te/TEMPLATE_CMP_LONG.S */
+ /*
+ * Compare two 64-bit values. Puts 0, 1, or -1 into the destination
+ * register based on the results of the comparison.
+ *
+ * We load the full values with LDM, but in practice many values could
+ * be resolved by only looking at the high word. This could be made
+ * faster or slower by splitting the LDM into a pair of LDRs.
+ *
+ * If we just wanted to set condition flags, we could do this:
+ * subs ip, r0, r2
+ * sbcs ip, r1, r3
+ * subeqs ip, r0, r2
+ * Leaving { <0, 0, >0 } in ip. However, we have to set it to a specific
+ * integer value, which we can do with 2 conditional mov/mvn instructions
+ * (set 1, set -1; if they're equal we already have 0 in ip), giving
+ * us a constant 5-cycle path plus a branch at the end to the
+ * instruction epilogue code. The multi-compare approach below needs
+ * 2 or 3 cycles + branch if the high word doesn't match, 6 + branch
+ * in the worst case (the 64-bit values are equal).
+ */
+ /* cmp-long vAA, vBB, vCC */
+ cmp r1, r3 @ compare (vBB+1, vCC+1)
+ blt .LTEMPLATE_CMP_LONG_less @ signed compare on high part
+ bgt .LTEMPLATE_CMP_LONG_greater
+ subs r0, r0, r2 @ r0<- r0 - r2
+ bxeq lr
+ bhi .LTEMPLATE_CMP_LONG_greater @ unsigned compare on low part
+.LTEMPLATE_CMP_LONG_less:
+ mvn r0, #0 @ r0<- -1
+ bx lr
+.LTEMPLATE_CMP_LONG_greater:
+ mov r0, #1 @ r0<- 1
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_RETURN
+dvmCompiler_TEMPLATE_RETURN:
+/* File: armv5te/TEMPLATE_RETURN.S */
+ /*
+ * Unwind a frame from the Dalvik stack for compiled OP_RETURN_XXX.
+ * If the stored value in returnAddr
+ * is non-zero, the caller is compiled by the JIT thus return to the
+ * address in the code cache following the invoke instruction. Otherwise
+ * return to the special dvmJitToInterpNoChain entry point.
+ */
+ SAVEAREA_FROM_FP(r0, rFP) @ r0<- saveArea (old)
+ ldr r10, [r0, #offStackSaveArea_prevFrame] @ r10<- saveArea->prevFrame
+ ldr r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+ ldr rPC, [r0, #offStackSaveArea_savedPc] @ rPC<- saveArea->savedPc
+#if !defined(WITH_SELF_VERIFICATION)
+ ldr r9, [r0, #offStackSaveArea_returnAddr] @ r9<- chaining cell ret
+#else
+ mov r9, #0 @ disable chaining
+#endif
+ ldr r2, [r10, #(offStackSaveArea_method - sizeofStackSaveArea)]
+ @ r2<- method we're returning to
+ ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
+ cmp r2, #0 @ break frame?
+#if !defined(WITH_SELF_VERIFICATION)
+ beq 1f @ bail to interpreter
+#else
+ blxeq lr @ punt to interpreter and compare state
+#endif
+ ldr r1, .LdvmJitToInterpNoChainNoProfile @ defined in footer.S
+ mov rFP, r10 @ publish new FP
+ ldrne r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+ ldr r8, [r8] @ r8<- suspendCount
+
+ str r2, [rGLUE, #offGlue_method]@ glue->method = newSave->method
+ ldr r0, [r10, #offClassObject_pDvmDex] @ r0<- method->clazz->pDvmDex
+ str rFP, [r3, #offThread_curFrame] @ self->curFrame = fp
+ add rPC, rPC, #6 @ publish new rPC (advance 6 bytes)
+ str r0, [rGLUE, #offGlue_methodClassDex]
+ cmp r8, #0 @ check the suspendCount
+ movne r9, #0 @ clear the chaining cell address
+ str r9, [r3, #offThread_inJitCodeCache] @ in code cache or not
+ cmp r9, #0 @ chaining cell exists?
+ blxne r9 @ jump to the chaining cell
+#if defined(WITH_JIT_TUNING)
+ mov r0, #kCallsiteInterpreted
+#endif
+ mov pc, r1 @ callsite is interpreted
+1:
+ stmia rGLUE, {rPC, rFP} @ SAVE_PC_FP_TO_GLUE()
+ ldr r2, .LdvmMterpStdBail @ defined in footer.S
+ mov r1, #0 @ changeInterp = false
+ mov r0, rGLUE @ Expecting rGLUE in r0
+ blx r2 @ exit the interpreter
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S */
+ /*
+ * For polymorphic callsites - setup the Dalvik frame and load Dalvik PC
+ * into rPC then jump to dvmJitToInterpNoChain to dispatch the
+ * runtime-resolved callee.
+ */
+ @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+ ldrh r7, [r0, #offMethod_registersSize] @ r7<- methodToCall->regsSize
+ ldrh r2, [r0, #offMethod_outsSize] @ r2<- methodToCall->outsSize
+ ldr r9, [rGLUE, #offGlue_interpStackEnd] @ r9<- interpStackEnd
+ ldr r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+ add r3, r1, #1 @ Thumb addr is odd
+ SAVEAREA_FROM_FP(r1, rFP) @ r1<- stack save area
+ sub r1, r1, r7, lsl #2 @ r1<- newFp (old savearea - regsSize)
+ SAVEAREA_FROM_FP(r10, r1) @ r10<- stack save area
+ sub r10, r10, r2, lsl #2 @ r10<- bottom (newsave - outsSize)
+ ldr r8, [r8] @ r8<- suspendCount (int)
+ cmp r10, r9 @ bottom < interpStackEnd?
+ bxlo lr @ return to raise stack overflow excep.
+ @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+ ldr r9, [r0, #offMethod_clazz] @ r9<- method->clazz
+ ldr r10, [r0, #offMethod_accessFlags] @ r10<- methodToCall->accessFlags
+ str rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+ str rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+ ldr rPC, [r0, #offMethod_insns] @ rPC<- methodToCall->insns
+
+
+ @ set up newSaveArea
+ str rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+ str r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+ str r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+ cmp r8, #0 @ suspendCount != 0
+ bxne lr @ bail to the interpreter
+ tst r10, #ACC_NATIVE
+#if !defined(WITH_SELF_VERIFICATION)
+ bne .LinvokeNative
+#else
+ bxne lr @ bail to the interpreter
+#endif
+
+ ldr r10, .LdvmJitToInterpTraceSelectNoChain
+ ldr r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+ ldr r2, [rGLUE, #offGlue_self] @ r2<- glue->self
+
+ @ Update "glue" values for the new method
+ str r0, [rGLUE, #offGlue_method] @ glue->method = methodToCall
+ str r3, [rGLUE, #offGlue_methodClassDex] @ glue->methodClassDex = ...
+ mov rFP, r1 @ fp = newFp
+ str rFP, [r2, #offThread_curFrame] @ self->curFrame = newFp
+
+ @ Start executing the callee
+#if defined(WITH_JIT_TUNING)
+ mov r0, #kInlineCacheMiss
+#endif
+ mov pc, r10 @ dvmJitToInterpTraceSelectNoChain
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN
+dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S */
+ /*
+ * For monomorphic callsite, setup the Dalvik frame and return to the
+ * Thumb code through the link register to transfer control to the callee
+ * method through a dedicated chaining cell.
+ */
+ @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+ @ methodToCall is guaranteed to be non-native
+.LinvokeChain:
+ ldrh r7, [r0, #offMethod_registersSize] @ r7<- methodToCall->regsSize
+ ldrh r2, [r0, #offMethod_outsSize] @ r2<- methodToCall->outsSize
+ ldr r9, [rGLUE, #offGlue_interpStackEnd] @ r9<- interpStackEnd
+ ldr r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+ add r3, r1, #1 @ Thumb addr is odd
+ SAVEAREA_FROM_FP(r1, rFP) @ r1<- stack save area
+ sub r1, r1, r7, lsl #2 @ r1<- newFp (old savearea - regsSize)
+ SAVEAREA_FROM_FP(r10, r1) @ r10<- stack save area
+ add r12, lr, #2 @ setup the punt-to-interp address
+ sub r10, r10, r2, lsl #2 @ r10<- bottom (newsave - outsSize)
+ ldr r8, [r8] @ r8<- suspendCount (int)
+ cmp r10, r9 @ bottom < interpStackEnd?
+ bxlo r12 @ return to raise stack overflow excep.
+ @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+ ldr r9, [r0, #offMethod_clazz] @ r9<- method->clazz
+ str rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+ str rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+ ldr rPC, [r0, #offMethod_insns] @ rPC<- methodToCall->insns
+
+
+ @ set up newSaveArea
+ str rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+ str r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+ str r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+ cmp r8, #0 @ suspendCount != 0
+ bxne r12 @ bail to the interpreter
+
+ ldr r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+ ldr r2, [rGLUE, #offGlue_self] @ r2<- glue->self
+
+ @ Update "glue" values for the new method
+ str r0, [rGLUE, #offGlue_method] @ glue->method = methodToCall
+ str r3, [rGLUE, #offGlue_methodClassDex] @ glue->methodClassDex = ...
+ mov rFP, r1 @ fp = newFp
+ str rFP, [r2, #offThread_curFrame] @ self->curFrame = newFp
+
+ bx lr @ return to the callee-chaining cell
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN
+dvmCompiler_TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S */
+ /*
+ * For polymorphic callsite, check whether the cached class pointer matches
+ * the current one. If so setup the Dalvik frame and return to the
+ * Thumb code through the link register to transfer control to the callee
+ * method through a dedicated chaining cell.
+ *
+ * The predicted chaining cell is declared in ArmLIR.h with the
+ * following layout:
+ *
+ * typedef struct PredictedChainingCell {
+ * u4 branch;
+ * const ClassObject *clazz;
+ * const Method *method;
+ * u4 counter;
+ * } PredictedChainingCell;
+ *
+ * Upon returning to the callsite:
+ * - lr : to branch to the chaining cell
+ * - lr+2: to punt to the interpreter
+ * - lr+4: to fully resolve the callee and may rechain.
+ * r3 <- class
+ * r9 <- counter
+ */
+ @ r0 = this, r1 = returnCell, r2 = predictedChainCell, rPC = dalvikCallsite
+ ldr r3, [r0, #offObject_clazz] @ r3 <- this->class
+ ldr r8, [r2, #4] @ r8 <- predictedChainCell->clazz
+ ldr r0, [r2, #8] @ r0 <- predictedChainCell->method
+ ldr r9, [rGLUE, #offGlue_icRechainCount] @ r1 <- shared rechainCount
+ cmp r3, r8 @ predicted class == actual class?
+#if defined(WITH_JIT_TUNING)
+ ldr r7, .LdvmICHitCount
+ ldreq r10, [r7, #0]
+ add r10, r10, #1
+ streq r10, [r7, #0]
+#endif
+ beq .LinvokeChain @ predicted chain is valid
+ ldr r7, [r3, #offClassObject_vtable] @ r7 <- this->class->vtable
+ cmp r8, #0 @ initialized class or not
+ moveq r1, #0
+ subne r1, r9, #1 @ count--
+ strne r1, [rGLUE, #offGlue_icRechainCount] @ write back to InterpState
+ add lr, lr, #4 @ return to fully-resolve landing pad
+ /*
+ * r1 <- count
+ * r2 <- &predictedChainCell
+ * r3 <- this->class
+ * r4 <- dPC
+ * r7 <- this->class->vtable
+ */
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE
+dvmCompiler_TEMPLATE_INVOKE_METHOD_NATIVE:
+/* File: armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S */
+ @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite
+ ldrh r7, [r0, #offMethod_registersSize] @ r7<- methodToCall->regsSize
+ ldr r9, [rGLUE, #offGlue_interpStackEnd] @ r9<- interpStackEnd
+ ldr r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount
+ add r3, r1, #1 @ Thumb addr is odd
+ SAVEAREA_FROM_FP(r1, rFP) @ r1<- stack save area
+ sub r1, r1, r7, lsl #2 @ r1<- newFp (old savearea - regsSize)
+ SAVEAREA_FROM_FP(r10, r1) @ r10<- stack save area
+ ldr r8, [r8] @ r3<- suspendCount (int)
+ cmp r10, r9 @ bottom < interpStackEnd?
+ bxlo lr @ return to raise stack overflow excep.
+ @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite
+ str rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+ str rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)]
+ ldr rPC, [r0, #offMethod_insns] @ rPC<- methodToCall->insns
+
+
+ @ set up newSaveArea
+ str rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)]
+ str r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)]
+ ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
+ str r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)]
+ cmp r8, #0 @ suspendCount != 0
+ ldr r8, [r0, #offMethod_nativeFunc] @ r8<- method->nativeFunc
+#if !defined(WITH_SELF_VERIFICATION)
+ bxne lr @ bail to the interpreter
+#else
+ bx lr @ bail to interpreter unconditionally
+#endif
+
+ @ go ahead and transfer control to the native code
+ ldr r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
+ mov r2, #0
+ str r1, [r3, #offThread_curFrame] @ self->curFrame = newFp
+ str r2, [r3, #offThread_inJitCodeCache] @ not in the jit code cache
+ str r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+ @ newFp->localRefCookie=top
+ mov r9, r3 @ r9<- glue->self (preserve)
+ SAVEAREA_FROM_FP(r10, r1) @ r10<- new stack save area
+
+ mov r2, r0 @ r2<- methodToCall
+ mov r0, r1 @ r0<- newFP
+ add r1, rGLUE, #offGlue_retval @ r1<- &retval
+
+ blx r8 @ off to the native code
+
+ @ native return; r9=self, r10=newSaveArea
+ @ equivalent to dvmPopJniLocals
+ ldr r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+ ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+ ldr r1, [r9, #offThread_exception] @ check for exception
+ str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
+ cmp r1, #0 @ null?
+ str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
+ ldr r0, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)]
+
+ @ r0 = dalvikCallsitePC
+ bne .LhandleException @ no, handle exception
+
+ str r2, [r9, #offThread_inJitCodeCache] @ set the mode properly
+ cmp r2, #0 @ return chaining cell still exists?
+ bxne r2 @ yes - go ahead
+
+ @ continue executing the next instruction through the interpreter
+ ldr r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+ add rPC, r0, #6 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+ mov r0, #kCallsiteInterpreted
+#endif
+ mov pc, r1
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_MUL_LONG
+dvmCompiler_TEMPLATE_MUL_LONG:
+/* File: armv5te/TEMPLATE_MUL_LONG.S */
+ /*
+ * Signed 64-bit integer multiply.
+ *
+ * For JIT: op1 in r0/r1, op2 in r2/r3, return in r0/r1
+ *
+ * Consider WXxYZ (r1r0 x r3r2) with a long multiply:
+ * WX
+ * x YZ
+ * --------
+ * ZW ZX
+ * YW YX
+ *
+ * The low word of the result holds ZX, the high word holds
+ * (ZW+YX) + (the high overflow from ZX). YW doesn't matter because
+ * it doesn't fit in the low 64 bits.
+ *
+ * Unlike most ARM math operations, multiply instructions have
+ * restrictions on using the same register more than once (Rd and Rm
+ * cannot be the same).
+ */
+ /* mul-long vAA, vBB, vCC */
+ mul ip, r2, r1 @ ip<- ZxW
+ umull r9, r10, r2, r0 @ r9/r10 <- ZxX
+ mla r2, r0, r3, ip @ r2<- YxX + (ZxW)
+ add r10, r2, r10 @ r10<- r10 + low(ZxW + (YxX))
+ mov r0,r9
+ mov r1,r10
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_SHL_LONG
+dvmCompiler_TEMPLATE_SHL_LONG:
+/* File: armv5te/TEMPLATE_SHL_LONG.S */
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to ignore all but the low
+ * 6 bits.
+ */
+ /* shl-long vAA, vBB, vCC */
+ and r2, r2, #63 @ r2<- r2 & 0x3f
+ mov r1, r1, asl r2 @ r1<- r1 << r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32)
+ mov r0, r0, asl r2 @ r0<- r0 << r2
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_SHR_LONG
+dvmCompiler_TEMPLATE_SHR_LONG:
+/* File: armv5te/TEMPLATE_SHR_LONG.S */
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to ignore all but the low
+ * 6 bits.
+ */
+ /* shr-long vAA, vBB, vCC */
+ and r2, r2, #63 @ r0<- r0 & 0x3f
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32)
+ mov r1, r1, asr r2 @ r1<- r1 >> r2
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_USHR_LONG
+dvmCompiler_TEMPLATE_USHR_LONG:
+/* File: armv5te/TEMPLATE_USHR_LONG.S */
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to ignore all but the low
+ * 6 bits.
+ */
+ /* ushr-long vAA, vBB, vCC */
+ and r2, r2, #63 @ r0<- r0 & 0x3f
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32)
+ mov r1, r1, lsr r2 @ r1<- r1 >>> r2
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_ADD_FLOAT_VFP
+dvmCompiler_TEMPLATE_ADD_FLOAT_VFP:
+/* File: armv5te-vfp/TEMPLATE_ADD_FLOAT_VFP.S */
+/* File: armv5te-vfp/fbinop.S */
+ /*
+ * Generic 32-bit floating point operation. Provide an "instr" line that
+ * specifies an instruction that performs s2 = s0 op s1.
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = op1 address
+ * r2 = op2 address
+ */
+ flds s0,[r1]
+ flds s1,[r2]
+ fadds s2, s0, s1
+ fsts s2,[r0]
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_SUB_FLOAT_VFP
+dvmCompiler_TEMPLATE_SUB_FLOAT_VFP:
+/* File: armv5te-vfp/TEMPLATE_SUB_FLOAT_VFP.S */
+/* File: armv5te-vfp/fbinop.S */
+ /*
+ * Generic 32-bit floating point operation. Provide an "instr" line that
+ * specifies an instruction that performs s2 = s0 op s1.
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = op1 address
+ * r2 = op2 address
+ */
+ flds s0,[r1]
+ flds s1,[r2]
+ fsubs s2, s0, s1
+ fsts s2,[r0]
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_MUL_FLOAT_VFP
+dvmCompiler_TEMPLATE_MUL_FLOAT_VFP:
+/* File: armv5te-vfp/TEMPLATE_MUL_FLOAT_VFP.S */
+/* File: armv5te-vfp/fbinop.S */
+ /*
+ * Generic 32-bit floating point operation. Provide an "instr" line that
+ * specifies an instruction that performs s2 = s0 op s1.
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = op1 address
+ * r2 = op2 address
+ */
+ flds s0,[r1]
+ flds s1,[r2]
+ fmuls s2, s0, s1
+ fsts s2,[r0]
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_DIV_FLOAT_VFP
+dvmCompiler_TEMPLATE_DIV_FLOAT_VFP:
+/* File: armv5te-vfp/TEMPLATE_DIV_FLOAT_VFP.S */
+/* File: armv5te-vfp/fbinop.S */
+ /*
+ * Generic 32-bit floating point operation. Provide an "instr" line that
+ * specifies an instruction that performs s2 = s0 op s1.
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = op1 address
+ * r2 = op2 address
+ */
+ flds s0,[r1]
+ flds s1,[r2]
+ fdivs s2, s0, s1
+ fsts s2,[r0]
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_ADD_DOUBLE_VFP
+dvmCompiler_TEMPLATE_ADD_DOUBLE_VFP:
+/* File: armv5te-vfp/TEMPLATE_ADD_DOUBLE_VFP.S */
+/* File: armv5te-vfp/fbinopWide.S */
+ /*
+ * Generic 64-bit floating point operation. Provide an "instr" line that
+ * specifies an instruction that performs s2 = s0 op s1.
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = op1 address
+ * r2 = op2 address
+ */
+ fldd d0,[r1]
+ fldd d1,[r2]
+ faddd d2, d0, d1
+ fstd d2,[r0]
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_SUB_DOUBLE_VFP
+dvmCompiler_TEMPLATE_SUB_DOUBLE_VFP:
+/* File: armv5te-vfp/TEMPLATE_SUB_DOUBLE_VFP.S */
+/* File: armv5te-vfp/fbinopWide.S */
+ /*
+ * Generic 64-bit floating point operation. Provide an "instr" line that
+ * specifies an instruction that performs s2 = s0 op s1.
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = op1 address
+ * r2 = op2 address
+ */
+ fldd d0,[r1]
+ fldd d1,[r2]
+ fsubd d2, d0, d1
+ fstd d2,[r0]
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_MUL_DOUBLE_VFP
+dvmCompiler_TEMPLATE_MUL_DOUBLE_VFP:
+/* File: armv5te-vfp/TEMPLATE_MUL_DOUBLE_VFP.S */
+/* File: armv5te-vfp/fbinopWide.S */
+ /*
+ * Generic 64-bit floating point operation. Provide an "instr" line that
+ * specifies an instruction that performs s2 = s0 op s1.
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = op1 address
+ * r2 = op2 address
+ */
+ fldd d0,[r1]
+ fldd d1,[r2]
+ fmuld d2, d0, d1
+ fstd d2,[r0]
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_DIV_DOUBLE_VFP
+dvmCompiler_TEMPLATE_DIV_DOUBLE_VFP:
+/* File: armv5te-vfp/TEMPLATE_DIV_DOUBLE_VFP.S */
+/* File: armv5te-vfp/fbinopWide.S */
+ /*
+ * Generic 64-bit floating point operation. Provide an "instr" line that
+ * specifies an instruction that performs s2 = s0 op s1.
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = op1 address
+ * r2 = op2 address
+ */
+ fldd d0,[r1]
+ fldd d1,[r2]
+ fdivd d2, d0, d1
+ fstd d2,[r0]
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_DOUBLE_TO_FLOAT_VFP
+dvmCompiler_TEMPLATE_DOUBLE_TO_FLOAT_VFP:
+/* File: armv5te-vfp/TEMPLATE_DOUBLE_TO_FLOAT_VFP.S */
+/* File: armv5te-vfp/funopNarrower.S */
+ /*
+ * Generic 64bit-to-32bit floating point unary operation. Provide an
+ * "instr" line that specifies an instruction that performs "s0 = op d0".
+ *
+ * For: double-to-int, double-to-float
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = src dalvik register address
+ */
+ /* unop vA, vB */
+ fldd d0, [r1] @ d0<- vB
+ fcvtsd s0, d0 @ s0<- op d0
+ fsts s0, [r0] @ vA<- s0
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_DOUBLE_TO_INT_VFP
+dvmCompiler_TEMPLATE_DOUBLE_TO_INT_VFP:
+/* File: armv5te-vfp/TEMPLATE_DOUBLE_TO_INT_VFP.S */
+/* File: armv5te-vfp/funopNarrower.S */
+ /*
+ * Generic 64bit-to-32bit floating point unary operation. Provide an
+ * "instr" line that specifies an instruction that performs "s0 = op d0".
+ *
+ * For: double-to-int, double-to-float
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = src dalvik register address
+ */
+ /* unop vA, vB */
+ fldd d0, [r1] @ d0<- vB
+ ftosizd s0, d0 @ s0<- op d0
+ fsts s0, [r0] @ vA<- s0
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_FLOAT_TO_DOUBLE_VFP
+dvmCompiler_TEMPLATE_FLOAT_TO_DOUBLE_VFP:
+/* File: armv5te-vfp/TEMPLATE_FLOAT_TO_DOUBLE_VFP.S */
+/* File: armv5te-vfp/funopWider.S */
+ /*
+ * Generic 32bit-to-64bit floating point unary operation. Provide an
+ * "instr" line that specifies an instruction that performs "d0 = op s0".
+ *
+ * For: int-to-double, float-to-double
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = src dalvik register address
+ */
+ /* unop vA, vB */
+ flds s0, [r1] @ s0<- vB
+ fcvtds d0, s0 @ d0<- op s0
+ fstd d0, [r0] @ vA<- d0
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_FLOAT_TO_INT_VFP
+dvmCompiler_TEMPLATE_FLOAT_TO_INT_VFP:
+/* File: armv5te-vfp/TEMPLATE_FLOAT_TO_INT_VFP.S */
+/* File: armv5te-vfp/funop.S */
+ /*
+ * Generic 32bit-to-32bit floating point unary operation. Provide an
+ * "instr" line that specifies an instruction that performs "s1 = op s0".
+ *
+ * For: float-to-int, int-to-float
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = src dalvik register address
+ */
+ /* unop vA, vB */
+ flds s0, [r1] @ s0<- vB
+ ftosizs s1, s0 @ s1<- op s0
+ fsts s1, [r0] @ vA<- s1
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_INT_TO_DOUBLE_VFP
+dvmCompiler_TEMPLATE_INT_TO_DOUBLE_VFP:
+/* File: armv5te-vfp/TEMPLATE_INT_TO_DOUBLE_VFP.S */
+/* File: armv5te-vfp/funopWider.S */
+ /*
+ * Generic 32bit-to-64bit floating point unary operation. Provide an
+ * "instr" line that specifies an instruction that performs "d0 = op s0".
+ *
+ * For: int-to-double, float-to-double
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = src dalvik register address
+ */
+ /* unop vA, vB */
+ flds s0, [r1] @ s0<- vB
+ fsitod d0, s0 @ d0<- op s0
+ fstd d0, [r0] @ vA<- d0
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_INT_TO_FLOAT_VFP
+dvmCompiler_TEMPLATE_INT_TO_FLOAT_VFP:
+/* File: armv5te-vfp/TEMPLATE_INT_TO_FLOAT_VFP.S */
+/* File: armv5te-vfp/funop.S */
+ /*
+ * Generic 32bit-to-32bit floating point unary operation. Provide an
+ * "instr" line that specifies an instruction that performs "s1 = op s0".
+ *
+ * For: float-to-int, int-to-float
+ *
+ * On entry:
+ * r0 = target dalvik register address
+ * r1 = src dalvik register address
+ */
+ /* unop vA, vB */
+ flds s0, [r1] @ s0<- vB
+ fsitos s1, s0 @ s1<- op s0
+ fsts s1, [r0] @ vA<- s1
+ bx lr
+
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_CMPG_DOUBLE_VFP
+dvmCompiler_TEMPLATE_CMPG_DOUBLE_VFP:
+/* File: armv5te-vfp/TEMPLATE_CMPG_DOUBLE_VFP.S */
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * int compare(x, y) {
+ * if (x == y) {
+ * return 0;
+ * } else if (x < y) {
+ * return -1;
+ * } else if (x > y) {
+ * return 1;
+ * } else {
+ * return 1;
+ * }
+ * }
+ *
+ * On entry:
+ * r0 = &op1 [vBB]
+ * r1 = &op2 [vCC]
+ */
+ /* op vAA, vBB, vCC */
+ fldd d0, [r0] @ d0<- vBB
+ fldd d1, [r1] @ d1<- vCC
+ fcmpd d0, d1 @ compare (vBB, vCC)
+ mov r0, #1 @ r0<- 1 (default)
+ fmstat @ export status flags
+ mvnmi r0, #0 @ (less than) r0<- -1
+ moveq r0, #0 @ (equal) r0<- 0
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_CMPL_DOUBLE_VFP
+dvmCompiler_TEMPLATE_CMPL_DOUBLE_VFP:
+/* File: armv5te-vfp/TEMPLATE_CMPL_DOUBLE_VFP.S */
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * int compare(x, y) {
+ * if (x == y) {
+ * return 0;
+ * } else if (x > y) {
+ * return 1;
+ * } else if (x < y) {
+ * return -1;
+ * } else {
+ * return -1;
+ * }
+ * }
+ * On entry:
+ * r0 = &op1 [vBB]
+ * r1 = &op2 [vCC]
+ */
+ /* op vAA, vBB, vCC */
+ fldd d0, [r0] @ d0<- vBB
+ fldd d1, [r1] @ d1<- vCC
+ fcmped d0, d1 @ compare (vBB, vCC)
+ mvn r0, #0 @ r0<- -1 (default)
+ fmstat @ export status flags
+ movgt r0, #1 @ (greater than) r0<- 1
+ moveq r0, #0 @ (equal) r0<- 0
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_CMPG_FLOAT_VFP
+dvmCompiler_TEMPLATE_CMPG_FLOAT_VFP:
+/* File: armv5te-vfp/TEMPLATE_CMPG_FLOAT_VFP.S */
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * int compare(x, y) {
+ * if (x == y) {
+ * return 0;
+ * } else if (x < y) {
+ * return -1;
+ * } else if (x > y) {
+ * return 1;
+ * } else {
+ * return 1;
+ * }
+ * }
+ * On entry:
+ * r0 = &op1 [vBB]
+ * r1 = &op2 [vCC]
+ */
+ /* op vAA, vBB, vCC */
+ flds s0, [r0] @ d0<- vBB
+ flds s1, [r1] @ d1<- vCC
+ fcmps s0, s1 @ compare (vBB, vCC)
+ mov r0, #1 @ r0<- 1 (default)
+ fmstat @ export status flags
+ mvnmi r0, #0 @ (less than) r0<- -1
+ moveq r0, #0 @ (equal) r0<- 0
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_CMPL_FLOAT_VFP
+dvmCompiler_TEMPLATE_CMPL_FLOAT_VFP:
+/* File: armv5te-vfp/TEMPLATE_CMPL_FLOAT_VFP.S */
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * int compare(x, y) {
+ * if (x == y) {
+ * return 0;
+ * } else if (x > y) {
+ * return 1;
+ * } else if (x < y) {
+ * return -1;
+ * } else {
+ * return -1;
+ * }
+ * }
+ * On entry:
+ * r0 = &op1 [vBB]
+ * r1 = &op2 [vCC]
+ */
+ /* op vAA, vBB, vCC */
+ flds s0, [r0] @ d0<- vBB
+ flds s1, [r1] @ d1<- vCC
+ fcmps s0, s1 @ compare (vBB, vCC)
+ mvn r0, #0 @ r0<- -1 (default)
+ fmstat @ export status flags
+ movgt r0, #1 @ (greater than) r0<- 1
+ moveq r0, #0 @ (equal) r0<- 0
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_SQRT_DOUBLE_VFP
+dvmCompiler_TEMPLATE_SQRT_DOUBLE_VFP:
+/* File: armv5te-vfp/TEMPLATE_SQRT_DOUBLE_VFP.S */
+ /*
+ * 64-bit floating point vfp sqrt operation.
+ * If the result is a NaN, bail out to library code to do
+ * the right thing.
+ *
+ * On entry:
+ * r2 src addr of op1
+ * On exit:
+ * r0,r1 = res
+ */
+ fldd d0, [r2]
+ fsqrtd d1, d0
+ fcmpd d1, d1
+ fmstat
+ fmrrd r0, r1, d1
+ bxeq lr @ Result OK - return
+ ldr r2, .Lsqrt
+ fmrrd r0, r1, d0 @ reload orig operand
+ bx r2 @ tail call to sqrt library routine
+
+.Lsqrt:
+ .word sqrt
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_THROW_EXCEPTION_COMMON
+dvmCompiler_TEMPLATE_THROW_EXCEPTION_COMMON:
+/* File: armv5te/TEMPLATE_THROW_EXCEPTION_COMMON.S */
+ /*
+ * Throw an exception from JIT'ed code.
+ * On entry:
+ * r0 Dalvik PC that raises the exception
+ */
+ b .LhandleException
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_MEM_OP_DECODE
+dvmCompiler_TEMPLATE_MEM_OP_DECODE:
+/* File: armv5te-vfp/TEMPLATE_MEM_OP_DECODE.S */
+#if defined(WITH_SELF_VERIFICATION)
+ /*
+ * This handler encapsulates heap memory ops for selfVerification mode.
+ *
+ * The call to the handler is inserted prior to a heap memory operation.
+ * This handler then calls a function to decode the memory op, and process
+ * it accordingly. Afterwards, the handler changes the return address to
+ * skip the memory op so it never gets executed.
+ */
+ vpush {d0-d15} @ save out all fp registers
+ push {r0-r12,lr} @ save out all registers
+ mov r0, lr @ arg0 <- link register
+ mov r1, sp @ arg1 <- stack pointer
+ ldr r2, .LdvmSelfVerificationMemOpDecode @ defined in footer.S
+ blx r2 @ decode and handle the mem op
+ pop {r0-r12,lr} @ restore all registers
+ vpop {d0-d15} @ restore all fp registers
+ bx lr @ return to compiled code
+#endif
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_STRING_COMPARETO
+dvmCompiler_TEMPLATE_STRING_COMPARETO:
+/* File: armv5te/TEMPLATE_STRING_COMPARETO.S */
+ /*
+ * String's compareTo.
+ *
+ * Requires r0/r1 to have been previously checked for null. Will
+ * return negative if this's string is < comp, 0 if they are the
+ * same and positive if >.
+ *
+ * IMPORTANT NOTE:
+ *
+ * This code relies on hard-coded offsets for string objects, and must be
+ * kept in sync with definitions in UtfString.h. See asm-constants.h
+ *
+ * On entry:
+ * r0: this object pointer
+ * r1: comp object pointer
+ *
+ */
+
+ mov r2, r0 @ this to r2, opening up r0 for return value
+ subs r0, r2, r1 @ Same?
+ bxeq lr
+
+ ldr r4, [r2, #STRING_FIELDOFF_OFFSET]
+ ldr r9, [r1, #STRING_FIELDOFF_OFFSET]
+ ldr r7, [r2, #STRING_FIELDOFF_COUNT]
+ ldr r10, [r1, #STRING_FIELDOFF_COUNT]
+ ldr r2, [r2, #STRING_FIELDOFF_VALUE]
+ ldr r1, [r1, #STRING_FIELDOFF_VALUE]
+
+ /*
+ * At this point, we have:
+ * value: r2/r1
+ * offset: r4/r9
+ * count: r7/r10
+ * We're going to compute
+ * r11 <- countDiff
+ * r10 <- minCount
+ */
+ subs r11, r7, r10
+ movls r10, r7
+
+ /* Now, build pointers to the string data */
+ add r2, r2, r4, lsl #1
+ add r1, r1, r9, lsl #1
+ /*
+ * Note: data pointers point to previous element so we can use pre-index
+ * mode with base writeback.
+ */
+ add r2, #16-2 @ offset to contents[-1]
+ add r1, #16-2 @ offset to contents[-1]
+
+ /*
+ * At this point we have:
+ * r2: *this string data
+ * r1: *comp string data
+ * r10: iteration count for comparison
+ * r11: value to return if the first part of the string is equal
+ * r0: reserved for result
+ * r3, r4, r7, r8, r9, r12 available for loading string data
+ */
+
+ subs r10, #2
+ blt do_remainder2
+
+ /*
+ * Unroll the first two checks so we can quickly catch early mismatch
+ * on long strings (but preserve incoming alignment)
+ */
+
+ ldrh r3, [r2, #2]!
+ ldrh r4, [r1, #2]!
+ ldrh r7, [r2, #2]!
+ ldrh r8, [r1, #2]!
+ subs r0, r3, r4
+ subeqs r0, r7, r8
+ bxne lr
+ cmp r10, #28
+ bgt do_memcmp16
+ subs r10, #3
+ blt do_remainder
+
+loopback_triple:
+ ldrh r3, [r2, #2]!
+ ldrh r4, [r1, #2]!
+ ldrh r7, [r2, #2]!
+ ldrh r8, [r1, #2]!
+ ldrh r9, [r2, #2]!
+ ldrh r12,[r1, #2]!
+ subs r0, r3, r4
+ subeqs r0, r7, r8
+ subeqs r0, r9, r12
+ bxne lr
+ subs r10, #3
+ bge loopback_triple
+
+do_remainder:
+ adds r10, #3
+ beq returnDiff
+
+loopback_single:
+ ldrh r3, [r2, #2]!
+ ldrh r4, [r1, #2]!
+ subs r0, r3, r4
+ bxne lr
+ subs r10, #1
+ bne loopback_single
+
+returnDiff:
+ mov r0, r11
+ bx lr
+
+do_remainder2:
+ adds r10, #2
+ bne loopback_single
+ mov r0, r11
+ bx lr
+
+ /* Long string case */
+do_memcmp16:
+ mov r4, lr
+ ldr lr, .Lmemcmp16
+ mov r7, r11
+ add r0, r2, #2
+ add r1, r1, #2
+ mov r2, r10
+ blx lr
+ cmp r0, #0
+ bxne r4
+ mov r0, r7
+ bx r4
+
+.Lmemcmp16:
+ .word __memcmp16
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_STRING_INDEXOF
+dvmCompiler_TEMPLATE_STRING_INDEXOF:
+/* File: armv5te/TEMPLATE_STRING_INDEXOF.S */
+ /*
+ * String's indexOf.
+ *
+ * Requires r0 to have been previously checked for null. Will
+ * return index of match of r1 in r0.
+ *
+ * IMPORTANT NOTE:
+ *
+ * This code relies on hard-coded offsets for string objects, and must be
+ * kept in sync wth definitions in UtfString.h See asm-constants.h
+ *
+ * On entry:
+ * r0: string object pointer
+ * r1: char to match
+ * r2: Starting offset in string data
+ */
+
+ ldr r7, [r0, #STRING_FIELDOFF_OFFSET]
+ ldr r8, [r0, #STRING_FIELDOFF_COUNT]
+ ldr r0, [r0, #STRING_FIELDOFF_VALUE]
+
+ /*
+ * At this point, we have:
+ * r0: object pointer
+ * r1: char to match
+ * r2: starting offset
+ * r7: offset
+ * r8: string length
+ */
+
+ /* Build pointer to start of string data */
+ add r0, #16
+ add r0, r0, r7, lsl #1
+
+ /* Save a copy of starting data in r7 */
+ mov r7, r0
+
+ /* Clamp start to [0..count] */
+ cmp r2, #0
+ movlt r2, #0
+ cmp r2, r8
+ movgt r2, r8
+
+ /* Build pointer to start of data to compare and pre-bias */
+ add r0, r0, r2, lsl #1
+ sub r0, #2
+
+ /* Compute iteration count */
+ sub r8, r2
+
+ /*
+ * At this point we have:
+ * r0: start of data to test
+ * r1: chat to compare
+ * r8: iteration count
+ * r7: original start of string
+ * r3, r4, r9, r10, r11, r12 available for loading string data
+ */
+
+ subs r8, #4
+ blt indexof_remainder
+
+indexof_loop4:
+ ldrh r3, [r0, #2]!
+ ldrh r4, [r0, #2]!
+ ldrh r10, [r0, #2]!
+ ldrh r11, [r0, #2]!
+ cmp r3, r1
+ beq match_0
+ cmp r4, r1
+ beq match_1
+ cmp r10, r1
+ beq match_2
+ cmp r11, r1
+ beq match_3
+ subs r8, #4
+ bge indexof_loop4
+
+indexof_remainder:
+ adds r8, #4
+ beq indexof_nomatch
+
+indexof_loop1:
+ ldrh r3, [r0, #2]!
+ cmp r3, r1
+ beq match_3
+ subs r8, #1
+ bne indexof_loop1
+
+indexof_nomatch:
+ mov r0, #-1
+ bx lr
+
+match_0:
+ sub r0, #6
+ sub r0, r7
+ asr r0, r0, #1
+ bx lr
+match_1:
+ sub r0, #4
+ sub r0, r7
+ asr r0, r0, #1
+ bx lr
+match_2:
+ sub r0, #2
+ sub r0, r7
+ asr r0, r0, #1
+ bx lr
+match_3:
+ sub r0, r7
+ asr r0, r0, #1
+ bx lr
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_INTERPRET
+dvmCompiler_TEMPLATE_INTERPRET:
+/* File: armv5te/TEMPLATE_INTERPRET.S */
+ /*
+ * This handler transfers control to the interpeter without performing
+ * any lookups. It may be called either as part of a normal chaining
+ * operation, or from the transition code in header.S. We distinquish
+ * the two cases by looking at the link register. If called from a
+ * translation chain, it will point to the chaining Dalvik PC -3.
+ * On entry:
+ * lr - if NULL:
+ * r1 - the Dalvik PC to begin interpretation.
+ * else
+ * [lr, #3] contains Dalvik PC to begin interpretation
+ * rGLUE - pointer to interpState
+ * rFP - Dalvik frame pointer
+ */
+ cmp lr, #0
+ ldrne r1,[lr, #3]
+ ldr r2, .LinterpPunt
+ mov r0, r1 @ set Dalvik PC
+ bx r2
+ @ doesn't return
+
+.LinterpPunt:
+ .word dvmJitToInterpPunt
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_MONITOR_ENTER
+dvmCompiler_TEMPLATE_MONITOR_ENTER:
+/* File: armv5te/TEMPLATE_MONITOR_ENTER.S */
+ /*
+ * Call out to the runtime to lock an object. Because this thread
+ * may have been suspended in THREAD_MONITOR state and the Jit's
+ * translation cache subsequently cleared, we cannot return directly.
+ * Instead, unconditionally transition to the interpreter to resume.
+ *
+ * On entry:
+ * r0 - self pointer
+ * r1 - the object (which has already been null-checked by the caller
+ * r4 - the Dalvik PC of the following instruction.
+ */
+ ldr r2, .LdvmLockObject
+ mov r3, #0 @ Record that we're not returning
+ str r3, [r0, #offThread_inJitCodeCache]
+ blx r2 @ dvmLockObject(self, obj)
+ @ refresh Jit's on/off status
+ ldr r0, [rGLUE, #offGlue_ppJitProfTable]
+ ldr r0, [r0]
+ ldr r2, .LdvmJitToInterpNoChain
+ str r0, [rGLUE, #offGlue_pJitProfTable]
+ @ Bail to interpreter - no chain [note - r4 still contains rPC]
+#if defined(WITH_JIT_TUNING)
+ mov r0, #kHeavyweightMonitor
+#endif
+ bx r2
+
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_MONITOR_ENTER_DEBUG
+dvmCompiler_TEMPLATE_MONITOR_ENTER_DEBUG:
+/* File: armv5te/TEMPLATE_MONITOR_ENTER_DEBUG.S */
+ /*
+ * To support deadlock prediction, this version of MONITOR_ENTER
+ * will always call the heavyweight dvmLockObject, check for an
+ * exception and then bail out to the interpreter.
+ *
+ * On entry:
+ * r0 - self pointer
+ * r1 - the object (which has already been null-checked by the caller
+ * r4 - the Dalvik PC of the following instruction.
+ *
+ */
+ ldr r2, .LdvmLockObject
+ mov r3, #0 @ Record that we're not returning
+ str r3, [r0, #offThread_inJitCodeCache]
+ blx r2 @ dvmLockObject(self, obj)
+ @ refresh Jit's on/off status & test for exception
+ ldr r0, [rGLUE, #offGlue_ppJitProfTable]
+ ldr r1, [rGLUE, #offGlue_self]
+ ldr r0, [r0]
+ ldr r1, [r1, #offThread_exception]
+ str r0, [rGLUE, #offGlue_pJitProfTable]
+ cmp r1, #0
+ beq 1f
+ ldr r2, .LhandleException
+ sub r0, r4, #2 @ roll dPC back to this monitor instruction
+ bx r2
+1:
+ @ Bail to interpreter - no chain [note - r4 still contains rPC]
+#if defined(WITH_JIT_TUNING)
+ mov r0, #kHeavyweightMonitor
+#endif
+ ldr pc, .LdvmJitToInterpNoChain
+
+ .size dvmCompilerTemplateStart, .-dvmCompilerTemplateStart
+/* File: armv5te/footer.S */
+/*
+ * ===========================================================================
+ * Common subroutines and data
+ * ===========================================================================
+ */
+
+ .text
+ .align 2
+.LinvokeNative:
+ @ Prep for the native call
+ @ r1 = newFP, r0 = methodToCall
+ ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
+ mov r2, #0
+ ldr r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
+ str r2, [r3, #offThread_inJitCodeCache] @ not in jit code cache
+ str r1, [r3, #offThread_curFrame] @ self->curFrame = newFp
+ str r9, [r1, #(offStackSaveArea_localRefCookie - sizeofStackSaveArea)]
+ @ newFp->localRefCookie=top
+ mov r9, r3 @ r9<- glue->self (preserve)
+ SAVEAREA_FROM_FP(r10, r1) @ r10<- new stack save area
+
+ mov r2, r0 @ r2<- methodToCall
+ mov r0, r1 @ r0<- newFP
+ add r1, rGLUE, #offGlue_retval @ r1<- &retval
+
+ LDR_PC_LR "[r2, #offMethod_nativeFunc]"
+
+ @ Refresh Jit's on/off status
+ ldr r3, [rGLUE, #offGlue_ppJitProfTable]
+
+ @ native return; r9=self, r10=newSaveArea
+ @ equivalent to dvmPopJniLocals
+ ldr r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret
+ ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved->top
+ ldr r1, [r9, #offThread_exception] @ check for exception
+ ldr r3, [r3] @ r1 <- pointer to Jit profile table
+ str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
+ cmp r1, #0 @ null?
+ str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
+ ldr r0, [r10, #offStackSaveArea_savedPc] @ reload rPC
+ str r3, [rGLUE, #offGlue_pJitProfTable] @ cache current JitProfTable
+
+ @ r0 = dalvikCallsitePC
+ bne .LhandleException @ no, handle exception
+
+ str r2, [r9, #offThread_inJitCodeCache] @ set the new mode
+ cmp r2, #0 @ return chaining cell still exists?
+ bxne r2 @ yes - go ahead
+
+ @ continue executing the next instruction through the interpreter
+ ldr r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
+ add rPC, r0, #6 @ reconstruct new rPC (advance 6 bytes)
+#if defined(WITH_JIT_TUNING)
+ mov r0, #kCallsiteInterpreted
+#endif
+ mov pc, r1
+
+/*
+ * On entry:
+ * r0 Faulting Dalvik PC
+ */
+.LhandleException:
+#if defined(WITH_SELF_VERIFICATION)
+ ldr pc, .LdeadFood @ should not see this under self-verification mode
+.LdeadFood:
+ .word 0xdeadf00d
+#endif
+ ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
+ mov r2, #0
+ str r2, [r3, #offThread_inJitCodeCache] @ in interpreter land
+ ldr r1, .LdvmMterpCommonExceptionThrown @ PIC way of getting &func
+ ldr rIBASE, .LdvmAsmInstructionStart @ same as above
+ mov rPC, r0 @ reload the faulting Dalvik address
+ mov pc, r1 @ branch to dvmMterpCommonExceptionThrown
+
+ .align 2
+.LdvmAsmInstructionStart:
+ .word dvmAsmInstructionStart
+.LdvmJitToInterpNoChainNoProfile:
+ .word dvmJitToInterpNoChainNoProfile
+.LdvmJitToInterpTraceSelectNoChain:
+ .word dvmJitToInterpTraceSelectNoChain
+.LdvmJitToInterpNoChain:
+ .word dvmJitToInterpNoChain
+.LdvmMterpStdBail:
+ .word dvmMterpStdBail
+.LdvmMterpCommonExceptionThrown:
+ .word dvmMterpCommonExceptionThrown
+.LdvmLockObject:
+ .word dvmLockObject
+#if defined(WITH_JIT_TUNING)
+.LdvmICHitCount:
+ .word gDvmICHitCount
+#endif
+#if defined(WITH_SELF_VERIFICATION)
+.LdvmSelfVerificationMemOpDecode:
+ .word dvmSelfVerificationMemOpDecode
+#endif
+.L__aeabi_cdcmple:
+ .word __aeabi_cdcmple
+.L__aeabi_cfcmple:
+ .word __aeabi_cfcmple
+
+ .global dmvCompilerTemplateEnd
+dmvCompilerTemplateEnd:
+
+#endif /* WITH_JIT */
+
diff --git a/vm/compiler/template/rebuild.sh b/vm/compiler/template/rebuild.sh
new file mode 100755
index 0000000..8c47dd7
--- /dev/null
+++ b/vm/compiler/template/rebuild.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Rebuild for all known targets. Necessary until the stuff in "out" gets
+# generated as part of the build.
+#
+set -e
+for arch in armv5te armv5te-vfp armv7-a armv7-a-neon; do TARGET_ARCH_EXT=$arch make -f Makefile-template; done
diff --git a/vm/hprof/Hprof.c b/vm/hprof/Hprof.c
new file mode 100644
index 0000000..bcad8b1
--- /dev/null
+++ b/vm/hprof/Hprof.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Preparation and completion of hprof data generation. The output is
+ * written into two files and then combined. This is necessary because
+ * we generate some of the data (strings and classes) while we dump the
+ * heap, and some analysis tools require that the class and string data
+ * appear first.
+ */
+#include "Hprof.h"
+
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <time.h>
+
+
+#define kHeadSuffix "-hptemp"
+
+hprof_context_t *
+hprofStartup(const char *outputFileName, int fd, bool directToDdms)
+{
+ hprofStartup_String();
+ hprofStartup_Class();
+#if WITH_HPROF_STACK
+ hprofStartup_StackFrame();
+ hprofStartup_Stack();
+#endif
+
+ hprof_context_t *ctx = malloc(sizeof(*ctx));
+ if (ctx == NULL) {
+ LOGE("hprof: can't allocate context.\n");
+ return NULL;
+ }
+
+ /* pass in name or descriptor of the output file */
+ hprofContextInit(ctx, strdup(outputFileName), fd, false, directToDdms);
+
+ assert(ctx->memFp != NULL);
+
+ return ctx;
+}
+
+/*
+ * Finish up the hprof dump. Returns true on success.
+ */
+bool
+hprofShutdown(hprof_context_t *tailCtx)
+{
+ /* flush the "tail" portion of the output */
+ hprofFlushCurrentRecord(tailCtx);
+
+ /*
+ * Create a new context struct for the start of the file. We
+ * heap-allocate it so we can share the "free" function.
+ */
+ hprof_context_t *headCtx = malloc(sizeof(*headCtx));
+ if (headCtx == NULL) {
+ LOGE("hprof: can't allocate context.\n");
+ hprofFreeContext(tailCtx);
+ return false;
+ }
+ hprofContextInit(headCtx, strdup(tailCtx->fileName), tailCtx->fd, true,
+ tailCtx->directToDdms);
+
+ LOGI("hprof: dumping heap strings to \"%s\".\n", tailCtx->fileName);
+ hprofDumpStrings(headCtx);
+ hprofDumpClasses(headCtx);
+
+ /* Write a dummy stack trace record so the analysis
+ * tools don't freak out.
+ */
+ hprofStartNewRecord(headCtx, HPROF_TAG_STACK_TRACE, HPROF_TIME);
+ hprofAddU4ToRecord(&headCtx->curRec, HPROF_NULL_STACK_TRACE);
+ hprofAddU4ToRecord(&headCtx->curRec, HPROF_NULL_THREAD);
+ hprofAddU4ToRecord(&headCtx->curRec, 0); // no frames
+
+#if WITH_HPROF_STACK
+ hprofDumpStackFrames(headCtx);
+ hprofDumpStacks(headCtx);
+#endif
+
+ hprofFlushCurrentRecord(headCtx);
+
+ hprofShutdown_Class();
+ hprofShutdown_String();
+#if WITH_HPROF_STACK
+ hprofShutdown_Stack();
+ hprofShutdown_StackFrame();
+#endif
+
+ /* flush to ensure memstream pointer and size are updated */
+ fflush(headCtx->memFp);
+ fflush(tailCtx->memFp);
+
+ if (tailCtx->directToDdms) {
+ /* send the data off to DDMS */
+ struct iovec iov[2];
+ iov[0].iov_base = headCtx->fileDataPtr;
+ iov[0].iov_len = headCtx->fileDataSize;
+ iov[1].iov_base = tailCtx->fileDataPtr;
+ iov[1].iov_len = tailCtx->fileDataSize;
+ dvmDbgDdmSendChunkV(CHUNK_TYPE("HPDS"), iov, 2);
+ } else {
+ /*
+ * Open the output file, and copy the head and tail to it.
+ */
+ assert(headCtx->fd == tailCtx->fd);
+
+ int outFd;
+ if (headCtx->fd >= 0) {
+ outFd = dup(headCtx->fd);
+ if (outFd < 0) {
+ LOGE("dup(%d) failed: %s\n", headCtx->fd, strerror(errno));
+ /* continue to fail-handler below */
+ }
+ } else {
+ outFd = open(tailCtx->fileName, O_WRONLY|O_CREAT, 0644);
+ if (outFd < 0) {
+ LOGE("can't open %s: %s\n", headCtx->fileName, strerror(errno));
+ /* continue to fail-handler below */
+ }
+ }
+ if (outFd < 0) {
+ hprofFreeContext(headCtx);
+ hprofFreeContext(tailCtx);
+ return false;
+ }
+
+ int result;
+ result = sysWriteFully(outFd, headCtx->fileDataPtr,
+ headCtx->fileDataSize, "hprof-head");
+ result |= sysWriteFully(outFd, tailCtx->fileDataPtr,
+ tailCtx->fileDataSize, "hprof-tail");
+ close(outFd);
+ if (result != 0) {
+ hprofFreeContext(headCtx);
+ hprofFreeContext(tailCtx);
+ return false;
+ }
+ }
+
+ /* throw out a log message for the benefit of "runhat" */
+ LOGI("hprof: heap dump completed (%dKB)\n",
+ (headCtx->fileDataSize + tailCtx->fileDataSize + 1023) / 1024);
+
+ hprofFreeContext(headCtx);
+ hprofFreeContext(tailCtx);
+
+ return true;
+}
+
+/*
+ * Free any heap-allocated items in "ctx", and then free "ctx" itself.
+ */
+void
+hprofFreeContext(hprof_context_t *ctx)
+{
+ assert(ctx != NULL);
+
+ /* we don't own ctx->fd, do not close */
+
+ if (ctx->memFp != NULL)
+ fclose(ctx->memFp);
+ free(ctx->curRec.body);
+ free(ctx->fileName);
+ free(ctx->fileDataPtr);
+ free(ctx);
+}
diff --git a/vm/hprof/Hprof.h b/vm/hprof/Hprof.h
new file mode 100644
index 0000000..18f4102
--- /dev/null
+++ b/vm/hprof/Hprof.h
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _DALVIK_HPROF_HPROF
+#define _DALVIK_HPROF_HPROF
+
+#include "Dalvik.h"
+
+#define HPROF_ID_SIZE (sizeof (u4))
+
+#define UNIQUE_ERROR() \
+ -((((uintptr_t)__func__) << 16 | __LINE__) & (0x7fffffff))
+
+#define HPROF_TIME 0
+#define HPROF_NULL_STACK_TRACE 0
+#define HPROF_NULL_THREAD 0
+
+typedef u4 hprof_id;
+typedef hprof_id hprof_string_id;
+typedef hprof_id hprof_object_id;
+typedef hprof_id hprof_class_object_id;
+#if WITH_HPROF_STACK
+typedef hprof_id hprof_stack_frame_id;
+#endif
+
+typedef enum hprof_basic_type {
+ hprof_basic_object = 2,
+ hprof_basic_boolean = 4,
+ hprof_basic_char = 5,
+ hprof_basic_float = 6,
+ hprof_basic_double = 7,
+ hprof_basic_byte = 8,
+ hprof_basic_short = 9,
+ hprof_basic_int = 10,
+ hprof_basic_long = 11,
+} hprof_basic_type;
+
+typedef enum hprof_tag_t {
+ HPROF_TAG_STRING = 0x01,
+ HPROF_TAG_LOAD_CLASS = 0x02,
+ HPROF_TAG_UNLOAD_CLASS = 0x03,
+ HPROF_TAG_STACK_FRAME = 0x04,
+ HPROF_TAG_STACK_TRACE = 0x05,
+ HPROF_TAG_ALLOC_SITES = 0x06,
+ HPROF_TAG_HEAP_SUMMARY = 0x07,
+ HPROF_TAG_START_THREAD = 0x0A,
+ HPROF_TAG_END_THREAD = 0x0B,
+ HPROF_TAG_HEAP_DUMP = 0x0C,
+ HPROF_TAG_HEAP_DUMP_SEGMENT = 0x1C,
+ HPROF_TAG_HEAP_DUMP_END = 0x2C,
+ HPROF_TAG_CPU_SAMPLES = 0x0D,
+ HPROF_TAG_CONTROL_SETTINGS = 0x0E,
+} hprof_tag_t;
+
+/* Values for the first byte of
+ * HEAP_DUMP and HEAP_DUMP_SEGMENT
+ * records:
+ */
+typedef enum hprof_heap_tag_t {
+ /* standard */
+ HPROF_ROOT_UNKNOWN = 0xFF,
+ HPROF_ROOT_JNI_GLOBAL = 0x01,
+ HPROF_ROOT_JNI_LOCAL = 0x02,
+ HPROF_ROOT_JAVA_FRAME = 0x03,
+ HPROF_ROOT_NATIVE_STACK = 0x04,
+ HPROF_ROOT_STICKY_CLASS = 0x05,
+ HPROF_ROOT_THREAD_BLOCK = 0x06,
+ HPROF_ROOT_MONITOR_USED = 0x07,
+ HPROF_ROOT_THREAD_OBJECT = 0x08,
+ HPROF_CLASS_DUMP = 0x20,
+ HPROF_INSTANCE_DUMP = 0x21,
+ HPROF_OBJECT_ARRAY_DUMP = 0x22,
+ HPROF_PRIMITIVE_ARRAY_DUMP = 0x23,
+
+ /* Android */
+ HPROF_HEAP_DUMP_INFO = 0xfe,
+ HPROF_ROOT_INTERNED_STRING = 0x89,
+ HPROF_ROOT_FINALIZING = 0x8a,
+ HPROF_ROOT_DEBUGGER = 0x8b,
+ HPROF_ROOT_REFERENCE_CLEANUP = 0x8c,
+ HPROF_ROOT_VM_INTERNAL = 0x8d,
+ HPROF_ROOT_JNI_MONITOR = 0x8e,
+ HPROF_UNREACHABLE = 0x90, /* deprecated */
+ HPROF_PRIMITIVE_ARRAY_NODATA_DUMP = 0xc3,
+} hprof_heap_tag_t;
+
+/* Represents a top-level hprof record, whose serialized
+ * format is:
+ *
+ * u1 TAG: denoting the type of the record
+ * u4 TIME: number of microseconds since the time stamp in the header
+ * u4 LENGTH: number of bytes that follow this u4 field
+ * and belong to this record
+ * [u1]* BODY: as many bytes as specified in the above u4 field
+ */
+typedef struct hprof_record_t {
+ unsigned char *body;
+ u4 time;
+ u4 length;
+ size_t allocLen;
+ u1 tag;
+ bool dirty;
+} hprof_record_t;
+
+typedef enum {
+ HPROF_HEAP_DEFAULT = 0,
+ HPROF_HEAP_ZYGOTE = 'Z',
+ HPROF_HEAP_APP = 'A'
+} HprofHeapId;
+
+typedef struct hprof_context_t {
+ /* curRec *must* be first so that we
+ * can cast from a context to a record.
+ */
+ hprof_record_t curRec;
+
+ u4 gcThreadSerialNumber;
+ u1 gcScanState;
+ HprofHeapId currentHeap; // which heap we're currently emitting
+ u4 stackTraceSerialNumber;
+ size_t objectsInSegment;
+
+ /*
+ * If directToDdms is set, "fileName" and "fd" will be ignored.
+ * Otherwise, "fileName" must be valid, though if "fd" >= 0 it will
+ * only be used for debug messages.
+ */
+ bool directToDdms;
+ char *fileName;
+ char *fileDataPtr; // for open_memstream
+ size_t fileDataSize; // for open_memstream
+ FILE *memFp;
+ int fd;
+} hprof_context_t;
+
+
+/*
+ * HprofString.c functions
+ */
+
+hprof_string_id hprofLookupStringId(const char *str);
+
+int hprofDumpStrings(hprof_context_t *ctx);
+
+int hprofStartup_String(void);
+int hprofShutdown_String(void);
+
+
+/*
+ * HprofClass.c functions
+ */
+
+hprof_class_object_id hprofLookupClassId(const ClassObject *clazz);
+
+int hprofDumpClasses(hprof_context_t *ctx);
+
+int hprofStartup_Class(void);
+int hprofShutdown_Class(void);
+
+
+/*
+ * HprofHeap.c functions
+ */
+
+int hprofStartHeapDump(hprof_context_t *ctx);
+int hprofFinishHeapDump(hprof_context_t *ctx);
+
+int hprofSetGcScanState(hprof_context_t *ctx,
+ hprof_heap_tag_t state, u4 threadSerialNumber);
+int hprofMarkRootObject(hprof_context_t *ctx,
+ const Object *obj, jobject jniObj);
+
+int hprofDumpHeapObject(hprof_context_t *ctx, const Object *obj);
+
+/*
+ * HprofOutput.c functions
+ */
+
+void hprofContextInit(hprof_context_t *ctx, char *fileName, int fd,
+ bool writeHeader, bool directToDdms);
+
+int hprofFlushRecord(hprof_record_t *rec, FILE *fp);
+int hprofFlushCurrentRecord(hprof_context_t *ctx);
+int hprofStartNewRecord(hprof_context_t *ctx, u1 tag, u4 time);
+
+int hprofAddU1ToRecord(hprof_record_t *rec, u1 value);
+int hprofAddU1ListToRecord(hprof_record_t *rec,
+ const u1 *values, size_t numValues);
+
+int hprofAddUtf8StringToRecord(hprof_record_t *rec, const char *str);
+
+int hprofAddU2ToRecord(hprof_record_t *rec, u2 value);
+int hprofAddU2ListToRecord(hprof_record_t *rec,
+ const u2 *values, size_t numValues);
+
+int hprofAddU4ToRecord(hprof_record_t *rec, u4 value);
+int hprofAddU4ListToRecord(hprof_record_t *rec,
+ const u4 *values, size_t numValues);
+
+int hprofAddU8ToRecord(hprof_record_t *rec, u8 value);
+int hprofAddU8ListToRecord(hprof_record_t *rec,
+ const u8 *values, size_t numValues);
+
+#define hprofAddIdToRecord(rec, id) hprofAddU4ToRecord((rec), (u4)(id))
+#define hprofAddIdListToRecord(rec, values, numValues) \
+ hprofAddU4ListToRecord((rec), (const u4 *)(values), (numValues))
+
+#if WITH_HPROF_STACK
+
+/*
+ * HprofStack.c functions
+ */
+
+void hprofFillInStackTrace(void *objectPtr);
+
+int hprofDumpStacks(hprof_context_t *ctx);
+
+int hprofStartup_Stack(void);
+int hprofShutdown_Stack(void);
+
+/*
+ * HprofStackFrame.c functions
+ */
+
+int hprofDumpStackFrames(hprof_context_t *ctx);
+
+int hprofStartup_StackFrame(void);
+int hprofShutdown_StackFrame(void);
+
+#endif
+
+/*
+ * Hprof.c functions
+ */
+
+hprof_context_t* hprofStartup(const char *outputFileName, int fd,
+ bool directToDdms);
+bool hprofShutdown(hprof_context_t *ctx);
+void hprofFreeContext(hprof_context_t *ctx);
+
+/*
+ * Heap.c functions
+ *
+ * The contents of the hprof directory have no knowledge of
+ * the heap implementation; these functions require heap knowledge,
+ * so they are implemented in Heap.c.
+ */
+int hprofDumpHeap(const char* fileName, int fd, bool directToDdms);
+void dvmHeapSetHprofGcScanState(hprof_heap_tag_t state, u4 threadSerialNumber);
+
+#endif // _DALVIK_HPROF_HPROF
diff --git a/vm/hprof/HprofClass.c b/vm/hprof/HprofClass.c
new file mode 100644
index 0000000..f76f159
--- /dev/null
+++ b/vm/hprof/HprofClass.c
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Class object pool
+ */
+
+#include "Hprof.h"
+
+static HashTable *gClassHashTable;
+
+int
+hprofStartup_Class()
+{
+ gClassHashTable = dvmHashTableCreate(128, NULL);
+ if (gClassHashTable == NULL) {
+ return UNIQUE_ERROR();
+ }
+ return 0;
+}
+
+int
+hprofShutdown_Class()
+{
+ dvmHashTableFree(gClassHashTable);
+
+ return 0;
+}
+
+static u4
+computeClassHash(const ClassObject *clazz)
+{
+ u4 hash;
+ const char *cp;
+ char c;
+
+ cp = clazz->descriptor;
+ hash = (u4)clazz->classLoader;
+ while ((c = *cp++) != '\0') {
+ hash = hash * 31 + c;
+ }
+
+ return hash;
+}
+
+static int
+classCmp(const void *v1, const void *v2)
+{
+ const ClassObject *c1 = (const ClassObject *)v1;
+ const ClassObject *c2 = (const ClassObject *)v2;
+ intptr_t diff;
+
+ diff = (uintptr_t)c1->classLoader - (uintptr_t)c2->classLoader;
+ if (diff == 0) {
+ return strcmp(c1->descriptor, c2->descriptor);
+ }
+ return diff;
+}
+
+static int
+getPrettyClassNameId(const char *descriptor)
+{
+ hprof_string_id classNameId;
+ char *dotName = dvmDescriptorToDot(descriptor);
+
+ /* Hprof suggests that array class names be converted from, e.g.,
+ * "[[[I" to "int[][][]" and "[Lorg.blort.Spaz;" to
+ * "org.blort.Spaz[]".
+ */
+ if (dotName[0] == '[') {
+ const char *c;
+ char *newName;
+ char *nc;
+ size_t dim;
+ size_t newLen;
+
+ c = dotName;
+ dim = 0;
+ while (*c == '[') {
+ dim++;
+ c++;
+ }
+ if (*c == 'L') {
+ c++;
+ } else {
+ /* It's a primitive type; we should use a pretty name.
+ * Add semicolons to make all strings have the format
+ * of object class names.
+ */
+ switch (*c) {
+ case 'Z': c = "boolean;"; break;
+ case 'C': c = "char;"; break;
+ case 'F': c = "float;"; break;
+ case 'D': c = "double;"; break;
+ case 'B': c = "byte;"; break;
+ case 'S': c = "short;"; break;
+ case 'I': c = "int;"; break;
+ case 'J': c = "long;"; break;
+ default: assert(false); c = "UNKNOWN;"; break;
+ }
+ }
+
+ /* We have a string of the form "name;" and
+ * we want to replace the semicolon with as many
+ * "[]" pairs as is in dim.
+ */
+ newLen = strlen(c)-1 + dim*2;
+ newName = malloc(newLen + 1);
+ if (newName == NULL) {
+ return -1;
+ }
+ strcpy(newName, c);
+ newName[newLen] = '\0';
+
+ /* Point nc to the semicolon.
+ */
+ nc = newName + newLen - dim*2;
+ assert(*nc == ';');
+
+ while (dim--) {
+ *nc++ = '[';
+ *nc++ = ']';
+ }
+ assert(*nc == '\0');
+
+ classNameId = hprofLookupStringId(newName);
+ free(newName);
+ } else {
+ classNameId = hprofLookupStringId(dotName);
+ }
+
+ free(dotName);
+ return classNameId;
+}
+
+
+hprof_class_object_id
+hprofLookupClassId(const ClassObject *clazz)
+{
+ void *val;
+
+ if (clazz == NULL) {
+ /* Someone's probably looking up the superclass
+ * of java.lang.Object or of a primitive class.
+ */
+ return (hprof_class_object_id)0;
+ }
+
+ dvmHashTableLock(gClassHashTable);
+
+ /* We're using the hash table as a list.
+ * TODO: replace the hash table with a more suitable structure
+ */
+ val = dvmHashTableLookup(gClassHashTable, computeClassHash(clazz),
+ (void *)clazz, classCmp, true);
+ assert(val != NULL);
+
+ dvmHashTableUnlock(gClassHashTable);
+
+ /* Make sure that the class's name is in the string table.
+ * This is a bunch of extra work that we only have to do
+ * because of the order of tables in the output file
+ * (strings need to be dumped before classes).
+ */
+ getPrettyClassNameId(clazz->descriptor);
+
+ return (hprof_class_object_id)clazz;
+}
+
+int
+hprofDumpClasses(hprof_context_t *ctx)
+{
+ HashIter iter;
+ hprof_record_t *rec = &ctx->curRec;
+ int err;
+
+ dvmHashTableLock(gClassHashTable);
+
+ for (err = 0, dvmHashIterBegin(gClassHashTable, &iter);
+ err == 0 && !dvmHashIterDone(&iter);
+ dvmHashIterNext(&iter))
+ {
+ err = hprofStartNewRecord(ctx, HPROF_TAG_LOAD_CLASS, HPROF_TIME);
+ if (err == 0) {
+ const ClassObject *clazz;
+
+ clazz = (const ClassObject *)dvmHashIterData(&iter);
+ assert(clazz != NULL);
+
+ /* LOAD CLASS format:
+ *
+ * u4: class serial number (always > 0)
+ * ID: class object ID
+ * u4: stack trace serial number
+ * ID: class name string ID
+ *
+ * We use the address of the class object structure as its ID.
+ */
+ hprofAddU4ToRecord(rec, clazz->serialNumber);
+ hprofAddIdToRecord(rec, (hprof_class_object_id)clazz);
+ hprofAddU4ToRecord(rec, HPROF_NULL_STACK_TRACE);
+ hprofAddIdToRecord(rec, getPrettyClassNameId(clazz->descriptor));
+ }
+ }
+
+ dvmHashTableUnlock(gClassHashTable);
+
+ return err;
+}
diff --git a/vm/hprof/HprofHeap.c b/vm/hprof/HprofHeap.c
new file mode 100644
index 0000000..75a1d2b
--- /dev/null
+++ b/vm/hprof/HprofHeap.c
@@ -0,0 +1,494 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Heap object dump
+ */
+#include "Hprof.h"
+
+#include "alloc/HeapInternal.h"
+#include "alloc/HeapSource.h"
+
+/* Set DUMP_PRIM_DATA to 1 if you want to include the contents
+ * of primitive arrays (byte arrays, character arrays, etc.)
+ * in heap dumps. This can be a large amount of data.
+ */
+#define DUMP_PRIM_DATA 1
+
+#define OBJECTS_PER_SEGMENT ((size_t)128)
+#define BYTES_PER_SEGMENT ((size_t)4096)
+
+/* The static field-name for the synthetic object generated to account
+ * for class Static overhead.
+ */
+#define STATIC_OVERHEAD_NAME "$staticOverhead"
+/* The ID for the synthetic object generated to account for class
+ * Static overhead.
+ */
+#define CLASS_STATICS_ID(clazz) ((hprof_object_id)(((u4)(clazz)) | 1))
+
+int
+hprofStartHeapDump(hprof_context_t *ctx)
+{
+ UNUSED_PARAMETER(ctx);
+
+ ctx->objectsInSegment = OBJECTS_PER_SEGMENT;
+ ctx->currentHeap = HPROF_HEAP_DEFAULT;
+ return 0;
+}
+
+int
+hprofFinishHeapDump(hprof_context_t *ctx)
+{
+ return hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_END, HPROF_TIME);
+}
+
+int
+hprofSetGcScanState(hprof_context_t *ctx,
+ hprof_heap_tag_t state, u4 threadSerialNumber)
+{
+ /* Used by hprofMarkRootObject()
+ */
+ ctx->gcScanState = state;
+ ctx->gcThreadSerialNumber = threadSerialNumber;
+ return 0;
+}
+
+static hprof_basic_type
+signatureToBasicTypeAndSize(const char *sig, size_t *sizeOut)
+{
+ char c = sig[0];
+ hprof_basic_type ret;
+ size_t size;
+
+ switch (c) {
+ case '[':
+ case 'L': ret = hprof_basic_object; size = 4; break;
+ case 'Z': ret = hprof_basic_boolean; size = 1; break;
+ case 'C': ret = hprof_basic_char; size = 2; break;
+ case 'F': ret = hprof_basic_float; size = 4; break;
+ case 'D': ret = hprof_basic_double; size = 8; break;
+ case 'B': ret = hprof_basic_byte; size = 1; break;
+ case 'S': ret = hprof_basic_short; size = 2; break;
+ default: assert(false);
+ case 'I': ret = hprof_basic_int; size = 4; break;
+ case 'J': ret = hprof_basic_long; size = 8; break;
+ }
+
+ if (sizeOut != NULL) {
+ *sizeOut = size;
+ }
+
+ return ret;
+}
+
+static hprof_basic_type
+primitiveToBasicTypeAndSize(PrimitiveType prim, size_t *sizeOut)
+{
+ hprof_basic_type ret;
+ size_t size;
+
+ switch (prim) {
+ case PRIM_BOOLEAN: ret = hprof_basic_boolean; size = 1; break;
+ case PRIM_CHAR: ret = hprof_basic_char; size = 2; break;
+ case PRIM_FLOAT: ret = hprof_basic_float; size = 4; break;
+ case PRIM_DOUBLE: ret = hprof_basic_double; size = 8; break;
+ case PRIM_BYTE: ret = hprof_basic_byte; size = 1; break;
+ case PRIM_SHORT: ret = hprof_basic_short; size = 2; break;
+ default: assert(false);
+ case PRIM_INT: ret = hprof_basic_int; size = 4; break;
+ case PRIM_LONG: ret = hprof_basic_long; size = 8; break;
+ }
+
+ if (sizeOut != NULL) {
+ *sizeOut = size;
+ }
+
+ return ret;
+}
+
+/* Always called when marking objects, but only does
+ * something when ctx->gcScanState is non-zero, which is usually
+ * only true when marking the root set or unreachable
+ * objects. Used to add rootset references to obj.
+ */
+int
+hprofMarkRootObject(hprof_context_t *ctx, const Object *obj, jobject jniObj)
+{
+ hprof_record_t *rec = &ctx->curRec;
+ int err;
+ hprof_heap_tag_t heapTag = ctx->gcScanState;
+
+ if (heapTag == 0) {
+ return 0;
+ }
+
+ if (ctx->objectsInSegment >= OBJECTS_PER_SEGMENT ||
+ rec->length >= BYTES_PER_SEGMENT)
+ {
+ /* This flushes the old segment and starts a new one.
+ */
+ hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
+ ctx->objectsInSegment = 0;
+ }
+
+ switch (heapTag) {
+ /* ID: object ID
+ */
+ case HPROF_ROOT_UNKNOWN:
+ case HPROF_ROOT_STICKY_CLASS:
+ case HPROF_ROOT_MONITOR_USED:
+ case HPROF_ROOT_INTERNED_STRING:
+ case HPROF_ROOT_FINALIZING:
+ case HPROF_ROOT_DEBUGGER:
+ case HPROF_ROOT_REFERENCE_CLEANUP:
+ case HPROF_ROOT_VM_INTERNAL:
+ hprofAddU1ToRecord(rec, heapTag);
+ hprofAddIdToRecord(rec, (hprof_object_id)obj);
+ break;
+
+ /* ID: object ID
+ * ID: JNI global ref ID
+ */
+ case HPROF_ROOT_JNI_GLOBAL:
+ hprofAddU1ToRecord(rec, heapTag);
+ hprofAddIdToRecord(rec, (hprof_object_id)obj);
+ hprofAddIdToRecord(rec, (hprof_id)jniObj);
+ break;
+
+ /* ID: object ID
+ * u4: thread serial number
+ * u4: frame number in stack trace (-1 for empty)
+ */
+ case HPROF_ROOT_JNI_LOCAL:
+ case HPROF_ROOT_JNI_MONITOR:
+ case HPROF_ROOT_JAVA_FRAME:
+ hprofAddU1ToRecord(rec, heapTag);
+ hprofAddIdToRecord(rec, (hprof_object_id)obj);
+ hprofAddU4ToRecord(rec, ctx->gcThreadSerialNumber);
+ hprofAddU4ToRecord(rec, (u4)-1);
+ break;
+
+ /* ID: object ID
+ * u4: thread serial number
+ */
+ case HPROF_ROOT_NATIVE_STACK:
+ case HPROF_ROOT_THREAD_BLOCK:
+ hprofAddU1ToRecord(rec, heapTag);
+ hprofAddIdToRecord(rec, (hprof_object_id)obj);
+ hprofAddU4ToRecord(rec, ctx->gcThreadSerialNumber);
+ break;
+
+ /* ID: thread object ID
+ * u4: thread serial number
+ * u4: stack trace serial number
+ */
+ case HPROF_ROOT_THREAD_OBJECT:
+ hprofAddU1ToRecord(rec, heapTag);
+ hprofAddIdToRecord(rec, (hprof_object_id)obj);
+ hprofAddU4ToRecord(rec, ctx->gcThreadSerialNumber);
+ hprofAddU4ToRecord(rec, (u4)-1); //xxx
+ break;
+
+ default:
+ err = 0;
+ break;
+ }
+
+ ctx->objectsInSegment++;
+
+ return err;
+}
+
+static int
+stackTraceSerialNumber(const void *obj)
+
+{
+#if WITH_HPROF_STACK
+ DvmHeapChunk *chunk = ptr2chunk(obj);
+ return chunk->stackTraceSerialNumber;
+#else
+ return HPROF_NULL_STACK_TRACE;
+#endif
+}
+
+int
+hprofDumpHeapObject(hprof_context_t *ctx, const Object *obj)
+{
+ const ClassObject *clazz;
+ hprof_record_t *rec = &ctx->curRec;
+ HprofHeapId desiredHeap;
+
+ desiredHeap =
+ dvmHeapSourceGetPtrFlag(obj, HS_ALLOCATED_IN_ZYGOTE) ?
+ HPROF_HEAP_ZYGOTE : HPROF_HEAP_APP;
+
+ if (ctx->objectsInSegment >= OBJECTS_PER_SEGMENT ||
+ rec->length >= BYTES_PER_SEGMENT)
+ {
+ /* This flushes the old segment and starts a new one.
+ */
+ hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
+ ctx->objectsInSegment = 0;
+
+ /* Starting a new HEAP_DUMP resets the heap to default.
+ */
+ ctx->currentHeap = HPROF_HEAP_DEFAULT;
+ }
+
+ if (desiredHeap != ctx->currentHeap) {
+ hprof_string_id nameId;
+
+ /* This object is in a different heap than the current one.
+ * Emit a HEAP_DUMP_INFO tag to change heaps.
+ */
+ hprofAddU1ToRecord(rec, HPROF_HEAP_DUMP_INFO);
+ hprofAddU4ToRecord(rec, (u4)desiredHeap); // u4: heap id
+ switch (desiredHeap) {
+ case HPROF_HEAP_APP:
+ nameId = hprofLookupStringId("app");
+ break;
+ case HPROF_HEAP_ZYGOTE:
+ nameId = hprofLookupStringId("zygote");
+ break;
+ default:
+ /* Internal error. */
+ assert(!"Unexpected desiredHeap");
+ nameId = hprofLookupStringId("<ILLEGAL>");
+ break;
+ }
+ hprofAddIdToRecord(rec, nameId);
+ ctx->currentHeap = desiredHeap;
+ }
+
+ clazz = obj->clazz;
+
+ if (clazz == NULL) {
+ /* This object will bother HprofReader, because it has a NULL
+ * class, so just don't dump it. It could be
+ * gDvm.unlinkedJavaLangClass or it could be an object just
+ * allocated which hasn't been initialized yet.
+ */
+ } else {
+ hprof_class_object_id clazzId;
+
+ clazzId = hprofLookupClassId(clazz);
+
+ if (clazz == gDvm.classJavaLangClass) {
+ const ClassObject *thisClass = (const ClassObject *)obj;
+ int i, sFieldCount, iFieldCount;
+ /* obj is a ClassObject.
+ */
+ sFieldCount = thisClass->sfieldCount;
+ if (sFieldCount != 0) {
+ int byteLength = sFieldCount*sizeof(StaticField);
+ /* Create a byte array to reflect the allocation of the
+ * StaticField array at the end of this class.
+ */
+ hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_DUMP);
+ hprofAddIdToRecord(rec, CLASS_STATICS_ID(obj));
+ hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
+ hprofAddU4ToRecord(rec, byteLength);
+ hprofAddU1ToRecord(rec, hprof_basic_byte);
+ for (i = 0; i < byteLength; i++) {
+ hprofAddU1ToRecord(rec, 0);
+ }
+ }
+
+ hprofAddU1ToRecord(rec, HPROF_CLASS_DUMP);
+ hprofAddIdToRecord(rec, hprofLookupClassId(thisClass));
+ hprofAddU4ToRecord(rec, stackTraceSerialNumber(thisClass));
+ hprofAddIdToRecord(rec, hprofLookupClassId(thisClass->super));
+ hprofAddIdToRecord(rec, (hprof_object_id)thisClass->classLoader);
+ hprofAddIdToRecord(rec, (hprof_object_id)0); // no signer
+ hprofAddIdToRecord(rec, (hprof_object_id)0); // no prot domain
+ hprofAddIdToRecord(rec, (hprof_id)0); // reserved
+ hprofAddIdToRecord(rec, (hprof_id)0); // reserved
+ if (obj == (Object *)gDvm.classJavaLangClass) {
+ // ClassObjects have their static fields appended, so
+ // aren't all the same size. But they're at least this
+ // size.
+ hprofAddU4ToRecord(rec, sizeof(ClassObject)); // instance size
+ } else {
+ hprofAddU4ToRecord(rec, thisClass->objectSize); // instance size
+ }
+
+ hprofAddU2ToRecord(rec, 0); // empty const pool
+
+ /* Static fields
+ */
+ if (sFieldCount == 0) {
+ hprofAddU2ToRecord(rec, (u2)0);
+ } else {
+ hprofAddU2ToRecord(rec, (u2)(sFieldCount+1));
+ hprofAddIdToRecord(rec,
+ hprofLookupStringId(STATIC_OVERHEAD_NAME));
+ hprofAddU1ToRecord(rec, hprof_basic_object);
+ hprofAddIdToRecord(rec, CLASS_STATICS_ID(obj));
+ for (i = 0; i < sFieldCount; i++) {
+ hprof_basic_type t;
+ size_t size;
+ const StaticField *f = &thisClass->sfields[i];
+
+ t = signatureToBasicTypeAndSize(f->field.signature, &size);
+ hprofAddIdToRecord(rec, hprofLookupStringId(f->field.name));
+ hprofAddU1ToRecord(rec, t);
+ if (size == 1) {
+ hprofAddU1ToRecord(rec, (u1)f->value.b);
+ } else if (size == 2) {
+ hprofAddU2ToRecord(rec, (u2)f->value.c);
+ } else if (size == 4) {
+ hprofAddU4ToRecord(rec, (u4)f->value.i);
+ } else if (size == 8) {
+ hprofAddU8ToRecord(rec, (u8)f->value.j);
+ } else {
+ assert(false);
+ }
+ }
+ }
+
+ /* Instance fields for this class (no superclass fields)
+ */
+ iFieldCount = thisClass->ifieldCount;
+ hprofAddU2ToRecord(rec, (u2)iFieldCount);
+ for (i = 0; i < iFieldCount; i++) {
+ const InstField *f = &thisClass->ifields[i];
+ hprof_basic_type t;
+
+ t = signatureToBasicTypeAndSize(f->field.signature, NULL);
+ hprofAddIdToRecord(rec, hprofLookupStringId(f->field.name));
+ hprofAddU1ToRecord(rec, t);
+ }
+ } else if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) {
+ const ArrayObject *aobj = (const ArrayObject *)obj;
+ u4 length = aobj->length;
+
+ if (IS_CLASS_FLAG_SET(clazz, CLASS_ISOBJECTARRAY)) {
+ /* obj is an object array.
+ */
+ hprofAddU1ToRecord(rec, HPROF_OBJECT_ARRAY_DUMP);
+
+ hprofAddIdToRecord(rec, (hprof_object_id)obj);
+ hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
+ hprofAddU4ToRecord(rec, length);
+ hprofAddIdToRecord(rec, hprofLookupClassId(clazz));
+
+ /* Dump the elements, which are always objects or NULL.
+ */
+ hprofAddIdListToRecord(rec,
+ (const hprof_object_id *)aobj->contents, length);
+ } else {
+ hprof_basic_type t;
+ size_t size;
+
+ t = primitiveToBasicTypeAndSize(clazz->elementClass->
+ primitiveType, &size);
+
+ /* obj is a primitive array.
+ */
+#if DUMP_PRIM_DATA
+ hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_DUMP);
+#else
+ hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_NODATA_DUMP);
+#endif
+
+ hprofAddIdToRecord(rec, (hprof_object_id)obj);
+ hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
+ hprofAddU4ToRecord(rec, length);
+ hprofAddU1ToRecord(rec, t);
+
+#if DUMP_PRIM_DATA
+ /* Dump the raw, packed element values.
+ */
+ if (size == 1) {
+ hprofAddU1ListToRecord(rec, (const u1 *)aobj->contents,
+ length);
+ } else if (size == 2) {
+ hprofAddU2ListToRecord(rec, (const u2 *)aobj->contents,
+ length);
+ } else if (size == 4) {
+ hprofAddU4ListToRecord(rec, (const u4 *)aobj->contents,
+ length);
+ } else if (size == 8) {
+ hprofAddU8ListToRecord(rec, (const u8 *)aobj->contents,
+ length);
+ }
+#endif
+ }
+ } else {
+ const ClassObject *sclass;
+ size_t sizePatchOffset, savedLen;
+
+ /* obj is an instance object.
+ */
+ hprofAddU1ToRecord(rec, HPROF_INSTANCE_DUMP);
+ hprofAddIdToRecord(rec, (hprof_object_id)obj);
+ hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
+ hprofAddIdToRecord(rec, hprofLookupClassId(clazz));
+
+ /* Reserve some space for the length of the instance
+ * data, which we won't know until we're done writing
+ * it.
+ */
+ sizePatchOffset = rec->length;
+ hprofAddU4ToRecord(rec, 0x77777777);
+
+ /* Write the instance data; fields for this
+ * class, followed by super class fields, and so on.
+ */
+ sclass = clazz;
+ while (sclass != NULL) {
+ int i, ifieldCount;
+
+ ifieldCount = sclass->ifieldCount;
+ for (i = 0; i < ifieldCount; i++) {
+ const InstField *f = &sclass->ifields[i];
+ hprof_basic_type t;
+ size_t size;
+
+ t = signatureToBasicTypeAndSize(f->field.signature, &size);
+ if (size == 1) {
+ hprofAddU1ToRecord(rec,
+ (u1)dvmGetFieldByte(obj, f->byteOffset));
+ } else if (size == 2) {
+ hprofAddU2ToRecord(rec,
+ (u2)dvmGetFieldChar(obj, f->byteOffset));
+ } else if (size == 4) {
+ hprofAddU4ToRecord(rec,
+ (u4)dvmGetFieldInt(obj, f->byteOffset));
+ } else if (size == 8) {
+ hprofAddU8ToRecord(rec,
+ (u8)dvmGetFieldLong(obj, f->byteOffset));
+ } else {
+ assert(false);
+ }
+ }
+
+ sclass = sclass->super;
+ }
+
+ /* Patch the instance field length.
+ */
+ savedLen = rec->length;
+ rec->length = sizePatchOffset;
+ hprofAddU4ToRecord(rec, savedLen - (sizePatchOffset + 4));
+ rec->length = savedLen;
+ }
+ }
+
+ ctx->objectsInSegment++;
+
+ return 0;
+}
diff --git a/vm/hprof/HprofOutput.c b/vm/hprof/HprofOutput.c
new file mode 100644
index 0000000..25512b2
--- /dev/null
+++ b/vm/hprof/HprofOutput.c
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <sys/time.h>
+#include <cutils/open_memstream.h>
+#include <time.h>
+#include <errno.h>
+#include "Hprof.h"
+
+#define HPROF_MAGIC_STRING "JAVA PROFILE 1.0.3"
+
+#define U2_TO_BUF_BE(buf, offset, value) \
+ do { \
+ unsigned char *buf_ = (unsigned char *)(buf); \
+ int offset_ = (int)(offset); \
+ u2 value_ = (u2)(value); \
+ buf_[offset_ + 0] = (unsigned char)(value_ >> 8); \
+ buf_[offset_ + 1] = (unsigned char)(value_ ); \
+ } while (0)
+
+#define U4_TO_BUF_BE(buf, offset, value) \
+ do { \
+ unsigned char *buf_ = (unsigned char *)(buf); \
+ int offset_ = (int)(offset); \
+ u4 value_ = (u4)(value); \
+ buf_[offset_ + 0] = (unsigned char)(value_ >> 24); \
+ buf_[offset_ + 1] = (unsigned char)(value_ >> 16); \
+ buf_[offset_ + 2] = (unsigned char)(value_ >> 8); \
+ buf_[offset_ + 3] = (unsigned char)(value_ ); \
+ } while (0)
+
+#define U8_TO_BUF_BE(buf, offset, value) \
+ do { \
+ unsigned char *buf_ = (unsigned char *)(buf); \
+ int offset_ = (int)(offset); \
+ u8 value_ = (u8)(value); \
+ buf_[offset_ + 0] = (unsigned char)(value_ >> 56); \
+ buf_[offset_ + 1] = (unsigned char)(value_ >> 48); \
+ buf_[offset_ + 2] = (unsigned char)(value_ >> 40); \
+ buf_[offset_ + 3] = (unsigned char)(value_ >> 32); \
+ buf_[offset_ + 4] = (unsigned char)(value_ >> 24); \
+ buf_[offset_ + 5] = (unsigned char)(value_ >> 16); \
+ buf_[offset_ + 6] = (unsigned char)(value_ >> 8); \
+ buf_[offset_ + 7] = (unsigned char)(value_ ); \
+ } while (0)
+
+/*
+ * Initialize an hprof context struct.
+ *
+ * This will take ownership of "fileName".
+ */
+void
+hprofContextInit(hprof_context_t *ctx, char *fileName, int fd,
+ bool writeHeader, bool directToDdms)
+{
+ memset(ctx, 0, sizeof (*ctx));
+
+ /*
+ * Have to do this here, because it must happen after we
+ * memset the struct (want to treat fileDataPtr/fileDataSize
+ * as read-only while the file is open).
+ */
+ FILE* fp = open_memstream(&ctx->fileDataPtr, &ctx->fileDataSize);
+ if (fp == NULL) {
+ /* not expected */
+ LOGE("hprof: open_memstream failed: %s\n", strerror(errno));
+ dvmAbort();
+ }
+
+ ctx->directToDdms = directToDdms;
+ ctx->fileName = fileName;
+ ctx->memFp = fp;
+ ctx->fd = fd;
+
+ ctx->curRec.allocLen = 128;
+ ctx->curRec.body = malloc(ctx->curRec.allocLen);
+//xxx check for/return an error
+
+ if (writeHeader) {
+ char magic[] = HPROF_MAGIC_STRING;
+ unsigned char buf[4];
+ struct timeval now;
+ u8 nowMs;
+
+ /* Write the file header.
+ *
+ * [u1]*: NUL-terminated magic string.
+ */
+ fwrite(magic, 1, sizeof(magic), fp);
+
+ /* u4: size of identifiers. We're using addresses
+ * as IDs, so make sure a pointer fits.
+ */
+ U4_TO_BUF_BE(buf, 0, sizeof(void *));
+ fwrite(buf, 1, sizeof(u4), fp);
+
+ /* The current time, in milliseconds since 0:00 GMT, 1/1/70.
+ */
+ if (gettimeofday(&now, NULL) < 0) {
+ nowMs = 0;
+ } else {
+ nowMs = (u8)now.tv_sec * 1000 + now.tv_usec / 1000;
+ }
+
+ /* u4: high word of the 64-bit time.
+ */
+ U4_TO_BUF_BE(buf, 0, (u4)(nowMs >> 32));
+ fwrite(buf, 1, sizeof(u4), fp);
+
+ /* u4: low word of the 64-bit time.
+ */
+ U4_TO_BUF_BE(buf, 0, (u4)(nowMs & 0xffffffffULL));
+ fwrite(buf, 1, sizeof(u4), fp); //xxx fix the time
+ }
+}
+
+int
+hprofFlushRecord(hprof_record_t *rec, FILE *fp)
+{
+ if (rec->dirty) {
+ unsigned char headBuf[sizeof (u1) + 2 * sizeof (u4)];
+ int nb;
+
+ headBuf[0] = rec->tag;
+ U4_TO_BUF_BE(headBuf, 1, rec->time);
+ U4_TO_BUF_BE(headBuf, 5, rec->length);
+
+ nb = fwrite(headBuf, 1, sizeof(headBuf), fp);
+ if (nb != sizeof(headBuf)) {
+ return UNIQUE_ERROR();
+ }
+ nb = fwrite(rec->body, 1, rec->length, fp);
+ if (nb != (int)rec->length) {
+ return UNIQUE_ERROR();
+ }
+
+ rec->dirty = false;
+ }
+//xxx if we used less than half (or whatever) of allocLen, shrink the buffer.
+
+ return 0;
+}
+
+int
+hprofFlushCurrentRecord(hprof_context_t *ctx)
+{
+ return hprofFlushRecord(&ctx->curRec, ctx->memFp);
+}
+
+int
+hprofStartNewRecord(hprof_context_t *ctx, u1 tag, u4 time)
+{
+ hprof_record_t *rec = &ctx->curRec;
+ int err;
+
+ err = hprofFlushRecord(rec, ctx->memFp);
+ if (err != 0) {
+ return err;
+ } else if (rec->dirty) {
+ return UNIQUE_ERROR();
+ }
+
+ rec->dirty = true;
+ rec->tag = tag;
+ rec->time = time;
+ rec->length = 0;
+
+ return 0;
+}
+
+static inline int
+guaranteeRecordAppend(hprof_record_t *rec, size_t nmore)
+{
+ size_t minSize;
+
+ minSize = rec->length + nmore;
+ if (minSize > rec->allocLen) {
+ unsigned char *newBody;
+ size_t newAllocLen;
+
+ newAllocLen = rec->allocLen * 2;
+ if (newAllocLen < minSize) {
+ newAllocLen = rec->allocLen + nmore + nmore/2;
+ }
+ newBody = realloc(rec->body, newAllocLen);
+ if (newBody != NULL) {
+ rec->body = newBody;
+ rec->allocLen = newAllocLen;
+ } else {
+//TODO: set an error flag so future ops will fail
+ return UNIQUE_ERROR();
+ }
+ }
+
+ assert(rec->length + nmore <= rec->allocLen);
+ return 0;
+}
+
+int
+hprofAddU1ListToRecord(hprof_record_t *rec, const u1 *values, size_t numValues)
+{
+ int err;
+
+ err = guaranteeRecordAppend(rec, numValues);
+ if (err != 0) {
+ return err;
+ }
+
+ memcpy(rec->body + rec->length, values, numValues);
+ rec->length += numValues;
+
+ return 0;
+}
+
+int
+hprofAddU1ToRecord(hprof_record_t *rec, u1 value)
+{
+ int err;
+
+ err = guaranteeRecordAppend(rec, 1);
+ if (err != 0) {
+ return err;
+ }
+
+ rec->body[rec->length++] = value;
+
+ return 0;
+}
+
+int
+hprofAddUtf8StringToRecord(hprof_record_t *rec, const char *str)
+{
+ /* The terminating NUL character is NOT written.
+ */
+//xxx don't do a strlen; add and grow as necessary, until NUL
+ return hprofAddU1ListToRecord(rec, (const u1 *)str, strlen(str));
+}
+
+int
+hprofAddU2ListToRecord(hprof_record_t *rec, const u2 *values, size_t numValues)
+{
+ unsigned char *insert;
+ size_t i;
+ int err;
+
+ err = guaranteeRecordAppend(rec, numValues * 2);
+ if (err != 0) {
+ return err;
+ }
+
+//xxx this can be way smarter
+//xxx also, don't do this bytewise if aligned and on a matching-endian arch
+ insert = rec->body + rec->length;
+ for (i = 0; i < numValues; i++) {
+ U2_TO_BUF_BE(insert, 0, *values++);
+ insert += sizeof(*values);
+ }
+ rec->length += numValues * 2;
+
+ return 0;
+}
+
+int
+hprofAddU2ToRecord(hprof_record_t *rec, u2 value)
+{
+ return hprofAddU2ListToRecord(rec, &value, 1);
+}
+
+int
+hprofAddU4ListToRecord(hprof_record_t *rec, const u4 *values, size_t numValues)
+{
+ unsigned char *insert;
+ size_t i;
+ int err;
+
+ err = guaranteeRecordAppend(rec, numValues * 4);
+ if (err != 0) {
+ return err;
+ }
+
+//xxx this can be way smarter
+//xxx also, don't do this bytewise if aligned and on a matching-endian arch
+ insert = rec->body + rec->length;
+ for (i = 0; i < numValues; i++) {
+ U4_TO_BUF_BE(insert, 0, *values++);
+ insert += sizeof(*values);
+ }
+ rec->length += numValues * 4;
+
+ return 0;
+}
+
+int
+hprofAddU4ToRecord(hprof_record_t *rec, u4 value)
+{
+ return hprofAddU4ListToRecord(rec, &value, 1);
+}
+
+int
+hprofAddU8ListToRecord(hprof_record_t *rec, const u8 *values, size_t numValues)
+{
+ unsigned char *insert;
+ size_t i;
+ int err;
+
+ err = guaranteeRecordAppend(rec, numValues * 8);
+ if (err != 0) {
+ return err;
+ }
+
+//xxx this can be way smarter
+//xxx also, don't do this bytewise if aligned and on a matching-endian arch
+ insert = rec->body + rec->length;
+ for (i = 0; i < numValues; i++) {
+ U8_TO_BUF_BE(insert, 0, *values++);
+ insert += sizeof(*values);
+ }
+ rec->length += numValues * 8;
+
+ return 0;
+}
+
+int
+hprofAddU8ToRecord(hprof_record_t *rec, u8 value)
+{
+ return hprofAddU8ListToRecord(rec, &value, 1);
+}
diff --git a/vm/hprof/HprofStack.c b/vm/hprof/HprofStack.c
new file mode 100644
index 0000000..04641ef
--- /dev/null
+++ b/vm/hprof/HprofStack.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Hprof.h"
+#include "HprofStack.h"
+#include "alloc/HeapInternal.h"
+
+static HashTable *gStackTraceHashTable = NULL;
+static int gSerialNumber = 0;
+
+/* Number of stack frames to cache */
+#define STACK_DEPTH 8
+
+typedef struct {
+ int serialNumber;
+ int threadSerialNumber;
+ int frameIds[STACK_DEPTH];
+} StackTrace;
+
+typedef struct {
+ StackTrace trace;
+ u1 live;
+} StackTraceEntry;
+
+static u4 computeStackTraceHash(const StackTraceEntry *stackTraceEntry);
+
+int
+hprofStartup_Stack()
+{
+ HashIter iter;
+
+ /* This will be called when a GC begins. */
+ for (dvmHashIterBegin(gStackTraceHashTable, &iter);
+ !dvmHashIterDone(&iter);
+ dvmHashIterNext(&iter)) {
+ StackTraceEntry *stackTraceEntry;
+
+ /* Clear the 'live' bit at the start of the GC pass. */
+ stackTraceEntry = (StackTraceEntry *) dvmHashIterData(&iter);
+ stackTraceEntry->live = 0;
+ }
+
+ return 0;
+}
+
+int
+hprofShutdown_Stack()
+{
+ HashIter iter;
+
+ /* This will be called when a GC has completed. */
+ for (dvmHashIterBegin(gStackTraceHashTable, &iter);
+ !dvmHashIterDone(&iter);
+ dvmHashIterNext(&iter)) {
+ StackTraceEntry *stackTraceEntry;
+
+ /*
+ * If the 'live' bit is 0, the trace is not in use by any current
+ * heap object and may be destroyed.
+ */
+ stackTraceEntry = (StackTraceEntry *) dvmHashIterData(&iter);
+ if (!stackTraceEntry->live) {
+ dvmHashTableRemove(gStackTraceHashTable,
+ computeStackTraceHash(stackTraceEntry), stackTraceEntry);
+ free(stackTraceEntry);
+ }
+ }
+
+ return 0;
+}
+
+static u4
+computeStackTraceHash(const StackTraceEntry *stackTraceEntry)
+{
+ u4 hash = 0;
+ const char *cp = (const char *) &stackTraceEntry->trace;
+ int i;
+
+ for (i = 0; i < (int) sizeof(StackTrace); i++) {
+ hash = hash * 31 + cp[i];
+ }
+
+ return hash;
+}
+
+/* Only compare the 'trace' portion of the StackTraceEntry. */
+static int
+stackCmp(const void *tableItem, const void *looseItem)
+{
+ return memcmp(&((StackTraceEntry *) tableItem)->trace,
+ &((StackTraceEntry *) looseItem)->trace, sizeof(StackTrace));
+}
+
+static StackTraceEntry *
+stackDup(const StackTraceEntry *stackTrace)
+{
+ StackTraceEntry *newStackTrace = malloc(sizeof(StackTraceEntry));
+ memcpy(newStackTrace, stackTrace, sizeof(StackTraceEntry));
+ return newStackTrace;
+}
+
+static u4
+hprofLookupStackSerialNumber(const StackTraceEntry *stackTrace)
+{
+ StackTraceEntry *val;
+ u4 hashValue;
+ int serial;
+
+ /*
+ * Create the hash table on first contact. We can't do this in
+ * hprofStartupStack, because we have to compute stack trace
+ * serial numbers and place them into object headers before the
+ * rest of hprof is triggered by a GC event.
+ */
+ if (gStackTraceHashTable == NULL) {
+ gStackTraceHashTable = dvmHashTableCreate(512, free);
+ }
+ dvmHashTableLock(gStackTraceHashTable);
+
+ hashValue = computeStackTraceHash(stackTrace);
+ val = dvmHashTableLookup(gStackTraceHashTable, hashValue, (void *)stackTrace,
+ (HashCompareFunc)stackCmp, false);
+ if (val == NULL) {
+ StackTraceEntry *newStackTrace;
+
+ newStackTrace = stackDup(stackTrace);
+ newStackTrace->trace.serialNumber = ++gSerialNumber;
+ val = dvmHashTableLookup(gStackTraceHashTable, hashValue,
+ (void *)newStackTrace, (HashCompareFunc)stackCmp, true);
+ assert(val != NULL);
+ }
+
+ /* Mark the trace as live (in use by an object in the current heap). */
+ val->live = 1;
+
+ /* Grab the serial number before unlocking the table. */
+ serial = val->trace.serialNumber;
+
+ dvmHashTableUnlock(gStackTraceHashTable);
+
+ return serial;
+}
+
+int
+hprofDumpStacks(hprof_context_t *ctx)
+{
+ HashIter iter;
+ hprof_record_t *rec = &ctx->curRec;
+
+ dvmHashTableLock(gStackTraceHashTable);
+
+ for (dvmHashIterBegin(gStackTraceHashTable, &iter);
+ !dvmHashIterDone(&iter);
+ dvmHashIterNext(&iter))
+ {
+ const StackTraceEntry *stackTraceEntry;
+ int count;
+ int i;
+
+ hprofStartNewRecord(ctx, HPROF_TAG_STACK_TRACE, HPROF_TIME);
+
+ stackTraceEntry = (const StackTraceEntry *) dvmHashIterData(&iter);
+ assert(stackTraceEntry != NULL);
+
+ /* STACK TRACE format:
+ *
+ * u4: serial number for this stack
+ * u4: serial number for the running thread
+ * u4: number of frames
+ * [ID]*: ID for the stack frame
+ */
+ hprofAddU4ToRecord(rec, stackTraceEntry->trace.serialNumber);
+ hprofAddU4ToRecord(rec, stackTraceEntry->trace.threadSerialNumber);
+
+ count = 0;
+ while ((count < STACK_DEPTH) &&
+ (stackTraceEntry->trace.frameIds[count] != 0)) {
+ count++;
+ }
+ hprofAddU4ToRecord(rec, count);
+ for (i = 0; i < count; i++) {
+ hprofAddU4ToRecord(rec, stackTraceEntry->trace.frameIds[i]);
+ }
+ }
+
+ dvmHashTableUnlock(gStackTraceHashTable);
+
+ return 0;
+}
+
+void
+hprofFillInStackTrace(void *objectPtr)
+
+{
+ DvmHeapChunk *chunk;
+ StackTraceEntry stackTraceEntry;
+ Thread* self;
+ void* fp;
+ int i;
+
+ if (objectPtr == NULL) {
+ return;
+ }
+ self = dvmThreadSelf();
+ if (self == NULL) {
+ return;
+ }
+ fp = self->curFrame;
+
+ /* Serial number to be filled in later. */
+ stackTraceEntry.trace.serialNumber = -1;
+
+ /*
+ * TODO - The HAT tool doesn't care about thread data, so we can defer
+ * actually emitting thread records and assigning thread serial numbers.
+ */
+ stackTraceEntry.trace.threadSerialNumber = (int) self;
+
+ memset(&stackTraceEntry.trace.frameIds, 0,
+ sizeof(stackTraceEntry.trace.frameIds));
+
+ i = 0;
+ while ((fp != NULL) && (i < STACK_DEPTH)) {
+ const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+ const Method* method = saveArea->method;
+ StackFrameEntry frame;
+
+ if (!dvmIsBreakFrame(fp)) {
+ frame.frame.method = method;
+ if (dvmIsNativeMethod(method)) {
+ frame.frame.pc = 0; /* no saved PC for native methods */
+ } else {
+ assert(saveArea->xtra.currentPc >= method->insns &&
+ saveArea->xtra.currentPc <
+ method->insns + dvmGetMethodInsnsSize(method));
+ frame.frame.pc = (int) (saveArea->xtra.currentPc -
+ method->insns);
+ }
+
+ // Canonicalize the frame and cache it in the hprof context
+ stackTraceEntry.trace.frameIds[i++] =
+ hprofLookupStackFrameId(&frame);
+ }
+
+ assert(fp != saveArea->prevFrame);
+ fp = saveArea->prevFrame;
+ }
+
+ /* Store the stack trace serial number in the object header */
+ chunk = ptr2chunk(objectPtr);
+ chunk->stackTraceSerialNumber =
+ hprofLookupStackSerialNumber(&stackTraceEntry);
+}
diff --git a/vm/hprof/HprofStack.h b/vm/hprof/HprofStack.h
new file mode 100644
index 0000000..1f16c1e
--- /dev/null
+++ b/vm/hprof/HprofStack.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _DALVIK_HPROF_STACK
+#define _DALVIK_HPROF_STACK
+
+#include "../alloc/HeapInternal.h"
+
+typedef struct {
+ const Method *method;
+ int pc;
+} StackFrame;
+
+typedef struct {
+ StackFrame frame;
+ unsigned char live;
+} StackFrameEntry;
+
+int hprofStartupStack();
+int hprofShutdown_Stack();
+int hprofDumpStacks(hprof_context_t *ctx);
+void hprofFillInStackTrace(void *objectPtr);
+
+int hprofStartup_StackFrame();
+int hprofShutdown_StackFrame();
+hprof_stack_frame_id hprofLookupStackFrameId(const StackFrameEntry
+ *stackFrameEntry);
+int hprofDumpStackFrames(hprof_context_t *ctx);
+
+#endif /* _DALVIK_HPROF_STACK */
diff --git a/vm/hprof/HprofStackFrame.c b/vm/hprof/HprofStackFrame.c
new file mode 100644
index 0000000..f9c789e
--- /dev/null
+++ b/vm/hprof/HprofStackFrame.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Hprof.h"
+#include "HprofStack.h"
+
+#include "alloc/HeapInternal.h"
+
+static HashTable *gStackFrameHashTable;
+
+static u4 computeStackFrameHash(const StackFrameEntry *stackFrameEntry);
+
+int
+hprofStartup_StackFrame()
+{
+ HashIter iter;
+
+ /* Cache the string "<unknown>" for use when the source file is
+ * unknown.
+ */
+ hprofLookupStringId("<unknown>");
+
+ /* This will be called when a GC begins. */
+ for (dvmHashIterBegin(gStackFrameHashTable, &iter);
+ !dvmHashIterDone(&iter);
+ dvmHashIterNext(&iter)) {
+ StackFrameEntry *stackFrameEntry;
+ const Method *method;
+
+ /* Clear the 'live' bit at the start of the GC pass. */
+ stackFrameEntry = (StackFrameEntry *) dvmHashIterData(&iter);
+ stackFrameEntry->live = 0;
+
+ method = stackFrameEntry->frame.method;
+ if (method == NULL) {
+ continue;
+ }
+
+ /* Make sure the method name, descriptor, and source file are in
+ * the string table, and that the method class is in the class
+ * table. This is needed because strings and classes will be dumped
+ * before stack frames.
+ */
+
+ if (method->name) {
+ hprofLookupStringId(method->name);
+ }
+
+ DexStringCache cache;
+ const char* descriptor;
+
+ dexStringCacheInit(&cache);
+ descriptor = dexProtoGetMethodDescriptor(&method->prototype, &cache);
+ hprofLookupStringId(descriptor);
+ dexStringCacheRelease(&cache);
+
+ const char* sourceFile = dvmGetMethodSourceFile(method);
+ if (sourceFile) {
+ hprofLookupStringId(sourceFile);
+ }
+
+ if (method->clazz) {
+ hprofLookupClassId(method->clazz);
+ }
+ }
+
+ return 0;
+}
+
+int
+hprofShutdown_StackFrame()
+{
+ HashIter iter;
+
+ /* This will be called when a GC has completed. */
+ for (dvmHashIterBegin(gStackFrameHashTable, &iter);
+ !dvmHashIterDone(&iter);
+ dvmHashIterNext(&iter)) {
+ const StackFrameEntry *stackFrameEntry;
+
+ /*
+ * If the 'live' bit is 0, the frame is not in use by any current
+ * heap object and may be destroyed.
+ */
+ stackFrameEntry = (const StackFrameEntry *) dvmHashIterData(&iter);
+ if (!stackFrameEntry->live) {
+ dvmHashTableRemove(gStackFrameHashTable,
+ computeStackFrameHash(stackFrameEntry),
+ (void*) stackFrameEntry);
+ free((void*) stackFrameEntry);
+ }
+ }
+
+ return 0;
+}
+
+/* Only hash the 'frame' portion of the StackFrameEntry. */
+static u4
+computeStackFrameHash(const StackFrameEntry *stackFrameEntry)
+{
+ u4 hash = 0;
+ const char *cp = (char *) &stackFrameEntry->frame;
+ int i;
+
+ for (i = 0; i < (int) sizeof(StackFrame); i++) {
+ hash = 31 * hash + cp[i];
+ }
+ return hash;
+}
+
+/* Only compare the 'frame' portion of the StackFrameEntry. */
+static int
+stackFrameCmp(const void *tableItem, const void *looseItem)
+{
+ return memcmp(&((StackFrameEntry *)tableItem)->frame,
+ &((StackFrameEntry *) looseItem)->frame, sizeof(StackFrame));
+}
+
+static StackFrameEntry *
+stackFrameDup(const StackFrameEntry *stackFrameEntry)
+{
+ StackFrameEntry *newStackFrameEntry = malloc(sizeof(StackFrameEntry));
+ memcpy(newStackFrameEntry, stackFrameEntry, sizeof(StackFrameEntry));
+ return newStackFrameEntry;
+}
+
+hprof_stack_frame_id
+hprofLookupStackFrameId(const StackFrameEntry *stackFrameEntry)
+{
+ StackFrameEntry *val;
+ u4 hashValue;
+
+ /*
+ * Create the hash table on first contact. We can't do this in
+ * hprofStartupStackFrame, because we have to compute stack trace
+ * serial numbers and place them into object headers before the
+ * rest of hprof is triggered by a GC event.
+ */
+ if (gStackFrameHashTable == NULL) {
+ gStackFrameHashTable = dvmHashTableCreate(512, free);
+ }
+ dvmHashTableLock(gStackFrameHashTable);
+
+ hashValue = computeStackFrameHash(stackFrameEntry);
+ val = dvmHashTableLookup(gStackFrameHashTable, hashValue,
+ (void *)stackFrameEntry, (HashCompareFunc)stackFrameCmp, false);
+ if (val == NULL) {
+ const StackFrameEntry *newStackFrameEntry;
+
+ newStackFrameEntry = stackFrameDup(stackFrameEntry);
+ val = dvmHashTableLookup(gStackFrameHashTable, hashValue,
+ (void *)newStackFrameEntry, (HashCompareFunc)stackFrameCmp, true);
+ assert(val != NULL);
+ }
+
+ /* Mark the frame as live (in use by an object in the current heap). */
+ val->live = 1;
+
+ dvmHashTableUnlock(gStackFrameHashTable);
+
+ return (hprof_stack_frame_id) val;
+}
+
+int
+hprofDumpStackFrames(hprof_context_t *ctx)
+{
+ HashIter iter;
+ hprof_record_t *rec = &ctx->curRec;
+
+ dvmHashTableLock(gStackFrameHashTable);
+
+ for (dvmHashIterBegin(gStackFrameHashTable, &iter);
+ !dvmHashIterDone(&iter);
+ dvmHashIterNext(&iter))
+ {
+ const StackFrameEntry *stackFrameEntry;
+ const Method *method;
+ int pc;
+ const char *sourceFile;
+ ClassObject *clazz;
+ int lineNum;
+
+ hprofStartNewRecord(ctx, HPROF_TAG_STACK_FRAME, HPROF_TIME);
+
+ stackFrameEntry = (const StackFrameEntry *) dvmHashIterData(&iter);
+ assert(stackFrameEntry != NULL);
+
+ method = stackFrameEntry->frame.method;
+ pc = stackFrameEntry->frame.pc;
+ sourceFile = dvmGetMethodSourceFile(method);
+ if (sourceFile == NULL) {
+ sourceFile = "<unknown>";
+ lineNum = 0;
+ } else {
+ lineNum = dvmLineNumFromPC(method, pc);
+ }
+ clazz = (ClassObject *) hprofLookupClassId(method->clazz);
+
+ /* STACK FRAME format:
+ *
+ * ID: ID for this stack frame
+ * ID: ID for the method name
+ * ID: ID for the method descriptor
+ * ID: ID for the source file name
+ * u4: class serial number
+ * u4: line number, 0 = no line information
+ *
+ * We use the address of the stack frame as its ID.
+ */
+
+ DexStringCache cache;
+ const char* descriptor;
+
+ dexStringCacheInit(&cache);
+ descriptor = dexProtoGetMethodDescriptor(&method->prototype, &cache);
+
+ hprofAddIdToRecord(rec, (u4) stackFrameEntry);
+ hprofAddIdToRecord(rec, hprofLookupStringId(method->name));
+ hprofAddIdToRecord(rec, hprofLookupStringId(descriptor));
+ hprofAddIdToRecord(rec, hprofLookupStringId(sourceFile));
+ hprofAddU4ToRecord(rec, (u4) clazz->serialNumber);
+ hprofAddU4ToRecord(rec, (u4) lineNum);
+
+ dexStringCacheRelease(&cache);
+ }
+
+ dvmHashTableUnlock(gStackFrameHashTable);
+ return 0;
+}
diff --git a/vm/hprof/HprofString.c b/vm/hprof/HprofString.c
new file mode 100644
index 0000000..3f697f5
--- /dev/null
+++ b/vm/hprof/HprofString.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Common string pool for the profiler
+ */
+#include "Hprof.h"
+
+static HashTable *gStringHashTable;
+
+int
+hprofStartup_String()
+{
+ gStringHashTable = dvmHashTableCreate(512, free);
+ if (gStringHashTable == NULL) {
+ return UNIQUE_ERROR();
+ }
+ return 0;
+}
+
+int
+hprofShutdown_String()
+{
+ dvmHashTableFree(gStringHashTable);
+ return 0;
+}
+
+static u4
+computeUtf8Hash(const char *str)
+{
+ u4 hash = 0;
+ const char *cp;
+ char c;
+
+ cp = str;
+ while ((c = *cp++) != '\0') {
+ hash = hash * 31 + c;
+ }
+
+ return hash;
+}
+
+hprof_string_id
+hprofLookupStringId(const char *str)
+{
+ void *val;
+ u4 hashValue;
+
+ dvmHashTableLock(gStringHashTable);
+
+ hashValue = computeUtf8Hash(str);
+ val = dvmHashTableLookup(gStringHashTable, hashValue, (void *)str,
+ (HashCompareFunc)strcmp, false);
+ if (val == NULL) {
+ const char *newStr;
+
+ newStr = strdup(str);
+ val = dvmHashTableLookup(gStringHashTable, hashValue, (void *)newStr,
+ (HashCompareFunc)strcmp, true);
+ assert(val != NULL);
+ }
+
+ dvmHashTableUnlock(gStringHashTable);
+
+ return (hprof_string_id)val;
+}
+
+int
+hprofDumpStrings(hprof_context_t *ctx)
+{
+ HashIter iter;
+ hprof_record_t *rec = &ctx->curRec;
+ int err;
+
+ dvmHashTableLock(gStringHashTable);
+
+ for (err = 0, dvmHashIterBegin(gStringHashTable, &iter);
+ err == 0 && !dvmHashIterDone(&iter);
+ dvmHashIterNext(&iter))
+ {
+ err = hprofStartNewRecord(ctx, HPROF_TAG_STRING, HPROF_TIME);
+ if (err == 0) {
+ const char *str;
+
+ str = (const char *)dvmHashIterData(&iter);
+ assert(str != NULL);
+
+ /* STRING format:
+ *
+ * ID: ID for this string
+ * [u1]*: UTF8 characters for string (NOT NULL terminated)
+ * (the record format encodes the length)
+ *
+ * We use the address of the string data as its ID.
+ */
+ err = hprofAddU4ToRecord(rec, (u4)str);
+ if (err == 0) {
+ err = hprofAddUtf8StringToRecord(rec, str);
+ }
+ }
+ }
+
+ dvmHashTableUnlock(gStringHashTable);
+
+ return err;
+}
diff --git a/vm/interp/Interp.c b/vm/interp/Interp.c
new file mode 100644
index 0000000..9f44a13
--- /dev/null
+++ b/vm/interp/Interp.c
@@ -0,0 +1,1360 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Main interpreter entry point and support functions.
+ *
+ * The entry point selects the "standard" or "debug" interpreter and
+ * facilitates switching between them. The standard interpreter may
+ * use the "fast" or "portable" implementation.
+ *
+ * Some debugger support functions are included here.
+ */
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+
+
+/*
+ * ===========================================================================
+ * Debugger support
+ * ===========================================================================
+ */
+
+// fwd
+static BreakpointSet* dvmBreakpointSetAlloc(void);
+static void dvmBreakpointSetFree(BreakpointSet* pSet);
+
+/*
+ * Initialize global breakpoint structures.
+ */
+bool dvmBreakpointStartup(void)
+{
+ gDvm.breakpointSet = dvmBreakpointSetAlloc();
+ return (gDvm.breakpointSet != NULL);
+}
+
+/*
+ * Free resources.
+ */
+void dvmBreakpointShutdown(void)
+{
+ dvmBreakpointSetFree(gDvm.breakpointSet);
+}
+
+
+/*
+ * This represents a breakpoint inserted in the instruction stream.
+ *
+ * The debugger may ask us to create the same breakpoint multiple times.
+ * We only remove the breakpoint when the last instance is cleared.
+ */
+typedef struct {
+ Method* method; /* method we're associated with */
+ u2* addr; /* absolute memory address */
+ u1 originalOpCode; /* original 8-bit opcode value */
+ int setCount; /* #of times this breakpoint was set */
+} Breakpoint;
+
+/*
+ * Set of breakpoints.
+ */
+struct BreakpointSet {
+ /* grab lock before reading or writing anything else in here */
+ pthread_mutex_t lock;
+
+ /* vector of breakpoint structures */
+ int alloc;
+ int count;
+ Breakpoint* breakpoints;
+};
+
+/*
+ * Initialize a BreakpointSet. Initially empty.
+ */
+static BreakpointSet* dvmBreakpointSetAlloc(void)
+{
+ BreakpointSet* pSet = (BreakpointSet*) calloc(1, sizeof(*pSet));
+
+ dvmInitMutex(&pSet->lock);
+ /* leave the rest zeroed -- will alloc on first use */
+
+ return pSet;
+}
+
+/*
+ * Free storage associated with a BreakpointSet.
+ */
+static void dvmBreakpointSetFree(BreakpointSet* pSet)
+{
+ if (pSet == NULL)
+ return;
+
+ free(pSet->breakpoints);
+ free(pSet);
+}
+
+/*
+ * Lock the breakpoint set.
+ *
+ * It's not currently necessary to switch to VMWAIT in the event of
+ * contention, because nothing in here can block. However, it's possible
+ * that the bytecode-updater code could become fancier in the future, so
+ * we do the trylock dance as a bit of future-proofing.
+ */
+static void dvmBreakpointSetLock(BreakpointSet* pSet)
+{
+ if (dvmTryLockMutex(&pSet->lock) != 0) {
+ Thread* self = dvmThreadSelf();
+ ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
+ dvmLockMutex(&pSet->lock);
+ dvmChangeStatus(self, oldStatus);
+ }
+}
+
+/*
+ * Unlock the breakpoint set.
+ */
+static void dvmBreakpointSetUnlock(BreakpointSet* pSet)
+{
+ dvmUnlockMutex(&pSet->lock);
+}
+
+/*
+ * Return the #of breakpoints.
+ */
+static int dvmBreakpointSetCount(const BreakpointSet* pSet)
+{
+ return pSet->count;
+}
+
+/*
+ * See if we already have an entry for this address.
+ *
+ * The BreakpointSet's lock must be acquired before calling here.
+ *
+ * Returns the index of the breakpoint entry, or -1 if not found.
+ */
+static int dvmBreakpointSetFind(const BreakpointSet* pSet, const u2* addr)
+{
+ int i;
+
+ for (i = 0; i < pSet->count; i++) {
+ Breakpoint* pBreak = &pSet->breakpoints[i];
+ if (pBreak->addr == addr)
+ return i;
+ }
+
+ return -1;
+}
+
+/*
+ * Retrieve the opcode that was originally at the specified location.
+ *
+ * The BreakpointSet's lock must be acquired before calling here.
+ *
+ * Returns "true" with the opcode in *pOrig on success.
+ */
+static bool dvmBreakpointSetOriginalOpCode(const BreakpointSet* pSet,
+ const u2* addr, u1* pOrig)
+{
+ int idx = dvmBreakpointSetFind(pSet, addr);
+ if (idx < 0)
+ return false;
+
+ *pOrig = pSet->breakpoints[idx].originalOpCode;
+ return true;
+}
+
+/*
+ * Check the opcode. If it's a "magic" NOP, indicating the start of
+ * switch or array data in the instruction stream, we don't want to set
+ * a breakpoint.
+ *
+ * This can happen because the line number information dx generates
+ * associates the switch data with the switch statement's line number,
+ * and some debuggers put breakpoints at every address associated with
+ * a given line. The result is that the breakpoint stomps on the NOP
+ * instruction that doubles as a data table magic number, and an explicit
+ * check in the interpreter results in an exception being thrown.
+ *
+ * We don't want to simply refuse to add the breakpoint to the table,
+ * because that confuses the housekeeping. We don't want to reject the
+ * debugger's event request, and we want to be sure that there's exactly
+ * one un-set operation for every set op.
+ */
+static bool instructionIsMagicNop(const u2* addr)
+{
+ u2 curVal = *addr;
+ return ((curVal & 0xff) == OP_NOP && (curVal >> 8) != 0);
+}
+
+/*
+ * Add a breakpoint at a specific address. If the address is already
+ * present in the table, this just increments the count.
+ *
+ * For a new entry, this will extract and preserve the current opcode from
+ * the instruction stream, and replace it with a breakpoint opcode.
+ *
+ * The BreakpointSet's lock must be acquired before calling here.
+ *
+ * Returns "true" on success.
+ */
+static bool dvmBreakpointSetAdd(BreakpointSet* pSet, Method* method,
+ unsigned int instrOffset)
+{
+ const int kBreakpointGrowth = 10;
+ const u2* addr = method->insns + instrOffset;
+ int idx = dvmBreakpointSetFind(pSet, addr);
+ Breakpoint* pBreak;
+
+ if (idx < 0) {
+ if (pSet->count == pSet->alloc) {
+ int newSize = pSet->alloc + kBreakpointGrowth;
+ Breakpoint* newVec;
+
+ LOGV("+++ increasing breakpoint set size to %d\n", newSize);
+
+ /* pSet->breakpoints will be NULL on first entry */
+ newVec = realloc(pSet->breakpoints, newSize * sizeof(Breakpoint));
+ if (newVec == NULL)
+ return false;
+
+ pSet->breakpoints = newVec;
+ pSet->alloc = newSize;
+ }
+
+ pBreak = &pSet->breakpoints[pSet->count++];
+ pBreak->method = method;
+ pBreak->addr = (u2*)addr;
+ pBreak->originalOpCode = *(u1*)addr;
+ pBreak->setCount = 1;
+
+ /*
+ * Change the opcode. We must ensure that the BreakpointSet
+ * updates happen before we change the opcode.
+ *
+ * If the method has not been verified, we do NOT insert the
+ * breakpoint yet, since that will screw up the verifier. The
+ * debugger is allowed to insert breakpoints in unverified code,
+ * but since we don't execute unverified code we don't need to
+ * alter the bytecode yet.
+ *
+ * The class init code will "flush" all pending opcode writes
+ * before verification completes.
+ */
+ assert(*(u1*)addr != OP_BREAKPOINT);
+ if (dvmIsClassVerified(method->clazz)) {
+ LOGV("Class %s verified, adding breakpoint at %p\n",
+ method->clazz->descriptor, addr);
+ if (instructionIsMagicNop(addr)) {
+ LOGV("Refusing to set breakpoint on %04x at %s.%s + 0x%x\n",
+ *addr, method->clazz->descriptor, method->name,
+ instrOffset);
+ } else {
+ ANDROID_MEMBAR_FULL();
+ dvmDexChangeDex1(method->clazz->pDvmDex, (u1*)addr,
+ OP_BREAKPOINT);
+ }
+ } else {
+ LOGV("Class %s NOT verified, deferring breakpoint at %p\n",
+ method->clazz->descriptor, addr);
+ }
+ } else {
+ /*
+ * Breakpoint already exists, just increase the count.
+ */
+ pBreak = &pSet->breakpoints[idx];
+ pBreak->setCount++;
+ }
+
+ return true;
+}
+
+/*
+ * Remove one instance of the specified breakpoint. When the count
+ * reaches zero, the entry is removed from the table, and the original
+ * opcode is restored.
+ *
+ * The BreakpointSet's lock must be acquired before calling here.
+ */
+static void dvmBreakpointSetRemove(BreakpointSet* pSet, Method* method,
+ unsigned int instrOffset)
+{
+ const u2* addr = method->insns + instrOffset;
+ int idx = dvmBreakpointSetFind(pSet, addr);
+
+ if (idx < 0) {
+ /* breakpoint not found in set -- unexpected */
+ if (*(u1*)addr == OP_BREAKPOINT) {
+ LOGE("Unable to restore breakpoint opcode (%s.%s +0x%x)\n",
+ method->clazz->descriptor, method->name, instrOffset);
+ dvmAbort();
+ } else {
+ LOGW("Breakpoint was already restored? (%s.%s +0x%x)\n",
+ method->clazz->descriptor, method->name, instrOffset);
+ }
+ } else {
+ Breakpoint* pBreak = &pSet->breakpoints[idx];
+ if (pBreak->setCount == 1) {
+ /*
+ * Must restore opcode before removing set entry.
+ *
+ * If the breakpoint was never flushed, we could be ovewriting
+ * a value with the same value. Not a problem, though we
+ * could end up causing a copy-on-write here when we didn't
+ * need to. (Not worth worrying about.)
+ */
+ dvmDexChangeDex1(method->clazz->pDvmDex, (u1*)addr,
+ pBreak->originalOpCode);
+ ANDROID_MEMBAR_FULL();
+
+ if (idx != pSet->count-1) {
+ /* shift down */
+ memmove(&pSet->breakpoints[idx], &pSet->breakpoints[idx+1],
+ (pSet->count-1 - idx) * sizeof(pSet->breakpoints[0]));
+ }
+ pSet->count--;
+ pSet->breakpoints[pSet->count].addr = (u2*) 0xdecadead; // debug
+ } else {
+ pBreak->setCount--;
+ assert(pBreak->setCount > 0);
+ }
+ }
+}
+
+/*
+ * Flush any breakpoints associated with methods in "clazz". We want to
+ * change the opcode, which might not have happened when the breakpoint
+ * was initially set because the class was in the process of being
+ * verified.
+ *
+ * The BreakpointSet's lock must be acquired before calling here.
+ */
+static void dvmBreakpointSetFlush(BreakpointSet* pSet, ClassObject* clazz)
+{
+ int i;
+ for (i = 0; i < pSet->count; i++) {
+ Breakpoint* pBreak = &pSet->breakpoints[i];
+ if (pBreak->method->clazz == clazz) {
+ /*
+ * The breakpoint is associated with a method in this class.
+ * It might already be there or it might not; either way,
+ * flush it out.
+ */
+ LOGV("Flushing breakpoint at %p for %s\n",
+ pBreak->addr, clazz->descriptor);
+ if (instructionIsMagicNop(pBreak->addr)) {
+ LOGV("Refusing to flush breakpoint on %04x at %s.%s + 0x%x\n",
+ *pBreak->addr, pBreak->method->clazz->descriptor,
+ pBreak->method->name, pBreak->addr - pBreak->method->insns);
+ } else {
+ dvmDexChangeDex1(clazz->pDvmDex, (u1*)pBreak->addr,
+ OP_BREAKPOINT);
+ }
+ }
+ }
+}
+
+
+/*
+ * Do any debugger-attach-time initialization.
+ */
+void dvmInitBreakpoints(void)
+{
+ /* quick sanity check */
+ BreakpointSet* pSet = gDvm.breakpointSet;
+ dvmBreakpointSetLock(pSet);
+ if (dvmBreakpointSetCount(pSet) != 0) {
+ LOGW("WARNING: %d leftover breakpoints\n", dvmBreakpointSetCount(pSet));
+ /* generally not good, but we can keep going */
+ }
+ dvmBreakpointSetUnlock(pSet);
+}
+
+/*
+ * Add an address to the list, putting it in the first non-empty slot.
+ *
+ * Sometimes the debugger likes to add two entries for one breakpoint.
+ * We add two entries here, so that we get the right behavior when it's
+ * removed twice.
+ *
+ * This will only be run from the JDWP thread, and it will happen while
+ * we are updating the event list, which is synchronized. We're guaranteed
+ * to be the only one adding entries, and the lock ensures that nobody
+ * will be trying to remove them while we're in here.
+ *
+ * "addr" is the absolute address of the breakpoint bytecode.
+ */
+void dvmAddBreakAddr(Method* method, unsigned int instrOffset)
+{
+ BreakpointSet* pSet = gDvm.breakpointSet;
+ dvmBreakpointSetLock(pSet);
+ dvmBreakpointSetAdd(pSet, method, instrOffset);
+ dvmBreakpointSetUnlock(pSet);
+}
+
+/*
+ * Remove an address from the list by setting the entry to NULL.
+ *
+ * This can be called from the JDWP thread (because the debugger has
+ * cancelled the breakpoint) or from an event thread (because it's a
+ * single-shot breakpoint, e.g. "run to line"). We only get here as
+ * the result of removing an entry from the event list, which is
+ * synchronized, so it should not be possible for two threads to be
+ * updating breakpoints at the same time.
+ */
+void dvmClearBreakAddr(Method* method, unsigned int instrOffset)
+{
+ BreakpointSet* pSet = gDvm.breakpointSet;
+ dvmBreakpointSetLock(pSet);
+ dvmBreakpointSetRemove(pSet, method, instrOffset);
+ dvmBreakpointSetUnlock(pSet);
+}
+
+/*
+ * Get the original opcode from under a breakpoint.
+ *
+ * On SMP hardware it's possible one core might try to execute a breakpoint
+ * after another core has cleared it. We need to handle the case where
+ * there's no entry in the breakpoint set. (The memory barriers in the
+ * locks and in the breakpoint update code should ensure that, once we've
+ * observed the absence of a breakpoint entry, we will also now observe
+ * the restoration of the original opcode. The fact that we're holding
+ * the lock prevents other threads from confusing things further.)
+ */
+u1 dvmGetOriginalOpCode(const u2* addr)
+{
+ BreakpointSet* pSet = gDvm.breakpointSet;
+ u1 orig = 0;
+
+ dvmBreakpointSetLock(pSet);
+ if (!dvmBreakpointSetOriginalOpCode(pSet, addr, &orig)) {
+ orig = *(u1*)addr;
+ if (orig == OP_BREAKPOINT) {
+ LOGE("GLITCH: can't find breakpoint, opcode is still set\n");
+ dvmAbort();
+ }
+ }
+ dvmBreakpointSetUnlock(pSet);
+
+ return orig;
+}
+
+/*
+ * Flush any breakpoints associated with methods in "clazz".
+ *
+ * We don't want to modify the bytecode of a method before the verifier
+ * gets a chance to look at it, so we postpone opcode replacement until
+ * after verification completes.
+ */
+void dvmFlushBreakpoints(ClassObject* clazz)
+{
+ BreakpointSet* pSet = gDvm.breakpointSet;
+
+ if (pSet == NULL)
+ return;
+
+ assert(dvmIsClassVerified(clazz));
+ dvmBreakpointSetLock(pSet);
+ dvmBreakpointSetFlush(pSet, clazz);
+ dvmBreakpointSetUnlock(pSet);
+}
+
+/*
+ * Add a single step event. Currently this is a global item.
+ *
+ * We set up some initial values based on the thread's current state. This
+ * won't work well if the thread is running, so it's up to the caller to
+ * verify that it's suspended.
+ *
+ * This is only called from the JDWP thread.
+ */
+bool dvmAddSingleStep(Thread* thread, int size, int depth)
+{
+ StepControl* pCtrl = &gDvm.stepControl;
+
+ if (pCtrl->active && thread != pCtrl->thread) {
+ LOGW("WARNING: single-step active for %p; adding %p\n",
+ pCtrl->thread, thread);
+
+ /*
+ * Keep going, overwriting previous. This can happen if you
+ * suspend a thread in Object.wait, hit the single-step key, then
+ * switch to another thread and do the same thing again.
+ * The first thread's step is still pending.
+ *
+ * TODO: consider making single-step per-thread. Adds to the
+ * overhead, but could be useful in rare situations.
+ */
+ }
+
+ pCtrl->size = size;
+ pCtrl->depth = depth;
+ pCtrl->thread = thread;
+
+ /*
+ * We may be stepping into or over method calls, or running until we
+ * return from the current method. To make this work we need to track
+ * the current line, current method, and current stack depth. We need
+ * to be checking these after most instructions, notably those that
+ * call methods, return from methods, or are on a different line from the
+ * previous instruction.
+ *
+ * We have to start with a snapshot of the current state. If we're in
+ * an interpreted method, everything we need is in the current frame. If
+ * we're in a native method, possibly with some extra JNI frames pushed
+ * on by PushLocalFrame, we want to use the topmost native method.
+ */
+ const StackSaveArea* saveArea;
+ void* fp;
+ void* prevFp = NULL;
+
+ for (fp = thread->curFrame; fp != NULL; fp = saveArea->prevFrame) {
+ const Method* method;
+
+ saveArea = SAVEAREA_FROM_FP(fp);
+ method = saveArea->method;
+
+ if (!dvmIsBreakFrame(fp) && !dvmIsNativeMethod(method))
+ break;
+ prevFp = fp;
+ }
+ if (fp == NULL) {
+ LOGW("Unexpected: step req in native-only threadid=%d\n",
+ thread->threadId);
+ return false;
+ }
+ if (prevFp != NULL) {
+ /*
+ * First interpreted frame wasn't the one at the bottom. Break
+ * frames are only inserted when calling from native->interp, so we
+ * don't need to worry about one being here.
+ */
+ LOGV("##### init step while in native method\n");
+ fp = prevFp;
+ assert(!dvmIsBreakFrame(fp));
+ assert(dvmIsNativeMethod(SAVEAREA_FROM_FP(fp)->method));
+ saveArea = SAVEAREA_FROM_FP(fp);
+ }
+
+ /*
+ * Pull the goodies out. "xtra.currentPc" should be accurate since
+ * we update it on every instruction while the debugger is connected.
+ */
+ pCtrl->method = saveArea->method;
+ // Clear out any old address set
+ if (pCtrl->pAddressSet != NULL) {
+ // (discard const)
+ free((void *)pCtrl->pAddressSet);
+ pCtrl->pAddressSet = NULL;
+ }
+ if (dvmIsNativeMethod(pCtrl->method)) {
+ pCtrl->line = -1;
+ } else {
+ pCtrl->line = dvmLineNumFromPC(saveArea->method,
+ saveArea->xtra.currentPc - saveArea->method->insns);
+ pCtrl->pAddressSet
+ = dvmAddressSetForLine(saveArea->method, pCtrl->line);
+ }
+ pCtrl->frameDepth = dvmComputeVagueFrameDepth(thread, thread->curFrame);
+ pCtrl->active = true;
+
+ LOGV("##### step init: thread=%p meth=%p '%s' line=%d frameDepth=%d depth=%s size=%s\n",
+ pCtrl->thread, pCtrl->method, pCtrl->method->name,
+ pCtrl->line, pCtrl->frameDepth,
+ dvmJdwpStepDepthStr(pCtrl->depth),
+ dvmJdwpStepSizeStr(pCtrl->size));
+
+ return true;
+}
+
+/*
+ * Disable a single step event.
+ */
+void dvmClearSingleStep(Thread* thread)
+{
+ UNUSED_PARAMETER(thread);
+
+ gDvm.stepControl.active = false;
+}
+
+
+/*
+ * Recover the "this" pointer from the current interpreted method. "this"
+ * is always in "in0" for non-static methods.
+ *
+ * The "ins" start at (#of registers - #of ins). Note in0 != v0.
+ *
+ * This works because "dx" guarantees that it will work. It's probably
+ * fairly common to have a virtual method that doesn't use its "this"
+ * pointer, in which case we're potentially wasting a register. However,
+ * the debugger doesn't treat "this" as just another argument. For
+ * example, events (such as breakpoints) can be enabled for specific
+ * values of "this". There is also a separate StackFrame.ThisObject call
+ * in JDWP that is expected to work for any non-native non-static method.
+ *
+ * Because we need it when setting up debugger event filters, we want to
+ * be able to do this quickly.
+ */
+Object* dvmGetThisPtr(const Method* method, const u4* fp)
+{
+ if (dvmIsStaticMethod(method))
+ return NULL;
+ return (Object*)fp[method->registersSize - method->insSize];
+}
+
+
+#if defined(WITH_TRACKREF_CHECKS)
+/*
+ * Verify that all internally-tracked references have been released. If
+ * they haven't, print them and abort the VM.
+ *
+ * "debugTrackedRefStart" indicates how many refs were on the list when
+ * we were first invoked.
+ */
+void dvmInterpCheckTrackedRefs(Thread* self, const Method* method,
+ int debugTrackedRefStart)
+{
+ if (dvmReferenceTableEntries(&self->internalLocalRefTable)
+ != (size_t) debugTrackedRefStart)
+ {
+ char* desc;
+ Object** top;
+ int count;
+
+ count = dvmReferenceTableEntries(&self->internalLocalRefTable);
+
+ LOGE("TRACK: unreleased internal reference (prev=%d total=%d)\n",
+ debugTrackedRefStart, count);
+ desc = dexProtoCopyMethodDescriptor(&method->prototype);
+ LOGE(" current method is %s.%s %s\n", method->clazz->descriptor,
+ method->name, desc);
+ free(desc);
+ top = self->internalLocalRefTable.table + debugTrackedRefStart;
+ while (top < self->internalLocalRefTable.nextEntry) {
+ LOGE(" %p (%s)\n",
+ *top,
+ ((*top)->clazz != NULL) ? (*top)->clazz->descriptor : "");
+ top++;
+ }
+ dvmDumpThread(self, false);
+
+ dvmAbort();
+ }
+ //LOGI("TRACK OK\n");
+}
+#endif
+
+
+#ifdef LOG_INSTR
+/*
+ * Dump the v-registers. Sent to the ILOG log tag.
+ */
+void dvmDumpRegs(const Method* method, const u4* framePtr, bool inOnly)
+{
+ int i, localCount;
+
+ localCount = method->registersSize - method->insSize;
+
+ LOG(LOG_VERBOSE, LOG_TAG"i", "Registers (fp=%p):\n", framePtr);
+ for (i = method->registersSize-1; i >= 0; i--) {
+ if (i >= localCount) {
+ LOG(LOG_VERBOSE, LOG_TAG"i", " v%-2d in%-2d : 0x%08x\n",
+ i, i-localCount, framePtr[i]);
+ } else {
+ if (inOnly) {
+ LOG(LOG_VERBOSE, LOG_TAG"i", " [...]\n");
+ break;
+ }
+ const char* name = "";
+#if 0 // "locals" structure has changed -- need to rewrite this
+ int j;
+ DexFile* pDexFile = method->clazz->pDexFile;
+ const DexCode* pDexCode = dvmGetMethodCode(method);
+ int localsSize = dexGetLocalsSize(pDexFile, pDexCode);
+ const DexLocal* locals = dvmDexGetLocals(pDexFile, pDexCode);
+ for (j = 0; j < localsSize, j++) {
+ if (locals[j].registerNum == (u4) i) {
+ name = dvmDexStringStr(locals[j].pName);
+ break;
+ }
+ }
+#endif
+ LOG(LOG_VERBOSE, LOG_TAG"i", " v%-2d : 0x%08x %s\n",
+ i, framePtr[i], name);
+ }
+ }
+}
+#endif
+
+
+/*
+ * ===========================================================================
+ * Entry point and general support functions
+ * ===========================================================================
+ */
+
+/*
+ * Construct an s4 from two consecutive half-words of switch data.
+ * This needs to check endianness because the DEX optimizer only swaps
+ * half-words in instruction stream.
+ *
+ * "switchData" must be 32-bit aligned.
+ */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+static inline s4 s4FromSwitchData(const void* switchData) {
+ return *(s4*) switchData;
+}
+#else
+static inline s4 s4FromSwitchData(const void* switchData) {
+ u2* data = switchData;
+ return data[0] | (((s4) data[1]) << 16);
+}
+#endif
+
+/*
+ * Find the matching case. Returns the offset to the handler instructions.
+ *
+ * Returns 3 if we don't find a match (it's the size of the packed-switch
+ * instruction).
+ */
+s4 dvmInterpHandlePackedSwitch(const u2* switchData, s4 testVal)
+{
+ const int kInstrLen = 3;
+ u2 size;
+ s4 firstKey;
+ const s4* entries;
+
+ /*
+ * Packed switch data format:
+ * ushort ident = 0x0100 magic value
+ * ushort size number of entries in the table
+ * int first_key first (and lowest) switch case value
+ * int targets[size] branch targets, relative to switch opcode
+ *
+ * Total size is (4+size*2) 16-bit code units.
+ */
+ if (*switchData++ != kPackedSwitchSignature) {
+ /* should have been caught by verifier */
+ dvmThrowException("Ljava/lang/InternalError;",
+ "bad packed switch magic");
+ return kInstrLen;
+ }
+
+ size = *switchData++;
+ assert(size > 0);
+
+ firstKey = *switchData++;
+ firstKey |= (*switchData++) << 16;
+
+ if (testVal < firstKey || testVal >= firstKey + size) {
+ LOGVV("Value %d not found in switch (%d-%d)\n",
+ testVal, firstKey, firstKey+size-1);
+ return kInstrLen;
+ }
+
+ /* The entries are guaranteed to be aligned on a 32-bit boundary;
+ * we can treat them as a native int array.
+ */
+ entries = (const s4*) switchData;
+ assert(((u4)entries & 0x3) == 0);
+
+ assert(testVal - firstKey >= 0 && testVal - firstKey < size);
+ LOGVV("Value %d found in slot %d (goto 0x%02x)\n",
+ testVal, testVal - firstKey,
+ s4FromSwitchData(&entries[testVal - firstKey]));
+ return s4FromSwitchData(&entries[testVal - firstKey]);
+}
+
+/*
+ * Find the matching case. Returns the offset to the handler instructions.
+ *
+ * Returns 3 if we don't find a match (it's the size of the sparse-switch
+ * instruction).
+ */
+s4 dvmInterpHandleSparseSwitch(const u2* switchData, s4 testVal)
+{
+ const int kInstrLen = 3;
+ u2 size;
+ const s4* keys;
+ const s4* entries;
+
+ /*
+ * Sparse switch data format:
+ * ushort ident = 0x0200 magic value
+ * ushort size number of entries in the table; > 0
+ * int keys[size] keys, sorted low-to-high; 32-bit aligned
+ * int targets[size] branch targets, relative to switch opcode
+ *
+ * Total size is (2+size*4) 16-bit code units.
+ */
+
+ if (*switchData++ != kSparseSwitchSignature) {
+ /* should have been caught by verifier */
+ dvmThrowException("Ljava/lang/InternalError;",
+ "bad sparse switch magic");
+ return kInstrLen;
+ }
+
+ size = *switchData++;
+ assert(size > 0);
+
+ /* The keys are guaranteed to be aligned on a 32-bit boundary;
+ * we can treat them as a native int array.
+ */
+ keys = (const s4*) switchData;
+ assert(((u4)keys & 0x3) == 0);
+
+ /* The entries are guaranteed to be aligned on a 32-bit boundary;
+ * we can treat them as a native int array.
+ */
+ entries = keys + size;
+ assert(((u4)entries & 0x3) == 0);
+
+ /*
+ * Binary-search through the array of keys, which are guaranteed to
+ * be sorted low-to-high.
+ */
+ int lo = 0;
+ int hi = size - 1;
+ while (lo <= hi) {
+ int mid = (lo + hi) >> 1;
+
+ s4 foundVal = s4FromSwitchData(&keys[mid]);
+ if (testVal < foundVal) {
+ hi = mid - 1;
+ } else if (testVal > foundVal) {
+ lo = mid + 1;
+ } else {
+ LOGVV("Value %d found in entry %d (goto 0x%02x)\n",
+ testVal, mid, s4FromSwitchData(&entries[mid]));
+ return s4FromSwitchData(&entries[mid]);
+ }
+ }
+
+ LOGVV("Value %d not found in switch\n", testVal);
+ return kInstrLen;
+}
+
+/*
+ * Copy data for a fill-array-data instruction. On a little-endian machine
+ * we can just do a memcpy(), on a big-endian system we have work to do.
+ *
+ * The trick here is that dexopt has byte-swapped each code unit, which is
+ * exactly what we want for short/char data. For byte data we need to undo
+ * the swap, and for 4- or 8-byte values we need to swap pieces within
+ * each word.
+ */
+static void copySwappedArrayData(void* dest, const u2* src, u4 size, u2 width)
+{
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ memcpy(dest, src, size*width);
+#else
+ int i;
+
+ switch (width) {
+ case 1:
+ /* un-swap pairs of bytes as we go */
+ for (i = (size-1) & ~1; i >= 0; i -= 2) {
+ ((u1*)dest)[i] = ((u1*)src)[i+1];
+ ((u1*)dest)[i+1] = ((u1*)src)[i];
+ }
+ /*
+ * "src" is padded to end on a two-byte boundary, but we don't want to
+ * assume "dest" is, so we handle odd length specially.
+ */
+ if ((size & 1) != 0) {
+ ((u1*)dest)[size-1] = ((u1*)src)[size];
+ }
+ break;
+ case 2:
+ /* already swapped correctly */
+ memcpy(dest, src, size*width);
+ break;
+ case 4:
+ /* swap word halves */
+ for (i = 0; i < (int) size; i++) {
+ ((u4*)dest)[i] = (src[(i << 1) + 1] << 16) | src[i << 1];
+ }
+ break;
+ case 8:
+ /* swap word halves and words */
+ for (i = 0; i < (int) (size << 1); i += 2) {
+ ((int*)dest)[i] = (src[(i << 1) + 3] << 16) | src[(i << 1) + 2];
+ ((int*)dest)[i+1] = (src[(i << 1) + 1] << 16) | src[i << 1];
+ }
+ break;
+ default:
+ LOGE("Unexpected width %d in copySwappedArrayData\n", width);
+ dvmAbort();
+ break;
+ }
+#endif
+}
+
+/*
+ * Fill the array with predefined constant values.
+ *
+ * Returns true if job is completed, otherwise false to indicate that
+ * an exception has been thrown.
+ */
+bool dvmInterpHandleFillArrayData(ArrayObject* arrayObj, const u2* arrayData)
+{
+ u2 width;
+ u4 size;
+
+ if (arrayObj == NULL) {
+ dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+ return false;
+ }
+ assert (!IS_CLASS_FLAG_SET(((Object *)arrayObj)->clazz,
+ CLASS_ISOBJECTARRAY));
+
+ /*
+ * Array data table format:
+ * ushort ident = 0x0300 magic value
+ * ushort width width of each element in the table
+ * uint size number of elements in the table
+ * ubyte data[size*width] table of data values (may contain a single-byte
+ * padding at the end)
+ *
+ * Total size is 4+(width * size + 1)/2 16-bit code units.
+ */
+ if (arrayData[0] != kArrayDataSignature) {
+ dvmThrowException("Ljava/lang/InternalError;", "bad array data magic");
+ return false;
+ }
+
+ width = arrayData[1];
+ size = arrayData[2] | (((u4)arrayData[3]) << 16);
+
+ if (size > arrayObj->length) {
+ dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", NULL);
+ return false;
+ }
+ copySwappedArrayData(arrayObj->contents, &arrayData[4], size, width);
+ return true;
+}
+
+/*
+ * Find the concrete method that corresponds to "methodIdx". The code in
+ * "method" is executing invoke-method with "thisClass" as its first argument.
+ *
+ * Returns NULL with an exception raised on failure.
+ */
+Method* dvmInterpFindInterfaceMethod(ClassObject* thisClass, u4 methodIdx,
+ const Method* method, DvmDex* methodClassDex)
+{
+ Method* absMethod;
+ Method* methodToCall;
+ int i, vtableIndex;
+
+ /*
+ * Resolve the method. This gives us the abstract method from the
+ * interface class declaration.
+ */
+ absMethod = dvmDexGetResolvedMethod(methodClassDex, methodIdx);
+ if (absMethod == NULL) {
+ absMethod = dvmResolveInterfaceMethod(method->clazz, methodIdx);
+ if (absMethod == NULL) {
+ LOGV("+ unknown method\n");
+ return NULL;
+ }
+ }
+
+ /* make sure absMethod->methodIndex means what we think it means */
+ assert(dvmIsAbstractMethod(absMethod));
+
+ /*
+ * Run through the "this" object's iftable. Find the entry for
+ * absMethod's class, then use absMethod->methodIndex to find
+ * the method's entry. The value there is the offset into our
+ * vtable of the actual method to execute.
+ *
+ * The verifier does not guarantee that objects stored into
+ * interface references actually implement the interface, so this
+ * check cannot be eliminated.
+ */
+ for (i = 0; i < thisClass->iftableCount; i++) {
+ if (thisClass->iftable[i].clazz == absMethod->clazz)
+ break;
+ }
+ if (i == thisClass->iftableCount) {
+ /* impossible in verified DEX, need to check for it in unverified */
+ dvmThrowException("Ljava/lang/IncompatibleClassChangeError;",
+ "interface not implemented");
+ return NULL;
+ }
+
+ assert(absMethod->methodIndex <
+ thisClass->iftable[i].clazz->virtualMethodCount);
+
+ vtableIndex =
+ thisClass->iftable[i].methodIndexArray[absMethod->methodIndex];
+ assert(vtableIndex >= 0 && vtableIndex < thisClass->vtableCount);
+ methodToCall = thisClass->vtable[vtableIndex];
+
+#if 0
+ /* this can happen when there's a stale class file */
+ if (dvmIsAbstractMethod(methodToCall)) {
+ dvmThrowException("Ljava/lang/AbstractMethodError;",
+ "interface method not implemented");
+ return NULL;
+ }
+#else
+ assert(!dvmIsAbstractMethod(methodToCall) ||
+ methodToCall->nativeFunc != NULL);
+#endif
+
+ LOGVV("+++ interface=%s.%s concrete=%s.%s\n",
+ absMethod->clazz->descriptor, absMethod->name,
+ methodToCall->clazz->descriptor, methodToCall->name);
+ assert(methodToCall != NULL);
+
+ return methodToCall;
+}
+
+
+
+/*
+ * Helpers for dvmThrowVerificationError().
+ *
+ * Each returns a newly-allocated string.
+ */
+#define kThrowShow_accessFromClass 1
+static char* classNameFromIndex(const Method* method, int ref,
+ VerifyErrorRefType refType, int flags)
+{
+ static const int kBufLen = 256;
+ const DvmDex* pDvmDex = method->clazz->pDvmDex;
+
+ if (refType == VERIFY_ERROR_REF_FIELD) {
+ /* get class ID from field ID */
+ const DexFieldId* pFieldId = dexGetFieldId(pDvmDex->pDexFile, ref);
+ ref = pFieldId->classIdx;
+ } else if (refType == VERIFY_ERROR_REF_METHOD) {
+ /* get class ID from method ID */
+ const DexMethodId* pMethodId = dexGetMethodId(pDvmDex->pDexFile, ref);
+ ref = pMethodId->classIdx;
+ }
+
+ const char* className = dexStringByTypeIdx(pDvmDex->pDexFile, ref);
+ char* dotClassName = dvmDescriptorToDot(className);
+ if (flags == 0)
+ return dotClassName;
+
+ char* result = (char*) malloc(kBufLen);
+
+ if ((flags & kThrowShow_accessFromClass) != 0) {
+ char* dotFromName = dvmDescriptorToDot(method->clazz->descriptor);
+ snprintf(result, kBufLen, "tried to access class %s from class %s",
+ dotClassName, dotFromName);
+ free(dotFromName);
+ } else {
+ assert(false); // should've been caught above
+ result[0] = '\0';
+ }
+
+ free(dotClassName);
+ return result;
+}
+static char* fieldNameFromIndex(const Method* method, int ref,
+ VerifyErrorRefType refType, int flags)
+{
+ static const int kBufLen = 256;
+ const DvmDex* pDvmDex = method->clazz->pDvmDex;
+ const DexFieldId* pFieldId;
+ const char* className;
+ const char* fieldName;
+
+ if (refType != VERIFY_ERROR_REF_FIELD) {
+ LOGW("Expected ref type %d, got %d\n", VERIFY_ERROR_REF_FIELD, refType);
+ return NULL; /* no message */
+ }
+
+ pFieldId = dexGetFieldId(pDvmDex->pDexFile, ref);
+ className = dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->classIdx);
+ fieldName = dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx);
+
+ char* dotName = dvmDescriptorToDot(className);
+ char* result = (char*) malloc(kBufLen);
+
+ if ((flags & kThrowShow_accessFromClass) != 0) {
+ char* dotFromName = dvmDescriptorToDot(method->clazz->descriptor);
+ snprintf(result, kBufLen, "tried to access field %s.%s from class %s",
+ dotName, fieldName, dotFromName);
+ free(dotFromName);
+ } else {
+ snprintf(result, kBufLen, "%s.%s", dotName, fieldName);
+ }
+
+ free(dotName);
+ return result;
+}
+static char* methodNameFromIndex(const Method* method, int ref,
+ VerifyErrorRefType refType, int flags)
+{
+ static const int kBufLen = 384;
+ const DvmDex* pDvmDex = method->clazz->pDvmDex;
+ const DexMethodId* pMethodId;
+ const char* className;
+ const char* methodName;
+
+ if (refType != VERIFY_ERROR_REF_METHOD) {
+ LOGW("Expected ref type %d, got %d\n", VERIFY_ERROR_REF_METHOD,refType);
+ return NULL; /* no message */
+ }
+
+ pMethodId = dexGetMethodId(pDvmDex->pDexFile, ref);
+ className = dexStringByTypeIdx(pDvmDex->pDexFile, pMethodId->classIdx);
+ methodName = dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx);
+
+ char* dotName = dvmDescriptorToDot(className);
+ char* result = (char*) malloc(kBufLen);
+
+ if ((flags & kThrowShow_accessFromClass) != 0) {
+ char* dotFromName = dvmDescriptorToDot(method->clazz->descriptor);
+ char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+ snprintf(result, kBufLen,
+ "tried to access method %s.%s:%s from class %s",
+ dotName, methodName, desc, dotFromName);
+ free(dotFromName);
+ free(desc);
+ } else {
+ snprintf(result, kBufLen, "%s.%s", dotName, methodName);
+ }
+
+ free(dotName);
+ return result;
+}
+
+/*
+ * Throw an exception for a problem identified by the verifier.
+ *
+ * This is used by the invoke-verification-error instruction. It always
+ * throws an exception.
+ *
+ * "kind" indicates the kind of failure encountered by the verifier. It
+ * has two parts, an error code and an indication of the reference type.
+ */
+void dvmThrowVerificationError(const Method* method, int kind, int ref)
+{
+ const int typeMask = 0xff << kVerifyErrorRefTypeShift;
+ VerifyError errorKind = kind & ~typeMask;
+ VerifyErrorRefType refType = kind >> kVerifyErrorRefTypeShift;
+ const char* exceptionName = "Ljava/lang/VerifyError;";
+ char* msg = NULL;
+
+ switch ((VerifyError) errorKind) {
+ case VERIFY_ERROR_NO_CLASS:
+ exceptionName = "Ljava/lang/NoClassDefFoundError;";
+ msg = classNameFromIndex(method, ref, refType, 0);
+ break;
+ case VERIFY_ERROR_NO_FIELD:
+ exceptionName = "Ljava/lang/NoSuchFieldError;";
+ msg = fieldNameFromIndex(method, ref, refType, 0);
+ break;
+ case VERIFY_ERROR_NO_METHOD:
+ exceptionName = "Ljava/lang/NoSuchMethodError;";
+ msg = methodNameFromIndex(method, ref, refType, 0);
+ break;
+ case VERIFY_ERROR_ACCESS_CLASS:
+ exceptionName = "Ljava/lang/IllegalAccessError;";
+ msg = classNameFromIndex(method, ref, refType,
+ kThrowShow_accessFromClass);
+ break;
+ case VERIFY_ERROR_ACCESS_FIELD:
+ exceptionName = "Ljava/lang/IllegalAccessError;";
+ msg = fieldNameFromIndex(method, ref, refType,
+ kThrowShow_accessFromClass);
+ break;
+ case VERIFY_ERROR_ACCESS_METHOD:
+ exceptionName = "Ljava/lang/IllegalAccessError;";
+ msg = methodNameFromIndex(method, ref, refType,
+ kThrowShow_accessFromClass);
+ break;
+ case VERIFY_ERROR_CLASS_CHANGE:
+ exceptionName = "Ljava/lang/IncompatibleClassChangeError;";
+ msg = classNameFromIndex(method, ref, refType, 0);
+ break;
+ case VERIFY_ERROR_INSTANTIATION:
+ exceptionName = "Ljava/lang/InstantiationError;";
+ msg = classNameFromIndex(method, ref, refType, 0);
+ break;
+
+ case VERIFY_ERROR_GENERIC:
+ /* generic VerifyError; use default exception, no message */
+ break;
+ case VERIFY_ERROR_NONE:
+ /* should never happen; use default exception */
+ assert(false);
+ msg = strdup("weird - no error specified");
+ break;
+
+ /* no default clause -- want warning if enum updated */
+ }
+
+ dvmThrowException(exceptionName, msg);
+ free(msg);
+}
+
+/*
+ * Main interpreter loop entry point. Select "standard" or "debug"
+ * interpreter and switch between them as required.
+ *
+ * This begins executing code at the start of "method". On exit, "pResult"
+ * holds the return value of the method (or, if "method" returns NULL, it
+ * holds an undefined value).
+ *
+ * The interpreted stack frame, which holds the method arguments, has
+ * already been set up.
+ */
+void dvmInterpret(Thread* self, const Method* method, JValue* pResult)
+{
+ InterpState interpState;
+ bool change;
+#if defined(WITH_JIT)
+ /* Target-specific save/restore */
+ extern void dvmJitCalleeSave(double *saveArea);
+ extern void dvmJitCalleeRestore(double *saveArea);
+ /* Interpreter entry points from compiled code */
+ extern void dvmJitToInterpNormal();
+ extern void dvmJitToInterpNoChain();
+ extern void dvmJitToInterpPunt();
+ extern void dvmJitToInterpSingleStep();
+ extern void dvmJitToInterpTraceSelectNoChain();
+ extern void dvmJitToInterpTraceSelect();
+ extern void dvmJitToPatchPredictedChain();
+#if defined(WITH_SELF_VERIFICATION)
+ extern void dvmJitToInterpBackwardBranch();
+#endif
+
+ /*
+ * Reserve a static entity here to quickly setup runtime contents as
+ * gcc will issue block copy instructions.
+ */
+ static struct JitToInterpEntries jitToInterpEntries = {
+ dvmJitToInterpNormal,
+ dvmJitToInterpNoChain,
+ dvmJitToInterpPunt,
+ dvmJitToInterpSingleStep,
+ dvmJitToInterpTraceSelectNoChain,
+ dvmJitToInterpTraceSelect,
+ dvmJitToPatchPredictedChain,
+#if defined(WITH_SELF_VERIFICATION)
+ dvmJitToInterpBackwardBranch,
+#endif
+ };
+
+ /*
+ * If the previous VM left the code cache through single-stepping the
+ * inJitCodeCache flag will be set when the VM is re-entered (for example,
+ * in self-verification mode we single-step NEW_INSTANCE which may re-enter
+ * the VM through findClassFromLoaderNoInit). Because of that, we cannot
+ * assert that self->inJitCodeCache is NULL here.
+ */
+#endif
+
+
+#if defined(WITH_TRACKREF_CHECKS)
+ interpState.debugTrackedRefStart =
+ dvmReferenceTableEntries(&self->internalLocalRefTable);
+#endif
+ interpState.debugIsMethodEntry = true;
+#if defined(WITH_JIT)
+ dvmJitCalleeSave(interpState.calleeSave);
+ /* Initialize the state to kJitNot */
+ interpState.jitState = kJitNot;
+
+ /* Setup the Jit-to-interpreter entry points */
+ interpState.jitToInterpEntries = jitToInterpEntries;
+
+ /*
+ * Initialize the threshold filter [don't bother to zero out the
+ * actual table. We're looking for matches, and an occasional
+ * false positive is acceptible.
+ */
+ interpState.lastThreshFilter = 0;
+
+ interpState.icRechainCount = PREDICTED_CHAIN_COUNTER_RECHAIN;
+#endif
+
+ /*
+ * Initialize working state.
+ *
+ * No need to initialize "retval".
+ */
+ interpState.method = method;
+ interpState.fp = (u4*) self->curFrame;
+ interpState.pc = method->insns;
+ interpState.entryPoint = kInterpEntryInstr;
+
+ if (dvmDebuggerOrProfilerActive())
+ interpState.nextMode = INTERP_DBG;
+ else
+ interpState.nextMode = INTERP_STD;
+
+ assert(!dvmIsNativeMethod(method));
+
+ /*
+ * Make sure the class is ready to go. Shouldn't be possible to get
+ * here otherwise.
+ */
+ if (method->clazz->status < CLASS_INITIALIZING ||
+ method->clazz->status == CLASS_ERROR)
+ {
+ LOGE("ERROR: tried to execute code in unprepared class '%s' (%d)\n",
+ method->clazz->descriptor, method->clazz->status);
+ dvmDumpThread(self, false);
+ dvmAbort();
+ }
+
+ typedef bool (*Interpreter)(Thread*, InterpState*);
+ Interpreter stdInterp;
+ if (gDvm.executionMode == kExecutionModeInterpFast)
+ stdInterp = dvmMterpStd;
+#if defined(WITH_JIT)
+ else if (gDvm.executionMode == kExecutionModeJit)
+/* If profiling overhead can be kept low enough, we can use a profiling
+ * mterp fast for both Jit and "fast" modes. If overhead is too high,
+ * create a specialized profiling interpreter.
+ */
+ stdInterp = dvmMterpStd;
+#endif
+ else
+ stdInterp = dvmInterpretStd;
+
+ change = true;
+ while (change) {
+ switch (interpState.nextMode) {
+ case INTERP_STD:
+ LOGVV("threadid=%d: interp STD\n", self->threadId);
+ change = (*stdInterp)(self, &interpState);
+ break;
+ case INTERP_DBG:
+ LOGVV("threadid=%d: interp DBG\n", self->threadId);
+ change = dvmInterpretDbg(self, &interpState);
+ break;
+ default:
+ dvmAbort();
+ }
+ }
+
+ *pResult = interpState.retval;
+#if defined(WITH_JIT)
+ dvmJitCalleeRestore(interpState.calleeSave);
+#endif
+}
diff --git a/vm/interp/Interp.h b/vm/interp/Interp.h
new file mode 100644
index 0000000..76d7d29
--- /dev/null
+++ b/vm/interp/Interp.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik interpreter public definitions.
+ */
+#ifndef _DALVIK_INTERP_INTERP
+#define _DALVIK_INTERP_INTERP
+
+/*
+ * Interpreter entry point. Call here after setting up the interpreted
+ * stack (most code will want to get here via dvmCallMethod().)
+ */
+void dvmInterpret(Thread* thread, const Method* method, JValue* pResult);
+
+/*
+ * Throw an exception for a problem detected by the verifier.
+ *
+ * This is called from the handler for the throw-verification-error
+ * instruction. "method" is the method currently being executed.
+ */
+void dvmThrowVerificationError(const Method* method, int kind, int ref);
+
+/*
+ * One-time initialization and shutdown.
+ */
+bool dvmBreakpointStartup(void);
+void dvmBreakpointShutdown(void);
+
+/*
+ * Breakpoint implementation.
+ */
+void dvmInitBreakpoints();
+void dvmAddBreakAddr(Method* method, unsigned int instrOffset);
+void dvmClearBreakAddr(Method* method, unsigned int instrOffset);
+bool dvmAddSingleStep(Thread* thread, int size, int depth);
+void dvmClearSingleStep(Thread* thread);
+
+/*
+ * Recover the opcode that was replaced by a breakpoint.
+ */
+u1 dvmGetOriginalOpCode(const u2* addr);
+
+/*
+ * Flush any breakpoints associated with methods in "clazz".
+ */
+void dvmFlushBreakpoints(ClassObject* clazz);
+
+#endif /*_DALVIK_INTERP_INTERP*/
diff --git a/vm/interp/InterpDefs.h b/vm/interp/InterpDefs.h
new file mode 100644
index 0000000..4ccdee3
--- /dev/null
+++ b/vm/interp/InterpDefs.h
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Dalvik interpreter definitions. These are internal to the interpreter.
+ *
+ * This includes defines, types, function declarations, and inline functions
+ * that are common to all interpreter implementations.
+ *
+ * Functions and globals declared here are defined in Interp.c.
+ */
+#ifndef _DALVIK_INTERP_DEFS
+#define _DALVIK_INTERP_DEFS
+
+
+/*
+ * Specify the starting point when switching between interpreters.
+ */
+typedef enum InterpEntry {
+ kInterpEntryInstr = 0, // continue to next instruction
+ kInterpEntryReturn = 1, // jump to method return
+ kInterpEntryThrow = 2, // jump to exception throw
+#if defined(WITH_JIT)
+ kInterpEntryResume = 3, // Resume after single-step
+#endif
+} InterpEntry;
+
+#if defined(WITH_JIT)
+/*
+ * There are six entry points from the compiled code to the interpreter:
+ * 1) dvmJitToInterpNormal: find if there is a corresponding compilation for
+ * the new dalvik PC. If so, chain the originating compilation with the
+ * target then jump to it.
+ * 2) dvmJitToInterpInvokeNoChain: similar to 1) but don't chain. This is
+ * for handling 1-to-many mappings like virtual method call and
+ * packed switch.
+ * 3) dvmJitToInterpPunt: use the fast interpreter to execute the next
+ * instruction(s) and stay there as long as it is appropriate to return
+ * to the compiled land. This is used when the jit'ed code is about to
+ * throw an exception.
+ * 4) dvmJitToInterpSingleStep: use the portable interpreter to execute the
+ * next instruction only and return to pre-specified location in the
+ * compiled code to resume execution. This is mainly used as debugging
+ * feature to bypass problematic opcode implementations without
+ * disturbing the trace formation.
+ * 5) dvmJitToTraceSelect: if there is a single exit from a translation that
+ * has already gone hot enough to be translated, we should assume that
+ * the exit point should also be translated (this is a common case for
+ * invokes). This trace exit will first check for a chaining
+ * opportunity, and if none is available will switch to the debug
+ * interpreter immediately for trace selection (as if threshold had
+ * just been reached).
+ * 6) dvmJitToPredictedChain: patch the chaining cell for a virtual call site
+ * to a predicted callee.
+ * 7) dvmJitToBackwardBranch: (WITH_SELF_VERIFICATION ONLY) special case of 1)
+ * and 5). This is used instead if the ending branch of the trace jumps back
+ * into the same basic block.
+ */
+struct JitToInterpEntries {
+ void *dvmJitToInterpNormal;
+ void *dvmJitToInterpNoChain;
+ void *dvmJitToInterpPunt;
+ void *dvmJitToInterpSingleStep;
+ void *dvmJitToInterpTraceSelectNoChain;
+ void *dvmJitToInterpTraceSelect;
+ void *dvmJitToPatchPredictedChain;
+#if defined(WITH_SELF_VERIFICATION)
+ void *dvmJitToInterpBackwardBranch;
+#endif
+};
+
+/*
+ * Size of save area for callee-save FP regs, which are not automatically
+ * saved by interpreter main because it doesn't use them (but Jit'd code
+ * may). Save/restore routine is defined by target, and size should
+ * be >= max needed by any target.
+ */
+#define JIT_CALLEE_SAVE_DOUBLE_COUNT 8
+
+/* Number of entries in the 2nd level JIT profiler filter cache */
+#define JIT_TRACE_THRESH_FILTER_SIZE 32
+/* Number of low dalvik pc address bits to include in 2nd level filter key */
+#define JIT_TRACE_THRESH_FILTER_PC_BITS 4
+#endif
+
+/*
+ * Interpreter context, used when switching from one interpreter to
+ * another. We also tuck "mterp" state in here.
+ */
+typedef struct InterpState {
+ /*
+ * To make some mterp state updates easier, "pc" and "fp" MUST come
+ * first and MUST appear in this order.
+ */
+ const u2* pc; // program counter
+ u4* fp; // frame pointer
+
+ JValue retval; // return value -- "out" only
+ const Method* method; // method being executed
+
+
+ /* ----------------------------------------------------------------------
+ * Mterp-only state
+ */
+ DvmDex* methodClassDex;
+ Thread* self;
+
+ /* housekeeping */
+ void* bailPtr;
+
+ /*
+ * These are available globally, from gDvm, or from another glue field
+ * (self/method). They're copied in here for speed.
+ */
+ /* copy of self->interpStackEnd */
+ const u1* interpStackEnd;
+ /* points at self->suspendCount */
+ volatile int* pSelfSuspendCount;
+ /* Biased base of GC's card table */
+ u1* cardTable;
+ /* points at gDvm.debuggerActive, or NULL if debugger not enabled */
+ volatile u1* pDebuggerActive;
+ /* points at gDvm.activeProfilers */
+ volatile int* pActiveProfilers;
+ /* ----------------------------------------------------------------------
+ */
+
+ /*
+ * Interpreter switching.
+ */
+ InterpEntry entryPoint; // what to do when we start
+ int nextMode; // INTERP_STD, INTERP_DBG
+
+#if defined(WITH_JIT)
+ /*
+ * Local copies of field from gDvm placed here for fast access
+ */
+ unsigned char* pJitProfTable;
+ JitState jitState;
+ const void* jitResumeNPC; // Native PC of compiled code
+ const u2* jitResumeDPC; // Dalvik PC corresponding to NPC
+ int jitThreshold;
+ /*
+ * ppJitProfTable holds the address of gDvmJit.pJitProfTable, which
+ * doubles as an on/off switch for the Jit. Because a change in
+ * the value of gDvmJit.pJitProfTable isn't reflected in the cached
+ * copy above (pJitProfTable), we need to periodically refresh it.
+ * ppJitProfTable is used for that purpose.
+ */
+ unsigned char** ppJitProfTable; // Used to refresh pJitProfTable
+ int icRechainCount; // Count down to next rechain request
+#endif
+
+ bool debugIsMethodEntry; // used for method entry event triggers
+#if defined(WITH_TRACKREF_CHECKS)
+ int debugTrackedRefStart; // tracked refs from prior invocations
+#endif
+
+#if defined(WITH_JIT)
+ struct JitToInterpEntries jitToInterpEntries;
+
+ int currTraceRun;
+ int totalTraceLen; // Number of Dalvik insts in trace
+ const u2* currTraceHead; // Start of the trace we're building
+ const u2* currRunHead; // Start of run we're building
+ int currRunLen; // Length of run in 16-bit words
+ int lastThreshFilter;
+ const u2* lastPC; // Stage the PC first for the threaded interpreter
+ intptr_t threshFilter[JIT_TRACE_THRESH_FILTER_SIZE];
+ JitTraceRun trace[MAX_JIT_RUN_LEN];
+ double calleeSave[JIT_CALLEE_SAVE_DOUBLE_COUNT];
+#endif
+
+} InterpState;
+
+/*
+ * These are generated from InterpCore.h.
+ */
+extern bool dvmInterpretDbg(Thread* self, InterpState* interpState);
+extern bool dvmInterpretStd(Thread* self, InterpState* interpState);
+#define INTERP_STD 0
+#define INTERP_DBG 1
+
+/*
+ * "mterp" interpreter.
+ */
+extern bool dvmMterpStd(Thread* self, InterpState* interpState);
+
+/*
+ * Get the "this" pointer from the current frame.
+ */
+Object* dvmGetThisPtr(const Method* method, const u4* fp);
+
+/*
+ * Verify that our tracked local references are valid.
+ */
+void dvmInterpCheckTrackedRefs(Thread* self, const Method* method,
+ int debugTrackedRefStart);
+
+/*
+ * Process switch statement.
+ */
+s4 dvmInterpHandlePackedSwitch(const u2* switchData, s4 testVal);
+s4 dvmInterpHandleSparseSwitch(const u2* switchData, s4 testVal);
+
+/*
+ * Process fill-array-data.
+ */
+bool dvmInterpHandleFillArrayData(ArrayObject* arrayObject,
+ const u2* arrayData);
+
+/*
+ * Find an interface method.
+ */
+Method* dvmInterpFindInterfaceMethod(ClassObject* thisClass, u4 methodIdx,
+ const Method* method, DvmDex* methodClassDex);
+
+/*
+ * Determine if the debugger or profiler is currently active. Used when
+ * selecting which interpreter to start or switch to.
+ */
+static inline bool dvmDebuggerOrProfilerActive(void)
+{
+ return gDvm.debuggerActive || gDvm.activeProfilers != 0;
+}
+
+#if defined(WITH_JIT)
+/*
+ * Determine if the jit, debugger or profiler is currently active. Used when
+ * selecting which interpreter to switch to.
+ */
+static inline bool dvmJitDebuggerOrProfilerActive()
+{
+ return gDvmJit.pProfTable != NULL
+ || gDvm.activeProfilers != 0
+ || gDvm.debuggerActive;
+}
+
+/*
+ * Hide the translations and stick with the interpreter as long as one of the
+ * following conditions is true.
+ */
+static inline bool dvmJitHideTranslation()
+{
+ return (gDvm.sumThreadSuspendCount != 0) ||
+ (gDvmJit.codeCacheFull == true) ||
+ (gDvmJit.pProfTable == NULL);
+}
+
+/*
+ * The fast and debug interpreter may be doing ping-pong without making forward
+ * progress if the same trace building request sent upon entering the fast
+ * interpreter is rejected immediately by the debug interpreter. Use the
+ * following function to poll the rejection reasons and stay in the debug
+ * interpreter until they are cleared. This will guarantee forward progress
+ * in the extreme corner cases (eg set compiler threashold to 1).
+ */
+static inline bool dvmJitStayInPortableInterpreter()
+{
+ return dvmJitHideTranslation() ||
+ (gDvmJit.compilerQueueLength >= gDvmJit.compilerHighWater);
+}
+#endif
+
+#endif /*_DALVIK_INTERP_DEFS*/
diff --git a/vm/interp/Jit.c b/vm/interp/Jit.c
new file mode 100644
index 0000000..c800fe5
--- /dev/null
+++ b/vm/interp/Jit.c
@@ -0,0 +1,1352 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifdef WITH_JIT
+
+/*
+ * Target independent portion of Android's Jit
+ */
+
+#include "Dalvik.h"
+#include "Jit.h"
+
+
+#include "libdex/OpCodeNames.h"
+#include <unistd.h>
+#include <pthread.h>
+#include <sys/time.h>
+#include <signal.h>
+#include "compiler/Compiler.h"
+#include "compiler/CompilerUtility.h"
+#include "compiler/CompilerIR.h"
+#include <errno.h>
+
+#if defined(WITH_SELF_VERIFICATION)
+/* Allocate space for per-thread ShadowSpace data structures */
+void* dvmSelfVerificationShadowSpaceAlloc(Thread* self)
+{
+ self->shadowSpace = (ShadowSpace*) calloc(1, sizeof(ShadowSpace));
+ if (self->shadowSpace == NULL)
+ return NULL;
+
+ self->shadowSpace->registerSpaceSize = REG_SPACE;
+ self->shadowSpace->registerSpace =
+ (int*) calloc(self->shadowSpace->registerSpaceSize, sizeof(int));
+
+ return self->shadowSpace->registerSpace;
+}
+
+/* Free per-thread ShadowSpace data structures */
+void dvmSelfVerificationShadowSpaceFree(Thread* self)
+{
+ free(self->shadowSpace->registerSpace);
+ free(self->shadowSpace);
+}
+
+/*
+ * Save out PC, FP, InterpState, and registers to shadow space.
+ * Return a pointer to the shadow space for JIT to use.
+ */
+void* dvmSelfVerificationSaveState(const u2* pc, const void* fp,
+ InterpState* interpState, int targetTrace)
+{
+ Thread *self = dvmThreadSelf();
+ ShadowSpace *shadowSpace = self->shadowSpace;
+ unsigned preBytes = interpState->method->outsSize*4 + sizeof(StackSaveArea);
+ unsigned postBytes = interpState->method->registersSize*4;
+
+ //LOGD("### selfVerificationSaveState(%d) pc: 0x%x fp: 0x%x",
+ // self->threadId, (int)pc, (int)fp);
+
+ if (shadowSpace->selfVerificationState != kSVSIdle) {
+ LOGD("~~~ Save: INCORRECT PREVIOUS STATE(%d): %d",
+ self->threadId, shadowSpace->selfVerificationState);
+ LOGD("********** SHADOW STATE DUMP **********");
+ LOGD("PC: 0x%x FP: 0x%x", (int)pc, (int)fp);
+ }
+ shadowSpace->selfVerificationState = kSVSStart;
+
+ if (interpState->entryPoint == kInterpEntryResume) {
+ interpState->entryPoint = kInterpEntryInstr;
+#if 0
+ /* Tracking the success rate of resume after single-stepping */
+ if (interpState->jitResumeDPC == pc) {
+ LOGD("SV single step resumed at %p", pc);
+ }
+ else {
+ LOGD("real %p DPC %p NPC %p", pc, interpState->jitResumeDPC,
+ interpState->jitResumeNPC);
+ }
+#endif
+ }
+
+ // Dynamically grow shadow register space if necessary
+ if (preBytes + postBytes > shadowSpace->registerSpaceSize * sizeof(u4)) {
+ free(shadowSpace->registerSpace);
+ shadowSpace->registerSpaceSize = (preBytes + postBytes) / sizeof(u4);
+ shadowSpace->registerSpace =
+ (int*) calloc(shadowSpace->registerSpaceSize, sizeof(u4));
+ }
+
+ // Remember original state
+ shadowSpace->startPC = pc;
+ shadowSpace->fp = fp;
+ shadowSpace->glue = interpState;
+ /*
+ * Store the original method here in case the trace ends with a
+ * return/invoke, the last method.
+ */
+ shadowSpace->method = interpState->method;
+ shadowSpace->shadowFP = shadowSpace->registerSpace +
+ shadowSpace->registerSpaceSize - postBytes/4;
+
+ // Create a copy of the InterpState
+ memcpy(&(shadowSpace->interpState), interpState, sizeof(InterpState));
+ shadowSpace->interpState.fp = shadowSpace->shadowFP;
+ shadowSpace->interpState.interpStackEnd = (u1*)shadowSpace->registerSpace;
+
+ // Create a copy of the stack
+ memcpy(((char*)shadowSpace->shadowFP)-preBytes, ((char*)fp)-preBytes,
+ preBytes+postBytes);
+
+ // Setup the shadowed heap space
+ shadowSpace->heapSpaceTail = shadowSpace->heapSpace;
+
+ // Reset trace length
+ shadowSpace->traceLength = 0;
+
+ return shadowSpace;
+}
+
+/*
+ * Save ending PC, FP and compiled code exit point to shadow space.
+ * Return a pointer to the shadow space for JIT to restore state.
+ */
+void* dvmSelfVerificationRestoreState(const u2* pc, const void* fp,
+ SelfVerificationState exitState)
+{
+ Thread *self = dvmThreadSelf();
+ ShadowSpace *shadowSpace = self->shadowSpace;
+ // Official InterpState structure
+ InterpState *realGlue = shadowSpace->glue;
+ shadowSpace->endPC = pc;
+ shadowSpace->endShadowFP = fp;
+ shadowSpace->jitExitState = exitState;
+
+ //LOGD("### selfVerificationRestoreState(%d) pc: 0x%x fp: 0x%x endPC: 0x%x",
+ // self->threadId, (int)shadowSpace->startPC, (int)shadowSpace->fp,
+ // (int)pc);
+
+ if (shadowSpace->selfVerificationState != kSVSStart) {
+ LOGD("~~~ Restore: INCORRECT PREVIOUS STATE(%d): %d",
+ self->threadId, shadowSpace->selfVerificationState);
+ LOGD("********** SHADOW STATE DUMP **********");
+ LOGD("Dalvik PC: 0x%x endPC: 0x%x", (int)shadowSpace->startPC,
+ (int)shadowSpace->endPC);
+ LOGD("Interp FP: 0x%x", (int)shadowSpace->fp);
+ LOGD("Shadow FP: 0x%x endFP: 0x%x", (int)shadowSpace->shadowFP,
+ (int)shadowSpace->endShadowFP);
+ }
+
+ // Move the resume [ND]PC from the shadow space to the real space so that
+ // the debug interpreter can return to the translation
+ if (exitState == kSVSSingleStep) {
+ realGlue->jitResumeNPC = shadowSpace->interpState.jitResumeNPC;
+ realGlue->jitResumeDPC = shadowSpace->interpState.jitResumeDPC;
+ } else {
+ realGlue->jitResumeNPC = NULL;
+ realGlue->jitResumeDPC = NULL;
+ }
+
+ // Special case when punting after a single instruction
+ if (exitState == kSVSPunt && pc == shadowSpace->startPC) {
+ shadowSpace->selfVerificationState = kSVSIdle;
+ } else if (exitState == kSVSBackwardBranch && pc < shadowSpace->startPC) {
+ /*
+ * Consider a trace with a backward branch:
+ * 1: ..
+ * 2: ..
+ * 3: ..
+ * 4: ..
+ * 5: Goto {1 or 2 or 3 or 4}
+ *
+ * If there instruction 5 goes to 1 and there is no single-step
+ * instruction in the loop, pc is equal to shadowSpace->startPC and
+ * we will honor the backward branch condition.
+ *
+ * If the single-step instruction is outside the loop, then after
+ * resuming in the trace the startPC will be less than pc so we will
+ * also honor the backward branch condition.
+ *
+ * If the single-step is inside the loop, we won't hit the same endPC
+ * twice when the interpreter is re-executing the trace so we want to
+ * cancel the backward branch condition. In this case it can be
+ * detected as the endPC (ie pc) will be less than startPC.
+ */
+ shadowSpace->selfVerificationState = kSVSNormal;
+ } else {
+ shadowSpace->selfVerificationState = exitState;
+ }
+
+ return shadowSpace;
+}
+
+/* Print contents of virtual registers */
+static void selfVerificationPrintRegisters(int* addr, int* addrRef,
+ int numWords)
+{
+ int i;
+ for (i = 0; i < numWords; i++) {
+ LOGD("(v%d) 0x%8x%s", i, addr[i], addr[i] != addrRef[i] ? " X" : "");
+ }
+}
+
+/* Print values maintained in shadowSpace */
+static void selfVerificationDumpState(const u2* pc, Thread* self)
+{
+ ShadowSpace* shadowSpace = self->shadowSpace;
+ StackSaveArea* stackSave = SAVEAREA_FROM_FP(self->curFrame);
+ int frameBytes = (int) shadowSpace->registerSpace +
+ shadowSpace->registerSpaceSize*4 -
+ (int) shadowSpace->shadowFP;
+ int localRegs = 0;
+ int frameBytes2 = 0;
+ if (self->curFrame < shadowSpace->fp) {
+ localRegs = (stackSave->method->registersSize -
+ stackSave->method->insSize)*4;
+ frameBytes2 = (int) shadowSpace->fp - (int) self->curFrame - localRegs;
+ }
+ LOGD("********** SHADOW STATE DUMP **********");
+ LOGD("CurrentPC: 0x%x, Offset: 0x%04x", (int)pc,
+ (int)(pc - stackSave->method->insns));
+ LOGD("Class: %s", shadowSpace->method->clazz->descriptor);
+ LOGD("Method: %s", shadowSpace->method->name);
+ LOGD("Dalvik PC: 0x%x endPC: 0x%x", (int)shadowSpace->startPC,
+ (int)shadowSpace->endPC);
+ LOGD("Interp FP: 0x%x endFP: 0x%x", (int)shadowSpace->fp,
+ (int)self->curFrame);
+ LOGD("Shadow FP: 0x%x endFP: 0x%x", (int)shadowSpace->shadowFP,
+ (int)shadowSpace->endShadowFP);
+ LOGD("Frame1 Bytes: %d Frame2 Local: %d Bytes: %d", frameBytes,
+ localRegs, frameBytes2);
+ LOGD("Trace length: %d State: %d", shadowSpace->traceLength,
+ shadowSpace->selfVerificationState);
+}
+
+/* Print decoded instructions in the current trace */
+static void selfVerificationDumpTrace(const u2* pc, Thread* self)
+{
+ ShadowSpace* shadowSpace = self->shadowSpace;
+ StackSaveArea* stackSave = SAVEAREA_FROM_FP(self->curFrame);
+ int i, addr, offset;
+ DecodedInstruction *decInsn;
+
+ LOGD("********** SHADOW TRACE DUMP **********");
+ for (i = 0; i < shadowSpace->traceLength; i++) {
+ addr = shadowSpace->trace[i].addr;
+ offset = (int)((u2*)addr - stackSave->method->insns);
+ decInsn = &(shadowSpace->trace[i].decInsn);
+ /* Not properly decoding instruction, some registers may be garbage */
+ LOGD("0x%x: (0x%04x) %s",
+ addr, offset, dexGetOpcodeName(decInsn->opCode));
+ }
+}
+
+/* Code is forced into this spin loop when a divergence is detected */
+static void selfVerificationSpinLoop(ShadowSpace *shadowSpace)
+{
+ const u2 *startPC = shadowSpace->startPC;
+ JitTraceDescription* desc = dvmCopyTraceDescriptor(startPC, NULL);
+ if (desc) {
+ dvmCompilerWorkEnqueue(startPC, kWorkOrderTraceDebug, desc);
+ /*
+ * This function effectively terminates the VM right here, so not
+ * freeing the desc pointer when the enqueuing fails is acceptable.
+ */
+ }
+ gDvmJit.selfVerificationSpin = true;
+ while(gDvmJit.selfVerificationSpin) sleep(10);
+}
+
+/* Manage self verification while in the debug interpreter */
+static bool selfVerificationDebugInterp(const u2* pc, Thread* self,
+ InterpState *interpState)
+{
+ ShadowSpace *shadowSpace = self->shadowSpace;
+ SelfVerificationState state = shadowSpace->selfVerificationState;
+
+ DecodedInstruction decInsn;
+ dexDecodeInstruction(gDvm.instrFormat, pc, &decInsn);
+
+ //LOGD("### DbgIntp(%d): PC: 0x%x endPC: 0x%x state: %d len: %d %s",
+ // self->threadId, (int)pc, (int)shadowSpace->endPC, state,
+ // shadowSpace->traceLength, dexGetOpcodeName(decInsn.opCode));
+
+ if (state == kSVSIdle || state == kSVSStart) {
+ LOGD("~~~ DbgIntrp: INCORRECT PREVIOUS STATE(%d): %d",
+ self->threadId, state);
+ selfVerificationDumpState(pc, self);
+ selfVerificationDumpTrace(pc, self);
+ }
+
+ /*
+ * Skip endPC once when trace has a backward branch. If the SV state is
+ * single step, keep it that way.
+ */
+ if ((state == kSVSBackwardBranch && pc == shadowSpace->endPC) ||
+ (state != kSVSBackwardBranch && state != kSVSSingleStep)) {
+ shadowSpace->selfVerificationState = kSVSDebugInterp;
+ }
+
+ /* Check that the current pc is the end of the trace */
+ if ((state == kSVSDebugInterp || state == kSVSSingleStep) &&
+ pc == shadowSpace->endPC) {
+
+ shadowSpace->selfVerificationState = kSVSIdle;
+
+ /* Check register space */
+ int frameBytes = (int) shadowSpace->registerSpace +
+ shadowSpace->registerSpaceSize*4 -
+ (int) shadowSpace->shadowFP;
+ if (memcmp(shadowSpace->fp, shadowSpace->shadowFP, frameBytes)) {
+ LOGD("~~~ DbgIntp(%d): REGISTERS DIVERGENCE!", self->threadId);
+ selfVerificationDumpState(pc, self);
+ selfVerificationDumpTrace(pc, self);
+ LOGD("*** Interp Registers: addr: 0x%x bytes: %d",
+ (int)shadowSpace->fp, frameBytes);
+ selfVerificationPrintRegisters((int*)shadowSpace->fp,
+ (int*)shadowSpace->shadowFP,
+ frameBytes/4);
+ LOGD("*** Shadow Registers: addr: 0x%x bytes: %d",
+ (int)shadowSpace->shadowFP, frameBytes);
+ selfVerificationPrintRegisters((int*)shadowSpace->shadowFP,
+ (int*)shadowSpace->fp,
+ frameBytes/4);
+ selfVerificationSpinLoop(shadowSpace);
+ }
+ /* Check new frame if it exists (invokes only) */
+ if (self->curFrame < shadowSpace->fp) {
+ StackSaveArea* stackSave = SAVEAREA_FROM_FP(self->curFrame);
+ int localRegs = (stackSave->method->registersSize -
+ stackSave->method->insSize)*4;
+ int frameBytes2 = (int) shadowSpace->fp -
+ (int) self->curFrame - localRegs;
+ if (memcmp(((char*)self->curFrame)+localRegs,
+ ((char*)shadowSpace->endShadowFP)+localRegs, frameBytes2)) {
+ LOGD("~~~ DbgIntp(%d): REGISTERS (FRAME2) DIVERGENCE!",
+ self->threadId);
+ selfVerificationDumpState(pc, self);
+ selfVerificationDumpTrace(pc, self);
+ LOGD("*** Interp Registers: addr: 0x%x l: %d bytes: %d",
+ (int)self->curFrame, localRegs, frameBytes2);
+ selfVerificationPrintRegisters((int*)self->curFrame,
+ (int*)shadowSpace->endShadowFP,
+ (frameBytes2+localRegs)/4);
+ LOGD("*** Shadow Registers: addr: 0x%x l: %d bytes: %d",
+ (int)shadowSpace->endShadowFP, localRegs, frameBytes2);
+ selfVerificationPrintRegisters((int*)shadowSpace->endShadowFP,
+ (int*)self->curFrame,
+ (frameBytes2+localRegs)/4);
+ selfVerificationSpinLoop(shadowSpace);
+ }
+ }
+
+ /* Check memory space */
+ bool memDiff = false;
+ ShadowHeap* heapSpacePtr;
+ for (heapSpacePtr = shadowSpace->heapSpace;
+ heapSpacePtr != shadowSpace->heapSpaceTail; heapSpacePtr++) {
+ int memData = *((unsigned int*) heapSpacePtr->addr);
+ if (heapSpacePtr->data != memData) {
+ LOGD("~~~ DbgIntp(%d): MEMORY DIVERGENCE!", self->threadId);
+ LOGD("Addr: 0x%x Intrp Data: 0x%x Jit Data: 0x%x",
+ heapSpacePtr->addr, memData, heapSpacePtr->data);
+ selfVerificationDumpState(pc, self);
+ selfVerificationDumpTrace(pc, self);
+ memDiff = true;
+ }
+ }
+ if (memDiff) selfVerificationSpinLoop(shadowSpace);
+
+ /*
+ * Switch to JIT single step mode to stay in the debug interpreter for
+ * one more instruction
+ */
+ if (state == kSVSSingleStep) {
+ interpState->jitState = kJitSingleStepEnd;
+ }
+ return true;
+
+ /* If end not been reached, make sure max length not exceeded */
+ } else if (shadowSpace->traceLength >= JIT_MAX_TRACE_LEN) {
+ LOGD("~~~ DbgIntp(%d): CONTROL DIVERGENCE!", self->threadId);
+ LOGD("startPC: 0x%x endPC: 0x%x currPC: 0x%x",
+ (int)shadowSpace->startPC, (int)shadowSpace->endPC, (int)pc);
+ selfVerificationDumpState(pc, self);
+ selfVerificationDumpTrace(pc, self);
+ selfVerificationSpinLoop(shadowSpace);
+
+ return true;
+ }
+ /* Log the instruction address and decoded instruction for debug */
+ shadowSpace->trace[shadowSpace->traceLength].addr = (int)pc;
+ shadowSpace->trace[shadowSpace->traceLength].decInsn = decInsn;
+ shadowSpace->traceLength++;
+
+ return false;
+}
+#endif
+
+/*
+ * If one of our fixed tables or the translation buffer fills up,
+ * call this routine to avoid wasting cycles on future translation requests.
+ */
+void dvmJitStopTranslationRequests()
+{
+ /*
+ * Note 1: This won't necessarily stop all translation requests, and
+ * operates on a delayed mechanism. Running threads look to the copy
+ * of this value in their private InterpState structures and won't see
+ * this change until it is refreshed (which happens on interpreter
+ * entry).
+ * Note 2: This is a one-shot memory leak on this table. Because this is a
+ * permanent off switch for Jit profiling, it is a one-time leak of 1K
+ * bytes, and no further attempt will be made to re-allocate it. Can't
+ * free it because some thread may be holding a reference.
+ */
+ gDvmJit.pProfTable = NULL;
+}
+
+#if defined(WITH_JIT_TUNING)
+/* Convenience function to increment counter from assembly code */
+void dvmBumpNoChain(int from)
+{
+ gDvmJit.noChainExit[from]++;
+}
+
+/* Convenience function to increment counter from assembly code */
+void dvmBumpNormal()
+{
+ gDvmJit.normalExit++;
+}
+
+/* Convenience function to increment counter from assembly code */
+void dvmBumpPunt(int from)
+{
+ gDvmJit.puntExit++;
+}
+#endif
+
+/* Dumps debugging & tuning stats to the log */
+void dvmJitStats()
+{
+ int i;
+ int hit;
+ int not_hit;
+ int chains;
+ int stubs;
+ if (gDvmJit.pJitEntryTable) {
+ for (i=0, stubs=chains=hit=not_hit=0;
+ i < (int) gDvmJit.jitTableSize;
+ i++) {
+ if (gDvmJit.pJitEntryTable[i].dPC != 0) {
+ hit++;
+ if (gDvmJit.pJitEntryTable[i].codeAddress ==
+ dvmCompilerGetInterpretTemplate())
+ stubs++;
+ } else
+ not_hit++;
+ if (gDvmJit.pJitEntryTable[i].u.info.chain != gDvmJit.jitTableSize)
+ chains++;
+ }
+ LOGD("JIT: table size is %d, entries used is %d",
+ gDvmJit.jitTableSize, gDvmJit.jitTableEntriesUsed);
+ LOGD("JIT: %d traces, %d slots, %d chains, %d thresh, %s",
+ hit, not_hit + hit, chains, gDvmJit.threshold,
+ gDvmJit.blockingMode ? "Blocking" : "Non-blocking");
+
+#if defined(WITH_JIT_TUNING)
+ LOGD("JIT: Code cache patches: %d", gDvmJit.codeCachePatches);
+
+ LOGD("JIT: Lookups: %d hits, %d misses; %d normal, %d punt",
+ gDvmJit.addrLookupsFound, gDvmJit.addrLookupsNotFound,
+ gDvmJit.normalExit, gDvmJit.puntExit);
+
+ LOGD("JIT: ICHits: %d", gDvmICHitCount);
+
+ LOGD("JIT: noChainExit: %d IC miss, %d interp callsite, "
+ "%d switch overflow",
+ gDvmJit.noChainExit[kInlineCacheMiss],
+ gDvmJit.noChainExit[kCallsiteInterpreted],
+ gDvmJit.noChainExit[kSwitchOverflow]);
+
+ LOGD("JIT: ICPatch: %d init, %d rejected, %d lock-free, %d queued, "
+ "%d dropped",
+ gDvmJit.icPatchInit, gDvmJit.icPatchRejected,
+ gDvmJit.icPatchLockFree, gDvmJit.icPatchQueued,
+ gDvmJit.icPatchDropped);
+
+ LOGD("JIT: Invoke: %d mono, %d poly, %d native, %d return",
+ gDvmJit.invokeMonomorphic, gDvmJit.invokePolymorphic,
+ gDvmJit.invokeNative, gDvmJit.returnOp);
+ LOGD("JIT: Inline: %d mgetter, %d msetter, %d pgetter, %d psetter",
+ gDvmJit.invokeMonoGetterInlined, gDvmJit.invokeMonoSetterInlined,
+ gDvmJit.invokePolyGetterInlined, gDvmJit.invokePolySetterInlined);
+ LOGD("JIT: Total compilation time: %llu ms", gDvmJit.jitTime / 1000);
+ LOGD("JIT: Avg unit compilation time: %llu us",
+ gDvmJit.jitTime / gDvmJit.numCompilations);
+#endif
+
+ LOGD("JIT: %d Translation chains, %d interp stubs",
+ gDvmJit.translationChains, stubs);
+ if (gDvmJit.profile) {
+ dvmCompilerSortAndPrintTraceProfiles();
+ }
+ }
+}
+
+
+void setTraceConstruction(JitEntry *slot, bool value)
+{
+
+ JitEntryInfoUnion oldValue;
+ JitEntryInfoUnion newValue;
+ do {
+ oldValue = slot->u;
+ newValue = oldValue;
+ newValue.info.traceConstruction = value;
+ } while (android_atomic_release_cas(oldValue.infoWord, newValue.infoWord,
+ &slot->u.infoWord) != 0);
+}
+
+void resetTracehead(InterpState* interpState, JitEntry *slot)
+{
+ slot->codeAddress = dvmCompilerGetInterpretTemplate();
+ setTraceConstruction(slot, false);
+}
+
+/* Clean up any pending trace builds */
+void dvmJitAbortTraceSelect(InterpState* interpState)
+{
+ if (interpState->jitState == kJitTSelect)
+ interpState->jitState = kJitDone;
+}
+
+/*
+ * Find an entry in the JitTable, creating if necessary.
+ * Returns null if table is full.
+ */
+static JitEntry *lookupAndAdd(const u2* dPC, bool callerLocked)
+{
+ u4 chainEndMarker = gDvmJit.jitTableSize;
+ u4 idx = dvmJitHash(dPC);
+
+ /* Walk the bucket chain to find an exact match for our PC */
+ while ((gDvmJit.pJitEntryTable[idx].u.info.chain != chainEndMarker) &&
+ (gDvmJit.pJitEntryTable[idx].dPC != dPC)) {
+ idx = gDvmJit.pJitEntryTable[idx].u.info.chain;
+ }
+
+ if (gDvmJit.pJitEntryTable[idx].dPC != dPC) {
+ /*
+ * No match. Aquire jitTableLock and find the last
+ * slot in the chain. Possibly continue the chain walk in case
+ * some other thread allocated the slot we were looking
+ * at previuosly (perhaps even the dPC we're trying to enter).
+ */
+ if (!callerLocked)
+ dvmLockMutex(&gDvmJit.tableLock);
+ /*
+ * At this point, if .dPC is NULL, then the slot we're
+ * looking at is the target slot from the primary hash
+ * (the simple, and common case). Otherwise we're going
+ * to have to find a free slot and chain it.
+ */
+ ANDROID_MEMBAR_FULL(); /* Make sure we reload [].dPC after lock */
+ if (gDvmJit.pJitEntryTable[idx].dPC != NULL) {
+ u4 prev;
+ while (gDvmJit.pJitEntryTable[idx].u.info.chain != chainEndMarker) {
+ if (gDvmJit.pJitEntryTable[idx].dPC == dPC) {
+ /* Another thread got there first for this dPC */
+ if (!callerLocked)
+ dvmUnlockMutex(&gDvmJit.tableLock);
+ return &gDvmJit.pJitEntryTable[idx];
+ }
+ idx = gDvmJit.pJitEntryTable[idx].u.info.chain;
+ }
+ /* Here, idx should be pointing to the last cell of an
+ * active chain whose last member contains a valid dPC */
+ assert(gDvmJit.pJitEntryTable[idx].dPC != NULL);
+ /* Linear walk to find a free cell and add it to the end */
+ prev = idx;
+ while (true) {
+ idx++;
+ if (idx == chainEndMarker)
+ idx = 0; /* Wraparound */
+ if ((gDvmJit.pJitEntryTable[idx].dPC == NULL) ||
+ (idx == prev))
+ break;
+ }
+ if (idx != prev) {
+ JitEntryInfoUnion oldValue;
+ JitEntryInfoUnion newValue;
+ /*
+ * Although we hold the lock so that noone else will
+ * be trying to update a chain field, the other fields
+ * packed into the word may be in use by other threads.
+ */
+ do {
+ oldValue = gDvmJit.pJitEntryTable[prev].u;
+ newValue = oldValue;
+ newValue.info.chain = idx;
+ } while (android_atomic_release_cas(oldValue.infoWord,
+ newValue.infoWord,
+ &gDvmJit.pJitEntryTable[prev].u.infoWord) != 0);
+ }
+ }
+ if (gDvmJit.pJitEntryTable[idx].dPC == NULL) {
+ /*
+ * Initialize codeAddress and allocate the slot. Must
+ * happen in this order (since dPC is set, the entry is live.
+ */
+ gDvmJit.pJitEntryTable[idx].dPC = dPC;
+ gDvmJit.jitTableEntriesUsed++;
+ } else {
+ /* Table is full */
+ idx = chainEndMarker;
+ }
+ if (!callerLocked)
+ dvmUnlockMutex(&gDvmJit.tableLock);
+ }
+ return (idx == chainEndMarker) ? NULL : &gDvmJit.pJitEntryTable[idx];
+}
+
+/*
+ * Append the class ptr of "this" and the current method ptr to the current
+ * trace. That is, the trace runs will contain the following components:
+ * + trace run that ends with an invoke (existing entry)
+ * + thisClass (new)
+ * + calleeMethod (new)
+ */
+static void insertClassMethodInfo(InterpState* interpState,
+ const ClassObject* thisClass,
+ const Method* calleeMethod,
+ const DecodedInstruction* insn)
+{
+ int currTraceRun = ++interpState->currTraceRun;
+ interpState->trace[currTraceRun].meta = (void *) thisClass;
+ currTraceRun = ++interpState->currTraceRun;
+ interpState->trace[currTraceRun].meta = (void *) calleeMethod;
+}
+
+/*
+ * Check if the next instruction following the invoke is a move-result and if
+ * so add it to the trace. That is, this will add the trace run that includes
+ * the move-result to the trace list.
+ *
+ * + trace run that ends with an invoke (existing entry)
+ * + thisClass (existing entry)
+ * + calleeMethod (existing entry)
+ * + move result (new)
+ *
+ * lastPC, len, offset are all from the preceding invoke instruction
+ */
+static void insertMoveResult(const u2 *lastPC, int len, int offset,
+ InterpState *interpState)
+{
+ DecodedInstruction nextDecInsn;
+ const u2 *moveResultPC = lastPC + len;
+
+ dexDecodeInstruction(gDvm.instrFormat, moveResultPC, &nextDecInsn);
+ if ((nextDecInsn.opCode != OP_MOVE_RESULT) &&
+ (nextDecInsn.opCode != OP_MOVE_RESULT_WIDE) &&
+ (nextDecInsn.opCode != OP_MOVE_RESULT_OBJECT))
+ return;
+
+ /* We need to start a new trace run */
+ int currTraceRun = ++interpState->currTraceRun;
+ interpState->currRunHead = moveResultPC;
+ interpState->trace[currTraceRun].frag.startOffset = offset + len;
+ interpState->trace[currTraceRun].frag.numInsts = 1;
+ interpState->trace[currTraceRun].frag.runEnd = false;
+ interpState->trace[currTraceRun].frag.hint = kJitHintNone;
+ interpState->trace[currTraceRun].frag.isCode = true;
+ interpState->totalTraceLen++;
+
+ interpState->currRunLen = dexGetInstrOrTableWidthAbs(gDvm.instrWidth,
+ moveResultPC);
+}
+
+/*
+ * Adds to the current trace request one instruction at a time, just
+ * before that instruction is interpreted. This is the primary trace
+ * selection function. NOTE: return instruction are handled a little
+ * differently. In general, instructions are "proposed" to be added
+ * to the current trace prior to interpretation. If the interpreter
+ * then successfully completes the instruction, is will be considered
+ * part of the request. This allows us to examine machine state prior
+ * to interpretation, and also abort the trace request if the instruction
+ * throws or does something unexpected. However, return instructions
+ * will cause an immediate end to the translation request - which will
+ * be passed to the compiler before the return completes. This is done
+ * in response to special handling of returns by the interpreter (and
+ * because returns cannot throw in a way that causes problems for the
+ * translated code.
+ */
+int dvmCheckJit(const u2* pc, Thread* self, InterpState* interpState,
+ const ClassObject* thisClass, const Method* curMethod)
+{
+ int flags, len;
+ int switchInterp = false;
+ bool debugOrProfile = dvmDebuggerOrProfilerActive();
+ /* Stay in the dbg interpreter for the next instruction */
+ bool stayOneMoreInst = false;
+
+ /*
+ * Bug 2710533 - dalvik crash when disconnecting debugger
+ *
+ * Reset the entry point to the default value. If needed it will be set to a
+ * specific value in the corresponding case statement (eg kJitSingleStepEnd)
+ */
+ interpState->entryPoint = kInterpEntryInstr;
+
+ /* Prepare to handle last PC and stage the current PC */
+ const u2 *lastPC = interpState->lastPC;
+ interpState->lastPC = pc;
+
+ switch (interpState->jitState) {
+ int offset;
+ DecodedInstruction decInsn;
+ case kJitTSelect:
+ /* First instruction - just remember the PC and exit */
+ if (lastPC == NULL) break;
+ /* Grow the trace around the last PC if jitState is kJitTSelect */
+ dexDecodeInstruction(gDvm.instrFormat, lastPC, &decInsn);
+
+ /*
+ * Treat {PACKED,SPARSE}_SWITCH as trace-ending instructions due
+ * to the amount of space it takes to generate the chaining
+ * cells.
+ */
+ if (interpState->totalTraceLen != 0 &&
+ (decInsn.opCode == OP_PACKED_SWITCH ||
+ decInsn.opCode == OP_SPARSE_SWITCH)) {
+ interpState->jitState = kJitTSelectEnd;
+ break;
+ }
+
+
+#if defined(SHOW_TRACE)
+ LOGD("TraceGen: adding %s", dexGetOpcodeName(decInsn.opCode));
+#endif
+ flags = dexGetInstrFlags(gDvm.instrFlags, decInsn.opCode);
+ len = dexGetInstrOrTableWidthAbs(gDvm.instrWidth, lastPC);
+ offset = lastPC - interpState->method->insns;
+ assert((unsigned) offset <
+ dvmGetMethodInsnsSize(interpState->method));
+ if (lastPC != interpState->currRunHead + interpState->currRunLen) {
+ int currTraceRun;
+ /* We need to start a new trace run */
+ currTraceRun = ++interpState->currTraceRun;
+ interpState->currRunLen = 0;
+ interpState->currRunHead = (u2*)lastPC;
+ interpState->trace[currTraceRun].frag.startOffset = offset;
+ interpState->trace[currTraceRun].frag.numInsts = 0;
+ interpState->trace[currTraceRun].frag.runEnd = false;
+ interpState->trace[currTraceRun].frag.hint = kJitHintNone;
+ interpState->trace[currTraceRun].frag.isCode = true;
+ }
+ interpState->trace[interpState->currTraceRun].frag.numInsts++;
+ interpState->totalTraceLen++;
+ interpState->currRunLen += len;
+
+ /*
+ * If the last instruction is an invoke, we will try to sneak in
+ * the move-result* (if existent) into a separate trace run.
+ */
+ int needReservedRun = (flags & kInstrInvoke) ? 1 : 0;
+
+ /* Will probably never hit this with the current trace buildier */
+ if (interpState->currTraceRun ==
+ (MAX_JIT_RUN_LEN - 1 - needReservedRun)) {
+ interpState->jitState = kJitTSelectEnd;
+ }
+
+ if ( ((flags & kInstrUnconditional) == 0) &&
+ /* don't end trace on INVOKE_DIRECT_EMPTY */
+ (decInsn.opCode != OP_INVOKE_DIRECT_EMPTY) &&
+ ((flags & (kInstrCanBranch |
+ kInstrCanSwitch |
+ kInstrCanReturn |
+ kInstrInvoke)) != 0)) {
+ interpState->jitState = kJitTSelectEnd;
+#if defined(SHOW_TRACE)
+ LOGD("TraceGen: ending on %s, basic block end",
+ dexGetOpcodeName(decInsn.opCode));
+#endif
+
+ /*
+ * If the current invoke is a {virtual,interface}, get the
+ * current class/method pair into the trace as well.
+ * If the next instruction is a variant of move-result, insert
+ * it to the trace too.
+ */
+ if (flags & kInstrInvoke) {
+ insertClassMethodInfo(interpState, thisClass, curMethod,
+ &decInsn);
+ insertMoveResult(lastPC, len, offset, interpState);
+ }
+ }
+ /* Break on throw or self-loop */
+ if ((decInsn.opCode == OP_THROW) || (lastPC == pc)){
+ interpState->jitState = kJitTSelectEnd;
+ }
+ if (interpState->totalTraceLen >= JIT_MAX_TRACE_LEN) {
+ interpState->jitState = kJitTSelectEnd;
+ }
+ /* Abandon the trace request if debugger/profiler is attached */
+ if (debugOrProfile) {
+ interpState->jitState = kJitDone;
+ break;
+ }
+ if ((flags & kInstrCanReturn) != kInstrCanReturn) {
+ break;
+ }
+ else {
+ /*
+ * Last instruction is a return - stay in the dbg interpreter
+ * for one more instruction if it is a non-void return, since
+ * we don't want to start a trace with move-result as the first
+ * instruction (which is already included in the trace
+ * containing the invoke.
+ */
+ if (decInsn.opCode != OP_RETURN_VOID) {
+ stayOneMoreInst = true;
+ }
+ }
+ /* NOTE: intentional fallthrough for returns */
+ case kJitTSelectEnd:
+ {
+ /* Bad trace */
+ if (interpState->totalTraceLen == 0) {
+ /* Bad trace - mark as untranslatable */
+ interpState->jitState = kJitDone;
+ switchInterp = true;
+ break;
+ }
+
+ int lastTraceDesc = interpState->currTraceRun;
+
+ /* Extend a new empty desc if the last slot is meta info */
+ if (!interpState->trace[lastTraceDesc].frag.isCode) {
+ lastTraceDesc = ++interpState->currTraceRun;
+ interpState->trace[lastTraceDesc].frag.startOffset = 0;
+ interpState->trace[lastTraceDesc].frag.numInsts = 0;
+ interpState->trace[lastTraceDesc].frag.hint = kJitHintNone;
+ interpState->trace[lastTraceDesc].frag.isCode = true;
+ }
+
+ /* Mark the end of the trace runs */
+ interpState->trace[lastTraceDesc].frag.runEnd = true;
+
+ JitTraceDescription* desc =
+ (JitTraceDescription*)malloc(sizeof(JitTraceDescription) +
+ sizeof(JitTraceRun) * (interpState->currTraceRun+1));
+
+ if (desc == NULL) {
+ LOGE("Out of memory in trace selection");
+ dvmJitStopTranslationRequests();
+ interpState->jitState = kJitDone;
+ switchInterp = true;
+ break;
+ }
+
+ desc->method = interpState->method;
+ memcpy((char*)&(desc->trace[0]),
+ (char*)&(interpState->trace[0]),
+ sizeof(JitTraceRun) * (interpState->currTraceRun+1));
+#if defined(SHOW_TRACE)
+ LOGD("TraceGen: trace done, adding to queue");
+#endif
+ if (dvmCompilerWorkEnqueue(
+ interpState->currTraceHead,kWorkOrderTrace,desc)) {
+ /* Work order successfully enqueued */
+ if (gDvmJit.blockingMode) {
+ dvmCompilerDrainQueue();
+ }
+ } else {
+ /*
+ * Make sure the descriptor for the abandoned work order is
+ * freed.
+ */
+ free(desc);
+ }
+ /*
+ * Reset "trace in progress" flag whether or not we
+ * successfully entered a work order.
+ */
+ JitEntry *jitEntry =
+ lookupAndAdd(interpState->currTraceHead, false);
+ if (jitEntry) {
+ setTraceConstruction(jitEntry, false);
+ }
+ interpState->jitState = kJitDone;
+ switchInterp = true;
+ }
+ break;
+ case kJitSingleStep:
+ interpState->jitState = kJitSingleStepEnd;
+ break;
+ case kJitSingleStepEnd:
+ /*
+ * Clear the inJitCodeCache flag and abandon the resume attempt if
+ * we cannot switch back to the translation due to corner-case
+ * conditions. If the flag is not cleared and the code cache is full
+ * we will be stuck in the debug interpreter as the code cache
+ * cannot be reset.
+ */
+ if (dvmJitStayInPortableInterpreter()) {
+ interpState->entryPoint = kInterpEntryInstr;
+ self->inJitCodeCache = 0;
+ } else {
+ interpState->entryPoint = kInterpEntryResume;
+ }
+ interpState->jitState = kJitDone;
+ switchInterp = true;
+ break;
+ case kJitDone:
+ switchInterp = true;
+ break;
+#if defined(WITH_SELF_VERIFICATION)
+ case kJitSelfVerification:
+ if (selfVerificationDebugInterp(pc, self, interpState)) {
+ /*
+ * If the next state is not single-step end, we can switch
+ * interpreter now.
+ */
+ if (interpState->jitState != kJitSingleStepEnd) {
+ interpState->jitState = kJitDone;
+ switchInterp = true;
+ }
+ }
+ break;
+#endif
+ case kJitNot:
+ switchInterp = !debugOrProfile;
+ break;
+ default:
+ LOGE("Unexpected JIT state: %d entry point: %d",
+ interpState->jitState, interpState->entryPoint);
+ dvmAbort();
+ break;
+ }
+ /*
+ * Final check to see if we can really switch the interpreter. Make sure
+ * the jitState is kJitDone or kJitNot when switchInterp is set to true.
+ */
+ assert(switchInterp == false || interpState->jitState == kJitDone ||
+ interpState->jitState == kJitNot);
+ return switchInterp && !debugOrProfile && !stayOneMoreInst &&
+ !dvmJitStayInPortableInterpreter();
+}
+
+JitEntry *dvmFindJitEntry(const u2* pc)
+{
+ int idx = dvmJitHash(pc);
+
+ /* Expect a high hit rate on 1st shot */
+ if (gDvmJit.pJitEntryTable[idx].dPC == pc)
+ return &gDvmJit.pJitEntryTable[idx];
+ else {
+ int chainEndMarker = gDvmJit.jitTableSize;
+ while (gDvmJit.pJitEntryTable[idx].u.info.chain != chainEndMarker) {
+ idx = gDvmJit.pJitEntryTable[idx].u.info.chain;
+ if (gDvmJit.pJitEntryTable[idx].dPC == pc)
+ return &gDvmJit.pJitEntryTable[idx];
+ }
+ }
+ return NULL;
+}
+
+/*
+ * If a translated code address exists for the davik byte code
+ * pointer return it. This routine needs to be fast.
+ */
+void* dvmJitGetCodeAddr(const u2* dPC)
+{
+ int idx = dvmJitHash(dPC);
+ const u2* npc = gDvmJit.pJitEntryTable[idx].dPC;
+ if (npc != NULL) {
+ bool hideTranslation = dvmJitHideTranslation();
+
+ if (npc == dPC) {
+#if defined(WITH_JIT_TUNING)
+ gDvmJit.addrLookupsFound++;
+#endif
+ return hideTranslation ?
+ NULL : gDvmJit.pJitEntryTable[idx].codeAddress;
+ } else {
+ int chainEndMarker = gDvmJit.jitTableSize;
+ while (gDvmJit.pJitEntryTable[idx].u.info.chain != chainEndMarker) {
+ idx = gDvmJit.pJitEntryTable[idx].u.info.chain;
+ if (gDvmJit.pJitEntryTable[idx].dPC == dPC) {
+#if defined(WITH_JIT_TUNING)
+ gDvmJit.addrLookupsFound++;
+#endif
+ return hideTranslation ?
+ NULL : gDvmJit.pJitEntryTable[idx].codeAddress;
+ }
+ }
+ }
+ }
+#if defined(WITH_JIT_TUNING)
+ gDvmJit.addrLookupsNotFound++;
+#endif
+ return NULL;
+}
+
+/*
+ * Register the translated code pointer into the JitTable.
+ * NOTE: Once a codeAddress field transitions from initial state to
+ * JIT'd code, it must not be altered without first halting all
+ * threads. This routine should only be called by the compiler
+ * thread.
+ */
+void dvmJitSetCodeAddr(const u2* dPC, void *nPC, JitInstructionSetType set) {
+ JitEntryInfoUnion oldValue;
+ JitEntryInfoUnion newValue;
+ JitEntry *jitEntry = lookupAndAdd(dPC, false);
+ assert(jitEntry);
+ /* Note: order of update is important */
+ do {
+ oldValue = jitEntry->u;
+ newValue = oldValue;
+ newValue.info.instructionSet = set;
+ } while (android_atomic_release_cas(
+ oldValue.infoWord, newValue.infoWord,
+ &jitEntry->u.infoWord) != 0);
+ jitEntry->codeAddress = nPC;
+}
+
+/*
+ * Determine if valid trace-bulding request is active. Return true
+ * if we need to abort and switch back to the fast interpreter, false
+ * otherwise.
+ */
+bool dvmJitCheckTraceRequest(Thread* self, InterpState* interpState)
+{
+ bool switchInterp = false; /* Assume success */
+ int i;
+ /*
+ * A note on trace "hotness" filtering:
+ *
+ * Our first level trigger is intentionally loose - we need it to
+ * fire easily not just to identify potential traces to compile, but
+ * also to allow re-entry into the code cache.
+ *
+ * The 2nd level filter (done here) exists to be selective about
+ * what we actually compile. It works by requiring the same
+ * trace head "key" (defined as filterKey below) to appear twice in
+ * a relatively short period of time. The difficulty is defining the
+ * shape of the filterKey. Unfortunately, there is no "one size fits
+ * all" approach.
+ *
+ * For spiky execution profiles dominated by a smallish
+ * number of very hot loops, we would want the second-level filter
+ * to be very selective. A good selective filter is requiring an
+ * exact match of the Dalvik PC. In other words, defining filterKey as:
+ * intptr_t filterKey = (intptr_t)interpState->pc
+ *
+ * However, for flat execution profiles we do best when aggressively
+ * translating. A heuristically decent proxy for this is to use
+ * the value of the method pointer containing the trace as the filterKey.
+ * Intuitively, this is saying that once any trace in a method appears hot,
+ * immediately translate any other trace from that same method that
+ * survives the first-level filter. Here, filterKey would be defined as:
+ * intptr_t filterKey = (intptr_t)interpState->method
+ *
+ * The problem is that we can't easily detect whether we're dealing
+ * with a spiky or flat profile. If we go with the "pc" match approach,
+ * flat profiles perform poorly. If we go with the loose "method" match,
+ * we end up generating a lot of useless translations. Probably the
+ * best approach in the future will be to retain profile information
+ * across runs of each application in order to determine it's profile,
+ * and then choose once we have enough history.
+ *
+ * However, for now we've decided to chose a compromise filter scheme that
+ * includes elements of both. The high order bits of the filter key
+ * are drawn from the enclosing method, and are combined with a slice
+ * of the low-order bits of the Dalvik pc of the trace head. The
+ * looseness of the filter can be adjusted by changing with width of
+ * the Dalvik pc slice (JIT_TRACE_THRESH_FILTER_PC_BITS). The wider
+ * the slice, the tighter the filter.
+ *
+ * Note: the fixed shifts in the function below reflect assumed word
+ * alignment for method pointers, and half-word alignment of the Dalvik pc.
+ * for method pointers and half-word alignment for dalvik pc.
+ */
+ u4 methodKey = (u4)interpState->method <<
+ (JIT_TRACE_THRESH_FILTER_PC_BITS - 2);
+ u4 pcKey = ((u4)interpState->pc >> 1) &
+ ((1 << JIT_TRACE_THRESH_FILTER_PC_BITS) - 1);
+ intptr_t filterKey = (intptr_t)(methodKey | pcKey);
+ bool debugOrProfile = dvmDebuggerOrProfilerActive();
+
+ /* Check if the JIT request can be handled now */
+ if (gDvmJit.pJitEntryTable != NULL && debugOrProfile == false) {
+ /* Bypass the filter for hot trace requests or during stress mode */
+ if (interpState->jitState == kJitTSelectRequest &&
+ gDvmJit.threshold > 6) {
+ /* Two-level filtering scheme */
+ for (i=0; i< JIT_TRACE_THRESH_FILTER_SIZE; i++) {
+ if (filterKey == interpState->threshFilter[i]) {
+ interpState->threshFilter[i] = 0; // Reset filter entry
+ break;
+ }
+ }
+ if (i == JIT_TRACE_THRESH_FILTER_SIZE) {
+ /*
+ * Use random replacement policy - otherwise we could miss a
+ * large loop that contains more traces than the size of our
+ * filter array.
+ */
+ i = rand() % JIT_TRACE_THRESH_FILTER_SIZE;
+ interpState->threshFilter[i] = filterKey;
+ interpState->jitState = kJitDone;
+ }
+ }
+
+ /* If the compiler is backlogged, cancel any JIT actions */
+ if (gDvmJit.compilerQueueLength >= gDvmJit.compilerHighWater) {
+ interpState->jitState = kJitDone;
+ }
+
+ /*
+ * Check for additional reasons that might force the trace select
+ * request to be dropped
+ */
+ if (interpState->jitState == kJitTSelectRequest ||
+ interpState->jitState == kJitTSelectRequestHot) {
+ JitEntry *slot = lookupAndAdd(interpState->pc, false);
+ if (slot == NULL) {
+ /*
+ * Table is full. This should have been
+ * detected by the compiler thread and the table
+ * resized before we run into it here. Assume bad things
+ * are afoot and disable profiling.
+ */
+ interpState->jitState = kJitDone;
+ LOGD("JIT: JitTable full, disabling profiling");
+ dvmJitStopTranslationRequests();
+ } else if (slot->u.info.traceConstruction) {
+ /*
+ * Trace request already in progress, but most likely it
+ * aborted without cleaning up. Assume the worst and
+ * mark trace head as untranslatable. If we're wrong,
+ * the compiler thread will correct the entry when the
+ * translation is completed. The downside here is that
+ * some existing translation may chain to the interpret-only
+ * template instead of the real translation during this
+ * window. Performance, but not correctness, issue.
+ */
+ interpState->jitState = kJitDone;
+ resetTracehead(interpState, slot);
+ } else if (slot->codeAddress) {
+ /* Nothing to do here - just return */
+ interpState->jitState = kJitDone;
+ } else {
+ /*
+ * Mark request. Note, we are not guaranteed exclusivity
+ * here. A window exists for another thread to be
+ * attempting to build this same trace. Rather than
+ * bear the cost of locking, we'll just allow that to
+ * happen. The compiler thread, if it chooses, can
+ * discard redundant requests.
+ */
+ setTraceConstruction(slot, true);
+ }
+ }
+
+ switch (interpState->jitState) {
+ case kJitTSelectRequest:
+ case kJitTSelectRequestHot:
+ interpState->jitState = kJitTSelect;
+ interpState->currTraceHead = interpState->pc;
+ interpState->currTraceRun = 0;
+ interpState->totalTraceLen = 0;
+ interpState->currRunHead = interpState->pc;
+ interpState->currRunLen = 0;
+ interpState->trace[0].frag.startOffset =
+ interpState->pc - interpState->method->insns;
+ interpState->trace[0].frag.numInsts = 0;
+ interpState->trace[0].frag.runEnd = false;
+ interpState->trace[0].frag.hint = kJitHintNone;
+ interpState->trace[0].frag.isCode = true;
+ interpState->lastPC = 0;
+ break;
+ /*
+ * For JIT's perspective there is no need to stay in the debug
+ * interpreter unless debugger/profiler is attached.
+ */
+ case kJitDone:
+ switchInterp = true;
+ break;
+ default:
+ LOGE("Unexpected JIT state: %d entry point: %d",
+ interpState->jitState, interpState->entryPoint);
+ dvmAbort();
+ }
+ } else {
+ /*
+ * Cannot build trace this time - ready to leave the dbg interpreter
+ */
+ interpState->jitState = kJitDone;
+ switchInterp = true;
+ }
+
+ /*
+ * Final check to see if we can really switch the interpreter. Make sure
+ * the jitState is kJitDone when switchInterp is set to true.
+ */
+ assert(switchInterp == false || interpState->jitState == kJitDone);
+ return switchInterp && !debugOrProfile &&
+ !dvmJitStayInPortableInterpreter();
+}
+
+/*
+ * Resizes the JitTable. Must be a power of 2, and returns true on failure.
+ * Stops all threads, and thus is a heavyweight operation. May only be called
+ * by the compiler thread.
+ */
+bool dvmJitResizeJitTable( unsigned int size )
+{
+ JitEntry *pNewTable;
+ JitEntry *pOldTable;
+ JitEntry tempEntry;
+ u4 newMask;
+ unsigned int oldSize;
+ unsigned int i;
+
+ assert(gDvmJit.pJitEntryTable != NULL);
+ assert(size && !(size & (size - 1))); /* Is power of 2? */
+
+ LOGI("Jit: resizing JitTable from %d to %d", gDvmJit.jitTableSize, size);
+
+ newMask = size - 1;
+
+ if (size <= gDvmJit.jitTableSize) {
+ return true;
+ }
+
+ /* Make sure requested size is compatible with chain field width */
+ tempEntry.u.info.chain = size;
+ if (tempEntry.u.info.chain != size) {
+ LOGD("Jit: JitTable request of %d too big", size);
+ return true;
+ }
+
+ pNewTable = (JitEntry*)calloc(size, sizeof(*pNewTable));
+ if (pNewTable == NULL) {
+ return true;
+ }
+ for (i=0; i< size; i++) {
+ pNewTable[i].u.info.chain = size; /* Initialize chain termination */
+ }
+
+ /* Stop all other interpreting/jit'ng threads */
+ dvmSuspendAllThreads(SUSPEND_FOR_TBL_RESIZE);
+
+ pOldTable = gDvmJit.pJitEntryTable;
+ oldSize = gDvmJit.jitTableSize;
+
+ dvmLockMutex(&gDvmJit.tableLock);
+ gDvmJit.pJitEntryTable = pNewTable;
+ gDvmJit.jitTableSize = size;
+ gDvmJit.jitTableMask = size - 1;
+ gDvmJit.jitTableEntriesUsed = 0;
+
+ for (i=0; i < oldSize; i++) {
+ if (pOldTable[i].dPC) {
+ JitEntry *p;
+ u2 chain;
+ p = lookupAndAdd(pOldTable[i].dPC, true /* holds tableLock*/ );
+ p->codeAddress = pOldTable[i].codeAddress;
+ /* We need to preserve the new chain field, but copy the rest */
+ chain = p->u.info.chain;
+ p->u = pOldTable[i].u;
+ p->u.info.chain = chain;
+ }
+ }
+ dvmUnlockMutex(&gDvmJit.tableLock);
+
+ free(pOldTable);
+
+ /* Restart the world */
+ dvmResumeAllThreads(SUSPEND_FOR_TBL_RESIZE);
+
+ return false;
+}
+
+/*
+ * Reset the JitTable to the initial clean state.
+ */
+void dvmJitResetTable(void)
+{
+ JitEntry *jitEntry = gDvmJit.pJitEntryTable;
+ unsigned int size = gDvmJit.jitTableSize;
+ unsigned int i;
+
+ dvmLockMutex(&gDvmJit.tableLock);
+ memset((void *) jitEntry, 0, sizeof(JitEntry) * size);
+ for (i=0; i< size; i++) {
+ jitEntry[i].u.info.chain = size; /* Initialize chain termination */
+ }
+ gDvmJit.jitTableEntriesUsed = 0;
+ dvmUnlockMutex(&gDvmJit.tableLock);
+}
+
+/*
+ * Float/double conversion requires clamping to min and max of integer form. If
+ * target doesn't support this normally, use these.
+ */
+s8 dvmJitd2l(double d)
+{
+ static const double kMaxLong = (double)(s8)0x7fffffffffffffffULL;
+ static const double kMinLong = (double)(s8)0x8000000000000000ULL;
+ if (d >= kMaxLong)
+ return (s8)0x7fffffffffffffffULL;
+ else if (d <= kMinLong)
+ return (s8)0x8000000000000000ULL;
+ else if (d != d) // NaN case
+ return 0;
+ else
+ return (s8)d;
+}
+
+s8 dvmJitf2l(float f)
+{
+ static const float kMaxLong = (float)(s8)0x7fffffffffffffffULL;
+ static const float kMinLong = (float)(s8)0x8000000000000000ULL;
+ if (f >= kMaxLong)
+ return (s8)0x7fffffffffffffffULL;
+ else if (f <= kMinLong)
+ return (s8)0x8000000000000000ULL;
+ else if (f != f) // NaN case
+ return 0;
+ else
+ return (s8)f;
+}
+
+#endif /* WITH_JIT */
diff --git a/vm/interp/Jit.h b/vm/interp/Jit.h
new file mode 100644
index 0000000..6101f54
--- /dev/null
+++ b/vm/interp/Jit.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Jit control
+ */
+#ifndef _DALVIK_INTERP_JIT
+#define _DALVIK_INTERP_JIT
+
+#include "InterpDefs.h"
+#include "mterp/common/jit-config.h"
+
+#define JIT_MAX_TRACE_LEN 100
+
+#if defined (WITH_SELF_VERIFICATION)
+
+#define REG_SPACE 256 /* default size of shadow space */
+#define HEAP_SPACE JIT_MAX_TRACE_LEN /* default size of heap space */
+
+typedef struct ShadowHeap {
+ int addr;
+ int data;
+} ShadowHeap;
+
+typedef struct InstructionTrace {
+ int addr;
+ DecodedInstruction decInsn;
+} InstructionTrace;
+
+typedef struct ShadowSpace {
+ const u2* startPC; /* starting pc of jitted region */
+ const void* fp; /* starting fp of jitted region */
+ void* glue; /* starting glue of jitted region */
+ SelfVerificationState jitExitState; /* exit point for JIT'ed code */
+ SelfVerificationState selfVerificationState; /* current SV running state */
+ const u2* endPC; /* ending pc of jitted region */
+ void* shadowFP; /* pointer to fp in shadow space */
+ InterpState interpState; /* copy of interpState */
+ int* registerSpace; /* copy of register state */
+ int registerSpaceSize; /* current size of register space */
+ ShadowHeap heapSpace[HEAP_SPACE]; /* copy of heap space */
+ ShadowHeap* heapSpaceTail; /* tail pointer to heapSpace */
+ const void* endShadowFP; /* ending fp in shadow space */
+ InstructionTrace trace[JIT_MAX_TRACE_LEN]; /* opcode trace for debugging */
+ int traceLength; /* counter for current trace length */
+ const Method* method; /* starting method of jitted region */
+} ShadowSpace;
+
+/*
+ * Self verification functions.
+ */
+void* dvmSelfVerificationShadowSpaceAlloc(Thread* self);
+void dvmSelfVerificationShadowSpaceFree(Thread* self);
+void* dvmSelfVerificationSaveState(const u2* pc, const void* fp,
+ InterpState* interpState,
+ int targetTrace);
+void* dvmSelfVerificationRestoreState(const u2* pc, const void* fp,
+ SelfVerificationState exitPoint);
+#endif
+
+/*
+ * JitTable hash function.
+ */
+
+static inline u4 dvmJitHashMask( const u2* p, u4 mask ) {
+ return ((((u4)p>>12)^(u4)p)>>1) & (mask);
+}
+
+static inline u4 dvmJitHash( const u2* p ) {
+ return dvmJitHashMask( p, gDvmJit.jitTableMask );
+}
+
+/*
+ * Entries in the JIT's address lookup hash table.
+ * Fields which may be updated by multiple threads packed into a
+ * single 32-bit word to allow use of atomic update.
+ */
+
+typedef struct JitEntryInfo {
+ unsigned int traceConstruction:1; /* build underway? */
+ unsigned int isMethodEntry:1;
+ unsigned int inlineCandidate:1;
+ unsigned int profileEnabled:1;
+ JitInstructionSetType instructionSet:4;
+ unsigned int unused:8;
+ u2 chain; /* Index of next in chain */
+} JitEntryInfo;
+
+typedef union JitEntryInfoUnion {
+ JitEntryInfo info;
+ volatile int infoWord;
+} JitEntryInfoUnion;
+
+typedef struct JitEntry {
+ JitEntryInfoUnion u;
+ const u2* dPC; /* Dalvik code address */
+ void* codeAddress; /* Code address of native translation */
+} JitEntry;
+
+int dvmCheckJit(const u2* pc, Thread* self, InterpState* interpState,
+ const ClassObject *callsiteClass, const Method* curMethod);
+void* dvmJitGetCodeAddr(const u2* dPC);
+bool dvmJitCheckTraceRequest(Thread* self, InterpState* interpState);
+void dvmJitStopTranslationRequests(void);
+void dvmJitStats(void);
+bool dvmJitResizeJitTable(unsigned int size);
+void dvmJitResetTable(void);
+struct JitEntry *dvmFindJitEntry(const u2* pc);
+s8 dvmJitd2l(double d);
+s8 dvmJitf2l(float f);
+void dvmJitSetCodeAddr(const u2* dPC, void *nPC, JitInstructionSetType set);
+void dvmJitAbortTraceSelect(InterpState* interpState);
+
+#endif /*_DALVIK_INTERP_JIT*/
diff --git a/vm/interp/README.txt b/vm/interp/README.txt
new file mode 100644
index 0000000..170d2b1
--- /dev/null
+++ b/vm/interp/README.txt
@@ -0,0 +1,3 @@
+Dalvik interpreter entry point.
+
+The "mterp" directory now holds the interpreter implementation.
diff --git a/vm/interp/Stack.c b/vm/interp/Stack.c
new file mode 100644
index 0000000..695aa44
--- /dev/null
+++ b/vm/interp/Stack.c
@@ -0,0 +1,1381 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Stacks and their uses (e.g. native --> interpreted method calls).
+ *
+ * See the majestic ASCII art in Stack.h.
+ */
+#include "Dalvik.h"
+#include "jni.h"
+
+#include <stdlib.h>
+#include <stdarg.h>
+
+/*
+ * Initialize the interpreter stack in a new thread.
+ *
+ * Currently this doesn't do much, since we don't need to zero out the
+ * stack (and we really don't want to if it was created with mmap).
+ */
+bool dvmInitInterpStack(Thread* thread, int stackSize)
+{
+ assert(thread->interpStackStart != NULL);
+
+ assert(thread->curFrame == NULL);
+
+ return true;
+}
+
+/*
+ * We're calling an interpreted method from an internal VM function or
+ * via reflection.
+ *
+ * Push a frame for an interpreted method onto the stack. This is only
+ * used when calling into interpreted code from native code. (The
+ * interpreter does its own stack frame manipulation for interp-->interp
+ * calls.)
+ *
+ * The size we need to reserve is the sum of parameters, local variables,
+ * saved goodies, and outbound parameters.
+ *
+ * We start by inserting a "break" frame, which ensures that the interpreter
+ * hands control back to us after the function we call returns or an
+ * uncaught exception is thrown.
+ */
+static bool dvmPushInterpFrame(Thread* self, const Method* method)
+{
+ StackSaveArea* saveBlock;
+ StackSaveArea* breakSaveBlock;
+ int stackReq;
+ u1* stackPtr;
+
+ assert(!dvmIsNativeMethod(method));
+ assert(!dvmIsAbstractMethod(method));
+
+ stackReq = method->registersSize * 4 // params + locals
+ + sizeof(StackSaveArea) * 2 // break frame + regular frame
+ + method->outsSize * 4; // args to other methods
+
+ if (self->curFrame != NULL)
+ stackPtr = (u1*) SAVEAREA_FROM_FP(self->curFrame);
+ else
+ stackPtr = self->interpStackStart;
+
+ if (stackPtr - stackReq < self->interpStackEnd) {
+ /* not enough space */
+ LOGW("Stack overflow on call to interp "
+ "(req=%d top=%p cur=%p size=%d %s.%s)\n",
+ stackReq, self->interpStackStart, self->curFrame,
+ self->interpStackSize, method->clazz->descriptor, method->name);
+ dvmHandleStackOverflow(self, method);
+ assert(dvmCheckException(self));
+ return false;
+ }
+
+ /*
+ * Shift the stack pointer down, leaving space for the function's
+ * args/registers and save area.
+ */
+ stackPtr -= sizeof(StackSaveArea);
+ breakSaveBlock = (StackSaveArea*)stackPtr;
+ stackPtr -= method->registersSize * 4 + sizeof(StackSaveArea);
+ saveBlock = (StackSaveArea*) stackPtr;
+
+#if !defined(NDEBUG) && !defined(PAD_SAVE_AREA)
+ /* debug -- memset the new stack, unless we want valgrind's help */
+ memset(stackPtr - (method->outsSize*4), 0xaf, stackReq);
+#endif
+#ifdef EASY_GDB
+ breakSaveBlock->prevSave = FP_FROM_SAVEAREA(self->curFrame);
+ saveBlock->prevSave = breakSaveBlock;
+#endif
+
+ breakSaveBlock->prevFrame = self->curFrame;
+ breakSaveBlock->savedPc = NULL; // not required
+ breakSaveBlock->xtra.localRefCookie = 0; // not required
+ breakSaveBlock->method = NULL;
+ saveBlock->prevFrame = FP_FROM_SAVEAREA(breakSaveBlock);
+ saveBlock->savedPc = NULL; // not required
+ saveBlock->xtra.currentPc = NULL; // not required?
+ saveBlock->method = method;
+
+ LOGVV("PUSH frame: old=%p new=%p (size=%d)\n",
+ self->curFrame, FP_FROM_SAVEAREA(saveBlock),
+ (u1*)self->curFrame - (u1*)FP_FROM_SAVEAREA(saveBlock));
+
+ self->curFrame = FP_FROM_SAVEAREA(saveBlock);
+
+ return true;
+}
+
+/*
+ * We're calling a JNI native method from an internal VM fuction or
+ * via reflection. This is also used to create the "fake" native-method
+ * frames at the top of the interpreted stack.
+ *
+ * This actually pushes two frames; the first is a "break" frame.
+ *
+ * The top frame has additional space for JNI local reference tracking.
+ */
+bool dvmPushJNIFrame(Thread* self, const Method* method)
+{
+ StackSaveArea* saveBlock;
+ StackSaveArea* breakSaveBlock;
+ int stackReq;
+ u1* stackPtr;
+
+ assert(dvmIsNativeMethod(method));
+
+ stackReq = method->registersSize * 4 // params only
+ + sizeof(StackSaveArea) * 2; // break frame + regular frame
+
+ if (self->curFrame != NULL)
+ stackPtr = (u1*) SAVEAREA_FROM_FP(self->curFrame);
+ else
+ stackPtr = self->interpStackStart;
+
+ if (stackPtr - stackReq < self->interpStackEnd) {
+ /* not enough space */
+ LOGW("Stack overflow on call to native "
+ "(req=%d top=%p cur=%p size=%d '%s')\n",
+ stackReq, self->interpStackStart, self->curFrame,
+ self->interpStackSize, method->name);
+ dvmHandleStackOverflow(self, method);
+ assert(dvmCheckException(self));
+ return false;
+ }
+
+ /*
+ * Shift the stack pointer down, leaving space for just the stack save
+ * area for the break frame, then shift down farther for the full frame.
+ * We leave space for the method args, which are copied in later.
+ */
+ stackPtr -= sizeof(StackSaveArea);
+ breakSaveBlock = (StackSaveArea*)stackPtr;
+ stackPtr -= method->registersSize * 4 + sizeof(StackSaveArea);
+ saveBlock = (StackSaveArea*) stackPtr;
+
+#if !defined(NDEBUG) && !defined(PAD_SAVE_AREA)
+ /* debug -- memset the new stack */
+ memset(stackPtr, 0xaf, stackReq);
+#endif
+#ifdef EASY_GDB
+ if (self->curFrame == NULL)
+ breakSaveBlock->prevSave = NULL;
+ else
+ breakSaveBlock->prevSave = FP_FROM_SAVEAREA(self->curFrame);
+ saveBlock->prevSave = breakSaveBlock;
+#endif
+
+ breakSaveBlock->prevFrame = self->curFrame;
+ breakSaveBlock->savedPc = NULL; // not required
+ breakSaveBlock->xtra.localRefCookie = 0; // not required
+ breakSaveBlock->method = NULL;
+ saveBlock->prevFrame = FP_FROM_SAVEAREA(breakSaveBlock);
+ saveBlock->savedPc = NULL; // not required
+#ifdef USE_INDIRECT_REF
+ saveBlock->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
+#else
+ saveBlock->xtra.localRefCookie = self->jniLocalRefTable.nextEntry;
+#endif
+ saveBlock->method = method;
+
+ LOGVV("PUSH JNI frame: old=%p new=%p (size=%d)\n",
+ self->curFrame, FP_FROM_SAVEAREA(saveBlock),
+ (u1*)self->curFrame - (u1*)FP_FROM_SAVEAREA(saveBlock));
+
+ self->curFrame = FP_FROM_SAVEAREA(saveBlock);
+
+ return true;
+}
+
+/*
+ * This is used by the JNI PushLocalFrame call. We push a new frame onto
+ * the stack that has no ins, outs, or locals, and no break frame above it.
+ * It's strictly used for tracking JNI local refs, and will be popped off
+ * by dvmPopFrame if it's not removed explicitly.
+ */
+bool dvmPushLocalFrame(Thread* self, const Method* method)
+{
+ StackSaveArea* saveBlock;
+ int stackReq;
+ u1* stackPtr;
+
+ assert(dvmIsNativeMethod(method));
+
+ stackReq = sizeof(StackSaveArea); // regular frame
+
+ assert(self->curFrame != NULL);
+ stackPtr = (u1*) SAVEAREA_FROM_FP(self->curFrame);
+
+ if (stackPtr - stackReq < self->interpStackEnd) {
+ /* not enough space; let JNI throw the exception */
+ LOGW("Stack overflow on PushLocal "
+ "(req=%d top=%p cur=%p size=%d '%s')\n",
+ stackReq, self->interpStackStart, self->curFrame,
+ self->interpStackSize, method->name);
+ dvmHandleStackOverflow(self, method);
+ assert(dvmCheckException(self));
+ return false;
+ }
+
+ /*
+ * Shift the stack pointer down, leaving space for just the stack save
+ * area for the break frame, then shift down farther for the full frame.
+ */
+ stackPtr -= sizeof(StackSaveArea);
+ saveBlock = (StackSaveArea*) stackPtr;
+
+#if !defined(NDEBUG) && !defined(PAD_SAVE_AREA)
+ /* debug -- memset the new stack */
+ memset(stackPtr, 0xaf, stackReq);
+#endif
+#ifdef EASY_GDB
+ saveBlock->prevSave = FP_FROM_SAVEAREA(self->curFrame);
+#endif
+
+ saveBlock->prevFrame = self->curFrame;
+ saveBlock->savedPc = NULL; // not required
+#ifdef USE_INDIRECT_REF
+ saveBlock->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
+#else
+ saveBlock->xtra.localRefCookie = self->jniLocalRefTable.nextEntry;
+#endif
+ saveBlock->method = method;
+
+ LOGVV("PUSH JNI local frame: old=%p new=%p (size=%d)\n",
+ self->curFrame, FP_FROM_SAVEAREA(saveBlock),
+ (u1*)self->curFrame - (u1*)FP_FROM_SAVEAREA(saveBlock));
+
+ self->curFrame = FP_FROM_SAVEAREA(saveBlock);
+
+ return true;
+}
+
+/*
+ * Pop one frame pushed on by JNI PushLocalFrame.
+ *
+ * If we've gone too far, the previous frame is either a break frame or
+ * an interpreted frame. Either way, the method pointer won't match.
+ */
+bool dvmPopLocalFrame(Thread* self)
+{
+ StackSaveArea* saveBlock = SAVEAREA_FROM_FP(self->curFrame);
+
+ assert(!dvmIsBreakFrame(self->curFrame));
+ if (saveBlock->method != SAVEAREA_FROM_FP(saveBlock->prevFrame)->method) {
+ /*
+ * The previous frame doesn't have the same method pointer -- we've
+ * been asked to pop too much.
+ */
+ assert(dvmIsBreakFrame(saveBlock->prevFrame) ||
+ !dvmIsNativeMethod(
+ SAVEAREA_FROM_FP(saveBlock->prevFrame)->method));
+ return false;
+ }
+
+ LOGVV("POP JNI local frame: removing %s, now %s\n",
+ saveBlock->method->name,
+ SAVEAREA_FROM_FP(saveBlock->prevFrame)->method->name);
+ dvmPopJniLocals(self, saveBlock);
+ self->curFrame = saveBlock->prevFrame;
+
+ return true;
+}
+
+/*
+ * Pop a frame we added. There should be one method frame and one break
+ * frame.
+ *
+ * If JNI Push/PopLocalFrame calls were mismatched, we might end up
+ * popping multiple method frames before we find the break.
+ *
+ * Returns "false" if there was no frame to pop.
+ */
+static bool dvmPopFrame(Thread* self)
+{
+ StackSaveArea* saveBlock;
+
+ if (self->curFrame == NULL)
+ return false;
+
+ saveBlock = SAVEAREA_FROM_FP(self->curFrame);
+ assert(!dvmIsBreakFrame(self->curFrame));
+
+ /*
+ * Remove everything up to the break frame. If this was a call into
+ * native code, pop the JNI local references table.
+ */
+ while (saveBlock->prevFrame != NULL && saveBlock->method != NULL) {
+ /* probably a native->native JNI call */
+
+ if (dvmIsNativeMethod(saveBlock->method)) {
+ LOGVV("Popping JNI stack frame for %s.%s%s\n",
+ saveBlock->method->clazz->descriptor,
+ saveBlock->method->name,
+ (SAVEAREA_FROM_FP(saveBlock->prevFrame)->method == NULL) ?
+ "" : " (JNI local)");
+ assert(saveBlock->xtra.localRefCookie != 0);
+ //assert(saveBlock->xtra.localRefCookie >= self->jniLocalRefTable.table &&
+ // saveBlock->xtra.localRefCookie <=self->jniLocalRefTable.nextEntry);
+
+ dvmPopJniLocals(self, saveBlock);
+ }
+
+ saveBlock = SAVEAREA_FROM_FP(saveBlock->prevFrame);
+ }
+ if (saveBlock->method != NULL) {
+ LOGE("PopFrame missed the break\n");
+ assert(false);
+ dvmAbort(); // stack trashed -- nowhere to go in this thread
+ }
+
+ LOGVV("POP frame: cur=%p new=%p\n",
+ self->curFrame, saveBlock->prevFrame);
+
+ self->curFrame = saveBlock->prevFrame;
+ return true;
+}
+
+/*
+ * Common code for dvmCallMethodV/A and dvmInvokeMethod.
+ *
+ * Pushes a call frame on, advancing self->curFrame.
+ */
+static ClassObject* callPrep(Thread* self, const Method* method, Object* obj,
+ bool checkAccess)
+{
+ ClassObject* clazz;
+
+#ifndef NDEBUG
+ if (self->status != THREAD_RUNNING) {
+ LOGW("threadid=%d: status=%d on call to %s.%s -\n",
+ self->threadId, self->status,
+ method->clazz->descriptor, method->name);
+ }
+#endif
+
+ assert(self != NULL);
+ assert(method != NULL);
+
+ if (obj != NULL)
+ clazz = obj->clazz;
+ else
+ clazz = method->clazz;
+
+ IF_LOGVV() {
+ char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+ LOGVV("thread=%d native code calling %s.%s %s\n", self->threadId,
+ clazz->descriptor, method->name, desc);
+ free(desc);
+ }
+
+ if (checkAccess) {
+ /* needed for java.lang.reflect.Method.invoke */
+ if (!dvmCheckMethodAccess(dvmGetCaller2Class(self->curFrame),
+ method))
+ {
+ /* note this throws IAException, not IAError */
+ dvmThrowException("Ljava/lang/IllegalAccessException;",
+ "access to method denied");
+ return NULL;
+ }
+ }
+
+ /*
+ * Push a call frame on. If there isn't enough room for ins, locals,
+ * outs, and the saved state, it will throw an exception.
+ *
+ * This updates self->curFrame.
+ */
+ if (dvmIsNativeMethod(method)) {
+ /* native code calling native code the hard way */
+ if (!dvmPushJNIFrame(self, method)) {
+ assert(dvmCheckException(self));
+ return NULL;
+ }
+ } else {
+ /* native code calling interpreted code */
+ if (!dvmPushInterpFrame(self, method)) {
+ assert(dvmCheckException(self));
+ return NULL;
+ }
+ }
+
+ return clazz;
+}
+
+/*
+ * Issue a method call.
+ *
+ * Pass in NULL for "obj" on calls to static methods.
+ *
+ * (Note this can't be inlined because it takes a variable number of args.)
+ */
+void dvmCallMethod(Thread* self, const Method* method, Object* obj,
+ JValue* pResult, ...)
+{
+ va_list args;
+ va_start(args, pResult);
+ dvmCallMethodV(self, method, obj, false, pResult, args);
+ va_end(args);
+}
+
+/*
+ * Issue a method call with a variable number of arguments. We process
+ * the contents of "args" by scanning the method signature.
+ *
+ * Pass in NULL for "obj" on calls to static methods.
+ *
+ * We don't need to take the class as an argument because, in Dalvik,
+ * we don't need to worry about static synchronized methods.
+ */
+void dvmCallMethodV(Thread* self, const Method* method, Object* obj,
+ bool fromJni, JValue* pResult, va_list args)
+{
+ const char* desc = &(method->shorty[1]); // [0] is the return type.
+ int verifyCount = 0;
+ ClassObject* clazz;
+ u4* ins;
+
+ clazz = callPrep(self, method, obj, false);
+ if (clazz == NULL)
+ return;
+
+ /* "ins" for new frame start at frame pointer plus locals */
+ ins = ((u4*)self->curFrame) + (method->registersSize - method->insSize);
+
+ //LOGD(" FP is %p, INs live at >= %p\n", self->curFrame, ins);
+
+ /* put "this" pointer into in0 if appropriate */
+ if (!dvmIsStaticMethod(method)) {
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+ assert(obj != NULL && dvmIsValidObject(obj));
+#endif
+ *ins++ = (u4) obj;
+ verifyCount++;
+ }
+
+ JNIEnv* env = self->jniEnv;
+ while (*desc != '\0') {
+ switch (*(desc++)) {
+ case 'D': case 'J': {
+ u8 val = va_arg(args, u8);
+ memcpy(ins, &val, 8); // EABI prevents direct store
+ ins += 2;
+ verifyCount += 2;
+ break;
+ }
+ case 'F': {
+ /* floats were normalized to doubles; convert back */
+ float f = (float) va_arg(args, double);
+ *ins++ = dvmFloatToU4(f);
+ verifyCount++;
+ break;
+ }
+ case 'L': { /* 'shorty' descr uses L for all refs, incl array */
+ void* argObj = va_arg(args, void*);
+ assert(obj == NULL || dvmIsValidObject(obj));
+ if (fromJni)
+ *ins++ = (u4) dvmDecodeIndirectRef(env, argObj);
+ else
+ *ins++ = (u4) argObj;
+ verifyCount++;
+ break;
+ }
+ default: {
+ /* Z B C S I -- all passed as 32-bit integers */
+ *ins++ = va_arg(args, u4);
+ verifyCount++;
+ break;
+ }
+ }
+ }
+
+#ifndef NDEBUG
+ if (verifyCount != method->insSize) {
+ LOGE("Got vfycount=%d insSize=%d for %s.%s\n", verifyCount,
+ method->insSize, clazz->descriptor, method->name);
+ assert(false);
+ goto bail;
+ }
+#endif
+
+ //dvmDumpThreadStack(dvmThreadSelf());
+
+ if (dvmIsNativeMethod(method)) {
+ TRACE_METHOD_ENTER(self, method);
+ /*
+ * Because we leave no space for local variables, "curFrame" points
+ * directly at the method arguments.
+ */
+ (*method->nativeFunc)(self->curFrame, pResult, method, self);
+ TRACE_METHOD_EXIT(self, method);
+ } else {
+ dvmInterpret(self, method, pResult);
+ }
+
+#ifndef NDEBUG
+bail:
+#endif
+ dvmPopFrame(self);
+}
+
+/*
+ * Issue a method call with arguments provided in an array. We process
+ * the contents of "args" by scanning the method signature.
+ *
+ * The values were likely placed into an uninitialized jvalue array using
+ * the field specifiers, which means that sub-32-bit fields (e.g. short,
+ * boolean) may not have 32 or 64 bits of valid data. This is different
+ * from the varargs invocation where the C compiler does a widening
+ * conversion when calling a function. As a result, we have to be a
+ * little more precise when pulling stuff out.
+ *
+ * "args" may be NULL if the method has no arguments.
+ */
+void dvmCallMethodA(Thread* self, const Method* method, Object* obj,
+ bool fromJni, JValue* pResult, const jvalue* args)
+{
+ const char* desc = &(method->shorty[1]); // [0] is the return type.
+ int verifyCount = 0;
+ ClassObject* clazz;
+ u4* ins;
+
+ clazz = callPrep(self, method, obj, false);
+ if (clazz == NULL)
+ return;
+
+ /* "ins" for new frame start at frame pointer plus locals */
+ ins = ((u4*)self->curFrame) + (method->registersSize - method->insSize);
+
+ /* put "this" pointer into in0 if appropriate */
+ if (!dvmIsStaticMethod(method)) {
+ assert(obj != NULL);
+ *ins++ = (u4) obj; /* obj is a "real" ref */
+ verifyCount++;
+ }
+
+ JNIEnv* env = self->jniEnv;
+ while (*desc != '\0') {
+ switch (*desc++) {
+ case 'D': /* 64-bit quantity; have to use */
+ case 'J': /* memcpy() in case of mis-alignment */
+ memcpy(ins, &args->j, 8);
+ ins += 2;
+ verifyCount++; /* this needs an extra push */
+ break;
+ case 'L': /* includes array refs */
+ if (fromJni)
+ *ins++ = (u4) dvmDecodeIndirectRef(env, args->l);
+ else
+ *ins++ = (u4) args->l;
+ break;
+ case 'F':
+ case 'I':
+ *ins++ = args->i; /* full 32 bits */
+ break;
+ case 'S':
+ *ins++ = args->s; /* 16 bits, sign-extended */
+ break;
+ case 'C':
+ *ins++ = args->c; /* 16 bits, unsigned */
+ break;
+ case 'B':
+ *ins++ = args->b; /* 8 bits, sign-extended */
+ break;
+ case 'Z':
+ *ins++ = args->z; /* 8 bits, zero or non-zero */
+ break;
+ default:
+ LOGE("Invalid char %c in short signature of %s.%s\n",
+ *(desc-1), clazz->descriptor, method->name);
+ assert(false);
+ goto bail;
+ }
+
+ verifyCount++;
+ args++;
+ }
+
+#ifndef NDEBUG
+ if (verifyCount != method->insSize) {
+ LOGE("Got vfycount=%d insSize=%d for %s.%s\n", verifyCount,
+ method->insSize, clazz->descriptor, method->name);
+ assert(false);
+ goto bail;
+ }
+#endif
+
+ if (dvmIsNativeMethod(method)) {
+ TRACE_METHOD_ENTER(self, method);
+ /*
+ * Because we leave no space for local variables, "curFrame" points
+ * directly at the method arguments.
+ */
+ (*method->nativeFunc)(self->curFrame, pResult, method, self);
+ TRACE_METHOD_EXIT(self, method);
+ } else {
+ dvmInterpret(self, method, pResult);
+ }
+
+bail:
+ dvmPopFrame(self);
+}
+
+/*
+ * Invoke a method, using the specified arguments and return type, through
+ * one of the reflection interfaces. Could be a virtual or direct method
+ * (including constructors). Used for reflection.
+ *
+ * Deals with boxing/unboxing primitives and performs widening conversions.
+ *
+ * "invokeObj" will be null for a static method.
+ *
+ * If the invocation returns with an exception raised, we have to wrap it.
+ */
+Object* dvmInvokeMethod(Object* obj, const Method* method,
+ ArrayObject* argList, ArrayObject* params, ClassObject* returnType,
+ bool noAccessCheck)
+{
+ ClassObject* clazz;
+ Object* retObj = NULL;
+ Thread* self = dvmThreadSelf();
+ s4* ins;
+ int verifyCount, argListLength;
+ JValue retval;
+ bool needPop = false;
+
+ /* verify arg count */
+ if (argList != NULL)
+ argListLength = argList->length;
+ else
+ argListLength = 0;
+ if (argListLength != (int) params->length) {
+ LOGI("invoke: expected %d args, received %d args\n",
+ params->length, argListLength);
+ dvmThrowException("Ljava/lang/IllegalArgumentException;",
+ "wrong number of arguments");
+ return NULL;
+ }
+
+ clazz = callPrep(self, method, obj, !noAccessCheck);
+ if (clazz == NULL)
+ return NULL;
+ needPop = true;
+
+ /* "ins" for new frame start at frame pointer plus locals */
+ ins = ((s4*)self->curFrame) + (method->registersSize - method->insSize);
+ verifyCount = 0;
+
+ //LOGD(" FP is %p, INs live at >= %p\n", self->curFrame, ins);
+
+ /* put "this" pointer into in0 if appropriate */
+ if (!dvmIsStaticMethod(method)) {
+ assert(obj != NULL);
+ *ins++ = (s4) obj;
+ verifyCount++;
+ }
+
+ /*
+ * Copy the args onto the stack. Primitive types are converted when
+ * necessary, and object types are verified.
+ */
+ DataObject** args;
+ ClassObject** types;
+ int i;
+
+ args = (DataObject**) argList->contents;
+ types = (ClassObject**) params->contents;
+ for (i = 0; i < argListLength; i++) {
+ int width;
+
+ width = dvmConvertArgument(*args++, *types++, ins);
+ if (width < 0) {
+ if (*(args-1) != NULL) {
+ LOGV("invoke: type mismatch on arg %d ('%s' '%s')\n",
+ i, (*(args-1))->obj.clazz->descriptor,
+ (*(types-1))->descriptor);
+ }
+ dvmPopFrame(self); // throw wants to pull PC out of stack
+ needPop = false;
+ dvmThrowException("Ljava/lang/IllegalArgumentException;",
+ "argument type mismatch");
+ goto bail;
+ }
+
+ ins += width;
+ verifyCount += width;
+ }
+
+ if (verifyCount != method->insSize) {
+ LOGE("Got vfycount=%d insSize=%d for %s.%s\n", verifyCount,
+ method->insSize, clazz->descriptor, method->name);
+ assert(false);
+ goto bail;
+ }
+ //dvmDumpThreadStack(dvmThreadSelf());
+
+ if (dvmIsNativeMethod(method)) {
+ TRACE_METHOD_ENTER(self, method);
+ /*
+ * Because we leave no space for local variables, "curFrame" points
+ * directly at the method arguments.
+ */
+ (*method->nativeFunc)(self->curFrame, &retval, method, self);
+ TRACE_METHOD_EXIT(self, method);
+ } else {
+ dvmInterpret(self, method, &retval);
+ }
+
+ /*
+ * Pop the frame immediately. The "wrap" calls below can cause
+ * allocations, and we don't want the GC to walk the now-dead frame.
+ */
+ dvmPopFrame(self);
+ needPop = false;
+
+ /*
+ * If an exception is raised, wrap and replace. This is necessary
+ * because the invoked method could have thrown a checked exception
+ * that the caller wasn't prepared for.
+ *
+ * We might be able to do this up in the interpreted code, but that will
+ * leave us with a shortened stack trace in the top-level exception.
+ */
+ if (dvmCheckException(self)) {
+ dvmWrapException("Ljava/lang/reflect/InvocationTargetException;");
+ } else {
+ /*
+ * If this isn't a void method or constructor, convert the return type
+ * to an appropriate object.
+ *
+ * We don't do this when an exception is raised because the value
+ * in "retval" is undefined.
+ */
+ if (returnType != NULL) {
+ retObj = (Object*)dvmWrapPrimitive(retval, returnType);
+ dvmReleaseTrackedAlloc(retObj, NULL);
+ }
+ }
+
+bail:
+ if (needPop) {
+ dvmPopFrame(self);
+ }
+ return retObj;
+}
+
+typedef struct LineNumFromPcContext {
+ u4 address;
+ u4 lineNum;
+} LineNumFromPcContext;
+
+static int lineNumForPcCb(void *cnxt, u4 address, u4 lineNum)
+{
+ LineNumFromPcContext *pContext = (LineNumFromPcContext *)cnxt;
+
+ // We know that this callback will be called in
+ // ascending address order, so keep going until we find
+ // a match or we've just gone past it.
+
+ if (address > pContext->address) {
+ // The line number from the previous positions callback
+ // wil be the final result.
+ return 1;
+ }
+
+ pContext->lineNum = lineNum;
+
+ return (address == pContext->address) ? 1 : 0;
+}
+
+/*
+ * Determine the source file line number based on the program counter.
+ * "pc" is an offset, in 16-bit units, from the start of the method's code.
+ *
+ * Returns -1 if no match was found (possibly because the source files were
+ * compiled without "-g", so no line number information is present).
+ * Returns -2 for native methods (as expected in exception traces).
+ */
+int dvmLineNumFromPC(const Method* method, u4 relPc)
+{
+ const DexCode* pDexCode = dvmGetMethodCode(method);
+
+ if (pDexCode == NULL) {
+ if (dvmIsNativeMethod(method) && !dvmIsAbstractMethod(method))
+ return -2;
+ return -1; /* can happen for abstract method stub */
+ }
+
+ LineNumFromPcContext context;
+ memset(&context, 0, sizeof(context));
+ context.address = relPc;
+ // A method with no line number info should return -1
+ context.lineNum = -1;
+
+ dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile, pDexCode,
+ method->clazz->descriptor,
+ method->prototype.protoIdx,
+ method->accessFlags,
+ lineNumForPcCb, NULL, &context);
+
+ return context.lineNum;
+}
+
+/*
+ * Compute the frame depth.
+ *
+ * Excludes "break" frames.
+ */
+int dvmComputeExactFrameDepth(const void* fp)
+{
+ int count = 0;
+
+ for ( ; fp != NULL; fp = SAVEAREA_FROM_FP(fp)->prevFrame) {
+ if (!dvmIsBreakFrame(fp))
+ count++;
+ }
+
+ return count;
+}
+
+/*
+ * Compute the "vague" frame depth, which is just a pointer subtraction.
+ * The result is NOT an overly generous assessment of the number of
+ * frames; the only meaningful use is to compare against the result of
+ * an earlier invocation.
+ *
+ * Useful for implementing single-step debugger modes, which may need to
+ * call this for every instruction.
+ */
+int dvmComputeVagueFrameDepth(Thread* thread, const void* fp)
+{
+ const u1* interpStackStart = thread->interpStackStart;
+
+ assert((u1*) fp >= interpStackStart - thread->interpStackSize);
+ assert((u1*) fp < interpStackStart);
+ return interpStackStart - (u1*) fp;
+}
+
+/*
+ * Get the calling frame. Pass in the current fp.
+ *
+ * Skip "break" frames and reflection invoke frames.
+ */
+void* dvmGetCallerFP(const void* curFrame)
+{
+ void* caller = SAVEAREA_FROM_FP(curFrame)->prevFrame;
+ StackSaveArea* saveArea;
+
+retry:
+ if (dvmIsBreakFrame(caller)) {
+ /* pop up one more */
+ caller = SAVEAREA_FROM_FP(caller)->prevFrame;
+ if (caller == NULL)
+ return NULL; /* hit the top */
+
+ /*
+ * If we got here by java.lang.reflect.Method.invoke(), we don't
+ * want to return Method's class loader. Shift up one and try
+ * again.
+ */
+ saveArea = SAVEAREA_FROM_FP(caller);
+ if (dvmIsReflectionMethod(saveArea->method)) {
+ caller = saveArea->prevFrame;
+ assert(caller != NULL);
+ goto retry;
+ }
+ }
+
+ return caller;
+}
+
+/*
+ * Get the caller's class. Pass in the current fp.
+ *
+ * This is used by e.g. java.lang.Class.
+ */
+ClassObject* dvmGetCallerClass(const void* curFrame)
+{
+ void* caller;
+
+ caller = dvmGetCallerFP(curFrame);
+ if (caller == NULL)
+ return NULL;
+
+ return SAVEAREA_FROM_FP(caller)->method->clazz;
+}
+
+/*
+ * Get the caller's caller's class. Pass in the current fp.
+ *
+ * This is used by e.g. java.lang.Class, which wants to know about the
+ * class loader of the method that called it.
+ */
+ClassObject* dvmGetCaller2Class(const void* curFrame)
+{
+ void* caller = SAVEAREA_FROM_FP(curFrame)->prevFrame;
+ void* callerCaller;
+
+ /* at the top? */
+ if (dvmIsBreakFrame(caller) && SAVEAREA_FROM_FP(caller)->prevFrame == NULL)
+ return NULL;
+
+ /* go one more */
+ callerCaller = dvmGetCallerFP(caller);
+ if (callerCaller == NULL)
+ return NULL;
+
+ return SAVEAREA_FROM_FP(callerCaller)->method->clazz;
+}
+
+/*
+ * Get the caller's caller's caller's class. Pass in the current fp.
+ *
+ * This is used by e.g. java.lang.Class, which wants to know about the
+ * class loader of the method that called it.
+ */
+ClassObject* dvmGetCaller3Class(const void* curFrame)
+{
+ void* caller = SAVEAREA_FROM_FP(curFrame)->prevFrame;
+ int i;
+
+ /* at the top? */
+ if (dvmIsBreakFrame(caller) && SAVEAREA_FROM_FP(caller)->prevFrame == NULL)
+ return NULL;
+
+ /* Walk up two frames if possible. */
+ for (i = 0; i < 2; i++) {
+ caller = dvmGetCallerFP(caller);
+ if (caller == NULL)
+ return NULL;
+ }
+
+ return SAVEAREA_FROM_FP(caller)->method->clazz;
+}
+
+/*
+ * Create a flat array of methods that comprise the current interpreter
+ * stack trace. Pass in the current frame ptr.
+ *
+ * Allocates a new array and fills it with method pointers. Break frames
+ * are skipped, but reflection invocations are not. The caller must free
+ * "*pArray".
+ *
+ * The current frame will be in element 0.
+ *
+ * Returns "true" on success, "false" on failure (e.g. malloc failed).
+ */
+bool dvmCreateStackTraceArray(const void* fp, const Method*** pArray,
+ int* pLength)
+{
+ const Method** array;
+ int idx, depth;
+
+ depth = dvmComputeExactFrameDepth(fp);
+ array = (const Method**) malloc(depth * sizeof(Method*));
+ if (array == NULL)
+ return false;
+
+ for (idx = 0; fp != NULL; fp = SAVEAREA_FROM_FP(fp)->prevFrame) {
+ if (!dvmIsBreakFrame(fp))
+ array[idx++] = SAVEAREA_FROM_FP(fp)->method;
+ }
+ assert(idx == depth);
+
+ *pArray = array;
+ *pLength = depth;
+ return true;
+}
+
+/*
+ * Open up the reserved area and throw an exception. The reserved area
+ * should only be needed to create and initialize the exception itself.
+ *
+ * If we already opened it and we're continuing to overflow, abort the VM.
+ *
+ * We have to leave the "reserved" area open until the "catch" handler has
+ * finished doing its processing. This is because the catch handler may
+ * need to resolve classes, which requires calling into the class loader if
+ * the classes aren't already in the "initiating loader" list.
+ */
+void dvmHandleStackOverflow(Thread* self, const Method* method)
+{
+ /*
+ * Can we make the reserved area available?
+ */
+ if (self->stackOverflowed) {
+ /*
+ * Already did, nothing to do but bail.
+ */
+ LOGE("DalvikVM: double-overflow of stack in threadid=%d; aborting\n",
+ self->threadId);
+ dvmDumpThread(self, false);
+ dvmAbort();
+ }
+
+ /* open it up to the full range */
+ LOGI("threadid=%d: stack overflow on call to %s.%s:%s\n",
+ self->threadId,
+ method->clazz->descriptor, method->name, method->shorty);
+ StackSaveArea* saveArea = SAVEAREA_FROM_FP(self->curFrame);
+ LOGI(" method requires %d+%d+%d=%d bytes, fp is %p (%d left)\n",
+ method->registersSize * 4, sizeof(StackSaveArea), method->outsSize * 4,
+ (method->registersSize + method->outsSize) * 4 + sizeof(StackSaveArea),
+ saveArea, (u1*) saveArea - self->interpStackEnd);
+ LOGI(" expanding stack end (%p to %p)\n", self->interpStackEnd,
+ self->interpStackStart - self->interpStackSize);
+ //dvmDumpThread(self, false);
+ self->interpStackEnd = self->interpStackStart - self->interpStackSize;
+ self->stackOverflowed = true;
+
+ /*
+ * If we were trying to throw an exception when the stack overflowed,
+ * we will blow up when doing the class lookup on StackOverflowError
+ * because of the pending exception. So, we clear it and make it
+ * the cause of the SOE.
+ */
+ Object* excep = dvmGetException(self);
+ if (excep != NULL) {
+ LOGW("Stack overflow while throwing exception\n");
+ dvmClearException(self);
+ }
+ dvmThrowChainedExceptionByClass(gDvm.classJavaLangStackOverflowError,
+ NULL, excep);
+}
+
+/*
+ * Reduce the available stack size. By this point we should have finished
+ * our overflow processing.
+ */
+void dvmCleanupStackOverflow(Thread* self, const Object* exception)
+{
+ const u1* newStackEnd;
+
+ assert(self->stackOverflowed);
+
+ if (exception->clazz != gDvm.classJavaLangStackOverflowError) {
+ /* exception caused during SOE, not the SOE itself */
+ return;
+ }
+
+ newStackEnd = (self->interpStackStart - self->interpStackSize)
+ + STACK_OVERFLOW_RESERVE;
+ if ((u1*)self->curFrame <= newStackEnd) {
+ LOGE("Can't shrink stack: curFrame is in reserved area (%p %p)\n",
+ self->interpStackEnd, self->curFrame);
+ dvmDumpThread(self, false);
+ dvmAbort();
+ }
+
+ self->interpStackEnd = newStackEnd;
+ self->stackOverflowed = false;
+
+ LOGI("Shrank stack (to %p, curFrame is %p)\n", self->interpStackEnd,
+ self->curFrame);
+}
+
+
+/*
+ * Extract the object that is the target of a monitor-enter instruction
+ * in the top stack frame of "thread".
+ *
+ * The other thread might be alive, so this has to work carefully.
+ *
+ * We assume the thread list lock is currently held.
+ *
+ * Returns "true" if we successfully recover the object. "*pOwner" will
+ * be NULL if we can't determine the owner for some reason (e.g. race
+ * condition on ownership transfer).
+ */
+static bool extractMonitorEnterObject(Thread* thread, Object** pLockObj,
+ Thread** pOwner)
+{
+ void* framePtr = thread->curFrame;
+
+ if (framePtr == NULL || dvmIsBreakFrame(framePtr))
+ return false;
+
+ const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
+ const Method* method = saveArea->method;
+ const u2* currentPc = saveArea->xtra.currentPc;
+
+ /* check Method* */
+ if (!dvmLinearAllocContains(method, sizeof(Method))) {
+ LOGD("ExtrMon: method %p not valid\n", method);
+ return false;
+ }
+
+ /* check currentPc */
+ u4 insnsSize = dvmGetMethodInsnsSize(method);
+ if (currentPc < method->insns ||
+ currentPc >= method->insns + insnsSize)
+ {
+ LOGD("ExtrMon: insns %p not valid (%p - %p)\n",
+ currentPc, method->insns, method->insns + insnsSize);
+ return false;
+ }
+
+ /* check the instruction */
+ if ((*currentPc & 0xff) != OP_MONITOR_ENTER) {
+ LOGD("ExtrMon: insn at %p is not monitor-enter (0x%02x)\n",
+ currentPc, *currentPc & 0xff);
+ return false;
+ }
+
+ /* get and check the register index */
+ unsigned int reg = *currentPc >> 8;
+ if (reg >= method->registersSize) {
+ LOGD("ExtrMon: invalid register %d (max %d)\n",
+ reg, method->registersSize);
+ return false;
+ }
+
+ /* get and check the object in that register */
+ u4* fp = (u4*) framePtr;
+ Object* obj = (Object*) fp[reg];
+ if (!dvmIsValidObject(obj)) {
+ LOGD("ExtrMon: invalid object %p at %p[%d]\n", obj, fp, reg);
+ return false;
+ }
+ *pLockObj = obj;
+
+ /*
+ * Try to determine the object's lock holder; it's okay if this fails.
+ *
+ * We're assuming the thread list lock is already held by this thread.
+ * If it's not, we may be living dangerously if we have to scan through
+ * the thread list to find a match. (The VM will generally be in a
+ * suspended state when executing here, so this is a minor concern
+ * unless we're dumping while threads are running, in which case there's
+ * a good chance of stuff blowing up anyway.)
+ */
+ *pOwner = dvmGetObjectLockHolder(obj);
+
+ return true;
+}
+
+/*
+ * Dump stack frames, starting from the specified frame and moving down.
+ *
+ * Each frame holds a pointer to the currently executing method, and the
+ * saved program counter from the caller ("previous" frame). This means
+ * we don't have the PC for the current method on the stack, which is
+ * pretty reasonable since it's in the "PC register" for the VM. Because
+ * exceptions need to show the correct line number we actually *do* have
+ * an updated version in the fame's "xtra.currentPc", but it's unreliable.
+ *
+ * Note "framePtr" could be NULL in rare circumstances.
+ */
+static void dumpFrames(const DebugOutputTarget* target, void* framePtr,
+ Thread* thread)
+{
+ const StackSaveArea* saveArea;
+ const Method* method;
+ int checkCount = 0;
+ const u2* currentPc = NULL;
+ bool first = true;
+
+ /*
+ * The "currentPc" is updated whenever we execute an instruction that
+ * might throw an exception. Show it here.
+ */
+ if (framePtr != NULL && !dvmIsBreakFrame(framePtr)) {
+ saveArea = SAVEAREA_FROM_FP(framePtr);
+
+ if (saveArea->xtra.currentPc != NULL)
+ currentPc = saveArea->xtra.currentPc;
+ }
+
+ while (framePtr != NULL) {
+ saveArea = SAVEAREA_FROM_FP(framePtr);
+ method = saveArea->method;
+
+ if (dvmIsBreakFrame(framePtr)) {
+ //dvmPrintDebugMessage(target, " (break frame)\n");
+ } else {
+ int relPc;
+
+ if (currentPc != NULL)
+ relPc = currentPc - saveArea->method->insns;
+ else
+ relPc = -1;
+
+ char* className = dvmDescriptorToDot(method->clazz->descriptor);
+ if (dvmIsNativeMethod(method))
+ dvmPrintDebugMessage(target,
+ " at %s.%s(Native Method)\n", className, method->name);
+ else {
+ dvmPrintDebugMessage(target,
+ " at %s.%s(%s:%s%d)\n",
+ className, method->name, dvmGetMethodSourceFile(method),
+ (relPc >= 0 && first) ? "~" : "",
+ relPc < 0 ? -1 : dvmLineNumFromPC(method, relPc));
+ }
+ free(className);
+
+ if (first) {
+ /*
+ * Decorate WAIT and MONITOR threads with some detail on
+ * the first frame.
+ *
+ * warning: wait status not stable, even in suspend
+ */
+ if (thread->status == THREAD_WAIT ||
+ thread->status == THREAD_TIMED_WAIT)
+ {
+ Monitor* mon = thread->waitMonitor;
+ Object* obj = dvmGetMonitorObject(mon);
+ if (obj != NULL) {
+ className = dvmDescriptorToDot(obj->clazz->descriptor);
+ dvmPrintDebugMessage(target,
+ " - waiting on <%p> (a %s)\n", obj, className);
+ free(className);
+ }
+ } else if (thread->status == THREAD_MONITOR) {
+ Object* obj;
+ Thread* owner;
+ if (extractMonitorEnterObject(thread, &obj, &owner)) {
+ className = dvmDescriptorToDot(obj->clazz->descriptor);
+ if (owner != NULL) {
+ char* threadName = dvmGetThreadName(owner);
+ dvmPrintDebugMessage(target,
+ " - waiting to lock <%p> (a %s) held by threadid=%d (%s)\n",
+ obj, className, owner->threadId, threadName);
+ free(threadName);
+ } else {
+ dvmPrintDebugMessage(target,
+ " - waiting to lock <%p> (a %s) held by ???\n",
+ obj, className);
+ }
+ free(className);
+ }
+ }
+ }
+ }
+
+ /*
+ * Get saved PC for previous frame. There's no savedPc in a "break"
+ * frame, because that represents native or interpreted code
+ * invoked by the VM. The saved PC is sitting in the "PC register",
+ * a local variable on the native stack.
+ */
+ currentPc = saveArea->savedPc;
+
+ first = false;
+
+ if (saveArea->prevFrame != NULL && saveArea->prevFrame <= framePtr) {
+ LOGW("Warning: loop in stack trace at frame %d (%p -> %p)\n",
+ checkCount, framePtr, saveArea->prevFrame);
+ break;
+ }
+ framePtr = saveArea->prevFrame;
+
+ checkCount++;
+ if (checkCount > 300) {
+ dvmPrintDebugMessage(target,
+ " ***** printed %d frames, not showing any more\n",
+ checkCount);
+ break;
+ }
+ }
+ dvmPrintDebugMessage(target, "\n");
+}
+
+
+/*
+ * Dump the stack for the specified thread.
+ */
+void dvmDumpThreadStack(const DebugOutputTarget* target, Thread* thread)
+{
+ dumpFrames(target, thread->curFrame, thread);
+}
+
+/*
+ * Dump the stack for the specified thread, which is still running.
+ *
+ * This is very dangerous, because stack frames are being pushed on and
+ * popped off, and if the thread exits we'll be looking at freed memory.
+ * The plan here is to take a snapshot of the stack and then dump that
+ * to try to minimize the chances of catching it mid-update. This should
+ * work reasonably well on a single-CPU system.
+ *
+ * There is a small chance that calling here will crash the VM.
+ */
+void dvmDumpRunningThreadStack(const DebugOutputTarget* target, Thread* thread)
+{
+ StackSaveArea* saveArea;
+ const u1* origStack;
+ u1* stackCopy = NULL;
+ int origSize, fpOffset;
+ void* fp;
+ int depthLimit = 200;
+
+ if (thread == NULL || thread->curFrame == NULL) {
+ dvmPrintDebugMessage(target,
+ "DumpRunning: Thread at %p has no curFrame (threadid=%d)\n",
+ thread, (thread != NULL) ? thread->threadId : 0);
+ return;
+ }
+
+ /* wait for a full quantum */
+ sched_yield();
+
+ /* copy the info we need, then the stack itself */
+ origSize = thread->interpStackSize;
+ origStack = (const u1*) thread->interpStackStart - origSize;
+ stackCopy = (u1*) malloc(origSize);
+ fpOffset = (u1*) thread->curFrame - origStack;
+ memcpy(stackCopy, origStack, origSize);
+
+ /*
+ * Run through the stack and rewrite the "prev" pointers.
+ */
+ //LOGI("DR: fpOff=%d (from %p %p)\n",fpOffset, origStack, thread->curFrame);
+ fp = stackCopy + fpOffset;
+ while (true) {
+ int prevOffset;
+
+ if (depthLimit-- < 0) {
+ /* we're probably screwed */
+ dvmPrintDebugMessage(target, "DumpRunning: depth limit hit\n");
+ dvmAbort();
+ }
+ saveArea = SAVEAREA_FROM_FP(fp);
+ if (saveArea->prevFrame == NULL)
+ break;
+
+ prevOffset = (u1*) saveArea->prevFrame - origStack;
+ if (prevOffset < 0 || prevOffset > origSize) {
+ dvmPrintDebugMessage(target,
+ "DumpRunning: bad offset found: %d (from %p %p)\n",
+ prevOffset, origStack, saveArea->prevFrame);
+ saveArea->prevFrame = NULL;
+ break;
+ }
+
+ saveArea->prevFrame = stackCopy + prevOffset;
+ fp = saveArea->prevFrame;
+ }
+
+ /*
+ * We still need to pass the Thread for some monitor wait stuff.
+ */
+ dumpFrames(target, stackCopy + fpOffset, thread);
+ free(stackCopy);
+}
diff --git a/vm/interp/Stack.h b/vm/interp/Stack.h
new file mode 100644
index 0000000..3f76cb1
--- /dev/null
+++ b/vm/interp/Stack.h
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Stack frames, and uses thereof.
+ */
+#ifndef _DALVIK_INTERP_STACK
+#define _DALVIK_INTERP_STACK
+
+#include "jni.h"
+#include <stdarg.h>
+
+
+/*
+Stack layout
+
+In what follows, the "top" of the stack is at a low position in memory,
+and the "bottom" of the stack is in a high position (put more simply,
+they grow downward). They may be merged with the native stack at a
+later date. The interpreter assumes that they have a fixed size,
+determined when the thread is created.
+
+Dalvik's registers (of which there can be up to 64K) map to the "ins"
+(method arguments) and "locals" (local variables). The "outs" (arguments
+to called methods) are specified by the "invoke" operand. The return
+value, which is passed through the interpreter rather than on the stack,
+is retrieved with a "move-result" instruction.
+
+ Low addresses (0x00000000)
+
+ +- - - - - - - - -+
+ - out0 -
+ +-----------------+ <-- stack ptr (top of stack)
+ + VM-specific +
+ + internal goop +
+ +-----------------+ <-- curFrame: FP for cur function
+ + v0 == local0 +
++-----------------+ +-----------------+
++ out0 + + v1 == in0 +
++-----------------+ +-----------------+
++ out1 + + v2 == in1 +
++-----------------+ +-----------------+
++ VM-specific +
++ internal goop +
++-----------------+ <-- frame ptr (FP) for previous function
++ v0 == local0 +
++-----------------+
++ v1 == local1 +
++-----------------+
++ v2 == in0 +
++-----------------+
++ v3 == in1 +
++-----------------+
++ v4 == in2 +
++-----------------+
+- -
+- -
+- -
++-----------------+ <-- interpStackStart
+
+ High addresses (0xffffffff)
+
+Note the "ins" and "outs" overlap -- values pushed into the "outs" area
+become the parameters to the called method. The VM guarantees that there
+will be enough room for all possible "outs" on the stack before calling
+into a method.
+
+All "V registers" are 32 bits, and all stack entries are 32-bit aligned.
+Registers are accessed as a positive offset from the frame pointer,
+e.g. register v2 is fp[2]. 64-bit quantities are stored in two adjacent
+registers, addressed by the lower-numbered register, and are in host order.
+64-bit quantities do not need to start in an even-numbered register.
+
+We push two stack frames on when calling an interpreted or native method
+directly from the VM (e.g. invoking <clinit> or via reflection "invoke()").
+The first is a "break" frame, which allows us to tell when a call return or
+exception unroll has reached the VM call site. Without the break frame the
+stack might look like an uninterrupted series of interpreted method calls.
+The second frame is for the method itself.
+
+The "break" frame is used as an alternative to adding additional fields
+to the StackSaveArea struct itself. They are recognized by having a
+NULL method pointer.
+
+When calling a native method from interpreted code, the stack setup is
+essentially identical to calling an interpreted method. Because it's a
+native method, though, there are never any "locals" or "outs".
+
+For native calls into JNI, we want to store a table of local references
+on the stack. The GC needs to scan them while the native code is running,
+and we want to trivially discard them when the method returns. See JNI.c
+for a discussion of how this is managed. In particular note that it is
+possible to push additional call frames on without calling a method.
+*/
+
+
+struct StackSaveArea;
+typedef struct StackSaveArea StackSaveArea;
+
+//#define PAD_SAVE_AREA /* help debug stack trampling */
+
+/*
+ * The VM-specific internal goop.
+ *
+ * The idea is to mimic a typical native stack frame, with copies of the
+ * saved PC and FP. At some point we'd like to have interpreted and
+ * native code share the same stack, though this makes portability harder.
+ */
+struct StackSaveArea {
+#ifdef PAD_SAVE_AREA
+ u4 pad0, pad1, pad2;
+#endif
+
+#ifdef EASY_GDB
+ /* make it easier to trek through stack frames in GDB */
+ StackSaveArea* prevSave;
+#endif
+
+ /* saved frame pointer for previous frame, or NULL if this is at bottom */
+ void* prevFrame;
+
+ /* saved program counter (from method in caller's frame) */
+ const u2* savedPc;
+
+ /* pointer to method we're *currently* executing; handy for exceptions */
+ const Method* method;
+
+ union {
+ /* for JNI native methods: bottom of local reference segment */
+#ifdef USE_INDIRECT_REF
+ u4 localRefCookie;
+#else
+ Object** localRefCookie;
+#endif
+
+ /* for interpreted methods: saved current PC, for exception stack
+ * traces and debugger traces */
+ const u2* currentPc;
+ } xtra;
+
+ /* Native return pointer for JIT, or 0 if interpreted */
+ const u2* returnAddr;
+#ifdef PAD_SAVE_AREA
+ u4 pad3, pad4, pad5;
+#endif
+};
+
+/* move between the stack save area and the frame pointer */
+#define SAVEAREA_FROM_FP(_fp) ((StackSaveArea*)(_fp) -1)
+#define FP_FROM_SAVEAREA(_save) ((void*) ((StackSaveArea*)(_save) +1))
+
+/* when calling a function, get a pointer to outs[0] */
+#define OUTS_FROM_FP(_fp, _argCount) \
+ ((u4*) ((u1*)SAVEAREA_FROM_FP(_fp) - sizeof(u4) * (_argCount)))
+
+/* reserve this many bytes for handling StackOverflowError */
+#define STACK_OVERFLOW_RESERVE 768
+
+/*
+ * Determine if the frame pointer points to a "break frame".
+ */
+INLINE bool dvmIsBreakFrame(const u4* fp)
+{
+ return SAVEAREA_FROM_FP(fp)->method == NULL;
+}
+
+/*
+ * Initialize the interp stack (call this after allocating storage and
+ * setting thread->interpStackStart).
+ */
+bool dvmInitInterpStack(Thread* thread, int stackSize);
+
+/*
+ * Push a native method frame directly onto the stack. Used to push the
+ * "fake" native frames at the top of each thread stack.
+ */
+bool dvmPushJNIFrame(Thread* thread, const Method* method);
+
+/*
+ * JNI local frame management.
+ */
+bool dvmPushLocalFrame(Thread* thread, const Method* method);
+bool dvmPopLocalFrame(Thread* thread);
+
+/*
+ * Call an interpreted method from native code. If this is being called
+ * from a JNI function, references in the argument list will be converted
+ * back to pointers.
+ *
+ * "obj" should be NULL for "direct" methods.
+ */
+void dvmCallMethod(Thread* self, const Method* method, Object* obj,
+ JValue* pResult, ...);
+void dvmCallMethodV(Thread* self, const Method* method, Object* obj,
+ bool fromJni, JValue* pResult, va_list args);
+void dvmCallMethodA(Thread* self, const Method* method, Object* obj,
+ bool fromJni, JValue* pResult, const jvalue* args);
+
+/*
+ * Invoke a method, using the specified arguments and return type, through
+ * a reflection interface.
+ *
+ * Deals with boxing/unboxing primitives and performs widening conversions.
+ *
+ * "obj" should be null for a static method.
+ *
+ * "params" and "returnType" come from the Method object, so we don't have
+ * to re-generate them from the method signature. "returnType" should be
+ * NULL if we're invoking a constructor.
+ */
+Object* dvmInvokeMethod(Object* invokeObj, const Method* meth,
+ ArrayObject* argList, ArrayObject* params, ClassObject* returnType,
+ bool noAccessCheck);
+
+/*
+ * Determine the source file line number, given the program counter offset
+ * into the specified method. Returns -2 for native methods, -1 if no
+ * match was found.
+ */
+int dvmLineNumFromPC(const Method* method, u4 relPc);
+
+/*
+ * Given a frame pointer, compute the current call depth. The value can be
+ * "exact" (a count of non-break frames) or "vague" (just subtracting
+ * pointers to give relative values).
+ */
+int dvmComputeExactFrameDepth(const void* fp);
+int dvmComputeVagueFrameDepth(Thread* thread, const void* fp);
+
+/*
+ * Get the frame pointer for the caller's stack frame.
+ */
+void* dvmGetCallerFP(const void* curFrame);
+
+/*
+ * Get the class of the method that called us.
+ */
+ClassObject* dvmGetCallerClass(const void* curFrame);
+
+/*
+ * Get the caller's caller's class. Pass in the current fp.
+ *
+ * This is used by e.g. java.lang.Class, which wants to know about the
+ * class loader of the method that called it.
+ */
+ClassObject* dvmGetCaller2Class(const void* curFrame);
+
+/*
+ * Get the caller's caller's caller's class. Pass in the current fp.
+ *
+ * This is used by e.g. java.lang.Class, which wants to know about the
+ * class loader of the method that called it.
+ */
+ClassObject* dvmGetCaller3Class(const void* curFrame);
+
+/*
+ * Allocate and fill an array of method pointers representing the current
+ * stack trace (element 0 is current frame).
+ */
+bool dvmCreateStackTraceArray(const void* fp, const Method*** pArray,
+ int* pLength);
+
+/*
+ * Common handling for stack overflow.
+ */
+void dvmHandleStackOverflow(Thread* self, const Method* method);
+void dvmCleanupStackOverflow(Thread* self, const Object* exception);
+
+/* debugging; dvmDumpThread() is probably a better starting point */
+void dvmDumpThreadStack(const DebugOutputTarget* target, Thread* thread);
+void dvmDumpRunningThreadStack(const DebugOutputTarget* target, Thread* thread);
+
+#endif /*_DALVIK_INTERP_STACK*/
diff --git a/vm/jdwp/ExpandBuf.c b/vm/jdwp/ExpandBuf.c
new file mode 100644
index 0000000..ade239c
--- /dev/null
+++ b/vm/jdwp/ExpandBuf.c
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Implementation of an expandable byte buffer. Designed for serializing
+ * primitive values, e.g. JDWP replies.
+ */
+#include "jdwp/ExpandBuf.h"
+#include "Bits.h"
+#include "Common.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * Data structure used to track buffer use.
+ */
+struct ExpandBuf {
+ u1* storage;
+ int curLen;
+ int maxLen;
+};
+
+#define kInitialStorage 64
+
+/*
+ * Allocate a JdwpBuf and some initial storage.
+ */
+ExpandBuf* expandBufAlloc(void)
+{
+ ExpandBuf* newBuf;
+
+ newBuf = (ExpandBuf*) malloc(sizeof(*newBuf));
+ newBuf->storage = (u1*) malloc(kInitialStorage);
+ newBuf->curLen = 0;
+ newBuf->maxLen = kInitialStorage;
+
+ return newBuf;
+}
+
+/*
+ * Free a JdwpBuf and associated storage.
+ */
+void expandBufFree(ExpandBuf* pBuf)
+{
+ if (pBuf == NULL)
+ return;
+
+ free(pBuf->storage);
+ free(pBuf);
+}
+
+/*
+ * Get a pointer to the start of the buffer.
+ */
+u1* expandBufGetBuffer(ExpandBuf* pBuf)
+{
+ return pBuf->storage;
+}
+
+/*
+ * Get the amount of data currently in the buffer.
+ */
+size_t expandBufGetLength(ExpandBuf* pBuf)
+{
+ return pBuf->curLen;
+}
+
+
+/*
+ * Ensure that the buffer has enough space to hold incoming data. If it
+ * doesn't, resize the buffer.
+ */
+static void ensureSpace(ExpandBuf* pBuf, int newCount)
+{
+ u1* newPtr;
+
+ if (pBuf->curLen + newCount <= pBuf->maxLen)
+ return;
+
+ while (pBuf->curLen + newCount > pBuf->maxLen)
+ pBuf->maxLen *= 2;
+
+ newPtr = realloc(pBuf->storage, pBuf->maxLen);
+ if (newPtr == NULL) {
+ LOGE("realloc(%d) failed\n", pBuf->maxLen);
+ abort();
+ }
+
+ pBuf->storage = newPtr;
+}
+
+/*
+ * Allocate some space in the buffer.
+ */
+u1* expandBufAddSpace(ExpandBuf* pBuf, int gapSize)
+{
+ u1* gapStart;
+
+ ensureSpace(pBuf, gapSize);
+ gapStart = pBuf->storage + pBuf->curLen;
+ /* do we want to garbage-fill the gap for debugging? */
+ pBuf->curLen += gapSize;
+
+ return gapStart;
+}
+
+/*
+ * Append a byte.
+ */
+void expandBufAdd1(ExpandBuf* pBuf, u1 val)
+{
+ ensureSpace(pBuf, sizeof(val));
+ *(pBuf->storage + pBuf->curLen) = val;
+ pBuf->curLen++;
+}
+
+/*
+ * Append two big-endian bytes.
+ */
+void expandBufAdd2BE(ExpandBuf* pBuf, u2 val)
+{
+ ensureSpace(pBuf, sizeof(val));
+ set2BE(pBuf->storage + pBuf->curLen, val);
+ pBuf->curLen += sizeof(val);
+}
+
+/*
+ * Append four big-endian bytes.
+ */
+void expandBufAdd4BE(ExpandBuf* pBuf, u4 val)
+{
+ ensureSpace(pBuf, sizeof(val));
+ set4BE(pBuf->storage + pBuf->curLen, val);
+ pBuf->curLen += sizeof(val);
+}
+
+/*
+ * Append eight big-endian bytes.
+ */
+void expandBufAdd8BE(ExpandBuf* pBuf, u8 val)
+{
+ ensureSpace(pBuf, sizeof(val));
+ set8BE(pBuf->storage + pBuf->curLen, val);
+ pBuf->curLen += sizeof(val);
+}
+
+/*
+ * Add a UTF8 string as a 4-byte length followed by a non-NULL-terminated
+ * string.
+ *
+ * Because these strings are coming out of the VM, it's safe to assume that
+ * they can be null-terminated (either they don't have null bytes or they
+ * have stored null bytes in a multi-byte encoding).
+ */
+void expandBufAddUtf8String(ExpandBuf* pBuf, const u1* str)
+{
+ int strLen = strlen((const char*)str);
+
+ ensureSpace(pBuf, sizeof(u4) + strLen);
+ setUtf8String(pBuf->storage + pBuf->curLen, str);
+ pBuf->curLen += sizeof(u4) + strLen;
+}
diff --git a/vm/jdwp/ExpandBuf.h b/vm/jdwp/ExpandBuf.h
new file mode 100644
index 0000000..8bdc8a7
--- /dev/null
+++ b/vm/jdwp/ExpandBuf.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Expanding byte buffer, with primitives for appending basic data types.
+ */
+#ifndef _DALVIK_JDWP_EXPANDBUF
+#define _DALVIK_JDWP_EXPANDBUF
+
+#include "Common.h" // need u1/u2/u4/u8 types
+
+struct ExpandBuf; /* private */
+typedef struct ExpandBuf ExpandBuf;
+
+/* create a new struct */
+ExpandBuf* expandBufAlloc(void);
+/* free storage */
+void expandBufFree(ExpandBuf* pBuf);
+
+/*
+ * Accessors. The buffer pointer and length will only be valid until more
+ * data is added.
+ */
+u1* expandBufGetBuffer(ExpandBuf* pBuf);
+size_t expandBufGetLength(ExpandBuf* pBuf);
+
+/*
+ * The "add" operations allocate additional storage and append the data.
+ *
+ * There are no "get" operations included with this "class", other than
+ * GetBuffer(). If you want to get or set data from a position other
+ * than the end, get a pointer to the buffer and use the inline functions
+ * defined elsewhere.
+ *
+ * expandBufAddSpace() returns a pointer to the *start* of the region
+ * added.
+ */
+u1* expandBufAddSpace(ExpandBuf* pBuf, int gapSize);
+void expandBufAdd1(ExpandBuf* pBuf, u1 val);
+void expandBufAdd2BE(ExpandBuf* pBuf, u2 val);
+void expandBufAdd4BE(ExpandBuf* pBuf, u4 val);
+void expandBufAdd8BE(ExpandBuf* pBuf, u8 val);
+void expandBufAddUtf8String(ExpandBuf* pBuf, const u1* str);
+
+#endif /*_DALVIK_JDWP_EXPANDBUF*/
diff --git a/vm/jdwp/Jdwp.h b/vm/jdwp/Jdwp.h
new file mode 100644
index 0000000..7313579
--- /dev/null
+++ b/vm/jdwp/Jdwp.h
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * JDWP "public" interface. The main body of the VM should only use JDWP
+ * structures and functions declared here.
+ *
+ * The JDWP code follows the DalvikVM rules for naming conventions, but
+ * attempts to remain independent of VM innards (e.g. it doesn't access VM
+ * data structures directly). All calls go through Debugger.c.
+ */
+#ifndef _DALVIK_JDWP_JDWP
+#define _DALVIK_JDWP_JDWP
+
+#include "jdwp/JdwpConstants.h"
+#include "jdwp/ExpandBuf.h"
+#include "Common.h"
+#include "Bits.h"
+#include <pthread.h>
+
+struct JdwpState; /* opaque */
+typedef struct JdwpState JdwpState;
+
+/*
+ * Fundamental types.
+ *
+ * ObjectId and RefTypeId must be the same size.
+ */
+typedef u4 FieldId; /* static or instance field */
+typedef u4 MethodId; /* any kind of method, including constructors */
+typedef u8 ObjectId; /* any object (threadID, stringID, arrayID, etc) */
+typedef u8 RefTypeId; /* like ObjectID, but unique for Class objects */
+typedef u8 FrameId; /* short-lived stack frame ID */
+
+/*
+ * Match these with the type sizes. This way we don't have to pass
+ * a value and a length.
+ */
+INLINE FieldId dvmReadFieldId(const u1** pBuf) { return read4BE(pBuf); }
+INLINE MethodId dvmReadMethodId(const u1** pBuf) { return read4BE(pBuf); }
+INLINE ObjectId dvmReadObjectId(const u1** pBuf) { return read8BE(pBuf); }
+INLINE RefTypeId dvmReadRefTypeId(const u1** pBuf) { return read8BE(pBuf); }
+INLINE FrameId dvmReadFrameId(const u1** pBuf) { return read8BE(pBuf); }
+INLINE void dvmSetFieldId(u1* buf, FieldId val) { return set4BE(buf, val); }
+INLINE void dvmSetMethodId(u1* buf, MethodId val) { return set4BE(buf, val); }
+INLINE void dvmSetObjectId(u1* buf, ObjectId val) { return set8BE(buf, val); }
+INLINE void dvmSetRefTypeId(u1* buf, RefTypeId val) { return set8BE(buf, val); }
+INLINE void dvmSetFrameId(u1* buf, FrameId val) { return set8BE(buf, val); }
+INLINE void expandBufAddFieldId(ExpandBuf* pReply, FieldId id) {
+ expandBufAdd4BE(pReply, id);
+}
+INLINE void expandBufAddMethodId(ExpandBuf* pReply, MethodId id) {
+ expandBufAdd4BE(pReply, id);
+}
+INLINE void expandBufAddObjectId(ExpandBuf* pReply, ObjectId id) {
+ expandBufAdd8BE(pReply, id);
+}
+INLINE void expandBufAddRefTypeId(ExpandBuf* pReply, RefTypeId id) {
+ expandBufAdd8BE(pReply, id);
+}
+INLINE void expandBufAddFrameId(ExpandBuf* pReply, FrameId id) {
+ expandBufAdd8BE(pReply, id);
+}
+
+
+/*
+ * Holds a JDWP "location".
+ */
+typedef struct JdwpLocation {
+ u1 typeTag; /* class or interface? */
+ RefTypeId classId; /* method->clazz */
+ MethodId methodId; /* method in which "idx" resides */
+ u8 idx; /* relative index into code block */
+} JdwpLocation;
+//#define kJDWPLocationSize (25)
+
+/*
+ * How we talk to the debugger.
+ */
+typedef enum JdwpTransportType {
+ kJdwpTransportUnknown = 0,
+ kJdwpTransportSocket, /* transport=dt_socket */
+ kJdwpTransportAndroidAdb, /* transport=dt_android_adb */
+} JdwpTransportType;
+
+/*
+ * Holds collection of JDWP initialization parameters.
+ */
+typedef struct JdwpStartupParams {
+ JdwpTransportType transport;
+ bool server;
+ bool suspend;
+ char host[64];
+ short port;
+ /* more will be here someday */
+} JdwpStartupParams;
+
+/*
+ * Perform one-time initialization.
+ *
+ * Among other things, this binds to a port to listen for a connection from
+ * the debugger.
+ *
+ * Returns a newly-allocated JdwpState struct on success, or NULL on failure.
+ */
+JdwpState* dvmJdwpStartup(const JdwpStartupParams* params);
+
+/*
+ * Shut everything down.
+ */
+void dvmJdwpShutdown(JdwpState* state);
+
+/*
+ * Returns "true" if a debugger or DDM is connected.
+ */
+bool dvmJdwpIsActive(JdwpState* state);
+
+/*
+ * Return the debugger thread's handle, or 0 if the debugger thread isn't
+ * running.
+ */
+pthread_t dvmJdwpGetDebugThread(JdwpState* state);
+
+/*
+ * Get time, in milliseconds, since the last debugger activity.
+ */
+s8 dvmJdwpLastDebuggerActivity(JdwpState* state);
+
+/*
+ * When we hit a debugger event that requires suspension, it's important
+ * that we wait for the thread to suspend itself before processing any
+ * additional requests. (Otherwise, if the debugger immediately sends a
+ * "resume thread" command, the resume might arrive before the thread has
+ * suspended itself.)
+ *
+ * The thread should call the "set" function before sending the event to
+ * the debugger. The main JDWP handler loop calls "get" before processing
+ * an event, and will wait for thread suspension if it's set. Once the
+ * thread has suspended itself, the JDWP handler calls "clear" and
+ * continues processing the current event. This works in the suspend-all
+ * case because the event thread doesn't suspend itself until everything
+ * else has suspended.
+ *
+ * It's possible that multiple threads could encounter thread-suspending
+ * events at the same time, so we grab a mutex in the "set" call, and
+ * release it in the "clear" call.
+ */
+//ObjectId dvmJdwpGetWaitForEventThread(JdwpState* state);
+void dvmJdwpSetWaitForEventThread(JdwpState* state, ObjectId threadId);
+void dvmJdwpClearWaitForEventThread(JdwpState* state);
+
+/*
+ * Network functions.
+ */
+bool dvmJdwpCheckConnection(JdwpState* state);
+bool dvmJdwpAcceptConnection(JdwpState* state);
+bool dvmJdwpEstablishConnection(JdwpState* state);
+void dvmJdwpCloseConnection(JdwpState* state);
+bool dvmJdwpProcessIncoming(JdwpState* state);
+
+
+/*
+ * These notify the debug code that something interesting has happened. This
+ * could be a thread starting or ending, an exception, or an opportunity
+ * for a breakpoint. These calls do not mean that an event the debugger
+ * is interested has happened, just that something has happened that the
+ * debugger *might* be interested in.
+ *
+ * The item of interest may trigger multiple events, some or all of which
+ * are grouped together in a single response.
+ *
+ * The event may cause the current thread or all threads (except the
+ * JDWP support thread) to be suspended.
+ */
+
+/*
+ * The VM has finished initializing. Only called when the debugger is
+ * connected at the time initialization completes.
+ */
+bool dvmJdwpPostVMStart(JdwpState* state, bool suspend);
+
+/*
+ * A location of interest has been reached. This is used for breakpoints,
+ * single-stepping, and method entry/exit. (JDWP requires that these four
+ * events are grouped together in a single response.)
+ *
+ * In some cases "*pLoc" will just have a method and class name, e.g. when
+ * issuing a MethodEntry on a native method.
+ *
+ * "eventFlags" indicates the types of events that have occurred.
+ */
+bool dvmJdwpPostLocationEvent(JdwpState* state, const JdwpLocation* pLoc,
+ ObjectId thisPtr, int eventFlags);
+
+/*
+ * An exception has been thrown.
+ *
+ * Pass in a zeroed-out "*pCatchLoc" if the exception wasn't caught.
+ */
+bool dvmJdwpPostException(JdwpState* state, const JdwpLocation* pThrowLoc,
+ ObjectId excepId, RefTypeId excepClassId, const JdwpLocation* pCatchLoc,
+ ObjectId thisPtr);
+
+/*
+ * A thread has started or stopped.
+ */
+bool dvmJdwpPostThreadChange(JdwpState* state, ObjectId threadId, bool start);
+
+/*
+ * Class has been prepared.
+ */
+bool dvmJdwpPostClassPrepare(JdwpState* state, int tag, RefTypeId refTypeId,
+ const char* signature, int status);
+
+/*
+ * The VM is about to stop.
+ */
+bool dvmJdwpPostVMDeath(JdwpState* state);
+
+/*
+ * Send up a chunk of DDM data.
+ */
+void dvmJdwpDdmSendChunkV(JdwpState* state, int type, const struct iovec* iov,
+ int iovcnt);
+
+#endif /*_DALVIK_JDWP_JDWP*/
diff --git a/vm/jdwp/JdwpAdb.c b/vm/jdwp/JdwpAdb.c
new file mode 100644
index 0000000..c3a1a72
--- /dev/null
+++ b/vm/jdwp/JdwpAdb.c
@@ -0,0 +1,761 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "jdwp/JdwpPriv.h"
+#include "jdwp/JdwpHandler.h"
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <errno.h>
+#include <unistd.h>
+
+/* the JDWP <-> ADB transport protocol is explained in details
+ * in //device/tools/adb/jdwp_service.c, here's a summary.
+ *
+ * 1/ when the JDWP thread starts, it tries to connect to a Unix
+ * domain stream socket (@jdwp-control) that is opened by the
+ * ADB daemon.
+ *
+ * 2/ it then sends the current process PID as a string of 4 hexadecimal
+ * chars (no terminating zero)
+ *
+ * 3/ then, it uses recvmsg to receive file descriptors from the
+ * daemon. each incoming file descriptor is a pass-through to
+ * a given JDWP debugger, that can be used to read the usual
+ * JDWP-handshake, etc...
+ *
+ */
+
+#define kInputBufferSize 8192
+
+#define kMagicHandshake "JDWP-Handshake"
+#define kMagicHandshakeLen (sizeof(kMagicHandshake)-1)
+
+#define kJdwpControlName "\0jdwp-control"
+#define kJdwpControlNameLen (sizeof(kJdwpControlName)-1)
+
+struct JdwpNetState {
+ int controlSock;
+ int clientSock;
+ bool awaitingHandshake;
+ bool shuttingDown;
+ int wakeFds[2];
+
+ int inputCount;
+ unsigned char inputBuffer[kInputBufferSize];
+
+ socklen_t controlAddrLen;
+ union {
+ struct sockaddr_un controlAddrUn;
+ struct sockaddr controlAddrPlain;
+ } controlAddr;
+};
+
+static void
+adbStateFree( JdwpNetState* netState )
+{
+ if (netState == NULL)
+ return;
+
+ if (netState->clientSock >= 0) {
+ shutdown(netState->clientSock, SHUT_RDWR);
+ close(netState->clientSock);
+ }
+ if (netState->controlSock >= 0) {
+ shutdown(netState->controlSock, SHUT_RDWR);
+ close(netState->controlSock);
+ }
+ if (netState->wakeFds[0] >= 0) {
+ close(netState->wakeFds[0]);
+ netState->wakeFds[0] = -1;
+ }
+ if (netState->wakeFds[1] >= 0) {
+ close(netState->wakeFds[1]);
+ netState->wakeFds[1] = -1;
+ }
+
+ free(netState);
+}
+
+
+static JdwpNetState*
+adbStateAlloc(void)
+{
+ JdwpNetState* netState = calloc(sizeof(*netState),1);
+
+ netState->controlSock = -1;
+ netState->clientSock = -1;
+
+ netState->controlAddr.controlAddrUn.sun_family = AF_UNIX;
+ netState->controlAddrLen =
+ sizeof(netState->controlAddr.controlAddrUn.sun_family) +
+ kJdwpControlNameLen;
+
+ memcpy(netState->controlAddr.controlAddrUn.sun_path,
+ kJdwpControlName, kJdwpControlNameLen);
+
+ netState->wakeFds[0] = -1;
+ netState->wakeFds[1] = -1;
+
+ return netState;
+}
+
+
+/*
+ * Do initial prep work, e.g. binding to ports and opening files. This
+ * runs in the main thread, before the JDWP thread starts, so it shouldn't
+ * do anything that might block forever.
+ */
+static bool startup(struct JdwpState* state, const JdwpStartupParams* pParams)
+{
+ JdwpNetState* netState;
+
+ LOGV("ADB transport startup\n");
+
+ state->netState = netState = adbStateAlloc();
+ if (netState == NULL)
+ return false;
+
+ return true;
+}
+
+/*
+ * Receive a file descriptor from ADB. The fd can be used to communicate
+ * directly with a debugger or DDMS.
+ *
+ * Returns the file descriptor on success. On failure, returns -1 and
+ * closes netState->controlSock.
+ */
+static int receiveClientFd(JdwpNetState* netState)
+{
+ struct msghdr msg;
+ struct cmsghdr* cmsg;
+ struct iovec iov;
+ char dummy = '!';
+ union {
+ struct cmsghdr cm;
+ char buffer[CMSG_SPACE(sizeof(int))];
+ } cm_un;
+ int ret;
+
+ iov.iov_base = &dummy;
+ iov.iov_len = 1;
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_flags = 0;
+ msg.msg_control = cm_un.buffer;
+ msg.msg_controllen = sizeof(cm_un.buffer);
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = msg.msg_controllen;
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ ((int*)CMSG_DATA(cmsg))[0] = -1;
+
+ do {
+ ret = recvmsg(netState->controlSock, &msg, 0);
+ } while (ret < 0 && errno == EINTR);
+
+ if (ret <= 0) {
+ if (ret < 0) {
+ LOGW("receiving file descriptor from ADB failed (socket %d): %s\n",
+ netState->controlSock, strerror(errno));
+ } else {
+ LOGD("adbd disconnected\n");
+ }
+ close(netState->controlSock);
+ netState->controlSock = -1;
+ return -1;
+ }
+
+ return ((int*)CMSG_DATA(cmsg))[0];
+}
+
+/*
+ * Block forever, waiting for a debugger to connect to us. Called from the
+ * JDWP thread.
+ *
+ * This needs to un-block and return "false" if the VM is shutting down. It
+ * should return "true" when it successfully accepts a connection.
+ */
+static bool acceptConnection(struct JdwpState* state)
+{
+ JdwpNetState* netState = state->netState;
+ int retryCount = 0;
+
+ /* first, ensure that we get a connection to the ADB daemon */
+
+retry:
+ if (netState->shuttingDown)
+ return false;
+
+ if (netState->controlSock < 0) {
+ int sleep_ms = 500;
+ const int sleep_max_ms = 2*1000;
+ char buff[5];
+
+ netState->controlSock = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (netState->controlSock < 0) {
+ LOGE("Could not create ADB control socket:%s\n",
+ strerror(errno));
+ return false;
+ }
+
+ if (pipe(netState->wakeFds) < 0) {
+ LOGE("pipe failed");
+ return false;
+ }
+
+ snprintf(buff, sizeof(buff), "%04x", getpid());
+ buff[4] = 0;
+
+ for (;;) {
+ /*
+ * If adbd isn't running, because USB debugging was disabled or
+ * perhaps the system is restarting it for "adb root", the
+ * connect() will fail. We loop here forever waiting for it
+ * to come back.
+ *
+ * Waking up and polling every couple of seconds is generally a
+ * bad thing to do, but we only do this if the application is
+ * debuggable *and* adbd isn't running. Still, for the sake
+ * of battery life, we should consider timing out and giving
+ * up after a few minutes in case somebody ships an app with
+ * the debuggable flag set.
+ */
+ int ret = connect(netState->controlSock,
+ &netState->controlAddr.controlAddrPlain,
+ netState->controlAddrLen);
+ if (!ret) {
+ /* now try to send our pid to the ADB daemon */
+ do {
+ ret = send( netState->controlSock, buff, 4, 0 );
+ } while (ret < 0 && errno == EINTR);
+
+ if (ret >= 0) {
+ LOGV("PID sent as '%.*s' to ADB\n", 4, buff);
+ break;
+ }
+
+ LOGE("Weird, can't send JDWP process pid to ADB: %s\n",
+ strerror(errno));
+ return false;
+ }
+ LOGV("Can't connect to ADB control socket:%s\n",
+ strerror(errno));
+
+ usleep( sleep_ms*1000 );
+
+ sleep_ms += (sleep_ms >> 1);
+ if (sleep_ms > sleep_max_ms)
+ sleep_ms = sleep_max_ms;
+ }
+ }
+
+ LOGV("trying to receive file descriptor from ADB\n");
+ /* now we can receive a client file descriptor */
+ netState->clientSock = receiveClientFd(netState);
+ if (netState->shuttingDown)
+ return false; // suppress logs and additional activity
+
+ if (netState->clientSock < 0) {
+ if (++retryCount > 5) {
+ LOGE("adb connection max retries exceeded\n");
+ return false;
+ }
+ goto retry;
+ } else {
+ LOGV("received file descriptor %d from ADB\n", netState->clientSock);
+ netState->awaitingHandshake = 1;
+ netState->inputCount = 0;
+ return true;
+ }
+}
+
+/*
+ * Connect out to a debugger (for server=n). Not required.
+ */
+static bool establishConnection(struct JdwpState* state)
+{
+ return false;
+}
+
+/*
+ * Close a connection from a debugger (which may have already dropped us).
+ * Only called from the JDWP thread.
+ */
+static void closeConnection(struct JdwpState* state)
+{
+ JdwpNetState* netState;
+
+ assert(state != NULL && state->netState != NULL);
+
+ netState = state->netState;
+ if (netState->clientSock < 0)
+ return;
+
+ LOGV("+++ closed JDWP <-> ADB connection\n");
+
+ close(netState->clientSock);
+ netState->clientSock = -1;
+}
+
+/*
+ * Close all network stuff, including the socket we use to listen for
+ * new connections.
+ *
+ * May be called from a non-JDWP thread, e.g. when the VM is shutting down.
+ */
+static void adbStateShutdown(struct JdwpNetState* netState)
+{
+ int controlSock;
+ int clientSock;
+
+ if (netState == NULL)
+ return;
+
+ netState->shuttingDown = true;
+
+ clientSock = netState->clientSock;
+ if (clientSock >= 0) {
+ shutdown(clientSock, SHUT_RDWR);
+ netState->clientSock = -1;
+ }
+
+ controlSock = netState->controlSock;
+ if (controlSock >= 0) {
+ shutdown(controlSock, SHUT_RDWR);
+ netState->controlSock = -1;
+ }
+
+ if (netState->wakeFds[1] >= 0) {
+ LOGV("+++ writing to wakePipe\n");
+ (void) write(netState->wakeFds[1], "", 1);
+ }
+}
+
+static void netShutdown(JdwpState* state)
+{
+ adbStateShutdown(state->netState);
+}
+
+/*
+ * Free up anything we put in state->netState. This is called after
+ * "netShutdown", after the JDWP thread has stopped.
+ */
+static void netFree(struct JdwpState* state)
+{
+ JdwpNetState* netState = state->netState;
+
+ adbStateFree(netState);
+}
+
+/*
+ * Is a debugger connected to us?
+ */
+static bool isConnected(struct JdwpState* state)
+{
+ return (state->netState != NULL &&
+ state->netState->clientSock >= 0);
+}
+
+/*
+ * Are we still waiting for the JDWP handshake?
+ */
+static bool awaitingHandshake(struct JdwpState* state)
+{
+ return state->netState->awaitingHandshake;
+}
+
+/*
+ * Figure out if we have a full packet in the buffer.
+ */
+static bool haveFullPacket(JdwpNetState* netState)
+{
+ long length;
+
+ if (netState->awaitingHandshake)
+ return (netState->inputCount >= (int) kMagicHandshakeLen);
+
+ if (netState->inputCount < 4)
+ return false;
+
+ length = get4BE(netState->inputBuffer);
+ return (netState->inputCount >= length);
+}
+
+/*
+ * Consume bytes from the buffer.
+ *
+ * This would be more efficient with a circular buffer. However, we're
+ * usually only going to find one packet, which is trivial to handle.
+ */
+static void consumeBytes(JdwpNetState* netState, int count)
+{
+ assert(count > 0);
+ assert(count <= netState->inputCount);
+
+ if (count == netState->inputCount) {
+ netState->inputCount = 0;
+ return;
+ }
+
+ memmove(netState->inputBuffer, netState->inputBuffer + count,
+ netState->inputCount - count);
+ netState->inputCount -= count;
+}
+
+/*
+ * Handle a packet. Returns "false" if we encounter a connection-fatal error.
+ */
+static bool handlePacket(JdwpState* state)
+{
+ JdwpNetState* netState = state->netState;
+ const unsigned char* buf = netState->inputBuffer;
+ JdwpReqHeader hdr;
+ u4 length, id;
+ u1 flags, cmdSet, cmd;
+ u2 error;
+ bool reply;
+ int dataLen;
+
+ cmd = cmdSet = 0; // shut up gcc
+
+ length = read4BE(&buf);
+ id = read4BE(&buf);
+ flags = read1(&buf);
+ if ((flags & kJDWPFlagReply) != 0) {
+ reply = true;
+ error = read2BE(&buf);
+ } else {
+ reply = false;
+ cmdSet = read1(&buf);
+ cmd = read1(&buf);
+ }
+
+ assert((int) length <= netState->inputCount);
+ dataLen = length - (buf - netState->inputBuffer);
+
+ if (!reply) {
+ ExpandBuf* pReply = expandBufAlloc();
+
+ hdr.length = length;
+ hdr.id = id;
+ hdr.cmdSet = cmdSet;
+ hdr.cmd = cmd;
+ dvmJdwpProcessRequest(state, &hdr, buf, dataLen, pReply);
+ if (expandBufGetLength(pReply) > 0) {
+ int cc;
+
+ /*
+ * TODO: we currently assume the write() will complete in one
+ * go, which may not be safe for a network socket. We may need
+ * to mutex this against sendRequest().
+ */
+ cc = write(netState->clientSock, expandBufGetBuffer(pReply),
+ expandBufGetLength(pReply));
+ if (cc != (int) expandBufGetLength(pReply)) {
+ LOGE("Failed sending reply to debugger: %s\n", strerror(errno));
+ expandBufFree(pReply);
+ return false;
+ }
+ } else {
+ LOGW("No reply created for set=%d cmd=%d\n", cmdSet, cmd);
+ }
+ expandBufFree(pReply);
+ } else {
+ LOGV("reply?!\n");
+ assert(false);
+ }
+
+ LOGV("----------\n");
+
+ consumeBytes(netState, length);
+ return true;
+}
+
+/*
+ * Process incoming data. If no data is available, this will block until
+ * some arrives.
+ *
+ * If we get a full packet, handle it.
+ *
+ * To take some of the mystery out of life, we want to reject incoming
+ * connections if we already have a debugger attached. If we don't, the
+ * debugger will just mysteriously hang until it times out. We could just
+ * close the listen socket, but there's a good chance we won't be able to
+ * bind to the same port again, which would confuse utilities.
+ *
+ * Returns "false" on error (indicating that the connection has been severed),
+ * "true" if things are still okay.
+ */
+static bool processIncoming(JdwpState* state)
+{
+ JdwpNetState* netState = state->netState;
+ int readCount;
+
+ assert(netState->clientSock >= 0);
+
+ if (!haveFullPacket(netState)) {
+ /* read some more, looping until we have data */
+ errno = 0;
+ while (1) {
+ int selCount;
+ fd_set readfds;
+ int maxfd = -1;
+ int fd;
+
+ FD_ZERO(&readfds);
+
+ /* configure fds; note these may get zapped by another thread */
+ fd = netState->controlSock;
+ if (fd >= 0) {
+ FD_SET(fd, &readfds);
+ if (maxfd < fd)
+ maxfd = fd;
+ }
+ fd = netState->clientSock;
+ if (fd >= 0) {
+ FD_SET(fd, &readfds);
+ if (maxfd < fd)
+ maxfd = fd;
+ }
+ fd = netState->wakeFds[0];
+ if (fd >= 0) {
+ FD_SET(fd, &readfds);
+ if (maxfd < fd)
+ maxfd = fd;
+ } else {
+ LOGI("NOTE: entering select w/o wakepipe\n");
+ }
+
+ if (maxfd < 0) {
+ LOGV("+++ all fds are closed\n");
+ return false;
+ }
+
+ /*
+ * Select blocks until it sees activity on the file descriptors.
+ * Closing the local file descriptor does not count as activity,
+ * so we can't rely on that to wake us up (it works for read()
+ * and accept(), but not select()).
+ *
+ * We can do one of three things: (1) send a signal and catch
+ * EINTR, (2) open an additional fd ("wakePipe") and write to
+ * it when it's time to exit, or (3) time out periodically and
+ * re-issue the select. We're currently using #2, as it's more
+ * reliable than #1 and generally better than #3. Wastes two fds.
+ */
+ selCount = select(maxfd+1, &readfds, NULL, NULL, NULL);
+ if (selCount < 0) {
+ if (errno == EINTR)
+ continue;
+ LOGE("select failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ if (netState->wakeFds[0] >= 0 &&
+ FD_ISSET(netState->wakeFds[0], &readfds))
+ {
+ LOGD("Got wake-up signal, bailing out of select\n");
+ goto fail;
+ }
+ if (netState->controlSock >= 0 &&
+ FD_ISSET(netState->controlSock, &readfds))
+ {
+ int sock = receiveClientFd(netState);
+ if (sock >= 0) {
+ LOGI("Ignoring second debugger -- accepting and dropping\n");
+ close(sock);
+ } else {
+ assert(netState->controlSock < 0);
+ /*
+ * Remote side most likely went away, so our next read
+ * on netState->clientSock will fail and throw us out
+ * of the loop.
+ */
+ }
+ }
+ if (netState->clientSock >= 0 &&
+ FD_ISSET(netState->clientSock, &readfds))
+ {
+ readCount = read(netState->clientSock,
+ netState->inputBuffer + netState->inputCount,
+ sizeof(netState->inputBuffer) - netState->inputCount);
+ if (readCount < 0) {
+ /* read failed */
+ if (errno != EINTR)
+ goto fail;
+ LOGD("+++ EINTR hit\n");
+ return true;
+ } else if (readCount == 0) {
+ /* EOF hit -- far end went away */
+ LOGV("+++ peer disconnected\n");
+ goto fail;
+ } else
+ break;
+ }
+ }
+
+ netState->inputCount += readCount;
+ if (!haveFullPacket(netState))
+ return true; /* still not there yet */
+ }
+
+ /*
+ * Special-case the initial handshake. For some bizarre reason we're
+ * expected to emulate bad tty settings by echoing the request back
+ * exactly as it was sent. Note the handshake is always initiated by
+ * the debugger, no matter who connects to whom.
+ *
+ * Other than this one case, the protocol [claims to be] stateless.
+ */
+ if (netState->awaitingHandshake) {
+ int cc;
+
+ if (memcmp(netState->inputBuffer,
+ kMagicHandshake, kMagicHandshakeLen) != 0)
+ {
+ LOGE("ERROR: bad handshake '%.14s'\n", netState->inputBuffer);
+ goto fail;
+ }
+
+ errno = 0;
+ cc = write(netState->clientSock, netState->inputBuffer,
+ kMagicHandshakeLen);
+ if (cc != kMagicHandshakeLen) {
+ LOGE("Failed writing handshake bytes: %s (%d of %d)\n",
+ strerror(errno), cc, (int) kMagicHandshakeLen);
+ goto fail;
+ }
+
+ consumeBytes(netState, kMagicHandshakeLen);
+ netState->awaitingHandshake = false;
+ LOGV("+++ handshake complete\n");
+ return true;
+ }
+
+ /*
+ * Handle this packet.
+ */
+ return handlePacket(state);
+
+fail:
+ closeConnection(state);
+ return false;
+}
+
+/*
+ * Send a request.
+ *
+ * The entire packet must be sent with a single write() call to avoid
+ * threading issues.
+ *
+ * Returns "true" if it was sent successfully.
+ */
+static bool sendRequest(JdwpState* state, ExpandBuf* pReq)
+{
+ JdwpNetState* netState = state->netState;
+ int cc;
+
+ if (netState->clientSock < 0) {
+ /* can happen with some DDMS events */
+ LOGV("NOT sending request -- no debugger is attached\n");
+ return false;
+ }
+
+ /*
+ * TODO: we currently assume the write() will complete in one
+ * go, which may not be safe for a network socket. We may need
+ * to mutex this against handlePacket().
+ */
+ errno = 0;
+ cc = write(netState->clientSock, expandBufGetBuffer(pReq),
+ expandBufGetLength(pReq));
+ if (cc != (int) expandBufGetLength(pReq)) {
+ LOGE("Failed sending req to debugger: %s (%d of %d)\n",
+ strerror(errno), cc, (int) expandBufGetLength(pReq));
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Send a request that was split into multiple buffers.
+ *
+ * The entire packet must be sent with a single writev() call to avoid
+ * threading issues.
+ *
+ * Returns "true" if it was sent successfully.
+ */
+static bool sendBufferedRequest(JdwpState* state, const struct iovec* iov,
+ int iovcnt)
+{
+ JdwpNetState* netState = state->netState;
+
+ if (netState->clientSock < 0) {
+ /* can happen with some DDMS events */
+ LOGV("NOT sending request -- no debugger is attached\n");
+ return false;
+ }
+
+ size_t expected = 0;
+ int i;
+ for (i = 0; i < iovcnt; i++)
+ expected += iov[i].iov_len;
+
+ /*
+ * TODO: we currently assume the writev() will complete in one
+ * go, which may not be safe for a network socket. We may need
+ * to mutex this against handlePacket().
+ */
+ ssize_t actual;
+ actual = writev(netState->clientSock, iov, iovcnt);
+ if ((size_t)actual != expected) {
+ LOGE("Failed sending b-req to debugger: %s (%d of %zu)\n",
+ strerror(errno), (int) actual, expected);
+ return false;
+ }
+
+ return true;
+}
+
+
+/*
+ * Our functions.
+ */
+static const JdwpTransport socketTransport = {
+ startup,
+ acceptConnection,
+ establishConnection,
+ closeConnection,
+ netShutdown,
+ netFree,
+ isConnected,
+ awaitingHandshake,
+ processIncoming,
+ sendRequest,
+ sendBufferedRequest
+};
+
+/*
+ * Return our set.
+ */
+const JdwpTransport* dvmJdwpAndroidAdbTransport(void)
+{
+ return &socketTransport;
+}
diff --git a/vm/jdwp/JdwpConstants.c b/vm/jdwp/JdwpConstants.c
new file mode 100644
index 0000000..898fe2c
--- /dev/null
+++ b/vm/jdwp/JdwpConstants.c
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * String constants to go along with enumerated values. (Pity we don't
+ * have enumerated constant reflection in C.) These are only needed for
+ * making the output human-readable.
+ */
+#include "jdwp/JdwpConstants.h"
+
+/*
+ * Return a string for the error code.
+ */
+const char* dvmJdwpErrorStr(enum JdwpError error)
+{
+ switch (error) {
+ case ERR_NONE:
+ return "NONE";
+ case ERR_INVALID_THREAD:
+ return "INVALID_THREAD";
+ case ERR_INVALID_THREAD_GROUP:
+ return "INVALID_THREAD_GROUP";
+ case ERR_INVALID_PRIORITY:
+ return "INVALID_PRIORITY";
+ case ERR_THREAD_NOT_SUSPENDED:
+ return "THREAD_NOT_SUSPENDED";
+ case ERR_THREAD_SUSPENDED:
+ return "THREAD_SUSPENDED";
+ case ERR_INVALID_OBJECT:
+ return "INVALID_OBJEC";
+ case ERR_INVALID_CLASS:
+ return "INVALID_CLASS";
+ case ERR_CLASS_NOT_PREPARED:
+ return "CLASS_NOT_PREPARED";
+ case ERR_INVALID_METHODID:
+ return "INVALID_METHODID";
+ case ERR_INVALID_LOCATION:
+ return "INVALID_LOCATION";
+ case ERR_INVALID_FIELDID:
+ return "INVALID_FIELDID";
+ case ERR_INVALID_FRAMEID:
+ return "INVALID_FRAMEID";
+ case ERR_NO_MORE_FRAMES:
+ return "NO_MORE_FRAMES";
+ case ERR_OPAQUE_FRAME:
+ return "OPAQUE_FRAME";
+ case ERR_NOT_CURRENT_FRAME:
+ return "NOT_CURRENT_FRAME";
+ case ERR_TYPE_MISMATCH:
+ return "TYPE_MISMATCH";
+ case ERR_INVALID_SLOT:
+ return "INVALID_SLOT";
+ case ERR_DUPLICATE:
+ return "DUPLICATE";
+ case ERR_NOT_FOUND:
+ return "NOT_FOUND";
+ case ERR_INVALID_MONITOR:
+ return "INVALID_MONITOR";
+ case ERR_NOT_MONITOR_OWNER:
+ return "NOT_MONITOR_OWNER";
+ case ERR_INTERRUPT:
+ return "INTERRUPT";
+ case ERR_INVALID_CLASS_FORMAT:
+ return "INVALID_CLASS_FORMAT";
+ case ERR_CIRCULAR_CLASS_DEFINITION:
+ return "CIRCULAR_CLASS_DEFINITION";
+ case ERR_FAILS_VERIFICATION:
+ return "FAILS_VERIFICATION";
+ case ERR_ADD_METHOD_NOT_IMPLEMENTED:
+ return "ADD_METHOD_NOT_IMPLEMENTED";
+ case ERR_SCHEMA_CHANGE_NOT_IMPLEMENTED:
+ return "SCHEMA_CHANGE_NOT_IMPLEMENTED";
+ case ERR_INVALID_TYPESTATE:
+ return "INVALID_TYPESTATE";
+ case ERR_HIERARCHY_CHANGE_NOT_IMPLEMENTED:
+ return "HIERARCHY_CHANGE_NOT_IMPLEMENTED";
+ case ERR_DELETE_METHOD_NOT_IMPLEMENTED:
+ return "DELETE_METHOD_NOT_IMPLEMENTED";
+ case ERR_UNSUPPORTED_VERSION:
+ return "UNSUPPORTED_VERSION";
+ case ERR_NAMES_DONT_MATCH:
+ return "NAMES_DONT_MATCH";
+ case ERR_CLASS_MODIFIERS_CHANGE_NOT_IMPLEMENTED:
+ return "CLASS_MODIFIERS_CHANGE_NOT_IMPLEMENTED";
+ case ERR_METHOD_MODIFIERS_CHANGE_NOT_IMPLEMENTED:
+ return "METHOD_MODIFIERS_CHANGE_NOT_IMPLEMENTED";
+ case ERR_NOT_IMPLEMENTED:
+ return "NOT_IMPLEMENTED";
+ case ERR_NULL_POINTER:
+ return "NULL_POINTER";
+ case ERR_ABSENT_INFORMATION:
+ return "ABSENT_INFORMATION";
+ case ERR_INVALID_EVENT_TYPE:
+ return "INVALID_EVENT_TYPE";
+ case ERR_ILLEGAL_ARGUMENT:
+ return "ILLEGAL_ARGUMENT";
+ case ERR_OUT_OF_MEMORY:
+ return "OUT_OF_MEMORY";
+ case ERR_ACCESS_DENIED:
+ return "ACCESS_DENIED";
+ case ERR_VM_DEAD:
+ return "VM_DEAD";
+ case ERR_INTERNAL:
+ return "INTERNAL";
+ case ERR_UNATTACHED_THREAD:
+ return "UNATTACHED_THREAD";
+ case ERR_INVALID_TAG:
+ return "INVALID_TAG";
+ case ERR_ALREADY_INVOKING:
+ return "ALREADY_INVOKING";
+ case ERR_INVALID_INDEX:
+ return "INVALID_INDEX";
+ case ERR_INVALID_LENGTH:
+ return "INVALID_LENGTH";
+ case ERR_INVALID_STRING:
+ return "INVALID_STRING";
+ case ERR_INVALID_CLASS_LOADER:
+ return "INVALID_CLASS_LOADER";
+ case ERR_INVALID_ARRAY:
+ return "INVALID_ARRAY";
+ case ERR_TRANSPORT_LOAD:
+ return "TRANSPORT_LOAD";
+ case ERR_TRANSPORT_INIT:
+ return "TRANSPORT_INIT";
+ case ERR_NATIVE_METHOD:
+ return "NATIVE_METHOD";
+ case ERR_INVALID_COUNT:
+ return "INVALID_COUNT";
+ default:
+ return "?UNKNOWN?";
+ }
+}
+
+/*
+ * Return a string for the EventKind.
+ */
+const char* dvmJdwpEventKindStr(enum JdwpEventKind kind)
+{
+ switch (kind) {
+ case EK_SINGLE_STEP: return "SINGLE_STEP";
+ case EK_BREAKPOINT: return "BREAKPOINT";
+ case EK_FRAME_POP: return "FRAME_POP";
+ case EK_EXCEPTION: return "EXCEPTION";
+ case EK_USER_DEFINED: return "USER_DEFINED";
+ case EK_THREAD_START: return "THREAD_START";
+ /*case EK_THREAD_END: return "THREAD_END";*/
+ case EK_CLASS_PREPARE: return "CLASS_PREPARE";
+ case EK_CLASS_UNLOAD: return "CLASS_UNLOAD";
+ case EK_CLASS_LOAD: return "CLASS_LOAD";
+ case EK_FIELD_ACCESS: return "FIELD_ACCESS";
+ case EK_FIELD_MODIFICATION: return "FIELD_MODIFICATION";
+ case EK_EXCEPTION_CATCH: return "EXCEPTION_CATCH";
+ case EK_METHOD_ENTRY: return "METHOD_ENTRY";
+ case EK_METHOD_EXIT: return "METHOD_EXIT";
+ case EK_VM_INIT: return "VM_INIT";
+ case EK_VM_DEATH: return "VM_DEATH";
+ case EK_VM_DISCONNECTED: return "VM_DISCONNECTED";
+ /*case EK_VM_START: return "VM_START";*/
+ case EK_THREAD_DEATH: return "THREAD_DEATH";
+ default: return "?UNKNOWN?";
+ }
+}
+
+/*
+ * Return a string for the StepDepth.
+ */
+const char* dvmJdwpStepDepthStr(enum JdwpStepDepth depth)
+{
+ switch (depth) {
+ case SD_INTO: return "INTO";
+ case SD_OVER: return "OVER";
+ case SD_OUT: return "OUT";
+ default: return "?UNKNOWN?";
+ }
+}
+
+/*
+ * Return a string for the StepSize.
+ */
+const char* dvmJdwpStepSizeStr(enum JdwpStepSize size)
+{
+ switch (size) {
+ case SS_MIN: return "MIN";
+ case SS_LINE: return "LINE";
+ default: return "?UNKNOWN?";
+ }
+}
+
+/*
+ * Return a string for the SuspendPolicy.
+ */
+const char* dvmJdwpSuspendPolicyStr(enum JdwpSuspendPolicy policy)
+{
+ switch (policy) {
+ case SP_NONE: return "NONE";
+ case SP_EVENT_THREAD: return "EVENT_THREAD";
+ case SP_ALL: return "ALL";
+ default: return "?UNKNOWN?";
+ }
+}
+
+/*
+ * Return a string for the SuspendStatus.
+ */
+const char* dvmJdwpSuspendStatusStr(enum JdwpSuspendStatus status)
+{
+ switch (status) {
+ case 0: return "Not SUSPENDED";
+ case SUSPEND_STATUS_SUSPENDED: return "SUSPENDED";
+ default: return "?UNKNOWN?";
+ }
+}
+
+/*
+ * Return a string for the ThreadStatus.
+ */
+const char* dvmJdwpThreadStatusStr(enum JdwpThreadStatus status)
+{
+ switch (status) {
+ case TS_ZOMBIE: return "ZOMBIE";
+ case TS_RUNNING: return "RUNNING";
+ case TS_SLEEPING: return "SLEEPING";
+ case TS_MONITOR: return "MONITOR";
+ case TS_WAIT: return "WAIT";
+ default: return "?UNKNOWN?";
+ }
+};
diff --git a/vm/jdwp/JdwpConstants.h b/vm/jdwp/JdwpConstants.h
new file mode 100644
index 0000000..922dbcd
--- /dev/null
+++ b/vm/jdwp/JdwpConstants.h
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * These come out of the JDWP documentation.
+ */
+#ifndef _DALVIK_JDWP_JDWPCONSTANTS
+#define _DALVIK_JDWP_JDWPCONSTANTS
+
+/*
+ * Error constants.
+ */
+enum JdwpError {
+ ERR_NONE = 0,
+ ERR_INVALID_THREAD = 10,
+ ERR_INVALID_THREAD_GROUP = 11,
+ ERR_INVALID_PRIORITY = 12,
+ ERR_THREAD_NOT_SUSPENDED = 13,
+ ERR_THREAD_SUSPENDED = 14,
+ ERR_INVALID_OBJECT = 20,
+ ERR_INVALID_CLASS = 21,
+ ERR_CLASS_NOT_PREPARED = 22,
+ ERR_INVALID_METHODID = 23,
+ ERR_INVALID_LOCATION = 24,
+ ERR_INVALID_FIELDID = 25,
+ ERR_INVALID_FRAMEID = 30,
+ ERR_NO_MORE_FRAMES = 31,
+ ERR_OPAQUE_FRAME = 32,
+ ERR_NOT_CURRENT_FRAME = 33,
+ ERR_TYPE_MISMATCH = 34,
+ ERR_INVALID_SLOT = 35,
+ ERR_DUPLICATE = 40,
+ ERR_NOT_FOUND = 41,
+ ERR_INVALID_MONITOR = 50,
+ ERR_NOT_MONITOR_OWNER = 51,
+ ERR_INTERRUPT = 52,
+ ERR_INVALID_CLASS_FORMAT = 60,
+ ERR_CIRCULAR_CLASS_DEFINITION = 61,
+ ERR_FAILS_VERIFICATION = 62,
+ ERR_ADD_METHOD_NOT_IMPLEMENTED = 63,
+ ERR_SCHEMA_CHANGE_NOT_IMPLEMENTED = 64,
+ ERR_INVALID_TYPESTATE = 65,
+ ERR_HIERARCHY_CHANGE_NOT_IMPLEMENTED = 66,
+ ERR_DELETE_METHOD_NOT_IMPLEMENTED = 67,
+ ERR_UNSUPPORTED_VERSION = 68,
+ ERR_NAMES_DONT_MATCH = 69,
+ ERR_CLASS_MODIFIERS_CHANGE_NOT_IMPLEMENTED = 70,
+ ERR_METHOD_MODIFIERS_CHANGE_NOT_IMPLEMENTED = 71,
+ ERR_NOT_IMPLEMENTED = 99,
+ ERR_NULL_POINTER = 100,
+ ERR_ABSENT_INFORMATION = 101,
+ ERR_INVALID_EVENT_TYPE = 102,
+ ERR_ILLEGAL_ARGUMENT = 103,
+ ERR_OUT_OF_MEMORY = 110,
+ ERR_ACCESS_DENIED = 111,
+ ERR_VM_DEAD = 112,
+ ERR_INTERNAL = 113,
+ ERR_UNATTACHED_THREAD = 115,
+ ERR_INVALID_TAG = 500,
+ ERR_ALREADY_INVOKING = 502,
+ ERR_INVALID_INDEX = 503,
+ ERR_INVALID_LENGTH = 504,
+ ERR_INVALID_STRING = 506,
+ ERR_INVALID_CLASS_LOADER = 507,
+ ERR_INVALID_ARRAY = 508,
+ ERR_TRANSPORT_LOAD = 509,
+ ERR_TRANSPORT_INIT = 510,
+ ERR_NATIVE_METHOD = 511,
+ ERR_INVALID_COUNT = 512,
+};
+typedef enum JdwpError JdwpError;
+const char* dvmJdwpErrorStr(enum JdwpError error);
+
+
+/*
+ * ClassStatus constants. These are bit flags that can be ORed together.
+ */
+enum JdwpClassStatus {
+ CS_VERIFIED = 0x01,
+ CS_PREPARED = 0x02,
+ CS_INITIALIZED = 0x04,
+ CS_ERROR = 0x08,
+};
+
+/*
+ * EventKind constants.
+ */
+enum JdwpEventKind {
+ EK_SINGLE_STEP = 1,
+ EK_BREAKPOINT = 2,
+ EK_FRAME_POP = 3,
+ EK_EXCEPTION = 4,
+ EK_USER_DEFINED = 5,
+ EK_THREAD_START = 6,
+ EK_THREAD_END = 7,
+ EK_CLASS_PREPARE = 8,
+ EK_CLASS_UNLOAD = 9,
+ EK_CLASS_LOAD = 10,
+ EK_FIELD_ACCESS = 20,
+ EK_FIELD_MODIFICATION = 21,
+ EK_EXCEPTION_CATCH = 30,
+ EK_METHOD_ENTRY = 40,
+ EK_METHOD_EXIT = 41,
+ EK_VM_INIT = 90,
+ EK_VM_DEATH = 99,
+ EK_VM_DISCONNECTED = 100, /* "Never sent across JDWP */
+ EK_VM_START = EK_VM_INIT,
+ EK_THREAD_DEATH = EK_THREAD_END,
+};
+const char* dvmJdwpEventKindStr(enum JdwpEventKind kind);
+
+/*
+ * Values for "modKind" in EventRequest.Set.
+ */
+enum JdwpModKind {
+ MK_COUNT = 1,
+ MK_CONDITIONAL = 2,
+ MK_THREAD_ONLY = 3,
+ MK_CLASS_ONLY = 4,
+ MK_CLASS_MATCH = 5,
+ MK_CLASS_EXCLUDE = 6,
+ MK_LOCATION_ONLY = 7,
+ MK_EXCEPTION_ONLY = 8,
+ MK_FIELD_ONLY = 9,
+ MK_STEP = 10,
+ MK_INSTANCE_ONLY = 11,
+};
+
+/*
+ * InvokeOptions constants (bit flags).
+ */
+enum JdwpInvokeOptions {
+ INVOKE_SINGLE_THREADED = 0x01,
+ INVOKE_NONVIRTUAL = 0x02,
+};
+
+/*
+ * StepDepth constants.
+ */
+enum JdwpStepDepth {
+ SD_INTO = 0, /* step into method calls */
+ SD_OVER = 1, /* step over method calls */
+ SD_OUT = 2, /* step out of current method */
+};
+const char* dvmJdwpStepDepthStr(enum JdwpStepDepth depth);
+
+/*
+ * StepSize constants.
+ */
+enum JdwpStepSize {
+ SS_MIN = 0, /* step by minimum (e.g. 1 bytecode inst) */
+ SS_LINE = 1, /* if possible, step to next line */
+};
+const char* dvmJdwpStepSizeStr(enum JdwpStepSize size);
+
+/*
+ * SuspendPolicy constants.
+ */
+enum JdwpSuspendPolicy {
+ SP_NONE = 0, /* suspend no threads */
+ SP_EVENT_THREAD = 1, /* suspend event thread */
+ SP_ALL = 2, /* suspend all threads */
+};
+const char* dvmJdwpSuspendPolicyStr(enum JdwpSuspendPolicy policy);
+
+/*
+ * SuspendStatus constants.
+ */
+enum JdwpSuspendStatus {
+ SUSPEND_STATUS_SUSPENDED = 1,
+};
+const char* dvmJdwpSuspendStatusStr(enum JdwpSuspendStatus status);
+
+/*
+ * ThreadStatus constants.
+ */
+enum JdwpThreadStatus {
+ TS_ZOMBIE = 0,
+ TS_RUNNING = 1, // RUNNING
+ TS_SLEEPING = 2, // (in Thread.sleep())
+ TS_MONITOR = 3, // WAITING (monitor wait)
+ TS_WAIT = 4, // (in Object.wait())
+};
+const char* dvmJdwpThreadStatusStr(enum JdwpThreadStatus status);
+
+/*
+ * TypeTag constants.
+ */
+enum JdwpTypeTag {
+ TT_CLASS = 1,
+ TT_INTERFACE = 2,
+ TT_ARRAY = 3,
+};
+
+/*
+ * Tag constants.
+ */
+enum JdwpType {
+ JT_ARRAY = '[',
+ JT_BYTE = 'B',
+ JT_CHAR = 'C',
+ JT_OBJECT = 'L',
+ JT_FLOAT = 'F',
+ JT_DOUBLE = 'D',
+ JT_INT = 'I',
+ JT_LONG = 'J',
+ JT_SHORT = 'S',
+ JT_VOID = 'V',
+ JT_BOOLEAN = 'Z',
+ JT_STRING = 's',
+ JT_THREAD = 't',
+ JT_THREAD_GROUP = 'g',
+ JT_CLASS_LOADER = 'l',
+ JT_CLASS_OBJECT = 'c',
+};
+
+#endif /*_DALVIK_JDWP_JDWPCONSTANTS*/
diff --git a/vm/jdwp/JdwpEvent.c b/vm/jdwp/JdwpEvent.c
new file mode 100644
index 0000000..69d2237
--- /dev/null
+++ b/vm/jdwp/JdwpEvent.c
@@ -0,0 +1,1292 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Send events to the debugger.
+ */
+#include "jdwp/JdwpPriv.h"
+#include "jdwp/JdwpConstants.h"
+#include "jdwp/JdwpHandler.h"
+#include "jdwp/JdwpEvent.h"
+#include "jdwp/ExpandBuf.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stddef.h> /* for offsetof() */
+#include <unistd.h>
+
+/*
+General notes:
+
+The event add/remove stuff usually happens from the debugger thread,
+in response to requests from the debugger, but can also happen as the
+result of an event in an arbitrary thread (e.g. an event with a "count"
+mod expires). It's important to keep the event list locked when processing
+events.
+
+Event posting can happen from any thread. The JDWP thread will not usually
+post anything but VM start/death, but if a JDWP request causes a class
+to be loaded, the ClassPrepare event will come from the JDWP thread.
+
+
+We can have serialization issues when we post an event to the debugger.
+For example, a thread could send an "I hit a breakpoint and am suspending
+myself" message to the debugger. Before it manages to suspend itself, the
+debugger's response ("not interested, resume thread") arrives and is
+processed. We try to resume a thread that hasn't yet suspended.
+
+This means that, after posting an event to the debugger, we need to wait
+for the event thread to suspend itself (and, potentially, all other threads)
+before processing any additional requests from the debugger. While doing
+so we need to be aware that multiple threads may be hitting breakpoints
+or other events simultaneously, so we either need to wait for all of them
+or serialize the events with each other.
+
+The current mechanism works like this:
+ Event thread:
+ - If I'm going to suspend, grab the "I am posting an event" token. Wait
+ for it if it's not currently available.
+ - Post the event to the debugger.
+ - If appropriate, suspend others and then myself. As part of suspending
+ myself, release the "I am posting" token.
+ JDWP thread:
+ - When an event arrives, see if somebody is posting an event. If so,
+ sleep until we can acquire the "I am posting an event" token. Release
+ it immediately and continue processing -- the event we have already
+ received should not interfere with other events that haven't yet
+ been posted.
+
+Some care must be taken to avoid deadlock:
+
+ - thread A and thread B exit near-simultaneously, and post thread-death
+ events with a "suspend all" clause
+ - thread A gets the event token, thread B sits and waits for it
+ - thread A wants to suspend all other threads, but thread B is waiting
+ for the token and can't be suspended
+
+So we need to mark thread B in such a way that thread A doesn't wait for it.
+
+If we just bracket the "grab event token" call with a change to VMWAIT
+before sleeping, the switch back to RUNNING state when we get the token
+will cause thread B to suspend (remember, thread A's global suspend is
+still in force, even after it releases the token). Suspending while
+holding the event token is very bad, because it prevents the JDWP thread
+from processing incoming messages.
+
+We need to change to VMWAIT state at the *start* of posting an event,
+and stay there until we either finish posting the event or decide to
+put ourselves to sleep. That way we don't interfere with anyone else and
+don't allow anyone else to interfere with us.
+*/
+
+
+#define kJdwpEventCommandSet 64
+#define kJdwpCompositeCommand 100
+
+/*
+ * Stuff to compare against when deciding if a mod matches. Only the
+ * values for mods valid for the event being evaluated will be filled in.
+ * The rest will be zeroed.
+ */
+typedef struct ModBasket {
+ const JdwpLocation* pLoc; /* LocationOnly */
+ const char* className; /* ClassMatch/ClassExclude */
+ ObjectId threadId; /* ThreadOnly */
+ RefTypeId classId; /* ClassOnly */
+ RefTypeId excepClassId; /* ExceptionOnly */
+ bool caught; /* ExceptionOnly */
+ FieldId field; /* FieldOnly */
+ ObjectId thisPtr; /* InstanceOnly */
+ /* nothing for StepOnly -- handled differently */
+} ModBasket;
+
+/*
+ * Get the next "request" serial number. We use this when sending
+ * packets to the debugger.
+ */
+u4 dvmJdwpNextRequestSerial(JdwpState* state)
+{
+ u4 result;
+
+ dvmDbgLockMutex(&state->serialLock);
+ result = state->requestSerial++;
+ dvmDbgUnlockMutex(&state->serialLock);
+
+ return result;
+}
+
+/*
+ * Get the next "event" serial number. We use this in the response to
+ * message type EventRequest.Set.
+ */
+u4 dvmJdwpNextEventSerial(JdwpState* state)
+{
+ u4 result;
+
+ dvmDbgLockMutex(&state->serialLock);
+ result = state->eventSerial++;
+ dvmDbgUnlockMutex(&state->serialLock);
+
+ return result;
+}
+
+/*
+ * Lock the "event" mutex, which guards the list of registered events.
+ */
+static void lockEventMutex(JdwpState* state)
+{
+ //dvmDbgThreadWaiting();
+ dvmDbgLockMutex(&state->eventLock);
+ //dvmDbgThreadRunning();
+}
+
+/*
+ * Unlock the "event" mutex.
+ */
+static void unlockEventMutex(JdwpState* state)
+{
+ dvmDbgUnlockMutex(&state->eventLock);
+}
+
+/*
+ * Add an event to the list. Ordering is not important.
+ *
+ * If something prevents the event from being registered, e.g. it's a
+ * single-step request on a thread that doesn't exist, the event will
+ * not be added to the list, and an appropriate error will be returned.
+ */
+JdwpError dvmJdwpRegisterEvent(JdwpState* state, JdwpEvent* pEvent)
+{
+ JdwpError err = ERR_NONE;
+ int i;
+
+ lockEventMutex(state);
+
+ assert(state != NULL);
+ assert(pEvent != NULL);
+ assert(pEvent->prev == NULL);
+ assert(pEvent->next == NULL);
+
+ /*
+ * If one or more LocationOnly mods are used, register them with
+ * the interpreter.
+ */
+ for (i = 0; i < pEvent->modCount; i++) {
+ JdwpEventMod* pMod = &pEvent->mods[i];
+ if (pMod->modKind == MK_LOCATION_ONLY) {
+ /* should only be for Breakpoint, Step, and Exception */
+ dvmDbgWatchLocation(&pMod->locationOnly.loc);
+ }
+ if (pMod->modKind == MK_STEP) {
+ /* should only be for EK_SINGLE_STEP; should only be one */
+ dvmDbgConfigureStep(pMod->step.threadId, pMod->step.size,
+ pMod->step.depth);
+ }
+ }
+
+ /*
+ * Add to list.
+ */
+ if (state->eventList != NULL) {
+ pEvent->next = state->eventList;
+ state->eventList->prev = pEvent;
+ }
+ state->eventList = pEvent;
+ state->numEvents++;
+
+ unlockEventMutex(state);
+
+ return err;
+}
+
+/*
+ * Remove an event from the list. This will also remove the event from
+ * any optimization tables, e.g. breakpoints.
+ *
+ * Does not free the JdwpEvent.
+ *
+ * Grab the eventLock before calling here.
+ */
+static void unregisterEvent(JdwpState* state, JdwpEvent* pEvent)
+{
+ int i;
+
+ if (pEvent->prev == NULL) {
+ /* head of the list */
+ assert(state->eventList == pEvent);
+
+ state->eventList = pEvent->next;
+ } else {
+ pEvent->prev->next = pEvent->next;
+ }
+
+ if (pEvent->next != NULL) {
+ pEvent->next->prev = pEvent->prev;
+ pEvent->next = NULL;
+ }
+ pEvent->prev = NULL;
+
+ /*
+ * Unhook us from the interpreter, if necessary.
+ */
+ for (i = 0; i < pEvent->modCount; i++) {
+ JdwpEventMod* pMod = &pEvent->mods[i];
+ if (pMod->modKind == MK_LOCATION_ONLY) {
+ /* should only be for Breakpoint, Step, and Exception */
+ dvmDbgUnwatchLocation(&pMod->locationOnly.loc);
+ }
+ if (pMod->modKind == MK_STEP) {
+ /* should only be for EK_SINGLE_STEP; should only be one */
+ dvmDbgUnconfigureStep(pMod->step.threadId);
+ }
+ }
+
+ state->numEvents--;
+ assert(state->numEvents != 0 || state->eventList == NULL);
+}
+
+/*
+ * Remove the event with the given ID from the list.
+ *
+ * Failure to find the event isn't really an error, but it is a little
+ * weird. (It looks like Eclipse will try to be extra careful and will
+ * explicitly remove one-off single-step events.)
+ */
+void dvmJdwpUnregisterEventById(JdwpState* state, u4 requestId)
+{
+ JdwpEvent* pEvent;
+
+ lockEventMutex(state);
+
+ pEvent = state->eventList;
+ while (pEvent != NULL) {
+ if (pEvent->requestId == requestId) {
+ unregisterEvent(state, pEvent);
+ dvmJdwpEventFree(pEvent);
+ goto done; /* there can be only one with a given ID */
+ }
+
+ pEvent = pEvent->next;
+ }
+
+ //LOGD("Odd: no match when removing event reqId=0x%04x\n", requestId);
+
+done:
+ unlockEventMutex(state);
+}
+
+/*
+ * Remove all entries from the event list.
+ */
+void dvmJdwpUnregisterAll(JdwpState* state)
+{
+ JdwpEvent* pEvent;
+ JdwpEvent* pNextEvent;
+
+ lockEventMutex(state);
+
+ pEvent = state->eventList;
+ while (pEvent != NULL) {
+ pNextEvent = pEvent->next;
+
+ unregisterEvent(state, pEvent);
+ dvmJdwpEventFree(pEvent);
+ pEvent = pNextEvent;
+ }
+
+ state->eventList = NULL;
+
+ unlockEventMutex(state);
+}
+
+
+
+/*
+ * Allocate a JdwpEvent struct with enough space to hold the specified
+ * number of mod records.
+ */
+JdwpEvent* dvmJdwpEventAlloc(int numMods)
+{
+ JdwpEvent* newEvent;
+ int allocSize = offsetof(JdwpEvent, mods) +
+ numMods * sizeof(newEvent->mods[0]);
+
+ newEvent = (JdwpEvent*)malloc(allocSize);
+ memset(newEvent, 0, allocSize);
+ return newEvent;
+}
+
+/*
+ * Free a JdwpEvent.
+ *
+ * Do not call this until the event has been removed from the list.
+ */
+void dvmJdwpEventFree(JdwpEvent* pEvent)
+{
+ int i;
+
+ if (pEvent == NULL)
+ return;
+
+ /* make sure it was removed from the list */
+ assert(pEvent->prev == NULL);
+ assert(pEvent->next == NULL);
+ /* want to assert state->eventList != pEvent */
+
+ /*
+ * Free any hairy bits in the mods.
+ */
+ for (i = 0; i < pEvent->modCount; i++) {
+ if (pEvent->mods[i].modKind == MK_CLASS_MATCH) {
+ free(pEvent->mods[i].classMatch.classPattern);
+ pEvent->mods[i].classMatch.classPattern = NULL;
+ }
+ if (pEvent->mods[i].modKind == MK_CLASS_EXCLUDE) {
+ free(pEvent->mods[i].classExclude.classPattern);
+ pEvent->mods[i].classExclude.classPattern = NULL;
+ }
+ }
+
+ free(pEvent);
+}
+
+/*
+ * Allocate storage for matching events. To keep things simple we
+ * use an array with enough storage for the entire list.
+ *
+ * The state->eventLock should be held before calling.
+ */
+static JdwpEvent** allocMatchList(JdwpState* state)
+{
+ return (JdwpEvent**) malloc(sizeof(JdwpEvent*) * state->numEvents);
+}
+
+/*
+ * Run through the list and remove any entries with an expired "count" mod
+ * from the event list, then free the match list.
+ */
+static void cleanupMatchList(JdwpState* state, JdwpEvent** matchList,
+ int matchCount)
+{
+ JdwpEvent** ppEvent = matchList;
+
+ while (matchCount--) {
+ JdwpEvent* pEvent = *ppEvent;
+ int i;
+
+ for (i = 0; i < pEvent->modCount; i++) {
+ if (pEvent->mods[i].modKind == MK_COUNT &&
+ pEvent->mods[i].count.count == 0)
+ {
+ LOGV("##### Removing expired event\n");
+ unregisterEvent(state, pEvent);
+ dvmJdwpEventFree(pEvent);
+ break;
+ }
+ }
+
+ ppEvent++;
+ }
+
+ free(matchList);
+}
+
+/*
+ * Match a string against a "restricted regular expression", which is just
+ * a string that may start or end with '*' (e.g. "*.Foo" or "java.*").
+ *
+ * ("Restricted name globbing" might have been a better term.)
+ */
+static bool patternMatch(const char* pattern, const char* target)
+{
+ int patLen = strlen(pattern);
+
+ if (pattern[0] == '*') {
+ int targetLen = strlen(target);
+ patLen--;
+ // TODO: remove printf when we find a test case to verify this
+ LOGE(">>> comparing '%s' to '%s'\n",
+ pattern+1, target + (targetLen-patLen));
+
+ if (targetLen < patLen)
+ return false;
+ return strcmp(pattern+1, target + (targetLen-patLen)) == 0;
+ } else if (pattern[patLen-1] == '*') {
+ return strncmp(pattern, target, patLen-1) == 0;
+ } else {
+ return strcmp(pattern, target) == 0;
+ }
+}
+
+/*
+ * See if two locations are equal.
+ *
+ * It's tempting to do a bitwise compare ("struct ==" or memcmp), but if
+ * the storage wasn't zeroed out there could be undefined values in the
+ * padding. Besides, the odds of "idx" being equal while the others aren't
+ * is very small, so this is usually just a simple integer comparison.
+ */
+static inline bool locationMatch(const JdwpLocation* pLoc1,
+ const JdwpLocation* pLoc2)
+{
+ return pLoc1->idx == pLoc2->idx &&
+ pLoc1->methodId == pLoc2->methodId &&
+ pLoc1->classId == pLoc2->classId &&
+ pLoc1->typeTag == pLoc2->typeTag;
+}
+
+/*
+ * See if the event's mods match up with the contents of "basket".
+ *
+ * If we find a Count mod before rejecting an event, we decrement it. We
+ * need to do this even if later mods cause us to ignore the event.
+ */
+static bool modsMatch(JdwpState* state, JdwpEvent* pEvent, ModBasket* basket)
+{
+ JdwpEventMod* pMod = pEvent->mods;
+ int i;
+
+ for (i = pEvent->modCount; i > 0; i--, pMod++) {
+ switch (pMod->modKind) {
+ case MK_COUNT:
+ assert(pMod->count.count > 0);
+ pMod->count.count--;
+ break;
+ case MK_CONDITIONAL:
+ assert(false); // should not be getting these
+ break;
+ case MK_THREAD_ONLY:
+ if (pMod->threadOnly.threadId != basket->threadId)
+ return false;
+ break;
+ case MK_CLASS_ONLY:
+ if (!dvmDbgMatchType(basket->classId,
+ pMod->classOnly.referenceTypeId))
+ return false;
+ break;
+ case MK_CLASS_MATCH:
+ if (!patternMatch(pMod->classMatch.classPattern,
+ basket->className))
+ return false;
+ break;
+ case MK_CLASS_EXCLUDE:
+ if (patternMatch(pMod->classMatch.classPattern,
+ basket->className))
+ return false;
+ break;
+ case MK_LOCATION_ONLY:
+ if (!locationMatch(&pMod->locationOnly.loc, basket->pLoc))
+ return false;
+ break;
+ case MK_EXCEPTION_ONLY:
+ if (pMod->exceptionOnly.refTypeId != 0 &&
+ !dvmDbgMatchType(basket->excepClassId,
+ pMod->exceptionOnly.refTypeId))
+ return false;
+ if ((basket->caught && !pMod->exceptionOnly.caught) ||
+ (!basket->caught && !pMod->exceptionOnly.uncaught))
+ return false;
+ break;
+ case MK_FIELD_ONLY:
+ // TODO
+ break;
+ case MK_STEP:
+ if (pMod->step.threadId != basket->threadId)
+ return false;
+ break;
+ case MK_INSTANCE_ONLY:
+ if (pMod->instanceOnly.objectId != basket->thisPtr)
+ return false;
+ break;
+ default:
+ LOGE("unhandled mod kind %d\n", pMod->modKind);
+ assert(false);
+ break;
+ }
+ }
+
+ return true;
+}
+
+/*
+ * Find all events of type "eventKind" with mods that match up with the
+ * rest of the arguments.
+ *
+ * Found events are appended to "matchList", and "*pMatchCount" is advanced,
+ * so this may be called multiple times for grouped events.
+ *
+ * DO NOT call this multiple times for the same eventKind, as Count mods are
+ * decremented during the scan.
+ */
+static void findMatchingEvents(JdwpState* state, enum JdwpEventKind eventKind,
+ ModBasket* basket, JdwpEvent** matchList, int* pMatchCount)
+{
+ JdwpEvent* pEvent;
+
+ /* start after the existing entries */
+ matchList += *pMatchCount;
+
+ pEvent = state->eventList;
+ while (pEvent != NULL) {
+ if (pEvent->eventKind == eventKind && modsMatch(state, pEvent, basket))
+ {
+ *matchList++ = pEvent;
+ (*pMatchCount)++;
+ }
+
+ pEvent = pEvent->next;
+ }
+}
+
+/*
+ * Scan through the list of matches and determine the most severe
+ * suspension policy.
+ */
+static enum JdwpSuspendPolicy scanSuspendPolicy(JdwpEvent** matchList,
+ int matchCount)
+{
+ enum JdwpSuspendPolicy policy = SP_NONE;
+
+ while (matchCount--) {
+ if ((*matchList)->suspendPolicy > policy)
+ policy = (*matchList)->suspendPolicy;
+ matchList++;
+ }
+
+ return policy;
+}
+
+/*
+ * Three possibilities:
+ * SP_NONE - do nothing
+ * SP_EVENT_THREAD - suspend ourselves
+ * SP_ALL - suspend everybody except JDWP support thread
+ */
+static void suspendByPolicy(JdwpState* state,
+ enum JdwpSuspendPolicy suspendPolicy)
+{
+ if (suspendPolicy == SP_NONE)
+ return;
+
+ if (suspendPolicy == SP_ALL) {
+ dvmDbgSuspendVM(true);
+ } else {
+ assert(suspendPolicy == SP_EVENT_THREAD);
+ }
+
+ /* this is rare but possible -- see CLASS_PREPARE handling */
+ if (dvmDbgGetThreadSelfId() == state->debugThreadId) {
+ LOGI("NOTE: suspendByPolicy not suspending JDWP thread\n");
+ return;
+ }
+
+ DebugInvokeReq* pReq = dvmDbgGetInvokeReq();
+ while (true) {
+ pReq->ready = true;
+ dvmDbgSuspendSelf();
+ pReq->ready = false;
+
+ /*
+ * The JDWP thread has told us (and possibly all other threads) to
+ * resume. See if it has left anything in our DebugInvokeReq mailbox.
+ */
+ if (!pReq->invokeNeeded) {
+ /*LOGD("suspendByPolicy: no invoke needed\n");*/
+ break;
+ }
+
+ /* grab this before posting/suspending again */
+ dvmJdwpSetWaitForEventThread(state, dvmDbgGetThreadSelfId());
+
+ /* leave pReq->invokeNeeded raised so we can check reentrancy */
+ LOGV("invoking method...\n");
+ dvmDbgExecuteMethod(pReq);
+
+ pReq->err = ERR_NONE;
+
+ /* clear this before signaling */
+ pReq->invokeNeeded = false;
+
+ LOGV("invoke complete, signaling and self-suspending\n");
+ dvmDbgLockMutex(&pReq->lock);
+ dvmDbgCondSignal(&pReq->cv);
+ dvmDbgUnlockMutex(&pReq->lock);
+ }
+}
+
+/*
+ * Determine if there is a method invocation in progress in the current
+ * thread.
+ *
+ * We look at the "invokeNeeded" flag in the per-thread DebugInvokeReq
+ * state. If set, we're in the process of invoking a method.
+ */
+static bool invokeInProgress(JdwpState* state)
+{
+ DebugInvokeReq* pReq = dvmDbgGetInvokeReq();
+ return pReq->invokeNeeded;
+}
+
+/*
+ * We need the JDWP thread to hold off on doing stuff while we post an
+ * event and then suspend ourselves.
+ *
+ * Call this with a threadId of zero if you just want to wait for the
+ * current thread operation to complete.
+ *
+ * This could go to sleep waiting for another thread, so it's important
+ * that the thread be marked as VMWAIT before calling here.
+ */
+void dvmJdwpSetWaitForEventThread(JdwpState* state, ObjectId threadId)
+{
+ bool waited = false;
+
+ /* this is held for very brief periods; contention is unlikely */
+ dvmDbgLockMutex(&state->eventThreadLock);
+
+ /*
+ * If another thread is already doing stuff, wait for it. This can
+ * go to sleep indefinitely.
+ */
+ while (state->eventThreadId != 0) {
+ LOGV("event in progress (0x%llx), 0x%llx sleeping\n",
+ state->eventThreadId, threadId);
+ waited = true;
+ dvmDbgCondWait(&state->eventThreadCond, &state->eventThreadLock);
+ }
+
+ if (waited || threadId != 0)
+ LOGV("event token grabbed (0x%llx)\n", threadId);
+ if (threadId != 0)
+ state->eventThreadId = threadId;
+
+ dvmDbgUnlockMutex(&state->eventThreadLock);
+}
+
+/*
+ * Clear the threadId and signal anybody waiting.
+ */
+void dvmJdwpClearWaitForEventThread(JdwpState* state)
+{
+ /*
+ * Grab the mutex. Don't try to go in/out of VMWAIT mode, as this
+ * function is called by dvmSuspendSelf(), and the transition back
+ * to RUNNING would confuse it.
+ */
+ dvmDbgLockMutex(&state->eventThreadLock);
+
+ assert(state->eventThreadId != 0);
+ LOGV("cleared event token (0x%llx)\n", state->eventThreadId);
+
+ state->eventThreadId = 0;
+
+ dvmDbgCondSignal(&state->eventThreadCond);
+
+ dvmDbgUnlockMutex(&state->eventThreadLock);
+}
+
+
+/*
+ * Prep an event. Allocates storage for the message and leaves space for
+ * the header.
+ */
+static ExpandBuf* eventPrep(void)
+{
+ ExpandBuf* pReq;
+
+ pReq = expandBufAlloc();
+ expandBufAddSpace(pReq, kJDWPHeaderLen);
+
+ return pReq;
+}
+
+/*
+ * Write the header into the buffer and send the packet off to the debugger.
+ *
+ * Takes ownership of "pReq" (currently discards it).
+ */
+static void eventFinish(JdwpState* state, ExpandBuf* pReq)
+{
+ u1* buf = expandBufGetBuffer(pReq);
+
+ set4BE(buf, expandBufGetLength(pReq));
+ set4BE(buf+4, dvmJdwpNextRequestSerial(state));
+ set1(buf+8, 0); /* flags */
+ set1(buf+9, kJdwpEventCommandSet);
+ set1(buf+10, kJdwpCompositeCommand);
+
+ dvmJdwpSendRequest(state, pReq);
+
+ expandBufFree(pReq);
+}
+
+
+/*
+ * Tell the debugger that we have finished initializing. This is always
+ * sent, even if the debugger hasn't requested it.
+ *
+ * This should be sent "before the main thread is started and before
+ * any application code has been executed". The thread ID in the message
+ * must be for the main thread.
+ */
+bool dvmJdwpPostVMStart(JdwpState* state, bool suspend)
+{
+ enum JdwpSuspendPolicy suspendPolicy;
+ ObjectId threadId = dvmDbgGetThreadSelfId();
+
+ if (suspend)
+ suspendPolicy = SP_ALL;
+ else
+ suspendPolicy = SP_NONE;
+
+ /* probably don't need this here */
+ lockEventMutex(state);
+
+ ExpandBuf* pReq = NULL;
+ if (true) {
+ LOGV("EVENT: %s\n", dvmJdwpEventKindStr(EK_VM_START));
+ LOGV(" suspendPolicy=%s\n", dvmJdwpSuspendPolicyStr(suspendPolicy));
+
+ pReq = eventPrep();
+ expandBufAdd1(pReq, suspendPolicy);
+ expandBufAdd4BE(pReq, 1);
+
+ expandBufAdd1(pReq, EK_VM_START);
+ expandBufAdd4BE(pReq, 0); /* requestId */
+ expandBufAdd8BE(pReq, threadId);
+ }
+
+ unlockEventMutex(state);
+
+ /* send request and possibly suspend ourselves */
+ if (pReq != NULL) {
+ int oldStatus = dvmDbgThreadWaiting();
+ if (suspendPolicy != SP_NONE)
+ dvmJdwpSetWaitForEventThread(state, threadId);
+
+ eventFinish(state, pReq);
+
+ suspendByPolicy(state, suspendPolicy);
+ dvmDbgThreadContinuing(oldStatus);
+ }
+
+ return true;
+}
+
+/*
+ * A location of interest has been reached. This handles:
+ * Breakpoint
+ * SingleStep
+ * MethodEntry
+ * MethodExit
+ * These four types must be grouped together in a single response. The
+ * "eventFlags" indicates the type of event(s) that have happened.
+ *
+ * Valid mods:
+ * Count, ThreadOnly, ClassOnly, ClassMatch, ClassExclude, InstanceOnly
+ * LocationOnly (for breakpoint/step only)
+ * Step (for step only)
+ *
+ * Interesting test cases:
+ * - Put a breakpoint on a native method. Eclipse creates METHOD_ENTRY
+ * and METHOD_EXIT events with a ClassOnly mod on the method's class.
+ * - Use "run to line". Eclipse creates a BREAKPOINT with Count=1.
+ * - Single-step to a line with a breakpoint. Should get a single
+ * event message with both events in it.
+ */
+bool dvmJdwpPostLocationEvent(JdwpState* state, const JdwpLocation* pLoc,
+ ObjectId thisPtr, int eventFlags)
+{
+ enum JdwpSuspendPolicy suspendPolicy = SP_NONE;
+ ModBasket basket;
+ JdwpEvent** matchList;
+ int matchCount;
+ char* nameAlloc = NULL;
+
+ memset(&basket, 0, sizeof(basket));
+ basket.pLoc = pLoc;
+ basket.classId = pLoc->classId;
+ basket.thisPtr = thisPtr;
+ basket.threadId = dvmDbgGetThreadSelfId();
+ basket.className = nameAlloc =
+ dvmDescriptorToName(dvmDbgGetClassDescriptor(pLoc->classId));
+
+ /*
+ * On rare occasions we may need to execute interpreted code in the VM
+ * while handling a request from the debugger. Don't fire breakpoints
+ * while doing so. (I don't think we currently do this at all, so
+ * this is mostly paranoia.)
+ */
+ if (basket.threadId == state->debugThreadId) {
+ LOGV("Ignoring location event in JDWP thread\n");
+ free(nameAlloc);
+ return false;
+ }
+
+ /*
+ * The debugger variable display tab may invoke the interpreter to format
+ * complex objects. We want to ignore breakpoints and method entry/exit
+ * traps while working on behalf of the debugger.
+ *
+ * If we don't ignore them, the VM will get hung up, because we'll
+ * suspend on a breakpoint while the debugger is still waiting for its
+ * method invocation to complete.
+ */
+ if (invokeInProgress(state)) {
+ LOGV("Not checking breakpoints during invoke (%s)\n", basket.className);
+ free(nameAlloc);
+ return false;
+ }
+
+ /* don't allow the list to be updated while we scan it */
+ lockEventMutex(state);
+
+ matchList = allocMatchList(state);
+ matchCount = 0;
+
+ if ((eventFlags & DBG_BREAKPOINT) != 0)
+ findMatchingEvents(state, EK_BREAKPOINT, &basket, matchList,
+ &matchCount);
+ if ((eventFlags & DBG_SINGLE_STEP) != 0)
+ findMatchingEvents(state, EK_SINGLE_STEP, &basket, matchList,
+ &matchCount);
+ if ((eventFlags & DBG_METHOD_ENTRY) != 0)
+ findMatchingEvents(state, EK_METHOD_ENTRY, &basket, matchList,
+ &matchCount);
+ if ((eventFlags & DBG_METHOD_EXIT) != 0)
+ findMatchingEvents(state, EK_METHOD_EXIT, &basket, matchList,
+ &matchCount);
+
+ ExpandBuf* pReq = NULL;
+ if (matchCount != 0) {
+ int i;
+
+ LOGV("EVENT: %s(%d total) %s.%s thread=%llx code=%llx)\n",
+ dvmJdwpEventKindStr(matchList[0]->eventKind), matchCount,
+ basket.className,
+ dvmDbgGetMethodName(pLoc->classId, pLoc->methodId),
+ basket.threadId, pLoc->idx);
+
+ suspendPolicy = scanSuspendPolicy(matchList, matchCount);
+ LOGV(" suspendPolicy=%s\n",
+ dvmJdwpSuspendPolicyStr(suspendPolicy));
+
+ pReq = eventPrep();
+ expandBufAdd1(pReq, suspendPolicy);
+ expandBufAdd4BE(pReq, matchCount);
+
+ for (i = 0; i < matchCount; i++) {
+ expandBufAdd1(pReq, matchList[i]->eventKind);
+ expandBufAdd4BE(pReq, matchList[i]->requestId);
+ expandBufAdd8BE(pReq, basket.threadId);
+ dvmJdwpAddLocation(pReq, pLoc);
+ }
+ }
+
+ cleanupMatchList(state, matchList, matchCount);
+ unlockEventMutex(state);
+
+ /* send request and possibly suspend ourselves */
+ if (pReq != NULL) {
+ int oldStatus = dvmDbgThreadWaiting();
+ if (suspendPolicy != SP_NONE)
+ dvmJdwpSetWaitForEventThread(state, basket.threadId);
+
+ eventFinish(state, pReq);
+
+ suspendByPolicy(state, suspendPolicy);
+ dvmDbgThreadContinuing(oldStatus);
+ }
+
+ free(nameAlloc);
+ return matchCount != 0;
+}
+
+/*
+ * A thread is starting or stopping.
+ *
+ * Valid mods:
+ * Count, ThreadOnly
+ */
+bool dvmJdwpPostThreadChange(JdwpState* state, ObjectId threadId, bool start)
+{
+ enum JdwpSuspendPolicy suspendPolicy = SP_NONE;
+ ModBasket basket;
+ JdwpEvent** matchList;
+ int matchCount;
+
+ assert(threadId = dvmDbgGetThreadSelfId());
+
+ /*
+ * I don't think this can happen.
+ */
+ if (invokeInProgress(state)) {
+ LOGW("Not posting thread change during invoke\n");
+ return false;
+ }
+
+ memset(&basket, 0, sizeof(basket));
+ basket.threadId = threadId;
+
+ /* don't allow the list to be updated while we scan it */
+ lockEventMutex(state);
+
+ matchList = allocMatchList(state);
+ matchCount = 0;
+
+ if (start)
+ findMatchingEvents(state, EK_THREAD_START, &basket, matchList,
+ &matchCount);
+ else
+ findMatchingEvents(state, EK_THREAD_DEATH, &basket, matchList,
+ &matchCount);
+
+ ExpandBuf* pReq = NULL;
+ if (matchCount != 0) {
+ int i;
+
+ LOGV("EVENT: %s(%d total) thread=%llx)\n",
+ dvmJdwpEventKindStr(matchList[0]->eventKind), matchCount,
+ basket.threadId);
+
+ suspendPolicy = scanSuspendPolicy(matchList, matchCount);
+ LOGV(" suspendPolicy=%s\n",
+ dvmJdwpSuspendPolicyStr(suspendPolicy));
+
+ pReq = eventPrep();
+ expandBufAdd1(pReq, suspendPolicy);
+ expandBufAdd4BE(pReq, matchCount);
+
+ for (i = 0; i < matchCount; i++) {
+ expandBufAdd1(pReq, matchList[i]->eventKind);
+ expandBufAdd4BE(pReq, matchList[i]->requestId);
+ expandBufAdd8BE(pReq, basket.threadId);
+ }
+
+ }
+
+ cleanupMatchList(state, matchList, matchCount);
+ unlockEventMutex(state);
+
+ /* send request and possibly suspend ourselves */
+ if (pReq != NULL) {
+ int oldStatus = dvmDbgThreadWaiting();
+ if (suspendPolicy != SP_NONE)
+ dvmJdwpSetWaitForEventThread(state, basket.threadId);
+
+ eventFinish(state, pReq);
+
+ suspendByPolicy(state, suspendPolicy);
+ dvmDbgThreadContinuing(oldStatus);
+ }
+
+ return matchCount != 0;
+}
+
+/*
+ * Send a polite "VM is dying" message to the debugger.
+ *
+ * Skips the usual "event token" stuff.
+ */
+bool dvmJdwpPostVMDeath(JdwpState* state)
+{
+ ExpandBuf* pReq;
+
+ LOGV("EVENT: %s\n", dvmJdwpEventKindStr(EK_VM_DEATH));
+
+ pReq = eventPrep();
+ expandBufAdd1(pReq, SP_NONE);
+ expandBufAdd4BE(pReq, 1);
+
+ expandBufAdd1(pReq, EK_VM_DEATH);
+ expandBufAdd4BE(pReq, 0);
+ eventFinish(state, pReq);
+ return true;
+}
+
+
+/*
+ * An exception has been thrown. It may or may not have been caught.
+ *
+ * Valid mods:
+ * Count, ThreadOnly, ClassOnly, ClassMatch, ClassExclude, LocationOnly,
+ * ExceptionOnly, InstanceOnly
+ *
+ * The "exceptionId" has not been added to the GC-visible object registry,
+ * because there's a pretty good chance that we're not going to send it
+ * up the debugger.
+ */
+bool dvmJdwpPostException(JdwpState* state, const JdwpLocation* pThrowLoc,
+ ObjectId exceptionId, RefTypeId exceptionClassId,
+ const JdwpLocation* pCatchLoc, ObjectId thisPtr)
+{
+ enum JdwpSuspendPolicy suspendPolicy = SP_NONE;
+ ModBasket basket;
+ JdwpEvent** matchList;
+ int matchCount;
+ char* nameAlloc = NULL;
+
+ memset(&basket, 0, sizeof(basket));
+ basket.pLoc = pThrowLoc;
+ basket.classId = pThrowLoc->classId;
+ basket.threadId = dvmDbgGetThreadSelfId();
+ basket.className = nameAlloc =
+ dvmDescriptorToName(dvmDbgGetClassDescriptor(basket.classId));
+ basket.excepClassId = exceptionClassId;
+ basket.caught = (pCatchLoc->classId != 0);
+ basket.thisPtr = thisPtr;
+
+ /* don't try to post an exception caused by the debugger */
+ if (invokeInProgress(state)) {
+ LOGV("Not posting exception hit during invoke (%s)\n",basket.className);
+ free(nameAlloc);
+ return false;
+ }
+
+ /* don't allow the list to be updated while we scan it */
+ lockEventMutex(state);
+
+ matchList = allocMatchList(state);
+ matchCount = 0;
+
+ findMatchingEvents(state, EK_EXCEPTION, &basket, matchList, &matchCount);
+
+ ExpandBuf* pReq = NULL;
+ if (matchCount != 0) {
+ int i;
+
+ LOGV("EVENT: %s(%d total) thread=%llx exceptId=%llx caught=%d)\n",
+ dvmJdwpEventKindStr(matchList[0]->eventKind), matchCount,
+ basket.threadId, exceptionId, basket.caught);
+ LOGV(" throw: %d %llx %x %lld (%s.%s)\n", pThrowLoc->typeTag,
+ pThrowLoc->classId, pThrowLoc->methodId, pThrowLoc->idx,
+ dvmDbgGetClassDescriptor(pThrowLoc->classId),
+ dvmDbgGetMethodName(pThrowLoc->classId, pThrowLoc->methodId));
+ if (pCatchLoc->classId == 0) {
+ LOGV(" catch: (not caught)\n");
+ } else {
+ LOGV(" catch: %d %llx %x %lld (%s.%s)\n", pCatchLoc->typeTag,
+ pCatchLoc->classId, pCatchLoc->methodId, pCatchLoc->idx,
+ dvmDbgGetClassDescriptor(pCatchLoc->classId),
+ dvmDbgGetMethodName(pCatchLoc->classId, pCatchLoc->methodId));
+ }
+
+ suspendPolicy = scanSuspendPolicy(matchList, matchCount);
+ LOGV(" suspendPolicy=%s\n",
+ dvmJdwpSuspendPolicyStr(suspendPolicy));
+
+ pReq = eventPrep();
+ expandBufAdd1(pReq, suspendPolicy);
+ expandBufAdd4BE(pReq, matchCount);
+
+ for (i = 0; i < matchCount; i++) {
+ expandBufAdd1(pReq, matchList[i]->eventKind);
+ expandBufAdd4BE(pReq, matchList[i]->requestId);
+ expandBufAdd8BE(pReq, basket.threadId);
+
+ dvmJdwpAddLocation(pReq, pThrowLoc);
+ expandBufAdd1(pReq, JT_OBJECT);
+ expandBufAdd8BE(pReq, exceptionId);
+ dvmJdwpAddLocation(pReq, pCatchLoc);
+ }
+
+ /* don't let the GC discard it */
+ dvmDbgRegisterObjectId(exceptionId);
+ }
+
+ cleanupMatchList(state, matchList, matchCount);
+ unlockEventMutex(state);
+
+ /* send request and possibly suspend ourselves */
+ if (pReq != NULL) {
+ int oldStatus = dvmDbgThreadWaiting();
+ if (suspendPolicy != SP_NONE)
+ dvmJdwpSetWaitForEventThread(state, basket.threadId);
+
+ eventFinish(state, pReq);
+
+ suspendByPolicy(state, suspendPolicy);
+ dvmDbgThreadContinuing(oldStatus);
+ }
+
+ free(nameAlloc);
+ return matchCount != 0;
+}
+
+/*
+ * Announce that a class has been loaded.
+ *
+ * Valid mods:
+ * Count, ThreadOnly, ClassOnly, ClassMatch, ClassExclude
+ */
+bool dvmJdwpPostClassPrepare(JdwpState* state, int tag, RefTypeId refTypeId,
+ const char* signature, int status)
+{
+ enum JdwpSuspendPolicy suspendPolicy = SP_NONE;
+ ModBasket basket;
+ JdwpEvent** matchList;
+ int matchCount;
+ char* nameAlloc = NULL;
+
+ memset(&basket, 0, sizeof(basket));
+ basket.classId = refTypeId;
+ basket.threadId = dvmDbgGetThreadSelfId();
+ basket.className = nameAlloc =
+ dvmDescriptorToName(dvmDbgGetClassDescriptor(basket.classId));
+
+ /* suppress class prep caused by debugger */
+ if (invokeInProgress(state)) {
+ LOGV("Not posting class prep caused by invoke (%s)\n",basket.className);
+ free(nameAlloc);
+ return false;
+ }
+
+ /* don't allow the list to be updated while we scan it */
+ lockEventMutex(state);
+
+ matchList = allocMatchList(state);
+ matchCount = 0;
+
+ findMatchingEvents(state, EK_CLASS_PREPARE, &basket, matchList,
+ &matchCount);
+
+ ExpandBuf* pReq = NULL;
+ if (matchCount != 0) {
+ int i;
+
+ LOGV("EVENT: %s(%d total) thread=%llx)\n",
+ dvmJdwpEventKindStr(matchList[0]->eventKind), matchCount,
+ basket.threadId);
+
+ suspendPolicy = scanSuspendPolicy(matchList, matchCount);
+ LOGV(" suspendPolicy=%s\n",
+ dvmJdwpSuspendPolicyStr(suspendPolicy));
+
+ if (basket.threadId == state->debugThreadId) {
+ /*
+ * JDWP says that, for a class prep in the debugger thread, we
+ * should set threadId to null and if any threads were supposed
+ * to be suspended then we suspend all other threads.
+ */
+ LOGV(" NOTE: class prepare in debugger thread!\n");
+ basket.threadId = 0;
+ if (suspendPolicy == SP_EVENT_THREAD)
+ suspendPolicy = SP_ALL;
+ }
+
+ pReq = eventPrep();
+ expandBufAdd1(pReq, suspendPolicy);
+ expandBufAdd4BE(pReq, matchCount);
+
+ for (i = 0; i < matchCount; i++) {
+ expandBufAdd1(pReq, matchList[i]->eventKind);
+ expandBufAdd4BE(pReq, matchList[i]->requestId);
+ expandBufAdd8BE(pReq, basket.threadId);
+
+ expandBufAdd1(pReq, tag);
+ expandBufAdd8BE(pReq, refTypeId);
+ expandBufAddUtf8String(pReq, (const u1*) signature);
+ expandBufAdd4BE(pReq, status);
+ }
+ }
+
+ cleanupMatchList(state, matchList, matchCount);
+
+ unlockEventMutex(state);
+
+ /* send request and possibly suspend ourselves */
+ if (pReq != NULL) {
+ int oldStatus = dvmDbgThreadWaiting();
+ if (suspendPolicy != SP_NONE)
+ dvmJdwpSetWaitForEventThread(state, basket.threadId);
+
+ eventFinish(state, pReq);
+
+ suspendByPolicy(state, suspendPolicy);
+ dvmDbgThreadContinuing(oldStatus);
+ }
+
+ free(nameAlloc);
+ return matchCount != 0;
+}
+
+/*
+ * Unload a class.
+ *
+ * Valid mods:
+ * Count, ClassMatch, ClassExclude
+ */
+bool dvmJdwpPostClassUnload(JdwpState* state, RefTypeId refTypeId)
+{
+ assert(false); // TODO
+ return false;
+}
+
+/*
+ * Get or set a field.
+ *
+ * Valid mods:
+ * Count, ThreadOnly, ClassOnly, ClassMatch, ClassExclude, FieldOnly,
+ * InstanceOnly
+ */
+bool dvmJdwpPostFieldAccess(JdwpState* state, int STUFF, ObjectId thisPtr,
+ bool modified)
+{
+ assert(false); // TODO
+ return false;
+}
+
+/*
+ * Send up a chunk of DDM data.
+ *
+ * While this takes the form of a JDWP "event", it doesn't interact with
+ * other debugger traffic, and can't suspend the VM, so we skip all of
+ * the fun event token gymnastics.
+ */
+void dvmJdwpDdmSendChunkV(JdwpState* state, int type, const struct iovec* iov,
+ int iovcnt)
+{
+ u1 header[kJDWPHeaderLen + 8];
+ size_t dataLen = 0;
+ int i;
+
+ assert(iov != NULL);
+ assert(iovcnt > 0 && iovcnt < 10);
+
+ /*
+ * "Wrap" the contents of the iovec with a JDWP/DDMS header. We do
+ * this by creating a new copy of the vector with space for the header.
+ */
+ struct iovec wrapiov[iovcnt+1];
+ for (i = 0; i < iovcnt; i++) {
+ wrapiov[i+1].iov_base = iov[i].iov_base;
+ wrapiov[i+1].iov_len = iov[i].iov_len;
+ dataLen += iov[i].iov_len;
+ }
+
+ /* form the header (JDWP plus DDMS) */
+ set4BE(header, sizeof(header) + dataLen);
+ set4BE(header+4, dvmJdwpNextRequestSerial(state));
+ set1(header+8, 0); /* flags */
+ set1(header+9, kJDWPDdmCmdSet);
+ set1(header+10, kJDWPDdmCmd);
+ set4BE(header+11, type);
+ set4BE(header+15, dataLen);
+
+ wrapiov[0].iov_base = header;
+ wrapiov[0].iov_len = sizeof(header);
+
+ dvmJdwpSendBufferedRequest(state, wrapiov, iovcnt+1);
+}
diff --git a/vm/jdwp/JdwpEvent.h b/vm/jdwp/JdwpEvent.h
new file mode 100644
index 0000000..1a6a2c7
--- /dev/null
+++ b/vm/jdwp/JdwpEvent.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Handle registration of events, and debugger event notification.
+ */
+#ifndef _DALVIK_JDWP_JDWPEVENT
+#define _DALVIK_JDWP_JDWPEVENT
+
+#include "JdwpConstants.h"
+#include "ExpandBuf.h"
+
+/*
+ * Event modifiers. A JdwpEvent may have zero or more of these.
+ */
+typedef union JdwpEventMod {
+ u1 modKind; /* JdwpModKind */
+ struct {
+ u1 modKind;
+ int count;
+ } count;
+ struct {
+ u1 modKind;
+ u4 exprId;
+ } conditional;
+ struct {
+ u1 modKind;
+ ObjectId threadId;
+ } threadOnly;
+ struct {
+ u1 modKind;
+ RefTypeId referenceTypeId;
+ } classOnly;
+ struct {
+ u1 modKind;
+ char* classPattern;
+ } classMatch;
+ struct {
+ u1 modKind;
+ char* classPattern;
+ } classExclude;
+ struct {
+ u1 modKind;
+ JdwpLocation loc;
+ } locationOnly;
+ struct {
+ u1 modKind;
+ u1 caught;
+ u1 uncaught;
+ RefTypeId refTypeId;
+ } exceptionOnly;
+ struct {
+ u1 modKind;
+ RefTypeId refTypeId;
+ FieldId fieldId;
+ } fieldOnly;
+ struct {
+ u1 modKind;
+ ObjectId threadId;
+ int size; /* JdwpStepSize */
+ int depth; /* JdwpStepDepth */
+ } step;
+ struct {
+ u1 modKind;
+ ObjectId objectId;
+ } instanceOnly;
+} JdwpEventMod;
+
+/*
+ * One of these for every registered event.
+ *
+ * We over-allocate the struct to hold the modifiers.
+ */
+typedef struct JdwpEvent {
+ struct JdwpEvent* prev; /* linked list */
+ struct JdwpEvent* next;
+
+ enum JdwpEventKind eventKind; /* what kind of event is this? */
+ enum JdwpSuspendPolicy suspendPolicy; /* suspend all, none, or self? */
+ int modCount; /* #of entries in mods[] */
+ u4 requestId; /* serial#, reported to debugger */
+
+ JdwpEventMod mods[1]; /* MUST be last field in struct */
+} JdwpEvent;
+
+/*
+ * Allocate an event structure with enough space.
+ */
+JdwpEvent* dvmJdwpEventAlloc(int numMods);
+void dvmJdwpEventFree(JdwpEvent* pEvent);
+
+/*
+ * Register an event by adding it to the event list.
+ *
+ * "*pEvent" must be storage allocated with jdwpEventAlloc(). The caller
+ * may discard its pointer after calling this.
+ */
+JdwpError dvmJdwpRegisterEvent(JdwpState* state, JdwpEvent* pEvent);
+
+/*
+ * Unregister an event, given the requestId.
+ */
+void dvmJdwpUnregisterEventById(JdwpState* state, u4 requestId);
+
+/*
+ * Unregister all events.
+ */
+void dvmJdwpUnregisterAll(JdwpState* state);
+
+/*
+ * Send an event, formatted into "pReq", to the debugger.
+ *
+ * (Messages are sent asynchronously, and do not receive a reply.)
+ */
+bool dvmJdwpSendRequest(JdwpState* state, ExpandBuf* pReq);
+
+#endif /*_DALVIK_JDWP_JDWPEVENT*/
diff --git a/vm/jdwp/JdwpHandler.c b/vm/jdwp/JdwpHandler.c
new file mode 100644
index 0000000..d2a657d
--- /dev/null
+++ b/vm/jdwp/JdwpHandler.c
@@ -0,0 +1,2223 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Handle messages from debugger.
+ *
+ * GENERAL NOTE: we're not currently testing the message length for
+ * correctness. This is usually a bad idea, but here we can probably
+ * get away with it so long as the debugger isn't broken. We can
+ * change the "read" macros to use "dataLen" to avoid wandering into
+ * bad territory, and have a single "is dataLen correct" check at the
+ * end of each function. Not needed at this time.
+ */
+#include "jdwp/JdwpPriv.h"
+#include "jdwp/JdwpHandler.h"
+#include "jdwp/JdwpEvent.h"
+#include "jdwp/JdwpConstants.h"
+#include "jdwp/ExpandBuf.h"
+
+#include "Bits.h"
+#include "Atomic.h"
+#include "DalvikVersion.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#if 0
+#include <time.h>
+#include <sys/time.h>
+static void showTime(const char* label)
+{
+ struct timeval tv;
+ int min, sec, msec;
+
+ gettimeofday(&tv, NULL);
+ min = (tv.tv_sec / 60) % 60;
+ sec = tv.tv_sec % 60;
+ msec = tv.tv_usec / 1000;
+
+ LOGI("%02d:%02d.%03d %s\n", min, sec, msec, label);
+}
+#endif
+
+/*
+ * Helper function: read a "location" from an input buffer.
+ */
+static void jdwpReadLocation(const u1** pBuf, JdwpLocation* pLoc)
+{
+ memset(pLoc, 0, sizeof(*pLoc)); /* allows memcmp() later */
+ pLoc->typeTag = read1(pBuf);
+ pLoc->classId = dvmReadObjectId(pBuf);
+ pLoc->methodId = dvmReadMethodId(pBuf);
+ pLoc->idx = read8BE(pBuf);
+}
+
+/*
+ * Helper function: write a "location" into the reply buffer.
+ */
+void dvmJdwpAddLocation(ExpandBuf* pReply, const JdwpLocation* pLoc)
+{
+ expandBufAdd1(pReply, pLoc->typeTag);
+ expandBufAddObjectId(pReply, pLoc->classId);
+ expandBufAddMethodId(pReply, pLoc->methodId);
+ expandBufAdd8BE(pReply, pLoc->idx);
+}
+
+/*
+ * Helper function: read a variable-width value from the input buffer.
+ */
+static u8 jdwpReadValue(const u1** pBuf, int width)
+{
+ u8 value;
+
+ switch (width) {
+ case 1: value = read1(pBuf); break;
+ case 2: value = read2BE(pBuf); break;
+ case 4: value = read4BE(pBuf); break;
+ case 8: value = read8BE(pBuf); break;
+ default: value = (u8) -1; assert(false); break;
+ }
+
+ return value;
+}
+
+/*
+ * Helper function: write a variable-width value into the output input buffer.
+ */
+static void jdwpWriteValue(ExpandBuf* pReply, int width, u8 value)
+{
+ switch (width) {
+ case 1: expandBufAdd1(pReply, value); break;
+ case 2: expandBufAdd2BE(pReply, value); break;
+ case 4: expandBufAdd4BE(pReply, value); break;
+ case 8: expandBufAdd8BE(pReply, value); break;
+ default: assert(false); break;
+ }
+}
+
+/*
+ * Common code for *_InvokeMethod requests.
+ *
+ * If "isConstructor" is set, this returns "objectId" rather than the
+ * expected-to-be-void return value of the called function.
+ */
+static JdwpError finishInvoke(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply,
+ ObjectId threadId, ObjectId objectId, RefTypeId classId, MethodId methodId,
+ bool isConstructor)
+{
+ JdwpError err = ERR_NONE;
+ u8* argArray = NULL;
+ u4 numArgs;
+ u4 options; /* enum InvokeOptions bit flags */
+ int i;
+
+ assert(!isConstructor || objectId != 0);
+
+ numArgs = read4BE(&buf);
+
+ LOGV(" --> threadId=%llx objectId=%llx\n", threadId, objectId);
+ LOGV(" classId=%llx methodId=%x %s.%s\n",
+ classId, methodId,
+ dvmDbgGetClassDescriptor(classId),
+ dvmDbgGetMethodName(classId, methodId));
+ LOGV(" %d args:\n", numArgs);
+
+ if (numArgs > 0)
+ argArray = (ObjectId*) malloc(sizeof(ObjectId) * numArgs);
+
+ for (i = 0; i < (int) numArgs; i++) {
+ u1 typeTag;
+ u8 value;
+ int width;
+
+ typeTag = read1(&buf);
+ width = dvmDbgGetTagWidth(typeTag);
+ value = jdwpReadValue(&buf, width);
+
+ LOGV(" '%c'(%d): 0x%llx\n", typeTag, width, value);
+ argArray[i] = value;
+ }
+
+ options = read4BE(&buf);
+ LOGV(" options=0x%04x%s%s\n", options,
+ (options & INVOKE_SINGLE_THREADED) ? " (SINGLE_THREADED)" : "",
+ (options & INVOKE_NONVIRTUAL) ? " (NONVIRTUAL)" : "");
+
+
+ u1 resultTag;
+ u8 resultValue;
+ ObjectId exceptObjId;
+
+ err = dvmDbgInvokeMethod(threadId, objectId, classId, methodId,
+ numArgs, argArray, options,
+ &resultTag, &resultValue, &exceptObjId);
+ if (err != ERR_NONE)
+ goto bail;
+
+ if (err == ERR_NONE) {
+ if (isConstructor) {
+ expandBufAdd1(pReply, JT_OBJECT);
+ expandBufAddObjectId(pReply, objectId);
+ } else {
+ int width = dvmDbgGetTagWidth(resultTag);
+
+ expandBufAdd1(pReply, resultTag);
+ if (width != 0)
+ jdwpWriteValue(pReply, width, resultValue);
+ }
+ expandBufAdd1(pReply, JT_OBJECT);
+ expandBufAddObjectId(pReply, exceptObjId);
+
+ LOGV(" --> returned '%c' 0x%llx (except=%08llx)\n",
+ resultTag, resultValue, exceptObjId);
+
+ /* show detailed debug output */
+ if (resultTag == JT_STRING && exceptObjId == 0) {
+ if (resultValue != 0) {
+ char* str = dvmDbgStringToUtf8(resultValue);
+ LOGV(" string '%s'\n", str);
+ free(str);
+ } else {
+ LOGV(" string (null)\n");
+ }
+ }
+ }
+
+bail:
+ free(argArray);
+ return err;
+}
+
+
+/*
+ * Request for version info.
+ */
+static JdwpError handleVM_Version(JdwpState* state, const u1* buf,
+ int dataLen, ExpandBuf* pReply)
+{
+ char tmpBuf[128];
+
+ /* text information on VM version */
+ sprintf(tmpBuf, "Android DalvikVM %d.%d.%d",
+ DALVIK_MAJOR_VERSION, DALVIK_MINOR_VERSION, DALVIK_BUG_VERSION);
+ expandBufAddUtf8String(pReply, (const u1*) tmpBuf);
+ /* JDWP version numbers */
+ expandBufAdd4BE(pReply, 1); // major
+ expandBufAdd4BE(pReply, 5); // minor
+ /* VM JRE version */
+ expandBufAddUtf8String(pReply, (const u1*) "1.5.0"); /* e.g. 1.5.0_04 */
+ /* target VM name */
+ expandBufAddUtf8String(pReply, (const u1*) "DalvikVM");
+
+ return ERR_NONE;
+}
+
+/*
+ * Given a class JNI signature (e.g. "Ljava/lang/Error;"), return the
+ * referenceTypeID. We need to send back more than one if the class has
+ * been loaded by multiple class loaders.
+ */
+static JdwpError handleVM_ClassesBySignature(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ char* classDescriptor = NULL;
+ u4 numClasses;
+ size_t strLen;
+ RefTypeId refTypeId;
+
+ classDescriptor = readNewUtf8String(&buf, &strLen);
+ LOGV(" Req for class by signature '%s'\n", classDescriptor);
+
+ /*
+ * TODO: if a class with the same name has been loaded multiple times
+ * (by different class loaders), we're supposed to return each of them.
+ *
+ * NOTE: this may mangle "className".
+ */
+ if (!dvmDbgFindLoadedClassBySignature(classDescriptor, &refTypeId)) {
+ /* not currently loaded */
+ LOGV(" --> no match!\n");
+ numClasses = 0;
+ } else {
+ /* just the one */
+ numClasses = 1;
+ }
+
+ expandBufAdd4BE(pReply, numClasses);
+
+ if (numClasses > 0) {
+ u1 typeTag;
+ u4 status;
+
+ /* get class vs. interface and status flags */
+ dvmDbgGetClassInfo(refTypeId, &typeTag, &status, NULL);
+
+ expandBufAdd1(pReply, typeTag);
+ expandBufAddRefTypeId(pReply, refTypeId);
+ expandBufAdd4BE(pReply, status);
+ }
+
+ free(classDescriptor);
+
+ return ERR_NONE;
+}
+
+/*
+ * Handle request for the thread IDs of all running threads.
+ *
+ * We exclude ourselves from the list, because we don't allow ourselves
+ * to be suspended, and that violates some JDWP expectations.
+ */
+static JdwpError handleVM_AllThreads(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ u4 threadCount;
+ ObjectId* pThreadIds;
+ ObjectId* walker;
+ int i;
+
+ dvmDbgGetAllThreads(&pThreadIds, &threadCount);
+
+ expandBufAdd4BE(pReply, threadCount);
+
+ walker = pThreadIds;
+ for (i = 0; i < (int) threadCount; i++) {
+ expandBufAddObjectId(pReply, *walker++);
+ }
+
+ free(pThreadIds);
+
+ return ERR_NONE;
+}
+
+/*
+ * List all thread groups that do not have a parent.
+ */
+static JdwpError handleVM_TopLevelThreadGroups(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ u4 groups;
+ ObjectId threadGroupId;
+
+ /*
+ * TODO: maintain a list of parentless thread groups in the VM.
+ *
+ * For now, just return "system". Application threads are created
+ * in "main", which is a child of "system".
+ */
+ groups = 1;
+ expandBufAdd4BE(pReply, groups);
+ //threadGroupId = debugGetMainThreadGroup();
+ //expandBufAdd8BE(pReply, threadGroupId);
+ threadGroupId = dvmDbgGetSystemThreadGroupId();
+ expandBufAddObjectId(pReply, threadGroupId);
+
+ return ERR_NONE;
+}
+
+/*
+ * Respond with the sizes of the basic debugger types.
+ *
+ * All IDs are 8 bytes.
+ */
+static JdwpError handleVM_IDSizes(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ expandBufAdd4BE(pReply, sizeof(FieldId));
+ expandBufAdd4BE(pReply, sizeof(MethodId));
+ expandBufAdd4BE(pReply, sizeof(ObjectId));
+ expandBufAdd4BE(pReply, sizeof(RefTypeId));
+ expandBufAdd4BE(pReply, sizeof(FrameId));
+ return ERR_NONE;
+}
+
+/*
+ * The debugger is politely asking to disconnect. We're good with that.
+ *
+ * We could resume threads and clean up pinned references, but we can do
+ * that when the TCP connection drops.
+ */
+static JdwpError handleVM_Dispose(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ return ERR_NONE;
+}
+
+/*
+ * Suspend the execution of the application running in the VM (i.e. suspend
+ * all threads).
+ *
+ * This needs to increment the "suspend count" on all threads.
+ */
+static JdwpError handleVM_Suspend(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ dvmDbgSuspendVM(false);
+ return ERR_NONE;
+}
+
+/*
+ * Resume execution. Decrements the "suspend count" of all threads.
+ */
+static JdwpError handleVM_Resume(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ dvmDbgResumeVM();
+ return ERR_NONE;
+}
+
+/*
+ * The debugger wants the entire VM to exit.
+ */
+static JdwpError handleVM_Exit(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ u4 exitCode;
+
+ exitCode = get4BE(buf);
+
+ LOGW("Debugger is telling the VM to exit with code=%d\n", exitCode);
+
+ dvmDbgExit(exitCode);
+ return ERR_NOT_IMPLEMENTED; // shouldn't get here
+}
+
+/*
+ * Create a new string in the VM and return its ID.
+ *
+ * (Ctrl-Shift-I in Eclipse on an array of objects causes it to create the
+ * string "java.util.Arrays".)
+ */
+static JdwpError handleVM_CreateString(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ char* str;
+ size_t strLen;
+ ObjectId stringId;
+
+ str = readNewUtf8String(&buf, &strLen);
+
+ LOGV(" Req to create string '%s'\n", str);
+
+ stringId = dvmDbgCreateString(str);
+ if (stringId == 0)
+ return ERR_OUT_OF_MEMORY;
+
+ expandBufAddObjectId(pReply, stringId);
+ return ERR_NONE;
+}
+
+/*
+ * Tell the debugger what we are capable of.
+ */
+static JdwpError handleVM_Capabilities(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ expandBufAdd1(pReply, false); /* canWatchFieldModification */
+ expandBufAdd1(pReply, false); /* canWatchFieldAccess */
+ expandBufAdd1(pReply, false); /* canGetBytecodes */
+ expandBufAdd1(pReply, false); /* canGetSyntheticAttribute */
+ expandBufAdd1(pReply, false); /* canGetOwnedMonitorInfo */
+ expandBufAdd1(pReply, false); /* canGetCurrentContendedMonitor */
+ expandBufAdd1(pReply, false); /* canGetMonitorInfo */
+ return ERR_NONE;
+}
+
+/*
+ * Return classpath and bootclasspath.
+ */
+static JdwpError handleVM_ClassPaths(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ char baseDir[2] = "/";
+ u4 classPaths;
+ u4 bootClassPaths;
+ int i;
+
+ /*
+ * TODO: make this real. Not important for remote debugging, but
+ * might be useful for local debugging.
+ */
+ classPaths = 1;
+ bootClassPaths = 0;
+
+ expandBufAddUtf8String(pReply, (const u1*) baseDir);
+ expandBufAdd4BE(pReply, classPaths);
+ for (i = 0; i < (int) classPaths; i++) {
+ expandBufAddUtf8String(pReply, (const u1*) ".");
+ }
+
+ expandBufAdd4BE(pReply, bootClassPaths);
+ for (i = 0; i < (int) classPaths; i++) {
+ /* add bootclasspath components as strings */
+ }
+
+ return ERR_NONE;
+}
+
+/*
+ * Release a list of object IDs. (Seen in jdb.)
+ *
+ * Currently does nothing.
+ */
+static JdwpError HandleVM_DisposeObjects(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ return ERR_NONE;
+}
+
+/*
+ * Tell the debugger what we are capable of.
+ */
+static JdwpError handleVM_CapabilitiesNew(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ int i;
+
+ expandBufAdd1(pReply, false); /* canWatchFieldModification */
+ expandBufAdd1(pReply, false); /* canWatchFieldAccess */
+ expandBufAdd1(pReply, false); /* canGetBytecodes */
+ expandBufAdd1(pReply, false); /* canGetSyntheticAttribute */
+ expandBufAdd1(pReply, false); /* canGetOwnedMonitorInfo */
+ expandBufAdd1(pReply, false); /* canGetCurrentContendedMonitor */
+ expandBufAdd1(pReply, false); /* canGetMonitorInfo */
+ expandBufAdd1(pReply, false); /* canRedefineClasses */
+ expandBufAdd1(pReply, false); /* canAddMethod */
+ expandBufAdd1(pReply, false); /* canUnrestrictedlyRedefineClasses */
+ expandBufAdd1(pReply, false); /* canPopFrames */
+ expandBufAdd1(pReply, false); /* canUseInstanceFilters */
+ expandBufAdd1(pReply, false); /* canGetSourceDebugExtension */
+ expandBufAdd1(pReply, false); /* canRequestVMDeathEvent */
+ expandBufAdd1(pReply, false); /* canSetDefaultStratum */
+ expandBufAdd1(pReply, false); /* 1.6: canGetInstanceInfo */
+ expandBufAdd1(pReply, false); /* 1.6: canRequestMonitorEvents */
+ expandBufAdd1(pReply, false); /* 1.6: canGetMonitorFrameInfo */
+ expandBufAdd1(pReply, false); /* 1.6: canUseSourceNameFilters */
+ expandBufAdd1(pReply, false); /* 1.6: canGetConstantPool */
+ expandBufAdd1(pReply, false); /* 1.6: canForceEarlyReturn */
+
+ /* fill in reserved22 through reserved32; note count started at 1 */
+ for (i = 22; i <= 32; i++)
+ expandBufAdd1(pReply, false); /* reservedN */
+ return ERR_NONE;
+}
+
+/*
+ * Cough up the complete list of classes.
+ */
+static JdwpError handleVM_AllClassesWithGeneric(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ u4 numClasses = 0;
+ RefTypeId* classRefBuf = NULL;
+ int i;
+
+ dvmDbgGetClassList(&numClasses, &classRefBuf);
+
+ expandBufAdd4BE(pReply, numClasses);
+
+ for (i = 0; i < (int) numClasses; i++) {
+ static const u1 genericSignature[1] = "";
+ u1 refTypeTag;
+ char* signature;
+ u4 status;
+
+ dvmDbgGetClassInfo(classRefBuf[i], &refTypeTag, &status, &signature);
+
+ expandBufAdd1(pReply, refTypeTag);
+ expandBufAddRefTypeId(pReply, classRefBuf[i]);
+ expandBufAddUtf8String(pReply, (const u1*) signature);
+ expandBufAddUtf8String(pReply, genericSignature);
+ expandBufAdd4BE(pReply, status);
+
+ free(signature);
+ }
+
+ free(classRefBuf);
+
+ return ERR_NONE;
+}
+
+/*
+ * Given a referenceTypeID, return a string with the JNI reference type
+ * signature (e.g. "Ljava/lang/Error;").
+ */
+static JdwpError handleRT_Signature(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ char* signature;
+ RefTypeId refTypeId;
+
+ refTypeId = dvmReadRefTypeId(&buf);
+
+ LOGV(" Req for signature of refTypeId=0x%llx\n", refTypeId);
+ signature = dvmDbgGetSignature(refTypeId);
+ expandBufAddUtf8String(pReply, (const u1*) signature);
+ free(signature);
+
+ return ERR_NONE;
+}
+
+/*
+ * Return the modifiers (a/k/a access flags) for a reference type.
+ */
+static JdwpError handleRT_Modifiers(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ RefTypeId refTypeId;
+ u4 modBits;
+
+ refTypeId = dvmReadRefTypeId(&buf);
+ modBits = dvmDbgGetAccessFlags(refTypeId);
+
+ expandBufAdd4BE(pReply, modBits);
+
+ return ERR_NONE;
+}
+
+/*
+ * Get values from static fields in a reference type.
+ */
+static JdwpError handleRT_GetValues(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ RefTypeId refTypeId;
+ u4 numFields;
+ int i;
+
+ refTypeId = dvmReadRefTypeId(&buf);
+ numFields = read4BE(&buf);
+
+ expandBufAdd4BE(pReply, numFields);
+ for (i = 0; i < (int) numFields; i++) {
+ FieldId fieldId;
+ u1 fieldTag;
+ int width;
+ u1* ptr;
+
+ fieldId = dvmReadFieldId(&buf);
+ fieldTag = dvmDbgGetFieldTag(refTypeId, fieldId);
+ width = dvmDbgGetTagWidth(fieldTag);
+
+ expandBufAdd1(pReply, fieldTag);
+ ptr = expandBufAddSpace(pReply, width);
+ dvmDbgGetStaticFieldValue(refTypeId, fieldId, ptr, width);
+ }
+
+ return ERR_NONE;
+}
+
+/*
+ * Get the name of the source file in which a reference type was declared.
+ */
+static JdwpError handleRT_SourceFile(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ RefTypeId refTypeId;
+ const char* fileName;
+
+ refTypeId = dvmReadRefTypeId(&buf);
+
+ fileName = dvmDbgGetSourceFile(refTypeId);
+ if (fileName != NULL) {
+ expandBufAddUtf8String(pReply, (const u1*) fileName);
+ return ERR_NONE;
+ } else {
+ return ERR_ABSENT_INFORMATION;
+ }
+}
+
+/*
+ * Return the current status of the reference type.
+ */
+static JdwpError handleRT_Status(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ RefTypeId refTypeId;
+ u1 typeTag;
+ u4 status;
+
+ refTypeId = dvmReadRefTypeId(&buf);
+
+ /* get status flags */
+ dvmDbgGetClassInfo(refTypeId, &typeTag, &status, NULL);
+ expandBufAdd4BE(pReply, status);
+ return ERR_NONE;
+}
+
+/*
+ * Return interfaces implemented directly by this class.
+ */
+static JdwpError handleRT_Interfaces(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ RefTypeId refTypeId;
+
+ refTypeId = dvmReadRefTypeId(&buf);
+
+ LOGV(" Req for interfaces in %llx (%s)\n", refTypeId,
+ dvmDbgGetClassDescriptor(refTypeId));
+
+ dvmDbgOutputAllInterfaces(refTypeId, pReply);
+
+ return ERR_NONE;
+}
+
+/*
+ * Return the class object corresponding to this type.
+ */
+static JdwpError handleRT_ClassObject(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ RefTypeId refTypeId;
+ ObjectId classObjId;
+
+ refTypeId = dvmReadRefTypeId(&buf);
+ classObjId = dvmDbgGetClassObject(refTypeId);
+
+ LOGV(" RefTypeId %llx -> ObjectId %llx\n", refTypeId, classObjId);
+
+ expandBufAddObjectId(pReply, classObjId);
+
+ return ERR_NONE;
+}
+
+/*
+ * Returns the value of the SourceDebugExtension attribute.
+ *
+ * JDB seems interested, but DEX files don't currently support this.
+ */
+static JdwpError handleRT_SourceDebugExtension(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ /* referenceTypeId in, string out */
+ return ERR_ABSENT_INFORMATION;
+}
+
+/*
+ * Like RT_Signature but with the possibility of a "generic signature".
+ */
+static JdwpError handleRT_SignatureWithGeneric(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ static const u1 genericSignature[1] = "";
+ char* signature;
+ RefTypeId refTypeId;
+
+ refTypeId = dvmReadRefTypeId(&buf);
+
+ LOGV(" Req for signature of refTypeId=0x%llx\n", refTypeId);
+ signature = dvmDbgGetSignature(refTypeId);
+ if (signature != NULL)
+ expandBufAddUtf8String(pReply, (const u1*) signature);
+ else
+ expandBufAddUtf8String(pReply, (const u1*) "Lunknown;"); /* native? */
+ expandBufAddUtf8String(pReply, genericSignature);
+ free(signature);
+
+ return ERR_NONE;
+}
+
+/*
+ * Return the instance of java.lang.ClassLoader that loaded the specified
+ * reference type, or null if it was loaded by the system loader.
+ */
+static JdwpError handleRT_ClassLoader(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ RefTypeId refTypeId;
+
+ refTypeId = dvmReadRefTypeId(&buf);
+
+ expandBufAddObjectId(pReply, dvmDbgGetClassLoader(refTypeId));
+
+ return ERR_NONE;
+}
+
+/*
+ * Given a referenceTypeId, return a block of stuff that describes the
+ * fields declared by a class.
+ */
+static JdwpError handleRT_FieldsWithGeneric(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ RefTypeId refTypeId;
+
+ refTypeId = dvmReadRefTypeId(&buf);
+ LOGV(" Req for fields in refTypeId=0x%llx\n", refTypeId);
+ {
+ char* tmp = dvmDbgGetSignature(refTypeId);
+ LOGV(" --> '%s'\n", tmp);
+ free(tmp);
+ }
+
+ dvmDbgOutputAllFields(refTypeId, true, pReply);
+
+ return ERR_NONE;
+}
+
+/*
+ * Given a referenceTypeID, return a block of goodies describing the
+ * methods declared by a class.
+ */
+static JdwpError handleRT_MethodsWithGeneric(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ RefTypeId refTypeId;
+
+ refTypeId = dvmReadRefTypeId(&buf);
+
+ LOGV(" Req for methods in refTypeId=0x%llx\n", refTypeId);
+ {
+ char* tmp = dvmDbgGetSignature(refTypeId);
+ LOGV(" --> '%s'\n", tmp);
+ free(tmp);
+ }
+
+ dvmDbgOutputAllMethods(refTypeId, true, pReply);
+
+ return ERR_NONE;
+}
+
+/*
+ * Return the immediate superclass of a class.
+ */
+static JdwpError handleCT_Superclass(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ RefTypeId classId;
+ RefTypeId superClassId;
+
+ classId = dvmReadRefTypeId(&buf);
+
+ superClassId = dvmDbgGetSuperclass(classId);
+
+ expandBufAddRefTypeId(pReply, superClassId);
+
+ return ERR_NONE;
+}
+
+/*
+ * Set static class values.
+ */
+static JdwpError handleCT_SetValues(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ RefTypeId classId;
+ u4 values;
+ int i;
+
+ classId = dvmReadRefTypeId(&buf);
+ values = read4BE(&buf);
+
+ LOGV(" Req to set %d values in classId=%llx\n", values, classId);
+
+ for (i = 0; i < (int) values; i++) {
+ FieldId fieldId;
+ u1 fieldTag;
+ u8 value;
+ int width;
+
+ fieldId = dvmReadFieldId(&buf);
+ fieldTag = dvmDbgGetStaticFieldTag(classId, fieldId);
+ width = dvmDbgGetTagWidth(fieldTag);
+ value = jdwpReadValue(&buf, width);
+
+ LOGV(" --> field=%x tag=%c -> %lld\n", fieldId, fieldTag, value);
+ dvmDbgSetStaticFieldValue(classId, fieldId, value, width);
+ }
+
+ return ERR_NONE;
+}
+
+/*
+ * Invoke a static method.
+ *
+ * Example: Eclipse sometimes uses java/lang/Class.forName(String s) on
+ * values in the "variables" display.
+ */
+static JdwpError handleCT_InvokeMethod(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ RefTypeId classId;
+ ObjectId threadId;
+ MethodId methodId;
+
+ classId = dvmReadRefTypeId(&buf);
+ threadId = dvmReadObjectId(&buf);
+ methodId = dvmReadMethodId(&buf);
+
+ return finishInvoke(state, buf, dataLen, pReply,
+ threadId, 0, classId, methodId, false);
+}
+
+/*
+ * Create a new object of the requested type, and invoke the specified
+ * constructor.
+ *
+ * Example: in IntelliJ, create a watch on "new String(myByteArray)" to
+ * see the contents of a byte[] as a string.
+ */
+static JdwpError handleCT_NewInstance(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ RefTypeId classId;
+ ObjectId threadId;
+ MethodId methodId;
+ ObjectId objectId;
+
+ classId = dvmReadRefTypeId(&buf);
+ threadId = dvmReadObjectId(&buf);
+ methodId = dvmReadMethodId(&buf);
+
+ LOGV("Creating instance of %s\n", dvmDbgGetClassDescriptor(classId));
+ objectId = dvmDbgCreateObject(classId);
+ if (objectId == 0)
+ return ERR_OUT_OF_MEMORY;
+
+ return finishInvoke(state, buf, dataLen, pReply,
+ threadId, objectId, classId, methodId, true);
+}
+
+/*
+ * Create a new array object of the requested type and length.
+ */
+static JdwpError handleAT_newInstance(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ RefTypeId arrayTypeId;
+ u4 length;
+ ObjectId objectId;
+
+ arrayTypeId = dvmReadRefTypeId(&buf);
+ length = read4BE(&buf);
+
+ LOGV("Creating array %s[%u]\n",
+ dvmDbgGetClassDescriptor(arrayTypeId), length);
+ objectId = dvmDbgCreateArrayObject(arrayTypeId, length);
+ if (objectId == 0)
+ return ERR_OUT_OF_MEMORY;
+
+ expandBufAdd1(pReply, JT_ARRAY);
+ expandBufAddObjectId(pReply, objectId);
+ return ERR_NONE;
+}
+
+/*
+ * Return line number information for the method, if present.
+ */
+static JdwpError handleM_LineTable(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ RefTypeId refTypeId;
+ MethodId methodId;
+
+ refTypeId = dvmReadRefTypeId(&buf);
+ methodId = dvmReadMethodId(&buf);
+
+ LOGV(" Req for line table in %s.%s\n",
+ dvmDbgGetClassDescriptor(refTypeId),
+ dvmDbgGetMethodName(refTypeId,methodId));
+
+ dvmDbgOutputLineTable(refTypeId, methodId, pReply);
+
+ return ERR_NONE;
+}
+
+/*
+ * Pull out the LocalVariableTable goodies.
+ */
+static JdwpError handleM_VariableTableWithGeneric(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ RefTypeId classId;
+ MethodId methodId;
+
+ classId = dvmReadRefTypeId(&buf);
+ methodId = dvmReadMethodId(&buf);
+
+ LOGV(" Req for LocalVarTab in class=%s method=%s\n",
+ dvmDbgGetClassDescriptor(classId),
+ dvmDbgGetMethodName(classId, methodId));
+
+ /*
+ * We could return ERR_ABSENT_INFORMATION here if the DEX file was
+ * built without local variable information. That will cause Eclipse
+ * to make a best-effort attempt at displaying local variables
+ * anonymously. However, the attempt isn't very good, so we're probably
+ * better off just not showing anything.
+ */
+ dvmDbgOutputVariableTable(classId, methodId, true, pReply);
+ return ERR_NONE;
+}
+
+/*
+ * Given an object reference, return the runtime type of the object
+ * (class or array).
+ *
+ * This can get called on different things, e.g. threadId gets
+ * passed in here.
+ */
+static JdwpError handleOR_ReferenceType(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ ObjectId objectId;
+ u1 refTypeTag;
+ RefTypeId typeId;
+
+ objectId = dvmReadObjectId(&buf);
+ LOGV(" Req for type of objectId=0x%llx\n", objectId);
+
+ dvmDbgGetObjectType(objectId, &refTypeTag, &typeId);
+
+ expandBufAdd1(pReply, refTypeTag);
+ expandBufAddRefTypeId(pReply, typeId);
+
+ return ERR_NONE;
+}
+
+/*
+ * Get values from the fields of an object.
+ */
+static JdwpError handleOR_GetValues(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ ObjectId objectId;
+ u4 numFields;
+ int i;
+
+ objectId = dvmReadObjectId(&buf);
+ numFields = read4BE(&buf);
+
+ LOGV(" Req for %d fields from objectId=0x%llx\n", numFields, objectId);
+
+ expandBufAdd4BE(pReply, numFields);
+
+ for (i = 0; i < (int) numFields; i++) {
+ FieldId fieldId;
+ u1 fieldTag;
+ int width;
+ u1* ptr;
+
+ fieldId = dvmReadFieldId(&buf);
+
+ fieldTag = dvmDbgGetFieldTag(objectId, fieldId);
+ width = dvmDbgGetTagWidth(fieldTag);
+
+ LOGV(" --> fieldId %x --> tag '%c'(%d)\n",
+ fieldId, fieldTag, width);
+
+ expandBufAdd1(pReply, fieldTag);
+ ptr = expandBufAddSpace(pReply, width);
+ dvmDbgGetFieldValue(objectId, fieldId, ptr, width);
+ }
+
+ return ERR_NONE;
+}
+
+/*
+ * Set values in the fields of an object.
+ */
+static JdwpError handleOR_SetValues(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ ObjectId objectId;
+ u4 numFields;
+ int i;
+
+ objectId = dvmReadObjectId(&buf);
+ numFields = read4BE(&buf);
+
+ LOGV(" Req to set %d fields in objectId=0x%llx\n", numFields, objectId);
+
+ for (i = 0; i < (int) numFields; i++) {
+ FieldId fieldId;
+ u1 fieldTag;
+ int width;
+ u8 value;
+
+ fieldId = dvmReadFieldId(&buf);
+
+ fieldTag = dvmDbgGetFieldTag(objectId, fieldId);
+ width = dvmDbgGetTagWidth(fieldTag);
+ value = jdwpReadValue(&buf, width);
+
+ LOGV(" --> fieldId=%x tag='%c'(%d) value=%lld\n",
+ fieldId, fieldTag, width, value);
+
+ dvmDbgSetFieldValue(objectId, fieldId, value, width);
+ }
+
+ return ERR_NONE;
+}
+
+/*
+ * Invoke an instance method. The invocation must occur in the specified
+ * thread, which must have been suspended by an event.
+ *
+ * The call is synchronous. All threads in the VM are resumed, unless the
+ * SINGLE_THREADED flag is set.
+ *
+ * If you ask Eclipse to "inspect" an object (or ask JDB to "print" an
+ * object), it will try to invoke the object's toString() function. This
+ * feature becomes crucial when examining ArrayLists with Eclipse.
+ */
+static JdwpError handleOR_InvokeMethod(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ ObjectId objectId;
+ ObjectId threadId;
+ RefTypeId classId;
+ MethodId methodId;
+
+ objectId = dvmReadObjectId(&buf);
+ threadId = dvmReadObjectId(&buf);
+ classId = dvmReadRefTypeId(&buf);
+ methodId = dvmReadMethodId(&buf);
+
+ return finishInvoke(state, buf, dataLen, pReply,
+ threadId, objectId, classId, methodId, false);
+}
+
+/*
+ * Disable garbage collection of the specified object.
+ */
+static JdwpError handleOR_DisableCollection(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ // this is currently a no-op
+ return ERR_NONE;
+}
+
+/*
+ * Enable garbage collection of the specified object.
+ */
+static JdwpError handleOR_EnableCollection(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ // this is currently a no-op
+ return ERR_NONE;
+}
+
+/*
+ * Determine whether an object has been garbage collected.
+ */
+static JdwpError handleOR_IsCollected(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ ObjectId objectId;
+
+ objectId = dvmReadObjectId(&buf);
+
+ LOGV(" Req IsCollected(0x%llx)\n", objectId);
+
+ // TODO: currently returning false; must integrate with GC
+ expandBufAdd1(pReply, 0);
+
+ return ERR_NONE;
+}
+
+/*
+ * Return the string value in a string object.
+ */
+static JdwpError handleSR_Value(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ ObjectId stringObject;
+ char* str;
+
+ stringObject = dvmReadObjectId(&buf);
+ str = dvmDbgStringToUtf8(stringObject);
+
+ LOGV(" Req for str %llx --> '%s'\n", stringObject, str);
+
+ expandBufAddUtf8String(pReply, (u1*) str);
+ free(str);
+
+ return ERR_NONE;
+}
+
+/*
+ * Return a thread's name.
+ */
+static JdwpError handleTR_Name(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ ObjectId threadId;
+ char* name;
+
+ threadId = dvmReadObjectId(&buf);
+
+ LOGV(" Req for name of thread 0x%llx\n", threadId);
+ name = dvmDbgGetThreadName(threadId);
+ if (name == NULL)
+ return ERR_INVALID_THREAD;
+
+ expandBufAddUtf8String(pReply, (u1*) name);
+ free(name);
+
+ return ERR_NONE;
+}
+
+/*
+ * Suspend the specified thread.
+ *
+ * It's supposed to remain suspended even if interpreted code wants to
+ * resume it; only the JDI is allowed to resume it.
+ */
+static JdwpError handleTR_Suspend(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ ObjectId threadId;
+
+ threadId = dvmReadObjectId(&buf);
+
+ if (threadId == dvmDbgGetThreadSelfId()) {
+ LOGI(" Warning: ignoring request to suspend self\n");
+ return ERR_THREAD_NOT_SUSPENDED;
+ }
+ LOGV(" Req to suspend thread 0x%llx\n", threadId);
+
+ dvmDbgSuspendThread(threadId);
+
+ return ERR_NONE;
+}
+
+/*
+ * Resume the specified thread.
+ */
+static JdwpError handleTR_Resume(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ ObjectId threadId;
+
+ threadId = dvmReadObjectId(&buf);
+
+ if (threadId == dvmDbgGetThreadSelfId()) {
+ LOGI(" Warning: ignoring request to resume self\n");
+ return ERR_NONE;
+ }
+ LOGV(" Req to resume thread 0x%llx\n", threadId);
+
+ dvmDbgResumeThread(threadId);
+
+ return ERR_NONE;
+}
+
+/*
+ * Return status of specified thread.
+ */
+static JdwpError handleTR_Status(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ ObjectId threadId;
+ u4 threadStatus;
+ u4 suspendStatus;
+
+ threadId = dvmReadObjectId(&buf);
+
+ LOGV(" Req for status of thread 0x%llx\n", threadId);
+
+ if (!dvmDbgGetThreadStatus(threadId, &threadStatus, &suspendStatus))
+ return ERR_INVALID_THREAD;
+
+ LOGV(" --> %s, %s\n", dvmJdwpThreadStatusStr(threadStatus),
+ dvmJdwpSuspendStatusStr(suspendStatus));
+
+ expandBufAdd4BE(pReply, threadStatus);
+ expandBufAdd4BE(pReply, suspendStatus);
+
+ return ERR_NONE;
+}
+
+/*
+ * Return the thread group that the specified thread is a member of.
+ */
+static JdwpError handleTR_ThreadGroup(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ ObjectId threadId;
+ ObjectId threadGroupId;
+
+ threadId = dvmReadObjectId(&buf);
+
+ /* currently not handling these */
+ threadGroupId = dvmDbgGetThreadGroup(threadId);
+ expandBufAddObjectId(pReply, threadGroupId);
+
+ return ERR_NONE;
+}
+
+/*
+ * Return the current call stack of a suspended thread.
+ *
+ * If the thread isn't suspended, the error code isn't defined, but should
+ * be THREAD_NOT_SUSPENDED.
+ */
+static JdwpError handleTR_Frames(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ ObjectId threadId;
+ u4 startFrame, length, frames;
+ int i, frameCount;
+
+ threadId = dvmReadObjectId(&buf);
+ startFrame = read4BE(&buf);
+ length = read4BE(&buf);
+
+ if (!dvmDbgThreadExists(threadId))
+ return ERR_INVALID_THREAD;
+ if (!dvmDbgIsSuspended(threadId)) {
+ LOGV(" Rejecting req for frames in running thread '%s' (%llx)\n",
+ dvmDbgGetThreadName(threadId), threadId);
+ return ERR_THREAD_NOT_SUSPENDED;
+ }
+
+ frameCount = dvmDbgGetThreadFrameCount(threadId);
+
+ LOGV(" Request for frames: threadId=%llx start=%d length=%d [count=%d]\n",
+ threadId, startFrame, length, frameCount);
+ if (frameCount <= 0)
+ return ERR_THREAD_NOT_SUSPENDED; /* == 0 means 100% native */
+
+ if (length == (u4) -1)
+ length = frameCount;
+ assert((int) startFrame >= 0 && (int) startFrame < frameCount);
+ assert((int) (startFrame + length) <= frameCount);
+
+ frames = length;
+ expandBufAdd4BE(pReply, frames);
+ for (i = startFrame; i < (int) (startFrame+length); i++) {
+ FrameId frameId;
+ JdwpLocation loc;
+
+ dvmDbgGetThreadFrame(threadId, i, &frameId, &loc);
+
+ expandBufAdd8BE(pReply, frameId);
+ dvmJdwpAddLocation(pReply, &loc);
+
+ LOGVV(" Frame %d: id=%llx loc={type=%d cls=%llx mth=%x loc=%llx}\n",
+ i, frameId, loc.typeTag, loc.classId, loc.methodId, loc.idx);
+ }
+
+ return ERR_NONE;
+}
+
+/*
+ * Returns the #of frames on the specified thread, which must be suspended.
+ */
+static JdwpError handleTR_FrameCount(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ ObjectId threadId;
+ int frameCount;
+
+ threadId = dvmReadObjectId(&buf);
+
+ if (!dvmDbgThreadExists(threadId))
+ return ERR_INVALID_THREAD;
+ if (!dvmDbgIsSuspended(threadId)) {
+ LOGV(" Rejecting req for frames in running thread '%s' (%llx)\n",
+ dvmDbgGetThreadName(threadId), threadId);
+ return ERR_THREAD_NOT_SUSPENDED;
+ }
+
+ frameCount = dvmDbgGetThreadFrameCount(threadId);
+ if (frameCount < 0)
+ return ERR_INVALID_THREAD;
+ expandBufAdd4BE(pReply, (u4)frameCount);
+
+ return ERR_NONE;
+}
+
+/*
+ * Get the monitor that the thread is waiting on.
+ */
+static JdwpError handleTR_CurrentContendedMonitor(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ ObjectId threadId;
+
+ threadId = dvmReadObjectId(&buf);
+
+ // TODO: create an Object to represent the monitor (we're currently
+ // just using a raw Monitor struct in the VM)
+
+ return ERR_NOT_IMPLEMENTED;
+}
+
+/*
+ * Return the suspend count for the specified thread.
+ *
+ * (The thread *might* still be running -- it might not have examined
+ * its suspend count recently.)
+ */
+static JdwpError handleTR_SuspendCount(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ ObjectId threadId;
+ u4 suspendCount;
+
+ threadId = dvmReadObjectId(&buf);
+
+ suspendCount = dvmDbgGetThreadSuspendCount(threadId);
+ expandBufAdd4BE(pReply, suspendCount);
+
+ return ERR_NONE;
+}
+
+/*
+ * Return the name of a thread group.
+ *
+ * The Eclipse debugger recognizes "main" and "system" as special.
+ */
+static JdwpError handleTGR_Name(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ ObjectId threadGroupId;
+ char* name = NULL;
+
+ threadGroupId = dvmReadObjectId(&buf);
+ LOGV(" Req for name of threadGroupId=0x%llx\n", threadGroupId);
+
+ name = dvmDbgGetThreadGroupName(threadGroupId);
+ if (name != NULL)
+ expandBufAddUtf8String(pReply, (u1*) name);
+ else {
+ expandBufAddUtf8String(pReply, (u1*) "BAD-GROUP-ID");
+ LOGW("bad thread group ID\n");
+ }
+
+ free(name);
+
+ return ERR_NONE;
+}
+
+/*
+ * Returns the thread group -- if any -- that contains the specified
+ * thread group.
+ */
+static JdwpError handleTGR_Parent(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ ObjectId groupId;
+ ObjectId parentGroup;
+
+ groupId = dvmReadObjectId(&buf);
+
+ parentGroup = dvmDbgGetThreadGroupParent(groupId);
+ expandBufAddObjectId(pReply, parentGroup);
+
+ return ERR_NONE;
+}
+
+/*
+ * Return the active threads and thread groups that are part of the
+ * specified thread group.
+ */
+static JdwpError handleTGR_Children(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ ObjectId threadGroupId;
+ u4 threadCount;
+ ObjectId* pThreadIds;
+ ObjectId* walker;
+ int i;
+
+ threadGroupId = dvmReadObjectId(&buf);
+ LOGV(" Req for threads in threadGroupId=0x%llx\n", threadGroupId);
+
+ dvmDbgGetThreadGroupThreads(threadGroupId, &pThreadIds, &threadCount);
+
+ expandBufAdd4BE(pReply, threadCount);
+
+ walker = pThreadIds;
+ for (i = 0; i < (int) threadCount; i++)
+ expandBufAddObjectId(pReply, pThreadIds[i]);
+ free(pThreadIds);
+
+ /*
+ * TODO: finish support for child groups
+ *
+ * For now, just show that "main" is a child of "system".
+ */
+ if (threadGroupId == dvmDbgGetSystemThreadGroupId()) {
+ expandBufAdd4BE(pReply, 1);
+ expandBufAddObjectId(pReply, dvmDbgGetMainThreadGroupId());
+ } else {
+ expandBufAdd4BE(pReply, 0);
+ }
+
+ return ERR_NONE;
+}
+
+/*
+ * Return the #of components in the array.
+ */
+static JdwpError handleAR_Length(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ ObjectId arrayId;
+ u4 arrayLength;
+
+ arrayId = dvmReadObjectId(&buf);
+ LOGV(" Req for length of array 0x%llx\n", arrayId);
+
+ arrayLength = dvmDbgGetArrayLength(arrayId);
+
+ LOGV(" --> %d\n", arrayLength);
+
+ expandBufAdd4BE(pReply, arrayLength);
+
+ return ERR_NONE;
+}
+
+/*
+ * Return the values from an array.
+ */
+static JdwpError handleAR_GetValues(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ ObjectId arrayId;
+ u4 firstIndex;
+ u4 length;
+ u1 tag;
+
+ arrayId = dvmReadObjectId(&buf);
+ firstIndex = read4BE(&buf);
+ length = read4BE(&buf);
+
+ tag = dvmDbgGetArrayElementTag(arrayId);
+ LOGV(" Req for array values 0x%llx first=%d len=%d (elem tag=%c)\n",
+ arrayId, firstIndex, length, tag);
+
+ expandBufAdd1(pReply, tag);
+ expandBufAdd4BE(pReply, length);
+
+ if (!dvmDbgOutputArray(arrayId, firstIndex, length, pReply))
+ return ERR_INVALID_LENGTH;
+
+ return ERR_NONE;
+}
+
+/*
+ * Set values in an array.
+ */
+static JdwpError handleAR_SetValues(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ ObjectId arrayId;
+ u4 firstIndex;
+ u4 values;
+
+ arrayId = dvmReadObjectId(&buf);
+ firstIndex = read4BE(&buf);
+ values = read4BE(&buf);
+
+ LOGV(" Req to set array values 0x%llx first=%d count=%d\n",
+ arrayId, firstIndex, values);
+
+ if (!dvmDbgSetArrayElements(arrayId, firstIndex, values, buf))
+ return ERR_INVALID_LENGTH;
+
+ return ERR_NONE;
+}
+
+/*
+ * Return the set of classes visible to a class loader. All classes which
+ * have the class loader as a defining or initiating loader are returned.
+ */
+static JdwpError handleCLR_VisibleClasses(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ ObjectId classLoaderObject;
+ u4 numClasses = 0;
+ RefTypeId* classRefBuf = NULL;
+ int i;
+
+ classLoaderObject = dvmReadObjectId(&buf);
+
+ dvmDbgGetVisibleClassList(classLoaderObject, &numClasses, &classRefBuf);
+
+ expandBufAdd4BE(pReply, numClasses);
+ for (i = 0; i < (int) numClasses; i++) {
+ u1 refTypeTag;
+
+ refTypeTag = dvmDbgGetClassObjectType(classRefBuf[i]);
+
+ expandBufAdd1(pReply, refTypeTag);
+ expandBufAddRefTypeId(pReply, classRefBuf[i]);
+ }
+
+ return ERR_NONE;
+}
+
+/*
+ * Set an event trigger.
+ *
+ * Reply with a requestID.
+ */
+static JdwpError handleER_Set(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ JdwpEvent* pEvent;
+ JdwpError err;
+ const u1* origBuf = buf;
+ /*int origDataLen = dataLen;*/
+ u1 eventKind;
+ u1 suspendPolicy;
+ u4 modifierCount;
+ u4 requestId;
+ int idx;
+
+ eventKind = read1(&buf);
+ suspendPolicy = read1(&buf);
+ modifierCount = read4BE(&buf);
+
+ LOGVV(" Set(kind=%s(%u) suspend=%s(%u) mods=%u)\n",
+ dvmJdwpEventKindStr(eventKind), eventKind,
+ dvmJdwpSuspendPolicyStr(suspendPolicy), suspendPolicy,
+ modifierCount);
+
+ assert(modifierCount < 256); /* reasonableness check */
+
+ pEvent = dvmJdwpEventAlloc(modifierCount);
+ pEvent->eventKind = eventKind;
+ pEvent->suspendPolicy = suspendPolicy;
+ pEvent->modCount = modifierCount;
+
+ /*
+ * Read modifiers. Ordering may be significant (see explanation of Count
+ * mods in JDWP doc).
+ */
+ for (idx = 0; idx < (int) modifierCount; idx++) {
+ u1 modKind;
+
+ modKind = read1(&buf);
+
+ pEvent->mods[idx].modKind = modKind;
+
+ switch (modKind) {
+ case MK_COUNT: /* report once, when "--count" reaches 0 */
+ {
+ u4 count = read4BE(&buf);
+ LOGVV(" Count: %u\n", count);
+ if (count == 0)
+ return ERR_INVALID_COUNT;
+ pEvent->mods[idx].count.count = count;
+ }
+ break;
+ case MK_CONDITIONAL: /* conditional on expression) */
+ {
+ u4 exprId = read4BE(&buf);
+ LOGVV(" Conditional: %d\n", exprId);
+ pEvent->mods[idx].conditional.exprId = exprId;
+ }
+ break;
+ case MK_THREAD_ONLY: /* only report events in specified thread */
+ {
+ ObjectId threadId = dvmReadObjectId(&buf);
+ LOGVV(" ThreadOnly: %llx\n", threadId);
+ pEvent->mods[idx].threadOnly.threadId = threadId;
+ }
+ break;
+ case MK_CLASS_ONLY: /* for ClassPrepare, MethodEntry */
+ {
+ RefTypeId clazzId = dvmReadRefTypeId(&buf);
+ LOGVV(" ClassOnly: %llx (%s)\n",
+ clazzId, dvmDbgGetClassDescriptor(clazzId));
+ pEvent->mods[idx].classOnly.referenceTypeId = clazzId;
+ }
+ break;
+ case MK_CLASS_MATCH: /* restrict events to matching classes */
+ {
+ char* pattern;
+ size_t strLen;
+
+ pattern = readNewUtf8String(&buf, &strLen);
+ LOGVV(" ClassMatch: '%s'\n", pattern);
+ /* pattern is "java.foo.*", we want "java/foo/ *" */
+ pEvent->mods[idx].classMatch.classPattern =
+ dvmDotToSlash(pattern);
+ free(pattern);
+ }
+ break;
+ case MK_CLASS_EXCLUDE: /* restrict events to non-matching classes */
+ {
+ char* pattern;
+ size_t strLen;
+
+ pattern = readNewUtf8String(&buf, &strLen);
+ LOGVV(" ClassExclude: '%s'\n", pattern);
+ pEvent->mods[idx].classExclude.classPattern =
+ dvmDotToSlash(pattern);
+ free(pattern);
+ }
+ break;
+ case MK_LOCATION_ONLY: /* restrict certain events based on loc */
+ {
+ JdwpLocation loc;
+
+ jdwpReadLocation(&buf, &loc);
+ LOGVV(" LocationOnly: typeTag=%d classId=%llx methodId=%x idx=%llx\n",
+ loc.typeTag, loc.classId, loc.methodId, loc.idx);
+ pEvent->mods[idx].locationOnly.loc = loc;
+ }
+ break;
+ case MK_EXCEPTION_ONLY: /* modifies EK_EXCEPTION events */
+ {
+ RefTypeId exceptionOrNull; /* null == all exceptions */
+ u1 caught, uncaught;
+
+ exceptionOrNull = dvmReadRefTypeId(&buf);
+ caught = read1(&buf);
+ uncaught = read1(&buf);
+ LOGVV(" ExceptionOnly: type=%llx(%s) caught=%d uncaught=%d\n",
+ exceptionOrNull, (exceptionOrNull == 0) ? "null"
+ : dvmDbgGetClassDescriptor(exceptionOrNull),
+ caught, uncaught);
+
+ pEvent->mods[idx].exceptionOnly.refTypeId = exceptionOrNull;
+ pEvent->mods[idx].exceptionOnly.caught = caught;
+ pEvent->mods[idx].exceptionOnly.uncaught = uncaught;
+ }
+ break;
+ case MK_FIELD_ONLY: /* for field access/mod events */
+ {
+ RefTypeId declaring = dvmReadRefTypeId(&buf);
+ FieldId fieldId = dvmReadFieldId(&buf);
+ LOGVV(" FieldOnly: %llx %x\n", declaring, fieldId);
+ pEvent->mods[idx].fieldOnly.refTypeId = declaring;
+ pEvent->mods[idx].fieldOnly.fieldId = fieldId;;
+ }
+ break;
+ case MK_STEP: /* for use with EK_SINGLE_STEP */
+ {
+ ObjectId threadId;
+ u4 size, depth;
+
+ threadId = dvmReadObjectId(&buf);
+ size = read4BE(&buf);
+ depth = read4BE(&buf);
+ LOGVV(" Step: thread=%llx size=%s depth=%s\n",
+ threadId, dvmJdwpStepSizeStr(size),
+ dvmJdwpStepDepthStr(depth));
+
+ pEvent->mods[idx].step.threadId = threadId;
+ pEvent->mods[idx].step.size = size;
+ pEvent->mods[idx].step.depth = depth;
+ }
+ break;
+ case MK_INSTANCE_ONLY: /* report events related to a specific obj */
+ {
+ ObjectId instance = dvmReadObjectId(&buf);
+ LOGVV(" InstanceOnly: %llx\n", instance);
+ pEvent->mods[idx].instanceOnly.objectId = instance;
+ }
+ break;
+ default:
+ LOGW("GLITCH: unsupported modKind=%d\n", modKind);
+ break;
+ }
+ }
+
+ /*
+ * Make sure we consumed all data. It is possible that the remote side
+ * has sent us bad stuff, but for now we blame ourselves.
+ */
+ if (buf != origBuf + dataLen) {
+ LOGW("GLITCH: dataLen is %d, we have consumed %d\n", dataLen,
+ (int) (buf - origBuf));
+ }
+
+ /*
+ * We reply with an integer "requestID".
+ */
+ requestId = dvmJdwpNextEventSerial(state);
+ expandBufAdd4BE(pReply, requestId);
+
+ pEvent->requestId = requestId;
+
+ LOGV(" --> event requestId=0x%x\n", requestId);
+
+ /* add it to the list */
+ err = dvmJdwpRegisterEvent(state, pEvent);
+ if (err != ERR_NONE) {
+ /* registration failed, probably because event is bogus */
+ dvmJdwpEventFree(pEvent);
+ LOGW("WARNING: event request rejected\n");
+ }
+ return err;
+}
+
+/*
+ * Clear an event. Failure to find an event with a matching ID is a no-op
+ * and does not return an error.
+ */
+static JdwpError handleER_Clear(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ u1 eventKind;
+ u4 requestId;
+
+ eventKind = read1(&buf);
+ requestId = read4BE(&buf);
+
+ LOGV(" Req to clear eventKind=%d requestId=0x%08x\n", eventKind,requestId);
+
+ dvmJdwpUnregisterEventById(state, requestId);
+
+ return ERR_NONE;
+}
+
+/*
+ * Return the values of arguments and local variables.
+ */
+static JdwpError handleSF_GetValues(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ ObjectId threadId;
+ FrameId frameId;
+ u4 slots;
+ int i;
+
+ threadId = dvmReadObjectId(&buf);
+ frameId = dvmReadFrameId(&buf);
+ slots = read4BE(&buf);
+
+ LOGV(" Req for %d slots in threadId=%llx frameId=%llx\n",
+ slots, threadId, frameId);
+
+ expandBufAdd4BE(pReply, slots); /* "int values" */
+ for (i = 0; i < (int) slots; i++) {
+ u4 slot;
+ u1 reqSigByte;
+ int width;
+ u1* ptr;
+
+ slot = read4BE(&buf);
+ reqSigByte = read1(&buf);
+
+ LOGV(" --> slot %d '%c'\n", slot, reqSigByte);
+
+ width = dvmDbgGetTagWidth(reqSigByte);
+ ptr = expandBufAddSpace(pReply, width+1);
+ dvmDbgGetLocalValue(threadId, frameId, slot, reqSigByte, ptr, width);
+ }
+
+ return ERR_NONE;
+}
+
+/*
+ * Set the values of arguments and local variables.
+ */
+static JdwpError handleSF_SetValues(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ ObjectId threadId;
+ FrameId frameId;
+ u4 slots;
+ int i;
+
+ threadId = dvmReadObjectId(&buf);
+ frameId = dvmReadFrameId(&buf);
+ slots = read4BE(&buf);
+
+ LOGV(" Req to set %d slots in threadId=%llx frameId=%llx\n",
+ slots, threadId, frameId);
+
+ for (i = 0; i < (int) slots; i++) {
+ u4 slot;
+ u1 sigByte;
+ u8 value;
+ int width;
+
+ slot = read4BE(&buf);
+ sigByte = read1(&buf);
+ width = dvmDbgGetTagWidth(sigByte);
+ value = jdwpReadValue(&buf, width);
+
+ LOGV(" --> slot %d '%c' %llx\n", slot, sigByte, value);
+ dvmDbgSetLocalValue(threadId, frameId, slot, sigByte, value, width);
+ }
+
+ return ERR_NONE;
+}
+
+/*
+ * Returns the value of "this" for the specified frame.
+ */
+static JdwpError handleSF_ThisObject(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ ObjectId threadId;
+ FrameId frameId;
+ u1 objectTag;
+ ObjectId objectId;
+ char* typeName;
+
+ threadId = dvmReadObjectId(&buf);
+ frameId = dvmReadFrameId(&buf);
+
+ if (!dvmDbgGetThisObject(threadId, frameId, &objectId))
+ return ERR_INVALID_FRAMEID;
+
+ if (objectId == 0) {
+ typeName = strdup("null");
+ objectTag = 0;
+ } else {
+ typeName = dvmDbgGetObjectTypeName(objectId);
+ objectTag = dvmDbgGetObjectTag(objectId, typeName);
+ }
+ LOGV(" Req for 'this' in thread=%llx frame=%llx --> %llx %s '%c'\n",
+ threadId, frameId, objectId, typeName, (char)objectTag);
+ free(typeName);
+
+ expandBufAdd1(pReply, objectTag);
+ expandBufAddObjectId(pReply, objectId);
+
+ return ERR_NONE;
+}
+
+/*
+ * Return the reference type reflected by this class object.
+ *
+ * This appears to be required because ReferenceTypeId values are NEVER
+ * reused, whereas ClassIds can be recycled like any other object. (Either
+ * that, or I have no idea what this is for.)
+ */
+static JdwpError handleCOR_ReflectedType(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ RefTypeId classObjectId;
+
+ classObjectId = dvmReadRefTypeId(&buf);
+
+ LOGV(" Req for refTypeId for class=%llx (%s)\n",
+ classObjectId, dvmDbgGetClassDescriptor(classObjectId));
+
+ /* just hand the type back to them */
+ if (dvmDbgIsInterface(classObjectId))
+ expandBufAdd1(pReply, TT_INTERFACE);
+ else
+ expandBufAdd1(pReply, TT_CLASS);
+ expandBufAddRefTypeId(pReply, classObjectId);
+
+ return ERR_NONE;
+}
+
+/*
+ * Handle a DDM packet with a single chunk in it.
+ */
+static JdwpError handleDDM_Chunk(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ u1* replyBuf = NULL;
+ int replyLen = -1;
+
+ LOGV(" Handling DDM packet (%.4s)\n", buf);
+
+ /*
+ * On first DDM packet, notify all handlers that DDM is running.
+ */
+ if (!state->ddmActive) {
+ state->ddmActive = true;
+ dvmDbgDdmConnected();
+ }
+
+ /*
+ * If they want to send something back, we copy it into the buffer.
+ * A no-copy approach would be nicer.
+ *
+ * TODO: consider altering the JDWP stuff to hold the packet header
+ * in a separate buffer. That would allow us to writev() DDM traffic
+ * instead of copying it into the expanding buffer. The reduction in
+ * heap requirements is probably more valuable than the efficiency.
+ */
+ if (dvmDbgDdmHandlePacket(buf, dataLen, &replyBuf, &replyLen)) {
+ assert(replyLen > 0 && replyLen < 1*1024*1024);
+ memcpy(expandBufAddSpace(pReply, replyLen), replyBuf, replyLen);
+ free(replyBuf);
+ }
+ return ERR_NONE;
+}
+
+/*
+ * Handler map decl.
+ */
+typedef JdwpError (*JdwpRequestHandler)(JdwpState* state,
+ const u1* buf, int dataLen, ExpandBuf* reply);
+
+typedef struct {
+ u1 cmdSet;
+ u1 cmd;
+ JdwpRequestHandler func;
+ const char* descr;
+} JdwpHandlerMap;
+
+/*
+ * Map commands to functions.
+ *
+ * Command sets 0-63 are incoming requests, 64-127 are outbound requests,
+ * and 128-256 are vendor-defined.
+ */
+static const JdwpHandlerMap gHandlerMap[] = {
+ /* VirtualMachine command set (1) */
+ { 1, 1, handleVM_Version, "VirtualMachine.Version" },
+ { 1, 2, handleVM_ClassesBySignature,
+ "VirtualMachine.ClassesBySignature" },
+ //1, 3, VirtualMachine.AllClasses
+ { 1, 4, handleVM_AllThreads, "VirtualMachine.AllThreads" },
+ { 1, 5, handleVM_TopLevelThreadGroups,
+ "VirtualMachine.TopLevelThreadGroups" },
+ { 1, 6, handleVM_Dispose, "VirtualMachine.Dispose" },
+ { 1, 7, handleVM_IDSizes, "VirtualMachine.IDSizes" },
+ { 1, 8, handleVM_Suspend, "VirtualMachine.Suspend" },
+ { 1, 9, handleVM_Resume, "VirtualMachine.Resume" },
+ { 1, 10, handleVM_Exit, "VirtualMachine.Exit" },
+ { 1, 11, handleVM_CreateString, "VirtualMachine.CreateString" },
+ { 1, 12, handleVM_Capabilities, "VirtualMachine.Capabilities" },
+ { 1, 13, handleVM_ClassPaths, "VirtualMachine.ClassPaths" },
+ { 1, 14, HandleVM_DisposeObjects, "VirtualMachine.DisposeObjects" },
+ //1, 15, HoldEvents
+ //1, 16, ReleaseEvents
+ { 1, 17, handleVM_CapabilitiesNew,
+ "VirtualMachine.CapabilitiesNew" },
+ //1, 18, RedefineClasses
+ //1, 19, SetDefaultStratum
+ { 1, 20, handleVM_AllClassesWithGeneric,
+ "VirtualMachine.AllClassesWithGeneric"},
+ //1, 21, InstanceCounts
+
+ /* ReferenceType command set (2) */
+ { 2, 1, handleRT_Signature, "ReferenceType.Signature" },
+ { 2, 2, handleRT_ClassLoader, "ReferenceType.ClassLoader" },
+ { 2, 3, handleRT_Modifiers, "ReferenceType.Modifiers" },
+ //2, 4, Fields
+ //2, 5, Methods
+ { 2, 6, handleRT_GetValues, "ReferenceType.GetValues" },
+ { 2, 7, handleRT_SourceFile, "ReferenceType.SourceFile" },
+ //2, 8, NestedTypes
+ { 2, 9, handleRT_Status, "ReferenceType.Status" },
+ { 2, 10, handleRT_Interfaces, "ReferenceType.Interfaces" },
+ { 2, 11, handleRT_ClassObject, "ReferenceType.ClassObject" },
+ { 2, 12, handleRT_SourceDebugExtension,
+ "ReferenceType.SourceDebugExtension" },
+ { 2, 13, handleRT_SignatureWithGeneric,
+ "ReferenceType.SignatureWithGeneric" },
+ { 2, 14, handleRT_FieldsWithGeneric,
+ "ReferenceType.FieldsWithGeneric" },
+ { 2, 15, handleRT_MethodsWithGeneric,
+ "ReferenceType.MethodsWithGeneric" },
+ //2, 16, Instances
+ //2, 17, ClassFileVersion
+ //2, 18, ConstantPool
+
+ /* ClassType command set (3) */
+ { 3, 1, handleCT_Superclass, "ClassType.Superclass" },
+ { 3, 2, handleCT_SetValues, "ClassType.SetValues" },
+ { 3, 3, handleCT_InvokeMethod, "ClassType.InvokeMethod" },
+ { 3, 4, handleCT_NewInstance, "ClassType.NewInstance" },
+
+ /* ArrayType command set (4) */
+ { 4, 1, handleAT_newInstance, "ArrayType.NewInstance" },
+
+ /* InterfaceType command set (5) */
+
+ /* Method command set (6) */
+ { 6, 1, handleM_LineTable, "Method.LineTable" },
+ //6, 2, VariableTable
+ //6, 3, Bytecodes
+ //6, 4, IsObsolete
+ { 6, 5, handleM_VariableTableWithGeneric,
+ "Method.VariableTableWithGeneric" },
+
+ /* Field command set (8) */
+
+ /* ObjectReference command set (9) */
+ { 9, 1, handleOR_ReferenceType, "ObjectReference.ReferenceType" },
+ { 9, 2, handleOR_GetValues, "ObjectReference.GetValues" },
+ { 9, 3, handleOR_SetValues, "ObjectReference.SetValues" },
+ //9, 4, (not defined)
+ //9, 5, MonitorInfo
+ { 9, 6, handleOR_InvokeMethod, "ObjectReference.InvokeMethod" },
+ { 9, 7, handleOR_DisableCollection,
+ "ObjectReference.DisableCollection" },
+ { 9, 8, handleOR_EnableCollection,
+ "ObjectReference.EnableCollection" },
+ { 9, 9, handleOR_IsCollected, "ObjectReference.IsCollected" },
+ //9, 10, ReferringObjects
+
+ /* StringReference command set (10) */
+ { 10, 1, handleSR_Value, "StringReference.Value" },
+
+ /* ThreadReference command set (11) */
+ { 11, 1, handleTR_Name, "ThreadReference.Name" },
+ { 11, 2, handleTR_Suspend, "ThreadReference.Suspend" },
+ { 11, 3, handleTR_Resume, "ThreadReference.Resume" },
+ { 11, 4, handleTR_Status, "ThreadReference.Status" },
+ { 11, 5, handleTR_ThreadGroup, "ThreadReference.ThreadGroup" },
+ { 11, 6, handleTR_Frames, "ThreadReference.Frames" },
+ { 11, 7, handleTR_FrameCount, "ThreadReference.FrameCount" },
+ //11, 8, OwnedMonitors
+ { 11, 9, handleTR_CurrentContendedMonitor,
+ "ThreadReference.CurrentContendedMonitor" },
+ //11, 10, Stop
+ //11, 11, Interrupt
+ { 11, 12, handleTR_SuspendCount, "ThreadReference.SuspendCount" },
+ //11, 13, OwnedMonitorsStackDepthInfo
+ //11, 14, ForceEarlyReturn
+
+ /* ThreadGroupReference command set (12) */
+ { 12, 1, handleTGR_Name, "ThreadGroupReference.Name" },
+ { 12, 2, handleTGR_Parent, "ThreadGroupReference.Parent" },
+ { 12, 3, handleTGR_Children, "ThreadGroupReference.Children" },
+
+ /* ArrayReference command set (13) */
+ { 13, 1, handleAR_Length, "ArrayReference.Length" },
+ { 13, 2, handleAR_GetValues, "ArrayReference.GetValues" },
+ { 13, 3, handleAR_SetValues, "ArrayReference.SetValues" },
+
+ /* ClassLoaderReference command set (14) */
+ { 14, 1, handleCLR_VisibleClasses,
+ "ClassLoaderReference.VisibleClasses" },
+
+ /* EventRequest command set (15) */
+ { 15, 1, handleER_Set, "EventRequest.Set" },
+ { 15, 2, handleER_Clear, "EventRequest.Clear" },
+ //15, 3, ClearAllBreakpoints
+
+ /* StackFrame command set (16) */
+ { 16, 1, handleSF_GetValues, "StackFrame.GetValues" },
+ { 16, 2, handleSF_SetValues, "StackFrame.SetValues" },
+ { 16, 3, handleSF_ThisObject, "StackFrame.ThisObject" },
+ //16, 4, PopFrames
+
+ /* ClassObjectReference command set (17) */
+ { 17, 1, handleCOR_ReflectedType,"ClassObjectReference.ReflectedType" },
+
+ /* Event command set (64) */
+ //64, 100, Composite <-- sent from VM to debugger, never received by VM
+
+ { 199, 1, handleDDM_Chunk, "DDM.Chunk" },
+};
+
+
+/*
+ * Process a request from the debugger.
+ *
+ * On entry, the JDWP thread is in VMWAIT.
+ */
+void dvmJdwpProcessRequest(JdwpState* state, const JdwpReqHeader* pHeader,
+ const u1* buf, int dataLen, ExpandBuf* pReply)
+{
+ JdwpError result = ERR_NONE;
+ int i, respLen;
+
+ if (pHeader->cmdSet != kJDWPDdmCmdSet) {
+ /*
+ * Activity from a debugger, not merely ddms. Mark us as having an
+ * active debugger session, and zero out the last-activity timestamp
+ * so waitForDebugger() doesn't return if we stall for a bit here.
+ */
+ dvmDbgActive();
+ dvmQuasiAtomicSwap64(0, &state->lastActivityWhen);
+ }
+
+ /*
+ * If a debugger event has fired in another thread, wait until the
+ * initiating thread has suspended itself before processing messages
+ * from the debugger. Otherwise we (the JDWP thread) could be told to
+ * resume the thread before it has suspended.
+ *
+ * We call with an argument of zero to wait for the current event
+ * thread to finish, and then clear the block. Depending on the thread
+ * suspend policy, this may allow events in other threads to fire,
+ * but those events have no bearing on what the debugger has sent us
+ * in the current request.
+ *
+ * Note that we MUST clear the event token before waking the event
+ * thread up, or risk waiting for the thread to suspend after we've
+ * told it to resume.
+ */
+ dvmJdwpSetWaitForEventThread(state, 0);
+
+ /*
+ * Tell the VM that we're running and shouldn't be interrupted by GC.
+ * Do this after anything that can stall indefinitely.
+ */
+ dvmDbgThreadRunning();
+
+ expandBufAddSpace(pReply, kJDWPHeaderLen);
+
+ for (i = 0; i < (int) NELEM(gHandlerMap); i++) {
+ if (gHandlerMap[i].cmdSet == pHeader->cmdSet &&
+ gHandlerMap[i].cmd == pHeader->cmd)
+ {
+ LOGV("REQ: %s (cmd=%d/%d dataLen=%d id=0x%06x)\n",
+ gHandlerMap[i].descr, pHeader->cmdSet, pHeader->cmd,
+ dataLen, pHeader->id);
+ result = (*gHandlerMap[i].func)(state, buf, dataLen, pReply);
+ break;
+ }
+ }
+ if (i == NELEM(gHandlerMap)) {
+ LOGE("REQ: UNSUPPORTED (cmd=%d/%d dataLen=%d id=0x%06x)\n",
+ pHeader->cmdSet, pHeader->cmd, dataLen, pHeader->id);
+ if (dataLen > 0)
+ dvmPrintHexDumpDbg(buf, dataLen, LOG_TAG);
+ assert(!"command not implemented"); // make it *really* obvious
+ result = ERR_NOT_IMPLEMENTED;
+ }
+
+ /*
+ * Set up the reply header.
+ *
+ * If we encountered an error, only send the header back.
+ */
+ u1* replyBuf = expandBufGetBuffer(pReply);
+ set4BE(replyBuf + 4, pHeader->id);
+ set1(replyBuf + 8, kJDWPFlagReply);
+ set2BE(replyBuf + 9, result);
+ if (result == ERR_NONE)
+ set4BE(replyBuf + 0, expandBufGetLength(pReply));
+ else
+ set4BE(replyBuf + 0, kJDWPHeaderLen);
+
+ respLen = expandBufGetLength(pReply) - kJDWPHeaderLen;
+ IF_LOG(LOG_VERBOSE, LOG_TAG) {
+ LOGV("reply: dataLen=%d err=%s(%d)%s\n", respLen,
+ dvmJdwpErrorStr(result), result,
+ result != ERR_NONE ? " **FAILED**" : "");
+ if (respLen > 0)
+ dvmPrintHexDumpDbg(expandBufGetBuffer(pReply) + kJDWPHeaderLen,
+ respLen, LOG_TAG);
+ }
+
+ /*
+ * Update last-activity timestamp. We really only need this during
+ * the initial setup. Only update if this is a non-DDMS packet.
+ */
+ if (pHeader->cmdSet != kJDWPDdmCmdSet) {
+ dvmQuasiAtomicSwap64(dvmJdwpGetNowMsec(), &state->lastActivityWhen);
+ }
+
+ /* tell the VM that GC is okay again */
+ dvmDbgThreadWaiting();
+}
diff --git a/vm/jdwp/JdwpHandler.h b/vm/jdwp/JdwpHandler.h
new file mode 100644
index 0000000..3a7a98c
--- /dev/null
+++ b/vm/jdwp/JdwpHandler.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Handle requests.
+ */
+#ifndef _DALVIK_JDWP_JDWPHANDLER
+#define _DALVIK_JDWP_JDWPHANDLER
+
+#include "Common.h"
+#include "ExpandBuf.h"
+
+/*
+ * JDWP message header for a request.
+ */
+typedef struct JdwpReqHeader {
+ u4 length;
+ u4 id;
+ u1 cmdSet;
+ u1 cmd;
+} JdwpReqHeader;
+
+/*
+ * Process a request from the debugger.
+ *
+ * "buf" points past the header, to the content of the message. "dataLen"
+ * can therefore be zero.
+ */
+void dvmJdwpProcessRequest(JdwpState* state, const JdwpReqHeader* pHeader,
+ const u1* buf, int dataLen, ExpandBuf* pReply);
+
+/* helper function */
+void dvmJdwpAddLocation(ExpandBuf* pReply, const JdwpLocation* pLoc);
+
+#endif /*_DALVIK_JDWP_JDWPHANDLER*/
diff --git a/vm/jdwp/JdwpMain.c b/vm/jdwp/JdwpMain.c
new file mode 100644
index 0000000..24e5c6c
--- /dev/null
+++ b/vm/jdwp/JdwpMain.c
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * JDWP initialization.
+ */
+#include "jdwp/JdwpPriv.h"
+#include "Dalvik.h"
+#include "Atomic.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <time.h>
+#include <errno.h>
+
+
+static void* jdwpThreadStart(void* arg);
+
+
+/*
+ * Initialize JDWP.
+ *
+ * Does not return until JDWP thread is running, but may return before
+ * the thread is accepting network connections.
+ */
+JdwpState* dvmJdwpStartup(const JdwpStartupParams* pParams)
+{
+ JdwpState* state = NULL;
+
+ /* comment this out when debugging JDWP itself */
+ android_setMinPriority(LOG_TAG, ANDROID_LOG_DEBUG);
+
+ state = (JdwpState*) calloc(1, sizeof(JdwpState));
+
+ state->params = *pParams;
+
+ state->requestSerial = 0x10000000;
+ state->eventSerial = 0x20000000;
+ dvmDbgInitMutex(&state->threadStartLock);
+ dvmDbgInitMutex(&state->attachLock);
+ dvmDbgInitMutex(&state->serialLock);
+ dvmDbgInitMutex(&state->eventLock);
+ state->eventThreadId = 0;
+ dvmDbgInitMutex(&state->eventThreadLock);
+ dvmDbgInitCond(&state->threadStartCond);
+ dvmDbgInitCond(&state->attachCond);
+ dvmDbgInitCond(&state->eventThreadCond);
+
+ switch (pParams->transport) {
+ case kJdwpTransportSocket:
+ // LOGD("prepping for JDWP over TCP\n");
+ state->transport = dvmJdwpSocketTransport();
+ break;
+ case kJdwpTransportAndroidAdb:
+ // LOGD("prepping for JDWP over ADB\n");
+ state->transport = dvmJdwpAndroidAdbTransport();
+ /* TODO */
+ break;
+ default:
+ LOGE("Unknown transport %d\n", pParams->transport);
+ assert(false);
+ goto fail;
+ }
+
+ if (!dvmJdwpNetStartup(state, pParams))
+ goto fail;
+
+ /*
+ * Grab a mutex or two before starting the thread. This ensures they
+ * won't signal the cond var before we're waiting.
+ */
+ dvmDbgLockMutex(&state->threadStartLock);
+ if (pParams->suspend)
+ dvmDbgLockMutex(&state->attachLock);
+
+ /*
+ * We have bound to a port, or are trying to connect outbound to a
+ * debugger. Create the JDWP thread and let it continue the mission.
+ */
+ if (!dvmCreateInternalThread(&state->debugThreadHandle, "JDWP",
+ jdwpThreadStart, state))
+ {
+ /* state is getting tossed, but unlock these anyway for cleanliness */
+ dvmDbgUnlockMutex(&state->threadStartLock);
+ if (pParams->suspend)
+ dvmDbgUnlockMutex(&state->attachLock);
+ goto fail;
+ }
+
+ /*
+ * Wait until the thread finishes basic initialization.
+ * TODO: cond vars should be waited upon in a loop
+ */
+ dvmDbgCondWait(&state->threadStartCond, &state->threadStartLock);
+ dvmDbgUnlockMutex(&state->threadStartLock);
+
+
+ /*
+ * For suspend=y, wait for the debugger to connect to us or for us to
+ * connect to the debugger.
+ *
+ * The JDWP thread will signal us when it connects successfully or
+ * times out (for timeout=xxx), so we have to check to see what happened
+ * when we wake up.
+ */
+ if (pParams->suspend) {
+ dvmChangeStatus(NULL, THREAD_VMWAIT);
+ dvmDbgCondWait(&state->attachCond, &state->attachLock);
+ dvmDbgUnlockMutex(&state->attachLock);
+ dvmChangeStatus(NULL, THREAD_RUNNING);
+
+ if (!dvmJdwpIsActive(state)) {
+ LOGE("JDWP connection failed\n");
+ goto fail;
+ }
+
+ LOGI("JDWP connected\n");
+
+ /*
+ * Ordinarily we would pause briefly to allow the debugger to set
+ * breakpoints and so on, but for "suspend=y" the VM init code will
+ * pause the VM when it sends the VM_START message.
+ */
+ }
+
+ return state;
+
+fail:
+ dvmJdwpShutdown(state); // frees state
+ return NULL;
+}
+
+/*
+ * Reset all session-related state. There should not be an active connection
+ * to the client at this point. The rest of the VM still thinks there is
+ * a debugger attached.
+ *
+ * This includes freeing up the debugger event list.
+ */
+void dvmJdwpResetState(JdwpState* state)
+{
+ /* could reset the serial numbers, but no need to */
+
+ dvmJdwpUnregisterAll(state);
+ assert(state->eventList == NULL);
+
+ /*
+ * Should not have one of these in progress. If the debugger went away
+ * mid-request, though, we could see this.
+ */
+ if (state->eventThreadId != 0) {
+ LOGW("WARNING: resetting state while event in progress\n");
+ assert(false);
+ }
+}
+
+/*
+ * Tell the JDWP thread to shut down. Frees "state".
+ */
+void dvmJdwpShutdown(JdwpState* state)
+{
+ void* threadReturn;
+
+ if (state == NULL)
+ return;
+
+ if (dvmJdwpIsTransportDefined(state)) {
+ if (dvmJdwpIsConnected(state))
+ dvmJdwpPostVMDeath(state);
+
+ /*
+ * Close down the network to inspire the thread to halt.
+ */
+ if (gDvm.verboseShutdown)
+ LOGD("JDWP shutting down net...\n");
+ dvmJdwpNetShutdown(state);
+
+ if (state->debugThreadStarted) {
+ state->run = false;
+ if (pthread_join(state->debugThreadHandle, &threadReturn) != 0) {
+ LOGW("JDWP thread join failed\n");
+ }
+ }
+
+ if (gDvm.verboseShutdown)
+ LOGD("JDWP freeing netstate...\n");
+ dvmJdwpNetFree(state);
+ state->netState = NULL;
+ }
+ assert(state->netState == NULL);
+
+ dvmJdwpResetState(state);
+ free(state);
+}
+
+/*
+ * Are we talking to a debugger?
+ */
+bool dvmJdwpIsActive(JdwpState* state)
+{
+ return dvmJdwpIsConnected(state);
+}
+
+/*
+ * Entry point for JDWP thread. The thread was created through the VM
+ * mechanisms, so there is a java/lang/Thread associated with us.
+ */
+static void* jdwpThreadStart(void* arg)
+{
+ JdwpState* state = (JdwpState*) arg;
+
+ LOGV("JDWP: thread running\n");
+
+ /*
+ * Finish initializing "state", then notify the creating thread that
+ * we're running.
+ */
+ state->debugThreadHandle = dvmThreadSelf()->handle;
+ state->run = true;
+ android_atomic_release_store(true, &state->debugThreadStarted);
+
+ dvmDbgLockMutex(&state->threadStartLock);
+ dvmDbgCondBroadcast(&state->threadStartCond);
+ dvmDbgUnlockMutex(&state->threadStartLock);
+
+ /* set the thread state to VMWAIT so GCs don't wait for us */
+ dvmDbgThreadWaiting();
+
+ /*
+ * Loop forever if we're in server mode, processing connections. In
+ * non-server mode, we bail out of the thread when the debugger drops
+ * us.
+ *
+ * We broadcast a notification when a debugger attaches, after we
+ * successfully process the handshake.
+ */
+ while (state->run) {
+ bool first;
+
+ if (state->params.server) {
+ /*
+ * Block forever, waiting for a connection. To support the
+ * "timeout=xxx" option we'll need to tweak this.
+ */
+ if (!dvmJdwpAcceptConnection(state))
+ break;
+ } else {
+ /*
+ * If we're not acting as a server, we need to connect out to the
+ * debugger. To support the "timeout=xxx" option we need to
+ * have a timeout if the handshake reply isn't received in a
+ * reasonable amount of time.
+ */
+ if (!dvmJdwpEstablishConnection(state)) {
+ /* wake anybody who was waiting for us to succeed */
+ dvmDbgLockMutex(&state->attachLock);
+ dvmDbgCondBroadcast(&state->attachCond);
+ dvmDbgUnlockMutex(&state->attachLock);
+ break;
+ }
+ }
+
+ /* prep debug code to handle the new connection */
+ dvmDbgConnected();
+
+ /* process requests until the debugger drops */
+ first = true;
+ while (true) {
+ // sanity check -- shouldn't happen?
+ if (dvmThreadSelf()->status != THREAD_VMWAIT) {
+ LOGE("JDWP thread no longer in VMWAIT (now %d); resetting\n",
+ dvmThreadSelf()->status);
+ dvmDbgThreadWaiting();
+ }
+
+ if (!dvmJdwpProcessIncoming(state)) /* blocking read */
+ break;
+
+ if (first && !dvmJdwpAwaitingHandshake(state)) {
+ /* handshake worked, tell the interpreter that we're active */
+ first = false;
+
+ /* set thread ID; requires object registry to be active */
+ state->debugThreadId = dvmDbgGetThreadSelfId();
+
+ /* wake anybody who's waiting for us */
+ dvmDbgLockMutex(&state->attachLock);
+ dvmDbgCondBroadcast(&state->attachCond);
+ dvmDbgUnlockMutex(&state->attachLock);
+ }
+ }
+
+ dvmJdwpCloseConnection(state);
+
+ if (state->ddmActive) {
+ state->ddmActive = false;
+
+ /* broadcast the disconnect; must be in RUNNING state */
+ dvmDbgThreadRunning();
+ dvmDbgDdmDisconnected();
+ dvmDbgThreadWaiting();
+ }
+
+ /* release session state, e.g. remove breakpoint instructions */
+ dvmJdwpResetState(state);
+
+ /* tell the interpreter that the debugger is no longer around */
+ dvmDbgDisconnected();
+
+ /* if we had threads suspended, resume them now */
+ dvmUndoDebuggerSuspensions();
+
+ /* if we connected out, this was a one-shot deal */
+ if (!state->params.server)
+ state->run = false;
+ }
+
+ /* back to running, for thread shutdown */
+ dvmDbgThreadRunning();
+
+ LOGV("JDWP: thread exiting\n");
+ return NULL;
+}
+
+
+/*
+ * Return the thread handle, or (pthread_t)0 if the debugger isn't running.
+ */
+pthread_t dvmJdwpGetDebugThread(JdwpState* state)
+{
+ if (state == NULL)
+ return 0;
+
+ return state->debugThreadHandle;
+}
+
+
+/*
+ * Support routines for waitForDebugger().
+ *
+ * We can't have a trivial "waitForDebugger" function that returns the
+ * instant the debugger connects, because we run the risk of executing code
+ * before the debugger has had a chance to configure breakpoints or issue
+ * suspend calls. It would be nice to just sit in the suspended state, but
+ * most debuggers don't expect any threads to be suspended when they attach.
+ *
+ * There's no JDWP event we can post to tell the debugger, "we've stopped,
+ * and we like it that way". We could send a fake breakpoint, which should
+ * cause the debugger to immediately send a resume, but the debugger might
+ * send the resume immediately or might throw an exception of its own upon
+ * receiving a breakpoint event that it didn't ask for.
+ *
+ * What we really want is a "wait until the debugger is done configuring
+ * stuff" event. We can approximate this with a "wait until the debugger
+ * has been idle for a brief period".
+ */
+
+/*
+ * Get a notion of the current time, in milliseconds.
+ */
+s8 dvmJdwpGetNowMsec(void)
+{
+#ifdef HAVE_POSIX_CLOCKS
+ struct timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ return now.tv_sec * 1000LL + now.tv_nsec / 1000000LL;
+#else
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ return now.tv_sec * 1000LL + now.tv_usec / 1000LL;
+#endif
+}
+
+/*
+ * Return the time, in milliseconds, since the last debugger activity.
+ *
+ * Returns -1 if no debugger is attached, or 0 if we're in the middle of
+ * processing a debugger request.
+ */
+s8 dvmJdwpLastDebuggerActivity(JdwpState* state)
+{
+ if (!gDvm.debuggerActive) {
+ LOGD("dvmJdwpLastDebuggerActivity: no active debugger\n");
+ return -1;
+ }
+
+ s8 last = dvmQuasiAtomicRead64(&state->lastActivityWhen);
+
+ /* initializing or in the middle of something? */
+ if (last == 0) {
+ LOGV("+++ last=busy\n");
+ return 0;
+ }
+
+ /* now get the current time */
+ s8 now = dvmJdwpGetNowMsec();
+ assert(now > last);
+
+ LOGV("+++ debugger interval=%lld\n", now - last);
+ return now - last;
+}
diff --git a/vm/jdwp/JdwpPriv.h b/vm/jdwp/JdwpPriv.h
new file mode 100644
index 0000000..bc249f1
--- /dev/null
+++ b/vm/jdwp/JdwpPriv.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * JDWP internal interfaces.
+ */
+#ifndef _DALVIK_JDWP_JDWPPRIV
+#define _DALVIK_JDWP_JDWPPRIV
+
+#define LOG_TAG "jdwp"
+
+#include "jdwp/Jdwp.h"
+#include "jdwp/JdwpEvent.h"
+#include "Debugger.h"
+
+#include <pthread.h>
+#include <sys/uio.h>
+
+/*
+ * JDWP constants.
+ */
+#define kJDWPHeaderLen 11
+#define kJDWPFlagReply 0x80
+
+/* DDM support */
+#define kJDWPDdmCmdSet 199 /* 0xc7, or 'G'+128 */
+#define kJDWPDdmCmd 1
+
+
+/*
+ * Transport-specific network status.
+ */
+struct JdwpNetState;
+typedef struct JdwpNetState JdwpNetState;
+
+struct JdwpState;
+
+/*
+ * Transport functions.
+ */
+typedef struct JdwpTransport {
+ bool (*startup)(struct JdwpState* state, const JdwpStartupParams* pParams);
+ bool (*accept)(struct JdwpState* state);
+ bool (*establish)(struct JdwpState* state);
+ void (*close)(struct JdwpState* state);
+ void (*shutdown)(struct JdwpState* state);
+ void (*free)(struct JdwpState* state);
+ bool (*isConnected)(struct JdwpState* state);
+ bool (*awaitingHandshake)(struct JdwpState* state);
+ bool (*processIncoming)(struct JdwpState* state);
+ bool (*sendRequest)(struct JdwpState* state, ExpandBuf* pReq);
+ bool (*sendBufferedRequest)(struct JdwpState* state,
+ const struct iovec* iov, int iovcnt);
+} JdwpTransport;
+
+const JdwpTransport* dvmJdwpSocketTransport();
+const JdwpTransport* dvmJdwpAndroidAdbTransport();
+
+
+/*
+ * State for JDWP functions.
+ */
+struct JdwpState {
+ JdwpStartupParams params;
+
+ /* wait for creation of the JDWP thread */
+ pthread_mutex_t threadStartLock;
+ pthread_cond_t threadStartCond;
+
+ int debugThreadStarted;
+ pthread_t debugThreadHandle;
+ ObjectId debugThreadId;
+ bool run;
+
+ const JdwpTransport* transport;
+ JdwpNetState* netState;
+
+ /* for wait-for-debugger */
+ pthread_mutex_t attachLock;
+ pthread_cond_t attachCond;
+
+ /* time of last debugger activity, in milliseconds */
+ s8 lastActivityWhen;
+
+ /* global counters and a mutex to protect them */
+ u4 requestSerial;
+ u4 eventSerial;
+ pthread_mutex_t serialLock;
+
+ /*
+ * Events requested by the debugger (breakpoints, class prep, etc).
+ */
+ int numEvents; /* #of elements in eventList */
+ JdwpEvent* eventList; /* linked list of events */
+ pthread_mutex_t eventLock; /* guards numEvents/eventList */
+
+ /*
+ * Synchronize suspension of event thread (to avoid receiving "resume"
+ * events before the thread has finished suspending itself).
+ */
+ pthread_mutex_t eventThreadLock;
+ pthread_cond_t eventThreadCond;
+ ObjectId eventThreadId;
+
+ /*
+ * DDM support.
+ */
+ bool ddmActive;
+};
+
+
+/* reset all session-specific data */
+void dvmJdwpResetState(JdwpState* state);
+
+/* atomic ops to get next serial number */
+u4 dvmJdwpNextRequestSerial(JdwpState* state);
+u4 dvmJdwpNextEventSerial(JdwpState* state);
+
+/* get current time, in msec */
+s8 dvmJdwpGetNowMsec(void);
+
+
+/*
+ * Transport functions.
+ */
+INLINE bool dvmJdwpNetStartup(JdwpState* state,
+ const JdwpStartupParams* pParams)
+{
+ return (*state->transport->startup)(state, pParams);
+}
+INLINE bool dvmJdwpAcceptConnection(JdwpState* state) {
+ return (*state->transport->accept)(state);
+}
+INLINE bool dvmJdwpEstablishConnection(JdwpState* state) {
+ return (*state->transport->establish)(state);
+}
+INLINE void dvmJdwpCloseConnection(JdwpState* state) {
+ (*state->transport->close)(state);
+}
+INLINE void dvmJdwpNetShutdown(JdwpState* state) {
+ (*state->transport->shutdown)(state);
+}
+INLINE void dvmJdwpNetFree(JdwpState* state) {
+ (*state->transport->free)(state);
+}
+INLINE bool dvmJdwpIsTransportDefined(JdwpState* state) {
+ return state != NULL && state->transport != NULL;
+}
+INLINE bool dvmJdwpIsConnected(JdwpState* state) {
+ return state != NULL && (*state->transport->isConnected)(state);
+}
+INLINE bool dvmJdwpAwaitingHandshake(JdwpState* state) {
+ return (*state->transport->awaitingHandshake)(state);
+}
+INLINE bool dvmJdwpProcessIncoming(JdwpState* state) {
+ return (*state->transport->processIncoming)(state);
+}
+INLINE bool dvmJdwpSendRequest(JdwpState* state, ExpandBuf* pReq) {
+ return (*state->transport->sendRequest)(state, pReq);
+}
+INLINE bool dvmJdwpSendBufferedRequest(JdwpState* state,
+ const struct iovec* iov, int iovcnt)
+{
+ return (*state->transport->sendBufferedRequest)(state, iov, iovcnt);
+}
+
+#endif /*_DALVIK_JDWP_JDWPPRIV*/
diff --git a/vm/jdwp/JdwpSocket.c b/vm/jdwp/JdwpSocket.c
new file mode 100644
index 0000000..0c39202
--- /dev/null
+++ b/vm/jdwp/JdwpSocket.c
@@ -0,0 +1,924 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * JDWP TCP socket network code.
+ */
+#include "jdwp/JdwpPriv.h"
+#include "jdwp/JdwpHandler.h"
+#include "Bits.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#define kBasePort 8000
+#define kMaxPort 8040
+
+#define kInputBufferSize 8192
+
+#define kMagicHandshake "JDWP-Handshake"
+#define kMagicHandshakeLen (sizeof(kMagicHandshake)-1)
+
+// fwd
+static void netShutdown(JdwpNetState* state);
+static void netFree(JdwpNetState* state);
+
+
+/*
+ * JDWP network state.
+ *
+ * We only talk to one debugger at a time.
+ */
+struct JdwpNetState {
+ short listenPort;
+ int listenSock; /* listen for connection from debugger */
+ int clientSock; /* active connection to debugger */
+ int wakePipe[2]; /* break out of select */
+
+ struct in_addr remoteAddr;
+ unsigned short remotePort;
+
+ bool awaitingHandshake; /* waiting for "JDWP-Handshake" */
+
+ /* pending data from the network; would be more efficient as circular buf */
+ unsigned char inputBuffer[kInputBufferSize];
+ int inputCount;
+};
+
+static JdwpNetState* netStartup(short port);
+
+/*
+ * Set up some stuff for transport=dt_socket.
+ */
+static bool prepareSocket(JdwpState* state, const JdwpStartupParams* pParams)
+{
+ unsigned short port;
+
+ if (pParams->server) {
+ if (pParams->port != 0) {
+ /* try only the specified port */
+ port = pParams->port;
+ state->netState = netStartup(port);
+ } else {
+ /* scan through a range of ports, binding to the first available */
+ for (port = kBasePort; port <= kMaxPort; port++) {
+ state->netState = netStartup(port);
+ if (state->netState != NULL)
+ break;
+ }
+ }
+ if (state->netState == NULL) {
+ LOGE("JDWP net startup failed (req port=%d)\n", pParams->port);
+ return false;
+ }
+ } else {
+ port = pParams->port; // used in a debug msg later
+ state->netState = netStartup(-1);
+ }
+
+ if (pParams->suspend)
+ LOGI("JDWP will wait for debugger on port %d\n", port);
+ else
+ LOGD("JDWP will %s on port %d\n",
+ pParams->server ? "listen" : "connect", port);
+
+ return true;
+}
+
+
+/*
+ * Are we still waiting for the handshake string?
+ */
+static bool awaitingHandshake(JdwpState* state)
+{
+ return state->netState->awaitingHandshake;
+}
+
+/*
+ * Initialize JDWP stuff.
+ *
+ * Allocates a new state structure. If "port" is non-negative, this also
+ * tries to bind to a listen port. If "port" is less than zero, we assume
+ * we're preparing for an outbound connection, and return without binding
+ * to anything.
+ *
+ * This may be called several times if we're probing for a port.
+ *
+ * Returns 0 on success.
+ */
+static JdwpNetState* netStartup(short port)
+{
+ JdwpNetState* netState;
+ int one = 1;
+
+ netState = (JdwpNetState*) malloc(sizeof(*netState));
+ memset(netState, 0, sizeof(*netState));
+ netState->listenSock = -1;
+ netState->clientSock = -1;
+ netState->wakePipe[0] = -1;
+ netState->wakePipe[1] = -1;
+
+ if (port < 0)
+ return netState;
+
+ assert(port != 0);
+
+ netState->listenSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (netState->listenSock < 0) {
+ LOGE("Socket create failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ /* allow immediate re-use */
+ if (setsockopt(netState->listenSock, SOL_SOCKET, SO_REUSEADDR, &one,
+ sizeof(one)) < 0)
+ {
+ LOGE("setsockopt(SO_REUSEADDR) failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ union {
+ struct sockaddr_in addrInet;
+ struct sockaddr addrPlain;
+ } addr;
+ addr.addrInet.sin_family = AF_INET;
+ addr.addrInet.sin_port = htons(port);
+ inet_aton("127.0.0.1", &addr.addrInet.sin_addr);
+
+ if (bind(netState->listenSock, &addr.addrPlain, sizeof(addr)) != 0) {
+ LOGV("attempt to bind to port %u failed: %s\n", port, strerror(errno));
+ goto fail;
+ }
+
+ netState->listenPort = port;
+ LOGVV("+++ bound to port %d\n", netState->listenPort);
+
+ if (listen(netState->listenSock, 5) != 0) {
+ LOGE("Listen failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ return netState;
+
+fail:
+ netShutdown(netState);
+ netFree(netState);
+ return NULL;
+}
+
+/*
+ * Shut down JDWP listener. Don't free state.
+ *
+ * Note that "netState" may be partially initialized if "startup" failed.
+ *
+ * This may be called from a non-JDWP thread as part of shutting the
+ * JDWP thread down.
+ *
+ * (This is currently called several times during startup as we probe
+ * for an open port.)
+ */
+static void netShutdown(JdwpNetState* netState)
+{
+ if (netState == NULL)
+ return;
+
+ int listenSock = netState->listenSock;
+ int clientSock = netState->clientSock;
+
+ /* clear these out so it doesn't wake up and try to reuse them */
+ netState->listenSock = netState->clientSock = -1;
+
+ /* "shutdown" dislodges blocking read() and accept() calls */
+ if (listenSock >= 0) {
+ shutdown(listenSock, SHUT_RDWR);
+ close(listenSock);
+ }
+ if (clientSock >= 0) {
+ shutdown(clientSock, SHUT_RDWR);
+ close(clientSock);
+ }
+
+ /* if we might be sitting in select, kick us loose */
+ if (netState->wakePipe[1] >= 0) {
+ LOGV("+++ writing to wakePipe\n");
+ (void) write(netState->wakePipe[1], "", 1);
+ }
+}
+static void netShutdownExtern(JdwpState* state)
+{
+ netShutdown(state->netState);
+}
+
+/*
+ * Free JDWP state.
+ *
+ * Call this after shutting the network down with netShutdown().
+ */
+static void netFree(JdwpNetState* netState)
+{
+ if (netState == NULL)
+ return;
+ assert(netState->listenSock == -1);
+ assert(netState->clientSock == -1);
+
+ if (netState->wakePipe[0] >= 0) {
+ close(netState->wakePipe[0]);
+ netState->wakePipe[0] = -1;
+ }
+ if (netState->wakePipe[1] >= 0) {
+ close(netState->wakePipe[1]);
+ netState->wakePipe[1] = -1;
+ }
+
+ free(netState);
+}
+static void netFreeExtern(JdwpState* state)
+{
+ netFree(state->netState);
+}
+
+/*
+ * Returns "true" if we're connected to a debugger.
+ */
+static bool isConnected(JdwpState* state)
+{
+ return (state->netState != NULL &&
+ state->netState->clientSock >= 0);
+}
+
+/*
+ * Returns "true" if the fd is ready, "false" if not.
+ */
+#if 0
+static bool isFdReadable(int sock)
+{
+ fd_set readfds;
+ struct timeval tv;
+ int count;
+
+ FD_ZERO(&readfds);
+ FD_SET(sock, &readfds);
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ count = select(sock+1, &readfds, NULL, NULL, &tv);
+ if (count <= 0)
+ return false;
+
+ if (FD_ISSET(sock, &readfds)) /* make sure it's our fd */
+ return true;
+
+ LOGE("WEIRD: odd behavior in select (count=%d)\n", count);
+ return false;
+}
+#endif
+
+#if 0
+/*
+ * Check to see if we have a pending connection from the debugger.
+ *
+ * Returns true on success (meaning a connection is available).
+ */
+static bool checkConnection(JdwpState* state)
+{
+ JdwpNetState* netState = state->netState;
+
+ assert(netState->listenSock >= 0);
+ /* not expecting to be called when debugger is actively connected */
+ assert(netState->clientSock < 0);
+
+ if (!isFdReadable(netState->listenSock))
+ return false;
+ return true;
+}
+#endif
+
+/*
+ * Disable the TCP Nagle algorithm, which delays transmission of outbound
+ * packets until the previous transmissions have been acked. JDWP does a
+ * lot of back-and-forth with small packets, so this may help.
+ */
+static int setNoDelay(int fd)
+{
+ int cc, on = 1;
+
+ cc = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
+ assert(cc == 0);
+ return cc;
+}
+
+/*
+ * Accept a connection. This will block waiting for somebody to show up.
+ * If that's not desirable, use checkConnection() to make sure something
+ * is pending.
+ */
+static bool acceptConnection(JdwpState* state)
+{
+ JdwpNetState* netState = state->netState;
+ union {
+ struct sockaddr_in addrInet;
+ struct sockaddr addrPlain;
+ } addr;
+ socklen_t addrlen;
+ int sock;
+
+ if (netState->listenSock < 0)
+ return false; /* you're not listening! */
+
+ assert(netState->clientSock < 0); /* must not already be talking */
+
+ addrlen = sizeof(addr);
+ do {
+ sock = accept(netState->listenSock, &addr.addrPlain, &addrlen);
+ if (sock < 0 && errno != EINTR) {
+ // When we call shutdown() on the socket, accept() returns with
+ // EINVAL. Don't gripe about it.
+ if (errno == EINVAL)
+ LOGVV("accept failed: %s\n", strerror(errno));
+ else
+ LOGE("accept failed: %s\n", strerror(errno));
+ return false;
+ }
+ } while (sock < 0);
+
+ netState->remoteAddr = addr.addrInet.sin_addr;
+ netState->remotePort = ntohs(addr.addrInet.sin_port);
+ LOGV("+++ accepted connection from %s:%u\n",
+ inet_ntoa(netState->remoteAddr), netState->remotePort);
+
+ netState->clientSock = sock;
+ netState->awaitingHandshake = true;
+ netState->inputCount = 0;
+
+ LOGV("Setting TCP_NODELAY on accepted socket\n");
+ setNoDelay(netState->clientSock);
+
+ if (pipe(netState->wakePipe) < 0) {
+ LOGE("pipe failed");
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Create a connection to a waiting debugger.
+ */
+static bool establishConnection(JdwpState* state)
+{
+ union {
+ struct sockaddr_in addrInet;
+ struct sockaddr addrPlain;
+ } addr;
+ struct hostent* pEntry;
+ int h_errno;
+
+ assert(state != NULL && state->netState != NULL);
+ assert(!state->params.server);
+ assert(state->params.host[0] != '\0');
+ assert(state->params.port != 0);
+
+ /*
+ * Start by resolving the host name.
+ */
+//#undef HAVE_GETHOSTBYNAME_R
+//#warning "forcing non-R"
+#ifdef HAVE_GETHOSTBYNAME_R
+ struct hostent he;
+ char auxBuf[128];
+ int cc = gethostbyname_r(state->params.host, &he, auxBuf, sizeof(auxBuf),
+ &pEntry, &h_errno);
+ if (cc != 0) {
+ LOGW("gethostbyname_r('%s') failed: %s\n",
+ state->params.host, strerror(errno));
+ return false;
+ }
+
+#else
+ h_errno = 0;
+ pEntry = gethostbyname(state->params.host);
+ if (pEntry == NULL) {
+ LOGW("gethostbyname('%s') failed: %s\n",
+ state->params.host, strerror(h_errno));
+ return false;
+ }
+#endif
+
+ /* copy it out ASAP to minimize risk of multithreaded annoyances */
+ memcpy(&addr.addrInet.sin_addr, pEntry->h_addr, pEntry->h_length);
+ addr.addrInet.sin_family = pEntry->h_addrtype;
+
+ addr.addrInet.sin_port = htons(state->params.port);
+
+ LOGI("Connecting out to '%s' %d\n",
+ inet_ntoa(addr.addrInet.sin_addr), ntohs(addr.addrInet.sin_port));
+
+ /*
+ * Create a socket.
+ */
+ JdwpNetState* netState;
+ netState = state->netState;
+ netState->clientSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (netState->clientSock < 0) {
+ LOGE("Unable to create socket: %s\n", strerror(errno));
+ return false;
+ }
+
+ /*
+ * Try to connect.
+ */
+ if (connect(netState->clientSock, &addr.addrPlain, sizeof(addr)) != 0) {
+ LOGE("Unable to connect to %s:%d: %s\n",
+ inet_ntoa(addr.addrInet.sin_addr), ntohs(addr.addrInet.sin_port),
+ strerror(errno));
+ close(netState->clientSock);
+ netState->clientSock = -1;
+ return false;
+ }
+
+ LOGI("Connection established to %s (%s:%d)\n",
+ state->params.host, inet_ntoa(addr.addrInet.sin_addr),
+ ntohs(addr.addrInet.sin_port));
+ netState->awaitingHandshake = true;
+ netState->inputCount = 0;
+
+ setNoDelay(netState->clientSock);
+
+ if (pipe(netState->wakePipe) < 0) {
+ LOGE("pipe failed");
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Close the connection to the debugger.
+ *
+ * Reset the state so we're ready to receive a new connection.
+ */
+static void closeConnection(JdwpState* state)
+{
+ JdwpNetState* netState;
+
+ assert(state != NULL && state->netState != NULL);
+
+ netState = state->netState;
+ if (netState->clientSock < 0)
+ return;
+
+ LOGV("+++ closed connection to %s:%u\n",
+ inet_ntoa(netState->remoteAddr), netState->remotePort);
+
+ close(netState->clientSock);
+ netState->clientSock = -1;
+
+ return;
+}
+
+/*
+ * Figure out if we have a full packet in the buffer.
+ */
+static bool haveFullPacket(JdwpNetState* netState)
+{
+ long length;
+
+ if (netState->awaitingHandshake)
+ return (netState->inputCount >= (int) kMagicHandshakeLen);
+
+ if (netState->inputCount < 4)
+ return false;
+
+ length = get4BE(netState->inputBuffer);
+ return (netState->inputCount >= length);
+}
+
+/*
+ * Consume bytes from the buffer.
+ *
+ * This would be more efficient with a circular buffer. However, we're
+ * usually only going to find one packet, which is trivial to handle.
+ */
+static void consumeBytes(JdwpNetState* netState, int count)
+{
+ assert(count > 0);
+ assert(count <= netState->inputCount);
+
+ if (count == netState->inputCount) {
+ netState->inputCount = 0;
+ return;
+ }
+
+ memmove(netState->inputBuffer, netState->inputBuffer + count,
+ netState->inputCount - count);
+ netState->inputCount -= count;
+}
+
+/*
+ * Dump the contents of a packet to stdout.
+ */
+#if 0
+static void dumpPacket(const unsigned char* packetBuf)
+{
+ const unsigned char* buf = packetBuf;
+ u4 length, id;
+ u1 flags, cmdSet, cmd;
+ u2 error;
+ bool reply;
+ int dataLen;
+
+ cmd = cmdSet = 0xcc;
+
+ length = read4BE(&buf);
+ id = read4BE(&buf);
+ flags = read1(&buf);
+ if ((flags & kJDWPFlagReply) != 0) {
+ reply = true;
+ error = read2BE(&buf);
+ } else {
+ reply = false;
+ cmdSet = read1(&buf);
+ cmd = read1(&buf);
+ }
+
+ dataLen = length - (buf - packetBuf);
+
+ LOGV("--- %s: dataLen=%u id=0x%08x flags=0x%02x cmd=%d/%d\n",
+ reply ? "reply" : "req",
+ dataLen, id, flags, cmdSet, cmd);
+ if (dataLen > 0)
+ dvmPrintHexDumpDbg(buf, dataLen, LOG_TAG);
+}
+#endif
+
+/*
+ * Handle a packet. Returns "false" if we encounter a connection-fatal error.
+ */
+static bool handlePacket(JdwpState* state)
+{
+ JdwpNetState* netState = state->netState;
+ const unsigned char* buf = netState->inputBuffer;
+ JdwpReqHeader hdr;
+ u4 length, id;
+ u1 flags, cmdSet, cmd;
+ u2 error;
+ bool reply;
+ int dataLen;
+
+ cmd = cmdSet = 0; // shut up gcc
+
+ /*dumpPacket(netState->inputBuffer);*/
+
+ length = read4BE(&buf);
+ id = read4BE(&buf);
+ flags = read1(&buf);
+ if ((flags & kJDWPFlagReply) != 0) {
+ reply = true;
+ error = read2BE(&buf);
+ } else {
+ reply = false;
+ cmdSet = read1(&buf);
+ cmd = read1(&buf);
+ }
+
+ assert((int) length <= netState->inputCount);
+ dataLen = length - (buf - netState->inputBuffer);
+
+ if (!reply) {
+ ExpandBuf* pReply = expandBufAlloc();
+
+ hdr.length = length;
+ hdr.id = id;
+ hdr.cmdSet = cmdSet;
+ hdr.cmd = cmd;
+ dvmJdwpProcessRequest(state, &hdr, buf, dataLen, pReply);
+ if (expandBufGetLength(pReply) > 0) {
+ int cc;
+
+ /*
+ * TODO: we currently assume the write() will complete in one
+ * go, which may not be safe for a network socket. We may need
+ * to mutex this against sendRequest().
+ */
+ cc = write(netState->clientSock, expandBufGetBuffer(pReply),
+ expandBufGetLength(pReply));
+ if (cc != (int) expandBufGetLength(pReply)) {
+ LOGE("Failed sending reply to debugger: %s\n", strerror(errno));
+ expandBufFree(pReply);
+ return false;
+ }
+ } else {
+ LOGW("No reply created for set=%d cmd=%d\n", cmdSet, cmd);
+ }
+ expandBufFree(pReply);
+ } else {
+ LOGV("reply?!\n");
+ assert(false);
+ }
+
+ LOGV("----------\n");
+
+ consumeBytes(netState, length);
+ return true;
+}
+
+/*
+ * Process incoming data. If no data is available, this will block until
+ * some arrives.
+ *
+ * If we get a full packet, handle it.
+ *
+ * To take some of the mystery out of life, we want to reject incoming
+ * connections if we already have a debugger attached. If we don't, the
+ * debugger will just mysteriously hang until it times out. We could just
+ * close the listen socket, but there's a good chance we won't be able to
+ * bind to the same port again, which would confuse utilities.
+ *
+ * Returns "false" on error (indicating that the connection has been severed),
+ * "true" if things are still okay.
+ */
+static bool processIncoming(JdwpState* state)
+{
+ JdwpNetState* netState = state->netState;
+ int readCount;
+
+ assert(netState->clientSock >= 0);
+
+ if (!haveFullPacket(netState)) {
+ /* read some more, looping until we have data */
+ errno = 0;
+ while (1) {
+ int selCount;
+ fd_set readfds;
+ int maxfd;
+ int fd;
+
+ maxfd = netState->listenSock;
+ if (netState->clientSock > maxfd)
+ maxfd = netState->clientSock;
+ if (netState->wakePipe[0] > maxfd)
+ maxfd = netState->wakePipe[0];
+
+ if (maxfd < 0) {
+ LOGV("+++ all fds are closed\n");
+ return false;
+ }
+
+ FD_ZERO(&readfds);
+
+ /* configure fds; note these may get zapped by another thread */
+ fd = netState->listenSock;
+ if (fd >= 0)
+ FD_SET(fd, &readfds);
+ fd = netState->clientSock;
+ if (fd >= 0)
+ FD_SET(fd, &readfds);
+ fd = netState->wakePipe[0];
+ if (fd >= 0) {
+ FD_SET(fd, &readfds);
+ } else {
+ LOGI("NOTE: entering select w/o wakepipe\n");
+ }
+
+ /*
+ * Select blocks until it sees activity on the file descriptors.
+ * Closing the local file descriptor does not count as activity,
+ * so we can't rely on that to wake us up (it works for read()
+ * and accept(), but not select()).
+ *
+ * We can do one of three things: (1) send a signal and catch
+ * EINTR, (2) open an additional fd ("wakePipe") and write to
+ * it when it's time to exit, or (3) time out periodically and
+ * re-issue the select. We're currently using #2, as it's more
+ * reliable than #1 and generally better than #3. Wastes two fds.
+ */
+ selCount = select(maxfd+1, &readfds, NULL, NULL, NULL);
+ if (selCount < 0) {
+ if (errno == EINTR)
+ continue;
+ LOGE("select failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ if (netState->wakePipe[0] >= 0 &&
+ FD_ISSET(netState->wakePipe[0], &readfds))
+ {
+ if (netState->listenSock >= 0)
+ LOGE("Exit wake set, but not exiting?\n");
+ else
+ LOGD("Got wake-up signal, bailing out of select\n");
+ goto fail;
+ }
+ if (netState->listenSock >= 0 &&
+ FD_ISSET(netState->listenSock, &readfds))
+ {
+ LOGI("Ignoring second debugger -- accepting and dropping\n");
+ union {
+ struct sockaddr_in addrInet;
+ struct sockaddr addrPlain;
+ } addr;
+ socklen_t addrlen;
+ int tmpSock;
+ tmpSock = accept(netState->listenSock, &addr.addrPlain,
+ &addrlen);
+ if (tmpSock < 0)
+ LOGI("Weird -- accept failed\n");
+ else
+ close(tmpSock);
+ }
+ if (netState->clientSock >= 0 &&
+ FD_ISSET(netState->clientSock, &readfds))
+ {
+ readCount = read(netState->clientSock,
+ netState->inputBuffer + netState->inputCount,
+ sizeof(netState->inputBuffer) - netState->inputCount);
+ if (readCount < 0) {
+ /* read failed */
+ if (errno != EINTR)
+ goto fail;
+ LOGD("+++ EINTR hit\n");
+ return true;
+ } else if (readCount == 0) {
+ /* EOF hit -- far end went away */
+ LOGD("+++ peer disconnected\n");
+ goto fail;
+ } else
+ break;
+ }
+ }
+
+ netState->inputCount += readCount;
+ if (!haveFullPacket(netState))
+ return true; /* still not there yet */
+ }
+
+ /*
+ * Special-case the initial handshake. For some bizarre reason we're
+ * expected to emulate bad tty settings by echoing the request back
+ * exactly as it was sent. Note the handshake is always initiated by
+ * the debugger, no matter who connects to whom.
+ *
+ * Other than this one case, the protocol [claims to be] stateless.
+ */
+ if (netState->awaitingHandshake) {
+ int cc;
+
+ if (memcmp(netState->inputBuffer,
+ kMagicHandshake, kMagicHandshakeLen) != 0)
+ {
+ LOGE("ERROR: bad handshake '%.14s'\n", netState->inputBuffer);
+ goto fail;
+ }
+
+ errno = 0;
+ cc = write(netState->clientSock, netState->inputBuffer,
+ kMagicHandshakeLen);
+ if (cc != kMagicHandshakeLen) {
+ LOGE("Failed writing handshake bytes: %s (%d of %d)\n",
+ strerror(errno), cc, (int) kMagicHandshakeLen);
+ goto fail;
+ }
+
+ consumeBytes(netState, kMagicHandshakeLen);
+ netState->awaitingHandshake = false;
+ LOGV("+++ handshake complete\n");
+ return true;
+ }
+
+ /*
+ * Handle this packet.
+ */
+ return handlePacket(state);
+
+fail:
+ closeConnection(state);
+ return false;
+}
+
+/*
+ * Send a request.
+ *
+ * The entire packet must be sent with a single write() call to avoid
+ * threading issues.
+ *
+ * Returns "true" if it was sent successfully.
+ */
+static bool sendRequest(JdwpState* state, ExpandBuf* pReq)
+{
+ JdwpNetState* netState = state->netState;
+ int cc;
+
+ /*dumpPacket(expandBufGetBuffer(pReq));*/
+ if (netState->clientSock < 0) {
+ /* can happen with some DDMS events */
+ LOGV("NOT sending request -- no debugger is attached\n");
+ return false;
+ }
+
+ /*
+ * TODO: we currently assume the write() will complete in one
+ * go, which may not be safe for a network socket. We may need
+ * to mutex this against handlePacket().
+ */
+ errno = 0;
+ cc = write(netState->clientSock, expandBufGetBuffer(pReq),
+ expandBufGetLength(pReq));
+ if (cc != (int) expandBufGetLength(pReq)) {
+ LOGE("Failed sending req to debugger: %s (%d of %d)\n",
+ strerror(errno), cc, (int) expandBufGetLength(pReq));
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Send a request that was split into multiple buffers.
+ *
+ * The entire packet must be sent with a single writev() call to avoid
+ * threading issues.
+ *
+ * Returns "true" if it was sent successfully.
+ */
+static bool sendBufferedRequest(JdwpState* state, const struct iovec* iov,
+ int iovcnt)
+{
+ JdwpNetState* netState = state->netState;
+
+ if (netState->clientSock < 0) {
+ /* can happen with some DDMS events */
+ LOGV("NOT sending request -- no debugger is attached\n");
+ return false;
+ }
+
+ size_t expected = 0;
+ int i;
+ for (i = 0; i < iovcnt; i++)
+ expected += iov[i].iov_len;
+
+ /*
+ * TODO: we currently assume the writev() will complete in one
+ * go, which may not be safe for a network socket. We may need
+ * to mutex this against handlePacket().
+ */
+ ssize_t actual;
+ actual = writev(netState->clientSock, iov, iovcnt);
+ if ((size_t)actual != expected) {
+ LOGE("Failed sending b-req to debugger: %s (%d of %zu)\n",
+ strerror(errno), (int) actual, expected);
+ return false;
+ }
+
+ return true;
+}
+
+
+/*
+ * Our functions.
+ *
+ * We can't generally share the implementations with other transports,
+ * even if they're also socket-based, because our JdwpNetState will be
+ * different from theirs.
+ */
+static const JdwpTransport socketTransport = {
+ prepareSocket,
+ acceptConnection,
+ establishConnection,
+ closeConnection,
+ netShutdownExtern,
+ netFreeExtern,
+ isConnected,
+ awaitingHandshake,
+ processIncoming,
+ sendRequest,
+ sendBufferedRequest,
+};
+
+/*
+ * Return our set.
+ */
+const JdwpTransport* dvmJdwpSocketTransport(void)
+{
+ return &socketTransport;
+}
diff --git a/vm/jdwp/README.txt b/vm/jdwp/README.txt
new file mode 100644
index 0000000..c97a069
--- /dev/null
+++ b/vm/jdwp/README.txt
@@ -0,0 +1,12 @@
+Java Debug Wire Protocol support
+
+This is a reasonably complete implementation, but only messages that are
+actually generated by debuggers have been implemented. The reasoning
+behind this is that it's better to leave a call unimplemented than have
+something that appears implemented but has never been tested.
+
+An attempt has been made to keep the implementation distinct from the VM,
+with Debugger.c acting as a sort of portability layer, so that the code
+might be useful in other projects. Once you get multiple simultaneous
+events and debugger requests with thread suspension bouncing around,
+though, it's difficult to keep things "generic".
diff --git a/vm/mterp/Makefile-mterp b/vm/mterp/Makefile-mterp
new file mode 100644
index 0000000..2f96d59
--- /dev/null
+++ b/vm/mterp/Makefile-mterp
@@ -0,0 +1,51 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Makefile for the Dalvik modular interpreter. This is not currently
+# integrated into the build system.
+#
+
+SHELL := /bin/sh
+
+# Build system has TARGET_ARCH=arm, but we need the exact architecture.
+# The base assumption for an ARM platform is ARMv5TE, but we may want to
+# support older ARMv4 devices, or use special features from ARMv6 or VFP.
+# The simulator build is "desktop".
+#
+# To generate sources for all targets:
+# for arch in desktop armv5te; do TARGET_ARCH_EXT=$arch make -f Makefile-mterp; done
+#
+#TARGET_ARCH_EXT := armv5te
+
+OUTPUT_DIR := out
+
+# Accumulate all possible dependencies for the generated files in a very
+# conservative fashion. If it's not one of the generated files in "out",
+# assume it's a dependency.
+SOURCE_DEPS := \
+ $(shell find . -path ./$(OUTPUT_DIR) -prune -o -type f -print) \
+ ../Android.mk ../../Android.mk
+
+# Source files generated by the script. There's always one C and one
+# assembly file, though in practice one or the other could be empty.
+GEN_SOURCES := \
+ $(OUTPUT_DIR)/InterpC-$(TARGET_ARCH_EXT).c \
+ $(OUTPUT_DIR)/InterpAsm-$(TARGET_ARCH_EXT).S
+
+target: $(GEN_SOURCES)
+
+$(GEN_SOURCES): $(SOURCE_DEPS)
+ @mkdir -p out
+ ./gen-mterp.py $(TARGET_ARCH_EXT) $(OUTPUT_DIR)
diff --git a/vm/mterp/Mterp.c b/vm/mterp/Mterp.c
new file mode 100644
index 0000000..dbf5003
--- /dev/null
+++ b/vm/mterp/Mterp.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Mterp entry point and support functions.
+ */
+#include "mterp/Mterp.h"
+
+#include <stddef.h>
+
+
+/*
+ * Verify some constants used by the mterp interpreter.
+ */
+bool dvmCheckAsmConstants(void)
+{
+ bool failed = false;
+
+#ifndef DVM_NO_ASM_INTERP
+
+ extern char dvmAsmInstructionStart[];
+ extern char dvmAsmInstructionEnd[];
+
+#define ASM_DEF_VERIFY
+#include "mterp/common/asm-constants.h"
+
+ if (failed) {
+ LOGE("Please correct the values in mterp/common/asm-constants.h\n");
+ dvmAbort();
+ }
+
+ /*
+ * If an instruction overflows the 64-byte handler size limit, it will
+ * push everything up and alter the total size. Check it here.
+ */
+ const int width = 64;
+ int interpSize = dvmAsmInstructionEnd - dvmAsmInstructionStart;
+ if (interpSize != 0 && interpSize != 256*width) {
+ LOGE("ERROR: unexpected asm interp size %d\n", interpSize);
+ LOGE("(did an instruction handler exceed %d bytes?)\n", width);
+ dvmAbort();
+ }
+
+#endif // ndef DVM_NO_ASM_INTERP
+
+ return !failed;
+}
+
+
+/*
+ * "Standard" mterp entry point. This sets up a "glue" structure and then
+ * calls into the assembly interpreter implementation.
+ *
+ * (There is presently no "debug" entry point.)
+ */
+bool dvmMterpStd(Thread* self, InterpState* glue)
+{
+ int changeInterp;
+
+ /* configure mterp items */
+ glue->self = self;
+ glue->methodClassDex = glue->method->clazz->pDvmDex;
+
+ glue->interpStackEnd = self->interpStackEnd;
+ glue->pSelfSuspendCount = &self->suspendCount;
+ glue->cardTable = gDvm.biasedCardTableBase;
+#if defined(WITH_JIT)
+ glue->pJitProfTable = gDvmJit.pProfTable;
+ glue->ppJitProfTable = &gDvmJit.pProfTable;
+ glue->jitThreshold = gDvmJit.threshold;
+#endif
+ if (gDvm.jdwpConfigured) {
+ glue->pDebuggerActive = &gDvm.debuggerActive;
+ } else {
+ glue->pDebuggerActive = NULL;
+ }
+ glue->pActiveProfilers = &gDvm.activeProfilers;
+
+ IF_LOGVV() {
+ char* desc = dexProtoCopyMethodDescriptor(&glue->method->prototype);
+ LOGVV("mterp threadid=%d entry %d: %s.%s %s\n",
+ dvmThreadSelf()->threadId,
+ glue->entryPoint,
+ glue->method->clazz->descriptor,
+ glue->method->name,
+ desc);
+ free(desc);
+ }
+ //LOGI("glue is %p, pc=%p, fp=%p\n", glue, glue->pc, glue->fp);
+ //LOGI("first instruction is 0x%04x\n", glue->pc[0]);
+
+ changeInterp = dvmMterpStdRun(glue);
+
+#if defined(WITH_JIT)
+ if (glue->jitState != kJitSingleStep) {
+ glue->self->inJitCodeCache = NULL;
+ }
+#endif
+
+ if (!changeInterp) {
+ /* this is a "normal" exit; we're not coming back */
+#ifdef LOG_INSTR
+ LOGD("|-- Leaving interpreter loop");
+#endif
+ return false;
+ } else {
+ /* we're "standard", so switch to "debug" */
+ LOGVV(" mterp returned, changeInterp=%d\n", changeInterp);
+ glue->nextMode = INTERP_DBG;
+ return true;
+ }
+}
diff --git a/vm/mterp/Mterp.h b/vm/mterp/Mterp.h
new file mode 100644
index 0000000..8b3f7b4
--- /dev/null
+++ b/vm/mterp/Mterp.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Some declarations used throughout mterp.
+ */
+#ifndef _DALVIK_MTERP_MTERP
+#define _DALVIK_MTERP_MTERP
+
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#if defined(WITH_JIT)
+#include "interp/Jit.h"
+#endif
+
+/*
+ * Interpreter state, passed into C functions from assembly stubs. The
+ * assembly code exports all registers into the "glue" structure before
+ * calling, then extracts them when the call returns.
+ */
+typedef InterpState MterpGlue;
+
+/*
+ * Call this during initialization to verify that the values in asm-constants.h
+ * are still correct.
+ */
+bool dvmCheckAsmConstants(void);
+
+/*
+ * Local entry and exit points. The platform-specific implementation must
+ * provide these two.
+ *
+ * dvmMterpStdRun() returns the "changeInterp" argument from dvmMterpStdBail(),
+ * indicating whether we want to bail out of the interpreter or just switch
+ * between "standard" and "debug" mode.
+ *
+ * The "mterp" interpreter is always "standard".
+ */
+bool dvmMterpStdRun(MterpGlue* glue);
+void dvmMterpStdBail(MterpGlue* glue, bool changeInterp);
+
+#endif /*_DALVIK_MTERP_MTERP*/
diff --git a/vm/mterp/NOTES.txt b/vm/mterp/NOTES.txt
new file mode 100644
index 0000000..5dc7caa
--- /dev/null
+++ b/vm/mterp/NOTES.txt
@@ -0,0 +1,71 @@
+Interpreter Notes
+
+
+==== Thread suspension and GC points ====
+
+The interpreter is expected to use a safe-point mechanism to allow thread
+suspension for garbage collection and the debugger. This typically
+means an explicit check of the thread-suspend flag on backward branches
+(including self-branches on certain instructions), exception throws,
+and method returns. The interpreter must also be prepared to switch in
+and out of the "debug" interpreter at these points.
+
+There are other ways for a thread to be stopped when a GC happens, notably:
+
+ - object allocation (either while executing an instruction that performs
+ allocation, or indirectly by allocating an exception when something
+ goes wrong)
+ - transitions to native code or indefinite wait (e.g. monitor-enter)
+
+(A debugger can in theory cause the interpreter to advance one instruction
+at a time, but since that only happens in the "debug" interpreter it's not
+necessary for authors of platform-specific code to worry about it.)
+
+For the most part the implementation does not need to worry about these
+things, but they matter when considering the contents of Dalvik's virtual
+registers for "precise" garbage collection. So, all opcode handlers must
+follow this rule:
+
+ * Do not modify the contents of a virtual register before executing
+ code that can pause the thread.
+
+This should be fairly hard to violate given the nature of essentially all
+instructions, which will compute a result and then finish by storing that
+result into the specified destination register. Using a virtual register
+to hold a partial or temporary result is not allowed. Virtual registers
+must not be modified if we abort the instruction with an exception.
+
+
+==== Method results and GC ====
+
+The return value from a method is held in local storage (on the native
+stack for the portable interpreter, and in glue->retval for asm). It's not
+accessible to a GC. In practice this isn't a problem, because if the
+following instruction is not a "move-result" then the result is ignored,
+and none of the move-result* instructions are GC points.
+
+(This is potentially an issue when debugging, since we can theoretically
+single-step by individual bytecodes, but in practice we always step by
+source lines and move-result is grouped with the method invoke.)
+
+This suggests a rule:
+
+ * Don't do anything that can cause a GC in the invoke-* handler after
+ a method returns successfully.
+
+Unsuccessful returns, such as a native method call that returns with an
+exception pending, are not interesting because the return value is ignored.
+
+If it's not possible to obey this rule, then we need to track the value
+used in a return-object instruction for a brief period. The easiest way
+to accomplish this is to store it in the interpreted stack where the GC
+can find it, and use a live-precise GC to ignore the value.
+
+The "trackref" functions can also be used, but they add overhead to method
+calls returning objects, and ensuring that we stop tracking the reference
+when it's no longer needed can be awkward.
+
+Any solution must work correctly when returning into or returning from native
+code. JNI handles returns from interp->native by adding the value to the
+local references table, but returns from native->interp are simply stored
+in the usual "retval".
diff --git a/vm/mterp/README.txt b/vm/mterp/README.txt
new file mode 100644
index 0000000..80596ea
--- /dev/null
+++ b/vm/mterp/README.txt
@@ -0,0 +1,215 @@
+Dalvik "mterp" README
+
+NOTE: Find rebuilding instructions at the bottom of this file.
+
+
+==== Overview ====
+
+This is the source code for the Dalvik interpreter. The core of the
+original version was implemented as a single C function, but to improve
+performance we rewrote it in assembly. To make this and future assembly
+ports easier and less error-prone, we used a modular approach that allows
+development of platform-specific code one opcode at a time.
+
+The original all-in-one-function C version still exists as the "portable"
+interpreter, and is generated using the same sources and tools that
+generate the platform-specific versions. One form of the portable
+interpreter includes support for profiling and debugging features, and
+is included even if we have a platform-optimized implementation.
+
+Every configuration has a "config-*" file that controls how the sources
+are generated. The sources are written into the "out" directory, where
+they are picked up by the Android build system.
+
+The best way to become familiar with the interpreter is to look at the
+generated files in the "out" directory, such as out/InterpC-portstd.c,
+rather than trying to look at the various component pieces in (say)
+armv5te.
+
+
+==== Platform-specific source generation ====
+
+The architecture-specific config files determine what goes into two
+generated output files (InterpC-<arch>.c, InterpAsm-<arch>.S). The goal is
+to make it easy to swap C and assembly sources during initial development
+and testing, and to provide a way to use architecture-specific versions of
+some operations (e.g. making use of PLD instructions on ARMv6 or avoiding
+CLZ on ARMv4T).
+
+Two basic assumptions are made about the operation of the interpreter:
+
+ - The assembly version uses fixed-size areas for each instruction
+ (e.g. 64 bytes). "Overflow" code is tacked on to the end.
+ - When a C implementation is desired, the assembly version packs all
+ local state into a "glue" struct, and passes that into the C function.
+ Updates to the state are pulled out of the "glue" on return.
+
+The "arch" value should indicate an architecture family with common
+programming characteristics, so "armv5te" would work for all ARMv5TE CPUs,
+but might not be backward- or forward-compatible. (We *might* want to
+specify the ABI model as well, e.g. "armv5te-eabi", but currently that adds
+verbosity without value.)
+
+
+==== Config file format ====
+
+The config files are parsed from top to bottom. Each line in the file
+may be blank, hold a comment (line starts with '#'), or be a command.
+
+The commands are:
+
+ handler-size <bytes>
+
+ Specify the size of the assembly region, in bytes. On most platforms
+ this will need to be a power of 2.
+
+ import <filename>
+
+ The specified file is included immediately, in its entirety. No
+ substitutions are performed. ".c" and ".h" files are copied to the
+ C output, ".S" files are copied to the asm output.
+
+ asm-stub <filename>
+
+ The named file will be included whenever an assembly "stub" is needed.
+ Text substitution is performed on the opcode name.
+
+ op-start <directory>
+
+ Indicates the start of the opcode list. Must precede any "op"
+ commands. The specified directory is the default location to pull
+ instruction files from.
+
+ op <opcode> <directory>
+
+ Can only appear after "op-start" and before "op-end". Overrides the
+ default source file location of the specified opcode. The opcode
+ definition will come from the specified file, e.g. "op OP_NOP armv5te"
+ will load from "armv5te/OP_NOP.S". A substitution dictionary will be
+ applied (see below).
+
+ op-end
+
+ Indicates the end of the opcode list. All 256 opcodes are emitted
+ when this is seen, followed by any code that didn't fit inside the
+ fixed-size instruction handler space.
+
+
+The order of "op" directives is not significant; the generation tool will
+extract ordering info from the VM sources.
+
+Typically the form in which most opcodes currently exist is used in
+the "op-start" directive. For a new port you would start with "c",
+and add architecture-specific "op" entries as you write instructions.
+When complete it will default to the target architecture, and you insert
+"c" ops to stub out platform-specific code.
+
+For the <directory> specified in the "op" command, the "c" directory
+is special in two ways: (1) the sources are assumed to be C code, and
+will be inserted into the generated C file; (2) when a C implementation
+is emitted, a "glue stub" is emitted in the assembly source file.
+(The generator script always emits 256 assembly instructions, unless
+"asm-stub" was left blank, in which case it only emits some labels.)
+
+
+==== Instruction file format ====
+
+The assembly instruction files are simply fragments of assembly sources.
+The starting label will be provided by the generation tool, as will
+declarations for the segment type and alignment. The expected target
+assembler is GNU "as", but others will work (may require fiddling with
+some of the pseudo-ops emitted by the generation tool).
+
+The C files do a bunch of fancy things with macros in an attempt to share
+code with the portable interpreter. (This is expected to be reduced in
+the future.)
+
+A substitution dictionary is applied to all opcode fragments as they are
+appended to the output. Substitutions can look like "$value" or "${value}".
+
+The dictionary always includes:
+
+ $opcode - opcode name, e.g. "OP_NOP"
+ $opnum - opcode number, e.g. 0 for OP_NOP
+ $handler_size_bytes - max size of an instruction handler, in bytes
+ $handler_size_bits - max size of an instruction handler, log 2
+
+Both C and assembly sources will be passed through the C pre-processor,
+so you can take advantage of C-style comments and preprocessor directives
+like "#define".
+
+Some generator operations are available.
+
+ %include "filename" [subst-dict]
+
+ Includes the file, which should look like "armv5te/OP_NOP.S". You can
+ specify values for the substitution dictionary, using standard Python
+ syntax. For example, this:
+ %include "armv5te/unop.S" {"result":"r1"}
+ would insert "armv5te/unop.S" at the current file position, replacing
+ occurrences of "$result" with "r1".
+
+ %default <subst-dict>
+
+ Specify default substitution dictionary values, using standard Python
+ syntax. Useful if you want to have a "base" version and variants.
+
+ %break
+
+ Identifies the split between the main portion of the instruction
+ handler (which must fit in "handler-size" bytes) and the "sister"
+ code, which is appended to the end of the instruction handler block.
+
+ %verify "message"
+
+ Leave a note to yourself about what needs to be tested. (This may
+ turn into something more interesting someday; for now, it just gets
+ stripped out before the output is generated.)
+
+The generation tool does *not* print a warning if your instructions
+exceed "handler-size", but the VM will abort on startup if it detects an
+oversized handler. On architectures with fixed-width instructions this
+is easy to work with, on others this you will need to count bytes.
+
+
+==== Using C constants from assembly sources ====
+
+The file "common/asm-constants.h" has some definitions for constant
+values, structure sizes, and struct member offsets. The format is fairly
+restricted, as simple macros are used to massage it for use with both C
+(where it is verified) and assembly (where the definitions are used).
+
+If a constant in the file becomes out of sync, the VM will log an error
+message and abort during startup.
+
+
+==== Development tips ====
+
+If you need to debug the initial piece of an opcode handler, and your
+debug code expands it beyond the handler size limit, you can insert a
+generic header at the top:
+
+ b ${opcode}_start
+%break
+${opcode}_start:
+
+If you already have a %break, it's okay to leave it in place -- the second
+%break is ignored.
+
+
+==== Rebuilding ====
+
+If you change any of the source file fragments, you need to rebuild the
+combined source files in the "out" directory. Make sure the files in
+"out" are editable, then:
+
+ $ cd mterp
+ $ ./rebuild.sh
+
+As of this writing, this requires Python 2.5. You may see inscrutible
+error messages or just general failure if you have a different version
+of Python installed.
+
+The ultimate goal is to have the build system generate the necessary
+output files without requiring this separate step, but we're not yet
+ready to require Python in the build.
diff --git a/vm/mterp/arm-vfp/OP_ADD_DOUBLE.S b/vm/mterp/arm-vfp/OP_ADD_DOUBLE.S
new file mode 100644
index 0000000..5a5ad1d
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_ADD_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinopWide.S" {"instr":"faddd d2, d0, d1"}
diff --git a/vm/mterp/arm-vfp/OP_ADD_DOUBLE_2ADDR.S b/vm/mterp/arm-vfp/OP_ADD_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..9823765
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_ADD_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinopWide2addr.S" {"instr":"faddd d2, d0, d1"}
diff --git a/vm/mterp/arm-vfp/OP_ADD_FLOAT.S b/vm/mterp/arm-vfp/OP_ADD_FLOAT.S
new file mode 100644
index 0000000..22023ec
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_ADD_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinop.S" {"instr":"fadds s2, s0, s1"}
diff --git a/vm/mterp/arm-vfp/OP_ADD_FLOAT_2ADDR.S b/vm/mterp/arm-vfp/OP_ADD_FLOAT_2ADDR.S
new file mode 100644
index 0000000..e787589
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_ADD_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinop2addr.S" {"instr":"fadds s2, s0, s1"}
diff --git a/vm/mterp/arm-vfp/OP_CMPG_DOUBLE.S b/vm/mterp/arm-vfp/OP_CMPG_DOUBLE.S
new file mode 100644
index 0000000..b75216e
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_CMPG_DOUBLE.S
@@ -0,0 +1,42 @@
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * int compare(x, y) {
+ * if (x == y) {
+ * return 0;
+ * } else if (x < y) {
+ * return -1;
+ * } else if (x > y) {
+ * return 1;
+ * } else {
+ * return 1;
+ * }
+ * }
+ */
+ /* op vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ fldd d0, [r2] @ d0<- vBB
+ fldd d1, [r3] @ d1<- vCC
+ fcmped d0, d1 @ compare (vBB, vCC)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ mov r0, #1 @ r0<- 1 (default)
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fmstat @ export status flags
+ mvnmi r0, #0 @ (less than) r1<- -1
+ moveq r0, #0 @ (equal) r1<- 0
+ b .L${opcode}_finish @ argh
+
+%break
+.L${opcode}_finish:
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/arm-vfp/OP_CMPG_FLOAT.S b/vm/mterp/arm-vfp/OP_CMPG_FLOAT.S
new file mode 100644
index 0000000..eade97d
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_CMPG_FLOAT.S
@@ -0,0 +1,42 @@
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * int compare(x, y) {
+ * if (x == y) {
+ * return 0;
+ * } else if (x < y) {
+ * return -1;
+ * } else if (x > y) {
+ * return 1;
+ * } else {
+ * return 1;
+ * }
+ * }
+ */
+ /* op vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ flds s0, [r2] @ s0<- vBB
+ flds s1, [r3] @ s1<- vCC
+ fcmpes s0, s1 @ compare (vBB, vCC)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ mov r0, #1 @ r0<- 1 (default)
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fmstat @ export status flags
+ mvnmi r0, #0 @ (less than) r1<- -1
+ moveq r0, #0 @ (equal) r1<- 0
+ b .L${opcode}_finish @ argh
+
+%break
+.L${opcode}_finish:
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/arm-vfp/OP_CMPL_DOUBLE.S b/vm/mterp/arm-vfp/OP_CMPL_DOUBLE.S
new file mode 100644
index 0000000..6e85fe7
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_CMPL_DOUBLE.S
@@ -0,0 +1,42 @@
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * int compare(x, y) {
+ * if (x == y) {
+ * return 0;
+ * } else if (x > y) {
+ * return 1;
+ * } else if (x < y) {
+ * return -1;
+ * } else {
+ * return -1;
+ * }
+ * }
+ */
+ /* op vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ fldd d0, [r2] @ d0<- vBB
+ fldd d1, [r3] @ d1<- vCC
+ fcmped d0, d1 @ compare (vBB, vCC)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ mvn r0, #0 @ r0<- -1 (default)
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fmstat @ export status flags
+ movgt r0, #1 @ (greater than) r1<- 1
+ moveq r0, #0 @ (equal) r1<- 0
+ b .L${opcode}_finish @ argh
+
+%break
+.L${opcode}_finish:
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/arm-vfp/OP_CMPL_FLOAT.S b/vm/mterp/arm-vfp/OP_CMPL_FLOAT.S
new file mode 100644
index 0000000..bdeb0be
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_CMPL_FLOAT.S
@@ -0,0 +1,42 @@
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * int compare(x, y) {
+ * if (x == y) {
+ * return 0;
+ * } else if (x > y) {
+ * return 1;
+ * } else if (x < y) {
+ * return -1;
+ * } else {
+ * return -1;
+ * }
+ * }
+ */
+ /* op vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ flds s0, [r2] @ s0<- vBB
+ flds s1, [r3] @ s1<- vCC
+ fcmpes s0, s1 @ compare (vBB, vCC)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ mvn r0, #0 @ r0<- -1 (default)
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fmstat @ export status flags
+ movgt r0, #1 @ (greater than) r1<- 1
+ moveq r0, #0 @ (equal) r1<- 0
+ b .L${opcode}_finish @ argh
+
+%break
+.L${opcode}_finish:
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/arm-vfp/OP_DIV_DOUBLE.S b/vm/mterp/arm-vfp/OP_DIV_DOUBLE.S
new file mode 100644
index 0000000..11770ad
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_DIV_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinopWide.S" {"instr":"fdivd d2, d0, d1"}
diff --git a/vm/mterp/arm-vfp/OP_DIV_DOUBLE_2ADDR.S b/vm/mterp/arm-vfp/OP_DIV_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..a52f434
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_DIV_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinopWide2addr.S" {"instr":"fdivd d2, d0, d1"}
diff --git a/vm/mterp/arm-vfp/OP_DIV_FLOAT.S b/vm/mterp/arm-vfp/OP_DIV_FLOAT.S
new file mode 100644
index 0000000..2e82ada
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_DIV_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinop.S" {"instr":"fdivs s2, s0, s1"}
diff --git a/vm/mterp/arm-vfp/OP_DIV_FLOAT_2ADDR.S b/vm/mterp/arm-vfp/OP_DIV_FLOAT_2ADDR.S
new file mode 100644
index 0000000..2147583
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_DIV_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinop2addr.S" {"instr":"fdivs s2, s0, s1"}
diff --git a/vm/mterp/arm-vfp/OP_DOUBLE_TO_FLOAT.S b/vm/mterp/arm-vfp/OP_DOUBLE_TO_FLOAT.S
new file mode 100644
index 0000000..33d5b61
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_DOUBLE_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/funopNarrower.S" {"instr":"fcvtsd s0, d0"}
diff --git a/vm/mterp/arm-vfp/OP_DOUBLE_TO_INT.S b/vm/mterp/arm-vfp/OP_DOUBLE_TO_INT.S
new file mode 100644
index 0000000..2ef4838
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_DOUBLE_TO_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/funopNarrower.S" {"instr":"ftosizd s0, d0"}
diff --git a/vm/mterp/arm-vfp/OP_FLOAT_TO_DOUBLE.S b/vm/mterp/arm-vfp/OP_FLOAT_TO_DOUBLE.S
new file mode 100644
index 0000000..0acb3d8
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_FLOAT_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/funopWider.S" {"instr":"fcvtds d0, s0"}
diff --git a/vm/mterp/arm-vfp/OP_FLOAT_TO_INT.S b/vm/mterp/arm-vfp/OP_FLOAT_TO_INT.S
new file mode 100644
index 0000000..d0a9a2e
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_FLOAT_TO_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/funop.S" {"instr":"ftosizs s1, s0"}
diff --git a/vm/mterp/arm-vfp/OP_INT_TO_DOUBLE.S b/vm/mterp/arm-vfp/OP_INT_TO_DOUBLE.S
new file mode 100644
index 0000000..6eb430e
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_INT_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/funopWider.S" {"instr":"fsitod d0, s0"}
diff --git a/vm/mterp/arm-vfp/OP_INT_TO_FLOAT.S b/vm/mterp/arm-vfp/OP_INT_TO_FLOAT.S
new file mode 100644
index 0000000..698bdc7
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_INT_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/funop.S" {"instr":"fsitos s1, s0"}
diff --git a/vm/mterp/arm-vfp/OP_MUL_DOUBLE.S b/vm/mterp/arm-vfp/OP_MUL_DOUBLE.S
new file mode 100644
index 0000000..7563191
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_MUL_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinopWide.S" {"instr":"fmuld d2, d0, d1"}
diff --git a/vm/mterp/arm-vfp/OP_MUL_DOUBLE_2ADDR.S b/vm/mterp/arm-vfp/OP_MUL_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..eadf101
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_MUL_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinopWide2addr.S" {"instr":"fmuld d2, d0, d1"}
diff --git a/vm/mterp/arm-vfp/OP_MUL_FLOAT.S b/vm/mterp/arm-vfp/OP_MUL_FLOAT.S
new file mode 100644
index 0000000..bb3ab42
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_MUL_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinop.S" {"instr":"fmuls s2, s0, s1"}
diff --git a/vm/mterp/arm-vfp/OP_MUL_FLOAT_2ADDR.S b/vm/mterp/arm-vfp/OP_MUL_FLOAT_2ADDR.S
new file mode 100644
index 0000000..3918537
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_MUL_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinop2addr.S" {"instr":"fmuls s2, s0, s1"}
diff --git a/vm/mterp/arm-vfp/OP_SUB_DOUBLE.S b/vm/mterp/arm-vfp/OP_SUB_DOUBLE.S
new file mode 100644
index 0000000..d40e083
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_SUB_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinopWide.S" {"instr":"fsubd d2, d0, d1"}
diff --git a/vm/mterp/arm-vfp/OP_SUB_DOUBLE_2ADDR.S b/vm/mterp/arm-vfp/OP_SUB_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..705124f
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_SUB_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinopWide2addr.S" {"instr":"fsubd d2, d0, d1"}
diff --git a/vm/mterp/arm-vfp/OP_SUB_FLOAT.S b/vm/mterp/arm-vfp/OP_SUB_FLOAT.S
new file mode 100644
index 0000000..0bf2bc0
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_SUB_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinop.S" {"instr":"fsubs s2, s0, s1"}
diff --git a/vm/mterp/arm-vfp/OP_SUB_FLOAT_2ADDR.S b/vm/mterp/arm-vfp/OP_SUB_FLOAT_2ADDR.S
new file mode 100644
index 0000000..e214068
--- /dev/null
+++ b/vm/mterp/arm-vfp/OP_SUB_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "arm-vfp/fbinop2addr.S" {"instr":"fsubs s2, s0, s1"}
diff --git a/vm/mterp/arm-vfp/README.txt b/vm/mterp/arm-vfp/README.txt
new file mode 100644
index 0000000..c94e1e8
--- /dev/null
+++ b/vm/mterp/arm-vfp/README.txt
@@ -0,0 +1,7 @@
+Instruction handlers that take advantage of ARM VFP. These work with VFP
+v2 and v3 (VFPLite).
+
+The ARM code driving the floating-point calculations will run on ARMv5TE
+and later. It assumes that word alignment is sufficient for double-word
+accesses (which is true for some ARMv5 and all ARMv6/v7), to avoid having
+to transfer double-precision values in two steps.
diff --git a/vm/mterp/arm-vfp/fbinop.S b/vm/mterp/arm-vfp/fbinop.S
new file mode 100644
index 0000000..ff9683e
--- /dev/null
+++ b/vm/mterp/arm-vfp/fbinop.S
@@ -0,0 +1,23 @@
+ /*
+ * Generic 32-bit floating-point operation. Provide an "instr" line that
+ * specifies an instruction that performs "s2 = s0 op s1". Because we
+ * use the "softfp" ABI, this must be an instruction, not a function call.
+ *
+ * For: add-float, sub-float, mul-float, div-float
+ */
+ /* floatop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ flds s1, [r3] @ s1<- vCC
+ flds s0, [r2] @ s0<- vBB
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ $instr @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vAA
+ fsts s2, [r9] @ vAA<- s2
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/arm-vfp/fbinop2addr.S b/vm/mterp/arm-vfp/fbinop2addr.S
new file mode 100644
index 0000000..85b9fab
--- /dev/null
+++ b/vm/mterp/arm-vfp/fbinop2addr.S
@@ -0,0 +1,21 @@
+ /*
+ * Generic 32-bit floating point "/2addr" binary operation. Provide
+ * an "instr" line that specifies an instruction that performs
+ * "s2 = s0 op s1".
+ *
+ * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ and r9, r9, #15 @ r9<- A
+ flds s1, [r3] @ s1<- vB
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ flds s0, [r9] @ s0<- vA
+
+ $instr @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fsts s2, [r9] @ vAA<- s2
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/arm-vfp/fbinopWide.S b/vm/mterp/arm-vfp/fbinopWide.S
new file mode 100644
index 0000000..2b9ad69
--- /dev/null
+++ b/vm/mterp/arm-vfp/fbinopWide.S
@@ -0,0 +1,23 @@
+ /*
+ * Generic 64-bit double-precision floating point binary operation.
+ * Provide an "instr" line that specifies an instruction that performs
+ * "d2 = d0 op d1".
+ *
+ * for: add-double, sub-double, mul-double, div-double
+ */
+ /* doubleop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ fldd d1, [r3] @ d1<- vCC
+ fldd d0, [r2] @ d0<- vBB
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ $instr @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vAA
+ fstd d2, [r9] @ vAA<- d2
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/arm-vfp/fbinopWide2addr.S b/vm/mterp/arm-vfp/fbinopWide2addr.S
new file mode 100644
index 0000000..15d9424
--- /dev/null
+++ b/vm/mterp/arm-vfp/fbinopWide2addr.S
@@ -0,0 +1,22 @@
+ /*
+ * Generic 64-bit floating point "/2addr" binary operation. Provide
+ * an "instr" line that specifies an instruction that performs
+ * "d2 = d0 op d1".
+ *
+ * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+ * div-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ and r9, r9, #15 @ r9<- A
+ fldd d1, [r3] @ d1<- vB
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ fldd d0, [r9] @ d0<- vA
+
+ $instr @ d2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fstd d2, [r9] @ vAA<- d2
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/arm-vfp/funop.S b/vm/mterp/arm-vfp/funop.S
new file mode 100644
index 0000000..a5846ce
--- /dev/null
+++ b/vm/mterp/arm-vfp/funop.S
@@ -0,0 +1,18 @@
+ /*
+ * Generic 32-bit unary floating-point operation. Provide an "instr"
+ * line that specifies an instruction that performs "s1 = op s0".
+ *
+ * for: int-to-float, float-to-int
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ flds s0, [r3] @ s0<- vB
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ and r9, r9, #15 @ r9<- A
+ $instr @ s1<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ fsts s1, [r9] @ vA<- s1
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/arm-vfp/funopNarrower.S b/vm/mterp/arm-vfp/funopNarrower.S
new file mode 100644
index 0000000..7ae1676
--- /dev/null
+++ b/vm/mterp/arm-vfp/funopNarrower.S
@@ -0,0 +1,18 @@
+ /*
+ * Generic 64bit-to-32bit unary floating point operation. Provide an
+ * "instr" line that specifies an instruction that performs "s0 = op d0".
+ *
+ * For: double-to-int, double-to-float
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ fldd d0, [r3] @ d0<- vB
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ and r9, r9, #15 @ r9<- A
+ $instr @ s0<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ fsts s0, [r9] @ vA<- s0
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/arm-vfp/funopWider.S b/vm/mterp/arm-vfp/funopWider.S
new file mode 100644
index 0000000..055b851
--- /dev/null
+++ b/vm/mterp/arm-vfp/funopWider.S
@@ -0,0 +1,18 @@
+ /*
+ * Generic 32bit-to-64bit floating point unary operation. Provide an
+ * "instr" line that specifies an instruction that performs "d0 = op s0".
+ *
+ * For: int-to-double, float-to-double
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ flds s0, [r3] @ s0<- vB
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ and r9, r9, #15 @ r9<- A
+ $instr @ d0<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ fstd d0, [r9] @ vA<- d0
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv4t/OP_AGET_WIDE.S b/vm/mterp/armv4t/OP_AGET_WIDE.S
new file mode 100644
index 0000000..dc5eee8
--- /dev/null
+++ b/vm/mterp/armv4t/OP_AGET_WIDE.S
@@ -0,0 +1,33 @@
+%verify "executed"
+ /*
+ * Array get, 64 bits. vAA <- vBB[vCC].
+ *
+ * Arrays of long/double are 64-bit aligned, so it's okay to use LDRD.
+ */
+ /* aget-wide vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #3 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcc .L${opcode}_finish @ okay, continue below
+ b common_errArrayIndex @ index >= length, bail
+ @ May want to swap the order of these two branches depending on how the
+ @ branch prediction (if any) handles conditional forward branches vs.
+ @ unconditional forward branches.
+%break
+
+.L${opcode}_finish:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ add r0, r0, #offArrayObject_contents
+ ldmia r0, {r2-r3} @ r2/r3 <- vBB[vCC]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r2-r3} @ vAA/vAA+1<- r2/r3
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv4t/OP_APUT_WIDE.S b/vm/mterp/armv4t/OP_APUT_WIDE.S
new file mode 100644
index 0000000..9a718f2
--- /dev/null
+++ b/vm/mterp/armv4t/OP_APUT_WIDE.S
@@ -0,0 +1,31 @@
+%verify "executed"
+ /*
+ * Array put, 64 bits. vBB[vCC] <- vAA.
+ */
+ /* aput-wide vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #3 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ bcc .L${opcode}_finish @ okay, continue below
+ b common_errArrayIndex @ index >= length, bail
+ @ May want to swap the order of these two branches depending on how the
+ @ branch prediction (if any) handles conditional forward branches vs.
+ @ unconditional forward branches.
+%break
+
+.L${opcode}_finish:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldmia r9, {r2-r3} @ r2/r3<- vAA/vAA+1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ add r0, #offArrayObject_contents
+ stmia r0, {r2-r3} @ vBB[vCC] <- r2/r3
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv4t/OP_IGET_WIDE.S b/vm/mterp/armv4t/OP_IGET_WIDE.S
new file mode 100644
index 0000000..5940f56
--- /dev/null
+++ b/vm/mterp/armv4t/OP_IGET_WIDE.S
@@ -0,0 +1,50 @@
+%default {"volatile":"0"}
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+ /*
+ * Wide 32-bit instance field get.
+ */
+ /* iget-wide vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .L${opcode}_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .L${opcode}_finish
+ b common_exceptionThrown
+%break
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.L${opcode}_finish:
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ .if $volatile
+ add r0, r9, r3 @ r0<- address of field
+ bl dvmQuasiAtomicRead64 @ r0/r1<- contents of field
+ .else
+ add r9, r9, r3 @ r9<- obj + field offset
+ ldmia r9, {r0-r1} @ r0/r1<- obj.field (64-bit align ok)
+ .endif
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ add r3, rFP, r2, lsl #2 @ r3<- &fp[A]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ fp[A]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv4t/OP_IGET_WIDE_QUICK.S b/vm/mterp/armv4t/OP_IGET_WIDE_QUICK.S
new file mode 100644
index 0000000..b0022f5
--- /dev/null
+++ b/vm/mterp/armv4t/OP_IGET_WIDE_QUICK.S
@@ -0,0 +1,17 @@
+%verify "executed"
+%verify "null object"
+ /* iget-wide-quick vA, vB, offset@CCCC */
+ mov r2, rINST, lsr #12 @ r2<- B
+ GET_VREG(r3, r2) @ r3<- object we're operating on
+ FETCH(r1, 1) @ r1<- field byte offset
+ cmp r3, #0 @ check object for null
+ mov r2, rINST, lsr #8 @ r2<- A(+)
+ beq common_errNullObject @ object was null
+ add r9, r3, r1 @ r9<- object + offset
+ ldmia r9, {r0-r1} @ r0/r1<- obj.field (64 bits, aligned)
+ and r2, r2, #15 @ r2<- A
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ add r3, rFP, r2, lsl #2 @ r3<- &fp[A]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ fp[A]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv4t/OP_IGET_WIDE_VOLATILE.S b/vm/mterp/armv4t/OP_IGET_WIDE_VOLATILE.S
new file mode 100644
index 0000000..cdd8708
--- /dev/null
+++ b/vm/mterp/armv4t/OP_IGET_WIDE_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv4t/OP_IGET_WIDE.S" {"volatile":"1"}
diff --git a/vm/mterp/armv4t/OP_IPUT_WIDE.S b/vm/mterp/armv4t/OP_IPUT_WIDE.S
new file mode 100644
index 0000000..d0f7315
--- /dev/null
+++ b/vm/mterp/armv4t/OP_IPUT_WIDE.S
@@ -0,0 +1,46 @@
+%default {"volatile":"0"}
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+ /* iput-wide vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .L${opcode}_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .L${opcode}_finish @ yes, finish up
+ b common_exceptionThrown
+%break
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.L${opcode}_finish:
+ mov r2, rINST, lsr #8 @ r2<- A+
+ cmp r9, #0 @ check object for null
+ and r2, r2, #15 @ r2<- A
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ add r2, rFP, r2, lsl #2 @ r3<- &fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldmia r2, {r0-r1} @ r0/r1<- fp[A]
+ GET_INST_OPCODE(r10) @ extract opcode from rINST
+ add r2, r9, r3 @ r2<- object + byte offset
+ .if $volatile
+ bl dvmQuasiAtomicSwap64 @ stores r0/r1 into addr r2
+ .else
+ stmia r2, {r0-r1} @ obj.field (64 bits, aligned)<- r0/r1
+ .endif
+ GOTO_OPCODE(r10) @ jump to next instruction
diff --git a/vm/mterp/armv4t/OP_IPUT_WIDE_QUICK.S b/vm/mterp/armv4t/OP_IPUT_WIDE_QUICK.S
new file mode 100644
index 0000000..b062127
--- /dev/null
+++ b/vm/mterp/armv4t/OP_IPUT_WIDE_QUICK.S
@@ -0,0 +1,17 @@
+%verify "executed"
+%verify "null object"
+ /* iput-wide-quick vA, vB, offset@CCCC */
+ mov r0, rINST, lsr #8 @ r0<- A(+)
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r0, r0, #15
+ GET_VREG(r2, r1) @ r2<- fp[B], the object pointer
+ add r3, rFP, r0, lsl #2 @ r3<- &fp[A]
+ cmp r2, #0 @ check object for null
+ ldmia r3, {r0-r1} @ r0/r1<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH(r3, 1) @ r3<- field byte offset
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ add r2, r2, r3 @ r2<- object + byte offset
+ stmia r2, {r0-r1} @ obj.field (64 bits, aligned)<- r0/r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv4t/OP_IPUT_WIDE_VOLATILE.S b/vm/mterp/armv4t/OP_IPUT_WIDE_VOLATILE.S
new file mode 100644
index 0000000..6b297f0
--- /dev/null
+++ b/vm/mterp/armv4t/OP_IPUT_WIDE_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv4t/OP_IPUT_WIDE.S" {"volatile":"1"}
diff --git a/vm/mterp/armv4t/OP_SGET_WIDE.S b/vm/mterp/armv4t/OP_SGET_WIDE.S
new file mode 100644
index 0000000..91e195d
--- /dev/null
+++ b/vm/mterp/armv4t/OP_SGET_WIDE.S
@@ -0,0 +1,44 @@
+%default {"volatile":"0"}
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+ /*
+ * 64-bit SGET handler.
+ */
+ /* sget-wide vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .L${opcode}_resolve @ yes, do resolve
+.L${opcode}_finish:
+ mov r9, rINST, lsr #8 @ r9<- AA
+ add r0, r0, #offStaticField_value @ r0<- pointer to data
+ .if $volatile
+ bl dvmQuasiAtomicRead64 @ r0/r1<- contents of field
+ .else
+ ldmia r0, {r0-r1} @ r0/r1<- field value (aligned)
+ .endif
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+%break
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ *
+ * Returns StaticField pointer in r0.
+ */
+.L${opcode}_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .L${opcode}_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
diff --git a/vm/mterp/armv4t/OP_SGET_WIDE_VOLATILE.S b/vm/mterp/armv4t/OP_SGET_WIDE_VOLATILE.S
new file mode 100644
index 0000000..5615ab6
--- /dev/null
+++ b/vm/mterp/armv4t/OP_SGET_WIDE_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv4t/OP_SGET_WIDE.S" {"volatile":"1"}
diff --git a/vm/mterp/armv4t/OP_SPUT_WIDE.S b/vm/mterp/armv4t/OP_SPUT_WIDE.S
new file mode 100644
index 0000000..a67e2d8
--- /dev/null
+++ b/vm/mterp/armv4t/OP_SPUT_WIDE.S
@@ -0,0 +1,46 @@
+%default {"volatile":"0"}
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+ /*
+ * 64-bit SPUT handler.
+ */
+ /* sput-wide vAA, field@BBBB */
+ ldr r0, [rGLUE, #offGlue_methodClassDex] @ r0<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r0, [r0, #offDvmDex_pResFields] @ r0<- dvmDex->pResFields
+ mov r9, rINST, lsr #8 @ r9<- AA
+ ldr r2, [r0, r1, lsl #2] @ r2<- resolved StaticField ptr
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ cmp r2, #0 @ is resolved entry null?
+ beq .L${opcode}_resolve @ yes, do resolve
+.L${opcode}_finish: @ field ptr in r2, AA in r9
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ GET_INST_OPCODE(r10) @ extract opcode from rINST
+ add r2, r2, #offStaticField_value @ r2<- pointer to data
+ .if $volatile
+ bl dvmQuasiAtomicSwap64 @ stores r0/r1 into addr r2
+ .else
+ stmia r2, {r0-r1} @ field<- vAA/vAA+1
+ .endif
+ GOTO_OPCODE(r10) @ jump to next instruction
+%break
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ * r9: &fp[AA]
+ *
+ * Returns StaticField pointer in r2.
+ */
+.L${opcode}_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ mov r2, r0 @ copy to r2
+ bne .L${opcode}_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
diff --git a/vm/mterp/armv4t/OP_SPUT_WIDE_VOLATILE.S b/vm/mterp/armv4t/OP_SPUT_WIDE_VOLATILE.S
new file mode 100644
index 0000000..850e83b
--- /dev/null
+++ b/vm/mterp/armv4t/OP_SPUT_WIDE_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv4t/OP_SPUT_WIDE.S" {"volatile":"1"}
diff --git a/vm/mterp/armv4t/platform.S b/vm/mterp/armv4t/platform.S
new file mode 100644
index 0000000..eca940a
--- /dev/null
+++ b/vm/mterp/armv4t/platform.S
@@ -0,0 +1,44 @@
+/*
+ * ===========================================================================
+ * CPU-version-specific defines
+ * ===========================================================================
+ */
+
+/*
+ * Macro for "LDR PC,xxx", which is not allowed pre-ARMv5. Essentially a
+ * one-way branch.
+ *
+ * May modify IP. Does not modify LR.
+ */
+.macro LDR_PC source
+ ldr ip, \source
+ bx ip
+.endm
+
+/*
+ * Macro for "MOV LR,PC / LDR PC,xxx", which is not allowed pre-ARMv5.
+ * Jump to subroutine.
+ *
+ * May modify IP and LR.
+ */
+.macro LDR_PC_LR source
+ ldr ip, \source
+ mov lr, pc
+ bx ip
+.endm
+
+/*
+ * Macro for "LDMFD SP!,{...regs...,PC}".
+ *
+ * May modify IP and LR.
+ */
+.macro LDMFD_PC regs
+ ldmfd sp!, {\regs,lr}
+ bx lr
+.endm
+
+/*
+ * Macro for data memory barrier; not meaningful pre-ARMv6K.
+ */
+.macro SMP_DMB
+.endm
diff --git a/vm/mterp/armv5te/OP_ADD_DOUBLE.S b/vm/mterp/armv5te/OP_ADD_DOUBLE.S
new file mode 100644
index 0000000..de36691
--- /dev/null
+++ b/vm/mterp/armv5te/OP_ADD_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide.S" {"instr":"bl __aeabi_dadd"}
diff --git a/vm/mterp/armv5te/OP_ADD_DOUBLE_2ADDR.S b/vm/mterp/armv5te/OP_ADD_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..744b2ab
--- /dev/null
+++ b/vm/mterp/armv5te/OP_ADD_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide2addr.S" {"instr":"bl __aeabi_dadd"}
diff --git a/vm/mterp/armv5te/OP_ADD_FLOAT.S b/vm/mterp/armv5te/OP_ADD_FLOAT.S
new file mode 100644
index 0000000..badf1f7
--- /dev/null
+++ b/vm/mterp/armv5te/OP_ADD_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"instr":"bl __aeabi_fadd"}
diff --git a/vm/mterp/armv5te/OP_ADD_FLOAT_2ADDR.S b/vm/mterp/armv5te/OP_ADD_FLOAT_2ADDR.S
new file mode 100644
index 0000000..70f1fe8
--- /dev/null
+++ b/vm/mterp/armv5te/OP_ADD_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"instr":"bl __aeabi_fadd"}
diff --git a/vm/mterp/armv5te/OP_ADD_INT.S b/vm/mterp/armv5te/OP_ADD_INT.S
new file mode 100644
index 0000000..97cb5fe
--- /dev/null
+++ b/vm/mterp/armv5te/OP_ADD_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"instr":"add r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_ADD_INT_2ADDR.S b/vm/mterp/armv5te/OP_ADD_INT_2ADDR.S
new file mode 100644
index 0000000..c424f0c
--- /dev/null
+++ b/vm/mterp/armv5te/OP_ADD_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"instr":"add r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_ADD_INT_LIT16.S b/vm/mterp/armv5te/OP_ADD_INT_LIT16.S
new file mode 100644
index 0000000..f5a1603
--- /dev/null
+++ b/vm/mterp/armv5te/OP_ADD_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit16.S" {"instr":"add r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_ADD_INT_LIT8.S b/vm/mterp/armv5te/OP_ADD_INT_LIT8.S
new file mode 100644
index 0000000..93e57d3
--- /dev/null
+++ b/vm/mterp/armv5te/OP_ADD_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit8.S" {"instr":"add r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_ADD_LONG.S b/vm/mterp/armv5te/OP_ADD_LONG.S
new file mode 100644
index 0000000..b30cd05
--- /dev/null
+++ b/vm/mterp/armv5te/OP_ADD_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide.S" {"preinstr":"adds r0, r0, r2", "instr":"adc r1, r1, r3"}
diff --git a/vm/mterp/armv5te/OP_ADD_LONG_2ADDR.S b/vm/mterp/armv5te/OP_ADD_LONG_2ADDR.S
new file mode 100644
index 0000000..ff34ed5
--- /dev/null
+++ b/vm/mterp/armv5te/OP_ADD_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide2addr.S" {"preinstr":"adds r0, r0, r2", "instr":"adc r1, r1, r3"}
diff --git a/vm/mterp/armv5te/OP_AGET.S b/vm/mterp/armv5te/OP_AGET.S
new file mode 100644
index 0000000..8d8ed58
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AGET.S
@@ -0,0 +1,27 @@
+%default { "load":"ldr", "shift":"2" }
+%verify "executed"
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #$shift @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ $load r2, [r0, #offArrayObject_contents] @ r2<- vBB[vCC]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r9) @ vAA<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_AGET_BOOLEAN.S b/vm/mterp/armv5te/OP_AGET_BOOLEAN.S
new file mode 100644
index 0000000..ccce848
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AGET_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_AGET.S" { "load":"ldrb", "shift":"0" }
diff --git a/vm/mterp/armv5te/OP_AGET_BYTE.S b/vm/mterp/armv5te/OP_AGET_BYTE.S
new file mode 100644
index 0000000..c290922
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AGET_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_AGET.S" { "load":"ldrsb", "shift":"0" }
diff --git a/vm/mterp/armv5te/OP_AGET_CHAR.S b/vm/mterp/armv5te/OP_AGET_CHAR.S
new file mode 100644
index 0000000..78c40a0
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AGET_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_AGET.S" { "load":"ldrh", "shift":"1" }
diff --git a/vm/mterp/armv5te/OP_AGET_OBJECT.S b/vm/mterp/armv5te/OP_AGET_OBJECT.S
new file mode 100644
index 0000000..9d64585
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AGET_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_AGET.S"
diff --git a/vm/mterp/armv5te/OP_AGET_SHORT.S b/vm/mterp/armv5te/OP_AGET_SHORT.S
new file mode 100644
index 0000000..77951e6
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AGET_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_AGET.S" { "load":"ldrsh", "shift":"1" }
diff --git a/vm/mterp/armv5te/OP_AGET_WIDE.S b/vm/mterp/armv5te/OP_AGET_WIDE.S
new file mode 100644
index 0000000..6f641dc
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AGET_WIDE.S
@@ -0,0 +1,32 @@
+%verify "executed"
+ /*
+ * Array get, 64 bits. vAA <- vBB[vCC].
+ *
+ * Arrays of long/double are 64-bit aligned, so it's okay to use LDRD.
+ */
+ /* aget-wide vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #3 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcc .L${opcode}_finish @ okay, continue below
+ b common_errArrayIndex @ index >= length, bail
+ @ May want to swap the order of these two branches depending on how the
+ @ branch prediction (if any) handles conditional forward branches vs.
+ @ unconditional forward branches.
+%break
+
+.L${opcode}_finish:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldrd r2, [r0, #offArrayObject_contents] @ r2/r3<- vBB[vCC]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r2-r3} @ vAA/vAA+1<- r2/r3
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_AND_INT.S b/vm/mterp/armv5te/OP_AND_INT.S
new file mode 100644
index 0000000..2a0022e
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AND_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"instr":"and r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_AND_INT_2ADDR.S b/vm/mterp/armv5te/OP_AND_INT_2ADDR.S
new file mode 100644
index 0000000..24203aa
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AND_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"instr":"and r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_AND_INT_LIT16.S b/vm/mterp/armv5te/OP_AND_INT_LIT16.S
new file mode 100644
index 0000000..a06084d
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AND_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit16.S" {"instr":"and r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_AND_INT_LIT8.S b/vm/mterp/armv5te/OP_AND_INT_LIT8.S
new file mode 100644
index 0000000..bd309bb
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AND_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit8.S" {"instr":"and r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_AND_LONG.S b/vm/mterp/armv5te/OP_AND_LONG.S
new file mode 100644
index 0000000..c76bae8
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AND_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide.S" {"preinstr":"and r0, r0, r2", "instr":"and r1, r1, r3"}
diff --git a/vm/mterp/armv5te/OP_AND_LONG_2ADDR.S b/vm/mterp/armv5te/OP_AND_LONG_2ADDR.S
new file mode 100644
index 0000000..6b7c830
--- /dev/null
+++ b/vm/mterp/armv5te/OP_AND_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide2addr.S" {"preinstr":"and r0, r0, r2", "instr":"and r1, r1, r3"}
diff --git a/vm/mterp/armv5te/OP_APUT.S b/vm/mterp/armv5te/OP_APUT.S
new file mode 100644
index 0000000..741aadd
--- /dev/null
+++ b/vm/mterp/armv5te/OP_APUT.S
@@ -0,0 +1,27 @@
+%default { "store":"str", "shift":"2" }
+%verify "executed"
+ /*
+ * Array put, 32 bits or less. vBB[vCC] <- vAA.
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #$shift @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r2, r9) @ r2<- vAA
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ $store r2, [r0, #offArrayObject_contents] @ vBB[vCC]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_APUT_BOOLEAN.S b/vm/mterp/armv5te/OP_APUT_BOOLEAN.S
new file mode 100644
index 0000000..d46650d
--- /dev/null
+++ b/vm/mterp/armv5te/OP_APUT_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_APUT.S" { "store":"strb", "shift":"0" }
diff --git a/vm/mterp/armv5te/OP_APUT_BYTE.S b/vm/mterp/armv5te/OP_APUT_BYTE.S
new file mode 100644
index 0000000..d46650d
--- /dev/null
+++ b/vm/mterp/armv5te/OP_APUT_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_APUT.S" { "store":"strb", "shift":"0" }
diff --git a/vm/mterp/armv5te/OP_APUT_CHAR.S b/vm/mterp/armv5te/OP_APUT_CHAR.S
new file mode 100644
index 0000000..2ff31be
--- /dev/null
+++ b/vm/mterp/armv5te/OP_APUT_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_APUT.S" { "store":"strh", "shift":"1" }
diff --git a/vm/mterp/armv5te/OP_APUT_OBJECT.S b/vm/mterp/armv5te/OP_APUT_OBJECT.S
new file mode 100644
index 0000000..9e98784
--- /dev/null
+++ b/vm/mterp/armv5te/OP_APUT_OBJECT.S
@@ -0,0 +1,51 @@
+%verify "executed"
+ /*
+ * Store an object into an array. vBB[vCC] <- vAA.
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ */
+ /* op vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ GET_VREG(rINST, r2) @ rINST<- vBB (array object)
+ GET_VREG(r0, r3) @ r0<- vCC (requested index)
+ cmp rINST, #0 @ null array object?
+ GET_VREG(r9, r9) @ r9<- vAA
+ beq common_errNullObject @ yes, bail
+ ldr r3, [rINST, #offArrayObject_length] @ r3<- arrayObj->length
+ add r10, rINST, r0, lsl #2 @ r10<- arrayObj + index*width
+ cmp r0, r3 @ compare unsigned index, length
+ bcc .L${opcode}_finish @ we're okay, continue on
+ b common_errArrayIndex @ index >= length, bail
+
+%break
+ /*
+ * On entry:
+ * rINST = vBB (arrayObj)
+ * r9 = vAA (obj)
+ * r10 = offset into array (vBB + vCC * width)
+ */
+.L${opcode}_finish:
+ cmp r9, #0 @ storing null reference?
+ beq .L${opcode}_skip_check @ yes, skip type checks
+ ldr r0, [r9, #offObject_clazz] @ r0<- obj->clazz
+ ldr r1, [rINST, #offObject_clazz] @ r1<- arrayObj->clazz
+ bl dvmCanPutArrayElement @ test object type vs. array type
+ cmp r0, #0 @ okay?
+ beq common_errArrayStore @ no
+ mov r1, rINST @ r1<- arrayObj
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldr r2, [rGLUE, #offGlue_cardTable] @ get biased CT base
+ add r10, #offArrayObject_contents @ r0<- pointer to slot
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ str r9, [r10] @ vBB[vCC]<- vAA
+ strb r2, [r2, r1, lsr #GC_CARD_SHIFT] @ mark card using object head
+ GOTO_OPCODE(ip) @ jump to next instruction
+.L${opcode}_skip_check:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ str r9, [r10, #offArrayObject_contents] @ vBB[vCC]<- vAA
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_APUT_SHORT.S b/vm/mterp/armv5te/OP_APUT_SHORT.S
new file mode 100644
index 0000000..2ff31be
--- /dev/null
+++ b/vm/mterp/armv5te/OP_APUT_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_APUT.S" { "store":"strh", "shift":"1" }
diff --git a/vm/mterp/armv5te/OP_APUT_WIDE.S b/vm/mterp/armv5te/OP_APUT_WIDE.S
new file mode 100644
index 0000000..cc9f332
--- /dev/null
+++ b/vm/mterp/armv5te/OP_APUT_WIDE.S
@@ -0,0 +1,32 @@
+%verify "executed"
+ /*
+ * Array put, 64 bits. vBB[vCC] <- vAA.
+ *
+ * Arrays of long/double are 64-bit aligned, so it's okay to use STRD.
+ */
+ /* aput-wide vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #3 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ bcc .L${opcode}_finish @ okay, continue below
+ b common_errArrayIndex @ index >= length, bail
+ @ May want to swap the order of these two branches depending on how the
+ @ branch prediction (if any) handles conditional forward branches vs.
+ @ unconditional forward branches.
+%break
+
+.L${opcode}_finish:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldmia r9, {r2-r3} @ r2/r3<- vAA/vAA+1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ strd r2, [r0, #offArrayObject_contents] @ r2/r3<- vBB[vCC]
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_ARRAY_LENGTH.S b/vm/mterp/armv5te/OP_ARRAY_LENGTH.S
new file mode 100644
index 0000000..3a6faf3
--- /dev/null
+++ b/vm/mterp/armv5te/OP_ARRAY_LENGTH.S
@@ -0,0 +1,15 @@
+%verify "executed"
+ /*
+ * Return the length of an array.
+ */
+ mov r1, rINST, lsr #12 @ r1<- B
+ mov r2, rINST, lsr #8 @ r2<- A+
+ GET_VREG(r0, r1) @ r0<- vB (object ref)
+ and r2, r2, #15 @ r2<- A
+ cmp r0, #0 @ is object null?
+ beq common_errNullObject @ yup, fail
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ ldr r3, [r0, #offArrayObject_length] @ r3<- array length
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r3, r2) @ vB<- length
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_BREAKPOINT.S b/vm/mterp/armv5te/OP_BREAKPOINT.S
new file mode 100644
index 0000000..faa7246
--- /dev/null
+++ b/vm/mterp/armv5te/OP_BREAKPOINT.S
@@ -0,0 +1 @@
+%include "armv5te/unused.S"
diff --git a/vm/mterp/armv5te/OP_CHECK_CAST.S b/vm/mterp/armv5te/OP_CHECK_CAST.S
new file mode 100644
index 0000000..c711276
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CHECK_CAST.S
@@ -0,0 +1,72 @@
+%verify "executed"
+%verify "null object"
+%verify "class cast exception thrown, with correct class name"
+%verify "class cast exception not thrown on same class"
+%verify "class cast exception not thrown on subclass"
+%verify "class not resolved"
+%verify "class already resolved"
+ /*
+ * Check to see if a cast from one class to another is allowed.
+ */
+ /* check-cast vAA, class@BBBB */
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH(r2, 1) @ r2<- BBBB
+ GET_VREG(r9, r3) @ r9<- object
+ ldr r0, [rGLUE, #offGlue_methodClassDex] @ r0<- pDvmDex
+ cmp r9, #0 @ is object null?
+ ldr r0, [r0, #offDvmDex_pResClasses] @ r0<- pDvmDex->pResClasses
+ beq .L${opcode}_okay @ null obj, cast always succeeds
+ ldr r1, [r0, r2, lsl #2] @ r1<- resolved class
+ ldr r0, [r9, #offObject_clazz] @ r0<- obj->clazz
+ cmp r1, #0 @ have we resolved this before?
+ beq .L${opcode}_resolve @ not resolved, do it now
+.L${opcode}_resolved:
+ cmp r0, r1 @ same class (trivial success)?
+ bne .L${opcode}_fullcheck @ no, do full check
+.L${opcode}_okay:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+%break
+
+ /*
+ * Trivial test failed, need to perform full check. This is common.
+ * r0 holds obj->clazz
+ * r1 holds class resolved from BBBB
+ * r9 holds object
+ */
+.L${opcode}_fullcheck:
+ bl dvmInstanceofNonTrivial @ r0<- boolean result
+ cmp r0, #0 @ failed?
+ bne .L${opcode}_okay @ no, success
+
+ @ A cast has failed. We need to throw a ClassCastException with the
+ @ class of the object that failed to be cast.
+ EXPORT_PC() @ about to throw
+ ldr r3, [r9, #offObject_clazz] @ r3<- obj->clazz
+ ldr r0, .LstrClassCastExceptionPtr
+ ldr r1, [r3, #offClassObject_descriptor] @ r1<- obj->clazz->descriptor
+ bl dvmThrowExceptionWithClassMessage
+ b common_exceptionThrown
+
+ /*
+ * Resolution required. This is the least-likely path.
+ *
+ * r2 holds BBBB
+ * r9 holds object
+ */
+.L${opcode}_resolve:
+ EXPORT_PC() @ resolve() could throw
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ mov r1, r2 @ r1<- BBBB
+ mov r2, #0 @ r2<- false
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- resolved ClassObject ptr
+ cmp r0, #0 @ got null?
+ beq common_exceptionThrown @ yes, handle exception
+ mov r1, r0 @ r1<- class resolved from BBB
+ ldr r0, [r9, #offObject_clazz] @ r0<- obj->clazz
+ b .L${opcode}_resolved @ pick up where we left off
+
+.LstrClassCastExceptionPtr:
+ .word .LstrClassCastException
diff --git a/vm/mterp/armv5te/OP_CMPG_DOUBLE.S b/vm/mterp/armv5te/OP_CMPG_DOUBLE.S
new file mode 100644
index 0000000..a18c186
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CMPG_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_CMPL_DOUBLE.S" { "naninst":"mov r1, #1" }
diff --git a/vm/mterp/armv5te/OP_CMPG_FLOAT.S b/vm/mterp/armv5te/OP_CMPG_FLOAT.S
new file mode 100644
index 0000000..3f87470
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CMPG_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_CMPL_FLOAT.S" { "naninst":"mov r1, #1" }
diff --git a/vm/mterp/armv5te/OP_CMPL_DOUBLE.S b/vm/mterp/armv5te/OP_CMPL_DOUBLE.S
new file mode 100644
index 0000000..01a63f7
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CMPL_DOUBLE.S
@@ -0,0 +1,48 @@
+%default { "naninst":"mvn r1, #0" }
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+ * on what value we'd like to return when one of the operands is NaN.
+ *
+ * See OP_CMPL_FLOAT for an explanation.
+ *
+ * For: cmpl-double, cmpg-double
+ */
+ /* op vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ and r9, r0, #255 @ r9<- BB
+ mov r10, r0, lsr #8 @ r10<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[BB]
+ add r10, rFP, r10, lsl #2 @ r10<- &fp[CC]
+ ldmia r9, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r10, {r2-r3} @ r2/r3<- vCC/vCC+1
+ bl __aeabi_cdcmple @ cmp <=: C clear if <, Z set if eq
+ bhi .L${opcode}_gt_or_nan @ C set and Z clear, disambiguate
+ mvncc r1, #0 @ (less than) r1<- -1
+ moveq r1, #0 @ (equal) r1<- 0, trumps less than
+.L${opcode}_finish:
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r3) @ vAA<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+%break
+
+ @ Test for NaN with a second comparison. EABI forbids testing bit
+ @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+ @ make the library call.
+.L${opcode}_gt_or_nan:
+ ldmia r10, {r0-r1} @ reverse order
+ ldmia r9, {r2-r3}
+ bl __aeabi_cdcmple @ r0<- Z set if eq, C clear if <
+ @bleq common_abort
+ movcc r1, #1 @ (greater than) r1<- 1
+ bcc .L${opcode}_finish
+ $naninst @ r1<- 1 or -1 for NaN
+ b .L${opcode}_finish
diff --git a/vm/mterp/armv5te/OP_CMPL_FLOAT.S b/vm/mterp/armv5te/OP_CMPL_FLOAT.S
new file mode 100644
index 0000000..657f0dc
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CMPL_FLOAT.S
@@ -0,0 +1,115 @@
+%default { "naninst":"mvn r1, #0" }
+%verify "executed"
+%verify "basic lt, gt, eq */
+%verify "left arg NaN"
+%verify "right arg NaN"
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+ * on what value we'd like to return when one of the operands is NaN.
+ *
+ * The operation we're implementing is:
+ * if (x == y)
+ * return 0;
+ * else if (x < y)
+ * return -1;
+ * else if (x > y)
+ * return 1;
+ * else
+ * return {-1,1}; // one or both operands was NaN
+ *
+ * The straightforward implementation requires 3 calls to functions
+ * that return a result in r0. We can do it with two calls if our
+ * EABI library supports __aeabi_cfcmple (only one if we want to check
+ * for NaN directly):
+ * check x <= y
+ * if <, return -1
+ * if ==, return 0
+ * check y <= x
+ * if <, return 1
+ * return {-1,1}
+ *
+ * for: cmpl-float, cmpg-float
+ */
+ /* op vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ GET_VREG(r9, r2) @ r9<- vBB
+ GET_VREG(r10, r3) @ r10<- vCC
+ mov r0, r9 @ copy to arg registers
+ mov r1, r10
+ bl __aeabi_cfcmple @ cmp <=: C clear if <, Z set if eq
+ bhi .L${opcode}_gt_or_nan @ C set and Z clear, disambiguate
+ mvncc r1, #0 @ (less than) r1<- -1
+ moveq r1, #0 @ (equal) r1<- 0, trumps less than
+.L${opcode}_finish:
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r3) @ vAA<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+%break
+
+ @ Test for NaN with a second comparison. EABI forbids testing bit
+ @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+ @ make the library call.
+.L${opcode}_gt_or_nan:
+ mov r1, r9 @ reverse order
+ mov r0, r10
+ bl __aeabi_cfcmple @ r0<- Z set if eq, C clear if <
+ @bleq common_abort
+ movcc r1, #1 @ (greater than) r1<- 1
+ bcc .L${opcode}_finish
+ $naninst @ r1<- 1 or -1 for NaN
+ b .L${opcode}_finish
+
+
+#if 0 /* "clasic" form */
+ FETCH(r0, 1) @ r0<- CCBB
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ GET_VREG(r9, r2) @ r9<- vBB
+ GET_VREG(r10, r3) @ r10<- vCC
+ mov r0, r9 @ r0<- vBB
+ mov r1, r10 @ r1<- vCC
+ bl __aeabi_fcmpeq @ r0<- (vBB == vCC)
+ cmp r0, #0 @ equal?
+ movne r1, #0 @ yes, result is 0
+ bne ${opcode}_finish
+ mov r0, r9 @ r0<- vBB
+ mov r1, r10 @ r1<- vCC
+ bl __aeabi_fcmplt @ r0<- (vBB < vCC)
+ cmp r0, #0 @ less than?
+ b ${opcode}_continue
+@%break
+
+${opcode}_continue:
+ mvnne r1, #0 @ yes, result is -1
+ bne ${opcode}_finish
+ mov r0, r9 @ r0<- vBB
+ mov r1, r10 @ r1<- vCC
+ bl __aeabi_fcmpgt @ r0<- (vBB > vCC)
+ cmp r0, #0 @ greater than?
+ beq ${opcode}_nan @ no, must be NaN
+ mov r1, #1 @ yes, result is 1
+ @ fall through to _finish
+
+${opcode}_finish:
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r3) @ vAA<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+ /*
+ * This is expected to be uncommon, so we double-branch (once to here,
+ * again back to _finish).
+ */
+${opcode}_nan:
+ $naninst @ r1<- 1 or -1 for NaN
+ b ${opcode}_finish
+
+#endif
diff --git a/vm/mterp/armv5te/OP_CMP_LONG.S b/vm/mterp/armv5te/OP_CMP_LONG.S
new file mode 100644
index 0000000..084a3f2
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CMP_LONG.S
@@ -0,0 +1,60 @@
+%verify "executed"
+%verify "basic lt, gt, eq"
+%verify "hi equal, lo <=>"
+%verify "lo equal, hi <=>"
+ /*
+ * Compare two 64-bit values. Puts 0, 1, or -1 into the destination
+ * register based on the results of the comparison.
+ *
+ * We load the full values with LDM, but in practice many values could
+ * be resolved by only looking at the high word. This could be made
+ * faster or slower by splitting the LDM into a pair of LDRs.
+ *
+ * If we just wanted to set condition flags, we could do this:
+ * subs ip, r0, r2
+ * sbcs ip, r1, r3
+ * subeqs ip, r0, r2
+ * Leaving { <0, 0, >0 } in ip. However, we have to set it to a specific
+ * integer value, which we can do with 2 conditional mov/mvn instructions
+ * (set 1, set -1; if they're equal we already have 0 in ip), giving
+ * us a constant 5-cycle path plus a branch at the end to the
+ * instruction epilogue code. The multi-compare approach below needs
+ * 2 or 3 cycles + branch if the high word doesn't match, 6 + branch
+ * in the worst case (the 64-bit values are equal).
+ */
+ /* cmp-long vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ cmp r1, r3 @ compare (vBB+1, vCC+1)
+ blt .L${opcode}_less @ signed compare on high part
+ bgt .L${opcode}_greater
+ subs r1, r0, r2 @ r1<- r0 - r2
+ bhi .L${opcode}_greater @ unsigned compare on low part
+ bne .L${opcode}_less
+ b .L${opcode}_finish @ equal; r1 already holds 0
+%break
+
+.L${opcode}_less:
+ mvn r1, #0 @ r1<- -1
+ @ Want to cond code the next mov so we can avoid branch, but don't see it;
+ @ instead, we just replicate the tail end.
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r9) @ vAA<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+.L${opcode}_greater:
+ mov r1, #1 @ r1<- 1
+ @ fall through to _finish
+
+.L${opcode}_finish:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r9) @ vAA<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_CONST.S b/vm/mterp/armv5te/OP_CONST.S
new file mode 100644
index 0000000..a813c52
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CONST.S
@@ -0,0 +1,10 @@
+%verify "executed"
+ /* const vAA, #+BBBBbbbb */
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH(r0, 1) @ r0<- bbbb (low)
+ FETCH(r1, 2) @ r1<- BBBB (high)
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ orr r0, r0, r1, lsl #16 @ r0<- BBBBbbbb
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r3) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_CONST_16.S b/vm/mterp/armv5te/OP_CONST_16.S
new file mode 100644
index 0000000..b5654a3
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CONST_16.S
@@ -0,0 +1,8 @@
+%verify "executed"
+ /* const/16 vAA, #+BBBB */
+ FETCH_S(r0, 1) @ r0<- ssssBBBB (sign-extended)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r0, r3) @ vAA<- r0
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_CONST_4.S b/vm/mterp/armv5te/OP_CONST_4.S
new file mode 100644
index 0000000..6dd53af
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CONST_4.S
@@ -0,0 +1,10 @@
+%verify "executed"
+ /* const/4 vA, #+B */
+ mov r1, rINST, lsl #16 @ r1<- Bxxx0000
+ mov r0, rINST, lsr #8 @ r0<- A+
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ mov r1, r1, asr #28 @ r1<- sssssssB (sign-extended)
+ and r0, r0, #15
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ SET_VREG(r1, r0) @ fp[A]<- r1
+ GOTO_OPCODE(ip) @ execute next instruction
diff --git a/vm/mterp/armv5te/OP_CONST_CLASS.S b/vm/mterp/armv5te/OP_CONST_CLASS.S
new file mode 100644
index 0000000..665e582
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CONST_CLASS.S
@@ -0,0 +1,35 @@
+%verify "executed"
+%verify "Class already resolved"
+%verify "Class not yet resolved"
+%verify "Class cannot be resolved"
+ /* const/class vAA, Class@BBBB */
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- glue->methodClassDex
+ mov r9, rINST, lsr #8 @ r9<- AA
+ ldr r2, [r2, #offDvmDex_pResClasses] @ r2<- dvmDex->pResClasses
+ ldr r0, [r2, r1, lsl #2] @ r0<- pResClasses[BBBB]
+ cmp r0, #0 @ not yet resolved?
+ beq .L${opcode}_resolve
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+%break
+
+ /*
+ * Continuation if the Class has not yet been resolved.
+ * r1: BBBB (Class ref)
+ * r9: target register
+ */
+.L${opcode}_resolve:
+ EXPORT_PC()
+ ldr r0, [rGLUE, #offGlue_method] @ r0<- glue->method
+ mov r2, #1 @ r2<- true
+ ldr r0, [r0, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- Class reference
+ cmp r0, #0 @ failed?
+ beq common_exceptionThrown @ yup, handle the exception
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_CONST_HIGH16.S b/vm/mterp/armv5te/OP_CONST_HIGH16.S
new file mode 100644
index 0000000..4536d3a
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CONST_HIGH16.S
@@ -0,0 +1,9 @@
+%verify "executed"
+ /* const/high16 vAA, #+BBBB0000 */
+ FETCH(r0, 1) @ r0<- 0000BBBB (zero-extended)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ mov r0, r0, lsl #16 @ r0<- BBBB0000
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r0, r3) @ vAA<- r0
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_CONST_STRING.S b/vm/mterp/armv5te/OP_CONST_STRING.S
new file mode 100644
index 0000000..2df3fda
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CONST_STRING.S
@@ -0,0 +1,34 @@
+%verify "executed"
+%verify "String already resolved"
+%verify "String not yet resolved"
+%verify "String cannot be resolved"
+ /* const/string vAA, String@BBBB */
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- glue->methodClassDex
+ mov r9, rINST, lsr #8 @ r9<- AA
+ ldr r2, [r2, #offDvmDex_pResStrings] @ r2<- dvmDex->pResStrings
+ ldr r0, [r2, r1, lsl #2] @ r0<- pResStrings[BBBB]
+ cmp r0, #0 @ not yet resolved?
+ beq .L${opcode}_resolve
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+%break
+
+ /*
+ * Continuation if the String has not yet been resolved.
+ * r1: BBBB (String ref)
+ * r9: target register
+ */
+.L${opcode}_resolve:
+ EXPORT_PC()
+ ldr r0, [rGLUE, #offGlue_method] @ r0<- glue->method
+ ldr r0, [r0, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveString @ r0<- String reference
+ cmp r0, #0 @ failed?
+ beq common_exceptionThrown @ yup, handle the exception
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_CONST_STRING_JUMBO.S b/vm/mterp/armv5te/OP_CONST_STRING_JUMBO.S
new file mode 100644
index 0000000..cf9b009
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CONST_STRING_JUMBO.S
@@ -0,0 +1,36 @@
+%verify "executed"
+%verify "String already resolved"
+%verify "String not yet resolved"
+%verify "String cannot be resolved"
+ /* const/string vAA, String@BBBBBBBB */
+ FETCH(r0, 1) @ r0<- bbbb (low)
+ FETCH(r1, 2) @ r1<- BBBB (high)
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- glue->methodClassDex
+ mov r9, rINST, lsr #8 @ r9<- AA
+ ldr r2, [r2, #offDvmDex_pResStrings] @ r2<- dvmDex->pResStrings
+ orr r1, r0, r1, lsl #16 @ r1<- BBBBbbbb
+ ldr r0, [r2, r1, lsl #2] @ r0<- pResStrings[BBBB]
+ cmp r0, #0
+ beq .L${opcode}_resolve
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+%break
+
+ /*
+ * Continuation if the String has not yet been resolved.
+ * r1: BBBBBBBB (String ref)
+ * r9: target register
+ */
+.L${opcode}_resolve:
+ EXPORT_PC()
+ ldr r0, [rGLUE, #offGlue_method] @ r0<- glue->method
+ ldr r0, [r0, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveString @ r0<- String reference
+ cmp r0, #0 @ failed?
+ beq common_exceptionThrown @ yup, handle the exception
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_CONST_WIDE.S b/vm/mterp/armv5te/OP_CONST_WIDE.S
new file mode 100644
index 0000000..b724264
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CONST_WIDE.S
@@ -0,0 +1,14 @@
+%verify "executed"
+ /* const-wide vAA, #+HHHHhhhhBBBBbbbb */
+ FETCH(r0, 1) @ r0<- bbbb (low)
+ FETCH(r1, 2) @ r1<- BBBB (low middle)
+ FETCH(r2, 3) @ r2<- hhhh (high middle)
+ orr r0, r0, r1, lsl #16 @ r0<- BBBBbbbb (low word)
+ FETCH(r3, 4) @ r3<- HHHH (high)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ orr r1, r2, r3, lsl #16 @ r1<- HHHHhhhh (high word)
+ FETCH_ADVANCE_INST(5) @ advance rPC, load rINST
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_CONST_WIDE_16.S b/vm/mterp/armv5te/OP_CONST_WIDE_16.S
new file mode 100644
index 0000000..e3e4149
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CONST_WIDE_16.S
@@ -0,0 +1,10 @@
+%verify "executed"
+ /* const-wide/16 vAA, #+BBBB */
+ FETCH_S(r0, 1) @ r0<- ssssBBBB (sign-extended)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ mov r1, r0, asr #31 @ r1<- ssssssss
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_CONST_WIDE_32.S b/vm/mterp/armv5te/OP_CONST_WIDE_32.S
new file mode 100644
index 0000000..a86e042
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CONST_WIDE_32.S
@@ -0,0 +1,12 @@
+%verify "executed"
+ /* const-wide/32 vAA, #+BBBBbbbb */
+ FETCH(r0, 1) @ r0<- 0000bbbb (low)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH_S(r2, 2) @ r2<- ssssBBBB (high)
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ orr r0, r0, r2, lsl #16 @ r0<- BBBBbbbb
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[AA]
+ mov r1, r0, asr #31 @ r1<- ssssssss
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_CONST_WIDE_HIGH16.S b/vm/mterp/armv5te/OP_CONST_WIDE_HIGH16.S
new file mode 100644
index 0000000..11bf518
--- /dev/null
+++ b/vm/mterp/armv5te/OP_CONST_WIDE_HIGH16.S
@@ -0,0 +1,11 @@
+%verify "executed"
+ /* const-wide/high16 vAA, #+BBBB000000000000 */
+ FETCH(r1, 1) @ r1<- 0000BBBB (zero-extended)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ mov r0, #0 @ r0<- 00000000
+ mov r1, r1, lsl #16 @ r1<- BBBB0000
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_DIV_DOUBLE.S b/vm/mterp/armv5te/OP_DIV_DOUBLE.S
new file mode 100644
index 0000000..2fb085b
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DIV_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide.S" {"instr":"bl __aeabi_ddiv"}
diff --git a/vm/mterp/armv5te/OP_DIV_DOUBLE_2ADDR.S b/vm/mterp/armv5te/OP_DIV_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..2dcef28
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DIV_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide2addr.S" {"instr":"bl __aeabi_ddiv"}
diff --git a/vm/mterp/armv5te/OP_DIV_FLOAT.S b/vm/mterp/armv5te/OP_DIV_FLOAT.S
new file mode 100644
index 0000000..8fc2fa9
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DIV_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"instr":"bl __aeabi_fdiv"}
diff --git a/vm/mterp/armv5te/OP_DIV_FLOAT_2ADDR.S b/vm/mterp/armv5te/OP_DIV_FLOAT_2ADDR.S
new file mode 100644
index 0000000..e2d57dd
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DIV_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"instr":"bl __aeabi_fdiv"}
diff --git a/vm/mterp/armv5te/OP_DIV_INT.S b/vm/mterp/armv5te/OP_DIV_INT.S
new file mode 100644
index 0000000..1e326d9
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DIV_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"instr":"bl __aeabi_idiv","chkzero":"1"}
diff --git a/vm/mterp/armv5te/OP_DIV_INT_2ADDR.S b/vm/mterp/armv5te/OP_DIV_INT_2ADDR.S
new file mode 100644
index 0000000..22c8380
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DIV_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"instr":"bl __aeabi_idiv","chkzero":"1"}
diff --git a/vm/mterp/armv5te/OP_DIV_INT_LIT16.S b/vm/mterp/armv5te/OP_DIV_INT_LIT16.S
new file mode 100644
index 0000000..574594f
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DIV_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit16.S" {"instr":"bl __aeabi_idiv","chkzero":"1"}
diff --git a/vm/mterp/armv5te/OP_DIV_INT_LIT8.S b/vm/mterp/armv5te/OP_DIV_INT_LIT8.S
new file mode 100644
index 0000000..8e56a4a
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DIV_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit8.S" {"instr":"bl __aeabi_idiv","chkzero":"1"}
diff --git a/vm/mterp/armv5te/OP_DIV_LONG.S b/vm/mterp/armv5te/OP_DIV_LONG.S
new file mode 100644
index 0000000..ec5db4a
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DIV_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide.S" {"instr":"bl __aeabi_ldivmod", "chkzero":"1"}
diff --git a/vm/mterp/armv5te/OP_DIV_LONG_2ADDR.S b/vm/mterp/armv5te/OP_DIV_LONG_2ADDR.S
new file mode 100644
index 0000000..4651383
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DIV_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide2addr.S" {"instr":"bl __aeabi_ldivmod", "chkzero":"1"}
diff --git a/vm/mterp/armv5te/OP_DOUBLE_TO_FLOAT.S b/vm/mterp/armv5te/OP_DOUBLE_TO_FLOAT.S
new file mode 100644
index 0000000..f0b1a33
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DOUBLE_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unopNarrower.S" {"instr":"bl __aeabi_d2f"}
diff --git a/vm/mterp/armv5te/OP_DOUBLE_TO_INT.S b/vm/mterp/armv5te/OP_DOUBLE_TO_INT.S
new file mode 100644
index 0000000..3e0a26b
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DOUBLE_TO_INT.S
@@ -0,0 +1,54 @@
+%verify "executed"
+/* EABI appears to have Java-style conversions of +inf/-inf/NaN */
+%include "armv5te/unopNarrower.S" {"instr":"bl __aeabi_d2iz"}
+
+#if 0
+@include "armv5te/unopNarrower.S" {"instr":"bl d2i_doconv"}
+@break
+/*
+ * Convert the double in r0/r1 to an int in r0.
+ *
+ * We have to clip values to int min/max per the specification. The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer. The EABI convert function isn't doing this for us.
+ */
+d2i_doconv:
+ stmfd sp!, {r4, r5, lr} @ save regs
+ mov r2, #0x80000000 @ maxint, as a double (low word)
+ mov r2, r2, asr #9 @ 0xffc00000
+ sub sp, sp, #4 @ align for EABI
+ mvn r3, #0xbe000000 @ maxint, as a double (high word)
+ sub r3, r3, #0x00200000 @ 0x41dfffff
+ mov r4, r0 @ save a copy of r0
+ mov r5, r1 @ and r1
+ bl __aeabi_dcmpge @ is arg >= maxint?
+ cmp r0, #0 @ nonzero == yes
+ mvnne r0, #0x80000000 @ return maxint (0x7fffffff)
+ bne 1f
+
+ mov r0, r4 @ recover arg
+ mov r1, r5
+ mov r3, #0xc1000000 @ minint, as a double (high word)
+ add r3, r3, #0x00e00000 @ 0xc1e00000
+ mov r2, #0 @ minint, as a double (low word)
+ bl __aeabi_dcmple @ is arg <= minint?
+ cmp r0, #0 @ nonzero == yes
+ movne r0, #0x80000000 @ return minint (80000000)
+ bne 1f
+
+ mov r0, r4 @ recover arg
+ mov r1, r5
+ mov r2, r4 @ compare against self
+ mov r3, r5
+ bl __aeabi_dcmpeq @ is arg == self?
+ cmp r0, #0 @ zero == no
+ beq 1f @ return zero for NaN
+
+ mov r0, r4 @ recover arg
+ mov r1, r5
+ bl __aeabi_d2iz @ convert double to int
+
+1:
+ add sp, sp, #4
+ ldmfd sp!, {r4, r5, pc}
+#endif
diff --git a/vm/mterp/armv5te/OP_DOUBLE_TO_LONG.S b/vm/mterp/armv5te/OP_DOUBLE_TO_LONG.S
new file mode 100644
index 0000000..ff0fd2e
--- /dev/null
+++ b/vm/mterp/armv5te/OP_DOUBLE_TO_LONG.S
@@ -0,0 +1,53 @@
+%verify "executed"
+@include "armv5te/unopWide.S" {"instr":"bl __aeabi_d2lz"}
+%include "armv5te/unopWide.S" {"instr":"bl d2l_doconv"}
+
+%break
+/*
+ * Convert the double in r0/r1 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification. The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer. The EABI convert function isn't doing this for us.
+ */
+d2l_doconv:
+ stmfd sp!, {r4, r5, lr} @ save regs
+ mov r3, #0x43000000 @ maxlong, as a double (high word)
+ add r3, #0x00e00000 @ 0x43e00000
+ mov r2, #0 @ maxlong, as a double (low word)
+ sub sp, sp, #4 @ align for EABI
+ mov r4, r0 @ save a copy of r0
+ mov r5, r1 @ and r1
+ bl __aeabi_dcmpge @ is arg >= maxlong?
+ cmp r0, #0 @ nonzero == yes
+ mvnne r0, #0 @ return maxlong (7fffffffffffffff)
+ mvnne r1, #0x80000000
+ bne 1f
+
+ mov r0, r4 @ recover arg
+ mov r1, r5
+ mov r3, #0xc3000000 @ minlong, as a double (high word)
+ add r3, #0x00e00000 @ 0xc3e00000
+ mov r2, #0 @ minlong, as a double (low word)
+ bl __aeabi_dcmple @ is arg <= minlong?
+ cmp r0, #0 @ nonzero == yes
+ movne r0, #0 @ return minlong (8000000000000000)
+ movne r1, #0x80000000
+ bne 1f
+
+ mov r0, r4 @ recover arg
+ mov r1, r5
+ mov r2, r4 @ compare against self
+ mov r3, r5
+ bl __aeabi_dcmpeq @ is arg == self?
+ cmp r0, #0 @ zero == no
+ moveq r1, #0 @ return zero for NaN
+ beq 1f
+
+ mov r0, r4 @ recover arg
+ mov r1, r5
+ bl __aeabi_d2lz @ convert double to long
+
+1:
+ add sp, sp, #4
+ ldmfd sp!, {r4, r5, pc}
diff --git a/vm/mterp/armv5te/OP_EXECUTE_INLINE.S b/vm/mterp/armv5te/OP_EXECUTE_INLINE.S
new file mode 100644
index 0000000..f7ca704
--- /dev/null
+++ b/vm/mterp/armv5te/OP_EXECUTE_INLINE.S
@@ -0,0 +1,60 @@
+%verify "executed"
+%verify "exception handled"
+ /*
+ * Execute a "native inline" instruction.
+ *
+ * We need to call an InlineOp4Func:
+ * bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+ *
+ * The first four args are in r0-r3, pointer to return value storage
+ * is on the stack. The function's return value is a flag that tells
+ * us if an exception was thrown.
+ */
+ /* [opt] execute-inline vAA, {vC, vD, vE, vF}, inline@BBBB */
+ FETCH(r10, 1) @ r10<- BBBB
+ add r1, rGLUE, #offGlue_retval @ r1<- &glue->retval
+ EXPORT_PC() @ can throw
+ sub sp, sp, #8 @ make room for arg, +64 bit align
+ mov r0, rINST, lsr #12 @ r0<- B
+ str r1, [sp] @ push &glue->retval
+ bl .L${opcode}_continue @ make call; will return after
+ add sp, sp, #8 @ pop stack
+ cmp r0, #0 @ test boolean result of inline
+ beq common_exceptionThrown @ returned false, handle exception
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+%break
+
+ /*
+ * Extract args, call function.
+ * r0 = #of args (0-4)
+ * r10 = call index
+ * lr = return addr, above [DO NOT bl out of here w/o preserving LR]
+ *
+ * Other ideas:
+ * - Use a jump table from the main piece to jump directly into the
+ * AND/LDR pairs. Costs a data load, saves a branch.
+ * - Have five separate pieces that do the loading, so we can work the
+ * interleave a little better. Increases code size.
+ */
+.L${opcode}_continue:
+ rsb r0, r0, #4 @ r0<- 4-r0
+ FETCH(r9, 2) @ r9<- FEDC
+ add pc, pc, r0, lsl #3 @ computed goto, 2 instrs each
+ bl common_abort @ (skipped due to ARM prefetch)
+4: and ip, r9, #0xf000 @ isolate F
+ ldr r3, [rFP, ip, lsr #10] @ r3<- vF (shift right 12, left 2)
+3: and ip, r9, #0x0f00 @ isolate E
+ ldr r2, [rFP, ip, lsr #6] @ r2<- vE
+2: and ip, r9, #0x00f0 @ isolate D
+ ldr r1, [rFP, ip, lsr #2] @ r1<- vD
+1: and ip, r9, #0x000f @ isolate C
+ ldr r0, [rFP, ip, lsl #2] @ r0<- vC
+0:
+ ldr r9, .L${opcode}_table @ table of InlineOperation
+ LDR_PC "[r9, r10, lsl #4]" @ sizeof=16, "func" is first entry
+ @ (not reached)
+
+.L${opcode}_table:
+ .word gDvmInlineOpsTable
diff --git a/vm/mterp/armv5te/OP_EXECUTE_INLINE_RANGE.S b/vm/mterp/armv5te/OP_EXECUTE_INLINE_RANGE.S
new file mode 100644
index 0000000..cf8b151
--- /dev/null
+++ b/vm/mterp/armv5te/OP_EXECUTE_INLINE_RANGE.S
@@ -0,0 +1,55 @@
+%verify "executed"
+%verify "exception handled"
+ /*
+ * Execute a "native inline" instruction, using "/range" semantics.
+ * Same idea as execute-inline, but we get the args differently.
+ *
+ * We need to call an InlineOp4Func:
+ * bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+ *
+ * The first four args are in r0-r3, pointer to return value storage
+ * is on the stack. The function's return value is a flag that tells
+ * us if an exception was thrown.
+ */
+ /* [opt] execute-inline/range {vCCCC..v(CCCC+AA-1)}, inline@BBBB */
+ FETCH(r10, 1) @ r10<- BBBB
+ add r1, rGLUE, #offGlue_retval @ r1<- &glue->retval
+ EXPORT_PC() @ can throw
+ sub sp, sp, #8 @ make room for arg, +64 bit align
+ mov r0, rINST, lsr #8 @ r0<- AA
+ str r1, [sp] @ push &glue->retval
+ bl .L${opcode}_continue @ make call; will return after
+ add sp, sp, #8 @ pop stack
+ cmp r0, #0 @ test boolean result of inline
+ beq common_exceptionThrown @ returned false, handle exception
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+%break
+
+ /*
+ * Extract args, call function.
+ * r0 = #of args (0-4)
+ * r10 = call index
+ * lr = return addr, above [DO NOT bl out of here w/o preserving LR]
+ */
+.L${opcode}_continue:
+ rsb r0, r0, #4 @ r0<- 4-r0
+ FETCH(r9, 2) @ r9<- CCCC
+ add pc, pc, r0, lsl #3 @ computed goto, 2 instrs each
+ bl common_abort @ (skipped due to ARM prefetch)
+4: add ip, r9, #3 @ base+3
+ GET_VREG(r3, ip) @ r3<- vBase[3]
+3: add ip, r9, #2 @ base+2
+ GET_VREG(r2, ip) @ r2<- vBase[2]
+2: add ip, r9, #1 @ base+1
+ GET_VREG(r1, ip) @ r1<- vBase[1]
+1: add ip, r9, #0 @ (nop)
+ GET_VREG(r0, ip) @ r0<- vBase[0]
+0:
+ ldr r9, .L${opcode}_table @ table of InlineOperation
+ LDR_PC "[r9, r10, lsl #4]" @ sizeof=16, "func" is first entry
+ @ (not reached)
+
+.L${opcode}_table:
+ .word gDvmInlineOpsTable
diff --git a/vm/mterp/armv5te/OP_FILLED_NEW_ARRAY.S b/vm/mterp/armv5te/OP_FILLED_NEW_ARRAY.S
new file mode 100644
index 0000000..5bb2d43
--- /dev/null
+++ b/vm/mterp/armv5te/OP_FILLED_NEW_ARRAY.S
@@ -0,0 +1,108 @@
+%default { "isrange":"0" }
+%verify "executed"
+%verify "unimplemented array type"
+ /*
+ * Create a new array with elements filled from registers.
+ *
+ * for: filled-new-array, filled-new-array/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResClasses] @ r3<- pDvmDex->pResClasses
+ EXPORT_PC() @ need for resolve and alloc
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved class
+ mov r10, rINST, lsr #8 @ r10<- AA or BA
+ cmp r0, #0 @ already resolved?
+ bne .L${opcode}_continue @ yes, continue on
+8: ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ mov r2, #0 @ r2<- false
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- call(clazz, ref)
+ cmp r0, #0 @ got null?
+ beq common_exceptionThrown @ yes, handle exception
+ b .L${opcode}_continue
+%break
+
+ /*
+ * On entry:
+ * r0 holds array class
+ * r10 holds AA or BA
+ */
+.L${opcode}_continue:
+ ldr r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor
+ mov r2, #ALLOC_DONT_TRACK @ r2<- alloc flags
+ ldrb rINST, [r3, #1] @ rINST<- descriptor[1]
+ .if $isrange
+ mov r1, r10 @ r1<- AA (length)
+ .else
+ mov r1, r10, lsr #4 @ r1<- B (length)
+ .endif
+ cmp rINST, #'I' @ array of ints?
+ cmpne rINST, #'L' @ array of objects?
+ cmpne rINST, #'[' @ array of arrays?
+ mov r9, r1 @ save length in r9
+ bne .L${opcode}_notimpl @ no, not handled yet
+ bl dvmAllocArrayByClass @ r0<- call(arClass, length, flags)
+ cmp r0, #0 @ null return?
+ beq common_exceptionThrown @ alloc failed, handle exception
+
+ FETCH(r1, 2) @ r1<- FEDC or CCCC
+ str r0, [rGLUE, #offGlue_retval] @ retval.l <- new array
+ str rINST, [rGLUE, #offGlue_retval+4] @ retval.h <- type
+ add r0, r0, #offArrayObject_contents @ r0<- newArray->contents
+ subs r9, r9, #1 @ length--, check for neg
+ FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST
+ bmi 2f @ was zero, bail
+
+ @ copy values from registers into the array
+ @ r0=array, r1=CCCC/FEDC, r9=length (from AA or B), r10=AA/BA
+ .if $isrange
+ add r2, rFP, r1, lsl #2 @ r2<- &fp[CCCC]
+1: ldr r3, [r2], #4 @ r3<- *r2++
+ subs r9, r9, #1 @ count--
+ str r3, [r0], #4 @ *contents++ = vX
+ bpl 1b
+ @ continue at 2
+ .else
+ cmp r9, #4 @ length was initially 5?
+ and r2, r10, #15 @ r2<- A
+ bne 1f @ <= 4 args, branch
+ GET_VREG(r3, r2) @ r3<- vA
+ sub r9, r9, #1 @ count--
+ str r3, [r0, #16] @ contents[4] = vA
+1: and r2, r1, #15 @ r2<- F/E/D/C
+ GET_VREG(r3, r2) @ r3<- vF/vE/vD/vC
+ mov r1, r1, lsr #4 @ r1<- next reg in low 4
+ subs r9, r9, #1 @ count--
+ str r3, [r0], #4 @ *contents++ = vX
+ bpl 1b
+ @ continue at 2
+ .endif
+
+2:
+ ldr r0, [rGLUE, #offGlue_retval] @ r0<- object
+ ldr r1, [rGLUE, #offGlue_retval+4] @ r1<- type
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ cmp r1, #'I' @ Is int array?
+ strneb r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card based on object head
+ GOTO_OPCODE(ip) @ execute it
+
+ /*
+ * Throw an exception indicating that we have not implemented this
+ * mode of filled-new-array.
+ */
+.L${opcode}_notimpl:
+ ldr r0, .L_strInternalError
+ ldr r1, .L_strFilledNewArrayNotImpl
+ bl dvmThrowException
+ b common_exceptionThrown
+
+ .if (!$isrange) @ define in one or the other, not both
+.L_strFilledNewArrayNotImpl:
+ .word .LstrFilledNewArrayNotImpl
+.L_strInternalError:
+ .word .LstrInternalError
+ .endif
diff --git a/vm/mterp/armv5te/OP_FILLED_NEW_ARRAY_RANGE.S b/vm/mterp/armv5te/OP_FILLED_NEW_ARRAY_RANGE.S
new file mode 100644
index 0000000..a2273c4
--- /dev/null
+++ b/vm/mterp/armv5te/OP_FILLED_NEW_ARRAY_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_FILLED_NEW_ARRAY.S" { "isrange":"1" }
diff --git a/vm/mterp/armv5te/OP_FILL_ARRAY_DATA.S b/vm/mterp/armv5te/OP_FILL_ARRAY_DATA.S
new file mode 100644
index 0000000..a0d8399
--- /dev/null
+++ b/vm/mterp/armv5te/OP_FILL_ARRAY_DATA.S
@@ -0,0 +1,15 @@
+%verify "executed"
+ /* fill-array-data vAA, +BBBBBBBB */
+ FETCH(r0, 1) @ r0<- bbbb (lo)
+ FETCH(r1, 2) @ r1<- BBBB (hi)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ orr r1, r0, r1, lsl #16 @ r1<- BBBBbbbb
+ GET_VREG(r0, r3) @ r0<- vAA (array object)
+ add r1, rPC, r1, lsl #1 @ r1<- PC + BBBBbbbb*2 (array data off.)
+ EXPORT_PC();
+ bl dvmInterpHandleFillArrayData@ fill the array with predefined data
+ cmp r0, #0 @ 0 means an exception is thrown
+ beq common_exceptionThrown @ has exception
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_FLOAT_TO_DOUBLE.S b/vm/mterp/armv5te/OP_FLOAT_TO_DOUBLE.S
new file mode 100644
index 0000000..b235e61
--- /dev/null
+++ b/vm/mterp/armv5te/OP_FLOAT_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unopWider.S" {"instr":"bl __aeabi_f2d"}
diff --git a/vm/mterp/armv5te/OP_FLOAT_TO_INT.S b/vm/mterp/armv5te/OP_FLOAT_TO_INT.S
new file mode 100644
index 0000000..c9cb957
--- /dev/null
+++ b/vm/mterp/armv5te/OP_FLOAT_TO_INT.S
@@ -0,0 +1,40 @@
+%verify "executed"
+/* EABI appears to have Java-style conversions of +inf/-inf/NaN */
+%include "armv5te/unop.S" {"instr":"bl __aeabi_f2iz"}
+
+#if 0
+@include "armv5te/unop.S" {"instr":"bl f2i_doconv"}
+@break
+/*
+ * Convert the float in r0 to an int in r0.
+ *
+ * We have to clip values to int min/max per the specification. The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer. The EABI convert function isn't doing this for us.
+ */
+f2i_doconv:
+ stmfd sp!, {r4, lr}
+ mov r1, #0x4f000000 @ (float)maxint
+ mov r4, r0
+ bl __aeabi_fcmpge @ is arg >= maxint?
+ cmp r0, #0 @ nonzero == yes
+ mvnne r0, #0x80000000 @ return maxint (7fffffff)
+ ldmnefd sp!, {r4, pc}
+
+ mov r0, r4 @ recover arg
+ mov r1, #0xcf000000 @ (float)minint
+ bl __aeabi_fcmple @ is arg <= minint?
+ cmp r0, #0 @ nonzero == yes
+ movne r0, #0x80000000 @ return minint (80000000)
+ ldmnefd sp!, {r4, pc}
+
+ mov r0, r4 @ recover arg
+ mov r1, r4
+ bl __aeabi_fcmpeq @ is arg == self?
+ cmp r0, #0 @ zero == no
+ ldmeqfd sp!, {r4, pc} @ return zero for NaN
+
+ mov r0, r4 @ recover arg
+ bl __aeabi_f2iz @ convert float to int
+ ldmfd sp!, {r4, pc}
+#endif
diff --git a/vm/mterp/armv5te/OP_FLOAT_TO_LONG.S b/vm/mterp/armv5te/OP_FLOAT_TO_LONG.S
new file mode 100644
index 0000000..e42e1a4
--- /dev/null
+++ b/vm/mterp/armv5te/OP_FLOAT_TO_LONG.S
@@ -0,0 +1,40 @@
+%verify "executed"
+@include "armv5te/unopWider.S" {"instr":"bl __aeabi_f2lz"}
+%include "armv5te/unopWider.S" {"instr":"bl f2l_doconv"}
+
+%break
+/*
+ * Convert the float in r0 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification. The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer. The EABI convert function isn't doing this for us.
+ */
+f2l_doconv:
+ stmfd sp!, {r4, lr}
+ mov r1, #0x5f000000 @ (float)maxlong
+ mov r4, r0
+ bl __aeabi_fcmpge @ is arg >= maxlong?
+ cmp r0, #0 @ nonzero == yes
+ mvnne r0, #0 @ return maxlong (7fffffff)
+ mvnne r1, #0x80000000
+ ldmnefd sp!, {r4, pc}
+
+ mov r0, r4 @ recover arg
+ mov r1, #0xdf000000 @ (float)minlong
+ bl __aeabi_fcmple @ is arg <= minlong?
+ cmp r0, #0 @ nonzero == yes
+ movne r0, #0 @ return minlong (80000000)
+ movne r1, #0x80000000
+ ldmnefd sp!, {r4, pc}
+
+ mov r0, r4 @ recover arg
+ mov r1, r4
+ bl __aeabi_fcmpeq @ is arg == self?
+ cmp r0, #0 @ zero == no
+ moveq r1, #0 @ return zero for NaN
+ ldmeqfd sp!, {r4, pc}
+
+ mov r0, r4 @ recover arg
+ bl __aeabi_f2lz @ convert float to long
+ ldmfd sp!, {r4, pc}
diff --git a/vm/mterp/armv5te/OP_GOTO.S b/vm/mterp/armv5te/OP_GOTO.S
new file mode 100644
index 0000000..26f0c8f
--- /dev/null
+++ b/vm/mterp/armv5te/OP_GOTO.S
@@ -0,0 +1,25 @@
+%verify "executed"
+%verify "forward and backward"
+ /*
+ * Unconditional branch, 8-bit offset.
+ *
+ * The branch distance is a signed code-unit offset, which we need to
+ * double to get a byte offset.
+ */
+ /* goto +AA */
+ mov r0, rINST, lsl #16 @ r0<- AAxx0000
+ movs r9, r0, asr #24 @ r9<- ssssssAA (sign-extended)
+ mov r9, r9, lsl #1 @ r9<- byte offset
+ bmi common_backwardBranch @ backward branch, do periodic checks
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
diff --git a/vm/mterp/armv5te/OP_GOTO_16.S b/vm/mterp/armv5te/OP_GOTO_16.S
new file mode 100644
index 0000000..5a8edee
--- /dev/null
+++ b/vm/mterp/armv5te/OP_GOTO_16.S
@@ -0,0 +1,24 @@
+%verify "executed"
+%verify "forward and backward"
+ /*
+ * Unconditional branch, 16-bit offset.
+ *
+ * The branch distance is a signed code-unit offset, which we need to
+ * double to get a byte offset.
+ */
+ /* goto/16 +AAAA */
+ FETCH_S(r0, 1) @ r0<- ssssAAAA (sign-extended)
+ movs r9, r0, asl #1 @ r9<- byte offset, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
diff --git a/vm/mterp/armv5te/OP_GOTO_32.S b/vm/mterp/armv5te/OP_GOTO_32.S
new file mode 100644
index 0000000..17780b9
--- /dev/null
+++ b/vm/mterp/armv5te/OP_GOTO_32.S
@@ -0,0 +1,32 @@
+%verify "executed"
+%verify "forward, backward, self"
+ /*
+ * Unconditional branch, 32-bit offset.
+ *
+ * The branch distance is a signed code-unit offset, which we need to
+ * double to get a byte offset.
+ *
+ * Unlike most opcodes, this one is allowed to branch to itself, so
+ * our "backward branch" test must be "<=0" instead of "<0". The ORRS
+ * instruction doesn't affect the V flag, so we need to clear it
+ * explicitly.
+ */
+ /* goto/32 +AAAAAAAA */
+ FETCH(r0, 1) @ r0<- aaaa (lo)
+ FETCH(r1, 2) @ r1<- AAAA (hi)
+ cmp ip, ip @ (clear V flag during stall)
+ orrs r0, r0, r1, lsl #16 @ r0<- AAAAaaaa, check sign
+ mov r9, r0, asl #1 @ r9<- byte offset
+ ble common_backwardBranch @ backward branch, do periodic checks
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
diff --git a/vm/mterp/armv5te/OP_IF_EQ.S b/vm/mterp/armv5te/OP_IF_EQ.S
new file mode 100644
index 0000000..ecd2c55
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IF_EQ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/bincmp.S" { "revcmp":"ne" }
diff --git a/vm/mterp/armv5te/OP_IF_EQZ.S b/vm/mterp/armv5te/OP_IF_EQZ.S
new file mode 100644
index 0000000..2011a03
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IF_EQZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/zcmp.S" { "revcmp":"ne" }
diff --git a/vm/mterp/armv5te/OP_IF_GE.S b/vm/mterp/armv5te/OP_IF_GE.S
new file mode 100644
index 0000000..7424939
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IF_GE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/bincmp.S" { "revcmp":"lt" }
diff --git a/vm/mterp/armv5te/OP_IF_GEZ.S b/vm/mterp/armv5te/OP_IF_GEZ.S
new file mode 100644
index 0000000..2cae521
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IF_GEZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/zcmp.S" { "revcmp":"lt" }
diff --git a/vm/mterp/armv5te/OP_IF_GT.S b/vm/mterp/armv5te/OP_IF_GT.S
new file mode 100644
index 0000000..553a74a
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IF_GT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/bincmp.S" { "revcmp":"le" }
diff --git a/vm/mterp/armv5te/OP_IF_GTZ.S b/vm/mterp/armv5te/OP_IF_GTZ.S
new file mode 100644
index 0000000..c82911f
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IF_GTZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/zcmp.S" { "revcmp":"le" }
diff --git a/vm/mterp/armv5te/OP_IF_LE.S b/vm/mterp/armv5te/OP_IF_LE.S
new file mode 100644
index 0000000..dd99709
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IF_LE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/bincmp.S" { "revcmp":"gt" }
diff --git a/vm/mterp/armv5te/OP_IF_LEZ.S b/vm/mterp/armv5te/OP_IF_LEZ.S
new file mode 100644
index 0000000..382c34d
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IF_LEZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/zcmp.S" { "revcmp":"gt" }
diff --git a/vm/mterp/armv5te/OP_IF_LT.S b/vm/mterp/armv5te/OP_IF_LT.S
new file mode 100644
index 0000000..34dc445
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IF_LT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/bincmp.S" { "revcmp":"ge" }
diff --git a/vm/mterp/armv5te/OP_IF_LTZ.S b/vm/mterp/armv5te/OP_IF_LTZ.S
new file mode 100644
index 0000000..6a0432f
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IF_LTZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/zcmp.S" { "revcmp":"ge" }
diff --git a/vm/mterp/armv5te/OP_IF_NE.S b/vm/mterp/armv5te/OP_IF_NE.S
new file mode 100644
index 0000000..950c916
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IF_NE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/bincmp.S" { "revcmp":"eq" }
diff --git a/vm/mterp/armv5te/OP_IF_NEZ.S b/vm/mterp/armv5te/OP_IF_NEZ.S
new file mode 100644
index 0000000..226549c
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IF_NEZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/zcmp.S" { "revcmp":"eq" }
diff --git a/vm/mterp/armv5te/OP_IGET.S b/vm/mterp/armv5te/OP_IGET.S
new file mode 100644
index 0000000..b9cdee4
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET.S
@@ -0,0 +1,47 @@
+%default { "load":"ldr", "barrier":"@ no-op ", "sqnum":"0" }
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .L${opcode}_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .L${opcode}_finish
+ b common_exceptionThrown
+%break
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.L${opcode}_finish:
+ @bl common_squeak${sqnum}
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ $load r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ $barrier @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_IGET_BOOLEAN.S b/vm/mterp/armv5te/OP_IGET_BOOLEAN.S
new file mode 100644
index 0000000..0fbd0aa
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET_BOOLEAN.S
@@ -0,0 +1,3 @@
+%verify "executed"
+@include "armv5te/OP_IGET.S" { "load":"ldrb", "sqnum":"1" }
+%include "armv5te/OP_IGET.S" { "load":"ldr", "sqnum":"1" }
diff --git a/vm/mterp/armv5te/OP_IGET_BYTE.S b/vm/mterp/armv5te/OP_IGET_BYTE.S
new file mode 100644
index 0000000..ef8fdfc
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET_BYTE.S
@@ -0,0 +1,4 @@
+%verify "executed"
+%verify "negative value is sign-extended"
+@include "armv5te/OP_IGET.S" { "load":"ldrsb", "sqnum":"2" }
+%include "armv5te/OP_IGET.S" { "load":"ldr", "sqnum":"2" }
diff --git a/vm/mterp/armv5te/OP_IGET_CHAR.S b/vm/mterp/armv5te/OP_IGET_CHAR.S
new file mode 100644
index 0000000..b88b017
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET_CHAR.S
@@ -0,0 +1,4 @@
+%verify "executed"
+%verify "large values are not sign-extended"
+@include "armv5te/OP_IGET.S" { "load":"ldrh", "sqnum":"3" }
+%include "armv5te/OP_IGET.S" { "load":"ldr", "sqnum":"3" }
diff --git a/vm/mterp/armv5te/OP_IGET_OBJECT.S b/vm/mterp/armv5te/OP_IGET_OBJECT.S
new file mode 100644
index 0000000..e5ca05f
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_IGET.S"
diff --git a/vm/mterp/armv5te/OP_IGET_OBJECT_QUICK.S b/vm/mterp/armv5te/OP_IGET_OBJECT_QUICK.S
new file mode 100644
index 0000000..727b2aa
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET_OBJECT_QUICK.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_IGET_QUICK.S"
diff --git a/vm/mterp/armv5te/OP_IGET_OBJECT_VOLATILE.S b/vm/mterp/armv5te/OP_IGET_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..acf9ac0
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET_OBJECT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_IGET.S" {"barrier":"SMP_DMB"}
diff --git a/vm/mterp/armv5te/OP_IGET_QUICK.S b/vm/mterp/armv5te/OP_IGET_QUICK.S
new file mode 100644
index 0000000..c19f870
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET_QUICK.S
@@ -0,0 +1,16 @@
+%verify "executed"
+%verify "null object"
+ /* For: iget-quick, iget-object-quick */
+ /* op vA, vB, offset@CCCC */
+ mov r2, rINST, lsr #12 @ r2<- B
+ GET_VREG(r3, r2) @ r3<- object we're operating on
+ FETCH(r1, 1) @ r1<- field byte offset
+ cmp r3, #0 @ check object for null
+ mov r2, rINST, lsr #8 @ r2<- A(+)
+ beq common_errNullObject @ object was null
+ ldr r0, [r3, r1] @ r0<- obj.field (always 32 bits)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_IGET_SHORT.S b/vm/mterp/armv5te/OP_IGET_SHORT.S
new file mode 100644
index 0000000..a1b60b1
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET_SHORT.S
@@ -0,0 +1,4 @@
+%verify "executed"
+%verify "negative value is sign-extended"
+@include "armv5te/OP_IGET.S" { "load":"ldrsh", "sqnum":"4" }
+%include "armv5te/OP_IGET.S" { "load":"ldr", "sqnum":"4" }
diff --git a/vm/mterp/armv5te/OP_IGET_VOLATILE.S b/vm/mterp/armv5te/OP_IGET_VOLATILE.S
new file mode 100644
index 0000000..acf9ac0
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_IGET.S" {"barrier":"SMP_DMB"}
diff --git a/vm/mterp/armv5te/OP_IGET_WIDE.S b/vm/mterp/armv5te/OP_IGET_WIDE.S
new file mode 100644
index 0000000..95944de
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET_WIDE.S
@@ -0,0 +1,49 @@
+%default {"volatile":"0"}
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+ /*
+ * Wide 32-bit instance field get.
+ */
+ /* iget-wide vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .L${opcode}_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .L${opcode}_finish
+ b common_exceptionThrown
+%break
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.L${opcode}_finish:
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ .if $volatile
+ add r0, r9, r3 @ r0<- address of field
+ bl dvmQuasiAtomicRead64 @ r0/r1<- contents of field
+ .else
+ ldrd r0, [r9, r3] @ r0/r1<- obj.field (64-bit align ok)
+ .endif
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ add r3, rFP, r2, lsl #2 @ r3<- &fp[A]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ fp[A]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_IGET_WIDE_QUICK.S b/vm/mterp/armv5te/OP_IGET_WIDE_QUICK.S
new file mode 100644
index 0000000..b32e429
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET_WIDE_QUICK.S
@@ -0,0 +1,16 @@
+%verify "executed"
+%verify "null object"
+ /* iget-wide-quick vA, vB, offset@CCCC */
+ mov r2, rINST, lsr #12 @ r2<- B
+ GET_VREG(r3, r2) @ r3<- object we're operating on
+ FETCH(ip, 1) @ ip<- field byte offset
+ cmp r3, #0 @ check object for null
+ mov r2, rINST, lsr #8 @ r2<- A(+)
+ beq common_errNullObject @ object was null
+ ldrd r0, [r3, ip] @ r0<- obj.field (64 bits, aligned)
+ and r2, r2, #15
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ add r3, rFP, r2, lsl #2 @ r3<- &fp[A]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ fp[A]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_IGET_WIDE_VOLATILE.S b/vm/mterp/armv5te/OP_IGET_WIDE_VOLATILE.S
new file mode 100644
index 0000000..face363
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IGET_WIDE_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_IGET_WIDE.S" {"volatile":"1"}
diff --git a/vm/mterp/armv5te/OP_INSTANCE_OF.S b/vm/mterp/armv5te/OP_INSTANCE_OF.S
new file mode 100644
index 0000000..66f0df3
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INSTANCE_OF.S
@@ -0,0 +1,85 @@
+%verify "executed"
+%verify "null object"
+%verify "class cast exception thrown, with correct class name"
+%verify "class cast exception not thrown on same class"
+%verify "class cast exception not thrown on subclass"
+%verify "class not resolved"
+%verify "class already resolved"
+ /*
+ * Check to see if an object reference is an instance of a class.
+ *
+ * Most common situation is a non-null object, being compared against
+ * an already-resolved class.
+ */
+ /* instance-of vA, vB, class@CCCC */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r3) @ r0<- vB (object)
+ and r9, r9, #15 @ r9<- A
+ cmp r0, #0 @ is object null?
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- pDvmDex
+ beq .L${opcode}_store @ null obj, not an instance, store r0
+ FETCH(r3, 1) @ r3<- CCCC
+ ldr r2, [r2, #offDvmDex_pResClasses] @ r2<- pDvmDex->pResClasses
+ ldr r1, [r2, r3, lsl #2] @ r1<- resolved class
+ ldr r0, [r0, #offObject_clazz] @ r0<- obj->clazz
+ cmp r1, #0 @ have we resolved this before?
+ beq .L${opcode}_resolve @ not resolved, do it now
+.L${opcode}_resolved: @ r0=obj->clazz, r1=resolved class
+ cmp r0, r1 @ same class (trivial success)?
+ beq .L${opcode}_trivial @ yes, trivial finish
+ b .L${opcode}_fullcheck @ no, do full check
+%break
+
+ /*
+ * Trivial test failed, need to perform full check. This is common.
+ * r0 holds obj->clazz
+ * r1 holds class resolved from BBBB
+ * r9 holds A
+ */
+.L${opcode}_fullcheck:
+ bl dvmInstanceofNonTrivial @ r0<- boolean result
+ @ fall through to ${opcode}_store
+
+ /*
+ * r0 holds boolean result
+ * r9 holds A
+ */
+.L${opcode}_store:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r0, r9) @ vA<- r0
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+ /*
+ * Trivial test succeeded, save and bail.
+ * r9 holds A
+ */
+.L${opcode}_trivial:
+ mov r0, #1 @ indicate success
+ @ could b ${opcode}_store, but copying is faster and cheaper
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r0, r9) @ vA<- r0
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+ /*
+ * Resolution required. This is the least-likely path.
+ *
+ * r3 holds BBBB
+ * r9 holds A
+ */
+.L${opcode}_resolve:
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [rGLUE, #offGlue_method] @ r0<- glue->method
+ mov r1, r3 @ r1<- BBBB
+ mov r2, #1 @ r2<- true
+ ldr r0, [r0, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- resolved ClassObject ptr
+ cmp r0, #0 @ got null?
+ beq common_exceptionThrown @ yes, handle exception
+ mov r1, r0 @ r1<- class resolved from BBB
+ mov r3, rINST, lsr #12 @ r3<- B
+ GET_VREG(r0, r3) @ r0<- vB (object)
+ ldr r0, [r0, #offObject_clazz] @ r0<- obj->clazz
+ b .L${opcode}_resolved @ pick up where we left off
diff --git a/vm/mterp/armv5te/OP_INT_TO_BYTE.S b/vm/mterp/armv5te/OP_INT_TO_BYTE.S
new file mode 100644
index 0000000..cf1c981
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INT_TO_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unop.S" {"preinstr":"mov r0, r0, asl #24", "instr":"mov r0, r0, asr #24"}
diff --git a/vm/mterp/armv5te/OP_INT_TO_CHAR.S b/vm/mterp/armv5te/OP_INT_TO_CHAR.S
new file mode 100644
index 0000000..568cf08
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INT_TO_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unop.S" {"preinstr":"mov r0, r0, asl #16", "instr":"mov r0, r0, lsr #16"}
diff --git a/vm/mterp/armv5te/OP_INT_TO_DOUBLE.S b/vm/mterp/armv5te/OP_INT_TO_DOUBLE.S
new file mode 100644
index 0000000..8b00b64
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INT_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unopWider.S" {"instr":"bl __aeabi_i2d"}
diff --git a/vm/mterp/armv5te/OP_INT_TO_FLOAT.S b/vm/mterp/armv5te/OP_INT_TO_FLOAT.S
new file mode 100644
index 0000000..53d49df
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INT_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unop.S" {"instr":"bl __aeabi_i2f"}
diff --git a/vm/mterp/armv5te/OP_INT_TO_LONG.S b/vm/mterp/armv5te/OP_INT_TO_LONG.S
new file mode 100644
index 0000000..b744439
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INT_TO_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unopWider.S" {"instr":"mov r1, r0, asr #31"}
diff --git a/vm/mterp/armv5te/OP_INT_TO_SHORT.S b/vm/mterp/armv5te/OP_INT_TO_SHORT.S
new file mode 100644
index 0000000..b6deb85
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INT_TO_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unop.S" {"preinstr":"mov r0, r0, asl #16", "instr":"mov r0, r0, asr #16"}
diff --git a/vm/mterp/armv5te/OP_INVOKE_DIRECT.S b/vm/mterp/armv5te/OP_INVOKE_DIRECT.S
new file mode 100644
index 0000000..14ba8f7
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_DIRECT.S
@@ -0,0 +1,47 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+ /*
+ * Handle a direct method call.
+ *
+ * (We could defer the "is 'this' pointer null" test to the common
+ * method invocation code, and use a flag to indicate that static
+ * calls don't count. If we do this as part of copying the arguments
+ * out we could avoiding loading the first arg twice.)
+ *
+ * for: invoke-direct, invoke-direct/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved methodToCall
+ .if (!$isrange)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ must export for invoke
+ GET_VREG(r2, r10) @ r2<- "this" ptr
+ beq .L${opcode}_resolve @ not resolved, do it now
+.L${opcode}_finish:
+ cmp r2, #0 @ null "this" ref?
+ bne common_invokeMethod${routine} @ no, continue on
+ b common_errNullObject @ yes, throw exception
+%break
+
+ /*
+ * On entry:
+ * r1 = reference (BBBB or CCCC)
+ * r10 = "this" register
+ */
+.L${opcode}_resolve:
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ mov r2, #METHOD_DIRECT @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ GET_VREG(r2, r10) @ r2<- "this" ptr (reload)
+ bne .L${opcode}_finish @ no, continue
+ b common_exceptionThrown @ yes, handle exception
diff --git a/vm/mterp/armv5te/OP_INVOKE_DIRECT_EMPTY.S b/vm/mterp/armv5te/OP_INVOKE_DIRECT_EMPTY.S
new file mode 100644
index 0000000..3c6b192
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_DIRECT_EMPTY.S
@@ -0,0 +1,7 @@
+%verify "executed"
+ /*
+ * invoke-direct-empty is a no-op in a "standard" interpreter.
+ */
+ FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ GOTO_OPCODE(ip) @ execute it
diff --git a/vm/mterp/armv5te/OP_INVOKE_DIRECT_RANGE.S b/vm/mterp/armv5te/OP_INVOKE_DIRECT_RANGE.S
new file mode 100644
index 0000000..0b799e5
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_DIRECT_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_INVOKE_DIRECT.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/armv5te/OP_INVOKE_INTERFACE.S b/vm/mterp/armv5te/OP_INVOKE_INTERFACE.S
new file mode 100644
index 0000000..7d52454
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_INTERFACE.S
@@ -0,0 +1,27 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+%verify "null object"
+ /*
+ * Handle an interface method call.
+ *
+ * for: invoke-interface, invoke-interface/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r2, 2) @ r2<- FEDC or CCCC
+ FETCH(r1, 1) @ r1<- BBBB
+ .if (!$isrange)
+ and r2, r2, #15 @ r2<- C (or stays CCCC)
+ .endif
+ EXPORT_PC() @ must export for invoke
+ GET_VREG(r0, r2) @ r0<- first arg ("this")
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- methodClassDex
+ cmp r0, #0 @ null obj?
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- method
+ beq common_errNullObject @ yes, fail
+ ldr r0, [r0, #offObject_clazz] @ r0<- thisPtr->clazz
+ bl dvmFindInterfaceMethodInCache @ r0<- call(class, ref, method, dex)
+ cmp r0, #0 @ failed?
+ beq common_exceptionThrown @ yes, handle exception
+ b common_invokeMethod${routine} @ jump to common handler
diff --git a/vm/mterp/armv5te/OP_INVOKE_INTERFACE_RANGE.S b/vm/mterp/armv5te/OP_INVOKE_INTERFACE_RANGE.S
new file mode 100644
index 0000000..f1eb27d
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_INTERFACE_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_INVOKE_INTERFACE.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/armv5te/OP_INVOKE_STATIC.S b/vm/mterp/armv5te/OP_INVOKE_STATIC.S
new file mode 100644
index 0000000..cb359e6
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_STATIC.S
@@ -0,0 +1,24 @@
+%default { "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+ /*
+ * Handle a static method call.
+ *
+ * for: invoke-static, invoke-static/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved methodToCall
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ must export for invoke
+ bne common_invokeMethod${routine} @ yes, continue on
+0: ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ mov r2, #METHOD_STATIC @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ bne common_invokeMethod${routine} @ no, continue
+ b common_exceptionThrown @ yes, handle exception
diff --git a/vm/mterp/armv5te/OP_INVOKE_STATIC_RANGE.S b/vm/mterp/armv5te/OP_INVOKE_STATIC_RANGE.S
new file mode 100644
index 0000000..92b9ca5
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_STATIC_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_INVOKE_STATIC.S" { "routine":"Range" }
diff --git a/vm/mterp/armv5te/OP_INVOKE_SUPER.S b/vm/mterp/armv5te/OP_INVOKE_SUPER.S
new file mode 100644
index 0000000..6117947
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_SUPER.S
@@ -0,0 +1,60 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+ /*
+ * Handle a "super" method call.
+ *
+ * for: invoke-super, invoke-super/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ .if (!$isrange)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ GET_VREG(r2, r10) @ r2<- "this" ptr
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved baseMethod
+ cmp r2, #0 @ null "this"?
+ ldr r9, [rGLUE, #offGlue_method] @ r9<- current method
+ beq common_errNullObject @ null "this", throw exception
+ cmp r0, #0 @ already resolved?
+ ldr r9, [r9, #offMethod_clazz] @ r9<- method->clazz
+ EXPORT_PC() @ must export for invoke
+ bne .L${opcode}_continue @ resolved, continue on
+ b .L${opcode}_resolve @ do resolve now
+%break
+
+ /*
+ * At this point:
+ * r0 = resolved base method
+ * r9 = method->clazz
+ */
+.L${opcode}_continue:
+ ldr r1, [r9, #offClassObject_super] @ r1<- method->clazz->super
+ ldrh r2, [r0, #offMethod_methodIndex] @ r2<- baseMethod->methodIndex
+ ldr r3, [r1, #offClassObject_vtableCount] @ r3<- super->vtableCount
+ EXPORT_PC() @ must export for invoke
+ cmp r2, r3 @ compare (methodIndex, vtableCount)
+ bcs .L${opcode}_nsm @ method not present in superclass
+ ldr r1, [r1, #offClassObject_vtable] @ r1<- ...clazz->super->vtable
+ ldr r0, [r1, r2, lsl #2] @ r3<- vtable[methodIndex]
+ bl common_invokeMethod${routine} @ continue on
+
+.L${opcode}_resolve:
+ mov r0, r9 @ r0<- method->clazz
+ mov r2, #METHOD_VIRTUAL @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ bne .L${opcode}_continue @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+ /*
+ * Throw a NoSuchMethodError with the method name as the message.
+ * r0 = resolved base method
+ */
+.L${opcode}_nsm:
+ ldr r1, [r0, #offMethod_name] @ r1<- method name
+ b common_errNoSuchMethod
diff --git a/vm/mterp/armv5te/OP_INVOKE_SUPER_QUICK.S b/vm/mterp/armv5te/OP_INVOKE_SUPER_QUICK.S
new file mode 100644
index 0000000..bd07d06
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_SUPER_QUICK.S
@@ -0,0 +1,25 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+ /*
+ * Handle an optimized "super" method call.
+ *
+ * for: [opt] invoke-super-quick, invoke-super-quick/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ .if (!$isrange)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r2, [r2, #offMethod_clazz] @ r2<- method->clazz
+ EXPORT_PC() @ must export for invoke
+ ldr r2, [r2, #offClassObject_super] @ r2<- method->clazz->super
+ GET_VREG(r3, r10) @ r3<- "this"
+ ldr r2, [r2, #offClassObject_vtable] @ r2<- ...clazz->super->vtable
+ cmp r3, #0 @ null "this" ref?
+ ldr r0, [r2, r1, lsl #2] @ r0<- super->vtable[BBBB]
+ beq common_errNullObject @ "this" is null, throw exception
+ bl common_invokeMethod${routine} @ continue on
diff --git a/vm/mterp/armv5te/OP_INVOKE_SUPER_QUICK_RANGE.S b/vm/mterp/armv5te/OP_INVOKE_SUPER_QUICK_RANGE.S
new file mode 100644
index 0000000..77d43cb
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_SUPER_QUICK_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_INVOKE_SUPER_QUICK.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/armv5te/OP_INVOKE_SUPER_RANGE.S b/vm/mterp/armv5te/OP_INVOKE_SUPER_RANGE.S
new file mode 100644
index 0000000..0a0f737
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_SUPER_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_INVOKE_SUPER.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/armv5te/OP_INVOKE_VIRTUAL.S b/vm/mterp/armv5te/OP_INVOKE_VIRTUAL.S
new file mode 100644
index 0000000..d92c6a9
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_VIRTUAL.S
@@ -0,0 +1,45 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+%verify "null object"
+ /*
+ * Handle a virtual method call.
+ *
+ * for: invoke-virtual, invoke-virtual/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved baseMethod
+ .if (!$isrange)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ must export for invoke
+ bne .L${opcode}_continue @ yes, continue on
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ mov r2, #METHOD_VIRTUAL @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ bne .L${opcode}_continue @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+%break
+
+ /*
+ * At this point:
+ * r0 = resolved base method
+ * r10 = C or CCCC (index of first arg, which is the "this" ptr)
+ */
+.L${opcode}_continue:
+ GET_VREG(r1, r10) @ r1<- "this" ptr
+ ldrh r2, [r0, #offMethod_methodIndex] @ r2<- baseMethod->methodIndex
+ cmp r1, #0 @ is "this" null?
+ beq common_errNullObject @ null "this", throw exception
+ ldr r3, [r1, #offObject_clazz] @ r1<- thisPtr->clazz
+ ldr r3, [r3, #offClassObject_vtable] @ r3<- thisPtr->clazz->vtable
+ ldr r0, [r3, r2, lsl #2] @ r3<- vtable[methodIndex]
+ bl common_invokeMethod${routine} @ continue on
diff --git a/vm/mterp/armv5te/OP_INVOKE_VIRTUAL_QUICK.S b/vm/mterp/armv5te/OP_INVOKE_VIRTUAL_QUICK.S
new file mode 100644
index 0000000..bc34023
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_VIRTUAL_QUICK.S
@@ -0,0 +1,23 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "null object"
+ /*
+ * Handle an optimized virtual method call.
+ *
+ * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r3, 2) @ r3<- FEDC or CCCC
+ FETCH(r1, 1) @ r1<- BBBB
+ .if (!$isrange)
+ and r3, r3, #15 @ r3<- C (or stays CCCC)
+ .endif
+ GET_VREG(r2, r3) @ r2<- vC ("this" ptr)
+ cmp r2, #0 @ is "this" null?
+ beq common_errNullObject @ null "this", throw exception
+ ldr r2, [r2, #offObject_clazz] @ r2<- thisPtr->clazz
+ ldr r2, [r2, #offClassObject_vtable] @ r2<- thisPtr->clazz->vtable
+ EXPORT_PC() @ invoke must export
+ ldr r0, [r2, r1, lsl #2] @ r3<- vtable[BBBB]
+ bl common_invokeMethod${routine} @ continue on
diff --git a/vm/mterp/armv5te/OP_INVOKE_VIRTUAL_QUICK_RANGE.S b/vm/mterp/armv5te/OP_INVOKE_VIRTUAL_QUICK_RANGE.S
new file mode 100644
index 0000000..d257f2b
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_VIRTUAL_QUICK_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_INVOKE_VIRTUAL_QUICK.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/armv5te/OP_INVOKE_VIRTUAL_RANGE.S b/vm/mterp/armv5te/OP_INVOKE_VIRTUAL_RANGE.S
new file mode 100644
index 0000000..4f9ca50
--- /dev/null
+++ b/vm/mterp/armv5te/OP_INVOKE_VIRTUAL_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_INVOKE_VIRTUAL.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/armv5te/OP_IPUT.S b/vm/mterp/armv5te/OP_IPUT.S
new file mode 100644
index 0000000..53f4b4e
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT.S
@@ -0,0 +1,47 @@
+%default { "store":"str", "barrier":"@ no-op ", "sqnum":"0" }
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .L${opcode}_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .L${opcode}_finish @ yes, finish up
+ b common_exceptionThrown
+%break
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.L${opcode}_finish:
+ @bl common_squeak${sqnum}
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ $barrier @ releasing store
+ $store r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_IPUT_BOOLEAN.S b/vm/mterp/armv5te/OP_IPUT_BOOLEAN.S
new file mode 100644
index 0000000..9ac68ca
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_BOOLEAN.S
@@ -0,0 +1,3 @@
+%verify "executed"
+@include "armv5te/OP_IPUT.S" { "store":"strb", "sqnum":"1" }
+%include "armv5te/OP_IPUT.S" { "store":"str", "sqnum":"1" }
diff --git a/vm/mterp/armv5te/OP_IPUT_BYTE.S b/vm/mterp/armv5te/OP_IPUT_BYTE.S
new file mode 100644
index 0000000..3871999
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_BYTE.S
@@ -0,0 +1,3 @@
+%verify "executed"
+@include "armv5te/OP_IPUT.S" { "store":"strb", "sqnum":"2" }
+%include "armv5te/OP_IPUT.S" { "store":"str", "sqnum":"2" }
diff --git a/vm/mterp/armv5te/OP_IPUT_CHAR.S b/vm/mterp/armv5te/OP_IPUT_CHAR.S
new file mode 100644
index 0000000..60e136c
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_CHAR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+@include "armv5te/OP_IPUT.S" { "store":"strh", "sqnum":"3" }
+%include "armv5te/OP_IPUT.S" { "store":"str", "sqnum":"3" }
diff --git a/vm/mterp/armv5te/OP_IPUT_OBJECT.S b/vm/mterp/armv5te/OP_IPUT_OBJECT.S
new file mode 100644
index 0000000..079094e
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_OBJECT.S
@@ -0,0 +1,50 @@
+%default { "barrier":"@ no-op ", "sqnum":"0" }
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+ /*
+ * 32-bit instance field put.
+ *
+ * for: iput-object, iput-object-volatile
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .L${opcode}_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .L${opcode}_finish @ yes, finish up
+ b common_exceptionThrown
+%break
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.L${opcode}_finish:
+ @bl common_squeak${sqnum}
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ $barrier @ releasing store
+ str r0, [r9, r3] @ obj.field (32 bits)<- r0
+ cmp r0, #0 @ stored a null reference?
+ strneb r2, [r2, r9, lsr #GC_CARD_SHIFT] @ mark card if not
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_IPUT_OBJECT_QUICK.S b/vm/mterp/armv5te/OP_IPUT_OBJECT_QUICK.S
new file mode 100644
index 0000000..7e7144a
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_OBJECT_QUICK.S
@@ -0,0 +1,19 @@
+%verify "executed"
+%verify "null object"
+ /* For: iput-object-quick */
+ /* op vA, vB, offset@CCCC */
+ mov r2, rINST, lsr #12 @ r2<- B
+ GET_VREG(r3, r2) @ r3<- fp[B], the object pointer
+ FETCH(r1, 1) @ r1<- field byte offset
+ cmp r3, #0 @ check object for null
+ mov r2, rINST, lsr #8 @ r2<- A(+)
+ beq common_errNullObject @ object was null
+ and r2, r2, #15
+ GET_VREG(r0, r2) @ r0<- fp[A]
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ str r0, [r3, r1] @ obj.field (always 32 bits)<- r0
+ cmp r0, #0
+ strneb r2, [r2, r3, lsr #GC_CARD_SHIFT] @ mark card based on obj head
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_IPUT_OBJECT_VOLATILE.S b/vm/mterp/armv5te/OP_IPUT_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..b4d24e7
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_OBJECT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_IPUT_OBJECT.S" {"barrier":"SMP_DMB"}
diff --git a/vm/mterp/armv5te/OP_IPUT_QUICK.S b/vm/mterp/armv5te/OP_IPUT_QUICK.S
new file mode 100644
index 0000000..ad76eca
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_QUICK.S
@@ -0,0 +1,16 @@
+%verify "executed"
+%verify "null object"
+ /* For: iput-quick */
+ /* op vA, vB, offset@CCCC */
+ mov r2, rINST, lsr #12 @ r2<- B
+ GET_VREG(r3, r2) @ r3<- fp[B], the object pointer
+ FETCH(r1, 1) @ r1<- field byte offset
+ cmp r3, #0 @ check object for null
+ mov r2, rINST, lsr #8 @ r2<- A(+)
+ beq common_errNullObject @ object was null
+ and r2, r2, #15
+ GET_VREG(r0, r2) @ r0<- fp[A]
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ str r0, [r3, r1] @ obj.field (always 32 bits)<- r0
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_IPUT_SHORT.S b/vm/mterp/armv5te/OP_IPUT_SHORT.S
new file mode 100644
index 0000000..4844575
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_SHORT.S
@@ -0,0 +1,3 @@
+%verify "executed"
+@include "armv5te/OP_IPUT.S" { "store":"strh", "sqnum":"4" }
+%include "armv5te/OP_IPUT.S" { "store":"str", "sqnum":"4" }
diff --git a/vm/mterp/armv5te/OP_IPUT_VOLATILE.S b/vm/mterp/armv5te/OP_IPUT_VOLATILE.S
new file mode 100644
index 0000000..ba3f615
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_IPUT.S" {"barrier":"SMP_DMB"}
diff --git a/vm/mterp/armv5te/OP_IPUT_WIDE.S b/vm/mterp/armv5te/OP_IPUT_WIDE.S
new file mode 100644
index 0000000..8796cbb
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_WIDE.S
@@ -0,0 +1,46 @@
+%default {"volatile":"0"}
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+ /* iput-wide vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .L${opcode}_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .L${opcode}_finish @ yes, finish up
+ b common_exceptionThrown
+%break
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.L${opcode}_finish:
+ mov r2, rINST, lsr #8 @ r2<- A+
+ cmp r9, #0 @ check object for null
+ and r2, r2, #15 @ r2<- A
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ add r2, rFP, r2, lsl #2 @ r3<- &fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldmia r2, {r0-r1} @ r0/r1<- fp[A]
+ GET_INST_OPCODE(r10) @ extract opcode from rINST
+ .if $volatile
+ add r2, r9, r3 @ r2<- target address
+ bl dvmQuasiAtomicSwap64 @ stores r0/r1 into addr r2
+ .else
+ strd r0, [r9, r3] @ obj.field (64 bits, aligned)<- r0/r1
+ .endif
+ GOTO_OPCODE(r10) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_IPUT_WIDE_QUICK.S b/vm/mterp/armv5te/OP_IPUT_WIDE_QUICK.S
new file mode 100644
index 0000000..b7dd703
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_WIDE_QUICK.S
@@ -0,0 +1,16 @@
+%verify "executed"
+%verify "null object"
+ /* iput-wide-quick vA, vB, offset@CCCC */
+ mov r0, rINST, lsr #8 @ r0<- A(+)
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r0, r0, #15
+ GET_VREG(r2, r1) @ r2<- fp[B], the object pointer
+ add r3, rFP, r0, lsl #2 @ r3<- &fp[A]
+ cmp r2, #0 @ check object for null
+ ldmia r3, {r0-r1} @ r0/r1<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH(r3, 1) @ r3<- field byte offset
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ strd r0, [r2, r3] @ obj.field (64 bits, aligned)<- r0/r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_IPUT_WIDE_VOLATILE.S b/vm/mterp/armv5te/OP_IPUT_WIDE_VOLATILE.S
new file mode 100644
index 0000000..944811b
--- /dev/null
+++ b/vm/mterp/armv5te/OP_IPUT_WIDE_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_IPUT_WIDE.S" {"volatile":"1"}
diff --git a/vm/mterp/armv5te/OP_LONG_TO_DOUBLE.S b/vm/mterp/armv5te/OP_LONG_TO_DOUBLE.S
new file mode 100644
index 0000000..842a8ff
--- /dev/null
+++ b/vm/mterp/armv5te/OP_LONG_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unopWide.S" {"instr":"bl __aeabi_l2d"}
diff --git a/vm/mterp/armv5te/OP_LONG_TO_FLOAT.S b/vm/mterp/armv5te/OP_LONG_TO_FLOAT.S
new file mode 100644
index 0000000..ba250cb
--- /dev/null
+++ b/vm/mterp/armv5te/OP_LONG_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unopNarrower.S" {"instr":"bl __aeabi_l2f"}
diff --git a/vm/mterp/armv5te/OP_LONG_TO_INT.S b/vm/mterp/armv5te/OP_LONG_TO_INT.S
new file mode 100644
index 0000000..4509546
--- /dev/null
+++ b/vm/mterp/armv5te/OP_LONG_TO_INT.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* we ignore the high word, making this equivalent to a 32-bit reg move */
+%include "armv5te/OP_MOVE.S"
diff --git a/vm/mterp/armv5te/OP_MONITOR_ENTER.S b/vm/mterp/armv5te/OP_MONITOR_ENTER.S
new file mode 100644
index 0000000..36faabc
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MONITOR_ENTER.S
@@ -0,0 +1,22 @@
+%verify "executed"
+%verify "exception for null object"
+ /*
+ * Synchronize on an object.
+ */
+ /* monitor-enter vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ GET_VREG(r1, r2) @ r1<- vAA (object)
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ cmp r1, #0 @ null object?
+ EXPORT_PC() @ need for precise GC, MONITOR_TRACKING
+ beq common_errNullObject @ null object, throw an exception
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ bl dvmLockObject @ call(self, obj)
+#ifdef WITH_DEADLOCK_PREDICTION /* implies WITH_MONITOR_TRACKING */
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ ldr r1, [r0, #offThread_exception] @ check for exception
+ cmp r1, #0
+ bne common_exceptionThrown @ exception raised, bail out
+#endif
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_MONITOR_EXIT.S b/vm/mterp/armv5te/OP_MONITOR_EXIT.S
new file mode 100644
index 0000000..5c1b3c7
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MONITOR_EXIT.S
@@ -0,0 +1,26 @@
+%verify "executed"
+%verify "exception for null object (impossible in javac)"
+%verify "dvmUnlockObject fails"
+ /*
+ * Unlock an object.
+ *
+ * Exceptions that occur when unlocking a monitor need to appear as
+ * if they happened at the following instruction. See the Dalvik
+ * instruction spec.
+ */
+ /* monitor-exit vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ EXPORT_PC() @ before fetch: export the PC
+ GET_VREG(r1, r2) @ r1<- vAA (object)
+ cmp r1, #0 @ null object?
+ beq 1f @ yes
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ bl dvmUnlockObject @ r0<- success for unlock(self, obj)
+ cmp r0, #0 @ failed?
+ FETCH_ADVANCE_INST(1) @ before throw: advance rPC, load rINST
+ beq common_exceptionThrown @ yes, exception is pending
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+1:
+ FETCH_ADVANCE_INST(1) @ advance before throw
+ b common_errNullObject
diff --git a/vm/mterp/armv5te/OP_MOVE.S b/vm/mterp/armv5te/OP_MOVE.S
new file mode 100644
index 0000000..efeddf4
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE.S
@@ -0,0 +1,11 @@
+%verify "executed"
+ /* for move, move-object, long-to-int */
+ /* op vA, vB */
+ mov r1, rINST, lsr #12 @ r1<- B from 15:12
+ mov r0, rINST, lsr #8 @ r0<- A from 11:8
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[B]
+ and r0, r0, #15
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ SET_VREG(r2, r0) @ fp[A]<- r2
+ GOTO_OPCODE(ip) @ execute next instruction
diff --git a/vm/mterp/armv5te/OP_MOVE_16.S b/vm/mterp/armv5te/OP_MOVE_16.S
new file mode 100644
index 0000000..3c08759
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE_16.S
@@ -0,0 +1,10 @@
+%verify "executed"
+ /* for: move/16, move-object/16 */
+ /* op vAAAA, vBBBB */
+ FETCH(r1, 2) @ r1<- BBBB
+ FETCH(r0, 1) @ r0<- AAAA
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[BBBB]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r0) @ fp[AAAA]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_MOVE_EXCEPTION.S b/vm/mterp/armv5te/OP_MOVE_EXCEPTION.S
new file mode 100644
index 0000000..f9e4cff
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE_EXCEPTION.S
@@ -0,0 +1,11 @@
+%verify "executed"
+ /* move-exception vAA */
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ mov r2, rINST, lsr #8 @ r2<- AA
+ ldr r3, [r0, #offThread_exception] @ r3<- dvmGetException bypass
+ mov r1, #0 @ r1<- 0
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ SET_VREG(r3, r2) @ fp[AA]<- exception obj
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ str r1, [r0, #offThread_exception] @ dvmClearException bypass
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_MOVE_FROM16.S b/vm/mterp/armv5te/OP_MOVE_FROM16.S
new file mode 100644
index 0000000..fdcc64e
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE_FROM16.S
@@ -0,0 +1,10 @@
+%verify "executed"
+ /* for: move/from16, move-object/from16 */
+ /* op vAA, vBBBB */
+ FETCH(r1, 1) @ r1<- BBBB
+ mov r0, rINST, lsr #8 @ r0<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[BBBB]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r0) @ fp[AA]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_MOVE_OBJECT.S b/vm/mterp/armv5te/OP_MOVE_OBJECT.S
new file mode 100644
index 0000000..0cd09df
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_MOVE.S"
diff --git a/vm/mterp/armv5te/OP_MOVE_OBJECT_16.S b/vm/mterp/armv5te/OP_MOVE_OBJECT_16.S
new file mode 100644
index 0000000..9f0f1a9
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE_OBJECT_16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_MOVE_16.S"
diff --git a/vm/mterp/armv5te/OP_MOVE_OBJECT_FROM16.S b/vm/mterp/armv5te/OP_MOVE_OBJECT_FROM16.S
new file mode 100644
index 0000000..c331a82
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE_OBJECT_FROM16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_MOVE_FROM16.S"
diff --git a/vm/mterp/armv5te/OP_MOVE_RESULT.S b/vm/mterp/armv5te/OP_MOVE_RESULT.S
new file mode 100644
index 0000000..9de8401
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE_RESULT.S
@@ -0,0 +1,9 @@
+%verify "executed"
+ /* for: move-result, move-result-object */
+ /* op vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ ldr r0, [rGLUE, #offGlue_retval] @ r0<- glue->retval.i
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[AA]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_MOVE_RESULT_OBJECT.S b/vm/mterp/armv5te/OP_MOVE_RESULT_OBJECT.S
new file mode 100644
index 0000000..a04cdb4
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE_RESULT_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_MOVE_RESULT.S"
diff --git a/vm/mterp/armv5te/OP_MOVE_RESULT_WIDE.S b/vm/mterp/armv5te/OP_MOVE_RESULT_WIDE.S
new file mode 100644
index 0000000..92f7443
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE_RESULT_WIDE.S
@@ -0,0 +1,10 @@
+%verify "executed"
+ /* move-result-wide vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ add r3, rGLUE, #offGlue_retval @ r3<- &glue->retval
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[AA]
+ ldmia r3, {r0-r1} @ r0/r1<- retval.j
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r2, {r0-r1} @ fp[AA]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_MOVE_WIDE.S b/vm/mterp/armv5te/OP_MOVE_WIDE.S
new file mode 100644
index 0000000..05151e1
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE_WIDE.S
@@ -0,0 +1,13 @@
+%verify "executed"
+ /* move-wide vA, vB */
+ /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+ mov r2, rINST, lsr #8 @ r2<- A(+)
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r2, r2, #15
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[A]
+ ldmia r3, {r0-r1} @ r0/r1<- fp[B]
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r2, {r0-r1} @ fp[A]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_MOVE_WIDE_16.S b/vm/mterp/armv5te/OP_MOVE_WIDE_16.S
new file mode 100644
index 0000000..172ef03
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE_WIDE_16.S
@@ -0,0 +1,12 @@
+%verify "executed"
+ /* move-wide/16 vAAAA, vBBBB */
+ /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+ FETCH(r3, 2) @ r3<- BBBB
+ FETCH(r2, 1) @ r2<- AAAA
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[BBBB]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[AAAA]
+ ldmia r3, {r0-r1} @ r0/r1<- fp[BBBB]
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r2, {r0-r1} @ fp[AAAA]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_MOVE_WIDE_FROM16.S b/vm/mterp/armv5te/OP_MOVE_WIDE_FROM16.S
new file mode 100644
index 0000000..81ae7dc
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MOVE_WIDE_FROM16.S
@@ -0,0 +1,12 @@
+%verify "executed"
+ /* move-wide/from16 vAA, vBBBB */
+ /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+ FETCH(r3, 1) @ r3<- BBBB
+ mov r2, rINST, lsr #8 @ r2<- AA
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[BBBB]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[AA]
+ ldmia r3, {r0-r1} @ r0/r1<- fp[BBBB]
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r2, {r0-r1} @ fp[AA]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_MUL_DOUBLE.S b/vm/mterp/armv5te/OP_MUL_DOUBLE.S
new file mode 100644
index 0000000..69a10f9
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MUL_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide.S" {"instr":"bl __aeabi_dmul"}
diff --git a/vm/mterp/armv5te/OP_MUL_DOUBLE_2ADDR.S b/vm/mterp/armv5te/OP_MUL_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..2201414
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MUL_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide2addr.S" {"instr":"bl __aeabi_dmul"}
diff --git a/vm/mterp/armv5te/OP_MUL_FLOAT.S b/vm/mterp/armv5te/OP_MUL_FLOAT.S
new file mode 100644
index 0000000..13e64e0
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MUL_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"instr":"bl __aeabi_fmul"}
diff --git a/vm/mterp/armv5te/OP_MUL_FLOAT_2ADDR.S b/vm/mterp/armv5te/OP_MUL_FLOAT_2ADDR.S
new file mode 100644
index 0000000..d97e80f
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MUL_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"instr":"bl __aeabi_fmul"}
diff --git a/vm/mterp/armv5te/OP_MUL_INT.S b/vm/mterp/armv5te/OP_MUL_INT.S
new file mode 100644
index 0000000..252114b
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MUL_INT.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+%include "armv5te/binop.S" {"instr":"mul r0, r1, r0"}
diff --git a/vm/mterp/armv5te/OP_MUL_INT_2ADDR.S b/vm/mterp/armv5te/OP_MUL_INT_2ADDR.S
new file mode 100644
index 0000000..a5a3754
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MUL_INT_2ADDR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+%include "armv5te/binop2addr.S" {"instr":"mul r0, r1, r0"}
diff --git a/vm/mterp/armv5te/OP_MUL_INT_LIT16.S b/vm/mterp/armv5te/OP_MUL_INT_LIT16.S
new file mode 100644
index 0000000..1a28d8b
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MUL_INT_LIT16.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+%include "armv5te/binopLit16.S" {"instr":"mul r0, r1, r0"}
diff --git a/vm/mterp/armv5te/OP_MUL_INT_LIT8.S b/vm/mterp/armv5te/OP_MUL_INT_LIT8.S
new file mode 100644
index 0000000..e714ed9
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MUL_INT_LIT8.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+%include "armv5te/binopLit8.S" {"instr":"mul r0, r1, r0"}
diff --git a/vm/mterp/armv5te/OP_MUL_LONG.S b/vm/mterp/armv5te/OP_MUL_LONG.S
new file mode 100644
index 0000000..3a7aac1
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MUL_LONG.S
@@ -0,0 +1,41 @@
+%verify "executed"
+ /*
+ * Signed 64-bit integer multiply.
+ *
+ * Consider WXxYZ (r1r0 x r3r2) with a long multiply:
+ * WX
+ * x YZ
+ * --------
+ * ZW ZX
+ * YW YX
+ *
+ * The low word of the result holds ZX, the high word holds
+ * (ZW+YX) + (the high overflow from ZX). YW doesn't matter because
+ * it doesn't fit in the low 64 bits.
+ *
+ * Unlike most ARM math operations, multiply instructions have
+ * restrictions on using the same register more than once (Rd and Rm
+ * cannot be the same).
+ */
+ /* mul-long vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ mul ip, r2, r1 @ ip<- ZxW
+ umull r9, r10, r2, r0 @ r9/r10 <- ZxX
+ mla r2, r0, r3, ip @ r2<- YxX + (ZxW)
+ mov r0, rINST, lsr #8 @ r0<- AA
+ add r10, r2, r10 @ r10<- r10 + low(ZxW + (YxX))
+ add r0, rFP, r0, lsl #2 @ r0<- &fp[AA]
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ b .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r0, {r9-r10} @ vAA/vAA+1<- r9/r10
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_MUL_LONG_2ADDR.S b/vm/mterp/armv5te/OP_MUL_LONG_2ADDR.S
new file mode 100644
index 0000000..a561dc9
--- /dev/null
+++ b/vm/mterp/armv5te/OP_MUL_LONG_2ADDR.S
@@ -0,0 +1,26 @@
+%verify "executed"
+ /*
+ * Signed 64-bit integer multiply, "/2addr" version.
+ *
+ * See OP_MUL_LONG for an explanation.
+ *
+ * We get a little tight on registers, so to avoid looking up &fp[A]
+ * again we stuff it into rINST.
+ */
+ /* mul-long/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r9, r9, #15
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add rINST, rFP, r9, lsl #2 @ rINST<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia rINST, {r0-r1} @ r0/r1<- vAA/vAA+1
+ mul ip, r2, r1 @ ip<- ZxW
+ umull r9, r10, r2, r0 @ r9/r10 <- ZxX
+ mla r2, r0, r3, ip @ r2<- YxX + (ZxW)
+ mov r0, rINST @ r0<- &fp[A] (free up rINST)
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ add r10, r2, r10 @ r10<- r10 + low(ZxW + (YxX))
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r0, {r9-r10} @ vAA/vAA+1<- r9/r10
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_NEG_DOUBLE.S b/vm/mterp/armv5te/OP_NEG_DOUBLE.S
new file mode 100644
index 0000000..d371016
--- /dev/null
+++ b/vm/mterp/armv5te/OP_NEG_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unopWide.S" {"instr":"add r1, r1, #0x80000000"}
diff --git a/vm/mterp/armv5te/OP_NEG_FLOAT.S b/vm/mterp/armv5te/OP_NEG_FLOAT.S
new file mode 100644
index 0000000..81b439c
--- /dev/null
+++ b/vm/mterp/armv5te/OP_NEG_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unop.S" {"instr":"add r0, r0, #0x80000000"}
diff --git a/vm/mterp/armv5te/OP_NEG_INT.S b/vm/mterp/armv5te/OP_NEG_INT.S
new file mode 100644
index 0000000..2f57540
--- /dev/null
+++ b/vm/mterp/armv5te/OP_NEG_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unop.S" {"instr":"rsb r0, r0, #0"}
diff --git a/vm/mterp/armv5te/OP_NEG_LONG.S b/vm/mterp/armv5te/OP_NEG_LONG.S
new file mode 100644
index 0000000..215f056
--- /dev/null
+++ b/vm/mterp/armv5te/OP_NEG_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unopWide.S" {"preinstr":"rsbs r0, r0, #0", "instr":"rsc r1, r1, #0"}
diff --git a/vm/mterp/armv5te/OP_NEW_ARRAY.S b/vm/mterp/armv5te/OP_NEW_ARRAY.S
new file mode 100644
index 0000000..da93c45
--- /dev/null
+++ b/vm/mterp/armv5te/OP_NEW_ARRAY.S
@@ -0,0 +1,61 @@
+%verify "executed"
+%verify "negative array length"
+%verify "allocation fails"
+ /*
+ * Allocate an array of objects, specified with the array class
+ * and a count.
+ *
+ * The verifier guarantees that this is an array class, so we don't
+ * check for it here.
+ */
+ /* new-array vA, vB, class@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ FETCH(r2, 1) @ r2<- CCCC
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ GET_VREG(r1, r0) @ r1<- vB (array length)
+ ldr r3, [r3, #offDvmDex_pResClasses] @ r3<- pDvmDex->pResClasses
+ cmp r1, #0 @ check length
+ ldr r0, [r3, r2, lsl #2] @ r0<- resolved class
+ bmi common_errNegativeArraySize @ negative length, bail
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ req'd for resolve, alloc
+ bne .L${opcode}_finish @ resolved, continue
+ b .L${opcode}_resolve @ do resolve now
+%break
+
+
+ /*
+ * Resolve class. (This is an uncommon case.)
+ *
+ * r1 holds array length
+ * r2 holds class ref CCCC
+ */
+.L${opcode}_resolve:
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ mov r9, r1 @ r9<- length (save)
+ mov r1, r2 @ r1<- CCCC
+ mov r2, #0 @ r2<- false
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- call(clazz, ref)
+ cmp r0, #0 @ got null?
+ mov r1, r9 @ r1<- length (restore)
+ beq common_exceptionThrown @ yes, handle exception
+ @ fall through to ${opcode}_finish
+
+ /*
+ * Finish allocation.
+ *
+ * r0 holds class
+ * r1 holds array length
+ */
+.L${opcode}_finish:
+ mov r2, #ALLOC_DONT_TRACK @ don't track in local refs table
+ bl dvmAllocArrayByClass @ r0<- call(clazz, length, flags)
+ cmp r0, #0 @ failed?
+ mov r2, rINST, lsr #8 @ r2<- A+
+ beq common_exceptionThrown @ yes, handle the exception
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ vA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_NEW_INSTANCE.S b/vm/mterp/armv5te/OP_NEW_INSTANCE.S
new file mode 100644
index 0000000..2687e55
--- /dev/null
+++ b/vm/mterp/armv5te/OP_NEW_INSTANCE.S
@@ -0,0 +1,68 @@
+%verify "executed"
+%verify "class not resolved"
+%verify "class cannot be resolved"
+%verify "class not initialized"
+%verify "class fails to initialize"
+%verify "class already resolved/initialized"
+%verify "class is abstract or interface"
+%verify "allocation fails"
+ /*
+ * Create a new instance of a class.
+ */
+ /* new-instance vAA, class@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResClasses] @ r3<- pDvmDex->pResClasses
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved class
+ EXPORT_PC() @ req'd for init, resolve, alloc
+ cmp r0, #0 @ already resolved?
+ beq .L${opcode}_resolve @ no, resolve it now
+.L${opcode}_resolved: @ r0=class
+ ldrb r1, [r0, #offClassObject_status] @ r1<- ClassStatus enum
+ cmp r1, #CLASS_INITIALIZED @ has class been initialized?
+ bne .L${opcode}_needinit @ no, init class now
+.L${opcode}_initialized: @ r0=class
+ mov r1, #ALLOC_DONT_TRACK @ flags for alloc call
+ bl dvmAllocObject @ r0<- new object
+ b .L${opcode}_finish @ continue
+%break
+
+ .balign 32 @ minimize cache lines
+.L${opcode}_finish: @ r0=new object
+ mov r3, rINST, lsr #8 @ r3<- AA
+ cmp r0, #0 @ failed?
+ beq common_exceptionThrown @ yes, handle the exception
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r3) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+ /*
+ * Class initialization required.
+ *
+ * r0 holds class object
+ */
+.L${opcode}_needinit:
+ mov r9, r0 @ save r0
+ bl dvmInitClass @ initialize class
+ cmp r0, #0 @ check boolean result
+ mov r0, r9 @ restore r0
+ bne .L${opcode}_initialized @ success, continue
+ b common_exceptionThrown @ failed, deal with init exception
+
+ /*
+ * Resolution required. This is the least-likely path.
+ *
+ * r1 holds BBBB
+ */
+.L${opcode}_resolve:
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ mov r2, #0 @ r2<- false
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- resolved ClassObject ptr
+ cmp r0, #0 @ got null?
+ bne .L${opcode}_resolved @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+.LstrInstantiationErrorPtr:
+ .word .LstrInstantiationError
diff --git a/vm/mterp/armv5te/OP_NOP.S b/vm/mterp/armv5te/OP_NOP.S
new file mode 100644
index 0000000..ff4cf5a
--- /dev/null
+++ b/vm/mterp/armv5te/OP_NOP.S
@@ -0,0 +1,14 @@
+%verify "executed"
+ FETCH_ADVANCE_INST(1) @ advance to next instr, load rINST
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ GOTO_OPCODE(ip) @ execute it
+
+#ifdef ASSIST_DEBUGGER
+ /* insert fake function header to help gdb find the stack frame */
+ .type dalvik_inst, %function
+dalvik_inst:
+ .fnstart
+ MTERP_ENTRY1
+ MTERP_ENTRY2
+ .fnend
+#endif
diff --git a/vm/mterp/armv5te/OP_NOT_INT.S b/vm/mterp/armv5te/OP_NOT_INT.S
new file mode 100644
index 0000000..7281cbe
--- /dev/null
+++ b/vm/mterp/armv5te/OP_NOT_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unop.S" {"instr":"mvn r0, r0"}
diff --git a/vm/mterp/armv5te/OP_NOT_LONG.S b/vm/mterp/armv5te/OP_NOT_LONG.S
new file mode 100644
index 0000000..fe741dd
--- /dev/null
+++ b/vm/mterp/armv5te/OP_NOT_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unopWide.S" {"preinstr":"mvn r0, r0", "instr":"mvn r1, r1"}
diff --git a/vm/mterp/armv5te/OP_OR_INT.S b/vm/mterp/armv5te/OP_OR_INT.S
new file mode 100644
index 0000000..f0e5b2d
--- /dev/null
+++ b/vm/mterp/armv5te/OP_OR_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"instr":"orr r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_OR_INT_2ADDR.S b/vm/mterp/armv5te/OP_OR_INT_2ADDR.S
new file mode 100644
index 0000000..3b2cf7f
--- /dev/null
+++ b/vm/mterp/armv5te/OP_OR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"instr":"orr r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_OR_INT_LIT16.S b/vm/mterp/armv5te/OP_OR_INT_LIT16.S
new file mode 100644
index 0000000..9098c76
--- /dev/null
+++ b/vm/mterp/armv5te/OP_OR_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit16.S" {"instr":"orr r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_OR_INT_LIT8.S b/vm/mterp/armv5te/OP_OR_INT_LIT8.S
new file mode 100644
index 0000000..b69e315
--- /dev/null
+++ b/vm/mterp/armv5te/OP_OR_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit8.S" {"instr":"orr r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_OR_LONG.S b/vm/mterp/armv5te/OP_OR_LONG.S
new file mode 100644
index 0000000..43110a2
--- /dev/null
+++ b/vm/mterp/armv5te/OP_OR_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide.S" {"preinstr":"orr r0, r0, r2", "instr":"orr r1, r1, r3"}
diff --git a/vm/mterp/armv5te/OP_OR_LONG_2ADDR.S b/vm/mterp/armv5te/OP_OR_LONG_2ADDR.S
new file mode 100644
index 0000000..97d5f5b
--- /dev/null
+++ b/vm/mterp/armv5te/OP_OR_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide2addr.S" {"preinstr":"orr r0, r0, r2", "instr":"orr r1, r1, r3"}
diff --git a/vm/mterp/armv5te/OP_PACKED_SWITCH.S b/vm/mterp/armv5te/OP_PACKED_SWITCH.S
new file mode 100644
index 0000000..941b232
--- /dev/null
+++ b/vm/mterp/armv5te/OP_PACKED_SWITCH.S
@@ -0,0 +1,34 @@
+%default { "func":"dvmInterpHandlePackedSwitch" }
+%verify executed
+ /*
+ * Handle a packed-switch or sparse-switch instruction. In both cases
+ * we decode it and hand it off to a helper function.
+ *
+ * We don't really expect backward branches in a switch statement, but
+ * they're perfectly legal, so we check for them here.
+ *
+ * for: packed-switch, sparse-switch
+ */
+ /* op vAA, +BBBB */
+ FETCH(r0, 1) @ r0<- bbbb (lo)
+ FETCH(r1, 2) @ r1<- BBBB (hi)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ orr r0, r0, r1, lsl #16 @ r0<- BBBBbbbb
+ GET_VREG(r1, r3) @ r1<- vAA
+ add r0, rPC, r0, lsl #1 @ r0<- PC + BBBBbbbb*2
+ bl $func @ r0<- code-unit branch offset
+ movs r9, r0, asl #1 @ r9<- branch byte offset, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+ beq common_backwardBranch @ (want to use BLE but V is unknown)
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
diff --git a/vm/mterp/armv5te/OP_REM_DOUBLE.S b/vm/mterp/armv5te/OP_REM_DOUBLE.S
new file mode 100644
index 0000000..54e0214
--- /dev/null
+++ b/vm/mterp/armv5te/OP_REM_DOUBLE.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* EABI doesn't define a double remainder function, but libm does */
+%include "armv5te/binopWide.S" {"instr":"bl fmod"}
diff --git a/vm/mterp/armv5te/OP_REM_DOUBLE_2ADDR.S b/vm/mterp/armv5te/OP_REM_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..9808c56
--- /dev/null
+++ b/vm/mterp/armv5te/OP_REM_DOUBLE_2ADDR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* EABI doesn't define a double remainder function, but libm does */
+%include "armv5te/binopWide2addr.S" {"instr":"bl fmod"}
diff --git a/vm/mterp/armv5te/OP_REM_FLOAT.S b/vm/mterp/armv5te/OP_REM_FLOAT.S
new file mode 100644
index 0000000..09cba5c
--- /dev/null
+++ b/vm/mterp/armv5te/OP_REM_FLOAT.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* EABI doesn't define a float remainder function, but libm does */
+%include "armv5te/binop.S" {"instr":"bl fmodf"}
diff --git a/vm/mterp/armv5te/OP_REM_FLOAT_2ADDR.S b/vm/mterp/armv5te/OP_REM_FLOAT_2ADDR.S
new file mode 100644
index 0000000..83af133
--- /dev/null
+++ b/vm/mterp/armv5te/OP_REM_FLOAT_2ADDR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* EABI doesn't define a float remainder function, but libm does */
+%include "armv5te/binop2addr.S" {"instr":"bl fmodf"}
diff --git a/vm/mterp/armv5te/OP_REM_INT.S b/vm/mterp/armv5te/OP_REM_INT.S
new file mode 100644
index 0000000..fbe9ad3
--- /dev/null
+++ b/vm/mterp/armv5te/OP_REM_INT.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* idivmod returns quotient in r0 and remainder in r1 */
+%include "armv5te/binop.S" {"instr":"bl __aeabi_idivmod", "result":"r1", "chkzero":"1"}
diff --git a/vm/mterp/armv5te/OP_REM_INT_2ADDR.S b/vm/mterp/armv5te/OP_REM_INT_2ADDR.S
new file mode 100644
index 0000000..42337c7
--- /dev/null
+++ b/vm/mterp/armv5te/OP_REM_INT_2ADDR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* idivmod returns quotient in r0 and remainder in r1 */
+%include "armv5te/binop2addr.S" {"instr":"bl __aeabi_idivmod", "result":"r1", "chkzero":"1"}
diff --git a/vm/mterp/armv5te/OP_REM_INT_LIT16.S b/vm/mterp/armv5te/OP_REM_INT_LIT16.S
new file mode 100644
index 0000000..396e890
--- /dev/null
+++ b/vm/mterp/armv5te/OP_REM_INT_LIT16.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* idivmod returns quotient in r0 and remainder in r1 */
+%include "armv5te/binopLit16.S" {"instr":"bl __aeabi_idivmod", "result":"r1", "chkzero":"1"}
diff --git a/vm/mterp/armv5te/OP_REM_INT_LIT8.S b/vm/mterp/armv5te/OP_REM_INT_LIT8.S
new file mode 100644
index 0000000..906c5c4
--- /dev/null
+++ b/vm/mterp/armv5te/OP_REM_INT_LIT8.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* idivmod returns quotient in r0 and remainder in r1 */
+%include "armv5te/binopLit8.S" {"instr":"bl __aeabi_idivmod", "result":"r1", "chkzero":"1"}
diff --git a/vm/mterp/armv5te/OP_REM_LONG.S b/vm/mterp/armv5te/OP_REM_LONG.S
new file mode 100644
index 0000000..d703047
--- /dev/null
+++ b/vm/mterp/armv5te/OP_REM_LONG.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* ldivmod returns quotient in r0/r1 and remainder in r2/r3 */
+%include "armv5te/binopWide.S" {"instr":"bl __aeabi_ldivmod", "result0":"r2", "result1":"r3", "chkzero":"1"}
diff --git a/vm/mterp/armv5te/OP_REM_LONG_2ADDR.S b/vm/mterp/armv5te/OP_REM_LONG_2ADDR.S
new file mode 100644
index 0000000..b62f093
--- /dev/null
+++ b/vm/mterp/armv5te/OP_REM_LONG_2ADDR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* ldivmod returns quotient in r0/r1 and remainder in r2/r3 */
+%include "armv5te/binopWide2addr.S" {"instr":"bl __aeabi_ldivmod", "result0":"r2", "result1":"r3", "chkzero":"1"}
diff --git a/vm/mterp/armv5te/OP_RETURN.S b/vm/mterp/armv5te/OP_RETURN.S
new file mode 100644
index 0000000..8838182
--- /dev/null
+++ b/vm/mterp/armv5te/OP_RETURN.S
@@ -0,0 +1,12 @@
+%verify "executed"
+ /*
+ * Return a 32-bit value. Copies the return value into the "glue"
+ * structure, then jumps to the return handler.
+ *
+ * for: return, return-object
+ */
+ /* op vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ GET_VREG(r0, r2) @ r0<- vAA
+ str r0, [rGLUE, #offGlue_retval] @ retval.i <- vAA
+ b common_returnFromMethod
diff --git a/vm/mterp/armv5te/OP_RETURN_OBJECT.S b/vm/mterp/armv5te/OP_RETURN_OBJECT.S
new file mode 100644
index 0000000..7956b45
--- /dev/null
+++ b/vm/mterp/armv5te/OP_RETURN_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_RETURN.S"
diff --git a/vm/mterp/armv5te/OP_RETURN_VOID.S b/vm/mterp/armv5te/OP_RETURN_VOID.S
new file mode 100644
index 0000000..e09ebb0
--- /dev/null
+++ b/vm/mterp/armv5te/OP_RETURN_VOID.S
@@ -0,0 +1,2 @@
+%verify "executed"
+ b common_returnFromMethod
diff --git a/vm/mterp/armv5te/OP_RETURN_WIDE.S b/vm/mterp/armv5te/OP_RETURN_WIDE.S
new file mode 100644
index 0000000..33880de
--- /dev/null
+++ b/vm/mterp/armv5te/OP_RETURN_WIDE.S
@@ -0,0 +1,12 @@
+%verify "executed"
+ /*
+ * Return a 64-bit value. Copies the return value into the "glue"
+ * structure, then jumps to the return handler.
+ */
+ /* return-wide vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[AA]
+ add r3, rGLUE, #offGlue_retval @ r3<- &glue->retval
+ ldmia r2, {r0-r1} @ r0/r1 <- vAA/vAA+1
+ stmia r3, {r0-r1} @ retval<- r0/r1
+ b common_returnFromMethod
diff --git a/vm/mterp/armv5te/OP_RSUB_INT.S b/vm/mterp/armv5te/OP_RSUB_INT.S
new file mode 100644
index 0000000..8113456
--- /dev/null
+++ b/vm/mterp/armv5te/OP_RSUB_INT.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* this op is "rsub-int", but can be thought of as "rsub-int/lit16" */
+%include "armv5te/binopLit16.S" {"instr":"rsb r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_RSUB_INT_LIT8.S b/vm/mterp/armv5te/OP_RSUB_INT_LIT8.S
new file mode 100644
index 0000000..5d5e0fb
--- /dev/null
+++ b/vm/mterp/armv5te/OP_RSUB_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit8.S" {"instr":"rsb r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_SGET.S b/vm/mterp/armv5te/OP_SGET.S
new file mode 100644
index 0000000..c803d27
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SGET.S
@@ -0,0 +1,39 @@
+%default { "barrier":"@ no-op " }
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .L${opcode}_resolve @ yes, do resolve
+.L${opcode}_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ $barrier @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+%break
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.L${opcode}_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .L${opcode}_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
diff --git a/vm/mterp/armv5te/OP_SGET_BOOLEAN.S b/vm/mterp/armv5te/OP_SGET_BOOLEAN.S
new file mode 100644
index 0000000..358fd09
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SGET_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SGET.S"
diff --git a/vm/mterp/armv5te/OP_SGET_BYTE.S b/vm/mterp/armv5te/OP_SGET_BYTE.S
new file mode 100644
index 0000000..358fd09
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SGET_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SGET.S"
diff --git a/vm/mterp/armv5te/OP_SGET_CHAR.S b/vm/mterp/armv5te/OP_SGET_CHAR.S
new file mode 100644
index 0000000..358fd09
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SGET_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SGET.S"
diff --git a/vm/mterp/armv5te/OP_SGET_OBJECT.S b/vm/mterp/armv5te/OP_SGET_OBJECT.S
new file mode 100644
index 0000000..358fd09
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SGET_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SGET.S"
diff --git a/vm/mterp/armv5te/OP_SGET_OBJECT_VOLATILE.S b/vm/mterp/armv5te/OP_SGET_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..8b9c103
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SGET_OBJECT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SGET.S" {"barrier":"SMP_DMB"}
diff --git a/vm/mterp/armv5te/OP_SGET_SHORT.S b/vm/mterp/armv5te/OP_SGET_SHORT.S
new file mode 100644
index 0000000..358fd09
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SGET_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SGET.S"
diff --git a/vm/mterp/armv5te/OP_SGET_VOLATILE.S b/vm/mterp/armv5te/OP_SGET_VOLATILE.S
new file mode 100644
index 0000000..8b9c103
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SGET_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SGET.S" {"barrier":"SMP_DMB"}
diff --git a/vm/mterp/armv5te/OP_SGET_WIDE.S b/vm/mterp/armv5te/OP_SGET_WIDE.S
new file mode 100644
index 0000000..768b9da
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SGET_WIDE.S
@@ -0,0 +1,44 @@
+%default {"volatile":"0"}
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+ /*
+ * 64-bit SGET handler.
+ */
+ /* sget-wide vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .L${opcode}_resolve @ yes, do resolve
+.L${opcode}_finish:
+ mov r9, rINST, lsr #8 @ r9<- AA
+ .if $volatile
+ add r0, r0, #offStaticField_value @ r0<- pointer to data
+ bl dvmQuasiAtomicRead64 @ r0/r1<- contents of field
+ .else
+ ldrd r0, [r0, #offStaticField_value] @ r0/r1<- field value (aligned)
+ .endif
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+%break
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ *
+ * Returns StaticField pointer in r0.
+ */
+.L${opcode}_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .L${opcode}_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
diff --git a/vm/mterp/armv5te/OP_SGET_WIDE_VOLATILE.S b/vm/mterp/armv5te/OP_SGET_WIDE_VOLATILE.S
new file mode 100644
index 0000000..b852348
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SGET_WIDE_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SGET_WIDE.S" {"volatile":"1"}
diff --git a/vm/mterp/armv5te/OP_SHL_INT.S b/vm/mterp/armv5te/OP_SHL_INT.S
new file mode 100644
index 0000000..7470344
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SHL_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"preinstr":"and r1, r1, #31", "instr":"mov r0, r0, asl r1"}
diff --git a/vm/mterp/armv5te/OP_SHL_INT_2ADDR.S b/vm/mterp/armv5te/OP_SHL_INT_2ADDR.S
new file mode 100644
index 0000000..15b6579
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SHL_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"preinstr":"and r1, r1, #31", "instr":"mov r0, r0, asl r1"}
diff --git a/vm/mterp/armv5te/OP_SHL_INT_LIT8.S b/vm/mterp/armv5te/OP_SHL_INT_LIT8.S
new file mode 100644
index 0000000..4da9a0f
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SHL_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit8.S" {"preinstr":"and r1, r1, #31", "instr":"mov r0, r0, asl r1"}
diff --git a/vm/mterp/armv5te/OP_SHL_LONG.S b/vm/mterp/armv5te/OP_SHL_LONG.S
new file mode 100644
index 0000000..b48ca5e
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SHL_LONG.S
@@ -0,0 +1,32 @@
+%verify "executed"
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to mask off the low
+ * 6 bits of the shift distance.
+ */
+ /* shl-long vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r3, r0, #255 @ r3<- BB
+ mov r0, r0, lsr #8 @ r0<- CC
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[BB]
+ GET_VREG(r2, r0) @ r2<- vCC
+ ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1
+ and r2, r2, #63 @ r2<- r2 & 0x3f
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+
+ mov r1, r1, asl r2 @ r1<- r1 << r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ b .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+ mov r0, r0, asl r2 @ r0<- r0 << r2
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_SHL_LONG_2ADDR.S b/vm/mterp/armv5te/OP_SHL_LONG_2ADDR.S
new file mode 100644
index 0000000..42a0904
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SHL_LONG_2ADDR.S
@@ -0,0 +1,28 @@
+%verify "executed"
+ /*
+ * Long integer shift, 2addr version. vA is 64-bit value/result, vB is
+ * 32-bit shift distance.
+ */
+ /* shl-long/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r2, r3) @ r2<- vB
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ and r2, r2, #63 @ r2<- r2 & 0x3f
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+
+ mov r1, r1, asl r2 @ r1<- r1 << r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32)
+ mov r0, r0, asl r2 @ r0<- r0 << r2
+ b .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_SHR_INT.S b/vm/mterp/armv5te/OP_SHR_INT.S
new file mode 100644
index 0000000..586f294
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SHR_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"preinstr":"and r1, r1, #31", "instr":"mov r0, r0, asr r1"}
diff --git a/vm/mterp/armv5te/OP_SHR_INT_2ADDR.S b/vm/mterp/armv5te/OP_SHR_INT_2ADDR.S
new file mode 100644
index 0000000..8b97195
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SHR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"preinstr":"and r1, r1, #31", "instr":"mov r0, r0, asr r1"}
diff --git a/vm/mterp/armv5te/OP_SHR_INT_LIT8.S b/vm/mterp/armv5te/OP_SHR_INT_LIT8.S
new file mode 100644
index 0000000..465b61f
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SHR_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit8.S" {"preinstr":"and r1, r1, #31", "instr":"mov r0, r0, asr r1"}
diff --git a/vm/mterp/armv5te/OP_SHR_LONG.S b/vm/mterp/armv5te/OP_SHR_LONG.S
new file mode 100644
index 0000000..e6489a0
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SHR_LONG.S
@@ -0,0 +1,32 @@
+%verify "executed"
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to mask off the low
+ * 6 bits of the shift distance.
+ */
+ /* shr-long vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r3, r0, #255 @ r3<- BB
+ mov r0, r0, lsr #8 @ r0<- CC
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[BB]
+ GET_VREG(r2, r0) @ r2<- vCC
+ ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1
+ and r2, r2, #63 @ r0<- r0 & 0x3f
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ b .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+ mov r1, r1, asr r2 @ r1<- r1 >> r2
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_SHR_LONG_2ADDR.S b/vm/mterp/armv5te/OP_SHR_LONG_2ADDR.S
new file mode 100644
index 0000000..9414ffb
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SHR_LONG_2ADDR.S
@@ -0,0 +1,28 @@
+%verify "executed"
+ /*
+ * Long integer shift, 2addr version. vA is 64-bit value/result, vB is
+ * 32-bit shift distance.
+ */
+ /* shr-long/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r2, r3) @ r2<- vB
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ and r2, r2, #63 @ r2<- r2 & 0x3f
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32)
+ mov r1, r1, asr r2 @ r1<- r1 >> r2
+ b .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_SPARSE_SWITCH.S b/vm/mterp/armv5te/OP_SPARSE_SWITCH.S
new file mode 100644
index 0000000..2447286
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPARSE_SWITCH.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_PACKED_SWITCH.S" { "func":"dvmInterpHandleSparseSwitch" }
diff --git a/vm/mterp/armv5te/OP_SPUT.S b/vm/mterp/armv5te/OP_SPUT.S
new file mode 100644
index 0000000..e709b22
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPUT.S
@@ -0,0 +1,39 @@
+%default { "barrier":"@ no-op " }
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .L${opcode}_resolve @ yes, do resolve
+.L${opcode}_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ $barrier @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ GOTO_OPCODE(ip) @ jump to next instruction
+%break
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.L${opcode}_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .L${opcode}_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
diff --git a/vm/mterp/armv5te/OP_SPUT_BOOLEAN.S b/vm/mterp/armv5te/OP_SPUT_BOOLEAN.S
new file mode 100644
index 0000000..1048982
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPUT_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SPUT.S"
diff --git a/vm/mterp/armv5te/OP_SPUT_BYTE.S b/vm/mterp/armv5te/OP_SPUT_BYTE.S
new file mode 100644
index 0000000..1048982
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPUT_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SPUT.S"
diff --git a/vm/mterp/armv5te/OP_SPUT_CHAR.S b/vm/mterp/armv5te/OP_SPUT_CHAR.S
new file mode 100644
index 0000000..1048982
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPUT_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SPUT.S"
diff --git a/vm/mterp/armv5te/OP_SPUT_OBJECT.S b/vm/mterp/armv5te/OP_SPUT_OBJECT.S
new file mode 100644
index 0000000..fe9fa4c
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPUT_OBJECT.S
@@ -0,0 +1,38 @@
+%default { "barrier":"@ no-op " }
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+ /*
+ * 32-bit SPUT handler for objects
+ *
+ * for: sput-object, sput-object-volatile
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .L${opcode}_finish @ no, continue
+ ldr r9, [rGLUE, #offGlue_method] @ r9<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r9, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .L${opcode}_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+%break
+.L${opcode}_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ ldr r9, [r0, #offField_clazz] @ r9<- field->clazz
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ $barrier @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ cmp r1, #0 @ stored a null object?
+ strneb r2, [r2, r9, lsr #GC_CARD_SHIFT] @ mark card based on obj head
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_SPUT_OBJECT_VOLATILE.S b/vm/mterp/armv5te/OP_SPUT_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..fe12b9e
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPUT_OBJECT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SPUT_OBJECT.S" {"barrier":"SMP_DMB"}
diff --git a/vm/mterp/armv5te/OP_SPUT_SHORT.S b/vm/mterp/armv5te/OP_SPUT_SHORT.S
new file mode 100644
index 0000000..1048982
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPUT_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SPUT.S"
diff --git a/vm/mterp/armv5te/OP_SPUT_VOLATILE.S b/vm/mterp/armv5te/OP_SPUT_VOLATILE.S
new file mode 100644
index 0000000..cfb2b27
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPUT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SPUT.S" {"barrier":"SMP_DMB"}
diff --git a/vm/mterp/armv5te/OP_SPUT_WIDE.S b/vm/mterp/armv5te/OP_SPUT_WIDE.S
new file mode 100644
index 0000000..330c72b
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPUT_WIDE.S
@@ -0,0 +1,46 @@
+%default {"volatile":"0"}
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+ /*
+ * 64-bit SPUT handler.
+ */
+ /* sput-wide vAA, field@BBBB */
+ ldr r0, [rGLUE, #offGlue_methodClassDex] @ r0<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r0, [r0, #offDvmDex_pResFields] @ r0<- dvmDex->pResFields
+ mov r9, rINST, lsr #8 @ r9<- AA
+ ldr r2, [r0, r1, lsl #2] @ r2<- resolved StaticField ptr
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ cmp r2, #0 @ is resolved entry null?
+ beq .L${opcode}_resolve @ yes, do resolve
+.L${opcode}_finish: @ field ptr in r2, AA in r9
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ GET_INST_OPCODE(r10) @ extract opcode from rINST
+ .if $volatile
+ add r2, r2, #offStaticField_value @ r2<- pointer to data
+ bl dvmQuasiAtomicSwap64 @ stores r0/r1 into addr r2
+ .else
+ strd r0, [r2, #offStaticField_value] @ field<- vAA/vAA+1
+ .endif
+ GOTO_OPCODE(r10) @ jump to next instruction
+%break
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ * r9: &fp[AA]
+ *
+ * Returns StaticField pointer in r2.
+ */
+.L${opcode}_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ mov r2, r0 @ copy to r2
+ bne .L${opcode}_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
diff --git a/vm/mterp/armv5te/OP_SPUT_WIDE_VOLATILE.S b/vm/mterp/armv5te/OP_SPUT_WIDE_VOLATILE.S
new file mode 100644
index 0000000..a88de85
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SPUT_WIDE_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/OP_SPUT_WIDE.S" {"volatile":"1"}
diff --git a/vm/mterp/armv5te/OP_SUB_DOUBLE.S b/vm/mterp/armv5te/OP_SUB_DOUBLE.S
new file mode 100644
index 0000000..fed41d2
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SUB_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide.S" {"instr":"bl __aeabi_dsub"}
diff --git a/vm/mterp/armv5te/OP_SUB_DOUBLE_2ADDR.S b/vm/mterp/armv5te/OP_SUB_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..d8d51a7
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SUB_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide2addr.S" {"instr":"bl __aeabi_dsub"}
diff --git a/vm/mterp/armv5te/OP_SUB_FLOAT.S b/vm/mterp/armv5te/OP_SUB_FLOAT.S
new file mode 100644
index 0000000..651669c
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SUB_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"instr":"bl __aeabi_fsub"}
diff --git a/vm/mterp/armv5te/OP_SUB_FLOAT_2ADDR.S b/vm/mterp/armv5te/OP_SUB_FLOAT_2ADDR.S
new file mode 100644
index 0000000..e7d27c7
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SUB_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"instr":"bl __aeabi_fsub"}
diff --git a/vm/mterp/armv5te/OP_SUB_INT.S b/vm/mterp/armv5te/OP_SUB_INT.S
new file mode 100644
index 0000000..f25e643
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SUB_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"instr":"sub r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_SUB_INT_2ADDR.S b/vm/mterp/armv5te/OP_SUB_INT_2ADDR.S
new file mode 100644
index 0000000..56ff9bc
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SUB_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"instr":"sub r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_SUB_LONG.S b/vm/mterp/armv5te/OP_SUB_LONG.S
new file mode 100644
index 0000000..cfdcc88
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SUB_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide.S" {"preinstr":"subs r0, r0, r2", "instr":"sbc r1, r1, r3"}
diff --git a/vm/mterp/armv5te/OP_SUB_LONG_2ADDR.S b/vm/mterp/armv5te/OP_SUB_LONG_2ADDR.S
new file mode 100644
index 0000000..4fd6610
--- /dev/null
+++ b/vm/mterp/armv5te/OP_SUB_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide2addr.S" {"preinstr":"subs r0, r0, r2", "instr":"sbc r1, r1, r3"}
diff --git a/vm/mterp/armv5te/OP_THROW.S b/vm/mterp/armv5te/OP_THROW.S
new file mode 100644
index 0000000..dd0a0b8
--- /dev/null
+++ b/vm/mterp/armv5te/OP_THROW.S
@@ -0,0 +1,15 @@
+%verify "executed"
+%verify "exception for null object"
+ /*
+ * Throw an exception object in the current thread.
+ */
+ /* throw vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ GET_VREG(r1, r2) @ r1<- vAA (exception object)
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ EXPORT_PC() @ exception handler can throw
+ cmp r1, #0 @ null object?
+ beq common_errNullObject @ yes, throw an NPE instead
+ @ bypass dvmSetException, just store it
+ str r1, [r0, #offThread_exception] @ thread->exception<- obj
+ b common_exceptionThrown
diff --git a/vm/mterp/armv5te/OP_THROW_VERIFICATION_ERROR.S b/vm/mterp/armv5te/OP_THROW_VERIFICATION_ERROR.S
new file mode 100644
index 0000000..8bd4f35
--- /dev/null
+++ b/vm/mterp/armv5te/OP_THROW_VERIFICATION_ERROR.S
@@ -0,0 +1,13 @@
+%verify executed
+ /*
+ * Handle a throw-verification-error instruction. This throws an
+ * exception for an error discovered during verification. The
+ * exception is indicated by AA, with some detail provided by BBBB.
+ */
+ /* op AA, ref@BBBB */
+ ldr r0, [rGLUE, #offGlue_method] @ r0<- glue->method
+ FETCH(r2, 1) @ r2<- BBBB
+ EXPORT_PC() @ export the PC
+ mov r1, rINST, lsr #8 @ r1<- AA
+ bl dvmThrowVerificationError @ always throws
+ b common_exceptionThrown @ handle exception
diff --git a/vm/mterp/armv5te/OP_UNUSED_3E.S b/vm/mterp/armv5te/OP_UNUSED_3E.S
new file mode 100644
index 0000000..faa7246
--- /dev/null
+++ b/vm/mterp/armv5te/OP_UNUSED_3E.S
@@ -0,0 +1 @@
+%include "armv5te/unused.S"
diff --git a/vm/mterp/armv5te/OP_UNUSED_3F.S b/vm/mterp/armv5te/OP_UNUSED_3F.S
new file mode 100644
index 0000000..faa7246
--- /dev/null
+++ b/vm/mterp/armv5te/OP_UNUSED_3F.S
@@ -0,0 +1 @@
+%include "armv5te/unused.S"
diff --git a/vm/mterp/armv5te/OP_UNUSED_40.S b/vm/mterp/armv5te/OP_UNUSED_40.S
new file mode 100644
index 0000000..faa7246
--- /dev/null
+++ b/vm/mterp/armv5te/OP_UNUSED_40.S
@@ -0,0 +1 @@
+%include "armv5te/unused.S"
diff --git a/vm/mterp/armv5te/OP_UNUSED_41.S b/vm/mterp/armv5te/OP_UNUSED_41.S
new file mode 100644
index 0000000..faa7246
--- /dev/null
+++ b/vm/mterp/armv5te/OP_UNUSED_41.S
@@ -0,0 +1 @@
+%include "armv5te/unused.S"
diff --git a/vm/mterp/armv5te/OP_UNUSED_42.S b/vm/mterp/armv5te/OP_UNUSED_42.S
new file mode 100644
index 0000000..faa7246
--- /dev/null
+++ b/vm/mterp/armv5te/OP_UNUSED_42.S
@@ -0,0 +1 @@
+%include "armv5te/unused.S"
diff --git a/vm/mterp/armv5te/OP_UNUSED_43.S b/vm/mterp/armv5te/OP_UNUSED_43.S
new file mode 100644
index 0000000..faa7246
--- /dev/null
+++ b/vm/mterp/armv5te/OP_UNUSED_43.S
@@ -0,0 +1 @@
+%include "armv5te/unused.S"
diff --git a/vm/mterp/armv5te/OP_UNUSED_73.S b/vm/mterp/armv5te/OP_UNUSED_73.S
new file mode 100644
index 0000000..faa7246
--- /dev/null
+++ b/vm/mterp/armv5te/OP_UNUSED_73.S
@@ -0,0 +1 @@
+%include "armv5te/unused.S"
diff --git a/vm/mterp/armv5te/OP_UNUSED_79.S b/vm/mterp/armv5te/OP_UNUSED_79.S
new file mode 100644
index 0000000..faa7246
--- /dev/null
+++ b/vm/mterp/armv5te/OP_UNUSED_79.S
@@ -0,0 +1 @@
+%include "armv5te/unused.S"
diff --git a/vm/mterp/armv5te/OP_UNUSED_7A.S b/vm/mterp/armv5te/OP_UNUSED_7A.S
new file mode 100644
index 0000000..faa7246
--- /dev/null
+++ b/vm/mterp/armv5te/OP_UNUSED_7A.S
@@ -0,0 +1 @@
+%include "armv5te/unused.S"
diff --git a/vm/mterp/armv5te/OP_UNUSED_F1.S b/vm/mterp/armv5te/OP_UNUSED_F1.S
new file mode 100644
index 0000000..faa7246
--- /dev/null
+++ b/vm/mterp/armv5te/OP_UNUSED_F1.S
@@ -0,0 +1 @@
+%include "armv5te/unused.S"
diff --git a/vm/mterp/armv5te/OP_UNUSED_FF.S b/vm/mterp/armv5te/OP_UNUSED_FF.S
new file mode 100644
index 0000000..faa7246
--- /dev/null
+++ b/vm/mterp/armv5te/OP_UNUSED_FF.S
@@ -0,0 +1 @@
+%include "armv5te/unused.S"
diff --git a/vm/mterp/armv5te/OP_USHR_INT.S b/vm/mterp/armv5te/OP_USHR_INT.S
new file mode 100644
index 0000000..a72c2cb
--- /dev/null
+++ b/vm/mterp/armv5te/OP_USHR_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"preinstr":"and r1, r1, #31", "instr":"mov r0, r0, lsr r1"}
diff --git a/vm/mterp/armv5te/OP_USHR_INT_2ADDR.S b/vm/mterp/armv5te/OP_USHR_INT_2ADDR.S
new file mode 100644
index 0000000..8ace5f9
--- /dev/null
+++ b/vm/mterp/armv5te/OP_USHR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"preinstr":"and r1, r1, #31", "instr":"mov r0, r0, lsr r1"}
diff --git a/vm/mterp/armv5te/OP_USHR_INT_LIT8.S b/vm/mterp/armv5te/OP_USHR_INT_LIT8.S
new file mode 100644
index 0000000..9d038c5
--- /dev/null
+++ b/vm/mterp/armv5te/OP_USHR_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit8.S" {"preinstr":"and r1, r1, #31", "instr":"mov r0, r0, lsr r1"}
diff --git a/vm/mterp/armv5te/OP_USHR_LONG.S b/vm/mterp/armv5te/OP_USHR_LONG.S
new file mode 100644
index 0000000..d9ae338
--- /dev/null
+++ b/vm/mterp/armv5te/OP_USHR_LONG.S
@@ -0,0 +1,32 @@
+%verify "executed"
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to mask off the low
+ * 6 bits of the shift distance.
+ */
+ /* ushr-long vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r3, r0, #255 @ r3<- BB
+ mov r0, r0, lsr #8 @ r0<- CC
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[BB]
+ GET_VREG(r2, r0) @ r2<- vCC
+ ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1
+ and r2, r2, #63 @ r0<- r0 & 0x3f
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ b .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+ mov r1, r1, lsr r2 @ r1<- r1 >>> r2
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_USHR_LONG_2ADDR.S b/vm/mterp/armv5te/OP_USHR_LONG_2ADDR.S
new file mode 100644
index 0000000..27592a4
--- /dev/null
+++ b/vm/mterp/armv5te/OP_USHR_LONG_2ADDR.S
@@ -0,0 +1,28 @@
+%verify "executed"
+ /*
+ * Long integer shift, 2addr version. vA is 64-bit value/result, vB is
+ * 32-bit shift distance.
+ */
+ /* ushr-long/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r2, r3) @ r2<- vB
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ and r2, r2, #63 @ r2<- r2 & 0x3f
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32)
+ mov r1, r1, lsr r2 @ r1<- r1 >>> r2
+ b .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv5te/OP_XOR_INT.S b/vm/mterp/armv5te/OP_XOR_INT.S
new file mode 100644
index 0000000..81bfa00
--- /dev/null
+++ b/vm/mterp/armv5te/OP_XOR_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop.S" {"instr":"eor r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_XOR_INT_2ADDR.S b/vm/mterp/armv5te/OP_XOR_INT_2ADDR.S
new file mode 100644
index 0000000..ead63c5
--- /dev/null
+++ b/vm/mterp/armv5te/OP_XOR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binop2addr.S" {"instr":"eor r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_XOR_INT_LIT16.S b/vm/mterp/armv5te/OP_XOR_INT_LIT16.S
new file mode 100644
index 0000000..fcb4abb
--- /dev/null
+++ b/vm/mterp/armv5te/OP_XOR_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit16.S" {"instr":"eor r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_XOR_INT_LIT8.S b/vm/mterp/armv5te/OP_XOR_INT_LIT8.S
new file mode 100644
index 0000000..5ef0711
--- /dev/null
+++ b/vm/mterp/armv5te/OP_XOR_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopLit8.S" {"instr":"eor r0, r0, r1"}
diff --git a/vm/mterp/armv5te/OP_XOR_LONG.S b/vm/mterp/armv5te/OP_XOR_LONG.S
new file mode 100644
index 0000000..29a0075
--- /dev/null
+++ b/vm/mterp/armv5te/OP_XOR_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide.S" {"preinstr":"eor r0, r0, r2", "instr":"eor r1, r1, r3"}
diff --git a/vm/mterp/armv5te/OP_XOR_LONG_2ADDR.S b/vm/mterp/armv5te/OP_XOR_LONG_2ADDR.S
new file mode 100644
index 0000000..7dc71c9
--- /dev/null
+++ b/vm/mterp/armv5te/OP_XOR_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/binopWide2addr.S" {"preinstr":"eor r0, r0, r2", "instr":"eor r1, r1, r3"}
diff --git a/vm/mterp/armv5te/bincmp.S b/vm/mterp/armv5te/bincmp.S
new file mode 100644
index 0000000..58f9079
--- /dev/null
+++ b/vm/mterp/armv5te/bincmp.S
@@ -0,0 +1,31 @@
+%verify "branch taken"
+%verify "branch not taken"
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ mov r0, rINST, lsr #8 @ r0<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r0, r0, #15
+ GET_VREG(r3, r1) @ r3<- vB
+ GET_VREG(r2, r0) @ r2<- vA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, r3 @ compare (vA, vB)
+ b${revcmp} 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ b common_testUpdateProfile
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
diff --git a/vm/mterp/armv5te/binop.S b/vm/mterp/armv5te/binop.S
new file mode 100644
index 0000000..d169ed6
--- /dev/null
+++ b/vm/mterp/armv5te/binop.S
@@ -0,0 +1,35 @@
+%default {"preinstr":"", "result":"r0", "chkzero":"0"}
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if $chkzero
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ $preinstr @ optional op; may set condition codes
+ $instr @ $result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG($result, r9) @ vAA<- $result
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
diff --git a/vm/mterp/armv5te/binop2addr.S b/vm/mterp/armv5te/binop2addr.S
new file mode 100644
index 0000000..061242a
--- /dev/null
+++ b/vm/mterp/armv5te/binop2addr.S
@@ -0,0 +1,33 @@
+%default {"preinstr":"", "result":"r0", "chkzero":"0"}
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if $chkzero
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ $preinstr @ optional op; may set condition codes
+ $instr @ $result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG($result, r9) @ vAA<- $result
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
diff --git a/vm/mterp/armv5te/binopLit16.S b/vm/mterp/armv5te/binopLit16.S
new file mode 100644
index 0000000..df49929
--- /dev/null
+++ b/vm/mterp/armv5te/binopLit16.S
@@ -0,0 +1,30 @@
+%default {"result":"r0", "chkzero":"0"}
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r2) @ r0<- vB
+ and r9, r9, #15
+ .if $chkzero
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ $instr @ $result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG($result, r9) @ vAA<- $result
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
diff --git a/vm/mterp/armv5te/binopLit8.S b/vm/mterp/armv5te/binopLit8.S
new file mode 100644
index 0000000..2addd26
--- /dev/null
+++ b/vm/mterp/armv5te/binopLit8.S
@@ -0,0 +1,32 @@
+%default {"preinstr":"", "result":"r0", "chkzero":"0"}
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if $chkzero
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ $preinstr @ optional op; may set condition codes
+ $instr @ $result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG($result, r9) @ vAA<- $result
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
diff --git a/vm/mterp/armv5te/binopWide.S b/vm/mterp/armv5te/binopWide.S
new file mode 100644
index 0000000..71fd3fe
--- /dev/null
+++ b/vm/mterp/armv5te/binopWide.S
@@ -0,0 +1,38 @@
+%default {"preinstr":"", "result0":"r0", "result1":"r1", "chkzero":"0"}
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if $chkzero
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ $preinstr @ optional op; may set condition codes
+ $instr @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {$result0,$result1} @ vAA/vAA+1<- $result0/$result1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
diff --git a/vm/mterp/armv5te/binopWide2addr.S b/vm/mterp/armv5te/binopWide2addr.S
new file mode 100644
index 0000000..3fd5747
--- /dev/null
+++ b/vm/mterp/armv5te/binopWide2addr.S
@@ -0,0 +1,35 @@
+%default {"preinstr":"", "result0":"r0", "result1":"r1", "chkzero":"0"}
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r9, r9, #15
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if $chkzero
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ $preinstr @ optional op; may set condition codes
+ $instr @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {$result0,$result1} @ vAA/vAA+1<- $result0/$result1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
diff --git a/vm/mterp/armv5te/debug.c b/vm/mterp/armv5te/debug.c
new file mode 100644
index 0000000..9f893fe
--- /dev/null
+++ b/vm/mterp/armv5te/debug.c
@@ -0,0 +1,79 @@
+#include <inttypes.h>
+
+/*
+ * Dump the fixed-purpose ARM registers, along with some other info.
+ *
+ * This function MUST be compiled in ARM mode -- THUMB will yield bogus
+ * results.
+ *
+ * This will NOT preserve r0-r3/ip.
+ */
+void dvmMterpDumpArmRegs(uint32_t r0, uint32_t r1, uint32_t r2, uint32_t r3)
+{
+ register uint32_t rPC asm("r4");
+ register uint32_t rFP asm("r5");
+ register uint32_t rGLUE asm("r6");
+ register uint32_t rINST asm("r7");
+ register uint32_t rIBASE asm("r8");
+ register uint32_t r9 asm("r9");
+ register uint32_t r10 asm("r10");
+
+ //extern char dvmAsmInstructionStart[];
+
+ printf("REGS: r0=%08x r1=%08x r2=%08x r3=%08x\n", r0, r1, r2, r3);
+ printf(" : rPC=%08x rFP=%08x rGLUE=%08x rINST=%08x\n",
+ rPC, rFP, rGLUE, rINST);
+ printf(" : rIBASE=%08x r9=%08x r10=%08x\n", rIBASE, r9, r10);
+
+ //MterpGlue* glue = (MterpGlue*) rGLUE;
+ //const Method* method = glue->method;
+ printf(" + self is %p\n", dvmThreadSelf());
+ //printf(" + currently in %s.%s %s\n",
+ // method->clazz->descriptor, method->name, method->shorty);
+ //printf(" + dvmAsmInstructionStart = %p\n", dvmAsmInstructionStart);
+ //printf(" + next handler for 0x%02x = %p\n",
+ // rINST & 0xff, dvmAsmInstructionStart + (rINST & 0xff) * 64);
+}
+
+/*
+ * Dump the StackSaveArea for the specified frame pointer.
+ */
+void dvmDumpFp(void* fp, StackSaveArea* otherSaveArea)
+{
+ StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+ printf("StackSaveArea for fp %p [%p/%p]:\n", fp, saveArea, otherSaveArea);
+#ifdef EASY_GDB
+ printf(" prevSave=%p, prevFrame=%p savedPc=%p meth=%p curPc=%p\n",
+ saveArea->prevSave, saveArea->prevFrame, saveArea->savedPc,
+ saveArea->method, saveArea->xtra.currentPc);
+#else
+ printf(" prevFrame=%p savedPc=%p meth=%p curPc=%p fp[0]=0x%08x\n",
+ saveArea->prevFrame, saveArea->savedPc,
+ saveArea->method, saveArea->xtra.currentPc,
+ *(u4*)fp);
+#endif
+}
+
+/*
+ * Does the bulk of the work for common_printMethod().
+ */
+void dvmMterpPrintMethod(Method* method)
+{
+ /*
+ * It is a direct (non-virtual) method if it is static, private,
+ * or a constructor.
+ */
+ bool isDirect =
+ ((method->accessFlags & (ACC_STATIC|ACC_PRIVATE)) != 0) ||
+ (method->name[0] == '<');
+
+ char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+
+ printf("<%c:%s.%s %s> ",
+ isDirect ? 'D' : 'V',
+ method->clazz->descriptor,
+ method->name,
+ desc);
+
+ free(desc);
+}
diff --git a/vm/mterp/armv5te/entry.S b/vm/mterp/armv5te/entry.S
new file mode 100644
index 0000000..eddac53
--- /dev/null
+++ b/vm/mterp/armv5te/entry.S
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Interpreter entry point.
+ */
+
+/*
+ * We don't have formal stack frames, so gdb scans upward in the code
+ * to find the start of the function (a label with the %function type),
+ * and then looks at the next few instructions to figure out what
+ * got pushed onto the stack. From this it figures out how to restore
+ * the registers, including PC, for the previous stack frame. If gdb
+ * sees a non-function label, it stops scanning, so either we need to
+ * have nothing but assembler-local labels between the entry point and
+ * the break, or we need to fake it out.
+ *
+ * When this is defined, we add some stuff to make gdb less confused.
+ */
+#define ASSIST_DEBUGGER 1
+
+ .text
+ .align 2
+ .global dvmMterpStdRun
+ .type dvmMterpStdRun, %function
+
+/*
+ * On entry:
+ * r0 MterpGlue* glue
+ *
+ * This function returns a boolean "changeInterp" value. The return comes
+ * via a call to dvmMterpStdBail().
+ */
+dvmMterpStdRun:
+#define MTERP_ENTRY1 \
+ .save {r4-r10,fp,lr}; \
+ stmfd sp!, {r4-r10,fp,lr} @ save 9 regs
+#define MTERP_ENTRY2 \
+ .pad #4; \
+ sub sp, sp, #4 @ align 64
+
+ .fnstart
+ MTERP_ENTRY1
+ MTERP_ENTRY2
+
+ /* save stack pointer, add magic word for debuggerd */
+ str sp, [r0, #offGlue_bailPtr] @ save SP for eventual return
+
+ /* set up "named" registers, figure out entry point */
+ mov rGLUE, r0 @ set rGLUE
+ ldr r1, [r0, #offGlue_entryPoint] @ enum is 4 bytes in aapcs-EABI
+ LOAD_PC_FP_FROM_GLUE() @ load rPC and rFP from "glue"
+ adr rIBASE, dvmAsmInstructionStart @ set rIBASE
+ cmp r1, #kInterpEntryInstr @ usual case?
+ bne .Lnot_instr @ no, handle it
+
+#if defined(WITH_JIT)
+.LentryInstr:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ /* Entry is always a possible trace start */
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_INST()
+ mov r1, #0 @ prepare the value for the new state
+ str r1, [r10, #offThread_inJitCodeCache] @ back to the interp land
+ cmp r0,#0 @ is profiling disabled?
+#if !defined(WITH_SELF_VERIFICATION)
+ bne common_updateProfile @ profiling is enabled
+#else
+ ldr r2, [r10, #offThread_shadowSpace] @ to find out the jit exit state
+ beq 1f @ profiling is disabled
+ ldr r3, [r2, #offShadowSpace_jitExitState] @ jit exit state
+ cmp r3, #kSVSTraceSelect @ hot trace following?
+ moveq r2,#kJitTSelectRequestHot @ ask for trace selection
+ beq common_selectTrace @ go build the trace
+ cmp r3, #kSVSNoProfile @ don't profile the next instruction?
+ beq 1f @ intrepret the next instruction
+ b common_updateProfile @ collect profiles
+#endif
+1:
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+#else
+ /* start executing the instruction at rPC */
+ FETCH_INST() @ load rINST from rPC
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+.Lnot_instr:
+ cmp r1, #kInterpEntryReturn @ were we returning from a method?
+ beq common_returnFromMethod
+
+.Lnot_return:
+ cmp r1, #kInterpEntryThrow @ were we throwing an exception?
+ beq common_exceptionThrown
+
+#if defined(WITH_JIT)
+.Lnot_throw:
+ ldr r10,[rGLUE, #offGlue_jitResumeNPC]
+ ldr r2,[rGLUE, #offGlue_jitResumeDPC]
+ cmp r1, #kInterpEntryResume @ resuming after Jit single-step?
+ bne .Lbad_arg
+ cmp rPC,r2
+ bne .LentryInstr @ must have branched, don't resume
+#if defined(WITH_SELF_VERIFICATION)
+ @ glue->entryPoint will be set in dvmSelfVerificationSaveState
+ b jitSVShadowRunStart @ re-enter the translation after the
+ @ single-stepped instruction
+ @noreturn
+#endif
+ mov r1, #kInterpEntryInstr
+ str r1, [rGLUE, #offGlue_entryPoint]
+ bx r10 @ re-enter the translation
+#endif
+
+.Lbad_arg:
+ ldr r0, strBadEntryPoint
+ @ r1 holds value of entryPoint
+ bl printf
+ bl dvmAbort
+ .fnend
+
+
+ .global dvmMterpStdBail
+ .type dvmMterpStdBail, %function
+
+/*
+ * Restore the stack pointer and PC from the save point established on entry.
+ * This is essentially the same as a longjmp, but should be cheaper. The
+ * last instruction causes us to return to whoever called dvmMterpStdRun.
+ *
+ * We pushed some registers on the stack in dvmMterpStdRun, then saved
+ * SP and LR. Here we restore SP, restore the registers, and then restore
+ * LR to PC.
+ *
+ * On entry:
+ * r0 MterpGlue* glue
+ * r1 bool changeInterp
+ */
+dvmMterpStdBail:
+ ldr sp, [r0, #offGlue_bailPtr] @ sp<- saved SP
+ mov r0, r1 @ return the changeInterp value
+ add sp, sp, #4 @ un-align 64
+ LDMFD_PC "r4-r10,fp" @ restore 9 regs and return
+
+
+/*
+ * String references.
+ */
+strBadEntryPoint:
+ .word .LstrBadEntryPoint
diff --git a/vm/mterp/armv5te/footer.S b/vm/mterp/armv5te/footer.S
new file mode 100644
index 0000000..b69aef8
--- /dev/null
+++ b/vm/mterp/armv5te/footer.S
@@ -0,0 +1,1229 @@
+
+/*
+ * ===========================================================================
+ * Common subroutines and data
+ * ===========================================================================
+ */
+
+
+
+ .text
+ .align 2
+
+#if defined(WITH_JIT)
+#if defined(WITH_SELF_VERIFICATION)
+ .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r2,#kSVSPunt @ r2<- interpreter entry point
+ mov r3, #0
+ str r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+ str lr,[rGLUE,#offGlue_jitResumeNPC]
+ str r1,[rGLUE,#offGlue_jitResumeDPC]
+ mov r2,#kSVSSingleStep @ r2<- interpreter entry point
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r0,rPC @ pass our target PC
+ mov r2,#kSVSNoProfile @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [r10, #offThread_inJitCodeCache] @ back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r0,rPC @ pass our target PC
+ mov r2,#kSVSTraceSelect @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ ldr r0,[lr, #-1] @ pass our target PC
+ mov r2,#kSVSTraceSelect @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpBackwardBranch
+dvmJitToInterpBackwardBranch:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ ldr r0,[lr, #-1] @ pass our target PC
+ mov r2,#kSVSBackwardBranch @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ ldr r0,[lr, #-1] @ pass our target PC
+ mov r2,#kSVSNormal @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r0,rPC @ pass our target PC
+ mov r2,#kSVSNoChain @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+#else
+/*
+ * Return from the translation cache to the interpreter when the compiler is
+ * having issues translating/executing a Dalvik instruction. We have to skip
+ * the code cache lookup otherwise it is possible to indefinitely bouce
+ * between the interpreter and the code cache if the instruction that fails
+ * to be compiled happens to be at a trace start.
+ */
+ .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov rPC, r0
+#if defined(WITH_JIT_TUNING)
+ mov r0,lr
+ bl dvmBumpPunt;
+#endif
+ EXPORT_PC()
+ mov r0, #0
+ str r0, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ adrl rIBASE, dvmAsmInstructionStart
+ FETCH_INST()
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+
+/*
+ * Return to the interpreter to handle a single instruction.
+ * On entry:
+ * r0 <= PC
+ * r1 <= PC of resume instruction
+ * lr <= resume point in translation
+ */
+ .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+ str lr,[rGLUE,#offGlue_jitResumeNPC]
+ str r1,[rGLUE,#offGlue_jitResumeDPC]
+ mov r1,#kInterpEntryInstr
+ @ enum is 4 byte in aapcs-EABI
+ str r1, [rGLUE, #offGlue_entryPoint]
+ mov rPC,r0
+ EXPORT_PC()
+
+ adrl rIBASE, dvmAsmInstructionStart
+ mov r2,#kJitSingleStep @ Ask for single step and then revert
+ str r2,[rGLUE,#offGlue_jitState]
+ mov r1,#1 @ set changeInterp to bail to debug interp
+ b common_gotoBail
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target. Commonly used for callees.
+ */
+ .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+#if defined(WITH_JIT_TUNING)
+ bl dvmBumpNoChain
+#endif
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r0,rPC
+ bl dvmJitGetCodeAddr @ Is there a translation?
+ str r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0 @ !0 means translation exists
+ bxne r0 @ continue native execution if so
+ b 2f @ branch over to use the interpreter
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target. Commonly used following
+ * invokes.
+ */
+ .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+ ldr rPC,[lr, #-1] @ get our target PC
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ add rINST,lr,#-5 @ save start of chain branch
+ add rINST, #-4 @ .. which is 9 bytes back
+ mov r0,rPC
+ bl dvmJitGetCodeAddr @ Is there a translation?
+ str r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ cmp r0,#0
+ beq 2f
+ mov r1,rINST
+ bl dvmJitChain @ r0<- dvmJitChain(codeAddr,chainAddr)
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0 @ successful chain?
+ bxne r0 @ continue native execution
+ b toInterpreter @ didn't chain - resume with interpreter
+
+/* No translation, so request one if profiling isn't disabled*/
+2:
+ adrl rIBASE, dvmAsmInstructionStart
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_INST()
+ cmp r0, #0
+ movne r2,#kJitTSelectRequestHot @ ask for trace selection
+ bne common_selectTrace
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+
+/*
+ * Return from the translation cache to the interpreter.
+ * The return was done with a BLX from thumb mode, and
+ * the following 32-bit word contains the target rPC value.
+ * Note that lr (r14) will have its low-order bit set to denote
+ * its thumb-mode origin.
+ *
+ * We'll need to stash our lr origin away, recover the new
+ * target and then check to see if there is a translation available
+ * for our new target. If so, we do a translation chain and
+ * go back to native execution. Otherwise, it's back to the
+ * interpreter (after treating this entry as a potential
+ * trace start).
+ */
+ .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+ ldr rPC,[lr, #-1] @ get our target PC
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ add rINST,lr,#-5 @ save start of chain branch
+ add rINST,#-4 @ .. which is 9 bytes back
+#if defined(WITH_JIT_TUNING)
+ bl dvmBumpNormal
+#endif
+ mov r0,rPC
+ bl dvmJitGetCodeAddr @ Is there a translation?
+ str r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ cmp r0,#0
+ beq toInterpreter @ go if not, otherwise do chain
+ mov r1,rINST
+ bl dvmJitChain @ r0<- dvmJitChain(codeAddr,chainAddr)
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0 @ successful chain?
+ bxne r0 @ continue native execution
+ b toInterpreter @ didn't chain - resume with interpreter
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+ .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+#if defined(WITH_JIT_TUNING)
+ bl dvmBumpNoChain
+#endif
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r0,rPC
+ bl dvmJitGetCodeAddr @ Is there a translation?
+ str r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0
+ bxne r0 @ continue native execution if so
+ EXPORT_PC()
+ adrl rIBASE, dvmAsmInstructionStart
+ FETCH_INST()
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+ .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+#if defined(WITH_JIT_TUNING)
+ bl dvmBumpNoChain
+#endif
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r0,rPC
+ bl dvmJitGetCodeAddr @ Is there a translation?
+ str r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0
+ bxne r0 @ continue native execution if so
+#endif
+
+/*
+ * No translation, restore interpreter regs and start interpreting.
+ * rGLUE & rFP were preserved in the translated code, and rPC has
+ * already been restored by the time we get here. We'll need to set
+ * up rIBASE & rINST, and load the address of the JitTable into r0.
+ */
+toInterpreter:
+ EXPORT_PC()
+ adrl rIBASE, dvmAsmInstructionStart
+ FETCH_INST()
+ GET_JIT_PROF_TABLE(r0)
+ @ NOTE: intended fallthrough
+
+/*
+ * Common code to update potential trace start counter, and initiate
+ * a trace-build if appropriate. On entry, rPC should point to the
+ * next instruction to execute, and rINST should be already loaded with
+ * the next opcode word, and r0 holds a pointer to the jit profile
+ * table (pJitProfTable).
+ */
+common_testUpdateProfile:
+ cmp r0,#0
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE_IFEQ(ip) @ if not profiling, fallthrough otherwise */
+
+common_updateProfile:
+ eor r3,rPC,rPC,lsr #12 @ cheap, but fast hash function
+ lsl r3,r3,#(32 - JIT_PROF_SIZE_LOG_2) @ shift out excess bits
+ ldrb r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ get counter
+ GET_INST_OPCODE(ip)
+ subs r1,r1,#1 @ decrement counter
+ strb r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ and store it
+ GOTO_OPCODE_IFNE(ip) @ if not threshold, fallthrough otherwise */
+
+/*
+ * Here, we switch to the debug interpreter to request
+ * trace selection. First, though, check to see if there
+ * is already a native translation in place (and, if so,
+ * jump to it now).
+ */
+ GET_JIT_THRESHOLD(r1)
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ strb r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ reset counter
+ EXPORT_PC()
+ mov r0,rPC
+ bl dvmJitGetCodeAddr @ r0<- dvmJitGetCodeAddr(rPC)
+ str r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0
+#if !defined(WITH_SELF_VERIFICATION)
+ bxne r0 @ jump to the translation
+ mov r2,#kJitTSelectRequest @ ask for trace selection
+ @ fall-through to common_selectTrace
+#else
+ moveq r2,#kJitTSelectRequest @ ask for trace selection
+ beq common_selectTrace
+ /*
+ * At this point, we have a target translation. However, if
+ * that translation is actually the interpret-only pseudo-translation
+ * we want to treat it the same as no translation.
+ */
+ mov r10, r0 @ save target
+ bl dvmCompilerGetInterpretTemplate
+ cmp r0, r10 @ special case?
+ bne jitSVShadowRunStart @ set up self verification shadow space
+ @ Need to clear the inJitCodeCache flag
+ ldr r10, [rGLUE, #offGlue_self] @ r10 <- glue->self
+ mov r3, #0 @ 0 means not in the JIT code cache
+ str r3, [r10, #offThread_inJitCodeCache] @ back to the interp land
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+ /* no return */
+#endif
+
+/*
+ * On entry:
+ * r2 is jit state, e.g. kJitTSelectRequest or kJitTSelectRequestHot
+ */
+common_selectTrace:
+ str r2,[rGLUE,#offGlue_jitState]
+ mov r2,#kInterpEntryInstr @ normal entry reason
+ str r2,[rGLUE,#offGlue_entryPoint]
+ mov r1,#1 @ set changeInterp
+ b common_gotoBail
+
+#if defined(WITH_SELF_VERIFICATION)
+/*
+ * Save PC and registers to shadow memory for self verification mode
+ * before jumping to native translation.
+ * On entry:
+ * rPC, rFP, rGLUE: the values that they should contain
+ * r10: the address of the target translation.
+ */
+jitSVShadowRunStart:
+ mov r0,rPC @ r0<- program counter
+ mov r1,rFP @ r1<- frame pointer
+ mov r2,rGLUE @ r2<- InterpState pointer
+ mov r3,r10 @ r3<- target translation
+ bl dvmSelfVerificationSaveState @ save registers to shadow space
+ ldr rFP,[r0,#offShadowSpace_shadowFP] @ rFP<- fp in shadow space
+ add rGLUE,r0,#offShadowSpace_interpState @ rGLUE<- rGLUE in shadow space
+ bx r10 @ jump to the translation
+
+/*
+ * Restore PC, registers, and interpState to original values
+ * before jumping back to the interpreter.
+ */
+jitSVShadowRunEnd:
+ mov r1,rFP @ pass ending fp
+ bl dvmSelfVerificationRestoreState @ restore pc and fp values
+ ldr rPC,[r0,#offShadowSpace_startPC] @ restore PC
+ ldr rFP,[r0,#offShadowSpace_fp] @ restore FP
+ ldr rGLUE,[r0,#offShadowSpace_glue] @ restore InterpState
+ ldr r1,[r0,#offShadowSpace_svState] @ get self verification state
+ cmp r1,#0 @ check for punt condition
+ beq 1f
+ mov r2,#kJitSelfVerification @ ask for self verification
+ str r2,[rGLUE,#offGlue_jitState]
+ mov r2,#kInterpEntryInstr @ normal entry reason
+ str r2,[rGLUE,#offGlue_entryPoint]
+ mov r1,#1 @ set changeInterp
+ b common_gotoBail
+
+1: @ exit to interpreter without check
+ EXPORT_PC()
+ adrl rIBASE, dvmAsmInstructionStart
+ FETCH_INST()
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+#endif
+
+#endif
+
+/*
+ * Common code when a backward branch is taken.
+ *
+ * TODO: we could avoid a branch by just setting r0 and falling through
+ * into the common_periodicChecks code, and having a test on r0 at the
+ * end determine if we should return to the caller or update & branch to
+ * the next instr.
+ *
+ * On entry:
+ * r9 is PC adjustment *in bytes*
+ */
+common_backwardBranch:
+ mov r0, #kInterpEntryInstr
+ bl common_periodicChecks
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/*
+ * Need to see if the thread needs to be suspended or debugger/profiler
+ * activity has begun. If so, we suspend the thread or side-exit to
+ * the debug interpreter as appropriate.
+ *
+ * The common case is no activity on any of these, so we want to figure
+ * that out quickly. If something is up, we can then sort out what.
+ *
+ * We want to be fast if the VM was built without debugger or profiler
+ * support, but we also need to recognize that the system is usually
+ * shipped with both of these enabled.
+ *
+ * TODO: reduce this so we're just checking a single location.
+ *
+ * On entry:
+ * r0 is reentry type, e.g. kInterpEntryInstr (for debugger/profiling)
+ * r9 is trampoline PC adjustment *in bytes*
+ */
+common_periodicChecks:
+ ldr r3, [rGLUE, #offGlue_pSelfSuspendCount] @ r3<- &suspendCount
+
+ ldr r1, [rGLUE, #offGlue_pDebuggerActive] @ r1<- &debuggerActive
+ ldr r2, [rGLUE, #offGlue_pActiveProfilers] @ r2<- &activeProfilers
+
+ ldr ip, [r3] @ ip<- suspendCount (int)
+
+ cmp r1, #0 @ debugger enabled?
+ ldrneb r1, [r1] @ yes, r1<- debuggerActive (boolean)
+ ldr r2, [r2] @ r2<- activeProfilers (int)
+ orrne ip, ip, r1 @ ip<- suspendCount | debuggerActive
+ orrs ip, ip, r2 @ ip<- suspend|debugger|profiler; set Z
+
+ bxeq lr @ all zero, return
+
+ /*
+ * One or more interesting events have happened. Figure out what.
+ *
+ * If debugging or profiling are compiled in, we need to disambiguate.
+ *
+ * r0 still holds the reentry type.
+ */
+ ldr ip, [r3] @ ip<- suspendCount (int)
+ cmp ip, #0 @ want suspend?
+ beq 1f @ no, must be debugger/profiler
+
+ stmfd sp!, {r0, lr} @ preserve r0 and lr
+#if defined(WITH_JIT)
+ /*
+ * Refresh the Jit's cached copy of profile table pointer. This pointer
+ * doubles as the Jit's on/off switch.
+ */
+ ldr r3, [rGLUE, #offGlue_ppJitProfTable] @ r3<-&gDvmJit.pJitProfTable
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ ldr r3, [r3] @ r3 <- pJitProfTable
+ EXPORT_PC() @ need for precise GC
+ str r3, [rGLUE, #offGlue_pJitProfTable] @ refresh Jit's on/off switch
+#else
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ EXPORT_PC() @ need for precise GC
+#endif
+ bl dvmCheckSuspendPending @ do full check, suspend if necessary
+ ldmfd sp!, {r0, lr} @ restore r0 and lr
+
+ /*
+ * Reload the debugger/profiler enable flags. We're checking to see
+ * if either of these got set while we were suspended.
+ *
+ * We can't really avoid the #ifdefs here, because the fields don't
+ * exist when the feature is disabled.
+ */
+ ldr r1, [rGLUE, #offGlue_pDebuggerActive] @ r1<- &debuggerActive
+ cmp r1, #0 @ debugger enabled?
+ ldrneb r1, [r1] @ yes, r1<- debuggerActive (boolean)
+ ldr r2, [rGLUE, #offGlue_pActiveProfilers] @ r2<- &activeProfilers
+ ldr r2, [r2] @ r2<- activeProfilers (int)
+
+ orrs r1, r1, r2
+ beq 2f
+
+1: @ debugger/profiler enabled, bail out; glue->entryPoint was set above
+ str r0, [rGLUE, #offGlue_entryPoint] @ store r0, need for debug/prof
+ add rPC, rPC, r9 @ update rPC
+ mov r1, #1 @ "want switch" = true
+ b common_gotoBail @ side exit
+
+2:
+ bx lr @ nothing to do, return
+
+
+/*
+ * The equivalent of "goto bail", this calls through the "bail handler".
+ *
+ * State registers will be saved to the "glue" area before bailing.
+ *
+ * On entry:
+ * r1 is "bool changeInterp", indicating if we want to switch to the
+ * other interpreter or just bail all the way out
+ */
+common_gotoBail:
+ SAVE_PC_FP_TO_GLUE() @ export state to "glue"
+ mov r0, rGLUE @ r0<- glue ptr
+ b dvmMterpStdBail @ call(glue, changeInterp)
+
+ @add r1, r1, #1 @ using (boolean+1)
+ @add r0, rGLUE, #offGlue_jmpBuf @ r0<- &glue->jmpBuf
+ @bl _longjmp @ does not return
+ @bl common_abort
+
+
+/*
+ * Common code for method invocation with range.
+ *
+ * On entry:
+ * r0 is "Method* methodToCall", the method we're trying to call
+ */
+common_invokeMethodRange:
+.LinvokeNewRange:
+ @ prepare to copy args to "outs" area of current frame
+ movs r2, rINST, lsr #8 @ r2<- AA (arg count) -- test for zero
+ SAVEAREA_FROM_FP(r10, rFP) @ r10<- stack save area
+ beq .LinvokeArgsDone @ if no args, skip the rest
+ FETCH(r1, 2) @ r1<- CCCC
+
+ @ r0=methodToCall, r1=CCCC, r2=count, r10=outs
+ @ (very few methods have > 10 args; could unroll for common cases)
+ add r3, rFP, r1, lsl #2 @ r3<- &fp[CCCC]
+ sub r10, r10, r2, lsl #2 @ r10<- "outs" area, for call args
+ ldrh r9, [r0, #offMethod_registersSize] @ r9<- methodToCall->regsSize
+1: ldr r1, [r3], #4 @ val = *fp++
+ subs r2, r2, #1 @ count--
+ str r1, [r10], #4 @ *outs++ = val
+ bne 1b @ ...while count != 0
+ ldrh r3, [r0, #offMethod_outsSize] @ r3<- methodToCall->outsSize
+ b .LinvokeArgsDone
+
+/*
+ * Common code for method invocation without range.
+ *
+ * On entry:
+ * r0 is "Method* methodToCall", the method we're trying to call
+ */
+common_invokeMethodNoRange:
+.LinvokeNewNoRange:
+ @ prepare to copy args to "outs" area of current frame
+ movs r2, rINST, lsr #12 @ r2<- B (arg count) -- test for zero
+ SAVEAREA_FROM_FP(r10, rFP) @ r10<- stack save area
+ FETCH(r1, 2) @ r1<- GFED (load here to hide latency)
+ ldrh r9, [r0, #offMethod_registersSize] @ r9<- methodToCall->regsSize
+ ldrh r3, [r0, #offMethod_outsSize] @ r3<- methodToCall->outsSize
+ beq .LinvokeArgsDone
+
+ @ r0=methodToCall, r1=GFED, r3=outSize, r2=count, r9=regSize, r10=outs
+.LinvokeNonRange:
+ rsb r2, r2, #5 @ r2<- 5-r2
+ add pc, pc, r2, lsl #4 @ computed goto, 4 instrs each
+ bl common_abort @ (skipped due to ARM prefetch)
+5: and ip, rINST, #0x0f00 @ isolate A
+ ldr r2, [rFP, ip, lsr #6] @ r2<- vA (shift right 8, left 2)
+ mov r0, r0 @ nop
+ str r2, [r10, #-4]! @ *--outs = vA
+4: and ip, r1, #0xf000 @ isolate G
+ ldr r2, [rFP, ip, lsr #10] @ r2<- vG (shift right 12, left 2)
+ mov r0, r0 @ nop
+ str r2, [r10, #-4]! @ *--outs = vG
+3: and ip, r1, #0x0f00 @ isolate F
+ ldr r2, [rFP, ip, lsr #6] @ r2<- vF
+ mov r0, r0 @ nop
+ str r2, [r10, #-4]! @ *--outs = vF
+2: and ip, r1, #0x00f0 @ isolate E
+ ldr r2, [rFP, ip, lsr #2] @ r2<- vE
+ mov r0, r0 @ nop
+ str r2, [r10, #-4]! @ *--outs = vE
+1: and ip, r1, #0x000f @ isolate D
+ ldr r2, [rFP, ip, lsl #2] @ r2<- vD
+ mov r0, r0 @ nop
+ str r2, [r10, #-4]! @ *--outs = vD
+0: @ fall through to .LinvokeArgsDone
+
+.LinvokeArgsDone: @ r0=methodToCall, r3=outSize, r9=regSize
+ ldr r2, [r0, #offMethod_insns] @ r2<- method->insns
+ ldr rINST, [r0, #offMethod_clazz] @ rINST<- method->clazz
+ @ find space for the new stack frame, check for overflow
+ SAVEAREA_FROM_FP(r1, rFP) @ r1<- stack save area
+ sub r1, r1, r9, lsl #2 @ r1<- newFp (old savearea - regsSize)
+ SAVEAREA_FROM_FP(r10, r1) @ r10<- newSaveArea
+@ bl common_dumpRegs
+ ldr r9, [rGLUE, #offGlue_interpStackEnd] @ r9<- interpStackEnd
+ sub r3, r10, r3, lsl #2 @ r3<- bottom (newsave - outsSize)
+ cmp r3, r9 @ bottom < interpStackEnd?
+ ldr r3, [r0, #offMethod_accessFlags] @ r3<- methodToCall->accessFlags
+ blo .LstackOverflow @ yes, this frame will overflow stack
+
+ @ set up newSaveArea
+#ifdef EASY_GDB
+ SAVEAREA_FROM_FP(ip, rFP) @ ip<- stack save area
+ str ip, [r10, #offStackSaveArea_prevSave]
+#endif
+ str rFP, [r10, #offStackSaveArea_prevFrame]
+ str rPC, [r10, #offStackSaveArea_savedPc]
+#if defined(WITH_JIT)
+ mov r9, #0
+ str r9, [r10, #offStackSaveArea_returnAddr]
+#endif
+ str r0, [r10, #offStackSaveArea_method]
+ tst r3, #ACC_NATIVE
+ bne .LinvokeNative
+
+ /*
+ stmfd sp!, {r0-r3}
+ bl common_printNewline
+ mov r0, rFP
+ mov r1, #0
+ bl dvmDumpFp
+ ldmfd sp!, {r0-r3}
+ stmfd sp!, {r0-r3}
+ mov r0, r1
+ mov r1, r10
+ bl dvmDumpFp
+ bl common_printNewline
+ ldmfd sp!, {r0-r3}
+ */
+
+ ldrh r9, [r2] @ r9 <- load INST from new PC
+ ldr r3, [rINST, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+ mov rPC, r2 @ publish new rPC
+ ldr r2, [rGLUE, #offGlue_self] @ r2<- glue->self
+
+ @ Update "glue" values for the new method
+ @ r0=methodToCall, r1=newFp, r2=self, r3=newMethodClass, r9=newINST
+ str r0, [rGLUE, #offGlue_method] @ glue->method = methodToCall
+ str r3, [rGLUE, #offGlue_methodClassDex] @ glue->methodClassDex = ...
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ mov rFP, r1 @ fp = newFp
+ GET_PREFETCHED_OPCODE(ip, r9) @ extract prefetched opcode from r9
+ mov rINST, r9 @ publish new rINST
+ str r1, [r2, #offThread_curFrame] @ self->curFrame = newFp
+ cmp r0,#0
+ bne common_updateProfile
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ mov rFP, r1 @ fp = newFp
+ GET_PREFETCHED_OPCODE(ip, r9) @ extract prefetched opcode from r9
+ mov rINST, r9 @ publish new rINST
+ str r1, [r2, #offThread_curFrame] @ self->curFrame = newFp
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+.LinvokeNative:
+ @ Prep for the native call
+ @ r0=methodToCall, r1=newFp, r10=newSaveArea
+ ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
+ ldr r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
+ str r1, [r3, #offThread_curFrame] @ self->curFrame = newFp
+ str r9, [r10, #offStackSaveArea_localRefCookie] @newFp->localRefCookie=top
+ mov r9, r3 @ r9<- glue->self (preserve)
+
+ mov r2, r0 @ r2<- methodToCall
+ mov r0, r1 @ r0<- newFp (points to args)
+ add r1, rGLUE, #offGlue_retval @ r1<- &retval
+
+#ifdef ASSIST_DEBUGGER
+ /* insert fake function header to help gdb find the stack frame */
+ b .Lskip
+ .type dalvik_mterp, %function
+dalvik_mterp:
+ .fnstart
+ MTERP_ENTRY1
+ MTERP_ENTRY2
+.Lskip:
+#endif
+
+ @mov lr, pc @ set return addr
+ @ldr pc, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
+ LDR_PC_LR "[r2, #offMethod_nativeFunc]"
+
+#if defined(WITH_JIT)
+ ldr r3, [rGLUE, #offGlue_ppJitProfTable] @ Refresh Jit's on/off status
+#endif
+
+ @ native return; r9=self, r10=newSaveArea
+ @ equivalent to dvmPopJniLocals
+ ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved top
+ ldr r1, [r9, #offThread_exception] @ check for exception
+#if defined(WITH_JIT)
+ ldr r3, [r3] @ r3 <- gDvmJit.pProfTable
+#endif
+ str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
+ cmp r1, #0 @ null?
+ str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
+#if defined(WITH_JIT)
+ str r3, [rGLUE, #offGlue_pJitProfTable] @ refresh cached on/off switch
+#endif
+ bne common_exceptionThrown @ no, handle exception
+
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+.LstackOverflow: @ r0=methodToCall
+ mov r1, r0 @ r1<- methodToCall
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- self
+ bl dvmHandleStackOverflow
+ b common_exceptionThrown
+#ifdef ASSIST_DEBUGGER
+ .fnend
+#endif
+
+
+ /*
+ * Common code for method invocation, calling through "glue code".
+ *
+ * TODO: now that we have range and non-range invoke handlers, this
+ * needs to be split into two. Maybe just create entry points
+ * that set r9 and jump here?
+ *
+ * On entry:
+ * r0 is "Method* methodToCall", the method we're trying to call
+ * r9 is "bool methodCallRange", indicating if this is a /range variant
+ */
+ .if 0
+.LinvokeOld:
+ sub sp, sp, #8 @ space for args + pad
+ FETCH(ip, 2) @ ip<- FEDC or CCCC
+ mov r2, r0 @ A2<- methodToCall
+ mov r0, rGLUE @ A0<- glue
+ SAVE_PC_FP_TO_GLUE() @ export state to "glue"
+ mov r1, r9 @ A1<- methodCallRange
+ mov r3, rINST, lsr #8 @ A3<- AA
+ str ip, [sp, #0] @ A4<- ip
+ bl dvmMterp_invokeMethod @ call the C invokeMethod
+ add sp, sp, #8 @ remove arg area
+ b common_resumeAfterGlueCall @ continue to next instruction
+ .endif
+
+
+
+/*
+ * Common code for handling a return instruction.
+ *
+ * This does not return.
+ */
+common_returnFromMethod:
+.LreturnNew:
+ mov r0, #kInterpEntryReturn
+ mov r9, #0
+ bl common_periodicChecks
+
+ SAVEAREA_FROM_FP(r0, rFP) @ r0<- saveArea (old)
+ ldr rFP, [r0, #offStackSaveArea_prevFrame] @ fp = saveArea->prevFrame
+ ldr r9, [r0, #offStackSaveArea_savedPc] @ r9 = saveArea->savedPc
+ ldr r2, [rFP, #(offStackSaveArea_method - sizeofStackSaveArea)]
+ @ r2<- method we're returning to
+ ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
+ cmp r2, #0 @ is this a break frame?
+ ldrne r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+ mov r1, #0 @ "want switch" = false
+ beq common_gotoBail @ break frame, bail out completely
+
+ PREFETCH_ADVANCE_INST(rINST, r9, 3) @ advance r9, update new rINST
+ str r2, [rGLUE, #offGlue_method]@ glue->method = newSave->method
+ ldr r1, [r10, #offClassObject_pDvmDex] @ r1<- method->clazz->pDvmDex
+ str rFP, [r3, #offThread_curFrame] @ self->curFrame = fp
+#if defined(WITH_JIT)
+ ldr r10, [r0, #offStackSaveArea_returnAddr] @ r10 = saveArea->returnAddr
+ mov rPC, r9 @ publish new rPC
+ str r1, [rGLUE, #offGlue_methodClassDex]
+ str r10, [r3, #offThread_inJitCodeCache] @ may return to JIT'ed land
+ cmp r10, #0 @ caller is compiled code
+ blxne r10
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ mov rPC, r9 @ publish new rPC
+ str r1, [rGLUE, #offGlue_methodClassDex]
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+ /*
+ * Return handling, calls through "glue code".
+ */
+ .if 0
+.LreturnOld:
+ SAVE_PC_FP_TO_GLUE() @ export state
+ mov r0, rGLUE @ arg to function
+ bl dvmMterp_returnFromMethod
+ b common_resumeAfterGlueCall
+ .endif
+
+
+/*
+ * Somebody has thrown an exception. Handle it.
+ *
+ * If the exception processing code returns to us (instead of falling
+ * out of the interpreter), continue with whatever the next instruction
+ * now happens to be.
+ *
+ * This does not return.
+ */
+ .global dvmMterpCommonExceptionThrown
+dvmMterpCommonExceptionThrown:
+common_exceptionThrown:
+.LexceptionNew:
+ mov r0, #kInterpEntryThrow
+ mov r9, #0
+ bl common_periodicChecks
+
+ ldr r10, [rGLUE, #offGlue_self] @ r10<- glue->self
+ ldr r9, [r10, #offThread_exception] @ r9<- self->exception
+ mov r1, r10 @ r1<- self
+ mov r0, r9 @ r0<- exception
+ bl dvmAddTrackedAlloc @ don't let the exception be GCed
+ mov r3, #0 @ r3<- NULL
+ str r3, [r10, #offThread_exception] @ self->exception = NULL
+
+ /* set up args and a local for "&fp" */
+ /* (str sp, [sp, #-4]! would be perfect here, but is discouraged) */
+ str rFP, [sp, #-4]! @ *--sp = fp
+ mov ip, sp @ ip<- &fp
+ mov r3, #0 @ r3<- false
+ str ip, [sp, #-4]! @ *--sp = &fp
+ ldr r1, [rGLUE, #offGlue_method] @ r1<- glue->method
+ mov r0, r10 @ r0<- self
+ ldr r1, [r1, #offMethod_insns] @ r1<- method->insns
+ mov r2, r9 @ r2<- exception
+ sub r1, rPC, r1 @ r1<- pc - method->insns
+ mov r1, r1, asr #1 @ r1<- offset in code units
+
+ /* call, r0 gets catchRelPc (a code-unit offset) */
+ bl dvmFindCatchBlock @ call(self, relPc, exc, scan?, &fp)
+
+ /* fix earlier stack overflow if necessary; may trash rFP */
+ ldrb r1, [r10, #offThread_stackOverflowed]
+ cmp r1, #0 @ did we overflow earlier?
+ beq 1f @ no, skip ahead
+ mov rFP, r0 @ save relPc result in rFP
+ mov r0, r10 @ r0<- self
+ mov r1, r9 @ r1<- exception
+ bl dvmCleanupStackOverflow @ call(self)
+ mov r0, rFP @ restore result
+1:
+
+ /* update frame pointer and check result from dvmFindCatchBlock */
+ ldr rFP, [sp, #4] @ retrieve the updated rFP
+ cmp r0, #0 @ is catchRelPc < 0?
+ add sp, sp, #8 @ restore stack
+ bmi .LnotCaughtLocally
+
+ /* adjust locals to match self->curFrame and updated PC */
+ SAVEAREA_FROM_FP(r1, rFP) @ r1<- new save area
+ ldr r1, [r1, #offStackSaveArea_method] @ r1<- new method
+ str r1, [rGLUE, #offGlue_method] @ glue->method = new method
+ ldr r2, [r1, #offMethod_clazz] @ r2<- method->clazz
+ ldr r3, [r1, #offMethod_insns] @ r3<- method->insns
+ ldr r2, [r2, #offClassObject_pDvmDex] @ r2<- method->clazz->pDvmDex
+ add rPC, r3, r0, asl #1 @ rPC<- method->insns + catchRelPc
+ str r2, [rGLUE, #offGlue_methodClassDex] @ glue->pDvmDex = meth...
+
+ /* release the tracked alloc on the exception */
+ mov r0, r9 @ r0<- exception
+ mov r1, r10 @ r1<- self
+ bl dvmReleaseTrackedAlloc @ release the exception
+
+ /* restore the exception if the handler wants it */
+ FETCH_INST() @ load rINST from rPC
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ cmp ip, #OP_MOVE_EXCEPTION @ is it "move-exception"?
+ streq r9, [r10, #offThread_exception] @ yes, restore the exception
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+.LnotCaughtLocally: @ r9=exception, r10=self
+ /* fix stack overflow if necessary */
+ ldrb r1, [r10, #offThread_stackOverflowed]
+ cmp r1, #0 @ did we overflow earlier?
+ movne r0, r10 @ if yes: r0<- self
+ movne r1, r9 @ if yes: r1<- exception
+ blne dvmCleanupStackOverflow @ if yes: call(self)
+
+ @ may want to show "not caught locally" debug messages here
+#if DVM_SHOW_EXCEPTION >= 2
+ /* call __android_log_print(prio, tag, format, ...) */
+ /* "Exception %s from %s:%d not caught locally" */
+ @ dvmLineNumFromPC(method, pc - method->insns)
+ ldr r0, [rGLUE, #offGlue_method]
+ ldr r1, [r0, #offMethod_insns]
+ sub r1, rPC, r1
+ asr r1, r1, #1
+ bl dvmLineNumFromPC
+ str r0, [sp, #-4]!
+ @ dvmGetMethodSourceFile(method)
+ ldr r0, [rGLUE, #offGlue_method]
+ bl dvmGetMethodSourceFile
+ str r0, [sp, #-4]!
+ @ exception->clazz->descriptor
+ ldr r3, [r9, #offObject_clazz]
+ ldr r3, [r3, #offClassObject_descriptor]
+ @
+ ldr r2, strExceptionNotCaughtLocally
+ ldr r1, strLogTag
+ mov r0, #3 @ LOG_DEBUG
+ bl __android_log_print
+#endif
+ str r9, [r10, #offThread_exception] @ restore exception
+ mov r0, r9 @ r0<- exception
+ mov r1, r10 @ r1<- self
+ bl dvmReleaseTrackedAlloc @ release the exception
+ mov r1, #0 @ "want switch" = false
+ b common_gotoBail @ bail out
+
+
+ /*
+ * Exception handling, calls through "glue code".
+ */
+ .if 0
+.LexceptionOld:
+ SAVE_PC_FP_TO_GLUE() @ export state
+ mov r0, rGLUE @ arg to function
+ bl dvmMterp_exceptionThrown
+ b common_resumeAfterGlueCall
+ .endif
+
+
+/*
+ * After returning from a "glued" function, pull out the updated
+ * values and start executing at the next instruction.
+ */
+common_resumeAfterGlueCall:
+ LOAD_PC_FP_FROM_GLUE() @ pull rPC and rFP out of glue
+ FETCH_INST() @ load rINST from rPC
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/*
+ * Invalid array index.
+ */
+common_errArrayIndex:
+ EXPORT_PC()
+ ldr r0, strArrayIndexException
+ mov r1, #0
+ bl dvmThrowException
+ b common_exceptionThrown
+
+/*
+ * Invalid array value.
+ */
+common_errArrayStore:
+ EXPORT_PC()
+ ldr r0, strArrayStoreException
+ mov r1, #0
+ bl dvmThrowException
+ b common_exceptionThrown
+
+/*
+ * Integer divide or mod by zero.
+ */
+common_errDivideByZero:
+ EXPORT_PC()
+ ldr r0, strArithmeticException
+ ldr r1, strDivideByZero
+ bl dvmThrowException
+ b common_exceptionThrown
+
+/*
+ * Attempt to allocate an array with a negative size.
+ */
+common_errNegativeArraySize:
+ EXPORT_PC()
+ ldr r0, strNegativeArraySizeException
+ mov r1, #0
+ bl dvmThrowException
+ b common_exceptionThrown
+
+/*
+ * Invocation of a non-existent method.
+ */
+common_errNoSuchMethod:
+ EXPORT_PC()
+ ldr r0, strNoSuchMethodError
+ mov r1, #0
+ bl dvmThrowException
+ b common_exceptionThrown
+
+/*
+ * We encountered a null object when we weren't expecting one. We
+ * export the PC, throw a NullPointerException, and goto the exception
+ * processing code.
+ */
+common_errNullObject:
+ EXPORT_PC()
+ ldr r0, strNullPointerException
+ mov r1, #0
+ bl dvmThrowException
+ b common_exceptionThrown
+
+/*
+ * For debugging, cause an immediate fault. The source address will
+ * be in lr (use a bl instruction to jump here).
+ */
+common_abort:
+ ldr pc, .LdeadFood
+.LdeadFood:
+ .word 0xdeadf00d
+
+/*
+ * Spit out a "we were here", preserving all registers. (The attempt
+ * to save ip won't work, but we need to save an even number of
+ * registers for EABI 64-bit stack alignment.)
+ */
+ .macro SQUEAK num
+common_squeak\num:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ ldr r0, strSqueak
+ mov r1, #\num
+ bl printf
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+ .endm
+
+ SQUEAK 0
+ SQUEAK 1
+ SQUEAK 2
+ SQUEAK 3
+ SQUEAK 4
+ SQUEAK 5
+
+/*
+ * Spit out the number in r0, preserving registers.
+ */
+common_printNum:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ mov r1, r0
+ ldr r0, strSqueak
+ bl printf
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+
+/*
+ * Print a newline, preserving registers.
+ */
+common_printNewline:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ ldr r0, strNewline
+ bl printf
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+
+ /*
+ * Print the 32-bit quantity in r0 as a hex value, preserving registers.
+ */
+common_printHex:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ mov r1, r0
+ ldr r0, strPrintHex
+ bl printf
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+
+/*
+ * Print the 64-bit quantity in r0-r1, preserving registers.
+ */
+common_printLong:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ mov r3, r1
+ mov r2, r0
+ ldr r0, strPrintLong
+ bl printf
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+
+/*
+ * Print full method info. Pass the Method* in r0. Preserves regs.
+ */
+common_printMethod:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bl dvmMterpPrintMethod
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+
+/*
+ * Call a C helper function that dumps regs and possibly some
+ * additional info. Requires the C function to be compiled in.
+ */
+ .if 0
+common_dumpRegs:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bl dvmMterpDumpArmRegs
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+ .endif
+
+#if 0
+/*
+ * Experiment on VFP mode.
+ *
+ * uint32_t setFPSCR(uint32_t val, uint32_t mask)
+ *
+ * Updates the bits specified by "mask", setting them to the values in "val".
+ */
+setFPSCR:
+ and r0, r0, r1 @ make sure no stray bits are set
+ fmrx r2, fpscr @ get VFP reg
+ mvn r1, r1 @ bit-invert mask
+ and r2, r2, r1 @ clear masked bits
+ orr r2, r2, r0 @ set specified bits
+ fmxr fpscr, r2 @ set VFP reg
+ mov r0, r2 @ return new value
+ bx lr
+
+ .align 2
+ .global dvmConfigureFP
+ .type dvmConfigureFP, %function
+dvmConfigureFP:
+ stmfd sp!, {ip, lr}
+ /* 0x03000000 sets DN/FZ */
+ /* 0x00009f00 clears the six exception enable flags */
+ bl common_squeak0
+ mov r0, #0x03000000 @ r0<- 0x03000000
+ add r1, r0, #0x9f00 @ r1<- 0x03009f00
+ bl setFPSCR
+ ldmfd sp!, {ip, pc}
+#endif
+
+
+/*
+ * String references, must be close to the code that uses them.
+ */
+ .align 2
+strArithmeticException:
+ .word .LstrArithmeticException
+strArrayIndexException:
+ .word .LstrArrayIndexException
+strArrayStoreException:
+ .word .LstrArrayStoreException
+strDivideByZero:
+ .word .LstrDivideByZero
+strNegativeArraySizeException:
+ .word .LstrNegativeArraySizeException
+strNoSuchMethodError:
+ .word .LstrNoSuchMethodError
+strNullPointerException:
+ .word .LstrNullPointerException
+
+strLogTag:
+ .word .LstrLogTag
+strExceptionNotCaughtLocally:
+ .word .LstrExceptionNotCaughtLocally
+
+strNewline:
+ .word .LstrNewline
+strSqueak:
+ .word .LstrSqueak
+strPrintHex:
+ .word .LstrPrintHex
+strPrintLong:
+ .word .LstrPrintLong
+
+/*
+ * Zero-terminated ASCII string data.
+ *
+ * On ARM we have two choices: do like gcc does, and LDR from a .word
+ * with the address, or use an ADR pseudo-op to get the address
+ * directly. ADR saves 4 bytes and an indirection, but it's using a
+ * PC-relative addressing mode and hence has a limited range, which
+ * makes it not work well with mergeable string sections.
+ */
+ .section .rodata.str1.4,"aMS",%progbits,1
+
+.LstrBadEntryPoint:
+ .asciz "Bad entry point %d\n"
+.LstrArithmeticException:
+ .asciz "Ljava/lang/ArithmeticException;"
+.LstrArrayIndexException:
+ .asciz "Ljava/lang/ArrayIndexOutOfBoundsException;"
+.LstrArrayStoreException:
+ .asciz "Ljava/lang/ArrayStoreException;"
+.LstrClassCastException:
+ .asciz "Ljava/lang/ClassCastException;"
+.LstrDivideByZero:
+ .asciz "divide by zero"
+.LstrFilledNewArrayNotImpl:
+ .asciz "filled-new-array only implemented for objects and 'int'"
+.LstrInternalError:
+ .asciz "Ljava/lang/InternalError;"
+.LstrInstantiationError:
+ .asciz "Ljava/lang/InstantiationError;"
+.LstrNegativeArraySizeException:
+ .asciz "Ljava/lang/NegativeArraySizeException;"
+.LstrNoSuchMethodError:
+ .asciz "Ljava/lang/NoSuchMethodError;"
+.LstrNullPointerException:
+ .asciz "Ljava/lang/NullPointerException;"
+
+.LstrLogTag:
+ .asciz "mterp"
+.LstrExceptionNotCaughtLocally:
+ .asciz "Exception %s from %s:%d not caught locally\n"
+
+.LstrNewline:
+ .asciz "\n"
+.LstrSqueak:
+ .asciz "<%d>"
+.LstrPrintHex:
+ .asciz "<0x%x>"
+.LstrPrintLong:
+ .asciz "<%lld>"
diff --git a/vm/mterp/armv5te/header.S b/vm/mterp/armv5te/header.S
new file mode 100644
index 0000000..bd6b7ee
--- /dev/null
+++ b/vm/mterp/armv5te/header.S
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * ARMv5 definitions and declarations.
+ */
+
+/*
+ARM EABI general notes:
+
+r0-r3 hold first 4 args to a method; they are not preserved across method calls
+r4-r8 are available for general use
+r9 is given special treatment in some situations, but not for us
+r10 (sl) seems to be generally available
+r11 (fp) is used by gcc (unless -fomit-frame-pointer is set)
+r12 (ip) is scratch -- not preserved across method calls
+r13 (sp) should be managed carefully in case a signal arrives
+r14 (lr) must be preserved
+r15 (pc) can be tinkered with directly
+
+r0 holds returns of <= 4 bytes
+r0-r1 hold returns of 8 bytes, low word in r0
+
+Callee must save/restore r4+ (except r12) if it modifies them. If VFP
+is present, registers s16-s31 (a/k/a d8-d15, a/k/a q4-q7) must be preserved,
+s0-s15 (d0-d7, q0-a3) do not need to be.
+
+Stack is "full descending". Only the arguments that don't fit in the first 4
+registers are placed on the stack. "sp" points at the first stacked argument
+(i.e. the 5th arg).
+
+VFP: single-precision results in s0, double-precision results in d0.
+
+In the EABI, "sp" must be 64-bit aligned on entry to a function, and any
+64-bit quantities (long long, double) must be 64-bit aligned.
+*/
+
+/*
+Mterp and ARM notes:
+
+The following registers have fixed assignments:
+
+ reg nick purpose
+ r4 rPC interpreted program counter, used for fetching instructions
+ r5 rFP interpreted frame pointer, used for accessing locals and args
+ r6 rGLUE MterpGlue pointer
+ r7 rINST first 16-bit code unit of current instruction
+ r8 rIBASE interpreted instruction base pointer, used for computed goto
+
+Macros are provided for common operations. Each macro MUST emit only
+one instruction to make instruction-counting easier. They MUST NOT alter
+unspecified registers or condition codes.
+*/
+
+/* single-purpose registers, given names for clarity */
+#define rPC r4
+#define rFP r5
+#define rGLUE r6
+#define rINST r7
+#define rIBASE r8
+
+/* save/restore the PC and/or FP from the glue struct */
+#define LOAD_PC_FROM_GLUE() ldr rPC, [rGLUE, #offGlue_pc]
+#define SAVE_PC_TO_GLUE() str rPC, [rGLUE, #offGlue_pc]
+#define LOAD_FP_FROM_GLUE() ldr rFP, [rGLUE, #offGlue_fp]
+#define SAVE_FP_TO_GLUE() str rFP, [rGLUE, #offGlue_fp]
+#define LOAD_PC_FP_FROM_GLUE() ldmia rGLUE, {rPC, rFP}
+#define SAVE_PC_FP_TO_GLUE() stmia rGLUE, {rPC, rFP}
+
+/*
+ * "export" the PC to the stack frame, f/b/o future exception objects. Must
+ * be done *before* something calls dvmThrowException.
+ *
+ * In C this is "SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc", i.e.
+ * fp - sizeof(StackSaveArea) + offsetof(SaveArea, xtra.currentPc)
+ *
+ * It's okay to do this more than once.
+ */
+#define EXPORT_PC() \
+ str rPC, [rFP, #(-sizeofStackSaveArea + offStackSaveArea_currentPc)]
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+#define SAVEAREA_FROM_FP(_reg, _fpreg) \
+ sub _reg, _fpreg, #sizeofStackSaveArea
+
+/*
+ * Fetch the next instruction from rPC into rINST. Does not advance rPC.
+ */
+#define FETCH_INST() ldrh rINST, [rPC]
+
+/*
+ * Fetch the next instruction from the specified offset. Advances rPC
+ * to point to the next instruction. "_count" is in 16-bit code units.
+ *
+ * Because of the limited size of immediate constants on ARM, this is only
+ * suitable for small forward movements (i.e. don't try to implement "goto"
+ * with this).
+ *
+ * This must come AFTER anything that can throw an exception, or the
+ * exception catch may miss. (This also implies that it must come after
+ * EXPORT_PC().)
+ */
+#define FETCH_ADVANCE_INST(_count) ldrh rINST, [rPC, #(_count*2)]!
+
+/*
+ * The operation performed here is similar to FETCH_ADVANCE_INST, except the
+ * src and dest registers are parameterized (not hard-wired to rPC and rINST).
+ */
+#define PREFETCH_ADVANCE_INST(_dreg, _sreg, _count) \
+ ldrh _dreg, [_sreg, #(_count*2)]!
+
+/*
+ * Fetch the next instruction from an offset specified by _reg. Updates
+ * rPC to point to the next instruction. "_reg" must specify the distance
+ * in bytes, *not* 16-bit code units, and may be a signed value.
+ *
+ * We want to write "ldrh rINST, [rPC, _reg, lsl #2]!", but some of the
+ * bits that hold the shift distance are used for the half/byte/sign flags.
+ * In some cases we can pre-double _reg for free, so we require a byte offset
+ * here.
+ */
+#define FETCH_ADVANCE_INST_RB(_reg) ldrh rINST, [rPC, _reg]!
+
+/*
+ * Fetch a half-word code unit from an offset past the current PC. The
+ * "_count" value is in 16-bit code units. Does not advance rPC.
+ *
+ * The "_S" variant works the same but treats the value as signed.
+ */
+#define FETCH(_reg, _count) ldrh _reg, [rPC, #(_count*2)]
+#define FETCH_S(_reg, _count) ldrsh _reg, [rPC, #(_count*2)]
+
+/*
+ * Fetch one byte from an offset past the current PC. Pass in the same
+ * "_count" as you would for FETCH, and an additional 0/1 indicating which
+ * byte of the halfword you want (lo/hi).
+ */
+#define FETCH_B(_reg, _count, _byte) ldrb _reg, [rPC, #(_count*2+_byte)]
+
+/*
+ * Put the instruction's opcode field into the specified register.
+ */
+#define GET_INST_OPCODE(_reg) and _reg, rINST, #255
+
+/*
+ * Put the prefetched instruction's opcode field into the specified register.
+ */
+#define GET_PREFETCHED_OPCODE(_oreg, _ireg) and _oreg, _ireg, #255
+
+/*
+ * Begin executing the opcode in _reg. Because this only jumps within the
+ * interpreter, we don't have to worry about pre-ARMv5 THUMB interwork.
+ */
+#define GOTO_OPCODE(_reg) add pc, rIBASE, _reg, lsl #${handler_size_bits}
+#define GOTO_OPCODE_IFEQ(_reg) addeq pc, rIBASE, _reg, lsl #${handler_size_bits}
+#define GOTO_OPCODE_IFNE(_reg) addne pc, rIBASE, _reg, lsl #${handler_size_bits}
+
+/*
+ * Get/set the 32-bit value from a Dalvik register.
+ */
+#define GET_VREG(_reg, _vreg) ldr _reg, [rFP, _vreg, lsl #2]
+#define SET_VREG(_reg, _vreg) str _reg, [rFP, _vreg, lsl #2]
+
+#if defined(WITH_JIT)
+#define GET_JIT_PROF_TABLE(_reg) ldr _reg,[rGLUE,#offGlue_pJitProfTable]
+#define GET_JIT_THRESHOLD(_reg) ldr _reg,[rGLUE,#offGlue_jitThreshold]
+#endif
+
+/*
+ * Convert a virtual register index into an address.
+ */
+#define VREG_INDEX_TO_ADDR(_reg, _vreg) \
+ add _reg, rFP, _vreg, lsl #2
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../common/asm-constants.h"
+
+#if defined(WITH_JIT)
+#include "../common/jit-config.h"
+#endif
diff --git a/vm/mterp/armv5te/platform.S b/vm/mterp/armv5te/platform.S
new file mode 100644
index 0000000..9f59c8b
--- /dev/null
+++ b/vm/mterp/armv5te/platform.S
@@ -0,0 +1,41 @@
+/*
+ * ===========================================================================
+ * CPU-version-specific defines
+ * ===========================================================================
+ */
+
+/*
+ * Macro for "LDR PC,xxx", which is not allowed pre-ARMv5. Essentially a
+ * one-way branch.
+ *
+ * May modify IP. Does not modify LR.
+ */
+.macro LDR_PC source
+ ldr pc, \source
+.endm
+
+/*
+ * Macro for "MOV LR,PC / LDR PC,xxx", which is not allowed pre-ARMv5.
+ * Jump to subroutine.
+ *
+ * May modify IP and LR.
+ */
+.macro LDR_PC_LR source
+ mov lr, pc
+ ldr pc, \source
+.endm
+
+/*
+ * Macro for "LDMFD SP!, {...regs...,PC}".
+ *
+ * May modify IP and LR.
+ */
+.macro LDMFD_PC regs
+ ldmfd sp!, {\regs,pc}
+.endm
+
+/*
+ * Macro for data memory barrier; not meaningful pre-ARMv6K.
+ */
+.macro SMP_DMB
+.endm
diff --git a/vm/mterp/armv5te/stub.S b/vm/mterp/armv5te/stub.S
new file mode 100644
index 0000000..54f0778
--- /dev/null
+++ b/vm/mterp/armv5te/stub.S
@@ -0,0 +1,8 @@
+ /* (stub) */
+ SAVE_PC_FP_TO_GLUE() @ only need to export these two
+ mov r0, rGLUE @ glue is first arg to function
+ bl dvmMterp_${opcode} @ call
+ LOAD_PC_FP_FROM_GLUE() @ retrieve updated values
+ FETCH_INST() @ load next instruction from rPC
+ GET_INST_OPCODE(ip) @ ...trim down to just the opcode
+ GOTO_OPCODE(ip) @ ...and jump to the handler
diff --git a/vm/mterp/armv5te/unop.S b/vm/mterp/armv5te/unop.S
new file mode 100644
index 0000000..12d8206
--- /dev/null
+++ b/vm/mterp/armv5te/unop.S
@@ -0,0 +1,21 @@
+%default {"preinstr":""}
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0".
+ * This could be an ARM instruction or a function call.
+ *
+ * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+ * int-to-byte, int-to-char, int-to-short
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r3) @ r0<- vB
+ and r9, r9, #15
+ $preinstr @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ $instr @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 9-10 instructions */
diff --git a/vm/mterp/armv5te/unopNarrower.S b/vm/mterp/armv5te/unopNarrower.S
new file mode 100644
index 0000000..f1ad902
--- /dev/null
+++ b/vm/mterp/armv5te/unopNarrower.S
@@ -0,0 +1,24 @@
+%default {"preinstr":""}
+ /*
+ * Generic 64bit-to-32bit unary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = op r0/r1", where
+ * "result" is a 32-bit quantity in r0.
+ *
+ * For: long-to-float, double-to-int, double-to-float
+ *
+ * (This would work for long-to-int, but that instruction is actually
+ * an exact match for OP_MOVE.)
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ and r9, r9, #15
+ ldmia r3, {r0-r1} @ r0/r1<- vB/vB+1
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ $preinstr @ optional op; may set condition codes
+ $instr @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-11 instructions */
diff --git a/vm/mterp/armv5te/unopWide.S b/vm/mterp/armv5te/unopWide.S
new file mode 100644
index 0000000..0805fdf
--- /dev/null
+++ b/vm/mterp/armv5te/unopWide.S
@@ -0,0 +1,22 @@
+%default {"preinstr":""}
+ /*
+ * Generic 64-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0/r1".
+ * This could be an ARM instruction or a function call.
+ *
+ * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+ */
+ /* unop vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r3, {r0-r1} @ r0/r1<- vAA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ $preinstr @ optional op; may set condition codes
+ $instr @ r0/r1<- op, r2-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-13 instructions */
diff --git a/vm/mterp/armv5te/unopWider.S b/vm/mterp/armv5te/unopWider.S
new file mode 100644
index 0000000..df1baea
--- /dev/null
+++ b/vm/mterp/armv5te/unopWider.S
@@ -0,0 +1,21 @@
+%default {"preinstr":""}
+ /*
+ * Generic 32bit-to-64bit unary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = op r0", where
+ * "result" is a 64-bit quantity in r0/r1.
+ *
+ * For: int-to-long, int-to-double, float-to-long, float-to-double
+ */
+ /* unop vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r0, r3) @ r0<- vB
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ $preinstr @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ $instr @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vA/vA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-11 instructions */
diff --git a/vm/mterp/armv5te/unused.S b/vm/mterp/armv5te/unused.S
new file mode 100644
index 0000000..0194f58
--- /dev/null
+++ b/vm/mterp/armv5te/unused.S
@@ -0,0 +1 @@
+ bl common_abort
diff --git a/vm/mterp/armv5te/zcmp.S b/vm/mterp/armv5te/zcmp.S
new file mode 100644
index 0000000..d79e7c4
--- /dev/null
+++ b/vm/mterp/armv5te/zcmp.S
@@ -0,0 +1,31 @@
+%verify "branch taken"
+%verify "branch not taken"
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ mov r0, rINST, lsr #8 @ r0<- AA
+ GET_VREG(r2, r0) @ r2<- vAA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, #0 @ compare (vA, 0)
+ b${revcmp} 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
diff --git a/vm/mterp/armv6/OP_INT_TO_BYTE.S b/vm/mterp/armv6/OP_INT_TO_BYTE.S
new file mode 100644
index 0000000..40d8a5c
--- /dev/null
+++ b/vm/mterp/armv6/OP_INT_TO_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unop.S" {"instr":"sxtb r0, r0"}
diff --git a/vm/mterp/armv6/OP_INT_TO_CHAR.S b/vm/mterp/armv6/OP_INT_TO_CHAR.S
new file mode 100644
index 0000000..3f0fdad
--- /dev/null
+++ b/vm/mterp/armv6/OP_INT_TO_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unop.S" {"instr":"uxth r0, r0"}
diff --git a/vm/mterp/armv6/OP_INT_TO_SHORT.S b/vm/mterp/armv6/OP_INT_TO_SHORT.S
new file mode 100644
index 0000000..82274c4
--- /dev/null
+++ b/vm/mterp/armv6/OP_INT_TO_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv5te/unop.S" {"instr":"sxth r0, r0"}
diff --git a/vm/mterp/armv6t2/OP_ADD_DOUBLE_2ADDR.S b/vm/mterp/armv6t2/OP_ADD_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..d81ece9
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_ADD_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopWide2addr.S" {"instr":"bl __aeabi_dadd"}
diff --git a/vm/mterp/armv6t2/OP_ADD_FLOAT_2ADDR.S b/vm/mterp/armv6t2/OP_ADD_FLOAT_2ADDR.S
new file mode 100644
index 0000000..ec6cdf1
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_ADD_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"instr":"bl __aeabi_fadd"}
diff --git a/vm/mterp/armv6t2/OP_ADD_INT_2ADDR.S b/vm/mterp/armv6t2/OP_ADD_INT_2ADDR.S
new file mode 100644
index 0000000..af271cb
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_ADD_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"instr":"add r0, r0, r1"}
diff --git a/vm/mterp/armv6t2/OP_ADD_INT_LIT16.S b/vm/mterp/armv6t2/OP_ADD_INT_LIT16.S
new file mode 100644
index 0000000..f66b1d4
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_ADD_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopLit16.S" {"instr":"add r0, r0, r1"}
diff --git a/vm/mterp/armv6t2/OP_ADD_LONG_2ADDR.S b/vm/mterp/armv6t2/OP_ADD_LONG_2ADDR.S
new file mode 100644
index 0000000..0e3a901
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_ADD_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopWide2addr.S" {"preinstr":"adds r0, r0, r2", "instr":"adc r1, r1, r3"}
diff --git a/vm/mterp/armv6t2/OP_AND_INT_2ADDR.S b/vm/mterp/armv6t2/OP_AND_INT_2ADDR.S
new file mode 100644
index 0000000..e7b716d
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_AND_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"instr":"and r0, r0, r1"}
diff --git a/vm/mterp/armv6t2/OP_AND_INT_LIT16.S b/vm/mterp/armv6t2/OP_AND_INT_LIT16.S
new file mode 100644
index 0000000..77bb06b
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_AND_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopLit16.S" {"instr":"and r0, r0, r1"}
diff --git a/vm/mterp/armv6t2/OP_AND_LONG_2ADDR.S b/vm/mterp/armv6t2/OP_AND_LONG_2ADDR.S
new file mode 100644
index 0000000..b77fbd2
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_AND_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopWide2addr.S" {"preinstr":"and r0, r0, r2", "instr":"and r1, r1, r3"}
diff --git a/vm/mterp/armv6t2/OP_ARRAY_LENGTH.S b/vm/mterp/armv6t2/OP_ARRAY_LENGTH.S
new file mode 100644
index 0000000..05991be
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_ARRAY_LENGTH.S
@@ -0,0 +1,14 @@
+%verify "executed"
+ /*
+ * Return the length of an array.
+ */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r2, rINST, #8, #4 @ r2<- A
+ GET_VREG(r0, r1) @ r0<- vB (object ref)
+ cmp r0, #0 @ is object null?
+ beq common_errNullObject @ yup, fail
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ ldr r3, [r0, #offArrayObject_length] @ r3<- array length
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r3, r2) @ vB<- length
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_CONST_4.S b/vm/mterp/armv6t2/OP_CONST_4.S
new file mode 100644
index 0000000..8ec67d7
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_CONST_4.S
@@ -0,0 +1,9 @@
+%verify "executed"
+ /* const/4 vA, #+B */
+ mov r1, rINST, lsl #16 @ r1<- Bxxx0000
+ ubfx r0, rINST, #8, #4 @ r0<- A
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ mov r1, r1, asr #28 @ r1<- sssssssB (sign-extended)
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ SET_VREG(r1, r0) @ fp[A]<- r1
+ GOTO_OPCODE(ip) @ execute next instruction
diff --git a/vm/mterp/armv6t2/OP_DIV_DOUBLE_2ADDR.S b/vm/mterp/armv6t2/OP_DIV_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..a3b7ffb
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_DIV_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopWide2addr.S" {"instr":"bl __aeabi_ddiv"}
diff --git a/vm/mterp/armv6t2/OP_DIV_FLOAT_2ADDR.S b/vm/mterp/armv6t2/OP_DIV_FLOAT_2ADDR.S
new file mode 100644
index 0000000..125230c
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_DIV_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"instr":"bl __aeabi_fdiv"}
diff --git a/vm/mterp/armv6t2/OP_DIV_INT_2ADDR.S b/vm/mterp/armv6t2/OP_DIV_INT_2ADDR.S
new file mode 100644
index 0000000..8e58043
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_DIV_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"instr":"bl __aeabi_idiv","chkzero":"1"}
diff --git a/vm/mterp/armv6t2/OP_DIV_INT_LIT16.S b/vm/mterp/armv6t2/OP_DIV_INT_LIT16.S
new file mode 100644
index 0000000..b4df053
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_DIV_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopLit16.S" {"instr":"bl __aeabi_idiv","chkzero":"1"}
diff --git a/vm/mterp/armv6t2/OP_DIV_LONG_2ADDR.S b/vm/mterp/armv6t2/OP_DIV_LONG_2ADDR.S
new file mode 100644
index 0000000..cbd9c2d
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_DIV_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopWide2addr.S" {"instr":"bl __aeabi_ldivmod", "chkzero":"1"}
diff --git a/vm/mterp/armv6t2/OP_DOUBLE_TO_FLOAT.S b/vm/mterp/armv6t2/OP_DOUBLE_TO_FLOAT.S
new file mode 100644
index 0000000..bdbb2fb
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_DOUBLE_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unopNarrower.S" {"instr":"bl __aeabi_d2f"}
diff --git a/vm/mterp/armv6t2/OP_DOUBLE_TO_INT.S b/vm/mterp/armv6t2/OP_DOUBLE_TO_INT.S
new file mode 100644
index 0000000..f66dc5f
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_DOUBLE_TO_INT.S
@@ -0,0 +1,54 @@
+%verify "executed"
+/* EABI appears to have Java-style conversions of +inf/-inf/NaN */
+%include "armv6t2/unopNarrower.S" {"instr":"bl __aeabi_d2iz"}
+
+#if 0
+@include "armv5te/unopNarrower.S" {"instr":"bl d2i_doconv"}
+@break
+/*
+ * Convert the double in r0/r1 to an int in r0.
+ *
+ * We have to clip values to int min/max per the specification. The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer. The EABI convert function isn't doing this for us.
+ */
+d2i_doconv:
+ stmfd sp!, {r4, r5, lr} @ save regs
+ mov r2, #0x80000000 @ maxint, as a double (low word)
+ mov r2, r2, asr #9 @ 0xffc00000
+ sub sp, sp, #4 @ align for EABI
+ mvn r3, #0xbe000000 @ maxint, as a double (high word)
+ sub r3, r3, #0x00200000 @ 0x41dfffff
+ mov r4, r0 @ save a copy of r0
+ mov r5, r1 @ and r1
+ bl __aeabi_dcmpge @ is arg >= maxint?
+ cmp r0, #0 @ nonzero == yes
+ mvnne r0, #0x80000000 @ return maxint (0x7fffffff)
+ bne 1f
+
+ mov r0, r4 @ recover arg
+ mov r1, r5
+ mov r3, #0xc1000000 @ minint, as a double (high word)
+ add r3, r3, #0x00e00000 @ 0xc1e00000
+ mov r2, #0 @ minint, as a double (low word)
+ bl __aeabi_dcmple @ is arg <= minint?
+ cmp r0, #0 @ nonzero == yes
+ movne r0, #0x80000000 @ return minint (80000000)
+ bne 1f
+
+ mov r0, r4 @ recover arg
+ mov r1, r5
+ mov r2, r4 @ compare against self
+ mov r3, r5
+ bl __aeabi_dcmpeq @ is arg == self?
+ cmp r0, #0 @ zero == no
+ beq 1f @ return zero for NaN
+
+ mov r0, r4 @ recover arg
+ mov r1, r5
+ bl __aeabi_d2iz @ convert double to int
+
+1:
+ add sp, sp, #4
+ ldmfd sp!, {r4, r5, pc}
+#endif
diff --git a/vm/mterp/armv6t2/OP_DOUBLE_TO_LONG.S b/vm/mterp/armv6t2/OP_DOUBLE_TO_LONG.S
new file mode 100644
index 0000000..ac751de
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_DOUBLE_TO_LONG.S
@@ -0,0 +1,53 @@
+%verify "executed"
+@include "armv6t2/unopWide.S" {"instr":"bl __aeabi_d2lz"}
+%include "armv6t2/unopWide.S" {"instr":"bl d2l_doconv"}
+
+%break
+/*
+ * Convert the double in r0/r1 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification. The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer. The EABI convert function isn't doing this for us.
+ */
+d2l_doconv:
+ stmfd sp!, {r4, r5, lr} @ save regs
+ mov r3, #0x43000000 @ maxlong, as a double (high word)
+ add r3, #0x00e00000 @ 0x43e00000
+ mov r2, #0 @ maxlong, as a double (low word)
+ sub sp, sp, #4 @ align for EABI
+ mov r4, r0 @ save a copy of r0
+ mov r5, r1 @ and r1
+ bl __aeabi_dcmpge @ is arg >= maxlong?
+ cmp r0, #0 @ nonzero == yes
+ mvnne r0, #0 @ return maxlong (7fffffffffffffff)
+ mvnne r1, #0x80000000
+ bne 1f
+
+ mov r0, r4 @ recover arg
+ mov r1, r5
+ mov r3, #0xc3000000 @ minlong, as a double (high word)
+ add r3, #0x00e00000 @ 0xc3e00000
+ mov r2, #0 @ minlong, as a double (low word)
+ bl __aeabi_dcmple @ is arg <= minlong?
+ cmp r0, #0 @ nonzero == yes
+ movne r0, #0 @ return minlong (8000000000000000)
+ movne r1, #0x80000000
+ bne 1f
+
+ mov r0, r4 @ recover arg
+ mov r1, r5
+ mov r2, r4 @ compare against self
+ mov r3, r5
+ bl __aeabi_dcmpeq @ is arg == self?
+ cmp r0, #0 @ zero == no
+ moveq r1, #0 @ return zero for NaN
+ beq 1f
+
+ mov r0, r4 @ recover arg
+ mov r1, r5
+ bl __aeabi_d2lz @ convert double to long
+
+1:
+ add sp, sp, #4
+ ldmfd sp!, {r4, r5, pc}
diff --git a/vm/mterp/armv6t2/OP_FLOAT_TO_DOUBLE.S b/vm/mterp/armv6t2/OP_FLOAT_TO_DOUBLE.S
new file mode 100644
index 0000000..64ca64c
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_FLOAT_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unopWider.S" {"instr":"bl __aeabi_f2d"}
diff --git a/vm/mterp/armv6t2/OP_FLOAT_TO_INT.S b/vm/mterp/armv6t2/OP_FLOAT_TO_INT.S
new file mode 100644
index 0000000..4ba28e7
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_FLOAT_TO_INT.S
@@ -0,0 +1,40 @@
+%verify "executed"
+/* EABI appears to have Java-style conversions of +inf/-inf/NaN */
+%include "armv6t2/unop.S" {"instr":"bl __aeabi_f2iz"}
+
+#if 0
+@include "armv6t2/unop.S" {"instr":"bl f2i_doconv"}
+@break
+/*
+ * Convert the float in r0 to an int in r0.
+ *
+ * We have to clip values to int min/max per the specification. The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer. The EABI convert function isn't doing this for us.
+ */
+f2i_doconv:
+ stmfd sp!, {r4, lr}
+ mov r1, #0x4f000000 @ (float)maxint
+ mov r4, r0
+ bl __aeabi_fcmpge @ is arg >= maxint?
+ cmp r0, #0 @ nonzero == yes
+ mvnne r0, #0x80000000 @ return maxint (7fffffff)
+ ldmnefd sp!, {r4, pc}
+
+ mov r0, r4 @ recover arg
+ mov r1, #0xcf000000 @ (float)minint
+ bl __aeabi_fcmple @ is arg <= minint?
+ cmp r0, #0 @ nonzero == yes
+ movne r0, #0x80000000 @ return minint (80000000)
+ ldmnefd sp!, {r4, pc}
+
+ mov r0, r4 @ recover arg
+ mov r1, r4
+ bl __aeabi_fcmpeq @ is arg == self?
+ cmp r0, #0 @ zero == no
+ ldmeqfd sp!, {r4, pc} @ return zero for NaN
+
+ mov r0, r4 @ recover arg
+ bl __aeabi_f2iz @ convert float to int
+ ldmfd sp!, {r4, pc}
+#endif
diff --git a/vm/mterp/armv6t2/OP_FLOAT_TO_LONG.S b/vm/mterp/armv6t2/OP_FLOAT_TO_LONG.S
new file mode 100644
index 0000000..168e338
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_FLOAT_TO_LONG.S
@@ -0,0 +1,40 @@
+%verify "executed"
+@include "armv6t2/unopWider.S" {"instr":"bl __aeabi_f2lz"}
+%include "armv6t2/unopWider.S" {"instr":"bl f2l_doconv"}
+
+%break
+/*
+ * Convert the float in r0 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification. The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer. The EABI convert function isn't doing this for us.
+ */
+f2l_doconv:
+ stmfd sp!, {r4, lr}
+ mov r1, #0x5f000000 @ (float)maxlong
+ mov r4, r0
+ bl __aeabi_fcmpge @ is arg >= maxlong?
+ cmp r0, #0 @ nonzero == yes
+ mvnne r0, #0 @ return maxlong (7fffffff)
+ mvnne r1, #0x80000000
+ ldmnefd sp!, {r4, pc}
+
+ mov r0, r4 @ recover arg
+ mov r1, #0xdf000000 @ (float)minlong
+ bl __aeabi_fcmple @ is arg <= minlong?
+ cmp r0, #0 @ nonzero == yes
+ movne r0, #0 @ return minlong (80000000)
+ movne r1, #0x80000000
+ ldmnefd sp!, {r4, pc}
+
+ mov r0, r4 @ recover arg
+ mov r1, r4
+ bl __aeabi_fcmpeq @ is arg == self?
+ cmp r0, #0 @ zero == no
+ moveq r1, #0 @ return zero for NaN
+ ldmeqfd sp!, {r4, pc}
+
+ mov r0, r4 @ recover arg
+ bl __aeabi_f2lz @ convert float to long
+ ldmfd sp!, {r4, pc}
diff --git a/vm/mterp/armv6t2/OP_IF_EQ.S b/vm/mterp/armv6t2/OP_IF_EQ.S
new file mode 100644
index 0000000..d14b10b
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IF_EQ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/bincmp.S" { "revcmp":"ne" }
diff --git a/vm/mterp/armv6t2/OP_IF_GE.S b/vm/mterp/armv6t2/OP_IF_GE.S
new file mode 100644
index 0000000..e6c518d
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IF_GE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/bincmp.S" { "revcmp":"lt" }
diff --git a/vm/mterp/armv6t2/OP_IF_GT.S b/vm/mterp/armv6t2/OP_IF_GT.S
new file mode 100644
index 0000000..6e89b3c
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IF_GT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/bincmp.S" { "revcmp":"le" }
diff --git a/vm/mterp/armv6t2/OP_IF_LE.S b/vm/mterp/armv6t2/OP_IF_LE.S
new file mode 100644
index 0000000..0be9f60
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IF_LE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/bincmp.S" { "revcmp":"gt" }
diff --git a/vm/mterp/armv6t2/OP_IF_LT.S b/vm/mterp/armv6t2/OP_IF_LT.S
new file mode 100644
index 0000000..cea79b1
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IF_LT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/bincmp.S" { "revcmp":"ge" }
diff --git a/vm/mterp/armv6t2/OP_IF_NE.S b/vm/mterp/armv6t2/OP_IF_NE.S
new file mode 100644
index 0000000..ad1f936
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IF_NE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/bincmp.S" { "revcmp":"eq" }
diff --git a/vm/mterp/armv6t2/OP_IGET.S b/vm/mterp/armv6t2/OP_IGET.S
new file mode 100644
index 0000000..f5a21eb
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IGET.S
@@ -0,0 +1,45 @@
+%default { "load":"ldr", "sqnum":"0" }
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .L${opcode}_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .L${opcode}_finish
+ b common_exceptionThrown
+%break
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.L${opcode}_finish:
+ @bl common_squeak${sqnum}
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ $load r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ ubfx r2, rINST, #8, #4 @ r2<- A
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_IGET_QUICK.S b/vm/mterp/armv6t2/OP_IGET_QUICK.S
new file mode 100644
index 0000000..0ce2ebc
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IGET_QUICK.S
@@ -0,0 +1,15 @@
+%verify "executed"
+%verify "null object"
+ /* For: iget-quick, iget-object-quick */
+ /* op vA, vB, offset@CCCC */
+ mov r2, rINST, lsr #12 @ r2<- B
+ FETCH(r1, 1) @ r1<- field byte offset
+ GET_VREG(r3, r2) @ r3<- object we're operating on
+ ubfx r2, rINST, #8, #4 @ r2<- A
+ cmp r3, #0 @ check object for null
+ beq common_errNullObject @ object was null
+ ldr r0, [r3, r1] @ r0<- obj.field (always 32 bits)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_IGET_WIDE.S b/vm/mterp/armv6t2/OP_IGET_WIDE.S
new file mode 100644
index 0000000..92cd1a6
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IGET_WIDE.S
@@ -0,0 +1,42 @@
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+ /*
+ * Wide 32-bit instance field get.
+ */
+ /* iget-wide vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .L${opcode}_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .L${opcode}_finish
+ b common_exceptionThrown
+%break
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.L${opcode}_finish:
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldrd r0, [r9, r3] @ r0/r1<- obj.field (64-bit align ok)
+ ubfx r2, rINST, #8, #4 @ r2<- A
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ add r3, rFP, r2, lsl #2 @ r3<- &fp[A]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ fp[A]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_IGET_WIDE_QUICK.S b/vm/mterp/armv6t2/OP_IGET_WIDE_QUICK.S
new file mode 100644
index 0000000..067d40d
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IGET_WIDE_QUICK.S
@@ -0,0 +1,15 @@
+%verify "executed"
+%verify "null object"
+ /* iget-wide-quick vA, vB, offset@CCCC */
+ mov r2, rINST, lsr #12 @ r2<- B
+ FETCH(ip, 1) @ ip<- field byte offset
+ GET_VREG(r3, r2) @ r3<- object we're operating on
+ ubfx r2, rINST, #8, #4 @ r2<- A
+ cmp r3, #0 @ check object for null
+ beq common_errNullObject @ object was null
+ ldrd r0, [r3, ip] @ r0<- obj.field (64 bits, aligned)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ add r3, rFP, r2, lsl #2 @ r3<- &fp[A]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ fp[A]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_INT_TO_BYTE.S b/vm/mterp/armv6t2/OP_INT_TO_BYTE.S
new file mode 100644
index 0000000..27f92e6
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_INT_TO_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unop.S" {"instr":"sxtb r0, r0"}
diff --git a/vm/mterp/armv6t2/OP_INT_TO_CHAR.S b/vm/mterp/armv6t2/OP_INT_TO_CHAR.S
new file mode 100644
index 0000000..db1eaa3
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_INT_TO_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unop.S" {"instr":"uxth r0, r0"}
diff --git a/vm/mterp/armv6t2/OP_INT_TO_DOUBLE.S b/vm/mterp/armv6t2/OP_INT_TO_DOUBLE.S
new file mode 100644
index 0000000..38a2ef2
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_INT_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unopWider.S" {"instr":"bl __aeabi_i2d"}
diff --git a/vm/mterp/armv6t2/OP_INT_TO_FLOAT.S b/vm/mterp/armv6t2/OP_INT_TO_FLOAT.S
new file mode 100644
index 0000000..7407a73
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_INT_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unop.S" {"instr":"bl __aeabi_i2f"}
diff --git a/vm/mterp/armv6t2/OP_INT_TO_LONG.S b/vm/mterp/armv6t2/OP_INT_TO_LONG.S
new file mode 100644
index 0000000..e4d4221
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_INT_TO_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unopWider.S" {"instr":"mov r1, r0, asr #31"}
diff --git a/vm/mterp/armv6t2/OP_INT_TO_SHORT.S b/vm/mterp/armv6t2/OP_INT_TO_SHORT.S
new file mode 100644
index 0000000..6426a9f
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_INT_TO_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unop.S" {"instr":"sxth r0, r0"}
diff --git a/vm/mterp/armv6t2/OP_IPUT.S b/vm/mterp/armv6t2/OP_IPUT.S
new file mode 100644
index 0000000..b69443b
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IPUT.S
@@ -0,0 +1,45 @@
+%default { "store":"str", "sqnum":"0" }
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .L${opcode}_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .L${opcode}_finish @ yes, finish up
+ b common_exceptionThrown
+%break
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.L${opcode}_finish:
+ @bl common_squeak${sqnum}
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ ubfx r1, rINST, #8, #4 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ $store r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_IPUT_QUICK.S b/vm/mterp/armv6t2/OP_IPUT_QUICK.S
new file mode 100644
index 0000000..ad87b55
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IPUT_QUICK.S
@@ -0,0 +1,15 @@
+%verify "executed"
+%verify "null object"
+ /* For: iput-quick, iput-object-quick */
+ /* op vA, vB, offset@CCCC */
+ mov r2, rINST, lsr #12 @ r2<- B
+ FETCH(r1, 1) @ r1<- field byte offset
+ GET_VREG(r3, r2) @ r3<- fp[B], the object pointer
+ ubfx r2, rINST, #8, #4 @ r2<- A
+ cmp r3, #0 @ check object for null
+ beq common_errNullObject @ object was null
+ GET_VREG(r0, r2) @ r0<- fp[A]
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ str r0, [r3, r1] @ obj.field (always 32 bits)<- r0
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_IPUT_WIDE.S b/vm/mterp/armv6t2/OP_IPUT_WIDE.S
new file mode 100644
index 0000000..334e352
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IPUT_WIDE.S
@@ -0,0 +1,39 @@
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+ /* iput-wide vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .L${opcode}_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .L${opcode}_finish @ yes, finish up
+ b common_exceptionThrown
+%break
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.L${opcode}_finish:
+ ubfx r2, rINST, #8, #4 @ r2<- A
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ add r2, rFP, r2, lsl #2 @ r3<- &fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldmia r2, {r0-r1} @ r0/r1<- fp[A]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ strd r0, [r9, r3] @ obj.field (64 bits, aligned)<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_IPUT_WIDE_QUICK.S b/vm/mterp/armv6t2/OP_IPUT_WIDE_QUICK.S
new file mode 100644
index 0000000..09f7a8e
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_IPUT_WIDE_QUICK.S
@@ -0,0 +1,15 @@
+%verify "executed"
+%verify "null object"
+ /* iput-wide-quick vA, vB, offset@CCCC */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r0, rINST, #8, #4 @ r0<- A
+ GET_VREG(r2, r1) @ r2<- fp[B], the object pointer
+ add r3, rFP, r0, lsl #2 @ r3<- &fp[A]
+ cmp r2, #0 @ check object for null
+ ldmia r3, {r0-r1} @ r0/r1<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH(r3, 1) @ r3<- field byte offset
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ strd r0, [r2, r3] @ obj.field (64 bits, aligned)<- r0/r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_LONG_TO_DOUBLE.S b/vm/mterp/armv6t2/OP_LONG_TO_DOUBLE.S
new file mode 100644
index 0000000..f04f917
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_LONG_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unopWide.S" {"instr":"bl __aeabi_l2d"}
diff --git a/vm/mterp/armv6t2/OP_LONG_TO_FLOAT.S b/vm/mterp/armv6t2/OP_LONG_TO_FLOAT.S
new file mode 100644
index 0000000..eaf983b
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_LONG_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unopNarrower.S" {"instr":"bl __aeabi_l2f"}
diff --git a/vm/mterp/armv6t2/OP_MOVE.S b/vm/mterp/armv6t2/OP_MOVE.S
new file mode 100644
index 0000000..3047158
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_MOVE.S
@@ -0,0 +1,10 @@
+%verify "executed"
+ /* for move, move-object, long-to-int */
+ /* op vA, vB */
+ mov r1, rINST, lsr #12 @ r1<- B from 15:12
+ ubfx r0, rINST, #8, #4 @ r0<- A from 11:8
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[B]
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ SET_VREG(r2, r0) @ fp[A]<- r2
+ GOTO_OPCODE(ip) @ execute next instruction
diff --git a/vm/mterp/armv6t2/OP_MOVE_WIDE.S b/vm/mterp/armv6t2/OP_MOVE_WIDE.S
new file mode 100644
index 0000000..adc2c95
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_MOVE_WIDE.S
@@ -0,0 +1,12 @@
+%verify "executed"
+ /* move-wide vA, vB */
+ /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r2, rINST, #8, #4 @ r2<- A
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[A]
+ ldmia r3, {r0-r1} @ r0/r1<- fp[B]
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r2, {r0-r1} @ fp[A]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_MUL_DOUBLE_2ADDR.S b/vm/mterp/armv6t2/OP_MUL_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..b2b1297
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_MUL_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopWide2addr.S" {"instr":"bl __aeabi_dmul"}
diff --git a/vm/mterp/armv6t2/OP_MUL_FLOAT_2ADDR.S b/vm/mterp/armv6t2/OP_MUL_FLOAT_2ADDR.S
new file mode 100644
index 0000000..a48a3a0
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_MUL_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"instr":"bl __aeabi_fmul"}
diff --git a/vm/mterp/armv6t2/OP_MUL_INT_2ADDR.S b/vm/mterp/armv6t2/OP_MUL_INT_2ADDR.S
new file mode 100644
index 0000000..e822fce
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_MUL_INT_2ADDR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+%include "armv6t2/binop2addr.S" {"instr":"mul r0, r1, r0"}
diff --git a/vm/mterp/armv6t2/OP_MUL_INT_LIT16.S b/vm/mterp/armv6t2/OP_MUL_INT_LIT16.S
new file mode 100644
index 0000000..a07e540
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_MUL_INT_LIT16.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+%include "armv6t2/binopLit16.S" {"instr":"mul r0, r1, r0"}
diff --git a/vm/mterp/armv6t2/OP_MUL_LONG_2ADDR.S b/vm/mterp/armv6t2/OP_MUL_LONG_2ADDR.S
new file mode 100644
index 0000000..1526a1e
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_MUL_LONG_2ADDR.S
@@ -0,0 +1,25 @@
+%verify "executed"
+ /*
+ * Signed 64-bit integer multiply, "/2addr" version.
+ *
+ * See OP_MUL_LONG for an explanation.
+ *
+ * We get a little tight on registers, so to avoid looking up &fp[A]
+ * again we stuff it into rINST.
+ */
+ /* mul-long/2addr vA, vB */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add rINST, rFP, r9, lsl #2 @ rINST<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia rINST, {r0-r1} @ r0/r1<- vAA/vAA+1
+ mul ip, r2, r1 @ ip<- ZxW
+ umull r9, r10, r2, r0 @ r9/r10 <- ZxX
+ mla r2, r0, r3, ip @ r2<- YxX + (ZxW)
+ mov r0, rINST @ r0<- &fp[A] (free up rINST)
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ add r10, r2, r10 @ r10<- r10 + low(ZxW + (YxX))
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r0, {r9-r10} @ vAA/vAA+1<- r9/r10
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_NEG_DOUBLE.S b/vm/mterp/armv6t2/OP_NEG_DOUBLE.S
new file mode 100644
index 0000000..52ef346
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_NEG_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unopWide.S" {"instr":"add r1, r1, #0x80000000"}
diff --git a/vm/mterp/armv6t2/OP_NEG_FLOAT.S b/vm/mterp/armv6t2/OP_NEG_FLOAT.S
new file mode 100644
index 0000000..34672d3
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_NEG_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unop.S" {"instr":"add r0, r0, #0x80000000"}
diff --git a/vm/mterp/armv6t2/OP_NEG_INT.S b/vm/mterp/armv6t2/OP_NEG_INT.S
new file mode 100644
index 0000000..98fb1b3
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_NEG_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unop.S" {"instr":"rsb r0, r0, #0"}
diff --git a/vm/mterp/armv6t2/OP_NEG_LONG.S b/vm/mterp/armv6t2/OP_NEG_LONG.S
new file mode 100644
index 0000000..22f45fd
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_NEG_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unopWide.S" {"preinstr":"rsbs r0, r0, #0", "instr":"rsc r1, r1, #0"}
diff --git a/vm/mterp/armv6t2/OP_NOT_INT.S b/vm/mterp/armv6t2/OP_NOT_INT.S
new file mode 100644
index 0000000..5ce758e
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_NOT_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unop.S" {"instr":"mvn r0, r0"}
diff --git a/vm/mterp/armv6t2/OP_NOT_LONG.S b/vm/mterp/armv6t2/OP_NOT_LONG.S
new file mode 100644
index 0000000..ac7e875
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_NOT_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/unopWide.S" {"preinstr":"mvn r0, r0", "instr":"mvn r1, r1"}
diff --git a/vm/mterp/armv6t2/OP_OR_INT_2ADDR.S b/vm/mterp/armv6t2/OP_OR_INT_2ADDR.S
new file mode 100644
index 0000000..b13b90c
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_OR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"instr":"orr r0, r0, r1"}
diff --git a/vm/mterp/armv6t2/OP_OR_INT_LIT16.S b/vm/mterp/armv6t2/OP_OR_INT_LIT16.S
new file mode 100644
index 0000000..87db288
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_OR_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopLit16.S" {"instr":"orr r0, r0, r1"}
diff --git a/vm/mterp/armv6t2/OP_OR_LONG_2ADDR.S b/vm/mterp/armv6t2/OP_OR_LONG_2ADDR.S
new file mode 100644
index 0000000..a1891e5
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_OR_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopWide2addr.S" {"preinstr":"orr r0, r0, r2", "instr":"orr r1, r1, r3"}
diff --git a/vm/mterp/armv6t2/OP_REM_DOUBLE_2ADDR.S b/vm/mterp/armv6t2/OP_REM_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..48e4cc3
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_REM_DOUBLE_2ADDR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* EABI doesn't define a double remainder function, but libm does */
+%include "armv6t2/binopWide2addr.S" {"instr":"bl fmod"}
diff --git a/vm/mterp/armv6t2/OP_REM_FLOAT_2ADDR.S b/vm/mterp/armv6t2/OP_REM_FLOAT_2ADDR.S
new file mode 100644
index 0000000..8273df1
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_REM_FLOAT_2ADDR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* EABI doesn't define a float remainder function, but libm does */
+%include "armv6t2/binop2addr.S" {"instr":"bl fmodf"}
diff --git a/vm/mterp/armv6t2/OP_REM_INT_2ADDR.S b/vm/mterp/armv6t2/OP_REM_INT_2ADDR.S
new file mode 100644
index 0000000..be4951d
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_REM_INT_2ADDR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* idivmod returns quotient in r0 and remainder in r1 */
+%include "armv6t2/binop2addr.S" {"instr":"bl __aeabi_idivmod", "result":"r1", "chkzero":"1"}
diff --git a/vm/mterp/armv6t2/OP_REM_INT_LIT16.S b/vm/mterp/armv6t2/OP_REM_INT_LIT16.S
new file mode 100644
index 0000000..ba66b48
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_REM_INT_LIT16.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* idivmod returns quotient in r0 and remainder in r1 */
+%include "armv6t2/binopLit16.S" {"instr":"bl __aeabi_idivmod", "result":"r1", "chkzero":"1"}
diff --git a/vm/mterp/armv6t2/OP_REM_LONG_2ADDR.S b/vm/mterp/armv6t2/OP_REM_LONG_2ADDR.S
new file mode 100644
index 0000000..c663f78
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_REM_LONG_2ADDR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* ldivmod returns quotient in r0/r1 and remainder in r2/r3 */
+%include "armv6t2/binopWide2addr.S" {"instr":"bl __aeabi_ldivmod", "result0":"r2", "result1":"r3", "chkzero":"1"}
diff --git a/vm/mterp/armv6t2/OP_RSUB_INT.S b/vm/mterp/armv6t2/OP_RSUB_INT.S
new file mode 100644
index 0000000..761259c
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_RSUB_INT.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* this op is "rsub-int", but can be thought of as "rsub-int/lit16" */
+%include "armv6t2/binopLit16.S" {"instr":"rsb r0, r0, r1"}
diff --git a/vm/mterp/armv6t2/OP_SHL_INT_2ADDR.S b/vm/mterp/armv6t2/OP_SHL_INT_2ADDR.S
new file mode 100644
index 0000000..c6959b2
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_SHL_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"preinstr":"and r1, r1, #31", "instr":"mov r0, r0, asl r1"}
diff --git a/vm/mterp/armv6t2/OP_SHL_LONG_2ADDR.S b/vm/mterp/armv6t2/OP_SHL_LONG_2ADDR.S
new file mode 100644
index 0000000..502481e
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_SHL_LONG_2ADDR.S
@@ -0,0 +1,27 @@
+%verify "executed"
+ /*
+ * Long integer shift, 2addr version. vA is 64-bit value/result, vB is
+ * 32-bit shift distance.
+ */
+ /* shl-long/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r2, r3) @ r2<- vB
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ and r2, r2, #63 @ r2<- r2 & 0x3f
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+
+ mov r1, r1, asl r2 @ r1<- r1 << r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32)
+ mov r0, r0, asl r2 @ r0<- r0 << r2
+ b .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_SHR_INT_2ADDR.S b/vm/mterp/armv6t2/OP_SHR_INT_2ADDR.S
new file mode 100644
index 0000000..ce0a2ce
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_SHR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"preinstr":"and r1, r1, #31", "instr":"mov r0, r0, asr r1"}
diff --git a/vm/mterp/armv6t2/OP_SHR_LONG_2ADDR.S b/vm/mterp/armv6t2/OP_SHR_LONG_2ADDR.S
new file mode 100644
index 0000000..501b3f2
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_SHR_LONG_2ADDR.S
@@ -0,0 +1,27 @@
+%verify "executed"
+ /*
+ * Long integer shift, 2addr version. vA is 64-bit value/result, vB is
+ * 32-bit shift distance.
+ */
+ /* shr-long/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r2, r3) @ r2<- vB
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ and r2, r2, #63 @ r2<- r2 & 0x3f
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32)
+ mov r1, r1, asr r2 @ r1<- r1 >> r2
+ b .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_SUB_DOUBLE_2ADDR.S b/vm/mterp/armv6t2/OP_SUB_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..631187b
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_SUB_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopWide2addr.S" {"instr":"bl __aeabi_dsub"}
diff --git a/vm/mterp/armv6t2/OP_SUB_FLOAT_2ADDR.S b/vm/mterp/armv6t2/OP_SUB_FLOAT_2ADDR.S
new file mode 100644
index 0000000..13ee1b4
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_SUB_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"instr":"bl __aeabi_fsub"}
diff --git a/vm/mterp/armv6t2/OP_SUB_INT_2ADDR.S b/vm/mterp/armv6t2/OP_SUB_INT_2ADDR.S
new file mode 100644
index 0000000..a3bd5e5
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_SUB_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"instr":"sub r0, r0, r1"}
diff --git a/vm/mterp/armv6t2/OP_SUB_LONG_2ADDR.S b/vm/mterp/armv6t2/OP_SUB_LONG_2ADDR.S
new file mode 100644
index 0000000..46dda45
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_SUB_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopWide2addr.S" {"preinstr":"subs r0, r0, r2", "instr":"sbc r1, r1, r3"}
diff --git a/vm/mterp/armv6t2/OP_USHR_INT_2ADDR.S b/vm/mterp/armv6t2/OP_USHR_INT_2ADDR.S
new file mode 100644
index 0000000..5d5808e
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_USHR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"preinstr":"and r1, r1, #31", "instr":"mov r0, r0, lsr r1"}
diff --git a/vm/mterp/armv6t2/OP_USHR_LONG_2ADDR.S b/vm/mterp/armv6t2/OP_USHR_LONG_2ADDR.S
new file mode 100644
index 0000000..a1fcef4
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_USHR_LONG_2ADDR.S
@@ -0,0 +1,27 @@
+%verify "executed"
+ /*
+ * Long integer shift, 2addr version. vA is 64-bit value/result, vB is
+ * 32-bit shift distance.
+ */
+ /* ushr-long/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r2, r3) @ r2<- vB
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ and r2, r2, #63 @ r2<- r2 & 0x3f
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32)
+ mov r1, r1, lsr r2 @ r1<- r1 >>> r2
+ b .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
diff --git a/vm/mterp/armv6t2/OP_XOR_INT_2ADDR.S b/vm/mterp/armv6t2/OP_XOR_INT_2ADDR.S
new file mode 100644
index 0000000..49c82d6
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_XOR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binop2addr.S" {"instr":"eor r0, r0, r1"}
diff --git a/vm/mterp/armv6t2/OP_XOR_INT_LIT16.S b/vm/mterp/armv6t2/OP_XOR_INT_LIT16.S
new file mode 100644
index 0000000..6fe178d
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_XOR_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopLit16.S" {"instr":"eor r0, r0, r1"}
diff --git a/vm/mterp/armv6t2/OP_XOR_LONG_2ADDR.S b/vm/mterp/armv6t2/OP_XOR_LONG_2ADDR.S
new file mode 100644
index 0000000..8d5ba2b
--- /dev/null
+++ b/vm/mterp/armv6t2/OP_XOR_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "armv6t2/binopWide2addr.S" {"preinstr":"eor r0, r0, r2", "instr":"eor r1, r1, r3"}
diff --git a/vm/mterp/armv6t2/bincmp.S b/vm/mterp/armv6t2/bincmp.S
new file mode 100644
index 0000000..002eeed
--- /dev/null
+++ b/vm/mterp/armv6t2/bincmp.S
@@ -0,0 +1,30 @@
+%verify "branch taken"
+%verify "branch not taken"
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r0, rINST, #8, #4 @ r0<- A
+ GET_VREG(r3, r1) @ r3<- vB
+ GET_VREG(r2, r0) @ r2<- vA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, r3 @ compare (vA, vB)
+ b${revcmp} 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ b common_testUpdateProfile
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
diff --git a/vm/mterp/armv6t2/binop2addr.S b/vm/mterp/armv6t2/binop2addr.S
new file mode 100644
index 0000000..b402156
--- /dev/null
+++ b/vm/mterp/armv6t2/binop2addr.S
@@ -0,0 +1,32 @@
+%default {"preinstr":"", "result":"r0", "chkzero":"0"}
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if $chkzero
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ $preinstr @ optional op; may set condition codes
+ $instr @ $result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG($result, r9) @ vAA<- $result
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
diff --git a/vm/mterp/armv6t2/binopLit16.S b/vm/mterp/armv6t2/binopLit16.S
new file mode 100644
index 0000000..1f67524
--- /dev/null
+++ b/vm/mterp/armv6t2/binopLit16.S
@@ -0,0 +1,29 @@
+%default {"result":"r0", "chkzero":"0"}
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r0, r2) @ r0<- vB
+ .if $chkzero
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ $instr @ $result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG($result, r9) @ vAA<- $result
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
diff --git a/vm/mterp/armv6t2/binopWide2addr.S b/vm/mterp/armv6t2/binopWide2addr.S
new file mode 100644
index 0000000..0b4691f
--- /dev/null
+++ b/vm/mterp/armv6t2/binopWide2addr.S
@@ -0,0 +1,34 @@
+%default {"preinstr":"", "result0":"r0", "result1":"r1", "chkzero":"0"}
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if $chkzero
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ $preinstr @ optional op; may set condition codes
+ $instr @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {$result0,$result1} @ vAA/vAA+1<- $result0/$result1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
diff --git a/vm/mterp/armv6t2/unop.S b/vm/mterp/armv6t2/unop.S
new file mode 100644
index 0000000..58465fe
--- /dev/null
+++ b/vm/mterp/armv6t2/unop.S
@@ -0,0 +1,20 @@
+%default {"preinstr":""}
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0".
+ * This could be an ARM instruction or a function call.
+ *
+ * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+ * int-to-byte, int-to-char, int-to-short
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r0, r3) @ r0<- vB
+ $preinstr @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ $instr @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 8-9 instructions */
diff --git a/vm/mterp/armv6t2/unopNarrower.S b/vm/mterp/armv6t2/unopNarrower.S
new file mode 100644
index 0000000..224e8e7
--- /dev/null
+++ b/vm/mterp/armv6t2/unopNarrower.S
@@ -0,0 +1,23 @@
+%default {"preinstr":""}
+ /*
+ * Generic 64bit-to-32bit unary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = op r0/r1", where
+ * "result" is a 32-bit quantity in r0.
+ *
+ * For: long-to-float, double-to-int, double-to-float
+ *
+ * (This would work for long-to-int, but that instruction is actually
+ * an exact match for OP_MOVE.)
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ ldmia r3, {r0-r1} @ r0/r1<- vB/vB+1
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ $preinstr @ optional op; may set condition codes
+ $instr @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 9-10 instructions */
diff --git a/vm/mterp/armv6t2/unopWide.S b/vm/mterp/armv6t2/unopWide.S
new file mode 100644
index 0000000..e0a303e
--- /dev/null
+++ b/vm/mterp/armv6t2/unopWide.S
@@ -0,0 +1,21 @@
+%default {"preinstr":""}
+ /*
+ * Generic 64-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0/r1".
+ * This could be an ARM instruction or a function call.
+ *
+ * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r3, {r0-r1} @ r0/r1<- vAA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ $preinstr @ optional op; may set condition codes
+ $instr @ r0/r1<- op, r2-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-11 instructions */
diff --git a/vm/mterp/armv6t2/unopWider.S b/vm/mterp/armv6t2/unopWider.S
new file mode 100644
index 0000000..7ec221b
--- /dev/null
+++ b/vm/mterp/armv6t2/unopWider.S
@@ -0,0 +1,20 @@
+%default {"preinstr":""}
+ /*
+ * Generic 32bit-to-64bit unary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = op r0", where
+ * "result" is a 64-bit quantity in r0/r1.
+ *
+ * For: int-to-long, int-to-double, float-to-long, float-to-double
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r0, r3) @ r0<- vB
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ $preinstr @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ $instr @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vA/vA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 9-10 instructions */
diff --git a/vm/mterp/armv7-a/platform.S b/vm/mterp/armv7-a/platform.S
new file mode 100644
index 0000000..6419da1
--- /dev/null
+++ b/vm/mterp/armv7-a/platform.S
@@ -0,0 +1,51 @@
+/*
+ * ===========================================================================
+ * CPU-version-specific defines
+ * ===========================================================================
+ */
+
+/*
+ * Macro for "LDR PC,xxx", which is not allowed pre-ARMv5. Essentially a
+ * one-way branch.
+ *
+ * May modify IP. Does not modify LR.
+ */
+.macro LDR_PC source
+ ldr pc, \source
+.endm
+
+/*
+ * Macro for "MOV LR,PC / LDR PC,xxx", which is not allowed pre-ARMv5.
+ * Jump to subroutine.
+ *
+ * May modify IP and LR.
+ */
+.macro LDR_PC_LR source
+ mov lr, pc
+ ldr pc, \source
+.endm
+
+/*
+ * Macro for "LDMFD SP!, {...regs...,PC}".
+ *
+ * May modify IP and LR.
+ */
+.macro LDMFD_PC regs
+ ldmfd sp!, {\regs,pc}
+.endm
+
+#if !defined(ANDROID_SMP)
+# error "Must define ANDROID_SMP"
+#endif
+
+/*
+ * Macro for data memory barrier; not meaningful pre-ARMv6K.
+ * If the argument is nonzero, emit barrier; otherwise, emit nothing.
+ */
+.macro SMP_DMB
+#if ANDROID_SMP != 0
+ dmb
+#else
+ /* not SMP */
+#endif
+.endm
diff --git a/vm/mterp/c/OP_ADD_DOUBLE.c b/vm/mterp/c/OP_ADD_DOUBLE.c
new file mode 100644
index 0000000..571aeb8
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_DOUBLE.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_DOUBLE(OP_ADD_DOUBLE, "add", +)
+OP_END
diff --git a/vm/mterp/c/OP_ADD_DOUBLE_2ADDR.c b/vm/mterp/c/OP_ADD_DOUBLE_2ADDR.c
new file mode 100644
index 0000000..af952cb
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_DOUBLE_2ADDR.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_DOUBLE_2ADDR(OP_ADD_DOUBLE_2ADDR, "add", +)
+OP_END
diff --git a/vm/mterp/c/OP_ADD_FLOAT.c b/vm/mterp/c/OP_ADD_FLOAT.c
new file mode 100644
index 0000000..dab7d33
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_FLOAT.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_FLOAT(OP_ADD_FLOAT, "add", +)
+OP_END
diff --git a/vm/mterp/c/OP_ADD_FLOAT_2ADDR.c b/vm/mterp/c/OP_ADD_FLOAT_2ADDR.c
new file mode 100644
index 0000000..a068fd0
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_FLOAT_2ADDR.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_FLOAT_2ADDR(OP_ADD_FLOAT_2ADDR, "add", +)
+OP_END
diff --git a/vm/mterp/c/OP_ADD_INT.c b/vm/mterp/c/OP_ADD_INT.c
new file mode 100644
index 0000000..bfaa590
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_INT.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT(OP_ADD_INT, "add", +, 0)
+OP_END
diff --git a/vm/mterp/c/OP_ADD_INT_2ADDR.c b/vm/mterp/c/OP_ADD_INT_2ADDR.c
new file mode 100644
index 0000000..dfd3289
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_INT_2ADDR.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_2ADDR(OP_ADD_INT_2ADDR, "add", +, 0)
+OP_END
diff --git a/vm/mterp/c/OP_ADD_INT_LIT16.c b/vm/mterp/c/OP_ADD_INT_LIT16.c
new file mode 100644
index 0000000..442ab40
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_INT_LIT16.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT16(OP_ADD_INT_LIT16, "add", +, 0)
+OP_END
diff --git a/vm/mterp/c/OP_ADD_INT_LIT8.c b/vm/mterp/c/OP_ADD_INT_LIT8.c
new file mode 100644
index 0000000..1455599
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_INT_LIT8.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT8(OP_ADD_INT_LIT8, "add", +, 0)
+OP_END
diff --git a/vm/mterp/c/OP_ADD_LONG.c b/vm/mterp/c/OP_ADD_LONG.c
new file mode 100644
index 0000000..25d1f47
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_LONG.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG(OP_ADD_LONG, "add", +, 0)
+OP_END
diff --git a/vm/mterp/c/OP_ADD_LONG_2ADDR.c b/vm/mterp/c/OP_ADD_LONG_2ADDR.c
new file mode 100644
index 0000000..4ae71bb
--- /dev/null
+++ b/vm/mterp/c/OP_ADD_LONG_2ADDR.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG_2ADDR(OP_ADD_LONG_2ADDR, "add", +, 0)
+OP_END
diff --git a/vm/mterp/c/OP_AGET.c b/vm/mterp/c/OP_AGET.c
new file mode 100644
index 0000000..766beaf
--- /dev/null
+++ b/vm/mterp/c/OP_AGET.c
@@ -0,0 +1,2 @@
+HANDLE_OP_AGET(OP_AGET, "", u4, )
+OP_END
diff --git a/vm/mterp/c/OP_AGET_BOOLEAN.c b/vm/mterp/c/OP_AGET_BOOLEAN.c
new file mode 100644
index 0000000..d63bc10
--- /dev/null
+++ b/vm/mterp/c/OP_AGET_BOOLEAN.c
@@ -0,0 +1,2 @@
+HANDLE_OP_AGET(OP_AGET_BOOLEAN, "-boolean", u1, )
+OP_END
diff --git a/vm/mterp/c/OP_AGET_BYTE.c b/vm/mterp/c/OP_AGET_BYTE.c
new file mode 100644
index 0000000..61ecc05
--- /dev/null
+++ b/vm/mterp/c/OP_AGET_BYTE.c
@@ -0,0 +1,2 @@
+HANDLE_OP_AGET(OP_AGET_BYTE, "-byte", s1, )
+OP_END
diff --git a/vm/mterp/c/OP_AGET_CHAR.c b/vm/mterp/c/OP_AGET_CHAR.c
new file mode 100644
index 0000000..55e16ef
--- /dev/null
+++ b/vm/mterp/c/OP_AGET_CHAR.c
@@ -0,0 +1,2 @@
+HANDLE_OP_AGET(OP_AGET_CHAR, "-char", u2, )
+OP_END
diff --git a/vm/mterp/c/OP_AGET_OBJECT.c b/vm/mterp/c/OP_AGET_OBJECT.c
new file mode 100644
index 0000000..903637c
--- /dev/null
+++ b/vm/mterp/c/OP_AGET_OBJECT.c
@@ -0,0 +1,2 @@
+HANDLE_OP_AGET(OP_AGET_OBJECT, "-object", u4, )
+OP_END
diff --git a/vm/mterp/c/OP_AGET_SHORT.c b/vm/mterp/c/OP_AGET_SHORT.c
new file mode 100644
index 0000000..176b4a6
--- /dev/null
+++ b/vm/mterp/c/OP_AGET_SHORT.c
@@ -0,0 +1,2 @@
+HANDLE_OP_AGET(OP_AGET_SHORT, "-short", s2, )
+OP_END
diff --git a/vm/mterp/c/OP_AGET_WIDE.c b/vm/mterp/c/OP_AGET_WIDE.c
new file mode 100644
index 0000000..e7974cb
--- /dev/null
+++ b/vm/mterp/c/OP_AGET_WIDE.c
@@ -0,0 +1,2 @@
+HANDLE_OP_AGET(OP_AGET_WIDE, "-wide", s8, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_AND_INT.c b/vm/mterp/c/OP_AND_INT.c
new file mode 100644
index 0000000..3cf31cb
--- /dev/null
+++ b/vm/mterp/c/OP_AND_INT.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT(OP_AND_INT, "and", &, 0)
+OP_END
diff --git a/vm/mterp/c/OP_AND_INT_2ADDR.c b/vm/mterp/c/OP_AND_INT_2ADDR.c
new file mode 100644
index 0000000..9f69292
--- /dev/null
+++ b/vm/mterp/c/OP_AND_INT_2ADDR.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_2ADDR(OP_AND_INT_2ADDR, "and", &, 0)
+OP_END
diff --git a/vm/mterp/c/OP_AND_INT_LIT16.c b/vm/mterp/c/OP_AND_INT_LIT16.c
new file mode 100644
index 0000000..19f825b
--- /dev/null
+++ b/vm/mterp/c/OP_AND_INT_LIT16.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT16(OP_AND_INT_LIT16, "and", &, 0)
+OP_END
diff --git a/vm/mterp/c/OP_AND_INT_LIT8.c b/vm/mterp/c/OP_AND_INT_LIT8.c
new file mode 100644
index 0000000..c0e1315
--- /dev/null
+++ b/vm/mterp/c/OP_AND_INT_LIT8.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT8(OP_AND_INT_LIT8, "and", &, 0)
+OP_END
diff --git a/vm/mterp/c/OP_AND_LONG.c b/vm/mterp/c/OP_AND_LONG.c
new file mode 100644
index 0000000..1c638fb
--- /dev/null
+++ b/vm/mterp/c/OP_AND_LONG.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG(OP_AND_LONG, "and", &, 0)
+OP_END
diff --git a/vm/mterp/c/OP_AND_LONG_2ADDR.c b/vm/mterp/c/OP_AND_LONG_2ADDR.c
new file mode 100644
index 0000000..23c464d
--- /dev/null
+++ b/vm/mterp/c/OP_AND_LONG_2ADDR.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG_2ADDR(OP_AND_LONG_2ADDR, "and", &, 0)
+OP_END
diff --git a/vm/mterp/c/OP_APUT.c b/vm/mterp/c/OP_APUT.c
new file mode 100644
index 0000000..07d3e04
--- /dev/null
+++ b/vm/mterp/c/OP_APUT.c
@@ -0,0 +1,2 @@
+HANDLE_OP_APUT(OP_APUT, "", u4, )
+OP_END
diff --git a/vm/mterp/c/OP_APUT_BOOLEAN.c b/vm/mterp/c/OP_APUT_BOOLEAN.c
new file mode 100644
index 0000000..fc69147
--- /dev/null
+++ b/vm/mterp/c/OP_APUT_BOOLEAN.c
@@ -0,0 +1,2 @@
+HANDLE_OP_APUT(OP_APUT_BOOLEAN, "-boolean", u1, )
+OP_END
diff --git a/vm/mterp/c/OP_APUT_BYTE.c b/vm/mterp/c/OP_APUT_BYTE.c
new file mode 100644
index 0000000..45aeb0b
--- /dev/null
+++ b/vm/mterp/c/OP_APUT_BYTE.c
@@ -0,0 +1,2 @@
+HANDLE_OP_APUT(OP_APUT_BYTE, "-byte", s1, )
+OP_END
diff --git a/vm/mterp/c/OP_APUT_CHAR.c b/vm/mterp/c/OP_APUT_CHAR.c
new file mode 100644
index 0000000..1553c27
--- /dev/null
+++ b/vm/mterp/c/OP_APUT_CHAR.c
@@ -0,0 +1,2 @@
+HANDLE_OP_APUT(OP_APUT_CHAR, "-char", u2, )
+OP_END
diff --git a/vm/mterp/c/OP_APUT_OBJECT.c b/vm/mterp/c/OP_APUT_OBJECT.c
new file mode 100644
index 0000000..07e48c6
--- /dev/null
+++ b/vm/mterp/c/OP_APUT_OBJECT.c
@@ -0,0 +1,40 @@
+HANDLE_OPCODE(OP_APUT_OBJECT /*vAA, vBB, vCC*/)
+ {
+ ArrayObject* arrayObj;
+ Object* obj;
+ u2 arrayInfo;
+ EXPORT_PC();
+ vdst = INST_AA(inst); /* AA: source value */
+ arrayInfo = FETCH(1);
+ vsrc1 = arrayInfo & 0xff; /* BB: array ptr */
+ vsrc2 = arrayInfo >> 8; /* CC: index */
+ ILOGV("|aput%s v%d,v%d,v%d", "-object", vdst, vsrc1, vsrc2);
+ arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);
+ if (!checkForNull((Object*) arrayObj))
+ GOTO_exceptionThrown();
+ if (GET_REGISTER(vsrc2) >= arrayObj->length) {
+ dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;",
+ NULL);
+ GOTO_exceptionThrown();
+ }
+ obj = (Object*) GET_REGISTER(vdst);
+ if (obj != NULL) {
+ if (!checkForNull(obj))
+ GOTO_exceptionThrown();
+ if (!dvmCanPutArrayElement(obj->clazz, arrayObj->obj.clazz)) {
+ LOGV("Can't put a '%s'(%p) into array type='%s'(%p)\n",
+ obj->clazz->descriptor, obj,
+ arrayObj->obj.clazz->descriptor, arrayObj);
+ //dvmDumpClass(obj->clazz);
+ //dvmDumpClass(arrayObj->obj.clazz);
+ dvmThrowException("Ljava/lang/ArrayStoreException;", NULL);
+ GOTO_exceptionThrown();
+ }
+ }
+ ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));
+ dvmSetObjectArrayElement(arrayObj,
+ GET_REGISTER(vsrc2),
+ (Object *)GET_REGISTER(vdst));
+ }
+ FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_APUT_SHORT.c b/vm/mterp/c/OP_APUT_SHORT.c
new file mode 100644
index 0000000..a72b5ea
--- /dev/null
+++ b/vm/mterp/c/OP_APUT_SHORT.c
@@ -0,0 +1,2 @@
+HANDLE_OP_APUT(OP_APUT_SHORT, "-short", s2, )
+OP_END
diff --git a/vm/mterp/c/OP_APUT_WIDE.c b/vm/mterp/c/OP_APUT_WIDE.c
new file mode 100644
index 0000000..39c8cfa
--- /dev/null
+++ b/vm/mterp/c/OP_APUT_WIDE.c
@@ -0,0 +1,2 @@
+HANDLE_OP_APUT(OP_APUT_WIDE, "-wide", s8, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_ARRAY_LENGTH.c b/vm/mterp/c/OP_ARRAY_LENGTH.c
new file mode 100644
index 0000000..0d5a933
--- /dev/null
+++ b/vm/mterp/c/OP_ARRAY_LENGTH.c
@@ -0,0 +1,15 @@
+HANDLE_OPCODE(OP_ARRAY_LENGTH /*vA, vB*/)
+ {
+ ArrayObject* arrayObj;
+
+ vdst = INST_A(inst);
+ vsrc1 = INST_B(inst);
+ arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);
+ ILOGV("|array-length v%d,v%d (%p)", vdst, vsrc1, arrayObj);
+ if (!checkForNullExportPC((Object*) arrayObj, fp, pc))
+ GOTO_exceptionThrown();
+ /* verifier guarantees this is an array reference */
+ SET_REGISTER(vdst, arrayObj->length);
+ }
+ FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_BREAKPOINT.c b/vm/mterp/c/OP_BREAKPOINT.c
new file mode 100644
index 0000000..91cd36c
--- /dev/null
+++ b/vm/mterp/c/OP_BREAKPOINT.c
@@ -0,0 +1,29 @@
+HANDLE_OPCODE(OP_BREAKPOINT)
+#if (INTERP_TYPE == INTERP_DBG)
+ {
+ /*
+ * Restart this instruction with the original opcode. We do
+ * this by simply jumping to the handler.
+ *
+ * It's probably not necessary to update "inst", but we do it
+ * for the sake of anything that needs to do disambiguation in a
+ * common handler with INST_INST.
+ *
+ * The breakpoint itself is handled over in updateDebugger(),
+ * because we need to detect other events (method entry, single
+ * step) and report them in the same event packet, and we're not
+ * yet handling those through breakpoint instructions. By the
+ * time we get here, the breakpoint has already been handled and
+ * the thread resumed.
+ */
+ u1 originalOpCode = dvmGetOriginalOpCode(pc);
+ LOGV("+++ break 0x%02x (0x%04x -> 0x%04x)\n", originalOpCode, inst,
+ INST_REPLACE_OP(inst, originalOpCode));
+ inst = INST_REPLACE_OP(inst, originalOpCode);
+ FINISH_BKPT(originalOpCode);
+ }
+#else
+ LOGE("Breakpoint hit in non-debug interpreter\n");
+ dvmAbort();
+#endif
+OP_END
diff --git a/vm/mterp/c/OP_CHECK_CAST.c b/vm/mterp/c/OP_CHECK_CAST.c
new file mode 100644
index 0000000..9a7ecfb
--- /dev/null
+++ b/vm/mterp/c/OP_CHECK_CAST.c
@@ -0,0 +1,32 @@
+HANDLE_OPCODE(OP_CHECK_CAST /*vAA, class@BBBB*/)
+ {
+ ClassObject* clazz;
+ Object* obj;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst);
+ ref = FETCH(1); /* class to check against */
+ ILOGV("|check-cast v%d,class@0x%04x", vsrc1, ref);
+
+ obj = (Object*)GET_REGISTER(vsrc1);
+ if (obj != NULL) {
+#if defined(WITH_EXTRA_OBJECT_VALIDATION)
+ if (!checkForNull(obj))
+ GOTO_exceptionThrown();
+#endif
+ clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+ if (clazz == NULL) {
+ clazz = dvmResolveClass(curMethod->clazz, ref, false);
+ if (clazz == NULL)
+ GOTO_exceptionThrown();
+ }
+ if (!dvmInstanceof(obj->clazz, clazz)) {
+ dvmThrowExceptionWithClassMessage(
+ "Ljava/lang/ClassCastException;", obj->clazz->descriptor);
+ GOTO_exceptionThrown();
+ }
+ }
+ }
+ FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_CMPG_DOUBLE.c b/vm/mterp/c/OP_CMPG_DOUBLE.c
new file mode 100644
index 0000000..3f4082c
--- /dev/null
+++ b/vm/mterp/c/OP_CMPG_DOUBLE.c
@@ -0,0 +1,2 @@
+HANDLE_OP_CMPX(OP_CMPG_DOUBLE, "g-double", double, _DOUBLE, 1)
+OP_END
diff --git a/vm/mterp/c/OP_CMPG_FLOAT.c b/vm/mterp/c/OP_CMPG_FLOAT.c
new file mode 100644
index 0000000..0bba49e
--- /dev/null
+++ b/vm/mterp/c/OP_CMPG_FLOAT.c
@@ -0,0 +1,2 @@
+HANDLE_OP_CMPX(OP_CMPG_FLOAT, "g-float", float, _FLOAT, 1)
+OP_END
diff --git a/vm/mterp/c/OP_CMPL_DOUBLE.c b/vm/mterp/c/OP_CMPL_DOUBLE.c
new file mode 100644
index 0000000..4da18b4
--- /dev/null
+++ b/vm/mterp/c/OP_CMPL_DOUBLE.c
@@ -0,0 +1,2 @@
+HANDLE_OP_CMPX(OP_CMPL_DOUBLE, "l-double", double, _DOUBLE, -1)
+OP_END
diff --git a/vm/mterp/c/OP_CMPL_FLOAT.c b/vm/mterp/c/OP_CMPL_FLOAT.c
new file mode 100644
index 0000000..7916193
--- /dev/null
+++ b/vm/mterp/c/OP_CMPL_FLOAT.c
@@ -0,0 +1,2 @@
+HANDLE_OP_CMPX(OP_CMPL_FLOAT, "l-float", float, _FLOAT, -1)
+OP_END
diff --git a/vm/mterp/c/OP_CMP_LONG.c b/vm/mterp/c/OP_CMP_LONG.c
new file mode 100644
index 0000000..a0e412c
--- /dev/null
+++ b/vm/mterp/c/OP_CMP_LONG.c
@@ -0,0 +1,2 @@
+HANDLE_OP_CMPX(OP_CMP_LONG, "-long", s8, _WIDE, 0)
+OP_END
diff --git a/vm/mterp/c/OP_CONST.c b/vm/mterp/c/OP_CONST.c
new file mode 100644
index 0000000..e281a51
--- /dev/null
+++ b/vm/mterp/c/OP_CONST.c
@@ -0,0 +1,12 @@
+HANDLE_OPCODE(OP_CONST /*vAA, #+BBBBBBBB*/)
+ {
+ u4 tmp;
+
+ vdst = INST_AA(inst);
+ tmp = FETCH(1);
+ tmp |= (u4)FETCH(2) << 16;
+ ILOGV("|const v%d,#0x%08x", vdst, tmp);
+ SET_REGISTER(vdst, tmp);
+ }
+ FINISH(3);
+OP_END
diff --git a/vm/mterp/c/OP_CONST_16.c b/vm/mterp/c/OP_CONST_16.c
new file mode 100644
index 0000000..f58f50c
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_16.c
@@ -0,0 +1,7 @@
+HANDLE_OPCODE(OP_CONST_16 /*vAA, #+BBBB*/)
+ vdst = INST_AA(inst);
+ vsrc1 = FETCH(1);
+ ILOGV("|const/16 v%d,#0x%04x", vdst, (s2)vsrc1);
+ SET_REGISTER(vdst, (s2) vsrc1);
+ FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_CONST_4.c b/vm/mterp/c/OP_CONST_4.c
new file mode 100644
index 0000000..800ef9a
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_4.c
@@ -0,0 +1,11 @@
+HANDLE_OPCODE(OP_CONST_4 /*vA, #+B*/)
+ {
+ s4 tmp;
+
+ vdst = INST_A(inst);
+ tmp = (s4) (INST_B(inst) << 28) >> 28; // sign extend 4-bit value
+ ILOGV("|const/4 v%d,#0x%02x", vdst, (s4)tmp);
+ SET_REGISTER(vdst, tmp);
+ }
+ FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_CONST_CLASS.c b/vm/mterp/c/OP_CONST_CLASS.c
new file mode 100644
index 0000000..9c60a27
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_CLASS.c
@@ -0,0 +1,18 @@
+HANDLE_OPCODE(OP_CONST_CLASS /*vAA, class@BBBB*/)
+ {
+ ClassObject* clazz;
+
+ vdst = INST_AA(inst);
+ ref = FETCH(1);
+ ILOGV("|const-class v%d class@0x%04x", vdst, ref);
+ clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+ if (clazz == NULL) {
+ EXPORT_PC();
+ clazz = dvmResolveClass(curMethod->clazz, ref, true);
+ if (clazz == NULL)
+ GOTO_exceptionThrown();
+ }
+ SET_REGISTER(vdst, (u4) clazz);
+ }
+ FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_CONST_HIGH16.c b/vm/mterp/c/OP_CONST_HIGH16.c
new file mode 100644
index 0000000..26b22f4
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_HIGH16.c
@@ -0,0 +1,7 @@
+HANDLE_OPCODE(OP_CONST_HIGH16 /*vAA, #+BBBB0000*/)
+ vdst = INST_AA(inst);
+ vsrc1 = FETCH(1);
+ ILOGV("|const/high16 v%d,#0x%04x0000", vdst, vsrc1);
+ SET_REGISTER(vdst, vsrc1 << 16);
+ FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_CONST_STRING.c b/vm/mterp/c/OP_CONST_STRING.c
new file mode 100644
index 0000000..748119a
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_STRING.c
@@ -0,0 +1,18 @@
+HANDLE_OPCODE(OP_CONST_STRING /*vAA, string@BBBB*/)
+ {
+ StringObject* strObj;
+
+ vdst = INST_AA(inst);
+ ref = FETCH(1);
+ ILOGV("|const-string v%d string@0x%04x", vdst, ref);
+ strObj = dvmDexGetResolvedString(methodClassDex, ref);
+ if (strObj == NULL) {
+ EXPORT_PC();
+ strObj = dvmResolveString(curMethod->clazz, ref);
+ if (strObj == NULL)
+ GOTO_exceptionThrown();
+ }
+ SET_REGISTER(vdst, (u4) strObj);
+ }
+ FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_CONST_STRING_JUMBO.c b/vm/mterp/c/OP_CONST_STRING_JUMBO.c
new file mode 100644
index 0000000..435b34c
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_STRING_JUMBO.c
@@ -0,0 +1,20 @@
+HANDLE_OPCODE(OP_CONST_STRING_JUMBO /*vAA, string@BBBBBBBB*/)
+ {
+ StringObject* strObj;
+ u4 tmp;
+
+ vdst = INST_AA(inst);
+ tmp = FETCH(1);
+ tmp |= (u4)FETCH(2) << 16;
+ ILOGV("|const-string/jumbo v%d string@0x%08x", vdst, tmp);
+ strObj = dvmDexGetResolvedString(methodClassDex, tmp);
+ if (strObj == NULL) {
+ EXPORT_PC();
+ strObj = dvmResolveString(curMethod->clazz, tmp);
+ if (strObj == NULL)
+ GOTO_exceptionThrown();
+ }
+ SET_REGISTER(vdst, (u4) strObj);
+ }
+ FINISH(3);
+OP_END
diff --git a/vm/mterp/c/OP_CONST_WIDE.c b/vm/mterp/c/OP_CONST_WIDE.c
new file mode 100644
index 0000000..ccb3955
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_WIDE.c
@@ -0,0 +1,14 @@
+HANDLE_OPCODE(OP_CONST_WIDE /*vAA, #+BBBBBBBBBBBBBBBB*/)
+ {
+ u8 tmp;
+
+ vdst = INST_AA(inst);
+ tmp = FETCH(1);
+ tmp |= (u8)FETCH(2) << 16;
+ tmp |= (u8)FETCH(3) << 32;
+ tmp |= (u8)FETCH(4) << 48;
+ ILOGV("|const-wide v%d,#0x%08llx", vdst, tmp);
+ SET_REGISTER_WIDE(vdst, tmp);
+ }
+ FINISH(5);
+OP_END
diff --git a/vm/mterp/c/OP_CONST_WIDE_16.c b/vm/mterp/c/OP_CONST_WIDE_16.c
new file mode 100644
index 0000000..da69f37
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_WIDE_16.c
@@ -0,0 +1,7 @@
+HANDLE_OPCODE(OP_CONST_WIDE_16 /*vAA, #+BBBB*/)
+ vdst = INST_AA(inst);
+ vsrc1 = FETCH(1);
+ ILOGV("|const-wide/16 v%d,#0x%04x", vdst, (s2)vsrc1);
+ SET_REGISTER_WIDE(vdst, (s2)vsrc1);
+ FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_CONST_WIDE_32.c b/vm/mterp/c/OP_CONST_WIDE_32.c
new file mode 100644
index 0000000..ad4acbb
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_WIDE_32.c
@@ -0,0 +1,12 @@
+HANDLE_OPCODE(OP_CONST_WIDE_32 /*vAA, #+BBBBBBBB*/)
+ {
+ u4 tmp;
+
+ vdst = INST_AA(inst);
+ tmp = FETCH(1);
+ tmp |= (u4)FETCH(2) << 16;
+ ILOGV("|const-wide/32 v%d,#0x%08x", vdst, tmp);
+ SET_REGISTER_WIDE(vdst, (s4) tmp);
+ }
+ FINISH(3);
+OP_END
diff --git a/vm/mterp/c/OP_CONST_WIDE_HIGH16.c b/vm/mterp/c/OP_CONST_WIDE_HIGH16.c
new file mode 100644
index 0000000..bcc0664
--- /dev/null
+++ b/vm/mterp/c/OP_CONST_WIDE_HIGH16.c
@@ -0,0 +1,7 @@
+HANDLE_OPCODE(OP_CONST_WIDE_HIGH16 /*vAA, #+BBBB000000000000*/)
+ vdst = INST_AA(inst);
+ vsrc1 = FETCH(1);
+ ILOGV("|const-wide/high16 v%d,#0x%04x000000000000", vdst, vsrc1);
+ SET_REGISTER_WIDE(vdst, ((u8) vsrc1) << 48);
+ FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_DIV_DOUBLE.c b/vm/mterp/c/OP_DIV_DOUBLE.c
new file mode 100644
index 0000000..d6e4b55
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_DOUBLE.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_DOUBLE(OP_DIV_DOUBLE, "div", /)
+OP_END
diff --git a/vm/mterp/c/OP_DIV_DOUBLE_2ADDR.c b/vm/mterp/c/OP_DIV_DOUBLE_2ADDR.c
new file mode 100644
index 0000000..85a1523
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_DOUBLE_2ADDR.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_DOUBLE_2ADDR(OP_DIV_DOUBLE_2ADDR, "div", /)
+OP_END
diff --git a/vm/mterp/c/OP_DIV_FLOAT.c b/vm/mterp/c/OP_DIV_FLOAT.c
new file mode 100644
index 0000000..2c5049b
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_FLOAT.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_FLOAT(OP_DIV_FLOAT, "div", /)
+OP_END
diff --git a/vm/mterp/c/OP_DIV_FLOAT_2ADDR.c b/vm/mterp/c/OP_DIV_FLOAT_2ADDR.c
new file mode 100644
index 0000000..cd7b4d9
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_FLOAT_2ADDR.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_FLOAT_2ADDR(OP_DIV_FLOAT_2ADDR, "div", /)
+OP_END
diff --git a/vm/mterp/c/OP_DIV_INT.c b/vm/mterp/c/OP_DIV_INT.c
new file mode 100644
index 0000000..af6e8c6
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_INT.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT(OP_DIV_INT, "div", /, 1)
+OP_END
diff --git a/vm/mterp/c/OP_DIV_INT_2ADDR.c b/vm/mterp/c/OP_DIV_INT_2ADDR.c
new file mode 100644
index 0000000..80c0da7
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_INT_2ADDR.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_2ADDR(OP_DIV_INT_2ADDR, "div", /, 1)
+OP_END
diff --git a/vm/mterp/c/OP_DIV_INT_LIT16.c b/vm/mterp/c/OP_DIV_INT_LIT16.c
new file mode 100644
index 0000000..4e8d901
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_INT_LIT16.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT16(OP_DIV_INT_LIT16, "div", /, 1)
+OP_END
diff --git a/vm/mterp/c/OP_DIV_INT_LIT8.c b/vm/mterp/c/OP_DIV_INT_LIT8.c
new file mode 100644
index 0000000..eec5389
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_INT_LIT8.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT8(OP_DIV_INT_LIT8, "div", /, 1)
+OP_END
diff --git a/vm/mterp/c/OP_DIV_LONG.c b/vm/mterp/c/OP_DIV_LONG.c
new file mode 100644
index 0000000..557b56b
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_LONG.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG(OP_DIV_LONG, "div", /, 1)
+OP_END
diff --git a/vm/mterp/c/OP_DIV_LONG_2ADDR.c b/vm/mterp/c/OP_DIV_LONG_2ADDR.c
new file mode 100644
index 0000000..00e0e6c
--- /dev/null
+++ b/vm/mterp/c/OP_DIV_LONG_2ADDR.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG_2ADDR(OP_DIV_LONG_2ADDR, "div", /, 1)
+OP_END
diff --git a/vm/mterp/c/OP_DOUBLE_TO_FLOAT.c b/vm/mterp/c/OP_DOUBLE_TO_FLOAT.c
new file mode 100644
index 0000000..152e5fd
--- /dev/null
+++ b/vm/mterp/c/OP_DOUBLE_TO_FLOAT.c
@@ -0,0 +1,2 @@
+HANDLE_NUMCONV(OP_DOUBLE_TO_FLOAT, "double-to-float", _DOUBLE, _FLOAT)
+OP_END
diff --git a/vm/mterp/c/OP_DOUBLE_TO_INT.c b/vm/mterp/c/OP_DOUBLE_TO_INT.c
new file mode 100644
index 0000000..e210b92
--- /dev/null
+++ b/vm/mterp/c/OP_DOUBLE_TO_INT.c
@@ -0,0 +1,3 @@
+HANDLE_FLOAT_TO_INT(OP_DOUBLE_TO_INT, "double-to-int",
+ double, _DOUBLE, s4, _INT)
+OP_END
diff --git a/vm/mterp/c/OP_DOUBLE_TO_LONG.c b/vm/mterp/c/OP_DOUBLE_TO_LONG.c
new file mode 100644
index 0000000..44d548c
--- /dev/null
+++ b/vm/mterp/c/OP_DOUBLE_TO_LONG.c
@@ -0,0 +1,3 @@
+HANDLE_FLOAT_TO_INT(OP_DOUBLE_TO_LONG, "double-to-long",
+ double, _DOUBLE, s8, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_EXECUTE_INLINE.c b/vm/mterp/c/OP_EXECUTE_INLINE.c
new file mode 100644
index 0000000..bc10f1a
--- /dev/null
+++ b/vm/mterp/c/OP_EXECUTE_INLINE.c
@@ -0,0 +1,59 @@
+HANDLE_OPCODE(OP_EXECUTE_INLINE /*vB, {vD, vE, vF, vG}, inline@CCCC*/)
+ {
+ /*
+ * This has the same form as other method calls, but we ignore
+ * the 5th argument (vA). This is chiefly because the first four
+ * arguments to a function on ARM are in registers.
+ *
+ * We only set the arguments that are actually used, leaving
+ * the rest uninitialized. We're assuming that, if the method
+ * needs them, they'll be specified in the call.
+ *
+ * However, this annoys gcc when optimizations are enabled,
+ * causing a "may be used uninitialized" warning. Quieting
+ * the warnings incurs a slight penalty (5%: 373ns vs. 393ns
+ * on empty method). Note that valgrind is perfectly happy
+ * either way as the uninitialiezd values are never actually
+ * used.
+ */
+ u4 arg0, arg1, arg2, arg3;
+ arg0 = arg1 = arg2 = arg3 = 0;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_B(inst); /* #of args */
+ ref = FETCH(1); /* inline call "ref" */
+ vdst = FETCH(2); /* 0-4 register indices */
+ ILOGV("|execute-inline args=%d @%d {regs=0x%04x}",
+ vsrc1, ref, vdst);
+
+ assert((vdst >> 16) == 0); // 16-bit type -or- high 16 bits clear
+ assert(vsrc1 <= 4);
+
+ switch (vsrc1) {
+ case 4:
+ arg3 = GET_REGISTER(vdst >> 12);
+ /* fall through */
+ case 3:
+ arg2 = GET_REGISTER((vdst & 0x0f00) >> 8);
+ /* fall through */
+ case 2:
+ arg1 = GET_REGISTER((vdst & 0x00f0) >> 4);
+ /* fall through */
+ case 1:
+ arg0 = GET_REGISTER(vdst & 0x0f);
+ /* fall through */
+ default: // case 0
+ ;
+ }
+
+#if INTERP_TYPE == INTERP_DBG
+ if (!dvmPerformInlineOp4Dbg(arg0, arg1, arg2, arg3, &retval, ref))
+ GOTO_exceptionThrown();
+#else
+ if (!dvmPerformInlineOp4Std(arg0, arg1, arg2, arg3, &retval, ref))
+ GOTO_exceptionThrown();
+#endif
+ }
+ FINISH(3);
+OP_END
diff --git a/vm/mterp/c/OP_EXECUTE_INLINE_RANGE.c b/vm/mterp/c/OP_EXECUTE_INLINE_RANGE.c
new file mode 100644
index 0000000..a767106
--- /dev/null
+++ b/vm/mterp/c/OP_EXECUTE_INLINE_RANGE.c
@@ -0,0 +1,43 @@
+HANDLE_OPCODE(OP_EXECUTE_INLINE_RANGE /*{vCCCC..v(CCCC+AA-1)}, inline@BBBB*/)
+ {
+ u4 arg0, arg1, arg2, arg3;
+ arg0 = arg1 = arg2 = arg3 = 0; /* placate gcc */
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst); /* #of args */
+ ref = FETCH(1); /* inline call "ref" */
+ vdst = FETCH(2); /* range base */
+ ILOGV("|execute-inline-range args=%d @%d {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+
+ assert((vdst >> 16) == 0); // 16-bit type -or- high 16 bits clear
+ assert(vsrc1 <= 4);
+
+ switch (vsrc1) {
+ case 4:
+ arg3 = GET_REGISTER(vdst+3);
+ /* fall through */
+ case 3:
+ arg2 = GET_REGISTER(vdst+2);
+ /* fall through */
+ case 2:
+ arg1 = GET_REGISTER(vdst+1);
+ /* fall through */
+ case 1:
+ arg0 = GET_REGISTER(vdst+0);
+ /* fall through */
+ default: // case 0
+ ;
+ }
+
+#if INTERP_TYPE == INTERP_DBG
+ if (!dvmPerformInlineOp4Dbg(arg0, arg1, arg2, arg3, &retval, ref))
+ GOTO_exceptionThrown();
+#else
+ if (!dvmPerformInlineOp4Std(arg0, arg1, arg2, arg3, &retval, ref))
+ GOTO_exceptionThrown();
+#endif
+ }
+ FINISH(3);
+OP_END
diff --git a/vm/mterp/c/OP_FILLED_NEW_ARRAY.c b/vm/mterp/c/OP_FILLED_NEW_ARRAY.c
new file mode 100644
index 0000000..fad7dbb
--- /dev/null
+++ b/vm/mterp/c/OP_FILLED_NEW_ARRAY.c
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_FILLED_NEW_ARRAY /*vB, {vD, vE, vF, vG, vA}, class@CCCC*/)
+ GOTO_invoke(filledNewArray, false);
+OP_END
diff --git a/vm/mterp/c/OP_FILLED_NEW_ARRAY_RANGE.c b/vm/mterp/c/OP_FILLED_NEW_ARRAY_RANGE.c
new file mode 100644
index 0000000..06c3a79
--- /dev/null
+++ b/vm/mterp/c/OP_FILLED_NEW_ARRAY_RANGE.c
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_FILLED_NEW_ARRAY_RANGE /*{vCCCC..v(CCCC+AA-1)}, class@BBBB*/)
+ GOTO_invoke(filledNewArray, true);
+OP_END
diff --git a/vm/mterp/c/OP_FILL_ARRAY_DATA.c b/vm/mterp/c/OP_FILL_ARRAY_DATA.c
new file mode 100644
index 0000000..095b465
--- /dev/null
+++ b/vm/mterp/c/OP_FILL_ARRAY_DATA.c
@@ -0,0 +1,28 @@
+HANDLE_OPCODE(OP_FILL_ARRAY_DATA) /*vAA, +BBBBBBBB*/
+ {
+ const u2* arrayData;
+ s4 offset;
+ ArrayObject* arrayObj;
+
+ EXPORT_PC();
+ vsrc1 = INST_AA(inst);
+ offset = FETCH(1) | (((s4) FETCH(2)) << 16);
+ ILOGV("|fill-array-data v%d +0x%04x", vsrc1, offset);
+ arrayData = pc + offset; // offset in 16-bit units
+#ifndef NDEBUG
+ if (arrayData < curMethod->insns ||
+ arrayData >= curMethod->insns + dvmGetMethodInsnsSize(curMethod))
+ {
+ /* should have been caught in verifier */
+ dvmThrowException("Ljava/lang/InternalError;",
+ "bad fill array data");
+ GOTO_exceptionThrown();
+ }
+#endif
+ arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);
+ if (!dvmInterpHandleFillArrayData(arrayObj, arrayData)) {
+ GOTO_exceptionThrown();
+ }
+ FINISH(3);
+ }
+OP_END
diff --git a/vm/mterp/c/OP_FLOAT_TO_DOUBLE.c b/vm/mterp/c/OP_FLOAT_TO_DOUBLE.c
new file mode 100644
index 0000000..ea5e7a6
--- /dev/null
+++ b/vm/mterp/c/OP_FLOAT_TO_DOUBLE.c
@@ -0,0 +1,2 @@
+HANDLE_NUMCONV(OP_FLOAT_TO_DOUBLE, "float-to-double", _FLOAT, _DOUBLE)
+OP_END
diff --git a/vm/mterp/c/OP_FLOAT_TO_INT.c b/vm/mterp/c/OP_FLOAT_TO_INT.c
new file mode 100644
index 0000000..15522f8
--- /dev/null
+++ b/vm/mterp/c/OP_FLOAT_TO_INT.c
@@ -0,0 +1,3 @@
+HANDLE_FLOAT_TO_INT(OP_FLOAT_TO_INT, "float-to-int",
+ float, _FLOAT, s4, _INT)
+OP_END
diff --git a/vm/mterp/c/OP_FLOAT_TO_LONG.c b/vm/mterp/c/OP_FLOAT_TO_LONG.c
new file mode 100644
index 0000000..03bd30d
--- /dev/null
+++ b/vm/mterp/c/OP_FLOAT_TO_LONG.c
@@ -0,0 +1,3 @@
+HANDLE_FLOAT_TO_INT(OP_FLOAT_TO_LONG, "float-to-long",
+ float, _FLOAT, s8, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_GOTO.c b/vm/mterp/c/OP_GOTO.c
new file mode 100644
index 0000000..eed7b9f
--- /dev/null
+++ b/vm/mterp/c/OP_GOTO.c
@@ -0,0 +1,11 @@
+HANDLE_OPCODE(OP_GOTO /*+AA*/)
+ vdst = INST_AA(inst);
+ if ((s1)vdst < 0)
+ ILOGV("|goto -0x%02x", -((s1)vdst));
+ else
+ ILOGV("|goto +0x%02x", ((s1)vdst));
+ ILOGV("> branch taken");
+ if ((s1)vdst < 0)
+ PERIODIC_CHECKS(kInterpEntryInstr, (s1)vdst);
+ FINISH((s1)vdst);
+OP_END
diff --git a/vm/mterp/c/OP_GOTO_16.c b/vm/mterp/c/OP_GOTO_16.c
new file mode 100644
index 0000000..afdccb3
--- /dev/null
+++ b/vm/mterp/c/OP_GOTO_16.c
@@ -0,0 +1,14 @@
+HANDLE_OPCODE(OP_GOTO_16 /*+AAAA*/)
+ {
+ s4 offset = (s2) FETCH(1); /* sign-extend next code unit */
+
+ if (offset < 0)
+ ILOGV("|goto/16 -0x%04x", -offset);
+ else
+ ILOGV("|goto/16 +0x%04x", offset);
+ ILOGV("> branch taken");
+ if (offset < 0)
+ PERIODIC_CHECKS(kInterpEntryInstr, offset);
+ FINISH(offset);
+ }
+OP_END
diff --git a/vm/mterp/c/OP_GOTO_32.c b/vm/mterp/c/OP_GOTO_32.c
new file mode 100644
index 0000000..d1cee32
--- /dev/null
+++ b/vm/mterp/c/OP_GOTO_32.c
@@ -0,0 +1,15 @@
+HANDLE_OPCODE(OP_GOTO_32 /*+AAAAAAAA*/)
+ {
+ s4 offset = FETCH(1); /* low-order 16 bits */
+ offset |= ((s4) FETCH(2)) << 16; /* high-order 16 bits */
+
+ if (offset < 0)
+ ILOGV("|goto/32 -0x%08x", -offset);
+ else
+ ILOGV("|goto/32 +0x%08x", offset);
+ ILOGV("> branch taken");
+ if (offset <= 0) /* allowed to branch to self */
+ PERIODIC_CHECKS(kInterpEntryInstr, offset);
+ FINISH(offset);
+ }
+OP_END
diff --git a/vm/mterp/c/OP_IF_EQ.c b/vm/mterp/c/OP_IF_EQ.c
new file mode 100644
index 0000000..2c3b9b5
--- /dev/null
+++ b/vm/mterp/c/OP_IF_EQ.c
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XX(OP_IF_EQ, "eq", ==)
+OP_END
diff --git a/vm/mterp/c/OP_IF_EQZ.c b/vm/mterp/c/OP_IF_EQZ.c
new file mode 100644
index 0000000..d2dd1aa
--- /dev/null
+++ b/vm/mterp/c/OP_IF_EQZ.c
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XXZ(OP_IF_EQZ, "eqz", ==)
+OP_END
diff --git a/vm/mterp/c/OP_IF_GE.c b/vm/mterp/c/OP_IF_GE.c
new file mode 100644
index 0000000..8aa85c4
--- /dev/null
+++ b/vm/mterp/c/OP_IF_GE.c
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XX(OP_IF_GE, "ge", >=)
+OP_END
diff --git a/vm/mterp/c/OP_IF_GEZ.c b/vm/mterp/c/OP_IF_GEZ.c
new file mode 100644
index 0000000..8c4b78a
--- /dev/null
+++ b/vm/mterp/c/OP_IF_GEZ.c
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XXZ(OP_IF_GEZ, "gez", >=)
+OP_END
diff --git a/vm/mterp/c/OP_IF_GT.c b/vm/mterp/c/OP_IF_GT.c
new file mode 100644
index 0000000..d35eb29
--- /dev/null
+++ b/vm/mterp/c/OP_IF_GT.c
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XX(OP_IF_GT, "gt", >)
+OP_END
diff --git a/vm/mterp/c/OP_IF_GTZ.c b/vm/mterp/c/OP_IF_GTZ.c
new file mode 100644
index 0000000..63a0073
--- /dev/null
+++ b/vm/mterp/c/OP_IF_GTZ.c
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XXZ(OP_IF_GTZ, "gtz", >)
+OP_END
diff --git a/vm/mterp/c/OP_IF_LE.c b/vm/mterp/c/OP_IF_LE.c
new file mode 100644
index 0000000..f4b213a
--- /dev/null
+++ b/vm/mterp/c/OP_IF_LE.c
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XX(OP_IF_LE, "le", <=)
+OP_END
diff --git a/vm/mterp/c/OP_IF_LEZ.c b/vm/mterp/c/OP_IF_LEZ.c
new file mode 100644
index 0000000..1d57a50
--- /dev/null
+++ b/vm/mterp/c/OP_IF_LEZ.c
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XXZ(OP_IF_LEZ, "lez", <=)
+OP_END
diff --git a/vm/mterp/c/OP_IF_LT.c b/vm/mterp/c/OP_IF_LT.c
new file mode 100644
index 0000000..0233892
--- /dev/null
+++ b/vm/mterp/c/OP_IF_LT.c
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XX(OP_IF_LT, "lt", <)
+OP_END
diff --git a/vm/mterp/c/OP_IF_LTZ.c b/vm/mterp/c/OP_IF_LTZ.c
new file mode 100644
index 0000000..b4b9be2
--- /dev/null
+++ b/vm/mterp/c/OP_IF_LTZ.c
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XXZ(OP_IF_LTZ, "ltz", <)
+OP_END
diff --git a/vm/mterp/c/OP_IF_NE.c b/vm/mterp/c/OP_IF_NE.c
new file mode 100644
index 0000000..8da70a5
--- /dev/null
+++ b/vm/mterp/c/OP_IF_NE.c
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XX(OP_IF_NE, "ne", !=)
+OP_END
diff --git a/vm/mterp/c/OP_IF_NEZ.c b/vm/mterp/c/OP_IF_NEZ.c
new file mode 100644
index 0000000..209e836
--- /dev/null
+++ b/vm/mterp/c/OP_IF_NEZ.c
@@ -0,0 +1,2 @@
+HANDLE_OP_IF_XXZ(OP_IF_NEZ, "nez", !=)
+OP_END
diff --git a/vm/mterp/c/OP_IGET.c b/vm/mterp/c/OP_IGET.c
new file mode 100644
index 0000000..c6333e5
--- /dev/null
+++ b/vm/mterp/c/OP_IGET.c
@@ -0,0 +1,2 @@
+HANDLE_IGET_X(OP_IGET, "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IGET_BOOLEAN.c b/vm/mterp/c/OP_IGET_BOOLEAN.c
new file mode 100644
index 0000000..a5a47be
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_BOOLEAN.c
@@ -0,0 +1,2 @@
+HANDLE_IGET_X(OP_IGET_BOOLEAN, "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IGET_BYTE.c b/vm/mterp/c/OP_IGET_BYTE.c
new file mode 100644
index 0000000..647f311
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_BYTE.c
@@ -0,0 +1,2 @@
+HANDLE_IGET_X(OP_IGET_BYTE, "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IGET_CHAR.c b/vm/mterp/c/OP_IGET_CHAR.c
new file mode 100644
index 0000000..9a8adb0
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_CHAR.c
@@ -0,0 +1,2 @@
+HANDLE_IGET_X(OP_IGET_CHAR, "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IGET_OBJECT.c b/vm/mterp/c/OP_IGET_OBJECT.c
new file mode 100644
index 0000000..03c9e50
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_OBJECT.c
@@ -0,0 +1,2 @@
+HANDLE_IGET_X(OP_IGET_OBJECT, "-object", Object, _AS_OBJECT)
+OP_END
diff --git a/vm/mterp/c/OP_IGET_OBJECT_QUICK.c b/vm/mterp/c/OP_IGET_OBJECT_QUICK.c
new file mode 100644
index 0000000..2ac3a54
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_OBJECT_QUICK.c
@@ -0,0 +1,2 @@
+HANDLE_IGET_X_QUICK(OP_IGET_OBJECT_QUICK, "-object", Object, _AS_OBJECT)
+OP_END
diff --git a/vm/mterp/c/OP_IGET_OBJECT_VOLATILE.c b/vm/mterp/c/OP_IGET_OBJECT_VOLATILE.c
new file mode 100644
index 0000000..3577552
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_OBJECT_VOLATILE.c
@@ -0,0 +1,2 @@
+HANDLE_IGET_X(OP_IGET_OBJECT_VOLATILE, "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
diff --git a/vm/mterp/c/OP_IGET_QUICK.c b/vm/mterp/c/OP_IGET_QUICK.c
new file mode 100644
index 0000000..b5724cc
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_QUICK.c
@@ -0,0 +1,2 @@
+HANDLE_IGET_X_QUICK(OP_IGET_QUICK, "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IGET_SHORT.c b/vm/mterp/c/OP_IGET_SHORT.c
new file mode 100644
index 0000000..3e77789
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_SHORT.c
@@ -0,0 +1,2 @@
+HANDLE_IGET_X(OP_IGET_SHORT, "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IGET_VOLATILE.c b/vm/mterp/c/OP_IGET_VOLATILE.c
new file mode 100644
index 0000000..7a3be56
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_VOLATILE.c
@@ -0,0 +1,2 @@
+HANDLE_IGET_X(OP_IGET_VOLATILE, "-volatile", IntVolatile, )
+OP_END
diff --git a/vm/mterp/c/OP_IGET_WIDE.c b/vm/mterp/c/OP_IGET_WIDE.c
new file mode 100644
index 0000000..cb1fcca
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_WIDE.c
@@ -0,0 +1,2 @@
+HANDLE_IGET_X(OP_IGET_WIDE, "-wide", Long, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_IGET_WIDE_QUICK.c b/vm/mterp/c/OP_IGET_WIDE_QUICK.c
new file mode 100644
index 0000000..adb4fc1
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_WIDE_QUICK.c
@@ -0,0 +1,2 @@
+HANDLE_IGET_X_QUICK(OP_IGET_WIDE_QUICK, "-wide", Long, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_IGET_WIDE_VOLATILE.c b/vm/mterp/c/OP_IGET_WIDE_VOLATILE.c
new file mode 100644
index 0000000..a080823
--- /dev/null
+++ b/vm/mterp/c/OP_IGET_WIDE_VOLATILE.c
@@ -0,0 +1,2 @@
+HANDLE_IGET_X(OP_IGET_WIDE_VOLATILE, "-wide-volatile", LongVolatile, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_INSTANCE_OF.c b/vm/mterp/c/OP_INSTANCE_OF.c
new file mode 100644
index 0000000..8b8f9d3
--- /dev/null
+++ b/vm/mterp/c/OP_INSTANCE_OF.c
@@ -0,0 +1,30 @@
+HANDLE_OPCODE(OP_INSTANCE_OF /*vA, vB, class@CCCC*/)
+ {
+ ClassObject* clazz;
+ Object* obj;
+
+ vdst = INST_A(inst);
+ vsrc1 = INST_B(inst); /* object to check */
+ ref = FETCH(1); /* class to check against */
+ ILOGV("|instance-of v%d,v%d,class@0x%04x", vdst, vsrc1, ref);
+
+ obj = (Object*)GET_REGISTER(vsrc1);
+ if (obj == NULL) {
+ SET_REGISTER(vdst, 0);
+ } else {
+#if defined(WITH_EXTRA_OBJECT_VALIDATION)
+ if (!checkForNullExportPC(obj, fp, pc))
+ GOTO_exceptionThrown();
+#endif
+ clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+ if (clazz == NULL) {
+ EXPORT_PC();
+ clazz = dvmResolveClass(curMethod->clazz, ref, true);
+ if (clazz == NULL)
+ GOTO_exceptionThrown();
+ }
+ SET_REGISTER(vdst, dvmInstanceof(obj->clazz, clazz));
+ }
+ }
+ FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_INT_TO_BYTE.c b/vm/mterp/c/OP_INT_TO_BYTE.c
new file mode 100644
index 0000000..ea75747
--- /dev/null
+++ b/vm/mterp/c/OP_INT_TO_BYTE.c
@@ -0,0 +1,2 @@
+HANDLE_INT_TO_SMALL(OP_INT_TO_BYTE, "byte", s1)
+OP_END
diff --git a/vm/mterp/c/OP_INT_TO_CHAR.c b/vm/mterp/c/OP_INT_TO_CHAR.c
new file mode 100644
index 0000000..45ae0df
--- /dev/null
+++ b/vm/mterp/c/OP_INT_TO_CHAR.c
@@ -0,0 +1,2 @@
+HANDLE_INT_TO_SMALL(OP_INT_TO_CHAR, "char", u2)
+OP_END
diff --git a/vm/mterp/c/OP_INT_TO_DOUBLE.c b/vm/mterp/c/OP_INT_TO_DOUBLE.c
new file mode 100644
index 0000000..624c702
--- /dev/null
+++ b/vm/mterp/c/OP_INT_TO_DOUBLE.c
@@ -0,0 +1,2 @@
+HANDLE_NUMCONV(OP_INT_TO_DOUBLE, "int-to-double", _INT, _DOUBLE)
+OP_END
diff --git a/vm/mterp/c/OP_INT_TO_FLOAT.c b/vm/mterp/c/OP_INT_TO_FLOAT.c
new file mode 100644
index 0000000..fd15199
--- /dev/null
+++ b/vm/mterp/c/OP_INT_TO_FLOAT.c
@@ -0,0 +1,2 @@
+HANDLE_NUMCONV(OP_INT_TO_FLOAT, "int-to-float", _INT, _FLOAT)
+OP_END
diff --git a/vm/mterp/c/OP_INT_TO_LONG.c b/vm/mterp/c/OP_INT_TO_LONG.c
new file mode 100644
index 0000000..8bc4223
--- /dev/null
+++ b/vm/mterp/c/OP_INT_TO_LONG.c
@@ -0,0 +1,2 @@
+HANDLE_NUMCONV(OP_INT_TO_LONG, "int-to-long", _INT, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_INT_TO_SHORT.c b/vm/mterp/c/OP_INT_TO_SHORT.c
new file mode 100644
index 0000000..0f06739
--- /dev/null
+++ b/vm/mterp/c/OP_INT_TO_SHORT.c
@@ -0,0 +1,2 @@
+HANDLE_INT_TO_SMALL(OP_INT_TO_SHORT, "short", s2) /* want sign bit */
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_DIRECT.c b/vm/mterp/c/OP_INVOKE_DIRECT.c
new file mode 100644
index 0000000..58cfe5b
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_DIRECT.c
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_DIRECT /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+ GOTO_invoke(invokeDirect, false);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_DIRECT_EMPTY.c b/vm/mterp/c/OP_INVOKE_DIRECT_EMPTY.c
new file mode 100644
index 0000000..d649252
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_DIRECT_EMPTY.c
@@ -0,0 +1,15 @@
+HANDLE_OPCODE(OP_INVOKE_DIRECT_EMPTY /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+#if INTERP_TYPE != INTERP_DBG
+ //LOGI("Ignoring empty\n");
+ FINISH(3);
+#else
+ if (!gDvm.debuggerActive) {
+ //LOGI("Skipping empty\n");
+ FINISH(3); // don't want it to show up in profiler output
+ } else {
+ //LOGI("Running empty\n");
+ /* fall through to OP_INVOKE_DIRECT */
+ GOTO_invoke(invokeDirect, false);
+ }
+#endif
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_DIRECT_RANGE.c b/vm/mterp/c/OP_INVOKE_DIRECT_RANGE.c
new file mode 100644
index 0000000..9877bbe
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_DIRECT_RANGE.c
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_DIRECT_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+ GOTO_invoke(invokeDirect, true);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_INTERFACE.c b/vm/mterp/c/OP_INVOKE_INTERFACE.c
new file mode 100644
index 0000000..9c639d5
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_INTERFACE.c
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_INTERFACE /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+ GOTO_invoke(invokeInterface, false);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_INTERFACE_RANGE.c b/vm/mterp/c/OP_INVOKE_INTERFACE_RANGE.c
new file mode 100644
index 0000000..6244c9e
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_INTERFACE_RANGE.c
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_INTERFACE_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+ GOTO_invoke(invokeInterface, true);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_STATIC.c b/vm/mterp/c/OP_INVOKE_STATIC.c
new file mode 100644
index 0000000..81f3d62
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_STATIC.c
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_STATIC /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+ GOTO_invoke(invokeStatic, false);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_STATIC_RANGE.c b/vm/mterp/c/OP_INVOKE_STATIC_RANGE.c
new file mode 100644
index 0000000..3fc4c35
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_STATIC_RANGE.c
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_STATIC_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+ GOTO_invoke(invokeStatic, true);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_SUPER.c b/vm/mterp/c/OP_INVOKE_SUPER.c
new file mode 100644
index 0000000..e7baea4
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_SUPER.c
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_SUPER /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+ GOTO_invoke(invokeSuper, false);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_SUPER_QUICK.c b/vm/mterp/c/OP_INVOKE_SUPER_QUICK.c
new file mode 100644
index 0000000..b66e033
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_SUPER_QUICK.c
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_SUPER_QUICK /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+ GOTO_invoke(invokeSuperQuick, false);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_SUPER_QUICK_RANGE.c b/vm/mterp/c/OP_INVOKE_SUPER_QUICK_RANGE.c
new file mode 100644
index 0000000..879497b
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_SUPER_QUICK_RANGE.c
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_SUPER_QUICK_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+ GOTO_invoke(invokeSuperQuick, true);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_SUPER_RANGE.c b/vm/mterp/c/OP_INVOKE_SUPER_RANGE.c
new file mode 100644
index 0000000..724e3a0
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_SUPER_RANGE.c
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_SUPER_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+ GOTO_invoke(invokeSuper, true);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_VIRTUAL.c b/vm/mterp/c/OP_INVOKE_VIRTUAL.c
new file mode 100644
index 0000000..29a4560
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_VIRTUAL.c
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+ GOTO_invoke(invokeVirtual, false);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_VIRTUAL_QUICK.c b/vm/mterp/c/OP_INVOKE_VIRTUAL_QUICK.c
new file mode 100644
index 0000000..244fed4
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_VIRTUAL_QUICK.c
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL_QUICK /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+ GOTO_invoke(invokeVirtualQuick, false);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_VIRTUAL_QUICK_RANGE.c b/vm/mterp/c/OP_INVOKE_VIRTUAL_QUICK_RANGE.c
new file mode 100644
index 0000000..9adb4ad
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_VIRTUAL_QUICK_RANGE.c
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL_QUICK_RANGE/*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+ GOTO_invoke(invokeVirtualQuick, true);
+OP_END
diff --git a/vm/mterp/c/OP_INVOKE_VIRTUAL_RANGE.c b/vm/mterp/c/OP_INVOKE_VIRTUAL_RANGE.c
new file mode 100644
index 0000000..94671ae
--- /dev/null
+++ b/vm/mterp/c/OP_INVOKE_VIRTUAL_RANGE.c
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+ GOTO_invoke(invokeVirtual, true);
+OP_END
diff --git a/vm/mterp/c/OP_IPUT.c b/vm/mterp/c/OP_IPUT.c
new file mode 100644
index 0000000..9d503ef
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT.c
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X(OP_IPUT, "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_BOOLEAN.c b/vm/mterp/c/OP_IPUT_BOOLEAN.c
new file mode 100644
index 0000000..7fe4929
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_BOOLEAN.c
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X(OP_IPUT_BOOLEAN, "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_BYTE.c b/vm/mterp/c/OP_IPUT_BYTE.c
new file mode 100644
index 0000000..8a49fb7
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_BYTE.c
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X(OP_IPUT_BYTE, "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_CHAR.c b/vm/mterp/c/OP_IPUT_CHAR.c
new file mode 100644
index 0000000..b2812c2
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_CHAR.c
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X(OP_IPUT_CHAR, "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_OBJECT.c b/vm/mterp/c/OP_IPUT_OBJECT.c
new file mode 100644
index 0000000..dbfb5ab
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_OBJECT.c
@@ -0,0 +1,13 @@
+/*
+ * The VM spec says we should verify that the reference being stored into
+ * the field is assignment compatible. In practice, many popular VMs don't
+ * do this because it slows down a very common operation. It's not so bad
+ * for us, since "dexopt" quickens it whenever possible, but it's still an
+ * issue.
+ *
+ * To make this spec-complaint, we'd need to add a ClassObject pointer to
+ * the Field struct, resolve the field's type descriptor at link or class
+ * init time, and then verify the type here.
+ */
+HANDLE_IPUT_X(OP_IPUT_OBJECT, "-object", Object, _AS_OBJECT)
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_OBJECT_QUICK.c b/vm/mterp/c/OP_IPUT_OBJECT_QUICK.c
new file mode 100644
index 0000000..8670188
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_OBJECT_QUICK.c
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X_QUICK(OP_IPUT_OBJECT_QUICK, "-object", Object, _AS_OBJECT)
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_OBJECT_VOLATILE.c b/vm/mterp/c/OP_IPUT_OBJECT_VOLATILE.c
new file mode 100644
index 0000000..cce63c1
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_OBJECT_VOLATILE.c
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X(OP_IPUT_OBJECT_VOLATILE, "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_QUICK.c b/vm/mterp/c/OP_IPUT_QUICK.c
new file mode 100644
index 0000000..483b9b1
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_QUICK.c
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X_QUICK(OP_IPUT_QUICK, "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_SHORT.c b/vm/mterp/c/OP_IPUT_SHORT.c
new file mode 100644
index 0000000..0a63ebc
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_SHORT.c
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X(OP_IPUT_SHORT, "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_VOLATILE.c b/vm/mterp/c/OP_IPUT_VOLATILE.c
new file mode 100644
index 0000000..814379e
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_VOLATILE.c
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X(OP_IPUT_VOLATILE, "-volatile", IntVolatile, )
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_WIDE.c b/vm/mterp/c/OP_IPUT_WIDE.c
new file mode 100644
index 0000000..bb4926c
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_WIDE.c
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X(OP_IPUT_WIDE, "-wide", Long, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_WIDE_QUICK.c b/vm/mterp/c/OP_IPUT_WIDE_QUICK.c
new file mode 100644
index 0000000..691630b
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_WIDE_QUICK.c
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X_QUICK(OP_IPUT_WIDE_QUICK, "-wide", Long, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_IPUT_WIDE_VOLATILE.c b/vm/mterp/c/OP_IPUT_WIDE_VOLATILE.c
new file mode 100644
index 0000000..d888b4a
--- /dev/null
+++ b/vm/mterp/c/OP_IPUT_WIDE_VOLATILE.c
@@ -0,0 +1,2 @@
+HANDLE_IPUT_X(OP_IPUT_WIDE_VOLATILE, "-wide-volatile", LongVolatile, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_LONG_TO_DOUBLE.c b/vm/mterp/c/OP_LONG_TO_DOUBLE.c
new file mode 100644
index 0000000..91b5eb2
--- /dev/null
+++ b/vm/mterp/c/OP_LONG_TO_DOUBLE.c
@@ -0,0 +1,2 @@
+HANDLE_NUMCONV(OP_LONG_TO_DOUBLE, "long-to-double", _WIDE, _DOUBLE)
+OP_END
diff --git a/vm/mterp/c/OP_LONG_TO_FLOAT.c b/vm/mterp/c/OP_LONG_TO_FLOAT.c
new file mode 100644
index 0000000..ff1f5fb
--- /dev/null
+++ b/vm/mterp/c/OP_LONG_TO_FLOAT.c
@@ -0,0 +1,2 @@
+HANDLE_NUMCONV(OP_LONG_TO_FLOAT, "long-to-float", _WIDE, _FLOAT)
+OP_END
diff --git a/vm/mterp/c/OP_LONG_TO_INT.c b/vm/mterp/c/OP_LONG_TO_INT.c
new file mode 100644
index 0000000..87c9a2e
--- /dev/null
+++ b/vm/mterp/c/OP_LONG_TO_INT.c
@@ -0,0 +1,2 @@
+HANDLE_NUMCONV(OP_LONG_TO_INT, "long-to-int", _WIDE, _INT)
+OP_END
diff --git a/vm/mterp/c/OP_MONITOR_ENTER.c b/vm/mterp/c/OP_MONITOR_ENTER.c
new file mode 100644
index 0000000..c9d8999
--- /dev/null
+++ b/vm/mterp/c/OP_MONITOR_ENTER.c
@@ -0,0 +1,20 @@
+HANDLE_OPCODE(OP_MONITOR_ENTER /*vAA*/)
+ {
+ Object* obj;
+
+ vsrc1 = INST_AA(inst);
+ ILOGV("|monitor-enter v%d %s(0x%08x)",
+ vsrc1, kSpacing+6, GET_REGISTER(vsrc1));
+ obj = (Object*)GET_REGISTER(vsrc1);
+ if (!checkForNullExportPC(obj, fp, pc))
+ GOTO_exceptionThrown();
+ ILOGV("+ locking %p %s\n", obj, obj->clazz->descriptor);
+ EXPORT_PC(); /* need for precise GC, also WITH_MONITOR_TRACKING */
+ dvmLockObject(self, obj);
+#ifdef WITH_DEADLOCK_PREDICTION
+ if (dvmCheckException(self))
+ GOTO_exceptionThrown();
+#endif
+ }
+ FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_MONITOR_EXIT.c b/vm/mterp/c/OP_MONITOR_EXIT.c
new file mode 100644
index 0000000..cb8f99b
--- /dev/null
+++ b/vm/mterp/c/OP_MONITOR_EXIT.c
@@ -0,0 +1,30 @@
+HANDLE_OPCODE(OP_MONITOR_EXIT /*vAA*/)
+ {
+ Object* obj;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst);
+ ILOGV("|monitor-exit v%d %s(0x%08x)",
+ vsrc1, kSpacing+5, GET_REGISTER(vsrc1));
+ obj = (Object*)GET_REGISTER(vsrc1);
+ if (!checkForNull(obj)) {
+ /*
+ * The exception needs to be processed at the *following*
+ * instruction, not the current instruction (see the Dalvik
+ * spec). Because we're jumping to an exception handler,
+ * we're not actually at risk of skipping an instruction
+ * by doing so.
+ */
+ ADJUST_PC(1); /* monitor-exit width is 1 */
+ GOTO_exceptionThrown();
+ }
+ ILOGV("+ unlocking %p %s\n", obj, obj->clazz->descriptor);
+ if (!dvmUnlockObject(self, obj)) {
+ assert(dvmCheckException(self));
+ ADJUST_PC(1);
+ GOTO_exceptionThrown();
+ }
+ }
+ FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_MOVE.c b/vm/mterp/c/OP_MOVE.c
new file mode 100644
index 0000000..6666199
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE.c
@@ -0,0 +1,9 @@
+HANDLE_OPCODE($opcode /*vA, vB*/)
+ vdst = INST_A(inst);
+ vsrc1 = INST_B(inst);
+ ILOGV("|move%s v%d,v%d %s(v%d=0x%08x)",
+ (INST_INST(inst) == OP_MOVE) ? "" : "-object", vdst, vsrc1,
+ kSpacing, vdst, GET_REGISTER(vsrc1));
+ SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+ FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_MOVE_16.c b/vm/mterp/c/OP_MOVE_16.c
new file mode 100644
index 0000000..53af5d5
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_16.c
@@ -0,0 +1,9 @@
+HANDLE_OPCODE($opcode /*vAAAA, vBBBB*/)
+ vdst = FETCH(1);
+ vsrc1 = FETCH(2);
+ ILOGV("|move%s/16 v%d,v%d %s(v%d=0x%08x)",
+ (INST_INST(inst) == OP_MOVE_16) ? "" : "-object", vdst, vsrc1,
+ kSpacing, vdst, GET_REGISTER(vsrc1));
+ SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+ FINISH(3);
+OP_END
diff --git a/vm/mterp/c/OP_MOVE_EXCEPTION.c b/vm/mterp/c/OP_MOVE_EXCEPTION.c
new file mode 100644
index 0000000..86587ca
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_EXCEPTION.c
@@ -0,0 +1,8 @@
+HANDLE_OPCODE(OP_MOVE_EXCEPTION /*vAA*/)
+ vdst = INST_AA(inst);
+ ILOGV("|move-exception v%d", vdst);
+ assert(self->exception != NULL);
+ SET_REGISTER(vdst, (u4)self->exception);
+ dvmClearException(self);
+ FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_MOVE_FROM16.c b/vm/mterp/c/OP_MOVE_FROM16.c
new file mode 100644
index 0000000..59fc285
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_FROM16.c
@@ -0,0 +1,9 @@
+HANDLE_OPCODE($opcode /*vAA, vBBBB*/)
+ vdst = INST_AA(inst);
+ vsrc1 = FETCH(1);
+ ILOGV("|move%s/from16 v%d,v%d %s(v%d=0x%08x)",
+ (INST_INST(inst) == OP_MOVE_FROM16) ? "" : "-object", vdst, vsrc1,
+ kSpacing, vdst, GET_REGISTER(vsrc1));
+ SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+ FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_MOVE_OBJECT.c b/vm/mterp/c/OP_MOVE_OBJECT.c
new file mode 100644
index 0000000..6ace6d9
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_OBJECT.c
@@ -0,0 +1 @@
+%include "c/OP_MOVE.c"
diff --git a/vm/mterp/c/OP_MOVE_OBJECT_16.c b/vm/mterp/c/OP_MOVE_OBJECT_16.c
new file mode 100644
index 0000000..7789ef4
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_OBJECT_16.c
@@ -0,0 +1 @@
+%include "c/OP_MOVE_16.c"
diff --git a/vm/mterp/c/OP_MOVE_OBJECT_FROM16.c b/vm/mterp/c/OP_MOVE_OBJECT_FROM16.c
new file mode 100644
index 0000000..8caf995
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_OBJECT_FROM16.c
@@ -0,0 +1 @@
+%include "c/OP_MOVE_FROM16.c"
diff --git a/vm/mterp/c/OP_MOVE_RESULT.c b/vm/mterp/c/OP_MOVE_RESULT.c
new file mode 100644
index 0000000..ddf535b
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_RESULT.c
@@ -0,0 +1,8 @@
+HANDLE_OPCODE($opcode /*vAA*/)
+ vdst = INST_AA(inst);
+ ILOGV("|move-result%s v%d %s(v%d=0x%08x)",
+ (INST_INST(inst) == OP_MOVE_RESULT) ? "" : "-object",
+ vdst, kSpacing+4, vdst,retval.i);
+ SET_REGISTER(vdst, retval.i);
+ FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_MOVE_RESULT_OBJECT.c b/vm/mterp/c/OP_MOVE_RESULT_OBJECT.c
new file mode 100644
index 0000000..a8358b1
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_RESULT_OBJECT.c
@@ -0,0 +1 @@
+%include "c/OP_MOVE_RESULT.c"
diff --git a/vm/mterp/c/OP_MOVE_RESULT_WIDE.c b/vm/mterp/c/OP_MOVE_RESULT_WIDE.c
new file mode 100644
index 0000000..f6ec8d9
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_RESULT_WIDE.c
@@ -0,0 +1,6 @@
+HANDLE_OPCODE(OP_MOVE_RESULT_WIDE /*vAA*/)
+ vdst = INST_AA(inst);
+ ILOGV("|move-result-wide v%d %s(0x%08llx)", vdst, kSpacing, retval.j);
+ SET_REGISTER_WIDE(vdst, retval.j);
+ FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_MOVE_WIDE.c b/vm/mterp/c/OP_MOVE_WIDE.c
new file mode 100644
index 0000000..9ee323d
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_WIDE.c
@@ -0,0 +1,10 @@
+HANDLE_OPCODE(OP_MOVE_WIDE /*vA, vB*/)
+ /* IMPORTANT: must correctly handle overlapping registers, e.g. both
+ * "move-wide v6, v7" and "move-wide v7, v6" */
+ vdst = INST_A(inst);
+ vsrc1 = INST_B(inst);
+ ILOGV("|move-wide v%d,v%d %s(v%d=0x%08llx)", vdst, vsrc1,
+ kSpacing+5, vdst, GET_REGISTER_WIDE(vsrc1));
+ SET_REGISTER_WIDE(vdst, GET_REGISTER_WIDE(vsrc1));
+ FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_MOVE_WIDE_16.c b/vm/mterp/c/OP_MOVE_WIDE_16.c
new file mode 100644
index 0000000..e3d0e16
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_WIDE_16.c
@@ -0,0 +1,8 @@
+HANDLE_OPCODE(OP_MOVE_WIDE_16 /*vAAAA, vBBBB*/)
+ vdst = FETCH(1);
+ vsrc1 = FETCH(2);
+ ILOGV("|move-wide/16 v%d,v%d %s(v%d=0x%08llx)", vdst, vsrc1,
+ kSpacing+8, vdst, GET_REGISTER_WIDE(vsrc1));
+ SET_REGISTER_WIDE(vdst, GET_REGISTER_WIDE(vsrc1));
+ FINISH(3);
+OP_END
diff --git a/vm/mterp/c/OP_MOVE_WIDE_FROM16.c b/vm/mterp/c/OP_MOVE_WIDE_FROM16.c
new file mode 100644
index 0000000..cdbaa2e
--- /dev/null
+++ b/vm/mterp/c/OP_MOVE_WIDE_FROM16.c
@@ -0,0 +1,8 @@
+HANDLE_OPCODE(OP_MOVE_WIDE_FROM16 /*vAA, vBBBB*/)
+ vdst = INST_AA(inst);
+ vsrc1 = FETCH(1);
+ ILOGV("|move-wide/from16 v%d,v%d (v%d=0x%08llx)", vdst, vsrc1,
+ vdst, GET_REGISTER_WIDE(vsrc1));
+ SET_REGISTER_WIDE(vdst, GET_REGISTER_WIDE(vsrc1));
+ FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_MUL_DOUBLE.c b/vm/mterp/c/OP_MUL_DOUBLE.c
new file mode 100644
index 0000000..3e65efa
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_DOUBLE.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_DOUBLE(OP_MUL_DOUBLE, "mul", *)
+OP_END
diff --git a/vm/mterp/c/OP_MUL_DOUBLE_2ADDR.c b/vm/mterp/c/OP_MUL_DOUBLE_2ADDR.c
new file mode 100644
index 0000000..905b6a7
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_DOUBLE_2ADDR.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_DOUBLE_2ADDR(OP_MUL_DOUBLE_2ADDR, "mul", *)
+OP_END
diff --git a/vm/mterp/c/OP_MUL_FLOAT.c b/vm/mterp/c/OP_MUL_FLOAT.c
new file mode 100644
index 0000000..310495c
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_FLOAT.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_FLOAT(OP_MUL_FLOAT, "mul", *)
+OP_END
diff --git a/vm/mterp/c/OP_MUL_FLOAT_2ADDR.c b/vm/mterp/c/OP_MUL_FLOAT_2ADDR.c
new file mode 100644
index 0000000..03623ca
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_FLOAT_2ADDR.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_FLOAT_2ADDR(OP_MUL_FLOAT_2ADDR, "mul", *)
+OP_END
diff --git a/vm/mterp/c/OP_MUL_INT.c b/vm/mterp/c/OP_MUL_INT.c
new file mode 100644
index 0000000..b723a29
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_INT.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT(OP_MUL_INT, "mul", *, 0)
+OP_END
diff --git a/vm/mterp/c/OP_MUL_INT_2ADDR.c b/vm/mterp/c/OP_MUL_INT_2ADDR.c
new file mode 100644
index 0000000..f7a179c
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_INT_2ADDR.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_2ADDR(OP_MUL_INT_2ADDR, "mul", *, 0)
+OP_END
diff --git a/vm/mterp/c/OP_MUL_INT_LIT16.c b/vm/mterp/c/OP_MUL_INT_LIT16.c
new file mode 100644
index 0000000..3a34dbc
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_INT_LIT16.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT16(OP_MUL_INT_LIT16, "mul", *, 0)
+OP_END
diff --git a/vm/mterp/c/OP_MUL_INT_LIT8.c b/vm/mterp/c/OP_MUL_INT_LIT8.c
new file mode 100644
index 0000000..2ca0036
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_INT_LIT8.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT8(OP_MUL_INT_LIT8, "mul", *, 0)
+OP_END
diff --git a/vm/mterp/c/OP_MUL_LONG.c b/vm/mterp/c/OP_MUL_LONG.c
new file mode 100644
index 0000000..768a2ad
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_LONG.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG(OP_MUL_LONG, "mul", *, 0)
+OP_END
diff --git a/vm/mterp/c/OP_MUL_LONG_2ADDR.c b/vm/mterp/c/OP_MUL_LONG_2ADDR.c
new file mode 100644
index 0000000..1469a22
--- /dev/null
+++ b/vm/mterp/c/OP_MUL_LONG_2ADDR.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG_2ADDR(OP_MUL_LONG_2ADDR, "mul", *, 0)
+OP_END
diff --git a/vm/mterp/c/OP_NEG_DOUBLE.c b/vm/mterp/c/OP_NEG_DOUBLE.c
new file mode 100644
index 0000000..805082c
--- /dev/null
+++ b/vm/mterp/c/OP_NEG_DOUBLE.c
@@ -0,0 +1,2 @@
+HANDLE_UNOP(OP_NEG_DOUBLE, "neg-double", -, , _DOUBLE)
+OP_END
diff --git a/vm/mterp/c/OP_NEG_FLOAT.c b/vm/mterp/c/OP_NEG_FLOAT.c
new file mode 100644
index 0000000..00e14f5
--- /dev/null
+++ b/vm/mterp/c/OP_NEG_FLOAT.c
@@ -0,0 +1,2 @@
+HANDLE_UNOP(OP_NEG_FLOAT, "neg-float", -, , _FLOAT)
+OP_END
diff --git a/vm/mterp/c/OP_NEG_INT.c b/vm/mterp/c/OP_NEG_INT.c
new file mode 100644
index 0000000..9b97bef
--- /dev/null
+++ b/vm/mterp/c/OP_NEG_INT.c
@@ -0,0 +1,2 @@
+HANDLE_UNOP(OP_NEG_INT, "neg-int", -, , )
+OP_END
diff --git a/vm/mterp/c/OP_NEG_LONG.c b/vm/mterp/c/OP_NEG_LONG.c
new file mode 100644
index 0000000..52d553a
--- /dev/null
+++ b/vm/mterp/c/OP_NEG_LONG.c
@@ -0,0 +1,2 @@
+HANDLE_UNOP(OP_NEG_LONG, "neg-long", -, , _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_NEW_ARRAY.c b/vm/mterp/c/OP_NEW_ARRAY.c
new file mode 100644
index 0000000..525c43b
--- /dev/null
+++ b/vm/mterp/c/OP_NEW_ARRAY.c
@@ -0,0 +1,35 @@
+HANDLE_OPCODE(OP_NEW_ARRAY /*vA, vB, class@CCCC*/)
+ {
+ ClassObject* arrayClass;
+ ArrayObject* newArray;
+ s4 length;
+
+ EXPORT_PC();
+
+ vdst = INST_A(inst);
+ vsrc1 = INST_B(inst); /* length reg */
+ ref = FETCH(1);
+ ILOGV("|new-array v%d,v%d,class@0x%04x (%d elements)",
+ vdst, vsrc1, ref, (s4) GET_REGISTER(vsrc1));
+ length = (s4) GET_REGISTER(vsrc1);
+ if (length < 0) {
+ dvmThrowException("Ljava/lang/NegativeArraySizeException;", NULL);
+ GOTO_exceptionThrown();
+ }
+ arrayClass = dvmDexGetResolvedClass(methodClassDex, ref);
+ if (arrayClass == NULL) {
+ arrayClass = dvmResolveClass(curMethod->clazz, ref, false);
+ if (arrayClass == NULL)
+ GOTO_exceptionThrown();
+ }
+ /* verifier guarantees this is an array class */
+ assert(dvmIsArrayClass(arrayClass));
+ assert(dvmIsClassInitialized(arrayClass));
+
+ newArray = dvmAllocArrayByClass(arrayClass, length, ALLOC_DONT_TRACK);
+ if (newArray == NULL)
+ GOTO_exceptionThrown();
+ SET_REGISTER(vdst, (u4) newArray);
+ }
+ FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_NEW_INSTANCE.c b/vm/mterp/c/OP_NEW_INSTANCE.c
new file mode 100644
index 0000000..f7d4c64
--- /dev/null
+++ b/vm/mterp/c/OP_NEW_INSTANCE.c
@@ -0,0 +1,45 @@
+HANDLE_OPCODE(OP_NEW_INSTANCE /*vAA, class@BBBB*/)
+ {
+ ClassObject* clazz;
+ Object* newObj;
+
+ EXPORT_PC();
+
+ vdst = INST_AA(inst);
+ ref = FETCH(1);
+ ILOGV("|new-instance v%d,class@0x%04x", vdst, ref);
+ clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+ if (clazz == NULL) {
+ clazz = dvmResolveClass(curMethod->clazz, ref, false);
+ if (clazz == NULL)
+ GOTO_exceptionThrown();
+ }
+
+ if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))
+ GOTO_exceptionThrown();
+
+ /*
+ * The JIT needs dvmDexGetResolvedClass() to return non-null.
+ * Since we use the portable interpreter to build the trace, this extra
+ * check is not needed for mterp.
+ */
+ if (!dvmDexGetResolvedClass(methodClassDex, ref)) {
+ /* Class initialization is still ongoing - abandon the trace */
+ ABORT_JIT_TSELECT();
+ }
+
+ /*
+ * Verifier now tests for interface/abstract class.
+ */
+ //if (dvmIsInterfaceClass(clazz) || dvmIsAbstractClass(clazz)) {
+ // dvmThrowExceptionWithClassMessage("Ljava/lang/InstantiationError;",
+ // clazz->descriptor);
+ // GOTO_exceptionThrown();
+ //}
+ newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+ if (newObj == NULL)
+ GOTO_exceptionThrown();
+ SET_REGISTER(vdst, (u4) newObj);
+ }
+ FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_NOP.c b/vm/mterp/c/OP_NOP.c
new file mode 100644
index 0000000..d9fd744
--- /dev/null
+++ b/vm/mterp/c/OP_NOP.c
@@ -0,0 +1,3 @@
+HANDLE_OPCODE(OP_NOP)
+ FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_NOT_INT.c b/vm/mterp/c/OP_NOT_INT.c
new file mode 100644
index 0000000..e585f62
--- /dev/null
+++ b/vm/mterp/c/OP_NOT_INT.c
@@ -0,0 +1,2 @@
+HANDLE_UNOP(OP_NOT_INT, "not-int", , ^ 0xffffffff, )
+OP_END
diff --git a/vm/mterp/c/OP_NOT_LONG.c b/vm/mterp/c/OP_NOT_LONG.c
new file mode 100644
index 0000000..4fb393a
--- /dev/null
+++ b/vm/mterp/c/OP_NOT_LONG.c
@@ -0,0 +1,2 @@
+HANDLE_UNOP(OP_NOT_LONG, "not-long", , ^ 0xffffffffffffffffULL, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_OR_INT.c b/vm/mterp/c/OP_OR_INT.c
new file mode 100644
index 0000000..19e397b
--- /dev/null
+++ b/vm/mterp/c/OP_OR_INT.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT(OP_OR_INT, "or", |, 0)
+OP_END
diff --git a/vm/mterp/c/OP_OR_INT_2ADDR.c b/vm/mterp/c/OP_OR_INT_2ADDR.c
new file mode 100644
index 0000000..5d5fde0
--- /dev/null
+++ b/vm/mterp/c/OP_OR_INT_2ADDR.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_2ADDR(OP_OR_INT_2ADDR, "or", |, 0)
+OP_END
diff --git a/vm/mterp/c/OP_OR_INT_LIT16.c b/vm/mterp/c/OP_OR_INT_LIT16.c
new file mode 100644
index 0000000..5a682fa
--- /dev/null
+++ b/vm/mterp/c/OP_OR_INT_LIT16.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT16(OP_OR_INT_LIT16, "or", |, 0)
+OP_END
diff --git a/vm/mterp/c/OP_OR_INT_LIT8.c b/vm/mterp/c/OP_OR_INT_LIT8.c
new file mode 100644
index 0000000..40b9837
--- /dev/null
+++ b/vm/mterp/c/OP_OR_INT_LIT8.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT8(OP_OR_INT_LIT8, "or", |, 0)
+OP_END
diff --git a/vm/mterp/c/OP_OR_LONG.c b/vm/mterp/c/OP_OR_LONG.c
new file mode 100644
index 0000000..62f4dd3
--- /dev/null
+++ b/vm/mterp/c/OP_OR_LONG.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG(OP_OR_LONG, "or", |, 0)
+OP_END
diff --git a/vm/mterp/c/OP_OR_LONG_2ADDR.c b/vm/mterp/c/OP_OR_LONG_2ADDR.c
new file mode 100644
index 0000000..03a7c65
--- /dev/null
+++ b/vm/mterp/c/OP_OR_LONG_2ADDR.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG_2ADDR(OP_OR_LONG_2ADDR, "or", |, 0)
+OP_END
diff --git a/vm/mterp/c/OP_PACKED_SWITCH.c b/vm/mterp/c/OP_PACKED_SWITCH.c
new file mode 100644
index 0000000..d0986dc
--- /dev/null
+++ b/vm/mterp/c/OP_PACKED_SWITCH.c
@@ -0,0 +1,29 @@
+HANDLE_OPCODE(OP_PACKED_SWITCH /*vAA, +BBBB*/)
+ {
+ const u2* switchData;
+ u4 testVal;
+ s4 offset;
+
+ vsrc1 = INST_AA(inst);
+ offset = FETCH(1) | (((s4) FETCH(2)) << 16);
+ ILOGV("|packed-switch v%d +0x%04x", vsrc1, vsrc2);
+ switchData = pc + offset; // offset in 16-bit units
+#ifndef NDEBUG
+ if (switchData < curMethod->insns ||
+ switchData >= curMethod->insns + dvmGetMethodInsnsSize(curMethod))
+ {
+ /* should have been caught in verifier */
+ EXPORT_PC();
+ dvmThrowException("Ljava/lang/InternalError;", "bad packed switch");
+ GOTO_exceptionThrown();
+ }
+#endif
+ testVal = GET_REGISTER(vsrc1);
+
+ offset = dvmInterpHandlePackedSwitch(switchData, testVal);
+ ILOGV("> branch taken (0x%04x)\n", offset);
+ if (offset <= 0) /* uncommon */
+ PERIODIC_CHECKS(kInterpEntryInstr, offset);
+ FINISH(offset);
+ }
+OP_END
diff --git a/vm/mterp/c/OP_REM_DOUBLE.c b/vm/mterp/c/OP_REM_DOUBLE.c
new file mode 100644
index 0000000..343e25e
--- /dev/null
+++ b/vm/mterp/c/OP_REM_DOUBLE.c
@@ -0,0 +1,13 @@
+HANDLE_OPCODE(OP_REM_DOUBLE /*vAA, vBB, vCC*/)
+ {
+ u2 srcRegs;
+ vdst = INST_AA(inst);
+ srcRegs = FETCH(1);
+ vsrc1 = srcRegs & 0xff;
+ vsrc2 = srcRegs >> 8;
+ ILOGV("|%s-double v%d,v%d,v%d", "mod", vdst, vsrc1, vsrc2);
+ SET_REGISTER_DOUBLE(vdst,
+ fmod(GET_REGISTER_DOUBLE(vsrc1), GET_REGISTER_DOUBLE(vsrc2)));
+ }
+ FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_REM_DOUBLE_2ADDR.c b/vm/mterp/c/OP_REM_DOUBLE_2ADDR.c
new file mode 100644
index 0000000..392eacf
--- /dev/null
+++ b/vm/mterp/c/OP_REM_DOUBLE_2ADDR.c
@@ -0,0 +1,8 @@
+HANDLE_OPCODE(OP_REM_DOUBLE_2ADDR /*vA, vB*/)
+ vdst = INST_A(inst);
+ vsrc1 = INST_B(inst);
+ ILOGV("|%s-double-2addr v%d,v%d", "mod", vdst, vsrc1);
+ SET_REGISTER_DOUBLE(vdst,
+ fmod(GET_REGISTER_DOUBLE(vdst), GET_REGISTER_DOUBLE(vsrc1)));
+ FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_REM_FLOAT.c b/vm/mterp/c/OP_REM_FLOAT.c
new file mode 100644
index 0000000..9604b30
--- /dev/null
+++ b/vm/mterp/c/OP_REM_FLOAT.c
@@ -0,0 +1,13 @@
+HANDLE_OPCODE(OP_REM_FLOAT /*vAA, vBB, vCC*/)
+ {
+ u2 srcRegs;
+ vdst = INST_AA(inst);
+ srcRegs = FETCH(1);
+ vsrc1 = srcRegs & 0xff;
+ vsrc2 = srcRegs >> 8;
+ ILOGV("|%s-float v%d,v%d,v%d", "mod", vdst, vsrc1, vsrc2);
+ SET_REGISTER_FLOAT(vdst,
+ fmodf(GET_REGISTER_FLOAT(vsrc1), GET_REGISTER_FLOAT(vsrc2)));
+ }
+ FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_REM_FLOAT_2ADDR.c b/vm/mterp/c/OP_REM_FLOAT_2ADDR.c
new file mode 100644
index 0000000..87bb31e
--- /dev/null
+++ b/vm/mterp/c/OP_REM_FLOAT_2ADDR.c
@@ -0,0 +1,8 @@
+HANDLE_OPCODE(OP_REM_FLOAT_2ADDR /*vA, vB*/)
+ vdst = INST_A(inst);
+ vsrc1 = INST_B(inst);
+ ILOGV("|%s-float-2addr v%d,v%d", "mod", vdst, vsrc1);
+ SET_REGISTER_FLOAT(vdst,
+ fmodf(GET_REGISTER_FLOAT(vdst), GET_REGISTER_FLOAT(vsrc1)));
+ FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_REM_INT.c b/vm/mterp/c/OP_REM_INT.c
new file mode 100644
index 0000000..0e3efe6
--- /dev/null
+++ b/vm/mterp/c/OP_REM_INT.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT(OP_REM_INT, "rem", %, 2)
+OP_END
diff --git a/vm/mterp/c/OP_REM_INT_2ADDR.c b/vm/mterp/c/OP_REM_INT_2ADDR.c
new file mode 100644
index 0000000..5801f35
--- /dev/null
+++ b/vm/mterp/c/OP_REM_INT_2ADDR.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_2ADDR(OP_REM_INT_2ADDR, "rem", %, 2)
+OP_END
diff --git a/vm/mterp/c/OP_REM_INT_LIT16.c b/vm/mterp/c/OP_REM_INT_LIT16.c
new file mode 100644
index 0000000..a4dabe8
--- /dev/null
+++ b/vm/mterp/c/OP_REM_INT_LIT16.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT16(OP_REM_INT_LIT16, "rem", %, 2)
+OP_END
diff --git a/vm/mterp/c/OP_REM_INT_LIT8.c b/vm/mterp/c/OP_REM_INT_LIT8.c
new file mode 100644
index 0000000..2bc88be
--- /dev/null
+++ b/vm/mterp/c/OP_REM_INT_LIT8.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT8(OP_REM_INT_LIT8, "rem", %, 2)
+OP_END
diff --git a/vm/mterp/c/OP_REM_LONG.c b/vm/mterp/c/OP_REM_LONG.c
new file mode 100644
index 0000000..fb2ba71
--- /dev/null
+++ b/vm/mterp/c/OP_REM_LONG.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG(OP_REM_LONG, "rem", %, 2)
+OP_END
diff --git a/vm/mterp/c/OP_REM_LONG_2ADDR.c b/vm/mterp/c/OP_REM_LONG_2ADDR.c
new file mode 100644
index 0000000..3049770
--- /dev/null
+++ b/vm/mterp/c/OP_REM_LONG_2ADDR.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG_2ADDR(OP_REM_LONG_2ADDR, "rem", %, 2)
+OP_END
diff --git a/vm/mterp/c/OP_RETURN.c b/vm/mterp/c/OP_RETURN.c
new file mode 100644
index 0000000..89d3b3b
--- /dev/null
+++ b/vm/mterp/c/OP_RETURN.c
@@ -0,0 +1,7 @@
+HANDLE_OPCODE($opcode /*vAA*/)
+ vsrc1 = INST_AA(inst);
+ ILOGV("|return%s v%d",
+ (INST_INST(inst) == OP_RETURN) ? "" : "-object", vsrc1);
+ retval.i = GET_REGISTER(vsrc1);
+ GOTO_returnFromMethod();
+OP_END
diff --git a/vm/mterp/c/OP_RETURN_OBJECT.c b/vm/mterp/c/OP_RETURN_OBJECT.c
new file mode 100644
index 0000000..87cca39
--- /dev/null
+++ b/vm/mterp/c/OP_RETURN_OBJECT.c
@@ -0,0 +1 @@
+%include "c/OP_RETURN.c"
diff --git a/vm/mterp/c/OP_RETURN_VOID.c b/vm/mterp/c/OP_RETURN_VOID.c
new file mode 100644
index 0000000..7431f60
--- /dev/null
+++ b/vm/mterp/c/OP_RETURN_VOID.c
@@ -0,0 +1,7 @@
+HANDLE_OPCODE(OP_RETURN_VOID /**/)
+ ILOGV("|return-void");
+#ifndef NDEBUG
+ retval.j = 0xababababULL; // placate valgrind
+#endif
+ GOTO_returnFromMethod();
+OP_END
diff --git a/vm/mterp/c/OP_RETURN_WIDE.c b/vm/mterp/c/OP_RETURN_WIDE.c
new file mode 100644
index 0000000..a27bfd4
--- /dev/null
+++ b/vm/mterp/c/OP_RETURN_WIDE.c
@@ -0,0 +1,6 @@
+HANDLE_OPCODE(OP_RETURN_WIDE /*vAA*/)
+ vsrc1 = INST_AA(inst);
+ ILOGV("|return-wide v%d", vsrc1);
+ retval.j = GET_REGISTER_WIDE(vsrc1);
+ GOTO_returnFromMethod();
+OP_END
diff --git a/vm/mterp/c/OP_RSUB_INT.c b/vm/mterp/c/OP_RSUB_INT.c
new file mode 100644
index 0000000..336ca55
--- /dev/null
+++ b/vm/mterp/c/OP_RSUB_INT.c
@@ -0,0 +1,10 @@
+HANDLE_OPCODE(OP_RSUB_INT /*vA, vB, #+CCCC*/)
+ {
+ vdst = INST_A(inst);
+ vsrc1 = INST_B(inst);
+ vsrc2 = FETCH(1);
+ ILOGV("|rsub-int v%d,v%d,#+0x%04x", vdst, vsrc1, vsrc2);
+ SET_REGISTER(vdst, (s2) vsrc2 - (s4) GET_REGISTER(vsrc1));
+ }
+ FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_RSUB_INT_LIT8.c b/vm/mterp/c/OP_RSUB_INT_LIT8.c
new file mode 100644
index 0000000..742854b
--- /dev/null
+++ b/vm/mterp/c/OP_RSUB_INT_LIT8.c
@@ -0,0 +1,12 @@
+HANDLE_OPCODE(OP_RSUB_INT_LIT8 /*vAA, vBB, #+CC*/)
+ {
+ u2 litInfo;
+ vdst = INST_AA(inst);
+ litInfo = FETCH(1);
+ vsrc1 = litInfo & 0xff;
+ vsrc2 = litInfo >> 8;
+ ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x", "rsub", vdst, vsrc1, vsrc2);
+ SET_REGISTER(vdst, (s1) vsrc2 - (s4) GET_REGISTER(vsrc1));
+ }
+ FINISH(2);
+OP_END
diff --git a/vm/mterp/c/OP_SGET.c b/vm/mterp/c/OP_SGET.c
new file mode 100644
index 0000000..5297cd7
--- /dev/null
+++ b/vm/mterp/c/OP_SGET.c
@@ -0,0 +1,2 @@
+HANDLE_SGET_X(OP_SGET, "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SGET_BOOLEAN.c b/vm/mterp/c/OP_SGET_BOOLEAN.c
new file mode 100644
index 0000000..7c5d45e
--- /dev/null
+++ b/vm/mterp/c/OP_SGET_BOOLEAN.c
@@ -0,0 +1,2 @@
+HANDLE_SGET_X(OP_SGET_BOOLEAN, "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SGET_BYTE.c b/vm/mterp/c/OP_SGET_BYTE.c
new file mode 100644
index 0000000..b37cab4
--- /dev/null
+++ b/vm/mterp/c/OP_SGET_BYTE.c
@@ -0,0 +1,2 @@
+HANDLE_SGET_X(OP_SGET_BYTE, "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SGET_CHAR.c b/vm/mterp/c/OP_SGET_CHAR.c
new file mode 100644
index 0000000..7ede5ec
--- /dev/null
+++ b/vm/mterp/c/OP_SGET_CHAR.c
@@ -0,0 +1,2 @@
+HANDLE_SGET_X(OP_SGET_CHAR, "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SGET_OBJECT.c b/vm/mterp/c/OP_SGET_OBJECT.c
new file mode 100644
index 0000000..9f3b63d
--- /dev/null
+++ b/vm/mterp/c/OP_SGET_OBJECT.c
@@ -0,0 +1,2 @@
+HANDLE_SGET_X(OP_SGET_OBJECT, "-object", Object, _AS_OBJECT)
+OP_END
diff --git a/vm/mterp/c/OP_SGET_OBJECT_VOLATILE.c b/vm/mterp/c/OP_SGET_OBJECT_VOLATILE.c
new file mode 100644
index 0000000..0a9049f
--- /dev/null
+++ b/vm/mterp/c/OP_SGET_OBJECT_VOLATILE.c
@@ -0,0 +1,2 @@
+HANDLE_SGET_X(OP_SGET_OBJECT_VOLATILE, "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
diff --git a/vm/mterp/c/OP_SGET_SHORT.c b/vm/mterp/c/OP_SGET_SHORT.c
new file mode 100644
index 0000000..cd1fe4c
--- /dev/null
+++ b/vm/mterp/c/OP_SGET_SHORT.c
@@ -0,0 +1,2 @@
+HANDLE_SGET_X(OP_SGET_SHORT, "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SGET_VOLATILE.c b/vm/mterp/c/OP_SGET_VOLATILE.c
new file mode 100644
index 0000000..6713a54
--- /dev/null
+++ b/vm/mterp/c/OP_SGET_VOLATILE.c
@@ -0,0 +1,2 @@
+HANDLE_SGET_X(OP_SGET_VOLATILE, "-volatile", IntVolatile, )
+OP_END
diff --git a/vm/mterp/c/OP_SGET_WIDE.c b/vm/mterp/c/OP_SGET_WIDE.c
new file mode 100644
index 0000000..817c6e7
--- /dev/null
+++ b/vm/mterp/c/OP_SGET_WIDE.c
@@ -0,0 +1,2 @@
+HANDLE_SGET_X(OP_SGET_WIDE, "-wide", Long, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_SGET_WIDE_VOLATILE.c b/vm/mterp/c/OP_SGET_WIDE_VOLATILE.c
new file mode 100644
index 0000000..26a67bf
--- /dev/null
+++ b/vm/mterp/c/OP_SGET_WIDE_VOLATILE.c
@@ -0,0 +1,2 @@
+HANDLE_SGET_X(OP_SGET_WIDE_VOLATILE, "-wide-volatile", LongVolatile, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_SHL_INT.c b/vm/mterp/c/OP_SHL_INT.c
new file mode 100644
index 0000000..e32af49
--- /dev/null
+++ b/vm/mterp/c/OP_SHL_INT.c
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_INT(OP_SHL_INT, "shl", (s4), <<)
+OP_END
diff --git a/vm/mterp/c/OP_SHL_INT_2ADDR.c b/vm/mterp/c/OP_SHL_INT_2ADDR.c
new file mode 100644
index 0000000..c5f5399
--- /dev/null
+++ b/vm/mterp/c/OP_SHL_INT_2ADDR.c
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_INT_2ADDR(OP_SHL_INT_2ADDR, "shl", (s4), <<)
+OP_END
diff --git a/vm/mterp/c/OP_SHL_INT_LIT8.c b/vm/mterp/c/OP_SHL_INT_LIT8.c
new file mode 100644
index 0000000..009d14e
--- /dev/null
+++ b/vm/mterp/c/OP_SHL_INT_LIT8.c
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_INT_LIT8(OP_SHL_INT_LIT8, "shl", (s4), <<)
+OP_END
diff --git a/vm/mterp/c/OP_SHL_LONG.c b/vm/mterp/c/OP_SHL_LONG.c
new file mode 100644
index 0000000..f6b502a
--- /dev/null
+++ b/vm/mterp/c/OP_SHL_LONG.c
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_LONG(OP_SHL_LONG, "shl", (s8), <<)
+OP_END
diff --git a/vm/mterp/c/OP_SHL_LONG_2ADDR.c b/vm/mterp/c/OP_SHL_LONG_2ADDR.c
new file mode 100644
index 0000000..b8a9954
--- /dev/null
+++ b/vm/mterp/c/OP_SHL_LONG_2ADDR.c
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_LONG_2ADDR(OP_SHL_LONG_2ADDR, "shl", (s8), <<)
+OP_END
diff --git a/vm/mterp/c/OP_SHR_INT.c b/vm/mterp/c/OP_SHR_INT.c
new file mode 100644
index 0000000..3834824
--- /dev/null
+++ b/vm/mterp/c/OP_SHR_INT.c
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_INT(OP_SHR_INT, "shr", (s4), >>)
+OP_END
diff --git a/vm/mterp/c/OP_SHR_INT_2ADDR.c b/vm/mterp/c/OP_SHR_INT_2ADDR.c
new file mode 100644
index 0000000..c76c178
--- /dev/null
+++ b/vm/mterp/c/OP_SHR_INT_2ADDR.c
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_INT_2ADDR(OP_SHR_INT_2ADDR, "shr", (s4), >>)
+OP_END
diff --git a/vm/mterp/c/OP_SHR_INT_LIT8.c b/vm/mterp/c/OP_SHR_INT_LIT8.c
new file mode 100644
index 0000000..e2657d7
--- /dev/null
+++ b/vm/mterp/c/OP_SHR_INT_LIT8.c
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_INT_LIT8(OP_SHR_INT_LIT8, "shr", (s4), >>)
+OP_END
diff --git a/vm/mterp/c/OP_SHR_LONG.c b/vm/mterp/c/OP_SHR_LONG.c
new file mode 100644
index 0000000..357a666
--- /dev/null
+++ b/vm/mterp/c/OP_SHR_LONG.c
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_LONG(OP_SHR_LONG, "shr", (s8), >>)
+OP_END
diff --git a/vm/mterp/c/OP_SHR_LONG_2ADDR.c b/vm/mterp/c/OP_SHR_LONG_2ADDR.c
new file mode 100644
index 0000000..43e27ea
--- /dev/null
+++ b/vm/mterp/c/OP_SHR_LONG_2ADDR.c
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_LONG_2ADDR(OP_SHR_LONG_2ADDR, "shr", (s8), >>)
+OP_END
diff --git a/vm/mterp/c/OP_SPARSE_SWITCH.c b/vm/mterp/c/OP_SPARSE_SWITCH.c
new file mode 100644
index 0000000..7f3648a
--- /dev/null
+++ b/vm/mterp/c/OP_SPARSE_SWITCH.c
@@ -0,0 +1,29 @@
+HANDLE_OPCODE(OP_SPARSE_SWITCH /*vAA, +BBBB*/)
+ {
+ const u2* switchData;
+ u4 testVal;
+ s4 offset;
+
+ vsrc1 = INST_AA(inst);
+ offset = FETCH(1) | (((s4) FETCH(2)) << 16);
+ ILOGV("|sparse-switch v%d +0x%04x", vsrc1, vsrc2);
+ switchData = pc + offset; // offset in 16-bit units
+#ifndef NDEBUG
+ if (switchData < curMethod->insns ||
+ switchData >= curMethod->insns + dvmGetMethodInsnsSize(curMethod))
+ {
+ /* should have been caught in verifier */
+ EXPORT_PC();
+ dvmThrowException("Ljava/lang/InternalError;", "bad sparse switch");
+ GOTO_exceptionThrown();
+ }
+#endif
+ testVal = GET_REGISTER(vsrc1);
+
+ offset = dvmInterpHandleSparseSwitch(switchData, testVal);
+ ILOGV("> branch taken (0x%04x)\n", offset);
+ if (offset <= 0) /* uncommon */
+ PERIODIC_CHECKS(kInterpEntryInstr, offset);
+ FINISH(offset);
+ }
+OP_END
diff --git a/vm/mterp/c/OP_SPUT.c b/vm/mterp/c/OP_SPUT.c
new file mode 100644
index 0000000..286e64c
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT.c
@@ -0,0 +1,2 @@
+HANDLE_SPUT_X(OP_SPUT, "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SPUT_BOOLEAN.c b/vm/mterp/c/OP_SPUT_BOOLEAN.c
new file mode 100644
index 0000000..55ceb11
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT_BOOLEAN.c
@@ -0,0 +1,2 @@
+HANDLE_SPUT_X(OP_SPUT_BOOLEAN, "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SPUT_BYTE.c b/vm/mterp/c/OP_SPUT_BYTE.c
new file mode 100644
index 0000000..d242fe1
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT_BYTE.c
@@ -0,0 +1,2 @@
+HANDLE_SPUT_X(OP_SPUT_BYTE, "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SPUT_CHAR.c b/vm/mterp/c/OP_SPUT_CHAR.c
new file mode 100644
index 0000000..18a2f06
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT_CHAR.c
@@ -0,0 +1,2 @@
+HANDLE_SPUT_X(OP_SPUT_CHAR, "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SPUT_OBJECT.c b/vm/mterp/c/OP_SPUT_OBJECT.c
new file mode 100644
index 0000000..fb223d6
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT_OBJECT.c
@@ -0,0 +1,2 @@
+HANDLE_SPUT_X(OP_SPUT_OBJECT, "-object", Object, _AS_OBJECT)
+OP_END
diff --git a/vm/mterp/c/OP_SPUT_OBJECT_VOLATILE.c b/vm/mterp/c/OP_SPUT_OBJECT_VOLATILE.c
new file mode 100644
index 0000000..38d6c0d
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT_OBJECT_VOLATILE.c
@@ -0,0 +1,2 @@
+HANDLE_SPUT_X(OP_SPUT_OBJECT_VOLATILE, "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
diff --git a/vm/mterp/c/OP_SPUT_SHORT.c b/vm/mterp/c/OP_SPUT_SHORT.c
new file mode 100644
index 0000000..c6cd8d6
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT_SHORT.c
@@ -0,0 +1,2 @@
+HANDLE_SPUT_X(OP_SPUT_SHORT, "", Int, )
+OP_END
diff --git a/vm/mterp/c/OP_SPUT_VOLATILE.c b/vm/mterp/c/OP_SPUT_VOLATILE.c
new file mode 100644
index 0000000..7899d05
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT_VOLATILE.c
@@ -0,0 +1,2 @@
+HANDLE_SPUT_X(OP_SPUT_VOLATILE, "-volatile", IntVolatile, )
+OP_END
diff --git a/vm/mterp/c/OP_SPUT_WIDE.c b/vm/mterp/c/OP_SPUT_WIDE.c
new file mode 100644
index 0000000..0c74651
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT_WIDE.c
@@ -0,0 +1,2 @@
+HANDLE_SPUT_X(OP_SPUT_WIDE, "-wide", Long, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_SPUT_WIDE_VOLATILE.c b/vm/mterp/c/OP_SPUT_WIDE_VOLATILE.c
new file mode 100644
index 0000000..bdf552c
--- /dev/null
+++ b/vm/mterp/c/OP_SPUT_WIDE_VOLATILE.c
@@ -0,0 +1,2 @@
+HANDLE_SPUT_X(OP_SPUT_WIDE_VOLATILE, "-wide-volatile", LongVolatile, _WIDE)
+OP_END
diff --git a/vm/mterp/c/OP_SUB_DOUBLE.c b/vm/mterp/c/OP_SUB_DOUBLE.c
new file mode 100644
index 0000000..64a112d
--- /dev/null
+++ b/vm/mterp/c/OP_SUB_DOUBLE.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_DOUBLE(OP_SUB_DOUBLE, "sub", -)
+OP_END
diff --git a/vm/mterp/c/OP_SUB_DOUBLE_2ADDR.c b/vm/mterp/c/OP_SUB_DOUBLE_2ADDR.c
new file mode 100644
index 0000000..5870400
--- /dev/null
+++ b/vm/mterp/c/OP_SUB_DOUBLE_2ADDR.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_DOUBLE_2ADDR(OP_SUB_DOUBLE_2ADDR, "sub", -)
+OP_END
diff --git a/vm/mterp/c/OP_SUB_FLOAT.c b/vm/mterp/c/OP_SUB_FLOAT.c
new file mode 100644
index 0000000..96c5fbd
--- /dev/null
+++ b/vm/mterp/c/OP_SUB_FLOAT.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_FLOAT(OP_SUB_FLOAT, "sub", -)
+OP_END
diff --git a/vm/mterp/c/OP_SUB_FLOAT_2ADDR.c b/vm/mterp/c/OP_SUB_FLOAT_2ADDR.c
new file mode 100644
index 0000000..802935c
--- /dev/null
+++ b/vm/mterp/c/OP_SUB_FLOAT_2ADDR.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_FLOAT_2ADDR(OP_SUB_FLOAT_2ADDR, "sub", -)
+OP_END
diff --git a/vm/mterp/c/OP_SUB_INT.c b/vm/mterp/c/OP_SUB_INT.c
new file mode 100644
index 0000000..2c1006d
--- /dev/null
+++ b/vm/mterp/c/OP_SUB_INT.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT(OP_SUB_INT, "sub", -, 0)
+OP_END
diff --git a/vm/mterp/c/OP_SUB_INT_2ADDR.c b/vm/mterp/c/OP_SUB_INT_2ADDR.c
new file mode 100644
index 0000000..328ed33
--- /dev/null
+++ b/vm/mterp/c/OP_SUB_INT_2ADDR.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_2ADDR(OP_SUB_INT_2ADDR, "sub", -, 0)
+OP_END
diff --git a/vm/mterp/c/OP_SUB_LONG.c b/vm/mterp/c/OP_SUB_LONG.c
new file mode 100644
index 0000000..bace11a
--- /dev/null
+++ b/vm/mterp/c/OP_SUB_LONG.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG(OP_SUB_LONG, "sub", -, 0)
+OP_END
diff --git a/vm/mterp/c/OP_SUB_LONG_2ADDR.c b/vm/mterp/c/OP_SUB_LONG_2ADDR.c
new file mode 100644
index 0000000..f234dd4
--- /dev/null
+++ b/vm/mterp/c/OP_SUB_LONG_2ADDR.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG_2ADDR(OP_SUB_LONG_2ADDR, "sub", -, 0)
+OP_END
diff --git a/vm/mterp/c/OP_THROW.c b/vm/mterp/c/OP_THROW.c
new file mode 100644
index 0000000..0dcaced
--- /dev/null
+++ b/vm/mterp/c/OP_THROW.c
@@ -0,0 +1,24 @@
+HANDLE_OPCODE(OP_THROW /*vAA*/)
+ {
+ Object* obj;
+
+ /*
+ * We don't create an exception here, but the process of searching
+ * for a catch block can do class lookups and throw exceptions.
+ * We need to update the saved PC.
+ */
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst);
+ ILOGV("|throw v%d (%p)", vsrc1, (void*)GET_REGISTER(vsrc1));
+ obj = (Object*) GET_REGISTER(vsrc1);
+ if (!checkForNull(obj)) {
+ /* will throw a null pointer exception */
+ LOGVV("Bad exception\n");
+ } else {
+ /* use the requested exception */
+ dvmSetException(self, obj);
+ }
+ GOTO_exceptionThrown();
+ }
+OP_END
diff --git a/vm/mterp/c/OP_THROW_VERIFICATION_ERROR.c b/vm/mterp/c/OP_THROW_VERIFICATION_ERROR.c
new file mode 100644
index 0000000..85cc8fb
--- /dev/null
+++ b/vm/mterp/c/OP_THROW_VERIFICATION_ERROR.c
@@ -0,0 +1,7 @@
+HANDLE_OPCODE(OP_THROW_VERIFICATION_ERROR)
+ EXPORT_PC();
+ vsrc1 = INST_AA(inst);
+ ref = FETCH(1); /* class/field/method ref */
+ dvmThrowVerificationError(curMethod, vsrc1, ref);
+ GOTO_exceptionThrown();
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_3E.c b/vm/mterp/c/OP_UNUSED_3E.c
new file mode 100644
index 0000000..9ecf8e3
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_3E.c
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_3E)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_3F.c b/vm/mterp/c/OP_UNUSED_3F.c
new file mode 100644
index 0000000..9d1d68d
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_3F.c
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_3F)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_40.c b/vm/mterp/c/OP_UNUSED_40.c
new file mode 100644
index 0000000..f73a59c
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_40.c
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_40)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_41.c b/vm/mterp/c/OP_UNUSED_41.c
new file mode 100644
index 0000000..38747e6
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_41.c
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_41)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_42.c b/vm/mterp/c/OP_UNUSED_42.c
new file mode 100644
index 0000000..154d293
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_42.c
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_42)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_43.c b/vm/mterp/c/OP_UNUSED_43.c
new file mode 100644
index 0000000..c7e702c
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_43.c
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_43)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_73.c b/vm/mterp/c/OP_UNUSED_73.c
new file mode 100644
index 0000000..85aa95f
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_73.c
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_73)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_79.c b/vm/mterp/c/OP_UNUSED_79.c
new file mode 100644
index 0000000..1fa86e9
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_79.c
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_79)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_7A.c b/vm/mterp/c/OP_UNUSED_7A.c
new file mode 100644
index 0000000..beab006
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_7A.c
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_7A)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_F1.c b/vm/mterp/c/OP_UNUSED_F1.c
new file mode 100644
index 0000000..af26195
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_F1.c
@@ -0,0 +1,2 @@
+HANDLE_OPCODE(OP_UNUSED_F1)
+OP_END
diff --git a/vm/mterp/c/OP_UNUSED_FF.c b/vm/mterp/c/OP_UNUSED_FF.c
new file mode 100644
index 0000000..f4743db
--- /dev/null
+++ b/vm/mterp/c/OP_UNUSED_FF.c
@@ -0,0 +1,8 @@
+HANDLE_OPCODE(OP_UNUSED_FF)
+ /*
+ * In portable interp, most unused opcodes will fall through to here.
+ */
+ LOGE("unknown opcode 0x%02x\n", INST_INST(inst));
+ dvmAbort();
+ FINISH(1);
+OP_END
diff --git a/vm/mterp/c/OP_USHR_INT.c b/vm/mterp/c/OP_USHR_INT.c
new file mode 100644
index 0000000..7596c94
--- /dev/null
+++ b/vm/mterp/c/OP_USHR_INT.c
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_INT(OP_USHR_INT, "ushr", (u4), >>)
+OP_END
diff --git a/vm/mterp/c/OP_USHR_INT_2ADDR.c b/vm/mterp/c/OP_USHR_INT_2ADDR.c
new file mode 100644
index 0000000..5fa2b94
--- /dev/null
+++ b/vm/mterp/c/OP_USHR_INT_2ADDR.c
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_INT_2ADDR(OP_USHR_INT_2ADDR, "ushr", (u4), >>)
+OP_END
diff --git a/vm/mterp/c/OP_USHR_INT_LIT8.c b/vm/mterp/c/OP_USHR_INT_LIT8.c
new file mode 100644
index 0000000..0d325d7
--- /dev/null
+++ b/vm/mterp/c/OP_USHR_INT_LIT8.c
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_INT_LIT8(OP_USHR_INT_LIT8, "ushr", (u4), >>)
+OP_END
diff --git a/vm/mterp/c/OP_USHR_LONG.c b/vm/mterp/c/OP_USHR_LONG.c
new file mode 100644
index 0000000..9b7e757
--- /dev/null
+++ b/vm/mterp/c/OP_USHR_LONG.c
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_LONG(OP_USHR_LONG, "ushr", (u8), >>)
+OP_END
diff --git a/vm/mterp/c/OP_USHR_LONG_2ADDR.c b/vm/mterp/c/OP_USHR_LONG_2ADDR.c
new file mode 100644
index 0000000..4ac0598
--- /dev/null
+++ b/vm/mterp/c/OP_USHR_LONG_2ADDR.c
@@ -0,0 +1,2 @@
+HANDLE_OP_SHX_LONG_2ADDR(OP_USHR_LONG_2ADDR, "ushr", (u8), >>)
+OP_END
diff --git a/vm/mterp/c/OP_XOR_INT.c b/vm/mterp/c/OP_XOR_INT.c
new file mode 100644
index 0000000..d591909
--- /dev/null
+++ b/vm/mterp/c/OP_XOR_INT.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT(OP_XOR_INT, "xor", ^, 0)
+OP_END
diff --git a/vm/mterp/c/OP_XOR_INT_2ADDR.c b/vm/mterp/c/OP_XOR_INT_2ADDR.c
new file mode 100644
index 0000000..7d32879
--- /dev/null
+++ b/vm/mterp/c/OP_XOR_INT_2ADDR.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_2ADDR(OP_XOR_INT_2ADDR, "xor", ^, 0)
+OP_END
diff --git a/vm/mterp/c/OP_XOR_INT_LIT16.c b/vm/mterp/c/OP_XOR_INT_LIT16.c
new file mode 100644
index 0000000..c204b79
--- /dev/null
+++ b/vm/mterp/c/OP_XOR_INT_LIT16.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT16(OP_XOR_INT_LIT16, "xor", ^, 0)
+OP_END
diff --git a/vm/mterp/c/OP_XOR_INT_LIT8.c b/vm/mterp/c/OP_XOR_INT_LIT8.c
new file mode 100644
index 0000000..f01773a
--- /dev/null
+++ b/vm/mterp/c/OP_XOR_INT_LIT8.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_INT_LIT8(OP_XOR_INT_LIT8, "xor", ^, 0)
+OP_END
diff --git a/vm/mterp/c/OP_XOR_LONG.c b/vm/mterp/c/OP_XOR_LONG.c
new file mode 100644
index 0000000..d3dbc4c
--- /dev/null
+++ b/vm/mterp/c/OP_XOR_LONG.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG(OP_XOR_LONG, "xor", ^, 0)
+OP_END
diff --git a/vm/mterp/c/OP_XOR_LONG_2ADDR.c b/vm/mterp/c/OP_XOR_LONG_2ADDR.c
new file mode 100644
index 0000000..e7a50f4
--- /dev/null
+++ b/vm/mterp/c/OP_XOR_LONG_2ADDR.c
@@ -0,0 +1,2 @@
+HANDLE_OP_X_LONG_2ADDR(OP_XOR_LONG_2ADDR, "xor", ^, 0)
+OP_END
diff --git a/vm/mterp/c/gotoTargets.c b/vm/mterp/c/gotoTargets.c
new file mode 100644
index 0000000..0db6fb7
--- /dev/null
+++ b/vm/mterp/c/gotoTargets.c
@@ -0,0 +1,992 @@
+/*
+ * C footer. This has some common code shared by the various targets.
+ */
+
+/*
+ * Everything from here on is a "goto target". In the basic interpreter
+ * we jump into these targets and then jump directly to the handler for
+ * next instruction. Here, these are subroutines that return to the caller.
+ */
+
+GOTO_TARGET(filledNewArray, bool methodCallRange)
+ {
+ ClassObject* arrayClass;
+ ArrayObject* newArray;
+ u4* contents;
+ char typeCh;
+ int i;
+ u4 arg5;
+
+ EXPORT_PC();
+
+ ref = FETCH(1); /* class ref */
+ vdst = FETCH(2); /* first 4 regs -or- range base */
+
+ if (methodCallRange) {
+ vsrc1 = INST_AA(inst); /* #of elements */
+ arg5 = -1; /* silence compiler warning */
+ ILOGV("|filled-new-array-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ } else {
+ arg5 = INST_A(inst);
+ vsrc1 = INST_B(inst); /* #of elements */
+ ILOGV("|filled-new-array args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1, ref, vdst, arg5);
+ }
+
+ /*
+ * Resolve the array class.
+ */
+ arrayClass = dvmDexGetResolvedClass(methodClassDex, ref);
+ if (arrayClass == NULL) {
+ arrayClass = dvmResolveClass(curMethod->clazz, ref, false);
+ if (arrayClass == NULL)
+ GOTO_exceptionThrown();
+ }
+ /*
+ if (!dvmIsArrayClass(arrayClass)) {
+ dvmThrowException("Ljava/lang/RuntimeError;",
+ "filled-new-array needs array class");
+ GOTO_exceptionThrown();
+ }
+ */
+ /* verifier guarantees this is an array class */
+ assert(dvmIsArrayClass(arrayClass));
+ assert(dvmIsClassInitialized(arrayClass));
+
+ /*
+ * Create an array of the specified type.
+ */
+ LOGVV("+++ filled-new-array type is '%s'\n", arrayClass->descriptor);
+ typeCh = arrayClass->descriptor[1];
+ if (typeCh == 'D' || typeCh == 'J') {
+ /* category 2 primitives not allowed */
+ dvmThrowException("Ljava/lang/RuntimeError;",
+ "bad filled array req");
+ GOTO_exceptionThrown();
+ } else if (typeCh != 'L' && typeCh != '[' && typeCh != 'I') {
+ /* TODO: requires multiple "fill in" loops with different widths */
+ LOGE("non-int primitives not implemented\n");
+ dvmThrowException("Ljava/lang/InternalError;",
+ "filled-new-array not implemented for anything but 'int'");
+ GOTO_exceptionThrown();
+ }
+
+ newArray = dvmAllocArrayByClass(arrayClass, vsrc1, ALLOC_DONT_TRACK);
+ if (newArray == NULL)
+ GOTO_exceptionThrown();
+
+ /*
+ * Fill in the elements. It's legal for vsrc1 to be zero.
+ */
+ contents = (u4*) newArray->contents;
+ if (methodCallRange) {
+ for (i = 0; i < vsrc1; i++)
+ contents[i] = GET_REGISTER(vdst+i);
+ } else {
+ assert(vsrc1 <= 5);
+ if (vsrc1 == 5) {
+ contents[4] = GET_REGISTER(arg5);
+ vsrc1--;
+ }
+ for (i = 0; i < vsrc1; i++) {
+ contents[i] = GET_REGISTER(vdst & 0x0f);
+ vdst >>= 4;
+ }
+ }
+ if (typeCh == 'L' || typeCh == '[') {
+ dvmWriteBarrierArray(newArray, 0, newArray->length);
+ }
+
+ retval.l = newArray;
+ }
+ FINISH(3);
+GOTO_TARGET_END
+
+
+GOTO_TARGET(invokeVirtual, bool methodCallRange)
+ {
+ Method* baseMethod;
+ Object* thisPtr;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* method ref */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ /*
+ * The object against which we are executing a method is always
+ * in the first argument.
+ */
+ if (methodCallRange) {
+ assert(vsrc1 > 0);
+ ILOGV("|invoke-virtual-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ thisPtr = (Object*) GET_REGISTER(vdst);
+ } else {
+ assert((vsrc1>>4) > 0);
+ ILOGV("|invoke-virtual args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+ thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+ }
+
+ if (!checkForNull(thisPtr))
+ GOTO_exceptionThrown();
+
+ /*
+ * Resolve the method. This is the correct method for the static
+ * type of the object. We also verify access permissions here.
+ */
+ baseMethod = dvmDexGetResolvedMethod(methodClassDex, ref);
+ if (baseMethod == NULL) {
+ baseMethod = dvmResolveMethod(curMethod->clazz, ref,METHOD_VIRTUAL);
+ if (baseMethod == NULL) {
+ ILOGV("+ unknown method or access denied\n");
+ GOTO_exceptionThrown();
+ }
+ }
+
+ /*
+ * Combine the object we found with the vtable offset in the
+ * method.
+ */
+ assert(baseMethod->methodIndex < thisPtr->clazz->vtableCount);
+ methodToCall = thisPtr->clazz->vtable[baseMethod->methodIndex];
+
+#if defined(WITH_JIT) && (INTERP_TYPE == INTERP_DBG)
+ callsiteClass = thisPtr->clazz;
+#endif
+
+#if 0
+ if (dvmIsAbstractMethod(methodToCall)) {
+ /*
+ * This can happen if you create two classes, Base and Sub, where
+ * Sub is a sub-class of Base. Declare a protected abstract
+ * method foo() in Base, and invoke foo() from a method in Base.
+ * Base is an "abstract base class" and is never instantiated
+ * directly. Now, Override foo() in Sub, and use Sub. This
+ * Works fine unless Sub stops providing an implementation of
+ * the method.
+ */
+ dvmThrowException("Ljava/lang/AbstractMethodError;",
+ "abstract method not implemented");
+ GOTO_exceptionThrown();
+ }
+#else
+ assert(!dvmIsAbstractMethod(methodToCall) ||
+ methodToCall->nativeFunc != NULL);
+#endif
+
+ LOGVV("+++ base=%s.%s virtual[%d]=%s.%s\n",
+ baseMethod->clazz->descriptor, baseMethod->name,
+ (u4) baseMethod->methodIndex,
+ methodToCall->clazz->descriptor, methodToCall->name);
+ assert(methodToCall != NULL);
+
+#if 0
+ if (vsrc1 != methodToCall->insSize) {
+ LOGW("WRONG METHOD: base=%s.%s virtual[%d]=%s.%s\n",
+ baseMethod->clazz->descriptor, baseMethod->name,
+ (u4) baseMethod->methodIndex,
+ methodToCall->clazz->descriptor, methodToCall->name);
+ //dvmDumpClass(baseMethod->clazz);
+ //dvmDumpClass(methodToCall->clazz);
+ dvmDumpAllClasses(0);
+ }
+#endif
+
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+ }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeSuper, bool methodCallRange)
+ {
+ Method* baseMethod;
+ u2 thisReg;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* method ref */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ if (methodCallRange) {
+ ILOGV("|invoke-super-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ thisReg = vdst;
+ } else {
+ ILOGV("|invoke-super args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+ thisReg = vdst & 0x0f;
+ }
+ /* impossible in well-formed code, but we must check nevertheless */
+ if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+ GOTO_exceptionThrown();
+
+ /*
+ * Resolve the method. This is the correct method for the static
+ * type of the object. We also verify access permissions here.
+ * The first arg to dvmResolveMethod() is just the referring class
+ * (used for class loaders and such), so we don't want to pass
+ * the superclass into the resolution call.
+ */
+ baseMethod = dvmDexGetResolvedMethod(methodClassDex, ref);
+ if (baseMethod == NULL) {
+ baseMethod = dvmResolveMethod(curMethod->clazz, ref,METHOD_VIRTUAL);
+ if (baseMethod == NULL) {
+ ILOGV("+ unknown method or access denied\n");
+ GOTO_exceptionThrown();
+ }
+ }
+
+ /*
+ * Combine the object we found with the vtable offset in the
+ * method's class.
+ *
+ * We're using the current method's class' superclass, not the
+ * superclass of "this". This is because we might be executing
+ * in a method inherited from a superclass, and we want to run
+ * in that class' superclass.
+ */
+ if (baseMethod->methodIndex >= curMethod->clazz->super->vtableCount) {
+ /*
+ * Method does not exist in the superclass. Could happen if
+ * superclass gets updated.
+ */
+ dvmThrowException("Ljava/lang/NoSuchMethodError;",
+ baseMethod->name);
+ GOTO_exceptionThrown();
+ }
+ methodToCall = curMethod->clazz->super->vtable[baseMethod->methodIndex];
+#if 0
+ if (dvmIsAbstractMethod(methodToCall)) {
+ dvmThrowException("Ljava/lang/AbstractMethodError;",
+ "abstract method not implemented");
+ GOTO_exceptionThrown();
+ }
+#else
+ assert(!dvmIsAbstractMethod(methodToCall) ||
+ methodToCall->nativeFunc != NULL);
+#endif
+ LOGVV("+++ base=%s.%s super-virtual=%s.%s\n",
+ baseMethod->clazz->descriptor, baseMethod->name,
+ methodToCall->clazz->descriptor, methodToCall->name);
+ assert(methodToCall != NULL);
+
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+ }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeInterface, bool methodCallRange)
+ {
+ Object* thisPtr;
+ ClassObject* thisClass;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* method ref */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ /*
+ * The object against which we are executing a method is always
+ * in the first argument.
+ */
+ if (methodCallRange) {
+ assert(vsrc1 > 0);
+ ILOGV("|invoke-interface-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ thisPtr = (Object*) GET_REGISTER(vdst);
+ } else {
+ assert((vsrc1>>4) > 0);
+ ILOGV("|invoke-interface args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+ thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+ }
+ if (!checkForNull(thisPtr))
+ GOTO_exceptionThrown();
+
+ thisClass = thisPtr->clazz;
+
+#if defined(WITH_JIT) && (INTERP_TYPE == INTERP_DBG)
+ callsiteClass = thisClass;
+#endif
+
+ /*
+ * Given a class and a method index, find the Method* with the
+ * actual code we want to execute.
+ */
+ methodToCall = dvmFindInterfaceMethodInCache(thisClass, ref, curMethod,
+ methodClassDex);
+ if (methodToCall == NULL) {
+ assert(dvmCheckException(self));
+ GOTO_exceptionThrown();
+ }
+
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+ }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeDirect, bool methodCallRange)
+ {
+ u2 thisReg;
+
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* method ref */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ EXPORT_PC();
+
+ if (methodCallRange) {
+ ILOGV("|invoke-direct-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ thisReg = vdst;
+ } else {
+ ILOGV("|invoke-direct args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+ thisReg = vdst & 0x0f;
+ }
+ if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+ GOTO_exceptionThrown();
+
+ methodToCall = dvmDexGetResolvedMethod(methodClassDex, ref);
+ if (methodToCall == NULL) {
+ methodToCall = dvmResolveMethod(curMethod->clazz, ref,
+ METHOD_DIRECT);
+ if (methodToCall == NULL) {
+ ILOGV("+ unknown direct method\n"); // should be impossible
+ GOTO_exceptionThrown();
+ }
+ }
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+ }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeStatic, bool methodCallRange)
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* method ref */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ EXPORT_PC();
+
+ if (methodCallRange)
+ ILOGV("|invoke-static-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ else
+ ILOGV("|invoke-static args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+
+ methodToCall = dvmDexGetResolvedMethod(methodClassDex, ref);
+ if (methodToCall == NULL) {
+ methodToCall = dvmResolveMethod(curMethod->clazz, ref, METHOD_STATIC);
+ if (methodToCall == NULL) {
+ ILOGV("+ unknown method\n");
+ GOTO_exceptionThrown();
+ }
+
+ /*
+ * The JIT needs dvmDexGetResolvedMethod() to return non-null.
+ * Since we use the portable interpreter to build the trace, this extra
+ * check is not needed for mterp.
+ */
+ if (dvmDexGetResolvedMethod(methodClassDex, ref) == NULL) {
+ /* Class initialization is still ongoing */
+ ABORT_JIT_TSELECT();
+ }
+ }
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeVirtualQuick, bool methodCallRange)
+ {
+ Object* thisPtr;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* vtable index */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ /*
+ * The object against which we are executing a method is always
+ * in the first argument.
+ */
+ if (methodCallRange) {
+ assert(vsrc1 > 0);
+ ILOGV("|invoke-virtual-quick-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ thisPtr = (Object*) GET_REGISTER(vdst);
+ } else {
+ assert((vsrc1>>4) > 0);
+ ILOGV("|invoke-virtual-quick args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+ thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+ }
+
+ if (!checkForNull(thisPtr))
+ GOTO_exceptionThrown();
+
+#if defined(WITH_JIT) && (INTERP_TYPE == INTERP_DBG)
+ callsiteClass = thisPtr->clazz;
+#endif
+
+ /*
+ * Combine the object we found with the vtable offset in the
+ * method.
+ */
+ assert(ref < thisPtr->clazz->vtableCount);
+ methodToCall = thisPtr->clazz->vtable[ref];
+
+#if 0
+ if (dvmIsAbstractMethod(methodToCall)) {
+ dvmThrowException("Ljava/lang/AbstractMethodError;",
+ "abstract method not implemented");
+ GOTO_exceptionThrown();
+ }
+#else
+ assert(!dvmIsAbstractMethod(methodToCall) ||
+ methodToCall->nativeFunc != NULL);
+#endif
+
+ LOGVV("+++ virtual[%d]=%s.%s\n",
+ ref, methodToCall->clazz->descriptor, methodToCall->name);
+ assert(methodToCall != NULL);
+
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+ }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeSuperQuick, bool methodCallRange)
+ {
+ u2 thisReg;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* vtable index */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ if (methodCallRange) {
+ ILOGV("|invoke-super-quick-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ thisReg = vdst;
+ } else {
+ ILOGV("|invoke-super-quick args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+ thisReg = vdst & 0x0f;
+ }
+ /* impossible in well-formed code, but we must check nevertheless */
+ if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+ GOTO_exceptionThrown();
+
+#if 0 /* impossible in optimized + verified code */
+ if (ref >= curMethod->clazz->super->vtableCount) {
+ dvmThrowException("Ljava/lang/NoSuchMethodError;", NULL);
+ GOTO_exceptionThrown();
+ }
+#else
+ assert(ref < curMethod->clazz->super->vtableCount);
+#endif
+
+ /*
+ * Combine the object we found with the vtable offset in the
+ * method's class.
+ *
+ * We're using the current method's class' superclass, not the
+ * superclass of "this". This is because we might be executing
+ * in a method inherited from a superclass, and we want to run
+ * in the method's class' superclass.
+ */
+ methodToCall = curMethod->clazz->super->vtable[ref];
+
+#if 0
+ if (dvmIsAbstractMethod(methodToCall)) {
+ dvmThrowException("Ljava/lang/AbstractMethodError;",
+ "abstract method not implemented");
+ GOTO_exceptionThrown();
+ }
+#else
+ assert(!dvmIsAbstractMethod(methodToCall) ||
+ methodToCall->nativeFunc != NULL);
+#endif
+ LOGVV("+++ super-virtual[%d]=%s.%s\n",
+ ref, methodToCall->clazz->descriptor, methodToCall->name);
+ assert(methodToCall != NULL);
+
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+ }
+GOTO_TARGET_END
+
+
+ /*
+ * General handling for return-void, return, and return-wide. Put the
+ * return value in "retval" before jumping here.
+ */
+GOTO_TARGET(returnFromMethod)
+ {
+ StackSaveArea* saveArea;
+
+ /*
+ * We must do this BEFORE we pop the previous stack frame off, so
+ * that the GC can see the return value (if any) in the local vars.
+ *
+ * Since this is now an interpreter switch point, we must do it before
+ * we do anything at all.
+ */
+ PERIODIC_CHECKS(kInterpEntryReturn, 0);
+
+ ILOGV("> retval=0x%llx (leaving %s.%s %s)",
+ retval.j, curMethod->clazz->descriptor, curMethod->name,
+ curMethod->shorty);
+ //DUMP_REGS(curMethod, fp);
+
+ saveArea = SAVEAREA_FROM_FP(fp);
+
+#ifdef EASY_GDB
+ debugSaveArea = saveArea;
+#endif
+#if (INTERP_TYPE == INTERP_DBG)
+ TRACE_METHOD_EXIT(self, curMethod);
+#endif
+
+ /* back up to previous frame and see if we hit a break */
+ fp = saveArea->prevFrame;
+ assert(fp != NULL);
+ if (dvmIsBreakFrame(fp)) {
+ /* bail without popping the method frame from stack */
+ LOGVV("+++ returned into break frame\n");
+#if defined(WITH_JIT)
+ /* Let the Jit know the return is terminating normally */
+ CHECK_JIT_VOID();
+#endif
+ GOTO_bail();
+ }
+
+ /* update thread FP, and reset local variables */
+ self->curFrame = fp;
+ curMethod = SAVEAREA_FROM_FP(fp)->method;
+ //methodClass = curMethod->clazz;
+ methodClassDex = curMethod->clazz->pDvmDex;
+ pc = saveArea->savedPc;
+ ILOGD("> (return to %s.%s %s)", curMethod->clazz->descriptor,
+ curMethod->name, curMethod->shorty);
+
+ /* use FINISH on the caller's invoke instruction */
+ //u2 invokeInstr = INST_INST(FETCH(0));
+ if (true /*invokeInstr >= OP_INVOKE_VIRTUAL &&
+ invokeInstr <= OP_INVOKE_INTERFACE*/)
+ {
+ FINISH(3);
+ } else {
+ //LOGE("Unknown invoke instr %02x at %d\n",
+ // invokeInstr, (int) (pc - curMethod->insns));
+ assert(false);
+ }
+ }
+GOTO_TARGET_END
+
+
+ /*
+ * Jump here when the code throws an exception.
+ *
+ * By the time we get here, the Throwable has been created and the stack
+ * trace has been saved off.
+ */
+GOTO_TARGET(exceptionThrown)
+ {
+ Object* exception;
+ int catchRelPc;
+
+ /*
+ * Since this is now an interpreter switch point, we must do it before
+ * we do anything at all.
+ */
+ PERIODIC_CHECKS(kInterpEntryThrow, 0);
+
+#if defined(WITH_JIT)
+ // Something threw during trace selection - abort the current trace
+ ABORT_JIT_TSELECT();
+#endif
+ /*
+ * We save off the exception and clear the exception status. While
+ * processing the exception we might need to load some Throwable
+ * classes, and we don't want class loader exceptions to get
+ * confused with this one.
+ */
+ assert(dvmCheckException(self));
+ exception = dvmGetException(self);
+ dvmAddTrackedAlloc(exception, self);
+ dvmClearException(self);
+
+ LOGV("Handling exception %s at %s:%d\n",
+ exception->clazz->descriptor, curMethod->name,
+ dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+
+#if (INTERP_TYPE == INTERP_DBG)
+ /*
+ * Tell the debugger about it.
+ *
+ * TODO: if the exception was thrown by interpreted code, control
+ * fell through native, and then back to us, we will report the
+ * exception at the point of the throw and again here. We can avoid
+ * this by not reporting exceptions when we jump here directly from
+ * the native call code above, but then we won't report exceptions
+ * that were thrown *from* the JNI code (as opposed to *through* it).
+ *
+ * The correct solution is probably to ignore from-native exceptions
+ * here, and have the JNI exception code do the reporting to the
+ * debugger.
+ */
+ if (gDvm.debuggerActive) {
+ void* catchFrame;
+ catchRelPc = dvmFindCatchBlock(self, pc - curMethod->insns,
+ exception, true, &catchFrame);
+ dvmDbgPostException(fp, pc - curMethod->insns, catchFrame,
+ catchRelPc, exception);
+ }
+#endif
+
+ /*
+ * We need to unroll to the catch block or the nearest "break"
+ * frame.
+ *
+ * A break frame could indicate that we have reached an intermediate
+ * native call, or have gone off the top of the stack and the thread
+ * needs to exit. Either way, we return from here, leaving the
+ * exception raised.
+ *
+ * If we do find a catch block, we want to transfer execution to
+ * that point.
+ *
+ * Note this can cause an exception while resolving classes in
+ * the "catch" blocks.
+ */
+ catchRelPc = dvmFindCatchBlock(self, pc - curMethod->insns,
+ exception, false, (void*)&fp);
+
+ /*
+ * Restore the stack bounds after an overflow. This isn't going to
+ * be correct in all circumstances, e.g. if JNI code devours the
+ * exception this won't happen until some other exception gets
+ * thrown. If the code keeps pushing the stack bounds we'll end
+ * up aborting the VM.
+ *
+ * Note we want to do this *after* the call to dvmFindCatchBlock,
+ * because that may need extra stack space to resolve exception
+ * classes (e.g. through a class loader).
+ *
+ * It's possible for the stack overflow handling to cause an
+ * exception (specifically, class resolution in a "catch" block
+ * during the call above), so we could see the thread's overflow
+ * flag raised but actually be running in a "nested" interpreter
+ * frame. We don't allow doubled-up StackOverflowErrors, so
+ * we can check for this by just looking at the exception type
+ * in the cleanup function. Also, we won't unroll past the SOE
+ * point because the more-recent exception will hit a break frame
+ * as it unrolls to here.
+ */
+ if (self->stackOverflowed)
+ dvmCleanupStackOverflow(self, exception);
+
+ if (catchRelPc < 0) {
+ /* falling through to JNI code or off the bottom of the stack */
+#if DVM_SHOW_EXCEPTION >= 2
+ LOGD("Exception %s from %s:%d not caught locally\n",
+ exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+ dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+#endif
+ dvmSetException(self, exception);
+ dvmReleaseTrackedAlloc(exception, self);
+ GOTO_bail();
+ }
+
+#if DVM_SHOW_EXCEPTION >= 3
+ {
+ const Method* catchMethod = SAVEAREA_FROM_FP(fp)->method;
+ LOGD("Exception %s thrown from %s:%d to %s:%d\n",
+ exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+ dvmLineNumFromPC(curMethod, pc - curMethod->insns),
+ dvmGetMethodSourceFile(catchMethod),
+ dvmLineNumFromPC(catchMethod, catchRelPc));
+ }
+#endif
+
+ /*
+ * Adjust local variables to match self->curFrame and the
+ * updated PC.
+ */
+ //fp = (u4*) self->curFrame;
+ curMethod = SAVEAREA_FROM_FP(fp)->method;
+ //methodClass = curMethod->clazz;
+ methodClassDex = curMethod->clazz->pDvmDex;
+ pc = curMethod->insns + catchRelPc;
+ ILOGV("> pc <-- %s.%s %s", curMethod->clazz->descriptor,
+ curMethod->name, curMethod->shorty);
+ DUMP_REGS(curMethod, fp, false); // show all regs
+
+ /*
+ * Restore the exception if the handler wants it.
+ *
+ * The Dalvik spec mandates that, if an exception handler wants to
+ * do something with the exception, the first instruction executed
+ * must be "move-exception". We can pass the exception along
+ * through the thread struct, and let the move-exception instruction
+ * clear it for us.
+ *
+ * If the handler doesn't call move-exception, we don't want to
+ * finish here with an exception still pending.
+ */
+ if (INST_INST(FETCH(0)) == OP_MOVE_EXCEPTION)
+ dvmSetException(self, exception);
+
+ dvmReleaseTrackedAlloc(exception, self);
+ FINISH(0);
+ }
+GOTO_TARGET_END
+
+
+
+ /*
+ * General handling for invoke-{virtual,super,direct,static,interface},
+ * including "quick" variants.
+ *
+ * Set "methodToCall" to the Method we're calling, and "methodCallRange"
+ * depending on whether this is a "/range" instruction.
+ *
+ * For a range call:
+ * "vsrc1" holds the argument count (8 bits)
+ * "vdst" holds the first argument in the range
+ * For a non-range call:
+ * "vsrc1" holds the argument count (4 bits) and the 5th argument index
+ * "vdst" holds four 4-bit register indices
+ *
+ * The caller must EXPORT_PC before jumping here, because any method
+ * call can throw a stack overflow exception.
+ */
+GOTO_TARGET(invokeMethod, bool methodCallRange, const Method* _methodToCall,
+ u2 count, u2 regs)
+ {
+ STUB_HACK(vsrc1 = count; vdst = regs; methodToCall = _methodToCall;);
+
+ //printf("range=%d call=%p count=%d regs=0x%04x\n",
+ // methodCallRange, methodToCall, count, regs);
+ //printf(" --> %s.%s %s\n", methodToCall->clazz->descriptor,
+ // methodToCall->name, methodToCall->shorty);
+
+ u4* outs;
+ int i;
+
+ /*
+ * Copy args. This may corrupt vsrc1/vdst.
+ */
+ if (methodCallRange) {
+ // could use memcpy or a "Duff's device"; most functions have
+ // so few args it won't matter much
+ assert(vsrc1 <= curMethod->outsSize);
+ assert(vsrc1 == methodToCall->insSize);
+ outs = OUTS_FROM_FP(fp, vsrc1);
+ for (i = 0; i < vsrc1; i++)
+ outs[i] = GET_REGISTER(vdst+i);
+ } else {
+ u4 count = vsrc1 >> 4;
+
+ assert(count <= curMethod->outsSize);
+ assert(count == methodToCall->insSize);
+ assert(count <= 5);
+
+ outs = OUTS_FROM_FP(fp, count);
+#if 0
+ if (count == 5) {
+ outs[4] = GET_REGISTER(vsrc1 & 0x0f);
+ count--;
+ }
+ for (i = 0; i < (int) count; i++) {
+ outs[i] = GET_REGISTER(vdst & 0x0f);
+ vdst >>= 4;
+ }
+#else
+ // This version executes fewer instructions but is larger
+ // overall. Seems to be a teensy bit faster.
+ assert((vdst >> 16) == 0); // 16 bits -or- high 16 bits clear
+ switch (count) {
+ case 5:
+ outs[4] = GET_REGISTER(vsrc1 & 0x0f);
+ case 4:
+ outs[3] = GET_REGISTER(vdst >> 12);
+ case 3:
+ outs[2] = GET_REGISTER((vdst & 0x0f00) >> 8);
+ case 2:
+ outs[1] = GET_REGISTER((vdst & 0x00f0) >> 4);
+ case 1:
+ outs[0] = GET_REGISTER(vdst & 0x0f);
+ default:
+ ;
+ }
+#endif
+ }
+ }
+
+ /*
+ * (This was originally a "goto" target; I've kept it separate from the
+ * stuff above in case we want to refactor things again.)
+ *
+ * At this point, we have the arguments stored in the "outs" area of
+ * the current method's stack frame, and the method to call in
+ * "methodToCall". Push a new stack frame.
+ */
+ {
+ StackSaveArea* newSaveArea;
+ u4* newFp;
+
+ ILOGV("> %s%s.%s %s",
+ dvmIsNativeMethod(methodToCall) ? "(NATIVE) " : "",
+ methodToCall->clazz->descriptor, methodToCall->name,
+ methodToCall->shorty);
+
+ newFp = (u4*) SAVEAREA_FROM_FP(fp) - methodToCall->registersSize;
+ newSaveArea = SAVEAREA_FROM_FP(newFp);
+
+ /* verify that we have enough space */
+ if (true) {
+ u1* bottom;
+ bottom = (u1*) newSaveArea - methodToCall->outsSize * sizeof(u4);
+ if (bottom < self->interpStackEnd) {
+ /* stack overflow */
+ LOGV("Stack overflow on method call (start=%p end=%p newBot=%p(%d) size=%d '%s')\n",
+ self->interpStackStart, self->interpStackEnd, bottom,
+ (u1*) fp - bottom, self->interpStackSize,
+ methodToCall->name);
+ dvmHandleStackOverflow(self, methodToCall);
+ assert(dvmCheckException(self));
+ GOTO_exceptionThrown();
+ }
+ //LOGD("+++ fp=%p newFp=%p newSave=%p bottom=%p\n",
+ // fp, newFp, newSaveArea, bottom);
+ }
+
+#ifdef LOG_INSTR
+ if (methodToCall->registersSize > methodToCall->insSize) {
+ /*
+ * This makes valgrind quiet when we print registers that
+ * haven't been initialized. Turn it off when the debug
+ * messages are disabled -- we want valgrind to report any
+ * used-before-initialized issues.
+ */
+ memset(newFp, 0xcc,
+ (methodToCall->registersSize - methodToCall->insSize) * 4);
+ }
+#endif
+
+#ifdef EASY_GDB
+ newSaveArea->prevSave = SAVEAREA_FROM_FP(fp);
+#endif
+ newSaveArea->prevFrame = fp;
+ newSaveArea->savedPc = pc;
+#if defined(WITH_JIT)
+ newSaveArea->returnAddr = 0;
+#endif
+ newSaveArea->method = methodToCall;
+
+ if (!dvmIsNativeMethod(methodToCall)) {
+ /*
+ * "Call" interpreted code. Reposition the PC, update the
+ * frame pointer and other local state, and continue.
+ */
+ curMethod = methodToCall;
+ methodClassDex = curMethod->clazz->pDvmDex;
+ pc = methodToCall->insns;
+ fp = self->curFrame = newFp;
+#ifdef EASY_GDB
+ debugSaveArea = SAVEAREA_FROM_FP(newFp);
+#endif
+#if INTERP_TYPE == INTERP_DBG
+ debugIsMethodEntry = true; // profiling, debugging
+#endif
+ ILOGD("> pc <-- %s.%s %s", curMethod->clazz->descriptor,
+ curMethod->name, curMethod->shorty);
+ DUMP_REGS(curMethod, fp, true); // show input args
+ FINISH(0); // jump to method start
+ } else {
+ /* set this up for JNI locals, even if not a JNI native */
+#ifdef USE_INDIRECT_REF
+ newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
+#else
+ newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.nextEntry;
+#endif
+
+ self->curFrame = newFp;
+
+ DUMP_REGS(methodToCall, newFp, true); // show input args
+
+#if (INTERP_TYPE == INTERP_DBG)
+ if (gDvm.debuggerActive) {
+ dvmDbgPostLocationEvent(methodToCall, -1,
+ dvmGetThisPtr(curMethod, fp), DBG_METHOD_ENTRY);
+ }
+#endif
+#if (INTERP_TYPE == INTERP_DBG)
+ TRACE_METHOD_ENTER(self, methodToCall);
+#endif
+
+ {
+ ILOGD("> native <-- %s.%s %s", methodToCall->clazz->descriptor,
+ methodToCall->name, methodToCall->shorty);
+ }
+
+#if defined(WITH_JIT)
+ /* Allow the Jit to end any pending trace building */
+ CHECK_JIT_VOID();
+#endif
+
+ /*
+ * Jump through native call bridge. Because we leave no
+ * space for locals on native calls, "newFp" points directly
+ * to the method arguments.
+ */
+ (*methodToCall->nativeFunc)(newFp, &retval, methodToCall, self);
+
+#if (INTERP_TYPE == INTERP_DBG)
+ if (gDvm.debuggerActive) {
+ dvmDbgPostLocationEvent(methodToCall, -1,
+ dvmGetThisPtr(curMethod, fp), DBG_METHOD_EXIT);
+ }
+#endif
+#if (INTERP_TYPE == INTERP_DBG)
+ TRACE_METHOD_EXIT(self, methodToCall);
+#endif
+
+ /* pop frame off */
+ dvmPopJniLocals(self, newSaveArea);
+ self->curFrame = fp;
+
+ /*
+ * If the native code threw an exception, or interpreted code
+ * invoked by the native call threw one and nobody has cleared
+ * it, jump to our local exception handling.
+ */
+ if (dvmCheckException(self)) {
+ LOGV("Exception thrown by/below native code\n");
+ GOTO_exceptionThrown();
+ }
+
+ ILOGD("> retval=0x%llx (leaving native)", retval.j);
+ ILOGD("> (return from native %s.%s to %s.%s %s)",
+ methodToCall->clazz->descriptor, methodToCall->name,
+ curMethod->clazz->descriptor, curMethod->name,
+ curMethod->shorty);
+
+ //u2 invokeInstr = INST_INST(FETCH(0));
+ if (true /*invokeInstr >= OP_INVOKE_VIRTUAL &&
+ invokeInstr <= OP_INVOKE_INTERFACE*/)
+ {
+ FINISH(3);
+ } else {
+ //LOGE("Unknown invoke instr %02x at %d\n",
+ // invokeInstr, (int) (pc - curMethod->insns));
+ assert(false);
+ }
+ }
+ }
+ assert(false); // should not get here
+GOTO_TARGET_END
diff --git a/vm/mterp/c/header.c b/vm/mterp/c/header.c
new file mode 100644
index 0000000..aaf6dab
--- /dev/null
+++ b/vm/mterp/c/header.c
@@ -0,0 +1,405 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* common includes */
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "mterp/Mterp.h"
+#include <math.h> // needed for fmod, fmodf
+#include "mterp/common/FindInterface.h"
+
+/*
+ * Configuration defines. These affect the C implementations, i.e. the
+ * portable interpreter(s) and C stubs.
+ *
+ * Some defines are controlled by the Makefile, e.g.:
+ * WITH_INSTR_CHECKS
+ * WITH_TRACKREF_CHECKS
+ * EASY_GDB
+ * NDEBUG
+ *
+ * If THREADED_INTERP is not defined, we use a classic "while true / switch"
+ * interpreter. If it is defined, then the tail end of each instruction
+ * handler fetches the next instruction and jumps directly to the handler.
+ * This increases the size of the "Std" interpreter by about 10%, but
+ * provides a speedup of about the same magnitude.
+ *
+ * There's a "hybrid" approach that uses a goto table instead of a switch
+ * statement, avoiding the "is the opcode in range" tests required for switch.
+ * The performance is close to the threaded version, and without the 10%
+ * size increase, but the benchmark results are off enough that it's not
+ * worth adding as a third option.
+ */
+#define THREADED_INTERP /* threaded vs. while-loop interpreter */
+
+#ifdef WITH_INSTR_CHECKS /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * ARM EABI requires 64-bit alignment for access to 64-bit data types. We
+ * can't just use pointers to copy 64-bit values out of our interpreted
+ * register set, because gcc will generate ldrd/strd.
+ *
+ * The __UNION version copies data in and out of a union. The __MEMCPY
+ * version uses a memcpy() call to do the transfer; gcc is smart enough to
+ * not actually call memcpy(). The __UNION version is very bad on ARM;
+ * it only uses one more instruction than __MEMCPY, but for some reason
+ * gcc thinks it needs separate storage for every instance of the union.
+ * On top of that, it feels the need to zero them out at the start of the
+ * method. Net result is we zero out ~700 bytes of stack space at the top
+ * of the interpreter using ARM STM instructions.
+ */
+#if defined(__ARM_EABI__)
+//# define NO_UNALIGN_64__UNION
+# define NO_UNALIGN_64__MEMCPY
+#endif
+
+//#define LOG_INSTR /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Keep a tally of accesses to fields. Currently only works if full DEX
+ * optimization is disabled.
+ */
+#ifdef PROFILE_FIELD_ACCESS
+# define UPDATE_FIELD_GET(_field) { (_field)->gets++; }
+# define UPDATE_FIELD_PUT(_field) { (_field)->puts++; }
+#else
+# define UPDATE_FIELD_GET(_field) ((void)0)
+# define UPDATE_FIELD_PUT(_field) ((void)0)
+#endif
+
+/*
+ * Export another copy of the PC on every instruction; this is largely
+ * redundant with EXPORT_PC and the debugger code. This value can be
+ * compared against what we have stored on the stack with EXPORT_PC to
+ * help ensure that we aren't missing any export calls.
+ */
+#if WITH_EXTRA_GC_CHECKS > 1
+# define EXPORT_EXTRA_PC() (self->currentPc2 = pc)
+#else
+# define EXPORT_EXTRA_PC()
+#endif
+
+/*
+ * Adjust the program counter. "_offset" is a signed int, in 16-bit units.
+ *
+ * Assumes the existence of "const u2* pc" and "const u2* curMethod->insns".
+ *
+ * We don't advance the program counter until we finish an instruction or
+ * branch, because we do want to have to unroll the PC if there's an
+ * exception.
+ */
+#ifdef CHECK_BRANCH_OFFSETS
+# define ADJUST_PC(_offset) do { \
+ int myoff = _offset; /* deref only once */ \
+ if (pc + myoff < curMethod->insns || \
+ pc + myoff >= curMethod->insns + dvmGetMethodInsnsSize(curMethod)) \
+ { \
+ char* desc; \
+ desc = dexProtoCopyMethodDescriptor(&curMethod->prototype); \
+ LOGE("Invalid branch %d at 0x%04x in %s.%s %s\n", \
+ myoff, (int) (pc - curMethod->insns), \
+ curMethod->clazz->descriptor, curMethod->name, desc); \
+ free(desc); \
+ dvmAbort(); \
+ } \
+ pc += myoff; \
+ EXPORT_EXTRA_PC(); \
+ } while (false)
+#else
+# define ADJUST_PC(_offset) do { \
+ pc += _offset; \
+ EXPORT_EXTRA_PC(); \
+ } while (false)
+#endif
+
+/*
+ * If enabled, log instructions as we execute them.
+ */
+#ifdef LOG_INSTR
+# define ILOGD(...) ILOG(LOG_DEBUG, __VA_ARGS__)
+# define ILOGV(...) ILOG(LOG_VERBOSE, __VA_ARGS__)
+# define ILOG(_level, ...) do { \
+ char debugStrBuf[128]; \
+ snprintf(debugStrBuf, sizeof(debugStrBuf), __VA_ARGS__); \
+ if (curMethod != NULL) \
+ LOG(_level, LOG_TAG"i", "%-2d|%04x%s\n", \
+ self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+ else \
+ LOG(_level, LOG_TAG"i", "%-2d|####%s\n", \
+ self->threadId, debugStrBuf); \
+ } while(false)
+void dvmDumpRegs(const Method* method, const u4* framePtr, bool inOnly);
+# define DUMP_REGS(_meth, _frame, _inOnly) dvmDumpRegs(_meth, _frame, _inOnly)
+static const char kSpacing[] = " ";
+#else
+# define ILOGD(...) ((void)0)
+# define ILOGV(...) ((void)0)
+# define DUMP_REGS(_meth, _frame, _inOnly) ((void)0)
+#endif
+
+/* get a long from an array of u4 */
+static inline s8 getLongFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { s8 ll; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.parts[0] = ptr[0];
+ conv.parts[1] = ptr[1];
+ return conv.ll;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ s8 val;
+ memcpy(&val, &ptr[idx], 8);
+ return val;
+#else
+ return *((s8*) &ptr[idx]);
+#endif
+}
+
+/* store a long into an array of u4 */
+static inline void putLongToArray(u4* ptr, int idx, s8 val)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { s8 ll; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.ll = val;
+ ptr[0] = conv.parts[0];
+ ptr[1] = conv.parts[1];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ memcpy(&ptr[idx], &val, 8);
+#else
+ *((s8*) &ptr[idx]) = val;
+#endif
+}
+
+/* get a double from an array of u4 */
+static inline double getDoubleFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { double d; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.parts[0] = ptr[0];
+ conv.parts[1] = ptr[1];
+ return conv.d;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ double dval;
+ memcpy(&dval, &ptr[idx], 8);
+ return dval;
+#else
+ return *((double*) &ptr[idx]);
+#endif
+}
+
+/* store a double into an array of u4 */
+static inline void putDoubleToArray(u4* ptr, int idx, double dval)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { double d; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.d = dval;
+ ptr[0] = conv.parts[0];
+ ptr[1] = conv.parts[1];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ memcpy(&ptr[idx], &dval, 8);
+#else
+ *((double*) &ptr[idx]) = dval;
+#endif
+}
+
+/*
+ * If enabled, validate the register number on every access. Otherwise,
+ * just do an array access.
+ *
+ * Assumes the existence of "u4* fp".
+ *
+ * "_idx" may be referenced more than once.
+ */
+#ifdef CHECK_REGISTER_INDICES
+# define GET_REGISTER(_idx) \
+ ( (_idx) < curMethod->registersSize ? \
+ (fp[(_idx)]) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER(_idx, _val) \
+ ( (_idx) < curMethod->registersSize ? \
+ (fp[(_idx)] = (u4)(_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_AS_OBJECT(_idx) ((Object *)GET_REGISTER(_idx))
+# define SET_REGISTER_AS_OBJECT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_INT(_idx) ((s4) GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ getLongFromArray(fp, (_idx)) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER_WIDE(_idx, _val) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ putLongToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_FLOAT(_idx) \
+ ( (_idx) < curMethod->registersSize ? \
+ (*((float*) &fp[(_idx)])) : (assert(!"bad reg"),1969.0f) )
+# define SET_REGISTER_FLOAT(_idx, _val) \
+ ( (_idx) < curMethod->registersSize ? \
+ (*((float*) &fp[(_idx)]) = (_val)) : (assert(!"bad reg"),1969.0f) )
+# define GET_REGISTER_DOUBLE(_idx) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ getDoubleFromArray(fp, (_idx)) : (assert(!"bad reg"),1969.0) )
+# define SET_REGISTER_DOUBLE(_idx, _val) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ putDoubleToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969.0) )
+#else
+# define GET_REGISTER(_idx) (fp[(_idx)])
+# define SET_REGISTER(_idx, _val) (fp[(_idx)] = (_val))
+# define GET_REGISTER_AS_OBJECT(_idx) ((Object*) fp[(_idx)])
+# define SET_REGISTER_AS_OBJECT(_idx, _val) (fp[(_idx)] = (u4)(_val))
+# define GET_REGISTER_INT(_idx) ((s4)GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) getLongFromArray(fp, (_idx))
+# define SET_REGISTER_WIDE(_idx, _val) putLongToArray(fp, (_idx), (_val))
+# define GET_REGISTER_FLOAT(_idx) (*((float*) &fp[(_idx)]))
+# define SET_REGISTER_FLOAT(_idx, _val) (*((float*) &fp[(_idx)]) = (_val))
+# define GET_REGISTER_DOUBLE(_idx) getDoubleFromArray(fp, (_idx))
+# define SET_REGISTER_DOUBLE(_idx, _val) putDoubleToArray(fp, (_idx), (_val))
+#endif
+
+/*
+ * Get 16 bits from the specified offset of the program counter. We always
+ * want to load 16 bits at a time from the instruction stream -- it's more
+ * efficient than 8 and won't have the alignment problems that 32 might.
+ *
+ * Assumes existence of "const u2* pc".
+ */
+#define FETCH(_offset) (pc[(_offset)])
+
+/*
+ * Extract instruction byte from 16-bit fetch (_inst is a u2).
+ */
+#define INST_INST(_inst) ((_inst) & 0xff)
+
+/*
+ * Replace the opcode (used when handling breakpoints). _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
+ * Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
+ */
+#define INST_A(_inst) (((_inst) >> 8) & 0x0f)
+#define INST_B(_inst) ((_inst) >> 12)
+
+/*
+ * Get the 8-bit "vAA" 8-bit register index from the instruction word.
+ * (_inst is u2)
+ */
+#define INST_AA(_inst) ((_inst) >> 8)
+
+/*
+ * The current PC must be available to Throwable constructors, e.g.
+ * those created by dvmThrowException(), so that the exception stack
+ * trace can be generated correctly. If we don't do this, the offset
+ * within the current method won't be shown correctly. See the notes
+ * in Exception.c.
+ *
+ * This is also used to determine the address for precise GC.
+ *
+ * Assumes existence of "u4* fp" and "const u2* pc".
+ */
+#define EXPORT_PC() (SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc)
+
+/*
+ * Determine if we need to switch to a different interpreter. "_current"
+ * is either INTERP_STD or INTERP_DBG. It should be fixed for a given
+ * interpreter generation file, which should remove the outer conditional
+ * from the following.
+ *
+ * If we're building without debug and profiling support, we never switch.
+ */
+#if defined(WITH_JIT)
+# define NEED_INTERP_SWITCH(_current) ( \
+ (_current == INTERP_STD) ? \
+ dvmJitDebuggerOrProfilerActive() : !dvmJitDebuggerOrProfilerActive() )
+#else
+# define NEED_INTERP_SWITCH(_current) ( \
+ (_current == INTERP_STD) ? \
+ dvmDebuggerOrProfilerActive() : !dvmDebuggerOrProfilerActive() )
+#endif
+
+/*
+ * Check to see if "obj" is NULL. If so, throw an exception. Assumes the
+ * pc has already been exported to the stack.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler calls into
+ * something that could throw an exception (so we have already called
+ * EXPORT_PC at the top).
+ */
+static inline bool checkForNull(Object* obj)
+{
+ if (obj == NULL) {
+ dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+ return false;
+ }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+ if (!dvmIsValidObject(obj)) {
+ LOGE("Invalid object %p\n", obj);
+ dvmAbort();
+ }
+#endif
+#ifndef NDEBUG
+ if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+ /* probable heap corruption */
+ LOGE("Invalid object class %p (in %p)\n", obj->clazz, obj);
+ dvmAbort();
+ }
+#endif
+ return true;
+}
+
+/*
+ * Check to see if "obj" is NULL. If so, export the PC into the stack
+ * frame and throw an exception.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler doesn't do
+ * anything else that can throw an exception.
+ */
+static inline bool checkForNullExportPC(Object* obj, u4* fp, const u2* pc)
+{
+ if (obj == NULL) {
+ EXPORT_PC();
+ dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+ return false;
+ }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+ if (!dvmIsValidObject(obj)) {
+ LOGE("Invalid object %p\n", obj);
+ dvmAbort();
+ }
+#endif
+#ifndef NDEBUG
+ if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+ /* probable heap corruption */
+ LOGE("Invalid object class %p (in %p)\n", obj->clazz, obj);
+ dvmAbort();
+ }
+#endif
+ return true;
+}
diff --git a/vm/mterp/c/opcommon.c b/vm/mterp/c/opcommon.c
new file mode 100644
index 0000000..43ee5bc
--- /dev/null
+++ b/vm/mterp/c/opcommon.c
@@ -0,0 +1,656 @@
+/* forward declarations of goto targets */
+GOTO_TARGET_DECL(filledNewArray, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtual, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuper, bool methodCallRange);
+GOTO_TARGET_DECL(invokeInterface, bool methodCallRange);
+GOTO_TARGET_DECL(invokeDirect, bool methodCallRange);
+GOTO_TARGET_DECL(invokeStatic, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtualQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuperQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeMethod, bool methodCallRange, const Method* methodToCall,
+ u2 count, u2 regs);
+GOTO_TARGET_DECL(returnFromMethod);
+GOTO_TARGET_DECL(exceptionThrown);
+
+/*
+ * ===========================================================================
+ *
+ * What follows are opcode definitions shared between multiple opcodes with
+ * minor substitutions handled by the C pre-processor. These should probably
+ * use the mterp substitution mechanism instead, with the code here moved
+ * into common fragment files (like the asm "binop.S"), although it's hard
+ * to give up the C preprocessor in favor of the much simpler text subst.
+ *
+ * ===========================================================================
+ */
+
+#define HANDLE_NUMCONV(_opcode, _opname, _fromtype, _totype) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER##_totype(vdst, \
+ GET_REGISTER##_fromtype(vsrc1)); \
+ FINISH(1);
+
+#define HANDLE_FLOAT_TO_INT(_opcode, _opname, _fromvtype, _fromrtype, \
+ _tovtype, _tortype) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ { \
+ /* spec defines specific handling for +/- inf and NaN values */ \
+ _fromvtype val; \
+ _tovtype intMin, intMax, result; \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1); \
+ val = GET_REGISTER##_fromrtype(vsrc1); \
+ intMin = (_tovtype) 1 << (sizeof(_tovtype) * 8 -1); \
+ intMax = ~intMin; \
+ result = (_tovtype) val; \
+ if (val >= intMax) /* +inf */ \
+ result = intMax; \
+ else if (val <= intMin) /* -inf */ \
+ result = intMin; \
+ else if (val != val) /* NaN */ \
+ result = 0; \
+ else \
+ result = (_tovtype) val; \
+ SET_REGISTER##_tortype(vdst, result); \
+ } \
+ FINISH(1);
+
+#define HANDLE_INT_TO_SMALL(_opcode, _opname, _type) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|int-to-%s v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER(vdst, (_type) GET_REGISTER(vsrc1)); \
+ FINISH(1);
+
+/* NOTE: the comparison result is always a signed 4-byte integer */
+#define HANDLE_OP_CMPX(_opcode, _opname, _varType, _type, _nanVal) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ int result; \
+ u2 regs; \
+ _varType val1, val2; \
+ vdst = INST_AA(inst); \
+ regs = FETCH(1); \
+ vsrc1 = regs & 0xff; \
+ vsrc2 = regs >> 8; \
+ ILOGV("|cmp%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ val1 = GET_REGISTER##_type(vsrc1); \
+ val2 = GET_REGISTER##_type(vsrc2); \
+ if (val1 == val2) \
+ result = 0; \
+ else if (val1 < val2) \
+ result = -1; \
+ else if (val1 > val2) \
+ result = 1; \
+ else \
+ result = (_nanVal); \
+ ILOGV("+ result=%d\n", result); \
+ SET_REGISTER(vdst, result); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_IF_XX(_opcode, _opname, _cmp) \
+ HANDLE_OPCODE(_opcode /*vA, vB, +CCCC*/) \
+ vsrc1 = INST_A(inst); \
+ vsrc2 = INST_B(inst); \
+ if ((s4) GET_REGISTER(vsrc1) _cmp (s4) GET_REGISTER(vsrc2)) { \
+ int branchOffset = (s2)FETCH(1); /* sign-extended */ \
+ ILOGV("|if-%s v%d,v%d,+0x%04x", (_opname), vsrc1, vsrc2, \
+ branchOffset); \
+ ILOGV("> branch taken"); \
+ if (branchOffset < 0) \
+ PERIODIC_CHECKS(kInterpEntryInstr, branchOffset); \
+ FINISH(branchOffset); \
+ } else { \
+ ILOGV("|if-%s v%d,v%d,-", (_opname), vsrc1, vsrc2); \
+ FINISH(2); \
+ }
+
+#define HANDLE_OP_IF_XXZ(_opcode, _opname, _cmp) \
+ HANDLE_OPCODE(_opcode /*vAA, +BBBB*/) \
+ vsrc1 = INST_AA(inst); \
+ if ((s4) GET_REGISTER(vsrc1) _cmp 0) { \
+ int branchOffset = (s2)FETCH(1); /* sign-extended */ \
+ ILOGV("|if-%s v%d,+0x%04x", (_opname), vsrc1, branchOffset); \
+ ILOGV("> branch taken"); \
+ if (branchOffset < 0) \
+ PERIODIC_CHECKS(kInterpEntryInstr, branchOffset); \
+ FINISH(branchOffset); \
+ } else { \
+ ILOGV("|if-%s v%d,-", (_opname), vsrc1); \
+ FINISH(2); \
+ }
+
+#define HANDLE_UNOP(_opcode, _opname, _pfx, _sfx, _type) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER##_type(vdst, _pfx GET_REGISTER##_type(vsrc1) _sfx); \
+ FINISH(1);
+
+#define HANDLE_OP_X_INT(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER(vsrc1); \
+ secondVal = GET_REGISTER(vsrc2); \
+ if (secondVal == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && secondVal == -1) { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ /* non-div/rem case */ \
+ SET_REGISTER(vdst, \
+ (s4) GET_REGISTER(vsrc1) _op (s4) GET_REGISTER(vsrc2)); \
+ } \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_SHX_INT(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER(vdst, \
+ _cast GET_REGISTER(vsrc1) _op (GET_REGISTER(vsrc2) & 0x1f)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT16(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vA, vB, #+CCCC*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ vsrc2 = FETCH(1); \
+ ILOGV("|%s-int/lit16 v%d,v%d,#+0x%04x", \
+ (_opname), vdst, vsrc1, vsrc2); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, result; \
+ firstVal = GET_REGISTER(vsrc1); \
+ if ((s2) vsrc2 == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && ((s2) vsrc2) == -1) { \
+ /* won't generate /lit16 instr for this; check anyway */ \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op (s2) vsrc2; \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ /* non-div/rem case */ \
+ SET_REGISTER(vdst, GET_REGISTER(vsrc1) _op (s2) vsrc2); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT8(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/) \
+ { \
+ u2 litInfo; \
+ vdst = INST_AA(inst); \
+ litInfo = FETCH(1); \
+ vsrc1 = litInfo & 0xff; \
+ vsrc2 = litInfo >> 8; /* constant */ \
+ ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x", \
+ (_opname), vdst, vsrc1, vsrc2); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, result; \
+ firstVal = GET_REGISTER(vsrc1); \
+ if ((s1) vsrc2 == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && ((s1) vsrc2) == -1) { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op ((s1) vsrc2); \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ SET_REGISTER(vdst, \
+ (s4) GET_REGISTER(vsrc1) _op (s1) vsrc2); \
+ } \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_SHX_INT_LIT8(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/) \
+ { \
+ u2 litInfo; \
+ vdst = INST_AA(inst); \
+ litInfo = FETCH(1); \
+ vsrc1 = litInfo & 0xff; \
+ vsrc2 = litInfo >> 8; /* constant */ \
+ ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x", \
+ (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER(vdst, \
+ _cast GET_REGISTER(vsrc1) _op (vsrc2 & 0x1f)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_INT_2ADDR(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER(vdst); \
+ secondVal = GET_REGISTER(vsrc1); \
+ if (secondVal == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && secondVal == -1) { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ SET_REGISTER(vdst, \
+ (s4) GET_REGISTER(vdst) _op (s4) GET_REGISTER(vsrc1)); \
+ } \
+ FINISH(1);
+
+#define HANDLE_OP_SHX_INT_2ADDR(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER(vdst, \
+ _cast GET_REGISTER(vdst) _op (GET_REGISTER(vsrc1) & 0x1f)); \
+ FINISH(1);
+
+#define HANDLE_OP_X_LONG(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ if (_chkdiv != 0) { \
+ s8 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER_WIDE(vsrc1); \
+ secondVal = GET_REGISTER_WIDE(vsrc2); \
+ if (secondVal == 0LL) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u8)firstVal == 0x8000000000000000ULL && \
+ secondVal == -1LL) \
+ { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER_WIDE(vdst, result); \
+ } else { \
+ SET_REGISTER_WIDE(vdst, \
+ (s8) GET_REGISTER_WIDE(vsrc1) _op (s8) GET_REGISTER_WIDE(vsrc2)); \
+ } \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_SHX_LONG(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER_WIDE(vdst, \
+ _cast GET_REGISTER_WIDE(vsrc1) _op (GET_REGISTER(vsrc2) & 0x3f)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_LONG_2ADDR(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ if (_chkdiv != 0) { \
+ s8 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER_WIDE(vdst); \
+ secondVal = GET_REGISTER_WIDE(vsrc1); \
+ if (secondVal == 0LL) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u8)firstVal == 0x8000000000000000ULL && \
+ secondVal == -1LL) \
+ { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER_WIDE(vdst, result); \
+ } else { \
+ SET_REGISTER_WIDE(vdst, \
+ (s8) GET_REGISTER_WIDE(vdst) _op (s8)GET_REGISTER_WIDE(vsrc1));\
+ } \
+ FINISH(1);
+
+#define HANDLE_OP_SHX_LONG_2ADDR(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER_WIDE(vdst, \
+ _cast GET_REGISTER_WIDE(vdst) _op (GET_REGISTER(vsrc1) & 0x3f)); \
+ FINISH(1);
+
+#define HANDLE_OP_X_FLOAT(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-float v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER_FLOAT(vdst, \
+ GET_REGISTER_FLOAT(vsrc1) _op GET_REGISTER_FLOAT(vsrc2)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_DOUBLE(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-double v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER_DOUBLE(vdst, \
+ GET_REGISTER_DOUBLE(vsrc1) _op GET_REGISTER_DOUBLE(vsrc2)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_FLOAT_2ADDR(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-float-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER_FLOAT(vdst, \
+ GET_REGISTER_FLOAT(vdst) _op GET_REGISTER_FLOAT(vsrc1)); \
+ FINISH(1);
+
+#define HANDLE_OP_X_DOUBLE_2ADDR(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-double-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER_DOUBLE(vdst, \
+ GET_REGISTER_DOUBLE(vdst) _op GET_REGISTER_DOUBLE(vsrc1)); \
+ FINISH(1);
+
+#define HANDLE_OP_AGET(_opcode, _opname, _type, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ ArrayObject* arrayObj; \
+ u2 arrayInfo; \
+ EXPORT_PC(); \
+ vdst = INST_AA(inst); \
+ arrayInfo = FETCH(1); \
+ vsrc1 = arrayInfo & 0xff; /* array ptr */ \
+ vsrc2 = arrayInfo >> 8; /* index */ \
+ ILOGV("|aget%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ arrayObj = (ArrayObject*) GET_REGISTER(vsrc1); \
+ if (!checkForNull((Object*) arrayObj)) \
+ GOTO_exceptionThrown(); \
+ if (GET_REGISTER(vsrc2) >= arrayObj->length) { \
+ LOGV("Invalid array access: %p %d (len=%d)\n", \
+ arrayObj, vsrc2, arrayObj->length); \
+ dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+ NULL); \
+ GOTO_exceptionThrown(); \
+ } \
+ SET_REGISTER##_regsize(vdst, \
+ ((_type*) arrayObj->contents)[GET_REGISTER(vsrc2)]); \
+ ILOGV("+ AGET[%d]=0x%x", GET_REGISTER(vsrc2), GET_REGISTER(vdst)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_APUT(_opcode, _opname, _type, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ ArrayObject* arrayObj; \
+ u2 arrayInfo; \
+ EXPORT_PC(); \
+ vdst = INST_AA(inst); /* AA: source value */ \
+ arrayInfo = FETCH(1); \
+ vsrc1 = arrayInfo & 0xff; /* BB: array ptr */ \
+ vsrc2 = arrayInfo >> 8; /* CC: index */ \
+ ILOGV("|aput%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ arrayObj = (ArrayObject*) GET_REGISTER(vsrc1); \
+ if (!checkForNull((Object*) arrayObj)) \
+ GOTO_exceptionThrown(); \
+ if (GET_REGISTER(vsrc2) >= arrayObj->length) { \
+ dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+ NULL); \
+ GOTO_exceptionThrown(); \
+ } \
+ ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+ ((_type*) arrayObj->contents)[GET_REGISTER(vsrc2)] = \
+ GET_REGISTER##_regsize(vdst); \
+ } \
+ FINISH(2);
+
+/*
+ * It's possible to get a bad value out of a field with sub-32-bit stores
+ * because the -quick versions always operate on 32 bits. Consider:
+ * short foo = -1 (sets a 32-bit register to 0xffffffff)
+ * iput-quick foo (writes all 32 bits to the field)
+ * short bar = 1 (sets a 32-bit register to 0x00000001)
+ * iput-short (writes the low 16 bits to the field)
+ * iget-quick foo (reads all 32 bits from the field, yielding 0xffff0001)
+ * This can only happen when optimized and non-optimized code has interleaved
+ * access to the same field. This is unlikely but possible.
+ *
+ * The easiest way to fix this is to always read/write 32 bits at a time. On
+ * a device with a 16-bit data bus this is sub-optimal. (The alternative
+ * approach is to have sub-int versions of iget-quick, but now we're wasting
+ * Dalvik instruction space and making it less likely that handler code will
+ * already be in the CPU i-cache.)
+ */
+#define HANDLE_IGET_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ InstField* ifield; \
+ Object* obj; \
+ EXPORT_PC(); \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|iget%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNull(obj)) \
+ GOTO_exceptionThrown(); \
+ ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref); \
+ if (ifield == NULL) { \
+ ifield = dvmResolveInstField(curMethod->clazz, ref); \
+ if (ifield == NULL) \
+ GOTO_exceptionThrown(); \
+ } \
+ SET_REGISTER##_regsize(vdst, \
+ dvmGetField##_ftype(obj, ifield->byteOffset)); \
+ ILOGV("+ IGET '%s'=0x%08llx", ifield->field.name, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_GET(&ifield->field); \
+ } \
+ FINISH(2);
+
+#define HANDLE_IGET_X_QUICK(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ Object* obj; \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field offset */ \
+ ILOGV("|iget%s-quick v%d,v%d,field@+%u", \
+ (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNullExportPC(obj, fp, pc)) \
+ GOTO_exceptionThrown(); \
+ SET_REGISTER##_regsize(vdst, dvmGetField##_ftype(obj, ref)); \
+ ILOGV("+ IGETQ %d=0x%08llx", ref, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_IPUT_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ InstField* ifield; \
+ Object* obj; \
+ EXPORT_PC(); \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|iput%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNull(obj)) \
+ GOTO_exceptionThrown(); \
+ ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref); \
+ if (ifield == NULL) { \
+ ifield = dvmResolveInstField(curMethod->clazz, ref); \
+ if (ifield == NULL) \
+ GOTO_exceptionThrown(); \
+ } \
+ dvmSetField##_ftype(obj, ifield->byteOffset, \
+ GET_REGISTER##_regsize(vdst)); \
+ ILOGV("+ IPUT '%s'=0x%08llx", ifield->field.name, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_PUT(&ifield->field); \
+ } \
+ FINISH(2);
+
+#define HANDLE_IPUT_X_QUICK(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ Object* obj; \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field offset */ \
+ ILOGV("|iput%s-quick v%d,v%d,field@0x%04x", \
+ (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNullExportPC(obj, fp, pc)) \
+ GOTO_exceptionThrown(); \
+ dvmSetField##_ftype(obj, ref, GET_REGISTER##_regsize(vdst)); \
+ ILOGV("+ IPUTQ %d=0x%08llx", ref, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ } \
+ FINISH(2);
+
+/*
+ * The JIT needs dvmDexGetResolvedField() to return non-null.
+ * Since we use the portable interpreter to build the trace, the extra
+ * checks in HANDLE_SGET_X and HANDLE_SPUT_X are not needed for mterp.
+ */
+#define HANDLE_SGET_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/) \
+ { \
+ StaticField* sfield; \
+ vdst = INST_AA(inst); \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|sget%s v%d,sfield@0x%04x", (_opname), vdst, ref); \
+ sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+ if (sfield == NULL) { \
+ EXPORT_PC(); \
+ sfield = dvmResolveStaticField(curMethod->clazz, ref); \
+ if (sfield == NULL) \
+ GOTO_exceptionThrown(); \
+ if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) { \
+ ABORT_JIT_TSELECT(); \
+ } \
+ } \
+ SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield)); \
+ ILOGV("+ SGET '%s'=0x%08llx", \
+ sfield->field.name, (u8)GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_GET(&sfield->field); \
+ } \
+ FINISH(2);
+
+#define HANDLE_SPUT_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/) \
+ { \
+ StaticField* sfield; \
+ vdst = INST_AA(inst); \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|sput%s v%d,sfield@0x%04x", (_opname), vdst, ref); \
+ sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+ if (sfield == NULL) { \
+ EXPORT_PC(); \
+ sfield = dvmResolveStaticField(curMethod->clazz, ref); \
+ if (sfield == NULL) \
+ GOTO_exceptionThrown(); \
+ if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) { \
+ ABORT_JIT_TSELECT(); \
+ } \
+ } \
+ dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst)); \
+ ILOGV("+ SPUT '%s'=0x%08llx", \
+ sfield->field.name, (u8)GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_PUT(&sfield->field); \
+ } \
+ FINISH(2);
diff --git a/vm/mterp/common/FindInterface.h b/vm/mterp/common/FindInterface.h
new file mode 100644
index 0000000..021ed65
--- /dev/null
+++ b/vm/mterp/common/FindInterface.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* common includes */
+#include "Dalvik.h"
+
+/*
+ * Look up an interface on a class using the cache.
+ *
+ * This function used to be defined in mterp/c/header.c, but it is now used by
+ * the JIT compiler as well so it is separated into its own header file to
+ * avoid potential out-of-sync changes in the future.
+ */
+INLINE Method* dvmFindInterfaceMethodInCache(ClassObject* thisClass,
+ u4 methodIdx, const Method* method, DvmDex* methodClassDex)
+{
+#define ATOMIC_CACHE_CALC \
+ dvmInterpFindInterfaceMethod(thisClass, methodIdx, method, methodClassDex)
+
+ return (Method*) ATOMIC_CACHE_LOOKUP(methodClassDex->pInterfaceCache,
+ DEX_INTERFACE_CACHE_SIZE, thisClass, methodIdx);
+
+#undef ATOMIC_CACHE_CALC
+}
diff --git a/vm/mterp/common/asm-constants.h b/vm/mterp/common/asm-constants.h
new file mode 100644
index 0000000..aeed88b
--- /dev/null
+++ b/vm/mterp/common/asm-constants.h
@@ -0,0 +1,326 @@
+/*
+ * Copyright 2008 The Android Open Source Project
+ *
+ * Constants used by the assembler and verified by the C compiler.
+ */
+
+#if defined(ASM_DEF_VERIFY)
+ /*
+ * Generate C fragments that verify values; assumes "bool failed" exists.
+ * These are all constant expressions, so on success these will compile
+ * down to nothing.
+ */
+# define MTERP_OFFSET(_name, _type, _field, _offset) \
+ if (offsetof(_type, _field) != _offset) { \
+ LOGE("Bad asm offset %s (%d), should be %d\n", \
+ #_name, _offset, offsetof(_type, _field)); \
+ failed = true; \
+ }
+# define MTERP_SIZEOF(_name, _type, _size) \
+ if (sizeof(_type) != (_size)) { \
+ LOGE("Bad asm sizeof %s (%d), should be %d\n", \
+ #_name, (_size), sizeof(_type)); \
+ failed = true; \
+ }
+# define MTERP_CONSTANT(_name, _value) \
+ if ((_name) != (_value)) { \
+ LOGE("Bad asm constant %s (%d), should be %d\n", \
+ #_name, (_value), (_name)); \
+ failed = true; \
+ }
+#else
+ /* generate constant labels for the assembly output */
+# define MTERP_OFFSET(name, type, field, offset) name = offset
+# define MTERP_SIZEOF(name, type, size) name = size
+# define MTERP_CONSTANT(name, value) name = value
+#endif
+
+/*
+ * Platform dependencies. Some platforms require 64-bit alignment of 64-bit
+ * data structures. Some versions of gcc will hold small enumerated types
+ * in a char instead of an int.
+ */
+#if defined(__ARM_EABI__)
+# define MTERP_NO_UNALIGN_64
+#endif
+#if defined(HAVE_SHORT_ENUMS)
+# define MTERP_SMALL_ENUM 1
+#else
+# define MTERP_SMALL_ENUM 4
+#endif
+
+/*
+ * This file must only contain the following kinds of statements:
+ *
+ * MTERP_OFFSET(name, StructType, fieldname, offset)
+ *
+ * Declares that the expected offset of StructType.fieldname is "offset".
+ * This will break whenever the contents of StructType are rearranged.
+ *
+ * MTERP_SIZEOF(name, Type, size)
+ *
+ * Declares that the expected size of Type is "size".
+ *
+ * MTERP_CONSTANT(name, value)
+ *
+ * Declares that the expected value of "name" is "value". Useful for
+ * enumerations and defined constants that are inaccessible to the
+ * assembly source. (Note this assumes you will use the same name in
+ * both C and assembly, which is good practice.)
+ *
+ * In all cases the "name" field is the label you will use in the assembler.
+ *
+ * The "value" field must always be an actual number, not a symbol, unless
+ * you are sure that the symbol's value will be visible to both C and
+ * assembly sources. There may be restrictions on the possible range of
+ * values (which are usually provided as immediate operands), so it's best
+ * to restrict numbers assuming a signed 8-bit field.
+ *
+ * On the assembly side, these just become "name=value" constants. On the
+ * C side, these turn into assertions that cause the VM to abort if the
+ * values are incorrect.
+ */
+
+/* globals (sanity check for LDR vs LDRB) */
+MTERP_SIZEOF(sizeofGlobal_debuggerActive, gDvm.debuggerActive, 1)
+MTERP_SIZEOF(sizeofGlobal_activeProfilers, gDvm.activeProfilers, 4)
+
+/* MterpGlue fields */
+MTERP_OFFSET(offGlue_pc, MterpGlue, pc, 0)
+MTERP_OFFSET(offGlue_fp, MterpGlue, fp, 4)
+MTERP_OFFSET(offGlue_retval, MterpGlue, retval, 8)
+MTERP_OFFSET(offGlue_method, MterpGlue, method, 16)
+MTERP_OFFSET(offGlue_methodClassDex, MterpGlue, methodClassDex, 20)
+MTERP_OFFSET(offGlue_self, MterpGlue, self, 24)
+MTERP_OFFSET(offGlue_bailPtr, MterpGlue, bailPtr, 28)
+MTERP_OFFSET(offGlue_interpStackEnd, MterpGlue, interpStackEnd, 32)
+MTERP_OFFSET(offGlue_pSelfSuspendCount, MterpGlue, pSelfSuspendCount, 36)
+MTERP_OFFSET(offGlue_cardTable, MterpGlue, cardTable, 40)
+MTERP_OFFSET(offGlue_pDebuggerActive, MterpGlue, pDebuggerActive, 44)
+MTERP_OFFSET(offGlue_pActiveProfilers, MterpGlue, pActiveProfilers, 48)
+MTERP_OFFSET(offGlue_entryPoint, MterpGlue, entryPoint, 52)
+#if defined(WITH_JIT)
+MTERP_OFFSET(offGlue_pJitProfTable, MterpGlue, pJitProfTable, 60)
+MTERP_OFFSET(offGlue_jitState, MterpGlue, jitState, 64)
+MTERP_OFFSET(offGlue_jitResumeNPC, MterpGlue, jitResumeNPC, 68)
+MTERP_OFFSET(offGlue_jitResumeDPC, MterpGlue, jitResumeDPC, 72)
+MTERP_OFFSET(offGlue_jitThreshold, MterpGlue, jitThreshold, 76)
+MTERP_OFFSET(offGlue_ppJitProfTable, MterpGlue, ppJitProfTable, 80)
+MTERP_OFFSET(offGlue_icRechainCount, MterpGlue, icRechainCount, 84)
+#endif
+/* make sure all JValue union members are stored at the same offset */
+MTERP_OFFSET(offGlue_retval_z, MterpGlue, retval.z, 8)
+MTERP_OFFSET(offGlue_retval_i, MterpGlue, retval.i, 8)
+MTERP_OFFSET(offGlue_retval_j, MterpGlue, retval.j, 8)
+MTERP_OFFSET(offGlue_retval_l, MterpGlue, retval.l, 8)
+
+/* DvmDex fields */
+MTERP_OFFSET(offDvmDex_pResStrings, DvmDex, pResStrings, 8)
+MTERP_OFFSET(offDvmDex_pResClasses, DvmDex, pResClasses, 12)
+MTERP_OFFSET(offDvmDex_pResMethods, DvmDex, pResMethods, 16)
+MTERP_OFFSET(offDvmDex_pResFields, DvmDex, pResFields, 20)
+MTERP_OFFSET(offDvmDex_pInterfaceCache, DvmDex, pInterfaceCache, 24)
+
+/* StackSaveArea fields */
+#ifdef EASY_GDB
+MTERP_OFFSET(offStackSaveArea_prevSave, StackSaveArea, prevSave, 0)
+MTERP_OFFSET(offStackSaveArea_prevFrame, StackSaveArea, prevFrame, 4)
+MTERP_OFFSET(offStackSaveArea_savedPc, StackSaveArea, savedPc, 8)
+MTERP_OFFSET(offStackSaveArea_method, StackSaveArea, method, 12)
+MTERP_OFFSET(offStackSaveArea_currentPc, StackSaveArea, xtra.currentPc, 16)
+MTERP_OFFSET(offStackSaveArea_localRefCookie, \
+ StackSaveArea, xtra.localRefCookie, 16)
+MTERP_OFFSET(offStackSaveArea_returnAddr, StackSaveArea, returnAddr, 20)
+MTERP_SIZEOF(sizeofStackSaveArea, StackSaveArea, 24)
+#else
+MTERP_OFFSET(offStackSaveArea_prevFrame, StackSaveArea, prevFrame, 0)
+MTERP_OFFSET(offStackSaveArea_savedPc, StackSaveArea, savedPc, 4)
+MTERP_OFFSET(offStackSaveArea_method, StackSaveArea, method, 8)
+MTERP_OFFSET(offStackSaveArea_currentPc, StackSaveArea, xtra.currentPc, 12)
+MTERP_OFFSET(offStackSaveArea_localRefCookie, \
+ StackSaveArea, xtra.localRefCookie, 12)
+MTERP_OFFSET(offStackSaveArea_returnAddr, StackSaveArea, returnAddr, 16)
+MTERP_SIZEOF(sizeofStackSaveArea, StackSaveArea, 20)
+#endif
+
+ /* ShadowSpace fields */
+#if defined(WITH_JIT) && defined(WITH_SELF_VERIFICATION)
+MTERP_OFFSET(offShadowSpace_startPC, ShadowSpace, startPC, 0)
+MTERP_OFFSET(offShadowSpace_fp, ShadowSpace, fp, 4)
+MTERP_OFFSET(offShadowSpace_glue, ShadowSpace, glue, 8)
+MTERP_OFFSET(offShadowSpace_jitExitState,ShadowSpace, jitExitState, 12)
+MTERP_OFFSET(offShadowSpace_svState, ShadowSpace, selfVerificationState, 16)
+MTERP_OFFSET(offShadowSpace_shadowFP, ShadowSpace, shadowFP, 24)
+MTERP_OFFSET(offShadowSpace_interpState, ShadowSpace, interpState, 32)
+#endif
+
+/* InstField fields */
+#ifdef PROFILE_FIELD_ACCESS
+MTERP_OFFSET(offInstField_byteOffset, InstField, byteOffset, 24)
+#else
+MTERP_OFFSET(offInstField_byteOffset, InstField, byteOffset, 16)
+#endif
+
+/* Field fields */
+MTERP_OFFSET(offField_clazz, Field, clazz, 0)
+
+/* StaticField fields */
+#ifdef PROFILE_FIELD_ACCESS
+MTERP_OFFSET(offStaticField_value, StaticField, value, 24)
+#else
+MTERP_OFFSET(offStaticField_value, StaticField, value, 16)
+#endif
+
+/* Method fields */
+MTERP_OFFSET(offMethod_clazz, Method, clazz, 0)
+MTERP_OFFSET(offMethod_accessFlags, Method, accessFlags, 4)
+MTERP_OFFSET(offMethod_methodIndex, Method, methodIndex, 8)
+MTERP_OFFSET(offMethod_registersSize, Method, registersSize, 10)
+MTERP_OFFSET(offMethod_outsSize, Method, outsSize, 12)
+MTERP_OFFSET(offMethod_name, Method, name, 16)
+MTERP_OFFSET(offMethod_insns, Method, insns, 32)
+MTERP_OFFSET(offMethod_nativeFunc, Method, nativeFunc, 40)
+
+/* InlineOperation fields -- code assumes "func" offset is zero, do not alter */
+MTERP_OFFSET(offInlineOperation_func, InlineOperation, func, 0)
+
+/* Thread fields */
+MTERP_OFFSET(offThread_stackOverflowed, Thread, stackOverflowed, 36)
+MTERP_OFFSET(offThread_curFrame, Thread, curFrame, 40)
+MTERP_OFFSET(offThread_exception, Thread, exception, 44)
+
+#if defined(WITH_JIT)
+MTERP_OFFSET(offThread_inJitCodeCache, Thread, inJitCodeCache, 72)
+#if defined(WITH_SELF_VERIFICATION)
+MTERP_OFFSET(offThread_shadowSpace, Thread, shadowSpace, 76)
+#ifdef USE_INDIRECT_REF
+MTERP_OFFSET(offThread_jniLocal_topCookie, \
+ Thread, jniLocalRefTable.segmentState.all, 80)
+#else
+MTERP_OFFSET(offThread_jniLocal_topCookie, \
+ Thread, jniLocalRefTable.nextEntry, 80)
+#endif
+#else
+#ifdef USE_INDIRECT_REF
+MTERP_OFFSET(offThread_jniLocal_topCookie, \
+ Thread, jniLocalRefTable.segmentState.all, 76)
+#else
+MTERP_OFFSET(offThread_jniLocal_topCookie, \
+ Thread, jniLocalRefTable.nextEntry, 76)
+#endif
+#endif
+#else
+#ifdef USE_INDIRECT_REF
+MTERP_OFFSET(offThread_jniLocal_topCookie, \
+ Thread, jniLocalRefTable.segmentState.all, 72)
+#else
+MTERP_OFFSET(offThread_jniLocal_topCookie, \
+ Thread, jniLocalRefTable.nextEntry, 72)
+#endif
+#endif
+
+/* Object fields */
+MTERP_OFFSET(offObject_clazz, Object, clazz, 0)
+MTERP_OFFSET(offObject_lock, Object, lock, 4)
+
+/* Lock shape */
+MTERP_CONSTANT(LW_LOCK_OWNER_SHIFT, 3)
+MTERP_CONSTANT(LW_HASH_STATE_SHIFT, 1)
+
+/* ArrayObject fields */
+MTERP_OFFSET(offArrayObject_length, ArrayObject, length, 8)
+#ifdef MTERP_NO_UNALIGN_64
+MTERP_OFFSET(offArrayObject_contents, ArrayObject, contents, 16)
+#else
+MTERP_OFFSET(offArrayObject_contents, ArrayObject, contents, 12)
+#endif
+
+/* String fields */
+MTERP_CONSTANT(STRING_FIELDOFF_VALUE, 8)
+MTERP_CONSTANT(STRING_FIELDOFF_HASHCODE, 12)
+MTERP_CONSTANT(STRING_FIELDOFF_OFFSET, 16)
+MTERP_CONSTANT(STRING_FIELDOFF_COUNT, 20)
+
+#if defined(WITH_JIT)
+/*
+ * Reasons for the non-chaining interpreter entry points
+ * Enums defined in vm/Globals.h
+ */
+MTERP_CONSTANT(kInlineCacheMiss, 0)
+MTERP_CONSTANT(kCallsiteInterpreted, 1)
+MTERP_CONSTANT(kSwitchOverflow, 2)
+MTERP_CONSTANT(kHeavyweightMonitor, 3)
+
+/* Size of callee save area */
+MTERP_CONSTANT(JIT_CALLEE_SAVE_DOUBLE_COUNT, 8)
+#endif
+
+/* ClassObject fields */
+MTERP_OFFSET(offClassObject_descriptor, ClassObject, descriptor, 24)
+MTERP_OFFSET(offClassObject_accessFlags, ClassObject, accessFlags, 32)
+MTERP_OFFSET(offClassObject_pDvmDex, ClassObject, pDvmDex, 40)
+MTERP_OFFSET(offClassObject_status, ClassObject, status, 44)
+MTERP_OFFSET(offClassObject_super, ClassObject, super, 72)
+MTERP_OFFSET(offClassObject_vtableCount, ClassObject, vtableCount, 112)
+MTERP_OFFSET(offClassObject_vtable, ClassObject, vtable, 116)
+
+/* InterpEntry enumeration */
+MTERP_SIZEOF(sizeofClassStatus, InterpEntry, MTERP_SMALL_ENUM)
+MTERP_CONSTANT(kInterpEntryInstr, 0)
+MTERP_CONSTANT(kInterpEntryReturn, 1)
+MTERP_CONSTANT(kInterpEntryThrow, 2)
+#if defined(WITH_JIT)
+MTERP_CONSTANT(kInterpEntryResume, 3)
+#endif
+
+#if defined(WITH_JIT)
+MTERP_CONSTANT(kJitNot, 0)
+MTERP_CONSTANT(kJitTSelectRequest, 1)
+MTERP_CONSTANT(kJitTSelectRequestHot, 2)
+MTERP_CONSTANT(kJitSelfVerification, 3)
+MTERP_CONSTANT(kJitTSelect, 4)
+MTERP_CONSTANT(kJitTSelectEnd, 5)
+MTERP_CONSTANT(kJitSingleStep, 6)
+MTERP_CONSTANT(kJitSingleStepEnd, 7)
+MTERP_CONSTANT(kJitDone, 8)
+
+#if defined(WITH_SELF_VERIFICATION)
+MTERP_CONSTANT(kSVSIdle, 0)
+MTERP_CONSTANT(kSVSStart, 1)
+MTERP_CONSTANT(kSVSPunt, 2)
+MTERP_CONSTANT(kSVSSingleStep, 3)
+MTERP_CONSTANT(kSVSNoProfile, 4)
+MTERP_CONSTANT(kSVSTraceSelect, 5)
+MTERP_CONSTANT(kSVSNormal, 6)
+MTERP_CONSTANT(kSVSNoChain, 7)
+MTERP_CONSTANT(kSVSBackwardBranch, 8)
+MTERP_CONSTANT(kSVSDebugInterp, 9)
+#endif
+#endif
+
+/* ClassStatus enumeration */
+MTERP_SIZEOF(sizeofClassStatus, ClassStatus, MTERP_SMALL_ENUM)
+MTERP_CONSTANT(CLASS_INITIALIZED, 7)
+
+/* MethodType enumeration */
+MTERP_SIZEOF(sizeofMethodType, MethodType, MTERP_SMALL_ENUM)
+MTERP_CONSTANT(METHOD_DIRECT, 1)
+MTERP_CONSTANT(METHOD_STATIC, 2)
+MTERP_CONSTANT(METHOD_VIRTUAL, 3)
+MTERP_CONSTANT(METHOD_INTERFACE, 4)
+
+/* ClassObject constants */
+MTERP_CONSTANT(ACC_PRIVATE, 0x0002)
+MTERP_CONSTANT(ACC_STATIC, 0x0008)
+MTERP_CONSTANT(ACC_NATIVE, 0x0100)
+MTERP_CONSTANT(ACC_INTERFACE, 0x0200)
+MTERP_CONSTANT(ACC_ABSTRACT, 0x0400)
+
+/* flags for dvmMalloc */
+MTERP_CONSTANT(ALLOC_DONT_TRACK, 0x01)
+
+/* for GC */
+MTERP_CONSTANT(GC_CARD_SHIFT, 7)
+
+/* opcode number */
+MTERP_CONSTANT(OP_MOVE_EXCEPTION, 0x0d)
diff --git a/vm/mterp/common/jit-config.h b/vm/mterp/common/jit-config.h
new file mode 100644
index 0000000..8cc32e3
--- /dev/null
+++ b/vm/mterp/common/jit-config.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if __ARM_ARCH_5TE__
+#define JIT_PROF_SIZE_LOG_2 9
+#else
+#define JIT_PROF_SIZE_LOG_2 11
+#endif
+
+#define JIT_PROF_SIZE (1 << JIT_PROF_SIZE_LOG_2)
diff --git a/vm/mterp/config-allstubs b/vm/mterp/config-allstubs
new file mode 100644
index 0000000..23796d8
--- /dev/null
+++ b/vm/mterp/config-allstubs
@@ -0,0 +1,44 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Configuration for "allstubs" target. This is structured like the
+# assembly interpreters, but consists entirely of C stubs, making it
+# a handy if inefficient way to exercise all of the C handlers.
+#
+
+handler-size 64
+
+# C file header and basic definitions
+import c/header.c
+
+# C pre-processor defines for stub C instructions
+import cstubs/stubdefs.c
+
+# common defs for the C opcodes
+import c/opcommon.c
+
+# opcode list; argument to op-start is default directory
+op-start c
+ # use nothing but C stubs
+op-end
+
+# arch-specific entry point to interpreter
+import cstubs/entry.c
+
+# "helper" code
+import c/gotoTargets.c
+
+# finish
+import cstubs/enddefs.c
diff --git a/vm/mterp/config-armv4t b/vm/mterp/config-armv4t
new file mode 100644
index 0000000..ed39b42
--- /dev/null
+++ b/vm/mterp/config-armv4t
@@ -0,0 +1,68 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Configuration for ARMv4T architecture targets. This is largely pulled
+# from the ARMv5TE sources, but we can't use certain instructions introduced
+# in ARMv5 (BLX, CLZ, LDC2, MCR2, MRC2, STC2) or ARMv5TE (PLD, LDRD, MCRR,
+# MRRC, QADD, QDADD, QDSUB, QSUB, SMLA, SMLAL, SMLAW, SMUL, SMULW, STRD).
+#
+
+handler-size 64
+
+# source for the instruction table stub
+asm-stub armv5te/stub.S
+
+# file header and basic definitions
+import c/header.c
+import armv5te/header.S
+
+# C pre-processor defines for stub C instructions
+import cstubs/stubdefs.c
+
+# highly-platform-specific defs
+import armv5te/platform.S
+
+# common defs for the C helpers; include this before the instruction handlers
+import c/opcommon.c
+
+# arch-specific entry point to interpreter
+import armv5te/entry.S
+
+# opcode list; argument to op-start is default directory
+op-start armv5te
+ op OP_AGET_WIDE armv4t
+ op OP_APUT_WIDE armv4t
+ op OP_IGET_WIDE armv4t
+ op OP_IGET_WIDE_QUICK armv4t
+ op OP_IPUT_WIDE armv4t
+ op OP_IPUT_WIDE_QUICK armv4t
+ op OP_SGET_WIDE armv4t
+ op OP_SPUT_WIDE armv4t
+ op OP_IGET_WIDE_VOLATILE armv4t
+ op OP_IPUT_WIDE_VOLATILE armv4t
+ op OP_SGET_WIDE_VOLATILE armv4t
+ op OP_SPUT_WIDE_VOLATILE armv4t
+op-end
+
+# "helper" code for C; include if you use any of the C stubs (this generates
+# object code, so it's normally excluded)
+#import c/gotoTargets.c
+
+# end of defs; include this when cstubs/stubdefs.c is included
+import cstubs/enddefs.c
+
+# common subroutines for asm
+import armv5te/footer.S
+import armv5te/debug.c
diff --git a/vm/mterp/config-armv5te b/vm/mterp/config-armv5te
new file mode 100644
index 0000000..2dceb04
--- /dev/null
+++ b/vm/mterp/config-armv5te
@@ -0,0 +1,54 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Configuration for ARMv5TE architecture targets.
+#
+
+handler-size 64
+
+# source for the instruction table stub
+asm-stub armv5te/stub.S
+
+# file header and basic definitions
+import c/header.c
+import armv5te/header.S
+
+# C pre-processor defines for stub C instructions
+import cstubs/stubdefs.c
+
+# highly-platform-specific defs
+import armv5te/platform.S
+
+# common defs for the C helpers; include this before the instruction handlers
+import c/opcommon.c
+
+# arch-specific entry point to interpreter
+import armv5te/entry.S
+
+# opcode list; argument to op-start is default directory
+op-start armv5te
+ #op OP_FILL_ARRAY_DATA c
+op-end
+
+# "helper" code for C; include if you use any of the C stubs (this generates
+# object code, so it's normally excluded)
+##import c/gotoTargets.c
+
+# end of defs; include this when cstubs/stubdefs.c is included
+import cstubs/enddefs.c
+
+# common subroutines for asm
+import armv5te/footer.S
+import armv5te/debug.c
diff --git a/vm/mterp/config-armv5te-vfp b/vm/mterp/config-armv5te-vfp
new file mode 100644
index 0000000..ce0c521
--- /dev/null
+++ b/vm/mterp/config-armv5te-vfp
@@ -0,0 +1,104 @@
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Configuration for ARMv5TE targets with VFP support.
+#
+# This is just ARMv5TE with replacements for the handlers that can benefit
+# from floating-point instructions. Essentially all float/double
+# operations except for "remainder" and conversions to/from 64-bit ints.
+#
+
+handler-size 64
+
+# source for the instruction table stub
+asm-stub armv5te/stub.S
+
+# file header and basic definitions
+import c/header.c
+import armv5te/header.S
+
+# C pre-processor defines for stub C instructions
+import cstubs/stubdefs.c
+
+# highly-platform-specific defs
+import armv5te/platform.S
+
+# common defs for the C helpers; include this before the instruction handlers
+import c/opcommon.c
+
+# arch-specific entry point to interpreter
+import armv5te/entry.S
+
+# opcode list; argument to op-start is default directory
+op-start armv5te
+ op OP_ADD_DOUBLE arm-vfp
+ op OP_ADD_DOUBLE_2ADDR arm-vfp
+ op OP_ADD_FLOAT arm-vfp
+ op OP_ADD_FLOAT_2ADDR arm-vfp
+ op OP_CMPG_DOUBLE arm-vfp
+ op OP_CMPG_FLOAT arm-vfp
+ op OP_CMPL_DOUBLE arm-vfp
+ op OP_CMPL_FLOAT arm-vfp
+ op OP_DIV_DOUBLE arm-vfp
+ op OP_DIV_DOUBLE_2ADDR arm-vfp
+ op OP_DIV_FLOAT arm-vfp
+ op OP_DIV_FLOAT_2ADDR arm-vfp
+ op OP_DOUBLE_TO_FLOAT arm-vfp
+ op OP_DOUBLE_TO_INT arm-vfp
+ op OP_FLOAT_TO_DOUBLE arm-vfp
+ op OP_FLOAT_TO_INT arm-vfp
+ op OP_INT_TO_DOUBLE arm-vfp
+ op OP_INT_TO_FLOAT arm-vfp
+ op OP_MUL_DOUBLE arm-vfp
+ op OP_MUL_DOUBLE_2ADDR arm-vfp
+ op OP_MUL_FLOAT arm-vfp
+ op OP_MUL_FLOAT_2ADDR arm-vfp
+ op OP_SUB_DOUBLE arm-vfp
+ op OP_SUB_DOUBLE_2ADDR arm-vfp
+ op OP_SUB_FLOAT arm-vfp
+ op OP_SUB_FLOAT_2ADDR arm-vfp
+
+ # use trivial integer operation
+ #op OP_NEG_DOUBLE armv5te
+ #op OP_NEG_FLOAT armv5te
+
+ # use __aeabi_* functions
+ #op OP_DOUBLE_TO_LONG armv5te
+ #op OP_FLOAT_TO_LONG armv5te
+ #op OP_LONG_TO_DOUBLE armv5te
+ #op OP_LONG_TO_FLOAT armv5te
+
+ # no "remainder" op in vfp or libgcc.a; use libc function
+ #op OP_REM_DOUBLE armv5te
+ #op OP_REM_DOUBLE_2ADDR armv5te
+ #op OP_REM_FLOAT armv5te
+ #op OP_REM_FLOAT_2ADDR armv5te
+
+ # experiment, unrelated to vfp
+ #op OP_INT_TO_BYTE armv6
+ #op OP_INT_TO_CHAR armv6
+ #op OP_INT_TO_SHORT armv6
+op-end
+
+# "helper" code for C; include if you use any of the C stubs (this generates
+# object code, so it's normally excluded)
+##import c/gotoTargets.c
+
+# end of defs; include this when cstubs/stubdefs.c is included
+import cstubs/enddefs.c
+
+# common subroutines for asm
+import armv5te/footer.S
+import armv5te/debug.c
diff --git a/vm/mterp/config-armv7-a b/vm/mterp/config-armv7-a
new file mode 100644
index 0000000..e66640c
--- /dev/null
+++ b/vm/mterp/config-armv7-a
@@ -0,0 +1,166 @@
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Configuration for ARMv7-A targets.
+#
+# This target includes Thumb-2 and Thumb2-EE support, as well as VFPLite.
+#
+# The difference in performance between this and ARMv5TE appears to be
+# negligible on a Cortex-A8 CPU, so this is really just an experiment.
+#
+
+handler-size 64
+
+# source for the instruction table stub
+asm-stub armv5te/stub.S
+
+# file header and basic definitions
+import c/header.c
+import armv5te/header.S
+
+# C pre-processor defines for stub C instructions
+import cstubs/stubdefs.c
+
+# highly-platform-specific defs
+import armv7-a/platform.S
+
+# common defs for the C helpers; include this before the instruction handlers
+import c/opcommon.c
+
+# arch-specific entry point to interpreter
+import armv5te/entry.S
+
+# opcode list; argument to op-start is default directory
+op-start armv5te
+ # handlers that take advantage of >= ARMv6T2 instructions
+ op OP_ADD_DOUBLE_2ADDR armv6t2
+ op OP_ADD_FLOAT_2ADDR armv6t2
+ op OP_ADD_INT_2ADDR armv6t2
+ op OP_ADD_INT_LIT16 armv6t2
+ op OP_ADD_LONG_2ADDR armv6t2
+ op OP_AND_INT_2ADDR armv6t2
+ op OP_AND_INT_LIT16 armv6t2
+ op OP_AND_LONG_2ADDR armv6t2
+ op OP_ARRAY_LENGTH armv6t2
+ op OP_CONST_4 armv6t2
+ op OP_DIV_DOUBLE_2ADDR armv6t2
+ op OP_DIV_FLOAT_2ADDR armv6t2
+ op OP_DIV_INT_2ADDR armv6t2
+ op OP_DIV_INT_LIT16 armv6t2
+ op OP_DIV_LONG_2ADDR armv6t2
+ op OP_DOUBLE_TO_FLOAT armv6t2
+ op OP_DOUBLE_TO_INT armv6t2
+ op OP_DOUBLE_TO_LONG armv6t2
+ op OP_FLOAT_TO_DOUBLE armv6t2
+ op OP_FLOAT_TO_INT armv6t2
+ op OP_FLOAT_TO_LONG armv6t2
+ op OP_IF_EQ armv6t2
+ op OP_IF_GE armv6t2
+ op OP_IF_GT armv6t2
+ op OP_IF_LE armv6t2
+ op OP_IF_LT armv6t2
+ op OP_IF_NE armv6t2
+ op OP_IGET armv6t2
+ op OP_IGET_QUICK armv6t2
+ op OP_IGET_WIDE armv6t2
+ op OP_IGET_WIDE_QUICK armv6t2
+ op OP_INT_TO_BYTE armv6t2
+ op OP_INT_TO_CHAR armv6t2
+ op OP_INT_TO_DOUBLE armv6t2
+ op OP_INT_TO_FLOAT armv6t2
+ op OP_INT_TO_LONG armv6t2
+ op OP_INT_TO_SHORT armv6t2
+ op OP_IPUT armv6t2
+ op OP_IPUT_QUICK armv6t2
+ op OP_IPUT_WIDE armv6t2
+ op OP_IPUT_WIDE_QUICK armv6t2
+ op OP_LONG_TO_DOUBLE armv6t2
+ op OP_LONG_TO_FLOAT armv6t2
+ op OP_MOVE armv6t2
+ op OP_MOVE_WIDE armv6t2
+ op OP_MUL_DOUBLE_2ADDR armv6t2
+ op OP_MUL_FLOAT_2ADDR armv6t2
+ op OP_MUL_INT_2ADDR armv6t2
+ op OP_MUL_INT_LIT16 armv6t2
+ op OP_MUL_LONG_2ADDR armv6t2
+ op OP_NEG_DOUBLE armv6t2
+ op OP_NEG_FLOAT armv6t2
+ op OP_NEG_INT armv6t2
+ op OP_NEG_LONG armv6t2
+ op OP_NOT_INT armv6t2
+ op OP_NOT_LONG armv6t2
+ op OP_OR_INT_2ADDR armv6t2
+ op OP_OR_INT_LIT16 armv6t2
+ op OP_OR_LONG_2ADDR armv6t2
+ op OP_REM_DOUBLE_2ADDR armv6t2
+ op OP_REM_FLOAT_2ADDR armv6t2
+ op OP_REM_INT_2ADDR armv6t2
+ op OP_REM_INT_LIT16 armv6t2
+ op OP_REM_LONG_2ADDR armv6t2
+ op OP_RSUB_INT armv6t2
+ op OP_SHL_INT_2ADDR armv6t2
+ op OP_SHL_LONG_2ADDR armv6t2
+ op OP_SHR_INT_2ADDR armv6t2
+ op OP_SHR_LONG_2ADDR armv6t2
+ op OP_SUB_DOUBLE_2ADDR armv6t2
+ op OP_SUB_FLOAT_2ADDR armv6t2
+ op OP_SUB_INT_2ADDR armv6t2
+ op OP_SUB_LONG_2ADDR armv6t2
+ op OP_USHR_INT_2ADDR armv6t2
+ op OP_USHR_LONG_2ADDR armv6t2
+ op OP_XOR_INT_2ADDR armv6t2
+ op OP_XOR_INT_LIT16 armv6t2
+ op OP_XOR_LONG_2ADDR armv6t2
+
+ # floating point handlers that use VFP
+ # these override the handlers specified earlier
+ op OP_ADD_DOUBLE arm-vfp
+ op OP_ADD_DOUBLE_2ADDR arm-vfp
+ op OP_ADD_FLOAT arm-vfp
+ op OP_ADD_FLOAT_2ADDR arm-vfp
+ op OP_CMPG_DOUBLE arm-vfp
+ op OP_CMPG_FLOAT arm-vfp
+ op OP_CMPL_DOUBLE arm-vfp
+ op OP_CMPL_FLOAT arm-vfp
+ op OP_DIV_DOUBLE arm-vfp
+ op OP_DIV_DOUBLE_2ADDR arm-vfp
+ op OP_DIV_FLOAT arm-vfp
+ op OP_DIV_FLOAT_2ADDR arm-vfp
+ op OP_DOUBLE_TO_FLOAT arm-vfp
+ op OP_DOUBLE_TO_INT arm-vfp
+ op OP_FLOAT_TO_DOUBLE arm-vfp
+ op OP_FLOAT_TO_INT arm-vfp
+ op OP_INT_TO_DOUBLE arm-vfp
+ op OP_INT_TO_FLOAT arm-vfp
+ op OP_MUL_DOUBLE arm-vfp
+ op OP_MUL_DOUBLE_2ADDR arm-vfp
+ op OP_MUL_FLOAT arm-vfp
+ op OP_MUL_FLOAT_2ADDR arm-vfp
+ op OP_SUB_DOUBLE arm-vfp
+ op OP_SUB_DOUBLE_2ADDR arm-vfp
+ op OP_SUB_FLOAT arm-vfp
+ op OP_SUB_FLOAT_2ADDR arm-vfp
+op-end
+
+# "helper" code for C; include if you use any of the C stubs (this generates
+# object code, so it's normally excluded)
+##import c/gotoTargets.c
+
+# end of defs; include this when cstubs/stubdefs.c is included
+import cstubs/enddefs.c
+
+# common subroutines for asm
+import armv5te/footer.S
+import armv5te/debug.c
diff --git a/vm/mterp/config-armv7-a-neon b/vm/mterp/config-armv7-a-neon
new file mode 100644
index 0000000..e66640c
--- /dev/null
+++ b/vm/mterp/config-armv7-a-neon
@@ -0,0 +1,166 @@
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Configuration for ARMv7-A targets.
+#
+# This target includes Thumb-2 and Thumb2-EE support, as well as VFPLite.
+#
+# The difference in performance between this and ARMv5TE appears to be
+# negligible on a Cortex-A8 CPU, so this is really just an experiment.
+#
+
+handler-size 64
+
+# source for the instruction table stub
+asm-stub armv5te/stub.S
+
+# file header and basic definitions
+import c/header.c
+import armv5te/header.S
+
+# C pre-processor defines for stub C instructions
+import cstubs/stubdefs.c
+
+# highly-platform-specific defs
+import armv7-a/platform.S
+
+# common defs for the C helpers; include this before the instruction handlers
+import c/opcommon.c
+
+# arch-specific entry point to interpreter
+import armv5te/entry.S
+
+# opcode list; argument to op-start is default directory
+op-start armv5te
+ # handlers that take advantage of >= ARMv6T2 instructions
+ op OP_ADD_DOUBLE_2ADDR armv6t2
+ op OP_ADD_FLOAT_2ADDR armv6t2
+ op OP_ADD_INT_2ADDR armv6t2
+ op OP_ADD_INT_LIT16 armv6t2
+ op OP_ADD_LONG_2ADDR armv6t2
+ op OP_AND_INT_2ADDR armv6t2
+ op OP_AND_INT_LIT16 armv6t2
+ op OP_AND_LONG_2ADDR armv6t2
+ op OP_ARRAY_LENGTH armv6t2
+ op OP_CONST_4 armv6t2
+ op OP_DIV_DOUBLE_2ADDR armv6t2
+ op OP_DIV_FLOAT_2ADDR armv6t2
+ op OP_DIV_INT_2ADDR armv6t2
+ op OP_DIV_INT_LIT16 armv6t2
+ op OP_DIV_LONG_2ADDR armv6t2
+ op OP_DOUBLE_TO_FLOAT armv6t2
+ op OP_DOUBLE_TO_INT armv6t2
+ op OP_DOUBLE_TO_LONG armv6t2
+ op OP_FLOAT_TO_DOUBLE armv6t2
+ op OP_FLOAT_TO_INT armv6t2
+ op OP_FLOAT_TO_LONG armv6t2
+ op OP_IF_EQ armv6t2
+ op OP_IF_GE armv6t2
+ op OP_IF_GT armv6t2
+ op OP_IF_LE armv6t2
+ op OP_IF_LT armv6t2
+ op OP_IF_NE armv6t2
+ op OP_IGET armv6t2
+ op OP_IGET_QUICK armv6t2
+ op OP_IGET_WIDE armv6t2
+ op OP_IGET_WIDE_QUICK armv6t2
+ op OP_INT_TO_BYTE armv6t2
+ op OP_INT_TO_CHAR armv6t2
+ op OP_INT_TO_DOUBLE armv6t2
+ op OP_INT_TO_FLOAT armv6t2
+ op OP_INT_TO_LONG armv6t2
+ op OP_INT_TO_SHORT armv6t2
+ op OP_IPUT armv6t2
+ op OP_IPUT_QUICK armv6t2
+ op OP_IPUT_WIDE armv6t2
+ op OP_IPUT_WIDE_QUICK armv6t2
+ op OP_LONG_TO_DOUBLE armv6t2
+ op OP_LONG_TO_FLOAT armv6t2
+ op OP_MOVE armv6t2
+ op OP_MOVE_WIDE armv6t2
+ op OP_MUL_DOUBLE_2ADDR armv6t2
+ op OP_MUL_FLOAT_2ADDR armv6t2
+ op OP_MUL_INT_2ADDR armv6t2
+ op OP_MUL_INT_LIT16 armv6t2
+ op OP_MUL_LONG_2ADDR armv6t2
+ op OP_NEG_DOUBLE armv6t2
+ op OP_NEG_FLOAT armv6t2
+ op OP_NEG_INT armv6t2
+ op OP_NEG_LONG armv6t2
+ op OP_NOT_INT armv6t2
+ op OP_NOT_LONG armv6t2
+ op OP_OR_INT_2ADDR armv6t2
+ op OP_OR_INT_LIT16 armv6t2
+ op OP_OR_LONG_2ADDR armv6t2
+ op OP_REM_DOUBLE_2ADDR armv6t2
+ op OP_REM_FLOAT_2ADDR armv6t2
+ op OP_REM_INT_2ADDR armv6t2
+ op OP_REM_INT_LIT16 armv6t2
+ op OP_REM_LONG_2ADDR armv6t2
+ op OP_RSUB_INT armv6t2
+ op OP_SHL_INT_2ADDR armv6t2
+ op OP_SHL_LONG_2ADDR armv6t2
+ op OP_SHR_INT_2ADDR armv6t2
+ op OP_SHR_LONG_2ADDR armv6t2
+ op OP_SUB_DOUBLE_2ADDR armv6t2
+ op OP_SUB_FLOAT_2ADDR armv6t2
+ op OP_SUB_INT_2ADDR armv6t2
+ op OP_SUB_LONG_2ADDR armv6t2
+ op OP_USHR_INT_2ADDR armv6t2
+ op OP_USHR_LONG_2ADDR armv6t2
+ op OP_XOR_INT_2ADDR armv6t2
+ op OP_XOR_INT_LIT16 armv6t2
+ op OP_XOR_LONG_2ADDR armv6t2
+
+ # floating point handlers that use VFP
+ # these override the handlers specified earlier
+ op OP_ADD_DOUBLE arm-vfp
+ op OP_ADD_DOUBLE_2ADDR arm-vfp
+ op OP_ADD_FLOAT arm-vfp
+ op OP_ADD_FLOAT_2ADDR arm-vfp
+ op OP_CMPG_DOUBLE arm-vfp
+ op OP_CMPG_FLOAT arm-vfp
+ op OP_CMPL_DOUBLE arm-vfp
+ op OP_CMPL_FLOAT arm-vfp
+ op OP_DIV_DOUBLE arm-vfp
+ op OP_DIV_DOUBLE_2ADDR arm-vfp
+ op OP_DIV_FLOAT arm-vfp
+ op OP_DIV_FLOAT_2ADDR arm-vfp
+ op OP_DOUBLE_TO_FLOAT arm-vfp
+ op OP_DOUBLE_TO_INT arm-vfp
+ op OP_FLOAT_TO_DOUBLE arm-vfp
+ op OP_FLOAT_TO_INT arm-vfp
+ op OP_INT_TO_DOUBLE arm-vfp
+ op OP_INT_TO_FLOAT arm-vfp
+ op OP_MUL_DOUBLE arm-vfp
+ op OP_MUL_DOUBLE_2ADDR arm-vfp
+ op OP_MUL_FLOAT arm-vfp
+ op OP_MUL_FLOAT_2ADDR arm-vfp
+ op OP_SUB_DOUBLE arm-vfp
+ op OP_SUB_DOUBLE_2ADDR arm-vfp
+ op OP_SUB_FLOAT arm-vfp
+ op OP_SUB_FLOAT_2ADDR arm-vfp
+op-end
+
+# "helper" code for C; include if you use any of the C stubs (this generates
+# object code, so it's normally excluded)
+##import c/gotoTargets.c
+
+# end of defs; include this when cstubs/stubdefs.c is included
+import cstubs/enddefs.c
+
+# common subroutines for asm
+import armv5te/footer.S
+import armv5te/debug.c
diff --git a/vm/mterp/config-portdbg b/vm/mterp/config-portdbg
new file mode 100644
index 0000000..c6982d7
--- /dev/null
+++ b/vm/mterp/config-portdbg
@@ -0,0 +1,49 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Configuration for "portdbg" target, a/k/a the portable interpreter with
+# debugging enabled.
+#
+
+#handler-size 64
+
+# C file header and basic definitions
+import c/header.c
+
+# simple def to specify the "debug" interp
+import portable/portdbg.c
+
+# C pre-processor defines for stub C instructions
+import portable/stubdefs.c
+
+# common defs for the C opcodes
+import c/opcommon.c
+
+# debug-only code
+import portable/debug.c
+
+# entry point
+import portable/entry.c
+
+# opcode list; argument to op-start is default directory
+op-start c
+ # concatenate all C implementations
+op-end
+
+# "helper" code
+import c/gotoTargets.c
+
+# finish
+import portable/enddefs.c
diff --git a/vm/mterp/config-portstd b/vm/mterp/config-portstd
new file mode 100644
index 0000000..41ecb4f
--- /dev/null
+++ b/vm/mterp/config-portstd
@@ -0,0 +1,48 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Configuration for "portstd" target, a/k/a the portable interpreter. This
+# differs from other "mterp" targets in that it doesn't use the usual
+# stub/glue mechanism, and defines different entry points. We generate it
+# here because it's convenient.
+#
+
+#handler-size 64
+
+# C file header and basic definitions
+import c/header.c
+
+# simple def to specify the "standard" interp
+import portable/portstd.c
+
+# C pre-processor defines for stub C instructions
+import portable/stubdefs.c
+
+# common defs for the C opcodes
+import c/opcommon.c
+
+# entry point
+import portable/entry.c
+
+# opcode list; argument to op-start is default directory
+op-start c
+ # concatenate all C implementations
+op-end
+
+# "helper" code
+import c/gotoTargets.c
+
+# finish
+import portable/enddefs.c
diff --git a/vm/mterp/config-x86 b/vm/mterp/config-x86
new file mode 100644
index 0000000..627e06c
--- /dev/null
+++ b/vm/mterp/config-x86
@@ -0,0 +1,56 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Configuration for "desktop" targets.
+#
+
+handler-size 64
+
+# source for the instruction table stub
+asm-stub x86/stub.S
+
+# C file header and basic definitions
+import c/header.c
+import x86/header.S
+
+# C pre-processor defines for stub C instructions
+import cstubs/stubdefs.c
+
+# common defs for the C opcodes
+import c/opcommon.c
+
+# opcode list; argument to op-start is default directory
+op-start x86
+ # stub -- need native impl
+ op OP_EXECUTE_INLINE_RANGE c
+ op OP_IGET_WIDE_VOLATILE c
+ op OP_IPUT_WIDE_VOLATILE c
+ op OP_SGET_WIDE_VOLATILE c
+ op OP_SPUT_WIDE_VOLATILE c
+op-end
+
+# arch-specific entry point to interpreter
+import x86/entry.S
+
+# "helper" code for C; include if you use any of the C stubs (this generates
+# object code, so it's normally excluded)
+# (asm code is currently calling into dvmMterp_exceptionThrown)
+import c/gotoTargets.c
+
+# end of defs; include this when cstubs/stubdefs.c is included
+import cstubs/enddefs.c
+
+# common subroutines for asm
+import x86/footer.S
diff --git a/vm/mterp/config-x86-atom b/vm/mterp/config-x86-atom
new file mode 100644
index 0000000..e1c3f07
--- /dev/null
+++ b/vm/mterp/config-x86-atom
@@ -0,0 +1,300 @@
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Specifies the size of the assembly region in bytes
+handler-size 64
+
+# source for the instruction table stub
+asm-stub x86-atom/stub.S
+
+# file header, macros and definitions
+import c/header.c
+import x86-atom/header.S
+
+# common defs for the C helper; include this before the instruction handlers
+import cstubs/stubdefs.c
+import c/opcommon.c
+
+# start of opcode list; command gives default directory location of instruction files
+op-start x86-atom
+
+#op OP_ADD_DOUBLE_2ADDR c
+#op OP_ADD_DOUBLE c
+#op OP_ADD_FLOAT_2ADDR c
+#op OP_ADD_FLOAT c
+#op OP_ADD_INT_2ADDR c
+#op OP_ADD_INT_LIT16 c
+#op OP_ADD_INT_LIT8 c
+#op OP_ADD_INT c
+#op OP_ADD_LONG_2ADDR c
+#op OP_ADD_LONG c
+#op OP_AGET_BOOLEAN c
+#op OP_AGET_BYTE c
+#op OP_AGET_CHAR c
+#op OP_AGET_OBJECT c
+#op OP_AGET c
+#op OP_AGET_SHORT c
+#op OP_AGET_WIDE c
+#op OP_AND_INT_2ADDR c
+#op OP_AND_INT_LIT16 c
+#op OP_AND_INT_LIT8 c
+#op OP_AND_INT c
+#op OP_AND_LONG_2ADDR c
+#op OP_AND_LONG c
+#op OP_APUT_BOOLEAN c
+#op OP_APUT_BYTE c
+#op OP_APUT_CHAR c
+#op OP_APUT_OBJECT c
+#op OP_APUT c
+#op OP_APUT_SHORT c
+#op OP_APUT_WIDE c
+#op OP_ARRAY_LENGTH c
+#op OP_CHECK_CAST c
+#op OP_CMPG_DOUBLE c
+#op OP_CMPG_FLOAT c
+#op OP_CMPL_DOUBLE c
+#op OP_CMPL_FLOAT c
+#op OP_CMP_LONG c
+#op OP_CONST_16 c
+#op OP_CONST_4 c
+#op OP_CONST_CLASS c
+#op OP_CONST_HIGH16 c
+#op OP_CONST c
+#op OP_CONST_STRING_JUMBO c
+#op OP_CONST_STRING c
+#op OP_CONST_WIDE_16 c
+#op OP_CONST_WIDE_32 c
+#op OP_CONST_WIDE_HIGH16 c
+#op OP_CONST_WIDE c
+#op OP_DIV_DOUBLE_2ADDR c
+#op OP_DIV_DOUBLE c
+#op OP_DIV_FLOAT_2ADDR c
+#op OP_DIV_FLOAT c
+#op OP_DIV_INT_2ADDR c
+#op OP_DIV_INT_LIT16 c
+#op OP_DIV_INT_LIT8 c
+#op OP_DIV_INT c
+#op OP_DIV_LONG_2ADDR c
+#op OP_DIV_LONG c
+#op OP_DOUBLE_TO_FLOAT c
+#op OP_DOUBLE_TO_INT c
+#op OP_DOUBLE_TO_LONG c
+#op OP_EXECUTE_INLINE c
+#op OP_FILL_ARRAY_DATA c
+#op OP_FILLED_NEW_ARRAY_RANGE c
+#op OP_FILLED_NEW_ARRAY c
+#op OP_FLOAT_TO_DOUBLE c
+#op OP_FLOAT_TO_INT c
+#op OP_FLOAT_TO_LONG c
+#op OP_GOTO_16 c
+#op OP_GOTO_32 c
+#op OP_GOTO c
+#op OP_IF_EQ c
+#op OP_IF_EQZ c
+#op OP_IF_GE c
+#op OP_IF_GEZ c
+#op OP_IF_GT c
+#op OP_IF_GTZ c
+#op OP_IF_LE c
+#op OP_IF_LEZ c
+#op OP_IF_LT c
+#op OP_IF_LTZ c
+#op OP_IF_NE c
+#op OP_IF_NEZ c
+#op OP_IGET_BOOLEAN c
+#op OP_IGET_BYTE c
+#op OP_IGET_CHAR c
+#op OP_IGET_OBJECT_QUICK c
+#op OP_IGET_OBJECT c
+#op OP_IGET_QUICK c
+#op OP_IGET c
+#op OP_IGET_SHORT c
+#op OP_IGET_WIDE_QUICK c
+#op OP_IGET_WIDE c
+#op OP_INSTANCE_OF c
+#op OP_INT_TO_BYTE c
+#op OP_INT_TO_CHAR c
+#op OP_INT_TO_DOUBLE c
+#op OP_INT_TO_FLOAT c
+#op OP_INT_TO_LONG c
+#op OP_INT_TO_SHORT c
+#op OP_INVOKE_DIRECT_EMPTY c
+#op OP_INVOKE_DIRECT_RANGE c
+#op OP_INVOKE_DIRECT c
+#op OP_INVOKE_INTERFACE_RANGE c
+#op OP_INVOKE_INTERFACE c
+#op OP_INVOKE_STATIC_RANGE c
+#op OP_INVOKE_STATIC c
+#op OP_INVOKE_SUPER_QUICK_RANGE c
+#op OP_INVOKE_SUPER_QUICK c
+#op OP_INVOKE_SUPER_RANGE c
+#op OP_INVOKE_SUPER c
+#op OP_INVOKE_VIRTUAL_QUICK_RANGE c
+#op OP_INVOKE_VIRTUAL_QUICK c
+#op OP_INVOKE_VIRTUAL_RANGE c
+#op OP_INVOKE_VIRTUAL c
+#op OP_IPUT_BOOLEAN c
+#op OP_IPUT_BYTE c
+#op OP_IPUT_CHAR c
+#op OP_IPUT_OBJECT_QUICK c
+#op OP_IPUT_OBJECT c
+#op OP_IPUT_QUICK c
+#op OP_IPUT c
+#op OP_IPUT_SHORT c
+#op OP_IPUT_WIDE_QUICK c
+#op OP_IPUT_WIDE c
+#op OP_LONG_TO_DOUBLE c
+#op OP_LONG_TO_FLOAT c
+#op OP_LONG_TO_INT c
+#op OP_MONITOR_ENTER c
+#op OP_MONITOR_EXIT c
+#op OP_MOVE_16 c
+#op OP_MOVE_EXCEPTION c
+#op OP_MOVE_FROM16 c
+#op OP_MOVE_OBJECT_16 c
+#op OP_MOVE_OBJECT_FROM16 c
+#op OP_MOVE_OBJECT c
+#op OP_MOVE_RESULT_OBJECT c
+#op OP_MOVE_RESULT c
+#op OP_MOVE_RESULT_WIDE c
+#op OP_MOVE c
+#op OP_MOVE_WIDE_16 c
+#op OP_MOVE_WIDE_FROM16 c
+#op OP_MOVE_WIDE c
+#op OP_MUL_DOUBLE_2ADDR c
+#op OP_MUL_DOUBLE c
+#op OP_MUL_FLOAT_2ADDR c
+#op OP_MUL_FLOAT c
+#op OP_MUL_INT_2ADDR c
+#op OP_MUL_INT_LIT16 c
+#op OP_MUL_INT_LIT8 c
+#op OP_MUL_INT c
+#op OP_MUL_LONG_2ADDR c
+#op OP_MUL_LONG c
+#op OP_NEG_DOUBLE c
+#op OP_NEG_FLOAT c
+#op OP_NEG_INT c
+#op OP_NEG_LONG c
+#op OP_NEW_ARRAY c
+#op OP_NEW_INSTANCE c
+#op OP_NOP c
+#op OP_NOT_INT c
+#op OP_NOT_LONG c
+#op OP_OR_INT_2ADDR c
+#op OP_OR_INT_LIT16 c
+#op OP_OR_INT_LIT8 c
+#op OP_OR_INT c
+#op OP_OR_LONG_2ADDR c
+#op OP_OR_LONG c
+#op OP_PACKED_SWITCH c
+#op OP_REM_DOUBLE_2ADDR c
+#op OP_REM_DOUBLE c
+#op OP_REM_FLOAT_2ADDR c
+#op OP_REM_FLOAT c
+#op OP_REM_INT_2ADDR c
+#op OP_REM_INT_LIT16 c
+#op OP_REM_INT_LIT8 c
+#op OP_REM_INT c
+#op OP_REM_LONG_2ADDR c
+#op OP_REM_LONG c
+#op OP_RETURN_OBJECT c
+#op OP_RETURN c
+#op OP_RETURN_VOID c
+#op OP_RETURN_WIDE c
+#op OP_RSUB_INT_LIT8 c
+#op OP_RSUB_INT c
+#op OP_SGET_BOOLEAN c
+#op OP_SGET_BYTE c
+#op OP_SGET_CHAR c
+#op OP_SGET_OBJECT c
+#op OP_SGET c
+#op OP_SGET_SHORT c
+#op OP_SGET_WIDE c
+#op OP_SHL_INT_2ADDR c
+#op OP_SHL_INT_LIT8 c
+#op OP_SHL_INT c
+#op OP_SHL_LONG_2ADDR c
+#op OP_SHL_LONG c
+#op OP_SHR_INT_2ADDR c
+#op OP_SHR_INT_LIT8 c
+#op OP_SHR_INT c
+#op OP_SHR_LONG_2ADDR c
+#op OP_SHR_LONG c
+#op OP_SPARSE_SWITCH c
+#op OP_SPUT_BOOLEAN c
+#op OP_SPUT_BYTE c
+#op OP_SPUT_CHAR c
+#op OP_SPUT_OBJECT c
+#op OP_SPUT c
+#op OP_SPUT_SHORT c
+#op OP_SPUT_WIDE c
+#op OP_SUB_DOUBLE_2ADDR c
+#op OP_SUB_DOUBLE c
+#op OP_SUB_FLOAT_2ADDR c
+#op OP_SUB_FLOAT c
+#op OP_SUB_INT_2ADDR c
+#op OP_SUB_INT c
+#op OP_SUB_LONG_2ADDR c
+#op OP_SUB_LONG c
+#op OP_THROW c
+#op OP_UNUSED_3E c
+#op OP_UNUSED_3F c
+#op OP_UNUSED_40 c
+#op OP_UNUSED_41 c
+#op OP_UNUSED_42 c
+#op OP_UNUSED_43 c
+#op OP_UNUSED_73 c
+#op OP_UNUSED_79 c
+#op OP_UNUSED_7A c
+#op OP_UNUSED_F1 c
+#op OP_UNUSED_FF c
+#op OP_USHR_INT_2ADDR c
+#op OP_USHR_INT_LIT8 c
+#op OP_USHR_INT c
+#op OP_USHR_LONG_2ADDR c
+#op OP_USHR_LONG c
+#op OP_XOR_INT_2ADDR c
+#op OP_XOR_INT_LIT16 c
+#op OP_XOR_INT_LIT8 c
+#op OP_XOR_INT c
+#op OP_XOR_LONG_2ADDR c
+#op OP_XOR_LONG c
+
+# TODO: provide native implementations
+op OP_BREAKPOINT c
+op OP_EXECUTE_INLINE_RANGE c
+op OP_IGET_VOLATILE c
+op OP_IPUT_VOLATILE c
+op OP_SGET_VOLATILE c
+op OP_SPUT_VOLATILE c
+op OP_IGET_OBJECT_VOLATILE c
+op OP_IPUT_OBJECT_VOLATILE c
+op OP_SGET_OBJECT_VOLATILE c
+op OP_SPUT_OBJECT_VOLATILE c
+op OP_IGET_WIDE_VOLATILE c
+op OP_IPUT_WIDE_VOLATILE c
+op OP_SGET_WIDE_VOLATILE c
+op OP_SPUT_WIDE_VOLATILE c
+
+op-end
+
+# arch-specific entry point to interpreter
+import x86-atom/entry.S
+
+# "helper" code for C; include this after the instruction handlers
+import c/gotoTargets.c
+import cstubs/enddefs.c
+
+# common subroutines for asm
+import x86-atom/footer.S
diff --git a/vm/mterp/cstubs/enddefs.c b/vm/mterp/cstubs/enddefs.c
new file mode 100644
index 0000000..cac74bf
--- /dev/null
+++ b/vm/mterp/cstubs/enddefs.c
@@ -0,0 +1,9 @@
+
+/* undefine "magic" name remapping */
+#undef retval
+#undef pc
+#undef fp
+#undef curMethod
+#undef methodClassDex
+#undef self
+#undef debugTrackedRefStart
diff --git a/vm/mterp/cstubs/entry.c b/vm/mterp/cstubs/entry.c
new file mode 100644
index 0000000..af31a3d
--- /dev/null
+++ b/vm/mterp/cstubs/entry.c
@@ -0,0 +1,80 @@
+/*
+ * Handler function table, one entry per opcode.
+ */
+#undef H
+#define H(_op) dvmMterp_##_op
+DEFINE_GOTO_TABLE(gDvmMterpHandlers)
+
+#undef H
+#define H(_op) #_op
+DEFINE_GOTO_TABLE(gDvmMterpHandlerNames)
+
+#include <setjmp.h>
+
+/*
+ * C mterp entry point. This just calls the various C fallbacks, making
+ * this a slow but portable interpeter.
+ *
+ * This is only used for the "allstubs" variant.
+ */
+bool dvmMterpStdRun(MterpGlue* glue)
+{
+ jmp_buf jmpBuf;
+ int changeInterp;
+
+ glue->bailPtr = &jmpBuf;
+
+ /*
+ * We want to return "changeInterp" as a boolean, but we can't return
+ * zero through longjmp, so we return (boolean+1).
+ */
+ changeInterp = setjmp(jmpBuf) -1;
+ if (changeInterp >= 0) {
+ Thread* threadSelf = dvmThreadSelf();
+ LOGVV("mterp threadid=%d returning %d\n",
+ threadSelf->threadId, changeInterp);
+ return changeInterp;
+ }
+
+ /*
+ * We may not be starting at a point where we're executing instructions.
+ * We need to pick up where the other interpreter left off.
+ *
+ * In some cases we need to call into a throw/return handler which
+ * will do some processing and then either return to us (updating "glue")
+ * or longjmp back out.
+ */
+ switch (glue->entryPoint) {
+ case kInterpEntryInstr:
+ /* just start at the start */
+ break;
+ case kInterpEntryReturn:
+ dvmMterp_returnFromMethod(glue);
+ break;
+ case kInterpEntryThrow:
+ dvmMterp_exceptionThrown(glue);
+ break;
+ default:
+ dvmAbort();
+ }
+
+ /* run until somebody longjmp()s out */
+ while (true) {
+ typedef void (*Handler)(MterpGlue* glue);
+
+ u2 inst = /*glue->*/pc[0];
+ Handler handler = (Handler) gDvmMterpHandlers[inst & 0xff];
+ LOGVV("handler %p %s\n",
+ handler, (const char*) gDvmMterpHandlerNames[inst & 0xff]);
+ (*handler)(glue);
+ }
+}
+
+/*
+ * C mterp exit point. Call here to bail out of the interpreter.
+ */
+void dvmMterpStdBail(MterpGlue* glue, bool changeInterp)
+{
+ jmp_buf* pJmpBuf = glue->bailPtr;
+ longjmp(*pJmpBuf, ((int)changeInterp)+1);
+}
diff --git a/vm/mterp/cstubs/stubdefs.c b/vm/mterp/cstubs/stubdefs.c
new file mode 100644
index 0000000..bf870c6
--- /dev/null
+++ b/vm/mterp/cstubs/stubdefs.c
@@ -0,0 +1,124 @@
+/* this is a standard (no debug support) interpreter */
+#define INTERP_TYPE INTERP_STD
+#define CHECK_DEBUG_AND_PROF() ((void)0)
+# define CHECK_TRACKED_REFS() ((void)0)
+#define CHECK_JIT_BOOL() (false)
+#define CHECK_JIT_VOID()
+#define ABORT_JIT_TSELECT() ((void)0)
+
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...) \
+ void dvmMterp_##_target(MterpGlue* glue, ## __VA_ARGS__);
+
+#define GOTO_TARGET(_target, ...) \
+ void dvmMterp_##_target(MterpGlue* glue, ## __VA_ARGS__) { \
+ u2 ref, vsrc1, vsrc2, vdst; \
+ u2 inst = FETCH(0); \
+ const Method* methodToCall; \
+ StackSaveArea* debugSaveArea;
+
+#define GOTO_TARGET_END }
+
+/*
+ * Redefine what used to be local variable accesses into MterpGlue struct
+ * references. (These are undefined down in "footer.c".)
+ */
+#define retval glue->retval
+#define pc glue->pc
+#define fp glue->fp
+#define curMethod glue->method
+#define methodClassDex glue->methodClassDex
+#define self glue->self
+#define debugTrackedRefStart glue->debugTrackedRefStart
+
+/* ugh */
+#define STUB_HACK(x) x
+
+
+/*
+ * Opcode handler framing macros. Here, each opcode is a separate function
+ * that takes a "glue" argument and returns void. We can't declare
+ * these "static" because they may be called from an assembly stub.
+ */
+#define HANDLE_OPCODE(_op) \
+ void dvmMterp_##_op(MterpGlue* glue) { \
+ u2 ref, vsrc1, vsrc2, vdst; \
+ u2 inst = FETCH(0);
+
+#define OP_END }
+
+/*
+ * Like the "portable" FINISH, but don't reload "inst", and return to caller
+ * when done.
+ */
+#define FINISH(_offset) { \
+ ADJUST_PC(_offset); \
+ CHECK_DEBUG_AND_PROF(); \
+ CHECK_TRACKED_REFS(); \
+ return; \
+ }
+
+
+/*
+ * The "goto label" statements turn into function calls followed by
+ * return statements. Some of the functions take arguments, which in the
+ * portable interpreter are handled by assigning values to globals.
+ */
+
+#define GOTO_exceptionThrown() \
+ do { \
+ dvmMterp_exceptionThrown(glue); \
+ return; \
+ } while(false)
+
+#define GOTO_returnFromMethod() \
+ do { \
+ dvmMterp_returnFromMethod(glue); \
+ return; \
+ } while(false)
+
+#define GOTO_invoke(_target, _methodCallRange) \
+ do { \
+ dvmMterp_##_target(glue, _methodCallRange); \
+ return; \
+ } while(false)
+
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst) \
+ do { \
+ dvmMterp_invokeMethod(glue, _methodCallRange, _methodToCall, \
+ _vsrc1, _vdst); \
+ return; \
+ } while(false)
+
+/*
+ * As a special case, "goto bail" turns into a longjmp. Use "bail_switch"
+ * if we need to switch to the other interpreter upon our return.
+ */
+#define GOTO_bail() \
+ dvmMterpStdBail(glue, false);
+#define GOTO_bail_switch() \
+ dvmMterpStdBail(glue, true);
+
+/*
+ * Periodically check for thread suspension.
+ *
+ * While we're at it, see if a debugger has attached or the profiler has
+ * started. If so, switch to a different "goto" table.
+ */
+#define PERIODIC_CHECKS(_entryPoint, _pcadj) { \
+ if (dvmCheckSuspendQuick(self)) { \
+ EXPORT_PC(); /* need for precise GC */ \
+ dvmCheckSuspendPending(self); \
+ } \
+ if (NEED_INTERP_SWITCH(INTERP_TYPE)) { \
+ ADJUST_PC(_pcadj); \
+ glue->entryPoint = _entryPoint; \
+ LOGVV("threadid=%d: switch to STD ep=%d adj=%d\n", \
+ self->threadId, (_entryPoint), (_pcadj)); \
+ GOTO_bail_switch(); \
+ } \
+ }
diff --git a/vm/mterp/gen-mterp.py b/vm/mterp/gen-mterp.py
new file mode 100755
index 0000000..e0d854d
--- /dev/null
+++ b/vm/mterp/gen-mterp.py
@@ -0,0 +1,479 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Using instructions from an architecture-specific config file, generate C
+# and assembly source files for the Dalvik interpreter.
+#
+
+import sys, string, re, time
+from string import Template
+
+interp_defs_file = "../../libdex/OpCode.h" # need opcode list
+
+verbose = False
+handler_size_bits = -1000
+handler_size_bytes = -1000
+in_op_start = 0 # 0=not started, 1=started, 2=ended
+default_op_dir = None
+opcode_locations = {}
+asm_stub_text = []
+label_prefix = ".L" # use ".L" to hide labels from gdb
+
+# Exception class.
+class DataParseError(SyntaxError):
+ "Failure when parsing data file"
+
+#
+# Set any omnipresent substitution values.
+#
+def getGlobalSubDict():
+ return { "handler_size_bits":handler_size_bits,
+ "handler_size_bytes":handler_size_bytes }
+
+#
+# Parse arch config file --
+# Set handler_size_bytes to the value of tokens[1], and handler_size_bits to
+# log2(handler_size_bytes). Throws an exception if "bytes" is not a power
+# of two.
+#
+def setHandlerSize(tokens):
+ global handler_size_bits, handler_size_bytes
+ if len(tokens) != 2:
+ raise DataParseError("handler-size requires one argument")
+ if handler_size_bits != -1000:
+ raise DataParseError("handler-size may only be set once")
+
+ # compute log2(n), and make sure n is a power of 2
+ handler_size_bytes = bytes = int(tokens[1])
+ bits = -1
+ while bytes > 0:
+ bytes //= 2 # halve with truncating division
+ bits += 1
+
+ if handler_size_bytes == 0 or handler_size_bytes != (1 << bits):
+ raise DataParseError("handler-size (%d) must be power of 2 and > 0" \
+ % orig_bytes)
+ handler_size_bits = bits
+
+#
+# Parse arch config file --
+# Copy a file in to the C or asm output file.
+#
+def importFile(tokens):
+ if len(tokens) != 2:
+ raise DataParseError("import requires one argument")
+ source = tokens[1]
+ if source.endswith(".c"):
+ appendSourceFile(tokens[1], getGlobalSubDict(), c_fp, None)
+ elif source.endswith(".S"):
+ appendSourceFile(tokens[1], getGlobalSubDict(), asm_fp, None)
+ else:
+ raise DataParseError("don't know how to import %s (expecting .c/.S)"
+ % source)
+
+#
+# Parse arch config file --
+# Copy a file in to the C or asm output file.
+#
+def setAsmStub(tokens):
+ global asm_stub_text
+ if len(tokens) != 2:
+ raise DataParseError("import requires one argument")
+ try:
+ stub_fp = open(tokens[1])
+ asm_stub_text = stub_fp.readlines()
+ except IOError, err:
+ stub_fp.close()
+ raise DataParseError("unable to load asm-stub: %s" % str(err))
+ stub_fp.close()
+
+#
+# Parse arch config file --
+# Start of opcode list.
+#
+def opStart(tokens):
+ global in_op_start
+ global default_op_dir
+ if len(tokens) != 2:
+ raise DataParseError("opStart takes a directory name argument")
+ if in_op_start != 0:
+ raise DataParseError("opStart can only be specified once")
+ default_op_dir = tokens[1]
+ in_op_start = 1
+
+#
+# Parse arch config file --
+# Set location of a single opcode's source file.
+#
+def opEntry(tokens):
+ #global opcode_locations
+ if len(tokens) != 3:
+ raise DataParseError("op requires exactly two arguments")
+ if in_op_start != 1:
+ raise DataParseError("op statements must be between opStart/opEnd")
+ try:
+ index = opcodes.index(tokens[1])
+ except ValueError:
+ raise DataParseError("unknown opcode %s" % tokens[1])
+ if opcode_locations.has_key(tokens[1]):
+ print "Warning: op overrides earlier %s (%s -> %s)" \
+ % (tokens[1], opcode_locations[tokens[1]], tokens[2])
+ opcode_locations[tokens[1]] = tokens[2]
+
+#
+# Parse arch config file --
+# End of opcode list; emit instruction blocks.
+#
+def opEnd(tokens):
+ global in_op_start
+ if len(tokens) != 1:
+ raise DataParseError("opEnd takes no arguments")
+ if in_op_start != 1:
+ raise DataParseError("opEnd must follow opStart, and only appear once")
+ in_op_start = 2
+
+ loadAndEmitOpcodes()
+
+
+#
+# Extract an ordered list of instructions from the VM sources. We use the
+# "goto table" definition macro, which has exactly 256 entries.
+#
+def getOpcodeList():
+ opcodes = []
+ opcode_fp = open(interp_defs_file)
+ opcode_re = re.compile(r"^\s*H\(OP_(\w+)\),.*", re.DOTALL)
+ for line in opcode_fp:
+ match = opcode_re.match(line)
+ if not match:
+ continue
+ opcodes.append("OP_" + match.group(1))
+ opcode_fp.close()
+
+ if len(opcodes) != 256:
+ print "ERROR: found %d opcodes in Interp.h (expected 256)" \
+ % len(opcodes)
+ raise SyntaxError, "bad opcode count"
+ return opcodes
+
+
+#
+# Load and emit opcodes for all 256 instructions.
+#
+def loadAndEmitOpcodes():
+ sister_list = []
+ assert len(opcodes) == 256
+ need_dummy_start = False
+
+ # point dvmAsmInstructionStart at the first handler or stub
+ asm_fp.write("\n .global dvmAsmInstructionStart\n")
+ asm_fp.write(" .type dvmAsmInstructionStart, %function\n")
+ asm_fp.write("dvmAsmInstructionStart = " + label_prefix + "_OP_NOP\n")
+ asm_fp.write(" .text\n\n")
+
+ for i in xrange(256):
+ op = opcodes[i]
+
+ if opcode_locations.has_key(op):
+ location = opcode_locations[op]
+ else:
+ location = default_op_dir
+
+ if location == "c":
+ loadAndEmitC(location, i)
+ if len(asm_stub_text) == 0:
+ need_dummy_start = True
+ else:
+ loadAndEmitAsm(location, i, sister_list)
+
+ # For a 100% C implementation, there are no asm handlers or stubs. We
+ # need to have the dvmAsmInstructionStart label point at OP_NOP, and it's
+ # too annoying to try to slide it in after the alignment psuedo-op, so
+ # we take the low road and just emit a dummy OP_NOP here.
+ if need_dummy_start:
+ asm_fp.write(" .balign %d\n" % handler_size_bytes)
+ asm_fp.write(label_prefix + "_OP_NOP: /* dummy */\n");
+
+ asm_fp.write("\n .balign %d\n" % handler_size_bytes)
+ asm_fp.write(" .size dvmAsmInstructionStart, .-dvmAsmInstructionStart\n")
+ asm_fp.write(" .global dvmAsmInstructionEnd\n")
+ asm_fp.write("dvmAsmInstructionEnd:\n")
+
+ emitSectionComment("Sister implementations", asm_fp)
+ asm_fp.write(" .global dvmAsmSisterStart\n")
+ asm_fp.write(" .type dvmAsmSisterStart, %function\n")
+ asm_fp.write(" .text\n")
+ asm_fp.write(" .balign 4\n")
+ asm_fp.write("dvmAsmSisterStart:\n")
+ asm_fp.writelines(sister_list)
+
+ asm_fp.write("\n .size dvmAsmSisterStart, .-dvmAsmSisterStart\n")
+ asm_fp.write(" .global dvmAsmSisterEnd\n")
+ asm_fp.write("dvmAsmSisterEnd:\n\n")
+
+#
+# Load a C fragment and emit it, then output an assembly stub.
+#
+def loadAndEmitC(location, opindex):
+ op = opcodes[opindex]
+ source = "%s/%s.c" % (location, op)
+ if verbose:
+ print " emit %s --> C" % source
+ dict = getGlobalSubDict()
+ dict.update({ "opcode":op, "opnum":opindex })
+
+ appendSourceFile(source, dict, c_fp, None)
+
+ if len(asm_stub_text) != 0:
+ emitAsmStub(asm_fp, dict)
+
+#
+# Load an assembly fragment and emit it.
+#
+def loadAndEmitAsm(location, opindex, sister_list):
+ op = opcodes[opindex]
+ source = "%s/%s.S" % (location, op)
+ dict = getGlobalSubDict()
+ dict.update({ "opcode":op, "opnum":opindex })
+ if verbose:
+ print " emit %s --> asm" % source
+
+ emitAsmHeader(asm_fp, dict)
+ appendSourceFile(source, dict, asm_fp, sister_list)
+
+#
+# Output the alignment directive and label for an assembly piece.
+#
+def emitAsmHeader(outfp, dict):
+ outfp.write("/* ------------------------------ */\n")
+ # The alignment directive ensures that the handler occupies
+ # at least the correct amount of space. We don't try to deal
+ # with overflow here.
+ outfp.write(" .balign %d\n" % handler_size_bytes)
+ # Emit a label so that gdb will say the right thing. We prepend an
+ # underscore so the symbol name doesn't clash with the OpCode enum.
+ outfp.write(label_prefix + "_%(opcode)s: /* 0x%(opnum)02x */\n" % dict)
+
+#
+# Output a generic instruction stub that updates the "glue" struct and
+# calls the C implementation.
+#
+def emitAsmStub(outfp, dict):
+ emitAsmHeader(outfp, dict)
+ for line in asm_stub_text:
+ templ = Template(line)
+ outfp.write(templ.substitute(dict))
+
+#
+# Append the file specified by "source" to the open "outfp". Each line will
+# be template-replaced using the substitution dictionary "dict".
+#
+# If the first line of the file starts with "%" it is taken as a directive.
+# A "%include" line contains a filename and, optionally, a Python-style
+# dictionary declaration with substitution strings. (This is implemented
+# with recursion.)
+#
+# If "sister_list" is provided, and we find a line that contains only "&",
+# all subsequent lines from the file will be appended to sister_list instead
+# of copied to the output.
+#
+# This may modify "dict".
+#
+def appendSourceFile(source, dict, outfp, sister_list):
+ outfp.write("/* File: %s */\n" % source)
+ infp = open(source, "r")
+ in_sister = False
+ for line in infp:
+ if line.startswith("%include"):
+ # Parse the "include" line
+ tokens = line.strip().split(' ', 2)
+ if len(tokens) < 2:
+ raise DataParseError("malformed %%include in %s" % source)
+
+ alt_source = tokens[1].strip("\"")
+ if alt_source == source:
+ raise DataParseError("self-referential %%include in %s"
+ % source)
+
+ new_dict = dict.copy()
+ if len(tokens) == 3:
+ new_dict.update(eval(tokens[2]))
+ #print " including src=%s dict=%s" % (alt_source, new_dict)
+ appendSourceFile(alt_source, new_dict, outfp, sister_list)
+ continue
+
+ elif line.startswith("%default"):
+ # copy keywords into dictionary
+ tokens = line.strip().split(' ', 1)
+ if len(tokens) < 2:
+ raise DataParseError("malformed %%default in %s" % source)
+ defaultValues = eval(tokens[1])
+ for entry in defaultValues:
+ dict.setdefault(entry, defaultValues[entry])
+ continue
+
+ elif line.startswith("%verify"):
+ # more to come, someday
+ continue
+
+ elif line.startswith("%break") and sister_list != None:
+ # allow more than one %break, ignoring all following the first
+ if not in_sister:
+ in_sister = True
+ sister_list.append("\n/* continuation for %(opcode)s */\n"%dict)
+ continue
+
+ # perform keyword substitution if a dictionary was provided
+ if dict != None:
+ templ = Template(line)
+ try:
+ subline = templ.substitute(dict)
+ except KeyError, err:
+ raise DataParseError("keyword substitution failed in %s: %s"
+ % (source, str(err)))
+ except:
+ print "ERROR: substitution failed: " + line
+ raise
+ else:
+ subline = line
+
+ # write output to appropriate file
+ if in_sister:
+ sister_list.append(subline)
+ else:
+ outfp.write(subline)
+ outfp.write("\n")
+ infp.close()
+
+#
+# Emit a C-style section header comment.
+#
+def emitSectionComment(str, fp):
+ equals = "========================================" \
+ "==================================="
+
+ fp.write("\n/*\n * %s\n * %s\n * %s\n */\n" %
+ (equals, str, equals))
+
+
+#
+# ===========================================================================
+# "main" code
+#
+
+#
+# Check args.
+#
+if len(sys.argv) != 3:
+ print "Usage: %s target-arch output-dir" % sys.argv[0]
+ sys.exit(2)
+
+target_arch = sys.argv[1]
+output_dir = sys.argv[2]
+
+#
+# Extract opcode list.
+#
+opcodes = getOpcodeList()
+#for op in opcodes:
+# print " %s" % op
+
+#
+# Open config file.
+#
+try:
+ config_fp = open("config-%s" % target_arch)
+except:
+ print "Unable to open config file 'config-%s'" % target_arch
+ sys.exit(1)
+
+#
+# Open and prepare output files.
+#
+try:
+ c_fp = open("%s/InterpC-%s.c" % (output_dir, target_arch), "w")
+ asm_fp = open("%s/InterpAsm-%s.S" % (output_dir, target_arch), "w")
+except:
+ print "Unable to open output files"
+ print "Make sure directory '%s' exists and existing files are writable" \
+ % output_dir
+ # Ideally we'd remove the files to avoid confusing "make", but if they
+ # failed to open we probably won't be able to remove them either.
+ sys.exit(1)
+
+print "Generating %s, %s" % (c_fp.name, asm_fp.name)
+
+file_header = """/*
+ * This file was generated automatically by gen-mterp.py for '%s'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+""" % (target_arch)
+
+c_fp.write(file_header)
+asm_fp.write(file_header)
+
+#
+# Process the config file.
+#
+failed = False
+try:
+ for line in config_fp:
+ line = line.strip() # remove CRLF, leading spaces
+ tokens = line.split(' ') # tokenize
+ #print "%d: %s" % (len(tokens), tokens)
+ if len(tokens[0]) == 0:
+ #print " blank"
+ pass
+ elif tokens[0][0] == '#':
+ #print " comment"
+ pass
+ else:
+ if tokens[0] == "handler-size":
+ setHandlerSize(tokens)
+ elif tokens[0] == "import":
+ importFile(tokens)
+ elif tokens[0] == "asm-stub":
+ setAsmStub(tokens)
+ elif tokens[0] == "op-start":
+ opStart(tokens)
+ elif tokens[0] == "op-end":
+ opEnd(tokens)
+ elif tokens[0] == "op":
+ opEntry(tokens)
+ else:
+ raise DataParseError, "unrecognized command '%s'" % tokens[0]
+except DataParseError, err:
+ print "Failed: " + str(err)
+ # TODO: remove output files so "make" doesn't get confused
+ failed = True
+ c_fp.close()
+ asm_fp.close()
+ c_fp = asm_fp = None
+
+config_fp.close()
+
+#
+# Done!
+#
+if c_fp:
+ c_fp.close()
+if asm_fp:
+ asm_fp.close()
+
+sys.exit(failed)
diff --git a/vm/mterp/out/InterpAsm-allstubs.S b/vm/mterp/out/InterpAsm-allstubs.S
new file mode 100644
index 0000000..a6973ae
--- /dev/null
+++ b/vm/mterp/out/InterpAsm-allstubs.S
@@ -0,0 +1,35 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'allstubs'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+
+ .global dvmAsmInstructionStart
+ .type dvmAsmInstructionStart, %function
+dvmAsmInstructionStart = .L_OP_NOP
+ .text
+
+ .balign 64
+.L_OP_NOP: /* dummy */
+
+ .balign 64
+ .size dvmAsmInstructionStart, .-dvmAsmInstructionStart
+ .global dvmAsmInstructionEnd
+dvmAsmInstructionEnd:
+
+/*
+ * ===========================================================================
+ * Sister implementations
+ * ===========================================================================
+ */
+ .global dvmAsmSisterStart
+ .type dvmAsmSisterStart, %function
+ .text
+ .balign 4
+dvmAsmSisterStart:
+
+ .size dvmAsmSisterStart, .-dvmAsmSisterStart
+ .global dvmAsmSisterEnd
+dvmAsmSisterEnd:
+
diff --git a/vm/mterp/out/InterpAsm-armv4t.S b/vm/mterp/out/InterpAsm-armv4t.S
new file mode 100644
index 0000000..2afeb9b
--- /dev/null
+++ b/vm/mterp/out/InterpAsm-armv4t.S
@@ -0,0 +1,11077 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'armv4t'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: armv5te/header.S */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * ARMv5 definitions and declarations.
+ */
+
+/*
+ARM EABI general notes:
+
+r0-r3 hold first 4 args to a method; they are not preserved across method calls
+r4-r8 are available for general use
+r9 is given special treatment in some situations, but not for us
+r10 (sl) seems to be generally available
+r11 (fp) is used by gcc (unless -fomit-frame-pointer is set)
+r12 (ip) is scratch -- not preserved across method calls
+r13 (sp) should be managed carefully in case a signal arrives
+r14 (lr) must be preserved
+r15 (pc) can be tinkered with directly
+
+r0 holds returns of <= 4 bytes
+r0-r1 hold returns of 8 bytes, low word in r0
+
+Callee must save/restore r4+ (except r12) if it modifies them. If VFP
+is present, registers s16-s31 (a/k/a d8-d15, a/k/a q4-q7) must be preserved,
+s0-s15 (d0-d7, q0-a3) do not need to be.
+
+Stack is "full descending". Only the arguments that don't fit in the first 4
+registers are placed on the stack. "sp" points at the first stacked argument
+(i.e. the 5th arg).
+
+VFP: single-precision results in s0, double-precision results in d0.
+
+In the EABI, "sp" must be 64-bit aligned on entry to a function, and any
+64-bit quantities (long long, double) must be 64-bit aligned.
+*/
+
+/*
+Mterp and ARM notes:
+
+The following registers have fixed assignments:
+
+ reg nick purpose
+ r4 rPC interpreted program counter, used for fetching instructions
+ r5 rFP interpreted frame pointer, used for accessing locals and args
+ r6 rGLUE MterpGlue pointer
+ r7 rINST first 16-bit code unit of current instruction
+ r8 rIBASE interpreted instruction base pointer, used for computed goto
+
+Macros are provided for common operations. Each macro MUST emit only
+one instruction to make instruction-counting easier. They MUST NOT alter
+unspecified registers or condition codes.
+*/
+
+/* single-purpose registers, given names for clarity */
+#define rPC r4
+#define rFP r5
+#define rGLUE r6
+#define rINST r7
+#define rIBASE r8
+
+/* save/restore the PC and/or FP from the glue struct */
+#define LOAD_PC_FROM_GLUE() ldr rPC, [rGLUE, #offGlue_pc]
+#define SAVE_PC_TO_GLUE() str rPC, [rGLUE, #offGlue_pc]
+#define LOAD_FP_FROM_GLUE() ldr rFP, [rGLUE, #offGlue_fp]
+#define SAVE_FP_TO_GLUE() str rFP, [rGLUE, #offGlue_fp]
+#define LOAD_PC_FP_FROM_GLUE() ldmia rGLUE, {rPC, rFP}
+#define SAVE_PC_FP_TO_GLUE() stmia rGLUE, {rPC, rFP}
+
+/*
+ * "export" the PC to the stack frame, f/b/o future exception objects. Must
+ * be done *before* something calls dvmThrowException.
+ *
+ * In C this is "SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc", i.e.
+ * fp - sizeof(StackSaveArea) + offsetof(SaveArea, xtra.currentPc)
+ *
+ * It's okay to do this more than once.
+ */
+#define EXPORT_PC() \
+ str rPC, [rFP, #(-sizeofStackSaveArea + offStackSaveArea_currentPc)]
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+#define SAVEAREA_FROM_FP(_reg, _fpreg) \
+ sub _reg, _fpreg, #sizeofStackSaveArea
+
+/*
+ * Fetch the next instruction from rPC into rINST. Does not advance rPC.
+ */
+#define FETCH_INST() ldrh rINST, [rPC]
+
+/*
+ * Fetch the next instruction from the specified offset. Advances rPC
+ * to point to the next instruction. "_count" is in 16-bit code units.
+ *
+ * Because of the limited size of immediate constants on ARM, this is only
+ * suitable for small forward movements (i.e. don't try to implement "goto"
+ * with this).
+ *
+ * This must come AFTER anything that can throw an exception, or the
+ * exception catch may miss. (This also implies that it must come after
+ * EXPORT_PC().)
+ */
+#define FETCH_ADVANCE_INST(_count) ldrh rINST, [rPC, #(_count*2)]!
+
+/*
+ * The operation performed here is similar to FETCH_ADVANCE_INST, except the
+ * src and dest registers are parameterized (not hard-wired to rPC and rINST).
+ */
+#define PREFETCH_ADVANCE_INST(_dreg, _sreg, _count) \
+ ldrh _dreg, [_sreg, #(_count*2)]!
+
+/*
+ * Fetch the next instruction from an offset specified by _reg. Updates
+ * rPC to point to the next instruction. "_reg" must specify the distance
+ * in bytes, *not* 16-bit code units, and may be a signed value.
+ *
+ * We want to write "ldrh rINST, [rPC, _reg, lsl #2]!", but some of the
+ * bits that hold the shift distance are used for the half/byte/sign flags.
+ * In some cases we can pre-double _reg for free, so we require a byte offset
+ * here.
+ */
+#define FETCH_ADVANCE_INST_RB(_reg) ldrh rINST, [rPC, _reg]!
+
+/*
+ * Fetch a half-word code unit from an offset past the current PC. The
+ * "_count" value is in 16-bit code units. Does not advance rPC.
+ *
+ * The "_S" variant works the same but treats the value as signed.
+ */
+#define FETCH(_reg, _count) ldrh _reg, [rPC, #(_count*2)]
+#define FETCH_S(_reg, _count) ldrsh _reg, [rPC, #(_count*2)]
+
+/*
+ * Fetch one byte from an offset past the current PC. Pass in the same
+ * "_count" as you would for FETCH, and an additional 0/1 indicating which
+ * byte of the halfword you want (lo/hi).
+ */
+#define FETCH_B(_reg, _count, _byte) ldrb _reg, [rPC, #(_count*2+_byte)]
+
+/*
+ * Put the instruction's opcode field into the specified register.
+ */
+#define GET_INST_OPCODE(_reg) and _reg, rINST, #255
+
+/*
+ * Put the prefetched instruction's opcode field into the specified register.
+ */
+#define GET_PREFETCHED_OPCODE(_oreg, _ireg) and _oreg, _ireg, #255
+
+/*
+ * Begin executing the opcode in _reg. Because this only jumps within the
+ * interpreter, we don't have to worry about pre-ARMv5 THUMB interwork.
+ */
+#define GOTO_OPCODE(_reg) add pc, rIBASE, _reg, lsl #6
+#define GOTO_OPCODE_IFEQ(_reg) addeq pc, rIBASE, _reg, lsl #6
+#define GOTO_OPCODE_IFNE(_reg) addne pc, rIBASE, _reg, lsl #6
+
+/*
+ * Get/set the 32-bit value from a Dalvik register.
+ */
+#define GET_VREG(_reg, _vreg) ldr _reg, [rFP, _vreg, lsl #2]
+#define SET_VREG(_reg, _vreg) str _reg, [rFP, _vreg, lsl #2]
+
+#if defined(WITH_JIT)
+#define GET_JIT_PROF_TABLE(_reg) ldr _reg,[rGLUE,#offGlue_pJitProfTable]
+#define GET_JIT_THRESHOLD(_reg) ldr _reg,[rGLUE,#offGlue_jitThreshold]
+#endif
+
+/*
+ * Convert a virtual register index into an address.
+ */
+#define VREG_INDEX_TO_ADDR(_reg, _vreg) \
+ add _reg, rFP, _vreg, lsl #2
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../common/asm-constants.h"
+
+#if defined(WITH_JIT)
+#include "../common/jit-config.h"
+#endif
+
+/* File: armv5te/platform.S */
+/*
+ * ===========================================================================
+ * CPU-version-specific defines
+ * ===========================================================================
+ */
+
+/*
+ * Macro for "LDR PC,xxx", which is not allowed pre-ARMv5. Essentially a
+ * one-way branch.
+ *
+ * May modify IP. Does not modify LR.
+ */
+.macro LDR_PC source
+ ldr pc, \source
+.endm
+
+/*
+ * Macro for "MOV LR,PC / LDR PC,xxx", which is not allowed pre-ARMv5.
+ * Jump to subroutine.
+ *
+ * May modify IP and LR.
+ */
+.macro LDR_PC_LR source
+ mov lr, pc
+ ldr pc, \source
+.endm
+
+/*
+ * Macro for "LDMFD SP!, {...regs...,PC}".
+ *
+ * May modify IP and LR.
+ */
+.macro LDMFD_PC regs
+ ldmfd sp!, {\regs,pc}
+.endm
+
+/*
+ * Macro for data memory barrier; not meaningful pre-ARMv6K.
+ */
+.macro SMP_DMB
+.endm
+
+/* File: armv5te/entry.S */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Interpreter entry point.
+ */
+
+/*
+ * We don't have formal stack frames, so gdb scans upward in the code
+ * to find the start of the function (a label with the %function type),
+ * and then looks at the next few instructions to figure out what
+ * got pushed onto the stack. From this it figures out how to restore
+ * the registers, including PC, for the previous stack frame. If gdb
+ * sees a non-function label, it stops scanning, so either we need to
+ * have nothing but assembler-local labels between the entry point and
+ * the break, or we need to fake it out.
+ *
+ * When this is defined, we add some stuff to make gdb less confused.
+ */
+#define ASSIST_DEBUGGER 1
+
+ .text
+ .align 2
+ .global dvmMterpStdRun
+ .type dvmMterpStdRun, %function
+
+/*
+ * On entry:
+ * r0 MterpGlue* glue
+ *
+ * This function returns a boolean "changeInterp" value. The return comes
+ * via a call to dvmMterpStdBail().
+ */
+dvmMterpStdRun:
+#define MTERP_ENTRY1 \
+ .save {r4-r10,fp,lr}; \
+ stmfd sp!, {r4-r10,fp,lr} @ save 9 regs
+#define MTERP_ENTRY2 \
+ .pad #4; \
+ sub sp, sp, #4 @ align 64
+
+ .fnstart
+ MTERP_ENTRY1
+ MTERP_ENTRY2
+
+ /* save stack pointer, add magic word for debuggerd */
+ str sp, [r0, #offGlue_bailPtr] @ save SP for eventual return
+
+ /* set up "named" registers, figure out entry point */
+ mov rGLUE, r0 @ set rGLUE
+ ldr r1, [r0, #offGlue_entryPoint] @ enum is 4 bytes in aapcs-EABI
+ LOAD_PC_FP_FROM_GLUE() @ load rPC and rFP from "glue"
+ adr rIBASE, dvmAsmInstructionStart @ set rIBASE
+ cmp r1, #kInterpEntryInstr @ usual case?
+ bne .Lnot_instr @ no, handle it
+
+#if defined(WITH_JIT)
+.LentryInstr:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ /* Entry is always a possible trace start */
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_INST()
+ mov r1, #0 @ prepare the value for the new state
+ str r1, [r10, #offThread_inJitCodeCache] @ back to the interp land
+ cmp r0,#0 @ is profiling disabled?
+#if !defined(WITH_SELF_VERIFICATION)
+ bne common_updateProfile @ profiling is enabled
+#else
+ ldr r2, [r10, #offThread_shadowSpace] @ to find out the jit exit state
+ beq 1f @ profiling is disabled
+ ldr r3, [r2, #offShadowSpace_jitExitState] @ jit exit state
+ cmp r3, #kSVSTraceSelect @ hot trace following?
+ moveq r2,#kJitTSelectRequestHot @ ask for trace selection
+ beq common_selectTrace @ go build the trace
+ cmp r3, #kSVSNoProfile @ don't profile the next instruction?
+ beq 1f @ intrepret the next instruction
+ b common_updateProfile @ collect profiles
+#endif
+1:
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+#else
+ /* start executing the instruction at rPC */
+ FETCH_INST() @ load rINST from rPC
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+.Lnot_instr:
+ cmp r1, #kInterpEntryReturn @ were we returning from a method?
+ beq common_returnFromMethod
+
+.Lnot_return:
+ cmp r1, #kInterpEntryThrow @ were we throwing an exception?
+ beq common_exceptionThrown
+
+#if defined(WITH_JIT)
+.Lnot_throw:
+ ldr r10,[rGLUE, #offGlue_jitResumeNPC]
+ ldr r2,[rGLUE, #offGlue_jitResumeDPC]
+ cmp r1, #kInterpEntryResume @ resuming after Jit single-step?
+ bne .Lbad_arg
+ cmp rPC,r2
+ bne .LentryInstr @ must have branched, don't resume
+#if defined(WITH_SELF_VERIFICATION)
+ @ glue->entryPoint will be set in dvmSelfVerificationSaveState
+ b jitSVShadowRunStart @ re-enter the translation after the
+ @ single-stepped instruction
+ @noreturn
+#endif
+ mov r1, #kInterpEntryInstr
+ str r1, [rGLUE, #offGlue_entryPoint]
+ bx r10 @ re-enter the translation
+#endif
+
+.Lbad_arg:
+ ldr r0, strBadEntryPoint
+ @ r1 holds value of entryPoint
+ bl printf
+ bl dvmAbort
+ .fnend
+
+
+ .global dvmMterpStdBail
+ .type dvmMterpStdBail, %function
+
+/*
+ * Restore the stack pointer and PC from the save point established on entry.
+ * This is essentially the same as a longjmp, but should be cheaper. The
+ * last instruction causes us to return to whoever called dvmMterpStdRun.
+ *
+ * We pushed some registers on the stack in dvmMterpStdRun, then saved
+ * SP and LR. Here we restore SP, restore the registers, and then restore
+ * LR to PC.
+ *
+ * On entry:
+ * r0 MterpGlue* glue
+ * r1 bool changeInterp
+ */
+dvmMterpStdBail:
+ ldr sp, [r0, #offGlue_bailPtr] @ sp<- saved SP
+ mov r0, r1 @ return the changeInterp value
+ add sp, sp, #4 @ un-align 64
+ LDMFD_PC "r4-r10,fp" @ restore 9 regs and return
+
+
+/*
+ * String references.
+ */
+strBadEntryPoint:
+ .word .LstrBadEntryPoint
+
+
+ .global dvmAsmInstructionStart
+ .type dvmAsmInstructionStart, %function
+dvmAsmInstructionStart = .L_OP_NOP
+ .text
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NOP: /* 0x00 */
+/* File: armv5te/OP_NOP.S */
+ FETCH_ADVANCE_INST(1) @ advance to next instr, load rINST
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ GOTO_OPCODE(ip) @ execute it
+
+#ifdef ASSIST_DEBUGGER
+ /* insert fake function header to help gdb find the stack frame */
+ .type dalvik_inst, %function
+dalvik_inst:
+ .fnstart
+ MTERP_ENTRY1
+ MTERP_ENTRY2
+ .fnend
+#endif
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE: /* 0x01 */
+/* File: armv5te/OP_MOVE.S */
+ /* for move, move-object, long-to-int */
+ /* op vA, vB */
+ mov r1, rINST, lsr #12 @ r1<- B from 15:12
+ mov r0, rINST, lsr #8 @ r0<- A from 11:8
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[B]
+ and r0, r0, #15
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ SET_VREG(r2, r0) @ fp[A]<- r2
+ GOTO_OPCODE(ip) @ execute next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_FROM16: /* 0x02 */
+/* File: armv5te/OP_MOVE_FROM16.S */
+ /* for: move/from16, move-object/from16 */
+ /* op vAA, vBBBB */
+ FETCH(r1, 1) @ r1<- BBBB
+ mov r0, rINST, lsr #8 @ r0<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[BBBB]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r0) @ fp[AA]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_16: /* 0x03 */
+/* File: armv5te/OP_MOVE_16.S */
+ /* for: move/16, move-object/16 */
+ /* op vAAAA, vBBBB */
+ FETCH(r1, 2) @ r1<- BBBB
+ FETCH(r0, 1) @ r0<- AAAA
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[BBBB]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r0) @ fp[AAAA]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_WIDE: /* 0x04 */
+/* File: armv5te/OP_MOVE_WIDE.S */
+ /* move-wide vA, vB */
+ /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+ mov r2, rINST, lsr #8 @ r2<- A(+)
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r2, r2, #15
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[A]
+ ldmia r3, {r0-r1} @ r0/r1<- fp[B]
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r2, {r0-r1} @ fp[A]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_WIDE_FROM16: /* 0x05 */
+/* File: armv5te/OP_MOVE_WIDE_FROM16.S */
+ /* move-wide/from16 vAA, vBBBB */
+ /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+ FETCH(r3, 1) @ r3<- BBBB
+ mov r2, rINST, lsr #8 @ r2<- AA
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[BBBB]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[AA]
+ ldmia r3, {r0-r1} @ r0/r1<- fp[BBBB]
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r2, {r0-r1} @ fp[AA]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_WIDE_16: /* 0x06 */
+/* File: armv5te/OP_MOVE_WIDE_16.S */
+ /* move-wide/16 vAAAA, vBBBB */
+ /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+ FETCH(r3, 2) @ r3<- BBBB
+ FETCH(r2, 1) @ r2<- AAAA
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[BBBB]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[AAAA]
+ ldmia r3, {r0-r1} @ r0/r1<- fp[BBBB]
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r2, {r0-r1} @ fp[AAAA]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_OBJECT: /* 0x07 */
+/* File: armv5te/OP_MOVE_OBJECT.S */
+/* File: armv5te/OP_MOVE.S */
+ /* for move, move-object, long-to-int */
+ /* op vA, vB */
+ mov r1, rINST, lsr #12 @ r1<- B from 15:12
+ mov r0, rINST, lsr #8 @ r0<- A from 11:8
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[B]
+ and r0, r0, #15
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ SET_VREG(r2, r0) @ fp[A]<- r2
+ GOTO_OPCODE(ip) @ execute next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_OBJECT_FROM16: /* 0x08 */
+/* File: armv5te/OP_MOVE_OBJECT_FROM16.S */
+/* File: armv5te/OP_MOVE_FROM16.S */
+ /* for: move/from16, move-object/from16 */
+ /* op vAA, vBBBB */
+ FETCH(r1, 1) @ r1<- BBBB
+ mov r0, rINST, lsr #8 @ r0<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[BBBB]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r0) @ fp[AA]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_OBJECT_16: /* 0x09 */
+/* File: armv5te/OP_MOVE_OBJECT_16.S */
+/* File: armv5te/OP_MOVE_16.S */
+ /* for: move/16, move-object/16 */
+ /* op vAAAA, vBBBB */
+ FETCH(r1, 2) @ r1<- BBBB
+ FETCH(r0, 1) @ r0<- AAAA
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[BBBB]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r0) @ fp[AAAA]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_RESULT: /* 0x0a */
+/* File: armv5te/OP_MOVE_RESULT.S */
+ /* for: move-result, move-result-object */
+ /* op vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ ldr r0, [rGLUE, #offGlue_retval] @ r0<- glue->retval.i
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[AA]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_RESULT_WIDE: /* 0x0b */
+/* File: armv5te/OP_MOVE_RESULT_WIDE.S */
+ /* move-result-wide vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ add r3, rGLUE, #offGlue_retval @ r3<- &glue->retval
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[AA]
+ ldmia r3, {r0-r1} @ r0/r1<- retval.j
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r2, {r0-r1} @ fp[AA]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_RESULT_OBJECT: /* 0x0c */
+/* File: armv5te/OP_MOVE_RESULT_OBJECT.S */
+/* File: armv5te/OP_MOVE_RESULT.S */
+ /* for: move-result, move-result-object */
+ /* op vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ ldr r0, [rGLUE, #offGlue_retval] @ r0<- glue->retval.i
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[AA]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_EXCEPTION: /* 0x0d */
+/* File: armv5te/OP_MOVE_EXCEPTION.S */
+ /* move-exception vAA */
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ mov r2, rINST, lsr #8 @ r2<- AA
+ ldr r3, [r0, #offThread_exception] @ r3<- dvmGetException bypass
+ mov r1, #0 @ r1<- 0
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ SET_VREG(r3, r2) @ fp[AA]<- exception obj
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ str r1, [r0, #offThread_exception] @ dvmClearException bypass
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RETURN_VOID: /* 0x0e */
+/* File: armv5te/OP_RETURN_VOID.S */
+ b common_returnFromMethod
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RETURN: /* 0x0f */
+/* File: armv5te/OP_RETURN.S */
+ /*
+ * Return a 32-bit value. Copies the return value into the "glue"
+ * structure, then jumps to the return handler.
+ *
+ * for: return, return-object
+ */
+ /* op vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ GET_VREG(r0, r2) @ r0<- vAA
+ str r0, [rGLUE, #offGlue_retval] @ retval.i <- vAA
+ b common_returnFromMethod
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RETURN_WIDE: /* 0x10 */
+/* File: armv5te/OP_RETURN_WIDE.S */
+ /*
+ * Return a 64-bit value. Copies the return value into the "glue"
+ * structure, then jumps to the return handler.
+ */
+ /* return-wide vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[AA]
+ add r3, rGLUE, #offGlue_retval @ r3<- &glue->retval
+ ldmia r2, {r0-r1} @ r0/r1 <- vAA/vAA+1
+ stmia r3, {r0-r1} @ retval<- r0/r1
+ b common_returnFromMethod
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RETURN_OBJECT: /* 0x11 */
+/* File: armv5te/OP_RETURN_OBJECT.S */
+/* File: armv5te/OP_RETURN.S */
+ /*
+ * Return a 32-bit value. Copies the return value into the "glue"
+ * structure, then jumps to the return handler.
+ *
+ * for: return, return-object
+ */
+ /* op vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ GET_VREG(r0, r2) @ r0<- vAA
+ str r0, [rGLUE, #offGlue_retval] @ retval.i <- vAA
+ b common_returnFromMethod
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_4: /* 0x12 */
+/* File: armv5te/OP_CONST_4.S */
+ /* const/4 vA, #+B */
+ mov r1, rINST, lsl #16 @ r1<- Bxxx0000
+ mov r0, rINST, lsr #8 @ r0<- A+
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ mov r1, r1, asr #28 @ r1<- sssssssB (sign-extended)
+ and r0, r0, #15
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ SET_VREG(r1, r0) @ fp[A]<- r1
+ GOTO_OPCODE(ip) @ execute next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_16: /* 0x13 */
+/* File: armv5te/OP_CONST_16.S */
+ /* const/16 vAA, #+BBBB */
+ FETCH_S(r0, 1) @ r0<- ssssBBBB (sign-extended)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r0, r3) @ vAA<- r0
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST: /* 0x14 */
+/* File: armv5te/OP_CONST.S */
+ /* const vAA, #+BBBBbbbb */
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH(r0, 1) @ r0<- bbbb (low)
+ FETCH(r1, 2) @ r1<- BBBB (high)
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ orr r0, r0, r1, lsl #16 @ r0<- BBBBbbbb
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r3) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_HIGH16: /* 0x15 */
+/* File: armv5te/OP_CONST_HIGH16.S */
+ /* const/high16 vAA, #+BBBB0000 */
+ FETCH(r0, 1) @ r0<- 0000BBBB (zero-extended)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ mov r0, r0, lsl #16 @ r0<- BBBB0000
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r0, r3) @ vAA<- r0
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_WIDE_16: /* 0x16 */
+/* File: armv5te/OP_CONST_WIDE_16.S */
+ /* const-wide/16 vAA, #+BBBB */
+ FETCH_S(r0, 1) @ r0<- ssssBBBB (sign-extended)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ mov r1, r0, asr #31 @ r1<- ssssssss
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_WIDE_32: /* 0x17 */
+/* File: armv5te/OP_CONST_WIDE_32.S */
+ /* const-wide/32 vAA, #+BBBBbbbb */
+ FETCH(r0, 1) @ r0<- 0000bbbb (low)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH_S(r2, 2) @ r2<- ssssBBBB (high)
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ orr r0, r0, r2, lsl #16 @ r0<- BBBBbbbb
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[AA]
+ mov r1, r0, asr #31 @ r1<- ssssssss
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_WIDE: /* 0x18 */
+/* File: armv5te/OP_CONST_WIDE.S */
+ /* const-wide vAA, #+HHHHhhhhBBBBbbbb */
+ FETCH(r0, 1) @ r0<- bbbb (low)
+ FETCH(r1, 2) @ r1<- BBBB (low middle)
+ FETCH(r2, 3) @ r2<- hhhh (high middle)
+ orr r0, r0, r1, lsl #16 @ r0<- BBBBbbbb (low word)
+ FETCH(r3, 4) @ r3<- HHHH (high)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ orr r1, r2, r3, lsl #16 @ r1<- HHHHhhhh (high word)
+ FETCH_ADVANCE_INST(5) @ advance rPC, load rINST
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_WIDE_HIGH16: /* 0x19 */
+/* File: armv5te/OP_CONST_WIDE_HIGH16.S */
+ /* const-wide/high16 vAA, #+BBBB000000000000 */
+ FETCH(r1, 1) @ r1<- 0000BBBB (zero-extended)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ mov r0, #0 @ r0<- 00000000
+ mov r1, r1, lsl #16 @ r1<- BBBB0000
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_STRING: /* 0x1a */
+/* File: armv5te/OP_CONST_STRING.S */
+ /* const/string vAA, String@BBBB */
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- glue->methodClassDex
+ mov r9, rINST, lsr #8 @ r9<- AA
+ ldr r2, [r2, #offDvmDex_pResStrings] @ r2<- dvmDex->pResStrings
+ ldr r0, [r2, r1, lsl #2] @ r0<- pResStrings[BBBB]
+ cmp r0, #0 @ not yet resolved?
+ beq .LOP_CONST_STRING_resolve
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_STRING_JUMBO: /* 0x1b */
+/* File: armv5te/OP_CONST_STRING_JUMBO.S */
+ /* const/string vAA, String@BBBBBBBB */
+ FETCH(r0, 1) @ r0<- bbbb (low)
+ FETCH(r1, 2) @ r1<- BBBB (high)
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- glue->methodClassDex
+ mov r9, rINST, lsr #8 @ r9<- AA
+ ldr r2, [r2, #offDvmDex_pResStrings] @ r2<- dvmDex->pResStrings
+ orr r1, r0, r1, lsl #16 @ r1<- BBBBbbbb
+ ldr r0, [r2, r1, lsl #2] @ r0<- pResStrings[BBBB]
+ cmp r0, #0
+ beq .LOP_CONST_STRING_JUMBO_resolve
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_CLASS: /* 0x1c */
+/* File: armv5te/OP_CONST_CLASS.S */
+ /* const/class vAA, Class@BBBB */
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- glue->methodClassDex
+ mov r9, rINST, lsr #8 @ r9<- AA
+ ldr r2, [r2, #offDvmDex_pResClasses] @ r2<- dvmDex->pResClasses
+ ldr r0, [r2, r1, lsl #2] @ r0<- pResClasses[BBBB]
+ cmp r0, #0 @ not yet resolved?
+ beq .LOP_CONST_CLASS_resolve
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MONITOR_ENTER: /* 0x1d */
+/* File: armv5te/OP_MONITOR_ENTER.S */
+ /*
+ * Synchronize on an object.
+ */
+ /* monitor-enter vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ GET_VREG(r1, r2) @ r1<- vAA (object)
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ cmp r1, #0 @ null object?
+ EXPORT_PC() @ need for precise GC, MONITOR_TRACKING
+ beq common_errNullObject @ null object, throw an exception
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ bl dvmLockObject @ call(self, obj)
+#ifdef WITH_DEADLOCK_PREDICTION /* implies WITH_MONITOR_TRACKING */
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ ldr r1, [r0, #offThread_exception] @ check for exception
+ cmp r1, #0
+ bne common_exceptionThrown @ exception raised, bail out
+#endif
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MONITOR_EXIT: /* 0x1e */
+/* File: armv5te/OP_MONITOR_EXIT.S */
+ /*
+ * Unlock an object.
+ *
+ * Exceptions that occur when unlocking a monitor need to appear as
+ * if they happened at the following instruction. See the Dalvik
+ * instruction spec.
+ */
+ /* monitor-exit vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ EXPORT_PC() @ before fetch: export the PC
+ GET_VREG(r1, r2) @ r1<- vAA (object)
+ cmp r1, #0 @ null object?
+ beq 1f @ yes
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ bl dvmUnlockObject @ r0<- success for unlock(self, obj)
+ cmp r0, #0 @ failed?
+ FETCH_ADVANCE_INST(1) @ before throw: advance rPC, load rINST
+ beq common_exceptionThrown @ yes, exception is pending
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+1:
+ FETCH_ADVANCE_INST(1) @ advance before throw
+ b common_errNullObject
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CHECK_CAST: /* 0x1f */
+/* File: armv5te/OP_CHECK_CAST.S */
+ /*
+ * Check to see if a cast from one class to another is allowed.
+ */
+ /* check-cast vAA, class@BBBB */
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH(r2, 1) @ r2<- BBBB
+ GET_VREG(r9, r3) @ r9<- object
+ ldr r0, [rGLUE, #offGlue_methodClassDex] @ r0<- pDvmDex
+ cmp r9, #0 @ is object null?
+ ldr r0, [r0, #offDvmDex_pResClasses] @ r0<- pDvmDex->pResClasses
+ beq .LOP_CHECK_CAST_okay @ null obj, cast always succeeds
+ ldr r1, [r0, r2, lsl #2] @ r1<- resolved class
+ ldr r0, [r9, #offObject_clazz] @ r0<- obj->clazz
+ cmp r1, #0 @ have we resolved this before?
+ beq .LOP_CHECK_CAST_resolve @ not resolved, do it now
+.LOP_CHECK_CAST_resolved:
+ cmp r0, r1 @ same class (trivial success)?
+ bne .LOP_CHECK_CAST_fullcheck @ no, do full check
+.LOP_CHECK_CAST_okay:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INSTANCE_OF: /* 0x20 */
+/* File: armv5te/OP_INSTANCE_OF.S */
+ /*
+ * Check to see if an object reference is an instance of a class.
+ *
+ * Most common situation is a non-null object, being compared against
+ * an already-resolved class.
+ */
+ /* instance-of vA, vB, class@CCCC */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r3) @ r0<- vB (object)
+ and r9, r9, #15 @ r9<- A
+ cmp r0, #0 @ is object null?
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- pDvmDex
+ beq .LOP_INSTANCE_OF_store @ null obj, not an instance, store r0
+ FETCH(r3, 1) @ r3<- CCCC
+ ldr r2, [r2, #offDvmDex_pResClasses] @ r2<- pDvmDex->pResClasses
+ ldr r1, [r2, r3, lsl #2] @ r1<- resolved class
+ ldr r0, [r0, #offObject_clazz] @ r0<- obj->clazz
+ cmp r1, #0 @ have we resolved this before?
+ beq .LOP_INSTANCE_OF_resolve @ not resolved, do it now
+.LOP_INSTANCE_OF_resolved: @ r0=obj->clazz, r1=resolved class
+ cmp r0, r1 @ same class (trivial success)?
+ beq .LOP_INSTANCE_OF_trivial @ yes, trivial finish
+ b .LOP_INSTANCE_OF_fullcheck @ no, do full check
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ARRAY_LENGTH: /* 0x21 */
+/* File: armv5te/OP_ARRAY_LENGTH.S */
+ /*
+ * Return the length of an array.
+ */
+ mov r1, rINST, lsr #12 @ r1<- B
+ mov r2, rINST, lsr #8 @ r2<- A+
+ GET_VREG(r0, r1) @ r0<- vB (object ref)
+ and r2, r2, #15 @ r2<- A
+ cmp r0, #0 @ is object null?
+ beq common_errNullObject @ yup, fail
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ ldr r3, [r0, #offArrayObject_length] @ r3<- array length
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r3, r2) @ vB<- length
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEW_INSTANCE: /* 0x22 */
+/* File: armv5te/OP_NEW_INSTANCE.S */
+ /*
+ * Create a new instance of a class.
+ */
+ /* new-instance vAA, class@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResClasses] @ r3<- pDvmDex->pResClasses
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved class
+ EXPORT_PC() @ req'd for init, resolve, alloc
+ cmp r0, #0 @ already resolved?
+ beq .LOP_NEW_INSTANCE_resolve @ no, resolve it now
+.LOP_NEW_INSTANCE_resolved: @ r0=class
+ ldrb r1, [r0, #offClassObject_status] @ r1<- ClassStatus enum
+ cmp r1, #CLASS_INITIALIZED @ has class been initialized?
+ bne .LOP_NEW_INSTANCE_needinit @ no, init class now
+.LOP_NEW_INSTANCE_initialized: @ r0=class
+ mov r1, #ALLOC_DONT_TRACK @ flags for alloc call
+ bl dvmAllocObject @ r0<- new object
+ b .LOP_NEW_INSTANCE_finish @ continue
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEW_ARRAY: /* 0x23 */
+/* File: armv5te/OP_NEW_ARRAY.S */
+ /*
+ * Allocate an array of objects, specified with the array class
+ * and a count.
+ *
+ * The verifier guarantees that this is an array class, so we don't
+ * check for it here.
+ */
+ /* new-array vA, vB, class@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ FETCH(r2, 1) @ r2<- CCCC
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ GET_VREG(r1, r0) @ r1<- vB (array length)
+ ldr r3, [r3, #offDvmDex_pResClasses] @ r3<- pDvmDex->pResClasses
+ cmp r1, #0 @ check length
+ ldr r0, [r3, r2, lsl #2] @ r0<- resolved class
+ bmi common_errNegativeArraySize @ negative length, bail
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ req'd for resolve, alloc
+ bne .LOP_NEW_ARRAY_finish @ resolved, continue
+ b .LOP_NEW_ARRAY_resolve @ do resolve now
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FILLED_NEW_ARRAY: /* 0x24 */
+/* File: armv5te/OP_FILLED_NEW_ARRAY.S */
+ /*
+ * Create a new array with elements filled from registers.
+ *
+ * for: filled-new-array, filled-new-array/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResClasses] @ r3<- pDvmDex->pResClasses
+ EXPORT_PC() @ need for resolve and alloc
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved class
+ mov r10, rINST, lsr #8 @ r10<- AA or BA
+ cmp r0, #0 @ already resolved?
+ bne .LOP_FILLED_NEW_ARRAY_continue @ yes, continue on
+8: ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ mov r2, #0 @ r2<- false
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- call(clazz, ref)
+ cmp r0, #0 @ got null?
+ beq common_exceptionThrown @ yes, handle exception
+ b .LOP_FILLED_NEW_ARRAY_continue
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FILLED_NEW_ARRAY_RANGE: /* 0x25 */
+/* File: armv5te/OP_FILLED_NEW_ARRAY_RANGE.S */
+/* File: armv5te/OP_FILLED_NEW_ARRAY.S */
+ /*
+ * Create a new array with elements filled from registers.
+ *
+ * for: filled-new-array, filled-new-array/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResClasses] @ r3<- pDvmDex->pResClasses
+ EXPORT_PC() @ need for resolve and alloc
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved class
+ mov r10, rINST, lsr #8 @ r10<- AA or BA
+ cmp r0, #0 @ already resolved?
+ bne .LOP_FILLED_NEW_ARRAY_RANGE_continue @ yes, continue on
+8: ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ mov r2, #0 @ r2<- false
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- call(clazz, ref)
+ cmp r0, #0 @ got null?
+ beq common_exceptionThrown @ yes, handle exception
+ b .LOP_FILLED_NEW_ARRAY_RANGE_continue
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FILL_ARRAY_DATA: /* 0x26 */
+/* File: armv5te/OP_FILL_ARRAY_DATA.S */
+ /* fill-array-data vAA, +BBBBBBBB */
+ FETCH(r0, 1) @ r0<- bbbb (lo)
+ FETCH(r1, 2) @ r1<- BBBB (hi)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ orr r1, r0, r1, lsl #16 @ r1<- BBBBbbbb
+ GET_VREG(r0, r3) @ r0<- vAA (array object)
+ add r1, rPC, r1, lsl #1 @ r1<- PC + BBBBbbbb*2 (array data off.)
+ EXPORT_PC();
+ bl dvmInterpHandleFillArrayData@ fill the array with predefined data
+ cmp r0, #0 @ 0 means an exception is thrown
+ beq common_exceptionThrown @ has exception
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_THROW: /* 0x27 */
+/* File: armv5te/OP_THROW.S */
+ /*
+ * Throw an exception object in the current thread.
+ */
+ /* throw vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ GET_VREG(r1, r2) @ r1<- vAA (exception object)
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ EXPORT_PC() @ exception handler can throw
+ cmp r1, #0 @ null object?
+ beq common_errNullObject @ yes, throw an NPE instead
+ @ bypass dvmSetException, just store it
+ str r1, [r0, #offThread_exception] @ thread->exception<- obj
+ b common_exceptionThrown
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_GOTO: /* 0x28 */
+/* File: armv5te/OP_GOTO.S */
+ /*
+ * Unconditional branch, 8-bit offset.
+ *
+ * The branch distance is a signed code-unit offset, which we need to
+ * double to get a byte offset.
+ */
+ /* goto +AA */
+ mov r0, rINST, lsl #16 @ r0<- AAxx0000
+ movs r9, r0, asr #24 @ r9<- ssssssAA (sign-extended)
+ mov r9, r9, lsl #1 @ r9<- byte offset
+ bmi common_backwardBranch @ backward branch, do periodic checks
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_GOTO_16: /* 0x29 */
+/* File: armv5te/OP_GOTO_16.S */
+ /*
+ * Unconditional branch, 16-bit offset.
+ *
+ * The branch distance is a signed code-unit offset, which we need to
+ * double to get a byte offset.
+ */
+ /* goto/16 +AAAA */
+ FETCH_S(r0, 1) @ r0<- ssssAAAA (sign-extended)
+ movs r9, r0, asl #1 @ r9<- byte offset, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_GOTO_32: /* 0x2a */
+/* File: armv5te/OP_GOTO_32.S */
+ /*
+ * Unconditional branch, 32-bit offset.
+ *
+ * The branch distance is a signed code-unit offset, which we need to
+ * double to get a byte offset.
+ *
+ * Unlike most opcodes, this one is allowed to branch to itself, so
+ * our "backward branch" test must be "<=0" instead of "<0". The ORRS
+ * instruction doesn't affect the V flag, so we need to clear it
+ * explicitly.
+ */
+ /* goto/32 +AAAAAAAA */
+ FETCH(r0, 1) @ r0<- aaaa (lo)
+ FETCH(r1, 2) @ r1<- AAAA (hi)
+ cmp ip, ip @ (clear V flag during stall)
+ orrs r0, r0, r1, lsl #16 @ r0<- AAAAaaaa, check sign
+ mov r9, r0, asl #1 @ r9<- byte offset
+ ble common_backwardBranch @ backward branch, do periodic checks
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_PACKED_SWITCH: /* 0x2b */
+/* File: armv5te/OP_PACKED_SWITCH.S */
+ /*
+ * Handle a packed-switch or sparse-switch instruction. In both cases
+ * we decode it and hand it off to a helper function.
+ *
+ * We don't really expect backward branches in a switch statement, but
+ * they're perfectly legal, so we check for them here.
+ *
+ * for: packed-switch, sparse-switch
+ */
+ /* op vAA, +BBBB */
+ FETCH(r0, 1) @ r0<- bbbb (lo)
+ FETCH(r1, 2) @ r1<- BBBB (hi)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ orr r0, r0, r1, lsl #16 @ r0<- BBBBbbbb
+ GET_VREG(r1, r3) @ r1<- vAA
+ add r0, rPC, r0, lsl #1 @ r0<- PC + BBBBbbbb*2
+ bl dvmInterpHandlePackedSwitch @ r0<- code-unit branch offset
+ movs r9, r0, asl #1 @ r9<- branch byte offset, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+ beq common_backwardBranch @ (want to use BLE but V is unknown)
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPARSE_SWITCH: /* 0x2c */
+/* File: armv5te/OP_SPARSE_SWITCH.S */
+/* File: armv5te/OP_PACKED_SWITCH.S */
+ /*
+ * Handle a packed-switch or sparse-switch instruction. In both cases
+ * we decode it and hand it off to a helper function.
+ *
+ * We don't really expect backward branches in a switch statement, but
+ * they're perfectly legal, so we check for them here.
+ *
+ * for: packed-switch, sparse-switch
+ */
+ /* op vAA, +BBBB */
+ FETCH(r0, 1) @ r0<- bbbb (lo)
+ FETCH(r1, 2) @ r1<- BBBB (hi)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ orr r0, r0, r1, lsl #16 @ r0<- BBBBbbbb
+ GET_VREG(r1, r3) @ r1<- vAA
+ add r0, rPC, r0, lsl #1 @ r0<- PC + BBBBbbbb*2
+ bl dvmInterpHandleSparseSwitch @ r0<- code-unit branch offset
+ movs r9, r0, asl #1 @ r9<- branch byte offset, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+ beq common_backwardBranch @ (want to use BLE but V is unknown)
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CMPL_FLOAT: /* 0x2d */
+/* File: armv5te/OP_CMPL_FLOAT.S */
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+ * on what value we'd like to return when one of the operands is NaN.
+ *
+ * The operation we're implementing is:
+ * if (x == y)
+ * return 0;
+ * else if (x < y)
+ * return -1;
+ * else if (x > y)
+ * return 1;
+ * else
+ * return {-1,1}; // one or both operands was NaN
+ *
+ * The straightforward implementation requires 3 calls to functions
+ * that return a result in r0. We can do it with two calls if our
+ * EABI library supports __aeabi_cfcmple (only one if we want to check
+ * for NaN directly):
+ * check x <= y
+ * if <, return -1
+ * if ==, return 0
+ * check y <= x
+ * if <, return 1
+ * return {-1,1}
+ *
+ * for: cmpl-float, cmpg-float
+ */
+ /* op vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ GET_VREG(r9, r2) @ r9<- vBB
+ GET_VREG(r10, r3) @ r10<- vCC
+ mov r0, r9 @ copy to arg registers
+ mov r1, r10
+ bl __aeabi_cfcmple @ cmp <=: C clear if <, Z set if eq
+ bhi .LOP_CMPL_FLOAT_gt_or_nan @ C set and Z clear, disambiguate
+ mvncc r1, #0 @ (less than) r1<- -1
+ moveq r1, #0 @ (equal) r1<- 0, trumps less than
+.LOP_CMPL_FLOAT_finish:
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r3) @ vAA<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CMPG_FLOAT: /* 0x2e */
+/* File: armv5te/OP_CMPG_FLOAT.S */
+/* File: armv5te/OP_CMPL_FLOAT.S */
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+ * on what value we'd like to return when one of the operands is NaN.
+ *
+ * The operation we're implementing is:
+ * if (x == y)
+ * return 0;
+ * else if (x < y)
+ * return -1;
+ * else if (x > y)
+ * return 1;
+ * else
+ * return {-1,1}; // one or both operands was NaN
+ *
+ * The straightforward implementation requires 3 calls to functions
+ * that return a result in r0. We can do it with two calls if our
+ * EABI library supports __aeabi_cfcmple (only one if we want to check
+ * for NaN directly):
+ * check x <= y
+ * if <, return -1
+ * if ==, return 0
+ * check y <= x
+ * if <, return 1
+ * return {-1,1}
+ *
+ * for: cmpl-float, cmpg-float
+ */
+ /* op vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ GET_VREG(r9, r2) @ r9<- vBB
+ GET_VREG(r10, r3) @ r10<- vCC
+ mov r0, r9 @ copy to arg registers
+ mov r1, r10
+ bl __aeabi_cfcmple @ cmp <=: C clear if <, Z set if eq
+ bhi .LOP_CMPG_FLOAT_gt_or_nan @ C set and Z clear, disambiguate
+ mvncc r1, #0 @ (less than) r1<- -1
+ moveq r1, #0 @ (equal) r1<- 0, trumps less than
+.LOP_CMPG_FLOAT_finish:
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r3) @ vAA<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CMPL_DOUBLE: /* 0x2f */
+/* File: armv5te/OP_CMPL_DOUBLE.S */
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+ * on what value we'd like to return when one of the operands is NaN.
+ *
+ * See OP_CMPL_FLOAT for an explanation.
+ *
+ * For: cmpl-double, cmpg-double
+ */
+ /* op vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ and r9, r0, #255 @ r9<- BB
+ mov r10, r0, lsr #8 @ r10<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[BB]
+ add r10, rFP, r10, lsl #2 @ r10<- &fp[CC]
+ ldmia r9, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r10, {r2-r3} @ r2/r3<- vCC/vCC+1
+ bl __aeabi_cdcmple @ cmp <=: C clear if <, Z set if eq
+ bhi .LOP_CMPL_DOUBLE_gt_or_nan @ C set and Z clear, disambiguate
+ mvncc r1, #0 @ (less than) r1<- -1
+ moveq r1, #0 @ (equal) r1<- 0, trumps less than
+.LOP_CMPL_DOUBLE_finish:
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r3) @ vAA<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CMPG_DOUBLE: /* 0x30 */
+/* File: armv5te/OP_CMPG_DOUBLE.S */
+/* File: armv5te/OP_CMPL_DOUBLE.S */
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+ * on what value we'd like to return when one of the operands is NaN.
+ *
+ * See OP_CMPL_FLOAT for an explanation.
+ *
+ * For: cmpl-double, cmpg-double
+ */
+ /* op vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ and r9, r0, #255 @ r9<- BB
+ mov r10, r0, lsr #8 @ r10<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[BB]
+ add r10, rFP, r10, lsl #2 @ r10<- &fp[CC]
+ ldmia r9, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r10, {r2-r3} @ r2/r3<- vCC/vCC+1
+ bl __aeabi_cdcmple @ cmp <=: C clear if <, Z set if eq
+ bhi .LOP_CMPG_DOUBLE_gt_or_nan @ C set and Z clear, disambiguate
+ mvncc r1, #0 @ (less than) r1<- -1
+ moveq r1, #0 @ (equal) r1<- 0, trumps less than
+.LOP_CMPG_DOUBLE_finish:
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r3) @ vAA<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CMP_LONG: /* 0x31 */
+/* File: armv5te/OP_CMP_LONG.S */
+ /*
+ * Compare two 64-bit values. Puts 0, 1, or -1 into the destination
+ * register based on the results of the comparison.
+ *
+ * We load the full values with LDM, but in practice many values could
+ * be resolved by only looking at the high word. This could be made
+ * faster or slower by splitting the LDM into a pair of LDRs.
+ *
+ * If we just wanted to set condition flags, we could do this:
+ * subs ip, r0, r2
+ * sbcs ip, r1, r3
+ * subeqs ip, r0, r2
+ * Leaving { <0, 0, >0 } in ip. However, we have to set it to a specific
+ * integer value, which we can do with 2 conditional mov/mvn instructions
+ * (set 1, set -1; if they're equal we already have 0 in ip), giving
+ * us a constant 5-cycle path plus a branch at the end to the
+ * instruction epilogue code. The multi-compare approach below needs
+ * 2 or 3 cycles + branch if the high word doesn't match, 6 + branch
+ * in the worst case (the 64-bit values are equal).
+ */
+ /* cmp-long vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ cmp r1, r3 @ compare (vBB+1, vCC+1)
+ blt .LOP_CMP_LONG_less @ signed compare on high part
+ bgt .LOP_CMP_LONG_greater
+ subs r1, r0, r2 @ r1<- r0 - r2
+ bhi .LOP_CMP_LONG_greater @ unsigned compare on low part
+ bne .LOP_CMP_LONG_less
+ b .LOP_CMP_LONG_finish @ equal; r1 already holds 0
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_EQ: /* 0x32 */
+/* File: armv5te/OP_IF_EQ.S */
+/* File: armv5te/bincmp.S */
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ mov r0, rINST, lsr #8 @ r0<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r0, r0, #15
+ GET_VREG(r3, r1) @ r3<- vB
+ GET_VREG(r2, r0) @ r2<- vA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, r3 @ compare (vA, vB)
+ bne 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ b common_testUpdateProfile
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_NE: /* 0x33 */
+/* File: armv5te/OP_IF_NE.S */
+/* File: armv5te/bincmp.S */
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ mov r0, rINST, lsr #8 @ r0<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r0, r0, #15
+ GET_VREG(r3, r1) @ r3<- vB
+ GET_VREG(r2, r0) @ r2<- vA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, r3 @ compare (vA, vB)
+ beq 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ b common_testUpdateProfile
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_LT: /* 0x34 */
+/* File: armv5te/OP_IF_LT.S */
+/* File: armv5te/bincmp.S */
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ mov r0, rINST, lsr #8 @ r0<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r0, r0, #15
+ GET_VREG(r3, r1) @ r3<- vB
+ GET_VREG(r2, r0) @ r2<- vA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, r3 @ compare (vA, vB)
+ bge 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ b common_testUpdateProfile
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_GE: /* 0x35 */
+/* File: armv5te/OP_IF_GE.S */
+/* File: armv5te/bincmp.S */
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ mov r0, rINST, lsr #8 @ r0<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r0, r0, #15
+ GET_VREG(r3, r1) @ r3<- vB
+ GET_VREG(r2, r0) @ r2<- vA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, r3 @ compare (vA, vB)
+ blt 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ b common_testUpdateProfile
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_GT: /* 0x36 */
+/* File: armv5te/OP_IF_GT.S */
+/* File: armv5te/bincmp.S */
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ mov r0, rINST, lsr #8 @ r0<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r0, r0, #15
+ GET_VREG(r3, r1) @ r3<- vB
+ GET_VREG(r2, r0) @ r2<- vA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, r3 @ compare (vA, vB)
+ ble 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ b common_testUpdateProfile
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_LE: /* 0x37 */
+/* File: armv5te/OP_IF_LE.S */
+/* File: armv5te/bincmp.S */
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ mov r0, rINST, lsr #8 @ r0<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r0, r0, #15
+ GET_VREG(r3, r1) @ r3<- vB
+ GET_VREG(r2, r0) @ r2<- vA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, r3 @ compare (vA, vB)
+ bgt 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ b common_testUpdateProfile
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_EQZ: /* 0x38 */
+/* File: armv5te/OP_IF_EQZ.S */
+/* File: armv5te/zcmp.S */
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ mov r0, rINST, lsr #8 @ r0<- AA
+ GET_VREG(r2, r0) @ r2<- vAA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, #0 @ compare (vA, 0)
+ bne 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_NEZ: /* 0x39 */
+/* File: armv5te/OP_IF_NEZ.S */
+/* File: armv5te/zcmp.S */
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ mov r0, rINST, lsr #8 @ r0<- AA
+ GET_VREG(r2, r0) @ r2<- vAA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, #0 @ compare (vA, 0)
+ beq 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_LTZ: /* 0x3a */
+/* File: armv5te/OP_IF_LTZ.S */
+/* File: armv5te/zcmp.S */
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ mov r0, rINST, lsr #8 @ r0<- AA
+ GET_VREG(r2, r0) @ r2<- vAA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, #0 @ compare (vA, 0)
+ bge 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_GEZ: /* 0x3b */
+/* File: armv5te/OP_IF_GEZ.S */
+/* File: armv5te/zcmp.S */
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ mov r0, rINST, lsr #8 @ r0<- AA
+ GET_VREG(r2, r0) @ r2<- vAA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, #0 @ compare (vA, 0)
+ blt 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_GTZ: /* 0x3c */
+/* File: armv5te/OP_IF_GTZ.S */
+/* File: armv5te/zcmp.S */
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ mov r0, rINST, lsr #8 @ r0<- AA
+ GET_VREG(r2, r0) @ r2<- vAA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, #0 @ compare (vA, 0)
+ ble 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_LEZ: /* 0x3d */
+/* File: armv5te/OP_IF_LEZ.S */
+/* File: armv5te/zcmp.S */
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ mov r0, rINST, lsr #8 @ r0<- AA
+ GET_VREG(r2, r0) @ r2<- vAA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, #0 @ compare (vA, 0)
+ bgt 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_3E: /* 0x3e */
+/* File: armv5te/OP_UNUSED_3E.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_3F: /* 0x3f */
+/* File: armv5te/OP_UNUSED_3F.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_40: /* 0x40 */
+/* File: armv5te/OP_UNUSED_40.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_41: /* 0x41 */
+/* File: armv5te/OP_UNUSED_41.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_42: /* 0x42 */
+/* File: armv5te/OP_UNUSED_42.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_43: /* 0x43 */
+/* File: armv5te/OP_UNUSED_43.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET: /* 0x44 */
+/* File: armv5te/OP_AGET.S */
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #2 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldr r2, [r0, #offArrayObject_contents] @ r2<- vBB[vCC]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r9) @ vAA<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_WIDE: /* 0x45 */
+/* File: armv4t/OP_AGET_WIDE.S */
+ /*
+ * Array get, 64 bits. vAA <- vBB[vCC].
+ *
+ * Arrays of long/double are 64-bit aligned, so it's okay to use LDRD.
+ */
+ /* aget-wide vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #3 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcc .LOP_AGET_WIDE_finish @ okay, continue below
+ b common_errArrayIndex @ index >= length, bail
+ @ May want to swap the order of these two branches depending on how the
+ @ branch prediction (if any) handles conditional forward branches vs.
+ @ unconditional forward branches.
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_OBJECT: /* 0x46 */
+/* File: armv5te/OP_AGET_OBJECT.S */
+/* File: armv5te/OP_AGET.S */
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #2 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldr r2, [r0, #offArrayObject_contents] @ r2<- vBB[vCC]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r9) @ vAA<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_BOOLEAN: /* 0x47 */
+/* File: armv5te/OP_AGET_BOOLEAN.S */
+/* File: armv5te/OP_AGET.S */
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #0 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldrb r2, [r0, #offArrayObject_contents] @ r2<- vBB[vCC]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r9) @ vAA<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_BYTE: /* 0x48 */
+/* File: armv5te/OP_AGET_BYTE.S */
+/* File: armv5te/OP_AGET.S */
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #0 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldrsb r2, [r0, #offArrayObject_contents] @ r2<- vBB[vCC]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r9) @ vAA<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_CHAR: /* 0x49 */
+/* File: armv5te/OP_AGET_CHAR.S */
+/* File: armv5te/OP_AGET.S */
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #1 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldrh r2, [r0, #offArrayObject_contents] @ r2<- vBB[vCC]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r9) @ vAA<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_SHORT: /* 0x4a */
+/* File: armv5te/OP_AGET_SHORT.S */
+/* File: armv5te/OP_AGET.S */
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #1 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldrsh r2, [r0, #offArrayObject_contents] @ r2<- vBB[vCC]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r9) @ vAA<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT: /* 0x4b */
+/* File: armv5te/OP_APUT.S */
+ /*
+ * Array put, 32 bits or less. vBB[vCC] <- vAA.
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #2 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r2, r9) @ r2<- vAA
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ str r2, [r0, #offArrayObject_contents] @ vBB[vCC]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_WIDE: /* 0x4c */
+/* File: armv4t/OP_APUT_WIDE.S */
+ /*
+ * Array put, 64 bits. vBB[vCC] <- vAA.
+ */
+ /* aput-wide vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #3 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ bcc .LOP_APUT_WIDE_finish @ okay, continue below
+ b common_errArrayIndex @ index >= length, bail
+ @ May want to swap the order of these two branches depending on how the
+ @ branch prediction (if any) handles conditional forward branches vs.
+ @ unconditional forward branches.
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_OBJECT: /* 0x4d */
+/* File: armv5te/OP_APUT_OBJECT.S */
+ /*
+ * Store an object into an array. vBB[vCC] <- vAA.
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ */
+ /* op vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ GET_VREG(rINST, r2) @ rINST<- vBB (array object)
+ GET_VREG(r0, r3) @ r0<- vCC (requested index)
+ cmp rINST, #0 @ null array object?
+ GET_VREG(r9, r9) @ r9<- vAA
+ beq common_errNullObject @ yes, bail
+ ldr r3, [rINST, #offArrayObject_length] @ r3<- arrayObj->length
+ add r10, rINST, r0, lsl #2 @ r10<- arrayObj + index*width
+ cmp r0, r3 @ compare unsigned index, length
+ bcc .LOP_APUT_OBJECT_finish @ we're okay, continue on
+ b common_errArrayIndex @ index >= length, bail
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_BOOLEAN: /* 0x4e */
+/* File: armv5te/OP_APUT_BOOLEAN.S */
+/* File: armv5te/OP_APUT.S */
+ /*
+ * Array put, 32 bits or less. vBB[vCC] <- vAA.
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #0 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r2, r9) @ r2<- vAA
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ strb r2, [r0, #offArrayObject_contents] @ vBB[vCC]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_BYTE: /* 0x4f */
+/* File: armv5te/OP_APUT_BYTE.S */
+/* File: armv5te/OP_APUT.S */
+ /*
+ * Array put, 32 bits or less. vBB[vCC] <- vAA.
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #0 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r2, r9) @ r2<- vAA
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ strb r2, [r0, #offArrayObject_contents] @ vBB[vCC]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_CHAR: /* 0x50 */
+/* File: armv5te/OP_APUT_CHAR.S */
+/* File: armv5te/OP_APUT.S */
+ /*
+ * Array put, 32 bits or less. vBB[vCC] <- vAA.
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #1 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r2, r9) @ r2<- vAA
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ strh r2, [r0, #offArrayObject_contents] @ vBB[vCC]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_SHORT: /* 0x51 */
+/* File: armv5te/OP_APUT_SHORT.S */
+/* File: armv5te/OP_APUT.S */
+ /*
+ * Array put, 32 bits or less. vBB[vCC] <- vAA.
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #1 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r2, r9) @ r2<- vAA
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ strh r2, [r0, #offArrayObject_contents] @ vBB[vCC]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET: /* 0x52 */
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_finish
+ b common_exceptionThrown
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_WIDE: /* 0x53 */
+/* File: armv4t/OP_IGET_WIDE.S */
+ /*
+ * Wide 32-bit instance field get.
+ */
+ /* iget-wide vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_WIDE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_WIDE_finish
+ b common_exceptionThrown
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_OBJECT: /* 0x54 */
+/* File: armv5te/OP_IGET_OBJECT.S */
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_OBJECT_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_OBJECT_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_BOOLEAN: /* 0x55 */
+/* File: armv5te/OP_IGET_BOOLEAN.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrb", "sqnum":"1" }
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_BOOLEAN_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_BOOLEAN_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_BYTE: /* 0x56 */
+/* File: armv5te/OP_IGET_BYTE.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrsb", "sqnum":"2" }
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_BYTE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_BYTE_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_CHAR: /* 0x57 */
+/* File: armv5te/OP_IGET_CHAR.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrh", "sqnum":"3" }
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_CHAR_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_CHAR_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_SHORT: /* 0x58 */
+/* File: armv5te/OP_IGET_SHORT.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrsh", "sqnum":"4" }
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_SHORT_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_SHORT_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT: /* 0x59 */
+/* File: armv5te/OP_IPUT.S */
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_finish @ yes, finish up
+ b common_exceptionThrown
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_WIDE: /* 0x5a */
+/* File: armv4t/OP_IPUT_WIDE.S */
+ /* iput-wide vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_WIDE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_WIDE_finish @ yes, finish up
+ b common_exceptionThrown
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_OBJECT: /* 0x5b */
+/* File: armv5te/OP_IPUT_OBJECT.S */
+ /*
+ * 32-bit instance field put.
+ *
+ * for: iput-object, iput-object-volatile
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_OBJECT_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_OBJECT_finish @ yes, finish up
+ b common_exceptionThrown
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_BOOLEAN: /* 0x5c */
+/* File: armv5te/OP_IPUT_BOOLEAN.S */
+@include "armv5te/OP_IPUT.S" { "store":"strb", "sqnum":"1" }
+/* File: armv5te/OP_IPUT.S */
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_BOOLEAN_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_BOOLEAN_finish @ yes, finish up
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_BYTE: /* 0x5d */
+/* File: armv5te/OP_IPUT_BYTE.S */
+@include "armv5te/OP_IPUT.S" { "store":"strb", "sqnum":"2" }
+/* File: armv5te/OP_IPUT.S */
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_BYTE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_BYTE_finish @ yes, finish up
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_CHAR: /* 0x5e */
+/* File: armv5te/OP_IPUT_CHAR.S */
+@include "armv5te/OP_IPUT.S" { "store":"strh", "sqnum":"3" }
+/* File: armv5te/OP_IPUT.S */
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_CHAR_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_CHAR_finish @ yes, finish up
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_SHORT: /* 0x5f */
+/* File: armv5te/OP_IPUT_SHORT.S */
+@include "armv5te/OP_IPUT.S" { "store":"strh", "sqnum":"4" }
+/* File: armv5te/OP_IPUT.S */
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_SHORT_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_SHORT_finish @ yes, finish up
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET: /* 0x60 */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_resolve @ yes, do resolve
+.LOP_SGET_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_WIDE: /* 0x61 */
+/* File: armv4t/OP_SGET_WIDE.S */
+ /*
+ * 64-bit SGET handler.
+ */
+ /* sget-wide vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_WIDE_resolve @ yes, do resolve
+.LOP_SGET_WIDE_finish:
+ mov r9, rINST, lsr #8 @ r9<- AA
+ add r0, r0, #offStaticField_value @ r0<- pointer to data
+ .if 0
+ bl dvmQuasiAtomicRead64 @ r0/r1<- contents of field
+ .else
+ ldmia r0, {r0-r1} @ r0/r1<- field value (aligned)
+ .endif
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_OBJECT: /* 0x62 */
+/* File: armv5te/OP_SGET_OBJECT.S */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_OBJECT_resolve @ yes, do resolve
+.LOP_SGET_OBJECT_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_BOOLEAN: /* 0x63 */
+/* File: armv5te/OP_SGET_BOOLEAN.S */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_BOOLEAN_resolve @ yes, do resolve
+.LOP_SGET_BOOLEAN_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_BYTE: /* 0x64 */
+/* File: armv5te/OP_SGET_BYTE.S */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_BYTE_resolve @ yes, do resolve
+.LOP_SGET_BYTE_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_CHAR: /* 0x65 */
+/* File: armv5te/OP_SGET_CHAR.S */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_CHAR_resolve @ yes, do resolve
+.LOP_SGET_CHAR_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_SHORT: /* 0x66 */
+/* File: armv5te/OP_SGET_SHORT.S */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_SHORT_resolve @ yes, do resolve
+.LOP_SGET_SHORT_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT: /* 0x67 */
+/* File: armv5te/OP_SPUT.S */
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SPUT_resolve @ yes, do resolve
+.LOP_SPUT_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_WIDE: /* 0x68 */
+/* File: armv4t/OP_SPUT_WIDE.S */
+ /*
+ * 64-bit SPUT handler.
+ */
+ /* sput-wide vAA, field@BBBB */
+ ldr r0, [rGLUE, #offGlue_methodClassDex] @ r0<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r0, [r0, #offDvmDex_pResFields] @ r0<- dvmDex->pResFields
+ mov r9, rINST, lsr #8 @ r9<- AA
+ ldr r2, [r0, r1, lsl #2] @ r2<- resolved StaticField ptr
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ cmp r2, #0 @ is resolved entry null?
+ beq .LOP_SPUT_WIDE_resolve @ yes, do resolve
+.LOP_SPUT_WIDE_finish: @ field ptr in r2, AA in r9
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ GET_INST_OPCODE(r10) @ extract opcode from rINST
+ add r2, r2, #offStaticField_value @ r2<- pointer to data
+ .if 0
+ bl dvmQuasiAtomicSwap64 @ stores r0/r1 into addr r2
+ .else
+ stmia r2, {r0-r1} @ field<- vAA/vAA+1
+ .endif
+ GOTO_OPCODE(r10) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_OBJECT: /* 0x69 */
+/* File: armv5te/OP_SPUT_OBJECT.S */
+ /*
+ * 32-bit SPUT handler for objects
+ *
+ * for: sput-object, sput-object-volatile
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_SPUT_OBJECT_finish @ no, continue
+ ldr r9, [rGLUE, #offGlue_method] @ r9<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r9, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_OBJECT_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_BOOLEAN: /* 0x6a */
+/* File: armv5te/OP_SPUT_BOOLEAN.S */
+/* File: armv5te/OP_SPUT.S */
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SPUT_BOOLEAN_resolve @ yes, do resolve
+.LOP_SPUT_BOOLEAN_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_BYTE: /* 0x6b */
+/* File: armv5te/OP_SPUT_BYTE.S */
+/* File: armv5te/OP_SPUT.S */
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SPUT_BYTE_resolve @ yes, do resolve
+.LOP_SPUT_BYTE_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_CHAR: /* 0x6c */
+/* File: armv5te/OP_SPUT_CHAR.S */
+/* File: armv5te/OP_SPUT.S */
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SPUT_CHAR_resolve @ yes, do resolve
+.LOP_SPUT_CHAR_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_SHORT: /* 0x6d */
+/* File: armv5te/OP_SPUT_SHORT.S */
+/* File: armv5te/OP_SPUT.S */
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SPUT_SHORT_resolve @ yes, do resolve
+.LOP_SPUT_SHORT_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_VIRTUAL: /* 0x6e */
+/* File: armv5te/OP_INVOKE_VIRTUAL.S */
+ /*
+ * Handle a virtual method call.
+ *
+ * for: invoke-virtual, invoke-virtual/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved baseMethod
+ .if (!0)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ must export for invoke
+ bne .LOP_INVOKE_VIRTUAL_continue @ yes, continue on
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ mov r2, #METHOD_VIRTUAL @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ bne .LOP_INVOKE_VIRTUAL_continue @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_SUPER: /* 0x6f */
+/* File: armv5te/OP_INVOKE_SUPER.S */
+ /*
+ * Handle a "super" method call.
+ *
+ * for: invoke-super, invoke-super/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ .if (!0)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ GET_VREG(r2, r10) @ r2<- "this" ptr
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved baseMethod
+ cmp r2, #0 @ null "this"?
+ ldr r9, [rGLUE, #offGlue_method] @ r9<- current method
+ beq common_errNullObject @ null "this", throw exception
+ cmp r0, #0 @ already resolved?
+ ldr r9, [r9, #offMethod_clazz] @ r9<- method->clazz
+ EXPORT_PC() @ must export for invoke
+ bne .LOP_INVOKE_SUPER_continue @ resolved, continue on
+ b .LOP_INVOKE_SUPER_resolve @ do resolve now
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_DIRECT: /* 0x70 */
+/* File: armv5te/OP_INVOKE_DIRECT.S */
+ /*
+ * Handle a direct method call.
+ *
+ * (We could defer the "is 'this' pointer null" test to the common
+ * method invocation code, and use a flag to indicate that static
+ * calls don't count. If we do this as part of copying the arguments
+ * out we could avoiding loading the first arg twice.)
+ *
+ * for: invoke-direct, invoke-direct/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved methodToCall
+ .if (!0)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ must export for invoke
+ GET_VREG(r2, r10) @ r2<- "this" ptr
+ beq .LOP_INVOKE_DIRECT_resolve @ not resolved, do it now
+.LOP_INVOKE_DIRECT_finish:
+ cmp r2, #0 @ null "this" ref?
+ bne common_invokeMethodNoRange @ no, continue on
+ b common_errNullObject @ yes, throw exception
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_STATIC: /* 0x71 */
+/* File: armv5te/OP_INVOKE_STATIC.S */
+ /*
+ * Handle a static method call.
+ *
+ * for: invoke-static, invoke-static/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved methodToCall
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ must export for invoke
+ bne common_invokeMethodNoRange @ yes, continue on
+0: ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ mov r2, #METHOD_STATIC @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ bne common_invokeMethodNoRange @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_INTERFACE: /* 0x72 */
+/* File: armv5te/OP_INVOKE_INTERFACE.S */
+ /*
+ * Handle an interface method call.
+ *
+ * for: invoke-interface, invoke-interface/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r2, 2) @ r2<- FEDC or CCCC
+ FETCH(r1, 1) @ r1<- BBBB
+ .if (!0)
+ and r2, r2, #15 @ r2<- C (or stays CCCC)
+ .endif
+ EXPORT_PC() @ must export for invoke
+ GET_VREG(r0, r2) @ r0<- first arg ("this")
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- methodClassDex
+ cmp r0, #0 @ null obj?
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- method
+ beq common_errNullObject @ yes, fail
+ ldr r0, [r0, #offObject_clazz] @ r0<- thisPtr->clazz
+ bl dvmFindInterfaceMethodInCache @ r0<- call(class, ref, method, dex)
+ cmp r0, #0 @ failed?
+ beq common_exceptionThrown @ yes, handle exception
+ b common_invokeMethodNoRange @ jump to common handler
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_73: /* 0x73 */
+/* File: armv5te/OP_UNUSED_73.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_VIRTUAL_RANGE: /* 0x74 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_RANGE.S */
+/* File: armv5te/OP_INVOKE_VIRTUAL.S */
+ /*
+ * Handle a virtual method call.
+ *
+ * for: invoke-virtual, invoke-virtual/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved baseMethod
+ .if (!1)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ must export for invoke
+ bne .LOP_INVOKE_VIRTUAL_RANGE_continue @ yes, continue on
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ mov r2, #METHOD_VIRTUAL @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ bne .LOP_INVOKE_VIRTUAL_RANGE_continue @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_SUPER_RANGE: /* 0x75 */
+/* File: armv5te/OP_INVOKE_SUPER_RANGE.S */
+/* File: armv5te/OP_INVOKE_SUPER.S */
+ /*
+ * Handle a "super" method call.
+ *
+ * for: invoke-super, invoke-super/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ .if (!1)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ GET_VREG(r2, r10) @ r2<- "this" ptr
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved baseMethod
+ cmp r2, #0 @ null "this"?
+ ldr r9, [rGLUE, #offGlue_method] @ r9<- current method
+ beq common_errNullObject @ null "this", throw exception
+ cmp r0, #0 @ already resolved?
+ ldr r9, [r9, #offMethod_clazz] @ r9<- method->clazz
+ EXPORT_PC() @ must export for invoke
+ bne .LOP_INVOKE_SUPER_RANGE_continue @ resolved, continue on
+ b .LOP_INVOKE_SUPER_RANGE_resolve @ do resolve now
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_DIRECT_RANGE: /* 0x76 */
+/* File: armv5te/OP_INVOKE_DIRECT_RANGE.S */
+/* File: armv5te/OP_INVOKE_DIRECT.S */
+ /*
+ * Handle a direct method call.
+ *
+ * (We could defer the "is 'this' pointer null" test to the common
+ * method invocation code, and use a flag to indicate that static
+ * calls don't count. If we do this as part of copying the arguments
+ * out we could avoiding loading the first arg twice.)
+ *
+ * for: invoke-direct, invoke-direct/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved methodToCall
+ .if (!1)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ must export for invoke
+ GET_VREG(r2, r10) @ r2<- "this" ptr
+ beq .LOP_INVOKE_DIRECT_RANGE_resolve @ not resolved, do it now
+.LOP_INVOKE_DIRECT_RANGE_finish:
+ cmp r2, #0 @ null "this" ref?
+ bne common_invokeMethodRange @ no, continue on
+ b common_errNullObject @ yes, throw exception
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_STATIC_RANGE: /* 0x77 */
+/* File: armv5te/OP_INVOKE_STATIC_RANGE.S */
+/* File: armv5te/OP_INVOKE_STATIC.S */
+ /*
+ * Handle a static method call.
+ *
+ * for: invoke-static, invoke-static/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved methodToCall
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ must export for invoke
+ bne common_invokeMethodRange @ yes, continue on
+0: ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ mov r2, #METHOD_STATIC @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ bne common_invokeMethodRange @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_INTERFACE_RANGE: /* 0x78 */
+/* File: armv5te/OP_INVOKE_INTERFACE_RANGE.S */
+/* File: armv5te/OP_INVOKE_INTERFACE.S */
+ /*
+ * Handle an interface method call.
+ *
+ * for: invoke-interface, invoke-interface/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r2, 2) @ r2<- FEDC or CCCC
+ FETCH(r1, 1) @ r1<- BBBB
+ .if (!1)
+ and r2, r2, #15 @ r2<- C (or stays CCCC)
+ .endif
+ EXPORT_PC() @ must export for invoke
+ GET_VREG(r0, r2) @ r0<- first arg ("this")
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- methodClassDex
+ cmp r0, #0 @ null obj?
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- method
+ beq common_errNullObject @ yes, fail
+ ldr r0, [r0, #offObject_clazz] @ r0<- thisPtr->clazz
+ bl dvmFindInterfaceMethodInCache @ r0<- call(class, ref, method, dex)
+ cmp r0, #0 @ failed?
+ beq common_exceptionThrown @ yes, handle exception
+ b common_invokeMethodRange @ jump to common handler
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_79: /* 0x79 */
+/* File: armv5te/OP_UNUSED_79.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_7A: /* 0x7a */
+/* File: armv5te/OP_UNUSED_7A.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEG_INT: /* 0x7b */
+/* File: armv5te/OP_NEG_INT.S */
+/* File: armv5te/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0".
+ * This could be an ARM instruction or a function call.
+ *
+ * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+ * int-to-byte, int-to-char, int-to-short
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r3) @ r0<- vB
+ and r9, r9, #15
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ rsb r0, r0, #0 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 9-10 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NOT_INT: /* 0x7c */
+/* File: armv5te/OP_NOT_INT.S */
+/* File: armv5te/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0".
+ * This could be an ARM instruction or a function call.
+ *
+ * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+ * int-to-byte, int-to-char, int-to-short
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r3) @ r0<- vB
+ and r9, r9, #15
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ mvn r0, r0 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 9-10 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEG_LONG: /* 0x7d */
+/* File: armv5te/OP_NEG_LONG.S */
+/* File: armv5te/unopWide.S */
+ /*
+ * Generic 64-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0/r1".
+ * This could be an ARM instruction or a function call.
+ *
+ * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+ */
+ /* unop vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r3, {r0-r1} @ r0/r1<- vAA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ rsbs r0, r0, #0 @ optional op; may set condition codes
+ rsc r1, r1, #0 @ r0/r1<- op, r2-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NOT_LONG: /* 0x7e */
+/* File: armv5te/OP_NOT_LONG.S */
+/* File: armv5te/unopWide.S */
+ /*
+ * Generic 64-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0/r1".
+ * This could be an ARM instruction or a function call.
+ *
+ * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+ */
+ /* unop vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r3, {r0-r1} @ r0/r1<- vAA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ mvn r0, r0 @ optional op; may set condition codes
+ mvn r1, r1 @ r0/r1<- op, r2-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEG_FLOAT: /* 0x7f */
+/* File: armv5te/OP_NEG_FLOAT.S */
+/* File: armv5te/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0".
+ * This could be an ARM instruction or a function call.
+ *
+ * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+ * int-to-byte, int-to-char, int-to-short
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r3) @ r0<- vB
+ and r9, r9, #15
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ add r0, r0, #0x80000000 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 9-10 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEG_DOUBLE: /* 0x80 */
+/* File: armv5te/OP_NEG_DOUBLE.S */
+/* File: armv5te/unopWide.S */
+ /*
+ * Generic 64-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0/r1".
+ * This could be an ARM instruction or a function call.
+ *
+ * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+ */
+ /* unop vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r3, {r0-r1} @ r0/r1<- vAA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ add r1, r1, #0x80000000 @ r0/r1<- op, r2-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_LONG: /* 0x81 */
+/* File: armv5te/OP_INT_TO_LONG.S */
+/* File: armv5te/unopWider.S */
+ /*
+ * Generic 32bit-to-64bit unary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = op r0", where
+ * "result" is a 64-bit quantity in r0/r1.
+ *
+ * For: int-to-long, int-to-double, float-to-long, float-to-double
+ */
+ /* unop vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r0, r3) @ r0<- vB
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ mov r1, r0, asr #31 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vA/vA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-11 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_FLOAT: /* 0x82 */
+/* File: armv5te/OP_INT_TO_FLOAT.S */
+/* File: armv5te/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0".
+ * This could be an ARM instruction or a function call.
+ *
+ * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+ * int-to-byte, int-to-char, int-to-short
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r3) @ r0<- vB
+ and r9, r9, #15
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ bl __aeabi_i2f @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 9-10 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_DOUBLE: /* 0x83 */
+/* File: armv5te/OP_INT_TO_DOUBLE.S */
+/* File: armv5te/unopWider.S */
+ /*
+ * Generic 32bit-to-64bit unary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = op r0", where
+ * "result" is a 64-bit quantity in r0/r1.
+ *
+ * For: int-to-long, int-to-double, float-to-long, float-to-double
+ */
+ /* unop vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r0, r3) @ r0<- vB
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ bl __aeabi_i2d @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vA/vA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-11 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_LONG_TO_INT: /* 0x84 */
+/* File: armv5te/OP_LONG_TO_INT.S */
+/* we ignore the high word, making this equivalent to a 32-bit reg move */
+/* File: armv5te/OP_MOVE.S */
+ /* for move, move-object, long-to-int */
+ /* op vA, vB */
+ mov r1, rINST, lsr #12 @ r1<- B from 15:12
+ mov r0, rINST, lsr #8 @ r0<- A from 11:8
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[B]
+ and r0, r0, #15
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ SET_VREG(r2, r0) @ fp[A]<- r2
+ GOTO_OPCODE(ip) @ execute next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_LONG_TO_FLOAT: /* 0x85 */
+/* File: armv5te/OP_LONG_TO_FLOAT.S */
+/* File: armv5te/unopNarrower.S */
+ /*
+ * Generic 64bit-to-32bit unary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = op r0/r1", where
+ * "result" is a 32-bit quantity in r0.
+ *
+ * For: long-to-float, double-to-int, double-to-float
+ *
+ * (This would work for long-to-int, but that instruction is actually
+ * an exact match for OP_MOVE.)
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ and r9, r9, #15
+ ldmia r3, {r0-r1} @ r0/r1<- vB/vB+1
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl __aeabi_l2f @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-11 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_LONG_TO_DOUBLE: /* 0x86 */
+/* File: armv5te/OP_LONG_TO_DOUBLE.S */
+/* File: armv5te/unopWide.S */
+ /*
+ * Generic 64-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0/r1".
+ * This could be an ARM instruction or a function call.
+ *
+ * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+ */
+ /* unop vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r3, {r0-r1} @ r0/r1<- vAA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl __aeabi_l2d @ r0/r1<- op, r2-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FLOAT_TO_INT: /* 0x87 */
+/* File: armv5te/OP_FLOAT_TO_INT.S */
+/* EABI appears to have Java-style conversions of +inf/-inf/NaN */
+/* File: armv5te/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0".
+ * This could be an ARM instruction or a function call.
+ *
+ * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+ * int-to-byte, int-to-char, int-to-short
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r3) @ r0<- vB
+ and r9, r9, #15
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ bl __aeabi_f2iz @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 9-10 instructions */
+
+
+#if 0
+@include "armv5te/unop.S" {"instr":"bl f2i_doconv"}
+@break
+/*
+ * Convert the float in r0 to an int in r0.
+ *
+ * We have to clip values to int min/max per the specification. The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer. The EABI convert function isn't doing this for us.
+ */
+f2i_doconv:
+ stmfd sp!, {r4, lr}
+ mov r1, #0x4f000000 @ (float)maxint
+ mov r4, r0
+ bl __aeabi_fcmpge @ is arg >= maxint?
+ cmp r0, #0 @ nonzero == yes
+ mvnne r0, #0x80000000 @ return maxint (7fffffff)
+ ldmnefd sp!, {r4, pc}
+
+ mov r0, r4 @ recover arg
+ mov r1, #0xcf000000 @ (float)minint
+ bl __aeabi_fcmple @ is arg <= minint?
+ cmp r0, #0 @ nonzero == yes
+ movne r0, #0x80000000 @ return minint (80000000)
+ ldmnefd sp!, {r4, pc}
+
+ mov r0, r4 @ recover arg
+ mov r1, r4
+ bl __aeabi_fcmpeq @ is arg == self?
+ cmp r0, #0 @ zero == no
+ ldmeqfd sp!, {r4, pc} @ return zero for NaN
+
+ mov r0, r4 @ recover arg
+ bl __aeabi_f2iz @ convert float to int
+ ldmfd sp!, {r4, pc}
+#endif
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FLOAT_TO_LONG: /* 0x88 */
+/* File: armv5te/OP_FLOAT_TO_LONG.S */
+@include "armv5te/unopWider.S" {"instr":"bl __aeabi_f2lz"}
+/* File: armv5te/unopWider.S */
+ /*
+ * Generic 32bit-to-64bit unary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = op r0", where
+ * "result" is a 64-bit quantity in r0/r1.
+ *
+ * For: int-to-long, int-to-double, float-to-long, float-to-double
+ */
+ /* unop vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r0, r3) @ r0<- vB
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ bl f2l_doconv @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vA/vA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-11 instructions */
+
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FLOAT_TO_DOUBLE: /* 0x89 */
+/* File: armv5te/OP_FLOAT_TO_DOUBLE.S */
+/* File: armv5te/unopWider.S */
+ /*
+ * Generic 32bit-to-64bit unary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = op r0", where
+ * "result" is a 64-bit quantity in r0/r1.
+ *
+ * For: int-to-long, int-to-double, float-to-long, float-to-double
+ */
+ /* unop vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r0, r3) @ r0<- vB
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ bl __aeabi_f2d @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vA/vA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-11 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DOUBLE_TO_INT: /* 0x8a */
+/* File: armv5te/OP_DOUBLE_TO_INT.S */
+/* EABI appears to have Java-style conversions of +inf/-inf/NaN */
+/* File: armv5te/unopNarrower.S */
+ /*
+ * Generic 64bit-to-32bit unary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = op r0/r1", where
+ * "result" is a 32-bit quantity in r0.
+ *
+ * For: long-to-float, double-to-int, double-to-float
+ *
+ * (This would work for long-to-int, but that instruction is actually
+ * an exact match for OP_MOVE.)
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ and r9, r9, #15
+ ldmia r3, {r0-r1} @ r0/r1<- vB/vB+1
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl __aeabi_d2iz @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-11 instructions */
+
+
+#if 0
+@include "armv5te/unopNarrower.S" {"instr":"bl d2i_doconv"}
+@break
+/*
+ * Convert the double in r0/r1 to an int in r0.
+ *
+ * We have to clip values to int min/max per the specification. The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer. The EABI convert function isn't doing this for us.
+ */
+d2i_doconv:
+ stmfd sp!, {r4, r5, lr} @ save regs
+ mov r2, #0x80000000 @ maxint, as a double (low word)
+ mov r2, r2, asr #9 @ 0xffc00000
+ sub sp, sp, #4 @ align for EABI
+ mvn r3, #0xbe000000 @ maxint, as a double (high word)
+ sub r3, r3, #0x00200000 @ 0x41dfffff
+ mov r4, r0 @ save a copy of r0
+ mov r5, r1 @ and r1
+ bl __aeabi_dcmpge @ is arg >= maxint?
+ cmp r0, #0 @ nonzero == yes
+ mvnne r0, #0x80000000 @ return maxint (0x7fffffff)
+ bne 1f
+
+ mov r0, r4 @ recover arg
+ mov r1, r5
+ mov r3, #0xc1000000 @ minint, as a double (high word)
+ add r3, r3, #0x00e00000 @ 0xc1e00000
+ mov r2, #0 @ minint, as a double (low word)
+ bl __aeabi_dcmple @ is arg <= minint?
+ cmp r0, #0 @ nonzero == yes
+ movne r0, #0x80000000 @ return minint (80000000)
+ bne 1f
+
+ mov r0, r4 @ recover arg
+ mov r1, r5
+ mov r2, r4 @ compare against self
+ mov r3, r5
+ bl __aeabi_dcmpeq @ is arg == self?
+ cmp r0, #0 @ zero == no
+ beq 1f @ return zero for NaN
+
+ mov r0, r4 @ recover arg
+ mov r1, r5
+ bl __aeabi_d2iz @ convert double to int
+
+1:
+ add sp, sp, #4
+ ldmfd sp!, {r4, r5, pc}
+#endif
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DOUBLE_TO_LONG: /* 0x8b */
+/* File: armv5te/OP_DOUBLE_TO_LONG.S */
+@include "armv5te/unopWide.S" {"instr":"bl __aeabi_d2lz"}
+/* File: armv5te/unopWide.S */
+ /*
+ * Generic 64-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0/r1".
+ * This could be an ARM instruction or a function call.
+ *
+ * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+ */
+ /* unop vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r3, {r0-r1} @ r0/r1<- vAA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl d2l_doconv @ r0/r1<- op, r2-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-13 instructions */
+
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DOUBLE_TO_FLOAT: /* 0x8c */
+/* File: armv5te/OP_DOUBLE_TO_FLOAT.S */
+/* File: armv5te/unopNarrower.S */
+ /*
+ * Generic 64bit-to-32bit unary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = op r0/r1", where
+ * "result" is a 32-bit quantity in r0.
+ *
+ * For: long-to-float, double-to-int, double-to-float
+ *
+ * (This would work for long-to-int, but that instruction is actually
+ * an exact match for OP_MOVE.)
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ and r9, r9, #15
+ ldmia r3, {r0-r1} @ r0/r1<- vB/vB+1
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl __aeabi_d2f @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-11 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_BYTE: /* 0x8d */
+/* File: armv5te/OP_INT_TO_BYTE.S */
+/* File: armv5te/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0".
+ * This could be an ARM instruction or a function call.
+ *
+ * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+ * int-to-byte, int-to-char, int-to-short
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r3) @ r0<- vB
+ and r9, r9, #15
+ mov r0, r0, asl #24 @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ mov r0, r0, asr #24 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 9-10 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_CHAR: /* 0x8e */
+/* File: armv5te/OP_INT_TO_CHAR.S */
+/* File: armv5te/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0".
+ * This could be an ARM instruction or a function call.
+ *
+ * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+ * int-to-byte, int-to-char, int-to-short
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r3) @ r0<- vB
+ and r9, r9, #15
+ mov r0, r0, asl #16 @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ mov r0, r0, lsr #16 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 9-10 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_SHORT: /* 0x8f */
+/* File: armv5te/OP_INT_TO_SHORT.S */
+/* File: armv5te/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0".
+ * This could be an ARM instruction or a function call.
+ *
+ * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+ * int-to-byte, int-to-char, int-to-short
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r3) @ r0<- vB
+ and r9, r9, #15
+ mov r0, r0, asl #16 @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ mov r0, r0, asr #16 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 9-10 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_INT: /* 0x90 */
+/* File: armv5te/OP_ADD_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ add r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_INT: /* 0x91 */
+/* File: armv5te/OP_SUB_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ sub r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_INT: /* 0x92 */
+/* File: armv5te/OP_MUL_INT.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ mul r0, r1, r0 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_INT: /* 0x93 */
+/* File: armv5te/OP_DIV_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 1
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl __aeabi_idiv @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_INT: /* 0x94 */
+/* File: armv5te/OP_REM_INT.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 1
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl __aeabi_idivmod @ r1<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r1, r9) @ vAA<- r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_INT: /* 0x95 */
+/* File: armv5te/OP_AND_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ and r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_INT: /* 0x96 */
+/* File: armv5te/OP_OR_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ orr r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_INT: /* 0x97 */
+/* File: armv5te/OP_XOR_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ eor r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHL_INT: /* 0x98 */
+/* File: armv5te/OP_SHL_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, asl r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHR_INT: /* 0x99 */
+/* File: armv5te/OP_SHR_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, asr r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_USHR_INT: /* 0x9a */
+/* File: armv5te/OP_USHR_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, lsr r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_LONG: /* 0x9b */
+/* File: armv5te/OP_ADD_LONG.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ adds r0, r0, r2 @ optional op; may set condition codes
+ adc r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_LONG: /* 0x9c */
+/* File: armv5te/OP_SUB_LONG.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ subs r0, r0, r2 @ optional op; may set condition codes
+ sbc r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_LONG: /* 0x9d */
+/* File: armv5te/OP_MUL_LONG.S */
+ /*
+ * Signed 64-bit integer multiply.
+ *
+ * Consider WXxYZ (r1r0 x r3r2) with a long multiply:
+ * WX
+ * x YZ
+ * --------
+ * ZW ZX
+ * YW YX
+ *
+ * The low word of the result holds ZX, the high word holds
+ * (ZW+YX) + (the high overflow from ZX). YW doesn't matter because
+ * it doesn't fit in the low 64 bits.
+ *
+ * Unlike most ARM math operations, multiply instructions have
+ * restrictions on using the same register more than once (Rd and Rm
+ * cannot be the same).
+ */
+ /* mul-long vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ mul ip, r2, r1 @ ip<- ZxW
+ umull r9, r10, r2, r0 @ r9/r10 <- ZxX
+ mla r2, r0, r3, ip @ r2<- YxX + (ZxW)
+ mov r0, rINST, lsr #8 @ r0<- AA
+ add r10, r2, r10 @ r10<- r10 + low(ZxW + (YxX))
+ add r0, rFP, r0, lsl #2 @ r0<- &fp[AA]
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ b .LOP_MUL_LONG_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_LONG: /* 0x9e */
+/* File: armv5te/OP_DIV_LONG.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 1
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_ldivmod @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_LONG: /* 0x9f */
+/* File: armv5te/OP_REM_LONG.S */
+/* ldivmod returns quotient in r0/r1 and remainder in r2/r3 */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 1
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_ldivmod @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r2,r3} @ vAA/vAA+1<- r2/r3
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_LONG: /* 0xa0 */
+/* File: armv5te/OP_AND_LONG.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ and r0, r0, r2 @ optional op; may set condition codes
+ and r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_LONG: /* 0xa1 */
+/* File: armv5te/OP_OR_LONG.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ orr r0, r0, r2 @ optional op; may set condition codes
+ orr r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_LONG: /* 0xa2 */
+/* File: armv5te/OP_XOR_LONG.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ eor r0, r0, r2 @ optional op; may set condition codes
+ eor r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHL_LONG: /* 0xa3 */
+/* File: armv5te/OP_SHL_LONG.S */
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to mask off the low
+ * 6 bits of the shift distance.
+ */
+ /* shl-long vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r3, r0, #255 @ r3<- BB
+ mov r0, r0, lsr #8 @ r0<- CC
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[BB]
+ GET_VREG(r2, r0) @ r2<- vCC
+ ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1
+ and r2, r2, #63 @ r2<- r2 & 0x3f
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+
+ mov r1, r1, asl r2 @ r1<- r1 << r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ b .LOP_SHL_LONG_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHR_LONG: /* 0xa4 */
+/* File: armv5te/OP_SHR_LONG.S */
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to mask off the low
+ * 6 bits of the shift distance.
+ */
+ /* shr-long vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r3, r0, #255 @ r3<- BB
+ mov r0, r0, lsr #8 @ r0<- CC
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[BB]
+ GET_VREG(r2, r0) @ r2<- vCC
+ ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1
+ and r2, r2, #63 @ r0<- r0 & 0x3f
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ b .LOP_SHR_LONG_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_USHR_LONG: /* 0xa5 */
+/* File: armv5te/OP_USHR_LONG.S */
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to mask off the low
+ * 6 bits of the shift distance.
+ */
+ /* ushr-long vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r3, r0, #255 @ r3<- BB
+ mov r0, r0, lsr #8 @ r0<- CC
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[BB]
+ GET_VREG(r2, r0) @ r2<- vCC
+ ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1
+ and r2, r2, #63 @ r0<- r0 & 0x3f
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ b .LOP_USHR_LONG_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_FLOAT: /* 0xa6 */
+/* File: armv5te/OP_ADD_FLOAT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl __aeabi_fadd @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_FLOAT: /* 0xa7 */
+/* File: armv5te/OP_SUB_FLOAT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl __aeabi_fsub @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_FLOAT: /* 0xa8 */
+/* File: armv5te/OP_MUL_FLOAT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl __aeabi_fmul @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_FLOAT: /* 0xa9 */
+/* File: armv5te/OP_DIV_FLOAT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl __aeabi_fdiv @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_FLOAT: /* 0xaa */
+/* File: armv5te/OP_REM_FLOAT.S */
+/* EABI doesn't define a float remainder function, but libm does */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl fmodf @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_DOUBLE: /* 0xab */
+/* File: armv5te/OP_ADD_DOUBLE.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_dadd @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_DOUBLE: /* 0xac */
+/* File: armv5te/OP_SUB_DOUBLE.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_dsub @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_DOUBLE: /* 0xad */
+/* File: armv5te/OP_MUL_DOUBLE.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_dmul @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_DOUBLE: /* 0xae */
+/* File: armv5te/OP_DIV_DOUBLE.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_ddiv @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_DOUBLE: /* 0xaf */
+/* File: armv5te/OP_REM_DOUBLE.S */
+/* EABI doesn't define a double remainder function, but libm does */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl fmod @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_INT_2ADDR: /* 0xb0 */
+/* File: armv5te/OP_ADD_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ add r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_INT_2ADDR: /* 0xb1 */
+/* File: armv5te/OP_SUB_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ sub r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_INT_2ADDR: /* 0xb2 */
+/* File: armv5te/OP_MUL_INT_2ADDR.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ mul r0, r1, r0 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_INT_2ADDR: /* 0xb3 */
+/* File: armv5te/OP_DIV_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 1
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_idiv @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_INT_2ADDR: /* 0xb4 */
+/* File: armv5te/OP_REM_INT_2ADDR.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 1
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_idivmod @ r1<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r1, r9) @ vAA<- r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_INT_2ADDR: /* 0xb5 */
+/* File: armv5te/OP_AND_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ and r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_INT_2ADDR: /* 0xb6 */
+/* File: armv5te/OP_OR_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ orr r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_INT_2ADDR: /* 0xb7 */
+/* File: armv5te/OP_XOR_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ eor r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHL_INT_2ADDR: /* 0xb8 */
+/* File: armv5te/OP_SHL_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, asl r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHR_INT_2ADDR: /* 0xb9 */
+/* File: armv5te/OP_SHR_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, asr r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_USHR_INT_2ADDR: /* 0xba */
+/* File: armv5te/OP_USHR_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, lsr r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_LONG_2ADDR: /* 0xbb */
+/* File: armv5te/OP_ADD_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r9, r9, #15
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ adds r0, r0, r2 @ optional op; may set condition codes
+ adc r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_LONG_2ADDR: /* 0xbc */
+/* File: armv5te/OP_SUB_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r9, r9, #15
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ subs r0, r0, r2 @ optional op; may set condition codes
+ sbc r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_LONG_2ADDR: /* 0xbd */
+/* File: armv5te/OP_MUL_LONG_2ADDR.S */
+ /*
+ * Signed 64-bit integer multiply, "/2addr" version.
+ *
+ * See OP_MUL_LONG for an explanation.
+ *
+ * We get a little tight on registers, so to avoid looking up &fp[A]
+ * again we stuff it into rINST.
+ */
+ /* mul-long/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r9, r9, #15
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add rINST, rFP, r9, lsl #2 @ rINST<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia rINST, {r0-r1} @ r0/r1<- vAA/vAA+1
+ mul ip, r2, r1 @ ip<- ZxW
+ umull r9, r10, r2, r0 @ r9/r10 <- ZxX
+ mla r2, r0, r3, ip @ r2<- YxX + (ZxW)
+ mov r0, rINST @ r0<- &fp[A] (free up rINST)
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ add r10, r2, r10 @ r10<- r10 + low(ZxW + (YxX))
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r0, {r9-r10} @ vAA/vAA+1<- r9/r10
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_LONG_2ADDR: /* 0xbe */
+/* File: armv5te/OP_DIV_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r9, r9, #15
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 1
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_ldivmod @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_LONG_2ADDR: /* 0xbf */
+/* File: armv5te/OP_REM_LONG_2ADDR.S */
+/* ldivmod returns quotient in r0/r1 and remainder in r2/r3 */
+/* File: armv5te/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r9, r9, #15
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 1
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_ldivmod @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r2,r3} @ vAA/vAA+1<- r2/r3
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_LONG_2ADDR: /* 0xc0 */
+/* File: armv5te/OP_AND_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r9, r9, #15
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ and r0, r0, r2 @ optional op; may set condition codes
+ and r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_LONG_2ADDR: /* 0xc1 */
+/* File: armv5te/OP_OR_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r9, r9, #15
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ orr r0, r0, r2 @ optional op; may set condition codes
+ orr r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_LONG_2ADDR: /* 0xc2 */
+/* File: armv5te/OP_XOR_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r9, r9, #15
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ eor r0, r0, r2 @ optional op; may set condition codes
+ eor r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHL_LONG_2ADDR: /* 0xc3 */
+/* File: armv5te/OP_SHL_LONG_2ADDR.S */
+ /*
+ * Long integer shift, 2addr version. vA is 64-bit value/result, vB is
+ * 32-bit shift distance.
+ */
+ /* shl-long/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r2, r3) @ r2<- vB
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ and r2, r2, #63 @ r2<- r2 & 0x3f
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+
+ mov r1, r1, asl r2 @ r1<- r1 << r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32)
+ mov r0, r0, asl r2 @ r0<- r0 << r2
+ b .LOP_SHL_LONG_2ADDR_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHR_LONG_2ADDR: /* 0xc4 */
+/* File: armv5te/OP_SHR_LONG_2ADDR.S */
+ /*
+ * Long integer shift, 2addr version. vA is 64-bit value/result, vB is
+ * 32-bit shift distance.
+ */
+ /* shr-long/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r2, r3) @ r2<- vB
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ and r2, r2, #63 @ r2<- r2 & 0x3f
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32)
+ mov r1, r1, asr r2 @ r1<- r1 >> r2
+ b .LOP_SHR_LONG_2ADDR_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_USHR_LONG_2ADDR: /* 0xc5 */
+/* File: armv5te/OP_USHR_LONG_2ADDR.S */
+ /*
+ * Long integer shift, 2addr version. vA is 64-bit value/result, vB is
+ * 32-bit shift distance.
+ */
+ /* ushr-long/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r2, r3) @ r2<- vB
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ and r2, r2, #63 @ r2<- r2 & 0x3f
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32)
+ mov r1, r1, lsr r2 @ r1<- r1 >>> r2
+ b .LOP_USHR_LONG_2ADDR_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_FLOAT_2ADDR: /* 0xc6 */
+/* File: armv5te/OP_ADD_FLOAT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_fadd @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_FLOAT_2ADDR: /* 0xc7 */
+/* File: armv5te/OP_SUB_FLOAT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_fsub @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_FLOAT_2ADDR: /* 0xc8 */
+/* File: armv5te/OP_MUL_FLOAT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_fmul @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_FLOAT_2ADDR: /* 0xc9 */
+/* File: armv5te/OP_DIV_FLOAT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_fdiv @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_FLOAT_2ADDR: /* 0xca */
+/* File: armv5te/OP_REM_FLOAT_2ADDR.S */
+/* EABI doesn't define a float remainder function, but libm does */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl fmodf @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_DOUBLE_2ADDR: /* 0xcb */
+/* File: armv5te/OP_ADD_DOUBLE_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r9, r9, #15
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_dadd @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_DOUBLE_2ADDR: /* 0xcc */
+/* File: armv5te/OP_SUB_DOUBLE_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r9, r9, #15
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_dsub @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_DOUBLE_2ADDR: /* 0xcd */
+/* File: armv5te/OP_MUL_DOUBLE_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r9, r9, #15
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_dmul @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_DOUBLE_2ADDR: /* 0xce */
+/* File: armv5te/OP_DIV_DOUBLE_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r9, r9, #15
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_ddiv @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_DOUBLE_2ADDR: /* 0xcf */
+/* File: armv5te/OP_REM_DOUBLE_2ADDR.S */
+/* EABI doesn't define a double remainder function, but libm does */
+/* File: armv5te/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r9, r9, #15
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl fmod @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_INT_LIT16: /* 0xd0 */
+/* File: armv5te/OP_ADD_INT_LIT16.S */
+/* File: armv5te/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r2) @ r0<- vB
+ and r9, r9, #15
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ add r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RSUB_INT: /* 0xd1 */
+/* File: armv5te/OP_RSUB_INT.S */
+/* this op is "rsub-int", but can be thought of as "rsub-int/lit16" */
+/* File: armv5te/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r2) @ r0<- vB
+ and r9, r9, #15
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ rsb r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_INT_LIT16: /* 0xd2 */
+/* File: armv5te/OP_MUL_INT_LIT16.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r2) @ r0<- vB
+ and r9, r9, #15
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ mul r0, r1, r0 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_INT_LIT16: /* 0xd3 */
+/* File: armv5te/OP_DIV_INT_LIT16.S */
+/* File: armv5te/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r2) @ r0<- vB
+ and r9, r9, #15
+ .if 1
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ bl __aeabi_idiv @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_INT_LIT16: /* 0xd4 */
+/* File: armv5te/OP_REM_INT_LIT16.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* File: armv5te/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r2) @ r0<- vB
+ and r9, r9, #15
+ .if 1
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ bl __aeabi_idivmod @ r1<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r1, r9) @ vAA<- r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_INT_LIT16: /* 0xd5 */
+/* File: armv5te/OP_AND_INT_LIT16.S */
+/* File: armv5te/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r2) @ r0<- vB
+ and r9, r9, #15
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ and r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_INT_LIT16: /* 0xd6 */
+/* File: armv5te/OP_OR_INT_LIT16.S */
+/* File: armv5te/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r2) @ r0<- vB
+ and r9, r9, #15
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ orr r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_INT_LIT16: /* 0xd7 */
+/* File: armv5te/OP_XOR_INT_LIT16.S */
+/* File: armv5te/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r2) @ r0<- vB
+ and r9, r9, #15
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ eor r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_INT_LIT8: /* 0xd8 */
+/* File: armv5te/OP_ADD_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ add r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RSUB_INT_LIT8: /* 0xd9 */
+/* File: armv5te/OP_RSUB_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ rsb r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_INT_LIT8: /* 0xda */
+/* File: armv5te/OP_MUL_INT_LIT8.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ mul r0, r1, r0 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_INT_LIT8: /* 0xdb */
+/* File: armv5te/OP_DIV_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 1
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_idiv @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_INT_LIT8: /* 0xdc */
+/* File: armv5te/OP_REM_INT_LIT8.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 1
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_idivmod @ r1<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r1, r9) @ vAA<- r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_INT_LIT8: /* 0xdd */
+/* File: armv5te/OP_AND_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ and r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_INT_LIT8: /* 0xde */
+/* File: armv5te/OP_OR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ orr r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_INT_LIT8: /* 0xdf */
+/* File: armv5te/OP_XOR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ eor r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHL_INT_LIT8: /* 0xe0 */
+/* File: armv5te/OP_SHL_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, asl r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHR_INT_LIT8: /* 0xe1 */
+/* File: armv5te/OP_SHR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, asr r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_USHR_INT_LIT8: /* 0xe2 */
+/* File: armv5te/OP_USHR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, lsr r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_VOLATILE: /* 0xe3 */
+/* File: armv5te/OP_IGET_VOLATILE.S */
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_VOLATILE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_VOLATILE_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_VOLATILE: /* 0xe4 */
+/* File: armv5te/OP_IPUT_VOLATILE.S */
+/* File: armv5te/OP_IPUT.S */
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_VOLATILE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_VOLATILE_finish @ yes, finish up
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_VOLATILE: /* 0xe5 */
+/* File: armv5te/OP_SGET_VOLATILE.S */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_VOLATILE_resolve @ yes, do resolve
+.LOP_SGET_VOLATILE_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ SMP_DMB @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_VOLATILE: /* 0xe6 */
+/* File: armv5te/OP_SPUT_VOLATILE.S */
+/* File: armv5te/OP_SPUT.S */
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SPUT_VOLATILE_resolve @ yes, do resolve
+.LOP_SPUT_VOLATILE_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SMP_DMB @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_OBJECT_VOLATILE: /* 0xe7 */
+/* File: armv5te/OP_IGET_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_OBJECT_VOLATILE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_OBJECT_VOLATILE_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_WIDE_VOLATILE: /* 0xe8 */
+/* File: armv4t/OP_IGET_WIDE_VOLATILE.S */
+/* File: armv4t/OP_IGET_WIDE.S */
+ /*
+ * Wide 32-bit instance field get.
+ */
+ /* iget-wide vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_WIDE_VOLATILE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_WIDE_VOLATILE_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_WIDE_VOLATILE: /* 0xe9 */
+/* File: armv4t/OP_IPUT_WIDE_VOLATILE.S */
+/* File: armv4t/OP_IPUT_WIDE.S */
+ /* iput-wide vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_WIDE_VOLATILE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_WIDE_VOLATILE_finish @ yes, finish up
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_WIDE_VOLATILE: /* 0xea */
+/* File: armv4t/OP_SGET_WIDE_VOLATILE.S */
+/* File: armv4t/OP_SGET_WIDE.S */
+ /*
+ * 64-bit SGET handler.
+ */
+ /* sget-wide vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_WIDE_VOLATILE_resolve @ yes, do resolve
+.LOP_SGET_WIDE_VOLATILE_finish:
+ mov r9, rINST, lsr #8 @ r9<- AA
+ add r0, r0, #offStaticField_value @ r0<- pointer to data
+ .if 1
+ bl dvmQuasiAtomicRead64 @ r0/r1<- contents of field
+ .else
+ ldmia r0, {r0-r1} @ r0/r1<- field value (aligned)
+ .endif
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_WIDE_VOLATILE: /* 0xeb */
+/* File: armv4t/OP_SPUT_WIDE_VOLATILE.S */
+/* File: armv4t/OP_SPUT_WIDE.S */
+ /*
+ * 64-bit SPUT handler.
+ */
+ /* sput-wide vAA, field@BBBB */
+ ldr r0, [rGLUE, #offGlue_methodClassDex] @ r0<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r0, [r0, #offDvmDex_pResFields] @ r0<- dvmDex->pResFields
+ mov r9, rINST, lsr #8 @ r9<- AA
+ ldr r2, [r0, r1, lsl #2] @ r2<- resolved StaticField ptr
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ cmp r2, #0 @ is resolved entry null?
+ beq .LOP_SPUT_WIDE_VOLATILE_resolve @ yes, do resolve
+.LOP_SPUT_WIDE_VOLATILE_finish: @ field ptr in r2, AA in r9
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ GET_INST_OPCODE(r10) @ extract opcode from rINST
+ add r2, r2, #offStaticField_value @ r2<- pointer to data
+ .if 1
+ bl dvmQuasiAtomicSwap64 @ stores r0/r1 into addr r2
+ .else
+ stmia r2, {r0-r1} @ field<- vAA/vAA+1
+ .endif
+ GOTO_OPCODE(r10) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_BREAKPOINT: /* 0xec */
+/* File: armv5te/OP_BREAKPOINT.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_THROW_VERIFICATION_ERROR: /* 0xed */
+/* File: armv5te/OP_THROW_VERIFICATION_ERROR.S */
+ /*
+ * Handle a throw-verification-error instruction. This throws an
+ * exception for an error discovered during verification. The
+ * exception is indicated by AA, with some detail provided by BBBB.
+ */
+ /* op AA, ref@BBBB */
+ ldr r0, [rGLUE, #offGlue_method] @ r0<- glue->method
+ FETCH(r2, 1) @ r2<- BBBB
+ EXPORT_PC() @ export the PC
+ mov r1, rINST, lsr #8 @ r1<- AA
+ bl dvmThrowVerificationError @ always throws
+ b common_exceptionThrown @ handle exception
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_EXECUTE_INLINE: /* 0xee */
+/* File: armv5te/OP_EXECUTE_INLINE.S */
+ /*
+ * Execute a "native inline" instruction.
+ *
+ * We need to call an InlineOp4Func:
+ * bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+ *
+ * The first four args are in r0-r3, pointer to return value storage
+ * is on the stack. The function's return value is a flag that tells
+ * us if an exception was thrown.
+ */
+ /* [opt] execute-inline vAA, {vC, vD, vE, vF}, inline@BBBB */
+ FETCH(r10, 1) @ r10<- BBBB
+ add r1, rGLUE, #offGlue_retval @ r1<- &glue->retval
+ EXPORT_PC() @ can throw
+ sub sp, sp, #8 @ make room for arg, +64 bit align
+ mov r0, rINST, lsr #12 @ r0<- B
+ str r1, [sp] @ push &glue->retval
+ bl .LOP_EXECUTE_INLINE_continue @ make call; will return after
+ add sp, sp, #8 @ pop stack
+ cmp r0, #0 @ test boolean result of inline
+ beq common_exceptionThrown @ returned false, handle exception
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_EXECUTE_INLINE_RANGE: /* 0xef */
+/* File: armv5te/OP_EXECUTE_INLINE_RANGE.S */
+ /*
+ * Execute a "native inline" instruction, using "/range" semantics.
+ * Same idea as execute-inline, but we get the args differently.
+ *
+ * We need to call an InlineOp4Func:
+ * bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+ *
+ * The first four args are in r0-r3, pointer to return value storage
+ * is on the stack. The function's return value is a flag that tells
+ * us if an exception was thrown.
+ */
+ /* [opt] execute-inline/range {vCCCC..v(CCCC+AA-1)}, inline@BBBB */
+ FETCH(r10, 1) @ r10<- BBBB
+ add r1, rGLUE, #offGlue_retval @ r1<- &glue->retval
+ EXPORT_PC() @ can throw
+ sub sp, sp, #8 @ make room for arg, +64 bit align
+ mov r0, rINST, lsr #8 @ r0<- AA
+ str r1, [sp] @ push &glue->retval
+ bl .LOP_EXECUTE_INLINE_RANGE_continue @ make call; will return after
+ add sp, sp, #8 @ pop stack
+ cmp r0, #0 @ test boolean result of inline
+ beq common_exceptionThrown @ returned false, handle exception
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_DIRECT_EMPTY: /* 0xf0 */
+/* File: armv5te/OP_INVOKE_DIRECT_EMPTY.S */
+ /*
+ * invoke-direct-empty is a no-op in a "standard" interpreter.
+ */
+ FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ GOTO_OPCODE(ip) @ execute it
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_F1: /* 0xf1 */
+/* File: armv5te/OP_UNUSED_F1.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_QUICK: /* 0xf2 */
+/* File: armv5te/OP_IGET_QUICK.S */
+ /* For: iget-quick, iget-object-quick */
+ /* op vA, vB, offset@CCCC */
+ mov r2, rINST, lsr #12 @ r2<- B
+ GET_VREG(r3, r2) @ r3<- object we're operating on
+ FETCH(r1, 1) @ r1<- field byte offset
+ cmp r3, #0 @ check object for null
+ mov r2, rINST, lsr #8 @ r2<- A(+)
+ beq common_errNullObject @ object was null
+ ldr r0, [r3, r1] @ r0<- obj.field (always 32 bits)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_WIDE_QUICK: /* 0xf3 */
+/* File: armv4t/OP_IGET_WIDE_QUICK.S */
+ /* iget-wide-quick vA, vB, offset@CCCC */
+ mov r2, rINST, lsr #12 @ r2<- B
+ GET_VREG(r3, r2) @ r3<- object we're operating on
+ FETCH(r1, 1) @ r1<- field byte offset
+ cmp r3, #0 @ check object for null
+ mov r2, rINST, lsr #8 @ r2<- A(+)
+ beq common_errNullObject @ object was null
+ add r9, r3, r1 @ r9<- object + offset
+ ldmia r9, {r0-r1} @ r0/r1<- obj.field (64 bits, aligned)
+ and r2, r2, #15 @ r2<- A
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ add r3, rFP, r2, lsl #2 @ r3<- &fp[A]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ fp[A]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_OBJECT_QUICK: /* 0xf4 */
+/* File: armv5te/OP_IGET_OBJECT_QUICK.S */
+/* File: armv5te/OP_IGET_QUICK.S */
+ /* For: iget-quick, iget-object-quick */
+ /* op vA, vB, offset@CCCC */
+ mov r2, rINST, lsr #12 @ r2<- B
+ GET_VREG(r3, r2) @ r3<- object we're operating on
+ FETCH(r1, 1) @ r1<- field byte offset
+ cmp r3, #0 @ check object for null
+ mov r2, rINST, lsr #8 @ r2<- A(+)
+ beq common_errNullObject @ object was null
+ ldr r0, [r3, r1] @ r0<- obj.field (always 32 bits)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_QUICK: /* 0xf5 */
+/* File: armv5te/OP_IPUT_QUICK.S */
+ /* For: iput-quick */
+ /* op vA, vB, offset@CCCC */
+ mov r2, rINST, lsr #12 @ r2<- B
+ GET_VREG(r3, r2) @ r3<- fp[B], the object pointer
+ FETCH(r1, 1) @ r1<- field byte offset
+ cmp r3, #0 @ check object for null
+ mov r2, rINST, lsr #8 @ r2<- A(+)
+ beq common_errNullObject @ object was null
+ and r2, r2, #15
+ GET_VREG(r0, r2) @ r0<- fp[A]
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ str r0, [r3, r1] @ obj.field (always 32 bits)<- r0
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_WIDE_QUICK: /* 0xf6 */
+/* File: armv4t/OP_IPUT_WIDE_QUICK.S */
+ /* iput-wide-quick vA, vB, offset@CCCC */
+ mov r0, rINST, lsr #8 @ r0<- A(+)
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r0, r0, #15
+ GET_VREG(r2, r1) @ r2<- fp[B], the object pointer
+ add r3, rFP, r0, lsl #2 @ r3<- &fp[A]
+ cmp r2, #0 @ check object for null
+ ldmia r3, {r0-r1} @ r0/r1<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH(r3, 1) @ r3<- field byte offset
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ add r2, r2, r3 @ r2<- object + byte offset
+ stmia r2, {r0-r1} @ obj.field (64 bits, aligned)<- r0/r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_OBJECT_QUICK: /* 0xf7 */
+/* File: armv5te/OP_IPUT_OBJECT_QUICK.S */
+ /* For: iput-object-quick */
+ /* op vA, vB, offset@CCCC */
+ mov r2, rINST, lsr #12 @ r2<- B
+ GET_VREG(r3, r2) @ r3<- fp[B], the object pointer
+ FETCH(r1, 1) @ r1<- field byte offset
+ cmp r3, #0 @ check object for null
+ mov r2, rINST, lsr #8 @ r2<- A(+)
+ beq common_errNullObject @ object was null
+ and r2, r2, #15
+ GET_VREG(r0, r2) @ r0<- fp[A]
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ str r0, [r3, r1] @ obj.field (always 32 bits)<- r0
+ cmp r0, #0
+ strneb r2, [r2, r3, lsr #GC_CARD_SHIFT] @ mark card based on obj head
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_VIRTUAL_QUICK: /* 0xf8 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK.S */
+ /*
+ * Handle an optimized virtual method call.
+ *
+ * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r3, 2) @ r3<- FEDC or CCCC
+ FETCH(r1, 1) @ r1<- BBBB
+ .if (!0)
+ and r3, r3, #15 @ r3<- C (or stays CCCC)
+ .endif
+ GET_VREG(r2, r3) @ r2<- vC ("this" ptr)
+ cmp r2, #0 @ is "this" null?
+ beq common_errNullObject @ null "this", throw exception
+ ldr r2, [r2, #offObject_clazz] @ r2<- thisPtr->clazz
+ ldr r2, [r2, #offClassObject_vtable] @ r2<- thisPtr->clazz->vtable
+ EXPORT_PC() @ invoke must export
+ ldr r0, [r2, r1, lsl #2] @ r3<- vtable[BBBB]
+ bl common_invokeMethodNoRange @ continue on
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_VIRTUAL_QUICK_RANGE: /* 0xf9 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK_RANGE.S */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK.S */
+ /*
+ * Handle an optimized virtual method call.
+ *
+ * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r3, 2) @ r3<- FEDC or CCCC
+ FETCH(r1, 1) @ r1<- BBBB
+ .if (!1)
+ and r3, r3, #15 @ r3<- C (or stays CCCC)
+ .endif
+ GET_VREG(r2, r3) @ r2<- vC ("this" ptr)
+ cmp r2, #0 @ is "this" null?
+ beq common_errNullObject @ null "this", throw exception
+ ldr r2, [r2, #offObject_clazz] @ r2<- thisPtr->clazz
+ ldr r2, [r2, #offClassObject_vtable] @ r2<- thisPtr->clazz->vtable
+ EXPORT_PC() @ invoke must export
+ ldr r0, [r2, r1, lsl #2] @ r3<- vtable[BBBB]
+ bl common_invokeMethodRange @ continue on
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_SUPER_QUICK: /* 0xfa */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK.S */
+ /*
+ * Handle an optimized "super" method call.
+ *
+ * for: [opt] invoke-super-quick, invoke-super-quick/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ .if (!0)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r2, [r2, #offMethod_clazz] @ r2<- method->clazz
+ EXPORT_PC() @ must export for invoke
+ ldr r2, [r2, #offClassObject_super] @ r2<- method->clazz->super
+ GET_VREG(r3, r10) @ r3<- "this"
+ ldr r2, [r2, #offClassObject_vtable] @ r2<- ...clazz->super->vtable
+ cmp r3, #0 @ null "this" ref?
+ ldr r0, [r2, r1, lsl #2] @ r0<- super->vtable[BBBB]
+ beq common_errNullObject @ "this" is null, throw exception
+ bl common_invokeMethodNoRange @ continue on
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_SUPER_QUICK_RANGE: /* 0xfb */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK_RANGE.S */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK.S */
+ /*
+ * Handle an optimized "super" method call.
+ *
+ * for: [opt] invoke-super-quick, invoke-super-quick/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ .if (!1)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r2, [r2, #offMethod_clazz] @ r2<- method->clazz
+ EXPORT_PC() @ must export for invoke
+ ldr r2, [r2, #offClassObject_super] @ r2<- method->clazz->super
+ GET_VREG(r3, r10) @ r3<- "this"
+ ldr r2, [r2, #offClassObject_vtable] @ r2<- ...clazz->super->vtable
+ cmp r3, #0 @ null "this" ref?
+ ldr r0, [r2, r1, lsl #2] @ r0<- super->vtable[BBBB]
+ beq common_errNullObject @ "this" is null, throw exception
+ bl common_invokeMethodRange @ continue on
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */
+/* File: armv5te/OP_IPUT_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_IPUT_OBJECT.S */
+ /*
+ * 32-bit instance field put.
+ *
+ * for: iput-object, iput-object-volatile
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_OBJECT_VOLATILE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_OBJECT_VOLATILE_finish @ yes, finish up
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_OBJECT_VOLATILE: /* 0xfd */
+/* File: armv5te/OP_SGET_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_OBJECT_VOLATILE_resolve @ yes, do resolve
+.LOP_SGET_OBJECT_VOLATILE_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ SMP_DMB @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */
+/* File: armv5te/OP_SPUT_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_SPUT_OBJECT.S */
+ /*
+ * 32-bit SPUT handler for objects
+ *
+ * for: sput-object, sput-object-volatile
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_SPUT_OBJECT_VOLATILE_finish @ no, continue
+ ldr r9, [rGLUE, #offGlue_method] @ r9<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r9, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_OBJECT_VOLATILE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_FF: /* 0xff */
+/* File: armv5te/OP_UNUSED_FF.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+
+ .balign 64
+ .size dvmAsmInstructionStart, .-dvmAsmInstructionStart
+ .global dvmAsmInstructionEnd
+dvmAsmInstructionEnd:
+
+/*
+ * ===========================================================================
+ * Sister implementations
+ * ===========================================================================
+ */
+ .global dvmAsmSisterStart
+ .type dvmAsmSisterStart, %function
+ .text
+ .balign 4
+dvmAsmSisterStart:
+
+/* continuation for OP_CONST_STRING */
+
+ /*
+ * Continuation if the String has not yet been resolved.
+ * r1: BBBB (String ref)
+ * r9: target register
+ */
+.LOP_CONST_STRING_resolve:
+ EXPORT_PC()
+ ldr r0, [rGLUE, #offGlue_method] @ r0<- glue->method
+ ldr r0, [r0, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveString @ r0<- String reference
+ cmp r0, #0 @ failed?
+ beq common_exceptionThrown @ yup, handle the exception
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_CONST_STRING_JUMBO */
+
+ /*
+ * Continuation if the String has not yet been resolved.
+ * r1: BBBBBBBB (String ref)
+ * r9: target register
+ */
+.LOP_CONST_STRING_JUMBO_resolve:
+ EXPORT_PC()
+ ldr r0, [rGLUE, #offGlue_method] @ r0<- glue->method
+ ldr r0, [r0, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveString @ r0<- String reference
+ cmp r0, #0 @ failed?
+ beq common_exceptionThrown @ yup, handle the exception
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_CONST_CLASS */
+
+ /*
+ * Continuation if the Class has not yet been resolved.
+ * r1: BBBB (Class ref)
+ * r9: target register
+ */
+.LOP_CONST_CLASS_resolve:
+ EXPORT_PC()
+ ldr r0, [rGLUE, #offGlue_method] @ r0<- glue->method
+ mov r2, #1 @ r2<- true
+ ldr r0, [r0, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- Class reference
+ cmp r0, #0 @ failed?
+ beq common_exceptionThrown @ yup, handle the exception
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_CHECK_CAST */
+
+ /*
+ * Trivial test failed, need to perform full check. This is common.
+ * r0 holds obj->clazz
+ * r1 holds class resolved from BBBB
+ * r9 holds object
+ */
+.LOP_CHECK_CAST_fullcheck:
+ bl dvmInstanceofNonTrivial @ r0<- boolean result
+ cmp r0, #0 @ failed?
+ bne .LOP_CHECK_CAST_okay @ no, success
+
+ @ A cast has failed. We need to throw a ClassCastException with the
+ @ class of the object that failed to be cast.
+ EXPORT_PC() @ about to throw
+ ldr r3, [r9, #offObject_clazz] @ r3<- obj->clazz
+ ldr r0, .LstrClassCastExceptionPtr
+ ldr r1, [r3, #offClassObject_descriptor] @ r1<- obj->clazz->descriptor
+ bl dvmThrowExceptionWithClassMessage
+ b common_exceptionThrown
+
+ /*
+ * Resolution required. This is the least-likely path.
+ *
+ * r2 holds BBBB
+ * r9 holds object
+ */
+.LOP_CHECK_CAST_resolve:
+ EXPORT_PC() @ resolve() could throw
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ mov r1, r2 @ r1<- BBBB
+ mov r2, #0 @ r2<- false
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- resolved ClassObject ptr
+ cmp r0, #0 @ got null?
+ beq common_exceptionThrown @ yes, handle exception
+ mov r1, r0 @ r1<- class resolved from BBB
+ ldr r0, [r9, #offObject_clazz] @ r0<- obj->clazz
+ b .LOP_CHECK_CAST_resolved @ pick up where we left off
+
+.LstrClassCastExceptionPtr:
+ .word .LstrClassCastException
+
+/* continuation for OP_INSTANCE_OF */
+
+ /*
+ * Trivial test failed, need to perform full check. This is common.
+ * r0 holds obj->clazz
+ * r1 holds class resolved from BBBB
+ * r9 holds A
+ */
+.LOP_INSTANCE_OF_fullcheck:
+ bl dvmInstanceofNonTrivial @ r0<- boolean result
+ @ fall through to OP_INSTANCE_OF_store
+
+ /*
+ * r0 holds boolean result
+ * r9 holds A
+ */
+.LOP_INSTANCE_OF_store:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r0, r9) @ vA<- r0
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+ /*
+ * Trivial test succeeded, save and bail.
+ * r9 holds A
+ */
+.LOP_INSTANCE_OF_trivial:
+ mov r0, #1 @ indicate success
+ @ could b OP_INSTANCE_OF_store, but copying is faster and cheaper
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r0, r9) @ vA<- r0
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+ /*
+ * Resolution required. This is the least-likely path.
+ *
+ * r3 holds BBBB
+ * r9 holds A
+ */
+.LOP_INSTANCE_OF_resolve:
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [rGLUE, #offGlue_method] @ r0<- glue->method
+ mov r1, r3 @ r1<- BBBB
+ mov r2, #1 @ r2<- true
+ ldr r0, [r0, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- resolved ClassObject ptr
+ cmp r0, #0 @ got null?
+ beq common_exceptionThrown @ yes, handle exception
+ mov r1, r0 @ r1<- class resolved from BBB
+ mov r3, rINST, lsr #12 @ r3<- B
+ GET_VREG(r0, r3) @ r0<- vB (object)
+ ldr r0, [r0, #offObject_clazz] @ r0<- obj->clazz
+ b .LOP_INSTANCE_OF_resolved @ pick up where we left off
+
+/* continuation for OP_NEW_INSTANCE */
+
+ .balign 32 @ minimize cache lines
+.LOP_NEW_INSTANCE_finish: @ r0=new object
+ mov r3, rINST, lsr #8 @ r3<- AA
+ cmp r0, #0 @ failed?
+ beq common_exceptionThrown @ yes, handle the exception
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r3) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+ /*
+ * Class initialization required.
+ *
+ * r0 holds class object
+ */
+.LOP_NEW_INSTANCE_needinit:
+ mov r9, r0 @ save r0
+ bl dvmInitClass @ initialize class
+ cmp r0, #0 @ check boolean result
+ mov r0, r9 @ restore r0
+ bne .LOP_NEW_INSTANCE_initialized @ success, continue
+ b common_exceptionThrown @ failed, deal with init exception
+
+ /*
+ * Resolution required. This is the least-likely path.
+ *
+ * r1 holds BBBB
+ */
+.LOP_NEW_INSTANCE_resolve:
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ mov r2, #0 @ r2<- false
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- resolved ClassObject ptr
+ cmp r0, #0 @ got null?
+ bne .LOP_NEW_INSTANCE_resolved @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+.LstrInstantiationErrorPtr:
+ .word .LstrInstantiationError
+
+/* continuation for OP_NEW_ARRAY */
+
+
+ /*
+ * Resolve class. (This is an uncommon case.)
+ *
+ * r1 holds array length
+ * r2 holds class ref CCCC
+ */
+.LOP_NEW_ARRAY_resolve:
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ mov r9, r1 @ r9<- length (save)
+ mov r1, r2 @ r1<- CCCC
+ mov r2, #0 @ r2<- false
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- call(clazz, ref)
+ cmp r0, #0 @ got null?
+ mov r1, r9 @ r1<- length (restore)
+ beq common_exceptionThrown @ yes, handle exception
+ @ fall through to OP_NEW_ARRAY_finish
+
+ /*
+ * Finish allocation.
+ *
+ * r0 holds class
+ * r1 holds array length
+ */
+.LOP_NEW_ARRAY_finish:
+ mov r2, #ALLOC_DONT_TRACK @ don't track in local refs table
+ bl dvmAllocArrayByClass @ r0<- call(clazz, length, flags)
+ cmp r0, #0 @ failed?
+ mov r2, rINST, lsr #8 @ r2<- A+
+ beq common_exceptionThrown @ yes, handle the exception
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ vA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_FILLED_NEW_ARRAY */
+
+ /*
+ * On entry:
+ * r0 holds array class
+ * r10 holds AA or BA
+ */
+.LOP_FILLED_NEW_ARRAY_continue:
+ ldr r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor
+ mov r2, #ALLOC_DONT_TRACK @ r2<- alloc flags
+ ldrb rINST, [r3, #1] @ rINST<- descriptor[1]
+ .if 0
+ mov r1, r10 @ r1<- AA (length)
+ .else
+ mov r1, r10, lsr #4 @ r1<- B (length)
+ .endif
+ cmp rINST, #'I' @ array of ints?
+ cmpne rINST, #'L' @ array of objects?
+ cmpne rINST, #'[' @ array of arrays?
+ mov r9, r1 @ save length in r9
+ bne .LOP_FILLED_NEW_ARRAY_notimpl @ no, not handled yet
+ bl dvmAllocArrayByClass @ r0<- call(arClass, length, flags)
+ cmp r0, #0 @ null return?
+ beq common_exceptionThrown @ alloc failed, handle exception
+
+ FETCH(r1, 2) @ r1<- FEDC or CCCC
+ str r0, [rGLUE, #offGlue_retval] @ retval.l <- new array
+ str rINST, [rGLUE, #offGlue_retval+4] @ retval.h <- type
+ add r0, r0, #offArrayObject_contents @ r0<- newArray->contents
+ subs r9, r9, #1 @ length--, check for neg
+ FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST
+ bmi 2f @ was zero, bail
+
+ @ copy values from registers into the array
+ @ r0=array, r1=CCCC/FEDC, r9=length (from AA or B), r10=AA/BA
+ .if 0
+ add r2, rFP, r1, lsl #2 @ r2<- &fp[CCCC]
+1: ldr r3, [r2], #4 @ r3<- *r2++
+ subs r9, r9, #1 @ count--
+ str r3, [r0], #4 @ *contents++ = vX
+ bpl 1b
+ @ continue at 2
+ .else
+ cmp r9, #4 @ length was initially 5?
+ and r2, r10, #15 @ r2<- A
+ bne 1f @ <= 4 args, branch
+ GET_VREG(r3, r2) @ r3<- vA
+ sub r9, r9, #1 @ count--
+ str r3, [r0, #16] @ contents[4] = vA
+1: and r2, r1, #15 @ r2<- F/E/D/C
+ GET_VREG(r3, r2) @ r3<- vF/vE/vD/vC
+ mov r1, r1, lsr #4 @ r1<- next reg in low 4
+ subs r9, r9, #1 @ count--
+ str r3, [r0], #4 @ *contents++ = vX
+ bpl 1b
+ @ continue at 2
+ .endif
+
+2:
+ ldr r0, [rGLUE, #offGlue_retval] @ r0<- object
+ ldr r1, [rGLUE, #offGlue_retval+4] @ r1<- type
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ cmp r1, #'I' @ Is int array?
+ strneb r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card based on object head
+ GOTO_OPCODE(ip) @ execute it
+
+ /*
+ * Throw an exception indicating that we have not implemented this
+ * mode of filled-new-array.
+ */
+.LOP_FILLED_NEW_ARRAY_notimpl:
+ ldr r0, .L_strInternalError
+ ldr r1, .L_strFilledNewArrayNotImpl
+ bl dvmThrowException
+ b common_exceptionThrown
+
+ .if (!0) @ define in one or the other, not both
+.L_strFilledNewArrayNotImpl:
+ .word .LstrFilledNewArrayNotImpl
+.L_strInternalError:
+ .word .LstrInternalError
+ .endif
+
+/* continuation for OP_FILLED_NEW_ARRAY_RANGE */
+
+ /*
+ * On entry:
+ * r0 holds array class
+ * r10 holds AA or BA
+ */
+.LOP_FILLED_NEW_ARRAY_RANGE_continue:
+ ldr r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor
+ mov r2, #ALLOC_DONT_TRACK @ r2<- alloc flags
+ ldrb rINST, [r3, #1] @ rINST<- descriptor[1]
+ .if 1
+ mov r1, r10 @ r1<- AA (length)
+ .else
+ mov r1, r10, lsr #4 @ r1<- B (length)
+ .endif
+ cmp rINST, #'I' @ array of ints?
+ cmpne rINST, #'L' @ array of objects?
+ cmpne rINST, #'[' @ array of arrays?
+ mov r9, r1 @ save length in r9
+ bne .LOP_FILLED_NEW_ARRAY_RANGE_notimpl @ no, not handled yet
+ bl dvmAllocArrayByClass @ r0<- call(arClass, length, flags)
+ cmp r0, #0 @ null return?
+ beq common_exceptionThrown @ alloc failed, handle exception
+
+ FETCH(r1, 2) @ r1<- FEDC or CCCC
+ str r0, [rGLUE, #offGlue_retval] @ retval.l <- new array
+ str rINST, [rGLUE, #offGlue_retval+4] @ retval.h <- type
+ add r0, r0, #offArrayObject_contents @ r0<- newArray->contents
+ subs r9, r9, #1 @ length--, check for neg
+ FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST
+ bmi 2f @ was zero, bail
+
+ @ copy values from registers into the array
+ @ r0=array, r1=CCCC/FEDC, r9=length (from AA or B), r10=AA/BA
+ .if 1
+ add r2, rFP, r1, lsl #2 @ r2<- &fp[CCCC]
+1: ldr r3, [r2], #4 @ r3<- *r2++
+ subs r9, r9, #1 @ count--
+ str r3, [r0], #4 @ *contents++ = vX
+ bpl 1b
+ @ continue at 2
+ .else
+ cmp r9, #4 @ length was initially 5?
+ and r2, r10, #15 @ r2<- A
+ bne 1f @ <= 4 args, branch
+ GET_VREG(r3, r2) @ r3<- vA
+ sub r9, r9, #1 @ count--
+ str r3, [r0, #16] @ contents[4] = vA
+1: and r2, r1, #15 @ r2<- F/E/D/C
+ GET_VREG(r3, r2) @ r3<- vF/vE/vD/vC
+ mov r1, r1, lsr #4 @ r1<- next reg in low 4
+ subs r9, r9, #1 @ count--
+ str r3, [r0], #4 @ *contents++ = vX
+ bpl 1b
+ @ continue at 2
+ .endif
+
+2:
+ ldr r0, [rGLUE, #offGlue_retval] @ r0<- object
+ ldr r1, [rGLUE, #offGlue_retval+4] @ r1<- type
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ cmp r1, #'I' @ Is int array?
+ strneb r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card based on object head
+ GOTO_OPCODE(ip) @ execute it
+
+ /*
+ * Throw an exception indicating that we have not implemented this
+ * mode of filled-new-array.
+ */
+.LOP_FILLED_NEW_ARRAY_RANGE_notimpl:
+ ldr r0, .L_strInternalError
+ ldr r1, .L_strFilledNewArrayNotImpl
+ bl dvmThrowException
+ b common_exceptionThrown
+
+ .if (!1) @ define in one or the other, not both
+.L_strFilledNewArrayNotImpl:
+ .word .LstrFilledNewArrayNotImpl
+.L_strInternalError:
+ .word .LstrInternalError
+ .endif
+
+/* continuation for OP_CMPL_FLOAT */
+
+ @ Test for NaN with a second comparison. EABI forbids testing bit
+ @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+ @ make the library call.
+.LOP_CMPL_FLOAT_gt_or_nan:
+ mov r1, r9 @ reverse order
+ mov r0, r10
+ bl __aeabi_cfcmple @ r0<- Z set if eq, C clear if <
+ @bleq common_abort
+ movcc r1, #1 @ (greater than) r1<- 1
+ bcc .LOP_CMPL_FLOAT_finish
+ mvn r1, #0 @ r1<- 1 or -1 for NaN
+ b .LOP_CMPL_FLOAT_finish
+
+
+#if 0 /* "clasic" form */
+ FETCH(r0, 1) @ r0<- CCBB
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ GET_VREG(r9, r2) @ r9<- vBB
+ GET_VREG(r10, r3) @ r10<- vCC
+ mov r0, r9 @ r0<- vBB
+ mov r1, r10 @ r1<- vCC
+ bl __aeabi_fcmpeq @ r0<- (vBB == vCC)
+ cmp r0, #0 @ equal?
+ movne r1, #0 @ yes, result is 0
+ bne OP_CMPL_FLOAT_finish
+ mov r0, r9 @ r0<- vBB
+ mov r1, r10 @ r1<- vCC
+ bl __aeabi_fcmplt @ r0<- (vBB < vCC)
+ cmp r0, #0 @ less than?
+ b OP_CMPL_FLOAT_continue
+@%break
+
+OP_CMPL_FLOAT_continue:
+ mvnne r1, #0 @ yes, result is -1
+ bne OP_CMPL_FLOAT_finish
+ mov r0, r9 @ r0<- vBB
+ mov r1, r10 @ r1<- vCC
+ bl __aeabi_fcmpgt @ r0<- (vBB > vCC)
+ cmp r0, #0 @ greater than?
+ beq OP_CMPL_FLOAT_nan @ no, must be NaN
+ mov r1, #1 @ yes, result is 1
+ @ fall through to _finish
+
+OP_CMPL_FLOAT_finish:
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r3) @ vAA<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+ /*
+ * This is expected to be uncommon, so we double-branch (once to here,
+ * again back to _finish).
+ */
+OP_CMPL_FLOAT_nan:
+ mvn r1, #0 @ r1<- 1 or -1 for NaN
+ b OP_CMPL_FLOAT_finish
+
+#endif
+
+/* continuation for OP_CMPG_FLOAT */
+
+ @ Test for NaN with a second comparison. EABI forbids testing bit
+ @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+ @ make the library call.
+.LOP_CMPG_FLOAT_gt_or_nan:
+ mov r1, r9 @ reverse order
+ mov r0, r10
+ bl __aeabi_cfcmple @ r0<- Z set if eq, C clear if <
+ @bleq common_abort
+ movcc r1, #1 @ (greater than) r1<- 1
+ bcc .LOP_CMPG_FLOAT_finish
+ mov r1, #1 @ r1<- 1 or -1 for NaN
+ b .LOP_CMPG_FLOAT_finish
+
+
+#if 0 /* "clasic" form */
+ FETCH(r0, 1) @ r0<- CCBB
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ GET_VREG(r9, r2) @ r9<- vBB
+ GET_VREG(r10, r3) @ r10<- vCC
+ mov r0, r9 @ r0<- vBB
+ mov r1, r10 @ r1<- vCC
+ bl __aeabi_fcmpeq @ r0<- (vBB == vCC)
+ cmp r0, #0 @ equal?
+ movne r1, #0 @ yes, result is 0
+ bne OP_CMPG_FLOAT_finish
+ mov r0, r9 @ r0<- vBB
+ mov r1, r10 @ r1<- vCC
+ bl __aeabi_fcmplt @ r0<- (vBB < vCC)
+ cmp r0, #0 @ less than?
+ b OP_CMPG_FLOAT_continue
+@%break
+
+OP_CMPG_FLOAT_continue:
+ mvnne r1, #0 @ yes, result is -1
+ bne OP_CMPG_FLOAT_finish
+ mov r0, r9 @ r0<- vBB
+ mov r1, r10 @ r1<- vCC
+ bl __aeabi_fcmpgt @ r0<- (vBB > vCC)
+ cmp r0, #0 @ greater than?
+ beq OP_CMPG_FLOAT_nan @ no, must be NaN
+ mov r1, #1 @ yes, result is 1
+ @ fall through to _finish
+
+OP_CMPG_FLOAT_finish:
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r3) @ vAA<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+ /*
+ * This is expected to be uncommon, so we double-branch (once to here,
+ * again back to _finish).
+ */
+OP_CMPG_FLOAT_nan:
+ mov r1, #1 @ r1<- 1 or -1 for NaN
+ b OP_CMPG_FLOAT_finish
+
+#endif
+
+/* continuation for OP_CMPL_DOUBLE */
+
+ @ Test for NaN with a second comparison. EABI forbids testing bit
+ @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+ @ make the library call.
+.LOP_CMPL_DOUBLE_gt_or_nan:
+ ldmia r10, {r0-r1} @ reverse order
+ ldmia r9, {r2-r3}
+ bl __aeabi_cdcmple @ r0<- Z set if eq, C clear if <
+ @bleq common_abort
+ movcc r1, #1 @ (greater than) r1<- 1
+ bcc .LOP_CMPL_DOUBLE_finish
+ mvn r1, #0 @ r1<- 1 or -1 for NaN
+ b .LOP_CMPL_DOUBLE_finish
+
+/* continuation for OP_CMPG_DOUBLE */
+
+ @ Test for NaN with a second comparison. EABI forbids testing bit
+ @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+ @ make the library call.
+.LOP_CMPG_DOUBLE_gt_or_nan:
+ ldmia r10, {r0-r1} @ reverse order
+ ldmia r9, {r2-r3}
+ bl __aeabi_cdcmple @ r0<- Z set if eq, C clear if <
+ @bleq common_abort
+ movcc r1, #1 @ (greater than) r1<- 1
+ bcc .LOP_CMPG_DOUBLE_finish
+ mov r1, #1 @ r1<- 1 or -1 for NaN
+ b .LOP_CMPG_DOUBLE_finish
+
+/* continuation for OP_CMP_LONG */
+
+.LOP_CMP_LONG_less:
+ mvn r1, #0 @ r1<- -1
+ @ Want to cond code the next mov so we can avoid branch, but don't see it;
+ @ instead, we just replicate the tail end.
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r9) @ vAA<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+.LOP_CMP_LONG_greater:
+ mov r1, #1 @ r1<- 1
+ @ fall through to _finish
+
+.LOP_CMP_LONG_finish:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r9) @ vAA<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_AGET_WIDE */
+
+.LOP_AGET_WIDE_finish:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ add r0, r0, #offArrayObject_contents
+ ldmia r0, {r2-r3} @ r2/r3 <- vBB[vCC]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r2-r3} @ vAA/vAA+1<- r2/r3
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_APUT_WIDE */
+
+.LOP_APUT_WIDE_finish:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldmia r9, {r2-r3} @ r2/r3<- vAA/vAA+1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ add r0, #offArrayObject_contents
+ stmia r0, {r2-r3} @ vBB[vCC] <- r2/r3
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_APUT_OBJECT */
+ /*
+ * On entry:
+ * rINST = vBB (arrayObj)
+ * r9 = vAA (obj)
+ * r10 = offset into array (vBB + vCC * width)
+ */
+.LOP_APUT_OBJECT_finish:
+ cmp r9, #0 @ storing null reference?
+ beq .LOP_APUT_OBJECT_skip_check @ yes, skip type checks
+ ldr r0, [r9, #offObject_clazz] @ r0<- obj->clazz
+ ldr r1, [rINST, #offObject_clazz] @ r1<- arrayObj->clazz
+ bl dvmCanPutArrayElement @ test object type vs. array type
+ cmp r0, #0 @ okay?
+ beq common_errArrayStore @ no
+ mov r1, rINST @ r1<- arrayObj
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldr r2, [rGLUE, #offGlue_cardTable] @ get biased CT base
+ add r10, #offArrayObject_contents @ r0<- pointer to slot
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ str r9, [r10] @ vBB[vCC]<- vAA
+ strb r2, [r2, r1, lsr #GC_CARD_SHIFT] @ mark card using object head
+ GOTO_OPCODE(ip) @ jump to next instruction
+.LOP_APUT_OBJECT_skip_check:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ str r9, [r10, #offArrayObject_contents] @ vBB[vCC]<- vAA
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_finish:
+ @bl common_squeak0
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_WIDE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_WIDE_finish:
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ .if 0
+ add r0, r9, r3 @ r0<- address of field
+ bl dvmQuasiAtomicRead64 @ r0/r1<- contents of field
+ .else
+ add r9, r9, r3 @ r9<- obj + field offset
+ ldmia r9, {r0-r1} @ r0/r1<- obj.field (64-bit align ok)
+ .endif
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ add r3, rFP, r2, lsl #2 @ r3<- &fp[A]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ fp[A]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_OBJECT */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_OBJECT_finish:
+ @bl common_squeak0
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_BOOLEAN */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_BOOLEAN_finish:
+ @bl common_squeak1
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_BYTE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_BYTE_finish:
+ @bl common_squeak2
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_CHAR */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_CHAR_finish:
+ @bl common_squeak3
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_SHORT */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_SHORT_finish:
+ @bl common_squeak4
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_finish:
+ @bl common_squeak0
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT_WIDE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_WIDE_finish:
+ mov r2, rINST, lsr #8 @ r2<- A+
+ cmp r9, #0 @ check object for null
+ and r2, r2, #15 @ r2<- A
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ add r2, rFP, r2, lsl #2 @ r3<- &fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldmia r2, {r0-r1} @ r0/r1<- fp[A]
+ GET_INST_OPCODE(r10) @ extract opcode from rINST
+ add r2, r9, r3 @ r2<- object + byte offset
+ .if 0
+ bl dvmQuasiAtomicSwap64 @ stores r0/r1 into addr r2
+ .else
+ stmia r2, {r0-r1} @ obj.field (64 bits, aligned)<- r0/r1
+ .endif
+ GOTO_OPCODE(r10) @ jump to next instruction
+
+/* continuation for OP_IPUT_OBJECT */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_OBJECT_finish:
+ @bl common_squeak0
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r0, [r9, r3] @ obj.field (32 bits)<- r0
+ cmp r0, #0 @ stored a null reference?
+ strneb r2, [r2, r9, lsr #GC_CARD_SHIFT] @ mark card if not
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT_BOOLEAN */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_BOOLEAN_finish:
+ @bl common_squeak1
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT_BYTE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_BYTE_finish:
+ @bl common_squeak2
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT_CHAR */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_CHAR_finish:
+ @bl common_squeak3
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT_SHORT */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_SHORT_finish:
+ @bl common_squeak4
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SGET */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SGET_WIDE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ *
+ * Returns StaticField pointer in r0.
+ */
+.LOP_SGET_WIDE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_WIDE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SGET_OBJECT */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_OBJECT_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_OBJECT_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SGET_BOOLEAN */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_BOOLEAN_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_BOOLEAN_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SGET_BYTE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_BYTE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_BYTE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SGET_CHAR */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_CHAR_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_CHAR_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SGET_SHORT */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_SHORT_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_SHORT_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SPUT_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_WIDE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ * r9: &fp[AA]
+ *
+ * Returns StaticField pointer in r2.
+ */
+.LOP_SPUT_WIDE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ mov r2, r0 @ copy to r2
+ bne .LOP_SPUT_WIDE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_OBJECT */
+.LOP_SPUT_OBJECT_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ ldr r9, [r0, #offField_clazz] @ r9<- field->clazz
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ cmp r1, #0 @ stored a null object?
+ strneb r2, [r2, r9, lsr #GC_CARD_SHIFT] @ mark card based on obj head
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SPUT_BOOLEAN */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SPUT_BOOLEAN_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_BOOLEAN_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_BYTE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SPUT_BYTE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_BYTE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_CHAR */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SPUT_CHAR_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_CHAR_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_SHORT */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SPUT_SHORT_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_SHORT_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_INVOKE_VIRTUAL */
+
+ /*
+ * At this point:
+ * r0 = resolved base method
+ * r10 = C or CCCC (index of first arg, which is the "this" ptr)
+ */
+.LOP_INVOKE_VIRTUAL_continue:
+ GET_VREG(r1, r10) @ r1<- "this" ptr
+ ldrh r2, [r0, #offMethod_methodIndex] @ r2<- baseMethod->methodIndex
+ cmp r1, #0 @ is "this" null?
+ beq common_errNullObject @ null "this", throw exception
+ ldr r3, [r1, #offObject_clazz] @ r1<- thisPtr->clazz
+ ldr r3, [r3, #offClassObject_vtable] @ r3<- thisPtr->clazz->vtable
+ ldr r0, [r3, r2, lsl #2] @ r3<- vtable[methodIndex]
+ bl common_invokeMethodNoRange @ continue on
+
+/* continuation for OP_INVOKE_SUPER */
+
+ /*
+ * At this point:
+ * r0 = resolved base method
+ * r9 = method->clazz
+ */
+.LOP_INVOKE_SUPER_continue:
+ ldr r1, [r9, #offClassObject_super] @ r1<- method->clazz->super
+ ldrh r2, [r0, #offMethod_methodIndex] @ r2<- baseMethod->methodIndex
+ ldr r3, [r1, #offClassObject_vtableCount] @ r3<- super->vtableCount
+ EXPORT_PC() @ must export for invoke
+ cmp r2, r3 @ compare (methodIndex, vtableCount)
+ bcs .LOP_INVOKE_SUPER_nsm @ method not present in superclass
+ ldr r1, [r1, #offClassObject_vtable] @ r1<- ...clazz->super->vtable
+ ldr r0, [r1, r2, lsl #2] @ r3<- vtable[methodIndex]
+ bl common_invokeMethodNoRange @ continue on
+
+.LOP_INVOKE_SUPER_resolve:
+ mov r0, r9 @ r0<- method->clazz
+ mov r2, #METHOD_VIRTUAL @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ bne .LOP_INVOKE_SUPER_continue @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+ /*
+ * Throw a NoSuchMethodError with the method name as the message.
+ * r0 = resolved base method
+ */
+.LOP_INVOKE_SUPER_nsm:
+ ldr r1, [r0, #offMethod_name] @ r1<- method name
+ b common_errNoSuchMethod
+
+/* continuation for OP_INVOKE_DIRECT */
+
+ /*
+ * On entry:
+ * r1 = reference (BBBB or CCCC)
+ * r10 = "this" register
+ */
+.LOP_INVOKE_DIRECT_resolve:
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ mov r2, #METHOD_DIRECT @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ GET_VREG(r2, r10) @ r2<- "this" ptr (reload)
+ bne .LOP_INVOKE_DIRECT_finish @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+/* continuation for OP_INVOKE_VIRTUAL_RANGE */
+
+ /*
+ * At this point:
+ * r0 = resolved base method
+ * r10 = C or CCCC (index of first arg, which is the "this" ptr)
+ */
+.LOP_INVOKE_VIRTUAL_RANGE_continue:
+ GET_VREG(r1, r10) @ r1<- "this" ptr
+ ldrh r2, [r0, #offMethod_methodIndex] @ r2<- baseMethod->methodIndex
+ cmp r1, #0 @ is "this" null?
+ beq common_errNullObject @ null "this", throw exception
+ ldr r3, [r1, #offObject_clazz] @ r1<- thisPtr->clazz
+ ldr r3, [r3, #offClassObject_vtable] @ r3<- thisPtr->clazz->vtable
+ ldr r0, [r3, r2, lsl #2] @ r3<- vtable[methodIndex]
+ bl common_invokeMethodRange @ continue on
+
+/* continuation for OP_INVOKE_SUPER_RANGE */
+
+ /*
+ * At this point:
+ * r0 = resolved base method
+ * r9 = method->clazz
+ */
+.LOP_INVOKE_SUPER_RANGE_continue:
+ ldr r1, [r9, #offClassObject_super] @ r1<- method->clazz->super
+ ldrh r2, [r0, #offMethod_methodIndex] @ r2<- baseMethod->methodIndex
+ ldr r3, [r1, #offClassObject_vtableCount] @ r3<- super->vtableCount
+ EXPORT_PC() @ must export for invoke
+ cmp r2, r3 @ compare (methodIndex, vtableCount)
+ bcs .LOP_INVOKE_SUPER_RANGE_nsm @ method not present in superclass
+ ldr r1, [r1, #offClassObject_vtable] @ r1<- ...clazz->super->vtable
+ ldr r0, [r1, r2, lsl #2] @ r3<- vtable[methodIndex]
+ bl common_invokeMethodRange @ continue on
+
+.LOP_INVOKE_SUPER_RANGE_resolve:
+ mov r0, r9 @ r0<- method->clazz
+ mov r2, #METHOD_VIRTUAL @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ bne .LOP_INVOKE_SUPER_RANGE_continue @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+ /*
+ * Throw a NoSuchMethodError with the method name as the message.
+ * r0 = resolved base method
+ */
+.LOP_INVOKE_SUPER_RANGE_nsm:
+ ldr r1, [r0, #offMethod_name] @ r1<- method name
+ b common_errNoSuchMethod
+
+/* continuation for OP_INVOKE_DIRECT_RANGE */
+
+ /*
+ * On entry:
+ * r1 = reference (BBBB or CCCC)
+ * r10 = "this" register
+ */
+.LOP_INVOKE_DIRECT_RANGE_resolve:
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ mov r2, #METHOD_DIRECT @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ GET_VREG(r2, r10) @ r2<- "this" ptr (reload)
+ bne .LOP_INVOKE_DIRECT_RANGE_finish @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+/* continuation for OP_FLOAT_TO_LONG */
+/*
+ * Convert the float in r0 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification. The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer. The EABI convert function isn't doing this for us.
+ */
+f2l_doconv:
+ stmfd sp!, {r4, lr}
+ mov r1, #0x5f000000 @ (float)maxlong
+ mov r4, r0
+ bl __aeabi_fcmpge @ is arg >= maxlong?
+ cmp r0, #0 @ nonzero == yes
+ mvnne r0, #0 @ return maxlong (7fffffff)
+ mvnne r1, #0x80000000
+ ldmnefd sp!, {r4, pc}
+
+ mov r0, r4 @ recover arg
+ mov r1, #0xdf000000 @ (float)minlong
+ bl __aeabi_fcmple @ is arg <= minlong?
+ cmp r0, #0 @ nonzero == yes
+ movne r0, #0 @ return minlong (80000000)
+ movne r1, #0x80000000
+ ldmnefd sp!, {r4, pc}
+
+ mov r0, r4 @ recover arg
+ mov r1, r4
+ bl __aeabi_fcmpeq @ is arg == self?
+ cmp r0, #0 @ zero == no
+ moveq r1, #0 @ return zero for NaN
+ ldmeqfd sp!, {r4, pc}
+
+ mov r0, r4 @ recover arg
+ bl __aeabi_f2lz @ convert float to long
+ ldmfd sp!, {r4, pc}
+
+/* continuation for OP_DOUBLE_TO_LONG */
+/*
+ * Convert the double in r0/r1 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification. The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer. The EABI convert function isn't doing this for us.
+ */
+d2l_doconv:
+ stmfd sp!, {r4, r5, lr} @ save regs
+ mov r3, #0x43000000 @ maxlong, as a double (high word)
+ add r3, #0x00e00000 @ 0x43e00000
+ mov r2, #0 @ maxlong, as a double (low word)
+ sub sp, sp, #4 @ align for EABI
+ mov r4, r0 @ save a copy of r0
+ mov r5, r1 @ and r1
+ bl __aeabi_dcmpge @ is arg >= maxlong?
+ cmp r0, #0 @ nonzero == yes
+ mvnne r0, #0 @ return maxlong (7fffffffffffffff)
+ mvnne r1, #0x80000000
+ bne 1f
+
+ mov r0, r4 @ recover arg
+ mov r1, r5
+ mov r3, #0xc3000000 @ minlong, as a double (high word)
+ add r3, #0x00e00000 @ 0xc3e00000
+ mov r2, #0 @ minlong, as a double (low word)
+ bl __aeabi_dcmple @ is arg <= minlong?
+ cmp r0, #0 @ nonzero == yes
+ movne r0, #0 @ return minlong (8000000000000000)
+ movne r1, #0x80000000
+ bne 1f
+
+ mov r0, r4 @ recover arg
+ mov r1, r5
+ mov r2, r4 @ compare against self
+ mov r3, r5
+ bl __aeabi_dcmpeq @ is arg == self?
+ cmp r0, #0 @ zero == no
+ moveq r1, #0 @ return zero for NaN
+ beq 1f
+
+ mov r0, r4 @ recover arg
+ mov r1, r5
+ bl __aeabi_d2lz @ convert double to long
+
+1:
+ add sp, sp, #4
+ ldmfd sp!, {r4, r5, pc}
+
+/* continuation for OP_MUL_LONG */
+
+.LOP_MUL_LONG_finish:
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r0, {r9-r10} @ vAA/vAA+1<- r9/r10
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SHL_LONG */
+
+.LOP_SHL_LONG_finish:
+ mov r0, r0, asl r2 @ r0<- r0 << r2
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SHR_LONG */
+
+.LOP_SHR_LONG_finish:
+ mov r1, r1, asr r2 @ r1<- r1 >> r2
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_USHR_LONG */
+
+.LOP_USHR_LONG_finish:
+ mov r1, r1, lsr r2 @ r1<- r1 >>> r2
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SHL_LONG_2ADDR */
+
+.LOP_SHL_LONG_2ADDR_finish:
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SHR_LONG_2ADDR */
+
+.LOP_SHR_LONG_2ADDR_finish:
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_USHR_LONG_2ADDR */
+
+.LOP_USHR_LONG_2ADDR_finish:
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_VOLATILE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_VOLATILE_finish:
+ @bl common_squeak0
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ SMP_DMB @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT_VOLATILE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_VOLATILE_finish:
+ @bl common_squeak0
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SMP_DMB @ releasing store
+ str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SGET_VOLATILE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_VOLATILE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_VOLATILE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_VOLATILE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SPUT_VOLATILE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_VOLATILE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_IGET_OBJECT_VOLATILE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_OBJECT_VOLATILE_finish:
+ @bl common_squeak0
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ SMP_DMB @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_WIDE_VOLATILE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_WIDE_VOLATILE_finish:
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ .if 1
+ add r0, r9, r3 @ r0<- address of field
+ bl dvmQuasiAtomicRead64 @ r0/r1<- contents of field
+ .else
+ add r9, r9, r3 @ r9<- obj + field offset
+ ldmia r9, {r0-r1} @ r0/r1<- obj.field (64-bit align ok)
+ .endif
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ add r3, rFP, r2, lsl #2 @ r3<- &fp[A]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ fp[A]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT_WIDE_VOLATILE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_WIDE_VOLATILE_finish:
+ mov r2, rINST, lsr #8 @ r2<- A+
+ cmp r9, #0 @ check object for null
+ and r2, r2, #15 @ r2<- A
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ add r2, rFP, r2, lsl #2 @ r3<- &fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldmia r2, {r0-r1} @ r0/r1<- fp[A]
+ GET_INST_OPCODE(r10) @ extract opcode from rINST
+ add r2, r9, r3 @ r2<- object + byte offset
+ .if 1
+ bl dvmQuasiAtomicSwap64 @ stores r0/r1 into addr r2
+ .else
+ stmia r2, {r0-r1} @ obj.field (64 bits, aligned)<- r0/r1
+ .endif
+ GOTO_OPCODE(r10) @ jump to next instruction
+
+/* continuation for OP_SGET_WIDE_VOLATILE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ *
+ * Returns StaticField pointer in r0.
+ */
+.LOP_SGET_WIDE_VOLATILE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_WIDE_VOLATILE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_WIDE_VOLATILE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ * r9: &fp[AA]
+ *
+ * Returns StaticField pointer in r2.
+ */
+.LOP_SPUT_WIDE_VOLATILE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ mov r2, r0 @ copy to r2
+ bne .LOP_SPUT_WIDE_VOLATILE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_EXECUTE_INLINE */
+
+ /*
+ * Extract args, call function.
+ * r0 = #of args (0-4)
+ * r10 = call index
+ * lr = return addr, above [DO NOT bl out of here w/o preserving LR]
+ *
+ * Other ideas:
+ * - Use a jump table from the main piece to jump directly into the
+ * AND/LDR pairs. Costs a data load, saves a branch.
+ * - Have five separate pieces that do the loading, so we can work the
+ * interleave a little better. Increases code size.
+ */
+.LOP_EXECUTE_INLINE_continue:
+ rsb r0, r0, #4 @ r0<- 4-r0
+ FETCH(r9, 2) @ r9<- FEDC
+ add pc, pc, r0, lsl #3 @ computed goto, 2 instrs each
+ bl common_abort @ (skipped due to ARM prefetch)
+4: and ip, r9, #0xf000 @ isolate F
+ ldr r3, [rFP, ip, lsr #10] @ r3<- vF (shift right 12, left 2)
+3: and ip, r9, #0x0f00 @ isolate E
+ ldr r2, [rFP, ip, lsr #6] @ r2<- vE
+2: and ip, r9, #0x00f0 @ isolate D
+ ldr r1, [rFP, ip, lsr #2] @ r1<- vD
+1: and ip, r9, #0x000f @ isolate C
+ ldr r0, [rFP, ip, lsl #2] @ r0<- vC
+0:
+ ldr r9, .LOP_EXECUTE_INLINE_table @ table of InlineOperation
+ LDR_PC "[r9, r10, lsl #4]" @ sizeof=16, "func" is first entry
+ @ (not reached)
+
+.LOP_EXECUTE_INLINE_table:
+ .word gDvmInlineOpsTable
+
+/* continuation for OP_EXECUTE_INLINE_RANGE */
+
+ /*
+ * Extract args, call function.
+ * r0 = #of args (0-4)
+ * r10 = call index
+ * lr = return addr, above [DO NOT bl out of here w/o preserving LR]
+ */
+.LOP_EXECUTE_INLINE_RANGE_continue:
+ rsb r0, r0, #4 @ r0<- 4-r0
+ FETCH(r9, 2) @ r9<- CCCC
+ add pc, pc, r0, lsl #3 @ computed goto, 2 instrs each
+ bl common_abort @ (skipped due to ARM prefetch)
+4: add ip, r9, #3 @ base+3
+ GET_VREG(r3, ip) @ r3<- vBase[3]
+3: add ip, r9, #2 @ base+2
+ GET_VREG(r2, ip) @ r2<- vBase[2]
+2: add ip, r9, #1 @ base+1
+ GET_VREG(r1, ip) @ r1<- vBase[1]
+1: add ip, r9, #0 @ (nop)
+ GET_VREG(r0, ip) @ r0<- vBase[0]
+0:
+ ldr r9, .LOP_EXECUTE_INLINE_RANGE_table @ table of InlineOperation
+ LDR_PC "[r9, r10, lsl #4]" @ sizeof=16, "func" is first entry
+ @ (not reached)
+
+.LOP_EXECUTE_INLINE_RANGE_table:
+ .word gDvmInlineOpsTable
+
+/* continuation for OP_IPUT_OBJECT_VOLATILE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_OBJECT_VOLATILE_finish:
+ @bl common_squeak0
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SMP_DMB @ releasing store
+ str r0, [r9, r3] @ obj.field (32 bits)<- r0
+ cmp r0, #0 @ stored a null reference?
+ strneb r2, [r2, r9, lsr #GC_CARD_SHIFT] @ mark card if not
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SGET_OBJECT_VOLATILE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_OBJECT_VOLATILE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_OBJECT_VOLATILE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_OBJECT_VOLATILE */
+.LOP_SPUT_OBJECT_VOLATILE_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ ldr r9, [r0, #offField_clazz] @ r9<- field->clazz
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SMP_DMB @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ cmp r1, #0 @ stored a null object?
+ strneb r2, [r2, r9, lsr #GC_CARD_SHIFT] @ mark card based on obj head
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+ .size dvmAsmSisterStart, .-dvmAsmSisterStart
+ .global dvmAsmSisterEnd
+dvmAsmSisterEnd:
+
+/* File: armv5te/footer.S */
+
+/*
+ * ===========================================================================
+ * Common subroutines and data
+ * ===========================================================================
+ */
+
+
+
+ .text
+ .align 2
+
+#if defined(WITH_JIT)
+#if defined(WITH_SELF_VERIFICATION)
+ .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r2,#kSVSPunt @ r2<- interpreter entry point
+ mov r3, #0
+ str r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+ str lr,[rGLUE,#offGlue_jitResumeNPC]
+ str r1,[rGLUE,#offGlue_jitResumeDPC]
+ mov r2,#kSVSSingleStep @ r2<- interpreter entry point
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r0,rPC @ pass our target PC
+ mov r2,#kSVSNoProfile @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [r10, #offThread_inJitCodeCache] @ back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r0,rPC @ pass our target PC
+ mov r2,#kSVSTraceSelect @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ ldr r0,[lr, #-1] @ pass our target PC
+ mov r2,#kSVSTraceSelect @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpBackwardBranch
+dvmJitToInterpBackwardBranch:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ ldr r0,[lr, #-1] @ pass our target PC
+ mov r2,#kSVSBackwardBranch @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ ldr r0,[lr, #-1] @ pass our target PC
+ mov r2,#kSVSNormal @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r0,rPC @ pass our target PC
+ mov r2,#kSVSNoChain @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+#else
+/*
+ * Return from the translation cache to the interpreter when the compiler is
+ * having issues translating/executing a Dalvik instruction. We have to skip
+ * the code cache lookup otherwise it is possible to indefinitely bouce
+ * between the interpreter and the code cache if the instruction that fails
+ * to be compiled happens to be at a trace start.
+ */
+ .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov rPC, r0
+#if defined(WITH_JIT_TUNING)
+ mov r0,lr
+ bl dvmBumpPunt;
+#endif
+ EXPORT_PC()
+ mov r0, #0
+ str r0, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ adrl rIBASE, dvmAsmInstructionStart
+ FETCH_INST()
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+
+/*
+ * Return to the interpreter to handle a single instruction.
+ * On entry:
+ * r0 <= PC
+ * r1 <= PC of resume instruction
+ * lr <= resume point in translation
+ */
+ .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+ str lr,[rGLUE,#offGlue_jitResumeNPC]
+ str r1,[rGLUE,#offGlue_jitResumeDPC]
+ mov r1,#kInterpEntryInstr
+ @ enum is 4 byte in aapcs-EABI
+ str r1, [rGLUE, #offGlue_entryPoint]
+ mov rPC,r0
+ EXPORT_PC()
+
+ adrl rIBASE, dvmAsmInstructionStart
+ mov r2,#kJitSingleStep @ Ask for single step and then revert
+ str r2,[rGLUE,#offGlue_jitState]
+ mov r1,#1 @ set changeInterp to bail to debug interp
+ b common_gotoBail
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target. Commonly used for callees.
+ */
+ .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+#if defined(WITH_JIT_TUNING)
+ bl dvmBumpNoChain
+#endif
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r0,rPC
+ bl dvmJitGetCodeAddr @ Is there a translation?
+ str r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0 @ !0 means translation exists
+ bxne r0 @ continue native execution if so
+ b 2f @ branch over to use the interpreter
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target. Commonly used following
+ * invokes.
+ */
+ .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+ ldr rPC,[lr, #-1] @ get our target PC
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ add rINST,lr,#-5 @ save start of chain branch
+ add rINST, #-4 @ .. which is 9 bytes back
+ mov r0,rPC
+ bl dvmJitGetCodeAddr @ Is there a translation?
+ str r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ cmp r0,#0
+ beq 2f
+ mov r1,rINST
+ bl dvmJitChain @ r0<- dvmJitChain(codeAddr,chainAddr)
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0 @ successful chain?
+ bxne r0 @ continue native execution
+ b toInterpreter @ didn't chain - resume with interpreter
+
+/* No translation, so request one if profiling isn't disabled*/
+2:
+ adrl rIBASE, dvmAsmInstructionStart
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_INST()
+ cmp r0, #0
+ movne r2,#kJitTSelectRequestHot @ ask for trace selection
+ bne common_selectTrace
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+
+/*
+ * Return from the translation cache to the interpreter.
+ * The return was done with a BLX from thumb mode, and
+ * the following 32-bit word contains the target rPC value.
+ * Note that lr (r14) will have its low-order bit set to denote
+ * its thumb-mode origin.
+ *
+ * We'll need to stash our lr origin away, recover the new
+ * target and then check to see if there is a translation available
+ * for our new target. If so, we do a translation chain and
+ * go back to native execution. Otherwise, it's back to the
+ * interpreter (after treating this entry as a potential
+ * trace start).
+ */
+ .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+ ldr rPC,[lr, #-1] @ get our target PC
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ add rINST,lr,#-5 @ save start of chain branch
+ add rINST,#-4 @ .. which is 9 bytes back
+#if defined(WITH_JIT_TUNING)
+ bl dvmBumpNormal
+#endif
+ mov r0,rPC
+ bl dvmJitGetCodeAddr @ Is there a translation?
+ str r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ cmp r0,#0
+ beq toInterpreter @ go if not, otherwise do chain
+ mov r1,rINST
+ bl dvmJitChain @ r0<- dvmJitChain(codeAddr,chainAddr)
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0 @ successful chain?
+ bxne r0 @ continue native execution
+ b toInterpreter @ didn't chain - resume with interpreter
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+ .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+#if defined(WITH_JIT_TUNING)
+ bl dvmBumpNoChain
+#endif
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r0,rPC
+ bl dvmJitGetCodeAddr @ Is there a translation?
+ str r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0
+ bxne r0 @ continue native execution if so
+ EXPORT_PC()
+ adrl rIBASE, dvmAsmInstructionStart
+ FETCH_INST()
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+ .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+#if defined(WITH_JIT_TUNING)
+ bl dvmBumpNoChain
+#endif
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r0,rPC
+ bl dvmJitGetCodeAddr @ Is there a translation?
+ str r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0
+ bxne r0 @ continue native execution if so
+#endif
+
+/*
+ * No translation, restore interpreter regs and start interpreting.
+ * rGLUE & rFP were preserved in the translated code, and rPC has
+ * already been restored by the time we get here. We'll need to set
+ * up rIBASE & rINST, and load the address of the JitTable into r0.
+ */
+toInterpreter:
+ EXPORT_PC()
+ adrl rIBASE, dvmAsmInstructionStart
+ FETCH_INST()
+ GET_JIT_PROF_TABLE(r0)
+ @ NOTE: intended fallthrough
+
+/*
+ * Common code to update potential trace start counter, and initiate
+ * a trace-build if appropriate. On entry, rPC should point to the
+ * next instruction to execute, and rINST should be already loaded with
+ * the next opcode word, and r0 holds a pointer to the jit profile
+ * table (pJitProfTable).
+ */
+common_testUpdateProfile:
+ cmp r0,#0
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE_IFEQ(ip) @ if not profiling, fallthrough otherwise */
+
+common_updateProfile:
+ eor r3,rPC,rPC,lsr #12 @ cheap, but fast hash function
+ lsl r3,r3,#(32 - JIT_PROF_SIZE_LOG_2) @ shift out excess bits
+ ldrb r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ get counter
+ GET_INST_OPCODE(ip)
+ subs r1,r1,#1 @ decrement counter
+ strb r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ and store it
+ GOTO_OPCODE_IFNE(ip) @ if not threshold, fallthrough otherwise */
+
+/*
+ * Here, we switch to the debug interpreter to request
+ * trace selection. First, though, check to see if there
+ * is already a native translation in place (and, if so,
+ * jump to it now).
+ */
+ GET_JIT_THRESHOLD(r1)
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ strb r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ reset counter
+ EXPORT_PC()
+ mov r0,rPC
+ bl dvmJitGetCodeAddr @ r0<- dvmJitGetCodeAddr(rPC)
+ str r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0
+#if !defined(WITH_SELF_VERIFICATION)
+ bxne r0 @ jump to the translation
+ mov r2,#kJitTSelectRequest @ ask for trace selection
+ @ fall-through to common_selectTrace
+#else
+ moveq r2,#kJitTSelectRequest @ ask for trace selection
+ beq common_selectTrace
+ /*
+ * At this point, we have a target translation. However, if
+ * that translation is actually the interpret-only pseudo-translation
+ * we want to treat it the same as no translation.
+ */
+ mov r10, r0 @ save target
+ bl dvmCompilerGetInterpretTemplate
+ cmp r0, r10 @ special case?
+ bne jitSVShadowRunStart @ set up self verification shadow space
+ @ Need to clear the inJitCodeCache flag
+ ldr r10, [rGLUE, #offGlue_self] @ r10 <- glue->self
+ mov r3, #0 @ 0 means not in the JIT code cache
+ str r3, [r10, #offThread_inJitCodeCache] @ back to the interp land
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+ /* no return */
+#endif
+
+/*
+ * On entry:
+ * r2 is jit state, e.g. kJitTSelectRequest or kJitTSelectRequestHot
+ */
+common_selectTrace:
+ str r2,[rGLUE,#offGlue_jitState]
+ mov r2,#kInterpEntryInstr @ normal entry reason
+ str r2,[rGLUE,#offGlue_entryPoint]
+ mov r1,#1 @ set changeInterp
+ b common_gotoBail
+
+#if defined(WITH_SELF_VERIFICATION)
+/*
+ * Save PC and registers to shadow memory for self verification mode
+ * before jumping to native translation.
+ * On entry:
+ * rPC, rFP, rGLUE: the values that they should contain
+ * r10: the address of the target translation.
+ */
+jitSVShadowRunStart:
+ mov r0,rPC @ r0<- program counter
+ mov r1,rFP @ r1<- frame pointer
+ mov r2,rGLUE @ r2<- InterpState pointer
+ mov r3,r10 @ r3<- target translation
+ bl dvmSelfVerificationSaveState @ save registers to shadow space
+ ldr rFP,[r0,#offShadowSpace_shadowFP] @ rFP<- fp in shadow space
+ add rGLUE,r0,#offShadowSpace_interpState @ rGLUE<- rGLUE in shadow space
+ bx r10 @ jump to the translation
+
+/*
+ * Restore PC, registers, and interpState to original values
+ * before jumping back to the interpreter.
+ */
+jitSVShadowRunEnd:
+ mov r1,rFP @ pass ending fp
+ bl dvmSelfVerificationRestoreState @ restore pc and fp values
+ ldr rPC,[r0,#offShadowSpace_startPC] @ restore PC
+ ldr rFP,[r0,#offShadowSpace_fp] @ restore FP
+ ldr rGLUE,[r0,#offShadowSpace_glue] @ restore InterpState
+ ldr r1,[r0,#offShadowSpace_svState] @ get self verification state
+ cmp r1,#0 @ check for punt condition
+ beq 1f
+ mov r2,#kJitSelfVerification @ ask for self verification
+ str r2,[rGLUE,#offGlue_jitState]
+ mov r2,#kInterpEntryInstr @ normal entry reason
+ str r2,[rGLUE,#offGlue_entryPoint]
+ mov r1,#1 @ set changeInterp
+ b common_gotoBail
+
+1: @ exit to interpreter without check
+ EXPORT_PC()
+ adrl rIBASE, dvmAsmInstructionStart
+ FETCH_INST()
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+#endif
+
+#endif
+
+/*
+ * Common code when a backward branch is taken.
+ *
+ * TODO: we could avoid a branch by just setting r0 and falling through
+ * into the common_periodicChecks code, and having a test on r0 at the
+ * end determine if we should return to the caller or update & branch to
+ * the next instr.
+ *
+ * On entry:
+ * r9 is PC adjustment *in bytes*
+ */
+common_backwardBranch:
+ mov r0, #kInterpEntryInstr
+ bl common_periodicChecks
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/*
+ * Need to see if the thread needs to be suspended or debugger/profiler
+ * activity has begun. If so, we suspend the thread or side-exit to
+ * the debug interpreter as appropriate.
+ *
+ * The common case is no activity on any of these, so we want to figure
+ * that out quickly. If something is up, we can then sort out what.
+ *
+ * We want to be fast if the VM was built without debugger or profiler
+ * support, but we also need to recognize that the system is usually
+ * shipped with both of these enabled.
+ *
+ * TODO: reduce this so we're just checking a single location.
+ *
+ * On entry:
+ * r0 is reentry type, e.g. kInterpEntryInstr (for debugger/profiling)
+ * r9 is trampoline PC adjustment *in bytes*
+ */
+common_periodicChecks:
+ ldr r3, [rGLUE, #offGlue_pSelfSuspendCount] @ r3<- &suspendCount
+
+ ldr r1, [rGLUE, #offGlue_pDebuggerActive] @ r1<- &debuggerActive
+ ldr r2, [rGLUE, #offGlue_pActiveProfilers] @ r2<- &activeProfilers
+
+ ldr ip, [r3] @ ip<- suspendCount (int)
+
+ cmp r1, #0 @ debugger enabled?
+ ldrneb r1, [r1] @ yes, r1<- debuggerActive (boolean)
+ ldr r2, [r2] @ r2<- activeProfilers (int)
+ orrne ip, ip, r1 @ ip<- suspendCount | debuggerActive
+ orrs ip, ip, r2 @ ip<- suspend|debugger|profiler; set Z
+
+ bxeq lr @ all zero, return
+
+ /*
+ * One or more interesting events have happened. Figure out what.
+ *
+ * If debugging or profiling are compiled in, we need to disambiguate.
+ *
+ * r0 still holds the reentry type.
+ */
+ ldr ip, [r3] @ ip<- suspendCount (int)
+ cmp ip, #0 @ want suspend?
+ beq 1f @ no, must be debugger/profiler
+
+ stmfd sp!, {r0, lr} @ preserve r0 and lr
+#if defined(WITH_JIT)
+ /*
+ * Refresh the Jit's cached copy of profile table pointer. This pointer
+ * doubles as the Jit's on/off switch.
+ */
+ ldr r3, [rGLUE, #offGlue_ppJitProfTable] @ r3<-&gDvmJit.pJitProfTable
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ ldr r3, [r3] @ r3 <- pJitProfTable
+ EXPORT_PC() @ need for precise GC
+ str r3, [rGLUE, #offGlue_pJitProfTable] @ refresh Jit's on/off switch
+#else
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ EXPORT_PC() @ need for precise GC
+#endif
+ bl dvmCheckSuspendPending @ do full check, suspend if necessary
+ ldmfd sp!, {r0, lr} @ restore r0 and lr
+
+ /*
+ * Reload the debugger/profiler enable flags. We're checking to see
+ * if either of these got set while we were suspended.
+ *
+ * We can't really avoid the #ifdefs here, because the fields don't
+ * exist when the feature is disabled.
+ */
+ ldr r1, [rGLUE, #offGlue_pDebuggerActive] @ r1<- &debuggerActive
+ cmp r1, #0 @ debugger enabled?
+ ldrneb r1, [r1] @ yes, r1<- debuggerActive (boolean)
+ ldr r2, [rGLUE, #offGlue_pActiveProfilers] @ r2<- &activeProfilers
+ ldr r2, [r2] @ r2<- activeProfilers (int)
+
+ orrs r1, r1, r2
+ beq 2f
+
+1: @ debugger/profiler enabled, bail out; glue->entryPoint was set above
+ str r0, [rGLUE, #offGlue_entryPoint] @ store r0, need for debug/prof
+ add rPC, rPC, r9 @ update rPC
+ mov r1, #1 @ "want switch" = true
+ b common_gotoBail @ side exit
+
+2:
+ bx lr @ nothing to do, return
+
+
+/*
+ * The equivalent of "goto bail", this calls through the "bail handler".
+ *
+ * State registers will be saved to the "glue" area before bailing.
+ *
+ * On entry:
+ * r1 is "bool changeInterp", indicating if we want to switch to the
+ * other interpreter or just bail all the way out
+ */
+common_gotoBail:
+ SAVE_PC_FP_TO_GLUE() @ export state to "glue"
+ mov r0, rGLUE @ r0<- glue ptr
+ b dvmMterpStdBail @ call(glue, changeInterp)
+
+ @add r1, r1, #1 @ using (boolean+1)
+ @add r0, rGLUE, #offGlue_jmpBuf @ r0<- &glue->jmpBuf
+ @bl _longjmp @ does not return
+ @bl common_abort
+
+
+/*
+ * Common code for method invocation with range.
+ *
+ * On entry:
+ * r0 is "Method* methodToCall", the method we're trying to call
+ */
+common_invokeMethodRange:
+.LinvokeNewRange:
+ @ prepare to copy args to "outs" area of current frame
+ movs r2, rINST, lsr #8 @ r2<- AA (arg count) -- test for zero
+ SAVEAREA_FROM_FP(r10, rFP) @ r10<- stack save area
+ beq .LinvokeArgsDone @ if no args, skip the rest
+ FETCH(r1, 2) @ r1<- CCCC
+
+ @ r0=methodToCall, r1=CCCC, r2=count, r10=outs
+ @ (very few methods have > 10 args; could unroll for common cases)
+ add r3, rFP, r1, lsl #2 @ r3<- &fp[CCCC]
+ sub r10, r10, r2, lsl #2 @ r10<- "outs" area, for call args
+ ldrh r9, [r0, #offMethod_registersSize] @ r9<- methodToCall->regsSize
+1: ldr r1, [r3], #4 @ val = *fp++
+ subs r2, r2, #1 @ count--
+ str r1, [r10], #4 @ *outs++ = val
+ bne 1b @ ...while count != 0
+ ldrh r3, [r0, #offMethod_outsSize] @ r3<- methodToCall->outsSize
+ b .LinvokeArgsDone
+
+/*
+ * Common code for method invocation without range.
+ *
+ * On entry:
+ * r0 is "Method* methodToCall", the method we're trying to call
+ */
+common_invokeMethodNoRange:
+.LinvokeNewNoRange:
+ @ prepare to copy args to "outs" area of current frame
+ movs r2, rINST, lsr #12 @ r2<- B (arg count) -- test for zero
+ SAVEAREA_FROM_FP(r10, rFP) @ r10<- stack save area
+ FETCH(r1, 2) @ r1<- GFED (load here to hide latency)
+ ldrh r9, [r0, #offMethod_registersSize] @ r9<- methodToCall->regsSize
+ ldrh r3, [r0, #offMethod_outsSize] @ r3<- methodToCall->outsSize
+ beq .LinvokeArgsDone
+
+ @ r0=methodToCall, r1=GFED, r3=outSize, r2=count, r9=regSize, r10=outs
+.LinvokeNonRange:
+ rsb r2, r2, #5 @ r2<- 5-r2
+ add pc, pc, r2, lsl #4 @ computed goto, 4 instrs each
+ bl common_abort @ (skipped due to ARM prefetch)
+5: and ip, rINST, #0x0f00 @ isolate A
+ ldr r2, [rFP, ip, lsr #6] @ r2<- vA (shift right 8, left 2)
+ mov r0, r0 @ nop
+ str r2, [r10, #-4]! @ *--outs = vA
+4: and ip, r1, #0xf000 @ isolate G
+ ldr r2, [rFP, ip, lsr #10] @ r2<- vG (shift right 12, left 2)
+ mov r0, r0 @ nop
+ str r2, [r10, #-4]! @ *--outs = vG
+3: and ip, r1, #0x0f00 @ isolate F
+ ldr r2, [rFP, ip, lsr #6] @ r2<- vF
+ mov r0, r0 @ nop
+ str r2, [r10, #-4]! @ *--outs = vF
+2: and ip, r1, #0x00f0 @ isolate E
+ ldr r2, [rFP, ip, lsr #2] @ r2<- vE
+ mov r0, r0 @ nop
+ str r2, [r10, #-4]! @ *--outs = vE
+1: and ip, r1, #0x000f @ isolate D
+ ldr r2, [rFP, ip, lsl #2] @ r2<- vD
+ mov r0, r0 @ nop
+ str r2, [r10, #-4]! @ *--outs = vD
+0: @ fall through to .LinvokeArgsDone
+
+.LinvokeArgsDone: @ r0=methodToCall, r3=outSize, r9=regSize
+ ldr r2, [r0, #offMethod_insns] @ r2<- method->insns
+ ldr rINST, [r0, #offMethod_clazz] @ rINST<- method->clazz
+ @ find space for the new stack frame, check for overflow
+ SAVEAREA_FROM_FP(r1, rFP) @ r1<- stack save area
+ sub r1, r1, r9, lsl #2 @ r1<- newFp (old savearea - regsSize)
+ SAVEAREA_FROM_FP(r10, r1) @ r10<- newSaveArea
+@ bl common_dumpRegs
+ ldr r9, [rGLUE, #offGlue_interpStackEnd] @ r9<- interpStackEnd
+ sub r3, r10, r3, lsl #2 @ r3<- bottom (newsave - outsSize)
+ cmp r3, r9 @ bottom < interpStackEnd?
+ ldr r3, [r0, #offMethod_accessFlags] @ r3<- methodToCall->accessFlags
+ blo .LstackOverflow @ yes, this frame will overflow stack
+
+ @ set up newSaveArea
+#ifdef EASY_GDB
+ SAVEAREA_FROM_FP(ip, rFP) @ ip<- stack save area
+ str ip, [r10, #offStackSaveArea_prevSave]
+#endif
+ str rFP, [r10, #offStackSaveArea_prevFrame]
+ str rPC, [r10, #offStackSaveArea_savedPc]
+#if defined(WITH_JIT)
+ mov r9, #0
+ str r9, [r10, #offStackSaveArea_returnAddr]
+#endif
+ str r0, [r10, #offStackSaveArea_method]
+ tst r3, #ACC_NATIVE
+ bne .LinvokeNative
+
+ /*
+ stmfd sp!, {r0-r3}
+ bl common_printNewline
+ mov r0, rFP
+ mov r1, #0
+ bl dvmDumpFp
+ ldmfd sp!, {r0-r3}
+ stmfd sp!, {r0-r3}
+ mov r0, r1
+ mov r1, r10
+ bl dvmDumpFp
+ bl common_printNewline
+ ldmfd sp!, {r0-r3}
+ */
+
+ ldrh r9, [r2] @ r9 <- load INST from new PC
+ ldr r3, [rINST, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+ mov rPC, r2 @ publish new rPC
+ ldr r2, [rGLUE, #offGlue_self] @ r2<- glue->self
+
+ @ Update "glue" values for the new method
+ @ r0=methodToCall, r1=newFp, r2=self, r3=newMethodClass, r9=newINST
+ str r0, [rGLUE, #offGlue_method] @ glue->method = methodToCall
+ str r3, [rGLUE, #offGlue_methodClassDex] @ glue->methodClassDex = ...
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ mov rFP, r1 @ fp = newFp
+ GET_PREFETCHED_OPCODE(ip, r9) @ extract prefetched opcode from r9
+ mov rINST, r9 @ publish new rINST
+ str r1, [r2, #offThread_curFrame] @ self->curFrame = newFp
+ cmp r0,#0
+ bne common_updateProfile
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ mov rFP, r1 @ fp = newFp
+ GET_PREFETCHED_OPCODE(ip, r9) @ extract prefetched opcode from r9
+ mov rINST, r9 @ publish new rINST
+ str r1, [r2, #offThread_curFrame] @ self->curFrame = newFp
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+.LinvokeNative:
+ @ Prep for the native call
+ @ r0=methodToCall, r1=newFp, r10=newSaveArea
+ ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
+ ldr r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
+ str r1, [r3, #offThread_curFrame] @ self->curFrame = newFp
+ str r9, [r10, #offStackSaveArea_localRefCookie] @newFp->localRefCookie=top
+ mov r9, r3 @ r9<- glue->self (preserve)
+
+ mov r2, r0 @ r2<- methodToCall
+ mov r0, r1 @ r0<- newFp (points to args)
+ add r1, rGLUE, #offGlue_retval @ r1<- &retval
+
+#ifdef ASSIST_DEBUGGER
+ /* insert fake function header to help gdb find the stack frame */
+ b .Lskip
+ .type dalvik_mterp, %function
+dalvik_mterp:
+ .fnstart
+ MTERP_ENTRY1
+ MTERP_ENTRY2
+.Lskip:
+#endif
+
+ @mov lr, pc @ set return addr
+ @ldr pc, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
+ LDR_PC_LR "[r2, #offMethod_nativeFunc]"
+
+#if defined(WITH_JIT)
+ ldr r3, [rGLUE, #offGlue_ppJitProfTable] @ Refresh Jit's on/off status
+#endif
+
+ @ native return; r9=self, r10=newSaveArea
+ @ equivalent to dvmPopJniLocals
+ ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved top
+ ldr r1, [r9, #offThread_exception] @ check for exception
+#if defined(WITH_JIT)
+ ldr r3, [r3] @ r3 <- gDvmJit.pProfTable
+#endif
+ str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
+ cmp r1, #0 @ null?
+ str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
+#if defined(WITH_JIT)
+ str r3, [rGLUE, #offGlue_pJitProfTable] @ refresh cached on/off switch
+#endif
+ bne common_exceptionThrown @ no, handle exception
+
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+.LstackOverflow: @ r0=methodToCall
+ mov r1, r0 @ r1<- methodToCall
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- self
+ bl dvmHandleStackOverflow
+ b common_exceptionThrown
+#ifdef ASSIST_DEBUGGER
+ .fnend
+#endif
+
+
+ /*
+ * Common code for method invocation, calling through "glue code".
+ *
+ * TODO: now that we have range and non-range invoke handlers, this
+ * needs to be split into two. Maybe just create entry points
+ * that set r9 and jump here?
+ *
+ * On entry:
+ * r0 is "Method* methodToCall", the method we're trying to call
+ * r9 is "bool methodCallRange", indicating if this is a /range variant
+ */
+ .if 0
+.LinvokeOld:
+ sub sp, sp, #8 @ space for args + pad
+ FETCH(ip, 2) @ ip<- FEDC or CCCC
+ mov r2, r0 @ A2<- methodToCall
+ mov r0, rGLUE @ A0<- glue
+ SAVE_PC_FP_TO_GLUE() @ export state to "glue"
+ mov r1, r9 @ A1<- methodCallRange
+ mov r3, rINST, lsr #8 @ A3<- AA
+ str ip, [sp, #0] @ A4<- ip
+ bl dvmMterp_invokeMethod @ call the C invokeMethod
+ add sp, sp, #8 @ remove arg area
+ b common_resumeAfterGlueCall @ continue to next instruction
+ .endif
+
+
+
+/*
+ * Common code for handling a return instruction.
+ *
+ * This does not return.
+ */
+common_returnFromMethod:
+.LreturnNew:
+ mov r0, #kInterpEntryReturn
+ mov r9, #0
+ bl common_periodicChecks
+
+ SAVEAREA_FROM_FP(r0, rFP) @ r0<- saveArea (old)
+ ldr rFP, [r0, #offStackSaveArea_prevFrame] @ fp = saveArea->prevFrame
+ ldr r9, [r0, #offStackSaveArea_savedPc] @ r9 = saveArea->savedPc
+ ldr r2, [rFP, #(offStackSaveArea_method - sizeofStackSaveArea)]
+ @ r2<- method we're returning to
+ ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
+ cmp r2, #0 @ is this a break frame?
+ ldrne r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+ mov r1, #0 @ "want switch" = false
+ beq common_gotoBail @ break frame, bail out completely
+
+ PREFETCH_ADVANCE_INST(rINST, r9, 3) @ advance r9, update new rINST
+ str r2, [rGLUE, #offGlue_method]@ glue->method = newSave->method
+ ldr r1, [r10, #offClassObject_pDvmDex] @ r1<- method->clazz->pDvmDex
+ str rFP, [r3, #offThread_curFrame] @ self->curFrame = fp
+#if defined(WITH_JIT)
+ ldr r10, [r0, #offStackSaveArea_returnAddr] @ r10 = saveArea->returnAddr
+ mov rPC, r9 @ publish new rPC
+ str r1, [rGLUE, #offGlue_methodClassDex]
+ str r10, [r3, #offThread_inJitCodeCache] @ may return to JIT'ed land
+ cmp r10, #0 @ caller is compiled code
+ blxne r10
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ mov rPC, r9 @ publish new rPC
+ str r1, [rGLUE, #offGlue_methodClassDex]
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+ /*
+ * Return handling, calls through "glue code".
+ */
+ .if 0
+.LreturnOld:
+ SAVE_PC_FP_TO_GLUE() @ export state
+ mov r0, rGLUE @ arg to function
+ bl dvmMterp_returnFromMethod
+ b common_resumeAfterGlueCall
+ .endif
+
+
+/*
+ * Somebody has thrown an exception. Handle it.
+ *
+ * If the exception processing code returns to us (instead of falling
+ * out of the interpreter), continue with whatever the next instruction
+ * now happens to be.
+ *
+ * This does not return.
+ */
+ .global dvmMterpCommonExceptionThrown
+dvmMterpCommonExceptionThrown:
+common_exceptionThrown:
+.LexceptionNew:
+ mov r0, #kInterpEntryThrow
+ mov r9, #0
+ bl common_periodicChecks
+
+ ldr r10, [rGLUE, #offGlue_self] @ r10<- glue->self
+ ldr r9, [r10, #offThread_exception] @ r9<- self->exception
+ mov r1, r10 @ r1<- self
+ mov r0, r9 @ r0<- exception
+ bl dvmAddTrackedAlloc @ don't let the exception be GCed
+ mov r3, #0 @ r3<- NULL
+ str r3, [r10, #offThread_exception] @ self->exception = NULL
+
+ /* set up args and a local for "&fp" */
+ /* (str sp, [sp, #-4]! would be perfect here, but is discouraged) */
+ str rFP, [sp, #-4]! @ *--sp = fp
+ mov ip, sp @ ip<- &fp
+ mov r3, #0 @ r3<- false
+ str ip, [sp, #-4]! @ *--sp = &fp
+ ldr r1, [rGLUE, #offGlue_method] @ r1<- glue->method
+ mov r0, r10 @ r0<- self
+ ldr r1, [r1, #offMethod_insns] @ r1<- method->insns
+ mov r2, r9 @ r2<- exception
+ sub r1, rPC, r1 @ r1<- pc - method->insns
+ mov r1, r1, asr #1 @ r1<- offset in code units
+
+ /* call, r0 gets catchRelPc (a code-unit offset) */
+ bl dvmFindCatchBlock @ call(self, relPc, exc, scan?, &fp)
+
+ /* fix earlier stack overflow if necessary; may trash rFP */
+ ldrb r1, [r10, #offThread_stackOverflowed]
+ cmp r1, #0 @ did we overflow earlier?
+ beq 1f @ no, skip ahead
+ mov rFP, r0 @ save relPc result in rFP
+ mov r0, r10 @ r0<- self
+ mov r1, r9 @ r1<- exception
+ bl dvmCleanupStackOverflow @ call(self)
+ mov r0, rFP @ restore result
+1:
+
+ /* update frame pointer and check result from dvmFindCatchBlock */
+ ldr rFP, [sp, #4] @ retrieve the updated rFP
+ cmp r0, #0 @ is catchRelPc < 0?
+ add sp, sp, #8 @ restore stack
+ bmi .LnotCaughtLocally
+
+ /* adjust locals to match self->curFrame and updated PC */
+ SAVEAREA_FROM_FP(r1, rFP) @ r1<- new save area
+ ldr r1, [r1, #offStackSaveArea_method] @ r1<- new method
+ str r1, [rGLUE, #offGlue_method] @ glue->method = new method
+ ldr r2, [r1, #offMethod_clazz] @ r2<- method->clazz
+ ldr r3, [r1, #offMethod_insns] @ r3<- method->insns
+ ldr r2, [r2, #offClassObject_pDvmDex] @ r2<- method->clazz->pDvmDex
+ add rPC, r3, r0, asl #1 @ rPC<- method->insns + catchRelPc
+ str r2, [rGLUE, #offGlue_methodClassDex] @ glue->pDvmDex = meth...
+
+ /* release the tracked alloc on the exception */
+ mov r0, r9 @ r0<- exception
+ mov r1, r10 @ r1<- self
+ bl dvmReleaseTrackedAlloc @ release the exception
+
+ /* restore the exception if the handler wants it */
+ FETCH_INST() @ load rINST from rPC
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ cmp ip, #OP_MOVE_EXCEPTION @ is it "move-exception"?
+ streq r9, [r10, #offThread_exception] @ yes, restore the exception
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+.LnotCaughtLocally: @ r9=exception, r10=self
+ /* fix stack overflow if necessary */
+ ldrb r1, [r10, #offThread_stackOverflowed]
+ cmp r1, #0 @ did we overflow earlier?
+ movne r0, r10 @ if yes: r0<- self
+ movne r1, r9 @ if yes: r1<- exception
+ blne dvmCleanupStackOverflow @ if yes: call(self)
+
+ @ may want to show "not caught locally" debug messages here
+#if DVM_SHOW_EXCEPTION >= 2
+ /* call __android_log_print(prio, tag, format, ...) */
+ /* "Exception %s from %s:%d not caught locally" */
+ @ dvmLineNumFromPC(method, pc - method->insns)
+ ldr r0, [rGLUE, #offGlue_method]
+ ldr r1, [r0, #offMethod_insns]
+ sub r1, rPC, r1
+ asr r1, r1, #1
+ bl dvmLineNumFromPC
+ str r0, [sp, #-4]!
+ @ dvmGetMethodSourceFile(method)
+ ldr r0, [rGLUE, #offGlue_method]
+ bl dvmGetMethodSourceFile
+ str r0, [sp, #-4]!
+ @ exception->clazz->descriptor
+ ldr r3, [r9, #offObject_clazz]
+ ldr r3, [r3, #offClassObject_descriptor]
+ @
+ ldr r2, strExceptionNotCaughtLocally
+ ldr r1, strLogTag
+ mov r0, #3 @ LOG_DEBUG
+ bl __android_log_print
+#endif
+ str r9, [r10, #offThread_exception] @ restore exception
+ mov r0, r9 @ r0<- exception
+ mov r1, r10 @ r1<- self
+ bl dvmReleaseTrackedAlloc @ release the exception
+ mov r1, #0 @ "want switch" = false
+ b common_gotoBail @ bail out
+
+
+ /*
+ * Exception handling, calls through "glue code".
+ */
+ .if 0
+.LexceptionOld:
+ SAVE_PC_FP_TO_GLUE() @ export state
+ mov r0, rGLUE @ arg to function
+ bl dvmMterp_exceptionThrown
+ b common_resumeAfterGlueCall
+ .endif
+
+
+/*
+ * After returning from a "glued" function, pull out the updated
+ * values and start executing at the next instruction.
+ */
+common_resumeAfterGlueCall:
+ LOAD_PC_FP_FROM_GLUE() @ pull rPC and rFP out of glue
+ FETCH_INST() @ load rINST from rPC
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/*
+ * Invalid array index.
+ */
+common_errArrayIndex:
+ EXPORT_PC()
+ ldr r0, strArrayIndexException
+ mov r1, #0
+ bl dvmThrowException
+ b common_exceptionThrown
+
+/*
+ * Invalid array value.
+ */
+common_errArrayStore:
+ EXPORT_PC()
+ ldr r0, strArrayStoreException
+ mov r1, #0
+ bl dvmThrowException
+ b common_exceptionThrown
+
+/*
+ * Integer divide or mod by zero.
+ */
+common_errDivideByZero:
+ EXPORT_PC()
+ ldr r0, strArithmeticException
+ ldr r1, strDivideByZero
+ bl dvmThrowException
+ b common_exceptionThrown
+
+/*
+ * Attempt to allocate an array with a negative size.
+ */
+common_errNegativeArraySize:
+ EXPORT_PC()
+ ldr r0, strNegativeArraySizeException
+ mov r1, #0
+ bl dvmThrowException
+ b common_exceptionThrown
+
+/*
+ * Invocation of a non-existent method.
+ */
+common_errNoSuchMethod:
+ EXPORT_PC()
+ ldr r0, strNoSuchMethodError
+ mov r1, #0
+ bl dvmThrowException
+ b common_exceptionThrown
+
+/*
+ * We encountered a null object when we weren't expecting one. We
+ * export the PC, throw a NullPointerException, and goto the exception
+ * processing code.
+ */
+common_errNullObject:
+ EXPORT_PC()
+ ldr r0, strNullPointerException
+ mov r1, #0
+ bl dvmThrowException
+ b common_exceptionThrown
+
+/*
+ * For debugging, cause an immediate fault. The source address will
+ * be in lr (use a bl instruction to jump here).
+ */
+common_abort:
+ ldr pc, .LdeadFood
+.LdeadFood:
+ .word 0xdeadf00d
+
+/*
+ * Spit out a "we were here", preserving all registers. (The attempt
+ * to save ip won't work, but we need to save an even number of
+ * registers for EABI 64-bit stack alignment.)
+ */
+ .macro SQUEAK num
+common_squeak\num:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ ldr r0, strSqueak
+ mov r1, #\num
+ bl printf
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+ .endm
+
+ SQUEAK 0
+ SQUEAK 1
+ SQUEAK 2
+ SQUEAK 3
+ SQUEAK 4
+ SQUEAK 5
+
+/*
+ * Spit out the number in r0, preserving registers.
+ */
+common_printNum:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ mov r1, r0
+ ldr r0, strSqueak
+ bl printf
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+
+/*
+ * Print a newline, preserving registers.
+ */
+common_printNewline:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ ldr r0, strNewline
+ bl printf
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+
+ /*
+ * Print the 32-bit quantity in r0 as a hex value, preserving registers.
+ */
+common_printHex:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ mov r1, r0
+ ldr r0, strPrintHex
+ bl printf
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+
+/*
+ * Print the 64-bit quantity in r0-r1, preserving registers.
+ */
+common_printLong:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ mov r3, r1
+ mov r2, r0
+ ldr r0, strPrintLong
+ bl printf
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+
+/*
+ * Print full method info. Pass the Method* in r0. Preserves regs.
+ */
+common_printMethod:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bl dvmMterpPrintMethod
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+
+/*
+ * Call a C helper function that dumps regs and possibly some
+ * additional info. Requires the C function to be compiled in.
+ */
+ .if 0
+common_dumpRegs:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bl dvmMterpDumpArmRegs
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+ .endif
+
+#if 0
+/*
+ * Experiment on VFP mode.
+ *
+ * uint32_t setFPSCR(uint32_t val, uint32_t mask)
+ *
+ * Updates the bits specified by "mask", setting them to the values in "val".
+ */
+setFPSCR:
+ and r0, r0, r1 @ make sure no stray bits are set
+ fmrx r2, fpscr @ get VFP reg
+ mvn r1, r1 @ bit-invert mask
+ and r2, r2, r1 @ clear masked bits
+ orr r2, r2, r0 @ set specified bits
+ fmxr fpscr, r2 @ set VFP reg
+ mov r0, r2 @ return new value
+ bx lr
+
+ .align 2
+ .global dvmConfigureFP
+ .type dvmConfigureFP, %function
+dvmConfigureFP:
+ stmfd sp!, {ip, lr}
+ /* 0x03000000 sets DN/FZ */
+ /* 0x00009f00 clears the six exception enable flags */
+ bl common_squeak0
+ mov r0, #0x03000000 @ r0<- 0x03000000
+ add r1, r0, #0x9f00 @ r1<- 0x03009f00
+ bl setFPSCR
+ ldmfd sp!, {ip, pc}
+#endif
+
+
+/*
+ * String references, must be close to the code that uses them.
+ */
+ .align 2
+strArithmeticException:
+ .word .LstrArithmeticException
+strArrayIndexException:
+ .word .LstrArrayIndexException
+strArrayStoreException:
+ .word .LstrArrayStoreException
+strDivideByZero:
+ .word .LstrDivideByZero
+strNegativeArraySizeException:
+ .word .LstrNegativeArraySizeException
+strNoSuchMethodError:
+ .word .LstrNoSuchMethodError
+strNullPointerException:
+ .word .LstrNullPointerException
+
+strLogTag:
+ .word .LstrLogTag
+strExceptionNotCaughtLocally:
+ .word .LstrExceptionNotCaughtLocally
+
+strNewline:
+ .word .LstrNewline
+strSqueak:
+ .word .LstrSqueak
+strPrintHex:
+ .word .LstrPrintHex
+strPrintLong:
+ .word .LstrPrintLong
+
+/*
+ * Zero-terminated ASCII string data.
+ *
+ * On ARM we have two choices: do like gcc does, and LDR from a .word
+ * with the address, or use an ADR pseudo-op to get the address
+ * directly. ADR saves 4 bytes and an indirection, but it's using a
+ * PC-relative addressing mode and hence has a limited range, which
+ * makes it not work well with mergeable string sections.
+ */
+ .section .rodata.str1.4,"aMS",%progbits,1
+
+.LstrBadEntryPoint:
+ .asciz "Bad entry point %d\n"
+.LstrArithmeticException:
+ .asciz "Ljava/lang/ArithmeticException;"
+.LstrArrayIndexException:
+ .asciz "Ljava/lang/ArrayIndexOutOfBoundsException;"
+.LstrArrayStoreException:
+ .asciz "Ljava/lang/ArrayStoreException;"
+.LstrClassCastException:
+ .asciz "Ljava/lang/ClassCastException;"
+.LstrDivideByZero:
+ .asciz "divide by zero"
+.LstrFilledNewArrayNotImpl:
+ .asciz "filled-new-array only implemented for objects and 'int'"
+.LstrInternalError:
+ .asciz "Ljava/lang/InternalError;"
+.LstrInstantiationError:
+ .asciz "Ljava/lang/InstantiationError;"
+.LstrNegativeArraySizeException:
+ .asciz "Ljava/lang/NegativeArraySizeException;"
+.LstrNoSuchMethodError:
+ .asciz "Ljava/lang/NoSuchMethodError;"
+.LstrNullPointerException:
+ .asciz "Ljava/lang/NullPointerException;"
+
+.LstrLogTag:
+ .asciz "mterp"
+.LstrExceptionNotCaughtLocally:
+ .asciz "Exception %s from %s:%d not caught locally\n"
+
+.LstrNewline:
+ .asciz "\n"
+.LstrSqueak:
+ .asciz "<%d>"
+.LstrPrintHex:
+ .asciz "<0x%x>"
+.LstrPrintLong:
+ .asciz "<%lld>"
+
diff --git a/vm/mterp/out/InterpAsm-armv5te-vfp.S b/vm/mterp/out/InterpAsm-armv5te-vfp.S
new file mode 100644
index 0000000..2637e59
--- /dev/null
+++ b/vm/mterp/out/InterpAsm-armv5te-vfp.S
@@ -0,0 +1,10615 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'armv5te-vfp'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: armv5te/header.S */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * ARMv5 definitions and declarations.
+ */
+
+/*
+ARM EABI general notes:
+
+r0-r3 hold first 4 args to a method; they are not preserved across method calls
+r4-r8 are available for general use
+r9 is given special treatment in some situations, but not for us
+r10 (sl) seems to be generally available
+r11 (fp) is used by gcc (unless -fomit-frame-pointer is set)
+r12 (ip) is scratch -- not preserved across method calls
+r13 (sp) should be managed carefully in case a signal arrives
+r14 (lr) must be preserved
+r15 (pc) can be tinkered with directly
+
+r0 holds returns of <= 4 bytes
+r0-r1 hold returns of 8 bytes, low word in r0
+
+Callee must save/restore r4+ (except r12) if it modifies them. If VFP
+is present, registers s16-s31 (a/k/a d8-d15, a/k/a q4-q7) must be preserved,
+s0-s15 (d0-d7, q0-a3) do not need to be.
+
+Stack is "full descending". Only the arguments that don't fit in the first 4
+registers are placed on the stack. "sp" points at the first stacked argument
+(i.e. the 5th arg).
+
+VFP: single-precision results in s0, double-precision results in d0.
+
+In the EABI, "sp" must be 64-bit aligned on entry to a function, and any
+64-bit quantities (long long, double) must be 64-bit aligned.
+*/
+
+/*
+Mterp and ARM notes:
+
+The following registers have fixed assignments:
+
+ reg nick purpose
+ r4 rPC interpreted program counter, used for fetching instructions
+ r5 rFP interpreted frame pointer, used for accessing locals and args
+ r6 rGLUE MterpGlue pointer
+ r7 rINST first 16-bit code unit of current instruction
+ r8 rIBASE interpreted instruction base pointer, used for computed goto
+
+Macros are provided for common operations. Each macro MUST emit only
+one instruction to make instruction-counting easier. They MUST NOT alter
+unspecified registers or condition codes.
+*/
+
+/* single-purpose registers, given names for clarity */
+#define rPC r4
+#define rFP r5
+#define rGLUE r6
+#define rINST r7
+#define rIBASE r8
+
+/* save/restore the PC and/or FP from the glue struct */
+#define LOAD_PC_FROM_GLUE() ldr rPC, [rGLUE, #offGlue_pc]
+#define SAVE_PC_TO_GLUE() str rPC, [rGLUE, #offGlue_pc]
+#define LOAD_FP_FROM_GLUE() ldr rFP, [rGLUE, #offGlue_fp]
+#define SAVE_FP_TO_GLUE() str rFP, [rGLUE, #offGlue_fp]
+#define LOAD_PC_FP_FROM_GLUE() ldmia rGLUE, {rPC, rFP}
+#define SAVE_PC_FP_TO_GLUE() stmia rGLUE, {rPC, rFP}
+
+/*
+ * "export" the PC to the stack frame, f/b/o future exception objects. Must
+ * be done *before* something calls dvmThrowException.
+ *
+ * In C this is "SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc", i.e.
+ * fp - sizeof(StackSaveArea) + offsetof(SaveArea, xtra.currentPc)
+ *
+ * It's okay to do this more than once.
+ */
+#define EXPORT_PC() \
+ str rPC, [rFP, #(-sizeofStackSaveArea + offStackSaveArea_currentPc)]
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+#define SAVEAREA_FROM_FP(_reg, _fpreg) \
+ sub _reg, _fpreg, #sizeofStackSaveArea
+
+/*
+ * Fetch the next instruction from rPC into rINST. Does not advance rPC.
+ */
+#define FETCH_INST() ldrh rINST, [rPC]
+
+/*
+ * Fetch the next instruction from the specified offset. Advances rPC
+ * to point to the next instruction. "_count" is in 16-bit code units.
+ *
+ * Because of the limited size of immediate constants on ARM, this is only
+ * suitable for small forward movements (i.e. don't try to implement "goto"
+ * with this).
+ *
+ * This must come AFTER anything that can throw an exception, or the
+ * exception catch may miss. (This also implies that it must come after
+ * EXPORT_PC().)
+ */
+#define FETCH_ADVANCE_INST(_count) ldrh rINST, [rPC, #(_count*2)]!
+
+/*
+ * The operation performed here is similar to FETCH_ADVANCE_INST, except the
+ * src and dest registers are parameterized (not hard-wired to rPC and rINST).
+ */
+#define PREFETCH_ADVANCE_INST(_dreg, _sreg, _count) \
+ ldrh _dreg, [_sreg, #(_count*2)]!
+
+/*
+ * Fetch the next instruction from an offset specified by _reg. Updates
+ * rPC to point to the next instruction. "_reg" must specify the distance
+ * in bytes, *not* 16-bit code units, and may be a signed value.
+ *
+ * We want to write "ldrh rINST, [rPC, _reg, lsl #2]!", but some of the
+ * bits that hold the shift distance are used for the half/byte/sign flags.
+ * In some cases we can pre-double _reg for free, so we require a byte offset
+ * here.
+ */
+#define FETCH_ADVANCE_INST_RB(_reg) ldrh rINST, [rPC, _reg]!
+
+/*
+ * Fetch a half-word code unit from an offset past the current PC. The
+ * "_count" value is in 16-bit code units. Does not advance rPC.
+ *
+ * The "_S" variant works the same but treats the value as signed.
+ */
+#define FETCH(_reg, _count) ldrh _reg, [rPC, #(_count*2)]
+#define FETCH_S(_reg, _count) ldrsh _reg, [rPC, #(_count*2)]
+
+/*
+ * Fetch one byte from an offset past the current PC. Pass in the same
+ * "_count" as you would for FETCH, and an additional 0/1 indicating which
+ * byte of the halfword you want (lo/hi).
+ */
+#define FETCH_B(_reg, _count, _byte) ldrb _reg, [rPC, #(_count*2+_byte)]
+
+/*
+ * Put the instruction's opcode field into the specified register.
+ */
+#define GET_INST_OPCODE(_reg) and _reg, rINST, #255
+
+/*
+ * Put the prefetched instruction's opcode field into the specified register.
+ */
+#define GET_PREFETCHED_OPCODE(_oreg, _ireg) and _oreg, _ireg, #255
+
+/*
+ * Begin executing the opcode in _reg. Because this only jumps within the
+ * interpreter, we don't have to worry about pre-ARMv5 THUMB interwork.
+ */
+#define GOTO_OPCODE(_reg) add pc, rIBASE, _reg, lsl #6
+#define GOTO_OPCODE_IFEQ(_reg) addeq pc, rIBASE, _reg, lsl #6
+#define GOTO_OPCODE_IFNE(_reg) addne pc, rIBASE, _reg, lsl #6
+
+/*
+ * Get/set the 32-bit value from a Dalvik register.
+ */
+#define GET_VREG(_reg, _vreg) ldr _reg, [rFP, _vreg, lsl #2]
+#define SET_VREG(_reg, _vreg) str _reg, [rFP, _vreg, lsl #2]
+
+#if defined(WITH_JIT)
+#define GET_JIT_PROF_TABLE(_reg) ldr _reg,[rGLUE,#offGlue_pJitProfTable]
+#define GET_JIT_THRESHOLD(_reg) ldr _reg,[rGLUE,#offGlue_jitThreshold]
+#endif
+
+/*
+ * Convert a virtual register index into an address.
+ */
+#define VREG_INDEX_TO_ADDR(_reg, _vreg) \
+ add _reg, rFP, _vreg, lsl #2
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../common/asm-constants.h"
+
+#if defined(WITH_JIT)
+#include "../common/jit-config.h"
+#endif
+
+/* File: armv5te/platform.S */
+/*
+ * ===========================================================================
+ * CPU-version-specific defines
+ * ===========================================================================
+ */
+
+/*
+ * Macro for "LDR PC,xxx", which is not allowed pre-ARMv5. Essentially a
+ * one-way branch.
+ *
+ * May modify IP. Does not modify LR.
+ */
+.macro LDR_PC source
+ ldr pc, \source
+.endm
+
+/*
+ * Macro for "MOV LR,PC / LDR PC,xxx", which is not allowed pre-ARMv5.
+ * Jump to subroutine.
+ *
+ * May modify IP and LR.
+ */
+.macro LDR_PC_LR source
+ mov lr, pc
+ ldr pc, \source
+.endm
+
+/*
+ * Macro for "LDMFD SP!, {...regs...,PC}".
+ *
+ * May modify IP and LR.
+ */
+.macro LDMFD_PC regs
+ ldmfd sp!, {\regs,pc}
+.endm
+
+/*
+ * Macro for data memory barrier; not meaningful pre-ARMv6K.
+ */
+.macro SMP_DMB
+.endm
+
+/* File: armv5te/entry.S */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Interpreter entry point.
+ */
+
+/*
+ * We don't have formal stack frames, so gdb scans upward in the code
+ * to find the start of the function (a label with the %function type),
+ * and then looks at the next few instructions to figure out what
+ * got pushed onto the stack. From this it figures out how to restore
+ * the registers, including PC, for the previous stack frame. If gdb
+ * sees a non-function label, it stops scanning, so either we need to
+ * have nothing but assembler-local labels between the entry point and
+ * the break, or we need to fake it out.
+ *
+ * When this is defined, we add some stuff to make gdb less confused.
+ */
+#define ASSIST_DEBUGGER 1
+
+ .text
+ .align 2
+ .global dvmMterpStdRun
+ .type dvmMterpStdRun, %function
+
+/*
+ * On entry:
+ * r0 MterpGlue* glue
+ *
+ * This function returns a boolean "changeInterp" value. The return comes
+ * via a call to dvmMterpStdBail().
+ */
+dvmMterpStdRun:
+#define MTERP_ENTRY1 \
+ .save {r4-r10,fp,lr}; \
+ stmfd sp!, {r4-r10,fp,lr} @ save 9 regs
+#define MTERP_ENTRY2 \
+ .pad #4; \
+ sub sp, sp, #4 @ align 64
+
+ .fnstart
+ MTERP_ENTRY1
+ MTERP_ENTRY2
+
+ /* save stack pointer, add magic word for debuggerd */
+ str sp, [r0, #offGlue_bailPtr] @ save SP for eventual return
+
+ /* set up "named" registers, figure out entry point */
+ mov rGLUE, r0 @ set rGLUE
+ ldr r1, [r0, #offGlue_entryPoint] @ enum is 4 bytes in aapcs-EABI
+ LOAD_PC_FP_FROM_GLUE() @ load rPC and rFP from "glue"
+ adr rIBASE, dvmAsmInstructionStart @ set rIBASE
+ cmp r1, #kInterpEntryInstr @ usual case?
+ bne .Lnot_instr @ no, handle it
+
+#if defined(WITH_JIT)
+.LentryInstr:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ /* Entry is always a possible trace start */
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_INST()
+ mov r1, #0 @ prepare the value for the new state
+ str r1, [r10, #offThread_inJitCodeCache] @ back to the interp land
+ cmp r0,#0 @ is profiling disabled?
+#if !defined(WITH_SELF_VERIFICATION)
+ bne common_updateProfile @ profiling is enabled
+#else
+ ldr r2, [r10, #offThread_shadowSpace] @ to find out the jit exit state
+ beq 1f @ profiling is disabled
+ ldr r3, [r2, #offShadowSpace_jitExitState] @ jit exit state
+ cmp r3, #kSVSTraceSelect @ hot trace following?
+ moveq r2,#kJitTSelectRequestHot @ ask for trace selection
+ beq common_selectTrace @ go build the trace
+ cmp r3, #kSVSNoProfile @ don't profile the next instruction?
+ beq 1f @ intrepret the next instruction
+ b common_updateProfile @ collect profiles
+#endif
+1:
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+#else
+ /* start executing the instruction at rPC */
+ FETCH_INST() @ load rINST from rPC
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+.Lnot_instr:
+ cmp r1, #kInterpEntryReturn @ were we returning from a method?
+ beq common_returnFromMethod
+
+.Lnot_return:
+ cmp r1, #kInterpEntryThrow @ were we throwing an exception?
+ beq common_exceptionThrown
+
+#if defined(WITH_JIT)
+.Lnot_throw:
+ ldr r10,[rGLUE, #offGlue_jitResumeNPC]
+ ldr r2,[rGLUE, #offGlue_jitResumeDPC]
+ cmp r1, #kInterpEntryResume @ resuming after Jit single-step?
+ bne .Lbad_arg
+ cmp rPC,r2
+ bne .LentryInstr @ must have branched, don't resume
+#if defined(WITH_SELF_VERIFICATION)
+ @ glue->entryPoint will be set in dvmSelfVerificationSaveState
+ b jitSVShadowRunStart @ re-enter the translation after the
+ @ single-stepped instruction
+ @noreturn
+#endif
+ mov r1, #kInterpEntryInstr
+ str r1, [rGLUE, #offGlue_entryPoint]
+ bx r10 @ re-enter the translation
+#endif
+
+.Lbad_arg:
+ ldr r0, strBadEntryPoint
+ @ r1 holds value of entryPoint
+ bl printf
+ bl dvmAbort
+ .fnend
+
+
+ .global dvmMterpStdBail
+ .type dvmMterpStdBail, %function
+
+/*
+ * Restore the stack pointer and PC from the save point established on entry.
+ * This is essentially the same as a longjmp, but should be cheaper. The
+ * last instruction causes us to return to whoever called dvmMterpStdRun.
+ *
+ * We pushed some registers on the stack in dvmMterpStdRun, then saved
+ * SP and LR. Here we restore SP, restore the registers, and then restore
+ * LR to PC.
+ *
+ * On entry:
+ * r0 MterpGlue* glue
+ * r1 bool changeInterp
+ */
+dvmMterpStdBail:
+ ldr sp, [r0, #offGlue_bailPtr] @ sp<- saved SP
+ mov r0, r1 @ return the changeInterp value
+ add sp, sp, #4 @ un-align 64
+ LDMFD_PC "r4-r10,fp" @ restore 9 regs and return
+
+
+/*
+ * String references.
+ */
+strBadEntryPoint:
+ .word .LstrBadEntryPoint
+
+
+ .global dvmAsmInstructionStart
+ .type dvmAsmInstructionStart, %function
+dvmAsmInstructionStart = .L_OP_NOP
+ .text
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NOP: /* 0x00 */
+/* File: armv5te/OP_NOP.S */
+ FETCH_ADVANCE_INST(1) @ advance to next instr, load rINST
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ GOTO_OPCODE(ip) @ execute it
+
+#ifdef ASSIST_DEBUGGER
+ /* insert fake function header to help gdb find the stack frame */
+ .type dalvik_inst, %function
+dalvik_inst:
+ .fnstart
+ MTERP_ENTRY1
+ MTERP_ENTRY2
+ .fnend
+#endif
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE: /* 0x01 */
+/* File: armv5te/OP_MOVE.S */
+ /* for move, move-object, long-to-int */
+ /* op vA, vB */
+ mov r1, rINST, lsr #12 @ r1<- B from 15:12
+ mov r0, rINST, lsr #8 @ r0<- A from 11:8
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[B]
+ and r0, r0, #15
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ SET_VREG(r2, r0) @ fp[A]<- r2
+ GOTO_OPCODE(ip) @ execute next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_FROM16: /* 0x02 */
+/* File: armv5te/OP_MOVE_FROM16.S */
+ /* for: move/from16, move-object/from16 */
+ /* op vAA, vBBBB */
+ FETCH(r1, 1) @ r1<- BBBB
+ mov r0, rINST, lsr #8 @ r0<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[BBBB]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r0) @ fp[AA]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_16: /* 0x03 */
+/* File: armv5te/OP_MOVE_16.S */
+ /* for: move/16, move-object/16 */
+ /* op vAAAA, vBBBB */
+ FETCH(r1, 2) @ r1<- BBBB
+ FETCH(r0, 1) @ r0<- AAAA
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[BBBB]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r0) @ fp[AAAA]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_WIDE: /* 0x04 */
+/* File: armv5te/OP_MOVE_WIDE.S */
+ /* move-wide vA, vB */
+ /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+ mov r2, rINST, lsr #8 @ r2<- A(+)
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r2, r2, #15
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[A]
+ ldmia r3, {r0-r1} @ r0/r1<- fp[B]
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r2, {r0-r1} @ fp[A]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_WIDE_FROM16: /* 0x05 */
+/* File: armv5te/OP_MOVE_WIDE_FROM16.S */
+ /* move-wide/from16 vAA, vBBBB */
+ /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+ FETCH(r3, 1) @ r3<- BBBB
+ mov r2, rINST, lsr #8 @ r2<- AA
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[BBBB]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[AA]
+ ldmia r3, {r0-r1} @ r0/r1<- fp[BBBB]
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r2, {r0-r1} @ fp[AA]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_WIDE_16: /* 0x06 */
+/* File: armv5te/OP_MOVE_WIDE_16.S */
+ /* move-wide/16 vAAAA, vBBBB */
+ /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+ FETCH(r3, 2) @ r3<- BBBB
+ FETCH(r2, 1) @ r2<- AAAA
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[BBBB]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[AAAA]
+ ldmia r3, {r0-r1} @ r0/r1<- fp[BBBB]
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r2, {r0-r1} @ fp[AAAA]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_OBJECT: /* 0x07 */
+/* File: armv5te/OP_MOVE_OBJECT.S */
+/* File: armv5te/OP_MOVE.S */
+ /* for move, move-object, long-to-int */
+ /* op vA, vB */
+ mov r1, rINST, lsr #12 @ r1<- B from 15:12
+ mov r0, rINST, lsr #8 @ r0<- A from 11:8
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[B]
+ and r0, r0, #15
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ SET_VREG(r2, r0) @ fp[A]<- r2
+ GOTO_OPCODE(ip) @ execute next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_OBJECT_FROM16: /* 0x08 */
+/* File: armv5te/OP_MOVE_OBJECT_FROM16.S */
+/* File: armv5te/OP_MOVE_FROM16.S */
+ /* for: move/from16, move-object/from16 */
+ /* op vAA, vBBBB */
+ FETCH(r1, 1) @ r1<- BBBB
+ mov r0, rINST, lsr #8 @ r0<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[BBBB]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r0) @ fp[AA]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_OBJECT_16: /* 0x09 */
+/* File: armv5te/OP_MOVE_OBJECT_16.S */
+/* File: armv5te/OP_MOVE_16.S */
+ /* for: move/16, move-object/16 */
+ /* op vAAAA, vBBBB */
+ FETCH(r1, 2) @ r1<- BBBB
+ FETCH(r0, 1) @ r0<- AAAA
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[BBBB]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r0) @ fp[AAAA]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_RESULT: /* 0x0a */
+/* File: armv5te/OP_MOVE_RESULT.S */
+ /* for: move-result, move-result-object */
+ /* op vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ ldr r0, [rGLUE, #offGlue_retval] @ r0<- glue->retval.i
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[AA]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_RESULT_WIDE: /* 0x0b */
+/* File: armv5te/OP_MOVE_RESULT_WIDE.S */
+ /* move-result-wide vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ add r3, rGLUE, #offGlue_retval @ r3<- &glue->retval
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[AA]
+ ldmia r3, {r0-r1} @ r0/r1<- retval.j
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r2, {r0-r1} @ fp[AA]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_RESULT_OBJECT: /* 0x0c */
+/* File: armv5te/OP_MOVE_RESULT_OBJECT.S */
+/* File: armv5te/OP_MOVE_RESULT.S */
+ /* for: move-result, move-result-object */
+ /* op vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ ldr r0, [rGLUE, #offGlue_retval] @ r0<- glue->retval.i
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[AA]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_EXCEPTION: /* 0x0d */
+/* File: armv5te/OP_MOVE_EXCEPTION.S */
+ /* move-exception vAA */
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ mov r2, rINST, lsr #8 @ r2<- AA
+ ldr r3, [r0, #offThread_exception] @ r3<- dvmGetException bypass
+ mov r1, #0 @ r1<- 0
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ SET_VREG(r3, r2) @ fp[AA]<- exception obj
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ str r1, [r0, #offThread_exception] @ dvmClearException bypass
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RETURN_VOID: /* 0x0e */
+/* File: armv5te/OP_RETURN_VOID.S */
+ b common_returnFromMethod
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RETURN: /* 0x0f */
+/* File: armv5te/OP_RETURN.S */
+ /*
+ * Return a 32-bit value. Copies the return value into the "glue"
+ * structure, then jumps to the return handler.
+ *
+ * for: return, return-object
+ */
+ /* op vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ GET_VREG(r0, r2) @ r0<- vAA
+ str r0, [rGLUE, #offGlue_retval] @ retval.i <- vAA
+ b common_returnFromMethod
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RETURN_WIDE: /* 0x10 */
+/* File: armv5te/OP_RETURN_WIDE.S */
+ /*
+ * Return a 64-bit value. Copies the return value into the "glue"
+ * structure, then jumps to the return handler.
+ */
+ /* return-wide vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[AA]
+ add r3, rGLUE, #offGlue_retval @ r3<- &glue->retval
+ ldmia r2, {r0-r1} @ r0/r1 <- vAA/vAA+1
+ stmia r3, {r0-r1} @ retval<- r0/r1
+ b common_returnFromMethod
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RETURN_OBJECT: /* 0x11 */
+/* File: armv5te/OP_RETURN_OBJECT.S */
+/* File: armv5te/OP_RETURN.S */
+ /*
+ * Return a 32-bit value. Copies the return value into the "glue"
+ * structure, then jumps to the return handler.
+ *
+ * for: return, return-object
+ */
+ /* op vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ GET_VREG(r0, r2) @ r0<- vAA
+ str r0, [rGLUE, #offGlue_retval] @ retval.i <- vAA
+ b common_returnFromMethod
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_4: /* 0x12 */
+/* File: armv5te/OP_CONST_4.S */
+ /* const/4 vA, #+B */
+ mov r1, rINST, lsl #16 @ r1<- Bxxx0000
+ mov r0, rINST, lsr #8 @ r0<- A+
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ mov r1, r1, asr #28 @ r1<- sssssssB (sign-extended)
+ and r0, r0, #15
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ SET_VREG(r1, r0) @ fp[A]<- r1
+ GOTO_OPCODE(ip) @ execute next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_16: /* 0x13 */
+/* File: armv5te/OP_CONST_16.S */
+ /* const/16 vAA, #+BBBB */
+ FETCH_S(r0, 1) @ r0<- ssssBBBB (sign-extended)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r0, r3) @ vAA<- r0
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST: /* 0x14 */
+/* File: armv5te/OP_CONST.S */
+ /* const vAA, #+BBBBbbbb */
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH(r0, 1) @ r0<- bbbb (low)
+ FETCH(r1, 2) @ r1<- BBBB (high)
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ orr r0, r0, r1, lsl #16 @ r0<- BBBBbbbb
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r3) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_HIGH16: /* 0x15 */
+/* File: armv5te/OP_CONST_HIGH16.S */
+ /* const/high16 vAA, #+BBBB0000 */
+ FETCH(r0, 1) @ r0<- 0000BBBB (zero-extended)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ mov r0, r0, lsl #16 @ r0<- BBBB0000
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r0, r3) @ vAA<- r0
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_WIDE_16: /* 0x16 */
+/* File: armv5te/OP_CONST_WIDE_16.S */
+ /* const-wide/16 vAA, #+BBBB */
+ FETCH_S(r0, 1) @ r0<- ssssBBBB (sign-extended)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ mov r1, r0, asr #31 @ r1<- ssssssss
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_WIDE_32: /* 0x17 */
+/* File: armv5te/OP_CONST_WIDE_32.S */
+ /* const-wide/32 vAA, #+BBBBbbbb */
+ FETCH(r0, 1) @ r0<- 0000bbbb (low)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH_S(r2, 2) @ r2<- ssssBBBB (high)
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ orr r0, r0, r2, lsl #16 @ r0<- BBBBbbbb
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[AA]
+ mov r1, r0, asr #31 @ r1<- ssssssss
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_WIDE: /* 0x18 */
+/* File: armv5te/OP_CONST_WIDE.S */
+ /* const-wide vAA, #+HHHHhhhhBBBBbbbb */
+ FETCH(r0, 1) @ r0<- bbbb (low)
+ FETCH(r1, 2) @ r1<- BBBB (low middle)
+ FETCH(r2, 3) @ r2<- hhhh (high middle)
+ orr r0, r0, r1, lsl #16 @ r0<- BBBBbbbb (low word)
+ FETCH(r3, 4) @ r3<- HHHH (high)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ orr r1, r2, r3, lsl #16 @ r1<- HHHHhhhh (high word)
+ FETCH_ADVANCE_INST(5) @ advance rPC, load rINST
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_WIDE_HIGH16: /* 0x19 */
+/* File: armv5te/OP_CONST_WIDE_HIGH16.S */
+ /* const-wide/high16 vAA, #+BBBB000000000000 */
+ FETCH(r1, 1) @ r1<- 0000BBBB (zero-extended)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ mov r0, #0 @ r0<- 00000000
+ mov r1, r1, lsl #16 @ r1<- BBBB0000
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_STRING: /* 0x1a */
+/* File: armv5te/OP_CONST_STRING.S */
+ /* const/string vAA, String@BBBB */
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- glue->methodClassDex
+ mov r9, rINST, lsr #8 @ r9<- AA
+ ldr r2, [r2, #offDvmDex_pResStrings] @ r2<- dvmDex->pResStrings
+ ldr r0, [r2, r1, lsl #2] @ r0<- pResStrings[BBBB]
+ cmp r0, #0 @ not yet resolved?
+ beq .LOP_CONST_STRING_resolve
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_STRING_JUMBO: /* 0x1b */
+/* File: armv5te/OP_CONST_STRING_JUMBO.S */
+ /* const/string vAA, String@BBBBBBBB */
+ FETCH(r0, 1) @ r0<- bbbb (low)
+ FETCH(r1, 2) @ r1<- BBBB (high)
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- glue->methodClassDex
+ mov r9, rINST, lsr #8 @ r9<- AA
+ ldr r2, [r2, #offDvmDex_pResStrings] @ r2<- dvmDex->pResStrings
+ orr r1, r0, r1, lsl #16 @ r1<- BBBBbbbb
+ ldr r0, [r2, r1, lsl #2] @ r0<- pResStrings[BBBB]
+ cmp r0, #0
+ beq .LOP_CONST_STRING_JUMBO_resolve
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_CLASS: /* 0x1c */
+/* File: armv5te/OP_CONST_CLASS.S */
+ /* const/class vAA, Class@BBBB */
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- glue->methodClassDex
+ mov r9, rINST, lsr #8 @ r9<- AA
+ ldr r2, [r2, #offDvmDex_pResClasses] @ r2<- dvmDex->pResClasses
+ ldr r0, [r2, r1, lsl #2] @ r0<- pResClasses[BBBB]
+ cmp r0, #0 @ not yet resolved?
+ beq .LOP_CONST_CLASS_resolve
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MONITOR_ENTER: /* 0x1d */
+/* File: armv5te/OP_MONITOR_ENTER.S */
+ /*
+ * Synchronize on an object.
+ */
+ /* monitor-enter vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ GET_VREG(r1, r2) @ r1<- vAA (object)
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ cmp r1, #0 @ null object?
+ EXPORT_PC() @ need for precise GC, MONITOR_TRACKING
+ beq common_errNullObject @ null object, throw an exception
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ bl dvmLockObject @ call(self, obj)
+#ifdef WITH_DEADLOCK_PREDICTION /* implies WITH_MONITOR_TRACKING */
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ ldr r1, [r0, #offThread_exception] @ check for exception
+ cmp r1, #0
+ bne common_exceptionThrown @ exception raised, bail out
+#endif
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MONITOR_EXIT: /* 0x1e */
+/* File: armv5te/OP_MONITOR_EXIT.S */
+ /*
+ * Unlock an object.
+ *
+ * Exceptions that occur when unlocking a monitor need to appear as
+ * if they happened at the following instruction. See the Dalvik
+ * instruction spec.
+ */
+ /* monitor-exit vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ EXPORT_PC() @ before fetch: export the PC
+ GET_VREG(r1, r2) @ r1<- vAA (object)
+ cmp r1, #0 @ null object?
+ beq 1f @ yes
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ bl dvmUnlockObject @ r0<- success for unlock(self, obj)
+ cmp r0, #0 @ failed?
+ FETCH_ADVANCE_INST(1) @ before throw: advance rPC, load rINST
+ beq common_exceptionThrown @ yes, exception is pending
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+1:
+ FETCH_ADVANCE_INST(1) @ advance before throw
+ b common_errNullObject
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CHECK_CAST: /* 0x1f */
+/* File: armv5te/OP_CHECK_CAST.S */
+ /*
+ * Check to see if a cast from one class to another is allowed.
+ */
+ /* check-cast vAA, class@BBBB */
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH(r2, 1) @ r2<- BBBB
+ GET_VREG(r9, r3) @ r9<- object
+ ldr r0, [rGLUE, #offGlue_methodClassDex] @ r0<- pDvmDex
+ cmp r9, #0 @ is object null?
+ ldr r0, [r0, #offDvmDex_pResClasses] @ r0<- pDvmDex->pResClasses
+ beq .LOP_CHECK_CAST_okay @ null obj, cast always succeeds
+ ldr r1, [r0, r2, lsl #2] @ r1<- resolved class
+ ldr r0, [r9, #offObject_clazz] @ r0<- obj->clazz
+ cmp r1, #0 @ have we resolved this before?
+ beq .LOP_CHECK_CAST_resolve @ not resolved, do it now
+.LOP_CHECK_CAST_resolved:
+ cmp r0, r1 @ same class (trivial success)?
+ bne .LOP_CHECK_CAST_fullcheck @ no, do full check
+.LOP_CHECK_CAST_okay:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INSTANCE_OF: /* 0x20 */
+/* File: armv5te/OP_INSTANCE_OF.S */
+ /*
+ * Check to see if an object reference is an instance of a class.
+ *
+ * Most common situation is a non-null object, being compared against
+ * an already-resolved class.
+ */
+ /* instance-of vA, vB, class@CCCC */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r3) @ r0<- vB (object)
+ and r9, r9, #15 @ r9<- A
+ cmp r0, #0 @ is object null?
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- pDvmDex
+ beq .LOP_INSTANCE_OF_store @ null obj, not an instance, store r0
+ FETCH(r3, 1) @ r3<- CCCC
+ ldr r2, [r2, #offDvmDex_pResClasses] @ r2<- pDvmDex->pResClasses
+ ldr r1, [r2, r3, lsl #2] @ r1<- resolved class
+ ldr r0, [r0, #offObject_clazz] @ r0<- obj->clazz
+ cmp r1, #0 @ have we resolved this before?
+ beq .LOP_INSTANCE_OF_resolve @ not resolved, do it now
+.LOP_INSTANCE_OF_resolved: @ r0=obj->clazz, r1=resolved class
+ cmp r0, r1 @ same class (trivial success)?
+ beq .LOP_INSTANCE_OF_trivial @ yes, trivial finish
+ b .LOP_INSTANCE_OF_fullcheck @ no, do full check
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ARRAY_LENGTH: /* 0x21 */
+/* File: armv5te/OP_ARRAY_LENGTH.S */
+ /*
+ * Return the length of an array.
+ */
+ mov r1, rINST, lsr #12 @ r1<- B
+ mov r2, rINST, lsr #8 @ r2<- A+
+ GET_VREG(r0, r1) @ r0<- vB (object ref)
+ and r2, r2, #15 @ r2<- A
+ cmp r0, #0 @ is object null?
+ beq common_errNullObject @ yup, fail
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ ldr r3, [r0, #offArrayObject_length] @ r3<- array length
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r3, r2) @ vB<- length
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEW_INSTANCE: /* 0x22 */
+/* File: armv5te/OP_NEW_INSTANCE.S */
+ /*
+ * Create a new instance of a class.
+ */
+ /* new-instance vAA, class@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResClasses] @ r3<- pDvmDex->pResClasses
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved class
+ EXPORT_PC() @ req'd for init, resolve, alloc
+ cmp r0, #0 @ already resolved?
+ beq .LOP_NEW_INSTANCE_resolve @ no, resolve it now
+.LOP_NEW_INSTANCE_resolved: @ r0=class
+ ldrb r1, [r0, #offClassObject_status] @ r1<- ClassStatus enum
+ cmp r1, #CLASS_INITIALIZED @ has class been initialized?
+ bne .LOP_NEW_INSTANCE_needinit @ no, init class now
+.LOP_NEW_INSTANCE_initialized: @ r0=class
+ mov r1, #ALLOC_DONT_TRACK @ flags for alloc call
+ bl dvmAllocObject @ r0<- new object
+ b .LOP_NEW_INSTANCE_finish @ continue
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEW_ARRAY: /* 0x23 */
+/* File: armv5te/OP_NEW_ARRAY.S */
+ /*
+ * Allocate an array of objects, specified with the array class
+ * and a count.
+ *
+ * The verifier guarantees that this is an array class, so we don't
+ * check for it here.
+ */
+ /* new-array vA, vB, class@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ FETCH(r2, 1) @ r2<- CCCC
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ GET_VREG(r1, r0) @ r1<- vB (array length)
+ ldr r3, [r3, #offDvmDex_pResClasses] @ r3<- pDvmDex->pResClasses
+ cmp r1, #0 @ check length
+ ldr r0, [r3, r2, lsl #2] @ r0<- resolved class
+ bmi common_errNegativeArraySize @ negative length, bail
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ req'd for resolve, alloc
+ bne .LOP_NEW_ARRAY_finish @ resolved, continue
+ b .LOP_NEW_ARRAY_resolve @ do resolve now
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FILLED_NEW_ARRAY: /* 0x24 */
+/* File: armv5te/OP_FILLED_NEW_ARRAY.S */
+ /*
+ * Create a new array with elements filled from registers.
+ *
+ * for: filled-new-array, filled-new-array/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResClasses] @ r3<- pDvmDex->pResClasses
+ EXPORT_PC() @ need for resolve and alloc
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved class
+ mov r10, rINST, lsr #8 @ r10<- AA or BA
+ cmp r0, #0 @ already resolved?
+ bne .LOP_FILLED_NEW_ARRAY_continue @ yes, continue on
+8: ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ mov r2, #0 @ r2<- false
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- call(clazz, ref)
+ cmp r0, #0 @ got null?
+ beq common_exceptionThrown @ yes, handle exception
+ b .LOP_FILLED_NEW_ARRAY_continue
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FILLED_NEW_ARRAY_RANGE: /* 0x25 */
+/* File: armv5te/OP_FILLED_NEW_ARRAY_RANGE.S */
+/* File: armv5te/OP_FILLED_NEW_ARRAY.S */
+ /*
+ * Create a new array with elements filled from registers.
+ *
+ * for: filled-new-array, filled-new-array/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResClasses] @ r3<- pDvmDex->pResClasses
+ EXPORT_PC() @ need for resolve and alloc
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved class
+ mov r10, rINST, lsr #8 @ r10<- AA or BA
+ cmp r0, #0 @ already resolved?
+ bne .LOP_FILLED_NEW_ARRAY_RANGE_continue @ yes, continue on
+8: ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ mov r2, #0 @ r2<- false
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- call(clazz, ref)
+ cmp r0, #0 @ got null?
+ beq common_exceptionThrown @ yes, handle exception
+ b .LOP_FILLED_NEW_ARRAY_RANGE_continue
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FILL_ARRAY_DATA: /* 0x26 */
+/* File: armv5te/OP_FILL_ARRAY_DATA.S */
+ /* fill-array-data vAA, +BBBBBBBB */
+ FETCH(r0, 1) @ r0<- bbbb (lo)
+ FETCH(r1, 2) @ r1<- BBBB (hi)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ orr r1, r0, r1, lsl #16 @ r1<- BBBBbbbb
+ GET_VREG(r0, r3) @ r0<- vAA (array object)
+ add r1, rPC, r1, lsl #1 @ r1<- PC + BBBBbbbb*2 (array data off.)
+ EXPORT_PC();
+ bl dvmInterpHandleFillArrayData@ fill the array with predefined data
+ cmp r0, #0 @ 0 means an exception is thrown
+ beq common_exceptionThrown @ has exception
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_THROW: /* 0x27 */
+/* File: armv5te/OP_THROW.S */
+ /*
+ * Throw an exception object in the current thread.
+ */
+ /* throw vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ GET_VREG(r1, r2) @ r1<- vAA (exception object)
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ EXPORT_PC() @ exception handler can throw
+ cmp r1, #0 @ null object?
+ beq common_errNullObject @ yes, throw an NPE instead
+ @ bypass dvmSetException, just store it
+ str r1, [r0, #offThread_exception] @ thread->exception<- obj
+ b common_exceptionThrown
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_GOTO: /* 0x28 */
+/* File: armv5te/OP_GOTO.S */
+ /*
+ * Unconditional branch, 8-bit offset.
+ *
+ * The branch distance is a signed code-unit offset, which we need to
+ * double to get a byte offset.
+ */
+ /* goto +AA */
+ mov r0, rINST, lsl #16 @ r0<- AAxx0000
+ movs r9, r0, asr #24 @ r9<- ssssssAA (sign-extended)
+ mov r9, r9, lsl #1 @ r9<- byte offset
+ bmi common_backwardBranch @ backward branch, do periodic checks
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_GOTO_16: /* 0x29 */
+/* File: armv5te/OP_GOTO_16.S */
+ /*
+ * Unconditional branch, 16-bit offset.
+ *
+ * The branch distance is a signed code-unit offset, which we need to
+ * double to get a byte offset.
+ */
+ /* goto/16 +AAAA */
+ FETCH_S(r0, 1) @ r0<- ssssAAAA (sign-extended)
+ movs r9, r0, asl #1 @ r9<- byte offset, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_GOTO_32: /* 0x2a */
+/* File: armv5te/OP_GOTO_32.S */
+ /*
+ * Unconditional branch, 32-bit offset.
+ *
+ * The branch distance is a signed code-unit offset, which we need to
+ * double to get a byte offset.
+ *
+ * Unlike most opcodes, this one is allowed to branch to itself, so
+ * our "backward branch" test must be "<=0" instead of "<0". The ORRS
+ * instruction doesn't affect the V flag, so we need to clear it
+ * explicitly.
+ */
+ /* goto/32 +AAAAAAAA */
+ FETCH(r0, 1) @ r0<- aaaa (lo)
+ FETCH(r1, 2) @ r1<- AAAA (hi)
+ cmp ip, ip @ (clear V flag during stall)
+ orrs r0, r0, r1, lsl #16 @ r0<- AAAAaaaa, check sign
+ mov r9, r0, asl #1 @ r9<- byte offset
+ ble common_backwardBranch @ backward branch, do periodic checks
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_PACKED_SWITCH: /* 0x2b */
+/* File: armv5te/OP_PACKED_SWITCH.S */
+ /*
+ * Handle a packed-switch or sparse-switch instruction. In both cases
+ * we decode it and hand it off to a helper function.
+ *
+ * We don't really expect backward branches in a switch statement, but
+ * they're perfectly legal, so we check for them here.
+ *
+ * for: packed-switch, sparse-switch
+ */
+ /* op vAA, +BBBB */
+ FETCH(r0, 1) @ r0<- bbbb (lo)
+ FETCH(r1, 2) @ r1<- BBBB (hi)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ orr r0, r0, r1, lsl #16 @ r0<- BBBBbbbb
+ GET_VREG(r1, r3) @ r1<- vAA
+ add r0, rPC, r0, lsl #1 @ r0<- PC + BBBBbbbb*2
+ bl dvmInterpHandlePackedSwitch @ r0<- code-unit branch offset
+ movs r9, r0, asl #1 @ r9<- branch byte offset, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+ beq common_backwardBranch @ (want to use BLE but V is unknown)
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPARSE_SWITCH: /* 0x2c */
+/* File: armv5te/OP_SPARSE_SWITCH.S */
+/* File: armv5te/OP_PACKED_SWITCH.S */
+ /*
+ * Handle a packed-switch or sparse-switch instruction. In both cases
+ * we decode it and hand it off to a helper function.
+ *
+ * We don't really expect backward branches in a switch statement, but
+ * they're perfectly legal, so we check for them here.
+ *
+ * for: packed-switch, sparse-switch
+ */
+ /* op vAA, +BBBB */
+ FETCH(r0, 1) @ r0<- bbbb (lo)
+ FETCH(r1, 2) @ r1<- BBBB (hi)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ orr r0, r0, r1, lsl #16 @ r0<- BBBBbbbb
+ GET_VREG(r1, r3) @ r1<- vAA
+ add r0, rPC, r0, lsl #1 @ r0<- PC + BBBBbbbb*2
+ bl dvmInterpHandleSparseSwitch @ r0<- code-unit branch offset
+ movs r9, r0, asl #1 @ r9<- branch byte offset, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+ beq common_backwardBranch @ (want to use BLE but V is unknown)
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CMPL_FLOAT: /* 0x2d */
+/* File: arm-vfp/OP_CMPL_FLOAT.S */
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * int compare(x, y) {
+ * if (x == y) {
+ * return 0;
+ * } else if (x > y) {
+ * return 1;
+ * } else if (x < y) {
+ * return -1;
+ * } else {
+ * return -1;
+ * }
+ * }
+ */
+ /* op vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ flds s0, [r2] @ s0<- vBB
+ flds s1, [r3] @ s1<- vCC
+ fcmpes s0, s1 @ compare (vBB, vCC)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ mvn r0, #0 @ r0<- -1 (default)
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fmstat @ export status flags
+ movgt r0, #1 @ (greater than) r1<- 1
+ moveq r0, #0 @ (equal) r1<- 0
+ b .LOP_CMPL_FLOAT_finish @ argh
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CMPG_FLOAT: /* 0x2e */
+/* File: arm-vfp/OP_CMPG_FLOAT.S */
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * int compare(x, y) {
+ * if (x == y) {
+ * return 0;
+ * } else if (x < y) {
+ * return -1;
+ * } else if (x > y) {
+ * return 1;
+ * } else {
+ * return 1;
+ * }
+ * }
+ */
+ /* op vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ flds s0, [r2] @ s0<- vBB
+ flds s1, [r3] @ s1<- vCC
+ fcmpes s0, s1 @ compare (vBB, vCC)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ mov r0, #1 @ r0<- 1 (default)
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fmstat @ export status flags
+ mvnmi r0, #0 @ (less than) r1<- -1
+ moveq r0, #0 @ (equal) r1<- 0
+ b .LOP_CMPG_FLOAT_finish @ argh
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CMPL_DOUBLE: /* 0x2f */
+/* File: arm-vfp/OP_CMPL_DOUBLE.S */
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * int compare(x, y) {
+ * if (x == y) {
+ * return 0;
+ * } else if (x > y) {
+ * return 1;
+ * } else if (x < y) {
+ * return -1;
+ * } else {
+ * return -1;
+ * }
+ * }
+ */
+ /* op vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ fldd d0, [r2] @ d0<- vBB
+ fldd d1, [r3] @ d1<- vCC
+ fcmped d0, d1 @ compare (vBB, vCC)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ mvn r0, #0 @ r0<- -1 (default)
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fmstat @ export status flags
+ movgt r0, #1 @ (greater than) r1<- 1
+ moveq r0, #0 @ (equal) r1<- 0
+ b .LOP_CMPL_DOUBLE_finish @ argh
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CMPG_DOUBLE: /* 0x30 */
+/* File: arm-vfp/OP_CMPG_DOUBLE.S */
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * int compare(x, y) {
+ * if (x == y) {
+ * return 0;
+ * } else if (x < y) {
+ * return -1;
+ * } else if (x > y) {
+ * return 1;
+ * } else {
+ * return 1;
+ * }
+ * }
+ */
+ /* op vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ fldd d0, [r2] @ d0<- vBB
+ fldd d1, [r3] @ d1<- vCC
+ fcmped d0, d1 @ compare (vBB, vCC)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ mov r0, #1 @ r0<- 1 (default)
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fmstat @ export status flags
+ mvnmi r0, #0 @ (less than) r1<- -1
+ moveq r0, #0 @ (equal) r1<- 0
+ b .LOP_CMPG_DOUBLE_finish @ argh
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CMP_LONG: /* 0x31 */
+/* File: armv5te/OP_CMP_LONG.S */
+ /*
+ * Compare two 64-bit values. Puts 0, 1, or -1 into the destination
+ * register based on the results of the comparison.
+ *
+ * We load the full values with LDM, but in practice many values could
+ * be resolved by only looking at the high word. This could be made
+ * faster or slower by splitting the LDM into a pair of LDRs.
+ *
+ * If we just wanted to set condition flags, we could do this:
+ * subs ip, r0, r2
+ * sbcs ip, r1, r3
+ * subeqs ip, r0, r2
+ * Leaving { <0, 0, >0 } in ip. However, we have to set it to a specific
+ * integer value, which we can do with 2 conditional mov/mvn instructions
+ * (set 1, set -1; if they're equal we already have 0 in ip), giving
+ * us a constant 5-cycle path plus a branch at the end to the
+ * instruction epilogue code. The multi-compare approach below needs
+ * 2 or 3 cycles + branch if the high word doesn't match, 6 + branch
+ * in the worst case (the 64-bit values are equal).
+ */
+ /* cmp-long vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ cmp r1, r3 @ compare (vBB+1, vCC+1)
+ blt .LOP_CMP_LONG_less @ signed compare on high part
+ bgt .LOP_CMP_LONG_greater
+ subs r1, r0, r2 @ r1<- r0 - r2
+ bhi .LOP_CMP_LONG_greater @ unsigned compare on low part
+ bne .LOP_CMP_LONG_less
+ b .LOP_CMP_LONG_finish @ equal; r1 already holds 0
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_EQ: /* 0x32 */
+/* File: armv5te/OP_IF_EQ.S */
+/* File: armv5te/bincmp.S */
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ mov r0, rINST, lsr #8 @ r0<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r0, r0, #15
+ GET_VREG(r3, r1) @ r3<- vB
+ GET_VREG(r2, r0) @ r2<- vA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, r3 @ compare (vA, vB)
+ bne 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ b common_testUpdateProfile
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_NE: /* 0x33 */
+/* File: armv5te/OP_IF_NE.S */
+/* File: armv5te/bincmp.S */
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ mov r0, rINST, lsr #8 @ r0<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r0, r0, #15
+ GET_VREG(r3, r1) @ r3<- vB
+ GET_VREG(r2, r0) @ r2<- vA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, r3 @ compare (vA, vB)
+ beq 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ b common_testUpdateProfile
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_LT: /* 0x34 */
+/* File: armv5te/OP_IF_LT.S */
+/* File: armv5te/bincmp.S */
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ mov r0, rINST, lsr #8 @ r0<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r0, r0, #15
+ GET_VREG(r3, r1) @ r3<- vB
+ GET_VREG(r2, r0) @ r2<- vA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, r3 @ compare (vA, vB)
+ bge 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ b common_testUpdateProfile
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_GE: /* 0x35 */
+/* File: armv5te/OP_IF_GE.S */
+/* File: armv5te/bincmp.S */
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ mov r0, rINST, lsr #8 @ r0<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r0, r0, #15
+ GET_VREG(r3, r1) @ r3<- vB
+ GET_VREG(r2, r0) @ r2<- vA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, r3 @ compare (vA, vB)
+ blt 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ b common_testUpdateProfile
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_GT: /* 0x36 */
+/* File: armv5te/OP_IF_GT.S */
+/* File: armv5te/bincmp.S */
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ mov r0, rINST, lsr #8 @ r0<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r0, r0, #15
+ GET_VREG(r3, r1) @ r3<- vB
+ GET_VREG(r2, r0) @ r2<- vA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, r3 @ compare (vA, vB)
+ ble 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ b common_testUpdateProfile
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_LE: /* 0x37 */
+/* File: armv5te/OP_IF_LE.S */
+/* File: armv5te/bincmp.S */
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ mov r0, rINST, lsr #8 @ r0<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r0, r0, #15
+ GET_VREG(r3, r1) @ r3<- vB
+ GET_VREG(r2, r0) @ r2<- vA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, r3 @ compare (vA, vB)
+ bgt 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ b common_testUpdateProfile
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_EQZ: /* 0x38 */
+/* File: armv5te/OP_IF_EQZ.S */
+/* File: armv5te/zcmp.S */
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ mov r0, rINST, lsr #8 @ r0<- AA
+ GET_VREG(r2, r0) @ r2<- vAA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, #0 @ compare (vA, 0)
+ bne 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_NEZ: /* 0x39 */
+/* File: armv5te/OP_IF_NEZ.S */
+/* File: armv5te/zcmp.S */
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ mov r0, rINST, lsr #8 @ r0<- AA
+ GET_VREG(r2, r0) @ r2<- vAA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, #0 @ compare (vA, 0)
+ beq 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_LTZ: /* 0x3a */
+/* File: armv5te/OP_IF_LTZ.S */
+/* File: armv5te/zcmp.S */
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ mov r0, rINST, lsr #8 @ r0<- AA
+ GET_VREG(r2, r0) @ r2<- vAA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, #0 @ compare (vA, 0)
+ bge 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_GEZ: /* 0x3b */
+/* File: armv5te/OP_IF_GEZ.S */
+/* File: armv5te/zcmp.S */
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ mov r0, rINST, lsr #8 @ r0<- AA
+ GET_VREG(r2, r0) @ r2<- vAA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, #0 @ compare (vA, 0)
+ blt 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_GTZ: /* 0x3c */
+/* File: armv5te/OP_IF_GTZ.S */
+/* File: armv5te/zcmp.S */
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ mov r0, rINST, lsr #8 @ r0<- AA
+ GET_VREG(r2, r0) @ r2<- vAA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, #0 @ compare (vA, 0)
+ ble 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_LEZ: /* 0x3d */
+/* File: armv5te/OP_IF_LEZ.S */
+/* File: armv5te/zcmp.S */
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ mov r0, rINST, lsr #8 @ r0<- AA
+ GET_VREG(r2, r0) @ r2<- vAA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, #0 @ compare (vA, 0)
+ bgt 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_3E: /* 0x3e */
+/* File: armv5te/OP_UNUSED_3E.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_3F: /* 0x3f */
+/* File: armv5te/OP_UNUSED_3F.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_40: /* 0x40 */
+/* File: armv5te/OP_UNUSED_40.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_41: /* 0x41 */
+/* File: armv5te/OP_UNUSED_41.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_42: /* 0x42 */
+/* File: armv5te/OP_UNUSED_42.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_43: /* 0x43 */
+/* File: armv5te/OP_UNUSED_43.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET: /* 0x44 */
+/* File: armv5te/OP_AGET.S */
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #2 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldr r2, [r0, #offArrayObject_contents] @ r2<- vBB[vCC]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r9) @ vAA<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_WIDE: /* 0x45 */
+/* File: armv5te/OP_AGET_WIDE.S */
+ /*
+ * Array get, 64 bits. vAA <- vBB[vCC].
+ *
+ * Arrays of long/double are 64-bit aligned, so it's okay to use LDRD.
+ */
+ /* aget-wide vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #3 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcc .LOP_AGET_WIDE_finish @ okay, continue below
+ b common_errArrayIndex @ index >= length, bail
+ @ May want to swap the order of these two branches depending on how the
+ @ branch prediction (if any) handles conditional forward branches vs.
+ @ unconditional forward branches.
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_OBJECT: /* 0x46 */
+/* File: armv5te/OP_AGET_OBJECT.S */
+/* File: armv5te/OP_AGET.S */
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #2 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldr r2, [r0, #offArrayObject_contents] @ r2<- vBB[vCC]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r9) @ vAA<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_BOOLEAN: /* 0x47 */
+/* File: armv5te/OP_AGET_BOOLEAN.S */
+/* File: armv5te/OP_AGET.S */
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #0 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldrb r2, [r0, #offArrayObject_contents] @ r2<- vBB[vCC]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r9) @ vAA<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_BYTE: /* 0x48 */
+/* File: armv5te/OP_AGET_BYTE.S */
+/* File: armv5te/OP_AGET.S */
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #0 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldrsb r2, [r0, #offArrayObject_contents] @ r2<- vBB[vCC]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r9) @ vAA<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_CHAR: /* 0x49 */
+/* File: armv5te/OP_AGET_CHAR.S */
+/* File: armv5te/OP_AGET.S */
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #1 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldrh r2, [r0, #offArrayObject_contents] @ r2<- vBB[vCC]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r9) @ vAA<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_SHORT: /* 0x4a */
+/* File: armv5te/OP_AGET_SHORT.S */
+/* File: armv5te/OP_AGET.S */
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #1 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldrsh r2, [r0, #offArrayObject_contents] @ r2<- vBB[vCC]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r9) @ vAA<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT: /* 0x4b */
+/* File: armv5te/OP_APUT.S */
+ /*
+ * Array put, 32 bits or less. vBB[vCC] <- vAA.
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #2 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r2, r9) @ r2<- vAA
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ str r2, [r0, #offArrayObject_contents] @ vBB[vCC]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_WIDE: /* 0x4c */
+/* File: armv5te/OP_APUT_WIDE.S */
+ /*
+ * Array put, 64 bits. vBB[vCC] <- vAA.
+ *
+ * Arrays of long/double are 64-bit aligned, so it's okay to use STRD.
+ */
+ /* aput-wide vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #3 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ bcc .LOP_APUT_WIDE_finish @ okay, continue below
+ b common_errArrayIndex @ index >= length, bail
+ @ May want to swap the order of these two branches depending on how the
+ @ branch prediction (if any) handles conditional forward branches vs.
+ @ unconditional forward branches.
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_OBJECT: /* 0x4d */
+/* File: armv5te/OP_APUT_OBJECT.S */
+ /*
+ * Store an object into an array. vBB[vCC] <- vAA.
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ */
+ /* op vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ GET_VREG(rINST, r2) @ rINST<- vBB (array object)
+ GET_VREG(r0, r3) @ r0<- vCC (requested index)
+ cmp rINST, #0 @ null array object?
+ GET_VREG(r9, r9) @ r9<- vAA
+ beq common_errNullObject @ yes, bail
+ ldr r3, [rINST, #offArrayObject_length] @ r3<- arrayObj->length
+ add r10, rINST, r0, lsl #2 @ r10<- arrayObj + index*width
+ cmp r0, r3 @ compare unsigned index, length
+ bcc .LOP_APUT_OBJECT_finish @ we're okay, continue on
+ b common_errArrayIndex @ index >= length, bail
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_BOOLEAN: /* 0x4e */
+/* File: armv5te/OP_APUT_BOOLEAN.S */
+/* File: armv5te/OP_APUT.S */
+ /*
+ * Array put, 32 bits or less. vBB[vCC] <- vAA.
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #0 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r2, r9) @ r2<- vAA
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ strb r2, [r0, #offArrayObject_contents] @ vBB[vCC]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_BYTE: /* 0x4f */
+/* File: armv5te/OP_APUT_BYTE.S */
+/* File: armv5te/OP_APUT.S */
+ /*
+ * Array put, 32 bits or less. vBB[vCC] <- vAA.
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #0 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r2, r9) @ r2<- vAA
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ strb r2, [r0, #offArrayObject_contents] @ vBB[vCC]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_CHAR: /* 0x50 */
+/* File: armv5te/OP_APUT_CHAR.S */
+/* File: armv5te/OP_APUT.S */
+ /*
+ * Array put, 32 bits or less. vBB[vCC] <- vAA.
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #1 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r2, r9) @ r2<- vAA
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ strh r2, [r0, #offArrayObject_contents] @ vBB[vCC]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_SHORT: /* 0x51 */
+/* File: armv5te/OP_APUT_SHORT.S */
+/* File: armv5te/OP_APUT.S */
+ /*
+ * Array put, 32 bits or less. vBB[vCC] <- vAA.
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #1 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r2, r9) @ r2<- vAA
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ strh r2, [r0, #offArrayObject_contents] @ vBB[vCC]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET: /* 0x52 */
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_finish
+ b common_exceptionThrown
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_WIDE: /* 0x53 */
+/* File: armv5te/OP_IGET_WIDE.S */
+ /*
+ * Wide 32-bit instance field get.
+ */
+ /* iget-wide vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_WIDE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_WIDE_finish
+ b common_exceptionThrown
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_OBJECT: /* 0x54 */
+/* File: armv5te/OP_IGET_OBJECT.S */
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_OBJECT_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_OBJECT_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_BOOLEAN: /* 0x55 */
+/* File: armv5te/OP_IGET_BOOLEAN.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrb", "sqnum":"1" }
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_BOOLEAN_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_BOOLEAN_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_BYTE: /* 0x56 */
+/* File: armv5te/OP_IGET_BYTE.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrsb", "sqnum":"2" }
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_BYTE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_BYTE_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_CHAR: /* 0x57 */
+/* File: armv5te/OP_IGET_CHAR.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrh", "sqnum":"3" }
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_CHAR_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_CHAR_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_SHORT: /* 0x58 */
+/* File: armv5te/OP_IGET_SHORT.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrsh", "sqnum":"4" }
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_SHORT_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_SHORT_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT: /* 0x59 */
+/* File: armv5te/OP_IPUT.S */
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_finish @ yes, finish up
+ b common_exceptionThrown
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_WIDE: /* 0x5a */
+/* File: armv5te/OP_IPUT_WIDE.S */
+ /* iput-wide vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_WIDE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_WIDE_finish @ yes, finish up
+ b common_exceptionThrown
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_OBJECT: /* 0x5b */
+/* File: armv5te/OP_IPUT_OBJECT.S */
+ /*
+ * 32-bit instance field put.
+ *
+ * for: iput-object, iput-object-volatile
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_OBJECT_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_OBJECT_finish @ yes, finish up
+ b common_exceptionThrown
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_BOOLEAN: /* 0x5c */
+/* File: armv5te/OP_IPUT_BOOLEAN.S */
+@include "armv5te/OP_IPUT.S" { "store":"strb", "sqnum":"1" }
+/* File: armv5te/OP_IPUT.S */
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_BOOLEAN_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_BOOLEAN_finish @ yes, finish up
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_BYTE: /* 0x5d */
+/* File: armv5te/OP_IPUT_BYTE.S */
+@include "armv5te/OP_IPUT.S" { "store":"strb", "sqnum":"2" }
+/* File: armv5te/OP_IPUT.S */
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_BYTE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_BYTE_finish @ yes, finish up
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_CHAR: /* 0x5e */
+/* File: armv5te/OP_IPUT_CHAR.S */
+@include "armv5te/OP_IPUT.S" { "store":"strh", "sqnum":"3" }
+/* File: armv5te/OP_IPUT.S */
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_CHAR_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_CHAR_finish @ yes, finish up
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_SHORT: /* 0x5f */
+/* File: armv5te/OP_IPUT_SHORT.S */
+@include "armv5te/OP_IPUT.S" { "store":"strh", "sqnum":"4" }
+/* File: armv5te/OP_IPUT.S */
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_SHORT_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_SHORT_finish @ yes, finish up
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET: /* 0x60 */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_resolve @ yes, do resolve
+.LOP_SGET_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_WIDE: /* 0x61 */
+/* File: armv5te/OP_SGET_WIDE.S */
+ /*
+ * 64-bit SGET handler.
+ */
+ /* sget-wide vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_WIDE_resolve @ yes, do resolve
+.LOP_SGET_WIDE_finish:
+ mov r9, rINST, lsr #8 @ r9<- AA
+ .if 0
+ add r0, r0, #offStaticField_value @ r0<- pointer to data
+ bl dvmQuasiAtomicRead64 @ r0/r1<- contents of field
+ .else
+ ldrd r0, [r0, #offStaticField_value] @ r0/r1<- field value (aligned)
+ .endif
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_OBJECT: /* 0x62 */
+/* File: armv5te/OP_SGET_OBJECT.S */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_OBJECT_resolve @ yes, do resolve
+.LOP_SGET_OBJECT_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_BOOLEAN: /* 0x63 */
+/* File: armv5te/OP_SGET_BOOLEAN.S */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_BOOLEAN_resolve @ yes, do resolve
+.LOP_SGET_BOOLEAN_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_BYTE: /* 0x64 */
+/* File: armv5te/OP_SGET_BYTE.S */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_BYTE_resolve @ yes, do resolve
+.LOP_SGET_BYTE_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_CHAR: /* 0x65 */
+/* File: armv5te/OP_SGET_CHAR.S */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_CHAR_resolve @ yes, do resolve
+.LOP_SGET_CHAR_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_SHORT: /* 0x66 */
+/* File: armv5te/OP_SGET_SHORT.S */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_SHORT_resolve @ yes, do resolve
+.LOP_SGET_SHORT_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT: /* 0x67 */
+/* File: armv5te/OP_SPUT.S */
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SPUT_resolve @ yes, do resolve
+.LOP_SPUT_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_WIDE: /* 0x68 */
+/* File: armv5te/OP_SPUT_WIDE.S */
+ /*
+ * 64-bit SPUT handler.
+ */
+ /* sput-wide vAA, field@BBBB */
+ ldr r0, [rGLUE, #offGlue_methodClassDex] @ r0<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r0, [r0, #offDvmDex_pResFields] @ r0<- dvmDex->pResFields
+ mov r9, rINST, lsr #8 @ r9<- AA
+ ldr r2, [r0, r1, lsl #2] @ r2<- resolved StaticField ptr
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ cmp r2, #0 @ is resolved entry null?
+ beq .LOP_SPUT_WIDE_resolve @ yes, do resolve
+.LOP_SPUT_WIDE_finish: @ field ptr in r2, AA in r9
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ GET_INST_OPCODE(r10) @ extract opcode from rINST
+ .if 0
+ add r2, r2, #offStaticField_value @ r2<- pointer to data
+ bl dvmQuasiAtomicSwap64 @ stores r0/r1 into addr r2
+ .else
+ strd r0, [r2, #offStaticField_value] @ field<- vAA/vAA+1
+ .endif
+ GOTO_OPCODE(r10) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_OBJECT: /* 0x69 */
+/* File: armv5te/OP_SPUT_OBJECT.S */
+ /*
+ * 32-bit SPUT handler for objects
+ *
+ * for: sput-object, sput-object-volatile
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_SPUT_OBJECT_finish @ no, continue
+ ldr r9, [rGLUE, #offGlue_method] @ r9<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r9, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_OBJECT_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_BOOLEAN: /* 0x6a */
+/* File: armv5te/OP_SPUT_BOOLEAN.S */
+/* File: armv5te/OP_SPUT.S */
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SPUT_BOOLEAN_resolve @ yes, do resolve
+.LOP_SPUT_BOOLEAN_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_BYTE: /* 0x6b */
+/* File: armv5te/OP_SPUT_BYTE.S */
+/* File: armv5te/OP_SPUT.S */
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SPUT_BYTE_resolve @ yes, do resolve
+.LOP_SPUT_BYTE_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_CHAR: /* 0x6c */
+/* File: armv5te/OP_SPUT_CHAR.S */
+/* File: armv5te/OP_SPUT.S */
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SPUT_CHAR_resolve @ yes, do resolve
+.LOP_SPUT_CHAR_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_SHORT: /* 0x6d */
+/* File: armv5te/OP_SPUT_SHORT.S */
+/* File: armv5te/OP_SPUT.S */
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SPUT_SHORT_resolve @ yes, do resolve
+.LOP_SPUT_SHORT_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_VIRTUAL: /* 0x6e */
+/* File: armv5te/OP_INVOKE_VIRTUAL.S */
+ /*
+ * Handle a virtual method call.
+ *
+ * for: invoke-virtual, invoke-virtual/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved baseMethod
+ .if (!0)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ must export for invoke
+ bne .LOP_INVOKE_VIRTUAL_continue @ yes, continue on
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ mov r2, #METHOD_VIRTUAL @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ bne .LOP_INVOKE_VIRTUAL_continue @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_SUPER: /* 0x6f */
+/* File: armv5te/OP_INVOKE_SUPER.S */
+ /*
+ * Handle a "super" method call.
+ *
+ * for: invoke-super, invoke-super/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ .if (!0)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ GET_VREG(r2, r10) @ r2<- "this" ptr
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved baseMethod
+ cmp r2, #0 @ null "this"?
+ ldr r9, [rGLUE, #offGlue_method] @ r9<- current method
+ beq common_errNullObject @ null "this", throw exception
+ cmp r0, #0 @ already resolved?
+ ldr r9, [r9, #offMethod_clazz] @ r9<- method->clazz
+ EXPORT_PC() @ must export for invoke
+ bne .LOP_INVOKE_SUPER_continue @ resolved, continue on
+ b .LOP_INVOKE_SUPER_resolve @ do resolve now
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_DIRECT: /* 0x70 */
+/* File: armv5te/OP_INVOKE_DIRECT.S */
+ /*
+ * Handle a direct method call.
+ *
+ * (We could defer the "is 'this' pointer null" test to the common
+ * method invocation code, and use a flag to indicate that static
+ * calls don't count. If we do this as part of copying the arguments
+ * out we could avoiding loading the first arg twice.)
+ *
+ * for: invoke-direct, invoke-direct/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved methodToCall
+ .if (!0)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ must export for invoke
+ GET_VREG(r2, r10) @ r2<- "this" ptr
+ beq .LOP_INVOKE_DIRECT_resolve @ not resolved, do it now
+.LOP_INVOKE_DIRECT_finish:
+ cmp r2, #0 @ null "this" ref?
+ bne common_invokeMethodNoRange @ no, continue on
+ b common_errNullObject @ yes, throw exception
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_STATIC: /* 0x71 */
+/* File: armv5te/OP_INVOKE_STATIC.S */
+ /*
+ * Handle a static method call.
+ *
+ * for: invoke-static, invoke-static/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved methodToCall
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ must export for invoke
+ bne common_invokeMethodNoRange @ yes, continue on
+0: ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ mov r2, #METHOD_STATIC @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ bne common_invokeMethodNoRange @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_INTERFACE: /* 0x72 */
+/* File: armv5te/OP_INVOKE_INTERFACE.S */
+ /*
+ * Handle an interface method call.
+ *
+ * for: invoke-interface, invoke-interface/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r2, 2) @ r2<- FEDC or CCCC
+ FETCH(r1, 1) @ r1<- BBBB
+ .if (!0)
+ and r2, r2, #15 @ r2<- C (or stays CCCC)
+ .endif
+ EXPORT_PC() @ must export for invoke
+ GET_VREG(r0, r2) @ r0<- first arg ("this")
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- methodClassDex
+ cmp r0, #0 @ null obj?
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- method
+ beq common_errNullObject @ yes, fail
+ ldr r0, [r0, #offObject_clazz] @ r0<- thisPtr->clazz
+ bl dvmFindInterfaceMethodInCache @ r0<- call(class, ref, method, dex)
+ cmp r0, #0 @ failed?
+ beq common_exceptionThrown @ yes, handle exception
+ b common_invokeMethodNoRange @ jump to common handler
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_73: /* 0x73 */
+/* File: armv5te/OP_UNUSED_73.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_VIRTUAL_RANGE: /* 0x74 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_RANGE.S */
+/* File: armv5te/OP_INVOKE_VIRTUAL.S */
+ /*
+ * Handle a virtual method call.
+ *
+ * for: invoke-virtual, invoke-virtual/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved baseMethod
+ .if (!1)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ must export for invoke
+ bne .LOP_INVOKE_VIRTUAL_RANGE_continue @ yes, continue on
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ mov r2, #METHOD_VIRTUAL @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ bne .LOP_INVOKE_VIRTUAL_RANGE_continue @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_SUPER_RANGE: /* 0x75 */
+/* File: armv5te/OP_INVOKE_SUPER_RANGE.S */
+/* File: armv5te/OP_INVOKE_SUPER.S */
+ /*
+ * Handle a "super" method call.
+ *
+ * for: invoke-super, invoke-super/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ .if (!1)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ GET_VREG(r2, r10) @ r2<- "this" ptr
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved baseMethod
+ cmp r2, #0 @ null "this"?
+ ldr r9, [rGLUE, #offGlue_method] @ r9<- current method
+ beq common_errNullObject @ null "this", throw exception
+ cmp r0, #0 @ already resolved?
+ ldr r9, [r9, #offMethod_clazz] @ r9<- method->clazz
+ EXPORT_PC() @ must export for invoke
+ bne .LOP_INVOKE_SUPER_RANGE_continue @ resolved, continue on
+ b .LOP_INVOKE_SUPER_RANGE_resolve @ do resolve now
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_DIRECT_RANGE: /* 0x76 */
+/* File: armv5te/OP_INVOKE_DIRECT_RANGE.S */
+/* File: armv5te/OP_INVOKE_DIRECT.S */
+ /*
+ * Handle a direct method call.
+ *
+ * (We could defer the "is 'this' pointer null" test to the common
+ * method invocation code, and use a flag to indicate that static
+ * calls don't count. If we do this as part of copying the arguments
+ * out we could avoiding loading the first arg twice.)
+ *
+ * for: invoke-direct, invoke-direct/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved methodToCall
+ .if (!1)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ must export for invoke
+ GET_VREG(r2, r10) @ r2<- "this" ptr
+ beq .LOP_INVOKE_DIRECT_RANGE_resolve @ not resolved, do it now
+.LOP_INVOKE_DIRECT_RANGE_finish:
+ cmp r2, #0 @ null "this" ref?
+ bne common_invokeMethodRange @ no, continue on
+ b common_errNullObject @ yes, throw exception
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_STATIC_RANGE: /* 0x77 */
+/* File: armv5te/OP_INVOKE_STATIC_RANGE.S */
+/* File: armv5te/OP_INVOKE_STATIC.S */
+ /*
+ * Handle a static method call.
+ *
+ * for: invoke-static, invoke-static/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved methodToCall
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ must export for invoke
+ bne common_invokeMethodRange @ yes, continue on
+0: ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ mov r2, #METHOD_STATIC @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ bne common_invokeMethodRange @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_INTERFACE_RANGE: /* 0x78 */
+/* File: armv5te/OP_INVOKE_INTERFACE_RANGE.S */
+/* File: armv5te/OP_INVOKE_INTERFACE.S */
+ /*
+ * Handle an interface method call.
+ *
+ * for: invoke-interface, invoke-interface/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r2, 2) @ r2<- FEDC or CCCC
+ FETCH(r1, 1) @ r1<- BBBB
+ .if (!1)
+ and r2, r2, #15 @ r2<- C (or stays CCCC)
+ .endif
+ EXPORT_PC() @ must export for invoke
+ GET_VREG(r0, r2) @ r0<- first arg ("this")
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- methodClassDex
+ cmp r0, #0 @ null obj?
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- method
+ beq common_errNullObject @ yes, fail
+ ldr r0, [r0, #offObject_clazz] @ r0<- thisPtr->clazz
+ bl dvmFindInterfaceMethodInCache @ r0<- call(class, ref, method, dex)
+ cmp r0, #0 @ failed?
+ beq common_exceptionThrown @ yes, handle exception
+ b common_invokeMethodRange @ jump to common handler
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_79: /* 0x79 */
+/* File: armv5te/OP_UNUSED_79.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_7A: /* 0x7a */
+/* File: armv5te/OP_UNUSED_7A.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEG_INT: /* 0x7b */
+/* File: armv5te/OP_NEG_INT.S */
+/* File: armv5te/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0".
+ * This could be an ARM instruction or a function call.
+ *
+ * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+ * int-to-byte, int-to-char, int-to-short
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r3) @ r0<- vB
+ and r9, r9, #15
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ rsb r0, r0, #0 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 9-10 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NOT_INT: /* 0x7c */
+/* File: armv5te/OP_NOT_INT.S */
+/* File: armv5te/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0".
+ * This could be an ARM instruction or a function call.
+ *
+ * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+ * int-to-byte, int-to-char, int-to-short
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r3) @ r0<- vB
+ and r9, r9, #15
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ mvn r0, r0 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 9-10 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEG_LONG: /* 0x7d */
+/* File: armv5te/OP_NEG_LONG.S */
+/* File: armv5te/unopWide.S */
+ /*
+ * Generic 64-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0/r1".
+ * This could be an ARM instruction or a function call.
+ *
+ * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+ */
+ /* unop vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r3, {r0-r1} @ r0/r1<- vAA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ rsbs r0, r0, #0 @ optional op; may set condition codes
+ rsc r1, r1, #0 @ r0/r1<- op, r2-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NOT_LONG: /* 0x7e */
+/* File: armv5te/OP_NOT_LONG.S */
+/* File: armv5te/unopWide.S */
+ /*
+ * Generic 64-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0/r1".
+ * This could be an ARM instruction or a function call.
+ *
+ * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+ */
+ /* unop vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r3, {r0-r1} @ r0/r1<- vAA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ mvn r0, r0 @ optional op; may set condition codes
+ mvn r1, r1 @ r0/r1<- op, r2-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEG_FLOAT: /* 0x7f */
+/* File: armv5te/OP_NEG_FLOAT.S */
+/* File: armv5te/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0".
+ * This could be an ARM instruction or a function call.
+ *
+ * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+ * int-to-byte, int-to-char, int-to-short
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r3) @ r0<- vB
+ and r9, r9, #15
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ add r0, r0, #0x80000000 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 9-10 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEG_DOUBLE: /* 0x80 */
+/* File: armv5te/OP_NEG_DOUBLE.S */
+/* File: armv5te/unopWide.S */
+ /*
+ * Generic 64-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0/r1".
+ * This could be an ARM instruction or a function call.
+ *
+ * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+ */
+ /* unop vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r3, {r0-r1} @ r0/r1<- vAA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ add r1, r1, #0x80000000 @ r0/r1<- op, r2-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_LONG: /* 0x81 */
+/* File: armv5te/OP_INT_TO_LONG.S */
+/* File: armv5te/unopWider.S */
+ /*
+ * Generic 32bit-to-64bit unary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = op r0", where
+ * "result" is a 64-bit quantity in r0/r1.
+ *
+ * For: int-to-long, int-to-double, float-to-long, float-to-double
+ */
+ /* unop vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r0, r3) @ r0<- vB
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ mov r1, r0, asr #31 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vA/vA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-11 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_FLOAT: /* 0x82 */
+/* File: arm-vfp/OP_INT_TO_FLOAT.S */
+/* File: arm-vfp/funop.S */
+ /*
+ * Generic 32-bit unary floating-point operation. Provide an "instr"
+ * line that specifies an instruction that performs "s1 = op s0".
+ *
+ * for: int-to-float, float-to-int
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ flds s0, [r3] @ s0<- vB
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ and r9, r9, #15 @ r9<- A
+ fsitos s1, s0 @ s1<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ fsts s1, [r9] @ vA<- s1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_DOUBLE: /* 0x83 */
+/* File: arm-vfp/OP_INT_TO_DOUBLE.S */
+/* File: arm-vfp/funopWider.S */
+ /*
+ * Generic 32bit-to-64bit floating point unary operation. Provide an
+ * "instr" line that specifies an instruction that performs "d0 = op s0".
+ *
+ * For: int-to-double, float-to-double
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ flds s0, [r3] @ s0<- vB
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ and r9, r9, #15 @ r9<- A
+ fsitod d0, s0 @ d0<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ fstd d0, [r9] @ vA<- d0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_LONG_TO_INT: /* 0x84 */
+/* File: armv5te/OP_LONG_TO_INT.S */
+/* we ignore the high word, making this equivalent to a 32-bit reg move */
+/* File: armv5te/OP_MOVE.S */
+ /* for move, move-object, long-to-int */
+ /* op vA, vB */
+ mov r1, rINST, lsr #12 @ r1<- B from 15:12
+ mov r0, rINST, lsr #8 @ r0<- A from 11:8
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[B]
+ and r0, r0, #15
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ SET_VREG(r2, r0) @ fp[A]<- r2
+ GOTO_OPCODE(ip) @ execute next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_LONG_TO_FLOAT: /* 0x85 */
+/* File: armv5te/OP_LONG_TO_FLOAT.S */
+/* File: armv5te/unopNarrower.S */
+ /*
+ * Generic 64bit-to-32bit unary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = op r0/r1", where
+ * "result" is a 32-bit quantity in r0.
+ *
+ * For: long-to-float, double-to-int, double-to-float
+ *
+ * (This would work for long-to-int, but that instruction is actually
+ * an exact match for OP_MOVE.)
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ and r9, r9, #15
+ ldmia r3, {r0-r1} @ r0/r1<- vB/vB+1
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl __aeabi_l2f @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-11 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_LONG_TO_DOUBLE: /* 0x86 */
+/* File: armv5te/OP_LONG_TO_DOUBLE.S */
+/* File: armv5te/unopWide.S */
+ /*
+ * Generic 64-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0/r1".
+ * This could be an ARM instruction or a function call.
+ *
+ * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+ */
+ /* unop vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r3, {r0-r1} @ r0/r1<- vAA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl __aeabi_l2d @ r0/r1<- op, r2-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FLOAT_TO_INT: /* 0x87 */
+/* File: arm-vfp/OP_FLOAT_TO_INT.S */
+/* File: arm-vfp/funop.S */
+ /*
+ * Generic 32-bit unary floating-point operation. Provide an "instr"
+ * line that specifies an instruction that performs "s1 = op s0".
+ *
+ * for: int-to-float, float-to-int
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ flds s0, [r3] @ s0<- vB
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ and r9, r9, #15 @ r9<- A
+ ftosizs s1, s0 @ s1<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ fsts s1, [r9] @ vA<- s1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FLOAT_TO_LONG: /* 0x88 */
+/* File: armv5te/OP_FLOAT_TO_LONG.S */
+@include "armv5te/unopWider.S" {"instr":"bl __aeabi_f2lz"}
+/* File: armv5te/unopWider.S */
+ /*
+ * Generic 32bit-to-64bit unary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = op r0", where
+ * "result" is a 64-bit quantity in r0/r1.
+ *
+ * For: int-to-long, int-to-double, float-to-long, float-to-double
+ */
+ /* unop vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r0, r3) @ r0<- vB
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ bl f2l_doconv @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vA/vA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-11 instructions */
+
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FLOAT_TO_DOUBLE: /* 0x89 */
+/* File: arm-vfp/OP_FLOAT_TO_DOUBLE.S */
+/* File: arm-vfp/funopWider.S */
+ /*
+ * Generic 32bit-to-64bit floating point unary operation. Provide an
+ * "instr" line that specifies an instruction that performs "d0 = op s0".
+ *
+ * For: int-to-double, float-to-double
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ flds s0, [r3] @ s0<- vB
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ and r9, r9, #15 @ r9<- A
+ fcvtds d0, s0 @ d0<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ fstd d0, [r9] @ vA<- d0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DOUBLE_TO_INT: /* 0x8a */
+/* File: arm-vfp/OP_DOUBLE_TO_INT.S */
+/* File: arm-vfp/funopNarrower.S */
+ /*
+ * Generic 64bit-to-32bit unary floating point operation. Provide an
+ * "instr" line that specifies an instruction that performs "s0 = op d0".
+ *
+ * For: double-to-int, double-to-float
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ fldd d0, [r3] @ d0<- vB
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ and r9, r9, #15 @ r9<- A
+ ftosizd s0, d0 @ s0<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ fsts s0, [r9] @ vA<- s0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DOUBLE_TO_LONG: /* 0x8b */
+/* File: armv5te/OP_DOUBLE_TO_LONG.S */
+@include "armv5te/unopWide.S" {"instr":"bl __aeabi_d2lz"}
+/* File: armv5te/unopWide.S */
+ /*
+ * Generic 64-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0/r1".
+ * This could be an ARM instruction or a function call.
+ *
+ * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+ */
+ /* unop vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r3, {r0-r1} @ r0/r1<- vAA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl d2l_doconv @ r0/r1<- op, r2-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-13 instructions */
+
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DOUBLE_TO_FLOAT: /* 0x8c */
+/* File: arm-vfp/OP_DOUBLE_TO_FLOAT.S */
+/* File: arm-vfp/funopNarrower.S */
+ /*
+ * Generic 64bit-to-32bit unary floating point operation. Provide an
+ * "instr" line that specifies an instruction that performs "s0 = op d0".
+ *
+ * For: double-to-int, double-to-float
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ fldd d0, [r3] @ d0<- vB
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ and r9, r9, #15 @ r9<- A
+ fcvtsd s0, d0 @ s0<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ fsts s0, [r9] @ vA<- s0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_BYTE: /* 0x8d */
+/* File: armv5te/OP_INT_TO_BYTE.S */
+/* File: armv5te/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0".
+ * This could be an ARM instruction or a function call.
+ *
+ * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+ * int-to-byte, int-to-char, int-to-short
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r3) @ r0<- vB
+ and r9, r9, #15
+ mov r0, r0, asl #24 @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ mov r0, r0, asr #24 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 9-10 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_CHAR: /* 0x8e */
+/* File: armv5te/OP_INT_TO_CHAR.S */
+/* File: armv5te/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0".
+ * This could be an ARM instruction or a function call.
+ *
+ * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+ * int-to-byte, int-to-char, int-to-short
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r3) @ r0<- vB
+ and r9, r9, #15
+ mov r0, r0, asl #16 @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ mov r0, r0, lsr #16 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 9-10 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_SHORT: /* 0x8f */
+/* File: armv5te/OP_INT_TO_SHORT.S */
+/* File: armv5te/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0".
+ * This could be an ARM instruction or a function call.
+ *
+ * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+ * int-to-byte, int-to-char, int-to-short
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r3) @ r0<- vB
+ and r9, r9, #15
+ mov r0, r0, asl #16 @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ mov r0, r0, asr #16 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 9-10 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_INT: /* 0x90 */
+/* File: armv5te/OP_ADD_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ add r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_INT: /* 0x91 */
+/* File: armv5te/OP_SUB_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ sub r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_INT: /* 0x92 */
+/* File: armv5te/OP_MUL_INT.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ mul r0, r1, r0 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_INT: /* 0x93 */
+/* File: armv5te/OP_DIV_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 1
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl __aeabi_idiv @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_INT: /* 0x94 */
+/* File: armv5te/OP_REM_INT.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 1
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl __aeabi_idivmod @ r1<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r1, r9) @ vAA<- r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_INT: /* 0x95 */
+/* File: armv5te/OP_AND_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ and r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_INT: /* 0x96 */
+/* File: armv5te/OP_OR_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ orr r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_INT: /* 0x97 */
+/* File: armv5te/OP_XOR_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ eor r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHL_INT: /* 0x98 */
+/* File: armv5te/OP_SHL_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, asl r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHR_INT: /* 0x99 */
+/* File: armv5te/OP_SHR_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, asr r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_USHR_INT: /* 0x9a */
+/* File: armv5te/OP_USHR_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, lsr r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_LONG: /* 0x9b */
+/* File: armv5te/OP_ADD_LONG.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ adds r0, r0, r2 @ optional op; may set condition codes
+ adc r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_LONG: /* 0x9c */
+/* File: armv5te/OP_SUB_LONG.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ subs r0, r0, r2 @ optional op; may set condition codes
+ sbc r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_LONG: /* 0x9d */
+/* File: armv5te/OP_MUL_LONG.S */
+ /*
+ * Signed 64-bit integer multiply.
+ *
+ * Consider WXxYZ (r1r0 x r3r2) with a long multiply:
+ * WX
+ * x YZ
+ * --------
+ * ZW ZX
+ * YW YX
+ *
+ * The low word of the result holds ZX, the high word holds
+ * (ZW+YX) + (the high overflow from ZX). YW doesn't matter because
+ * it doesn't fit in the low 64 bits.
+ *
+ * Unlike most ARM math operations, multiply instructions have
+ * restrictions on using the same register more than once (Rd and Rm
+ * cannot be the same).
+ */
+ /* mul-long vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ mul ip, r2, r1 @ ip<- ZxW
+ umull r9, r10, r2, r0 @ r9/r10 <- ZxX
+ mla r2, r0, r3, ip @ r2<- YxX + (ZxW)
+ mov r0, rINST, lsr #8 @ r0<- AA
+ add r10, r2, r10 @ r10<- r10 + low(ZxW + (YxX))
+ add r0, rFP, r0, lsl #2 @ r0<- &fp[AA]
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ b .LOP_MUL_LONG_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_LONG: /* 0x9e */
+/* File: armv5te/OP_DIV_LONG.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 1
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_ldivmod @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_LONG: /* 0x9f */
+/* File: armv5te/OP_REM_LONG.S */
+/* ldivmod returns quotient in r0/r1 and remainder in r2/r3 */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 1
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_ldivmod @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r2,r3} @ vAA/vAA+1<- r2/r3
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_LONG: /* 0xa0 */
+/* File: armv5te/OP_AND_LONG.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ and r0, r0, r2 @ optional op; may set condition codes
+ and r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_LONG: /* 0xa1 */
+/* File: armv5te/OP_OR_LONG.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ orr r0, r0, r2 @ optional op; may set condition codes
+ orr r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_LONG: /* 0xa2 */
+/* File: armv5te/OP_XOR_LONG.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ eor r0, r0, r2 @ optional op; may set condition codes
+ eor r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHL_LONG: /* 0xa3 */
+/* File: armv5te/OP_SHL_LONG.S */
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to mask off the low
+ * 6 bits of the shift distance.
+ */
+ /* shl-long vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r3, r0, #255 @ r3<- BB
+ mov r0, r0, lsr #8 @ r0<- CC
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[BB]
+ GET_VREG(r2, r0) @ r2<- vCC
+ ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1
+ and r2, r2, #63 @ r2<- r2 & 0x3f
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+
+ mov r1, r1, asl r2 @ r1<- r1 << r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ b .LOP_SHL_LONG_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHR_LONG: /* 0xa4 */
+/* File: armv5te/OP_SHR_LONG.S */
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to mask off the low
+ * 6 bits of the shift distance.
+ */
+ /* shr-long vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r3, r0, #255 @ r3<- BB
+ mov r0, r0, lsr #8 @ r0<- CC
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[BB]
+ GET_VREG(r2, r0) @ r2<- vCC
+ ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1
+ and r2, r2, #63 @ r0<- r0 & 0x3f
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ b .LOP_SHR_LONG_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_USHR_LONG: /* 0xa5 */
+/* File: armv5te/OP_USHR_LONG.S */
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to mask off the low
+ * 6 bits of the shift distance.
+ */
+ /* ushr-long vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r3, r0, #255 @ r3<- BB
+ mov r0, r0, lsr #8 @ r0<- CC
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[BB]
+ GET_VREG(r2, r0) @ r2<- vCC
+ ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1
+ and r2, r2, #63 @ r0<- r0 & 0x3f
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ b .LOP_USHR_LONG_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_FLOAT: /* 0xa6 */
+/* File: arm-vfp/OP_ADD_FLOAT.S */
+/* File: arm-vfp/fbinop.S */
+ /*
+ * Generic 32-bit floating-point operation. Provide an "instr" line that
+ * specifies an instruction that performs "s2 = s0 op s1". Because we
+ * use the "softfp" ABI, this must be an instruction, not a function call.
+ *
+ * For: add-float, sub-float, mul-float, div-float
+ */
+ /* floatop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ flds s1, [r3] @ s1<- vCC
+ flds s0, [r2] @ s0<- vBB
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ fadds s2, s0, s1 @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vAA
+ fsts s2, [r9] @ vAA<- s2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_FLOAT: /* 0xa7 */
+/* File: arm-vfp/OP_SUB_FLOAT.S */
+/* File: arm-vfp/fbinop.S */
+ /*
+ * Generic 32-bit floating-point operation. Provide an "instr" line that
+ * specifies an instruction that performs "s2 = s0 op s1". Because we
+ * use the "softfp" ABI, this must be an instruction, not a function call.
+ *
+ * For: add-float, sub-float, mul-float, div-float
+ */
+ /* floatop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ flds s1, [r3] @ s1<- vCC
+ flds s0, [r2] @ s0<- vBB
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ fsubs s2, s0, s1 @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vAA
+ fsts s2, [r9] @ vAA<- s2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_FLOAT: /* 0xa8 */
+/* File: arm-vfp/OP_MUL_FLOAT.S */
+/* File: arm-vfp/fbinop.S */
+ /*
+ * Generic 32-bit floating-point operation. Provide an "instr" line that
+ * specifies an instruction that performs "s2 = s0 op s1". Because we
+ * use the "softfp" ABI, this must be an instruction, not a function call.
+ *
+ * For: add-float, sub-float, mul-float, div-float
+ */
+ /* floatop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ flds s1, [r3] @ s1<- vCC
+ flds s0, [r2] @ s0<- vBB
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ fmuls s2, s0, s1 @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vAA
+ fsts s2, [r9] @ vAA<- s2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_FLOAT: /* 0xa9 */
+/* File: arm-vfp/OP_DIV_FLOAT.S */
+/* File: arm-vfp/fbinop.S */
+ /*
+ * Generic 32-bit floating-point operation. Provide an "instr" line that
+ * specifies an instruction that performs "s2 = s0 op s1". Because we
+ * use the "softfp" ABI, this must be an instruction, not a function call.
+ *
+ * For: add-float, sub-float, mul-float, div-float
+ */
+ /* floatop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ flds s1, [r3] @ s1<- vCC
+ flds s0, [r2] @ s0<- vBB
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ fdivs s2, s0, s1 @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vAA
+ fsts s2, [r9] @ vAA<- s2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_FLOAT: /* 0xaa */
+/* File: armv5te/OP_REM_FLOAT.S */
+/* EABI doesn't define a float remainder function, but libm does */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl fmodf @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_DOUBLE: /* 0xab */
+/* File: arm-vfp/OP_ADD_DOUBLE.S */
+/* File: arm-vfp/fbinopWide.S */
+ /*
+ * Generic 64-bit double-precision floating point binary operation.
+ * Provide an "instr" line that specifies an instruction that performs
+ * "d2 = d0 op d1".
+ *
+ * for: add-double, sub-double, mul-double, div-double
+ */
+ /* doubleop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ fldd d1, [r3] @ d1<- vCC
+ fldd d0, [r2] @ d0<- vBB
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ faddd d2, d0, d1 @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vAA
+ fstd d2, [r9] @ vAA<- d2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_DOUBLE: /* 0xac */
+/* File: arm-vfp/OP_SUB_DOUBLE.S */
+/* File: arm-vfp/fbinopWide.S */
+ /*
+ * Generic 64-bit double-precision floating point binary operation.
+ * Provide an "instr" line that specifies an instruction that performs
+ * "d2 = d0 op d1".
+ *
+ * for: add-double, sub-double, mul-double, div-double
+ */
+ /* doubleop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ fldd d1, [r3] @ d1<- vCC
+ fldd d0, [r2] @ d0<- vBB
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ fsubd d2, d0, d1 @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vAA
+ fstd d2, [r9] @ vAA<- d2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_DOUBLE: /* 0xad */
+/* File: arm-vfp/OP_MUL_DOUBLE.S */
+/* File: arm-vfp/fbinopWide.S */
+ /*
+ * Generic 64-bit double-precision floating point binary operation.
+ * Provide an "instr" line that specifies an instruction that performs
+ * "d2 = d0 op d1".
+ *
+ * for: add-double, sub-double, mul-double, div-double
+ */
+ /* doubleop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ fldd d1, [r3] @ d1<- vCC
+ fldd d0, [r2] @ d0<- vBB
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ fmuld d2, d0, d1 @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vAA
+ fstd d2, [r9] @ vAA<- d2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_DOUBLE: /* 0xae */
+/* File: arm-vfp/OP_DIV_DOUBLE.S */
+/* File: arm-vfp/fbinopWide.S */
+ /*
+ * Generic 64-bit double-precision floating point binary operation.
+ * Provide an "instr" line that specifies an instruction that performs
+ * "d2 = d0 op d1".
+ *
+ * for: add-double, sub-double, mul-double, div-double
+ */
+ /* doubleop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ fldd d1, [r3] @ d1<- vCC
+ fldd d0, [r2] @ d0<- vBB
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ fdivd d2, d0, d1 @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vAA
+ fstd d2, [r9] @ vAA<- d2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_DOUBLE: /* 0xaf */
+/* File: armv5te/OP_REM_DOUBLE.S */
+/* EABI doesn't define a double remainder function, but libm does */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl fmod @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_INT_2ADDR: /* 0xb0 */
+/* File: armv5te/OP_ADD_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ add r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_INT_2ADDR: /* 0xb1 */
+/* File: armv5te/OP_SUB_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ sub r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_INT_2ADDR: /* 0xb2 */
+/* File: armv5te/OP_MUL_INT_2ADDR.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ mul r0, r1, r0 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_INT_2ADDR: /* 0xb3 */
+/* File: armv5te/OP_DIV_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 1
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_idiv @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_INT_2ADDR: /* 0xb4 */
+/* File: armv5te/OP_REM_INT_2ADDR.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 1
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_idivmod @ r1<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r1, r9) @ vAA<- r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_INT_2ADDR: /* 0xb5 */
+/* File: armv5te/OP_AND_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ and r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_INT_2ADDR: /* 0xb6 */
+/* File: armv5te/OP_OR_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ orr r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_INT_2ADDR: /* 0xb7 */
+/* File: armv5te/OP_XOR_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ eor r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHL_INT_2ADDR: /* 0xb8 */
+/* File: armv5te/OP_SHL_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, asl r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHR_INT_2ADDR: /* 0xb9 */
+/* File: armv5te/OP_SHR_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, asr r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_USHR_INT_2ADDR: /* 0xba */
+/* File: armv5te/OP_USHR_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, lsr r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_LONG_2ADDR: /* 0xbb */
+/* File: armv5te/OP_ADD_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r9, r9, #15
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ adds r0, r0, r2 @ optional op; may set condition codes
+ adc r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_LONG_2ADDR: /* 0xbc */
+/* File: armv5te/OP_SUB_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r9, r9, #15
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ subs r0, r0, r2 @ optional op; may set condition codes
+ sbc r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_LONG_2ADDR: /* 0xbd */
+/* File: armv5te/OP_MUL_LONG_2ADDR.S */
+ /*
+ * Signed 64-bit integer multiply, "/2addr" version.
+ *
+ * See OP_MUL_LONG for an explanation.
+ *
+ * We get a little tight on registers, so to avoid looking up &fp[A]
+ * again we stuff it into rINST.
+ */
+ /* mul-long/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r9, r9, #15
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add rINST, rFP, r9, lsl #2 @ rINST<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia rINST, {r0-r1} @ r0/r1<- vAA/vAA+1
+ mul ip, r2, r1 @ ip<- ZxW
+ umull r9, r10, r2, r0 @ r9/r10 <- ZxX
+ mla r2, r0, r3, ip @ r2<- YxX + (ZxW)
+ mov r0, rINST @ r0<- &fp[A] (free up rINST)
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ add r10, r2, r10 @ r10<- r10 + low(ZxW + (YxX))
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r0, {r9-r10} @ vAA/vAA+1<- r9/r10
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_LONG_2ADDR: /* 0xbe */
+/* File: armv5te/OP_DIV_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r9, r9, #15
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 1
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_ldivmod @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_LONG_2ADDR: /* 0xbf */
+/* File: armv5te/OP_REM_LONG_2ADDR.S */
+/* ldivmod returns quotient in r0/r1 and remainder in r2/r3 */
+/* File: armv5te/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r9, r9, #15
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 1
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_ldivmod @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r2,r3} @ vAA/vAA+1<- r2/r3
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_LONG_2ADDR: /* 0xc0 */
+/* File: armv5te/OP_AND_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r9, r9, #15
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ and r0, r0, r2 @ optional op; may set condition codes
+ and r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_LONG_2ADDR: /* 0xc1 */
+/* File: armv5te/OP_OR_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r9, r9, #15
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ orr r0, r0, r2 @ optional op; may set condition codes
+ orr r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_LONG_2ADDR: /* 0xc2 */
+/* File: armv5te/OP_XOR_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r9, r9, #15
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ eor r0, r0, r2 @ optional op; may set condition codes
+ eor r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHL_LONG_2ADDR: /* 0xc3 */
+/* File: armv5te/OP_SHL_LONG_2ADDR.S */
+ /*
+ * Long integer shift, 2addr version. vA is 64-bit value/result, vB is
+ * 32-bit shift distance.
+ */
+ /* shl-long/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r2, r3) @ r2<- vB
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ and r2, r2, #63 @ r2<- r2 & 0x3f
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+
+ mov r1, r1, asl r2 @ r1<- r1 << r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32)
+ mov r0, r0, asl r2 @ r0<- r0 << r2
+ b .LOP_SHL_LONG_2ADDR_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHR_LONG_2ADDR: /* 0xc4 */
+/* File: armv5te/OP_SHR_LONG_2ADDR.S */
+ /*
+ * Long integer shift, 2addr version. vA is 64-bit value/result, vB is
+ * 32-bit shift distance.
+ */
+ /* shr-long/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r2, r3) @ r2<- vB
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ and r2, r2, #63 @ r2<- r2 & 0x3f
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32)
+ mov r1, r1, asr r2 @ r1<- r1 >> r2
+ b .LOP_SHR_LONG_2ADDR_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_USHR_LONG_2ADDR: /* 0xc5 */
+/* File: armv5te/OP_USHR_LONG_2ADDR.S */
+ /*
+ * Long integer shift, 2addr version. vA is 64-bit value/result, vB is
+ * 32-bit shift distance.
+ */
+ /* ushr-long/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r2, r3) @ r2<- vB
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ and r2, r2, #63 @ r2<- r2 & 0x3f
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32)
+ mov r1, r1, lsr r2 @ r1<- r1 >>> r2
+ b .LOP_USHR_LONG_2ADDR_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_FLOAT_2ADDR: /* 0xc6 */
+/* File: arm-vfp/OP_ADD_FLOAT_2ADDR.S */
+/* File: arm-vfp/fbinop2addr.S */
+ /*
+ * Generic 32-bit floating point "/2addr" binary operation. Provide
+ * an "instr" line that specifies an instruction that performs
+ * "s2 = s0 op s1".
+ *
+ * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ and r9, r9, #15 @ r9<- A
+ flds s1, [r3] @ s1<- vB
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ flds s0, [r9] @ s0<- vA
+
+ fadds s2, s0, s1 @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fsts s2, [r9] @ vAA<- s2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_FLOAT_2ADDR: /* 0xc7 */
+/* File: arm-vfp/OP_SUB_FLOAT_2ADDR.S */
+/* File: arm-vfp/fbinop2addr.S */
+ /*
+ * Generic 32-bit floating point "/2addr" binary operation. Provide
+ * an "instr" line that specifies an instruction that performs
+ * "s2 = s0 op s1".
+ *
+ * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ and r9, r9, #15 @ r9<- A
+ flds s1, [r3] @ s1<- vB
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ flds s0, [r9] @ s0<- vA
+
+ fsubs s2, s0, s1 @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fsts s2, [r9] @ vAA<- s2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_FLOAT_2ADDR: /* 0xc8 */
+/* File: arm-vfp/OP_MUL_FLOAT_2ADDR.S */
+/* File: arm-vfp/fbinop2addr.S */
+ /*
+ * Generic 32-bit floating point "/2addr" binary operation. Provide
+ * an "instr" line that specifies an instruction that performs
+ * "s2 = s0 op s1".
+ *
+ * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ and r9, r9, #15 @ r9<- A
+ flds s1, [r3] @ s1<- vB
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ flds s0, [r9] @ s0<- vA
+
+ fmuls s2, s0, s1 @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fsts s2, [r9] @ vAA<- s2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_FLOAT_2ADDR: /* 0xc9 */
+/* File: arm-vfp/OP_DIV_FLOAT_2ADDR.S */
+/* File: arm-vfp/fbinop2addr.S */
+ /*
+ * Generic 32-bit floating point "/2addr" binary operation. Provide
+ * an "instr" line that specifies an instruction that performs
+ * "s2 = s0 op s1".
+ *
+ * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ and r9, r9, #15 @ r9<- A
+ flds s1, [r3] @ s1<- vB
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ flds s0, [r9] @ s0<- vA
+
+ fdivs s2, s0, s1 @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fsts s2, [r9] @ vAA<- s2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_FLOAT_2ADDR: /* 0xca */
+/* File: armv5te/OP_REM_FLOAT_2ADDR.S */
+/* EABI doesn't define a float remainder function, but libm does */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl fmodf @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_DOUBLE_2ADDR: /* 0xcb */
+/* File: arm-vfp/OP_ADD_DOUBLE_2ADDR.S */
+/* File: arm-vfp/fbinopWide2addr.S */
+ /*
+ * Generic 64-bit floating point "/2addr" binary operation. Provide
+ * an "instr" line that specifies an instruction that performs
+ * "d2 = d0 op d1".
+ *
+ * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+ * div-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ and r9, r9, #15 @ r9<- A
+ fldd d1, [r3] @ d1<- vB
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ fldd d0, [r9] @ d0<- vA
+
+ faddd d2, d0, d1 @ d2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fstd d2, [r9] @ vAA<- d2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_DOUBLE_2ADDR: /* 0xcc */
+/* File: arm-vfp/OP_SUB_DOUBLE_2ADDR.S */
+/* File: arm-vfp/fbinopWide2addr.S */
+ /*
+ * Generic 64-bit floating point "/2addr" binary operation. Provide
+ * an "instr" line that specifies an instruction that performs
+ * "d2 = d0 op d1".
+ *
+ * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+ * div-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ and r9, r9, #15 @ r9<- A
+ fldd d1, [r3] @ d1<- vB
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ fldd d0, [r9] @ d0<- vA
+
+ fsubd d2, d0, d1 @ d2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fstd d2, [r9] @ vAA<- d2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_DOUBLE_2ADDR: /* 0xcd */
+/* File: arm-vfp/OP_MUL_DOUBLE_2ADDR.S */
+/* File: arm-vfp/fbinopWide2addr.S */
+ /*
+ * Generic 64-bit floating point "/2addr" binary operation. Provide
+ * an "instr" line that specifies an instruction that performs
+ * "d2 = d0 op d1".
+ *
+ * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+ * div-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ and r9, r9, #15 @ r9<- A
+ fldd d1, [r3] @ d1<- vB
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ fldd d0, [r9] @ d0<- vA
+
+ fmuld d2, d0, d1 @ d2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fstd d2, [r9] @ vAA<- d2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_DOUBLE_2ADDR: /* 0xce */
+/* File: arm-vfp/OP_DIV_DOUBLE_2ADDR.S */
+/* File: arm-vfp/fbinopWide2addr.S */
+ /*
+ * Generic 64-bit floating point "/2addr" binary operation. Provide
+ * an "instr" line that specifies an instruction that performs
+ * "d2 = d0 op d1".
+ *
+ * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+ * div-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ and r9, r9, #15 @ r9<- A
+ fldd d1, [r3] @ d1<- vB
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ fldd d0, [r9] @ d0<- vA
+
+ fdivd d2, d0, d1 @ d2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fstd d2, [r9] @ vAA<- d2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_DOUBLE_2ADDR: /* 0xcf */
+/* File: armv5te/OP_REM_DOUBLE_2ADDR.S */
+/* EABI doesn't define a double remainder function, but libm does */
+/* File: armv5te/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r9, r9, #15
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl fmod @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_INT_LIT16: /* 0xd0 */
+/* File: armv5te/OP_ADD_INT_LIT16.S */
+/* File: armv5te/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r2) @ r0<- vB
+ and r9, r9, #15
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ add r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RSUB_INT: /* 0xd1 */
+/* File: armv5te/OP_RSUB_INT.S */
+/* this op is "rsub-int", but can be thought of as "rsub-int/lit16" */
+/* File: armv5te/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r2) @ r0<- vB
+ and r9, r9, #15
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ rsb r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_INT_LIT16: /* 0xd2 */
+/* File: armv5te/OP_MUL_INT_LIT16.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r2) @ r0<- vB
+ and r9, r9, #15
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ mul r0, r1, r0 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_INT_LIT16: /* 0xd3 */
+/* File: armv5te/OP_DIV_INT_LIT16.S */
+/* File: armv5te/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r2) @ r0<- vB
+ and r9, r9, #15
+ .if 1
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ bl __aeabi_idiv @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_INT_LIT16: /* 0xd4 */
+/* File: armv5te/OP_REM_INT_LIT16.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* File: armv5te/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r2) @ r0<- vB
+ and r9, r9, #15
+ .if 1
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ bl __aeabi_idivmod @ r1<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r1, r9) @ vAA<- r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_INT_LIT16: /* 0xd5 */
+/* File: armv5te/OP_AND_INT_LIT16.S */
+/* File: armv5te/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r2) @ r0<- vB
+ and r9, r9, #15
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ and r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_INT_LIT16: /* 0xd6 */
+/* File: armv5te/OP_OR_INT_LIT16.S */
+/* File: armv5te/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r2) @ r0<- vB
+ and r9, r9, #15
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ orr r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_INT_LIT16: /* 0xd7 */
+/* File: armv5te/OP_XOR_INT_LIT16.S */
+/* File: armv5te/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r2) @ r0<- vB
+ and r9, r9, #15
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ eor r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_INT_LIT8: /* 0xd8 */
+/* File: armv5te/OP_ADD_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ add r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RSUB_INT_LIT8: /* 0xd9 */
+/* File: armv5te/OP_RSUB_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ rsb r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_INT_LIT8: /* 0xda */
+/* File: armv5te/OP_MUL_INT_LIT8.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ mul r0, r1, r0 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_INT_LIT8: /* 0xdb */
+/* File: armv5te/OP_DIV_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 1
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_idiv @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_INT_LIT8: /* 0xdc */
+/* File: armv5te/OP_REM_INT_LIT8.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 1
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_idivmod @ r1<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r1, r9) @ vAA<- r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_INT_LIT8: /* 0xdd */
+/* File: armv5te/OP_AND_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ and r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_INT_LIT8: /* 0xde */
+/* File: armv5te/OP_OR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ orr r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_INT_LIT8: /* 0xdf */
+/* File: armv5te/OP_XOR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ eor r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHL_INT_LIT8: /* 0xe0 */
+/* File: armv5te/OP_SHL_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, asl r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHR_INT_LIT8: /* 0xe1 */
+/* File: armv5te/OP_SHR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, asr r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_USHR_INT_LIT8: /* 0xe2 */
+/* File: armv5te/OP_USHR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, lsr r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_VOLATILE: /* 0xe3 */
+/* File: armv5te/OP_IGET_VOLATILE.S */
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_VOLATILE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_VOLATILE_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_VOLATILE: /* 0xe4 */
+/* File: armv5te/OP_IPUT_VOLATILE.S */
+/* File: armv5te/OP_IPUT.S */
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_VOLATILE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_VOLATILE_finish @ yes, finish up
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_VOLATILE: /* 0xe5 */
+/* File: armv5te/OP_SGET_VOLATILE.S */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_VOLATILE_resolve @ yes, do resolve
+.LOP_SGET_VOLATILE_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ SMP_DMB @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_VOLATILE: /* 0xe6 */
+/* File: armv5te/OP_SPUT_VOLATILE.S */
+/* File: armv5te/OP_SPUT.S */
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SPUT_VOLATILE_resolve @ yes, do resolve
+.LOP_SPUT_VOLATILE_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SMP_DMB @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_OBJECT_VOLATILE: /* 0xe7 */
+/* File: armv5te/OP_IGET_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_OBJECT_VOLATILE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_OBJECT_VOLATILE_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_WIDE_VOLATILE: /* 0xe8 */
+/* File: armv5te/OP_IGET_WIDE_VOLATILE.S */
+/* File: armv5te/OP_IGET_WIDE.S */
+ /*
+ * Wide 32-bit instance field get.
+ */
+ /* iget-wide vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_WIDE_VOLATILE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_WIDE_VOLATILE_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_WIDE_VOLATILE: /* 0xe9 */
+/* File: armv5te/OP_IPUT_WIDE_VOLATILE.S */
+/* File: armv5te/OP_IPUT_WIDE.S */
+ /* iput-wide vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_WIDE_VOLATILE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_WIDE_VOLATILE_finish @ yes, finish up
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_WIDE_VOLATILE: /* 0xea */
+/* File: armv5te/OP_SGET_WIDE_VOLATILE.S */
+/* File: armv5te/OP_SGET_WIDE.S */
+ /*
+ * 64-bit SGET handler.
+ */
+ /* sget-wide vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_WIDE_VOLATILE_resolve @ yes, do resolve
+.LOP_SGET_WIDE_VOLATILE_finish:
+ mov r9, rINST, lsr #8 @ r9<- AA
+ .if 1
+ add r0, r0, #offStaticField_value @ r0<- pointer to data
+ bl dvmQuasiAtomicRead64 @ r0/r1<- contents of field
+ .else
+ ldrd r0, [r0, #offStaticField_value] @ r0/r1<- field value (aligned)
+ .endif
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_WIDE_VOLATILE: /* 0xeb */
+/* File: armv5te/OP_SPUT_WIDE_VOLATILE.S */
+/* File: armv5te/OP_SPUT_WIDE.S */
+ /*
+ * 64-bit SPUT handler.
+ */
+ /* sput-wide vAA, field@BBBB */
+ ldr r0, [rGLUE, #offGlue_methodClassDex] @ r0<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r0, [r0, #offDvmDex_pResFields] @ r0<- dvmDex->pResFields
+ mov r9, rINST, lsr #8 @ r9<- AA
+ ldr r2, [r0, r1, lsl #2] @ r2<- resolved StaticField ptr
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ cmp r2, #0 @ is resolved entry null?
+ beq .LOP_SPUT_WIDE_VOLATILE_resolve @ yes, do resolve
+.LOP_SPUT_WIDE_VOLATILE_finish: @ field ptr in r2, AA in r9
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ GET_INST_OPCODE(r10) @ extract opcode from rINST
+ .if 1
+ add r2, r2, #offStaticField_value @ r2<- pointer to data
+ bl dvmQuasiAtomicSwap64 @ stores r0/r1 into addr r2
+ .else
+ strd r0, [r2, #offStaticField_value] @ field<- vAA/vAA+1
+ .endif
+ GOTO_OPCODE(r10) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_BREAKPOINT: /* 0xec */
+/* File: armv5te/OP_BREAKPOINT.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_THROW_VERIFICATION_ERROR: /* 0xed */
+/* File: armv5te/OP_THROW_VERIFICATION_ERROR.S */
+ /*
+ * Handle a throw-verification-error instruction. This throws an
+ * exception for an error discovered during verification. The
+ * exception is indicated by AA, with some detail provided by BBBB.
+ */
+ /* op AA, ref@BBBB */
+ ldr r0, [rGLUE, #offGlue_method] @ r0<- glue->method
+ FETCH(r2, 1) @ r2<- BBBB
+ EXPORT_PC() @ export the PC
+ mov r1, rINST, lsr #8 @ r1<- AA
+ bl dvmThrowVerificationError @ always throws
+ b common_exceptionThrown @ handle exception
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_EXECUTE_INLINE: /* 0xee */
+/* File: armv5te/OP_EXECUTE_INLINE.S */
+ /*
+ * Execute a "native inline" instruction.
+ *
+ * We need to call an InlineOp4Func:
+ * bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+ *
+ * The first four args are in r0-r3, pointer to return value storage
+ * is on the stack. The function's return value is a flag that tells
+ * us if an exception was thrown.
+ */
+ /* [opt] execute-inline vAA, {vC, vD, vE, vF}, inline@BBBB */
+ FETCH(r10, 1) @ r10<- BBBB
+ add r1, rGLUE, #offGlue_retval @ r1<- &glue->retval
+ EXPORT_PC() @ can throw
+ sub sp, sp, #8 @ make room for arg, +64 bit align
+ mov r0, rINST, lsr #12 @ r0<- B
+ str r1, [sp] @ push &glue->retval
+ bl .LOP_EXECUTE_INLINE_continue @ make call; will return after
+ add sp, sp, #8 @ pop stack
+ cmp r0, #0 @ test boolean result of inline
+ beq common_exceptionThrown @ returned false, handle exception
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_EXECUTE_INLINE_RANGE: /* 0xef */
+/* File: armv5te/OP_EXECUTE_INLINE_RANGE.S */
+ /*
+ * Execute a "native inline" instruction, using "/range" semantics.
+ * Same idea as execute-inline, but we get the args differently.
+ *
+ * We need to call an InlineOp4Func:
+ * bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+ *
+ * The first four args are in r0-r3, pointer to return value storage
+ * is on the stack. The function's return value is a flag that tells
+ * us if an exception was thrown.
+ */
+ /* [opt] execute-inline/range {vCCCC..v(CCCC+AA-1)}, inline@BBBB */
+ FETCH(r10, 1) @ r10<- BBBB
+ add r1, rGLUE, #offGlue_retval @ r1<- &glue->retval
+ EXPORT_PC() @ can throw
+ sub sp, sp, #8 @ make room for arg, +64 bit align
+ mov r0, rINST, lsr #8 @ r0<- AA
+ str r1, [sp] @ push &glue->retval
+ bl .LOP_EXECUTE_INLINE_RANGE_continue @ make call; will return after
+ add sp, sp, #8 @ pop stack
+ cmp r0, #0 @ test boolean result of inline
+ beq common_exceptionThrown @ returned false, handle exception
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_DIRECT_EMPTY: /* 0xf0 */
+/* File: armv5te/OP_INVOKE_DIRECT_EMPTY.S */
+ /*
+ * invoke-direct-empty is a no-op in a "standard" interpreter.
+ */
+ FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ GOTO_OPCODE(ip) @ execute it
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_F1: /* 0xf1 */
+/* File: armv5te/OP_UNUSED_F1.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_QUICK: /* 0xf2 */
+/* File: armv5te/OP_IGET_QUICK.S */
+ /* For: iget-quick, iget-object-quick */
+ /* op vA, vB, offset@CCCC */
+ mov r2, rINST, lsr #12 @ r2<- B
+ GET_VREG(r3, r2) @ r3<- object we're operating on
+ FETCH(r1, 1) @ r1<- field byte offset
+ cmp r3, #0 @ check object for null
+ mov r2, rINST, lsr #8 @ r2<- A(+)
+ beq common_errNullObject @ object was null
+ ldr r0, [r3, r1] @ r0<- obj.field (always 32 bits)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_WIDE_QUICK: /* 0xf3 */
+/* File: armv5te/OP_IGET_WIDE_QUICK.S */
+ /* iget-wide-quick vA, vB, offset@CCCC */
+ mov r2, rINST, lsr #12 @ r2<- B
+ GET_VREG(r3, r2) @ r3<- object we're operating on
+ FETCH(ip, 1) @ ip<- field byte offset
+ cmp r3, #0 @ check object for null
+ mov r2, rINST, lsr #8 @ r2<- A(+)
+ beq common_errNullObject @ object was null
+ ldrd r0, [r3, ip] @ r0<- obj.field (64 bits, aligned)
+ and r2, r2, #15
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ add r3, rFP, r2, lsl #2 @ r3<- &fp[A]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ fp[A]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_OBJECT_QUICK: /* 0xf4 */
+/* File: armv5te/OP_IGET_OBJECT_QUICK.S */
+/* File: armv5te/OP_IGET_QUICK.S */
+ /* For: iget-quick, iget-object-quick */
+ /* op vA, vB, offset@CCCC */
+ mov r2, rINST, lsr #12 @ r2<- B
+ GET_VREG(r3, r2) @ r3<- object we're operating on
+ FETCH(r1, 1) @ r1<- field byte offset
+ cmp r3, #0 @ check object for null
+ mov r2, rINST, lsr #8 @ r2<- A(+)
+ beq common_errNullObject @ object was null
+ ldr r0, [r3, r1] @ r0<- obj.field (always 32 bits)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_QUICK: /* 0xf5 */
+/* File: armv5te/OP_IPUT_QUICK.S */
+ /* For: iput-quick */
+ /* op vA, vB, offset@CCCC */
+ mov r2, rINST, lsr #12 @ r2<- B
+ GET_VREG(r3, r2) @ r3<- fp[B], the object pointer
+ FETCH(r1, 1) @ r1<- field byte offset
+ cmp r3, #0 @ check object for null
+ mov r2, rINST, lsr #8 @ r2<- A(+)
+ beq common_errNullObject @ object was null
+ and r2, r2, #15
+ GET_VREG(r0, r2) @ r0<- fp[A]
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ str r0, [r3, r1] @ obj.field (always 32 bits)<- r0
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_WIDE_QUICK: /* 0xf6 */
+/* File: armv5te/OP_IPUT_WIDE_QUICK.S */
+ /* iput-wide-quick vA, vB, offset@CCCC */
+ mov r0, rINST, lsr #8 @ r0<- A(+)
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r0, r0, #15
+ GET_VREG(r2, r1) @ r2<- fp[B], the object pointer
+ add r3, rFP, r0, lsl #2 @ r3<- &fp[A]
+ cmp r2, #0 @ check object for null
+ ldmia r3, {r0-r1} @ r0/r1<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH(r3, 1) @ r3<- field byte offset
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ strd r0, [r2, r3] @ obj.field (64 bits, aligned)<- r0/r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_OBJECT_QUICK: /* 0xf7 */
+/* File: armv5te/OP_IPUT_OBJECT_QUICK.S */
+ /* For: iput-object-quick */
+ /* op vA, vB, offset@CCCC */
+ mov r2, rINST, lsr #12 @ r2<- B
+ GET_VREG(r3, r2) @ r3<- fp[B], the object pointer
+ FETCH(r1, 1) @ r1<- field byte offset
+ cmp r3, #0 @ check object for null
+ mov r2, rINST, lsr #8 @ r2<- A(+)
+ beq common_errNullObject @ object was null
+ and r2, r2, #15
+ GET_VREG(r0, r2) @ r0<- fp[A]
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ str r0, [r3, r1] @ obj.field (always 32 bits)<- r0
+ cmp r0, #0
+ strneb r2, [r2, r3, lsr #GC_CARD_SHIFT] @ mark card based on obj head
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_VIRTUAL_QUICK: /* 0xf8 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK.S */
+ /*
+ * Handle an optimized virtual method call.
+ *
+ * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r3, 2) @ r3<- FEDC or CCCC
+ FETCH(r1, 1) @ r1<- BBBB
+ .if (!0)
+ and r3, r3, #15 @ r3<- C (or stays CCCC)
+ .endif
+ GET_VREG(r2, r3) @ r2<- vC ("this" ptr)
+ cmp r2, #0 @ is "this" null?
+ beq common_errNullObject @ null "this", throw exception
+ ldr r2, [r2, #offObject_clazz] @ r2<- thisPtr->clazz
+ ldr r2, [r2, #offClassObject_vtable] @ r2<- thisPtr->clazz->vtable
+ EXPORT_PC() @ invoke must export
+ ldr r0, [r2, r1, lsl #2] @ r3<- vtable[BBBB]
+ bl common_invokeMethodNoRange @ continue on
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_VIRTUAL_QUICK_RANGE: /* 0xf9 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK_RANGE.S */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK.S */
+ /*
+ * Handle an optimized virtual method call.
+ *
+ * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r3, 2) @ r3<- FEDC or CCCC
+ FETCH(r1, 1) @ r1<- BBBB
+ .if (!1)
+ and r3, r3, #15 @ r3<- C (or stays CCCC)
+ .endif
+ GET_VREG(r2, r3) @ r2<- vC ("this" ptr)
+ cmp r2, #0 @ is "this" null?
+ beq common_errNullObject @ null "this", throw exception
+ ldr r2, [r2, #offObject_clazz] @ r2<- thisPtr->clazz
+ ldr r2, [r2, #offClassObject_vtable] @ r2<- thisPtr->clazz->vtable
+ EXPORT_PC() @ invoke must export
+ ldr r0, [r2, r1, lsl #2] @ r3<- vtable[BBBB]
+ bl common_invokeMethodRange @ continue on
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_SUPER_QUICK: /* 0xfa */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK.S */
+ /*
+ * Handle an optimized "super" method call.
+ *
+ * for: [opt] invoke-super-quick, invoke-super-quick/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ .if (!0)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r2, [r2, #offMethod_clazz] @ r2<- method->clazz
+ EXPORT_PC() @ must export for invoke
+ ldr r2, [r2, #offClassObject_super] @ r2<- method->clazz->super
+ GET_VREG(r3, r10) @ r3<- "this"
+ ldr r2, [r2, #offClassObject_vtable] @ r2<- ...clazz->super->vtable
+ cmp r3, #0 @ null "this" ref?
+ ldr r0, [r2, r1, lsl #2] @ r0<- super->vtable[BBBB]
+ beq common_errNullObject @ "this" is null, throw exception
+ bl common_invokeMethodNoRange @ continue on
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_SUPER_QUICK_RANGE: /* 0xfb */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK_RANGE.S */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK.S */
+ /*
+ * Handle an optimized "super" method call.
+ *
+ * for: [opt] invoke-super-quick, invoke-super-quick/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ .if (!1)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r2, [r2, #offMethod_clazz] @ r2<- method->clazz
+ EXPORT_PC() @ must export for invoke
+ ldr r2, [r2, #offClassObject_super] @ r2<- method->clazz->super
+ GET_VREG(r3, r10) @ r3<- "this"
+ ldr r2, [r2, #offClassObject_vtable] @ r2<- ...clazz->super->vtable
+ cmp r3, #0 @ null "this" ref?
+ ldr r0, [r2, r1, lsl #2] @ r0<- super->vtable[BBBB]
+ beq common_errNullObject @ "this" is null, throw exception
+ bl common_invokeMethodRange @ continue on
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */
+/* File: armv5te/OP_IPUT_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_IPUT_OBJECT.S */
+ /*
+ * 32-bit instance field put.
+ *
+ * for: iput-object, iput-object-volatile
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_OBJECT_VOLATILE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_OBJECT_VOLATILE_finish @ yes, finish up
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_OBJECT_VOLATILE: /* 0xfd */
+/* File: armv5te/OP_SGET_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_OBJECT_VOLATILE_resolve @ yes, do resolve
+.LOP_SGET_OBJECT_VOLATILE_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ SMP_DMB @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */
+/* File: armv5te/OP_SPUT_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_SPUT_OBJECT.S */
+ /*
+ * 32-bit SPUT handler for objects
+ *
+ * for: sput-object, sput-object-volatile
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_SPUT_OBJECT_VOLATILE_finish @ no, continue
+ ldr r9, [rGLUE, #offGlue_method] @ r9<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r9, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_OBJECT_VOLATILE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_FF: /* 0xff */
+/* File: armv5te/OP_UNUSED_FF.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+
+ .balign 64
+ .size dvmAsmInstructionStart, .-dvmAsmInstructionStart
+ .global dvmAsmInstructionEnd
+dvmAsmInstructionEnd:
+
+/*
+ * ===========================================================================
+ * Sister implementations
+ * ===========================================================================
+ */
+ .global dvmAsmSisterStart
+ .type dvmAsmSisterStart, %function
+ .text
+ .balign 4
+dvmAsmSisterStart:
+
+/* continuation for OP_CONST_STRING */
+
+ /*
+ * Continuation if the String has not yet been resolved.
+ * r1: BBBB (String ref)
+ * r9: target register
+ */
+.LOP_CONST_STRING_resolve:
+ EXPORT_PC()
+ ldr r0, [rGLUE, #offGlue_method] @ r0<- glue->method
+ ldr r0, [r0, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveString @ r0<- String reference
+ cmp r0, #0 @ failed?
+ beq common_exceptionThrown @ yup, handle the exception
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_CONST_STRING_JUMBO */
+
+ /*
+ * Continuation if the String has not yet been resolved.
+ * r1: BBBBBBBB (String ref)
+ * r9: target register
+ */
+.LOP_CONST_STRING_JUMBO_resolve:
+ EXPORT_PC()
+ ldr r0, [rGLUE, #offGlue_method] @ r0<- glue->method
+ ldr r0, [r0, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveString @ r0<- String reference
+ cmp r0, #0 @ failed?
+ beq common_exceptionThrown @ yup, handle the exception
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_CONST_CLASS */
+
+ /*
+ * Continuation if the Class has not yet been resolved.
+ * r1: BBBB (Class ref)
+ * r9: target register
+ */
+.LOP_CONST_CLASS_resolve:
+ EXPORT_PC()
+ ldr r0, [rGLUE, #offGlue_method] @ r0<- glue->method
+ mov r2, #1 @ r2<- true
+ ldr r0, [r0, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- Class reference
+ cmp r0, #0 @ failed?
+ beq common_exceptionThrown @ yup, handle the exception
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_CHECK_CAST */
+
+ /*
+ * Trivial test failed, need to perform full check. This is common.
+ * r0 holds obj->clazz
+ * r1 holds class resolved from BBBB
+ * r9 holds object
+ */
+.LOP_CHECK_CAST_fullcheck:
+ bl dvmInstanceofNonTrivial @ r0<- boolean result
+ cmp r0, #0 @ failed?
+ bne .LOP_CHECK_CAST_okay @ no, success
+
+ @ A cast has failed. We need to throw a ClassCastException with the
+ @ class of the object that failed to be cast.
+ EXPORT_PC() @ about to throw
+ ldr r3, [r9, #offObject_clazz] @ r3<- obj->clazz
+ ldr r0, .LstrClassCastExceptionPtr
+ ldr r1, [r3, #offClassObject_descriptor] @ r1<- obj->clazz->descriptor
+ bl dvmThrowExceptionWithClassMessage
+ b common_exceptionThrown
+
+ /*
+ * Resolution required. This is the least-likely path.
+ *
+ * r2 holds BBBB
+ * r9 holds object
+ */
+.LOP_CHECK_CAST_resolve:
+ EXPORT_PC() @ resolve() could throw
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ mov r1, r2 @ r1<- BBBB
+ mov r2, #0 @ r2<- false
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- resolved ClassObject ptr
+ cmp r0, #0 @ got null?
+ beq common_exceptionThrown @ yes, handle exception
+ mov r1, r0 @ r1<- class resolved from BBB
+ ldr r0, [r9, #offObject_clazz] @ r0<- obj->clazz
+ b .LOP_CHECK_CAST_resolved @ pick up where we left off
+
+.LstrClassCastExceptionPtr:
+ .word .LstrClassCastException
+
+/* continuation for OP_INSTANCE_OF */
+
+ /*
+ * Trivial test failed, need to perform full check. This is common.
+ * r0 holds obj->clazz
+ * r1 holds class resolved from BBBB
+ * r9 holds A
+ */
+.LOP_INSTANCE_OF_fullcheck:
+ bl dvmInstanceofNonTrivial @ r0<- boolean result
+ @ fall through to OP_INSTANCE_OF_store
+
+ /*
+ * r0 holds boolean result
+ * r9 holds A
+ */
+.LOP_INSTANCE_OF_store:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r0, r9) @ vA<- r0
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+ /*
+ * Trivial test succeeded, save and bail.
+ * r9 holds A
+ */
+.LOP_INSTANCE_OF_trivial:
+ mov r0, #1 @ indicate success
+ @ could b OP_INSTANCE_OF_store, but copying is faster and cheaper
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r0, r9) @ vA<- r0
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+ /*
+ * Resolution required. This is the least-likely path.
+ *
+ * r3 holds BBBB
+ * r9 holds A
+ */
+.LOP_INSTANCE_OF_resolve:
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [rGLUE, #offGlue_method] @ r0<- glue->method
+ mov r1, r3 @ r1<- BBBB
+ mov r2, #1 @ r2<- true
+ ldr r0, [r0, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- resolved ClassObject ptr
+ cmp r0, #0 @ got null?
+ beq common_exceptionThrown @ yes, handle exception
+ mov r1, r0 @ r1<- class resolved from BBB
+ mov r3, rINST, lsr #12 @ r3<- B
+ GET_VREG(r0, r3) @ r0<- vB (object)
+ ldr r0, [r0, #offObject_clazz] @ r0<- obj->clazz
+ b .LOP_INSTANCE_OF_resolved @ pick up where we left off
+
+/* continuation for OP_NEW_INSTANCE */
+
+ .balign 32 @ minimize cache lines
+.LOP_NEW_INSTANCE_finish: @ r0=new object
+ mov r3, rINST, lsr #8 @ r3<- AA
+ cmp r0, #0 @ failed?
+ beq common_exceptionThrown @ yes, handle the exception
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r3) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+ /*
+ * Class initialization required.
+ *
+ * r0 holds class object
+ */
+.LOP_NEW_INSTANCE_needinit:
+ mov r9, r0 @ save r0
+ bl dvmInitClass @ initialize class
+ cmp r0, #0 @ check boolean result
+ mov r0, r9 @ restore r0
+ bne .LOP_NEW_INSTANCE_initialized @ success, continue
+ b common_exceptionThrown @ failed, deal with init exception
+
+ /*
+ * Resolution required. This is the least-likely path.
+ *
+ * r1 holds BBBB
+ */
+.LOP_NEW_INSTANCE_resolve:
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ mov r2, #0 @ r2<- false
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- resolved ClassObject ptr
+ cmp r0, #0 @ got null?
+ bne .LOP_NEW_INSTANCE_resolved @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+.LstrInstantiationErrorPtr:
+ .word .LstrInstantiationError
+
+/* continuation for OP_NEW_ARRAY */
+
+
+ /*
+ * Resolve class. (This is an uncommon case.)
+ *
+ * r1 holds array length
+ * r2 holds class ref CCCC
+ */
+.LOP_NEW_ARRAY_resolve:
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ mov r9, r1 @ r9<- length (save)
+ mov r1, r2 @ r1<- CCCC
+ mov r2, #0 @ r2<- false
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- call(clazz, ref)
+ cmp r0, #0 @ got null?
+ mov r1, r9 @ r1<- length (restore)
+ beq common_exceptionThrown @ yes, handle exception
+ @ fall through to OP_NEW_ARRAY_finish
+
+ /*
+ * Finish allocation.
+ *
+ * r0 holds class
+ * r1 holds array length
+ */
+.LOP_NEW_ARRAY_finish:
+ mov r2, #ALLOC_DONT_TRACK @ don't track in local refs table
+ bl dvmAllocArrayByClass @ r0<- call(clazz, length, flags)
+ cmp r0, #0 @ failed?
+ mov r2, rINST, lsr #8 @ r2<- A+
+ beq common_exceptionThrown @ yes, handle the exception
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ vA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_FILLED_NEW_ARRAY */
+
+ /*
+ * On entry:
+ * r0 holds array class
+ * r10 holds AA or BA
+ */
+.LOP_FILLED_NEW_ARRAY_continue:
+ ldr r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor
+ mov r2, #ALLOC_DONT_TRACK @ r2<- alloc flags
+ ldrb rINST, [r3, #1] @ rINST<- descriptor[1]
+ .if 0
+ mov r1, r10 @ r1<- AA (length)
+ .else
+ mov r1, r10, lsr #4 @ r1<- B (length)
+ .endif
+ cmp rINST, #'I' @ array of ints?
+ cmpne rINST, #'L' @ array of objects?
+ cmpne rINST, #'[' @ array of arrays?
+ mov r9, r1 @ save length in r9
+ bne .LOP_FILLED_NEW_ARRAY_notimpl @ no, not handled yet
+ bl dvmAllocArrayByClass @ r0<- call(arClass, length, flags)
+ cmp r0, #0 @ null return?
+ beq common_exceptionThrown @ alloc failed, handle exception
+
+ FETCH(r1, 2) @ r1<- FEDC or CCCC
+ str r0, [rGLUE, #offGlue_retval] @ retval.l <- new array
+ str rINST, [rGLUE, #offGlue_retval+4] @ retval.h <- type
+ add r0, r0, #offArrayObject_contents @ r0<- newArray->contents
+ subs r9, r9, #1 @ length--, check for neg
+ FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST
+ bmi 2f @ was zero, bail
+
+ @ copy values from registers into the array
+ @ r0=array, r1=CCCC/FEDC, r9=length (from AA or B), r10=AA/BA
+ .if 0
+ add r2, rFP, r1, lsl #2 @ r2<- &fp[CCCC]
+1: ldr r3, [r2], #4 @ r3<- *r2++
+ subs r9, r9, #1 @ count--
+ str r3, [r0], #4 @ *contents++ = vX
+ bpl 1b
+ @ continue at 2
+ .else
+ cmp r9, #4 @ length was initially 5?
+ and r2, r10, #15 @ r2<- A
+ bne 1f @ <= 4 args, branch
+ GET_VREG(r3, r2) @ r3<- vA
+ sub r9, r9, #1 @ count--
+ str r3, [r0, #16] @ contents[4] = vA
+1: and r2, r1, #15 @ r2<- F/E/D/C
+ GET_VREG(r3, r2) @ r3<- vF/vE/vD/vC
+ mov r1, r1, lsr #4 @ r1<- next reg in low 4
+ subs r9, r9, #1 @ count--
+ str r3, [r0], #4 @ *contents++ = vX
+ bpl 1b
+ @ continue at 2
+ .endif
+
+2:
+ ldr r0, [rGLUE, #offGlue_retval] @ r0<- object
+ ldr r1, [rGLUE, #offGlue_retval+4] @ r1<- type
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ cmp r1, #'I' @ Is int array?
+ strneb r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card based on object head
+ GOTO_OPCODE(ip) @ execute it
+
+ /*
+ * Throw an exception indicating that we have not implemented this
+ * mode of filled-new-array.
+ */
+.LOP_FILLED_NEW_ARRAY_notimpl:
+ ldr r0, .L_strInternalError
+ ldr r1, .L_strFilledNewArrayNotImpl
+ bl dvmThrowException
+ b common_exceptionThrown
+
+ .if (!0) @ define in one or the other, not both
+.L_strFilledNewArrayNotImpl:
+ .word .LstrFilledNewArrayNotImpl
+.L_strInternalError:
+ .word .LstrInternalError
+ .endif
+
+/* continuation for OP_FILLED_NEW_ARRAY_RANGE */
+
+ /*
+ * On entry:
+ * r0 holds array class
+ * r10 holds AA or BA
+ */
+.LOP_FILLED_NEW_ARRAY_RANGE_continue:
+ ldr r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor
+ mov r2, #ALLOC_DONT_TRACK @ r2<- alloc flags
+ ldrb rINST, [r3, #1] @ rINST<- descriptor[1]
+ .if 1
+ mov r1, r10 @ r1<- AA (length)
+ .else
+ mov r1, r10, lsr #4 @ r1<- B (length)
+ .endif
+ cmp rINST, #'I' @ array of ints?
+ cmpne rINST, #'L' @ array of objects?
+ cmpne rINST, #'[' @ array of arrays?
+ mov r9, r1 @ save length in r9
+ bne .LOP_FILLED_NEW_ARRAY_RANGE_notimpl @ no, not handled yet
+ bl dvmAllocArrayByClass @ r0<- call(arClass, length, flags)
+ cmp r0, #0 @ null return?
+ beq common_exceptionThrown @ alloc failed, handle exception
+
+ FETCH(r1, 2) @ r1<- FEDC or CCCC
+ str r0, [rGLUE, #offGlue_retval] @ retval.l <- new array
+ str rINST, [rGLUE, #offGlue_retval+4] @ retval.h <- type
+ add r0, r0, #offArrayObject_contents @ r0<- newArray->contents
+ subs r9, r9, #1 @ length--, check for neg
+ FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST
+ bmi 2f @ was zero, bail
+
+ @ copy values from registers into the array
+ @ r0=array, r1=CCCC/FEDC, r9=length (from AA or B), r10=AA/BA
+ .if 1
+ add r2, rFP, r1, lsl #2 @ r2<- &fp[CCCC]
+1: ldr r3, [r2], #4 @ r3<- *r2++
+ subs r9, r9, #1 @ count--
+ str r3, [r0], #4 @ *contents++ = vX
+ bpl 1b
+ @ continue at 2
+ .else
+ cmp r9, #4 @ length was initially 5?
+ and r2, r10, #15 @ r2<- A
+ bne 1f @ <= 4 args, branch
+ GET_VREG(r3, r2) @ r3<- vA
+ sub r9, r9, #1 @ count--
+ str r3, [r0, #16] @ contents[4] = vA
+1: and r2, r1, #15 @ r2<- F/E/D/C
+ GET_VREG(r3, r2) @ r3<- vF/vE/vD/vC
+ mov r1, r1, lsr #4 @ r1<- next reg in low 4
+ subs r9, r9, #1 @ count--
+ str r3, [r0], #4 @ *contents++ = vX
+ bpl 1b
+ @ continue at 2
+ .endif
+
+2:
+ ldr r0, [rGLUE, #offGlue_retval] @ r0<- object
+ ldr r1, [rGLUE, #offGlue_retval+4] @ r1<- type
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ cmp r1, #'I' @ Is int array?
+ strneb r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card based on object head
+ GOTO_OPCODE(ip) @ execute it
+
+ /*
+ * Throw an exception indicating that we have not implemented this
+ * mode of filled-new-array.
+ */
+.LOP_FILLED_NEW_ARRAY_RANGE_notimpl:
+ ldr r0, .L_strInternalError
+ ldr r1, .L_strFilledNewArrayNotImpl
+ bl dvmThrowException
+ b common_exceptionThrown
+
+ .if (!1) @ define in one or the other, not both
+.L_strFilledNewArrayNotImpl:
+ .word .LstrFilledNewArrayNotImpl
+.L_strInternalError:
+ .word .LstrInternalError
+ .endif
+
+/* continuation for OP_CMPL_FLOAT */
+.LOP_CMPL_FLOAT_finish:
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_CMPG_FLOAT */
+.LOP_CMPG_FLOAT_finish:
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_CMPL_DOUBLE */
+.LOP_CMPL_DOUBLE_finish:
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_CMPG_DOUBLE */
+.LOP_CMPG_DOUBLE_finish:
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_CMP_LONG */
+
+.LOP_CMP_LONG_less:
+ mvn r1, #0 @ r1<- -1
+ @ Want to cond code the next mov so we can avoid branch, but don't see it;
+ @ instead, we just replicate the tail end.
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r9) @ vAA<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+.LOP_CMP_LONG_greater:
+ mov r1, #1 @ r1<- 1
+ @ fall through to _finish
+
+.LOP_CMP_LONG_finish:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r9) @ vAA<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_AGET_WIDE */
+
+.LOP_AGET_WIDE_finish:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldrd r2, [r0, #offArrayObject_contents] @ r2/r3<- vBB[vCC]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r2-r3} @ vAA/vAA+1<- r2/r3
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_APUT_WIDE */
+
+.LOP_APUT_WIDE_finish:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldmia r9, {r2-r3} @ r2/r3<- vAA/vAA+1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ strd r2, [r0, #offArrayObject_contents] @ r2/r3<- vBB[vCC]
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_APUT_OBJECT */
+ /*
+ * On entry:
+ * rINST = vBB (arrayObj)
+ * r9 = vAA (obj)
+ * r10 = offset into array (vBB + vCC * width)
+ */
+.LOP_APUT_OBJECT_finish:
+ cmp r9, #0 @ storing null reference?
+ beq .LOP_APUT_OBJECT_skip_check @ yes, skip type checks
+ ldr r0, [r9, #offObject_clazz] @ r0<- obj->clazz
+ ldr r1, [rINST, #offObject_clazz] @ r1<- arrayObj->clazz
+ bl dvmCanPutArrayElement @ test object type vs. array type
+ cmp r0, #0 @ okay?
+ beq common_errArrayStore @ no
+ mov r1, rINST @ r1<- arrayObj
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldr r2, [rGLUE, #offGlue_cardTable] @ get biased CT base
+ add r10, #offArrayObject_contents @ r0<- pointer to slot
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ str r9, [r10] @ vBB[vCC]<- vAA
+ strb r2, [r2, r1, lsr #GC_CARD_SHIFT] @ mark card using object head
+ GOTO_OPCODE(ip) @ jump to next instruction
+.LOP_APUT_OBJECT_skip_check:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ str r9, [r10, #offArrayObject_contents] @ vBB[vCC]<- vAA
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_finish:
+ @bl common_squeak0
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_WIDE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_WIDE_finish:
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ .if 0
+ add r0, r9, r3 @ r0<- address of field
+ bl dvmQuasiAtomicRead64 @ r0/r1<- contents of field
+ .else
+ ldrd r0, [r9, r3] @ r0/r1<- obj.field (64-bit align ok)
+ .endif
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ add r3, rFP, r2, lsl #2 @ r3<- &fp[A]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ fp[A]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_OBJECT */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_OBJECT_finish:
+ @bl common_squeak0
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_BOOLEAN */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_BOOLEAN_finish:
+ @bl common_squeak1
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_BYTE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_BYTE_finish:
+ @bl common_squeak2
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_CHAR */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_CHAR_finish:
+ @bl common_squeak3
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_SHORT */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_SHORT_finish:
+ @bl common_squeak4
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_finish:
+ @bl common_squeak0
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT_WIDE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_WIDE_finish:
+ mov r2, rINST, lsr #8 @ r2<- A+
+ cmp r9, #0 @ check object for null
+ and r2, r2, #15 @ r2<- A
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ add r2, rFP, r2, lsl #2 @ r3<- &fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldmia r2, {r0-r1} @ r0/r1<- fp[A]
+ GET_INST_OPCODE(r10) @ extract opcode from rINST
+ .if 0
+ add r2, r9, r3 @ r2<- target address
+ bl dvmQuasiAtomicSwap64 @ stores r0/r1 into addr r2
+ .else
+ strd r0, [r9, r3] @ obj.field (64 bits, aligned)<- r0/r1
+ .endif
+ GOTO_OPCODE(r10) @ jump to next instruction
+
+/* continuation for OP_IPUT_OBJECT */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_OBJECT_finish:
+ @bl common_squeak0
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r0, [r9, r3] @ obj.field (32 bits)<- r0
+ cmp r0, #0 @ stored a null reference?
+ strneb r2, [r2, r9, lsr #GC_CARD_SHIFT] @ mark card if not
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT_BOOLEAN */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_BOOLEAN_finish:
+ @bl common_squeak1
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT_BYTE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_BYTE_finish:
+ @bl common_squeak2
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT_CHAR */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_CHAR_finish:
+ @bl common_squeak3
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT_SHORT */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_SHORT_finish:
+ @bl common_squeak4
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SGET */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SGET_WIDE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ *
+ * Returns StaticField pointer in r0.
+ */
+.LOP_SGET_WIDE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_WIDE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SGET_OBJECT */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_OBJECT_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_OBJECT_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SGET_BOOLEAN */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_BOOLEAN_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_BOOLEAN_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SGET_BYTE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_BYTE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_BYTE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SGET_CHAR */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_CHAR_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_CHAR_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SGET_SHORT */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_SHORT_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_SHORT_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SPUT_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_WIDE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ * r9: &fp[AA]
+ *
+ * Returns StaticField pointer in r2.
+ */
+.LOP_SPUT_WIDE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ mov r2, r0 @ copy to r2
+ bne .LOP_SPUT_WIDE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_OBJECT */
+.LOP_SPUT_OBJECT_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ ldr r9, [r0, #offField_clazz] @ r9<- field->clazz
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ cmp r1, #0 @ stored a null object?
+ strneb r2, [r2, r9, lsr #GC_CARD_SHIFT] @ mark card based on obj head
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SPUT_BOOLEAN */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SPUT_BOOLEAN_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_BOOLEAN_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_BYTE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SPUT_BYTE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_BYTE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_CHAR */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SPUT_CHAR_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_CHAR_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_SHORT */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SPUT_SHORT_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_SHORT_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_INVOKE_VIRTUAL */
+
+ /*
+ * At this point:
+ * r0 = resolved base method
+ * r10 = C or CCCC (index of first arg, which is the "this" ptr)
+ */
+.LOP_INVOKE_VIRTUAL_continue:
+ GET_VREG(r1, r10) @ r1<- "this" ptr
+ ldrh r2, [r0, #offMethod_methodIndex] @ r2<- baseMethod->methodIndex
+ cmp r1, #0 @ is "this" null?
+ beq common_errNullObject @ null "this", throw exception
+ ldr r3, [r1, #offObject_clazz] @ r1<- thisPtr->clazz
+ ldr r3, [r3, #offClassObject_vtable] @ r3<- thisPtr->clazz->vtable
+ ldr r0, [r3, r2, lsl #2] @ r3<- vtable[methodIndex]
+ bl common_invokeMethodNoRange @ continue on
+
+/* continuation for OP_INVOKE_SUPER */
+
+ /*
+ * At this point:
+ * r0 = resolved base method
+ * r9 = method->clazz
+ */
+.LOP_INVOKE_SUPER_continue:
+ ldr r1, [r9, #offClassObject_super] @ r1<- method->clazz->super
+ ldrh r2, [r0, #offMethod_methodIndex] @ r2<- baseMethod->methodIndex
+ ldr r3, [r1, #offClassObject_vtableCount] @ r3<- super->vtableCount
+ EXPORT_PC() @ must export for invoke
+ cmp r2, r3 @ compare (methodIndex, vtableCount)
+ bcs .LOP_INVOKE_SUPER_nsm @ method not present in superclass
+ ldr r1, [r1, #offClassObject_vtable] @ r1<- ...clazz->super->vtable
+ ldr r0, [r1, r2, lsl #2] @ r3<- vtable[methodIndex]
+ bl common_invokeMethodNoRange @ continue on
+
+.LOP_INVOKE_SUPER_resolve:
+ mov r0, r9 @ r0<- method->clazz
+ mov r2, #METHOD_VIRTUAL @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ bne .LOP_INVOKE_SUPER_continue @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+ /*
+ * Throw a NoSuchMethodError with the method name as the message.
+ * r0 = resolved base method
+ */
+.LOP_INVOKE_SUPER_nsm:
+ ldr r1, [r0, #offMethod_name] @ r1<- method name
+ b common_errNoSuchMethod
+
+/* continuation for OP_INVOKE_DIRECT */
+
+ /*
+ * On entry:
+ * r1 = reference (BBBB or CCCC)
+ * r10 = "this" register
+ */
+.LOP_INVOKE_DIRECT_resolve:
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ mov r2, #METHOD_DIRECT @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ GET_VREG(r2, r10) @ r2<- "this" ptr (reload)
+ bne .LOP_INVOKE_DIRECT_finish @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+/* continuation for OP_INVOKE_VIRTUAL_RANGE */
+
+ /*
+ * At this point:
+ * r0 = resolved base method
+ * r10 = C or CCCC (index of first arg, which is the "this" ptr)
+ */
+.LOP_INVOKE_VIRTUAL_RANGE_continue:
+ GET_VREG(r1, r10) @ r1<- "this" ptr
+ ldrh r2, [r0, #offMethod_methodIndex] @ r2<- baseMethod->methodIndex
+ cmp r1, #0 @ is "this" null?
+ beq common_errNullObject @ null "this", throw exception
+ ldr r3, [r1, #offObject_clazz] @ r1<- thisPtr->clazz
+ ldr r3, [r3, #offClassObject_vtable] @ r3<- thisPtr->clazz->vtable
+ ldr r0, [r3, r2, lsl #2] @ r3<- vtable[methodIndex]
+ bl common_invokeMethodRange @ continue on
+
+/* continuation for OP_INVOKE_SUPER_RANGE */
+
+ /*
+ * At this point:
+ * r0 = resolved base method
+ * r9 = method->clazz
+ */
+.LOP_INVOKE_SUPER_RANGE_continue:
+ ldr r1, [r9, #offClassObject_super] @ r1<- method->clazz->super
+ ldrh r2, [r0, #offMethod_methodIndex] @ r2<- baseMethod->methodIndex
+ ldr r3, [r1, #offClassObject_vtableCount] @ r3<- super->vtableCount
+ EXPORT_PC() @ must export for invoke
+ cmp r2, r3 @ compare (methodIndex, vtableCount)
+ bcs .LOP_INVOKE_SUPER_RANGE_nsm @ method not present in superclass
+ ldr r1, [r1, #offClassObject_vtable] @ r1<- ...clazz->super->vtable
+ ldr r0, [r1, r2, lsl #2] @ r3<- vtable[methodIndex]
+ bl common_invokeMethodRange @ continue on
+
+.LOP_INVOKE_SUPER_RANGE_resolve:
+ mov r0, r9 @ r0<- method->clazz
+ mov r2, #METHOD_VIRTUAL @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ bne .LOP_INVOKE_SUPER_RANGE_continue @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+ /*
+ * Throw a NoSuchMethodError with the method name as the message.
+ * r0 = resolved base method
+ */
+.LOP_INVOKE_SUPER_RANGE_nsm:
+ ldr r1, [r0, #offMethod_name] @ r1<- method name
+ b common_errNoSuchMethod
+
+/* continuation for OP_INVOKE_DIRECT_RANGE */
+
+ /*
+ * On entry:
+ * r1 = reference (BBBB or CCCC)
+ * r10 = "this" register
+ */
+.LOP_INVOKE_DIRECT_RANGE_resolve:
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ mov r2, #METHOD_DIRECT @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ GET_VREG(r2, r10) @ r2<- "this" ptr (reload)
+ bne .LOP_INVOKE_DIRECT_RANGE_finish @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+/* continuation for OP_FLOAT_TO_LONG */
+/*
+ * Convert the float in r0 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification. The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer. The EABI convert function isn't doing this for us.
+ */
+f2l_doconv:
+ stmfd sp!, {r4, lr}
+ mov r1, #0x5f000000 @ (float)maxlong
+ mov r4, r0
+ bl __aeabi_fcmpge @ is arg >= maxlong?
+ cmp r0, #0 @ nonzero == yes
+ mvnne r0, #0 @ return maxlong (7fffffff)
+ mvnne r1, #0x80000000
+ ldmnefd sp!, {r4, pc}
+
+ mov r0, r4 @ recover arg
+ mov r1, #0xdf000000 @ (float)minlong
+ bl __aeabi_fcmple @ is arg <= minlong?
+ cmp r0, #0 @ nonzero == yes
+ movne r0, #0 @ return minlong (80000000)
+ movne r1, #0x80000000
+ ldmnefd sp!, {r4, pc}
+
+ mov r0, r4 @ recover arg
+ mov r1, r4
+ bl __aeabi_fcmpeq @ is arg == self?
+ cmp r0, #0 @ zero == no
+ moveq r1, #0 @ return zero for NaN
+ ldmeqfd sp!, {r4, pc}
+
+ mov r0, r4 @ recover arg
+ bl __aeabi_f2lz @ convert float to long
+ ldmfd sp!, {r4, pc}
+
+/* continuation for OP_DOUBLE_TO_LONG */
+/*
+ * Convert the double in r0/r1 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification. The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer. The EABI convert function isn't doing this for us.
+ */
+d2l_doconv:
+ stmfd sp!, {r4, r5, lr} @ save regs
+ mov r3, #0x43000000 @ maxlong, as a double (high word)
+ add r3, #0x00e00000 @ 0x43e00000
+ mov r2, #0 @ maxlong, as a double (low word)
+ sub sp, sp, #4 @ align for EABI
+ mov r4, r0 @ save a copy of r0
+ mov r5, r1 @ and r1
+ bl __aeabi_dcmpge @ is arg >= maxlong?
+ cmp r0, #0 @ nonzero == yes
+ mvnne r0, #0 @ return maxlong (7fffffffffffffff)
+ mvnne r1, #0x80000000
+ bne 1f
+
+ mov r0, r4 @ recover arg
+ mov r1, r5
+ mov r3, #0xc3000000 @ minlong, as a double (high word)
+ add r3, #0x00e00000 @ 0xc3e00000
+ mov r2, #0 @ minlong, as a double (low word)
+ bl __aeabi_dcmple @ is arg <= minlong?
+ cmp r0, #0 @ nonzero == yes
+ movne r0, #0 @ return minlong (8000000000000000)
+ movne r1, #0x80000000
+ bne 1f
+
+ mov r0, r4 @ recover arg
+ mov r1, r5
+ mov r2, r4 @ compare against self
+ mov r3, r5
+ bl __aeabi_dcmpeq @ is arg == self?
+ cmp r0, #0 @ zero == no
+ moveq r1, #0 @ return zero for NaN
+ beq 1f
+
+ mov r0, r4 @ recover arg
+ mov r1, r5
+ bl __aeabi_d2lz @ convert double to long
+
+1:
+ add sp, sp, #4
+ ldmfd sp!, {r4, r5, pc}
+
+/* continuation for OP_MUL_LONG */
+
+.LOP_MUL_LONG_finish:
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r0, {r9-r10} @ vAA/vAA+1<- r9/r10
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SHL_LONG */
+
+.LOP_SHL_LONG_finish:
+ mov r0, r0, asl r2 @ r0<- r0 << r2
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SHR_LONG */
+
+.LOP_SHR_LONG_finish:
+ mov r1, r1, asr r2 @ r1<- r1 >> r2
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_USHR_LONG */
+
+.LOP_USHR_LONG_finish:
+ mov r1, r1, lsr r2 @ r1<- r1 >>> r2
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SHL_LONG_2ADDR */
+
+.LOP_SHL_LONG_2ADDR_finish:
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SHR_LONG_2ADDR */
+
+.LOP_SHR_LONG_2ADDR_finish:
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_USHR_LONG_2ADDR */
+
+.LOP_USHR_LONG_2ADDR_finish:
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_VOLATILE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_VOLATILE_finish:
+ @bl common_squeak0
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ SMP_DMB @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT_VOLATILE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_VOLATILE_finish:
+ @bl common_squeak0
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SMP_DMB @ releasing store
+ str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SGET_VOLATILE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_VOLATILE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_VOLATILE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_VOLATILE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SPUT_VOLATILE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_VOLATILE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_IGET_OBJECT_VOLATILE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_OBJECT_VOLATILE_finish:
+ @bl common_squeak0
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ SMP_DMB @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_WIDE_VOLATILE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_WIDE_VOLATILE_finish:
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ .if 1
+ add r0, r9, r3 @ r0<- address of field
+ bl dvmQuasiAtomicRead64 @ r0/r1<- contents of field
+ .else
+ ldrd r0, [r9, r3] @ r0/r1<- obj.field (64-bit align ok)
+ .endif
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ add r3, rFP, r2, lsl #2 @ r3<- &fp[A]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ fp[A]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT_WIDE_VOLATILE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_WIDE_VOLATILE_finish:
+ mov r2, rINST, lsr #8 @ r2<- A+
+ cmp r9, #0 @ check object for null
+ and r2, r2, #15 @ r2<- A
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ add r2, rFP, r2, lsl #2 @ r3<- &fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldmia r2, {r0-r1} @ r0/r1<- fp[A]
+ GET_INST_OPCODE(r10) @ extract opcode from rINST
+ .if 1
+ add r2, r9, r3 @ r2<- target address
+ bl dvmQuasiAtomicSwap64 @ stores r0/r1 into addr r2
+ .else
+ strd r0, [r9, r3] @ obj.field (64 bits, aligned)<- r0/r1
+ .endif
+ GOTO_OPCODE(r10) @ jump to next instruction
+
+/* continuation for OP_SGET_WIDE_VOLATILE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ *
+ * Returns StaticField pointer in r0.
+ */
+.LOP_SGET_WIDE_VOLATILE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_WIDE_VOLATILE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_WIDE_VOLATILE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ * r9: &fp[AA]
+ *
+ * Returns StaticField pointer in r2.
+ */
+.LOP_SPUT_WIDE_VOLATILE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ mov r2, r0 @ copy to r2
+ bne .LOP_SPUT_WIDE_VOLATILE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_EXECUTE_INLINE */
+
+ /*
+ * Extract args, call function.
+ * r0 = #of args (0-4)
+ * r10 = call index
+ * lr = return addr, above [DO NOT bl out of here w/o preserving LR]
+ *
+ * Other ideas:
+ * - Use a jump table from the main piece to jump directly into the
+ * AND/LDR pairs. Costs a data load, saves a branch.
+ * - Have five separate pieces that do the loading, so we can work the
+ * interleave a little better. Increases code size.
+ */
+.LOP_EXECUTE_INLINE_continue:
+ rsb r0, r0, #4 @ r0<- 4-r0
+ FETCH(r9, 2) @ r9<- FEDC
+ add pc, pc, r0, lsl #3 @ computed goto, 2 instrs each
+ bl common_abort @ (skipped due to ARM prefetch)
+4: and ip, r9, #0xf000 @ isolate F
+ ldr r3, [rFP, ip, lsr #10] @ r3<- vF (shift right 12, left 2)
+3: and ip, r9, #0x0f00 @ isolate E
+ ldr r2, [rFP, ip, lsr #6] @ r2<- vE
+2: and ip, r9, #0x00f0 @ isolate D
+ ldr r1, [rFP, ip, lsr #2] @ r1<- vD
+1: and ip, r9, #0x000f @ isolate C
+ ldr r0, [rFP, ip, lsl #2] @ r0<- vC
+0:
+ ldr r9, .LOP_EXECUTE_INLINE_table @ table of InlineOperation
+ LDR_PC "[r9, r10, lsl #4]" @ sizeof=16, "func" is first entry
+ @ (not reached)
+
+.LOP_EXECUTE_INLINE_table:
+ .word gDvmInlineOpsTable
+
+/* continuation for OP_EXECUTE_INLINE_RANGE */
+
+ /*
+ * Extract args, call function.
+ * r0 = #of args (0-4)
+ * r10 = call index
+ * lr = return addr, above [DO NOT bl out of here w/o preserving LR]
+ */
+.LOP_EXECUTE_INLINE_RANGE_continue:
+ rsb r0, r0, #4 @ r0<- 4-r0
+ FETCH(r9, 2) @ r9<- CCCC
+ add pc, pc, r0, lsl #3 @ computed goto, 2 instrs each
+ bl common_abort @ (skipped due to ARM prefetch)
+4: add ip, r9, #3 @ base+3
+ GET_VREG(r3, ip) @ r3<- vBase[3]
+3: add ip, r9, #2 @ base+2
+ GET_VREG(r2, ip) @ r2<- vBase[2]
+2: add ip, r9, #1 @ base+1
+ GET_VREG(r1, ip) @ r1<- vBase[1]
+1: add ip, r9, #0 @ (nop)
+ GET_VREG(r0, ip) @ r0<- vBase[0]
+0:
+ ldr r9, .LOP_EXECUTE_INLINE_RANGE_table @ table of InlineOperation
+ LDR_PC "[r9, r10, lsl #4]" @ sizeof=16, "func" is first entry
+ @ (not reached)
+
+.LOP_EXECUTE_INLINE_RANGE_table:
+ .word gDvmInlineOpsTable
+
+/* continuation for OP_IPUT_OBJECT_VOLATILE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_OBJECT_VOLATILE_finish:
+ @bl common_squeak0
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SMP_DMB @ releasing store
+ str r0, [r9, r3] @ obj.field (32 bits)<- r0
+ cmp r0, #0 @ stored a null reference?
+ strneb r2, [r2, r9, lsr #GC_CARD_SHIFT] @ mark card if not
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SGET_OBJECT_VOLATILE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_OBJECT_VOLATILE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_OBJECT_VOLATILE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_OBJECT_VOLATILE */
+.LOP_SPUT_OBJECT_VOLATILE_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ ldr r9, [r0, #offField_clazz] @ r9<- field->clazz
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SMP_DMB @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ cmp r1, #0 @ stored a null object?
+ strneb r2, [r2, r9, lsr #GC_CARD_SHIFT] @ mark card based on obj head
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+ .size dvmAsmSisterStart, .-dvmAsmSisterStart
+ .global dvmAsmSisterEnd
+dvmAsmSisterEnd:
+
+/* File: armv5te/footer.S */
+
+/*
+ * ===========================================================================
+ * Common subroutines and data
+ * ===========================================================================
+ */
+
+
+
+ .text
+ .align 2
+
+#if defined(WITH_JIT)
+#if defined(WITH_SELF_VERIFICATION)
+ .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r2,#kSVSPunt @ r2<- interpreter entry point
+ mov r3, #0
+ str r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+ str lr,[rGLUE,#offGlue_jitResumeNPC]
+ str r1,[rGLUE,#offGlue_jitResumeDPC]
+ mov r2,#kSVSSingleStep @ r2<- interpreter entry point
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r0,rPC @ pass our target PC
+ mov r2,#kSVSNoProfile @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [r10, #offThread_inJitCodeCache] @ back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r0,rPC @ pass our target PC
+ mov r2,#kSVSTraceSelect @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ ldr r0,[lr, #-1] @ pass our target PC
+ mov r2,#kSVSTraceSelect @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpBackwardBranch
+dvmJitToInterpBackwardBranch:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ ldr r0,[lr, #-1] @ pass our target PC
+ mov r2,#kSVSBackwardBranch @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ ldr r0,[lr, #-1] @ pass our target PC
+ mov r2,#kSVSNormal @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r0,rPC @ pass our target PC
+ mov r2,#kSVSNoChain @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+#else
+/*
+ * Return from the translation cache to the interpreter when the compiler is
+ * having issues translating/executing a Dalvik instruction. We have to skip
+ * the code cache lookup otherwise it is possible to indefinitely bouce
+ * between the interpreter and the code cache if the instruction that fails
+ * to be compiled happens to be at a trace start.
+ */
+ .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov rPC, r0
+#if defined(WITH_JIT_TUNING)
+ mov r0,lr
+ bl dvmBumpPunt;
+#endif
+ EXPORT_PC()
+ mov r0, #0
+ str r0, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ adrl rIBASE, dvmAsmInstructionStart
+ FETCH_INST()
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+
+/*
+ * Return to the interpreter to handle a single instruction.
+ * On entry:
+ * r0 <= PC
+ * r1 <= PC of resume instruction
+ * lr <= resume point in translation
+ */
+ .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+ str lr,[rGLUE,#offGlue_jitResumeNPC]
+ str r1,[rGLUE,#offGlue_jitResumeDPC]
+ mov r1,#kInterpEntryInstr
+ @ enum is 4 byte in aapcs-EABI
+ str r1, [rGLUE, #offGlue_entryPoint]
+ mov rPC,r0
+ EXPORT_PC()
+
+ adrl rIBASE, dvmAsmInstructionStart
+ mov r2,#kJitSingleStep @ Ask for single step and then revert
+ str r2,[rGLUE,#offGlue_jitState]
+ mov r1,#1 @ set changeInterp to bail to debug interp
+ b common_gotoBail
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target. Commonly used for callees.
+ */
+ .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+#if defined(WITH_JIT_TUNING)
+ bl dvmBumpNoChain
+#endif
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r0,rPC
+ bl dvmJitGetCodeAddr @ Is there a translation?
+ str r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0 @ !0 means translation exists
+ bxne r0 @ continue native execution if so
+ b 2f @ branch over to use the interpreter
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target. Commonly used following
+ * invokes.
+ */
+ .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+ ldr rPC,[lr, #-1] @ get our target PC
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ add rINST,lr,#-5 @ save start of chain branch
+ add rINST, #-4 @ .. which is 9 bytes back
+ mov r0,rPC
+ bl dvmJitGetCodeAddr @ Is there a translation?
+ str r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ cmp r0,#0
+ beq 2f
+ mov r1,rINST
+ bl dvmJitChain @ r0<- dvmJitChain(codeAddr,chainAddr)
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0 @ successful chain?
+ bxne r0 @ continue native execution
+ b toInterpreter @ didn't chain - resume with interpreter
+
+/* No translation, so request one if profiling isn't disabled*/
+2:
+ adrl rIBASE, dvmAsmInstructionStart
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_INST()
+ cmp r0, #0
+ movne r2,#kJitTSelectRequestHot @ ask for trace selection
+ bne common_selectTrace
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+
+/*
+ * Return from the translation cache to the interpreter.
+ * The return was done with a BLX from thumb mode, and
+ * the following 32-bit word contains the target rPC value.
+ * Note that lr (r14) will have its low-order bit set to denote
+ * its thumb-mode origin.
+ *
+ * We'll need to stash our lr origin away, recover the new
+ * target and then check to see if there is a translation available
+ * for our new target. If so, we do a translation chain and
+ * go back to native execution. Otherwise, it's back to the
+ * interpreter (after treating this entry as a potential
+ * trace start).
+ */
+ .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+ ldr rPC,[lr, #-1] @ get our target PC
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ add rINST,lr,#-5 @ save start of chain branch
+ add rINST,#-4 @ .. which is 9 bytes back
+#if defined(WITH_JIT_TUNING)
+ bl dvmBumpNormal
+#endif
+ mov r0,rPC
+ bl dvmJitGetCodeAddr @ Is there a translation?
+ str r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ cmp r0,#0
+ beq toInterpreter @ go if not, otherwise do chain
+ mov r1,rINST
+ bl dvmJitChain @ r0<- dvmJitChain(codeAddr,chainAddr)
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0 @ successful chain?
+ bxne r0 @ continue native execution
+ b toInterpreter @ didn't chain - resume with interpreter
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+ .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+#if defined(WITH_JIT_TUNING)
+ bl dvmBumpNoChain
+#endif
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r0,rPC
+ bl dvmJitGetCodeAddr @ Is there a translation?
+ str r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0
+ bxne r0 @ continue native execution if so
+ EXPORT_PC()
+ adrl rIBASE, dvmAsmInstructionStart
+ FETCH_INST()
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+ .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+#if defined(WITH_JIT_TUNING)
+ bl dvmBumpNoChain
+#endif
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r0,rPC
+ bl dvmJitGetCodeAddr @ Is there a translation?
+ str r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0
+ bxne r0 @ continue native execution if so
+#endif
+
+/*
+ * No translation, restore interpreter regs and start interpreting.
+ * rGLUE & rFP were preserved in the translated code, and rPC has
+ * already been restored by the time we get here. We'll need to set
+ * up rIBASE & rINST, and load the address of the JitTable into r0.
+ */
+toInterpreter:
+ EXPORT_PC()
+ adrl rIBASE, dvmAsmInstructionStart
+ FETCH_INST()
+ GET_JIT_PROF_TABLE(r0)
+ @ NOTE: intended fallthrough
+
+/*
+ * Common code to update potential trace start counter, and initiate
+ * a trace-build if appropriate. On entry, rPC should point to the
+ * next instruction to execute, and rINST should be already loaded with
+ * the next opcode word, and r0 holds a pointer to the jit profile
+ * table (pJitProfTable).
+ */
+common_testUpdateProfile:
+ cmp r0,#0
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE_IFEQ(ip) @ if not profiling, fallthrough otherwise */
+
+common_updateProfile:
+ eor r3,rPC,rPC,lsr #12 @ cheap, but fast hash function
+ lsl r3,r3,#(32 - JIT_PROF_SIZE_LOG_2) @ shift out excess bits
+ ldrb r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ get counter
+ GET_INST_OPCODE(ip)
+ subs r1,r1,#1 @ decrement counter
+ strb r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ and store it
+ GOTO_OPCODE_IFNE(ip) @ if not threshold, fallthrough otherwise */
+
+/*
+ * Here, we switch to the debug interpreter to request
+ * trace selection. First, though, check to see if there
+ * is already a native translation in place (and, if so,
+ * jump to it now).
+ */
+ GET_JIT_THRESHOLD(r1)
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ strb r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ reset counter
+ EXPORT_PC()
+ mov r0,rPC
+ bl dvmJitGetCodeAddr @ r0<- dvmJitGetCodeAddr(rPC)
+ str r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0
+#if !defined(WITH_SELF_VERIFICATION)
+ bxne r0 @ jump to the translation
+ mov r2,#kJitTSelectRequest @ ask for trace selection
+ @ fall-through to common_selectTrace
+#else
+ moveq r2,#kJitTSelectRequest @ ask for trace selection
+ beq common_selectTrace
+ /*
+ * At this point, we have a target translation. However, if
+ * that translation is actually the interpret-only pseudo-translation
+ * we want to treat it the same as no translation.
+ */
+ mov r10, r0 @ save target
+ bl dvmCompilerGetInterpretTemplate
+ cmp r0, r10 @ special case?
+ bne jitSVShadowRunStart @ set up self verification shadow space
+ @ Need to clear the inJitCodeCache flag
+ ldr r10, [rGLUE, #offGlue_self] @ r10 <- glue->self
+ mov r3, #0 @ 0 means not in the JIT code cache
+ str r3, [r10, #offThread_inJitCodeCache] @ back to the interp land
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+ /* no return */
+#endif
+
+/*
+ * On entry:
+ * r2 is jit state, e.g. kJitTSelectRequest or kJitTSelectRequestHot
+ */
+common_selectTrace:
+ str r2,[rGLUE,#offGlue_jitState]
+ mov r2,#kInterpEntryInstr @ normal entry reason
+ str r2,[rGLUE,#offGlue_entryPoint]
+ mov r1,#1 @ set changeInterp
+ b common_gotoBail
+
+#if defined(WITH_SELF_VERIFICATION)
+/*
+ * Save PC and registers to shadow memory for self verification mode
+ * before jumping to native translation.
+ * On entry:
+ * rPC, rFP, rGLUE: the values that they should contain
+ * r10: the address of the target translation.
+ */
+jitSVShadowRunStart:
+ mov r0,rPC @ r0<- program counter
+ mov r1,rFP @ r1<- frame pointer
+ mov r2,rGLUE @ r2<- InterpState pointer
+ mov r3,r10 @ r3<- target translation
+ bl dvmSelfVerificationSaveState @ save registers to shadow space
+ ldr rFP,[r0,#offShadowSpace_shadowFP] @ rFP<- fp in shadow space
+ add rGLUE,r0,#offShadowSpace_interpState @ rGLUE<- rGLUE in shadow space
+ bx r10 @ jump to the translation
+
+/*
+ * Restore PC, registers, and interpState to original values
+ * before jumping back to the interpreter.
+ */
+jitSVShadowRunEnd:
+ mov r1,rFP @ pass ending fp
+ bl dvmSelfVerificationRestoreState @ restore pc and fp values
+ ldr rPC,[r0,#offShadowSpace_startPC] @ restore PC
+ ldr rFP,[r0,#offShadowSpace_fp] @ restore FP
+ ldr rGLUE,[r0,#offShadowSpace_glue] @ restore InterpState
+ ldr r1,[r0,#offShadowSpace_svState] @ get self verification state
+ cmp r1,#0 @ check for punt condition
+ beq 1f
+ mov r2,#kJitSelfVerification @ ask for self verification
+ str r2,[rGLUE,#offGlue_jitState]
+ mov r2,#kInterpEntryInstr @ normal entry reason
+ str r2,[rGLUE,#offGlue_entryPoint]
+ mov r1,#1 @ set changeInterp
+ b common_gotoBail
+
+1: @ exit to interpreter without check
+ EXPORT_PC()
+ adrl rIBASE, dvmAsmInstructionStart
+ FETCH_INST()
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+#endif
+
+#endif
+
+/*
+ * Common code when a backward branch is taken.
+ *
+ * TODO: we could avoid a branch by just setting r0 and falling through
+ * into the common_periodicChecks code, and having a test on r0 at the
+ * end determine if we should return to the caller or update & branch to
+ * the next instr.
+ *
+ * On entry:
+ * r9 is PC adjustment *in bytes*
+ */
+common_backwardBranch:
+ mov r0, #kInterpEntryInstr
+ bl common_periodicChecks
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/*
+ * Need to see if the thread needs to be suspended or debugger/profiler
+ * activity has begun. If so, we suspend the thread or side-exit to
+ * the debug interpreter as appropriate.
+ *
+ * The common case is no activity on any of these, so we want to figure
+ * that out quickly. If something is up, we can then sort out what.
+ *
+ * We want to be fast if the VM was built without debugger or profiler
+ * support, but we also need to recognize that the system is usually
+ * shipped with both of these enabled.
+ *
+ * TODO: reduce this so we're just checking a single location.
+ *
+ * On entry:
+ * r0 is reentry type, e.g. kInterpEntryInstr (for debugger/profiling)
+ * r9 is trampoline PC adjustment *in bytes*
+ */
+common_periodicChecks:
+ ldr r3, [rGLUE, #offGlue_pSelfSuspendCount] @ r3<- &suspendCount
+
+ ldr r1, [rGLUE, #offGlue_pDebuggerActive] @ r1<- &debuggerActive
+ ldr r2, [rGLUE, #offGlue_pActiveProfilers] @ r2<- &activeProfilers
+
+ ldr ip, [r3] @ ip<- suspendCount (int)
+
+ cmp r1, #0 @ debugger enabled?
+ ldrneb r1, [r1] @ yes, r1<- debuggerActive (boolean)
+ ldr r2, [r2] @ r2<- activeProfilers (int)
+ orrne ip, ip, r1 @ ip<- suspendCount | debuggerActive
+ orrs ip, ip, r2 @ ip<- suspend|debugger|profiler; set Z
+
+ bxeq lr @ all zero, return
+
+ /*
+ * One or more interesting events have happened. Figure out what.
+ *
+ * If debugging or profiling are compiled in, we need to disambiguate.
+ *
+ * r0 still holds the reentry type.
+ */
+ ldr ip, [r3] @ ip<- suspendCount (int)
+ cmp ip, #0 @ want suspend?
+ beq 1f @ no, must be debugger/profiler
+
+ stmfd sp!, {r0, lr} @ preserve r0 and lr
+#if defined(WITH_JIT)
+ /*
+ * Refresh the Jit's cached copy of profile table pointer. This pointer
+ * doubles as the Jit's on/off switch.
+ */
+ ldr r3, [rGLUE, #offGlue_ppJitProfTable] @ r3<-&gDvmJit.pJitProfTable
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ ldr r3, [r3] @ r3 <- pJitProfTable
+ EXPORT_PC() @ need for precise GC
+ str r3, [rGLUE, #offGlue_pJitProfTable] @ refresh Jit's on/off switch
+#else
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ EXPORT_PC() @ need for precise GC
+#endif
+ bl dvmCheckSuspendPending @ do full check, suspend if necessary
+ ldmfd sp!, {r0, lr} @ restore r0 and lr
+
+ /*
+ * Reload the debugger/profiler enable flags. We're checking to see
+ * if either of these got set while we were suspended.
+ *
+ * We can't really avoid the #ifdefs here, because the fields don't
+ * exist when the feature is disabled.
+ */
+ ldr r1, [rGLUE, #offGlue_pDebuggerActive] @ r1<- &debuggerActive
+ cmp r1, #0 @ debugger enabled?
+ ldrneb r1, [r1] @ yes, r1<- debuggerActive (boolean)
+ ldr r2, [rGLUE, #offGlue_pActiveProfilers] @ r2<- &activeProfilers
+ ldr r2, [r2] @ r2<- activeProfilers (int)
+
+ orrs r1, r1, r2
+ beq 2f
+
+1: @ debugger/profiler enabled, bail out; glue->entryPoint was set above
+ str r0, [rGLUE, #offGlue_entryPoint] @ store r0, need for debug/prof
+ add rPC, rPC, r9 @ update rPC
+ mov r1, #1 @ "want switch" = true
+ b common_gotoBail @ side exit
+
+2:
+ bx lr @ nothing to do, return
+
+
+/*
+ * The equivalent of "goto bail", this calls through the "bail handler".
+ *
+ * State registers will be saved to the "glue" area before bailing.
+ *
+ * On entry:
+ * r1 is "bool changeInterp", indicating if we want to switch to the
+ * other interpreter or just bail all the way out
+ */
+common_gotoBail:
+ SAVE_PC_FP_TO_GLUE() @ export state to "glue"
+ mov r0, rGLUE @ r0<- glue ptr
+ b dvmMterpStdBail @ call(glue, changeInterp)
+
+ @add r1, r1, #1 @ using (boolean+1)
+ @add r0, rGLUE, #offGlue_jmpBuf @ r0<- &glue->jmpBuf
+ @bl _longjmp @ does not return
+ @bl common_abort
+
+
+/*
+ * Common code for method invocation with range.
+ *
+ * On entry:
+ * r0 is "Method* methodToCall", the method we're trying to call
+ */
+common_invokeMethodRange:
+.LinvokeNewRange:
+ @ prepare to copy args to "outs" area of current frame
+ movs r2, rINST, lsr #8 @ r2<- AA (arg count) -- test for zero
+ SAVEAREA_FROM_FP(r10, rFP) @ r10<- stack save area
+ beq .LinvokeArgsDone @ if no args, skip the rest
+ FETCH(r1, 2) @ r1<- CCCC
+
+ @ r0=methodToCall, r1=CCCC, r2=count, r10=outs
+ @ (very few methods have > 10 args; could unroll for common cases)
+ add r3, rFP, r1, lsl #2 @ r3<- &fp[CCCC]
+ sub r10, r10, r2, lsl #2 @ r10<- "outs" area, for call args
+ ldrh r9, [r0, #offMethod_registersSize] @ r9<- methodToCall->regsSize
+1: ldr r1, [r3], #4 @ val = *fp++
+ subs r2, r2, #1 @ count--
+ str r1, [r10], #4 @ *outs++ = val
+ bne 1b @ ...while count != 0
+ ldrh r3, [r0, #offMethod_outsSize] @ r3<- methodToCall->outsSize
+ b .LinvokeArgsDone
+
+/*
+ * Common code for method invocation without range.
+ *
+ * On entry:
+ * r0 is "Method* methodToCall", the method we're trying to call
+ */
+common_invokeMethodNoRange:
+.LinvokeNewNoRange:
+ @ prepare to copy args to "outs" area of current frame
+ movs r2, rINST, lsr #12 @ r2<- B (arg count) -- test for zero
+ SAVEAREA_FROM_FP(r10, rFP) @ r10<- stack save area
+ FETCH(r1, 2) @ r1<- GFED (load here to hide latency)
+ ldrh r9, [r0, #offMethod_registersSize] @ r9<- methodToCall->regsSize
+ ldrh r3, [r0, #offMethod_outsSize] @ r3<- methodToCall->outsSize
+ beq .LinvokeArgsDone
+
+ @ r0=methodToCall, r1=GFED, r3=outSize, r2=count, r9=regSize, r10=outs
+.LinvokeNonRange:
+ rsb r2, r2, #5 @ r2<- 5-r2
+ add pc, pc, r2, lsl #4 @ computed goto, 4 instrs each
+ bl common_abort @ (skipped due to ARM prefetch)
+5: and ip, rINST, #0x0f00 @ isolate A
+ ldr r2, [rFP, ip, lsr #6] @ r2<- vA (shift right 8, left 2)
+ mov r0, r0 @ nop
+ str r2, [r10, #-4]! @ *--outs = vA
+4: and ip, r1, #0xf000 @ isolate G
+ ldr r2, [rFP, ip, lsr #10] @ r2<- vG (shift right 12, left 2)
+ mov r0, r0 @ nop
+ str r2, [r10, #-4]! @ *--outs = vG
+3: and ip, r1, #0x0f00 @ isolate F
+ ldr r2, [rFP, ip, lsr #6] @ r2<- vF
+ mov r0, r0 @ nop
+ str r2, [r10, #-4]! @ *--outs = vF
+2: and ip, r1, #0x00f0 @ isolate E
+ ldr r2, [rFP, ip, lsr #2] @ r2<- vE
+ mov r0, r0 @ nop
+ str r2, [r10, #-4]! @ *--outs = vE
+1: and ip, r1, #0x000f @ isolate D
+ ldr r2, [rFP, ip, lsl #2] @ r2<- vD
+ mov r0, r0 @ nop
+ str r2, [r10, #-4]! @ *--outs = vD
+0: @ fall through to .LinvokeArgsDone
+
+.LinvokeArgsDone: @ r0=methodToCall, r3=outSize, r9=regSize
+ ldr r2, [r0, #offMethod_insns] @ r2<- method->insns
+ ldr rINST, [r0, #offMethod_clazz] @ rINST<- method->clazz
+ @ find space for the new stack frame, check for overflow
+ SAVEAREA_FROM_FP(r1, rFP) @ r1<- stack save area
+ sub r1, r1, r9, lsl #2 @ r1<- newFp (old savearea - regsSize)
+ SAVEAREA_FROM_FP(r10, r1) @ r10<- newSaveArea
+@ bl common_dumpRegs
+ ldr r9, [rGLUE, #offGlue_interpStackEnd] @ r9<- interpStackEnd
+ sub r3, r10, r3, lsl #2 @ r3<- bottom (newsave - outsSize)
+ cmp r3, r9 @ bottom < interpStackEnd?
+ ldr r3, [r0, #offMethod_accessFlags] @ r3<- methodToCall->accessFlags
+ blo .LstackOverflow @ yes, this frame will overflow stack
+
+ @ set up newSaveArea
+#ifdef EASY_GDB
+ SAVEAREA_FROM_FP(ip, rFP) @ ip<- stack save area
+ str ip, [r10, #offStackSaveArea_prevSave]
+#endif
+ str rFP, [r10, #offStackSaveArea_prevFrame]
+ str rPC, [r10, #offStackSaveArea_savedPc]
+#if defined(WITH_JIT)
+ mov r9, #0
+ str r9, [r10, #offStackSaveArea_returnAddr]
+#endif
+ str r0, [r10, #offStackSaveArea_method]
+ tst r3, #ACC_NATIVE
+ bne .LinvokeNative
+
+ /*
+ stmfd sp!, {r0-r3}
+ bl common_printNewline
+ mov r0, rFP
+ mov r1, #0
+ bl dvmDumpFp
+ ldmfd sp!, {r0-r3}
+ stmfd sp!, {r0-r3}
+ mov r0, r1
+ mov r1, r10
+ bl dvmDumpFp
+ bl common_printNewline
+ ldmfd sp!, {r0-r3}
+ */
+
+ ldrh r9, [r2] @ r9 <- load INST from new PC
+ ldr r3, [rINST, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+ mov rPC, r2 @ publish new rPC
+ ldr r2, [rGLUE, #offGlue_self] @ r2<- glue->self
+
+ @ Update "glue" values for the new method
+ @ r0=methodToCall, r1=newFp, r2=self, r3=newMethodClass, r9=newINST
+ str r0, [rGLUE, #offGlue_method] @ glue->method = methodToCall
+ str r3, [rGLUE, #offGlue_methodClassDex] @ glue->methodClassDex = ...
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ mov rFP, r1 @ fp = newFp
+ GET_PREFETCHED_OPCODE(ip, r9) @ extract prefetched opcode from r9
+ mov rINST, r9 @ publish new rINST
+ str r1, [r2, #offThread_curFrame] @ self->curFrame = newFp
+ cmp r0,#0
+ bne common_updateProfile
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ mov rFP, r1 @ fp = newFp
+ GET_PREFETCHED_OPCODE(ip, r9) @ extract prefetched opcode from r9
+ mov rINST, r9 @ publish new rINST
+ str r1, [r2, #offThread_curFrame] @ self->curFrame = newFp
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+.LinvokeNative:
+ @ Prep for the native call
+ @ r0=methodToCall, r1=newFp, r10=newSaveArea
+ ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
+ ldr r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
+ str r1, [r3, #offThread_curFrame] @ self->curFrame = newFp
+ str r9, [r10, #offStackSaveArea_localRefCookie] @newFp->localRefCookie=top
+ mov r9, r3 @ r9<- glue->self (preserve)
+
+ mov r2, r0 @ r2<- methodToCall
+ mov r0, r1 @ r0<- newFp (points to args)
+ add r1, rGLUE, #offGlue_retval @ r1<- &retval
+
+#ifdef ASSIST_DEBUGGER
+ /* insert fake function header to help gdb find the stack frame */
+ b .Lskip
+ .type dalvik_mterp, %function
+dalvik_mterp:
+ .fnstart
+ MTERP_ENTRY1
+ MTERP_ENTRY2
+.Lskip:
+#endif
+
+ @mov lr, pc @ set return addr
+ @ldr pc, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
+ LDR_PC_LR "[r2, #offMethod_nativeFunc]"
+
+#if defined(WITH_JIT)
+ ldr r3, [rGLUE, #offGlue_ppJitProfTable] @ Refresh Jit's on/off status
+#endif
+
+ @ native return; r9=self, r10=newSaveArea
+ @ equivalent to dvmPopJniLocals
+ ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved top
+ ldr r1, [r9, #offThread_exception] @ check for exception
+#if defined(WITH_JIT)
+ ldr r3, [r3] @ r3 <- gDvmJit.pProfTable
+#endif
+ str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
+ cmp r1, #0 @ null?
+ str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
+#if defined(WITH_JIT)
+ str r3, [rGLUE, #offGlue_pJitProfTable] @ refresh cached on/off switch
+#endif
+ bne common_exceptionThrown @ no, handle exception
+
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+.LstackOverflow: @ r0=methodToCall
+ mov r1, r0 @ r1<- methodToCall
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- self
+ bl dvmHandleStackOverflow
+ b common_exceptionThrown
+#ifdef ASSIST_DEBUGGER
+ .fnend
+#endif
+
+
+ /*
+ * Common code for method invocation, calling through "glue code".
+ *
+ * TODO: now that we have range and non-range invoke handlers, this
+ * needs to be split into two. Maybe just create entry points
+ * that set r9 and jump here?
+ *
+ * On entry:
+ * r0 is "Method* methodToCall", the method we're trying to call
+ * r9 is "bool methodCallRange", indicating if this is a /range variant
+ */
+ .if 0
+.LinvokeOld:
+ sub sp, sp, #8 @ space for args + pad
+ FETCH(ip, 2) @ ip<- FEDC or CCCC
+ mov r2, r0 @ A2<- methodToCall
+ mov r0, rGLUE @ A0<- glue
+ SAVE_PC_FP_TO_GLUE() @ export state to "glue"
+ mov r1, r9 @ A1<- methodCallRange
+ mov r3, rINST, lsr #8 @ A3<- AA
+ str ip, [sp, #0] @ A4<- ip
+ bl dvmMterp_invokeMethod @ call the C invokeMethod
+ add sp, sp, #8 @ remove arg area
+ b common_resumeAfterGlueCall @ continue to next instruction
+ .endif
+
+
+
+/*
+ * Common code for handling a return instruction.
+ *
+ * This does not return.
+ */
+common_returnFromMethod:
+.LreturnNew:
+ mov r0, #kInterpEntryReturn
+ mov r9, #0
+ bl common_periodicChecks
+
+ SAVEAREA_FROM_FP(r0, rFP) @ r0<- saveArea (old)
+ ldr rFP, [r0, #offStackSaveArea_prevFrame] @ fp = saveArea->prevFrame
+ ldr r9, [r0, #offStackSaveArea_savedPc] @ r9 = saveArea->savedPc
+ ldr r2, [rFP, #(offStackSaveArea_method - sizeofStackSaveArea)]
+ @ r2<- method we're returning to
+ ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
+ cmp r2, #0 @ is this a break frame?
+ ldrne r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+ mov r1, #0 @ "want switch" = false
+ beq common_gotoBail @ break frame, bail out completely
+
+ PREFETCH_ADVANCE_INST(rINST, r9, 3) @ advance r9, update new rINST
+ str r2, [rGLUE, #offGlue_method]@ glue->method = newSave->method
+ ldr r1, [r10, #offClassObject_pDvmDex] @ r1<- method->clazz->pDvmDex
+ str rFP, [r3, #offThread_curFrame] @ self->curFrame = fp
+#if defined(WITH_JIT)
+ ldr r10, [r0, #offStackSaveArea_returnAddr] @ r10 = saveArea->returnAddr
+ mov rPC, r9 @ publish new rPC
+ str r1, [rGLUE, #offGlue_methodClassDex]
+ str r10, [r3, #offThread_inJitCodeCache] @ may return to JIT'ed land
+ cmp r10, #0 @ caller is compiled code
+ blxne r10
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ mov rPC, r9 @ publish new rPC
+ str r1, [rGLUE, #offGlue_methodClassDex]
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+ /*
+ * Return handling, calls through "glue code".
+ */
+ .if 0
+.LreturnOld:
+ SAVE_PC_FP_TO_GLUE() @ export state
+ mov r0, rGLUE @ arg to function
+ bl dvmMterp_returnFromMethod
+ b common_resumeAfterGlueCall
+ .endif
+
+
+/*
+ * Somebody has thrown an exception. Handle it.
+ *
+ * If the exception processing code returns to us (instead of falling
+ * out of the interpreter), continue with whatever the next instruction
+ * now happens to be.
+ *
+ * This does not return.
+ */
+ .global dvmMterpCommonExceptionThrown
+dvmMterpCommonExceptionThrown:
+common_exceptionThrown:
+.LexceptionNew:
+ mov r0, #kInterpEntryThrow
+ mov r9, #0
+ bl common_periodicChecks
+
+ ldr r10, [rGLUE, #offGlue_self] @ r10<- glue->self
+ ldr r9, [r10, #offThread_exception] @ r9<- self->exception
+ mov r1, r10 @ r1<- self
+ mov r0, r9 @ r0<- exception
+ bl dvmAddTrackedAlloc @ don't let the exception be GCed
+ mov r3, #0 @ r3<- NULL
+ str r3, [r10, #offThread_exception] @ self->exception = NULL
+
+ /* set up args and a local for "&fp" */
+ /* (str sp, [sp, #-4]! would be perfect here, but is discouraged) */
+ str rFP, [sp, #-4]! @ *--sp = fp
+ mov ip, sp @ ip<- &fp
+ mov r3, #0 @ r3<- false
+ str ip, [sp, #-4]! @ *--sp = &fp
+ ldr r1, [rGLUE, #offGlue_method] @ r1<- glue->method
+ mov r0, r10 @ r0<- self
+ ldr r1, [r1, #offMethod_insns] @ r1<- method->insns
+ mov r2, r9 @ r2<- exception
+ sub r1, rPC, r1 @ r1<- pc - method->insns
+ mov r1, r1, asr #1 @ r1<- offset in code units
+
+ /* call, r0 gets catchRelPc (a code-unit offset) */
+ bl dvmFindCatchBlock @ call(self, relPc, exc, scan?, &fp)
+
+ /* fix earlier stack overflow if necessary; may trash rFP */
+ ldrb r1, [r10, #offThread_stackOverflowed]
+ cmp r1, #0 @ did we overflow earlier?
+ beq 1f @ no, skip ahead
+ mov rFP, r0 @ save relPc result in rFP
+ mov r0, r10 @ r0<- self
+ mov r1, r9 @ r1<- exception
+ bl dvmCleanupStackOverflow @ call(self)
+ mov r0, rFP @ restore result
+1:
+
+ /* update frame pointer and check result from dvmFindCatchBlock */
+ ldr rFP, [sp, #4] @ retrieve the updated rFP
+ cmp r0, #0 @ is catchRelPc < 0?
+ add sp, sp, #8 @ restore stack
+ bmi .LnotCaughtLocally
+
+ /* adjust locals to match self->curFrame and updated PC */
+ SAVEAREA_FROM_FP(r1, rFP) @ r1<- new save area
+ ldr r1, [r1, #offStackSaveArea_method] @ r1<- new method
+ str r1, [rGLUE, #offGlue_method] @ glue->method = new method
+ ldr r2, [r1, #offMethod_clazz] @ r2<- method->clazz
+ ldr r3, [r1, #offMethod_insns] @ r3<- method->insns
+ ldr r2, [r2, #offClassObject_pDvmDex] @ r2<- method->clazz->pDvmDex
+ add rPC, r3, r0, asl #1 @ rPC<- method->insns + catchRelPc
+ str r2, [rGLUE, #offGlue_methodClassDex] @ glue->pDvmDex = meth...
+
+ /* release the tracked alloc on the exception */
+ mov r0, r9 @ r0<- exception
+ mov r1, r10 @ r1<- self
+ bl dvmReleaseTrackedAlloc @ release the exception
+
+ /* restore the exception if the handler wants it */
+ FETCH_INST() @ load rINST from rPC
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ cmp ip, #OP_MOVE_EXCEPTION @ is it "move-exception"?
+ streq r9, [r10, #offThread_exception] @ yes, restore the exception
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+.LnotCaughtLocally: @ r9=exception, r10=self
+ /* fix stack overflow if necessary */
+ ldrb r1, [r10, #offThread_stackOverflowed]
+ cmp r1, #0 @ did we overflow earlier?
+ movne r0, r10 @ if yes: r0<- self
+ movne r1, r9 @ if yes: r1<- exception
+ blne dvmCleanupStackOverflow @ if yes: call(self)
+
+ @ may want to show "not caught locally" debug messages here
+#if DVM_SHOW_EXCEPTION >= 2
+ /* call __android_log_print(prio, tag, format, ...) */
+ /* "Exception %s from %s:%d not caught locally" */
+ @ dvmLineNumFromPC(method, pc - method->insns)
+ ldr r0, [rGLUE, #offGlue_method]
+ ldr r1, [r0, #offMethod_insns]
+ sub r1, rPC, r1
+ asr r1, r1, #1
+ bl dvmLineNumFromPC
+ str r0, [sp, #-4]!
+ @ dvmGetMethodSourceFile(method)
+ ldr r0, [rGLUE, #offGlue_method]
+ bl dvmGetMethodSourceFile
+ str r0, [sp, #-4]!
+ @ exception->clazz->descriptor
+ ldr r3, [r9, #offObject_clazz]
+ ldr r3, [r3, #offClassObject_descriptor]
+ @
+ ldr r2, strExceptionNotCaughtLocally
+ ldr r1, strLogTag
+ mov r0, #3 @ LOG_DEBUG
+ bl __android_log_print
+#endif
+ str r9, [r10, #offThread_exception] @ restore exception
+ mov r0, r9 @ r0<- exception
+ mov r1, r10 @ r1<- self
+ bl dvmReleaseTrackedAlloc @ release the exception
+ mov r1, #0 @ "want switch" = false
+ b common_gotoBail @ bail out
+
+
+ /*
+ * Exception handling, calls through "glue code".
+ */
+ .if 0
+.LexceptionOld:
+ SAVE_PC_FP_TO_GLUE() @ export state
+ mov r0, rGLUE @ arg to function
+ bl dvmMterp_exceptionThrown
+ b common_resumeAfterGlueCall
+ .endif
+
+
+/*
+ * After returning from a "glued" function, pull out the updated
+ * values and start executing at the next instruction.
+ */
+common_resumeAfterGlueCall:
+ LOAD_PC_FP_FROM_GLUE() @ pull rPC and rFP out of glue
+ FETCH_INST() @ load rINST from rPC
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/*
+ * Invalid array index.
+ */
+common_errArrayIndex:
+ EXPORT_PC()
+ ldr r0, strArrayIndexException
+ mov r1, #0
+ bl dvmThrowException
+ b common_exceptionThrown
+
+/*
+ * Invalid array value.
+ */
+common_errArrayStore:
+ EXPORT_PC()
+ ldr r0, strArrayStoreException
+ mov r1, #0
+ bl dvmThrowException
+ b common_exceptionThrown
+
+/*
+ * Integer divide or mod by zero.
+ */
+common_errDivideByZero:
+ EXPORT_PC()
+ ldr r0, strArithmeticException
+ ldr r1, strDivideByZero
+ bl dvmThrowException
+ b common_exceptionThrown
+
+/*
+ * Attempt to allocate an array with a negative size.
+ */
+common_errNegativeArraySize:
+ EXPORT_PC()
+ ldr r0, strNegativeArraySizeException
+ mov r1, #0
+ bl dvmThrowException
+ b common_exceptionThrown
+
+/*
+ * Invocation of a non-existent method.
+ */
+common_errNoSuchMethod:
+ EXPORT_PC()
+ ldr r0, strNoSuchMethodError
+ mov r1, #0
+ bl dvmThrowException
+ b common_exceptionThrown
+
+/*
+ * We encountered a null object when we weren't expecting one. We
+ * export the PC, throw a NullPointerException, and goto the exception
+ * processing code.
+ */
+common_errNullObject:
+ EXPORT_PC()
+ ldr r0, strNullPointerException
+ mov r1, #0
+ bl dvmThrowException
+ b common_exceptionThrown
+
+/*
+ * For debugging, cause an immediate fault. The source address will
+ * be in lr (use a bl instruction to jump here).
+ */
+common_abort:
+ ldr pc, .LdeadFood
+.LdeadFood:
+ .word 0xdeadf00d
+
+/*
+ * Spit out a "we were here", preserving all registers. (The attempt
+ * to save ip won't work, but we need to save an even number of
+ * registers for EABI 64-bit stack alignment.)
+ */
+ .macro SQUEAK num
+common_squeak\num:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ ldr r0, strSqueak
+ mov r1, #\num
+ bl printf
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+ .endm
+
+ SQUEAK 0
+ SQUEAK 1
+ SQUEAK 2
+ SQUEAK 3
+ SQUEAK 4
+ SQUEAK 5
+
+/*
+ * Spit out the number in r0, preserving registers.
+ */
+common_printNum:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ mov r1, r0
+ ldr r0, strSqueak
+ bl printf
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+
+/*
+ * Print a newline, preserving registers.
+ */
+common_printNewline:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ ldr r0, strNewline
+ bl printf
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+
+ /*
+ * Print the 32-bit quantity in r0 as a hex value, preserving registers.
+ */
+common_printHex:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ mov r1, r0
+ ldr r0, strPrintHex
+ bl printf
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+
+/*
+ * Print the 64-bit quantity in r0-r1, preserving registers.
+ */
+common_printLong:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ mov r3, r1
+ mov r2, r0
+ ldr r0, strPrintLong
+ bl printf
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+
+/*
+ * Print full method info. Pass the Method* in r0. Preserves regs.
+ */
+common_printMethod:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bl dvmMterpPrintMethod
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+
+/*
+ * Call a C helper function that dumps regs and possibly some
+ * additional info. Requires the C function to be compiled in.
+ */
+ .if 0
+common_dumpRegs:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bl dvmMterpDumpArmRegs
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+ .endif
+
+#if 0
+/*
+ * Experiment on VFP mode.
+ *
+ * uint32_t setFPSCR(uint32_t val, uint32_t mask)
+ *
+ * Updates the bits specified by "mask", setting them to the values in "val".
+ */
+setFPSCR:
+ and r0, r0, r1 @ make sure no stray bits are set
+ fmrx r2, fpscr @ get VFP reg
+ mvn r1, r1 @ bit-invert mask
+ and r2, r2, r1 @ clear masked bits
+ orr r2, r2, r0 @ set specified bits
+ fmxr fpscr, r2 @ set VFP reg
+ mov r0, r2 @ return new value
+ bx lr
+
+ .align 2
+ .global dvmConfigureFP
+ .type dvmConfigureFP, %function
+dvmConfigureFP:
+ stmfd sp!, {ip, lr}
+ /* 0x03000000 sets DN/FZ */
+ /* 0x00009f00 clears the six exception enable flags */
+ bl common_squeak0
+ mov r0, #0x03000000 @ r0<- 0x03000000
+ add r1, r0, #0x9f00 @ r1<- 0x03009f00
+ bl setFPSCR
+ ldmfd sp!, {ip, pc}
+#endif
+
+
+/*
+ * String references, must be close to the code that uses them.
+ */
+ .align 2
+strArithmeticException:
+ .word .LstrArithmeticException
+strArrayIndexException:
+ .word .LstrArrayIndexException
+strArrayStoreException:
+ .word .LstrArrayStoreException
+strDivideByZero:
+ .word .LstrDivideByZero
+strNegativeArraySizeException:
+ .word .LstrNegativeArraySizeException
+strNoSuchMethodError:
+ .word .LstrNoSuchMethodError
+strNullPointerException:
+ .word .LstrNullPointerException
+
+strLogTag:
+ .word .LstrLogTag
+strExceptionNotCaughtLocally:
+ .word .LstrExceptionNotCaughtLocally
+
+strNewline:
+ .word .LstrNewline
+strSqueak:
+ .word .LstrSqueak
+strPrintHex:
+ .word .LstrPrintHex
+strPrintLong:
+ .word .LstrPrintLong
+
+/*
+ * Zero-terminated ASCII string data.
+ *
+ * On ARM we have two choices: do like gcc does, and LDR from a .word
+ * with the address, or use an ADR pseudo-op to get the address
+ * directly. ADR saves 4 bytes and an indirection, but it's using a
+ * PC-relative addressing mode and hence has a limited range, which
+ * makes it not work well with mergeable string sections.
+ */
+ .section .rodata.str1.4,"aMS",%progbits,1
+
+.LstrBadEntryPoint:
+ .asciz "Bad entry point %d\n"
+.LstrArithmeticException:
+ .asciz "Ljava/lang/ArithmeticException;"
+.LstrArrayIndexException:
+ .asciz "Ljava/lang/ArrayIndexOutOfBoundsException;"
+.LstrArrayStoreException:
+ .asciz "Ljava/lang/ArrayStoreException;"
+.LstrClassCastException:
+ .asciz "Ljava/lang/ClassCastException;"
+.LstrDivideByZero:
+ .asciz "divide by zero"
+.LstrFilledNewArrayNotImpl:
+ .asciz "filled-new-array only implemented for objects and 'int'"
+.LstrInternalError:
+ .asciz "Ljava/lang/InternalError;"
+.LstrInstantiationError:
+ .asciz "Ljava/lang/InstantiationError;"
+.LstrNegativeArraySizeException:
+ .asciz "Ljava/lang/NegativeArraySizeException;"
+.LstrNoSuchMethodError:
+ .asciz "Ljava/lang/NoSuchMethodError;"
+.LstrNullPointerException:
+ .asciz "Ljava/lang/NullPointerException;"
+
+.LstrLogTag:
+ .asciz "mterp"
+.LstrExceptionNotCaughtLocally:
+ .asciz "Exception %s from %s:%d not caught locally\n"
+
+.LstrNewline:
+ .asciz "\n"
+.LstrSqueak:
+ .asciz "<%d>"
+.LstrPrintHex:
+ .asciz "<0x%x>"
+.LstrPrintLong:
+ .asciz "<%lld>"
+
diff --git a/vm/mterp/out/InterpAsm-armv5te.S b/vm/mterp/out/InterpAsm-armv5te.S
new file mode 100644
index 0000000..e6abac5
--- /dev/null
+++ b/vm/mterp/out/InterpAsm-armv5te.S
@@ -0,0 +1,11073 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'armv5te'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: armv5te/header.S */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * ARMv5 definitions and declarations.
+ */
+
+/*
+ARM EABI general notes:
+
+r0-r3 hold first 4 args to a method; they are not preserved across method calls
+r4-r8 are available for general use
+r9 is given special treatment in some situations, but not for us
+r10 (sl) seems to be generally available
+r11 (fp) is used by gcc (unless -fomit-frame-pointer is set)
+r12 (ip) is scratch -- not preserved across method calls
+r13 (sp) should be managed carefully in case a signal arrives
+r14 (lr) must be preserved
+r15 (pc) can be tinkered with directly
+
+r0 holds returns of <= 4 bytes
+r0-r1 hold returns of 8 bytes, low word in r0
+
+Callee must save/restore r4+ (except r12) if it modifies them. If VFP
+is present, registers s16-s31 (a/k/a d8-d15, a/k/a q4-q7) must be preserved,
+s0-s15 (d0-d7, q0-a3) do not need to be.
+
+Stack is "full descending". Only the arguments that don't fit in the first 4
+registers are placed on the stack. "sp" points at the first stacked argument
+(i.e. the 5th arg).
+
+VFP: single-precision results in s0, double-precision results in d0.
+
+In the EABI, "sp" must be 64-bit aligned on entry to a function, and any
+64-bit quantities (long long, double) must be 64-bit aligned.
+*/
+
+/*
+Mterp and ARM notes:
+
+The following registers have fixed assignments:
+
+ reg nick purpose
+ r4 rPC interpreted program counter, used for fetching instructions
+ r5 rFP interpreted frame pointer, used for accessing locals and args
+ r6 rGLUE MterpGlue pointer
+ r7 rINST first 16-bit code unit of current instruction
+ r8 rIBASE interpreted instruction base pointer, used for computed goto
+
+Macros are provided for common operations. Each macro MUST emit only
+one instruction to make instruction-counting easier. They MUST NOT alter
+unspecified registers or condition codes.
+*/
+
+/* single-purpose registers, given names for clarity */
+#define rPC r4
+#define rFP r5
+#define rGLUE r6
+#define rINST r7
+#define rIBASE r8
+
+/* save/restore the PC and/or FP from the glue struct */
+#define LOAD_PC_FROM_GLUE() ldr rPC, [rGLUE, #offGlue_pc]
+#define SAVE_PC_TO_GLUE() str rPC, [rGLUE, #offGlue_pc]
+#define LOAD_FP_FROM_GLUE() ldr rFP, [rGLUE, #offGlue_fp]
+#define SAVE_FP_TO_GLUE() str rFP, [rGLUE, #offGlue_fp]
+#define LOAD_PC_FP_FROM_GLUE() ldmia rGLUE, {rPC, rFP}
+#define SAVE_PC_FP_TO_GLUE() stmia rGLUE, {rPC, rFP}
+
+/*
+ * "export" the PC to the stack frame, f/b/o future exception objects. Must
+ * be done *before* something calls dvmThrowException.
+ *
+ * In C this is "SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc", i.e.
+ * fp - sizeof(StackSaveArea) + offsetof(SaveArea, xtra.currentPc)
+ *
+ * It's okay to do this more than once.
+ */
+#define EXPORT_PC() \
+ str rPC, [rFP, #(-sizeofStackSaveArea + offStackSaveArea_currentPc)]
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+#define SAVEAREA_FROM_FP(_reg, _fpreg) \
+ sub _reg, _fpreg, #sizeofStackSaveArea
+
+/*
+ * Fetch the next instruction from rPC into rINST. Does not advance rPC.
+ */
+#define FETCH_INST() ldrh rINST, [rPC]
+
+/*
+ * Fetch the next instruction from the specified offset. Advances rPC
+ * to point to the next instruction. "_count" is in 16-bit code units.
+ *
+ * Because of the limited size of immediate constants on ARM, this is only
+ * suitable for small forward movements (i.e. don't try to implement "goto"
+ * with this).
+ *
+ * This must come AFTER anything that can throw an exception, or the
+ * exception catch may miss. (This also implies that it must come after
+ * EXPORT_PC().)
+ */
+#define FETCH_ADVANCE_INST(_count) ldrh rINST, [rPC, #(_count*2)]!
+
+/*
+ * The operation performed here is similar to FETCH_ADVANCE_INST, except the
+ * src and dest registers are parameterized (not hard-wired to rPC and rINST).
+ */
+#define PREFETCH_ADVANCE_INST(_dreg, _sreg, _count) \
+ ldrh _dreg, [_sreg, #(_count*2)]!
+
+/*
+ * Fetch the next instruction from an offset specified by _reg. Updates
+ * rPC to point to the next instruction. "_reg" must specify the distance
+ * in bytes, *not* 16-bit code units, and may be a signed value.
+ *
+ * We want to write "ldrh rINST, [rPC, _reg, lsl #2]!", but some of the
+ * bits that hold the shift distance are used for the half/byte/sign flags.
+ * In some cases we can pre-double _reg for free, so we require a byte offset
+ * here.
+ */
+#define FETCH_ADVANCE_INST_RB(_reg) ldrh rINST, [rPC, _reg]!
+
+/*
+ * Fetch a half-word code unit from an offset past the current PC. The
+ * "_count" value is in 16-bit code units. Does not advance rPC.
+ *
+ * The "_S" variant works the same but treats the value as signed.
+ */
+#define FETCH(_reg, _count) ldrh _reg, [rPC, #(_count*2)]
+#define FETCH_S(_reg, _count) ldrsh _reg, [rPC, #(_count*2)]
+
+/*
+ * Fetch one byte from an offset past the current PC. Pass in the same
+ * "_count" as you would for FETCH, and an additional 0/1 indicating which
+ * byte of the halfword you want (lo/hi).
+ */
+#define FETCH_B(_reg, _count, _byte) ldrb _reg, [rPC, #(_count*2+_byte)]
+
+/*
+ * Put the instruction's opcode field into the specified register.
+ */
+#define GET_INST_OPCODE(_reg) and _reg, rINST, #255
+
+/*
+ * Put the prefetched instruction's opcode field into the specified register.
+ */
+#define GET_PREFETCHED_OPCODE(_oreg, _ireg) and _oreg, _ireg, #255
+
+/*
+ * Begin executing the opcode in _reg. Because this only jumps within the
+ * interpreter, we don't have to worry about pre-ARMv5 THUMB interwork.
+ */
+#define GOTO_OPCODE(_reg) add pc, rIBASE, _reg, lsl #6
+#define GOTO_OPCODE_IFEQ(_reg) addeq pc, rIBASE, _reg, lsl #6
+#define GOTO_OPCODE_IFNE(_reg) addne pc, rIBASE, _reg, lsl #6
+
+/*
+ * Get/set the 32-bit value from a Dalvik register.
+ */
+#define GET_VREG(_reg, _vreg) ldr _reg, [rFP, _vreg, lsl #2]
+#define SET_VREG(_reg, _vreg) str _reg, [rFP, _vreg, lsl #2]
+
+#if defined(WITH_JIT)
+#define GET_JIT_PROF_TABLE(_reg) ldr _reg,[rGLUE,#offGlue_pJitProfTable]
+#define GET_JIT_THRESHOLD(_reg) ldr _reg,[rGLUE,#offGlue_jitThreshold]
+#endif
+
+/*
+ * Convert a virtual register index into an address.
+ */
+#define VREG_INDEX_TO_ADDR(_reg, _vreg) \
+ add _reg, rFP, _vreg, lsl #2
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../common/asm-constants.h"
+
+#if defined(WITH_JIT)
+#include "../common/jit-config.h"
+#endif
+
+/* File: armv5te/platform.S */
+/*
+ * ===========================================================================
+ * CPU-version-specific defines
+ * ===========================================================================
+ */
+
+/*
+ * Macro for "LDR PC,xxx", which is not allowed pre-ARMv5. Essentially a
+ * one-way branch.
+ *
+ * May modify IP. Does not modify LR.
+ */
+.macro LDR_PC source
+ ldr pc, \source
+.endm
+
+/*
+ * Macro for "MOV LR,PC / LDR PC,xxx", which is not allowed pre-ARMv5.
+ * Jump to subroutine.
+ *
+ * May modify IP and LR.
+ */
+.macro LDR_PC_LR source
+ mov lr, pc
+ ldr pc, \source
+.endm
+
+/*
+ * Macro for "LDMFD SP!, {...regs...,PC}".
+ *
+ * May modify IP and LR.
+ */
+.macro LDMFD_PC regs
+ ldmfd sp!, {\regs,pc}
+.endm
+
+/*
+ * Macro for data memory barrier; not meaningful pre-ARMv6K.
+ */
+.macro SMP_DMB
+.endm
+
+/* File: armv5te/entry.S */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Interpreter entry point.
+ */
+
+/*
+ * We don't have formal stack frames, so gdb scans upward in the code
+ * to find the start of the function (a label with the %function type),
+ * and then looks at the next few instructions to figure out what
+ * got pushed onto the stack. From this it figures out how to restore
+ * the registers, including PC, for the previous stack frame. If gdb
+ * sees a non-function label, it stops scanning, so either we need to
+ * have nothing but assembler-local labels between the entry point and
+ * the break, or we need to fake it out.
+ *
+ * When this is defined, we add some stuff to make gdb less confused.
+ */
+#define ASSIST_DEBUGGER 1
+
+ .text
+ .align 2
+ .global dvmMterpStdRun
+ .type dvmMterpStdRun, %function
+
+/*
+ * On entry:
+ * r0 MterpGlue* glue
+ *
+ * This function returns a boolean "changeInterp" value. The return comes
+ * via a call to dvmMterpStdBail().
+ */
+dvmMterpStdRun:
+#define MTERP_ENTRY1 \
+ .save {r4-r10,fp,lr}; \
+ stmfd sp!, {r4-r10,fp,lr} @ save 9 regs
+#define MTERP_ENTRY2 \
+ .pad #4; \
+ sub sp, sp, #4 @ align 64
+
+ .fnstart
+ MTERP_ENTRY1
+ MTERP_ENTRY2
+
+ /* save stack pointer, add magic word for debuggerd */
+ str sp, [r0, #offGlue_bailPtr] @ save SP for eventual return
+
+ /* set up "named" registers, figure out entry point */
+ mov rGLUE, r0 @ set rGLUE
+ ldr r1, [r0, #offGlue_entryPoint] @ enum is 4 bytes in aapcs-EABI
+ LOAD_PC_FP_FROM_GLUE() @ load rPC and rFP from "glue"
+ adr rIBASE, dvmAsmInstructionStart @ set rIBASE
+ cmp r1, #kInterpEntryInstr @ usual case?
+ bne .Lnot_instr @ no, handle it
+
+#if defined(WITH_JIT)
+.LentryInstr:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ /* Entry is always a possible trace start */
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_INST()
+ mov r1, #0 @ prepare the value for the new state
+ str r1, [r10, #offThread_inJitCodeCache] @ back to the interp land
+ cmp r0,#0 @ is profiling disabled?
+#if !defined(WITH_SELF_VERIFICATION)
+ bne common_updateProfile @ profiling is enabled
+#else
+ ldr r2, [r10, #offThread_shadowSpace] @ to find out the jit exit state
+ beq 1f @ profiling is disabled
+ ldr r3, [r2, #offShadowSpace_jitExitState] @ jit exit state
+ cmp r3, #kSVSTraceSelect @ hot trace following?
+ moveq r2,#kJitTSelectRequestHot @ ask for trace selection
+ beq common_selectTrace @ go build the trace
+ cmp r3, #kSVSNoProfile @ don't profile the next instruction?
+ beq 1f @ intrepret the next instruction
+ b common_updateProfile @ collect profiles
+#endif
+1:
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+#else
+ /* start executing the instruction at rPC */
+ FETCH_INST() @ load rINST from rPC
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+.Lnot_instr:
+ cmp r1, #kInterpEntryReturn @ were we returning from a method?
+ beq common_returnFromMethod
+
+.Lnot_return:
+ cmp r1, #kInterpEntryThrow @ were we throwing an exception?
+ beq common_exceptionThrown
+
+#if defined(WITH_JIT)
+.Lnot_throw:
+ ldr r10,[rGLUE, #offGlue_jitResumeNPC]
+ ldr r2,[rGLUE, #offGlue_jitResumeDPC]
+ cmp r1, #kInterpEntryResume @ resuming after Jit single-step?
+ bne .Lbad_arg
+ cmp rPC,r2
+ bne .LentryInstr @ must have branched, don't resume
+#if defined(WITH_SELF_VERIFICATION)
+ @ glue->entryPoint will be set in dvmSelfVerificationSaveState
+ b jitSVShadowRunStart @ re-enter the translation after the
+ @ single-stepped instruction
+ @noreturn
+#endif
+ mov r1, #kInterpEntryInstr
+ str r1, [rGLUE, #offGlue_entryPoint]
+ bx r10 @ re-enter the translation
+#endif
+
+.Lbad_arg:
+ ldr r0, strBadEntryPoint
+ @ r1 holds value of entryPoint
+ bl printf
+ bl dvmAbort
+ .fnend
+
+
+ .global dvmMterpStdBail
+ .type dvmMterpStdBail, %function
+
+/*
+ * Restore the stack pointer and PC from the save point established on entry.
+ * This is essentially the same as a longjmp, but should be cheaper. The
+ * last instruction causes us to return to whoever called dvmMterpStdRun.
+ *
+ * We pushed some registers on the stack in dvmMterpStdRun, then saved
+ * SP and LR. Here we restore SP, restore the registers, and then restore
+ * LR to PC.
+ *
+ * On entry:
+ * r0 MterpGlue* glue
+ * r1 bool changeInterp
+ */
+dvmMterpStdBail:
+ ldr sp, [r0, #offGlue_bailPtr] @ sp<- saved SP
+ mov r0, r1 @ return the changeInterp value
+ add sp, sp, #4 @ un-align 64
+ LDMFD_PC "r4-r10,fp" @ restore 9 regs and return
+
+
+/*
+ * String references.
+ */
+strBadEntryPoint:
+ .word .LstrBadEntryPoint
+
+
+ .global dvmAsmInstructionStart
+ .type dvmAsmInstructionStart, %function
+dvmAsmInstructionStart = .L_OP_NOP
+ .text
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NOP: /* 0x00 */
+/* File: armv5te/OP_NOP.S */
+ FETCH_ADVANCE_INST(1) @ advance to next instr, load rINST
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ GOTO_OPCODE(ip) @ execute it
+
+#ifdef ASSIST_DEBUGGER
+ /* insert fake function header to help gdb find the stack frame */
+ .type dalvik_inst, %function
+dalvik_inst:
+ .fnstart
+ MTERP_ENTRY1
+ MTERP_ENTRY2
+ .fnend
+#endif
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE: /* 0x01 */
+/* File: armv5te/OP_MOVE.S */
+ /* for move, move-object, long-to-int */
+ /* op vA, vB */
+ mov r1, rINST, lsr #12 @ r1<- B from 15:12
+ mov r0, rINST, lsr #8 @ r0<- A from 11:8
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[B]
+ and r0, r0, #15
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ SET_VREG(r2, r0) @ fp[A]<- r2
+ GOTO_OPCODE(ip) @ execute next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_FROM16: /* 0x02 */
+/* File: armv5te/OP_MOVE_FROM16.S */
+ /* for: move/from16, move-object/from16 */
+ /* op vAA, vBBBB */
+ FETCH(r1, 1) @ r1<- BBBB
+ mov r0, rINST, lsr #8 @ r0<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[BBBB]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r0) @ fp[AA]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_16: /* 0x03 */
+/* File: armv5te/OP_MOVE_16.S */
+ /* for: move/16, move-object/16 */
+ /* op vAAAA, vBBBB */
+ FETCH(r1, 2) @ r1<- BBBB
+ FETCH(r0, 1) @ r0<- AAAA
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[BBBB]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r0) @ fp[AAAA]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_WIDE: /* 0x04 */
+/* File: armv5te/OP_MOVE_WIDE.S */
+ /* move-wide vA, vB */
+ /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+ mov r2, rINST, lsr #8 @ r2<- A(+)
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r2, r2, #15
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[A]
+ ldmia r3, {r0-r1} @ r0/r1<- fp[B]
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r2, {r0-r1} @ fp[A]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_WIDE_FROM16: /* 0x05 */
+/* File: armv5te/OP_MOVE_WIDE_FROM16.S */
+ /* move-wide/from16 vAA, vBBBB */
+ /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+ FETCH(r3, 1) @ r3<- BBBB
+ mov r2, rINST, lsr #8 @ r2<- AA
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[BBBB]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[AA]
+ ldmia r3, {r0-r1} @ r0/r1<- fp[BBBB]
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r2, {r0-r1} @ fp[AA]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_WIDE_16: /* 0x06 */
+/* File: armv5te/OP_MOVE_WIDE_16.S */
+ /* move-wide/16 vAAAA, vBBBB */
+ /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+ FETCH(r3, 2) @ r3<- BBBB
+ FETCH(r2, 1) @ r2<- AAAA
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[BBBB]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[AAAA]
+ ldmia r3, {r0-r1} @ r0/r1<- fp[BBBB]
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r2, {r0-r1} @ fp[AAAA]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_OBJECT: /* 0x07 */
+/* File: armv5te/OP_MOVE_OBJECT.S */
+/* File: armv5te/OP_MOVE.S */
+ /* for move, move-object, long-to-int */
+ /* op vA, vB */
+ mov r1, rINST, lsr #12 @ r1<- B from 15:12
+ mov r0, rINST, lsr #8 @ r0<- A from 11:8
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[B]
+ and r0, r0, #15
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ SET_VREG(r2, r0) @ fp[A]<- r2
+ GOTO_OPCODE(ip) @ execute next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_OBJECT_FROM16: /* 0x08 */
+/* File: armv5te/OP_MOVE_OBJECT_FROM16.S */
+/* File: armv5te/OP_MOVE_FROM16.S */
+ /* for: move/from16, move-object/from16 */
+ /* op vAA, vBBBB */
+ FETCH(r1, 1) @ r1<- BBBB
+ mov r0, rINST, lsr #8 @ r0<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[BBBB]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r0) @ fp[AA]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_OBJECT_16: /* 0x09 */
+/* File: armv5te/OP_MOVE_OBJECT_16.S */
+/* File: armv5te/OP_MOVE_16.S */
+ /* for: move/16, move-object/16 */
+ /* op vAAAA, vBBBB */
+ FETCH(r1, 2) @ r1<- BBBB
+ FETCH(r0, 1) @ r0<- AAAA
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[BBBB]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r0) @ fp[AAAA]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_RESULT: /* 0x0a */
+/* File: armv5te/OP_MOVE_RESULT.S */
+ /* for: move-result, move-result-object */
+ /* op vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ ldr r0, [rGLUE, #offGlue_retval] @ r0<- glue->retval.i
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[AA]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_RESULT_WIDE: /* 0x0b */
+/* File: armv5te/OP_MOVE_RESULT_WIDE.S */
+ /* move-result-wide vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ add r3, rGLUE, #offGlue_retval @ r3<- &glue->retval
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[AA]
+ ldmia r3, {r0-r1} @ r0/r1<- retval.j
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r2, {r0-r1} @ fp[AA]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_RESULT_OBJECT: /* 0x0c */
+/* File: armv5te/OP_MOVE_RESULT_OBJECT.S */
+/* File: armv5te/OP_MOVE_RESULT.S */
+ /* for: move-result, move-result-object */
+ /* op vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ ldr r0, [rGLUE, #offGlue_retval] @ r0<- glue->retval.i
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[AA]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_EXCEPTION: /* 0x0d */
+/* File: armv5te/OP_MOVE_EXCEPTION.S */
+ /* move-exception vAA */
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ mov r2, rINST, lsr #8 @ r2<- AA
+ ldr r3, [r0, #offThread_exception] @ r3<- dvmGetException bypass
+ mov r1, #0 @ r1<- 0
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ SET_VREG(r3, r2) @ fp[AA]<- exception obj
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ str r1, [r0, #offThread_exception] @ dvmClearException bypass
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RETURN_VOID: /* 0x0e */
+/* File: armv5te/OP_RETURN_VOID.S */
+ b common_returnFromMethod
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RETURN: /* 0x0f */
+/* File: armv5te/OP_RETURN.S */
+ /*
+ * Return a 32-bit value. Copies the return value into the "glue"
+ * structure, then jumps to the return handler.
+ *
+ * for: return, return-object
+ */
+ /* op vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ GET_VREG(r0, r2) @ r0<- vAA
+ str r0, [rGLUE, #offGlue_retval] @ retval.i <- vAA
+ b common_returnFromMethod
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RETURN_WIDE: /* 0x10 */
+/* File: armv5te/OP_RETURN_WIDE.S */
+ /*
+ * Return a 64-bit value. Copies the return value into the "glue"
+ * structure, then jumps to the return handler.
+ */
+ /* return-wide vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[AA]
+ add r3, rGLUE, #offGlue_retval @ r3<- &glue->retval
+ ldmia r2, {r0-r1} @ r0/r1 <- vAA/vAA+1
+ stmia r3, {r0-r1} @ retval<- r0/r1
+ b common_returnFromMethod
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RETURN_OBJECT: /* 0x11 */
+/* File: armv5te/OP_RETURN_OBJECT.S */
+/* File: armv5te/OP_RETURN.S */
+ /*
+ * Return a 32-bit value. Copies the return value into the "glue"
+ * structure, then jumps to the return handler.
+ *
+ * for: return, return-object
+ */
+ /* op vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ GET_VREG(r0, r2) @ r0<- vAA
+ str r0, [rGLUE, #offGlue_retval] @ retval.i <- vAA
+ b common_returnFromMethod
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_4: /* 0x12 */
+/* File: armv5te/OP_CONST_4.S */
+ /* const/4 vA, #+B */
+ mov r1, rINST, lsl #16 @ r1<- Bxxx0000
+ mov r0, rINST, lsr #8 @ r0<- A+
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ mov r1, r1, asr #28 @ r1<- sssssssB (sign-extended)
+ and r0, r0, #15
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ SET_VREG(r1, r0) @ fp[A]<- r1
+ GOTO_OPCODE(ip) @ execute next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_16: /* 0x13 */
+/* File: armv5te/OP_CONST_16.S */
+ /* const/16 vAA, #+BBBB */
+ FETCH_S(r0, 1) @ r0<- ssssBBBB (sign-extended)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r0, r3) @ vAA<- r0
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST: /* 0x14 */
+/* File: armv5te/OP_CONST.S */
+ /* const vAA, #+BBBBbbbb */
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH(r0, 1) @ r0<- bbbb (low)
+ FETCH(r1, 2) @ r1<- BBBB (high)
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ orr r0, r0, r1, lsl #16 @ r0<- BBBBbbbb
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r3) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_HIGH16: /* 0x15 */
+/* File: armv5te/OP_CONST_HIGH16.S */
+ /* const/high16 vAA, #+BBBB0000 */
+ FETCH(r0, 1) @ r0<- 0000BBBB (zero-extended)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ mov r0, r0, lsl #16 @ r0<- BBBB0000
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r0, r3) @ vAA<- r0
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_WIDE_16: /* 0x16 */
+/* File: armv5te/OP_CONST_WIDE_16.S */
+ /* const-wide/16 vAA, #+BBBB */
+ FETCH_S(r0, 1) @ r0<- ssssBBBB (sign-extended)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ mov r1, r0, asr #31 @ r1<- ssssssss
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_WIDE_32: /* 0x17 */
+/* File: armv5te/OP_CONST_WIDE_32.S */
+ /* const-wide/32 vAA, #+BBBBbbbb */
+ FETCH(r0, 1) @ r0<- 0000bbbb (low)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH_S(r2, 2) @ r2<- ssssBBBB (high)
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ orr r0, r0, r2, lsl #16 @ r0<- BBBBbbbb
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[AA]
+ mov r1, r0, asr #31 @ r1<- ssssssss
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_WIDE: /* 0x18 */
+/* File: armv5te/OP_CONST_WIDE.S */
+ /* const-wide vAA, #+HHHHhhhhBBBBbbbb */
+ FETCH(r0, 1) @ r0<- bbbb (low)
+ FETCH(r1, 2) @ r1<- BBBB (low middle)
+ FETCH(r2, 3) @ r2<- hhhh (high middle)
+ orr r0, r0, r1, lsl #16 @ r0<- BBBBbbbb (low word)
+ FETCH(r3, 4) @ r3<- HHHH (high)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ orr r1, r2, r3, lsl #16 @ r1<- HHHHhhhh (high word)
+ FETCH_ADVANCE_INST(5) @ advance rPC, load rINST
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_WIDE_HIGH16: /* 0x19 */
+/* File: armv5te/OP_CONST_WIDE_HIGH16.S */
+ /* const-wide/high16 vAA, #+BBBB000000000000 */
+ FETCH(r1, 1) @ r1<- 0000BBBB (zero-extended)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ mov r0, #0 @ r0<- 00000000
+ mov r1, r1, lsl #16 @ r1<- BBBB0000
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_STRING: /* 0x1a */
+/* File: armv5te/OP_CONST_STRING.S */
+ /* const/string vAA, String@BBBB */
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- glue->methodClassDex
+ mov r9, rINST, lsr #8 @ r9<- AA
+ ldr r2, [r2, #offDvmDex_pResStrings] @ r2<- dvmDex->pResStrings
+ ldr r0, [r2, r1, lsl #2] @ r0<- pResStrings[BBBB]
+ cmp r0, #0 @ not yet resolved?
+ beq .LOP_CONST_STRING_resolve
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_STRING_JUMBO: /* 0x1b */
+/* File: armv5te/OP_CONST_STRING_JUMBO.S */
+ /* const/string vAA, String@BBBBBBBB */
+ FETCH(r0, 1) @ r0<- bbbb (low)
+ FETCH(r1, 2) @ r1<- BBBB (high)
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- glue->methodClassDex
+ mov r9, rINST, lsr #8 @ r9<- AA
+ ldr r2, [r2, #offDvmDex_pResStrings] @ r2<- dvmDex->pResStrings
+ orr r1, r0, r1, lsl #16 @ r1<- BBBBbbbb
+ ldr r0, [r2, r1, lsl #2] @ r0<- pResStrings[BBBB]
+ cmp r0, #0
+ beq .LOP_CONST_STRING_JUMBO_resolve
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_CLASS: /* 0x1c */
+/* File: armv5te/OP_CONST_CLASS.S */
+ /* const/class vAA, Class@BBBB */
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- glue->methodClassDex
+ mov r9, rINST, lsr #8 @ r9<- AA
+ ldr r2, [r2, #offDvmDex_pResClasses] @ r2<- dvmDex->pResClasses
+ ldr r0, [r2, r1, lsl #2] @ r0<- pResClasses[BBBB]
+ cmp r0, #0 @ not yet resolved?
+ beq .LOP_CONST_CLASS_resolve
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MONITOR_ENTER: /* 0x1d */
+/* File: armv5te/OP_MONITOR_ENTER.S */
+ /*
+ * Synchronize on an object.
+ */
+ /* monitor-enter vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ GET_VREG(r1, r2) @ r1<- vAA (object)
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ cmp r1, #0 @ null object?
+ EXPORT_PC() @ need for precise GC, MONITOR_TRACKING
+ beq common_errNullObject @ null object, throw an exception
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ bl dvmLockObject @ call(self, obj)
+#ifdef WITH_DEADLOCK_PREDICTION /* implies WITH_MONITOR_TRACKING */
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ ldr r1, [r0, #offThread_exception] @ check for exception
+ cmp r1, #0
+ bne common_exceptionThrown @ exception raised, bail out
+#endif
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MONITOR_EXIT: /* 0x1e */
+/* File: armv5te/OP_MONITOR_EXIT.S */
+ /*
+ * Unlock an object.
+ *
+ * Exceptions that occur when unlocking a monitor need to appear as
+ * if they happened at the following instruction. See the Dalvik
+ * instruction spec.
+ */
+ /* monitor-exit vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ EXPORT_PC() @ before fetch: export the PC
+ GET_VREG(r1, r2) @ r1<- vAA (object)
+ cmp r1, #0 @ null object?
+ beq 1f @ yes
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ bl dvmUnlockObject @ r0<- success for unlock(self, obj)
+ cmp r0, #0 @ failed?
+ FETCH_ADVANCE_INST(1) @ before throw: advance rPC, load rINST
+ beq common_exceptionThrown @ yes, exception is pending
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+1:
+ FETCH_ADVANCE_INST(1) @ advance before throw
+ b common_errNullObject
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CHECK_CAST: /* 0x1f */
+/* File: armv5te/OP_CHECK_CAST.S */
+ /*
+ * Check to see if a cast from one class to another is allowed.
+ */
+ /* check-cast vAA, class@BBBB */
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH(r2, 1) @ r2<- BBBB
+ GET_VREG(r9, r3) @ r9<- object
+ ldr r0, [rGLUE, #offGlue_methodClassDex] @ r0<- pDvmDex
+ cmp r9, #0 @ is object null?
+ ldr r0, [r0, #offDvmDex_pResClasses] @ r0<- pDvmDex->pResClasses
+ beq .LOP_CHECK_CAST_okay @ null obj, cast always succeeds
+ ldr r1, [r0, r2, lsl #2] @ r1<- resolved class
+ ldr r0, [r9, #offObject_clazz] @ r0<- obj->clazz
+ cmp r1, #0 @ have we resolved this before?
+ beq .LOP_CHECK_CAST_resolve @ not resolved, do it now
+.LOP_CHECK_CAST_resolved:
+ cmp r0, r1 @ same class (trivial success)?
+ bne .LOP_CHECK_CAST_fullcheck @ no, do full check
+.LOP_CHECK_CAST_okay:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INSTANCE_OF: /* 0x20 */
+/* File: armv5te/OP_INSTANCE_OF.S */
+ /*
+ * Check to see if an object reference is an instance of a class.
+ *
+ * Most common situation is a non-null object, being compared against
+ * an already-resolved class.
+ */
+ /* instance-of vA, vB, class@CCCC */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r3) @ r0<- vB (object)
+ and r9, r9, #15 @ r9<- A
+ cmp r0, #0 @ is object null?
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- pDvmDex
+ beq .LOP_INSTANCE_OF_store @ null obj, not an instance, store r0
+ FETCH(r3, 1) @ r3<- CCCC
+ ldr r2, [r2, #offDvmDex_pResClasses] @ r2<- pDvmDex->pResClasses
+ ldr r1, [r2, r3, lsl #2] @ r1<- resolved class
+ ldr r0, [r0, #offObject_clazz] @ r0<- obj->clazz
+ cmp r1, #0 @ have we resolved this before?
+ beq .LOP_INSTANCE_OF_resolve @ not resolved, do it now
+.LOP_INSTANCE_OF_resolved: @ r0=obj->clazz, r1=resolved class
+ cmp r0, r1 @ same class (trivial success)?
+ beq .LOP_INSTANCE_OF_trivial @ yes, trivial finish
+ b .LOP_INSTANCE_OF_fullcheck @ no, do full check
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ARRAY_LENGTH: /* 0x21 */
+/* File: armv5te/OP_ARRAY_LENGTH.S */
+ /*
+ * Return the length of an array.
+ */
+ mov r1, rINST, lsr #12 @ r1<- B
+ mov r2, rINST, lsr #8 @ r2<- A+
+ GET_VREG(r0, r1) @ r0<- vB (object ref)
+ and r2, r2, #15 @ r2<- A
+ cmp r0, #0 @ is object null?
+ beq common_errNullObject @ yup, fail
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ ldr r3, [r0, #offArrayObject_length] @ r3<- array length
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r3, r2) @ vB<- length
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEW_INSTANCE: /* 0x22 */
+/* File: armv5te/OP_NEW_INSTANCE.S */
+ /*
+ * Create a new instance of a class.
+ */
+ /* new-instance vAA, class@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResClasses] @ r3<- pDvmDex->pResClasses
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved class
+ EXPORT_PC() @ req'd for init, resolve, alloc
+ cmp r0, #0 @ already resolved?
+ beq .LOP_NEW_INSTANCE_resolve @ no, resolve it now
+.LOP_NEW_INSTANCE_resolved: @ r0=class
+ ldrb r1, [r0, #offClassObject_status] @ r1<- ClassStatus enum
+ cmp r1, #CLASS_INITIALIZED @ has class been initialized?
+ bne .LOP_NEW_INSTANCE_needinit @ no, init class now
+.LOP_NEW_INSTANCE_initialized: @ r0=class
+ mov r1, #ALLOC_DONT_TRACK @ flags for alloc call
+ bl dvmAllocObject @ r0<- new object
+ b .LOP_NEW_INSTANCE_finish @ continue
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEW_ARRAY: /* 0x23 */
+/* File: armv5te/OP_NEW_ARRAY.S */
+ /*
+ * Allocate an array of objects, specified with the array class
+ * and a count.
+ *
+ * The verifier guarantees that this is an array class, so we don't
+ * check for it here.
+ */
+ /* new-array vA, vB, class@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ FETCH(r2, 1) @ r2<- CCCC
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ GET_VREG(r1, r0) @ r1<- vB (array length)
+ ldr r3, [r3, #offDvmDex_pResClasses] @ r3<- pDvmDex->pResClasses
+ cmp r1, #0 @ check length
+ ldr r0, [r3, r2, lsl #2] @ r0<- resolved class
+ bmi common_errNegativeArraySize @ negative length, bail
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ req'd for resolve, alloc
+ bne .LOP_NEW_ARRAY_finish @ resolved, continue
+ b .LOP_NEW_ARRAY_resolve @ do resolve now
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FILLED_NEW_ARRAY: /* 0x24 */
+/* File: armv5te/OP_FILLED_NEW_ARRAY.S */
+ /*
+ * Create a new array with elements filled from registers.
+ *
+ * for: filled-new-array, filled-new-array/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResClasses] @ r3<- pDvmDex->pResClasses
+ EXPORT_PC() @ need for resolve and alloc
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved class
+ mov r10, rINST, lsr #8 @ r10<- AA or BA
+ cmp r0, #0 @ already resolved?
+ bne .LOP_FILLED_NEW_ARRAY_continue @ yes, continue on
+8: ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ mov r2, #0 @ r2<- false
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- call(clazz, ref)
+ cmp r0, #0 @ got null?
+ beq common_exceptionThrown @ yes, handle exception
+ b .LOP_FILLED_NEW_ARRAY_continue
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FILLED_NEW_ARRAY_RANGE: /* 0x25 */
+/* File: armv5te/OP_FILLED_NEW_ARRAY_RANGE.S */
+/* File: armv5te/OP_FILLED_NEW_ARRAY.S */
+ /*
+ * Create a new array with elements filled from registers.
+ *
+ * for: filled-new-array, filled-new-array/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResClasses] @ r3<- pDvmDex->pResClasses
+ EXPORT_PC() @ need for resolve and alloc
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved class
+ mov r10, rINST, lsr #8 @ r10<- AA or BA
+ cmp r0, #0 @ already resolved?
+ bne .LOP_FILLED_NEW_ARRAY_RANGE_continue @ yes, continue on
+8: ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ mov r2, #0 @ r2<- false
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- call(clazz, ref)
+ cmp r0, #0 @ got null?
+ beq common_exceptionThrown @ yes, handle exception
+ b .LOP_FILLED_NEW_ARRAY_RANGE_continue
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FILL_ARRAY_DATA: /* 0x26 */
+/* File: armv5te/OP_FILL_ARRAY_DATA.S */
+ /* fill-array-data vAA, +BBBBBBBB */
+ FETCH(r0, 1) @ r0<- bbbb (lo)
+ FETCH(r1, 2) @ r1<- BBBB (hi)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ orr r1, r0, r1, lsl #16 @ r1<- BBBBbbbb
+ GET_VREG(r0, r3) @ r0<- vAA (array object)
+ add r1, rPC, r1, lsl #1 @ r1<- PC + BBBBbbbb*2 (array data off.)
+ EXPORT_PC();
+ bl dvmInterpHandleFillArrayData@ fill the array with predefined data
+ cmp r0, #0 @ 0 means an exception is thrown
+ beq common_exceptionThrown @ has exception
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_THROW: /* 0x27 */
+/* File: armv5te/OP_THROW.S */
+ /*
+ * Throw an exception object in the current thread.
+ */
+ /* throw vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ GET_VREG(r1, r2) @ r1<- vAA (exception object)
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ EXPORT_PC() @ exception handler can throw
+ cmp r1, #0 @ null object?
+ beq common_errNullObject @ yes, throw an NPE instead
+ @ bypass dvmSetException, just store it
+ str r1, [r0, #offThread_exception] @ thread->exception<- obj
+ b common_exceptionThrown
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_GOTO: /* 0x28 */
+/* File: armv5te/OP_GOTO.S */
+ /*
+ * Unconditional branch, 8-bit offset.
+ *
+ * The branch distance is a signed code-unit offset, which we need to
+ * double to get a byte offset.
+ */
+ /* goto +AA */
+ mov r0, rINST, lsl #16 @ r0<- AAxx0000
+ movs r9, r0, asr #24 @ r9<- ssssssAA (sign-extended)
+ mov r9, r9, lsl #1 @ r9<- byte offset
+ bmi common_backwardBranch @ backward branch, do periodic checks
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_GOTO_16: /* 0x29 */
+/* File: armv5te/OP_GOTO_16.S */
+ /*
+ * Unconditional branch, 16-bit offset.
+ *
+ * The branch distance is a signed code-unit offset, which we need to
+ * double to get a byte offset.
+ */
+ /* goto/16 +AAAA */
+ FETCH_S(r0, 1) @ r0<- ssssAAAA (sign-extended)
+ movs r9, r0, asl #1 @ r9<- byte offset, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_GOTO_32: /* 0x2a */
+/* File: armv5te/OP_GOTO_32.S */
+ /*
+ * Unconditional branch, 32-bit offset.
+ *
+ * The branch distance is a signed code-unit offset, which we need to
+ * double to get a byte offset.
+ *
+ * Unlike most opcodes, this one is allowed to branch to itself, so
+ * our "backward branch" test must be "<=0" instead of "<0". The ORRS
+ * instruction doesn't affect the V flag, so we need to clear it
+ * explicitly.
+ */
+ /* goto/32 +AAAAAAAA */
+ FETCH(r0, 1) @ r0<- aaaa (lo)
+ FETCH(r1, 2) @ r1<- AAAA (hi)
+ cmp ip, ip @ (clear V flag during stall)
+ orrs r0, r0, r1, lsl #16 @ r0<- AAAAaaaa, check sign
+ mov r9, r0, asl #1 @ r9<- byte offset
+ ble common_backwardBranch @ backward branch, do periodic checks
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_PACKED_SWITCH: /* 0x2b */
+/* File: armv5te/OP_PACKED_SWITCH.S */
+ /*
+ * Handle a packed-switch or sparse-switch instruction. In both cases
+ * we decode it and hand it off to a helper function.
+ *
+ * We don't really expect backward branches in a switch statement, but
+ * they're perfectly legal, so we check for them here.
+ *
+ * for: packed-switch, sparse-switch
+ */
+ /* op vAA, +BBBB */
+ FETCH(r0, 1) @ r0<- bbbb (lo)
+ FETCH(r1, 2) @ r1<- BBBB (hi)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ orr r0, r0, r1, lsl #16 @ r0<- BBBBbbbb
+ GET_VREG(r1, r3) @ r1<- vAA
+ add r0, rPC, r0, lsl #1 @ r0<- PC + BBBBbbbb*2
+ bl dvmInterpHandlePackedSwitch @ r0<- code-unit branch offset
+ movs r9, r0, asl #1 @ r9<- branch byte offset, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+ beq common_backwardBranch @ (want to use BLE but V is unknown)
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPARSE_SWITCH: /* 0x2c */
+/* File: armv5te/OP_SPARSE_SWITCH.S */
+/* File: armv5te/OP_PACKED_SWITCH.S */
+ /*
+ * Handle a packed-switch or sparse-switch instruction. In both cases
+ * we decode it and hand it off to a helper function.
+ *
+ * We don't really expect backward branches in a switch statement, but
+ * they're perfectly legal, so we check for them here.
+ *
+ * for: packed-switch, sparse-switch
+ */
+ /* op vAA, +BBBB */
+ FETCH(r0, 1) @ r0<- bbbb (lo)
+ FETCH(r1, 2) @ r1<- BBBB (hi)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ orr r0, r0, r1, lsl #16 @ r0<- BBBBbbbb
+ GET_VREG(r1, r3) @ r1<- vAA
+ add r0, rPC, r0, lsl #1 @ r0<- PC + BBBBbbbb*2
+ bl dvmInterpHandleSparseSwitch @ r0<- code-unit branch offset
+ movs r9, r0, asl #1 @ r9<- branch byte offset, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+ beq common_backwardBranch @ (want to use BLE but V is unknown)
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CMPL_FLOAT: /* 0x2d */
+/* File: armv5te/OP_CMPL_FLOAT.S */
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+ * on what value we'd like to return when one of the operands is NaN.
+ *
+ * The operation we're implementing is:
+ * if (x == y)
+ * return 0;
+ * else if (x < y)
+ * return -1;
+ * else if (x > y)
+ * return 1;
+ * else
+ * return {-1,1}; // one or both operands was NaN
+ *
+ * The straightforward implementation requires 3 calls to functions
+ * that return a result in r0. We can do it with two calls if our
+ * EABI library supports __aeabi_cfcmple (only one if we want to check
+ * for NaN directly):
+ * check x <= y
+ * if <, return -1
+ * if ==, return 0
+ * check y <= x
+ * if <, return 1
+ * return {-1,1}
+ *
+ * for: cmpl-float, cmpg-float
+ */
+ /* op vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ GET_VREG(r9, r2) @ r9<- vBB
+ GET_VREG(r10, r3) @ r10<- vCC
+ mov r0, r9 @ copy to arg registers
+ mov r1, r10
+ bl __aeabi_cfcmple @ cmp <=: C clear if <, Z set if eq
+ bhi .LOP_CMPL_FLOAT_gt_or_nan @ C set and Z clear, disambiguate
+ mvncc r1, #0 @ (less than) r1<- -1
+ moveq r1, #0 @ (equal) r1<- 0, trumps less than
+.LOP_CMPL_FLOAT_finish:
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r3) @ vAA<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CMPG_FLOAT: /* 0x2e */
+/* File: armv5te/OP_CMPG_FLOAT.S */
+/* File: armv5te/OP_CMPL_FLOAT.S */
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+ * on what value we'd like to return when one of the operands is NaN.
+ *
+ * The operation we're implementing is:
+ * if (x == y)
+ * return 0;
+ * else if (x < y)
+ * return -1;
+ * else if (x > y)
+ * return 1;
+ * else
+ * return {-1,1}; // one or both operands was NaN
+ *
+ * The straightforward implementation requires 3 calls to functions
+ * that return a result in r0. We can do it with two calls if our
+ * EABI library supports __aeabi_cfcmple (only one if we want to check
+ * for NaN directly):
+ * check x <= y
+ * if <, return -1
+ * if ==, return 0
+ * check y <= x
+ * if <, return 1
+ * return {-1,1}
+ *
+ * for: cmpl-float, cmpg-float
+ */
+ /* op vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ GET_VREG(r9, r2) @ r9<- vBB
+ GET_VREG(r10, r3) @ r10<- vCC
+ mov r0, r9 @ copy to arg registers
+ mov r1, r10
+ bl __aeabi_cfcmple @ cmp <=: C clear if <, Z set if eq
+ bhi .LOP_CMPG_FLOAT_gt_or_nan @ C set and Z clear, disambiguate
+ mvncc r1, #0 @ (less than) r1<- -1
+ moveq r1, #0 @ (equal) r1<- 0, trumps less than
+.LOP_CMPG_FLOAT_finish:
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r3) @ vAA<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CMPL_DOUBLE: /* 0x2f */
+/* File: armv5te/OP_CMPL_DOUBLE.S */
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+ * on what value we'd like to return when one of the operands is NaN.
+ *
+ * See OP_CMPL_FLOAT for an explanation.
+ *
+ * For: cmpl-double, cmpg-double
+ */
+ /* op vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ and r9, r0, #255 @ r9<- BB
+ mov r10, r0, lsr #8 @ r10<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[BB]
+ add r10, rFP, r10, lsl #2 @ r10<- &fp[CC]
+ ldmia r9, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r10, {r2-r3} @ r2/r3<- vCC/vCC+1
+ bl __aeabi_cdcmple @ cmp <=: C clear if <, Z set if eq
+ bhi .LOP_CMPL_DOUBLE_gt_or_nan @ C set and Z clear, disambiguate
+ mvncc r1, #0 @ (less than) r1<- -1
+ moveq r1, #0 @ (equal) r1<- 0, trumps less than
+.LOP_CMPL_DOUBLE_finish:
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r3) @ vAA<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CMPG_DOUBLE: /* 0x30 */
+/* File: armv5te/OP_CMPG_DOUBLE.S */
+/* File: armv5te/OP_CMPL_DOUBLE.S */
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * Provide a "naninst" instruction that puts 1 or -1 into r1 depending
+ * on what value we'd like to return when one of the operands is NaN.
+ *
+ * See OP_CMPL_FLOAT for an explanation.
+ *
+ * For: cmpl-double, cmpg-double
+ */
+ /* op vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ and r9, r0, #255 @ r9<- BB
+ mov r10, r0, lsr #8 @ r10<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[BB]
+ add r10, rFP, r10, lsl #2 @ r10<- &fp[CC]
+ ldmia r9, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r10, {r2-r3} @ r2/r3<- vCC/vCC+1
+ bl __aeabi_cdcmple @ cmp <=: C clear if <, Z set if eq
+ bhi .LOP_CMPG_DOUBLE_gt_or_nan @ C set and Z clear, disambiguate
+ mvncc r1, #0 @ (less than) r1<- -1
+ moveq r1, #0 @ (equal) r1<- 0, trumps less than
+.LOP_CMPG_DOUBLE_finish:
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r3) @ vAA<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CMP_LONG: /* 0x31 */
+/* File: armv5te/OP_CMP_LONG.S */
+ /*
+ * Compare two 64-bit values. Puts 0, 1, or -1 into the destination
+ * register based on the results of the comparison.
+ *
+ * We load the full values with LDM, but in practice many values could
+ * be resolved by only looking at the high word. This could be made
+ * faster or slower by splitting the LDM into a pair of LDRs.
+ *
+ * If we just wanted to set condition flags, we could do this:
+ * subs ip, r0, r2
+ * sbcs ip, r1, r3
+ * subeqs ip, r0, r2
+ * Leaving { <0, 0, >0 } in ip. However, we have to set it to a specific
+ * integer value, which we can do with 2 conditional mov/mvn instructions
+ * (set 1, set -1; if they're equal we already have 0 in ip), giving
+ * us a constant 5-cycle path plus a branch at the end to the
+ * instruction epilogue code. The multi-compare approach below needs
+ * 2 or 3 cycles + branch if the high word doesn't match, 6 + branch
+ * in the worst case (the 64-bit values are equal).
+ */
+ /* cmp-long vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ cmp r1, r3 @ compare (vBB+1, vCC+1)
+ blt .LOP_CMP_LONG_less @ signed compare on high part
+ bgt .LOP_CMP_LONG_greater
+ subs r1, r0, r2 @ r1<- r0 - r2
+ bhi .LOP_CMP_LONG_greater @ unsigned compare on low part
+ bne .LOP_CMP_LONG_less
+ b .LOP_CMP_LONG_finish @ equal; r1 already holds 0
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_EQ: /* 0x32 */
+/* File: armv5te/OP_IF_EQ.S */
+/* File: armv5te/bincmp.S */
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ mov r0, rINST, lsr #8 @ r0<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r0, r0, #15
+ GET_VREG(r3, r1) @ r3<- vB
+ GET_VREG(r2, r0) @ r2<- vA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, r3 @ compare (vA, vB)
+ bne 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ b common_testUpdateProfile
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_NE: /* 0x33 */
+/* File: armv5te/OP_IF_NE.S */
+/* File: armv5te/bincmp.S */
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ mov r0, rINST, lsr #8 @ r0<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r0, r0, #15
+ GET_VREG(r3, r1) @ r3<- vB
+ GET_VREG(r2, r0) @ r2<- vA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, r3 @ compare (vA, vB)
+ beq 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ b common_testUpdateProfile
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_LT: /* 0x34 */
+/* File: armv5te/OP_IF_LT.S */
+/* File: armv5te/bincmp.S */
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ mov r0, rINST, lsr #8 @ r0<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r0, r0, #15
+ GET_VREG(r3, r1) @ r3<- vB
+ GET_VREG(r2, r0) @ r2<- vA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, r3 @ compare (vA, vB)
+ bge 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ b common_testUpdateProfile
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_GE: /* 0x35 */
+/* File: armv5te/OP_IF_GE.S */
+/* File: armv5te/bincmp.S */
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ mov r0, rINST, lsr #8 @ r0<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r0, r0, #15
+ GET_VREG(r3, r1) @ r3<- vB
+ GET_VREG(r2, r0) @ r2<- vA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, r3 @ compare (vA, vB)
+ blt 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ b common_testUpdateProfile
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_GT: /* 0x36 */
+/* File: armv5te/OP_IF_GT.S */
+/* File: armv5te/bincmp.S */
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ mov r0, rINST, lsr #8 @ r0<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r0, r0, #15
+ GET_VREG(r3, r1) @ r3<- vB
+ GET_VREG(r2, r0) @ r2<- vA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, r3 @ compare (vA, vB)
+ ble 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ b common_testUpdateProfile
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_LE: /* 0x37 */
+/* File: armv5te/OP_IF_LE.S */
+/* File: armv5te/bincmp.S */
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ mov r0, rINST, lsr #8 @ r0<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r0, r0, #15
+ GET_VREG(r3, r1) @ r3<- vB
+ GET_VREG(r2, r0) @ r2<- vA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, r3 @ compare (vA, vB)
+ bgt 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ b common_testUpdateProfile
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_EQZ: /* 0x38 */
+/* File: armv5te/OP_IF_EQZ.S */
+/* File: armv5te/zcmp.S */
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ mov r0, rINST, lsr #8 @ r0<- AA
+ GET_VREG(r2, r0) @ r2<- vAA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, #0 @ compare (vA, 0)
+ bne 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_NEZ: /* 0x39 */
+/* File: armv5te/OP_IF_NEZ.S */
+/* File: armv5te/zcmp.S */
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ mov r0, rINST, lsr #8 @ r0<- AA
+ GET_VREG(r2, r0) @ r2<- vAA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, #0 @ compare (vA, 0)
+ beq 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_LTZ: /* 0x3a */
+/* File: armv5te/OP_IF_LTZ.S */
+/* File: armv5te/zcmp.S */
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ mov r0, rINST, lsr #8 @ r0<- AA
+ GET_VREG(r2, r0) @ r2<- vAA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, #0 @ compare (vA, 0)
+ bge 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_GEZ: /* 0x3b */
+/* File: armv5te/OP_IF_GEZ.S */
+/* File: armv5te/zcmp.S */
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ mov r0, rINST, lsr #8 @ r0<- AA
+ GET_VREG(r2, r0) @ r2<- vAA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, #0 @ compare (vA, 0)
+ blt 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_GTZ: /* 0x3c */
+/* File: armv5te/OP_IF_GTZ.S */
+/* File: armv5te/zcmp.S */
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ mov r0, rINST, lsr #8 @ r0<- AA
+ GET_VREG(r2, r0) @ r2<- vAA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, #0 @ compare (vA, 0)
+ ble 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_LEZ: /* 0x3d */
+/* File: armv5te/OP_IF_LEZ.S */
+/* File: armv5te/zcmp.S */
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ mov r0, rINST, lsr #8 @ r0<- AA
+ GET_VREG(r2, r0) @ r2<- vAA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, #0 @ compare (vA, 0)
+ bgt 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_3E: /* 0x3e */
+/* File: armv5te/OP_UNUSED_3E.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_3F: /* 0x3f */
+/* File: armv5te/OP_UNUSED_3F.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_40: /* 0x40 */
+/* File: armv5te/OP_UNUSED_40.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_41: /* 0x41 */
+/* File: armv5te/OP_UNUSED_41.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_42: /* 0x42 */
+/* File: armv5te/OP_UNUSED_42.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_43: /* 0x43 */
+/* File: armv5te/OP_UNUSED_43.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET: /* 0x44 */
+/* File: armv5te/OP_AGET.S */
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #2 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldr r2, [r0, #offArrayObject_contents] @ r2<- vBB[vCC]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r9) @ vAA<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_WIDE: /* 0x45 */
+/* File: armv5te/OP_AGET_WIDE.S */
+ /*
+ * Array get, 64 bits. vAA <- vBB[vCC].
+ *
+ * Arrays of long/double are 64-bit aligned, so it's okay to use LDRD.
+ */
+ /* aget-wide vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #3 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcc .LOP_AGET_WIDE_finish @ okay, continue below
+ b common_errArrayIndex @ index >= length, bail
+ @ May want to swap the order of these two branches depending on how the
+ @ branch prediction (if any) handles conditional forward branches vs.
+ @ unconditional forward branches.
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_OBJECT: /* 0x46 */
+/* File: armv5te/OP_AGET_OBJECT.S */
+/* File: armv5te/OP_AGET.S */
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #2 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldr r2, [r0, #offArrayObject_contents] @ r2<- vBB[vCC]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r9) @ vAA<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_BOOLEAN: /* 0x47 */
+/* File: armv5te/OP_AGET_BOOLEAN.S */
+/* File: armv5te/OP_AGET.S */
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #0 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldrb r2, [r0, #offArrayObject_contents] @ r2<- vBB[vCC]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r9) @ vAA<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_BYTE: /* 0x48 */
+/* File: armv5te/OP_AGET_BYTE.S */
+/* File: armv5te/OP_AGET.S */
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #0 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldrsb r2, [r0, #offArrayObject_contents] @ r2<- vBB[vCC]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r9) @ vAA<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_CHAR: /* 0x49 */
+/* File: armv5te/OP_AGET_CHAR.S */
+/* File: armv5te/OP_AGET.S */
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #1 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldrh r2, [r0, #offArrayObject_contents] @ r2<- vBB[vCC]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r9) @ vAA<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_SHORT: /* 0x4a */
+/* File: armv5te/OP_AGET_SHORT.S */
+/* File: armv5te/OP_AGET.S */
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #1 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldrsh r2, [r0, #offArrayObject_contents] @ r2<- vBB[vCC]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r9) @ vAA<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT: /* 0x4b */
+/* File: armv5te/OP_APUT.S */
+ /*
+ * Array put, 32 bits or less. vBB[vCC] <- vAA.
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #2 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r2, r9) @ r2<- vAA
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ str r2, [r0, #offArrayObject_contents] @ vBB[vCC]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_WIDE: /* 0x4c */
+/* File: armv5te/OP_APUT_WIDE.S */
+ /*
+ * Array put, 64 bits. vBB[vCC] <- vAA.
+ *
+ * Arrays of long/double are 64-bit aligned, so it's okay to use STRD.
+ */
+ /* aput-wide vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #3 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ bcc .LOP_APUT_WIDE_finish @ okay, continue below
+ b common_errArrayIndex @ index >= length, bail
+ @ May want to swap the order of these two branches depending on how the
+ @ branch prediction (if any) handles conditional forward branches vs.
+ @ unconditional forward branches.
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_OBJECT: /* 0x4d */
+/* File: armv5te/OP_APUT_OBJECT.S */
+ /*
+ * Store an object into an array. vBB[vCC] <- vAA.
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ */
+ /* op vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ GET_VREG(rINST, r2) @ rINST<- vBB (array object)
+ GET_VREG(r0, r3) @ r0<- vCC (requested index)
+ cmp rINST, #0 @ null array object?
+ GET_VREG(r9, r9) @ r9<- vAA
+ beq common_errNullObject @ yes, bail
+ ldr r3, [rINST, #offArrayObject_length] @ r3<- arrayObj->length
+ add r10, rINST, r0, lsl #2 @ r10<- arrayObj + index*width
+ cmp r0, r3 @ compare unsigned index, length
+ bcc .LOP_APUT_OBJECT_finish @ we're okay, continue on
+ b common_errArrayIndex @ index >= length, bail
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_BOOLEAN: /* 0x4e */
+/* File: armv5te/OP_APUT_BOOLEAN.S */
+/* File: armv5te/OP_APUT.S */
+ /*
+ * Array put, 32 bits or less. vBB[vCC] <- vAA.
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #0 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r2, r9) @ r2<- vAA
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ strb r2, [r0, #offArrayObject_contents] @ vBB[vCC]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_BYTE: /* 0x4f */
+/* File: armv5te/OP_APUT_BYTE.S */
+/* File: armv5te/OP_APUT.S */
+ /*
+ * Array put, 32 bits or less. vBB[vCC] <- vAA.
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #0 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r2, r9) @ r2<- vAA
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ strb r2, [r0, #offArrayObject_contents] @ vBB[vCC]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_CHAR: /* 0x50 */
+/* File: armv5te/OP_APUT_CHAR.S */
+/* File: armv5te/OP_APUT.S */
+ /*
+ * Array put, 32 bits or less. vBB[vCC] <- vAA.
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #1 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r2, r9) @ r2<- vAA
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ strh r2, [r0, #offArrayObject_contents] @ vBB[vCC]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_SHORT: /* 0x51 */
+/* File: armv5te/OP_APUT_SHORT.S */
+/* File: armv5te/OP_APUT.S */
+ /*
+ * Array put, 32 bits or less. vBB[vCC] <- vAA.
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #1 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r2, r9) @ r2<- vAA
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ strh r2, [r0, #offArrayObject_contents] @ vBB[vCC]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET: /* 0x52 */
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_finish
+ b common_exceptionThrown
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_WIDE: /* 0x53 */
+/* File: armv5te/OP_IGET_WIDE.S */
+ /*
+ * Wide 32-bit instance field get.
+ */
+ /* iget-wide vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_WIDE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_WIDE_finish
+ b common_exceptionThrown
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_OBJECT: /* 0x54 */
+/* File: armv5te/OP_IGET_OBJECT.S */
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_OBJECT_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_OBJECT_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_BOOLEAN: /* 0x55 */
+/* File: armv5te/OP_IGET_BOOLEAN.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrb", "sqnum":"1" }
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_BOOLEAN_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_BOOLEAN_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_BYTE: /* 0x56 */
+/* File: armv5te/OP_IGET_BYTE.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrsb", "sqnum":"2" }
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_BYTE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_BYTE_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_CHAR: /* 0x57 */
+/* File: armv5te/OP_IGET_CHAR.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrh", "sqnum":"3" }
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_CHAR_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_CHAR_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_SHORT: /* 0x58 */
+/* File: armv5te/OP_IGET_SHORT.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrsh", "sqnum":"4" }
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_SHORT_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_SHORT_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT: /* 0x59 */
+/* File: armv5te/OP_IPUT.S */
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_finish @ yes, finish up
+ b common_exceptionThrown
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_WIDE: /* 0x5a */
+/* File: armv5te/OP_IPUT_WIDE.S */
+ /* iput-wide vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_WIDE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_WIDE_finish @ yes, finish up
+ b common_exceptionThrown
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_OBJECT: /* 0x5b */
+/* File: armv5te/OP_IPUT_OBJECT.S */
+ /*
+ * 32-bit instance field put.
+ *
+ * for: iput-object, iput-object-volatile
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_OBJECT_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_OBJECT_finish @ yes, finish up
+ b common_exceptionThrown
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_BOOLEAN: /* 0x5c */
+/* File: armv5te/OP_IPUT_BOOLEAN.S */
+@include "armv5te/OP_IPUT.S" { "store":"strb", "sqnum":"1" }
+/* File: armv5te/OP_IPUT.S */
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_BOOLEAN_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_BOOLEAN_finish @ yes, finish up
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_BYTE: /* 0x5d */
+/* File: armv5te/OP_IPUT_BYTE.S */
+@include "armv5te/OP_IPUT.S" { "store":"strb", "sqnum":"2" }
+/* File: armv5te/OP_IPUT.S */
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_BYTE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_BYTE_finish @ yes, finish up
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_CHAR: /* 0x5e */
+/* File: armv5te/OP_IPUT_CHAR.S */
+@include "armv5te/OP_IPUT.S" { "store":"strh", "sqnum":"3" }
+/* File: armv5te/OP_IPUT.S */
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_CHAR_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_CHAR_finish @ yes, finish up
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_SHORT: /* 0x5f */
+/* File: armv5te/OP_IPUT_SHORT.S */
+@include "armv5te/OP_IPUT.S" { "store":"strh", "sqnum":"4" }
+/* File: armv5te/OP_IPUT.S */
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_SHORT_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_SHORT_finish @ yes, finish up
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET: /* 0x60 */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_resolve @ yes, do resolve
+.LOP_SGET_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_WIDE: /* 0x61 */
+/* File: armv5te/OP_SGET_WIDE.S */
+ /*
+ * 64-bit SGET handler.
+ */
+ /* sget-wide vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_WIDE_resolve @ yes, do resolve
+.LOP_SGET_WIDE_finish:
+ mov r9, rINST, lsr #8 @ r9<- AA
+ .if 0
+ add r0, r0, #offStaticField_value @ r0<- pointer to data
+ bl dvmQuasiAtomicRead64 @ r0/r1<- contents of field
+ .else
+ ldrd r0, [r0, #offStaticField_value] @ r0/r1<- field value (aligned)
+ .endif
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_OBJECT: /* 0x62 */
+/* File: armv5te/OP_SGET_OBJECT.S */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_OBJECT_resolve @ yes, do resolve
+.LOP_SGET_OBJECT_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_BOOLEAN: /* 0x63 */
+/* File: armv5te/OP_SGET_BOOLEAN.S */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_BOOLEAN_resolve @ yes, do resolve
+.LOP_SGET_BOOLEAN_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_BYTE: /* 0x64 */
+/* File: armv5te/OP_SGET_BYTE.S */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_BYTE_resolve @ yes, do resolve
+.LOP_SGET_BYTE_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_CHAR: /* 0x65 */
+/* File: armv5te/OP_SGET_CHAR.S */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_CHAR_resolve @ yes, do resolve
+.LOP_SGET_CHAR_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_SHORT: /* 0x66 */
+/* File: armv5te/OP_SGET_SHORT.S */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_SHORT_resolve @ yes, do resolve
+.LOP_SGET_SHORT_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT: /* 0x67 */
+/* File: armv5te/OP_SPUT.S */
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SPUT_resolve @ yes, do resolve
+.LOP_SPUT_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_WIDE: /* 0x68 */
+/* File: armv5te/OP_SPUT_WIDE.S */
+ /*
+ * 64-bit SPUT handler.
+ */
+ /* sput-wide vAA, field@BBBB */
+ ldr r0, [rGLUE, #offGlue_methodClassDex] @ r0<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r0, [r0, #offDvmDex_pResFields] @ r0<- dvmDex->pResFields
+ mov r9, rINST, lsr #8 @ r9<- AA
+ ldr r2, [r0, r1, lsl #2] @ r2<- resolved StaticField ptr
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ cmp r2, #0 @ is resolved entry null?
+ beq .LOP_SPUT_WIDE_resolve @ yes, do resolve
+.LOP_SPUT_WIDE_finish: @ field ptr in r2, AA in r9
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ GET_INST_OPCODE(r10) @ extract opcode from rINST
+ .if 0
+ add r2, r2, #offStaticField_value @ r2<- pointer to data
+ bl dvmQuasiAtomicSwap64 @ stores r0/r1 into addr r2
+ .else
+ strd r0, [r2, #offStaticField_value] @ field<- vAA/vAA+1
+ .endif
+ GOTO_OPCODE(r10) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_OBJECT: /* 0x69 */
+/* File: armv5te/OP_SPUT_OBJECT.S */
+ /*
+ * 32-bit SPUT handler for objects
+ *
+ * for: sput-object, sput-object-volatile
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_SPUT_OBJECT_finish @ no, continue
+ ldr r9, [rGLUE, #offGlue_method] @ r9<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r9, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_OBJECT_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_BOOLEAN: /* 0x6a */
+/* File: armv5te/OP_SPUT_BOOLEAN.S */
+/* File: armv5te/OP_SPUT.S */
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SPUT_BOOLEAN_resolve @ yes, do resolve
+.LOP_SPUT_BOOLEAN_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_BYTE: /* 0x6b */
+/* File: armv5te/OP_SPUT_BYTE.S */
+/* File: armv5te/OP_SPUT.S */
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SPUT_BYTE_resolve @ yes, do resolve
+.LOP_SPUT_BYTE_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_CHAR: /* 0x6c */
+/* File: armv5te/OP_SPUT_CHAR.S */
+/* File: armv5te/OP_SPUT.S */
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SPUT_CHAR_resolve @ yes, do resolve
+.LOP_SPUT_CHAR_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_SHORT: /* 0x6d */
+/* File: armv5te/OP_SPUT_SHORT.S */
+/* File: armv5te/OP_SPUT.S */
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SPUT_SHORT_resolve @ yes, do resolve
+.LOP_SPUT_SHORT_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_VIRTUAL: /* 0x6e */
+/* File: armv5te/OP_INVOKE_VIRTUAL.S */
+ /*
+ * Handle a virtual method call.
+ *
+ * for: invoke-virtual, invoke-virtual/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved baseMethod
+ .if (!0)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ must export for invoke
+ bne .LOP_INVOKE_VIRTUAL_continue @ yes, continue on
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ mov r2, #METHOD_VIRTUAL @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ bne .LOP_INVOKE_VIRTUAL_continue @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_SUPER: /* 0x6f */
+/* File: armv5te/OP_INVOKE_SUPER.S */
+ /*
+ * Handle a "super" method call.
+ *
+ * for: invoke-super, invoke-super/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ .if (!0)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ GET_VREG(r2, r10) @ r2<- "this" ptr
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved baseMethod
+ cmp r2, #0 @ null "this"?
+ ldr r9, [rGLUE, #offGlue_method] @ r9<- current method
+ beq common_errNullObject @ null "this", throw exception
+ cmp r0, #0 @ already resolved?
+ ldr r9, [r9, #offMethod_clazz] @ r9<- method->clazz
+ EXPORT_PC() @ must export for invoke
+ bne .LOP_INVOKE_SUPER_continue @ resolved, continue on
+ b .LOP_INVOKE_SUPER_resolve @ do resolve now
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_DIRECT: /* 0x70 */
+/* File: armv5te/OP_INVOKE_DIRECT.S */
+ /*
+ * Handle a direct method call.
+ *
+ * (We could defer the "is 'this' pointer null" test to the common
+ * method invocation code, and use a flag to indicate that static
+ * calls don't count. If we do this as part of copying the arguments
+ * out we could avoiding loading the first arg twice.)
+ *
+ * for: invoke-direct, invoke-direct/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved methodToCall
+ .if (!0)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ must export for invoke
+ GET_VREG(r2, r10) @ r2<- "this" ptr
+ beq .LOP_INVOKE_DIRECT_resolve @ not resolved, do it now
+.LOP_INVOKE_DIRECT_finish:
+ cmp r2, #0 @ null "this" ref?
+ bne common_invokeMethodNoRange @ no, continue on
+ b common_errNullObject @ yes, throw exception
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_STATIC: /* 0x71 */
+/* File: armv5te/OP_INVOKE_STATIC.S */
+ /*
+ * Handle a static method call.
+ *
+ * for: invoke-static, invoke-static/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved methodToCall
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ must export for invoke
+ bne common_invokeMethodNoRange @ yes, continue on
+0: ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ mov r2, #METHOD_STATIC @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ bne common_invokeMethodNoRange @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_INTERFACE: /* 0x72 */
+/* File: armv5te/OP_INVOKE_INTERFACE.S */
+ /*
+ * Handle an interface method call.
+ *
+ * for: invoke-interface, invoke-interface/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r2, 2) @ r2<- FEDC or CCCC
+ FETCH(r1, 1) @ r1<- BBBB
+ .if (!0)
+ and r2, r2, #15 @ r2<- C (or stays CCCC)
+ .endif
+ EXPORT_PC() @ must export for invoke
+ GET_VREG(r0, r2) @ r0<- first arg ("this")
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- methodClassDex
+ cmp r0, #0 @ null obj?
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- method
+ beq common_errNullObject @ yes, fail
+ ldr r0, [r0, #offObject_clazz] @ r0<- thisPtr->clazz
+ bl dvmFindInterfaceMethodInCache @ r0<- call(class, ref, method, dex)
+ cmp r0, #0 @ failed?
+ beq common_exceptionThrown @ yes, handle exception
+ b common_invokeMethodNoRange @ jump to common handler
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_73: /* 0x73 */
+/* File: armv5te/OP_UNUSED_73.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_VIRTUAL_RANGE: /* 0x74 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_RANGE.S */
+/* File: armv5te/OP_INVOKE_VIRTUAL.S */
+ /*
+ * Handle a virtual method call.
+ *
+ * for: invoke-virtual, invoke-virtual/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved baseMethod
+ .if (!1)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ must export for invoke
+ bne .LOP_INVOKE_VIRTUAL_RANGE_continue @ yes, continue on
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ mov r2, #METHOD_VIRTUAL @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ bne .LOP_INVOKE_VIRTUAL_RANGE_continue @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_SUPER_RANGE: /* 0x75 */
+/* File: armv5te/OP_INVOKE_SUPER_RANGE.S */
+/* File: armv5te/OP_INVOKE_SUPER.S */
+ /*
+ * Handle a "super" method call.
+ *
+ * for: invoke-super, invoke-super/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ .if (!1)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ GET_VREG(r2, r10) @ r2<- "this" ptr
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved baseMethod
+ cmp r2, #0 @ null "this"?
+ ldr r9, [rGLUE, #offGlue_method] @ r9<- current method
+ beq common_errNullObject @ null "this", throw exception
+ cmp r0, #0 @ already resolved?
+ ldr r9, [r9, #offMethod_clazz] @ r9<- method->clazz
+ EXPORT_PC() @ must export for invoke
+ bne .LOP_INVOKE_SUPER_RANGE_continue @ resolved, continue on
+ b .LOP_INVOKE_SUPER_RANGE_resolve @ do resolve now
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_DIRECT_RANGE: /* 0x76 */
+/* File: armv5te/OP_INVOKE_DIRECT_RANGE.S */
+/* File: armv5te/OP_INVOKE_DIRECT.S */
+ /*
+ * Handle a direct method call.
+ *
+ * (We could defer the "is 'this' pointer null" test to the common
+ * method invocation code, and use a flag to indicate that static
+ * calls don't count. If we do this as part of copying the arguments
+ * out we could avoiding loading the first arg twice.)
+ *
+ * for: invoke-direct, invoke-direct/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved methodToCall
+ .if (!1)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ must export for invoke
+ GET_VREG(r2, r10) @ r2<- "this" ptr
+ beq .LOP_INVOKE_DIRECT_RANGE_resolve @ not resolved, do it now
+.LOP_INVOKE_DIRECT_RANGE_finish:
+ cmp r2, #0 @ null "this" ref?
+ bne common_invokeMethodRange @ no, continue on
+ b common_errNullObject @ yes, throw exception
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_STATIC_RANGE: /* 0x77 */
+/* File: armv5te/OP_INVOKE_STATIC_RANGE.S */
+/* File: armv5te/OP_INVOKE_STATIC.S */
+ /*
+ * Handle a static method call.
+ *
+ * for: invoke-static, invoke-static/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved methodToCall
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ must export for invoke
+ bne common_invokeMethodRange @ yes, continue on
+0: ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ mov r2, #METHOD_STATIC @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ bne common_invokeMethodRange @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_INTERFACE_RANGE: /* 0x78 */
+/* File: armv5te/OP_INVOKE_INTERFACE_RANGE.S */
+/* File: armv5te/OP_INVOKE_INTERFACE.S */
+ /*
+ * Handle an interface method call.
+ *
+ * for: invoke-interface, invoke-interface/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r2, 2) @ r2<- FEDC or CCCC
+ FETCH(r1, 1) @ r1<- BBBB
+ .if (!1)
+ and r2, r2, #15 @ r2<- C (or stays CCCC)
+ .endif
+ EXPORT_PC() @ must export for invoke
+ GET_VREG(r0, r2) @ r0<- first arg ("this")
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- methodClassDex
+ cmp r0, #0 @ null obj?
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- method
+ beq common_errNullObject @ yes, fail
+ ldr r0, [r0, #offObject_clazz] @ r0<- thisPtr->clazz
+ bl dvmFindInterfaceMethodInCache @ r0<- call(class, ref, method, dex)
+ cmp r0, #0 @ failed?
+ beq common_exceptionThrown @ yes, handle exception
+ b common_invokeMethodRange @ jump to common handler
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_79: /* 0x79 */
+/* File: armv5te/OP_UNUSED_79.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_7A: /* 0x7a */
+/* File: armv5te/OP_UNUSED_7A.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEG_INT: /* 0x7b */
+/* File: armv5te/OP_NEG_INT.S */
+/* File: armv5te/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0".
+ * This could be an ARM instruction or a function call.
+ *
+ * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+ * int-to-byte, int-to-char, int-to-short
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r3) @ r0<- vB
+ and r9, r9, #15
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ rsb r0, r0, #0 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 9-10 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NOT_INT: /* 0x7c */
+/* File: armv5te/OP_NOT_INT.S */
+/* File: armv5te/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0".
+ * This could be an ARM instruction or a function call.
+ *
+ * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+ * int-to-byte, int-to-char, int-to-short
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r3) @ r0<- vB
+ and r9, r9, #15
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ mvn r0, r0 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 9-10 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEG_LONG: /* 0x7d */
+/* File: armv5te/OP_NEG_LONG.S */
+/* File: armv5te/unopWide.S */
+ /*
+ * Generic 64-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0/r1".
+ * This could be an ARM instruction or a function call.
+ *
+ * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+ */
+ /* unop vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r3, {r0-r1} @ r0/r1<- vAA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ rsbs r0, r0, #0 @ optional op; may set condition codes
+ rsc r1, r1, #0 @ r0/r1<- op, r2-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NOT_LONG: /* 0x7e */
+/* File: armv5te/OP_NOT_LONG.S */
+/* File: armv5te/unopWide.S */
+ /*
+ * Generic 64-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0/r1".
+ * This could be an ARM instruction or a function call.
+ *
+ * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+ */
+ /* unop vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r3, {r0-r1} @ r0/r1<- vAA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ mvn r0, r0 @ optional op; may set condition codes
+ mvn r1, r1 @ r0/r1<- op, r2-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEG_FLOAT: /* 0x7f */
+/* File: armv5te/OP_NEG_FLOAT.S */
+/* File: armv5te/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0".
+ * This could be an ARM instruction or a function call.
+ *
+ * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+ * int-to-byte, int-to-char, int-to-short
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r3) @ r0<- vB
+ and r9, r9, #15
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ add r0, r0, #0x80000000 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 9-10 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEG_DOUBLE: /* 0x80 */
+/* File: armv5te/OP_NEG_DOUBLE.S */
+/* File: armv5te/unopWide.S */
+ /*
+ * Generic 64-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0/r1".
+ * This could be an ARM instruction or a function call.
+ *
+ * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+ */
+ /* unop vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r3, {r0-r1} @ r0/r1<- vAA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ add r1, r1, #0x80000000 @ r0/r1<- op, r2-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_LONG: /* 0x81 */
+/* File: armv5te/OP_INT_TO_LONG.S */
+/* File: armv5te/unopWider.S */
+ /*
+ * Generic 32bit-to-64bit unary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = op r0", where
+ * "result" is a 64-bit quantity in r0/r1.
+ *
+ * For: int-to-long, int-to-double, float-to-long, float-to-double
+ */
+ /* unop vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r0, r3) @ r0<- vB
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ mov r1, r0, asr #31 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vA/vA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-11 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_FLOAT: /* 0x82 */
+/* File: armv5te/OP_INT_TO_FLOAT.S */
+/* File: armv5te/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0".
+ * This could be an ARM instruction or a function call.
+ *
+ * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+ * int-to-byte, int-to-char, int-to-short
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r3) @ r0<- vB
+ and r9, r9, #15
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ bl __aeabi_i2f @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 9-10 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_DOUBLE: /* 0x83 */
+/* File: armv5te/OP_INT_TO_DOUBLE.S */
+/* File: armv5te/unopWider.S */
+ /*
+ * Generic 32bit-to-64bit unary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = op r0", where
+ * "result" is a 64-bit quantity in r0/r1.
+ *
+ * For: int-to-long, int-to-double, float-to-long, float-to-double
+ */
+ /* unop vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r0, r3) @ r0<- vB
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ bl __aeabi_i2d @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vA/vA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-11 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_LONG_TO_INT: /* 0x84 */
+/* File: armv5te/OP_LONG_TO_INT.S */
+/* we ignore the high word, making this equivalent to a 32-bit reg move */
+/* File: armv5te/OP_MOVE.S */
+ /* for move, move-object, long-to-int */
+ /* op vA, vB */
+ mov r1, rINST, lsr #12 @ r1<- B from 15:12
+ mov r0, rINST, lsr #8 @ r0<- A from 11:8
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[B]
+ and r0, r0, #15
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ SET_VREG(r2, r0) @ fp[A]<- r2
+ GOTO_OPCODE(ip) @ execute next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_LONG_TO_FLOAT: /* 0x85 */
+/* File: armv5te/OP_LONG_TO_FLOAT.S */
+/* File: armv5te/unopNarrower.S */
+ /*
+ * Generic 64bit-to-32bit unary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = op r0/r1", where
+ * "result" is a 32-bit quantity in r0.
+ *
+ * For: long-to-float, double-to-int, double-to-float
+ *
+ * (This would work for long-to-int, but that instruction is actually
+ * an exact match for OP_MOVE.)
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ and r9, r9, #15
+ ldmia r3, {r0-r1} @ r0/r1<- vB/vB+1
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl __aeabi_l2f @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-11 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_LONG_TO_DOUBLE: /* 0x86 */
+/* File: armv5te/OP_LONG_TO_DOUBLE.S */
+/* File: armv5te/unopWide.S */
+ /*
+ * Generic 64-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0/r1".
+ * This could be an ARM instruction or a function call.
+ *
+ * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+ */
+ /* unop vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r3, {r0-r1} @ r0/r1<- vAA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl __aeabi_l2d @ r0/r1<- op, r2-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FLOAT_TO_INT: /* 0x87 */
+/* File: armv5te/OP_FLOAT_TO_INT.S */
+/* EABI appears to have Java-style conversions of +inf/-inf/NaN */
+/* File: armv5te/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0".
+ * This could be an ARM instruction or a function call.
+ *
+ * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+ * int-to-byte, int-to-char, int-to-short
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r3) @ r0<- vB
+ and r9, r9, #15
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ bl __aeabi_f2iz @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 9-10 instructions */
+
+
+#if 0
+@include "armv5te/unop.S" {"instr":"bl f2i_doconv"}
+@break
+/*
+ * Convert the float in r0 to an int in r0.
+ *
+ * We have to clip values to int min/max per the specification. The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer. The EABI convert function isn't doing this for us.
+ */
+f2i_doconv:
+ stmfd sp!, {r4, lr}
+ mov r1, #0x4f000000 @ (float)maxint
+ mov r4, r0
+ bl __aeabi_fcmpge @ is arg >= maxint?
+ cmp r0, #0 @ nonzero == yes
+ mvnne r0, #0x80000000 @ return maxint (7fffffff)
+ ldmnefd sp!, {r4, pc}
+
+ mov r0, r4 @ recover arg
+ mov r1, #0xcf000000 @ (float)minint
+ bl __aeabi_fcmple @ is arg <= minint?
+ cmp r0, #0 @ nonzero == yes
+ movne r0, #0x80000000 @ return minint (80000000)
+ ldmnefd sp!, {r4, pc}
+
+ mov r0, r4 @ recover arg
+ mov r1, r4
+ bl __aeabi_fcmpeq @ is arg == self?
+ cmp r0, #0 @ zero == no
+ ldmeqfd sp!, {r4, pc} @ return zero for NaN
+
+ mov r0, r4 @ recover arg
+ bl __aeabi_f2iz @ convert float to int
+ ldmfd sp!, {r4, pc}
+#endif
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FLOAT_TO_LONG: /* 0x88 */
+/* File: armv5te/OP_FLOAT_TO_LONG.S */
+@include "armv5te/unopWider.S" {"instr":"bl __aeabi_f2lz"}
+/* File: armv5te/unopWider.S */
+ /*
+ * Generic 32bit-to-64bit unary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = op r0", where
+ * "result" is a 64-bit quantity in r0/r1.
+ *
+ * For: int-to-long, int-to-double, float-to-long, float-to-double
+ */
+ /* unop vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r0, r3) @ r0<- vB
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ bl f2l_doconv @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vA/vA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-11 instructions */
+
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FLOAT_TO_DOUBLE: /* 0x89 */
+/* File: armv5te/OP_FLOAT_TO_DOUBLE.S */
+/* File: armv5te/unopWider.S */
+ /*
+ * Generic 32bit-to-64bit unary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = op r0", where
+ * "result" is a 64-bit quantity in r0/r1.
+ *
+ * For: int-to-long, int-to-double, float-to-long, float-to-double
+ */
+ /* unop vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r0, r3) @ r0<- vB
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ bl __aeabi_f2d @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vA/vA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-11 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DOUBLE_TO_INT: /* 0x8a */
+/* File: armv5te/OP_DOUBLE_TO_INT.S */
+/* EABI appears to have Java-style conversions of +inf/-inf/NaN */
+/* File: armv5te/unopNarrower.S */
+ /*
+ * Generic 64bit-to-32bit unary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = op r0/r1", where
+ * "result" is a 32-bit quantity in r0.
+ *
+ * For: long-to-float, double-to-int, double-to-float
+ *
+ * (This would work for long-to-int, but that instruction is actually
+ * an exact match for OP_MOVE.)
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ and r9, r9, #15
+ ldmia r3, {r0-r1} @ r0/r1<- vB/vB+1
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl __aeabi_d2iz @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-11 instructions */
+
+
+#if 0
+@include "armv5te/unopNarrower.S" {"instr":"bl d2i_doconv"}
+@break
+/*
+ * Convert the double in r0/r1 to an int in r0.
+ *
+ * We have to clip values to int min/max per the specification. The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer. The EABI convert function isn't doing this for us.
+ */
+d2i_doconv:
+ stmfd sp!, {r4, r5, lr} @ save regs
+ mov r2, #0x80000000 @ maxint, as a double (low word)
+ mov r2, r2, asr #9 @ 0xffc00000
+ sub sp, sp, #4 @ align for EABI
+ mvn r3, #0xbe000000 @ maxint, as a double (high word)
+ sub r3, r3, #0x00200000 @ 0x41dfffff
+ mov r4, r0 @ save a copy of r0
+ mov r5, r1 @ and r1
+ bl __aeabi_dcmpge @ is arg >= maxint?
+ cmp r0, #0 @ nonzero == yes
+ mvnne r0, #0x80000000 @ return maxint (0x7fffffff)
+ bne 1f
+
+ mov r0, r4 @ recover arg
+ mov r1, r5
+ mov r3, #0xc1000000 @ minint, as a double (high word)
+ add r3, r3, #0x00e00000 @ 0xc1e00000
+ mov r2, #0 @ minint, as a double (low word)
+ bl __aeabi_dcmple @ is arg <= minint?
+ cmp r0, #0 @ nonzero == yes
+ movne r0, #0x80000000 @ return minint (80000000)
+ bne 1f
+
+ mov r0, r4 @ recover arg
+ mov r1, r5
+ mov r2, r4 @ compare against self
+ mov r3, r5
+ bl __aeabi_dcmpeq @ is arg == self?
+ cmp r0, #0 @ zero == no
+ beq 1f @ return zero for NaN
+
+ mov r0, r4 @ recover arg
+ mov r1, r5
+ bl __aeabi_d2iz @ convert double to int
+
+1:
+ add sp, sp, #4
+ ldmfd sp!, {r4, r5, pc}
+#endif
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DOUBLE_TO_LONG: /* 0x8b */
+/* File: armv5te/OP_DOUBLE_TO_LONG.S */
+@include "armv5te/unopWide.S" {"instr":"bl __aeabi_d2lz"}
+/* File: armv5te/unopWide.S */
+ /*
+ * Generic 64-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0/r1".
+ * This could be an ARM instruction or a function call.
+ *
+ * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+ */
+ /* unop vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r3, {r0-r1} @ r0/r1<- vAA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl d2l_doconv @ r0/r1<- op, r2-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-13 instructions */
+
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DOUBLE_TO_FLOAT: /* 0x8c */
+/* File: armv5te/OP_DOUBLE_TO_FLOAT.S */
+/* File: armv5te/unopNarrower.S */
+ /*
+ * Generic 64bit-to-32bit unary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = op r0/r1", where
+ * "result" is a 32-bit quantity in r0.
+ *
+ * For: long-to-float, double-to-int, double-to-float
+ *
+ * (This would work for long-to-int, but that instruction is actually
+ * an exact match for OP_MOVE.)
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ and r9, r9, #15
+ ldmia r3, {r0-r1} @ r0/r1<- vB/vB+1
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl __aeabi_d2f @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-11 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_BYTE: /* 0x8d */
+/* File: armv5te/OP_INT_TO_BYTE.S */
+/* File: armv5te/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0".
+ * This could be an ARM instruction or a function call.
+ *
+ * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+ * int-to-byte, int-to-char, int-to-short
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r3) @ r0<- vB
+ and r9, r9, #15
+ mov r0, r0, asl #24 @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ mov r0, r0, asr #24 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 9-10 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_CHAR: /* 0x8e */
+/* File: armv5te/OP_INT_TO_CHAR.S */
+/* File: armv5te/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0".
+ * This could be an ARM instruction or a function call.
+ *
+ * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+ * int-to-byte, int-to-char, int-to-short
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r3) @ r0<- vB
+ and r9, r9, #15
+ mov r0, r0, asl #16 @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ mov r0, r0, lsr #16 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 9-10 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_SHORT: /* 0x8f */
+/* File: armv5te/OP_INT_TO_SHORT.S */
+/* File: armv5te/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0".
+ * This could be an ARM instruction or a function call.
+ *
+ * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+ * int-to-byte, int-to-char, int-to-short
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r3) @ r0<- vB
+ and r9, r9, #15
+ mov r0, r0, asl #16 @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ mov r0, r0, asr #16 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 9-10 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_INT: /* 0x90 */
+/* File: armv5te/OP_ADD_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ add r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_INT: /* 0x91 */
+/* File: armv5te/OP_SUB_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ sub r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_INT: /* 0x92 */
+/* File: armv5te/OP_MUL_INT.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ mul r0, r1, r0 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_INT: /* 0x93 */
+/* File: armv5te/OP_DIV_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 1
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl __aeabi_idiv @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_INT: /* 0x94 */
+/* File: armv5te/OP_REM_INT.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 1
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl __aeabi_idivmod @ r1<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r1, r9) @ vAA<- r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_INT: /* 0x95 */
+/* File: armv5te/OP_AND_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ and r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_INT: /* 0x96 */
+/* File: armv5te/OP_OR_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ orr r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_INT: /* 0x97 */
+/* File: armv5te/OP_XOR_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ eor r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHL_INT: /* 0x98 */
+/* File: armv5te/OP_SHL_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, asl r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHR_INT: /* 0x99 */
+/* File: armv5te/OP_SHR_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, asr r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_USHR_INT: /* 0x9a */
+/* File: armv5te/OP_USHR_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, lsr r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_LONG: /* 0x9b */
+/* File: armv5te/OP_ADD_LONG.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ adds r0, r0, r2 @ optional op; may set condition codes
+ adc r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_LONG: /* 0x9c */
+/* File: armv5te/OP_SUB_LONG.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ subs r0, r0, r2 @ optional op; may set condition codes
+ sbc r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_LONG: /* 0x9d */
+/* File: armv5te/OP_MUL_LONG.S */
+ /*
+ * Signed 64-bit integer multiply.
+ *
+ * Consider WXxYZ (r1r0 x r3r2) with a long multiply:
+ * WX
+ * x YZ
+ * --------
+ * ZW ZX
+ * YW YX
+ *
+ * The low word of the result holds ZX, the high word holds
+ * (ZW+YX) + (the high overflow from ZX). YW doesn't matter because
+ * it doesn't fit in the low 64 bits.
+ *
+ * Unlike most ARM math operations, multiply instructions have
+ * restrictions on using the same register more than once (Rd and Rm
+ * cannot be the same).
+ */
+ /* mul-long vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ mul ip, r2, r1 @ ip<- ZxW
+ umull r9, r10, r2, r0 @ r9/r10 <- ZxX
+ mla r2, r0, r3, ip @ r2<- YxX + (ZxW)
+ mov r0, rINST, lsr #8 @ r0<- AA
+ add r10, r2, r10 @ r10<- r10 + low(ZxW + (YxX))
+ add r0, rFP, r0, lsl #2 @ r0<- &fp[AA]
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ b .LOP_MUL_LONG_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_LONG: /* 0x9e */
+/* File: armv5te/OP_DIV_LONG.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 1
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_ldivmod @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_LONG: /* 0x9f */
+/* File: armv5te/OP_REM_LONG.S */
+/* ldivmod returns quotient in r0/r1 and remainder in r2/r3 */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 1
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_ldivmod @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r2,r3} @ vAA/vAA+1<- r2/r3
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_LONG: /* 0xa0 */
+/* File: armv5te/OP_AND_LONG.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ and r0, r0, r2 @ optional op; may set condition codes
+ and r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_LONG: /* 0xa1 */
+/* File: armv5te/OP_OR_LONG.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ orr r0, r0, r2 @ optional op; may set condition codes
+ orr r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_LONG: /* 0xa2 */
+/* File: armv5te/OP_XOR_LONG.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ eor r0, r0, r2 @ optional op; may set condition codes
+ eor r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHL_LONG: /* 0xa3 */
+/* File: armv5te/OP_SHL_LONG.S */
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to mask off the low
+ * 6 bits of the shift distance.
+ */
+ /* shl-long vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r3, r0, #255 @ r3<- BB
+ mov r0, r0, lsr #8 @ r0<- CC
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[BB]
+ GET_VREG(r2, r0) @ r2<- vCC
+ ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1
+ and r2, r2, #63 @ r2<- r2 & 0x3f
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+
+ mov r1, r1, asl r2 @ r1<- r1 << r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ b .LOP_SHL_LONG_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHR_LONG: /* 0xa4 */
+/* File: armv5te/OP_SHR_LONG.S */
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to mask off the low
+ * 6 bits of the shift distance.
+ */
+ /* shr-long vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r3, r0, #255 @ r3<- BB
+ mov r0, r0, lsr #8 @ r0<- CC
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[BB]
+ GET_VREG(r2, r0) @ r2<- vCC
+ ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1
+ and r2, r2, #63 @ r0<- r0 & 0x3f
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ b .LOP_SHR_LONG_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_USHR_LONG: /* 0xa5 */
+/* File: armv5te/OP_USHR_LONG.S */
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to mask off the low
+ * 6 bits of the shift distance.
+ */
+ /* ushr-long vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r3, r0, #255 @ r3<- BB
+ mov r0, r0, lsr #8 @ r0<- CC
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[BB]
+ GET_VREG(r2, r0) @ r2<- vCC
+ ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1
+ and r2, r2, #63 @ r0<- r0 & 0x3f
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ b .LOP_USHR_LONG_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_FLOAT: /* 0xa6 */
+/* File: armv5te/OP_ADD_FLOAT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl __aeabi_fadd @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_FLOAT: /* 0xa7 */
+/* File: armv5te/OP_SUB_FLOAT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl __aeabi_fsub @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_FLOAT: /* 0xa8 */
+/* File: armv5te/OP_MUL_FLOAT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl __aeabi_fmul @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_FLOAT: /* 0xa9 */
+/* File: armv5te/OP_DIV_FLOAT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl __aeabi_fdiv @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_FLOAT: /* 0xaa */
+/* File: armv5te/OP_REM_FLOAT.S */
+/* EABI doesn't define a float remainder function, but libm does */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl fmodf @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_DOUBLE: /* 0xab */
+/* File: armv5te/OP_ADD_DOUBLE.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_dadd @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_DOUBLE: /* 0xac */
+/* File: armv5te/OP_SUB_DOUBLE.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_dsub @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_DOUBLE: /* 0xad */
+/* File: armv5te/OP_MUL_DOUBLE.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_dmul @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_DOUBLE: /* 0xae */
+/* File: armv5te/OP_DIV_DOUBLE.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_ddiv @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_DOUBLE: /* 0xaf */
+/* File: armv5te/OP_REM_DOUBLE.S */
+/* EABI doesn't define a double remainder function, but libm does */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl fmod @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_INT_2ADDR: /* 0xb0 */
+/* File: armv5te/OP_ADD_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ add r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_INT_2ADDR: /* 0xb1 */
+/* File: armv5te/OP_SUB_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ sub r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_INT_2ADDR: /* 0xb2 */
+/* File: armv5te/OP_MUL_INT_2ADDR.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ mul r0, r1, r0 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_INT_2ADDR: /* 0xb3 */
+/* File: armv5te/OP_DIV_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 1
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_idiv @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_INT_2ADDR: /* 0xb4 */
+/* File: armv5te/OP_REM_INT_2ADDR.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 1
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_idivmod @ r1<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r1, r9) @ vAA<- r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_INT_2ADDR: /* 0xb5 */
+/* File: armv5te/OP_AND_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ and r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_INT_2ADDR: /* 0xb6 */
+/* File: armv5te/OP_OR_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ orr r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_INT_2ADDR: /* 0xb7 */
+/* File: armv5te/OP_XOR_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ eor r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHL_INT_2ADDR: /* 0xb8 */
+/* File: armv5te/OP_SHL_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, asl r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHR_INT_2ADDR: /* 0xb9 */
+/* File: armv5te/OP_SHR_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, asr r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_USHR_INT_2ADDR: /* 0xba */
+/* File: armv5te/OP_USHR_INT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, lsr r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_LONG_2ADDR: /* 0xbb */
+/* File: armv5te/OP_ADD_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r9, r9, #15
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ adds r0, r0, r2 @ optional op; may set condition codes
+ adc r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_LONG_2ADDR: /* 0xbc */
+/* File: armv5te/OP_SUB_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r9, r9, #15
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ subs r0, r0, r2 @ optional op; may set condition codes
+ sbc r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_LONG_2ADDR: /* 0xbd */
+/* File: armv5te/OP_MUL_LONG_2ADDR.S */
+ /*
+ * Signed 64-bit integer multiply, "/2addr" version.
+ *
+ * See OP_MUL_LONG for an explanation.
+ *
+ * We get a little tight on registers, so to avoid looking up &fp[A]
+ * again we stuff it into rINST.
+ */
+ /* mul-long/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r9, r9, #15
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add rINST, rFP, r9, lsl #2 @ rINST<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia rINST, {r0-r1} @ r0/r1<- vAA/vAA+1
+ mul ip, r2, r1 @ ip<- ZxW
+ umull r9, r10, r2, r0 @ r9/r10 <- ZxX
+ mla r2, r0, r3, ip @ r2<- YxX + (ZxW)
+ mov r0, rINST @ r0<- &fp[A] (free up rINST)
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ add r10, r2, r10 @ r10<- r10 + low(ZxW + (YxX))
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r0, {r9-r10} @ vAA/vAA+1<- r9/r10
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_LONG_2ADDR: /* 0xbe */
+/* File: armv5te/OP_DIV_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r9, r9, #15
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 1
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_ldivmod @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_LONG_2ADDR: /* 0xbf */
+/* File: armv5te/OP_REM_LONG_2ADDR.S */
+/* ldivmod returns quotient in r0/r1 and remainder in r2/r3 */
+/* File: armv5te/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r9, r9, #15
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 1
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_ldivmod @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r2,r3} @ vAA/vAA+1<- r2/r3
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_LONG_2ADDR: /* 0xc0 */
+/* File: armv5te/OP_AND_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r9, r9, #15
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ and r0, r0, r2 @ optional op; may set condition codes
+ and r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_LONG_2ADDR: /* 0xc1 */
+/* File: armv5te/OP_OR_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r9, r9, #15
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ orr r0, r0, r2 @ optional op; may set condition codes
+ orr r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_LONG_2ADDR: /* 0xc2 */
+/* File: armv5te/OP_XOR_LONG_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r9, r9, #15
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ eor r0, r0, r2 @ optional op; may set condition codes
+ eor r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHL_LONG_2ADDR: /* 0xc3 */
+/* File: armv5te/OP_SHL_LONG_2ADDR.S */
+ /*
+ * Long integer shift, 2addr version. vA is 64-bit value/result, vB is
+ * 32-bit shift distance.
+ */
+ /* shl-long/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r2, r3) @ r2<- vB
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ and r2, r2, #63 @ r2<- r2 & 0x3f
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+
+ mov r1, r1, asl r2 @ r1<- r1 << r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32)
+ mov r0, r0, asl r2 @ r0<- r0 << r2
+ b .LOP_SHL_LONG_2ADDR_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHR_LONG_2ADDR: /* 0xc4 */
+/* File: armv5te/OP_SHR_LONG_2ADDR.S */
+ /*
+ * Long integer shift, 2addr version. vA is 64-bit value/result, vB is
+ * 32-bit shift distance.
+ */
+ /* shr-long/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r2, r3) @ r2<- vB
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ and r2, r2, #63 @ r2<- r2 & 0x3f
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32)
+ mov r1, r1, asr r2 @ r1<- r1 >> r2
+ b .LOP_SHR_LONG_2ADDR_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_USHR_LONG_2ADDR: /* 0xc5 */
+/* File: armv5te/OP_USHR_LONG_2ADDR.S */
+ /*
+ * Long integer shift, 2addr version. vA is 64-bit value/result, vB is
+ * 32-bit shift distance.
+ */
+ /* ushr-long/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r2, r3) @ r2<- vB
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ and r2, r2, #63 @ r2<- r2 & 0x3f
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32)
+ mov r1, r1, lsr r2 @ r1<- r1 >>> r2
+ b .LOP_USHR_LONG_2ADDR_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_FLOAT_2ADDR: /* 0xc6 */
+/* File: armv5te/OP_ADD_FLOAT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_fadd @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_FLOAT_2ADDR: /* 0xc7 */
+/* File: armv5te/OP_SUB_FLOAT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_fsub @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_FLOAT_2ADDR: /* 0xc8 */
+/* File: armv5te/OP_MUL_FLOAT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_fmul @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_FLOAT_2ADDR: /* 0xc9 */
+/* File: armv5te/OP_DIV_FLOAT_2ADDR.S */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_fdiv @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_FLOAT_2ADDR: /* 0xca */
+/* File: armv5te/OP_REM_FLOAT_2ADDR.S */
+/* EABI doesn't define a float remainder function, but libm does */
+/* File: armv5te/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r3, rINST, lsr #12 @ r3<- B
+ and r9, r9, #15
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl fmodf @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_DOUBLE_2ADDR: /* 0xcb */
+/* File: armv5te/OP_ADD_DOUBLE_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r9, r9, #15
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_dadd @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_DOUBLE_2ADDR: /* 0xcc */
+/* File: armv5te/OP_SUB_DOUBLE_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r9, r9, #15
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_dsub @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_DOUBLE_2ADDR: /* 0xcd */
+/* File: armv5te/OP_MUL_DOUBLE_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r9, r9, #15
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_dmul @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_DOUBLE_2ADDR: /* 0xce */
+/* File: armv5te/OP_DIV_DOUBLE_2ADDR.S */
+/* File: armv5te/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r9, r9, #15
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_ddiv @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_DOUBLE_2ADDR: /* 0xcf */
+/* File: armv5te/OP_REM_DOUBLE_2ADDR.S */
+/* EABI doesn't define a double remainder function, but libm does */
+/* File: armv5te/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r9, rINST, lsr #8 @ r9<- A+
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r9, r9, #15
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl fmod @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_INT_LIT16: /* 0xd0 */
+/* File: armv5te/OP_ADD_INT_LIT16.S */
+/* File: armv5te/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r2) @ r0<- vB
+ and r9, r9, #15
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ add r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RSUB_INT: /* 0xd1 */
+/* File: armv5te/OP_RSUB_INT.S */
+/* this op is "rsub-int", but can be thought of as "rsub-int/lit16" */
+/* File: armv5te/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r2) @ r0<- vB
+ and r9, r9, #15
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ rsb r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_INT_LIT16: /* 0xd2 */
+/* File: armv5te/OP_MUL_INT_LIT16.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r2) @ r0<- vB
+ and r9, r9, #15
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ mul r0, r1, r0 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_INT_LIT16: /* 0xd3 */
+/* File: armv5te/OP_DIV_INT_LIT16.S */
+/* File: armv5te/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r2) @ r0<- vB
+ and r9, r9, #15
+ .if 1
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ bl __aeabi_idiv @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_INT_LIT16: /* 0xd4 */
+/* File: armv5te/OP_REM_INT_LIT16.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* File: armv5te/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r2) @ r0<- vB
+ and r9, r9, #15
+ .if 1
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ bl __aeabi_idivmod @ r1<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r1, r9) @ vAA<- r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_INT_LIT16: /* 0xd5 */
+/* File: armv5te/OP_AND_INT_LIT16.S */
+/* File: armv5te/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r2) @ r0<- vB
+ and r9, r9, #15
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ and r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_INT_LIT16: /* 0xd6 */
+/* File: armv5te/OP_OR_INT_LIT16.S */
+/* File: armv5te/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r2) @ r0<- vB
+ and r9, r9, #15
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ orr r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_INT_LIT16: /* 0xd7 */
+/* File: armv5te/OP_XOR_INT_LIT16.S */
+/* File: armv5te/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r2) @ r0<- vB
+ and r9, r9, #15
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ eor r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_INT_LIT8: /* 0xd8 */
+/* File: armv5te/OP_ADD_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ add r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RSUB_INT_LIT8: /* 0xd9 */
+/* File: armv5te/OP_RSUB_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ rsb r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_INT_LIT8: /* 0xda */
+/* File: armv5te/OP_MUL_INT_LIT8.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ mul r0, r1, r0 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_INT_LIT8: /* 0xdb */
+/* File: armv5te/OP_DIV_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 1
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_idiv @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_INT_LIT8: /* 0xdc */
+/* File: armv5te/OP_REM_INT_LIT8.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 1
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_idivmod @ r1<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r1, r9) @ vAA<- r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_INT_LIT8: /* 0xdd */
+/* File: armv5te/OP_AND_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ and r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_INT_LIT8: /* 0xde */
+/* File: armv5te/OP_OR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ orr r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_INT_LIT8: /* 0xdf */
+/* File: armv5te/OP_XOR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ eor r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHL_INT_LIT8: /* 0xe0 */
+/* File: armv5te/OP_SHL_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, asl r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHR_INT_LIT8: /* 0xe1 */
+/* File: armv5te/OP_SHR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, asr r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_USHR_INT_LIT8: /* 0xe2 */
+/* File: armv5te/OP_USHR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, lsr r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_VOLATILE: /* 0xe3 */
+/* File: armv5te/OP_IGET_VOLATILE.S */
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_VOLATILE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_VOLATILE_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_VOLATILE: /* 0xe4 */
+/* File: armv5te/OP_IPUT_VOLATILE.S */
+/* File: armv5te/OP_IPUT.S */
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_VOLATILE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_VOLATILE_finish @ yes, finish up
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_VOLATILE: /* 0xe5 */
+/* File: armv5te/OP_SGET_VOLATILE.S */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_VOLATILE_resolve @ yes, do resolve
+.LOP_SGET_VOLATILE_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ SMP_DMB @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_VOLATILE: /* 0xe6 */
+/* File: armv5te/OP_SPUT_VOLATILE.S */
+/* File: armv5te/OP_SPUT.S */
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SPUT_VOLATILE_resolve @ yes, do resolve
+.LOP_SPUT_VOLATILE_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SMP_DMB @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_OBJECT_VOLATILE: /* 0xe7 */
+/* File: armv5te/OP_IGET_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_OBJECT_VOLATILE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_OBJECT_VOLATILE_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_WIDE_VOLATILE: /* 0xe8 */
+/* File: armv5te/OP_IGET_WIDE_VOLATILE.S */
+/* File: armv5te/OP_IGET_WIDE.S */
+ /*
+ * Wide 32-bit instance field get.
+ */
+ /* iget-wide vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_WIDE_VOLATILE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_WIDE_VOLATILE_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_WIDE_VOLATILE: /* 0xe9 */
+/* File: armv5te/OP_IPUT_WIDE_VOLATILE.S */
+/* File: armv5te/OP_IPUT_WIDE.S */
+ /* iput-wide vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_WIDE_VOLATILE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_WIDE_VOLATILE_finish @ yes, finish up
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_WIDE_VOLATILE: /* 0xea */
+/* File: armv5te/OP_SGET_WIDE_VOLATILE.S */
+/* File: armv5te/OP_SGET_WIDE.S */
+ /*
+ * 64-bit SGET handler.
+ */
+ /* sget-wide vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_WIDE_VOLATILE_resolve @ yes, do resolve
+.LOP_SGET_WIDE_VOLATILE_finish:
+ mov r9, rINST, lsr #8 @ r9<- AA
+ .if 1
+ add r0, r0, #offStaticField_value @ r0<- pointer to data
+ bl dvmQuasiAtomicRead64 @ r0/r1<- contents of field
+ .else
+ ldrd r0, [r0, #offStaticField_value] @ r0/r1<- field value (aligned)
+ .endif
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_WIDE_VOLATILE: /* 0xeb */
+/* File: armv5te/OP_SPUT_WIDE_VOLATILE.S */
+/* File: armv5te/OP_SPUT_WIDE.S */
+ /*
+ * 64-bit SPUT handler.
+ */
+ /* sput-wide vAA, field@BBBB */
+ ldr r0, [rGLUE, #offGlue_methodClassDex] @ r0<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r0, [r0, #offDvmDex_pResFields] @ r0<- dvmDex->pResFields
+ mov r9, rINST, lsr #8 @ r9<- AA
+ ldr r2, [r0, r1, lsl #2] @ r2<- resolved StaticField ptr
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ cmp r2, #0 @ is resolved entry null?
+ beq .LOP_SPUT_WIDE_VOLATILE_resolve @ yes, do resolve
+.LOP_SPUT_WIDE_VOLATILE_finish: @ field ptr in r2, AA in r9
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ GET_INST_OPCODE(r10) @ extract opcode from rINST
+ .if 1
+ add r2, r2, #offStaticField_value @ r2<- pointer to data
+ bl dvmQuasiAtomicSwap64 @ stores r0/r1 into addr r2
+ .else
+ strd r0, [r2, #offStaticField_value] @ field<- vAA/vAA+1
+ .endif
+ GOTO_OPCODE(r10) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_BREAKPOINT: /* 0xec */
+/* File: armv5te/OP_BREAKPOINT.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_THROW_VERIFICATION_ERROR: /* 0xed */
+/* File: armv5te/OP_THROW_VERIFICATION_ERROR.S */
+ /*
+ * Handle a throw-verification-error instruction. This throws an
+ * exception for an error discovered during verification. The
+ * exception is indicated by AA, with some detail provided by BBBB.
+ */
+ /* op AA, ref@BBBB */
+ ldr r0, [rGLUE, #offGlue_method] @ r0<- glue->method
+ FETCH(r2, 1) @ r2<- BBBB
+ EXPORT_PC() @ export the PC
+ mov r1, rINST, lsr #8 @ r1<- AA
+ bl dvmThrowVerificationError @ always throws
+ b common_exceptionThrown @ handle exception
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_EXECUTE_INLINE: /* 0xee */
+/* File: armv5te/OP_EXECUTE_INLINE.S */
+ /*
+ * Execute a "native inline" instruction.
+ *
+ * We need to call an InlineOp4Func:
+ * bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+ *
+ * The first four args are in r0-r3, pointer to return value storage
+ * is on the stack. The function's return value is a flag that tells
+ * us if an exception was thrown.
+ */
+ /* [opt] execute-inline vAA, {vC, vD, vE, vF}, inline@BBBB */
+ FETCH(r10, 1) @ r10<- BBBB
+ add r1, rGLUE, #offGlue_retval @ r1<- &glue->retval
+ EXPORT_PC() @ can throw
+ sub sp, sp, #8 @ make room for arg, +64 bit align
+ mov r0, rINST, lsr #12 @ r0<- B
+ str r1, [sp] @ push &glue->retval
+ bl .LOP_EXECUTE_INLINE_continue @ make call; will return after
+ add sp, sp, #8 @ pop stack
+ cmp r0, #0 @ test boolean result of inline
+ beq common_exceptionThrown @ returned false, handle exception
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_EXECUTE_INLINE_RANGE: /* 0xef */
+/* File: armv5te/OP_EXECUTE_INLINE_RANGE.S */
+ /*
+ * Execute a "native inline" instruction, using "/range" semantics.
+ * Same idea as execute-inline, but we get the args differently.
+ *
+ * We need to call an InlineOp4Func:
+ * bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+ *
+ * The first four args are in r0-r3, pointer to return value storage
+ * is on the stack. The function's return value is a flag that tells
+ * us if an exception was thrown.
+ */
+ /* [opt] execute-inline/range {vCCCC..v(CCCC+AA-1)}, inline@BBBB */
+ FETCH(r10, 1) @ r10<- BBBB
+ add r1, rGLUE, #offGlue_retval @ r1<- &glue->retval
+ EXPORT_PC() @ can throw
+ sub sp, sp, #8 @ make room for arg, +64 bit align
+ mov r0, rINST, lsr #8 @ r0<- AA
+ str r1, [sp] @ push &glue->retval
+ bl .LOP_EXECUTE_INLINE_RANGE_continue @ make call; will return after
+ add sp, sp, #8 @ pop stack
+ cmp r0, #0 @ test boolean result of inline
+ beq common_exceptionThrown @ returned false, handle exception
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_DIRECT_EMPTY: /* 0xf0 */
+/* File: armv5te/OP_INVOKE_DIRECT_EMPTY.S */
+ /*
+ * invoke-direct-empty is a no-op in a "standard" interpreter.
+ */
+ FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ GOTO_OPCODE(ip) @ execute it
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_F1: /* 0xf1 */
+/* File: armv5te/OP_UNUSED_F1.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_QUICK: /* 0xf2 */
+/* File: armv5te/OP_IGET_QUICK.S */
+ /* For: iget-quick, iget-object-quick */
+ /* op vA, vB, offset@CCCC */
+ mov r2, rINST, lsr #12 @ r2<- B
+ GET_VREG(r3, r2) @ r3<- object we're operating on
+ FETCH(r1, 1) @ r1<- field byte offset
+ cmp r3, #0 @ check object for null
+ mov r2, rINST, lsr #8 @ r2<- A(+)
+ beq common_errNullObject @ object was null
+ ldr r0, [r3, r1] @ r0<- obj.field (always 32 bits)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_WIDE_QUICK: /* 0xf3 */
+/* File: armv5te/OP_IGET_WIDE_QUICK.S */
+ /* iget-wide-quick vA, vB, offset@CCCC */
+ mov r2, rINST, lsr #12 @ r2<- B
+ GET_VREG(r3, r2) @ r3<- object we're operating on
+ FETCH(ip, 1) @ ip<- field byte offset
+ cmp r3, #0 @ check object for null
+ mov r2, rINST, lsr #8 @ r2<- A(+)
+ beq common_errNullObject @ object was null
+ ldrd r0, [r3, ip] @ r0<- obj.field (64 bits, aligned)
+ and r2, r2, #15
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ add r3, rFP, r2, lsl #2 @ r3<- &fp[A]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ fp[A]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_OBJECT_QUICK: /* 0xf4 */
+/* File: armv5te/OP_IGET_OBJECT_QUICK.S */
+/* File: armv5te/OP_IGET_QUICK.S */
+ /* For: iget-quick, iget-object-quick */
+ /* op vA, vB, offset@CCCC */
+ mov r2, rINST, lsr #12 @ r2<- B
+ GET_VREG(r3, r2) @ r3<- object we're operating on
+ FETCH(r1, 1) @ r1<- field byte offset
+ cmp r3, #0 @ check object for null
+ mov r2, rINST, lsr #8 @ r2<- A(+)
+ beq common_errNullObject @ object was null
+ ldr r0, [r3, r1] @ r0<- obj.field (always 32 bits)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_QUICK: /* 0xf5 */
+/* File: armv5te/OP_IPUT_QUICK.S */
+ /* For: iput-quick */
+ /* op vA, vB, offset@CCCC */
+ mov r2, rINST, lsr #12 @ r2<- B
+ GET_VREG(r3, r2) @ r3<- fp[B], the object pointer
+ FETCH(r1, 1) @ r1<- field byte offset
+ cmp r3, #0 @ check object for null
+ mov r2, rINST, lsr #8 @ r2<- A(+)
+ beq common_errNullObject @ object was null
+ and r2, r2, #15
+ GET_VREG(r0, r2) @ r0<- fp[A]
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ str r0, [r3, r1] @ obj.field (always 32 bits)<- r0
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_WIDE_QUICK: /* 0xf6 */
+/* File: armv5te/OP_IPUT_WIDE_QUICK.S */
+ /* iput-wide-quick vA, vB, offset@CCCC */
+ mov r0, rINST, lsr #8 @ r0<- A(+)
+ mov r1, rINST, lsr #12 @ r1<- B
+ and r0, r0, #15
+ GET_VREG(r2, r1) @ r2<- fp[B], the object pointer
+ add r3, rFP, r0, lsl #2 @ r3<- &fp[A]
+ cmp r2, #0 @ check object for null
+ ldmia r3, {r0-r1} @ r0/r1<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH(r3, 1) @ r3<- field byte offset
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ strd r0, [r2, r3] @ obj.field (64 bits, aligned)<- r0/r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_OBJECT_QUICK: /* 0xf7 */
+/* File: armv5te/OP_IPUT_OBJECT_QUICK.S */
+ /* For: iput-object-quick */
+ /* op vA, vB, offset@CCCC */
+ mov r2, rINST, lsr #12 @ r2<- B
+ GET_VREG(r3, r2) @ r3<- fp[B], the object pointer
+ FETCH(r1, 1) @ r1<- field byte offset
+ cmp r3, #0 @ check object for null
+ mov r2, rINST, lsr #8 @ r2<- A(+)
+ beq common_errNullObject @ object was null
+ and r2, r2, #15
+ GET_VREG(r0, r2) @ r0<- fp[A]
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ str r0, [r3, r1] @ obj.field (always 32 bits)<- r0
+ cmp r0, #0
+ strneb r2, [r2, r3, lsr #GC_CARD_SHIFT] @ mark card based on obj head
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_VIRTUAL_QUICK: /* 0xf8 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK.S */
+ /*
+ * Handle an optimized virtual method call.
+ *
+ * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r3, 2) @ r3<- FEDC or CCCC
+ FETCH(r1, 1) @ r1<- BBBB
+ .if (!0)
+ and r3, r3, #15 @ r3<- C (or stays CCCC)
+ .endif
+ GET_VREG(r2, r3) @ r2<- vC ("this" ptr)
+ cmp r2, #0 @ is "this" null?
+ beq common_errNullObject @ null "this", throw exception
+ ldr r2, [r2, #offObject_clazz] @ r2<- thisPtr->clazz
+ ldr r2, [r2, #offClassObject_vtable] @ r2<- thisPtr->clazz->vtable
+ EXPORT_PC() @ invoke must export
+ ldr r0, [r2, r1, lsl #2] @ r3<- vtable[BBBB]
+ bl common_invokeMethodNoRange @ continue on
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_VIRTUAL_QUICK_RANGE: /* 0xf9 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK_RANGE.S */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK.S */
+ /*
+ * Handle an optimized virtual method call.
+ *
+ * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r3, 2) @ r3<- FEDC or CCCC
+ FETCH(r1, 1) @ r1<- BBBB
+ .if (!1)
+ and r3, r3, #15 @ r3<- C (or stays CCCC)
+ .endif
+ GET_VREG(r2, r3) @ r2<- vC ("this" ptr)
+ cmp r2, #0 @ is "this" null?
+ beq common_errNullObject @ null "this", throw exception
+ ldr r2, [r2, #offObject_clazz] @ r2<- thisPtr->clazz
+ ldr r2, [r2, #offClassObject_vtable] @ r2<- thisPtr->clazz->vtable
+ EXPORT_PC() @ invoke must export
+ ldr r0, [r2, r1, lsl #2] @ r3<- vtable[BBBB]
+ bl common_invokeMethodRange @ continue on
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_SUPER_QUICK: /* 0xfa */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK.S */
+ /*
+ * Handle an optimized "super" method call.
+ *
+ * for: [opt] invoke-super-quick, invoke-super-quick/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ .if (!0)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r2, [r2, #offMethod_clazz] @ r2<- method->clazz
+ EXPORT_PC() @ must export for invoke
+ ldr r2, [r2, #offClassObject_super] @ r2<- method->clazz->super
+ GET_VREG(r3, r10) @ r3<- "this"
+ ldr r2, [r2, #offClassObject_vtable] @ r2<- ...clazz->super->vtable
+ cmp r3, #0 @ null "this" ref?
+ ldr r0, [r2, r1, lsl #2] @ r0<- super->vtable[BBBB]
+ beq common_errNullObject @ "this" is null, throw exception
+ bl common_invokeMethodNoRange @ continue on
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_SUPER_QUICK_RANGE: /* 0xfb */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK_RANGE.S */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK.S */
+ /*
+ * Handle an optimized "super" method call.
+ *
+ * for: [opt] invoke-super-quick, invoke-super-quick/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ .if (!1)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r2, [r2, #offMethod_clazz] @ r2<- method->clazz
+ EXPORT_PC() @ must export for invoke
+ ldr r2, [r2, #offClassObject_super] @ r2<- method->clazz->super
+ GET_VREG(r3, r10) @ r3<- "this"
+ ldr r2, [r2, #offClassObject_vtable] @ r2<- ...clazz->super->vtable
+ cmp r3, #0 @ null "this" ref?
+ ldr r0, [r2, r1, lsl #2] @ r0<- super->vtable[BBBB]
+ beq common_errNullObject @ "this" is null, throw exception
+ bl common_invokeMethodRange @ continue on
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */
+/* File: armv5te/OP_IPUT_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_IPUT_OBJECT.S */
+ /*
+ * 32-bit instance field put.
+ *
+ * for: iput-object, iput-object-volatile
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_OBJECT_VOLATILE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_OBJECT_VOLATILE_finish @ yes, finish up
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_OBJECT_VOLATILE: /* 0xfd */
+/* File: armv5te/OP_SGET_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_OBJECT_VOLATILE_resolve @ yes, do resolve
+.LOP_SGET_OBJECT_VOLATILE_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ SMP_DMB @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */
+/* File: armv5te/OP_SPUT_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_SPUT_OBJECT.S */
+ /*
+ * 32-bit SPUT handler for objects
+ *
+ * for: sput-object, sput-object-volatile
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_SPUT_OBJECT_VOLATILE_finish @ no, continue
+ ldr r9, [rGLUE, #offGlue_method] @ r9<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r9, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_OBJECT_VOLATILE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_FF: /* 0xff */
+/* File: armv5te/OP_UNUSED_FF.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+
+ .balign 64
+ .size dvmAsmInstructionStart, .-dvmAsmInstructionStart
+ .global dvmAsmInstructionEnd
+dvmAsmInstructionEnd:
+
+/*
+ * ===========================================================================
+ * Sister implementations
+ * ===========================================================================
+ */
+ .global dvmAsmSisterStart
+ .type dvmAsmSisterStart, %function
+ .text
+ .balign 4
+dvmAsmSisterStart:
+
+/* continuation for OP_CONST_STRING */
+
+ /*
+ * Continuation if the String has not yet been resolved.
+ * r1: BBBB (String ref)
+ * r9: target register
+ */
+.LOP_CONST_STRING_resolve:
+ EXPORT_PC()
+ ldr r0, [rGLUE, #offGlue_method] @ r0<- glue->method
+ ldr r0, [r0, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveString @ r0<- String reference
+ cmp r0, #0 @ failed?
+ beq common_exceptionThrown @ yup, handle the exception
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_CONST_STRING_JUMBO */
+
+ /*
+ * Continuation if the String has not yet been resolved.
+ * r1: BBBBBBBB (String ref)
+ * r9: target register
+ */
+.LOP_CONST_STRING_JUMBO_resolve:
+ EXPORT_PC()
+ ldr r0, [rGLUE, #offGlue_method] @ r0<- glue->method
+ ldr r0, [r0, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveString @ r0<- String reference
+ cmp r0, #0 @ failed?
+ beq common_exceptionThrown @ yup, handle the exception
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_CONST_CLASS */
+
+ /*
+ * Continuation if the Class has not yet been resolved.
+ * r1: BBBB (Class ref)
+ * r9: target register
+ */
+.LOP_CONST_CLASS_resolve:
+ EXPORT_PC()
+ ldr r0, [rGLUE, #offGlue_method] @ r0<- glue->method
+ mov r2, #1 @ r2<- true
+ ldr r0, [r0, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- Class reference
+ cmp r0, #0 @ failed?
+ beq common_exceptionThrown @ yup, handle the exception
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_CHECK_CAST */
+
+ /*
+ * Trivial test failed, need to perform full check. This is common.
+ * r0 holds obj->clazz
+ * r1 holds class resolved from BBBB
+ * r9 holds object
+ */
+.LOP_CHECK_CAST_fullcheck:
+ bl dvmInstanceofNonTrivial @ r0<- boolean result
+ cmp r0, #0 @ failed?
+ bne .LOP_CHECK_CAST_okay @ no, success
+
+ @ A cast has failed. We need to throw a ClassCastException with the
+ @ class of the object that failed to be cast.
+ EXPORT_PC() @ about to throw
+ ldr r3, [r9, #offObject_clazz] @ r3<- obj->clazz
+ ldr r0, .LstrClassCastExceptionPtr
+ ldr r1, [r3, #offClassObject_descriptor] @ r1<- obj->clazz->descriptor
+ bl dvmThrowExceptionWithClassMessage
+ b common_exceptionThrown
+
+ /*
+ * Resolution required. This is the least-likely path.
+ *
+ * r2 holds BBBB
+ * r9 holds object
+ */
+.LOP_CHECK_CAST_resolve:
+ EXPORT_PC() @ resolve() could throw
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ mov r1, r2 @ r1<- BBBB
+ mov r2, #0 @ r2<- false
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- resolved ClassObject ptr
+ cmp r0, #0 @ got null?
+ beq common_exceptionThrown @ yes, handle exception
+ mov r1, r0 @ r1<- class resolved from BBB
+ ldr r0, [r9, #offObject_clazz] @ r0<- obj->clazz
+ b .LOP_CHECK_CAST_resolved @ pick up where we left off
+
+.LstrClassCastExceptionPtr:
+ .word .LstrClassCastException
+
+/* continuation for OP_INSTANCE_OF */
+
+ /*
+ * Trivial test failed, need to perform full check. This is common.
+ * r0 holds obj->clazz
+ * r1 holds class resolved from BBBB
+ * r9 holds A
+ */
+.LOP_INSTANCE_OF_fullcheck:
+ bl dvmInstanceofNonTrivial @ r0<- boolean result
+ @ fall through to OP_INSTANCE_OF_store
+
+ /*
+ * r0 holds boolean result
+ * r9 holds A
+ */
+.LOP_INSTANCE_OF_store:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r0, r9) @ vA<- r0
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+ /*
+ * Trivial test succeeded, save and bail.
+ * r9 holds A
+ */
+.LOP_INSTANCE_OF_trivial:
+ mov r0, #1 @ indicate success
+ @ could b OP_INSTANCE_OF_store, but copying is faster and cheaper
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r0, r9) @ vA<- r0
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+ /*
+ * Resolution required. This is the least-likely path.
+ *
+ * r3 holds BBBB
+ * r9 holds A
+ */
+.LOP_INSTANCE_OF_resolve:
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [rGLUE, #offGlue_method] @ r0<- glue->method
+ mov r1, r3 @ r1<- BBBB
+ mov r2, #1 @ r2<- true
+ ldr r0, [r0, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- resolved ClassObject ptr
+ cmp r0, #0 @ got null?
+ beq common_exceptionThrown @ yes, handle exception
+ mov r1, r0 @ r1<- class resolved from BBB
+ mov r3, rINST, lsr #12 @ r3<- B
+ GET_VREG(r0, r3) @ r0<- vB (object)
+ ldr r0, [r0, #offObject_clazz] @ r0<- obj->clazz
+ b .LOP_INSTANCE_OF_resolved @ pick up where we left off
+
+/* continuation for OP_NEW_INSTANCE */
+
+ .balign 32 @ minimize cache lines
+.LOP_NEW_INSTANCE_finish: @ r0=new object
+ mov r3, rINST, lsr #8 @ r3<- AA
+ cmp r0, #0 @ failed?
+ beq common_exceptionThrown @ yes, handle the exception
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r3) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+ /*
+ * Class initialization required.
+ *
+ * r0 holds class object
+ */
+.LOP_NEW_INSTANCE_needinit:
+ mov r9, r0 @ save r0
+ bl dvmInitClass @ initialize class
+ cmp r0, #0 @ check boolean result
+ mov r0, r9 @ restore r0
+ bne .LOP_NEW_INSTANCE_initialized @ success, continue
+ b common_exceptionThrown @ failed, deal with init exception
+
+ /*
+ * Resolution required. This is the least-likely path.
+ *
+ * r1 holds BBBB
+ */
+.LOP_NEW_INSTANCE_resolve:
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ mov r2, #0 @ r2<- false
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- resolved ClassObject ptr
+ cmp r0, #0 @ got null?
+ bne .LOP_NEW_INSTANCE_resolved @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+.LstrInstantiationErrorPtr:
+ .word .LstrInstantiationError
+
+/* continuation for OP_NEW_ARRAY */
+
+
+ /*
+ * Resolve class. (This is an uncommon case.)
+ *
+ * r1 holds array length
+ * r2 holds class ref CCCC
+ */
+.LOP_NEW_ARRAY_resolve:
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ mov r9, r1 @ r9<- length (save)
+ mov r1, r2 @ r1<- CCCC
+ mov r2, #0 @ r2<- false
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- call(clazz, ref)
+ cmp r0, #0 @ got null?
+ mov r1, r9 @ r1<- length (restore)
+ beq common_exceptionThrown @ yes, handle exception
+ @ fall through to OP_NEW_ARRAY_finish
+
+ /*
+ * Finish allocation.
+ *
+ * r0 holds class
+ * r1 holds array length
+ */
+.LOP_NEW_ARRAY_finish:
+ mov r2, #ALLOC_DONT_TRACK @ don't track in local refs table
+ bl dvmAllocArrayByClass @ r0<- call(clazz, length, flags)
+ cmp r0, #0 @ failed?
+ mov r2, rINST, lsr #8 @ r2<- A+
+ beq common_exceptionThrown @ yes, handle the exception
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ vA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_FILLED_NEW_ARRAY */
+
+ /*
+ * On entry:
+ * r0 holds array class
+ * r10 holds AA or BA
+ */
+.LOP_FILLED_NEW_ARRAY_continue:
+ ldr r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor
+ mov r2, #ALLOC_DONT_TRACK @ r2<- alloc flags
+ ldrb rINST, [r3, #1] @ rINST<- descriptor[1]
+ .if 0
+ mov r1, r10 @ r1<- AA (length)
+ .else
+ mov r1, r10, lsr #4 @ r1<- B (length)
+ .endif
+ cmp rINST, #'I' @ array of ints?
+ cmpne rINST, #'L' @ array of objects?
+ cmpne rINST, #'[' @ array of arrays?
+ mov r9, r1 @ save length in r9
+ bne .LOP_FILLED_NEW_ARRAY_notimpl @ no, not handled yet
+ bl dvmAllocArrayByClass @ r0<- call(arClass, length, flags)
+ cmp r0, #0 @ null return?
+ beq common_exceptionThrown @ alloc failed, handle exception
+
+ FETCH(r1, 2) @ r1<- FEDC or CCCC
+ str r0, [rGLUE, #offGlue_retval] @ retval.l <- new array
+ str rINST, [rGLUE, #offGlue_retval+4] @ retval.h <- type
+ add r0, r0, #offArrayObject_contents @ r0<- newArray->contents
+ subs r9, r9, #1 @ length--, check for neg
+ FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST
+ bmi 2f @ was zero, bail
+
+ @ copy values from registers into the array
+ @ r0=array, r1=CCCC/FEDC, r9=length (from AA or B), r10=AA/BA
+ .if 0
+ add r2, rFP, r1, lsl #2 @ r2<- &fp[CCCC]
+1: ldr r3, [r2], #4 @ r3<- *r2++
+ subs r9, r9, #1 @ count--
+ str r3, [r0], #4 @ *contents++ = vX
+ bpl 1b
+ @ continue at 2
+ .else
+ cmp r9, #4 @ length was initially 5?
+ and r2, r10, #15 @ r2<- A
+ bne 1f @ <= 4 args, branch
+ GET_VREG(r3, r2) @ r3<- vA
+ sub r9, r9, #1 @ count--
+ str r3, [r0, #16] @ contents[4] = vA
+1: and r2, r1, #15 @ r2<- F/E/D/C
+ GET_VREG(r3, r2) @ r3<- vF/vE/vD/vC
+ mov r1, r1, lsr #4 @ r1<- next reg in low 4
+ subs r9, r9, #1 @ count--
+ str r3, [r0], #4 @ *contents++ = vX
+ bpl 1b
+ @ continue at 2
+ .endif
+
+2:
+ ldr r0, [rGLUE, #offGlue_retval] @ r0<- object
+ ldr r1, [rGLUE, #offGlue_retval+4] @ r1<- type
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ cmp r1, #'I' @ Is int array?
+ strneb r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card based on object head
+ GOTO_OPCODE(ip) @ execute it
+
+ /*
+ * Throw an exception indicating that we have not implemented this
+ * mode of filled-new-array.
+ */
+.LOP_FILLED_NEW_ARRAY_notimpl:
+ ldr r0, .L_strInternalError
+ ldr r1, .L_strFilledNewArrayNotImpl
+ bl dvmThrowException
+ b common_exceptionThrown
+
+ .if (!0) @ define in one or the other, not both
+.L_strFilledNewArrayNotImpl:
+ .word .LstrFilledNewArrayNotImpl
+.L_strInternalError:
+ .word .LstrInternalError
+ .endif
+
+/* continuation for OP_FILLED_NEW_ARRAY_RANGE */
+
+ /*
+ * On entry:
+ * r0 holds array class
+ * r10 holds AA or BA
+ */
+.LOP_FILLED_NEW_ARRAY_RANGE_continue:
+ ldr r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor
+ mov r2, #ALLOC_DONT_TRACK @ r2<- alloc flags
+ ldrb rINST, [r3, #1] @ rINST<- descriptor[1]
+ .if 1
+ mov r1, r10 @ r1<- AA (length)
+ .else
+ mov r1, r10, lsr #4 @ r1<- B (length)
+ .endif
+ cmp rINST, #'I' @ array of ints?
+ cmpne rINST, #'L' @ array of objects?
+ cmpne rINST, #'[' @ array of arrays?
+ mov r9, r1 @ save length in r9
+ bne .LOP_FILLED_NEW_ARRAY_RANGE_notimpl @ no, not handled yet
+ bl dvmAllocArrayByClass @ r0<- call(arClass, length, flags)
+ cmp r0, #0 @ null return?
+ beq common_exceptionThrown @ alloc failed, handle exception
+
+ FETCH(r1, 2) @ r1<- FEDC or CCCC
+ str r0, [rGLUE, #offGlue_retval] @ retval.l <- new array
+ str rINST, [rGLUE, #offGlue_retval+4] @ retval.h <- type
+ add r0, r0, #offArrayObject_contents @ r0<- newArray->contents
+ subs r9, r9, #1 @ length--, check for neg
+ FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST
+ bmi 2f @ was zero, bail
+
+ @ copy values from registers into the array
+ @ r0=array, r1=CCCC/FEDC, r9=length (from AA or B), r10=AA/BA
+ .if 1
+ add r2, rFP, r1, lsl #2 @ r2<- &fp[CCCC]
+1: ldr r3, [r2], #4 @ r3<- *r2++
+ subs r9, r9, #1 @ count--
+ str r3, [r0], #4 @ *contents++ = vX
+ bpl 1b
+ @ continue at 2
+ .else
+ cmp r9, #4 @ length was initially 5?
+ and r2, r10, #15 @ r2<- A
+ bne 1f @ <= 4 args, branch
+ GET_VREG(r3, r2) @ r3<- vA
+ sub r9, r9, #1 @ count--
+ str r3, [r0, #16] @ contents[4] = vA
+1: and r2, r1, #15 @ r2<- F/E/D/C
+ GET_VREG(r3, r2) @ r3<- vF/vE/vD/vC
+ mov r1, r1, lsr #4 @ r1<- next reg in low 4
+ subs r9, r9, #1 @ count--
+ str r3, [r0], #4 @ *contents++ = vX
+ bpl 1b
+ @ continue at 2
+ .endif
+
+2:
+ ldr r0, [rGLUE, #offGlue_retval] @ r0<- object
+ ldr r1, [rGLUE, #offGlue_retval+4] @ r1<- type
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ cmp r1, #'I' @ Is int array?
+ strneb r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card based on object head
+ GOTO_OPCODE(ip) @ execute it
+
+ /*
+ * Throw an exception indicating that we have not implemented this
+ * mode of filled-new-array.
+ */
+.LOP_FILLED_NEW_ARRAY_RANGE_notimpl:
+ ldr r0, .L_strInternalError
+ ldr r1, .L_strFilledNewArrayNotImpl
+ bl dvmThrowException
+ b common_exceptionThrown
+
+ .if (!1) @ define in one or the other, not both
+.L_strFilledNewArrayNotImpl:
+ .word .LstrFilledNewArrayNotImpl
+.L_strInternalError:
+ .word .LstrInternalError
+ .endif
+
+/* continuation for OP_CMPL_FLOAT */
+
+ @ Test for NaN with a second comparison. EABI forbids testing bit
+ @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+ @ make the library call.
+.LOP_CMPL_FLOAT_gt_or_nan:
+ mov r1, r9 @ reverse order
+ mov r0, r10
+ bl __aeabi_cfcmple @ r0<- Z set if eq, C clear if <
+ @bleq common_abort
+ movcc r1, #1 @ (greater than) r1<- 1
+ bcc .LOP_CMPL_FLOAT_finish
+ mvn r1, #0 @ r1<- 1 or -1 for NaN
+ b .LOP_CMPL_FLOAT_finish
+
+
+#if 0 /* "clasic" form */
+ FETCH(r0, 1) @ r0<- CCBB
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ GET_VREG(r9, r2) @ r9<- vBB
+ GET_VREG(r10, r3) @ r10<- vCC
+ mov r0, r9 @ r0<- vBB
+ mov r1, r10 @ r1<- vCC
+ bl __aeabi_fcmpeq @ r0<- (vBB == vCC)
+ cmp r0, #0 @ equal?
+ movne r1, #0 @ yes, result is 0
+ bne OP_CMPL_FLOAT_finish
+ mov r0, r9 @ r0<- vBB
+ mov r1, r10 @ r1<- vCC
+ bl __aeabi_fcmplt @ r0<- (vBB < vCC)
+ cmp r0, #0 @ less than?
+ b OP_CMPL_FLOAT_continue
+@%break
+
+OP_CMPL_FLOAT_continue:
+ mvnne r1, #0 @ yes, result is -1
+ bne OP_CMPL_FLOAT_finish
+ mov r0, r9 @ r0<- vBB
+ mov r1, r10 @ r1<- vCC
+ bl __aeabi_fcmpgt @ r0<- (vBB > vCC)
+ cmp r0, #0 @ greater than?
+ beq OP_CMPL_FLOAT_nan @ no, must be NaN
+ mov r1, #1 @ yes, result is 1
+ @ fall through to _finish
+
+OP_CMPL_FLOAT_finish:
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r3) @ vAA<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+ /*
+ * This is expected to be uncommon, so we double-branch (once to here,
+ * again back to _finish).
+ */
+OP_CMPL_FLOAT_nan:
+ mvn r1, #0 @ r1<- 1 or -1 for NaN
+ b OP_CMPL_FLOAT_finish
+
+#endif
+
+/* continuation for OP_CMPG_FLOAT */
+
+ @ Test for NaN with a second comparison. EABI forbids testing bit
+ @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+ @ make the library call.
+.LOP_CMPG_FLOAT_gt_or_nan:
+ mov r1, r9 @ reverse order
+ mov r0, r10
+ bl __aeabi_cfcmple @ r0<- Z set if eq, C clear if <
+ @bleq common_abort
+ movcc r1, #1 @ (greater than) r1<- 1
+ bcc .LOP_CMPG_FLOAT_finish
+ mov r1, #1 @ r1<- 1 or -1 for NaN
+ b .LOP_CMPG_FLOAT_finish
+
+
+#if 0 /* "clasic" form */
+ FETCH(r0, 1) @ r0<- CCBB
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ GET_VREG(r9, r2) @ r9<- vBB
+ GET_VREG(r10, r3) @ r10<- vCC
+ mov r0, r9 @ r0<- vBB
+ mov r1, r10 @ r1<- vCC
+ bl __aeabi_fcmpeq @ r0<- (vBB == vCC)
+ cmp r0, #0 @ equal?
+ movne r1, #0 @ yes, result is 0
+ bne OP_CMPG_FLOAT_finish
+ mov r0, r9 @ r0<- vBB
+ mov r1, r10 @ r1<- vCC
+ bl __aeabi_fcmplt @ r0<- (vBB < vCC)
+ cmp r0, #0 @ less than?
+ b OP_CMPG_FLOAT_continue
+@%break
+
+OP_CMPG_FLOAT_continue:
+ mvnne r1, #0 @ yes, result is -1
+ bne OP_CMPG_FLOAT_finish
+ mov r0, r9 @ r0<- vBB
+ mov r1, r10 @ r1<- vCC
+ bl __aeabi_fcmpgt @ r0<- (vBB > vCC)
+ cmp r0, #0 @ greater than?
+ beq OP_CMPG_FLOAT_nan @ no, must be NaN
+ mov r1, #1 @ yes, result is 1
+ @ fall through to _finish
+
+OP_CMPG_FLOAT_finish:
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r3) @ vAA<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+ /*
+ * This is expected to be uncommon, so we double-branch (once to here,
+ * again back to _finish).
+ */
+OP_CMPG_FLOAT_nan:
+ mov r1, #1 @ r1<- 1 or -1 for NaN
+ b OP_CMPG_FLOAT_finish
+
+#endif
+
+/* continuation for OP_CMPL_DOUBLE */
+
+ @ Test for NaN with a second comparison. EABI forbids testing bit
+ @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+ @ make the library call.
+.LOP_CMPL_DOUBLE_gt_or_nan:
+ ldmia r10, {r0-r1} @ reverse order
+ ldmia r9, {r2-r3}
+ bl __aeabi_cdcmple @ r0<- Z set if eq, C clear if <
+ @bleq common_abort
+ movcc r1, #1 @ (greater than) r1<- 1
+ bcc .LOP_CMPL_DOUBLE_finish
+ mvn r1, #0 @ r1<- 1 or -1 for NaN
+ b .LOP_CMPL_DOUBLE_finish
+
+/* continuation for OP_CMPG_DOUBLE */
+
+ @ Test for NaN with a second comparison. EABI forbids testing bit
+ @ patterns, and we can't represent 0x7fc00000 in immediate form, so
+ @ make the library call.
+.LOP_CMPG_DOUBLE_gt_or_nan:
+ ldmia r10, {r0-r1} @ reverse order
+ ldmia r9, {r2-r3}
+ bl __aeabi_cdcmple @ r0<- Z set if eq, C clear if <
+ @bleq common_abort
+ movcc r1, #1 @ (greater than) r1<- 1
+ bcc .LOP_CMPG_DOUBLE_finish
+ mov r1, #1 @ r1<- 1 or -1 for NaN
+ b .LOP_CMPG_DOUBLE_finish
+
+/* continuation for OP_CMP_LONG */
+
+.LOP_CMP_LONG_less:
+ mvn r1, #0 @ r1<- -1
+ @ Want to cond code the next mov so we can avoid branch, but don't see it;
+ @ instead, we just replicate the tail end.
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r9) @ vAA<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+.LOP_CMP_LONG_greater:
+ mov r1, #1 @ r1<- 1
+ @ fall through to _finish
+
+.LOP_CMP_LONG_finish:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r9) @ vAA<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_AGET_WIDE */
+
+.LOP_AGET_WIDE_finish:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldrd r2, [r0, #offArrayObject_contents] @ r2/r3<- vBB[vCC]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r2-r3} @ vAA/vAA+1<- r2/r3
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_APUT_WIDE */
+
+.LOP_APUT_WIDE_finish:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldmia r9, {r2-r3} @ r2/r3<- vAA/vAA+1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ strd r2, [r0, #offArrayObject_contents] @ r2/r3<- vBB[vCC]
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_APUT_OBJECT */
+ /*
+ * On entry:
+ * rINST = vBB (arrayObj)
+ * r9 = vAA (obj)
+ * r10 = offset into array (vBB + vCC * width)
+ */
+.LOP_APUT_OBJECT_finish:
+ cmp r9, #0 @ storing null reference?
+ beq .LOP_APUT_OBJECT_skip_check @ yes, skip type checks
+ ldr r0, [r9, #offObject_clazz] @ r0<- obj->clazz
+ ldr r1, [rINST, #offObject_clazz] @ r1<- arrayObj->clazz
+ bl dvmCanPutArrayElement @ test object type vs. array type
+ cmp r0, #0 @ okay?
+ beq common_errArrayStore @ no
+ mov r1, rINST @ r1<- arrayObj
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldr r2, [rGLUE, #offGlue_cardTable] @ get biased CT base
+ add r10, #offArrayObject_contents @ r0<- pointer to slot
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ str r9, [r10] @ vBB[vCC]<- vAA
+ strb r2, [r2, r1, lsr #GC_CARD_SHIFT] @ mark card using object head
+ GOTO_OPCODE(ip) @ jump to next instruction
+.LOP_APUT_OBJECT_skip_check:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ str r9, [r10, #offArrayObject_contents] @ vBB[vCC]<- vAA
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_finish:
+ @bl common_squeak0
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_WIDE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_WIDE_finish:
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ .if 0
+ add r0, r9, r3 @ r0<- address of field
+ bl dvmQuasiAtomicRead64 @ r0/r1<- contents of field
+ .else
+ ldrd r0, [r9, r3] @ r0/r1<- obj.field (64-bit align ok)
+ .endif
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ add r3, rFP, r2, lsl #2 @ r3<- &fp[A]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ fp[A]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_OBJECT */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_OBJECT_finish:
+ @bl common_squeak0
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_BOOLEAN */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_BOOLEAN_finish:
+ @bl common_squeak1
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_BYTE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_BYTE_finish:
+ @bl common_squeak2
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_CHAR */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_CHAR_finish:
+ @bl common_squeak3
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_SHORT */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_SHORT_finish:
+ @bl common_squeak4
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_finish:
+ @bl common_squeak0
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT_WIDE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_WIDE_finish:
+ mov r2, rINST, lsr #8 @ r2<- A+
+ cmp r9, #0 @ check object for null
+ and r2, r2, #15 @ r2<- A
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ add r2, rFP, r2, lsl #2 @ r3<- &fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldmia r2, {r0-r1} @ r0/r1<- fp[A]
+ GET_INST_OPCODE(r10) @ extract opcode from rINST
+ .if 0
+ add r2, r9, r3 @ r2<- target address
+ bl dvmQuasiAtomicSwap64 @ stores r0/r1 into addr r2
+ .else
+ strd r0, [r9, r3] @ obj.field (64 bits, aligned)<- r0/r1
+ .endif
+ GOTO_OPCODE(r10) @ jump to next instruction
+
+/* continuation for OP_IPUT_OBJECT */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_OBJECT_finish:
+ @bl common_squeak0
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r0, [r9, r3] @ obj.field (32 bits)<- r0
+ cmp r0, #0 @ stored a null reference?
+ strneb r2, [r2, r9, lsr #GC_CARD_SHIFT] @ mark card if not
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT_BOOLEAN */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_BOOLEAN_finish:
+ @bl common_squeak1
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT_BYTE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_BYTE_finish:
+ @bl common_squeak2
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT_CHAR */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_CHAR_finish:
+ @bl common_squeak3
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT_SHORT */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_SHORT_finish:
+ @bl common_squeak4
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SGET */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SGET_WIDE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ *
+ * Returns StaticField pointer in r0.
+ */
+.LOP_SGET_WIDE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_WIDE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SGET_OBJECT */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_OBJECT_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_OBJECT_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SGET_BOOLEAN */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_BOOLEAN_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_BOOLEAN_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SGET_BYTE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_BYTE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_BYTE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SGET_CHAR */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_CHAR_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_CHAR_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SGET_SHORT */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_SHORT_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_SHORT_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SPUT_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_WIDE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ * r9: &fp[AA]
+ *
+ * Returns StaticField pointer in r2.
+ */
+.LOP_SPUT_WIDE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ mov r2, r0 @ copy to r2
+ bne .LOP_SPUT_WIDE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_OBJECT */
+.LOP_SPUT_OBJECT_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ ldr r9, [r0, #offField_clazz] @ r9<- field->clazz
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ cmp r1, #0 @ stored a null object?
+ strneb r2, [r2, r9, lsr #GC_CARD_SHIFT] @ mark card based on obj head
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SPUT_BOOLEAN */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SPUT_BOOLEAN_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_BOOLEAN_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_BYTE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SPUT_BYTE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_BYTE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_CHAR */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SPUT_CHAR_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_CHAR_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_SHORT */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SPUT_SHORT_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_SHORT_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_INVOKE_VIRTUAL */
+
+ /*
+ * At this point:
+ * r0 = resolved base method
+ * r10 = C or CCCC (index of first arg, which is the "this" ptr)
+ */
+.LOP_INVOKE_VIRTUAL_continue:
+ GET_VREG(r1, r10) @ r1<- "this" ptr
+ ldrh r2, [r0, #offMethod_methodIndex] @ r2<- baseMethod->methodIndex
+ cmp r1, #0 @ is "this" null?
+ beq common_errNullObject @ null "this", throw exception
+ ldr r3, [r1, #offObject_clazz] @ r1<- thisPtr->clazz
+ ldr r3, [r3, #offClassObject_vtable] @ r3<- thisPtr->clazz->vtable
+ ldr r0, [r3, r2, lsl #2] @ r3<- vtable[methodIndex]
+ bl common_invokeMethodNoRange @ continue on
+
+/* continuation for OP_INVOKE_SUPER */
+
+ /*
+ * At this point:
+ * r0 = resolved base method
+ * r9 = method->clazz
+ */
+.LOP_INVOKE_SUPER_continue:
+ ldr r1, [r9, #offClassObject_super] @ r1<- method->clazz->super
+ ldrh r2, [r0, #offMethod_methodIndex] @ r2<- baseMethod->methodIndex
+ ldr r3, [r1, #offClassObject_vtableCount] @ r3<- super->vtableCount
+ EXPORT_PC() @ must export for invoke
+ cmp r2, r3 @ compare (methodIndex, vtableCount)
+ bcs .LOP_INVOKE_SUPER_nsm @ method not present in superclass
+ ldr r1, [r1, #offClassObject_vtable] @ r1<- ...clazz->super->vtable
+ ldr r0, [r1, r2, lsl #2] @ r3<- vtable[methodIndex]
+ bl common_invokeMethodNoRange @ continue on
+
+.LOP_INVOKE_SUPER_resolve:
+ mov r0, r9 @ r0<- method->clazz
+ mov r2, #METHOD_VIRTUAL @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ bne .LOP_INVOKE_SUPER_continue @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+ /*
+ * Throw a NoSuchMethodError with the method name as the message.
+ * r0 = resolved base method
+ */
+.LOP_INVOKE_SUPER_nsm:
+ ldr r1, [r0, #offMethod_name] @ r1<- method name
+ b common_errNoSuchMethod
+
+/* continuation for OP_INVOKE_DIRECT */
+
+ /*
+ * On entry:
+ * r1 = reference (BBBB or CCCC)
+ * r10 = "this" register
+ */
+.LOP_INVOKE_DIRECT_resolve:
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ mov r2, #METHOD_DIRECT @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ GET_VREG(r2, r10) @ r2<- "this" ptr (reload)
+ bne .LOP_INVOKE_DIRECT_finish @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+/* continuation for OP_INVOKE_VIRTUAL_RANGE */
+
+ /*
+ * At this point:
+ * r0 = resolved base method
+ * r10 = C or CCCC (index of first arg, which is the "this" ptr)
+ */
+.LOP_INVOKE_VIRTUAL_RANGE_continue:
+ GET_VREG(r1, r10) @ r1<- "this" ptr
+ ldrh r2, [r0, #offMethod_methodIndex] @ r2<- baseMethod->methodIndex
+ cmp r1, #0 @ is "this" null?
+ beq common_errNullObject @ null "this", throw exception
+ ldr r3, [r1, #offObject_clazz] @ r1<- thisPtr->clazz
+ ldr r3, [r3, #offClassObject_vtable] @ r3<- thisPtr->clazz->vtable
+ ldr r0, [r3, r2, lsl #2] @ r3<- vtable[methodIndex]
+ bl common_invokeMethodRange @ continue on
+
+/* continuation for OP_INVOKE_SUPER_RANGE */
+
+ /*
+ * At this point:
+ * r0 = resolved base method
+ * r9 = method->clazz
+ */
+.LOP_INVOKE_SUPER_RANGE_continue:
+ ldr r1, [r9, #offClassObject_super] @ r1<- method->clazz->super
+ ldrh r2, [r0, #offMethod_methodIndex] @ r2<- baseMethod->methodIndex
+ ldr r3, [r1, #offClassObject_vtableCount] @ r3<- super->vtableCount
+ EXPORT_PC() @ must export for invoke
+ cmp r2, r3 @ compare (methodIndex, vtableCount)
+ bcs .LOP_INVOKE_SUPER_RANGE_nsm @ method not present in superclass
+ ldr r1, [r1, #offClassObject_vtable] @ r1<- ...clazz->super->vtable
+ ldr r0, [r1, r2, lsl #2] @ r3<- vtable[methodIndex]
+ bl common_invokeMethodRange @ continue on
+
+.LOP_INVOKE_SUPER_RANGE_resolve:
+ mov r0, r9 @ r0<- method->clazz
+ mov r2, #METHOD_VIRTUAL @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ bne .LOP_INVOKE_SUPER_RANGE_continue @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+ /*
+ * Throw a NoSuchMethodError with the method name as the message.
+ * r0 = resolved base method
+ */
+.LOP_INVOKE_SUPER_RANGE_nsm:
+ ldr r1, [r0, #offMethod_name] @ r1<- method name
+ b common_errNoSuchMethod
+
+/* continuation for OP_INVOKE_DIRECT_RANGE */
+
+ /*
+ * On entry:
+ * r1 = reference (BBBB or CCCC)
+ * r10 = "this" register
+ */
+.LOP_INVOKE_DIRECT_RANGE_resolve:
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ mov r2, #METHOD_DIRECT @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ GET_VREG(r2, r10) @ r2<- "this" ptr (reload)
+ bne .LOP_INVOKE_DIRECT_RANGE_finish @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+/* continuation for OP_FLOAT_TO_LONG */
+/*
+ * Convert the float in r0 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification. The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer. The EABI convert function isn't doing this for us.
+ */
+f2l_doconv:
+ stmfd sp!, {r4, lr}
+ mov r1, #0x5f000000 @ (float)maxlong
+ mov r4, r0
+ bl __aeabi_fcmpge @ is arg >= maxlong?
+ cmp r0, #0 @ nonzero == yes
+ mvnne r0, #0 @ return maxlong (7fffffff)
+ mvnne r1, #0x80000000
+ ldmnefd sp!, {r4, pc}
+
+ mov r0, r4 @ recover arg
+ mov r1, #0xdf000000 @ (float)minlong
+ bl __aeabi_fcmple @ is arg <= minlong?
+ cmp r0, #0 @ nonzero == yes
+ movne r0, #0 @ return minlong (80000000)
+ movne r1, #0x80000000
+ ldmnefd sp!, {r4, pc}
+
+ mov r0, r4 @ recover arg
+ mov r1, r4
+ bl __aeabi_fcmpeq @ is arg == self?
+ cmp r0, #0 @ zero == no
+ moveq r1, #0 @ return zero for NaN
+ ldmeqfd sp!, {r4, pc}
+
+ mov r0, r4 @ recover arg
+ bl __aeabi_f2lz @ convert float to long
+ ldmfd sp!, {r4, pc}
+
+/* continuation for OP_DOUBLE_TO_LONG */
+/*
+ * Convert the double in r0/r1 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification. The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer. The EABI convert function isn't doing this for us.
+ */
+d2l_doconv:
+ stmfd sp!, {r4, r5, lr} @ save regs
+ mov r3, #0x43000000 @ maxlong, as a double (high word)
+ add r3, #0x00e00000 @ 0x43e00000
+ mov r2, #0 @ maxlong, as a double (low word)
+ sub sp, sp, #4 @ align for EABI
+ mov r4, r0 @ save a copy of r0
+ mov r5, r1 @ and r1
+ bl __aeabi_dcmpge @ is arg >= maxlong?
+ cmp r0, #0 @ nonzero == yes
+ mvnne r0, #0 @ return maxlong (7fffffffffffffff)
+ mvnne r1, #0x80000000
+ bne 1f
+
+ mov r0, r4 @ recover arg
+ mov r1, r5
+ mov r3, #0xc3000000 @ minlong, as a double (high word)
+ add r3, #0x00e00000 @ 0xc3e00000
+ mov r2, #0 @ minlong, as a double (low word)
+ bl __aeabi_dcmple @ is arg <= minlong?
+ cmp r0, #0 @ nonzero == yes
+ movne r0, #0 @ return minlong (8000000000000000)
+ movne r1, #0x80000000
+ bne 1f
+
+ mov r0, r4 @ recover arg
+ mov r1, r5
+ mov r2, r4 @ compare against self
+ mov r3, r5
+ bl __aeabi_dcmpeq @ is arg == self?
+ cmp r0, #0 @ zero == no
+ moveq r1, #0 @ return zero for NaN
+ beq 1f
+
+ mov r0, r4 @ recover arg
+ mov r1, r5
+ bl __aeabi_d2lz @ convert double to long
+
+1:
+ add sp, sp, #4
+ ldmfd sp!, {r4, r5, pc}
+
+/* continuation for OP_MUL_LONG */
+
+.LOP_MUL_LONG_finish:
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r0, {r9-r10} @ vAA/vAA+1<- r9/r10
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SHL_LONG */
+
+.LOP_SHL_LONG_finish:
+ mov r0, r0, asl r2 @ r0<- r0 << r2
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SHR_LONG */
+
+.LOP_SHR_LONG_finish:
+ mov r1, r1, asr r2 @ r1<- r1 >> r2
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_USHR_LONG */
+
+.LOP_USHR_LONG_finish:
+ mov r1, r1, lsr r2 @ r1<- r1 >>> r2
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SHL_LONG_2ADDR */
+
+.LOP_SHL_LONG_2ADDR_finish:
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SHR_LONG_2ADDR */
+
+.LOP_SHR_LONG_2ADDR_finish:
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_USHR_LONG_2ADDR */
+
+.LOP_USHR_LONG_2ADDR_finish:
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_VOLATILE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_VOLATILE_finish:
+ @bl common_squeak0
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ SMP_DMB @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT_VOLATILE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_VOLATILE_finish:
+ @bl common_squeak0
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SMP_DMB @ releasing store
+ str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SGET_VOLATILE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_VOLATILE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_VOLATILE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_VOLATILE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SPUT_VOLATILE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_VOLATILE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_IGET_OBJECT_VOLATILE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_OBJECT_VOLATILE_finish:
+ @bl common_squeak0
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ SMP_DMB @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_WIDE_VOLATILE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_WIDE_VOLATILE_finish:
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ .if 1
+ add r0, r9, r3 @ r0<- address of field
+ bl dvmQuasiAtomicRead64 @ r0/r1<- contents of field
+ .else
+ ldrd r0, [r9, r3] @ r0/r1<- obj.field (64-bit align ok)
+ .endif
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ add r3, rFP, r2, lsl #2 @ r3<- &fp[A]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ fp[A]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT_WIDE_VOLATILE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_WIDE_VOLATILE_finish:
+ mov r2, rINST, lsr #8 @ r2<- A+
+ cmp r9, #0 @ check object for null
+ and r2, r2, #15 @ r2<- A
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ add r2, rFP, r2, lsl #2 @ r3<- &fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldmia r2, {r0-r1} @ r0/r1<- fp[A]
+ GET_INST_OPCODE(r10) @ extract opcode from rINST
+ .if 1
+ add r2, r9, r3 @ r2<- target address
+ bl dvmQuasiAtomicSwap64 @ stores r0/r1 into addr r2
+ .else
+ strd r0, [r9, r3] @ obj.field (64 bits, aligned)<- r0/r1
+ .endif
+ GOTO_OPCODE(r10) @ jump to next instruction
+
+/* continuation for OP_SGET_WIDE_VOLATILE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ *
+ * Returns StaticField pointer in r0.
+ */
+.LOP_SGET_WIDE_VOLATILE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_WIDE_VOLATILE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_WIDE_VOLATILE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ * r9: &fp[AA]
+ *
+ * Returns StaticField pointer in r2.
+ */
+.LOP_SPUT_WIDE_VOLATILE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ mov r2, r0 @ copy to r2
+ bne .LOP_SPUT_WIDE_VOLATILE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_EXECUTE_INLINE */
+
+ /*
+ * Extract args, call function.
+ * r0 = #of args (0-4)
+ * r10 = call index
+ * lr = return addr, above [DO NOT bl out of here w/o preserving LR]
+ *
+ * Other ideas:
+ * - Use a jump table from the main piece to jump directly into the
+ * AND/LDR pairs. Costs a data load, saves a branch.
+ * - Have five separate pieces that do the loading, so we can work the
+ * interleave a little better. Increases code size.
+ */
+.LOP_EXECUTE_INLINE_continue:
+ rsb r0, r0, #4 @ r0<- 4-r0
+ FETCH(r9, 2) @ r9<- FEDC
+ add pc, pc, r0, lsl #3 @ computed goto, 2 instrs each
+ bl common_abort @ (skipped due to ARM prefetch)
+4: and ip, r9, #0xf000 @ isolate F
+ ldr r3, [rFP, ip, lsr #10] @ r3<- vF (shift right 12, left 2)
+3: and ip, r9, #0x0f00 @ isolate E
+ ldr r2, [rFP, ip, lsr #6] @ r2<- vE
+2: and ip, r9, #0x00f0 @ isolate D
+ ldr r1, [rFP, ip, lsr #2] @ r1<- vD
+1: and ip, r9, #0x000f @ isolate C
+ ldr r0, [rFP, ip, lsl #2] @ r0<- vC
+0:
+ ldr r9, .LOP_EXECUTE_INLINE_table @ table of InlineOperation
+ LDR_PC "[r9, r10, lsl #4]" @ sizeof=16, "func" is first entry
+ @ (not reached)
+
+.LOP_EXECUTE_INLINE_table:
+ .word gDvmInlineOpsTable
+
+/* continuation for OP_EXECUTE_INLINE_RANGE */
+
+ /*
+ * Extract args, call function.
+ * r0 = #of args (0-4)
+ * r10 = call index
+ * lr = return addr, above [DO NOT bl out of here w/o preserving LR]
+ */
+.LOP_EXECUTE_INLINE_RANGE_continue:
+ rsb r0, r0, #4 @ r0<- 4-r0
+ FETCH(r9, 2) @ r9<- CCCC
+ add pc, pc, r0, lsl #3 @ computed goto, 2 instrs each
+ bl common_abort @ (skipped due to ARM prefetch)
+4: add ip, r9, #3 @ base+3
+ GET_VREG(r3, ip) @ r3<- vBase[3]
+3: add ip, r9, #2 @ base+2
+ GET_VREG(r2, ip) @ r2<- vBase[2]
+2: add ip, r9, #1 @ base+1
+ GET_VREG(r1, ip) @ r1<- vBase[1]
+1: add ip, r9, #0 @ (nop)
+ GET_VREG(r0, ip) @ r0<- vBase[0]
+0:
+ ldr r9, .LOP_EXECUTE_INLINE_RANGE_table @ table of InlineOperation
+ LDR_PC "[r9, r10, lsl #4]" @ sizeof=16, "func" is first entry
+ @ (not reached)
+
+.LOP_EXECUTE_INLINE_RANGE_table:
+ .word gDvmInlineOpsTable
+
+/* continuation for OP_IPUT_OBJECT_VOLATILE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_OBJECT_VOLATILE_finish:
+ @bl common_squeak0
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SMP_DMB @ releasing store
+ str r0, [r9, r3] @ obj.field (32 bits)<- r0
+ cmp r0, #0 @ stored a null reference?
+ strneb r2, [r2, r9, lsr #GC_CARD_SHIFT] @ mark card if not
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SGET_OBJECT_VOLATILE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_OBJECT_VOLATILE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_OBJECT_VOLATILE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_OBJECT_VOLATILE */
+.LOP_SPUT_OBJECT_VOLATILE_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ ldr r9, [r0, #offField_clazz] @ r9<- field->clazz
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SMP_DMB @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ cmp r1, #0 @ stored a null object?
+ strneb r2, [r2, r9, lsr #GC_CARD_SHIFT] @ mark card based on obj head
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+ .size dvmAsmSisterStart, .-dvmAsmSisterStart
+ .global dvmAsmSisterEnd
+dvmAsmSisterEnd:
+
+/* File: armv5te/footer.S */
+
+/*
+ * ===========================================================================
+ * Common subroutines and data
+ * ===========================================================================
+ */
+
+
+
+ .text
+ .align 2
+
+#if defined(WITH_JIT)
+#if defined(WITH_SELF_VERIFICATION)
+ .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r2,#kSVSPunt @ r2<- interpreter entry point
+ mov r3, #0
+ str r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+ str lr,[rGLUE,#offGlue_jitResumeNPC]
+ str r1,[rGLUE,#offGlue_jitResumeDPC]
+ mov r2,#kSVSSingleStep @ r2<- interpreter entry point
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r0,rPC @ pass our target PC
+ mov r2,#kSVSNoProfile @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [r10, #offThread_inJitCodeCache] @ back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r0,rPC @ pass our target PC
+ mov r2,#kSVSTraceSelect @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ ldr r0,[lr, #-1] @ pass our target PC
+ mov r2,#kSVSTraceSelect @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpBackwardBranch
+dvmJitToInterpBackwardBranch:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ ldr r0,[lr, #-1] @ pass our target PC
+ mov r2,#kSVSBackwardBranch @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ ldr r0,[lr, #-1] @ pass our target PC
+ mov r2,#kSVSNormal @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r0,rPC @ pass our target PC
+ mov r2,#kSVSNoChain @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+#else
+/*
+ * Return from the translation cache to the interpreter when the compiler is
+ * having issues translating/executing a Dalvik instruction. We have to skip
+ * the code cache lookup otherwise it is possible to indefinitely bouce
+ * between the interpreter and the code cache if the instruction that fails
+ * to be compiled happens to be at a trace start.
+ */
+ .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov rPC, r0
+#if defined(WITH_JIT_TUNING)
+ mov r0,lr
+ bl dvmBumpPunt;
+#endif
+ EXPORT_PC()
+ mov r0, #0
+ str r0, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ adrl rIBASE, dvmAsmInstructionStart
+ FETCH_INST()
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+
+/*
+ * Return to the interpreter to handle a single instruction.
+ * On entry:
+ * r0 <= PC
+ * r1 <= PC of resume instruction
+ * lr <= resume point in translation
+ */
+ .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+ str lr,[rGLUE,#offGlue_jitResumeNPC]
+ str r1,[rGLUE,#offGlue_jitResumeDPC]
+ mov r1,#kInterpEntryInstr
+ @ enum is 4 byte in aapcs-EABI
+ str r1, [rGLUE, #offGlue_entryPoint]
+ mov rPC,r0
+ EXPORT_PC()
+
+ adrl rIBASE, dvmAsmInstructionStart
+ mov r2,#kJitSingleStep @ Ask for single step and then revert
+ str r2,[rGLUE,#offGlue_jitState]
+ mov r1,#1 @ set changeInterp to bail to debug interp
+ b common_gotoBail
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target. Commonly used for callees.
+ */
+ .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+#if defined(WITH_JIT_TUNING)
+ bl dvmBumpNoChain
+#endif
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r0,rPC
+ bl dvmJitGetCodeAddr @ Is there a translation?
+ str r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0 @ !0 means translation exists
+ bxne r0 @ continue native execution if so
+ b 2f @ branch over to use the interpreter
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target. Commonly used following
+ * invokes.
+ */
+ .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+ ldr rPC,[lr, #-1] @ get our target PC
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ add rINST,lr,#-5 @ save start of chain branch
+ add rINST, #-4 @ .. which is 9 bytes back
+ mov r0,rPC
+ bl dvmJitGetCodeAddr @ Is there a translation?
+ str r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ cmp r0,#0
+ beq 2f
+ mov r1,rINST
+ bl dvmJitChain @ r0<- dvmJitChain(codeAddr,chainAddr)
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0 @ successful chain?
+ bxne r0 @ continue native execution
+ b toInterpreter @ didn't chain - resume with interpreter
+
+/* No translation, so request one if profiling isn't disabled*/
+2:
+ adrl rIBASE, dvmAsmInstructionStart
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_INST()
+ cmp r0, #0
+ movne r2,#kJitTSelectRequestHot @ ask for trace selection
+ bne common_selectTrace
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+
+/*
+ * Return from the translation cache to the interpreter.
+ * The return was done with a BLX from thumb mode, and
+ * the following 32-bit word contains the target rPC value.
+ * Note that lr (r14) will have its low-order bit set to denote
+ * its thumb-mode origin.
+ *
+ * We'll need to stash our lr origin away, recover the new
+ * target and then check to see if there is a translation available
+ * for our new target. If so, we do a translation chain and
+ * go back to native execution. Otherwise, it's back to the
+ * interpreter (after treating this entry as a potential
+ * trace start).
+ */
+ .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+ ldr rPC,[lr, #-1] @ get our target PC
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ add rINST,lr,#-5 @ save start of chain branch
+ add rINST,#-4 @ .. which is 9 bytes back
+#if defined(WITH_JIT_TUNING)
+ bl dvmBumpNormal
+#endif
+ mov r0,rPC
+ bl dvmJitGetCodeAddr @ Is there a translation?
+ str r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ cmp r0,#0
+ beq toInterpreter @ go if not, otherwise do chain
+ mov r1,rINST
+ bl dvmJitChain @ r0<- dvmJitChain(codeAddr,chainAddr)
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0 @ successful chain?
+ bxne r0 @ continue native execution
+ b toInterpreter @ didn't chain - resume with interpreter
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+ .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+#if defined(WITH_JIT_TUNING)
+ bl dvmBumpNoChain
+#endif
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r0,rPC
+ bl dvmJitGetCodeAddr @ Is there a translation?
+ str r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0
+ bxne r0 @ continue native execution if so
+ EXPORT_PC()
+ adrl rIBASE, dvmAsmInstructionStart
+ FETCH_INST()
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+ .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+#if defined(WITH_JIT_TUNING)
+ bl dvmBumpNoChain
+#endif
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r0,rPC
+ bl dvmJitGetCodeAddr @ Is there a translation?
+ str r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0
+ bxne r0 @ continue native execution if so
+#endif
+
+/*
+ * No translation, restore interpreter regs and start interpreting.
+ * rGLUE & rFP were preserved in the translated code, and rPC has
+ * already been restored by the time we get here. We'll need to set
+ * up rIBASE & rINST, and load the address of the JitTable into r0.
+ */
+toInterpreter:
+ EXPORT_PC()
+ adrl rIBASE, dvmAsmInstructionStart
+ FETCH_INST()
+ GET_JIT_PROF_TABLE(r0)
+ @ NOTE: intended fallthrough
+
+/*
+ * Common code to update potential trace start counter, and initiate
+ * a trace-build if appropriate. On entry, rPC should point to the
+ * next instruction to execute, and rINST should be already loaded with
+ * the next opcode word, and r0 holds a pointer to the jit profile
+ * table (pJitProfTable).
+ */
+common_testUpdateProfile:
+ cmp r0,#0
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE_IFEQ(ip) @ if not profiling, fallthrough otherwise */
+
+common_updateProfile:
+ eor r3,rPC,rPC,lsr #12 @ cheap, but fast hash function
+ lsl r3,r3,#(32 - JIT_PROF_SIZE_LOG_2) @ shift out excess bits
+ ldrb r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ get counter
+ GET_INST_OPCODE(ip)
+ subs r1,r1,#1 @ decrement counter
+ strb r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ and store it
+ GOTO_OPCODE_IFNE(ip) @ if not threshold, fallthrough otherwise */
+
+/*
+ * Here, we switch to the debug interpreter to request
+ * trace selection. First, though, check to see if there
+ * is already a native translation in place (and, if so,
+ * jump to it now).
+ */
+ GET_JIT_THRESHOLD(r1)
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ strb r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ reset counter
+ EXPORT_PC()
+ mov r0,rPC
+ bl dvmJitGetCodeAddr @ r0<- dvmJitGetCodeAddr(rPC)
+ str r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0
+#if !defined(WITH_SELF_VERIFICATION)
+ bxne r0 @ jump to the translation
+ mov r2,#kJitTSelectRequest @ ask for trace selection
+ @ fall-through to common_selectTrace
+#else
+ moveq r2,#kJitTSelectRequest @ ask for trace selection
+ beq common_selectTrace
+ /*
+ * At this point, we have a target translation. However, if
+ * that translation is actually the interpret-only pseudo-translation
+ * we want to treat it the same as no translation.
+ */
+ mov r10, r0 @ save target
+ bl dvmCompilerGetInterpretTemplate
+ cmp r0, r10 @ special case?
+ bne jitSVShadowRunStart @ set up self verification shadow space
+ @ Need to clear the inJitCodeCache flag
+ ldr r10, [rGLUE, #offGlue_self] @ r10 <- glue->self
+ mov r3, #0 @ 0 means not in the JIT code cache
+ str r3, [r10, #offThread_inJitCodeCache] @ back to the interp land
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+ /* no return */
+#endif
+
+/*
+ * On entry:
+ * r2 is jit state, e.g. kJitTSelectRequest or kJitTSelectRequestHot
+ */
+common_selectTrace:
+ str r2,[rGLUE,#offGlue_jitState]
+ mov r2,#kInterpEntryInstr @ normal entry reason
+ str r2,[rGLUE,#offGlue_entryPoint]
+ mov r1,#1 @ set changeInterp
+ b common_gotoBail
+
+#if defined(WITH_SELF_VERIFICATION)
+/*
+ * Save PC and registers to shadow memory for self verification mode
+ * before jumping to native translation.
+ * On entry:
+ * rPC, rFP, rGLUE: the values that they should contain
+ * r10: the address of the target translation.
+ */
+jitSVShadowRunStart:
+ mov r0,rPC @ r0<- program counter
+ mov r1,rFP @ r1<- frame pointer
+ mov r2,rGLUE @ r2<- InterpState pointer
+ mov r3,r10 @ r3<- target translation
+ bl dvmSelfVerificationSaveState @ save registers to shadow space
+ ldr rFP,[r0,#offShadowSpace_shadowFP] @ rFP<- fp in shadow space
+ add rGLUE,r0,#offShadowSpace_interpState @ rGLUE<- rGLUE in shadow space
+ bx r10 @ jump to the translation
+
+/*
+ * Restore PC, registers, and interpState to original values
+ * before jumping back to the interpreter.
+ */
+jitSVShadowRunEnd:
+ mov r1,rFP @ pass ending fp
+ bl dvmSelfVerificationRestoreState @ restore pc and fp values
+ ldr rPC,[r0,#offShadowSpace_startPC] @ restore PC
+ ldr rFP,[r0,#offShadowSpace_fp] @ restore FP
+ ldr rGLUE,[r0,#offShadowSpace_glue] @ restore InterpState
+ ldr r1,[r0,#offShadowSpace_svState] @ get self verification state
+ cmp r1,#0 @ check for punt condition
+ beq 1f
+ mov r2,#kJitSelfVerification @ ask for self verification
+ str r2,[rGLUE,#offGlue_jitState]
+ mov r2,#kInterpEntryInstr @ normal entry reason
+ str r2,[rGLUE,#offGlue_entryPoint]
+ mov r1,#1 @ set changeInterp
+ b common_gotoBail
+
+1: @ exit to interpreter without check
+ EXPORT_PC()
+ adrl rIBASE, dvmAsmInstructionStart
+ FETCH_INST()
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+#endif
+
+#endif
+
+/*
+ * Common code when a backward branch is taken.
+ *
+ * TODO: we could avoid a branch by just setting r0 and falling through
+ * into the common_periodicChecks code, and having a test on r0 at the
+ * end determine if we should return to the caller or update & branch to
+ * the next instr.
+ *
+ * On entry:
+ * r9 is PC adjustment *in bytes*
+ */
+common_backwardBranch:
+ mov r0, #kInterpEntryInstr
+ bl common_periodicChecks
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/*
+ * Need to see if the thread needs to be suspended or debugger/profiler
+ * activity has begun. If so, we suspend the thread or side-exit to
+ * the debug interpreter as appropriate.
+ *
+ * The common case is no activity on any of these, so we want to figure
+ * that out quickly. If something is up, we can then sort out what.
+ *
+ * We want to be fast if the VM was built without debugger or profiler
+ * support, but we also need to recognize that the system is usually
+ * shipped with both of these enabled.
+ *
+ * TODO: reduce this so we're just checking a single location.
+ *
+ * On entry:
+ * r0 is reentry type, e.g. kInterpEntryInstr (for debugger/profiling)
+ * r9 is trampoline PC adjustment *in bytes*
+ */
+common_periodicChecks:
+ ldr r3, [rGLUE, #offGlue_pSelfSuspendCount] @ r3<- &suspendCount
+
+ ldr r1, [rGLUE, #offGlue_pDebuggerActive] @ r1<- &debuggerActive
+ ldr r2, [rGLUE, #offGlue_pActiveProfilers] @ r2<- &activeProfilers
+
+ ldr ip, [r3] @ ip<- suspendCount (int)
+
+ cmp r1, #0 @ debugger enabled?
+ ldrneb r1, [r1] @ yes, r1<- debuggerActive (boolean)
+ ldr r2, [r2] @ r2<- activeProfilers (int)
+ orrne ip, ip, r1 @ ip<- suspendCount | debuggerActive
+ orrs ip, ip, r2 @ ip<- suspend|debugger|profiler; set Z
+
+ bxeq lr @ all zero, return
+
+ /*
+ * One or more interesting events have happened. Figure out what.
+ *
+ * If debugging or profiling are compiled in, we need to disambiguate.
+ *
+ * r0 still holds the reentry type.
+ */
+ ldr ip, [r3] @ ip<- suspendCount (int)
+ cmp ip, #0 @ want suspend?
+ beq 1f @ no, must be debugger/profiler
+
+ stmfd sp!, {r0, lr} @ preserve r0 and lr
+#if defined(WITH_JIT)
+ /*
+ * Refresh the Jit's cached copy of profile table pointer. This pointer
+ * doubles as the Jit's on/off switch.
+ */
+ ldr r3, [rGLUE, #offGlue_ppJitProfTable] @ r3<-&gDvmJit.pJitProfTable
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ ldr r3, [r3] @ r3 <- pJitProfTable
+ EXPORT_PC() @ need for precise GC
+ str r3, [rGLUE, #offGlue_pJitProfTable] @ refresh Jit's on/off switch
+#else
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ EXPORT_PC() @ need for precise GC
+#endif
+ bl dvmCheckSuspendPending @ do full check, suspend if necessary
+ ldmfd sp!, {r0, lr} @ restore r0 and lr
+
+ /*
+ * Reload the debugger/profiler enable flags. We're checking to see
+ * if either of these got set while we were suspended.
+ *
+ * We can't really avoid the #ifdefs here, because the fields don't
+ * exist when the feature is disabled.
+ */
+ ldr r1, [rGLUE, #offGlue_pDebuggerActive] @ r1<- &debuggerActive
+ cmp r1, #0 @ debugger enabled?
+ ldrneb r1, [r1] @ yes, r1<- debuggerActive (boolean)
+ ldr r2, [rGLUE, #offGlue_pActiveProfilers] @ r2<- &activeProfilers
+ ldr r2, [r2] @ r2<- activeProfilers (int)
+
+ orrs r1, r1, r2
+ beq 2f
+
+1: @ debugger/profiler enabled, bail out; glue->entryPoint was set above
+ str r0, [rGLUE, #offGlue_entryPoint] @ store r0, need for debug/prof
+ add rPC, rPC, r9 @ update rPC
+ mov r1, #1 @ "want switch" = true
+ b common_gotoBail @ side exit
+
+2:
+ bx lr @ nothing to do, return
+
+
+/*
+ * The equivalent of "goto bail", this calls through the "bail handler".
+ *
+ * State registers will be saved to the "glue" area before bailing.
+ *
+ * On entry:
+ * r1 is "bool changeInterp", indicating if we want to switch to the
+ * other interpreter or just bail all the way out
+ */
+common_gotoBail:
+ SAVE_PC_FP_TO_GLUE() @ export state to "glue"
+ mov r0, rGLUE @ r0<- glue ptr
+ b dvmMterpStdBail @ call(glue, changeInterp)
+
+ @add r1, r1, #1 @ using (boolean+1)
+ @add r0, rGLUE, #offGlue_jmpBuf @ r0<- &glue->jmpBuf
+ @bl _longjmp @ does not return
+ @bl common_abort
+
+
+/*
+ * Common code for method invocation with range.
+ *
+ * On entry:
+ * r0 is "Method* methodToCall", the method we're trying to call
+ */
+common_invokeMethodRange:
+.LinvokeNewRange:
+ @ prepare to copy args to "outs" area of current frame
+ movs r2, rINST, lsr #8 @ r2<- AA (arg count) -- test for zero
+ SAVEAREA_FROM_FP(r10, rFP) @ r10<- stack save area
+ beq .LinvokeArgsDone @ if no args, skip the rest
+ FETCH(r1, 2) @ r1<- CCCC
+
+ @ r0=methodToCall, r1=CCCC, r2=count, r10=outs
+ @ (very few methods have > 10 args; could unroll for common cases)
+ add r3, rFP, r1, lsl #2 @ r3<- &fp[CCCC]
+ sub r10, r10, r2, lsl #2 @ r10<- "outs" area, for call args
+ ldrh r9, [r0, #offMethod_registersSize] @ r9<- methodToCall->regsSize
+1: ldr r1, [r3], #4 @ val = *fp++
+ subs r2, r2, #1 @ count--
+ str r1, [r10], #4 @ *outs++ = val
+ bne 1b @ ...while count != 0
+ ldrh r3, [r0, #offMethod_outsSize] @ r3<- methodToCall->outsSize
+ b .LinvokeArgsDone
+
+/*
+ * Common code for method invocation without range.
+ *
+ * On entry:
+ * r0 is "Method* methodToCall", the method we're trying to call
+ */
+common_invokeMethodNoRange:
+.LinvokeNewNoRange:
+ @ prepare to copy args to "outs" area of current frame
+ movs r2, rINST, lsr #12 @ r2<- B (arg count) -- test for zero
+ SAVEAREA_FROM_FP(r10, rFP) @ r10<- stack save area
+ FETCH(r1, 2) @ r1<- GFED (load here to hide latency)
+ ldrh r9, [r0, #offMethod_registersSize] @ r9<- methodToCall->regsSize
+ ldrh r3, [r0, #offMethod_outsSize] @ r3<- methodToCall->outsSize
+ beq .LinvokeArgsDone
+
+ @ r0=methodToCall, r1=GFED, r3=outSize, r2=count, r9=regSize, r10=outs
+.LinvokeNonRange:
+ rsb r2, r2, #5 @ r2<- 5-r2
+ add pc, pc, r2, lsl #4 @ computed goto, 4 instrs each
+ bl common_abort @ (skipped due to ARM prefetch)
+5: and ip, rINST, #0x0f00 @ isolate A
+ ldr r2, [rFP, ip, lsr #6] @ r2<- vA (shift right 8, left 2)
+ mov r0, r0 @ nop
+ str r2, [r10, #-4]! @ *--outs = vA
+4: and ip, r1, #0xf000 @ isolate G
+ ldr r2, [rFP, ip, lsr #10] @ r2<- vG (shift right 12, left 2)
+ mov r0, r0 @ nop
+ str r2, [r10, #-4]! @ *--outs = vG
+3: and ip, r1, #0x0f00 @ isolate F
+ ldr r2, [rFP, ip, lsr #6] @ r2<- vF
+ mov r0, r0 @ nop
+ str r2, [r10, #-4]! @ *--outs = vF
+2: and ip, r1, #0x00f0 @ isolate E
+ ldr r2, [rFP, ip, lsr #2] @ r2<- vE
+ mov r0, r0 @ nop
+ str r2, [r10, #-4]! @ *--outs = vE
+1: and ip, r1, #0x000f @ isolate D
+ ldr r2, [rFP, ip, lsl #2] @ r2<- vD
+ mov r0, r0 @ nop
+ str r2, [r10, #-4]! @ *--outs = vD
+0: @ fall through to .LinvokeArgsDone
+
+.LinvokeArgsDone: @ r0=methodToCall, r3=outSize, r9=regSize
+ ldr r2, [r0, #offMethod_insns] @ r2<- method->insns
+ ldr rINST, [r0, #offMethod_clazz] @ rINST<- method->clazz
+ @ find space for the new stack frame, check for overflow
+ SAVEAREA_FROM_FP(r1, rFP) @ r1<- stack save area
+ sub r1, r1, r9, lsl #2 @ r1<- newFp (old savearea - regsSize)
+ SAVEAREA_FROM_FP(r10, r1) @ r10<- newSaveArea
+@ bl common_dumpRegs
+ ldr r9, [rGLUE, #offGlue_interpStackEnd] @ r9<- interpStackEnd
+ sub r3, r10, r3, lsl #2 @ r3<- bottom (newsave - outsSize)
+ cmp r3, r9 @ bottom < interpStackEnd?
+ ldr r3, [r0, #offMethod_accessFlags] @ r3<- methodToCall->accessFlags
+ blo .LstackOverflow @ yes, this frame will overflow stack
+
+ @ set up newSaveArea
+#ifdef EASY_GDB
+ SAVEAREA_FROM_FP(ip, rFP) @ ip<- stack save area
+ str ip, [r10, #offStackSaveArea_prevSave]
+#endif
+ str rFP, [r10, #offStackSaveArea_prevFrame]
+ str rPC, [r10, #offStackSaveArea_savedPc]
+#if defined(WITH_JIT)
+ mov r9, #0
+ str r9, [r10, #offStackSaveArea_returnAddr]
+#endif
+ str r0, [r10, #offStackSaveArea_method]
+ tst r3, #ACC_NATIVE
+ bne .LinvokeNative
+
+ /*
+ stmfd sp!, {r0-r3}
+ bl common_printNewline
+ mov r0, rFP
+ mov r1, #0
+ bl dvmDumpFp
+ ldmfd sp!, {r0-r3}
+ stmfd sp!, {r0-r3}
+ mov r0, r1
+ mov r1, r10
+ bl dvmDumpFp
+ bl common_printNewline
+ ldmfd sp!, {r0-r3}
+ */
+
+ ldrh r9, [r2] @ r9 <- load INST from new PC
+ ldr r3, [rINST, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+ mov rPC, r2 @ publish new rPC
+ ldr r2, [rGLUE, #offGlue_self] @ r2<- glue->self
+
+ @ Update "glue" values for the new method
+ @ r0=methodToCall, r1=newFp, r2=self, r3=newMethodClass, r9=newINST
+ str r0, [rGLUE, #offGlue_method] @ glue->method = methodToCall
+ str r3, [rGLUE, #offGlue_methodClassDex] @ glue->methodClassDex = ...
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ mov rFP, r1 @ fp = newFp
+ GET_PREFETCHED_OPCODE(ip, r9) @ extract prefetched opcode from r9
+ mov rINST, r9 @ publish new rINST
+ str r1, [r2, #offThread_curFrame] @ self->curFrame = newFp
+ cmp r0,#0
+ bne common_updateProfile
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ mov rFP, r1 @ fp = newFp
+ GET_PREFETCHED_OPCODE(ip, r9) @ extract prefetched opcode from r9
+ mov rINST, r9 @ publish new rINST
+ str r1, [r2, #offThread_curFrame] @ self->curFrame = newFp
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+.LinvokeNative:
+ @ Prep for the native call
+ @ r0=methodToCall, r1=newFp, r10=newSaveArea
+ ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
+ ldr r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
+ str r1, [r3, #offThread_curFrame] @ self->curFrame = newFp
+ str r9, [r10, #offStackSaveArea_localRefCookie] @newFp->localRefCookie=top
+ mov r9, r3 @ r9<- glue->self (preserve)
+
+ mov r2, r0 @ r2<- methodToCall
+ mov r0, r1 @ r0<- newFp (points to args)
+ add r1, rGLUE, #offGlue_retval @ r1<- &retval
+
+#ifdef ASSIST_DEBUGGER
+ /* insert fake function header to help gdb find the stack frame */
+ b .Lskip
+ .type dalvik_mterp, %function
+dalvik_mterp:
+ .fnstart
+ MTERP_ENTRY1
+ MTERP_ENTRY2
+.Lskip:
+#endif
+
+ @mov lr, pc @ set return addr
+ @ldr pc, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
+ LDR_PC_LR "[r2, #offMethod_nativeFunc]"
+
+#if defined(WITH_JIT)
+ ldr r3, [rGLUE, #offGlue_ppJitProfTable] @ Refresh Jit's on/off status
+#endif
+
+ @ native return; r9=self, r10=newSaveArea
+ @ equivalent to dvmPopJniLocals
+ ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved top
+ ldr r1, [r9, #offThread_exception] @ check for exception
+#if defined(WITH_JIT)
+ ldr r3, [r3] @ r3 <- gDvmJit.pProfTable
+#endif
+ str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
+ cmp r1, #0 @ null?
+ str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
+#if defined(WITH_JIT)
+ str r3, [rGLUE, #offGlue_pJitProfTable] @ refresh cached on/off switch
+#endif
+ bne common_exceptionThrown @ no, handle exception
+
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+.LstackOverflow: @ r0=methodToCall
+ mov r1, r0 @ r1<- methodToCall
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- self
+ bl dvmHandleStackOverflow
+ b common_exceptionThrown
+#ifdef ASSIST_DEBUGGER
+ .fnend
+#endif
+
+
+ /*
+ * Common code for method invocation, calling through "glue code".
+ *
+ * TODO: now that we have range and non-range invoke handlers, this
+ * needs to be split into two. Maybe just create entry points
+ * that set r9 and jump here?
+ *
+ * On entry:
+ * r0 is "Method* methodToCall", the method we're trying to call
+ * r9 is "bool methodCallRange", indicating if this is a /range variant
+ */
+ .if 0
+.LinvokeOld:
+ sub sp, sp, #8 @ space for args + pad
+ FETCH(ip, 2) @ ip<- FEDC or CCCC
+ mov r2, r0 @ A2<- methodToCall
+ mov r0, rGLUE @ A0<- glue
+ SAVE_PC_FP_TO_GLUE() @ export state to "glue"
+ mov r1, r9 @ A1<- methodCallRange
+ mov r3, rINST, lsr #8 @ A3<- AA
+ str ip, [sp, #0] @ A4<- ip
+ bl dvmMterp_invokeMethod @ call the C invokeMethod
+ add sp, sp, #8 @ remove arg area
+ b common_resumeAfterGlueCall @ continue to next instruction
+ .endif
+
+
+
+/*
+ * Common code for handling a return instruction.
+ *
+ * This does not return.
+ */
+common_returnFromMethod:
+.LreturnNew:
+ mov r0, #kInterpEntryReturn
+ mov r9, #0
+ bl common_periodicChecks
+
+ SAVEAREA_FROM_FP(r0, rFP) @ r0<- saveArea (old)
+ ldr rFP, [r0, #offStackSaveArea_prevFrame] @ fp = saveArea->prevFrame
+ ldr r9, [r0, #offStackSaveArea_savedPc] @ r9 = saveArea->savedPc
+ ldr r2, [rFP, #(offStackSaveArea_method - sizeofStackSaveArea)]
+ @ r2<- method we're returning to
+ ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
+ cmp r2, #0 @ is this a break frame?
+ ldrne r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+ mov r1, #0 @ "want switch" = false
+ beq common_gotoBail @ break frame, bail out completely
+
+ PREFETCH_ADVANCE_INST(rINST, r9, 3) @ advance r9, update new rINST
+ str r2, [rGLUE, #offGlue_method]@ glue->method = newSave->method
+ ldr r1, [r10, #offClassObject_pDvmDex] @ r1<- method->clazz->pDvmDex
+ str rFP, [r3, #offThread_curFrame] @ self->curFrame = fp
+#if defined(WITH_JIT)
+ ldr r10, [r0, #offStackSaveArea_returnAddr] @ r10 = saveArea->returnAddr
+ mov rPC, r9 @ publish new rPC
+ str r1, [rGLUE, #offGlue_methodClassDex]
+ str r10, [r3, #offThread_inJitCodeCache] @ may return to JIT'ed land
+ cmp r10, #0 @ caller is compiled code
+ blxne r10
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ mov rPC, r9 @ publish new rPC
+ str r1, [rGLUE, #offGlue_methodClassDex]
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+ /*
+ * Return handling, calls through "glue code".
+ */
+ .if 0
+.LreturnOld:
+ SAVE_PC_FP_TO_GLUE() @ export state
+ mov r0, rGLUE @ arg to function
+ bl dvmMterp_returnFromMethod
+ b common_resumeAfterGlueCall
+ .endif
+
+
+/*
+ * Somebody has thrown an exception. Handle it.
+ *
+ * If the exception processing code returns to us (instead of falling
+ * out of the interpreter), continue with whatever the next instruction
+ * now happens to be.
+ *
+ * This does not return.
+ */
+ .global dvmMterpCommonExceptionThrown
+dvmMterpCommonExceptionThrown:
+common_exceptionThrown:
+.LexceptionNew:
+ mov r0, #kInterpEntryThrow
+ mov r9, #0
+ bl common_periodicChecks
+
+ ldr r10, [rGLUE, #offGlue_self] @ r10<- glue->self
+ ldr r9, [r10, #offThread_exception] @ r9<- self->exception
+ mov r1, r10 @ r1<- self
+ mov r0, r9 @ r0<- exception
+ bl dvmAddTrackedAlloc @ don't let the exception be GCed
+ mov r3, #0 @ r3<- NULL
+ str r3, [r10, #offThread_exception] @ self->exception = NULL
+
+ /* set up args and a local for "&fp" */
+ /* (str sp, [sp, #-4]! would be perfect here, but is discouraged) */
+ str rFP, [sp, #-4]! @ *--sp = fp
+ mov ip, sp @ ip<- &fp
+ mov r3, #0 @ r3<- false
+ str ip, [sp, #-4]! @ *--sp = &fp
+ ldr r1, [rGLUE, #offGlue_method] @ r1<- glue->method
+ mov r0, r10 @ r0<- self
+ ldr r1, [r1, #offMethod_insns] @ r1<- method->insns
+ mov r2, r9 @ r2<- exception
+ sub r1, rPC, r1 @ r1<- pc - method->insns
+ mov r1, r1, asr #1 @ r1<- offset in code units
+
+ /* call, r0 gets catchRelPc (a code-unit offset) */
+ bl dvmFindCatchBlock @ call(self, relPc, exc, scan?, &fp)
+
+ /* fix earlier stack overflow if necessary; may trash rFP */
+ ldrb r1, [r10, #offThread_stackOverflowed]
+ cmp r1, #0 @ did we overflow earlier?
+ beq 1f @ no, skip ahead
+ mov rFP, r0 @ save relPc result in rFP
+ mov r0, r10 @ r0<- self
+ mov r1, r9 @ r1<- exception
+ bl dvmCleanupStackOverflow @ call(self)
+ mov r0, rFP @ restore result
+1:
+
+ /* update frame pointer and check result from dvmFindCatchBlock */
+ ldr rFP, [sp, #4] @ retrieve the updated rFP
+ cmp r0, #0 @ is catchRelPc < 0?
+ add sp, sp, #8 @ restore stack
+ bmi .LnotCaughtLocally
+
+ /* adjust locals to match self->curFrame and updated PC */
+ SAVEAREA_FROM_FP(r1, rFP) @ r1<- new save area
+ ldr r1, [r1, #offStackSaveArea_method] @ r1<- new method
+ str r1, [rGLUE, #offGlue_method] @ glue->method = new method
+ ldr r2, [r1, #offMethod_clazz] @ r2<- method->clazz
+ ldr r3, [r1, #offMethod_insns] @ r3<- method->insns
+ ldr r2, [r2, #offClassObject_pDvmDex] @ r2<- method->clazz->pDvmDex
+ add rPC, r3, r0, asl #1 @ rPC<- method->insns + catchRelPc
+ str r2, [rGLUE, #offGlue_methodClassDex] @ glue->pDvmDex = meth...
+
+ /* release the tracked alloc on the exception */
+ mov r0, r9 @ r0<- exception
+ mov r1, r10 @ r1<- self
+ bl dvmReleaseTrackedAlloc @ release the exception
+
+ /* restore the exception if the handler wants it */
+ FETCH_INST() @ load rINST from rPC
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ cmp ip, #OP_MOVE_EXCEPTION @ is it "move-exception"?
+ streq r9, [r10, #offThread_exception] @ yes, restore the exception
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+.LnotCaughtLocally: @ r9=exception, r10=self
+ /* fix stack overflow if necessary */
+ ldrb r1, [r10, #offThread_stackOverflowed]
+ cmp r1, #0 @ did we overflow earlier?
+ movne r0, r10 @ if yes: r0<- self
+ movne r1, r9 @ if yes: r1<- exception
+ blne dvmCleanupStackOverflow @ if yes: call(self)
+
+ @ may want to show "not caught locally" debug messages here
+#if DVM_SHOW_EXCEPTION >= 2
+ /* call __android_log_print(prio, tag, format, ...) */
+ /* "Exception %s from %s:%d not caught locally" */
+ @ dvmLineNumFromPC(method, pc - method->insns)
+ ldr r0, [rGLUE, #offGlue_method]
+ ldr r1, [r0, #offMethod_insns]
+ sub r1, rPC, r1
+ asr r1, r1, #1
+ bl dvmLineNumFromPC
+ str r0, [sp, #-4]!
+ @ dvmGetMethodSourceFile(method)
+ ldr r0, [rGLUE, #offGlue_method]
+ bl dvmGetMethodSourceFile
+ str r0, [sp, #-4]!
+ @ exception->clazz->descriptor
+ ldr r3, [r9, #offObject_clazz]
+ ldr r3, [r3, #offClassObject_descriptor]
+ @
+ ldr r2, strExceptionNotCaughtLocally
+ ldr r1, strLogTag
+ mov r0, #3 @ LOG_DEBUG
+ bl __android_log_print
+#endif
+ str r9, [r10, #offThread_exception] @ restore exception
+ mov r0, r9 @ r0<- exception
+ mov r1, r10 @ r1<- self
+ bl dvmReleaseTrackedAlloc @ release the exception
+ mov r1, #0 @ "want switch" = false
+ b common_gotoBail @ bail out
+
+
+ /*
+ * Exception handling, calls through "glue code".
+ */
+ .if 0
+.LexceptionOld:
+ SAVE_PC_FP_TO_GLUE() @ export state
+ mov r0, rGLUE @ arg to function
+ bl dvmMterp_exceptionThrown
+ b common_resumeAfterGlueCall
+ .endif
+
+
+/*
+ * After returning from a "glued" function, pull out the updated
+ * values and start executing at the next instruction.
+ */
+common_resumeAfterGlueCall:
+ LOAD_PC_FP_FROM_GLUE() @ pull rPC and rFP out of glue
+ FETCH_INST() @ load rINST from rPC
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/*
+ * Invalid array index.
+ */
+common_errArrayIndex:
+ EXPORT_PC()
+ ldr r0, strArrayIndexException
+ mov r1, #0
+ bl dvmThrowException
+ b common_exceptionThrown
+
+/*
+ * Invalid array value.
+ */
+common_errArrayStore:
+ EXPORT_PC()
+ ldr r0, strArrayStoreException
+ mov r1, #0
+ bl dvmThrowException
+ b common_exceptionThrown
+
+/*
+ * Integer divide or mod by zero.
+ */
+common_errDivideByZero:
+ EXPORT_PC()
+ ldr r0, strArithmeticException
+ ldr r1, strDivideByZero
+ bl dvmThrowException
+ b common_exceptionThrown
+
+/*
+ * Attempt to allocate an array with a negative size.
+ */
+common_errNegativeArraySize:
+ EXPORT_PC()
+ ldr r0, strNegativeArraySizeException
+ mov r1, #0
+ bl dvmThrowException
+ b common_exceptionThrown
+
+/*
+ * Invocation of a non-existent method.
+ */
+common_errNoSuchMethod:
+ EXPORT_PC()
+ ldr r0, strNoSuchMethodError
+ mov r1, #0
+ bl dvmThrowException
+ b common_exceptionThrown
+
+/*
+ * We encountered a null object when we weren't expecting one. We
+ * export the PC, throw a NullPointerException, and goto the exception
+ * processing code.
+ */
+common_errNullObject:
+ EXPORT_PC()
+ ldr r0, strNullPointerException
+ mov r1, #0
+ bl dvmThrowException
+ b common_exceptionThrown
+
+/*
+ * For debugging, cause an immediate fault. The source address will
+ * be in lr (use a bl instruction to jump here).
+ */
+common_abort:
+ ldr pc, .LdeadFood
+.LdeadFood:
+ .word 0xdeadf00d
+
+/*
+ * Spit out a "we were here", preserving all registers. (The attempt
+ * to save ip won't work, but we need to save an even number of
+ * registers for EABI 64-bit stack alignment.)
+ */
+ .macro SQUEAK num
+common_squeak\num:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ ldr r0, strSqueak
+ mov r1, #\num
+ bl printf
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+ .endm
+
+ SQUEAK 0
+ SQUEAK 1
+ SQUEAK 2
+ SQUEAK 3
+ SQUEAK 4
+ SQUEAK 5
+
+/*
+ * Spit out the number in r0, preserving registers.
+ */
+common_printNum:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ mov r1, r0
+ ldr r0, strSqueak
+ bl printf
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+
+/*
+ * Print a newline, preserving registers.
+ */
+common_printNewline:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ ldr r0, strNewline
+ bl printf
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+
+ /*
+ * Print the 32-bit quantity in r0 as a hex value, preserving registers.
+ */
+common_printHex:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ mov r1, r0
+ ldr r0, strPrintHex
+ bl printf
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+
+/*
+ * Print the 64-bit quantity in r0-r1, preserving registers.
+ */
+common_printLong:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ mov r3, r1
+ mov r2, r0
+ ldr r0, strPrintLong
+ bl printf
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+
+/*
+ * Print full method info. Pass the Method* in r0. Preserves regs.
+ */
+common_printMethod:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bl dvmMterpPrintMethod
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+
+/*
+ * Call a C helper function that dumps regs and possibly some
+ * additional info. Requires the C function to be compiled in.
+ */
+ .if 0
+common_dumpRegs:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bl dvmMterpDumpArmRegs
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+ .endif
+
+#if 0
+/*
+ * Experiment on VFP mode.
+ *
+ * uint32_t setFPSCR(uint32_t val, uint32_t mask)
+ *
+ * Updates the bits specified by "mask", setting them to the values in "val".
+ */
+setFPSCR:
+ and r0, r0, r1 @ make sure no stray bits are set
+ fmrx r2, fpscr @ get VFP reg
+ mvn r1, r1 @ bit-invert mask
+ and r2, r2, r1 @ clear masked bits
+ orr r2, r2, r0 @ set specified bits
+ fmxr fpscr, r2 @ set VFP reg
+ mov r0, r2 @ return new value
+ bx lr
+
+ .align 2
+ .global dvmConfigureFP
+ .type dvmConfigureFP, %function
+dvmConfigureFP:
+ stmfd sp!, {ip, lr}
+ /* 0x03000000 sets DN/FZ */
+ /* 0x00009f00 clears the six exception enable flags */
+ bl common_squeak0
+ mov r0, #0x03000000 @ r0<- 0x03000000
+ add r1, r0, #0x9f00 @ r1<- 0x03009f00
+ bl setFPSCR
+ ldmfd sp!, {ip, pc}
+#endif
+
+
+/*
+ * String references, must be close to the code that uses them.
+ */
+ .align 2
+strArithmeticException:
+ .word .LstrArithmeticException
+strArrayIndexException:
+ .word .LstrArrayIndexException
+strArrayStoreException:
+ .word .LstrArrayStoreException
+strDivideByZero:
+ .word .LstrDivideByZero
+strNegativeArraySizeException:
+ .word .LstrNegativeArraySizeException
+strNoSuchMethodError:
+ .word .LstrNoSuchMethodError
+strNullPointerException:
+ .word .LstrNullPointerException
+
+strLogTag:
+ .word .LstrLogTag
+strExceptionNotCaughtLocally:
+ .word .LstrExceptionNotCaughtLocally
+
+strNewline:
+ .word .LstrNewline
+strSqueak:
+ .word .LstrSqueak
+strPrintHex:
+ .word .LstrPrintHex
+strPrintLong:
+ .word .LstrPrintLong
+
+/*
+ * Zero-terminated ASCII string data.
+ *
+ * On ARM we have two choices: do like gcc does, and LDR from a .word
+ * with the address, or use an ADR pseudo-op to get the address
+ * directly. ADR saves 4 bytes and an indirection, but it's using a
+ * PC-relative addressing mode and hence has a limited range, which
+ * makes it not work well with mergeable string sections.
+ */
+ .section .rodata.str1.4,"aMS",%progbits,1
+
+.LstrBadEntryPoint:
+ .asciz "Bad entry point %d\n"
+.LstrArithmeticException:
+ .asciz "Ljava/lang/ArithmeticException;"
+.LstrArrayIndexException:
+ .asciz "Ljava/lang/ArrayIndexOutOfBoundsException;"
+.LstrArrayStoreException:
+ .asciz "Ljava/lang/ArrayStoreException;"
+.LstrClassCastException:
+ .asciz "Ljava/lang/ClassCastException;"
+.LstrDivideByZero:
+ .asciz "divide by zero"
+.LstrFilledNewArrayNotImpl:
+ .asciz "filled-new-array only implemented for objects and 'int'"
+.LstrInternalError:
+ .asciz "Ljava/lang/InternalError;"
+.LstrInstantiationError:
+ .asciz "Ljava/lang/InstantiationError;"
+.LstrNegativeArraySizeException:
+ .asciz "Ljava/lang/NegativeArraySizeException;"
+.LstrNoSuchMethodError:
+ .asciz "Ljava/lang/NoSuchMethodError;"
+.LstrNullPointerException:
+ .asciz "Ljava/lang/NullPointerException;"
+
+.LstrLogTag:
+ .asciz "mterp"
+.LstrExceptionNotCaughtLocally:
+ .asciz "Exception %s from %s:%d not caught locally\n"
+
+.LstrNewline:
+ .asciz "\n"
+.LstrSqueak:
+ .asciz "<%d>"
+.LstrPrintHex:
+ .asciz "<0x%x>"
+.LstrPrintLong:
+ .asciz "<%lld>"
+
diff --git a/vm/mterp/out/InterpAsm-armv7-a-neon.S b/vm/mterp/out/InterpAsm-armv7-a-neon.S
new file mode 100644
index 0000000..3ffd35d
--- /dev/null
+++ b/vm/mterp/out/InterpAsm-armv7-a-neon.S
@@ -0,0 +1,10549 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'armv7-a-neon'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: armv5te/header.S */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * ARMv5 definitions and declarations.
+ */
+
+/*
+ARM EABI general notes:
+
+r0-r3 hold first 4 args to a method; they are not preserved across method calls
+r4-r8 are available for general use
+r9 is given special treatment in some situations, but not for us
+r10 (sl) seems to be generally available
+r11 (fp) is used by gcc (unless -fomit-frame-pointer is set)
+r12 (ip) is scratch -- not preserved across method calls
+r13 (sp) should be managed carefully in case a signal arrives
+r14 (lr) must be preserved
+r15 (pc) can be tinkered with directly
+
+r0 holds returns of <= 4 bytes
+r0-r1 hold returns of 8 bytes, low word in r0
+
+Callee must save/restore r4+ (except r12) if it modifies them. If VFP
+is present, registers s16-s31 (a/k/a d8-d15, a/k/a q4-q7) must be preserved,
+s0-s15 (d0-d7, q0-a3) do not need to be.
+
+Stack is "full descending". Only the arguments that don't fit in the first 4
+registers are placed on the stack. "sp" points at the first stacked argument
+(i.e. the 5th arg).
+
+VFP: single-precision results in s0, double-precision results in d0.
+
+In the EABI, "sp" must be 64-bit aligned on entry to a function, and any
+64-bit quantities (long long, double) must be 64-bit aligned.
+*/
+
+/*
+Mterp and ARM notes:
+
+The following registers have fixed assignments:
+
+ reg nick purpose
+ r4 rPC interpreted program counter, used for fetching instructions
+ r5 rFP interpreted frame pointer, used for accessing locals and args
+ r6 rGLUE MterpGlue pointer
+ r7 rINST first 16-bit code unit of current instruction
+ r8 rIBASE interpreted instruction base pointer, used for computed goto
+
+Macros are provided for common operations. Each macro MUST emit only
+one instruction to make instruction-counting easier. They MUST NOT alter
+unspecified registers or condition codes.
+*/
+
+/* single-purpose registers, given names for clarity */
+#define rPC r4
+#define rFP r5
+#define rGLUE r6
+#define rINST r7
+#define rIBASE r8
+
+/* save/restore the PC and/or FP from the glue struct */
+#define LOAD_PC_FROM_GLUE() ldr rPC, [rGLUE, #offGlue_pc]
+#define SAVE_PC_TO_GLUE() str rPC, [rGLUE, #offGlue_pc]
+#define LOAD_FP_FROM_GLUE() ldr rFP, [rGLUE, #offGlue_fp]
+#define SAVE_FP_TO_GLUE() str rFP, [rGLUE, #offGlue_fp]
+#define LOAD_PC_FP_FROM_GLUE() ldmia rGLUE, {rPC, rFP}
+#define SAVE_PC_FP_TO_GLUE() stmia rGLUE, {rPC, rFP}
+
+/*
+ * "export" the PC to the stack frame, f/b/o future exception objects. Must
+ * be done *before* something calls dvmThrowException.
+ *
+ * In C this is "SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc", i.e.
+ * fp - sizeof(StackSaveArea) + offsetof(SaveArea, xtra.currentPc)
+ *
+ * It's okay to do this more than once.
+ */
+#define EXPORT_PC() \
+ str rPC, [rFP, #(-sizeofStackSaveArea + offStackSaveArea_currentPc)]
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+#define SAVEAREA_FROM_FP(_reg, _fpreg) \
+ sub _reg, _fpreg, #sizeofStackSaveArea
+
+/*
+ * Fetch the next instruction from rPC into rINST. Does not advance rPC.
+ */
+#define FETCH_INST() ldrh rINST, [rPC]
+
+/*
+ * Fetch the next instruction from the specified offset. Advances rPC
+ * to point to the next instruction. "_count" is in 16-bit code units.
+ *
+ * Because of the limited size of immediate constants on ARM, this is only
+ * suitable for small forward movements (i.e. don't try to implement "goto"
+ * with this).
+ *
+ * This must come AFTER anything that can throw an exception, or the
+ * exception catch may miss. (This also implies that it must come after
+ * EXPORT_PC().)
+ */
+#define FETCH_ADVANCE_INST(_count) ldrh rINST, [rPC, #(_count*2)]!
+
+/*
+ * The operation performed here is similar to FETCH_ADVANCE_INST, except the
+ * src and dest registers are parameterized (not hard-wired to rPC and rINST).
+ */
+#define PREFETCH_ADVANCE_INST(_dreg, _sreg, _count) \
+ ldrh _dreg, [_sreg, #(_count*2)]!
+
+/*
+ * Fetch the next instruction from an offset specified by _reg. Updates
+ * rPC to point to the next instruction. "_reg" must specify the distance
+ * in bytes, *not* 16-bit code units, and may be a signed value.
+ *
+ * We want to write "ldrh rINST, [rPC, _reg, lsl #2]!", but some of the
+ * bits that hold the shift distance are used for the half/byte/sign flags.
+ * In some cases we can pre-double _reg for free, so we require a byte offset
+ * here.
+ */
+#define FETCH_ADVANCE_INST_RB(_reg) ldrh rINST, [rPC, _reg]!
+
+/*
+ * Fetch a half-word code unit from an offset past the current PC. The
+ * "_count" value is in 16-bit code units. Does not advance rPC.
+ *
+ * The "_S" variant works the same but treats the value as signed.
+ */
+#define FETCH(_reg, _count) ldrh _reg, [rPC, #(_count*2)]
+#define FETCH_S(_reg, _count) ldrsh _reg, [rPC, #(_count*2)]
+
+/*
+ * Fetch one byte from an offset past the current PC. Pass in the same
+ * "_count" as you would for FETCH, and an additional 0/1 indicating which
+ * byte of the halfword you want (lo/hi).
+ */
+#define FETCH_B(_reg, _count, _byte) ldrb _reg, [rPC, #(_count*2+_byte)]
+
+/*
+ * Put the instruction's opcode field into the specified register.
+ */
+#define GET_INST_OPCODE(_reg) and _reg, rINST, #255
+
+/*
+ * Put the prefetched instruction's opcode field into the specified register.
+ */
+#define GET_PREFETCHED_OPCODE(_oreg, _ireg) and _oreg, _ireg, #255
+
+/*
+ * Begin executing the opcode in _reg. Because this only jumps within the
+ * interpreter, we don't have to worry about pre-ARMv5 THUMB interwork.
+ */
+#define GOTO_OPCODE(_reg) add pc, rIBASE, _reg, lsl #6
+#define GOTO_OPCODE_IFEQ(_reg) addeq pc, rIBASE, _reg, lsl #6
+#define GOTO_OPCODE_IFNE(_reg) addne pc, rIBASE, _reg, lsl #6
+
+/*
+ * Get/set the 32-bit value from a Dalvik register.
+ */
+#define GET_VREG(_reg, _vreg) ldr _reg, [rFP, _vreg, lsl #2]
+#define SET_VREG(_reg, _vreg) str _reg, [rFP, _vreg, lsl #2]
+
+#if defined(WITH_JIT)
+#define GET_JIT_PROF_TABLE(_reg) ldr _reg,[rGLUE,#offGlue_pJitProfTable]
+#define GET_JIT_THRESHOLD(_reg) ldr _reg,[rGLUE,#offGlue_jitThreshold]
+#endif
+
+/*
+ * Convert a virtual register index into an address.
+ */
+#define VREG_INDEX_TO_ADDR(_reg, _vreg) \
+ add _reg, rFP, _vreg, lsl #2
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../common/asm-constants.h"
+
+#if defined(WITH_JIT)
+#include "../common/jit-config.h"
+#endif
+
+/* File: armv7-a/platform.S */
+/*
+ * ===========================================================================
+ * CPU-version-specific defines
+ * ===========================================================================
+ */
+
+/*
+ * Macro for "LDR PC,xxx", which is not allowed pre-ARMv5. Essentially a
+ * one-way branch.
+ *
+ * May modify IP. Does not modify LR.
+ */
+.macro LDR_PC source
+ ldr pc, \source
+.endm
+
+/*
+ * Macro for "MOV LR,PC / LDR PC,xxx", which is not allowed pre-ARMv5.
+ * Jump to subroutine.
+ *
+ * May modify IP and LR.
+ */
+.macro LDR_PC_LR source
+ mov lr, pc
+ ldr pc, \source
+.endm
+
+/*
+ * Macro for "LDMFD SP!, {...regs...,PC}".
+ *
+ * May modify IP and LR.
+ */
+.macro LDMFD_PC regs
+ ldmfd sp!, {\regs,pc}
+.endm
+
+#if !defined(ANDROID_SMP)
+# error "Must define ANDROID_SMP"
+#endif
+
+/*
+ * Macro for data memory barrier; not meaningful pre-ARMv6K.
+ * If the argument is nonzero, emit barrier; otherwise, emit nothing.
+ */
+.macro SMP_DMB
+#if ANDROID_SMP != 0
+ dmb
+#else
+ /* not SMP */
+#endif
+.endm
+
+/* File: armv5te/entry.S */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Interpreter entry point.
+ */
+
+/*
+ * We don't have formal stack frames, so gdb scans upward in the code
+ * to find the start of the function (a label with the %function type),
+ * and then looks at the next few instructions to figure out what
+ * got pushed onto the stack. From this it figures out how to restore
+ * the registers, including PC, for the previous stack frame. If gdb
+ * sees a non-function label, it stops scanning, so either we need to
+ * have nothing but assembler-local labels between the entry point and
+ * the break, or we need to fake it out.
+ *
+ * When this is defined, we add some stuff to make gdb less confused.
+ */
+#define ASSIST_DEBUGGER 1
+
+ .text
+ .align 2
+ .global dvmMterpStdRun
+ .type dvmMterpStdRun, %function
+
+/*
+ * On entry:
+ * r0 MterpGlue* glue
+ *
+ * This function returns a boolean "changeInterp" value. The return comes
+ * via a call to dvmMterpStdBail().
+ */
+dvmMterpStdRun:
+#define MTERP_ENTRY1 \
+ .save {r4-r10,fp,lr}; \
+ stmfd sp!, {r4-r10,fp,lr} @ save 9 regs
+#define MTERP_ENTRY2 \
+ .pad #4; \
+ sub sp, sp, #4 @ align 64
+
+ .fnstart
+ MTERP_ENTRY1
+ MTERP_ENTRY2
+
+ /* save stack pointer, add magic word for debuggerd */
+ str sp, [r0, #offGlue_bailPtr] @ save SP for eventual return
+
+ /* set up "named" registers, figure out entry point */
+ mov rGLUE, r0 @ set rGLUE
+ ldr r1, [r0, #offGlue_entryPoint] @ enum is 4 bytes in aapcs-EABI
+ LOAD_PC_FP_FROM_GLUE() @ load rPC and rFP from "glue"
+ adr rIBASE, dvmAsmInstructionStart @ set rIBASE
+ cmp r1, #kInterpEntryInstr @ usual case?
+ bne .Lnot_instr @ no, handle it
+
+#if defined(WITH_JIT)
+.LentryInstr:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ /* Entry is always a possible trace start */
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_INST()
+ mov r1, #0 @ prepare the value for the new state
+ str r1, [r10, #offThread_inJitCodeCache] @ back to the interp land
+ cmp r0,#0 @ is profiling disabled?
+#if !defined(WITH_SELF_VERIFICATION)
+ bne common_updateProfile @ profiling is enabled
+#else
+ ldr r2, [r10, #offThread_shadowSpace] @ to find out the jit exit state
+ beq 1f @ profiling is disabled
+ ldr r3, [r2, #offShadowSpace_jitExitState] @ jit exit state
+ cmp r3, #kSVSTraceSelect @ hot trace following?
+ moveq r2,#kJitTSelectRequestHot @ ask for trace selection
+ beq common_selectTrace @ go build the trace
+ cmp r3, #kSVSNoProfile @ don't profile the next instruction?
+ beq 1f @ intrepret the next instruction
+ b common_updateProfile @ collect profiles
+#endif
+1:
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+#else
+ /* start executing the instruction at rPC */
+ FETCH_INST() @ load rINST from rPC
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+.Lnot_instr:
+ cmp r1, #kInterpEntryReturn @ were we returning from a method?
+ beq common_returnFromMethod
+
+.Lnot_return:
+ cmp r1, #kInterpEntryThrow @ were we throwing an exception?
+ beq common_exceptionThrown
+
+#if defined(WITH_JIT)
+.Lnot_throw:
+ ldr r10,[rGLUE, #offGlue_jitResumeNPC]
+ ldr r2,[rGLUE, #offGlue_jitResumeDPC]
+ cmp r1, #kInterpEntryResume @ resuming after Jit single-step?
+ bne .Lbad_arg
+ cmp rPC,r2
+ bne .LentryInstr @ must have branched, don't resume
+#if defined(WITH_SELF_VERIFICATION)
+ @ glue->entryPoint will be set in dvmSelfVerificationSaveState
+ b jitSVShadowRunStart @ re-enter the translation after the
+ @ single-stepped instruction
+ @noreturn
+#endif
+ mov r1, #kInterpEntryInstr
+ str r1, [rGLUE, #offGlue_entryPoint]
+ bx r10 @ re-enter the translation
+#endif
+
+.Lbad_arg:
+ ldr r0, strBadEntryPoint
+ @ r1 holds value of entryPoint
+ bl printf
+ bl dvmAbort
+ .fnend
+
+
+ .global dvmMterpStdBail
+ .type dvmMterpStdBail, %function
+
+/*
+ * Restore the stack pointer and PC from the save point established on entry.
+ * This is essentially the same as a longjmp, but should be cheaper. The
+ * last instruction causes us to return to whoever called dvmMterpStdRun.
+ *
+ * We pushed some registers on the stack in dvmMterpStdRun, then saved
+ * SP and LR. Here we restore SP, restore the registers, and then restore
+ * LR to PC.
+ *
+ * On entry:
+ * r0 MterpGlue* glue
+ * r1 bool changeInterp
+ */
+dvmMterpStdBail:
+ ldr sp, [r0, #offGlue_bailPtr] @ sp<- saved SP
+ mov r0, r1 @ return the changeInterp value
+ add sp, sp, #4 @ un-align 64
+ LDMFD_PC "r4-r10,fp" @ restore 9 regs and return
+
+
+/*
+ * String references.
+ */
+strBadEntryPoint:
+ .word .LstrBadEntryPoint
+
+
+ .global dvmAsmInstructionStart
+ .type dvmAsmInstructionStart, %function
+dvmAsmInstructionStart = .L_OP_NOP
+ .text
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NOP: /* 0x00 */
+/* File: armv5te/OP_NOP.S */
+ FETCH_ADVANCE_INST(1) @ advance to next instr, load rINST
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ GOTO_OPCODE(ip) @ execute it
+
+#ifdef ASSIST_DEBUGGER
+ /* insert fake function header to help gdb find the stack frame */
+ .type dalvik_inst, %function
+dalvik_inst:
+ .fnstart
+ MTERP_ENTRY1
+ MTERP_ENTRY2
+ .fnend
+#endif
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE: /* 0x01 */
+/* File: armv6t2/OP_MOVE.S */
+ /* for move, move-object, long-to-int */
+ /* op vA, vB */
+ mov r1, rINST, lsr #12 @ r1<- B from 15:12
+ ubfx r0, rINST, #8, #4 @ r0<- A from 11:8
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[B]
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ SET_VREG(r2, r0) @ fp[A]<- r2
+ GOTO_OPCODE(ip) @ execute next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_FROM16: /* 0x02 */
+/* File: armv5te/OP_MOVE_FROM16.S */
+ /* for: move/from16, move-object/from16 */
+ /* op vAA, vBBBB */
+ FETCH(r1, 1) @ r1<- BBBB
+ mov r0, rINST, lsr #8 @ r0<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[BBBB]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r0) @ fp[AA]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_16: /* 0x03 */
+/* File: armv5te/OP_MOVE_16.S */
+ /* for: move/16, move-object/16 */
+ /* op vAAAA, vBBBB */
+ FETCH(r1, 2) @ r1<- BBBB
+ FETCH(r0, 1) @ r0<- AAAA
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[BBBB]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r0) @ fp[AAAA]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_WIDE: /* 0x04 */
+/* File: armv6t2/OP_MOVE_WIDE.S */
+ /* move-wide vA, vB */
+ /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r2, rINST, #8, #4 @ r2<- A
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[A]
+ ldmia r3, {r0-r1} @ r0/r1<- fp[B]
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r2, {r0-r1} @ fp[A]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_WIDE_FROM16: /* 0x05 */
+/* File: armv5te/OP_MOVE_WIDE_FROM16.S */
+ /* move-wide/from16 vAA, vBBBB */
+ /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+ FETCH(r3, 1) @ r3<- BBBB
+ mov r2, rINST, lsr #8 @ r2<- AA
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[BBBB]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[AA]
+ ldmia r3, {r0-r1} @ r0/r1<- fp[BBBB]
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r2, {r0-r1} @ fp[AA]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_WIDE_16: /* 0x06 */
+/* File: armv5te/OP_MOVE_WIDE_16.S */
+ /* move-wide/16 vAAAA, vBBBB */
+ /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+ FETCH(r3, 2) @ r3<- BBBB
+ FETCH(r2, 1) @ r2<- AAAA
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[BBBB]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[AAAA]
+ ldmia r3, {r0-r1} @ r0/r1<- fp[BBBB]
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r2, {r0-r1} @ fp[AAAA]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_OBJECT: /* 0x07 */
+/* File: armv5te/OP_MOVE_OBJECT.S */
+/* File: armv5te/OP_MOVE.S */
+ /* for move, move-object, long-to-int */
+ /* op vA, vB */
+ mov r1, rINST, lsr #12 @ r1<- B from 15:12
+ mov r0, rINST, lsr #8 @ r0<- A from 11:8
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[B]
+ and r0, r0, #15
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ SET_VREG(r2, r0) @ fp[A]<- r2
+ GOTO_OPCODE(ip) @ execute next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_OBJECT_FROM16: /* 0x08 */
+/* File: armv5te/OP_MOVE_OBJECT_FROM16.S */
+/* File: armv5te/OP_MOVE_FROM16.S */
+ /* for: move/from16, move-object/from16 */
+ /* op vAA, vBBBB */
+ FETCH(r1, 1) @ r1<- BBBB
+ mov r0, rINST, lsr #8 @ r0<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[BBBB]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r0) @ fp[AA]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_OBJECT_16: /* 0x09 */
+/* File: armv5te/OP_MOVE_OBJECT_16.S */
+/* File: armv5te/OP_MOVE_16.S */
+ /* for: move/16, move-object/16 */
+ /* op vAAAA, vBBBB */
+ FETCH(r1, 2) @ r1<- BBBB
+ FETCH(r0, 1) @ r0<- AAAA
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[BBBB]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r0) @ fp[AAAA]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_RESULT: /* 0x0a */
+/* File: armv5te/OP_MOVE_RESULT.S */
+ /* for: move-result, move-result-object */
+ /* op vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ ldr r0, [rGLUE, #offGlue_retval] @ r0<- glue->retval.i
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[AA]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_RESULT_WIDE: /* 0x0b */
+/* File: armv5te/OP_MOVE_RESULT_WIDE.S */
+ /* move-result-wide vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ add r3, rGLUE, #offGlue_retval @ r3<- &glue->retval
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[AA]
+ ldmia r3, {r0-r1} @ r0/r1<- retval.j
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r2, {r0-r1} @ fp[AA]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_RESULT_OBJECT: /* 0x0c */
+/* File: armv5te/OP_MOVE_RESULT_OBJECT.S */
+/* File: armv5te/OP_MOVE_RESULT.S */
+ /* for: move-result, move-result-object */
+ /* op vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ ldr r0, [rGLUE, #offGlue_retval] @ r0<- glue->retval.i
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[AA]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_EXCEPTION: /* 0x0d */
+/* File: armv5te/OP_MOVE_EXCEPTION.S */
+ /* move-exception vAA */
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ mov r2, rINST, lsr #8 @ r2<- AA
+ ldr r3, [r0, #offThread_exception] @ r3<- dvmGetException bypass
+ mov r1, #0 @ r1<- 0
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ SET_VREG(r3, r2) @ fp[AA]<- exception obj
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ str r1, [r0, #offThread_exception] @ dvmClearException bypass
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RETURN_VOID: /* 0x0e */
+/* File: armv5te/OP_RETURN_VOID.S */
+ b common_returnFromMethod
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RETURN: /* 0x0f */
+/* File: armv5te/OP_RETURN.S */
+ /*
+ * Return a 32-bit value. Copies the return value into the "glue"
+ * structure, then jumps to the return handler.
+ *
+ * for: return, return-object
+ */
+ /* op vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ GET_VREG(r0, r2) @ r0<- vAA
+ str r0, [rGLUE, #offGlue_retval] @ retval.i <- vAA
+ b common_returnFromMethod
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RETURN_WIDE: /* 0x10 */
+/* File: armv5te/OP_RETURN_WIDE.S */
+ /*
+ * Return a 64-bit value. Copies the return value into the "glue"
+ * structure, then jumps to the return handler.
+ */
+ /* return-wide vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[AA]
+ add r3, rGLUE, #offGlue_retval @ r3<- &glue->retval
+ ldmia r2, {r0-r1} @ r0/r1 <- vAA/vAA+1
+ stmia r3, {r0-r1} @ retval<- r0/r1
+ b common_returnFromMethod
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RETURN_OBJECT: /* 0x11 */
+/* File: armv5te/OP_RETURN_OBJECT.S */
+/* File: armv5te/OP_RETURN.S */
+ /*
+ * Return a 32-bit value. Copies the return value into the "glue"
+ * structure, then jumps to the return handler.
+ *
+ * for: return, return-object
+ */
+ /* op vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ GET_VREG(r0, r2) @ r0<- vAA
+ str r0, [rGLUE, #offGlue_retval] @ retval.i <- vAA
+ b common_returnFromMethod
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_4: /* 0x12 */
+/* File: armv6t2/OP_CONST_4.S */
+ /* const/4 vA, #+B */
+ mov r1, rINST, lsl #16 @ r1<- Bxxx0000
+ ubfx r0, rINST, #8, #4 @ r0<- A
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ mov r1, r1, asr #28 @ r1<- sssssssB (sign-extended)
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ SET_VREG(r1, r0) @ fp[A]<- r1
+ GOTO_OPCODE(ip) @ execute next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_16: /* 0x13 */
+/* File: armv5te/OP_CONST_16.S */
+ /* const/16 vAA, #+BBBB */
+ FETCH_S(r0, 1) @ r0<- ssssBBBB (sign-extended)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r0, r3) @ vAA<- r0
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST: /* 0x14 */
+/* File: armv5te/OP_CONST.S */
+ /* const vAA, #+BBBBbbbb */
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH(r0, 1) @ r0<- bbbb (low)
+ FETCH(r1, 2) @ r1<- BBBB (high)
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ orr r0, r0, r1, lsl #16 @ r0<- BBBBbbbb
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r3) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_HIGH16: /* 0x15 */
+/* File: armv5te/OP_CONST_HIGH16.S */
+ /* const/high16 vAA, #+BBBB0000 */
+ FETCH(r0, 1) @ r0<- 0000BBBB (zero-extended)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ mov r0, r0, lsl #16 @ r0<- BBBB0000
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r0, r3) @ vAA<- r0
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_WIDE_16: /* 0x16 */
+/* File: armv5te/OP_CONST_WIDE_16.S */
+ /* const-wide/16 vAA, #+BBBB */
+ FETCH_S(r0, 1) @ r0<- ssssBBBB (sign-extended)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ mov r1, r0, asr #31 @ r1<- ssssssss
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_WIDE_32: /* 0x17 */
+/* File: armv5te/OP_CONST_WIDE_32.S */
+ /* const-wide/32 vAA, #+BBBBbbbb */
+ FETCH(r0, 1) @ r0<- 0000bbbb (low)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH_S(r2, 2) @ r2<- ssssBBBB (high)
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ orr r0, r0, r2, lsl #16 @ r0<- BBBBbbbb
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[AA]
+ mov r1, r0, asr #31 @ r1<- ssssssss
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_WIDE: /* 0x18 */
+/* File: armv5te/OP_CONST_WIDE.S */
+ /* const-wide vAA, #+HHHHhhhhBBBBbbbb */
+ FETCH(r0, 1) @ r0<- bbbb (low)
+ FETCH(r1, 2) @ r1<- BBBB (low middle)
+ FETCH(r2, 3) @ r2<- hhhh (high middle)
+ orr r0, r0, r1, lsl #16 @ r0<- BBBBbbbb (low word)
+ FETCH(r3, 4) @ r3<- HHHH (high)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ orr r1, r2, r3, lsl #16 @ r1<- HHHHhhhh (high word)
+ FETCH_ADVANCE_INST(5) @ advance rPC, load rINST
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_WIDE_HIGH16: /* 0x19 */
+/* File: armv5te/OP_CONST_WIDE_HIGH16.S */
+ /* const-wide/high16 vAA, #+BBBB000000000000 */
+ FETCH(r1, 1) @ r1<- 0000BBBB (zero-extended)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ mov r0, #0 @ r0<- 00000000
+ mov r1, r1, lsl #16 @ r1<- BBBB0000
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_STRING: /* 0x1a */
+/* File: armv5te/OP_CONST_STRING.S */
+ /* const/string vAA, String@BBBB */
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- glue->methodClassDex
+ mov r9, rINST, lsr #8 @ r9<- AA
+ ldr r2, [r2, #offDvmDex_pResStrings] @ r2<- dvmDex->pResStrings
+ ldr r0, [r2, r1, lsl #2] @ r0<- pResStrings[BBBB]
+ cmp r0, #0 @ not yet resolved?
+ beq .LOP_CONST_STRING_resolve
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_STRING_JUMBO: /* 0x1b */
+/* File: armv5te/OP_CONST_STRING_JUMBO.S */
+ /* const/string vAA, String@BBBBBBBB */
+ FETCH(r0, 1) @ r0<- bbbb (low)
+ FETCH(r1, 2) @ r1<- BBBB (high)
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- glue->methodClassDex
+ mov r9, rINST, lsr #8 @ r9<- AA
+ ldr r2, [r2, #offDvmDex_pResStrings] @ r2<- dvmDex->pResStrings
+ orr r1, r0, r1, lsl #16 @ r1<- BBBBbbbb
+ ldr r0, [r2, r1, lsl #2] @ r0<- pResStrings[BBBB]
+ cmp r0, #0
+ beq .LOP_CONST_STRING_JUMBO_resolve
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_CLASS: /* 0x1c */
+/* File: armv5te/OP_CONST_CLASS.S */
+ /* const/class vAA, Class@BBBB */
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- glue->methodClassDex
+ mov r9, rINST, lsr #8 @ r9<- AA
+ ldr r2, [r2, #offDvmDex_pResClasses] @ r2<- dvmDex->pResClasses
+ ldr r0, [r2, r1, lsl #2] @ r0<- pResClasses[BBBB]
+ cmp r0, #0 @ not yet resolved?
+ beq .LOP_CONST_CLASS_resolve
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MONITOR_ENTER: /* 0x1d */
+/* File: armv5te/OP_MONITOR_ENTER.S */
+ /*
+ * Synchronize on an object.
+ */
+ /* monitor-enter vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ GET_VREG(r1, r2) @ r1<- vAA (object)
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ cmp r1, #0 @ null object?
+ EXPORT_PC() @ need for precise GC, MONITOR_TRACKING
+ beq common_errNullObject @ null object, throw an exception
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ bl dvmLockObject @ call(self, obj)
+#ifdef WITH_DEADLOCK_PREDICTION /* implies WITH_MONITOR_TRACKING */
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ ldr r1, [r0, #offThread_exception] @ check for exception
+ cmp r1, #0
+ bne common_exceptionThrown @ exception raised, bail out
+#endif
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MONITOR_EXIT: /* 0x1e */
+/* File: armv5te/OP_MONITOR_EXIT.S */
+ /*
+ * Unlock an object.
+ *
+ * Exceptions that occur when unlocking a monitor need to appear as
+ * if they happened at the following instruction. See the Dalvik
+ * instruction spec.
+ */
+ /* monitor-exit vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ EXPORT_PC() @ before fetch: export the PC
+ GET_VREG(r1, r2) @ r1<- vAA (object)
+ cmp r1, #0 @ null object?
+ beq 1f @ yes
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ bl dvmUnlockObject @ r0<- success for unlock(self, obj)
+ cmp r0, #0 @ failed?
+ FETCH_ADVANCE_INST(1) @ before throw: advance rPC, load rINST
+ beq common_exceptionThrown @ yes, exception is pending
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+1:
+ FETCH_ADVANCE_INST(1) @ advance before throw
+ b common_errNullObject
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CHECK_CAST: /* 0x1f */
+/* File: armv5te/OP_CHECK_CAST.S */
+ /*
+ * Check to see if a cast from one class to another is allowed.
+ */
+ /* check-cast vAA, class@BBBB */
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH(r2, 1) @ r2<- BBBB
+ GET_VREG(r9, r3) @ r9<- object
+ ldr r0, [rGLUE, #offGlue_methodClassDex] @ r0<- pDvmDex
+ cmp r9, #0 @ is object null?
+ ldr r0, [r0, #offDvmDex_pResClasses] @ r0<- pDvmDex->pResClasses
+ beq .LOP_CHECK_CAST_okay @ null obj, cast always succeeds
+ ldr r1, [r0, r2, lsl #2] @ r1<- resolved class
+ ldr r0, [r9, #offObject_clazz] @ r0<- obj->clazz
+ cmp r1, #0 @ have we resolved this before?
+ beq .LOP_CHECK_CAST_resolve @ not resolved, do it now
+.LOP_CHECK_CAST_resolved:
+ cmp r0, r1 @ same class (trivial success)?
+ bne .LOP_CHECK_CAST_fullcheck @ no, do full check
+.LOP_CHECK_CAST_okay:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INSTANCE_OF: /* 0x20 */
+/* File: armv5te/OP_INSTANCE_OF.S */
+ /*
+ * Check to see if an object reference is an instance of a class.
+ *
+ * Most common situation is a non-null object, being compared against
+ * an already-resolved class.
+ */
+ /* instance-of vA, vB, class@CCCC */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r3) @ r0<- vB (object)
+ and r9, r9, #15 @ r9<- A
+ cmp r0, #0 @ is object null?
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- pDvmDex
+ beq .LOP_INSTANCE_OF_store @ null obj, not an instance, store r0
+ FETCH(r3, 1) @ r3<- CCCC
+ ldr r2, [r2, #offDvmDex_pResClasses] @ r2<- pDvmDex->pResClasses
+ ldr r1, [r2, r3, lsl #2] @ r1<- resolved class
+ ldr r0, [r0, #offObject_clazz] @ r0<- obj->clazz
+ cmp r1, #0 @ have we resolved this before?
+ beq .LOP_INSTANCE_OF_resolve @ not resolved, do it now
+.LOP_INSTANCE_OF_resolved: @ r0=obj->clazz, r1=resolved class
+ cmp r0, r1 @ same class (trivial success)?
+ beq .LOP_INSTANCE_OF_trivial @ yes, trivial finish
+ b .LOP_INSTANCE_OF_fullcheck @ no, do full check
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ARRAY_LENGTH: /* 0x21 */
+/* File: armv6t2/OP_ARRAY_LENGTH.S */
+ /*
+ * Return the length of an array.
+ */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r2, rINST, #8, #4 @ r2<- A
+ GET_VREG(r0, r1) @ r0<- vB (object ref)
+ cmp r0, #0 @ is object null?
+ beq common_errNullObject @ yup, fail
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ ldr r3, [r0, #offArrayObject_length] @ r3<- array length
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r3, r2) @ vB<- length
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEW_INSTANCE: /* 0x22 */
+/* File: armv5te/OP_NEW_INSTANCE.S */
+ /*
+ * Create a new instance of a class.
+ */
+ /* new-instance vAA, class@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResClasses] @ r3<- pDvmDex->pResClasses
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved class
+ EXPORT_PC() @ req'd for init, resolve, alloc
+ cmp r0, #0 @ already resolved?
+ beq .LOP_NEW_INSTANCE_resolve @ no, resolve it now
+.LOP_NEW_INSTANCE_resolved: @ r0=class
+ ldrb r1, [r0, #offClassObject_status] @ r1<- ClassStatus enum
+ cmp r1, #CLASS_INITIALIZED @ has class been initialized?
+ bne .LOP_NEW_INSTANCE_needinit @ no, init class now
+.LOP_NEW_INSTANCE_initialized: @ r0=class
+ mov r1, #ALLOC_DONT_TRACK @ flags for alloc call
+ bl dvmAllocObject @ r0<- new object
+ b .LOP_NEW_INSTANCE_finish @ continue
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEW_ARRAY: /* 0x23 */
+/* File: armv5te/OP_NEW_ARRAY.S */
+ /*
+ * Allocate an array of objects, specified with the array class
+ * and a count.
+ *
+ * The verifier guarantees that this is an array class, so we don't
+ * check for it here.
+ */
+ /* new-array vA, vB, class@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ FETCH(r2, 1) @ r2<- CCCC
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ GET_VREG(r1, r0) @ r1<- vB (array length)
+ ldr r3, [r3, #offDvmDex_pResClasses] @ r3<- pDvmDex->pResClasses
+ cmp r1, #0 @ check length
+ ldr r0, [r3, r2, lsl #2] @ r0<- resolved class
+ bmi common_errNegativeArraySize @ negative length, bail
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ req'd for resolve, alloc
+ bne .LOP_NEW_ARRAY_finish @ resolved, continue
+ b .LOP_NEW_ARRAY_resolve @ do resolve now
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FILLED_NEW_ARRAY: /* 0x24 */
+/* File: armv5te/OP_FILLED_NEW_ARRAY.S */
+ /*
+ * Create a new array with elements filled from registers.
+ *
+ * for: filled-new-array, filled-new-array/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResClasses] @ r3<- pDvmDex->pResClasses
+ EXPORT_PC() @ need for resolve and alloc
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved class
+ mov r10, rINST, lsr #8 @ r10<- AA or BA
+ cmp r0, #0 @ already resolved?
+ bne .LOP_FILLED_NEW_ARRAY_continue @ yes, continue on
+8: ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ mov r2, #0 @ r2<- false
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- call(clazz, ref)
+ cmp r0, #0 @ got null?
+ beq common_exceptionThrown @ yes, handle exception
+ b .LOP_FILLED_NEW_ARRAY_continue
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FILLED_NEW_ARRAY_RANGE: /* 0x25 */
+/* File: armv5te/OP_FILLED_NEW_ARRAY_RANGE.S */
+/* File: armv5te/OP_FILLED_NEW_ARRAY.S */
+ /*
+ * Create a new array with elements filled from registers.
+ *
+ * for: filled-new-array, filled-new-array/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResClasses] @ r3<- pDvmDex->pResClasses
+ EXPORT_PC() @ need for resolve and alloc
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved class
+ mov r10, rINST, lsr #8 @ r10<- AA or BA
+ cmp r0, #0 @ already resolved?
+ bne .LOP_FILLED_NEW_ARRAY_RANGE_continue @ yes, continue on
+8: ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ mov r2, #0 @ r2<- false
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- call(clazz, ref)
+ cmp r0, #0 @ got null?
+ beq common_exceptionThrown @ yes, handle exception
+ b .LOP_FILLED_NEW_ARRAY_RANGE_continue
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FILL_ARRAY_DATA: /* 0x26 */
+/* File: armv5te/OP_FILL_ARRAY_DATA.S */
+ /* fill-array-data vAA, +BBBBBBBB */
+ FETCH(r0, 1) @ r0<- bbbb (lo)
+ FETCH(r1, 2) @ r1<- BBBB (hi)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ orr r1, r0, r1, lsl #16 @ r1<- BBBBbbbb
+ GET_VREG(r0, r3) @ r0<- vAA (array object)
+ add r1, rPC, r1, lsl #1 @ r1<- PC + BBBBbbbb*2 (array data off.)
+ EXPORT_PC();
+ bl dvmInterpHandleFillArrayData@ fill the array with predefined data
+ cmp r0, #0 @ 0 means an exception is thrown
+ beq common_exceptionThrown @ has exception
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_THROW: /* 0x27 */
+/* File: armv5te/OP_THROW.S */
+ /*
+ * Throw an exception object in the current thread.
+ */
+ /* throw vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ GET_VREG(r1, r2) @ r1<- vAA (exception object)
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ EXPORT_PC() @ exception handler can throw
+ cmp r1, #0 @ null object?
+ beq common_errNullObject @ yes, throw an NPE instead
+ @ bypass dvmSetException, just store it
+ str r1, [r0, #offThread_exception] @ thread->exception<- obj
+ b common_exceptionThrown
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_GOTO: /* 0x28 */
+/* File: armv5te/OP_GOTO.S */
+ /*
+ * Unconditional branch, 8-bit offset.
+ *
+ * The branch distance is a signed code-unit offset, which we need to
+ * double to get a byte offset.
+ */
+ /* goto +AA */
+ mov r0, rINST, lsl #16 @ r0<- AAxx0000
+ movs r9, r0, asr #24 @ r9<- ssssssAA (sign-extended)
+ mov r9, r9, lsl #1 @ r9<- byte offset
+ bmi common_backwardBranch @ backward branch, do periodic checks
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_GOTO_16: /* 0x29 */
+/* File: armv5te/OP_GOTO_16.S */
+ /*
+ * Unconditional branch, 16-bit offset.
+ *
+ * The branch distance is a signed code-unit offset, which we need to
+ * double to get a byte offset.
+ */
+ /* goto/16 +AAAA */
+ FETCH_S(r0, 1) @ r0<- ssssAAAA (sign-extended)
+ movs r9, r0, asl #1 @ r9<- byte offset, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_GOTO_32: /* 0x2a */
+/* File: armv5te/OP_GOTO_32.S */
+ /*
+ * Unconditional branch, 32-bit offset.
+ *
+ * The branch distance is a signed code-unit offset, which we need to
+ * double to get a byte offset.
+ *
+ * Unlike most opcodes, this one is allowed to branch to itself, so
+ * our "backward branch" test must be "<=0" instead of "<0". The ORRS
+ * instruction doesn't affect the V flag, so we need to clear it
+ * explicitly.
+ */
+ /* goto/32 +AAAAAAAA */
+ FETCH(r0, 1) @ r0<- aaaa (lo)
+ FETCH(r1, 2) @ r1<- AAAA (hi)
+ cmp ip, ip @ (clear V flag during stall)
+ orrs r0, r0, r1, lsl #16 @ r0<- AAAAaaaa, check sign
+ mov r9, r0, asl #1 @ r9<- byte offset
+ ble common_backwardBranch @ backward branch, do periodic checks
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_PACKED_SWITCH: /* 0x2b */
+/* File: armv5te/OP_PACKED_SWITCH.S */
+ /*
+ * Handle a packed-switch or sparse-switch instruction. In both cases
+ * we decode it and hand it off to a helper function.
+ *
+ * We don't really expect backward branches in a switch statement, but
+ * they're perfectly legal, so we check for them here.
+ *
+ * for: packed-switch, sparse-switch
+ */
+ /* op vAA, +BBBB */
+ FETCH(r0, 1) @ r0<- bbbb (lo)
+ FETCH(r1, 2) @ r1<- BBBB (hi)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ orr r0, r0, r1, lsl #16 @ r0<- BBBBbbbb
+ GET_VREG(r1, r3) @ r1<- vAA
+ add r0, rPC, r0, lsl #1 @ r0<- PC + BBBBbbbb*2
+ bl dvmInterpHandlePackedSwitch @ r0<- code-unit branch offset
+ movs r9, r0, asl #1 @ r9<- branch byte offset, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+ beq common_backwardBranch @ (want to use BLE but V is unknown)
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPARSE_SWITCH: /* 0x2c */
+/* File: armv5te/OP_SPARSE_SWITCH.S */
+/* File: armv5te/OP_PACKED_SWITCH.S */
+ /*
+ * Handle a packed-switch or sparse-switch instruction. In both cases
+ * we decode it and hand it off to a helper function.
+ *
+ * We don't really expect backward branches in a switch statement, but
+ * they're perfectly legal, so we check for them here.
+ *
+ * for: packed-switch, sparse-switch
+ */
+ /* op vAA, +BBBB */
+ FETCH(r0, 1) @ r0<- bbbb (lo)
+ FETCH(r1, 2) @ r1<- BBBB (hi)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ orr r0, r0, r1, lsl #16 @ r0<- BBBBbbbb
+ GET_VREG(r1, r3) @ r1<- vAA
+ add r0, rPC, r0, lsl #1 @ r0<- PC + BBBBbbbb*2
+ bl dvmInterpHandleSparseSwitch @ r0<- code-unit branch offset
+ movs r9, r0, asl #1 @ r9<- branch byte offset, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+ beq common_backwardBranch @ (want to use BLE but V is unknown)
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CMPL_FLOAT: /* 0x2d */
+/* File: arm-vfp/OP_CMPL_FLOAT.S */
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * int compare(x, y) {
+ * if (x == y) {
+ * return 0;
+ * } else if (x > y) {
+ * return 1;
+ * } else if (x < y) {
+ * return -1;
+ * } else {
+ * return -1;
+ * }
+ * }
+ */
+ /* op vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ flds s0, [r2] @ s0<- vBB
+ flds s1, [r3] @ s1<- vCC
+ fcmpes s0, s1 @ compare (vBB, vCC)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ mvn r0, #0 @ r0<- -1 (default)
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fmstat @ export status flags
+ movgt r0, #1 @ (greater than) r1<- 1
+ moveq r0, #0 @ (equal) r1<- 0
+ b .LOP_CMPL_FLOAT_finish @ argh
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CMPG_FLOAT: /* 0x2e */
+/* File: arm-vfp/OP_CMPG_FLOAT.S */
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * int compare(x, y) {
+ * if (x == y) {
+ * return 0;
+ * } else if (x < y) {
+ * return -1;
+ * } else if (x > y) {
+ * return 1;
+ * } else {
+ * return 1;
+ * }
+ * }
+ */
+ /* op vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ flds s0, [r2] @ s0<- vBB
+ flds s1, [r3] @ s1<- vCC
+ fcmpes s0, s1 @ compare (vBB, vCC)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ mov r0, #1 @ r0<- 1 (default)
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fmstat @ export status flags
+ mvnmi r0, #0 @ (less than) r1<- -1
+ moveq r0, #0 @ (equal) r1<- 0
+ b .LOP_CMPG_FLOAT_finish @ argh
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CMPL_DOUBLE: /* 0x2f */
+/* File: arm-vfp/OP_CMPL_DOUBLE.S */
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * int compare(x, y) {
+ * if (x == y) {
+ * return 0;
+ * } else if (x > y) {
+ * return 1;
+ * } else if (x < y) {
+ * return -1;
+ * } else {
+ * return -1;
+ * }
+ * }
+ */
+ /* op vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ fldd d0, [r2] @ d0<- vBB
+ fldd d1, [r3] @ d1<- vCC
+ fcmped d0, d1 @ compare (vBB, vCC)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ mvn r0, #0 @ r0<- -1 (default)
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fmstat @ export status flags
+ movgt r0, #1 @ (greater than) r1<- 1
+ moveq r0, #0 @ (equal) r1<- 0
+ b .LOP_CMPL_DOUBLE_finish @ argh
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CMPG_DOUBLE: /* 0x30 */
+/* File: arm-vfp/OP_CMPG_DOUBLE.S */
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * int compare(x, y) {
+ * if (x == y) {
+ * return 0;
+ * } else if (x < y) {
+ * return -1;
+ * } else if (x > y) {
+ * return 1;
+ * } else {
+ * return 1;
+ * }
+ * }
+ */
+ /* op vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ fldd d0, [r2] @ d0<- vBB
+ fldd d1, [r3] @ d1<- vCC
+ fcmped d0, d1 @ compare (vBB, vCC)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ mov r0, #1 @ r0<- 1 (default)
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fmstat @ export status flags
+ mvnmi r0, #0 @ (less than) r1<- -1
+ moveq r0, #0 @ (equal) r1<- 0
+ b .LOP_CMPG_DOUBLE_finish @ argh
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CMP_LONG: /* 0x31 */
+/* File: armv5te/OP_CMP_LONG.S */
+ /*
+ * Compare two 64-bit values. Puts 0, 1, or -1 into the destination
+ * register based on the results of the comparison.
+ *
+ * We load the full values with LDM, but in practice many values could
+ * be resolved by only looking at the high word. This could be made
+ * faster or slower by splitting the LDM into a pair of LDRs.
+ *
+ * If we just wanted to set condition flags, we could do this:
+ * subs ip, r0, r2
+ * sbcs ip, r1, r3
+ * subeqs ip, r0, r2
+ * Leaving { <0, 0, >0 } in ip. However, we have to set it to a specific
+ * integer value, which we can do with 2 conditional mov/mvn instructions
+ * (set 1, set -1; if they're equal we already have 0 in ip), giving
+ * us a constant 5-cycle path plus a branch at the end to the
+ * instruction epilogue code. The multi-compare approach below needs
+ * 2 or 3 cycles + branch if the high word doesn't match, 6 + branch
+ * in the worst case (the 64-bit values are equal).
+ */
+ /* cmp-long vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ cmp r1, r3 @ compare (vBB+1, vCC+1)
+ blt .LOP_CMP_LONG_less @ signed compare on high part
+ bgt .LOP_CMP_LONG_greater
+ subs r1, r0, r2 @ r1<- r0 - r2
+ bhi .LOP_CMP_LONG_greater @ unsigned compare on low part
+ bne .LOP_CMP_LONG_less
+ b .LOP_CMP_LONG_finish @ equal; r1 already holds 0
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_EQ: /* 0x32 */
+/* File: armv6t2/OP_IF_EQ.S */
+/* File: armv6t2/bincmp.S */
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r0, rINST, #8, #4 @ r0<- A
+ GET_VREG(r3, r1) @ r3<- vB
+ GET_VREG(r2, r0) @ r2<- vA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, r3 @ compare (vA, vB)
+ bne 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ b common_testUpdateProfile
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_NE: /* 0x33 */
+/* File: armv6t2/OP_IF_NE.S */
+/* File: armv6t2/bincmp.S */
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r0, rINST, #8, #4 @ r0<- A
+ GET_VREG(r3, r1) @ r3<- vB
+ GET_VREG(r2, r0) @ r2<- vA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, r3 @ compare (vA, vB)
+ beq 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ b common_testUpdateProfile
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_LT: /* 0x34 */
+/* File: armv6t2/OP_IF_LT.S */
+/* File: armv6t2/bincmp.S */
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r0, rINST, #8, #4 @ r0<- A
+ GET_VREG(r3, r1) @ r3<- vB
+ GET_VREG(r2, r0) @ r2<- vA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, r3 @ compare (vA, vB)
+ bge 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ b common_testUpdateProfile
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_GE: /* 0x35 */
+/* File: armv6t2/OP_IF_GE.S */
+/* File: armv6t2/bincmp.S */
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r0, rINST, #8, #4 @ r0<- A
+ GET_VREG(r3, r1) @ r3<- vB
+ GET_VREG(r2, r0) @ r2<- vA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, r3 @ compare (vA, vB)
+ blt 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ b common_testUpdateProfile
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_GT: /* 0x36 */
+/* File: armv6t2/OP_IF_GT.S */
+/* File: armv6t2/bincmp.S */
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r0, rINST, #8, #4 @ r0<- A
+ GET_VREG(r3, r1) @ r3<- vB
+ GET_VREG(r2, r0) @ r2<- vA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, r3 @ compare (vA, vB)
+ ble 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ b common_testUpdateProfile
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_LE: /* 0x37 */
+/* File: armv6t2/OP_IF_LE.S */
+/* File: armv6t2/bincmp.S */
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r0, rINST, #8, #4 @ r0<- A
+ GET_VREG(r3, r1) @ r3<- vB
+ GET_VREG(r2, r0) @ r2<- vA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, r3 @ compare (vA, vB)
+ bgt 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ b common_testUpdateProfile
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_EQZ: /* 0x38 */
+/* File: armv5te/OP_IF_EQZ.S */
+/* File: armv5te/zcmp.S */
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ mov r0, rINST, lsr #8 @ r0<- AA
+ GET_VREG(r2, r0) @ r2<- vAA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, #0 @ compare (vA, 0)
+ bne 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_NEZ: /* 0x39 */
+/* File: armv5te/OP_IF_NEZ.S */
+/* File: armv5te/zcmp.S */
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ mov r0, rINST, lsr #8 @ r0<- AA
+ GET_VREG(r2, r0) @ r2<- vAA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, #0 @ compare (vA, 0)
+ beq 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_LTZ: /* 0x3a */
+/* File: armv5te/OP_IF_LTZ.S */
+/* File: armv5te/zcmp.S */
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ mov r0, rINST, lsr #8 @ r0<- AA
+ GET_VREG(r2, r0) @ r2<- vAA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, #0 @ compare (vA, 0)
+ bge 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_GEZ: /* 0x3b */
+/* File: armv5te/OP_IF_GEZ.S */
+/* File: armv5te/zcmp.S */
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ mov r0, rINST, lsr #8 @ r0<- AA
+ GET_VREG(r2, r0) @ r2<- vAA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, #0 @ compare (vA, 0)
+ blt 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_GTZ: /* 0x3c */
+/* File: armv5te/OP_IF_GTZ.S */
+/* File: armv5te/zcmp.S */
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ mov r0, rINST, lsr #8 @ r0<- AA
+ GET_VREG(r2, r0) @ r2<- vAA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, #0 @ compare (vA, 0)
+ ble 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_LEZ: /* 0x3d */
+/* File: armv5te/OP_IF_LEZ.S */
+/* File: armv5te/zcmp.S */
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ mov r0, rINST, lsr #8 @ r0<- AA
+ GET_VREG(r2, r0) @ r2<- vAA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, #0 @ compare (vA, 0)
+ bgt 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_3E: /* 0x3e */
+/* File: armv5te/OP_UNUSED_3E.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_3F: /* 0x3f */
+/* File: armv5te/OP_UNUSED_3F.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_40: /* 0x40 */
+/* File: armv5te/OP_UNUSED_40.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_41: /* 0x41 */
+/* File: armv5te/OP_UNUSED_41.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_42: /* 0x42 */
+/* File: armv5te/OP_UNUSED_42.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_43: /* 0x43 */
+/* File: armv5te/OP_UNUSED_43.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET: /* 0x44 */
+/* File: armv5te/OP_AGET.S */
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #2 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldr r2, [r0, #offArrayObject_contents] @ r2<- vBB[vCC]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r9) @ vAA<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_WIDE: /* 0x45 */
+/* File: armv5te/OP_AGET_WIDE.S */
+ /*
+ * Array get, 64 bits. vAA <- vBB[vCC].
+ *
+ * Arrays of long/double are 64-bit aligned, so it's okay to use LDRD.
+ */
+ /* aget-wide vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #3 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcc .LOP_AGET_WIDE_finish @ okay, continue below
+ b common_errArrayIndex @ index >= length, bail
+ @ May want to swap the order of these two branches depending on how the
+ @ branch prediction (if any) handles conditional forward branches vs.
+ @ unconditional forward branches.
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_OBJECT: /* 0x46 */
+/* File: armv5te/OP_AGET_OBJECT.S */
+/* File: armv5te/OP_AGET.S */
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #2 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldr r2, [r0, #offArrayObject_contents] @ r2<- vBB[vCC]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r9) @ vAA<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_BOOLEAN: /* 0x47 */
+/* File: armv5te/OP_AGET_BOOLEAN.S */
+/* File: armv5te/OP_AGET.S */
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #0 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldrb r2, [r0, #offArrayObject_contents] @ r2<- vBB[vCC]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r9) @ vAA<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_BYTE: /* 0x48 */
+/* File: armv5te/OP_AGET_BYTE.S */
+/* File: armv5te/OP_AGET.S */
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #0 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldrsb r2, [r0, #offArrayObject_contents] @ r2<- vBB[vCC]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r9) @ vAA<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_CHAR: /* 0x49 */
+/* File: armv5te/OP_AGET_CHAR.S */
+/* File: armv5te/OP_AGET.S */
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #1 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldrh r2, [r0, #offArrayObject_contents] @ r2<- vBB[vCC]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r9) @ vAA<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_SHORT: /* 0x4a */
+/* File: armv5te/OP_AGET_SHORT.S */
+/* File: armv5te/OP_AGET.S */
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #1 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldrsh r2, [r0, #offArrayObject_contents] @ r2<- vBB[vCC]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r9) @ vAA<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT: /* 0x4b */
+/* File: armv5te/OP_APUT.S */
+ /*
+ * Array put, 32 bits or less. vBB[vCC] <- vAA.
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #2 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r2, r9) @ r2<- vAA
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ str r2, [r0, #offArrayObject_contents] @ vBB[vCC]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_WIDE: /* 0x4c */
+/* File: armv5te/OP_APUT_WIDE.S */
+ /*
+ * Array put, 64 bits. vBB[vCC] <- vAA.
+ *
+ * Arrays of long/double are 64-bit aligned, so it's okay to use STRD.
+ */
+ /* aput-wide vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #3 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ bcc .LOP_APUT_WIDE_finish @ okay, continue below
+ b common_errArrayIndex @ index >= length, bail
+ @ May want to swap the order of these two branches depending on how the
+ @ branch prediction (if any) handles conditional forward branches vs.
+ @ unconditional forward branches.
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_OBJECT: /* 0x4d */
+/* File: armv5te/OP_APUT_OBJECT.S */
+ /*
+ * Store an object into an array. vBB[vCC] <- vAA.
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ */
+ /* op vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ GET_VREG(rINST, r2) @ rINST<- vBB (array object)
+ GET_VREG(r0, r3) @ r0<- vCC (requested index)
+ cmp rINST, #0 @ null array object?
+ GET_VREG(r9, r9) @ r9<- vAA
+ beq common_errNullObject @ yes, bail
+ ldr r3, [rINST, #offArrayObject_length] @ r3<- arrayObj->length
+ add r10, rINST, r0, lsl #2 @ r10<- arrayObj + index*width
+ cmp r0, r3 @ compare unsigned index, length
+ bcc .LOP_APUT_OBJECT_finish @ we're okay, continue on
+ b common_errArrayIndex @ index >= length, bail
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_BOOLEAN: /* 0x4e */
+/* File: armv5te/OP_APUT_BOOLEAN.S */
+/* File: armv5te/OP_APUT.S */
+ /*
+ * Array put, 32 bits or less. vBB[vCC] <- vAA.
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #0 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r2, r9) @ r2<- vAA
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ strb r2, [r0, #offArrayObject_contents] @ vBB[vCC]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_BYTE: /* 0x4f */
+/* File: armv5te/OP_APUT_BYTE.S */
+/* File: armv5te/OP_APUT.S */
+ /*
+ * Array put, 32 bits or less. vBB[vCC] <- vAA.
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #0 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r2, r9) @ r2<- vAA
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ strb r2, [r0, #offArrayObject_contents] @ vBB[vCC]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_CHAR: /* 0x50 */
+/* File: armv5te/OP_APUT_CHAR.S */
+/* File: armv5te/OP_APUT.S */
+ /*
+ * Array put, 32 bits or less. vBB[vCC] <- vAA.
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #1 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r2, r9) @ r2<- vAA
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ strh r2, [r0, #offArrayObject_contents] @ vBB[vCC]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_SHORT: /* 0x51 */
+/* File: armv5te/OP_APUT_SHORT.S */
+/* File: armv5te/OP_APUT.S */
+ /*
+ * Array put, 32 bits or less. vBB[vCC] <- vAA.
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #1 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r2, r9) @ r2<- vAA
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ strh r2, [r0, #offArrayObject_contents] @ vBB[vCC]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET: /* 0x52 */
+/* File: armv6t2/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_finish
+ b common_exceptionThrown
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_WIDE: /* 0x53 */
+/* File: armv6t2/OP_IGET_WIDE.S */
+ /*
+ * Wide 32-bit instance field get.
+ */
+ /* iget-wide vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_WIDE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_WIDE_finish
+ b common_exceptionThrown
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_OBJECT: /* 0x54 */
+/* File: armv5te/OP_IGET_OBJECT.S */
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_OBJECT_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_OBJECT_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_BOOLEAN: /* 0x55 */
+/* File: armv5te/OP_IGET_BOOLEAN.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrb", "sqnum":"1" }
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_BOOLEAN_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_BOOLEAN_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_BYTE: /* 0x56 */
+/* File: armv5te/OP_IGET_BYTE.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrsb", "sqnum":"2" }
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_BYTE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_BYTE_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_CHAR: /* 0x57 */
+/* File: armv5te/OP_IGET_CHAR.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrh", "sqnum":"3" }
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_CHAR_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_CHAR_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_SHORT: /* 0x58 */
+/* File: armv5te/OP_IGET_SHORT.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrsh", "sqnum":"4" }
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_SHORT_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_SHORT_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT: /* 0x59 */
+/* File: armv6t2/OP_IPUT.S */
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_finish @ yes, finish up
+ b common_exceptionThrown
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_WIDE: /* 0x5a */
+/* File: armv6t2/OP_IPUT_WIDE.S */
+ /* iput-wide vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_WIDE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_WIDE_finish @ yes, finish up
+ b common_exceptionThrown
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_OBJECT: /* 0x5b */
+/* File: armv5te/OP_IPUT_OBJECT.S */
+ /*
+ * 32-bit instance field put.
+ *
+ * for: iput-object, iput-object-volatile
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_OBJECT_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_OBJECT_finish @ yes, finish up
+ b common_exceptionThrown
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_BOOLEAN: /* 0x5c */
+/* File: armv5te/OP_IPUT_BOOLEAN.S */
+@include "armv5te/OP_IPUT.S" { "store":"strb", "sqnum":"1" }
+/* File: armv5te/OP_IPUT.S */
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_BOOLEAN_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_BOOLEAN_finish @ yes, finish up
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_BYTE: /* 0x5d */
+/* File: armv5te/OP_IPUT_BYTE.S */
+@include "armv5te/OP_IPUT.S" { "store":"strb", "sqnum":"2" }
+/* File: armv5te/OP_IPUT.S */
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_BYTE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_BYTE_finish @ yes, finish up
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_CHAR: /* 0x5e */
+/* File: armv5te/OP_IPUT_CHAR.S */
+@include "armv5te/OP_IPUT.S" { "store":"strh", "sqnum":"3" }
+/* File: armv5te/OP_IPUT.S */
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_CHAR_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_CHAR_finish @ yes, finish up
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_SHORT: /* 0x5f */
+/* File: armv5te/OP_IPUT_SHORT.S */
+@include "armv5te/OP_IPUT.S" { "store":"strh", "sqnum":"4" }
+/* File: armv5te/OP_IPUT.S */
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_SHORT_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_SHORT_finish @ yes, finish up
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET: /* 0x60 */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_resolve @ yes, do resolve
+.LOP_SGET_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_WIDE: /* 0x61 */
+/* File: armv5te/OP_SGET_WIDE.S */
+ /*
+ * 64-bit SGET handler.
+ */
+ /* sget-wide vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_WIDE_resolve @ yes, do resolve
+.LOP_SGET_WIDE_finish:
+ mov r9, rINST, lsr #8 @ r9<- AA
+ .if 0
+ add r0, r0, #offStaticField_value @ r0<- pointer to data
+ bl dvmQuasiAtomicRead64 @ r0/r1<- contents of field
+ .else
+ ldrd r0, [r0, #offStaticField_value] @ r0/r1<- field value (aligned)
+ .endif
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_OBJECT: /* 0x62 */
+/* File: armv5te/OP_SGET_OBJECT.S */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_OBJECT_resolve @ yes, do resolve
+.LOP_SGET_OBJECT_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_BOOLEAN: /* 0x63 */
+/* File: armv5te/OP_SGET_BOOLEAN.S */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_BOOLEAN_resolve @ yes, do resolve
+.LOP_SGET_BOOLEAN_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_BYTE: /* 0x64 */
+/* File: armv5te/OP_SGET_BYTE.S */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_BYTE_resolve @ yes, do resolve
+.LOP_SGET_BYTE_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_CHAR: /* 0x65 */
+/* File: armv5te/OP_SGET_CHAR.S */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_CHAR_resolve @ yes, do resolve
+.LOP_SGET_CHAR_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_SHORT: /* 0x66 */
+/* File: armv5te/OP_SGET_SHORT.S */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_SHORT_resolve @ yes, do resolve
+.LOP_SGET_SHORT_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT: /* 0x67 */
+/* File: armv5te/OP_SPUT.S */
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SPUT_resolve @ yes, do resolve
+.LOP_SPUT_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_WIDE: /* 0x68 */
+/* File: armv5te/OP_SPUT_WIDE.S */
+ /*
+ * 64-bit SPUT handler.
+ */
+ /* sput-wide vAA, field@BBBB */
+ ldr r0, [rGLUE, #offGlue_methodClassDex] @ r0<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r0, [r0, #offDvmDex_pResFields] @ r0<- dvmDex->pResFields
+ mov r9, rINST, lsr #8 @ r9<- AA
+ ldr r2, [r0, r1, lsl #2] @ r2<- resolved StaticField ptr
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ cmp r2, #0 @ is resolved entry null?
+ beq .LOP_SPUT_WIDE_resolve @ yes, do resolve
+.LOP_SPUT_WIDE_finish: @ field ptr in r2, AA in r9
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ GET_INST_OPCODE(r10) @ extract opcode from rINST
+ .if 0
+ add r2, r2, #offStaticField_value @ r2<- pointer to data
+ bl dvmQuasiAtomicSwap64 @ stores r0/r1 into addr r2
+ .else
+ strd r0, [r2, #offStaticField_value] @ field<- vAA/vAA+1
+ .endif
+ GOTO_OPCODE(r10) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_OBJECT: /* 0x69 */
+/* File: armv5te/OP_SPUT_OBJECT.S */
+ /*
+ * 32-bit SPUT handler for objects
+ *
+ * for: sput-object, sput-object-volatile
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_SPUT_OBJECT_finish @ no, continue
+ ldr r9, [rGLUE, #offGlue_method] @ r9<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r9, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_OBJECT_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_BOOLEAN: /* 0x6a */
+/* File: armv5te/OP_SPUT_BOOLEAN.S */
+/* File: armv5te/OP_SPUT.S */
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SPUT_BOOLEAN_resolve @ yes, do resolve
+.LOP_SPUT_BOOLEAN_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_BYTE: /* 0x6b */
+/* File: armv5te/OP_SPUT_BYTE.S */
+/* File: armv5te/OP_SPUT.S */
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SPUT_BYTE_resolve @ yes, do resolve
+.LOP_SPUT_BYTE_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_CHAR: /* 0x6c */
+/* File: armv5te/OP_SPUT_CHAR.S */
+/* File: armv5te/OP_SPUT.S */
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SPUT_CHAR_resolve @ yes, do resolve
+.LOP_SPUT_CHAR_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_SHORT: /* 0x6d */
+/* File: armv5te/OP_SPUT_SHORT.S */
+/* File: armv5te/OP_SPUT.S */
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SPUT_SHORT_resolve @ yes, do resolve
+.LOP_SPUT_SHORT_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_VIRTUAL: /* 0x6e */
+/* File: armv5te/OP_INVOKE_VIRTUAL.S */
+ /*
+ * Handle a virtual method call.
+ *
+ * for: invoke-virtual, invoke-virtual/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved baseMethod
+ .if (!0)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ must export for invoke
+ bne .LOP_INVOKE_VIRTUAL_continue @ yes, continue on
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ mov r2, #METHOD_VIRTUAL @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ bne .LOP_INVOKE_VIRTUAL_continue @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_SUPER: /* 0x6f */
+/* File: armv5te/OP_INVOKE_SUPER.S */
+ /*
+ * Handle a "super" method call.
+ *
+ * for: invoke-super, invoke-super/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ .if (!0)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ GET_VREG(r2, r10) @ r2<- "this" ptr
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved baseMethod
+ cmp r2, #0 @ null "this"?
+ ldr r9, [rGLUE, #offGlue_method] @ r9<- current method
+ beq common_errNullObject @ null "this", throw exception
+ cmp r0, #0 @ already resolved?
+ ldr r9, [r9, #offMethod_clazz] @ r9<- method->clazz
+ EXPORT_PC() @ must export for invoke
+ bne .LOP_INVOKE_SUPER_continue @ resolved, continue on
+ b .LOP_INVOKE_SUPER_resolve @ do resolve now
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_DIRECT: /* 0x70 */
+/* File: armv5te/OP_INVOKE_DIRECT.S */
+ /*
+ * Handle a direct method call.
+ *
+ * (We could defer the "is 'this' pointer null" test to the common
+ * method invocation code, and use a flag to indicate that static
+ * calls don't count. If we do this as part of copying the arguments
+ * out we could avoiding loading the first arg twice.)
+ *
+ * for: invoke-direct, invoke-direct/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved methodToCall
+ .if (!0)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ must export for invoke
+ GET_VREG(r2, r10) @ r2<- "this" ptr
+ beq .LOP_INVOKE_DIRECT_resolve @ not resolved, do it now
+.LOP_INVOKE_DIRECT_finish:
+ cmp r2, #0 @ null "this" ref?
+ bne common_invokeMethodNoRange @ no, continue on
+ b common_errNullObject @ yes, throw exception
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_STATIC: /* 0x71 */
+/* File: armv5te/OP_INVOKE_STATIC.S */
+ /*
+ * Handle a static method call.
+ *
+ * for: invoke-static, invoke-static/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved methodToCall
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ must export for invoke
+ bne common_invokeMethodNoRange @ yes, continue on
+0: ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ mov r2, #METHOD_STATIC @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ bne common_invokeMethodNoRange @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_INTERFACE: /* 0x72 */
+/* File: armv5te/OP_INVOKE_INTERFACE.S */
+ /*
+ * Handle an interface method call.
+ *
+ * for: invoke-interface, invoke-interface/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r2, 2) @ r2<- FEDC or CCCC
+ FETCH(r1, 1) @ r1<- BBBB
+ .if (!0)
+ and r2, r2, #15 @ r2<- C (or stays CCCC)
+ .endif
+ EXPORT_PC() @ must export for invoke
+ GET_VREG(r0, r2) @ r0<- first arg ("this")
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- methodClassDex
+ cmp r0, #0 @ null obj?
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- method
+ beq common_errNullObject @ yes, fail
+ ldr r0, [r0, #offObject_clazz] @ r0<- thisPtr->clazz
+ bl dvmFindInterfaceMethodInCache @ r0<- call(class, ref, method, dex)
+ cmp r0, #0 @ failed?
+ beq common_exceptionThrown @ yes, handle exception
+ b common_invokeMethodNoRange @ jump to common handler
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_73: /* 0x73 */
+/* File: armv5te/OP_UNUSED_73.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_VIRTUAL_RANGE: /* 0x74 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_RANGE.S */
+/* File: armv5te/OP_INVOKE_VIRTUAL.S */
+ /*
+ * Handle a virtual method call.
+ *
+ * for: invoke-virtual, invoke-virtual/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved baseMethod
+ .if (!1)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ must export for invoke
+ bne .LOP_INVOKE_VIRTUAL_RANGE_continue @ yes, continue on
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ mov r2, #METHOD_VIRTUAL @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ bne .LOP_INVOKE_VIRTUAL_RANGE_continue @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_SUPER_RANGE: /* 0x75 */
+/* File: armv5te/OP_INVOKE_SUPER_RANGE.S */
+/* File: armv5te/OP_INVOKE_SUPER.S */
+ /*
+ * Handle a "super" method call.
+ *
+ * for: invoke-super, invoke-super/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ .if (!1)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ GET_VREG(r2, r10) @ r2<- "this" ptr
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved baseMethod
+ cmp r2, #0 @ null "this"?
+ ldr r9, [rGLUE, #offGlue_method] @ r9<- current method
+ beq common_errNullObject @ null "this", throw exception
+ cmp r0, #0 @ already resolved?
+ ldr r9, [r9, #offMethod_clazz] @ r9<- method->clazz
+ EXPORT_PC() @ must export for invoke
+ bne .LOP_INVOKE_SUPER_RANGE_continue @ resolved, continue on
+ b .LOP_INVOKE_SUPER_RANGE_resolve @ do resolve now
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_DIRECT_RANGE: /* 0x76 */
+/* File: armv5te/OP_INVOKE_DIRECT_RANGE.S */
+/* File: armv5te/OP_INVOKE_DIRECT.S */
+ /*
+ * Handle a direct method call.
+ *
+ * (We could defer the "is 'this' pointer null" test to the common
+ * method invocation code, and use a flag to indicate that static
+ * calls don't count. If we do this as part of copying the arguments
+ * out we could avoiding loading the first arg twice.)
+ *
+ * for: invoke-direct, invoke-direct/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved methodToCall
+ .if (!1)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ must export for invoke
+ GET_VREG(r2, r10) @ r2<- "this" ptr
+ beq .LOP_INVOKE_DIRECT_RANGE_resolve @ not resolved, do it now
+.LOP_INVOKE_DIRECT_RANGE_finish:
+ cmp r2, #0 @ null "this" ref?
+ bne common_invokeMethodRange @ no, continue on
+ b common_errNullObject @ yes, throw exception
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_STATIC_RANGE: /* 0x77 */
+/* File: armv5te/OP_INVOKE_STATIC_RANGE.S */
+/* File: armv5te/OP_INVOKE_STATIC.S */
+ /*
+ * Handle a static method call.
+ *
+ * for: invoke-static, invoke-static/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved methodToCall
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ must export for invoke
+ bne common_invokeMethodRange @ yes, continue on
+0: ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ mov r2, #METHOD_STATIC @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ bne common_invokeMethodRange @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_INTERFACE_RANGE: /* 0x78 */
+/* File: armv5te/OP_INVOKE_INTERFACE_RANGE.S */
+/* File: armv5te/OP_INVOKE_INTERFACE.S */
+ /*
+ * Handle an interface method call.
+ *
+ * for: invoke-interface, invoke-interface/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r2, 2) @ r2<- FEDC or CCCC
+ FETCH(r1, 1) @ r1<- BBBB
+ .if (!1)
+ and r2, r2, #15 @ r2<- C (or stays CCCC)
+ .endif
+ EXPORT_PC() @ must export for invoke
+ GET_VREG(r0, r2) @ r0<- first arg ("this")
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- methodClassDex
+ cmp r0, #0 @ null obj?
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- method
+ beq common_errNullObject @ yes, fail
+ ldr r0, [r0, #offObject_clazz] @ r0<- thisPtr->clazz
+ bl dvmFindInterfaceMethodInCache @ r0<- call(class, ref, method, dex)
+ cmp r0, #0 @ failed?
+ beq common_exceptionThrown @ yes, handle exception
+ b common_invokeMethodRange @ jump to common handler
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_79: /* 0x79 */
+/* File: armv5te/OP_UNUSED_79.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_7A: /* 0x7a */
+/* File: armv5te/OP_UNUSED_7A.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEG_INT: /* 0x7b */
+/* File: armv6t2/OP_NEG_INT.S */
+/* File: armv6t2/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0".
+ * This could be an ARM instruction or a function call.
+ *
+ * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+ * int-to-byte, int-to-char, int-to-short
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r0, r3) @ r0<- vB
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ rsb r0, r0, #0 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 8-9 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NOT_INT: /* 0x7c */
+/* File: armv6t2/OP_NOT_INT.S */
+/* File: armv6t2/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0".
+ * This could be an ARM instruction or a function call.
+ *
+ * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+ * int-to-byte, int-to-char, int-to-short
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r0, r3) @ r0<- vB
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ mvn r0, r0 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 8-9 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEG_LONG: /* 0x7d */
+/* File: armv6t2/OP_NEG_LONG.S */
+/* File: armv6t2/unopWide.S */
+ /*
+ * Generic 64-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0/r1".
+ * This could be an ARM instruction or a function call.
+ *
+ * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r3, {r0-r1} @ r0/r1<- vAA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ rsbs r0, r0, #0 @ optional op; may set condition codes
+ rsc r1, r1, #0 @ r0/r1<- op, r2-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-11 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NOT_LONG: /* 0x7e */
+/* File: armv6t2/OP_NOT_LONG.S */
+/* File: armv6t2/unopWide.S */
+ /*
+ * Generic 64-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0/r1".
+ * This could be an ARM instruction or a function call.
+ *
+ * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r3, {r0-r1} @ r0/r1<- vAA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ mvn r0, r0 @ optional op; may set condition codes
+ mvn r1, r1 @ r0/r1<- op, r2-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-11 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEG_FLOAT: /* 0x7f */
+/* File: armv6t2/OP_NEG_FLOAT.S */
+/* File: armv6t2/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0".
+ * This could be an ARM instruction or a function call.
+ *
+ * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+ * int-to-byte, int-to-char, int-to-short
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r0, r3) @ r0<- vB
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ add r0, r0, #0x80000000 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 8-9 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEG_DOUBLE: /* 0x80 */
+/* File: armv6t2/OP_NEG_DOUBLE.S */
+/* File: armv6t2/unopWide.S */
+ /*
+ * Generic 64-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0/r1".
+ * This could be an ARM instruction or a function call.
+ *
+ * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r3, {r0-r1} @ r0/r1<- vAA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ add r1, r1, #0x80000000 @ r0/r1<- op, r2-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-11 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_LONG: /* 0x81 */
+/* File: armv6t2/OP_INT_TO_LONG.S */
+/* File: armv6t2/unopWider.S */
+ /*
+ * Generic 32bit-to-64bit unary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = op r0", where
+ * "result" is a 64-bit quantity in r0/r1.
+ *
+ * For: int-to-long, int-to-double, float-to-long, float-to-double
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r0, r3) @ r0<- vB
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ mov r1, r0, asr #31 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vA/vA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 9-10 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_FLOAT: /* 0x82 */
+/* File: arm-vfp/OP_INT_TO_FLOAT.S */
+/* File: arm-vfp/funop.S */
+ /*
+ * Generic 32-bit unary floating-point operation. Provide an "instr"
+ * line that specifies an instruction that performs "s1 = op s0".
+ *
+ * for: int-to-float, float-to-int
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ flds s0, [r3] @ s0<- vB
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ and r9, r9, #15 @ r9<- A
+ fsitos s1, s0 @ s1<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ fsts s1, [r9] @ vA<- s1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_DOUBLE: /* 0x83 */
+/* File: arm-vfp/OP_INT_TO_DOUBLE.S */
+/* File: arm-vfp/funopWider.S */
+ /*
+ * Generic 32bit-to-64bit floating point unary operation. Provide an
+ * "instr" line that specifies an instruction that performs "d0 = op s0".
+ *
+ * For: int-to-double, float-to-double
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ flds s0, [r3] @ s0<- vB
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ and r9, r9, #15 @ r9<- A
+ fsitod d0, s0 @ d0<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ fstd d0, [r9] @ vA<- d0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_LONG_TO_INT: /* 0x84 */
+/* File: armv5te/OP_LONG_TO_INT.S */
+/* we ignore the high word, making this equivalent to a 32-bit reg move */
+/* File: armv5te/OP_MOVE.S */
+ /* for move, move-object, long-to-int */
+ /* op vA, vB */
+ mov r1, rINST, lsr #12 @ r1<- B from 15:12
+ mov r0, rINST, lsr #8 @ r0<- A from 11:8
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[B]
+ and r0, r0, #15
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ SET_VREG(r2, r0) @ fp[A]<- r2
+ GOTO_OPCODE(ip) @ execute next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_LONG_TO_FLOAT: /* 0x85 */
+/* File: armv6t2/OP_LONG_TO_FLOAT.S */
+/* File: armv6t2/unopNarrower.S */
+ /*
+ * Generic 64bit-to-32bit unary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = op r0/r1", where
+ * "result" is a 32-bit quantity in r0.
+ *
+ * For: long-to-float, double-to-int, double-to-float
+ *
+ * (This would work for long-to-int, but that instruction is actually
+ * an exact match for OP_MOVE.)
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ ldmia r3, {r0-r1} @ r0/r1<- vB/vB+1
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl __aeabi_l2f @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 9-10 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_LONG_TO_DOUBLE: /* 0x86 */
+/* File: armv6t2/OP_LONG_TO_DOUBLE.S */
+/* File: armv6t2/unopWide.S */
+ /*
+ * Generic 64-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0/r1".
+ * This could be an ARM instruction or a function call.
+ *
+ * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r3, {r0-r1} @ r0/r1<- vAA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl __aeabi_l2d @ r0/r1<- op, r2-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-11 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FLOAT_TO_INT: /* 0x87 */
+/* File: arm-vfp/OP_FLOAT_TO_INT.S */
+/* File: arm-vfp/funop.S */
+ /*
+ * Generic 32-bit unary floating-point operation. Provide an "instr"
+ * line that specifies an instruction that performs "s1 = op s0".
+ *
+ * for: int-to-float, float-to-int
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ flds s0, [r3] @ s0<- vB
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ and r9, r9, #15 @ r9<- A
+ ftosizs s1, s0 @ s1<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ fsts s1, [r9] @ vA<- s1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FLOAT_TO_LONG: /* 0x88 */
+/* File: armv6t2/OP_FLOAT_TO_LONG.S */
+@include "armv6t2/unopWider.S" {"instr":"bl __aeabi_f2lz"}
+/* File: armv6t2/unopWider.S */
+ /*
+ * Generic 32bit-to-64bit unary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = op r0", where
+ * "result" is a 64-bit quantity in r0/r1.
+ *
+ * For: int-to-long, int-to-double, float-to-long, float-to-double
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r0, r3) @ r0<- vB
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ bl f2l_doconv @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vA/vA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 9-10 instructions */
+
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FLOAT_TO_DOUBLE: /* 0x89 */
+/* File: arm-vfp/OP_FLOAT_TO_DOUBLE.S */
+/* File: arm-vfp/funopWider.S */
+ /*
+ * Generic 32bit-to-64bit floating point unary operation. Provide an
+ * "instr" line that specifies an instruction that performs "d0 = op s0".
+ *
+ * For: int-to-double, float-to-double
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ flds s0, [r3] @ s0<- vB
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ and r9, r9, #15 @ r9<- A
+ fcvtds d0, s0 @ d0<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ fstd d0, [r9] @ vA<- d0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DOUBLE_TO_INT: /* 0x8a */
+/* File: arm-vfp/OP_DOUBLE_TO_INT.S */
+/* File: arm-vfp/funopNarrower.S */
+ /*
+ * Generic 64bit-to-32bit unary floating point operation. Provide an
+ * "instr" line that specifies an instruction that performs "s0 = op d0".
+ *
+ * For: double-to-int, double-to-float
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ fldd d0, [r3] @ d0<- vB
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ and r9, r9, #15 @ r9<- A
+ ftosizd s0, d0 @ s0<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ fsts s0, [r9] @ vA<- s0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DOUBLE_TO_LONG: /* 0x8b */
+/* File: armv6t2/OP_DOUBLE_TO_LONG.S */
+@include "armv6t2/unopWide.S" {"instr":"bl __aeabi_d2lz"}
+/* File: armv6t2/unopWide.S */
+ /*
+ * Generic 64-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0/r1".
+ * This could be an ARM instruction or a function call.
+ *
+ * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r3, {r0-r1} @ r0/r1<- vAA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl d2l_doconv @ r0/r1<- op, r2-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-11 instructions */
+
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DOUBLE_TO_FLOAT: /* 0x8c */
+/* File: arm-vfp/OP_DOUBLE_TO_FLOAT.S */
+/* File: arm-vfp/funopNarrower.S */
+ /*
+ * Generic 64bit-to-32bit unary floating point operation. Provide an
+ * "instr" line that specifies an instruction that performs "s0 = op d0".
+ *
+ * For: double-to-int, double-to-float
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ fldd d0, [r3] @ d0<- vB
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ and r9, r9, #15 @ r9<- A
+ fcvtsd s0, d0 @ s0<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ fsts s0, [r9] @ vA<- s0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_BYTE: /* 0x8d */
+/* File: armv6t2/OP_INT_TO_BYTE.S */
+/* File: armv6t2/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0".
+ * This could be an ARM instruction or a function call.
+ *
+ * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+ * int-to-byte, int-to-char, int-to-short
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r0, r3) @ r0<- vB
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ sxtb r0, r0 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 8-9 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_CHAR: /* 0x8e */
+/* File: armv6t2/OP_INT_TO_CHAR.S */
+/* File: armv6t2/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0".
+ * This could be an ARM instruction or a function call.
+ *
+ * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+ * int-to-byte, int-to-char, int-to-short
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r0, r3) @ r0<- vB
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ uxth r0, r0 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 8-9 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_SHORT: /* 0x8f */
+/* File: armv6t2/OP_INT_TO_SHORT.S */
+/* File: armv6t2/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0".
+ * This could be an ARM instruction or a function call.
+ *
+ * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+ * int-to-byte, int-to-char, int-to-short
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r0, r3) @ r0<- vB
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ sxth r0, r0 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 8-9 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_INT: /* 0x90 */
+/* File: armv5te/OP_ADD_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ add r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_INT: /* 0x91 */
+/* File: armv5te/OP_SUB_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ sub r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_INT: /* 0x92 */
+/* File: armv5te/OP_MUL_INT.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ mul r0, r1, r0 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_INT: /* 0x93 */
+/* File: armv5te/OP_DIV_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 1
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl __aeabi_idiv @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_INT: /* 0x94 */
+/* File: armv5te/OP_REM_INT.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 1
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl __aeabi_idivmod @ r1<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r1, r9) @ vAA<- r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_INT: /* 0x95 */
+/* File: armv5te/OP_AND_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ and r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_INT: /* 0x96 */
+/* File: armv5te/OP_OR_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ orr r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_INT: /* 0x97 */
+/* File: armv5te/OP_XOR_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ eor r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHL_INT: /* 0x98 */
+/* File: armv5te/OP_SHL_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, asl r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHR_INT: /* 0x99 */
+/* File: armv5te/OP_SHR_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, asr r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_USHR_INT: /* 0x9a */
+/* File: armv5te/OP_USHR_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, lsr r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_LONG: /* 0x9b */
+/* File: armv5te/OP_ADD_LONG.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ adds r0, r0, r2 @ optional op; may set condition codes
+ adc r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_LONG: /* 0x9c */
+/* File: armv5te/OP_SUB_LONG.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ subs r0, r0, r2 @ optional op; may set condition codes
+ sbc r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_LONG: /* 0x9d */
+/* File: armv5te/OP_MUL_LONG.S */
+ /*
+ * Signed 64-bit integer multiply.
+ *
+ * Consider WXxYZ (r1r0 x r3r2) with a long multiply:
+ * WX
+ * x YZ
+ * --------
+ * ZW ZX
+ * YW YX
+ *
+ * The low word of the result holds ZX, the high word holds
+ * (ZW+YX) + (the high overflow from ZX). YW doesn't matter because
+ * it doesn't fit in the low 64 bits.
+ *
+ * Unlike most ARM math operations, multiply instructions have
+ * restrictions on using the same register more than once (Rd and Rm
+ * cannot be the same).
+ */
+ /* mul-long vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ mul ip, r2, r1 @ ip<- ZxW
+ umull r9, r10, r2, r0 @ r9/r10 <- ZxX
+ mla r2, r0, r3, ip @ r2<- YxX + (ZxW)
+ mov r0, rINST, lsr #8 @ r0<- AA
+ add r10, r2, r10 @ r10<- r10 + low(ZxW + (YxX))
+ add r0, rFP, r0, lsl #2 @ r0<- &fp[AA]
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ b .LOP_MUL_LONG_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_LONG: /* 0x9e */
+/* File: armv5te/OP_DIV_LONG.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 1
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_ldivmod @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_LONG: /* 0x9f */
+/* File: armv5te/OP_REM_LONG.S */
+/* ldivmod returns quotient in r0/r1 and remainder in r2/r3 */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 1
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_ldivmod @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r2,r3} @ vAA/vAA+1<- r2/r3
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_LONG: /* 0xa0 */
+/* File: armv5te/OP_AND_LONG.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ and r0, r0, r2 @ optional op; may set condition codes
+ and r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_LONG: /* 0xa1 */
+/* File: armv5te/OP_OR_LONG.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ orr r0, r0, r2 @ optional op; may set condition codes
+ orr r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_LONG: /* 0xa2 */
+/* File: armv5te/OP_XOR_LONG.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ eor r0, r0, r2 @ optional op; may set condition codes
+ eor r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHL_LONG: /* 0xa3 */
+/* File: armv5te/OP_SHL_LONG.S */
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to mask off the low
+ * 6 bits of the shift distance.
+ */
+ /* shl-long vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r3, r0, #255 @ r3<- BB
+ mov r0, r0, lsr #8 @ r0<- CC
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[BB]
+ GET_VREG(r2, r0) @ r2<- vCC
+ ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1
+ and r2, r2, #63 @ r2<- r2 & 0x3f
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+
+ mov r1, r1, asl r2 @ r1<- r1 << r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ b .LOP_SHL_LONG_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHR_LONG: /* 0xa4 */
+/* File: armv5te/OP_SHR_LONG.S */
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to mask off the low
+ * 6 bits of the shift distance.
+ */
+ /* shr-long vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r3, r0, #255 @ r3<- BB
+ mov r0, r0, lsr #8 @ r0<- CC
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[BB]
+ GET_VREG(r2, r0) @ r2<- vCC
+ ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1
+ and r2, r2, #63 @ r0<- r0 & 0x3f
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ b .LOP_SHR_LONG_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_USHR_LONG: /* 0xa5 */
+/* File: armv5te/OP_USHR_LONG.S */
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to mask off the low
+ * 6 bits of the shift distance.
+ */
+ /* ushr-long vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r3, r0, #255 @ r3<- BB
+ mov r0, r0, lsr #8 @ r0<- CC
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[BB]
+ GET_VREG(r2, r0) @ r2<- vCC
+ ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1
+ and r2, r2, #63 @ r0<- r0 & 0x3f
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ b .LOP_USHR_LONG_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_FLOAT: /* 0xa6 */
+/* File: arm-vfp/OP_ADD_FLOAT.S */
+/* File: arm-vfp/fbinop.S */
+ /*
+ * Generic 32-bit floating-point operation. Provide an "instr" line that
+ * specifies an instruction that performs "s2 = s0 op s1". Because we
+ * use the "softfp" ABI, this must be an instruction, not a function call.
+ *
+ * For: add-float, sub-float, mul-float, div-float
+ */
+ /* floatop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ flds s1, [r3] @ s1<- vCC
+ flds s0, [r2] @ s0<- vBB
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ fadds s2, s0, s1 @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vAA
+ fsts s2, [r9] @ vAA<- s2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_FLOAT: /* 0xa7 */
+/* File: arm-vfp/OP_SUB_FLOAT.S */
+/* File: arm-vfp/fbinop.S */
+ /*
+ * Generic 32-bit floating-point operation. Provide an "instr" line that
+ * specifies an instruction that performs "s2 = s0 op s1". Because we
+ * use the "softfp" ABI, this must be an instruction, not a function call.
+ *
+ * For: add-float, sub-float, mul-float, div-float
+ */
+ /* floatop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ flds s1, [r3] @ s1<- vCC
+ flds s0, [r2] @ s0<- vBB
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ fsubs s2, s0, s1 @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vAA
+ fsts s2, [r9] @ vAA<- s2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_FLOAT: /* 0xa8 */
+/* File: arm-vfp/OP_MUL_FLOAT.S */
+/* File: arm-vfp/fbinop.S */
+ /*
+ * Generic 32-bit floating-point operation. Provide an "instr" line that
+ * specifies an instruction that performs "s2 = s0 op s1". Because we
+ * use the "softfp" ABI, this must be an instruction, not a function call.
+ *
+ * For: add-float, sub-float, mul-float, div-float
+ */
+ /* floatop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ flds s1, [r3] @ s1<- vCC
+ flds s0, [r2] @ s0<- vBB
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ fmuls s2, s0, s1 @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vAA
+ fsts s2, [r9] @ vAA<- s2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_FLOAT: /* 0xa9 */
+/* File: arm-vfp/OP_DIV_FLOAT.S */
+/* File: arm-vfp/fbinop.S */
+ /*
+ * Generic 32-bit floating-point operation. Provide an "instr" line that
+ * specifies an instruction that performs "s2 = s0 op s1". Because we
+ * use the "softfp" ABI, this must be an instruction, not a function call.
+ *
+ * For: add-float, sub-float, mul-float, div-float
+ */
+ /* floatop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ flds s1, [r3] @ s1<- vCC
+ flds s0, [r2] @ s0<- vBB
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ fdivs s2, s0, s1 @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vAA
+ fsts s2, [r9] @ vAA<- s2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_FLOAT: /* 0xaa */
+/* File: armv5te/OP_REM_FLOAT.S */
+/* EABI doesn't define a float remainder function, but libm does */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl fmodf @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_DOUBLE: /* 0xab */
+/* File: arm-vfp/OP_ADD_DOUBLE.S */
+/* File: arm-vfp/fbinopWide.S */
+ /*
+ * Generic 64-bit double-precision floating point binary operation.
+ * Provide an "instr" line that specifies an instruction that performs
+ * "d2 = d0 op d1".
+ *
+ * for: add-double, sub-double, mul-double, div-double
+ */
+ /* doubleop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ fldd d1, [r3] @ d1<- vCC
+ fldd d0, [r2] @ d0<- vBB
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ faddd d2, d0, d1 @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vAA
+ fstd d2, [r9] @ vAA<- d2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_DOUBLE: /* 0xac */
+/* File: arm-vfp/OP_SUB_DOUBLE.S */
+/* File: arm-vfp/fbinopWide.S */
+ /*
+ * Generic 64-bit double-precision floating point binary operation.
+ * Provide an "instr" line that specifies an instruction that performs
+ * "d2 = d0 op d1".
+ *
+ * for: add-double, sub-double, mul-double, div-double
+ */
+ /* doubleop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ fldd d1, [r3] @ d1<- vCC
+ fldd d0, [r2] @ d0<- vBB
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ fsubd d2, d0, d1 @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vAA
+ fstd d2, [r9] @ vAA<- d2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_DOUBLE: /* 0xad */
+/* File: arm-vfp/OP_MUL_DOUBLE.S */
+/* File: arm-vfp/fbinopWide.S */
+ /*
+ * Generic 64-bit double-precision floating point binary operation.
+ * Provide an "instr" line that specifies an instruction that performs
+ * "d2 = d0 op d1".
+ *
+ * for: add-double, sub-double, mul-double, div-double
+ */
+ /* doubleop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ fldd d1, [r3] @ d1<- vCC
+ fldd d0, [r2] @ d0<- vBB
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ fmuld d2, d0, d1 @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vAA
+ fstd d2, [r9] @ vAA<- d2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_DOUBLE: /* 0xae */
+/* File: arm-vfp/OP_DIV_DOUBLE.S */
+/* File: arm-vfp/fbinopWide.S */
+ /*
+ * Generic 64-bit double-precision floating point binary operation.
+ * Provide an "instr" line that specifies an instruction that performs
+ * "d2 = d0 op d1".
+ *
+ * for: add-double, sub-double, mul-double, div-double
+ */
+ /* doubleop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ fldd d1, [r3] @ d1<- vCC
+ fldd d0, [r2] @ d0<- vBB
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ fdivd d2, d0, d1 @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vAA
+ fstd d2, [r9] @ vAA<- d2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_DOUBLE: /* 0xaf */
+/* File: armv5te/OP_REM_DOUBLE.S */
+/* EABI doesn't define a double remainder function, but libm does */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl fmod @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_INT_2ADDR: /* 0xb0 */
+/* File: armv6t2/OP_ADD_INT_2ADDR.S */
+/* File: armv6t2/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ add r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_INT_2ADDR: /* 0xb1 */
+/* File: armv6t2/OP_SUB_INT_2ADDR.S */
+/* File: armv6t2/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ sub r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_INT_2ADDR: /* 0xb2 */
+/* File: armv6t2/OP_MUL_INT_2ADDR.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv6t2/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ mul r0, r1, r0 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_INT_2ADDR: /* 0xb3 */
+/* File: armv6t2/OP_DIV_INT_2ADDR.S */
+/* File: armv6t2/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 1
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_idiv @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_INT_2ADDR: /* 0xb4 */
+/* File: armv6t2/OP_REM_INT_2ADDR.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* File: armv6t2/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 1
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_idivmod @ r1<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r1, r9) @ vAA<- r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_INT_2ADDR: /* 0xb5 */
+/* File: armv6t2/OP_AND_INT_2ADDR.S */
+/* File: armv6t2/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ and r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_INT_2ADDR: /* 0xb6 */
+/* File: armv6t2/OP_OR_INT_2ADDR.S */
+/* File: armv6t2/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ orr r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_INT_2ADDR: /* 0xb7 */
+/* File: armv6t2/OP_XOR_INT_2ADDR.S */
+/* File: armv6t2/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ eor r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHL_INT_2ADDR: /* 0xb8 */
+/* File: armv6t2/OP_SHL_INT_2ADDR.S */
+/* File: armv6t2/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, asl r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHR_INT_2ADDR: /* 0xb9 */
+/* File: armv6t2/OP_SHR_INT_2ADDR.S */
+/* File: armv6t2/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, asr r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_USHR_INT_2ADDR: /* 0xba */
+/* File: armv6t2/OP_USHR_INT_2ADDR.S */
+/* File: armv6t2/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, lsr r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_LONG_2ADDR: /* 0xbb */
+/* File: armv6t2/OP_ADD_LONG_2ADDR.S */
+/* File: armv6t2/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ adds r0, r0, r2 @ optional op; may set condition codes
+ adc r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_LONG_2ADDR: /* 0xbc */
+/* File: armv6t2/OP_SUB_LONG_2ADDR.S */
+/* File: armv6t2/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ subs r0, r0, r2 @ optional op; may set condition codes
+ sbc r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_LONG_2ADDR: /* 0xbd */
+/* File: armv6t2/OP_MUL_LONG_2ADDR.S */
+ /*
+ * Signed 64-bit integer multiply, "/2addr" version.
+ *
+ * See OP_MUL_LONG for an explanation.
+ *
+ * We get a little tight on registers, so to avoid looking up &fp[A]
+ * again we stuff it into rINST.
+ */
+ /* mul-long/2addr vA, vB */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add rINST, rFP, r9, lsl #2 @ rINST<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia rINST, {r0-r1} @ r0/r1<- vAA/vAA+1
+ mul ip, r2, r1 @ ip<- ZxW
+ umull r9, r10, r2, r0 @ r9/r10 <- ZxX
+ mla r2, r0, r3, ip @ r2<- YxX + (ZxW)
+ mov r0, rINST @ r0<- &fp[A] (free up rINST)
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ add r10, r2, r10 @ r10<- r10 + low(ZxW + (YxX))
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r0, {r9-r10} @ vAA/vAA+1<- r9/r10
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_LONG_2ADDR: /* 0xbe */
+/* File: armv6t2/OP_DIV_LONG_2ADDR.S */
+/* File: armv6t2/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 1
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_ldivmod @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_LONG_2ADDR: /* 0xbf */
+/* File: armv6t2/OP_REM_LONG_2ADDR.S */
+/* ldivmod returns quotient in r0/r1 and remainder in r2/r3 */
+/* File: armv6t2/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 1
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_ldivmod @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r2,r3} @ vAA/vAA+1<- r2/r3
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_LONG_2ADDR: /* 0xc0 */
+/* File: armv6t2/OP_AND_LONG_2ADDR.S */
+/* File: armv6t2/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ and r0, r0, r2 @ optional op; may set condition codes
+ and r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_LONG_2ADDR: /* 0xc1 */
+/* File: armv6t2/OP_OR_LONG_2ADDR.S */
+/* File: armv6t2/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ orr r0, r0, r2 @ optional op; may set condition codes
+ orr r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_LONG_2ADDR: /* 0xc2 */
+/* File: armv6t2/OP_XOR_LONG_2ADDR.S */
+/* File: armv6t2/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ eor r0, r0, r2 @ optional op; may set condition codes
+ eor r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHL_LONG_2ADDR: /* 0xc3 */
+/* File: armv6t2/OP_SHL_LONG_2ADDR.S */
+ /*
+ * Long integer shift, 2addr version. vA is 64-bit value/result, vB is
+ * 32-bit shift distance.
+ */
+ /* shl-long/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r2, r3) @ r2<- vB
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ and r2, r2, #63 @ r2<- r2 & 0x3f
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+
+ mov r1, r1, asl r2 @ r1<- r1 << r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32)
+ mov r0, r0, asl r2 @ r0<- r0 << r2
+ b .LOP_SHL_LONG_2ADDR_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHR_LONG_2ADDR: /* 0xc4 */
+/* File: armv6t2/OP_SHR_LONG_2ADDR.S */
+ /*
+ * Long integer shift, 2addr version. vA is 64-bit value/result, vB is
+ * 32-bit shift distance.
+ */
+ /* shr-long/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r2, r3) @ r2<- vB
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ and r2, r2, #63 @ r2<- r2 & 0x3f
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32)
+ mov r1, r1, asr r2 @ r1<- r1 >> r2
+ b .LOP_SHR_LONG_2ADDR_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_USHR_LONG_2ADDR: /* 0xc5 */
+/* File: armv6t2/OP_USHR_LONG_2ADDR.S */
+ /*
+ * Long integer shift, 2addr version. vA is 64-bit value/result, vB is
+ * 32-bit shift distance.
+ */
+ /* ushr-long/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r2, r3) @ r2<- vB
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ and r2, r2, #63 @ r2<- r2 & 0x3f
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32)
+ mov r1, r1, lsr r2 @ r1<- r1 >>> r2
+ b .LOP_USHR_LONG_2ADDR_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_FLOAT_2ADDR: /* 0xc6 */
+/* File: arm-vfp/OP_ADD_FLOAT_2ADDR.S */
+/* File: arm-vfp/fbinop2addr.S */
+ /*
+ * Generic 32-bit floating point "/2addr" binary operation. Provide
+ * an "instr" line that specifies an instruction that performs
+ * "s2 = s0 op s1".
+ *
+ * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ and r9, r9, #15 @ r9<- A
+ flds s1, [r3] @ s1<- vB
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ flds s0, [r9] @ s0<- vA
+
+ fadds s2, s0, s1 @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fsts s2, [r9] @ vAA<- s2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_FLOAT_2ADDR: /* 0xc7 */
+/* File: arm-vfp/OP_SUB_FLOAT_2ADDR.S */
+/* File: arm-vfp/fbinop2addr.S */
+ /*
+ * Generic 32-bit floating point "/2addr" binary operation. Provide
+ * an "instr" line that specifies an instruction that performs
+ * "s2 = s0 op s1".
+ *
+ * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ and r9, r9, #15 @ r9<- A
+ flds s1, [r3] @ s1<- vB
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ flds s0, [r9] @ s0<- vA
+
+ fsubs s2, s0, s1 @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fsts s2, [r9] @ vAA<- s2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_FLOAT_2ADDR: /* 0xc8 */
+/* File: arm-vfp/OP_MUL_FLOAT_2ADDR.S */
+/* File: arm-vfp/fbinop2addr.S */
+ /*
+ * Generic 32-bit floating point "/2addr" binary operation. Provide
+ * an "instr" line that specifies an instruction that performs
+ * "s2 = s0 op s1".
+ *
+ * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ and r9, r9, #15 @ r9<- A
+ flds s1, [r3] @ s1<- vB
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ flds s0, [r9] @ s0<- vA
+
+ fmuls s2, s0, s1 @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fsts s2, [r9] @ vAA<- s2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_FLOAT_2ADDR: /* 0xc9 */
+/* File: arm-vfp/OP_DIV_FLOAT_2ADDR.S */
+/* File: arm-vfp/fbinop2addr.S */
+ /*
+ * Generic 32-bit floating point "/2addr" binary operation. Provide
+ * an "instr" line that specifies an instruction that performs
+ * "s2 = s0 op s1".
+ *
+ * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ and r9, r9, #15 @ r9<- A
+ flds s1, [r3] @ s1<- vB
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ flds s0, [r9] @ s0<- vA
+
+ fdivs s2, s0, s1 @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fsts s2, [r9] @ vAA<- s2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_FLOAT_2ADDR: /* 0xca */
+/* File: armv6t2/OP_REM_FLOAT_2ADDR.S */
+/* EABI doesn't define a float remainder function, but libm does */
+/* File: armv6t2/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl fmodf @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_DOUBLE_2ADDR: /* 0xcb */
+/* File: arm-vfp/OP_ADD_DOUBLE_2ADDR.S */
+/* File: arm-vfp/fbinopWide2addr.S */
+ /*
+ * Generic 64-bit floating point "/2addr" binary operation. Provide
+ * an "instr" line that specifies an instruction that performs
+ * "d2 = d0 op d1".
+ *
+ * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+ * div-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ and r9, r9, #15 @ r9<- A
+ fldd d1, [r3] @ d1<- vB
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ fldd d0, [r9] @ d0<- vA
+
+ faddd d2, d0, d1 @ d2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fstd d2, [r9] @ vAA<- d2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_DOUBLE_2ADDR: /* 0xcc */
+/* File: arm-vfp/OP_SUB_DOUBLE_2ADDR.S */
+/* File: arm-vfp/fbinopWide2addr.S */
+ /*
+ * Generic 64-bit floating point "/2addr" binary operation. Provide
+ * an "instr" line that specifies an instruction that performs
+ * "d2 = d0 op d1".
+ *
+ * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+ * div-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ and r9, r9, #15 @ r9<- A
+ fldd d1, [r3] @ d1<- vB
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ fldd d0, [r9] @ d0<- vA
+
+ fsubd d2, d0, d1 @ d2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fstd d2, [r9] @ vAA<- d2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_DOUBLE_2ADDR: /* 0xcd */
+/* File: arm-vfp/OP_MUL_DOUBLE_2ADDR.S */
+/* File: arm-vfp/fbinopWide2addr.S */
+ /*
+ * Generic 64-bit floating point "/2addr" binary operation. Provide
+ * an "instr" line that specifies an instruction that performs
+ * "d2 = d0 op d1".
+ *
+ * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+ * div-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ and r9, r9, #15 @ r9<- A
+ fldd d1, [r3] @ d1<- vB
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ fldd d0, [r9] @ d0<- vA
+
+ fmuld d2, d0, d1 @ d2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fstd d2, [r9] @ vAA<- d2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_DOUBLE_2ADDR: /* 0xce */
+/* File: arm-vfp/OP_DIV_DOUBLE_2ADDR.S */
+/* File: arm-vfp/fbinopWide2addr.S */
+ /*
+ * Generic 64-bit floating point "/2addr" binary operation. Provide
+ * an "instr" line that specifies an instruction that performs
+ * "d2 = d0 op d1".
+ *
+ * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+ * div-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ and r9, r9, #15 @ r9<- A
+ fldd d1, [r3] @ d1<- vB
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ fldd d0, [r9] @ d0<- vA
+
+ fdivd d2, d0, d1 @ d2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fstd d2, [r9] @ vAA<- d2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_DOUBLE_2ADDR: /* 0xcf */
+/* File: armv6t2/OP_REM_DOUBLE_2ADDR.S */
+/* EABI doesn't define a double remainder function, but libm does */
+/* File: armv6t2/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl fmod @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_INT_LIT16: /* 0xd0 */
+/* File: armv6t2/OP_ADD_INT_LIT16.S */
+/* File: armv6t2/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r0, r2) @ r0<- vB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ add r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RSUB_INT: /* 0xd1 */
+/* File: armv6t2/OP_RSUB_INT.S */
+/* this op is "rsub-int", but can be thought of as "rsub-int/lit16" */
+/* File: armv6t2/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r0, r2) @ r0<- vB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ rsb r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_INT_LIT16: /* 0xd2 */
+/* File: armv6t2/OP_MUL_INT_LIT16.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv6t2/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r0, r2) @ r0<- vB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ mul r0, r1, r0 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_INT_LIT16: /* 0xd3 */
+/* File: armv6t2/OP_DIV_INT_LIT16.S */
+/* File: armv6t2/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r0, r2) @ r0<- vB
+ .if 1
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ bl __aeabi_idiv @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_INT_LIT16: /* 0xd4 */
+/* File: armv6t2/OP_REM_INT_LIT16.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* File: armv6t2/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r0, r2) @ r0<- vB
+ .if 1
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ bl __aeabi_idivmod @ r1<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r1, r9) @ vAA<- r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_INT_LIT16: /* 0xd5 */
+/* File: armv6t2/OP_AND_INT_LIT16.S */
+/* File: armv6t2/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r0, r2) @ r0<- vB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ and r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_INT_LIT16: /* 0xd6 */
+/* File: armv6t2/OP_OR_INT_LIT16.S */
+/* File: armv6t2/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r0, r2) @ r0<- vB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ orr r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_INT_LIT16: /* 0xd7 */
+/* File: armv6t2/OP_XOR_INT_LIT16.S */
+/* File: armv6t2/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r0, r2) @ r0<- vB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ eor r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_INT_LIT8: /* 0xd8 */
+/* File: armv5te/OP_ADD_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ add r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RSUB_INT_LIT8: /* 0xd9 */
+/* File: armv5te/OP_RSUB_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ rsb r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_INT_LIT8: /* 0xda */
+/* File: armv5te/OP_MUL_INT_LIT8.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ mul r0, r1, r0 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_INT_LIT8: /* 0xdb */
+/* File: armv5te/OP_DIV_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 1
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_idiv @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_INT_LIT8: /* 0xdc */
+/* File: armv5te/OP_REM_INT_LIT8.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 1
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_idivmod @ r1<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r1, r9) @ vAA<- r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_INT_LIT8: /* 0xdd */
+/* File: armv5te/OP_AND_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ and r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_INT_LIT8: /* 0xde */
+/* File: armv5te/OP_OR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ orr r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_INT_LIT8: /* 0xdf */
+/* File: armv5te/OP_XOR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ eor r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHL_INT_LIT8: /* 0xe0 */
+/* File: armv5te/OP_SHL_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, asl r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHR_INT_LIT8: /* 0xe1 */
+/* File: armv5te/OP_SHR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, asr r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_USHR_INT_LIT8: /* 0xe2 */
+/* File: armv5te/OP_USHR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, lsr r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_VOLATILE: /* 0xe3 */
+/* File: armv5te/OP_IGET_VOLATILE.S */
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_VOLATILE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_VOLATILE_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_VOLATILE: /* 0xe4 */
+/* File: armv5te/OP_IPUT_VOLATILE.S */
+/* File: armv5te/OP_IPUT.S */
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_VOLATILE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_VOLATILE_finish @ yes, finish up
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_VOLATILE: /* 0xe5 */
+/* File: armv5te/OP_SGET_VOLATILE.S */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_VOLATILE_resolve @ yes, do resolve
+.LOP_SGET_VOLATILE_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ SMP_DMB @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_VOLATILE: /* 0xe6 */
+/* File: armv5te/OP_SPUT_VOLATILE.S */
+/* File: armv5te/OP_SPUT.S */
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SPUT_VOLATILE_resolve @ yes, do resolve
+.LOP_SPUT_VOLATILE_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SMP_DMB @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_OBJECT_VOLATILE: /* 0xe7 */
+/* File: armv5te/OP_IGET_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_OBJECT_VOLATILE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_OBJECT_VOLATILE_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_WIDE_VOLATILE: /* 0xe8 */
+/* File: armv5te/OP_IGET_WIDE_VOLATILE.S */
+/* File: armv5te/OP_IGET_WIDE.S */
+ /*
+ * Wide 32-bit instance field get.
+ */
+ /* iget-wide vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_WIDE_VOLATILE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_WIDE_VOLATILE_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_WIDE_VOLATILE: /* 0xe9 */
+/* File: armv5te/OP_IPUT_WIDE_VOLATILE.S */
+/* File: armv5te/OP_IPUT_WIDE.S */
+ /* iput-wide vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_WIDE_VOLATILE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_WIDE_VOLATILE_finish @ yes, finish up
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_WIDE_VOLATILE: /* 0xea */
+/* File: armv5te/OP_SGET_WIDE_VOLATILE.S */
+/* File: armv5te/OP_SGET_WIDE.S */
+ /*
+ * 64-bit SGET handler.
+ */
+ /* sget-wide vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_WIDE_VOLATILE_resolve @ yes, do resolve
+.LOP_SGET_WIDE_VOLATILE_finish:
+ mov r9, rINST, lsr #8 @ r9<- AA
+ .if 1
+ add r0, r0, #offStaticField_value @ r0<- pointer to data
+ bl dvmQuasiAtomicRead64 @ r0/r1<- contents of field
+ .else
+ ldrd r0, [r0, #offStaticField_value] @ r0/r1<- field value (aligned)
+ .endif
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_WIDE_VOLATILE: /* 0xeb */
+/* File: armv5te/OP_SPUT_WIDE_VOLATILE.S */
+/* File: armv5te/OP_SPUT_WIDE.S */
+ /*
+ * 64-bit SPUT handler.
+ */
+ /* sput-wide vAA, field@BBBB */
+ ldr r0, [rGLUE, #offGlue_methodClassDex] @ r0<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r0, [r0, #offDvmDex_pResFields] @ r0<- dvmDex->pResFields
+ mov r9, rINST, lsr #8 @ r9<- AA
+ ldr r2, [r0, r1, lsl #2] @ r2<- resolved StaticField ptr
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ cmp r2, #0 @ is resolved entry null?
+ beq .LOP_SPUT_WIDE_VOLATILE_resolve @ yes, do resolve
+.LOP_SPUT_WIDE_VOLATILE_finish: @ field ptr in r2, AA in r9
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ GET_INST_OPCODE(r10) @ extract opcode from rINST
+ .if 1
+ add r2, r2, #offStaticField_value @ r2<- pointer to data
+ bl dvmQuasiAtomicSwap64 @ stores r0/r1 into addr r2
+ .else
+ strd r0, [r2, #offStaticField_value] @ field<- vAA/vAA+1
+ .endif
+ GOTO_OPCODE(r10) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_BREAKPOINT: /* 0xec */
+/* File: armv5te/OP_BREAKPOINT.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_THROW_VERIFICATION_ERROR: /* 0xed */
+/* File: armv5te/OP_THROW_VERIFICATION_ERROR.S */
+ /*
+ * Handle a throw-verification-error instruction. This throws an
+ * exception for an error discovered during verification. The
+ * exception is indicated by AA, with some detail provided by BBBB.
+ */
+ /* op AA, ref@BBBB */
+ ldr r0, [rGLUE, #offGlue_method] @ r0<- glue->method
+ FETCH(r2, 1) @ r2<- BBBB
+ EXPORT_PC() @ export the PC
+ mov r1, rINST, lsr #8 @ r1<- AA
+ bl dvmThrowVerificationError @ always throws
+ b common_exceptionThrown @ handle exception
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_EXECUTE_INLINE: /* 0xee */
+/* File: armv5te/OP_EXECUTE_INLINE.S */
+ /*
+ * Execute a "native inline" instruction.
+ *
+ * We need to call an InlineOp4Func:
+ * bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+ *
+ * The first four args are in r0-r3, pointer to return value storage
+ * is on the stack. The function's return value is a flag that tells
+ * us if an exception was thrown.
+ */
+ /* [opt] execute-inline vAA, {vC, vD, vE, vF}, inline@BBBB */
+ FETCH(r10, 1) @ r10<- BBBB
+ add r1, rGLUE, #offGlue_retval @ r1<- &glue->retval
+ EXPORT_PC() @ can throw
+ sub sp, sp, #8 @ make room for arg, +64 bit align
+ mov r0, rINST, lsr #12 @ r0<- B
+ str r1, [sp] @ push &glue->retval
+ bl .LOP_EXECUTE_INLINE_continue @ make call; will return after
+ add sp, sp, #8 @ pop stack
+ cmp r0, #0 @ test boolean result of inline
+ beq common_exceptionThrown @ returned false, handle exception
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_EXECUTE_INLINE_RANGE: /* 0xef */
+/* File: armv5te/OP_EXECUTE_INLINE_RANGE.S */
+ /*
+ * Execute a "native inline" instruction, using "/range" semantics.
+ * Same idea as execute-inline, but we get the args differently.
+ *
+ * We need to call an InlineOp4Func:
+ * bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+ *
+ * The first four args are in r0-r3, pointer to return value storage
+ * is on the stack. The function's return value is a flag that tells
+ * us if an exception was thrown.
+ */
+ /* [opt] execute-inline/range {vCCCC..v(CCCC+AA-1)}, inline@BBBB */
+ FETCH(r10, 1) @ r10<- BBBB
+ add r1, rGLUE, #offGlue_retval @ r1<- &glue->retval
+ EXPORT_PC() @ can throw
+ sub sp, sp, #8 @ make room for arg, +64 bit align
+ mov r0, rINST, lsr #8 @ r0<- AA
+ str r1, [sp] @ push &glue->retval
+ bl .LOP_EXECUTE_INLINE_RANGE_continue @ make call; will return after
+ add sp, sp, #8 @ pop stack
+ cmp r0, #0 @ test boolean result of inline
+ beq common_exceptionThrown @ returned false, handle exception
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_DIRECT_EMPTY: /* 0xf0 */
+/* File: armv5te/OP_INVOKE_DIRECT_EMPTY.S */
+ /*
+ * invoke-direct-empty is a no-op in a "standard" interpreter.
+ */
+ FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ GOTO_OPCODE(ip) @ execute it
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_F1: /* 0xf1 */
+/* File: armv5te/OP_UNUSED_F1.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_QUICK: /* 0xf2 */
+/* File: armv6t2/OP_IGET_QUICK.S */
+ /* For: iget-quick, iget-object-quick */
+ /* op vA, vB, offset@CCCC */
+ mov r2, rINST, lsr #12 @ r2<- B
+ FETCH(r1, 1) @ r1<- field byte offset
+ GET_VREG(r3, r2) @ r3<- object we're operating on
+ ubfx r2, rINST, #8, #4 @ r2<- A
+ cmp r3, #0 @ check object for null
+ beq common_errNullObject @ object was null
+ ldr r0, [r3, r1] @ r0<- obj.field (always 32 bits)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_WIDE_QUICK: /* 0xf3 */
+/* File: armv6t2/OP_IGET_WIDE_QUICK.S */
+ /* iget-wide-quick vA, vB, offset@CCCC */
+ mov r2, rINST, lsr #12 @ r2<- B
+ FETCH(ip, 1) @ ip<- field byte offset
+ GET_VREG(r3, r2) @ r3<- object we're operating on
+ ubfx r2, rINST, #8, #4 @ r2<- A
+ cmp r3, #0 @ check object for null
+ beq common_errNullObject @ object was null
+ ldrd r0, [r3, ip] @ r0<- obj.field (64 bits, aligned)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ add r3, rFP, r2, lsl #2 @ r3<- &fp[A]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ fp[A]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_OBJECT_QUICK: /* 0xf4 */
+/* File: armv5te/OP_IGET_OBJECT_QUICK.S */
+/* File: armv5te/OP_IGET_QUICK.S */
+ /* For: iget-quick, iget-object-quick */
+ /* op vA, vB, offset@CCCC */
+ mov r2, rINST, lsr #12 @ r2<- B
+ GET_VREG(r3, r2) @ r3<- object we're operating on
+ FETCH(r1, 1) @ r1<- field byte offset
+ cmp r3, #0 @ check object for null
+ mov r2, rINST, lsr #8 @ r2<- A(+)
+ beq common_errNullObject @ object was null
+ ldr r0, [r3, r1] @ r0<- obj.field (always 32 bits)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_QUICK: /* 0xf5 */
+/* File: armv6t2/OP_IPUT_QUICK.S */
+ /* For: iput-quick, iput-object-quick */
+ /* op vA, vB, offset@CCCC */
+ mov r2, rINST, lsr #12 @ r2<- B
+ FETCH(r1, 1) @ r1<- field byte offset
+ GET_VREG(r3, r2) @ r3<- fp[B], the object pointer
+ ubfx r2, rINST, #8, #4 @ r2<- A
+ cmp r3, #0 @ check object for null
+ beq common_errNullObject @ object was null
+ GET_VREG(r0, r2) @ r0<- fp[A]
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ str r0, [r3, r1] @ obj.field (always 32 bits)<- r0
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_WIDE_QUICK: /* 0xf6 */
+/* File: armv6t2/OP_IPUT_WIDE_QUICK.S */
+ /* iput-wide-quick vA, vB, offset@CCCC */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r0, rINST, #8, #4 @ r0<- A
+ GET_VREG(r2, r1) @ r2<- fp[B], the object pointer
+ add r3, rFP, r0, lsl #2 @ r3<- &fp[A]
+ cmp r2, #0 @ check object for null
+ ldmia r3, {r0-r1} @ r0/r1<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH(r3, 1) @ r3<- field byte offset
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ strd r0, [r2, r3] @ obj.field (64 bits, aligned)<- r0/r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_OBJECT_QUICK: /* 0xf7 */
+/* File: armv5te/OP_IPUT_OBJECT_QUICK.S */
+ /* For: iput-object-quick */
+ /* op vA, vB, offset@CCCC */
+ mov r2, rINST, lsr #12 @ r2<- B
+ GET_VREG(r3, r2) @ r3<- fp[B], the object pointer
+ FETCH(r1, 1) @ r1<- field byte offset
+ cmp r3, #0 @ check object for null
+ mov r2, rINST, lsr #8 @ r2<- A(+)
+ beq common_errNullObject @ object was null
+ and r2, r2, #15
+ GET_VREG(r0, r2) @ r0<- fp[A]
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ str r0, [r3, r1] @ obj.field (always 32 bits)<- r0
+ cmp r0, #0
+ strneb r2, [r2, r3, lsr #GC_CARD_SHIFT] @ mark card based on obj head
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_VIRTUAL_QUICK: /* 0xf8 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK.S */
+ /*
+ * Handle an optimized virtual method call.
+ *
+ * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r3, 2) @ r3<- FEDC or CCCC
+ FETCH(r1, 1) @ r1<- BBBB
+ .if (!0)
+ and r3, r3, #15 @ r3<- C (or stays CCCC)
+ .endif
+ GET_VREG(r2, r3) @ r2<- vC ("this" ptr)
+ cmp r2, #0 @ is "this" null?
+ beq common_errNullObject @ null "this", throw exception
+ ldr r2, [r2, #offObject_clazz] @ r2<- thisPtr->clazz
+ ldr r2, [r2, #offClassObject_vtable] @ r2<- thisPtr->clazz->vtable
+ EXPORT_PC() @ invoke must export
+ ldr r0, [r2, r1, lsl #2] @ r3<- vtable[BBBB]
+ bl common_invokeMethodNoRange @ continue on
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_VIRTUAL_QUICK_RANGE: /* 0xf9 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK_RANGE.S */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK.S */
+ /*
+ * Handle an optimized virtual method call.
+ *
+ * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r3, 2) @ r3<- FEDC or CCCC
+ FETCH(r1, 1) @ r1<- BBBB
+ .if (!1)
+ and r3, r3, #15 @ r3<- C (or stays CCCC)
+ .endif
+ GET_VREG(r2, r3) @ r2<- vC ("this" ptr)
+ cmp r2, #0 @ is "this" null?
+ beq common_errNullObject @ null "this", throw exception
+ ldr r2, [r2, #offObject_clazz] @ r2<- thisPtr->clazz
+ ldr r2, [r2, #offClassObject_vtable] @ r2<- thisPtr->clazz->vtable
+ EXPORT_PC() @ invoke must export
+ ldr r0, [r2, r1, lsl #2] @ r3<- vtable[BBBB]
+ bl common_invokeMethodRange @ continue on
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_SUPER_QUICK: /* 0xfa */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK.S */
+ /*
+ * Handle an optimized "super" method call.
+ *
+ * for: [opt] invoke-super-quick, invoke-super-quick/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ .if (!0)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r2, [r2, #offMethod_clazz] @ r2<- method->clazz
+ EXPORT_PC() @ must export for invoke
+ ldr r2, [r2, #offClassObject_super] @ r2<- method->clazz->super
+ GET_VREG(r3, r10) @ r3<- "this"
+ ldr r2, [r2, #offClassObject_vtable] @ r2<- ...clazz->super->vtable
+ cmp r3, #0 @ null "this" ref?
+ ldr r0, [r2, r1, lsl #2] @ r0<- super->vtable[BBBB]
+ beq common_errNullObject @ "this" is null, throw exception
+ bl common_invokeMethodNoRange @ continue on
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_SUPER_QUICK_RANGE: /* 0xfb */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK_RANGE.S */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK.S */
+ /*
+ * Handle an optimized "super" method call.
+ *
+ * for: [opt] invoke-super-quick, invoke-super-quick/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ .if (!1)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r2, [r2, #offMethod_clazz] @ r2<- method->clazz
+ EXPORT_PC() @ must export for invoke
+ ldr r2, [r2, #offClassObject_super] @ r2<- method->clazz->super
+ GET_VREG(r3, r10) @ r3<- "this"
+ ldr r2, [r2, #offClassObject_vtable] @ r2<- ...clazz->super->vtable
+ cmp r3, #0 @ null "this" ref?
+ ldr r0, [r2, r1, lsl #2] @ r0<- super->vtable[BBBB]
+ beq common_errNullObject @ "this" is null, throw exception
+ bl common_invokeMethodRange @ continue on
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */
+/* File: armv5te/OP_IPUT_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_IPUT_OBJECT.S */
+ /*
+ * 32-bit instance field put.
+ *
+ * for: iput-object, iput-object-volatile
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_OBJECT_VOLATILE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_OBJECT_VOLATILE_finish @ yes, finish up
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_OBJECT_VOLATILE: /* 0xfd */
+/* File: armv5te/OP_SGET_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_OBJECT_VOLATILE_resolve @ yes, do resolve
+.LOP_SGET_OBJECT_VOLATILE_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ SMP_DMB @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */
+/* File: armv5te/OP_SPUT_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_SPUT_OBJECT.S */
+ /*
+ * 32-bit SPUT handler for objects
+ *
+ * for: sput-object, sput-object-volatile
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_SPUT_OBJECT_VOLATILE_finish @ no, continue
+ ldr r9, [rGLUE, #offGlue_method] @ r9<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r9, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_OBJECT_VOLATILE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_FF: /* 0xff */
+/* File: armv5te/OP_UNUSED_FF.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+
+ .balign 64
+ .size dvmAsmInstructionStart, .-dvmAsmInstructionStart
+ .global dvmAsmInstructionEnd
+dvmAsmInstructionEnd:
+
+/*
+ * ===========================================================================
+ * Sister implementations
+ * ===========================================================================
+ */
+ .global dvmAsmSisterStart
+ .type dvmAsmSisterStart, %function
+ .text
+ .balign 4
+dvmAsmSisterStart:
+
+/* continuation for OP_CONST_STRING */
+
+ /*
+ * Continuation if the String has not yet been resolved.
+ * r1: BBBB (String ref)
+ * r9: target register
+ */
+.LOP_CONST_STRING_resolve:
+ EXPORT_PC()
+ ldr r0, [rGLUE, #offGlue_method] @ r0<- glue->method
+ ldr r0, [r0, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveString @ r0<- String reference
+ cmp r0, #0 @ failed?
+ beq common_exceptionThrown @ yup, handle the exception
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_CONST_STRING_JUMBO */
+
+ /*
+ * Continuation if the String has not yet been resolved.
+ * r1: BBBBBBBB (String ref)
+ * r9: target register
+ */
+.LOP_CONST_STRING_JUMBO_resolve:
+ EXPORT_PC()
+ ldr r0, [rGLUE, #offGlue_method] @ r0<- glue->method
+ ldr r0, [r0, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveString @ r0<- String reference
+ cmp r0, #0 @ failed?
+ beq common_exceptionThrown @ yup, handle the exception
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_CONST_CLASS */
+
+ /*
+ * Continuation if the Class has not yet been resolved.
+ * r1: BBBB (Class ref)
+ * r9: target register
+ */
+.LOP_CONST_CLASS_resolve:
+ EXPORT_PC()
+ ldr r0, [rGLUE, #offGlue_method] @ r0<- glue->method
+ mov r2, #1 @ r2<- true
+ ldr r0, [r0, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- Class reference
+ cmp r0, #0 @ failed?
+ beq common_exceptionThrown @ yup, handle the exception
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_CHECK_CAST */
+
+ /*
+ * Trivial test failed, need to perform full check. This is common.
+ * r0 holds obj->clazz
+ * r1 holds class resolved from BBBB
+ * r9 holds object
+ */
+.LOP_CHECK_CAST_fullcheck:
+ bl dvmInstanceofNonTrivial @ r0<- boolean result
+ cmp r0, #0 @ failed?
+ bne .LOP_CHECK_CAST_okay @ no, success
+
+ @ A cast has failed. We need to throw a ClassCastException with the
+ @ class of the object that failed to be cast.
+ EXPORT_PC() @ about to throw
+ ldr r3, [r9, #offObject_clazz] @ r3<- obj->clazz
+ ldr r0, .LstrClassCastExceptionPtr
+ ldr r1, [r3, #offClassObject_descriptor] @ r1<- obj->clazz->descriptor
+ bl dvmThrowExceptionWithClassMessage
+ b common_exceptionThrown
+
+ /*
+ * Resolution required. This is the least-likely path.
+ *
+ * r2 holds BBBB
+ * r9 holds object
+ */
+.LOP_CHECK_CAST_resolve:
+ EXPORT_PC() @ resolve() could throw
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ mov r1, r2 @ r1<- BBBB
+ mov r2, #0 @ r2<- false
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- resolved ClassObject ptr
+ cmp r0, #0 @ got null?
+ beq common_exceptionThrown @ yes, handle exception
+ mov r1, r0 @ r1<- class resolved from BBB
+ ldr r0, [r9, #offObject_clazz] @ r0<- obj->clazz
+ b .LOP_CHECK_CAST_resolved @ pick up where we left off
+
+.LstrClassCastExceptionPtr:
+ .word .LstrClassCastException
+
+/* continuation for OP_INSTANCE_OF */
+
+ /*
+ * Trivial test failed, need to perform full check. This is common.
+ * r0 holds obj->clazz
+ * r1 holds class resolved from BBBB
+ * r9 holds A
+ */
+.LOP_INSTANCE_OF_fullcheck:
+ bl dvmInstanceofNonTrivial @ r0<- boolean result
+ @ fall through to OP_INSTANCE_OF_store
+
+ /*
+ * r0 holds boolean result
+ * r9 holds A
+ */
+.LOP_INSTANCE_OF_store:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r0, r9) @ vA<- r0
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+ /*
+ * Trivial test succeeded, save and bail.
+ * r9 holds A
+ */
+.LOP_INSTANCE_OF_trivial:
+ mov r0, #1 @ indicate success
+ @ could b OP_INSTANCE_OF_store, but copying is faster and cheaper
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r0, r9) @ vA<- r0
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+ /*
+ * Resolution required. This is the least-likely path.
+ *
+ * r3 holds BBBB
+ * r9 holds A
+ */
+.LOP_INSTANCE_OF_resolve:
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [rGLUE, #offGlue_method] @ r0<- glue->method
+ mov r1, r3 @ r1<- BBBB
+ mov r2, #1 @ r2<- true
+ ldr r0, [r0, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- resolved ClassObject ptr
+ cmp r0, #0 @ got null?
+ beq common_exceptionThrown @ yes, handle exception
+ mov r1, r0 @ r1<- class resolved from BBB
+ mov r3, rINST, lsr #12 @ r3<- B
+ GET_VREG(r0, r3) @ r0<- vB (object)
+ ldr r0, [r0, #offObject_clazz] @ r0<- obj->clazz
+ b .LOP_INSTANCE_OF_resolved @ pick up where we left off
+
+/* continuation for OP_NEW_INSTANCE */
+
+ .balign 32 @ minimize cache lines
+.LOP_NEW_INSTANCE_finish: @ r0=new object
+ mov r3, rINST, lsr #8 @ r3<- AA
+ cmp r0, #0 @ failed?
+ beq common_exceptionThrown @ yes, handle the exception
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r3) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+ /*
+ * Class initialization required.
+ *
+ * r0 holds class object
+ */
+.LOP_NEW_INSTANCE_needinit:
+ mov r9, r0 @ save r0
+ bl dvmInitClass @ initialize class
+ cmp r0, #0 @ check boolean result
+ mov r0, r9 @ restore r0
+ bne .LOP_NEW_INSTANCE_initialized @ success, continue
+ b common_exceptionThrown @ failed, deal with init exception
+
+ /*
+ * Resolution required. This is the least-likely path.
+ *
+ * r1 holds BBBB
+ */
+.LOP_NEW_INSTANCE_resolve:
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ mov r2, #0 @ r2<- false
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- resolved ClassObject ptr
+ cmp r0, #0 @ got null?
+ bne .LOP_NEW_INSTANCE_resolved @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+.LstrInstantiationErrorPtr:
+ .word .LstrInstantiationError
+
+/* continuation for OP_NEW_ARRAY */
+
+
+ /*
+ * Resolve class. (This is an uncommon case.)
+ *
+ * r1 holds array length
+ * r2 holds class ref CCCC
+ */
+.LOP_NEW_ARRAY_resolve:
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ mov r9, r1 @ r9<- length (save)
+ mov r1, r2 @ r1<- CCCC
+ mov r2, #0 @ r2<- false
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- call(clazz, ref)
+ cmp r0, #0 @ got null?
+ mov r1, r9 @ r1<- length (restore)
+ beq common_exceptionThrown @ yes, handle exception
+ @ fall through to OP_NEW_ARRAY_finish
+
+ /*
+ * Finish allocation.
+ *
+ * r0 holds class
+ * r1 holds array length
+ */
+.LOP_NEW_ARRAY_finish:
+ mov r2, #ALLOC_DONT_TRACK @ don't track in local refs table
+ bl dvmAllocArrayByClass @ r0<- call(clazz, length, flags)
+ cmp r0, #0 @ failed?
+ mov r2, rINST, lsr #8 @ r2<- A+
+ beq common_exceptionThrown @ yes, handle the exception
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ vA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_FILLED_NEW_ARRAY */
+
+ /*
+ * On entry:
+ * r0 holds array class
+ * r10 holds AA or BA
+ */
+.LOP_FILLED_NEW_ARRAY_continue:
+ ldr r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor
+ mov r2, #ALLOC_DONT_TRACK @ r2<- alloc flags
+ ldrb rINST, [r3, #1] @ rINST<- descriptor[1]
+ .if 0
+ mov r1, r10 @ r1<- AA (length)
+ .else
+ mov r1, r10, lsr #4 @ r1<- B (length)
+ .endif
+ cmp rINST, #'I' @ array of ints?
+ cmpne rINST, #'L' @ array of objects?
+ cmpne rINST, #'[' @ array of arrays?
+ mov r9, r1 @ save length in r9
+ bne .LOP_FILLED_NEW_ARRAY_notimpl @ no, not handled yet
+ bl dvmAllocArrayByClass @ r0<- call(arClass, length, flags)
+ cmp r0, #0 @ null return?
+ beq common_exceptionThrown @ alloc failed, handle exception
+
+ FETCH(r1, 2) @ r1<- FEDC or CCCC
+ str r0, [rGLUE, #offGlue_retval] @ retval.l <- new array
+ str rINST, [rGLUE, #offGlue_retval+4] @ retval.h <- type
+ add r0, r0, #offArrayObject_contents @ r0<- newArray->contents
+ subs r9, r9, #1 @ length--, check for neg
+ FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST
+ bmi 2f @ was zero, bail
+
+ @ copy values from registers into the array
+ @ r0=array, r1=CCCC/FEDC, r9=length (from AA or B), r10=AA/BA
+ .if 0
+ add r2, rFP, r1, lsl #2 @ r2<- &fp[CCCC]
+1: ldr r3, [r2], #4 @ r3<- *r2++
+ subs r9, r9, #1 @ count--
+ str r3, [r0], #4 @ *contents++ = vX
+ bpl 1b
+ @ continue at 2
+ .else
+ cmp r9, #4 @ length was initially 5?
+ and r2, r10, #15 @ r2<- A
+ bne 1f @ <= 4 args, branch
+ GET_VREG(r3, r2) @ r3<- vA
+ sub r9, r9, #1 @ count--
+ str r3, [r0, #16] @ contents[4] = vA
+1: and r2, r1, #15 @ r2<- F/E/D/C
+ GET_VREG(r3, r2) @ r3<- vF/vE/vD/vC
+ mov r1, r1, lsr #4 @ r1<- next reg in low 4
+ subs r9, r9, #1 @ count--
+ str r3, [r0], #4 @ *contents++ = vX
+ bpl 1b
+ @ continue at 2
+ .endif
+
+2:
+ ldr r0, [rGLUE, #offGlue_retval] @ r0<- object
+ ldr r1, [rGLUE, #offGlue_retval+4] @ r1<- type
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ cmp r1, #'I' @ Is int array?
+ strneb r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card based on object head
+ GOTO_OPCODE(ip) @ execute it
+
+ /*
+ * Throw an exception indicating that we have not implemented this
+ * mode of filled-new-array.
+ */
+.LOP_FILLED_NEW_ARRAY_notimpl:
+ ldr r0, .L_strInternalError
+ ldr r1, .L_strFilledNewArrayNotImpl
+ bl dvmThrowException
+ b common_exceptionThrown
+
+ .if (!0) @ define in one or the other, not both
+.L_strFilledNewArrayNotImpl:
+ .word .LstrFilledNewArrayNotImpl
+.L_strInternalError:
+ .word .LstrInternalError
+ .endif
+
+/* continuation for OP_FILLED_NEW_ARRAY_RANGE */
+
+ /*
+ * On entry:
+ * r0 holds array class
+ * r10 holds AA or BA
+ */
+.LOP_FILLED_NEW_ARRAY_RANGE_continue:
+ ldr r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor
+ mov r2, #ALLOC_DONT_TRACK @ r2<- alloc flags
+ ldrb rINST, [r3, #1] @ rINST<- descriptor[1]
+ .if 1
+ mov r1, r10 @ r1<- AA (length)
+ .else
+ mov r1, r10, lsr #4 @ r1<- B (length)
+ .endif
+ cmp rINST, #'I' @ array of ints?
+ cmpne rINST, #'L' @ array of objects?
+ cmpne rINST, #'[' @ array of arrays?
+ mov r9, r1 @ save length in r9
+ bne .LOP_FILLED_NEW_ARRAY_RANGE_notimpl @ no, not handled yet
+ bl dvmAllocArrayByClass @ r0<- call(arClass, length, flags)
+ cmp r0, #0 @ null return?
+ beq common_exceptionThrown @ alloc failed, handle exception
+
+ FETCH(r1, 2) @ r1<- FEDC or CCCC
+ str r0, [rGLUE, #offGlue_retval] @ retval.l <- new array
+ str rINST, [rGLUE, #offGlue_retval+4] @ retval.h <- type
+ add r0, r0, #offArrayObject_contents @ r0<- newArray->contents
+ subs r9, r9, #1 @ length--, check for neg
+ FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST
+ bmi 2f @ was zero, bail
+
+ @ copy values from registers into the array
+ @ r0=array, r1=CCCC/FEDC, r9=length (from AA or B), r10=AA/BA
+ .if 1
+ add r2, rFP, r1, lsl #2 @ r2<- &fp[CCCC]
+1: ldr r3, [r2], #4 @ r3<- *r2++
+ subs r9, r9, #1 @ count--
+ str r3, [r0], #4 @ *contents++ = vX
+ bpl 1b
+ @ continue at 2
+ .else
+ cmp r9, #4 @ length was initially 5?
+ and r2, r10, #15 @ r2<- A
+ bne 1f @ <= 4 args, branch
+ GET_VREG(r3, r2) @ r3<- vA
+ sub r9, r9, #1 @ count--
+ str r3, [r0, #16] @ contents[4] = vA
+1: and r2, r1, #15 @ r2<- F/E/D/C
+ GET_VREG(r3, r2) @ r3<- vF/vE/vD/vC
+ mov r1, r1, lsr #4 @ r1<- next reg in low 4
+ subs r9, r9, #1 @ count--
+ str r3, [r0], #4 @ *contents++ = vX
+ bpl 1b
+ @ continue at 2
+ .endif
+
+2:
+ ldr r0, [rGLUE, #offGlue_retval] @ r0<- object
+ ldr r1, [rGLUE, #offGlue_retval+4] @ r1<- type
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ cmp r1, #'I' @ Is int array?
+ strneb r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card based on object head
+ GOTO_OPCODE(ip) @ execute it
+
+ /*
+ * Throw an exception indicating that we have not implemented this
+ * mode of filled-new-array.
+ */
+.LOP_FILLED_NEW_ARRAY_RANGE_notimpl:
+ ldr r0, .L_strInternalError
+ ldr r1, .L_strFilledNewArrayNotImpl
+ bl dvmThrowException
+ b common_exceptionThrown
+
+ .if (!1) @ define in one or the other, not both
+.L_strFilledNewArrayNotImpl:
+ .word .LstrFilledNewArrayNotImpl
+.L_strInternalError:
+ .word .LstrInternalError
+ .endif
+
+/* continuation for OP_CMPL_FLOAT */
+.LOP_CMPL_FLOAT_finish:
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_CMPG_FLOAT */
+.LOP_CMPG_FLOAT_finish:
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_CMPL_DOUBLE */
+.LOP_CMPL_DOUBLE_finish:
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_CMPG_DOUBLE */
+.LOP_CMPG_DOUBLE_finish:
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_CMP_LONG */
+
+.LOP_CMP_LONG_less:
+ mvn r1, #0 @ r1<- -1
+ @ Want to cond code the next mov so we can avoid branch, but don't see it;
+ @ instead, we just replicate the tail end.
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r9) @ vAA<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+.LOP_CMP_LONG_greater:
+ mov r1, #1 @ r1<- 1
+ @ fall through to _finish
+
+.LOP_CMP_LONG_finish:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r9) @ vAA<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_AGET_WIDE */
+
+.LOP_AGET_WIDE_finish:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldrd r2, [r0, #offArrayObject_contents] @ r2/r3<- vBB[vCC]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r2-r3} @ vAA/vAA+1<- r2/r3
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_APUT_WIDE */
+
+.LOP_APUT_WIDE_finish:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldmia r9, {r2-r3} @ r2/r3<- vAA/vAA+1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ strd r2, [r0, #offArrayObject_contents] @ r2/r3<- vBB[vCC]
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_APUT_OBJECT */
+ /*
+ * On entry:
+ * rINST = vBB (arrayObj)
+ * r9 = vAA (obj)
+ * r10 = offset into array (vBB + vCC * width)
+ */
+.LOP_APUT_OBJECT_finish:
+ cmp r9, #0 @ storing null reference?
+ beq .LOP_APUT_OBJECT_skip_check @ yes, skip type checks
+ ldr r0, [r9, #offObject_clazz] @ r0<- obj->clazz
+ ldr r1, [rINST, #offObject_clazz] @ r1<- arrayObj->clazz
+ bl dvmCanPutArrayElement @ test object type vs. array type
+ cmp r0, #0 @ okay?
+ beq common_errArrayStore @ no
+ mov r1, rINST @ r1<- arrayObj
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldr r2, [rGLUE, #offGlue_cardTable] @ get biased CT base
+ add r10, #offArrayObject_contents @ r0<- pointer to slot
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ str r9, [r10] @ vBB[vCC]<- vAA
+ strb r2, [r2, r1, lsr #GC_CARD_SHIFT] @ mark card using object head
+ GOTO_OPCODE(ip) @ jump to next instruction
+.LOP_APUT_OBJECT_skip_check:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ str r9, [r10, #offArrayObject_contents] @ vBB[vCC]<- vAA
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_finish:
+ @bl common_squeak0
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ ubfx r2, rINST, #8, #4 @ r2<- A
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_WIDE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_WIDE_finish:
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldrd r0, [r9, r3] @ r0/r1<- obj.field (64-bit align ok)
+ ubfx r2, rINST, #8, #4 @ r2<- A
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ add r3, rFP, r2, lsl #2 @ r3<- &fp[A]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ fp[A]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_OBJECT */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_OBJECT_finish:
+ @bl common_squeak0
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_BOOLEAN */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_BOOLEAN_finish:
+ @bl common_squeak1
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_BYTE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_BYTE_finish:
+ @bl common_squeak2
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_CHAR */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_CHAR_finish:
+ @bl common_squeak3
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_SHORT */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_SHORT_finish:
+ @bl common_squeak4
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_finish:
+ @bl common_squeak0
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ ubfx r1, rINST, #8, #4 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT_WIDE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_WIDE_finish:
+ ubfx r2, rINST, #8, #4 @ r2<- A
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ add r2, rFP, r2, lsl #2 @ r3<- &fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldmia r2, {r0-r1} @ r0/r1<- fp[A]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ strd r0, [r9, r3] @ obj.field (64 bits, aligned)<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT_OBJECT */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_OBJECT_finish:
+ @bl common_squeak0
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r0, [r9, r3] @ obj.field (32 bits)<- r0
+ cmp r0, #0 @ stored a null reference?
+ strneb r2, [r2, r9, lsr #GC_CARD_SHIFT] @ mark card if not
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT_BOOLEAN */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_BOOLEAN_finish:
+ @bl common_squeak1
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT_BYTE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_BYTE_finish:
+ @bl common_squeak2
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT_CHAR */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_CHAR_finish:
+ @bl common_squeak3
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT_SHORT */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_SHORT_finish:
+ @bl common_squeak4
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SGET */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SGET_WIDE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ *
+ * Returns StaticField pointer in r0.
+ */
+.LOP_SGET_WIDE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_WIDE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SGET_OBJECT */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_OBJECT_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_OBJECT_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SGET_BOOLEAN */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_BOOLEAN_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_BOOLEAN_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SGET_BYTE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_BYTE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_BYTE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SGET_CHAR */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_CHAR_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_CHAR_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SGET_SHORT */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_SHORT_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_SHORT_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SPUT_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_WIDE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ * r9: &fp[AA]
+ *
+ * Returns StaticField pointer in r2.
+ */
+.LOP_SPUT_WIDE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ mov r2, r0 @ copy to r2
+ bne .LOP_SPUT_WIDE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_OBJECT */
+.LOP_SPUT_OBJECT_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ ldr r9, [r0, #offField_clazz] @ r9<- field->clazz
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ cmp r1, #0 @ stored a null object?
+ strneb r2, [r2, r9, lsr #GC_CARD_SHIFT] @ mark card based on obj head
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SPUT_BOOLEAN */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SPUT_BOOLEAN_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_BOOLEAN_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_BYTE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SPUT_BYTE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_BYTE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_CHAR */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SPUT_CHAR_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_CHAR_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_SHORT */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SPUT_SHORT_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_SHORT_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_INVOKE_VIRTUAL */
+
+ /*
+ * At this point:
+ * r0 = resolved base method
+ * r10 = C or CCCC (index of first arg, which is the "this" ptr)
+ */
+.LOP_INVOKE_VIRTUAL_continue:
+ GET_VREG(r1, r10) @ r1<- "this" ptr
+ ldrh r2, [r0, #offMethod_methodIndex] @ r2<- baseMethod->methodIndex
+ cmp r1, #0 @ is "this" null?
+ beq common_errNullObject @ null "this", throw exception
+ ldr r3, [r1, #offObject_clazz] @ r1<- thisPtr->clazz
+ ldr r3, [r3, #offClassObject_vtable] @ r3<- thisPtr->clazz->vtable
+ ldr r0, [r3, r2, lsl #2] @ r3<- vtable[methodIndex]
+ bl common_invokeMethodNoRange @ continue on
+
+/* continuation for OP_INVOKE_SUPER */
+
+ /*
+ * At this point:
+ * r0 = resolved base method
+ * r9 = method->clazz
+ */
+.LOP_INVOKE_SUPER_continue:
+ ldr r1, [r9, #offClassObject_super] @ r1<- method->clazz->super
+ ldrh r2, [r0, #offMethod_methodIndex] @ r2<- baseMethod->methodIndex
+ ldr r3, [r1, #offClassObject_vtableCount] @ r3<- super->vtableCount
+ EXPORT_PC() @ must export for invoke
+ cmp r2, r3 @ compare (methodIndex, vtableCount)
+ bcs .LOP_INVOKE_SUPER_nsm @ method not present in superclass
+ ldr r1, [r1, #offClassObject_vtable] @ r1<- ...clazz->super->vtable
+ ldr r0, [r1, r2, lsl #2] @ r3<- vtable[methodIndex]
+ bl common_invokeMethodNoRange @ continue on
+
+.LOP_INVOKE_SUPER_resolve:
+ mov r0, r9 @ r0<- method->clazz
+ mov r2, #METHOD_VIRTUAL @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ bne .LOP_INVOKE_SUPER_continue @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+ /*
+ * Throw a NoSuchMethodError with the method name as the message.
+ * r0 = resolved base method
+ */
+.LOP_INVOKE_SUPER_nsm:
+ ldr r1, [r0, #offMethod_name] @ r1<- method name
+ b common_errNoSuchMethod
+
+/* continuation for OP_INVOKE_DIRECT */
+
+ /*
+ * On entry:
+ * r1 = reference (BBBB or CCCC)
+ * r10 = "this" register
+ */
+.LOP_INVOKE_DIRECT_resolve:
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ mov r2, #METHOD_DIRECT @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ GET_VREG(r2, r10) @ r2<- "this" ptr (reload)
+ bne .LOP_INVOKE_DIRECT_finish @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+/* continuation for OP_INVOKE_VIRTUAL_RANGE */
+
+ /*
+ * At this point:
+ * r0 = resolved base method
+ * r10 = C or CCCC (index of first arg, which is the "this" ptr)
+ */
+.LOP_INVOKE_VIRTUAL_RANGE_continue:
+ GET_VREG(r1, r10) @ r1<- "this" ptr
+ ldrh r2, [r0, #offMethod_methodIndex] @ r2<- baseMethod->methodIndex
+ cmp r1, #0 @ is "this" null?
+ beq common_errNullObject @ null "this", throw exception
+ ldr r3, [r1, #offObject_clazz] @ r1<- thisPtr->clazz
+ ldr r3, [r3, #offClassObject_vtable] @ r3<- thisPtr->clazz->vtable
+ ldr r0, [r3, r2, lsl #2] @ r3<- vtable[methodIndex]
+ bl common_invokeMethodRange @ continue on
+
+/* continuation for OP_INVOKE_SUPER_RANGE */
+
+ /*
+ * At this point:
+ * r0 = resolved base method
+ * r9 = method->clazz
+ */
+.LOP_INVOKE_SUPER_RANGE_continue:
+ ldr r1, [r9, #offClassObject_super] @ r1<- method->clazz->super
+ ldrh r2, [r0, #offMethod_methodIndex] @ r2<- baseMethod->methodIndex
+ ldr r3, [r1, #offClassObject_vtableCount] @ r3<- super->vtableCount
+ EXPORT_PC() @ must export for invoke
+ cmp r2, r3 @ compare (methodIndex, vtableCount)
+ bcs .LOP_INVOKE_SUPER_RANGE_nsm @ method not present in superclass
+ ldr r1, [r1, #offClassObject_vtable] @ r1<- ...clazz->super->vtable
+ ldr r0, [r1, r2, lsl #2] @ r3<- vtable[methodIndex]
+ bl common_invokeMethodRange @ continue on
+
+.LOP_INVOKE_SUPER_RANGE_resolve:
+ mov r0, r9 @ r0<- method->clazz
+ mov r2, #METHOD_VIRTUAL @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ bne .LOP_INVOKE_SUPER_RANGE_continue @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+ /*
+ * Throw a NoSuchMethodError with the method name as the message.
+ * r0 = resolved base method
+ */
+.LOP_INVOKE_SUPER_RANGE_nsm:
+ ldr r1, [r0, #offMethod_name] @ r1<- method name
+ b common_errNoSuchMethod
+
+/* continuation for OP_INVOKE_DIRECT_RANGE */
+
+ /*
+ * On entry:
+ * r1 = reference (BBBB or CCCC)
+ * r10 = "this" register
+ */
+.LOP_INVOKE_DIRECT_RANGE_resolve:
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ mov r2, #METHOD_DIRECT @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ GET_VREG(r2, r10) @ r2<- "this" ptr (reload)
+ bne .LOP_INVOKE_DIRECT_RANGE_finish @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+/* continuation for OP_FLOAT_TO_LONG */
+/*
+ * Convert the float in r0 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification. The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer. The EABI convert function isn't doing this for us.
+ */
+f2l_doconv:
+ stmfd sp!, {r4, lr}
+ mov r1, #0x5f000000 @ (float)maxlong
+ mov r4, r0
+ bl __aeabi_fcmpge @ is arg >= maxlong?
+ cmp r0, #0 @ nonzero == yes
+ mvnne r0, #0 @ return maxlong (7fffffff)
+ mvnne r1, #0x80000000
+ ldmnefd sp!, {r4, pc}
+
+ mov r0, r4 @ recover arg
+ mov r1, #0xdf000000 @ (float)minlong
+ bl __aeabi_fcmple @ is arg <= minlong?
+ cmp r0, #0 @ nonzero == yes
+ movne r0, #0 @ return minlong (80000000)
+ movne r1, #0x80000000
+ ldmnefd sp!, {r4, pc}
+
+ mov r0, r4 @ recover arg
+ mov r1, r4
+ bl __aeabi_fcmpeq @ is arg == self?
+ cmp r0, #0 @ zero == no
+ moveq r1, #0 @ return zero for NaN
+ ldmeqfd sp!, {r4, pc}
+
+ mov r0, r4 @ recover arg
+ bl __aeabi_f2lz @ convert float to long
+ ldmfd sp!, {r4, pc}
+
+/* continuation for OP_DOUBLE_TO_LONG */
+/*
+ * Convert the double in r0/r1 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification. The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer. The EABI convert function isn't doing this for us.
+ */
+d2l_doconv:
+ stmfd sp!, {r4, r5, lr} @ save regs
+ mov r3, #0x43000000 @ maxlong, as a double (high word)
+ add r3, #0x00e00000 @ 0x43e00000
+ mov r2, #0 @ maxlong, as a double (low word)
+ sub sp, sp, #4 @ align for EABI
+ mov r4, r0 @ save a copy of r0
+ mov r5, r1 @ and r1
+ bl __aeabi_dcmpge @ is arg >= maxlong?
+ cmp r0, #0 @ nonzero == yes
+ mvnne r0, #0 @ return maxlong (7fffffffffffffff)
+ mvnne r1, #0x80000000
+ bne 1f
+
+ mov r0, r4 @ recover arg
+ mov r1, r5
+ mov r3, #0xc3000000 @ minlong, as a double (high word)
+ add r3, #0x00e00000 @ 0xc3e00000
+ mov r2, #0 @ minlong, as a double (low word)
+ bl __aeabi_dcmple @ is arg <= minlong?
+ cmp r0, #0 @ nonzero == yes
+ movne r0, #0 @ return minlong (8000000000000000)
+ movne r1, #0x80000000
+ bne 1f
+
+ mov r0, r4 @ recover arg
+ mov r1, r5
+ mov r2, r4 @ compare against self
+ mov r3, r5
+ bl __aeabi_dcmpeq @ is arg == self?
+ cmp r0, #0 @ zero == no
+ moveq r1, #0 @ return zero for NaN
+ beq 1f
+
+ mov r0, r4 @ recover arg
+ mov r1, r5
+ bl __aeabi_d2lz @ convert double to long
+
+1:
+ add sp, sp, #4
+ ldmfd sp!, {r4, r5, pc}
+
+/* continuation for OP_MUL_LONG */
+
+.LOP_MUL_LONG_finish:
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r0, {r9-r10} @ vAA/vAA+1<- r9/r10
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SHL_LONG */
+
+.LOP_SHL_LONG_finish:
+ mov r0, r0, asl r2 @ r0<- r0 << r2
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SHR_LONG */
+
+.LOP_SHR_LONG_finish:
+ mov r1, r1, asr r2 @ r1<- r1 >> r2
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_USHR_LONG */
+
+.LOP_USHR_LONG_finish:
+ mov r1, r1, lsr r2 @ r1<- r1 >>> r2
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SHL_LONG_2ADDR */
+
+.LOP_SHL_LONG_2ADDR_finish:
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SHR_LONG_2ADDR */
+
+.LOP_SHR_LONG_2ADDR_finish:
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_USHR_LONG_2ADDR */
+
+.LOP_USHR_LONG_2ADDR_finish:
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_VOLATILE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_VOLATILE_finish:
+ @bl common_squeak0
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ SMP_DMB @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT_VOLATILE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_VOLATILE_finish:
+ @bl common_squeak0
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SMP_DMB @ releasing store
+ str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SGET_VOLATILE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_VOLATILE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_VOLATILE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_VOLATILE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SPUT_VOLATILE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_VOLATILE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_IGET_OBJECT_VOLATILE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_OBJECT_VOLATILE_finish:
+ @bl common_squeak0
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ SMP_DMB @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_WIDE_VOLATILE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_WIDE_VOLATILE_finish:
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ .if 1
+ add r0, r9, r3 @ r0<- address of field
+ bl dvmQuasiAtomicRead64 @ r0/r1<- contents of field
+ .else
+ ldrd r0, [r9, r3] @ r0/r1<- obj.field (64-bit align ok)
+ .endif
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ add r3, rFP, r2, lsl #2 @ r3<- &fp[A]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ fp[A]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT_WIDE_VOLATILE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_WIDE_VOLATILE_finish:
+ mov r2, rINST, lsr #8 @ r2<- A+
+ cmp r9, #0 @ check object for null
+ and r2, r2, #15 @ r2<- A
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ add r2, rFP, r2, lsl #2 @ r3<- &fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldmia r2, {r0-r1} @ r0/r1<- fp[A]
+ GET_INST_OPCODE(r10) @ extract opcode from rINST
+ .if 1
+ add r2, r9, r3 @ r2<- target address
+ bl dvmQuasiAtomicSwap64 @ stores r0/r1 into addr r2
+ .else
+ strd r0, [r9, r3] @ obj.field (64 bits, aligned)<- r0/r1
+ .endif
+ GOTO_OPCODE(r10) @ jump to next instruction
+
+/* continuation for OP_SGET_WIDE_VOLATILE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ *
+ * Returns StaticField pointer in r0.
+ */
+.LOP_SGET_WIDE_VOLATILE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_WIDE_VOLATILE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_WIDE_VOLATILE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ * r9: &fp[AA]
+ *
+ * Returns StaticField pointer in r2.
+ */
+.LOP_SPUT_WIDE_VOLATILE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ mov r2, r0 @ copy to r2
+ bne .LOP_SPUT_WIDE_VOLATILE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_EXECUTE_INLINE */
+
+ /*
+ * Extract args, call function.
+ * r0 = #of args (0-4)
+ * r10 = call index
+ * lr = return addr, above [DO NOT bl out of here w/o preserving LR]
+ *
+ * Other ideas:
+ * - Use a jump table from the main piece to jump directly into the
+ * AND/LDR pairs. Costs a data load, saves a branch.
+ * - Have five separate pieces that do the loading, so we can work the
+ * interleave a little better. Increases code size.
+ */
+.LOP_EXECUTE_INLINE_continue:
+ rsb r0, r0, #4 @ r0<- 4-r0
+ FETCH(r9, 2) @ r9<- FEDC
+ add pc, pc, r0, lsl #3 @ computed goto, 2 instrs each
+ bl common_abort @ (skipped due to ARM prefetch)
+4: and ip, r9, #0xf000 @ isolate F
+ ldr r3, [rFP, ip, lsr #10] @ r3<- vF (shift right 12, left 2)
+3: and ip, r9, #0x0f00 @ isolate E
+ ldr r2, [rFP, ip, lsr #6] @ r2<- vE
+2: and ip, r9, #0x00f0 @ isolate D
+ ldr r1, [rFP, ip, lsr #2] @ r1<- vD
+1: and ip, r9, #0x000f @ isolate C
+ ldr r0, [rFP, ip, lsl #2] @ r0<- vC
+0:
+ ldr r9, .LOP_EXECUTE_INLINE_table @ table of InlineOperation
+ LDR_PC "[r9, r10, lsl #4]" @ sizeof=16, "func" is first entry
+ @ (not reached)
+
+.LOP_EXECUTE_INLINE_table:
+ .word gDvmInlineOpsTable
+
+/* continuation for OP_EXECUTE_INLINE_RANGE */
+
+ /*
+ * Extract args, call function.
+ * r0 = #of args (0-4)
+ * r10 = call index
+ * lr = return addr, above [DO NOT bl out of here w/o preserving LR]
+ */
+.LOP_EXECUTE_INLINE_RANGE_continue:
+ rsb r0, r0, #4 @ r0<- 4-r0
+ FETCH(r9, 2) @ r9<- CCCC
+ add pc, pc, r0, lsl #3 @ computed goto, 2 instrs each
+ bl common_abort @ (skipped due to ARM prefetch)
+4: add ip, r9, #3 @ base+3
+ GET_VREG(r3, ip) @ r3<- vBase[3]
+3: add ip, r9, #2 @ base+2
+ GET_VREG(r2, ip) @ r2<- vBase[2]
+2: add ip, r9, #1 @ base+1
+ GET_VREG(r1, ip) @ r1<- vBase[1]
+1: add ip, r9, #0 @ (nop)
+ GET_VREG(r0, ip) @ r0<- vBase[0]
+0:
+ ldr r9, .LOP_EXECUTE_INLINE_RANGE_table @ table of InlineOperation
+ LDR_PC "[r9, r10, lsl #4]" @ sizeof=16, "func" is first entry
+ @ (not reached)
+
+.LOP_EXECUTE_INLINE_RANGE_table:
+ .word gDvmInlineOpsTable
+
+/* continuation for OP_IPUT_OBJECT_VOLATILE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_OBJECT_VOLATILE_finish:
+ @bl common_squeak0
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SMP_DMB @ releasing store
+ str r0, [r9, r3] @ obj.field (32 bits)<- r0
+ cmp r0, #0 @ stored a null reference?
+ strneb r2, [r2, r9, lsr #GC_CARD_SHIFT] @ mark card if not
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SGET_OBJECT_VOLATILE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_OBJECT_VOLATILE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_OBJECT_VOLATILE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_OBJECT_VOLATILE */
+.LOP_SPUT_OBJECT_VOLATILE_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ ldr r9, [r0, #offField_clazz] @ r9<- field->clazz
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SMP_DMB @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ cmp r1, #0 @ stored a null object?
+ strneb r2, [r2, r9, lsr #GC_CARD_SHIFT] @ mark card based on obj head
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+ .size dvmAsmSisterStart, .-dvmAsmSisterStart
+ .global dvmAsmSisterEnd
+dvmAsmSisterEnd:
+
+/* File: armv5te/footer.S */
+
+/*
+ * ===========================================================================
+ * Common subroutines and data
+ * ===========================================================================
+ */
+
+
+
+ .text
+ .align 2
+
+#if defined(WITH_JIT)
+#if defined(WITH_SELF_VERIFICATION)
+ .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r2,#kSVSPunt @ r2<- interpreter entry point
+ mov r3, #0
+ str r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+ str lr,[rGLUE,#offGlue_jitResumeNPC]
+ str r1,[rGLUE,#offGlue_jitResumeDPC]
+ mov r2,#kSVSSingleStep @ r2<- interpreter entry point
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r0,rPC @ pass our target PC
+ mov r2,#kSVSNoProfile @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [r10, #offThread_inJitCodeCache] @ back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r0,rPC @ pass our target PC
+ mov r2,#kSVSTraceSelect @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ ldr r0,[lr, #-1] @ pass our target PC
+ mov r2,#kSVSTraceSelect @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpBackwardBranch
+dvmJitToInterpBackwardBranch:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ ldr r0,[lr, #-1] @ pass our target PC
+ mov r2,#kSVSBackwardBranch @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ ldr r0,[lr, #-1] @ pass our target PC
+ mov r2,#kSVSNormal @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r0,rPC @ pass our target PC
+ mov r2,#kSVSNoChain @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+#else
+/*
+ * Return from the translation cache to the interpreter when the compiler is
+ * having issues translating/executing a Dalvik instruction. We have to skip
+ * the code cache lookup otherwise it is possible to indefinitely bouce
+ * between the interpreter and the code cache if the instruction that fails
+ * to be compiled happens to be at a trace start.
+ */
+ .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov rPC, r0
+#if defined(WITH_JIT_TUNING)
+ mov r0,lr
+ bl dvmBumpPunt;
+#endif
+ EXPORT_PC()
+ mov r0, #0
+ str r0, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ adrl rIBASE, dvmAsmInstructionStart
+ FETCH_INST()
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+
+/*
+ * Return to the interpreter to handle a single instruction.
+ * On entry:
+ * r0 <= PC
+ * r1 <= PC of resume instruction
+ * lr <= resume point in translation
+ */
+ .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+ str lr,[rGLUE,#offGlue_jitResumeNPC]
+ str r1,[rGLUE,#offGlue_jitResumeDPC]
+ mov r1,#kInterpEntryInstr
+ @ enum is 4 byte in aapcs-EABI
+ str r1, [rGLUE, #offGlue_entryPoint]
+ mov rPC,r0
+ EXPORT_PC()
+
+ adrl rIBASE, dvmAsmInstructionStart
+ mov r2,#kJitSingleStep @ Ask for single step and then revert
+ str r2,[rGLUE,#offGlue_jitState]
+ mov r1,#1 @ set changeInterp to bail to debug interp
+ b common_gotoBail
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target. Commonly used for callees.
+ */
+ .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+#if defined(WITH_JIT_TUNING)
+ bl dvmBumpNoChain
+#endif
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r0,rPC
+ bl dvmJitGetCodeAddr @ Is there a translation?
+ str r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0 @ !0 means translation exists
+ bxne r0 @ continue native execution if so
+ b 2f @ branch over to use the interpreter
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target. Commonly used following
+ * invokes.
+ */
+ .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+ ldr rPC,[lr, #-1] @ get our target PC
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ add rINST,lr,#-5 @ save start of chain branch
+ add rINST, #-4 @ .. which is 9 bytes back
+ mov r0,rPC
+ bl dvmJitGetCodeAddr @ Is there a translation?
+ str r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ cmp r0,#0
+ beq 2f
+ mov r1,rINST
+ bl dvmJitChain @ r0<- dvmJitChain(codeAddr,chainAddr)
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0 @ successful chain?
+ bxne r0 @ continue native execution
+ b toInterpreter @ didn't chain - resume with interpreter
+
+/* No translation, so request one if profiling isn't disabled*/
+2:
+ adrl rIBASE, dvmAsmInstructionStart
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_INST()
+ cmp r0, #0
+ movne r2,#kJitTSelectRequestHot @ ask for trace selection
+ bne common_selectTrace
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+
+/*
+ * Return from the translation cache to the interpreter.
+ * The return was done with a BLX from thumb mode, and
+ * the following 32-bit word contains the target rPC value.
+ * Note that lr (r14) will have its low-order bit set to denote
+ * its thumb-mode origin.
+ *
+ * We'll need to stash our lr origin away, recover the new
+ * target and then check to see if there is a translation available
+ * for our new target. If so, we do a translation chain and
+ * go back to native execution. Otherwise, it's back to the
+ * interpreter (after treating this entry as a potential
+ * trace start).
+ */
+ .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+ ldr rPC,[lr, #-1] @ get our target PC
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ add rINST,lr,#-5 @ save start of chain branch
+ add rINST,#-4 @ .. which is 9 bytes back
+#if defined(WITH_JIT_TUNING)
+ bl dvmBumpNormal
+#endif
+ mov r0,rPC
+ bl dvmJitGetCodeAddr @ Is there a translation?
+ str r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ cmp r0,#0
+ beq toInterpreter @ go if not, otherwise do chain
+ mov r1,rINST
+ bl dvmJitChain @ r0<- dvmJitChain(codeAddr,chainAddr)
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0 @ successful chain?
+ bxne r0 @ continue native execution
+ b toInterpreter @ didn't chain - resume with interpreter
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+ .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+#if defined(WITH_JIT_TUNING)
+ bl dvmBumpNoChain
+#endif
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r0,rPC
+ bl dvmJitGetCodeAddr @ Is there a translation?
+ str r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0
+ bxne r0 @ continue native execution if so
+ EXPORT_PC()
+ adrl rIBASE, dvmAsmInstructionStart
+ FETCH_INST()
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+ .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+#if defined(WITH_JIT_TUNING)
+ bl dvmBumpNoChain
+#endif
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r0,rPC
+ bl dvmJitGetCodeAddr @ Is there a translation?
+ str r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0
+ bxne r0 @ continue native execution if so
+#endif
+
+/*
+ * No translation, restore interpreter regs and start interpreting.
+ * rGLUE & rFP were preserved in the translated code, and rPC has
+ * already been restored by the time we get here. We'll need to set
+ * up rIBASE & rINST, and load the address of the JitTable into r0.
+ */
+toInterpreter:
+ EXPORT_PC()
+ adrl rIBASE, dvmAsmInstructionStart
+ FETCH_INST()
+ GET_JIT_PROF_TABLE(r0)
+ @ NOTE: intended fallthrough
+
+/*
+ * Common code to update potential trace start counter, and initiate
+ * a trace-build if appropriate. On entry, rPC should point to the
+ * next instruction to execute, and rINST should be already loaded with
+ * the next opcode word, and r0 holds a pointer to the jit profile
+ * table (pJitProfTable).
+ */
+common_testUpdateProfile:
+ cmp r0,#0
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE_IFEQ(ip) @ if not profiling, fallthrough otherwise */
+
+common_updateProfile:
+ eor r3,rPC,rPC,lsr #12 @ cheap, but fast hash function
+ lsl r3,r3,#(32 - JIT_PROF_SIZE_LOG_2) @ shift out excess bits
+ ldrb r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ get counter
+ GET_INST_OPCODE(ip)
+ subs r1,r1,#1 @ decrement counter
+ strb r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ and store it
+ GOTO_OPCODE_IFNE(ip) @ if not threshold, fallthrough otherwise */
+
+/*
+ * Here, we switch to the debug interpreter to request
+ * trace selection. First, though, check to see if there
+ * is already a native translation in place (and, if so,
+ * jump to it now).
+ */
+ GET_JIT_THRESHOLD(r1)
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ strb r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ reset counter
+ EXPORT_PC()
+ mov r0,rPC
+ bl dvmJitGetCodeAddr @ r0<- dvmJitGetCodeAddr(rPC)
+ str r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0
+#if !defined(WITH_SELF_VERIFICATION)
+ bxne r0 @ jump to the translation
+ mov r2,#kJitTSelectRequest @ ask for trace selection
+ @ fall-through to common_selectTrace
+#else
+ moveq r2,#kJitTSelectRequest @ ask for trace selection
+ beq common_selectTrace
+ /*
+ * At this point, we have a target translation. However, if
+ * that translation is actually the interpret-only pseudo-translation
+ * we want to treat it the same as no translation.
+ */
+ mov r10, r0 @ save target
+ bl dvmCompilerGetInterpretTemplate
+ cmp r0, r10 @ special case?
+ bne jitSVShadowRunStart @ set up self verification shadow space
+ @ Need to clear the inJitCodeCache flag
+ ldr r10, [rGLUE, #offGlue_self] @ r10 <- glue->self
+ mov r3, #0 @ 0 means not in the JIT code cache
+ str r3, [r10, #offThread_inJitCodeCache] @ back to the interp land
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+ /* no return */
+#endif
+
+/*
+ * On entry:
+ * r2 is jit state, e.g. kJitTSelectRequest or kJitTSelectRequestHot
+ */
+common_selectTrace:
+ str r2,[rGLUE,#offGlue_jitState]
+ mov r2,#kInterpEntryInstr @ normal entry reason
+ str r2,[rGLUE,#offGlue_entryPoint]
+ mov r1,#1 @ set changeInterp
+ b common_gotoBail
+
+#if defined(WITH_SELF_VERIFICATION)
+/*
+ * Save PC and registers to shadow memory for self verification mode
+ * before jumping to native translation.
+ * On entry:
+ * rPC, rFP, rGLUE: the values that they should contain
+ * r10: the address of the target translation.
+ */
+jitSVShadowRunStart:
+ mov r0,rPC @ r0<- program counter
+ mov r1,rFP @ r1<- frame pointer
+ mov r2,rGLUE @ r2<- InterpState pointer
+ mov r3,r10 @ r3<- target translation
+ bl dvmSelfVerificationSaveState @ save registers to shadow space
+ ldr rFP,[r0,#offShadowSpace_shadowFP] @ rFP<- fp in shadow space
+ add rGLUE,r0,#offShadowSpace_interpState @ rGLUE<- rGLUE in shadow space
+ bx r10 @ jump to the translation
+
+/*
+ * Restore PC, registers, and interpState to original values
+ * before jumping back to the interpreter.
+ */
+jitSVShadowRunEnd:
+ mov r1,rFP @ pass ending fp
+ bl dvmSelfVerificationRestoreState @ restore pc and fp values
+ ldr rPC,[r0,#offShadowSpace_startPC] @ restore PC
+ ldr rFP,[r0,#offShadowSpace_fp] @ restore FP
+ ldr rGLUE,[r0,#offShadowSpace_glue] @ restore InterpState
+ ldr r1,[r0,#offShadowSpace_svState] @ get self verification state
+ cmp r1,#0 @ check for punt condition
+ beq 1f
+ mov r2,#kJitSelfVerification @ ask for self verification
+ str r2,[rGLUE,#offGlue_jitState]
+ mov r2,#kInterpEntryInstr @ normal entry reason
+ str r2,[rGLUE,#offGlue_entryPoint]
+ mov r1,#1 @ set changeInterp
+ b common_gotoBail
+
+1: @ exit to interpreter without check
+ EXPORT_PC()
+ adrl rIBASE, dvmAsmInstructionStart
+ FETCH_INST()
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+#endif
+
+#endif
+
+/*
+ * Common code when a backward branch is taken.
+ *
+ * TODO: we could avoid a branch by just setting r0 and falling through
+ * into the common_periodicChecks code, and having a test on r0 at the
+ * end determine if we should return to the caller or update & branch to
+ * the next instr.
+ *
+ * On entry:
+ * r9 is PC adjustment *in bytes*
+ */
+common_backwardBranch:
+ mov r0, #kInterpEntryInstr
+ bl common_periodicChecks
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/*
+ * Need to see if the thread needs to be suspended or debugger/profiler
+ * activity has begun. If so, we suspend the thread or side-exit to
+ * the debug interpreter as appropriate.
+ *
+ * The common case is no activity on any of these, so we want to figure
+ * that out quickly. If something is up, we can then sort out what.
+ *
+ * We want to be fast if the VM was built without debugger or profiler
+ * support, but we also need to recognize that the system is usually
+ * shipped with both of these enabled.
+ *
+ * TODO: reduce this so we're just checking a single location.
+ *
+ * On entry:
+ * r0 is reentry type, e.g. kInterpEntryInstr (for debugger/profiling)
+ * r9 is trampoline PC adjustment *in bytes*
+ */
+common_periodicChecks:
+ ldr r3, [rGLUE, #offGlue_pSelfSuspendCount] @ r3<- &suspendCount
+
+ ldr r1, [rGLUE, #offGlue_pDebuggerActive] @ r1<- &debuggerActive
+ ldr r2, [rGLUE, #offGlue_pActiveProfilers] @ r2<- &activeProfilers
+
+ ldr ip, [r3] @ ip<- suspendCount (int)
+
+ cmp r1, #0 @ debugger enabled?
+ ldrneb r1, [r1] @ yes, r1<- debuggerActive (boolean)
+ ldr r2, [r2] @ r2<- activeProfilers (int)
+ orrne ip, ip, r1 @ ip<- suspendCount | debuggerActive
+ orrs ip, ip, r2 @ ip<- suspend|debugger|profiler; set Z
+
+ bxeq lr @ all zero, return
+
+ /*
+ * One or more interesting events have happened. Figure out what.
+ *
+ * If debugging or profiling are compiled in, we need to disambiguate.
+ *
+ * r0 still holds the reentry type.
+ */
+ ldr ip, [r3] @ ip<- suspendCount (int)
+ cmp ip, #0 @ want suspend?
+ beq 1f @ no, must be debugger/profiler
+
+ stmfd sp!, {r0, lr} @ preserve r0 and lr
+#if defined(WITH_JIT)
+ /*
+ * Refresh the Jit's cached copy of profile table pointer. This pointer
+ * doubles as the Jit's on/off switch.
+ */
+ ldr r3, [rGLUE, #offGlue_ppJitProfTable] @ r3<-&gDvmJit.pJitProfTable
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ ldr r3, [r3] @ r3 <- pJitProfTable
+ EXPORT_PC() @ need for precise GC
+ str r3, [rGLUE, #offGlue_pJitProfTable] @ refresh Jit's on/off switch
+#else
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ EXPORT_PC() @ need for precise GC
+#endif
+ bl dvmCheckSuspendPending @ do full check, suspend if necessary
+ ldmfd sp!, {r0, lr} @ restore r0 and lr
+
+ /*
+ * Reload the debugger/profiler enable flags. We're checking to see
+ * if either of these got set while we were suspended.
+ *
+ * We can't really avoid the #ifdefs here, because the fields don't
+ * exist when the feature is disabled.
+ */
+ ldr r1, [rGLUE, #offGlue_pDebuggerActive] @ r1<- &debuggerActive
+ cmp r1, #0 @ debugger enabled?
+ ldrneb r1, [r1] @ yes, r1<- debuggerActive (boolean)
+ ldr r2, [rGLUE, #offGlue_pActiveProfilers] @ r2<- &activeProfilers
+ ldr r2, [r2] @ r2<- activeProfilers (int)
+
+ orrs r1, r1, r2
+ beq 2f
+
+1: @ debugger/profiler enabled, bail out; glue->entryPoint was set above
+ str r0, [rGLUE, #offGlue_entryPoint] @ store r0, need for debug/prof
+ add rPC, rPC, r9 @ update rPC
+ mov r1, #1 @ "want switch" = true
+ b common_gotoBail @ side exit
+
+2:
+ bx lr @ nothing to do, return
+
+
+/*
+ * The equivalent of "goto bail", this calls through the "bail handler".
+ *
+ * State registers will be saved to the "glue" area before bailing.
+ *
+ * On entry:
+ * r1 is "bool changeInterp", indicating if we want to switch to the
+ * other interpreter or just bail all the way out
+ */
+common_gotoBail:
+ SAVE_PC_FP_TO_GLUE() @ export state to "glue"
+ mov r0, rGLUE @ r0<- glue ptr
+ b dvmMterpStdBail @ call(glue, changeInterp)
+
+ @add r1, r1, #1 @ using (boolean+1)
+ @add r0, rGLUE, #offGlue_jmpBuf @ r0<- &glue->jmpBuf
+ @bl _longjmp @ does not return
+ @bl common_abort
+
+
+/*
+ * Common code for method invocation with range.
+ *
+ * On entry:
+ * r0 is "Method* methodToCall", the method we're trying to call
+ */
+common_invokeMethodRange:
+.LinvokeNewRange:
+ @ prepare to copy args to "outs" area of current frame
+ movs r2, rINST, lsr #8 @ r2<- AA (arg count) -- test for zero
+ SAVEAREA_FROM_FP(r10, rFP) @ r10<- stack save area
+ beq .LinvokeArgsDone @ if no args, skip the rest
+ FETCH(r1, 2) @ r1<- CCCC
+
+ @ r0=methodToCall, r1=CCCC, r2=count, r10=outs
+ @ (very few methods have > 10 args; could unroll for common cases)
+ add r3, rFP, r1, lsl #2 @ r3<- &fp[CCCC]
+ sub r10, r10, r2, lsl #2 @ r10<- "outs" area, for call args
+ ldrh r9, [r0, #offMethod_registersSize] @ r9<- methodToCall->regsSize
+1: ldr r1, [r3], #4 @ val = *fp++
+ subs r2, r2, #1 @ count--
+ str r1, [r10], #4 @ *outs++ = val
+ bne 1b @ ...while count != 0
+ ldrh r3, [r0, #offMethod_outsSize] @ r3<- methodToCall->outsSize
+ b .LinvokeArgsDone
+
+/*
+ * Common code for method invocation without range.
+ *
+ * On entry:
+ * r0 is "Method* methodToCall", the method we're trying to call
+ */
+common_invokeMethodNoRange:
+.LinvokeNewNoRange:
+ @ prepare to copy args to "outs" area of current frame
+ movs r2, rINST, lsr #12 @ r2<- B (arg count) -- test for zero
+ SAVEAREA_FROM_FP(r10, rFP) @ r10<- stack save area
+ FETCH(r1, 2) @ r1<- GFED (load here to hide latency)
+ ldrh r9, [r0, #offMethod_registersSize] @ r9<- methodToCall->regsSize
+ ldrh r3, [r0, #offMethod_outsSize] @ r3<- methodToCall->outsSize
+ beq .LinvokeArgsDone
+
+ @ r0=methodToCall, r1=GFED, r3=outSize, r2=count, r9=regSize, r10=outs
+.LinvokeNonRange:
+ rsb r2, r2, #5 @ r2<- 5-r2
+ add pc, pc, r2, lsl #4 @ computed goto, 4 instrs each
+ bl common_abort @ (skipped due to ARM prefetch)
+5: and ip, rINST, #0x0f00 @ isolate A
+ ldr r2, [rFP, ip, lsr #6] @ r2<- vA (shift right 8, left 2)
+ mov r0, r0 @ nop
+ str r2, [r10, #-4]! @ *--outs = vA
+4: and ip, r1, #0xf000 @ isolate G
+ ldr r2, [rFP, ip, lsr #10] @ r2<- vG (shift right 12, left 2)
+ mov r0, r0 @ nop
+ str r2, [r10, #-4]! @ *--outs = vG
+3: and ip, r1, #0x0f00 @ isolate F
+ ldr r2, [rFP, ip, lsr #6] @ r2<- vF
+ mov r0, r0 @ nop
+ str r2, [r10, #-4]! @ *--outs = vF
+2: and ip, r1, #0x00f0 @ isolate E
+ ldr r2, [rFP, ip, lsr #2] @ r2<- vE
+ mov r0, r0 @ nop
+ str r2, [r10, #-4]! @ *--outs = vE
+1: and ip, r1, #0x000f @ isolate D
+ ldr r2, [rFP, ip, lsl #2] @ r2<- vD
+ mov r0, r0 @ nop
+ str r2, [r10, #-4]! @ *--outs = vD
+0: @ fall through to .LinvokeArgsDone
+
+.LinvokeArgsDone: @ r0=methodToCall, r3=outSize, r9=regSize
+ ldr r2, [r0, #offMethod_insns] @ r2<- method->insns
+ ldr rINST, [r0, #offMethod_clazz] @ rINST<- method->clazz
+ @ find space for the new stack frame, check for overflow
+ SAVEAREA_FROM_FP(r1, rFP) @ r1<- stack save area
+ sub r1, r1, r9, lsl #2 @ r1<- newFp (old savearea - regsSize)
+ SAVEAREA_FROM_FP(r10, r1) @ r10<- newSaveArea
+@ bl common_dumpRegs
+ ldr r9, [rGLUE, #offGlue_interpStackEnd] @ r9<- interpStackEnd
+ sub r3, r10, r3, lsl #2 @ r3<- bottom (newsave - outsSize)
+ cmp r3, r9 @ bottom < interpStackEnd?
+ ldr r3, [r0, #offMethod_accessFlags] @ r3<- methodToCall->accessFlags
+ blo .LstackOverflow @ yes, this frame will overflow stack
+
+ @ set up newSaveArea
+#ifdef EASY_GDB
+ SAVEAREA_FROM_FP(ip, rFP) @ ip<- stack save area
+ str ip, [r10, #offStackSaveArea_prevSave]
+#endif
+ str rFP, [r10, #offStackSaveArea_prevFrame]
+ str rPC, [r10, #offStackSaveArea_savedPc]
+#if defined(WITH_JIT)
+ mov r9, #0
+ str r9, [r10, #offStackSaveArea_returnAddr]
+#endif
+ str r0, [r10, #offStackSaveArea_method]
+ tst r3, #ACC_NATIVE
+ bne .LinvokeNative
+
+ /*
+ stmfd sp!, {r0-r3}
+ bl common_printNewline
+ mov r0, rFP
+ mov r1, #0
+ bl dvmDumpFp
+ ldmfd sp!, {r0-r3}
+ stmfd sp!, {r0-r3}
+ mov r0, r1
+ mov r1, r10
+ bl dvmDumpFp
+ bl common_printNewline
+ ldmfd sp!, {r0-r3}
+ */
+
+ ldrh r9, [r2] @ r9 <- load INST from new PC
+ ldr r3, [rINST, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+ mov rPC, r2 @ publish new rPC
+ ldr r2, [rGLUE, #offGlue_self] @ r2<- glue->self
+
+ @ Update "glue" values for the new method
+ @ r0=methodToCall, r1=newFp, r2=self, r3=newMethodClass, r9=newINST
+ str r0, [rGLUE, #offGlue_method] @ glue->method = methodToCall
+ str r3, [rGLUE, #offGlue_methodClassDex] @ glue->methodClassDex = ...
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ mov rFP, r1 @ fp = newFp
+ GET_PREFETCHED_OPCODE(ip, r9) @ extract prefetched opcode from r9
+ mov rINST, r9 @ publish new rINST
+ str r1, [r2, #offThread_curFrame] @ self->curFrame = newFp
+ cmp r0,#0
+ bne common_updateProfile
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ mov rFP, r1 @ fp = newFp
+ GET_PREFETCHED_OPCODE(ip, r9) @ extract prefetched opcode from r9
+ mov rINST, r9 @ publish new rINST
+ str r1, [r2, #offThread_curFrame] @ self->curFrame = newFp
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+.LinvokeNative:
+ @ Prep for the native call
+ @ r0=methodToCall, r1=newFp, r10=newSaveArea
+ ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
+ ldr r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
+ str r1, [r3, #offThread_curFrame] @ self->curFrame = newFp
+ str r9, [r10, #offStackSaveArea_localRefCookie] @newFp->localRefCookie=top
+ mov r9, r3 @ r9<- glue->self (preserve)
+
+ mov r2, r0 @ r2<- methodToCall
+ mov r0, r1 @ r0<- newFp (points to args)
+ add r1, rGLUE, #offGlue_retval @ r1<- &retval
+
+#ifdef ASSIST_DEBUGGER
+ /* insert fake function header to help gdb find the stack frame */
+ b .Lskip
+ .type dalvik_mterp, %function
+dalvik_mterp:
+ .fnstart
+ MTERP_ENTRY1
+ MTERP_ENTRY2
+.Lskip:
+#endif
+
+ @mov lr, pc @ set return addr
+ @ldr pc, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
+ LDR_PC_LR "[r2, #offMethod_nativeFunc]"
+
+#if defined(WITH_JIT)
+ ldr r3, [rGLUE, #offGlue_ppJitProfTable] @ Refresh Jit's on/off status
+#endif
+
+ @ native return; r9=self, r10=newSaveArea
+ @ equivalent to dvmPopJniLocals
+ ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved top
+ ldr r1, [r9, #offThread_exception] @ check for exception
+#if defined(WITH_JIT)
+ ldr r3, [r3] @ r3 <- gDvmJit.pProfTable
+#endif
+ str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
+ cmp r1, #0 @ null?
+ str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
+#if defined(WITH_JIT)
+ str r3, [rGLUE, #offGlue_pJitProfTable] @ refresh cached on/off switch
+#endif
+ bne common_exceptionThrown @ no, handle exception
+
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+.LstackOverflow: @ r0=methodToCall
+ mov r1, r0 @ r1<- methodToCall
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- self
+ bl dvmHandleStackOverflow
+ b common_exceptionThrown
+#ifdef ASSIST_DEBUGGER
+ .fnend
+#endif
+
+
+ /*
+ * Common code for method invocation, calling through "glue code".
+ *
+ * TODO: now that we have range and non-range invoke handlers, this
+ * needs to be split into two. Maybe just create entry points
+ * that set r9 and jump here?
+ *
+ * On entry:
+ * r0 is "Method* methodToCall", the method we're trying to call
+ * r9 is "bool methodCallRange", indicating if this is a /range variant
+ */
+ .if 0
+.LinvokeOld:
+ sub sp, sp, #8 @ space for args + pad
+ FETCH(ip, 2) @ ip<- FEDC or CCCC
+ mov r2, r0 @ A2<- methodToCall
+ mov r0, rGLUE @ A0<- glue
+ SAVE_PC_FP_TO_GLUE() @ export state to "glue"
+ mov r1, r9 @ A1<- methodCallRange
+ mov r3, rINST, lsr #8 @ A3<- AA
+ str ip, [sp, #0] @ A4<- ip
+ bl dvmMterp_invokeMethod @ call the C invokeMethod
+ add sp, sp, #8 @ remove arg area
+ b common_resumeAfterGlueCall @ continue to next instruction
+ .endif
+
+
+
+/*
+ * Common code for handling a return instruction.
+ *
+ * This does not return.
+ */
+common_returnFromMethod:
+.LreturnNew:
+ mov r0, #kInterpEntryReturn
+ mov r9, #0
+ bl common_periodicChecks
+
+ SAVEAREA_FROM_FP(r0, rFP) @ r0<- saveArea (old)
+ ldr rFP, [r0, #offStackSaveArea_prevFrame] @ fp = saveArea->prevFrame
+ ldr r9, [r0, #offStackSaveArea_savedPc] @ r9 = saveArea->savedPc
+ ldr r2, [rFP, #(offStackSaveArea_method - sizeofStackSaveArea)]
+ @ r2<- method we're returning to
+ ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
+ cmp r2, #0 @ is this a break frame?
+ ldrne r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+ mov r1, #0 @ "want switch" = false
+ beq common_gotoBail @ break frame, bail out completely
+
+ PREFETCH_ADVANCE_INST(rINST, r9, 3) @ advance r9, update new rINST
+ str r2, [rGLUE, #offGlue_method]@ glue->method = newSave->method
+ ldr r1, [r10, #offClassObject_pDvmDex] @ r1<- method->clazz->pDvmDex
+ str rFP, [r3, #offThread_curFrame] @ self->curFrame = fp
+#if defined(WITH_JIT)
+ ldr r10, [r0, #offStackSaveArea_returnAddr] @ r10 = saveArea->returnAddr
+ mov rPC, r9 @ publish new rPC
+ str r1, [rGLUE, #offGlue_methodClassDex]
+ str r10, [r3, #offThread_inJitCodeCache] @ may return to JIT'ed land
+ cmp r10, #0 @ caller is compiled code
+ blxne r10
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ mov rPC, r9 @ publish new rPC
+ str r1, [rGLUE, #offGlue_methodClassDex]
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+ /*
+ * Return handling, calls through "glue code".
+ */
+ .if 0
+.LreturnOld:
+ SAVE_PC_FP_TO_GLUE() @ export state
+ mov r0, rGLUE @ arg to function
+ bl dvmMterp_returnFromMethod
+ b common_resumeAfterGlueCall
+ .endif
+
+
+/*
+ * Somebody has thrown an exception. Handle it.
+ *
+ * If the exception processing code returns to us (instead of falling
+ * out of the interpreter), continue with whatever the next instruction
+ * now happens to be.
+ *
+ * This does not return.
+ */
+ .global dvmMterpCommonExceptionThrown
+dvmMterpCommonExceptionThrown:
+common_exceptionThrown:
+.LexceptionNew:
+ mov r0, #kInterpEntryThrow
+ mov r9, #0
+ bl common_periodicChecks
+
+ ldr r10, [rGLUE, #offGlue_self] @ r10<- glue->self
+ ldr r9, [r10, #offThread_exception] @ r9<- self->exception
+ mov r1, r10 @ r1<- self
+ mov r0, r9 @ r0<- exception
+ bl dvmAddTrackedAlloc @ don't let the exception be GCed
+ mov r3, #0 @ r3<- NULL
+ str r3, [r10, #offThread_exception] @ self->exception = NULL
+
+ /* set up args and a local for "&fp" */
+ /* (str sp, [sp, #-4]! would be perfect here, but is discouraged) */
+ str rFP, [sp, #-4]! @ *--sp = fp
+ mov ip, sp @ ip<- &fp
+ mov r3, #0 @ r3<- false
+ str ip, [sp, #-4]! @ *--sp = &fp
+ ldr r1, [rGLUE, #offGlue_method] @ r1<- glue->method
+ mov r0, r10 @ r0<- self
+ ldr r1, [r1, #offMethod_insns] @ r1<- method->insns
+ mov r2, r9 @ r2<- exception
+ sub r1, rPC, r1 @ r1<- pc - method->insns
+ mov r1, r1, asr #1 @ r1<- offset in code units
+
+ /* call, r0 gets catchRelPc (a code-unit offset) */
+ bl dvmFindCatchBlock @ call(self, relPc, exc, scan?, &fp)
+
+ /* fix earlier stack overflow if necessary; may trash rFP */
+ ldrb r1, [r10, #offThread_stackOverflowed]
+ cmp r1, #0 @ did we overflow earlier?
+ beq 1f @ no, skip ahead
+ mov rFP, r0 @ save relPc result in rFP
+ mov r0, r10 @ r0<- self
+ mov r1, r9 @ r1<- exception
+ bl dvmCleanupStackOverflow @ call(self)
+ mov r0, rFP @ restore result
+1:
+
+ /* update frame pointer and check result from dvmFindCatchBlock */
+ ldr rFP, [sp, #4] @ retrieve the updated rFP
+ cmp r0, #0 @ is catchRelPc < 0?
+ add sp, sp, #8 @ restore stack
+ bmi .LnotCaughtLocally
+
+ /* adjust locals to match self->curFrame and updated PC */
+ SAVEAREA_FROM_FP(r1, rFP) @ r1<- new save area
+ ldr r1, [r1, #offStackSaveArea_method] @ r1<- new method
+ str r1, [rGLUE, #offGlue_method] @ glue->method = new method
+ ldr r2, [r1, #offMethod_clazz] @ r2<- method->clazz
+ ldr r3, [r1, #offMethod_insns] @ r3<- method->insns
+ ldr r2, [r2, #offClassObject_pDvmDex] @ r2<- method->clazz->pDvmDex
+ add rPC, r3, r0, asl #1 @ rPC<- method->insns + catchRelPc
+ str r2, [rGLUE, #offGlue_methodClassDex] @ glue->pDvmDex = meth...
+
+ /* release the tracked alloc on the exception */
+ mov r0, r9 @ r0<- exception
+ mov r1, r10 @ r1<- self
+ bl dvmReleaseTrackedAlloc @ release the exception
+
+ /* restore the exception if the handler wants it */
+ FETCH_INST() @ load rINST from rPC
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ cmp ip, #OP_MOVE_EXCEPTION @ is it "move-exception"?
+ streq r9, [r10, #offThread_exception] @ yes, restore the exception
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+.LnotCaughtLocally: @ r9=exception, r10=self
+ /* fix stack overflow if necessary */
+ ldrb r1, [r10, #offThread_stackOverflowed]
+ cmp r1, #0 @ did we overflow earlier?
+ movne r0, r10 @ if yes: r0<- self
+ movne r1, r9 @ if yes: r1<- exception
+ blne dvmCleanupStackOverflow @ if yes: call(self)
+
+ @ may want to show "not caught locally" debug messages here
+#if DVM_SHOW_EXCEPTION >= 2
+ /* call __android_log_print(prio, tag, format, ...) */
+ /* "Exception %s from %s:%d not caught locally" */
+ @ dvmLineNumFromPC(method, pc - method->insns)
+ ldr r0, [rGLUE, #offGlue_method]
+ ldr r1, [r0, #offMethod_insns]
+ sub r1, rPC, r1
+ asr r1, r1, #1
+ bl dvmLineNumFromPC
+ str r0, [sp, #-4]!
+ @ dvmGetMethodSourceFile(method)
+ ldr r0, [rGLUE, #offGlue_method]
+ bl dvmGetMethodSourceFile
+ str r0, [sp, #-4]!
+ @ exception->clazz->descriptor
+ ldr r3, [r9, #offObject_clazz]
+ ldr r3, [r3, #offClassObject_descriptor]
+ @
+ ldr r2, strExceptionNotCaughtLocally
+ ldr r1, strLogTag
+ mov r0, #3 @ LOG_DEBUG
+ bl __android_log_print
+#endif
+ str r9, [r10, #offThread_exception] @ restore exception
+ mov r0, r9 @ r0<- exception
+ mov r1, r10 @ r1<- self
+ bl dvmReleaseTrackedAlloc @ release the exception
+ mov r1, #0 @ "want switch" = false
+ b common_gotoBail @ bail out
+
+
+ /*
+ * Exception handling, calls through "glue code".
+ */
+ .if 0
+.LexceptionOld:
+ SAVE_PC_FP_TO_GLUE() @ export state
+ mov r0, rGLUE @ arg to function
+ bl dvmMterp_exceptionThrown
+ b common_resumeAfterGlueCall
+ .endif
+
+
+/*
+ * After returning from a "glued" function, pull out the updated
+ * values and start executing at the next instruction.
+ */
+common_resumeAfterGlueCall:
+ LOAD_PC_FP_FROM_GLUE() @ pull rPC and rFP out of glue
+ FETCH_INST() @ load rINST from rPC
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/*
+ * Invalid array index.
+ */
+common_errArrayIndex:
+ EXPORT_PC()
+ ldr r0, strArrayIndexException
+ mov r1, #0
+ bl dvmThrowException
+ b common_exceptionThrown
+
+/*
+ * Invalid array value.
+ */
+common_errArrayStore:
+ EXPORT_PC()
+ ldr r0, strArrayStoreException
+ mov r1, #0
+ bl dvmThrowException
+ b common_exceptionThrown
+
+/*
+ * Integer divide or mod by zero.
+ */
+common_errDivideByZero:
+ EXPORT_PC()
+ ldr r0, strArithmeticException
+ ldr r1, strDivideByZero
+ bl dvmThrowException
+ b common_exceptionThrown
+
+/*
+ * Attempt to allocate an array with a negative size.
+ */
+common_errNegativeArraySize:
+ EXPORT_PC()
+ ldr r0, strNegativeArraySizeException
+ mov r1, #0
+ bl dvmThrowException
+ b common_exceptionThrown
+
+/*
+ * Invocation of a non-existent method.
+ */
+common_errNoSuchMethod:
+ EXPORT_PC()
+ ldr r0, strNoSuchMethodError
+ mov r1, #0
+ bl dvmThrowException
+ b common_exceptionThrown
+
+/*
+ * We encountered a null object when we weren't expecting one. We
+ * export the PC, throw a NullPointerException, and goto the exception
+ * processing code.
+ */
+common_errNullObject:
+ EXPORT_PC()
+ ldr r0, strNullPointerException
+ mov r1, #0
+ bl dvmThrowException
+ b common_exceptionThrown
+
+/*
+ * For debugging, cause an immediate fault. The source address will
+ * be in lr (use a bl instruction to jump here).
+ */
+common_abort:
+ ldr pc, .LdeadFood
+.LdeadFood:
+ .word 0xdeadf00d
+
+/*
+ * Spit out a "we were here", preserving all registers. (The attempt
+ * to save ip won't work, but we need to save an even number of
+ * registers for EABI 64-bit stack alignment.)
+ */
+ .macro SQUEAK num
+common_squeak\num:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ ldr r0, strSqueak
+ mov r1, #\num
+ bl printf
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+ .endm
+
+ SQUEAK 0
+ SQUEAK 1
+ SQUEAK 2
+ SQUEAK 3
+ SQUEAK 4
+ SQUEAK 5
+
+/*
+ * Spit out the number in r0, preserving registers.
+ */
+common_printNum:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ mov r1, r0
+ ldr r0, strSqueak
+ bl printf
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+
+/*
+ * Print a newline, preserving registers.
+ */
+common_printNewline:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ ldr r0, strNewline
+ bl printf
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+
+ /*
+ * Print the 32-bit quantity in r0 as a hex value, preserving registers.
+ */
+common_printHex:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ mov r1, r0
+ ldr r0, strPrintHex
+ bl printf
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+
+/*
+ * Print the 64-bit quantity in r0-r1, preserving registers.
+ */
+common_printLong:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ mov r3, r1
+ mov r2, r0
+ ldr r0, strPrintLong
+ bl printf
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+
+/*
+ * Print full method info. Pass the Method* in r0. Preserves regs.
+ */
+common_printMethod:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bl dvmMterpPrintMethod
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+
+/*
+ * Call a C helper function that dumps regs and possibly some
+ * additional info. Requires the C function to be compiled in.
+ */
+ .if 0
+common_dumpRegs:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bl dvmMterpDumpArmRegs
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+ .endif
+
+#if 0
+/*
+ * Experiment on VFP mode.
+ *
+ * uint32_t setFPSCR(uint32_t val, uint32_t mask)
+ *
+ * Updates the bits specified by "mask", setting them to the values in "val".
+ */
+setFPSCR:
+ and r0, r0, r1 @ make sure no stray bits are set
+ fmrx r2, fpscr @ get VFP reg
+ mvn r1, r1 @ bit-invert mask
+ and r2, r2, r1 @ clear masked bits
+ orr r2, r2, r0 @ set specified bits
+ fmxr fpscr, r2 @ set VFP reg
+ mov r0, r2 @ return new value
+ bx lr
+
+ .align 2
+ .global dvmConfigureFP
+ .type dvmConfigureFP, %function
+dvmConfigureFP:
+ stmfd sp!, {ip, lr}
+ /* 0x03000000 sets DN/FZ */
+ /* 0x00009f00 clears the six exception enable flags */
+ bl common_squeak0
+ mov r0, #0x03000000 @ r0<- 0x03000000
+ add r1, r0, #0x9f00 @ r1<- 0x03009f00
+ bl setFPSCR
+ ldmfd sp!, {ip, pc}
+#endif
+
+
+/*
+ * String references, must be close to the code that uses them.
+ */
+ .align 2
+strArithmeticException:
+ .word .LstrArithmeticException
+strArrayIndexException:
+ .word .LstrArrayIndexException
+strArrayStoreException:
+ .word .LstrArrayStoreException
+strDivideByZero:
+ .word .LstrDivideByZero
+strNegativeArraySizeException:
+ .word .LstrNegativeArraySizeException
+strNoSuchMethodError:
+ .word .LstrNoSuchMethodError
+strNullPointerException:
+ .word .LstrNullPointerException
+
+strLogTag:
+ .word .LstrLogTag
+strExceptionNotCaughtLocally:
+ .word .LstrExceptionNotCaughtLocally
+
+strNewline:
+ .word .LstrNewline
+strSqueak:
+ .word .LstrSqueak
+strPrintHex:
+ .word .LstrPrintHex
+strPrintLong:
+ .word .LstrPrintLong
+
+/*
+ * Zero-terminated ASCII string data.
+ *
+ * On ARM we have two choices: do like gcc does, and LDR from a .word
+ * with the address, or use an ADR pseudo-op to get the address
+ * directly. ADR saves 4 bytes and an indirection, but it's using a
+ * PC-relative addressing mode and hence has a limited range, which
+ * makes it not work well with mergeable string sections.
+ */
+ .section .rodata.str1.4,"aMS",%progbits,1
+
+.LstrBadEntryPoint:
+ .asciz "Bad entry point %d\n"
+.LstrArithmeticException:
+ .asciz "Ljava/lang/ArithmeticException;"
+.LstrArrayIndexException:
+ .asciz "Ljava/lang/ArrayIndexOutOfBoundsException;"
+.LstrArrayStoreException:
+ .asciz "Ljava/lang/ArrayStoreException;"
+.LstrClassCastException:
+ .asciz "Ljava/lang/ClassCastException;"
+.LstrDivideByZero:
+ .asciz "divide by zero"
+.LstrFilledNewArrayNotImpl:
+ .asciz "filled-new-array only implemented for objects and 'int'"
+.LstrInternalError:
+ .asciz "Ljava/lang/InternalError;"
+.LstrInstantiationError:
+ .asciz "Ljava/lang/InstantiationError;"
+.LstrNegativeArraySizeException:
+ .asciz "Ljava/lang/NegativeArraySizeException;"
+.LstrNoSuchMethodError:
+ .asciz "Ljava/lang/NoSuchMethodError;"
+.LstrNullPointerException:
+ .asciz "Ljava/lang/NullPointerException;"
+
+.LstrLogTag:
+ .asciz "mterp"
+.LstrExceptionNotCaughtLocally:
+ .asciz "Exception %s from %s:%d not caught locally\n"
+
+.LstrNewline:
+ .asciz "\n"
+.LstrSqueak:
+ .asciz "<%d>"
+.LstrPrintHex:
+ .asciz "<0x%x>"
+.LstrPrintLong:
+ .asciz "<%lld>"
+
diff --git a/vm/mterp/out/InterpAsm-armv7-a.S b/vm/mterp/out/InterpAsm-armv7-a.S
new file mode 100644
index 0000000..ac5b2c3
--- /dev/null
+++ b/vm/mterp/out/InterpAsm-armv7-a.S
@@ -0,0 +1,10549 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'armv7-a'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: armv5te/header.S */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * ARMv5 definitions and declarations.
+ */
+
+/*
+ARM EABI general notes:
+
+r0-r3 hold first 4 args to a method; they are not preserved across method calls
+r4-r8 are available for general use
+r9 is given special treatment in some situations, but not for us
+r10 (sl) seems to be generally available
+r11 (fp) is used by gcc (unless -fomit-frame-pointer is set)
+r12 (ip) is scratch -- not preserved across method calls
+r13 (sp) should be managed carefully in case a signal arrives
+r14 (lr) must be preserved
+r15 (pc) can be tinkered with directly
+
+r0 holds returns of <= 4 bytes
+r0-r1 hold returns of 8 bytes, low word in r0
+
+Callee must save/restore r4+ (except r12) if it modifies them. If VFP
+is present, registers s16-s31 (a/k/a d8-d15, a/k/a q4-q7) must be preserved,
+s0-s15 (d0-d7, q0-a3) do not need to be.
+
+Stack is "full descending". Only the arguments that don't fit in the first 4
+registers are placed on the stack. "sp" points at the first stacked argument
+(i.e. the 5th arg).
+
+VFP: single-precision results in s0, double-precision results in d0.
+
+In the EABI, "sp" must be 64-bit aligned on entry to a function, and any
+64-bit quantities (long long, double) must be 64-bit aligned.
+*/
+
+/*
+Mterp and ARM notes:
+
+The following registers have fixed assignments:
+
+ reg nick purpose
+ r4 rPC interpreted program counter, used for fetching instructions
+ r5 rFP interpreted frame pointer, used for accessing locals and args
+ r6 rGLUE MterpGlue pointer
+ r7 rINST first 16-bit code unit of current instruction
+ r8 rIBASE interpreted instruction base pointer, used for computed goto
+
+Macros are provided for common operations. Each macro MUST emit only
+one instruction to make instruction-counting easier. They MUST NOT alter
+unspecified registers or condition codes.
+*/
+
+/* single-purpose registers, given names for clarity */
+#define rPC r4
+#define rFP r5
+#define rGLUE r6
+#define rINST r7
+#define rIBASE r8
+
+/* save/restore the PC and/or FP from the glue struct */
+#define LOAD_PC_FROM_GLUE() ldr rPC, [rGLUE, #offGlue_pc]
+#define SAVE_PC_TO_GLUE() str rPC, [rGLUE, #offGlue_pc]
+#define LOAD_FP_FROM_GLUE() ldr rFP, [rGLUE, #offGlue_fp]
+#define SAVE_FP_TO_GLUE() str rFP, [rGLUE, #offGlue_fp]
+#define LOAD_PC_FP_FROM_GLUE() ldmia rGLUE, {rPC, rFP}
+#define SAVE_PC_FP_TO_GLUE() stmia rGLUE, {rPC, rFP}
+
+/*
+ * "export" the PC to the stack frame, f/b/o future exception objects. Must
+ * be done *before* something calls dvmThrowException.
+ *
+ * In C this is "SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc", i.e.
+ * fp - sizeof(StackSaveArea) + offsetof(SaveArea, xtra.currentPc)
+ *
+ * It's okay to do this more than once.
+ */
+#define EXPORT_PC() \
+ str rPC, [rFP, #(-sizeofStackSaveArea + offStackSaveArea_currentPc)]
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+#define SAVEAREA_FROM_FP(_reg, _fpreg) \
+ sub _reg, _fpreg, #sizeofStackSaveArea
+
+/*
+ * Fetch the next instruction from rPC into rINST. Does not advance rPC.
+ */
+#define FETCH_INST() ldrh rINST, [rPC]
+
+/*
+ * Fetch the next instruction from the specified offset. Advances rPC
+ * to point to the next instruction. "_count" is in 16-bit code units.
+ *
+ * Because of the limited size of immediate constants on ARM, this is only
+ * suitable for small forward movements (i.e. don't try to implement "goto"
+ * with this).
+ *
+ * This must come AFTER anything that can throw an exception, or the
+ * exception catch may miss. (This also implies that it must come after
+ * EXPORT_PC().)
+ */
+#define FETCH_ADVANCE_INST(_count) ldrh rINST, [rPC, #(_count*2)]!
+
+/*
+ * The operation performed here is similar to FETCH_ADVANCE_INST, except the
+ * src and dest registers are parameterized (not hard-wired to rPC and rINST).
+ */
+#define PREFETCH_ADVANCE_INST(_dreg, _sreg, _count) \
+ ldrh _dreg, [_sreg, #(_count*2)]!
+
+/*
+ * Fetch the next instruction from an offset specified by _reg. Updates
+ * rPC to point to the next instruction. "_reg" must specify the distance
+ * in bytes, *not* 16-bit code units, and may be a signed value.
+ *
+ * We want to write "ldrh rINST, [rPC, _reg, lsl #2]!", but some of the
+ * bits that hold the shift distance are used for the half/byte/sign flags.
+ * In some cases we can pre-double _reg for free, so we require a byte offset
+ * here.
+ */
+#define FETCH_ADVANCE_INST_RB(_reg) ldrh rINST, [rPC, _reg]!
+
+/*
+ * Fetch a half-word code unit from an offset past the current PC. The
+ * "_count" value is in 16-bit code units. Does not advance rPC.
+ *
+ * The "_S" variant works the same but treats the value as signed.
+ */
+#define FETCH(_reg, _count) ldrh _reg, [rPC, #(_count*2)]
+#define FETCH_S(_reg, _count) ldrsh _reg, [rPC, #(_count*2)]
+
+/*
+ * Fetch one byte from an offset past the current PC. Pass in the same
+ * "_count" as you would for FETCH, and an additional 0/1 indicating which
+ * byte of the halfword you want (lo/hi).
+ */
+#define FETCH_B(_reg, _count, _byte) ldrb _reg, [rPC, #(_count*2+_byte)]
+
+/*
+ * Put the instruction's opcode field into the specified register.
+ */
+#define GET_INST_OPCODE(_reg) and _reg, rINST, #255
+
+/*
+ * Put the prefetched instruction's opcode field into the specified register.
+ */
+#define GET_PREFETCHED_OPCODE(_oreg, _ireg) and _oreg, _ireg, #255
+
+/*
+ * Begin executing the opcode in _reg. Because this only jumps within the
+ * interpreter, we don't have to worry about pre-ARMv5 THUMB interwork.
+ */
+#define GOTO_OPCODE(_reg) add pc, rIBASE, _reg, lsl #6
+#define GOTO_OPCODE_IFEQ(_reg) addeq pc, rIBASE, _reg, lsl #6
+#define GOTO_OPCODE_IFNE(_reg) addne pc, rIBASE, _reg, lsl #6
+
+/*
+ * Get/set the 32-bit value from a Dalvik register.
+ */
+#define GET_VREG(_reg, _vreg) ldr _reg, [rFP, _vreg, lsl #2]
+#define SET_VREG(_reg, _vreg) str _reg, [rFP, _vreg, lsl #2]
+
+#if defined(WITH_JIT)
+#define GET_JIT_PROF_TABLE(_reg) ldr _reg,[rGLUE,#offGlue_pJitProfTable]
+#define GET_JIT_THRESHOLD(_reg) ldr _reg,[rGLUE,#offGlue_jitThreshold]
+#endif
+
+/*
+ * Convert a virtual register index into an address.
+ */
+#define VREG_INDEX_TO_ADDR(_reg, _vreg) \
+ add _reg, rFP, _vreg, lsl #2
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../common/asm-constants.h"
+
+#if defined(WITH_JIT)
+#include "../common/jit-config.h"
+#endif
+
+/* File: armv7-a/platform.S */
+/*
+ * ===========================================================================
+ * CPU-version-specific defines
+ * ===========================================================================
+ */
+
+/*
+ * Macro for "LDR PC,xxx", which is not allowed pre-ARMv5. Essentially a
+ * one-way branch.
+ *
+ * May modify IP. Does not modify LR.
+ */
+.macro LDR_PC source
+ ldr pc, \source
+.endm
+
+/*
+ * Macro for "MOV LR,PC / LDR PC,xxx", which is not allowed pre-ARMv5.
+ * Jump to subroutine.
+ *
+ * May modify IP and LR.
+ */
+.macro LDR_PC_LR source
+ mov lr, pc
+ ldr pc, \source
+.endm
+
+/*
+ * Macro for "LDMFD SP!, {...regs...,PC}".
+ *
+ * May modify IP and LR.
+ */
+.macro LDMFD_PC regs
+ ldmfd sp!, {\regs,pc}
+.endm
+
+#if !defined(ANDROID_SMP)
+# error "Must define ANDROID_SMP"
+#endif
+
+/*
+ * Macro for data memory barrier; not meaningful pre-ARMv6K.
+ * If the argument is nonzero, emit barrier; otherwise, emit nothing.
+ */
+.macro SMP_DMB
+#if ANDROID_SMP != 0
+ dmb
+#else
+ /* not SMP */
+#endif
+.endm
+
+/* File: armv5te/entry.S */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Interpreter entry point.
+ */
+
+/*
+ * We don't have formal stack frames, so gdb scans upward in the code
+ * to find the start of the function (a label with the %function type),
+ * and then looks at the next few instructions to figure out what
+ * got pushed onto the stack. From this it figures out how to restore
+ * the registers, including PC, for the previous stack frame. If gdb
+ * sees a non-function label, it stops scanning, so either we need to
+ * have nothing but assembler-local labels between the entry point and
+ * the break, or we need to fake it out.
+ *
+ * When this is defined, we add some stuff to make gdb less confused.
+ */
+#define ASSIST_DEBUGGER 1
+
+ .text
+ .align 2
+ .global dvmMterpStdRun
+ .type dvmMterpStdRun, %function
+
+/*
+ * On entry:
+ * r0 MterpGlue* glue
+ *
+ * This function returns a boolean "changeInterp" value. The return comes
+ * via a call to dvmMterpStdBail().
+ */
+dvmMterpStdRun:
+#define MTERP_ENTRY1 \
+ .save {r4-r10,fp,lr}; \
+ stmfd sp!, {r4-r10,fp,lr} @ save 9 regs
+#define MTERP_ENTRY2 \
+ .pad #4; \
+ sub sp, sp, #4 @ align 64
+
+ .fnstart
+ MTERP_ENTRY1
+ MTERP_ENTRY2
+
+ /* save stack pointer, add magic word for debuggerd */
+ str sp, [r0, #offGlue_bailPtr] @ save SP for eventual return
+
+ /* set up "named" registers, figure out entry point */
+ mov rGLUE, r0 @ set rGLUE
+ ldr r1, [r0, #offGlue_entryPoint] @ enum is 4 bytes in aapcs-EABI
+ LOAD_PC_FP_FROM_GLUE() @ load rPC and rFP from "glue"
+ adr rIBASE, dvmAsmInstructionStart @ set rIBASE
+ cmp r1, #kInterpEntryInstr @ usual case?
+ bne .Lnot_instr @ no, handle it
+
+#if defined(WITH_JIT)
+.LentryInstr:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ /* Entry is always a possible trace start */
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_INST()
+ mov r1, #0 @ prepare the value for the new state
+ str r1, [r10, #offThread_inJitCodeCache] @ back to the interp land
+ cmp r0,#0 @ is profiling disabled?
+#if !defined(WITH_SELF_VERIFICATION)
+ bne common_updateProfile @ profiling is enabled
+#else
+ ldr r2, [r10, #offThread_shadowSpace] @ to find out the jit exit state
+ beq 1f @ profiling is disabled
+ ldr r3, [r2, #offShadowSpace_jitExitState] @ jit exit state
+ cmp r3, #kSVSTraceSelect @ hot trace following?
+ moveq r2,#kJitTSelectRequestHot @ ask for trace selection
+ beq common_selectTrace @ go build the trace
+ cmp r3, #kSVSNoProfile @ don't profile the next instruction?
+ beq 1f @ intrepret the next instruction
+ b common_updateProfile @ collect profiles
+#endif
+1:
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+#else
+ /* start executing the instruction at rPC */
+ FETCH_INST() @ load rINST from rPC
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+.Lnot_instr:
+ cmp r1, #kInterpEntryReturn @ were we returning from a method?
+ beq common_returnFromMethod
+
+.Lnot_return:
+ cmp r1, #kInterpEntryThrow @ were we throwing an exception?
+ beq common_exceptionThrown
+
+#if defined(WITH_JIT)
+.Lnot_throw:
+ ldr r10,[rGLUE, #offGlue_jitResumeNPC]
+ ldr r2,[rGLUE, #offGlue_jitResumeDPC]
+ cmp r1, #kInterpEntryResume @ resuming after Jit single-step?
+ bne .Lbad_arg
+ cmp rPC,r2
+ bne .LentryInstr @ must have branched, don't resume
+#if defined(WITH_SELF_VERIFICATION)
+ @ glue->entryPoint will be set in dvmSelfVerificationSaveState
+ b jitSVShadowRunStart @ re-enter the translation after the
+ @ single-stepped instruction
+ @noreturn
+#endif
+ mov r1, #kInterpEntryInstr
+ str r1, [rGLUE, #offGlue_entryPoint]
+ bx r10 @ re-enter the translation
+#endif
+
+.Lbad_arg:
+ ldr r0, strBadEntryPoint
+ @ r1 holds value of entryPoint
+ bl printf
+ bl dvmAbort
+ .fnend
+
+
+ .global dvmMterpStdBail
+ .type dvmMterpStdBail, %function
+
+/*
+ * Restore the stack pointer and PC from the save point established on entry.
+ * This is essentially the same as a longjmp, but should be cheaper. The
+ * last instruction causes us to return to whoever called dvmMterpStdRun.
+ *
+ * We pushed some registers on the stack in dvmMterpStdRun, then saved
+ * SP and LR. Here we restore SP, restore the registers, and then restore
+ * LR to PC.
+ *
+ * On entry:
+ * r0 MterpGlue* glue
+ * r1 bool changeInterp
+ */
+dvmMterpStdBail:
+ ldr sp, [r0, #offGlue_bailPtr] @ sp<- saved SP
+ mov r0, r1 @ return the changeInterp value
+ add sp, sp, #4 @ un-align 64
+ LDMFD_PC "r4-r10,fp" @ restore 9 regs and return
+
+
+/*
+ * String references.
+ */
+strBadEntryPoint:
+ .word .LstrBadEntryPoint
+
+
+ .global dvmAsmInstructionStart
+ .type dvmAsmInstructionStart, %function
+dvmAsmInstructionStart = .L_OP_NOP
+ .text
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NOP: /* 0x00 */
+/* File: armv5te/OP_NOP.S */
+ FETCH_ADVANCE_INST(1) @ advance to next instr, load rINST
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ GOTO_OPCODE(ip) @ execute it
+
+#ifdef ASSIST_DEBUGGER
+ /* insert fake function header to help gdb find the stack frame */
+ .type dalvik_inst, %function
+dalvik_inst:
+ .fnstart
+ MTERP_ENTRY1
+ MTERP_ENTRY2
+ .fnend
+#endif
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE: /* 0x01 */
+/* File: armv6t2/OP_MOVE.S */
+ /* for move, move-object, long-to-int */
+ /* op vA, vB */
+ mov r1, rINST, lsr #12 @ r1<- B from 15:12
+ ubfx r0, rINST, #8, #4 @ r0<- A from 11:8
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[B]
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ SET_VREG(r2, r0) @ fp[A]<- r2
+ GOTO_OPCODE(ip) @ execute next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_FROM16: /* 0x02 */
+/* File: armv5te/OP_MOVE_FROM16.S */
+ /* for: move/from16, move-object/from16 */
+ /* op vAA, vBBBB */
+ FETCH(r1, 1) @ r1<- BBBB
+ mov r0, rINST, lsr #8 @ r0<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[BBBB]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r0) @ fp[AA]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_16: /* 0x03 */
+/* File: armv5te/OP_MOVE_16.S */
+ /* for: move/16, move-object/16 */
+ /* op vAAAA, vBBBB */
+ FETCH(r1, 2) @ r1<- BBBB
+ FETCH(r0, 1) @ r0<- AAAA
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[BBBB]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r0) @ fp[AAAA]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_WIDE: /* 0x04 */
+/* File: armv6t2/OP_MOVE_WIDE.S */
+ /* move-wide vA, vB */
+ /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r2, rINST, #8, #4 @ r2<- A
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[A]
+ ldmia r3, {r0-r1} @ r0/r1<- fp[B]
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r2, {r0-r1} @ fp[A]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_WIDE_FROM16: /* 0x05 */
+/* File: armv5te/OP_MOVE_WIDE_FROM16.S */
+ /* move-wide/from16 vAA, vBBBB */
+ /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+ FETCH(r3, 1) @ r3<- BBBB
+ mov r2, rINST, lsr #8 @ r2<- AA
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[BBBB]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[AA]
+ ldmia r3, {r0-r1} @ r0/r1<- fp[BBBB]
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r2, {r0-r1} @ fp[AA]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_WIDE_16: /* 0x06 */
+/* File: armv5te/OP_MOVE_WIDE_16.S */
+ /* move-wide/16 vAAAA, vBBBB */
+ /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+ FETCH(r3, 2) @ r3<- BBBB
+ FETCH(r2, 1) @ r2<- AAAA
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[BBBB]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[AAAA]
+ ldmia r3, {r0-r1} @ r0/r1<- fp[BBBB]
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r2, {r0-r1} @ fp[AAAA]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_OBJECT: /* 0x07 */
+/* File: armv5te/OP_MOVE_OBJECT.S */
+/* File: armv5te/OP_MOVE.S */
+ /* for move, move-object, long-to-int */
+ /* op vA, vB */
+ mov r1, rINST, lsr #12 @ r1<- B from 15:12
+ mov r0, rINST, lsr #8 @ r0<- A from 11:8
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[B]
+ and r0, r0, #15
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ SET_VREG(r2, r0) @ fp[A]<- r2
+ GOTO_OPCODE(ip) @ execute next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_OBJECT_FROM16: /* 0x08 */
+/* File: armv5te/OP_MOVE_OBJECT_FROM16.S */
+/* File: armv5te/OP_MOVE_FROM16.S */
+ /* for: move/from16, move-object/from16 */
+ /* op vAA, vBBBB */
+ FETCH(r1, 1) @ r1<- BBBB
+ mov r0, rINST, lsr #8 @ r0<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[BBBB]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r0) @ fp[AA]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_OBJECT_16: /* 0x09 */
+/* File: armv5te/OP_MOVE_OBJECT_16.S */
+/* File: armv5te/OP_MOVE_16.S */
+ /* for: move/16, move-object/16 */
+ /* op vAAAA, vBBBB */
+ FETCH(r1, 2) @ r1<- BBBB
+ FETCH(r0, 1) @ r0<- AAAA
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[BBBB]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r0) @ fp[AAAA]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_RESULT: /* 0x0a */
+/* File: armv5te/OP_MOVE_RESULT.S */
+ /* for: move-result, move-result-object */
+ /* op vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ ldr r0, [rGLUE, #offGlue_retval] @ r0<- glue->retval.i
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[AA]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_RESULT_WIDE: /* 0x0b */
+/* File: armv5te/OP_MOVE_RESULT_WIDE.S */
+ /* move-result-wide vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ add r3, rGLUE, #offGlue_retval @ r3<- &glue->retval
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[AA]
+ ldmia r3, {r0-r1} @ r0/r1<- retval.j
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r2, {r0-r1} @ fp[AA]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_RESULT_OBJECT: /* 0x0c */
+/* File: armv5te/OP_MOVE_RESULT_OBJECT.S */
+/* File: armv5te/OP_MOVE_RESULT.S */
+ /* for: move-result, move-result-object */
+ /* op vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ ldr r0, [rGLUE, #offGlue_retval] @ r0<- glue->retval.i
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[AA]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_EXCEPTION: /* 0x0d */
+/* File: armv5te/OP_MOVE_EXCEPTION.S */
+ /* move-exception vAA */
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ mov r2, rINST, lsr #8 @ r2<- AA
+ ldr r3, [r0, #offThread_exception] @ r3<- dvmGetException bypass
+ mov r1, #0 @ r1<- 0
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ SET_VREG(r3, r2) @ fp[AA]<- exception obj
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ str r1, [r0, #offThread_exception] @ dvmClearException bypass
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RETURN_VOID: /* 0x0e */
+/* File: armv5te/OP_RETURN_VOID.S */
+ b common_returnFromMethod
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RETURN: /* 0x0f */
+/* File: armv5te/OP_RETURN.S */
+ /*
+ * Return a 32-bit value. Copies the return value into the "glue"
+ * structure, then jumps to the return handler.
+ *
+ * for: return, return-object
+ */
+ /* op vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ GET_VREG(r0, r2) @ r0<- vAA
+ str r0, [rGLUE, #offGlue_retval] @ retval.i <- vAA
+ b common_returnFromMethod
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RETURN_WIDE: /* 0x10 */
+/* File: armv5te/OP_RETURN_WIDE.S */
+ /*
+ * Return a 64-bit value. Copies the return value into the "glue"
+ * structure, then jumps to the return handler.
+ */
+ /* return-wide vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[AA]
+ add r3, rGLUE, #offGlue_retval @ r3<- &glue->retval
+ ldmia r2, {r0-r1} @ r0/r1 <- vAA/vAA+1
+ stmia r3, {r0-r1} @ retval<- r0/r1
+ b common_returnFromMethod
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RETURN_OBJECT: /* 0x11 */
+/* File: armv5te/OP_RETURN_OBJECT.S */
+/* File: armv5te/OP_RETURN.S */
+ /*
+ * Return a 32-bit value. Copies the return value into the "glue"
+ * structure, then jumps to the return handler.
+ *
+ * for: return, return-object
+ */
+ /* op vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ GET_VREG(r0, r2) @ r0<- vAA
+ str r0, [rGLUE, #offGlue_retval] @ retval.i <- vAA
+ b common_returnFromMethod
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_4: /* 0x12 */
+/* File: armv6t2/OP_CONST_4.S */
+ /* const/4 vA, #+B */
+ mov r1, rINST, lsl #16 @ r1<- Bxxx0000
+ ubfx r0, rINST, #8, #4 @ r0<- A
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ mov r1, r1, asr #28 @ r1<- sssssssB (sign-extended)
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ SET_VREG(r1, r0) @ fp[A]<- r1
+ GOTO_OPCODE(ip) @ execute next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_16: /* 0x13 */
+/* File: armv5te/OP_CONST_16.S */
+ /* const/16 vAA, #+BBBB */
+ FETCH_S(r0, 1) @ r0<- ssssBBBB (sign-extended)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r0, r3) @ vAA<- r0
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST: /* 0x14 */
+/* File: armv5te/OP_CONST.S */
+ /* const vAA, #+BBBBbbbb */
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH(r0, 1) @ r0<- bbbb (low)
+ FETCH(r1, 2) @ r1<- BBBB (high)
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ orr r0, r0, r1, lsl #16 @ r0<- BBBBbbbb
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r3) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_HIGH16: /* 0x15 */
+/* File: armv5te/OP_CONST_HIGH16.S */
+ /* const/high16 vAA, #+BBBB0000 */
+ FETCH(r0, 1) @ r0<- 0000BBBB (zero-extended)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ mov r0, r0, lsl #16 @ r0<- BBBB0000
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r0, r3) @ vAA<- r0
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_WIDE_16: /* 0x16 */
+/* File: armv5te/OP_CONST_WIDE_16.S */
+ /* const-wide/16 vAA, #+BBBB */
+ FETCH_S(r0, 1) @ r0<- ssssBBBB (sign-extended)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ mov r1, r0, asr #31 @ r1<- ssssssss
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_WIDE_32: /* 0x17 */
+/* File: armv5te/OP_CONST_WIDE_32.S */
+ /* const-wide/32 vAA, #+BBBBbbbb */
+ FETCH(r0, 1) @ r0<- 0000bbbb (low)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH_S(r2, 2) @ r2<- ssssBBBB (high)
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ orr r0, r0, r2, lsl #16 @ r0<- BBBBbbbb
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[AA]
+ mov r1, r0, asr #31 @ r1<- ssssssss
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_WIDE: /* 0x18 */
+/* File: armv5te/OP_CONST_WIDE.S */
+ /* const-wide vAA, #+HHHHhhhhBBBBbbbb */
+ FETCH(r0, 1) @ r0<- bbbb (low)
+ FETCH(r1, 2) @ r1<- BBBB (low middle)
+ FETCH(r2, 3) @ r2<- hhhh (high middle)
+ orr r0, r0, r1, lsl #16 @ r0<- BBBBbbbb (low word)
+ FETCH(r3, 4) @ r3<- HHHH (high)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ orr r1, r2, r3, lsl #16 @ r1<- HHHHhhhh (high word)
+ FETCH_ADVANCE_INST(5) @ advance rPC, load rINST
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_WIDE_HIGH16: /* 0x19 */
+/* File: armv5te/OP_CONST_WIDE_HIGH16.S */
+ /* const-wide/high16 vAA, #+BBBB000000000000 */
+ FETCH(r1, 1) @ r1<- 0000BBBB (zero-extended)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ mov r0, #0 @ r0<- 00000000
+ mov r1, r1, lsl #16 @ r1<- BBBB0000
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_STRING: /* 0x1a */
+/* File: armv5te/OP_CONST_STRING.S */
+ /* const/string vAA, String@BBBB */
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- glue->methodClassDex
+ mov r9, rINST, lsr #8 @ r9<- AA
+ ldr r2, [r2, #offDvmDex_pResStrings] @ r2<- dvmDex->pResStrings
+ ldr r0, [r2, r1, lsl #2] @ r0<- pResStrings[BBBB]
+ cmp r0, #0 @ not yet resolved?
+ beq .LOP_CONST_STRING_resolve
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_STRING_JUMBO: /* 0x1b */
+/* File: armv5te/OP_CONST_STRING_JUMBO.S */
+ /* const/string vAA, String@BBBBBBBB */
+ FETCH(r0, 1) @ r0<- bbbb (low)
+ FETCH(r1, 2) @ r1<- BBBB (high)
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- glue->methodClassDex
+ mov r9, rINST, lsr #8 @ r9<- AA
+ ldr r2, [r2, #offDvmDex_pResStrings] @ r2<- dvmDex->pResStrings
+ orr r1, r0, r1, lsl #16 @ r1<- BBBBbbbb
+ ldr r0, [r2, r1, lsl #2] @ r0<- pResStrings[BBBB]
+ cmp r0, #0
+ beq .LOP_CONST_STRING_JUMBO_resolve
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_CLASS: /* 0x1c */
+/* File: armv5te/OP_CONST_CLASS.S */
+ /* const/class vAA, Class@BBBB */
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- glue->methodClassDex
+ mov r9, rINST, lsr #8 @ r9<- AA
+ ldr r2, [r2, #offDvmDex_pResClasses] @ r2<- dvmDex->pResClasses
+ ldr r0, [r2, r1, lsl #2] @ r0<- pResClasses[BBBB]
+ cmp r0, #0 @ not yet resolved?
+ beq .LOP_CONST_CLASS_resolve
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MONITOR_ENTER: /* 0x1d */
+/* File: armv5te/OP_MONITOR_ENTER.S */
+ /*
+ * Synchronize on an object.
+ */
+ /* monitor-enter vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ GET_VREG(r1, r2) @ r1<- vAA (object)
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ cmp r1, #0 @ null object?
+ EXPORT_PC() @ need for precise GC, MONITOR_TRACKING
+ beq common_errNullObject @ null object, throw an exception
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ bl dvmLockObject @ call(self, obj)
+#ifdef WITH_DEADLOCK_PREDICTION /* implies WITH_MONITOR_TRACKING */
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ ldr r1, [r0, #offThread_exception] @ check for exception
+ cmp r1, #0
+ bne common_exceptionThrown @ exception raised, bail out
+#endif
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MONITOR_EXIT: /* 0x1e */
+/* File: armv5te/OP_MONITOR_EXIT.S */
+ /*
+ * Unlock an object.
+ *
+ * Exceptions that occur when unlocking a monitor need to appear as
+ * if they happened at the following instruction. See the Dalvik
+ * instruction spec.
+ */
+ /* monitor-exit vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ EXPORT_PC() @ before fetch: export the PC
+ GET_VREG(r1, r2) @ r1<- vAA (object)
+ cmp r1, #0 @ null object?
+ beq 1f @ yes
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ bl dvmUnlockObject @ r0<- success for unlock(self, obj)
+ cmp r0, #0 @ failed?
+ FETCH_ADVANCE_INST(1) @ before throw: advance rPC, load rINST
+ beq common_exceptionThrown @ yes, exception is pending
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+1:
+ FETCH_ADVANCE_INST(1) @ advance before throw
+ b common_errNullObject
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CHECK_CAST: /* 0x1f */
+/* File: armv5te/OP_CHECK_CAST.S */
+ /*
+ * Check to see if a cast from one class to another is allowed.
+ */
+ /* check-cast vAA, class@BBBB */
+ mov r3, rINST, lsr #8 @ r3<- AA
+ FETCH(r2, 1) @ r2<- BBBB
+ GET_VREG(r9, r3) @ r9<- object
+ ldr r0, [rGLUE, #offGlue_methodClassDex] @ r0<- pDvmDex
+ cmp r9, #0 @ is object null?
+ ldr r0, [r0, #offDvmDex_pResClasses] @ r0<- pDvmDex->pResClasses
+ beq .LOP_CHECK_CAST_okay @ null obj, cast always succeeds
+ ldr r1, [r0, r2, lsl #2] @ r1<- resolved class
+ ldr r0, [r9, #offObject_clazz] @ r0<- obj->clazz
+ cmp r1, #0 @ have we resolved this before?
+ beq .LOP_CHECK_CAST_resolve @ not resolved, do it now
+.LOP_CHECK_CAST_resolved:
+ cmp r0, r1 @ same class (trivial success)?
+ bne .LOP_CHECK_CAST_fullcheck @ no, do full check
+.LOP_CHECK_CAST_okay:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INSTANCE_OF: /* 0x20 */
+/* File: armv5te/OP_INSTANCE_OF.S */
+ /*
+ * Check to see if an object reference is an instance of a class.
+ *
+ * Most common situation is a non-null object, being compared against
+ * an already-resolved class.
+ */
+ /* instance-of vA, vB, class@CCCC */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ GET_VREG(r0, r3) @ r0<- vB (object)
+ and r9, r9, #15 @ r9<- A
+ cmp r0, #0 @ is object null?
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- pDvmDex
+ beq .LOP_INSTANCE_OF_store @ null obj, not an instance, store r0
+ FETCH(r3, 1) @ r3<- CCCC
+ ldr r2, [r2, #offDvmDex_pResClasses] @ r2<- pDvmDex->pResClasses
+ ldr r1, [r2, r3, lsl #2] @ r1<- resolved class
+ ldr r0, [r0, #offObject_clazz] @ r0<- obj->clazz
+ cmp r1, #0 @ have we resolved this before?
+ beq .LOP_INSTANCE_OF_resolve @ not resolved, do it now
+.LOP_INSTANCE_OF_resolved: @ r0=obj->clazz, r1=resolved class
+ cmp r0, r1 @ same class (trivial success)?
+ beq .LOP_INSTANCE_OF_trivial @ yes, trivial finish
+ b .LOP_INSTANCE_OF_fullcheck @ no, do full check
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ARRAY_LENGTH: /* 0x21 */
+/* File: armv6t2/OP_ARRAY_LENGTH.S */
+ /*
+ * Return the length of an array.
+ */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r2, rINST, #8, #4 @ r2<- A
+ GET_VREG(r0, r1) @ r0<- vB (object ref)
+ cmp r0, #0 @ is object null?
+ beq common_errNullObject @ yup, fail
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ ldr r3, [r0, #offArrayObject_length] @ r3<- array length
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r3, r2) @ vB<- length
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEW_INSTANCE: /* 0x22 */
+/* File: armv5te/OP_NEW_INSTANCE.S */
+ /*
+ * Create a new instance of a class.
+ */
+ /* new-instance vAA, class@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResClasses] @ r3<- pDvmDex->pResClasses
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved class
+ EXPORT_PC() @ req'd for init, resolve, alloc
+ cmp r0, #0 @ already resolved?
+ beq .LOP_NEW_INSTANCE_resolve @ no, resolve it now
+.LOP_NEW_INSTANCE_resolved: @ r0=class
+ ldrb r1, [r0, #offClassObject_status] @ r1<- ClassStatus enum
+ cmp r1, #CLASS_INITIALIZED @ has class been initialized?
+ bne .LOP_NEW_INSTANCE_needinit @ no, init class now
+.LOP_NEW_INSTANCE_initialized: @ r0=class
+ mov r1, #ALLOC_DONT_TRACK @ flags for alloc call
+ bl dvmAllocObject @ r0<- new object
+ b .LOP_NEW_INSTANCE_finish @ continue
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEW_ARRAY: /* 0x23 */
+/* File: armv5te/OP_NEW_ARRAY.S */
+ /*
+ * Allocate an array of objects, specified with the array class
+ * and a count.
+ *
+ * The verifier guarantees that this is an array class, so we don't
+ * check for it here.
+ */
+ /* new-array vA, vB, class@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ FETCH(r2, 1) @ r2<- CCCC
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ GET_VREG(r1, r0) @ r1<- vB (array length)
+ ldr r3, [r3, #offDvmDex_pResClasses] @ r3<- pDvmDex->pResClasses
+ cmp r1, #0 @ check length
+ ldr r0, [r3, r2, lsl #2] @ r0<- resolved class
+ bmi common_errNegativeArraySize @ negative length, bail
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ req'd for resolve, alloc
+ bne .LOP_NEW_ARRAY_finish @ resolved, continue
+ b .LOP_NEW_ARRAY_resolve @ do resolve now
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FILLED_NEW_ARRAY: /* 0x24 */
+/* File: armv5te/OP_FILLED_NEW_ARRAY.S */
+ /*
+ * Create a new array with elements filled from registers.
+ *
+ * for: filled-new-array, filled-new-array/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResClasses] @ r3<- pDvmDex->pResClasses
+ EXPORT_PC() @ need for resolve and alloc
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved class
+ mov r10, rINST, lsr #8 @ r10<- AA or BA
+ cmp r0, #0 @ already resolved?
+ bne .LOP_FILLED_NEW_ARRAY_continue @ yes, continue on
+8: ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ mov r2, #0 @ r2<- false
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- call(clazz, ref)
+ cmp r0, #0 @ got null?
+ beq common_exceptionThrown @ yes, handle exception
+ b .LOP_FILLED_NEW_ARRAY_continue
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FILLED_NEW_ARRAY_RANGE: /* 0x25 */
+/* File: armv5te/OP_FILLED_NEW_ARRAY_RANGE.S */
+/* File: armv5te/OP_FILLED_NEW_ARRAY.S */
+ /*
+ * Create a new array with elements filled from registers.
+ *
+ * for: filled-new-array, filled-new-array/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResClasses] @ r3<- pDvmDex->pResClasses
+ EXPORT_PC() @ need for resolve and alloc
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved class
+ mov r10, rINST, lsr #8 @ r10<- AA or BA
+ cmp r0, #0 @ already resolved?
+ bne .LOP_FILLED_NEW_ARRAY_RANGE_continue @ yes, continue on
+8: ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ mov r2, #0 @ r2<- false
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- call(clazz, ref)
+ cmp r0, #0 @ got null?
+ beq common_exceptionThrown @ yes, handle exception
+ b .LOP_FILLED_NEW_ARRAY_RANGE_continue
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FILL_ARRAY_DATA: /* 0x26 */
+/* File: armv5te/OP_FILL_ARRAY_DATA.S */
+ /* fill-array-data vAA, +BBBBBBBB */
+ FETCH(r0, 1) @ r0<- bbbb (lo)
+ FETCH(r1, 2) @ r1<- BBBB (hi)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ orr r1, r0, r1, lsl #16 @ r1<- BBBBbbbb
+ GET_VREG(r0, r3) @ r0<- vAA (array object)
+ add r1, rPC, r1, lsl #1 @ r1<- PC + BBBBbbbb*2 (array data off.)
+ EXPORT_PC();
+ bl dvmInterpHandleFillArrayData@ fill the array with predefined data
+ cmp r0, #0 @ 0 means an exception is thrown
+ beq common_exceptionThrown @ has exception
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_THROW: /* 0x27 */
+/* File: armv5te/OP_THROW.S */
+ /*
+ * Throw an exception object in the current thread.
+ */
+ /* throw vAA */
+ mov r2, rINST, lsr #8 @ r2<- AA
+ GET_VREG(r1, r2) @ r1<- vAA (exception object)
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ EXPORT_PC() @ exception handler can throw
+ cmp r1, #0 @ null object?
+ beq common_errNullObject @ yes, throw an NPE instead
+ @ bypass dvmSetException, just store it
+ str r1, [r0, #offThread_exception] @ thread->exception<- obj
+ b common_exceptionThrown
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_GOTO: /* 0x28 */
+/* File: armv5te/OP_GOTO.S */
+ /*
+ * Unconditional branch, 8-bit offset.
+ *
+ * The branch distance is a signed code-unit offset, which we need to
+ * double to get a byte offset.
+ */
+ /* goto +AA */
+ mov r0, rINST, lsl #16 @ r0<- AAxx0000
+ movs r9, r0, asr #24 @ r9<- ssssssAA (sign-extended)
+ mov r9, r9, lsl #1 @ r9<- byte offset
+ bmi common_backwardBranch @ backward branch, do periodic checks
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_GOTO_16: /* 0x29 */
+/* File: armv5te/OP_GOTO_16.S */
+ /*
+ * Unconditional branch, 16-bit offset.
+ *
+ * The branch distance is a signed code-unit offset, which we need to
+ * double to get a byte offset.
+ */
+ /* goto/16 +AAAA */
+ FETCH_S(r0, 1) @ r0<- ssssAAAA (sign-extended)
+ movs r9, r0, asl #1 @ r9<- byte offset, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_GOTO_32: /* 0x2a */
+/* File: armv5te/OP_GOTO_32.S */
+ /*
+ * Unconditional branch, 32-bit offset.
+ *
+ * The branch distance is a signed code-unit offset, which we need to
+ * double to get a byte offset.
+ *
+ * Unlike most opcodes, this one is allowed to branch to itself, so
+ * our "backward branch" test must be "<=0" instead of "<0". The ORRS
+ * instruction doesn't affect the V flag, so we need to clear it
+ * explicitly.
+ */
+ /* goto/32 +AAAAAAAA */
+ FETCH(r0, 1) @ r0<- aaaa (lo)
+ FETCH(r1, 2) @ r1<- AAAA (hi)
+ cmp ip, ip @ (clear V flag during stall)
+ orrs r0, r0, r1, lsl #16 @ r0<- AAAAaaaa, check sign
+ mov r9, r0, asl #1 @ r9<- byte offset
+ ble common_backwardBranch @ backward branch, do periodic checks
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_PACKED_SWITCH: /* 0x2b */
+/* File: armv5te/OP_PACKED_SWITCH.S */
+ /*
+ * Handle a packed-switch or sparse-switch instruction. In both cases
+ * we decode it and hand it off to a helper function.
+ *
+ * We don't really expect backward branches in a switch statement, but
+ * they're perfectly legal, so we check for them here.
+ *
+ * for: packed-switch, sparse-switch
+ */
+ /* op vAA, +BBBB */
+ FETCH(r0, 1) @ r0<- bbbb (lo)
+ FETCH(r1, 2) @ r1<- BBBB (hi)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ orr r0, r0, r1, lsl #16 @ r0<- BBBBbbbb
+ GET_VREG(r1, r3) @ r1<- vAA
+ add r0, rPC, r0, lsl #1 @ r0<- PC + BBBBbbbb*2
+ bl dvmInterpHandlePackedSwitch @ r0<- code-unit branch offset
+ movs r9, r0, asl #1 @ r9<- branch byte offset, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+ beq common_backwardBranch @ (want to use BLE but V is unknown)
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPARSE_SWITCH: /* 0x2c */
+/* File: armv5te/OP_SPARSE_SWITCH.S */
+/* File: armv5te/OP_PACKED_SWITCH.S */
+ /*
+ * Handle a packed-switch or sparse-switch instruction. In both cases
+ * we decode it and hand it off to a helper function.
+ *
+ * We don't really expect backward branches in a switch statement, but
+ * they're perfectly legal, so we check for them here.
+ *
+ * for: packed-switch, sparse-switch
+ */
+ /* op vAA, +BBBB */
+ FETCH(r0, 1) @ r0<- bbbb (lo)
+ FETCH(r1, 2) @ r1<- BBBB (hi)
+ mov r3, rINST, lsr #8 @ r3<- AA
+ orr r0, r0, r1, lsl #16 @ r0<- BBBBbbbb
+ GET_VREG(r1, r3) @ r1<- vAA
+ add r0, rPC, r0, lsl #1 @ r0<- PC + BBBBbbbb*2
+ bl dvmInterpHandleSparseSwitch @ r0<- code-unit branch offset
+ movs r9, r0, asl #1 @ r9<- branch byte offset, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+ beq common_backwardBranch @ (want to use BLE but V is unknown)
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CMPL_FLOAT: /* 0x2d */
+/* File: arm-vfp/OP_CMPL_FLOAT.S */
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * int compare(x, y) {
+ * if (x == y) {
+ * return 0;
+ * } else if (x > y) {
+ * return 1;
+ * } else if (x < y) {
+ * return -1;
+ * } else {
+ * return -1;
+ * }
+ * }
+ */
+ /* op vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ flds s0, [r2] @ s0<- vBB
+ flds s1, [r3] @ s1<- vCC
+ fcmpes s0, s1 @ compare (vBB, vCC)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ mvn r0, #0 @ r0<- -1 (default)
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fmstat @ export status flags
+ movgt r0, #1 @ (greater than) r1<- 1
+ moveq r0, #0 @ (equal) r1<- 0
+ b .LOP_CMPL_FLOAT_finish @ argh
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CMPG_FLOAT: /* 0x2e */
+/* File: arm-vfp/OP_CMPG_FLOAT.S */
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * int compare(x, y) {
+ * if (x == y) {
+ * return 0;
+ * } else if (x < y) {
+ * return -1;
+ * } else if (x > y) {
+ * return 1;
+ * } else {
+ * return 1;
+ * }
+ * }
+ */
+ /* op vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ flds s0, [r2] @ s0<- vBB
+ flds s1, [r3] @ s1<- vCC
+ fcmpes s0, s1 @ compare (vBB, vCC)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ mov r0, #1 @ r0<- 1 (default)
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fmstat @ export status flags
+ mvnmi r0, #0 @ (less than) r1<- -1
+ moveq r0, #0 @ (equal) r1<- 0
+ b .LOP_CMPG_FLOAT_finish @ argh
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CMPL_DOUBLE: /* 0x2f */
+/* File: arm-vfp/OP_CMPL_DOUBLE.S */
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * int compare(x, y) {
+ * if (x == y) {
+ * return 0;
+ * } else if (x > y) {
+ * return 1;
+ * } else if (x < y) {
+ * return -1;
+ * } else {
+ * return -1;
+ * }
+ * }
+ */
+ /* op vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ fldd d0, [r2] @ d0<- vBB
+ fldd d1, [r3] @ d1<- vCC
+ fcmped d0, d1 @ compare (vBB, vCC)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ mvn r0, #0 @ r0<- -1 (default)
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fmstat @ export status flags
+ movgt r0, #1 @ (greater than) r1<- 1
+ moveq r0, #0 @ (equal) r1<- 0
+ b .LOP_CMPL_DOUBLE_finish @ argh
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CMPG_DOUBLE: /* 0x30 */
+/* File: arm-vfp/OP_CMPG_DOUBLE.S */
+ /*
+ * Compare two floating-point values. Puts 0, 1, or -1 into the
+ * destination register based on the results of the comparison.
+ *
+ * int compare(x, y) {
+ * if (x == y) {
+ * return 0;
+ * } else if (x < y) {
+ * return -1;
+ * } else if (x > y) {
+ * return 1;
+ * } else {
+ * return 1;
+ * }
+ * }
+ */
+ /* op vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ fldd d0, [r2] @ d0<- vBB
+ fldd d1, [r3] @ d1<- vCC
+ fcmped d0, d1 @ compare (vBB, vCC)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ mov r0, #1 @ r0<- 1 (default)
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fmstat @ export status flags
+ mvnmi r0, #0 @ (less than) r1<- -1
+ moveq r0, #0 @ (equal) r1<- 0
+ b .LOP_CMPG_DOUBLE_finish @ argh
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CMP_LONG: /* 0x31 */
+/* File: armv5te/OP_CMP_LONG.S */
+ /*
+ * Compare two 64-bit values. Puts 0, 1, or -1 into the destination
+ * register based on the results of the comparison.
+ *
+ * We load the full values with LDM, but in practice many values could
+ * be resolved by only looking at the high word. This could be made
+ * faster or slower by splitting the LDM into a pair of LDRs.
+ *
+ * If we just wanted to set condition flags, we could do this:
+ * subs ip, r0, r2
+ * sbcs ip, r1, r3
+ * subeqs ip, r0, r2
+ * Leaving { <0, 0, >0 } in ip. However, we have to set it to a specific
+ * integer value, which we can do with 2 conditional mov/mvn instructions
+ * (set 1, set -1; if they're equal we already have 0 in ip), giving
+ * us a constant 5-cycle path plus a branch at the end to the
+ * instruction epilogue code. The multi-compare approach below needs
+ * 2 or 3 cycles + branch if the high word doesn't match, 6 + branch
+ * in the worst case (the 64-bit values are equal).
+ */
+ /* cmp-long vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ cmp r1, r3 @ compare (vBB+1, vCC+1)
+ blt .LOP_CMP_LONG_less @ signed compare on high part
+ bgt .LOP_CMP_LONG_greater
+ subs r1, r0, r2 @ r1<- r0 - r2
+ bhi .LOP_CMP_LONG_greater @ unsigned compare on low part
+ bne .LOP_CMP_LONG_less
+ b .LOP_CMP_LONG_finish @ equal; r1 already holds 0
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_EQ: /* 0x32 */
+/* File: armv6t2/OP_IF_EQ.S */
+/* File: armv6t2/bincmp.S */
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r0, rINST, #8, #4 @ r0<- A
+ GET_VREG(r3, r1) @ r3<- vB
+ GET_VREG(r2, r0) @ r2<- vA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, r3 @ compare (vA, vB)
+ bne 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ b common_testUpdateProfile
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_NE: /* 0x33 */
+/* File: armv6t2/OP_IF_NE.S */
+/* File: armv6t2/bincmp.S */
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r0, rINST, #8, #4 @ r0<- A
+ GET_VREG(r3, r1) @ r3<- vB
+ GET_VREG(r2, r0) @ r2<- vA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, r3 @ compare (vA, vB)
+ beq 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ b common_testUpdateProfile
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_LT: /* 0x34 */
+/* File: armv6t2/OP_IF_LT.S */
+/* File: armv6t2/bincmp.S */
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r0, rINST, #8, #4 @ r0<- A
+ GET_VREG(r3, r1) @ r3<- vB
+ GET_VREG(r2, r0) @ r2<- vA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, r3 @ compare (vA, vB)
+ bge 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ b common_testUpdateProfile
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_GE: /* 0x35 */
+/* File: armv6t2/OP_IF_GE.S */
+/* File: armv6t2/bincmp.S */
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r0, rINST, #8, #4 @ r0<- A
+ GET_VREG(r3, r1) @ r3<- vB
+ GET_VREG(r2, r0) @ r2<- vA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, r3 @ compare (vA, vB)
+ blt 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ b common_testUpdateProfile
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_GT: /* 0x36 */
+/* File: armv6t2/OP_IF_GT.S */
+/* File: armv6t2/bincmp.S */
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r0, rINST, #8, #4 @ r0<- A
+ GET_VREG(r3, r1) @ r3<- vB
+ GET_VREG(r2, r0) @ r2<- vA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, r3 @ compare (vA, vB)
+ ble 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ b common_testUpdateProfile
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_LE: /* 0x37 */
+/* File: armv6t2/OP_IF_LE.S */
+/* File: armv6t2/bincmp.S */
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r0, rINST, #8, #4 @ r0<- A
+ GET_VREG(r3, r1) @ r3<- vB
+ GET_VREG(r2, r0) @ r2<- vA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, r3 @ compare (vA, vB)
+ bgt 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ yes, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ b common_testUpdateProfile
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_EQZ: /* 0x38 */
+/* File: armv5te/OP_IF_EQZ.S */
+/* File: armv5te/zcmp.S */
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ mov r0, rINST, lsr #8 @ r0<- AA
+ GET_VREG(r2, r0) @ r2<- vAA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, #0 @ compare (vA, 0)
+ bne 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_NEZ: /* 0x39 */
+/* File: armv5te/OP_IF_NEZ.S */
+/* File: armv5te/zcmp.S */
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ mov r0, rINST, lsr #8 @ r0<- AA
+ GET_VREG(r2, r0) @ r2<- vAA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, #0 @ compare (vA, 0)
+ beq 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_LTZ: /* 0x3a */
+/* File: armv5te/OP_IF_LTZ.S */
+/* File: armv5te/zcmp.S */
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ mov r0, rINST, lsr #8 @ r0<- AA
+ GET_VREG(r2, r0) @ r2<- vAA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, #0 @ compare (vA, 0)
+ bge 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_GEZ: /* 0x3b */
+/* File: armv5te/OP_IF_GEZ.S */
+/* File: armv5te/zcmp.S */
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ mov r0, rINST, lsr #8 @ r0<- AA
+ GET_VREG(r2, r0) @ r2<- vAA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, #0 @ compare (vA, 0)
+ blt 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_GTZ: /* 0x3c */
+/* File: armv5te/OP_IF_GTZ.S */
+/* File: armv5te/zcmp.S */
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ mov r0, rINST, lsr #8 @ r0<- AA
+ GET_VREG(r2, r0) @ r2<- vAA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, #0 @ compare (vA, 0)
+ ble 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_LEZ: /* 0x3d */
+/* File: armv5te/OP_IF_LEZ.S */
+/* File: armv5te/zcmp.S */
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ mov r0, rINST, lsr #8 @ r0<- AA
+ GET_VREG(r2, r0) @ r2<- vAA
+ mov r9, #4 @ r0<- BYTE branch dist for not-taken
+ cmp r2, #0 @ compare (vA, 0)
+ bgt 1f @ branch to 1 if comparison failed
+ FETCH_S(r9, 1) @ r9<- branch offset, in code units
+ movs r9, r9, asl #1 @ convert to bytes, check sign
+ bmi common_backwardBranch @ backward branch, do periodic checks
+1:
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_3E: /* 0x3e */
+/* File: armv5te/OP_UNUSED_3E.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_3F: /* 0x3f */
+/* File: armv5te/OP_UNUSED_3F.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_40: /* 0x40 */
+/* File: armv5te/OP_UNUSED_40.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_41: /* 0x41 */
+/* File: armv5te/OP_UNUSED_41.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_42: /* 0x42 */
+/* File: armv5te/OP_UNUSED_42.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_43: /* 0x43 */
+/* File: armv5te/OP_UNUSED_43.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET: /* 0x44 */
+/* File: armv5te/OP_AGET.S */
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #2 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldr r2, [r0, #offArrayObject_contents] @ r2<- vBB[vCC]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r9) @ vAA<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_WIDE: /* 0x45 */
+/* File: armv5te/OP_AGET_WIDE.S */
+ /*
+ * Array get, 64 bits. vAA <- vBB[vCC].
+ *
+ * Arrays of long/double are 64-bit aligned, so it's okay to use LDRD.
+ */
+ /* aget-wide vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #3 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcc .LOP_AGET_WIDE_finish @ okay, continue below
+ b common_errArrayIndex @ index >= length, bail
+ @ May want to swap the order of these two branches depending on how the
+ @ branch prediction (if any) handles conditional forward branches vs.
+ @ unconditional forward branches.
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_OBJECT: /* 0x46 */
+/* File: armv5te/OP_AGET_OBJECT.S */
+/* File: armv5te/OP_AGET.S */
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #2 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldr r2, [r0, #offArrayObject_contents] @ r2<- vBB[vCC]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r9) @ vAA<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_BOOLEAN: /* 0x47 */
+/* File: armv5te/OP_AGET_BOOLEAN.S */
+/* File: armv5te/OP_AGET.S */
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #0 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldrb r2, [r0, #offArrayObject_contents] @ r2<- vBB[vCC]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r9) @ vAA<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_BYTE: /* 0x48 */
+/* File: armv5te/OP_AGET_BYTE.S */
+/* File: armv5te/OP_AGET.S */
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #0 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldrsb r2, [r0, #offArrayObject_contents] @ r2<- vBB[vCC]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r9) @ vAA<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_CHAR: /* 0x49 */
+/* File: armv5te/OP_AGET_CHAR.S */
+/* File: armv5te/OP_AGET.S */
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #1 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldrh r2, [r0, #offArrayObject_contents] @ r2<- vBB[vCC]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r9) @ vAA<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_SHORT: /* 0x4a */
+/* File: armv5te/OP_AGET_SHORT.S */
+/* File: armv5te/OP_AGET.S */
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #1 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldrsh r2, [r0, #offArrayObject_contents] @ r2<- vBB[vCC]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r2, r9) @ vAA<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT: /* 0x4b */
+/* File: armv5te/OP_APUT.S */
+ /*
+ * Array put, 32 bits or less. vBB[vCC] <- vAA.
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #2 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r2, r9) @ r2<- vAA
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ str r2, [r0, #offArrayObject_contents] @ vBB[vCC]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_WIDE: /* 0x4c */
+/* File: armv5te/OP_APUT_WIDE.S */
+ /*
+ * Array put, 64 bits. vBB[vCC] <- vAA.
+ *
+ * Arrays of long/double are 64-bit aligned, so it's okay to use STRD.
+ */
+ /* aput-wide vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #3 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ bcc .LOP_APUT_WIDE_finish @ okay, continue below
+ b common_errArrayIndex @ index >= length, bail
+ @ May want to swap the order of these two branches depending on how the
+ @ branch prediction (if any) handles conditional forward branches vs.
+ @ unconditional forward branches.
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_OBJECT: /* 0x4d */
+/* File: armv5te/OP_APUT_OBJECT.S */
+ /*
+ * Store an object into an array. vBB[vCC] <- vAA.
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ */
+ /* op vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ GET_VREG(rINST, r2) @ rINST<- vBB (array object)
+ GET_VREG(r0, r3) @ r0<- vCC (requested index)
+ cmp rINST, #0 @ null array object?
+ GET_VREG(r9, r9) @ r9<- vAA
+ beq common_errNullObject @ yes, bail
+ ldr r3, [rINST, #offArrayObject_length] @ r3<- arrayObj->length
+ add r10, rINST, r0, lsl #2 @ r10<- arrayObj + index*width
+ cmp r0, r3 @ compare unsigned index, length
+ bcc .LOP_APUT_OBJECT_finish @ we're okay, continue on
+ b common_errArrayIndex @ index >= length, bail
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_BOOLEAN: /* 0x4e */
+/* File: armv5te/OP_APUT_BOOLEAN.S */
+/* File: armv5te/OP_APUT.S */
+ /*
+ * Array put, 32 bits or less. vBB[vCC] <- vAA.
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #0 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r2, r9) @ r2<- vAA
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ strb r2, [r0, #offArrayObject_contents] @ vBB[vCC]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_BYTE: /* 0x4f */
+/* File: armv5te/OP_APUT_BYTE.S */
+/* File: armv5te/OP_APUT.S */
+ /*
+ * Array put, 32 bits or less. vBB[vCC] <- vAA.
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #0 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r2, r9) @ r2<- vAA
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ strb r2, [r0, #offArrayObject_contents] @ vBB[vCC]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_CHAR: /* 0x50 */
+/* File: armv5te/OP_APUT_CHAR.S */
+/* File: armv5te/OP_APUT.S */
+ /*
+ * Array put, 32 bits or less. vBB[vCC] <- vAA.
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #1 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r2, r9) @ r2<- vAA
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ strh r2, [r0, #offArrayObject_contents] @ vBB[vCC]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_SHORT: /* 0x51 */
+/* File: armv5te/OP_APUT_SHORT.S */
+/* File: armv5te/OP_APUT.S */
+ /*
+ * Array put, 32 bits or less. vBB[vCC] <- vAA.
+ *
+ * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17
+ * instructions. We use a pair of FETCH_Bs instead.
+ *
+ * for: aput, aput-boolean, aput-byte, aput-char, aput-short
+ */
+ /* op vAA, vBB, vCC */
+ FETCH_B(r2, 1, 0) @ r2<- BB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ FETCH_B(r3, 1, 1) @ r3<- CC
+ GET_VREG(r0, r2) @ r0<- vBB (array object)
+ GET_VREG(r1, r3) @ r1<- vCC (requested index)
+ cmp r0, #0 @ null array object?
+ beq common_errNullObject @ yes, bail
+ ldr r3, [r0, #offArrayObject_length] @ r3<- arrayObj->length
+ add r0, r0, r1, lsl #1 @ r0<- arrayObj + index*width
+ cmp r1, r3 @ compare unsigned index, length
+ bcs common_errArrayIndex @ index >= length, bail
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r2, r9) @ r2<- vAA
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ strh r2, [r0, #offArrayObject_contents] @ vBB[vCC]<- r2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET: /* 0x52 */
+/* File: armv6t2/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_finish
+ b common_exceptionThrown
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_WIDE: /* 0x53 */
+/* File: armv6t2/OP_IGET_WIDE.S */
+ /*
+ * Wide 32-bit instance field get.
+ */
+ /* iget-wide vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_WIDE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_WIDE_finish
+ b common_exceptionThrown
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_OBJECT: /* 0x54 */
+/* File: armv5te/OP_IGET_OBJECT.S */
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_OBJECT_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_OBJECT_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_BOOLEAN: /* 0x55 */
+/* File: armv5te/OP_IGET_BOOLEAN.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrb", "sqnum":"1" }
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_BOOLEAN_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_BOOLEAN_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_BYTE: /* 0x56 */
+/* File: armv5te/OP_IGET_BYTE.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrsb", "sqnum":"2" }
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_BYTE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_BYTE_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_CHAR: /* 0x57 */
+/* File: armv5te/OP_IGET_CHAR.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrh", "sqnum":"3" }
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_CHAR_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_CHAR_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_SHORT: /* 0x58 */
+/* File: armv5te/OP_IGET_SHORT.S */
+@include "armv5te/OP_IGET.S" { "load":"ldrsh", "sqnum":"4" }
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_SHORT_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_SHORT_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT: /* 0x59 */
+/* File: armv6t2/OP_IPUT.S */
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_finish @ yes, finish up
+ b common_exceptionThrown
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_WIDE: /* 0x5a */
+/* File: armv6t2/OP_IPUT_WIDE.S */
+ /* iput-wide vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_WIDE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_WIDE_finish @ yes, finish up
+ b common_exceptionThrown
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_OBJECT: /* 0x5b */
+/* File: armv5te/OP_IPUT_OBJECT.S */
+ /*
+ * 32-bit instance field put.
+ *
+ * for: iput-object, iput-object-volatile
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_OBJECT_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_OBJECT_finish @ yes, finish up
+ b common_exceptionThrown
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_BOOLEAN: /* 0x5c */
+/* File: armv5te/OP_IPUT_BOOLEAN.S */
+@include "armv5te/OP_IPUT.S" { "store":"strb", "sqnum":"1" }
+/* File: armv5te/OP_IPUT.S */
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_BOOLEAN_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_BOOLEAN_finish @ yes, finish up
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_BYTE: /* 0x5d */
+/* File: armv5te/OP_IPUT_BYTE.S */
+@include "armv5te/OP_IPUT.S" { "store":"strb", "sqnum":"2" }
+/* File: armv5te/OP_IPUT.S */
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_BYTE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_BYTE_finish @ yes, finish up
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_CHAR: /* 0x5e */
+/* File: armv5te/OP_IPUT_CHAR.S */
+@include "armv5te/OP_IPUT.S" { "store":"strh", "sqnum":"3" }
+/* File: armv5te/OP_IPUT.S */
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_CHAR_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_CHAR_finish @ yes, finish up
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_SHORT: /* 0x5f */
+/* File: armv5te/OP_IPUT_SHORT.S */
+@include "armv5te/OP_IPUT.S" { "store":"strh", "sqnum":"4" }
+/* File: armv5te/OP_IPUT.S */
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_SHORT_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_SHORT_finish @ yes, finish up
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET: /* 0x60 */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_resolve @ yes, do resolve
+.LOP_SGET_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_WIDE: /* 0x61 */
+/* File: armv5te/OP_SGET_WIDE.S */
+ /*
+ * 64-bit SGET handler.
+ */
+ /* sget-wide vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_WIDE_resolve @ yes, do resolve
+.LOP_SGET_WIDE_finish:
+ mov r9, rINST, lsr #8 @ r9<- AA
+ .if 0
+ add r0, r0, #offStaticField_value @ r0<- pointer to data
+ bl dvmQuasiAtomicRead64 @ r0/r1<- contents of field
+ .else
+ ldrd r0, [r0, #offStaticField_value] @ r0/r1<- field value (aligned)
+ .endif
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_OBJECT: /* 0x62 */
+/* File: armv5te/OP_SGET_OBJECT.S */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_OBJECT_resolve @ yes, do resolve
+.LOP_SGET_OBJECT_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_BOOLEAN: /* 0x63 */
+/* File: armv5te/OP_SGET_BOOLEAN.S */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_BOOLEAN_resolve @ yes, do resolve
+.LOP_SGET_BOOLEAN_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_BYTE: /* 0x64 */
+/* File: armv5te/OP_SGET_BYTE.S */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_BYTE_resolve @ yes, do resolve
+.LOP_SGET_BYTE_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_CHAR: /* 0x65 */
+/* File: armv5te/OP_SGET_CHAR.S */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_CHAR_resolve @ yes, do resolve
+.LOP_SGET_CHAR_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_SHORT: /* 0x66 */
+/* File: armv5te/OP_SGET_SHORT.S */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_SHORT_resolve @ yes, do resolve
+.LOP_SGET_SHORT_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT: /* 0x67 */
+/* File: armv5te/OP_SPUT.S */
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SPUT_resolve @ yes, do resolve
+.LOP_SPUT_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_WIDE: /* 0x68 */
+/* File: armv5te/OP_SPUT_WIDE.S */
+ /*
+ * 64-bit SPUT handler.
+ */
+ /* sput-wide vAA, field@BBBB */
+ ldr r0, [rGLUE, #offGlue_methodClassDex] @ r0<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r0, [r0, #offDvmDex_pResFields] @ r0<- dvmDex->pResFields
+ mov r9, rINST, lsr #8 @ r9<- AA
+ ldr r2, [r0, r1, lsl #2] @ r2<- resolved StaticField ptr
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ cmp r2, #0 @ is resolved entry null?
+ beq .LOP_SPUT_WIDE_resolve @ yes, do resolve
+.LOP_SPUT_WIDE_finish: @ field ptr in r2, AA in r9
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ GET_INST_OPCODE(r10) @ extract opcode from rINST
+ .if 0
+ add r2, r2, #offStaticField_value @ r2<- pointer to data
+ bl dvmQuasiAtomicSwap64 @ stores r0/r1 into addr r2
+ .else
+ strd r0, [r2, #offStaticField_value] @ field<- vAA/vAA+1
+ .endif
+ GOTO_OPCODE(r10) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_OBJECT: /* 0x69 */
+/* File: armv5te/OP_SPUT_OBJECT.S */
+ /*
+ * 32-bit SPUT handler for objects
+ *
+ * for: sput-object, sput-object-volatile
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_SPUT_OBJECT_finish @ no, continue
+ ldr r9, [rGLUE, #offGlue_method] @ r9<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r9, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_OBJECT_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_BOOLEAN: /* 0x6a */
+/* File: armv5te/OP_SPUT_BOOLEAN.S */
+/* File: armv5te/OP_SPUT.S */
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SPUT_BOOLEAN_resolve @ yes, do resolve
+.LOP_SPUT_BOOLEAN_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_BYTE: /* 0x6b */
+/* File: armv5te/OP_SPUT_BYTE.S */
+/* File: armv5te/OP_SPUT.S */
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SPUT_BYTE_resolve @ yes, do resolve
+.LOP_SPUT_BYTE_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_CHAR: /* 0x6c */
+/* File: armv5te/OP_SPUT_CHAR.S */
+/* File: armv5te/OP_SPUT.S */
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SPUT_CHAR_resolve @ yes, do resolve
+.LOP_SPUT_CHAR_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_SHORT: /* 0x6d */
+/* File: armv5te/OP_SPUT_SHORT.S */
+/* File: armv5te/OP_SPUT.S */
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SPUT_SHORT_resolve @ yes, do resolve
+.LOP_SPUT_SHORT_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_VIRTUAL: /* 0x6e */
+/* File: armv5te/OP_INVOKE_VIRTUAL.S */
+ /*
+ * Handle a virtual method call.
+ *
+ * for: invoke-virtual, invoke-virtual/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved baseMethod
+ .if (!0)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ must export for invoke
+ bne .LOP_INVOKE_VIRTUAL_continue @ yes, continue on
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ mov r2, #METHOD_VIRTUAL @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ bne .LOP_INVOKE_VIRTUAL_continue @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_SUPER: /* 0x6f */
+/* File: armv5te/OP_INVOKE_SUPER.S */
+ /*
+ * Handle a "super" method call.
+ *
+ * for: invoke-super, invoke-super/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ .if (!0)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ GET_VREG(r2, r10) @ r2<- "this" ptr
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved baseMethod
+ cmp r2, #0 @ null "this"?
+ ldr r9, [rGLUE, #offGlue_method] @ r9<- current method
+ beq common_errNullObject @ null "this", throw exception
+ cmp r0, #0 @ already resolved?
+ ldr r9, [r9, #offMethod_clazz] @ r9<- method->clazz
+ EXPORT_PC() @ must export for invoke
+ bne .LOP_INVOKE_SUPER_continue @ resolved, continue on
+ b .LOP_INVOKE_SUPER_resolve @ do resolve now
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_DIRECT: /* 0x70 */
+/* File: armv5te/OP_INVOKE_DIRECT.S */
+ /*
+ * Handle a direct method call.
+ *
+ * (We could defer the "is 'this' pointer null" test to the common
+ * method invocation code, and use a flag to indicate that static
+ * calls don't count. If we do this as part of copying the arguments
+ * out we could avoiding loading the first arg twice.)
+ *
+ * for: invoke-direct, invoke-direct/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved methodToCall
+ .if (!0)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ must export for invoke
+ GET_VREG(r2, r10) @ r2<- "this" ptr
+ beq .LOP_INVOKE_DIRECT_resolve @ not resolved, do it now
+.LOP_INVOKE_DIRECT_finish:
+ cmp r2, #0 @ null "this" ref?
+ bne common_invokeMethodNoRange @ no, continue on
+ b common_errNullObject @ yes, throw exception
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_STATIC: /* 0x71 */
+/* File: armv5te/OP_INVOKE_STATIC.S */
+ /*
+ * Handle a static method call.
+ *
+ * for: invoke-static, invoke-static/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved methodToCall
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ must export for invoke
+ bne common_invokeMethodNoRange @ yes, continue on
+0: ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ mov r2, #METHOD_STATIC @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ bne common_invokeMethodNoRange @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_INTERFACE: /* 0x72 */
+/* File: armv5te/OP_INVOKE_INTERFACE.S */
+ /*
+ * Handle an interface method call.
+ *
+ * for: invoke-interface, invoke-interface/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r2, 2) @ r2<- FEDC or CCCC
+ FETCH(r1, 1) @ r1<- BBBB
+ .if (!0)
+ and r2, r2, #15 @ r2<- C (or stays CCCC)
+ .endif
+ EXPORT_PC() @ must export for invoke
+ GET_VREG(r0, r2) @ r0<- first arg ("this")
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- methodClassDex
+ cmp r0, #0 @ null obj?
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- method
+ beq common_errNullObject @ yes, fail
+ ldr r0, [r0, #offObject_clazz] @ r0<- thisPtr->clazz
+ bl dvmFindInterfaceMethodInCache @ r0<- call(class, ref, method, dex)
+ cmp r0, #0 @ failed?
+ beq common_exceptionThrown @ yes, handle exception
+ b common_invokeMethodNoRange @ jump to common handler
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_73: /* 0x73 */
+/* File: armv5te/OP_UNUSED_73.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_VIRTUAL_RANGE: /* 0x74 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_RANGE.S */
+/* File: armv5te/OP_INVOKE_VIRTUAL.S */
+ /*
+ * Handle a virtual method call.
+ *
+ * for: invoke-virtual, invoke-virtual/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved baseMethod
+ .if (!1)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ must export for invoke
+ bne .LOP_INVOKE_VIRTUAL_RANGE_continue @ yes, continue on
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ mov r2, #METHOD_VIRTUAL @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ bne .LOP_INVOKE_VIRTUAL_RANGE_continue @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_SUPER_RANGE: /* 0x75 */
+/* File: armv5te/OP_INVOKE_SUPER_RANGE.S */
+/* File: armv5te/OP_INVOKE_SUPER.S */
+ /*
+ * Handle a "super" method call.
+ *
+ * for: invoke-super, invoke-super/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ .if (!1)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ GET_VREG(r2, r10) @ r2<- "this" ptr
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved baseMethod
+ cmp r2, #0 @ null "this"?
+ ldr r9, [rGLUE, #offGlue_method] @ r9<- current method
+ beq common_errNullObject @ null "this", throw exception
+ cmp r0, #0 @ already resolved?
+ ldr r9, [r9, #offMethod_clazz] @ r9<- method->clazz
+ EXPORT_PC() @ must export for invoke
+ bne .LOP_INVOKE_SUPER_RANGE_continue @ resolved, continue on
+ b .LOP_INVOKE_SUPER_RANGE_resolve @ do resolve now
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_DIRECT_RANGE: /* 0x76 */
+/* File: armv5te/OP_INVOKE_DIRECT_RANGE.S */
+/* File: armv5te/OP_INVOKE_DIRECT.S */
+ /*
+ * Handle a direct method call.
+ *
+ * (We could defer the "is 'this' pointer null" test to the common
+ * method invocation code, and use a flag to indicate that static
+ * calls don't count. If we do this as part of copying the arguments
+ * out we could avoiding loading the first arg twice.)
+ *
+ * for: invoke-direct, invoke-direct/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved methodToCall
+ .if (!1)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ must export for invoke
+ GET_VREG(r2, r10) @ r2<- "this" ptr
+ beq .LOP_INVOKE_DIRECT_RANGE_resolve @ not resolved, do it now
+.LOP_INVOKE_DIRECT_RANGE_finish:
+ cmp r2, #0 @ null "this" ref?
+ bne common_invokeMethodRange @ no, continue on
+ b common_errNullObject @ yes, throw exception
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_STATIC_RANGE: /* 0x77 */
+/* File: armv5te/OP_INVOKE_STATIC_RANGE.S */
+/* File: armv5te/OP_INVOKE_STATIC.S */
+ /*
+ * Handle a static method call.
+ *
+ * for: invoke-static, invoke-static/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- pDvmDex
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r3, [r3, #offDvmDex_pResMethods] @ r3<- pDvmDex->pResMethods
+ ldr r0, [r3, r1, lsl #2] @ r0<- resolved methodToCall
+ cmp r0, #0 @ already resolved?
+ EXPORT_PC() @ must export for invoke
+ bne common_invokeMethodRange @ yes, continue on
+0: ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ mov r2, #METHOD_STATIC @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ bne common_invokeMethodRange @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_INTERFACE_RANGE: /* 0x78 */
+/* File: armv5te/OP_INVOKE_INTERFACE_RANGE.S */
+/* File: armv5te/OP_INVOKE_INTERFACE.S */
+ /*
+ * Handle an interface method call.
+ *
+ * for: invoke-interface, invoke-interface/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r2, 2) @ r2<- FEDC or CCCC
+ FETCH(r1, 1) @ r1<- BBBB
+ .if (!1)
+ and r2, r2, #15 @ r2<- C (or stays CCCC)
+ .endif
+ EXPORT_PC() @ must export for invoke
+ GET_VREG(r0, r2) @ r0<- first arg ("this")
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- methodClassDex
+ cmp r0, #0 @ null obj?
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- method
+ beq common_errNullObject @ yes, fail
+ ldr r0, [r0, #offObject_clazz] @ r0<- thisPtr->clazz
+ bl dvmFindInterfaceMethodInCache @ r0<- call(class, ref, method, dex)
+ cmp r0, #0 @ failed?
+ beq common_exceptionThrown @ yes, handle exception
+ b common_invokeMethodRange @ jump to common handler
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_79: /* 0x79 */
+/* File: armv5te/OP_UNUSED_79.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_7A: /* 0x7a */
+/* File: armv5te/OP_UNUSED_7A.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEG_INT: /* 0x7b */
+/* File: armv6t2/OP_NEG_INT.S */
+/* File: armv6t2/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0".
+ * This could be an ARM instruction or a function call.
+ *
+ * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+ * int-to-byte, int-to-char, int-to-short
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r0, r3) @ r0<- vB
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ rsb r0, r0, #0 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 8-9 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NOT_INT: /* 0x7c */
+/* File: armv6t2/OP_NOT_INT.S */
+/* File: armv6t2/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0".
+ * This could be an ARM instruction or a function call.
+ *
+ * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+ * int-to-byte, int-to-char, int-to-short
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r0, r3) @ r0<- vB
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ mvn r0, r0 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 8-9 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEG_LONG: /* 0x7d */
+/* File: armv6t2/OP_NEG_LONG.S */
+/* File: armv6t2/unopWide.S */
+ /*
+ * Generic 64-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0/r1".
+ * This could be an ARM instruction or a function call.
+ *
+ * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r3, {r0-r1} @ r0/r1<- vAA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ rsbs r0, r0, #0 @ optional op; may set condition codes
+ rsc r1, r1, #0 @ r0/r1<- op, r2-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-11 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NOT_LONG: /* 0x7e */
+/* File: armv6t2/OP_NOT_LONG.S */
+/* File: armv6t2/unopWide.S */
+ /*
+ * Generic 64-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0/r1".
+ * This could be an ARM instruction or a function call.
+ *
+ * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r3, {r0-r1} @ r0/r1<- vAA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ mvn r0, r0 @ optional op; may set condition codes
+ mvn r1, r1 @ r0/r1<- op, r2-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-11 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEG_FLOAT: /* 0x7f */
+/* File: armv6t2/OP_NEG_FLOAT.S */
+/* File: armv6t2/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0".
+ * This could be an ARM instruction or a function call.
+ *
+ * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+ * int-to-byte, int-to-char, int-to-short
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r0, r3) @ r0<- vB
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ add r0, r0, #0x80000000 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 8-9 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEG_DOUBLE: /* 0x80 */
+/* File: armv6t2/OP_NEG_DOUBLE.S */
+/* File: armv6t2/unopWide.S */
+ /*
+ * Generic 64-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0/r1".
+ * This could be an ARM instruction or a function call.
+ *
+ * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r3, {r0-r1} @ r0/r1<- vAA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ add r1, r1, #0x80000000 @ r0/r1<- op, r2-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-11 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_LONG: /* 0x81 */
+/* File: armv6t2/OP_INT_TO_LONG.S */
+/* File: armv6t2/unopWider.S */
+ /*
+ * Generic 32bit-to-64bit unary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = op r0", where
+ * "result" is a 64-bit quantity in r0/r1.
+ *
+ * For: int-to-long, int-to-double, float-to-long, float-to-double
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r0, r3) @ r0<- vB
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ mov r1, r0, asr #31 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vA/vA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 9-10 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_FLOAT: /* 0x82 */
+/* File: arm-vfp/OP_INT_TO_FLOAT.S */
+/* File: arm-vfp/funop.S */
+ /*
+ * Generic 32-bit unary floating-point operation. Provide an "instr"
+ * line that specifies an instruction that performs "s1 = op s0".
+ *
+ * for: int-to-float, float-to-int
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ flds s0, [r3] @ s0<- vB
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ and r9, r9, #15 @ r9<- A
+ fsitos s1, s0 @ s1<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ fsts s1, [r9] @ vA<- s1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_DOUBLE: /* 0x83 */
+/* File: arm-vfp/OP_INT_TO_DOUBLE.S */
+/* File: arm-vfp/funopWider.S */
+ /*
+ * Generic 32bit-to-64bit floating point unary operation. Provide an
+ * "instr" line that specifies an instruction that performs "d0 = op s0".
+ *
+ * For: int-to-double, float-to-double
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ flds s0, [r3] @ s0<- vB
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ and r9, r9, #15 @ r9<- A
+ fsitod d0, s0 @ d0<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ fstd d0, [r9] @ vA<- d0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_LONG_TO_INT: /* 0x84 */
+/* File: armv5te/OP_LONG_TO_INT.S */
+/* we ignore the high word, making this equivalent to a 32-bit reg move */
+/* File: armv5te/OP_MOVE.S */
+ /* for move, move-object, long-to-int */
+ /* op vA, vB */
+ mov r1, rINST, lsr #12 @ r1<- B from 15:12
+ mov r0, rINST, lsr #8 @ r0<- A from 11:8
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ GET_VREG(r2, r1) @ r2<- fp[B]
+ and r0, r0, #15
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ SET_VREG(r2, r0) @ fp[A]<- r2
+ GOTO_OPCODE(ip) @ execute next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_LONG_TO_FLOAT: /* 0x85 */
+/* File: armv6t2/OP_LONG_TO_FLOAT.S */
+/* File: armv6t2/unopNarrower.S */
+ /*
+ * Generic 64bit-to-32bit unary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = op r0/r1", where
+ * "result" is a 32-bit quantity in r0.
+ *
+ * For: long-to-float, double-to-int, double-to-float
+ *
+ * (This would work for long-to-int, but that instruction is actually
+ * an exact match for OP_MOVE.)
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ ldmia r3, {r0-r1} @ r0/r1<- vB/vB+1
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl __aeabi_l2f @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 9-10 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_LONG_TO_DOUBLE: /* 0x86 */
+/* File: armv6t2/OP_LONG_TO_DOUBLE.S */
+/* File: armv6t2/unopWide.S */
+ /*
+ * Generic 64-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0/r1".
+ * This could be an ARM instruction or a function call.
+ *
+ * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r3, {r0-r1} @ r0/r1<- vAA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl __aeabi_l2d @ r0/r1<- op, r2-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-11 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FLOAT_TO_INT: /* 0x87 */
+/* File: arm-vfp/OP_FLOAT_TO_INT.S */
+/* File: arm-vfp/funop.S */
+ /*
+ * Generic 32-bit unary floating-point operation. Provide an "instr"
+ * line that specifies an instruction that performs "s1 = op s0".
+ *
+ * for: int-to-float, float-to-int
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ flds s0, [r3] @ s0<- vB
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ and r9, r9, #15 @ r9<- A
+ ftosizs s1, s0 @ s1<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ fsts s1, [r9] @ vA<- s1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FLOAT_TO_LONG: /* 0x88 */
+/* File: armv6t2/OP_FLOAT_TO_LONG.S */
+@include "armv6t2/unopWider.S" {"instr":"bl __aeabi_f2lz"}
+/* File: armv6t2/unopWider.S */
+ /*
+ * Generic 32bit-to-64bit unary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = op r0", where
+ * "result" is a 64-bit quantity in r0/r1.
+ *
+ * For: int-to-long, int-to-double, float-to-long, float-to-double
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r0, r3) @ r0<- vB
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ bl f2l_doconv @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vA/vA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 9-10 instructions */
+
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FLOAT_TO_DOUBLE: /* 0x89 */
+/* File: arm-vfp/OP_FLOAT_TO_DOUBLE.S */
+/* File: arm-vfp/funopWider.S */
+ /*
+ * Generic 32bit-to-64bit floating point unary operation. Provide an
+ * "instr" line that specifies an instruction that performs "d0 = op s0".
+ *
+ * For: int-to-double, float-to-double
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ flds s0, [r3] @ s0<- vB
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ and r9, r9, #15 @ r9<- A
+ fcvtds d0, s0 @ d0<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ fstd d0, [r9] @ vA<- d0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DOUBLE_TO_INT: /* 0x8a */
+/* File: arm-vfp/OP_DOUBLE_TO_INT.S */
+/* File: arm-vfp/funopNarrower.S */
+ /*
+ * Generic 64bit-to-32bit unary floating point operation. Provide an
+ * "instr" line that specifies an instruction that performs "s0 = op d0".
+ *
+ * For: double-to-int, double-to-float
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ fldd d0, [r3] @ d0<- vB
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ and r9, r9, #15 @ r9<- A
+ ftosizd s0, d0 @ s0<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ fsts s0, [r9] @ vA<- s0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DOUBLE_TO_LONG: /* 0x8b */
+/* File: armv6t2/OP_DOUBLE_TO_LONG.S */
+@include "armv6t2/unopWide.S" {"instr":"bl __aeabi_d2lz"}
+/* File: armv6t2/unopWide.S */
+ /*
+ * Generic 64-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0/r1".
+ * This could be an ARM instruction or a function call.
+ *
+ * For: neg-long, not-long, neg-double, long-to-double, double-to-long
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r3, {r0-r1} @ r0/r1<- vAA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl d2l_doconv @ r0/r1<- op, r2-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-11 instructions */
+
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DOUBLE_TO_FLOAT: /* 0x8c */
+/* File: arm-vfp/OP_DOUBLE_TO_FLOAT.S */
+/* File: arm-vfp/funopNarrower.S */
+ /*
+ * Generic 64bit-to-32bit unary floating point operation. Provide an
+ * "instr" line that specifies an instruction that performs "s0 = op d0".
+ *
+ * For: double-to-int, double-to-float
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ fldd d0, [r3] @ d0<- vB
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ and r9, r9, #15 @ r9<- A
+ fcvtsd s0, d0 @ s0<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ fsts s0, [r9] @ vA<- s0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_BYTE: /* 0x8d */
+/* File: armv6t2/OP_INT_TO_BYTE.S */
+/* File: armv6t2/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0".
+ * This could be an ARM instruction or a function call.
+ *
+ * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+ * int-to-byte, int-to-char, int-to-short
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r0, r3) @ r0<- vB
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ sxtb r0, r0 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 8-9 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_CHAR: /* 0x8e */
+/* File: armv6t2/OP_INT_TO_CHAR.S */
+/* File: armv6t2/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0".
+ * This could be an ARM instruction or a function call.
+ *
+ * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+ * int-to-byte, int-to-char, int-to-short
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r0, r3) @ r0<- vB
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ uxth r0, r0 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 8-9 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_SHORT: /* 0x8f */
+/* File: armv6t2/OP_INT_TO_SHORT.S */
+/* File: armv6t2/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op r0".
+ * This could be an ARM instruction or a function call.
+ *
+ * for: neg-int, not-int, neg-float, int-to-float, float-to-int,
+ * int-to-byte, int-to-char, int-to-short
+ */
+ /* unop vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r0, r3) @ r0<- vB
+ @ optional op; may set condition codes
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ sxth r0, r0 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 8-9 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_INT: /* 0x90 */
+/* File: armv5te/OP_ADD_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ add r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_INT: /* 0x91 */
+/* File: armv5te/OP_SUB_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ sub r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_INT: /* 0x92 */
+/* File: armv5te/OP_MUL_INT.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ mul r0, r1, r0 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_INT: /* 0x93 */
+/* File: armv5te/OP_DIV_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 1
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl __aeabi_idiv @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_INT: /* 0x94 */
+/* File: armv5te/OP_REM_INT.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 1
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl __aeabi_idivmod @ r1<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r1, r9) @ vAA<- r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_INT: /* 0x95 */
+/* File: armv5te/OP_AND_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ and r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_INT: /* 0x96 */
+/* File: armv5te/OP_OR_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ orr r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_INT: /* 0x97 */
+/* File: armv5te/OP_XOR_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ eor r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHL_INT: /* 0x98 */
+/* File: armv5te/OP_SHL_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, asl r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHR_INT: /* 0x99 */
+/* File: armv5te/OP_SHR_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, asr r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_USHR_INT: /* 0x9a */
+/* File: armv5te/OP_USHR_INT.S */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, lsr r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_LONG: /* 0x9b */
+/* File: armv5te/OP_ADD_LONG.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ adds r0, r0, r2 @ optional op; may set condition codes
+ adc r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_LONG: /* 0x9c */
+/* File: armv5te/OP_SUB_LONG.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ subs r0, r0, r2 @ optional op; may set condition codes
+ sbc r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_LONG: /* 0x9d */
+/* File: armv5te/OP_MUL_LONG.S */
+ /*
+ * Signed 64-bit integer multiply.
+ *
+ * Consider WXxYZ (r1r0 x r3r2) with a long multiply:
+ * WX
+ * x YZ
+ * --------
+ * ZW ZX
+ * YW YX
+ *
+ * The low word of the result holds ZX, the high word holds
+ * (ZW+YX) + (the high overflow from ZX). YW doesn't matter because
+ * it doesn't fit in the low 64 bits.
+ *
+ * Unlike most ARM math operations, multiply instructions have
+ * restrictions on using the same register more than once (Rd and Rm
+ * cannot be the same).
+ */
+ /* mul-long vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ mul ip, r2, r1 @ ip<- ZxW
+ umull r9, r10, r2, r0 @ r9/r10 <- ZxX
+ mla r2, r0, r3, ip @ r2<- YxX + (ZxW)
+ mov r0, rINST, lsr #8 @ r0<- AA
+ add r10, r2, r10 @ r10<- r10 + low(ZxW + (YxX))
+ add r0, rFP, r0, lsl #2 @ r0<- &fp[AA]
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ b .LOP_MUL_LONG_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_LONG: /* 0x9e */
+/* File: armv5te/OP_DIV_LONG.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 1
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_ldivmod @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_LONG: /* 0x9f */
+/* File: armv5te/OP_REM_LONG.S */
+/* ldivmod returns quotient in r0/r1 and remainder in r2/r3 */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 1
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_ldivmod @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r2,r3} @ vAA/vAA+1<- r2/r3
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_LONG: /* 0xa0 */
+/* File: armv5te/OP_AND_LONG.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ and r0, r0, r2 @ optional op; may set condition codes
+ and r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_LONG: /* 0xa1 */
+/* File: armv5te/OP_OR_LONG.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ orr r0, r0, r2 @ optional op; may set condition codes
+ orr r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_LONG: /* 0xa2 */
+/* File: armv5te/OP_XOR_LONG.S */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ eor r0, r0, r2 @ optional op; may set condition codes
+ eor r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHL_LONG: /* 0xa3 */
+/* File: armv5te/OP_SHL_LONG.S */
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to mask off the low
+ * 6 bits of the shift distance.
+ */
+ /* shl-long vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r3, r0, #255 @ r3<- BB
+ mov r0, r0, lsr #8 @ r0<- CC
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[BB]
+ GET_VREG(r2, r0) @ r2<- vCC
+ ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1
+ and r2, r2, #63 @ r2<- r2 & 0x3f
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+
+ mov r1, r1, asl r2 @ r1<- r1 << r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ b .LOP_SHL_LONG_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHR_LONG: /* 0xa4 */
+/* File: armv5te/OP_SHR_LONG.S */
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to mask off the low
+ * 6 bits of the shift distance.
+ */
+ /* shr-long vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r3, r0, #255 @ r3<- BB
+ mov r0, r0, lsr #8 @ r0<- CC
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[BB]
+ GET_VREG(r2, r0) @ r2<- vCC
+ ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1
+ and r2, r2, #63 @ r0<- r0 & 0x3f
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ b .LOP_SHR_LONG_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_USHR_LONG: /* 0xa5 */
+/* File: armv5te/OP_USHR_LONG.S */
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to mask off the low
+ * 6 bits of the shift distance.
+ */
+ /* ushr-long vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r3, r0, #255 @ r3<- BB
+ mov r0, r0, lsr #8 @ r0<- CC
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[BB]
+ GET_VREG(r2, r0) @ r2<- vCC
+ ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1
+ and r2, r2, #63 @ r0<- r0 & 0x3f
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ b .LOP_USHR_LONG_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_FLOAT: /* 0xa6 */
+/* File: arm-vfp/OP_ADD_FLOAT.S */
+/* File: arm-vfp/fbinop.S */
+ /*
+ * Generic 32-bit floating-point operation. Provide an "instr" line that
+ * specifies an instruction that performs "s2 = s0 op s1". Because we
+ * use the "softfp" ABI, this must be an instruction, not a function call.
+ *
+ * For: add-float, sub-float, mul-float, div-float
+ */
+ /* floatop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ flds s1, [r3] @ s1<- vCC
+ flds s0, [r2] @ s0<- vBB
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ fadds s2, s0, s1 @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vAA
+ fsts s2, [r9] @ vAA<- s2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_FLOAT: /* 0xa7 */
+/* File: arm-vfp/OP_SUB_FLOAT.S */
+/* File: arm-vfp/fbinop.S */
+ /*
+ * Generic 32-bit floating-point operation. Provide an "instr" line that
+ * specifies an instruction that performs "s2 = s0 op s1". Because we
+ * use the "softfp" ABI, this must be an instruction, not a function call.
+ *
+ * For: add-float, sub-float, mul-float, div-float
+ */
+ /* floatop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ flds s1, [r3] @ s1<- vCC
+ flds s0, [r2] @ s0<- vBB
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ fsubs s2, s0, s1 @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vAA
+ fsts s2, [r9] @ vAA<- s2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_FLOAT: /* 0xa8 */
+/* File: arm-vfp/OP_MUL_FLOAT.S */
+/* File: arm-vfp/fbinop.S */
+ /*
+ * Generic 32-bit floating-point operation. Provide an "instr" line that
+ * specifies an instruction that performs "s2 = s0 op s1". Because we
+ * use the "softfp" ABI, this must be an instruction, not a function call.
+ *
+ * For: add-float, sub-float, mul-float, div-float
+ */
+ /* floatop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ flds s1, [r3] @ s1<- vCC
+ flds s0, [r2] @ s0<- vBB
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ fmuls s2, s0, s1 @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vAA
+ fsts s2, [r9] @ vAA<- s2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_FLOAT: /* 0xa9 */
+/* File: arm-vfp/OP_DIV_FLOAT.S */
+/* File: arm-vfp/fbinop.S */
+ /*
+ * Generic 32-bit floating-point operation. Provide an "instr" line that
+ * specifies an instruction that performs "s2 = s0 op s1". Because we
+ * use the "softfp" ABI, this must be an instruction, not a function call.
+ *
+ * For: add-float, sub-float, mul-float, div-float
+ */
+ /* floatop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ flds s1, [r3] @ s1<- vCC
+ flds s0, [r2] @ s0<- vBB
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ fdivs s2, s0, s1 @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vAA
+ fsts s2, [r9] @ vAA<- s2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_FLOAT: /* 0xaa */
+/* File: armv5te/OP_REM_FLOAT.S */
+/* EABI doesn't define a float remainder function, but libm does */
+/* File: armv5te/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus. Note that we
+ * *don't* check for (INT_MIN / -1) here, because the ARM math lib
+ * handles it correctly.
+ *
+ * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int, add-float, sub-float,
+ * mul-float, div-float, rem-float
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ GET_VREG(r1, r3) @ r1<- vCC
+ GET_VREG(r0, r2) @ r0<- vBB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ @ optional op; may set condition codes
+ bl fmodf @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 11-14 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_DOUBLE: /* 0xab */
+/* File: arm-vfp/OP_ADD_DOUBLE.S */
+/* File: arm-vfp/fbinopWide.S */
+ /*
+ * Generic 64-bit double-precision floating point binary operation.
+ * Provide an "instr" line that specifies an instruction that performs
+ * "d2 = d0 op d1".
+ *
+ * for: add-double, sub-double, mul-double, div-double
+ */
+ /* doubleop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ fldd d1, [r3] @ d1<- vCC
+ fldd d0, [r2] @ d0<- vBB
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ faddd d2, d0, d1 @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vAA
+ fstd d2, [r9] @ vAA<- d2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_DOUBLE: /* 0xac */
+/* File: arm-vfp/OP_SUB_DOUBLE.S */
+/* File: arm-vfp/fbinopWide.S */
+ /*
+ * Generic 64-bit double-precision floating point binary operation.
+ * Provide an "instr" line that specifies an instruction that performs
+ * "d2 = d0 op d1".
+ *
+ * for: add-double, sub-double, mul-double, div-double
+ */
+ /* doubleop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ fldd d1, [r3] @ d1<- vCC
+ fldd d0, [r2] @ d0<- vBB
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ fsubd d2, d0, d1 @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vAA
+ fstd d2, [r9] @ vAA<- d2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_DOUBLE: /* 0xad */
+/* File: arm-vfp/OP_MUL_DOUBLE.S */
+/* File: arm-vfp/fbinopWide.S */
+ /*
+ * Generic 64-bit double-precision floating point binary operation.
+ * Provide an "instr" line that specifies an instruction that performs
+ * "d2 = d0 op d1".
+ *
+ * for: add-double, sub-double, mul-double, div-double
+ */
+ /* doubleop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ fldd d1, [r3] @ d1<- vCC
+ fldd d0, [r2] @ d0<- vBB
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ fmuld d2, d0, d1 @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vAA
+ fstd d2, [r9] @ vAA<- d2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_DOUBLE: /* 0xae */
+/* File: arm-vfp/OP_DIV_DOUBLE.S */
+/* File: arm-vfp/fbinopWide.S */
+ /*
+ * Generic 64-bit double-precision floating point binary operation.
+ * Provide an "instr" line that specifies an instruction that performs
+ * "d2 = d0 op d1".
+ *
+ * for: add-double, sub-double, mul-double, div-double
+ */
+ /* doubleop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ mov r3, r0, lsr #8 @ r3<- CC
+ and r2, r0, #255 @ r2<- BB
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vCC
+ VREG_INDEX_TO_ADDR(r2, r2) @ r2<- &vBB
+ fldd d1, [r3] @ d1<- vCC
+ fldd d0, [r2] @ d0<- vBB
+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ fdivd d2, d0, d1 @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vAA
+ fstd d2, [r9] @ vAA<- d2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_DOUBLE: /* 0xaf */
+/* File: armv5te/OP_REM_DOUBLE.S */
+/* EABI doesn't define a double remainder function, but libm does */
+/* File: armv5te/binopWide.S */
+ /*
+ * Generic 64-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * for: add-long, sub-long, div-long, rem-long, and-long, or-long,
+ * xor-long, add-double, sub-double, mul-double, div-double,
+ * rem-double
+ *
+ * IMPORTANT: you may specify "chkzero" or "preinstr" but not both.
+ */
+ /* binop vAA, vBB, vCC */
+ FETCH(r0, 1) @ r0<- CCBB
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r0, #255 @ r2<- BB
+ mov r3, r0, lsr #8 @ r3<- CC
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ add r2, rFP, r2, lsl #2 @ r2<- &fp[BB]
+ add r3, rFP, r3, lsl #2 @ r3<- &fp[CC]
+ ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1
+ ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl fmod @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 14-17 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_INT_2ADDR: /* 0xb0 */
+/* File: armv6t2/OP_ADD_INT_2ADDR.S */
+/* File: armv6t2/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ add r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_INT_2ADDR: /* 0xb1 */
+/* File: armv6t2/OP_SUB_INT_2ADDR.S */
+/* File: armv6t2/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ sub r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_INT_2ADDR: /* 0xb2 */
+/* File: armv6t2/OP_MUL_INT_2ADDR.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv6t2/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ mul r0, r1, r0 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_INT_2ADDR: /* 0xb3 */
+/* File: armv6t2/OP_DIV_INT_2ADDR.S */
+/* File: armv6t2/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 1
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_idiv @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_INT_2ADDR: /* 0xb4 */
+/* File: armv6t2/OP_REM_INT_2ADDR.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* File: armv6t2/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 1
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_idivmod @ r1<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r1, r9) @ vAA<- r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_INT_2ADDR: /* 0xb5 */
+/* File: armv6t2/OP_AND_INT_2ADDR.S */
+/* File: armv6t2/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ and r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_INT_2ADDR: /* 0xb6 */
+/* File: armv6t2/OP_OR_INT_2ADDR.S */
+/* File: armv6t2/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ orr r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_INT_2ADDR: /* 0xb7 */
+/* File: armv6t2/OP_XOR_INT_2ADDR.S */
+/* File: armv6t2/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ eor r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHL_INT_2ADDR: /* 0xb8 */
+/* File: armv6t2/OP_SHL_INT_2ADDR.S */
+/* File: armv6t2/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, asl r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHR_INT_2ADDR: /* 0xb9 */
+/* File: armv6t2/OP_SHR_INT_2ADDR.S */
+/* File: armv6t2/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, asr r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_USHR_INT_2ADDR: /* 0xba */
+/* File: armv6t2/OP_USHR_INT_2ADDR.S */
+/* File: armv6t2/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, lsr r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_LONG_2ADDR: /* 0xbb */
+/* File: armv6t2/OP_ADD_LONG_2ADDR.S */
+/* File: armv6t2/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ adds r0, r0, r2 @ optional op; may set condition codes
+ adc r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_LONG_2ADDR: /* 0xbc */
+/* File: armv6t2/OP_SUB_LONG_2ADDR.S */
+/* File: armv6t2/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ subs r0, r0, r2 @ optional op; may set condition codes
+ sbc r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_LONG_2ADDR: /* 0xbd */
+/* File: armv6t2/OP_MUL_LONG_2ADDR.S */
+ /*
+ * Signed 64-bit integer multiply, "/2addr" version.
+ *
+ * See OP_MUL_LONG for an explanation.
+ *
+ * We get a little tight on registers, so to avoid looking up &fp[A]
+ * again we stuff it into rINST.
+ */
+ /* mul-long/2addr vA, vB */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add rINST, rFP, r9, lsl #2 @ rINST<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia rINST, {r0-r1} @ r0/r1<- vAA/vAA+1
+ mul ip, r2, r1 @ ip<- ZxW
+ umull r9, r10, r2, r0 @ r9/r10 <- ZxX
+ mla r2, r0, r3, ip @ r2<- YxX + (ZxW)
+ mov r0, rINST @ r0<- &fp[A] (free up rINST)
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ add r10, r2, r10 @ r10<- r10 + low(ZxW + (YxX))
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r0, {r9-r10} @ vAA/vAA+1<- r9/r10
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_LONG_2ADDR: /* 0xbe */
+/* File: armv6t2/OP_DIV_LONG_2ADDR.S */
+/* File: armv6t2/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 1
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_ldivmod @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_LONG_2ADDR: /* 0xbf */
+/* File: armv6t2/OP_REM_LONG_2ADDR.S */
+/* ldivmod returns quotient in r0/r1 and remainder in r2/r3 */
+/* File: armv6t2/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 1
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_ldivmod @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r2,r3} @ vAA/vAA+1<- r2/r3
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_LONG_2ADDR: /* 0xc0 */
+/* File: armv6t2/OP_AND_LONG_2ADDR.S */
+/* File: armv6t2/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ and r0, r0, r2 @ optional op; may set condition codes
+ and r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_LONG_2ADDR: /* 0xc1 */
+/* File: armv6t2/OP_OR_LONG_2ADDR.S */
+/* File: armv6t2/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ orr r0, r0, r2 @ optional op; may set condition codes
+ orr r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_LONG_2ADDR: /* 0xc2 */
+/* File: armv6t2/OP_XOR_LONG_2ADDR.S */
+/* File: armv6t2/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ eor r0, r0, r2 @ optional op; may set condition codes
+ eor r1, r1, r3 @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHL_LONG_2ADDR: /* 0xc3 */
+/* File: armv6t2/OP_SHL_LONG_2ADDR.S */
+ /*
+ * Long integer shift, 2addr version. vA is 64-bit value/result, vB is
+ * 32-bit shift distance.
+ */
+ /* shl-long/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r2, r3) @ r2<- vB
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ and r2, r2, #63 @ r2<- r2 & 0x3f
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+
+ mov r1, r1, asl r2 @ r1<- r1 << r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32)
+ mov r0, r0, asl r2 @ r0<- r0 << r2
+ b .LOP_SHL_LONG_2ADDR_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHR_LONG_2ADDR: /* 0xc4 */
+/* File: armv6t2/OP_SHR_LONG_2ADDR.S */
+ /*
+ * Long integer shift, 2addr version. vA is 64-bit value/result, vB is
+ * 32-bit shift distance.
+ */
+ /* shr-long/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r2, r3) @ r2<- vB
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ and r2, r2, #63 @ r2<- r2 & 0x3f
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32)
+ mov r1, r1, asr r2 @ r1<- r1 >> r2
+ b .LOP_SHR_LONG_2ADDR_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_USHR_LONG_2ADDR: /* 0xc5 */
+/* File: armv6t2/OP_USHR_LONG_2ADDR.S */
+ /*
+ * Long integer shift, 2addr version. vA is 64-bit value/result, vB is
+ * 32-bit shift distance.
+ */
+ /* ushr-long/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r2, r3) @ r2<- vB
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ and r2, r2, #63 @ r2<- r2 & 0x3f
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32)
+ mov r1, r1, lsr r2 @ r1<- r1 >>> r2
+ b .LOP_USHR_LONG_2ADDR_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_FLOAT_2ADDR: /* 0xc6 */
+/* File: arm-vfp/OP_ADD_FLOAT_2ADDR.S */
+/* File: arm-vfp/fbinop2addr.S */
+ /*
+ * Generic 32-bit floating point "/2addr" binary operation. Provide
+ * an "instr" line that specifies an instruction that performs
+ * "s2 = s0 op s1".
+ *
+ * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ and r9, r9, #15 @ r9<- A
+ flds s1, [r3] @ s1<- vB
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ flds s0, [r9] @ s0<- vA
+
+ fadds s2, s0, s1 @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fsts s2, [r9] @ vAA<- s2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_FLOAT_2ADDR: /* 0xc7 */
+/* File: arm-vfp/OP_SUB_FLOAT_2ADDR.S */
+/* File: arm-vfp/fbinop2addr.S */
+ /*
+ * Generic 32-bit floating point "/2addr" binary operation. Provide
+ * an "instr" line that specifies an instruction that performs
+ * "s2 = s0 op s1".
+ *
+ * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ and r9, r9, #15 @ r9<- A
+ flds s1, [r3] @ s1<- vB
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ flds s0, [r9] @ s0<- vA
+
+ fsubs s2, s0, s1 @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fsts s2, [r9] @ vAA<- s2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_FLOAT_2ADDR: /* 0xc8 */
+/* File: arm-vfp/OP_MUL_FLOAT_2ADDR.S */
+/* File: arm-vfp/fbinop2addr.S */
+ /*
+ * Generic 32-bit floating point "/2addr" binary operation. Provide
+ * an "instr" line that specifies an instruction that performs
+ * "s2 = s0 op s1".
+ *
+ * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ and r9, r9, #15 @ r9<- A
+ flds s1, [r3] @ s1<- vB
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ flds s0, [r9] @ s0<- vA
+
+ fmuls s2, s0, s1 @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fsts s2, [r9] @ vAA<- s2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_FLOAT_2ADDR: /* 0xc9 */
+/* File: arm-vfp/OP_DIV_FLOAT_2ADDR.S */
+/* File: arm-vfp/fbinop2addr.S */
+ /*
+ * Generic 32-bit floating point "/2addr" binary operation. Provide
+ * an "instr" line that specifies an instruction that performs
+ * "s2 = s0 op s1".
+ *
+ * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ and r9, r9, #15 @ r9<- A
+ flds s1, [r3] @ s1<- vB
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ flds s0, [r9] @ s0<- vA
+
+ fdivs s2, s0, s1 @ s2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fsts s2, [r9] @ vAA<- s2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_FLOAT_2ADDR: /* 0xca */
+/* File: armv6t2/OP_REM_FLOAT_2ADDR.S */
+/* EABI doesn't define a float remainder function, but libm does */
+/* File: armv6t2/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r1, r3) @ r1<- vB
+ GET_VREG(r0, r9) @ r0<- vA
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl fmodf @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_DOUBLE_2ADDR: /* 0xcb */
+/* File: arm-vfp/OP_ADD_DOUBLE_2ADDR.S */
+/* File: arm-vfp/fbinopWide2addr.S */
+ /*
+ * Generic 64-bit floating point "/2addr" binary operation. Provide
+ * an "instr" line that specifies an instruction that performs
+ * "d2 = d0 op d1".
+ *
+ * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+ * div-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ and r9, r9, #15 @ r9<- A
+ fldd d1, [r3] @ d1<- vB
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ fldd d0, [r9] @ d0<- vA
+
+ faddd d2, d0, d1 @ d2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fstd d2, [r9] @ vAA<- d2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_DOUBLE_2ADDR: /* 0xcc */
+/* File: arm-vfp/OP_SUB_DOUBLE_2ADDR.S */
+/* File: arm-vfp/fbinopWide2addr.S */
+ /*
+ * Generic 64-bit floating point "/2addr" binary operation. Provide
+ * an "instr" line that specifies an instruction that performs
+ * "d2 = d0 op d1".
+ *
+ * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+ * div-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ and r9, r9, #15 @ r9<- A
+ fldd d1, [r3] @ d1<- vB
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ fldd d0, [r9] @ d0<- vA
+
+ fsubd d2, d0, d1 @ d2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fstd d2, [r9] @ vAA<- d2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_DOUBLE_2ADDR: /* 0xcd */
+/* File: arm-vfp/OP_MUL_DOUBLE_2ADDR.S */
+/* File: arm-vfp/fbinopWide2addr.S */
+ /*
+ * Generic 64-bit floating point "/2addr" binary operation. Provide
+ * an "instr" line that specifies an instruction that performs
+ * "d2 = d0 op d1".
+ *
+ * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+ * div-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ and r9, r9, #15 @ r9<- A
+ fldd d1, [r3] @ d1<- vB
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ fldd d0, [r9] @ d0<- vA
+
+ fmuld d2, d0, d1 @ d2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fstd d2, [r9] @ vAA<- d2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_DOUBLE_2ADDR: /* 0xce */
+/* File: arm-vfp/OP_DIV_DOUBLE_2ADDR.S */
+/* File: arm-vfp/fbinopWide2addr.S */
+ /*
+ * Generic 64-bit floating point "/2addr" binary operation. Provide
+ * an "instr" line that specifies an instruction that performs
+ * "d2 = d0 op d1".
+ *
+ * For: add-double/2addr, sub-double/2addr, mul-double/2addr,
+ * div-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r3, rINST, lsr #12 @ r3<- B
+ mov r9, rINST, lsr #8 @ r9<- A+
+ VREG_INDEX_TO_ADDR(r3, r3) @ r3<- &vB
+ and r9, r9, #15 @ r9<- A
+ fldd d1, [r3] @ d1<- vB
+ VREG_INDEX_TO_ADDR(r9, r9) @ r9<- &vA
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+ fldd d0, [r9] @ d0<- vA
+
+ fdivd d2, d0, d1 @ d2<- op
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ fstd d2, [r9] @ vAA<- d2
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_DOUBLE_2ADDR: /* 0xcf */
+/* File: armv6t2/OP_REM_DOUBLE_2ADDR.S */
+/* EABI doesn't define a double remainder function, but libm does */
+/* File: armv6t2/binopWide2addr.S */
+ /*
+ * Generic 64-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0-r1 op r2-r3".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr,
+ * and-long/2addr, or-long/2addr, xor-long/2addr, add-double/2addr,
+ * sub-double/2addr, mul-double/2addr, div-double/2addr,
+ * rem-double/2addr
+ */
+ /* binop/2addr vA, vB */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ add r1, rFP, r1, lsl #2 @ r1<- &fp[B]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
+ ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ .if 0
+ orrs ip, r2, r3 @ second arg (r2-r3) is zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(1) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl fmod @ result<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0,r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 12-15 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_INT_LIT16: /* 0xd0 */
+/* File: armv6t2/OP_ADD_INT_LIT16.S */
+/* File: armv6t2/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r0, r2) @ r0<- vB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ add r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RSUB_INT: /* 0xd1 */
+/* File: armv6t2/OP_RSUB_INT.S */
+/* this op is "rsub-int", but can be thought of as "rsub-int/lit16" */
+/* File: armv6t2/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r0, r2) @ r0<- vB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ rsb r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_INT_LIT16: /* 0xd2 */
+/* File: armv6t2/OP_MUL_INT_LIT16.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv6t2/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r0, r2) @ r0<- vB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ mul r0, r1, r0 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_INT_LIT16: /* 0xd3 */
+/* File: armv6t2/OP_DIV_INT_LIT16.S */
+/* File: armv6t2/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r0, r2) @ r0<- vB
+ .if 1
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ bl __aeabi_idiv @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_INT_LIT16: /* 0xd4 */
+/* File: armv6t2/OP_REM_INT_LIT16.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* File: armv6t2/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r0, r2) @ r0<- vB
+ .if 1
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ bl __aeabi_idivmod @ r1<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r1, r9) @ vAA<- r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_INT_LIT16: /* 0xd5 */
+/* File: armv6t2/OP_AND_INT_LIT16.S */
+/* File: armv6t2/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r0, r2) @ r0<- vB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ and r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_INT_LIT16: /* 0xd6 */
+/* File: armv6t2/OP_OR_INT_LIT16.S */
+/* File: armv6t2/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r0, r2) @ r0<- vB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ orr r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_INT_LIT16: /* 0xd7 */
+/* File: armv6t2/OP_XOR_INT_LIT16.S */
+/* File: armv6t2/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16,
+ * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ FETCH_S(r1, 1) @ r1<- ssssCCCC (sign-extended)
+ mov r2, rINST, lsr #12 @ r2<- B
+ ubfx r9, rINST, #8, #4 @ r9<- A
+ GET_VREG(r0, r2) @ r0<- vB
+ .if 0
+ cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ eor r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-13 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_INT_LIT8: /* 0xd8 */
+/* File: armv5te/OP_ADD_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ add r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RSUB_INT_LIT8: /* 0xd9 */
+/* File: armv5te/OP_RSUB_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ rsb r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_INT_LIT8: /* 0xda */
+/* File: armv5te/OP_MUL_INT_LIT8.S */
+/* must be "mul r0, r1, r0" -- "r0, r0, r1" is illegal */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ mul r0, r1, r0 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_INT_LIT8: /* 0xdb */
+/* File: armv5te/OP_DIV_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 1
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_idiv @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_INT_LIT8: /* 0xdc */
+/* File: armv5te/OP_REM_INT_LIT8.S */
+/* idivmod returns quotient in r0 and remainder in r1 */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 1
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ bl __aeabi_idivmod @ r1<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r1, r9) @ vAA<- r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_INT_LIT8: /* 0xdd */
+/* File: armv5te/OP_AND_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ and r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_INT_LIT8: /* 0xde */
+/* File: armv5te/OP_OR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ orr r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_INT_LIT8: /* 0xdf */
+/* File: armv5te/OP_XOR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ @ optional op; may set condition codes
+ eor r0, r0, r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHL_INT_LIT8: /* 0xe0 */
+/* File: armv5te/OP_SHL_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, asl r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHR_INT_LIT8: /* 0xe1 */
+/* File: armv5te/OP_SHR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, asr r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_USHR_INT_LIT8: /* 0xe2 */
+/* File: armv5te/OP_USHR_INT_LIT8.S */
+/* File: armv5te/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8,
+ * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ FETCH_S(r3, 1) @ r3<- ssssCCBB (sign-extended for CC)
+ mov r9, rINST, lsr #8 @ r9<- AA
+ and r2, r3, #255 @ r2<- BB
+ GET_VREG(r0, r2) @ r0<- vBB
+ movs r1, r3, asr #8 @ r1<- ssssssCC (sign extended)
+ .if 0
+ @cmp r1, #0 @ is second operand zero?
+ beq common_errDivideByZero
+ .endif
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+
+ and r1, r1, #31 @ optional op; may set condition codes
+ mov r0, r0, lsr r1 @ r0<- op, r0-r3 changed
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+ /* 10-12 instructions */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_VOLATILE: /* 0xe3 */
+/* File: armv5te/OP_IGET_VOLATILE.S */
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_VOLATILE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_VOLATILE_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_VOLATILE: /* 0xe4 */
+/* File: armv5te/OP_IPUT_VOLATILE.S */
+/* File: armv5te/OP_IPUT.S */
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_VOLATILE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_VOLATILE_finish @ yes, finish up
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_VOLATILE: /* 0xe5 */
+/* File: armv5te/OP_SGET_VOLATILE.S */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_VOLATILE_resolve @ yes, do resolve
+.LOP_SGET_VOLATILE_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ SMP_DMB @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_VOLATILE: /* 0xe6 */
+/* File: armv5te/OP_SPUT_VOLATILE.S */
+/* File: armv5te/OP_SPUT.S */
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SPUT_VOLATILE_resolve @ yes, do resolve
+.LOP_SPUT_VOLATILE_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SMP_DMB @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_OBJECT_VOLATILE: /* 0xe7 */
+/* File: armv5te/OP_IGET_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_OBJECT_VOLATILE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_OBJECT_VOLATILE_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_WIDE_VOLATILE: /* 0xe8 */
+/* File: armv5te/OP_IGET_WIDE_VOLATILE.S */
+/* File: armv5te/OP_IGET_WIDE.S */
+ /*
+ * Wide 32-bit instance field get.
+ */
+ /* iget-wide vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IGET_WIDE_VOLATILE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0
+ bne .LOP_IGET_WIDE_VOLATILE_finish
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_WIDE_VOLATILE: /* 0xe9 */
+/* File: armv5te/OP_IPUT_WIDE_VOLATILE.S */
+/* File: armv5te/OP_IPUT_WIDE.S */
+ /* iput-wide vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_WIDE_VOLATILE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_WIDE_VOLATILE_finish @ yes, finish up
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_WIDE_VOLATILE: /* 0xea */
+/* File: armv5te/OP_SGET_WIDE_VOLATILE.S */
+/* File: armv5te/OP_SGET_WIDE.S */
+ /*
+ * 64-bit SGET handler.
+ */
+ /* sget-wide vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_WIDE_VOLATILE_resolve @ yes, do resolve
+.LOP_SGET_WIDE_VOLATILE_finish:
+ mov r9, rINST, lsr #8 @ r9<- AA
+ .if 1
+ add r0, r0, #offStaticField_value @ r0<- pointer to data
+ bl dvmQuasiAtomicRead64 @ r0/r1<- contents of field
+ .else
+ ldrd r0, [r0, #offStaticField_value] @ r0/r1<- field value (aligned)
+ .endif
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_WIDE_VOLATILE: /* 0xeb */
+/* File: armv5te/OP_SPUT_WIDE_VOLATILE.S */
+/* File: armv5te/OP_SPUT_WIDE.S */
+ /*
+ * 64-bit SPUT handler.
+ */
+ /* sput-wide vAA, field@BBBB */
+ ldr r0, [rGLUE, #offGlue_methodClassDex] @ r0<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r0, [r0, #offDvmDex_pResFields] @ r0<- dvmDex->pResFields
+ mov r9, rINST, lsr #8 @ r9<- AA
+ ldr r2, [r0, r1, lsl #2] @ r2<- resolved StaticField ptr
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ cmp r2, #0 @ is resolved entry null?
+ beq .LOP_SPUT_WIDE_VOLATILE_resolve @ yes, do resolve
+.LOP_SPUT_WIDE_VOLATILE_finish: @ field ptr in r2, AA in r9
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
+ GET_INST_OPCODE(r10) @ extract opcode from rINST
+ .if 1
+ add r2, r2, #offStaticField_value @ r2<- pointer to data
+ bl dvmQuasiAtomicSwap64 @ stores r0/r1 into addr r2
+ .else
+ strd r0, [r2, #offStaticField_value] @ field<- vAA/vAA+1
+ .endif
+ GOTO_OPCODE(r10) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_BREAKPOINT: /* 0xec */
+/* File: armv5te/OP_BREAKPOINT.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_THROW_VERIFICATION_ERROR: /* 0xed */
+/* File: armv5te/OP_THROW_VERIFICATION_ERROR.S */
+ /*
+ * Handle a throw-verification-error instruction. This throws an
+ * exception for an error discovered during verification. The
+ * exception is indicated by AA, with some detail provided by BBBB.
+ */
+ /* op AA, ref@BBBB */
+ ldr r0, [rGLUE, #offGlue_method] @ r0<- glue->method
+ FETCH(r2, 1) @ r2<- BBBB
+ EXPORT_PC() @ export the PC
+ mov r1, rINST, lsr #8 @ r1<- AA
+ bl dvmThrowVerificationError @ always throws
+ b common_exceptionThrown @ handle exception
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_EXECUTE_INLINE: /* 0xee */
+/* File: armv5te/OP_EXECUTE_INLINE.S */
+ /*
+ * Execute a "native inline" instruction.
+ *
+ * We need to call an InlineOp4Func:
+ * bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+ *
+ * The first four args are in r0-r3, pointer to return value storage
+ * is on the stack. The function's return value is a flag that tells
+ * us if an exception was thrown.
+ */
+ /* [opt] execute-inline vAA, {vC, vD, vE, vF}, inline@BBBB */
+ FETCH(r10, 1) @ r10<- BBBB
+ add r1, rGLUE, #offGlue_retval @ r1<- &glue->retval
+ EXPORT_PC() @ can throw
+ sub sp, sp, #8 @ make room for arg, +64 bit align
+ mov r0, rINST, lsr #12 @ r0<- B
+ str r1, [sp] @ push &glue->retval
+ bl .LOP_EXECUTE_INLINE_continue @ make call; will return after
+ add sp, sp, #8 @ pop stack
+ cmp r0, #0 @ test boolean result of inline
+ beq common_exceptionThrown @ returned false, handle exception
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_EXECUTE_INLINE_RANGE: /* 0xef */
+/* File: armv5te/OP_EXECUTE_INLINE_RANGE.S */
+ /*
+ * Execute a "native inline" instruction, using "/range" semantics.
+ * Same idea as execute-inline, but we get the args differently.
+ *
+ * We need to call an InlineOp4Func:
+ * bool (func)(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult)
+ *
+ * The first four args are in r0-r3, pointer to return value storage
+ * is on the stack. The function's return value is a flag that tells
+ * us if an exception was thrown.
+ */
+ /* [opt] execute-inline/range {vCCCC..v(CCCC+AA-1)}, inline@BBBB */
+ FETCH(r10, 1) @ r10<- BBBB
+ add r1, rGLUE, #offGlue_retval @ r1<- &glue->retval
+ EXPORT_PC() @ can throw
+ sub sp, sp, #8 @ make room for arg, +64 bit align
+ mov r0, rINST, lsr #8 @ r0<- AA
+ str r1, [sp] @ push &glue->retval
+ bl .LOP_EXECUTE_INLINE_RANGE_continue @ make call; will return after
+ add sp, sp, #8 @ pop stack
+ cmp r0, #0 @ test boolean result of inline
+ beq common_exceptionThrown @ returned false, handle exception
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_DIRECT_EMPTY: /* 0xf0 */
+/* File: armv5te/OP_INVOKE_DIRECT_EMPTY.S */
+ /*
+ * invoke-direct-empty is a no-op in a "standard" interpreter.
+ */
+ FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ GOTO_OPCODE(ip) @ execute it
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_F1: /* 0xf1 */
+/* File: armv5te/OP_UNUSED_F1.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_QUICK: /* 0xf2 */
+/* File: armv6t2/OP_IGET_QUICK.S */
+ /* For: iget-quick, iget-object-quick */
+ /* op vA, vB, offset@CCCC */
+ mov r2, rINST, lsr #12 @ r2<- B
+ FETCH(r1, 1) @ r1<- field byte offset
+ GET_VREG(r3, r2) @ r3<- object we're operating on
+ ubfx r2, rINST, #8, #4 @ r2<- A
+ cmp r3, #0 @ check object for null
+ beq common_errNullObject @ object was null
+ ldr r0, [r3, r1] @ r0<- obj.field (always 32 bits)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_WIDE_QUICK: /* 0xf3 */
+/* File: armv6t2/OP_IGET_WIDE_QUICK.S */
+ /* iget-wide-quick vA, vB, offset@CCCC */
+ mov r2, rINST, lsr #12 @ r2<- B
+ FETCH(ip, 1) @ ip<- field byte offset
+ GET_VREG(r3, r2) @ r3<- object we're operating on
+ ubfx r2, rINST, #8, #4 @ r2<- A
+ cmp r3, #0 @ check object for null
+ beq common_errNullObject @ object was null
+ ldrd r0, [r3, ip] @ r0<- obj.field (64 bits, aligned)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ add r3, rFP, r2, lsl #2 @ r3<- &fp[A]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ fp[A]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_OBJECT_QUICK: /* 0xf4 */
+/* File: armv5te/OP_IGET_OBJECT_QUICK.S */
+/* File: armv5te/OP_IGET_QUICK.S */
+ /* For: iget-quick, iget-object-quick */
+ /* op vA, vB, offset@CCCC */
+ mov r2, rINST, lsr #12 @ r2<- B
+ GET_VREG(r3, r2) @ r3<- object we're operating on
+ FETCH(r1, 1) @ r1<- field byte offset
+ cmp r3, #0 @ check object for null
+ mov r2, rINST, lsr #8 @ r2<- A(+)
+ beq common_errNullObject @ object was null
+ ldr r0, [r3, r1] @ r0<- obj.field (always 32 bits)
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_QUICK: /* 0xf5 */
+/* File: armv6t2/OP_IPUT_QUICK.S */
+ /* For: iput-quick, iput-object-quick */
+ /* op vA, vB, offset@CCCC */
+ mov r2, rINST, lsr #12 @ r2<- B
+ FETCH(r1, 1) @ r1<- field byte offset
+ GET_VREG(r3, r2) @ r3<- fp[B], the object pointer
+ ubfx r2, rINST, #8, #4 @ r2<- A
+ cmp r3, #0 @ check object for null
+ beq common_errNullObject @ object was null
+ GET_VREG(r0, r2) @ r0<- fp[A]
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ str r0, [r3, r1] @ obj.field (always 32 bits)<- r0
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_WIDE_QUICK: /* 0xf6 */
+/* File: armv6t2/OP_IPUT_WIDE_QUICK.S */
+ /* iput-wide-quick vA, vB, offset@CCCC */
+ mov r1, rINST, lsr #12 @ r1<- B
+ ubfx r0, rINST, #8, #4 @ r0<- A
+ GET_VREG(r2, r1) @ r2<- fp[B], the object pointer
+ add r3, rFP, r0, lsl #2 @ r3<- &fp[A]
+ cmp r2, #0 @ check object for null
+ ldmia r3, {r0-r1} @ r0/r1<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH(r3, 1) @ r3<- field byte offset
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ strd r0, [r2, r3] @ obj.field (64 bits, aligned)<- r0/r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_OBJECT_QUICK: /* 0xf7 */
+/* File: armv5te/OP_IPUT_OBJECT_QUICK.S */
+ /* For: iput-object-quick */
+ /* op vA, vB, offset@CCCC */
+ mov r2, rINST, lsr #12 @ r2<- B
+ GET_VREG(r3, r2) @ r3<- fp[B], the object pointer
+ FETCH(r1, 1) @ r1<- field byte offset
+ cmp r3, #0 @ check object for null
+ mov r2, rINST, lsr #8 @ r2<- A(+)
+ beq common_errNullObject @ object was null
+ and r2, r2, #15
+ GET_VREG(r0, r2) @ r0<- fp[A]
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ str r0, [r3, r1] @ obj.field (always 32 bits)<- r0
+ cmp r0, #0
+ strneb r2, [r2, r3, lsr #GC_CARD_SHIFT] @ mark card based on obj head
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_VIRTUAL_QUICK: /* 0xf8 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK.S */
+ /*
+ * Handle an optimized virtual method call.
+ *
+ * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r3, 2) @ r3<- FEDC or CCCC
+ FETCH(r1, 1) @ r1<- BBBB
+ .if (!0)
+ and r3, r3, #15 @ r3<- C (or stays CCCC)
+ .endif
+ GET_VREG(r2, r3) @ r2<- vC ("this" ptr)
+ cmp r2, #0 @ is "this" null?
+ beq common_errNullObject @ null "this", throw exception
+ ldr r2, [r2, #offObject_clazz] @ r2<- thisPtr->clazz
+ ldr r2, [r2, #offClassObject_vtable] @ r2<- thisPtr->clazz->vtable
+ EXPORT_PC() @ invoke must export
+ ldr r0, [r2, r1, lsl #2] @ r3<- vtable[BBBB]
+ bl common_invokeMethodNoRange @ continue on
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_VIRTUAL_QUICK_RANGE: /* 0xf9 */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK_RANGE.S */
+/* File: armv5te/OP_INVOKE_VIRTUAL_QUICK.S */
+ /*
+ * Handle an optimized virtual method call.
+ *
+ * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r3, 2) @ r3<- FEDC or CCCC
+ FETCH(r1, 1) @ r1<- BBBB
+ .if (!1)
+ and r3, r3, #15 @ r3<- C (or stays CCCC)
+ .endif
+ GET_VREG(r2, r3) @ r2<- vC ("this" ptr)
+ cmp r2, #0 @ is "this" null?
+ beq common_errNullObject @ null "this", throw exception
+ ldr r2, [r2, #offObject_clazz] @ r2<- thisPtr->clazz
+ ldr r2, [r2, #offClassObject_vtable] @ r2<- thisPtr->clazz->vtable
+ EXPORT_PC() @ invoke must export
+ ldr r0, [r2, r1, lsl #2] @ r3<- vtable[BBBB]
+ bl common_invokeMethodRange @ continue on
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_SUPER_QUICK: /* 0xfa */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK.S */
+ /*
+ * Handle an optimized "super" method call.
+ *
+ * for: [opt] invoke-super-quick, invoke-super-quick/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ .if (!0)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r2, [r2, #offMethod_clazz] @ r2<- method->clazz
+ EXPORT_PC() @ must export for invoke
+ ldr r2, [r2, #offClassObject_super] @ r2<- method->clazz->super
+ GET_VREG(r3, r10) @ r3<- "this"
+ ldr r2, [r2, #offClassObject_vtable] @ r2<- ...clazz->super->vtable
+ cmp r3, #0 @ null "this" ref?
+ ldr r0, [r2, r1, lsl #2] @ r0<- super->vtable[BBBB]
+ beq common_errNullObject @ "this" is null, throw exception
+ bl common_invokeMethodNoRange @ continue on
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_SUPER_QUICK_RANGE: /* 0xfb */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK_RANGE.S */
+/* File: armv5te/OP_INVOKE_SUPER_QUICK.S */
+ /*
+ * Handle an optimized "super" method call.
+ *
+ * for: [opt] invoke-super-quick, invoke-super-quick/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ FETCH(r10, 2) @ r10<- GFED or CCCC
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ .if (!1)
+ and r10, r10, #15 @ r10<- D (or stays CCCC)
+ .endif
+ FETCH(r1, 1) @ r1<- BBBB
+ ldr r2, [r2, #offMethod_clazz] @ r2<- method->clazz
+ EXPORT_PC() @ must export for invoke
+ ldr r2, [r2, #offClassObject_super] @ r2<- method->clazz->super
+ GET_VREG(r3, r10) @ r3<- "this"
+ ldr r2, [r2, #offClassObject_vtable] @ r2<- ...clazz->super->vtable
+ cmp r3, #0 @ null "this" ref?
+ ldr r0, [r2, r1, lsl #2] @ r0<- super->vtable[BBBB]
+ beq common_errNullObject @ "this" is null, throw exception
+ bl common_invokeMethodRange @ continue on
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */
+/* File: armv5te/OP_IPUT_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_IPUT_OBJECT.S */
+ /*
+ * 32-bit instance field put.
+ *
+ * for: iput-object, iput-object-volatile
+ */
+ /* op vA, vB, field@CCCC */
+ mov r0, rINST, lsr #12 @ r0<- B
+ ldr r3, [rGLUE, #offGlue_methodClassDex] @ r3<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref CCCC
+ ldr r2, [r3, #offDvmDex_pResFields] @ r2<- pDvmDex->pResFields
+ GET_VREG(r9, r0) @ r9<- fp[B], the object pointer
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved InstField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_IPUT_OBJECT_VOLATILE_finish @ no, already resolved
+8: ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveInstField @ r0<- resolved InstField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_IPUT_OBJECT_VOLATILE_finish @ yes, finish up
+ b common_exceptionThrown
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_OBJECT_VOLATILE: /* 0xfd */
+/* File: armv5te/OP_SGET_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ beq .LOP_SGET_OBJECT_VOLATILE_resolve @ yes, do resolve
+.LOP_SGET_OBJECT_VOLATILE_finish: @ field ptr in r0
+ ldr r1, [r0, #offStaticField_value] @ r1<- field value
+ SMP_DMB @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r2) @ fp[AA]<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */
+/* File: armv5te/OP_SPUT_OBJECT_VOLATILE.S */
+/* File: armv5te/OP_SPUT_OBJECT.S */
+ /*
+ * 32-bit SPUT handler for objects
+ *
+ * for: sput-object, sput-object-volatile
+ */
+ /* op vAA, field@BBBB */
+ ldr r2, [rGLUE, #offGlue_methodClassDex] @ r2<- DvmDex
+ FETCH(r1, 1) @ r1<- field ref BBBB
+ ldr r2, [r2, #offDvmDex_pResFields] @ r2<- dvmDex->pResFields
+ ldr r0, [r2, r1, lsl #2] @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ is resolved entry null?
+ bne .LOP_SPUT_OBJECT_VOLATILE_finish @ no, continue
+ ldr r9, [rGLUE, #offGlue_method] @ r9<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r9, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_OBJECT_VOLATILE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_FF: /* 0xff */
+/* File: armv5te/OP_UNUSED_FF.S */
+/* File: armv5te/unused.S */
+ bl common_abort
+
+
+
+ .balign 64
+ .size dvmAsmInstructionStart, .-dvmAsmInstructionStart
+ .global dvmAsmInstructionEnd
+dvmAsmInstructionEnd:
+
+/*
+ * ===========================================================================
+ * Sister implementations
+ * ===========================================================================
+ */
+ .global dvmAsmSisterStart
+ .type dvmAsmSisterStart, %function
+ .text
+ .balign 4
+dvmAsmSisterStart:
+
+/* continuation for OP_CONST_STRING */
+
+ /*
+ * Continuation if the String has not yet been resolved.
+ * r1: BBBB (String ref)
+ * r9: target register
+ */
+.LOP_CONST_STRING_resolve:
+ EXPORT_PC()
+ ldr r0, [rGLUE, #offGlue_method] @ r0<- glue->method
+ ldr r0, [r0, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveString @ r0<- String reference
+ cmp r0, #0 @ failed?
+ beq common_exceptionThrown @ yup, handle the exception
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_CONST_STRING_JUMBO */
+
+ /*
+ * Continuation if the String has not yet been resolved.
+ * r1: BBBBBBBB (String ref)
+ * r9: target register
+ */
+.LOP_CONST_STRING_JUMBO_resolve:
+ EXPORT_PC()
+ ldr r0, [rGLUE, #offGlue_method] @ r0<- glue->method
+ ldr r0, [r0, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveString @ r0<- String reference
+ cmp r0, #0 @ failed?
+ beq common_exceptionThrown @ yup, handle the exception
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_CONST_CLASS */
+
+ /*
+ * Continuation if the Class has not yet been resolved.
+ * r1: BBBB (Class ref)
+ * r9: target register
+ */
+.LOP_CONST_CLASS_resolve:
+ EXPORT_PC()
+ ldr r0, [rGLUE, #offGlue_method] @ r0<- glue->method
+ mov r2, #1 @ r2<- true
+ ldr r0, [r0, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- Class reference
+ cmp r0, #0 @ failed?
+ beq common_exceptionThrown @ yup, handle the exception
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_CHECK_CAST */
+
+ /*
+ * Trivial test failed, need to perform full check. This is common.
+ * r0 holds obj->clazz
+ * r1 holds class resolved from BBBB
+ * r9 holds object
+ */
+.LOP_CHECK_CAST_fullcheck:
+ bl dvmInstanceofNonTrivial @ r0<- boolean result
+ cmp r0, #0 @ failed?
+ bne .LOP_CHECK_CAST_okay @ no, success
+
+ @ A cast has failed. We need to throw a ClassCastException with the
+ @ class of the object that failed to be cast.
+ EXPORT_PC() @ about to throw
+ ldr r3, [r9, #offObject_clazz] @ r3<- obj->clazz
+ ldr r0, .LstrClassCastExceptionPtr
+ ldr r1, [r3, #offClassObject_descriptor] @ r1<- obj->clazz->descriptor
+ bl dvmThrowExceptionWithClassMessage
+ b common_exceptionThrown
+
+ /*
+ * Resolution required. This is the least-likely path.
+ *
+ * r2 holds BBBB
+ * r9 holds object
+ */
+.LOP_CHECK_CAST_resolve:
+ EXPORT_PC() @ resolve() could throw
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ mov r1, r2 @ r1<- BBBB
+ mov r2, #0 @ r2<- false
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- resolved ClassObject ptr
+ cmp r0, #0 @ got null?
+ beq common_exceptionThrown @ yes, handle exception
+ mov r1, r0 @ r1<- class resolved from BBB
+ ldr r0, [r9, #offObject_clazz] @ r0<- obj->clazz
+ b .LOP_CHECK_CAST_resolved @ pick up where we left off
+
+.LstrClassCastExceptionPtr:
+ .word .LstrClassCastException
+
+/* continuation for OP_INSTANCE_OF */
+
+ /*
+ * Trivial test failed, need to perform full check. This is common.
+ * r0 holds obj->clazz
+ * r1 holds class resolved from BBBB
+ * r9 holds A
+ */
+.LOP_INSTANCE_OF_fullcheck:
+ bl dvmInstanceofNonTrivial @ r0<- boolean result
+ @ fall through to OP_INSTANCE_OF_store
+
+ /*
+ * r0 holds boolean result
+ * r9 holds A
+ */
+.LOP_INSTANCE_OF_store:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r0, r9) @ vA<- r0
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+ /*
+ * Trivial test succeeded, save and bail.
+ * r9 holds A
+ */
+.LOP_INSTANCE_OF_trivial:
+ mov r0, #1 @ indicate success
+ @ could b OP_INSTANCE_OF_store, but copying is faster and cheaper
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r0, r9) @ vA<- r0
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+ /*
+ * Resolution required. This is the least-likely path.
+ *
+ * r3 holds BBBB
+ * r9 holds A
+ */
+.LOP_INSTANCE_OF_resolve:
+ EXPORT_PC() @ resolve() could throw
+ ldr r0, [rGLUE, #offGlue_method] @ r0<- glue->method
+ mov r1, r3 @ r1<- BBBB
+ mov r2, #1 @ r2<- true
+ ldr r0, [r0, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- resolved ClassObject ptr
+ cmp r0, #0 @ got null?
+ beq common_exceptionThrown @ yes, handle exception
+ mov r1, r0 @ r1<- class resolved from BBB
+ mov r3, rINST, lsr #12 @ r3<- B
+ GET_VREG(r0, r3) @ r0<- vB (object)
+ ldr r0, [r0, #offObject_clazz] @ r0<- obj->clazz
+ b .LOP_INSTANCE_OF_resolved @ pick up where we left off
+
+/* continuation for OP_NEW_INSTANCE */
+
+ .balign 32 @ minimize cache lines
+.LOP_NEW_INSTANCE_finish: @ r0=new object
+ mov r3, rINST, lsr #8 @ r3<- AA
+ cmp r0, #0 @ failed?
+ beq common_exceptionThrown @ yes, handle the exception
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r3) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+ /*
+ * Class initialization required.
+ *
+ * r0 holds class object
+ */
+.LOP_NEW_INSTANCE_needinit:
+ mov r9, r0 @ save r0
+ bl dvmInitClass @ initialize class
+ cmp r0, #0 @ check boolean result
+ mov r0, r9 @ restore r0
+ bne .LOP_NEW_INSTANCE_initialized @ success, continue
+ b common_exceptionThrown @ failed, deal with init exception
+
+ /*
+ * Resolution required. This is the least-likely path.
+ *
+ * r1 holds BBBB
+ */
+.LOP_NEW_INSTANCE_resolve:
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ mov r2, #0 @ r2<- false
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- resolved ClassObject ptr
+ cmp r0, #0 @ got null?
+ bne .LOP_NEW_INSTANCE_resolved @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+.LstrInstantiationErrorPtr:
+ .word .LstrInstantiationError
+
+/* continuation for OP_NEW_ARRAY */
+
+
+ /*
+ * Resolve class. (This is an uncommon case.)
+ *
+ * r1 holds array length
+ * r2 holds class ref CCCC
+ */
+.LOP_NEW_ARRAY_resolve:
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ mov r9, r1 @ r9<- length (save)
+ mov r1, r2 @ r1<- CCCC
+ mov r2, #0 @ r2<- false
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveClass @ r0<- call(clazz, ref)
+ cmp r0, #0 @ got null?
+ mov r1, r9 @ r1<- length (restore)
+ beq common_exceptionThrown @ yes, handle exception
+ @ fall through to OP_NEW_ARRAY_finish
+
+ /*
+ * Finish allocation.
+ *
+ * r0 holds class
+ * r1 holds array length
+ */
+.LOP_NEW_ARRAY_finish:
+ mov r2, #ALLOC_DONT_TRACK @ don't track in local refs table
+ bl dvmAllocArrayByClass @ r0<- call(clazz, length, flags)
+ cmp r0, #0 @ failed?
+ mov r2, rINST, lsr #8 @ r2<- A+
+ beq common_exceptionThrown @ yes, handle the exception
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ vA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_FILLED_NEW_ARRAY */
+
+ /*
+ * On entry:
+ * r0 holds array class
+ * r10 holds AA or BA
+ */
+.LOP_FILLED_NEW_ARRAY_continue:
+ ldr r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor
+ mov r2, #ALLOC_DONT_TRACK @ r2<- alloc flags
+ ldrb rINST, [r3, #1] @ rINST<- descriptor[1]
+ .if 0
+ mov r1, r10 @ r1<- AA (length)
+ .else
+ mov r1, r10, lsr #4 @ r1<- B (length)
+ .endif
+ cmp rINST, #'I' @ array of ints?
+ cmpne rINST, #'L' @ array of objects?
+ cmpne rINST, #'[' @ array of arrays?
+ mov r9, r1 @ save length in r9
+ bne .LOP_FILLED_NEW_ARRAY_notimpl @ no, not handled yet
+ bl dvmAllocArrayByClass @ r0<- call(arClass, length, flags)
+ cmp r0, #0 @ null return?
+ beq common_exceptionThrown @ alloc failed, handle exception
+
+ FETCH(r1, 2) @ r1<- FEDC or CCCC
+ str r0, [rGLUE, #offGlue_retval] @ retval.l <- new array
+ str rINST, [rGLUE, #offGlue_retval+4] @ retval.h <- type
+ add r0, r0, #offArrayObject_contents @ r0<- newArray->contents
+ subs r9, r9, #1 @ length--, check for neg
+ FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST
+ bmi 2f @ was zero, bail
+
+ @ copy values from registers into the array
+ @ r0=array, r1=CCCC/FEDC, r9=length (from AA or B), r10=AA/BA
+ .if 0
+ add r2, rFP, r1, lsl #2 @ r2<- &fp[CCCC]
+1: ldr r3, [r2], #4 @ r3<- *r2++
+ subs r9, r9, #1 @ count--
+ str r3, [r0], #4 @ *contents++ = vX
+ bpl 1b
+ @ continue at 2
+ .else
+ cmp r9, #4 @ length was initially 5?
+ and r2, r10, #15 @ r2<- A
+ bne 1f @ <= 4 args, branch
+ GET_VREG(r3, r2) @ r3<- vA
+ sub r9, r9, #1 @ count--
+ str r3, [r0, #16] @ contents[4] = vA
+1: and r2, r1, #15 @ r2<- F/E/D/C
+ GET_VREG(r3, r2) @ r3<- vF/vE/vD/vC
+ mov r1, r1, lsr #4 @ r1<- next reg in low 4
+ subs r9, r9, #1 @ count--
+ str r3, [r0], #4 @ *contents++ = vX
+ bpl 1b
+ @ continue at 2
+ .endif
+
+2:
+ ldr r0, [rGLUE, #offGlue_retval] @ r0<- object
+ ldr r1, [rGLUE, #offGlue_retval+4] @ r1<- type
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ cmp r1, #'I' @ Is int array?
+ strneb r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card based on object head
+ GOTO_OPCODE(ip) @ execute it
+
+ /*
+ * Throw an exception indicating that we have not implemented this
+ * mode of filled-new-array.
+ */
+.LOP_FILLED_NEW_ARRAY_notimpl:
+ ldr r0, .L_strInternalError
+ ldr r1, .L_strFilledNewArrayNotImpl
+ bl dvmThrowException
+ b common_exceptionThrown
+
+ .if (!0) @ define in one or the other, not both
+.L_strFilledNewArrayNotImpl:
+ .word .LstrFilledNewArrayNotImpl
+.L_strInternalError:
+ .word .LstrInternalError
+ .endif
+
+/* continuation for OP_FILLED_NEW_ARRAY_RANGE */
+
+ /*
+ * On entry:
+ * r0 holds array class
+ * r10 holds AA or BA
+ */
+.LOP_FILLED_NEW_ARRAY_RANGE_continue:
+ ldr r3, [r0, #offClassObject_descriptor] @ r3<- arrayClass->descriptor
+ mov r2, #ALLOC_DONT_TRACK @ r2<- alloc flags
+ ldrb rINST, [r3, #1] @ rINST<- descriptor[1]
+ .if 1
+ mov r1, r10 @ r1<- AA (length)
+ .else
+ mov r1, r10, lsr #4 @ r1<- B (length)
+ .endif
+ cmp rINST, #'I' @ array of ints?
+ cmpne rINST, #'L' @ array of objects?
+ cmpne rINST, #'[' @ array of arrays?
+ mov r9, r1 @ save length in r9
+ bne .LOP_FILLED_NEW_ARRAY_RANGE_notimpl @ no, not handled yet
+ bl dvmAllocArrayByClass @ r0<- call(arClass, length, flags)
+ cmp r0, #0 @ null return?
+ beq common_exceptionThrown @ alloc failed, handle exception
+
+ FETCH(r1, 2) @ r1<- FEDC or CCCC
+ str r0, [rGLUE, #offGlue_retval] @ retval.l <- new array
+ str rINST, [rGLUE, #offGlue_retval+4] @ retval.h <- type
+ add r0, r0, #offArrayObject_contents @ r0<- newArray->contents
+ subs r9, r9, #1 @ length--, check for neg
+ FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST
+ bmi 2f @ was zero, bail
+
+ @ copy values from registers into the array
+ @ r0=array, r1=CCCC/FEDC, r9=length (from AA or B), r10=AA/BA
+ .if 1
+ add r2, rFP, r1, lsl #2 @ r2<- &fp[CCCC]
+1: ldr r3, [r2], #4 @ r3<- *r2++
+ subs r9, r9, #1 @ count--
+ str r3, [r0], #4 @ *contents++ = vX
+ bpl 1b
+ @ continue at 2
+ .else
+ cmp r9, #4 @ length was initially 5?
+ and r2, r10, #15 @ r2<- A
+ bne 1f @ <= 4 args, branch
+ GET_VREG(r3, r2) @ r3<- vA
+ sub r9, r9, #1 @ count--
+ str r3, [r0, #16] @ contents[4] = vA
+1: and r2, r1, #15 @ r2<- F/E/D/C
+ GET_VREG(r3, r2) @ r3<- vF/vE/vD/vC
+ mov r1, r1, lsr #4 @ r1<- next reg in low 4
+ subs r9, r9, #1 @ count--
+ str r3, [r0], #4 @ *contents++ = vX
+ bpl 1b
+ @ continue at 2
+ .endif
+
+2:
+ ldr r0, [rGLUE, #offGlue_retval] @ r0<- object
+ ldr r1, [rGLUE, #offGlue_retval+4] @ r1<- type
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ GET_INST_OPCODE(ip) @ ip<- opcode from rINST
+ cmp r1, #'I' @ Is int array?
+ strneb r2, [r2, r0, lsr #GC_CARD_SHIFT] @ Mark card based on object head
+ GOTO_OPCODE(ip) @ execute it
+
+ /*
+ * Throw an exception indicating that we have not implemented this
+ * mode of filled-new-array.
+ */
+.LOP_FILLED_NEW_ARRAY_RANGE_notimpl:
+ ldr r0, .L_strInternalError
+ ldr r1, .L_strFilledNewArrayNotImpl
+ bl dvmThrowException
+ b common_exceptionThrown
+
+ .if (!1) @ define in one or the other, not both
+.L_strFilledNewArrayNotImpl:
+ .word .LstrFilledNewArrayNotImpl
+.L_strInternalError:
+ .word .LstrInternalError
+ .endif
+
+/* continuation for OP_CMPL_FLOAT */
+.LOP_CMPL_FLOAT_finish:
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_CMPG_FLOAT */
+.LOP_CMPG_FLOAT_finish:
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_CMPL_DOUBLE */
+.LOP_CMPL_DOUBLE_finish:
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_CMPG_DOUBLE */
+.LOP_CMPG_DOUBLE_finish:
+ SET_VREG(r0, r9) @ vAA<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_CMP_LONG */
+
+.LOP_CMP_LONG_less:
+ mvn r1, #0 @ r1<- -1
+ @ Want to cond code the next mov so we can avoid branch, but don't see it;
+ @ instead, we just replicate the tail end.
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r9) @ vAA<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+.LOP_CMP_LONG_greater:
+ mov r1, #1 @ r1<- 1
+ @ fall through to _finish
+
+.LOP_CMP_LONG_finish:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ SET_VREG(r1, r9) @ vAA<- r1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_AGET_WIDE */
+
+.LOP_AGET_WIDE_finish:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldrd r2, [r0, #offArrayObject_contents] @ r2/r3<- vBB[vCC]
+ add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r2-r3} @ vAA/vAA+1<- r2/r3
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_APUT_WIDE */
+
+.LOP_APUT_WIDE_finish:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldmia r9, {r2-r3} @ r2/r3<- vAA/vAA+1
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ strd r2, [r0, #offArrayObject_contents] @ r2/r3<- vBB[vCC]
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_APUT_OBJECT */
+ /*
+ * On entry:
+ * rINST = vBB (arrayObj)
+ * r9 = vAA (obj)
+ * r10 = offset into array (vBB + vCC * width)
+ */
+.LOP_APUT_OBJECT_finish:
+ cmp r9, #0 @ storing null reference?
+ beq .LOP_APUT_OBJECT_skip_check @ yes, skip type checks
+ ldr r0, [r9, #offObject_clazz] @ r0<- obj->clazz
+ ldr r1, [rINST, #offObject_clazz] @ r1<- arrayObj->clazz
+ bl dvmCanPutArrayElement @ test object type vs. array type
+ cmp r0, #0 @ okay?
+ beq common_errArrayStore @ no
+ mov r1, rINST @ r1<- arrayObj
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldr r2, [rGLUE, #offGlue_cardTable] @ get biased CT base
+ add r10, #offArrayObject_contents @ r0<- pointer to slot
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ str r9, [r10] @ vBB[vCC]<- vAA
+ strb r2, [r2, r1, lsr #GC_CARD_SHIFT] @ mark card using object head
+ GOTO_OPCODE(ip) @ jump to next instruction
+.LOP_APUT_OBJECT_skip_check:
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ str r9, [r10, #offArrayObject_contents] @ vBB[vCC]<- vAA
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_finish:
+ @bl common_squeak0
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ ubfx r2, rINST, #8, #4 @ r2<- A
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_WIDE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_WIDE_finish:
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldrd r0, [r9, r3] @ r0/r1<- obj.field (64-bit align ok)
+ ubfx r2, rINST, #8, #4 @ r2<- A
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ add r3, rFP, r2, lsl #2 @ r3<- &fp[A]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ fp[A]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_OBJECT */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_OBJECT_finish:
+ @bl common_squeak0
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_BOOLEAN */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_BOOLEAN_finish:
+ @bl common_squeak1
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_BYTE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_BYTE_finish:
+ @bl common_squeak2
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_CHAR */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_CHAR_finish:
+ @bl common_squeak3
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_SHORT */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_SHORT_finish:
+ @bl common_squeak4
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ @ no-op @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_finish:
+ @bl common_squeak0
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ ubfx r1, rINST, #8, #4 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT_WIDE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_WIDE_finish:
+ ubfx r2, rINST, #8, #4 @ r2<- A
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ add r2, rFP, r2, lsl #2 @ r3<- &fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldmia r2, {r0-r1} @ r0/r1<- fp[A]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ strd r0, [r9, r3] @ obj.field (64 bits, aligned)<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT_OBJECT */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_OBJECT_finish:
+ @bl common_squeak0
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r0, [r9, r3] @ obj.field (32 bits)<- r0
+ cmp r0, #0 @ stored a null reference?
+ strneb r2, [r2, r9, lsr #GC_CARD_SHIFT] @ mark card if not
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT_BOOLEAN */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_BOOLEAN_finish:
+ @bl common_squeak1
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT_BYTE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_BYTE_finish:
+ @bl common_squeak2
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT_CHAR */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_CHAR_finish:
+ @bl common_squeak3
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT_SHORT */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_SHORT_finish:
+ @bl common_squeak4
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SGET */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SGET_WIDE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ *
+ * Returns StaticField pointer in r0.
+ */
+.LOP_SGET_WIDE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_WIDE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SGET_OBJECT */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_OBJECT_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_OBJECT_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SGET_BOOLEAN */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_BOOLEAN_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_BOOLEAN_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SGET_BYTE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_BYTE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_BYTE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SGET_CHAR */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_CHAR_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_CHAR_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SGET_SHORT */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_SHORT_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_SHORT_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SPUT_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_WIDE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ * r9: &fp[AA]
+ *
+ * Returns StaticField pointer in r2.
+ */
+.LOP_SPUT_WIDE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ mov r2, r0 @ copy to r2
+ bne .LOP_SPUT_WIDE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_OBJECT */
+.LOP_SPUT_OBJECT_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ ldr r9, [r0, #offField_clazz] @ r9<- field->clazz
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ @ no-op @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ cmp r1, #0 @ stored a null object?
+ strneb r2, [r2, r9, lsr #GC_CARD_SHIFT] @ mark card based on obj head
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SPUT_BOOLEAN */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SPUT_BOOLEAN_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_BOOLEAN_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_BYTE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SPUT_BYTE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_BYTE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_CHAR */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SPUT_CHAR_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_CHAR_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_SHORT */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SPUT_SHORT_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_SHORT_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_INVOKE_VIRTUAL */
+
+ /*
+ * At this point:
+ * r0 = resolved base method
+ * r10 = C or CCCC (index of first arg, which is the "this" ptr)
+ */
+.LOP_INVOKE_VIRTUAL_continue:
+ GET_VREG(r1, r10) @ r1<- "this" ptr
+ ldrh r2, [r0, #offMethod_methodIndex] @ r2<- baseMethod->methodIndex
+ cmp r1, #0 @ is "this" null?
+ beq common_errNullObject @ null "this", throw exception
+ ldr r3, [r1, #offObject_clazz] @ r1<- thisPtr->clazz
+ ldr r3, [r3, #offClassObject_vtable] @ r3<- thisPtr->clazz->vtable
+ ldr r0, [r3, r2, lsl #2] @ r3<- vtable[methodIndex]
+ bl common_invokeMethodNoRange @ continue on
+
+/* continuation for OP_INVOKE_SUPER */
+
+ /*
+ * At this point:
+ * r0 = resolved base method
+ * r9 = method->clazz
+ */
+.LOP_INVOKE_SUPER_continue:
+ ldr r1, [r9, #offClassObject_super] @ r1<- method->clazz->super
+ ldrh r2, [r0, #offMethod_methodIndex] @ r2<- baseMethod->methodIndex
+ ldr r3, [r1, #offClassObject_vtableCount] @ r3<- super->vtableCount
+ EXPORT_PC() @ must export for invoke
+ cmp r2, r3 @ compare (methodIndex, vtableCount)
+ bcs .LOP_INVOKE_SUPER_nsm @ method not present in superclass
+ ldr r1, [r1, #offClassObject_vtable] @ r1<- ...clazz->super->vtable
+ ldr r0, [r1, r2, lsl #2] @ r3<- vtable[methodIndex]
+ bl common_invokeMethodNoRange @ continue on
+
+.LOP_INVOKE_SUPER_resolve:
+ mov r0, r9 @ r0<- method->clazz
+ mov r2, #METHOD_VIRTUAL @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ bne .LOP_INVOKE_SUPER_continue @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+ /*
+ * Throw a NoSuchMethodError with the method name as the message.
+ * r0 = resolved base method
+ */
+.LOP_INVOKE_SUPER_nsm:
+ ldr r1, [r0, #offMethod_name] @ r1<- method name
+ b common_errNoSuchMethod
+
+/* continuation for OP_INVOKE_DIRECT */
+
+ /*
+ * On entry:
+ * r1 = reference (BBBB or CCCC)
+ * r10 = "this" register
+ */
+.LOP_INVOKE_DIRECT_resolve:
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ mov r2, #METHOD_DIRECT @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ GET_VREG(r2, r10) @ r2<- "this" ptr (reload)
+ bne .LOP_INVOKE_DIRECT_finish @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+/* continuation for OP_INVOKE_VIRTUAL_RANGE */
+
+ /*
+ * At this point:
+ * r0 = resolved base method
+ * r10 = C or CCCC (index of first arg, which is the "this" ptr)
+ */
+.LOP_INVOKE_VIRTUAL_RANGE_continue:
+ GET_VREG(r1, r10) @ r1<- "this" ptr
+ ldrh r2, [r0, #offMethod_methodIndex] @ r2<- baseMethod->methodIndex
+ cmp r1, #0 @ is "this" null?
+ beq common_errNullObject @ null "this", throw exception
+ ldr r3, [r1, #offObject_clazz] @ r1<- thisPtr->clazz
+ ldr r3, [r3, #offClassObject_vtable] @ r3<- thisPtr->clazz->vtable
+ ldr r0, [r3, r2, lsl #2] @ r3<- vtable[methodIndex]
+ bl common_invokeMethodRange @ continue on
+
+/* continuation for OP_INVOKE_SUPER_RANGE */
+
+ /*
+ * At this point:
+ * r0 = resolved base method
+ * r9 = method->clazz
+ */
+.LOP_INVOKE_SUPER_RANGE_continue:
+ ldr r1, [r9, #offClassObject_super] @ r1<- method->clazz->super
+ ldrh r2, [r0, #offMethod_methodIndex] @ r2<- baseMethod->methodIndex
+ ldr r3, [r1, #offClassObject_vtableCount] @ r3<- super->vtableCount
+ EXPORT_PC() @ must export for invoke
+ cmp r2, r3 @ compare (methodIndex, vtableCount)
+ bcs .LOP_INVOKE_SUPER_RANGE_nsm @ method not present in superclass
+ ldr r1, [r1, #offClassObject_vtable] @ r1<- ...clazz->super->vtable
+ ldr r0, [r1, r2, lsl #2] @ r3<- vtable[methodIndex]
+ bl common_invokeMethodRange @ continue on
+
+.LOP_INVOKE_SUPER_RANGE_resolve:
+ mov r0, r9 @ r0<- method->clazz
+ mov r2, #METHOD_VIRTUAL @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ bne .LOP_INVOKE_SUPER_RANGE_continue @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+ /*
+ * Throw a NoSuchMethodError with the method name as the message.
+ * r0 = resolved base method
+ */
+.LOP_INVOKE_SUPER_RANGE_nsm:
+ ldr r1, [r0, #offMethod_name] @ r1<- method name
+ b common_errNoSuchMethod
+
+/* continuation for OP_INVOKE_DIRECT_RANGE */
+
+ /*
+ * On entry:
+ * r1 = reference (BBBB or CCCC)
+ * r10 = "this" register
+ */
+.LOP_INVOKE_DIRECT_RANGE_resolve:
+ ldr r3, [rGLUE, #offGlue_method] @ r3<- glue->method
+ ldr r0, [r3, #offMethod_clazz] @ r0<- method->clazz
+ mov r2, #METHOD_DIRECT @ resolver method type
+ bl dvmResolveMethod @ r0<- call(clazz, ref, flags)
+ cmp r0, #0 @ got null?
+ GET_VREG(r2, r10) @ r2<- "this" ptr (reload)
+ bne .LOP_INVOKE_DIRECT_RANGE_finish @ no, continue
+ b common_exceptionThrown @ yes, handle exception
+
+/* continuation for OP_FLOAT_TO_LONG */
+/*
+ * Convert the float in r0 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification. The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer. The EABI convert function isn't doing this for us.
+ */
+f2l_doconv:
+ stmfd sp!, {r4, lr}
+ mov r1, #0x5f000000 @ (float)maxlong
+ mov r4, r0
+ bl __aeabi_fcmpge @ is arg >= maxlong?
+ cmp r0, #0 @ nonzero == yes
+ mvnne r0, #0 @ return maxlong (7fffffff)
+ mvnne r1, #0x80000000
+ ldmnefd sp!, {r4, pc}
+
+ mov r0, r4 @ recover arg
+ mov r1, #0xdf000000 @ (float)minlong
+ bl __aeabi_fcmple @ is arg <= minlong?
+ cmp r0, #0 @ nonzero == yes
+ movne r0, #0 @ return minlong (80000000)
+ movne r1, #0x80000000
+ ldmnefd sp!, {r4, pc}
+
+ mov r0, r4 @ recover arg
+ mov r1, r4
+ bl __aeabi_fcmpeq @ is arg == self?
+ cmp r0, #0 @ zero == no
+ moveq r1, #0 @ return zero for NaN
+ ldmeqfd sp!, {r4, pc}
+
+ mov r0, r4 @ recover arg
+ bl __aeabi_f2lz @ convert float to long
+ ldmfd sp!, {r4, pc}
+
+/* continuation for OP_DOUBLE_TO_LONG */
+/*
+ * Convert the double in r0/r1 to a long in r0/r1.
+ *
+ * We have to clip values to long min/max per the specification. The
+ * expected common case is a "reasonable" value that converts directly
+ * to modest integer. The EABI convert function isn't doing this for us.
+ */
+d2l_doconv:
+ stmfd sp!, {r4, r5, lr} @ save regs
+ mov r3, #0x43000000 @ maxlong, as a double (high word)
+ add r3, #0x00e00000 @ 0x43e00000
+ mov r2, #0 @ maxlong, as a double (low word)
+ sub sp, sp, #4 @ align for EABI
+ mov r4, r0 @ save a copy of r0
+ mov r5, r1 @ and r1
+ bl __aeabi_dcmpge @ is arg >= maxlong?
+ cmp r0, #0 @ nonzero == yes
+ mvnne r0, #0 @ return maxlong (7fffffffffffffff)
+ mvnne r1, #0x80000000
+ bne 1f
+
+ mov r0, r4 @ recover arg
+ mov r1, r5
+ mov r3, #0xc3000000 @ minlong, as a double (high word)
+ add r3, #0x00e00000 @ 0xc3e00000
+ mov r2, #0 @ minlong, as a double (low word)
+ bl __aeabi_dcmple @ is arg <= minlong?
+ cmp r0, #0 @ nonzero == yes
+ movne r0, #0 @ return minlong (8000000000000000)
+ movne r1, #0x80000000
+ bne 1f
+
+ mov r0, r4 @ recover arg
+ mov r1, r5
+ mov r2, r4 @ compare against self
+ mov r3, r5
+ bl __aeabi_dcmpeq @ is arg == self?
+ cmp r0, #0 @ zero == no
+ moveq r1, #0 @ return zero for NaN
+ beq 1f
+
+ mov r0, r4 @ recover arg
+ mov r1, r5
+ bl __aeabi_d2lz @ convert double to long
+
+1:
+ add sp, sp, #4
+ ldmfd sp!, {r4, r5, pc}
+
+/* continuation for OP_MUL_LONG */
+
+.LOP_MUL_LONG_finish:
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r0, {r9-r10} @ vAA/vAA+1<- r9/r10
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SHL_LONG */
+
+.LOP_SHL_LONG_finish:
+ mov r0, r0, asl r2 @ r0<- r0 << r2
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SHR_LONG */
+
+.LOP_SHR_LONG_finish:
+ mov r1, r1, asr r2 @ r1<- r1 >> r2
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_USHR_LONG */
+
+.LOP_USHR_LONG_finish:
+ mov r1, r1, lsr r2 @ r1<- r1 >>> r2
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SHL_LONG_2ADDR */
+
+.LOP_SHL_LONG_2ADDR_finish:
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SHR_LONG_2ADDR */
+
+.LOP_SHR_LONG_2ADDR_finish:
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_USHR_LONG_2ADDR */
+
+.LOP_USHR_LONG_2ADDR_finish:
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_VOLATILE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_VOLATILE_finish:
+ @bl common_squeak0
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ SMP_DMB @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT_VOLATILE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_VOLATILE_finish:
+ @bl common_squeak0
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SMP_DMB @ releasing store
+ str r0, [r9, r3] @ obj.field (8/16/32 bits)<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SGET_VOLATILE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_VOLATILE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_VOLATILE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_VOLATILE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SPUT_VOLATILE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SPUT_VOLATILE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_IGET_OBJECT_VOLATILE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_OBJECT_VOLATILE_finish:
+ @bl common_squeak0
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ ldr r0, [r9, r3] @ r0<- obj.field (8/16/32 bits)
+ SMP_DMB @ acquiring load
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SET_VREG(r0, r2) @ fp[A]<- r0
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IGET_WIDE_VOLATILE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IGET_WIDE_VOLATILE_finish:
+ cmp r9, #0 @ check object for null
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ beq common_errNullObject @ object was null
+ .if 1
+ add r0, r9, r3 @ r0<- address of field
+ bl dvmQuasiAtomicRead64 @ r0/r1<- contents of field
+ .else
+ ldrd r0, [r9, r3] @ r0/r1<- obj.field (64-bit align ok)
+ .endif
+ mov r2, rINST, lsr #8 @ r2<- A+
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ and r2, r2, #15 @ r2<- A
+ add r3, rFP, r2, lsl #2 @ r3<- &fp[A]
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ stmia r3, {r0-r1} @ fp[A]<- r0/r1
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_IPUT_WIDE_VOLATILE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_WIDE_VOLATILE_finish:
+ mov r2, rINST, lsr #8 @ r2<- A+
+ cmp r9, #0 @ check object for null
+ and r2, r2, #15 @ r2<- A
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ add r2, rFP, r2, lsl #2 @ r3<- &fp[A]
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ ldmia r2, {r0-r1} @ r0/r1<- fp[A]
+ GET_INST_OPCODE(r10) @ extract opcode from rINST
+ .if 1
+ add r2, r9, r3 @ r2<- target address
+ bl dvmQuasiAtomicSwap64 @ stores r0/r1 into addr r2
+ .else
+ strd r0, [r9, r3] @ obj.field (64 bits, aligned)<- r0/r1
+ .endif
+ GOTO_OPCODE(r10) @ jump to next instruction
+
+/* continuation for OP_SGET_WIDE_VOLATILE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ *
+ * Returns StaticField pointer in r0.
+ */
+.LOP_SGET_WIDE_VOLATILE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_WIDE_VOLATILE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_WIDE_VOLATILE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ * r9: &fp[AA]
+ *
+ * Returns StaticField pointer in r2.
+ */
+.LOP_SPUT_WIDE_VOLATILE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ mov r2, r0 @ copy to r2
+ bne .LOP_SPUT_WIDE_VOLATILE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_EXECUTE_INLINE */
+
+ /*
+ * Extract args, call function.
+ * r0 = #of args (0-4)
+ * r10 = call index
+ * lr = return addr, above [DO NOT bl out of here w/o preserving LR]
+ *
+ * Other ideas:
+ * - Use a jump table from the main piece to jump directly into the
+ * AND/LDR pairs. Costs a data load, saves a branch.
+ * - Have five separate pieces that do the loading, so we can work the
+ * interleave a little better. Increases code size.
+ */
+.LOP_EXECUTE_INLINE_continue:
+ rsb r0, r0, #4 @ r0<- 4-r0
+ FETCH(r9, 2) @ r9<- FEDC
+ add pc, pc, r0, lsl #3 @ computed goto, 2 instrs each
+ bl common_abort @ (skipped due to ARM prefetch)
+4: and ip, r9, #0xf000 @ isolate F
+ ldr r3, [rFP, ip, lsr #10] @ r3<- vF (shift right 12, left 2)
+3: and ip, r9, #0x0f00 @ isolate E
+ ldr r2, [rFP, ip, lsr #6] @ r2<- vE
+2: and ip, r9, #0x00f0 @ isolate D
+ ldr r1, [rFP, ip, lsr #2] @ r1<- vD
+1: and ip, r9, #0x000f @ isolate C
+ ldr r0, [rFP, ip, lsl #2] @ r0<- vC
+0:
+ ldr r9, .LOP_EXECUTE_INLINE_table @ table of InlineOperation
+ LDR_PC "[r9, r10, lsl #4]" @ sizeof=16, "func" is first entry
+ @ (not reached)
+
+.LOP_EXECUTE_INLINE_table:
+ .word gDvmInlineOpsTable
+
+/* continuation for OP_EXECUTE_INLINE_RANGE */
+
+ /*
+ * Extract args, call function.
+ * r0 = #of args (0-4)
+ * r10 = call index
+ * lr = return addr, above [DO NOT bl out of here w/o preserving LR]
+ */
+.LOP_EXECUTE_INLINE_RANGE_continue:
+ rsb r0, r0, #4 @ r0<- 4-r0
+ FETCH(r9, 2) @ r9<- CCCC
+ add pc, pc, r0, lsl #3 @ computed goto, 2 instrs each
+ bl common_abort @ (skipped due to ARM prefetch)
+4: add ip, r9, #3 @ base+3
+ GET_VREG(r3, ip) @ r3<- vBase[3]
+3: add ip, r9, #2 @ base+2
+ GET_VREG(r2, ip) @ r2<- vBase[2]
+2: add ip, r9, #1 @ base+1
+ GET_VREG(r1, ip) @ r1<- vBase[1]
+1: add ip, r9, #0 @ (nop)
+ GET_VREG(r0, ip) @ r0<- vBase[0]
+0:
+ ldr r9, .LOP_EXECUTE_INLINE_RANGE_table @ table of InlineOperation
+ LDR_PC "[r9, r10, lsl #4]" @ sizeof=16, "func" is first entry
+ @ (not reached)
+
+.LOP_EXECUTE_INLINE_RANGE_table:
+ .word gDvmInlineOpsTable
+
+/* continuation for OP_IPUT_OBJECT_VOLATILE */
+
+ /*
+ * Currently:
+ * r0 holds resolved field
+ * r9 holds object
+ */
+.LOP_IPUT_OBJECT_VOLATILE_finish:
+ @bl common_squeak0
+ mov r1, rINST, lsr #8 @ r1<- A+
+ ldr r3, [r0, #offInstField_byteOffset] @ r3<- byte offset of field
+ and r1, r1, #15 @ r1<- A
+ cmp r9, #0 @ check object for null
+ GET_VREG(r0, r1) @ r0<- fp[A]
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ beq common_errNullObject @ object was null
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SMP_DMB @ releasing store
+ str r0, [r9, r3] @ obj.field (32 bits)<- r0
+ cmp r0, #0 @ stored a null reference?
+ strneb r2, [r2, r9, lsr #GC_CARD_SHIFT] @ mark card if not
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/* continuation for OP_SGET_OBJECT_VOLATILE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * r1: BBBB field ref
+ */
+.LOP_SGET_OBJECT_VOLATILE_resolve:
+ ldr r2, [rGLUE, #offGlue_method] @ r2<- current method
+ EXPORT_PC() @ resolve() could throw, so export now
+ ldr r0, [r2, #offMethod_clazz] @ r0<- method->clazz
+ bl dvmResolveStaticField @ r0<- resolved StaticField ptr
+ cmp r0, #0 @ success?
+ bne .LOP_SGET_OBJECT_VOLATILE_finish @ yes, finish
+ b common_exceptionThrown @ no, handle exception
+
+/* continuation for OP_SPUT_OBJECT_VOLATILE */
+.LOP_SPUT_OBJECT_VOLATILE_finish: @ field ptr in r0
+ mov r2, rINST, lsr #8 @ r2<- AA
+ FETCH_ADVANCE_INST(2) @ advance rPC, load rINST
+ GET_VREG(r1, r2) @ r1<- fp[AA]
+ ldr r2, [rGLUE, #offGlue_cardTable] @ r2<- card table base
+ ldr r9, [r0, #offField_clazz] @ r9<- field->clazz
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ SMP_DMB @ releasing store
+ str r1, [r0, #offStaticField_value] @ field<- vAA
+ cmp r1, #0 @ stored a null object?
+ strneb r2, [r2, r9, lsr #GC_CARD_SHIFT] @ mark card based on obj head
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+ .size dvmAsmSisterStart, .-dvmAsmSisterStart
+ .global dvmAsmSisterEnd
+dvmAsmSisterEnd:
+
+/* File: armv5te/footer.S */
+
+/*
+ * ===========================================================================
+ * Common subroutines and data
+ * ===========================================================================
+ */
+
+
+
+ .text
+ .align 2
+
+#if defined(WITH_JIT)
+#if defined(WITH_SELF_VERIFICATION)
+ .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r2,#kSVSPunt @ r2<- interpreter entry point
+ mov r3, #0
+ str r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+ str lr,[rGLUE,#offGlue_jitResumeNPC]
+ str r1,[rGLUE,#offGlue_jitResumeDPC]
+ mov r2,#kSVSSingleStep @ r2<- interpreter entry point
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r0,rPC @ pass our target PC
+ mov r2,#kSVSNoProfile @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [r10, #offThread_inJitCodeCache] @ back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r0,rPC @ pass our target PC
+ mov r2,#kSVSTraceSelect @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ ldr r0,[lr, #-1] @ pass our target PC
+ mov r2,#kSVSTraceSelect @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpBackwardBranch
+dvmJitToInterpBackwardBranch:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ ldr r0,[lr, #-1] @ pass our target PC
+ mov r2,#kSVSBackwardBranch @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ ldr r0,[lr, #-1] @ pass our target PC
+ mov r2,#kSVSNormal @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+
+ .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r0,rPC @ pass our target PC
+ mov r2,#kSVSNoChain @ r2<- interpreter entry point
+ mov r3, #0 @ 0 means !inJitCodeCache
+ str r3, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ b jitSVShadowRunEnd @ doesn't return
+#else
+/*
+ * Return from the translation cache to the interpreter when the compiler is
+ * having issues translating/executing a Dalvik instruction. We have to skip
+ * the code cache lookup otherwise it is possible to indefinitely bouce
+ * between the interpreter and the code cache if the instruction that fails
+ * to be compiled happens to be at a trace start.
+ */
+ .global dvmJitToInterpPunt
+dvmJitToInterpPunt:
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov rPC, r0
+#if defined(WITH_JIT_TUNING)
+ mov r0,lr
+ bl dvmBumpPunt;
+#endif
+ EXPORT_PC()
+ mov r0, #0
+ str r0, [r10, #offThread_inJitCodeCache] @ Back to the interp land
+ adrl rIBASE, dvmAsmInstructionStart
+ FETCH_INST()
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+
+/*
+ * Return to the interpreter to handle a single instruction.
+ * On entry:
+ * r0 <= PC
+ * r1 <= PC of resume instruction
+ * lr <= resume point in translation
+ */
+ .global dvmJitToInterpSingleStep
+dvmJitToInterpSingleStep:
+ str lr,[rGLUE,#offGlue_jitResumeNPC]
+ str r1,[rGLUE,#offGlue_jitResumeDPC]
+ mov r1,#kInterpEntryInstr
+ @ enum is 4 byte in aapcs-EABI
+ str r1, [rGLUE, #offGlue_entryPoint]
+ mov rPC,r0
+ EXPORT_PC()
+
+ adrl rIBASE, dvmAsmInstructionStart
+ mov r2,#kJitSingleStep @ Ask for single step and then revert
+ str r2,[rGLUE,#offGlue_jitState]
+ mov r1,#1 @ set changeInterp to bail to debug interp
+ b common_gotoBail
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target. Commonly used for callees.
+ */
+ .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+#if defined(WITH_JIT_TUNING)
+ bl dvmBumpNoChain
+#endif
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r0,rPC
+ bl dvmJitGetCodeAddr @ Is there a translation?
+ str r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0 @ !0 means translation exists
+ bxne r0 @ continue native execution if so
+ b 2f @ branch over to use the interpreter
+
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target. Commonly used following
+ * invokes.
+ */
+ .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
+ ldr rPC,[lr, #-1] @ get our target PC
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ add rINST,lr,#-5 @ save start of chain branch
+ add rINST, #-4 @ .. which is 9 bytes back
+ mov r0,rPC
+ bl dvmJitGetCodeAddr @ Is there a translation?
+ str r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ cmp r0,#0
+ beq 2f
+ mov r1,rINST
+ bl dvmJitChain @ r0<- dvmJitChain(codeAddr,chainAddr)
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0 @ successful chain?
+ bxne r0 @ continue native execution
+ b toInterpreter @ didn't chain - resume with interpreter
+
+/* No translation, so request one if profiling isn't disabled*/
+2:
+ adrl rIBASE, dvmAsmInstructionStart
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_INST()
+ cmp r0, #0
+ movne r2,#kJitTSelectRequestHot @ ask for trace selection
+ bne common_selectTrace
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+
+/*
+ * Return from the translation cache to the interpreter.
+ * The return was done with a BLX from thumb mode, and
+ * the following 32-bit word contains the target rPC value.
+ * Note that lr (r14) will have its low-order bit set to denote
+ * its thumb-mode origin.
+ *
+ * We'll need to stash our lr origin away, recover the new
+ * target and then check to see if there is a translation available
+ * for our new target. If so, we do a translation chain and
+ * go back to native execution. Otherwise, it's back to the
+ * interpreter (after treating this entry as a potential
+ * trace start).
+ */
+ .global dvmJitToInterpNormal
+dvmJitToInterpNormal:
+ ldr rPC,[lr, #-1] @ get our target PC
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ add rINST,lr,#-5 @ save start of chain branch
+ add rINST,#-4 @ .. which is 9 bytes back
+#if defined(WITH_JIT_TUNING)
+ bl dvmBumpNormal
+#endif
+ mov r0,rPC
+ bl dvmJitGetCodeAddr @ Is there a translation?
+ str r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ cmp r0,#0
+ beq toInterpreter @ go if not, otherwise do chain
+ mov r1,rINST
+ bl dvmJitChain @ r0<- dvmJitChain(codeAddr,chainAddr)
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0 @ successful chain?
+ bxne r0 @ continue native execution
+ b toInterpreter @ didn't chain - resume with interpreter
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+ .global dvmJitToInterpNoChainNoProfile
+dvmJitToInterpNoChainNoProfile:
+#if defined(WITH_JIT_TUNING)
+ bl dvmBumpNoChain
+#endif
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r0,rPC
+ bl dvmJitGetCodeAddr @ Is there a translation?
+ str r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0
+ bxne r0 @ continue native execution if so
+ EXPORT_PC()
+ adrl rIBASE, dvmAsmInstructionStart
+ FETCH_INST()
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/*
+ * Return from the translation cache to the interpreter to do method invocation.
+ * Check if translation exists for the callee, but don't chain to it.
+ */
+ .global dvmJitToInterpNoChain
+dvmJitToInterpNoChain:
+#if defined(WITH_JIT_TUNING)
+ bl dvmBumpNoChain
+#endif
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ mov r0,rPC
+ bl dvmJitGetCodeAddr @ Is there a translation?
+ str r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0
+ bxne r0 @ continue native execution if so
+#endif
+
+/*
+ * No translation, restore interpreter regs and start interpreting.
+ * rGLUE & rFP were preserved in the translated code, and rPC has
+ * already been restored by the time we get here. We'll need to set
+ * up rIBASE & rINST, and load the address of the JitTable into r0.
+ */
+toInterpreter:
+ EXPORT_PC()
+ adrl rIBASE, dvmAsmInstructionStart
+ FETCH_INST()
+ GET_JIT_PROF_TABLE(r0)
+ @ NOTE: intended fallthrough
+
+/*
+ * Common code to update potential trace start counter, and initiate
+ * a trace-build if appropriate. On entry, rPC should point to the
+ * next instruction to execute, and rINST should be already loaded with
+ * the next opcode word, and r0 holds a pointer to the jit profile
+ * table (pJitProfTable).
+ */
+common_testUpdateProfile:
+ cmp r0,#0
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE_IFEQ(ip) @ if not profiling, fallthrough otherwise */
+
+common_updateProfile:
+ eor r3,rPC,rPC,lsr #12 @ cheap, but fast hash function
+ lsl r3,r3,#(32 - JIT_PROF_SIZE_LOG_2) @ shift out excess bits
+ ldrb r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ get counter
+ GET_INST_OPCODE(ip)
+ subs r1,r1,#1 @ decrement counter
+ strb r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ and store it
+ GOTO_OPCODE_IFNE(ip) @ if not threshold, fallthrough otherwise */
+
+/*
+ * Here, we switch to the debug interpreter to request
+ * trace selection. First, though, check to see if there
+ * is already a native translation in place (and, if so,
+ * jump to it now).
+ */
+ GET_JIT_THRESHOLD(r1)
+ ldr r10, [rGLUE, #offGlue_self] @ callee saved r10 <- glue->self
+ strb r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ reset counter
+ EXPORT_PC()
+ mov r0,rPC
+ bl dvmJitGetCodeAddr @ r0<- dvmJitGetCodeAddr(rPC)
+ str r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+ mov r1, rPC @ arg1 of translation may need this
+ mov lr, #0 @ in case target is HANDLER_INTERPRET
+ cmp r0,#0
+#if !defined(WITH_SELF_VERIFICATION)
+ bxne r0 @ jump to the translation
+ mov r2,#kJitTSelectRequest @ ask for trace selection
+ @ fall-through to common_selectTrace
+#else
+ moveq r2,#kJitTSelectRequest @ ask for trace selection
+ beq common_selectTrace
+ /*
+ * At this point, we have a target translation. However, if
+ * that translation is actually the interpret-only pseudo-translation
+ * we want to treat it the same as no translation.
+ */
+ mov r10, r0 @ save target
+ bl dvmCompilerGetInterpretTemplate
+ cmp r0, r10 @ special case?
+ bne jitSVShadowRunStart @ set up self verification shadow space
+ @ Need to clear the inJitCodeCache flag
+ ldr r10, [rGLUE, #offGlue_self] @ r10 <- glue->self
+ mov r3, #0 @ 0 means not in the JIT code cache
+ str r3, [r10, #offThread_inJitCodeCache] @ back to the interp land
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+ /* no return */
+#endif
+
+/*
+ * On entry:
+ * r2 is jit state, e.g. kJitTSelectRequest or kJitTSelectRequestHot
+ */
+common_selectTrace:
+ str r2,[rGLUE,#offGlue_jitState]
+ mov r2,#kInterpEntryInstr @ normal entry reason
+ str r2,[rGLUE,#offGlue_entryPoint]
+ mov r1,#1 @ set changeInterp
+ b common_gotoBail
+
+#if defined(WITH_SELF_VERIFICATION)
+/*
+ * Save PC and registers to shadow memory for self verification mode
+ * before jumping to native translation.
+ * On entry:
+ * rPC, rFP, rGLUE: the values that they should contain
+ * r10: the address of the target translation.
+ */
+jitSVShadowRunStart:
+ mov r0,rPC @ r0<- program counter
+ mov r1,rFP @ r1<- frame pointer
+ mov r2,rGLUE @ r2<- InterpState pointer
+ mov r3,r10 @ r3<- target translation
+ bl dvmSelfVerificationSaveState @ save registers to shadow space
+ ldr rFP,[r0,#offShadowSpace_shadowFP] @ rFP<- fp in shadow space
+ add rGLUE,r0,#offShadowSpace_interpState @ rGLUE<- rGLUE in shadow space
+ bx r10 @ jump to the translation
+
+/*
+ * Restore PC, registers, and interpState to original values
+ * before jumping back to the interpreter.
+ */
+jitSVShadowRunEnd:
+ mov r1,rFP @ pass ending fp
+ bl dvmSelfVerificationRestoreState @ restore pc and fp values
+ ldr rPC,[r0,#offShadowSpace_startPC] @ restore PC
+ ldr rFP,[r0,#offShadowSpace_fp] @ restore FP
+ ldr rGLUE,[r0,#offShadowSpace_glue] @ restore InterpState
+ ldr r1,[r0,#offShadowSpace_svState] @ get self verification state
+ cmp r1,#0 @ check for punt condition
+ beq 1f
+ mov r2,#kJitSelfVerification @ ask for self verification
+ str r2,[rGLUE,#offGlue_jitState]
+ mov r2,#kInterpEntryInstr @ normal entry reason
+ str r2,[rGLUE,#offGlue_entryPoint]
+ mov r1,#1 @ set changeInterp
+ b common_gotoBail
+
+1: @ exit to interpreter without check
+ EXPORT_PC()
+ adrl rIBASE, dvmAsmInstructionStart
+ FETCH_INST()
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+#endif
+
+#endif
+
+/*
+ * Common code when a backward branch is taken.
+ *
+ * TODO: we could avoid a branch by just setting r0 and falling through
+ * into the common_periodicChecks code, and having a test on r0 at the
+ * end determine if we should return to the caller or update & branch to
+ * the next instr.
+ *
+ * On entry:
+ * r9 is PC adjustment *in bytes*
+ */
+common_backwardBranch:
+ mov r0, #kInterpEntryInstr
+ bl common_periodicChecks
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ cmp r0,#0
+ bne common_updateProfile
+ GET_INST_OPCODE(ip)
+ GOTO_OPCODE(ip)
+#else
+ FETCH_ADVANCE_INST_RB(r9) @ update rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+
+/*
+ * Need to see if the thread needs to be suspended or debugger/profiler
+ * activity has begun. If so, we suspend the thread or side-exit to
+ * the debug interpreter as appropriate.
+ *
+ * The common case is no activity on any of these, so we want to figure
+ * that out quickly. If something is up, we can then sort out what.
+ *
+ * We want to be fast if the VM was built without debugger or profiler
+ * support, but we also need to recognize that the system is usually
+ * shipped with both of these enabled.
+ *
+ * TODO: reduce this so we're just checking a single location.
+ *
+ * On entry:
+ * r0 is reentry type, e.g. kInterpEntryInstr (for debugger/profiling)
+ * r9 is trampoline PC adjustment *in bytes*
+ */
+common_periodicChecks:
+ ldr r3, [rGLUE, #offGlue_pSelfSuspendCount] @ r3<- &suspendCount
+
+ ldr r1, [rGLUE, #offGlue_pDebuggerActive] @ r1<- &debuggerActive
+ ldr r2, [rGLUE, #offGlue_pActiveProfilers] @ r2<- &activeProfilers
+
+ ldr ip, [r3] @ ip<- suspendCount (int)
+
+ cmp r1, #0 @ debugger enabled?
+ ldrneb r1, [r1] @ yes, r1<- debuggerActive (boolean)
+ ldr r2, [r2] @ r2<- activeProfilers (int)
+ orrne ip, ip, r1 @ ip<- suspendCount | debuggerActive
+ orrs ip, ip, r2 @ ip<- suspend|debugger|profiler; set Z
+
+ bxeq lr @ all zero, return
+
+ /*
+ * One or more interesting events have happened. Figure out what.
+ *
+ * If debugging or profiling are compiled in, we need to disambiguate.
+ *
+ * r0 still holds the reentry type.
+ */
+ ldr ip, [r3] @ ip<- suspendCount (int)
+ cmp ip, #0 @ want suspend?
+ beq 1f @ no, must be debugger/profiler
+
+ stmfd sp!, {r0, lr} @ preserve r0 and lr
+#if defined(WITH_JIT)
+ /*
+ * Refresh the Jit's cached copy of profile table pointer. This pointer
+ * doubles as the Jit's on/off switch.
+ */
+ ldr r3, [rGLUE, #offGlue_ppJitProfTable] @ r3<-&gDvmJit.pJitProfTable
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ ldr r3, [r3] @ r3 <- pJitProfTable
+ EXPORT_PC() @ need for precise GC
+ str r3, [rGLUE, #offGlue_pJitProfTable] @ refresh Jit's on/off switch
+#else
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
+ EXPORT_PC() @ need for precise GC
+#endif
+ bl dvmCheckSuspendPending @ do full check, suspend if necessary
+ ldmfd sp!, {r0, lr} @ restore r0 and lr
+
+ /*
+ * Reload the debugger/profiler enable flags. We're checking to see
+ * if either of these got set while we were suspended.
+ *
+ * We can't really avoid the #ifdefs here, because the fields don't
+ * exist when the feature is disabled.
+ */
+ ldr r1, [rGLUE, #offGlue_pDebuggerActive] @ r1<- &debuggerActive
+ cmp r1, #0 @ debugger enabled?
+ ldrneb r1, [r1] @ yes, r1<- debuggerActive (boolean)
+ ldr r2, [rGLUE, #offGlue_pActiveProfilers] @ r2<- &activeProfilers
+ ldr r2, [r2] @ r2<- activeProfilers (int)
+
+ orrs r1, r1, r2
+ beq 2f
+
+1: @ debugger/profiler enabled, bail out; glue->entryPoint was set above
+ str r0, [rGLUE, #offGlue_entryPoint] @ store r0, need for debug/prof
+ add rPC, rPC, r9 @ update rPC
+ mov r1, #1 @ "want switch" = true
+ b common_gotoBail @ side exit
+
+2:
+ bx lr @ nothing to do, return
+
+
+/*
+ * The equivalent of "goto bail", this calls through the "bail handler".
+ *
+ * State registers will be saved to the "glue" area before bailing.
+ *
+ * On entry:
+ * r1 is "bool changeInterp", indicating if we want to switch to the
+ * other interpreter or just bail all the way out
+ */
+common_gotoBail:
+ SAVE_PC_FP_TO_GLUE() @ export state to "glue"
+ mov r0, rGLUE @ r0<- glue ptr
+ b dvmMterpStdBail @ call(glue, changeInterp)
+
+ @add r1, r1, #1 @ using (boolean+1)
+ @add r0, rGLUE, #offGlue_jmpBuf @ r0<- &glue->jmpBuf
+ @bl _longjmp @ does not return
+ @bl common_abort
+
+
+/*
+ * Common code for method invocation with range.
+ *
+ * On entry:
+ * r0 is "Method* methodToCall", the method we're trying to call
+ */
+common_invokeMethodRange:
+.LinvokeNewRange:
+ @ prepare to copy args to "outs" area of current frame
+ movs r2, rINST, lsr #8 @ r2<- AA (arg count) -- test for zero
+ SAVEAREA_FROM_FP(r10, rFP) @ r10<- stack save area
+ beq .LinvokeArgsDone @ if no args, skip the rest
+ FETCH(r1, 2) @ r1<- CCCC
+
+ @ r0=methodToCall, r1=CCCC, r2=count, r10=outs
+ @ (very few methods have > 10 args; could unroll for common cases)
+ add r3, rFP, r1, lsl #2 @ r3<- &fp[CCCC]
+ sub r10, r10, r2, lsl #2 @ r10<- "outs" area, for call args
+ ldrh r9, [r0, #offMethod_registersSize] @ r9<- methodToCall->regsSize
+1: ldr r1, [r3], #4 @ val = *fp++
+ subs r2, r2, #1 @ count--
+ str r1, [r10], #4 @ *outs++ = val
+ bne 1b @ ...while count != 0
+ ldrh r3, [r0, #offMethod_outsSize] @ r3<- methodToCall->outsSize
+ b .LinvokeArgsDone
+
+/*
+ * Common code for method invocation without range.
+ *
+ * On entry:
+ * r0 is "Method* methodToCall", the method we're trying to call
+ */
+common_invokeMethodNoRange:
+.LinvokeNewNoRange:
+ @ prepare to copy args to "outs" area of current frame
+ movs r2, rINST, lsr #12 @ r2<- B (arg count) -- test for zero
+ SAVEAREA_FROM_FP(r10, rFP) @ r10<- stack save area
+ FETCH(r1, 2) @ r1<- GFED (load here to hide latency)
+ ldrh r9, [r0, #offMethod_registersSize] @ r9<- methodToCall->regsSize
+ ldrh r3, [r0, #offMethod_outsSize] @ r3<- methodToCall->outsSize
+ beq .LinvokeArgsDone
+
+ @ r0=methodToCall, r1=GFED, r3=outSize, r2=count, r9=regSize, r10=outs
+.LinvokeNonRange:
+ rsb r2, r2, #5 @ r2<- 5-r2
+ add pc, pc, r2, lsl #4 @ computed goto, 4 instrs each
+ bl common_abort @ (skipped due to ARM prefetch)
+5: and ip, rINST, #0x0f00 @ isolate A
+ ldr r2, [rFP, ip, lsr #6] @ r2<- vA (shift right 8, left 2)
+ mov r0, r0 @ nop
+ str r2, [r10, #-4]! @ *--outs = vA
+4: and ip, r1, #0xf000 @ isolate G
+ ldr r2, [rFP, ip, lsr #10] @ r2<- vG (shift right 12, left 2)
+ mov r0, r0 @ nop
+ str r2, [r10, #-4]! @ *--outs = vG
+3: and ip, r1, #0x0f00 @ isolate F
+ ldr r2, [rFP, ip, lsr #6] @ r2<- vF
+ mov r0, r0 @ nop
+ str r2, [r10, #-4]! @ *--outs = vF
+2: and ip, r1, #0x00f0 @ isolate E
+ ldr r2, [rFP, ip, lsr #2] @ r2<- vE
+ mov r0, r0 @ nop
+ str r2, [r10, #-4]! @ *--outs = vE
+1: and ip, r1, #0x000f @ isolate D
+ ldr r2, [rFP, ip, lsl #2] @ r2<- vD
+ mov r0, r0 @ nop
+ str r2, [r10, #-4]! @ *--outs = vD
+0: @ fall through to .LinvokeArgsDone
+
+.LinvokeArgsDone: @ r0=methodToCall, r3=outSize, r9=regSize
+ ldr r2, [r0, #offMethod_insns] @ r2<- method->insns
+ ldr rINST, [r0, #offMethod_clazz] @ rINST<- method->clazz
+ @ find space for the new stack frame, check for overflow
+ SAVEAREA_FROM_FP(r1, rFP) @ r1<- stack save area
+ sub r1, r1, r9, lsl #2 @ r1<- newFp (old savearea - regsSize)
+ SAVEAREA_FROM_FP(r10, r1) @ r10<- newSaveArea
+@ bl common_dumpRegs
+ ldr r9, [rGLUE, #offGlue_interpStackEnd] @ r9<- interpStackEnd
+ sub r3, r10, r3, lsl #2 @ r3<- bottom (newsave - outsSize)
+ cmp r3, r9 @ bottom < interpStackEnd?
+ ldr r3, [r0, #offMethod_accessFlags] @ r3<- methodToCall->accessFlags
+ blo .LstackOverflow @ yes, this frame will overflow stack
+
+ @ set up newSaveArea
+#ifdef EASY_GDB
+ SAVEAREA_FROM_FP(ip, rFP) @ ip<- stack save area
+ str ip, [r10, #offStackSaveArea_prevSave]
+#endif
+ str rFP, [r10, #offStackSaveArea_prevFrame]
+ str rPC, [r10, #offStackSaveArea_savedPc]
+#if defined(WITH_JIT)
+ mov r9, #0
+ str r9, [r10, #offStackSaveArea_returnAddr]
+#endif
+ str r0, [r10, #offStackSaveArea_method]
+ tst r3, #ACC_NATIVE
+ bne .LinvokeNative
+
+ /*
+ stmfd sp!, {r0-r3}
+ bl common_printNewline
+ mov r0, rFP
+ mov r1, #0
+ bl dvmDumpFp
+ ldmfd sp!, {r0-r3}
+ stmfd sp!, {r0-r3}
+ mov r0, r1
+ mov r1, r10
+ bl dvmDumpFp
+ bl common_printNewline
+ ldmfd sp!, {r0-r3}
+ */
+
+ ldrh r9, [r2] @ r9 <- load INST from new PC
+ ldr r3, [rINST, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
+ mov rPC, r2 @ publish new rPC
+ ldr r2, [rGLUE, #offGlue_self] @ r2<- glue->self
+
+ @ Update "glue" values for the new method
+ @ r0=methodToCall, r1=newFp, r2=self, r3=newMethodClass, r9=newINST
+ str r0, [rGLUE, #offGlue_method] @ glue->method = methodToCall
+ str r3, [rGLUE, #offGlue_methodClassDex] @ glue->methodClassDex = ...
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE(r0)
+ mov rFP, r1 @ fp = newFp
+ GET_PREFETCHED_OPCODE(ip, r9) @ extract prefetched opcode from r9
+ mov rINST, r9 @ publish new rINST
+ str r1, [r2, #offThread_curFrame] @ self->curFrame = newFp
+ cmp r0,#0
+ bne common_updateProfile
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ mov rFP, r1 @ fp = newFp
+ GET_PREFETCHED_OPCODE(ip, r9) @ extract prefetched opcode from r9
+ mov rINST, r9 @ publish new rINST
+ str r1, [r2, #offThread_curFrame] @ self->curFrame = newFp
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+.LinvokeNative:
+ @ Prep for the native call
+ @ r0=methodToCall, r1=newFp, r10=newSaveArea
+ ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
+ ldr r9, [r3, #offThread_jniLocal_topCookie] @ r9<- thread->localRef->...
+ str r1, [r3, #offThread_curFrame] @ self->curFrame = newFp
+ str r9, [r10, #offStackSaveArea_localRefCookie] @newFp->localRefCookie=top
+ mov r9, r3 @ r9<- glue->self (preserve)
+
+ mov r2, r0 @ r2<- methodToCall
+ mov r0, r1 @ r0<- newFp (points to args)
+ add r1, rGLUE, #offGlue_retval @ r1<- &retval
+
+#ifdef ASSIST_DEBUGGER
+ /* insert fake function header to help gdb find the stack frame */
+ b .Lskip
+ .type dalvik_mterp, %function
+dalvik_mterp:
+ .fnstart
+ MTERP_ENTRY1
+ MTERP_ENTRY2
+.Lskip:
+#endif
+
+ @mov lr, pc @ set return addr
+ @ldr pc, [r2, #offMethod_nativeFunc] @ pc<- methodToCall->nativeFunc
+ LDR_PC_LR "[r2, #offMethod_nativeFunc]"
+
+#if defined(WITH_JIT)
+ ldr r3, [rGLUE, #offGlue_ppJitProfTable] @ Refresh Jit's on/off status
+#endif
+
+ @ native return; r9=self, r10=newSaveArea
+ @ equivalent to dvmPopJniLocals
+ ldr r0, [r10, #offStackSaveArea_localRefCookie] @ r0<- saved top
+ ldr r1, [r9, #offThread_exception] @ check for exception
+#if defined(WITH_JIT)
+ ldr r3, [r3] @ r3 <- gDvmJit.pProfTable
+#endif
+ str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp
+ cmp r1, #0 @ null?
+ str r0, [r9, #offThread_jniLocal_topCookie] @ new top <- old top
+#if defined(WITH_JIT)
+ str r3, [rGLUE, #offGlue_pJitProfTable] @ refresh cached on/off switch
+#endif
+ bne common_exceptionThrown @ no, handle exception
+
+ FETCH_ADVANCE_INST(3) @ advance rPC, load rINST
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+.LstackOverflow: @ r0=methodToCall
+ mov r1, r0 @ r1<- methodToCall
+ ldr r0, [rGLUE, #offGlue_self] @ r0<- self
+ bl dvmHandleStackOverflow
+ b common_exceptionThrown
+#ifdef ASSIST_DEBUGGER
+ .fnend
+#endif
+
+
+ /*
+ * Common code for method invocation, calling through "glue code".
+ *
+ * TODO: now that we have range and non-range invoke handlers, this
+ * needs to be split into two. Maybe just create entry points
+ * that set r9 and jump here?
+ *
+ * On entry:
+ * r0 is "Method* methodToCall", the method we're trying to call
+ * r9 is "bool methodCallRange", indicating if this is a /range variant
+ */
+ .if 0
+.LinvokeOld:
+ sub sp, sp, #8 @ space for args + pad
+ FETCH(ip, 2) @ ip<- FEDC or CCCC
+ mov r2, r0 @ A2<- methodToCall
+ mov r0, rGLUE @ A0<- glue
+ SAVE_PC_FP_TO_GLUE() @ export state to "glue"
+ mov r1, r9 @ A1<- methodCallRange
+ mov r3, rINST, lsr #8 @ A3<- AA
+ str ip, [sp, #0] @ A4<- ip
+ bl dvmMterp_invokeMethod @ call the C invokeMethod
+ add sp, sp, #8 @ remove arg area
+ b common_resumeAfterGlueCall @ continue to next instruction
+ .endif
+
+
+
+/*
+ * Common code for handling a return instruction.
+ *
+ * This does not return.
+ */
+common_returnFromMethod:
+.LreturnNew:
+ mov r0, #kInterpEntryReturn
+ mov r9, #0
+ bl common_periodicChecks
+
+ SAVEAREA_FROM_FP(r0, rFP) @ r0<- saveArea (old)
+ ldr rFP, [r0, #offStackSaveArea_prevFrame] @ fp = saveArea->prevFrame
+ ldr r9, [r0, #offStackSaveArea_savedPc] @ r9 = saveArea->savedPc
+ ldr r2, [rFP, #(offStackSaveArea_method - sizeofStackSaveArea)]
+ @ r2<- method we're returning to
+ ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self
+ cmp r2, #0 @ is this a break frame?
+ ldrne r10, [r2, #offMethod_clazz] @ r10<- method->clazz
+ mov r1, #0 @ "want switch" = false
+ beq common_gotoBail @ break frame, bail out completely
+
+ PREFETCH_ADVANCE_INST(rINST, r9, 3) @ advance r9, update new rINST
+ str r2, [rGLUE, #offGlue_method]@ glue->method = newSave->method
+ ldr r1, [r10, #offClassObject_pDvmDex] @ r1<- method->clazz->pDvmDex
+ str rFP, [r3, #offThread_curFrame] @ self->curFrame = fp
+#if defined(WITH_JIT)
+ ldr r10, [r0, #offStackSaveArea_returnAddr] @ r10 = saveArea->returnAddr
+ mov rPC, r9 @ publish new rPC
+ str r1, [rGLUE, #offGlue_methodClassDex]
+ str r10, [r3, #offThread_inJitCodeCache] @ may return to JIT'ed land
+ cmp r10, #0 @ caller is compiled code
+ blxne r10
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+#else
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ mov rPC, r9 @ publish new rPC
+ str r1, [rGLUE, #offGlue_methodClassDex]
+ GOTO_OPCODE(ip) @ jump to next instruction
+#endif
+
+ /*
+ * Return handling, calls through "glue code".
+ */
+ .if 0
+.LreturnOld:
+ SAVE_PC_FP_TO_GLUE() @ export state
+ mov r0, rGLUE @ arg to function
+ bl dvmMterp_returnFromMethod
+ b common_resumeAfterGlueCall
+ .endif
+
+
+/*
+ * Somebody has thrown an exception. Handle it.
+ *
+ * If the exception processing code returns to us (instead of falling
+ * out of the interpreter), continue with whatever the next instruction
+ * now happens to be.
+ *
+ * This does not return.
+ */
+ .global dvmMterpCommonExceptionThrown
+dvmMterpCommonExceptionThrown:
+common_exceptionThrown:
+.LexceptionNew:
+ mov r0, #kInterpEntryThrow
+ mov r9, #0
+ bl common_periodicChecks
+
+ ldr r10, [rGLUE, #offGlue_self] @ r10<- glue->self
+ ldr r9, [r10, #offThread_exception] @ r9<- self->exception
+ mov r1, r10 @ r1<- self
+ mov r0, r9 @ r0<- exception
+ bl dvmAddTrackedAlloc @ don't let the exception be GCed
+ mov r3, #0 @ r3<- NULL
+ str r3, [r10, #offThread_exception] @ self->exception = NULL
+
+ /* set up args and a local for "&fp" */
+ /* (str sp, [sp, #-4]! would be perfect here, but is discouraged) */
+ str rFP, [sp, #-4]! @ *--sp = fp
+ mov ip, sp @ ip<- &fp
+ mov r3, #0 @ r3<- false
+ str ip, [sp, #-4]! @ *--sp = &fp
+ ldr r1, [rGLUE, #offGlue_method] @ r1<- glue->method
+ mov r0, r10 @ r0<- self
+ ldr r1, [r1, #offMethod_insns] @ r1<- method->insns
+ mov r2, r9 @ r2<- exception
+ sub r1, rPC, r1 @ r1<- pc - method->insns
+ mov r1, r1, asr #1 @ r1<- offset in code units
+
+ /* call, r0 gets catchRelPc (a code-unit offset) */
+ bl dvmFindCatchBlock @ call(self, relPc, exc, scan?, &fp)
+
+ /* fix earlier stack overflow if necessary; may trash rFP */
+ ldrb r1, [r10, #offThread_stackOverflowed]
+ cmp r1, #0 @ did we overflow earlier?
+ beq 1f @ no, skip ahead
+ mov rFP, r0 @ save relPc result in rFP
+ mov r0, r10 @ r0<- self
+ mov r1, r9 @ r1<- exception
+ bl dvmCleanupStackOverflow @ call(self)
+ mov r0, rFP @ restore result
+1:
+
+ /* update frame pointer and check result from dvmFindCatchBlock */
+ ldr rFP, [sp, #4] @ retrieve the updated rFP
+ cmp r0, #0 @ is catchRelPc < 0?
+ add sp, sp, #8 @ restore stack
+ bmi .LnotCaughtLocally
+
+ /* adjust locals to match self->curFrame and updated PC */
+ SAVEAREA_FROM_FP(r1, rFP) @ r1<- new save area
+ ldr r1, [r1, #offStackSaveArea_method] @ r1<- new method
+ str r1, [rGLUE, #offGlue_method] @ glue->method = new method
+ ldr r2, [r1, #offMethod_clazz] @ r2<- method->clazz
+ ldr r3, [r1, #offMethod_insns] @ r3<- method->insns
+ ldr r2, [r2, #offClassObject_pDvmDex] @ r2<- method->clazz->pDvmDex
+ add rPC, r3, r0, asl #1 @ rPC<- method->insns + catchRelPc
+ str r2, [rGLUE, #offGlue_methodClassDex] @ glue->pDvmDex = meth...
+
+ /* release the tracked alloc on the exception */
+ mov r0, r9 @ r0<- exception
+ mov r1, r10 @ r1<- self
+ bl dvmReleaseTrackedAlloc @ release the exception
+
+ /* restore the exception if the handler wants it */
+ FETCH_INST() @ load rINST from rPC
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ cmp ip, #OP_MOVE_EXCEPTION @ is it "move-exception"?
+ streq r9, [r10, #offThread_exception] @ yes, restore the exception
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+.LnotCaughtLocally: @ r9=exception, r10=self
+ /* fix stack overflow if necessary */
+ ldrb r1, [r10, #offThread_stackOverflowed]
+ cmp r1, #0 @ did we overflow earlier?
+ movne r0, r10 @ if yes: r0<- self
+ movne r1, r9 @ if yes: r1<- exception
+ blne dvmCleanupStackOverflow @ if yes: call(self)
+
+ @ may want to show "not caught locally" debug messages here
+#if DVM_SHOW_EXCEPTION >= 2
+ /* call __android_log_print(prio, tag, format, ...) */
+ /* "Exception %s from %s:%d not caught locally" */
+ @ dvmLineNumFromPC(method, pc - method->insns)
+ ldr r0, [rGLUE, #offGlue_method]
+ ldr r1, [r0, #offMethod_insns]
+ sub r1, rPC, r1
+ asr r1, r1, #1
+ bl dvmLineNumFromPC
+ str r0, [sp, #-4]!
+ @ dvmGetMethodSourceFile(method)
+ ldr r0, [rGLUE, #offGlue_method]
+ bl dvmGetMethodSourceFile
+ str r0, [sp, #-4]!
+ @ exception->clazz->descriptor
+ ldr r3, [r9, #offObject_clazz]
+ ldr r3, [r3, #offClassObject_descriptor]
+ @
+ ldr r2, strExceptionNotCaughtLocally
+ ldr r1, strLogTag
+ mov r0, #3 @ LOG_DEBUG
+ bl __android_log_print
+#endif
+ str r9, [r10, #offThread_exception] @ restore exception
+ mov r0, r9 @ r0<- exception
+ mov r1, r10 @ r1<- self
+ bl dvmReleaseTrackedAlloc @ release the exception
+ mov r1, #0 @ "want switch" = false
+ b common_gotoBail @ bail out
+
+
+ /*
+ * Exception handling, calls through "glue code".
+ */
+ .if 0
+.LexceptionOld:
+ SAVE_PC_FP_TO_GLUE() @ export state
+ mov r0, rGLUE @ arg to function
+ bl dvmMterp_exceptionThrown
+ b common_resumeAfterGlueCall
+ .endif
+
+
+/*
+ * After returning from a "glued" function, pull out the updated
+ * values and start executing at the next instruction.
+ */
+common_resumeAfterGlueCall:
+ LOAD_PC_FP_FROM_GLUE() @ pull rPC and rFP out of glue
+ FETCH_INST() @ load rINST from rPC
+ GET_INST_OPCODE(ip) @ extract opcode from rINST
+ GOTO_OPCODE(ip) @ jump to next instruction
+
+/*
+ * Invalid array index.
+ */
+common_errArrayIndex:
+ EXPORT_PC()
+ ldr r0, strArrayIndexException
+ mov r1, #0
+ bl dvmThrowException
+ b common_exceptionThrown
+
+/*
+ * Invalid array value.
+ */
+common_errArrayStore:
+ EXPORT_PC()
+ ldr r0, strArrayStoreException
+ mov r1, #0
+ bl dvmThrowException
+ b common_exceptionThrown
+
+/*
+ * Integer divide or mod by zero.
+ */
+common_errDivideByZero:
+ EXPORT_PC()
+ ldr r0, strArithmeticException
+ ldr r1, strDivideByZero
+ bl dvmThrowException
+ b common_exceptionThrown
+
+/*
+ * Attempt to allocate an array with a negative size.
+ */
+common_errNegativeArraySize:
+ EXPORT_PC()
+ ldr r0, strNegativeArraySizeException
+ mov r1, #0
+ bl dvmThrowException
+ b common_exceptionThrown
+
+/*
+ * Invocation of a non-existent method.
+ */
+common_errNoSuchMethod:
+ EXPORT_PC()
+ ldr r0, strNoSuchMethodError
+ mov r1, #0
+ bl dvmThrowException
+ b common_exceptionThrown
+
+/*
+ * We encountered a null object when we weren't expecting one. We
+ * export the PC, throw a NullPointerException, and goto the exception
+ * processing code.
+ */
+common_errNullObject:
+ EXPORT_PC()
+ ldr r0, strNullPointerException
+ mov r1, #0
+ bl dvmThrowException
+ b common_exceptionThrown
+
+/*
+ * For debugging, cause an immediate fault. The source address will
+ * be in lr (use a bl instruction to jump here).
+ */
+common_abort:
+ ldr pc, .LdeadFood
+.LdeadFood:
+ .word 0xdeadf00d
+
+/*
+ * Spit out a "we were here", preserving all registers. (The attempt
+ * to save ip won't work, but we need to save an even number of
+ * registers for EABI 64-bit stack alignment.)
+ */
+ .macro SQUEAK num
+common_squeak\num:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ ldr r0, strSqueak
+ mov r1, #\num
+ bl printf
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+ .endm
+
+ SQUEAK 0
+ SQUEAK 1
+ SQUEAK 2
+ SQUEAK 3
+ SQUEAK 4
+ SQUEAK 5
+
+/*
+ * Spit out the number in r0, preserving registers.
+ */
+common_printNum:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ mov r1, r0
+ ldr r0, strSqueak
+ bl printf
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+
+/*
+ * Print a newline, preserving registers.
+ */
+common_printNewline:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ ldr r0, strNewline
+ bl printf
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+
+ /*
+ * Print the 32-bit quantity in r0 as a hex value, preserving registers.
+ */
+common_printHex:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ mov r1, r0
+ ldr r0, strPrintHex
+ bl printf
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+
+/*
+ * Print the 64-bit quantity in r0-r1, preserving registers.
+ */
+common_printLong:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ mov r3, r1
+ mov r2, r0
+ ldr r0, strPrintLong
+ bl printf
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+
+/*
+ * Print full method info. Pass the Method* in r0. Preserves regs.
+ */
+common_printMethod:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bl dvmMterpPrintMethod
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+
+/*
+ * Call a C helper function that dumps regs and possibly some
+ * additional info. Requires the C function to be compiled in.
+ */
+ .if 0
+common_dumpRegs:
+ stmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bl dvmMterpDumpArmRegs
+ ldmfd sp!, {r0, r1, r2, r3, ip, lr}
+ bx lr
+ .endif
+
+#if 0
+/*
+ * Experiment on VFP mode.
+ *
+ * uint32_t setFPSCR(uint32_t val, uint32_t mask)
+ *
+ * Updates the bits specified by "mask", setting them to the values in "val".
+ */
+setFPSCR:
+ and r0, r0, r1 @ make sure no stray bits are set
+ fmrx r2, fpscr @ get VFP reg
+ mvn r1, r1 @ bit-invert mask
+ and r2, r2, r1 @ clear masked bits
+ orr r2, r2, r0 @ set specified bits
+ fmxr fpscr, r2 @ set VFP reg
+ mov r0, r2 @ return new value
+ bx lr
+
+ .align 2
+ .global dvmConfigureFP
+ .type dvmConfigureFP, %function
+dvmConfigureFP:
+ stmfd sp!, {ip, lr}
+ /* 0x03000000 sets DN/FZ */
+ /* 0x00009f00 clears the six exception enable flags */
+ bl common_squeak0
+ mov r0, #0x03000000 @ r0<- 0x03000000
+ add r1, r0, #0x9f00 @ r1<- 0x03009f00
+ bl setFPSCR
+ ldmfd sp!, {ip, pc}
+#endif
+
+
+/*
+ * String references, must be close to the code that uses them.
+ */
+ .align 2
+strArithmeticException:
+ .word .LstrArithmeticException
+strArrayIndexException:
+ .word .LstrArrayIndexException
+strArrayStoreException:
+ .word .LstrArrayStoreException
+strDivideByZero:
+ .word .LstrDivideByZero
+strNegativeArraySizeException:
+ .word .LstrNegativeArraySizeException
+strNoSuchMethodError:
+ .word .LstrNoSuchMethodError
+strNullPointerException:
+ .word .LstrNullPointerException
+
+strLogTag:
+ .word .LstrLogTag
+strExceptionNotCaughtLocally:
+ .word .LstrExceptionNotCaughtLocally
+
+strNewline:
+ .word .LstrNewline
+strSqueak:
+ .word .LstrSqueak
+strPrintHex:
+ .word .LstrPrintHex
+strPrintLong:
+ .word .LstrPrintLong
+
+/*
+ * Zero-terminated ASCII string data.
+ *
+ * On ARM we have two choices: do like gcc does, and LDR from a .word
+ * with the address, or use an ADR pseudo-op to get the address
+ * directly. ADR saves 4 bytes and an indirection, but it's using a
+ * PC-relative addressing mode and hence has a limited range, which
+ * makes it not work well with mergeable string sections.
+ */
+ .section .rodata.str1.4,"aMS",%progbits,1
+
+.LstrBadEntryPoint:
+ .asciz "Bad entry point %d\n"
+.LstrArithmeticException:
+ .asciz "Ljava/lang/ArithmeticException;"
+.LstrArrayIndexException:
+ .asciz "Ljava/lang/ArrayIndexOutOfBoundsException;"
+.LstrArrayStoreException:
+ .asciz "Ljava/lang/ArrayStoreException;"
+.LstrClassCastException:
+ .asciz "Ljava/lang/ClassCastException;"
+.LstrDivideByZero:
+ .asciz "divide by zero"
+.LstrFilledNewArrayNotImpl:
+ .asciz "filled-new-array only implemented for objects and 'int'"
+.LstrInternalError:
+ .asciz "Ljava/lang/InternalError;"
+.LstrInstantiationError:
+ .asciz "Ljava/lang/InstantiationError;"
+.LstrNegativeArraySizeException:
+ .asciz "Ljava/lang/NegativeArraySizeException;"
+.LstrNoSuchMethodError:
+ .asciz "Ljava/lang/NoSuchMethodError;"
+.LstrNullPointerException:
+ .asciz "Ljava/lang/NullPointerException;"
+
+.LstrLogTag:
+ .asciz "mterp"
+.LstrExceptionNotCaughtLocally:
+ .asciz "Exception %s from %s:%d not caught locally\n"
+
+.LstrNewline:
+ .asciz "\n"
+.LstrSqueak:
+ .asciz "<%d>"
+.LstrPrintHex:
+ .asciz "<0x%x>"
+.LstrPrintLong:
+ .asciz "<%lld>"
+
diff --git a/vm/mterp/out/InterpAsm-x86-atom.S b/vm/mterp/out/InterpAsm-x86-atom.S
new file mode 100644
index 0000000..9d384dd
--- /dev/null
+++ b/vm/mterp/out/InterpAsm-x86-atom.S
@@ -0,0 +1,18547 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'x86-atom'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: x86-atom/header.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: header.S
+ */
+
+ /*
+ * IA32 calling convention and general notes:
+ *
+ * EAX, ECX, EDX - general purpose scratch registers (caller-saved);
+ *
+ * The stack (%esp) - used to pass arguments to functions
+ *
+ * EAX - holds the first 4 bytes of a return
+ * EDX - holds the second 4 bytes of a return
+ *
+ * EBX, ESI, EDI, EBP - are callee saved
+ *
+ * CS, DS, SS - are segment registers
+ * ES, FS, GS - are segment registers. We will try to avoid using these registers
+ *
+ * The stack is "full descending". Only the arguments that do not fit * in the first two arg registers are placed on the stack.
+ * "%esp" points to the first stacked argument (i.e. the 3rd arg).
+ */
+
+ /*
+ * Mterp and IA32 notes
+ *
+ * mem nick purpose
+ * (%ebp) rGLUE InterpState base pointer (A.K.A. MterpGlue Pointer)
+ * %esi rPC interpreted program counter, used for fetching
+ * instructions
+ * %ebx rINST first 16-bit code unit of current instruction
+ * %edi rFP interpreted frame pointer, used for accessing
+ * locals and args
+ */
+
+ /*
+ * Includes
+ */
+
+#include "../common/asm-constants.h"
+
+ /*
+ * Reserved registers
+ */
+
+#define rGLUE (%ebp)
+#define rINST %ebx
+#define rINSTbl %bl
+#define rINSTbh %bh
+#define rINSTw %bx
+#define rPC %esi
+#define rFP %edi
+
+ /*
+ * Temporary register used when finishing an opcode
+ */
+
+#define rFinish %edx
+
+ /*
+ * Stack locations used for temporary data. For convenience.
+ */
+
+#define sReg0 4(%ebp)
+#define sReg1 8(%ebp)
+#define sReg2 12(%ebp)
+#define sReg3 16(%ebp)
+
+ /*
+ * Save the PC and FP to the glue struct
+ */
+
+ .macro SAVE_PC_FP_TO_GLUE _reg
+ movl rGLUE, \_reg
+ movl rPC, offGlue_pc(\_reg)
+ movl rFP, offGlue_fp(\_reg)
+ .endm
+
+ /*
+ * Restore the PC and FP from the glue struct
+ */
+
+ .macro LOAD_PC_FP_FROM_GLUE
+ movl rGLUE, rFP
+ movl offGlue_pc(rFP), rPC
+ movl offGlue_fp(rFP), rFP
+ .endm
+
+ /*
+ * "Export" the PC to the stack frame, f/b/o future exception objects. This must
+ * be done *before* something calls dvmThrowException.
+ *
+ * In C this is "SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc", i.e.
+ * fp - sizeof(StackSaveArea) + offsetof(SaveArea, xtra.currentPc)
+ *
+ * It's okay to do this more than once.
+ */
+
+ .macro EXPORT_PC
+ movl rPC, (-sizeofStackSaveArea + offStackSaveArea_currentPc)(rFP)
+ .endm
+
+ /*
+ * Given a frame pointer, find the stack save area.
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+
+ .macro SAVEAREA_FROM_FP _reg
+ lea -sizeofStackSaveArea(rFP), \_reg
+ .endm
+
+ /*
+ * Get the 32-bit value from a dalvik register.
+ */
+
+ .macro GET_VREG _vreg
+ movl (rFP,\_vreg, 4), \_vreg
+ .endm
+
+ /*
+ * Set the 32-bit value from a dalvik register.
+ */
+
+ .macro SET_VREG _reg _vreg
+ movl \_reg, (rFP,\_vreg, 4)
+ .endm
+
+ /*
+ * Fetch the next instruction from rPC into rINST. Does not advance rPC.
+ */
+
+ .macro FETCH_INST
+ movzwl (rPC), rINST
+ .endm
+
+ /*
+ * Fetch the next instruction from the specified offset. Advances rPC
+ * to point to the next instruction. "_count" is in 16-bit code units.
+ *
+ * This must come AFTER anything that can throw an exception, or the
+ * exception catch may miss. (This also implies that it must come after
+ * EXPORT_PC())
+ */
+
+ .macro FETCH_ADVANCE_INST _count
+ add $(\_count*2), rPC
+ movzwl (rPC), rINST
+ .endm
+
+ /*
+ * Fetch the next instruction from an offset specified by _reg. Updates
+ * rPC to point to the next instruction. "_reg" must specify the distance
+ * in bytes, *not* 16-bit code units, and may be a signed value.
+ */
+
+ .macro FETCH_ADVANCE_INST_RB _reg
+ addl \_reg, rPC
+ movzwl (rPC), rINST
+ .endm
+
+ /*
+ * Fetch a half-word code unit from an offset past the current PC. The
+ * "_count" value is in 16-bit code units. Does not advance rPC.
+ * For example, given instruction of format: AA|op BBBB, it
+ * fetches BBBB.
+ */
+
+ .macro FETCH _count _reg
+ movzwl (\_count*2)(rPC), \_reg
+ .endm
+
+ /*
+ * Fetch a half-word code unit from an offset past the current PC. The
+ * "_count" value is in 16-bit code units. Does not advance rPC.
+ * This variant treats the value as signed.
+ */
+
+ .macro FETCHs _count _reg
+ movswl (\_count*2)(rPC), \_reg
+ .endm
+
+ /*
+ * Fetch the first byte from an offset past the current PC. The
+ * "_count" value is in 16-bit code units. Does not advance rPC.
+ * For example, given instruction of format: AA|op CC|BB, it
+ * fetches BB.
+ */
+
+ .macro FETCH_BB _count _reg
+ movzbl (\_count*2)(rPC), \_reg
+ .endm
+
+ /*
+ * Fetch the second byte from an offset past the current PC. The
+ * "_count" value is in 16-bit code units. Does not advance rPC.
+ * For example, given instruction of format: AA|op CC|BB, it
+ * fetches CC.
+ */
+
+ .macro FETCH_CC _count _reg
+ movzbl (\_count*2 + 1)(rPC), \_reg
+ .endm
+
+ /*
+ * Fetch the second byte from an offset past the current PC. The
+ * "_count" value is in 16-bit code units. Does not advance rPC.
+ * This variant treats the value as signed.
+ */
+
+ .macro FETCH_CCs _count _reg
+ movsbl (\_count*2 + 1)(rPC), \_reg
+ .endm
+
+
+ /*
+ * Fetch one byte from an offset past the current PC. Pass in the same
+ * "_count" as you would for FETCH, and an additional 0/1 indicating which
+ * byte of the halfword you want (lo/hi).
+ */
+
+ .macro FETCH_B _reg _count _byte
+ movzbl (\_count*2+\_byte)(rPC), \_reg
+ .endm
+
+ /*
+ * Put the instruction's opcode field into the specified register.
+ */
+
+ .macro GET_INST_OPCODE _reg
+ movzbl rINSTbl, \_reg
+ .endm
+
+ /*
+ * Begin executing the opcode in _reg.
+ */
+
+ .macro GOTO_OPCODE _reg
+ shl $6, \_reg
+ addl $dvmAsmInstructionStart,\_reg
+ jmp *\_reg
+ .endm
+
+
+
+ /*
+ * Macros pair attempts to speed up FETCH_INST, GET_INST_OPCODE and GOTO_OPCODE
+ * by using a jump table. _rFinish should must be the same register for
+ * both macros.
+ */
+
+ .macro FFETCH _rFinish
+ movzbl (rPC), \_rFinish
+ .endm
+
+ .macro FGETOP_JMPa _rFinish
+ movzbl 1(rPC), rINST
+ jmp *dvmAsmInstructionJmpTable(,\_rFinish, 4)
+ .endm
+
+ /*
+ * Macro pair attempts to speed up FETCH_INST, GET_INST_OPCODE and GOTO_OPCODE
+ * by using a jump table. _rFinish and _count should must be the same register for
+ * both macros.
+ */
+
+ .macro FFETCH_ADV _count _rFinish
+ movzbl (\_count*2)(rPC), \_rFinish
+ .endm
+
+ .macro FGETOP_JMP _count _rFinish
+ movzbl (\_count*2 + 1)(rPC), rINST
+ addl $(\_count*2), rPC
+ jmp *dvmAsmInstructionJmpTable(,\_rFinish, 4)
+ .endm
+
+ /*
+ * Macro pair attempts to speed up FETCH_INST, GET_INST_OPCODE and GOTO_OPCODE
+ * by using a jump table. _rFinish and _reg should must be the same register for
+ * both macros.
+ */
+
+ .macro FFETCH_ADV_RB _reg _rFinish
+ movzbl (\_reg, rPC), \_rFinish
+ .endm
+
+ .macro FGETOP_RB_JMP _reg _rFinish
+ movzbl 1(\_reg, rPC), rINST
+ addl \_reg, rPC
+ jmp *dvmAsmInstructionJmpTable(,\_rFinish, 4)
+ .endm
+
+ /*
+ * Attempts to speed up FETCH_INST, GET_INST_OPCODE using
+ * a jump table. This macro should be called before FINISH_JMP where
+ * rFinish should be the same register containing the opcode value.
+ * This is an attempt to split up FINISH in order to reduce or remove
+ * potential stalls due to the wait for rFINISH.
+ */
+
+ .macro FINISH_FETCH _rFinish
+ movzbl (rPC), \_rFinish
+ movzbl 1(rPC), rINST
+ .endm
+
+
+ /*
+ * Attempts to speed up FETCH_ADVANCE_INST, GET_INST_OPCODE using
+ * a jump table. This macro should be called before FINISH_JMP where
+ * rFinish should be the same register containing the opcode value.
+ * This is an attempt to split up FINISH in order to reduce or remove
+ * potential stalls due to the wait for rFINISH.
+ */
+
+ .macro FINISH_FETCH_ADVANCE _count _rFinish
+ movzbl (\_count*2)(rPC), \_rFinish
+ movzbl (\_count*2 + 1)(rPC), rINST
+ addl $(\_count*2), rPC
+ .endm
+
+ /*
+ * Attempts to speed up FETCH_ADVANCE_INST_RB, GET_INST_OPCODE using
+ * a jump table. This macro should be called before FINISH_JMP where
+ * rFinish should be the same register containing the opcode value.
+ * This is an attempt to split up FINISH in order to reduce or remove
+ * potential stalls due to the wait for rFINISH.
+ */
+
+ .macro FINISH_FETCH_ADVANCE_RB _reg _rFinish
+ movzbl (\_reg, rPC), \_rFinish
+ movzbl 1(\_reg, rPC), rINST
+ addl \_reg, rPC
+ .endm
+
+ /*
+ * Attempts to speed up GOTO_OPCODE using a jump table. This macro should
+ * be called after a FINISH_FETCH* instruction where rFinish should be the
+ * same register containing the opcode value. This is an attempt to split up
+ * FINISH in order to reduce or remove potential stalls due to the wait for rFINISH.
+ */
+
+ .macro FINISH_JMP _rFinish
+ jmp *dvmAsmInstructionJmpTable(,\_rFinish, 4)
+ .endm
+
+ /*
+ * Attempts to speed up FETCH_INST, GET_INST_OPCODE, GOTO_OPCODE by using
+ * a jump table. Uses a single macro - but it should be faster if we
+ * split up the fetch for rFinish and the jump using rFinish.
+ */
+
+ .macro FINISH_A
+ movzbl (rPC), rFinish
+ movzbl 1(rPC), rINST
+ jmp *dvmAsmInstructionJmpTable(,rFinish, 4)
+ .endm
+
+ /*
+ * Attempts to speed up FETCH_ADVANCE_INST, GET_INST_OPCODE,
+ * GOTO_OPCODE by using a jump table. Uses a single macro -
+ * but it should be faster if we split up the fetch for rFinish
+ * and the jump using rFinish.
+ */
+
+ .macro FINISH _count
+ movzbl (\_count*2)(rPC), rFinish
+ movzbl (\_count*2 + 1)(rPC), rINST
+ addl $(\_count*2), rPC
+ jmp *dvmAsmInstructionJmpTable(,rFinish, 4)
+ .endm
+
+ /*
+ * Attempts to speed up FETCH_ADVANCE_INST_RB, GET_INST_OPCODE,
+ * GOTO_OPCODE by using a jump table. Uses a single macro -
+ * but it should be faster if we split up the fetch for rFinish
+ * and the jump using rFinish.
+ */
+
+ .macro FINISH_RB _reg _rFinish
+ movzbl (\_reg, rPC), \_rFinish
+ movzbl 1(\_reg, rPC), rINST
+ addl \_reg, rPC
+ jmp *dvmAsmInstructionJmpTable(,\_rFinish, 4)
+ .endm
+
+ /*
+ * Hard coded helper values.
+ */
+
+.balign 16
+
+.LdoubNeg:
+ .quad 0x8000000000000000
+
+.L64bits:
+ .quad 0xFFFFFFFFFFFFFFFF
+
+.LshiftMask2:
+ .quad 0x0000000000000000
+.LshiftMask:
+ .quad 0x000000000000003F
+
+.Lvalue64:
+ .quad 0x0000000000000040
+
+.LvaluePosInfLong:
+ .quad 0x7FFFFFFFFFFFFFFF
+
+.LvalueNegInfLong:
+ .quad 0x8000000000000000
+
+.LvalueNanLong:
+ .quad 0x0000000000000000
+
+.LintMin:
+.long 0x80000000
+
+.LintMax:
+.long 0x7FFFFFFF
+
+
+ .global dvmAsmInstructionStart
+ .type dvmAsmInstructionStart, %function
+dvmAsmInstructionStart = .L_OP_NOP
+ .text
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NOP: /* 0x00 */
+/* File: x86-atom/OP_NOP.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_NOP.S
+ *
+ * Code: Use a cycle. Uses no substitutions.
+ *
+ * For: nop
+ *
+ * Description: No operation. Use a cycle
+ *
+ * Format: ØØ|op (10x)
+ *
+ * Syntax: op
+ */
+
+ FINISH 1 # jump to next instruction
+
+#ifdef ASSIST_DEBUGGER
+
+ /*
+ * insert fake function header to help gdb find the stack frame
+ */
+
+ .type dalvik_inst, %function
+dalvik_inst:
+ MTERP_ENTRY
+#endif
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE: /* 0x01 */
+/* File: x86-atom/OP_MOVE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MOVE.S
+ *
+ * Code: Copies contents from one register to another. Uses no
+ * substitutions.
+ *
+ * For: move, move-object, long-to-int
+ *
+ * Description: Copies contents from one non-object register to another.
+ * vA<- vB; fp[A]<- fp[B]
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %ecx # %ecx<- BA
+ shr $4, rINST # rINST<- B
+ and $15, %ecx # %ecx<- A
+ FFETCH_ADV 1, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG rINST # rINST<- vB
+ SET_VREG rINST, %ecx # vA<- vB; %edx
+ FGETOP_JMP 1, %eax # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_FROM16: /* 0x02 */
+/* File: x86-atom/OP_MOVE_FROM16.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MOVE_FROM16.S
+ *
+ * Code: Copies contents from one register to another
+ *
+ * For: move/from16, move-object/from16
+ *
+ * Description: Copies contents from one non-object register to another.
+ * vA<- vB; fp[A]<- fp[B]
+ *
+ * Format: AA|op BBBB (22x)
+ *
+ * Syntax: op vAA, vBBBB
+ */
+
+ FETCH 1, %edx # %edx<- BBBB
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %edx # %edx<- vB
+ SET_VREG %edx, rINST # vA<- vB; %edx
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_16: /* 0x03 */
+/* File: x86-atom/OP_MOVE_16.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MOVE_16.S
+ *
+ * Code: Copies contents from one register to another
+ *
+ * For: move/16, move-object/16
+ *
+ * Description: Copies contents from one non-object register to another.
+ * fp[A]<- fp[B]
+ *
+ * Format: ØØ|op AAAA BBBB (32x)
+ *
+ * Syntax: op vAAAA, vBBBB
+ */
+
+ FETCH 2, %edx # %edx<- BBBB
+ FETCH 1, %ecx # %ecx<- AAAA
+ FFETCH_ADV 3, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %edx # %edx<- vB
+ SET_VREG %edx, %ecx # vA<- vB; %edx
+ FGETOP_JMP 3, %eax # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_WIDE: /* 0x04 */
+/* File: x86-atom/OP_MOVE_WIDE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MOVE_WIDE.S
+ *
+ * Code: Copies contents from one register to another. Uses no
+ * substitutions.
+ *
+ * For: move-wide
+ *
+ * Description: Copies contents from one non-object register to another.
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA+
+ shr $4, %edx # %edx<- B
+ and $15, rINST # rINST<- A
+ FFETCH_ADV 1, %eax # %eax<- next instruction hi; fetch, advance
+ movq (rFP, %edx, 4), %xmm0 # %xmm0<- vB
+ movq %xmm0, (rFP, rINST, 4) # vA<- vB
+ FGETOP_JMP 1, %eax # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_WIDE_FROM16: /* 0x05 */
+/* File: x86-atom/OP_MOVE_WIDE_FROM16.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MOVE_WIDE_FROM16.S
+ *
+ * Code: Copies contents from one register to another
+ *
+ * For: move-wide/from16
+ *
+ * Description: Copies contents from one non-object register to another.
+ *
+ * Format: AA|op BBBB (22x)
+ *
+ * Syntax: op vAA, vBBBB
+ */
+
+ FETCH 1, %edx # %edx<- BBBB
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movq (rFP, %edx, 4), %xmm0 # %xmm0<- vB
+ movq %xmm0, (rFP, rINST, 4) # vA<- vB
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_WIDE_16: /* 0x06 */
+/* File: x86-atom/OP_MOVE_WIDE_16.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MOVE_WIDE_16.S
+ *
+ * Code: Copies contents from one register to another. Uses no
+ * substitutions.
+ *
+ * For: move-wide/16
+ *
+ * Description: Copies contents from one non-object register to another.
+ *
+ * Format: ØØ|op AAAA BBBB (32x)
+ *
+ * Syntax: op vAAAA, vBBBB
+ */
+
+ FETCH 2, %edx # %edx<- BBBB
+ FETCH 1, %ecx # %ecx<- AAAA
+ FFETCH_ADV 3, %eax # %eax<- next instruction hi; fetch, advance
+ movq (rFP, %edx, 4), %xmm0 # %xmm0<- vB
+ movq %xmm0, (rFP, %ecx, 4) # vA<- vB; %xmm0
+ FGETOP_JMP 3, %eax # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_OBJECT: /* 0x07 */
+/* File: x86-atom/OP_MOVE_OBJECT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MOVE_OBJECT.S
+ */
+
+/* File: x86-atom/OP_MOVE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MOVE.S
+ *
+ * Code: Copies contents from one register to another. Uses no
+ * substitutions.
+ *
+ * For: move, move-object, long-to-int
+ *
+ * Description: Copies contents from one non-object register to another.
+ * vA<- vB; fp[A]<- fp[B]
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %ecx # %ecx<- BA
+ shr $4, rINST # rINST<- B
+ and $15, %ecx # %ecx<- A
+ FFETCH_ADV 1, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG rINST # rINST<- vB
+ SET_VREG rINST, %ecx # vA<- vB; %edx
+ FGETOP_JMP 1, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_OBJECT_FROM16: /* 0x08 */
+/* File: x86-atom/OP_MOVE_OBJECT_FROM16.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MOVE_OBJECT_FROM16.S
+ */
+
+/* File: x86-atom/OP_MOVE_FROM16.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MOVE_FROM16.S
+ *
+ * Code: Copies contents from one register to another
+ *
+ * For: move/from16, move-object/from16
+ *
+ * Description: Copies contents from one non-object register to another.
+ * vA<- vB; fp[A]<- fp[B]
+ *
+ * Format: AA|op BBBB (22x)
+ *
+ * Syntax: op vAA, vBBBB
+ */
+
+ FETCH 1, %edx # %edx<- BBBB
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %edx # %edx<- vB
+ SET_VREG %edx, rINST # vA<- vB; %edx
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_OBJECT_16: /* 0x09 */
+/* File: x86-atom/OP_MOVE_OBJECT_16.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MOVE_OBJECT_16.S
+ */
+
+/* File: x86-atom/OP_MOVE_16.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MOVE_16.S
+ *
+ * Code: Copies contents from one register to another
+ *
+ * For: move/16, move-object/16
+ *
+ * Description: Copies contents from one non-object register to another.
+ * fp[A]<- fp[B]
+ *
+ * Format: ØØ|op AAAA BBBB (32x)
+ *
+ * Syntax: op vAAAA, vBBBB
+ */
+
+ FETCH 2, %edx # %edx<- BBBB
+ FETCH 1, %ecx # %ecx<- AAAA
+ FFETCH_ADV 3, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %edx # %edx<- vB
+ SET_VREG %edx, %ecx # vA<- vB; %edx
+ FGETOP_JMP 3, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_RESULT: /* 0x0a */
+/* File: x86-atom/OP_MOVE_RESULT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MOVE_RESULT.S
+ *
+ * Code: Copies a return value to a register
+ *
+ * For: move-result, move-result-object
+ *
+ * Description: Move the single-word non-object result of the most
+ * recent method invocation into the indicated register. This
+ * must be done as the instruction immediately after a
+ * method invocation whose (single-word, non-object) result
+ * is not to be ignored; anywhere else is invalid.
+ *
+ * Format: AA|op (11x)
+ *
+ * Syntax: op vAA
+ */
+
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ FFETCH_ADV 1, %ecx # %ecx<- next instruction hi; fetch, advance
+ movl offGlue_retval(%eax), %edx # %edx<- glue->retval
+ SET_VREG %edx, rINST # vA<- glue->retval
+ FGETOP_JMP 1, %ecx # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_RESULT_WIDE: /* 0x0b */
+/* File: x86-atom/OP_MOVE_RESULT_WIDE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MOVE_RESULT_WIDE.S
+ *
+ * Code: Copies a return value to a register
+ *
+ * For: move-result-wide
+ *
+ * Description: Move the double-word non-object result of the most
+ * recent method invocation into the indicated register. This
+ * must be done as the instruction immediately after a
+ * method invocation whose (single-word, non-object) result
+ * is not to be ignored; anywhere else is invalid.
+ *
+ * Format: AA|op (11x)
+ *
+ * Syntax: op vAA
+ */
+
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ movq offGlue_retval(%eax), %xmm0 # %xmm0<- glue->retval
+ movq %xmm0, (rFP, rINST, 4) # vA<- glue->retval
+ FFETCH_ADV 1, %edx # %edx<- next instruction hi; fetch, advance
+ FGETOP_JMP 1, %edx # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_RESULT_OBJECT: /* 0x0c */
+/* File: x86-atom/OP_MOVE_RESULT_OBJECT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MOVE_RESULT_OBJECT.S
+ */
+
+/* File: x86-atom/OP_MOVE_RESULT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MOVE_RESULT.S
+ *
+ * Code: Copies a return value to a register
+ *
+ * For: move-result, move-result-object
+ *
+ * Description: Move the single-word non-object result of the most
+ * recent method invocation into the indicated register. This
+ * must be done as the instruction immediately after a
+ * method invocation whose (single-word, non-object) result
+ * is not to be ignored; anywhere else is invalid.
+ *
+ * Format: AA|op (11x)
+ *
+ * Syntax: op vAA
+ */
+
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ FFETCH_ADV 1, %ecx # %ecx<- next instruction hi; fetch, advance
+ movl offGlue_retval(%eax), %edx # %edx<- glue->retval
+ SET_VREG %edx, rINST # vA<- glue->retval
+ FGETOP_JMP 1, %ecx # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_EXCEPTION: /* 0x0d */
+/* File: x86-atom/OP_MOVE_EXCEPTION.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MOVE_EXCEPTION.S
+ *
+ * Code: Moves an exception to a register
+ *
+ * For: move-exception
+ *
+ * Description: Save a just-caught exception into the given register. This
+ * instruction is only valid as the first instruction of an
+ * exception handler.
+ *
+ * Format: AA|op (11x)
+ *
+ * Syntax: op vAA
+ */
+
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ movl offGlue_self(%eax), %ecx # %ecx<- glue->self
+ FFETCH_ADV 1, %eax # %eax<- next instruction hi; fetch, advance
+ movl offThread_exception(%ecx), %edx # %edx<- glue->self->exception
+ movl $0, offThread_exception(%ecx) # clear exception
+ SET_VREG %edx, rINST # vAA<- glue->self->exception
+ FGETOP_JMP 1, %eax # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RETURN_VOID: /* 0x0e */
+/* File: x86-atom/OP_RETURN_VOID.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_RETURN_VOID.S
+ */
+
+ jmp common_returnFromMethod
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RETURN: /* 0x0f */
+/* File: x86-atom/OP_RETURN.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_RETURN.S
+ */
+
+/* File: x86-atom/OP_RETURN_COMMON.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_RETURN_COMMON.S
+ *
+ * Code: Return a 32-bit value. Uses no substitutions.
+ *
+ * For: return, return-object
+ *
+ * Description: Copies the return value into the "glue"
+ * structure, then jumps to the return handler.
+ *
+ * Format: AA|op (11x)
+ *
+ * Syntax: op vAA
+ */
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ GET_VREG rINST # rINST<- vAA
+ movl rINST, offGlue_retval(%edx) # glue->retval<- vAA
+ jmp common_returnFromMethod # jump to common return code
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RETURN_WIDE: /* 0x10 */
+/* File: x86-atom/OP_RETURN_WIDE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_RETURN_WIDE.S
+ *
+ * Code: Return a 64-bit value. Uses no substitutions.
+ *
+ * For: return-wide
+ *
+ * Description: Copies the return value into the "glue"
+ * structure, then jumps to the return handler.
+ *
+ * Format: AA|op (11x)
+ *
+ * Syntax: op vAA
+ */
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movq (rFP, rINST, 4), %xmm0 # %xmm0<- vAA
+ movq %xmm0, offGlue_retval(%edx)# glue->retval<- vAA
+ jmp common_returnFromMethod # jump to common return code
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RETURN_OBJECT: /* 0x11 */
+/* File: x86-atom/OP_RETURN_OBJECT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_RETURN_OBJECT.S
+ */
+
+/* File: x86-atom/OP_RETURN_COMMON.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_RETURN_COMMON.S
+ *
+ * Code: Return a 32-bit value. Uses no substitutions.
+ *
+ * For: return, return-object
+ *
+ * Description: Copies the return value into the "glue"
+ * structure, then jumps to the return handler.
+ *
+ * Format: AA|op (11x)
+ *
+ * Syntax: op vAA
+ */
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ GET_VREG rINST # rINST<- vAA
+ movl rINST, offGlue_retval(%edx) # glue->retval<- vAA
+ jmp common_returnFromMethod # jump to common return code
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_4: /* 0x12 */
+/* File: x86-atom/OP_CONST_4.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_CONST_4.S
+ *
+ * Code: Moves a literal to a register. Uses no substitutions.
+ *
+ * For: const/4
+ *
+ * Description: Move the given literal value (right-zero-extended to 32
+ * bits) into the specified register.
+ *
+ * Format: B|A|op (11n)
+ *
+ * Syntax: op vA, #+B
+ */
+
+ movl rINST, %edx # %edx<- BA
+ andl $15, rINST # rINST<- A
+ shl $24, %edx # %edx<- B000
+ sar $28, %edx # %edx<- right-zero-extended B
+ FFETCH_ADV 1, %eax # %eax<- next instruction hi; fetch, advance
+ SET_VREG %edx, rINST # vA<- %edx; literal
+ FGETOP_JMP 1, %eax # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_16: /* 0x13 */
+/* File: x86-atom/OP_CONST_16.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_CONST_16.S
+ *
+ * Code: Moves a literal to a register. Uses no substitutions.
+ *
+ * For: const/16
+ *
+ * Description: Move the given literal value (right-zero-extended to 32
+ * bits) into the specified register
+ *
+ * Format: AA|op BBBB (21s)
+ *
+ * Syntax: op vAA, #+BBBB
+ */
+
+ FETCHs 1, %edx # %edx<- BBBB
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ SET_VREG %edx rINST # vAA<- BBBB; literal
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST: /* 0x14 */
+/* File: x86-atom/OP_CONST.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_CONST.S
+ *
+ * Code: Move a literal to a register. Uses no substitutions.
+ *
+ * For: const
+ *
+ * Description: Move the given literal value into the specified register
+ *
+ * Format: AA|op BBBBlo BBBBhi (31i)
+ *
+ * Syntax: op vAA, #+BBBBBBBB
+ */
+
+ FETCH 2, %edx # %edx<- BBBBhi
+ FETCH 1, %ecx # %ecx<- BBBBlo
+ shl $16, %edx # move BBBB to high bits
+ or %edx, %ecx # %ecx<- #+BBBBBBBB
+ FFETCH_ADV 3, %eax # %eax<- next instruction hi; fetch, advance
+ SET_VREG %ecx, rINST # vAA<- %ecx; literal
+ FGETOP_JMP 3, %eax # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_HIGH16: /* 0x15 */
+/* File: x86-atom/OP_CONST_HIGH16.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_CONST_HIGH16.S
+ *
+ * Code: Move a literal to a register. Uses no substitutions.
+ *
+ * For: const/high16
+ *
+ * Description: Move the given literal value (right-zero-extended to 32
+ * bits) into the specified register
+ *
+ * Format: AA|op BBBB (21h)
+ *
+ * Syntax: op vAA, #+BBBB0000
+ */
+
+ FETCH 1, %ecx # %ecx<- 0000BBBB (zero-extended)
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ shl $16, %ecx # %ecx<- BBBB0000
+ SET_VREG %ecx, rINST # vAA<- %ecx; BBBB0000
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_WIDE_16: /* 0x16 */
+/* File: x86-atom/OP_CONST_WIDE_16.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_CONST_WIDE_16.S
+ *
+ * Code: Move a literal to a register. Uses no substitutions.
+ *
+ * For: const-wide/16
+ *
+ * Description: Move the given literal value (sign-extended to 64 bits)
+ * into the specified register-pair
+ *
+ * Format: AA|op BBBB (21s)
+ *
+ * Syntax: op vAA, #+BBBB
+ */
+
+ FETCHs 1, %ecx # %ecx<- ssssBBBB (sign-extended)
+ movl %ecx, %edx # %edx<- ssssBBBB (sign-extended)
+ sar $31, %ecx # %ecx<- sign bit
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movl %edx, (rFP, rINST, 4) # vAA<- ssssBBBB
+ movl %ecx, 4(rFP, rINST, 4) # vAA+1<- ssssssss
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_WIDE_32: /* 0x17 */
+/* File: x86-atom/OP_CONST_WIDE_32.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_CONST_WIDE_32.S
+ *
+ * Code: Move a literal to a register. Uses no substitutions.
+ *
+ * For: const-wide/32
+ *
+ * Description: Move the given literal value (sign-extended to 64 bits)
+ * into the specified register-pair
+ *
+ * Format: AA|op BBBBlo BBBBhi (31i)
+ *
+ * Syntax: op vAA, #+BBBBBBBB
+ */
+
+ FETCH 1, %edx # %edx<- BBBBlo
+ FETCHs 2, %ecx # %ecx<- BBBBhi
+ shl $16, %ecx # prepare to create #+BBBBBBBB
+ or %ecx, %edx # %edx<- %edx<- #+BBBBBBBB
+ sar $31, %ecx # %ecx<- sign bit
+ FFETCH_ADV 3, %eax # %eax<- next instruction hi; fetch, advance
+ movl %edx, (rFP, rINST, 4) # vAA<- BBBBBBBB
+ movl %ecx, 4(rFP, rINST, 4) # vAA+1<- ssssssss
+ FGETOP_JMP 3, %eax # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_WIDE: /* 0x18 */
+/* File: x86-atom/OP_CONST_WIDE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_CONST_WIDE.S
+ *
+ * Code: Move a literal to a register. Uses no substitutions.
+ *
+ * For: const-wide
+ *
+ * Description: Move the given literal value into the specified
+ * register pair
+ *
+ * Format: AA|op BBBBlolo BBBBlohi BBBBhilo BBBBhihi (51l)
+ *
+ * Syntax: op vAA, #+BBBBBBBBBBBBBBBB
+ */
+
+ FETCH 1, %ecx # %ecx<- BBBBlolo
+ FETCH 2, %edx # %edx<- BBBBlohi
+ shl $16, %edx # %edx<- prepare to create #+BBBBBBBBlo
+ or %edx, %ecx # %ecx<- #+BBBBBBBBlo
+ movl %ecx, (rFP, rINST, 4) # vAA <- #+BBBBBBBBlo
+ FETCH 3, %ecx # %ecx<- BBBBhilo
+ FETCH 4, %edx # %edx<- BBBBhihi
+ FFETCH_ADV 5, %eax # %eax<- next instruction hi; fetch, advance
+ shl $16, %edx # %edx<- prepare to create #+BBBBBBBBhi
+ or %edx, %ecx # %ecx<- #+BBBBBBBBhi
+ movl %ecx, 4(rFP, rINST, 4) # vAA+1 <- #+BBBBBBBBlo
+ FGETOP_JMP 5, %eax # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_WIDE_HIGH16: /* 0x19 */
+/* File: x86-atom/OP_CONST_WIDE_HIGH16.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_CONST_WIDE_HIGH16.S
+ *
+ * Code: Move a literal value to a register. Uses no substitutions.
+ *
+ * For: const-wide/high16
+ *
+ * Description: Move the given literal value (right-zero-extended to 64
+ * bits) into the specified register
+ *
+ * Format: AA|op BBBB (21h)
+ *
+ * Syntax: op vAA, #+BBBB000000000000
+ */
+
+ FETCH 1, %ecx # %ecx<- 0000BBBB (zero-extended)
+ shl $16, %ecx # rINST<- AA
+ movl $0, (rFP, rINST, 4) # vAAlow<- 00000000
+ movl %ecx, 4(rFP, rINST, 4) # vAAhigh<- %ecx; BBBB0000
+ FINISH 2 # jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_STRING: /* 0x1a */
+/* File: x86-atom/OP_CONST_STRING.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_CONST_STRING.S
+ *
+ * Code: Move a string reference to a register. Uses no substitutions.
+ *
+ * For: const/string
+ *
+ * Description: Move a referece to the string specified by the given
+ * index into the specified register. vAA <- pResString[BBBB]
+ *
+ * Format: AA|op BBBB (21c)
+ *
+ * Syntax: op vAA, string@BBBB
+ */
+
+ FETCH 1, %ecx # %ecx<- BBBB
+ movl rGLUE, %edx # get MterpGlue pointer
+ movl offGlue_methodClassDex(%edx), %eax # %eax<- glue->methodClassDex
+ movl offDvmDex_pResStrings(%eax), %eax # %eax<- glue->methodClassDex->pResStrings
+ movl (%eax, %ecx, 4), %eax # %eax<- pResStrings[BBBB]
+ cmp $0, %eax # check if string is resolved
+ je .LOP_CONST_STRING_resolve # resolve string reference
+ SET_VREG %eax, rINST # vAA<- %eax; pResString[BBBB]
+ FINISH 2 # jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_STRING_JUMBO: /* 0x1b */
+/* File: x86-atom/OP_CONST_STRING_JUMBO.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_CONST_STRING_JUMBO.S
+ *
+ * Code: Move a string reference to a register. Uses no substitutions.
+ *
+ * For: const/string-jumbo
+ *
+ * Description: Move a reference to the string specified by the given
+ * index into the specified register. vAA <- pResString[BBBB]
+ *
+ * Format: AA|op BBBBlo BBBBhi (31c)
+ *
+ * Syntax: op vAA, string@BBBBBBBB
+ */
+
+ movl rGLUE, %edx # get MterpGlue pointer
+ movl offGlue_methodClassDex(%edx), %eax # %eax<- glue->methodClassDex
+ movl offDvmDex_pResStrings(%eax), %eax # %eax<- glue->methodClassDex->pResStrings
+ FETCH 1, %ecx # %ecx<- BBBBlo
+ FETCH 2, %edx # %edx<- BBBBhi
+ shl $16, %edx # %edx<- prepare to create &BBBBBBBB
+ or %edx, %ecx # %ecx<- &BBBBBBBB
+ movl (%eax, %ecx, 4), %eax # %eax<- pResStrings[BBBB]
+ cmp $0, %eax # check if string is resolved
+ je .LOP_CONST_STRING_JUMBO_resolve # resolve string reference
+ SET_VREG %eax, rINST # vAA<- %eax; pResString[BBBB]
+ FINISH 3 # jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_CLASS: /* 0x1c */
+/* File: x86-atom/OP_CONST_CLASS.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_CONST_CLASS.S
+ *
+ * Code: Move a class reference to a register. Uses no substitutions.
+ *
+ * For: const/class
+ *
+ * Description: Move a reference to the class specified
+ * by the given index into the specified register.
+ * In the case where the indicated type is primitive,
+ * this will store a reference to the primitive type's
+ * degenerate class.
+ *
+ * Format: AA|op BBBBlo BBBBhi (21c)
+ *
+ * Syntax: op vAA, field@BBBB
+ */
+
+ movl rGLUE, %edx # get MterpGlue pointer
+ FETCH 1, %ecx # %ecx<- BBBB
+ movl offGlue_methodClassDex(%edx), %eax # %eax<- pDvmDex
+ movl offDvmDex_pResClasses(%eax), %eax # %eax<- pDvmDex->pResClasses
+ movl (%eax, %ecx, 4), %eax # %eax<- resolved class
+ cmp $0, %eax # check if classes is resolved before?
+ je .LOP_CONST_CLASS_resolve # resolve class
+ SET_VREG %eax, rINST # vAA<- resolved class
+ FINISH 2 # jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MONITOR_ENTER: /* 0x1d */
+/* File: x86-atom/OP_MONITOR_ENTER.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MONITOR_ENTER.S
+ *
+ * Code: Aquire a monitor
+ *
+ * For: monitor-enter
+ *
+ * Description: Aquire a monitor for the indicated object.
+ *
+ *
+ *
+ * Format: AA|op (11x)
+ *
+ * Syntax: op vAA
+ */
+
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ GET_VREG rINST # rINST<- vAA
+ cmp $0, rINST # check for null object
+ movl offGlue_self(%eax), %eax # %eax<- glue->self
+#ifdef WITH_MONITOR_TRACKING
+ EXPORT_PC # export PC so we can grab stack trace
+#endif
+ je common_errNullObject # handle null object
+# jmp .LOP_MONITOR_ENTER_finish
+#%break
+#.LOP_MONITOR_ENTER_finish:
+ movl rINST, -4(%esp) # push parameter reference
+ movl %eax, -8(%esp) # push parameter
+ lea -8(%esp), %esp
+ call dvmLockObject # call: (struct Thread* self,
+ # struct Object* obj)
+ # return: void
+ FFETCH_ADV 1, %edx # %edx<- next instruction hi; fetch, advance
+ lea 8(%esp), %esp
+#ifdef WITH_DEADLOCK_PREDICTION
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ movl offGlue_self(%eax), %eax # %eax<- glue->self
+ movl offThread_exception(%eax), %eax # %eax<- glue->self->exception
+ cmp $0, %eax # check for exception
+ jne common_exceptionThrown # handle exception
+#endif
+ FGETOP_JMP 1, %edx # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MONITOR_EXIT: /* 0x1e */
+/* File: x86-atom/OP_MONITOR_EXIT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MONITOR_EXIT.S
+ *
+ * Code: Release a monitor
+ *
+ * For: monitor-exit
+ *
+ * Description: Release a monitor for the indicated object. If this instruction needs
+ * to throw an execption, it must do so as if the pc has already
+ * advanced pased the instruction.
+ *
+ * Format: AA|op (11x)
+ *
+ * Syntax: op vAA
+ */
+
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ EXPORT_PC # export the pc
+ GET_VREG rINST # rINST<- vAA
+ cmp $0, rINST # rINST<- check for null object
+ je common_errNullObject # handle null object
+ push rINST # push parameter object
+ push offGlue_self(%eax) # push parameter self
+ call dvmUnlockObject # call: (struct Thread* self,
+ # struct Object* obj)
+ # return: bool
+ FINISH_FETCH_ADVANCE 1, %edx # advance pc before exception
+ cmp $0, %eax # check for success
+ lea 8(%esp), %esp
+ je common_exceptionThrown # handle exception
+ FINISH_JMP %edx # jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CHECK_CAST: /* 0x1f */
+/* File: x86-atom/OP_CHECK_CAST.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_CHECK_CAST.S
+ *
+ * Code: Checks to see if a cast is allowed. Uses no substitutions.
+ *
+ * For: check-cast
+ *
+ * Description: Throw if the reference in the given register cannot be
+ * cast to the indicated type. The type must be a reference
+ * type (not a primitive type).
+ *
+ * Format: AA|op BBBB (21c)
+ *
+ * Syntax: op vAA, type@BBBB
+ */
+
+ movl rGLUE, %edx # get MterpGlue pointer
+ movl offGlue_methodClassDex(%edx), %eax # %eax<- pDvmDex
+ GET_VREG rINST # rINST<- vAA
+ movl offDvmDex_pResClasses(%eax), %eax # %eax<- pDvmDex->pResClasses
+ cmp $0, rINST # check for null reference object
+ je .LOP_CHECK_CAST_okay # can always cast null object
+ FETCH 1, %ecx # %ecx<- BBBB
+ movl (%eax, %ecx, 4), %ecx # %ecx<- resolved class
+ cmp $0, %ecx # check if classes is resolved before?
+ je .LOP_CHECK_CAST_resolve # resolve class
+ jmp .LOP_CHECK_CAST_resolved # continue
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INSTANCE_OF: /* 0x20 */
+/* File: x86-atom/OP_INSTANCE_OF.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INSTANCE_OF.S
+ *
+ * Code: Checks if object is instance of a class. Uses no substitutions.
+ *
+ * For: instance-of
+ *
+ * Description: Store in the given destination register 1 if the indicated
+ * reference is an instance of the given type, or 0 if not.
+ * The type must be a reference type (not a primitive type).
+ *
+ * Format: B|A|op CCCC (22c)
+ *
+ * Syntax: op vA, vB, type@CCCC
+ * op vA, vB, field@CCCC
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $4, %edx # %edx<- B
+ GET_VREG %edx # %edx<- vB
+ cmp $0, %edx # check for null object
+ je .LOP_INSTANCE_OF_store # null object
+ jmp .LOP_INSTANCE_OF_break
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ARRAY_LENGTH: /* 0x21 */
+/* File: x86-atom/OP_ARRAY_LENGTH.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_ARRAY_LENGTH.S
+ *
+ * Code: 32-bit array length operation.
+ *
+ * For: array-length
+ *
+ * Description: Store the length of the indicated array in the given
+ * destination register. vB <- offArrayObject_length(vA)
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %eax # %eax<- BA
+ shr $4, %eax # %eax<- B
+ andl $15, rINST # rINST<- A
+ GET_VREG %eax # %eax<- vB
+ cmp $0, %eax # check for null array object
+ je common_errNullObject # handle null array object
+ FFETCH_ADV 1, %edx # %edx<- next instruction hi; fetch, advance
+ movl offArrayObject_length(%eax), %eax # %eax<- array length
+ movl %eax, (rFP, rINST, 4) # vA<- %eax; array length
+ FGETOP_JMP 1, %edx # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEW_INSTANCE: /* 0x22 */
+/* File: x86-atom/OP_NEW_INSTANCE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_NEW_INSTANCE.S
+ *
+ * Code: Create a new instance of a given type. Uses no substitutions.
+ *
+ * For: new-instance
+ *
+ * Description: Construct a new instance of the indicated type,
+ * storing a reference to it in the destination.
+ * The type must refer to a non-array class.
+ *
+ *
+ *
+ * Format: AA|op BBBB (21c)
+ *
+ * Syntax: op vAA, type@BBBB
+ * op vAA, field@BBBB
+ * op vAA, string@BBBB
+ */
+
+ movl rGLUE, %ecx # %ecx<- pMterpGlue
+ movl offGlue_methodClassDex(%ecx), %ecx # %ecx<- glue->pDvmDex
+ FETCH 1, %edx # %edx<- BBBB
+ movl offDvmDex_pResClasses(%ecx), %ecx # %ecx<- glue->pDvmDex->pResClasses
+ movl (%ecx, %edx, 4), %edx # %edx<- vB
+ EXPORT_PC # required for resolve
+ cmp $0, %edx # check for null
+ je .LOP_NEW_INSTANCE_resolve # need to resolve
+
+ /*
+ * %edx holds class object
+ */
+
+.LOP_NEW_INSTANCE_resolved:
+ movzbl offClassObject_status(%edx), %eax # %eax<- class status
+ cmp $CLASS_INITIALIZED, %eax # check if class is initialized
+ jne .LOP_NEW_INSTANCE_needinit # initialize class
+
+ /*
+ * %edx holds class object
+ */
+
+.LOP_NEW_INSTANCE_initialized:
+ testl $(ACC_INTERFACE|ACC_ABSTRACT), offClassObject_accessFlags(%edx)
+ mov $ALLOC_DONT_TRACK, %eax # %eax<- flag for alloc call
+ je .LOP_NEW_INSTANCE_finish # continue
+ jmp .LOP_NEW_INSTANCE_abstract # handle abstract or interface
+
+ /*
+ * %edx holds class object
+ * %eax holds flags for alloc call
+ */
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEW_ARRAY: /* 0x23 */
+/* File: x86-atom/OP_NEW_ARRAY.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_NEW_ARRAY.S
+ *
+ * Code: Create a new array. Uses no substitutions.
+ *
+ * For: new-array
+ *
+ * Description: Construct a new array of the indicated type and size.
+ * The type must be an array type.
+ *
+ * Format: B|A|op CCCC (22c)
+ *
+ * Syntax: op vA, vB, type@CCCC
+ * op vA, vB, field@CCCC
+ */
+
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ movl rINST, %edx # %edx<- BA
+ shr $4, %edx # %edx<- B
+ movl offGlue_methodClassDex(%eax), %eax # %eax<- glue->pDvmDex
+ FETCH 1, %ecx # %ecx<- CCCC
+ GET_VREG %edx # %edx<- vB
+ movl offDvmDex_pResClasses(%eax), %eax # %eax<- glue->pDvmDex->pResClasses
+ cmp $0, %edx # check for negative length
+ movl (%eax, %ecx, 4), %eax # %eax<- resolved class
+ js common_errNegativeArraySize # handle negative array length
+ cmp $0, %eax # check for null
+ EXPORT_PC # required for resolve
+ jne .LOP_NEW_ARRAY_finish # already resovled so continue
+ jmp .LOP_NEW_ARRAY_resolve # need to resolve
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FILLED_NEW_ARRAY: /* 0x24 */
+/* File: x86-atom/OP_FILLED_NEW_ARRAY.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_FILLED_NEW_ARRAY.S
+ *
+ * Code: Constructs and fills an array with the given data. Provides
+ *
+ * For: float-to-int
+ *
+ * Description: Construct an array of the given type and size,
+ * filling it with the supplied contents. The type
+ * must be an array type. The array's contents
+ * must be single-word. The constructed instance
+ * is stored as a result in the same way that the
+ * method invocation instructions store their results,
+ * so the constructed instance must be moved to a
+ * register with a subsequent move-result-object
+ * instruction.
+ *
+ * Format: B|A|op CCCC G|F|E|D (35c)
+ * AA|op BBBB CCCC (3rc) (range)
+ *
+ * Syntax: [B=5] op {vD, vE, vF, vG, vA}, vtaboff@CCCC
+ * [B=4] op {vD, vE, vF, vG}, vtaboff@CCCC
+ * [B=3] op {vD, vE, vF}, vtaboff@CCCC
+ * [B=2] op {vD, vE}, vtaboff@CCCC
+ * [B=1] op {vD}, vtaboff@CCCC
+ *
+ * op {vCCCC .. vNNNN}, meth@BBBB
+ * op {vCCCC .. vNNNN}, type@BBBB
+ */
+
+
+ movl rGLUE, %edx # %edx<- MterpGlue pointer
+ movl offGlue_methodClassDex(%edx), %edx # %edx<- glue->methodClassDex
+ movl offDvmDex_pResClasses(%edx), %edx # %edx<- glue->methodClassDex->pResClasses
+ FETCH 1, %ecx # %ecx<- BBBB
+ EXPORT_PC
+ movl (%edx, %ecx, 4), %eax # %eax<- possibly resolved class
+ cmp $0, %eax # %eax<- check if already resolved
+ jne .LOP_FILLED_NEW_ARRAY_continue
+ jmp .LOP_FILLED_NEW_ARRAY_break
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FILLED_NEW_ARRAY_RANGE: /* 0x25 */
+/* File: x86-atom/OP_FILLED_NEW_ARRAY_RANGE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_FILLED_NEW_ARRAY_RANGE.S
+ */
+
+/* File: x86-atom/OP_FILLED_NEW_ARRAY.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_FILLED_NEW_ARRAY.S
+ *
+ * Code: Constructs and fills an array with the given data. Provides
+ *
+ * For: float-to-int
+ *
+ * Description: Construct an array of the given type and size,
+ * filling it with the supplied contents. The type
+ * must be an array type. The array's contents
+ * must be single-word. The constructed instance
+ * is stored as a result in the same way that the
+ * method invocation instructions store their results,
+ * so the constructed instance must be moved to a
+ * register with a subsequent move-result-object
+ * instruction.
+ *
+ * Format: B|A|op CCCC G|F|E|D (35c)
+ * AA|op BBBB CCCC (3rc) (range)
+ *
+ * Syntax: [B=5] op {vD, vE, vF, vG, vA}, vtaboff@CCCC
+ * [B=4] op {vD, vE, vF, vG}, vtaboff@CCCC
+ * [B=3] op {vD, vE, vF}, vtaboff@CCCC
+ * [B=2] op {vD, vE}, vtaboff@CCCC
+ * [B=1] op {vD}, vtaboff@CCCC
+ *
+ * op {vCCCC .. vNNNN}, meth@BBBB
+ * op {vCCCC .. vNNNN}, type@BBBB
+ */
+
+
+ movl rGLUE, %edx # %edx<- MterpGlue pointer
+ movl offGlue_methodClassDex(%edx), %edx # %edx<- glue->methodClassDex
+ movl offDvmDex_pResClasses(%edx), %edx # %edx<- glue->methodClassDex->pResClasses
+ FETCH 1, %ecx # %ecx<- BBBB
+ EXPORT_PC
+ movl (%edx, %ecx, 4), %eax # %eax<- possibly resolved class
+ cmp $0, %eax # %eax<- check if already resolved
+ jne .LOP_FILLED_NEW_ARRAY_RANGE_continue
+ jmp .LOP_FILLED_NEW_ARRAY_RANGE_break
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FILL_ARRAY_DATA: /* 0x26 */
+/* File: x86-atom/OP_FILL_ARRAY_DATA.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_FILL_ARRAY_DATA.S
+ *
+ * Code: Fills an array with given data. Uses no substitutions.
+ *
+ * For: fill-array-data
+ *
+ * Description: Fill the given array with the idicated data. The reference
+ * must be an array of primitives, and the data table must
+ * match it in type and size
+ *
+ * Format: AA|op BBBBlo BBBBhi (31t)
+ *
+ * Syntax: op vAA, +BBBBBBBB
+ */
+
+ FETCH 1, %ecx # %ecx<- BBBBlo
+ FETCH 2, %edx # %edx<- BBBBhi
+ shl $16, %edx # prepare to create +BBBBBBBB
+ or %ecx, %edx # %edx<- +BBBBBBBB
+ lea (rPC, %edx, 2), %edx # %edx<- PC + +BBBBBBBB; array data location
+ EXPORT_PC
+ push %edx
+ push (rFP, rINST, 4)
+ call dvmInterpHandleFillArrayData # call: (ArrayObject* arrayObject, const u2* arrayData)
+ # return: bool
+ FFETCH_ADV 3, %edx # %edx<- next instruction hi; fetch, advance
+ cmp $0, %eax
+ lea 8(%esp), %esp
+ je common_exceptionThrown
+ FGETOP_JMP 3, %edx # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_THROW: /* 0x27 */
+/* File: x86-atom/OP_THROW.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_THROW.S
+ *
+ * Code: Throw an exception
+ *
+ * For: throw
+ *
+ * Description: Throw an exception object in the current thread.
+ *
+ * Format: AA|op (11x)
+ *
+ * Syntax: op vAA
+ */
+
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ EXPORT_PC # export the pc
+ GET_VREG rINST # rINST<- vAA
+ cmp $0, rINST # check for null
+ movl offGlue_self(%eax), %ecx # %ecx<- glue->self
+ je common_errNullObject # handle null object
+ movl rINST, offThread_exception(%ecx) # thread->exception<- object
+ jmp common_exceptionThrown # handle exception
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_GOTO: /* 0x28 */
+/* File: x86-atom/OP_GOTO.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_GOTO.S
+ *
+ * Code: Do an unconditional branch. Uses no substitutions.
+ *
+ * For: goto
+ *
+ * Description: Performs an unconditionally jump to the indicated instruction.
+ * The branch uses an 8-bit offset that cannot be zero.
+ *
+ * Format: AA|op (10t)
+ *
+ * Syntax: op +AA
+ */
+
+LOP_GOTO.S:
+
+ movsbl rINSTbl, %edx # %edx<- +AA
+ shl $1, %edx # %edx is shifted for byte offset
+ js common_periodicChecks_backwardBranch # do check on backwards branch
+ FINISH_RB %edx, %ecx # jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_GOTO_16: /* 0x29 */
+/* File: x86-atom/OP_GOTO_16.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_GOTO_16.S
+ *
+ * Code: Do an unconditional branch. Uses no substitutions.
+ *
+ * For: goto/16
+ *
+ * Description: Performs an unconditionally jump to the indicated instruction.
+ * The branch uses a 16-bit offset that cannot be zero.
+ *
+ * Format: ØØ|op AAAA (20t)
+ *
+ * Syntax: op +AAAA
+ */
+
+ FETCHs 1, %edx # %edx<- ssssAAAA (sign-extended)
+ shl $1, %edx # %edx is doubled to get the byte offset
+ js common_periodicChecks_backwardBranch # do check on backwards branch
+ FINISH_RB %edx, %ecx # jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_GOTO_32: /* 0x2a */
+/* File: x86-atom/OP_GOTO_32.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_GOTO_32.S
+ *
+ * Code: Do an unconditional branch. Uses no substitutions.
+ *
+ * For: goto/32
+ *
+ * Description: Performs an unconditionally jump to the indicated instruction.
+ * The branch uses a 32-bit offset that can be zero.
+ *
+ * Format: ØØ|op AAAAlo AAAAhi (30t)
+ *
+ * Syntax: op +AAAAAAAA
+ */
+
+ FETCH 1, %edx # %edx<- AAAAlo
+ FETCH 2, %ecx # %ecx<- AAAAhi
+ shl $16, %ecx # prepare to create +AAAAAAAA
+ or %ecx, %edx # %edx<- +AAAAAAAA
+ shl $1, %edx # %edx is doubled to get the byte offset
+ jle common_periodicChecks_backwardBranch # do check on backwards branch
+ FINISH_RB %edx, %ecx # jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_PACKED_SWITCH: /* 0x2b */
+/* File: x86-atom/OP_PACKED_SWITCH.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_PACKED_SWITCH.S
+ *
+ * Code: Jump to a new instruction using a jump table
+ *
+ * For: packed-switch, sparse-switch
+ *
+ * Description: Jump to a new instruction based on the value in the given
+ * register, using a table of offsets corresponding to each
+ * value in a particular integral range, or fall through to
+ * the next instruction if there is no match.
+ *
+ * Format: AA|op BBBBlo BBBBhi (31t)
+ *
+ * Syntax: op vAA, +BBBBBBBB
+ */
+
+
+ FETCH 1, %ecx # %ecx<- BBBBlo
+ FETCH 2, %edx # %edx<- BBBBhi
+ shl $16, %edx # prepare to create +BBBBBBBB
+ or %edx, %ecx # %ecx<- +BBBBBBBB
+ GET_VREG rINST # rINST<- vAA
+ movl rINST, -4(%esp) # push parameter vAA
+ lea (rPC, %ecx, 2), %ecx # %ecx<- PC + +BBBBBBBB*2
+ movl %ecx, -8(%esp) # push parameter PC + +BBBBBBBB*2
+ lea -8(%esp), %esp
+ call dvmInterpHandlePackedSwitch # call code-unit branch offset
+ shl $1, %eax # shift for byte offset
+ movl %eax, %edx # %edx<- offset
+ lea 8(%esp), %esp
+ jle common_periodicChecks_backwardBranch # do backward branch
+ jmp .LOP_PACKED_SWITCH_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPARSE_SWITCH: /* 0x2c */
+/* File: x86-atom/OP_SPARSE_SWITCH.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SPARSE_SWITCH.S
+ */
+
+/* File: x86-atom/OP_PACKED_SWITCH.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_PACKED_SWITCH.S
+ *
+ * Code: Jump to a new instruction using a jump table
+ *
+ * For: packed-switch, sparse-switch
+ *
+ * Description: Jump to a new instruction based on the value in the given
+ * register, using a table of offsets corresponding to each
+ * value in a particular integral range, or fall through to
+ * the next instruction if there is no match.
+ *
+ * Format: AA|op BBBBlo BBBBhi (31t)
+ *
+ * Syntax: op vAA, +BBBBBBBB
+ */
+
+
+ FETCH 1, %ecx # %ecx<- BBBBlo
+ FETCH 2, %edx # %edx<- BBBBhi
+ shl $16, %edx # prepare to create +BBBBBBBB
+ or %edx, %ecx # %ecx<- +BBBBBBBB
+ GET_VREG rINST # rINST<- vAA
+ movl rINST, -4(%esp) # push parameter vAA
+ lea (rPC, %ecx, 2), %ecx # %ecx<- PC + +BBBBBBBB*2
+ movl %ecx, -8(%esp) # push parameter PC + +BBBBBBBB*2
+ lea -8(%esp), %esp
+ call dvmInterpHandleSparseSwitch # call code-unit branch offset
+ shl $1, %eax # shift for byte offset
+ movl %eax, %edx # %edx<- offset
+ lea 8(%esp), %esp
+ jle common_periodicChecks_backwardBranch # do backward branch
+ jmp .LOP_SPARSE_SWITCH_finish
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CMPL_FLOAT: /* 0x2d */
+/* File: x86-atom/OP_CMPL_FLOAT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_CMPL_FLOAT.S
+ *
+ * Code: Provides a "nan" variable to specify the return value for
+ * NaN. Provides a variable "sod" which appends a "s" or a "d"
+ * to the move and comparison instructions, depending on if we
+ * are working with a float or a double. For instructions
+ * cmpx-float and cmpx-double, the x will be eiher a g or a l
+ * to specify positive or negative bias for NaN.
+ *
+ * For: cmpg-double, dmpg-float, cmpl-double, cmpl-float
+ *
+ * Description: Perform the indicated floating point or long comparison,
+ * storing 0 if the two arguments are equal, 1 if the second
+ * argument is larger, or -1 if the first argument is larger.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movss (rFP, %ecx, 4), %xmm0 # %xmm0<- vBB
+ comiss (rFP, %edx, 4), %xmm0 # do comparison
+ ja .LOP_CMPL_FLOAT_greater
+ jp .LOP_CMPL_FLOAT_finalNan
+ jz .LOP_CMPL_FLOAT_final
+
+.LOP_CMPL_FLOAT_less:
+ movl $0xFFFFFFFF, (rFP, rINST, 4) # vAA<- less than
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CMPG_FLOAT: /* 0x2e */
+/* File: x86-atom/OP_CMPG_FLOAT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_CMPG_FLOAT.S
+ */
+
+/* File: x86-atom/OP_CMPL_FLOAT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_CMPL_FLOAT.S
+ *
+ * Code: Provides a "nan" variable to specify the return value for
+ * NaN. Provides a variable "sod" which appends a "s" or a "d"
+ * to the move and comparison instructions, depending on if we
+ * are working with a float or a double. For instructions
+ * cmpx-float and cmpx-double, the x will be eiher a g or a l
+ * to specify positive or negative bias for NaN.
+ *
+ * For: cmpg-double, dmpg-float, cmpl-double, cmpl-float
+ *
+ * Description: Perform the indicated floating point or long comparison,
+ * storing 0 if the two arguments are equal, 1 if the second
+ * argument is larger, or -1 if the first argument is larger.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movss (rFP, %ecx, 4), %xmm0 # %xmm0<- vBB
+ comiss (rFP, %edx, 4), %xmm0 # do comparison
+ ja .LOP_CMPG_FLOAT_greater
+ jp .LOP_CMPG_FLOAT_finalNan
+ jz .LOP_CMPG_FLOAT_final
+
+.LOP_CMPG_FLOAT_less:
+ movl $0xFFFFFFFF, (rFP, rINST, 4) # vAA<- less than
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CMPL_DOUBLE: /* 0x2f */
+/* File: x86-atom/OP_CMPL_DOUBLE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_CMPL_DOUBLE.S
+ */
+
+/* File: x86-atom/OP_CMPL_FLOAT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_CMPL_FLOAT.S
+ *
+ * Code: Provides a "nan" variable to specify the return value for
+ * NaN. Provides a variable "sod" which appends a "s" or a "d"
+ * to the move and comparison instructions, depending on if we
+ * are working with a float or a double. For instructions
+ * cmpx-float and cmpx-double, the x will be eiher a g or a l
+ * to specify positive or negative bias for NaN.
+ *
+ * For: cmpg-double, dmpg-float, cmpl-double, cmpl-float
+ *
+ * Description: Perform the indicated floating point or long comparison,
+ * storing 0 if the two arguments are equal, 1 if the second
+ * argument is larger, or -1 if the first argument is larger.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movsd (rFP, %ecx, 4), %xmm0 # %xmm0<- vBB
+ comisd (rFP, %edx, 4), %xmm0 # do comparison
+ ja .LOP_CMPL_DOUBLE_greater
+ jp .LOP_CMPL_DOUBLE_finalNan
+ jz .LOP_CMPL_DOUBLE_final
+
+.LOP_CMPL_DOUBLE_less:
+ movl $0xFFFFFFFF, (rFP, rINST, 4) # vAA<- less than
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CMPG_DOUBLE: /* 0x30 */
+/* File: x86-atom/OP_CMPG_DOUBLE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_CMPG_DOUBLE.S
+ */
+
+/* File: x86-atom/OP_CMPL_FLOAT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_CMPL_FLOAT.S
+ *
+ * Code: Provides a "nan" variable to specify the return value for
+ * NaN. Provides a variable "sod" which appends a "s" or a "d"
+ * to the move and comparison instructions, depending on if we
+ * are working with a float or a double. For instructions
+ * cmpx-float and cmpx-double, the x will be eiher a g or a l
+ * to specify positive or negative bias for NaN.
+ *
+ * For: cmpg-double, dmpg-float, cmpl-double, cmpl-float
+ *
+ * Description: Perform the indicated floating point or long comparison,
+ * storing 0 if the two arguments are equal, 1 if the second
+ * argument is larger, or -1 if the first argument is larger.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movsd (rFP, %ecx, 4), %xmm0 # %xmm0<- vBB
+ comisd (rFP, %edx, 4), %xmm0 # do comparison
+ ja .LOP_CMPG_DOUBLE_greater
+ jp .LOP_CMPG_DOUBLE_finalNan
+ jz .LOP_CMPG_DOUBLE_final
+
+.LOP_CMPG_DOUBLE_less:
+ movl $0xFFFFFFFF, (rFP, rINST, 4) # vAA<- less than
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CMP_LONG: /* 0x31 */
+/* File: x86-atom/OP_CMP_LONG.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_CMP_LONG.S
+ *
+ * Code: Compare floating point values. Uses no substitutions.
+ *
+ * For: cmp-long
+ *
+ * Description: Perform a long comparison, storing 0 if the two
+ * arguments are equal, 1 if the second argument is larger
+ * or -1 if the first argument is larger.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ movl 4(rFP, %ecx, 4), %eax # %eax<- vBBhi
+ cmp 4(rFP, %edx, 4), %eax # compare vCChi and vBBhi
+ jl .LOP_CMP_LONG_less
+ jg .LOP_CMP_LONG_greater
+ movl (rFP, %ecx, 4), %eax # %eax<- vBBlo
+ cmp (rFP, %edx, 4), %eax # compare vCClo and vBBlo
+ ja .LOP_CMP_LONG_greater
+ jne .LOP_CMP_LONG_less
+ jmp .LOP_CMP_LONG_final
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_EQ: /* 0x32 */
+/* File: x86-atom/OP_IF_EQ.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IF_EQ.S
+ */
+
+/* File: x86-atom/bincmp.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: bincmp.S
+ *
+ * Code: Generic 32-bit comparison operation. Provides a "revcmp"
+ * variable to specify the reverse comparison to perform.
+ *
+ * For: if-eq, if-ge, if-gt, if-le, if-lt, if-ne
+ *
+ * Description: Branch to the given destination if the comparison
+ * test between the given registers values is true.
+ *
+ * Format: B|A|op CCCC (22t)
+ *
+ * Syntax: op vA, vB, +CCCC
+ */
+
+ movl rINST, %eax # %eax<- BA
+ andl $15, rINST # rINST<- A
+ shr $4, %eax # %eax<- B
+ GET_VREG rINST # rINST<- vA
+ movl $4, %edx # %edx<- 4
+ cmp (rFP, %eax, 4), rINST # compare vA and vB
+ jne 1f # goto next instruction if reverse
+ # comparison is true
+ FETCHs 1, %edx # %edx<- +CCCC, Branch offset
+ sal $1, %edx
+ js common_periodicChecks_backwardBranch
+1:
+ FINISH_RB %edx, %ecx # jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_NE: /* 0x33 */
+/* File: x86-atom/OP_IF_NE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IF_NE.S
+ */
+
+/* File: x86-atom/bincmp.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: bincmp.S
+ *
+ * Code: Generic 32-bit comparison operation. Provides a "revcmp"
+ * variable to specify the reverse comparison to perform.
+ *
+ * For: if-eq, if-ge, if-gt, if-le, if-lt, if-ne
+ *
+ * Description: Branch to the given destination if the comparison
+ * test between the given registers values is true.
+ *
+ * Format: B|A|op CCCC (22t)
+ *
+ * Syntax: op vA, vB, +CCCC
+ */
+
+ movl rINST, %eax # %eax<- BA
+ andl $15, rINST # rINST<- A
+ shr $4, %eax # %eax<- B
+ GET_VREG rINST # rINST<- vA
+ movl $4, %edx # %edx<- 4
+ cmp (rFP, %eax, 4), rINST # compare vA and vB
+ je 1f # goto next instruction if reverse
+ # comparison is true
+ FETCHs 1, %edx # %edx<- +CCCC, Branch offset
+ sal $1, %edx
+ js common_periodicChecks_backwardBranch
+1:
+ FINISH_RB %edx, %ecx # jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_LT: /* 0x34 */
+/* File: x86-atom/OP_IF_LT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IF_LT.S
+ */
+
+/* File: x86-atom/bincmp.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: bincmp.S
+ *
+ * Code: Generic 32-bit comparison operation. Provides a "revcmp"
+ * variable to specify the reverse comparison to perform.
+ *
+ * For: if-eq, if-ge, if-gt, if-le, if-lt, if-ne
+ *
+ * Description: Branch to the given destination if the comparison
+ * test between the given registers values is true.
+ *
+ * Format: B|A|op CCCC (22t)
+ *
+ * Syntax: op vA, vB, +CCCC
+ */
+
+ movl rINST, %eax # %eax<- BA
+ andl $15, rINST # rINST<- A
+ shr $4, %eax # %eax<- B
+ GET_VREG rINST # rINST<- vA
+ movl $4, %edx # %edx<- 4
+ cmp (rFP, %eax, 4), rINST # compare vA and vB
+ jge 1f # goto next instruction if reverse
+ # comparison is true
+ FETCHs 1, %edx # %edx<- +CCCC, Branch offset
+ sal $1, %edx
+ js common_periodicChecks_backwardBranch
+1:
+ FINISH_RB %edx, %ecx # jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_GE: /* 0x35 */
+/* File: x86-atom/OP_IF_GE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IF_GE.S
+ */
+
+/* File: x86-atom/bincmp.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: bincmp.S
+ *
+ * Code: Generic 32-bit comparison operation. Provides a "revcmp"
+ * variable to specify the reverse comparison to perform.
+ *
+ * For: if-eq, if-ge, if-gt, if-le, if-lt, if-ne
+ *
+ * Description: Branch to the given destination if the comparison
+ * test between the given registers values is true.
+ *
+ * Format: B|A|op CCCC (22t)
+ *
+ * Syntax: op vA, vB, +CCCC
+ */
+
+ movl rINST, %eax # %eax<- BA
+ andl $15, rINST # rINST<- A
+ shr $4, %eax # %eax<- B
+ GET_VREG rINST # rINST<- vA
+ movl $4, %edx # %edx<- 4
+ cmp (rFP, %eax, 4), rINST # compare vA and vB
+ jl 1f # goto next instruction if reverse
+ # comparison is true
+ FETCHs 1, %edx # %edx<- +CCCC, Branch offset
+ sal $1, %edx
+ js common_periodicChecks_backwardBranch
+1:
+ FINISH_RB %edx, %ecx # jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_GT: /* 0x36 */
+/* File: x86-atom/OP_IF_GT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IF_GT.S
+ */
+
+/* File: x86-atom/bincmp.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: bincmp.S
+ *
+ * Code: Generic 32-bit comparison operation. Provides a "revcmp"
+ * variable to specify the reverse comparison to perform.
+ *
+ * For: if-eq, if-ge, if-gt, if-le, if-lt, if-ne
+ *
+ * Description: Branch to the given destination if the comparison
+ * test between the given registers values is true.
+ *
+ * Format: B|A|op CCCC (22t)
+ *
+ * Syntax: op vA, vB, +CCCC
+ */
+
+ movl rINST, %eax # %eax<- BA
+ andl $15, rINST # rINST<- A
+ shr $4, %eax # %eax<- B
+ GET_VREG rINST # rINST<- vA
+ movl $4, %edx # %edx<- 4
+ cmp (rFP, %eax, 4), rINST # compare vA and vB
+ jle 1f # goto next instruction if reverse
+ # comparison is true
+ FETCHs 1, %edx # %edx<- +CCCC, Branch offset
+ sal $1, %edx
+ js common_periodicChecks_backwardBranch
+1:
+ FINISH_RB %edx, %ecx # jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_LE: /* 0x37 */
+/* File: x86-atom/OP_IF_LE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IF_LE.S
+ */
+
+/* File: x86-atom/bincmp.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: bincmp.S
+ *
+ * Code: Generic 32-bit comparison operation. Provides a "revcmp"
+ * variable to specify the reverse comparison to perform.
+ *
+ * For: if-eq, if-ge, if-gt, if-le, if-lt, if-ne
+ *
+ * Description: Branch to the given destination if the comparison
+ * test between the given registers values is true.
+ *
+ * Format: B|A|op CCCC (22t)
+ *
+ * Syntax: op vA, vB, +CCCC
+ */
+
+ movl rINST, %eax # %eax<- BA
+ andl $15, rINST # rINST<- A
+ shr $4, %eax # %eax<- B
+ GET_VREG rINST # rINST<- vA
+ movl $4, %edx # %edx<- 4
+ cmp (rFP, %eax, 4), rINST # compare vA and vB
+ jg 1f # goto next instruction if reverse
+ # comparison is true
+ FETCHs 1, %edx # %edx<- +CCCC, Branch offset
+ sal $1, %edx
+ js common_periodicChecks_backwardBranch
+1:
+ FINISH_RB %edx, %ecx # jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_EQZ: /* 0x38 */
+/* File: x86-atom/OP_IF_EQZ.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IF_EQZ.S
+ */
+
+/* File: x86-atom/zcmp.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: zcmp.S
+ *
+ * Code: Generic 32-bit comparison operation. Provides a "revcmp"
+ * variable to specify the reverse comparison to perform
+ *
+ * For: if-eqz, if-gez, if-gtz, if-lez, if-ltz, if-nez
+ *
+ * Description: Branch to the given destination if the given register's
+ * value compares with 0 as specified.
+ *
+ * Format: AA|op BBBB (21t)
+ *
+ * Syntax: op vAA, +BBBB
+ */
+
+ cmp $0, (rFP, rINST, 4) # compare vAA with zero
+ jne OP_IF_EQZ_2f # goto next instruction or branch
+ FETCHs 1, %edx # %edx<- BBBB; branch offset
+ sal $1, %edx # %edx<- adjust byte offset
+
+ /*
+ * Inline common_backwardBranch
+ */
+
+ js common_periodicChecks_backwardBranch # jump on backwards branch
+1:
+ FINISH_RB %edx, %ecx # jump to next instruction
+
+ /*
+ * FINISH code
+ */
+
+OP_IF_EQZ_2f:
+ movzbl 4(rPC), %edx # grab the next opcode
+ movzbl 5(rPC), rINST # update the instruction
+ addl $4, rPC # update the program counter
+ jmp *dvmAsmInstructionJmpTable(, %edx, 4) # jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_NEZ: /* 0x39 */
+/* File: x86-atom/OP_IF_NEZ.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IF_NEZ.S
+ */
+
+/* File: x86-atom/zcmp.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: zcmp.S
+ *
+ * Code: Generic 32-bit comparison operation. Provides a "revcmp"
+ * variable to specify the reverse comparison to perform
+ *
+ * For: if-eqz, if-gez, if-gtz, if-lez, if-ltz, if-nez
+ *
+ * Description: Branch to the given destination if the given register's
+ * value compares with 0 as specified.
+ *
+ * Format: AA|op BBBB (21t)
+ *
+ * Syntax: op vAA, +BBBB
+ */
+
+ cmp $0, (rFP, rINST, 4) # compare vAA with zero
+ je OP_IF_NEZ_2f # goto next instruction or branch
+ FETCHs 1, %edx # %edx<- BBBB; branch offset
+ sal $1, %edx # %edx<- adjust byte offset
+
+ /*
+ * Inline common_backwardBranch
+ */
+
+ js common_periodicChecks_backwardBranch # jump on backwards branch
+1:
+ FINISH_RB %edx, %ecx # jump to next instruction
+
+ /*
+ * FINISH code
+ */
+
+OP_IF_NEZ_2f:
+ movzbl 4(rPC), %edx # grab the next opcode
+ movzbl 5(rPC), rINST # update the instruction
+ addl $4, rPC # update the program counter
+ jmp *dvmAsmInstructionJmpTable(, %edx, 4) # jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_LTZ: /* 0x3a */
+/* File: x86-atom/OP_IF_LTZ.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IF_LTZ.S
+ */
+
+/* File: x86-atom/zcmp.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: zcmp.S
+ *
+ * Code: Generic 32-bit comparison operation. Provides a "revcmp"
+ * variable to specify the reverse comparison to perform
+ *
+ * For: if-eqz, if-gez, if-gtz, if-lez, if-ltz, if-nez
+ *
+ * Description: Branch to the given destination if the given register's
+ * value compares with 0 as specified.
+ *
+ * Format: AA|op BBBB (21t)
+ *
+ * Syntax: op vAA, +BBBB
+ */
+
+ cmp $0, (rFP, rINST, 4) # compare vAA with zero
+ jge OP_IF_LTZ_2f # goto next instruction or branch
+ FETCHs 1, %edx # %edx<- BBBB; branch offset
+ sal $1, %edx # %edx<- adjust byte offset
+
+ /*
+ * Inline common_backwardBranch
+ */
+
+ js common_periodicChecks_backwardBranch # jump on backwards branch
+1:
+ FINISH_RB %edx, %ecx # jump to next instruction
+
+ /*
+ * FINISH code
+ */
+
+OP_IF_LTZ_2f:
+ movzbl 4(rPC), %edx # grab the next opcode
+ movzbl 5(rPC), rINST # update the instruction
+ addl $4, rPC # update the program counter
+ jmp *dvmAsmInstructionJmpTable(, %edx, 4) # jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_GEZ: /* 0x3b */
+/* File: x86-atom/OP_IF_GEZ.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IF_GEZ.S
+ */
+
+/* File: x86-atom/zcmp.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: zcmp.S
+ *
+ * Code: Generic 32-bit comparison operation. Provides a "revcmp"
+ * variable to specify the reverse comparison to perform
+ *
+ * For: if-eqz, if-gez, if-gtz, if-lez, if-ltz, if-nez
+ *
+ * Description: Branch to the given destination if the given register's
+ * value compares with 0 as specified.
+ *
+ * Format: AA|op BBBB (21t)
+ *
+ * Syntax: op vAA, +BBBB
+ */
+
+ cmp $0, (rFP, rINST, 4) # compare vAA with zero
+ jl OP_IF_GEZ_2f # goto next instruction or branch
+ FETCHs 1, %edx # %edx<- BBBB; branch offset
+ sal $1, %edx # %edx<- adjust byte offset
+
+ /*
+ * Inline common_backwardBranch
+ */
+
+ js common_periodicChecks_backwardBranch # jump on backwards branch
+1:
+ FINISH_RB %edx, %ecx # jump to next instruction
+
+ /*
+ * FINISH code
+ */
+
+OP_IF_GEZ_2f:
+ movzbl 4(rPC), %edx # grab the next opcode
+ movzbl 5(rPC), rINST # update the instruction
+ addl $4, rPC # update the program counter
+ jmp *dvmAsmInstructionJmpTable(, %edx, 4) # jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_GTZ: /* 0x3c */
+/* File: x86-atom/OP_IF_GTZ.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IF_GTZ.S
+ */
+
+/* File: x86-atom/zcmp.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: zcmp.S
+ *
+ * Code: Generic 32-bit comparison operation. Provides a "revcmp"
+ * variable to specify the reverse comparison to perform
+ *
+ * For: if-eqz, if-gez, if-gtz, if-lez, if-ltz, if-nez
+ *
+ * Description: Branch to the given destination if the given register's
+ * value compares with 0 as specified.
+ *
+ * Format: AA|op BBBB (21t)
+ *
+ * Syntax: op vAA, +BBBB
+ */
+
+ cmp $0, (rFP, rINST, 4) # compare vAA with zero
+ jle OP_IF_GTZ_2f # goto next instruction or branch
+ FETCHs 1, %edx # %edx<- BBBB; branch offset
+ sal $1, %edx # %edx<- adjust byte offset
+
+ /*
+ * Inline common_backwardBranch
+ */
+
+ js common_periodicChecks_backwardBranch # jump on backwards branch
+1:
+ FINISH_RB %edx, %ecx # jump to next instruction
+
+ /*
+ * FINISH code
+ */
+
+OP_IF_GTZ_2f:
+ movzbl 4(rPC), %edx # grab the next opcode
+ movzbl 5(rPC), rINST # update the instruction
+ addl $4, rPC # update the program counter
+ jmp *dvmAsmInstructionJmpTable(, %edx, 4) # jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_LEZ: /* 0x3d */
+/* File: x86-atom/OP_IF_LEZ.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IF_LEZ.S
+ */
+
+/* File: x86-atom/zcmp.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: zcmp.S
+ *
+ * Code: Generic 32-bit comparison operation. Provides a "revcmp"
+ * variable to specify the reverse comparison to perform
+ *
+ * For: if-eqz, if-gez, if-gtz, if-lez, if-ltz, if-nez
+ *
+ * Description: Branch to the given destination if the given register's
+ * value compares with 0 as specified.
+ *
+ * Format: AA|op BBBB (21t)
+ *
+ * Syntax: op vAA, +BBBB
+ */
+
+ cmp $0, (rFP, rINST, 4) # compare vAA with zero
+ jg OP_IF_LEZ_2f # goto next instruction or branch
+ FETCHs 1, %edx # %edx<- BBBB; branch offset
+ sal $1, %edx # %edx<- adjust byte offset
+
+ /*
+ * Inline common_backwardBranch
+ */
+
+ js common_periodicChecks_backwardBranch # jump on backwards branch
+1:
+ FINISH_RB %edx, %ecx # jump to next instruction
+
+ /*
+ * FINISH code
+ */
+
+OP_IF_LEZ_2f:
+ movzbl 4(rPC), %edx # grab the next opcode
+ movzbl 5(rPC), rINST # update the instruction
+ addl $4, rPC # update the program counter
+ jmp *dvmAsmInstructionJmpTable(, %edx, 4) # jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_3E: /* 0x3e */
+/* File: x86-atom/OP_UNUSED_3E.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_UNUSED_3E.S
+ */
+
+/* File: x86-atom/unused.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: unused.S
+ *
+ * Code: Common code for unused bytecodes. Uses no subtitutions.
+ *
+ * For: all unused bytecodes
+ *
+ * Description: aborts if executed.
+ *
+ * Format: ØØ|op (10x)
+ *
+ * Syntax: op
+ */
+
+ call common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_3F: /* 0x3f */
+/* File: x86-atom/OP_UNUSED_3F.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_UNUSED_3F.S
+ */
+
+/* File: x86-atom/unused.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: unused.S
+ *
+ * Code: Common code for unused bytecodes. Uses no subtitutions.
+ *
+ * For: all unused bytecodes
+ *
+ * Description: aborts if executed.
+ *
+ * Format: ØØ|op (10x)
+ *
+ * Syntax: op
+ */
+
+ call common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_40: /* 0x40 */
+/* File: x86-atom/OP_UNUSED_40.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_UNUSED_40.S
+ */
+
+/* File: x86-atom/unused.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: unused.S
+ *
+ * Code: Common code for unused bytecodes. Uses no subtitutions.
+ *
+ * For: all unused bytecodes
+ *
+ * Description: aborts if executed.
+ *
+ * Format: ØØ|op (10x)
+ *
+ * Syntax: op
+ */
+
+ call common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_41: /* 0x41 */
+/* File: x86-atom/OP_UNUSED_41.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_UNUSED_41.S
+ */
+
+/* File: x86-atom/unused.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: unused.S
+ *
+ * Code: Common code for unused bytecodes. Uses no subtitutions.
+ *
+ * For: all unused bytecodes
+ *
+ * Description: aborts if executed.
+ *
+ * Format: ØØ|op (10x)
+ *
+ * Syntax: op
+ */
+
+ call common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_42: /* 0x42 */
+/* File: x86-atom/OP_UNUSED_42.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_UNUSED_42.S
+ */
+
+/* File: x86-atom/unused.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: unused.S
+ *
+ * Code: Common code for unused bytecodes. Uses no subtitutions.
+ *
+ * For: all unused bytecodes
+ *
+ * Description: aborts if executed.
+ *
+ * Format: ØØ|op (10x)
+ *
+ * Syntax: op
+ */
+
+ call common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_43: /* 0x43 */
+/* File: x86-atom/OP_UNUSED_43.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_UNUSED_43.S
+ */
+
+/* File: x86-atom/unused.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: unused.S
+ *
+ * Code: Common code for unused bytecodes. Uses no subtitutions.
+ *
+ * For: all unused bytecodes
+ *
+ * Description: aborts if executed.
+ *
+ * Format: ØØ|op (10x)
+ *
+ * Syntax: op
+ */
+
+ call common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET: /* 0x44 */
+/* File: x86-atom/OP_AGET.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_AGET.S
+ *
+ * Code: Generic 32-bit array "get" operation. Provides a "scale" variable
+ * to specify a scale value which depends on the width of the array
+ * elements. Provides a "mov" variable which determines the type of
+ * mov performed also dependent on the type of the array element.
+ *
+ * For: aget, aget-boolean, aget-byte, aget-char, aget-object, sget, aget-short
+ *
+ * Description: Perform an array get operation at the identified index
+ * of a given array; load the array value into the value
+ * register. vAA <- vBB[vCC].
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ GET_VREG %ecx # %ecx<- vBB
+ GET_VREG %edx # %edx<- vCC
+ cmp $0, %ecx # check for null array object
+ je common_errNullObject # handle null array object
+ cmp offArrayObject_length(%ecx), %edx # compare index to arrayObj->length
+ jnc common_errArrayIndex # handle index >= length, bail
+ lea (%ecx, %edx, 4), %ecx # %ecx<- &vBB[vCC]
+ # trying: lea (%ecx, %edx, scale), %ecx
+ # to reduce code size
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movl offArrayObject_contents(%ecx), %edx # %edx<- vBB[vCC]
+ # doing this and the previous instr
+ # with one instr was not faster
+ SET_VREG %edx rINST # vAA<- %edx; value
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_WIDE: /* 0x45 */
+/* File: x86-atom/OP_AGET_WIDE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_AGET_WIDE.S
+ *
+ * Code: 64-bit array get operation.
+ *
+ * For: aget-wide
+ *
+ * Description: Perform an array get operation at the identified index
+ * of a given array; load the array value into the destination
+ * register. vAA <- vBB[vCC].
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ GET_VREG %ecx # %ecx<- vBB
+ GET_VREG %edx # %edx<- vCC
+ cmp $0, %ecx # check for null array object
+ je common_errNullObject # handle null array object
+ cmp offArrayObject_length(%ecx), %edx # compare index to arrayObj->length
+ jnc common_errArrayIndex # handle index >= length, bail
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movq offArrayObject_contents(%ecx, %edx, 8), %xmm0 # %xmm0<- vBB[vCC]
+ movq %xmm0, (rFP, rINST, 4) # vAA<- %xmm0; value
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_OBJECT: /* 0x46 */
+/* File: x86-atom/OP_AGET_OBJECT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_AGET_OBJECT.S
+ */
+
+/* File: x86-atom/OP_AGET.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_AGET.S
+ *
+ * Code: Generic 32-bit array "get" operation. Provides a "scale" variable
+ * to specify a scale value which depends on the width of the array
+ * elements. Provides a "mov" variable which determines the type of
+ * mov performed also dependent on the type of the array element.
+ *
+ * For: aget, aget-boolean, aget-byte, aget-char, aget-object, sget, aget-short
+ *
+ * Description: Perform an array get operation at the identified index
+ * of a given array; load the array value into the value
+ * register. vAA <- vBB[vCC].
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ GET_VREG %ecx # %ecx<- vBB
+ GET_VREG %edx # %edx<- vCC
+ cmp $0, %ecx # check for null array object
+ je common_errNullObject # handle null array object
+ cmp offArrayObject_length(%ecx), %edx # compare index to arrayObj->length
+ jnc common_errArrayIndex # handle index >= length, bail
+ lea (%ecx, %edx, 4), %ecx # %ecx<- &vBB[vCC]
+ # trying: lea (%ecx, %edx, scale), %ecx
+ # to reduce code size
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movl offArrayObject_contents(%ecx), %edx # %edx<- vBB[vCC]
+ # doing this and the previous instr
+ # with one instr was not faster
+ SET_VREG %edx rINST # vAA<- %edx; value
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_BOOLEAN: /* 0x47 */
+/* File: x86-atom/OP_AGET_BOOLEAN.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_AGET_BOOLEAN.S
+ */
+
+/* File: x86-atom/OP_AGET.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_AGET.S
+ *
+ * Code: Generic 32-bit array "get" operation. Provides a "scale" variable
+ * to specify a scale value which depends on the width of the array
+ * elements. Provides a "mov" variable which determines the type of
+ * mov performed also dependent on the type of the array element.
+ *
+ * For: aget, aget-boolean, aget-byte, aget-char, aget-object, sget, aget-short
+ *
+ * Description: Perform an array get operation at the identified index
+ * of a given array; load the array value into the value
+ * register. vAA <- vBB[vCC].
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ GET_VREG %ecx # %ecx<- vBB
+ GET_VREG %edx # %edx<- vCC
+ cmp $0, %ecx # check for null array object
+ je common_errNullObject # handle null array object
+ cmp offArrayObject_length(%ecx), %edx # compare index to arrayObj->length
+ jnc common_errArrayIndex # handle index >= length, bail
+ lea (%ecx, %edx, 1), %ecx # %ecx<- &vBB[vCC]
+ # trying: lea (%ecx, %edx, scale), %ecx
+ # to reduce code size
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movzbl offArrayObject_contents(%ecx), %edx # %edx<- vBB[vCC]
+ # doing this and the previous instr
+ # with one instr was not faster
+ SET_VREG %edx rINST # vAA<- %edx; value
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_BYTE: /* 0x48 */
+/* File: x86-atom/OP_AGET_BYTE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_AGET_BYTE.S
+ */
+
+/* File: x86-atom/OP_AGET.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_AGET.S
+ *
+ * Code: Generic 32-bit array "get" operation. Provides a "scale" variable
+ * to specify a scale value which depends on the width of the array
+ * elements. Provides a "mov" variable which determines the type of
+ * mov performed also dependent on the type of the array element.
+ *
+ * For: aget, aget-boolean, aget-byte, aget-char, aget-object, sget, aget-short
+ *
+ * Description: Perform an array get operation at the identified index
+ * of a given array; load the array value into the value
+ * register. vAA <- vBB[vCC].
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ GET_VREG %ecx # %ecx<- vBB
+ GET_VREG %edx # %edx<- vCC
+ cmp $0, %ecx # check for null array object
+ je common_errNullObject # handle null array object
+ cmp offArrayObject_length(%ecx), %edx # compare index to arrayObj->length
+ jnc common_errArrayIndex # handle index >= length, bail
+ lea (%ecx, %edx, 1), %ecx # %ecx<- &vBB[vCC]
+ # trying: lea (%ecx, %edx, scale), %ecx
+ # to reduce code size
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movsbl offArrayObject_contents(%ecx), %edx # %edx<- vBB[vCC]
+ # doing this and the previous instr
+ # with one instr was not faster
+ SET_VREG %edx rINST # vAA<- %edx; value
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_CHAR: /* 0x49 */
+/* File: x86-atom/OP_AGET_CHAR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_AGET_CHAR.S
+ */
+
+/* File: x86-atom/OP_AGET.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_AGET.S
+ *
+ * Code: Generic 32-bit array "get" operation. Provides a "scale" variable
+ * to specify a scale value which depends on the width of the array
+ * elements. Provides a "mov" variable which determines the type of
+ * mov performed also dependent on the type of the array element.
+ *
+ * For: aget, aget-boolean, aget-byte, aget-char, aget-object, sget, aget-short
+ *
+ * Description: Perform an array get operation at the identified index
+ * of a given array; load the array value into the value
+ * register. vAA <- vBB[vCC].
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ GET_VREG %ecx # %ecx<- vBB
+ GET_VREG %edx # %edx<- vCC
+ cmp $0, %ecx # check for null array object
+ je common_errNullObject # handle null array object
+ cmp offArrayObject_length(%ecx), %edx # compare index to arrayObj->length
+ jnc common_errArrayIndex # handle index >= length, bail
+ lea (%ecx, %edx, 2), %ecx # %ecx<- &vBB[vCC]
+ # trying: lea (%ecx, %edx, scale), %ecx
+ # to reduce code size
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movzwl offArrayObject_contents(%ecx), %edx # %edx<- vBB[vCC]
+ # doing this and the previous instr
+ # with one instr was not faster
+ SET_VREG %edx rINST # vAA<- %edx; value
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_SHORT: /* 0x4a */
+/* File: x86-atom/OP_AGET_SHORT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_AGET_SHORT.S
+ */
+
+/* File: x86-atom/OP_AGET.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_AGET.S
+ *
+ * Code: Generic 32-bit array "get" operation. Provides a "scale" variable
+ * to specify a scale value which depends on the width of the array
+ * elements. Provides a "mov" variable which determines the type of
+ * mov performed also dependent on the type of the array element.
+ *
+ * For: aget, aget-boolean, aget-byte, aget-char, aget-object, sget, aget-short
+ *
+ * Description: Perform an array get operation at the identified index
+ * of a given array; load the array value into the value
+ * register. vAA <- vBB[vCC].
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ GET_VREG %ecx # %ecx<- vBB
+ GET_VREG %edx # %edx<- vCC
+ cmp $0, %ecx # check for null array object
+ je common_errNullObject # handle null array object
+ cmp offArrayObject_length(%ecx), %edx # compare index to arrayObj->length
+ jnc common_errArrayIndex # handle index >= length, bail
+ lea (%ecx, %edx, 2), %ecx # %ecx<- &vBB[vCC]
+ # trying: lea (%ecx, %edx, scale), %ecx
+ # to reduce code size
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movswl offArrayObject_contents(%ecx), %edx # %edx<- vBB[vCC]
+ # doing this and the previous instr
+ # with one instr was not faster
+ SET_VREG %edx rINST # vAA<- %edx; value
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT: /* 0x4b */
+/* File: x86-atom/OP_APUT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_APUT.S
+ *
+ * Code: Generic 32-bit array put operation. Provides a "scale" variable
+ * to specify a scale value which depends on the width of the array
+ * elements. Provides a "mov" variable which determines the type of
+ * move performed also dependent on the type of the array element.
+ * Provides a "value" register to specify the source of the move
+ *
+ * For: aput-boolean, aput-byte, aput-char, aput-object, aput-short
+ *
+ * Description: Perform an array put operation from the value register;
+ * store the value register at the identified index of a
+ * given array. vBB[vCC] <- vAA
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ GET_VREG %ecx # %ecx<- vBB
+ GET_VREG %edx # %edx<- vCC
+ cmp $0, %ecx # check for null array object
+ je common_errNullObject # handle null array object
+ cmp offArrayObject_length(%ecx), %edx # compare index to arrayObj->length
+ jnc common_errArrayIndex # handle index >= length, bail
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ lea (%ecx, %edx, 4), %ecx # %ecx<- &vBB[vCC]
+ GET_VREG rINST # rINST<- vAA
+ movl rINST, offArrayObject_contents(%ecx) # vBB[vCC]<- rINSTx; value
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_WIDE: /* 0x4c */
+/* File: x86-atom/OP_APUT_WIDE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_APUT_WIDE.S
+ *
+ * Code: 64-bit array put operation.
+ *
+ * For: aput-wide
+ *
+ * Description: Perform an array put operation from the value register;
+ * store the value register at the identified index of a
+ * given array. vBB[vCC] <- vAA.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ GET_VREG %ecx # %ecx<- vBB
+ GET_VREG %edx # %edx<- vCC
+ cmp $0, %ecx # check for null array object
+ je common_errNullObject # handle null array object
+ cmp offArrayObject_length(%ecx), %edx # compare index to arrayObj->length
+ jnc common_errArrayIndex # handle index >= length, bail
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movq (rFP, rINST, 4), %xmm0 # %xmm0<- vAA
+ movq %xmm0, offArrayObject_contents(%ecx, %edx, 8) # vBB[vCC]<- %xmm0; value
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_OBJECT: /* 0x4d */
+/* File: x86-atom/OP_APUT_OBJECT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_APUT_OBJECT.S
+ *
+ * Code: 32-bit array put operation. Provides an "scale" variable
+ * specify a scale value which depends on the width of the array
+ * elements. Provides a "mov" variable which determines the type of
+ * mov performed also dependent on the type of the array element.
+ * Provides a "value" register to specify the source of the mov
+ *
+ * For: aput-boolean, aput-byte, aput-char, aput-object, aput-short
+ *
+ * Description: Perform an array put operation from the value register;
+ * store the value register at the identified index of a
+ * given array. vBB[vCC] <- vAA
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %eax # %eax<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ GET_VREG %eax # %eax<- vBB
+ GET_VREG %edx # %edx<- vCC
+ cmp $0, %eax # check for null array object
+ je common_errNullObject # handle null array object
+ cmp offArrayObject_length(%eax), %edx # compare index to arrayObj->length
+ jnc common_errArrayIndex # handle index >= length, bail
+ GET_VREG rINST # rINST<- vAA
+ lea (%eax, %edx, 4), %edx # %edx<- &vBB[vCC]
+ cmp $0, rINST # check for null reference
+ je .LOP_APUT_OBJECT_skip_check # reference is null so skip type check
+ jmp .LOP_APUT_OBJECT_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_BOOLEAN: /* 0x4e */
+/* File: x86-atom/OP_APUT_BOOLEAN.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_APUT_BOOLEAN.S
+ */
+
+/* File: x86-atom/OP_APUT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_APUT.S
+ *
+ * Code: Generic 32-bit array put operation. Provides a "scale" variable
+ * to specify a scale value which depends on the width of the array
+ * elements. Provides a "mov" variable which determines the type of
+ * move performed also dependent on the type of the array element.
+ * Provides a "value" register to specify the source of the move
+ *
+ * For: aput-boolean, aput-byte, aput-char, aput-object, aput-short
+ *
+ * Description: Perform an array put operation from the value register;
+ * store the value register at the identified index of a
+ * given array. vBB[vCC] <- vAA
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ GET_VREG %ecx # %ecx<- vBB
+ GET_VREG %edx # %edx<- vCC
+ cmp $0, %ecx # check for null array object
+ je common_errNullObject # handle null array object
+ cmp offArrayObject_length(%ecx), %edx # compare index to arrayObj->length
+ jnc common_errArrayIndex # handle index >= length, bail
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ lea (%ecx, %edx, 1), %ecx # %ecx<- &vBB[vCC]
+ GET_VREG rINST # rINST<- vAA
+ movb rINSTbl, offArrayObject_contents(%ecx) # vBB[vCC]<- rINSTx; value
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_BYTE: /* 0x4f */
+/* File: x86-atom/OP_APUT_BYTE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_APUT_BYTE.S
+ */
+
+/* File: x86-atom/OP_APUT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_APUT.S
+ *
+ * Code: Generic 32-bit array put operation. Provides a "scale" variable
+ * to specify a scale value which depends on the width of the array
+ * elements. Provides a "mov" variable which determines the type of
+ * move performed also dependent on the type of the array element.
+ * Provides a "value" register to specify the source of the move
+ *
+ * For: aput-boolean, aput-byte, aput-char, aput-object, aput-short
+ *
+ * Description: Perform an array put operation from the value register;
+ * store the value register at the identified index of a
+ * given array. vBB[vCC] <- vAA
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ GET_VREG %ecx # %ecx<- vBB
+ GET_VREG %edx # %edx<- vCC
+ cmp $0, %ecx # check for null array object
+ je common_errNullObject # handle null array object
+ cmp offArrayObject_length(%ecx), %edx # compare index to arrayObj->length
+ jnc common_errArrayIndex # handle index >= length, bail
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ lea (%ecx, %edx, 1), %ecx # %ecx<- &vBB[vCC]
+ GET_VREG rINST # rINST<- vAA
+ movb rINSTbl, offArrayObject_contents(%ecx) # vBB[vCC]<- rINSTx; value
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_CHAR: /* 0x50 */
+/* File: x86-atom/OP_APUT_CHAR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_APUT_CHAR.S
+ */
+
+/* File: x86-atom/OP_APUT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_APUT.S
+ *
+ * Code: Generic 32-bit array put operation. Provides a "scale" variable
+ * to specify a scale value which depends on the width of the array
+ * elements. Provides a "mov" variable which determines the type of
+ * move performed also dependent on the type of the array element.
+ * Provides a "value" register to specify the source of the move
+ *
+ * For: aput-boolean, aput-byte, aput-char, aput-object, aput-short
+ *
+ * Description: Perform an array put operation from the value register;
+ * store the value register at the identified index of a
+ * given array. vBB[vCC] <- vAA
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ GET_VREG %ecx # %ecx<- vBB
+ GET_VREG %edx # %edx<- vCC
+ cmp $0, %ecx # check for null array object
+ je common_errNullObject # handle null array object
+ cmp offArrayObject_length(%ecx), %edx # compare index to arrayObj->length
+ jnc common_errArrayIndex # handle index >= length, bail
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ lea (%ecx, %edx, 2), %ecx # %ecx<- &vBB[vCC]
+ GET_VREG rINST # rINST<- vAA
+ movw rINSTw, offArrayObject_contents(%ecx) # vBB[vCC]<- rINSTx; value
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_SHORT: /* 0x51 */
+/* File: x86-atom/OP_APUT_SHORT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_APUT_SHORT.S
+ */
+
+/* File: x86-atom/OP_APUT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_APUT.S
+ *
+ * Code: Generic 32-bit array put operation. Provides a "scale" variable
+ * to specify a scale value which depends on the width of the array
+ * elements. Provides a "mov" variable which determines the type of
+ * move performed also dependent on the type of the array element.
+ * Provides a "value" register to specify the source of the move
+ *
+ * For: aput-boolean, aput-byte, aput-char, aput-object, aput-short
+ *
+ * Description: Perform an array put operation from the value register;
+ * store the value register at the identified index of a
+ * given array. vBB[vCC] <- vAA
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ GET_VREG %ecx # %ecx<- vBB
+ GET_VREG %edx # %edx<- vCC
+ cmp $0, %ecx # check for null array object
+ je common_errNullObject # handle null array object
+ cmp offArrayObject_length(%ecx), %edx # compare index to arrayObj->length
+ jnc common_errArrayIndex # handle index >= length, bail
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ lea (%ecx, %edx, 2), %ecx # %ecx<- &vBB[vCC]
+ GET_VREG rINST # rINST<- vAA
+ movw rINSTw, offArrayObject_contents(%ecx) # vBB[vCC]<- rINSTx; value
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET: /* 0x52 */
+/* File: x86-atom/OP_IGET.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IGET.S
+ *
+ * Code: Generic 32-bit instance field "get" operation. Provides a
+ * "mov" variable which determines the type of mov performed.
+ * Currently, none of the iget's use this variable - may want
+ * to change this, but seems ok for now.
+ *
+ * For: iget-boolean, iget-byte, iget-char, iget-object, iget
+ * iget-short
+ *
+ * Description: Perform the object instance field "get" operation
+ * with the identified field; load the instance value into
+ * the value register.
+ *
+ *
+ * Format: B|A|op CCCC (22c)
+ *
+ * Syntax: op vA, vB, type@CCCC
+ * op vA, vB, field@CCCC
+ */
+
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_methodClassDex(%edx), %edx # %edx<- pDvmDex
+ FETCH 1, %ecx # %ecx<- CCCC
+ movl offDvmDex_pResFields(%edx), %edx # %edx<- pDvmDex->pResFields
+ cmp $0, (%edx, %ecx, 4) # check for null ptr; resolved InstField ptr
+ movl (%edx, %ecx, 4), %eax # %eax<- resolved InstField ptr
+ jne .LOP_IGET_finish2
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_method(%edx), %edx # %edx <- current method
+ EXPORT_PC # in case an exception is thrown
+ movl offMethod_clazz(%edx), %edx # %edx<- method->clazz
+ movl %ecx, -4(%esp) # push parameter CCCC; field ref
+ movl %edx, -8(%esp) # push parameter method->clazz
+ lea -8(%esp), %esp
+ jmp .LOP_IGET_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_WIDE: /* 0x53 */
+/* File: x86-atom/OP_IGET_WIDE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IGET_WIDE.S
+ *
+ * Code: 64 bit instance field "get" operation. Uses no substitutions.
+ *
+ * For: iget-wide
+ *
+ * Description: Perform the object instance field "get" operation
+ * with the identified field; load the instance value into
+ * the value register.
+ *
+ * Format: B|A|op CCCC (22c)
+ *
+ * Syntax: op vA, vB, type@CCCC
+ * op vA, vB, field@CCCC
+ */
+
+ movl rGLUE, %eax # %eax<- MterpGlue pointer
+ movl offGlue_methodClassDex(%eax), %ecx # %ecx<- pDvmDex
+ movl offDvmDex_pResFields(%ecx), %ecx # %ecx<- CCCC
+ FETCH 1, %edx # %edx<- pDvmDex->pResFields
+ movl (%ecx, %edx, 4), %ecx # %ecx<- resolved InstField ptr
+ cmp $0, %ecx # check for null ptr; resolved InstField ptr
+ jne .LOP_IGET_WIDE_finish
+ movl offGlue_method(%eax), %ecx # %ecx <- current method
+ EXPORT_PC # in case an exception is thrown
+ movl offMethod_clazz(%ecx), %ecx # %ecx<- method->clazz
+ movl %ecx, -8(%esp) # push parameter CCCC; field ref
+ movl %edx, -4(%esp) # push parameter method->clazz
+ jmp .LOP_IGET_WIDE_finish2
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_OBJECT: /* 0x54 */
+/* File: x86-atom/OP_IGET_OBJECT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IGET_OBJECT.S
+ */
+
+/* File: x86-atom/OP_IGET.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IGET.S
+ *
+ * Code: Generic 32-bit instance field "get" operation. Provides a
+ * "mov" variable which determines the type of mov performed.
+ * Currently, none of the iget's use this variable - may want
+ * to change this, but seems ok for now.
+ *
+ * For: iget-boolean, iget-byte, iget-char, iget-object, iget
+ * iget-short
+ *
+ * Description: Perform the object instance field "get" operation
+ * with the identified field; load the instance value into
+ * the value register.
+ *
+ *
+ * Format: B|A|op CCCC (22c)
+ *
+ * Syntax: op vA, vB, type@CCCC
+ * op vA, vB, field@CCCC
+ */
+
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_methodClassDex(%edx), %edx # %edx<- pDvmDex
+ FETCH 1, %ecx # %ecx<- CCCC
+ movl offDvmDex_pResFields(%edx), %edx # %edx<- pDvmDex->pResFields
+ cmp $0, (%edx, %ecx, 4) # check for null ptr; resolved InstField ptr
+ movl (%edx, %ecx, 4), %eax # %eax<- resolved InstField ptr
+ jne .LOP_IGET_OBJECT_finish2
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_method(%edx), %edx # %edx <- current method
+ EXPORT_PC # in case an exception is thrown
+ movl offMethod_clazz(%edx), %edx # %edx<- method->clazz
+ movl %ecx, -4(%esp) # push parameter CCCC; field ref
+ movl %edx, -8(%esp) # push parameter method->clazz
+ lea -8(%esp), %esp
+ jmp .LOP_IGET_OBJECT_finish
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_BOOLEAN: /* 0x55 */
+/* File: x86-atom/OP_IGET_BOOLEAN.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IGET_BOOLEAN.S
+ */
+
+/* File: x86-atom/OP_IGET.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IGET.S
+ *
+ * Code: Generic 32-bit instance field "get" operation. Provides a
+ * "mov" variable which determines the type of mov performed.
+ * Currently, none of the iget's use this variable - may want
+ * to change this, but seems ok for now.
+ *
+ * For: iget-boolean, iget-byte, iget-char, iget-object, iget
+ * iget-short
+ *
+ * Description: Perform the object instance field "get" operation
+ * with the identified field; load the instance value into
+ * the value register.
+ *
+ *
+ * Format: B|A|op CCCC (22c)
+ *
+ * Syntax: op vA, vB, type@CCCC
+ * op vA, vB, field@CCCC
+ */
+
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_methodClassDex(%edx), %edx # %edx<- pDvmDex
+ FETCH 1, %ecx # %ecx<- CCCC
+ movl offDvmDex_pResFields(%edx), %edx # %edx<- pDvmDex->pResFields
+ cmp $0, (%edx, %ecx, 4) # check for null ptr; resolved InstField ptr
+ movl (%edx, %ecx, 4), %eax # %eax<- resolved InstField ptr
+ jne .LOP_IGET_BOOLEAN_finish2
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_method(%edx), %edx # %edx <- current method
+ EXPORT_PC # in case an exception is thrown
+ movl offMethod_clazz(%edx), %edx # %edx<- method->clazz
+ movl %ecx, -4(%esp) # push parameter CCCC; field ref
+ movl %edx, -8(%esp) # push parameter method->clazz
+ lea -8(%esp), %esp
+ jmp .LOP_IGET_BOOLEAN_finish
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_BYTE: /* 0x56 */
+/* File: x86-atom/OP_IGET_BYTE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IGET_BYTE.S
+ */
+
+/* File: x86-atom/OP_IGET.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IGET.S
+ *
+ * Code: Generic 32-bit instance field "get" operation. Provides a
+ * "mov" variable which determines the type of mov performed.
+ * Currently, none of the iget's use this variable - may want
+ * to change this, but seems ok for now.
+ *
+ * For: iget-boolean, iget-byte, iget-char, iget-object, iget
+ * iget-short
+ *
+ * Description: Perform the object instance field "get" operation
+ * with the identified field; load the instance value into
+ * the value register.
+ *
+ *
+ * Format: B|A|op CCCC (22c)
+ *
+ * Syntax: op vA, vB, type@CCCC
+ * op vA, vB, field@CCCC
+ */
+
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_methodClassDex(%edx), %edx # %edx<- pDvmDex
+ FETCH 1, %ecx # %ecx<- CCCC
+ movl offDvmDex_pResFields(%edx), %edx # %edx<- pDvmDex->pResFields
+ cmp $0, (%edx, %ecx, 4) # check for null ptr; resolved InstField ptr
+ movl (%edx, %ecx, 4), %eax # %eax<- resolved InstField ptr
+ jne .LOP_IGET_BYTE_finish2
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_method(%edx), %edx # %edx <- current method
+ EXPORT_PC # in case an exception is thrown
+ movl offMethod_clazz(%edx), %edx # %edx<- method->clazz
+ movl %ecx, -4(%esp) # push parameter CCCC; field ref
+ movl %edx, -8(%esp) # push parameter method->clazz
+ lea -8(%esp), %esp
+ jmp .LOP_IGET_BYTE_finish
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_CHAR: /* 0x57 */
+/* File: x86-atom/OP_IGET_CHAR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IGET_CHAR.S
+ */
+
+/* File: x86-atom/OP_IGET.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IGET.S
+ *
+ * Code: Generic 32-bit instance field "get" operation. Provides a
+ * "mov" variable which determines the type of mov performed.
+ * Currently, none of the iget's use this variable - may want
+ * to change this, but seems ok for now.
+ *
+ * For: iget-boolean, iget-byte, iget-char, iget-object, iget
+ * iget-short
+ *
+ * Description: Perform the object instance field "get" operation
+ * with the identified field; load the instance value into
+ * the value register.
+ *
+ *
+ * Format: B|A|op CCCC (22c)
+ *
+ * Syntax: op vA, vB, type@CCCC
+ * op vA, vB, field@CCCC
+ */
+
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_methodClassDex(%edx), %edx # %edx<- pDvmDex
+ FETCH 1, %ecx # %ecx<- CCCC
+ movl offDvmDex_pResFields(%edx), %edx # %edx<- pDvmDex->pResFields
+ cmp $0, (%edx, %ecx, 4) # check for null ptr; resolved InstField ptr
+ movl (%edx, %ecx, 4), %eax # %eax<- resolved InstField ptr
+ jne .LOP_IGET_CHAR_finish2
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_method(%edx), %edx # %edx <- current method
+ EXPORT_PC # in case an exception is thrown
+ movl offMethod_clazz(%edx), %edx # %edx<- method->clazz
+ movl %ecx, -4(%esp) # push parameter CCCC; field ref
+ movl %edx, -8(%esp) # push parameter method->clazz
+ lea -8(%esp), %esp
+ jmp .LOP_IGET_CHAR_finish
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_SHORT: /* 0x58 */
+/* File: x86-atom/OP_IGET_SHORT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IGET_SHORT.S
+ */
+
+/* File: x86-atom/OP_IGET.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IGET.S
+ *
+ * Code: Generic 32-bit instance field "get" operation. Provides a
+ * "mov" variable which determines the type of mov performed.
+ * Currently, none of the iget's use this variable - may want
+ * to change this, but seems ok for now.
+ *
+ * For: iget-boolean, iget-byte, iget-char, iget-object, iget
+ * iget-short
+ *
+ * Description: Perform the object instance field "get" operation
+ * with the identified field; load the instance value into
+ * the value register.
+ *
+ *
+ * Format: B|A|op CCCC (22c)
+ *
+ * Syntax: op vA, vB, type@CCCC
+ * op vA, vB, field@CCCC
+ */
+
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_methodClassDex(%edx), %edx # %edx<- pDvmDex
+ FETCH 1, %ecx # %ecx<- CCCC
+ movl offDvmDex_pResFields(%edx), %edx # %edx<- pDvmDex->pResFields
+ cmp $0, (%edx, %ecx, 4) # check for null ptr; resolved InstField ptr
+ movl (%edx, %ecx, 4), %eax # %eax<- resolved InstField ptr
+ jne .LOP_IGET_SHORT_finish2
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_method(%edx), %edx # %edx <- current method
+ EXPORT_PC # in case an exception is thrown
+ movl offMethod_clazz(%edx), %edx # %edx<- method->clazz
+ movl %ecx, -4(%esp) # push parameter CCCC; field ref
+ movl %edx, -8(%esp) # push parameter method->clazz
+ lea -8(%esp), %esp
+ jmp .LOP_IGET_SHORT_finish
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT: /* 0x59 */
+/* File: x86-atom/OP_IPUT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IPUT.S
+ *
+ * Code: Generic 32-bit instance field "put" operation. Provides a
+ * "mov" variable which determines the type of mov performed.
+ * Currently, none of the iput's use this variable - may want
+ * to change this, but seems ok for now.
+ *
+ * For: iput-boolean, iput-byte, iput-char, iput-object, iput
+ * iput-short
+ *
+ * Description: Perform the object instance field "get" operation
+ * with the identified field; load the instance value into
+ * the value register.
+ *
+ *
+ * Format: B|A|op CCCC (22c)
+ *
+ * Syntax: op vA, vB, type@CCCC
+ * op vA, vB, field@CCCC
+ */
+
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_methodClassDex(%edx), %edx # %edx<- pDvmDex
+ FETCH 1, %ecx # %ecx<- CCCC
+ movl offDvmDex_pResFields(%edx), %edx # %edx<- pDvmDex->pResFields
+ cmp $0, (%edx, %ecx, 4) # check for null ptr; resolved InstField ptr
+ movl (%edx, %ecx, 4), %eax # %eax<- resolved InstField ptr
+ jne .LOP_IPUT_finish2
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ jmp .LOP_IPUT_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_WIDE: /* 0x5a */
+/* File: x86-atom/OP_IPUT_WIDE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IPUT_WIDE.S
+ *
+ * Code: 64 bit instance field "put" operation. Uses no substitutions.
+ *
+ * For: iget-wide
+ *
+ * Description: Perform the object instance field "put" operation
+ * with the identified field; load the instance value into
+ * the value register.
+ *
+ * Format: B|A|op CCCC (22c)
+ *
+ * Syntax: op vA, vB, type@CCCC
+ * op vA, vB, field@CCCC
+ */
+
+ movl rGLUE, %eax # %eax<- MterpGlue pointer
+ movl offGlue_methodClassDex(%eax), %ecx # %ecx<- pDvmDex
+ movl offDvmDex_pResFields(%ecx), %ecx # %ecx<- CCCC
+ FETCH 1, %edx # %edx<- pDvmDex->pResFields
+ movl (%ecx, %edx, 4), %ecx # %ecx<- resolved InstField ptr
+ cmp $0, %ecx # check for null ptr; resolved InstField ptr
+ jne .LOP_IPUT_WIDE_finish
+ movl offGlue_method(%eax), %ecx # %ecx <- current method
+ EXPORT_PC # in case an exception is thrown
+ movl offMethod_clazz(%ecx), %ecx # %ecx<- method->clazz
+ movl %ecx, -8(%esp) # push parameter CCCC; field ref
+ movl %edx, -4(%esp) # push parameter method->clazz
+ jmp .LOP_IPUT_WIDE_finish2
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_OBJECT: /* 0x5b */
+/* File: x86-atom/OP_IPUT_OBJECT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IPUT.S
+ *
+ * Code: Generic 32-bit instance field "put" operation. Provides a
+ * "mov" variable which determines the type of mov performed.
+ * Currently, none of the iput's use this variable - may want
+ * to change this, but seems ok for now.
+ *
+ * For: iput-boolean, iput-byte, iput-char, iput-object, iput
+ * iput-short
+ *
+ * Description: Perform the object instance field "get" operation
+ * with the identified field; load the instance value into
+ * the value register.
+ *
+ *
+ * Format: B|A|op CCCC (22c)
+ *
+ * Syntax: op vA, vB, type@CCCC
+ * op vA, vB, field@CCCC
+ */
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_methodClassDex(%edx), %edx # %edx<- pDvmDex
+ FETCH 1, %ecx # %ecx<- CCCC
+ movl offDvmDex_pResFields(%edx), %edx # %edx<- pDvmDex->pResFields
+ cmp $0, (%edx, %ecx, 4) # check for null ptr; resolved InstField ptr
+ movl (%edx, %ecx, 4), %eax # %eax<- resolved InstField ptr
+ jne .LOP_IPUT_OBJECT_finish2
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ jmp .LOP_IPUT_OBJECT_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_BOOLEAN: /* 0x5c */
+/* File: x86-atom/OP_IPUT_BOOLEAN.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IPUT_BOOLEAN.S
+ */
+
+/* File: x86-atom/OP_IPUT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IPUT.S
+ *
+ * Code: Generic 32-bit instance field "put" operation. Provides a
+ * "mov" variable which determines the type of mov performed.
+ * Currently, none of the iput's use this variable - may want
+ * to change this, but seems ok for now.
+ *
+ * For: iput-boolean, iput-byte, iput-char, iput-object, iput
+ * iput-short
+ *
+ * Description: Perform the object instance field "get" operation
+ * with the identified field; load the instance value into
+ * the value register.
+ *
+ *
+ * Format: B|A|op CCCC (22c)
+ *
+ * Syntax: op vA, vB, type@CCCC
+ * op vA, vB, field@CCCC
+ */
+
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_methodClassDex(%edx), %edx # %edx<- pDvmDex
+ FETCH 1, %ecx # %ecx<- CCCC
+ movl offDvmDex_pResFields(%edx), %edx # %edx<- pDvmDex->pResFields
+ cmp $0, (%edx, %ecx, 4) # check for null ptr; resolved InstField ptr
+ movl (%edx, %ecx, 4), %eax # %eax<- resolved InstField ptr
+ jne .LOP_IPUT_BOOLEAN_finish2
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ jmp .LOP_IPUT_BOOLEAN_finish
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_BYTE: /* 0x5d */
+/* File: x86-atom/OP_IPUT_BYTE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IPUT_BYTE.S
+ */
+
+/* File: x86-atom/OP_IPUT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IPUT.S
+ *
+ * Code: Generic 32-bit instance field "put" operation. Provides a
+ * "mov" variable which determines the type of mov performed.
+ * Currently, none of the iput's use this variable - may want
+ * to change this, but seems ok for now.
+ *
+ * For: iput-boolean, iput-byte, iput-char, iput-object, iput
+ * iput-short
+ *
+ * Description: Perform the object instance field "get" operation
+ * with the identified field; load the instance value into
+ * the value register.
+ *
+ *
+ * Format: B|A|op CCCC (22c)
+ *
+ * Syntax: op vA, vB, type@CCCC
+ * op vA, vB, field@CCCC
+ */
+
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_methodClassDex(%edx), %edx # %edx<- pDvmDex
+ FETCH 1, %ecx # %ecx<- CCCC
+ movl offDvmDex_pResFields(%edx), %edx # %edx<- pDvmDex->pResFields
+ cmp $0, (%edx, %ecx, 4) # check for null ptr; resolved InstField ptr
+ movl (%edx, %ecx, 4), %eax # %eax<- resolved InstField ptr
+ jne .LOP_IPUT_BYTE_finish2
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ jmp .LOP_IPUT_BYTE_finish
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_CHAR: /* 0x5e */
+/* File: x86-atom/OP_IPUT_CHAR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IPUT_CHAR.S
+ */
+
+/* File: x86-atom/OP_IPUT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IPUT.S
+ *
+ * Code: Generic 32-bit instance field "put" operation. Provides a
+ * "mov" variable which determines the type of mov performed.
+ * Currently, none of the iput's use this variable - may want
+ * to change this, but seems ok for now.
+ *
+ * For: iput-boolean, iput-byte, iput-char, iput-object, iput
+ * iput-short
+ *
+ * Description: Perform the object instance field "get" operation
+ * with the identified field; load the instance value into
+ * the value register.
+ *
+ *
+ * Format: B|A|op CCCC (22c)
+ *
+ * Syntax: op vA, vB, type@CCCC
+ * op vA, vB, field@CCCC
+ */
+
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_methodClassDex(%edx), %edx # %edx<- pDvmDex
+ FETCH 1, %ecx # %ecx<- CCCC
+ movl offDvmDex_pResFields(%edx), %edx # %edx<- pDvmDex->pResFields
+ cmp $0, (%edx, %ecx, 4) # check for null ptr; resolved InstField ptr
+ movl (%edx, %ecx, 4), %eax # %eax<- resolved InstField ptr
+ jne .LOP_IPUT_CHAR_finish2
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ jmp .LOP_IPUT_CHAR_finish
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_SHORT: /* 0x5f */
+/* File: x86-atom/OP_IPUT_SHORT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IPUT_SHORT.S
+ */
+
+/* File: x86-atom/OP_IPUT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IPUT.S
+ *
+ * Code: Generic 32-bit instance field "put" operation. Provides a
+ * "mov" variable which determines the type of mov performed.
+ * Currently, none of the iput's use this variable - may want
+ * to change this, but seems ok for now.
+ *
+ * For: iput-boolean, iput-byte, iput-char, iput-object, iput
+ * iput-short
+ *
+ * Description: Perform the object instance field "get" operation
+ * with the identified field; load the instance value into
+ * the value register.
+ *
+ *
+ * Format: B|A|op CCCC (22c)
+ *
+ * Syntax: op vA, vB, type@CCCC
+ * op vA, vB, field@CCCC
+ */
+
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_methodClassDex(%edx), %edx # %edx<- pDvmDex
+ FETCH 1, %ecx # %ecx<- CCCC
+ movl offDvmDex_pResFields(%edx), %edx # %edx<- pDvmDex->pResFields
+ cmp $0, (%edx, %ecx, 4) # check for null ptr; resolved InstField ptr
+ movl (%edx, %ecx, 4), %eax # %eax<- resolved InstField ptr
+ jne .LOP_IPUT_SHORT_finish2
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ jmp .LOP_IPUT_SHORT_finish
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET: /* 0x60 */
+/* File: x86-atom/OP_SGET.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SGET.S
+ *
+ * Code: Generic 32-bit static field "get" operation. Uses no substitutions.
+ *
+ * For: sget-boolean, sget-byte, sget-char, sget-object, sget, sget-short
+ *
+ * Description: Perform the identified object static field operation
+ * with the identified static field; load the field value
+ * into the value register.
+ *
+ * Format: AA|op BBBB (21c)
+ *
+ * Syntax: op vAA, string@BBBB
+ */
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_methodClassDex(%edx), %ecx # %ecx<- glue->pDvmDex
+ FETCH 1, %eax # %eax<- BBBB
+ movl offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+ cmp $0, (%ecx, %eax, 4) # check for null ptr; resolved StaticField ptr
+ movl (%ecx, %eax, 4), %ecx # %ecx<- resolved StaticField ptr
+ je .LOP_SGET_resolve
+ jmp .LOP_SGET_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_WIDE: /* 0x61 */
+/* File: x86-atom/OP_SGET_WIDE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SGET_WIDE.S
+ *
+ * Code: 64-bit static field "get" operation. Uses no substitutions.
+ *
+ * For: sget-wide
+ *
+ * Description: Perform the identified object static field operation
+ * with the identified static field, loading or storing
+ * into the value register.
+ *
+ * Format: AA|op BBBB (21c)
+ *
+ * Syntax: op vAA, string@BBBB
+ */
+
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ movl offGlue_methodClassDex(%eax), %ecx # %ecx<- glue->pDvmDex
+ FETCH 1, %edx # %edx<- BBBB
+ movl offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+ cmp $0, (%ecx, %edx, 4) # check for null ptr; resolved StaticField ptr
+ movl (%ecx, %edx, 4), %ecx # %ecx<- resolved StaticField ptr
+ je .LOP_SGET_WIDE_resolve
+
+.LOP_SGET_WIDE_finish:
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movq offStaticField_value(%ecx), %xmm0 # %xmm0<- field value
+ movq %xmm0, (rFP, rINST, 4) # vAA<- field value
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_OBJECT: /* 0x62 */
+/* File: x86-atom/OP_SGET_OBJECT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SGET_OBJECT.S
+ */
+
+/* File: x86-atom/OP_SGET.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SGET.S
+ *
+ * Code: Generic 32-bit static field "get" operation. Uses no substitutions.
+ *
+ * For: sget-boolean, sget-byte, sget-char, sget-object, sget, sget-short
+ *
+ * Description: Perform the identified object static field operation
+ * with the identified static field; load the field value
+ * into the value register.
+ *
+ * Format: AA|op BBBB (21c)
+ *
+ * Syntax: op vAA, string@BBBB
+ */
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_methodClassDex(%edx), %ecx # %ecx<- glue->pDvmDex
+ FETCH 1, %eax # %eax<- BBBB
+ movl offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+ cmp $0, (%ecx, %eax, 4) # check for null ptr; resolved StaticField ptr
+ movl (%ecx, %eax, 4), %ecx # %ecx<- resolved StaticField ptr
+ je .LOP_SGET_OBJECT_resolve
+ jmp .LOP_SGET_OBJECT_finish
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_BOOLEAN: /* 0x63 */
+/* File: x86-atom/OP_SGET_BOOLEAN.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SGET_BOOLEAN.S
+ */
+
+/* File: x86-atom/OP_SGET.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SGET.S
+ *
+ * Code: Generic 32-bit static field "get" operation. Uses no substitutions.
+ *
+ * For: sget-boolean, sget-byte, sget-char, sget-object, sget, sget-short
+ *
+ * Description: Perform the identified object static field operation
+ * with the identified static field; load the field value
+ * into the value register.
+ *
+ * Format: AA|op BBBB (21c)
+ *
+ * Syntax: op vAA, string@BBBB
+ */
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_methodClassDex(%edx), %ecx # %ecx<- glue->pDvmDex
+ FETCH 1, %eax # %eax<- BBBB
+ movl offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+ cmp $0, (%ecx, %eax, 4) # check for null ptr; resolved StaticField ptr
+ movl (%ecx, %eax, 4), %ecx # %ecx<- resolved StaticField ptr
+ je .LOP_SGET_BOOLEAN_resolve
+ jmp .LOP_SGET_BOOLEAN_finish
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_BYTE: /* 0x64 */
+/* File: x86-atom/OP_SGET_BYTE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SGET_BYTE.S
+ */
+
+/* File: x86-atom/OP_SGET.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SGET.S
+ *
+ * Code: Generic 32-bit static field "get" operation. Uses no substitutions.
+ *
+ * For: sget-boolean, sget-byte, sget-char, sget-object, sget, sget-short
+ *
+ * Description: Perform the identified object static field operation
+ * with the identified static field; load the field value
+ * into the value register.
+ *
+ * Format: AA|op BBBB (21c)
+ *
+ * Syntax: op vAA, string@BBBB
+ */
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_methodClassDex(%edx), %ecx # %ecx<- glue->pDvmDex
+ FETCH 1, %eax # %eax<- BBBB
+ movl offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+ cmp $0, (%ecx, %eax, 4) # check for null ptr; resolved StaticField ptr
+ movl (%ecx, %eax, 4), %ecx # %ecx<- resolved StaticField ptr
+ je .LOP_SGET_BYTE_resolve
+ jmp .LOP_SGET_BYTE_finish
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_CHAR: /* 0x65 */
+/* File: x86-atom/OP_SGET_CHAR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SGET_CHAR.S
+ */
+
+/* File: x86-atom/OP_SGET.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SGET.S
+ *
+ * Code: Generic 32-bit static field "get" operation. Uses no substitutions.
+ *
+ * For: sget-boolean, sget-byte, sget-char, sget-object, sget, sget-short
+ *
+ * Description: Perform the identified object static field operation
+ * with the identified static field; load the field value
+ * into the value register.
+ *
+ * Format: AA|op BBBB (21c)
+ *
+ * Syntax: op vAA, string@BBBB
+ */
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_methodClassDex(%edx), %ecx # %ecx<- glue->pDvmDex
+ FETCH 1, %eax # %eax<- BBBB
+ movl offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+ cmp $0, (%ecx, %eax, 4) # check for null ptr; resolved StaticField ptr
+ movl (%ecx, %eax, 4), %ecx # %ecx<- resolved StaticField ptr
+ je .LOP_SGET_CHAR_resolve
+ jmp .LOP_SGET_CHAR_finish
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_SHORT: /* 0x66 */
+/* File: x86-atom/OP_SGET_SHORT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SGET_SHORT.S
+ */
+
+/* File: x86-atom/OP_SGET.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SGET.S
+ *
+ * Code: Generic 32-bit static field "get" operation. Uses no substitutions.
+ *
+ * For: sget-boolean, sget-byte, sget-char, sget-object, sget, sget-short
+ *
+ * Description: Perform the identified object static field operation
+ * with the identified static field; load the field value
+ * into the value register.
+ *
+ * Format: AA|op BBBB (21c)
+ *
+ * Syntax: op vAA, string@BBBB
+ */
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_methodClassDex(%edx), %ecx # %ecx<- glue->pDvmDex
+ FETCH 1, %eax # %eax<- BBBB
+ movl offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+ cmp $0, (%ecx, %eax, 4) # check for null ptr; resolved StaticField ptr
+ movl (%ecx, %eax, 4), %ecx # %ecx<- resolved StaticField ptr
+ je .LOP_SGET_SHORT_resolve
+ jmp .LOP_SGET_SHORT_finish
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT: /* 0x67 */
+/* File: x86-atom/OP_SPUT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SPUT.S
+ *
+ * Code: Generic 32-bit static field "put" operation. Uses no substitutions.
+ *
+ * For: sput-boolean, sput-byte, sput-char, sput-object, sput, sput-short
+ *
+ * Description: Perform the identified object static field operation
+ * with the identified static field; store the field value
+ * register.
+ *
+ * Format: AA|op BBBB (21c)
+ *
+ * Syntax: op vAA, string@BBBB
+ */
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_methodClassDex(%edx), %ecx # %ecx<- pDvmDex
+ FETCH 1, %eax # %eax<- BBBB
+ movl offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+ cmp $0, (%ecx, %eax, 4) # check for null ptr; resolved StaticField ptr
+ movl (%ecx, %eax, 4), %ecx # %ecx<- resolved StaticField ptr
+ je .LOP_SPUT_resolve
+ jmp .LOP_SPUT_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_WIDE: /* 0x68 */
+/* File: x86-atom/OP_SPUT_WIDE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SPUT_WIDE.S
+ *
+ * Code: Generic 32-bit static field "put" operation. Uses no substitutions.
+ *
+ * For: sput-boolean, sput-byte, sput-char, sput-object, sput, sput-short
+ *
+ * Description: Perform the identified object static field operation
+ * with the identified static field; store the field value
+ * register.
+ *
+ * Format: AA|op BBBB (21c)
+ *
+ * Syntax: op vAA, string@BBBB
+ */
+
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ movl offGlue_methodClassDex(%eax), %ecx # %ecx<- glue->pDvmDex
+ FETCH 1, %edx # %edx<- BBBB
+ movl offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+ cmp $0, (%ecx, %edx, 4) # check for null ptr; resolved StaticField ptr
+ movl (%ecx, %edx, 4), %ecx # %ecx<- resolved StaticField ptr
+ je .LOP_SPUT_WIDE_resolve
+
+.LOP_SPUT_WIDE_finish:
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movq (rFP, rINST, 4), %xmm0 # %xmm0<- vAA
+ movq %xmm0, offStaticField_value(%ecx) # field value<- field value
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_OBJECT: /* 0x69 */
+/* File: x86-atom/OP_SPUT_OBJECT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SPUT_OBJECT.S
+ *
+ * Code: Generic 32-bit static field "put" operation. Uses no substitutions.
+ *
+ * For: sput-boolean, sput-byte, sput-char, sput-object, sput, sput-short
+ *
+ * Description: Perform the identified object static field operation
+ * with the identified static field; store the field value
+ * register.
+ *
+ * Format: AA|op BBBB (21c)
+ *
+ * Syntax: op vAA, string@BBBB
+ */
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_methodClassDex(%edx), %ecx # %ecx<- pDvmDex
+ FETCH 1, %eax # %eax<- BBBB
+ movl offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+ cmp $0, (%ecx, %eax, 4) # check for null ptr; resolved StaticField
+ movl (%ecx, %eax, 4), %ecx # %ecx<- resolved StaticField
+ je .LOP_SPUT_OBJECT_resolve
+ jmp .LOP_SPUT_OBJECT_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_BOOLEAN: /* 0x6a */
+/* File: x86-atom/OP_SPUT_BOOLEAN.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SPUT_BOOLEAN.S
+ */
+
+/* File: x86-atom/OP_SPUT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SPUT.S
+ *
+ * Code: Generic 32-bit static field "put" operation. Uses no substitutions.
+ *
+ * For: sput-boolean, sput-byte, sput-char, sput-object, sput, sput-short
+ *
+ * Description: Perform the identified object static field operation
+ * with the identified static field; store the field value
+ * register.
+ *
+ * Format: AA|op BBBB (21c)
+ *
+ * Syntax: op vAA, string@BBBB
+ */
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_methodClassDex(%edx), %ecx # %ecx<- pDvmDex
+ FETCH 1, %eax # %eax<- BBBB
+ movl offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+ cmp $0, (%ecx, %eax, 4) # check for null ptr; resolved StaticField ptr
+ movl (%ecx, %eax, 4), %ecx # %ecx<- resolved StaticField ptr
+ je .LOP_SPUT_BOOLEAN_resolve
+ jmp .LOP_SPUT_BOOLEAN_finish
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_BYTE: /* 0x6b */
+/* File: x86-atom/OP_SPUT_BYTE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SPUT_BYTE.S
+ */
+
+/* File: x86-atom/OP_SPUT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SPUT.S
+ *
+ * Code: Generic 32-bit static field "put" operation. Uses no substitutions.
+ *
+ * For: sput-boolean, sput-byte, sput-char, sput-object, sput, sput-short
+ *
+ * Description: Perform the identified object static field operation
+ * with the identified static field; store the field value
+ * register.
+ *
+ * Format: AA|op BBBB (21c)
+ *
+ * Syntax: op vAA, string@BBBB
+ */
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_methodClassDex(%edx), %ecx # %ecx<- pDvmDex
+ FETCH 1, %eax # %eax<- BBBB
+ movl offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+ cmp $0, (%ecx, %eax, 4) # check for null ptr; resolved StaticField ptr
+ movl (%ecx, %eax, 4), %ecx # %ecx<- resolved StaticField ptr
+ je .LOP_SPUT_BYTE_resolve
+ jmp .LOP_SPUT_BYTE_finish
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_CHAR: /* 0x6c */
+/* File: x86-atom/OP_SPUT_CHAR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SPUT_CHAR.S
+ */
+
+/* File: x86-atom/OP_SPUT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SPUT.S
+ *
+ * Code: Generic 32-bit static field "put" operation. Uses no substitutions.
+ *
+ * For: sput-boolean, sput-byte, sput-char, sput-object, sput, sput-short
+ *
+ * Description: Perform the identified object static field operation
+ * with the identified static field; store the field value
+ * register.
+ *
+ * Format: AA|op BBBB (21c)
+ *
+ * Syntax: op vAA, string@BBBB
+ */
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_methodClassDex(%edx), %ecx # %ecx<- pDvmDex
+ FETCH 1, %eax # %eax<- BBBB
+ movl offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+ cmp $0, (%ecx, %eax, 4) # check for null ptr; resolved StaticField ptr
+ movl (%ecx, %eax, 4), %ecx # %ecx<- resolved StaticField ptr
+ je .LOP_SPUT_CHAR_resolve
+ jmp .LOP_SPUT_CHAR_finish
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_SHORT: /* 0x6d */
+/* File: x86-atom/OP_SPUT_SHORT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SPUT_SHORT.S
+ */
+
+/* File: x86-atom/OP_SPUT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SPUT.S
+ *
+ * Code: Generic 32-bit static field "put" operation. Uses no substitutions.
+ *
+ * For: sput-boolean, sput-byte, sput-char, sput-object, sput, sput-short
+ *
+ * Description: Perform the identified object static field operation
+ * with the identified static field; store the field value
+ * register.
+ *
+ * Format: AA|op BBBB (21c)
+ *
+ * Syntax: op vAA, string@BBBB
+ */
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_methodClassDex(%edx), %ecx # %ecx<- pDvmDex
+ FETCH 1, %eax # %eax<- BBBB
+ movl offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+ cmp $0, (%ecx, %eax, 4) # check for null ptr; resolved StaticField ptr
+ movl (%ecx, %eax, 4), %ecx # %ecx<- resolved StaticField ptr
+ je .LOP_SPUT_SHORT_resolve
+ jmp .LOP_SPUT_SHORT_finish
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_VIRTUAL: /* 0x6e */
+/* File: x86-atom/OP_INVOKE_VIRTUAL.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INVOKE_VIRTUAL.S
+ *
+ * Code: Call a virtual method. Provides an "isrange" variable and
+ * a "routine" variable to specify this is the "range" version of
+ * invoke_direct that allows up to 255 arguments.
+ *
+ * For: invoke-virtual, invoke-virtual/range
+ *
+ * Description: invoke-virtual is used to invoke a normal virtual method;
+ * a method that is not static or final, and is not a constructor.
+ *
+ * Format: B|A|op CCCC G|F|E|D (35c)
+ * AA|op BBBB CCCC (3rc)
+ *
+ * Syntax: [B=5] op {vD, vE, vF, vG, vA}, meth@CCCC (35c)
+ * [B=5] op {vD, vE, vF, vG, vA}, type@CCCC (35c)
+ * [B=4] op {vD, vE, vF, vG}, kind@CCCC (35c)
+ * [B=3] op {vD, vE, vF}, kind@CCCC (35c)
+ * [B=2] op {vD, vE}, kind@CCCC (35c)
+ * [B=1] op {vD}, kind@CCCC (35c)
+ * [B=0] op {}, kind@CCCC (35c)
+ *
+ * op {vCCCC .. vNNNN}, meth@BBBB (3rc) (where NNNN = CCCC+AA-1, that
+ * op {vCCCC .. vNNNN}, type@BBBB (3rc) is A determines the count 0..255,
+ * and C determines the first register)
+ */
+
+
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ EXPORT_PC # must export pc for invoke
+ movl offGlue_methodClassDex(%eax), %eax # %eax<- pDvmDex
+ FETCH 1, %ecx # %ecx<- method index
+ movl offDvmDex_pResMethods(%eax), %eax # %eax<- pDvmDex->pResMethods
+ FETCH 2, %edx # %edx<- GFED or CCCC
+ .if (!0)
+ and $15, %edx # %edx<- D if not range
+ .endif
+ cmp $0, (%eax, %ecx, 4) # check if already resolved
+ je .LOP_INVOKE_VIRTUAL_break
+ movl (%eax, %ecx, 4), %eax # %eax<- resolved base method
+ jmp .LOP_INVOKE_VIRTUAL_continue
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_SUPER: /* 0x6f */
+/* File: x86-atom/OP_INVOKE_SUPER.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INVOKE_SUPER.S
+ *
+ * Code: Call super method.
+ *
+ * For: invoke-super, invoke-super/range
+ *
+ * Description: invoke-super is used to invoke the closest superclass's virtual
+ * method (as opposed to the one with the same method_id in the
+ * calling class).
+ *
+ * Format: B|A|op CCCC G|F|E|D (35c)
+ * AA|op BBBB CCCC (3rc)
+ *
+ * Syntax: [B=5] op {vD, vE, vF, vG, vA}, meth@CCCC (35c)
+ * [B=5] op {vD, vE, vF, vG, vA}, type@CCCC (35c)
+ * [B=4] op {vD, vE, vF, vG}, kind@CCCC (35c)
+ * [B=3] op {vD, vE, vF}, kind@CCCC (35c)
+ * [B=2] op {vD, vE}, kind@CCCC (35c)
+ * [B=1] op {vD}, kind@CCCC (35c)
+ * [B=0] op {}, kind@CCCC (35c)
+ *
+ * op {vCCCC .. vNNNN}, meth@BBBB (3rc) (where NNNN = CCCC+AA-1, that
+ * op {vCCCC .. vNNNN}, type@BBBB (3rc) is A determines the count 0..255,
+ * and C determines the first register)
+ */
+
+
+ movl rGLUE, %ecx # %ecx<- pMterpGlue
+ FETCH 2, %eax # %eax<- GFED or CCCC
+ movl offGlue_methodClassDex(%ecx), %ecx # %ecx<- pDvmDex
+ .if (!0)
+ and $15, %eax # %eax<- D if not range
+ .endif
+ FETCH 1, %edx # %edx<- method index
+ movl offDvmDex_pResMethods(%ecx), %ecx # %ecx<- pDvmDex->pResMethods
+ cmp $0, (rFP, %eax, 4) # check for null object
+ movl (%ecx, %edx, 4), %ecx # %ecx<- resolved base method
+ je common_errNullObject # handle null object
+ jmp .LOP_INVOKE_SUPER_continue2
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_DIRECT: /* 0x70 */
+/* File: x86-atom/OP_INVOKE_DIRECT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INVOKE_DIRECT.S
+ *
+ * Code: Call a non-static direct method. Provides an "isrange" variable and
+ * a "routine" variable to specify this is the "range" version of
+ * invoke_direct that allows up to 255 arguments.
+ *
+ * For: invoke-direct, invoke-direct/range
+ *
+ * Description: invoke-direct is used to invoke a non-static direct method;
+ * an instance method that is non-overridable, for example,
+ * either a private instance method or a constructor.
+ *
+ * Format: B|A|op CCCC G|F|E|D (35c)
+ * AA|op BBBB CCCC (3rc)
+ *
+ * Syntax: [B=5] op {vD, vE, vF, vG, vA}, meth@CCCC (35c)
+ * [B=5] op {vD, vE, vF, vG, vA}, type@CCCC (35c)
+ * [B=4] op {vD, vE, vF, vG}, kind@CCCC (35c)
+ * [B=3] op {vD, vE, vF}, kind@CCCC (35c)
+ * [B=2] op {vD, vE}, kind@CCCC (35c)
+ * [B=1] op {vD}, kind@CCCC (35c)
+ * [B=0] op {}, kind@CCCC (35c)
+ *
+ * op {vCCCC .. vNNNN}, meth@BBBB (3rc) (where NNNN = CCCC+AA-1, that
+ * op {vCCCC .. vNNNN}, type@BBBB (3rc) is A determines the count 0..255,
+ * and C determines the first register)
+ */
+
+
+ movl rGLUE, %ecx # %ecx<- pMterpGlue
+ movl offGlue_methodClassDex(%ecx), %ecx # %ecx<- pDvmDex
+ FETCH 1, %eax # %eax<- method index
+ movl offDvmDex_pResMethods(%ecx), %ecx # %ecx<- pDvmDex->pResMethods
+ FETCH 2, %edx # %edx<- GFED or CCCC
+ movl (%ecx, %eax, 4), %ecx # %ecx<- resolved method to call
+ .if (!0)
+ andl $15, %edx # %edx<- D if not range
+ .endif
+ EXPORT_PC # must export for invoke
+ movl %edx, -4(%esp) # save "this" pointer register
+ cmp $0, %ecx # check if already resolved
+ GET_VREG %edx # %edx<- "this" pointer
+ je .LOP_INVOKE_DIRECT_resolve # handle resolve
+
+.LOP_INVOKE_DIRECT_finish:
+ cmp $0, %edx # check for null "this"
+ jne common_invokeMethodNoRange # invoke method common code
+ jmp common_errNullObject
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_STATIC: /* 0x71 */
+/* File: x86-atom/OP_INVOKE_STATIC.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INVOKE_STATIC.S
+ *
+ * Code: Call static direct method. Provides an "isrange" variable and
+ * a "routine" variable to specify this is the "range" version of
+ * invoke_static that allows up to 255 arguments.
+ *
+ * For: invoke-static, invoke-static/range
+ *
+ * Description: invoke-static is used to invoke static direct method.
+ *
+ * Format: B|A|op CCCC G|F|E|D (35c)
+ * AA|op BBBB CCCC (3rc)
+ *
+ * Syntax: [B=5] op {vD, vE, vF, vG, vA}, meth@CCCC (35c)
+ * [B=5] op {vD, vE, vF, vG, vA}, type@CCCC (35c)
+ * [B=4] op {vD, vE, vF, vG}, kind@CCCC (35c)
+ * [B=3] op {vD, vE, vF}, kind@CCCC (35c)
+ * [B=2] op {vD, vE}, kind@CCCC (35c)
+ * [B=1] op {vD}, kind@CCCC (35c)
+ * [B=0] op {}, kind@CCCC (35c)
+ *
+ * op {vCCCC .. vNNNN}, meth@BBBB (3rc) (where NNNN = CCCC+AA-1, that
+ * op {vCCCC .. vNNNN}, type@BBBB (3rc) is A determines the count 0..255,
+ * and C determines the first register)
+ */
+
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_methodClassDex(%edx), %ecx # %edx<- pDvmDex
+ FETCH 1, %eax # %eax<- method index
+ movl offDvmDex_pResMethods(%ecx), %ecx # %edx<- pDvmDex->pResMethods
+ movl (%ecx, %eax, 4), %ecx # %ecx<- resolved method to call
+ cmp $0, %ecx # check if already resolved
+ EXPORT_PC # must export for invoke
+ jne common_invokeMethodNoRange # invoke method common code
+ jmp .LOP_INVOKE_STATIC_break
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_INTERFACE: /* 0x72 */
+/* File: x86-atom/OP_INVOKE_INTERFACE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INVOKE_INTERFACE.S
+ *
+ * Code: Call at method. Provides an "isrange" variable and
+ * a "routine" variable to specify this is the "range" version of
+ * invoke_interface that allows up to 255 arguments.
+ *
+ * For: invoke-interface, invoke-interface-range
+ *
+ * Description: invoke-interface is used to invoke an interface method; on an
+ * object whose concrete class isn't known, using a method_id that
+ * refers to an interface.
+ *
+ * Format: B|A|op CCCC G|F|E|D (35c)
+ * AA|op BBBB CCCC (3rc)
+ *
+ * Syntax: [B=5] op {vD, vE, vF, vG, vA}, meth@CCCC (35c)
+ * [B=5] op {vD, vE, vF, vG, vA}, type@CCCC (35c)
+ * [B=4] op {vD, vE, vF, vG}, kind@CCCC (35c)
+ * [B=3] op {vD, vE, vF}, kind@CCCC (35c)
+ * [B=2] op {vD, vE}, kind@CCCC (35c)
+ * [B=1] op {vD}, kind@CCCC (35c)
+ * [B=0] op {}, kind@CCCC (35c)
+ *
+ * op {vCCCC .. vNNNN}, meth@BBBB (3rc) (where NNNN = CCCC+AA-1, that
+ * op {vCCCC .. vNNNN}, type@BBBB (3rc) is A determines the count 0..255,
+ * and C determines the first register)
+ */
+
+
+ FETCH 2, %edx # %edx<- GFED or CCCC
+ FETCH 1, %ecx # %ecx<- method index
+ movl %ecx, -12(%esp) # push argument method index
+ .if (!0)
+ and $15, %edx # %edx<- D if not range
+ .endif
+ EXPORT_PC # must export for invoke
+ GET_VREG %edx # %edx<- first arg "this pointer"
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ movl offGlue_methodClassDex(%eax), %eax # %eax<- glue->pDvmDex
+ movl %eax, -4(%esp) # push parameter class
+ cmp $0, %edx # check for null object
+ je common_errNullObject # handle null object
+ jmp .LOP_INVOKE_INTERFACE_break
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_73: /* 0x73 */
+/* File: x86-atom/OP_UNUSED_73.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_UNUSED_73.S
+ */
+
+/* File: x86-atom/unused.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: unused.S
+ *
+ * Code: Common code for unused bytecodes. Uses no subtitutions.
+ *
+ * For: all unused bytecodes
+ *
+ * Description: aborts if executed.
+ *
+ * Format: ØØ|op (10x)
+ *
+ * Syntax: op
+ */
+
+ call common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_VIRTUAL_RANGE: /* 0x74 */
+/* File: x86-atom/OP_INVOKE_VIRTUAL_RANGE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INVOKE_VIRTUAL_RANGE.S
+ */
+
+/* File: x86-atom/OP_INVOKE_VIRTUAL.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INVOKE_VIRTUAL.S
+ *
+ * Code: Call a virtual method. Provides an "isrange" variable and
+ * a "routine" variable to specify this is the "range" version of
+ * invoke_direct that allows up to 255 arguments.
+ *
+ * For: invoke-virtual, invoke-virtual/range
+ *
+ * Description: invoke-virtual is used to invoke a normal virtual method;
+ * a method that is not static or final, and is not a constructor.
+ *
+ * Format: B|A|op CCCC G|F|E|D (35c)
+ * AA|op BBBB CCCC (3rc)
+ *
+ * Syntax: [B=5] op {vD, vE, vF, vG, vA}, meth@CCCC (35c)
+ * [B=5] op {vD, vE, vF, vG, vA}, type@CCCC (35c)
+ * [B=4] op {vD, vE, vF, vG}, kind@CCCC (35c)
+ * [B=3] op {vD, vE, vF}, kind@CCCC (35c)
+ * [B=2] op {vD, vE}, kind@CCCC (35c)
+ * [B=1] op {vD}, kind@CCCC (35c)
+ * [B=0] op {}, kind@CCCC (35c)
+ *
+ * op {vCCCC .. vNNNN}, meth@BBBB (3rc) (where NNNN = CCCC+AA-1, that
+ * op {vCCCC .. vNNNN}, type@BBBB (3rc) is A determines the count 0..255,
+ * and C determines the first register)
+ */
+
+
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ EXPORT_PC # must export pc for invoke
+ movl offGlue_methodClassDex(%eax), %eax # %eax<- pDvmDex
+ FETCH 1, %ecx # %ecx<- method index
+ movl offDvmDex_pResMethods(%eax), %eax # %eax<- pDvmDex->pResMethods
+ FETCH 2, %edx # %edx<- GFED or CCCC
+ .if (!1)
+ and $15, %edx # %edx<- D if not range
+ .endif
+ cmp $0, (%eax, %ecx, 4) # check if already resolved
+ je .LOP_INVOKE_VIRTUAL_RANGE_break
+ movl (%eax, %ecx, 4), %eax # %eax<- resolved base method
+ jmp .LOP_INVOKE_VIRTUAL_RANGE_continue
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_SUPER_RANGE: /* 0x75 */
+/* File: x86-atom/OP_INVOKE_SUPER_RANGE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INVOKE_SUPER_RANGE.S
+ */
+
+/* File: x86-atom/OP_INVOKE_SUPER.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INVOKE_SUPER.S
+ *
+ * Code: Call super method.
+ *
+ * For: invoke-super, invoke-super/range
+ *
+ * Description: invoke-super is used to invoke the closest superclass's virtual
+ * method (as opposed to the one with the same method_id in the
+ * calling class).
+ *
+ * Format: B|A|op CCCC G|F|E|D (35c)
+ * AA|op BBBB CCCC (3rc)
+ *
+ * Syntax: [B=5] op {vD, vE, vF, vG, vA}, meth@CCCC (35c)
+ * [B=5] op {vD, vE, vF, vG, vA}, type@CCCC (35c)
+ * [B=4] op {vD, vE, vF, vG}, kind@CCCC (35c)
+ * [B=3] op {vD, vE, vF}, kind@CCCC (35c)
+ * [B=2] op {vD, vE}, kind@CCCC (35c)
+ * [B=1] op {vD}, kind@CCCC (35c)
+ * [B=0] op {}, kind@CCCC (35c)
+ *
+ * op {vCCCC .. vNNNN}, meth@BBBB (3rc) (where NNNN = CCCC+AA-1, that
+ * op {vCCCC .. vNNNN}, type@BBBB (3rc) is A determines the count 0..255,
+ * and C determines the first register)
+ */
+
+
+ movl rGLUE, %ecx # %ecx<- pMterpGlue
+ FETCH 2, %eax # %eax<- GFED or CCCC
+ movl offGlue_methodClassDex(%ecx), %ecx # %ecx<- pDvmDex
+ .if (!1)
+ and $15, %eax # %eax<- D if not range
+ .endif
+ FETCH 1, %edx # %edx<- method index
+ movl offDvmDex_pResMethods(%ecx), %ecx # %ecx<- pDvmDex->pResMethods
+ cmp $0, (rFP, %eax, 4) # check for null object
+ movl (%ecx, %edx, 4), %ecx # %ecx<- resolved base method
+ je common_errNullObject # handle null object
+ jmp .LOP_INVOKE_SUPER_RANGE_continue2
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_DIRECT_RANGE: /* 0x76 */
+/* File: x86-atom/OP_INVOKE_DIRECT_RANGE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INVOKE_DIRECT_RANGE.S
+ */
+
+/* File: x86-atom/OP_INVOKE_DIRECT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INVOKE_DIRECT.S
+ *
+ * Code: Call a non-static direct method. Provides an "isrange" variable and
+ * a "routine" variable to specify this is the "range" version of
+ * invoke_direct that allows up to 255 arguments.
+ *
+ * For: invoke-direct, invoke-direct/range
+ *
+ * Description: invoke-direct is used to invoke a non-static direct method;
+ * an instance method that is non-overridable, for example,
+ * either a private instance method or a constructor.
+ *
+ * Format: B|A|op CCCC G|F|E|D (35c)
+ * AA|op BBBB CCCC (3rc)
+ *
+ * Syntax: [B=5] op {vD, vE, vF, vG, vA}, meth@CCCC (35c)
+ * [B=5] op {vD, vE, vF, vG, vA}, type@CCCC (35c)
+ * [B=4] op {vD, vE, vF, vG}, kind@CCCC (35c)
+ * [B=3] op {vD, vE, vF}, kind@CCCC (35c)
+ * [B=2] op {vD, vE}, kind@CCCC (35c)
+ * [B=1] op {vD}, kind@CCCC (35c)
+ * [B=0] op {}, kind@CCCC (35c)
+ *
+ * op {vCCCC .. vNNNN}, meth@BBBB (3rc) (where NNNN = CCCC+AA-1, that
+ * op {vCCCC .. vNNNN}, type@BBBB (3rc) is A determines the count 0..255,
+ * and C determines the first register)
+ */
+
+
+ movl rGLUE, %ecx # %ecx<- pMterpGlue
+ movl offGlue_methodClassDex(%ecx), %ecx # %ecx<- pDvmDex
+ FETCH 1, %eax # %eax<- method index
+ movl offDvmDex_pResMethods(%ecx), %ecx # %ecx<- pDvmDex->pResMethods
+ FETCH 2, %edx # %edx<- GFED or CCCC
+ movl (%ecx, %eax, 4), %ecx # %ecx<- resolved method to call
+ .if (!1)
+ andl $15, %edx # %edx<- D if not range
+ .endif
+ EXPORT_PC # must export for invoke
+ movl %edx, -4(%esp) # save "this" pointer register
+ cmp $0, %ecx # check if already resolved
+ GET_VREG %edx # %edx<- "this" pointer
+ je .LOP_INVOKE_DIRECT_RANGE_resolve # handle resolve
+
+.LOP_INVOKE_DIRECT_RANGE_finish:
+ cmp $0, %edx # check for null "this"
+ jne common_invokeMethodRange # invoke method common code
+ jmp common_errNullObject
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_STATIC_RANGE: /* 0x77 */
+/* File: x86-atom/OP_INVOKE_STATIC_RANGE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INVOKE_STATIC_RANGE.S
+ */
+
+/* File: x86-atom/OP_INVOKE_STATIC.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INVOKE_STATIC.S
+ *
+ * Code: Call static direct method. Provides an "isrange" variable and
+ * a "routine" variable to specify this is the "range" version of
+ * invoke_static that allows up to 255 arguments.
+ *
+ * For: invoke-static, invoke-static/range
+ *
+ * Description: invoke-static is used to invoke static direct method.
+ *
+ * Format: B|A|op CCCC G|F|E|D (35c)
+ * AA|op BBBB CCCC (3rc)
+ *
+ * Syntax: [B=5] op {vD, vE, vF, vG, vA}, meth@CCCC (35c)
+ * [B=5] op {vD, vE, vF, vG, vA}, type@CCCC (35c)
+ * [B=4] op {vD, vE, vF, vG}, kind@CCCC (35c)
+ * [B=3] op {vD, vE, vF}, kind@CCCC (35c)
+ * [B=2] op {vD, vE}, kind@CCCC (35c)
+ * [B=1] op {vD}, kind@CCCC (35c)
+ * [B=0] op {}, kind@CCCC (35c)
+ *
+ * op {vCCCC .. vNNNN}, meth@BBBB (3rc) (where NNNN = CCCC+AA-1, that
+ * op {vCCCC .. vNNNN}, type@BBBB (3rc) is A determines the count 0..255,
+ * and C determines the first register)
+ */
+
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_methodClassDex(%edx), %ecx # %edx<- pDvmDex
+ FETCH 1, %eax # %eax<- method index
+ movl offDvmDex_pResMethods(%ecx), %ecx # %edx<- pDvmDex->pResMethods
+ movl (%ecx, %eax, 4), %ecx # %ecx<- resolved method to call
+ cmp $0, %ecx # check if already resolved
+ EXPORT_PC # must export for invoke
+ jne common_invokeMethodRange # invoke method common code
+ jmp .LOP_INVOKE_STATIC_RANGE_break
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_INTERFACE_RANGE: /* 0x78 */
+/* File: x86-atom/OP_INVOKE_INTERFACE_RANGE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INVOKE_INTERFACE_RANGE.S
+ */
+
+/* File: x86-atom/OP_INVOKE_INTERFACE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INVOKE_INTERFACE.S
+ *
+ * Code: Call at method. Provides an "isrange" variable and
+ * a "routine" variable to specify this is the "range" version of
+ * invoke_interface that allows up to 255 arguments.
+ *
+ * For: invoke-interface, invoke-interface-range
+ *
+ * Description: invoke-interface is used to invoke an interface method; on an
+ * object whose concrete class isn't known, using a method_id that
+ * refers to an interface.
+ *
+ * Format: B|A|op CCCC G|F|E|D (35c)
+ * AA|op BBBB CCCC (3rc)
+ *
+ * Syntax: [B=5] op {vD, vE, vF, vG, vA}, meth@CCCC (35c)
+ * [B=5] op {vD, vE, vF, vG, vA}, type@CCCC (35c)
+ * [B=4] op {vD, vE, vF, vG}, kind@CCCC (35c)
+ * [B=3] op {vD, vE, vF}, kind@CCCC (35c)
+ * [B=2] op {vD, vE}, kind@CCCC (35c)
+ * [B=1] op {vD}, kind@CCCC (35c)
+ * [B=0] op {}, kind@CCCC (35c)
+ *
+ * op {vCCCC .. vNNNN}, meth@BBBB (3rc) (where NNNN = CCCC+AA-1, that
+ * op {vCCCC .. vNNNN}, type@BBBB (3rc) is A determines the count 0..255,
+ * and C determines the first register)
+ */
+
+
+ FETCH 2, %edx # %edx<- GFED or CCCC
+ FETCH 1, %ecx # %ecx<- method index
+ movl %ecx, -12(%esp) # push argument method index
+ .if (!1)
+ and $15, %edx # %edx<- D if not range
+ .endif
+ EXPORT_PC # must export for invoke
+ GET_VREG %edx # %edx<- first arg "this pointer"
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ movl offGlue_methodClassDex(%eax), %eax # %eax<- glue->pDvmDex
+ movl %eax, -4(%esp) # push parameter class
+ cmp $0, %edx # check for null object
+ je common_errNullObject # handle null object
+ jmp .LOP_INVOKE_INTERFACE_RANGE_break
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_79: /* 0x79 */
+/* File: x86-atom/OP_UNUSED_79.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_UNUSED_79.S
+ */
+
+/* File: x86-atom/unused.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: unused.S
+ *
+ * Code: Common code for unused bytecodes. Uses no subtitutions.
+ *
+ * For: all unused bytecodes
+ *
+ * Description: aborts if executed.
+ *
+ * Format: ØØ|op (10x)
+ *
+ * Syntax: op
+ */
+
+ call common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_7A: /* 0x7a */
+/* File: x86-atom/OP_UNUSED_7A.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_UNUSED_7A.S
+ */
+
+/* File: x86-atom/unused.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: unused.S
+ *
+ * Code: Common code for unused bytecodes. Uses no subtitutions.
+ *
+ * For: all unused bytecodes
+ *
+ * Description: aborts if executed.
+ *
+ * Format: ØØ|op (10x)
+ *
+ * Syntax: op
+ */
+
+ call common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEG_INT: /* 0x7b */
+/* File: x86-atom/OP_NEG_INT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_NEG_INT.S
+ */
+
+/* File: x86-atom/unop.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: unop.S
+ *
+ * Code: Generic 32-bit unary operation. Provide an "instr" variable and a
+ * preinstr variable that together specify an instruction that
+ * performs, for example, "%ecx = op %edx".
+ *
+ * For: int-to-byte, int-to-char, int-to-short, neg-float, neg-int, not-int
+ *
+ * Description: Perform the identified unary operation on the source
+ * register, storing the result in the destination register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+
+ movl rINST, %ecx # %ecx<- BA+
+ shr $4, %ecx # %ecx<- B
+ and $15, rINST # rINST<- A
+ FFETCH_ADV 1, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %ecx # %ecx<- vB
+ # do operation part 1
+ neg %ecx # do operation part 2
+ SET_VREG %ecx, rINST # vA<- result
+ FGETOP_JMP 1, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NOT_INT: /* 0x7c */
+/* File: x86-atom/OP_NOT_INT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_NOT_INT.S
+ */
+
+/* File: x86-atom/unop.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: unop.S
+ *
+ * Code: Generic 32-bit unary operation. Provide an "instr" variable and a
+ * preinstr variable that together specify an instruction that
+ * performs, for example, "%ecx = op %edx".
+ *
+ * For: int-to-byte, int-to-char, int-to-short, neg-float, neg-int, not-int
+ *
+ * Description: Perform the identified unary operation on the source
+ * register, storing the result in the destination register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+
+ movl rINST, %ecx # %ecx<- BA+
+ shr $4, %ecx # %ecx<- B
+ and $15, rINST # rINST<- A
+ FFETCH_ADV 1, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %ecx # %ecx<- vB
+ # do operation part 1
+ not %ecx # do operation part 2
+ SET_VREG %ecx, rINST # vA<- result
+ FGETOP_JMP 1, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEG_LONG: /* 0x7d */
+/* File: x86-atom/OP_NEG_LONG.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_NEG_LONG.S
+ */
+
+/* File: x86-atom/unopWide.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: unopWide.S
+ *
+ * Code: Generic 64-bit unary operation. Provide an "instr" variable and a
+ * preinstr variable that together specify an instruction that
+ * performs, for example, "%xmm0 = op %xmm1".
+ *
+ * For: neg-double, neg-long, not-long
+ *
+ * Description: Perform the identified unary operation on the source
+ * register, storing the result in the destination register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+
+ movl rINST, %ecx # %ecx<- BA+
+ shr $4, rINST # rINST<- B
+ and $15, %ecx # %ecx<- A
+ FFETCH_ADV 1, %eax # %eax<- next instruction hi; fetch, advance
+ movq (rFP, rINST, 4), %xmm0 # %xmm0<- vB
+ xorps %xmm1, %xmm1 # do operation part 1
+ psubq %xmm0, %xmm1 # do operation part 2
+ movq %xmm1, (rFP, %ecx, 4) # vA<- result
+ FGETOP_JMP 1, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NOT_LONG: /* 0x7e */
+/* File: x86-atom/OP_NOT_LONG.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_NOT_LONG.S
+ */
+
+/* File: x86-atom/unopWide.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: unopWide.S
+ *
+ * Code: Generic 64-bit unary operation. Provide an "instr" variable and a
+ * preinstr variable that together specify an instruction that
+ * performs, for example, "%xmm0 = op %xmm1".
+ *
+ * For: neg-double, neg-long, not-long
+ *
+ * Description: Perform the identified unary operation on the source
+ * register, storing the result in the destination register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+
+ movl rINST, %ecx # %ecx<- BA+
+ shr $4, rINST # rINST<- B
+ and $15, %ecx # %ecx<- A
+ FFETCH_ADV 1, %eax # %eax<- next instruction hi; fetch, advance
+ movq (rFP, rINST, 4), %xmm0 # %xmm0<- vB
+ # do operation part 1
+ pandn 0xFFFFFFFF, %xmm0 # do operation part 2
+ movq %xmm0, (rFP, %ecx, 4) # vA<- result
+ FGETOP_JMP 1, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEG_FLOAT: /* 0x7f */
+/* File: x86-atom/OP_NEG_FLOAT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_NEG_FLOAT.S
+ */
+
+/* File: x86-atom/unop.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: unop.S
+ *
+ * Code: Generic 32-bit unary operation. Provide an "instr" variable and a
+ * preinstr variable that together specify an instruction that
+ * performs, for example, "%ecx = op %edx".
+ *
+ * For: int-to-byte, int-to-char, int-to-short, neg-float, neg-int, not-int
+ *
+ * Description: Perform the identified unary operation on the source
+ * register, storing the result in the destination register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+
+ movl rINST, %ecx # %ecx<- BA+
+ shr $4, %ecx # %ecx<- B
+ and $15, rINST # rINST<- A
+ FFETCH_ADV 1, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %ecx # %ecx<- vB
+ # do operation part 1
+ addl $0x80000000, %ecx # do operation part 2
+ SET_VREG %ecx, rINST # vA<- result
+ FGETOP_JMP 1, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEG_DOUBLE: /* 0x80 */
+/* File: x86-atom/OP_NEG_DOUBLE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_NEG_DOUBLE.S
+ */
+
+/* File: x86-atom/unopWide.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: unopWide.S
+ *
+ * Code: Generic 64-bit unary operation. Provide an "instr" variable and a
+ * preinstr variable that together specify an instruction that
+ * performs, for example, "%xmm0 = op %xmm1".
+ *
+ * For: neg-double, neg-long, not-long
+ *
+ * Description: Perform the identified unary operation on the source
+ * register, storing the result in the destination register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+
+ movl rINST, %ecx # %ecx<- BA+
+ shr $4, rINST # rINST<- B
+ and $15, %ecx # %ecx<- A
+ FFETCH_ADV 1, %eax # %eax<- next instruction hi; fetch, advance
+ movq (rFP, rINST, 4), %xmm0 # %xmm0<- vB
+ movq .LdoubNeg, %xmm1 # do operation part 1
+ pxor %xmm1, %xmm0 # do operation part 2
+ movq %xmm0, (rFP, %ecx, 4) # vA<- result
+ FGETOP_JMP 1, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_LONG: /* 0x81 */
+/* File: x86-atom/OP_INT_TO_LONG.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INT_TO_LONG.S
+ *
+ * Code: Convert an int to a long. Uses no substitutions.
+ *
+ * For:
+ *
+ * Description: Convert an int in the source register, to a long, and
+ * stores the result in the destintation register. vA<- (long) vB
+ *
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %eax # %eax<- BA+
+ movl rINST, %ecx # %ecx<- BA+
+ shr $4, %eax # %eax<- B
+ andl $15, %ecx # %ecx<- A
+ GET_VREG %eax # %eax<- vB
+ cdq # %edx:%eax<- sign-extend of %eax
+ movl %eax, (rFP, %ecx, 4) # vA<- lo part
+ movl %edx, 4(rFP, %ecx, 4) # vA+1<- hi part
+ FINISH 1 # jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_FLOAT: /* 0x82 */
+/* File: x86-atom/OP_INT_TO_FLOAT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INT_TO_FLOAT.S
+ *
+ * Code: Convert an int to a float. Uses no substitutions.
+ *
+ * For: int-to-float
+ *
+ * Description: Convert an int in the source register, to a float, and
+ * stores the result in the destintation register. vA<- (float) vB
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %eax # %eax<- BA+
+ shr $4, %eax # %eax<- B
+ andl $15, rINST # rINST<- A
+ cvtsi2ss (rFP,%eax,4), %xmm0 # %xmm0<- vB
+ movss %xmm0, (rFP, rINST, 4) # vA<- %xmm0
+ FINISH 1 # jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_DOUBLE: /* 0x83 */
+/* File: x86-atom/OP_INT_TO_DOUBLE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INT_TO_DOUBLE.S
+ *
+ * Code: Convert an int to a double. Uses no substitutions.
+ *
+ * For: int-to-double
+ *
+ * Description: Converts an int in the source register, to a double, and
+ * stores the result in the destination register. vA<- (double) vB
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %eax # %eax<- BA+
+ shr $4, %eax # %eax<- B
+ andl $15, rINST # rINST<- A
+ cvtsi2sd (rFP, %eax, 4), %xmm0 # %xmm0<- vB
+ movq %xmm0, (rFP, rINST, 4) # vA<- %xmm0; (double) vB
+ FFETCH_ADV 1, %edx # %edx<- next instruction hi; fetch, advance
+ FGETOP_JMP 1, %edx # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_LONG_TO_INT: /* 0x84 */
+/* File: x86-atom/OP_LONG_TO_INT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_LONG_TO_INT.S
+ */
+
+/* File: x86-atom/OP_MOVE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MOVE.S
+ *
+ * Code: Copies contents from one register to another. Uses no
+ * substitutions.
+ *
+ * For: move, move-object, long-to-int
+ *
+ * Description: Copies contents from one non-object register to another.
+ * vA<- vB; fp[A]<- fp[B]
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %ecx # %ecx<- BA
+ shr $4, rINST # rINST<- B
+ and $15, %ecx # %ecx<- A
+ FFETCH_ADV 1, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG rINST # rINST<- vB
+ SET_VREG rINST, %ecx # vA<- vB; %edx
+ FGETOP_JMP 1, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_LONG_TO_FLOAT: /* 0x85 */
+/* File: x86-atom/OP_LONG_TO_FLOAT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_LONG_TO_FLOAT.S
+ *
+ * Code: Convert a long to a float. Uses no substitutions.
+ *
+ * For: int-to-float
+ *
+ * Description: Converts a float in the source register, to a float, and
+ * stores the result in the destination register. vA<- (double) vB
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %ecx # %ecx<- BA+
+ shr $4, rINST # rINST<- B
+ and $15, %ecx # %ecx<- A
+ FFETCH_ADV 1, %eax # %eax<- next instruction hi; fetch, advance
+ fildll (rFP, rINST, 4) # FPU<- vB
+ fstps (rFP, %ecx, 4) # vA<- FPU; (float) vB
+ FGETOP_JMP 1, %eax # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_LONG_TO_DOUBLE: /* 0x86 */
+/* File: x86-atom/OP_LONG_TO_DOUBLE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_LONG_TO_DOUBLE.S
+ *
+ * Code: Convert a long to a dobule. Uses no substitutions.
+ *
+ * For: long-to-double
+ *
+ * Description: Converts a long in the source register to a double, and
+ * stores the result in the destination register. vA<- (double) vB
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %ecx # %ecx<- BA+
+ shr $4, rINST # rINST<- B
+ and $15, %ecx # %ecx<- A
+ FFETCH_ADV 1, %eax # %eax<- next instruction hi; fetch, advance
+ fildll (rFP, rINST, 4) # FPU<- vB
+ fstpl (rFP, %ecx, 4) # vA<- FPU; (double) vB
+ FGETOP_JMP 1, %eax # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FLOAT_TO_INT: /* 0x87 */
+/* File: x86-atom/OP_FLOAT_TO_INT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_FLOAT_TO_INT.S
+ *
+ * Code: Converts a float to a int. Uses no substitutions.
+ *
+ * For: float-to-int
+ *
+ * Description: Convert the float in source register to a int
+ * and store the result in the destintation register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $4, rINST # rINST<- B
+ and $15, %edx # %edx<- A
+ flds (rFP, rINST, 4) # push vB to floating point stack
+ fildl .LintMax # push max int value
+ fildl .LintMin # push min int value
+ fucomip %st(2), %st(0) # check for negInf
+ jae .LOP_FLOAT_TO_INT_negInf # handle negInf
+ fucomip %st(1), %st(0) # check for posInf or NaN
+ jc .LOP_FLOAT_TO_INT_nanInf # handle posInf or NaN
+ jmp .LOP_FLOAT_TO_INT_break # do conversion
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FLOAT_TO_LONG: /* 0x88 */
+/* File: x86-atom/OP_FLOAT_TO_LONG.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_FLOAT_TO_LONG.S
+ *
+ * Code: Converts a float to a long. Uses no substitutions.
+ *
+ * For: float-to-long
+ *
+ * Description: Convert the float in source register to a long
+ * and store the result in the destintation register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $4, rINST # rINST<- B
+ and $15, %edx # %edx<- A
+ flds (rFP, rINST, 4) # push vB to floating point stack
+ fildll .LvaluePosInfLong # push max int value
+ fildll .LvalueNegInfLong # push min int value
+ fucomip %st(2), %st(0) # check for negInf
+ jae .LOP_FLOAT_TO_LONG_negInf # handle negInf
+ fucomip %st(1), %st(0) # check for posInf or NaN
+ jc .LOP_FLOAT_TO_LONG_nanInf # handle posInf or NaN
+ jmp .LOP_FLOAT_TO_LONG_break # do conversion
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FLOAT_TO_DOUBLE: /* 0x89 */
+/* File: x86-atom/OP_FLOAT_TO_DOUBLE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_FLOAT_TO_DOUBLE.S
+ *
+ * Code: Converts a float to a double. Uses no substitutions.
+ *
+ * For: float-to-double
+ *
+ * Description: Convert the float in source register to a double
+ * and store the result in the destintation register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $4, rINST # rINST<- B
+ and $15, %edx # %edx<- A
+ flds (rFP, rINST, 4) # load float
+ fstpl (rFP, %edx, 4) # store double
+ FINISH 1 # jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DOUBLE_TO_INT: /* 0x8a */
+/* File: x86-atom/OP_DOUBLE_TO_INT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_DOUBLE_TO_INT.S
+ *
+ * Code: Converts a double to an integer. Uses no substitutions.
+ *
+ * For: double-to-int
+ *
+ * Description: Convert the source register (a double) to an integer
+ * and store the result in the destination register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $4, rINST # rINST<- B
+ and $15, %edx # %edx<- A
+ fldl (rFP, rINST, 4) # load &vB
+ fildl .LintMax # push max int value
+ fildl .LintMin # push min int value
+ fucomip %st(2), %st(0) # check for negInf
+ jae .LOP_DOUBLE_TO_INT_negInf # handle negInf
+ fucomip %st(1), %st(0) # check for posInf or NaN
+ jc .LOP_DOUBLE_TO_INT_nanInf # handle posInf or NaN
+ jmp .LOP_DOUBLE_TO_INT_break # do conversion
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DOUBLE_TO_LONG: /* 0x8b */
+/* File: x86-atom/OP_DOUBLE_TO_LONG.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_DOUBLE_TO_LONG.S
+ *
+ * Code: Converts a double to a long. Uses no substitutions.
+ *
+ * For: double-to-long
+ *
+ * Description: Convert the double in source register to a long
+ * and store in the destintation register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %ecx<- BA
+ shr $4, rINST # rINST<- B
+ and $15, %edx # %ecx<- A
+ fldl (rFP, rINST, 4) # push vB to floating point stack
+ fildll .LvaluePosInfLong # push max int value
+ fildll .LvalueNegInfLong # push min int value
+ fucomip %st(2), %st(0) # check for negInf
+ jae .LOP_DOUBLE_TO_LONG_negInf # handle negInf
+ fucomip %st(1), %st(0) # check for posInf or NaN
+ jc .LOP_DOUBLE_TO_LONG_nanInf # handle posInf or NaN
+ jmp .LOP_DOUBLE_TO_LONG_break # do conversion
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DOUBLE_TO_FLOAT: /* 0x8c */
+/* File: x86-atom/OP_DOUBLE_TO_FLOAT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_DOUBLE_TO_FLOAT.S
+ *
+ * Code: Converts a double to a float. Uses no substitutions.
+ *
+ * For: double-to-float
+ *
+ * Description: Convert the source register (a double) to a float
+ * and store the result in the destination register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $4, rINST # rINST<- B
+ and $15, %edx # %edx<- A
+ fldl (rFP, rINST, 4) # load &vB
+ fstps (rFP, %edx, 4) # store float
+ FINISH 1 # jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_BYTE: /* 0x8d */
+/* File: x86-atom/OP_INT_TO_BYTE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INT_TO_BYTE.S
+ */
+
+/* File: x86-atom/unop.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: unop.S
+ *
+ * Code: Generic 32-bit unary operation. Provide an "instr" variable and a
+ * preinstr variable that together specify an instruction that
+ * performs, for example, "%ecx = op %edx".
+ *
+ * For: int-to-byte, int-to-char, int-to-short, neg-float, neg-int, not-int
+ *
+ * Description: Perform the identified unary operation on the source
+ * register, storing the result in the destination register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+
+ movl rINST, %ecx # %ecx<- BA+
+ shr $4, %ecx # %ecx<- B
+ and $15, rINST # rINST<- A
+ FFETCH_ADV 1, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %ecx # %ecx<- vB
+ sal $24, %ecx # do operation part 1
+ sar $24, %ecx # do operation part 2
+ SET_VREG %ecx, rINST # vA<- result
+ FGETOP_JMP 1, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_CHAR: /* 0x8e */
+/* File: x86-atom/OP_INT_TO_CHAR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INT_TO_CHAR.S
+ */
+
+/* File: x86-atom/unop.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: unop.S
+ *
+ * Code: Generic 32-bit unary operation. Provide an "instr" variable and a
+ * preinstr variable that together specify an instruction that
+ * performs, for example, "%ecx = op %edx".
+ *
+ * For: int-to-byte, int-to-char, int-to-short, neg-float, neg-int, not-int
+ *
+ * Description: Perform the identified unary operation on the source
+ * register, storing the result in the destination register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+
+ movl rINST, %ecx # %ecx<- BA+
+ shr $4, %ecx # %ecx<- B
+ and $15, rINST # rINST<- A
+ FFETCH_ADV 1, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %ecx # %ecx<- vB
+ sal $16, %ecx # do operation part 1
+ shr $16, %ecx # do operation part 2
+ SET_VREG %ecx, rINST # vA<- result
+ FGETOP_JMP 1, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_SHORT: /* 0x8f */
+/* File: x86-atom/OP_INT_TO_SHORT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INT_TO_SHORT.S
+ */
+
+/* File: x86-atom/unop.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: unop.S
+ *
+ * Code: Generic 32-bit unary operation. Provide an "instr" variable and a
+ * preinstr variable that together specify an instruction that
+ * performs, for example, "%ecx = op %edx".
+ *
+ * For: int-to-byte, int-to-char, int-to-short, neg-float, neg-int, not-int
+ *
+ * Description: Perform the identified unary operation on the source
+ * register, storing the result in the destination register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+
+ movl rINST, %ecx # %ecx<- BA+
+ shr $4, %ecx # %ecx<- B
+ and $15, rINST # rINST<- A
+ FFETCH_ADV 1, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %ecx # %ecx<- vB
+ sal $16, %ecx # do operation part 1
+ sar $16, %ecx # do operation part 2
+ SET_VREG %ecx, rINST # vA<- result
+ FGETOP_JMP 1, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_INT: /* 0x90 */
+/* File: x86-atom/OP_ADD_INT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_ADD_INT.S
+ */
+
+/* File: x86-atom/binop.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binop.S
+ *
+ * Code: Generic 32-bit binary operation. Provides an "instr" line to
+ * specify an instruction that performs "%ecx = %ecx op %edx"
+ *
+ * For: add-int, and-int, mul-int, or-int, sub-int, xor-int
+ *
+ * Description: Perform a binary operation on two source registers
+ * and store the result in a destination register.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %ecx # %ecx<- vBB
+ GET_VREG %edx # %edx<- vCC
+ addl %edx, %ecx # %ecx<- vBB op vCC
+ SET_VREG %ecx, rINST # vAA<- %ecx; result
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_INT: /* 0x91 */
+/* File: x86-atom/OP_SUB_INT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SUB_INT.S
+ */
+
+/* File: x86-atom/binop.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binop.S
+ *
+ * Code: Generic 32-bit binary operation. Provides an "instr" line to
+ * specify an instruction that performs "%ecx = %ecx op %edx"
+ *
+ * For: add-int, and-int, mul-int, or-int, sub-int, xor-int
+ *
+ * Description: Perform a binary operation on two source registers
+ * and store the result in a destination register.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %ecx # %ecx<- vBB
+ GET_VREG %edx # %edx<- vCC
+ subl %edx, %ecx # %ecx<- vBB op vCC
+ SET_VREG %ecx, rINST # vAA<- %ecx; result
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_INT: /* 0x92 */
+/* File: x86-atom/OP_MUL_INT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MUL_INT.S
+ */
+
+/* File: x86-atom/binop.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binop.S
+ *
+ * Code: Generic 32-bit binary operation. Provides an "instr" line to
+ * specify an instruction that performs "%ecx = %ecx op %edx"
+ *
+ * For: add-int, and-int, mul-int, or-int, sub-int, xor-int
+ *
+ * Description: Perform a binary operation on two source registers
+ * and store the result in a destination register.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %ecx # %ecx<- vBB
+ GET_VREG %edx # %edx<- vCC
+ imul %edx, %ecx # %ecx<- vBB op vCC
+ SET_VREG %ecx, rINST # vAA<- %ecx; result
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_INT: /* 0x93 */
+/* File: x86-atom/OP_DIV_INT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_DIV_INT.S
+ */
+
+/* File: x86-atom/binopD.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopD.S
+ *
+ * Code: 32-bit integer divide operation. If "div" is set, the code
+ * returns the quotient, else it returns the remainder.
+ * Also, a divide-by-zero check is done.
+ *
+ * For: div-int, rem-int
+ *
+ * Description: Perform a binary operation on two source
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+
+ FETCH_BB 1, %eax # %eax<- BB
+ FETCH_CC 1, %ecx # %ecx<- CC
+ GET_VREG %eax # %eax<- vBB
+ GET_VREG %ecx # %ecx<- vCC
+ cmp $0, %ecx # check for divide by zero
+ je common_errDivideByZero # handle divide by zero
+ cmpl $-1, %ecx # handle -1 special case divide error
+ jne .LOP_DIV_INT_noerror
+ cmpl $0x80000000,%eax # handle min int special case divide error
+ je .LOP_DIV_INT_break
+.LOP_DIV_INT_noerror:
+ cdq # sign-extend %eax to %edx
+ idiv %ecx # divide %edx:%eax by %ecx
+ .if 1
+ SET_VREG %eax rINST # vAA<- %eax (quotient)
+ .else
+ SET_VREG %edx rINST # vAA<- %edx (remainder)
+ .endif
+ jmp .LOP_DIV_INT_break2
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_INT: /* 0x94 */
+/* File: x86-atom/OP_REM_INT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_REM_INT.S
+ */
+
+/* File: x86-atom/binopD.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopD.S
+ *
+ * Code: 32-bit integer divide operation. If "div" is set, the code
+ * returns the quotient, else it returns the remainder.
+ * Also, a divide-by-zero check is done.
+ *
+ * For: div-int, rem-int
+ *
+ * Description: Perform a binary operation on two source
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+
+ FETCH_BB 1, %eax # %eax<- BB
+ FETCH_CC 1, %ecx # %ecx<- CC
+ GET_VREG %eax # %eax<- vBB
+ GET_VREG %ecx # %ecx<- vCC
+ cmp $0, %ecx # check for divide by zero
+ je common_errDivideByZero # handle divide by zero
+ cmpl $-1, %ecx # handle -1 special case divide error
+ jne .LOP_REM_INT_noerror
+ cmpl $0x80000000,%eax # handle min int special case divide error
+ je .LOP_REM_INT_break
+.LOP_REM_INT_noerror:
+ cdq # sign-extend %eax to %edx
+ idiv %ecx # divide %edx:%eax by %ecx
+ .if 0
+ SET_VREG %eax rINST # vAA<- %eax (quotient)
+ .else
+ SET_VREG %edx rINST # vAA<- %edx (remainder)
+ .endif
+ jmp .LOP_REM_INT_break2
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_INT: /* 0x95 */
+/* File: x86-atom/OP_AND_INT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_AND_INT.S
+ */
+
+/* File: x86-atom/binop.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binop.S
+ *
+ * Code: Generic 32-bit binary operation. Provides an "instr" line to
+ * specify an instruction that performs "%ecx = %ecx op %edx"
+ *
+ * For: add-int, and-int, mul-int, or-int, sub-int, xor-int
+ *
+ * Description: Perform a binary operation on two source registers
+ * and store the result in a destination register.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %ecx # %ecx<- vBB
+ GET_VREG %edx # %edx<- vCC
+ andl %edx, %ecx # %ecx<- vBB op vCC
+ SET_VREG %ecx, rINST # vAA<- %ecx; result
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_INT: /* 0x96 */
+/* File: x86-atom/OP_OR_INT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_OR_INT.S
+ */
+
+/* File: x86-atom/binop.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binop.S
+ *
+ * Code: Generic 32-bit binary operation. Provides an "instr" line to
+ * specify an instruction that performs "%ecx = %ecx op %edx"
+ *
+ * For: add-int, and-int, mul-int, or-int, sub-int, xor-int
+ *
+ * Description: Perform a binary operation on two source registers
+ * and store the result in a destination register.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %ecx # %ecx<- vBB
+ GET_VREG %edx # %edx<- vCC
+ or %edx, %ecx # %ecx<- vBB op vCC
+ SET_VREG %ecx, rINST # vAA<- %ecx; result
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_INT: /* 0x97 */
+/* File: x86-atom/OP_XOR_INT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_XOR_INT.S
+ */
+
+/* File: x86-atom/binop.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binop.S
+ *
+ * Code: Generic 32-bit binary operation. Provides an "instr" line to
+ * specify an instruction that performs "%ecx = %ecx op %edx"
+ *
+ * For: add-int, and-int, mul-int, or-int, sub-int, xor-int
+ *
+ * Description: Perform a binary operation on two source registers
+ * and store the result in a destination register.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %ecx # %ecx<- vBB
+ GET_VREG %edx # %edx<- vCC
+ xor %edx, %ecx # %ecx<- vBB op vCC
+ SET_VREG %ecx, rINST # vAA<- %ecx; result
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHL_INT: /* 0x98 */
+/* File: x86-atom/OP_SHL_INT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SHL_INT.S
+ */
+
+/* File: x86-atom/binopS.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopS.S
+ *
+ * Code: Generic 32-bit binary operation. Provides an "instr" line to
+ * specify an instruction that performs "%edx = %edx op %cl"
+ *
+ * For: shl-int, shr-int, ushr-int
+ *
+ * Description: Perform a binary operation on two source registers
+ * and store the result in a destination register.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %edx # %edx<- BB
+ FETCH_CC 1, %ecx # %ecx<- CC
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %edx # %edx<- vBB
+ GET_VREG %ecx # %ecx<- vCC
+ sal %cl, %edx # %edx<- vBB op +CC
+ SET_VREG %edx, rINST # vAA<- %edx; result
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHR_INT: /* 0x99 */
+/* File: x86-atom/OP_SHR_INT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SHR_INT.S
+ */
+
+/* File: x86-atom/binopS.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopS.S
+ *
+ * Code: Generic 32-bit binary operation. Provides an "instr" line to
+ * specify an instruction that performs "%edx = %edx op %cl"
+ *
+ * For: shl-int, shr-int, ushr-int
+ *
+ * Description: Perform a binary operation on two source registers
+ * and store the result in a destination register.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %edx # %edx<- BB
+ FETCH_CC 1, %ecx # %ecx<- CC
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %edx # %edx<- vBB
+ GET_VREG %ecx # %ecx<- vCC
+ sar %cl, %edx # %edx<- vBB op +CC
+ SET_VREG %edx, rINST # vAA<- %edx; result
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_USHR_INT: /* 0x9a */
+/* File: x86-atom/OP_USHR_INT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_USHR_INT.S
+ */
+
+/* File: x86-atom/binopS.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopS.S
+ *
+ * Code: Generic 32-bit binary operation. Provides an "instr" line to
+ * specify an instruction that performs "%edx = %edx op %cl"
+ *
+ * For: shl-int, shr-int, ushr-int
+ *
+ * Description: Perform a binary operation on two source registers
+ * and store the result in a destination register.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %edx # %edx<- BB
+ FETCH_CC 1, %ecx # %ecx<- CC
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %edx # %edx<- vBB
+ GET_VREG %ecx # %ecx<- vCC
+ shr %cl, %edx # %edx<- vBB op +CC
+ SET_VREG %edx, rINST # vAA<- %edx; result
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_LONG: /* 0x9b */
+/* File: x86-atom/OP_ADD_LONG.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_ADD_LONG.S
+ */
+
+/* File: x86-atom/binopWide.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopWide.S
+ *
+ * Code: Generic 64-bit binary operation. Provides an "instr" variable to
+ * specify an instruction that performs "%xmm0 = %xmm0 op %xmm1"
+ *
+ * For: add-double, add-long, and-long, mul-double, or-long,
+ * sub-double, sub-long, xor-long
+ *
+ * Description: Perform a binary operation on two source registers
+ * and store the result in a destination register.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movq (rFP, %ecx, 4), %xmm0 # %xmm0<- vBB
+ movq (rFP, %edx, 4), %xmm1 # %xmm1<- vCC
+ paddq %xmm1, %xmm0 # %xmm0<- vBB op vCC
+ movq %xmm0, (rFP, rINST, 4) # vAA<- %ecx
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_LONG: /* 0x9c */
+/* File: x86-atom/OP_SUB_LONG.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SUB_LONG.S
+ */
+
+/* File: x86-atom/binopWide.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopWide.S
+ *
+ * Code: Generic 64-bit binary operation. Provides an "instr" variable to
+ * specify an instruction that performs "%xmm0 = %xmm0 op %xmm1"
+ *
+ * For: add-double, add-long, and-long, mul-double, or-long,
+ * sub-double, sub-long, xor-long
+ *
+ * Description: Perform a binary operation on two source registers
+ * and store the result in a destination register.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movq (rFP, %ecx, 4), %xmm0 # %xmm0<- vBB
+ movq (rFP, %edx, 4), %xmm1 # %xmm1<- vCC
+ psubq %xmm1, %xmm0 # %xmm0<- vBB op vCC
+ movq %xmm0, (rFP, rINST, 4) # vAA<- %ecx
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_LONG: /* 0x9d */
+/* File: x86-atom/OP_MUL_LONG.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MUL_LONG.S
+ *
+ * Code: 64-bit integer multiply
+ *
+ * For: mul-long
+ *
+ * Description: Multiply two source registers and store the
+ * result in a destination register.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ /*
+ * Signed 64-bit integer multiply.
+ *
+ * Consider WXxYZ (r1r0 x r3r2) with a long multiply:
+ * WX
+ * x YZ
+ * --------
+ * ZW ZX
+ * YW YX
+ *
+ * The low word of the result holds ZX, the high word holds
+ * (ZW+YX) + (the high overflow from ZX). YW doesn't matter because
+ * it doesn't fit in the low 64 bits.
+ */
+
+ movl rINST, -4(%esp) # -4(%esp)<- AA+
+ FETCH_BB 1, rINST # rINST<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ jmp .LOP_MUL_LONG_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_LONG: /* 0x9e */
+/* File: x86-atom/OP_DIV_LONG.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_DIV_LONG.S
+ */
+
+/* File: x86-atom/binopDivRemLong.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopDivRemLong.S
+ *
+ * Code: 64-bit long divide operation. Variable
+ * "func" defines the function called to do the operation.
+ *
+ * For: div-long, rem-long
+ *
+ * Description: Perform a binary operation on two source registers
+ * and store the result in a destination register.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+
+ FETCH_CC 1, %edx # %edx<- CC
+ movl (rFP, %edx, 4), %eax # %eax<- vCC
+ movl 4(rFP, %edx, 4), %ecx # %ecx<- vCC+1
+ movl %eax, -8(%esp) # push arg vCC
+ or %ecx, %eax # check for divide by zero
+ je common_errDivideByZero # handle divide by zero
+ FETCH_BB 1, %edx # %edx<- BB
+ movl %ecx, -4(%esp) # push arg vCC+1
+ movq (rFP, %edx, 4), %xmm0 # %xmm0<- vBB,vBB+1
+ jmp .LOP_DIV_LONG_finish
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_LONG: /* 0x9f */
+/* File: x86-atom/OP_REM_LONG.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_REM_LONG.S
+ */
+
+/* File: x86-atom/binopDivRemLong.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopDivRemLong.S
+ *
+ * Code: 64-bit long divide operation. Variable
+ * "func" defines the function called to do the operation.
+ *
+ * For: div-long, rem-long
+ *
+ * Description: Perform a binary operation on two source registers
+ * and store the result in a destination register.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+
+ FETCH_CC 1, %edx # %edx<- CC
+ movl (rFP, %edx, 4), %eax # %eax<- vCC
+ movl 4(rFP, %edx, 4), %ecx # %ecx<- vCC+1
+ movl %eax, -8(%esp) # push arg vCC
+ or %ecx, %eax # check for divide by zero
+ je common_errDivideByZero # handle divide by zero
+ FETCH_BB 1, %edx # %edx<- BB
+ movl %ecx, -4(%esp) # push arg vCC+1
+ movq (rFP, %edx, 4), %xmm0 # %xmm0<- vBB,vBB+1
+ jmp .LOP_REM_LONG_finish
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_LONG: /* 0xa0 */
+/* File: x86-atom/OP_AND_LONG.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_AND_LONG.S
+ */
+
+/* File: x86-atom/binopWide.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopWide.S
+ *
+ * Code: Generic 64-bit binary operation. Provides an "instr" variable to
+ * specify an instruction that performs "%xmm0 = %xmm0 op %xmm1"
+ *
+ * For: add-double, add-long, and-long, mul-double, or-long,
+ * sub-double, sub-long, xor-long
+ *
+ * Description: Perform a binary operation on two source registers
+ * and store the result in a destination register.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movq (rFP, %ecx, 4), %xmm0 # %xmm0<- vBB
+ movq (rFP, %edx, 4), %xmm1 # %xmm1<- vCC
+ pand %xmm1, %xmm0 # %xmm0<- vBB op vCC
+ movq %xmm0, (rFP, rINST, 4) # vAA<- %ecx
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_LONG: /* 0xa1 */
+/* File: x86-atom/OP_OR_LONG.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_OR_LONG.S
+ */
+
+/* File: x86-atom/binopWide.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopWide.S
+ *
+ * Code: Generic 64-bit binary operation. Provides an "instr" variable to
+ * specify an instruction that performs "%xmm0 = %xmm0 op %xmm1"
+ *
+ * For: add-double, add-long, and-long, mul-double, or-long,
+ * sub-double, sub-long, xor-long
+ *
+ * Description: Perform a binary operation on two source registers
+ * and store the result in a destination register.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movq (rFP, %ecx, 4), %xmm0 # %xmm0<- vBB
+ movq (rFP, %edx, 4), %xmm1 # %xmm1<- vCC
+ por %xmm1, %xmm0 # %xmm0<- vBB op vCC
+ movq %xmm0, (rFP, rINST, 4) # vAA<- %ecx
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_LONG: /* 0xa2 */
+/* File: x86-atom/OP_XOR_LONG.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_XOR_LONG.S
+ */
+
+/* File: x86-atom/binopWide.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopWide.S
+ *
+ * Code: Generic 64-bit binary operation. Provides an "instr" variable to
+ * specify an instruction that performs "%xmm0 = %xmm0 op %xmm1"
+ *
+ * For: add-double, add-long, and-long, mul-double, or-long,
+ * sub-double, sub-long, xor-long
+ *
+ * Description: Perform a binary operation on two source registers
+ * and store the result in a destination register.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movq (rFP, %ecx, 4), %xmm0 # %xmm0<- vBB
+ movq (rFP, %edx, 4), %xmm1 # %xmm1<- vCC
+ pxor %xmm1, %xmm0 # %xmm0<- vBB op vCC
+ movq %xmm0, (rFP, rINST, 4) # vAA<- %ecx
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHL_LONG: /* 0xa3 */
+/* File: x86-atom/OP_SHL_LONG.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SHL_LONG.S
+ *
+ * Code: Performs a shift left long. Uses no substitutions.
+ *
+ * For: shl-long
+ *
+ * Description: Perform a binary shift operation using two source registers
+ * where one is the shift amount and the other is the value to shift.
+ * Store the result in a destination register.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_CC 1, %eax # %eax<- CC
+ FETCH_BB 1, %edx # %edx<- BB
+ movq .LshiftMask, %xmm2 # %xmm2<- mask for the shift bits
+ movss (rFP, %eax, 4), %xmm0 # %xmm0<- vCC
+ pand %xmm2, %xmm0 # %xmm0<- masked shift bits
+ movq (rFP, %edx, 4), %xmm1 # %xmm1<- vBB
+ psllq %xmm0, %xmm1 # %xmm1<- shifted vBB
+ movq %xmm1, (rFP, rINST, 4) # vAA<- shifted vBB
+ FINISH 2 # jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHR_LONG: /* 0xa4 */
+/* File: x86-atom/OP_SHR_LONG.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SHR_LONG.S
+ *
+ * Code: Performs a shift right long
+ *
+ * For: shl-long
+ *
+ * Description: Perform a binary shift operation using two source registers
+ * where one is the shift amount and the other is the value to shift.
+ * Store the result in a destination register.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %edx # %edx<- BB
+ FETCH_CC 1, %eax # %eax<- CC
+ movq (rFP, %edx, 4), %xmm1 # %xmm1<- vBB
+ movss (rFP, %eax, 4), %xmm0 # %xmm0<- vCC
+ movq .LshiftMask, %xmm2
+ pand %xmm2, %xmm0 # %xmm0<- masked for the shift bits
+ psrlq %xmm0, %xmm1 # %xmm1<- shifted vBB
+ cmpl $0, 4(rFP, %edx, 4) # check if we need to consider sign
+ jl .LOP_SHR_LONG_finish # consider sign
+ jmp .LOP_SHR_LONG_final # sign is fine, finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_USHR_LONG: /* 0xa5 */
+/* File: x86-atom/OP_USHR_LONG.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_USHR_LONG.S
+ *
+ * Code: Performs an unsigned shift right long operation. Uses no substitutions.
+ *
+ * For: ushr-long
+ *
+ * Description: Perform a binary shift operation using two source registers
+ * where one is the shift amount and the other is the value to shift.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_CC 1, %eax # %eax<- CC
+ FETCH_BB 1, %edx # %edx<- BB
+ movsd .LshiftMask, %xmm2 # %xmm2<- mask for the shift bits
+ movss (rFP, %eax, 4), %xmm0 # %xmm0<- vCC
+ pand %xmm2, %xmm0 # %xmm0<- masked shift bits
+ movsd (rFP, %edx, 4), %xmm1 # %xmm1<- vBB
+ psrlq %xmm0, %xmm1 # %xmm1<- shifted vBB
+ movsd %xmm1, (rFP, rINST, 4) # vAA<- shifted vBB
+ FINISH 2 # jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_FLOAT: /* 0xa6 */
+/* File: x86-atom/OP_ADD_FLOAT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_ADD_FLOAT.S
+ */
+
+/* File: x86-atom/binopF.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopF.S
+ *
+ * Code: Generic 32-bit binary operation. Provides an "instr" line to
+ * specify an instruction that performs "%xmm0 = %xmm0 op %xmm1"
+ *
+ * For: add-float, mul-float, sub-float
+ *
+ * Description: Perform a binary operation on two source registers
+ * and store the result in a destination register.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movss (rFP, %ecx, 4), %xmm0 # %xmm0<-vBB
+ movss (rFP, %edx, 4), %xmm1 # %xmm1<- vCC
+ addss %xmm1, %xmm0 # %xmm0<- vBB op vCC
+ movss %xmm0, (rFP, rINST, 4) # vAA<- %xmm0; result
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_FLOAT: /* 0xa7 */
+/* File: x86-atom/OP_SUB_FLOAT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SUB_FLOAT.S
+ */
+
+/* File: x86-atom/binopF.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopF.S
+ *
+ * Code: Generic 32-bit binary operation. Provides an "instr" line to
+ * specify an instruction that performs "%xmm0 = %xmm0 op %xmm1"
+ *
+ * For: add-float, mul-float, sub-float
+ *
+ * Description: Perform a binary operation on two source registers
+ * and store the result in a destination register.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movss (rFP, %ecx, 4), %xmm0 # %xmm0<-vBB
+ movss (rFP, %edx, 4), %xmm1 # %xmm1<- vCC
+ subss %xmm1, %xmm0 # %xmm0<- vBB op vCC
+ movss %xmm0, (rFP, rINST, 4) # vAA<- %xmm0; result
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_FLOAT: /* 0xa8 */
+/* File: x86-atom/OP_MUL_FLOAT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MUL_FLOAT.S
+ */
+
+/* File: x86-atom/binopF.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopF.S
+ *
+ * Code: Generic 32-bit binary operation. Provides an "instr" line to
+ * specify an instruction that performs "%xmm0 = %xmm0 op %xmm1"
+ *
+ * For: add-float, mul-float, sub-float
+ *
+ * Description: Perform a binary operation on two source registers
+ * and store the result in a destination register.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movss (rFP, %ecx, 4), %xmm0 # %xmm0<-vBB
+ movss (rFP, %edx, 4), %xmm1 # %xmm1<- vCC
+ mulss %xmm1, %xmm0 # %xmm0<- vBB op vCC
+ movss %xmm0, (rFP, rINST, 4) # vAA<- %xmm0; result
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_FLOAT: /* 0xa9 */
+/* File: x86-atom/OP_DIV_FLOAT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_DIV_FLOAT.S
+ *
+ * Code: Divides floats. Uses no substitutions.
+ *
+ * For: div-float
+ *
+ * Description: Divide operation on two source registers, storing
+ * the result in a destiniation register
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %eax # %eax<- BB
+ FETCH_CC 1, %ecx # %ecx<- CC
+ flds (rFP, %eax, 4) # floating point stack vBB
+ fdivs (rFP, %ecx, 4) # divide double; vBB/vCC
+ fstps (rFP, rINST, 4) # vAA<- result
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_FLOAT: /* 0xaa */
+/* File: x86-atom/OP_REM_FLOAT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_REM_FLOAT.S
+ *
+ * Code: Computes the remainder of a division. Performs no substitutions.
+ *
+ * For: rem-float
+ *
+ * Description: Calls fmod to compute the remainder of the result of dividing a
+ * source register by a second, and stores the result in a
+ * destination register.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ GET_VREG %ecx # %ecx<- vBB
+ GET_VREG %edx # %edx<- vCC
+ movl %ecx, -8(%esp) # push parameter float
+ movl %edx, -4(%esp) # push parameter float
+ lea -8(%esp), %esp
+ call fmodf # call: (float x, float y)
+ # return: float
+ lea 8(%esp), %esp
+ fstps (rFP, rINST, 4) # vAA<- remainder; return of fmod
+ FINISH 2 # jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_DOUBLE: /* 0xab */
+/* File: x86-atom/OP_ADD_DOUBLE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_ADD_DOUBLE.S
+ */
+
+/* File: x86-atom/binopWide.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopWide.S
+ *
+ * Code: Generic 64-bit binary operation. Provides an "instr" variable to
+ * specify an instruction that performs "%xmm0 = %xmm0 op %xmm1"
+ *
+ * For: add-double, add-long, and-long, mul-double, or-long,
+ * sub-double, sub-long, xor-long
+ *
+ * Description: Perform a binary operation on two source registers
+ * and store the result in a destination register.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movq (rFP, %ecx, 4), %xmm0 # %xmm0<- vBB
+ movq (rFP, %edx, 4), %xmm1 # %xmm1<- vCC
+ addsd %xmm1, %xmm0 # %xmm0<- vBB op vCC
+ movq %xmm0, (rFP, rINST, 4) # vAA<- %ecx
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_DOUBLE: /* 0xac */
+/* File: x86-atom/OP_SUB_DOUBLE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SUB_DOUBLE.S
+ */
+
+/* File: x86-atom/binopWide.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopWide.S
+ *
+ * Code: Generic 64-bit binary operation. Provides an "instr" variable to
+ * specify an instruction that performs "%xmm0 = %xmm0 op %xmm1"
+ *
+ * For: add-double, add-long, and-long, mul-double, or-long,
+ * sub-double, sub-long, xor-long
+ *
+ * Description: Perform a binary operation on two source registers
+ * and store the result in a destination register.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movq (rFP, %ecx, 4), %xmm0 # %xmm0<- vBB
+ movq (rFP, %edx, 4), %xmm1 # %xmm1<- vCC
+ subsd %xmm1, %xmm0 # %xmm0<- vBB op vCC
+ movq %xmm0, (rFP, rINST, 4) # vAA<- %ecx
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_DOUBLE: /* 0xad */
+/* File: x86-atom/OP_MUL_DOUBLE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MUL_DOUBLE.S
+ */
+
+/* File: x86-atom/binopWide.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopWide.S
+ *
+ * Code: Generic 64-bit binary operation. Provides an "instr" variable to
+ * specify an instruction that performs "%xmm0 = %xmm0 op %xmm1"
+ *
+ * For: add-double, add-long, and-long, mul-double, or-long,
+ * sub-double, sub-long, xor-long
+ *
+ * Description: Perform a binary operation on two source registers
+ * and store the result in a destination register.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movq (rFP, %ecx, 4), %xmm0 # %xmm0<- vBB
+ movq (rFP, %edx, 4), %xmm1 # %xmm1<- vCC
+ mulsd %xmm1, %xmm0 # %xmm0<- vBB op vCC
+ movq %xmm0, (rFP, rINST, 4) # vAA<- %ecx
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_DOUBLE: /* 0xae */
+/* File: x86-atom/OP_DIV_DOUBLE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_DIV_DOUBLE.S
+ *
+ * Code: Divides doubles. Uses no substitutions.
+ *
+ * For: div-double
+ *
+ * Description: Divide operation on two source registers, storing
+ * the result in a destination register
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ fldl (rFP, %ecx, 4) # floating point stack vBB
+ fdivl (rFP, %edx, 4) # divide double; vBB/vCC
+ fstpl (rFP, rINST, 4) # vAA<- result
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_DOUBLE: /* 0xaf */
+/* File: x86-atom/OP_REM_DOUBLE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_REM_DOUBLE.S
+ *
+ * Code: Computes the remainder of a division. Performs no substitutions.
+ *
+ * For: rem-double
+ *
+ * Description: Calls fmod to compute the remainder of the result of dividing a
+ * source register by a second, and stores the result in a
+ * destination register.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ movl (rFP, %ecx, 4), %eax # %eax<- vBBlo
+ movl %eax, -16(%esp) # push parameter double lo
+ movl 4(rFP, %ecx, 4), %eax # %eax<- vBBhi
+ movl %eax, -12(%esp) # push parameter double hi
+ movl (rFP, %edx, 4), %eax # %eax<- vCClo
+ movl %eax, -8(%esp) # push parameter double lo
+ movl 4(rFP, %edx, 4), %eax # %eax<- vCChi
+ movl %eax, -4(%esp) # push parameter double hi
+ lea -16(%esp), %esp
+ jmp .LOP_REM_DOUBLE_break
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_INT_2ADDR: /* 0xb0 */
+/* File: x86-atom/OP_ADD_INT_2ADDR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_ADD_INT_2ADDR.S
+ */
+
+/* File: x86-atom/binop2addr.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binop2addr.S
+ *
+ * Code: Generic 32-bit "/2addr" binary operation. Provides an
+ * "instr" line to specify an instruction that performs
+ * "%ecx = %ecx op %edx".
+ *
+ * For: add-int/2addr, and-int/2addr, mul-int/2addr, or-int/2addr,
+ * sub-int/2addr, xor-int/2addr
+ *
+ * Description: Perform a binary operation on two sources registers
+ * and store the result in the first source register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $4, %edx # %edx<- B
+ andl $15, rINST # rINST<- A
+ movl rINST, %ecx # %ecx<- A
+ FFETCH_ADV 1, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %edx # %edx<- vB
+ GET_VREG %ecx # %ecx<- vA
+ addl %edx, %ecx # %ecx<- vA op vB
+ SET_VREG %ecx, rINST # vAA<- %ecx; result
+ FGETOP_JMP 1, %eax # jump to next instruction; getop, jmp
+
+
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_INT_2ADDR: /* 0xb1 */
+/* File: x86-atom/OP_SUB_INT_2ADDR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SUB_INT_2ADDR.S
+ */
+
+/* File: x86-atom/binop2addr.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binop2addr.S
+ *
+ * Code: Generic 32-bit "/2addr" binary operation. Provides an
+ * "instr" line to specify an instruction that performs
+ * "%ecx = %ecx op %edx".
+ *
+ * For: add-int/2addr, and-int/2addr, mul-int/2addr, or-int/2addr,
+ * sub-int/2addr, xor-int/2addr
+ *
+ * Description: Perform a binary operation on two sources registers
+ * and store the result in the first source register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $4, %edx # %edx<- B
+ andl $15, rINST # rINST<- A
+ movl rINST, %ecx # %ecx<- A
+ FFETCH_ADV 1, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %edx # %edx<- vB
+ GET_VREG %ecx # %ecx<- vA
+ subl %edx, %ecx # %ecx<- vA op vB
+ SET_VREG %ecx, rINST # vAA<- %ecx; result
+ FGETOP_JMP 1, %eax # jump to next instruction; getop, jmp
+
+
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_INT_2ADDR: /* 0xb2 */
+/* File: x86-atom/OP_MUL_INT_2ADDR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MUL_INT_2ADDR.S
+ */
+
+/* File: x86-atom/binop2addr.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binop2addr.S
+ *
+ * Code: Generic 32-bit "/2addr" binary operation. Provides an
+ * "instr" line to specify an instruction that performs
+ * "%ecx = %ecx op %edx".
+ *
+ * For: add-int/2addr, and-int/2addr, mul-int/2addr, or-int/2addr,
+ * sub-int/2addr, xor-int/2addr
+ *
+ * Description: Perform a binary operation on two sources registers
+ * and store the result in the first source register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $4, %edx # %edx<- B
+ andl $15, rINST # rINST<- A
+ movl rINST, %ecx # %ecx<- A
+ FFETCH_ADV 1, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %edx # %edx<- vB
+ GET_VREG %ecx # %ecx<- vA
+ imul %edx, %ecx # %ecx<- vA op vB
+ SET_VREG %ecx, rINST # vAA<- %ecx; result
+ FGETOP_JMP 1, %eax # jump to next instruction; getop, jmp
+
+
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_INT_2ADDR: /* 0xb3 */
+/* File: x86-atom/OP_DIV_INT_2ADDR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_DIV_INT_2ADDR.S
+ */
+
+/* File: x86-atom/binopD2addr.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopD2addr.S
+ *
+ * Code: 32-bit "/2addr" integer divde operation. If "div"
+ * is set, the code returns the quotient, else it returns
+ * the remainder. Also, a divide-by-zero check is done.
+ *
+ * For: div-int/2addr, rem-int/2addr
+ *
+ * Description: Perform a binary operation on two sources registers
+ * and store the result in the first source register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %ecx # %ecx<- BA
+ andl $15, rINST # rINST<- A, to be used as dest
+ movl rINST, %eax # %eax<- A
+ shr $4, %ecx # %ecx<- B
+ GET_VREG %eax # %eax<- vA
+ GET_VREG %ecx # %edx<- vB
+ cmp $0, %ecx # check for divide by zero
+ je common_errDivideByZero # handle divide by zero
+ cmpl $-1, %ecx # handle -1 special case divide error
+ jne .LOP_DIV_INT_2ADDR_noerror
+ cmpl $0x80000000,%eax # handle min int special case divide error
+ je .LOP_DIV_INT_2ADDR_break
+.LOP_DIV_INT_2ADDR_noerror:
+ cdq # sign-extend %eax to %edx
+ idiv %ecx # divide %edx:%eax by %ecx
+ .if 1
+ SET_VREG %eax rINST # vAA<- %eax (quotient)
+ .else
+ SET_VREG %edx rINST # vAA<- %edx (remainder)
+ .endif
+ jmp .LOP_DIV_INT_2ADDR_break2
+ #FFETCH_ADV 1, %edx # %ecx<- next instruction hi; fetch, advance
+ #FGETOP_JMP 1, %edx # jump to next instruction; getop, jmp
+
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_INT_2ADDR: /* 0xb4 */
+/* File: x86-atom/OP_REM_INT_2ADDR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_REM_INT_2ADDR.S
+ */
+
+/* File: x86-atom/binopD2addr.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopD2addr.S
+ *
+ * Code: 32-bit "/2addr" integer divde operation. If "div"
+ * is set, the code returns the quotient, else it returns
+ * the remainder. Also, a divide-by-zero check is done.
+ *
+ * For: div-int/2addr, rem-int/2addr
+ *
+ * Description: Perform a binary operation on two sources registers
+ * and store the result in the first source register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %ecx # %ecx<- BA
+ andl $15, rINST # rINST<- A, to be used as dest
+ movl rINST, %eax # %eax<- A
+ shr $4, %ecx # %ecx<- B
+ GET_VREG %eax # %eax<- vA
+ GET_VREG %ecx # %edx<- vB
+ cmp $0, %ecx # check for divide by zero
+ je common_errDivideByZero # handle divide by zero
+ cmpl $-1, %ecx # handle -1 special case divide error
+ jne .LOP_REM_INT_2ADDR_noerror
+ cmpl $0x80000000,%eax # handle min int special case divide error
+ je .LOP_REM_INT_2ADDR_break
+.LOP_REM_INT_2ADDR_noerror:
+ cdq # sign-extend %eax to %edx
+ idiv %ecx # divide %edx:%eax by %ecx
+ .if 0
+ SET_VREG %eax rINST # vAA<- %eax (quotient)
+ .else
+ SET_VREG %edx rINST # vAA<- %edx (remainder)
+ .endif
+ jmp .LOP_REM_INT_2ADDR_break2
+ #FFETCH_ADV 1, %edx # %ecx<- next instruction hi; fetch, advance
+ #FGETOP_JMP 1, %edx # jump to next instruction; getop, jmp
+
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_INT_2ADDR: /* 0xb5 */
+/* File: x86-atom/OP_AND_INT_2ADDR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_AND_INT_2ADDR.S
+ */
+
+/* File: x86-atom/binop2addr.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binop2addr.S
+ *
+ * Code: Generic 32-bit "/2addr" binary operation. Provides an
+ * "instr" line to specify an instruction that performs
+ * "%ecx = %ecx op %edx".
+ *
+ * For: add-int/2addr, and-int/2addr, mul-int/2addr, or-int/2addr,
+ * sub-int/2addr, xor-int/2addr
+ *
+ * Description: Perform a binary operation on two sources registers
+ * and store the result in the first source register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $4, %edx # %edx<- B
+ andl $15, rINST # rINST<- A
+ movl rINST, %ecx # %ecx<- A
+ FFETCH_ADV 1, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %edx # %edx<- vB
+ GET_VREG %ecx # %ecx<- vA
+ andl %edx, %ecx # %ecx<- vA op vB
+ SET_VREG %ecx, rINST # vAA<- %ecx; result
+ FGETOP_JMP 1, %eax # jump to next instruction; getop, jmp
+
+
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_INT_2ADDR: /* 0xb6 */
+/* File: x86-atom/OP_OR_INT_2ADDR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_OR_INT_2ADDR.S
+ */
+
+/* File: x86-atom/binop2addr.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binop2addr.S
+ *
+ * Code: Generic 32-bit "/2addr" binary operation. Provides an
+ * "instr" line to specify an instruction that performs
+ * "%ecx = %ecx op %edx".
+ *
+ * For: add-int/2addr, and-int/2addr, mul-int/2addr, or-int/2addr,
+ * sub-int/2addr, xor-int/2addr
+ *
+ * Description: Perform a binary operation on two sources registers
+ * and store the result in the first source register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $4, %edx # %edx<- B
+ andl $15, rINST # rINST<- A
+ movl rINST, %ecx # %ecx<- A
+ FFETCH_ADV 1, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %edx # %edx<- vB
+ GET_VREG %ecx # %ecx<- vA
+ or %edx, %ecx # %ecx<- vA op vB
+ SET_VREG %ecx, rINST # vAA<- %ecx; result
+ FGETOP_JMP 1, %eax # jump to next instruction; getop, jmp
+
+
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_INT_2ADDR: /* 0xb7 */
+/* File: x86-atom/OP_XOR_INT_2ADDR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_XOR_INT_2ADDR.S
+ */
+
+/* File: x86-atom/binop2addr.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binop2addr.S
+ *
+ * Code: Generic 32-bit "/2addr" binary operation. Provides an
+ * "instr" line to specify an instruction that performs
+ * "%ecx = %ecx op %edx".
+ *
+ * For: add-int/2addr, and-int/2addr, mul-int/2addr, or-int/2addr,
+ * sub-int/2addr, xor-int/2addr
+ *
+ * Description: Perform a binary operation on two sources registers
+ * and store the result in the first source register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $4, %edx # %edx<- B
+ andl $15, rINST # rINST<- A
+ movl rINST, %ecx # %ecx<- A
+ FFETCH_ADV 1, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %edx # %edx<- vB
+ GET_VREG %ecx # %ecx<- vA
+ xor %edx, %ecx # %ecx<- vA op vB
+ SET_VREG %ecx, rINST # vAA<- %ecx; result
+ FGETOP_JMP 1, %eax # jump to next instruction; getop, jmp
+
+
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHL_INT_2ADDR: /* 0xb8 */
+/* File: x86-atom/OP_SHL_INT_2ADDR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SHL_INT_2ADDR.S
+ */
+
+/* File: x86-atom/binopS2addr.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopS2addr.S
+ *
+ * Code: Generic 32-bit "/2addr" binary operation. Provides an
+ * "instr" line to specify an instruction that performs
+ * "%edx = %edx op %cl".
+ *
+ * For: shl-int/2addr, shr-int/2addr, ushr-int/2addr
+ *
+ * Description: Perform a binary operation on two sources registers
+ * and store the result in the first source register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %ecx # %ecx<- BA
+ shr $4, %ecx # %ecx<- B
+ andl $15, rINST # rINST<- A
+ movl rINST, %edx # %edx<- A
+ FFETCH_ADV 1, %eax # %ecx<- next instruction hi; fetch, advance
+ GET_VREG %ecx # %ecx<- vB
+ GET_VREG %edx # %edx<- vA
+ sal %cl, %edx # %edx<- vA op vB
+ SET_VREG %edx, rINST # vAA<- %edx; result
+ FGETOP_JMP 1, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHR_INT_2ADDR: /* 0xb9 */
+/* File: x86-atom/OP_SHR_INT_2ADDR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SHR_INT_2ADDR.S
+ */
+
+/* File: x86-atom/binopS2addr.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopS2addr.S
+ *
+ * Code: Generic 32-bit "/2addr" binary operation. Provides an
+ * "instr" line to specify an instruction that performs
+ * "%edx = %edx op %cl".
+ *
+ * For: shl-int/2addr, shr-int/2addr, ushr-int/2addr
+ *
+ * Description: Perform a binary operation on two sources registers
+ * and store the result in the first source register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %ecx # %ecx<- BA
+ shr $4, %ecx # %ecx<- B
+ andl $15, rINST # rINST<- A
+ movl rINST, %edx # %edx<- A
+ FFETCH_ADV 1, %eax # %ecx<- next instruction hi; fetch, advance
+ GET_VREG %ecx # %ecx<- vB
+ GET_VREG %edx # %edx<- vA
+ sar %cl, %edx # %edx<- vA op vB
+ SET_VREG %edx, rINST # vAA<- %edx; result
+ FGETOP_JMP 1, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_USHR_INT_2ADDR: /* 0xba */
+/* File: x86-atom/OP_USHR_INT_2ADDR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_USHR_INT_2ADDR.S
+ */
+
+/* File: x86-atom/binopS2addr.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopS2addr.S
+ *
+ * Code: Generic 32-bit "/2addr" binary operation. Provides an
+ * "instr" line to specify an instruction that performs
+ * "%edx = %edx op %cl".
+ *
+ * For: shl-int/2addr, shr-int/2addr, ushr-int/2addr
+ *
+ * Description: Perform a binary operation on two sources registers
+ * and store the result in the first source register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %ecx # %ecx<- BA
+ shr $4, %ecx # %ecx<- B
+ andl $15, rINST # rINST<- A
+ movl rINST, %edx # %edx<- A
+ FFETCH_ADV 1, %eax # %ecx<- next instruction hi; fetch, advance
+ GET_VREG %ecx # %ecx<- vB
+ GET_VREG %edx # %edx<- vA
+ shr %cl, %edx # %edx<- vA op vB
+ SET_VREG %edx, rINST # vAA<- %edx; result
+ FGETOP_JMP 1, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_LONG_2ADDR: /* 0xbb */
+/* File: x86-atom/OP_ADD_LONG_2ADDR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_ADD_LONG_2ADDR.S
+ */
+
+/* File: x86-atom/binopWide2addr.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopWide2addr.S
+ *
+ * Code: Generic 64-bit "/2addr" binary operation. Provides an
+ * "instr" line to specify an instruction that performs
+ * "%xmm0= %xmm0 op %xmm1".
+ *
+ * For: add-double/2addr, add-long/2addr, and-long/2addr, mul-long/2addr,
+ * or-long/2addr, sub-double/2addr, sub-long/2addr, xor-long/2addr
+ *
+ * Description: Perform a binary operation on two sources registers
+ * and store the result in the first source register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $4, rINST # rINST<- B
+ andl $15, %edx # %edx<- A
+ movq (rFP, rINST, 4), %xmm1 # %xmm1<- vB
+ movq (rFP, %edx, 4), %xmm0 # %xmm0<- vA
+ paddq %xmm1, %xmm0 # %xmm0<- vA op vB
+ movq %xmm0, (rFP, %edx, 4) # vA<- %xmm0; result
+ FINISH 1 # jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_LONG_2ADDR: /* 0xbc */
+/* File: x86-atom/OP_SUB_LONG_2ADDR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SUB_LONG_2ADDR.S
+ */
+
+/* File: x86-atom/binopWide2addr.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopWide2addr.S
+ *
+ * Code: Generic 64-bit "/2addr" binary operation. Provides an
+ * "instr" line to specify an instruction that performs
+ * "%xmm0= %xmm0 op %xmm1".
+ *
+ * For: add-double/2addr, add-long/2addr, and-long/2addr, mul-long/2addr,
+ * or-long/2addr, sub-double/2addr, sub-long/2addr, xor-long/2addr
+ *
+ * Description: Perform a binary operation on two sources registers
+ * and store the result in the first source register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $4, rINST # rINST<- B
+ andl $15, %edx # %edx<- A
+ movq (rFP, rINST, 4), %xmm1 # %xmm1<- vB
+ movq (rFP, %edx, 4), %xmm0 # %xmm0<- vA
+ psubq %xmm1, %xmm0 # %xmm0<- vA op vB
+ movq %xmm0, (rFP, %edx, 4) # vA<- %xmm0; result
+ FINISH 1 # jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_LONG_2ADDR: /* 0xbd */
+/* File: x86-atom/OP_MUL_LONG_2ADDR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MUL_LONG_2ADDR.S
+ *
+ * Code: 64-bit integer multiply
+ *
+ * For: mul-long/2addr
+ *
+ * Description: Multiply two sources registers and store the result
+ * in the first source register.
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ /*
+ * Signed 64-bit integer multiply.
+ *
+ * Consider WXxYZ (r1r0 x r3r2) with a long multiply:
+ * WX
+ * x YZ
+ * --------
+ * ZW ZX
+ * YW YX
+ *
+ * The low word of the result holds ZX, the high word holds
+ * (ZW+YX) + (the high overflow from ZX). YW doesn't matter because
+ * it doesn't fit in the low 64 bits.
+ */
+
+ movl rINST, %edx # %edx<- BA+
+ shr $4, rINST # rINST<- B
+ andl $15, %edx # %edx<- A
+ movl %edx, sReg0 # sReg0<- A
+ jmp .LOP_MUL_LONG_2ADDR_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_LONG_2ADDR: /* 0xbe */
+/* File: x86-atom/OP_DIV_LONG_2ADDR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_DIV_LONG_2ADDR.S
+ */
+
+/* File: x86-atom/binopDivRemLong2Addr.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopDivRemLong2Addr.S
+ *
+ * Code: 64-bit "/2addr" long divide operation. Variable
+ * "func" defines the function called to do the operation.
+ *
+ * For: div-long/2addr, rem-long/2addr
+ *
+ * Description: Perform a binary operation on two sources registers
+ * and store the result in the first source register.
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+
+ movl rINST, %edx # %edx<- BA
+ shr $4, %edx # %edx<- B
+ and $15, rINST # rINST<- A
+ movl (rFP, %edx, 4), %eax # %eax<- vB
+ movl %eax, -12(%esp) # push arg vB
+ movl 4(rFP, %edx, 4), %ecx # %ecx<- vB+1
+ or %ecx, %eax # check for divide by zero
+ je common_errDivideByZero # handle divide by zero
+ movl %ecx, -8(%esp) # push arg vB+1
+ movq (rFP, rINST, 4), %xmm0 # %xmm0<- vA,vA+1
+ jmp .LOP_DIV_LONG_2ADDR_break
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_LONG_2ADDR: /* 0xbf */
+/* File: x86-atom/OP_REM_LONG_2ADDR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_REM_LONG_2ADDR.S
+ */
+
+/* File: x86-atom/binopDivRemLong2Addr.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopDivRemLong2Addr.S
+ *
+ * Code: 64-bit "/2addr" long divide operation. Variable
+ * "func" defines the function called to do the operation.
+ *
+ * For: div-long/2addr, rem-long/2addr
+ *
+ * Description: Perform a binary operation on two sources registers
+ * and store the result in the first source register.
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+
+ movl rINST, %edx # %edx<- BA
+ shr $4, %edx # %edx<- B
+ and $15, rINST # rINST<- A
+ movl (rFP, %edx, 4), %eax # %eax<- vB
+ movl %eax, -12(%esp) # push arg vB
+ movl 4(rFP, %edx, 4), %ecx # %ecx<- vB+1
+ or %ecx, %eax # check for divide by zero
+ je common_errDivideByZero # handle divide by zero
+ movl %ecx, -8(%esp) # push arg vB+1
+ movq (rFP, rINST, 4), %xmm0 # %xmm0<- vA,vA+1
+ jmp .LOP_REM_LONG_2ADDR_break
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_LONG_2ADDR: /* 0xc0 */
+/* File: x86-atom/OP_AND_LONG_2ADDR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_AND_LONG_2ADDR.S
+ */
+
+/* File: x86-atom/binopWide2addr.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopWide2addr.S
+ *
+ * Code: Generic 64-bit "/2addr" binary operation. Provides an
+ * "instr" line to specify an instruction that performs
+ * "%xmm0= %xmm0 op %xmm1".
+ *
+ * For: add-double/2addr, add-long/2addr, and-long/2addr, mul-long/2addr,
+ * or-long/2addr, sub-double/2addr, sub-long/2addr, xor-long/2addr
+ *
+ * Description: Perform a binary operation on two sources registers
+ * and store the result in the first source register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $4, rINST # rINST<- B
+ andl $15, %edx # %edx<- A
+ movq (rFP, rINST, 4), %xmm1 # %xmm1<- vB
+ movq (rFP, %edx, 4), %xmm0 # %xmm0<- vA
+ pand %xmm1, %xmm0 # %xmm0<- vA op vB
+ movq %xmm0, (rFP, %edx, 4) # vA<- %xmm0; result
+ FINISH 1 # jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_LONG_2ADDR: /* 0xc1 */
+/* File: x86-atom/OP_OR_LONG_2ADDR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_OR_LONG_2ADDR.S
+ */
+
+/* File: x86-atom/binopWide2addr.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopWide2addr.S
+ *
+ * Code: Generic 64-bit "/2addr" binary operation. Provides an
+ * "instr" line to specify an instruction that performs
+ * "%xmm0= %xmm0 op %xmm1".
+ *
+ * For: add-double/2addr, add-long/2addr, and-long/2addr, mul-long/2addr,
+ * or-long/2addr, sub-double/2addr, sub-long/2addr, xor-long/2addr
+ *
+ * Description: Perform a binary operation on two sources registers
+ * and store the result in the first source register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $4, rINST # rINST<- B
+ andl $15, %edx # %edx<- A
+ movq (rFP, rINST, 4), %xmm1 # %xmm1<- vB
+ movq (rFP, %edx, 4), %xmm0 # %xmm0<- vA
+ por %xmm1, %xmm0 # %xmm0<- vA op vB
+ movq %xmm0, (rFP, %edx, 4) # vA<- %xmm0; result
+ FINISH 1 # jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_LONG_2ADDR: /* 0xc2 */
+/* File: x86-atom/OP_XOR_LONG_2ADDR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_XOR_LONG_2ADDR.S
+ */
+
+/* File: x86-atom/binopWide2addr.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopWide2addr.S
+ *
+ * Code: Generic 64-bit "/2addr" binary operation. Provides an
+ * "instr" line to specify an instruction that performs
+ * "%xmm0= %xmm0 op %xmm1".
+ *
+ * For: add-double/2addr, add-long/2addr, and-long/2addr, mul-long/2addr,
+ * or-long/2addr, sub-double/2addr, sub-long/2addr, xor-long/2addr
+ *
+ * Description: Perform a binary operation on two sources registers
+ * and store the result in the first source register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $4, rINST # rINST<- B
+ andl $15, %edx # %edx<- A
+ movq (rFP, rINST, 4), %xmm1 # %xmm1<- vB
+ movq (rFP, %edx, 4), %xmm0 # %xmm0<- vA
+ pxor %xmm1, %xmm0 # %xmm0<- vA op vB
+ movq %xmm0, (rFP, %edx, 4) # vA<- %xmm0; result
+ FINISH 1 # jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHL_LONG_2ADDR: /* 0xc3 */
+/* File: x86-atom/OP_SHL_LONG_2ADDR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SHL_LONG_2ADDR.S
+ *
+ * Code: Performs a shift left long. Uses no substitutions.
+ *
+ * For: shl-long/2addr
+ *
+ * Description: Perform a binary shift operation using two source registers
+ * where the fist is the value to shift and the second is the
+ * shift amount. Store the result in the first source register.
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $4, %edx # %edx<- B
+ andl $15, rINST # rINST<- A
+ movss (rFP, %edx, 4), %xmm0 # %xmm0<- vB
+ movq (rFP, rINST, 4), %xmm1 # %xmm1<- vA
+ movq .LshiftMask, %xmm2 # %xmm2<- mask for the shift bits
+ pand %xmm2, %xmm0 # %xmm0<- masked shift bits
+ psllq %xmm0, %xmm1 # %xmm1<- shifted vA
+ movq %xmm1, (rFP, rINST, 4) # vA<- shifted vA
+ FINISH 1 # jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHR_LONG_2ADDR: /* 0xc4 */
+/* File: x86-atom/OP_SHR_LONG_2ADDR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SHR_LONG_2ADDR.S
+ *
+ * Code: Performs a shift left long
+ *
+ * For: shl-long/2addr
+ *
+ * Description: Perform a binary shift operation using two source registers
+ * where the fist is the value to shift and the second is the
+ * shift amount. Store the result in the first source register.
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $4, %edx # %edx<- B
+ andl $15, rINST # rINST<- BA
+ movss (rFP, %edx, 4), %xmm0 # %xmm0<- vB
+ movq (rFP, rINST, 4), %xmm1 # %xmm1<- vA
+ movq .LshiftMask, %xmm2
+ pand %xmm2, %xmm0 # %xmm0<- masked for the shift bits
+ psrlq %xmm0, %xmm1 # %xmm1<- shifted vBB
+ cmpl $0, 4(rFP, rINST, 4) # check if we need to consider sign
+ jl .LOP_SHR_LONG_2ADDR_finish # consider sign
+ jmp .LOP_SHR_LONG_2ADDR_final # sign is fine, finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_USHR_LONG_2ADDR: /* 0xc5 */
+/* File: x86-atom/OP_USHR_LONG_2ADDR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_USHR_LONG_2ADDR.S
+ *
+ * Code: Performs an unsigned shift right long operation. Uses no substiutions.
+ *
+ * For: ushr-long/2addr
+ *
+ * Description: Perform a binary shift operation using two source registers
+ * where the fist is the value to shift and the second is the
+ * shift amount. Store the result in the first source register.
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $4, %edx # %edx<- B
+ andl $15, rINST # rINST<- A
+ movq .LshiftMask, %xmm2 # %xmm2<- mask for the shift bits
+ movss (rFP, %edx, 4), %xmm0 # %xmm0<- vB
+ movq (rFP, rINST, 4), %xmm1 # %xmm1<- vA
+ pand %xmm2, %xmm0 # %xmm0<- masked shift bits
+ psrlq %xmm0, %xmm1 # %xmm1<- shifted vA
+ movq %xmm1, (rFP, rINST, 4) # vA<- shifted vA
+ FINISH 1 # jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_FLOAT_2ADDR: /* 0xc6 */
+/* File: x86-atom/OP_ADD_FLOAT_2ADDR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_ADD_FLOAT_2ADDR.S
+ */
+
+/* File: x86-atom/binopF2addr.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopF2addr.S
+ *
+ * Code: Generic 32-bit "/2addr" binary operation. Provides an
+ * "instr" line to specify an instruction that performs
+ * "%xmm0 = %xmm0 op %xmm1".
+ *
+ * For: add-float/2addr, mul-float/2addr, sub-float/2addr
+ *
+ * Description: Perform a binary operation on two sources registers
+ * and store the result in the first source register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %ecx # %ecx<- BA
+ andl $15, %ecx # %ecx<- A
+ shr $4, rINST # rINST<- B
+ FFETCH_ADV 1, %edx # %ecx<- next instruction hi; fetch, advance
+ movss (rFP, %ecx, 4), %xmm0 # %xmm0<- vA
+ movss (rFP, rINST, 4), %xmm1 # %xmm1<- vB
+ addss %xmm1, %xmm0 # %xmm0<- vA op vB
+ movss %xmm0, (rFP, %ecx, 4) # vA<- %xmm0; result
+ FGETOP_JMP 1, %edx # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_FLOAT_2ADDR: /* 0xc7 */
+/* File: x86-atom/OP_SUB_FLOAT_2ADDR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SUB_FLOAT_2ADDR.S
+ */
+
+/* File: x86-atom/binopF2addr.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopF2addr.S
+ *
+ * Code: Generic 32-bit "/2addr" binary operation. Provides an
+ * "instr" line to specify an instruction that performs
+ * "%xmm0 = %xmm0 op %xmm1".
+ *
+ * For: add-float/2addr, mul-float/2addr, sub-float/2addr
+ *
+ * Description: Perform a binary operation on two sources registers
+ * and store the result in the first source register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %ecx # %ecx<- BA
+ andl $15, %ecx # %ecx<- A
+ shr $4, rINST # rINST<- B
+ FFETCH_ADV 1, %edx # %ecx<- next instruction hi; fetch, advance
+ movss (rFP, %ecx, 4), %xmm0 # %xmm0<- vA
+ movss (rFP, rINST, 4), %xmm1 # %xmm1<- vB
+ subss %xmm1, %xmm0 # %xmm0<- vA op vB
+ movss %xmm0, (rFP, %ecx, 4) # vA<- %xmm0; result
+ FGETOP_JMP 1, %edx # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_FLOAT_2ADDR: /* 0xc8 */
+/* File: x86-atom/OP_MUL_FLOAT_2ADDR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MUL_FLOAT_2ADDR.S
+ */
+
+/* File: x86-atom/binopF2addr.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopF2addr.S
+ *
+ * Code: Generic 32-bit "/2addr" binary operation. Provides an
+ * "instr" line to specify an instruction that performs
+ * "%xmm0 = %xmm0 op %xmm1".
+ *
+ * For: add-float/2addr, mul-float/2addr, sub-float/2addr
+ *
+ * Description: Perform a binary operation on two sources registers
+ * and store the result in the first source register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %ecx # %ecx<- BA
+ andl $15, %ecx # %ecx<- A
+ shr $4, rINST # rINST<- B
+ FFETCH_ADV 1, %edx # %ecx<- next instruction hi; fetch, advance
+ movss (rFP, %ecx, 4), %xmm0 # %xmm0<- vA
+ movss (rFP, rINST, 4), %xmm1 # %xmm1<- vB
+ mulss %xmm1, %xmm0 # %xmm0<- vA op vB
+ movss %xmm0, (rFP, %ecx, 4) # vA<- %xmm0; result
+ FGETOP_JMP 1, %edx # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_FLOAT_2ADDR: /* 0xc9 */
+/* File: x86-atom/OP_DIV_FLOAT_2ADDR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_DIV_FLOAT_2ADDR.S
+ *
+ * Code: Divides floats. Uses no substitutions.
+ *
+ * For: div-float/2addr
+ *
+ * Description: Divide operation on two source registers, storing
+ * the result in the first source reigster
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %ecx # %ecx<- BA
+ andl $15, %ecx # %ecx<- A
+ shr $4, rINST # rINST<- B
+ flds (rFP, %ecx, 4) # %xmm0<- vA
+ fdivs (rFP, rINST, 4) # divide double; vA/vB
+ fstps (rFP, %ecx, 4) # vAA<- result
+ FINISH 1 # jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_FLOAT_2ADDR: /* 0xca */
+/* File: x86-atom/OP_REM_FLOAT_2ADDR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_REM_FLOAT_2ADDR.S
+ *
+ * Code: Computes the remainder of a division. Performs no substitutions.
+ *
+ * For: rem-float/2addr
+ *
+ * Description: Calls fmod to compute the remainder of the result of dividing a
+ * source register by a second, and stores the result in the first
+ * source register.
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $4, %edx # %edx<- B
+ andl $15, rINST # rINST<- A
+ GET_VREG %edx # %edx<- vB
+ movl (rFP, rINST, 4), %ecx # %ecx<- vA
+ movl %ecx, -8(%esp) # push parameter vA
+ movl %edx, -4(%esp) # push parameter vB
+ lea -8(%esp), %esp
+ call fmodf # call: (float x, float y)
+ # return: float
+ lea 8(%esp), %esp
+ fstps (rFP, rINST, 4)
+ FINISH 1 # jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_DOUBLE_2ADDR: /* 0xcb */
+/* File: x86-atom/OP_ADD_DOUBLE_2ADDR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_ADD_DOUBLE_2ADDR.S
+ */
+
+/* File: x86-atom/binopWide2addr.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopWide2addr.S
+ *
+ * Code: Generic 64-bit "/2addr" binary operation. Provides an
+ * "instr" line to specify an instruction that performs
+ * "%xmm0= %xmm0 op %xmm1".
+ *
+ * For: add-double/2addr, add-long/2addr, and-long/2addr, mul-long/2addr,
+ * or-long/2addr, sub-double/2addr, sub-long/2addr, xor-long/2addr
+ *
+ * Description: Perform a binary operation on two sources registers
+ * and store the result in the first source register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $4, rINST # rINST<- B
+ andl $15, %edx # %edx<- A
+ movq (rFP, rINST, 4), %xmm1 # %xmm1<- vB
+ movq (rFP, %edx, 4), %xmm0 # %xmm0<- vA
+ addsd %xmm1, %xmm0 # %xmm0<- vA op vB
+ movq %xmm0, (rFP, %edx, 4) # vA<- %xmm0; result
+ FINISH 1 # jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_DOUBLE_2ADDR: /* 0xcc */
+/* File: x86-atom/OP_SUB_DOUBLE_2ADDR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SUB_DOUBLE_2ADDR.S
+ */
+
+/* File: x86-atom/binopWide2addr.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopWide2addr.S
+ *
+ * Code: Generic 64-bit "/2addr" binary operation. Provides an
+ * "instr" line to specify an instruction that performs
+ * "%xmm0= %xmm0 op %xmm1".
+ *
+ * For: add-double/2addr, add-long/2addr, and-long/2addr, mul-long/2addr,
+ * or-long/2addr, sub-double/2addr, sub-long/2addr, xor-long/2addr
+ *
+ * Description: Perform a binary operation on two sources registers
+ * and store the result in the first source register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $4, rINST # rINST<- B
+ andl $15, %edx # %edx<- A
+ movq (rFP, rINST, 4), %xmm1 # %xmm1<- vB
+ movq (rFP, %edx, 4), %xmm0 # %xmm0<- vA
+ subsd %xmm1, %xmm0 # %xmm0<- vA op vB
+ movq %xmm0, (rFP, %edx, 4) # vA<- %xmm0; result
+ FINISH 1 # jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_DOUBLE_2ADDR: /* 0xcd */
+/* File: x86-atom/OP_MUL_DOUBLE_2ADDR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MUL_DOUBLE_2ADDR.S
+ */
+
+/* File: x86-atom/binopWide2addr.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopWide2addr.S
+ *
+ * Code: Generic 64-bit "/2addr" binary operation. Provides an
+ * "instr" line to specify an instruction that performs
+ * "%xmm0= %xmm0 op %xmm1".
+ *
+ * For: add-double/2addr, add-long/2addr, and-long/2addr, mul-long/2addr,
+ * or-long/2addr, sub-double/2addr, sub-long/2addr, xor-long/2addr
+ *
+ * Description: Perform a binary operation on two sources registers
+ * and store the result in the first source register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $4, rINST # rINST<- B
+ andl $15, %edx # %edx<- A
+ movq (rFP, rINST, 4), %xmm1 # %xmm1<- vB
+ movq (rFP, %edx, 4), %xmm0 # %xmm0<- vA
+ mulsd %xmm1, %xmm0 # %xmm0<- vA op vB
+ movq %xmm0, (rFP, %edx, 4) # vA<- %xmm0; result
+ FINISH 1 # jump to next instruction
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_DOUBLE_2ADDR: /* 0xce */
+/* File: x86-atom/OP_DIV_DOUBLE_2ADDR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_DIV_DOUBLE_2ADDR.S
+ *
+ * Code: Divides doubles. Uses no substitutions.
+ *
+ * For: div-double/2addr
+ *
+ * Description: Divide operation on two source registers, storing
+ * the result in the first source reigster
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ andl $15, %edx # %edx<- A
+ shr $4, rINST # rINST<- B
+ fldl (rFP, %edx, 4) # %xmm0<- vA
+ fdivl (rFP, rINST, 4) # divide double; vA/vB
+ fstpl (rFP, %edx, 4) # vAA<- result
+ FINISH 1 # jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_DOUBLE_2ADDR: /* 0xcf */
+/* File: x86-atom/OP_REM_DOUBLE_2ADDR.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_REM_DOUBLE_2ADDR.S
+ *
+ * Code: Computes the remainder of a division. Performs no substitutions.
+ *
+ * For: rem-double/2addr
+ *
+ * Description: Calls fmod to compute the remainder of the result of dividing a
+ * source register by a second, and stores the result in the first
+ * source register.
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ and $15, rINST # rINST<- A
+ shr $4, %edx # %edx<- B
+ movl (rFP, rINST, 4), %eax # %eax<- vAlo
+ movl %eax, -20(%esp) # push parameter vAAlo
+ movl 4(rFP, rINST, 4), %eax # %eax<- vAhi
+ movl %eax, -16(%esp) # push parameter vAAhi
+ movl (rFP, %edx, 4), %eax # %eax<- vBlo
+ movl %eax, -12(%esp) # push parameter vBBlo
+ movl 4(rFP, %edx, 4), %eax # %eax<- vBhi
+ movl %eax, -8(%esp) # push parameter vBBhi
+ lea -20(%esp), %esp
+ jmp .LOP_REM_DOUBLE_2ADDR_break
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_INT_LIT16: /* 0xd0 */
+/* File: x86-atom/OP_ADD_INT_LIT16.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_ADD_INT_LIT16.S
+ */
+
+/* File: x86-atom/binopLit16.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopLit16.S
+ *
+ * Code: 32-bit "lit16" operation. Provides an "instr" line to
+ * specify an instruction that performs "%ecx = %ecx op %edx"
+ *
+ *
+ * For: add-int/lit16, and-int/lit16, mul-int/lit16, or-int/lit16
+ * xor-int/lit16
+ *
+ * Description: Perform a binary operation on a register and a
+ * sign extended 16-bit literal value and store the
+ * result in a destination register.
+ *
+ * Format: B|A|op CCCC (22s)
+ *
+ * Syntax: op vA, vB, #+CCCC
+ */
+
+ movl rINST, %ecx # %ecx<- BA
+ shr $4, %ecx # %ecx<- B
+ andl $15, rINST # rINST<- A
+ FETCHs 1, %edx # %edx<- +CCCC, sign-extended literal
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %ecx # %ecx<- vB
+ addl %edx, %ecx # %ecx<- vA op vB
+ SET_VREG %ecx, rINST # vA<- %ecx; result
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RSUB_INT: /* 0xd1 */
+/* File: x86-atom/OP_RSUB_INT.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_RSUB_INT.S
+ *
+ * Code: 32-bit reverse-subtraction. Uses no substitutions.
+ *
+ * For: rsub-int
+ *
+ * Description: Perform a reverse subtraction on a register and a
+ * signed extended 16-bit literal value and store the
+ * result in a destination register.
+ *
+ * Format: B|A|op CCCC (22s)
+ *
+ * Syntax: op vA, vB, #+CCCC
+ */
+
+ movl rINST, %ecx # %ecx<- BA
+ shr $4, %ecx # %ecx<- B
+ andl $15, rINST # rINST<- A
+ FETCHs 1, %edx # %edx<- +CCCC, sign-extended literal
+ GET_VREG %ecx # %ecx<- vB
+ subl %ecx, %edx # %edx<- +CCCC sub vB
+ SET_VREG %edx, rINST # vA<- %edx; result
+ FINISH 2 # jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_INT_LIT16: /* 0xd2 */
+/* File: x86-atom/OP_MUL_INT_LIT16.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MUL_INT_LIT16.S
+ */
+
+/* File: x86-atom/binopLit16.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopLit16.S
+ *
+ * Code: 32-bit "lit16" operation. Provides an "instr" line to
+ * specify an instruction that performs "%ecx = %ecx op %edx"
+ *
+ *
+ * For: add-int/lit16, and-int/lit16, mul-int/lit16, or-int/lit16
+ * xor-int/lit16
+ *
+ * Description: Perform a binary operation on a register and a
+ * sign extended 16-bit literal value and store the
+ * result in a destination register.
+ *
+ * Format: B|A|op CCCC (22s)
+ *
+ * Syntax: op vA, vB, #+CCCC
+ */
+
+ movl rINST, %ecx # %ecx<- BA
+ shr $4, %ecx # %ecx<- B
+ andl $15, rINST # rINST<- A
+ FETCHs 1, %edx # %edx<- +CCCC, sign-extended literal
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %ecx # %ecx<- vB
+ imul %edx, %ecx # %ecx<- vA op vB
+ SET_VREG %ecx, rINST # vA<- %ecx; result
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_INT_LIT16: /* 0xd3 */
+/* File: x86-atom/OP_DIV_INT_LIT16.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_DIV_INT_LIT16.S
+ */
+
+/* File: x86-atom/binopDLit16.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopDLit16.S
+ *
+ * Code: 32-bit "lit16" divide operation. If "div" is set, the code
+ * returns the quotient, else it returns the remainder.
+ * Also, a divide-by-zero check is done.
+ *
+ * For: div-int/lit16, rem-int/lit16
+ *
+ * Description: Perform a binary operation on a register and a
+ * sign extended 16-bit literal value
+ *
+ * Format: B|A|op CCCC (22s)
+ *
+ * Syntax: op vA, vB, #+CCCC
+ */
+
+
+ movl rINST, %eax # %eax<- BA
+ shr $4, %eax # %eax<- B
+ FETCHs 1, %ecx # %ecx<- +CCCC, sign-extended literal
+ cmp $0, %ecx # check for divide by zero
+ GET_VREG %eax # %eax<- vB
+ je common_errDivideByZero # handle divide by zero
+ andl $15, rINST # rINST<- A
+ cmpl $-1, %ecx # handle -1 special case divide error
+ jne .LOP_DIV_INT_LIT16_noerror
+ cmpl $0x80000000,%eax # handle min int special case divide error
+ je .LOP_DIV_INT_LIT16_break
+.LOP_DIV_INT_LIT16_noerror:
+ cdq # sign-extend %eax to %edx
+ idiv %ecx # divide %edx:%eax by %ecx
+ #FFETCH_ADV 2, %ecx # %ecx<- next instruction hi; fetch, advance
+ .if 1
+ SET_VREG %eax rINST # vA<- %eax (quotient)
+ .else
+ SET_VREG %edx rINST # vA<- %edx (remainder)
+ .endif
+ jmp .LOP_DIV_INT_LIT16_break2
+
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_INT_LIT16: /* 0xd4 */
+/* File: x86-atom/OP_REM_INT_LIT16.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_REM_INT_LIT16.S
+ */
+
+/* File: x86-atom/binopDLit16.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopDLit16.S
+ *
+ * Code: 32-bit "lit16" divide operation. If "div" is set, the code
+ * returns the quotient, else it returns the remainder.
+ * Also, a divide-by-zero check is done.
+ *
+ * For: div-int/lit16, rem-int/lit16
+ *
+ * Description: Perform a binary operation on a register and a
+ * sign extended 16-bit literal value
+ *
+ * Format: B|A|op CCCC (22s)
+ *
+ * Syntax: op vA, vB, #+CCCC
+ */
+
+
+ movl rINST, %eax # %eax<- BA
+ shr $4, %eax # %eax<- B
+ FETCHs 1, %ecx # %ecx<- +CCCC, sign-extended literal
+ cmp $0, %ecx # check for divide by zero
+ GET_VREG %eax # %eax<- vB
+ je common_errDivideByZero # handle divide by zero
+ andl $15, rINST # rINST<- A
+ cmpl $-1, %ecx # handle -1 special case divide error
+ jne .LOP_REM_INT_LIT16_noerror
+ cmpl $0x80000000,%eax # handle min int special case divide error
+ je .LOP_REM_INT_LIT16_break
+.LOP_REM_INT_LIT16_noerror:
+ cdq # sign-extend %eax to %edx
+ idiv %ecx # divide %edx:%eax by %ecx
+ #FFETCH_ADV 2, %ecx # %ecx<- next instruction hi; fetch, advance
+ .if 0
+ SET_VREG %eax rINST # vA<- %eax (quotient)
+ .else
+ SET_VREG %edx rINST # vA<- %edx (remainder)
+ .endif
+ jmp .LOP_REM_INT_LIT16_break2
+
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_INT_LIT16: /* 0xd5 */
+/* File: x86-atom/OP_AND_INT_LIT16.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_AND_INT_LIT16.S
+ */
+
+/* File: x86-atom/binopLit16.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopLit16.S
+ *
+ * Code: 32-bit "lit16" operation. Provides an "instr" line to
+ * specify an instruction that performs "%ecx = %ecx op %edx"
+ *
+ *
+ * For: add-int/lit16, and-int/lit16, mul-int/lit16, or-int/lit16
+ * xor-int/lit16
+ *
+ * Description: Perform a binary operation on a register and a
+ * sign extended 16-bit literal value and store the
+ * result in a destination register.
+ *
+ * Format: B|A|op CCCC (22s)
+ *
+ * Syntax: op vA, vB, #+CCCC
+ */
+
+ movl rINST, %ecx # %ecx<- BA
+ shr $4, %ecx # %ecx<- B
+ andl $15, rINST # rINST<- A
+ FETCHs 1, %edx # %edx<- +CCCC, sign-extended literal
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %ecx # %ecx<- vB
+ andl %edx, %ecx # %ecx<- vA op vB
+ SET_VREG %ecx, rINST # vA<- %ecx; result
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_INT_LIT16: /* 0xd6 */
+/* File: x86-atom/OP_OR_INT_LIT16.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_OR_INT_LIT16.S
+ */
+
+/* File: x86-atom/binopLit16.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopLit16.S
+ *
+ * Code: 32-bit "lit16" operation. Provides an "instr" line to
+ * specify an instruction that performs "%ecx = %ecx op %edx"
+ *
+ *
+ * For: add-int/lit16, and-int/lit16, mul-int/lit16, or-int/lit16
+ * xor-int/lit16
+ *
+ * Description: Perform a binary operation on a register and a
+ * sign extended 16-bit literal value and store the
+ * result in a destination register.
+ *
+ * Format: B|A|op CCCC (22s)
+ *
+ * Syntax: op vA, vB, #+CCCC
+ */
+
+ movl rINST, %ecx # %ecx<- BA
+ shr $4, %ecx # %ecx<- B
+ andl $15, rINST # rINST<- A
+ FETCHs 1, %edx # %edx<- +CCCC, sign-extended literal
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %ecx # %ecx<- vB
+ or %edx, %ecx # %ecx<- vA op vB
+ SET_VREG %ecx, rINST # vA<- %ecx; result
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_INT_LIT16: /* 0xd7 */
+/* File: x86-atom/OP_XOR_INT_LIT16.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_XOR_INT_LIT16.S
+ */
+
+/* File: x86-atom/binopLit16.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopLit16.S
+ *
+ * Code: 32-bit "lit16" operation. Provides an "instr" line to
+ * specify an instruction that performs "%ecx = %ecx op %edx"
+ *
+ *
+ * For: add-int/lit16, and-int/lit16, mul-int/lit16, or-int/lit16
+ * xor-int/lit16
+ *
+ * Description: Perform a binary operation on a register and a
+ * sign extended 16-bit literal value and store the
+ * result in a destination register.
+ *
+ * Format: B|A|op CCCC (22s)
+ *
+ * Syntax: op vA, vB, #+CCCC
+ */
+
+ movl rINST, %ecx # %ecx<- BA
+ shr $4, %ecx # %ecx<- B
+ andl $15, rINST # rINST<- A
+ FETCHs 1, %edx # %edx<- +CCCC, sign-extended literal
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %ecx # %ecx<- vB
+ xor %edx, %ecx # %ecx<- vA op vB
+ SET_VREG %ecx, rINST # vA<- %ecx; result
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_INT_LIT8: /* 0xd8 */
+/* File: x86-atom/OP_ADD_INT_LIT8.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_ADD_INT_LIT8.S
+ */
+
+/* File: x86-atom/binopLit8.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopLit8.S
+ *
+ * Code: 32-bit "lit8" divide operation. Provides an "instr" line
+ * to specify an instruction that performs "%ecx = %ecx op %edx"
+ *
+ *
+ * For: add-int/lit8, and-int/lit8, mul-int/lit8, or-int/lit8
+ * xor-int/lit8
+ *
+ * Description: Perform a binary operation on a register and a
+ * signed extended 8-bit literal value
+ *
+ * Format: AA|op CC|BB (22b)
+ *
+ * Syntax: op vAA, vBB, #+CC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CCs 1, %edx # %edx<- +CC, sign-extended literal
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %ecx # %ecx<- vBB
+ addl %edx, %ecx # %ecx<- vBB op +CC
+ SET_VREG %ecx, rINST # vAA<- %ecx; result
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RSUB_INT_LIT8: /* 0xd9 */
+/* File: x86-atom/OP_RSUB_INT_LIT8.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_RSUB_INT_LIT8.S
+ *
+ * Code: 32-bit reverse-subtraction. Uses no substitutions.
+ *
+ * For: rsub-int/lit8
+ *
+ * Description: Perform a reverse subtraction on a register and a
+ * signed extended 8-bit literal value.
+ *
+ * Format: AA|op CC|BB (22b)
+ *
+ * Syntax: op vAA, vBB, #+CC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CCs 1, %edx # %edx<- +CC, sign-extended literal
+ GET_VREG %ecx # %ecx<- vBB
+ sub %ecx, %edx # %edx<- +CC sub vBB
+ SET_VREG %edx, rINST # vAA<- %edx; result
+ FINISH 2 # jump to next instruction
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_INT_LIT8: /* 0xda */
+/* File: x86-atom/OP_MUL_INT_LIT8.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MUL_INT_LIT8.S
+ */
+
+/* File: x86-atom/binopLit8.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopLit8.S
+ *
+ * Code: 32-bit "lit8" divide operation. Provides an "instr" line
+ * to specify an instruction that performs "%ecx = %ecx op %edx"
+ *
+ *
+ * For: add-int/lit8, and-int/lit8, mul-int/lit8, or-int/lit8
+ * xor-int/lit8
+ *
+ * Description: Perform a binary operation on a register and a
+ * signed extended 8-bit literal value
+ *
+ * Format: AA|op CC|BB (22b)
+ *
+ * Syntax: op vAA, vBB, #+CC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CCs 1, %edx # %edx<- +CC, sign-extended literal
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %ecx # %ecx<- vBB
+ imul %edx, %ecx # %ecx<- vBB op +CC
+ SET_VREG %ecx, rINST # vAA<- %ecx; result
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_INT_LIT8: /* 0xdb */
+/* File: x86-atom/OP_DIV_INT_LIT8.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_DIV_INT_LIT8.S
+ */
+
+/* File: x86-atom/binopDLit8.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopDLit8.S
+ *
+ * Code: 32-bit "lit8" divide operation. If "div" is set, the code
+ * returns the quotient, else it returns the remainder.
+ * Also, a divide-by-zero check is done.
+ *
+ * For: div-int/lit8, rem-int/lit8
+ *
+ * Description: Perform a binary operation on a register and a
+ * signe extended 8-bit literal value
+ *
+ * Format: AA|op CC|BB (22b)
+ *
+ * Syntax: op vAA, vBB, #+CC
+ */
+
+
+ FETCH_BB 1, %eax # %eax<- BB
+ FETCH_CCs 1, %ecx # %ecx<- +CC, sign-extended literal
+ cmp $0, %ecx # check for divide by zero
+ GET_VREG %eax # %eax<- vBB
+ je common_errDivideByZero # handle divide by zero
+
+ cmpl $-1, %ecx # handle -1 special case divide error
+ jne .LOP_DIV_INT_LIT8_noerror
+ cmpl $0x80000000,%eax # handle min int special case divide error
+ je .LOP_DIV_INT_LIT8_break
+.LOP_DIV_INT_LIT8_noerror:
+ cdq # sign-extend %eax to %edx
+ idiv %ecx # divide %edx:%eax by %ecx
+ #FFETCH_ADV 2, %ecx # %ecx<- next instruction hi; fetch, advance
+ .if 1
+ SET_VREG %eax rINST # vAA<- %eax (quotient)
+ .else
+ SET_VREG %edx rINST # vAA<- %edx (remainder)
+ .endif
+ jmp .LOP_DIV_INT_LIT8_break2
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_INT_LIT8: /* 0xdc */
+/* File: x86-atom/OP_REM_INT_LIT8.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_REM_INT_LIT8.S
+ */
+
+/* File: x86-atom/binopDLit8.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopDLit8.S
+ *
+ * Code: 32-bit "lit8" divide operation. If "div" is set, the code
+ * returns the quotient, else it returns the remainder.
+ * Also, a divide-by-zero check is done.
+ *
+ * For: div-int/lit8, rem-int/lit8
+ *
+ * Description: Perform a binary operation on a register and a
+ * signe extended 8-bit literal value
+ *
+ * Format: AA|op CC|BB (22b)
+ *
+ * Syntax: op vAA, vBB, #+CC
+ */
+
+
+ FETCH_BB 1, %eax # %eax<- BB
+ FETCH_CCs 1, %ecx # %ecx<- +CC, sign-extended literal
+ cmp $0, %ecx # check for divide by zero
+ GET_VREG %eax # %eax<- vBB
+ je common_errDivideByZero # handle divide by zero
+
+ cmpl $-1, %ecx # handle -1 special case divide error
+ jne .LOP_REM_INT_LIT8_noerror
+ cmpl $0x80000000,%eax # handle min int special case divide error
+ je .LOP_REM_INT_LIT8_break
+.LOP_REM_INT_LIT8_noerror:
+ cdq # sign-extend %eax to %edx
+ idiv %ecx # divide %edx:%eax by %ecx
+ #FFETCH_ADV 2, %ecx # %ecx<- next instruction hi; fetch, advance
+ .if 0
+ SET_VREG %eax rINST # vAA<- %eax (quotient)
+ .else
+ SET_VREG %edx rINST # vAA<- %edx (remainder)
+ .endif
+ jmp .LOP_REM_INT_LIT8_break2
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_INT_LIT8: /* 0xdd */
+/* File: x86-atom/OP_AND_INT_LIT8.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_AND_INT_LIT8.S
+ */
+
+/* File: x86-atom/binopLit8.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopLit8.S
+ *
+ * Code: 32-bit "lit8" divide operation. Provides an "instr" line
+ * to specify an instruction that performs "%ecx = %ecx op %edx"
+ *
+ *
+ * For: add-int/lit8, and-int/lit8, mul-int/lit8, or-int/lit8
+ * xor-int/lit8
+ *
+ * Description: Perform a binary operation on a register and a
+ * signed extended 8-bit literal value
+ *
+ * Format: AA|op CC|BB (22b)
+ *
+ * Syntax: op vAA, vBB, #+CC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CCs 1, %edx # %edx<- +CC, sign-extended literal
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %ecx # %ecx<- vBB
+ andl %edx, %ecx # %ecx<- vBB op +CC
+ SET_VREG %ecx, rINST # vAA<- %ecx; result
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_INT_LIT8: /* 0xde */
+/* File: x86-atom/OP_OR_INT_LIT8.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_OR_INT_LIT8.S
+ */
+
+/* File: x86-atom/binopLit8.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopLit8.S
+ *
+ * Code: 32-bit "lit8" divide operation. Provides an "instr" line
+ * to specify an instruction that performs "%ecx = %ecx op %edx"
+ *
+ *
+ * For: add-int/lit8, and-int/lit8, mul-int/lit8, or-int/lit8
+ * xor-int/lit8
+ *
+ * Description: Perform a binary operation on a register and a
+ * signed extended 8-bit literal value
+ *
+ * Format: AA|op CC|BB (22b)
+ *
+ * Syntax: op vAA, vBB, #+CC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CCs 1, %edx # %edx<- +CC, sign-extended literal
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %ecx # %ecx<- vBB
+ or %edx, %ecx # %ecx<- vBB op +CC
+ SET_VREG %ecx, rINST # vAA<- %ecx; result
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_INT_LIT8: /* 0xdf */
+/* File: x86-atom/OP_XOR_INT_LIT8.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_XOR_INT_LIT8.S
+ */
+
+/* File: x86-atom/binopLit8.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopLit8.S
+ *
+ * Code: 32-bit "lit8" divide operation. Provides an "instr" line
+ * to specify an instruction that performs "%ecx = %ecx op %edx"
+ *
+ *
+ * For: add-int/lit8, and-int/lit8, mul-int/lit8, or-int/lit8
+ * xor-int/lit8
+ *
+ * Description: Perform a binary operation on a register and a
+ * signed extended 8-bit literal value
+ *
+ * Format: AA|op CC|BB (22b)
+ *
+ * Syntax: op vAA, vBB, #+CC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CCs 1, %edx # %edx<- +CC, sign-extended literal
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %ecx # %ecx<- vBB
+ xor %edx, %ecx # %ecx<- vBB op +CC
+ SET_VREG %ecx, rINST # vAA<- %ecx; result
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHL_INT_LIT8: /* 0xe0 */
+/* File: x86-atom/OP_SHL_INT_LIT8.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SHL_INT_LIT8.S
+ */
+
+/* File: x86-atom/binopLit8S.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopLit8S.S
+ *
+ * Code: 32-bit "lit8" divide operation. Provides an "instr" line
+ * to specify an instruction that performs "%edx = %edx op %cl"
+ *
+ *
+ * For: shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ *
+ *
+ * Description: Perform a binary operation on a register and a
+ * signed extended 8-bit literal value
+ *
+ * Format: AA|op CC|BB (22b)
+ *
+ * Syntax: op vAA, vBB, #+CC
+ */
+
+ FETCH_BB 1, %edx # %edx<- BB
+ FETCH_CCs 1, %ecx # %ecx<- +CC, sign-extended literal
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %edx # %edx<- vBB
+ sal %cl, %edx # %edx<- vBB op +CC
+ SET_VREG %edx, rINST # vAA<- %edx; result
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHR_INT_LIT8: /* 0xe1 */
+/* File: x86-atom/OP_SHR_INT_LIT8.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SHR_INT_LIT8.S
+ */
+
+/* File: x86-atom/binopLit8S.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopLit8S.S
+ *
+ * Code: 32-bit "lit8" divide operation. Provides an "instr" line
+ * to specify an instruction that performs "%edx = %edx op %cl"
+ *
+ *
+ * For: shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ *
+ *
+ * Description: Perform a binary operation on a register and a
+ * signed extended 8-bit literal value
+ *
+ * Format: AA|op CC|BB (22b)
+ *
+ * Syntax: op vAA, vBB, #+CC
+ */
+
+ FETCH_BB 1, %edx # %edx<- BB
+ FETCH_CCs 1, %ecx # %ecx<- +CC, sign-extended literal
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %edx # %edx<- vBB
+ sar %cl, %edx # %edx<- vBB op +CC
+ SET_VREG %edx, rINST # vAA<- %edx; result
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_USHR_INT_LIT8: /* 0xe2 */
+/* File: x86-atom/OP_USHR_INT_LIT8.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_USHR_INT_LIT8.S
+ */
+
+/* File: x86-atom/binopLit8S.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopLit8S.S
+ *
+ * Code: 32-bit "lit8" divide operation. Provides an "instr" line
+ * to specify an instruction that performs "%edx = %edx op %cl"
+ *
+ *
+ * For: shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ *
+ *
+ * Description: Perform a binary operation on a register and a
+ * signed extended 8-bit literal value
+ *
+ * Format: AA|op CC|BB (22b)
+ *
+ * Syntax: op vAA, vBB, #+CC
+ */
+
+ FETCH_BB 1, %edx # %edx<- BB
+ FETCH_CCs 1, %ecx # %ecx<- +CC, sign-extended literal
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %edx # %edx<- vBB
+ shr %cl, %edx # %edx<- vBB op +CC
+ SET_VREG %edx, rINST # vAA<- %edx; result
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_VOLATILE: /* 0xe3 */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: stub.S
+ */
+
+ SAVE_PC_FP_TO_GLUE %edx # save program counter and frame pointer
+ pushl rGLUE # push parameter glue
+ call dvmMterp_OP_IGET_VOLATILE # call c-based implementation
+ lea 4(%esp), %esp
+ LOAD_PC_FP_FROM_GLUE # restore program counter and frame pointer
+ FINISH_A # jump to next instruction
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_VOLATILE: /* 0xe4 */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: stub.S
+ */
+
+ SAVE_PC_FP_TO_GLUE %edx # save program counter and frame pointer
+ pushl rGLUE # push parameter glue
+ call dvmMterp_OP_IPUT_VOLATILE # call c-based implementation
+ lea 4(%esp), %esp
+ LOAD_PC_FP_FROM_GLUE # restore program counter and frame pointer
+ FINISH_A # jump to next instruction
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_VOLATILE: /* 0xe5 */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: stub.S
+ */
+
+ SAVE_PC_FP_TO_GLUE %edx # save program counter and frame pointer
+ pushl rGLUE # push parameter glue
+ call dvmMterp_OP_SGET_VOLATILE # call c-based implementation
+ lea 4(%esp), %esp
+ LOAD_PC_FP_FROM_GLUE # restore program counter and frame pointer
+ FINISH_A # jump to next instruction
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_VOLATILE: /* 0xe6 */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: stub.S
+ */
+
+ SAVE_PC_FP_TO_GLUE %edx # save program counter and frame pointer
+ pushl rGLUE # push parameter glue
+ call dvmMterp_OP_SPUT_VOLATILE # call c-based implementation
+ lea 4(%esp), %esp
+ LOAD_PC_FP_FROM_GLUE # restore program counter and frame pointer
+ FINISH_A # jump to next instruction
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_OBJECT_VOLATILE: /* 0xe7 */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: stub.S
+ */
+
+ SAVE_PC_FP_TO_GLUE %edx # save program counter and frame pointer
+ pushl rGLUE # push parameter glue
+ call dvmMterp_OP_IGET_OBJECT_VOLATILE # call c-based implementation
+ lea 4(%esp), %esp
+ LOAD_PC_FP_FROM_GLUE # restore program counter and frame pointer
+ FINISH_A # jump to next instruction
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_WIDE_VOLATILE: /* 0xe8 */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: stub.S
+ */
+
+ SAVE_PC_FP_TO_GLUE %edx # save program counter and frame pointer
+ pushl rGLUE # push parameter glue
+ call dvmMterp_OP_IGET_WIDE_VOLATILE # call c-based implementation
+ lea 4(%esp), %esp
+ LOAD_PC_FP_FROM_GLUE # restore program counter and frame pointer
+ FINISH_A # jump to next instruction
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_WIDE_VOLATILE: /* 0xe9 */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: stub.S
+ */
+
+ SAVE_PC_FP_TO_GLUE %edx # save program counter and frame pointer
+ pushl rGLUE # push parameter glue
+ call dvmMterp_OP_IPUT_WIDE_VOLATILE # call c-based implementation
+ lea 4(%esp), %esp
+ LOAD_PC_FP_FROM_GLUE # restore program counter and frame pointer
+ FINISH_A # jump to next instruction
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_WIDE_VOLATILE: /* 0xea */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: stub.S
+ */
+
+ SAVE_PC_FP_TO_GLUE %edx # save program counter and frame pointer
+ pushl rGLUE # push parameter glue
+ call dvmMterp_OP_SGET_WIDE_VOLATILE # call c-based implementation
+ lea 4(%esp), %esp
+ LOAD_PC_FP_FROM_GLUE # restore program counter and frame pointer
+ FINISH_A # jump to next instruction
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_WIDE_VOLATILE: /* 0xeb */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: stub.S
+ */
+
+ SAVE_PC_FP_TO_GLUE %edx # save program counter and frame pointer
+ pushl rGLUE # push parameter glue
+ call dvmMterp_OP_SPUT_WIDE_VOLATILE # call c-based implementation
+ lea 4(%esp), %esp
+ LOAD_PC_FP_FROM_GLUE # restore program counter and frame pointer
+ FINISH_A # jump to next instruction
+/* ------------------------------ */
+ .balign 64
+.L_OP_BREAKPOINT: /* 0xec */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: stub.S
+ */
+
+ SAVE_PC_FP_TO_GLUE %edx # save program counter and frame pointer
+ pushl rGLUE # push parameter glue
+ call dvmMterp_OP_BREAKPOINT # call c-based implementation
+ lea 4(%esp), %esp
+ LOAD_PC_FP_FROM_GLUE # restore program counter and frame pointer
+ FINISH_A # jump to next instruction
+/* ------------------------------ */
+ .balign 64
+.L_OP_THROW_VERIFICATION_ERROR: /* 0xed */
+/* File: x86-atom/OP_THROW_VERIFICATION_ERROR.S */
+ /* Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_THROW_VERIFICATION_ERROR.S
+ *
+ * Code:
+ *
+ * For: throw-verification-error
+ *
+ * Description: Throws an exception for an error discovered during verification.
+ * The exception is indicated by AA with details provided by BBBB.
+ *
+ * Format: AA|op BBBB (21c)
+ *
+ * Syntax: op vAA, ref@BBBB
+ */
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_method(%edx), %ecx # %ecx<- glue->method
+ EXPORT_PC # in case an exception is thrown
+ FETCH 1, %eax # %eax<- BBBB
+ movl %eax, -4(%esp) # push parameter BBBB; ref
+ movl rINST, -8(%esp) # push parameter AA
+ movl %ecx, -12(%esp) # push parameter glue->method
+ lea -12(%esp), %esp
+ call dvmThrowVerificationError # call: (const Method* method, int kind, int ref)
+ jmp common_exceptionThrown # failed; handle exception
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_EXECUTE_INLINE: /* 0xee */
+/* File: x86-atom/OP_EXECUTE_INLINE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_EXECUTE_INLINE.S
+ *
+ * Code: Executes a "native inline" instruction. Uses no substitutions.
+ *
+ * For: execute-inline
+ *
+ * Description: Executes a "native inline" instruction. This instruction
+ * is generated by the optimizer.
+ *
+ * Format:
+ *
+ * Syntax: vAA, {vC, vD, vE, vF}, inline@BBBB
+ */
+
+ FETCH 1, %ecx # %ecx<- BBBB
+ movl rGLUE, %eax # %eax<- MterpGlue pointer
+ addl $offGlue_retval, %eax # %eax<- &glue->retval
+ EXPORT_PC
+ shr $4, rINST # rINST<- B
+ movl %eax, -8(%esp) # push parameter glue->retval
+ lea -24(%esp), %esp
+ jmp .LOP_EXECUTE_INLINE_continue
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_EXECUTE_INLINE_RANGE: /* 0xef */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: stub.S
+ */
+
+ SAVE_PC_FP_TO_GLUE %edx # save program counter and frame pointer
+ pushl rGLUE # push parameter glue
+ call dvmMterp_OP_EXECUTE_INLINE_RANGE # call c-based implementation
+ lea 4(%esp), %esp
+ LOAD_PC_FP_FROM_GLUE # restore program counter and frame pointer
+ FINISH_A # jump to next instruction
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_DIRECT_EMPTY: /* 0xf0 */
+/* File: x86-atom/OP_INVOKE_DIRECT_EMPTY.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INVOKE_DIRECT_EMPTY.S
+ *
+ * Code: Used as a no-op. Uses no substitutions.
+ *
+ * For: invoke-direct-empty
+ *
+ * Format: B|A|op CCCC G|F|E|D (35c)
+ */
+
+ FINISH 3
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_F1: /* 0xf1 */
+/* File: x86-atom/OP_UNUSED_F1.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_UNUSED_F1.S
+ */
+
+/* File: x86-atom/unused.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: unused.S
+ *
+ * Code: Common code for unused bytecodes. Uses no subtitutions.
+ *
+ * For: all unused bytecodes
+ *
+ * Description: aborts if executed.
+ *
+ * Format: ØØ|op (10x)
+ *
+ * Syntax: op
+ */
+
+ call common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_QUICK: /* 0xf2 */
+/* File: x86-atom/OP_IGET_QUICK.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IGET_QUICK.S
+ *
+ * Code: Optimization for iget
+ *
+ * For: iget-quick
+ *
+ * Format: B|A|op CCCC (22c)
+ *
+ * Syntax: op vA, vB, offset@CCCC
+ */
+
+ movl rINST, %eax # %eax<- BA
+ shr $4, %eax # %eax<- B
+ and $15, rINST # rINST<- A
+ GET_VREG %eax # %eax<- vB; object to operate on
+ FETCH 1, %ecx # %ecx<- CCCC; field byte offset
+ cmp $0, %eax # check if object is null
+ je common_errNullObject # handle null object
+ FFETCH_ADV 2, %edx # %eax<- next instruction hi; fetch, advance
+ movl (%ecx, %eax), %eax # %eax<- object field
+ SET_VREG %eax, rINST # fp[A]<- %eax
+ FGETOP_JMP 2, %edx # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_WIDE_QUICK: /* 0xf3 */
+/* File: x86-atom/OP_IGET_WIDE_QUICK.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IGET_WIDE_QUICK.S
+ *
+ * Code: Optimization for iget
+ *
+ * For: iget/wide-quick
+ *
+ * Format: B|A|op CCCC (22c)
+ *
+ * Syntax: op vA, vB, offset@CCCC
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $4, %edx # %edx<- B
+ andl $15, rINST # rINST<- A
+ GET_VREG %edx # %edx<- vB; object to operate on
+ cmp $0, %edx # check if object is null
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ je common_errNullObject # handle null object
+ FETCH 1, %ecx # %ecx<- CCCC; field byte offset
+ movq (%ecx, %edx), %xmm0 # %xmm0<- object field
+ movq %xmm0, (rFP, rINST, 4) # fp[A]<- %xmm0
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_OBJECT_QUICK: /* 0xf4 */
+/* File: x86-atom/OP_IGET_OBJECT_QUICK.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IGET_OBJECT_QUICK.S
+ */
+
+/* File: x86-atom/OP_IGET_QUICK.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IGET_QUICK.S
+ *
+ * Code: Optimization for iget
+ *
+ * For: iget-quick
+ *
+ * Format: B|A|op CCCC (22c)
+ *
+ * Syntax: op vA, vB, offset@CCCC
+ */
+
+ movl rINST, %eax # %eax<- BA
+ shr $4, %eax # %eax<- B
+ and $15, rINST # rINST<- A
+ GET_VREG %eax # %eax<- vB; object to operate on
+ FETCH 1, %ecx # %ecx<- CCCC; field byte offset
+ cmp $0, %eax # check if object is null
+ je common_errNullObject # handle null object
+ FFETCH_ADV 2, %edx # %eax<- next instruction hi; fetch, advance
+ movl (%ecx, %eax), %eax # %eax<- object field
+ SET_VREG %eax, rINST # fp[A]<- %eax
+ FGETOP_JMP 2, %edx # jump to next instruction; getop, jmp
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_QUICK: /* 0xf5 */
+/* File: x86-atom/OP_IPUT_QUICK.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IPUT_QUICK.S
+ * Code: Optimization for iput
+ *
+ * For: iput-quick
+ *
+ * Format: B|A|op CCCC (22c)
+ *
+ * Syntax: op vA, vB, offset@CCCC
+ */
+
+ movl rINST, %eax # %eax<- BA
+ shr $4, %eax # %eax<- B
+ and $15, rINST # rINST<- A
+ GET_VREG %eax # %eax<- vB; object to operate on
+ FETCH 1, %ecx # %ecx<- CCCC; field byte offset
+ cmp $0, %eax # check if object is null
+ je common_errNullObject # handle null object
+ FFETCH_ADV 2, %edx # %edx<- next instruction hi; fetch, advance
+ GET_VREG rINST # rINST<- vA
+ movl rINST, (%eax, %ecx) # object field<- vA
+ FGETOP_JMP 2, %edx # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_WIDE_QUICK: /* 0xf6 */
+/* File: x86-atom/OP_IPUT_WIDE_QUICK.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IPUT_WIDE_QUICK.S
+ *
+ * Code: Optimization for iput
+ *
+ * For: iput/wide-quick
+ *
+ * Format: B|A|op CCCC (22c)
+ *
+ * Syntax: op vA, vB, offset@CCCC
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $4, %edx # %edx<- B
+ andl $15, rINST # rINST<- A
+ GET_VREG %edx # %edx<- vB; object to operate on
+ cmp $0, %edx # check if object is null
+ FETCH 1, %ecx # %ecx<- CCCC; field byte offset
+ je common_errNullObject # handle null object
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movq (rFP, rINST, 4), %xmm0 # %xmm0<- fp[A]
+ movq %xmm0, (%edx, %ecx) # object field<- %xmm0; fp[A]
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_OBJECT_QUICK: /* 0xf7 */
+/* File: x86-atom/OP_IPUT_OBJECT_QUICK.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IPUT_QUICK.S
+ * Code: Optimization for iput
+ *
+ * For: iput-quick
+ *
+ * Format: B|A|op CCCC (22c)
+ *
+ * Syntax: op vA, vB, offset@CCCC
+ */
+
+ movl rINST, %eax # %eax<- BA
+ shr $4, %eax # %eax<- B
+ and $15, rINST # rINST<- A
+ GET_VREG %eax # %eax<- vB; object to operate on
+ FETCH 1, %ecx # %ecx<- CCCC; field byte offset
+ cmp $0, %eax # check if object is null
+ je common_errNullObject # handle null object
+ FFETCH_ADV 2, %edx # %edx<- next instruction hi; fetch, advance
+ GET_VREG rINST # rINST<- vA
+ movl rINST, (%eax, %ecx) # object field<- vA
+ testl rINST, rINST # did we write a null object
+ je 1f
+ movl rGLUE, %ecx # get glue
+ movl offGlue_cardTable(%ecx), %ecx # get card table base
+ shrl $GC_CARD_SHIFT, %eax # get gc card index
+ movb %cl, (%eax, %ecx) # mark gc card in table
+1:
+ FGETOP_JMP 2, %edx # jump to next instruction; getop, jmp
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_VIRTUAL_QUICK: /* 0xf8 */
+/* File: x86-atom/OP_INVOKE_VIRTUAL_QUICK.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INVOKE_VIRTUAL_QUICK.S
+ *
+ * Code: Optimization for invoke-virtual and invoke-virtual/range
+ *
+ * For: invoke-virtual/quick, invoke-virtual/quick-range
+ */
+
+
+ FETCH 2, %edx # %edx<- GFED or CCCC
+ .if (!0)
+ and $15, %edx # %edx<- D if not range
+ .endif
+ FETCH 1, %ecx # %ecx<- method index
+ GET_VREG %edx # %edx<- "this" ptr
+ cmp $0, %edx # %edx<- check for null "this"
+ EXPORT_PC # must export pc for invoke
+ je common_errNullObject
+ movl offObject_clazz(%edx), %edx # %edx<- thisPtr->clazz
+ movl offClassObject_vtable(%edx), %edx # %edx<- thisPtr->clazz->vtable
+ movl (%edx, %ecx, 4), %ecx # %ecx<- vtable[methodIndex]
+ jmp common_invokeMethodNoRange # invoke method common code
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_VIRTUAL_QUICK_RANGE: /* 0xf9 */
+/* File: x86-atom/OP_INVOKE_VIRTUAL_QUICK_RANGE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INVOKE_VIRTUAL_QUICK_RANGE.S
+ */
+
+/* File: x86-atom/OP_INVOKE_VIRTUAL_QUICK.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INVOKE_VIRTUAL_QUICK.S
+ *
+ * Code: Optimization for invoke-virtual and invoke-virtual/range
+ *
+ * For: invoke-virtual/quick, invoke-virtual/quick-range
+ */
+
+
+ FETCH 2, %edx # %edx<- GFED or CCCC
+ .if (!1)
+ and $15, %edx # %edx<- D if not range
+ .endif
+ FETCH 1, %ecx # %ecx<- method index
+ GET_VREG %edx # %edx<- "this" ptr
+ cmp $0, %edx # %edx<- check for null "this"
+ EXPORT_PC # must export pc for invoke
+ je common_errNullObject
+ movl offObject_clazz(%edx), %edx # %edx<- thisPtr->clazz
+ movl offClassObject_vtable(%edx), %edx # %edx<- thisPtr->clazz->vtable
+ movl (%edx, %ecx, 4), %ecx # %ecx<- vtable[methodIndex]
+ jmp common_invokeMethodRange # invoke method common code
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_SUPER_QUICK: /* 0xfa */
+/* File: x86-atom/OP_INVOKE_SUPER_QUICK.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INVOKE_SUPER_QUICK.S
+ *
+ * Code: Optimization for invoke-super and invoke-super/range
+ *
+ * For: invoke-super/quick, invoke-super/quick-range
+ */
+
+
+ FETCH 2, %edx # %edx<- GFED or CCCC
+ movl rGLUE, %ecx # %ecx<- pMterpGlue
+ movl offGlue_method(%ecx), %eax # %eax<- glue->method
+ .if (!0)
+ and $15, %edx # %edx<- D if not range
+ .endif
+ FETCH 1, %ecx # %ecx<- method index
+ movl offMethod_clazz(%eax), %eax # %eax<- glue->method->clazz
+ movl offClassObject_super(%eax), %eax # %eax<- glue->method->clazz->super
+ EXPORT_PC # must export for invoke
+ movl offClassObject_vtable(%eax), %eax # %edx<- glue->method->clazz->super->vtable
+ cmp $0, (rFP, %edx, 4) # check for null object
+ movl (%eax, %ecx, 4), %ecx # %ecx<- vtable[methodIndex]
+ je common_errNullObject # handle null object
+ jmp common_invokeMethodNoRange # invoke method common code
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_SUPER_QUICK_RANGE: /* 0xfb */
+/* File: x86-atom/OP_INVOKE_SUPER_QUICK_RANGE.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INVOKE_SUPER_QUICK_RANGE.S
+ */
+
+/* File: x86-atom/OP_INVOKE_SUPER_QUICK.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INVOKE_SUPER_QUICK.S
+ *
+ * Code: Optimization for invoke-super and invoke-super/range
+ *
+ * For: invoke-super/quick, invoke-super/quick-range
+ */
+
+
+ FETCH 2, %edx # %edx<- GFED or CCCC
+ movl rGLUE, %ecx # %ecx<- pMterpGlue
+ movl offGlue_method(%ecx), %eax # %eax<- glue->method
+ .if (!1)
+ and $15, %edx # %edx<- D if not range
+ .endif
+ FETCH 1, %ecx # %ecx<- method index
+ movl offMethod_clazz(%eax), %eax # %eax<- glue->method->clazz
+ movl offClassObject_super(%eax), %eax # %eax<- glue->method->clazz->super
+ EXPORT_PC # must export for invoke
+ movl offClassObject_vtable(%eax), %eax # %edx<- glue->method->clazz->super->vtable
+ cmp $0, (rFP, %edx, 4) # check for null object
+ movl (%eax, %ecx, 4), %ecx # %ecx<- vtable[methodIndex]
+ je common_errNullObject # handle null object
+ jmp common_invokeMethodRange # invoke method common code
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: stub.S
+ */
+
+ SAVE_PC_FP_TO_GLUE %edx # save program counter and frame pointer
+ pushl rGLUE # push parameter glue
+ call dvmMterp_OP_IPUT_OBJECT_VOLATILE # call c-based implementation
+ lea 4(%esp), %esp
+ LOAD_PC_FP_FROM_GLUE # restore program counter and frame pointer
+ FINISH_A # jump to next instruction
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_OBJECT_VOLATILE: /* 0xfd */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: stub.S
+ */
+
+ SAVE_PC_FP_TO_GLUE %edx # save program counter and frame pointer
+ pushl rGLUE # push parameter glue
+ call dvmMterp_OP_SGET_OBJECT_VOLATILE # call c-based implementation
+ lea 4(%esp), %esp
+ LOAD_PC_FP_FROM_GLUE # restore program counter and frame pointer
+ FINISH_A # jump to next instruction
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: stub.S
+ */
+
+ SAVE_PC_FP_TO_GLUE %edx # save program counter and frame pointer
+ pushl rGLUE # push parameter glue
+ call dvmMterp_OP_SPUT_OBJECT_VOLATILE # call c-based implementation
+ lea 4(%esp), %esp
+ LOAD_PC_FP_FROM_GLUE # restore program counter and frame pointer
+ FINISH_A # jump to next instruction
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_FF: /* 0xff */
+/* File: x86-atom/OP_UNUSED_FF.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_UNUSED_FF.S
+ */
+
+/* File: x86-atom/unused.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: unused.S
+ *
+ * Code: Common code for unused bytecodes. Uses no subtitutions.
+ *
+ * For: all unused bytecodes
+ *
+ * Description: aborts if executed.
+ *
+ * Format: ØØ|op (10x)
+ *
+ * Syntax: op
+ */
+
+ call common_abort
+
+
+
+ .balign 64
+ .size dvmAsmInstructionStart, .-dvmAsmInstructionStart
+ .global dvmAsmInstructionEnd
+dvmAsmInstructionEnd:
+
+/*
+ * ===========================================================================
+ * Sister implementations
+ * ===========================================================================
+ */
+ .global dvmAsmSisterStart
+ .type dvmAsmSisterStart, %function
+ .text
+ .balign 4
+dvmAsmSisterStart:
+
+/* continuation for OP_CONST_STRING */
+
+
+ /*
+ * Continuation if the Class has not yet been resolved.
+ * %ecx: BBBB (Class ref)
+ * need: target register
+ */
+
+.LOP_CONST_STRING_resolve:
+ EXPORT_PC
+ movl offGlue_method(%edx), %edx # %edx<- glue->method
+ movl offMethod_clazz(%edx), %edx # %edx<- glue->method->clazz
+ movl %ecx, -4(%esp) # push parameter class ref
+ movl %edx, -8(%esp) # push parameter glue->method->clazz
+ lea -8(%esp), %esp
+ call dvmResolveString # resolve string reference
+ # call: (const ClassObject* referrer, u4 stringIdx)
+ # return: StringObject*
+ lea 8(%esp), %esp
+ cmp $0, %eax # check if resolved string failed
+ je common_exceptionThrown # resolve failed; exception thrown
+ SET_VREG %eax, rINST # vAA<- %eax; pResString[BBBB]
+ FFETCH_ADV 2, %edx # %edx<- next instruction hi; fetch, advance
+ FGETOP_JMP 2, %edx # jump to next instruction; getop, jmp
+
+/* continuation for OP_CONST_STRING_JUMBO */
+
+
+ /*
+ * Continuation if the Class has not yet been resolved.
+ * %ecx: BBBB (Class ref)
+ * need: target register
+ */
+.LOP_CONST_STRING_JUMBO_resolve:
+ EXPORT_PC
+ movl rGLUE, %edx # get MterpGlue pointer
+ movl offGlue_method(%edx), %edx # %edx<- glue->method
+ movl offMethod_clazz(%edx), %edx # %edx <- glue->method->clazz
+ movl %ecx, -4(%esp) # push parameter class ref
+ movl %edx, -8(%esp) # push parameter glue->method->clazz
+ lea -8(%esp), %esp
+ call dvmResolveString # resolve string reference
+ # call: (const ClassObject* referrer, u4 stringIdx)
+ # return: StringObject*
+ lea 8(%esp), %esp
+ cmp $0, %eax # check if resolved string failed
+ je common_exceptionThrown # resolve failed; exception thrown
+ SET_VREG %eax, rINST # vAA<- %eax; pResString[BBBB]
+ FINISH 3 # jump to next instruction
+
+/* continuation for OP_CONST_CLASS */
+
+ /*
+ * Continuation if the Class has not yet been resolved.
+ * %ecx: BBBB (Class ref)
+ * need: target register
+ */
+
+.LOP_CONST_CLASS_resolve:
+ EXPORT_PC
+ movl offGlue_method(%edx), %edx # %edx<- glue->method
+ movl offMethod_clazz(%edx), %edx # %edx<- glue->method->clazz
+ movl $1, -4(%esp) # push parameter true
+ movl %ecx, -8(%esp) # push parameter
+ movl %edx, -12(%esp) # push parameter glue->method->clazz
+ lea -12(%esp), %esp
+ call dvmResolveClass # resolve ClassObject pointer
+ # class: (const ClassObject* referrer, u4 classIdx,
+ # bool fromUnverifiedConstant)
+ # return: ClassObject*
+ lea 12(%esp), %esp
+ cmp $0, %eax # check for null pointer
+ je common_exceptionThrown # handle exception
+ SET_VREG %eax, rINST # vAA<- resolved class
+ FINISH 2 # jump to next instruction
+
+/* continuation for OP_CHECK_CAST */
+
+.LOP_CHECK_CAST_resolved:
+ cmp %ecx, offObject_clazz(rINST) # check for same class
+ jne .LOP_CHECK_CAST_fullcheck # not same class; do full check
+
+.LOP_CHECK_CAST_okay:
+ FINISH 2 # jump to next instruction
+
+ /*
+ * Trivial test failed, need to perform full check.
+ * offObject_clazz(rINST) holds obj->clazz
+ * %ecx holds class resolved from BBBB
+ * rINST holds object
+ */
+
+.LOP_CHECK_CAST_fullcheck:
+ movl offObject_clazz(rINST), %eax # %eax<- obj->clazz
+ movl %eax, -12(%esp) # push parameter obj->clazz
+ movl %ecx, -8(%esp) # push parameter # push parameter resolved class
+ lea -12(%esp), %esp
+ call dvmInstanceofNonTrivial # call: (ClassObject* instance, ClassObject* clazz)
+ # return: int
+ lea 12(%esp), %esp
+ cmp $0, %eax # failed?
+ jne .LOP_CHECK_CAST_okay # success
+
+ /*
+ * A cast has failed. We need to throw a ClassCastException with the
+ * class of the object that failed to be cast.
+ */
+
+ EXPORT_PC # we will throw an exception
+ movl $.LstrClassCastExceptionPtr, -8(%esp) # push parameter message
+ movl offObject_clazz(rINST), rINST # rINST<- obj->clazz
+ movl offClassObject_descriptor(rINST), rINST # rINST<- obj->clazz->descriptor
+ movl rINST, -4(%esp) # push parameter obj->clazz->descriptor
+ lea -8(%esp), %esp
+ call dvmThrowExceptionWithClassMessage # call: (const char* exceptionDescriptor,
+ # const char* messageDescriptor, Object* cause)
+ # return: void
+ lea 8(%esp), %esp
+ jmp common_exceptionThrown
+
+ /*
+ * Resolution required. This is the least-likely path.
+ *
+ * rINST holds object
+ */
+
+.LOP_CHECK_CAST_resolve:
+ movl offGlue_method(%edx), %eax # %eax<- glue->method
+ FETCH 1, %ecx # %ecx holds BBBB
+ EXPORT_PC # in case we throw an exception
+ movl $0, -8(%esp) # push parameter false
+ movl offMethod_clazz(%eax), %eax # %eax<- glue->method->clazz
+ movl %ecx, -12(%esp) # push parameter BBBB
+ movl %eax, -16(%esp) # push parameter glue->method>clazz
+ lea -16(%esp), %esp
+ call dvmResolveClass # resolve ClassObject pointer
+ # call: (const ClassObject* referrer, u4 classIdx,
+ # bool fromUnverifiedConstant)
+ # return ClassObject*
+ lea 16(%esp), %esp
+ cmp $0, %eax # check for null pointer
+ je common_exceptionThrown # handle excpetion
+ movl %eax, %ecx # %ecx<- resolved class
+ jmp .LOP_CHECK_CAST_resolved
+
+.LstrClassCastExceptionPtr:
+.asciz "Ljava/lang/ClassCastException;"
+
+/* continuation for OP_INSTANCE_OF */
+
+.LOP_INSTANCE_OF_break:
+ movl rGLUE, %ecx # %ecx<- pMterpGlue
+ movl offGlue_methodClassDex(%ecx), %ecx # %ecx<- pDvmDex
+ FETCH 1, %eax # %eax<- CCCC
+ movl offDvmDex_pResClasses(%ecx), %ecx # %ecx<- pDvmDex->pResClasses
+ movl (%ecx, %eax, 4), %ecx # %ecx<- resolved class
+ movl offObject_clazz(%edx), %edx # %edx<- obj->clazz
+ cmp $0, %ecx # check if already resovled
+ je .LOP_INSTANCE_OF_resolve # not resolved before, so resolve now
+
+.LOP_INSTANCE_OF_resolved:
+ cmp %ecx, %edx # check if same class
+ je .LOP_INSTANCE_OF_trivial # yes, finish
+ jmp .LOP_INSTANCE_OF_fullcheck # no, do full check
+
+ /*
+ * The trivial test failed, we need to perform a full check.
+ * %edx holds obj->clazz
+ * %ecx holds class resolved from BBBB
+ */
+
+.LOP_INSTANCE_OF_fullcheck:
+ movl %edx, -8(%esp) # push parameter obj->clazz
+ movl %ecx, -4(%esp) # push parameter resolved class
+ lea -8(%esp), %esp
+ call dvmInstanceofNonTrivial # perform full check
+ # call: (ClassObject* instance, ClassObject* clazz)
+ # return: int
+ andl $15, rINST # rINST<- A
+ FFETCH_ADV 2, %edx # %edx<- next instruction hi; fetch, advance
+ lea 8(%esp), %esp
+ SET_VREG %eax, rINST # vA<- r0
+ FGETOP_JMP 2, %edx # jump to next instruction; getop, jmp
+
+ /*
+ * %edx holds boolean result
+ */
+
+.LOP_INSTANCE_OF_store:
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ andl $15, rINST # rINST<- A
+ SET_VREG %edx, rINST # vA<- r0
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+ /*
+ * Trivial test succeeded, save and bail.
+ */
+
+.LOP_INSTANCE_OF_trivial:
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ andl $15, rINST # rINST<- A
+ SET_VREG $1, rINST # vA<- r0
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+ /*
+ * Resolution required. This is the least-likely path.
+ * %eax holds BBBB
+ */
+
+.LOP_INSTANCE_OF_resolve:
+
+ movl rGLUE, %ecx # %ecx<- pMterpGlue
+ EXPORT_PC
+ movl offGlue_method(%ecx), %ecx # %ecx<- glue->method
+ movl offMethod_clazz(%ecx), %ecx # %ecx<- glue->method->clazz
+ movl %ecx, -12(%esp) # push parameter glue->method->clazz
+ movl %eax, -8(%esp) # push parameter CCCC; type index
+ movl $1, -4(%esp) # push parameter true
+ lea -12(%esp), %esp
+ call dvmResolveClass # call: (const ClassObject* referrer, u4 classIdx,
+ # bool fromUnverifiedConstant)
+ # return: ClassObject*
+ lea 12(%esp), %esp
+ cmp $0, %eax # check for null
+ je common_exceptionThrown # handle exception
+ movl rINST, %edx # %edx<- BA+
+ shr $4, %edx # %edx<- B
+ movl %eax, %ecx # need class in %ecx
+ GET_VREG %edx # %edx<- vB
+ movl offObject_clazz(%edx), %edx # %edx<- obj->clazz
+ jmp .LOP_INSTANCE_OF_resolved # clazz resolved, continue
+
+/* continuation for OP_NEW_INSTANCE */
+.balign 32
+.LOP_NEW_INSTANCE_finish:
+ movl %edx, -8(%esp) # push parameter object
+ movl %eax, -4(%esp) # push parameter flags
+ lea -8(%esp), %esp
+ call dvmAllocObject # call: (ClassObject* clazz, int flags)
+ # return: Object*
+ cmp $0, %eax # check for failure
+ lea 8(%esp), %esp
+ je common_exceptionThrown # handle exception
+ SET_VREG %eax, rINST # vAA<- pObject
+ FINISH 2 # jump to next instruction
+
+ /*
+ * Class initialization required.
+ *
+ * %edx holds class object
+ */
+
+.LOP_NEW_INSTANCE_needinit:
+ movl %edx, -4(%esp) # push parameter object
+ lea -4(%esp), %esp
+ call dvmInitClass # call: (ClassObject* clazz)
+ # return: bool
+ lea 4(%esp), %esp
+ cmp $0, %eax # check for failure
+ movl -4(%esp), %edx # %edx<- object
+ je common_exceptionThrown # handle exception
+ testl $(ACC_INTERFACE|ACC_ABSTRACT), offClassObject_accessFlags(%edx)
+ mov $ALLOC_DONT_TRACK, %eax # %eax<- flag for alloc call
+ je .LOP_NEW_INSTANCE_finish # continue
+ jmp .LOP_NEW_INSTANCE_abstract # handle abstract or interface
+
+ /*
+ * Resolution required. This is the least-likely path.
+ *
+ * BBBB in %eax
+ */
+
+.LOP_NEW_INSTANCE_resolve:
+
+
+ movl rGLUE, %ecx # %ecx<- pMterpGlue
+ FETCH 1, %eax # %eax<- BBBB
+ movl offGlue_method(%ecx), %ecx # %ecx<- glue->method
+ movl offMethod_clazz(%ecx), %ecx # %ecx<- glue->method->clazz
+ movl %ecx, -12(%esp) # push parameter clazz
+ movl $0, -4(%esp) # push parameter false
+ movl %eax, -8(%esp) # push parameter BBBB
+ lea -12(%esp), %esp
+ call dvmResolveClass # call: (const ClassObject* referrer,
+ # u4 classIdx, bool fromUnverifiedConstant)
+ # return: ClassObject*
+ lea 12(%esp), %esp
+ movl %eax, %edx # %edx<- pObject
+ cmp $0, %edx # check for failure
+ jne .LOP_NEW_INSTANCE_resolved # continue
+ jmp common_exceptionThrown # handle exception
+
+ /*
+ * We can't instantiate an abstract class or interface, so throw an
+ * InstantiationError with the class descriptor as the message.
+ *
+ * %edx holds class object
+ */
+
+.LOP_NEW_INSTANCE_abstract:
+ movl offClassObject_descriptor(%edx), %ecx # %ecx<- descriptor
+ movl %ecx, -4(%esp) # push parameter descriptor
+ movl $.LstrInstantiationErrorPtr, -8(%esp) # push parameter message
+ lea -8(%esp), %esp
+ call dvmThrowExceptionWithClassMessage # call: (const char* exceptionDescriptor,
+ # const char* messageDescriptor)
+ # return: void
+ jmp common_exceptionThrown # handle exception
+
+.LstrInstantiationErrorPtr:
+.asciz "Ljava/lang/InstantiationError;"
+
+/* continuation for OP_NEW_ARRAY */
+
+ /*
+ * Resolve class. (This is an uncommon case.)
+ *
+ * %edx holds array length
+ * %ecx holds class ref CCCC
+ */
+
+.LOP_NEW_ARRAY_resolve:
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ movl offGlue_method(%eax), %eax # %eax<- glue->method
+ movl %edx, -4(%esp) # save length
+ movl $0, -8(%esp) # push parameter false
+ movl %ecx, -12(%esp) # push parameter class ref
+ movl offMethod_clazz(%eax), %eax # %eax<- glue->method->clazz
+ movl %eax, -16(%esp) # push parameter clazz
+ lea -16(%esp), %esp
+ call dvmResolveClass # call: (const ClassObject* referrer,
+ # u4 classIdx, bool fromUnverifiedConstant)
+ # return: ClassObject*
+ cmp $0, %eax # check for failure
+ lea 16(%esp), %esp
+ je common_exceptionThrown # handle exception
+ movl -4(%esp), %edx # %edx<- length
+
+ /*
+ * Finish allocation.
+ *
+ * %eax holds class
+ * %edx holds array length
+ */
+
+.LOP_NEW_ARRAY_finish:
+ movl %eax, -12(%esp) # push parameter class
+ movl %edx, -8(%esp) # push parameter length
+ movl $ALLOC_DONT_TRACK, -4(%esp)
+ lea -12(%esp), %esp
+ call dvmAllocArrayByClass # call: (ClassObject* arrayClass,
+ # size_t length, int allocFlags)
+ # return: ArrayObject*
+ and $15, rINST # rINST<- A
+ cmp $0, %eax # check for allocation failure
+ lea 12(%esp), %esp
+ je common_exceptionThrown # handle exception
+ SET_VREG %eax, rINST # vA<- pArray
+ FINISH 2 # jump to next instruction
+
+/* continuation for OP_FILLED_NEW_ARRAY */
+
+.LOP_FILLED_NEW_ARRAY_break:
+ movl $0, -8(%esp) # push parameter false
+ movl %ecx, -12(%esp) # push parameter BBBB
+ movl rGLUE, %edx # %edx<- MterpGlue pointer
+ movl offGlue_method(%edx), %edx # %edx<- glue->method
+ movl offMethod_clazz(%edx), %edx # %edx<- glue->method->clazz
+ movl %edx, -16(%esp) # push parameter glue->method->clazz
+ lea -16(%esp), %esp
+ call dvmResolveClass # call: (const ClassObject* referrer, u4 classIdx,
+ # bool fromUnverifiedConstant)
+ # return: ClassObject*
+ lea 16(%esp), %esp
+ cmp $0, %eax # check for null return
+ je common_exceptionThrown # handle exception
+
+ /*
+ * On entry:
+ * %eax holds array class
+ * rINST holds BA or AA
+ */
+
+.LOP_FILLED_NEW_ARRAY_continue:
+ movl offClassObject_descriptor(%eax), %eax # %eax<- arrayClass->descriptor
+ movzbl 1(%eax), %eax # %eax<- descriptor[1]
+ cmpb $'I', %al # check if array of ints
+ je 1f
+ cmpb $'L', %al
+ je 1f
+ cmpb $'[', %al
+ jne .LOP_FILLED_NEW_ARRAY_notimpl # jump to not implemented
+1:
+ movl %eax, sReg0 # save type
+ movl rINST, -12(%esp) # push parameter length
+ movl %eax, -16(%esp) # push parameter descriptor[1]
+ movl $ALLOC_DONT_TRACK, -8(%esp) # push parameter to allocate flags
+ .if (!0)
+ shrl $4, -12(%esp) # parameter length is B
+ .endif
+ lea -16(%esp), %esp
+ call dvmAllocPrimitiveArray # call: (char type, size_t length, int allocFlags)
+ # return: ArrayObject*
+ lea 16(%esp), %esp
+ cmp $0, %eax # check for null return
+ je common_exceptionThrown # handle exception
+
+ FETCH 2, %edx # %edx<- FEDC or CCCC
+ movl rGLUE, %ecx # %ecx<- MterpGlue pointer
+ movl %eax, offGlue_retval(%ecx) # retval<- new array
+ lea offArrayObject_contents(%eax), %eax # %eax<- newArray->contents
+ subl $1, -12(%esp) # length--; check for negative
+ js 2f # if length was zero, finish
+
+ /*
+ * copy values from registers into the array
+ * %eax=array, %edx=CCCC/FEDC, -12(%esp)=length (from AA or B), rINST=AA/BA
+ */
+
+ .if 0
+ lea (rFP, %edx, 4), %ecx # %ecx<- &fpp[CCCC]
+1:
+ movl (%ecx), %edx # %edx<- %ecx++
+ lea 4(%ecx), %ecx # %ecx++
+ movl %edx, (%eax) # *contents<- vX
+ lea 4(%eax), %eax # %eax++; contents++
+ subl $1, -12(%esp) # length--
+ jns 1b # or continue at 2
+ .else
+ cmp $4, -12(%esp) # check length
+ jne 1f # has four args
+ and $15, rINST # rINST<- A
+ GET_VREG rINST # rINST<- vA
+ subl $1, -12(%esp) # count--
+ movl rINST, 16(%eax) # contents[4]<- vA
+1:
+ movl %edx, %ecx # %ecx<- %edx; ecx for temp
+ andl $15, %ecx # %ecx<- G/F/E/D
+ GET_VREG %ecx # %ecx<- vG/vF/vE/vD
+ shr $4, %edx # %edx<- put next reg in low 4
+ subl $1, -12(%esp) # count--
+ movl %ecx, (%eax) # *contents<- vX
+ lea 4(%eax), %eax # %eax++; contents++
+ jns 1b # or continue at 2
+ .endif
+2:
+ cmpb $'I', sReg0 # check for int array
+ je 3f
+ movl rGLUE, %ecx # %ecx<- MterpGlue pointer
+ movl offGlue_retval(%ecx), %eax # Object head
+ movl offGlue_cardTable(%ecx), %ecx # card table base
+ shrl $GC_CARD_SHIFT, %eax # convert to card num
+ movb %cl,(%ecx, %eax) # mark card based on object head
+3:
+ FINISH 3 # jump to next instruction
+
+ /*
+ * Throw an exception to indicate this mode of filled-new-array
+ * has not been implemented.
+ */
+
+.LOP_FILLED_NEW_ARRAY_notimpl:
+ movl $.LstrInternalError, -8(%esp)
+ movl $.LstrFilledNewArrayNotImpl, -4(%esp)
+ lea -8(%esp), %esp
+ call dvmThrowException # call: (const char* exceptionDescriptor,
+ # const char* msg)
+ # return: void
+ lea 8(%esp), %esp
+ jmp common_exceptionThrown
+
+.if (!0) # define in one or the other, not both
+.LstrFilledNewArrayNotImpl:
+.asciz "filled-new-array only implemented for 'int'"
+.LstrInternalError:
+.asciz "Ljava/lang/InternalError;"
+.endif
+
+/* continuation for OP_FILLED_NEW_ARRAY_RANGE */
+
+.LOP_FILLED_NEW_ARRAY_RANGE_break:
+ movl $0, -8(%esp) # push parameter false
+ movl %ecx, -12(%esp) # push parameter BBBB
+ movl rGLUE, %edx # %edx<- MterpGlue pointer
+ movl offGlue_method(%edx), %edx # %edx<- glue->method
+ movl offMethod_clazz(%edx), %edx # %edx<- glue->method->clazz
+ movl %edx, -16(%esp) # push parameter glue->method->clazz
+ lea -16(%esp), %esp
+ call dvmResolveClass # call: (const ClassObject* referrer, u4 classIdx,
+ # bool fromUnverifiedConstant)
+ # return: ClassObject*
+ lea 16(%esp), %esp
+ cmp $0, %eax # check for null return
+ je common_exceptionThrown # handle exception
+
+ /*
+ * On entry:
+ * %eax holds array class
+ * rINST holds BA or AA
+ */
+
+.LOP_FILLED_NEW_ARRAY_RANGE_continue:
+ movl offClassObject_descriptor(%eax), %eax # %eax<- arrayClass->descriptor
+ movzbl 1(%eax), %eax # %eax<- descriptor[1]
+ cmpb $'I', %al # check if array of ints
+ je 1f
+ cmpb $'L', %al
+ je 1f
+ cmpb $'[', %al
+ jne .LOP_FILLED_NEW_ARRAY_RANGE_notimpl # jump to not implemented
+1:
+ movl %eax, sReg0 # save type
+ movl rINST, -12(%esp) # push parameter length
+ movl %eax, -16(%esp) # push parameter descriptor[1]
+ movl $ALLOC_DONT_TRACK, -8(%esp) # push parameter to allocate flags
+ .if (!1)
+ shrl $4, -12(%esp) # parameter length is B
+ .endif
+ lea -16(%esp), %esp
+ call dvmAllocPrimitiveArray # call: (char type, size_t length, int allocFlags)
+ # return: ArrayObject*
+ lea 16(%esp), %esp
+ cmp $0, %eax # check for null return
+ je common_exceptionThrown # handle exception
+
+ FETCH 2, %edx # %edx<- FEDC or CCCC
+ movl rGLUE, %ecx # %ecx<- MterpGlue pointer
+ movl %eax, offGlue_retval(%ecx) # retval<- new array
+ lea offArrayObject_contents(%eax), %eax # %eax<- newArray->contents
+ subl $1, -12(%esp) # length--; check for negative
+ js 2f # if length was zero, finish
+
+ /*
+ * copy values from registers into the array
+ * %eax=array, %edx=CCCC/FEDC, -12(%esp)=length (from AA or B), rINST=AA/BA
+ */
+
+ .if 1
+ lea (rFP, %edx, 4), %ecx # %ecx<- &fpp[CCCC]
+1:
+ movl (%ecx), %edx # %edx<- %ecx++
+ lea 4(%ecx), %ecx # %ecx++
+ movl %edx, (%eax) # *contents<- vX
+ lea 4(%eax), %eax # %eax++; contents++
+ subl $1, -12(%esp) # length--
+ jns 1b # or continue at 2
+ .else
+ cmp $4, -12(%esp) # check length
+ jne 1f # has four args
+ and $15, rINST # rINST<- A
+ GET_VREG rINST # rINST<- vA
+ subl $1, -12(%esp) # count--
+ movl rINST, 16(%eax) # contents[4]<- vA
+1:
+ movl %edx, %ecx # %ecx<- %edx; ecx for temp
+ andl $15, %ecx # %ecx<- G/F/E/D
+ GET_VREG %ecx # %ecx<- vG/vF/vE/vD
+ shr $4, %edx # %edx<- put next reg in low 4
+ subl $1, -12(%esp) # count--
+ movl %ecx, (%eax) # *contents<- vX
+ lea 4(%eax), %eax # %eax++; contents++
+ jns 1b # or continue at 2
+ .endif
+2:
+ cmpb $'I', sReg0 # check for int array
+ je 3f
+ movl rGLUE, %ecx # %ecx<- MterpGlue pointer
+ movl offGlue_retval(%ecx), %eax # Object head
+ movl offGlue_cardTable(%ecx), %ecx # card table base
+ shrl $GC_CARD_SHIFT, %eax # convert to card num
+ movb %cl,(%ecx, %eax) # mark card based on object head
+3:
+ FINISH 3 # jump to next instruction
+
+ /*
+ * Throw an exception to indicate this mode of filled-new-array
+ * has not been implemented.
+ */
+
+.LOP_FILLED_NEW_ARRAY_RANGE_notimpl:
+ movl $.LstrInternalError, -8(%esp)
+ movl $.LstrFilledNewArrayNotImpl, -4(%esp)
+ lea -8(%esp), %esp
+ call dvmThrowException # call: (const char* exceptionDescriptor,
+ # const char* msg)
+ # return: void
+ lea 8(%esp), %esp
+ jmp common_exceptionThrown
+
+.if (!1) # define in one or the other, not both
+.LstrFilledNewArrayNotImpl:
+.asciz "filled-new-array only implemented for 'int'"
+.LstrInternalError:
+.asciz "Ljava/lang/InternalError;"
+.endif
+
+/* continuation for OP_PACKED_SWITCH */
+.LOP_PACKED_SWITCH_finish:
+ FINISH_RB %edx, %ecx # jump to next instruction
+
+/* continuation for OP_SPARSE_SWITCH */
+.LOP_SPARSE_SWITCH_finish:
+ FINISH_RB %edx, %ecx # jump to next instruction
+
+/* continuation for OP_CMPL_FLOAT */
+.LOP_CMPL_FLOAT_greater:
+ movl $0x1, (rFP, rINST, 4) # vAA<- greater than
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+.LOP_CMPL_FLOAT_final:
+ movl $0x0, (rFP, rINST, 4) # vAA<- equal
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+.LOP_CMPL_FLOAT_finalNan:
+ movl $0xFFFFFFFF, (rFP, rINST, 4) # vAA<- NaN
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* continuation for OP_CMPG_FLOAT */
+.LOP_CMPG_FLOAT_greater:
+ movl $0x1, (rFP, rINST, 4) # vAA<- greater than
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+.LOP_CMPG_FLOAT_final:
+ movl $0x0, (rFP, rINST, 4) # vAA<- equal
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+.LOP_CMPG_FLOAT_finalNan:
+ movl $0x1, (rFP, rINST, 4) # vAA<- NaN
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* continuation for OP_CMPL_DOUBLE */
+.LOP_CMPL_DOUBLE_greater:
+ movl $0x1, (rFP, rINST, 4) # vAA<- greater than
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+.LOP_CMPL_DOUBLE_final:
+ movl $0x0, (rFP, rINST, 4) # vAA<- equal
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+.LOP_CMPL_DOUBLE_finalNan:
+ movl $0xFFFFFFFF, (rFP, rINST, 4) # vAA<- NaN
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* continuation for OP_CMPG_DOUBLE */
+.LOP_CMPG_DOUBLE_greater:
+ movl $0x1, (rFP, rINST, 4) # vAA<- greater than
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+.LOP_CMPG_DOUBLE_final:
+ movl $0x0, (rFP, rINST, 4) # vAA<- equal
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+.LOP_CMPG_DOUBLE_finalNan:
+ movl $0x1, (rFP, rINST, 4) # vAA<- NaN
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* continuation for OP_CMP_LONG */
+
+.LOP_CMP_LONG_final:
+ movl $0x0, (rFP, rINST, 4) # vAA<- equal
+ FINISH 2 # jump to next instruction
+
+.LOP_CMP_LONG_less:
+ movl $0xFFFFFFFF, (rFP, rINST, 4) # vAA<- less than
+ FINISH 2 # jump to next instruction
+
+.LOP_CMP_LONG_greater:
+ movl $0x1, (rFP, rINST, 4) # vAA<- greater than
+ FINISH 2 # jump to next instruction
+
+/* continuation for OP_APUT_OBJECT */
+
+.LOP_APUT_OBJECT_finish:
+ movl %edx, sReg0 # save &vBB[vCC]
+ movl %eax, sReg1 # save object head
+ movl offObject_clazz(rINST), %edx # %edx<- obj->clazz
+ movl %edx, -8(%esp) # push parameter obj->clazz
+ movl offObject_clazz(%eax), %eax # %eax<- arrayObj->clazz
+ movl %eax, -4(%esp) # push parameter arrayObj->clazz
+ lea -8(%esp), %esp
+ call dvmCanPutArrayElement # test object type vs. array type
+ # call: ClassObject* elemClass, ClassObject* arrayClass)
+ # return: bool
+ lea 8(%esp), %esp
+ testl %eax, %eax # check for invalid array value
+ je common_errArrayStore # handle invalid array value
+ movl sReg0, %edx # restore &vBB[vCC]
+ movl rINST, offArrayObject_contents(%edx)
+ movl rGLUE, %eax
+ FFETCH_ADV 2, %ecx # %ecx<- next instruction hi; fetch, advance
+ movl offGlue_cardTable(%eax), %eax # get card table base
+ movl sReg1, %edx # restore object head
+ shrl $GC_CARD_SHIFT, %edx # object head to card number
+ movb %al, (%eax, %edx) # mark card using object head
+ FGETOP_JMP 2, %ecx # jump to next instruction; getop, jmp
+.LOP_APUT_OBJECT_skip_check:
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movl rINST, offArrayObject_contents(%edx)
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* continuation for OP_IGET */
+
+.LOP_IGET_finish:
+ call dvmResolveInstField # call: (const ClassObject* referrer, u4 ifieldIdx)
+ # return: InstField*
+ cmp $0, %eax # check if resolved
+ lea 8(%esp), %esp
+ je common_exceptionThrown # not resolved; handle exception
+
+ /*
+ * %eax holds resolved field
+ */
+
+.LOP_IGET_finish2:
+ movl rINST, %ecx # %ecx<- BA
+ shr $4, %ecx # %ecx<- B
+ and $15, rINST # rINST<- A
+
+ GET_VREG %ecx # %ecx<- vB
+ cmp $0, %ecx # check for null object
+ je common_errNullObject # handle null object
+ movl offInstField_byteOffset(%eax), %edx # %edx<- field offset
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movl (%ecx, %edx), %edx # %edx<- object field
+ SET_VREG %edx, rINST # vA<- %edx; object field
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* continuation for OP_IGET_WIDE */
+
+.LOP_IGET_WIDE_finish2:
+ lea -8(%esp), %esp
+ call dvmResolveInstField # resolve InstField ptr
+ # call: (const ClassObject* referrer, u4 ifieldIdx)
+ # return: InstField*
+ cmp $0, %eax # check if resolved
+ lea 8(%esp), %esp
+ movl %eax, %ecx # %ecx<- %eax; %ecx expected to hold field
+ je common_exceptionThrown
+
+ /*
+ * %ecx holds resolved field
+ */
+
+.LOP_IGET_WIDE_finish:
+
+ movl rINST, %edx # %edx<- BA
+ shr $4, %edx # %edx<- B
+ andl $15, rINST # rINST<- A
+ GET_VREG %edx # %edx<- vB
+ cmp $0, %edx # check for null object
+ je common_errNullObject
+ movl offInstField_byteOffset(%ecx), %ecx # %ecx<- field offset
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movq (%ecx, %edx), %xmm0 # %xmm0<- object field
+ movq %xmm0, (rFP, rINST, 4) # vA<- %xmm0; object field
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* continuation for OP_IGET_OBJECT */
+
+.LOP_IGET_OBJECT_finish:
+ call dvmResolveInstField # call: (const ClassObject* referrer, u4 ifieldIdx)
+ # return: InstField*
+ cmp $0, %eax # check if resolved
+ lea 8(%esp), %esp
+ je common_exceptionThrown # not resolved; handle exception
+
+ /*
+ * %eax holds resolved field
+ */
+
+.LOP_IGET_OBJECT_finish2:
+ movl rINST, %ecx # %ecx<- BA
+ shr $4, %ecx # %ecx<- B
+ and $15, rINST # rINST<- A
+
+ GET_VREG %ecx # %ecx<- vB
+ cmp $0, %ecx # check for null object
+ je common_errNullObject # handle null object
+ movl offInstField_byteOffset(%eax), %edx # %edx<- field offset
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movl (%ecx, %edx), %edx # %edx<- object field
+ SET_VREG %edx, rINST # vA<- %edx; object field
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* continuation for OP_IGET_BOOLEAN */
+
+.LOP_IGET_BOOLEAN_finish:
+ call dvmResolveInstField # call: (const ClassObject* referrer, u4 ifieldIdx)
+ # return: InstField*
+ cmp $0, %eax # check if resolved
+ lea 8(%esp), %esp
+ je common_exceptionThrown # not resolved; handle exception
+
+ /*
+ * %eax holds resolved field
+ */
+
+.LOP_IGET_BOOLEAN_finish2:
+ movl rINST, %ecx # %ecx<- BA
+ shr $4, %ecx # %ecx<- B
+ and $15, rINST # rINST<- A
+
+ GET_VREG %ecx # %ecx<- vB
+ cmp $0, %ecx # check for null object
+ je common_errNullObject # handle null object
+ movl offInstField_byteOffset(%eax), %edx # %edx<- field offset
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movl (%ecx, %edx), %edx # %edx<- object field
+ SET_VREG %edx, rINST # vA<- %edx; object field
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* continuation for OP_IGET_BYTE */
+
+.LOP_IGET_BYTE_finish:
+ call dvmResolveInstField # call: (const ClassObject* referrer, u4 ifieldIdx)
+ # return: InstField*
+ cmp $0, %eax # check if resolved
+ lea 8(%esp), %esp
+ je common_exceptionThrown # not resolved; handle exception
+
+ /*
+ * %eax holds resolved field
+ */
+
+.LOP_IGET_BYTE_finish2:
+ movl rINST, %ecx # %ecx<- BA
+ shr $4, %ecx # %ecx<- B
+ and $15, rINST # rINST<- A
+
+ GET_VREG %ecx # %ecx<- vB
+ cmp $0, %ecx # check for null object
+ je common_errNullObject # handle null object
+ movl offInstField_byteOffset(%eax), %edx # %edx<- field offset
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movl (%ecx, %edx), %edx # %edx<- object field
+ SET_VREG %edx, rINST # vA<- %edx; object field
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* continuation for OP_IGET_CHAR */
+
+.LOP_IGET_CHAR_finish:
+ call dvmResolveInstField # call: (const ClassObject* referrer, u4 ifieldIdx)
+ # return: InstField*
+ cmp $0, %eax # check if resolved
+ lea 8(%esp), %esp
+ je common_exceptionThrown # not resolved; handle exception
+
+ /*
+ * %eax holds resolved field
+ */
+
+.LOP_IGET_CHAR_finish2:
+ movl rINST, %ecx # %ecx<- BA
+ shr $4, %ecx # %ecx<- B
+ and $15, rINST # rINST<- A
+
+ GET_VREG %ecx # %ecx<- vB
+ cmp $0, %ecx # check for null object
+ je common_errNullObject # handle null object
+ movl offInstField_byteOffset(%eax), %edx # %edx<- field offset
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movl (%ecx, %edx), %edx # %edx<- object field
+ SET_VREG %edx, rINST # vA<- %edx; object field
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* continuation for OP_IGET_SHORT */
+
+.LOP_IGET_SHORT_finish:
+ call dvmResolveInstField # call: (const ClassObject* referrer, u4 ifieldIdx)
+ # return: InstField*
+ cmp $0, %eax # check if resolved
+ lea 8(%esp), %esp
+ je common_exceptionThrown # not resolved; handle exception
+
+ /*
+ * %eax holds resolved field
+ */
+
+.LOP_IGET_SHORT_finish2:
+ movl rINST, %ecx # %ecx<- BA
+ shr $4, %ecx # %ecx<- B
+ and $15, rINST # rINST<- A
+
+ GET_VREG %ecx # %ecx<- vB
+ cmp $0, %ecx # check for null object
+ je common_errNullObject # handle null object
+ movl offInstField_byteOffset(%eax), %edx # %edx<- field offset
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movl (%ecx, %edx), %edx # %edx<- object field
+ SET_VREG %edx, rINST # vA<- %edx; object field
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* continuation for OP_IPUT */
+
+.LOP_IPUT_finish:
+ movl offGlue_method(%edx), %edx # %edx<- glue->method
+ EXPORT_PC # in case an exception is thrown
+ movl %ecx, -4(%esp) # push parameter CCCC; field ref
+ movl offMethod_clazz(%edx), %edx # %edx<- method->clazz
+ lea -8(%esp), %esp
+ movl %edx, (%esp) # push parameter method->clazz
+ call dvmResolveInstField # call: (const ClassObject* referrer, u4 ifieldIdx)
+ # return: InstField*
+ lea 8(%esp), %esp
+ cmp $0, %eax # check if resolved
+ jne .LOP_IPUT_finish2
+ jmp common_exceptionThrown # not resolved; handle exception
+
+.LOP_IPUT_finish2:
+ movl rINST, %ecx # %ecx<- BA+
+ shr $4, %ecx # %ecx<- B
+ and $15, rINST # rINST<- A
+ GET_VREG %ecx # %ecx<- vB
+ cmp $0, %ecx # check for null object
+ je common_errNullObject # handle null object
+ movl offInstField_byteOffset(%eax), %edx # %edx<- field offset
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG rINST # rINST<- vA
+ movl rINST, (%edx, %ecx) # object field<- vA
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* continuation for OP_IPUT_WIDE */
+
+.LOP_IPUT_WIDE_finish2:
+ lea -8(%esp), %esp
+ call dvmResolveInstField # resolve InstField ptr
+ cmp $0, %eax # check if resolved
+ lea 8(%esp), %esp
+ movl %eax, %ecx # %ecx<- %eax; %ecx expected to hold field
+ jne .LOP_IPUT_WIDE_finish
+ jmp common_exceptionThrown
+
+ /*
+ * Currently:
+ * %ecx holds resolved field
+ * %edx does not hold object yet
+ */
+
+.LOP_IPUT_WIDE_finish:
+ movl rINST, %edx # %edx<- BA
+ shr $4, %edx # %edx<- B
+ andl $15, rINST # rINST<- A
+ GET_VREG %edx # %edx<- vB
+ cmp $0, %edx # check for null object
+ je common_errNullObject
+ movl offInstField_byteOffset(%ecx), %ecx # %ecx<- field offset
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movq (rFP, rINST, 4), %xmm0 # %xmm0<- vA
+ movq %xmm0, (%ecx, %edx) # object field<- %xmm0; vA
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* continuation for OP_IPUT_OBJECT */
+
+.LOP_IPUT_OBJECT_finish:
+ movl offGlue_method(%edx), %edx # %edx<- glue->method
+ EXPORT_PC # in case an exception is thrown
+ movl %ecx, -4(%esp) # push parameter CCCC; field ref
+ movl offMethod_clazz(%edx), %edx # %edx<- method->clazz
+ lea -8(%esp), %esp
+ movl %edx, (%esp) # push parameter method->clazz
+ call dvmResolveInstField # call: (const ClassObject* referrer, u4 ifieldIdx)
+ # return: InstField*
+ lea 8(%esp), %esp
+ cmp $0, %eax # check if resolved
+ jne .LOP_IPUT_OBJECT_finish2
+ jmp common_exceptionThrown # not resolved; handle exception
+
+.LOP_IPUT_OBJECT_finish2:
+ movl rINST, %ecx # %ecx<- BA+
+ shr $4, %ecx # %ecx<- B
+ and $15, rINST # rINST<- A
+ GET_VREG %ecx # %ecx<- vB
+ cmp $0, %ecx # check for null object
+ je common_errNullObject # handle null object
+ movl offInstField_byteOffset(%eax), %edx # %edx<- field offset
+ GET_VREG rINST # rINST<- vA
+ movl rINST, (%edx, %ecx) # object field<- vA
+ FFETCH_ADV 2, %edx # %edx<- next instruction hi; fetch, advance
+ movl rGLUE, %eax # get glue
+ movl offGlue_cardTable(%eax), %eax # get card table base
+ testl rINST, rINST # test if we stored a null value
+ je 1f # skip card mark if null stored
+ shrl $GC_CARD_SHIFT, %ecx # set obeject head to card number
+ movb %al, (%eax, %ecx)
+1:
+ FGETOP_JMP 2, %edx # jump to next instruction; getop, jmp
+
+/* continuation for OP_IPUT_BOOLEAN */
+
+.LOP_IPUT_BOOLEAN_finish:
+ movl offGlue_method(%edx), %edx # %edx<- glue->method
+ EXPORT_PC # in case an exception is thrown
+ movl %ecx, -4(%esp) # push parameter CCCC; field ref
+ movl offMethod_clazz(%edx), %edx # %edx<- method->clazz
+ lea -8(%esp), %esp
+ movl %edx, (%esp) # push parameter method->clazz
+ call dvmResolveInstField # call: (const ClassObject* referrer, u4 ifieldIdx)
+ # return: InstField*
+ lea 8(%esp), %esp
+ cmp $0, %eax # check if resolved
+ jne .LOP_IPUT_BOOLEAN_finish2
+ jmp common_exceptionThrown # not resolved; handle exception
+
+.LOP_IPUT_BOOLEAN_finish2:
+ movl rINST, %ecx # %ecx<- BA+
+ shr $4, %ecx # %ecx<- B
+ and $15, rINST # rINST<- A
+ GET_VREG %ecx # %ecx<- vB
+ cmp $0, %ecx # check for null object
+ je common_errNullObject # handle null object
+ movl offInstField_byteOffset(%eax), %edx # %edx<- field offset
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG rINST # rINST<- vA
+ movl rINST, (%edx, %ecx) # object field<- vA
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* continuation for OP_IPUT_BYTE */
+
+.LOP_IPUT_BYTE_finish:
+ movl offGlue_method(%edx), %edx # %edx<- glue->method
+ EXPORT_PC # in case an exception is thrown
+ movl %ecx, -4(%esp) # push parameter CCCC; field ref
+ movl offMethod_clazz(%edx), %edx # %edx<- method->clazz
+ lea -8(%esp), %esp
+ movl %edx, (%esp) # push parameter method->clazz
+ call dvmResolveInstField # call: (const ClassObject* referrer, u4 ifieldIdx)
+ # return: InstField*
+ lea 8(%esp), %esp
+ cmp $0, %eax # check if resolved
+ jne .LOP_IPUT_BYTE_finish2
+ jmp common_exceptionThrown # not resolved; handle exception
+
+.LOP_IPUT_BYTE_finish2:
+ movl rINST, %ecx # %ecx<- BA+
+ shr $4, %ecx # %ecx<- B
+ and $15, rINST # rINST<- A
+ GET_VREG %ecx # %ecx<- vB
+ cmp $0, %ecx # check for null object
+ je common_errNullObject # handle null object
+ movl offInstField_byteOffset(%eax), %edx # %edx<- field offset
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG rINST # rINST<- vA
+ movl rINST, (%edx, %ecx) # object field<- vA
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* continuation for OP_IPUT_CHAR */
+
+.LOP_IPUT_CHAR_finish:
+ movl offGlue_method(%edx), %edx # %edx<- glue->method
+ EXPORT_PC # in case an exception is thrown
+ movl %ecx, -4(%esp) # push parameter CCCC; field ref
+ movl offMethod_clazz(%edx), %edx # %edx<- method->clazz
+ lea -8(%esp), %esp
+ movl %edx, (%esp) # push parameter method->clazz
+ call dvmResolveInstField # call: (const ClassObject* referrer, u4 ifieldIdx)
+ # return: InstField*
+ lea 8(%esp), %esp
+ cmp $0, %eax # check if resolved
+ jne .LOP_IPUT_CHAR_finish2
+ jmp common_exceptionThrown # not resolved; handle exception
+
+.LOP_IPUT_CHAR_finish2:
+ movl rINST, %ecx # %ecx<- BA+
+ shr $4, %ecx # %ecx<- B
+ and $15, rINST # rINST<- A
+ GET_VREG %ecx # %ecx<- vB
+ cmp $0, %ecx # check for null object
+ je common_errNullObject # handle null object
+ movl offInstField_byteOffset(%eax), %edx # %edx<- field offset
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG rINST # rINST<- vA
+ movl rINST, (%edx, %ecx) # object field<- vA
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* continuation for OP_IPUT_SHORT */
+
+.LOP_IPUT_SHORT_finish:
+ movl offGlue_method(%edx), %edx # %edx<- glue->method
+ EXPORT_PC # in case an exception is thrown
+ movl %ecx, -4(%esp) # push parameter CCCC; field ref
+ movl offMethod_clazz(%edx), %edx # %edx<- method->clazz
+ lea -8(%esp), %esp
+ movl %edx, (%esp) # push parameter method->clazz
+ call dvmResolveInstField # call: (const ClassObject* referrer, u4 ifieldIdx)
+ # return: InstField*
+ lea 8(%esp), %esp
+ cmp $0, %eax # check if resolved
+ jne .LOP_IPUT_SHORT_finish2
+ jmp common_exceptionThrown # not resolved; handle exception
+
+.LOP_IPUT_SHORT_finish2:
+ movl rINST, %ecx # %ecx<- BA+
+ shr $4, %ecx # %ecx<- B
+ and $15, rINST # rINST<- A
+ GET_VREG %ecx # %ecx<- vB
+ cmp $0, %ecx # check for null object
+ je common_errNullObject # handle null object
+ movl offInstField_byteOffset(%eax), %edx # %edx<- field offset
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG rINST # rINST<- vA
+ movl rINST, (%edx, %ecx) # object field<- vA
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* continuation for OP_SGET */
+
+.LOP_SGET_resolve:
+ movl offGlue_method(%edx), %edx # %edx <- glue->method
+ EXPORT_PC # in case an exception is thrown
+ movl %eax, -4(%esp) # push parameter CCCC; field ref
+ movl offMethod_clazz(%edx), %edx # %edx<- method->clazz
+ movl %edx, -8(%esp) # push parameter method->clazz
+ lea -8(%esp), %esp
+ call dvmResolveStaticField # call: (const ClassObject* referrer, u4 ifieldIdx)
+ # return: StaticField*
+ cmp $0, %eax # check if initalization failed
+ lea 8(%esp), %esp
+ je common_exceptionThrown # failed; handle exception
+ mov %eax, %ecx # %ecx<- result
+
+.LOP_SGET_finish:
+ FFETCH_ADV 2, %edx # %edx<- next instruction hi; fetch, advance
+ movl offStaticField_value(%ecx), %eax # %eax<- field value
+ SET_VREG %eax, rINST # vAA<- field value
+ FGETOP_JMP 2, %edx # jump to next instruction; getop, jmp
+
+/* continuation for OP_SGET_WIDE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * %edx: BBBB field ref
+ */
+
+.LOP_SGET_WIDE_resolve:
+ movl offGlue_method(%eax), %eax # %eax <- glue->method
+ EXPORT_PC # in case an exception is thrown
+ movl %edx, -4(%esp) # push parameter CCCC; field ref
+ movl offMethod_clazz(%eax), %eax # %eax<- method->clazz
+ movl %eax, -8(%esp) # push parameter method->clazz
+ lea -8(%esp), %esp
+ call dvmResolveStaticField # call: (const ClassObject* referrer, u4 ifieldIdx)
+ # return: StaticField*
+ lea 8(%esp), %esp
+ cmp $0, %eax # check if initalization failed
+ movl %eax, %ecx # %ecx<- result
+ jne .LOP_SGET_WIDE_finish # success, continue
+ jmp common_exceptionThrown # failed; handle exception
+
+/* continuation for OP_SGET_OBJECT */
+
+.LOP_SGET_OBJECT_resolve:
+ movl offGlue_method(%edx), %edx # %edx <- glue->method
+ EXPORT_PC # in case an exception is thrown
+ movl %eax, -4(%esp) # push parameter CCCC; field ref
+ movl offMethod_clazz(%edx), %edx # %edx<- method->clazz
+ movl %edx, -8(%esp) # push parameter method->clazz
+ lea -8(%esp), %esp
+ call dvmResolveStaticField # call: (const ClassObject* referrer, u4 ifieldIdx)
+ # return: StaticField*
+ cmp $0, %eax # check if initalization failed
+ lea 8(%esp), %esp
+ je common_exceptionThrown # failed; handle exception
+ mov %eax, %ecx # %ecx<- result
+
+.LOP_SGET_OBJECT_finish:
+ FFETCH_ADV 2, %edx # %edx<- next instruction hi; fetch, advance
+ movl offStaticField_value(%ecx), %eax # %eax<- field value
+ SET_VREG %eax, rINST # vAA<- field value
+ FGETOP_JMP 2, %edx # jump to next instruction; getop, jmp
+
+/* continuation for OP_SGET_BOOLEAN */
+
+.LOP_SGET_BOOLEAN_resolve:
+ movl offGlue_method(%edx), %edx # %edx <- glue->method
+ EXPORT_PC # in case an exception is thrown
+ movl %eax, -4(%esp) # push parameter CCCC; field ref
+ movl offMethod_clazz(%edx), %edx # %edx<- method->clazz
+ movl %edx, -8(%esp) # push parameter method->clazz
+ lea -8(%esp), %esp
+ call dvmResolveStaticField # call: (const ClassObject* referrer, u4 ifieldIdx)
+ # return: StaticField*
+ cmp $0, %eax # check if initalization failed
+ lea 8(%esp), %esp
+ je common_exceptionThrown # failed; handle exception
+ mov %eax, %ecx # %ecx<- result
+
+.LOP_SGET_BOOLEAN_finish:
+ FFETCH_ADV 2, %edx # %edx<- next instruction hi; fetch, advance
+ movl offStaticField_value(%ecx), %eax # %eax<- field value
+ SET_VREG %eax, rINST # vAA<- field value
+ FGETOP_JMP 2, %edx # jump to next instruction; getop, jmp
+
+/* continuation for OP_SGET_BYTE */
+
+.LOP_SGET_BYTE_resolve:
+ movl offGlue_method(%edx), %edx # %edx <- glue->method
+ EXPORT_PC # in case an exception is thrown
+ movl %eax, -4(%esp) # push parameter CCCC; field ref
+ movl offMethod_clazz(%edx), %edx # %edx<- method->clazz
+ movl %edx, -8(%esp) # push parameter method->clazz
+ lea -8(%esp), %esp
+ call dvmResolveStaticField # call: (const ClassObject* referrer, u4 ifieldIdx)
+ # return: StaticField*
+ cmp $0, %eax # check if initalization failed
+ lea 8(%esp), %esp
+ je common_exceptionThrown # failed; handle exception
+ mov %eax, %ecx # %ecx<- result
+
+.LOP_SGET_BYTE_finish:
+ FFETCH_ADV 2, %edx # %edx<- next instruction hi; fetch, advance
+ movl offStaticField_value(%ecx), %eax # %eax<- field value
+ SET_VREG %eax, rINST # vAA<- field value
+ FGETOP_JMP 2, %edx # jump to next instruction; getop, jmp
+
+/* continuation for OP_SGET_CHAR */
+
+.LOP_SGET_CHAR_resolve:
+ movl offGlue_method(%edx), %edx # %edx <- glue->method
+ EXPORT_PC # in case an exception is thrown
+ movl %eax, -4(%esp) # push parameter CCCC; field ref
+ movl offMethod_clazz(%edx), %edx # %edx<- method->clazz
+ movl %edx, -8(%esp) # push parameter method->clazz
+ lea -8(%esp), %esp
+ call dvmResolveStaticField # call: (const ClassObject* referrer, u4 ifieldIdx)
+ # return: StaticField*
+ cmp $0, %eax # check if initalization failed
+ lea 8(%esp), %esp
+ je common_exceptionThrown # failed; handle exception
+ mov %eax, %ecx # %ecx<- result
+
+.LOP_SGET_CHAR_finish:
+ FFETCH_ADV 2, %edx # %edx<- next instruction hi; fetch, advance
+ movl offStaticField_value(%ecx), %eax # %eax<- field value
+ SET_VREG %eax, rINST # vAA<- field value
+ FGETOP_JMP 2, %edx # jump to next instruction; getop, jmp
+
+/* continuation for OP_SGET_SHORT */
+
+.LOP_SGET_SHORT_resolve:
+ movl offGlue_method(%edx), %edx # %edx <- glue->method
+ EXPORT_PC # in case an exception is thrown
+ movl %eax, -4(%esp) # push parameter CCCC; field ref
+ movl offMethod_clazz(%edx), %edx # %edx<- method->clazz
+ movl %edx, -8(%esp) # push parameter method->clazz
+ lea -8(%esp), %esp
+ call dvmResolveStaticField # call: (const ClassObject* referrer, u4 ifieldIdx)
+ # return: StaticField*
+ cmp $0, %eax # check if initalization failed
+ lea 8(%esp), %esp
+ je common_exceptionThrown # failed; handle exception
+ mov %eax, %ecx # %ecx<- result
+
+.LOP_SGET_SHORT_finish:
+ FFETCH_ADV 2, %edx # %edx<- next instruction hi; fetch, advance
+ movl offStaticField_value(%ecx), %eax # %eax<- field value
+ SET_VREG %eax, rINST # vAA<- field value
+ FGETOP_JMP 2, %edx # jump to next instruction; getop, jmp
+
+/* continuation for OP_SPUT */
+
+.LOP_SPUT_resolve:
+ movl offGlue_method(%edx), %edx # %edx <- glue->method
+ EXPORT_PC # in case an exception is thrown
+ movl %eax, -4(%esp) # push parameter CCCC; field ref
+ movl offMethod_clazz(%edx), %edx # %edx<- method->clazz
+ movl %edx, -8(%esp) # push parameter method->clazz
+ lea -8(%esp), %esp
+ call dvmResolveStaticField # call: (const ClassObject* referrer, u4 ifieldIdx)
+ # return: StaticField*
+ cmp $0, %eax # check if initalization failed
+ lea 8(%esp), %esp
+ je common_exceptionThrown # failed; handle exception
+ movl %eax, %ecx # %ecx<- result
+
+.LOP_SPUT_finish:
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG rINST # rINST<- vAA
+ movl rINST, offStaticField_value(%ecx) # field value<- vAA
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* continuation for OP_SPUT_WIDE */
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * %edx: BBBB field ref
+ */
+
+.LOP_SPUT_WIDE_resolve:
+ movl offGlue_method(%eax), %eax # %eax <- glue->method
+ EXPORT_PC # in case an exception is thrown
+ movl %edx, -4(%esp) # push parameter CCCC; field ref
+ movl offMethod_clazz(%eax), %eax # %eax<- method->clazz
+ movl %eax, -8(%esp)
+ lea -8(%esp), %esp
+ call dvmResolveStaticField # call: (const ClassObject* referrer, u4 ifieldIdx)
+ # return: StaticField*
+ lea 8(%esp), %esp
+ cmp $0, %eax # check if initalization failed
+ movl %eax, %ecx # %ecx<- result
+ jne .LOP_SPUT_WIDE_finish # success, continue
+ jmp common_exceptionThrown # failed; handle exception
+
+/* continuation for OP_SPUT_OBJECT */
+
+.LOP_SPUT_OBJECT_resolve:
+ movl offGlue_method(%edx), %edx # %edx <- glue->method
+ EXPORT_PC # in case an exception is thrown
+ movl %eax, -4(%esp) # push parameter CCCC; field ref
+ movl offMethod_clazz(%edx), %edx # %edx<- method->clazz
+ movl %edx, -8(%esp) # push parameter method->clazz
+ lea -8(%esp), %esp
+ call dvmResolveStaticField # call: (const ClassObject* referrer, u4 ifieldIdx)
+ # return: StaticField*
+ cmp $0, %eax # check if initalization failed
+ lea 8(%esp), %esp
+ je common_exceptionThrown # failed; handle exception
+ movl %eax, %ecx # %ecx<- result
+
+.LOP_SPUT_OBJECT_finish:
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG rINST # rINST<- vAA
+
+
+ movl rINST, offStaticField_value(%ecx) # field value<- vAA
+ testl rINST, rINST # stored null object ptr?
+ je 1f
+ movl rGLUE, %edx # get glue
+ movl offField_clazz(%ecx), %ecx # ecx<- field->clazz
+ movl offGlue_cardTable(%edx), %edx # get card table base
+ shrl $GC_CARD_SHIFT, %ecx # head to card number
+ movb %dl, (%edx, %ecx) # mark card
+1:
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* continuation for OP_SPUT_BOOLEAN */
+
+.LOP_SPUT_BOOLEAN_resolve:
+ movl offGlue_method(%edx), %edx # %edx <- glue->method
+ EXPORT_PC # in case an exception is thrown
+ movl %eax, -4(%esp) # push parameter CCCC; field ref
+ movl offMethod_clazz(%edx), %edx # %edx<- method->clazz
+ movl %edx, -8(%esp) # push parameter method->clazz
+ lea -8(%esp), %esp
+ call dvmResolveStaticField # call: (const ClassObject* referrer, u4 ifieldIdx)
+ # return: StaticField*
+ cmp $0, %eax # check if initalization failed
+ lea 8(%esp), %esp
+ je common_exceptionThrown # failed; handle exception
+ movl %eax, %ecx # %ecx<- result
+
+.LOP_SPUT_BOOLEAN_finish:
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG rINST # rINST<- vAA
+ movl rINST, offStaticField_value(%ecx) # field value<- vAA
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* continuation for OP_SPUT_BYTE */
+
+.LOP_SPUT_BYTE_resolve:
+ movl offGlue_method(%edx), %edx # %edx <- glue->method
+ EXPORT_PC # in case an exception is thrown
+ movl %eax, -4(%esp) # push parameter CCCC; field ref
+ movl offMethod_clazz(%edx), %edx # %edx<- method->clazz
+ movl %edx, -8(%esp) # push parameter method->clazz
+ lea -8(%esp), %esp
+ call dvmResolveStaticField # call: (const ClassObject* referrer, u4 ifieldIdx)
+ # return: StaticField*
+ cmp $0, %eax # check if initalization failed
+ lea 8(%esp), %esp
+ je common_exceptionThrown # failed; handle exception
+ movl %eax, %ecx # %ecx<- result
+
+.LOP_SPUT_BYTE_finish:
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG rINST # rINST<- vAA
+ movl rINST, offStaticField_value(%ecx) # field value<- vAA
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* continuation for OP_SPUT_CHAR */
+
+.LOP_SPUT_CHAR_resolve:
+ movl offGlue_method(%edx), %edx # %edx <- glue->method
+ EXPORT_PC # in case an exception is thrown
+ movl %eax, -4(%esp) # push parameter CCCC; field ref
+ movl offMethod_clazz(%edx), %edx # %edx<- method->clazz
+ movl %edx, -8(%esp) # push parameter method->clazz
+ lea -8(%esp), %esp
+ call dvmResolveStaticField # call: (const ClassObject* referrer, u4 ifieldIdx)
+ # return: StaticField*
+ cmp $0, %eax # check if initalization failed
+ lea 8(%esp), %esp
+ je common_exceptionThrown # failed; handle exception
+ movl %eax, %ecx # %ecx<- result
+
+.LOP_SPUT_CHAR_finish:
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG rINST # rINST<- vAA
+ movl rINST, offStaticField_value(%ecx) # field value<- vAA
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* continuation for OP_SPUT_SHORT */
+
+.LOP_SPUT_SHORT_resolve:
+ movl offGlue_method(%edx), %edx # %edx <- glue->method
+ EXPORT_PC # in case an exception is thrown
+ movl %eax, -4(%esp) # push parameter CCCC; field ref
+ movl offMethod_clazz(%edx), %edx # %edx<- method->clazz
+ movl %edx, -8(%esp) # push parameter method->clazz
+ lea -8(%esp), %esp
+ call dvmResolveStaticField # call: (const ClassObject* referrer, u4 ifieldIdx)
+ # return: StaticField*
+ cmp $0, %eax # check if initalization failed
+ lea 8(%esp), %esp
+ je common_exceptionThrown # failed; handle exception
+ movl %eax, %ecx # %ecx<- result
+
+.LOP_SPUT_SHORT_finish:
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG rINST # rINST<- vAA
+ movl rINST, offStaticField_value(%ecx) # field value<- vAA
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+/* continuation for OP_INVOKE_VIRTUAL */
+
+.LOP_INVOKE_VIRTUAL_break:
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ movl %edx, -4(%esp) # save "this" pointer register
+ movl offGlue_method(%eax), %eax # %eax<- glue->method
+ movl $METHOD_VIRTUAL, -8(%esp) # push parameter method type
+ movl %ecx, -12(%esp) # push paramter method index
+ movl offMethod_clazz(%eax), %eax # %eax<- glue->method->clazz
+ lea -16(%esp), %esp
+ movl %eax, (%esp) # push parameter clazz
+ call dvmResolveMethod # call: (const ClassObject* referrer,
+ # u4 methodIdx, MethodType methodType)
+ # return: Method*
+ lea 16(%esp), %esp
+ cmp $0, %eax # check for null method return
+ movl -4(%esp), %edx # get "this" pointer register
+ jne .LOP_INVOKE_VIRTUAL_continue
+ jmp common_exceptionThrown # null pointer; handle exception
+
+ /*
+ * At this point:
+ * %eax = resolved base method
+ * %edx = D or CCCC (index of first arg, which is the "this" ptr)
+ */
+
+.LOP_INVOKE_VIRTUAL_continue:
+ GET_VREG %edx # %edx<- "this" ptr
+ movzwl offMethod_methodIndex(%eax), %eax # %eax<- baseMethod->methodIndex
+ cmp $0, %edx # %edx<- check for null "this"
+ je common_errNullObject # handle null object
+ movl offObject_clazz(%edx), %edx # %edx<- thisPtr->clazz
+ movl offClassObject_vtable(%edx), %edx # %edx<- thisPtr->clazz->vtable
+ movl (%edx, %eax, 4), %ecx # %ecx<- vtable[methodIndex]
+ jmp common_invokeMethodNoRange # invoke method common code
+
+/* continuation for OP_INVOKE_SUPER */
+
+.LOP_INVOKE_SUPER_continue2:
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ movl offGlue_method(%eax), %eax # %eax<- glue->method
+ movl offMethod_clazz(%eax), %eax # %eax<- glue->method->clazz
+ EXPORT_PC # must export for invoke
+ cmp $0, %ecx # check if already resolved
+ jne .LOP_INVOKE_SUPER_continue
+ jmp .LOP_INVOKE_SUPER_resolve # handle resolve
+
+ /*
+ * %ecx = resolved base method
+ * %eax = method->clazz
+ */
+
+.LOP_INVOKE_SUPER_continue:
+ movl offClassObject_super(%eax), %edx # %edx<- glue->method->clazz->super
+ movzwl offMethod_methodIndex(%ecx), %ecx # %ecx<- baseMethod->methodIndex
+ cmp offClassObject_vtableCount(%edx), %ecx # compare vtableCount with methodIndex
+ EXPORT_PC # must export for invoke
+ jnc .LOP_INVOKE_SUPER_nsm # handle method not present
+ movl offClassObject_vtable(%edx), %edx # %edx<- glue->method->clazz->super->vtable
+ movl (%edx, %ecx, 4), %ecx # %ecx<- vtable[methodIndex]
+ jmp common_invokeMethodNoRange # invoke method common code
+
+.LOP_INVOKE_SUPER_resolve:
+ movl %eax, -12(%esp) # push parameter clazz
+ movl %edx, -8(%esp) # push parameter method index
+ movl $METHOD_VIRTUAL, -4(%esp) # push parameter method type
+ lea -12(%esp), %esp
+ call dvmResolveMethod # call: (const ClassObject* referrer,
+ # u4 methodIdx, MethodType methodType)
+ # return: Method*
+ lea 12(%esp), %esp
+ movl %eax, %ecx # %ecx<- method
+ cmp $0, %ecx # check for null method return
+ movl -12(%esp), %eax # %eax<- glue->method->clazz
+ jne .LOP_INVOKE_SUPER_continue
+ jmp common_exceptionThrown # null pointer; handle exception
+
+ /*
+ * Throw a NoSuchMethodError with the method name as the message.
+ * %ecx = resolved base method
+ */
+
+.LOP_INVOKE_SUPER_nsm:
+ movl offMethod_name(%ecx), %edx # %edx<- method name
+ jmp common_errNoSuchMethod
+
+/* continuation for OP_INVOKE_DIRECT */
+
+ /*
+ * %eax = reference (BBBB or CCCC)
+ * -4(%esp) = "this" register
+ */
+
+.LOP_INVOKE_DIRECT_resolve:
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl $METHOD_DIRECT, -8(%esp) # push parameter method type
+ movl offGlue_method(%edx), %edx # %edx<- glue->method
+ movl %eax, -12(%esp) # push parameter reference
+ lea -16(%esp), %esp
+ movl offMethod_clazz(%edx), %edx # %edx<- glue->method->clazz
+ movl %edx, (%esp) # push parameter clazz
+ call dvmResolveMethod # call: (const ClassObject* referrer,
+ # u4 methodIdx, MethodType methodType)
+ # return: Method*
+ lea 16(%esp), %esp
+ cmp $0, %eax # check for null method return
+ movl -4(%esp), %edx # get "this" pointer register
+ GET_VREG %edx # get "this" pointer
+ je common_exceptionThrown # null pointer; handle exception
+ cmp $0, %edx # check for null "this"
+ movl %eax, %ecx # %ecx<- method
+ jne common_invokeMethodNoRange # invoke method common code
+ jmp common_errNullObject # handle null object
+
+/* continuation for OP_INVOKE_STATIC */
+
+.LOP_INVOKE_STATIC_break:
+ movl offGlue_method(%edx), %edx # %edx<- glue->method
+ movl $METHOD_STATIC, -4(%esp) # resolver method type
+ movl %eax, -8(%esp) # push parameter method index
+ movl offMethod_clazz(%edx), %edx # %edx<- glue->method->clazz
+ movl %edx, -12(%esp) # push parameter method
+ lea -12(%esp), %esp
+ call dvmResolveMethod # call: (const ClassObject* referrer,
+ # u4 methodIdx, MethodType methodType)
+ # return: Method*
+ lea 12(%esp), %esp
+ cmp $0, %eax # check for null method
+ je common_exceptionThrown
+ movl %eax, %ecx # %ecx<- method
+ jmp common_invokeMethodNoRange # invoke method common code
+
+/* continuation for OP_INVOKE_INTERFACE */
+.LOP_INVOKE_INTERFACE_break:
+ movl rGLUE, %ecx # %ecx<- pMterpGlue
+ movl offGlue_method(%ecx), %ecx # %ecx<- glue->method
+ movl %ecx, -8(%esp) # push parameter method
+ movl offObject_clazz(%edx), %edx # %edx<- glue->method->clazz
+ movl %edx, -16(%esp) # push parameter
+ lea -16(%esp), %esp
+ call dvmFindInterfaceMethodInCache # call: (ClassObject* thisClass, u4 methodIdx,
+ # const Method* method, DvmDex* methodClassDex)
+ # return: Method*
+ lea 16(%esp), %esp
+ cmp $0, %eax # check if find failed
+ je common_exceptionThrown # handle exception
+ movl %eax, %ecx # %ecx<- method
+ jmp common_invokeMethodNoRange # invoke method common code
+
+/* continuation for OP_INVOKE_VIRTUAL_RANGE */
+
+.LOP_INVOKE_VIRTUAL_RANGE_break:
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ movl %edx, -4(%esp) # save "this" pointer register
+ movl offGlue_method(%eax), %eax # %eax<- glue->method
+ movl $METHOD_VIRTUAL, -8(%esp) # push parameter method type
+ movl %ecx, -12(%esp) # push paramter method index
+ movl offMethod_clazz(%eax), %eax # %eax<- glue->method->clazz
+ lea -16(%esp), %esp
+ movl %eax, (%esp) # push parameter clazz
+ call dvmResolveMethod # call: (const ClassObject* referrer,
+ # u4 methodIdx, MethodType methodType)
+ # return: Method*
+ lea 16(%esp), %esp
+ cmp $0, %eax # check for null method return
+ movl -4(%esp), %edx # get "this" pointer register
+ jne .LOP_INVOKE_VIRTUAL_RANGE_continue
+ jmp common_exceptionThrown # null pointer; handle exception
+
+ /*
+ * At this point:
+ * %eax = resolved base method
+ * %edx = D or CCCC (index of first arg, which is the "this" ptr)
+ */
+
+.LOP_INVOKE_VIRTUAL_RANGE_continue:
+ GET_VREG %edx # %edx<- "this" ptr
+ movzwl offMethod_methodIndex(%eax), %eax # %eax<- baseMethod->methodIndex
+ cmp $0, %edx # %edx<- check for null "this"
+ je common_errNullObject # handle null object
+ movl offObject_clazz(%edx), %edx # %edx<- thisPtr->clazz
+ movl offClassObject_vtable(%edx), %edx # %edx<- thisPtr->clazz->vtable
+ movl (%edx, %eax, 4), %ecx # %ecx<- vtable[methodIndex]
+ jmp common_invokeMethodRange # invoke method common code
+
+/* continuation for OP_INVOKE_SUPER_RANGE */
+
+.LOP_INVOKE_SUPER_RANGE_continue2:
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ movl offGlue_method(%eax), %eax # %eax<- glue->method
+ movl offMethod_clazz(%eax), %eax # %eax<- glue->method->clazz
+ EXPORT_PC # must export for invoke
+ cmp $0, %ecx # check if already resolved
+ jne .LOP_INVOKE_SUPER_RANGE_continue
+ jmp .LOP_INVOKE_SUPER_RANGE_resolve # handle resolve
+
+ /*
+ * %ecx = resolved base method
+ * %eax = method->clazz
+ */
+
+.LOP_INVOKE_SUPER_RANGE_continue:
+ movl offClassObject_super(%eax), %edx # %edx<- glue->method->clazz->super
+ movzwl offMethod_methodIndex(%ecx), %ecx # %ecx<- baseMethod->methodIndex
+ cmp offClassObject_vtableCount(%edx), %ecx # compare vtableCount with methodIndex
+ EXPORT_PC # must export for invoke
+ jnc .LOP_INVOKE_SUPER_RANGE_nsm # handle method not present
+ movl offClassObject_vtable(%edx), %edx # %edx<- glue->method->clazz->super->vtable
+ movl (%edx, %ecx, 4), %ecx # %ecx<- vtable[methodIndex]
+ jmp common_invokeMethodRange # invoke method common code
+
+.LOP_INVOKE_SUPER_RANGE_resolve:
+ movl %eax, -12(%esp) # push parameter clazz
+ movl %edx, -8(%esp) # push parameter method index
+ movl $METHOD_VIRTUAL, -4(%esp) # push parameter method type
+ lea -12(%esp), %esp
+ call dvmResolveMethod # call: (const ClassObject* referrer,
+ # u4 methodIdx, MethodType methodType)
+ # return: Method*
+ lea 12(%esp), %esp
+ movl %eax, %ecx # %ecx<- method
+ cmp $0, %ecx # check for null method return
+ movl -12(%esp), %eax # %eax<- glue->method->clazz
+ jne .LOP_INVOKE_SUPER_RANGE_continue
+ jmp common_exceptionThrown # null pointer; handle exception
+
+ /*
+ * Throw a NoSuchMethodError with the method name as the message.
+ * %ecx = resolved base method
+ */
+
+.LOP_INVOKE_SUPER_RANGE_nsm:
+ movl offMethod_name(%ecx), %edx # %edx<- method name
+ jmp common_errNoSuchMethod
+
+/* continuation for OP_INVOKE_DIRECT_RANGE */
+
+ /*
+ * %eax = reference (BBBB or CCCC)
+ * -4(%esp) = "this" register
+ */
+
+.LOP_INVOKE_DIRECT_RANGE_resolve:
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl $METHOD_DIRECT, -8(%esp) # push parameter method type
+ movl offGlue_method(%edx), %edx # %edx<- glue->method
+ movl %eax, -12(%esp) # push parameter reference
+ lea -16(%esp), %esp
+ movl offMethod_clazz(%edx), %edx # %edx<- glue->method->clazz
+ movl %edx, (%esp) # push parameter clazz
+ call dvmResolveMethod # call: (const ClassObject* referrer,
+ # u4 methodIdx, MethodType methodType)
+ # return: Method*
+ lea 16(%esp), %esp
+ cmp $0, %eax # check for null method return
+ movl -4(%esp), %edx # get "this" pointer register
+ GET_VREG %edx # get "this" pointer
+ je common_exceptionThrown # null pointer; handle exception
+ cmp $0, %edx # check for null "this"
+ movl %eax, %ecx # %ecx<- method
+ jne common_invokeMethodRange # invoke method common code
+ jmp common_errNullObject # handle null object
+
+/* continuation for OP_INVOKE_STATIC_RANGE */
+
+.LOP_INVOKE_STATIC_RANGE_break:
+ movl offGlue_method(%edx), %edx # %edx<- glue->method
+ movl $METHOD_STATIC, -4(%esp) # resolver method type
+ movl %eax, -8(%esp) # push parameter method index
+ movl offMethod_clazz(%edx), %edx # %edx<- glue->method->clazz
+ movl %edx, -12(%esp) # push parameter method
+ lea -12(%esp), %esp
+ call dvmResolveMethod # call: (const ClassObject* referrer,
+ # u4 methodIdx, MethodType methodType)
+ # return: Method*
+ lea 12(%esp), %esp
+ cmp $0, %eax # check for null method
+ je common_exceptionThrown
+ movl %eax, %ecx # %ecx<- method
+ jmp common_invokeMethodRange # invoke method common code
+
+/* continuation for OP_INVOKE_INTERFACE_RANGE */
+.LOP_INVOKE_INTERFACE_RANGE_break:
+ movl rGLUE, %ecx # %ecx<- pMterpGlue
+ movl offGlue_method(%ecx), %ecx # %ecx<- glue->method
+ movl %ecx, -8(%esp) # push parameter method
+ movl offObject_clazz(%edx), %edx # %edx<- glue->method->clazz
+ movl %edx, -16(%esp) # push parameter
+ lea -16(%esp), %esp
+ call dvmFindInterfaceMethodInCache # call: (ClassObject* thisClass, u4 methodIdx,
+ # const Method* method, DvmDex* methodClassDex)
+ # return: Method*
+ lea 16(%esp), %esp
+ cmp $0, %eax # check if find failed
+ je common_exceptionThrown # handle exception
+ movl %eax, %ecx # %ecx<- method
+ jmp common_invokeMethodRange # invoke method common code
+
+/* continuation for OP_FLOAT_TO_INT */
+
+.LOP_FLOAT_TO_INT_break:
+ fnstcw -2(%esp) # save control word
+ orl $0xc00, -2(%esp) # reset control
+ fldcw -2(%esp) # load control word
+ xorl $0xc00, -2(%esp) # reset control
+ fistpl (rFP, %edx, 4) # move converted int
+ fldcw -2(%esp) # load saved control word
+ FINISH 1 # jump to next instruction
+
+.LOP_FLOAT_TO_INT_nanInf:
+ jnp .LOP_FLOAT_TO_INT_posInf # handle posInf
+ fstps (rFP, %edx, 4) # pop floating point stack
+ movl $0x00000000, (rFP, %edx, 4) # vA<- NaN
+ FINISH 1 # jump to next instruction
+
+.LOP_FLOAT_TO_INT_posInf:
+ fstps (rFP, %edx, 4) # pop floating point stack
+ movl $0x7FFFFFFF, (rFP, %edx, 4) # vA<- posInf
+ FINISH 1 # jump to next instruction
+
+.LOP_FLOAT_TO_INT_negInf:
+ fstps (rFP, %edx, 4) # pop floating point stack
+ fstps (rFP, %edx, 4) # pop floating point stack
+ movl $0x80000000, (rFP, %edx, 4) # vA<- negInf
+ FINISH 1 # jump to next instruction
+
+/* continuation for OP_FLOAT_TO_LONG */
+
+.LOP_FLOAT_TO_LONG_break:
+ fnstcw -2(%esp) # save control word
+ orl $0xc00, -2(%esp) # update control
+ fldcw -2(%esp) # load control word
+ xorl $0xc00, -2(%esp) # reset control
+ fistpll (rFP, %edx, 4) # move converted int
+ fldcw -2(%esp) # load saved control word
+ FINISH 1 # jump to next instruction
+
+.LOP_FLOAT_TO_LONG_nanInf:
+ jnp .LOP_FLOAT_TO_LONG_posInf
+ fstpl (rFP, %edx, 4) # move converted int
+ movq .LvalueNanLong, %xmm0 # %xmm0<- NaN
+ movq %xmm0, (rFP, %edx, 4) # vA<- %xmm0; NaN
+ FINISH 1 # jump to next instruction
+
+.LOP_FLOAT_TO_LONG_posInf:
+ fstpl (rFP, %edx, 4) # move converted int
+ movq .LvaluePosInfLong, %xmm0 # %xmm0<- posInf
+ movq %xmm0, (rFP, %edx, 4) # vA<- %xmm0; posInf
+ FINISH 1 # jump to next instruction
+
+.LOP_FLOAT_TO_LONG_negInf:
+ fstpl (rFP, %edx, 4) # move converted int
+ movq .LvalueNegInfLong, %xmm0 # %xmm0<- negInf
+ fstpl (rFP, %edx, 4) # move converted int
+ movq %xmm0, (rFP, %edx, 4) # vA<- %xmm0; negInf
+ FINISH 1 # jump to next instruction
+
+/* continuation for OP_DOUBLE_TO_INT */
+
+.LOP_DOUBLE_TO_INT_break:
+ fnstcw -2(%esp) # save control word
+ orl $0xc00, -2(%esp) # reset control
+ fldcw -2(%esp) # load control word
+ xorl $0xc00, -2(%esp) # reset control
+ fistpl (rFP, %edx, 4) # move converted int
+ fldcw -2(%esp) # load saved control word
+ FINISH 1 # jump to next instruction
+
+.LOP_DOUBLE_TO_INT_nanInf:
+ jnp .LOP_DOUBLE_TO_INT_posInf
+ fstps (rFP, %edx, 4)
+ movl $0x00000000, (rFP, %edx, 4) # vA<- NaN
+ FINISH 1 # jump to next instruction
+
+.LOP_DOUBLE_TO_INT_posInf:
+ fstps (rFP, %edx, 4)
+ movl $0x7FFFFFFF, (rFP, %edx, 4) # vA<- posInf
+ FINISH 1 # jump to next instruction
+
+.LOP_DOUBLE_TO_INT_negInf:
+ fstps (rFP, %edx, 4)
+ fstps (rFP, %edx, 4)
+ movl $0x80000000, (rFP, %edx, 4) # vA<- negInf
+ FINISH 1 # jump to next instruction
+
+/* continuation for OP_DOUBLE_TO_LONG */
+
+.LOP_DOUBLE_TO_LONG_break:
+ fnstcw -2(%esp) # save control word
+ orl $0xc00, -2(%esp) # reset control
+ fldcw -2(%esp) # load control word
+ xorl $0xc00, -2(%esp) # reset control
+ fistpll (rFP, %edx, 4) # move converted int
+ fldcw -2(%esp) # load saved control word
+ FINISH 1 # jump to next instruction
+
+.LOP_DOUBLE_TO_LONG_nanInf:
+ jnp .LOP_DOUBLE_TO_LONG_posInf
+ fstpl (rFP, %edx, 4) # move converted int
+ movq .LvalueNanLong, %xmm0 # %xmm0<- NaN
+ movq %xmm0, (rFP, %edx, 4) # vA<- %xmm0; NaN
+ FINISH 1 # jump to next instruction
+
+.LOP_DOUBLE_TO_LONG_posInf:
+ fstpl (rFP, %edx, 4) # move converted int
+ movq .LvaluePosInfLong, %xmm0 # %xmm0<- posInf
+ movq %xmm0, (rFP, %edx, 4) # vA<- %xmm0; posInf
+ FINISH 1 # jump to next instruction
+
+.LOP_DOUBLE_TO_LONG_negInf:
+ fstpl (rFP, %edx, 4) # move converted int
+ movq .LvalueNegInfLong, %xmm0 # %xmm0<- negInf
+ fstpl (rFP, %edx, 4) # move converted int
+ movq %xmm0, (rFP, %edx, 4) # vA<- %xmm0; negInf
+ FINISH 1 # jump to next instruction
+
+/* continuation for OP_DIV_INT */
+.LOP_DIV_INT_break:
+ .if 1
+ movl $0x80000000, (rFP, rINST, 4) # vAA<- min int
+ .else
+ movl $0, (rFP, rINST, 4) # vAA<- 0
+ .endif
+.LOP_DIV_INT_break2:
+ FINISH 2 # jump to next instruction
+
+/* continuation for OP_REM_INT */
+.LOP_REM_INT_break:
+ .if 0
+ movl $0x80000000, (rFP, rINST, 4) # vAA<- min int
+ .else
+ movl $0, (rFP, rINST, 4) # vAA<- 0
+ .endif
+.LOP_REM_INT_break2:
+ FINISH 2 # jump to next instruction
+
+/* continuation for OP_MUL_LONG */
+
+ /*
+ * X = (rFP, rINST, 4)
+ * W = 4(rFP, rINST, 4)
+ * Z = (rFP, %edx, 4)
+ * Y = 4(rFP, %edx, 4)
+ */
+
+.LOP_MUL_LONG_finish:
+ movl 4(rFP, rINST, 4), %ecx # %ecx<- W
+ imull (rFP, %edx, 4), %ecx # %ecx<- WxZ
+ mov 4(rFP, %edx, 4), %eax # %ecx<- Y
+ imull (rFP, rINST, 4), %eax # %eax<- XxY
+ addl %eax, %ecx # %ecx<- (WZ + XY)
+ movl (rFP, %edx, 4), %eax # %eax<- Z
+ mull (rFP, rINST, 4) # %edx:eax<- XZ
+ movzbl -4(%esp), rINST # rINST<- AA
+ addl %edx, %ecx # %ecx<- carry + (WZ + XY)
+ movl %ecx, 4(rFP, rINST, 4) # vAA+1<- results hi
+ movl %eax, (rFP, rINST, 4) # vAA<- results lo
+ FINISH 2 # jump to next instruction
+
+/* continuation for OP_DIV_LONG */
+.LOP_DIV_LONG_finish:
+ movq %xmm0, -16(%esp) # push arg vBB,vBB+1
+ lea -16(%esp), %esp
+ call __divdi3 # call func
+ lea 16(%esp), %esp
+ movl %eax, (rFP, rINST, 4) # vAA<- return low
+ movl %edx, 4(rFP, rINST, 4) # vAA+1<- return high
+ FINISH 2 # jump to next instruction
+
+/* continuation for OP_REM_LONG */
+.LOP_REM_LONG_finish:
+ movq %xmm0, -16(%esp) # push arg vBB,vBB+1
+ lea -16(%esp), %esp
+ call __moddi3 # call func
+ lea 16(%esp), %esp
+ movl %eax, (rFP, rINST, 4) # vAA<- return low
+ movl %edx, 4(rFP, rINST, 4) # vAA+1<- return high
+ FINISH 2 # jump to next instruction
+
+/* continuation for OP_SHR_LONG */
+
+.LOP_SHR_LONG_finish:
+ movq .Lvalue64, %xmm3 # %xmm3<- 64
+ psubq %xmm0, %xmm3 # %xmm3<- 64 - shift amount
+ movq .L64bits, %xmm4 # %xmm4<- lower 64 bits set
+ psllq %xmm3, %xmm4 # %xmm4<- correct mask for sign bits
+ por %xmm4, %xmm1 # %xmm1<- signed and shifted vBB
+
+.LOP_SHR_LONG_final:
+ movq %xmm1, (rFP, rINST, 4) # vAA<- shifted vBB
+ FINISH 2 # jump to next instruction
+
+/* continuation for OP_REM_DOUBLE */
+
+.LOP_REM_DOUBLE_break:
+ call fmod # call: (long double x, long double y)
+ # return: double
+ lea 16(%esp), %esp
+ fstpl (rFP, rINST, 4) # vAA<- remainder; return of fmod
+ FINISH 2 # jump to next instruction
+
+/* continuation for OP_DIV_INT_2ADDR */
+.LOP_DIV_INT_2ADDR_break:
+ .if 1
+ movl $0x80000000, (rFP, rINST, 4) # vAA<- min int
+ .else
+ movl $0, (rFP, rINST, 4) # vAA<- 0
+ .endif
+.LOP_DIV_INT_2ADDR_break2:
+ FFETCH_ADV 1, %edx # %ecx<- next instruction hi; fetch, advance
+ FGETOP_JMP 1, %edx # jump to next instruction; getop, jmp
+
+
+/* continuation for OP_REM_INT_2ADDR */
+.LOP_REM_INT_2ADDR_break:
+ .if 0
+ movl $0x80000000, (rFP, rINST, 4) # vAA<- min int
+ .else
+ movl $0, (rFP, rINST, 4) # vAA<- 0
+ .endif
+.LOP_REM_INT_2ADDR_break2:
+ FFETCH_ADV 1, %edx # %ecx<- next instruction hi; fetch, advance
+ FGETOP_JMP 1, %edx # jump to next instruction; getop, jmp
+
+
+/* continuation for OP_MUL_LONG_2ADDR */
+
+ /*
+ * X = (rFP, rINST, 4)
+ * W = 4(rFP, rINST, 4)
+ * Z = (rFP, %edx, 4)
+ * Y = 4(rFP, %edx, 4)
+ */
+
+.LOP_MUL_LONG_2ADDR_finish:
+ movl 4(rFP, rINST, 4), %ecx # %ecx<- W
+ imull (rFP, %edx, 4), %ecx # %ecx<- WxZ
+ movl 4(rFP, %edx, 4), %eax # %eax<- Y
+ imull (rFP, rINST, 4), %eax # %eax<- X*Y
+ addl %eax, %ecx # %ecx<- (WZ + XY)
+ movl (rFP, %edx, 4), %eax # %eax<- Z
+ mull (rFP, rINST, 4) # %edx:eax<- XZ
+ addl %edx, %ecx # %ecx<- carry + (WZ + XY)
+ movl sReg0, %edx # %edx<- A
+ movl %ecx, 4(rFP, %edx, 4) # vA+1<- results hi
+ movl %eax, (rFP, %edx, 4) # vA<- results lo
+ FINISH 1 # jump to next instruction
+
+/* continuation for OP_DIV_LONG_2ADDR */
+.LOP_DIV_LONG_2ADDR_break:
+ movq %xmm0, -20(%esp) # push arg vA, vA+1
+ lea -20(%esp), %esp
+ call __divdi3 # call func
+ lea 20(%esp), %esp
+ movl %eax, (rFP, rINST, 4) # vA<- return low
+ movl %edx, 4(rFP, rINST, 4) # vA<- return high
+ FFETCH_ADV 1, %ecx # %ecx<- next instruction hi; fetch, advance
+ FGETOP_JMP 1, %ecx # jump to next instruction; getop, jmp
+
+/* continuation for OP_REM_LONG_2ADDR */
+.LOP_REM_LONG_2ADDR_break:
+ movq %xmm0, -20(%esp) # push arg vA, vA+1
+ lea -20(%esp), %esp
+ call __moddi3 # call func
+ lea 20(%esp), %esp
+ movl %eax, (rFP, rINST, 4) # vA<- return low
+ movl %edx, 4(rFP, rINST, 4) # vA<- return high
+ FFETCH_ADV 1, %ecx # %ecx<- next instruction hi; fetch, advance
+ FGETOP_JMP 1, %ecx # jump to next instruction; getop, jmp
+
+/* continuation for OP_SHR_LONG_2ADDR */
+
+.LOP_SHR_LONG_2ADDR_finish:
+ movq .Lvalue64, %xmm3 # %xmm3<- 64
+ psubq %xmm0, %xmm3 # %xmm3<- 64 - shift amount
+ movq .L64bits, %xmm4 # %xmm4<- lower 64 bits set
+ psllq %xmm3, %xmm4 # %xmm4<- correct mask for sign bits
+ por %xmm4, %xmm1 # %xmm1<- signed and shifted vBB
+
+.LOP_SHR_LONG_2ADDR_final:
+ movq %xmm1, (rFP, rINST, 4) # vAA<- shifted vBB
+ FINISH 1 # jump to next instruction
+
+/* continuation for OP_REM_DOUBLE_2ADDR */
+
+.LOP_REM_DOUBLE_2ADDR_break:
+ call fmod # call: (long double x, long double y)
+ # return: double
+ lea 20(%esp), %esp
+ fstpl (rFP, rINST, 4) # vAA<- remainder; return of fmod
+ FINISH 1 # jump to next instruction
+
+/* continuation for OP_DIV_INT_LIT16 */
+.LOP_DIV_INT_LIT16_break:
+ .if 1
+ movl $0x80000000, (rFP, rINST, 4) # vAA<- min int
+ .else
+ movl $0, (rFP, rINST, 4) # vAA<- 0
+ .endif
+.LOP_DIV_INT_LIT16_break2:
+
+ FINISH 2 # jump to next instruction
+
+/* continuation for OP_REM_INT_LIT16 */
+.LOP_REM_INT_LIT16_break:
+ .if 0
+ movl $0x80000000, (rFP, rINST, 4) # vAA<- min int
+ .else
+ movl $0, (rFP, rINST, 4) # vAA<- 0
+ .endif
+.LOP_REM_INT_LIT16_break2:
+
+ FINISH 2 # jump to next instruction
+
+/* continuation for OP_DIV_INT_LIT8 */
+.LOP_DIV_INT_LIT8_break:
+ .if 1
+ movl $0x80000000, (rFP, rINST, 4) # vAA<- min int
+ .else
+ movl $0, (rFP, rINST, 4) # vAA<- 0
+ .endif
+
+.LOP_DIV_INT_LIT8_break2:
+ FINISH 2 # jump to next instruction
+ #FGETOP_JMP 2, %ecx # jump to next instruction; getop, jmp
+
+/* continuation for OP_REM_INT_LIT8 */
+.LOP_REM_INT_LIT8_break:
+ .if 0
+ movl $0x80000000, (rFP, rINST, 4) # vAA<- min int
+ .else
+ movl $0, (rFP, rINST, 4) # vAA<- 0
+ .endif
+
+.LOP_REM_INT_LIT8_break2:
+ FINISH 2 # jump to next instruction
+ #FGETOP_JMP 2, %ecx # jump to next instruction; getop, jmp
+
+/* continuation for OP_EXECUTE_INLINE */
+
+ /*
+ * Extract args, call function.
+ * rINST = #of args (0-4)
+ * %ecx = call index
+ */
+
+.LOP_EXECUTE_INLINE_continue:
+ FETCH 2, %edx # %edx<- FEDC
+ cmp $1, rINST # determine number of arguments
+ jl 0f # handle zero args
+ je 1f # handle one arg
+ cmp $3, rINST
+ jl 2f # handle two args
+ je 3f # handle three args
+4:
+ movl %edx, rINST # rINST<- FEDC
+ and $0xf000, rINST # isolate F
+ shr $10, rINST
+ movl (rFP, rINST), rINST # rINST<- vF
+ movl rINST, 12(%esp) # push parameter vF
+3:
+ movl %edx, rINST # rINST<- FEDC
+ and $0x0f00, rINST # isolate E
+ shr $6, rINST
+ movl (rFP, rINST), rINST # rINST<- vE
+ movl rINST, 8(%esp) # push parameter E
+2:
+ movl %edx, rINST # rINST<- FEDC
+ and $0x00f0, rINST # isolate D
+ shr $2, rINST
+ movl (rFP, rINST), rINST # rINST<- vD
+ movl rINST, 4(%esp) # push parameter D
+1:
+ and $0x000f, %edx # isolate C
+ movl (rFP, %edx, 4), %edx # rINST<- vC
+ movl %edx, (%esp) # push parameter C
+0:
+ shl $4, %ecx
+ movl $gDvmInlineOpsTable, %eax # %eax<- address for table of inline operations
+ call *(%eax, %ecx) # call function
+
+ cmp $0, %eax # check boolean result of inline
+ FFETCH_ADV 3, %eax # %eax<- next instruction hi; fetch, advance
+ lea 24(%esp), %esp # update stack pointer
+ je common_exceptionThrown # handle exception
+ FGETOP_JMP 3, %eax # jump to next instruction; getop, jmp
+
+ .size dvmAsmSisterStart, .-dvmAsmSisterStart
+ .global dvmAsmSisterEnd
+dvmAsmSisterEnd:
+
+/* File: x86-atom/entry.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: entry.S
+ */
+
+#define ASSIST_DEBUGGER 1
+ .text
+ .align 2
+ .global dvmMterpStdRun
+ .type dvmMterpStdRun, %function
+
+ /*
+ * Save registers, initialize sp and fp.
+ * On entry:
+ * bool MterpGlue(glue *)
+ */
+
+ .macro MTERP_ENTRY
+ movl 4(%esp), %ecx # get first argument
+ movl %ebp, -4(%esp) # save caller base pointer
+ movl %ebx, -8(%esp) # save %ebx
+ movl %esi, -12(%esp) # save %esi
+ movl %edi, -16(%esp) # save %edi
+ lea -40(%esp), %ebp # set callee base pointer
+ lea -40(%esp), %esp # set callee stack pointer
+ .endm
+
+ /*
+ * Restore registers.
+ * This function returns a boolean "changeInterp" value.
+ * The return value is from dvmMterpStdBail().
+ */
+
+ .macro MTERP_EXIT
+ lea 40(%esp), %esp # correct stack pointer
+ movl -16(%esp), %edi # restore %edi
+ movl -12(%esp), %esi # restore %esi
+ movl -8(%esp), %ebx # restore %ebx
+ movl -4(%esp), %ebp # restore caller base pointer
+ ret # return
+ .endm
+
+ /*
+ * DvmMterpStdRun entry point: save stack pointer, setup memory locations, get
+ * entry point, start executing instructions.
+ */
+
+dvmMterpStdRun:
+ MTERP_ENTRY
+ movl %ecx, rGLUE # save value for pMterpGlue
+ movl offGlue_pc(%ecx), rPC # get program counter
+ cmp $kInterpEntryInstr, offGlue_entryPoint(%ecx) # check instruction
+ movl offGlue_fp(%ecx), rFP # get frame pointer
+ movl %esp, offGlue_bailPtr(%ecx) # save SP for eventual return
+ FFETCH %edx # %edx<- opcode
+ jne .Lnot_instr # no, handle it
+ FGETOP_JMPa %edx # start executing the instruction at rPC
+
+ /*
+ * Not an instruction. Are we returning from a method?
+ */
+
+.Lnot_instr:
+ cmpl $kInterpEntryReturn, offGlue_entryPoint(%ecx)
+ je common_returnFromMethod
+
+ /*
+ * No, are we throwing an exception?
+ */
+
+.Lnot_return:
+ cmpl $kInterpEntryThrow, offGlue_entryPoint(%ecx)
+ je common_exceptionThrown
+
+ /*
+ * No, then we must abort.
+ */
+
+.Lbad_arg:
+ pushl offGlue_entryPoint(%ecx)
+ movl $.LstrBadEntryPoint, -4(%esp)
+ lea -4(%esp), %esp
+ call printf
+ lea 8(%esp), %esp
+ call dvmAbort # call (void)
+
+ /*
+ * Restore the stack pointer and PC from the save point established on entry and
+ * return to whoever called dvmMterpStdRun.
+ *
+ * On entry:
+ * 4(%esp) MterpGlue* glue
+ * 8(%esp) bool changeInterp
+ */
+
+ .global dvmMterpStdBail
+ .type dvmMterpStdBail, %function
+
+dvmMterpStdBail:
+ movl 4(%esp), %ecx # get first argument
+ movl 8(%esp), %eax # get second argument
+ movl offGlue_bailPtr(%ecx), %esp # sp <- saved SP
+ MTERP_EXIT
+
+ /*
+ * String references.
+ */
+
+.LstrBadEntryPoint:
+ .asciz "Bad entry point %d\n"
+
+
+dvmAsmInstructionJmpTable = .LdvmAsmInstructionJmpTable
+.LdvmAsmInstructionJmpTable:
+.long .L_OP_NOP
+.long .L_OP_MOVE
+.long .L_OP_MOVE_FROM16
+.long .L_OP_MOVE_16
+.long .L_OP_MOVE_WIDE
+.long .L_OP_MOVE_WIDE_FROM16
+.long .L_OP_MOVE_WIDE_16
+.long .L_OP_MOVE_OBJECT
+.long .L_OP_MOVE_OBJECT_FROM16
+.long .L_OP_MOVE_OBJECT_16
+.long .L_OP_MOVE_RESULT
+.long .L_OP_MOVE_RESULT_WIDE
+.long .L_OP_MOVE_RESULT_OBJECT
+.long .L_OP_MOVE_EXCEPTION
+.long .L_OP_RETURN_VOID
+.long .L_OP_RETURN
+.long .L_OP_RETURN_WIDE
+.long .L_OP_RETURN_OBJECT
+.long .L_OP_CONST_4
+.long .L_OP_CONST_16
+.long .L_OP_CONST
+.long .L_OP_CONST_HIGH16
+.long .L_OP_CONST_WIDE_16
+.long .L_OP_CONST_WIDE_32
+.long .L_OP_CONST_WIDE
+.long .L_OP_CONST_WIDE_HIGH16
+.long .L_OP_CONST_STRING
+.long .L_OP_CONST_STRING_JUMBO
+.long .L_OP_CONST_CLASS
+.long .L_OP_MONITOR_ENTER
+.long .L_OP_MONITOR_EXIT
+.long .L_OP_CHECK_CAST
+.long .L_OP_INSTANCE_OF
+.long .L_OP_ARRAY_LENGTH
+.long .L_OP_NEW_INSTANCE
+.long .L_OP_NEW_ARRAY
+.long .L_OP_FILLED_NEW_ARRAY
+.long .L_OP_FILLED_NEW_ARRAY_RANGE
+.long .L_OP_FILL_ARRAY_DATA
+.long .L_OP_THROW
+.long .L_OP_GOTO
+.long .L_OP_GOTO_16
+.long .L_OP_GOTO_32
+.long .L_OP_PACKED_SWITCH
+.long .L_OP_SPARSE_SWITCH
+.long .L_OP_CMPL_FLOAT
+.long .L_OP_CMPG_FLOAT
+.long .L_OP_CMPL_DOUBLE
+.long .L_OP_CMPG_DOUBLE
+.long .L_OP_CMP_LONG
+.long .L_OP_IF_EQ
+.long .L_OP_IF_NE
+.long .L_OP_IF_LT
+.long .L_OP_IF_GE
+.long .L_OP_IF_GT
+.long .L_OP_IF_LE
+.long .L_OP_IF_EQZ
+.long .L_OP_IF_NEZ
+.long .L_OP_IF_LTZ
+.long .L_OP_IF_GEZ
+.long .L_OP_IF_GTZ
+.long .L_OP_IF_LEZ
+.long .L_OP_UNUSED_3E
+.long .L_OP_UNUSED_3F
+.long .L_OP_UNUSED_40
+.long .L_OP_UNUSED_41
+.long .L_OP_UNUSED_42
+.long .L_OP_UNUSED_43
+.long .L_OP_AGET
+.long .L_OP_AGET_WIDE
+.long .L_OP_AGET_OBJECT
+.long .L_OP_AGET_BOOLEAN
+.long .L_OP_AGET_BYTE
+.long .L_OP_AGET_CHAR
+.long .L_OP_AGET_SHORT
+.long .L_OP_APUT
+.long .L_OP_APUT_WIDE
+.long .L_OP_APUT_OBJECT
+.long .L_OP_APUT_BOOLEAN
+.long .L_OP_APUT_BYTE
+.long .L_OP_APUT_CHAR
+.long .L_OP_APUT_SHORT
+.long .L_OP_IGET
+.long .L_OP_IGET_WIDE
+.long .L_OP_IGET_OBJECT
+.long .L_OP_IGET_BOOLEAN
+.long .L_OP_IGET_BYTE
+.long .L_OP_IGET_CHAR
+.long .L_OP_IGET_SHORT
+.long .L_OP_IPUT
+.long .L_OP_IPUT_WIDE
+.long .L_OP_IPUT_OBJECT
+.long .L_OP_IPUT_BOOLEAN
+.long .L_OP_IPUT_BYTE
+.long .L_OP_IPUT_CHAR
+.long .L_OP_IPUT_SHORT
+.long .L_OP_SGET
+.long .L_OP_SGET_WIDE
+.long .L_OP_SGET_OBJECT
+.long .L_OP_SGET_BOOLEAN
+.long .L_OP_SGET_BYTE
+.long .L_OP_SGET_CHAR
+.long .L_OP_SGET_SHORT
+.long .L_OP_SPUT
+.long .L_OP_SPUT_WIDE
+.long .L_OP_SPUT_OBJECT
+.long .L_OP_SPUT_BOOLEAN
+.long .L_OP_SPUT_BYTE
+.long .L_OP_SPUT_CHAR
+.long .L_OP_SPUT_SHORT
+.long .L_OP_INVOKE_VIRTUAL
+.long .L_OP_INVOKE_SUPER
+.long .L_OP_INVOKE_DIRECT
+.long .L_OP_INVOKE_STATIC
+.long .L_OP_INVOKE_INTERFACE
+.long .L_OP_UNUSED_73
+.long .L_OP_INVOKE_VIRTUAL_RANGE
+.long .L_OP_INVOKE_SUPER_RANGE
+.long .L_OP_INVOKE_DIRECT_RANGE
+.long .L_OP_INVOKE_STATIC_RANGE
+.long .L_OP_INVOKE_INTERFACE_RANGE
+.long .L_OP_UNUSED_79
+.long .L_OP_UNUSED_7A
+.long .L_OP_NEG_INT
+.long .L_OP_NOT_INT
+.long .L_OP_NEG_LONG
+.long .L_OP_NOT_LONG
+.long .L_OP_NEG_FLOAT
+.long .L_OP_NEG_DOUBLE
+.long .L_OP_INT_TO_LONG
+.long .L_OP_INT_TO_FLOAT
+.long .L_OP_INT_TO_DOUBLE
+.long .L_OP_LONG_TO_INT
+.long .L_OP_LONG_TO_FLOAT
+.long .L_OP_LONG_TO_DOUBLE
+.long .L_OP_FLOAT_TO_INT
+.long .L_OP_FLOAT_TO_LONG
+.long .L_OP_FLOAT_TO_DOUBLE
+.long .L_OP_DOUBLE_TO_INT
+.long .L_OP_DOUBLE_TO_LONG
+.long .L_OP_DOUBLE_TO_FLOAT
+.long .L_OP_INT_TO_BYTE
+.long .L_OP_INT_TO_CHAR
+.long .L_OP_INT_TO_SHORT
+.long .L_OP_ADD_INT
+.long .L_OP_SUB_INT
+.long .L_OP_MUL_INT
+.long .L_OP_DIV_INT
+.long .L_OP_REM_INT
+.long .L_OP_AND_INT
+.long .L_OP_OR_INT
+.long .L_OP_XOR_INT
+.long .L_OP_SHL_INT
+.long .L_OP_SHR_INT
+.long .L_OP_USHR_INT
+.long .L_OP_ADD_LONG
+.long .L_OP_SUB_LONG
+.long .L_OP_MUL_LONG
+.long .L_OP_DIV_LONG
+.long .L_OP_REM_LONG
+.long .L_OP_AND_LONG
+.long .L_OP_OR_LONG
+.long .L_OP_XOR_LONG
+.long .L_OP_SHL_LONG
+.long .L_OP_SHR_LONG
+.long .L_OP_USHR_LONG
+.long .L_OP_ADD_FLOAT
+.long .L_OP_SUB_FLOAT
+.long .L_OP_MUL_FLOAT
+.long .L_OP_DIV_FLOAT
+.long .L_OP_REM_FLOAT
+.long .L_OP_ADD_DOUBLE
+.long .L_OP_SUB_DOUBLE
+.long .L_OP_MUL_DOUBLE
+.long .L_OP_DIV_DOUBLE
+.long .L_OP_REM_DOUBLE
+.long .L_OP_ADD_INT_2ADDR
+.long .L_OP_SUB_INT_2ADDR
+.long .L_OP_MUL_INT_2ADDR
+.long .L_OP_DIV_INT_2ADDR
+.long .L_OP_REM_INT_2ADDR
+.long .L_OP_AND_INT_2ADDR
+.long .L_OP_OR_INT_2ADDR
+.long .L_OP_XOR_INT_2ADDR
+.long .L_OP_SHL_INT_2ADDR
+.long .L_OP_SHR_INT_2ADDR
+.long .L_OP_USHR_INT_2ADDR
+.long .L_OP_ADD_LONG_2ADDR
+.long .L_OP_SUB_LONG_2ADDR
+.long .L_OP_MUL_LONG_2ADDR
+.long .L_OP_DIV_LONG_2ADDR
+.long .L_OP_REM_LONG_2ADDR
+.long .L_OP_AND_LONG_2ADDR
+.long .L_OP_OR_LONG_2ADDR
+.long .L_OP_XOR_LONG_2ADDR
+.long .L_OP_SHL_LONG_2ADDR
+.long .L_OP_SHR_LONG_2ADDR
+.long .L_OP_USHR_LONG_2ADDR
+.long .L_OP_ADD_FLOAT_2ADDR
+.long .L_OP_SUB_FLOAT_2ADDR
+.long .L_OP_MUL_FLOAT_2ADDR
+.long .L_OP_DIV_FLOAT_2ADDR
+.long .L_OP_REM_FLOAT_2ADDR
+.long .L_OP_ADD_DOUBLE_2ADDR
+.long .L_OP_SUB_DOUBLE_2ADDR
+.long .L_OP_MUL_DOUBLE_2ADDR
+.long .L_OP_DIV_DOUBLE_2ADDR
+.long .L_OP_REM_DOUBLE_2ADDR
+.long .L_OP_ADD_INT_LIT16
+.long .L_OP_RSUB_INT
+.long .L_OP_MUL_INT_LIT16
+.long .L_OP_DIV_INT_LIT16
+.long .L_OP_REM_INT_LIT16
+.long .L_OP_AND_INT_LIT16
+.long .L_OP_OR_INT_LIT16
+.long .L_OP_XOR_INT_LIT16
+.long .L_OP_ADD_INT_LIT8
+.long .L_OP_RSUB_INT_LIT8
+.long .L_OP_MUL_INT_LIT8
+.long .L_OP_DIV_INT_LIT8
+.long .L_OP_REM_INT_LIT8
+.long .L_OP_AND_INT_LIT8
+.long .L_OP_OR_INT_LIT8
+.long .L_OP_XOR_INT_LIT8
+.long .L_OP_SHL_INT_LIT8
+.long .L_OP_SHR_INT_LIT8
+.long .L_OP_USHR_INT_LIT8
+.long .L_OP_IGET_VOLATILE
+.long .L_OP_IPUT_VOLATILE
+.long .L_OP_SGET_VOLATILE
+.long .L_OP_SPUT_VOLATILE
+.long .L_OP_IGET_OBJECT_VOLATILE
+.long .L_OP_IGET_WIDE_VOLATILE
+.long .L_OP_IPUT_WIDE_VOLATILE
+.long .L_OP_SGET_WIDE_VOLATILE
+.long .L_OP_SPUT_WIDE_VOLATILE
+.long .L_OP_BREAKPOINT
+.long .L_OP_THROW_VERIFICATION_ERROR
+.long .L_OP_EXECUTE_INLINE
+.long .L_OP_EXECUTE_INLINE_RANGE
+.long .L_OP_INVOKE_DIRECT_EMPTY
+.long .L_OP_UNUSED_F1
+.long .L_OP_IGET_QUICK
+.long .L_OP_IGET_WIDE_QUICK
+.long .L_OP_IGET_OBJECT_QUICK
+.long .L_OP_IPUT_QUICK
+.long .L_OP_IPUT_WIDE_QUICK
+.long .L_OP_IPUT_OBJECT_QUICK
+.long .L_OP_INVOKE_VIRTUAL_QUICK
+.long .L_OP_INVOKE_VIRTUAL_QUICK_RANGE
+.long .L_OP_INVOKE_SUPER_QUICK
+.long .L_OP_INVOKE_SUPER_QUICK_RANGE
+.long .L_OP_IPUT_OBJECT_VOLATILE
+.long .L_OP_SGET_OBJECT_VOLATILE
+.long .L_OP_SPUT_OBJECT_VOLATILE
+.long .L_OP_UNUSED_FF
+
+/* File: x86-atom/footer.S */
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: footer.S
+ */
+
+ .text
+ .align 2
+
+ /*
+ * Check to see if the thread needs to be suspended or debugger/profiler
+ * activity has begun.
+ *
+ * On entry:
+ * %ecx is reentry type, e.g. kInterpEntryInstr
+ * %edx is PC adjustment in bytes
+ */
+
+common_periodicChecks:
+ movl %edx, -8(%esp) # save pc adjustments
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl %ebx, -4(%esp) # save %ebx to the stack
+ movl offGlue_pSelfSuspendCount(%edx), %ebx # %ebx<- pSuspendCount (int)
+4:
+ movl offGlue_pDebuggerActive(%edx), %eax # %eax<- pDebuggerActive
+ testl %eax, %eax
+ je 5f
+ movzbl (%eax), %eax # %eax<- get debuggerActive (boolean)
+5:
+ cmp $0, (%ebx) # check if suspend is pending
+ jne 2f # handle suspend
+ movl offGlue_pActiveProfilers(%edx), %ebx # %ebx<- activeProfilers (int)
+ orl (%ebx), %eax # %eax<- merge activeProfilers and debuggerActive
+ movl -8(%esp), %edx # %edx<- restore %edx
+ jne 3f # debugger or profiler active; switch interp
+ movl -4(%esp), %ebx # %ebx<- restore %ebx
+ ret # return
+2: # check suspended
+ EXPORT_PC
+ movl offGlue_self(%edx), %eax # %eax<- glue->self
+ movl %eax, -12(%esp) # push parameter boolean
+ lea -12(%esp), %esp
+ call dvmCheckSuspendPending # call: (Thread* self)
+ # return: bool
+ movl 4(%esp), %edx # %edx<- restore %edx
+ movl 8(%esp), %ebx # %ebx<- restore %ebx
+ lea 12(%esp), %esp
+ ret
+3: # debugger/profiler enabled, bail out
+ leal (rPC, %edx, 2), rPC # adjust pc to show target
+ movl rGLUE, %ecx # %ecx<- pMterpGlue
+ movb $kInterpEntryInstr, offGlue_entryPoint(%ecx)
+ movl $1, %edx # switch interpreter
+ jmp common_gotoBail # bail
+
+ /*
+ * Check to see if the thread needs to be suspended or debugger/profiler
+ * activity has begun. With this variant, the reentry type is hard coded
+ * as kInterpEntryInstr.
+ *
+ * On entry:
+ * %edx is PC adjustment in bytes
+ */
+
+common_periodicChecks_backwardBranch:
+ EXPORT_PC
+ movl rGLUE, %ecx # %ecx<- pMterpGlue
+ movl offGlue_pSelfSuspendCount(%ecx), rINST # %ebx<- pSuspendCount (int)
+4:
+ movl offGlue_pDebuggerActive(%ecx), %eax # %eax<- pDebuggerActive
+ testl %eax, %eax # test for NULL pointer
+ je 5f
+ movzbl (%eax), %eax # %eax<- get debuggerActive count
+5:
+ cmp $0, (rINST) # check if suspend is pending
+ jne 2f # handle suspend
+ movl offGlue_pActiveProfilers(%ecx), rINST # %edx<- activeProfilers (int)
+ orl (rINST), %eax # %eax<- merge activeProfilers and debuggerActive
+ jne 3f # debugger or profiler active; switch interp
+ FINISH_RB %edx, %ecx # jump to next instruction
+2: # check suspended
+ movl offGlue_self(%ecx), %eax# %eax<- glue->self
+ movl %edx, rINST
+ movl %eax, -12(%esp) # push parameter boolean
+ lea -12(%esp), %esp
+ call dvmCheckSuspendPending # call: (Thread* self)
+ # return: bool
+ movl rINST, %edx # %edx<- restore %edx
+ lea 12(%esp), %esp
+ FINISH_RB %edx, %ecx
+3: # debugger/profiler enabled, bail out
+ leal (rPC, %edx, 2), rPC # adjust pc to show target
+ movb $kInterpEntryInstr, offGlue_entryPoint(%ecx)
+ movl $1, %edx # switch interpreter
+ jmp common_gotoBail # bail
+
+ /*
+ * The equivalent of "goto bail", this calls through the "bail handler".
+ * State registers will be saved to the "glue" area before bailing.
+ *
+ * On entry:
+ * %edx is "bool changeInterp", indicating if we want to switch to the
+ * other interpreter or just bail all the way out
+ */
+
+common_gotoBail:
+ SAVE_PC_FP_TO_GLUE %ecx # save program counter and frame pointer
+
+ /*
+ * Inlined dvmMterpStdBail
+ */
+
+ lea 40(%ebp), %esp
+ movl %edx, %eax
+ movl 24(%ebp), %edi
+ movl 28(%ebp), %esi
+ movl 32(%ebp), %ebx
+ movl 36(%ebp), %ebp
+ ret
+
+ /*
+ * Common code for method invocation with range.
+ *
+ * On entry:
+ * %ecx is "Method* methodToCall", the method we're trying to call
+ */
+
+common_invokeMethodRange:
+.LinvokeNewRange:
+
+ /*
+ * prepare to copy args to "outs" area of current frame
+ */
+
+ SAVEAREA_FROM_FP %eax # %eax<- &outs; &StackSaveArea
+ test rINST, rINST # test for no args
+ movl rINST, sReg0 # sReg0<- AA
+ jz .LinvokeArgsDone # no args; jump to args done
+ FETCH 2, %edx # %edx<- CCCC
+
+ /*
+ * %ecx=methodToCall, %edx=CCCC, sReg0=count, %eax=&outs (&stackSaveArea)
+ * (very few methods have > 10 args; could unroll for common cases)
+ */
+
+ movl %ebx, sReg1 # sReg1<- save %ebx
+ lea (rFP, %edx, 4), %edx # %edx<- &vCCCC
+ shll $2, sReg0 # sReg0<- offset
+ subl sReg0, %eax # %eax<- update &outs
+ shrl $2, sReg0 # sReg0<- offset
+1:
+ movl (%edx), %ebx # %ebx<- vCCCC
+ lea 4(%edx), %edx # %edx<- &vCCCC++
+ subl $1, sReg0 # sReg<- sReg--
+ movl %ebx, (%eax) # *outs<- vCCCC
+ lea 4(%eax), %eax # outs++
+ jne 1b # loop if count (sReg0) not zero
+ movl sReg1, %ebx # %ebx<- restore %ebx
+ jmp .LinvokeArgsDone # continue
+
+ /*
+ * %ecx is "Method* methodToCall", the method we're trying to call
+ * prepare to copy args to "outs" area of current frame
+ */
+
+common_invokeMethodNoRange:
+.LinvokeNewNoRange:
+ movl rINST, sReg0 # sReg0<- BA
+ shrl $4, sReg0 # sReg0<- B
+ je .LinvokeArgsDone # no args; jump to args done
+ SAVEAREA_FROM_FP %eax # %eax<- &outs; &StackSaveArea
+ FETCH 2, %edx # %edx<- GFED
+
+ /*
+ * %ecx=methodToCall, %edx=GFED, sReg0=count, %eax=outs
+ */
+
+.LinvokeNonRange:
+ cmp $2, sReg0 # compare sReg0 to 2
+ movl %edx, sReg1 # sReg1<- GFED
+ jl 1f # handle 1 arg
+ je 2f # handle 2 args
+ cmp $4, sReg0 # compare sReg0 to 4
+ jl 3f # handle 3 args
+ je 4f # handle 4 args
+5:
+ andl $15, rINST # rINST<- A
+ lea -4(%eax), %eax # %eax<- update &outs; &outs--
+ movl (rFP, rINST, 4), %edx # %edx<- vA
+ movl %edx, (%eax) # *outs<- vA
+ movl sReg1, %edx # %edx<- GFED
+4:
+ shr $12, %edx # %edx<- G
+ lea -4(%eax), %eax # %eax<- update &outs; &outs--
+ movl (rFP, %edx, 4), %edx # %edx<- vG
+ movl %edx, (%eax) # *outs<- vG
+ movl sReg1, %edx # %edx<- GFED
+3:
+ and $0x0f00, %edx # %edx<- 0F00
+ shr $6, %edx # %edx<- F at correct offset
+ lea -4(%eax), %eax # %eax<- update &outs; &outs--
+ movl (rFP, %edx), %edx # %edx<- vF
+ movl %edx, (%eax) # *outs<- vF
+ movl sReg1, %edx # %edx<- GFED
+2:
+ and $0x00f0, %edx # %edx<- 00E0
+ shr $2, %edx # %edx<- E at correct offset
+ lea -4(%eax), %eax # %eax<- update &outs; &outs--
+ movl (rFP, %edx), %edx # %edx<- vE
+ movl %edx, (%eax) # *outs<- vE
+ movl sReg1, %edx # %edx<- GFED
+1:
+ and $0x000f, %edx # %edx<- 000D
+ movl (rFP, %edx, 4), %edx # %edx<- vD
+ movl %edx, -4(%eax) # *--outs<- vD
+0:
+
+ /*
+ * %ecx is "Method* methodToCall", the method we're trying to call
+ * find space for the new stack frame, check for overflow
+ */
+
+.LinvokeArgsDone:
+ movzwl offMethod_registersSize(%ecx), %eax # %eax<- methodToCall->regsSize
+ movzwl offMethod_outsSize(%ecx), %edx # %edx<- methodToCall->outsSize
+ movl %ecx, sReg0 # sReg<- methodToCall
+ shl $2, %eax # %eax<- update offset
+ SAVEAREA_FROM_FP %ecx # %ecx<- &outs; &StackSaveArea
+ subl %eax, %ecx # %ecx<- newFP; (old savearea - regsSize)
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ movl %ecx, sReg1 # sReg1<- &outs
+ subl $sizeofStackSaveArea, %ecx # %ecx<- newSaveArea (stack save area using newFP)
+ movl offGlue_interpStackEnd(%eax), %eax # %eax<- glue->interpStackEnd
+ movl %eax, sReg2 # sReg2<- glue->interpStackEnd
+ shl $2, %edx # %edx<- update offset for outsSize
+ movl %ecx, %eax # %eax<- newSaveArea
+ sub %edx, %ecx # %ecx<- bottom; (newSaveArea - outsSize)
+ cmp sReg2, %ecx # compare interpStackEnd and bottom
+ movl sReg0, %ecx # %ecx<- restore methodToCall
+ jl .LstackOverflow # handle frame overflow
+
+ /*
+ * set up newSaveArea
+ */
+
+#ifdef EASY_GDB
+ SAVEAREA_FROM_FP %edx # %edx<- &outs; &StackSaveArea
+ movl %edx, offStackSaveArea_prevSave(%eax) # newSaveArea->prevSave<- &outs
+#endif
+ movl rFP, offStackSaveArea_prevFrame(%eax) # newSaveArea->prevFrame<- rFP
+ movl rPC, offStackSaveArea_savedPc(%eax) # newSaveArea->savedPc<- rPC
+ testl $ACC_NATIVE, offMethod_accessFlags(%ecx) # check for native call
+ movl %ecx, offStackSaveArea_method(%eax) # newSaveArea->method<- method to call
+ jne .LinvokeNative # handle native call
+
+ /*
+ * Update "glue" values for the new method
+ * %ecx=methodToCall, sReg1=newFp
+ */
+
+ movl offMethod_clazz(%ecx), %edx # %edx<- method->clazz
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ movl %ecx, offGlue_method(%eax) # glue->method<- methodToCall
+ movl offClassObject_pDvmDex(%edx), %edx # %edx<- method->clazz->pDvmDex
+ movl offMethod_insns(%ecx), rPC # rPC<- methodToCall->insns
+ movl %edx, offGlue_methodClassDex(%eax) # glue->methodClassDex<- method->clazz->pDvmDex
+ movl offGlue_self(%eax), %ecx # %ecx<- glue->self
+ movl sReg1, rFP # rFP<- newFP
+ movl rFP, offThread_curFrame(%ecx) # glue->self->curFrame<- newFP
+ FINISH_A # jump to methodToCall->insns
+
+ /*
+ * Prep for the native call
+ * %ecx=methodToCall, sReg1=newFP, %eax=newSaveArea
+ */
+
+.LinvokeNative:
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl %ecx, -20(%esp) # push parameter methodToCall
+ movl offGlue_self(%edx), %edx # %edx<- glue->self
+ movl offThread_jniLocal_topCookie(%edx), %ecx # %ecx<- glue->self->thread->refNext
+ movl %ecx, offStackSaveArea_localRefCookie(%eax) # newSaveArea->localRefCookie<- refNext
+ movl %eax, -4(%esp) # save newSaveArea
+ movl sReg1, %eax # %eax<- newFP
+ movl %eax, offThread_curFrame(%edx) # glue->self->curFrame<- newFP
+ movl %edx, -8(%esp) # save glue->self
+ movl %edx, -16(%esp) # push parameter glue->self
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl -20(%esp), %ecx # %ecx<- methodToCall
+ lea offGlue_retval(%edx), %edx # %edx<- &retval
+ movl %edx, -24(%esp) # push parameter pMterpGlue
+ movl %eax, -28(%esp) # push parameter newFP
+ lea -28(%esp), %esp
+
+#ifdef ASSIST_DEBUGGER
+ jmp .Lskip
+ .type dalvik_mterp, %function
+dalvik_mterp:
+ MTERP_ENTRY
+.Lskip:
+#endif
+ call *offMethod_nativeFunc(%ecx) # call methodToCall->nativeFunc
+ lea 28(%esp), %esp
+ movl -4(%esp), %edx # %edx<- newSaveArea
+ movl -8(%esp), %ecx # %ecx<- glue->self
+ movl offStackSaveArea_localRefCookie(%edx), %eax # %eax<- newSaveArea->localRefCookie
+ FFETCH_ADV 3, %edx # %edx<- next instruction hi; fetch, advance
+ cmp $0, offThread_exception(%ecx) # check for exception
+ movl rFP, offThread_curFrame(%ecx) # glue->self->curFrame<- rFP
+ movl %eax, offThread_jniLocal_topCookie(%ecx) # glue->self<- newSaveArea->localRefCookie
+ jne common_exceptionThrown # handle exception
+ FGETOP_JMP 3, %edx # jump to next instruction; getop, jmp
+
+.LstackOverflow:
+ movl %ecx, -4(%esp) # push method to call
+ movl rGLUE, %ecx # %ecx<- pMterpGlue
+ movl offGlue_self(%ecx), %ecx # %ecx<- glue->self
+ movl %ecx, -8(%esp) # push parameter self
+ lea -8(%esp), %esp
+ call dvmHandleStackOverflow # call: (Thread* self, Method *meth)
+ # return: void
+ lea 8(%esp), %esp
+ jmp common_exceptionThrown # handle exception
+#ifdef ASSIST_DEBUGGER
+#endif
+
+ /*
+ * Common code for handling a return instruction.
+ *
+ * This does not return.
+ */
+
+common_returnFromMethod:
+.LreturnNew:
+
+ /*
+ * Inline common periodic checks
+ */
+
+ movl rGLUE, rINST # %ecx<- pMterpGlue
+ movl offGlue_pSelfSuspendCount(rINST), %edx # %ebx<- pSuspendCount (int)
+ movl offGlue_pDebuggerActive(rINST), %eax # %eax<- pDebuggerActive
+ movl (%eax), %eax # %eax<- get debuggerActive (boolean)
+ and $7, %eax # %eax<- mask for boolean (just how many bits does it take?)
+ cmp $0, (%edx) # check if suspend is pending
+ jne 2f # handle suspend
+ movl offGlue_pActiveProfilers(rINST), %edx # %edx<- activeProfilers (int)
+ or (%edx), %eax # %eax<- merge activeProfilers and debuggerActive
+ cmp $0, %eax # check for debuggerActive
+ jne 3f # debugger or profiler active; switch interp
+ jmp 4f
+2: # check suspended
+ movl offGlue_self(rINST), %eax# %eax<- glue->self
+ movl %eax, -12(%esp) # push parameter boolean
+ lea -12(%esp), %esp
+ call dvmCheckSuspendPending # call: (Thread* self)
+ # return: bool
+ lea 12(%esp), %esp
+ jmp 4f
+3: # debugger/profiler enabled, bail out
+ movl $kInterpEntryInstr, offGlue_entryPoint(rINST) # glue->entryPoint<- reentry type
+ movl $1, %edx # switch to interp<- true
+ jmp common_gotoBail # bail
+
+
+ /*
+ * Get save area; rGLUE is %ebx, rFP is %eax
+ */
+4:
+ SAVEAREA_FROM_FP %ecx # %ecx<- saveArea(old)
+ movl offStackSaveArea_prevFrame(%ecx), rFP # rFP<- saveArea->PrevFrame
+ movl (offStackSaveArea_method - sizeofStackSaveArea)(rFP), %edx # %edx<- method we are returning to
+ cmpl $0, %edx # check for break frame
+ je common_gotoBail # bail if break frame
+ movl offStackSaveArea_savedPc(%ecx), rPC # rPC<- saveAreaOld->savedPc
+ movl offGlue_self(rINST), %ecx # %eax<- glue->self
+ movl %edx, offGlue_method(rINST) # glue->method<- newSave->method
+ movl offMethod_clazz(%edx), %edx # %edx<- method->clazz
+ FFETCH_ADV 3, %eax # %ecx<- next instruction hi; fetch, advance
+ movl rFP, offThread_curFrame(%ecx) # glue->self->curFrame<- rFP
+ movl offClassObject_pDvmDex(%edx), %edx # %edx<- method->clazz->pDvmDex
+ movl %edx, offGlue_methodClassDex(rINST) # glue->pDvmDex<- method->clazz->pDvmDex
+ FGETOP_JMP 3, %eax # jump to next instruction; getop, jmp
+
+ /*
+ * Handle thrown an exception. If the exception processing code
+ * returns to us (instead of falling out of the interpreter),
+ * continue with whatever the next instruction now happens to be.
+ * This does not return.
+ */
+
+common_exceptionThrown:
+.LexceptionNew:
+ movl $kInterpEntryThrow, %ecx # %ecx<- reentry type
+ movl $0, %edx # %edx<- pc adjustment
+ call common_periodicChecks
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ movl offGlue_self(%eax), %edx # %edx<- glue->self
+ movl offThread_exception(%edx), %ecx # %ecx<- pMterpGlue->self->exception
+ movl %edx, -4(%esp) # push parameter self
+ movl %ecx, -8(%esp) # push parameter obj
+ lea -8(%esp), %esp
+ call dvmAddTrackedAlloc # don't allow the exception to be GC'd
+ # call: (Object* obj, Thread* self)
+ # return: void
+ movl 4(%esp), %edx # %edx<- glue->self
+ movl $0, offThread_exception(%edx) # glue->self->exception<- NULL
+
+ /*
+ * set up args and a local for &fp
+ */
+
+ movl rFP, -4(%esp) # move fp to stack
+ lea -4(%esp), %esp # update %esp
+ movl %esp, -4(%esp) # push parameter 4<- &fp
+ movl $0, -8(%esp) # push parameter 3<- false
+ movl 4(%esp), %edx
+ movl %edx, -12(%esp) # push parameter 2<- glue->self->exception
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ movl offGlue_method(%eax), %edx # %edx<- glue->method
+ movl offMethod_insns(%edx), %edx # %edx<- glue->method->insns
+ movl rPC, %ecx # %ecx<- rPC
+ subl %edx, %ecx # %ecx<- pc - glue->method->insns
+ sar $1, %ecx # %ecx<- adjust %ecx for offset
+ movl %ecx, -16(%esp) # push parameter 1<- glue->method->insns
+ movl 8(%esp), %edx
+ movl %edx, -20(%esp) # push parameter 0<- glue->self
+ lea -20(%esp), %esp
+
+ /*
+ * call dvmFindCatchBlock, %eax gets catchRelPc (a code-unit offset)
+ */
+
+ call dvmFindCatchBlock # call: (Thread* self, int relPc, Object* exception,
+ # bool doUnroll, void** newFrame)
+ # return: int
+ lea 32(%esp), %esp
+ movl -12(%esp), rFP # rFP<- updated rFP
+ cmp $0, %eax # check for catchRelPc < 0
+ jl .LnotCaughtLocally # handle not caught locally
+
+ /*
+ * fix stack overflow if necessary
+ */
+
+ movl -4(%esp), %ecx # %ecx<- glue->self
+ cmp $0, offThread_stackOverflowed(%ecx)
+ je 1f
+ movl %eax, -4(%esp) # save %eax for later
+ movl %ecx, -12(%esp) # push parameter 2 glue->self
+ lea -12(%esp), %esp
+ call dvmCleanupStackOverflow # call: (Thread* self, Object* exception)
+ # return: void
+ lea 12(%esp), %esp
+ movl -4(%esp), %eax # %eax<- restore %eax
+ jmp 2f
+1:
+ movl %ecx, -12(%esp) # push parameter 2 glue->self
+2:
+
+ /*
+ * adjust locals to match self->curFrame and updated PC
+ *
+ */
+
+ SAVEAREA_FROM_FP %edx # %edx<- get newSaveArea
+ movl rGLUE, %ecx # %ecx<- pMterpGlue
+ movl offStackSaveArea_method(%edx), rPC # rPC<- newMethod
+ movl rPC, offGlue_method(%ecx) # glue->method<- newMethod
+ movl offMethod_clazz(rPC), %edx # %edx<- method->clazz
+ movl offMethod_insns(rPC), rPC # rPC<- method->insns
+ movl offClassObject_pDvmDex(%edx), %edx # %edx<- method->clazz->pDvmDex
+ lea (rPC, %eax, 2), rPC # rPC<- method->insns + catchRelPc
+ movl %edx, offGlue_methodClassDex(%ecx) # glue->pDvmDex<- method->clazz->pDvmDex
+ movl -8(%esp), %eax
+ movl %eax, -16(%esp) # push parameter 1 obj
+ lea -16(%esp), %esp
+ call dvmReleaseTrackedAlloc # call: (Object* obj, Thread* self)
+ # return: void
+ lea 16(%esp), %esp
+ FINISH_FETCH %eax
+ cmp $OP_MOVE_EXCEPTION, %eax # is it a move exception
+ jne 1f
+ movl -12(%esp), %edx # %edx<- glue->self
+ movl -8(%esp), %ecx # %ecx<- exception
+ movl %ecx, offThread_exception(%edx) # restore the exception
+1:
+ FINISH_JMP %eax
+
+ /*
+ * -8(%esp) = exception, -4(%esp) = self
+ */
+
+.LnotCaughtLocally:
+ movl -4(%esp), %edx # %edx<- glue->self
+ movzb offThread_stackOverflowed(%edx), %eax # %eax<- self->stackOverflowed
+ cmp $0, %eax # check for stack overflow;
+ # maybe should use cmpb
+ je 1f #
+ movl %edx, -12(%esp) # push parameter 1 glue->self
+ lea -12(%esp), %esp
+ call dvmCleanupStackOverflow # call: (Thread* self, Object* exception)
+ # return: void
+ lea 12(%esp), %esp
+
+ /*
+ * Release the exception
+ * -8(%esp) = exception, -4(%esp) = self
+ */
+1:
+ movl -8(%esp), %ecx # %ecx<- exception
+ movl -4(%esp), %edx # %edx<- glue->self
+ movl %ecx, offThread_exception(%edx) # glue->self<- exception
+ lea -8(%esp), %esp
+ call dvmReleaseTrackedAlloc # call: (Object* obj, Thread* self)
+ # return: void
+ lea 8(%esp), %esp
+ movl $0, %edx # switch to interp<- false
+ jmp common_gotoBail # bail
+
+ /*
+ * After returning from a "glued" function, pull out the updated
+ * values and start executing at the next instruction.
+ */
+
+common_resumeAfterGlueCall:
+ LOAD_PC_FP_FROM_GLUE # pull rPC and rFP out of glue
+ FINISH_A # jump to next instruction
+
+ /*
+ * For debugging, cause an immediate fault.
+ */
+
+common_abort:
+ jmp .LdeadFood
+
+.LdeadFood:
+.int 0xdeadf00d
+
+ /*
+ * Invalid array index.
+ */
+
+common_errArrayIndex:
+ EXPORT_PC
+ movl $.LstrArrayIndexException, -8(%esp) # push parameter description
+ movl $0, -4(%esp) # push parameter msg paramter
+ lea -8(%esp), %esp
+ call dvmThrowException # call: (const char* exceptionDescriptor, const char* msg)
+ # return: void
+ lea 8(%esp), %esp
+ jmp common_exceptionThrown # handle exception
+
+ /*
+ * Invalid array value.
+ */
+
+common_errArrayStore:
+ EXPORT_PC
+ movl $.LstrArrayStoreException, -8(%esp) # push parameter description
+ movl $0, -4(%esp) # push parameter msg paramter
+ lea -8(%esp), %esp
+ call dvmThrowException # call: (const char* exceptionDescriptor, const char* msg)
+ # return: void
+ lea 8(%esp), %esp
+ jmp common_exceptionThrown # handle exception
+
+ /*
+ * Integer divide or mod by zero.
+ */
+
+common_errDivideByZero:
+ EXPORT_PC
+ movl $.LstrArithmeticException, -8(%esp) # push parameter description
+ movl $.LstrDivideByZero, -4(%esp) # push parameter msg paramter
+ lea -8(%esp), %esp
+ call dvmThrowException # call: (const char* exceptionDescriptor, const char* msg)
+ # return: void
+ lea 8(%esp), %esp
+ jmp common_exceptionThrown # handle exception
+
+ /*
+ * Attempt to allocate an array with a negative size.
+ */
+
+common_errNegativeArraySize:
+ EXPORT_PC
+ movl $.LstrNegativeArraySizeException, -8(%esp) # push parameter description
+ movl $0, -4(%esp) # push parameter msg paramter
+ lea -8(%esp), %esp
+ call dvmThrowException # call: (const char* exceptionDescriptor, const char* msg)
+ # return: void
+ lea 8(%esp), %esp
+ jmp common_exceptionThrown # handle exception
+
+ /*
+ * Invocation of a non-existent method.
+ */
+
+common_errNoSuchMethod:
+ EXPORT_PC
+ movl $.LstrNoSuchMethodError, -8(%esp) # push parameter description
+ movl $0, -4(%esp) # push parameter msg paramter
+ lea -8(%esp), %esp
+ call dvmThrowException # call: (const char* exceptionDescriptor, const char* msg)
+ # return: void
+ lea 8(%esp), %esp
+ jmp common_exceptionThrown # handle exception
+
+ /*
+ * Unexpected null object.
+ */
+
+common_errNullObject:
+ EXPORT_PC
+ movl $.LstrNullPointerException, -8(%esp) # push parameter description
+ movl $0, -4(%esp) # push parameter msg paramter
+ lea -8(%esp), %esp
+ call dvmThrowException # call: (const char* exceptionDescriptor, const char* msg)
+ # return: void
+ lea 8(%esp), %esp
+ jmp common_exceptionThrown # handle exception
+
+ /*
+ * String references
+ */
+
+ .align 4
+ .section .rodata
+.LstrArithmeticException:
+ .asciz "Ljava/lang/ArithmeticException;"
+.LstrArrayIndexException:
+ .asciz "Ljava/lang/ArrayIndexOutOfBoundsException;"
+.LstrArrayStoreException:
+ .asciz "Ljava/lang/ArrayStoreException;"
+.LstrClassCastException:
+ .asciz "Ljava/lang/ClassCastException;"
+.LstrDivideByZero:
+ .asciz "divide by zero"
+.LstrInstantiationError:
+ .asciz "Ljava/lang/InstantiationError;"
+.LstrNegativeArraySizeException:
+ .asciz "Ljava/lang/NegativeArraySizeException;"
+.LstrNoSuchMethodError:
+ .asciz "Ljava/lang/NoSuchMethodError;"
+.LstrNullPointerException:
+ .asciz "Ljava/lang/NullPointerException;"
+.LstrExceptionNotCaughtLocally:
+ .asciz "Exception %s from %s:%d not caught locally\n"
+
diff --git a/vm/mterp/out/InterpAsm-x86.S b/vm/mterp/out/InterpAsm-x86.S
new file mode 100644
index 0000000..92b2d9d
--- /dev/null
+++ b/vm/mterp/out/InterpAsm-x86.S
@@ -0,0 +1,9425 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'x86'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: x86/header.S */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * 32-bit x86 definitions and declarations.
+ */
+
+/*
+386 ABI general notes:
+
+Caller save set:
+ eax, edx, ecx, st(0)-st(7)
+Callee save set:
+ ebx, esi, edi, ebp
+Return regs:
+ 32-bit in eax
+ 64-bit in edx:eax (low-order 32 in eax)
+ fp on top of fp stack st(0)
+
+Parameters passed on stack, pushed right-to-left. On entry to target, first
+parm is at 4(%esp). Traditional entry code is:
+
+functEntry:
+ push %ebp # save old frame pointer
+ mov %ebp,%esp # establish new frame pointer
+ sub FrameSize,%esp # Allocate storage for spill, locals & outs
+
+Once past the prologue, arguments are referenced at ((argno + 2)*4)(%ebp)
+
+Alignment of stack not strictly required, but should be for performance. We'll
+align frame sizes to 16-byte multiples.
+
+If we're not doing variable stack allocation (alloca), the frame pointer can be
+eliminated and all arg references adjusted to be esp relative.
+
+Mterp notes:
+
+Some key interpreter variables will be assigned to registers. Note that each
+will also have an associated spill location (mostly used useful for those assigned
+to callee save registers).
+
+ nick reg purpose
+ rPC edx interpreted program counter, used for fetching instructions
+ rFP esi interpreted frame pointer, used for accessing locals and args
+ rIBASE edi Base pointer for instruction dispatch computed goto
+ rINST bx first 16-bit code of current instruction
+ rOPCODE bl opcode portion of instruction word
+ rINST_HI bh high byte of instruction word, usually contains src/tgt reg names
+
+Notes:
+ o High order 16 bits of ebx must be zero on entry to handler
+ o rPC, rFP, rIBASE, rINST/rOPCODE valid on handler entry and exit
+ o eax and ecx are scratch, rINST/ebx sometimes scratch
+ o rPC is in the caller save set, and will be killed across external calls. Don't
+ forget to SPILL/UNSPILL it around call points
+
+*/
+
+#define rPC %edx
+#define rFP %esi
+#define rIBASE %edi
+#define rINST_FULL %ebx
+#define rINST %bx
+#define rINST_HI %bh
+#define rINST_LO %bl
+#define rOPCODE %bl
+
+
+/* Frame diagram while executing dvmMterpStdRun, high to low addresses */
+#define IN_ARG0 ( 8)
+#define CALLER_RP ( 4)
+#define PREV_FP ( 0) /* <- dvmMterpStdRun ebp */
+/* Spill offsets relative to %ebp */
+#define EDI_SPILL ( -4)
+#define ESI_SPILL ( -8)
+#define EDX_SPILL (-12) /* <- esp following dmMterpStdRun header */
+#define rPC_SPILL (-16)
+#define rFP_SPILL (-20)
+#define rGLUE_SPILL (-24)
+#define rIBASE_SPILL (-28)
+#define rINST_FULL_SPILL (-32)
+#define TMP_SPILL (-36)
+#define LOCAL0_OFFSET (-40)
+#define LOCAL1_OFFSET (-44)
+#define LOCAL2_OFFSET (-48)
+#define LOCAL3_OFFSET (-52)
+/* Out Arg offsets, relative to %sp */
+#define OUT_ARG4 ( 16)
+#define OUT_ARG3 ( 12)
+#define OUT_ARG2 ( 8)
+#define OUT_ARG1 ( 4)
+#define OUT_ARG0 ( 0) /* <- dvmMterpStdRun esp */
+
+#define SPILL(reg) movl reg##,reg##_SPILL(%ebp)
+#define UNSPILL(reg) movl reg##_SPILL(%ebp),reg
+#define SPILL_TMP(reg) movl reg,TMP_SPILL(%ebp)
+#define UNSPILL_TMP(reg) movl TMP_SPILL(%ebp),reg
+
+
+/* save/restore the PC and/or FP from the glue struct */
+#define LOAD_PC_FROM_GLUE(_glu) movl offGlue_pc(_glu),rPC
+#define SAVE_PC_TO_GLUE(_glu) movl rPC,offGlue_pc(_glu)
+#define LOAD_FP_FROM_GLUE(_glu) movl offGlue_fp(_glu),rFP
+#define SAVE_FP_TO_GLUE(_glu) movl rFP,offGlue_fp(_glu)
+
+#define GET_GLUE(_reg) movl rGLUE_SPILL(%ebp),_reg
+
+/* The interpreter assumes a properly aligned stack on entry, and
+ * will preserve 16-byte alignment.
+ */
+
+/*
+ * "export" the PC to the interpreted stack frame, f/b/o future exception
+ * objects. Must * be done *before* something calls dvmThrowException.
+ *
+ * In C this is "SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc", i.e.
+ * fp - sizeof(StackSaveArea) + offsetof(SaveArea, xtra.currentPc)
+ *
+ * It's okay to do this more than once.
+ */
+#define EXPORT_PC() \
+ movl rPC, (-sizeofStackSaveArea + offStackSaveArea_currentPc)(rFP)
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+#define SAVEAREA_FROM_FP(_reg, _fpreg) \
+ leal -sizeofStackSaveArea(_fpreg),_reg
+
+/*
+ * Fetch the next instruction from rPC into rINST. Does not advance rPC.
+ */
+#define FETCH_INST() movzwl (rPC),rINST_FULL
+
+/*
+ * Fetch the nth instruction word from rPC into rINST. Does not advance
+ * rPC, and _count is in words
+ */
+#define FETCH_INST_WORD(_count) movzwl _count*2(rPC),rINST_FULL
+
+/*
+ * Fetch instruction word indexed (used for branching).
+ * Index is in instruction word units.
+ */
+#define FETCH_INST_INDEXED(_reg) movzwl (rPC,_reg,2),rINST_FULL
+
+/*
+ * Extract the opcode of the instruction in rINST
+ */
+#define EXTRACT_OPCODE(_reg) movzx rOPCODE,_reg
+
+/*
+ * Advance rPC by instruction count
+ */
+#define ADVANCE_PC(_count) leal 2*_count(rPC),rPC
+
+/*
+ * Advance rPC by branch offset in register
+ */
+#define ADVANCE_PC_INDEXED(_reg) leal (rPC,_reg,2),rPC
+
+/*
+ * Note: assumes opcode previously fetched and in rINST, and
+ * %eax is killable at this point.
+ */
+#if 1
+.macro GOTO_NEXT
+ /* For computed next version */
+ movzx rOPCODE,%eax
+ sall $6,%eax
+ addl rIBASE,%eax
+ jmp *%eax
+.endm
+#else
+ /* For jump table version */
+.macro GOTO_NEXT
+ movzx rOPCODE,%eax
+ jmp *(rIBASE,%eax,4)
+.endm
+#endif
+
+/*
+ * Get/set the 32-bit value from a Dalvik register.
+ */
+#define GET_VREG(_reg, _vreg) movl (rFP,_vreg,4),_reg
+#define SET_VREG(_reg, _vreg) movl _reg,(rFP,_vreg,4)
+#define GET_VREG_WORD(_reg, _vreg, _offset) movl 4*(_offset)(rFP,_vreg,4),_reg
+#define SET_VREG_WORD(_reg, _vreg, _offset) movl _reg,4*(_offset)(rFP,_vreg,4)
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../common/asm-constants.h"
+
+
+ .global dvmAsmInstructionStart
+ .type dvmAsmInstructionStart, %function
+dvmAsmInstructionStart = .L_OP_NOP
+ .text
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NOP: /* 0x00 */
+/* File: x86/OP_NOP.S */
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE: /* 0x01 */
+/* File: x86/OP_MOVE.S */
+ /* for move, move-object, long-to-int */
+ /* op vA, vB */
+ movzbl rINST_HI,%eax # eax<- BA
+ andb $0xf,%al # eax<- A
+ shrl $12,rINST_FULL # rINST_FULL<- B
+ GET_VREG(%ecx,rINST_FULL)
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ SET_VREG(%ecx,%eax) # fp[A]<-fp[B]
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_FROM16: /* 0x02 */
+/* File: x86/OP_MOVE_FROM16.S */
+ /* for: move/from16, move-object/from16 */
+ /* op vAA, vBBBB */
+ movzx rINST_HI,%eax # eax <= AA
+ movw 2(rPC),rINST # rINST <= BBBB
+ GET_VREG (%ecx,rINST_FULL) # ecx<- fp[BBBB]
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ SET_VREG (%ecx,%eax) # fp[AA]<- ecx]
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_16: /* 0x03 */
+/* File: x86/OP_MOVE_16.S */
+ /* for: move/16, move-object/16 */
+ /* op vAAAA, vBBBB */
+ movzwl 4(rPC),%ecx # ecx<- BBBB
+ movzwl 2(rPC),%eax # eax<- AAAA
+ GET_VREG(%ecx,%ecx)
+ FETCH_INST_WORD(3)
+ ADVANCE_PC(3)
+ SET_VREG(%ecx,%eax)
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_WIDE: /* 0x04 */
+/* File: x86/OP_MOVE_WIDE.S */
+ /* move-wide vA, vB */
+ /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+ movzbl rINST_HI,%ecx # ecx <- BA
+ sarl $12,rINST_FULL # rinst_FULL<- B
+ GET_VREG_WORD(%eax,rINST_FULL,0) # eax<- v[B+0]
+ GET_VREG_WORD(rINST_FULL,rINST_FULL,1) # rINST_FULL<- v[B+1]
+ andb $0xf,%cl # ecx <- A
+ SET_VREG_WORD(rINST_FULL,%ecx,1) # v[A+1]<- rINST_FULL
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ SET_VREG_WORD(%eax,%ecx,0) # v[A+0]<- eax
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_WIDE_FROM16: /* 0x05 */
+/* File: x86/OP_MOVE_WIDE_FROM16.S */
+ /* move-wide/from16 vAA, vBBBB */
+ /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+ movzwl 2(rPC),%ecx # ecx<- BBBB
+ movzbl rINST_HI,%eax # eax<- AAAA
+ GET_VREG_WORD(rINST_FULL,%ecx,0) # rINST_FULL<- v[BBBB+0]
+ GET_VREG_WORD(%ecx,%ecx,1) # ecx<- v[BBBB+1]
+ SET_VREG_WORD(rINST_FULL,%eax,0) # v[AAAA+0]<- rINST_FULL
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ SET_VREG_WORD(%ecx,%eax,1) # v[AAAA+1]<- eax
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_WIDE_16: /* 0x06 */
+/* File: x86/OP_MOVE_WIDE_16.S */
+ /* move-wide/16 vAAAA, vBBBB */
+ /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+ movzwl 4(rPC),%ecx # ecx<- BBBB
+ movzwl 2(rPC),%eax # eax<- AAAA
+ GET_VREG_WORD(rINST_FULL,%ecx,0) # rINST_WORD<- v[BBBB+0]
+ GET_VREG_WORD(%ecx,%ecx,1) # ecx<- v[BBBB+1]
+ SET_VREG_WORD(rINST_FULL,%eax,0) # v[AAAA+0]<- rINST_FULL
+ FETCH_INST_WORD(3)
+ ADVANCE_PC(3)
+ SET_VREG_WORD(%ecx,%eax,1) # v[AAAA+1]<- ecx
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_OBJECT: /* 0x07 */
+/* File: x86/OP_MOVE_OBJECT.S */
+/* File: x86/OP_MOVE.S */
+ /* for move, move-object, long-to-int */
+ /* op vA, vB */
+ movzbl rINST_HI,%eax # eax<- BA
+ andb $0xf,%al # eax<- A
+ shrl $12,rINST_FULL # rINST_FULL<- B
+ GET_VREG(%ecx,rINST_FULL)
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ SET_VREG(%ecx,%eax) # fp[A]<-fp[B]
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_OBJECT_FROM16: /* 0x08 */
+/* File: x86/OP_MOVE_OBJECT_FROM16.S */
+/* File: x86/OP_MOVE_FROM16.S */
+ /* for: move/from16, move-object/from16 */
+ /* op vAA, vBBBB */
+ movzx rINST_HI,%eax # eax <= AA
+ movw 2(rPC),rINST # rINST <= BBBB
+ GET_VREG (%ecx,rINST_FULL) # ecx<- fp[BBBB]
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ SET_VREG (%ecx,%eax) # fp[AA]<- ecx]
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_OBJECT_16: /* 0x09 */
+/* File: x86/OP_MOVE_OBJECT_16.S */
+/* File: x86/OP_MOVE_16.S */
+ /* for: move/16, move-object/16 */
+ /* op vAAAA, vBBBB */
+ movzwl 4(rPC),%ecx # ecx<- BBBB
+ movzwl 2(rPC),%eax # eax<- AAAA
+ GET_VREG(%ecx,%ecx)
+ FETCH_INST_WORD(3)
+ ADVANCE_PC(3)
+ SET_VREG(%ecx,%eax)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_RESULT: /* 0x0a */
+/* File: x86/OP_MOVE_RESULT.S */
+ /* for: move-result, move-result-object */
+ /* op vAA */
+ GET_GLUE(%eax) # eax<- rGLUE
+ movzx rINST_HI,%ecx # ecx<- AA
+ movl offGlue_retval(%eax),%eax # eax<- glue->retval.l
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ SET_VREG (%eax,%ecx) # fp[AA]<- retval.l
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_RESULT_WIDE: /* 0x0b */
+/* File: x86/OP_MOVE_RESULT_WIDE.S */
+ /* move-result-wide vAA */
+ GET_GLUE(%ecx)
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ movl offGlue_retval(%ecx),%eax
+ movl 4+offGlue_retval(%ecx),%ecx
+ SET_VREG_WORD(%eax,rINST_FULL,0) # v[AA+0] <- eax
+ SET_VREG_WORD(%ecx,rINST_FULL,1) # v[AA+1] <- ecx
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_RESULT_OBJECT: /* 0x0c */
+/* File: x86/OP_MOVE_RESULT_OBJECT.S */
+/* File: x86/OP_MOVE_RESULT.S */
+ /* for: move-result, move-result-object */
+ /* op vAA */
+ GET_GLUE(%eax) # eax<- rGLUE
+ movzx rINST_HI,%ecx # ecx<- AA
+ movl offGlue_retval(%eax),%eax # eax<- glue->retval.l
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ SET_VREG (%eax,%ecx) # fp[AA]<- retval.l
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MOVE_EXCEPTION: /* 0x0d */
+/* File: x86/OP_MOVE_EXCEPTION.S */
+ /* move-exception vAA */
+ GET_GLUE(%ecx)
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ movl offGlue_self(%ecx),%ecx # ecx<- glue->self
+ movl offThread_exception(%ecx),%eax # eax<- dvmGetException bypass
+ SET_VREG(%eax,rINST_FULL) # fp[AA]<- exception object
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ movl $0,offThread_exception(%ecx) # dvmClearException bypass
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RETURN_VOID: /* 0x0e */
+/* File: x86/OP_RETURN_VOID.S */
+ jmp common_returnFromMethod
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RETURN: /* 0x0f */
+/* File: x86/OP_RETURN.S */
+ /*
+ * Return a 32-bit value. Copies the return value into the "glue"
+ * structure, then jumps to the return handler.
+ *
+ * for: return, return-object
+ */
+ /* op vAA */
+ GET_GLUE(%ecx)
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG(%eax,rINST_FULL) # eax<- vAA
+ movl %eax,offGlue_retval(%ecx) # retval.i <- AA
+ jmp common_returnFromMethod
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RETURN_WIDE: /* 0x10 */
+/* File: x86/OP_RETURN_WIDE.S */
+ /*
+ * Return a 64-bit value. Copies the return value into the "glue"
+ * structure, then jumps to the return handler.
+ */
+ /* return-wide vAA */
+ GET_GLUE(%ecx)
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG_WORD(%eax,rINST_FULL,0) # eax<- v[AA+0]
+ GET_VREG_WORD(rINST_FULL,rINST_FULL,1) # rINST_FULL<- v[AA+1]
+ movl %eax,offGlue_retval(%ecx)
+ movl rINST_FULL,4+offGlue_retval(%ecx)
+ jmp common_returnFromMethod
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RETURN_OBJECT: /* 0x11 */
+/* File: x86/OP_RETURN_OBJECT.S */
+/* File: x86/OP_RETURN.S */
+ /*
+ * Return a 32-bit value. Copies the return value into the "glue"
+ * structure, then jumps to the return handler.
+ *
+ * for: return, return-object
+ */
+ /* op vAA */
+ GET_GLUE(%ecx)
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG(%eax,rINST_FULL) # eax<- vAA
+ movl %eax,offGlue_retval(%ecx) # retval.i <- AA
+ jmp common_returnFromMethod
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_4: /* 0x12 */
+/* File: x86/OP_CONST_4.S */
+ /* const/4 vA, #+B */
+ movsx rINST_HI,%eax # eax<-ssssssBx
+ movl $0xf,%ecx
+ andl %eax,%ecx # ecx<- A
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ sarl $4,%eax
+ SET_VREG(%eax,%ecx)
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_16: /* 0x13 */
+/* File: x86/OP_CONST_16.S */
+ /* const/16 vAA, #+BBBB */
+ movswl 2(rPC),%ecx # ecx<- ssssBBBB
+ movzx rINST_HI,%eax # eax<- AA
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ SET_VREG(%ecx,%eax) # vAA<- ssssBBBB
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST: /* 0x14 */
+/* File: x86/OP_CONST.S */
+ /* const vAA, #+BBBBbbbb */
+ movzbl rINST_HI,%ecx # ecx<- AA
+ movl 2(rPC),%eax # grab all 32 bits at once
+ FETCH_INST_WORD(3)
+ ADVANCE_PC(3)
+ SET_VREG(%eax,%ecx) # vAA<- eax
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_HIGH16: /* 0x15 */
+/* File: x86/OP_CONST_HIGH16.S */
+ /* const/high16 vAA, #+BBBB0000 */
+ movzwl 2(rPC),%eax # eax<- 0000BBBB
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ sall $16,%eax # eax<- BBBB0000
+ SET_VREG(%eax,%ecx) # vAA<- eax
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_WIDE_16: /* 0x16 */
+/* File: x86/OP_CONST_WIDE_16.S */
+ /* const-wide/16 vAA, #+BBBB */
+ movswl 2(rPC),%eax # eax<- ssssBBBB
+ SPILL(rPC)
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(2)
+ cltd # rPC:eax<- ssssssssssssBBBB
+ SET_VREG_WORD(rPC,%ecx,1) # store msw
+ UNSPILL(rPC)
+ SET_VREG_WORD(%eax,%ecx,0) # store lsw
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_WIDE_32: /* 0x17 */
+/* File: x86/OP_CONST_WIDE_32.S */
+ /* const-wide/32 vAA, #+BBBBbbbb */
+ movl 2(rPC),%eax # eax<- BBBBbbbb
+ SPILL(rPC)
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(3)
+ cltd # rPC:eax<- ssssssssssssBBBB
+ SET_VREG_WORD(rPC,%ecx,1) # store msw
+ UNSPILL(rPC)
+ SET_VREG_WORD(%eax,%ecx,0) # store lsw
+ ADVANCE_PC(3)
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_WIDE: /* 0x18 */
+/* File: x86/OP_CONST_WIDE.S */
+ /* const-wide vAA, #+HHHHhhhhBBBBbbbb */
+ movl 2(rPC),%eax # eax<- lsw
+ movzbl rINST_HI,%ecx # ecx <- AA
+ movl 6(rPC),rINST_FULL # rINST_FULL<- msw
+ leal (rFP,%ecx,4),%ecx # dst addr
+ movl rINST_FULL,4(%ecx)
+ FETCH_INST_WORD(5)
+ movl %eax,(%ecx)
+ ADVANCE_PC(5)
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_WIDE_HIGH16: /* 0x19 */
+/* File: x86/OP_CONST_WIDE_HIGH16.S */
+ /* const-wide/high16 vAA, #+BBBB000000000000 */
+ movzwl 2(rPC),%eax # eax<- 0000BBBB
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ sall $16,%eax # eax<- BBBB0000
+ SET_VREG_WORD(%eax,%ecx,1) # v[AA+1]<- eax
+ xorl %eax,%eax
+ SET_VREG_WORD(%eax,%ecx,0) # v[AA+0]<- eax
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_STRING: /* 0x1a */
+/* File: x86/OP_CONST_STRING.S */
+
+ /* const/string vAA, String@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- BBBB
+ movl offGlue_methodClassDex(%ecx),%ecx# ecx<- glue->methodClassDex
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ movl offDvmDex_pResStrings(%ecx),%ecx # ecx<- dvmDex->pResStrings
+ movl (%ecx,%eax,4),%eax # eax<- rResString[BBBB]
+ movl rINST_FULL,%ecx
+ FETCH_INST_WORD(2)
+ testl %eax,%eax # resolved yet?
+ je .LOP_CONST_STRING_resolve
+ SET_VREG(%eax,%ecx) # vAA<- rResString[BBBB]
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_STRING_JUMBO: /* 0x1b */
+/* File: x86/OP_CONST_STRING_JUMBO.S */
+
+ /* const/string vAA, String@BBBBBBBB */
+ GET_GLUE(%ecx)
+ movl 2(rPC),%eax # eax<- BBBBBBBB
+ movl offGlue_methodClassDex(%ecx),%ecx# ecx<- glue->methodClassDex
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ movl offDvmDex_pResStrings(%ecx),%ecx # ecx<- dvmDex->pResStrings
+ movl (%ecx,%eax,4),%eax # eax<- rResString[BBBB]
+ movl rINST_FULL,%ecx
+ FETCH_INST_WORD(3)
+ testl %eax,%eax # resolved yet?
+ je .LOP_CONST_STRING_JUMBO_resolve
+ SET_VREG(%eax,%ecx) # vAA<- rResString[BBBB]
+ ADVANCE_PC(3)
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CONST_CLASS: /* 0x1c */
+/* File: x86/OP_CONST_CLASS.S */
+
+ /* const/class vAA, Class@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- BBBB
+ movl offGlue_methodClassDex(%ecx),%ecx# ecx<- glue->methodClassDex
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ movl offDvmDex_pResClasses(%ecx),%ecx # ecx<- dvmDex->pResClasses
+ movl (%ecx,%eax,4),%eax # eax<- rResClasses[BBBB]
+ movl rINST_FULL,%ecx
+ FETCH_INST_WORD(2)
+ testl %eax,%eax # resolved yet?
+ je .LOP_CONST_CLASS_resolve
+ SET_VREG(%eax,%ecx) # vAA<- rResClasses[BBBB]
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MONITOR_ENTER: /* 0x1d */
+/* File: x86/OP_MONITOR_ENTER.S */
+ /*
+ * Synchronize on an object.
+ */
+ /* monitor-enter vAA */
+ GET_GLUE(%ecx)
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG(%eax,rINST_FULL) # eax<- vAA
+ movl offGlue_self(%ecx),%ecx # ecx<- glue->self
+ FETCH_INST_WORD(1)
+ testl %eax,%eax # null object?
+ EXPORT_PC() # need for precise GC, MONITOR_TRACKING
+ jne .LOP_MONITOR_ENTER_continue
+ jmp common_errNullObject
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MONITOR_EXIT: /* 0x1e */
+/* File: x86/OP_MONITOR_EXIT.S */
+ /*
+ * Unlock an object.
+ *
+ * Exceptions that occur when unlocking a monitor need to appear as
+ * if they happened at the following instruction. See the Dalvik
+ * instruction spec.
+ */
+ /* monitor-exit vAA */
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG(%eax,rINST_FULL)
+ GET_GLUE(%ecx)
+ EXPORT_PC()
+ testl %eax,%eax # null object?
+ je .LOP_MONITOR_EXIT_errNullObject # go if so
+ movl offGlue_self(%ecx),%ecx # ecx<- glue->self
+ movl %eax,OUT_ARG1(%esp)
+ SPILL(rPC)
+ movl %ecx,OUT_ARG0(%esp)
+ jmp .LOP_MONITOR_EXIT_continue
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CHECK_CAST: /* 0x1f */
+/* File: x86/OP_CHECK_CAST.S */
+ /*
+ * Check to see if a cast from one class to another is allowed.
+ */
+ /* check-cast vAA, class@BBBB */
+ GET_GLUE(%ecx)
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG(rINST_FULL,rINST_FULL) # rINST_FULL<- vAA (object)
+ movzwl 2(rPC),%eax # eax<- BBBB
+ movl offGlue_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+ testl rINST_FULL,rINST_FULL # is oject null?
+ movl offDvmDex_pResClasses(%ecx),%ecx # ecx<- pDvmDex->pResClasses
+ je .LOP_CHECK_CAST_okay # null obj, cast always succeeds
+ movl (%ecx,%eax,4),%eax # eax<- resolved class
+ movl offObject_clazz(rINST_FULL),%ecx # ecx<- obj->clazz
+ testl %eax,%eax # have we resolved this before?
+ je .LOP_CHECK_CAST_resolve # no, go do it now
+.LOP_CHECK_CAST_resolved:
+ cmpl %eax,%ecx # same class (trivial success)?
+ jne .LOP_CHECK_CAST_fullcheck # no, do full check
+.LOP_CHECK_CAST_okay:
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INSTANCE_OF: /* 0x20 */
+/* File: x86/OP_INSTANCE_OF.S */
+ /*
+ * Check to see if an object reference is an instance of a class.
+ *
+ * Most common situation is a non-null object, being compared against
+ * an already-resolved class.
+ */
+ /* instance-of vA, vB, class@CCCC */
+ movzbl rINST_HI,%eax # eax<- BA
+ sarl $4,%eax # eax<- B
+ GET_VREG(%eax,%eax) # eax<- vB (obj)
+ GET_GLUE(%ecx)
+ testl %eax,%eax # object null?
+ movl offGlue_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+ SPILL(rPC)
+ je .LOP_INSTANCE_OF_store # null obj, not instance, store it
+ movzwl 2(rPC),rPC # rPC<- CCCC
+ movl offDvmDex_pResClasses(%ecx),%ecx # ecx<- pDvmDex->pResClasses
+ movl (%ecx,rPC,4),%ecx # ecx<- resolved class
+ movl offObject_clazz(%eax),%eax # eax<- obj->clazz
+ testl %ecx,%ecx # have we resolved this before?
+ je .LOP_INSTANCE_OF_resolve # not resolved, do it now
+.LOP_INSTANCE_OF_resolved: # eax<- obj->clazz, ecx<- resolved class
+ cmpl %eax,%ecx # same class (trivial success)?
+ je .LOP_INSTANCE_OF_trivial # yes, trivial finish
+ jmp .LOP_INSTANCE_OF_fullcheck # no, do full check
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ARRAY_LENGTH: /* 0x21 */
+/* File: x86/OP_ARRAY_LENGTH.S */
+ /*
+ * Return the length of an array.
+ */
+ movzbl rINST_HI,%eax # eax<- BA
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ GET_VREG(%ecx,rINST_FULL) # ecx<- vB (object ref)
+ andb $0xf,%al # eax<- A
+ testl %ecx,%ecx # is null?
+ je common_errNullObject
+ FETCH_INST_WORD(1)
+ movl offArrayObject_length(%ecx),%ecx
+ ADVANCE_PC(1)
+ SET_VREG(%ecx,%eax)
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEW_INSTANCE: /* 0x22 */
+/* File: x86/OP_NEW_INSTANCE.S */
+ /*
+ * Create a new instance of a class.
+ */
+ /* new-instance vAA, class@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- BBBB
+ movl offGlue_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ movl offDvmDex_pResClasses(%ecx),%ecx # ecx<- pDvmDex->pResClasses
+ EXPORT_PC()
+ movl (%ecx,%eax,4),%ecx # ecx<- resolved class
+ SPILL(rPC)
+ testl %ecx,%ecx # resolved?
+ je .LOP_NEW_INSTANCE_resolve # no, go do it
+.LOP_NEW_INSTANCE_resolved: # on entry, ecx<- class
+ cmpb $CLASS_INITIALIZED,offClassObject_status(%ecx)
+ je .LOP_NEW_INSTANCE_initialized
+ jmp .LOP_NEW_INSTANCE_needinit
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEW_ARRAY: /* 0x23 */
+/* File: x86/OP_NEW_ARRAY.S */
+ /*
+ * Allocate an array of objects, specified with the array class
+ * and a count.
+ *
+ * The verifier guarantees that this is an array class, so we don't
+ * check for it here.
+ */
+ /* new-array vA, vB, class@CCCC */
+ GET_GLUE(%ecx)
+ EXPORT_PC()
+ movl offGlue_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+ movzwl 2(rPC),%eax # eax<- CCCC
+ movl offDvmDex_pResClasses(%ecx),%ecx # ecx<- pDvmDex->pResClasses
+ movl (%ecx,%eax,4),%ecx # ecx<- resolved class
+ movzbl rINST_HI,%eax
+ sarl $4,%eax # eax<- B
+ GET_VREG(%eax,%eax) # eax<- vB (array length)
+ movzbl rINST_HI,rINST_FULL
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ testl %eax,%eax
+ js common_errNegativeArraySize # bail
+ testl %ecx,%ecx # already resolved?
+ jne .LOP_NEW_ARRAY_finish # yes, fast path
+ jmp .LOP_NEW_ARRAY_resolve # resolve now
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FILLED_NEW_ARRAY: /* 0x24 */
+/* File: x86/OP_FILLED_NEW_ARRAY.S */
+ /*
+ * Create a new array with elements filled from registers.
+ *
+ * for: filled-new-array, filled-new-array/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+ GET_GLUE(%eax)
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA or BA
+ movl offGlue_methodClassDex(%eax),%eax # eax<- pDvmDex
+ movzwl 2(rPC),%ecx # ecx<- BBBB
+ movl offDvmDex_pResClasses(%eax),%eax # eax<- pDvmDex->pResClasses
+ SPILL(rPC)
+ movl (%eax,%ecx,4),%eax # eax<- resolved class
+ EXPORT_PC()
+ testl %eax,%eax # already resolved?
+ jne .LOP_FILLED_NEW_ARRAY_continue # yes, continue
+ # less frequent path, so we'll redo some work
+ GET_GLUE(%eax)
+ movl $0,OUT_ARG2(%esp) # arg2<- false
+ movl %ecx,OUT_ARG1(%esp) # arg1<- BBBB
+ movl offGlue_method(%eax),%eax # eax<- glue->method
+ jmp .LOP_FILLED_NEW_ARRAY_more
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FILLED_NEW_ARRAY_RANGE: /* 0x25 */
+/* File: x86/OP_FILLED_NEW_ARRAY_RANGE.S */
+/* File: x86/OP_FILLED_NEW_ARRAY.S */
+ /*
+ * Create a new array with elements filled from registers.
+ *
+ * for: filled-new-array, filled-new-array/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+ GET_GLUE(%eax)
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA or BA
+ movl offGlue_methodClassDex(%eax),%eax # eax<- pDvmDex
+ movzwl 2(rPC),%ecx # ecx<- BBBB
+ movl offDvmDex_pResClasses(%eax),%eax # eax<- pDvmDex->pResClasses
+ SPILL(rPC)
+ movl (%eax,%ecx,4),%eax # eax<- resolved class
+ EXPORT_PC()
+ testl %eax,%eax # already resolved?
+ jne .LOP_FILLED_NEW_ARRAY_RANGE_continue # yes, continue
+ # less frequent path, so we'll redo some work
+ GET_GLUE(%eax)
+ movl $0,OUT_ARG2(%esp) # arg2<- false
+ movl %ecx,OUT_ARG1(%esp) # arg1<- BBBB
+ movl offGlue_method(%eax),%eax # eax<- glue->method
+ jmp .LOP_FILLED_NEW_ARRAY_RANGE_more
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FILL_ARRAY_DATA: /* 0x26 */
+/* File: x86/OP_FILL_ARRAY_DATA.S */
+ /* fill-array-data vAA, +BBBBBBBB */
+ movl 2(rPC),%ecx # ecx<- BBBBbbbb
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ leal (rPC,%ecx,2),%ecx # ecx<- PC + BBBBbbbb*2
+ GET_VREG(%eax,rINST_FULL)
+ SPILL(rPC)
+ EXPORT_PC()
+ movl %eax,OUT_ARG0(%esp)
+ movl %ecx,OUT_ARG1(%esp)
+ call dvmInterpHandleFillArrayData
+ UNSPILL(rPC)
+ FETCH_INST_WORD(3)
+ testl %eax,%eax # exception thrown?
+ je common_exceptionThrown
+ ADVANCE_PC(3)
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_THROW: /* 0x27 */
+/* File: x86/OP_THROW.S */
+ /*
+ * Throw an exception object in the current thread.
+ */
+ /* throw vAA */
+ GET_GLUE(%ecx)
+ EXPORT_PC()
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG(%eax,rINST_FULL) # eax<- exception object
+ movl offGlue_self(%ecx),%ecx # ecx<- glue->self
+ testl %eax,%eax # null object?
+ je common_errNullObject
+ movl %eax,offThread_exception(%ecx) # thread->exception<- obj
+ jmp common_exceptionThrown
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_GOTO: /* 0x28 */
+/* File: x86/OP_GOTO.S */
+ /*
+ * Unconditional branch, 8-bit offset.
+ *
+ * The branch distance is a signed code-unit offset, which we need to
+ * double to get a byte offset.
+ */
+ /* goto +AA */
+ movsbl rINST_HI,rINST_FULL # ebx<- ssssssAA
+ testl rINST_FULL,rINST_FULL # test for <0
+ js common_backwardBranch
+ movl rINST_FULL,%eax
+ FETCH_INST_INDEXED(%eax)
+ ADVANCE_PC_INDEXED(%eax)
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_GOTO_16: /* 0x29 */
+/* File: x86/OP_GOTO_16.S */
+ /*
+ * Unconditional branch, 16-bit offset.
+ *
+ * The branch distance is a signed code-unit offset
+ */
+ /* goto/16 +AAAA */
+ movswl 2(rPC),rINST_FULL # rINST_FULL<- ssssAAAA
+ testl rINST_FULL,rINST_FULL # test for <0
+ js common_backwardBranch
+ movl rINST_FULL,%eax
+ FETCH_INST_INDEXED(%eax)
+ ADVANCE_PC_INDEXED(%eax)
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_GOTO_32: /* 0x2a */
+/* File: x86/OP_GOTO_32.S */
+ /*
+ * Unconditional branch, 32-bit offset.
+ *
+ * The branch distance is a signed code-unit offset.
+ *
+ * Unlike most opcodes, this one is allowed to branch to itself, so
+ * our "backward branch" test must be "<=0" instead of "<0".
+ */
+ /* goto/32 AAAAAAAA */
+ movl 2(rPC),rINST_FULL # rINST_FULL<- AAAAAAAA
+ cmpl $0,rINST_FULL # test for <= 0
+ jle common_backwardBranch
+ movl rINST_FULL,%eax
+ FETCH_INST_INDEXED(%eax)
+ ADVANCE_PC_INDEXED(%eax)
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_PACKED_SWITCH: /* 0x2b */
+/* File: x86/OP_PACKED_SWITCH.S */
+ /*
+ * Handle a packed-switch or sparse-switch instruction. In both cases
+ * we decode it and hand it off to a helper function.
+ *
+ * We don't really expect backward branches in a switch statement, but
+ * they're perfectly legal, so we check for them here.
+ *
+ * for: packed-switch, sparse-switch
+ */
+ /* op vAA, +BBBB */
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ movl 2(rPC),%ecx # ecx<- BBBBbbbb
+ GET_VREG(%eax,rINST_FULL) # eax<- vAA
+ leal (rPC,%ecx,2),%ecx # ecx<- PC + BBBBbbbb*2
+ movl %eax,OUT_ARG1(%esp) # ARG1<- vAA
+ movl %ecx,OUT_ARG0(%esp) # ARG0<- switchData
+ SPILL(rPC)
+ call dvmInterpHandlePackedSwitch
+ UNSPILL(rPC)
+ testl %eax,%eax
+ movl %eax,rINST_FULL # set up word offset
+ jle common_backwardBranch # check on special actions
+ ADVANCE_PC_INDEXED(rINST_FULL)
+ FETCH_INST()
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPARSE_SWITCH: /* 0x2c */
+/* File: x86/OP_SPARSE_SWITCH.S */
+/* File: x86/OP_PACKED_SWITCH.S */
+ /*
+ * Handle a packed-switch or sparse-switch instruction. In both cases
+ * we decode it and hand it off to a helper function.
+ *
+ * We don't really expect backward branches in a switch statement, but
+ * they're perfectly legal, so we check for them here.
+ *
+ * for: packed-switch, sparse-switch
+ */
+ /* op vAA, +BBBB */
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ movl 2(rPC),%ecx # ecx<- BBBBbbbb
+ GET_VREG(%eax,rINST_FULL) # eax<- vAA
+ leal (rPC,%ecx,2),%ecx # ecx<- PC + BBBBbbbb*2
+ movl %eax,OUT_ARG1(%esp) # ARG1<- vAA
+ movl %ecx,OUT_ARG0(%esp) # ARG0<- switchData
+ SPILL(rPC)
+ call dvmInterpHandleSparseSwitch
+ UNSPILL(rPC)
+ testl %eax,%eax
+ movl %eax,rINST_FULL # set up word offset
+ jle common_backwardBranch # check on special actions
+ ADVANCE_PC_INDEXED(rINST_FULL)
+ FETCH_INST()
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CMPL_FLOAT: /* 0x2d */
+/* File: x86/OP_CMPL_FLOAT.S */
+/* File: x86/OP_CMPG_DOUBLE.S */
+ /* float/double_cmp[gl] vAA, vBB, vCC */
+ movzbl 3(rPC),%eax # eax<- CC
+ movzbl 2(rPC),%ecx # ecx<- BB
+ .if 0
+ fldl (rFP,%eax,4)
+ fldl (rFP,%ecx,4)
+ .else
+ flds (rFP,%eax,4)
+ flds (rFP,%ecx,4)
+ .endif
+ movzbl rINST_HI,rINST_FULL
+ xorl %ecx,%ecx
+ fucompp # z if equal, p set if NaN, c set if st0 < st1
+ fnstsw %ax
+ sahf
+ movl rINST_FULL,%eax
+ FETCH_INST_WORD(2)
+ jp .LOP_CMPL_FLOAT_isNaN
+ je .LOP_CMPL_FLOAT_finish
+ sbbl %ecx,%ecx
+ jb .LOP_CMPL_FLOAT_finish
+ incl %ecx
+.LOP_CMPL_FLOAT_finish:
+ SET_VREG(%ecx,%eax)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CMPG_FLOAT: /* 0x2e */
+/* File: x86/OP_CMPG_FLOAT.S */
+/* File: x86/OP_CMPG_DOUBLE.S */
+ /* float/double_cmp[gl] vAA, vBB, vCC */
+ movzbl 3(rPC),%eax # eax<- CC
+ movzbl 2(rPC),%ecx # ecx<- BB
+ .if 0
+ fldl (rFP,%eax,4)
+ fldl (rFP,%ecx,4)
+ .else
+ flds (rFP,%eax,4)
+ flds (rFP,%ecx,4)
+ .endif
+ movzbl rINST_HI,rINST_FULL
+ xorl %ecx,%ecx
+ fucompp # z if equal, p set if NaN, c set if st0 < st1
+ fnstsw %ax
+ sahf
+ movl rINST_FULL,%eax
+ FETCH_INST_WORD(2)
+ jp .LOP_CMPG_FLOAT_isNaN
+ je .LOP_CMPG_FLOAT_finish
+ sbbl %ecx,%ecx
+ jb .LOP_CMPG_FLOAT_finish
+ incl %ecx
+.LOP_CMPG_FLOAT_finish:
+ SET_VREG(%ecx,%eax)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CMPL_DOUBLE: /* 0x2f */
+/* File: x86/OP_CMPL_DOUBLE.S */
+/* File: x86/OP_CMPG_DOUBLE.S */
+ /* float/double_cmp[gl] vAA, vBB, vCC */
+ movzbl 3(rPC),%eax # eax<- CC
+ movzbl 2(rPC),%ecx # ecx<- BB
+ .if 1
+ fldl (rFP,%eax,4)
+ fldl (rFP,%ecx,4)
+ .else
+ flds (rFP,%eax,4)
+ flds (rFP,%ecx,4)
+ .endif
+ movzbl rINST_HI,rINST_FULL
+ xorl %ecx,%ecx
+ fucompp # z if equal, p set if NaN, c set if st0 < st1
+ fnstsw %ax
+ sahf
+ movl rINST_FULL,%eax
+ FETCH_INST_WORD(2)
+ jp .LOP_CMPL_DOUBLE_isNaN
+ je .LOP_CMPL_DOUBLE_finish
+ sbbl %ecx,%ecx
+ jb .LOP_CMPL_DOUBLE_finish
+ incl %ecx
+.LOP_CMPL_DOUBLE_finish:
+ SET_VREG(%ecx,%eax)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CMPG_DOUBLE: /* 0x30 */
+/* File: x86/OP_CMPG_DOUBLE.S */
+ /* float/double_cmp[gl] vAA, vBB, vCC */
+ movzbl 3(rPC),%eax # eax<- CC
+ movzbl 2(rPC),%ecx # ecx<- BB
+ .if 1
+ fldl (rFP,%eax,4)
+ fldl (rFP,%ecx,4)
+ .else
+ flds (rFP,%eax,4)
+ flds (rFP,%ecx,4)
+ .endif
+ movzbl rINST_HI,rINST_FULL
+ xorl %ecx,%ecx
+ fucompp # z if equal, p set if NaN, c set if st0 < st1
+ fnstsw %ax
+ sahf
+ movl rINST_FULL,%eax
+ FETCH_INST_WORD(2)
+ jp .LOP_CMPG_DOUBLE_isNaN
+ je .LOP_CMPG_DOUBLE_finish
+ sbbl %ecx,%ecx
+ jb .LOP_CMPG_DOUBLE_finish
+ incl %ecx
+.LOP_CMPG_DOUBLE_finish:
+ SET_VREG(%ecx,%eax)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_CMP_LONG: /* 0x31 */
+/* File: x86/OP_CMP_LONG.S */
+ /*
+ * Compare two 64-bit values. Puts 0, 1, or -1 into the destination
+ * register based on the results of the comparison.
+ */
+ /* cmp-long vAA, vBB, vCC */
+ movzbl 2(rPC),%ecx # ecx<- BB
+ SPILL(rPC)
+ movzbl 3(rPC),rPC # rPC<- CC
+ GET_VREG_WORD(%eax,%ecx,1) # eax<- v[BB+1]
+ GET_VREG_WORD(%ecx,%ecx,0) # ecx<- v[BB+0]
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ cmpl 4(rFP,rPC,4),%eax
+ jl .LOP_CMP_LONG_smaller
+ jg .LOP_CMP_LONG_bigger
+ sub (rFP,rPC,4),%ecx
+ ja .LOP_CMP_LONG_bigger
+ jb .LOP_CMP_LONG_smaller
+ UNSPILL(rPC)
+ jmp .LOP_CMP_LONG_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_EQ: /* 0x32 */
+/* File: x86/OP_IF_EQ.S */
+/* File: x86/bincmp.S */
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ movzx rINST_HI,%ecx # ecx <- A+
+ andb $0xf,%cl # ecx <- A
+ GET_VREG(%eax,%ecx) # eax <- vA
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ cmpl (rFP,rINST_FULL,4),%eax # compare (vA, vB)
+ movswl 2(rPC),rINST_FULL # Get signed branch offset
+ movl $2,%eax # assume not taken
+ jne 1f
+ testl rINST_FULL,rINST_FULL
+ js common_backwardBranch
+ movl rINST_FULL,%eax
+1:
+ FETCH_INST_INDEXED(%eax)
+ ADVANCE_PC_INDEXED(%eax)
+ GOTO_NEXT
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_NE: /* 0x33 */
+/* File: x86/OP_IF_NE.S */
+/* File: x86/bincmp.S */
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ movzx rINST_HI,%ecx # ecx <- A+
+ andb $0xf,%cl # ecx <- A
+ GET_VREG(%eax,%ecx) # eax <- vA
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ cmpl (rFP,rINST_FULL,4),%eax # compare (vA, vB)
+ movswl 2(rPC),rINST_FULL # Get signed branch offset
+ movl $2,%eax # assume not taken
+ je 1f
+ testl rINST_FULL,rINST_FULL
+ js common_backwardBranch
+ movl rINST_FULL,%eax
+1:
+ FETCH_INST_INDEXED(%eax)
+ ADVANCE_PC_INDEXED(%eax)
+ GOTO_NEXT
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_LT: /* 0x34 */
+/* File: x86/OP_IF_LT.S */
+/* File: x86/bincmp.S */
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ movzx rINST_HI,%ecx # ecx <- A+
+ andb $0xf,%cl # ecx <- A
+ GET_VREG(%eax,%ecx) # eax <- vA
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ cmpl (rFP,rINST_FULL,4),%eax # compare (vA, vB)
+ movswl 2(rPC),rINST_FULL # Get signed branch offset
+ movl $2,%eax # assume not taken
+ jge 1f
+ testl rINST_FULL,rINST_FULL
+ js common_backwardBranch
+ movl rINST_FULL,%eax
+1:
+ FETCH_INST_INDEXED(%eax)
+ ADVANCE_PC_INDEXED(%eax)
+ GOTO_NEXT
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_GE: /* 0x35 */
+/* File: x86/OP_IF_GE.S */
+/* File: x86/bincmp.S */
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ movzx rINST_HI,%ecx # ecx <- A+
+ andb $0xf,%cl # ecx <- A
+ GET_VREG(%eax,%ecx) # eax <- vA
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ cmpl (rFP,rINST_FULL,4),%eax # compare (vA, vB)
+ movswl 2(rPC),rINST_FULL # Get signed branch offset
+ movl $2,%eax # assume not taken
+ jl 1f
+ testl rINST_FULL,rINST_FULL
+ js common_backwardBranch
+ movl rINST_FULL,%eax
+1:
+ FETCH_INST_INDEXED(%eax)
+ ADVANCE_PC_INDEXED(%eax)
+ GOTO_NEXT
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_GT: /* 0x36 */
+/* File: x86/OP_IF_GT.S */
+/* File: x86/bincmp.S */
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ movzx rINST_HI,%ecx # ecx <- A+
+ andb $0xf,%cl # ecx <- A
+ GET_VREG(%eax,%ecx) # eax <- vA
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ cmpl (rFP,rINST_FULL,4),%eax # compare (vA, vB)
+ movswl 2(rPC),rINST_FULL # Get signed branch offset
+ movl $2,%eax # assume not taken
+ jle 1f
+ testl rINST_FULL,rINST_FULL
+ js common_backwardBranch
+ movl rINST_FULL,%eax
+1:
+ FETCH_INST_INDEXED(%eax)
+ ADVANCE_PC_INDEXED(%eax)
+ GOTO_NEXT
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_LE: /* 0x37 */
+/* File: x86/OP_IF_LE.S */
+/* File: x86/bincmp.S */
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ movzx rINST_HI,%ecx # ecx <- A+
+ andb $0xf,%cl # ecx <- A
+ GET_VREG(%eax,%ecx) # eax <- vA
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ cmpl (rFP,rINST_FULL,4),%eax # compare (vA, vB)
+ movswl 2(rPC),rINST_FULL # Get signed branch offset
+ movl $2,%eax # assume not taken
+ jg 1f
+ testl rINST_FULL,rINST_FULL
+ js common_backwardBranch
+ movl rINST_FULL,%eax
+1:
+ FETCH_INST_INDEXED(%eax)
+ ADVANCE_PC_INDEXED(%eax)
+ GOTO_NEXT
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_EQZ: /* 0x38 */
+/* File: x86/OP_IF_EQZ.S */
+/* File: x86/zcmp.S */
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ movzx rINST_HI,%ecx # ecx <- AA
+ cmpl $0,(rFP,%ecx,4) # compare (vA, 0)
+ movswl 2(rPC),rINST_FULL # fetch signed displacement
+ movl $2,%eax # assume branch not taken
+ jne 1f
+ testl rINST_FULL,rINST_FULL
+ js common_backwardBranch
+ movl rINST_FULL,%eax
+1:
+ FETCH_INST_INDEXED(%eax)
+ ADVANCE_PC_INDEXED(%eax)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_NEZ: /* 0x39 */
+/* File: x86/OP_IF_NEZ.S */
+/* File: x86/zcmp.S */
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ movzx rINST_HI,%ecx # ecx <- AA
+ cmpl $0,(rFP,%ecx,4) # compare (vA, 0)
+ movswl 2(rPC),rINST_FULL # fetch signed displacement
+ movl $2,%eax # assume branch not taken
+ je 1f
+ testl rINST_FULL,rINST_FULL
+ js common_backwardBranch
+ movl rINST_FULL,%eax
+1:
+ FETCH_INST_INDEXED(%eax)
+ ADVANCE_PC_INDEXED(%eax)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_LTZ: /* 0x3a */
+/* File: x86/OP_IF_LTZ.S */
+/* File: x86/zcmp.S */
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ movzx rINST_HI,%ecx # ecx <- AA
+ cmpl $0,(rFP,%ecx,4) # compare (vA, 0)
+ movswl 2(rPC),rINST_FULL # fetch signed displacement
+ movl $2,%eax # assume branch not taken
+ jge 1f
+ testl rINST_FULL,rINST_FULL
+ js common_backwardBranch
+ movl rINST_FULL,%eax
+1:
+ FETCH_INST_INDEXED(%eax)
+ ADVANCE_PC_INDEXED(%eax)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_GEZ: /* 0x3b */
+/* File: x86/OP_IF_GEZ.S */
+/* File: x86/zcmp.S */
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ movzx rINST_HI,%ecx # ecx <- AA
+ cmpl $0,(rFP,%ecx,4) # compare (vA, 0)
+ movswl 2(rPC),rINST_FULL # fetch signed displacement
+ movl $2,%eax # assume branch not taken
+ jl 1f
+ testl rINST_FULL,rINST_FULL
+ js common_backwardBranch
+ movl rINST_FULL,%eax
+1:
+ FETCH_INST_INDEXED(%eax)
+ ADVANCE_PC_INDEXED(%eax)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_GTZ: /* 0x3c */
+/* File: x86/OP_IF_GTZ.S */
+/* File: x86/zcmp.S */
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ movzx rINST_HI,%ecx # ecx <- AA
+ cmpl $0,(rFP,%ecx,4) # compare (vA, 0)
+ movswl 2(rPC),rINST_FULL # fetch signed displacement
+ movl $2,%eax # assume branch not taken
+ jle 1f
+ testl rINST_FULL,rINST_FULL
+ js common_backwardBranch
+ movl rINST_FULL,%eax
+1:
+ FETCH_INST_INDEXED(%eax)
+ ADVANCE_PC_INDEXED(%eax)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IF_LEZ: /* 0x3d */
+/* File: x86/OP_IF_LEZ.S */
+/* File: x86/zcmp.S */
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ movzx rINST_HI,%ecx # ecx <- AA
+ cmpl $0,(rFP,%ecx,4) # compare (vA, 0)
+ movswl 2(rPC),rINST_FULL # fetch signed displacement
+ movl $2,%eax # assume branch not taken
+ jg 1f
+ testl rINST_FULL,rINST_FULL
+ js common_backwardBranch
+ movl rINST_FULL,%eax
+1:
+ FETCH_INST_INDEXED(%eax)
+ ADVANCE_PC_INDEXED(%eax)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_3E: /* 0x3e */
+/* File: x86/OP_UNUSED_3E.S */
+/* File: x86/unused.S */
+ jmp common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_3F: /* 0x3f */
+/* File: x86/OP_UNUSED_3F.S */
+/* File: x86/unused.S */
+ jmp common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_40: /* 0x40 */
+/* File: x86/OP_UNUSED_40.S */
+/* File: x86/unused.S */
+ jmp common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_41: /* 0x41 */
+/* File: x86/OP_UNUSED_41.S */
+/* File: x86/unused.S */
+ jmp common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_42: /* 0x42 */
+/* File: x86/OP_UNUSED_42.S */
+/* File: x86/unused.S */
+ jmp common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_43: /* 0x43 */
+/* File: x86/OP_UNUSED_43.S */
+/* File: x86/unused.S */
+ jmp common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET: /* 0x44 */
+/* File: x86/OP_AGET.S */
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG(%eax,%eax) # eax<- vBB (array object)
+ GET_VREG(%ecx,%ecx) # ecs<- vCC (requested index)
+ testl %eax,%eax # null array object?
+ je common_errNullObject # bail if so
+ cmpl offArrayObject_length(%eax),%ecx
+ jae common_errArrayIndex # index >= length, bail
+ movl offArrayObject_contents(%eax,%ecx,4),%eax
+ movl rINST_FULL,%ecx
+ FETCH_INST_WORD(2)
+ SET_VREG(%eax,%ecx)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_WIDE: /* 0x45 */
+/* File: x86/OP_AGET_WIDE.S */
+ /*
+ * Array get, 64 bits. vAA <- vBB[vCC].
+ *
+ */
+ /* op vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG(%eax,%eax) # eax<- vBB (array object)
+ GET_VREG(%ecx,%ecx) # ecs<- vCC (requested index)
+ testl %eax,%eax # null array object?
+ je common_errNullObject # bail if so
+ cmpl offArrayObject_length(%eax),%ecx
+ jb .LOP_AGET_WIDE_finish # index < length, OK
+ jmp common_errArrayIndex # index >= length, bail
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_OBJECT: /* 0x46 */
+/* File: x86/OP_AGET_OBJECT.S */
+/* File: x86/OP_AGET.S */
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG(%eax,%eax) # eax<- vBB (array object)
+ GET_VREG(%ecx,%ecx) # ecs<- vCC (requested index)
+ testl %eax,%eax # null array object?
+ je common_errNullObject # bail if so
+ cmpl offArrayObject_length(%eax),%ecx
+ jae common_errArrayIndex # index >= length, bail
+ movl offArrayObject_contents(%eax,%ecx,4),%eax
+ movl rINST_FULL,%ecx
+ FETCH_INST_WORD(2)
+ SET_VREG(%eax,%ecx)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_BOOLEAN: /* 0x47 */
+/* File: x86/OP_AGET_BOOLEAN.S */
+/* File: x86/OP_AGET.S */
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG(%eax,%eax) # eax<- vBB (array object)
+ GET_VREG(%ecx,%ecx) # ecs<- vCC (requested index)
+ testl %eax,%eax # null array object?
+ je common_errNullObject # bail if so
+ cmpl offArrayObject_length(%eax),%ecx
+ jae common_errArrayIndex # index >= length, bail
+ movzbl offArrayObject_contents(%eax,%ecx,1),%eax
+ movl rINST_FULL,%ecx
+ FETCH_INST_WORD(2)
+ SET_VREG(%eax,%ecx)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_BYTE: /* 0x48 */
+/* File: x86/OP_AGET_BYTE.S */
+/* File: x86/OP_AGET.S */
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG(%eax,%eax) # eax<- vBB (array object)
+ GET_VREG(%ecx,%ecx) # ecs<- vCC (requested index)
+ testl %eax,%eax # null array object?
+ je common_errNullObject # bail if so
+ cmpl offArrayObject_length(%eax),%ecx
+ jae common_errArrayIndex # index >= length, bail
+ movsbl offArrayObject_contents(%eax,%ecx,1),%eax
+ movl rINST_FULL,%ecx
+ FETCH_INST_WORD(2)
+ SET_VREG(%eax,%ecx)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_CHAR: /* 0x49 */
+/* File: x86/OP_AGET_CHAR.S */
+/* File: x86/OP_AGET.S */
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG(%eax,%eax) # eax<- vBB (array object)
+ GET_VREG(%ecx,%ecx) # ecs<- vCC (requested index)
+ testl %eax,%eax # null array object?
+ je common_errNullObject # bail if so
+ cmpl offArrayObject_length(%eax),%ecx
+ jae common_errArrayIndex # index >= length, bail
+ movzwl offArrayObject_contents(%eax,%ecx,2),%eax
+ movl rINST_FULL,%ecx
+ FETCH_INST_WORD(2)
+ SET_VREG(%eax,%ecx)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AGET_SHORT: /* 0x4a */
+/* File: x86/OP_AGET_SHORT.S */
+/* File: x86/OP_AGET.S */
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG(%eax,%eax) # eax<- vBB (array object)
+ GET_VREG(%ecx,%ecx) # ecs<- vCC (requested index)
+ testl %eax,%eax # null array object?
+ je common_errNullObject # bail if so
+ cmpl offArrayObject_length(%eax),%ecx
+ jae common_errArrayIndex # index >= length, bail
+ movswl offArrayObject_contents(%eax,%ecx,2),%eax
+ movl rINST_FULL,%ecx
+ FETCH_INST_WORD(2)
+ SET_VREG(%eax,%ecx)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT: /* 0x4b */
+/* File: x86/OP_APUT.S */
+ /*
+ * Array put, 32 bits or less. vBB[vCC] <- vAA
+ *
+ * for: aput, aput-object, aput-boolean, aput-byte, aput-char, aput-short
+ */
+ /* op vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG(%eax,%eax) # eax<- vBB (array object)
+ GET_VREG(%ecx,%ecx) # ecs<- vCC (requested index)
+ testl %eax,%eax # null array object?
+ je common_errNullObject # bail if so
+ cmpl offArrayObject_length(%eax),%ecx
+ jae common_errArrayIndex # index >= length, bail
+ leal offArrayObject_contents(%eax,%ecx,4),%eax
+ GET_VREG(%ecx,rINST_FULL)
+ FETCH_INST_WORD(2)
+ movl %ecx,(%eax)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_WIDE: /* 0x4c */
+/* File: x86/OP_APUT_WIDE.S */
+ /*
+ * Array put, 64 bits. vBB[vCC]<-vAA.
+ *
+ */
+ /* op vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG(%eax,%eax) # eax<- vBB (array object)
+ GET_VREG(%ecx,%ecx) # ecs<- vCC (requested index)
+ testl %eax,%eax # null array object?
+ je common_errNullObject # bail if so
+ cmpl offArrayObject_length(%eax),%ecx
+ jb .LOP_APUT_WIDE_finish # index < length, OK
+ jmp common_errArrayIndex # index >= length, bail
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_OBJECT: /* 0x4d */
+/* File: x86/OP_APUT_OBJECT.S */
+ /*
+ * Array put, 32 bits or less. vBB[vCC] <- vAA
+ *
+ * for: aput, aput-object, aput-boolean, aput-byte, aput-char, aput-short
+ */
+ /* op vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG(%eax,%eax) # eax<- vBB (array object)
+ GET_VREG(%ecx,%ecx) # ecs<- vCC (requested index)
+ GET_VREG(rINST_FULL,rINST_FULL) # rINST_FULL<- vAA
+ testl %eax,%eax # null array object?
+ je common_errNullObject # bail if so
+ cmpl offArrayObject_length(%eax),%ecx
+ jb .LOP_APUT_OBJECT_continue
+ jmp common_errArrayIndex # index >= length, bail
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_BOOLEAN: /* 0x4e */
+/* File: x86/OP_APUT_BOOLEAN.S */
+/* File: x86/OP_APUT.S */
+ /*
+ * Array put, 32 bits or less. vBB[vCC] <- vAA
+ *
+ * for: aput, aput-object, aput-boolean, aput-byte, aput-char, aput-short
+ */
+ /* op vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG(%eax,%eax) # eax<- vBB (array object)
+ GET_VREG(%ecx,%ecx) # ecs<- vCC (requested index)
+ testl %eax,%eax # null array object?
+ je common_errNullObject # bail if so
+ cmpl offArrayObject_length(%eax),%ecx
+ jae common_errArrayIndex # index >= length, bail
+ leal offArrayObject_contents(%eax,%ecx,1),%eax
+ GET_VREG(%ecx,rINST_FULL)
+ FETCH_INST_WORD(2)
+ movb %cl,(%eax)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_BYTE: /* 0x4f */
+/* File: x86/OP_APUT_BYTE.S */
+/* File: x86/OP_APUT.S */
+ /*
+ * Array put, 32 bits or less. vBB[vCC] <- vAA
+ *
+ * for: aput, aput-object, aput-boolean, aput-byte, aput-char, aput-short
+ */
+ /* op vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG(%eax,%eax) # eax<- vBB (array object)
+ GET_VREG(%ecx,%ecx) # ecs<- vCC (requested index)
+ testl %eax,%eax # null array object?
+ je common_errNullObject # bail if so
+ cmpl offArrayObject_length(%eax),%ecx
+ jae common_errArrayIndex # index >= length, bail
+ leal offArrayObject_contents(%eax,%ecx,1),%eax
+ GET_VREG(%ecx,rINST_FULL)
+ FETCH_INST_WORD(2)
+ movb %cl,(%eax)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_CHAR: /* 0x50 */
+/* File: x86/OP_APUT_CHAR.S */
+/* File: x86/OP_APUT.S */
+ /*
+ * Array put, 32 bits or less. vBB[vCC] <- vAA
+ *
+ * for: aput, aput-object, aput-boolean, aput-byte, aput-char, aput-short
+ */
+ /* op vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG(%eax,%eax) # eax<- vBB (array object)
+ GET_VREG(%ecx,%ecx) # ecs<- vCC (requested index)
+ testl %eax,%eax # null array object?
+ je common_errNullObject # bail if so
+ cmpl offArrayObject_length(%eax),%ecx
+ jae common_errArrayIndex # index >= length, bail
+ leal offArrayObject_contents(%eax,%ecx,2),%eax
+ GET_VREG(%ecx,rINST_FULL)
+ FETCH_INST_WORD(2)
+ movw %cx,(%eax)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_APUT_SHORT: /* 0x51 */
+/* File: x86/OP_APUT_SHORT.S */
+/* File: x86/OP_APUT.S */
+ /*
+ * Array put, 32 bits or less. vBB[vCC] <- vAA
+ *
+ * for: aput, aput-object, aput-boolean, aput-byte, aput-char, aput-short
+ */
+ /* op vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG(%eax,%eax) # eax<- vBB (array object)
+ GET_VREG(%ecx,%ecx) # ecs<- vCC (requested index)
+ testl %eax,%eax # null array object?
+ je common_errNullObject # bail if so
+ cmpl offArrayObject_length(%eax),%ecx
+ jae common_errArrayIndex # index >= length, bail
+ leal offArrayObject_contents(%eax,%ecx,2),%eax
+ GET_VREG(%ecx,rINST_FULL)
+ FETCH_INST_WORD(2)
+ movw %cx,(%eax)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET: /* 0x52 */
+/* File: x86/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ GET_GLUE(%ecx)
+ SPILL(rIBASE) # need another reg
+ movzwl 2(rPC),rIBASE # rIBASE<- 0000CCCC
+ movl offGlue_methodClassDex(%ecx),%eax # eax<- DvmDex
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $4,%ecx # ecx<- B
+ movl offDvmDex_pResFields(%eax),%eax # eax<- pDvmDex->pResFields
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG(%ecx,%ecx) # ecx<- fp[B], the object ptr
+ movl (%eax,rIBASE,4),%eax # resolved entry
+ testl %eax,%eax # is resolved entry null?
+ jne .LOP_IGET_finish # no, already resolved
+ movl rIBASE,OUT_ARG1(%esp) # needed by dvmResolveInstField
+ GET_GLUE(rIBASE)
+ jmp .LOP_IGET_resolve
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_WIDE: /* 0x53 */
+/* File: x86/OP_IGET_WIDE.S */
+ /*
+ * 64-bit instance field get.
+ *
+ */
+ /* op vA, vB, field@CCCC */
+ GET_GLUE(%ecx)
+ SPILL(rIBASE) # need another reg
+ movzwl 2(rPC),rIBASE # rIBASE<- 0000CCCC
+ movl offGlue_methodClassDex(%ecx),%eax # eax<- DvmDex
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $4,%ecx # ecx<- B
+ movl offDvmDex_pResFields(%eax),%eax # eax<- pDvmDex->pResFields
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG(%ecx,%ecx) # ecx<- fp[B], the object ptr
+ movl (%eax,rIBASE,4),%eax # resolved entry
+ testl %eax,%eax # is resolved entry null?
+ jne .LOP_IGET_WIDE_finish # no, already resolved
+ movl rIBASE,OUT_ARG1(%esp) # needed by dvmResolveInstField
+ GET_GLUE(rIBASE)
+ jmp .LOP_IGET_WIDE_resolve
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_OBJECT: /* 0x54 */
+/* File: x86/OP_IGET_OBJECT.S */
+/* File: x86/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ GET_GLUE(%ecx)
+ SPILL(rIBASE) # need another reg
+ movzwl 2(rPC),rIBASE # rIBASE<- 0000CCCC
+ movl offGlue_methodClassDex(%ecx),%eax # eax<- DvmDex
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $4,%ecx # ecx<- B
+ movl offDvmDex_pResFields(%eax),%eax # eax<- pDvmDex->pResFields
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG(%ecx,%ecx) # ecx<- fp[B], the object ptr
+ movl (%eax,rIBASE,4),%eax # resolved entry
+ testl %eax,%eax # is resolved entry null?
+ jne .LOP_IGET_OBJECT_finish # no, already resolved
+ movl rIBASE,OUT_ARG1(%esp) # needed by dvmResolveInstField
+ GET_GLUE(rIBASE)
+ jmp .LOP_IGET_OBJECT_resolve
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_BOOLEAN: /* 0x55 */
+/* File: x86/OP_IGET_BOOLEAN.S */
+/* File: x86/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ GET_GLUE(%ecx)
+ SPILL(rIBASE) # need another reg
+ movzwl 2(rPC),rIBASE # rIBASE<- 0000CCCC
+ movl offGlue_methodClassDex(%ecx),%eax # eax<- DvmDex
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $4,%ecx # ecx<- B
+ movl offDvmDex_pResFields(%eax),%eax # eax<- pDvmDex->pResFields
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG(%ecx,%ecx) # ecx<- fp[B], the object ptr
+ movl (%eax,rIBASE,4),%eax # resolved entry
+ testl %eax,%eax # is resolved entry null?
+ jne .LOP_IGET_BOOLEAN_finish # no, already resolved
+ movl rIBASE,OUT_ARG1(%esp) # needed by dvmResolveInstField
+ GET_GLUE(rIBASE)
+ jmp .LOP_IGET_BOOLEAN_resolve
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_BYTE: /* 0x56 */
+/* File: x86/OP_IGET_BYTE.S */
+/* File: x86/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ GET_GLUE(%ecx)
+ SPILL(rIBASE) # need another reg
+ movzwl 2(rPC),rIBASE # rIBASE<- 0000CCCC
+ movl offGlue_methodClassDex(%ecx),%eax # eax<- DvmDex
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $4,%ecx # ecx<- B
+ movl offDvmDex_pResFields(%eax),%eax # eax<- pDvmDex->pResFields
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG(%ecx,%ecx) # ecx<- fp[B], the object ptr
+ movl (%eax,rIBASE,4),%eax # resolved entry
+ testl %eax,%eax # is resolved entry null?
+ jne .LOP_IGET_BYTE_finish # no, already resolved
+ movl rIBASE,OUT_ARG1(%esp) # needed by dvmResolveInstField
+ GET_GLUE(rIBASE)
+ jmp .LOP_IGET_BYTE_resolve
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_CHAR: /* 0x57 */
+/* File: x86/OP_IGET_CHAR.S */
+/* File: x86/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ GET_GLUE(%ecx)
+ SPILL(rIBASE) # need another reg
+ movzwl 2(rPC),rIBASE # rIBASE<- 0000CCCC
+ movl offGlue_methodClassDex(%ecx),%eax # eax<- DvmDex
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $4,%ecx # ecx<- B
+ movl offDvmDex_pResFields(%eax),%eax # eax<- pDvmDex->pResFields
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG(%ecx,%ecx) # ecx<- fp[B], the object ptr
+ movl (%eax,rIBASE,4),%eax # resolved entry
+ testl %eax,%eax # is resolved entry null?
+ jne .LOP_IGET_CHAR_finish # no, already resolved
+ movl rIBASE,OUT_ARG1(%esp) # needed by dvmResolveInstField
+ GET_GLUE(rIBASE)
+ jmp .LOP_IGET_CHAR_resolve
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_SHORT: /* 0x58 */
+/* File: x86/OP_IGET_SHORT.S */
+/* File: x86/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ GET_GLUE(%ecx)
+ SPILL(rIBASE) # need another reg
+ movzwl 2(rPC),rIBASE # rIBASE<- 0000CCCC
+ movl offGlue_methodClassDex(%ecx),%eax # eax<- DvmDex
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $4,%ecx # ecx<- B
+ movl offDvmDex_pResFields(%eax),%eax # eax<- pDvmDex->pResFields
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG(%ecx,%ecx) # ecx<- fp[B], the object ptr
+ movl (%eax,rIBASE,4),%eax # resolved entry
+ testl %eax,%eax # is resolved entry null?
+ jne .LOP_IGET_SHORT_finish # no, already resolved
+ movl rIBASE,OUT_ARG1(%esp) # needed by dvmResolveInstField
+ GET_GLUE(rIBASE)
+ jmp .LOP_IGET_SHORT_resolve
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT: /* 0x59 */
+/* File: x86/OP_IPUT.S */
+
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ GET_GLUE(%ecx)
+ SPILL(rIBASE) # need another reg
+ movzwl 2(rPC),rIBASE # rIBASE<- 0000CCCC
+ movl offGlue_methodClassDex(%ecx),%eax # eax<- DvmDex
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $4,%ecx # ecx<- B
+ movl offDvmDex_pResFields(%eax),%eax # eax<- pDvmDex->pResFields
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG(%ecx,%ecx) # ecx<- fp[B], the object ptr
+ movl (%eax,rIBASE,4),%eax # resolved entry
+ testl %eax,%eax # is resolved entry null?
+ jne .LOP_IPUT_finish # no, already resolved
+ movl rIBASE,OUT_ARG1(%esp)
+ GET_GLUE(rIBASE)
+ jmp .LOP_IPUT_resolve
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_WIDE: /* 0x5a */
+/* File: x86/OP_IPUT_WIDE.S */
+ /*
+ * 64-bit instance field put.
+ *
+ */
+ /* op vA, vB, field@CCCC */
+ GET_GLUE(%ecx)
+ SPILL(rIBASE) # need another reg
+ movzwl 2(rPC),rIBASE # rIBASE<- 0000CCCC
+ movl offGlue_methodClassDex(%ecx),%eax # eax<- DvmDex
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $4,%ecx # ecx<- B
+ movl offDvmDex_pResFields(%eax),%eax # eax<- pDvmDex->pResFields
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG(%ecx,%ecx) # ecx<- fp[B], the object ptr
+ movl (%eax,rIBASE,4),%eax # resolved entry
+ testl %eax,%eax # is resolved entry null?
+ jne .LOP_IPUT_WIDE_finish # no, already resolved
+ movl rIBASE,OUT_ARG1(%esp)
+ GET_GLUE(rIBASE)
+ jmp .LOP_IPUT_WIDE_resolve
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_OBJECT: /* 0x5b */
+/* File: x86/OP_IPUT_OBJECT.S */
+ /*
+ * Object field put.
+ *
+ * for: iput-object
+ */
+ /* op vA, vB, field@CCCC */
+ GET_GLUE(%ecx)
+ SPILL(rIBASE) # need another reg
+ movzwl 2(rPC),rIBASE # rIBASE<- 0000CCCC
+ movl offGlue_methodClassDex(%ecx),%eax # eax<- DvmDex
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $4,%ecx # ecx<- B
+ movl offDvmDex_pResFields(%eax),%eax # eax<- pDvmDex->pResFields
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG(%ecx,%ecx) # ecx<- fp[B], the object ptr
+ movl (%eax,rIBASE,4),%eax # resolved entry
+ testl %eax,%eax # is resolved entry null?
+ jne .LOP_IPUT_OBJECT_finish # no, already resolved
+ movl rIBASE,OUT_ARG1(%esp)
+ GET_GLUE(rIBASE)
+ jmp .LOP_IPUT_OBJECT_resolve
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_BOOLEAN: /* 0x5c */
+/* File: x86/OP_IPUT_BOOLEAN.S */
+/* File: x86/OP_IPUT.S */
+
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ GET_GLUE(%ecx)
+ SPILL(rIBASE) # need another reg
+ movzwl 2(rPC),rIBASE # rIBASE<- 0000CCCC
+ movl offGlue_methodClassDex(%ecx),%eax # eax<- DvmDex
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $4,%ecx # ecx<- B
+ movl offDvmDex_pResFields(%eax),%eax # eax<- pDvmDex->pResFields
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG(%ecx,%ecx) # ecx<- fp[B], the object ptr
+ movl (%eax,rIBASE,4),%eax # resolved entry
+ testl %eax,%eax # is resolved entry null?
+ jne .LOP_IPUT_BOOLEAN_finish # no, already resolved
+ movl rIBASE,OUT_ARG1(%esp)
+ GET_GLUE(rIBASE)
+ jmp .LOP_IPUT_BOOLEAN_resolve
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_BYTE: /* 0x5d */
+/* File: x86/OP_IPUT_BYTE.S */
+/* File: x86/OP_IPUT.S */
+
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ GET_GLUE(%ecx)
+ SPILL(rIBASE) # need another reg
+ movzwl 2(rPC),rIBASE # rIBASE<- 0000CCCC
+ movl offGlue_methodClassDex(%ecx),%eax # eax<- DvmDex
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $4,%ecx # ecx<- B
+ movl offDvmDex_pResFields(%eax),%eax # eax<- pDvmDex->pResFields
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG(%ecx,%ecx) # ecx<- fp[B], the object ptr
+ movl (%eax,rIBASE,4),%eax # resolved entry
+ testl %eax,%eax # is resolved entry null?
+ jne .LOP_IPUT_BYTE_finish # no, already resolved
+ movl rIBASE,OUT_ARG1(%esp)
+ GET_GLUE(rIBASE)
+ jmp .LOP_IPUT_BYTE_resolve
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_CHAR: /* 0x5e */
+/* File: x86/OP_IPUT_CHAR.S */
+/* File: x86/OP_IPUT.S */
+
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ GET_GLUE(%ecx)
+ SPILL(rIBASE) # need another reg
+ movzwl 2(rPC),rIBASE # rIBASE<- 0000CCCC
+ movl offGlue_methodClassDex(%ecx),%eax # eax<- DvmDex
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $4,%ecx # ecx<- B
+ movl offDvmDex_pResFields(%eax),%eax # eax<- pDvmDex->pResFields
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG(%ecx,%ecx) # ecx<- fp[B], the object ptr
+ movl (%eax,rIBASE,4),%eax # resolved entry
+ testl %eax,%eax # is resolved entry null?
+ jne .LOP_IPUT_CHAR_finish # no, already resolved
+ movl rIBASE,OUT_ARG1(%esp)
+ GET_GLUE(rIBASE)
+ jmp .LOP_IPUT_CHAR_resolve
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_SHORT: /* 0x5f */
+/* File: x86/OP_IPUT_SHORT.S */
+/* File: x86/OP_IPUT.S */
+
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ GET_GLUE(%ecx)
+ SPILL(rIBASE) # need another reg
+ movzwl 2(rPC),rIBASE # rIBASE<- 0000CCCC
+ movl offGlue_methodClassDex(%ecx),%eax # eax<- DvmDex
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $4,%ecx # ecx<- B
+ movl offDvmDex_pResFields(%eax),%eax # eax<- pDvmDex->pResFields
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG(%ecx,%ecx) # ecx<- fp[B], the object ptr
+ movl (%eax,rIBASE,4),%eax # resolved entry
+ testl %eax,%eax # is resolved entry null?
+ jne .LOP_IPUT_SHORT_finish # no, already resolved
+ movl rIBASE,OUT_ARG1(%esp)
+ GET_GLUE(rIBASE)
+ jmp .LOP_IPUT_SHORT_resolve
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET: /* 0x60 */
+/* File: x86/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_methodClassDex(%ecx),%ecx # ecx<- DvmDex
+ movl offDvmDex_pResFields(%ecx),%ecx # ecx<- dvmDex->pResFields
+ movl (%ecx,%eax,4),%eax # eax<- resolved StaticField ptr
+ testl %eax,%eax # resolved entry null?
+ je .LOP_SGET_resolve # if not, make it so
+.LOP_SGET_finish: # field ptr in eax
+ movl offStaticField_value(%eax),%eax
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ SET_VREG(%eax,%ecx)
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_WIDE: /* 0x61 */
+/* File: x86/OP_SGET_WIDE.S */
+ /*
+ * 64-bit SGET handler.
+ *
+ */
+ /* sget-wide vAA, field@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_methodClassDex(%ecx),%ecx # ecx<- DvmDex
+ movl offDvmDex_pResFields(%ecx),%ecx # ecx<- dvmDex->pResFields
+ movl (%ecx,%eax,4),%eax # eax<- resolved StaticField ptr
+ testl %eax,%eax # resolved entry null?
+ je .LOP_SGET_WIDE_resolve # if not, make it so
+.LOP_SGET_WIDE_finish: # field ptr in eax
+ movl offStaticField_value(%eax),%ecx # ecx<- lsw
+ movl 4+offStaticField_value(%eax),%eax # eax<- msw
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ SET_VREG_WORD(%ecx,rINST_FULL,0)
+ SET_VREG_WORD(%eax,rINST_FULL,1)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_OBJECT: /* 0x62 */
+/* File: x86/OP_SGET_OBJECT.S */
+/* File: x86/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_methodClassDex(%ecx),%ecx # ecx<- DvmDex
+ movl offDvmDex_pResFields(%ecx),%ecx # ecx<- dvmDex->pResFields
+ movl (%ecx,%eax,4),%eax # eax<- resolved StaticField ptr
+ testl %eax,%eax # resolved entry null?
+ je .LOP_SGET_OBJECT_resolve # if not, make it so
+.LOP_SGET_OBJECT_finish: # field ptr in eax
+ movl offStaticField_value(%eax),%eax
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ SET_VREG(%eax,%ecx)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_BOOLEAN: /* 0x63 */
+/* File: x86/OP_SGET_BOOLEAN.S */
+/* File: x86/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_methodClassDex(%ecx),%ecx # ecx<- DvmDex
+ movl offDvmDex_pResFields(%ecx),%ecx # ecx<- dvmDex->pResFields
+ movl (%ecx,%eax,4),%eax # eax<- resolved StaticField ptr
+ testl %eax,%eax # resolved entry null?
+ je .LOP_SGET_BOOLEAN_resolve # if not, make it so
+.LOP_SGET_BOOLEAN_finish: # field ptr in eax
+ movl offStaticField_value(%eax),%eax
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ SET_VREG(%eax,%ecx)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_BYTE: /* 0x64 */
+/* File: x86/OP_SGET_BYTE.S */
+/* File: x86/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_methodClassDex(%ecx),%ecx # ecx<- DvmDex
+ movl offDvmDex_pResFields(%ecx),%ecx # ecx<- dvmDex->pResFields
+ movl (%ecx,%eax,4),%eax # eax<- resolved StaticField ptr
+ testl %eax,%eax # resolved entry null?
+ je .LOP_SGET_BYTE_resolve # if not, make it so
+.LOP_SGET_BYTE_finish: # field ptr in eax
+ movl offStaticField_value(%eax),%eax
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ SET_VREG(%eax,%ecx)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_CHAR: /* 0x65 */
+/* File: x86/OP_SGET_CHAR.S */
+/* File: x86/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_methodClassDex(%ecx),%ecx # ecx<- DvmDex
+ movl offDvmDex_pResFields(%ecx),%ecx # ecx<- dvmDex->pResFields
+ movl (%ecx,%eax,4),%eax # eax<- resolved StaticField ptr
+ testl %eax,%eax # resolved entry null?
+ je .LOP_SGET_CHAR_resolve # if not, make it so
+.LOP_SGET_CHAR_finish: # field ptr in eax
+ movl offStaticField_value(%eax),%eax
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ SET_VREG(%eax,%ecx)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_SHORT: /* 0x66 */
+/* File: x86/OP_SGET_SHORT.S */
+/* File: x86/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_methodClassDex(%ecx),%ecx # ecx<- DvmDex
+ movl offDvmDex_pResFields(%ecx),%ecx # ecx<- dvmDex->pResFields
+ movl (%ecx,%eax,4),%eax # eax<- resolved StaticField ptr
+ testl %eax,%eax # resolved entry null?
+ je .LOP_SGET_SHORT_resolve # if not, make it so
+.LOP_SGET_SHORT_finish: # field ptr in eax
+ movl offStaticField_value(%eax),%eax
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ SET_VREG(%eax,%ecx)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT: /* 0x67 */
+/* File: x86/OP_SPUT.S */
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_methodClassDex(%ecx),%ecx # ecx<- DvmDex
+ movl offDvmDex_pResFields(%ecx),%ecx # ecx<- dvmDex->pResFields
+ movl (%ecx,%eax,4),%eax # eax<- resolved StaticField ptr
+ testl %eax,%eax # resolved entry null?
+ je .LOP_SPUT_resolve # if not, make it so
+.LOP_SPUT_finish: # field ptr in eax
+ movzbl rINST_HI,%ecx # ecx<- AA
+ GET_VREG(%ecx,%ecx)
+ FETCH_INST_WORD(2)
+ movl %ecx,offStaticField_value(%eax)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_WIDE: /* 0x68 */
+/* File: x86/OP_SPUT_WIDE.S */
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_methodClassDex(%ecx),%ecx # ecx<- DvmDex
+ movl offDvmDex_pResFields(%ecx),%ecx # ecx<- dvmDex->pResFields
+ movl (%ecx,%eax,4),%eax # eax<- resolved StaticField ptr
+ testl %eax,%eax # resolved entry null?
+ je .LOP_SPUT_WIDE_resolve # if not, make it so
+.LOP_SPUT_WIDE_finish: # field ptr in eax
+ movzbl rINST_HI,%ecx # ecx<- AA
+ GET_VREG_WORD(rINST_FULL,%ecx,0) # rINST_FULL<- lsw
+ GET_VREG_WORD(%ecx,%ecx,1) # ecx<- msw
+ movl rINST_FULL,offStaticField_value(%eax)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ movl %ecx,4+offStaticField_value(%eax)
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_OBJECT: /* 0x69 */
+/* File: x86/OP_SPUT_OBJECT.S */
+ /*
+ * SPUT object handler.
+ */
+ /* op vAA, field@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_methodClassDex(%ecx),%ecx # ecx<- DvmDex
+ movl offDvmDex_pResFields(%ecx),%ecx # ecx<- dvmDex->pResFields
+ movl (%ecx,%eax,4),%eax # eax<- resolved StaticField
+ testl %eax,%eax # resolved entry null?
+ je .LOP_SPUT_OBJECT_resolve # if not, make it so
+.LOP_SPUT_OBJECT_finish: # field ptr in eax
+ movzbl rINST_HI,%ecx # ecx<- AA
+ GET_VREG(%ecx,%ecx)
+ jmp .LOP_SPUT_OBJECT_continue
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_BOOLEAN: /* 0x6a */
+/* File: x86/OP_SPUT_BOOLEAN.S */
+/* File: x86/OP_SPUT.S */
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_methodClassDex(%ecx),%ecx # ecx<- DvmDex
+ movl offDvmDex_pResFields(%ecx),%ecx # ecx<- dvmDex->pResFields
+ movl (%ecx,%eax,4),%eax # eax<- resolved StaticField ptr
+ testl %eax,%eax # resolved entry null?
+ je .LOP_SPUT_BOOLEAN_resolve # if not, make it so
+.LOP_SPUT_BOOLEAN_finish: # field ptr in eax
+ movzbl rINST_HI,%ecx # ecx<- AA
+ GET_VREG(%ecx,%ecx)
+ FETCH_INST_WORD(2)
+ movl %ecx,offStaticField_value(%eax)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_BYTE: /* 0x6b */
+/* File: x86/OP_SPUT_BYTE.S */
+/* File: x86/OP_SPUT.S */
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_methodClassDex(%ecx),%ecx # ecx<- DvmDex
+ movl offDvmDex_pResFields(%ecx),%ecx # ecx<- dvmDex->pResFields
+ movl (%ecx,%eax,4),%eax # eax<- resolved StaticField ptr
+ testl %eax,%eax # resolved entry null?
+ je .LOP_SPUT_BYTE_resolve # if not, make it so
+.LOP_SPUT_BYTE_finish: # field ptr in eax
+ movzbl rINST_HI,%ecx # ecx<- AA
+ GET_VREG(%ecx,%ecx)
+ FETCH_INST_WORD(2)
+ movl %ecx,offStaticField_value(%eax)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_CHAR: /* 0x6c */
+/* File: x86/OP_SPUT_CHAR.S */
+/* File: x86/OP_SPUT.S */
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_methodClassDex(%ecx),%ecx # ecx<- DvmDex
+ movl offDvmDex_pResFields(%ecx),%ecx # ecx<- dvmDex->pResFields
+ movl (%ecx,%eax,4),%eax # eax<- resolved StaticField ptr
+ testl %eax,%eax # resolved entry null?
+ je .LOP_SPUT_CHAR_resolve # if not, make it so
+.LOP_SPUT_CHAR_finish: # field ptr in eax
+ movzbl rINST_HI,%ecx # ecx<- AA
+ GET_VREG(%ecx,%ecx)
+ FETCH_INST_WORD(2)
+ movl %ecx,offStaticField_value(%eax)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_SHORT: /* 0x6d */
+/* File: x86/OP_SPUT_SHORT.S */
+/* File: x86/OP_SPUT.S */
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_methodClassDex(%ecx),%ecx # ecx<- DvmDex
+ movl offDvmDex_pResFields(%ecx),%ecx # ecx<- dvmDex->pResFields
+ movl (%ecx,%eax,4),%eax # eax<- resolved StaticField ptr
+ testl %eax,%eax # resolved entry null?
+ je .LOP_SPUT_SHORT_resolve # if not, make it so
+.LOP_SPUT_SHORT_finish: # field ptr in eax
+ movzbl rINST_HI,%ecx # ecx<- AA
+ GET_VREG(%ecx,%ecx)
+ FETCH_INST_WORD(2)
+ movl %ecx,offStaticField_value(%eax)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_VIRTUAL: /* 0x6e */
+/* File: x86/OP_INVOKE_VIRTUAL.S */
+
+ /*
+ * Handle a virtual method call.
+ *
+ * for: invoke-virtual, invoke-virtual/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ GET_GLUE(%eax)
+ movzwl 2(rPC),%ecx # ecx<- BBBB
+ movl offGlue_methodClassDex(%eax),%eax # eax<- pDvmDex
+ EXPORT_PC()
+ movl offDvmDex_pResMethods(%eax),%eax # eax<- pDvmDex->pResMethods
+ movl (%eax,%ecx,4),%eax # eax<- resolved baseMethod
+ testl %eax,%eax # already resolved?
+ jne .LOP_INVOKE_VIRTUAL_continue # yes, continue
+ GET_GLUE(%eax)
+ movl %ecx,OUT_ARG1(%esp) # arg1<- ref
+ movl offGlue_method(%eax),%eax # eax<- glue->method
+ SPILL(rPC)
+ jmp .LOP_INVOKE_VIRTUAL_more
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_SUPER: /* 0x6f */
+/* File: x86/OP_INVOKE_SUPER.S */
+ /*
+ * Handle a "super" method call.
+ *
+ * for: invoke-super, invoke-super/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ GET_GLUE(rINST_FULL)
+ movzwl 2(rPC),%eax # eax<- BBBB
+ movl offGlue_methodClassDex(rINST_FULL),%ecx # ecx<- pDvmDex
+ EXPORT_PC()
+ movl offDvmDex_pResMethods(%ecx),%ecx # ecx<- pDvmDex->pResMethods
+ movl (%ecx,%eax,4),%ecx # ecx<- resolved baseMethod
+ movl offGlue_method(rINST_FULL),%eax # eax<- method
+ movzwl 4(rPC),rINST_FULL # rINST_FULL<- GFED or CCCC
+ .if (!0)
+ andl $0xf,rINST_FULL # rINST_FULL<- D (or stays CCCC)
+ .endif
+ GET_VREG(rINST_FULL,rINST_FULL) # rINST_FULL<- "this" ptr
+ testl rINST_FULL,rINST_FULL # null "this"?
+ je common_errNullObject # yes, throw
+ movl offMethod_clazz(%eax),%eax # eax<- method->clazz
+ testl %ecx,%ecx # already resolved?
+ jne .LOP_INVOKE_SUPER_continue # yes - go on
+ jmp .LOP_INVOKE_SUPER_resolve
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_DIRECT: /* 0x70 */
+/* File: x86/OP_INVOKE_DIRECT.S */
+ /*
+ * Handle a direct method call.
+ *
+ * (We could defer the "is 'this' pointer null" test to the common
+ * method invocation code, and use a flag to indicate that static
+ * calls don't count. If we do this as part of copying the arguments
+ * out we could avoiding loading the first arg twice.)
+ *
+ * for: invoke-direct, invoke-direct/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- BBBB
+ movl offGlue_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+ EXPORT_PC()
+ SPILL(rPC)
+ movl offDvmDex_pResMethods(%ecx),%ecx # ecx<- pDvmDex->pResMethods
+ movzwl 4(rPC),rPC # rPC<- GFED or CCCC
+ movl (%ecx,%eax,4),%eax # eax<- resolved methodToCall
+ .if (!0)
+ andl $0xf,rPC # rPC<- D (or stays CCCC)
+ .endif
+ testl %eax,%eax # already resolved?
+ GET_VREG(%ecx,rPC) # ecx<- "this" ptr
+ je .LOP_INVOKE_DIRECT_resolve # not resolved, do it now
+.LOP_INVOKE_DIRECT_finish:
+ UNSPILL(rPC)
+ testl %ecx,%ecx # null "this"?
+ jne common_invokeMethodNoRange # no, continue on
+ jmp common_errNullObject
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_STATIC: /* 0x71 */
+/* File: x86/OP_INVOKE_STATIC.S */
+ /*
+ * Handle a static method call.
+ *
+ * for: invoke-static, invoke-static/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- BBBB
+ movl offGlue_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+ EXPORT_PC()
+ movl offDvmDex_pResMethods(%ecx),%ecx # ecx<- pDvmDex->pResMethods
+ movl (%ecx,%eax,4),%eax # eax<- resolved methodToCall
+ testl %eax,%eax
+ jne common_invokeMethodNoRange
+ GET_GLUE(%ecx)
+ movl offGlue_method(%ecx),%ecx # ecx<- glue->method
+ movzwl 2(rPC),%eax
+ movl offMethod_clazz(%ecx),%ecx# ecx<- method->clazz
+ movl %eax,OUT_ARG1(%esp) # arg1<- BBBB
+ movl %ecx,OUT_ARG0(%esp) # arg0<- clazz
+ jmp .LOP_INVOKE_STATIC_continue
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_INTERFACE: /* 0x72 */
+/* File: x86/OP_INVOKE_INTERFACE.S */
+ /*
+ * Handle an interface method call.
+ *
+ * for: invoke-interface, invoke-interface/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ movzwl 4(rPC),%eax # eax<- FEDC or CCCC
+ GET_GLUE(%ecx)
+ .if (!0)
+ andl $0xf,%eax # eax<- C (or stays CCCC)
+ .endif
+ GET_VREG(%eax,%eax) # eax<- "this"
+ EXPORT_PC()
+ testl %eax,%eax # null this?
+ je common_errNullObject # yes, fail
+ movl offObject_clazz(%eax),%eax# eax<- thisPtr->clazz
+ movl %eax,OUT_ARG0(%esp) # arg0<- class
+ movl offGlue_methodClassDex(%ecx),%eax # eax<- methodClassDex
+ movl offGlue_method(%ecx),%ecx # ecx<- method
+ movl %eax,OUT_ARG3(%esp) # arg3<- dex
+ movzwl 2(rPC),%eax # eax<- BBBB
+ movl %ecx,OUT_ARG2(%esp) # arg2<- method
+ movl %eax,OUT_ARG1(%esp) # arg1<- BBBB
+ SPILL(rPC)
+ jmp .LOP_INVOKE_INTERFACE_continue
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_73: /* 0x73 */
+/* File: x86/OP_UNUSED_73.S */
+/* File: x86/unused.S */
+ jmp common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_VIRTUAL_RANGE: /* 0x74 */
+/* File: x86/OP_INVOKE_VIRTUAL_RANGE.S */
+/* File: x86/OP_INVOKE_VIRTUAL.S */
+
+ /*
+ * Handle a virtual method call.
+ *
+ * for: invoke-virtual, invoke-virtual/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ GET_GLUE(%eax)
+ movzwl 2(rPC),%ecx # ecx<- BBBB
+ movl offGlue_methodClassDex(%eax),%eax # eax<- pDvmDex
+ EXPORT_PC()
+ movl offDvmDex_pResMethods(%eax),%eax # eax<- pDvmDex->pResMethods
+ movl (%eax,%ecx,4),%eax # eax<- resolved baseMethod
+ testl %eax,%eax # already resolved?
+ jne .LOP_INVOKE_VIRTUAL_RANGE_continue # yes, continue
+ GET_GLUE(%eax)
+ movl %ecx,OUT_ARG1(%esp) # arg1<- ref
+ movl offGlue_method(%eax),%eax # eax<- glue->method
+ SPILL(rPC)
+ jmp .LOP_INVOKE_VIRTUAL_RANGE_more
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_SUPER_RANGE: /* 0x75 */
+/* File: x86/OP_INVOKE_SUPER_RANGE.S */
+/* File: x86/OP_INVOKE_SUPER.S */
+ /*
+ * Handle a "super" method call.
+ *
+ * for: invoke-super, invoke-super/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ GET_GLUE(rINST_FULL)
+ movzwl 2(rPC),%eax # eax<- BBBB
+ movl offGlue_methodClassDex(rINST_FULL),%ecx # ecx<- pDvmDex
+ EXPORT_PC()
+ movl offDvmDex_pResMethods(%ecx),%ecx # ecx<- pDvmDex->pResMethods
+ movl (%ecx,%eax,4),%ecx # ecx<- resolved baseMethod
+ movl offGlue_method(rINST_FULL),%eax # eax<- method
+ movzwl 4(rPC),rINST_FULL # rINST_FULL<- GFED or CCCC
+ .if (!1)
+ andl $0xf,rINST_FULL # rINST_FULL<- D (or stays CCCC)
+ .endif
+ GET_VREG(rINST_FULL,rINST_FULL) # rINST_FULL<- "this" ptr
+ testl rINST_FULL,rINST_FULL # null "this"?
+ je common_errNullObject # yes, throw
+ movl offMethod_clazz(%eax),%eax # eax<- method->clazz
+ testl %ecx,%ecx # already resolved?
+ jne .LOP_INVOKE_SUPER_RANGE_continue # yes - go on
+ jmp .LOP_INVOKE_SUPER_RANGE_resolve
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_DIRECT_RANGE: /* 0x76 */
+/* File: x86/OP_INVOKE_DIRECT_RANGE.S */
+/* File: x86/OP_INVOKE_DIRECT.S */
+ /*
+ * Handle a direct method call.
+ *
+ * (We could defer the "is 'this' pointer null" test to the common
+ * method invocation code, and use a flag to indicate that static
+ * calls don't count. If we do this as part of copying the arguments
+ * out we could avoiding loading the first arg twice.)
+ *
+ * for: invoke-direct, invoke-direct/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- BBBB
+ movl offGlue_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+ EXPORT_PC()
+ SPILL(rPC)
+ movl offDvmDex_pResMethods(%ecx),%ecx # ecx<- pDvmDex->pResMethods
+ movzwl 4(rPC),rPC # rPC<- GFED or CCCC
+ movl (%ecx,%eax,4),%eax # eax<- resolved methodToCall
+ .if (!1)
+ andl $0xf,rPC # rPC<- D (or stays CCCC)
+ .endif
+ testl %eax,%eax # already resolved?
+ GET_VREG(%ecx,rPC) # ecx<- "this" ptr
+ je .LOP_INVOKE_DIRECT_RANGE_resolve # not resolved, do it now
+.LOP_INVOKE_DIRECT_RANGE_finish:
+ UNSPILL(rPC)
+ testl %ecx,%ecx # null "this"?
+ jne common_invokeMethodRange # no, continue on
+ jmp common_errNullObject
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_STATIC_RANGE: /* 0x77 */
+/* File: x86/OP_INVOKE_STATIC_RANGE.S */
+/* File: x86/OP_INVOKE_STATIC.S */
+ /*
+ * Handle a static method call.
+ *
+ * for: invoke-static, invoke-static/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- BBBB
+ movl offGlue_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+ EXPORT_PC()
+ movl offDvmDex_pResMethods(%ecx),%ecx # ecx<- pDvmDex->pResMethods
+ movl (%ecx,%eax,4),%eax # eax<- resolved methodToCall
+ testl %eax,%eax
+ jne common_invokeMethodRange
+ GET_GLUE(%ecx)
+ movl offGlue_method(%ecx),%ecx # ecx<- glue->method
+ movzwl 2(rPC),%eax
+ movl offMethod_clazz(%ecx),%ecx# ecx<- method->clazz
+ movl %eax,OUT_ARG1(%esp) # arg1<- BBBB
+ movl %ecx,OUT_ARG0(%esp) # arg0<- clazz
+ jmp .LOP_INVOKE_STATIC_RANGE_continue
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_INTERFACE_RANGE: /* 0x78 */
+/* File: x86/OP_INVOKE_INTERFACE_RANGE.S */
+/* File: x86/OP_INVOKE_INTERFACE.S */
+ /*
+ * Handle an interface method call.
+ *
+ * for: invoke-interface, invoke-interface/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ movzwl 4(rPC),%eax # eax<- FEDC or CCCC
+ GET_GLUE(%ecx)
+ .if (!1)
+ andl $0xf,%eax # eax<- C (or stays CCCC)
+ .endif
+ GET_VREG(%eax,%eax) # eax<- "this"
+ EXPORT_PC()
+ testl %eax,%eax # null this?
+ je common_errNullObject # yes, fail
+ movl offObject_clazz(%eax),%eax# eax<- thisPtr->clazz
+ movl %eax,OUT_ARG0(%esp) # arg0<- class
+ movl offGlue_methodClassDex(%ecx),%eax # eax<- methodClassDex
+ movl offGlue_method(%ecx),%ecx # ecx<- method
+ movl %eax,OUT_ARG3(%esp) # arg3<- dex
+ movzwl 2(rPC),%eax # eax<- BBBB
+ movl %ecx,OUT_ARG2(%esp) # arg2<- method
+ movl %eax,OUT_ARG1(%esp) # arg1<- BBBB
+ SPILL(rPC)
+ jmp .LOP_INVOKE_INTERFACE_RANGE_continue
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_79: /* 0x79 */
+/* File: x86/OP_UNUSED_79.S */
+/* File: x86/unused.S */
+ jmp common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_7A: /* 0x7a */
+/* File: x86/OP_UNUSED_7A.S */
+/* File: x86/unused.S */
+ jmp common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEG_INT: /* 0x7b */
+/* File: x86/OP_NEG_INT.S */
+/* File: x86/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op eax".
+ */
+ /* unop vA, vB */
+ movzbl rINST_HI,%ecx # ecx<- A+
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ GET_VREG(%eax,rINST_FULL) # eax<- vB
+ andb $0xf,%cl # ecx<- A
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+
+
+ negl %eax
+ SET_VREG(%eax,%ecx)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NOT_INT: /* 0x7c */
+/* File: x86/OP_NOT_INT.S */
+/* File: x86/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op eax".
+ */
+ /* unop vA, vB */
+ movzbl rINST_HI,%ecx # ecx<- A+
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ GET_VREG(%eax,rINST_FULL) # eax<- vB
+ andb $0xf,%cl # ecx<- A
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+
+
+ notl %eax
+ SET_VREG(%eax,%ecx)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEG_LONG: /* 0x7d */
+/* File: x86/OP_NEG_LONG.S */
+ /* unop vA, vB */
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $4,%ecx # ecx<- B
+ movzbl rINST_HI,rINST_FULL # ecx<- BA
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG_WORD(%eax,%ecx,0) # eax<- v[B+0]
+ GET_VREG_WORD(%ecx,%ecx,1) # ecx<- v[B+1]
+ negl %eax
+ adcl $0,%ecx
+ negl %ecx
+ SET_VREG_WORD(%eax,rINST_FULL,0) # v[A+0]<- eax
+ SET_VREG_WORD(%ecx,rINST_FULL,1) # v[A+1]<- ecx
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NOT_LONG: /* 0x7e */
+/* File: x86/OP_NOT_LONG.S */
+ /* unop vA, vB */
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $4,%ecx # ecx<- B
+ movzbl rINST_HI,rINST_FULL # ecx<- BA
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG_WORD(%eax,%ecx,0) # eax<- v[B+0]
+ GET_VREG_WORD(%ecx,%ecx,1) # ecx<- v[B+1]
+ notl %eax
+ notl %ecx
+ SET_VREG_WORD(%eax,rINST_FULL,0) # v[A+0]<- eax
+ SET_VREG_WORD(%ecx,rINST_FULL,1) # v[A+1]<- ecx
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEG_FLOAT: /* 0x7f */
+/* File: x86/OP_NEG_FLOAT.S */
+/* File: x86/fpcvt.S */
+ /*
+ * Generic 32-bit FP conversion operation.
+ */
+ /* unop vA, vB */
+ movzbl rINST_HI,%ecx # ecx<- A+
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ flds (rFP,rINST_FULL,4) # %st0<- vB
+ andb $0xf,%cl # ecx<- A
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ fchs
+ fstps (rFP,%ecx,4) # vA<- %st0
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_NEG_DOUBLE: /* 0x80 */
+/* File: x86/OP_NEG_DOUBLE.S */
+/* File: x86/fpcvt.S */
+ /*
+ * Generic 32-bit FP conversion operation.
+ */
+ /* unop vA, vB */
+ movzbl rINST_HI,%ecx # ecx<- A+
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ fldl (rFP,rINST_FULL,4) # %st0<- vB
+ andb $0xf,%cl # ecx<- A
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ fchs
+ fstpl (rFP,%ecx,4) # vA<- %st0
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_LONG: /* 0x81 */
+/* File: x86/OP_INT_TO_LONG.S */
+ /* int to long vA, vB */
+ movzbl rINST_HI,%ecx # ecx<- +A
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ GET_VREG(%eax,rINST_FULL) # eax<- vB
+ SPILL(rPC) # will step on edx later
+ andb $0xf,%cl # ecx<- A
+ cltd # edx:eax<- sssssssBBBBBBBB
+ SET_VREG_WORD(%edx,%ecx,1) # v[A+1]<- edx/rPC
+ UNSPILL(rPC)
+ SET_VREG_WORD(%eax,%ecx,0) # v[A+0]<- %eax
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_FLOAT: /* 0x82 */
+/* File: x86/OP_INT_TO_FLOAT.S */
+/* File: x86/fpcvt.S */
+ /*
+ * Generic 32-bit FP conversion operation.
+ */
+ /* unop vA, vB */
+ movzbl rINST_HI,%ecx # ecx<- A+
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ fildl (rFP,rINST_FULL,4) # %st0<- vB
+ andb $0xf,%cl # ecx<- A
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+
+ fstps (rFP,%ecx,4) # vA<- %st0
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_DOUBLE: /* 0x83 */
+/* File: x86/OP_INT_TO_DOUBLE.S */
+/* File: x86/fpcvt.S */
+ /*
+ * Generic 32-bit FP conversion operation.
+ */
+ /* unop vA, vB */
+ movzbl rINST_HI,%ecx # ecx<- A+
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ fildl (rFP,rINST_FULL,4) # %st0<- vB
+ andb $0xf,%cl # ecx<- A
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+
+ fstpl (rFP,%ecx,4) # vA<- %st0
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_LONG_TO_INT: /* 0x84 */
+/* File: x86/OP_LONG_TO_INT.S */
+/* we ignore the high word, making this equivalent to a 32-bit reg move */
+/* File: x86/OP_MOVE.S */
+ /* for move, move-object, long-to-int */
+ /* op vA, vB */
+ movzbl rINST_HI,%eax # eax<- BA
+ andb $0xf,%al # eax<- A
+ shrl $12,rINST_FULL # rINST_FULL<- B
+ GET_VREG(%ecx,rINST_FULL)
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ SET_VREG(%ecx,%eax) # fp[A]<-fp[B]
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_LONG_TO_FLOAT: /* 0x85 */
+/* File: x86/OP_LONG_TO_FLOAT.S */
+/* File: x86/fpcvt.S */
+ /*
+ * Generic 32-bit FP conversion operation.
+ */
+ /* unop vA, vB */
+ movzbl rINST_HI,%ecx # ecx<- A+
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ fildll (rFP,rINST_FULL,4) # %st0<- vB
+ andb $0xf,%cl # ecx<- A
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+
+ fstps (rFP,%ecx,4) # vA<- %st0
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_LONG_TO_DOUBLE: /* 0x86 */
+/* File: x86/OP_LONG_TO_DOUBLE.S */
+/* File: x86/fpcvt.S */
+ /*
+ * Generic 32-bit FP conversion operation.
+ */
+ /* unop vA, vB */
+ movzbl rINST_HI,%ecx # ecx<- A+
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ fildll (rFP,rINST_FULL,4) # %st0<- vB
+ andb $0xf,%cl # ecx<- A
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+
+ fstpl (rFP,%ecx,4) # vA<- %st0
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FLOAT_TO_INT: /* 0x87 */
+/* File: x86/OP_FLOAT_TO_INT.S */
+/* File: x86/cvtfp_int.S */
+/* On fp to int conversions, Java requires that
+ * if the result > maxint, it should be clamped to maxint. If it is less
+ * than minint, it should be clamped to minint. If it is a nan, the result
+ * should be zero. Further, the rounding mode is to truncate. This model
+ * differs from what is delivered normally via the x86 fpu, so we have
+ * to play some games.
+ */
+ /* float/double to int/long vA, vB */
+ movzbl rINST_HI,%ecx # ecx<- A+
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ .if 0
+ fldl (rFP,rINST_FULL,4) # %st0<- vB
+ .else
+ flds (rFP,rINST_FULL,4) # %st0<- vB
+ .endif
+ ftst
+ fnstcw LOCAL0_OFFSET(%ebp) # remember original rounding mode
+ movzwl LOCAL0_OFFSET(%ebp),%eax
+ movb $0xc,%ah
+ movw %ax,LOCAL0_OFFSET+2(%ebp)
+ fldcw LOCAL0_OFFSET+2(%ebp) # set "to zero" rounding mode
+ FETCH_INST_WORD(1)
+ andb $0xf,%cl # ecx<- A
+ .if 0
+ fistpll (rFP,%ecx,4) # convert and store
+ .else
+ fistpl (rFP,%ecx,4) # convert and store
+ .endif
+ fldcw LOCAL0_OFFSET(%ebp) # restore previous rounding mode
+ jmp .LOP_FLOAT_TO_INT_continue
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FLOAT_TO_LONG: /* 0x88 */
+/* File: x86/OP_FLOAT_TO_LONG.S */
+/* File: x86/cvtfp_int.S */
+/* On fp to int conversions, Java requires that
+ * if the result > maxint, it should be clamped to maxint. If it is less
+ * than minint, it should be clamped to minint. If it is a nan, the result
+ * should be zero. Further, the rounding mode is to truncate. This model
+ * differs from what is delivered normally via the x86 fpu, so we have
+ * to play some games.
+ */
+ /* float/double to int/long vA, vB */
+ movzbl rINST_HI,%ecx # ecx<- A+
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ .if 0
+ fldl (rFP,rINST_FULL,4) # %st0<- vB
+ .else
+ flds (rFP,rINST_FULL,4) # %st0<- vB
+ .endif
+ ftst
+ fnstcw LOCAL0_OFFSET(%ebp) # remember original rounding mode
+ movzwl LOCAL0_OFFSET(%ebp),%eax
+ movb $0xc,%ah
+ movw %ax,LOCAL0_OFFSET+2(%ebp)
+ fldcw LOCAL0_OFFSET+2(%ebp) # set "to zero" rounding mode
+ FETCH_INST_WORD(1)
+ andb $0xf,%cl # ecx<- A
+ .if 1
+ fistpll (rFP,%ecx,4) # convert and store
+ .else
+ fistpl (rFP,%ecx,4) # convert and store
+ .endif
+ fldcw LOCAL0_OFFSET(%ebp) # restore previous rounding mode
+ jmp .LOP_FLOAT_TO_LONG_continue
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_FLOAT_TO_DOUBLE: /* 0x89 */
+/* File: x86/OP_FLOAT_TO_DOUBLE.S */
+/* File: x86/fpcvt.S */
+ /*
+ * Generic 32-bit FP conversion operation.
+ */
+ /* unop vA, vB */
+ movzbl rINST_HI,%ecx # ecx<- A+
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ flds (rFP,rINST_FULL,4) # %st0<- vB
+ andb $0xf,%cl # ecx<- A
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+
+ fstpl (rFP,%ecx,4) # vA<- %st0
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DOUBLE_TO_INT: /* 0x8a */
+/* File: x86/OP_DOUBLE_TO_INT.S */
+/* File: x86/cvtfp_int.S */
+/* On fp to int conversions, Java requires that
+ * if the result > maxint, it should be clamped to maxint. If it is less
+ * than minint, it should be clamped to minint. If it is a nan, the result
+ * should be zero. Further, the rounding mode is to truncate. This model
+ * differs from what is delivered normally via the x86 fpu, so we have
+ * to play some games.
+ */
+ /* float/double to int/long vA, vB */
+ movzbl rINST_HI,%ecx # ecx<- A+
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ .if 1
+ fldl (rFP,rINST_FULL,4) # %st0<- vB
+ .else
+ flds (rFP,rINST_FULL,4) # %st0<- vB
+ .endif
+ ftst
+ fnstcw LOCAL0_OFFSET(%ebp) # remember original rounding mode
+ movzwl LOCAL0_OFFSET(%ebp),%eax
+ movb $0xc,%ah
+ movw %ax,LOCAL0_OFFSET+2(%ebp)
+ fldcw LOCAL0_OFFSET+2(%ebp) # set "to zero" rounding mode
+ FETCH_INST_WORD(1)
+ andb $0xf,%cl # ecx<- A
+ .if 0
+ fistpll (rFP,%ecx,4) # convert and store
+ .else
+ fistpl (rFP,%ecx,4) # convert and store
+ .endif
+ fldcw LOCAL0_OFFSET(%ebp) # restore previous rounding mode
+ jmp .LOP_DOUBLE_TO_INT_continue
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DOUBLE_TO_LONG: /* 0x8b */
+/* File: x86/OP_DOUBLE_TO_LONG.S */
+/* File: x86/cvtfp_int.S */
+/* On fp to int conversions, Java requires that
+ * if the result > maxint, it should be clamped to maxint. If it is less
+ * than minint, it should be clamped to minint. If it is a nan, the result
+ * should be zero. Further, the rounding mode is to truncate. This model
+ * differs from what is delivered normally via the x86 fpu, so we have
+ * to play some games.
+ */
+ /* float/double to int/long vA, vB */
+ movzbl rINST_HI,%ecx # ecx<- A+
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ .if 1
+ fldl (rFP,rINST_FULL,4) # %st0<- vB
+ .else
+ flds (rFP,rINST_FULL,4) # %st0<- vB
+ .endif
+ ftst
+ fnstcw LOCAL0_OFFSET(%ebp) # remember original rounding mode
+ movzwl LOCAL0_OFFSET(%ebp),%eax
+ movb $0xc,%ah
+ movw %ax,LOCAL0_OFFSET+2(%ebp)
+ fldcw LOCAL0_OFFSET+2(%ebp) # set "to zero" rounding mode
+ FETCH_INST_WORD(1)
+ andb $0xf,%cl # ecx<- A
+ .if 1
+ fistpll (rFP,%ecx,4) # convert and store
+ .else
+ fistpl (rFP,%ecx,4) # convert and store
+ .endif
+ fldcw LOCAL0_OFFSET(%ebp) # restore previous rounding mode
+ jmp .LOP_DOUBLE_TO_LONG_continue
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DOUBLE_TO_FLOAT: /* 0x8c */
+/* File: x86/OP_DOUBLE_TO_FLOAT.S */
+/* File: x86/fpcvt.S */
+ /*
+ * Generic 32-bit FP conversion operation.
+ */
+ /* unop vA, vB */
+ movzbl rINST_HI,%ecx # ecx<- A+
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ fldl (rFP,rINST_FULL,4) # %st0<- vB
+ andb $0xf,%cl # ecx<- A
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+
+ fstps (rFP,%ecx,4) # vA<- %st0
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_BYTE: /* 0x8d */
+/* File: x86/OP_INT_TO_BYTE.S */
+/* File: x86/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op eax".
+ */
+ /* unop vA, vB */
+ movzbl rINST_HI,%ecx # ecx<- A+
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ GET_VREG(%eax,rINST_FULL) # eax<- vB
+ andb $0xf,%cl # ecx<- A
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+
+
+ movsbl %al,%eax
+ SET_VREG(%eax,%ecx)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_CHAR: /* 0x8e */
+/* File: x86/OP_INT_TO_CHAR.S */
+/* File: x86/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op eax".
+ */
+ /* unop vA, vB */
+ movzbl rINST_HI,%ecx # ecx<- A+
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ GET_VREG(%eax,rINST_FULL) # eax<- vB
+ andb $0xf,%cl # ecx<- A
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+
+
+ movzwl %ax,%eax
+ SET_VREG(%eax,%ecx)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INT_TO_SHORT: /* 0x8f */
+/* File: x86/OP_INT_TO_SHORT.S */
+/* File: x86/unop.S */
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op eax".
+ */
+ /* unop vA, vB */
+ movzbl rINST_HI,%ecx # ecx<- A+
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ GET_VREG(%eax,rINST_FULL) # eax<- vB
+ andb $0xf,%cl # ecx<- A
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+
+
+ movswl %ax,%eax
+ SET_VREG(%eax,%ecx)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_INT: /* 0x90 */
+/* File: x86/OP_ADD_INT.S */
+/* File: x86/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = eax op (rFP,%ecx,4)".
+ * This could be an x86 instruction or a function call. (If the result
+ * comes back in a register other than eax, you can override "result".)
+ *
+ * For: add-int, sub-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int
+ */
+ /* binop vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ GET_VREG(%eax,%eax) # eax<- vBB
+ addl (rFP,%ecx,4),%eax # ex: addl (rFP,%ecx,4),%eax
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ SET_VREG(%eax,%ecx)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_INT: /* 0x91 */
+/* File: x86/OP_SUB_INT.S */
+/* File: x86/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = eax op (rFP,%ecx,4)".
+ * This could be an x86 instruction or a function call. (If the result
+ * comes back in a register other than eax, you can override "result".)
+ *
+ * For: add-int, sub-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int
+ */
+ /* binop vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ GET_VREG(%eax,%eax) # eax<- vBB
+ subl (rFP,%ecx,4),%eax # ex: addl (rFP,%ecx,4),%eax
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ SET_VREG(%eax,%ecx)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_INT: /* 0x92 */
+/* File: x86/OP_MUL_INT.S */
+ /*
+ * 32-bit binary multiplication.
+ */
+ /* mul vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ SPILL(rPC)
+ GET_VREG(%eax,%eax) # eax<- vBB
+ imull (rFP,%ecx,4),%eax # trashes rPC/edx
+ UNSPILL(rPC)
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ SET_VREG(%eax,%ecx)
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_INT: /* 0x93 */
+/* File: x86/OP_DIV_INT.S */
+/* File: x86/bindiv.S */
+
+ /*
+ * 32-bit binary div/rem operation. Handles special case of op0=minint and
+ * op1=-1.
+ */
+ /* binop vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ GET_VREG(%eax,%eax) # eax<- vBB
+ GET_VREG(%ecx,%ecx) # eax<- vBB
+ SPILL(rPC)
+ cmpl $0,%ecx
+ je common_errDivideByZero
+ cmpl $-1,%ecx
+ jne .LOP_DIV_INT_continue_div
+ cmpl $0x80000000,%eax
+ jne .LOP_DIV_INT_continue_div
+ movl $0x80000000,%eax
+ jmp .LOP_DIV_INT_finish_div
+
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_INT: /* 0x94 */
+/* File: x86/OP_REM_INT.S */
+/* File: x86/bindiv.S */
+
+ /*
+ * 32-bit binary div/rem operation. Handles special case of op0=minint and
+ * op1=-1.
+ */
+ /* binop vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ GET_VREG(%eax,%eax) # eax<- vBB
+ GET_VREG(%ecx,%ecx) # eax<- vBB
+ SPILL(rPC)
+ cmpl $0,%ecx
+ je common_errDivideByZero
+ cmpl $-1,%ecx
+ jne .LOP_REM_INT_continue_div
+ cmpl $0x80000000,%eax
+ jne .LOP_REM_INT_continue_div
+ movl $0,%edx
+ jmp .LOP_REM_INT_finish_div
+
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_INT: /* 0x95 */
+/* File: x86/OP_AND_INT.S */
+/* File: x86/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = eax op (rFP,%ecx,4)".
+ * This could be an x86 instruction or a function call. (If the result
+ * comes back in a register other than eax, you can override "result".)
+ *
+ * For: add-int, sub-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int
+ */
+ /* binop vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ GET_VREG(%eax,%eax) # eax<- vBB
+ andl (rFP,%ecx,4),%eax # ex: addl (rFP,%ecx,4),%eax
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ SET_VREG(%eax,%ecx)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_INT: /* 0x96 */
+/* File: x86/OP_OR_INT.S */
+/* File: x86/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = eax op (rFP,%ecx,4)".
+ * This could be an x86 instruction or a function call. (If the result
+ * comes back in a register other than eax, you can override "result".)
+ *
+ * For: add-int, sub-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int
+ */
+ /* binop vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ GET_VREG(%eax,%eax) # eax<- vBB
+ orl (rFP,%ecx,4),%eax # ex: addl (rFP,%ecx,4),%eax
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ SET_VREG(%eax,%ecx)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_INT: /* 0x97 */
+/* File: x86/OP_XOR_INT.S */
+/* File: x86/binop.S */
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = eax op (rFP,%ecx,4)".
+ * This could be an x86 instruction or a function call. (If the result
+ * comes back in a register other than eax, you can override "result".)
+ *
+ * For: add-int, sub-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int
+ */
+ /* binop vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ GET_VREG(%eax,%eax) # eax<- vBB
+ xorl (rFP,%ecx,4),%eax # ex: addl (rFP,%ecx,4),%eax
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ SET_VREG(%eax,%ecx)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHL_INT: /* 0x98 */
+/* File: x86/OP_SHL_INT.S */
+/* File: x86/binop1.S */
+ /*
+ * Generic 32-bit binary operation in which both operands loaded to
+ * registers (op0 in eax, op1 in ecx).
+ */
+ /* binop vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ GET_VREG(%eax,%eax) # eax<- vBB
+ GET_VREG(%ecx,%ecx) # eax<- vBB
+ sall %cl,%eax # ex: addl %ecx,%eax
+ movzbl rINST_HI,%ecx # tmp<- AA
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ SET_VREG(%eax,%ecx)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHR_INT: /* 0x99 */
+/* File: x86/OP_SHR_INT.S */
+/* File: x86/binop1.S */
+ /*
+ * Generic 32-bit binary operation in which both operands loaded to
+ * registers (op0 in eax, op1 in ecx).
+ */
+ /* binop vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ GET_VREG(%eax,%eax) # eax<- vBB
+ GET_VREG(%ecx,%ecx) # eax<- vBB
+ sarl %cl,%eax # ex: addl %ecx,%eax
+ movzbl rINST_HI,%ecx # tmp<- AA
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ SET_VREG(%eax,%ecx)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_USHR_INT: /* 0x9a */
+/* File: x86/OP_USHR_INT.S */
+/* File: x86/binop1.S */
+ /*
+ * Generic 32-bit binary operation in which both operands loaded to
+ * registers (op0 in eax, op1 in ecx).
+ */
+ /* binop vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ GET_VREG(%eax,%eax) # eax<- vBB
+ GET_VREG(%ecx,%ecx) # eax<- vBB
+ shrl %cl,%eax # ex: addl %ecx,%eax
+ movzbl rINST_HI,%ecx # tmp<- AA
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ SET_VREG(%eax,%ecx)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_LONG: /* 0x9b */
+/* File: x86/OP_ADD_LONG.S */
+/* File: x86/binopWide.S */
+ /*
+ * Generic 64-bit binary operation.
+ */
+ /* binop vAA, vBB, vCC */
+
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ SPILL(rPC)
+ GET_VREG_WORD(rPC,%eax,0) # rPC<- v[BB+0]
+ GET_VREG_WORD(%eax,%eax,1) # eax<- v[BB+1]
+ addl (rFP,%ecx,4),rPC # ex: addl (rFP,%ecx,4),rPC
+ adcl 4(rFP,%ecx,4),%eax # ex: adcl 4(rFP,%ecx,4),%eax
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ SET_VREG_WORD(rPC,rINST_FULL,0) # v[AA+0] <- rPC
+ UNSPILL(rPC)
+ SET_VREG_WORD(%eax,rINST_FULL,1) # v[AA+1] <- eax
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_LONG: /* 0x9c */
+/* File: x86/OP_SUB_LONG.S */
+/* File: x86/binopWide.S */
+ /*
+ * Generic 64-bit binary operation.
+ */
+ /* binop vAA, vBB, vCC */
+
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ SPILL(rPC)
+ GET_VREG_WORD(rPC,%eax,0) # rPC<- v[BB+0]
+ GET_VREG_WORD(%eax,%eax,1) # eax<- v[BB+1]
+ subl (rFP,%ecx,4),rPC # ex: addl (rFP,%ecx,4),rPC
+ sbbl 4(rFP,%ecx,4),%eax # ex: adcl 4(rFP,%ecx,4),%eax
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ SET_VREG_WORD(rPC,rINST_FULL,0) # v[AA+0] <- rPC
+ UNSPILL(rPC)
+ SET_VREG_WORD(%eax,rINST_FULL,1) # v[AA+1] <- eax
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_LONG: /* 0x9d */
+/* File: x86/OP_MUL_LONG.S */
+ /*
+ * Signed 64-bit integer multiply.
+ *
+ * We could definately use more free registers for
+ * this code. We must spill rPC (edx) because it
+ * is used by imul. We'll also spill rINST (ebx),
+ * giving us eax, ebc, ecx and edx as computational
+ * temps. On top of that, we'll spill rIBASE (edi)
+ * for use as the vB pointer and rFP (esi) for use
+ * as the vC pointer. Yuck.
+ */
+ /* mul-long vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- B
+ movzbl 3(rPC),%ecx # ecx<- C
+ SPILL(rPC)
+ SPILL(rIBASE)
+ SPILL(rFP)
+ SPILL(rINST_FULL)
+ leal (rFP,%eax,4),rIBASE # rIBASE<- &v[B]
+ leal (rFP,%ecx,4),rFP # rFP<- &v[C]
+ movl 4(rIBASE),%ecx # ecx<- Bmsw
+ imull (rFP),%ecx # ecx<- (Bmsw*Clsw)
+ movl 4(rFP),%eax # eax<- Cmsw
+ imull (rIBASE),%eax # eax<- (Cmsw*Blsw)
+ addl %eax,%ecx # ecx<- (Bmsw*Clsw)+(Cmsw*Blsw)
+ movl (rFP),%eax # eax<- Clsw
+ mull (rIBASE) # eax<- (Clsw*Alsw)
+ UNSPILL(rINST_FULL)
+ UNSPILL(rFP)
+ jmp .LOP_MUL_LONG_continue
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_LONG: /* 0x9e */
+/* File: x86/OP_DIV_LONG.S */
+ /* div vAA, vBB, vCC */
+ movzbl 3(rPC),%eax # eax<- CC
+ movzbl 2(rPC),%ecx # ecx<- BB
+ SPILL(rPC)
+ GET_VREG_WORD(rPC,%eax,0)
+ GET_VREG_WORD(%eax,%eax,1)
+ movl rPC,OUT_ARG2(%esp)
+ testl %eax,%eax
+ je .LOP_DIV_LONG_check_zero
+ cmpl $-1,%eax
+ je .LOP_DIV_LONG_check_neg1
+.LOP_DIV_LONG_notSpecial:
+ GET_VREG_WORD(rPC,%ecx,0)
+ GET_VREG_WORD(%ecx,%ecx,1)
+.LOP_DIV_LONG_notSpecial1:
+ movl %eax,OUT_ARG3(%esp)
+ movl rPC,OUT_ARG0(%esp)
+ movl %ecx,OUT_ARG1(%esp)
+ jmp .LOP_DIV_LONG_continue
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_LONG: /* 0x9f */
+/* File: x86/OP_REM_LONG.S */
+/* File: x86/OP_DIV_LONG.S */
+ /* div vAA, vBB, vCC */
+ movzbl 3(rPC),%eax # eax<- CC
+ movzbl 2(rPC),%ecx # ecx<- BB
+ SPILL(rPC)
+ GET_VREG_WORD(rPC,%eax,0)
+ GET_VREG_WORD(%eax,%eax,1)
+ movl rPC,OUT_ARG2(%esp)
+ testl %eax,%eax
+ je .LOP_REM_LONG_check_zero
+ cmpl $-1,%eax
+ je .LOP_REM_LONG_check_neg1
+.LOP_REM_LONG_notSpecial:
+ GET_VREG_WORD(rPC,%ecx,0)
+ GET_VREG_WORD(%ecx,%ecx,1)
+.LOP_REM_LONG_notSpecial1:
+ movl %eax,OUT_ARG3(%esp)
+ movl rPC,OUT_ARG0(%esp)
+ movl %ecx,OUT_ARG1(%esp)
+ jmp .LOP_REM_LONG_continue
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_LONG: /* 0xa0 */
+/* File: x86/OP_AND_LONG.S */
+/* File: x86/binopWide.S */
+ /*
+ * Generic 64-bit binary operation.
+ */
+ /* binop vAA, vBB, vCC */
+
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ SPILL(rPC)
+ GET_VREG_WORD(rPC,%eax,0) # rPC<- v[BB+0]
+ GET_VREG_WORD(%eax,%eax,1) # eax<- v[BB+1]
+ andl (rFP,%ecx,4),rPC # ex: addl (rFP,%ecx,4),rPC
+ andl 4(rFP,%ecx,4),%eax # ex: adcl 4(rFP,%ecx,4),%eax
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ SET_VREG_WORD(rPC,rINST_FULL,0) # v[AA+0] <- rPC
+ UNSPILL(rPC)
+ SET_VREG_WORD(%eax,rINST_FULL,1) # v[AA+1] <- eax
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_LONG: /* 0xa1 */
+/* File: x86/OP_OR_LONG.S */
+/* File: x86/binopWide.S */
+ /*
+ * Generic 64-bit binary operation.
+ */
+ /* binop vAA, vBB, vCC */
+
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ SPILL(rPC)
+ GET_VREG_WORD(rPC,%eax,0) # rPC<- v[BB+0]
+ GET_VREG_WORD(%eax,%eax,1) # eax<- v[BB+1]
+ orl (rFP,%ecx,4),rPC # ex: addl (rFP,%ecx,4),rPC
+ orl 4(rFP,%ecx,4),%eax # ex: adcl 4(rFP,%ecx,4),%eax
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ SET_VREG_WORD(rPC,rINST_FULL,0) # v[AA+0] <- rPC
+ UNSPILL(rPC)
+ SET_VREG_WORD(%eax,rINST_FULL,1) # v[AA+1] <- eax
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_LONG: /* 0xa2 */
+/* File: x86/OP_XOR_LONG.S */
+/* File: x86/binopWide.S */
+ /*
+ * Generic 64-bit binary operation.
+ */
+ /* binop vAA, vBB, vCC */
+
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ SPILL(rPC)
+ GET_VREG_WORD(rPC,%eax,0) # rPC<- v[BB+0]
+ GET_VREG_WORD(%eax,%eax,1) # eax<- v[BB+1]
+ xorl (rFP,%ecx,4),rPC # ex: addl (rFP,%ecx,4),rPC
+ xorl 4(rFP,%ecx,4),%eax # ex: adcl 4(rFP,%ecx,4),%eax
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ SET_VREG_WORD(rPC,rINST_FULL,0) # v[AA+0] <- rPC
+ UNSPILL(rPC)
+ SET_VREG_WORD(%eax,rINST_FULL,1) # v[AA+1] <- eax
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHL_LONG: /* 0xa3 */
+/* File: x86/OP_SHL_LONG.S */
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to mask off the low
+ * 6 bits of the shift distance. x86 shifts automatically mask off
+ * the low 5 bits of %cl, so have to handle the 64 > shiftcount > 31
+ * case specially.
+ */
+ /* shl-long vAA, vBB, vCC */
+ /* ecx gets shift count */
+ /* Need to spill edx */
+ /* rINST gets AA */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ SPILL(rPC) # spill edx
+ GET_VREG_WORD(%edx,%eax,1) # ecx<- v[BB+1]
+ GET_VREG (%ecx,%ecx) # ecx<- vCC
+ GET_VREG_WORD(%eax,%eax,0) # eax<- v[BB+0]
+ shldl %eax,%edx
+ sall %cl,%eax
+ testb $32,%cl
+ je 2f
+ movl %eax,%edx
+ xorl %eax,%eax
+2:
+ movzbl rINST_HI,%ecx
+ SET_VREG_WORD(%edx,%ecx,1) # v[AA+1]<- %edx
+ UNSPILL(rPC)
+ FETCH_INST_WORD(2)
+ jmp .LOP_SHL_LONG_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHR_LONG: /* 0xa4 */
+/* File: x86/OP_SHR_LONG.S */
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to mask off the low
+ * 6 bits of the shift distance. x86 shifts automatically mask off
+ * the low 5 bits of %cl, so have to handle the 64 > shiftcount > 31
+ * case specially.
+ */
+ /* shr-long vAA, vBB, vCC */
+ /* ecx gets shift count */
+ /* Need to spill edx */
+ /* rINST gets AA */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ SPILL(rPC) # spill edx
+ GET_VREG_WORD(%edx,%eax,1) # edx<- v[BB+1]
+ GET_VREG (%ecx,%ecx) # ecx<- vCC
+ GET_VREG_WORD(%eax,%eax,0) # eax<- v[BB+0]
+ shrdl %edx,%eax
+ sarl %cl,%edx
+ testb $32,%cl
+ je 2f
+ movl %edx,%eax
+ sarl $31,%edx
+2:
+ movzbl rINST_HI,%ecx
+ SET_VREG_WORD(%edx,%ecx,1) # v[AA+1]<- edx
+ UNSPILL(rPC)
+ FETCH_INST_WORD(2)
+ jmp .LOP_SHR_LONG_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_USHR_LONG: /* 0xa5 */
+/* File: x86/OP_USHR_LONG.S */
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to mask off the low
+ * 6 bits of the shift distance. x86 shifts automatically mask off
+ * the low 5 bits of %cl, so have to handle the 64 > shiftcount > 31
+ * case specially.
+ */
+ /* shr-long vAA, vBB, vCC */
+ /* ecx gets shift count */
+ /* Need to spill edx */
+ /* rINST gets AA */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ SPILL(rPC) # spill edx
+ GET_VREG_WORD(%edx,%eax,1) # edx<- v[BB+1]
+ GET_VREG (%ecx,%ecx) # ecx<- vCC
+ GET_VREG_WORD(%eax,%eax,0) # eax<- v[BB+0]
+ shrdl %edx,%eax
+ shrl %cl,%edx
+ testb $32,%cl
+ je 2f
+ movl %edx,%eax
+ xorl %edx,%edx
+2:
+ movzbl rINST_HI,%ecx
+ SET_VREG_WORD(%edx,%ecx,1) # v[BB+1]<- edx
+ UNSPILL(rPC)
+ jmp .LOP_USHR_LONG_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_FLOAT: /* 0xa6 */
+/* File: x86/OP_ADD_FLOAT.S */
+/* File: x86/binflop.S */
+ /*
+ * Generic 32-bit binary float operation.
+ *
+ * For: add-fp, sub-fp, mul-fp, div-fp
+ */
+ /* binop vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- CC
+ movzbl 3(rPC),%ecx # ecx<- BB
+ flds (rFP,%eax,4) # vCC to fp stack
+ fadds (rFP,%ecx,4) # ex: faddp
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ fstps (rFP,%ecx,4) # %st to vAA
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_FLOAT: /* 0xa7 */
+/* File: x86/OP_SUB_FLOAT.S */
+/* File: x86/binflop.S */
+ /*
+ * Generic 32-bit binary float operation.
+ *
+ * For: add-fp, sub-fp, mul-fp, div-fp
+ */
+ /* binop vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- CC
+ movzbl 3(rPC),%ecx # ecx<- BB
+ flds (rFP,%eax,4) # vCC to fp stack
+ fsubs (rFP,%ecx,4) # ex: faddp
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ fstps (rFP,%ecx,4) # %st to vAA
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_FLOAT: /* 0xa8 */
+/* File: x86/OP_MUL_FLOAT.S */
+/* File: x86/binflop.S */
+ /*
+ * Generic 32-bit binary float operation.
+ *
+ * For: add-fp, sub-fp, mul-fp, div-fp
+ */
+ /* binop vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- CC
+ movzbl 3(rPC),%ecx # ecx<- BB
+ flds (rFP,%eax,4) # vCC to fp stack
+ fmuls (rFP,%ecx,4) # ex: faddp
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ fstps (rFP,%ecx,4) # %st to vAA
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_FLOAT: /* 0xa9 */
+/* File: x86/OP_DIV_FLOAT.S */
+/* File: x86/binflop.S */
+ /*
+ * Generic 32-bit binary float operation.
+ *
+ * For: add-fp, sub-fp, mul-fp, div-fp
+ */
+ /* binop vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- CC
+ movzbl 3(rPC),%ecx # ecx<- BB
+ flds (rFP,%eax,4) # vCC to fp stack
+ fdivs (rFP,%ecx,4) # ex: faddp
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ fstps (rFP,%ecx,4) # %st to vAA
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_FLOAT: /* 0xaa */
+/* File: x86/OP_REM_FLOAT.S */
+ /* rem_float vAA, vBB, vCC */
+ movzbl 3(rPC),%ecx # ecx<- BB
+ movzbl 2(rPC),%eax # eax<- CC
+ flds (rFP,%ecx,4) # vCC to fp stack
+ flds (rFP,%eax,4) # vCC to fp stack
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(2)
+1:
+ fprem
+ fstsw %ax
+ sahf
+ jp 1b
+ fstp %st(1)
+ ADVANCE_PC(2)
+ fstps (rFP,%ecx,4) # %st to vAA
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_DOUBLE: /* 0xab */
+/* File: x86/OP_ADD_DOUBLE.S */
+/* File: x86/binflop.S */
+ /*
+ * Generic 32-bit binary float operation.
+ *
+ * For: add-fp, sub-fp, mul-fp, div-fp
+ */
+ /* binop vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- CC
+ movzbl 3(rPC),%ecx # ecx<- BB
+ fldl (rFP,%eax,4) # vCC to fp stack
+ faddl (rFP,%ecx,4) # ex: faddp
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ fstpl (rFP,%ecx,4) # %st to vAA
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_DOUBLE: /* 0xac */
+/* File: x86/OP_SUB_DOUBLE.S */
+/* File: x86/binflop.S */
+ /*
+ * Generic 32-bit binary float operation.
+ *
+ * For: add-fp, sub-fp, mul-fp, div-fp
+ */
+ /* binop vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- CC
+ movzbl 3(rPC),%ecx # ecx<- BB
+ fldl (rFP,%eax,4) # vCC to fp stack
+ fsubl (rFP,%ecx,4) # ex: faddp
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ fstpl (rFP,%ecx,4) # %st to vAA
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_DOUBLE: /* 0xad */
+/* File: x86/OP_MUL_DOUBLE.S */
+/* File: x86/binflop.S */
+ /*
+ * Generic 32-bit binary float operation.
+ *
+ * For: add-fp, sub-fp, mul-fp, div-fp
+ */
+ /* binop vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- CC
+ movzbl 3(rPC),%ecx # ecx<- BB
+ fldl (rFP,%eax,4) # vCC to fp stack
+ fmull (rFP,%ecx,4) # ex: faddp
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ fstpl (rFP,%ecx,4) # %st to vAA
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_DOUBLE: /* 0xae */
+/* File: x86/OP_DIV_DOUBLE.S */
+/* File: x86/binflop.S */
+ /*
+ * Generic 32-bit binary float operation.
+ *
+ * For: add-fp, sub-fp, mul-fp, div-fp
+ */
+ /* binop vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- CC
+ movzbl 3(rPC),%ecx # ecx<- BB
+ fldl (rFP,%eax,4) # vCC to fp stack
+ fdivl (rFP,%ecx,4) # ex: faddp
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ fstpl (rFP,%ecx,4) # %st to vAA
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_DOUBLE: /* 0xaf */
+/* File: x86/OP_REM_DOUBLE.S */
+ /* rem_float vAA, vBB, vCC */
+ movzbl 3(rPC),%ecx # ecx<- BB
+ movzbl 2(rPC),%eax # eax<- CC
+ fldl (rFP,%ecx,4) # vCC to fp stack
+ fldl (rFP,%eax,4) # vCC to fp stack
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(2)
+1:
+ fprem
+ fstsw %ax
+ sahf
+ jp 1b
+ fstp %st(1)
+ ADVANCE_PC(2)
+ fstpl (rFP,%ecx,4) # %st to vAA
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_INT_2ADDR: /* 0xb0 */
+/* File: x86/OP_ADD_INT_2ADDR.S */
+/* File: x86/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ movzx rINST_HI,%ecx # ecx<- A+
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ GET_VREG(%eax,rINST_FULL) # eax<- vB
+ FETCH_INST_WORD(1)
+ andb $0xf,%cl # ecx<- A
+ addl %eax,(rFP,%ecx,4) # for ex: addl %eax,(rFP,%ecx,4)
+ ADVANCE_PC(1)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_INT_2ADDR: /* 0xb1 */
+/* File: x86/OP_SUB_INT_2ADDR.S */
+/* File: x86/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ movzx rINST_HI,%ecx # ecx<- A+
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ GET_VREG(%eax,rINST_FULL) # eax<- vB
+ FETCH_INST_WORD(1)
+ andb $0xf,%cl # ecx<- A
+ subl %eax,(rFP,%ecx,4) # for ex: addl %eax,(rFP,%ecx,4)
+ ADVANCE_PC(1)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_INT_2ADDR: /* 0xb2 */
+/* File: x86/OP_MUL_INT_2ADDR.S */
+ /* mul vA, vB */
+ movzx rINST_HI,%ecx # ecx<- A+
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ GET_VREG(%eax,rINST_FULL) # eax<- vB
+ andb $0xf,%cl # ecx<- A
+ SPILL(rPC)
+ imull (rFP,%ecx,4),%eax
+ UNSPILL(rPC)
+ SET_VREG(%eax,%ecx)
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_INT_2ADDR: /* 0xb3 */
+/* File: x86/OP_DIV_INT_2ADDR.S */
+/* File: x86/bindiv2addr.S */
+ /*
+ * 32-bit binary div/rem operation. Handles special case of op0=minint and
+ * op1=-1.
+ */
+ /* div/rem/2addr vA, vB */
+ movzx rINST_HI,%ecx # eax<- BA
+ sarl $4,%ecx # ecx<- B
+ GET_VREG(%ecx,%ecx) # eax<- vBB
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG(%eax,rINST_FULL) # eax<- vBB
+ SPILL(rPC)
+ cmpl $0,%ecx
+ je common_errDivideByZero
+ cmpl $-1,%ecx
+ jne .LOP_DIV_INT_2ADDR_continue_div2addr
+ cmpl $0x80000000,%eax
+ jne .LOP_DIV_INT_2ADDR_continue_div2addr
+ movl $0x80000000,%eax
+ jmp .LOP_DIV_INT_2ADDR_finish_div2addr
+
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_INT_2ADDR: /* 0xb4 */
+/* File: x86/OP_REM_INT_2ADDR.S */
+/* File: x86/bindiv2addr.S */
+ /*
+ * 32-bit binary div/rem operation. Handles special case of op0=minint and
+ * op1=-1.
+ */
+ /* div/rem/2addr vA, vB */
+ movzx rINST_HI,%ecx # eax<- BA
+ sarl $4,%ecx # ecx<- B
+ GET_VREG(%ecx,%ecx) # eax<- vBB
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG(%eax,rINST_FULL) # eax<- vBB
+ SPILL(rPC)
+ cmpl $0,%ecx
+ je common_errDivideByZero
+ cmpl $-1,%ecx
+ jne .LOP_REM_INT_2ADDR_continue_div2addr
+ cmpl $0x80000000,%eax
+ jne .LOP_REM_INT_2ADDR_continue_div2addr
+ movl $0,%edx
+ jmp .LOP_REM_INT_2ADDR_finish_div2addr
+
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_INT_2ADDR: /* 0xb5 */
+/* File: x86/OP_AND_INT_2ADDR.S */
+/* File: x86/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ movzx rINST_HI,%ecx # ecx<- A+
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ GET_VREG(%eax,rINST_FULL) # eax<- vB
+ FETCH_INST_WORD(1)
+ andb $0xf,%cl # ecx<- A
+ andl %eax,(rFP,%ecx,4) # for ex: addl %eax,(rFP,%ecx,4)
+ ADVANCE_PC(1)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_INT_2ADDR: /* 0xb6 */
+/* File: x86/OP_OR_INT_2ADDR.S */
+/* File: x86/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ movzx rINST_HI,%ecx # ecx<- A+
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ GET_VREG(%eax,rINST_FULL) # eax<- vB
+ FETCH_INST_WORD(1)
+ andb $0xf,%cl # ecx<- A
+ orl %eax,(rFP,%ecx,4) # for ex: addl %eax,(rFP,%ecx,4)
+ ADVANCE_PC(1)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_INT_2ADDR: /* 0xb7 */
+/* File: x86/OP_XOR_INT_2ADDR.S */
+/* File: x86/binop2addr.S */
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ movzx rINST_HI,%ecx # ecx<- A+
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ GET_VREG(%eax,rINST_FULL) # eax<- vB
+ FETCH_INST_WORD(1)
+ andb $0xf,%cl # ecx<- A
+ xorl %eax,(rFP,%ecx,4) # for ex: addl %eax,(rFP,%ecx,4)
+ ADVANCE_PC(1)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHL_INT_2ADDR: /* 0xb8 */
+/* File: x86/OP_SHL_INT_2ADDR.S */
+/* File: x86/shop2addr.S */
+ /*
+ * Generic 32-bit "shift/2addr" operation.
+ */
+ /* shift/2addr vA, vB */
+ movzx rINST_HI,%ecx # eax<- BA
+ sarl $4,%ecx # ecx<- B
+ GET_VREG(%ecx,%ecx) # eax<- vBB
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG(%eax,rINST_FULL) # eax<- vAA
+ sall %cl,%eax # ex: sarl %cl,%eax
+ SET_VREG(%eax,rINST_FULL)
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHR_INT_2ADDR: /* 0xb9 */
+/* File: x86/OP_SHR_INT_2ADDR.S */
+/* File: x86/shop2addr.S */
+ /*
+ * Generic 32-bit "shift/2addr" operation.
+ */
+ /* shift/2addr vA, vB */
+ movzx rINST_HI,%ecx # eax<- BA
+ sarl $4,%ecx # ecx<- B
+ GET_VREG(%ecx,%ecx) # eax<- vBB
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG(%eax,rINST_FULL) # eax<- vAA
+ sarl %cl,%eax # ex: sarl %cl,%eax
+ SET_VREG(%eax,rINST_FULL)
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_USHR_INT_2ADDR: /* 0xba */
+/* File: x86/OP_USHR_INT_2ADDR.S */
+/* File: x86/shop2addr.S */
+ /*
+ * Generic 32-bit "shift/2addr" operation.
+ */
+ /* shift/2addr vA, vB */
+ movzx rINST_HI,%ecx # eax<- BA
+ sarl $4,%ecx # ecx<- B
+ GET_VREG(%ecx,%ecx) # eax<- vBB
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG(%eax,rINST_FULL) # eax<- vAA
+ shrl %cl,%eax # ex: sarl %cl,%eax
+ SET_VREG(%eax,rINST_FULL)
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_LONG_2ADDR: /* 0xbb */
+/* File: x86/OP_ADD_LONG_2ADDR.S */
+/* File: x86/binopWide2addr.S */
+ /*
+ * Generic 64-bit binary operation.
+ */
+ /* binop/2addr vA, vB */
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $4,%ecx # ecx<- B
+ GET_VREG_WORD(%eax,%ecx,0) # eax<- v[B+0]
+ GET_VREG_WORD(%ecx,%ecx,1) # eax<- v[B+1]
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $0xF,rINST_LO # rINST_FULL<- A
+ addl %eax,(rFP,rINST_FULL,4) # example: addl %eax,(rFP,rINST_FULL,4)
+ adcl %ecx,4(rFP,rINST_FULL,4) # example: adcl %ecx,4(rFP,rINST_FULL,4)
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_LONG_2ADDR: /* 0xbc */
+/* File: x86/OP_SUB_LONG_2ADDR.S */
+/* File: x86/binopWide2addr.S */
+ /*
+ * Generic 64-bit binary operation.
+ */
+ /* binop/2addr vA, vB */
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $4,%ecx # ecx<- B
+ GET_VREG_WORD(%eax,%ecx,0) # eax<- v[B+0]
+ GET_VREG_WORD(%ecx,%ecx,1) # eax<- v[B+1]
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $0xF,rINST_LO # rINST_FULL<- A
+ subl %eax,(rFP,rINST_FULL,4) # example: addl %eax,(rFP,rINST_FULL,4)
+ sbbl %ecx,4(rFP,rINST_FULL,4) # example: adcl %ecx,4(rFP,rINST_FULL,4)
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_LONG_2ADDR: /* 0xbd */
+/* File: x86/OP_MUL_LONG_2ADDR.S */
+ /*
+ * Signed 64-bit integer multiply, 2-addr version
+ *
+ * We could definately use more free registers for
+ * this code. We must spill rPC (edx) because it
+ * is used by imul. We'll also spill rINST (ebx),
+ * giving us eax, ebc, ecx and edx as computational
+ * temps. On top of that, we'll spill rIBASE (edi)
+ * for use as the vA pointer and rFP (esi) for use
+ * as the vB pointer. Yuck.
+ */
+ /* mul-long/2addr vA, vB */
+ movzbl rINST_HI,%eax # eax<- BA
+ andb $0xf,%al # eax<- A
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ SPILL(rPC)
+ SPILL(rIBASE)
+ SPILL(rFP)
+ leal (rFP,%eax,4),rIBASE # rIBASE<- &v[A]
+ leal (rFP,rINST_FULL,4),rFP # rFP<- &v[B]
+ movl 4(rIBASE),%ecx # ecx<- Amsw
+ imull (rFP),%ecx # ecx<- (Amsw*Blsw)
+ movl 4(rFP),%eax # eax<- Bmsw
+ imull (rIBASE),%eax # eax<- (Bmsw*Alsw)
+ addl %eax,%ecx # ecx<- (Amsw*Blsw)+(Bmsw*Alsw)
+ movl (rFP),%eax # eax<- Blsw
+ mull (rIBASE) # eax<- (Blsw*Alsw)
+ jmp .LOP_MUL_LONG_2ADDR_continue
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_LONG_2ADDR: /* 0xbe */
+/* File: x86/OP_DIV_LONG_2ADDR.S */
+ /* div/2addr vA, vB */
+ movzbl rINST_HI,%eax
+ shrl $4,%eax # eax<- B
+ movzbl rINST_HI,rINST_FULL
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ SPILL(rPC)
+ GET_VREG_WORD(rPC,%eax,0)
+ GET_VREG_WORD(%eax,%eax,1)
+ movl rPC,OUT_ARG2(%esp)
+ testl %eax,%eax
+ je .LOP_DIV_LONG_2ADDR_check_zero
+ cmpl $-1,%eax
+ je .LOP_DIV_LONG_2ADDR_check_neg1
+.LOP_DIV_LONG_2ADDR_notSpecial:
+ GET_VREG_WORD(rPC,rINST_FULL,0)
+ GET_VREG_WORD(%ecx,rINST_FULL,1)
+.LOP_DIV_LONG_2ADDR_notSpecial1:
+ jmp .LOP_DIV_LONG_2ADDR_continue
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_LONG_2ADDR: /* 0xbf */
+/* File: x86/OP_REM_LONG_2ADDR.S */
+/* File: x86/OP_DIV_LONG_2ADDR.S */
+ /* div/2addr vA, vB */
+ movzbl rINST_HI,%eax
+ shrl $4,%eax # eax<- B
+ movzbl rINST_HI,rINST_FULL
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ SPILL(rPC)
+ GET_VREG_WORD(rPC,%eax,0)
+ GET_VREG_WORD(%eax,%eax,1)
+ movl rPC,OUT_ARG2(%esp)
+ testl %eax,%eax
+ je .LOP_REM_LONG_2ADDR_check_zero
+ cmpl $-1,%eax
+ je .LOP_REM_LONG_2ADDR_check_neg1
+.LOP_REM_LONG_2ADDR_notSpecial:
+ GET_VREG_WORD(rPC,rINST_FULL,0)
+ GET_VREG_WORD(%ecx,rINST_FULL,1)
+.LOP_REM_LONG_2ADDR_notSpecial1:
+ jmp .LOP_REM_LONG_2ADDR_continue
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_LONG_2ADDR: /* 0xc0 */
+/* File: x86/OP_AND_LONG_2ADDR.S */
+/* File: x86/binopWide2addr.S */
+ /*
+ * Generic 64-bit binary operation.
+ */
+ /* binop/2addr vA, vB */
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $4,%ecx # ecx<- B
+ GET_VREG_WORD(%eax,%ecx,0) # eax<- v[B+0]
+ GET_VREG_WORD(%ecx,%ecx,1) # eax<- v[B+1]
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $0xF,rINST_LO # rINST_FULL<- A
+ andl %eax,(rFP,rINST_FULL,4) # example: addl %eax,(rFP,rINST_FULL,4)
+ andl %ecx,4(rFP,rINST_FULL,4) # example: adcl %ecx,4(rFP,rINST_FULL,4)
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_LONG_2ADDR: /* 0xc1 */
+/* File: x86/OP_OR_LONG_2ADDR.S */
+/* File: x86/binopWide2addr.S */
+ /*
+ * Generic 64-bit binary operation.
+ */
+ /* binop/2addr vA, vB */
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $4,%ecx # ecx<- B
+ GET_VREG_WORD(%eax,%ecx,0) # eax<- v[B+0]
+ GET_VREG_WORD(%ecx,%ecx,1) # eax<- v[B+1]
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $0xF,rINST_LO # rINST_FULL<- A
+ orl %eax,(rFP,rINST_FULL,4) # example: addl %eax,(rFP,rINST_FULL,4)
+ orl %ecx,4(rFP,rINST_FULL,4) # example: adcl %ecx,4(rFP,rINST_FULL,4)
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_LONG_2ADDR: /* 0xc2 */
+/* File: x86/OP_XOR_LONG_2ADDR.S */
+/* File: x86/binopWide2addr.S */
+ /*
+ * Generic 64-bit binary operation.
+ */
+ /* binop/2addr vA, vB */
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $4,%ecx # ecx<- B
+ GET_VREG_WORD(%eax,%ecx,0) # eax<- v[B+0]
+ GET_VREG_WORD(%ecx,%ecx,1) # eax<- v[B+1]
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $0xF,rINST_LO # rINST_FULL<- A
+ xorl %eax,(rFP,rINST_FULL,4) # example: addl %eax,(rFP,rINST_FULL,4)
+ xorl %ecx,4(rFP,rINST_FULL,4) # example: adcl %ecx,4(rFP,rINST_FULL,4)
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHL_LONG_2ADDR: /* 0xc3 */
+/* File: x86/OP_SHL_LONG_2ADDR.S */
+ /*
+ * Long integer shift, 2addr version. vA is 64-bit value/result, vB is
+ * 32-bit shift distance.
+ */
+ /* shl-long/2addr vA, vB */
+ /* ecx gets shift count */
+ /* Need to spill edx */
+ /* rINST gets AA */
+ movzbl rINST_HI,%ecx # ecx<- BA
+ movzbl rINST_HI,rINST_FULL # rINST_HI<- BA
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG_WORD(%eax,rINST_FULL,0) # eax<- v[AA+0]
+ sarl $4,%ecx # ecx<- B
+ SPILL(rPC)
+ GET_VREG_WORD(%edx,rINST_FULL,1) # edx<- v[AA+1]
+ GET_VREG(%ecx,%ecx) # ecx<- vBB
+ shldl %eax,%edx
+ sall %cl,%eax
+ testb $32,%cl
+ je 2f
+ movl %eax,%edx
+ xorl %eax,%eax
+2:
+ SET_VREG_WORD(%edx,rINST_FULL,1) # v[AA+1]<- edx
+ UNSPILL(rPC)
+ jmp .LOP_SHL_LONG_2ADDR_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHR_LONG_2ADDR: /* 0xc4 */
+/* File: x86/OP_SHR_LONG_2ADDR.S */
+ /*
+ * Long integer shift, 2addr version. vA is 64-bit value/result, vB is
+ * 32-bit shift distance.
+ */
+ /* shl-long/2addr vA, vB */
+ /* ecx gets shift count */
+ /* Need to spill edx */
+ /* rINST gets AA */
+ movzbl rINST_HI,%ecx # ecx<- BA
+ movzbl rINST_HI,rINST_FULL # rINST_HI<- BA
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG_WORD(%eax,rINST_FULL,0) # eax<- v[AA+0]
+ sarl $4,%ecx # ecx<- B
+ SPILL(rPC)
+ GET_VREG_WORD(%edx,rINST_FULL,1) # edx<- v[AA+1]
+ GET_VREG(%ecx,%ecx) # ecx<- vBB
+ shrdl %edx,%eax
+ sarl %cl,%edx
+ testb $32,%cl
+ je 2f
+ movl %edx,%eax
+ sarl $31,%edx
+2:
+ SET_VREG_WORD(%edx,rINST_FULL,1) # v[AA+1]<- edx
+ UNSPILL(rPC)
+ jmp .LOP_SHR_LONG_2ADDR_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_USHR_LONG_2ADDR: /* 0xc5 */
+/* File: x86/OP_USHR_LONG_2ADDR.S */
+ /*
+ * Long integer shift, 2addr version. vA is 64-bit value/result, vB is
+ * 32-bit shift distance.
+ */
+ /* shl-long/2addr vA, vB */
+ /* ecx gets shift count */
+ /* Need to spill edx */
+ /* rINST gets AA */
+ movzbl rINST_HI,%ecx # ecx<- BA
+ movzbl rINST_HI,rINST_FULL # rINST_HI<- BA
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG_WORD(%eax,rINST_FULL,0) # eax<- v[AA+0]
+ sarl $4,%ecx # ecx<- B
+ SPILL(rPC)
+ GET_VREG_WORD(%edx,rINST_FULL,1) # edx<- v[AA+1]
+ GET_VREG(%ecx,%ecx) # ecx<- vBB
+ shrdl %edx,%eax
+ shrl %cl,%edx
+ testb $32,%cl
+ je 2f
+ movl %edx,%eax
+ xorl %edx,%edx
+2:
+ SET_VREG_WORD(%edx,rINST_FULL,1) # v[AA+1]<- edx
+ UNSPILL(rPC)
+ jmp .LOP_USHR_LONG_2ADDR_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_FLOAT_2ADDR: /* 0xc6 */
+/* File: x86/OP_ADD_FLOAT_2ADDR.S */
+/* File: x86/binflop2addr.S */
+ /*
+ * Generic 32-bit binary float operation.
+ *
+ * For: add-fp, sub-fp, mul-fp, div-fp
+ */
+
+ /* binop/2addr vA, vB */
+ movzx rINST_HI,%ecx # ecx<- A+
+ andb $0xf,%cl # ecx<- A
+ flds (rFP,%ecx,4) # vAA to fp stack
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ fadds (rFP,rINST_FULL,4) # ex: faddp
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ fstps (rFP,%ecx,4) # %st to vA
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_FLOAT_2ADDR: /* 0xc7 */
+/* File: x86/OP_SUB_FLOAT_2ADDR.S */
+/* File: x86/binflop2addr.S */
+ /*
+ * Generic 32-bit binary float operation.
+ *
+ * For: add-fp, sub-fp, mul-fp, div-fp
+ */
+
+ /* binop/2addr vA, vB */
+ movzx rINST_HI,%ecx # ecx<- A+
+ andb $0xf,%cl # ecx<- A
+ flds (rFP,%ecx,4) # vAA to fp stack
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ fsubs (rFP,rINST_FULL,4) # ex: faddp
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ fstps (rFP,%ecx,4) # %st to vA
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_FLOAT_2ADDR: /* 0xc8 */
+/* File: x86/OP_MUL_FLOAT_2ADDR.S */
+/* File: x86/binflop2addr.S */
+ /*
+ * Generic 32-bit binary float operation.
+ *
+ * For: add-fp, sub-fp, mul-fp, div-fp
+ */
+
+ /* binop/2addr vA, vB */
+ movzx rINST_HI,%ecx # ecx<- A+
+ andb $0xf,%cl # ecx<- A
+ flds (rFP,%ecx,4) # vAA to fp stack
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ fmuls (rFP,rINST_FULL,4) # ex: faddp
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ fstps (rFP,%ecx,4) # %st to vA
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_FLOAT_2ADDR: /* 0xc9 */
+/* File: x86/OP_DIV_FLOAT_2ADDR.S */
+/* File: x86/binflop2addr.S */
+ /*
+ * Generic 32-bit binary float operation.
+ *
+ * For: add-fp, sub-fp, mul-fp, div-fp
+ */
+
+ /* binop/2addr vA, vB */
+ movzx rINST_HI,%ecx # ecx<- A+
+ andb $0xf,%cl # ecx<- A
+ flds (rFP,%ecx,4) # vAA to fp stack
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ fdivs (rFP,rINST_FULL,4) # ex: faddp
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ fstps (rFP,%ecx,4) # %st to vA
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_FLOAT_2ADDR: /* 0xca */
+/* File: x86/OP_REM_FLOAT_2ADDR.S */
+ /* rem_float/2addr vA, vB */
+ movzx rINST_HI,%ecx # ecx<- A+
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ flds (rFP,rINST_FULL,4) # vBB to fp stack
+ andb $0xf,%cl # ecx<- A
+ flds (rFP,%ecx,4) # vAA to fp stack
+ FETCH_INST_WORD(1)
+1:
+ fprem
+ fstsw %ax
+ sahf
+ jp 1b
+ fstp %st(1)
+ ADVANCE_PC(1)
+ fstps (rFP,%ecx,4) # %st to vA
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_DOUBLE_2ADDR: /* 0xcb */
+/* File: x86/OP_ADD_DOUBLE_2ADDR.S */
+/* File: x86/binflop2addr.S */
+ /*
+ * Generic 32-bit binary float operation.
+ *
+ * For: add-fp, sub-fp, mul-fp, div-fp
+ */
+
+ /* binop/2addr vA, vB */
+ movzx rINST_HI,%ecx # ecx<- A+
+ andb $0xf,%cl # ecx<- A
+ fldl (rFP,%ecx,4) # vAA to fp stack
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ faddl (rFP,rINST_FULL,4) # ex: faddp
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ fstpl (rFP,%ecx,4) # %st to vA
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SUB_DOUBLE_2ADDR: /* 0xcc */
+/* File: x86/OP_SUB_DOUBLE_2ADDR.S */
+/* File: x86/binflop2addr.S */
+ /*
+ * Generic 32-bit binary float operation.
+ *
+ * For: add-fp, sub-fp, mul-fp, div-fp
+ */
+
+ /* binop/2addr vA, vB */
+ movzx rINST_HI,%ecx # ecx<- A+
+ andb $0xf,%cl # ecx<- A
+ fldl (rFP,%ecx,4) # vAA to fp stack
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ fsubl (rFP,rINST_FULL,4) # ex: faddp
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ fstpl (rFP,%ecx,4) # %st to vA
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_DOUBLE_2ADDR: /* 0xcd */
+/* File: x86/OP_MUL_DOUBLE_2ADDR.S */
+/* File: x86/binflop2addr.S */
+ /*
+ * Generic 32-bit binary float operation.
+ *
+ * For: add-fp, sub-fp, mul-fp, div-fp
+ */
+
+ /* binop/2addr vA, vB */
+ movzx rINST_HI,%ecx # ecx<- A+
+ andb $0xf,%cl # ecx<- A
+ fldl (rFP,%ecx,4) # vAA to fp stack
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ fmull (rFP,rINST_FULL,4) # ex: faddp
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ fstpl (rFP,%ecx,4) # %st to vA
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_DOUBLE_2ADDR: /* 0xce */
+/* File: x86/OP_DIV_DOUBLE_2ADDR.S */
+/* File: x86/binflop2addr.S */
+ /*
+ * Generic 32-bit binary float operation.
+ *
+ * For: add-fp, sub-fp, mul-fp, div-fp
+ */
+
+ /* binop/2addr vA, vB */
+ movzx rINST_HI,%ecx # ecx<- A+
+ andb $0xf,%cl # ecx<- A
+ fldl (rFP,%ecx,4) # vAA to fp stack
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ fdivl (rFP,rINST_FULL,4) # ex: faddp
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ fstpl (rFP,%ecx,4) # %st to vA
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_DOUBLE_2ADDR: /* 0xcf */
+/* File: x86/OP_REM_DOUBLE_2ADDR.S */
+ /* rem_float/2addr vA, vB */
+ movzx rINST_HI,%ecx # ecx<- A+
+ sarl $12,rINST_FULL # rINST_FULL<- B
+ fldl (rFP,rINST_FULL,4) # vBB to fp stack
+ andb $0xf,%cl # ecx<- A
+ fldl (rFP,%ecx,4) # vAA to fp stack
+ FETCH_INST_WORD(1)
+1:
+ fprem
+ fstsw %ax
+ sahf
+ jp 1b
+ fstp %st(1)
+ ADVANCE_PC(1)
+ fstpl (rFP,%ecx,4) # %st to vA
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_INT_LIT16: /* 0xd0 */
+/* File: x86/OP_ADD_INT_LIT16.S */
+/* File: x86/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = eax op ecx".
+ * This could be an x86 instruction or a function call. (If the result
+ * comes back in a register other than eax, you can override "result".)
+ *
+ * For: add-int/lit16, rsub-int,
+ * and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ movzbl rINST_HI,%eax # eax<- 000000BA
+ sarl $4,%eax # eax<- B
+ GET_VREG(%eax,%eax) # eax<- vB
+ movswl 2(rPC),%ecx # ecx<- ssssCCCC
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ addl %ecx,%eax # for example: addl %ecx, %eax
+ SET_VREG(%eax,rINST_FULL)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RSUB_INT: /* 0xd1 */
+/* File: x86/OP_RSUB_INT.S */
+/* File: x86/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = eax op ecx".
+ * This could be an x86 instruction or a function call. (If the result
+ * comes back in a register other than eax, you can override "result".)
+ *
+ * For: add-int/lit16, rsub-int,
+ * and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ movzbl rINST_HI,%eax # eax<- 000000BA
+ sarl $4,%eax # eax<- B
+ GET_VREG(%eax,%eax) # eax<- vB
+ movswl 2(rPC),%ecx # ecx<- ssssCCCC
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ subl %eax,%ecx # for example: addl %ecx, %eax
+ SET_VREG(%ecx,rINST_FULL)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_INT_LIT16: /* 0xd2 */
+/* File: x86/OP_MUL_INT_LIT16.S */
+ /* mul/lit16 vA, vB, #+CCCC */
+ /* Need A in rINST_FULL, ssssCCCC in ecx, vB in eax */
+ movzbl rINST_HI,%eax # eax<- 000000BA
+ sarl $4,%eax # eax<- B
+ GET_VREG(%eax,%eax) # eax<- vB
+ movswl 2(rPC),%ecx # ecx<- ssssCCCC
+ SPILL(rPC)
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ imull %ecx,%eax # trashes rPC
+ UNSPILL(rPC)
+ SET_VREG(%eax,rINST_FULL)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_INT_LIT16: /* 0xd3 */
+/* File: x86/OP_DIV_INT_LIT16.S */
+/* File: x86/bindivLit16.S */
+ /*
+ * 32-bit binary div/rem operation. Handles special case of op0=minint and
+ * op1=-1.
+ */
+ /* div/rem/lit16 vA, vB, #+CCCC */
+ /* Need A in rINST_FULL, ssssCCCC in ecx, vB in eax */
+ movzbl rINST_HI,%eax # eax<- 000000BA
+ sarl $4,%eax # eax<- B
+ GET_VREG(%eax,%eax) # eax<- vB
+ movswl 2(rPC),%ecx # ecx<- ssssCCCC
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ SPILL(rPC)
+ cmpl $0,%ecx
+ je common_errDivideByZero
+ cmpl $-1,%ecx
+ jne .LOP_DIV_INT_LIT16_continue_div
+ cmpl $0x80000000,%eax
+ jne .LOP_DIV_INT_LIT16_continue_div
+ movl $0x80000000,%eax
+ jmp .LOP_DIV_INT_LIT16_finish_div
+
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_INT_LIT16: /* 0xd4 */
+/* File: x86/OP_REM_INT_LIT16.S */
+/* File: x86/bindivLit16.S */
+ /*
+ * 32-bit binary div/rem operation. Handles special case of op0=minint and
+ * op1=-1.
+ */
+ /* div/rem/lit16 vA, vB, #+CCCC */
+ /* Need A in rINST_FULL, ssssCCCC in ecx, vB in eax */
+ movzbl rINST_HI,%eax # eax<- 000000BA
+ sarl $4,%eax # eax<- B
+ GET_VREG(%eax,%eax) # eax<- vB
+ movswl 2(rPC),%ecx # ecx<- ssssCCCC
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ SPILL(rPC)
+ cmpl $0,%ecx
+ je common_errDivideByZero
+ cmpl $-1,%ecx
+ jne .LOP_REM_INT_LIT16_continue_div
+ cmpl $0x80000000,%eax
+ jne .LOP_REM_INT_LIT16_continue_div
+ movl $0,%edx
+ jmp .LOP_REM_INT_LIT16_finish_div
+
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_INT_LIT16: /* 0xd5 */
+/* File: x86/OP_AND_INT_LIT16.S */
+/* File: x86/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = eax op ecx".
+ * This could be an x86 instruction or a function call. (If the result
+ * comes back in a register other than eax, you can override "result".)
+ *
+ * For: add-int/lit16, rsub-int,
+ * and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ movzbl rINST_HI,%eax # eax<- 000000BA
+ sarl $4,%eax # eax<- B
+ GET_VREG(%eax,%eax) # eax<- vB
+ movswl 2(rPC),%ecx # ecx<- ssssCCCC
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ andl %ecx,%eax # for example: addl %ecx, %eax
+ SET_VREG(%eax,rINST_FULL)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_INT_LIT16: /* 0xd6 */
+/* File: x86/OP_OR_INT_LIT16.S */
+/* File: x86/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = eax op ecx".
+ * This could be an x86 instruction or a function call. (If the result
+ * comes back in a register other than eax, you can override "result".)
+ *
+ * For: add-int/lit16, rsub-int,
+ * and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ movzbl rINST_HI,%eax # eax<- 000000BA
+ sarl $4,%eax # eax<- B
+ GET_VREG(%eax,%eax) # eax<- vB
+ movswl 2(rPC),%ecx # ecx<- ssssCCCC
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ orl %ecx,%eax # for example: addl %ecx, %eax
+ SET_VREG(%eax,rINST_FULL)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_INT_LIT16: /* 0xd7 */
+/* File: x86/OP_XOR_INT_LIT16.S */
+/* File: x86/binopLit16.S */
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = eax op ecx".
+ * This could be an x86 instruction or a function call. (If the result
+ * comes back in a register other than eax, you can override "result".)
+ *
+ * For: add-int/lit16, rsub-int,
+ * and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ movzbl rINST_HI,%eax # eax<- 000000BA
+ sarl $4,%eax # eax<- B
+ GET_VREG(%eax,%eax) # eax<- vB
+ movswl 2(rPC),%ecx # ecx<- ssssCCCC
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ xor %ecx,%eax # for example: addl %ecx, %eax
+ SET_VREG(%eax,rINST_FULL)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_ADD_INT_LIT8: /* 0xd8 */
+/* File: x86/OP_ADD_INT_LIT8.S */
+/* File: x86/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = eax op ecx".
+ * This could be an x86 instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * For: add-int/lit8, rsub-int/lit8
+ * and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movsbl 3(rPC),%ecx # ecx<- ssssssCC
+ movzx rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG (%eax,%eax) # eax<- rBB
+ addl %ecx,%eax # ex: addl %ecx,%eax
+ SET_VREG (%eax,rINST_FULL)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_RSUB_INT_LIT8: /* 0xd9 */
+/* File: x86/OP_RSUB_INT_LIT8.S */
+/* File: x86/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = eax op ecx".
+ * This could be an x86 instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * For: add-int/lit8, rsub-int/lit8
+ * and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movsbl 3(rPC),%ecx # ecx<- ssssssCC
+ movzx rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG (%eax,%eax) # eax<- rBB
+ subl %eax,%ecx # ex: addl %ecx,%eax
+ SET_VREG (%ecx,rINST_FULL)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_MUL_INT_LIT8: /* 0xda */
+/* File: x86/OP_MUL_INT_LIT8.S */
+ /* mul/lit8 vAA, vBB, #+CC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movsbl 3(rPC),%ecx # ecx<- ssssssCC
+ SPILL(rPC)
+ movzx rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG (%eax,%eax) # eax<- rBB
+ imull %ecx,%eax # trashes rPC
+ UNSPILL(rPC)
+ SET_VREG (%eax,rINST_FULL)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_DIV_INT_LIT8: /* 0xdb */
+/* File: x86/OP_DIV_INT_LIT8.S */
+/* File: x86/bindivLit8.S */
+ /*
+ * 32-bit div/rem "lit8" binary operation. Handles special case of
+ * op0=minint & op1=-1
+ */
+ /* div/rem/lit8 vAA, vBB, #+CC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movsbl 3(rPC),%ecx # ecx<- ssssssCC
+ GET_VREG (%eax,%eax) # eax<- rBB
+ movzx rINST_HI,rINST_FULL # rINST_FULL<- AA
+ SPILL(rPC)
+ cmpl $0,%ecx
+ je common_errDivideByZero
+ cmpl $0x80000000,%eax
+ jne .LOP_DIV_INT_LIT8_continue_div
+ cmpl $-1,%ecx
+ jne .LOP_DIV_INT_LIT8_continue_div
+ movl $0x80000000,%eax
+ jmp .LOP_DIV_INT_LIT8_finish_div
+
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_REM_INT_LIT8: /* 0xdc */
+/* File: x86/OP_REM_INT_LIT8.S */
+/* File: x86/bindivLit8.S */
+ /*
+ * 32-bit div/rem "lit8" binary operation. Handles special case of
+ * op0=minint & op1=-1
+ */
+ /* div/rem/lit8 vAA, vBB, #+CC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movsbl 3(rPC),%ecx # ecx<- ssssssCC
+ GET_VREG (%eax,%eax) # eax<- rBB
+ movzx rINST_HI,rINST_FULL # rINST_FULL<- AA
+ SPILL(rPC)
+ cmpl $0,%ecx
+ je common_errDivideByZero
+ cmpl $0x80000000,%eax
+ jne .LOP_REM_INT_LIT8_continue_div
+ cmpl $-1,%ecx
+ jne .LOP_REM_INT_LIT8_continue_div
+ movl $0,%edx
+ jmp .LOP_REM_INT_LIT8_finish_div
+
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_AND_INT_LIT8: /* 0xdd */
+/* File: x86/OP_AND_INT_LIT8.S */
+/* File: x86/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = eax op ecx".
+ * This could be an x86 instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * For: add-int/lit8, rsub-int/lit8
+ * and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movsbl 3(rPC),%ecx # ecx<- ssssssCC
+ movzx rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG (%eax,%eax) # eax<- rBB
+ andl %ecx,%eax # ex: addl %ecx,%eax
+ SET_VREG (%eax,rINST_FULL)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_OR_INT_LIT8: /* 0xde */
+/* File: x86/OP_OR_INT_LIT8.S */
+/* File: x86/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = eax op ecx".
+ * This could be an x86 instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * For: add-int/lit8, rsub-int/lit8
+ * and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movsbl 3(rPC),%ecx # ecx<- ssssssCC
+ movzx rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG (%eax,%eax) # eax<- rBB
+ orl %ecx,%eax # ex: addl %ecx,%eax
+ SET_VREG (%eax,rINST_FULL)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_XOR_INT_LIT8: /* 0xdf */
+/* File: x86/OP_XOR_INT_LIT8.S */
+/* File: x86/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = eax op ecx".
+ * This could be an x86 instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * For: add-int/lit8, rsub-int/lit8
+ * and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movsbl 3(rPC),%ecx # ecx<- ssssssCC
+ movzx rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG (%eax,%eax) # eax<- rBB
+ xor %ecx,%eax # ex: addl %ecx,%eax
+ SET_VREG (%eax,rINST_FULL)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHL_INT_LIT8: /* 0xe0 */
+/* File: x86/OP_SHL_INT_LIT8.S */
+/* File: x86/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = eax op ecx".
+ * This could be an x86 instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * For: add-int/lit8, rsub-int/lit8
+ * and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movsbl 3(rPC),%ecx # ecx<- ssssssCC
+ movzx rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG (%eax,%eax) # eax<- rBB
+ sall %cl,%eax # ex: addl %ecx,%eax
+ SET_VREG (%eax,rINST_FULL)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SHR_INT_LIT8: /* 0xe1 */
+/* File: x86/OP_SHR_INT_LIT8.S */
+/* File: x86/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = eax op ecx".
+ * This could be an x86 instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * For: add-int/lit8, rsub-int/lit8
+ * and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movsbl 3(rPC),%ecx # ecx<- ssssssCC
+ movzx rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG (%eax,%eax) # eax<- rBB
+ sarl %cl,%eax # ex: addl %ecx,%eax
+ SET_VREG (%eax,rINST_FULL)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_USHR_INT_LIT8: /* 0xe2 */
+/* File: x86/OP_USHR_INT_LIT8.S */
+/* File: x86/binopLit8.S */
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = eax op ecx".
+ * This could be an x86 instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * For: add-int/lit8, rsub-int/lit8
+ * and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movsbl 3(rPC),%ecx # ecx<- ssssssCC
+ movzx rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG (%eax,%eax) # eax<- rBB
+ shrl %cl,%eax # ex: addl %ecx,%eax
+ SET_VREG (%eax,rINST_FULL)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_VOLATILE: /* 0xe3 */
+/* File: x86/OP_IGET_VOLATILE.S */
+/* File: x86/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ GET_GLUE(%ecx)
+ SPILL(rIBASE) # need another reg
+ movzwl 2(rPC),rIBASE # rIBASE<- 0000CCCC
+ movl offGlue_methodClassDex(%ecx),%eax # eax<- DvmDex
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $4,%ecx # ecx<- B
+ movl offDvmDex_pResFields(%eax),%eax # eax<- pDvmDex->pResFields
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG(%ecx,%ecx) # ecx<- fp[B], the object ptr
+ movl (%eax,rIBASE,4),%eax # resolved entry
+ testl %eax,%eax # is resolved entry null?
+ jne .LOP_IGET_VOLATILE_finish # no, already resolved
+ movl rIBASE,OUT_ARG1(%esp) # needed by dvmResolveInstField
+ GET_GLUE(rIBASE)
+ jmp .LOP_IGET_VOLATILE_resolve
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_VOLATILE: /* 0xe4 */
+/* File: x86/OP_IPUT_VOLATILE.S */
+/* File: x86/OP_IPUT.S */
+
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ GET_GLUE(%ecx)
+ SPILL(rIBASE) # need another reg
+ movzwl 2(rPC),rIBASE # rIBASE<- 0000CCCC
+ movl offGlue_methodClassDex(%ecx),%eax # eax<- DvmDex
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $4,%ecx # ecx<- B
+ movl offDvmDex_pResFields(%eax),%eax # eax<- pDvmDex->pResFields
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG(%ecx,%ecx) # ecx<- fp[B], the object ptr
+ movl (%eax,rIBASE,4),%eax # resolved entry
+ testl %eax,%eax # is resolved entry null?
+ jne .LOP_IPUT_VOLATILE_finish # no, already resolved
+ movl rIBASE,OUT_ARG1(%esp)
+ GET_GLUE(rIBASE)
+ jmp .LOP_IPUT_VOLATILE_resolve
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_VOLATILE: /* 0xe5 */
+/* File: x86/OP_SGET_VOLATILE.S */
+/* File: x86/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_methodClassDex(%ecx),%ecx # ecx<- DvmDex
+ movl offDvmDex_pResFields(%ecx),%ecx # ecx<- dvmDex->pResFields
+ movl (%ecx,%eax,4),%eax # eax<- resolved StaticField ptr
+ testl %eax,%eax # resolved entry null?
+ je .LOP_SGET_VOLATILE_resolve # if not, make it so
+.LOP_SGET_VOLATILE_finish: # field ptr in eax
+ movl offStaticField_value(%eax),%eax
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ SET_VREG(%eax,%ecx)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_VOLATILE: /* 0xe6 */
+/* File: x86/OP_SPUT_VOLATILE.S */
+/* File: x86/OP_SPUT.S */
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_methodClassDex(%ecx),%ecx # ecx<- DvmDex
+ movl offDvmDex_pResFields(%ecx),%ecx # ecx<- dvmDex->pResFields
+ movl (%ecx,%eax,4),%eax # eax<- resolved StaticField ptr
+ testl %eax,%eax # resolved entry null?
+ je .LOP_SPUT_VOLATILE_resolve # if not, make it so
+.LOP_SPUT_VOLATILE_finish: # field ptr in eax
+ movzbl rINST_HI,%ecx # ecx<- AA
+ GET_VREG(%ecx,%ecx)
+ FETCH_INST_WORD(2)
+ movl %ecx,offStaticField_value(%eax)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_OBJECT_VOLATILE: /* 0xe7 */
+/* File: x86/OP_IGET_OBJECT_VOLATILE.S */
+/* File: x86/OP_IGET.S */
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ GET_GLUE(%ecx)
+ SPILL(rIBASE) # need another reg
+ movzwl 2(rPC),rIBASE # rIBASE<- 0000CCCC
+ movl offGlue_methodClassDex(%ecx),%eax # eax<- DvmDex
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $4,%ecx # ecx<- B
+ movl offDvmDex_pResFields(%eax),%eax # eax<- pDvmDex->pResFields
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG(%ecx,%ecx) # ecx<- fp[B], the object ptr
+ movl (%eax,rIBASE,4),%eax # resolved entry
+ testl %eax,%eax # is resolved entry null?
+ jne .LOP_IGET_OBJECT_VOLATILE_finish # no, already resolved
+ movl rIBASE,OUT_ARG1(%esp) # needed by dvmResolveInstField
+ GET_GLUE(rIBASE)
+ jmp .LOP_IGET_OBJECT_VOLATILE_resolve
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_WIDE_VOLATILE: /* 0xe8 */
+ /* (stub) */
+ GET_GLUE(%ecx)
+ SAVE_PC_TO_GLUE(%ecx) # only need to export these two
+ SAVE_FP_TO_GLUE(%ecx) # only need to export these two
+ movl %ecx,OUT_ARG0(%esp) # glue is first arg to function
+ call dvmMterp_OP_IGET_WIDE_VOLATILE # do the real work
+ GET_GLUE(%ecx)
+ LOAD_PC_FROM_GLUE(%ecx) # retrieve updated values
+ LOAD_FP_FROM_GLUE(%ecx) # retrieve updated values
+ FETCH_INST()
+ GOTO_NEXT
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_WIDE_VOLATILE: /* 0xe9 */
+ /* (stub) */
+ GET_GLUE(%ecx)
+ SAVE_PC_TO_GLUE(%ecx) # only need to export these two
+ SAVE_FP_TO_GLUE(%ecx) # only need to export these two
+ movl %ecx,OUT_ARG0(%esp) # glue is first arg to function
+ call dvmMterp_OP_IPUT_WIDE_VOLATILE # do the real work
+ GET_GLUE(%ecx)
+ LOAD_PC_FROM_GLUE(%ecx) # retrieve updated values
+ LOAD_FP_FROM_GLUE(%ecx) # retrieve updated values
+ FETCH_INST()
+ GOTO_NEXT
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_WIDE_VOLATILE: /* 0xea */
+ /* (stub) */
+ GET_GLUE(%ecx)
+ SAVE_PC_TO_GLUE(%ecx) # only need to export these two
+ SAVE_FP_TO_GLUE(%ecx) # only need to export these two
+ movl %ecx,OUT_ARG0(%esp) # glue is first arg to function
+ call dvmMterp_OP_SGET_WIDE_VOLATILE # do the real work
+ GET_GLUE(%ecx)
+ LOAD_PC_FROM_GLUE(%ecx) # retrieve updated values
+ LOAD_FP_FROM_GLUE(%ecx) # retrieve updated values
+ FETCH_INST()
+ GOTO_NEXT
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_WIDE_VOLATILE: /* 0xeb */
+ /* (stub) */
+ GET_GLUE(%ecx)
+ SAVE_PC_TO_GLUE(%ecx) # only need to export these two
+ SAVE_FP_TO_GLUE(%ecx) # only need to export these two
+ movl %ecx,OUT_ARG0(%esp) # glue is first arg to function
+ call dvmMterp_OP_SPUT_WIDE_VOLATILE # do the real work
+ GET_GLUE(%ecx)
+ LOAD_PC_FROM_GLUE(%ecx) # retrieve updated values
+ LOAD_FP_FROM_GLUE(%ecx) # retrieve updated values
+ FETCH_INST()
+ GOTO_NEXT
+/* ------------------------------ */
+ .balign 64
+.L_OP_BREAKPOINT: /* 0xec */
+/* File: x86/OP_BREAKPOINT.S */
+/* File: x86/unused.S */
+ jmp common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_THROW_VERIFICATION_ERROR: /* 0xed */
+/* File: x86/OP_THROW_VERIFICATION_ERROR.S */
+ /*
+ * Handle a throw-verification-error instruction. This throws an
+ * exception for an error discovered during verification. The
+ * exception is indicated by AA, with some detail provided by BBBB.
+ */
+ /* op AA, ref@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- BBBB
+ movl offGlue_method(%ecx),%ecx # ecx<- glue->method
+ EXPORT_PC()
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ movl %eax,OUT_ARG2(%esp) # arg2<- BBBB
+ movl rINST_FULL,OUT_ARG1(%esp) # arg1<- AA
+ movl %ecx,OUT_ARG0(%esp) # arg0<- method
+ SPILL(rPC)
+ call dvmThrowVerificationError # call(method, kind, ref)
+ UNSPILL(rPC)
+ jmp common_exceptionThrown # handle exception
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_EXECUTE_INLINE: /* 0xee */
+/* File: x86/OP_EXECUTE_INLINE.S */
+ /*
+ * Execute a "native inline" instruction.
+ *
+ * We will be calling through a function table:
+ *
+ * (*gDvmInlineOpsTable[opIndex].func)(arg0, arg1, arg2, arg3, pResult)
+ *
+ */
+ /* [opt] execute-inline vAA, {vC, vD, vE, vF}, inline@BBBB */
+ GET_GLUE(%ecx)
+ EXPORT_PC()
+ movzwl 2(rPC),%eax # eax<- BBBB
+ leal offGlue_retval(%ecx),%ecx # ecx<- & glue->retval
+ movl %ecx,OUT_ARG4(%esp)
+ sarl $12,rINST_FULL # rINST_FULL<- arg count (0-4)
+ SPILL(rPC)
+ call .LOP_EXECUTE_INLINE_continue # make call; will return after
+ UNSPILL(rPC)
+ testl %eax,%eax # successful?
+ FETCH_INST_WORD(3)
+ je common_exceptionThrown # no, handle exception
+ ADVANCE_PC(3)
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_EXECUTE_INLINE_RANGE: /* 0xef */
+ /* (stub) */
+ GET_GLUE(%ecx)
+ SAVE_PC_TO_GLUE(%ecx) # only need to export these two
+ SAVE_FP_TO_GLUE(%ecx) # only need to export these two
+ movl %ecx,OUT_ARG0(%esp) # glue is first arg to function
+ call dvmMterp_OP_EXECUTE_INLINE_RANGE # do the real work
+ GET_GLUE(%ecx)
+ LOAD_PC_FROM_GLUE(%ecx) # retrieve updated values
+ LOAD_FP_FROM_GLUE(%ecx) # retrieve updated values
+ FETCH_INST()
+ GOTO_NEXT
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_DIRECT_EMPTY: /* 0xf0 */
+/* File: x86/OP_INVOKE_DIRECT_EMPTY.S */
+ /*
+ * invoke-direct-empty is a no-op in a "standard" interpreter.
+ */
+ FETCH_INST_WORD(3)
+ ADVANCE_PC(3)
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_F1: /* 0xf1 */
+/* File: x86/OP_UNUSED_F1.S */
+/* File: x86/unused.S */
+ jmp common_abort
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_QUICK: /* 0xf2 */
+/* File: x86/OP_IGET_QUICK.S */
+ /* For: iget-quick, iget-object-quick */
+ /* op vA, vB, offset@CCCC */
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $4,%ecx # ecx<- B
+ GET_VREG(%ecx,%ecx) # vB (object we're operating on)
+ movzwl 2(rPC),%eax # eax<- field byte offset
+ cmpl $0,%ecx # is object null?
+ je common_errNullObject
+ movl (%ecx,%eax,1),%eax
+ movzbl rINST_HI,%ecx
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ andb $0xf,%cl # rINST_FULL<- A
+ SET_VREG (%eax,%ecx) # fp[A]<- result
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_WIDE_QUICK: /* 0xf3 */
+/* File: x86/OP_IGET_WIDE_QUICK.S */
+ /* For: iget-wide-quick */
+ /* op vA, vB, offset@CCCC */
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $4,%ecx # ecx<- B
+ GET_VREG(%ecx,%ecx) # vB (object we're operating on)
+ movzwl 2(rPC),%eax # eax<- field byte offset
+ cmpl $0,%ecx # is object null?
+ je common_errNullObject
+ leal (%ecx,%eax,1),%eax # eax<- address of 64-bit source
+ movl (%eax),%ecx # ecx<- lsw
+ movl 4(%eax),%eax # eax<- msw
+ movzbl rINST_HI,rINST_FULL
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ SET_VREG_WORD(%ecx,rINST_FULL,0) # v[A+0]<- lsw
+ SET_VREG_WORD(%eax,rINST_FULL,1) # v[A+1]<- msw
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IGET_OBJECT_QUICK: /* 0xf4 */
+/* File: x86/OP_IGET_OBJECT_QUICK.S */
+/* File: x86/OP_IGET_QUICK.S */
+ /* For: iget-quick, iget-object-quick */
+ /* op vA, vB, offset@CCCC */
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $4,%ecx # ecx<- B
+ GET_VREG(%ecx,%ecx) # vB (object we're operating on)
+ movzwl 2(rPC),%eax # eax<- field byte offset
+ cmpl $0,%ecx # is object null?
+ je common_errNullObject
+ movl (%ecx,%eax,1),%eax
+ movzbl rINST_HI,%ecx
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ andb $0xf,%cl # rINST_FULL<- A
+ SET_VREG (%eax,%ecx) # fp[A]<- result
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_QUICK: /* 0xf5 */
+/* File: x86/OP_IPUT_QUICK.S */
+ /* For: iput-quick */
+ /* op vA, vB, offset@CCCC */
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $4,%ecx # ecx<- B
+ GET_VREG(%ecx,%ecx) # vB (object we're operating on)
+ movzbl rINST_HI,rINST_FULL
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG(rINST_FULL,rINST_FULL) # rINST_FULL<- v[A]
+ movzwl 2(rPC),%eax # eax<- field byte offset
+ testl %ecx,%ecx # is object null?
+ je common_errNullObject
+ movl rINST_FULL,(%ecx,%eax,1)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_WIDE_QUICK: /* 0xf6 */
+/* File: x86/OP_IPUT_WIDE_QUICK.S */
+ /* For: iput-wide-quick */
+ /* op vA, vB, offset@CCCC */
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $4,%ecx # ecx<- B
+ GET_VREG(%ecx,%ecx) # vB (object we're operating on)
+ movzwl 2(rPC),%eax # eax<- field byte offset
+ testl %ecx,%ecx # is object null?
+ je common_errNullObject
+ leal (%ecx,%eax,1),%ecx # ecx<- Address of 64-bit target
+ movzbl rINST_HI,rINST_FULL
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG_WORD(%eax,rINST_FULL,0) # eax<- lsw
+ GET_VREG_WORD(rINST_FULL,rINST_FULL,1) # rINST_FULL<- msw
+ movl %eax,(%ecx)
+ movl rINST_FULL,4(%ecx)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_OBJECT_QUICK: /* 0xf7 */
+/* File: x86/OP_IPUT_OBJECT_QUICK.S */
+ /* For: iput-object-quick */
+ /* op vA, vB, offset@CCCC */
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $4,%ecx # ecx<- B
+ GET_VREG(%ecx,%ecx) # vB (object we're operating on)
+ movzbl rINST_HI,rINST_FULL
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG(rINST_FULL,rINST_FULL) # rINST_FULL<- v[A]
+ movzwl 2(rPC),%eax # eax<- field byte offset
+ testl %ecx,%ecx # is object null?
+ je common_errNullObject
+ movl rINST_FULL,(%ecx,%eax,1)
+ GET_GLUE(%eax)
+ jmp .LOP_IPUT_OBJECT_QUICK_finish
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_VIRTUAL_QUICK: /* 0xf8 */
+/* File: x86/OP_INVOKE_VIRTUAL_QUICK.S */
+ /*
+ * Handle an optimized virtual method call.
+ *
+ * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ movzwl 4(rPC),%eax # eax<- FEDC or CCCC
+ movzwl 2(rPC),%ecx # ecx<- BBBB
+ .if (!0)
+ andl $0xf,%eax # eax<- C (or stays CCCC)
+ .endif
+ GET_VREG(%eax,%eax) # eax<- vC ("this" ptr)
+ testl %eax,%eax # null?
+ je common_errNullObject # yep, throw exception
+ movl offObject_clazz(%eax),%eax # eax<- thisPtr->clazz
+ movl offClassObject_vtable(%eax),%eax # eax<- thisPtr->clazz->vtable
+ EXPORT_PC() # might throw later - get ready
+ movl (%eax,%ecx,4),%eax # eax<- vtable[BBBB]
+ jmp common_invokeMethodNoRange
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_VIRTUAL_QUICK_RANGE: /* 0xf9 */
+/* File: x86/OP_INVOKE_VIRTUAL_QUICK_RANGE.S */
+/* File: x86/OP_INVOKE_VIRTUAL_QUICK.S */
+ /*
+ * Handle an optimized virtual method call.
+ *
+ * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ movzwl 4(rPC),%eax # eax<- FEDC or CCCC
+ movzwl 2(rPC),%ecx # ecx<- BBBB
+ .if (!1)
+ andl $0xf,%eax # eax<- C (or stays CCCC)
+ .endif
+ GET_VREG(%eax,%eax) # eax<- vC ("this" ptr)
+ testl %eax,%eax # null?
+ je common_errNullObject # yep, throw exception
+ movl offObject_clazz(%eax),%eax # eax<- thisPtr->clazz
+ movl offClassObject_vtable(%eax),%eax # eax<- thisPtr->clazz->vtable
+ EXPORT_PC() # might throw later - get ready
+ movl (%eax,%ecx,4),%eax # eax<- vtable[BBBB]
+ jmp common_invokeMethodRange
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_SUPER_QUICK: /* 0xfa */
+/* File: x86/OP_INVOKE_SUPER_QUICK.S */
+ /*
+ * Handle an optimized "super" method call.
+ *
+ * for: [opt] invoke-super-quick, invoke-super-quick/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 4(rPC),%eax # eax<- GFED or CCCC
+ movl offGlue_method(%ecx),%ecx # ecx<- current method
+ .if (!0)
+ andl $0xf,%eax # eax<- D (or stays CCCC)
+ .endif
+ movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+ GET_VREG(%eax,%eax) # eax<- "this"
+ movl offClassObject_super(%ecx),%ecx # ecx<- method->clazz->super
+ testl %eax,%eax # null "this"?
+ je common_errNullObject # "this" is null, throw exception
+ movzwl 2(rPC),%eax # eax<- BBBB
+ movl offClassObject_vtable(%ecx),%ecx # ecx<- vtable
+ EXPORT_PC()
+ movl (%ecx,%eax,4),%eax # eax<- super->vtable[BBBB]
+ jmp common_invokeMethodNoRange
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_INVOKE_SUPER_QUICK_RANGE: /* 0xfb */
+/* File: x86/OP_INVOKE_SUPER_QUICK_RANGE.S */
+/* File: x86/OP_INVOKE_SUPER_QUICK.S */
+ /*
+ * Handle an optimized "super" method call.
+ *
+ * for: [opt] invoke-super-quick, invoke-super-quick/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 4(rPC),%eax # eax<- GFED or CCCC
+ movl offGlue_method(%ecx),%ecx # ecx<- current method
+ .if (!1)
+ andl $0xf,%eax # eax<- D (or stays CCCC)
+ .endif
+ movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+ GET_VREG(%eax,%eax) # eax<- "this"
+ movl offClassObject_super(%ecx),%ecx # ecx<- method->clazz->super
+ testl %eax,%eax # null "this"?
+ je common_errNullObject # "this" is null, throw exception
+ movzwl 2(rPC),%eax # eax<- BBBB
+ movl offClassObject_vtable(%ecx),%ecx # ecx<- vtable
+ EXPORT_PC()
+ movl (%ecx,%eax,4),%eax # eax<- super->vtable[BBBB]
+ jmp common_invokeMethodRange
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_IPUT_OBJECT_VOLATILE: /* 0xfc */
+/* File: x86/OP_IPUT_OBJECT_VOLATILE.S */
+/* File: x86/OP_IPUT_OBJECT.S */
+ /*
+ * Object field put.
+ *
+ * for: iput-object
+ */
+ /* op vA, vB, field@CCCC */
+ GET_GLUE(%ecx)
+ SPILL(rIBASE) # need another reg
+ movzwl 2(rPC),rIBASE # rIBASE<- 0000CCCC
+ movl offGlue_methodClassDex(%ecx),%eax # eax<- DvmDex
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $4,%ecx # ecx<- B
+ movl offDvmDex_pResFields(%eax),%eax # eax<- pDvmDex->pResFields
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG(%ecx,%ecx) # ecx<- fp[B], the object ptr
+ movl (%eax,rIBASE,4),%eax # resolved entry
+ testl %eax,%eax # is resolved entry null?
+ jne .LOP_IPUT_OBJECT_VOLATILE_finish # no, already resolved
+ movl rIBASE,OUT_ARG1(%esp)
+ GET_GLUE(rIBASE)
+ jmp .LOP_IPUT_OBJECT_VOLATILE_resolve
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SGET_OBJECT_VOLATILE: /* 0xfd */
+/* File: x86/OP_SGET_OBJECT_VOLATILE.S */
+/* File: x86/OP_SGET.S */
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_methodClassDex(%ecx),%ecx # ecx<- DvmDex
+ movl offDvmDex_pResFields(%ecx),%ecx # ecx<- dvmDex->pResFields
+ movl (%ecx,%eax,4),%eax # eax<- resolved StaticField ptr
+ testl %eax,%eax # resolved entry null?
+ je .LOP_SGET_OBJECT_VOLATILE_resolve # if not, make it so
+.LOP_SGET_OBJECT_VOLATILE_finish: # field ptr in eax
+ movl offStaticField_value(%eax),%eax
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ SET_VREG(%eax,%ecx)
+ GOTO_NEXT
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_SPUT_OBJECT_VOLATILE: /* 0xfe */
+/* File: x86/OP_SPUT_OBJECT_VOLATILE.S */
+/* File: x86/OP_SPUT_OBJECT.S */
+ /*
+ * SPUT object handler.
+ */
+ /* op vAA, field@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_methodClassDex(%ecx),%ecx # ecx<- DvmDex
+ movl offDvmDex_pResFields(%ecx),%ecx # ecx<- dvmDex->pResFields
+ movl (%ecx,%eax,4),%eax # eax<- resolved StaticField
+ testl %eax,%eax # resolved entry null?
+ je .LOP_SPUT_OBJECT_VOLATILE_resolve # if not, make it so
+.LOP_SPUT_OBJECT_VOLATILE_finish: # field ptr in eax
+ movzbl rINST_HI,%ecx # ecx<- AA
+ GET_VREG(%ecx,%ecx)
+ jmp .LOP_SPUT_OBJECT_VOLATILE_continue
+
+
+/* ------------------------------ */
+ .balign 64
+.L_OP_UNUSED_FF: /* 0xff */
+/* File: x86/OP_UNUSED_FF.S */
+/* File: x86/unused.S */
+ jmp common_abort
+
+
+
+ .balign 64
+ .size dvmAsmInstructionStart, .-dvmAsmInstructionStart
+ .global dvmAsmInstructionEnd
+dvmAsmInstructionEnd:
+
+/*
+ * ===========================================================================
+ * Sister implementations
+ * ===========================================================================
+ */
+ .global dvmAsmSisterStart
+ .type dvmAsmSisterStart, %function
+ .text
+ .balign 4
+dvmAsmSisterStart:
+
+/* continuation for OP_CONST_STRING */
+
+/* This is the less common path, so we'll redo some work
+ here rather than force spills on the common path */
+.LOP_CONST_STRING_resolve:
+ GET_GLUE(%eax)
+ movl %ecx,rINST_FULL # rINST_FULL<- AA
+ EXPORT_PC()
+ movl offGlue_method(%eax),%eax # eax<- glue->method
+ movzwl 2(rPC),%ecx # ecx<- BBBB
+ movl offMethod_clazz(%eax),%eax
+ SPILL(rPC)
+ movl %ecx,OUT_ARG1(%esp)
+ movl %eax,OUT_ARG0(%esp)
+ call dvmResolveString # go resolve
+ UNSPILL(rPC)
+ testl %eax,%eax # failed?
+ je common_exceptionThrown
+ SET_VREG(%eax,rINST_FULL)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* continuation for OP_CONST_STRING_JUMBO */
+
+/* This is the less common path, so we'll redo some work
+ here rather than force spills on the common path */
+.LOP_CONST_STRING_JUMBO_resolve:
+ GET_GLUE(%eax)
+ movl %ecx,rINST_FULL # rINST_FULL<- AA
+ EXPORT_PC()
+ movl offGlue_method(%eax),%eax # eax<- glue->method
+ movl 2(rPC),%ecx # ecx<- BBBBBBBB
+ movl offMethod_clazz(%eax),%eax
+ SPILL(rPC)
+ movl %ecx,OUT_ARG1(%esp)
+ movl %eax,OUT_ARG0(%esp)
+ call dvmResolveString # go resolve
+ UNSPILL(rPC)
+ testl %eax,%eax # failed?
+ je common_exceptionThrown
+ SET_VREG(%eax,rINST_FULL)
+ FETCH_INST_WORD(3)
+ ADVANCE_PC(3)
+ GOTO_NEXT
+
+/* continuation for OP_CONST_CLASS */
+
+/* This is the less common path, so we'll redo some work
+ here rather than force spills on the common path */
+.LOP_CONST_CLASS_resolve:
+ GET_GLUE(%eax)
+ movl %ecx,rINST_FULL # rINST_FULL<- AA
+ EXPORT_PC()
+ movl offGlue_method(%eax),%eax # eax<- glue->method
+ movl $1,OUT_ARG2(%esp) # true
+ movzwl 2(rPC),%ecx # ecx<- BBBB
+ movl offMethod_clazz(%eax),%eax
+ SPILL(rPC)
+ movl %ecx,OUT_ARG1(%esp)
+ movl %eax,OUT_ARG0(%esp)
+ call dvmResolveClass # go resolve
+ UNSPILL(rPC)
+ testl %eax,%eax # failed?
+ je common_exceptionThrown
+ SET_VREG(%eax,rINST_FULL)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* continuation for OP_MONITOR_ENTER */
+
+.LOP_MONITOR_ENTER_continue:
+ SPILL(rPC) # have to - caller save
+ movl %ecx,OUT_ARG0(%esp)
+ movl %eax,OUT_ARG1(%esp)
+ call dvmLockObject # dvmLockObject(self,object)
+ UNSPILL(rPC)
+#ifdef WITH_DEADLOCK_PREDICTION
+ GET_GLUE(%ecx)
+ movl offGlueSelf(%ecx),%ecx # ecx<- glue->self
+ movl offThread_exception(%ecx),%eax
+ testl %eax,%eax
+ jne common_exceptionThrown
+#endif
+ ADVANCE_PC(1)
+ GOTO_NEXT
+
+/* continuation for OP_MONITOR_EXIT */
+
+.LOP_MONITOR_EXIT_continue:
+ call dvmUnlockObject # unlock(self,obj)
+ UNSPILL(rPC)
+ FETCH_INST_WORD(1)
+ testl %eax,%eax # success?
+ ADVANCE_PC(1)
+ je common_exceptionThrown # no, exception pending
+ GOTO_NEXT
+.LOP_MONITOR_EXIT_errNullObject:
+ ADVANCE_PC(1) # advance before throw
+ jmp common_errNullObject
+
+/* continuation for OP_CHECK_CAST */
+
+ /*
+ * Trivial test failed, need to perform full check. This is common.
+ * ecx holds obj->clazz
+ * eax holds class resolved from BBBB
+ * rINST_FULL holds object
+ */
+.LOP_CHECK_CAST_fullcheck:
+ movl %eax,OUT_ARG1(%esp)
+ movl %ecx,OUT_ARG0(%esp)
+ SPILL(rPC)
+ call dvmInstanceofNonTrivial # eax<- boolean result
+ UNSPILL(rPC)
+ testl %eax,%eax # failed?
+ jne .LOP_CHECK_CAST_okay # no, success
+
+ # A cast has failed. We need to throw a ClassCastException with the
+ # class of the object that failed to be cast.
+ EXPORT_PC()
+ movl offObject_clazz(rINST_FULL),%ecx # ecx<- obj->clazz
+ movl $.LstrClassCastException,%eax
+ movl offClassObject_descriptor(%ecx),%ecx
+ movl %eax,OUT_ARG0(%esp) # arg0<- message
+ movl %ecx,OUT_ARG1(%esp) # arg1<- obj->clazz->descriptor
+ SPILL(rPC)
+ call dvmThrowExceptionWithClassMessage
+ UNSPILL(rPC)
+ jmp common_exceptionThrown
+
+ /*
+ * Resolution required. This is the least-likely path, and we're
+ * going to have to recreate some data.
+ *
+ * rINST_FULL holds object
+ */
+.LOP_CHECK_CAST_resolve:
+ GET_GLUE(%ecx)
+ EXPORT_PC()
+ movzwl 2(rPC),%eax # eax<- BBBB
+ movl offGlue_method(%ecx),%ecx # ecx<- glue->method
+ movl %eax,OUT_ARG1(%esp) # arg1<- BBBB
+ movl offMethod_clazz(%ecx),%ecx # ecx<- metho->clazz
+ movl $0,OUT_ARG2(%esp) # arg2<- false
+ movl %ecx,OUT_ARG0(%esp) # arg0<- method->clazz
+ SPILL(rPC)
+ call dvmResolveClass # eax<- resolved ClassObject ptr
+ UNSPILL(rPC)
+ testl %eax,%eax # got null?
+ je common_exceptionThrown # yes, handle exception
+ movl offObject_clazz(rINST_FULL),%ecx # ecx<- obj->clazz
+ jmp .LOP_CHECK_CAST_resolved # pick up where we left off
+
+/* continuation for OP_INSTANCE_OF */
+
+ /*
+ * Trivial test failed, need to perform full check. This is common.
+ * eax holds obj->clazz
+ * ecx holds class resolved from BBBB
+ * rINST_HI has BA
+ * rPC already spilled
+ */
+.LOP_INSTANCE_OF_fullcheck:
+ movl %eax,OUT_ARG0(%esp)
+ movl %ecx,OUT_ARG1(%esp)
+ call dvmInstanceofNonTrivial # eax<- boolean result
+ # fall through to OP_INSTANCE_OF_store
+
+ /*
+ * eax holds boolean result
+ * rINST_HI holds BA
+ */
+.LOP_INSTANCE_OF_store:
+ UNSPILL(rPC)
+ movzbl rINST_HI,%ecx # ecx<- BA
+ FETCH_INST_WORD(2)
+ andb $0xf,%cl # ecl<- A
+ ADVANCE_PC(2)
+ SET_VREG(%eax,%ecx) # vA<- eax
+ GOTO_NEXT
+
+ /*
+ * Trivial test succeeded, save and bail.
+ * r9 holds A
+ */
+.LOP_INSTANCE_OF_trivial:
+ UNSPILL(rPC)
+ movzbl rINST_HI,%ecx # ecx<- BA
+ FETCH_INST_WORD(2)
+ andb $0xf,%cl # ecl<- A
+ ADVANCE_PC(2)
+ movl $1,%eax
+ SET_VREG(%eax,%ecx) # vA<- true
+ GOTO_NEXT
+
+ /*
+ * Resolution required. This is the least-likely path.
+ *
+ * rPC holds BBBB
+ * rINST_HI holds BA
+ */
+.LOP_INSTANCE_OF_resolve:
+ movl rPC,OUT_ARG1(%esp) # arg1<- BBBB
+ GET_GLUE(%ecx)
+ UNSPILL(rPC)
+ movl offGlue_method(%ecx),%ecx
+ movl $1,OUT_ARG2(%esp) # arg2<- true
+ movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+ EXPORT_PC()
+ movl %ecx,OUT_ARG0(%esp) # arg0<- method->clazz
+ call dvmResolveClass # eax<- resolved ClassObject ptr
+ UNSPILL(rPC)
+ testl %eax,%eax # success?
+ je common_exceptionThrown # no, handle exception
+/* Now, we need to sync up with fast path. We need eax to
+ * hold the obj->clazz, and ecx to hold the resolved class
+ */
+ movl %eax,%ecx # ecx<- resolved class
+ movzbl rINST_HI,%eax # eax<- BA
+ sarl $4,%eax # eax<- B
+ GET_VREG(%eax,%eax) # eax<- vB (obj)
+ movl offObject_clazz(%eax),%eax # eax<- obj->clazz
+ jmp .LOP_INSTANCE_OF_resolved
+
+/* continuation for OP_NEW_INSTANCE */
+
+.LOP_NEW_INSTANCE_initialized: # on entry, ecx<- class
+ /* TODO: remove test for interface/abstract, now done in verifier */
+ testl $(ACC_INTERFACE|ACC_ABSTRACT),offClassObject_accessFlags(%ecx)
+ movl $ALLOC_DONT_TRACK,OUT_ARG1(%esp)
+ jne .LOP_NEW_INSTANCE_abstract
+.LOP_NEW_INSTANCE_finish: # ecx=class
+ movl %ecx,OUT_ARG0(%esp)
+ call dvmAllocObject # eax<- new object
+ UNSPILL(rPC)
+ movl rINST_FULL,%ecx
+ FETCH_INST_WORD(2)
+ testl %eax,%eax # success?
+ je common_exceptionThrown # no, bail out
+ SET_VREG(%eax,%ecx)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+ /*
+ * Class initialization required.
+ *
+ * ecx holds class object
+ */
+.LOP_NEW_INSTANCE_needinit:
+ SPILL_TMP(%ecx) # save object
+ movl %ecx,OUT_ARG0(%esp)
+ call dvmInitClass # initialize class
+ UNSPILL_TMP(%ecx) # restore object
+ testl %eax,%eax # success?
+ jne .LOP_NEW_INSTANCE_initialized # success, continue
+ UNSPILL(rPC) # failed, restore PC
+ jmp common_exceptionThrown # go deal with init exception
+
+ /*
+ * Resolution required. This is the least-likely path.
+ *
+ */
+.LOP_NEW_INSTANCE_resolve:
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax
+ movl offGlue_method(%ecx),%ecx # ecx<- glue->method
+ movl %eax,OUT_ARG1(%esp)
+ movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+ movl $0,OUT_ARG2(%esp)
+ movl %ecx,OUT_ARG0(%esp)
+ call dvmResolveClass # call(clazz,off,flags)
+ movl %eax,%ecx # ecx<- resolved ClassObject ptr
+ testl %ecx,%ecx # success?
+ jne .LOP_NEW_INSTANCE_resolved # good to go
+ UNSPILL(rPC)
+ jmp common_exceptionThrown # no, handle exception
+
+ /*
+ * TODO: remove this
+ * We can't instantiate an abstract class or interface, so throw an
+ * InstantiationError with the class descriptor as the message.
+ *
+ * ecx holds class object
+ */
+.LOP_NEW_INSTANCE_abstract:
+ movl offClassObject_descriptor(%ecx),%eax
+ movl $.LstrInstantiationError,OUT_ARG0(%esp)
+ movl %eax,OUT_ARG1(%esp)
+ call dvmThrowExceptionWithClassMessage
+ UNSPILL(rPC)
+ jmp common_exceptionThrown
+
+/* continuation for OP_NEW_ARRAY */
+
+ /*
+ * Resolve class. (This is an uncommon case.)
+ * ecx holds class (null here)
+ * eax holds array length (vB)
+ */
+.LOP_NEW_ARRAY_resolve:
+ GET_GLUE(%ecx)
+ SPILL_TMP(%eax) # save array length
+ movl offGlue_method(%ecx),%ecx # ecx<- glue->method
+ movzwl 2(rPC),%eax # eax<- CCCC
+ movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+ movl %eax,OUT_ARG1(%esp)
+ movl $0,OUT_ARG2(%esp)
+ movl %ecx,OUT_ARG0(%esp)
+ SPILL(rPC)
+ call dvmResolveClass # eax<- call(clazz,ref,flag)
+ UNSPILL(rPC)
+ movl %eax,%ecx
+ UNSPILL_TMP(%eax)
+ testl %ecx,%ecx # successful resolution?
+ je common_exceptionThrown # no, bail.
+# fall through to OP_NEW_ARRAY_finish
+
+ /*
+ * Finish allocation
+ *
+ * ecx holds class
+ * eax holds array length (vB)
+ */
+.LOP_NEW_ARRAY_finish:
+ movl %ecx,OUT_ARG0(%esp)
+ movl %eax,OUT_ARG1(%esp)
+ movl $ALLOC_DONT_TRACK,OUT_ARG2(%esp)
+ SPILL(rPC)
+ call dvmAllocArrayByClass # eax<- call(clazz,length,flags)
+ UNSPILL(rPC)
+ testl %eax,%eax # failed?
+ je common_exceptionThrown # yup - go handle
+ movl rINST_FULL,%ecx
+ FETCH_INST_WORD(2)
+ SET_VREG(%eax,%ecx)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* continuation for OP_FILLED_NEW_ARRAY */
+
+.LOP_FILLED_NEW_ARRAY_more:
+ movl offMethod_clazz(%eax),%eax # eax<- method->clazz
+ movl %eax,OUT_ARG0(%esp) # arg0<- clazz
+ call dvmResolveClass # eax<- call(clazz,ref,flag)
+ UNSPILL(rPC)
+ testl %eax,%eax # null?
+ je common_exceptionThrown # yes, handle it
+
+ # note: fall through to .LOP_FILLED_NEW_ARRAY_continue
+
+ /*
+ * On entry:
+ * eax holds array class [r0]
+ * rINST_FULL holds AA or BB [r10]
+ * ecx is scratch
+ * rPC is valid, but has been spilled
+ */
+.LOP_FILLED_NEW_ARRAY_continue:
+ movl offClassObject_descriptor(%eax),%ecx # ecx<- arrayClass->descriptor
+ movl $ALLOC_DONT_TRACK,OUT_ARG2(%esp) # arg2<- flags
+ movzbl 1(%ecx),%ecx # ecx<- descriptor[1]
+ movl %eax,OUT_ARG0(%esp) # arg0<- arrayClass
+ GET_GLUE(%eax)
+ cmpb $'I',%cl # supported?
+ je 1f
+ cmpb $'L',%cl
+ je 1f
+ cmpb $'[',%cl
+ jne .LOP_FILLED_NEW_ARRAY_notimpl # no, not handled yet
+1:
+ movl %ecx,offGlue_retval+4(%eax) # save type
+ .if (!0)
+ SPILL_TMP(rINST_FULL) # save copy, need "B" later
+ sarl $4,rINST_FULL
+ .endif
+ movl rINST_FULL,OUT_ARG1(%esp) # arg1<- A or AA (length)
+ call dvmAllocArrayByClass # eax<- call(arrayClass, length, flags)
+ UNSPILL(rPC)
+ GET_GLUE(%ecx)
+ testl %eax,%eax # alloc successful?
+ je common_exceptionThrown # no, handle exception
+ movl %eax,offGlue_retval(%ecx) # retval.l<- new array
+ movzwl 4(rPC),%ecx # ecx<- FEDC or CCCC
+ leal offArrayObject_contents(%eax),%eax # eax<- newArray->contents
+
+/* at this point:
+ * eax is pointer to tgt
+ * rINST_FULL is length
+ * ecx is FEDC or CCCC
+ * TMP_SPILL is BA
+ * rPC is valid, but spilled
+ * We now need to copy values from registers into the array
+ */
+
+ .if 0
+ # set up src pointer
+ SPILL(rFP) # esi
+ SPILL(rIBASE) # edi
+ movl %eax,%edi # set up dst ptr
+ leal (rFP,%ecx,4),%esi # set up src ptr
+ movl rINST_FULL,%ecx # load count register
+ FETCH_INST_WORD(3)
+ rep
+ movsd
+ GET_GLUE(%ecx)
+ UNSPILL(rIBASE)
+ movl offGlue_retval+4(%ecx),%eax # eax<- type
+ UNSPILL(rFP)
+ .else
+ testl rINST_FULL,rINST_FULL
+ je 4f
+ UNSPILL_TMP(rPC)
+ andl $0x0f,rPC # rPC<- 0000000A
+ sall $16,rPC # rPC<- 000A0000
+ orl %ecx,rPC # rpc<- 000AFEDC
+3:
+ movl $0xf,%ecx
+ andl rPC,%ecx # ecx<- next reg to load
+ GET_VREG(%ecx,%ecx)
+ shrl $4,rPC
+ leal 4(%eax),%eax
+ movl %ecx,-4(%eax)
+ sub $1,rINST_FULL
+ jne 3b
+4:
+ GET_GLUE(%ecx)
+ UNSPILL(rPC)
+ movl offGlue_retval+4(%ecx),%eax # eax<- type
+ FETCH_INST_WORD(3)
+ .endif
+
+ cmpb $'I',%al # Int array?
+ je 5f # skip card mark if so
+ movl offGlue_retval(%ecx),%eax # eax<- object head
+ movl offGlue_cardTable(%ecx),%ecx # card table base
+ shrl $GC_CARD_SHIFT,%eax # convert to card num
+ movb %cl,(%ecx,%eax) # mark card based on object head
+5:
+ ADVANCE_PC(3)
+ GOTO_NEXT
+
+
+ /*
+ * Throw an exception indicating that we have not implemented this
+ * mode of filled-new-array.
+ */
+.LOP_FILLED_NEW_ARRAY_notimpl:
+ movl $.LstrInternalError,%eax
+ movl %eax,OUT_ARG0(%esp)
+ movl $.LstrFilledNewArrayNotImpl,%eax
+ movl %eax,OUT_ARG1(%esp)
+ call dvmThrowException
+ UNSPILL(rPC)
+ jmp common_exceptionThrown
+
+/* continuation for OP_FILLED_NEW_ARRAY_RANGE */
+
+.LOP_FILLED_NEW_ARRAY_RANGE_more:
+ movl offMethod_clazz(%eax),%eax # eax<- method->clazz
+ movl %eax,OUT_ARG0(%esp) # arg0<- clazz
+ call dvmResolveClass # eax<- call(clazz,ref,flag)
+ UNSPILL(rPC)
+ testl %eax,%eax # null?
+ je common_exceptionThrown # yes, handle it
+
+ # note: fall through to .LOP_FILLED_NEW_ARRAY_RANGE_continue
+
+ /*
+ * On entry:
+ * eax holds array class [r0]
+ * rINST_FULL holds AA or BB [r10]
+ * ecx is scratch
+ * rPC is valid, but has been spilled
+ */
+.LOP_FILLED_NEW_ARRAY_RANGE_continue:
+ movl offClassObject_descriptor(%eax),%ecx # ecx<- arrayClass->descriptor
+ movl $ALLOC_DONT_TRACK,OUT_ARG2(%esp) # arg2<- flags
+ movzbl 1(%ecx),%ecx # ecx<- descriptor[1]
+ movl %eax,OUT_ARG0(%esp) # arg0<- arrayClass
+ GET_GLUE(%eax)
+ cmpb $'I',%cl # supported?
+ je 1f
+ cmpb $'L',%cl
+ je 1f
+ cmpb $'[',%cl
+ jne .LOP_FILLED_NEW_ARRAY_RANGE_notimpl # no, not handled yet
+1:
+ movl %ecx,offGlue_retval+4(%eax) # save type
+ .if (!1)
+ SPILL_TMP(rINST_FULL) # save copy, need "B" later
+ sarl $4,rINST_FULL
+ .endif
+ movl rINST_FULL,OUT_ARG1(%esp) # arg1<- A or AA (length)
+ call dvmAllocArrayByClass # eax<- call(arrayClass, length, flags)
+ UNSPILL(rPC)
+ GET_GLUE(%ecx)
+ testl %eax,%eax # alloc successful?
+ je common_exceptionThrown # no, handle exception
+ movl %eax,offGlue_retval(%ecx) # retval.l<- new array
+ movzwl 4(rPC),%ecx # ecx<- FEDC or CCCC
+ leal offArrayObject_contents(%eax),%eax # eax<- newArray->contents
+
+/* at this point:
+ * eax is pointer to tgt
+ * rINST_FULL is length
+ * ecx is FEDC or CCCC
+ * TMP_SPILL is BA
+ * rPC is valid, but spilled
+ * We now need to copy values from registers into the array
+ */
+
+ .if 1
+ # set up src pointer
+ SPILL(rFP) # esi
+ SPILL(rIBASE) # edi
+ movl %eax,%edi # set up dst ptr
+ leal (rFP,%ecx,4),%esi # set up src ptr
+ movl rINST_FULL,%ecx # load count register
+ FETCH_INST_WORD(3)
+ rep
+ movsd
+ GET_GLUE(%ecx)
+ UNSPILL(rIBASE)
+ movl offGlue_retval+4(%ecx),%eax # eax<- type
+ UNSPILL(rFP)
+ .else
+ testl rINST_FULL,rINST_FULL
+ je 4f
+ UNSPILL_TMP(rPC)
+ andl $0x0f,rPC # rPC<- 0000000A
+ sall $16,rPC # rPC<- 000A0000
+ orl %ecx,rPC # rpc<- 000AFEDC
+3:
+ movl $0xf,%ecx
+ andl rPC,%ecx # ecx<- next reg to load
+ GET_VREG(%ecx,%ecx)
+ shrl $4,rPC
+ leal 4(%eax),%eax
+ movl %ecx,-4(%eax)
+ sub $1,rINST_FULL
+ jne 3b
+4:
+ GET_GLUE(%ecx)
+ UNSPILL(rPC)
+ movl offGlue_retval+4(%ecx),%eax # eax<- type
+ FETCH_INST_WORD(3)
+ .endif
+
+ cmpb $'I',%al # Int array?
+ je 5f # skip card mark if so
+ movl offGlue_retval(%ecx),%eax # eax<- object head
+ movl offGlue_cardTable(%ecx),%ecx # card table base
+ shrl $GC_CARD_SHIFT,%eax # convert to card num
+ movb %cl,(%ecx,%eax) # mark card based on object head
+5:
+ ADVANCE_PC(3)
+ GOTO_NEXT
+
+
+ /*
+ * Throw an exception indicating that we have not implemented this
+ * mode of filled-new-array.
+ */
+.LOP_FILLED_NEW_ARRAY_RANGE_notimpl:
+ movl $.LstrInternalError,%eax
+ movl %eax,OUT_ARG0(%esp)
+ movl $.LstrFilledNewArrayNotImpl,%eax
+ movl %eax,OUT_ARG1(%esp)
+ call dvmThrowException
+ UNSPILL(rPC)
+ jmp common_exceptionThrown
+
+/* continuation for OP_CMPL_FLOAT */
+
+.LOP_CMPL_FLOAT_isNaN:
+ movl $-1,%ecx
+ jmp .LOP_CMPL_FLOAT_finish
+
+/* continuation for OP_CMPG_FLOAT */
+
+.LOP_CMPG_FLOAT_isNaN:
+ movl $1,%ecx
+ jmp .LOP_CMPG_FLOAT_finish
+
+/* continuation for OP_CMPL_DOUBLE */
+
+.LOP_CMPL_DOUBLE_isNaN:
+ movl $-1,%ecx
+ jmp .LOP_CMPL_DOUBLE_finish
+
+/* continuation for OP_CMPG_DOUBLE */
+
+.LOP_CMPG_DOUBLE_isNaN:
+ movl $1,%ecx
+ jmp .LOP_CMPG_DOUBLE_finish
+
+/* continuation for OP_CMP_LONG */
+
+.LOP_CMP_LONG_bigger:
+ UNSPILL(rPC)
+ movl $1,%ecx
+ jmp .LOP_CMP_LONG_finish
+.LOP_CMP_LONG_smaller:
+ UNSPILL(rPC)
+ movl $-1,%ecx
+.LOP_CMP_LONG_finish:
+ SET_VREG(%ecx,rINST_FULL)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* continuation for OP_AGET_WIDE */
+
+.LOP_AGET_WIDE_finish:
+ leal offArrayObject_contents(%eax,%ecx,8),%eax
+ movl (%eax),%ecx
+ movl 4(%eax),%eax
+ SET_VREG_WORD(%ecx,rINST_FULL,0)
+ SET_VREG_WORD(%eax,rINST_FULL,1)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* continuation for OP_APUT_WIDE */
+
+.LOP_APUT_WIDE_finish:
+ leal offArrayObject_contents(%eax,%ecx,8),%eax
+ GET_VREG_WORD(%ecx,rINST_FULL,0)
+ GET_VREG_WORD(rINST_FULL,rINST_FULL,1)
+ movl rINST_FULL,4(%eax)
+ FETCH_INST_WORD(2)
+ movl %ecx,(%eax)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* continuation for OP_APUT_OBJECT */
+
+ /* On entry:
+ * eax<- array object
+ * ecx<- index
+ * rINST_FULL<- vAA
+ */
+.LOP_APUT_OBJECT_continue:
+ leal offArrayObject_contents(%eax,%ecx,4),%ecx
+ testl rINST_FULL,rINST_FULL # storing null reference?
+ je .LOP_APUT_OBJECT_skip_check
+ SPILL(rPC)
+ SPILL_TMP(%ecx)
+ movl %eax,LOCAL0_OFFSET(%ebp) # save copy of object head
+ movl offObject_clazz(%eax),%eax # eax<- arrayObj->clazz
+ movl offObject_clazz(rINST_FULL),%ecx # ecx<- obj->clazz
+ movl %eax,OUT_ARG1(%esp)
+ movl %ecx,OUT_ARG0(%esp)
+ call dvmCanPutArrayElement # test object type vs. array type
+ UNSPILL(rPC)
+ UNSPILL_TMP(%ecx)
+ testl %eax,%eax
+ GET_GLUE(%eax)
+ je common_errArrayStore
+ movl offGlue_cardTable(%eax),%eax # get card table base
+ movl rINST_FULL,(%ecx)
+ movl LOCAL0_OFFSET(%ebp),%ecx # recover object head
+ FETCH_INST_WORD(2)
+ shrl $GC_CARD_SHIFT,%ecx # object head to card number
+ movb %al,(%eax,%ecx) # mark card using object head
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+.LOP_APUT_OBJECT_skip_check:
+ movl rINST_FULL,(%ecx)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* continuation for OP_IGET */
+
+
+.LOP_IGET_resolve:
+ EXPORT_PC()
+ SPILL(rPC)
+ movl offGlue_method(rIBASE),rPC # rPC<- current method
+ UNSPILL(rIBASE)
+ movl offMethod_clazz(rPC),rPC # rPC<- method->clazz
+ SPILL_TMP(%ecx) # save object pointer across call
+ movl rPC,OUT_ARG0(%esp) # pass in method->clazz
+ call dvmResolveInstField # ... to dvmResolveInstField
+ UNSPILL_TMP(%ecx)
+ UNSPILL(rPC)
+ testl %eax,%eax # ... which returns InstrField ptr
+ jne .LOP_IGET_finish
+ jmp common_exceptionThrown
+
+.LOP_IGET_finish:
+ /*
+ * Currently:
+ * eax holds resolved field
+ * ecx holds object
+ * rIBASE is scratch, but needs to be unspilled
+ * rINST_FULL holds A
+ */
+ movl offInstField_byteOffset(%eax),%eax # eax<- byte offset of field
+ UNSPILL(rIBASE)
+ testl %ecx,%ecx # object null?
+ je common_errNullObject # object was null
+ movl (%ecx,%eax,1),%ecx # ecx<- obj.field (8/16/32 bits)
+ movl rINST_FULL,%eax # eax<- A
+ FETCH_INST_WORD(2)
+ SET_VREG(%ecx,%eax)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* continuation for OP_IGET_WIDE */
+
+
+.LOP_IGET_WIDE_resolve:
+ EXPORT_PC()
+ SPILL(rPC)
+ movl offGlue_method(rIBASE),rPC # rPC<- current method
+ UNSPILL(rIBASE)
+ movl offMethod_clazz(rPC),rPC # rPC<- method->clazz
+ SPILL_TMP(%ecx) # save object pointer across call
+ movl rPC,OUT_ARG0(%esp) # pass in method->clazz
+ call dvmResolveInstField # ... to dvmResolveInstField
+ UNSPILL_TMP(%ecx)
+ UNSPILL(rPC)
+ testl %eax,%eax # ... which returns InstrField ptr
+ jne .LOP_IGET_WIDE_finish
+ jmp common_exceptionThrown
+
+.LOP_IGET_WIDE_finish:
+ /*
+ * Currently:
+ * eax holds resolved field
+ * ecx holds object
+ * rIBASE is scratch, but needs to be unspilled
+ * rINST_FULL holds A
+ */
+ movl offInstField_byteOffset(%eax),%eax # eax<- byte offset of field
+ UNSPILL(rIBASE)
+ testl %ecx,%ecx # object null?
+ je common_errNullObject # object was null
+ leal (%ecx,%eax,1),%eax # eax<- address of field
+ movl (%eax),%ecx # ecx<- lsw
+ movl 4(%eax),%eax # eax<- msw
+ SET_VREG_WORD(%ecx,rINST_FULL,0)
+ SET_VREG_WORD(%eax,rINST_FULL,1)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* continuation for OP_IGET_OBJECT */
+
+
+.LOP_IGET_OBJECT_resolve:
+ EXPORT_PC()
+ SPILL(rPC)
+ movl offGlue_method(rIBASE),rPC # rPC<- current method
+ UNSPILL(rIBASE)
+ movl offMethod_clazz(rPC),rPC # rPC<- method->clazz
+ SPILL_TMP(%ecx) # save object pointer across call
+ movl rPC,OUT_ARG0(%esp) # pass in method->clazz
+ call dvmResolveInstField # ... to dvmResolveInstField
+ UNSPILL_TMP(%ecx)
+ UNSPILL(rPC)
+ testl %eax,%eax # ... which returns InstrField ptr
+ jne .LOP_IGET_OBJECT_finish
+ jmp common_exceptionThrown
+
+.LOP_IGET_OBJECT_finish:
+ /*
+ * Currently:
+ * eax holds resolved field
+ * ecx holds object
+ * rIBASE is scratch, but needs to be unspilled
+ * rINST_FULL holds A
+ */
+ movl offInstField_byteOffset(%eax),%eax # eax<- byte offset of field
+ UNSPILL(rIBASE)
+ testl %ecx,%ecx # object null?
+ je common_errNullObject # object was null
+ movl (%ecx,%eax,1),%ecx # ecx<- obj.field (8/16/32 bits)
+ movl rINST_FULL,%eax # eax<- A
+ FETCH_INST_WORD(2)
+ SET_VREG(%ecx,%eax)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* continuation for OP_IGET_BOOLEAN */
+
+
+.LOP_IGET_BOOLEAN_resolve:
+ EXPORT_PC()
+ SPILL(rPC)
+ movl offGlue_method(rIBASE),rPC # rPC<- current method
+ UNSPILL(rIBASE)
+ movl offMethod_clazz(rPC),rPC # rPC<- method->clazz
+ SPILL_TMP(%ecx) # save object pointer across call
+ movl rPC,OUT_ARG0(%esp) # pass in method->clazz
+ call dvmResolveInstField # ... to dvmResolveInstField
+ UNSPILL_TMP(%ecx)
+ UNSPILL(rPC)
+ testl %eax,%eax # ... which returns InstrField ptr
+ jne .LOP_IGET_BOOLEAN_finish
+ jmp common_exceptionThrown
+
+.LOP_IGET_BOOLEAN_finish:
+ /*
+ * Currently:
+ * eax holds resolved field
+ * ecx holds object
+ * rIBASE is scratch, but needs to be unspilled
+ * rINST_FULL holds A
+ */
+ movl offInstField_byteOffset(%eax),%eax # eax<- byte offset of field
+ UNSPILL(rIBASE)
+ testl %ecx,%ecx # object null?
+ je common_errNullObject # object was null
+ movzbl (%ecx,%eax,1),%ecx # ecx<- obj.field (8/16/32 bits)
+ movl rINST_FULL,%eax # eax<- A
+ FETCH_INST_WORD(2)
+ SET_VREG(%ecx,%eax)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* continuation for OP_IGET_BYTE */
+
+
+.LOP_IGET_BYTE_resolve:
+ EXPORT_PC()
+ SPILL(rPC)
+ movl offGlue_method(rIBASE),rPC # rPC<- current method
+ UNSPILL(rIBASE)
+ movl offMethod_clazz(rPC),rPC # rPC<- method->clazz
+ SPILL_TMP(%ecx) # save object pointer across call
+ movl rPC,OUT_ARG0(%esp) # pass in method->clazz
+ call dvmResolveInstField # ... to dvmResolveInstField
+ UNSPILL_TMP(%ecx)
+ UNSPILL(rPC)
+ testl %eax,%eax # ... which returns InstrField ptr
+ jne .LOP_IGET_BYTE_finish
+ jmp common_exceptionThrown
+
+.LOP_IGET_BYTE_finish:
+ /*
+ * Currently:
+ * eax holds resolved field
+ * ecx holds object
+ * rIBASE is scratch, but needs to be unspilled
+ * rINST_FULL holds A
+ */
+ movl offInstField_byteOffset(%eax),%eax # eax<- byte offset of field
+ UNSPILL(rIBASE)
+ testl %ecx,%ecx # object null?
+ je common_errNullObject # object was null
+ movsbl (%ecx,%eax,1),%ecx # ecx<- obj.field (8/16/32 bits)
+ movl rINST_FULL,%eax # eax<- A
+ FETCH_INST_WORD(2)
+ SET_VREG(%ecx,%eax)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* continuation for OP_IGET_CHAR */
+
+
+.LOP_IGET_CHAR_resolve:
+ EXPORT_PC()
+ SPILL(rPC)
+ movl offGlue_method(rIBASE),rPC # rPC<- current method
+ UNSPILL(rIBASE)
+ movl offMethod_clazz(rPC),rPC # rPC<- method->clazz
+ SPILL_TMP(%ecx) # save object pointer across call
+ movl rPC,OUT_ARG0(%esp) # pass in method->clazz
+ call dvmResolveInstField # ... to dvmResolveInstField
+ UNSPILL_TMP(%ecx)
+ UNSPILL(rPC)
+ testl %eax,%eax # ... which returns InstrField ptr
+ jne .LOP_IGET_CHAR_finish
+ jmp common_exceptionThrown
+
+.LOP_IGET_CHAR_finish:
+ /*
+ * Currently:
+ * eax holds resolved field
+ * ecx holds object
+ * rIBASE is scratch, but needs to be unspilled
+ * rINST_FULL holds A
+ */
+ movl offInstField_byteOffset(%eax),%eax # eax<- byte offset of field
+ UNSPILL(rIBASE)
+ testl %ecx,%ecx # object null?
+ je common_errNullObject # object was null
+ movzwl (%ecx,%eax,1),%ecx # ecx<- obj.field (8/16/32 bits)
+ movl rINST_FULL,%eax # eax<- A
+ FETCH_INST_WORD(2)
+ SET_VREG(%ecx,%eax)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* continuation for OP_IGET_SHORT */
+
+
+.LOP_IGET_SHORT_resolve:
+ EXPORT_PC()
+ SPILL(rPC)
+ movl offGlue_method(rIBASE),rPC # rPC<- current method
+ UNSPILL(rIBASE)
+ movl offMethod_clazz(rPC),rPC # rPC<- method->clazz
+ SPILL_TMP(%ecx) # save object pointer across call
+ movl rPC,OUT_ARG0(%esp) # pass in method->clazz
+ call dvmResolveInstField # ... to dvmResolveInstField
+ UNSPILL_TMP(%ecx)
+ UNSPILL(rPC)
+ testl %eax,%eax # ... which returns InstrField ptr
+ jne .LOP_IGET_SHORT_finish
+ jmp common_exceptionThrown
+
+.LOP_IGET_SHORT_finish:
+ /*
+ * Currently:
+ * eax holds resolved field
+ * ecx holds object
+ * rIBASE is scratch, but needs to be unspilled
+ * rINST_FULL holds A
+ */
+ movl offInstField_byteOffset(%eax),%eax # eax<- byte offset of field
+ UNSPILL(rIBASE)
+ testl %ecx,%ecx # object null?
+ je common_errNullObject # object was null
+ movswl (%ecx,%eax,1),%ecx # ecx<- obj.field (8/16/32 bits)
+ movl rINST_FULL,%eax # eax<- A
+ FETCH_INST_WORD(2)
+ SET_VREG(%ecx,%eax)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* continuation for OP_IPUT */
+
+
+.LOP_IPUT_resolve:
+ EXPORT_PC()
+ SPILL(rPC)
+ movl offGlue_method(rIBASE),rPC # rPC<- current method
+ UNSPILL(rIBASE)
+ movl offMethod_clazz(rPC),rPC # rPC<- method->clazz
+ SPILL_TMP(%ecx) # save object pointer across call
+ movl rPC,OUT_ARG0(%esp) # pass in method->clazz
+ call dvmResolveInstField # ... to dvmResolveInstField
+ UNSPILL_TMP(%ecx)
+ UNSPILL(rPC)
+ testl %eax,%eax # ... which returns InstrField ptr
+ jne .LOP_IPUT_finish
+ jmp common_exceptionThrown
+
+.LOP_IPUT_finish:
+ /*
+ * Currently:
+ * eax holds resolved field
+ * ecx holds object
+ * rIBASE is scratch, but needs to be unspilled
+ * rINST_FULL holds A
+ */
+ GET_VREG(rINST_FULL,rINST_FULL) # rINST_FULL<- v[A]
+ movl offInstField_byteOffset(%eax),%eax # eax<- byte offset of field
+ UNSPILL(rIBASE)
+ testl %ecx,%ecx # object null?
+ je common_errNullObject # object was null
+ movl rINST_FULL,(%ecx,%eax,1) # obj.field <- v[A](8/16/32 bits)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* continuation for OP_IPUT_WIDE */
+
+
+.LOP_IPUT_WIDE_resolve:
+ EXPORT_PC()
+ SPILL(rPC)
+ movl offGlue_method(rIBASE),rPC # rPC<- current method
+ UNSPILL(rIBASE)
+ movl offMethod_clazz(rPC),rPC # rPC<- method->clazz
+ SPILL_TMP(%ecx) # save object pointer across call
+ movl rPC,OUT_ARG0(%esp) # pass in method->clazz
+ call dvmResolveInstField # ... to dvmResolveInstField
+ UNSPILL_TMP(%ecx)
+ UNSPILL(rPC)
+ testl %eax,%eax # ... which returns InstrField ptr
+ jne .LOP_IPUT_WIDE_finish
+ jmp common_exceptionThrown
+
+.LOP_IPUT_WIDE_finish:
+ /*
+ * Currently:
+ * eax holds resolved field
+ * ecx holds object
+ * rIBASE is scratch, but needs to be unspilled
+ * rINST_FULL holds A
+ */
+ movl offInstField_byteOffset(%eax),%eax # eax<- byte offset of field
+ UNSPILL(rIBASE)
+ testl %ecx,%ecx # object null?
+ je common_errNullObject # object was null
+ leal (%ecx,%eax,1),%eax # eax<- address of field
+ GET_VREG_WORD(%ecx,rINST_FULL,0) # ecx<- lsw
+ GET_VREG_WORD(rINST_FULL,rINST_FULL,1) # rINST_FULL<- msw
+ movl rINST_FULL,4(%eax)
+ FETCH_INST_WORD(2)
+ movl %ecx,(%eax)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* continuation for OP_IPUT_OBJECT */
+
+
+.LOP_IPUT_OBJECT_resolve:
+ EXPORT_PC()
+ SPILL(rPC)
+ movl offGlue_method(rIBASE),rPC # rPC<- current method
+ UNSPILL(rIBASE)
+ movl offMethod_clazz(rPC),rPC # rPC<- method->clazz
+ SPILL_TMP(%ecx) # save object pointer across call
+ movl rPC,OUT_ARG0(%esp) # pass in method->clazz
+ call dvmResolveInstField # ... to dvmResolveInstField
+ UNSPILL_TMP(%ecx)
+ UNSPILL(rPC)
+ testl %eax,%eax # ... which returns InstrField ptr
+ jne .LOP_IPUT_OBJECT_finish
+ jmp common_exceptionThrown
+
+.LOP_IPUT_OBJECT_finish:
+ /*
+ * Currently:
+ * eax holds resolved field
+ * ecx holds object
+ * rIBASE is scratch, but needs to be unspilled
+ * rINST_FULL holds A
+ */
+ GET_VREG(rINST_FULL,rINST_FULL) # rINST_FULL<- v[A]
+ movl offInstField_byteOffset(%eax),%eax # eax<- byte offset of field
+ UNSPILL(rIBASE)
+ testl %ecx,%ecx # object null?
+ je common_errNullObject # object was null
+ movl rINST_FULL,(%ecx,%eax) # obj.field <- v[A](8/16/32 bits)
+ GET_GLUE(%eax)
+ testl rINST_FULL,rINST_FULL # stored a NULL?
+ movl offGlue_cardTable(%eax),%eax # get card table base
+ FETCH_INST_WORD(2)
+ je 1f # skip card mark if null store
+ shrl $GC_CARD_SHIFT,%ecx # object head to card number
+ movb %al,(%eax,%ecx) # mark card using object head
+1:
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* continuation for OP_IPUT_BOOLEAN */
+
+
+.LOP_IPUT_BOOLEAN_resolve:
+ EXPORT_PC()
+ SPILL(rPC)
+ movl offGlue_method(rIBASE),rPC # rPC<- current method
+ UNSPILL(rIBASE)
+ movl offMethod_clazz(rPC),rPC # rPC<- method->clazz
+ SPILL_TMP(%ecx) # save object pointer across call
+ movl rPC,OUT_ARG0(%esp) # pass in method->clazz
+ call dvmResolveInstField # ... to dvmResolveInstField
+ UNSPILL_TMP(%ecx)
+ UNSPILL(rPC)
+ testl %eax,%eax # ... which returns InstrField ptr
+ jne .LOP_IPUT_BOOLEAN_finish
+ jmp common_exceptionThrown
+
+.LOP_IPUT_BOOLEAN_finish:
+ /*
+ * Currently:
+ * eax holds resolved field
+ * ecx holds object
+ * rIBASE is scratch, but needs to be unspilled
+ * rINST_FULL holds A
+ */
+ GET_VREG(rINST_FULL,rINST_FULL) # rINST_FULL<- v[A]
+ movl offInstField_byteOffset(%eax),%eax # eax<- byte offset of field
+ UNSPILL(rIBASE)
+ testl %ecx,%ecx # object null?
+ je common_errNullObject # object was null
+ movb rINST_LO,(%ecx,%eax,1) # obj.field <- v[A](8/16/32 bits)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* continuation for OP_IPUT_BYTE */
+
+
+.LOP_IPUT_BYTE_resolve:
+ EXPORT_PC()
+ SPILL(rPC)
+ movl offGlue_method(rIBASE),rPC # rPC<- current method
+ UNSPILL(rIBASE)
+ movl offMethod_clazz(rPC),rPC # rPC<- method->clazz
+ SPILL_TMP(%ecx) # save object pointer across call
+ movl rPC,OUT_ARG0(%esp) # pass in method->clazz
+ call dvmResolveInstField # ... to dvmResolveInstField
+ UNSPILL_TMP(%ecx)
+ UNSPILL(rPC)
+ testl %eax,%eax # ... which returns InstrField ptr
+ jne .LOP_IPUT_BYTE_finish
+ jmp common_exceptionThrown
+
+.LOP_IPUT_BYTE_finish:
+ /*
+ * Currently:
+ * eax holds resolved field
+ * ecx holds object
+ * rIBASE is scratch, but needs to be unspilled
+ * rINST_FULL holds A
+ */
+ GET_VREG(rINST_FULL,rINST_FULL) # rINST_FULL<- v[A]
+ movl offInstField_byteOffset(%eax),%eax # eax<- byte offset of field
+ UNSPILL(rIBASE)
+ testl %ecx,%ecx # object null?
+ je common_errNullObject # object was null
+ movb rINST_LO,(%ecx,%eax,1) # obj.field <- v[A](8/16/32 bits)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* continuation for OP_IPUT_CHAR */
+
+
+.LOP_IPUT_CHAR_resolve:
+ EXPORT_PC()
+ SPILL(rPC)
+ movl offGlue_method(rIBASE),rPC # rPC<- current method
+ UNSPILL(rIBASE)
+ movl offMethod_clazz(rPC),rPC # rPC<- method->clazz
+ SPILL_TMP(%ecx) # save object pointer across call
+ movl rPC,OUT_ARG0(%esp) # pass in method->clazz
+ call dvmResolveInstField # ... to dvmResolveInstField
+ UNSPILL_TMP(%ecx)
+ UNSPILL(rPC)
+ testl %eax,%eax # ... which returns InstrField ptr
+ jne .LOP_IPUT_CHAR_finish
+ jmp common_exceptionThrown
+
+.LOP_IPUT_CHAR_finish:
+ /*
+ * Currently:
+ * eax holds resolved field
+ * ecx holds object
+ * rIBASE is scratch, but needs to be unspilled
+ * rINST_FULL holds A
+ */
+ GET_VREG(rINST_FULL,rINST_FULL) # rINST_FULL<- v[A]
+ movl offInstField_byteOffset(%eax),%eax # eax<- byte offset of field
+ UNSPILL(rIBASE)
+ testl %ecx,%ecx # object null?
+ je common_errNullObject # object was null
+ movw rINST,(%ecx,%eax,1) # obj.field <- v[A](8/16/32 bits)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* continuation for OP_IPUT_SHORT */
+
+
+.LOP_IPUT_SHORT_resolve:
+ EXPORT_PC()
+ SPILL(rPC)
+ movl offGlue_method(rIBASE),rPC # rPC<- current method
+ UNSPILL(rIBASE)
+ movl offMethod_clazz(rPC),rPC # rPC<- method->clazz
+ SPILL_TMP(%ecx) # save object pointer across call
+ movl rPC,OUT_ARG0(%esp) # pass in method->clazz
+ call dvmResolveInstField # ... to dvmResolveInstField
+ UNSPILL_TMP(%ecx)
+ UNSPILL(rPC)
+ testl %eax,%eax # ... which returns InstrField ptr
+ jne .LOP_IPUT_SHORT_finish
+ jmp common_exceptionThrown
+
+.LOP_IPUT_SHORT_finish:
+ /*
+ * Currently:
+ * eax holds resolved field
+ * ecx holds object
+ * rIBASE is scratch, but needs to be unspilled
+ * rINST_FULL holds A
+ */
+ GET_VREG(rINST_FULL,rINST_FULL) # rINST_FULL<- v[A]
+ movl offInstField_byteOffset(%eax),%eax # eax<- byte offset of field
+ UNSPILL(rIBASE)
+ testl %ecx,%ecx # object null?
+ je common_errNullObject # object was null
+ movw rINST,(%ecx,%eax,1) # obj.field <- v[A](8/16/32 bits)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* continuation for OP_SGET */
+
+ /*
+ * Go resolve the field
+ */
+.LOP_SGET_resolve:
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_method(%ecx),%ecx # ecx<- current method
+ EXPORT_PC() # could throw, need to export
+ movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+ SPILL(rPC)
+ movl %eax,OUT_ARG1(%esp)
+ movl %ecx,OUT_ARG0(%esp)
+ call dvmResolveStaticField # eax<- resolved StaticField ptr
+ UNSPILL(rPC)
+ testl %eax,%eax
+ jne .LOP_SGET_finish # success, continue
+ jmp common_exceptionThrown # no, handle exception
+
+/* continuation for OP_SGET_WIDE */
+
+ /*
+ * Go resolve the field
+ */
+.LOP_SGET_WIDE_resolve:
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_method(%ecx),%ecx # ecx<- current method
+ EXPORT_PC() # could throw, need to export
+ movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+ SPILL(rPC)
+ movl %eax,OUT_ARG1(%esp)
+ movl %ecx,OUT_ARG0(%esp)
+ call dvmResolveStaticField # eax<- resolved StaticField ptr
+ UNSPILL(rPC)
+ testl %eax,%eax
+ jne .LOP_SGET_WIDE_finish # success, continue
+ jmp common_exceptionThrown # no, handle exception
+
+/* continuation for OP_SGET_OBJECT */
+
+ /*
+ * Go resolve the field
+ */
+.LOP_SGET_OBJECT_resolve:
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_method(%ecx),%ecx # ecx<- current method
+ EXPORT_PC() # could throw, need to export
+ movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+ SPILL(rPC)
+ movl %eax,OUT_ARG1(%esp)
+ movl %ecx,OUT_ARG0(%esp)
+ call dvmResolveStaticField # eax<- resolved StaticField ptr
+ UNSPILL(rPC)
+ testl %eax,%eax
+ jne .LOP_SGET_OBJECT_finish # success, continue
+ jmp common_exceptionThrown # no, handle exception
+
+/* continuation for OP_SGET_BOOLEAN */
+
+ /*
+ * Go resolve the field
+ */
+.LOP_SGET_BOOLEAN_resolve:
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_method(%ecx),%ecx # ecx<- current method
+ EXPORT_PC() # could throw, need to export
+ movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+ SPILL(rPC)
+ movl %eax,OUT_ARG1(%esp)
+ movl %ecx,OUT_ARG0(%esp)
+ call dvmResolveStaticField # eax<- resolved StaticField ptr
+ UNSPILL(rPC)
+ testl %eax,%eax
+ jne .LOP_SGET_BOOLEAN_finish # success, continue
+ jmp common_exceptionThrown # no, handle exception
+
+/* continuation for OP_SGET_BYTE */
+
+ /*
+ * Go resolve the field
+ */
+.LOP_SGET_BYTE_resolve:
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_method(%ecx),%ecx # ecx<- current method
+ EXPORT_PC() # could throw, need to export
+ movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+ SPILL(rPC)
+ movl %eax,OUT_ARG1(%esp)
+ movl %ecx,OUT_ARG0(%esp)
+ call dvmResolveStaticField # eax<- resolved StaticField ptr
+ UNSPILL(rPC)
+ testl %eax,%eax
+ jne .LOP_SGET_BYTE_finish # success, continue
+ jmp common_exceptionThrown # no, handle exception
+
+/* continuation for OP_SGET_CHAR */
+
+ /*
+ * Go resolve the field
+ */
+.LOP_SGET_CHAR_resolve:
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_method(%ecx),%ecx # ecx<- current method
+ EXPORT_PC() # could throw, need to export
+ movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+ SPILL(rPC)
+ movl %eax,OUT_ARG1(%esp)
+ movl %ecx,OUT_ARG0(%esp)
+ call dvmResolveStaticField # eax<- resolved StaticField ptr
+ UNSPILL(rPC)
+ testl %eax,%eax
+ jne .LOP_SGET_CHAR_finish # success, continue
+ jmp common_exceptionThrown # no, handle exception
+
+/* continuation for OP_SGET_SHORT */
+
+ /*
+ * Go resolve the field
+ */
+.LOP_SGET_SHORT_resolve:
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_method(%ecx),%ecx # ecx<- current method
+ EXPORT_PC() # could throw, need to export
+ movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+ SPILL(rPC)
+ movl %eax,OUT_ARG1(%esp)
+ movl %ecx,OUT_ARG0(%esp)
+ call dvmResolveStaticField # eax<- resolved StaticField ptr
+ UNSPILL(rPC)
+ testl %eax,%eax
+ jne .LOP_SGET_SHORT_finish # success, continue
+ jmp common_exceptionThrown # no, handle exception
+
+/* continuation for OP_SPUT */
+
+ /*
+ * Go resolve the field
+ */
+.LOP_SPUT_resolve:
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_method(%ecx),%ecx # ecx<- current method
+ EXPORT_PC() # could throw, need to export
+ movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+ SPILL(rPC)
+ movl %eax,OUT_ARG1(%esp)
+ movl %ecx,OUT_ARG0(%esp)
+ call dvmResolveStaticField # eax<- resolved StaticField ptr
+ UNSPILL(rPC)
+ testl %eax,%eax
+ jne .LOP_SPUT_finish # success, continue
+ jmp common_exceptionThrown # no, handle exception
+
+/* continuation for OP_SPUT_WIDE */
+
+ /*
+ * Go resolve the field
+ */
+.LOP_SPUT_WIDE_resolve:
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_method(%ecx),%ecx # ecx<- current method
+ EXPORT_PC() # could throw, need to export
+ movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+ SPILL(rPC)
+ movl %eax,OUT_ARG1(%esp)
+ movl %ecx,OUT_ARG0(%esp)
+ call dvmResolveStaticField # eax<- resolved StaticField ptr
+ UNSPILL(rPC)
+ testl %eax,%eax
+ jne .LOP_SPUT_WIDE_finish # success, continue
+ jmp common_exceptionThrown # no, handle exception
+
+/* continuation for OP_SPUT_OBJECT */
+
+
+.LOP_SPUT_OBJECT_continue:
+ movl %ecx,offStaticField_value(%eax) # do the store
+ testl %ecx,%ecx # stored null object ptr?
+ FETCH_INST_WORD(2)
+ je 1f # skip card mark if null
+ GET_GLUE(%ecx)
+ movl offField_clazz(%eax),%eax # eax<- field->clazz
+ movl offGlue_cardTable(%ecx),%ecx # get card table base
+ shrl $GC_CARD_SHIFT,%eax # head to card number
+ movb %cl,(%ecx,%eax) # mark card
+1:
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+.LOP_SPUT_OBJECT_resolve:
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_method(%ecx),%ecx # ecx<- current method
+ EXPORT_PC() # could throw, need to export
+ movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+ SPILL(rPC)
+ movl %eax,OUT_ARG1(%esp)
+ movl %ecx,OUT_ARG0(%esp)
+ call dvmResolveStaticField # eax<- resolved StaticField ptr
+ UNSPILL(rPC)
+ testl %eax,%eax
+ jne .LOP_SPUT_OBJECT_finish # success, continue
+ jmp common_exceptionThrown # no, handle exception
+
+/* continuation for OP_SPUT_BOOLEAN */
+
+ /*
+ * Go resolve the field
+ */
+.LOP_SPUT_BOOLEAN_resolve:
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_method(%ecx),%ecx # ecx<- current method
+ EXPORT_PC() # could throw, need to export
+ movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+ SPILL(rPC)
+ movl %eax,OUT_ARG1(%esp)
+ movl %ecx,OUT_ARG0(%esp)
+ call dvmResolveStaticField # eax<- resolved StaticField ptr
+ UNSPILL(rPC)
+ testl %eax,%eax
+ jne .LOP_SPUT_BOOLEAN_finish # success, continue
+ jmp common_exceptionThrown # no, handle exception
+
+/* continuation for OP_SPUT_BYTE */
+
+ /*
+ * Go resolve the field
+ */
+.LOP_SPUT_BYTE_resolve:
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_method(%ecx),%ecx # ecx<- current method
+ EXPORT_PC() # could throw, need to export
+ movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+ SPILL(rPC)
+ movl %eax,OUT_ARG1(%esp)
+ movl %ecx,OUT_ARG0(%esp)
+ call dvmResolveStaticField # eax<- resolved StaticField ptr
+ UNSPILL(rPC)
+ testl %eax,%eax
+ jne .LOP_SPUT_BYTE_finish # success, continue
+ jmp common_exceptionThrown # no, handle exception
+
+/* continuation for OP_SPUT_CHAR */
+
+ /*
+ * Go resolve the field
+ */
+.LOP_SPUT_CHAR_resolve:
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_method(%ecx),%ecx # ecx<- current method
+ EXPORT_PC() # could throw, need to export
+ movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+ SPILL(rPC)
+ movl %eax,OUT_ARG1(%esp)
+ movl %ecx,OUT_ARG0(%esp)
+ call dvmResolveStaticField # eax<- resolved StaticField ptr
+ UNSPILL(rPC)
+ testl %eax,%eax
+ jne .LOP_SPUT_CHAR_finish # success, continue
+ jmp common_exceptionThrown # no, handle exception
+
+/* continuation for OP_SPUT_SHORT */
+
+ /*
+ * Go resolve the field
+ */
+.LOP_SPUT_SHORT_resolve:
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_method(%ecx),%ecx # ecx<- current method
+ EXPORT_PC() # could throw, need to export
+ movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+ SPILL(rPC)
+ movl %eax,OUT_ARG1(%esp)
+ movl %ecx,OUT_ARG0(%esp)
+ call dvmResolveStaticField # eax<- resolved StaticField ptr
+ UNSPILL(rPC)
+ testl %eax,%eax
+ jne .LOP_SPUT_SHORT_finish # success, continue
+ jmp common_exceptionThrown # no, handle exception
+
+/* continuation for OP_INVOKE_VIRTUAL */
+
+
+.LOP_INVOKE_VIRTUAL_more:
+ movl offMethod_clazz(%eax),%eax # ecx<- method->clazz
+ movl %eax,OUT_ARG0(%esp) # arg0<- clazz
+ movl $METHOD_VIRTUAL,OUT_ARG2(%esp) # arg2<- flags
+ call dvmResolveMethod # eax<- call(clazz, ref, flags)
+ UNSPILL(rPC)
+ testl %eax,%eax # got null?
+ jne .LOP_INVOKE_VIRTUAL_continue # no, continue
+ jmp common_exceptionThrown # yes, handle exception
+
+ /* At this point:
+ * eax = resolved base method
+ * ecx = scratch
+ */
+.LOP_INVOKE_VIRTUAL_continue:
+ movzwl 4(rPC),%ecx # ecx<- GFED or CCCC
+ .if (!0)
+ andl $0xf,%ecx # ecx<- D (or stays CCCC)
+ .endif
+ GET_VREG(%ecx,%ecx) # ecx<- "this"
+ movzwl offMethod_methodIndex(%eax),%eax # eax<- baseMethod->methodIndex
+ testl %ecx,%ecx # null this?
+ je common_errNullObject # go if so
+ movl offObject_clazz(%ecx),%ecx # ecx<- thisPtr->clazz
+ movl offClassObject_vtable(%ecx),%ecx # ecx<- thisPtr->clazz->vtable
+ movl (%ecx,%eax,4),%eax # eax<- vtable[methodIndex]
+ jmp common_invokeMethodNoRange
+
+/* continuation for OP_INVOKE_SUPER */
+
+ /*
+ * At this point:
+ * ecx = resolved base method [r0]
+ * eax = method->clazz [r9]
+ */
+.LOP_INVOKE_SUPER_continue:
+ movl offClassObject_super(%eax),%eax # eax<- method->clazz->super
+ movzwl offMethod_methodIndex(%ecx),%ecx # ecx<- baseMthod->methodIndex
+ cmpl offClassObject_vtableCount(%eax),%ecx # compare(methodIndex,vtableCount)
+ jae .LOP_INVOKE_SUPER_nsm # method not present in superclass
+ movl offClassObject_vtable(%eax),%eax # eax<- ...clazz->super->vtable
+ movl (%eax,%ecx,4),%eax # eax<- vtable[methodIndex]
+ jmp common_invokeMethodNoRange
+
+
+ /* At this point:
+ * ecx = null (needs to be resolved base method)
+ * eax = method->clazz
+ */
+.LOP_INVOKE_SUPER_resolve:
+ SPILL_TMP(%eax) # method->clazz
+ movl %eax,OUT_ARG0(%esp) # arg0<- method->clazz
+ movzwl 2(rPC),%ecx # ecx<- BBBB
+ movl $METHOD_VIRTUAL,OUT_ARG2(%esp) # arg2<- resolver method type
+ movl %ecx,OUT_ARG1(%esp) # arg1<- ref
+ SPILL(rPC)
+ call dvmResolveMethod # eax<- call(clazz, ref, flags)
+ UNSPILL(rPC)
+ testl %eax,%eax # got null?
+ movl %eax,%ecx # ecx<- resolved base method
+ UNSPILL_TMP(%eax) # restore method->clazz
+ jne .LOP_INVOKE_SUPER_continue # good to go - continue
+ jmp common_exceptionThrown # handle exception
+
+ /*
+ * Throw a NoSuchMethodError with the method name as the message.
+ * ecx = resolved base method
+ */
+.LOP_INVOKE_SUPER_nsm:
+ movl offMethod_name(%ecx),%eax
+ mov %eax,OUT_ARG1(%esp)
+ jmp common_errNoSuchMethod
+
+/* continuation for OP_INVOKE_DIRECT */
+
+ /*
+ * On entry:
+ * TMP_SPILL <- "this" register
+ * Things a bit ugly on this path, but it's the less
+ * frequent one. We'll have to do some reloading.
+ */
+.LOP_INVOKE_DIRECT_resolve:
+ SPILL_TMP(%ecx)
+ GET_GLUE(%ecx)
+ UNSPILL(rPC)
+ movl offGlue_method(%ecx),%ecx # ecx<- glue->method
+ movzwl 2(rPC),%eax # reference (BBBB or CCCC)
+ movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+ movl $METHOD_DIRECT,OUT_ARG2(%esp)
+ movl %eax,OUT_ARG1(%esp)
+ movl %ecx,OUT_ARG0(%esp)
+ call dvmResolveMethod # eax<- call(clazz, ref, flags)
+ UNSPILL_TMP(%ecx)
+ testl %eax,%eax
+ jne .LOP_INVOKE_DIRECT_finish
+ UNSPILL(rPC)
+ jmp common_exceptionThrown
+
+/* continuation for OP_INVOKE_STATIC */
+
+.LOP_INVOKE_STATIC_continue:
+ movl $METHOD_STATIC,%eax
+ movl %eax,OUT_ARG2(%esp) # arg2<- flags
+ SPILL(rPC)
+ call dvmResolveMethod # call(clazz,ref,flags)
+ UNSPILL(rPC)
+ testl %eax,%eax # got null?
+ jne common_invokeMethodNoRange
+ jmp common_exceptionThrown
+
+/* continuation for OP_INVOKE_INTERFACE */
+
+.LOP_INVOKE_INTERFACE_continue:
+ call dvmFindInterfaceMethodInCache # eax<- call(class, ref, method, dex)
+ UNSPILL(rPC)
+ testl %eax,%eax
+ je common_exceptionThrown
+ jmp common_invokeMethodNoRange
+
+/* continuation for OP_INVOKE_VIRTUAL_RANGE */
+
+
+.LOP_INVOKE_VIRTUAL_RANGE_more:
+ movl offMethod_clazz(%eax),%eax # ecx<- method->clazz
+ movl %eax,OUT_ARG0(%esp) # arg0<- clazz
+ movl $METHOD_VIRTUAL,OUT_ARG2(%esp) # arg2<- flags
+ call dvmResolveMethod # eax<- call(clazz, ref, flags)
+ UNSPILL(rPC)
+ testl %eax,%eax # got null?
+ jne .LOP_INVOKE_VIRTUAL_RANGE_continue # no, continue
+ jmp common_exceptionThrown # yes, handle exception
+
+ /* At this point:
+ * eax = resolved base method
+ * ecx = scratch
+ */
+.LOP_INVOKE_VIRTUAL_RANGE_continue:
+ movzwl 4(rPC),%ecx # ecx<- GFED or CCCC
+ .if (!1)
+ andl $0xf,%ecx # ecx<- D (or stays CCCC)
+ .endif
+ GET_VREG(%ecx,%ecx) # ecx<- "this"
+ movzwl offMethod_methodIndex(%eax),%eax # eax<- baseMethod->methodIndex
+ testl %ecx,%ecx # null this?
+ je common_errNullObject # go if so
+ movl offObject_clazz(%ecx),%ecx # ecx<- thisPtr->clazz
+ movl offClassObject_vtable(%ecx),%ecx # ecx<- thisPtr->clazz->vtable
+ movl (%ecx,%eax,4),%eax # eax<- vtable[methodIndex]
+ jmp common_invokeMethodRange
+
+/* continuation for OP_INVOKE_SUPER_RANGE */
+
+ /*
+ * At this point:
+ * ecx = resolved base method [r0]
+ * eax = method->clazz [r9]
+ */
+.LOP_INVOKE_SUPER_RANGE_continue:
+ movl offClassObject_super(%eax),%eax # eax<- method->clazz->super
+ movzwl offMethod_methodIndex(%ecx),%ecx # ecx<- baseMthod->methodIndex
+ cmpl offClassObject_vtableCount(%eax),%ecx # compare(methodIndex,vtableCount)
+ jae .LOP_INVOKE_SUPER_RANGE_nsm # method not present in superclass
+ movl offClassObject_vtable(%eax),%eax # eax<- ...clazz->super->vtable
+ movl (%eax,%ecx,4),%eax # eax<- vtable[methodIndex]
+ jmp common_invokeMethodRange
+
+
+ /* At this point:
+ * ecx = null (needs to be resolved base method)
+ * eax = method->clazz
+ */
+.LOP_INVOKE_SUPER_RANGE_resolve:
+ SPILL_TMP(%eax) # method->clazz
+ movl %eax,OUT_ARG0(%esp) # arg0<- method->clazz
+ movzwl 2(rPC),%ecx # ecx<- BBBB
+ movl $METHOD_VIRTUAL,OUT_ARG2(%esp) # arg2<- resolver method type
+ movl %ecx,OUT_ARG1(%esp) # arg1<- ref
+ SPILL(rPC)
+ call dvmResolveMethod # eax<- call(clazz, ref, flags)
+ UNSPILL(rPC)
+ testl %eax,%eax # got null?
+ movl %eax,%ecx # ecx<- resolved base method
+ UNSPILL_TMP(%eax) # restore method->clazz
+ jne .LOP_INVOKE_SUPER_RANGE_continue # good to go - continue
+ jmp common_exceptionThrown # handle exception
+
+ /*
+ * Throw a NoSuchMethodError with the method name as the message.
+ * ecx = resolved base method
+ */
+.LOP_INVOKE_SUPER_RANGE_nsm:
+ movl offMethod_name(%ecx),%eax
+ mov %eax,OUT_ARG1(%esp)
+ jmp common_errNoSuchMethod
+
+/* continuation for OP_INVOKE_DIRECT_RANGE */
+
+ /*
+ * On entry:
+ * TMP_SPILL <- "this" register
+ * Things a bit ugly on this path, but it's the less
+ * frequent one. We'll have to do some reloading.
+ */
+.LOP_INVOKE_DIRECT_RANGE_resolve:
+ SPILL_TMP(%ecx)
+ GET_GLUE(%ecx)
+ UNSPILL(rPC)
+ movl offGlue_method(%ecx),%ecx # ecx<- glue->method
+ movzwl 2(rPC),%eax # reference (BBBB or CCCC)
+ movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+ movl $METHOD_DIRECT,OUT_ARG2(%esp)
+ movl %eax,OUT_ARG1(%esp)
+ movl %ecx,OUT_ARG0(%esp)
+ call dvmResolveMethod # eax<- call(clazz, ref, flags)
+ UNSPILL_TMP(%ecx)
+ testl %eax,%eax
+ jne .LOP_INVOKE_DIRECT_RANGE_finish
+ UNSPILL(rPC)
+ jmp common_exceptionThrown
+
+/* continuation for OP_INVOKE_STATIC_RANGE */
+
+.LOP_INVOKE_STATIC_RANGE_continue:
+ movl $METHOD_STATIC,%eax
+ movl %eax,OUT_ARG2(%esp) # arg2<- flags
+ SPILL(rPC)
+ call dvmResolveMethod # call(clazz,ref,flags)
+ UNSPILL(rPC)
+ testl %eax,%eax # got null?
+ jne common_invokeMethodRange
+ jmp common_exceptionThrown
+
+/* continuation for OP_INVOKE_INTERFACE_RANGE */
+
+.LOP_INVOKE_INTERFACE_RANGE_continue:
+ call dvmFindInterfaceMethodInCache # eax<- call(class, ref, method, dex)
+ UNSPILL(rPC)
+ testl %eax,%eax
+ je common_exceptionThrown
+ jmp common_invokeMethodRange
+
+/* continuation for OP_FLOAT_TO_INT */
+
+
+.LOP_FLOAT_TO_INT_continue:
+ .if 0
+ movl $0x80000000,%eax
+ xorl 4(rFP,%ecx,4),%eax
+ orl (rFP,%ecx,4),%eax
+ .else
+ cmpl $0x80000000,(rFP,%ecx,4)
+ .endif
+ je .LOP_FLOAT_TO_INT_special_case # fix up result
+
+.LOP_FLOAT_TO_INT_finish:
+ ADVANCE_PC(1)
+ GOTO_NEXT
+
+.LOP_FLOAT_TO_INT_special_case:
+ fnstsw %ax
+ sahf
+ jp .LOP_FLOAT_TO_INT_isNaN
+ adcl $-1,(rFP,%ecx,4)
+ .if 0
+ adcl $-1,4(rFP,%ecx,4)
+ .endif
+ jmp .LOP_FLOAT_TO_INT_finish
+.LOP_FLOAT_TO_INT_isNaN:
+ movl $0,(rFP,%ecx,4)
+ .if 0
+ movl $0,4(rFP,%ecx,4)
+ .endif
+ jmp .LOP_FLOAT_TO_INT_finish
+
+/* continuation for OP_FLOAT_TO_LONG */
+
+
+.LOP_FLOAT_TO_LONG_continue:
+ .if 1
+ movl $0x80000000,%eax
+ xorl 4(rFP,%ecx,4),%eax
+ orl (rFP,%ecx,4),%eax
+ .else
+ cmpl $0x80000000,(rFP,%ecx,4)
+ .endif
+ je .LOP_FLOAT_TO_LONG_special_case # fix up result
+
+.LOP_FLOAT_TO_LONG_finish:
+ ADVANCE_PC(1)
+ GOTO_NEXT
+
+.LOP_FLOAT_TO_LONG_special_case:
+ fnstsw %ax
+ sahf
+ jp .LOP_FLOAT_TO_LONG_isNaN
+ adcl $-1,(rFP,%ecx,4)
+ .if 1
+ adcl $-1,4(rFP,%ecx,4)
+ .endif
+ jmp .LOP_FLOAT_TO_LONG_finish
+.LOP_FLOAT_TO_LONG_isNaN:
+ movl $0,(rFP,%ecx,4)
+ .if 1
+ movl $0,4(rFP,%ecx,4)
+ .endif
+ jmp .LOP_FLOAT_TO_LONG_finish
+
+/* continuation for OP_DOUBLE_TO_INT */
+
+
+.LOP_DOUBLE_TO_INT_continue:
+ .if 0
+ movl $0x80000000,%eax
+ xorl 4(rFP,%ecx,4),%eax
+ orl (rFP,%ecx,4),%eax
+ .else
+ cmpl $0x80000000,(rFP,%ecx,4)
+ .endif
+ je .LOP_DOUBLE_TO_INT_special_case # fix up result
+
+.LOP_DOUBLE_TO_INT_finish:
+ ADVANCE_PC(1)
+ GOTO_NEXT
+
+.LOP_DOUBLE_TO_INT_special_case:
+ fnstsw %ax
+ sahf
+ jp .LOP_DOUBLE_TO_INT_isNaN
+ adcl $-1,(rFP,%ecx,4)
+ .if 0
+ adcl $-1,4(rFP,%ecx,4)
+ .endif
+ jmp .LOP_DOUBLE_TO_INT_finish
+.LOP_DOUBLE_TO_INT_isNaN:
+ movl $0,(rFP,%ecx,4)
+ .if 0
+ movl $0,4(rFP,%ecx,4)
+ .endif
+ jmp .LOP_DOUBLE_TO_INT_finish
+
+/* continuation for OP_DOUBLE_TO_LONG */
+
+
+.LOP_DOUBLE_TO_LONG_continue:
+ .if 1
+ movl $0x80000000,%eax
+ xorl 4(rFP,%ecx,4),%eax
+ orl (rFP,%ecx,4),%eax
+ .else
+ cmpl $0x80000000,(rFP,%ecx,4)
+ .endif
+ je .LOP_DOUBLE_TO_LONG_special_case # fix up result
+
+.LOP_DOUBLE_TO_LONG_finish:
+ ADVANCE_PC(1)
+ GOTO_NEXT
+
+.LOP_DOUBLE_TO_LONG_special_case:
+ fnstsw %ax
+ sahf
+ jp .LOP_DOUBLE_TO_LONG_isNaN
+ adcl $-1,(rFP,%ecx,4)
+ .if 1
+ adcl $-1,4(rFP,%ecx,4)
+ .endif
+ jmp .LOP_DOUBLE_TO_LONG_finish
+.LOP_DOUBLE_TO_LONG_isNaN:
+ movl $0,(rFP,%ecx,4)
+ .if 1
+ movl $0,4(rFP,%ecx,4)
+ .endif
+ jmp .LOP_DOUBLE_TO_LONG_finish
+
+/* continuation for OP_DIV_INT */
+.LOP_DIV_INT_continue_div:
+ cltd
+ idivl %ecx
+.LOP_DIV_INT_finish_div:
+ movzbl rINST_HI,%ecx # ecl<- AA
+ SET_VREG(%eax,%ecx)
+ UNSPILL(rPC)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* continuation for OP_REM_INT */
+.LOP_REM_INT_continue_div:
+ cltd
+ idivl %ecx
+.LOP_REM_INT_finish_div:
+ movzbl rINST_HI,%ecx # ecl<- AA
+ SET_VREG(%edx,%ecx)
+ UNSPILL(rPC)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* continuation for OP_MUL_LONG */
+
+.LOP_MUL_LONG_continue:
+ leal (%ecx,%edx),%edx # full result now in %edx:%eax
+ movzbl rINST_HI,%ecx # ecx<- A
+ movl %edx,4(rFP,%ecx,4) # v[B+1]<- %edx
+ UNSPILL(rPC) # restore rPC/%edx
+ FETCH_INST_WORD(2)
+ UNSPILL(rIBASE)
+ movl %eax,(rFP,%ecx,4) # v[B]<- %eax
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* continuation for OP_DIV_LONG */
+
+.LOP_DIV_LONG_continue:
+ call __divdi3
+.LOP_DIV_LONG_finish:
+ movzbl rINST_HI,%ecx
+ SET_VREG_WORD(rPC,%ecx,1)
+ UNSPILL(rPC)
+ SET_VREG_WORD(%eax,%ecx,0)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+.LOP_DIV_LONG_check_zero:
+ testl rPC,rPC
+ jne .LOP_DIV_LONG_notSpecial
+ UNSPILL(rPC)
+ jmp common_errDivideByZero
+.LOP_DIV_LONG_check_neg1:
+ testl rPC,%eax
+ jne .LOP_DIV_LONG_notSpecial
+ GET_VREG_WORD(rPC,%ecx,0)
+ GET_VREG_WORD(%ecx,%ecx,1)
+ testl rPC,rPC
+ jne .LOP_DIV_LONG_notSpecial1
+ cmpl $0x80000000,%ecx
+ jne .LOP_DIV_LONG_notSpecial1
+ /* minint / -1, return minint on div, 0 on rem */
+ xorl %eax,%eax
+ movl $0x80000000,%edx
+ jmp .LOP_DIV_LONG_finish
+
+/* continuation for OP_REM_LONG */
+
+.LOP_REM_LONG_continue:
+ call __moddi3
+.LOP_REM_LONG_finish:
+ movzbl rINST_HI,%ecx
+ SET_VREG_WORD(rPC,%ecx,1)
+ UNSPILL(rPC)
+ SET_VREG_WORD(%eax,%ecx,0)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+.LOP_REM_LONG_check_zero:
+ testl rPC,rPC
+ jne .LOP_REM_LONG_notSpecial
+ UNSPILL(rPC)
+ jmp common_errDivideByZero
+.LOP_REM_LONG_check_neg1:
+ testl rPC,%eax
+ jne .LOP_REM_LONG_notSpecial
+ GET_VREG_WORD(rPC,%ecx,0)
+ GET_VREG_WORD(%ecx,%ecx,1)
+ testl rPC,rPC
+ jne .LOP_REM_LONG_notSpecial1
+ cmpl $0x80000000,%ecx
+ jne .LOP_REM_LONG_notSpecial1
+ /* minint / -1, return minint on div, 0 on rem */
+ xorl %eax,%eax
+ movl $0,%edx
+ jmp .LOP_REM_LONG_finish
+
+/* continuation for OP_SHL_LONG */
+
+.LOP_SHL_LONG_finish:
+ SET_VREG_WORD(%eax,%ecx,0) # v[AA+0]<- %eax
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* continuation for OP_SHR_LONG */
+
+
+.LOP_SHR_LONG_finish:
+ SET_VREG_WORD(%eax,%ecx,0) # v[AA+0]<- eax
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* continuation for OP_USHR_LONG */
+
+
+.LOP_USHR_LONG_finish:
+ SET_VREG_WORD(%eax,%ecx,0) # v[BB+0]<- eax
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* continuation for OP_DIV_INT_2ADDR */
+.LOP_DIV_INT_2ADDR_continue_div2addr:
+ cltd
+ idivl %ecx
+.LOP_DIV_INT_2ADDR_finish_div2addr:
+ SET_VREG(%eax,rINST_FULL)
+ UNSPILL(rPC)
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ GOTO_NEXT
+
+/* continuation for OP_REM_INT_2ADDR */
+.LOP_REM_INT_2ADDR_continue_div2addr:
+ cltd
+ idivl %ecx
+.LOP_REM_INT_2ADDR_finish_div2addr:
+ SET_VREG(%edx,rINST_FULL)
+ UNSPILL(rPC)
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ GOTO_NEXT
+
+/* continuation for OP_MUL_LONG_2ADDR */
+
+.LOP_MUL_LONG_2ADDR_continue:
+ leal (%ecx,%edx),%edx # full result now in %edx:%eax
+ movl %edx,4(rIBASE) # v[A+1]<- %edx
+ UNSPILL(rPC) # restore rPC/%edx
+ FETCH_INST_WORD(1)
+ movl %eax,(rIBASE) # v[A]<- %eax
+ UNSPILL(rFP)
+ UNSPILL(rIBASE)
+ ADVANCE_PC(1)
+ GOTO_NEXT
+
+/* continuation for OP_DIV_LONG_2ADDR */
+
+.LOP_DIV_LONG_2ADDR_continue:
+ movl %eax,OUT_ARG3(%esp)
+ movl rPC,OUT_ARG0(%esp)
+ movl %ecx,OUT_ARG1(%esp)
+ call __divdi3
+.LOP_DIV_LONG_2ADDR_finish:
+ movl rINST_FULL,%ecx
+ SET_VREG_WORD(rPC,%ecx,1)
+ UNSPILL(rPC)
+ SET_VREG_WORD(%eax,%ecx,0)
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ GOTO_NEXT
+
+.LOP_DIV_LONG_2ADDR_check_zero:
+ testl rPC,rPC
+ jne .LOP_DIV_LONG_2ADDR_notSpecial
+ UNSPILL(rPC)
+ jmp common_errDivideByZero
+.LOP_DIV_LONG_2ADDR_check_neg1:
+ testl rPC,%eax
+ jne .LOP_DIV_LONG_2ADDR_notSpecial
+ GET_VREG_WORD(rPC,rINST_FULL,0)
+ GET_VREG_WORD(%ecx,rINST_FULL,1)
+ testl rPC,rPC
+ jne .LOP_DIV_LONG_2ADDR_notSpecial1
+ cmpl $0x80000000,%ecx
+ jne .LOP_DIV_LONG_2ADDR_notSpecial1
+ /* minint / -1, return minint on div, 0 on rem */
+ xorl %eax,%eax
+ movl $0x80000000,%edx
+ jmp .LOP_DIV_LONG_2ADDR_finish
+
+/* continuation for OP_REM_LONG_2ADDR */
+
+.LOP_REM_LONG_2ADDR_continue:
+ movl %eax,OUT_ARG3(%esp)
+ movl rPC,OUT_ARG0(%esp)
+ movl %ecx,OUT_ARG1(%esp)
+ call __moddi3
+.LOP_REM_LONG_2ADDR_finish:
+ movl rINST_FULL,%ecx
+ SET_VREG_WORD(rPC,%ecx,1)
+ UNSPILL(rPC)
+ SET_VREG_WORD(%eax,%ecx,0)
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ GOTO_NEXT
+
+.LOP_REM_LONG_2ADDR_check_zero:
+ testl rPC,rPC
+ jne .LOP_REM_LONG_2ADDR_notSpecial
+ UNSPILL(rPC)
+ jmp common_errDivideByZero
+.LOP_REM_LONG_2ADDR_check_neg1:
+ testl rPC,%eax
+ jne .LOP_REM_LONG_2ADDR_notSpecial
+ GET_VREG_WORD(rPC,rINST_FULL,0)
+ GET_VREG_WORD(%ecx,rINST_FULL,1)
+ testl rPC,rPC
+ jne .LOP_REM_LONG_2ADDR_notSpecial1
+ cmpl $0x80000000,%ecx
+ jne .LOP_REM_LONG_2ADDR_notSpecial1
+ /* minint / -1, return minint on div, 0 on rem */
+ xorl %eax,%eax
+ movl $0,%edx
+ jmp .LOP_REM_LONG_2ADDR_finish
+
+/* continuation for OP_SHL_LONG_2ADDR */
+
+
+.LOP_SHL_LONG_2ADDR_finish:
+ SET_VREG_WORD(%eax,rINST_FULL,0) # v[AA+0]<- eax
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ GOTO_NEXT
+
+/* continuation for OP_SHR_LONG_2ADDR */
+
+
+.LOP_SHR_LONG_2ADDR_finish:
+ SET_VREG_WORD(%eax,rINST_FULL,0) # v[AA+0]<- eax
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ GOTO_NEXT
+
+/* continuation for OP_USHR_LONG_2ADDR */
+
+
+.LOP_USHR_LONG_2ADDR_finish:
+ SET_VREG_WORD(%eax,rINST_FULL,0) # v[AA+0]<- eax
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ GOTO_NEXT
+
+/* continuation for OP_DIV_INT_LIT16 */
+.LOP_DIV_INT_LIT16_continue_div:
+ cltd
+ idivl %ecx
+.LOP_DIV_INT_LIT16_finish_div:
+ SET_VREG(%eax,rINST_FULL)
+ UNSPILL(rPC)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* continuation for OP_REM_INT_LIT16 */
+.LOP_REM_INT_LIT16_continue_div:
+ cltd
+ idivl %ecx
+.LOP_REM_INT_LIT16_finish_div:
+ SET_VREG(%edx,rINST_FULL)
+ UNSPILL(rPC)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* continuation for OP_DIV_INT_LIT8 */
+.LOP_DIV_INT_LIT8_continue_div:
+ cltd
+ idivl %ecx
+.LOP_DIV_INT_LIT8_finish_div:
+ SET_VREG(%eax,rINST_FULL)
+ UNSPILL(rPC)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* continuation for OP_REM_INT_LIT8 */
+.LOP_REM_INT_LIT8_continue_div:
+ cltd
+ idivl %ecx
+.LOP_REM_INT_LIT8_finish_div:
+ SET_VREG(%edx,rINST_FULL)
+ UNSPILL(rPC)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* continuation for OP_IGET_VOLATILE */
+
+
+.LOP_IGET_VOLATILE_resolve:
+ EXPORT_PC()
+ SPILL(rPC)
+ movl offGlue_method(rIBASE),rPC # rPC<- current method
+ UNSPILL(rIBASE)
+ movl offMethod_clazz(rPC),rPC # rPC<- method->clazz
+ SPILL_TMP(%ecx) # save object pointer across call
+ movl rPC,OUT_ARG0(%esp) # pass in method->clazz
+ call dvmResolveInstField # ... to dvmResolveInstField
+ UNSPILL_TMP(%ecx)
+ UNSPILL(rPC)
+ testl %eax,%eax # ... which returns InstrField ptr
+ jne .LOP_IGET_VOLATILE_finish
+ jmp common_exceptionThrown
+
+.LOP_IGET_VOLATILE_finish:
+ /*
+ * Currently:
+ * eax holds resolved field
+ * ecx holds object
+ * rIBASE is scratch, but needs to be unspilled
+ * rINST_FULL holds A
+ */
+ movl offInstField_byteOffset(%eax),%eax # eax<- byte offset of field
+ UNSPILL(rIBASE)
+ testl %ecx,%ecx # object null?
+ je common_errNullObject # object was null
+ movl (%ecx,%eax,1),%ecx # ecx<- obj.field (8/16/32 bits)
+ movl rINST_FULL,%eax # eax<- A
+ FETCH_INST_WORD(2)
+ SET_VREG(%ecx,%eax)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* continuation for OP_IPUT_VOLATILE */
+
+
+.LOP_IPUT_VOLATILE_resolve:
+ EXPORT_PC()
+ SPILL(rPC)
+ movl offGlue_method(rIBASE),rPC # rPC<- current method
+ UNSPILL(rIBASE)
+ movl offMethod_clazz(rPC),rPC # rPC<- method->clazz
+ SPILL_TMP(%ecx) # save object pointer across call
+ movl rPC,OUT_ARG0(%esp) # pass in method->clazz
+ call dvmResolveInstField # ... to dvmResolveInstField
+ UNSPILL_TMP(%ecx)
+ UNSPILL(rPC)
+ testl %eax,%eax # ... which returns InstrField ptr
+ jne .LOP_IPUT_VOLATILE_finish
+ jmp common_exceptionThrown
+
+.LOP_IPUT_VOLATILE_finish:
+ /*
+ * Currently:
+ * eax holds resolved field
+ * ecx holds object
+ * rIBASE is scratch, but needs to be unspilled
+ * rINST_FULL holds A
+ */
+ GET_VREG(rINST_FULL,rINST_FULL) # rINST_FULL<- v[A]
+ movl offInstField_byteOffset(%eax),%eax # eax<- byte offset of field
+ UNSPILL(rIBASE)
+ testl %ecx,%ecx # object null?
+ je common_errNullObject # object was null
+ movl rINST_FULL,(%ecx,%eax,1) # obj.field <- v[A](8/16/32 bits)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* continuation for OP_SGET_VOLATILE */
+
+ /*
+ * Go resolve the field
+ */
+.LOP_SGET_VOLATILE_resolve:
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_method(%ecx),%ecx # ecx<- current method
+ EXPORT_PC() # could throw, need to export
+ movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+ SPILL(rPC)
+ movl %eax,OUT_ARG1(%esp)
+ movl %ecx,OUT_ARG0(%esp)
+ call dvmResolveStaticField # eax<- resolved StaticField ptr
+ UNSPILL(rPC)
+ testl %eax,%eax
+ jne .LOP_SGET_VOLATILE_finish # success, continue
+ jmp common_exceptionThrown # no, handle exception
+
+/* continuation for OP_SPUT_VOLATILE */
+
+ /*
+ * Go resolve the field
+ */
+.LOP_SPUT_VOLATILE_resolve:
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_method(%ecx),%ecx # ecx<- current method
+ EXPORT_PC() # could throw, need to export
+ movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+ SPILL(rPC)
+ movl %eax,OUT_ARG1(%esp)
+ movl %ecx,OUT_ARG0(%esp)
+ call dvmResolveStaticField # eax<- resolved StaticField ptr
+ UNSPILL(rPC)
+ testl %eax,%eax
+ jne .LOP_SPUT_VOLATILE_finish # success, continue
+ jmp common_exceptionThrown # no, handle exception
+
+/* continuation for OP_IGET_OBJECT_VOLATILE */
+
+
+.LOP_IGET_OBJECT_VOLATILE_resolve:
+ EXPORT_PC()
+ SPILL(rPC)
+ movl offGlue_method(rIBASE),rPC # rPC<- current method
+ UNSPILL(rIBASE)
+ movl offMethod_clazz(rPC),rPC # rPC<- method->clazz
+ SPILL_TMP(%ecx) # save object pointer across call
+ movl rPC,OUT_ARG0(%esp) # pass in method->clazz
+ call dvmResolveInstField # ... to dvmResolveInstField
+ UNSPILL_TMP(%ecx)
+ UNSPILL(rPC)
+ testl %eax,%eax # ... which returns InstrField ptr
+ jne .LOP_IGET_OBJECT_VOLATILE_finish
+ jmp common_exceptionThrown
+
+.LOP_IGET_OBJECT_VOLATILE_finish:
+ /*
+ * Currently:
+ * eax holds resolved field
+ * ecx holds object
+ * rIBASE is scratch, but needs to be unspilled
+ * rINST_FULL holds A
+ */
+ movl offInstField_byteOffset(%eax),%eax # eax<- byte offset of field
+ UNSPILL(rIBASE)
+ testl %ecx,%ecx # object null?
+ je common_errNullObject # object was null
+ movl (%ecx,%eax,1),%ecx # ecx<- obj.field (8/16/32 bits)
+ movl rINST_FULL,%eax # eax<- A
+ FETCH_INST_WORD(2)
+ SET_VREG(%ecx,%eax)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* continuation for OP_EXECUTE_INLINE */
+
+.LOP_EXECUTE_INLINE_continue:
+ /*
+ * Extract args, call function.
+ * ecx = #of args (0-4)
+ * eax = call index
+ * @esp = return addr
+ * esp is -4 from normal
+ *
+ * Go ahead and load all 4 args, even if not used.
+ */
+ movzwl 4(rPC),rPC
+
+ movl $0xf,%ecx
+ andl rPC,%ecx
+ GET_VREG(%ecx,%ecx)
+ sarl $4,rPC
+ movl %ecx,4+OUT_ARG0(%esp)
+
+ movl $0xf,%ecx
+ andl rPC,%ecx
+ GET_VREG(%ecx,%ecx)
+ sarl $4,rPC
+ movl %ecx,4+OUT_ARG1(%esp)
+
+ movl $0xf,%ecx
+ andl rPC,%ecx
+ GET_VREG(%ecx,%ecx)
+ sarl $4,rPC
+ movl %ecx,4+OUT_ARG2(%esp)
+
+ movl $0xf,%ecx
+ andl rPC,%ecx
+ GET_VREG(%ecx,%ecx)
+ sarl $4,rPC
+ movl %ecx,4+OUT_ARG3(%esp)
+
+ sall $4,%eax # index *= sizeof(table entry)
+ jmp *gDvmInlineOpsTable(%eax)
+ # will return to caller of .LOP_EXECUTE_INLINE_continue
+
+/* continuation for OP_IPUT_OBJECT_QUICK */
+
+.LOP_IPUT_OBJECT_QUICK_finish:
+ testl rINST_FULL,rINST_FULL # did we store null?
+ FETCH_INST_WORD(2)
+ movl offGlue_cardTable(%eax),%eax # get card table base
+ je 1f # skip card mark if null store
+ shrl $GC_CARD_SHIFT,%ecx # object head to card number
+ movb %al,(%eax,%ecx) # mark card based on object head
+1:
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* continuation for OP_IPUT_OBJECT_VOLATILE */
+
+
+.LOP_IPUT_OBJECT_VOLATILE_resolve:
+ EXPORT_PC()
+ SPILL(rPC)
+ movl offGlue_method(rIBASE),rPC # rPC<- current method
+ UNSPILL(rIBASE)
+ movl offMethod_clazz(rPC),rPC # rPC<- method->clazz
+ SPILL_TMP(%ecx) # save object pointer across call
+ movl rPC,OUT_ARG0(%esp) # pass in method->clazz
+ call dvmResolveInstField # ... to dvmResolveInstField
+ UNSPILL_TMP(%ecx)
+ UNSPILL(rPC)
+ testl %eax,%eax # ... which returns InstrField ptr
+ jne .LOP_IPUT_OBJECT_VOLATILE_finish
+ jmp common_exceptionThrown
+
+.LOP_IPUT_OBJECT_VOLATILE_finish:
+ /*
+ * Currently:
+ * eax holds resolved field
+ * ecx holds object
+ * rIBASE is scratch, but needs to be unspilled
+ * rINST_FULL holds A
+ */
+ GET_VREG(rINST_FULL,rINST_FULL) # rINST_FULL<- v[A]
+ movl offInstField_byteOffset(%eax),%eax # eax<- byte offset of field
+ UNSPILL(rIBASE)
+ testl %ecx,%ecx # object null?
+ je common_errNullObject # object was null
+ movl rINST_FULL,(%ecx,%eax) # obj.field <- v[A](8/16/32 bits)
+ GET_GLUE(%eax)
+ testl rINST_FULL,rINST_FULL # stored a NULL?
+ movl offGlue_cardTable(%eax),%eax # get card table base
+ FETCH_INST_WORD(2)
+ je 1f # skip card mark if null store
+ shrl $GC_CARD_SHIFT,%ecx # object head to card number
+ movb %al,(%eax,%ecx) # mark card using object head
+1:
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+/* continuation for OP_SGET_OBJECT_VOLATILE */
+
+ /*
+ * Go resolve the field
+ */
+.LOP_SGET_OBJECT_VOLATILE_resolve:
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_method(%ecx),%ecx # ecx<- current method
+ EXPORT_PC() # could throw, need to export
+ movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+ SPILL(rPC)
+ movl %eax,OUT_ARG1(%esp)
+ movl %ecx,OUT_ARG0(%esp)
+ call dvmResolveStaticField # eax<- resolved StaticField ptr
+ UNSPILL(rPC)
+ testl %eax,%eax
+ jne .LOP_SGET_OBJECT_VOLATILE_finish # success, continue
+ jmp common_exceptionThrown # no, handle exception
+
+/* continuation for OP_SPUT_OBJECT_VOLATILE */
+
+
+.LOP_SPUT_OBJECT_VOLATILE_continue:
+ movl %ecx,offStaticField_value(%eax) # do the store
+ testl %ecx,%ecx # stored null object ptr?
+ FETCH_INST_WORD(2)
+ je 1f # skip card mark if null
+ GET_GLUE(%ecx)
+ movl offField_clazz(%eax),%eax # eax<- field->clazz
+ movl offGlue_cardTable(%ecx),%ecx # get card table base
+ shrl $GC_CARD_SHIFT,%eax # head to card number
+ movb %cl,(%ecx,%eax) # mark card
+1:
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+.LOP_SPUT_OBJECT_VOLATILE_resolve:
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_method(%ecx),%ecx # ecx<- current method
+ EXPORT_PC() # could throw, need to export
+ movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+ SPILL(rPC)
+ movl %eax,OUT_ARG1(%esp)
+ movl %ecx,OUT_ARG0(%esp)
+ call dvmResolveStaticField # eax<- resolved StaticField ptr
+ UNSPILL(rPC)
+ testl %eax,%eax
+ jne .LOP_SPUT_OBJECT_VOLATILE_finish # success, continue
+ jmp common_exceptionThrown # no, handle exception
+
+ .size dvmAsmSisterStart, .-dvmAsmSisterStart
+ .global dvmAsmSisterEnd
+dvmAsmSisterEnd:
+
+/* File: x86/entry.S */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+ .text
+ .global dvmMterpStdRun
+ .type dvmMterpStdRun, %function
+/*
+ * bool dvmMterpStdRun(MterpGlue* glue)
+ *
+ * Interpreter entry point. Returns changeInterp.
+ *
+ */
+dvmMterpStdRun:
+ push %ebp
+ movl %esp,%ebp
+ push %edi
+ push %esi
+ push %ebx
+
+/* at this point, stack is misaligned by 1 word
+ We're allocating spill space for 6 words, plus
+ outgoing argument (5 words) and local variables
+ (4 words) - 15 words or 60 bytes total. See
+ diagram in header.S
+*/
+ subl $60,%esp
+
+/* Set up "named" registers */
+ movl IN_ARG0(%ebp),%ecx
+ movl %ecx,rGLUE_SPILL(%ebp)
+ LOAD_PC_FROM_GLUE(%ecx)
+ LOAD_FP_FROM_GLUE(%ecx)
+ movl $dvmAsmInstructionStart,rIBASE
+
+/* Remember %esp for future "longjmp" */
+ movl %esp,offGlue_bailPtr(%ecx)
+
+/* How to start? */
+ movb offGlue_entryPoint(%ecx),%al
+
+/* Normal start? */
+ cmpb $kInterpEntryInstr,%al
+ jne .Lnot_instr
+
+ /* Normal case: start executing the instruction at rPC */
+ FETCH_INST()
+ GOTO_NEXT
+
+.Lnot_instr:
+ /* Reset to normal case */
+ movb $kInterpEntryInstr,offGlue_entryPoint(%ecx)
+ cmpb $kInterpEntryReturn,%al
+ je common_returnFromMethod
+ cmpb $kInterpEntryThrow,%al
+ je common_exceptionThrown
+ movzx %al,%eax
+ movl %eax,OUT_ARG1(%esp)
+ movl $.LstrBadEntryPoint,OUT_ARG0(%esp)
+ call printf
+ call dvmAbort
+ /* Not reached */
+
+
+ .global dvmMterpStdBail
+ .type dvmMterpStdBail, %function
+/*
+ * void dvmMterpStdBail(MterpGlue* glue, bool changeInterp)
+ *
+ * Restore the stack pointer and PC from the save point established on entry.
+ * This is essentially the same as a longjmp, but should be cheaper. The
+ * last instruction causes us to return to whoever called dvmMterpStdRun.
+ *
+ * We're not going to build a standard frame here, so the arg accesses will
+ * look a little strange.
+ *
+ * On entry:
+ * esp+4 (arg0) MterpGlue* glue
+ * esp+8 (arg1) bool changeInterp
+ */
+dvmMterpStdBail:
+ movl 4(%esp),%ecx # grab glue
+ movl 8(%esp),%eax # changeInterp to return reg
+ movl offGlue_bailPtr(%ecx),%esp # Stack back to normal
+ addl $60,%esp # Strip dvmMterpStdRun's frame
+ pop %ebx
+ pop %esi
+ pop %edi
+ pop %ebp
+ ret # return to dvmMterpStdRun's caller
+
+
+/*
+ * Strings
+ */
+ .section .rodata
+.LstrBadEntryPoint:
+ .asciz "Bad entry point %d\n"
+
+/* File: x86/footer.S */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Common subroutines and data.
+ */
+
+/*
+ * Common code when a backwards branch is taken
+ *
+ * On entry:
+ * ebx (a.k.a. rINST_FULL) -> PC adjustment in 16-bit words
+ */
+common_backwardBranch:
+ GET_GLUE(%ecx)
+ call common_periodicChecks # Note: expects rPC to be preserved
+ ADVANCE_PC_INDEXED(rINST_FULL)
+ FETCH_INST()
+ GOTO_NEXT
+
+
+
+/*
+ * Common code for method invocation with range.
+ *
+ * On entry:
+ * eax = Method* methodToCall
+ * rINST trashed, must reload
+ */
+
+common_invokeMethodRange:
+.LinvokeNewRange:
+
+ /*
+ * prepare to copy args to "outs" area of current frame
+ */
+
+ movzbl 1(rPC),rINST_FULL # rINST_FULL<- AA
+ movzwl 4(rPC), %ecx # %ecx<- CCCC
+ SPILL(rPC)
+ SAVEAREA_FROM_FP(%edx,rFP) # %edx<- &StackSaveArea
+ test rINST_FULL, rINST_FULL
+ movl rINST_FULL, LOCAL0_OFFSET(%ebp) # LOCAL0_OFFSET(%ebp)<- AA
+ jz .LinvokeArgsDone # no args; jump to args done
+
+
+ /*
+ * %eax=methodToCall, %ecx=CCCC, LOCAL0_OFFSET(%ebp)=count, %edx=&outs (&stackSaveArea)
+ * (very few methods have > 10 args; could unroll for common cases)
+ */
+
+ movl %ebx, LOCAL1_OFFSET(%ebp) # LOCAL1_OFFSET(%ebp)<- save %ebx
+ lea (rFP, %ecx, 4), %ecx # %ecx<- &vCCCC
+ shll $2, LOCAL0_OFFSET(%ebp) # LOCAL0_OFFSET(%ebp)<- offset
+ subl LOCAL0_OFFSET(%ebp), %edx # %edx<- update &outs
+ shrl $2, LOCAL0_OFFSET(%ebp) # LOCAL0_OFFSET(%ebp)<- offset
+1:
+ movl (%ecx), %ebx # %ebx<- vCCCC
+ lea 4(%ecx), %ecx # %ecx<- &vCCCC++
+ subl $1, LOCAL0_OFFSET(%ebp) # LOCAL0_OFFSET<- LOCAL0_OFFSET--
+ movl %ebx, (%edx) # *outs<- vCCCC
+ lea 4(%edx), %edx # outs++
+ jne 1b # loop if count (LOCAL0_OFFSET(%ebp)) not zero
+ movl LOCAL1_OFFSET(%ebp), %ebx # %ebx<- restore %ebx
+ jmp .LinvokeArgsDone # continue
+
+ /*
+ * %eax is "Method* methodToCall", the method we're trying to call
+ * prepare to copy args to "outs" area of current frame
+ */
+
+common_invokeMethodNoRange:
+.LinvokeNewNoRange:
+ movzbl 1(rPC),rINST_FULL # rINST_FULL<- BA
+ SPILL(rPC)
+ movl rINST_FULL, LOCAL0_OFFSET(%ebp) # LOCAL0_OFFSET(%ebp)<- BA
+ shrl $4, LOCAL0_OFFSET(%ebp) # LOCAL0_OFFSET(%ebp)<- B
+ je .LinvokeArgsDone # no args; jump to args done
+ movzwl 4(rPC), %ecx # %ecx<- GFED
+ SAVEAREA_FROM_FP(%edx,rFP) # %edx<- &StackSaveArea
+
+ /*
+ * %eax=methodToCall, %ecx=GFED, LOCAL0_OFFSET(%ebp)=count, %edx=outs
+ */
+
+.LinvokeNonRange:
+ cmp $2, LOCAL0_OFFSET(%ebp) # compare LOCAL0_OFFSET(%ebp) to 2
+ movl %ecx, LOCAL1_OFFSET(%ebp) # LOCAL1_OFFSET(%ebp)<- GFED
+ jl 1f # handle 1 arg
+ je 2f # handle 2 args
+ cmp $4, LOCAL0_OFFSET(%ebp) # compare LOCAL0_OFFSET(%ebp) to 4
+ jl 3f # handle 3 args
+ je 4f # handle 4 args
+5:
+ andl $15, rINST_FULL # rINST<- A
+ lea -4(%edx), %edx # %edx<- update &outs; &outs--
+ movl (rFP, rINST_FULL, 4), %ecx # %ecx<- vA
+ movl %ecx, (%edx) # *outs<- vA
+ movl LOCAL1_OFFSET(%ebp), %ecx # %ecx<- GFED
+4:
+ shr $12, %ecx # %ecx<- G
+ lea -4(%edx), %edx # %edx<- update &outs; &outs--
+ movl (rFP, %ecx, 4), %ecx # %ecx<- vG
+ movl %ecx, (%edx) # *outs<- vG
+ movl LOCAL1_OFFSET(%ebp), %ecx # %ecx<- GFED
+3:
+ and $0x0f00, %ecx # %ecx<- 0F00
+ shr $8, %ecx # %ecx<- F
+ lea -4(%edx), %edx # %edx<- update &outs; &outs--
+ movl (rFP, %ecx, 4), %ecx # %ecx<- vF
+ movl %ecx, (%edx) # *outs<- vF
+ movl LOCAL1_OFFSET(%ebp), %ecx # %ecx<- GFED
+2:
+ and $0x00f0, %ecx # %ecx<- 00E0
+ shr $4, %ecx # %ecx<- E
+ lea -4(%edx), %edx # %edx<- update &outs; &outs--
+ movl (rFP, %ecx, 4), %ecx # %ecx<- vE
+ movl %ecx, (%edx) # *outs<- vE
+ movl LOCAL1_OFFSET(%ebp), %ecx # %ecx<- GFED
+1:
+ and $0x000f, %ecx # %ecx<- 000D
+ movl (rFP, %ecx, 4), %ecx # %ecx<- vD
+ movl %ecx, -4(%edx) # *--outs<- vD
+0:
+
+ /*
+ * %eax is "Method* methodToCall", the method we're trying to call
+ * find space for the new stack frame, check for overflow
+ */
+
+.LinvokeArgsDone:
+ movzwl offMethod_registersSize(%eax), %edx # %edx<- methodToCall->regsSize
+ movzwl offMethod_outsSize(%eax), %ecx # %ecx<- methodToCall->outsSize
+ movl %eax, LOCAL0_OFFSET(%ebp) # LOCAL0_OFFSET<- methodToCall
+ shl $2, %edx # %edx<- update offset
+ SAVEAREA_FROM_FP(%eax,rFP) # %eax<- &StackSaveArea
+ subl %edx, %eax # %eax<- newFP; (old savearea - regsSize)
+ GET_GLUE(%edx) # %edx<- pMterpGlue
+ movl %eax, LOCAL1_OFFSET(%ebp) # LOCAL1_OFFSET(%ebp)<- &outs
+ subl $sizeofStackSaveArea, %eax # %eax<- newSaveArea (stack save area using newFP)
+ movl offGlue_interpStackEnd(%edx), %edx # %edx<- glue->interpStackEnd
+ movl %edx, LOCAL2_OFFSET(%ebp) # LOCAL2_OFFSET<- glue->interpStackEnd
+ shl $2, %ecx # %ecx<- update offset for outsSize
+ movl %eax, %edx # %edx<- newSaveArea
+ sub %ecx, %eax # %eax<- bottom; (newSaveArea - outsSize)
+ cmp LOCAL2_OFFSET(%ebp), %eax # compare interpStackEnd and bottom
+ movl LOCAL0_OFFSET(%ebp), %eax # %eax<- restore methodToCall
+ jl .LstackOverflow # handle frame overflow
+
+ /*
+ * set up newSaveArea
+ */
+
+#ifdef EASY_GDB
+ SAVEAREA_FROM_FP(%ecx,rFP) # %ecx<- &StackSaveArea
+ movl %ecx, offStackSaveArea_prevSave(%edx) # newSaveArea->prevSave<- &outs
+#endif
+ movl rFP, offStackSaveArea_prevFrame(%edx) # newSaveArea->prevFrame<- rFP
+ movl rPC_SPILL(%ebp), %ecx
+ movl %ecx, offStackSaveArea_savedPc(%edx) # newSaveArea->savedPc<- rPC
+ testl $ACC_NATIVE, offMethod_accessFlags(%eax) # check for native call
+ movl %eax, offStackSaveArea_method(%edx) # newSaveArea->method<- method to call
+ jne .LinvokeNative # handle native call
+
+ /*
+ * Update "glue" values for the new method
+ * %eax=methodToCall, LOCAL1_OFFSET(%ebp)=newFp
+ */
+
+ movl offMethod_clazz(%eax), %edx # %edx<- method->clazz
+ GET_GLUE(%ecx) # %ecx<- pMterpGlue
+ movl offClassObject_pDvmDex(%edx), %edx # %edx<- method->clazz->pDvmDex
+ movl %eax, offGlue_method(%ecx) # glue->method<- methodToCall
+ movl %edx, offGlue_methodClassDex(%ecx) # glue->methodClassDex<- method->clazz->pDvmDex
+ movl offMethod_insns(%eax), rPC # rPC<- methodToCall->insns
+ movl offGlue_self(%ecx), %eax # %eax<- glue->self
+ movl LOCAL1_OFFSET(%ebp), rFP # rFP<- newFP
+ movl rFP, offThread_curFrame(%eax) # glue->self->curFrame<- newFP
+ FETCH_INST()
+ GOTO_NEXT # jump to methodToCall->insns
+
+ /*
+ * Prep for the native call
+ * %eax=methodToCall, LOCAL1_OFFSET(%ebp)=newFP, %edx=newSaveArea
+ */
+
+.LinvokeNative:
+ GET_GLUE(%ecx) # %ecx<- pMterpGlue
+ movl %eax, OUT_ARG1(%esp) # push parameter methodToCall
+ movl offGlue_self(%ecx), %ecx # %ecx<- glue->self
+ movl offThread_jniLocal_topCookie(%ecx), %eax # %eax<- self->localRef->...
+ movl %eax, offStackSaveArea_localRefCookie(%edx) # newSaveArea->localRefCookie<- top
+ movl %edx, OUT_ARG4(%esp) # save newSaveArea
+ movl LOCAL1_OFFSET(%ebp), %edx # %edx<- newFP
+ movl %edx, offThread_curFrame(%ecx) # glue->self->curFrame<- newFP
+ movl %ecx, OUT_ARG3(%esp) # save glue->self
+ movl %ecx, OUT_ARG2(%esp) # push parameter glue->self
+ GET_GLUE(%ecx) # %ecx<- pMterpGlue
+ movl OUT_ARG1(%esp), %eax # %eax<- methodToCall
+ lea offGlue_retval(%ecx), %ecx # %ecx<- &retval
+ movl %ecx, OUT_ARG0(%esp) # push parameter pMterpGlue
+ push %edx # push parameter newFP
+
+ call *offMethod_nativeFunc(%eax) # call methodToCall->nativeFunc
+ lea 4(%esp), %esp
+ movl OUT_ARG4(%esp), %ecx # %ecx<- newSaveArea
+ movl OUT_ARG3(%esp), %eax # %eax<- glue->self
+ movl offStackSaveArea_localRefCookie(%ecx), %edx # %edx<- old top
+ cmp $0, offThread_exception(%eax) # check for exception
+ movl rFP, offThread_curFrame(%eax) # glue->self->curFrame<- rFP
+ movl %edx, offThread_jniLocal_topCookie(%eax) # new top <- old top
+ UNSPILL(rPC)
+ jne common_exceptionThrown # handle exception
+ FETCH_INST_WORD(3)
+ ADVANCE_PC(3)
+ GOTO_NEXT # jump to next instruction
+
+.LstackOverflow: # eax=methodToCall
+ movl %eax, OUT_ARG1(%esp) # push parameter methodToCall
+ GET_GLUE(%eax) # %eax<- pMterpGlue
+ movl offGlue_self(%eax), %eax # %eax<- glue->self
+ movl %eax, OUT_ARG0(%esp) # push parameter self
+ call dvmHandleStackOverflow # call: (Thread* self, Method* meth)
+ UNSPILL(rPC) # return: void
+ jmp common_exceptionThrown # handle exception
+
+
+/*
+ * Common invoke code (old-style).
+ * TUNING: Rewrite along lines of new armv5 code?
+ *
+ * On entry:
+ * eax = Method* methodToCall
+ * ecx = bool methodCallRange
+ * rINST trashed, must reload
+ */
+common_invokeOld:
+ movl %ecx,OUT_ARG1(%esp) # arg1<- methodCallRange
+ GET_GLUE(%ecx)
+ movzwl (rPC),rINST_FULL # recover rINST
+ movl %eax,OUT_ARG2(%esp) # arg2<- method
+ movzwl 4(rPC),%eax # eax<- GFED or CCCC
+ SAVE_PC_TO_GLUE(%ecx)
+ SAVE_FP_TO_GLUE(%ecx)
+ movzbl rINST_HI,rINST_FULL
+ movl rINST_FULL,OUT_ARG3(%esp)# arg3<- AA
+ movl %ecx,OUT_ARG0(%esp) # arg0<- GLUE
+ movl %eax,OUT_ARG4(%esp) # arg4<- GFED/CCCC
+ call dvmMterp_invokeMethod
+ jmp common_resumeAfterGlueCall
+
+
+/*
+ * Do we need the thread to be suspended or have debugger/profiling activity?
+ *
+ * On entry:
+ * ebx -> PC adjustment in 16-bit words (must be preserved)
+ * ecx -> GLUE pointer
+ * reentry type, e.g. kInterpEntryInstr stored in rGLUE->entryPoint
+ *
+ * Note: A call will normally kill %eax, rPC/%edx and %ecx. To
+ * streamline the normal case, this routine will preserve rPC and
+ * %ecx in addition to the normal caller save regs. The save/restore
+ * is a bit ugly, but will happen in the relatively uncommon path.
+ * TODO: Basic-block style Jit will need a hook here as well. Fold it into
+ * the suspendCount check so we can get both in 1 shot.
+ */
+common_periodicChecks:
+ movl offGlue_pSelfSuspendCount(%ecx),%eax # eax <- &suspendCount
+ cmpl $0,(%eax)
+ jne 1f
+
+6:
+ movl offGlue_pDebuggerActive(%ecx),%eax # eax <- &DebuggerActive
+ movl offGlue_pActiveProfilers(%ecx),%ecx # ecx <- &ActiveProfilers
+ testl %eax,%eax # debugger enabled?
+ je 2f
+ movzbl (%eax),%eax # get active count
+2:
+ orl (%ecx),%eax # eax <- debuggerActive | activeProfilers
+ GET_GLUE(%ecx) # restore rGLUE
+ jne 3f # one or both active - switch interp
+
+5:
+ ret
+
+ /* Check for suspend */
+1:
+ /* At this point, the return pointer to the caller of
+ * common_periodicChecks is on the top of stack. We need to preserve
+ * rPC(edx) and GLUE(ecx). We'll spill rPC, and reload GLUE.
+ * The outgoing profile is:
+ * bool dvmCheckSuspendPending(Thread* self)
+ * Because we reached here via a call, go ahead and build a new frame.
+ */
+ EXPORT_PC() # need for precise GC
+ movl offGlue_self(%ecx),%eax # eax<- glue->self
+ SPILL(rPC) # save edx
+ push %ebp
+ movl %esp,%ebp
+ subl $24,%esp
+ movl %eax,OUT_ARG0(%esp)
+ call dvmCheckSuspendPending
+ addl $24,%esp
+ pop %ebp
+ UNSPILL(rPC)
+ GET_GLUE(%ecx)
+
+ /*
+ * Need to check to see if debugger or profiler flags got set
+ * while we were suspended.
+ */
+ jmp 6b
+
+ /* Switch interpreters */
+ /* Note: %ebx contains the 16-bit word offset to be applied to rPC to
+ * "complete" the interpretation of backwards branches. In effect, we
+ * are completing the interpretation of the branch instruction here,
+ * and the new interpreter will resume interpretation at the branch
+ * target. However, a switch request recognized during the handling
+ * of a return from method instruction results in an immediate abort,
+ * and the new interpreter will resume by re-interpreting the return
+ * instruction.
+ */
+3:
+ leal (rPC,%ebx,2),rPC # adjust pc to show target
+ GET_GLUE(%ecx) # bail expect GLUE already loaded
+ movl $1,rINST_FULL # set changeInterp to true
+ jmp common_gotoBail
+
+
+/*
+ * Common code for handling a return instruction
+ */
+common_returnFromMethod:
+ GET_GLUE(%ecx)
+ /* Set entry mode in case we bail */
+ movb $kInterpEntryReturn,offGlue_entryPoint(%ecx)
+ xorl rINST_FULL,rINST_FULL # zero offset in case we switch interps
+ call common_periodicChecks # Note: expects %ecx to be preserved
+
+ SAVEAREA_FROM_FP(%eax,rFP) # eax<- saveArea (old)
+ movl offStackSaveArea_prevFrame(%eax),rFP # rFP<- prevFrame
+ movl (offStackSaveArea_method-sizeofStackSaveArea)(rFP),rINST_FULL
+ cmpl $0,rINST_FULL # break?
+ je common_gotoBail # break frame, bail out completely
+
+ movl offStackSaveArea_savedPc(%eax),rPC # pc<- saveArea->savedPC
+ movl offGlue_self(%ecx),%eax # eax<- self
+ movl rINST_FULL,offGlue_method(%ecx) # glue->method = newSave->meethod
+ movl rFP,offThread_curFrame(%eax) # self->curFrame = fp
+ movl offMethod_clazz(rINST_FULL),%eax # eax<- method->clazz
+ FETCH_INST_WORD(3)
+ movl offClassObject_pDvmDex(%eax),%eax # eax<- method->clazz->pDvmDex
+ ADVANCE_PC(3)
+ movl %eax,offGlue_methodClassDex(%ecx)
+ /* not bailing - restore entry mode to default */
+ movb $kInterpEntryInstr,offGlue_entryPoint(%ecx)
+ GOTO_NEXT
+
+/*
+ * Prepare to strip the current frame and "longjump" back to caller of
+ * dvmMterpStdRun.
+ *
+ * on entry:
+ * rINST_FULL holds changeInterp
+ * ecx holds glue pointer
+ *
+ * expected profile: dvmMterpStdBail(MterpGlue *glue, bool changeInterp)
+ */
+common_gotoBail:
+ SAVE_PC_TO_GLUE(%ecx) # export state to glue
+ SAVE_FP_TO_GLUE(%ecx)
+ movl %ecx,OUT_ARG0(%esp) # glue in arg0
+ movl rINST_FULL,OUT_ARG1(%esp) # changeInterp in arg1
+ call dvmMterpStdBail # bail out....
+
+
+/*
+ * After returning from a "glued" function, pull out the updated values
+ * and start executing at the next instruction.
+ */
+ common_resumeAfterGlueCall:
+ GET_GLUE(%ecx)
+ LOAD_PC_FROM_GLUE(%ecx)
+ LOAD_FP_FROM_GLUE(%ecx)
+ FETCH_INST()
+ GOTO_NEXT
+
+/*
+ * Integer divide or mod by zero
+ */
+common_errDivideByZero:
+ EXPORT_PC()
+ movl $.LstrArithmeticException,%eax
+ movl %eax,OUT_ARG0(%esp)
+ movl $.LstrDivideByZero,%eax
+ movl %eax,OUT_ARG1(%esp)
+ SPILL(rPC)
+ call dvmThrowException
+ UNSPILL(rPC)
+ jmp common_exceptionThrown
+
+/*
+ * Attempt to allocate an array with a negative size.
+ */
+common_errNegativeArraySize:
+ EXPORT_PC()
+ movl $.LstrNegativeArraySizeException,%eax
+ movl %eax,OUT_ARG0(%esp)
+ xorl %eax,%eax
+ movl %eax,OUT_ARG1(%esp)
+ SPILL(rPC)
+ call dvmThrowException
+ UNSPILL(rPC)
+ jmp common_exceptionThrown
+
+/*
+ * Attempt to allocate an array with a negative size.
+ */
+common_errNoSuchMethod:
+
+ EXPORT_PC()
+ movl $.LstrNoSuchMethodError,%eax
+ movl %eax,OUT_ARG0(%esp)
+ xorl %eax,%eax
+ movl %eax,OUT_ARG1(%esp)
+ SPILL(rPC)
+ call dvmThrowException
+ UNSPILL(rPC)
+ jmp common_exceptionThrown
+
+/*
+ * Hit a null object when we weren't expecting one. Export the PC, throw a
+ * NullPointerException and goto the exception processing code.
+ */
+common_errNullObject:
+ EXPORT_PC()
+ movl $.LstrNullPointerException,%eax
+ movl %eax,OUT_ARG0(%esp)
+ xorl %eax,%eax
+ movl %eax,OUT_ARG1(%esp)
+ SPILL(rPC)
+ call dvmThrowException
+ UNSPILL(rPC)
+ jmp common_exceptionThrown
+
+/*
+ * Array index exceeds max.
+ */
+common_errArrayIndex:
+ EXPORT_PC()
+ movl $.LstrArrayIndexException,%eax
+ movl %eax,OUT_ARG0(%esp)
+ xorl %eax,%eax
+ movl %eax,OUT_ARG1(%esp)
+ SPILL(rPC)
+ call dvmThrowException
+ UNSPILL(rPC)
+ jmp common_exceptionThrown
+/*
+ * Invalid array value.
+ */
+common_errArrayStore:
+ EXPORT_PC()
+ movl $.LstrArrayStoreException,%eax
+ movl %eax,OUT_ARG0(%esp)
+ xorl %eax,%eax
+ movl %eax,OUT_ARG1(%esp)
+ SPILL(rPC)
+ call dvmThrowException
+ UNSPILL(rPC)
+ jmp common_exceptionThrown
+
+/*
+ * Somebody has thrown an exception. Handle it.
+ *
+ * If the exception processing code returns to us (instead of falling
+ * out of the interpreter), continue with whatever the next instruction
+ * now happens to be.
+ *
+ * This does not return.
+ */
+common_exceptionThrown:
+ GET_GLUE(%ecx)
+ SAVE_PC_TO_GLUE(%ecx)
+ SAVE_FP_TO_GLUE(%ecx)
+ movl %ecx,OUT_ARG0(%esp)
+ call dvmMterp_exceptionThrown
+ jmp common_resumeAfterGlueCall
+
+common_abort:
+ movl $0xdeadf00d,%eax
+ call *%eax
+
+
+/*
+ * Strings
+ */
+
+ .section .rodata
+.LstrNullPointerException:
+ .asciz "Ljava/lang/NullPointerException;"
+.LstrArithmeticException:
+ .asciz "Ljava/lang/ArithmeticException;"
+.LstrDivideByZero:
+ .asciz "divide by zero"
+.LstrArrayIndexException:
+ .asciz "Ljava/lang/ArrayIndexOutOfBoundsException;"
+.LstrArrayStoreException:
+ .asciz "Ljava/lang/ArrayStoreException;"
+.LstrNegativeArraySizeException:
+ .asciz "Ljava/lang/NegativeArraySizeException;"
+.LstrInstantiationError:
+ .asciz "Ljava/lang/InstantiationError;"
+.LstrClassCastException:
+ .asciz "Ljava/lang/ClassCastException;"
+.LstrNoSuchMethodError:
+ .asciz "Ljava/lang/NoSuchMethodError;"
+.LstrInternalError:
+ .asciz "Ljava/lang/InternalError;"
+.LstrFilledNewArrayNotImpl:
+ .asciz "filled-new-array only implemented for 'int'"
+
diff --git a/vm/mterp/out/InterpC-allstubs.c b/vm/mterp/out/InterpC-allstubs.c
new file mode 100644
index 0000000..ce6192c
--- /dev/null
+++ b/vm/mterp/out/InterpC-allstubs.c
@@ -0,0 +1,4132 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'allstubs'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: c/header.c */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* common includes */
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "mterp/Mterp.h"
+#include <math.h> // needed for fmod, fmodf
+#include "mterp/common/FindInterface.h"
+
+/*
+ * Configuration defines. These affect the C implementations, i.e. the
+ * portable interpreter(s) and C stubs.
+ *
+ * Some defines are controlled by the Makefile, e.g.:
+ * WITH_INSTR_CHECKS
+ * WITH_TRACKREF_CHECKS
+ * EASY_GDB
+ * NDEBUG
+ *
+ * If THREADED_INTERP is not defined, we use a classic "while true / switch"
+ * interpreter. If it is defined, then the tail end of each instruction
+ * handler fetches the next instruction and jumps directly to the handler.
+ * This increases the size of the "Std" interpreter by about 10%, but
+ * provides a speedup of about the same magnitude.
+ *
+ * There's a "hybrid" approach that uses a goto table instead of a switch
+ * statement, avoiding the "is the opcode in range" tests required for switch.
+ * The performance is close to the threaded version, and without the 10%
+ * size increase, but the benchmark results are off enough that it's not
+ * worth adding as a third option.
+ */
+#define THREADED_INTERP /* threaded vs. while-loop interpreter */
+
+#ifdef WITH_INSTR_CHECKS /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * ARM EABI requires 64-bit alignment for access to 64-bit data types. We
+ * can't just use pointers to copy 64-bit values out of our interpreted
+ * register set, because gcc will generate ldrd/strd.
+ *
+ * The __UNION version copies data in and out of a union. The __MEMCPY
+ * version uses a memcpy() call to do the transfer; gcc is smart enough to
+ * not actually call memcpy(). The __UNION version is very bad on ARM;
+ * it only uses one more instruction than __MEMCPY, but for some reason
+ * gcc thinks it needs separate storage for every instance of the union.
+ * On top of that, it feels the need to zero them out at the start of the
+ * method. Net result is we zero out ~700 bytes of stack space at the top
+ * of the interpreter using ARM STM instructions.
+ */
+#if defined(__ARM_EABI__)
+//# define NO_UNALIGN_64__UNION
+# define NO_UNALIGN_64__MEMCPY
+#endif
+
+//#define LOG_INSTR /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Keep a tally of accesses to fields. Currently only works if full DEX
+ * optimization is disabled.
+ */
+#ifdef PROFILE_FIELD_ACCESS
+# define UPDATE_FIELD_GET(_field) { (_field)->gets++; }
+# define UPDATE_FIELD_PUT(_field) { (_field)->puts++; }
+#else
+# define UPDATE_FIELD_GET(_field) ((void)0)
+# define UPDATE_FIELD_PUT(_field) ((void)0)
+#endif
+
+/*
+ * Export another copy of the PC on every instruction; this is largely
+ * redundant with EXPORT_PC and the debugger code. This value can be
+ * compared against what we have stored on the stack with EXPORT_PC to
+ * help ensure that we aren't missing any export calls.
+ */
+#if WITH_EXTRA_GC_CHECKS > 1
+# define EXPORT_EXTRA_PC() (self->currentPc2 = pc)
+#else
+# define EXPORT_EXTRA_PC()
+#endif
+
+/*
+ * Adjust the program counter. "_offset" is a signed int, in 16-bit units.
+ *
+ * Assumes the existence of "const u2* pc" and "const u2* curMethod->insns".
+ *
+ * We don't advance the program counter until we finish an instruction or
+ * branch, because we do want to have to unroll the PC if there's an
+ * exception.
+ */
+#ifdef CHECK_BRANCH_OFFSETS
+# define ADJUST_PC(_offset) do { \
+ int myoff = _offset; /* deref only once */ \
+ if (pc + myoff < curMethod->insns || \
+ pc + myoff >= curMethod->insns + dvmGetMethodInsnsSize(curMethod)) \
+ { \
+ char* desc; \
+ desc = dexProtoCopyMethodDescriptor(&curMethod->prototype); \
+ LOGE("Invalid branch %d at 0x%04x in %s.%s %s\n", \
+ myoff, (int) (pc - curMethod->insns), \
+ curMethod->clazz->descriptor, curMethod->name, desc); \
+ free(desc); \
+ dvmAbort(); \
+ } \
+ pc += myoff; \
+ EXPORT_EXTRA_PC(); \
+ } while (false)
+#else
+# define ADJUST_PC(_offset) do { \
+ pc += _offset; \
+ EXPORT_EXTRA_PC(); \
+ } while (false)
+#endif
+
+/*
+ * If enabled, log instructions as we execute them.
+ */
+#ifdef LOG_INSTR
+# define ILOGD(...) ILOG(LOG_DEBUG, __VA_ARGS__)
+# define ILOGV(...) ILOG(LOG_VERBOSE, __VA_ARGS__)
+# define ILOG(_level, ...) do { \
+ char debugStrBuf[128]; \
+ snprintf(debugStrBuf, sizeof(debugStrBuf), __VA_ARGS__); \
+ if (curMethod != NULL) \
+ LOG(_level, LOG_TAG"i", "%-2d|%04x%s\n", \
+ self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+ else \
+ LOG(_level, LOG_TAG"i", "%-2d|####%s\n", \
+ self->threadId, debugStrBuf); \
+ } while(false)
+void dvmDumpRegs(const Method* method, const u4* framePtr, bool inOnly);
+# define DUMP_REGS(_meth, _frame, _inOnly) dvmDumpRegs(_meth, _frame, _inOnly)
+static const char kSpacing[] = " ";
+#else
+# define ILOGD(...) ((void)0)
+# define ILOGV(...) ((void)0)
+# define DUMP_REGS(_meth, _frame, _inOnly) ((void)0)
+#endif
+
+/* get a long from an array of u4 */
+static inline s8 getLongFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { s8 ll; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.parts[0] = ptr[0];
+ conv.parts[1] = ptr[1];
+ return conv.ll;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ s8 val;
+ memcpy(&val, &ptr[idx], 8);
+ return val;
+#else
+ return *((s8*) &ptr[idx]);
+#endif
+}
+
+/* store a long into an array of u4 */
+static inline void putLongToArray(u4* ptr, int idx, s8 val)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { s8 ll; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.ll = val;
+ ptr[0] = conv.parts[0];
+ ptr[1] = conv.parts[1];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ memcpy(&ptr[idx], &val, 8);
+#else
+ *((s8*) &ptr[idx]) = val;
+#endif
+}
+
+/* get a double from an array of u4 */
+static inline double getDoubleFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { double d; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.parts[0] = ptr[0];
+ conv.parts[1] = ptr[1];
+ return conv.d;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ double dval;
+ memcpy(&dval, &ptr[idx], 8);
+ return dval;
+#else
+ return *((double*) &ptr[idx]);
+#endif
+}
+
+/* store a double into an array of u4 */
+static inline void putDoubleToArray(u4* ptr, int idx, double dval)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { double d; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.d = dval;
+ ptr[0] = conv.parts[0];
+ ptr[1] = conv.parts[1];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ memcpy(&ptr[idx], &dval, 8);
+#else
+ *((double*) &ptr[idx]) = dval;
+#endif
+}
+
+/*
+ * If enabled, validate the register number on every access. Otherwise,
+ * just do an array access.
+ *
+ * Assumes the existence of "u4* fp".
+ *
+ * "_idx" may be referenced more than once.
+ */
+#ifdef CHECK_REGISTER_INDICES
+# define GET_REGISTER(_idx) \
+ ( (_idx) < curMethod->registersSize ? \
+ (fp[(_idx)]) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER(_idx, _val) \
+ ( (_idx) < curMethod->registersSize ? \
+ (fp[(_idx)] = (u4)(_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_AS_OBJECT(_idx) ((Object *)GET_REGISTER(_idx))
+# define SET_REGISTER_AS_OBJECT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_INT(_idx) ((s4) GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ getLongFromArray(fp, (_idx)) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER_WIDE(_idx, _val) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ putLongToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_FLOAT(_idx) \
+ ( (_idx) < curMethod->registersSize ? \
+ (*((float*) &fp[(_idx)])) : (assert(!"bad reg"),1969.0f) )
+# define SET_REGISTER_FLOAT(_idx, _val) \
+ ( (_idx) < curMethod->registersSize ? \
+ (*((float*) &fp[(_idx)]) = (_val)) : (assert(!"bad reg"),1969.0f) )
+# define GET_REGISTER_DOUBLE(_idx) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ getDoubleFromArray(fp, (_idx)) : (assert(!"bad reg"),1969.0) )
+# define SET_REGISTER_DOUBLE(_idx, _val) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ putDoubleToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969.0) )
+#else
+# define GET_REGISTER(_idx) (fp[(_idx)])
+# define SET_REGISTER(_idx, _val) (fp[(_idx)] = (_val))
+# define GET_REGISTER_AS_OBJECT(_idx) ((Object*) fp[(_idx)])
+# define SET_REGISTER_AS_OBJECT(_idx, _val) (fp[(_idx)] = (u4)(_val))
+# define GET_REGISTER_INT(_idx) ((s4)GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) getLongFromArray(fp, (_idx))
+# define SET_REGISTER_WIDE(_idx, _val) putLongToArray(fp, (_idx), (_val))
+# define GET_REGISTER_FLOAT(_idx) (*((float*) &fp[(_idx)]))
+# define SET_REGISTER_FLOAT(_idx, _val) (*((float*) &fp[(_idx)]) = (_val))
+# define GET_REGISTER_DOUBLE(_idx) getDoubleFromArray(fp, (_idx))
+# define SET_REGISTER_DOUBLE(_idx, _val) putDoubleToArray(fp, (_idx), (_val))
+#endif
+
+/*
+ * Get 16 bits from the specified offset of the program counter. We always
+ * want to load 16 bits at a time from the instruction stream -- it's more
+ * efficient than 8 and won't have the alignment problems that 32 might.
+ *
+ * Assumes existence of "const u2* pc".
+ */
+#define FETCH(_offset) (pc[(_offset)])
+
+/*
+ * Extract instruction byte from 16-bit fetch (_inst is a u2).
+ */
+#define INST_INST(_inst) ((_inst) & 0xff)
+
+/*
+ * Replace the opcode (used when handling breakpoints). _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
+ * Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
+ */
+#define INST_A(_inst) (((_inst) >> 8) & 0x0f)
+#define INST_B(_inst) ((_inst) >> 12)
+
+/*
+ * Get the 8-bit "vAA" 8-bit register index from the instruction word.
+ * (_inst is u2)
+ */
+#define INST_AA(_inst) ((_inst) >> 8)
+
+/*
+ * The current PC must be available to Throwable constructors, e.g.
+ * those created by dvmThrowException(), so that the exception stack
+ * trace can be generated correctly. If we don't do this, the offset
+ * within the current method won't be shown correctly. See the notes
+ * in Exception.c.
+ *
+ * This is also used to determine the address for precise GC.
+ *
+ * Assumes existence of "u4* fp" and "const u2* pc".
+ */
+#define EXPORT_PC() (SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc)
+
+/*
+ * Determine if we need to switch to a different interpreter. "_current"
+ * is either INTERP_STD or INTERP_DBG. It should be fixed for a given
+ * interpreter generation file, which should remove the outer conditional
+ * from the following.
+ *
+ * If we're building without debug and profiling support, we never switch.
+ */
+#if defined(WITH_JIT)
+# define NEED_INTERP_SWITCH(_current) ( \
+ (_current == INTERP_STD) ? \
+ dvmJitDebuggerOrProfilerActive() : !dvmJitDebuggerOrProfilerActive() )
+#else
+# define NEED_INTERP_SWITCH(_current) ( \
+ (_current == INTERP_STD) ? \
+ dvmDebuggerOrProfilerActive() : !dvmDebuggerOrProfilerActive() )
+#endif
+
+/*
+ * Check to see if "obj" is NULL. If so, throw an exception. Assumes the
+ * pc has already been exported to the stack.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler calls into
+ * something that could throw an exception (so we have already called
+ * EXPORT_PC at the top).
+ */
+static inline bool checkForNull(Object* obj)
+{
+ if (obj == NULL) {
+ dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+ return false;
+ }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+ if (!dvmIsValidObject(obj)) {
+ LOGE("Invalid object %p\n", obj);
+ dvmAbort();
+ }
+#endif
+#ifndef NDEBUG
+ if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+ /* probable heap corruption */
+ LOGE("Invalid object class %p (in %p)\n", obj->clazz, obj);
+ dvmAbort();
+ }
+#endif
+ return true;
+}
+
+/*
+ * Check to see if "obj" is NULL. If so, export the PC into the stack
+ * frame and throw an exception.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler doesn't do
+ * anything else that can throw an exception.
+ */
+static inline bool checkForNullExportPC(Object* obj, u4* fp, const u2* pc)
+{
+ if (obj == NULL) {
+ EXPORT_PC();
+ dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+ return false;
+ }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+ if (!dvmIsValidObject(obj)) {
+ LOGE("Invalid object %p\n", obj);
+ dvmAbort();
+ }
+#endif
+#ifndef NDEBUG
+ if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+ /* probable heap corruption */
+ LOGE("Invalid object class %p (in %p)\n", obj->clazz, obj);
+ dvmAbort();
+ }
+#endif
+ return true;
+}
+
+/* File: cstubs/stubdefs.c */
+/* this is a standard (no debug support) interpreter */
+#define INTERP_TYPE INTERP_STD
+#define CHECK_DEBUG_AND_PROF() ((void)0)
+# define CHECK_TRACKED_REFS() ((void)0)
+#define CHECK_JIT_BOOL() (false)
+#define CHECK_JIT_VOID()
+#define ABORT_JIT_TSELECT() ((void)0)
+
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...) \
+ void dvmMterp_##_target(MterpGlue* glue, ## __VA_ARGS__);
+
+#define GOTO_TARGET(_target, ...) \
+ void dvmMterp_##_target(MterpGlue* glue, ## __VA_ARGS__) { \
+ u2 ref, vsrc1, vsrc2, vdst; \
+ u2 inst = FETCH(0); \
+ const Method* methodToCall; \
+ StackSaveArea* debugSaveArea;
+
+#define GOTO_TARGET_END }
+
+/*
+ * Redefine what used to be local variable accesses into MterpGlue struct
+ * references. (These are undefined down in "footer.c".)
+ */
+#define retval glue->retval
+#define pc glue->pc
+#define fp glue->fp
+#define curMethod glue->method
+#define methodClassDex glue->methodClassDex
+#define self glue->self
+#define debugTrackedRefStart glue->debugTrackedRefStart
+
+/* ugh */
+#define STUB_HACK(x) x
+
+
+/*
+ * Opcode handler framing macros. Here, each opcode is a separate function
+ * that takes a "glue" argument and returns void. We can't declare
+ * these "static" because they may be called from an assembly stub.
+ */
+#define HANDLE_OPCODE(_op) \
+ void dvmMterp_##_op(MterpGlue* glue) { \
+ u2 ref, vsrc1, vsrc2, vdst; \
+ u2 inst = FETCH(0);
+
+#define OP_END }
+
+/*
+ * Like the "portable" FINISH, but don't reload "inst", and return to caller
+ * when done.
+ */
+#define FINISH(_offset) { \
+ ADJUST_PC(_offset); \
+ CHECK_DEBUG_AND_PROF(); \
+ CHECK_TRACKED_REFS(); \
+ return; \
+ }
+
+
+/*
+ * The "goto label" statements turn into function calls followed by
+ * return statements. Some of the functions take arguments, which in the
+ * portable interpreter are handled by assigning values to globals.
+ */
+
+#define GOTO_exceptionThrown() \
+ do { \
+ dvmMterp_exceptionThrown(glue); \
+ return; \
+ } while(false)
+
+#define GOTO_returnFromMethod() \
+ do { \
+ dvmMterp_returnFromMethod(glue); \
+ return; \
+ } while(false)
+
+#define GOTO_invoke(_target, _methodCallRange) \
+ do { \
+ dvmMterp_##_target(glue, _methodCallRange); \
+ return; \
+ } while(false)
+
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst) \
+ do { \
+ dvmMterp_invokeMethod(glue, _methodCallRange, _methodToCall, \
+ _vsrc1, _vdst); \
+ return; \
+ } while(false)
+
+/*
+ * As a special case, "goto bail" turns into a longjmp. Use "bail_switch"
+ * if we need to switch to the other interpreter upon our return.
+ */
+#define GOTO_bail() \
+ dvmMterpStdBail(glue, false);
+#define GOTO_bail_switch() \
+ dvmMterpStdBail(glue, true);
+
+/*
+ * Periodically check for thread suspension.
+ *
+ * While we're at it, see if a debugger has attached or the profiler has
+ * started. If so, switch to a different "goto" table.
+ */
+#define PERIODIC_CHECKS(_entryPoint, _pcadj) { \
+ if (dvmCheckSuspendQuick(self)) { \
+ EXPORT_PC(); /* need for precise GC */ \
+ dvmCheckSuspendPending(self); \
+ } \
+ if (NEED_INTERP_SWITCH(INTERP_TYPE)) { \
+ ADJUST_PC(_pcadj); \
+ glue->entryPoint = _entryPoint; \
+ LOGVV("threadid=%d: switch to STD ep=%d adj=%d\n", \
+ self->threadId, (_entryPoint), (_pcadj)); \
+ GOTO_bail_switch(); \
+ } \
+ }
+
+/* File: c/opcommon.c */
+/* forward declarations of goto targets */
+GOTO_TARGET_DECL(filledNewArray, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtual, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuper, bool methodCallRange);
+GOTO_TARGET_DECL(invokeInterface, bool methodCallRange);
+GOTO_TARGET_DECL(invokeDirect, bool methodCallRange);
+GOTO_TARGET_DECL(invokeStatic, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtualQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuperQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeMethod, bool methodCallRange, const Method* methodToCall,
+ u2 count, u2 regs);
+GOTO_TARGET_DECL(returnFromMethod);
+GOTO_TARGET_DECL(exceptionThrown);
+
+/*
+ * ===========================================================================
+ *
+ * What follows are opcode definitions shared between multiple opcodes with
+ * minor substitutions handled by the C pre-processor. These should probably
+ * use the mterp substitution mechanism instead, with the code here moved
+ * into common fragment files (like the asm "binop.S"), although it's hard
+ * to give up the C preprocessor in favor of the much simpler text subst.
+ *
+ * ===========================================================================
+ */
+
+#define HANDLE_NUMCONV(_opcode, _opname, _fromtype, _totype) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER##_totype(vdst, \
+ GET_REGISTER##_fromtype(vsrc1)); \
+ FINISH(1);
+
+#define HANDLE_FLOAT_TO_INT(_opcode, _opname, _fromvtype, _fromrtype, \
+ _tovtype, _tortype) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ { \
+ /* spec defines specific handling for +/- inf and NaN values */ \
+ _fromvtype val; \
+ _tovtype intMin, intMax, result; \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1); \
+ val = GET_REGISTER##_fromrtype(vsrc1); \
+ intMin = (_tovtype) 1 << (sizeof(_tovtype) * 8 -1); \
+ intMax = ~intMin; \
+ result = (_tovtype) val; \
+ if (val >= intMax) /* +inf */ \
+ result = intMax; \
+ else if (val <= intMin) /* -inf */ \
+ result = intMin; \
+ else if (val != val) /* NaN */ \
+ result = 0; \
+ else \
+ result = (_tovtype) val; \
+ SET_REGISTER##_tortype(vdst, result); \
+ } \
+ FINISH(1);
+
+#define HANDLE_INT_TO_SMALL(_opcode, _opname, _type) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|int-to-%s v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER(vdst, (_type) GET_REGISTER(vsrc1)); \
+ FINISH(1);
+
+/* NOTE: the comparison result is always a signed 4-byte integer */
+#define HANDLE_OP_CMPX(_opcode, _opname, _varType, _type, _nanVal) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ int result; \
+ u2 regs; \
+ _varType val1, val2; \
+ vdst = INST_AA(inst); \
+ regs = FETCH(1); \
+ vsrc1 = regs & 0xff; \
+ vsrc2 = regs >> 8; \
+ ILOGV("|cmp%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ val1 = GET_REGISTER##_type(vsrc1); \
+ val2 = GET_REGISTER##_type(vsrc2); \
+ if (val1 == val2) \
+ result = 0; \
+ else if (val1 < val2) \
+ result = -1; \
+ else if (val1 > val2) \
+ result = 1; \
+ else \
+ result = (_nanVal); \
+ ILOGV("+ result=%d\n", result); \
+ SET_REGISTER(vdst, result); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_IF_XX(_opcode, _opname, _cmp) \
+ HANDLE_OPCODE(_opcode /*vA, vB, +CCCC*/) \
+ vsrc1 = INST_A(inst); \
+ vsrc2 = INST_B(inst); \
+ if ((s4) GET_REGISTER(vsrc1) _cmp (s4) GET_REGISTER(vsrc2)) { \
+ int branchOffset = (s2)FETCH(1); /* sign-extended */ \
+ ILOGV("|if-%s v%d,v%d,+0x%04x", (_opname), vsrc1, vsrc2, \
+ branchOffset); \
+ ILOGV("> branch taken"); \
+ if (branchOffset < 0) \
+ PERIODIC_CHECKS(kInterpEntryInstr, branchOffset); \
+ FINISH(branchOffset); \
+ } else { \
+ ILOGV("|if-%s v%d,v%d,-", (_opname), vsrc1, vsrc2); \
+ FINISH(2); \
+ }
+
+#define HANDLE_OP_IF_XXZ(_opcode, _opname, _cmp) \
+ HANDLE_OPCODE(_opcode /*vAA, +BBBB*/) \
+ vsrc1 = INST_AA(inst); \
+ if ((s4) GET_REGISTER(vsrc1) _cmp 0) { \
+ int branchOffset = (s2)FETCH(1); /* sign-extended */ \
+ ILOGV("|if-%s v%d,+0x%04x", (_opname), vsrc1, branchOffset); \
+ ILOGV("> branch taken"); \
+ if (branchOffset < 0) \
+ PERIODIC_CHECKS(kInterpEntryInstr, branchOffset); \
+ FINISH(branchOffset); \
+ } else { \
+ ILOGV("|if-%s v%d,-", (_opname), vsrc1); \
+ FINISH(2); \
+ }
+
+#define HANDLE_UNOP(_opcode, _opname, _pfx, _sfx, _type) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER##_type(vdst, _pfx GET_REGISTER##_type(vsrc1) _sfx); \
+ FINISH(1);
+
+#define HANDLE_OP_X_INT(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER(vsrc1); \
+ secondVal = GET_REGISTER(vsrc2); \
+ if (secondVal == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && secondVal == -1) { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ /* non-div/rem case */ \
+ SET_REGISTER(vdst, \
+ (s4) GET_REGISTER(vsrc1) _op (s4) GET_REGISTER(vsrc2)); \
+ } \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_SHX_INT(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER(vdst, \
+ _cast GET_REGISTER(vsrc1) _op (GET_REGISTER(vsrc2) & 0x1f)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT16(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vA, vB, #+CCCC*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ vsrc2 = FETCH(1); \
+ ILOGV("|%s-int/lit16 v%d,v%d,#+0x%04x", \
+ (_opname), vdst, vsrc1, vsrc2); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, result; \
+ firstVal = GET_REGISTER(vsrc1); \
+ if ((s2) vsrc2 == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && ((s2) vsrc2) == -1) { \
+ /* won't generate /lit16 instr for this; check anyway */ \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op (s2) vsrc2; \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ /* non-div/rem case */ \
+ SET_REGISTER(vdst, GET_REGISTER(vsrc1) _op (s2) vsrc2); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT8(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/) \
+ { \
+ u2 litInfo; \
+ vdst = INST_AA(inst); \
+ litInfo = FETCH(1); \
+ vsrc1 = litInfo & 0xff; \
+ vsrc2 = litInfo >> 8; /* constant */ \
+ ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x", \
+ (_opname), vdst, vsrc1, vsrc2); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, result; \
+ firstVal = GET_REGISTER(vsrc1); \
+ if ((s1) vsrc2 == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && ((s1) vsrc2) == -1) { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op ((s1) vsrc2); \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ SET_REGISTER(vdst, \
+ (s4) GET_REGISTER(vsrc1) _op (s1) vsrc2); \
+ } \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_SHX_INT_LIT8(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/) \
+ { \
+ u2 litInfo; \
+ vdst = INST_AA(inst); \
+ litInfo = FETCH(1); \
+ vsrc1 = litInfo & 0xff; \
+ vsrc2 = litInfo >> 8; /* constant */ \
+ ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x", \
+ (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER(vdst, \
+ _cast GET_REGISTER(vsrc1) _op (vsrc2 & 0x1f)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_INT_2ADDR(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER(vdst); \
+ secondVal = GET_REGISTER(vsrc1); \
+ if (secondVal == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && secondVal == -1) { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ SET_REGISTER(vdst, \
+ (s4) GET_REGISTER(vdst) _op (s4) GET_REGISTER(vsrc1)); \
+ } \
+ FINISH(1);
+
+#define HANDLE_OP_SHX_INT_2ADDR(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER(vdst, \
+ _cast GET_REGISTER(vdst) _op (GET_REGISTER(vsrc1) & 0x1f)); \
+ FINISH(1);
+
+#define HANDLE_OP_X_LONG(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ if (_chkdiv != 0) { \
+ s8 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER_WIDE(vsrc1); \
+ secondVal = GET_REGISTER_WIDE(vsrc2); \
+ if (secondVal == 0LL) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u8)firstVal == 0x8000000000000000ULL && \
+ secondVal == -1LL) \
+ { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER_WIDE(vdst, result); \
+ } else { \
+ SET_REGISTER_WIDE(vdst, \
+ (s8) GET_REGISTER_WIDE(vsrc1) _op (s8) GET_REGISTER_WIDE(vsrc2)); \
+ } \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_SHX_LONG(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER_WIDE(vdst, \
+ _cast GET_REGISTER_WIDE(vsrc1) _op (GET_REGISTER(vsrc2) & 0x3f)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_LONG_2ADDR(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ if (_chkdiv != 0) { \
+ s8 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER_WIDE(vdst); \
+ secondVal = GET_REGISTER_WIDE(vsrc1); \
+ if (secondVal == 0LL) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u8)firstVal == 0x8000000000000000ULL && \
+ secondVal == -1LL) \
+ { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER_WIDE(vdst, result); \
+ } else { \
+ SET_REGISTER_WIDE(vdst, \
+ (s8) GET_REGISTER_WIDE(vdst) _op (s8)GET_REGISTER_WIDE(vsrc1));\
+ } \
+ FINISH(1);
+
+#define HANDLE_OP_SHX_LONG_2ADDR(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER_WIDE(vdst, \
+ _cast GET_REGISTER_WIDE(vdst) _op (GET_REGISTER(vsrc1) & 0x3f)); \
+ FINISH(1);
+
+#define HANDLE_OP_X_FLOAT(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-float v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER_FLOAT(vdst, \
+ GET_REGISTER_FLOAT(vsrc1) _op GET_REGISTER_FLOAT(vsrc2)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_DOUBLE(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-double v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER_DOUBLE(vdst, \
+ GET_REGISTER_DOUBLE(vsrc1) _op GET_REGISTER_DOUBLE(vsrc2)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_FLOAT_2ADDR(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-float-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER_FLOAT(vdst, \
+ GET_REGISTER_FLOAT(vdst) _op GET_REGISTER_FLOAT(vsrc1)); \
+ FINISH(1);
+
+#define HANDLE_OP_X_DOUBLE_2ADDR(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-double-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER_DOUBLE(vdst, \
+ GET_REGISTER_DOUBLE(vdst) _op GET_REGISTER_DOUBLE(vsrc1)); \
+ FINISH(1);
+
+#define HANDLE_OP_AGET(_opcode, _opname, _type, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ ArrayObject* arrayObj; \
+ u2 arrayInfo; \
+ EXPORT_PC(); \
+ vdst = INST_AA(inst); \
+ arrayInfo = FETCH(1); \
+ vsrc1 = arrayInfo & 0xff; /* array ptr */ \
+ vsrc2 = arrayInfo >> 8; /* index */ \
+ ILOGV("|aget%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ arrayObj = (ArrayObject*) GET_REGISTER(vsrc1); \
+ if (!checkForNull((Object*) arrayObj)) \
+ GOTO_exceptionThrown(); \
+ if (GET_REGISTER(vsrc2) >= arrayObj->length) { \
+ LOGV("Invalid array access: %p %d (len=%d)\n", \
+ arrayObj, vsrc2, arrayObj->length); \
+ dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+ NULL); \
+ GOTO_exceptionThrown(); \
+ } \
+ SET_REGISTER##_regsize(vdst, \
+ ((_type*) arrayObj->contents)[GET_REGISTER(vsrc2)]); \
+ ILOGV("+ AGET[%d]=0x%x", GET_REGISTER(vsrc2), GET_REGISTER(vdst)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_APUT(_opcode, _opname, _type, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ ArrayObject* arrayObj; \
+ u2 arrayInfo; \
+ EXPORT_PC(); \
+ vdst = INST_AA(inst); /* AA: source value */ \
+ arrayInfo = FETCH(1); \
+ vsrc1 = arrayInfo & 0xff; /* BB: array ptr */ \
+ vsrc2 = arrayInfo >> 8; /* CC: index */ \
+ ILOGV("|aput%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ arrayObj = (ArrayObject*) GET_REGISTER(vsrc1); \
+ if (!checkForNull((Object*) arrayObj)) \
+ GOTO_exceptionThrown(); \
+ if (GET_REGISTER(vsrc2) >= arrayObj->length) { \
+ dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+ NULL); \
+ GOTO_exceptionThrown(); \
+ } \
+ ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+ ((_type*) arrayObj->contents)[GET_REGISTER(vsrc2)] = \
+ GET_REGISTER##_regsize(vdst); \
+ } \
+ FINISH(2);
+
+/*
+ * It's possible to get a bad value out of a field with sub-32-bit stores
+ * because the -quick versions always operate on 32 bits. Consider:
+ * short foo = -1 (sets a 32-bit register to 0xffffffff)
+ * iput-quick foo (writes all 32 bits to the field)
+ * short bar = 1 (sets a 32-bit register to 0x00000001)
+ * iput-short (writes the low 16 bits to the field)
+ * iget-quick foo (reads all 32 bits from the field, yielding 0xffff0001)
+ * This can only happen when optimized and non-optimized code has interleaved
+ * access to the same field. This is unlikely but possible.
+ *
+ * The easiest way to fix this is to always read/write 32 bits at a time. On
+ * a device with a 16-bit data bus this is sub-optimal. (The alternative
+ * approach is to have sub-int versions of iget-quick, but now we're wasting
+ * Dalvik instruction space and making it less likely that handler code will
+ * already be in the CPU i-cache.)
+ */
+#define HANDLE_IGET_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ InstField* ifield; \
+ Object* obj; \
+ EXPORT_PC(); \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|iget%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNull(obj)) \
+ GOTO_exceptionThrown(); \
+ ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref); \
+ if (ifield == NULL) { \
+ ifield = dvmResolveInstField(curMethod->clazz, ref); \
+ if (ifield == NULL) \
+ GOTO_exceptionThrown(); \
+ } \
+ SET_REGISTER##_regsize(vdst, \
+ dvmGetField##_ftype(obj, ifield->byteOffset)); \
+ ILOGV("+ IGET '%s'=0x%08llx", ifield->field.name, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_GET(&ifield->field); \
+ } \
+ FINISH(2);
+
+#define HANDLE_IGET_X_QUICK(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ Object* obj; \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field offset */ \
+ ILOGV("|iget%s-quick v%d,v%d,field@+%u", \
+ (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNullExportPC(obj, fp, pc)) \
+ GOTO_exceptionThrown(); \
+ SET_REGISTER##_regsize(vdst, dvmGetField##_ftype(obj, ref)); \
+ ILOGV("+ IGETQ %d=0x%08llx", ref, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_IPUT_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ InstField* ifield; \
+ Object* obj; \
+ EXPORT_PC(); \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|iput%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNull(obj)) \
+ GOTO_exceptionThrown(); \
+ ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref); \
+ if (ifield == NULL) { \
+ ifield = dvmResolveInstField(curMethod->clazz, ref); \
+ if (ifield == NULL) \
+ GOTO_exceptionThrown(); \
+ } \
+ dvmSetField##_ftype(obj, ifield->byteOffset, \
+ GET_REGISTER##_regsize(vdst)); \
+ ILOGV("+ IPUT '%s'=0x%08llx", ifield->field.name, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_PUT(&ifield->field); \
+ } \
+ FINISH(2);
+
+#define HANDLE_IPUT_X_QUICK(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ Object* obj; \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field offset */ \
+ ILOGV("|iput%s-quick v%d,v%d,field@0x%04x", \
+ (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNullExportPC(obj, fp, pc)) \
+ GOTO_exceptionThrown(); \
+ dvmSetField##_ftype(obj, ref, GET_REGISTER##_regsize(vdst)); \
+ ILOGV("+ IPUTQ %d=0x%08llx", ref, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ } \
+ FINISH(2);
+
+/*
+ * The JIT needs dvmDexGetResolvedField() to return non-null.
+ * Since we use the portable interpreter to build the trace, the extra
+ * checks in HANDLE_SGET_X and HANDLE_SPUT_X are not needed for mterp.
+ */
+#define HANDLE_SGET_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/) \
+ { \
+ StaticField* sfield; \
+ vdst = INST_AA(inst); \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|sget%s v%d,sfield@0x%04x", (_opname), vdst, ref); \
+ sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+ if (sfield == NULL) { \
+ EXPORT_PC(); \
+ sfield = dvmResolveStaticField(curMethod->clazz, ref); \
+ if (sfield == NULL) \
+ GOTO_exceptionThrown(); \
+ if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) { \
+ ABORT_JIT_TSELECT(); \
+ } \
+ } \
+ SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield)); \
+ ILOGV("+ SGET '%s'=0x%08llx", \
+ sfield->field.name, (u8)GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_GET(&sfield->field); \
+ } \
+ FINISH(2);
+
+#define HANDLE_SPUT_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/) \
+ { \
+ StaticField* sfield; \
+ vdst = INST_AA(inst); \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|sput%s v%d,sfield@0x%04x", (_opname), vdst, ref); \
+ sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+ if (sfield == NULL) { \
+ EXPORT_PC(); \
+ sfield = dvmResolveStaticField(curMethod->clazz, ref); \
+ if (sfield == NULL) \
+ GOTO_exceptionThrown(); \
+ if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) { \
+ ABORT_JIT_TSELECT(); \
+ } \
+ } \
+ dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst)); \
+ ILOGV("+ SPUT '%s'=0x%08llx", \
+ sfield->field.name, (u8)GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_PUT(&sfield->field); \
+ } \
+ FINISH(2);
+
+/* File: c/OP_NOP.c */
+HANDLE_OPCODE(OP_NOP)
+ FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE.c */
+HANDLE_OPCODE(OP_MOVE /*vA, vB*/)
+ vdst = INST_A(inst);
+ vsrc1 = INST_B(inst);
+ ILOGV("|move%s v%d,v%d %s(v%d=0x%08x)",
+ (INST_INST(inst) == OP_MOVE) ? "" : "-object", vdst, vsrc1,
+ kSpacing, vdst, GET_REGISTER(vsrc1));
+ SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+ FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE_FROM16.c */
+HANDLE_OPCODE(OP_MOVE_FROM16 /*vAA, vBBBB*/)
+ vdst = INST_AA(inst);
+ vsrc1 = FETCH(1);
+ ILOGV("|move%s/from16 v%d,v%d %s(v%d=0x%08x)",
+ (INST_INST(inst) == OP_MOVE_FROM16) ? "" : "-object", vdst, vsrc1,
+ kSpacing, vdst, GET_REGISTER(vsrc1));
+ SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+ FINISH(2);
+OP_END
+
+/* File: c/OP_MOVE_16.c */
+HANDLE_OPCODE(OP_MOVE_16 /*vAAAA, vBBBB*/)
+ vdst = FETCH(1);
+ vsrc1 = FETCH(2);
+ ILOGV("|move%s/16 v%d,v%d %s(v%d=0x%08x)",
+ (INST_INST(inst) == OP_MOVE_16) ? "" : "-object", vdst, vsrc1,
+ kSpacing, vdst, GET_REGISTER(vsrc1));
+ SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+ FINISH(3);
+OP_END
+
+/* File: c/OP_MOVE_WIDE.c */
+HANDLE_OPCODE(OP_MOVE_WIDE /*vA, vB*/)
+ /* IMPORTANT: must correctly handle overlapping registers, e.g. both
+ * "move-wide v6, v7" and "move-wide v7, v6" */
+ vdst = INST_A(inst);
+ vsrc1 = INST_B(inst);
+ ILOGV("|move-wide v%d,v%d %s(v%d=0x%08llx)", vdst, vsrc1,
+ kSpacing+5, vdst, GET_REGISTER_WIDE(vsrc1));
+ SET_REGISTER_WIDE(vdst, GET_REGISTER_WIDE(vsrc1));
+ FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE_WIDE_FROM16.c */
+HANDLE_OPCODE(OP_MOVE_WIDE_FROM16 /*vAA, vBBBB*/)
+ vdst = INST_AA(inst);
+ vsrc1 = FETCH(1);
+ ILOGV("|move-wide/from16 v%d,v%d (v%d=0x%08llx)", vdst, vsrc1,
+ vdst, GET_REGISTER_WIDE(vsrc1));
+ SET_REGISTER_WIDE(vdst, GET_REGISTER_WIDE(vsrc1));
+ FINISH(2);
+OP_END
+
+/* File: c/OP_MOVE_WIDE_16.c */
+HANDLE_OPCODE(OP_MOVE_WIDE_16 /*vAAAA, vBBBB*/)
+ vdst = FETCH(1);
+ vsrc1 = FETCH(2);
+ ILOGV("|move-wide/16 v%d,v%d %s(v%d=0x%08llx)", vdst, vsrc1,
+ kSpacing+8, vdst, GET_REGISTER_WIDE(vsrc1));
+ SET_REGISTER_WIDE(vdst, GET_REGISTER_WIDE(vsrc1));
+ FINISH(3);
+OP_END
+
+/* File: c/OP_MOVE_OBJECT.c */
+/* File: c/OP_MOVE.c */
+HANDLE_OPCODE(OP_MOVE_OBJECT /*vA, vB*/)
+ vdst = INST_A(inst);
+ vsrc1 = INST_B(inst);
+ ILOGV("|move%s v%d,v%d %s(v%d=0x%08x)",
+ (INST_INST(inst) == OP_MOVE) ? "" : "-object", vdst, vsrc1,
+ kSpacing, vdst, GET_REGISTER(vsrc1));
+ SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+ FINISH(1);
+OP_END
+
+
+/* File: c/OP_MOVE_OBJECT_FROM16.c */
+/* File: c/OP_MOVE_FROM16.c */
+HANDLE_OPCODE(OP_MOVE_OBJECT_FROM16 /*vAA, vBBBB*/)
+ vdst = INST_AA(inst);
+ vsrc1 = FETCH(1);
+ ILOGV("|move%s/from16 v%d,v%d %s(v%d=0x%08x)",
+ (INST_INST(inst) == OP_MOVE_FROM16) ? "" : "-object", vdst, vsrc1,
+ kSpacing, vdst, GET_REGISTER(vsrc1));
+ SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+ FINISH(2);
+OP_END
+
+
+/* File: c/OP_MOVE_OBJECT_16.c */
+/* File: c/OP_MOVE_16.c */
+HANDLE_OPCODE(OP_MOVE_OBJECT_16 /*vAAAA, vBBBB*/)
+ vdst = FETCH(1);
+ vsrc1 = FETCH(2);
+ ILOGV("|move%s/16 v%d,v%d %s(v%d=0x%08x)",
+ (INST_INST(inst) == OP_MOVE_16) ? "" : "-object", vdst, vsrc1,
+ kSpacing, vdst, GET_REGISTER(vsrc1));
+ SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+ FINISH(3);
+OP_END
+
+
+/* File: c/OP_MOVE_RESULT.c */
+HANDLE_OPCODE(OP_MOVE_RESULT /*vAA*/)
+ vdst = INST_AA(inst);
+ ILOGV("|move-result%s v%d %s(v%d=0x%08x)",
+ (INST_INST(inst) == OP_MOVE_RESULT) ? "" : "-object",
+ vdst, kSpacing+4, vdst,retval.i);
+ SET_REGISTER(vdst, retval.i);
+ FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE_RESULT_WIDE.c */
+HANDLE_OPCODE(OP_MOVE_RESULT_WIDE /*vAA*/)
+ vdst = INST_AA(inst);
+ ILOGV("|move-result-wide v%d %s(0x%08llx)", vdst, kSpacing, retval.j);
+ SET_REGISTER_WIDE(vdst, retval.j);
+ FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE_RESULT_OBJECT.c */
+/* File: c/OP_MOVE_RESULT.c */
+HANDLE_OPCODE(OP_MOVE_RESULT_OBJECT /*vAA*/)
+ vdst = INST_AA(inst);
+ ILOGV("|move-result%s v%d %s(v%d=0x%08x)",
+ (INST_INST(inst) == OP_MOVE_RESULT) ? "" : "-object",
+ vdst, kSpacing+4, vdst,retval.i);
+ SET_REGISTER(vdst, retval.i);
+ FINISH(1);
+OP_END
+
+
+/* File: c/OP_MOVE_EXCEPTION.c */
+HANDLE_OPCODE(OP_MOVE_EXCEPTION /*vAA*/)
+ vdst = INST_AA(inst);
+ ILOGV("|move-exception v%d", vdst);
+ assert(self->exception != NULL);
+ SET_REGISTER(vdst, (u4)self->exception);
+ dvmClearException(self);
+ FINISH(1);
+OP_END
+
+/* File: c/OP_RETURN_VOID.c */
+HANDLE_OPCODE(OP_RETURN_VOID /**/)
+ ILOGV("|return-void");
+#ifndef NDEBUG
+ retval.j = 0xababababULL; // placate valgrind
+#endif
+ GOTO_returnFromMethod();
+OP_END
+
+/* File: c/OP_RETURN.c */
+HANDLE_OPCODE(OP_RETURN /*vAA*/)
+ vsrc1 = INST_AA(inst);
+ ILOGV("|return%s v%d",
+ (INST_INST(inst) == OP_RETURN) ? "" : "-object", vsrc1);
+ retval.i = GET_REGISTER(vsrc1);
+ GOTO_returnFromMethod();
+OP_END
+
+/* File: c/OP_RETURN_WIDE.c */
+HANDLE_OPCODE(OP_RETURN_WIDE /*vAA*/)
+ vsrc1 = INST_AA(inst);
+ ILOGV("|return-wide v%d", vsrc1);
+ retval.j = GET_REGISTER_WIDE(vsrc1);
+ GOTO_returnFromMethod();
+OP_END
+
+/* File: c/OP_RETURN_OBJECT.c */
+/* File: c/OP_RETURN.c */
+HANDLE_OPCODE(OP_RETURN_OBJECT /*vAA*/)
+ vsrc1 = INST_AA(inst);
+ ILOGV("|return%s v%d",
+ (INST_INST(inst) == OP_RETURN) ? "" : "-object", vsrc1);
+ retval.i = GET_REGISTER(vsrc1);
+ GOTO_returnFromMethod();
+OP_END
+
+
+/* File: c/OP_CONST_4.c */
+HANDLE_OPCODE(OP_CONST_4 /*vA, #+B*/)
+ {
+ s4 tmp;
+
+ vdst = INST_A(inst);
+ tmp = (s4) (INST_B(inst) << 28) >> 28; // sign extend 4-bit value
+ ILOGV("|const/4 v%d,#0x%02x", vdst, (s4)tmp);
+ SET_REGISTER(vdst, tmp);
+ }
+ FINISH(1);
+OP_END
+
+/* File: c/OP_CONST_16.c */
+HANDLE_OPCODE(OP_CONST_16 /*vAA, #+BBBB*/)
+ vdst = INST_AA(inst);
+ vsrc1 = FETCH(1);
+ ILOGV("|const/16 v%d,#0x%04x", vdst, (s2)vsrc1);
+ SET_REGISTER(vdst, (s2) vsrc1);
+ FINISH(2);
+OP_END
+
+/* File: c/OP_CONST.c */
+HANDLE_OPCODE(OP_CONST /*vAA, #+BBBBBBBB*/)
+ {
+ u4 tmp;
+
+ vdst = INST_AA(inst);
+ tmp = FETCH(1);
+ tmp |= (u4)FETCH(2) << 16;
+ ILOGV("|const v%d,#0x%08x", vdst, tmp);
+ SET_REGISTER(vdst, tmp);
+ }
+ FINISH(3);
+OP_END
+
+/* File: c/OP_CONST_HIGH16.c */
+HANDLE_OPCODE(OP_CONST_HIGH16 /*vAA, #+BBBB0000*/)
+ vdst = INST_AA(inst);
+ vsrc1 = FETCH(1);
+ ILOGV("|const/high16 v%d,#0x%04x0000", vdst, vsrc1);
+ SET_REGISTER(vdst, vsrc1 << 16);
+ FINISH(2);
+OP_END
+
+/* File: c/OP_CONST_WIDE_16.c */
+HANDLE_OPCODE(OP_CONST_WIDE_16 /*vAA, #+BBBB*/)
+ vdst = INST_AA(inst);
+ vsrc1 = FETCH(1);
+ ILOGV("|const-wide/16 v%d,#0x%04x", vdst, (s2)vsrc1);
+ SET_REGISTER_WIDE(vdst, (s2)vsrc1);
+ FINISH(2);
+OP_END
+
+/* File: c/OP_CONST_WIDE_32.c */
+HANDLE_OPCODE(OP_CONST_WIDE_32 /*vAA, #+BBBBBBBB*/)
+ {
+ u4 tmp;
+
+ vdst = INST_AA(inst);
+ tmp = FETCH(1);
+ tmp |= (u4)FETCH(2) << 16;
+ ILOGV("|const-wide/32 v%d,#0x%08x", vdst, tmp);
+ SET_REGISTER_WIDE(vdst, (s4) tmp);
+ }
+ FINISH(3);
+OP_END
+
+/* File: c/OP_CONST_WIDE.c */
+HANDLE_OPCODE(OP_CONST_WIDE /*vAA, #+BBBBBBBBBBBBBBBB*/)
+ {
+ u8 tmp;
+
+ vdst = INST_AA(inst);
+ tmp = FETCH(1);
+ tmp |= (u8)FETCH(2) << 16;
+ tmp |= (u8)FETCH(3) << 32;
+ tmp |= (u8)FETCH(4) << 48;
+ ILOGV("|const-wide v%d,#0x%08llx", vdst, tmp);
+ SET_REGISTER_WIDE(vdst, tmp);
+ }
+ FINISH(5);
+OP_END
+
+/* File: c/OP_CONST_WIDE_HIGH16.c */
+HANDLE_OPCODE(OP_CONST_WIDE_HIGH16 /*vAA, #+BBBB000000000000*/)
+ vdst = INST_AA(inst);
+ vsrc1 = FETCH(1);
+ ILOGV("|const-wide/high16 v%d,#0x%04x000000000000", vdst, vsrc1);
+ SET_REGISTER_WIDE(vdst, ((u8) vsrc1) << 48);
+ FINISH(2);
+OP_END
+
+/* File: c/OP_CONST_STRING.c */
+HANDLE_OPCODE(OP_CONST_STRING /*vAA, string@BBBB*/)
+ {
+ StringObject* strObj;
+
+ vdst = INST_AA(inst);
+ ref = FETCH(1);
+ ILOGV("|const-string v%d string@0x%04x", vdst, ref);
+ strObj = dvmDexGetResolvedString(methodClassDex, ref);
+ if (strObj == NULL) {
+ EXPORT_PC();
+ strObj = dvmResolveString(curMethod->clazz, ref);
+ if (strObj == NULL)
+ GOTO_exceptionThrown();
+ }
+ SET_REGISTER(vdst, (u4) strObj);
+ }
+ FINISH(2);
+OP_END
+
+/* File: c/OP_CONST_STRING_JUMBO.c */
+HANDLE_OPCODE(OP_CONST_STRING_JUMBO /*vAA, string@BBBBBBBB*/)
+ {
+ StringObject* strObj;
+ u4 tmp;
+
+ vdst = INST_AA(inst);
+ tmp = FETCH(1);
+ tmp |= (u4)FETCH(2) << 16;
+ ILOGV("|const-string/jumbo v%d string@0x%08x", vdst, tmp);
+ strObj = dvmDexGetResolvedString(methodClassDex, tmp);
+ if (strObj == NULL) {
+ EXPORT_PC();
+ strObj = dvmResolveString(curMethod->clazz, tmp);
+ if (strObj == NULL)
+ GOTO_exceptionThrown();
+ }
+ SET_REGISTER(vdst, (u4) strObj);
+ }
+ FINISH(3);
+OP_END
+
+/* File: c/OP_CONST_CLASS.c */
+HANDLE_OPCODE(OP_CONST_CLASS /*vAA, class@BBBB*/)
+ {
+ ClassObject* clazz;
+
+ vdst = INST_AA(inst);
+ ref = FETCH(1);
+ ILOGV("|const-class v%d class@0x%04x", vdst, ref);
+ clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+ if (clazz == NULL) {
+ EXPORT_PC();
+ clazz = dvmResolveClass(curMethod->clazz, ref, true);
+ if (clazz == NULL)
+ GOTO_exceptionThrown();
+ }
+ SET_REGISTER(vdst, (u4) clazz);
+ }
+ FINISH(2);
+OP_END
+
+/* File: c/OP_MONITOR_ENTER.c */
+HANDLE_OPCODE(OP_MONITOR_ENTER /*vAA*/)
+ {
+ Object* obj;
+
+ vsrc1 = INST_AA(inst);
+ ILOGV("|monitor-enter v%d %s(0x%08x)",
+ vsrc1, kSpacing+6, GET_REGISTER(vsrc1));
+ obj = (Object*)GET_REGISTER(vsrc1);
+ if (!checkForNullExportPC(obj, fp, pc))
+ GOTO_exceptionThrown();
+ ILOGV("+ locking %p %s\n", obj, obj->clazz->descriptor);
+ EXPORT_PC(); /* need for precise GC, also WITH_MONITOR_TRACKING */
+ dvmLockObject(self, obj);
+#ifdef WITH_DEADLOCK_PREDICTION
+ if (dvmCheckException(self))
+ GOTO_exceptionThrown();
+#endif
+ }
+ FINISH(1);
+OP_END
+
+/* File: c/OP_MONITOR_EXIT.c */
+HANDLE_OPCODE(OP_MONITOR_EXIT /*vAA*/)
+ {
+ Object* obj;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst);
+ ILOGV("|monitor-exit v%d %s(0x%08x)",
+ vsrc1, kSpacing+5, GET_REGISTER(vsrc1));
+ obj = (Object*)GET_REGISTER(vsrc1);
+ if (!checkForNull(obj)) {
+ /*
+ * The exception needs to be processed at the *following*
+ * instruction, not the current instruction (see the Dalvik
+ * spec). Because we're jumping to an exception handler,
+ * we're not actually at risk of skipping an instruction
+ * by doing so.
+ */
+ ADJUST_PC(1); /* monitor-exit width is 1 */
+ GOTO_exceptionThrown();
+ }
+ ILOGV("+ unlocking %p %s\n", obj, obj->clazz->descriptor);
+ if (!dvmUnlockObject(self, obj)) {
+ assert(dvmCheckException(self));
+ ADJUST_PC(1);
+ GOTO_exceptionThrown();
+ }
+ }
+ FINISH(1);
+OP_END
+
+/* File: c/OP_CHECK_CAST.c */
+HANDLE_OPCODE(OP_CHECK_CAST /*vAA, class@BBBB*/)
+ {
+ ClassObject* clazz;
+ Object* obj;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst);
+ ref = FETCH(1); /* class to check against */
+ ILOGV("|check-cast v%d,class@0x%04x", vsrc1, ref);
+
+ obj = (Object*)GET_REGISTER(vsrc1);
+ if (obj != NULL) {
+#if defined(WITH_EXTRA_OBJECT_VALIDATION)
+ if (!checkForNull(obj))
+ GOTO_exceptionThrown();
+#endif
+ clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+ if (clazz == NULL) {
+ clazz = dvmResolveClass(curMethod->clazz, ref, false);
+ if (clazz == NULL)
+ GOTO_exceptionThrown();
+ }
+ if (!dvmInstanceof(obj->clazz, clazz)) {
+ dvmThrowExceptionWithClassMessage(
+ "Ljava/lang/ClassCastException;", obj->clazz->descriptor);
+ GOTO_exceptionThrown();
+ }
+ }
+ }
+ FINISH(2);
+OP_END
+
+/* File: c/OP_INSTANCE_OF.c */
+HANDLE_OPCODE(OP_INSTANCE_OF /*vA, vB, class@CCCC*/)
+ {
+ ClassObject* clazz;
+ Object* obj;
+
+ vdst = INST_A(inst);
+ vsrc1 = INST_B(inst); /* object to check */
+ ref = FETCH(1); /* class to check against */
+ ILOGV("|instance-of v%d,v%d,class@0x%04x", vdst, vsrc1, ref);
+
+ obj = (Object*)GET_REGISTER(vsrc1);
+ if (obj == NULL) {
+ SET_REGISTER(vdst, 0);
+ } else {
+#if defined(WITH_EXTRA_OBJECT_VALIDATION)
+ if (!checkForNullExportPC(obj, fp, pc))
+ GOTO_exceptionThrown();
+#endif
+ clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+ if (clazz == NULL) {
+ EXPORT_PC();
+ clazz = dvmResolveClass(curMethod->clazz, ref, true);
+ if (clazz == NULL)
+ GOTO_exceptionThrown();
+ }
+ SET_REGISTER(vdst, dvmInstanceof(obj->clazz, clazz));
+ }
+ }
+ FINISH(2);
+OP_END
+
+/* File: c/OP_ARRAY_LENGTH.c */
+HANDLE_OPCODE(OP_ARRAY_LENGTH /*vA, vB*/)
+ {
+ ArrayObject* arrayObj;
+
+ vdst = INST_A(inst);
+ vsrc1 = INST_B(inst);
+ arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);
+ ILOGV("|array-length v%d,v%d (%p)", vdst, vsrc1, arrayObj);
+ if (!checkForNullExportPC((Object*) arrayObj, fp, pc))
+ GOTO_exceptionThrown();
+ /* verifier guarantees this is an array reference */
+ SET_REGISTER(vdst, arrayObj->length);
+ }
+ FINISH(1);
+OP_END
+
+/* File: c/OP_NEW_INSTANCE.c */
+HANDLE_OPCODE(OP_NEW_INSTANCE /*vAA, class@BBBB*/)
+ {
+ ClassObject* clazz;
+ Object* newObj;
+
+ EXPORT_PC();
+
+ vdst = INST_AA(inst);
+ ref = FETCH(1);
+ ILOGV("|new-instance v%d,class@0x%04x", vdst, ref);
+ clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+ if (clazz == NULL) {
+ clazz = dvmResolveClass(curMethod->clazz, ref, false);
+ if (clazz == NULL)
+ GOTO_exceptionThrown();
+ }
+
+ if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))
+ GOTO_exceptionThrown();
+
+ /*
+ * The JIT needs dvmDexGetResolvedClass() to return non-null.
+ * Since we use the portable interpreter to build the trace, this extra
+ * check is not needed for mterp.
+ */
+ if (!dvmDexGetResolvedClass(methodClassDex, ref)) {
+ /* Class initialization is still ongoing - abandon the trace */
+ ABORT_JIT_TSELECT();
+ }
+
+ /*
+ * Verifier now tests for interface/abstract class.
+ */
+ //if (dvmIsInterfaceClass(clazz) || dvmIsAbstractClass(clazz)) {
+ // dvmThrowExceptionWithClassMessage("Ljava/lang/InstantiationError;",
+ // clazz->descriptor);
+ // GOTO_exceptionThrown();
+ //}
+ newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+ if (newObj == NULL)
+ GOTO_exceptionThrown();
+ SET_REGISTER(vdst, (u4) newObj);
+ }
+ FINISH(2);
+OP_END
+
+/* File: c/OP_NEW_ARRAY.c */
+HANDLE_OPCODE(OP_NEW_ARRAY /*vA, vB, class@CCCC*/)
+ {
+ ClassObject* arrayClass;
+ ArrayObject* newArray;
+ s4 length;
+
+ EXPORT_PC();
+
+ vdst = INST_A(inst);
+ vsrc1 = INST_B(inst); /* length reg */
+ ref = FETCH(1);
+ ILOGV("|new-array v%d,v%d,class@0x%04x (%d elements)",
+ vdst, vsrc1, ref, (s4) GET_REGISTER(vsrc1));
+ length = (s4) GET_REGISTER(vsrc1);
+ if (length < 0) {
+ dvmThrowException("Ljava/lang/NegativeArraySizeException;", NULL);
+ GOTO_exceptionThrown();
+ }
+ arrayClass = dvmDexGetResolvedClass(methodClassDex, ref);
+ if (arrayClass == NULL) {
+ arrayClass = dvmResolveClass(curMethod->clazz, ref, false);
+ if (arrayClass == NULL)
+ GOTO_exceptionThrown();
+ }
+ /* verifier guarantees this is an array class */
+ assert(dvmIsArrayClass(arrayClass));
+ assert(dvmIsClassInitialized(arrayClass));
+
+ newArray = dvmAllocArrayByClass(arrayClass, length, ALLOC_DONT_TRACK);
+ if (newArray == NULL)
+ GOTO_exceptionThrown();
+ SET_REGISTER(vdst, (u4) newArray);
+ }
+ FINISH(2);
+OP_END
+
+/* File: c/OP_FILLED_NEW_ARRAY.c */
+HANDLE_OPCODE(OP_FILLED_NEW_ARRAY /*vB, {vD, vE, vF, vG, vA}, class@CCCC*/)
+ GOTO_invoke(filledNewArray, false);
+OP_END
+
+/* File: c/OP_FILLED_NEW_ARRAY_RANGE.c */
+HANDLE_OPCODE(OP_FILLED_NEW_ARRAY_RANGE /*{vCCCC..v(CCCC+AA-1)}, class@BBBB*/)
+ GOTO_invoke(filledNewArray, true);
+OP_END
+
+/* File: c/OP_FILL_ARRAY_DATA.c */
+HANDLE_OPCODE(OP_FILL_ARRAY_DATA) /*vAA, +BBBBBBBB*/
+ {
+ const u2* arrayData;
+ s4 offset;
+ ArrayObject* arrayObj;
+
+ EXPORT_PC();
+ vsrc1 = INST_AA(inst);
+ offset = FETCH(1) | (((s4) FETCH(2)) << 16);
+ ILOGV("|fill-array-data v%d +0x%04x", vsrc1, offset);
+ arrayData = pc + offset; // offset in 16-bit units
+#ifndef NDEBUG
+ if (arrayData < curMethod->insns ||
+ arrayData >= curMethod->insns + dvmGetMethodInsnsSize(curMethod))
+ {
+ /* should have been caught in verifier */
+ dvmThrowException("Ljava/lang/InternalError;",
+ "bad fill array data");
+ GOTO_exceptionThrown();
+ }
+#endif
+ arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);
+ if (!dvmInterpHandleFillArrayData(arrayObj, arrayData)) {
+ GOTO_exceptionThrown();
+ }
+ FINISH(3);
+ }
+OP_END
+
+/* File: c/OP_THROW.c */
+HANDLE_OPCODE(OP_THROW /*vAA*/)
+ {
+ Object* obj;
+
+ /*
+ * We don't create an exception here, but the process of searching
+ * for a catch block can do class lookups and throw exceptions.
+ * We need to update the saved PC.
+ */
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst);
+ ILOGV("|throw v%d (%p)", vsrc1, (void*)GET_REGISTER(vsrc1));
+ obj = (Object*) GET_REGISTER(vsrc1);
+ if (!checkForNull(obj)) {
+ /* will throw a null pointer exception */
+ LOGVV("Bad exception\n");
+ } else {
+ /* use the requested exception */
+ dvmSetException(self, obj);
+ }
+ GOTO_exceptionThrown();
+ }
+OP_END
+
+/* File: c/OP_GOTO.c */
+HANDLE_OPCODE(OP_GOTO /*+AA*/)
+ vdst = INST_AA(inst);
+ if ((s1)vdst < 0)
+ ILOGV("|goto -0x%02x", -((s1)vdst));
+ else
+ ILOGV("|goto +0x%02x", ((s1)vdst));
+ ILOGV("> branch taken");
+ if ((s1)vdst < 0)
+ PERIODIC_CHECKS(kInterpEntryInstr, (s1)vdst);
+ FINISH((s1)vdst);
+OP_END
+
+/* File: c/OP_GOTO_16.c */
+HANDLE_OPCODE(OP_GOTO_16 /*+AAAA*/)
+ {
+ s4 offset = (s2) FETCH(1); /* sign-extend next code unit */
+
+ if (offset < 0)
+ ILOGV("|goto/16 -0x%04x", -offset);
+ else
+ ILOGV("|goto/16 +0x%04x", offset);
+ ILOGV("> branch taken");
+ if (offset < 0)
+ PERIODIC_CHECKS(kInterpEntryInstr, offset);
+ FINISH(offset);
+ }
+OP_END
+
+/* File: c/OP_GOTO_32.c */
+HANDLE_OPCODE(OP_GOTO_32 /*+AAAAAAAA*/)
+ {
+ s4 offset = FETCH(1); /* low-order 16 bits */
+ offset |= ((s4) FETCH(2)) << 16; /* high-order 16 bits */
+
+ if (offset < 0)
+ ILOGV("|goto/32 -0x%08x", -offset);
+ else
+ ILOGV("|goto/32 +0x%08x", offset);
+ ILOGV("> branch taken");
+ if (offset <= 0) /* allowed to branch to self */
+ PERIODIC_CHECKS(kInterpEntryInstr, offset);
+ FINISH(offset);
+ }
+OP_END
+
+/* File: c/OP_PACKED_SWITCH.c */
+HANDLE_OPCODE(OP_PACKED_SWITCH /*vAA, +BBBB*/)
+ {
+ const u2* switchData;
+ u4 testVal;
+ s4 offset;
+
+ vsrc1 = INST_AA(inst);
+ offset = FETCH(1) | (((s4) FETCH(2)) << 16);
+ ILOGV("|packed-switch v%d +0x%04x", vsrc1, vsrc2);
+ switchData = pc + offset; // offset in 16-bit units
+#ifndef NDEBUG
+ if (switchData < curMethod->insns ||
+ switchData >= curMethod->insns + dvmGetMethodInsnsSize(curMethod))
+ {
+ /* should have been caught in verifier */
+ EXPORT_PC();
+ dvmThrowException("Ljava/lang/InternalError;", "bad packed switch");
+ GOTO_exceptionThrown();
+ }
+#endif
+ testVal = GET_REGISTER(vsrc1);
+
+ offset = dvmInterpHandlePackedSwitch(switchData, testVal);
+ ILOGV("> branch taken (0x%04x)\n", offset);
+ if (offset <= 0) /* uncommon */
+ PERIODIC_CHECKS(kInterpEntryInstr, offset);
+ FINISH(offset);
+ }
+OP_END
+
+/* File: c/OP_SPARSE_SWITCH.c */
+HANDLE_OPCODE(OP_SPARSE_SWITCH /*vAA, +BBBB*/)
+ {
+ const u2* switchData;
+ u4 testVal;
+ s4 offset;
+
+ vsrc1 = INST_AA(inst);
+ offset = FETCH(1) | (((s4) FETCH(2)) << 16);
+ ILOGV("|sparse-switch v%d +0x%04x", vsrc1, vsrc2);
+ switchData = pc + offset; // offset in 16-bit units
+#ifndef NDEBUG
+ if (switchData < curMethod->insns ||
+ switchData >= curMethod->insns + dvmGetMethodInsnsSize(curMethod))
+ {
+ /* should have been caught in verifier */
+ EXPORT_PC();
+ dvmThrowException("Ljava/lang/InternalError;", "bad sparse switch");
+ GOTO_exceptionThrown();
+ }
+#endif
+ testVal = GET_REGISTER(vsrc1);
+
+ offset = dvmInterpHandleSparseSwitch(switchData, testVal);
+ ILOGV("> branch taken (0x%04x)\n", offset);
+ if (offset <= 0) /* uncommon */
+ PERIODIC_CHECKS(kInterpEntryInstr, offset);
+ FINISH(offset);
+ }
+OP_END
+
+/* File: c/OP_CMPL_FLOAT.c */
+HANDLE_OP_CMPX(OP_CMPL_FLOAT, "l-float", float, _FLOAT, -1)
+OP_END
+
+/* File: c/OP_CMPG_FLOAT.c */
+HANDLE_OP_CMPX(OP_CMPG_FLOAT, "g-float", float, _FLOAT, 1)
+OP_END
+
+/* File: c/OP_CMPL_DOUBLE.c */
+HANDLE_OP_CMPX(OP_CMPL_DOUBLE, "l-double", double, _DOUBLE, -1)
+OP_END
+
+/* File: c/OP_CMPG_DOUBLE.c */
+HANDLE_OP_CMPX(OP_CMPG_DOUBLE, "g-double", double, _DOUBLE, 1)
+OP_END
+
+/* File: c/OP_CMP_LONG.c */
+HANDLE_OP_CMPX(OP_CMP_LONG, "-long", s8, _WIDE, 0)
+OP_END
+
+/* File: c/OP_IF_EQ.c */
+HANDLE_OP_IF_XX(OP_IF_EQ, "eq", ==)
+OP_END
+
+/* File: c/OP_IF_NE.c */
+HANDLE_OP_IF_XX(OP_IF_NE, "ne", !=)
+OP_END
+
+/* File: c/OP_IF_LT.c */
+HANDLE_OP_IF_XX(OP_IF_LT, "lt", <)
+OP_END
+
+/* File: c/OP_IF_GE.c */
+HANDLE_OP_IF_XX(OP_IF_GE, "ge", >=)
+OP_END
+
+/* File: c/OP_IF_GT.c */
+HANDLE_OP_IF_XX(OP_IF_GT, "gt", >)
+OP_END
+
+/* File: c/OP_IF_LE.c */
+HANDLE_OP_IF_XX(OP_IF_LE, "le", <=)
+OP_END
+
+/* File: c/OP_IF_EQZ.c */
+HANDLE_OP_IF_XXZ(OP_IF_EQZ, "eqz", ==)
+OP_END
+
+/* File: c/OP_IF_NEZ.c */
+HANDLE_OP_IF_XXZ(OP_IF_NEZ, "nez", !=)
+OP_END
+
+/* File: c/OP_IF_LTZ.c */
+HANDLE_OP_IF_XXZ(OP_IF_LTZ, "ltz", <)
+OP_END
+
+/* File: c/OP_IF_GEZ.c */
+HANDLE_OP_IF_XXZ(OP_IF_GEZ, "gez", >=)
+OP_END
+
+/* File: c/OP_IF_GTZ.c */
+HANDLE_OP_IF_XXZ(OP_IF_GTZ, "gtz", >)
+OP_END
+
+/* File: c/OP_IF_LEZ.c */
+HANDLE_OP_IF_XXZ(OP_IF_LEZ, "lez", <=)
+OP_END
+
+/* File: c/OP_UNUSED_3E.c */
+HANDLE_OPCODE(OP_UNUSED_3E)
+OP_END
+
+/* File: c/OP_UNUSED_3F.c */
+HANDLE_OPCODE(OP_UNUSED_3F)
+OP_END
+
+/* File: c/OP_UNUSED_40.c */
+HANDLE_OPCODE(OP_UNUSED_40)
+OP_END
+
+/* File: c/OP_UNUSED_41.c */
+HANDLE_OPCODE(OP_UNUSED_41)
+OP_END
+
+/* File: c/OP_UNUSED_42.c */
+HANDLE_OPCODE(OP_UNUSED_42)
+OP_END
+
+/* File: c/OP_UNUSED_43.c */
+HANDLE_OPCODE(OP_UNUSED_43)
+OP_END
+
+/* File: c/OP_AGET.c */
+HANDLE_OP_AGET(OP_AGET, "", u4, )
+OP_END
+
+/* File: c/OP_AGET_WIDE.c */
+HANDLE_OP_AGET(OP_AGET_WIDE, "-wide", s8, _WIDE)
+OP_END
+
+/* File: c/OP_AGET_OBJECT.c */
+HANDLE_OP_AGET(OP_AGET_OBJECT, "-object", u4, )
+OP_END
+
+/* File: c/OP_AGET_BOOLEAN.c */
+HANDLE_OP_AGET(OP_AGET_BOOLEAN, "-boolean", u1, )
+OP_END
+
+/* File: c/OP_AGET_BYTE.c */
+HANDLE_OP_AGET(OP_AGET_BYTE, "-byte", s1, )
+OP_END
+
+/* File: c/OP_AGET_CHAR.c */
+HANDLE_OP_AGET(OP_AGET_CHAR, "-char", u2, )
+OP_END
+
+/* File: c/OP_AGET_SHORT.c */
+HANDLE_OP_AGET(OP_AGET_SHORT, "-short", s2, )
+OP_END
+
+/* File: c/OP_APUT.c */
+HANDLE_OP_APUT(OP_APUT, "", u4, )
+OP_END
+
+/* File: c/OP_APUT_WIDE.c */
+HANDLE_OP_APUT(OP_APUT_WIDE, "-wide", s8, _WIDE)
+OP_END
+
+/* File: c/OP_APUT_OBJECT.c */
+HANDLE_OPCODE(OP_APUT_OBJECT /*vAA, vBB, vCC*/)
+ {
+ ArrayObject* arrayObj;
+ Object* obj;
+ u2 arrayInfo;
+ EXPORT_PC();
+ vdst = INST_AA(inst); /* AA: source value */
+ arrayInfo = FETCH(1);
+ vsrc1 = arrayInfo & 0xff; /* BB: array ptr */
+ vsrc2 = arrayInfo >> 8; /* CC: index */
+ ILOGV("|aput%s v%d,v%d,v%d", "-object", vdst, vsrc1, vsrc2);
+ arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);
+ if (!checkForNull((Object*) arrayObj))
+ GOTO_exceptionThrown();
+ if (GET_REGISTER(vsrc2) >= arrayObj->length) {
+ dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;",
+ NULL);
+ GOTO_exceptionThrown();
+ }
+ obj = (Object*) GET_REGISTER(vdst);
+ if (obj != NULL) {
+ if (!checkForNull(obj))
+ GOTO_exceptionThrown();
+ if (!dvmCanPutArrayElement(obj->clazz, arrayObj->obj.clazz)) {
+ LOGV("Can't put a '%s'(%p) into array type='%s'(%p)\n",
+ obj->clazz->descriptor, obj,
+ arrayObj->obj.clazz->descriptor, arrayObj);
+ //dvmDumpClass(obj->clazz);
+ //dvmDumpClass(arrayObj->obj.clazz);
+ dvmThrowException("Ljava/lang/ArrayStoreException;", NULL);
+ GOTO_exceptionThrown();
+ }
+ }
+ ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));
+ dvmSetObjectArrayElement(arrayObj,
+ GET_REGISTER(vsrc2),
+ (Object *)GET_REGISTER(vdst));
+ }
+ FINISH(2);
+OP_END
+
+/* File: c/OP_APUT_BOOLEAN.c */
+HANDLE_OP_APUT(OP_APUT_BOOLEAN, "-boolean", u1, )
+OP_END
+
+/* File: c/OP_APUT_BYTE.c */
+HANDLE_OP_APUT(OP_APUT_BYTE, "-byte", s1, )
+OP_END
+
+/* File: c/OP_APUT_CHAR.c */
+HANDLE_OP_APUT(OP_APUT_CHAR, "-char", u2, )
+OP_END
+
+/* File: c/OP_APUT_SHORT.c */
+HANDLE_OP_APUT(OP_APUT_SHORT, "-short", s2, )
+OP_END
+
+/* File: c/OP_IGET.c */
+HANDLE_IGET_X(OP_IGET, "", Int, )
+OP_END
+
+/* File: c/OP_IGET_WIDE.c */
+HANDLE_IGET_X(OP_IGET_WIDE, "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IGET_OBJECT.c */
+HANDLE_IGET_X(OP_IGET_OBJECT, "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IGET_BOOLEAN.c */
+HANDLE_IGET_X(OP_IGET_BOOLEAN, "", Int, )
+OP_END
+
+/* File: c/OP_IGET_BYTE.c */
+HANDLE_IGET_X(OP_IGET_BYTE, "", Int, )
+OP_END
+
+/* File: c/OP_IGET_CHAR.c */
+HANDLE_IGET_X(OP_IGET_CHAR, "", Int, )
+OP_END
+
+/* File: c/OP_IGET_SHORT.c */
+HANDLE_IGET_X(OP_IGET_SHORT, "", Int, )
+OP_END
+
+/* File: c/OP_IPUT.c */
+HANDLE_IPUT_X(OP_IPUT, "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_WIDE.c */
+HANDLE_IPUT_X(OP_IPUT_WIDE, "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IPUT_OBJECT.c */
+/*
+ * The VM spec says we should verify that the reference being stored into
+ * the field is assignment compatible. In practice, many popular VMs don't
+ * do this because it slows down a very common operation. It's not so bad
+ * for us, since "dexopt" quickens it whenever possible, but it's still an
+ * issue.
+ *
+ * To make this spec-complaint, we'd need to add a ClassObject pointer to
+ * the Field struct, resolve the field's type descriptor at link or class
+ * init time, and then verify the type here.
+ */
+HANDLE_IPUT_X(OP_IPUT_OBJECT, "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IPUT_BOOLEAN.c */
+HANDLE_IPUT_X(OP_IPUT_BOOLEAN, "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_BYTE.c */
+HANDLE_IPUT_X(OP_IPUT_BYTE, "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_CHAR.c */
+HANDLE_IPUT_X(OP_IPUT_CHAR, "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_SHORT.c */
+HANDLE_IPUT_X(OP_IPUT_SHORT, "", Int, )
+OP_END
+
+/* File: c/OP_SGET.c */
+HANDLE_SGET_X(OP_SGET, "", Int, )
+OP_END
+
+/* File: c/OP_SGET_WIDE.c */
+HANDLE_SGET_X(OP_SGET_WIDE, "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_SGET_OBJECT.c */
+HANDLE_SGET_X(OP_SGET_OBJECT, "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SGET_BOOLEAN.c */
+HANDLE_SGET_X(OP_SGET_BOOLEAN, "", Int, )
+OP_END
+
+/* File: c/OP_SGET_BYTE.c */
+HANDLE_SGET_X(OP_SGET_BYTE, "", Int, )
+OP_END
+
+/* File: c/OP_SGET_CHAR.c */
+HANDLE_SGET_X(OP_SGET_CHAR, "", Int, )
+OP_END
+
+/* File: c/OP_SGET_SHORT.c */
+HANDLE_SGET_X(OP_SGET_SHORT, "", Int, )
+OP_END
+
+/* File: c/OP_SPUT.c */
+HANDLE_SPUT_X(OP_SPUT, "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_WIDE.c */
+HANDLE_SPUT_X(OP_SPUT_WIDE, "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_SPUT_OBJECT.c */
+HANDLE_SPUT_X(OP_SPUT_OBJECT, "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SPUT_BOOLEAN.c */
+HANDLE_SPUT_X(OP_SPUT_BOOLEAN, "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_BYTE.c */
+HANDLE_SPUT_X(OP_SPUT_BYTE, "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_CHAR.c */
+HANDLE_SPUT_X(OP_SPUT_CHAR, "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_SHORT.c */
+HANDLE_SPUT_X(OP_SPUT_SHORT, "", Int, )
+OP_END
+
+/* File: c/OP_INVOKE_VIRTUAL.c */
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+ GOTO_invoke(invokeVirtual, false);
+OP_END
+
+/* File: c/OP_INVOKE_SUPER.c */
+HANDLE_OPCODE(OP_INVOKE_SUPER /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+ GOTO_invoke(invokeSuper, false);
+OP_END
+
+/* File: c/OP_INVOKE_DIRECT.c */
+HANDLE_OPCODE(OP_INVOKE_DIRECT /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+ GOTO_invoke(invokeDirect, false);
+OP_END
+
+/* File: c/OP_INVOKE_STATIC.c */
+HANDLE_OPCODE(OP_INVOKE_STATIC /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+ GOTO_invoke(invokeStatic, false);
+OP_END
+
+/* File: c/OP_INVOKE_INTERFACE.c */
+HANDLE_OPCODE(OP_INVOKE_INTERFACE /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+ GOTO_invoke(invokeInterface, false);
+OP_END
+
+/* File: c/OP_UNUSED_73.c */
+HANDLE_OPCODE(OP_UNUSED_73)
+OP_END
+
+/* File: c/OP_INVOKE_VIRTUAL_RANGE.c */
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+ GOTO_invoke(invokeVirtual, true);
+OP_END
+
+/* File: c/OP_INVOKE_SUPER_RANGE.c */
+HANDLE_OPCODE(OP_INVOKE_SUPER_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+ GOTO_invoke(invokeSuper, true);
+OP_END
+
+/* File: c/OP_INVOKE_DIRECT_RANGE.c */
+HANDLE_OPCODE(OP_INVOKE_DIRECT_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+ GOTO_invoke(invokeDirect, true);
+OP_END
+
+/* File: c/OP_INVOKE_STATIC_RANGE.c */
+HANDLE_OPCODE(OP_INVOKE_STATIC_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+ GOTO_invoke(invokeStatic, true);
+OP_END
+
+/* File: c/OP_INVOKE_INTERFACE_RANGE.c */
+HANDLE_OPCODE(OP_INVOKE_INTERFACE_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+ GOTO_invoke(invokeInterface, true);
+OP_END
+
+/* File: c/OP_UNUSED_79.c */
+HANDLE_OPCODE(OP_UNUSED_79)
+OP_END
+
+/* File: c/OP_UNUSED_7A.c */
+HANDLE_OPCODE(OP_UNUSED_7A)
+OP_END
+
+/* File: c/OP_NEG_INT.c */
+HANDLE_UNOP(OP_NEG_INT, "neg-int", -, , )
+OP_END
+
+/* File: c/OP_NOT_INT.c */
+HANDLE_UNOP(OP_NOT_INT, "not-int", , ^ 0xffffffff, )
+OP_END
+
+/* File: c/OP_NEG_LONG.c */
+HANDLE_UNOP(OP_NEG_LONG, "neg-long", -, , _WIDE)
+OP_END
+
+/* File: c/OP_NOT_LONG.c */
+HANDLE_UNOP(OP_NOT_LONG, "not-long", , ^ 0xffffffffffffffffULL, _WIDE)
+OP_END
+
+/* File: c/OP_NEG_FLOAT.c */
+HANDLE_UNOP(OP_NEG_FLOAT, "neg-float", -, , _FLOAT)
+OP_END
+
+/* File: c/OP_NEG_DOUBLE.c */
+HANDLE_UNOP(OP_NEG_DOUBLE, "neg-double", -, , _DOUBLE)
+OP_END
+
+/* File: c/OP_INT_TO_LONG.c */
+HANDLE_NUMCONV(OP_INT_TO_LONG, "int-to-long", _INT, _WIDE)
+OP_END
+
+/* File: c/OP_INT_TO_FLOAT.c */
+HANDLE_NUMCONV(OP_INT_TO_FLOAT, "int-to-float", _INT, _FLOAT)
+OP_END
+
+/* File: c/OP_INT_TO_DOUBLE.c */
+HANDLE_NUMCONV(OP_INT_TO_DOUBLE, "int-to-double", _INT, _DOUBLE)
+OP_END
+
+/* File: c/OP_LONG_TO_INT.c */
+HANDLE_NUMCONV(OP_LONG_TO_INT, "long-to-int", _WIDE, _INT)
+OP_END
+
+/* File: c/OP_LONG_TO_FLOAT.c */
+HANDLE_NUMCONV(OP_LONG_TO_FLOAT, "long-to-float", _WIDE, _FLOAT)
+OP_END
+
+/* File: c/OP_LONG_TO_DOUBLE.c */
+HANDLE_NUMCONV(OP_LONG_TO_DOUBLE, "long-to-double", _WIDE, _DOUBLE)
+OP_END
+
+/* File: c/OP_FLOAT_TO_INT.c */
+HANDLE_FLOAT_TO_INT(OP_FLOAT_TO_INT, "float-to-int",
+ float, _FLOAT, s4, _INT)
+OP_END
+
+/* File: c/OP_FLOAT_TO_LONG.c */
+HANDLE_FLOAT_TO_INT(OP_FLOAT_TO_LONG, "float-to-long",
+ float, _FLOAT, s8, _WIDE)
+OP_END
+
+/* File: c/OP_FLOAT_TO_DOUBLE.c */
+HANDLE_NUMCONV(OP_FLOAT_TO_DOUBLE, "float-to-double", _FLOAT, _DOUBLE)
+OP_END
+
+/* File: c/OP_DOUBLE_TO_INT.c */
+HANDLE_FLOAT_TO_INT(OP_DOUBLE_TO_INT, "double-to-int",
+ double, _DOUBLE, s4, _INT)
+OP_END
+
+/* File: c/OP_DOUBLE_TO_LONG.c */
+HANDLE_FLOAT_TO_INT(OP_DOUBLE_TO_LONG, "double-to-long",
+ double, _DOUBLE, s8, _WIDE)
+OP_END
+
+/* File: c/OP_DOUBLE_TO_FLOAT.c */
+HANDLE_NUMCONV(OP_DOUBLE_TO_FLOAT, "double-to-float", _DOUBLE, _FLOAT)
+OP_END
+
+/* File: c/OP_INT_TO_BYTE.c */
+HANDLE_INT_TO_SMALL(OP_INT_TO_BYTE, "byte", s1)
+OP_END
+
+/* File: c/OP_INT_TO_CHAR.c */
+HANDLE_INT_TO_SMALL(OP_INT_TO_CHAR, "char", u2)
+OP_END
+
+/* File: c/OP_INT_TO_SHORT.c */
+HANDLE_INT_TO_SMALL(OP_INT_TO_SHORT, "short", s2) /* want sign bit */
+OP_END
+
+/* File: c/OP_ADD_INT.c */
+HANDLE_OP_X_INT(OP_ADD_INT, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_INT.c */
+HANDLE_OP_X_INT(OP_SUB_INT, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_INT.c */
+HANDLE_OP_X_INT(OP_MUL_INT, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT.c */
+HANDLE_OP_X_INT(OP_DIV_INT, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT.c */
+HANDLE_OP_X_INT(OP_REM_INT, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT.c */
+HANDLE_OP_X_INT(OP_AND_INT, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT.c */
+HANDLE_OP_X_INT(OP_OR_INT, "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT.c */
+HANDLE_OP_X_INT(OP_XOR_INT, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_INT.c */
+HANDLE_OP_SHX_INT(OP_SHL_INT, "shl", (s4), <<)
+OP_END
+
+/* File: c/OP_SHR_INT.c */
+HANDLE_OP_SHX_INT(OP_SHR_INT, "shr", (s4), >>)
+OP_END
+
+/* File: c/OP_USHR_INT.c */
+HANDLE_OP_SHX_INT(OP_USHR_INT, "ushr", (u4), >>)
+OP_END
+
+/* File: c/OP_ADD_LONG.c */
+HANDLE_OP_X_LONG(OP_ADD_LONG, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_LONG.c */
+HANDLE_OP_X_LONG(OP_SUB_LONG, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_LONG.c */
+HANDLE_OP_X_LONG(OP_MUL_LONG, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_LONG.c */
+HANDLE_OP_X_LONG(OP_DIV_LONG, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_LONG.c */
+HANDLE_OP_X_LONG(OP_REM_LONG, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_LONG.c */
+HANDLE_OP_X_LONG(OP_AND_LONG, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_LONG.c */
+HANDLE_OP_X_LONG(OP_OR_LONG, "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_LONG.c */
+HANDLE_OP_X_LONG(OP_XOR_LONG, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_LONG.c */
+HANDLE_OP_SHX_LONG(OP_SHL_LONG, "shl", (s8), <<)
+OP_END
+
+/* File: c/OP_SHR_LONG.c */
+HANDLE_OP_SHX_LONG(OP_SHR_LONG, "shr", (s8), >>)
+OP_END
+
+/* File: c/OP_USHR_LONG.c */
+HANDLE_OP_SHX_LONG(OP_USHR_LONG, "ushr", (u8), >>)
+OP_END
+
+/* File: c/OP_ADD_FLOAT.c */
+HANDLE_OP_X_FLOAT(OP_ADD_FLOAT, "add", +)
+OP_END
+
+/* File: c/OP_SUB_FLOAT.c */
+HANDLE_OP_X_FLOAT(OP_SUB_FLOAT, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_FLOAT.c */
+HANDLE_OP_X_FLOAT(OP_MUL_FLOAT, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_FLOAT.c */
+HANDLE_OP_X_FLOAT(OP_DIV_FLOAT, "div", /)
+OP_END
+
+/* File: c/OP_REM_FLOAT.c */
+HANDLE_OPCODE(OP_REM_FLOAT /*vAA, vBB, vCC*/)
+ {
+ u2 srcRegs;
+ vdst = INST_AA(inst);
+ srcRegs = FETCH(1);
+ vsrc1 = srcRegs & 0xff;
+ vsrc2 = srcRegs >> 8;
+ ILOGV("|%s-float v%d,v%d,v%d", "mod", vdst, vsrc1, vsrc2);
+ SET_REGISTER_FLOAT(vdst,
+ fmodf(GET_REGISTER_FLOAT(vsrc1), GET_REGISTER_FLOAT(vsrc2)));
+ }
+ FINISH(2);
+OP_END
+
+/* File: c/OP_ADD_DOUBLE.c */
+HANDLE_OP_X_DOUBLE(OP_ADD_DOUBLE, "add", +)
+OP_END
+
+/* File: c/OP_SUB_DOUBLE.c */
+HANDLE_OP_X_DOUBLE(OP_SUB_DOUBLE, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_DOUBLE.c */
+HANDLE_OP_X_DOUBLE(OP_MUL_DOUBLE, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_DOUBLE.c */
+HANDLE_OP_X_DOUBLE(OP_DIV_DOUBLE, "div", /)
+OP_END
+
+/* File: c/OP_REM_DOUBLE.c */
+HANDLE_OPCODE(OP_REM_DOUBLE /*vAA, vBB, vCC*/)
+ {
+ u2 srcRegs;
+ vdst = INST_AA(inst);
+ srcRegs = FETCH(1);
+ vsrc1 = srcRegs & 0xff;
+ vsrc2 = srcRegs >> 8;
+ ILOGV("|%s-double v%d,v%d,v%d", "mod", vdst, vsrc1, vsrc2);
+ SET_REGISTER_DOUBLE(vdst,
+ fmod(GET_REGISTER_DOUBLE(vsrc1), GET_REGISTER_DOUBLE(vsrc2)));
+ }
+ FINISH(2);
+OP_END
+
+/* File: c/OP_ADD_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_ADD_INT_2ADDR, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_SUB_INT_2ADDR, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_MUL_INT_2ADDR, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_DIV_INT_2ADDR, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_REM_INT_2ADDR, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_AND_INT_2ADDR, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_OR_INT_2ADDR, "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_XOR_INT_2ADDR, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_INT_2ADDR.c */
+HANDLE_OP_SHX_INT_2ADDR(OP_SHL_INT_2ADDR, "shl", (s4), <<)
+OP_END
+
+/* File: c/OP_SHR_INT_2ADDR.c */
+HANDLE_OP_SHX_INT_2ADDR(OP_SHR_INT_2ADDR, "shr", (s4), >>)
+OP_END
+
+/* File: c/OP_USHR_INT_2ADDR.c */
+HANDLE_OP_SHX_INT_2ADDR(OP_USHR_INT_2ADDR, "ushr", (u4), >>)
+OP_END
+
+/* File: c/OP_ADD_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_ADD_LONG_2ADDR, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_SUB_LONG_2ADDR, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_MUL_LONG_2ADDR, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_DIV_LONG_2ADDR, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_REM_LONG_2ADDR, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_AND_LONG_2ADDR, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_OR_LONG_2ADDR, "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_XOR_LONG_2ADDR, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_LONG_2ADDR.c */
+HANDLE_OP_SHX_LONG_2ADDR(OP_SHL_LONG_2ADDR, "shl", (s8), <<)
+OP_END
+
+/* File: c/OP_SHR_LONG_2ADDR.c */
+HANDLE_OP_SHX_LONG_2ADDR(OP_SHR_LONG_2ADDR, "shr", (s8), >>)
+OP_END
+
+/* File: c/OP_USHR_LONG_2ADDR.c */
+HANDLE_OP_SHX_LONG_2ADDR(OP_USHR_LONG_2ADDR, "ushr", (u8), >>)
+OP_END
+
+/* File: c/OP_ADD_FLOAT_2ADDR.c */
+HANDLE_OP_X_FLOAT_2ADDR(OP_ADD_FLOAT_2ADDR, "add", +)
+OP_END
+
+/* File: c/OP_SUB_FLOAT_2ADDR.c */
+HANDLE_OP_X_FLOAT_2ADDR(OP_SUB_FLOAT_2ADDR, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_FLOAT_2ADDR.c */
+HANDLE_OP_X_FLOAT_2ADDR(OP_MUL_FLOAT_2ADDR, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_FLOAT_2ADDR.c */
+HANDLE_OP_X_FLOAT_2ADDR(OP_DIV_FLOAT_2ADDR, "div", /)
+OP_END
+
+/* File: c/OP_REM_FLOAT_2ADDR.c */
+HANDLE_OPCODE(OP_REM_FLOAT_2ADDR /*vA, vB*/)
+ vdst = INST_A(inst);
+ vsrc1 = INST_B(inst);
+ ILOGV("|%s-float-2addr v%d,v%d", "mod", vdst, vsrc1);
+ SET_REGISTER_FLOAT(vdst,
+ fmodf(GET_REGISTER_FLOAT(vdst), GET_REGISTER_FLOAT(vsrc1)));
+ FINISH(1);
+OP_END
+
+/* File: c/OP_ADD_DOUBLE_2ADDR.c */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_ADD_DOUBLE_2ADDR, "add", +)
+OP_END
+
+/* File: c/OP_SUB_DOUBLE_2ADDR.c */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_SUB_DOUBLE_2ADDR, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_DOUBLE_2ADDR.c */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_MUL_DOUBLE_2ADDR, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_DOUBLE_2ADDR.c */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_DIV_DOUBLE_2ADDR, "div", /)
+OP_END
+
+/* File: c/OP_REM_DOUBLE_2ADDR.c */
+HANDLE_OPCODE(OP_REM_DOUBLE_2ADDR /*vA, vB*/)
+ vdst = INST_A(inst);
+ vsrc1 = INST_B(inst);
+ ILOGV("|%s-double-2addr v%d,v%d", "mod", vdst, vsrc1);
+ SET_REGISTER_DOUBLE(vdst,
+ fmod(GET_REGISTER_DOUBLE(vdst), GET_REGISTER_DOUBLE(vsrc1)));
+ FINISH(1);
+OP_END
+
+/* File: c/OP_ADD_INT_LIT16.c */
+HANDLE_OP_X_INT_LIT16(OP_ADD_INT_LIT16, "add", +, 0)
+OP_END
+
+/* File: c/OP_RSUB_INT.c */
+HANDLE_OPCODE(OP_RSUB_INT /*vA, vB, #+CCCC*/)
+ {
+ vdst = INST_A(inst);
+ vsrc1 = INST_B(inst);
+ vsrc2 = FETCH(1);
+ ILOGV("|rsub-int v%d,v%d,#+0x%04x", vdst, vsrc1, vsrc2);
+ SET_REGISTER(vdst, (s2) vsrc2 - (s4) GET_REGISTER(vsrc1));
+ }
+ FINISH(2);
+OP_END
+
+/* File: c/OP_MUL_INT_LIT16.c */
+HANDLE_OP_X_INT_LIT16(OP_MUL_INT_LIT16, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT_LIT16.c */
+HANDLE_OP_X_INT_LIT16(OP_DIV_INT_LIT16, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT_LIT16.c */
+HANDLE_OP_X_INT_LIT16(OP_REM_INT_LIT16, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT_LIT16.c */
+HANDLE_OP_X_INT_LIT16(OP_AND_INT_LIT16, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT_LIT16.c */
+HANDLE_OP_X_INT_LIT16(OP_OR_INT_LIT16, "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT_LIT16.c */
+HANDLE_OP_X_INT_LIT16(OP_XOR_INT_LIT16, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_ADD_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_ADD_INT_LIT8, "add", +, 0)
+OP_END
+
+/* File: c/OP_RSUB_INT_LIT8.c */
+HANDLE_OPCODE(OP_RSUB_INT_LIT8 /*vAA, vBB, #+CC*/)
+ {
+ u2 litInfo;
+ vdst = INST_AA(inst);
+ litInfo = FETCH(1);
+ vsrc1 = litInfo & 0xff;
+ vsrc2 = litInfo >> 8;
+ ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x", "rsub", vdst, vsrc1, vsrc2);
+ SET_REGISTER(vdst, (s1) vsrc2 - (s4) GET_REGISTER(vsrc1));
+ }
+ FINISH(2);
+OP_END
+
+/* File: c/OP_MUL_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_MUL_INT_LIT8, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_DIV_INT_LIT8, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_REM_INT_LIT8, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_AND_INT_LIT8, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_OR_INT_LIT8, "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_XOR_INT_LIT8, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_INT_LIT8.c */
+HANDLE_OP_SHX_INT_LIT8(OP_SHL_INT_LIT8, "shl", (s4), <<)
+OP_END
+
+/* File: c/OP_SHR_INT_LIT8.c */
+HANDLE_OP_SHX_INT_LIT8(OP_SHR_INT_LIT8, "shr", (s4), >>)
+OP_END
+
+/* File: c/OP_USHR_INT_LIT8.c */
+HANDLE_OP_SHX_INT_LIT8(OP_USHR_INT_LIT8, "ushr", (u4), >>)
+OP_END
+
+/* File: c/OP_IGET_VOLATILE.c */
+HANDLE_IGET_X(OP_IGET_VOLATILE, "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_IPUT_VOLATILE.c */
+HANDLE_IPUT_X(OP_IPUT_VOLATILE, "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_SGET_VOLATILE.c */
+HANDLE_SGET_X(OP_SGET_VOLATILE, "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_SPUT_VOLATILE.c */
+HANDLE_SPUT_X(OP_SPUT_VOLATILE, "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_IGET_OBJECT_VOLATILE.c */
+HANDLE_IGET_X(OP_IGET_OBJECT_VOLATILE, "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IGET_WIDE_VOLATILE.c */
+HANDLE_IGET_X(OP_IGET_WIDE_VOLATILE, "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_IPUT_WIDE_VOLATILE.c */
+HANDLE_IPUT_X(OP_IPUT_WIDE_VOLATILE, "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_SGET_WIDE_VOLATILE.c */
+HANDLE_SGET_X(OP_SGET_WIDE_VOLATILE, "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_SPUT_WIDE_VOLATILE.c */
+HANDLE_SPUT_X(OP_SPUT_WIDE_VOLATILE, "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_BREAKPOINT.c */
+HANDLE_OPCODE(OP_BREAKPOINT)
+#if (INTERP_TYPE == INTERP_DBG)
+ {
+ /*
+ * Restart this instruction with the original opcode. We do
+ * this by simply jumping to the handler.
+ *
+ * It's probably not necessary to update "inst", but we do it
+ * for the sake of anything that needs to do disambiguation in a
+ * common handler with INST_INST.
+ *
+ * The breakpoint itself is handled over in updateDebugger(),
+ * because we need to detect other events (method entry, single
+ * step) and report them in the same event packet, and we're not
+ * yet handling those through breakpoint instructions. By the
+ * time we get here, the breakpoint has already been handled and
+ * the thread resumed.
+ */
+ u1 originalOpCode = dvmGetOriginalOpCode(pc);
+ LOGV("+++ break 0x%02x (0x%04x -> 0x%04x)\n", originalOpCode, inst,
+ INST_REPLACE_OP(inst, originalOpCode));
+ inst = INST_REPLACE_OP(inst, originalOpCode);
+ FINISH_BKPT(originalOpCode);
+ }
+#else
+ LOGE("Breakpoint hit in non-debug interpreter\n");
+ dvmAbort();
+#endif
+OP_END
+
+/* File: c/OP_THROW_VERIFICATION_ERROR.c */
+HANDLE_OPCODE(OP_THROW_VERIFICATION_ERROR)
+ EXPORT_PC();
+ vsrc1 = INST_AA(inst);
+ ref = FETCH(1); /* class/field/method ref */
+ dvmThrowVerificationError(curMethod, vsrc1, ref);
+ GOTO_exceptionThrown();
+OP_END
+
+/* File: c/OP_EXECUTE_INLINE.c */
+HANDLE_OPCODE(OP_EXECUTE_INLINE /*vB, {vD, vE, vF, vG}, inline@CCCC*/)
+ {
+ /*
+ * This has the same form as other method calls, but we ignore
+ * the 5th argument (vA). This is chiefly because the first four
+ * arguments to a function on ARM are in registers.
+ *
+ * We only set the arguments that are actually used, leaving
+ * the rest uninitialized. We're assuming that, if the method
+ * needs them, they'll be specified in the call.
+ *
+ * However, this annoys gcc when optimizations are enabled,
+ * causing a "may be used uninitialized" warning. Quieting
+ * the warnings incurs a slight penalty (5%: 373ns vs. 393ns
+ * on empty method). Note that valgrind is perfectly happy
+ * either way as the uninitialiezd values are never actually
+ * used.
+ */
+ u4 arg0, arg1, arg2, arg3;
+ arg0 = arg1 = arg2 = arg3 = 0;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_B(inst); /* #of args */
+ ref = FETCH(1); /* inline call "ref" */
+ vdst = FETCH(2); /* 0-4 register indices */
+ ILOGV("|execute-inline args=%d @%d {regs=0x%04x}",
+ vsrc1, ref, vdst);
+
+ assert((vdst >> 16) == 0); // 16-bit type -or- high 16 bits clear
+ assert(vsrc1 <= 4);
+
+ switch (vsrc1) {
+ case 4:
+ arg3 = GET_REGISTER(vdst >> 12);
+ /* fall through */
+ case 3:
+ arg2 = GET_REGISTER((vdst & 0x0f00) >> 8);
+ /* fall through */
+ case 2:
+ arg1 = GET_REGISTER((vdst & 0x00f0) >> 4);
+ /* fall through */
+ case 1:
+ arg0 = GET_REGISTER(vdst & 0x0f);
+ /* fall through */
+ default: // case 0
+ ;
+ }
+
+#if INTERP_TYPE == INTERP_DBG
+ if (!dvmPerformInlineOp4Dbg(arg0, arg1, arg2, arg3, &retval, ref))
+ GOTO_exceptionThrown();
+#else
+ if (!dvmPerformInlineOp4Std(arg0, arg1, arg2, arg3, &retval, ref))
+ GOTO_exceptionThrown();
+#endif
+ }
+ FINISH(3);
+OP_END
+
+/* File: c/OP_EXECUTE_INLINE_RANGE.c */
+HANDLE_OPCODE(OP_EXECUTE_INLINE_RANGE /*{vCCCC..v(CCCC+AA-1)}, inline@BBBB*/)
+ {
+ u4 arg0, arg1, arg2, arg3;
+ arg0 = arg1 = arg2 = arg3 = 0; /* placate gcc */
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst); /* #of args */
+ ref = FETCH(1); /* inline call "ref" */
+ vdst = FETCH(2); /* range base */
+ ILOGV("|execute-inline-range args=%d @%d {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+
+ assert((vdst >> 16) == 0); // 16-bit type -or- high 16 bits clear
+ assert(vsrc1 <= 4);
+
+ switch (vsrc1) {
+ case 4:
+ arg3 = GET_REGISTER(vdst+3);
+ /* fall through */
+ case 3:
+ arg2 = GET_REGISTER(vdst+2);
+ /* fall through */
+ case 2:
+ arg1 = GET_REGISTER(vdst+1);
+ /* fall through */
+ case 1:
+ arg0 = GET_REGISTER(vdst+0);
+ /* fall through */
+ default: // case 0
+ ;
+ }
+
+#if INTERP_TYPE == INTERP_DBG
+ if (!dvmPerformInlineOp4Dbg(arg0, arg1, arg2, arg3, &retval, ref))
+ GOTO_exceptionThrown();
+#else
+ if (!dvmPerformInlineOp4Std(arg0, arg1, arg2, arg3, &retval, ref))
+ GOTO_exceptionThrown();
+#endif
+ }
+ FINISH(3);
+OP_END
+
+/* File: c/OP_INVOKE_DIRECT_EMPTY.c */
+HANDLE_OPCODE(OP_INVOKE_DIRECT_EMPTY /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+#if INTERP_TYPE != INTERP_DBG
+ //LOGI("Ignoring empty\n");
+ FINISH(3);
+#else
+ if (!gDvm.debuggerActive) {
+ //LOGI("Skipping empty\n");
+ FINISH(3); // don't want it to show up in profiler output
+ } else {
+ //LOGI("Running empty\n");
+ /* fall through to OP_INVOKE_DIRECT */
+ GOTO_invoke(invokeDirect, false);
+ }
+#endif
+OP_END
+
+/* File: c/OP_UNUSED_F1.c */
+HANDLE_OPCODE(OP_UNUSED_F1)
+OP_END
+
+/* File: c/OP_IGET_QUICK.c */
+HANDLE_IGET_X_QUICK(OP_IGET_QUICK, "", Int, )
+OP_END
+
+/* File: c/OP_IGET_WIDE_QUICK.c */
+HANDLE_IGET_X_QUICK(OP_IGET_WIDE_QUICK, "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IGET_OBJECT_QUICK.c */
+HANDLE_IGET_X_QUICK(OP_IGET_OBJECT_QUICK, "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IPUT_QUICK.c */
+HANDLE_IPUT_X_QUICK(OP_IPUT_QUICK, "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_WIDE_QUICK.c */
+HANDLE_IPUT_X_QUICK(OP_IPUT_WIDE_QUICK, "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IPUT_OBJECT_QUICK.c */
+HANDLE_IPUT_X_QUICK(OP_IPUT_OBJECT_QUICK, "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_INVOKE_VIRTUAL_QUICK.c */
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL_QUICK /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+ GOTO_invoke(invokeVirtualQuick, false);
+OP_END
+
+/* File: c/OP_INVOKE_VIRTUAL_QUICK_RANGE.c */
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL_QUICK_RANGE/*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+ GOTO_invoke(invokeVirtualQuick, true);
+OP_END
+
+/* File: c/OP_INVOKE_SUPER_QUICK.c */
+HANDLE_OPCODE(OP_INVOKE_SUPER_QUICK /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+ GOTO_invoke(invokeSuperQuick, false);
+OP_END
+
+/* File: c/OP_INVOKE_SUPER_QUICK_RANGE.c */
+HANDLE_OPCODE(OP_INVOKE_SUPER_QUICK_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+ GOTO_invoke(invokeSuperQuick, true);
+OP_END
+
+/* File: c/OP_IPUT_OBJECT_VOLATILE.c */
+HANDLE_IPUT_X(OP_IPUT_OBJECT_VOLATILE, "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SGET_OBJECT_VOLATILE.c */
+HANDLE_SGET_X(OP_SGET_OBJECT_VOLATILE, "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SPUT_OBJECT_VOLATILE.c */
+HANDLE_SPUT_X(OP_SPUT_OBJECT_VOLATILE, "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_UNUSED_FF.c */
+HANDLE_OPCODE(OP_UNUSED_FF)
+ /*
+ * In portable interp, most unused opcodes will fall through to here.
+ */
+ LOGE("unknown opcode 0x%02x\n", INST_INST(inst));
+ dvmAbort();
+ FINISH(1);
+OP_END
+
+/* File: cstubs/entry.c */
+/*
+ * Handler function table, one entry per opcode.
+ */
+#undef H
+#define H(_op) dvmMterp_##_op
+DEFINE_GOTO_TABLE(gDvmMterpHandlers)
+
+#undef H
+#define H(_op) #_op
+DEFINE_GOTO_TABLE(gDvmMterpHandlerNames)
+
+#include <setjmp.h>
+
+/*
+ * C mterp entry point. This just calls the various C fallbacks, making
+ * this a slow but portable interpeter.
+ *
+ * This is only used for the "allstubs" variant.
+ */
+bool dvmMterpStdRun(MterpGlue* glue)
+{
+ jmp_buf jmpBuf;
+ int changeInterp;
+
+ glue->bailPtr = &jmpBuf;
+
+ /*
+ * We want to return "changeInterp" as a boolean, but we can't return
+ * zero through longjmp, so we return (boolean+1).
+ */
+ changeInterp = setjmp(jmpBuf) -1;
+ if (changeInterp >= 0) {
+ Thread* threadSelf = dvmThreadSelf();
+ LOGVV("mterp threadid=%d returning %d\n",
+ threadSelf->threadId, changeInterp);
+ return changeInterp;
+ }
+
+ /*
+ * We may not be starting at a point where we're executing instructions.
+ * We need to pick up where the other interpreter left off.
+ *
+ * In some cases we need to call into a throw/return handler which
+ * will do some processing and then either return to us (updating "glue")
+ * or longjmp back out.
+ */
+ switch (glue->entryPoint) {
+ case kInterpEntryInstr:
+ /* just start at the start */
+ break;
+ case kInterpEntryReturn:
+ dvmMterp_returnFromMethod(glue);
+ break;
+ case kInterpEntryThrow:
+ dvmMterp_exceptionThrown(glue);
+ break;
+ default:
+ dvmAbort();
+ }
+
+ /* run until somebody longjmp()s out */
+ while (true) {
+ typedef void (*Handler)(MterpGlue* glue);
+
+ u2 inst = /*glue->*/pc[0];
+ Handler handler = (Handler) gDvmMterpHandlers[inst & 0xff];
+ LOGVV("handler %p %s\n",
+ handler, (const char*) gDvmMterpHandlerNames[inst & 0xff]);
+ (*handler)(glue);
+ }
+}
+
+/*
+ * C mterp exit point. Call here to bail out of the interpreter.
+ */
+void dvmMterpStdBail(MterpGlue* glue, bool changeInterp)
+{
+ jmp_buf* pJmpBuf = glue->bailPtr;
+ longjmp(*pJmpBuf, ((int)changeInterp)+1);
+}
+
+/* File: c/gotoTargets.c */
+/*
+ * C footer. This has some common code shared by the various targets.
+ */
+
+/*
+ * Everything from here on is a "goto target". In the basic interpreter
+ * we jump into these targets and then jump directly to the handler for
+ * next instruction. Here, these are subroutines that return to the caller.
+ */
+
+GOTO_TARGET(filledNewArray, bool methodCallRange)
+ {
+ ClassObject* arrayClass;
+ ArrayObject* newArray;
+ u4* contents;
+ char typeCh;
+ int i;
+ u4 arg5;
+
+ EXPORT_PC();
+
+ ref = FETCH(1); /* class ref */
+ vdst = FETCH(2); /* first 4 regs -or- range base */
+
+ if (methodCallRange) {
+ vsrc1 = INST_AA(inst); /* #of elements */
+ arg5 = -1; /* silence compiler warning */
+ ILOGV("|filled-new-array-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ } else {
+ arg5 = INST_A(inst);
+ vsrc1 = INST_B(inst); /* #of elements */
+ ILOGV("|filled-new-array args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1, ref, vdst, arg5);
+ }
+
+ /*
+ * Resolve the array class.
+ */
+ arrayClass = dvmDexGetResolvedClass(methodClassDex, ref);
+ if (arrayClass == NULL) {
+ arrayClass = dvmResolveClass(curMethod->clazz, ref, false);
+ if (arrayClass == NULL)
+ GOTO_exceptionThrown();
+ }
+ /*
+ if (!dvmIsArrayClass(arrayClass)) {
+ dvmThrowException("Ljava/lang/RuntimeError;",
+ "filled-new-array needs array class");
+ GOTO_exceptionThrown();
+ }
+ */
+ /* verifier guarantees this is an array class */
+ assert(dvmIsArrayClass(arrayClass));
+ assert(dvmIsClassInitialized(arrayClass));
+
+ /*
+ * Create an array of the specified type.
+ */
+ LOGVV("+++ filled-new-array type is '%s'\n", arrayClass->descriptor);
+ typeCh = arrayClass->descriptor[1];
+ if (typeCh == 'D' || typeCh == 'J') {
+ /* category 2 primitives not allowed */
+ dvmThrowException("Ljava/lang/RuntimeError;",
+ "bad filled array req");
+ GOTO_exceptionThrown();
+ } else if (typeCh != 'L' && typeCh != '[' && typeCh != 'I') {
+ /* TODO: requires multiple "fill in" loops with different widths */
+ LOGE("non-int primitives not implemented\n");
+ dvmThrowException("Ljava/lang/InternalError;",
+ "filled-new-array not implemented for anything but 'int'");
+ GOTO_exceptionThrown();
+ }
+
+ newArray = dvmAllocArrayByClass(arrayClass, vsrc1, ALLOC_DONT_TRACK);
+ if (newArray == NULL)
+ GOTO_exceptionThrown();
+
+ /*
+ * Fill in the elements. It's legal for vsrc1 to be zero.
+ */
+ contents = (u4*) newArray->contents;
+ if (methodCallRange) {
+ for (i = 0; i < vsrc1; i++)
+ contents[i] = GET_REGISTER(vdst+i);
+ } else {
+ assert(vsrc1 <= 5);
+ if (vsrc1 == 5) {
+ contents[4] = GET_REGISTER(arg5);
+ vsrc1--;
+ }
+ for (i = 0; i < vsrc1; i++) {
+ contents[i] = GET_REGISTER(vdst & 0x0f);
+ vdst >>= 4;
+ }
+ }
+ if (typeCh == 'L' || typeCh == '[') {
+ dvmWriteBarrierArray(newArray, 0, newArray->length);
+ }
+
+ retval.l = newArray;
+ }
+ FINISH(3);
+GOTO_TARGET_END
+
+
+GOTO_TARGET(invokeVirtual, bool methodCallRange)
+ {
+ Method* baseMethod;
+ Object* thisPtr;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* method ref */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ /*
+ * The object against which we are executing a method is always
+ * in the first argument.
+ */
+ if (methodCallRange) {
+ assert(vsrc1 > 0);
+ ILOGV("|invoke-virtual-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ thisPtr = (Object*) GET_REGISTER(vdst);
+ } else {
+ assert((vsrc1>>4) > 0);
+ ILOGV("|invoke-virtual args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+ thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+ }
+
+ if (!checkForNull(thisPtr))
+ GOTO_exceptionThrown();
+
+ /*
+ * Resolve the method. This is the correct method for the static
+ * type of the object. We also verify access permissions here.
+ */
+ baseMethod = dvmDexGetResolvedMethod(methodClassDex, ref);
+ if (baseMethod == NULL) {
+ baseMethod = dvmResolveMethod(curMethod->clazz, ref,METHOD_VIRTUAL);
+ if (baseMethod == NULL) {
+ ILOGV("+ unknown method or access denied\n");
+ GOTO_exceptionThrown();
+ }
+ }
+
+ /*
+ * Combine the object we found with the vtable offset in the
+ * method.
+ */
+ assert(baseMethod->methodIndex < thisPtr->clazz->vtableCount);
+ methodToCall = thisPtr->clazz->vtable[baseMethod->methodIndex];
+
+#if defined(WITH_JIT) && (INTERP_TYPE == INTERP_DBG)
+ callsiteClass = thisPtr->clazz;
+#endif
+
+#if 0
+ if (dvmIsAbstractMethod(methodToCall)) {
+ /*
+ * This can happen if you create two classes, Base and Sub, where
+ * Sub is a sub-class of Base. Declare a protected abstract
+ * method foo() in Base, and invoke foo() from a method in Base.
+ * Base is an "abstract base class" and is never instantiated
+ * directly. Now, Override foo() in Sub, and use Sub. This
+ * Works fine unless Sub stops providing an implementation of
+ * the method.
+ */
+ dvmThrowException("Ljava/lang/AbstractMethodError;",
+ "abstract method not implemented");
+ GOTO_exceptionThrown();
+ }
+#else
+ assert(!dvmIsAbstractMethod(methodToCall) ||
+ methodToCall->nativeFunc != NULL);
+#endif
+
+ LOGVV("+++ base=%s.%s virtual[%d]=%s.%s\n",
+ baseMethod->clazz->descriptor, baseMethod->name,
+ (u4) baseMethod->methodIndex,
+ methodToCall->clazz->descriptor, methodToCall->name);
+ assert(methodToCall != NULL);
+
+#if 0
+ if (vsrc1 != methodToCall->insSize) {
+ LOGW("WRONG METHOD: base=%s.%s virtual[%d]=%s.%s\n",
+ baseMethod->clazz->descriptor, baseMethod->name,
+ (u4) baseMethod->methodIndex,
+ methodToCall->clazz->descriptor, methodToCall->name);
+ //dvmDumpClass(baseMethod->clazz);
+ //dvmDumpClass(methodToCall->clazz);
+ dvmDumpAllClasses(0);
+ }
+#endif
+
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+ }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeSuper, bool methodCallRange)
+ {
+ Method* baseMethod;
+ u2 thisReg;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* method ref */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ if (methodCallRange) {
+ ILOGV("|invoke-super-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ thisReg = vdst;
+ } else {
+ ILOGV("|invoke-super args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+ thisReg = vdst & 0x0f;
+ }
+ /* impossible in well-formed code, but we must check nevertheless */
+ if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+ GOTO_exceptionThrown();
+
+ /*
+ * Resolve the method. This is the correct method for the static
+ * type of the object. We also verify access permissions here.
+ * The first arg to dvmResolveMethod() is just the referring class
+ * (used for class loaders and such), so we don't want to pass
+ * the superclass into the resolution call.
+ */
+ baseMethod = dvmDexGetResolvedMethod(methodClassDex, ref);
+ if (baseMethod == NULL) {
+ baseMethod = dvmResolveMethod(curMethod->clazz, ref,METHOD_VIRTUAL);
+ if (baseMethod == NULL) {
+ ILOGV("+ unknown method or access denied\n");
+ GOTO_exceptionThrown();
+ }
+ }
+
+ /*
+ * Combine the object we found with the vtable offset in the
+ * method's class.
+ *
+ * We're using the current method's class' superclass, not the
+ * superclass of "this". This is because we might be executing
+ * in a method inherited from a superclass, and we want to run
+ * in that class' superclass.
+ */
+ if (baseMethod->methodIndex >= curMethod->clazz->super->vtableCount) {
+ /*
+ * Method does not exist in the superclass. Could happen if
+ * superclass gets updated.
+ */
+ dvmThrowException("Ljava/lang/NoSuchMethodError;",
+ baseMethod->name);
+ GOTO_exceptionThrown();
+ }
+ methodToCall = curMethod->clazz->super->vtable[baseMethod->methodIndex];
+#if 0
+ if (dvmIsAbstractMethod(methodToCall)) {
+ dvmThrowException("Ljava/lang/AbstractMethodError;",
+ "abstract method not implemented");
+ GOTO_exceptionThrown();
+ }
+#else
+ assert(!dvmIsAbstractMethod(methodToCall) ||
+ methodToCall->nativeFunc != NULL);
+#endif
+ LOGVV("+++ base=%s.%s super-virtual=%s.%s\n",
+ baseMethod->clazz->descriptor, baseMethod->name,
+ methodToCall->clazz->descriptor, methodToCall->name);
+ assert(methodToCall != NULL);
+
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+ }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeInterface, bool methodCallRange)
+ {
+ Object* thisPtr;
+ ClassObject* thisClass;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* method ref */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ /*
+ * The object against which we are executing a method is always
+ * in the first argument.
+ */
+ if (methodCallRange) {
+ assert(vsrc1 > 0);
+ ILOGV("|invoke-interface-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ thisPtr = (Object*) GET_REGISTER(vdst);
+ } else {
+ assert((vsrc1>>4) > 0);
+ ILOGV("|invoke-interface args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+ thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+ }
+ if (!checkForNull(thisPtr))
+ GOTO_exceptionThrown();
+
+ thisClass = thisPtr->clazz;
+
+#if defined(WITH_JIT) && (INTERP_TYPE == INTERP_DBG)
+ callsiteClass = thisClass;
+#endif
+
+ /*
+ * Given a class and a method index, find the Method* with the
+ * actual code we want to execute.
+ */
+ methodToCall = dvmFindInterfaceMethodInCache(thisClass, ref, curMethod,
+ methodClassDex);
+ if (methodToCall == NULL) {
+ assert(dvmCheckException(self));
+ GOTO_exceptionThrown();
+ }
+
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+ }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeDirect, bool methodCallRange)
+ {
+ u2 thisReg;
+
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* method ref */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ EXPORT_PC();
+
+ if (methodCallRange) {
+ ILOGV("|invoke-direct-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ thisReg = vdst;
+ } else {
+ ILOGV("|invoke-direct args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+ thisReg = vdst & 0x0f;
+ }
+ if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+ GOTO_exceptionThrown();
+
+ methodToCall = dvmDexGetResolvedMethod(methodClassDex, ref);
+ if (methodToCall == NULL) {
+ methodToCall = dvmResolveMethod(curMethod->clazz, ref,
+ METHOD_DIRECT);
+ if (methodToCall == NULL) {
+ ILOGV("+ unknown direct method\n"); // should be impossible
+ GOTO_exceptionThrown();
+ }
+ }
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+ }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeStatic, bool methodCallRange)
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* method ref */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ EXPORT_PC();
+
+ if (methodCallRange)
+ ILOGV("|invoke-static-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ else
+ ILOGV("|invoke-static args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+
+ methodToCall = dvmDexGetResolvedMethod(methodClassDex, ref);
+ if (methodToCall == NULL) {
+ methodToCall = dvmResolveMethod(curMethod->clazz, ref, METHOD_STATIC);
+ if (methodToCall == NULL) {
+ ILOGV("+ unknown method\n");
+ GOTO_exceptionThrown();
+ }
+
+ /*
+ * The JIT needs dvmDexGetResolvedMethod() to return non-null.
+ * Since we use the portable interpreter to build the trace, this extra
+ * check is not needed for mterp.
+ */
+ if (dvmDexGetResolvedMethod(methodClassDex, ref) == NULL) {
+ /* Class initialization is still ongoing */
+ ABORT_JIT_TSELECT();
+ }
+ }
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeVirtualQuick, bool methodCallRange)
+ {
+ Object* thisPtr;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* vtable index */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ /*
+ * The object against which we are executing a method is always
+ * in the first argument.
+ */
+ if (methodCallRange) {
+ assert(vsrc1 > 0);
+ ILOGV("|invoke-virtual-quick-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ thisPtr = (Object*) GET_REGISTER(vdst);
+ } else {
+ assert((vsrc1>>4) > 0);
+ ILOGV("|invoke-virtual-quick args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+ thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+ }
+
+ if (!checkForNull(thisPtr))
+ GOTO_exceptionThrown();
+
+#if defined(WITH_JIT) && (INTERP_TYPE == INTERP_DBG)
+ callsiteClass = thisPtr->clazz;
+#endif
+
+ /*
+ * Combine the object we found with the vtable offset in the
+ * method.
+ */
+ assert(ref < thisPtr->clazz->vtableCount);
+ methodToCall = thisPtr->clazz->vtable[ref];
+
+#if 0
+ if (dvmIsAbstractMethod(methodToCall)) {
+ dvmThrowException("Ljava/lang/AbstractMethodError;",
+ "abstract method not implemented");
+ GOTO_exceptionThrown();
+ }
+#else
+ assert(!dvmIsAbstractMethod(methodToCall) ||
+ methodToCall->nativeFunc != NULL);
+#endif
+
+ LOGVV("+++ virtual[%d]=%s.%s\n",
+ ref, methodToCall->clazz->descriptor, methodToCall->name);
+ assert(methodToCall != NULL);
+
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+ }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeSuperQuick, bool methodCallRange)
+ {
+ u2 thisReg;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* vtable index */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ if (methodCallRange) {
+ ILOGV("|invoke-super-quick-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ thisReg = vdst;
+ } else {
+ ILOGV("|invoke-super-quick args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+ thisReg = vdst & 0x0f;
+ }
+ /* impossible in well-formed code, but we must check nevertheless */
+ if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+ GOTO_exceptionThrown();
+
+#if 0 /* impossible in optimized + verified code */
+ if (ref >= curMethod->clazz->super->vtableCount) {
+ dvmThrowException("Ljava/lang/NoSuchMethodError;", NULL);
+ GOTO_exceptionThrown();
+ }
+#else
+ assert(ref < curMethod->clazz->super->vtableCount);
+#endif
+
+ /*
+ * Combine the object we found with the vtable offset in the
+ * method's class.
+ *
+ * We're using the current method's class' superclass, not the
+ * superclass of "this". This is because we might be executing
+ * in a method inherited from a superclass, and we want to run
+ * in the method's class' superclass.
+ */
+ methodToCall = curMethod->clazz->super->vtable[ref];
+
+#if 0
+ if (dvmIsAbstractMethod(methodToCall)) {
+ dvmThrowException("Ljava/lang/AbstractMethodError;",
+ "abstract method not implemented");
+ GOTO_exceptionThrown();
+ }
+#else
+ assert(!dvmIsAbstractMethod(methodToCall) ||
+ methodToCall->nativeFunc != NULL);
+#endif
+ LOGVV("+++ super-virtual[%d]=%s.%s\n",
+ ref, methodToCall->clazz->descriptor, methodToCall->name);
+ assert(methodToCall != NULL);
+
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+ }
+GOTO_TARGET_END
+
+
+ /*
+ * General handling for return-void, return, and return-wide. Put the
+ * return value in "retval" before jumping here.
+ */
+GOTO_TARGET(returnFromMethod)
+ {
+ StackSaveArea* saveArea;
+
+ /*
+ * We must do this BEFORE we pop the previous stack frame off, so
+ * that the GC can see the return value (if any) in the local vars.
+ *
+ * Since this is now an interpreter switch point, we must do it before
+ * we do anything at all.
+ */
+ PERIODIC_CHECKS(kInterpEntryReturn, 0);
+
+ ILOGV("> retval=0x%llx (leaving %s.%s %s)",
+ retval.j, curMethod->clazz->descriptor, curMethod->name,
+ curMethod->shorty);
+ //DUMP_REGS(curMethod, fp);
+
+ saveArea = SAVEAREA_FROM_FP(fp);
+
+#ifdef EASY_GDB
+ debugSaveArea = saveArea;
+#endif
+#if (INTERP_TYPE == INTERP_DBG)
+ TRACE_METHOD_EXIT(self, curMethod);
+#endif
+
+ /* back up to previous frame and see if we hit a break */
+ fp = saveArea->prevFrame;
+ assert(fp != NULL);
+ if (dvmIsBreakFrame(fp)) {
+ /* bail without popping the method frame from stack */
+ LOGVV("+++ returned into break frame\n");
+#if defined(WITH_JIT)
+ /* Let the Jit know the return is terminating normally */
+ CHECK_JIT_VOID();
+#endif
+ GOTO_bail();
+ }
+
+ /* update thread FP, and reset local variables */
+ self->curFrame = fp;
+ curMethod = SAVEAREA_FROM_FP(fp)->method;
+ //methodClass = curMethod->clazz;
+ methodClassDex = curMethod->clazz->pDvmDex;
+ pc = saveArea->savedPc;
+ ILOGD("> (return to %s.%s %s)", curMethod->clazz->descriptor,
+ curMethod->name, curMethod->shorty);
+
+ /* use FINISH on the caller's invoke instruction */
+ //u2 invokeInstr = INST_INST(FETCH(0));
+ if (true /*invokeInstr >= OP_INVOKE_VIRTUAL &&
+ invokeInstr <= OP_INVOKE_INTERFACE*/)
+ {
+ FINISH(3);
+ } else {
+ //LOGE("Unknown invoke instr %02x at %d\n",
+ // invokeInstr, (int) (pc - curMethod->insns));
+ assert(false);
+ }
+ }
+GOTO_TARGET_END
+
+
+ /*
+ * Jump here when the code throws an exception.
+ *
+ * By the time we get here, the Throwable has been created and the stack
+ * trace has been saved off.
+ */
+GOTO_TARGET(exceptionThrown)
+ {
+ Object* exception;
+ int catchRelPc;
+
+ /*
+ * Since this is now an interpreter switch point, we must do it before
+ * we do anything at all.
+ */
+ PERIODIC_CHECKS(kInterpEntryThrow, 0);
+
+#if defined(WITH_JIT)
+ // Something threw during trace selection - abort the current trace
+ ABORT_JIT_TSELECT();
+#endif
+ /*
+ * We save off the exception and clear the exception status. While
+ * processing the exception we might need to load some Throwable
+ * classes, and we don't want class loader exceptions to get
+ * confused with this one.
+ */
+ assert(dvmCheckException(self));
+ exception = dvmGetException(self);
+ dvmAddTrackedAlloc(exception, self);
+ dvmClearException(self);
+
+ LOGV("Handling exception %s at %s:%d\n",
+ exception->clazz->descriptor, curMethod->name,
+ dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+
+#if (INTERP_TYPE == INTERP_DBG)
+ /*
+ * Tell the debugger about it.
+ *
+ * TODO: if the exception was thrown by interpreted code, control
+ * fell through native, and then back to us, we will report the
+ * exception at the point of the throw and again here. We can avoid
+ * this by not reporting exceptions when we jump here directly from
+ * the native call code above, but then we won't report exceptions
+ * that were thrown *from* the JNI code (as opposed to *through* it).
+ *
+ * The correct solution is probably to ignore from-native exceptions
+ * here, and have the JNI exception code do the reporting to the
+ * debugger.
+ */
+ if (gDvm.debuggerActive) {
+ void* catchFrame;
+ catchRelPc = dvmFindCatchBlock(self, pc - curMethod->insns,
+ exception, true, &catchFrame);
+ dvmDbgPostException(fp, pc - curMethod->insns, catchFrame,
+ catchRelPc, exception);
+ }
+#endif
+
+ /*
+ * We need to unroll to the catch block or the nearest "break"
+ * frame.
+ *
+ * A break frame could indicate that we have reached an intermediate
+ * native call, or have gone off the top of the stack and the thread
+ * needs to exit. Either way, we return from here, leaving the
+ * exception raised.
+ *
+ * If we do find a catch block, we want to transfer execution to
+ * that point.
+ *
+ * Note this can cause an exception while resolving classes in
+ * the "catch" blocks.
+ */
+ catchRelPc = dvmFindCatchBlock(self, pc - curMethod->insns,
+ exception, false, (void*)&fp);
+
+ /*
+ * Restore the stack bounds after an overflow. This isn't going to
+ * be correct in all circumstances, e.g. if JNI code devours the
+ * exception this won't happen until some other exception gets
+ * thrown. If the code keeps pushing the stack bounds we'll end
+ * up aborting the VM.
+ *
+ * Note we want to do this *after* the call to dvmFindCatchBlock,
+ * because that may need extra stack space to resolve exception
+ * classes (e.g. through a class loader).
+ *
+ * It's possible for the stack overflow handling to cause an
+ * exception (specifically, class resolution in a "catch" block
+ * during the call above), so we could see the thread's overflow
+ * flag raised but actually be running in a "nested" interpreter
+ * frame. We don't allow doubled-up StackOverflowErrors, so
+ * we can check for this by just looking at the exception type
+ * in the cleanup function. Also, we won't unroll past the SOE
+ * point because the more-recent exception will hit a break frame
+ * as it unrolls to here.
+ */
+ if (self->stackOverflowed)
+ dvmCleanupStackOverflow(self, exception);
+
+ if (catchRelPc < 0) {
+ /* falling through to JNI code or off the bottom of the stack */
+#if DVM_SHOW_EXCEPTION >= 2
+ LOGD("Exception %s from %s:%d not caught locally\n",
+ exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+ dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+#endif
+ dvmSetException(self, exception);
+ dvmReleaseTrackedAlloc(exception, self);
+ GOTO_bail();
+ }
+
+#if DVM_SHOW_EXCEPTION >= 3
+ {
+ const Method* catchMethod = SAVEAREA_FROM_FP(fp)->method;
+ LOGD("Exception %s thrown from %s:%d to %s:%d\n",
+ exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+ dvmLineNumFromPC(curMethod, pc - curMethod->insns),
+ dvmGetMethodSourceFile(catchMethod),
+ dvmLineNumFromPC(catchMethod, catchRelPc));
+ }
+#endif
+
+ /*
+ * Adjust local variables to match self->curFrame and the
+ * updated PC.
+ */
+ //fp = (u4*) self->curFrame;
+ curMethod = SAVEAREA_FROM_FP(fp)->method;
+ //methodClass = curMethod->clazz;
+ methodClassDex = curMethod->clazz->pDvmDex;
+ pc = curMethod->insns + catchRelPc;
+ ILOGV("> pc <-- %s.%s %s", curMethod->clazz->descriptor,
+ curMethod->name, curMethod->shorty);
+ DUMP_REGS(curMethod, fp, false); // show all regs
+
+ /*
+ * Restore the exception if the handler wants it.
+ *
+ * The Dalvik spec mandates that, if an exception handler wants to
+ * do something with the exception, the first instruction executed
+ * must be "move-exception". We can pass the exception along
+ * through the thread struct, and let the move-exception instruction
+ * clear it for us.
+ *
+ * If the handler doesn't call move-exception, we don't want to
+ * finish here with an exception still pending.
+ */
+ if (INST_INST(FETCH(0)) == OP_MOVE_EXCEPTION)
+ dvmSetException(self, exception);
+
+ dvmReleaseTrackedAlloc(exception, self);
+ FINISH(0);
+ }
+GOTO_TARGET_END
+
+
+
+ /*
+ * General handling for invoke-{virtual,super,direct,static,interface},
+ * including "quick" variants.
+ *
+ * Set "methodToCall" to the Method we're calling, and "methodCallRange"
+ * depending on whether this is a "/range" instruction.
+ *
+ * For a range call:
+ * "vsrc1" holds the argument count (8 bits)
+ * "vdst" holds the first argument in the range
+ * For a non-range call:
+ * "vsrc1" holds the argument count (4 bits) and the 5th argument index
+ * "vdst" holds four 4-bit register indices
+ *
+ * The caller must EXPORT_PC before jumping here, because any method
+ * call can throw a stack overflow exception.
+ */
+GOTO_TARGET(invokeMethod, bool methodCallRange, const Method* _methodToCall,
+ u2 count, u2 regs)
+ {
+ STUB_HACK(vsrc1 = count; vdst = regs; methodToCall = _methodToCall;);
+
+ //printf("range=%d call=%p count=%d regs=0x%04x\n",
+ // methodCallRange, methodToCall, count, regs);
+ //printf(" --> %s.%s %s\n", methodToCall->clazz->descriptor,
+ // methodToCall->name, methodToCall->shorty);
+
+ u4* outs;
+ int i;
+
+ /*
+ * Copy args. This may corrupt vsrc1/vdst.
+ */
+ if (methodCallRange) {
+ // could use memcpy or a "Duff's device"; most functions have
+ // so few args it won't matter much
+ assert(vsrc1 <= curMethod->outsSize);
+ assert(vsrc1 == methodToCall->insSize);
+ outs = OUTS_FROM_FP(fp, vsrc1);
+ for (i = 0; i < vsrc1; i++)
+ outs[i] = GET_REGISTER(vdst+i);
+ } else {
+ u4 count = vsrc1 >> 4;
+
+ assert(count <= curMethod->outsSize);
+ assert(count == methodToCall->insSize);
+ assert(count <= 5);
+
+ outs = OUTS_FROM_FP(fp, count);
+#if 0
+ if (count == 5) {
+ outs[4] = GET_REGISTER(vsrc1 & 0x0f);
+ count--;
+ }
+ for (i = 0; i < (int) count; i++) {
+ outs[i] = GET_REGISTER(vdst & 0x0f);
+ vdst >>= 4;
+ }
+#else
+ // This version executes fewer instructions but is larger
+ // overall. Seems to be a teensy bit faster.
+ assert((vdst >> 16) == 0); // 16 bits -or- high 16 bits clear
+ switch (count) {
+ case 5:
+ outs[4] = GET_REGISTER(vsrc1 & 0x0f);
+ case 4:
+ outs[3] = GET_REGISTER(vdst >> 12);
+ case 3:
+ outs[2] = GET_REGISTER((vdst & 0x0f00) >> 8);
+ case 2:
+ outs[1] = GET_REGISTER((vdst & 0x00f0) >> 4);
+ case 1:
+ outs[0] = GET_REGISTER(vdst & 0x0f);
+ default:
+ ;
+ }
+#endif
+ }
+ }
+
+ /*
+ * (This was originally a "goto" target; I've kept it separate from the
+ * stuff above in case we want to refactor things again.)
+ *
+ * At this point, we have the arguments stored in the "outs" area of
+ * the current method's stack frame, and the method to call in
+ * "methodToCall". Push a new stack frame.
+ */
+ {
+ StackSaveArea* newSaveArea;
+ u4* newFp;
+
+ ILOGV("> %s%s.%s %s",
+ dvmIsNativeMethod(methodToCall) ? "(NATIVE) " : "",
+ methodToCall->clazz->descriptor, methodToCall->name,
+ methodToCall->shorty);
+
+ newFp = (u4*) SAVEAREA_FROM_FP(fp) - methodToCall->registersSize;
+ newSaveArea = SAVEAREA_FROM_FP(newFp);
+
+ /* verify that we have enough space */
+ if (true) {
+ u1* bottom;
+ bottom = (u1*) newSaveArea - methodToCall->outsSize * sizeof(u4);
+ if (bottom < self->interpStackEnd) {
+ /* stack overflow */
+ LOGV("Stack overflow on method call (start=%p end=%p newBot=%p(%d) size=%d '%s')\n",
+ self->interpStackStart, self->interpStackEnd, bottom,
+ (u1*) fp - bottom, self->interpStackSize,
+ methodToCall->name);
+ dvmHandleStackOverflow(self, methodToCall);
+ assert(dvmCheckException(self));
+ GOTO_exceptionThrown();
+ }
+ //LOGD("+++ fp=%p newFp=%p newSave=%p bottom=%p\n",
+ // fp, newFp, newSaveArea, bottom);
+ }
+
+#ifdef LOG_INSTR
+ if (methodToCall->registersSize > methodToCall->insSize) {
+ /*
+ * This makes valgrind quiet when we print registers that
+ * haven't been initialized. Turn it off when the debug
+ * messages are disabled -- we want valgrind to report any
+ * used-before-initialized issues.
+ */
+ memset(newFp, 0xcc,
+ (methodToCall->registersSize - methodToCall->insSize) * 4);
+ }
+#endif
+
+#ifdef EASY_GDB
+ newSaveArea->prevSave = SAVEAREA_FROM_FP(fp);
+#endif
+ newSaveArea->prevFrame = fp;
+ newSaveArea->savedPc = pc;
+#if defined(WITH_JIT)
+ newSaveArea->returnAddr = 0;
+#endif
+ newSaveArea->method = methodToCall;
+
+ if (!dvmIsNativeMethod(methodToCall)) {
+ /*
+ * "Call" interpreted code. Reposition the PC, update the
+ * frame pointer and other local state, and continue.
+ */
+ curMethod = methodToCall;
+ methodClassDex = curMethod->clazz->pDvmDex;
+ pc = methodToCall->insns;
+ fp = self->curFrame = newFp;
+#ifdef EASY_GDB
+ debugSaveArea = SAVEAREA_FROM_FP(newFp);
+#endif
+#if INTERP_TYPE == INTERP_DBG
+ debugIsMethodEntry = true; // profiling, debugging
+#endif
+ ILOGD("> pc <-- %s.%s %s", curMethod->clazz->descriptor,
+ curMethod->name, curMethod->shorty);
+ DUMP_REGS(curMethod, fp, true); // show input args
+ FINISH(0); // jump to method start
+ } else {
+ /* set this up for JNI locals, even if not a JNI native */
+#ifdef USE_INDIRECT_REF
+ newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
+#else
+ newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.nextEntry;
+#endif
+
+ self->curFrame = newFp;
+
+ DUMP_REGS(methodToCall, newFp, true); // show input args
+
+#if (INTERP_TYPE == INTERP_DBG)
+ if (gDvm.debuggerActive) {
+ dvmDbgPostLocationEvent(methodToCall, -1,
+ dvmGetThisPtr(curMethod, fp), DBG_METHOD_ENTRY);
+ }
+#endif
+#if (INTERP_TYPE == INTERP_DBG)
+ TRACE_METHOD_ENTER(self, methodToCall);
+#endif
+
+ {
+ ILOGD("> native <-- %s.%s %s", methodToCall->clazz->descriptor,
+ methodToCall->name, methodToCall->shorty);
+ }
+
+#if defined(WITH_JIT)
+ /* Allow the Jit to end any pending trace building */
+ CHECK_JIT_VOID();
+#endif
+
+ /*
+ * Jump through native call bridge. Because we leave no
+ * space for locals on native calls, "newFp" points directly
+ * to the method arguments.
+ */
+ (*methodToCall->nativeFunc)(newFp, &retval, methodToCall, self);
+
+#if (INTERP_TYPE == INTERP_DBG)
+ if (gDvm.debuggerActive) {
+ dvmDbgPostLocationEvent(methodToCall, -1,
+ dvmGetThisPtr(curMethod, fp), DBG_METHOD_EXIT);
+ }
+#endif
+#if (INTERP_TYPE == INTERP_DBG)
+ TRACE_METHOD_EXIT(self, methodToCall);
+#endif
+
+ /* pop frame off */
+ dvmPopJniLocals(self, newSaveArea);
+ self->curFrame = fp;
+
+ /*
+ * If the native code threw an exception, or interpreted code
+ * invoked by the native call threw one and nobody has cleared
+ * it, jump to our local exception handling.
+ */
+ if (dvmCheckException(self)) {
+ LOGV("Exception thrown by/below native code\n");
+ GOTO_exceptionThrown();
+ }
+
+ ILOGD("> retval=0x%llx (leaving native)", retval.j);
+ ILOGD("> (return from native %s.%s to %s.%s %s)",
+ methodToCall->clazz->descriptor, methodToCall->name,
+ curMethod->clazz->descriptor, curMethod->name,
+ curMethod->shorty);
+
+ //u2 invokeInstr = INST_INST(FETCH(0));
+ if (true /*invokeInstr >= OP_INVOKE_VIRTUAL &&
+ invokeInstr <= OP_INVOKE_INTERFACE*/)
+ {
+ FINISH(3);
+ } else {
+ //LOGE("Unknown invoke instr %02x at %d\n",
+ // invokeInstr, (int) (pc - curMethod->insns));
+ assert(false);
+ }
+ }
+ }
+ assert(false); // should not get here
+GOTO_TARGET_END
+
+/* File: cstubs/enddefs.c */
+
+/* undefine "magic" name remapping */
+#undef retval
+#undef pc
+#undef fp
+#undef curMethod
+#undef methodClassDex
+#undef self
+#undef debugTrackedRefStart
+
diff --git a/vm/mterp/out/InterpC-armv4t.c b/vm/mterp/out/InterpC-armv4t.c
new file mode 100644
index 0000000..2e7716b
--- /dev/null
+++ b/vm/mterp/out/InterpC-armv4t.c
@@ -0,0 +1,1289 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'armv4t'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: c/header.c */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* common includes */
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "mterp/Mterp.h"
+#include <math.h> // needed for fmod, fmodf
+#include "mterp/common/FindInterface.h"
+
+/*
+ * Configuration defines. These affect the C implementations, i.e. the
+ * portable interpreter(s) and C stubs.
+ *
+ * Some defines are controlled by the Makefile, e.g.:
+ * WITH_INSTR_CHECKS
+ * WITH_TRACKREF_CHECKS
+ * EASY_GDB
+ * NDEBUG
+ *
+ * If THREADED_INTERP is not defined, we use a classic "while true / switch"
+ * interpreter. If it is defined, then the tail end of each instruction
+ * handler fetches the next instruction and jumps directly to the handler.
+ * This increases the size of the "Std" interpreter by about 10%, but
+ * provides a speedup of about the same magnitude.
+ *
+ * There's a "hybrid" approach that uses a goto table instead of a switch
+ * statement, avoiding the "is the opcode in range" tests required for switch.
+ * The performance is close to the threaded version, and without the 10%
+ * size increase, but the benchmark results are off enough that it's not
+ * worth adding as a third option.
+ */
+#define THREADED_INTERP /* threaded vs. while-loop interpreter */
+
+#ifdef WITH_INSTR_CHECKS /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * ARM EABI requires 64-bit alignment for access to 64-bit data types. We
+ * can't just use pointers to copy 64-bit values out of our interpreted
+ * register set, because gcc will generate ldrd/strd.
+ *
+ * The __UNION version copies data in and out of a union. The __MEMCPY
+ * version uses a memcpy() call to do the transfer; gcc is smart enough to
+ * not actually call memcpy(). The __UNION version is very bad on ARM;
+ * it only uses one more instruction than __MEMCPY, but for some reason
+ * gcc thinks it needs separate storage for every instance of the union.
+ * On top of that, it feels the need to zero them out at the start of the
+ * method. Net result is we zero out ~700 bytes of stack space at the top
+ * of the interpreter using ARM STM instructions.
+ */
+#if defined(__ARM_EABI__)
+//# define NO_UNALIGN_64__UNION
+# define NO_UNALIGN_64__MEMCPY
+#endif
+
+//#define LOG_INSTR /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Keep a tally of accesses to fields. Currently only works if full DEX
+ * optimization is disabled.
+ */
+#ifdef PROFILE_FIELD_ACCESS
+# define UPDATE_FIELD_GET(_field) { (_field)->gets++; }
+# define UPDATE_FIELD_PUT(_field) { (_field)->puts++; }
+#else
+# define UPDATE_FIELD_GET(_field) ((void)0)
+# define UPDATE_FIELD_PUT(_field) ((void)0)
+#endif
+
+/*
+ * Export another copy of the PC on every instruction; this is largely
+ * redundant with EXPORT_PC and the debugger code. This value can be
+ * compared against what we have stored on the stack with EXPORT_PC to
+ * help ensure that we aren't missing any export calls.
+ */
+#if WITH_EXTRA_GC_CHECKS > 1
+# define EXPORT_EXTRA_PC() (self->currentPc2 = pc)
+#else
+# define EXPORT_EXTRA_PC()
+#endif
+
+/*
+ * Adjust the program counter. "_offset" is a signed int, in 16-bit units.
+ *
+ * Assumes the existence of "const u2* pc" and "const u2* curMethod->insns".
+ *
+ * We don't advance the program counter until we finish an instruction or
+ * branch, because we do want to have to unroll the PC if there's an
+ * exception.
+ */
+#ifdef CHECK_BRANCH_OFFSETS
+# define ADJUST_PC(_offset) do { \
+ int myoff = _offset; /* deref only once */ \
+ if (pc + myoff < curMethod->insns || \
+ pc + myoff >= curMethod->insns + dvmGetMethodInsnsSize(curMethod)) \
+ { \
+ char* desc; \
+ desc = dexProtoCopyMethodDescriptor(&curMethod->prototype); \
+ LOGE("Invalid branch %d at 0x%04x in %s.%s %s\n", \
+ myoff, (int) (pc - curMethod->insns), \
+ curMethod->clazz->descriptor, curMethod->name, desc); \
+ free(desc); \
+ dvmAbort(); \
+ } \
+ pc += myoff; \
+ EXPORT_EXTRA_PC(); \
+ } while (false)
+#else
+# define ADJUST_PC(_offset) do { \
+ pc += _offset; \
+ EXPORT_EXTRA_PC(); \
+ } while (false)
+#endif
+
+/*
+ * If enabled, log instructions as we execute them.
+ */
+#ifdef LOG_INSTR
+# define ILOGD(...) ILOG(LOG_DEBUG, __VA_ARGS__)
+# define ILOGV(...) ILOG(LOG_VERBOSE, __VA_ARGS__)
+# define ILOG(_level, ...) do { \
+ char debugStrBuf[128]; \
+ snprintf(debugStrBuf, sizeof(debugStrBuf), __VA_ARGS__); \
+ if (curMethod != NULL) \
+ LOG(_level, LOG_TAG"i", "%-2d|%04x%s\n", \
+ self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+ else \
+ LOG(_level, LOG_TAG"i", "%-2d|####%s\n", \
+ self->threadId, debugStrBuf); \
+ } while(false)
+void dvmDumpRegs(const Method* method, const u4* framePtr, bool inOnly);
+# define DUMP_REGS(_meth, _frame, _inOnly) dvmDumpRegs(_meth, _frame, _inOnly)
+static const char kSpacing[] = " ";
+#else
+# define ILOGD(...) ((void)0)
+# define ILOGV(...) ((void)0)
+# define DUMP_REGS(_meth, _frame, _inOnly) ((void)0)
+#endif
+
+/* get a long from an array of u4 */
+static inline s8 getLongFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { s8 ll; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.parts[0] = ptr[0];
+ conv.parts[1] = ptr[1];
+ return conv.ll;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ s8 val;
+ memcpy(&val, &ptr[idx], 8);
+ return val;
+#else
+ return *((s8*) &ptr[idx]);
+#endif
+}
+
+/* store a long into an array of u4 */
+static inline void putLongToArray(u4* ptr, int idx, s8 val)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { s8 ll; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.ll = val;
+ ptr[0] = conv.parts[0];
+ ptr[1] = conv.parts[1];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ memcpy(&ptr[idx], &val, 8);
+#else
+ *((s8*) &ptr[idx]) = val;
+#endif
+}
+
+/* get a double from an array of u4 */
+static inline double getDoubleFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { double d; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.parts[0] = ptr[0];
+ conv.parts[1] = ptr[1];
+ return conv.d;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ double dval;
+ memcpy(&dval, &ptr[idx], 8);
+ return dval;
+#else
+ return *((double*) &ptr[idx]);
+#endif
+}
+
+/* store a double into an array of u4 */
+static inline void putDoubleToArray(u4* ptr, int idx, double dval)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { double d; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.d = dval;
+ ptr[0] = conv.parts[0];
+ ptr[1] = conv.parts[1];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ memcpy(&ptr[idx], &dval, 8);
+#else
+ *((double*) &ptr[idx]) = dval;
+#endif
+}
+
+/*
+ * If enabled, validate the register number on every access. Otherwise,
+ * just do an array access.
+ *
+ * Assumes the existence of "u4* fp".
+ *
+ * "_idx" may be referenced more than once.
+ */
+#ifdef CHECK_REGISTER_INDICES
+# define GET_REGISTER(_idx) \
+ ( (_idx) < curMethod->registersSize ? \
+ (fp[(_idx)]) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER(_idx, _val) \
+ ( (_idx) < curMethod->registersSize ? \
+ (fp[(_idx)] = (u4)(_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_AS_OBJECT(_idx) ((Object *)GET_REGISTER(_idx))
+# define SET_REGISTER_AS_OBJECT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_INT(_idx) ((s4) GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ getLongFromArray(fp, (_idx)) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER_WIDE(_idx, _val) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ putLongToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_FLOAT(_idx) \
+ ( (_idx) < curMethod->registersSize ? \
+ (*((float*) &fp[(_idx)])) : (assert(!"bad reg"),1969.0f) )
+# define SET_REGISTER_FLOAT(_idx, _val) \
+ ( (_idx) < curMethod->registersSize ? \
+ (*((float*) &fp[(_idx)]) = (_val)) : (assert(!"bad reg"),1969.0f) )
+# define GET_REGISTER_DOUBLE(_idx) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ getDoubleFromArray(fp, (_idx)) : (assert(!"bad reg"),1969.0) )
+# define SET_REGISTER_DOUBLE(_idx, _val) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ putDoubleToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969.0) )
+#else
+# define GET_REGISTER(_idx) (fp[(_idx)])
+# define SET_REGISTER(_idx, _val) (fp[(_idx)] = (_val))
+# define GET_REGISTER_AS_OBJECT(_idx) ((Object*) fp[(_idx)])
+# define SET_REGISTER_AS_OBJECT(_idx, _val) (fp[(_idx)] = (u4)(_val))
+# define GET_REGISTER_INT(_idx) ((s4)GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) getLongFromArray(fp, (_idx))
+# define SET_REGISTER_WIDE(_idx, _val) putLongToArray(fp, (_idx), (_val))
+# define GET_REGISTER_FLOAT(_idx) (*((float*) &fp[(_idx)]))
+# define SET_REGISTER_FLOAT(_idx, _val) (*((float*) &fp[(_idx)]) = (_val))
+# define GET_REGISTER_DOUBLE(_idx) getDoubleFromArray(fp, (_idx))
+# define SET_REGISTER_DOUBLE(_idx, _val) putDoubleToArray(fp, (_idx), (_val))
+#endif
+
+/*
+ * Get 16 bits from the specified offset of the program counter. We always
+ * want to load 16 bits at a time from the instruction stream -- it's more
+ * efficient than 8 and won't have the alignment problems that 32 might.
+ *
+ * Assumes existence of "const u2* pc".
+ */
+#define FETCH(_offset) (pc[(_offset)])
+
+/*
+ * Extract instruction byte from 16-bit fetch (_inst is a u2).
+ */
+#define INST_INST(_inst) ((_inst) & 0xff)
+
+/*
+ * Replace the opcode (used when handling breakpoints). _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
+ * Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
+ */
+#define INST_A(_inst) (((_inst) >> 8) & 0x0f)
+#define INST_B(_inst) ((_inst) >> 12)
+
+/*
+ * Get the 8-bit "vAA" 8-bit register index from the instruction word.
+ * (_inst is u2)
+ */
+#define INST_AA(_inst) ((_inst) >> 8)
+
+/*
+ * The current PC must be available to Throwable constructors, e.g.
+ * those created by dvmThrowException(), so that the exception stack
+ * trace can be generated correctly. If we don't do this, the offset
+ * within the current method won't be shown correctly. See the notes
+ * in Exception.c.
+ *
+ * This is also used to determine the address for precise GC.
+ *
+ * Assumes existence of "u4* fp" and "const u2* pc".
+ */
+#define EXPORT_PC() (SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc)
+
+/*
+ * Determine if we need to switch to a different interpreter. "_current"
+ * is either INTERP_STD or INTERP_DBG. It should be fixed for a given
+ * interpreter generation file, which should remove the outer conditional
+ * from the following.
+ *
+ * If we're building without debug and profiling support, we never switch.
+ */
+#if defined(WITH_JIT)
+# define NEED_INTERP_SWITCH(_current) ( \
+ (_current == INTERP_STD) ? \
+ dvmJitDebuggerOrProfilerActive() : !dvmJitDebuggerOrProfilerActive() )
+#else
+# define NEED_INTERP_SWITCH(_current) ( \
+ (_current == INTERP_STD) ? \
+ dvmDebuggerOrProfilerActive() : !dvmDebuggerOrProfilerActive() )
+#endif
+
+/*
+ * Check to see if "obj" is NULL. If so, throw an exception. Assumes the
+ * pc has already been exported to the stack.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler calls into
+ * something that could throw an exception (so we have already called
+ * EXPORT_PC at the top).
+ */
+static inline bool checkForNull(Object* obj)
+{
+ if (obj == NULL) {
+ dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+ return false;
+ }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+ if (!dvmIsValidObject(obj)) {
+ LOGE("Invalid object %p\n", obj);
+ dvmAbort();
+ }
+#endif
+#ifndef NDEBUG
+ if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+ /* probable heap corruption */
+ LOGE("Invalid object class %p (in %p)\n", obj->clazz, obj);
+ dvmAbort();
+ }
+#endif
+ return true;
+}
+
+/*
+ * Check to see if "obj" is NULL. If so, export the PC into the stack
+ * frame and throw an exception.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler doesn't do
+ * anything else that can throw an exception.
+ */
+static inline bool checkForNullExportPC(Object* obj, u4* fp, const u2* pc)
+{
+ if (obj == NULL) {
+ EXPORT_PC();
+ dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+ return false;
+ }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+ if (!dvmIsValidObject(obj)) {
+ LOGE("Invalid object %p\n", obj);
+ dvmAbort();
+ }
+#endif
+#ifndef NDEBUG
+ if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+ /* probable heap corruption */
+ LOGE("Invalid object class %p (in %p)\n", obj->clazz, obj);
+ dvmAbort();
+ }
+#endif
+ return true;
+}
+
+/* File: cstubs/stubdefs.c */
+/* this is a standard (no debug support) interpreter */
+#define INTERP_TYPE INTERP_STD
+#define CHECK_DEBUG_AND_PROF() ((void)0)
+# define CHECK_TRACKED_REFS() ((void)0)
+#define CHECK_JIT_BOOL() (false)
+#define CHECK_JIT_VOID()
+#define ABORT_JIT_TSELECT() ((void)0)
+
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...) \
+ void dvmMterp_##_target(MterpGlue* glue, ## __VA_ARGS__);
+
+#define GOTO_TARGET(_target, ...) \
+ void dvmMterp_##_target(MterpGlue* glue, ## __VA_ARGS__) { \
+ u2 ref, vsrc1, vsrc2, vdst; \
+ u2 inst = FETCH(0); \
+ const Method* methodToCall; \
+ StackSaveArea* debugSaveArea;
+
+#define GOTO_TARGET_END }
+
+/*
+ * Redefine what used to be local variable accesses into MterpGlue struct
+ * references. (These are undefined down in "footer.c".)
+ */
+#define retval glue->retval
+#define pc glue->pc
+#define fp glue->fp
+#define curMethod glue->method
+#define methodClassDex glue->methodClassDex
+#define self glue->self
+#define debugTrackedRefStart glue->debugTrackedRefStart
+
+/* ugh */
+#define STUB_HACK(x) x
+
+
+/*
+ * Opcode handler framing macros. Here, each opcode is a separate function
+ * that takes a "glue" argument and returns void. We can't declare
+ * these "static" because they may be called from an assembly stub.
+ */
+#define HANDLE_OPCODE(_op) \
+ void dvmMterp_##_op(MterpGlue* glue) { \
+ u2 ref, vsrc1, vsrc2, vdst; \
+ u2 inst = FETCH(0);
+
+#define OP_END }
+
+/*
+ * Like the "portable" FINISH, but don't reload "inst", and return to caller
+ * when done.
+ */
+#define FINISH(_offset) { \
+ ADJUST_PC(_offset); \
+ CHECK_DEBUG_AND_PROF(); \
+ CHECK_TRACKED_REFS(); \
+ return; \
+ }
+
+
+/*
+ * The "goto label" statements turn into function calls followed by
+ * return statements. Some of the functions take arguments, which in the
+ * portable interpreter are handled by assigning values to globals.
+ */
+
+#define GOTO_exceptionThrown() \
+ do { \
+ dvmMterp_exceptionThrown(glue); \
+ return; \
+ } while(false)
+
+#define GOTO_returnFromMethod() \
+ do { \
+ dvmMterp_returnFromMethod(glue); \
+ return; \
+ } while(false)
+
+#define GOTO_invoke(_target, _methodCallRange) \
+ do { \
+ dvmMterp_##_target(glue, _methodCallRange); \
+ return; \
+ } while(false)
+
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst) \
+ do { \
+ dvmMterp_invokeMethod(glue, _methodCallRange, _methodToCall, \
+ _vsrc1, _vdst); \
+ return; \
+ } while(false)
+
+/*
+ * As a special case, "goto bail" turns into a longjmp. Use "bail_switch"
+ * if we need to switch to the other interpreter upon our return.
+ */
+#define GOTO_bail() \
+ dvmMterpStdBail(glue, false);
+#define GOTO_bail_switch() \
+ dvmMterpStdBail(glue, true);
+
+/*
+ * Periodically check for thread suspension.
+ *
+ * While we're at it, see if a debugger has attached or the profiler has
+ * started. If so, switch to a different "goto" table.
+ */
+#define PERIODIC_CHECKS(_entryPoint, _pcadj) { \
+ if (dvmCheckSuspendQuick(self)) { \
+ EXPORT_PC(); /* need for precise GC */ \
+ dvmCheckSuspendPending(self); \
+ } \
+ if (NEED_INTERP_SWITCH(INTERP_TYPE)) { \
+ ADJUST_PC(_pcadj); \
+ glue->entryPoint = _entryPoint; \
+ LOGVV("threadid=%d: switch to STD ep=%d adj=%d\n", \
+ self->threadId, (_entryPoint), (_pcadj)); \
+ GOTO_bail_switch(); \
+ } \
+ }
+
+/* File: c/opcommon.c */
+/* forward declarations of goto targets */
+GOTO_TARGET_DECL(filledNewArray, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtual, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuper, bool methodCallRange);
+GOTO_TARGET_DECL(invokeInterface, bool methodCallRange);
+GOTO_TARGET_DECL(invokeDirect, bool methodCallRange);
+GOTO_TARGET_DECL(invokeStatic, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtualQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuperQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeMethod, bool methodCallRange, const Method* methodToCall,
+ u2 count, u2 regs);
+GOTO_TARGET_DECL(returnFromMethod);
+GOTO_TARGET_DECL(exceptionThrown);
+
+/*
+ * ===========================================================================
+ *
+ * What follows are opcode definitions shared between multiple opcodes with
+ * minor substitutions handled by the C pre-processor. These should probably
+ * use the mterp substitution mechanism instead, with the code here moved
+ * into common fragment files (like the asm "binop.S"), although it's hard
+ * to give up the C preprocessor in favor of the much simpler text subst.
+ *
+ * ===========================================================================
+ */
+
+#define HANDLE_NUMCONV(_opcode, _opname, _fromtype, _totype) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER##_totype(vdst, \
+ GET_REGISTER##_fromtype(vsrc1)); \
+ FINISH(1);
+
+#define HANDLE_FLOAT_TO_INT(_opcode, _opname, _fromvtype, _fromrtype, \
+ _tovtype, _tortype) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ { \
+ /* spec defines specific handling for +/- inf and NaN values */ \
+ _fromvtype val; \
+ _tovtype intMin, intMax, result; \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1); \
+ val = GET_REGISTER##_fromrtype(vsrc1); \
+ intMin = (_tovtype) 1 << (sizeof(_tovtype) * 8 -1); \
+ intMax = ~intMin; \
+ result = (_tovtype) val; \
+ if (val >= intMax) /* +inf */ \
+ result = intMax; \
+ else if (val <= intMin) /* -inf */ \
+ result = intMin; \
+ else if (val != val) /* NaN */ \
+ result = 0; \
+ else \
+ result = (_tovtype) val; \
+ SET_REGISTER##_tortype(vdst, result); \
+ } \
+ FINISH(1);
+
+#define HANDLE_INT_TO_SMALL(_opcode, _opname, _type) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|int-to-%s v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER(vdst, (_type) GET_REGISTER(vsrc1)); \
+ FINISH(1);
+
+/* NOTE: the comparison result is always a signed 4-byte integer */
+#define HANDLE_OP_CMPX(_opcode, _opname, _varType, _type, _nanVal) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ int result; \
+ u2 regs; \
+ _varType val1, val2; \
+ vdst = INST_AA(inst); \
+ regs = FETCH(1); \
+ vsrc1 = regs & 0xff; \
+ vsrc2 = regs >> 8; \
+ ILOGV("|cmp%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ val1 = GET_REGISTER##_type(vsrc1); \
+ val2 = GET_REGISTER##_type(vsrc2); \
+ if (val1 == val2) \
+ result = 0; \
+ else if (val1 < val2) \
+ result = -1; \
+ else if (val1 > val2) \
+ result = 1; \
+ else \
+ result = (_nanVal); \
+ ILOGV("+ result=%d\n", result); \
+ SET_REGISTER(vdst, result); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_IF_XX(_opcode, _opname, _cmp) \
+ HANDLE_OPCODE(_opcode /*vA, vB, +CCCC*/) \
+ vsrc1 = INST_A(inst); \
+ vsrc2 = INST_B(inst); \
+ if ((s4) GET_REGISTER(vsrc1) _cmp (s4) GET_REGISTER(vsrc2)) { \
+ int branchOffset = (s2)FETCH(1); /* sign-extended */ \
+ ILOGV("|if-%s v%d,v%d,+0x%04x", (_opname), vsrc1, vsrc2, \
+ branchOffset); \
+ ILOGV("> branch taken"); \
+ if (branchOffset < 0) \
+ PERIODIC_CHECKS(kInterpEntryInstr, branchOffset); \
+ FINISH(branchOffset); \
+ } else { \
+ ILOGV("|if-%s v%d,v%d,-", (_opname), vsrc1, vsrc2); \
+ FINISH(2); \
+ }
+
+#define HANDLE_OP_IF_XXZ(_opcode, _opname, _cmp) \
+ HANDLE_OPCODE(_opcode /*vAA, +BBBB*/) \
+ vsrc1 = INST_AA(inst); \
+ if ((s4) GET_REGISTER(vsrc1) _cmp 0) { \
+ int branchOffset = (s2)FETCH(1); /* sign-extended */ \
+ ILOGV("|if-%s v%d,+0x%04x", (_opname), vsrc1, branchOffset); \
+ ILOGV("> branch taken"); \
+ if (branchOffset < 0) \
+ PERIODIC_CHECKS(kInterpEntryInstr, branchOffset); \
+ FINISH(branchOffset); \
+ } else { \
+ ILOGV("|if-%s v%d,-", (_opname), vsrc1); \
+ FINISH(2); \
+ }
+
+#define HANDLE_UNOP(_opcode, _opname, _pfx, _sfx, _type) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER##_type(vdst, _pfx GET_REGISTER##_type(vsrc1) _sfx); \
+ FINISH(1);
+
+#define HANDLE_OP_X_INT(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER(vsrc1); \
+ secondVal = GET_REGISTER(vsrc2); \
+ if (secondVal == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && secondVal == -1) { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ /* non-div/rem case */ \
+ SET_REGISTER(vdst, \
+ (s4) GET_REGISTER(vsrc1) _op (s4) GET_REGISTER(vsrc2)); \
+ } \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_SHX_INT(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER(vdst, \
+ _cast GET_REGISTER(vsrc1) _op (GET_REGISTER(vsrc2) & 0x1f)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT16(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vA, vB, #+CCCC*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ vsrc2 = FETCH(1); \
+ ILOGV("|%s-int/lit16 v%d,v%d,#+0x%04x", \
+ (_opname), vdst, vsrc1, vsrc2); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, result; \
+ firstVal = GET_REGISTER(vsrc1); \
+ if ((s2) vsrc2 == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && ((s2) vsrc2) == -1) { \
+ /* won't generate /lit16 instr for this; check anyway */ \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op (s2) vsrc2; \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ /* non-div/rem case */ \
+ SET_REGISTER(vdst, GET_REGISTER(vsrc1) _op (s2) vsrc2); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT8(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/) \
+ { \
+ u2 litInfo; \
+ vdst = INST_AA(inst); \
+ litInfo = FETCH(1); \
+ vsrc1 = litInfo & 0xff; \
+ vsrc2 = litInfo >> 8; /* constant */ \
+ ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x", \
+ (_opname), vdst, vsrc1, vsrc2); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, result; \
+ firstVal = GET_REGISTER(vsrc1); \
+ if ((s1) vsrc2 == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && ((s1) vsrc2) == -1) { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op ((s1) vsrc2); \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ SET_REGISTER(vdst, \
+ (s4) GET_REGISTER(vsrc1) _op (s1) vsrc2); \
+ } \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_SHX_INT_LIT8(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/) \
+ { \
+ u2 litInfo; \
+ vdst = INST_AA(inst); \
+ litInfo = FETCH(1); \
+ vsrc1 = litInfo & 0xff; \
+ vsrc2 = litInfo >> 8; /* constant */ \
+ ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x", \
+ (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER(vdst, \
+ _cast GET_REGISTER(vsrc1) _op (vsrc2 & 0x1f)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_INT_2ADDR(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER(vdst); \
+ secondVal = GET_REGISTER(vsrc1); \
+ if (secondVal == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && secondVal == -1) { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ SET_REGISTER(vdst, \
+ (s4) GET_REGISTER(vdst) _op (s4) GET_REGISTER(vsrc1)); \
+ } \
+ FINISH(1);
+
+#define HANDLE_OP_SHX_INT_2ADDR(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER(vdst, \
+ _cast GET_REGISTER(vdst) _op (GET_REGISTER(vsrc1) & 0x1f)); \
+ FINISH(1);
+
+#define HANDLE_OP_X_LONG(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ if (_chkdiv != 0) { \
+ s8 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER_WIDE(vsrc1); \
+ secondVal = GET_REGISTER_WIDE(vsrc2); \
+ if (secondVal == 0LL) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u8)firstVal == 0x8000000000000000ULL && \
+ secondVal == -1LL) \
+ { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER_WIDE(vdst, result); \
+ } else { \
+ SET_REGISTER_WIDE(vdst, \
+ (s8) GET_REGISTER_WIDE(vsrc1) _op (s8) GET_REGISTER_WIDE(vsrc2)); \
+ } \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_SHX_LONG(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER_WIDE(vdst, \
+ _cast GET_REGISTER_WIDE(vsrc1) _op (GET_REGISTER(vsrc2) & 0x3f)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_LONG_2ADDR(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ if (_chkdiv != 0) { \
+ s8 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER_WIDE(vdst); \
+ secondVal = GET_REGISTER_WIDE(vsrc1); \
+ if (secondVal == 0LL) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u8)firstVal == 0x8000000000000000ULL && \
+ secondVal == -1LL) \
+ { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER_WIDE(vdst, result); \
+ } else { \
+ SET_REGISTER_WIDE(vdst, \
+ (s8) GET_REGISTER_WIDE(vdst) _op (s8)GET_REGISTER_WIDE(vsrc1));\
+ } \
+ FINISH(1);
+
+#define HANDLE_OP_SHX_LONG_2ADDR(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER_WIDE(vdst, \
+ _cast GET_REGISTER_WIDE(vdst) _op (GET_REGISTER(vsrc1) & 0x3f)); \
+ FINISH(1);
+
+#define HANDLE_OP_X_FLOAT(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-float v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER_FLOAT(vdst, \
+ GET_REGISTER_FLOAT(vsrc1) _op GET_REGISTER_FLOAT(vsrc2)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_DOUBLE(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-double v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER_DOUBLE(vdst, \
+ GET_REGISTER_DOUBLE(vsrc1) _op GET_REGISTER_DOUBLE(vsrc2)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_FLOAT_2ADDR(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-float-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER_FLOAT(vdst, \
+ GET_REGISTER_FLOAT(vdst) _op GET_REGISTER_FLOAT(vsrc1)); \
+ FINISH(1);
+
+#define HANDLE_OP_X_DOUBLE_2ADDR(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-double-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER_DOUBLE(vdst, \
+ GET_REGISTER_DOUBLE(vdst) _op GET_REGISTER_DOUBLE(vsrc1)); \
+ FINISH(1);
+
+#define HANDLE_OP_AGET(_opcode, _opname, _type, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ ArrayObject* arrayObj; \
+ u2 arrayInfo; \
+ EXPORT_PC(); \
+ vdst = INST_AA(inst); \
+ arrayInfo = FETCH(1); \
+ vsrc1 = arrayInfo & 0xff; /* array ptr */ \
+ vsrc2 = arrayInfo >> 8; /* index */ \
+ ILOGV("|aget%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ arrayObj = (ArrayObject*) GET_REGISTER(vsrc1); \
+ if (!checkForNull((Object*) arrayObj)) \
+ GOTO_exceptionThrown(); \
+ if (GET_REGISTER(vsrc2) >= arrayObj->length) { \
+ LOGV("Invalid array access: %p %d (len=%d)\n", \
+ arrayObj, vsrc2, arrayObj->length); \
+ dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+ NULL); \
+ GOTO_exceptionThrown(); \
+ } \
+ SET_REGISTER##_regsize(vdst, \
+ ((_type*) arrayObj->contents)[GET_REGISTER(vsrc2)]); \
+ ILOGV("+ AGET[%d]=0x%x", GET_REGISTER(vsrc2), GET_REGISTER(vdst)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_APUT(_opcode, _opname, _type, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ ArrayObject* arrayObj; \
+ u2 arrayInfo; \
+ EXPORT_PC(); \
+ vdst = INST_AA(inst); /* AA: source value */ \
+ arrayInfo = FETCH(1); \
+ vsrc1 = arrayInfo & 0xff; /* BB: array ptr */ \
+ vsrc2 = arrayInfo >> 8; /* CC: index */ \
+ ILOGV("|aput%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ arrayObj = (ArrayObject*) GET_REGISTER(vsrc1); \
+ if (!checkForNull((Object*) arrayObj)) \
+ GOTO_exceptionThrown(); \
+ if (GET_REGISTER(vsrc2) >= arrayObj->length) { \
+ dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+ NULL); \
+ GOTO_exceptionThrown(); \
+ } \
+ ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+ ((_type*) arrayObj->contents)[GET_REGISTER(vsrc2)] = \
+ GET_REGISTER##_regsize(vdst); \
+ } \
+ FINISH(2);
+
+/*
+ * It's possible to get a bad value out of a field with sub-32-bit stores
+ * because the -quick versions always operate on 32 bits. Consider:
+ * short foo = -1 (sets a 32-bit register to 0xffffffff)
+ * iput-quick foo (writes all 32 bits to the field)
+ * short bar = 1 (sets a 32-bit register to 0x00000001)
+ * iput-short (writes the low 16 bits to the field)
+ * iget-quick foo (reads all 32 bits from the field, yielding 0xffff0001)
+ * This can only happen when optimized and non-optimized code has interleaved
+ * access to the same field. This is unlikely but possible.
+ *
+ * The easiest way to fix this is to always read/write 32 bits at a time. On
+ * a device with a 16-bit data bus this is sub-optimal. (The alternative
+ * approach is to have sub-int versions of iget-quick, but now we're wasting
+ * Dalvik instruction space and making it less likely that handler code will
+ * already be in the CPU i-cache.)
+ */
+#define HANDLE_IGET_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ InstField* ifield; \
+ Object* obj; \
+ EXPORT_PC(); \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|iget%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNull(obj)) \
+ GOTO_exceptionThrown(); \
+ ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref); \
+ if (ifield == NULL) { \
+ ifield = dvmResolveInstField(curMethod->clazz, ref); \
+ if (ifield == NULL) \
+ GOTO_exceptionThrown(); \
+ } \
+ SET_REGISTER##_regsize(vdst, \
+ dvmGetField##_ftype(obj, ifield->byteOffset)); \
+ ILOGV("+ IGET '%s'=0x%08llx", ifield->field.name, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_GET(&ifield->field); \
+ } \
+ FINISH(2);
+
+#define HANDLE_IGET_X_QUICK(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ Object* obj; \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field offset */ \
+ ILOGV("|iget%s-quick v%d,v%d,field@+%u", \
+ (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNullExportPC(obj, fp, pc)) \
+ GOTO_exceptionThrown(); \
+ SET_REGISTER##_regsize(vdst, dvmGetField##_ftype(obj, ref)); \
+ ILOGV("+ IGETQ %d=0x%08llx", ref, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_IPUT_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ InstField* ifield; \
+ Object* obj; \
+ EXPORT_PC(); \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|iput%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNull(obj)) \
+ GOTO_exceptionThrown(); \
+ ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref); \
+ if (ifield == NULL) { \
+ ifield = dvmResolveInstField(curMethod->clazz, ref); \
+ if (ifield == NULL) \
+ GOTO_exceptionThrown(); \
+ } \
+ dvmSetField##_ftype(obj, ifield->byteOffset, \
+ GET_REGISTER##_regsize(vdst)); \
+ ILOGV("+ IPUT '%s'=0x%08llx", ifield->field.name, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_PUT(&ifield->field); \
+ } \
+ FINISH(2);
+
+#define HANDLE_IPUT_X_QUICK(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ Object* obj; \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field offset */ \
+ ILOGV("|iput%s-quick v%d,v%d,field@0x%04x", \
+ (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNullExportPC(obj, fp, pc)) \
+ GOTO_exceptionThrown(); \
+ dvmSetField##_ftype(obj, ref, GET_REGISTER##_regsize(vdst)); \
+ ILOGV("+ IPUTQ %d=0x%08llx", ref, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ } \
+ FINISH(2);
+
+/*
+ * The JIT needs dvmDexGetResolvedField() to return non-null.
+ * Since we use the portable interpreter to build the trace, the extra
+ * checks in HANDLE_SGET_X and HANDLE_SPUT_X are not needed for mterp.
+ */
+#define HANDLE_SGET_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/) \
+ { \
+ StaticField* sfield; \
+ vdst = INST_AA(inst); \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|sget%s v%d,sfield@0x%04x", (_opname), vdst, ref); \
+ sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+ if (sfield == NULL) { \
+ EXPORT_PC(); \
+ sfield = dvmResolveStaticField(curMethod->clazz, ref); \
+ if (sfield == NULL) \
+ GOTO_exceptionThrown(); \
+ if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) { \
+ ABORT_JIT_TSELECT(); \
+ } \
+ } \
+ SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield)); \
+ ILOGV("+ SGET '%s'=0x%08llx", \
+ sfield->field.name, (u8)GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_GET(&sfield->field); \
+ } \
+ FINISH(2);
+
+#define HANDLE_SPUT_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/) \
+ { \
+ StaticField* sfield; \
+ vdst = INST_AA(inst); \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|sput%s v%d,sfield@0x%04x", (_opname), vdst, ref); \
+ sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+ if (sfield == NULL) { \
+ EXPORT_PC(); \
+ sfield = dvmResolveStaticField(curMethod->clazz, ref); \
+ if (sfield == NULL) \
+ GOTO_exceptionThrown(); \
+ if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) { \
+ ABORT_JIT_TSELECT(); \
+ } \
+ } \
+ dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst)); \
+ ILOGV("+ SPUT '%s'=0x%08llx", \
+ sfield->field.name, (u8)GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_PUT(&sfield->field); \
+ } \
+ FINISH(2);
+
+/* File: cstubs/enddefs.c */
+
+/* undefine "magic" name remapping */
+#undef retval
+#undef pc
+#undef fp
+#undef curMethod
+#undef methodClassDex
+#undef self
+#undef debugTrackedRefStart
+
+/* File: armv5te/debug.c */
+#include <inttypes.h>
+
+/*
+ * Dump the fixed-purpose ARM registers, along with some other info.
+ *
+ * This function MUST be compiled in ARM mode -- THUMB will yield bogus
+ * results.
+ *
+ * This will NOT preserve r0-r3/ip.
+ */
+void dvmMterpDumpArmRegs(uint32_t r0, uint32_t r1, uint32_t r2, uint32_t r3)
+{
+ register uint32_t rPC asm("r4");
+ register uint32_t rFP asm("r5");
+ register uint32_t rGLUE asm("r6");
+ register uint32_t rINST asm("r7");
+ register uint32_t rIBASE asm("r8");
+ register uint32_t r9 asm("r9");
+ register uint32_t r10 asm("r10");
+
+ //extern char dvmAsmInstructionStart[];
+
+ printf("REGS: r0=%08x r1=%08x r2=%08x r3=%08x\n", r0, r1, r2, r3);
+ printf(" : rPC=%08x rFP=%08x rGLUE=%08x rINST=%08x\n",
+ rPC, rFP, rGLUE, rINST);
+ printf(" : rIBASE=%08x r9=%08x r10=%08x\n", rIBASE, r9, r10);
+
+ //MterpGlue* glue = (MterpGlue*) rGLUE;
+ //const Method* method = glue->method;
+ printf(" + self is %p\n", dvmThreadSelf());
+ //printf(" + currently in %s.%s %s\n",
+ // method->clazz->descriptor, method->name, method->shorty);
+ //printf(" + dvmAsmInstructionStart = %p\n", dvmAsmInstructionStart);
+ //printf(" + next handler for 0x%02x = %p\n",
+ // rINST & 0xff, dvmAsmInstructionStart + (rINST & 0xff) * 64);
+}
+
+/*
+ * Dump the StackSaveArea for the specified frame pointer.
+ */
+void dvmDumpFp(void* fp, StackSaveArea* otherSaveArea)
+{
+ StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+ printf("StackSaveArea for fp %p [%p/%p]:\n", fp, saveArea, otherSaveArea);
+#ifdef EASY_GDB
+ printf(" prevSave=%p, prevFrame=%p savedPc=%p meth=%p curPc=%p\n",
+ saveArea->prevSave, saveArea->prevFrame, saveArea->savedPc,
+ saveArea->method, saveArea->xtra.currentPc);
+#else
+ printf(" prevFrame=%p savedPc=%p meth=%p curPc=%p fp[0]=0x%08x\n",
+ saveArea->prevFrame, saveArea->savedPc,
+ saveArea->method, saveArea->xtra.currentPc,
+ *(u4*)fp);
+#endif
+}
+
+/*
+ * Does the bulk of the work for common_printMethod().
+ */
+void dvmMterpPrintMethod(Method* method)
+{
+ /*
+ * It is a direct (non-virtual) method if it is static, private,
+ * or a constructor.
+ */
+ bool isDirect =
+ ((method->accessFlags & (ACC_STATIC|ACC_PRIVATE)) != 0) ||
+ (method->name[0] == '<');
+
+ char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+
+ printf("<%c:%s.%s %s> ",
+ isDirect ? 'D' : 'V',
+ method->clazz->descriptor,
+ method->name,
+ desc);
+
+ free(desc);
+}
+
diff --git a/vm/mterp/out/InterpC-armv5te-vfp.c b/vm/mterp/out/InterpC-armv5te-vfp.c
new file mode 100644
index 0000000..48b8dbd
--- /dev/null
+++ b/vm/mterp/out/InterpC-armv5te-vfp.c
@@ -0,0 +1,1289 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'armv5te-vfp'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: c/header.c */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* common includes */
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "mterp/Mterp.h"
+#include <math.h> // needed for fmod, fmodf
+#include "mterp/common/FindInterface.h"
+
+/*
+ * Configuration defines. These affect the C implementations, i.e. the
+ * portable interpreter(s) and C stubs.
+ *
+ * Some defines are controlled by the Makefile, e.g.:
+ * WITH_INSTR_CHECKS
+ * WITH_TRACKREF_CHECKS
+ * EASY_GDB
+ * NDEBUG
+ *
+ * If THREADED_INTERP is not defined, we use a classic "while true / switch"
+ * interpreter. If it is defined, then the tail end of each instruction
+ * handler fetches the next instruction and jumps directly to the handler.
+ * This increases the size of the "Std" interpreter by about 10%, but
+ * provides a speedup of about the same magnitude.
+ *
+ * There's a "hybrid" approach that uses a goto table instead of a switch
+ * statement, avoiding the "is the opcode in range" tests required for switch.
+ * The performance is close to the threaded version, and without the 10%
+ * size increase, but the benchmark results are off enough that it's not
+ * worth adding as a third option.
+ */
+#define THREADED_INTERP /* threaded vs. while-loop interpreter */
+
+#ifdef WITH_INSTR_CHECKS /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * ARM EABI requires 64-bit alignment for access to 64-bit data types. We
+ * can't just use pointers to copy 64-bit values out of our interpreted
+ * register set, because gcc will generate ldrd/strd.
+ *
+ * The __UNION version copies data in and out of a union. The __MEMCPY
+ * version uses a memcpy() call to do the transfer; gcc is smart enough to
+ * not actually call memcpy(). The __UNION version is very bad on ARM;
+ * it only uses one more instruction than __MEMCPY, but for some reason
+ * gcc thinks it needs separate storage for every instance of the union.
+ * On top of that, it feels the need to zero them out at the start of the
+ * method. Net result is we zero out ~700 bytes of stack space at the top
+ * of the interpreter using ARM STM instructions.
+ */
+#if defined(__ARM_EABI__)
+//# define NO_UNALIGN_64__UNION
+# define NO_UNALIGN_64__MEMCPY
+#endif
+
+//#define LOG_INSTR /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Keep a tally of accesses to fields. Currently only works if full DEX
+ * optimization is disabled.
+ */
+#ifdef PROFILE_FIELD_ACCESS
+# define UPDATE_FIELD_GET(_field) { (_field)->gets++; }
+# define UPDATE_FIELD_PUT(_field) { (_field)->puts++; }
+#else
+# define UPDATE_FIELD_GET(_field) ((void)0)
+# define UPDATE_FIELD_PUT(_field) ((void)0)
+#endif
+
+/*
+ * Export another copy of the PC on every instruction; this is largely
+ * redundant with EXPORT_PC and the debugger code. This value can be
+ * compared against what we have stored on the stack with EXPORT_PC to
+ * help ensure that we aren't missing any export calls.
+ */
+#if WITH_EXTRA_GC_CHECKS > 1
+# define EXPORT_EXTRA_PC() (self->currentPc2 = pc)
+#else
+# define EXPORT_EXTRA_PC()
+#endif
+
+/*
+ * Adjust the program counter. "_offset" is a signed int, in 16-bit units.
+ *
+ * Assumes the existence of "const u2* pc" and "const u2* curMethod->insns".
+ *
+ * We don't advance the program counter until we finish an instruction or
+ * branch, because we do want to have to unroll the PC if there's an
+ * exception.
+ */
+#ifdef CHECK_BRANCH_OFFSETS
+# define ADJUST_PC(_offset) do { \
+ int myoff = _offset; /* deref only once */ \
+ if (pc + myoff < curMethod->insns || \
+ pc + myoff >= curMethod->insns + dvmGetMethodInsnsSize(curMethod)) \
+ { \
+ char* desc; \
+ desc = dexProtoCopyMethodDescriptor(&curMethod->prototype); \
+ LOGE("Invalid branch %d at 0x%04x in %s.%s %s\n", \
+ myoff, (int) (pc - curMethod->insns), \
+ curMethod->clazz->descriptor, curMethod->name, desc); \
+ free(desc); \
+ dvmAbort(); \
+ } \
+ pc += myoff; \
+ EXPORT_EXTRA_PC(); \
+ } while (false)
+#else
+# define ADJUST_PC(_offset) do { \
+ pc += _offset; \
+ EXPORT_EXTRA_PC(); \
+ } while (false)
+#endif
+
+/*
+ * If enabled, log instructions as we execute them.
+ */
+#ifdef LOG_INSTR
+# define ILOGD(...) ILOG(LOG_DEBUG, __VA_ARGS__)
+# define ILOGV(...) ILOG(LOG_VERBOSE, __VA_ARGS__)
+# define ILOG(_level, ...) do { \
+ char debugStrBuf[128]; \
+ snprintf(debugStrBuf, sizeof(debugStrBuf), __VA_ARGS__); \
+ if (curMethod != NULL) \
+ LOG(_level, LOG_TAG"i", "%-2d|%04x%s\n", \
+ self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+ else \
+ LOG(_level, LOG_TAG"i", "%-2d|####%s\n", \
+ self->threadId, debugStrBuf); \
+ } while(false)
+void dvmDumpRegs(const Method* method, const u4* framePtr, bool inOnly);
+# define DUMP_REGS(_meth, _frame, _inOnly) dvmDumpRegs(_meth, _frame, _inOnly)
+static const char kSpacing[] = " ";
+#else
+# define ILOGD(...) ((void)0)
+# define ILOGV(...) ((void)0)
+# define DUMP_REGS(_meth, _frame, _inOnly) ((void)0)
+#endif
+
+/* get a long from an array of u4 */
+static inline s8 getLongFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { s8 ll; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.parts[0] = ptr[0];
+ conv.parts[1] = ptr[1];
+ return conv.ll;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ s8 val;
+ memcpy(&val, &ptr[idx], 8);
+ return val;
+#else
+ return *((s8*) &ptr[idx]);
+#endif
+}
+
+/* store a long into an array of u4 */
+static inline void putLongToArray(u4* ptr, int idx, s8 val)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { s8 ll; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.ll = val;
+ ptr[0] = conv.parts[0];
+ ptr[1] = conv.parts[1];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ memcpy(&ptr[idx], &val, 8);
+#else
+ *((s8*) &ptr[idx]) = val;
+#endif
+}
+
+/* get a double from an array of u4 */
+static inline double getDoubleFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { double d; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.parts[0] = ptr[0];
+ conv.parts[1] = ptr[1];
+ return conv.d;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ double dval;
+ memcpy(&dval, &ptr[idx], 8);
+ return dval;
+#else
+ return *((double*) &ptr[idx]);
+#endif
+}
+
+/* store a double into an array of u4 */
+static inline void putDoubleToArray(u4* ptr, int idx, double dval)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { double d; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.d = dval;
+ ptr[0] = conv.parts[0];
+ ptr[1] = conv.parts[1];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ memcpy(&ptr[idx], &dval, 8);
+#else
+ *((double*) &ptr[idx]) = dval;
+#endif
+}
+
+/*
+ * If enabled, validate the register number on every access. Otherwise,
+ * just do an array access.
+ *
+ * Assumes the existence of "u4* fp".
+ *
+ * "_idx" may be referenced more than once.
+ */
+#ifdef CHECK_REGISTER_INDICES
+# define GET_REGISTER(_idx) \
+ ( (_idx) < curMethod->registersSize ? \
+ (fp[(_idx)]) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER(_idx, _val) \
+ ( (_idx) < curMethod->registersSize ? \
+ (fp[(_idx)] = (u4)(_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_AS_OBJECT(_idx) ((Object *)GET_REGISTER(_idx))
+# define SET_REGISTER_AS_OBJECT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_INT(_idx) ((s4) GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ getLongFromArray(fp, (_idx)) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER_WIDE(_idx, _val) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ putLongToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_FLOAT(_idx) \
+ ( (_idx) < curMethod->registersSize ? \
+ (*((float*) &fp[(_idx)])) : (assert(!"bad reg"),1969.0f) )
+# define SET_REGISTER_FLOAT(_idx, _val) \
+ ( (_idx) < curMethod->registersSize ? \
+ (*((float*) &fp[(_idx)]) = (_val)) : (assert(!"bad reg"),1969.0f) )
+# define GET_REGISTER_DOUBLE(_idx) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ getDoubleFromArray(fp, (_idx)) : (assert(!"bad reg"),1969.0) )
+# define SET_REGISTER_DOUBLE(_idx, _val) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ putDoubleToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969.0) )
+#else
+# define GET_REGISTER(_idx) (fp[(_idx)])
+# define SET_REGISTER(_idx, _val) (fp[(_idx)] = (_val))
+# define GET_REGISTER_AS_OBJECT(_idx) ((Object*) fp[(_idx)])
+# define SET_REGISTER_AS_OBJECT(_idx, _val) (fp[(_idx)] = (u4)(_val))
+# define GET_REGISTER_INT(_idx) ((s4)GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) getLongFromArray(fp, (_idx))
+# define SET_REGISTER_WIDE(_idx, _val) putLongToArray(fp, (_idx), (_val))
+# define GET_REGISTER_FLOAT(_idx) (*((float*) &fp[(_idx)]))
+# define SET_REGISTER_FLOAT(_idx, _val) (*((float*) &fp[(_idx)]) = (_val))
+# define GET_REGISTER_DOUBLE(_idx) getDoubleFromArray(fp, (_idx))
+# define SET_REGISTER_DOUBLE(_idx, _val) putDoubleToArray(fp, (_idx), (_val))
+#endif
+
+/*
+ * Get 16 bits from the specified offset of the program counter. We always
+ * want to load 16 bits at a time from the instruction stream -- it's more
+ * efficient than 8 and won't have the alignment problems that 32 might.
+ *
+ * Assumes existence of "const u2* pc".
+ */
+#define FETCH(_offset) (pc[(_offset)])
+
+/*
+ * Extract instruction byte from 16-bit fetch (_inst is a u2).
+ */
+#define INST_INST(_inst) ((_inst) & 0xff)
+
+/*
+ * Replace the opcode (used when handling breakpoints). _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
+ * Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
+ */
+#define INST_A(_inst) (((_inst) >> 8) & 0x0f)
+#define INST_B(_inst) ((_inst) >> 12)
+
+/*
+ * Get the 8-bit "vAA" 8-bit register index from the instruction word.
+ * (_inst is u2)
+ */
+#define INST_AA(_inst) ((_inst) >> 8)
+
+/*
+ * The current PC must be available to Throwable constructors, e.g.
+ * those created by dvmThrowException(), so that the exception stack
+ * trace can be generated correctly. If we don't do this, the offset
+ * within the current method won't be shown correctly. See the notes
+ * in Exception.c.
+ *
+ * This is also used to determine the address for precise GC.
+ *
+ * Assumes existence of "u4* fp" and "const u2* pc".
+ */
+#define EXPORT_PC() (SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc)
+
+/*
+ * Determine if we need to switch to a different interpreter. "_current"
+ * is either INTERP_STD or INTERP_DBG. It should be fixed for a given
+ * interpreter generation file, which should remove the outer conditional
+ * from the following.
+ *
+ * If we're building without debug and profiling support, we never switch.
+ */
+#if defined(WITH_JIT)
+# define NEED_INTERP_SWITCH(_current) ( \
+ (_current == INTERP_STD) ? \
+ dvmJitDebuggerOrProfilerActive() : !dvmJitDebuggerOrProfilerActive() )
+#else
+# define NEED_INTERP_SWITCH(_current) ( \
+ (_current == INTERP_STD) ? \
+ dvmDebuggerOrProfilerActive() : !dvmDebuggerOrProfilerActive() )
+#endif
+
+/*
+ * Check to see if "obj" is NULL. If so, throw an exception. Assumes the
+ * pc has already been exported to the stack.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler calls into
+ * something that could throw an exception (so we have already called
+ * EXPORT_PC at the top).
+ */
+static inline bool checkForNull(Object* obj)
+{
+ if (obj == NULL) {
+ dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+ return false;
+ }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+ if (!dvmIsValidObject(obj)) {
+ LOGE("Invalid object %p\n", obj);
+ dvmAbort();
+ }
+#endif
+#ifndef NDEBUG
+ if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+ /* probable heap corruption */
+ LOGE("Invalid object class %p (in %p)\n", obj->clazz, obj);
+ dvmAbort();
+ }
+#endif
+ return true;
+}
+
+/*
+ * Check to see if "obj" is NULL. If so, export the PC into the stack
+ * frame and throw an exception.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler doesn't do
+ * anything else that can throw an exception.
+ */
+static inline bool checkForNullExportPC(Object* obj, u4* fp, const u2* pc)
+{
+ if (obj == NULL) {
+ EXPORT_PC();
+ dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+ return false;
+ }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+ if (!dvmIsValidObject(obj)) {
+ LOGE("Invalid object %p\n", obj);
+ dvmAbort();
+ }
+#endif
+#ifndef NDEBUG
+ if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+ /* probable heap corruption */
+ LOGE("Invalid object class %p (in %p)\n", obj->clazz, obj);
+ dvmAbort();
+ }
+#endif
+ return true;
+}
+
+/* File: cstubs/stubdefs.c */
+/* this is a standard (no debug support) interpreter */
+#define INTERP_TYPE INTERP_STD
+#define CHECK_DEBUG_AND_PROF() ((void)0)
+# define CHECK_TRACKED_REFS() ((void)0)
+#define CHECK_JIT_BOOL() (false)
+#define CHECK_JIT_VOID()
+#define ABORT_JIT_TSELECT() ((void)0)
+
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...) \
+ void dvmMterp_##_target(MterpGlue* glue, ## __VA_ARGS__);
+
+#define GOTO_TARGET(_target, ...) \
+ void dvmMterp_##_target(MterpGlue* glue, ## __VA_ARGS__) { \
+ u2 ref, vsrc1, vsrc2, vdst; \
+ u2 inst = FETCH(0); \
+ const Method* methodToCall; \
+ StackSaveArea* debugSaveArea;
+
+#define GOTO_TARGET_END }
+
+/*
+ * Redefine what used to be local variable accesses into MterpGlue struct
+ * references. (These are undefined down in "footer.c".)
+ */
+#define retval glue->retval
+#define pc glue->pc
+#define fp glue->fp
+#define curMethod glue->method
+#define methodClassDex glue->methodClassDex
+#define self glue->self
+#define debugTrackedRefStart glue->debugTrackedRefStart
+
+/* ugh */
+#define STUB_HACK(x) x
+
+
+/*
+ * Opcode handler framing macros. Here, each opcode is a separate function
+ * that takes a "glue" argument and returns void. We can't declare
+ * these "static" because they may be called from an assembly stub.
+ */
+#define HANDLE_OPCODE(_op) \
+ void dvmMterp_##_op(MterpGlue* glue) { \
+ u2 ref, vsrc1, vsrc2, vdst; \
+ u2 inst = FETCH(0);
+
+#define OP_END }
+
+/*
+ * Like the "portable" FINISH, but don't reload "inst", and return to caller
+ * when done.
+ */
+#define FINISH(_offset) { \
+ ADJUST_PC(_offset); \
+ CHECK_DEBUG_AND_PROF(); \
+ CHECK_TRACKED_REFS(); \
+ return; \
+ }
+
+
+/*
+ * The "goto label" statements turn into function calls followed by
+ * return statements. Some of the functions take arguments, which in the
+ * portable interpreter are handled by assigning values to globals.
+ */
+
+#define GOTO_exceptionThrown() \
+ do { \
+ dvmMterp_exceptionThrown(glue); \
+ return; \
+ } while(false)
+
+#define GOTO_returnFromMethod() \
+ do { \
+ dvmMterp_returnFromMethod(glue); \
+ return; \
+ } while(false)
+
+#define GOTO_invoke(_target, _methodCallRange) \
+ do { \
+ dvmMterp_##_target(glue, _methodCallRange); \
+ return; \
+ } while(false)
+
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst) \
+ do { \
+ dvmMterp_invokeMethod(glue, _methodCallRange, _methodToCall, \
+ _vsrc1, _vdst); \
+ return; \
+ } while(false)
+
+/*
+ * As a special case, "goto bail" turns into a longjmp. Use "bail_switch"
+ * if we need to switch to the other interpreter upon our return.
+ */
+#define GOTO_bail() \
+ dvmMterpStdBail(glue, false);
+#define GOTO_bail_switch() \
+ dvmMterpStdBail(glue, true);
+
+/*
+ * Periodically check for thread suspension.
+ *
+ * While we're at it, see if a debugger has attached or the profiler has
+ * started. If so, switch to a different "goto" table.
+ */
+#define PERIODIC_CHECKS(_entryPoint, _pcadj) { \
+ if (dvmCheckSuspendQuick(self)) { \
+ EXPORT_PC(); /* need for precise GC */ \
+ dvmCheckSuspendPending(self); \
+ } \
+ if (NEED_INTERP_SWITCH(INTERP_TYPE)) { \
+ ADJUST_PC(_pcadj); \
+ glue->entryPoint = _entryPoint; \
+ LOGVV("threadid=%d: switch to STD ep=%d adj=%d\n", \
+ self->threadId, (_entryPoint), (_pcadj)); \
+ GOTO_bail_switch(); \
+ } \
+ }
+
+/* File: c/opcommon.c */
+/* forward declarations of goto targets */
+GOTO_TARGET_DECL(filledNewArray, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtual, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuper, bool methodCallRange);
+GOTO_TARGET_DECL(invokeInterface, bool methodCallRange);
+GOTO_TARGET_DECL(invokeDirect, bool methodCallRange);
+GOTO_TARGET_DECL(invokeStatic, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtualQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuperQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeMethod, bool methodCallRange, const Method* methodToCall,
+ u2 count, u2 regs);
+GOTO_TARGET_DECL(returnFromMethod);
+GOTO_TARGET_DECL(exceptionThrown);
+
+/*
+ * ===========================================================================
+ *
+ * What follows are opcode definitions shared between multiple opcodes with
+ * minor substitutions handled by the C pre-processor. These should probably
+ * use the mterp substitution mechanism instead, with the code here moved
+ * into common fragment files (like the asm "binop.S"), although it's hard
+ * to give up the C preprocessor in favor of the much simpler text subst.
+ *
+ * ===========================================================================
+ */
+
+#define HANDLE_NUMCONV(_opcode, _opname, _fromtype, _totype) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER##_totype(vdst, \
+ GET_REGISTER##_fromtype(vsrc1)); \
+ FINISH(1);
+
+#define HANDLE_FLOAT_TO_INT(_opcode, _opname, _fromvtype, _fromrtype, \
+ _tovtype, _tortype) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ { \
+ /* spec defines specific handling for +/- inf and NaN values */ \
+ _fromvtype val; \
+ _tovtype intMin, intMax, result; \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1); \
+ val = GET_REGISTER##_fromrtype(vsrc1); \
+ intMin = (_tovtype) 1 << (sizeof(_tovtype) * 8 -1); \
+ intMax = ~intMin; \
+ result = (_tovtype) val; \
+ if (val >= intMax) /* +inf */ \
+ result = intMax; \
+ else if (val <= intMin) /* -inf */ \
+ result = intMin; \
+ else if (val != val) /* NaN */ \
+ result = 0; \
+ else \
+ result = (_tovtype) val; \
+ SET_REGISTER##_tortype(vdst, result); \
+ } \
+ FINISH(1);
+
+#define HANDLE_INT_TO_SMALL(_opcode, _opname, _type) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|int-to-%s v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER(vdst, (_type) GET_REGISTER(vsrc1)); \
+ FINISH(1);
+
+/* NOTE: the comparison result is always a signed 4-byte integer */
+#define HANDLE_OP_CMPX(_opcode, _opname, _varType, _type, _nanVal) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ int result; \
+ u2 regs; \
+ _varType val1, val2; \
+ vdst = INST_AA(inst); \
+ regs = FETCH(1); \
+ vsrc1 = regs & 0xff; \
+ vsrc2 = regs >> 8; \
+ ILOGV("|cmp%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ val1 = GET_REGISTER##_type(vsrc1); \
+ val2 = GET_REGISTER##_type(vsrc2); \
+ if (val1 == val2) \
+ result = 0; \
+ else if (val1 < val2) \
+ result = -1; \
+ else if (val1 > val2) \
+ result = 1; \
+ else \
+ result = (_nanVal); \
+ ILOGV("+ result=%d\n", result); \
+ SET_REGISTER(vdst, result); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_IF_XX(_opcode, _opname, _cmp) \
+ HANDLE_OPCODE(_opcode /*vA, vB, +CCCC*/) \
+ vsrc1 = INST_A(inst); \
+ vsrc2 = INST_B(inst); \
+ if ((s4) GET_REGISTER(vsrc1) _cmp (s4) GET_REGISTER(vsrc2)) { \
+ int branchOffset = (s2)FETCH(1); /* sign-extended */ \
+ ILOGV("|if-%s v%d,v%d,+0x%04x", (_opname), vsrc1, vsrc2, \
+ branchOffset); \
+ ILOGV("> branch taken"); \
+ if (branchOffset < 0) \
+ PERIODIC_CHECKS(kInterpEntryInstr, branchOffset); \
+ FINISH(branchOffset); \
+ } else { \
+ ILOGV("|if-%s v%d,v%d,-", (_opname), vsrc1, vsrc2); \
+ FINISH(2); \
+ }
+
+#define HANDLE_OP_IF_XXZ(_opcode, _opname, _cmp) \
+ HANDLE_OPCODE(_opcode /*vAA, +BBBB*/) \
+ vsrc1 = INST_AA(inst); \
+ if ((s4) GET_REGISTER(vsrc1) _cmp 0) { \
+ int branchOffset = (s2)FETCH(1); /* sign-extended */ \
+ ILOGV("|if-%s v%d,+0x%04x", (_opname), vsrc1, branchOffset); \
+ ILOGV("> branch taken"); \
+ if (branchOffset < 0) \
+ PERIODIC_CHECKS(kInterpEntryInstr, branchOffset); \
+ FINISH(branchOffset); \
+ } else { \
+ ILOGV("|if-%s v%d,-", (_opname), vsrc1); \
+ FINISH(2); \
+ }
+
+#define HANDLE_UNOP(_opcode, _opname, _pfx, _sfx, _type) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER##_type(vdst, _pfx GET_REGISTER##_type(vsrc1) _sfx); \
+ FINISH(1);
+
+#define HANDLE_OP_X_INT(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER(vsrc1); \
+ secondVal = GET_REGISTER(vsrc2); \
+ if (secondVal == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && secondVal == -1) { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ /* non-div/rem case */ \
+ SET_REGISTER(vdst, \
+ (s4) GET_REGISTER(vsrc1) _op (s4) GET_REGISTER(vsrc2)); \
+ } \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_SHX_INT(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER(vdst, \
+ _cast GET_REGISTER(vsrc1) _op (GET_REGISTER(vsrc2) & 0x1f)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT16(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vA, vB, #+CCCC*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ vsrc2 = FETCH(1); \
+ ILOGV("|%s-int/lit16 v%d,v%d,#+0x%04x", \
+ (_opname), vdst, vsrc1, vsrc2); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, result; \
+ firstVal = GET_REGISTER(vsrc1); \
+ if ((s2) vsrc2 == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && ((s2) vsrc2) == -1) { \
+ /* won't generate /lit16 instr for this; check anyway */ \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op (s2) vsrc2; \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ /* non-div/rem case */ \
+ SET_REGISTER(vdst, GET_REGISTER(vsrc1) _op (s2) vsrc2); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT8(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/) \
+ { \
+ u2 litInfo; \
+ vdst = INST_AA(inst); \
+ litInfo = FETCH(1); \
+ vsrc1 = litInfo & 0xff; \
+ vsrc2 = litInfo >> 8; /* constant */ \
+ ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x", \
+ (_opname), vdst, vsrc1, vsrc2); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, result; \
+ firstVal = GET_REGISTER(vsrc1); \
+ if ((s1) vsrc2 == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && ((s1) vsrc2) == -1) { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op ((s1) vsrc2); \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ SET_REGISTER(vdst, \
+ (s4) GET_REGISTER(vsrc1) _op (s1) vsrc2); \
+ } \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_SHX_INT_LIT8(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/) \
+ { \
+ u2 litInfo; \
+ vdst = INST_AA(inst); \
+ litInfo = FETCH(1); \
+ vsrc1 = litInfo & 0xff; \
+ vsrc2 = litInfo >> 8; /* constant */ \
+ ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x", \
+ (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER(vdst, \
+ _cast GET_REGISTER(vsrc1) _op (vsrc2 & 0x1f)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_INT_2ADDR(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER(vdst); \
+ secondVal = GET_REGISTER(vsrc1); \
+ if (secondVal == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && secondVal == -1) { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ SET_REGISTER(vdst, \
+ (s4) GET_REGISTER(vdst) _op (s4) GET_REGISTER(vsrc1)); \
+ } \
+ FINISH(1);
+
+#define HANDLE_OP_SHX_INT_2ADDR(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER(vdst, \
+ _cast GET_REGISTER(vdst) _op (GET_REGISTER(vsrc1) & 0x1f)); \
+ FINISH(1);
+
+#define HANDLE_OP_X_LONG(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ if (_chkdiv != 0) { \
+ s8 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER_WIDE(vsrc1); \
+ secondVal = GET_REGISTER_WIDE(vsrc2); \
+ if (secondVal == 0LL) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u8)firstVal == 0x8000000000000000ULL && \
+ secondVal == -1LL) \
+ { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER_WIDE(vdst, result); \
+ } else { \
+ SET_REGISTER_WIDE(vdst, \
+ (s8) GET_REGISTER_WIDE(vsrc1) _op (s8) GET_REGISTER_WIDE(vsrc2)); \
+ } \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_SHX_LONG(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER_WIDE(vdst, \
+ _cast GET_REGISTER_WIDE(vsrc1) _op (GET_REGISTER(vsrc2) & 0x3f)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_LONG_2ADDR(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ if (_chkdiv != 0) { \
+ s8 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER_WIDE(vdst); \
+ secondVal = GET_REGISTER_WIDE(vsrc1); \
+ if (secondVal == 0LL) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u8)firstVal == 0x8000000000000000ULL && \
+ secondVal == -1LL) \
+ { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER_WIDE(vdst, result); \
+ } else { \
+ SET_REGISTER_WIDE(vdst, \
+ (s8) GET_REGISTER_WIDE(vdst) _op (s8)GET_REGISTER_WIDE(vsrc1));\
+ } \
+ FINISH(1);
+
+#define HANDLE_OP_SHX_LONG_2ADDR(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER_WIDE(vdst, \
+ _cast GET_REGISTER_WIDE(vdst) _op (GET_REGISTER(vsrc1) & 0x3f)); \
+ FINISH(1);
+
+#define HANDLE_OP_X_FLOAT(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-float v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER_FLOAT(vdst, \
+ GET_REGISTER_FLOAT(vsrc1) _op GET_REGISTER_FLOAT(vsrc2)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_DOUBLE(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-double v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER_DOUBLE(vdst, \
+ GET_REGISTER_DOUBLE(vsrc1) _op GET_REGISTER_DOUBLE(vsrc2)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_FLOAT_2ADDR(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-float-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER_FLOAT(vdst, \
+ GET_REGISTER_FLOAT(vdst) _op GET_REGISTER_FLOAT(vsrc1)); \
+ FINISH(1);
+
+#define HANDLE_OP_X_DOUBLE_2ADDR(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-double-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER_DOUBLE(vdst, \
+ GET_REGISTER_DOUBLE(vdst) _op GET_REGISTER_DOUBLE(vsrc1)); \
+ FINISH(1);
+
+#define HANDLE_OP_AGET(_opcode, _opname, _type, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ ArrayObject* arrayObj; \
+ u2 arrayInfo; \
+ EXPORT_PC(); \
+ vdst = INST_AA(inst); \
+ arrayInfo = FETCH(1); \
+ vsrc1 = arrayInfo & 0xff; /* array ptr */ \
+ vsrc2 = arrayInfo >> 8; /* index */ \
+ ILOGV("|aget%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ arrayObj = (ArrayObject*) GET_REGISTER(vsrc1); \
+ if (!checkForNull((Object*) arrayObj)) \
+ GOTO_exceptionThrown(); \
+ if (GET_REGISTER(vsrc2) >= arrayObj->length) { \
+ LOGV("Invalid array access: %p %d (len=%d)\n", \
+ arrayObj, vsrc2, arrayObj->length); \
+ dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+ NULL); \
+ GOTO_exceptionThrown(); \
+ } \
+ SET_REGISTER##_regsize(vdst, \
+ ((_type*) arrayObj->contents)[GET_REGISTER(vsrc2)]); \
+ ILOGV("+ AGET[%d]=0x%x", GET_REGISTER(vsrc2), GET_REGISTER(vdst)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_APUT(_opcode, _opname, _type, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ ArrayObject* arrayObj; \
+ u2 arrayInfo; \
+ EXPORT_PC(); \
+ vdst = INST_AA(inst); /* AA: source value */ \
+ arrayInfo = FETCH(1); \
+ vsrc1 = arrayInfo & 0xff; /* BB: array ptr */ \
+ vsrc2 = arrayInfo >> 8; /* CC: index */ \
+ ILOGV("|aput%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ arrayObj = (ArrayObject*) GET_REGISTER(vsrc1); \
+ if (!checkForNull((Object*) arrayObj)) \
+ GOTO_exceptionThrown(); \
+ if (GET_REGISTER(vsrc2) >= arrayObj->length) { \
+ dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+ NULL); \
+ GOTO_exceptionThrown(); \
+ } \
+ ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+ ((_type*) arrayObj->contents)[GET_REGISTER(vsrc2)] = \
+ GET_REGISTER##_regsize(vdst); \
+ } \
+ FINISH(2);
+
+/*
+ * It's possible to get a bad value out of a field with sub-32-bit stores
+ * because the -quick versions always operate on 32 bits. Consider:
+ * short foo = -1 (sets a 32-bit register to 0xffffffff)
+ * iput-quick foo (writes all 32 bits to the field)
+ * short bar = 1 (sets a 32-bit register to 0x00000001)
+ * iput-short (writes the low 16 bits to the field)
+ * iget-quick foo (reads all 32 bits from the field, yielding 0xffff0001)
+ * This can only happen when optimized and non-optimized code has interleaved
+ * access to the same field. This is unlikely but possible.
+ *
+ * The easiest way to fix this is to always read/write 32 bits at a time. On
+ * a device with a 16-bit data bus this is sub-optimal. (The alternative
+ * approach is to have sub-int versions of iget-quick, but now we're wasting
+ * Dalvik instruction space and making it less likely that handler code will
+ * already be in the CPU i-cache.)
+ */
+#define HANDLE_IGET_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ InstField* ifield; \
+ Object* obj; \
+ EXPORT_PC(); \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|iget%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNull(obj)) \
+ GOTO_exceptionThrown(); \
+ ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref); \
+ if (ifield == NULL) { \
+ ifield = dvmResolveInstField(curMethod->clazz, ref); \
+ if (ifield == NULL) \
+ GOTO_exceptionThrown(); \
+ } \
+ SET_REGISTER##_regsize(vdst, \
+ dvmGetField##_ftype(obj, ifield->byteOffset)); \
+ ILOGV("+ IGET '%s'=0x%08llx", ifield->field.name, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_GET(&ifield->field); \
+ } \
+ FINISH(2);
+
+#define HANDLE_IGET_X_QUICK(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ Object* obj; \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field offset */ \
+ ILOGV("|iget%s-quick v%d,v%d,field@+%u", \
+ (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNullExportPC(obj, fp, pc)) \
+ GOTO_exceptionThrown(); \
+ SET_REGISTER##_regsize(vdst, dvmGetField##_ftype(obj, ref)); \
+ ILOGV("+ IGETQ %d=0x%08llx", ref, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_IPUT_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ InstField* ifield; \
+ Object* obj; \
+ EXPORT_PC(); \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|iput%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNull(obj)) \
+ GOTO_exceptionThrown(); \
+ ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref); \
+ if (ifield == NULL) { \
+ ifield = dvmResolveInstField(curMethod->clazz, ref); \
+ if (ifield == NULL) \
+ GOTO_exceptionThrown(); \
+ } \
+ dvmSetField##_ftype(obj, ifield->byteOffset, \
+ GET_REGISTER##_regsize(vdst)); \
+ ILOGV("+ IPUT '%s'=0x%08llx", ifield->field.name, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_PUT(&ifield->field); \
+ } \
+ FINISH(2);
+
+#define HANDLE_IPUT_X_QUICK(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ Object* obj; \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field offset */ \
+ ILOGV("|iput%s-quick v%d,v%d,field@0x%04x", \
+ (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNullExportPC(obj, fp, pc)) \
+ GOTO_exceptionThrown(); \
+ dvmSetField##_ftype(obj, ref, GET_REGISTER##_regsize(vdst)); \
+ ILOGV("+ IPUTQ %d=0x%08llx", ref, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ } \
+ FINISH(2);
+
+/*
+ * The JIT needs dvmDexGetResolvedField() to return non-null.
+ * Since we use the portable interpreter to build the trace, the extra
+ * checks in HANDLE_SGET_X and HANDLE_SPUT_X are not needed for mterp.
+ */
+#define HANDLE_SGET_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/) \
+ { \
+ StaticField* sfield; \
+ vdst = INST_AA(inst); \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|sget%s v%d,sfield@0x%04x", (_opname), vdst, ref); \
+ sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+ if (sfield == NULL) { \
+ EXPORT_PC(); \
+ sfield = dvmResolveStaticField(curMethod->clazz, ref); \
+ if (sfield == NULL) \
+ GOTO_exceptionThrown(); \
+ if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) { \
+ ABORT_JIT_TSELECT(); \
+ } \
+ } \
+ SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield)); \
+ ILOGV("+ SGET '%s'=0x%08llx", \
+ sfield->field.name, (u8)GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_GET(&sfield->field); \
+ } \
+ FINISH(2);
+
+#define HANDLE_SPUT_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/) \
+ { \
+ StaticField* sfield; \
+ vdst = INST_AA(inst); \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|sput%s v%d,sfield@0x%04x", (_opname), vdst, ref); \
+ sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+ if (sfield == NULL) { \
+ EXPORT_PC(); \
+ sfield = dvmResolveStaticField(curMethod->clazz, ref); \
+ if (sfield == NULL) \
+ GOTO_exceptionThrown(); \
+ if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) { \
+ ABORT_JIT_TSELECT(); \
+ } \
+ } \
+ dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst)); \
+ ILOGV("+ SPUT '%s'=0x%08llx", \
+ sfield->field.name, (u8)GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_PUT(&sfield->field); \
+ } \
+ FINISH(2);
+
+/* File: cstubs/enddefs.c */
+
+/* undefine "magic" name remapping */
+#undef retval
+#undef pc
+#undef fp
+#undef curMethod
+#undef methodClassDex
+#undef self
+#undef debugTrackedRefStart
+
+/* File: armv5te/debug.c */
+#include <inttypes.h>
+
+/*
+ * Dump the fixed-purpose ARM registers, along with some other info.
+ *
+ * This function MUST be compiled in ARM mode -- THUMB will yield bogus
+ * results.
+ *
+ * This will NOT preserve r0-r3/ip.
+ */
+void dvmMterpDumpArmRegs(uint32_t r0, uint32_t r1, uint32_t r2, uint32_t r3)
+{
+ register uint32_t rPC asm("r4");
+ register uint32_t rFP asm("r5");
+ register uint32_t rGLUE asm("r6");
+ register uint32_t rINST asm("r7");
+ register uint32_t rIBASE asm("r8");
+ register uint32_t r9 asm("r9");
+ register uint32_t r10 asm("r10");
+
+ //extern char dvmAsmInstructionStart[];
+
+ printf("REGS: r0=%08x r1=%08x r2=%08x r3=%08x\n", r0, r1, r2, r3);
+ printf(" : rPC=%08x rFP=%08x rGLUE=%08x rINST=%08x\n",
+ rPC, rFP, rGLUE, rINST);
+ printf(" : rIBASE=%08x r9=%08x r10=%08x\n", rIBASE, r9, r10);
+
+ //MterpGlue* glue = (MterpGlue*) rGLUE;
+ //const Method* method = glue->method;
+ printf(" + self is %p\n", dvmThreadSelf());
+ //printf(" + currently in %s.%s %s\n",
+ // method->clazz->descriptor, method->name, method->shorty);
+ //printf(" + dvmAsmInstructionStart = %p\n", dvmAsmInstructionStart);
+ //printf(" + next handler for 0x%02x = %p\n",
+ // rINST & 0xff, dvmAsmInstructionStart + (rINST & 0xff) * 64);
+}
+
+/*
+ * Dump the StackSaveArea for the specified frame pointer.
+ */
+void dvmDumpFp(void* fp, StackSaveArea* otherSaveArea)
+{
+ StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+ printf("StackSaveArea for fp %p [%p/%p]:\n", fp, saveArea, otherSaveArea);
+#ifdef EASY_GDB
+ printf(" prevSave=%p, prevFrame=%p savedPc=%p meth=%p curPc=%p\n",
+ saveArea->prevSave, saveArea->prevFrame, saveArea->savedPc,
+ saveArea->method, saveArea->xtra.currentPc);
+#else
+ printf(" prevFrame=%p savedPc=%p meth=%p curPc=%p fp[0]=0x%08x\n",
+ saveArea->prevFrame, saveArea->savedPc,
+ saveArea->method, saveArea->xtra.currentPc,
+ *(u4*)fp);
+#endif
+}
+
+/*
+ * Does the bulk of the work for common_printMethod().
+ */
+void dvmMterpPrintMethod(Method* method)
+{
+ /*
+ * It is a direct (non-virtual) method if it is static, private,
+ * or a constructor.
+ */
+ bool isDirect =
+ ((method->accessFlags & (ACC_STATIC|ACC_PRIVATE)) != 0) ||
+ (method->name[0] == '<');
+
+ char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+
+ printf("<%c:%s.%s %s> ",
+ isDirect ? 'D' : 'V',
+ method->clazz->descriptor,
+ method->name,
+ desc);
+
+ free(desc);
+}
+
diff --git a/vm/mterp/out/InterpC-armv5te.c b/vm/mterp/out/InterpC-armv5te.c
new file mode 100644
index 0000000..dfadf21
--- /dev/null
+++ b/vm/mterp/out/InterpC-armv5te.c
@@ -0,0 +1,1289 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'armv5te'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: c/header.c */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* common includes */
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "mterp/Mterp.h"
+#include <math.h> // needed for fmod, fmodf
+#include "mterp/common/FindInterface.h"
+
+/*
+ * Configuration defines. These affect the C implementations, i.e. the
+ * portable interpreter(s) and C stubs.
+ *
+ * Some defines are controlled by the Makefile, e.g.:
+ * WITH_INSTR_CHECKS
+ * WITH_TRACKREF_CHECKS
+ * EASY_GDB
+ * NDEBUG
+ *
+ * If THREADED_INTERP is not defined, we use a classic "while true / switch"
+ * interpreter. If it is defined, then the tail end of each instruction
+ * handler fetches the next instruction and jumps directly to the handler.
+ * This increases the size of the "Std" interpreter by about 10%, but
+ * provides a speedup of about the same magnitude.
+ *
+ * There's a "hybrid" approach that uses a goto table instead of a switch
+ * statement, avoiding the "is the opcode in range" tests required for switch.
+ * The performance is close to the threaded version, and without the 10%
+ * size increase, but the benchmark results are off enough that it's not
+ * worth adding as a third option.
+ */
+#define THREADED_INTERP /* threaded vs. while-loop interpreter */
+
+#ifdef WITH_INSTR_CHECKS /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * ARM EABI requires 64-bit alignment for access to 64-bit data types. We
+ * can't just use pointers to copy 64-bit values out of our interpreted
+ * register set, because gcc will generate ldrd/strd.
+ *
+ * The __UNION version copies data in and out of a union. The __MEMCPY
+ * version uses a memcpy() call to do the transfer; gcc is smart enough to
+ * not actually call memcpy(). The __UNION version is very bad on ARM;
+ * it only uses one more instruction than __MEMCPY, but for some reason
+ * gcc thinks it needs separate storage for every instance of the union.
+ * On top of that, it feels the need to zero them out at the start of the
+ * method. Net result is we zero out ~700 bytes of stack space at the top
+ * of the interpreter using ARM STM instructions.
+ */
+#if defined(__ARM_EABI__)
+//# define NO_UNALIGN_64__UNION
+# define NO_UNALIGN_64__MEMCPY
+#endif
+
+//#define LOG_INSTR /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Keep a tally of accesses to fields. Currently only works if full DEX
+ * optimization is disabled.
+ */
+#ifdef PROFILE_FIELD_ACCESS
+# define UPDATE_FIELD_GET(_field) { (_field)->gets++; }
+# define UPDATE_FIELD_PUT(_field) { (_field)->puts++; }
+#else
+# define UPDATE_FIELD_GET(_field) ((void)0)
+# define UPDATE_FIELD_PUT(_field) ((void)0)
+#endif
+
+/*
+ * Export another copy of the PC on every instruction; this is largely
+ * redundant with EXPORT_PC and the debugger code. This value can be
+ * compared against what we have stored on the stack with EXPORT_PC to
+ * help ensure that we aren't missing any export calls.
+ */
+#if WITH_EXTRA_GC_CHECKS > 1
+# define EXPORT_EXTRA_PC() (self->currentPc2 = pc)
+#else
+# define EXPORT_EXTRA_PC()
+#endif
+
+/*
+ * Adjust the program counter. "_offset" is a signed int, in 16-bit units.
+ *
+ * Assumes the existence of "const u2* pc" and "const u2* curMethod->insns".
+ *
+ * We don't advance the program counter until we finish an instruction or
+ * branch, because we do want to have to unroll the PC if there's an
+ * exception.
+ */
+#ifdef CHECK_BRANCH_OFFSETS
+# define ADJUST_PC(_offset) do { \
+ int myoff = _offset; /* deref only once */ \
+ if (pc + myoff < curMethod->insns || \
+ pc + myoff >= curMethod->insns + dvmGetMethodInsnsSize(curMethod)) \
+ { \
+ char* desc; \
+ desc = dexProtoCopyMethodDescriptor(&curMethod->prototype); \
+ LOGE("Invalid branch %d at 0x%04x in %s.%s %s\n", \
+ myoff, (int) (pc - curMethod->insns), \
+ curMethod->clazz->descriptor, curMethod->name, desc); \
+ free(desc); \
+ dvmAbort(); \
+ } \
+ pc += myoff; \
+ EXPORT_EXTRA_PC(); \
+ } while (false)
+#else
+# define ADJUST_PC(_offset) do { \
+ pc += _offset; \
+ EXPORT_EXTRA_PC(); \
+ } while (false)
+#endif
+
+/*
+ * If enabled, log instructions as we execute them.
+ */
+#ifdef LOG_INSTR
+# define ILOGD(...) ILOG(LOG_DEBUG, __VA_ARGS__)
+# define ILOGV(...) ILOG(LOG_VERBOSE, __VA_ARGS__)
+# define ILOG(_level, ...) do { \
+ char debugStrBuf[128]; \
+ snprintf(debugStrBuf, sizeof(debugStrBuf), __VA_ARGS__); \
+ if (curMethod != NULL) \
+ LOG(_level, LOG_TAG"i", "%-2d|%04x%s\n", \
+ self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+ else \
+ LOG(_level, LOG_TAG"i", "%-2d|####%s\n", \
+ self->threadId, debugStrBuf); \
+ } while(false)
+void dvmDumpRegs(const Method* method, const u4* framePtr, bool inOnly);
+# define DUMP_REGS(_meth, _frame, _inOnly) dvmDumpRegs(_meth, _frame, _inOnly)
+static const char kSpacing[] = " ";
+#else
+# define ILOGD(...) ((void)0)
+# define ILOGV(...) ((void)0)
+# define DUMP_REGS(_meth, _frame, _inOnly) ((void)0)
+#endif
+
+/* get a long from an array of u4 */
+static inline s8 getLongFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { s8 ll; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.parts[0] = ptr[0];
+ conv.parts[1] = ptr[1];
+ return conv.ll;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ s8 val;
+ memcpy(&val, &ptr[idx], 8);
+ return val;
+#else
+ return *((s8*) &ptr[idx]);
+#endif
+}
+
+/* store a long into an array of u4 */
+static inline void putLongToArray(u4* ptr, int idx, s8 val)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { s8 ll; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.ll = val;
+ ptr[0] = conv.parts[0];
+ ptr[1] = conv.parts[1];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ memcpy(&ptr[idx], &val, 8);
+#else
+ *((s8*) &ptr[idx]) = val;
+#endif
+}
+
+/* get a double from an array of u4 */
+static inline double getDoubleFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { double d; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.parts[0] = ptr[0];
+ conv.parts[1] = ptr[1];
+ return conv.d;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ double dval;
+ memcpy(&dval, &ptr[idx], 8);
+ return dval;
+#else
+ return *((double*) &ptr[idx]);
+#endif
+}
+
+/* store a double into an array of u4 */
+static inline void putDoubleToArray(u4* ptr, int idx, double dval)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { double d; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.d = dval;
+ ptr[0] = conv.parts[0];
+ ptr[1] = conv.parts[1];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ memcpy(&ptr[idx], &dval, 8);
+#else
+ *((double*) &ptr[idx]) = dval;
+#endif
+}
+
+/*
+ * If enabled, validate the register number on every access. Otherwise,
+ * just do an array access.
+ *
+ * Assumes the existence of "u4* fp".
+ *
+ * "_idx" may be referenced more than once.
+ */
+#ifdef CHECK_REGISTER_INDICES
+# define GET_REGISTER(_idx) \
+ ( (_idx) < curMethod->registersSize ? \
+ (fp[(_idx)]) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER(_idx, _val) \
+ ( (_idx) < curMethod->registersSize ? \
+ (fp[(_idx)] = (u4)(_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_AS_OBJECT(_idx) ((Object *)GET_REGISTER(_idx))
+# define SET_REGISTER_AS_OBJECT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_INT(_idx) ((s4) GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ getLongFromArray(fp, (_idx)) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER_WIDE(_idx, _val) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ putLongToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_FLOAT(_idx) \
+ ( (_idx) < curMethod->registersSize ? \
+ (*((float*) &fp[(_idx)])) : (assert(!"bad reg"),1969.0f) )
+# define SET_REGISTER_FLOAT(_idx, _val) \
+ ( (_idx) < curMethod->registersSize ? \
+ (*((float*) &fp[(_idx)]) = (_val)) : (assert(!"bad reg"),1969.0f) )
+# define GET_REGISTER_DOUBLE(_idx) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ getDoubleFromArray(fp, (_idx)) : (assert(!"bad reg"),1969.0) )
+# define SET_REGISTER_DOUBLE(_idx, _val) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ putDoubleToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969.0) )
+#else
+# define GET_REGISTER(_idx) (fp[(_idx)])
+# define SET_REGISTER(_idx, _val) (fp[(_idx)] = (_val))
+# define GET_REGISTER_AS_OBJECT(_idx) ((Object*) fp[(_idx)])
+# define SET_REGISTER_AS_OBJECT(_idx, _val) (fp[(_idx)] = (u4)(_val))
+# define GET_REGISTER_INT(_idx) ((s4)GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) getLongFromArray(fp, (_idx))
+# define SET_REGISTER_WIDE(_idx, _val) putLongToArray(fp, (_idx), (_val))
+# define GET_REGISTER_FLOAT(_idx) (*((float*) &fp[(_idx)]))
+# define SET_REGISTER_FLOAT(_idx, _val) (*((float*) &fp[(_idx)]) = (_val))
+# define GET_REGISTER_DOUBLE(_idx) getDoubleFromArray(fp, (_idx))
+# define SET_REGISTER_DOUBLE(_idx, _val) putDoubleToArray(fp, (_idx), (_val))
+#endif
+
+/*
+ * Get 16 bits from the specified offset of the program counter. We always
+ * want to load 16 bits at a time from the instruction stream -- it's more
+ * efficient than 8 and won't have the alignment problems that 32 might.
+ *
+ * Assumes existence of "const u2* pc".
+ */
+#define FETCH(_offset) (pc[(_offset)])
+
+/*
+ * Extract instruction byte from 16-bit fetch (_inst is a u2).
+ */
+#define INST_INST(_inst) ((_inst) & 0xff)
+
+/*
+ * Replace the opcode (used when handling breakpoints). _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
+ * Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
+ */
+#define INST_A(_inst) (((_inst) >> 8) & 0x0f)
+#define INST_B(_inst) ((_inst) >> 12)
+
+/*
+ * Get the 8-bit "vAA" 8-bit register index from the instruction word.
+ * (_inst is u2)
+ */
+#define INST_AA(_inst) ((_inst) >> 8)
+
+/*
+ * The current PC must be available to Throwable constructors, e.g.
+ * those created by dvmThrowException(), so that the exception stack
+ * trace can be generated correctly. If we don't do this, the offset
+ * within the current method won't be shown correctly. See the notes
+ * in Exception.c.
+ *
+ * This is also used to determine the address for precise GC.
+ *
+ * Assumes existence of "u4* fp" and "const u2* pc".
+ */
+#define EXPORT_PC() (SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc)
+
+/*
+ * Determine if we need to switch to a different interpreter. "_current"
+ * is either INTERP_STD or INTERP_DBG. It should be fixed for a given
+ * interpreter generation file, which should remove the outer conditional
+ * from the following.
+ *
+ * If we're building without debug and profiling support, we never switch.
+ */
+#if defined(WITH_JIT)
+# define NEED_INTERP_SWITCH(_current) ( \
+ (_current == INTERP_STD) ? \
+ dvmJitDebuggerOrProfilerActive() : !dvmJitDebuggerOrProfilerActive() )
+#else
+# define NEED_INTERP_SWITCH(_current) ( \
+ (_current == INTERP_STD) ? \
+ dvmDebuggerOrProfilerActive() : !dvmDebuggerOrProfilerActive() )
+#endif
+
+/*
+ * Check to see if "obj" is NULL. If so, throw an exception. Assumes the
+ * pc has already been exported to the stack.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler calls into
+ * something that could throw an exception (so we have already called
+ * EXPORT_PC at the top).
+ */
+static inline bool checkForNull(Object* obj)
+{
+ if (obj == NULL) {
+ dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+ return false;
+ }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+ if (!dvmIsValidObject(obj)) {
+ LOGE("Invalid object %p\n", obj);
+ dvmAbort();
+ }
+#endif
+#ifndef NDEBUG
+ if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+ /* probable heap corruption */
+ LOGE("Invalid object class %p (in %p)\n", obj->clazz, obj);
+ dvmAbort();
+ }
+#endif
+ return true;
+}
+
+/*
+ * Check to see if "obj" is NULL. If so, export the PC into the stack
+ * frame and throw an exception.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler doesn't do
+ * anything else that can throw an exception.
+ */
+static inline bool checkForNullExportPC(Object* obj, u4* fp, const u2* pc)
+{
+ if (obj == NULL) {
+ EXPORT_PC();
+ dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+ return false;
+ }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+ if (!dvmIsValidObject(obj)) {
+ LOGE("Invalid object %p\n", obj);
+ dvmAbort();
+ }
+#endif
+#ifndef NDEBUG
+ if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+ /* probable heap corruption */
+ LOGE("Invalid object class %p (in %p)\n", obj->clazz, obj);
+ dvmAbort();
+ }
+#endif
+ return true;
+}
+
+/* File: cstubs/stubdefs.c */
+/* this is a standard (no debug support) interpreter */
+#define INTERP_TYPE INTERP_STD
+#define CHECK_DEBUG_AND_PROF() ((void)0)
+# define CHECK_TRACKED_REFS() ((void)0)
+#define CHECK_JIT_BOOL() (false)
+#define CHECK_JIT_VOID()
+#define ABORT_JIT_TSELECT() ((void)0)
+
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...) \
+ void dvmMterp_##_target(MterpGlue* glue, ## __VA_ARGS__);
+
+#define GOTO_TARGET(_target, ...) \
+ void dvmMterp_##_target(MterpGlue* glue, ## __VA_ARGS__) { \
+ u2 ref, vsrc1, vsrc2, vdst; \
+ u2 inst = FETCH(0); \
+ const Method* methodToCall; \
+ StackSaveArea* debugSaveArea;
+
+#define GOTO_TARGET_END }
+
+/*
+ * Redefine what used to be local variable accesses into MterpGlue struct
+ * references. (These are undefined down in "footer.c".)
+ */
+#define retval glue->retval
+#define pc glue->pc
+#define fp glue->fp
+#define curMethod glue->method
+#define methodClassDex glue->methodClassDex
+#define self glue->self
+#define debugTrackedRefStart glue->debugTrackedRefStart
+
+/* ugh */
+#define STUB_HACK(x) x
+
+
+/*
+ * Opcode handler framing macros. Here, each opcode is a separate function
+ * that takes a "glue" argument and returns void. We can't declare
+ * these "static" because they may be called from an assembly stub.
+ */
+#define HANDLE_OPCODE(_op) \
+ void dvmMterp_##_op(MterpGlue* glue) { \
+ u2 ref, vsrc1, vsrc2, vdst; \
+ u2 inst = FETCH(0);
+
+#define OP_END }
+
+/*
+ * Like the "portable" FINISH, but don't reload "inst", and return to caller
+ * when done.
+ */
+#define FINISH(_offset) { \
+ ADJUST_PC(_offset); \
+ CHECK_DEBUG_AND_PROF(); \
+ CHECK_TRACKED_REFS(); \
+ return; \
+ }
+
+
+/*
+ * The "goto label" statements turn into function calls followed by
+ * return statements. Some of the functions take arguments, which in the
+ * portable interpreter are handled by assigning values to globals.
+ */
+
+#define GOTO_exceptionThrown() \
+ do { \
+ dvmMterp_exceptionThrown(glue); \
+ return; \
+ } while(false)
+
+#define GOTO_returnFromMethod() \
+ do { \
+ dvmMterp_returnFromMethod(glue); \
+ return; \
+ } while(false)
+
+#define GOTO_invoke(_target, _methodCallRange) \
+ do { \
+ dvmMterp_##_target(glue, _methodCallRange); \
+ return; \
+ } while(false)
+
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst) \
+ do { \
+ dvmMterp_invokeMethod(glue, _methodCallRange, _methodToCall, \
+ _vsrc1, _vdst); \
+ return; \
+ } while(false)
+
+/*
+ * As a special case, "goto bail" turns into a longjmp. Use "bail_switch"
+ * if we need to switch to the other interpreter upon our return.
+ */
+#define GOTO_bail() \
+ dvmMterpStdBail(glue, false);
+#define GOTO_bail_switch() \
+ dvmMterpStdBail(glue, true);
+
+/*
+ * Periodically check for thread suspension.
+ *
+ * While we're at it, see if a debugger has attached or the profiler has
+ * started. If so, switch to a different "goto" table.
+ */
+#define PERIODIC_CHECKS(_entryPoint, _pcadj) { \
+ if (dvmCheckSuspendQuick(self)) { \
+ EXPORT_PC(); /* need for precise GC */ \
+ dvmCheckSuspendPending(self); \
+ } \
+ if (NEED_INTERP_SWITCH(INTERP_TYPE)) { \
+ ADJUST_PC(_pcadj); \
+ glue->entryPoint = _entryPoint; \
+ LOGVV("threadid=%d: switch to STD ep=%d adj=%d\n", \
+ self->threadId, (_entryPoint), (_pcadj)); \
+ GOTO_bail_switch(); \
+ } \
+ }
+
+/* File: c/opcommon.c */
+/* forward declarations of goto targets */
+GOTO_TARGET_DECL(filledNewArray, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtual, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuper, bool methodCallRange);
+GOTO_TARGET_DECL(invokeInterface, bool methodCallRange);
+GOTO_TARGET_DECL(invokeDirect, bool methodCallRange);
+GOTO_TARGET_DECL(invokeStatic, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtualQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuperQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeMethod, bool methodCallRange, const Method* methodToCall,
+ u2 count, u2 regs);
+GOTO_TARGET_DECL(returnFromMethod);
+GOTO_TARGET_DECL(exceptionThrown);
+
+/*
+ * ===========================================================================
+ *
+ * What follows are opcode definitions shared between multiple opcodes with
+ * minor substitutions handled by the C pre-processor. These should probably
+ * use the mterp substitution mechanism instead, with the code here moved
+ * into common fragment files (like the asm "binop.S"), although it's hard
+ * to give up the C preprocessor in favor of the much simpler text subst.
+ *
+ * ===========================================================================
+ */
+
+#define HANDLE_NUMCONV(_opcode, _opname, _fromtype, _totype) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER##_totype(vdst, \
+ GET_REGISTER##_fromtype(vsrc1)); \
+ FINISH(1);
+
+#define HANDLE_FLOAT_TO_INT(_opcode, _opname, _fromvtype, _fromrtype, \
+ _tovtype, _tortype) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ { \
+ /* spec defines specific handling for +/- inf and NaN values */ \
+ _fromvtype val; \
+ _tovtype intMin, intMax, result; \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1); \
+ val = GET_REGISTER##_fromrtype(vsrc1); \
+ intMin = (_tovtype) 1 << (sizeof(_tovtype) * 8 -1); \
+ intMax = ~intMin; \
+ result = (_tovtype) val; \
+ if (val >= intMax) /* +inf */ \
+ result = intMax; \
+ else if (val <= intMin) /* -inf */ \
+ result = intMin; \
+ else if (val != val) /* NaN */ \
+ result = 0; \
+ else \
+ result = (_tovtype) val; \
+ SET_REGISTER##_tortype(vdst, result); \
+ } \
+ FINISH(1);
+
+#define HANDLE_INT_TO_SMALL(_opcode, _opname, _type) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|int-to-%s v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER(vdst, (_type) GET_REGISTER(vsrc1)); \
+ FINISH(1);
+
+/* NOTE: the comparison result is always a signed 4-byte integer */
+#define HANDLE_OP_CMPX(_opcode, _opname, _varType, _type, _nanVal) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ int result; \
+ u2 regs; \
+ _varType val1, val2; \
+ vdst = INST_AA(inst); \
+ regs = FETCH(1); \
+ vsrc1 = regs & 0xff; \
+ vsrc2 = regs >> 8; \
+ ILOGV("|cmp%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ val1 = GET_REGISTER##_type(vsrc1); \
+ val2 = GET_REGISTER##_type(vsrc2); \
+ if (val1 == val2) \
+ result = 0; \
+ else if (val1 < val2) \
+ result = -1; \
+ else if (val1 > val2) \
+ result = 1; \
+ else \
+ result = (_nanVal); \
+ ILOGV("+ result=%d\n", result); \
+ SET_REGISTER(vdst, result); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_IF_XX(_opcode, _opname, _cmp) \
+ HANDLE_OPCODE(_opcode /*vA, vB, +CCCC*/) \
+ vsrc1 = INST_A(inst); \
+ vsrc2 = INST_B(inst); \
+ if ((s4) GET_REGISTER(vsrc1) _cmp (s4) GET_REGISTER(vsrc2)) { \
+ int branchOffset = (s2)FETCH(1); /* sign-extended */ \
+ ILOGV("|if-%s v%d,v%d,+0x%04x", (_opname), vsrc1, vsrc2, \
+ branchOffset); \
+ ILOGV("> branch taken"); \
+ if (branchOffset < 0) \
+ PERIODIC_CHECKS(kInterpEntryInstr, branchOffset); \
+ FINISH(branchOffset); \
+ } else { \
+ ILOGV("|if-%s v%d,v%d,-", (_opname), vsrc1, vsrc2); \
+ FINISH(2); \
+ }
+
+#define HANDLE_OP_IF_XXZ(_opcode, _opname, _cmp) \
+ HANDLE_OPCODE(_opcode /*vAA, +BBBB*/) \
+ vsrc1 = INST_AA(inst); \
+ if ((s4) GET_REGISTER(vsrc1) _cmp 0) { \
+ int branchOffset = (s2)FETCH(1); /* sign-extended */ \
+ ILOGV("|if-%s v%d,+0x%04x", (_opname), vsrc1, branchOffset); \
+ ILOGV("> branch taken"); \
+ if (branchOffset < 0) \
+ PERIODIC_CHECKS(kInterpEntryInstr, branchOffset); \
+ FINISH(branchOffset); \
+ } else { \
+ ILOGV("|if-%s v%d,-", (_opname), vsrc1); \
+ FINISH(2); \
+ }
+
+#define HANDLE_UNOP(_opcode, _opname, _pfx, _sfx, _type) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER##_type(vdst, _pfx GET_REGISTER##_type(vsrc1) _sfx); \
+ FINISH(1);
+
+#define HANDLE_OP_X_INT(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER(vsrc1); \
+ secondVal = GET_REGISTER(vsrc2); \
+ if (secondVal == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && secondVal == -1) { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ /* non-div/rem case */ \
+ SET_REGISTER(vdst, \
+ (s4) GET_REGISTER(vsrc1) _op (s4) GET_REGISTER(vsrc2)); \
+ } \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_SHX_INT(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER(vdst, \
+ _cast GET_REGISTER(vsrc1) _op (GET_REGISTER(vsrc2) & 0x1f)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT16(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vA, vB, #+CCCC*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ vsrc2 = FETCH(1); \
+ ILOGV("|%s-int/lit16 v%d,v%d,#+0x%04x", \
+ (_opname), vdst, vsrc1, vsrc2); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, result; \
+ firstVal = GET_REGISTER(vsrc1); \
+ if ((s2) vsrc2 == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && ((s2) vsrc2) == -1) { \
+ /* won't generate /lit16 instr for this; check anyway */ \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op (s2) vsrc2; \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ /* non-div/rem case */ \
+ SET_REGISTER(vdst, GET_REGISTER(vsrc1) _op (s2) vsrc2); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT8(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/) \
+ { \
+ u2 litInfo; \
+ vdst = INST_AA(inst); \
+ litInfo = FETCH(1); \
+ vsrc1 = litInfo & 0xff; \
+ vsrc2 = litInfo >> 8; /* constant */ \
+ ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x", \
+ (_opname), vdst, vsrc1, vsrc2); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, result; \
+ firstVal = GET_REGISTER(vsrc1); \
+ if ((s1) vsrc2 == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && ((s1) vsrc2) == -1) { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op ((s1) vsrc2); \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ SET_REGISTER(vdst, \
+ (s4) GET_REGISTER(vsrc1) _op (s1) vsrc2); \
+ } \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_SHX_INT_LIT8(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/) \
+ { \
+ u2 litInfo; \
+ vdst = INST_AA(inst); \
+ litInfo = FETCH(1); \
+ vsrc1 = litInfo & 0xff; \
+ vsrc2 = litInfo >> 8; /* constant */ \
+ ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x", \
+ (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER(vdst, \
+ _cast GET_REGISTER(vsrc1) _op (vsrc2 & 0x1f)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_INT_2ADDR(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER(vdst); \
+ secondVal = GET_REGISTER(vsrc1); \
+ if (secondVal == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && secondVal == -1) { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ SET_REGISTER(vdst, \
+ (s4) GET_REGISTER(vdst) _op (s4) GET_REGISTER(vsrc1)); \
+ } \
+ FINISH(1);
+
+#define HANDLE_OP_SHX_INT_2ADDR(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER(vdst, \
+ _cast GET_REGISTER(vdst) _op (GET_REGISTER(vsrc1) & 0x1f)); \
+ FINISH(1);
+
+#define HANDLE_OP_X_LONG(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ if (_chkdiv != 0) { \
+ s8 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER_WIDE(vsrc1); \
+ secondVal = GET_REGISTER_WIDE(vsrc2); \
+ if (secondVal == 0LL) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u8)firstVal == 0x8000000000000000ULL && \
+ secondVal == -1LL) \
+ { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER_WIDE(vdst, result); \
+ } else { \
+ SET_REGISTER_WIDE(vdst, \
+ (s8) GET_REGISTER_WIDE(vsrc1) _op (s8) GET_REGISTER_WIDE(vsrc2)); \
+ } \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_SHX_LONG(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER_WIDE(vdst, \
+ _cast GET_REGISTER_WIDE(vsrc1) _op (GET_REGISTER(vsrc2) & 0x3f)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_LONG_2ADDR(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ if (_chkdiv != 0) { \
+ s8 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER_WIDE(vdst); \
+ secondVal = GET_REGISTER_WIDE(vsrc1); \
+ if (secondVal == 0LL) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u8)firstVal == 0x8000000000000000ULL && \
+ secondVal == -1LL) \
+ { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER_WIDE(vdst, result); \
+ } else { \
+ SET_REGISTER_WIDE(vdst, \
+ (s8) GET_REGISTER_WIDE(vdst) _op (s8)GET_REGISTER_WIDE(vsrc1));\
+ } \
+ FINISH(1);
+
+#define HANDLE_OP_SHX_LONG_2ADDR(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER_WIDE(vdst, \
+ _cast GET_REGISTER_WIDE(vdst) _op (GET_REGISTER(vsrc1) & 0x3f)); \
+ FINISH(1);
+
+#define HANDLE_OP_X_FLOAT(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-float v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER_FLOAT(vdst, \
+ GET_REGISTER_FLOAT(vsrc1) _op GET_REGISTER_FLOAT(vsrc2)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_DOUBLE(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-double v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER_DOUBLE(vdst, \
+ GET_REGISTER_DOUBLE(vsrc1) _op GET_REGISTER_DOUBLE(vsrc2)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_FLOAT_2ADDR(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-float-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER_FLOAT(vdst, \
+ GET_REGISTER_FLOAT(vdst) _op GET_REGISTER_FLOAT(vsrc1)); \
+ FINISH(1);
+
+#define HANDLE_OP_X_DOUBLE_2ADDR(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-double-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER_DOUBLE(vdst, \
+ GET_REGISTER_DOUBLE(vdst) _op GET_REGISTER_DOUBLE(vsrc1)); \
+ FINISH(1);
+
+#define HANDLE_OP_AGET(_opcode, _opname, _type, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ ArrayObject* arrayObj; \
+ u2 arrayInfo; \
+ EXPORT_PC(); \
+ vdst = INST_AA(inst); \
+ arrayInfo = FETCH(1); \
+ vsrc1 = arrayInfo & 0xff; /* array ptr */ \
+ vsrc2 = arrayInfo >> 8; /* index */ \
+ ILOGV("|aget%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ arrayObj = (ArrayObject*) GET_REGISTER(vsrc1); \
+ if (!checkForNull((Object*) arrayObj)) \
+ GOTO_exceptionThrown(); \
+ if (GET_REGISTER(vsrc2) >= arrayObj->length) { \
+ LOGV("Invalid array access: %p %d (len=%d)\n", \
+ arrayObj, vsrc2, arrayObj->length); \
+ dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+ NULL); \
+ GOTO_exceptionThrown(); \
+ } \
+ SET_REGISTER##_regsize(vdst, \
+ ((_type*) arrayObj->contents)[GET_REGISTER(vsrc2)]); \
+ ILOGV("+ AGET[%d]=0x%x", GET_REGISTER(vsrc2), GET_REGISTER(vdst)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_APUT(_opcode, _opname, _type, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ ArrayObject* arrayObj; \
+ u2 arrayInfo; \
+ EXPORT_PC(); \
+ vdst = INST_AA(inst); /* AA: source value */ \
+ arrayInfo = FETCH(1); \
+ vsrc1 = arrayInfo & 0xff; /* BB: array ptr */ \
+ vsrc2 = arrayInfo >> 8; /* CC: index */ \
+ ILOGV("|aput%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ arrayObj = (ArrayObject*) GET_REGISTER(vsrc1); \
+ if (!checkForNull((Object*) arrayObj)) \
+ GOTO_exceptionThrown(); \
+ if (GET_REGISTER(vsrc2) >= arrayObj->length) { \
+ dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+ NULL); \
+ GOTO_exceptionThrown(); \
+ } \
+ ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+ ((_type*) arrayObj->contents)[GET_REGISTER(vsrc2)] = \
+ GET_REGISTER##_regsize(vdst); \
+ } \
+ FINISH(2);
+
+/*
+ * It's possible to get a bad value out of a field with sub-32-bit stores
+ * because the -quick versions always operate on 32 bits. Consider:
+ * short foo = -1 (sets a 32-bit register to 0xffffffff)
+ * iput-quick foo (writes all 32 bits to the field)
+ * short bar = 1 (sets a 32-bit register to 0x00000001)
+ * iput-short (writes the low 16 bits to the field)
+ * iget-quick foo (reads all 32 bits from the field, yielding 0xffff0001)
+ * This can only happen when optimized and non-optimized code has interleaved
+ * access to the same field. This is unlikely but possible.
+ *
+ * The easiest way to fix this is to always read/write 32 bits at a time. On
+ * a device with a 16-bit data bus this is sub-optimal. (The alternative
+ * approach is to have sub-int versions of iget-quick, but now we're wasting
+ * Dalvik instruction space and making it less likely that handler code will
+ * already be in the CPU i-cache.)
+ */
+#define HANDLE_IGET_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ InstField* ifield; \
+ Object* obj; \
+ EXPORT_PC(); \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|iget%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNull(obj)) \
+ GOTO_exceptionThrown(); \
+ ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref); \
+ if (ifield == NULL) { \
+ ifield = dvmResolveInstField(curMethod->clazz, ref); \
+ if (ifield == NULL) \
+ GOTO_exceptionThrown(); \
+ } \
+ SET_REGISTER##_regsize(vdst, \
+ dvmGetField##_ftype(obj, ifield->byteOffset)); \
+ ILOGV("+ IGET '%s'=0x%08llx", ifield->field.name, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_GET(&ifield->field); \
+ } \
+ FINISH(2);
+
+#define HANDLE_IGET_X_QUICK(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ Object* obj; \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field offset */ \
+ ILOGV("|iget%s-quick v%d,v%d,field@+%u", \
+ (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNullExportPC(obj, fp, pc)) \
+ GOTO_exceptionThrown(); \
+ SET_REGISTER##_regsize(vdst, dvmGetField##_ftype(obj, ref)); \
+ ILOGV("+ IGETQ %d=0x%08llx", ref, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_IPUT_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ InstField* ifield; \
+ Object* obj; \
+ EXPORT_PC(); \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|iput%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNull(obj)) \
+ GOTO_exceptionThrown(); \
+ ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref); \
+ if (ifield == NULL) { \
+ ifield = dvmResolveInstField(curMethod->clazz, ref); \
+ if (ifield == NULL) \
+ GOTO_exceptionThrown(); \
+ } \
+ dvmSetField##_ftype(obj, ifield->byteOffset, \
+ GET_REGISTER##_regsize(vdst)); \
+ ILOGV("+ IPUT '%s'=0x%08llx", ifield->field.name, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_PUT(&ifield->field); \
+ } \
+ FINISH(2);
+
+#define HANDLE_IPUT_X_QUICK(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ Object* obj; \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field offset */ \
+ ILOGV("|iput%s-quick v%d,v%d,field@0x%04x", \
+ (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNullExportPC(obj, fp, pc)) \
+ GOTO_exceptionThrown(); \
+ dvmSetField##_ftype(obj, ref, GET_REGISTER##_regsize(vdst)); \
+ ILOGV("+ IPUTQ %d=0x%08llx", ref, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ } \
+ FINISH(2);
+
+/*
+ * The JIT needs dvmDexGetResolvedField() to return non-null.
+ * Since we use the portable interpreter to build the trace, the extra
+ * checks in HANDLE_SGET_X and HANDLE_SPUT_X are not needed for mterp.
+ */
+#define HANDLE_SGET_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/) \
+ { \
+ StaticField* sfield; \
+ vdst = INST_AA(inst); \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|sget%s v%d,sfield@0x%04x", (_opname), vdst, ref); \
+ sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+ if (sfield == NULL) { \
+ EXPORT_PC(); \
+ sfield = dvmResolveStaticField(curMethod->clazz, ref); \
+ if (sfield == NULL) \
+ GOTO_exceptionThrown(); \
+ if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) { \
+ ABORT_JIT_TSELECT(); \
+ } \
+ } \
+ SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield)); \
+ ILOGV("+ SGET '%s'=0x%08llx", \
+ sfield->field.name, (u8)GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_GET(&sfield->field); \
+ } \
+ FINISH(2);
+
+#define HANDLE_SPUT_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/) \
+ { \
+ StaticField* sfield; \
+ vdst = INST_AA(inst); \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|sput%s v%d,sfield@0x%04x", (_opname), vdst, ref); \
+ sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+ if (sfield == NULL) { \
+ EXPORT_PC(); \
+ sfield = dvmResolveStaticField(curMethod->clazz, ref); \
+ if (sfield == NULL) \
+ GOTO_exceptionThrown(); \
+ if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) { \
+ ABORT_JIT_TSELECT(); \
+ } \
+ } \
+ dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst)); \
+ ILOGV("+ SPUT '%s'=0x%08llx", \
+ sfield->field.name, (u8)GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_PUT(&sfield->field); \
+ } \
+ FINISH(2);
+
+/* File: cstubs/enddefs.c */
+
+/* undefine "magic" name remapping */
+#undef retval
+#undef pc
+#undef fp
+#undef curMethod
+#undef methodClassDex
+#undef self
+#undef debugTrackedRefStart
+
+/* File: armv5te/debug.c */
+#include <inttypes.h>
+
+/*
+ * Dump the fixed-purpose ARM registers, along with some other info.
+ *
+ * This function MUST be compiled in ARM mode -- THUMB will yield bogus
+ * results.
+ *
+ * This will NOT preserve r0-r3/ip.
+ */
+void dvmMterpDumpArmRegs(uint32_t r0, uint32_t r1, uint32_t r2, uint32_t r3)
+{
+ register uint32_t rPC asm("r4");
+ register uint32_t rFP asm("r5");
+ register uint32_t rGLUE asm("r6");
+ register uint32_t rINST asm("r7");
+ register uint32_t rIBASE asm("r8");
+ register uint32_t r9 asm("r9");
+ register uint32_t r10 asm("r10");
+
+ //extern char dvmAsmInstructionStart[];
+
+ printf("REGS: r0=%08x r1=%08x r2=%08x r3=%08x\n", r0, r1, r2, r3);
+ printf(" : rPC=%08x rFP=%08x rGLUE=%08x rINST=%08x\n",
+ rPC, rFP, rGLUE, rINST);
+ printf(" : rIBASE=%08x r9=%08x r10=%08x\n", rIBASE, r9, r10);
+
+ //MterpGlue* glue = (MterpGlue*) rGLUE;
+ //const Method* method = glue->method;
+ printf(" + self is %p\n", dvmThreadSelf());
+ //printf(" + currently in %s.%s %s\n",
+ // method->clazz->descriptor, method->name, method->shorty);
+ //printf(" + dvmAsmInstructionStart = %p\n", dvmAsmInstructionStart);
+ //printf(" + next handler for 0x%02x = %p\n",
+ // rINST & 0xff, dvmAsmInstructionStart + (rINST & 0xff) * 64);
+}
+
+/*
+ * Dump the StackSaveArea for the specified frame pointer.
+ */
+void dvmDumpFp(void* fp, StackSaveArea* otherSaveArea)
+{
+ StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+ printf("StackSaveArea for fp %p [%p/%p]:\n", fp, saveArea, otherSaveArea);
+#ifdef EASY_GDB
+ printf(" prevSave=%p, prevFrame=%p savedPc=%p meth=%p curPc=%p\n",
+ saveArea->prevSave, saveArea->prevFrame, saveArea->savedPc,
+ saveArea->method, saveArea->xtra.currentPc);
+#else
+ printf(" prevFrame=%p savedPc=%p meth=%p curPc=%p fp[0]=0x%08x\n",
+ saveArea->prevFrame, saveArea->savedPc,
+ saveArea->method, saveArea->xtra.currentPc,
+ *(u4*)fp);
+#endif
+}
+
+/*
+ * Does the bulk of the work for common_printMethod().
+ */
+void dvmMterpPrintMethod(Method* method)
+{
+ /*
+ * It is a direct (non-virtual) method if it is static, private,
+ * or a constructor.
+ */
+ bool isDirect =
+ ((method->accessFlags & (ACC_STATIC|ACC_PRIVATE)) != 0) ||
+ (method->name[0] == '<');
+
+ char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+
+ printf("<%c:%s.%s %s> ",
+ isDirect ? 'D' : 'V',
+ method->clazz->descriptor,
+ method->name,
+ desc);
+
+ free(desc);
+}
+
diff --git a/vm/mterp/out/InterpC-armv7-a-neon.c b/vm/mterp/out/InterpC-armv7-a-neon.c
new file mode 100644
index 0000000..adccb2d
--- /dev/null
+++ b/vm/mterp/out/InterpC-armv7-a-neon.c
@@ -0,0 +1,1289 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'armv7-a-neon'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: c/header.c */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* common includes */
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "mterp/Mterp.h"
+#include <math.h> // needed for fmod, fmodf
+#include "mterp/common/FindInterface.h"
+
+/*
+ * Configuration defines. These affect the C implementations, i.e. the
+ * portable interpreter(s) and C stubs.
+ *
+ * Some defines are controlled by the Makefile, e.g.:
+ * WITH_INSTR_CHECKS
+ * WITH_TRACKREF_CHECKS
+ * EASY_GDB
+ * NDEBUG
+ *
+ * If THREADED_INTERP is not defined, we use a classic "while true / switch"
+ * interpreter. If it is defined, then the tail end of each instruction
+ * handler fetches the next instruction and jumps directly to the handler.
+ * This increases the size of the "Std" interpreter by about 10%, but
+ * provides a speedup of about the same magnitude.
+ *
+ * There's a "hybrid" approach that uses a goto table instead of a switch
+ * statement, avoiding the "is the opcode in range" tests required for switch.
+ * The performance is close to the threaded version, and without the 10%
+ * size increase, but the benchmark results are off enough that it's not
+ * worth adding as a third option.
+ */
+#define THREADED_INTERP /* threaded vs. while-loop interpreter */
+
+#ifdef WITH_INSTR_CHECKS /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * ARM EABI requires 64-bit alignment for access to 64-bit data types. We
+ * can't just use pointers to copy 64-bit values out of our interpreted
+ * register set, because gcc will generate ldrd/strd.
+ *
+ * The __UNION version copies data in and out of a union. The __MEMCPY
+ * version uses a memcpy() call to do the transfer; gcc is smart enough to
+ * not actually call memcpy(). The __UNION version is very bad on ARM;
+ * it only uses one more instruction than __MEMCPY, but for some reason
+ * gcc thinks it needs separate storage for every instance of the union.
+ * On top of that, it feels the need to zero them out at the start of the
+ * method. Net result is we zero out ~700 bytes of stack space at the top
+ * of the interpreter using ARM STM instructions.
+ */
+#if defined(__ARM_EABI__)
+//# define NO_UNALIGN_64__UNION
+# define NO_UNALIGN_64__MEMCPY
+#endif
+
+//#define LOG_INSTR /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Keep a tally of accesses to fields. Currently only works if full DEX
+ * optimization is disabled.
+ */
+#ifdef PROFILE_FIELD_ACCESS
+# define UPDATE_FIELD_GET(_field) { (_field)->gets++; }
+# define UPDATE_FIELD_PUT(_field) { (_field)->puts++; }
+#else
+# define UPDATE_FIELD_GET(_field) ((void)0)
+# define UPDATE_FIELD_PUT(_field) ((void)0)
+#endif
+
+/*
+ * Export another copy of the PC on every instruction; this is largely
+ * redundant with EXPORT_PC and the debugger code. This value can be
+ * compared against what we have stored on the stack with EXPORT_PC to
+ * help ensure that we aren't missing any export calls.
+ */
+#if WITH_EXTRA_GC_CHECKS > 1
+# define EXPORT_EXTRA_PC() (self->currentPc2 = pc)
+#else
+# define EXPORT_EXTRA_PC()
+#endif
+
+/*
+ * Adjust the program counter. "_offset" is a signed int, in 16-bit units.
+ *
+ * Assumes the existence of "const u2* pc" and "const u2* curMethod->insns".
+ *
+ * We don't advance the program counter until we finish an instruction or
+ * branch, because we do want to have to unroll the PC if there's an
+ * exception.
+ */
+#ifdef CHECK_BRANCH_OFFSETS
+# define ADJUST_PC(_offset) do { \
+ int myoff = _offset; /* deref only once */ \
+ if (pc + myoff < curMethod->insns || \
+ pc + myoff >= curMethod->insns + dvmGetMethodInsnsSize(curMethod)) \
+ { \
+ char* desc; \
+ desc = dexProtoCopyMethodDescriptor(&curMethod->prototype); \
+ LOGE("Invalid branch %d at 0x%04x in %s.%s %s\n", \
+ myoff, (int) (pc - curMethod->insns), \
+ curMethod->clazz->descriptor, curMethod->name, desc); \
+ free(desc); \
+ dvmAbort(); \
+ } \
+ pc += myoff; \
+ EXPORT_EXTRA_PC(); \
+ } while (false)
+#else
+# define ADJUST_PC(_offset) do { \
+ pc += _offset; \
+ EXPORT_EXTRA_PC(); \
+ } while (false)
+#endif
+
+/*
+ * If enabled, log instructions as we execute them.
+ */
+#ifdef LOG_INSTR
+# define ILOGD(...) ILOG(LOG_DEBUG, __VA_ARGS__)
+# define ILOGV(...) ILOG(LOG_VERBOSE, __VA_ARGS__)
+# define ILOG(_level, ...) do { \
+ char debugStrBuf[128]; \
+ snprintf(debugStrBuf, sizeof(debugStrBuf), __VA_ARGS__); \
+ if (curMethod != NULL) \
+ LOG(_level, LOG_TAG"i", "%-2d|%04x%s\n", \
+ self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+ else \
+ LOG(_level, LOG_TAG"i", "%-2d|####%s\n", \
+ self->threadId, debugStrBuf); \
+ } while(false)
+void dvmDumpRegs(const Method* method, const u4* framePtr, bool inOnly);
+# define DUMP_REGS(_meth, _frame, _inOnly) dvmDumpRegs(_meth, _frame, _inOnly)
+static const char kSpacing[] = " ";
+#else
+# define ILOGD(...) ((void)0)
+# define ILOGV(...) ((void)0)
+# define DUMP_REGS(_meth, _frame, _inOnly) ((void)0)
+#endif
+
+/* get a long from an array of u4 */
+static inline s8 getLongFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { s8 ll; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.parts[0] = ptr[0];
+ conv.parts[1] = ptr[1];
+ return conv.ll;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ s8 val;
+ memcpy(&val, &ptr[idx], 8);
+ return val;
+#else
+ return *((s8*) &ptr[idx]);
+#endif
+}
+
+/* store a long into an array of u4 */
+static inline void putLongToArray(u4* ptr, int idx, s8 val)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { s8 ll; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.ll = val;
+ ptr[0] = conv.parts[0];
+ ptr[1] = conv.parts[1];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ memcpy(&ptr[idx], &val, 8);
+#else
+ *((s8*) &ptr[idx]) = val;
+#endif
+}
+
+/* get a double from an array of u4 */
+static inline double getDoubleFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { double d; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.parts[0] = ptr[0];
+ conv.parts[1] = ptr[1];
+ return conv.d;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ double dval;
+ memcpy(&dval, &ptr[idx], 8);
+ return dval;
+#else
+ return *((double*) &ptr[idx]);
+#endif
+}
+
+/* store a double into an array of u4 */
+static inline void putDoubleToArray(u4* ptr, int idx, double dval)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { double d; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.d = dval;
+ ptr[0] = conv.parts[0];
+ ptr[1] = conv.parts[1];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ memcpy(&ptr[idx], &dval, 8);
+#else
+ *((double*) &ptr[idx]) = dval;
+#endif
+}
+
+/*
+ * If enabled, validate the register number on every access. Otherwise,
+ * just do an array access.
+ *
+ * Assumes the existence of "u4* fp".
+ *
+ * "_idx" may be referenced more than once.
+ */
+#ifdef CHECK_REGISTER_INDICES
+# define GET_REGISTER(_idx) \
+ ( (_idx) < curMethod->registersSize ? \
+ (fp[(_idx)]) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER(_idx, _val) \
+ ( (_idx) < curMethod->registersSize ? \
+ (fp[(_idx)] = (u4)(_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_AS_OBJECT(_idx) ((Object *)GET_REGISTER(_idx))
+# define SET_REGISTER_AS_OBJECT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_INT(_idx) ((s4) GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ getLongFromArray(fp, (_idx)) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER_WIDE(_idx, _val) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ putLongToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_FLOAT(_idx) \
+ ( (_idx) < curMethod->registersSize ? \
+ (*((float*) &fp[(_idx)])) : (assert(!"bad reg"),1969.0f) )
+# define SET_REGISTER_FLOAT(_idx, _val) \
+ ( (_idx) < curMethod->registersSize ? \
+ (*((float*) &fp[(_idx)]) = (_val)) : (assert(!"bad reg"),1969.0f) )
+# define GET_REGISTER_DOUBLE(_idx) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ getDoubleFromArray(fp, (_idx)) : (assert(!"bad reg"),1969.0) )
+# define SET_REGISTER_DOUBLE(_idx, _val) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ putDoubleToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969.0) )
+#else
+# define GET_REGISTER(_idx) (fp[(_idx)])
+# define SET_REGISTER(_idx, _val) (fp[(_idx)] = (_val))
+# define GET_REGISTER_AS_OBJECT(_idx) ((Object*) fp[(_idx)])
+# define SET_REGISTER_AS_OBJECT(_idx, _val) (fp[(_idx)] = (u4)(_val))
+# define GET_REGISTER_INT(_idx) ((s4)GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) getLongFromArray(fp, (_idx))
+# define SET_REGISTER_WIDE(_idx, _val) putLongToArray(fp, (_idx), (_val))
+# define GET_REGISTER_FLOAT(_idx) (*((float*) &fp[(_idx)]))
+# define SET_REGISTER_FLOAT(_idx, _val) (*((float*) &fp[(_idx)]) = (_val))
+# define GET_REGISTER_DOUBLE(_idx) getDoubleFromArray(fp, (_idx))
+# define SET_REGISTER_DOUBLE(_idx, _val) putDoubleToArray(fp, (_idx), (_val))
+#endif
+
+/*
+ * Get 16 bits from the specified offset of the program counter. We always
+ * want to load 16 bits at a time from the instruction stream -- it's more
+ * efficient than 8 and won't have the alignment problems that 32 might.
+ *
+ * Assumes existence of "const u2* pc".
+ */
+#define FETCH(_offset) (pc[(_offset)])
+
+/*
+ * Extract instruction byte from 16-bit fetch (_inst is a u2).
+ */
+#define INST_INST(_inst) ((_inst) & 0xff)
+
+/*
+ * Replace the opcode (used when handling breakpoints). _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
+ * Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
+ */
+#define INST_A(_inst) (((_inst) >> 8) & 0x0f)
+#define INST_B(_inst) ((_inst) >> 12)
+
+/*
+ * Get the 8-bit "vAA" 8-bit register index from the instruction word.
+ * (_inst is u2)
+ */
+#define INST_AA(_inst) ((_inst) >> 8)
+
+/*
+ * The current PC must be available to Throwable constructors, e.g.
+ * those created by dvmThrowException(), so that the exception stack
+ * trace can be generated correctly. If we don't do this, the offset
+ * within the current method won't be shown correctly. See the notes
+ * in Exception.c.
+ *
+ * This is also used to determine the address for precise GC.
+ *
+ * Assumes existence of "u4* fp" and "const u2* pc".
+ */
+#define EXPORT_PC() (SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc)
+
+/*
+ * Determine if we need to switch to a different interpreter. "_current"
+ * is either INTERP_STD or INTERP_DBG. It should be fixed for a given
+ * interpreter generation file, which should remove the outer conditional
+ * from the following.
+ *
+ * If we're building without debug and profiling support, we never switch.
+ */
+#if defined(WITH_JIT)
+# define NEED_INTERP_SWITCH(_current) ( \
+ (_current == INTERP_STD) ? \
+ dvmJitDebuggerOrProfilerActive() : !dvmJitDebuggerOrProfilerActive() )
+#else
+# define NEED_INTERP_SWITCH(_current) ( \
+ (_current == INTERP_STD) ? \
+ dvmDebuggerOrProfilerActive() : !dvmDebuggerOrProfilerActive() )
+#endif
+
+/*
+ * Check to see if "obj" is NULL. If so, throw an exception. Assumes the
+ * pc has already been exported to the stack.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler calls into
+ * something that could throw an exception (so we have already called
+ * EXPORT_PC at the top).
+ */
+static inline bool checkForNull(Object* obj)
+{
+ if (obj == NULL) {
+ dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+ return false;
+ }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+ if (!dvmIsValidObject(obj)) {
+ LOGE("Invalid object %p\n", obj);
+ dvmAbort();
+ }
+#endif
+#ifndef NDEBUG
+ if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+ /* probable heap corruption */
+ LOGE("Invalid object class %p (in %p)\n", obj->clazz, obj);
+ dvmAbort();
+ }
+#endif
+ return true;
+}
+
+/*
+ * Check to see if "obj" is NULL. If so, export the PC into the stack
+ * frame and throw an exception.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler doesn't do
+ * anything else that can throw an exception.
+ */
+static inline bool checkForNullExportPC(Object* obj, u4* fp, const u2* pc)
+{
+ if (obj == NULL) {
+ EXPORT_PC();
+ dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+ return false;
+ }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+ if (!dvmIsValidObject(obj)) {
+ LOGE("Invalid object %p\n", obj);
+ dvmAbort();
+ }
+#endif
+#ifndef NDEBUG
+ if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+ /* probable heap corruption */
+ LOGE("Invalid object class %p (in %p)\n", obj->clazz, obj);
+ dvmAbort();
+ }
+#endif
+ return true;
+}
+
+/* File: cstubs/stubdefs.c */
+/* this is a standard (no debug support) interpreter */
+#define INTERP_TYPE INTERP_STD
+#define CHECK_DEBUG_AND_PROF() ((void)0)
+# define CHECK_TRACKED_REFS() ((void)0)
+#define CHECK_JIT_BOOL() (false)
+#define CHECK_JIT_VOID()
+#define ABORT_JIT_TSELECT() ((void)0)
+
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...) \
+ void dvmMterp_##_target(MterpGlue* glue, ## __VA_ARGS__);
+
+#define GOTO_TARGET(_target, ...) \
+ void dvmMterp_##_target(MterpGlue* glue, ## __VA_ARGS__) { \
+ u2 ref, vsrc1, vsrc2, vdst; \
+ u2 inst = FETCH(0); \
+ const Method* methodToCall; \
+ StackSaveArea* debugSaveArea;
+
+#define GOTO_TARGET_END }
+
+/*
+ * Redefine what used to be local variable accesses into MterpGlue struct
+ * references. (These are undefined down in "footer.c".)
+ */
+#define retval glue->retval
+#define pc glue->pc
+#define fp glue->fp
+#define curMethod glue->method
+#define methodClassDex glue->methodClassDex
+#define self glue->self
+#define debugTrackedRefStart glue->debugTrackedRefStart
+
+/* ugh */
+#define STUB_HACK(x) x
+
+
+/*
+ * Opcode handler framing macros. Here, each opcode is a separate function
+ * that takes a "glue" argument and returns void. We can't declare
+ * these "static" because they may be called from an assembly stub.
+ */
+#define HANDLE_OPCODE(_op) \
+ void dvmMterp_##_op(MterpGlue* glue) { \
+ u2 ref, vsrc1, vsrc2, vdst; \
+ u2 inst = FETCH(0);
+
+#define OP_END }
+
+/*
+ * Like the "portable" FINISH, but don't reload "inst", and return to caller
+ * when done.
+ */
+#define FINISH(_offset) { \
+ ADJUST_PC(_offset); \
+ CHECK_DEBUG_AND_PROF(); \
+ CHECK_TRACKED_REFS(); \
+ return; \
+ }
+
+
+/*
+ * The "goto label" statements turn into function calls followed by
+ * return statements. Some of the functions take arguments, which in the
+ * portable interpreter are handled by assigning values to globals.
+ */
+
+#define GOTO_exceptionThrown() \
+ do { \
+ dvmMterp_exceptionThrown(glue); \
+ return; \
+ } while(false)
+
+#define GOTO_returnFromMethod() \
+ do { \
+ dvmMterp_returnFromMethod(glue); \
+ return; \
+ } while(false)
+
+#define GOTO_invoke(_target, _methodCallRange) \
+ do { \
+ dvmMterp_##_target(glue, _methodCallRange); \
+ return; \
+ } while(false)
+
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst) \
+ do { \
+ dvmMterp_invokeMethod(glue, _methodCallRange, _methodToCall, \
+ _vsrc1, _vdst); \
+ return; \
+ } while(false)
+
+/*
+ * As a special case, "goto bail" turns into a longjmp. Use "bail_switch"
+ * if we need to switch to the other interpreter upon our return.
+ */
+#define GOTO_bail() \
+ dvmMterpStdBail(glue, false);
+#define GOTO_bail_switch() \
+ dvmMterpStdBail(glue, true);
+
+/*
+ * Periodically check for thread suspension.
+ *
+ * While we're at it, see if a debugger has attached or the profiler has
+ * started. If so, switch to a different "goto" table.
+ */
+#define PERIODIC_CHECKS(_entryPoint, _pcadj) { \
+ if (dvmCheckSuspendQuick(self)) { \
+ EXPORT_PC(); /* need for precise GC */ \
+ dvmCheckSuspendPending(self); \
+ } \
+ if (NEED_INTERP_SWITCH(INTERP_TYPE)) { \
+ ADJUST_PC(_pcadj); \
+ glue->entryPoint = _entryPoint; \
+ LOGVV("threadid=%d: switch to STD ep=%d adj=%d\n", \
+ self->threadId, (_entryPoint), (_pcadj)); \
+ GOTO_bail_switch(); \
+ } \
+ }
+
+/* File: c/opcommon.c */
+/* forward declarations of goto targets */
+GOTO_TARGET_DECL(filledNewArray, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtual, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuper, bool methodCallRange);
+GOTO_TARGET_DECL(invokeInterface, bool methodCallRange);
+GOTO_TARGET_DECL(invokeDirect, bool methodCallRange);
+GOTO_TARGET_DECL(invokeStatic, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtualQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuperQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeMethod, bool methodCallRange, const Method* methodToCall,
+ u2 count, u2 regs);
+GOTO_TARGET_DECL(returnFromMethod);
+GOTO_TARGET_DECL(exceptionThrown);
+
+/*
+ * ===========================================================================
+ *
+ * What follows are opcode definitions shared between multiple opcodes with
+ * minor substitutions handled by the C pre-processor. These should probably
+ * use the mterp substitution mechanism instead, with the code here moved
+ * into common fragment files (like the asm "binop.S"), although it's hard
+ * to give up the C preprocessor in favor of the much simpler text subst.
+ *
+ * ===========================================================================
+ */
+
+#define HANDLE_NUMCONV(_opcode, _opname, _fromtype, _totype) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER##_totype(vdst, \
+ GET_REGISTER##_fromtype(vsrc1)); \
+ FINISH(1);
+
+#define HANDLE_FLOAT_TO_INT(_opcode, _opname, _fromvtype, _fromrtype, \
+ _tovtype, _tortype) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ { \
+ /* spec defines specific handling for +/- inf and NaN values */ \
+ _fromvtype val; \
+ _tovtype intMin, intMax, result; \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1); \
+ val = GET_REGISTER##_fromrtype(vsrc1); \
+ intMin = (_tovtype) 1 << (sizeof(_tovtype) * 8 -1); \
+ intMax = ~intMin; \
+ result = (_tovtype) val; \
+ if (val >= intMax) /* +inf */ \
+ result = intMax; \
+ else if (val <= intMin) /* -inf */ \
+ result = intMin; \
+ else if (val != val) /* NaN */ \
+ result = 0; \
+ else \
+ result = (_tovtype) val; \
+ SET_REGISTER##_tortype(vdst, result); \
+ } \
+ FINISH(1);
+
+#define HANDLE_INT_TO_SMALL(_opcode, _opname, _type) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|int-to-%s v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER(vdst, (_type) GET_REGISTER(vsrc1)); \
+ FINISH(1);
+
+/* NOTE: the comparison result is always a signed 4-byte integer */
+#define HANDLE_OP_CMPX(_opcode, _opname, _varType, _type, _nanVal) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ int result; \
+ u2 regs; \
+ _varType val1, val2; \
+ vdst = INST_AA(inst); \
+ regs = FETCH(1); \
+ vsrc1 = regs & 0xff; \
+ vsrc2 = regs >> 8; \
+ ILOGV("|cmp%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ val1 = GET_REGISTER##_type(vsrc1); \
+ val2 = GET_REGISTER##_type(vsrc2); \
+ if (val1 == val2) \
+ result = 0; \
+ else if (val1 < val2) \
+ result = -1; \
+ else if (val1 > val2) \
+ result = 1; \
+ else \
+ result = (_nanVal); \
+ ILOGV("+ result=%d\n", result); \
+ SET_REGISTER(vdst, result); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_IF_XX(_opcode, _opname, _cmp) \
+ HANDLE_OPCODE(_opcode /*vA, vB, +CCCC*/) \
+ vsrc1 = INST_A(inst); \
+ vsrc2 = INST_B(inst); \
+ if ((s4) GET_REGISTER(vsrc1) _cmp (s4) GET_REGISTER(vsrc2)) { \
+ int branchOffset = (s2)FETCH(1); /* sign-extended */ \
+ ILOGV("|if-%s v%d,v%d,+0x%04x", (_opname), vsrc1, vsrc2, \
+ branchOffset); \
+ ILOGV("> branch taken"); \
+ if (branchOffset < 0) \
+ PERIODIC_CHECKS(kInterpEntryInstr, branchOffset); \
+ FINISH(branchOffset); \
+ } else { \
+ ILOGV("|if-%s v%d,v%d,-", (_opname), vsrc1, vsrc2); \
+ FINISH(2); \
+ }
+
+#define HANDLE_OP_IF_XXZ(_opcode, _opname, _cmp) \
+ HANDLE_OPCODE(_opcode /*vAA, +BBBB*/) \
+ vsrc1 = INST_AA(inst); \
+ if ((s4) GET_REGISTER(vsrc1) _cmp 0) { \
+ int branchOffset = (s2)FETCH(1); /* sign-extended */ \
+ ILOGV("|if-%s v%d,+0x%04x", (_opname), vsrc1, branchOffset); \
+ ILOGV("> branch taken"); \
+ if (branchOffset < 0) \
+ PERIODIC_CHECKS(kInterpEntryInstr, branchOffset); \
+ FINISH(branchOffset); \
+ } else { \
+ ILOGV("|if-%s v%d,-", (_opname), vsrc1); \
+ FINISH(2); \
+ }
+
+#define HANDLE_UNOP(_opcode, _opname, _pfx, _sfx, _type) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER##_type(vdst, _pfx GET_REGISTER##_type(vsrc1) _sfx); \
+ FINISH(1);
+
+#define HANDLE_OP_X_INT(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER(vsrc1); \
+ secondVal = GET_REGISTER(vsrc2); \
+ if (secondVal == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && secondVal == -1) { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ /* non-div/rem case */ \
+ SET_REGISTER(vdst, \
+ (s4) GET_REGISTER(vsrc1) _op (s4) GET_REGISTER(vsrc2)); \
+ } \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_SHX_INT(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER(vdst, \
+ _cast GET_REGISTER(vsrc1) _op (GET_REGISTER(vsrc2) & 0x1f)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT16(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vA, vB, #+CCCC*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ vsrc2 = FETCH(1); \
+ ILOGV("|%s-int/lit16 v%d,v%d,#+0x%04x", \
+ (_opname), vdst, vsrc1, vsrc2); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, result; \
+ firstVal = GET_REGISTER(vsrc1); \
+ if ((s2) vsrc2 == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && ((s2) vsrc2) == -1) { \
+ /* won't generate /lit16 instr for this; check anyway */ \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op (s2) vsrc2; \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ /* non-div/rem case */ \
+ SET_REGISTER(vdst, GET_REGISTER(vsrc1) _op (s2) vsrc2); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT8(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/) \
+ { \
+ u2 litInfo; \
+ vdst = INST_AA(inst); \
+ litInfo = FETCH(1); \
+ vsrc1 = litInfo & 0xff; \
+ vsrc2 = litInfo >> 8; /* constant */ \
+ ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x", \
+ (_opname), vdst, vsrc1, vsrc2); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, result; \
+ firstVal = GET_REGISTER(vsrc1); \
+ if ((s1) vsrc2 == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && ((s1) vsrc2) == -1) { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op ((s1) vsrc2); \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ SET_REGISTER(vdst, \
+ (s4) GET_REGISTER(vsrc1) _op (s1) vsrc2); \
+ } \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_SHX_INT_LIT8(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/) \
+ { \
+ u2 litInfo; \
+ vdst = INST_AA(inst); \
+ litInfo = FETCH(1); \
+ vsrc1 = litInfo & 0xff; \
+ vsrc2 = litInfo >> 8; /* constant */ \
+ ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x", \
+ (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER(vdst, \
+ _cast GET_REGISTER(vsrc1) _op (vsrc2 & 0x1f)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_INT_2ADDR(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER(vdst); \
+ secondVal = GET_REGISTER(vsrc1); \
+ if (secondVal == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && secondVal == -1) { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ SET_REGISTER(vdst, \
+ (s4) GET_REGISTER(vdst) _op (s4) GET_REGISTER(vsrc1)); \
+ } \
+ FINISH(1);
+
+#define HANDLE_OP_SHX_INT_2ADDR(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER(vdst, \
+ _cast GET_REGISTER(vdst) _op (GET_REGISTER(vsrc1) & 0x1f)); \
+ FINISH(1);
+
+#define HANDLE_OP_X_LONG(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ if (_chkdiv != 0) { \
+ s8 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER_WIDE(vsrc1); \
+ secondVal = GET_REGISTER_WIDE(vsrc2); \
+ if (secondVal == 0LL) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u8)firstVal == 0x8000000000000000ULL && \
+ secondVal == -1LL) \
+ { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER_WIDE(vdst, result); \
+ } else { \
+ SET_REGISTER_WIDE(vdst, \
+ (s8) GET_REGISTER_WIDE(vsrc1) _op (s8) GET_REGISTER_WIDE(vsrc2)); \
+ } \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_SHX_LONG(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER_WIDE(vdst, \
+ _cast GET_REGISTER_WIDE(vsrc1) _op (GET_REGISTER(vsrc2) & 0x3f)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_LONG_2ADDR(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ if (_chkdiv != 0) { \
+ s8 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER_WIDE(vdst); \
+ secondVal = GET_REGISTER_WIDE(vsrc1); \
+ if (secondVal == 0LL) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u8)firstVal == 0x8000000000000000ULL && \
+ secondVal == -1LL) \
+ { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER_WIDE(vdst, result); \
+ } else { \
+ SET_REGISTER_WIDE(vdst, \
+ (s8) GET_REGISTER_WIDE(vdst) _op (s8)GET_REGISTER_WIDE(vsrc1));\
+ } \
+ FINISH(1);
+
+#define HANDLE_OP_SHX_LONG_2ADDR(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER_WIDE(vdst, \
+ _cast GET_REGISTER_WIDE(vdst) _op (GET_REGISTER(vsrc1) & 0x3f)); \
+ FINISH(1);
+
+#define HANDLE_OP_X_FLOAT(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-float v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER_FLOAT(vdst, \
+ GET_REGISTER_FLOAT(vsrc1) _op GET_REGISTER_FLOAT(vsrc2)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_DOUBLE(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-double v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER_DOUBLE(vdst, \
+ GET_REGISTER_DOUBLE(vsrc1) _op GET_REGISTER_DOUBLE(vsrc2)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_FLOAT_2ADDR(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-float-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER_FLOAT(vdst, \
+ GET_REGISTER_FLOAT(vdst) _op GET_REGISTER_FLOAT(vsrc1)); \
+ FINISH(1);
+
+#define HANDLE_OP_X_DOUBLE_2ADDR(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-double-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER_DOUBLE(vdst, \
+ GET_REGISTER_DOUBLE(vdst) _op GET_REGISTER_DOUBLE(vsrc1)); \
+ FINISH(1);
+
+#define HANDLE_OP_AGET(_opcode, _opname, _type, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ ArrayObject* arrayObj; \
+ u2 arrayInfo; \
+ EXPORT_PC(); \
+ vdst = INST_AA(inst); \
+ arrayInfo = FETCH(1); \
+ vsrc1 = arrayInfo & 0xff; /* array ptr */ \
+ vsrc2 = arrayInfo >> 8; /* index */ \
+ ILOGV("|aget%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ arrayObj = (ArrayObject*) GET_REGISTER(vsrc1); \
+ if (!checkForNull((Object*) arrayObj)) \
+ GOTO_exceptionThrown(); \
+ if (GET_REGISTER(vsrc2) >= arrayObj->length) { \
+ LOGV("Invalid array access: %p %d (len=%d)\n", \
+ arrayObj, vsrc2, arrayObj->length); \
+ dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+ NULL); \
+ GOTO_exceptionThrown(); \
+ } \
+ SET_REGISTER##_regsize(vdst, \
+ ((_type*) arrayObj->contents)[GET_REGISTER(vsrc2)]); \
+ ILOGV("+ AGET[%d]=0x%x", GET_REGISTER(vsrc2), GET_REGISTER(vdst)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_APUT(_opcode, _opname, _type, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ ArrayObject* arrayObj; \
+ u2 arrayInfo; \
+ EXPORT_PC(); \
+ vdst = INST_AA(inst); /* AA: source value */ \
+ arrayInfo = FETCH(1); \
+ vsrc1 = arrayInfo & 0xff; /* BB: array ptr */ \
+ vsrc2 = arrayInfo >> 8; /* CC: index */ \
+ ILOGV("|aput%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ arrayObj = (ArrayObject*) GET_REGISTER(vsrc1); \
+ if (!checkForNull((Object*) arrayObj)) \
+ GOTO_exceptionThrown(); \
+ if (GET_REGISTER(vsrc2) >= arrayObj->length) { \
+ dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+ NULL); \
+ GOTO_exceptionThrown(); \
+ } \
+ ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+ ((_type*) arrayObj->contents)[GET_REGISTER(vsrc2)] = \
+ GET_REGISTER##_regsize(vdst); \
+ } \
+ FINISH(2);
+
+/*
+ * It's possible to get a bad value out of a field with sub-32-bit stores
+ * because the -quick versions always operate on 32 bits. Consider:
+ * short foo = -1 (sets a 32-bit register to 0xffffffff)
+ * iput-quick foo (writes all 32 bits to the field)
+ * short bar = 1 (sets a 32-bit register to 0x00000001)
+ * iput-short (writes the low 16 bits to the field)
+ * iget-quick foo (reads all 32 bits from the field, yielding 0xffff0001)
+ * This can only happen when optimized and non-optimized code has interleaved
+ * access to the same field. This is unlikely but possible.
+ *
+ * The easiest way to fix this is to always read/write 32 bits at a time. On
+ * a device with a 16-bit data bus this is sub-optimal. (The alternative
+ * approach is to have sub-int versions of iget-quick, but now we're wasting
+ * Dalvik instruction space and making it less likely that handler code will
+ * already be in the CPU i-cache.)
+ */
+#define HANDLE_IGET_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ InstField* ifield; \
+ Object* obj; \
+ EXPORT_PC(); \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|iget%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNull(obj)) \
+ GOTO_exceptionThrown(); \
+ ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref); \
+ if (ifield == NULL) { \
+ ifield = dvmResolveInstField(curMethod->clazz, ref); \
+ if (ifield == NULL) \
+ GOTO_exceptionThrown(); \
+ } \
+ SET_REGISTER##_regsize(vdst, \
+ dvmGetField##_ftype(obj, ifield->byteOffset)); \
+ ILOGV("+ IGET '%s'=0x%08llx", ifield->field.name, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_GET(&ifield->field); \
+ } \
+ FINISH(2);
+
+#define HANDLE_IGET_X_QUICK(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ Object* obj; \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field offset */ \
+ ILOGV("|iget%s-quick v%d,v%d,field@+%u", \
+ (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNullExportPC(obj, fp, pc)) \
+ GOTO_exceptionThrown(); \
+ SET_REGISTER##_regsize(vdst, dvmGetField##_ftype(obj, ref)); \
+ ILOGV("+ IGETQ %d=0x%08llx", ref, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_IPUT_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ InstField* ifield; \
+ Object* obj; \
+ EXPORT_PC(); \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|iput%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNull(obj)) \
+ GOTO_exceptionThrown(); \
+ ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref); \
+ if (ifield == NULL) { \
+ ifield = dvmResolveInstField(curMethod->clazz, ref); \
+ if (ifield == NULL) \
+ GOTO_exceptionThrown(); \
+ } \
+ dvmSetField##_ftype(obj, ifield->byteOffset, \
+ GET_REGISTER##_regsize(vdst)); \
+ ILOGV("+ IPUT '%s'=0x%08llx", ifield->field.name, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_PUT(&ifield->field); \
+ } \
+ FINISH(2);
+
+#define HANDLE_IPUT_X_QUICK(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ Object* obj; \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field offset */ \
+ ILOGV("|iput%s-quick v%d,v%d,field@0x%04x", \
+ (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNullExportPC(obj, fp, pc)) \
+ GOTO_exceptionThrown(); \
+ dvmSetField##_ftype(obj, ref, GET_REGISTER##_regsize(vdst)); \
+ ILOGV("+ IPUTQ %d=0x%08llx", ref, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ } \
+ FINISH(2);
+
+/*
+ * The JIT needs dvmDexGetResolvedField() to return non-null.
+ * Since we use the portable interpreter to build the trace, the extra
+ * checks in HANDLE_SGET_X and HANDLE_SPUT_X are not needed for mterp.
+ */
+#define HANDLE_SGET_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/) \
+ { \
+ StaticField* sfield; \
+ vdst = INST_AA(inst); \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|sget%s v%d,sfield@0x%04x", (_opname), vdst, ref); \
+ sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+ if (sfield == NULL) { \
+ EXPORT_PC(); \
+ sfield = dvmResolveStaticField(curMethod->clazz, ref); \
+ if (sfield == NULL) \
+ GOTO_exceptionThrown(); \
+ if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) { \
+ ABORT_JIT_TSELECT(); \
+ } \
+ } \
+ SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield)); \
+ ILOGV("+ SGET '%s'=0x%08llx", \
+ sfield->field.name, (u8)GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_GET(&sfield->field); \
+ } \
+ FINISH(2);
+
+#define HANDLE_SPUT_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/) \
+ { \
+ StaticField* sfield; \
+ vdst = INST_AA(inst); \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|sput%s v%d,sfield@0x%04x", (_opname), vdst, ref); \
+ sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+ if (sfield == NULL) { \
+ EXPORT_PC(); \
+ sfield = dvmResolveStaticField(curMethod->clazz, ref); \
+ if (sfield == NULL) \
+ GOTO_exceptionThrown(); \
+ if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) { \
+ ABORT_JIT_TSELECT(); \
+ } \
+ } \
+ dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst)); \
+ ILOGV("+ SPUT '%s'=0x%08llx", \
+ sfield->field.name, (u8)GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_PUT(&sfield->field); \
+ } \
+ FINISH(2);
+
+/* File: cstubs/enddefs.c */
+
+/* undefine "magic" name remapping */
+#undef retval
+#undef pc
+#undef fp
+#undef curMethod
+#undef methodClassDex
+#undef self
+#undef debugTrackedRefStart
+
+/* File: armv5te/debug.c */
+#include <inttypes.h>
+
+/*
+ * Dump the fixed-purpose ARM registers, along with some other info.
+ *
+ * This function MUST be compiled in ARM mode -- THUMB will yield bogus
+ * results.
+ *
+ * This will NOT preserve r0-r3/ip.
+ */
+void dvmMterpDumpArmRegs(uint32_t r0, uint32_t r1, uint32_t r2, uint32_t r3)
+{
+ register uint32_t rPC asm("r4");
+ register uint32_t rFP asm("r5");
+ register uint32_t rGLUE asm("r6");
+ register uint32_t rINST asm("r7");
+ register uint32_t rIBASE asm("r8");
+ register uint32_t r9 asm("r9");
+ register uint32_t r10 asm("r10");
+
+ //extern char dvmAsmInstructionStart[];
+
+ printf("REGS: r0=%08x r1=%08x r2=%08x r3=%08x\n", r0, r1, r2, r3);
+ printf(" : rPC=%08x rFP=%08x rGLUE=%08x rINST=%08x\n",
+ rPC, rFP, rGLUE, rINST);
+ printf(" : rIBASE=%08x r9=%08x r10=%08x\n", rIBASE, r9, r10);
+
+ //MterpGlue* glue = (MterpGlue*) rGLUE;
+ //const Method* method = glue->method;
+ printf(" + self is %p\n", dvmThreadSelf());
+ //printf(" + currently in %s.%s %s\n",
+ // method->clazz->descriptor, method->name, method->shorty);
+ //printf(" + dvmAsmInstructionStart = %p\n", dvmAsmInstructionStart);
+ //printf(" + next handler for 0x%02x = %p\n",
+ // rINST & 0xff, dvmAsmInstructionStart + (rINST & 0xff) * 64);
+}
+
+/*
+ * Dump the StackSaveArea for the specified frame pointer.
+ */
+void dvmDumpFp(void* fp, StackSaveArea* otherSaveArea)
+{
+ StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+ printf("StackSaveArea for fp %p [%p/%p]:\n", fp, saveArea, otherSaveArea);
+#ifdef EASY_GDB
+ printf(" prevSave=%p, prevFrame=%p savedPc=%p meth=%p curPc=%p\n",
+ saveArea->prevSave, saveArea->prevFrame, saveArea->savedPc,
+ saveArea->method, saveArea->xtra.currentPc);
+#else
+ printf(" prevFrame=%p savedPc=%p meth=%p curPc=%p fp[0]=0x%08x\n",
+ saveArea->prevFrame, saveArea->savedPc,
+ saveArea->method, saveArea->xtra.currentPc,
+ *(u4*)fp);
+#endif
+}
+
+/*
+ * Does the bulk of the work for common_printMethod().
+ */
+void dvmMterpPrintMethod(Method* method)
+{
+ /*
+ * It is a direct (non-virtual) method if it is static, private,
+ * or a constructor.
+ */
+ bool isDirect =
+ ((method->accessFlags & (ACC_STATIC|ACC_PRIVATE)) != 0) ||
+ (method->name[0] == '<');
+
+ char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+
+ printf("<%c:%s.%s %s> ",
+ isDirect ? 'D' : 'V',
+ method->clazz->descriptor,
+ method->name,
+ desc);
+
+ free(desc);
+}
+
diff --git a/vm/mterp/out/InterpC-armv7-a.c b/vm/mterp/out/InterpC-armv7-a.c
new file mode 100644
index 0000000..d40fd7c
--- /dev/null
+++ b/vm/mterp/out/InterpC-armv7-a.c
@@ -0,0 +1,1289 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'armv7-a'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: c/header.c */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* common includes */
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "mterp/Mterp.h"
+#include <math.h> // needed for fmod, fmodf
+#include "mterp/common/FindInterface.h"
+
+/*
+ * Configuration defines. These affect the C implementations, i.e. the
+ * portable interpreter(s) and C stubs.
+ *
+ * Some defines are controlled by the Makefile, e.g.:
+ * WITH_INSTR_CHECKS
+ * WITH_TRACKREF_CHECKS
+ * EASY_GDB
+ * NDEBUG
+ *
+ * If THREADED_INTERP is not defined, we use a classic "while true / switch"
+ * interpreter. If it is defined, then the tail end of each instruction
+ * handler fetches the next instruction and jumps directly to the handler.
+ * This increases the size of the "Std" interpreter by about 10%, but
+ * provides a speedup of about the same magnitude.
+ *
+ * There's a "hybrid" approach that uses a goto table instead of a switch
+ * statement, avoiding the "is the opcode in range" tests required for switch.
+ * The performance is close to the threaded version, and without the 10%
+ * size increase, but the benchmark results are off enough that it's not
+ * worth adding as a third option.
+ */
+#define THREADED_INTERP /* threaded vs. while-loop interpreter */
+
+#ifdef WITH_INSTR_CHECKS /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * ARM EABI requires 64-bit alignment for access to 64-bit data types. We
+ * can't just use pointers to copy 64-bit values out of our interpreted
+ * register set, because gcc will generate ldrd/strd.
+ *
+ * The __UNION version copies data in and out of a union. The __MEMCPY
+ * version uses a memcpy() call to do the transfer; gcc is smart enough to
+ * not actually call memcpy(). The __UNION version is very bad on ARM;
+ * it only uses one more instruction than __MEMCPY, but for some reason
+ * gcc thinks it needs separate storage for every instance of the union.
+ * On top of that, it feels the need to zero them out at the start of the
+ * method. Net result is we zero out ~700 bytes of stack space at the top
+ * of the interpreter using ARM STM instructions.
+ */
+#if defined(__ARM_EABI__)
+//# define NO_UNALIGN_64__UNION
+# define NO_UNALIGN_64__MEMCPY
+#endif
+
+//#define LOG_INSTR /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Keep a tally of accesses to fields. Currently only works if full DEX
+ * optimization is disabled.
+ */
+#ifdef PROFILE_FIELD_ACCESS
+# define UPDATE_FIELD_GET(_field) { (_field)->gets++; }
+# define UPDATE_FIELD_PUT(_field) { (_field)->puts++; }
+#else
+# define UPDATE_FIELD_GET(_field) ((void)0)
+# define UPDATE_FIELD_PUT(_field) ((void)0)
+#endif
+
+/*
+ * Export another copy of the PC on every instruction; this is largely
+ * redundant with EXPORT_PC and the debugger code. This value can be
+ * compared against what we have stored on the stack with EXPORT_PC to
+ * help ensure that we aren't missing any export calls.
+ */
+#if WITH_EXTRA_GC_CHECKS > 1
+# define EXPORT_EXTRA_PC() (self->currentPc2 = pc)
+#else
+# define EXPORT_EXTRA_PC()
+#endif
+
+/*
+ * Adjust the program counter. "_offset" is a signed int, in 16-bit units.
+ *
+ * Assumes the existence of "const u2* pc" and "const u2* curMethod->insns".
+ *
+ * We don't advance the program counter until we finish an instruction or
+ * branch, because we do want to have to unroll the PC if there's an
+ * exception.
+ */
+#ifdef CHECK_BRANCH_OFFSETS
+# define ADJUST_PC(_offset) do { \
+ int myoff = _offset; /* deref only once */ \
+ if (pc + myoff < curMethod->insns || \
+ pc + myoff >= curMethod->insns + dvmGetMethodInsnsSize(curMethod)) \
+ { \
+ char* desc; \
+ desc = dexProtoCopyMethodDescriptor(&curMethod->prototype); \
+ LOGE("Invalid branch %d at 0x%04x in %s.%s %s\n", \
+ myoff, (int) (pc - curMethod->insns), \
+ curMethod->clazz->descriptor, curMethod->name, desc); \
+ free(desc); \
+ dvmAbort(); \
+ } \
+ pc += myoff; \
+ EXPORT_EXTRA_PC(); \
+ } while (false)
+#else
+# define ADJUST_PC(_offset) do { \
+ pc += _offset; \
+ EXPORT_EXTRA_PC(); \
+ } while (false)
+#endif
+
+/*
+ * If enabled, log instructions as we execute them.
+ */
+#ifdef LOG_INSTR
+# define ILOGD(...) ILOG(LOG_DEBUG, __VA_ARGS__)
+# define ILOGV(...) ILOG(LOG_VERBOSE, __VA_ARGS__)
+# define ILOG(_level, ...) do { \
+ char debugStrBuf[128]; \
+ snprintf(debugStrBuf, sizeof(debugStrBuf), __VA_ARGS__); \
+ if (curMethod != NULL) \
+ LOG(_level, LOG_TAG"i", "%-2d|%04x%s\n", \
+ self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+ else \
+ LOG(_level, LOG_TAG"i", "%-2d|####%s\n", \
+ self->threadId, debugStrBuf); \
+ } while(false)
+void dvmDumpRegs(const Method* method, const u4* framePtr, bool inOnly);
+# define DUMP_REGS(_meth, _frame, _inOnly) dvmDumpRegs(_meth, _frame, _inOnly)
+static const char kSpacing[] = " ";
+#else
+# define ILOGD(...) ((void)0)
+# define ILOGV(...) ((void)0)
+# define DUMP_REGS(_meth, _frame, _inOnly) ((void)0)
+#endif
+
+/* get a long from an array of u4 */
+static inline s8 getLongFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { s8 ll; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.parts[0] = ptr[0];
+ conv.parts[1] = ptr[1];
+ return conv.ll;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ s8 val;
+ memcpy(&val, &ptr[idx], 8);
+ return val;
+#else
+ return *((s8*) &ptr[idx]);
+#endif
+}
+
+/* store a long into an array of u4 */
+static inline void putLongToArray(u4* ptr, int idx, s8 val)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { s8 ll; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.ll = val;
+ ptr[0] = conv.parts[0];
+ ptr[1] = conv.parts[1];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ memcpy(&ptr[idx], &val, 8);
+#else
+ *((s8*) &ptr[idx]) = val;
+#endif
+}
+
+/* get a double from an array of u4 */
+static inline double getDoubleFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { double d; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.parts[0] = ptr[0];
+ conv.parts[1] = ptr[1];
+ return conv.d;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ double dval;
+ memcpy(&dval, &ptr[idx], 8);
+ return dval;
+#else
+ return *((double*) &ptr[idx]);
+#endif
+}
+
+/* store a double into an array of u4 */
+static inline void putDoubleToArray(u4* ptr, int idx, double dval)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { double d; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.d = dval;
+ ptr[0] = conv.parts[0];
+ ptr[1] = conv.parts[1];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ memcpy(&ptr[idx], &dval, 8);
+#else
+ *((double*) &ptr[idx]) = dval;
+#endif
+}
+
+/*
+ * If enabled, validate the register number on every access. Otherwise,
+ * just do an array access.
+ *
+ * Assumes the existence of "u4* fp".
+ *
+ * "_idx" may be referenced more than once.
+ */
+#ifdef CHECK_REGISTER_INDICES
+# define GET_REGISTER(_idx) \
+ ( (_idx) < curMethod->registersSize ? \
+ (fp[(_idx)]) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER(_idx, _val) \
+ ( (_idx) < curMethod->registersSize ? \
+ (fp[(_idx)] = (u4)(_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_AS_OBJECT(_idx) ((Object *)GET_REGISTER(_idx))
+# define SET_REGISTER_AS_OBJECT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_INT(_idx) ((s4) GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ getLongFromArray(fp, (_idx)) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER_WIDE(_idx, _val) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ putLongToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_FLOAT(_idx) \
+ ( (_idx) < curMethod->registersSize ? \
+ (*((float*) &fp[(_idx)])) : (assert(!"bad reg"),1969.0f) )
+# define SET_REGISTER_FLOAT(_idx, _val) \
+ ( (_idx) < curMethod->registersSize ? \
+ (*((float*) &fp[(_idx)]) = (_val)) : (assert(!"bad reg"),1969.0f) )
+# define GET_REGISTER_DOUBLE(_idx) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ getDoubleFromArray(fp, (_idx)) : (assert(!"bad reg"),1969.0) )
+# define SET_REGISTER_DOUBLE(_idx, _val) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ putDoubleToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969.0) )
+#else
+# define GET_REGISTER(_idx) (fp[(_idx)])
+# define SET_REGISTER(_idx, _val) (fp[(_idx)] = (_val))
+# define GET_REGISTER_AS_OBJECT(_idx) ((Object*) fp[(_idx)])
+# define SET_REGISTER_AS_OBJECT(_idx, _val) (fp[(_idx)] = (u4)(_val))
+# define GET_REGISTER_INT(_idx) ((s4)GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) getLongFromArray(fp, (_idx))
+# define SET_REGISTER_WIDE(_idx, _val) putLongToArray(fp, (_idx), (_val))
+# define GET_REGISTER_FLOAT(_idx) (*((float*) &fp[(_idx)]))
+# define SET_REGISTER_FLOAT(_idx, _val) (*((float*) &fp[(_idx)]) = (_val))
+# define GET_REGISTER_DOUBLE(_idx) getDoubleFromArray(fp, (_idx))
+# define SET_REGISTER_DOUBLE(_idx, _val) putDoubleToArray(fp, (_idx), (_val))
+#endif
+
+/*
+ * Get 16 bits from the specified offset of the program counter. We always
+ * want to load 16 bits at a time from the instruction stream -- it's more
+ * efficient than 8 and won't have the alignment problems that 32 might.
+ *
+ * Assumes existence of "const u2* pc".
+ */
+#define FETCH(_offset) (pc[(_offset)])
+
+/*
+ * Extract instruction byte from 16-bit fetch (_inst is a u2).
+ */
+#define INST_INST(_inst) ((_inst) & 0xff)
+
+/*
+ * Replace the opcode (used when handling breakpoints). _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
+ * Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
+ */
+#define INST_A(_inst) (((_inst) >> 8) & 0x0f)
+#define INST_B(_inst) ((_inst) >> 12)
+
+/*
+ * Get the 8-bit "vAA" 8-bit register index from the instruction word.
+ * (_inst is u2)
+ */
+#define INST_AA(_inst) ((_inst) >> 8)
+
+/*
+ * The current PC must be available to Throwable constructors, e.g.
+ * those created by dvmThrowException(), so that the exception stack
+ * trace can be generated correctly. If we don't do this, the offset
+ * within the current method won't be shown correctly. See the notes
+ * in Exception.c.
+ *
+ * This is also used to determine the address for precise GC.
+ *
+ * Assumes existence of "u4* fp" and "const u2* pc".
+ */
+#define EXPORT_PC() (SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc)
+
+/*
+ * Determine if we need to switch to a different interpreter. "_current"
+ * is either INTERP_STD or INTERP_DBG. It should be fixed for a given
+ * interpreter generation file, which should remove the outer conditional
+ * from the following.
+ *
+ * If we're building without debug and profiling support, we never switch.
+ */
+#if defined(WITH_JIT)
+# define NEED_INTERP_SWITCH(_current) ( \
+ (_current == INTERP_STD) ? \
+ dvmJitDebuggerOrProfilerActive() : !dvmJitDebuggerOrProfilerActive() )
+#else
+# define NEED_INTERP_SWITCH(_current) ( \
+ (_current == INTERP_STD) ? \
+ dvmDebuggerOrProfilerActive() : !dvmDebuggerOrProfilerActive() )
+#endif
+
+/*
+ * Check to see if "obj" is NULL. If so, throw an exception. Assumes the
+ * pc has already been exported to the stack.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler calls into
+ * something that could throw an exception (so we have already called
+ * EXPORT_PC at the top).
+ */
+static inline bool checkForNull(Object* obj)
+{
+ if (obj == NULL) {
+ dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+ return false;
+ }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+ if (!dvmIsValidObject(obj)) {
+ LOGE("Invalid object %p\n", obj);
+ dvmAbort();
+ }
+#endif
+#ifndef NDEBUG
+ if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+ /* probable heap corruption */
+ LOGE("Invalid object class %p (in %p)\n", obj->clazz, obj);
+ dvmAbort();
+ }
+#endif
+ return true;
+}
+
+/*
+ * Check to see if "obj" is NULL. If so, export the PC into the stack
+ * frame and throw an exception.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler doesn't do
+ * anything else that can throw an exception.
+ */
+static inline bool checkForNullExportPC(Object* obj, u4* fp, const u2* pc)
+{
+ if (obj == NULL) {
+ EXPORT_PC();
+ dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+ return false;
+ }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+ if (!dvmIsValidObject(obj)) {
+ LOGE("Invalid object %p\n", obj);
+ dvmAbort();
+ }
+#endif
+#ifndef NDEBUG
+ if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+ /* probable heap corruption */
+ LOGE("Invalid object class %p (in %p)\n", obj->clazz, obj);
+ dvmAbort();
+ }
+#endif
+ return true;
+}
+
+/* File: cstubs/stubdefs.c */
+/* this is a standard (no debug support) interpreter */
+#define INTERP_TYPE INTERP_STD
+#define CHECK_DEBUG_AND_PROF() ((void)0)
+# define CHECK_TRACKED_REFS() ((void)0)
+#define CHECK_JIT_BOOL() (false)
+#define CHECK_JIT_VOID()
+#define ABORT_JIT_TSELECT() ((void)0)
+
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...) \
+ void dvmMterp_##_target(MterpGlue* glue, ## __VA_ARGS__);
+
+#define GOTO_TARGET(_target, ...) \
+ void dvmMterp_##_target(MterpGlue* glue, ## __VA_ARGS__) { \
+ u2 ref, vsrc1, vsrc2, vdst; \
+ u2 inst = FETCH(0); \
+ const Method* methodToCall; \
+ StackSaveArea* debugSaveArea;
+
+#define GOTO_TARGET_END }
+
+/*
+ * Redefine what used to be local variable accesses into MterpGlue struct
+ * references. (These are undefined down in "footer.c".)
+ */
+#define retval glue->retval
+#define pc glue->pc
+#define fp glue->fp
+#define curMethod glue->method
+#define methodClassDex glue->methodClassDex
+#define self glue->self
+#define debugTrackedRefStart glue->debugTrackedRefStart
+
+/* ugh */
+#define STUB_HACK(x) x
+
+
+/*
+ * Opcode handler framing macros. Here, each opcode is a separate function
+ * that takes a "glue" argument and returns void. We can't declare
+ * these "static" because they may be called from an assembly stub.
+ */
+#define HANDLE_OPCODE(_op) \
+ void dvmMterp_##_op(MterpGlue* glue) { \
+ u2 ref, vsrc1, vsrc2, vdst; \
+ u2 inst = FETCH(0);
+
+#define OP_END }
+
+/*
+ * Like the "portable" FINISH, but don't reload "inst", and return to caller
+ * when done.
+ */
+#define FINISH(_offset) { \
+ ADJUST_PC(_offset); \
+ CHECK_DEBUG_AND_PROF(); \
+ CHECK_TRACKED_REFS(); \
+ return; \
+ }
+
+
+/*
+ * The "goto label" statements turn into function calls followed by
+ * return statements. Some of the functions take arguments, which in the
+ * portable interpreter are handled by assigning values to globals.
+ */
+
+#define GOTO_exceptionThrown() \
+ do { \
+ dvmMterp_exceptionThrown(glue); \
+ return; \
+ } while(false)
+
+#define GOTO_returnFromMethod() \
+ do { \
+ dvmMterp_returnFromMethod(glue); \
+ return; \
+ } while(false)
+
+#define GOTO_invoke(_target, _methodCallRange) \
+ do { \
+ dvmMterp_##_target(glue, _methodCallRange); \
+ return; \
+ } while(false)
+
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst) \
+ do { \
+ dvmMterp_invokeMethod(glue, _methodCallRange, _methodToCall, \
+ _vsrc1, _vdst); \
+ return; \
+ } while(false)
+
+/*
+ * As a special case, "goto bail" turns into a longjmp. Use "bail_switch"
+ * if we need to switch to the other interpreter upon our return.
+ */
+#define GOTO_bail() \
+ dvmMterpStdBail(glue, false);
+#define GOTO_bail_switch() \
+ dvmMterpStdBail(glue, true);
+
+/*
+ * Periodically check for thread suspension.
+ *
+ * While we're at it, see if a debugger has attached or the profiler has
+ * started. If so, switch to a different "goto" table.
+ */
+#define PERIODIC_CHECKS(_entryPoint, _pcadj) { \
+ if (dvmCheckSuspendQuick(self)) { \
+ EXPORT_PC(); /* need for precise GC */ \
+ dvmCheckSuspendPending(self); \
+ } \
+ if (NEED_INTERP_SWITCH(INTERP_TYPE)) { \
+ ADJUST_PC(_pcadj); \
+ glue->entryPoint = _entryPoint; \
+ LOGVV("threadid=%d: switch to STD ep=%d adj=%d\n", \
+ self->threadId, (_entryPoint), (_pcadj)); \
+ GOTO_bail_switch(); \
+ } \
+ }
+
+/* File: c/opcommon.c */
+/* forward declarations of goto targets */
+GOTO_TARGET_DECL(filledNewArray, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtual, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuper, bool methodCallRange);
+GOTO_TARGET_DECL(invokeInterface, bool methodCallRange);
+GOTO_TARGET_DECL(invokeDirect, bool methodCallRange);
+GOTO_TARGET_DECL(invokeStatic, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtualQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuperQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeMethod, bool methodCallRange, const Method* methodToCall,
+ u2 count, u2 regs);
+GOTO_TARGET_DECL(returnFromMethod);
+GOTO_TARGET_DECL(exceptionThrown);
+
+/*
+ * ===========================================================================
+ *
+ * What follows are opcode definitions shared between multiple opcodes with
+ * minor substitutions handled by the C pre-processor. These should probably
+ * use the mterp substitution mechanism instead, with the code here moved
+ * into common fragment files (like the asm "binop.S"), although it's hard
+ * to give up the C preprocessor in favor of the much simpler text subst.
+ *
+ * ===========================================================================
+ */
+
+#define HANDLE_NUMCONV(_opcode, _opname, _fromtype, _totype) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER##_totype(vdst, \
+ GET_REGISTER##_fromtype(vsrc1)); \
+ FINISH(1);
+
+#define HANDLE_FLOAT_TO_INT(_opcode, _opname, _fromvtype, _fromrtype, \
+ _tovtype, _tortype) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ { \
+ /* spec defines specific handling for +/- inf and NaN values */ \
+ _fromvtype val; \
+ _tovtype intMin, intMax, result; \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1); \
+ val = GET_REGISTER##_fromrtype(vsrc1); \
+ intMin = (_tovtype) 1 << (sizeof(_tovtype) * 8 -1); \
+ intMax = ~intMin; \
+ result = (_tovtype) val; \
+ if (val >= intMax) /* +inf */ \
+ result = intMax; \
+ else if (val <= intMin) /* -inf */ \
+ result = intMin; \
+ else if (val != val) /* NaN */ \
+ result = 0; \
+ else \
+ result = (_tovtype) val; \
+ SET_REGISTER##_tortype(vdst, result); \
+ } \
+ FINISH(1);
+
+#define HANDLE_INT_TO_SMALL(_opcode, _opname, _type) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|int-to-%s v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER(vdst, (_type) GET_REGISTER(vsrc1)); \
+ FINISH(1);
+
+/* NOTE: the comparison result is always a signed 4-byte integer */
+#define HANDLE_OP_CMPX(_opcode, _opname, _varType, _type, _nanVal) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ int result; \
+ u2 regs; \
+ _varType val1, val2; \
+ vdst = INST_AA(inst); \
+ regs = FETCH(1); \
+ vsrc1 = regs & 0xff; \
+ vsrc2 = regs >> 8; \
+ ILOGV("|cmp%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ val1 = GET_REGISTER##_type(vsrc1); \
+ val2 = GET_REGISTER##_type(vsrc2); \
+ if (val1 == val2) \
+ result = 0; \
+ else if (val1 < val2) \
+ result = -1; \
+ else if (val1 > val2) \
+ result = 1; \
+ else \
+ result = (_nanVal); \
+ ILOGV("+ result=%d\n", result); \
+ SET_REGISTER(vdst, result); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_IF_XX(_opcode, _opname, _cmp) \
+ HANDLE_OPCODE(_opcode /*vA, vB, +CCCC*/) \
+ vsrc1 = INST_A(inst); \
+ vsrc2 = INST_B(inst); \
+ if ((s4) GET_REGISTER(vsrc1) _cmp (s4) GET_REGISTER(vsrc2)) { \
+ int branchOffset = (s2)FETCH(1); /* sign-extended */ \
+ ILOGV("|if-%s v%d,v%d,+0x%04x", (_opname), vsrc1, vsrc2, \
+ branchOffset); \
+ ILOGV("> branch taken"); \
+ if (branchOffset < 0) \
+ PERIODIC_CHECKS(kInterpEntryInstr, branchOffset); \
+ FINISH(branchOffset); \
+ } else { \
+ ILOGV("|if-%s v%d,v%d,-", (_opname), vsrc1, vsrc2); \
+ FINISH(2); \
+ }
+
+#define HANDLE_OP_IF_XXZ(_opcode, _opname, _cmp) \
+ HANDLE_OPCODE(_opcode /*vAA, +BBBB*/) \
+ vsrc1 = INST_AA(inst); \
+ if ((s4) GET_REGISTER(vsrc1) _cmp 0) { \
+ int branchOffset = (s2)FETCH(1); /* sign-extended */ \
+ ILOGV("|if-%s v%d,+0x%04x", (_opname), vsrc1, branchOffset); \
+ ILOGV("> branch taken"); \
+ if (branchOffset < 0) \
+ PERIODIC_CHECKS(kInterpEntryInstr, branchOffset); \
+ FINISH(branchOffset); \
+ } else { \
+ ILOGV("|if-%s v%d,-", (_opname), vsrc1); \
+ FINISH(2); \
+ }
+
+#define HANDLE_UNOP(_opcode, _opname, _pfx, _sfx, _type) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER##_type(vdst, _pfx GET_REGISTER##_type(vsrc1) _sfx); \
+ FINISH(1);
+
+#define HANDLE_OP_X_INT(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER(vsrc1); \
+ secondVal = GET_REGISTER(vsrc2); \
+ if (secondVal == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && secondVal == -1) { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ /* non-div/rem case */ \
+ SET_REGISTER(vdst, \
+ (s4) GET_REGISTER(vsrc1) _op (s4) GET_REGISTER(vsrc2)); \
+ } \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_SHX_INT(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER(vdst, \
+ _cast GET_REGISTER(vsrc1) _op (GET_REGISTER(vsrc2) & 0x1f)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT16(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vA, vB, #+CCCC*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ vsrc2 = FETCH(1); \
+ ILOGV("|%s-int/lit16 v%d,v%d,#+0x%04x", \
+ (_opname), vdst, vsrc1, vsrc2); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, result; \
+ firstVal = GET_REGISTER(vsrc1); \
+ if ((s2) vsrc2 == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && ((s2) vsrc2) == -1) { \
+ /* won't generate /lit16 instr for this; check anyway */ \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op (s2) vsrc2; \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ /* non-div/rem case */ \
+ SET_REGISTER(vdst, GET_REGISTER(vsrc1) _op (s2) vsrc2); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT8(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/) \
+ { \
+ u2 litInfo; \
+ vdst = INST_AA(inst); \
+ litInfo = FETCH(1); \
+ vsrc1 = litInfo & 0xff; \
+ vsrc2 = litInfo >> 8; /* constant */ \
+ ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x", \
+ (_opname), vdst, vsrc1, vsrc2); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, result; \
+ firstVal = GET_REGISTER(vsrc1); \
+ if ((s1) vsrc2 == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && ((s1) vsrc2) == -1) { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op ((s1) vsrc2); \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ SET_REGISTER(vdst, \
+ (s4) GET_REGISTER(vsrc1) _op (s1) vsrc2); \
+ } \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_SHX_INT_LIT8(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/) \
+ { \
+ u2 litInfo; \
+ vdst = INST_AA(inst); \
+ litInfo = FETCH(1); \
+ vsrc1 = litInfo & 0xff; \
+ vsrc2 = litInfo >> 8; /* constant */ \
+ ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x", \
+ (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER(vdst, \
+ _cast GET_REGISTER(vsrc1) _op (vsrc2 & 0x1f)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_INT_2ADDR(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER(vdst); \
+ secondVal = GET_REGISTER(vsrc1); \
+ if (secondVal == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && secondVal == -1) { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ SET_REGISTER(vdst, \
+ (s4) GET_REGISTER(vdst) _op (s4) GET_REGISTER(vsrc1)); \
+ } \
+ FINISH(1);
+
+#define HANDLE_OP_SHX_INT_2ADDR(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER(vdst, \
+ _cast GET_REGISTER(vdst) _op (GET_REGISTER(vsrc1) & 0x1f)); \
+ FINISH(1);
+
+#define HANDLE_OP_X_LONG(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ if (_chkdiv != 0) { \
+ s8 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER_WIDE(vsrc1); \
+ secondVal = GET_REGISTER_WIDE(vsrc2); \
+ if (secondVal == 0LL) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u8)firstVal == 0x8000000000000000ULL && \
+ secondVal == -1LL) \
+ { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER_WIDE(vdst, result); \
+ } else { \
+ SET_REGISTER_WIDE(vdst, \
+ (s8) GET_REGISTER_WIDE(vsrc1) _op (s8) GET_REGISTER_WIDE(vsrc2)); \
+ } \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_SHX_LONG(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER_WIDE(vdst, \
+ _cast GET_REGISTER_WIDE(vsrc1) _op (GET_REGISTER(vsrc2) & 0x3f)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_LONG_2ADDR(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ if (_chkdiv != 0) { \
+ s8 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER_WIDE(vdst); \
+ secondVal = GET_REGISTER_WIDE(vsrc1); \
+ if (secondVal == 0LL) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u8)firstVal == 0x8000000000000000ULL && \
+ secondVal == -1LL) \
+ { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER_WIDE(vdst, result); \
+ } else { \
+ SET_REGISTER_WIDE(vdst, \
+ (s8) GET_REGISTER_WIDE(vdst) _op (s8)GET_REGISTER_WIDE(vsrc1));\
+ } \
+ FINISH(1);
+
+#define HANDLE_OP_SHX_LONG_2ADDR(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER_WIDE(vdst, \
+ _cast GET_REGISTER_WIDE(vdst) _op (GET_REGISTER(vsrc1) & 0x3f)); \
+ FINISH(1);
+
+#define HANDLE_OP_X_FLOAT(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-float v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER_FLOAT(vdst, \
+ GET_REGISTER_FLOAT(vsrc1) _op GET_REGISTER_FLOAT(vsrc2)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_DOUBLE(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-double v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER_DOUBLE(vdst, \
+ GET_REGISTER_DOUBLE(vsrc1) _op GET_REGISTER_DOUBLE(vsrc2)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_FLOAT_2ADDR(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-float-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER_FLOAT(vdst, \
+ GET_REGISTER_FLOAT(vdst) _op GET_REGISTER_FLOAT(vsrc1)); \
+ FINISH(1);
+
+#define HANDLE_OP_X_DOUBLE_2ADDR(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-double-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER_DOUBLE(vdst, \
+ GET_REGISTER_DOUBLE(vdst) _op GET_REGISTER_DOUBLE(vsrc1)); \
+ FINISH(1);
+
+#define HANDLE_OP_AGET(_opcode, _opname, _type, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ ArrayObject* arrayObj; \
+ u2 arrayInfo; \
+ EXPORT_PC(); \
+ vdst = INST_AA(inst); \
+ arrayInfo = FETCH(1); \
+ vsrc1 = arrayInfo & 0xff; /* array ptr */ \
+ vsrc2 = arrayInfo >> 8; /* index */ \
+ ILOGV("|aget%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ arrayObj = (ArrayObject*) GET_REGISTER(vsrc1); \
+ if (!checkForNull((Object*) arrayObj)) \
+ GOTO_exceptionThrown(); \
+ if (GET_REGISTER(vsrc2) >= arrayObj->length) { \
+ LOGV("Invalid array access: %p %d (len=%d)\n", \
+ arrayObj, vsrc2, arrayObj->length); \
+ dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+ NULL); \
+ GOTO_exceptionThrown(); \
+ } \
+ SET_REGISTER##_regsize(vdst, \
+ ((_type*) arrayObj->contents)[GET_REGISTER(vsrc2)]); \
+ ILOGV("+ AGET[%d]=0x%x", GET_REGISTER(vsrc2), GET_REGISTER(vdst)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_APUT(_opcode, _opname, _type, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ ArrayObject* arrayObj; \
+ u2 arrayInfo; \
+ EXPORT_PC(); \
+ vdst = INST_AA(inst); /* AA: source value */ \
+ arrayInfo = FETCH(1); \
+ vsrc1 = arrayInfo & 0xff; /* BB: array ptr */ \
+ vsrc2 = arrayInfo >> 8; /* CC: index */ \
+ ILOGV("|aput%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ arrayObj = (ArrayObject*) GET_REGISTER(vsrc1); \
+ if (!checkForNull((Object*) arrayObj)) \
+ GOTO_exceptionThrown(); \
+ if (GET_REGISTER(vsrc2) >= arrayObj->length) { \
+ dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+ NULL); \
+ GOTO_exceptionThrown(); \
+ } \
+ ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+ ((_type*) arrayObj->contents)[GET_REGISTER(vsrc2)] = \
+ GET_REGISTER##_regsize(vdst); \
+ } \
+ FINISH(2);
+
+/*
+ * It's possible to get a bad value out of a field with sub-32-bit stores
+ * because the -quick versions always operate on 32 bits. Consider:
+ * short foo = -1 (sets a 32-bit register to 0xffffffff)
+ * iput-quick foo (writes all 32 bits to the field)
+ * short bar = 1 (sets a 32-bit register to 0x00000001)
+ * iput-short (writes the low 16 bits to the field)
+ * iget-quick foo (reads all 32 bits from the field, yielding 0xffff0001)
+ * This can only happen when optimized and non-optimized code has interleaved
+ * access to the same field. This is unlikely but possible.
+ *
+ * The easiest way to fix this is to always read/write 32 bits at a time. On
+ * a device with a 16-bit data bus this is sub-optimal. (The alternative
+ * approach is to have sub-int versions of iget-quick, but now we're wasting
+ * Dalvik instruction space and making it less likely that handler code will
+ * already be in the CPU i-cache.)
+ */
+#define HANDLE_IGET_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ InstField* ifield; \
+ Object* obj; \
+ EXPORT_PC(); \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|iget%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNull(obj)) \
+ GOTO_exceptionThrown(); \
+ ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref); \
+ if (ifield == NULL) { \
+ ifield = dvmResolveInstField(curMethod->clazz, ref); \
+ if (ifield == NULL) \
+ GOTO_exceptionThrown(); \
+ } \
+ SET_REGISTER##_regsize(vdst, \
+ dvmGetField##_ftype(obj, ifield->byteOffset)); \
+ ILOGV("+ IGET '%s'=0x%08llx", ifield->field.name, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_GET(&ifield->field); \
+ } \
+ FINISH(2);
+
+#define HANDLE_IGET_X_QUICK(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ Object* obj; \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field offset */ \
+ ILOGV("|iget%s-quick v%d,v%d,field@+%u", \
+ (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNullExportPC(obj, fp, pc)) \
+ GOTO_exceptionThrown(); \
+ SET_REGISTER##_regsize(vdst, dvmGetField##_ftype(obj, ref)); \
+ ILOGV("+ IGETQ %d=0x%08llx", ref, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_IPUT_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ InstField* ifield; \
+ Object* obj; \
+ EXPORT_PC(); \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|iput%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNull(obj)) \
+ GOTO_exceptionThrown(); \
+ ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref); \
+ if (ifield == NULL) { \
+ ifield = dvmResolveInstField(curMethod->clazz, ref); \
+ if (ifield == NULL) \
+ GOTO_exceptionThrown(); \
+ } \
+ dvmSetField##_ftype(obj, ifield->byteOffset, \
+ GET_REGISTER##_regsize(vdst)); \
+ ILOGV("+ IPUT '%s'=0x%08llx", ifield->field.name, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_PUT(&ifield->field); \
+ } \
+ FINISH(2);
+
+#define HANDLE_IPUT_X_QUICK(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ Object* obj; \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field offset */ \
+ ILOGV("|iput%s-quick v%d,v%d,field@0x%04x", \
+ (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNullExportPC(obj, fp, pc)) \
+ GOTO_exceptionThrown(); \
+ dvmSetField##_ftype(obj, ref, GET_REGISTER##_regsize(vdst)); \
+ ILOGV("+ IPUTQ %d=0x%08llx", ref, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ } \
+ FINISH(2);
+
+/*
+ * The JIT needs dvmDexGetResolvedField() to return non-null.
+ * Since we use the portable interpreter to build the trace, the extra
+ * checks in HANDLE_SGET_X and HANDLE_SPUT_X are not needed for mterp.
+ */
+#define HANDLE_SGET_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/) \
+ { \
+ StaticField* sfield; \
+ vdst = INST_AA(inst); \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|sget%s v%d,sfield@0x%04x", (_opname), vdst, ref); \
+ sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+ if (sfield == NULL) { \
+ EXPORT_PC(); \
+ sfield = dvmResolveStaticField(curMethod->clazz, ref); \
+ if (sfield == NULL) \
+ GOTO_exceptionThrown(); \
+ if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) { \
+ ABORT_JIT_TSELECT(); \
+ } \
+ } \
+ SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield)); \
+ ILOGV("+ SGET '%s'=0x%08llx", \
+ sfield->field.name, (u8)GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_GET(&sfield->field); \
+ } \
+ FINISH(2);
+
+#define HANDLE_SPUT_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/) \
+ { \
+ StaticField* sfield; \
+ vdst = INST_AA(inst); \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|sput%s v%d,sfield@0x%04x", (_opname), vdst, ref); \
+ sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+ if (sfield == NULL) { \
+ EXPORT_PC(); \
+ sfield = dvmResolveStaticField(curMethod->clazz, ref); \
+ if (sfield == NULL) \
+ GOTO_exceptionThrown(); \
+ if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) { \
+ ABORT_JIT_TSELECT(); \
+ } \
+ } \
+ dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst)); \
+ ILOGV("+ SPUT '%s'=0x%08llx", \
+ sfield->field.name, (u8)GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_PUT(&sfield->field); \
+ } \
+ FINISH(2);
+
+/* File: cstubs/enddefs.c */
+
+/* undefine "magic" name remapping */
+#undef retval
+#undef pc
+#undef fp
+#undef curMethod
+#undef methodClassDex
+#undef self
+#undef debugTrackedRefStart
+
+/* File: armv5te/debug.c */
+#include <inttypes.h>
+
+/*
+ * Dump the fixed-purpose ARM registers, along with some other info.
+ *
+ * This function MUST be compiled in ARM mode -- THUMB will yield bogus
+ * results.
+ *
+ * This will NOT preserve r0-r3/ip.
+ */
+void dvmMterpDumpArmRegs(uint32_t r0, uint32_t r1, uint32_t r2, uint32_t r3)
+{
+ register uint32_t rPC asm("r4");
+ register uint32_t rFP asm("r5");
+ register uint32_t rGLUE asm("r6");
+ register uint32_t rINST asm("r7");
+ register uint32_t rIBASE asm("r8");
+ register uint32_t r9 asm("r9");
+ register uint32_t r10 asm("r10");
+
+ //extern char dvmAsmInstructionStart[];
+
+ printf("REGS: r0=%08x r1=%08x r2=%08x r3=%08x\n", r0, r1, r2, r3);
+ printf(" : rPC=%08x rFP=%08x rGLUE=%08x rINST=%08x\n",
+ rPC, rFP, rGLUE, rINST);
+ printf(" : rIBASE=%08x r9=%08x r10=%08x\n", rIBASE, r9, r10);
+
+ //MterpGlue* glue = (MterpGlue*) rGLUE;
+ //const Method* method = glue->method;
+ printf(" + self is %p\n", dvmThreadSelf());
+ //printf(" + currently in %s.%s %s\n",
+ // method->clazz->descriptor, method->name, method->shorty);
+ //printf(" + dvmAsmInstructionStart = %p\n", dvmAsmInstructionStart);
+ //printf(" + next handler for 0x%02x = %p\n",
+ // rINST & 0xff, dvmAsmInstructionStart + (rINST & 0xff) * 64);
+}
+
+/*
+ * Dump the StackSaveArea for the specified frame pointer.
+ */
+void dvmDumpFp(void* fp, StackSaveArea* otherSaveArea)
+{
+ StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+ printf("StackSaveArea for fp %p [%p/%p]:\n", fp, saveArea, otherSaveArea);
+#ifdef EASY_GDB
+ printf(" prevSave=%p, prevFrame=%p savedPc=%p meth=%p curPc=%p\n",
+ saveArea->prevSave, saveArea->prevFrame, saveArea->savedPc,
+ saveArea->method, saveArea->xtra.currentPc);
+#else
+ printf(" prevFrame=%p savedPc=%p meth=%p curPc=%p fp[0]=0x%08x\n",
+ saveArea->prevFrame, saveArea->savedPc,
+ saveArea->method, saveArea->xtra.currentPc,
+ *(u4*)fp);
+#endif
+}
+
+/*
+ * Does the bulk of the work for common_printMethod().
+ */
+void dvmMterpPrintMethod(Method* method)
+{
+ /*
+ * It is a direct (non-virtual) method if it is static, private,
+ * or a constructor.
+ */
+ bool isDirect =
+ ((method->accessFlags & (ACC_STATIC|ACC_PRIVATE)) != 0) ||
+ (method->name[0] == '<');
+
+ char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+
+ printf("<%c:%s.%s %s> ",
+ isDirect ? 'D' : 'V',
+ method->clazz->descriptor,
+ method->name,
+ desc);
+
+ free(desc);
+}
+
diff --git a/vm/mterp/out/InterpC-portdbg.c b/vm/mterp/out/InterpC-portdbg.c
new file mode 100644
index 0000000..e909db4
--- /dev/null
+++ b/vm/mterp/out/InterpC-portdbg.c
@@ -0,0 +1,4442 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'portdbg'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: c/header.c */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* common includes */
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "mterp/Mterp.h"
+#include <math.h> // needed for fmod, fmodf
+#include "mterp/common/FindInterface.h"
+
+/*
+ * Configuration defines. These affect the C implementations, i.e. the
+ * portable interpreter(s) and C stubs.
+ *
+ * Some defines are controlled by the Makefile, e.g.:
+ * WITH_INSTR_CHECKS
+ * WITH_TRACKREF_CHECKS
+ * EASY_GDB
+ * NDEBUG
+ *
+ * If THREADED_INTERP is not defined, we use a classic "while true / switch"
+ * interpreter. If it is defined, then the tail end of each instruction
+ * handler fetches the next instruction and jumps directly to the handler.
+ * This increases the size of the "Std" interpreter by about 10%, but
+ * provides a speedup of about the same magnitude.
+ *
+ * There's a "hybrid" approach that uses a goto table instead of a switch
+ * statement, avoiding the "is the opcode in range" tests required for switch.
+ * The performance is close to the threaded version, and without the 10%
+ * size increase, but the benchmark results are off enough that it's not
+ * worth adding as a third option.
+ */
+#define THREADED_INTERP /* threaded vs. while-loop interpreter */
+
+#ifdef WITH_INSTR_CHECKS /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * ARM EABI requires 64-bit alignment for access to 64-bit data types. We
+ * can't just use pointers to copy 64-bit values out of our interpreted
+ * register set, because gcc will generate ldrd/strd.
+ *
+ * The __UNION version copies data in and out of a union. The __MEMCPY
+ * version uses a memcpy() call to do the transfer; gcc is smart enough to
+ * not actually call memcpy(). The __UNION version is very bad on ARM;
+ * it only uses one more instruction than __MEMCPY, but for some reason
+ * gcc thinks it needs separate storage for every instance of the union.
+ * On top of that, it feels the need to zero them out at the start of the
+ * method. Net result is we zero out ~700 bytes of stack space at the top
+ * of the interpreter using ARM STM instructions.
+ */
+#if defined(__ARM_EABI__)
+//# define NO_UNALIGN_64__UNION
+# define NO_UNALIGN_64__MEMCPY
+#endif
+
+//#define LOG_INSTR /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Keep a tally of accesses to fields. Currently only works if full DEX
+ * optimization is disabled.
+ */
+#ifdef PROFILE_FIELD_ACCESS
+# define UPDATE_FIELD_GET(_field) { (_field)->gets++; }
+# define UPDATE_FIELD_PUT(_field) { (_field)->puts++; }
+#else
+# define UPDATE_FIELD_GET(_field) ((void)0)
+# define UPDATE_FIELD_PUT(_field) ((void)0)
+#endif
+
+/*
+ * Export another copy of the PC on every instruction; this is largely
+ * redundant with EXPORT_PC and the debugger code. This value can be
+ * compared against what we have stored on the stack with EXPORT_PC to
+ * help ensure that we aren't missing any export calls.
+ */
+#if WITH_EXTRA_GC_CHECKS > 1
+# define EXPORT_EXTRA_PC() (self->currentPc2 = pc)
+#else
+# define EXPORT_EXTRA_PC()
+#endif
+
+/*
+ * Adjust the program counter. "_offset" is a signed int, in 16-bit units.
+ *
+ * Assumes the existence of "const u2* pc" and "const u2* curMethod->insns".
+ *
+ * We don't advance the program counter until we finish an instruction or
+ * branch, because we do want to have to unroll the PC if there's an
+ * exception.
+ */
+#ifdef CHECK_BRANCH_OFFSETS
+# define ADJUST_PC(_offset) do { \
+ int myoff = _offset; /* deref only once */ \
+ if (pc + myoff < curMethod->insns || \
+ pc + myoff >= curMethod->insns + dvmGetMethodInsnsSize(curMethod)) \
+ { \
+ char* desc; \
+ desc = dexProtoCopyMethodDescriptor(&curMethod->prototype); \
+ LOGE("Invalid branch %d at 0x%04x in %s.%s %s\n", \
+ myoff, (int) (pc - curMethod->insns), \
+ curMethod->clazz->descriptor, curMethod->name, desc); \
+ free(desc); \
+ dvmAbort(); \
+ } \
+ pc += myoff; \
+ EXPORT_EXTRA_PC(); \
+ } while (false)
+#else
+# define ADJUST_PC(_offset) do { \
+ pc += _offset; \
+ EXPORT_EXTRA_PC(); \
+ } while (false)
+#endif
+
+/*
+ * If enabled, log instructions as we execute them.
+ */
+#ifdef LOG_INSTR
+# define ILOGD(...) ILOG(LOG_DEBUG, __VA_ARGS__)
+# define ILOGV(...) ILOG(LOG_VERBOSE, __VA_ARGS__)
+# define ILOG(_level, ...) do { \
+ char debugStrBuf[128]; \
+ snprintf(debugStrBuf, sizeof(debugStrBuf), __VA_ARGS__); \
+ if (curMethod != NULL) \
+ LOG(_level, LOG_TAG"i", "%-2d|%04x%s\n", \
+ self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+ else \
+ LOG(_level, LOG_TAG"i", "%-2d|####%s\n", \
+ self->threadId, debugStrBuf); \
+ } while(false)
+void dvmDumpRegs(const Method* method, const u4* framePtr, bool inOnly);
+# define DUMP_REGS(_meth, _frame, _inOnly) dvmDumpRegs(_meth, _frame, _inOnly)
+static const char kSpacing[] = " ";
+#else
+# define ILOGD(...) ((void)0)
+# define ILOGV(...) ((void)0)
+# define DUMP_REGS(_meth, _frame, _inOnly) ((void)0)
+#endif
+
+/* get a long from an array of u4 */
+static inline s8 getLongFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { s8 ll; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.parts[0] = ptr[0];
+ conv.parts[1] = ptr[1];
+ return conv.ll;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ s8 val;
+ memcpy(&val, &ptr[idx], 8);
+ return val;
+#else
+ return *((s8*) &ptr[idx]);
+#endif
+}
+
+/* store a long into an array of u4 */
+static inline void putLongToArray(u4* ptr, int idx, s8 val)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { s8 ll; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.ll = val;
+ ptr[0] = conv.parts[0];
+ ptr[1] = conv.parts[1];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ memcpy(&ptr[idx], &val, 8);
+#else
+ *((s8*) &ptr[idx]) = val;
+#endif
+}
+
+/* get a double from an array of u4 */
+static inline double getDoubleFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { double d; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.parts[0] = ptr[0];
+ conv.parts[1] = ptr[1];
+ return conv.d;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ double dval;
+ memcpy(&dval, &ptr[idx], 8);
+ return dval;
+#else
+ return *((double*) &ptr[idx]);
+#endif
+}
+
+/* store a double into an array of u4 */
+static inline void putDoubleToArray(u4* ptr, int idx, double dval)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { double d; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.d = dval;
+ ptr[0] = conv.parts[0];
+ ptr[1] = conv.parts[1];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ memcpy(&ptr[idx], &dval, 8);
+#else
+ *((double*) &ptr[idx]) = dval;
+#endif
+}
+
+/*
+ * If enabled, validate the register number on every access. Otherwise,
+ * just do an array access.
+ *
+ * Assumes the existence of "u4* fp".
+ *
+ * "_idx" may be referenced more than once.
+ */
+#ifdef CHECK_REGISTER_INDICES
+# define GET_REGISTER(_idx) \
+ ( (_idx) < curMethod->registersSize ? \
+ (fp[(_idx)]) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER(_idx, _val) \
+ ( (_idx) < curMethod->registersSize ? \
+ (fp[(_idx)] = (u4)(_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_AS_OBJECT(_idx) ((Object *)GET_REGISTER(_idx))
+# define SET_REGISTER_AS_OBJECT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_INT(_idx) ((s4) GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ getLongFromArray(fp, (_idx)) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER_WIDE(_idx, _val) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ putLongToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_FLOAT(_idx) \
+ ( (_idx) < curMethod->registersSize ? \
+ (*((float*) &fp[(_idx)])) : (assert(!"bad reg"),1969.0f) )
+# define SET_REGISTER_FLOAT(_idx, _val) \
+ ( (_idx) < curMethod->registersSize ? \
+ (*((float*) &fp[(_idx)]) = (_val)) : (assert(!"bad reg"),1969.0f) )
+# define GET_REGISTER_DOUBLE(_idx) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ getDoubleFromArray(fp, (_idx)) : (assert(!"bad reg"),1969.0) )
+# define SET_REGISTER_DOUBLE(_idx, _val) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ putDoubleToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969.0) )
+#else
+# define GET_REGISTER(_idx) (fp[(_idx)])
+# define SET_REGISTER(_idx, _val) (fp[(_idx)] = (_val))
+# define GET_REGISTER_AS_OBJECT(_idx) ((Object*) fp[(_idx)])
+# define SET_REGISTER_AS_OBJECT(_idx, _val) (fp[(_idx)] = (u4)(_val))
+# define GET_REGISTER_INT(_idx) ((s4)GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) getLongFromArray(fp, (_idx))
+# define SET_REGISTER_WIDE(_idx, _val) putLongToArray(fp, (_idx), (_val))
+# define GET_REGISTER_FLOAT(_idx) (*((float*) &fp[(_idx)]))
+# define SET_REGISTER_FLOAT(_idx, _val) (*((float*) &fp[(_idx)]) = (_val))
+# define GET_REGISTER_DOUBLE(_idx) getDoubleFromArray(fp, (_idx))
+# define SET_REGISTER_DOUBLE(_idx, _val) putDoubleToArray(fp, (_idx), (_val))
+#endif
+
+/*
+ * Get 16 bits from the specified offset of the program counter. We always
+ * want to load 16 bits at a time from the instruction stream -- it's more
+ * efficient than 8 and won't have the alignment problems that 32 might.
+ *
+ * Assumes existence of "const u2* pc".
+ */
+#define FETCH(_offset) (pc[(_offset)])
+
+/*
+ * Extract instruction byte from 16-bit fetch (_inst is a u2).
+ */
+#define INST_INST(_inst) ((_inst) & 0xff)
+
+/*
+ * Replace the opcode (used when handling breakpoints). _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
+ * Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
+ */
+#define INST_A(_inst) (((_inst) >> 8) & 0x0f)
+#define INST_B(_inst) ((_inst) >> 12)
+
+/*
+ * Get the 8-bit "vAA" 8-bit register index from the instruction word.
+ * (_inst is u2)
+ */
+#define INST_AA(_inst) ((_inst) >> 8)
+
+/*
+ * The current PC must be available to Throwable constructors, e.g.
+ * those created by dvmThrowException(), so that the exception stack
+ * trace can be generated correctly. If we don't do this, the offset
+ * within the current method won't be shown correctly. See the notes
+ * in Exception.c.
+ *
+ * This is also used to determine the address for precise GC.
+ *
+ * Assumes existence of "u4* fp" and "const u2* pc".
+ */
+#define EXPORT_PC() (SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc)
+
+/*
+ * Determine if we need to switch to a different interpreter. "_current"
+ * is either INTERP_STD or INTERP_DBG. It should be fixed for a given
+ * interpreter generation file, which should remove the outer conditional
+ * from the following.
+ *
+ * If we're building without debug and profiling support, we never switch.
+ */
+#if defined(WITH_JIT)
+# define NEED_INTERP_SWITCH(_current) ( \
+ (_current == INTERP_STD) ? \
+ dvmJitDebuggerOrProfilerActive() : !dvmJitDebuggerOrProfilerActive() )
+#else
+# define NEED_INTERP_SWITCH(_current) ( \
+ (_current == INTERP_STD) ? \
+ dvmDebuggerOrProfilerActive() : !dvmDebuggerOrProfilerActive() )
+#endif
+
+/*
+ * Check to see if "obj" is NULL. If so, throw an exception. Assumes the
+ * pc has already been exported to the stack.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler calls into
+ * something that could throw an exception (so we have already called
+ * EXPORT_PC at the top).
+ */
+static inline bool checkForNull(Object* obj)
+{
+ if (obj == NULL) {
+ dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+ return false;
+ }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+ if (!dvmIsValidObject(obj)) {
+ LOGE("Invalid object %p\n", obj);
+ dvmAbort();
+ }
+#endif
+#ifndef NDEBUG
+ if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+ /* probable heap corruption */
+ LOGE("Invalid object class %p (in %p)\n", obj->clazz, obj);
+ dvmAbort();
+ }
+#endif
+ return true;
+}
+
+/*
+ * Check to see if "obj" is NULL. If so, export the PC into the stack
+ * frame and throw an exception.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler doesn't do
+ * anything else that can throw an exception.
+ */
+static inline bool checkForNullExportPC(Object* obj, u4* fp, const u2* pc)
+{
+ if (obj == NULL) {
+ EXPORT_PC();
+ dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+ return false;
+ }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+ if (!dvmIsValidObject(obj)) {
+ LOGE("Invalid object %p\n", obj);
+ dvmAbort();
+ }
+#endif
+#ifndef NDEBUG
+ if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+ /* probable heap corruption */
+ LOGE("Invalid object class %p (in %p)\n", obj->clazz, obj);
+ dvmAbort();
+ }
+#endif
+ return true;
+}
+
+/* File: portable/portdbg.c */
+#define INTERP_FUNC_NAME dvmInterpretDbg
+#define INTERP_TYPE INTERP_DBG
+
+#define CHECK_DEBUG_AND_PROF() \
+ checkDebugAndProf(pc, fp, self, curMethod, &debugIsMethodEntry)
+
+#if defined(WITH_JIT)
+#define CHECK_JIT_BOOL() (dvmCheckJit(pc, self, interpState, callsiteClass,\
+ methodToCall))
+#define CHECK_JIT_VOID() (dvmCheckJit(pc, self, interpState, callsiteClass,\
+ methodToCall))
+#define ABORT_JIT_TSELECT() (dvmJitAbortTraceSelect(interpState))
+#else
+#define CHECK_JIT_BOOL() (false)
+#define CHECK_JIT_VOID()
+#define ABORT_JIT_TSELECT(x) ((void)0)
+#endif
+
+/* File: portable/stubdefs.c */
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...)
+
+#define GOTO_TARGET(_target, ...) _target:
+
+#define GOTO_TARGET_END
+
+/* ugh */
+#define STUB_HACK(x)
+
+/*
+ * Instruction framing. For a switch-oriented implementation this is
+ * case/break, for a threaded implementation it's a goto label and an
+ * instruction fetch/computed goto.
+ *
+ * Assumes the existence of "const u2* pc" and (for threaded operation)
+ * "u2 inst".
+ *
+ * TODO: remove "switch" version.
+ */
+#ifdef THREADED_INTERP
+# define H(_op) &&op_##_op
+# define HANDLE_OPCODE(_op) op_##_op:
+# define FINISH(_offset) { \
+ ADJUST_PC(_offset); \
+ inst = FETCH(0); \
+ CHECK_DEBUG_AND_PROF(); \
+ CHECK_TRACKED_REFS(); \
+ if (CHECK_JIT_BOOL()) GOTO_bail_switch(); \
+ goto *handlerTable[INST_INST(inst)]; \
+ }
+# define FINISH_BKPT(_opcode) { \
+ goto *handlerTable[_opcode]; \
+ }
+#else
+# define HANDLE_OPCODE(_op) case _op:
+# define FINISH(_offset) { ADJUST_PC(_offset); break; }
+# define FINISH_BKPT(opcode) { > not implemented < }
+#endif
+
+#define OP_END
+
+#if defined(WITH_TRACKREF_CHECKS)
+# define CHECK_TRACKED_REFS() \
+ dvmInterpCheckTrackedRefs(self, curMethod, debugTrackedRefStart)
+#else
+# define CHECK_TRACKED_REFS() ((void)0)
+#endif
+
+
+/*
+ * The "goto" targets just turn into goto statements. The "arguments" are
+ * passed through local variables.
+ */
+
+#define GOTO_exceptionThrown() goto exceptionThrown;
+
+#define GOTO_returnFromMethod() goto returnFromMethod;
+
+#define GOTO_invoke(_target, _methodCallRange) \
+ do { \
+ methodCallRange = _methodCallRange; \
+ goto _target; \
+ } while(false)
+
+/* for this, the "args" are already in the locals */
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst) goto invokeMethod;
+
+#define GOTO_bail() goto bail;
+#define GOTO_bail_switch() goto bail_switch;
+
+/*
+ * Periodically check for thread suspension.
+ *
+ * While we're at it, see if a debugger has attached or the profiler has
+ * started. If so, switch to a different "goto" table.
+ */
+#define PERIODIC_CHECKS(_entryPoint, _pcadj) { \
+ if (dvmCheckSuspendQuick(self)) { \
+ EXPORT_PC(); /* need for precise GC */ \
+ dvmCheckSuspendPending(self); \
+ } \
+ if (NEED_INTERP_SWITCH(INTERP_TYPE)) { \
+ ADJUST_PC(_pcadj); \
+ interpState->entryPoint = _entryPoint; \
+ LOGVV("threadid=%d: switch to %s ep=%d adj=%d\n", \
+ self->threadId, \
+ (interpState->nextMode == INTERP_STD) ? "STD" : "DBG", \
+ (_entryPoint), (_pcadj)); \
+ GOTO_bail_switch(); \
+ } \
+ }
+
+/* File: c/opcommon.c */
+/* forward declarations of goto targets */
+GOTO_TARGET_DECL(filledNewArray, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtual, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuper, bool methodCallRange);
+GOTO_TARGET_DECL(invokeInterface, bool methodCallRange);
+GOTO_TARGET_DECL(invokeDirect, bool methodCallRange);
+GOTO_TARGET_DECL(invokeStatic, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtualQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuperQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeMethod, bool methodCallRange, const Method* methodToCall,
+ u2 count, u2 regs);
+GOTO_TARGET_DECL(returnFromMethod);
+GOTO_TARGET_DECL(exceptionThrown);
+
+/*
+ * ===========================================================================
+ *
+ * What follows are opcode definitions shared between multiple opcodes with
+ * minor substitutions handled by the C pre-processor. These should probably
+ * use the mterp substitution mechanism instead, with the code here moved
+ * into common fragment files (like the asm "binop.S"), although it's hard
+ * to give up the C preprocessor in favor of the much simpler text subst.
+ *
+ * ===========================================================================
+ */
+
+#define HANDLE_NUMCONV(_opcode, _opname, _fromtype, _totype) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER##_totype(vdst, \
+ GET_REGISTER##_fromtype(vsrc1)); \
+ FINISH(1);
+
+#define HANDLE_FLOAT_TO_INT(_opcode, _opname, _fromvtype, _fromrtype, \
+ _tovtype, _tortype) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ { \
+ /* spec defines specific handling for +/- inf and NaN values */ \
+ _fromvtype val; \
+ _tovtype intMin, intMax, result; \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1); \
+ val = GET_REGISTER##_fromrtype(vsrc1); \
+ intMin = (_tovtype) 1 << (sizeof(_tovtype) * 8 -1); \
+ intMax = ~intMin; \
+ result = (_tovtype) val; \
+ if (val >= intMax) /* +inf */ \
+ result = intMax; \
+ else if (val <= intMin) /* -inf */ \
+ result = intMin; \
+ else if (val != val) /* NaN */ \
+ result = 0; \
+ else \
+ result = (_tovtype) val; \
+ SET_REGISTER##_tortype(vdst, result); \
+ } \
+ FINISH(1);
+
+#define HANDLE_INT_TO_SMALL(_opcode, _opname, _type) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|int-to-%s v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER(vdst, (_type) GET_REGISTER(vsrc1)); \
+ FINISH(1);
+
+/* NOTE: the comparison result is always a signed 4-byte integer */
+#define HANDLE_OP_CMPX(_opcode, _opname, _varType, _type, _nanVal) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ int result; \
+ u2 regs; \
+ _varType val1, val2; \
+ vdst = INST_AA(inst); \
+ regs = FETCH(1); \
+ vsrc1 = regs & 0xff; \
+ vsrc2 = regs >> 8; \
+ ILOGV("|cmp%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ val1 = GET_REGISTER##_type(vsrc1); \
+ val2 = GET_REGISTER##_type(vsrc2); \
+ if (val1 == val2) \
+ result = 0; \
+ else if (val1 < val2) \
+ result = -1; \
+ else if (val1 > val2) \
+ result = 1; \
+ else \
+ result = (_nanVal); \
+ ILOGV("+ result=%d\n", result); \
+ SET_REGISTER(vdst, result); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_IF_XX(_opcode, _opname, _cmp) \
+ HANDLE_OPCODE(_opcode /*vA, vB, +CCCC*/) \
+ vsrc1 = INST_A(inst); \
+ vsrc2 = INST_B(inst); \
+ if ((s4) GET_REGISTER(vsrc1) _cmp (s4) GET_REGISTER(vsrc2)) { \
+ int branchOffset = (s2)FETCH(1); /* sign-extended */ \
+ ILOGV("|if-%s v%d,v%d,+0x%04x", (_opname), vsrc1, vsrc2, \
+ branchOffset); \
+ ILOGV("> branch taken"); \
+ if (branchOffset < 0) \
+ PERIODIC_CHECKS(kInterpEntryInstr, branchOffset); \
+ FINISH(branchOffset); \
+ } else { \
+ ILOGV("|if-%s v%d,v%d,-", (_opname), vsrc1, vsrc2); \
+ FINISH(2); \
+ }
+
+#define HANDLE_OP_IF_XXZ(_opcode, _opname, _cmp) \
+ HANDLE_OPCODE(_opcode /*vAA, +BBBB*/) \
+ vsrc1 = INST_AA(inst); \
+ if ((s4) GET_REGISTER(vsrc1) _cmp 0) { \
+ int branchOffset = (s2)FETCH(1); /* sign-extended */ \
+ ILOGV("|if-%s v%d,+0x%04x", (_opname), vsrc1, branchOffset); \
+ ILOGV("> branch taken"); \
+ if (branchOffset < 0) \
+ PERIODIC_CHECKS(kInterpEntryInstr, branchOffset); \
+ FINISH(branchOffset); \
+ } else { \
+ ILOGV("|if-%s v%d,-", (_opname), vsrc1); \
+ FINISH(2); \
+ }
+
+#define HANDLE_UNOP(_opcode, _opname, _pfx, _sfx, _type) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER##_type(vdst, _pfx GET_REGISTER##_type(vsrc1) _sfx); \
+ FINISH(1);
+
+#define HANDLE_OP_X_INT(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER(vsrc1); \
+ secondVal = GET_REGISTER(vsrc2); \
+ if (secondVal == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && secondVal == -1) { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ /* non-div/rem case */ \
+ SET_REGISTER(vdst, \
+ (s4) GET_REGISTER(vsrc1) _op (s4) GET_REGISTER(vsrc2)); \
+ } \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_SHX_INT(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER(vdst, \
+ _cast GET_REGISTER(vsrc1) _op (GET_REGISTER(vsrc2) & 0x1f)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT16(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vA, vB, #+CCCC*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ vsrc2 = FETCH(1); \
+ ILOGV("|%s-int/lit16 v%d,v%d,#+0x%04x", \
+ (_opname), vdst, vsrc1, vsrc2); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, result; \
+ firstVal = GET_REGISTER(vsrc1); \
+ if ((s2) vsrc2 == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && ((s2) vsrc2) == -1) { \
+ /* won't generate /lit16 instr for this; check anyway */ \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op (s2) vsrc2; \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ /* non-div/rem case */ \
+ SET_REGISTER(vdst, GET_REGISTER(vsrc1) _op (s2) vsrc2); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT8(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/) \
+ { \
+ u2 litInfo; \
+ vdst = INST_AA(inst); \
+ litInfo = FETCH(1); \
+ vsrc1 = litInfo & 0xff; \
+ vsrc2 = litInfo >> 8; /* constant */ \
+ ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x", \
+ (_opname), vdst, vsrc1, vsrc2); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, result; \
+ firstVal = GET_REGISTER(vsrc1); \
+ if ((s1) vsrc2 == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && ((s1) vsrc2) == -1) { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op ((s1) vsrc2); \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ SET_REGISTER(vdst, \
+ (s4) GET_REGISTER(vsrc1) _op (s1) vsrc2); \
+ } \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_SHX_INT_LIT8(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/) \
+ { \
+ u2 litInfo; \
+ vdst = INST_AA(inst); \
+ litInfo = FETCH(1); \
+ vsrc1 = litInfo & 0xff; \
+ vsrc2 = litInfo >> 8; /* constant */ \
+ ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x", \
+ (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER(vdst, \
+ _cast GET_REGISTER(vsrc1) _op (vsrc2 & 0x1f)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_INT_2ADDR(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER(vdst); \
+ secondVal = GET_REGISTER(vsrc1); \
+ if (secondVal == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && secondVal == -1) { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ SET_REGISTER(vdst, \
+ (s4) GET_REGISTER(vdst) _op (s4) GET_REGISTER(vsrc1)); \
+ } \
+ FINISH(1);
+
+#define HANDLE_OP_SHX_INT_2ADDR(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER(vdst, \
+ _cast GET_REGISTER(vdst) _op (GET_REGISTER(vsrc1) & 0x1f)); \
+ FINISH(1);
+
+#define HANDLE_OP_X_LONG(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ if (_chkdiv != 0) { \
+ s8 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER_WIDE(vsrc1); \
+ secondVal = GET_REGISTER_WIDE(vsrc2); \
+ if (secondVal == 0LL) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u8)firstVal == 0x8000000000000000ULL && \
+ secondVal == -1LL) \
+ { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER_WIDE(vdst, result); \
+ } else { \
+ SET_REGISTER_WIDE(vdst, \
+ (s8) GET_REGISTER_WIDE(vsrc1) _op (s8) GET_REGISTER_WIDE(vsrc2)); \
+ } \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_SHX_LONG(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER_WIDE(vdst, \
+ _cast GET_REGISTER_WIDE(vsrc1) _op (GET_REGISTER(vsrc2) & 0x3f)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_LONG_2ADDR(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ if (_chkdiv != 0) { \
+ s8 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER_WIDE(vdst); \
+ secondVal = GET_REGISTER_WIDE(vsrc1); \
+ if (secondVal == 0LL) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u8)firstVal == 0x8000000000000000ULL && \
+ secondVal == -1LL) \
+ { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER_WIDE(vdst, result); \
+ } else { \
+ SET_REGISTER_WIDE(vdst, \
+ (s8) GET_REGISTER_WIDE(vdst) _op (s8)GET_REGISTER_WIDE(vsrc1));\
+ } \
+ FINISH(1);
+
+#define HANDLE_OP_SHX_LONG_2ADDR(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER_WIDE(vdst, \
+ _cast GET_REGISTER_WIDE(vdst) _op (GET_REGISTER(vsrc1) & 0x3f)); \
+ FINISH(1);
+
+#define HANDLE_OP_X_FLOAT(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-float v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER_FLOAT(vdst, \
+ GET_REGISTER_FLOAT(vsrc1) _op GET_REGISTER_FLOAT(vsrc2)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_DOUBLE(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-double v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER_DOUBLE(vdst, \
+ GET_REGISTER_DOUBLE(vsrc1) _op GET_REGISTER_DOUBLE(vsrc2)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_FLOAT_2ADDR(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-float-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER_FLOAT(vdst, \
+ GET_REGISTER_FLOAT(vdst) _op GET_REGISTER_FLOAT(vsrc1)); \
+ FINISH(1);
+
+#define HANDLE_OP_X_DOUBLE_2ADDR(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-double-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER_DOUBLE(vdst, \
+ GET_REGISTER_DOUBLE(vdst) _op GET_REGISTER_DOUBLE(vsrc1)); \
+ FINISH(1);
+
+#define HANDLE_OP_AGET(_opcode, _opname, _type, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ ArrayObject* arrayObj; \
+ u2 arrayInfo; \
+ EXPORT_PC(); \
+ vdst = INST_AA(inst); \
+ arrayInfo = FETCH(1); \
+ vsrc1 = arrayInfo & 0xff; /* array ptr */ \
+ vsrc2 = arrayInfo >> 8; /* index */ \
+ ILOGV("|aget%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ arrayObj = (ArrayObject*) GET_REGISTER(vsrc1); \
+ if (!checkForNull((Object*) arrayObj)) \
+ GOTO_exceptionThrown(); \
+ if (GET_REGISTER(vsrc2) >= arrayObj->length) { \
+ LOGV("Invalid array access: %p %d (len=%d)\n", \
+ arrayObj, vsrc2, arrayObj->length); \
+ dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+ NULL); \
+ GOTO_exceptionThrown(); \
+ } \
+ SET_REGISTER##_regsize(vdst, \
+ ((_type*) arrayObj->contents)[GET_REGISTER(vsrc2)]); \
+ ILOGV("+ AGET[%d]=0x%x", GET_REGISTER(vsrc2), GET_REGISTER(vdst)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_APUT(_opcode, _opname, _type, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ ArrayObject* arrayObj; \
+ u2 arrayInfo; \
+ EXPORT_PC(); \
+ vdst = INST_AA(inst); /* AA: source value */ \
+ arrayInfo = FETCH(1); \
+ vsrc1 = arrayInfo & 0xff; /* BB: array ptr */ \
+ vsrc2 = arrayInfo >> 8; /* CC: index */ \
+ ILOGV("|aput%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ arrayObj = (ArrayObject*) GET_REGISTER(vsrc1); \
+ if (!checkForNull((Object*) arrayObj)) \
+ GOTO_exceptionThrown(); \
+ if (GET_REGISTER(vsrc2) >= arrayObj->length) { \
+ dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+ NULL); \
+ GOTO_exceptionThrown(); \
+ } \
+ ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+ ((_type*) arrayObj->contents)[GET_REGISTER(vsrc2)] = \
+ GET_REGISTER##_regsize(vdst); \
+ } \
+ FINISH(2);
+
+/*
+ * It's possible to get a bad value out of a field with sub-32-bit stores
+ * because the -quick versions always operate on 32 bits. Consider:
+ * short foo = -1 (sets a 32-bit register to 0xffffffff)
+ * iput-quick foo (writes all 32 bits to the field)
+ * short bar = 1 (sets a 32-bit register to 0x00000001)
+ * iput-short (writes the low 16 bits to the field)
+ * iget-quick foo (reads all 32 bits from the field, yielding 0xffff0001)
+ * This can only happen when optimized and non-optimized code has interleaved
+ * access to the same field. This is unlikely but possible.
+ *
+ * The easiest way to fix this is to always read/write 32 bits at a time. On
+ * a device with a 16-bit data bus this is sub-optimal. (The alternative
+ * approach is to have sub-int versions of iget-quick, but now we're wasting
+ * Dalvik instruction space and making it less likely that handler code will
+ * already be in the CPU i-cache.)
+ */
+#define HANDLE_IGET_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ InstField* ifield; \
+ Object* obj; \
+ EXPORT_PC(); \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|iget%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNull(obj)) \
+ GOTO_exceptionThrown(); \
+ ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref); \
+ if (ifield == NULL) { \
+ ifield = dvmResolveInstField(curMethod->clazz, ref); \
+ if (ifield == NULL) \
+ GOTO_exceptionThrown(); \
+ } \
+ SET_REGISTER##_regsize(vdst, \
+ dvmGetField##_ftype(obj, ifield->byteOffset)); \
+ ILOGV("+ IGET '%s'=0x%08llx", ifield->field.name, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_GET(&ifield->field); \
+ } \
+ FINISH(2);
+
+#define HANDLE_IGET_X_QUICK(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ Object* obj; \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field offset */ \
+ ILOGV("|iget%s-quick v%d,v%d,field@+%u", \
+ (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNullExportPC(obj, fp, pc)) \
+ GOTO_exceptionThrown(); \
+ SET_REGISTER##_regsize(vdst, dvmGetField##_ftype(obj, ref)); \
+ ILOGV("+ IGETQ %d=0x%08llx", ref, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_IPUT_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ InstField* ifield; \
+ Object* obj; \
+ EXPORT_PC(); \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|iput%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNull(obj)) \
+ GOTO_exceptionThrown(); \
+ ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref); \
+ if (ifield == NULL) { \
+ ifield = dvmResolveInstField(curMethod->clazz, ref); \
+ if (ifield == NULL) \
+ GOTO_exceptionThrown(); \
+ } \
+ dvmSetField##_ftype(obj, ifield->byteOffset, \
+ GET_REGISTER##_regsize(vdst)); \
+ ILOGV("+ IPUT '%s'=0x%08llx", ifield->field.name, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_PUT(&ifield->field); \
+ } \
+ FINISH(2);
+
+#define HANDLE_IPUT_X_QUICK(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ Object* obj; \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field offset */ \
+ ILOGV("|iput%s-quick v%d,v%d,field@0x%04x", \
+ (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNullExportPC(obj, fp, pc)) \
+ GOTO_exceptionThrown(); \
+ dvmSetField##_ftype(obj, ref, GET_REGISTER##_regsize(vdst)); \
+ ILOGV("+ IPUTQ %d=0x%08llx", ref, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ } \
+ FINISH(2);
+
+/*
+ * The JIT needs dvmDexGetResolvedField() to return non-null.
+ * Since we use the portable interpreter to build the trace, the extra
+ * checks in HANDLE_SGET_X and HANDLE_SPUT_X are not needed for mterp.
+ */
+#define HANDLE_SGET_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/) \
+ { \
+ StaticField* sfield; \
+ vdst = INST_AA(inst); \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|sget%s v%d,sfield@0x%04x", (_opname), vdst, ref); \
+ sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+ if (sfield == NULL) { \
+ EXPORT_PC(); \
+ sfield = dvmResolveStaticField(curMethod->clazz, ref); \
+ if (sfield == NULL) \
+ GOTO_exceptionThrown(); \
+ if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) { \
+ ABORT_JIT_TSELECT(); \
+ } \
+ } \
+ SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield)); \
+ ILOGV("+ SGET '%s'=0x%08llx", \
+ sfield->field.name, (u8)GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_GET(&sfield->field); \
+ } \
+ FINISH(2);
+
+#define HANDLE_SPUT_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/) \
+ { \
+ StaticField* sfield; \
+ vdst = INST_AA(inst); \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|sput%s v%d,sfield@0x%04x", (_opname), vdst, ref); \
+ sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+ if (sfield == NULL) { \
+ EXPORT_PC(); \
+ sfield = dvmResolveStaticField(curMethod->clazz, ref); \
+ if (sfield == NULL) \
+ GOTO_exceptionThrown(); \
+ if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) { \
+ ABORT_JIT_TSELECT(); \
+ } \
+ } \
+ dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst)); \
+ ILOGV("+ SPUT '%s'=0x%08llx", \
+ sfield->field.name, (u8)GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_PUT(&sfield->field); \
+ } \
+ FINISH(2);
+
+/* File: portable/debug.c */
+/* code in here is only included in portable-debug interpreter */
+
+/*
+ * Update the debugger on interesting events, such as hitting a breakpoint
+ * or a single-step point. This is called from the top of the interpreter
+ * loop, before the current instruction is processed.
+ *
+ * Set "methodEntry" if we've just entered the method. This detects
+ * method exit by checking to see if the next instruction is "return".
+ *
+ * This can't catch native method entry/exit, so we have to handle that
+ * at the point of invocation. We also need to catch it in dvmCallMethod
+ * if we want to capture native->native calls made through JNI.
+ *
+ * Notes to self:
+ * - Don't want to switch to VMWAIT while posting events to the debugger.
+ * Let the debugger code decide if we need to change state.
+ * - We may want to check for debugger-induced thread suspensions on
+ * every instruction. That would make a "suspend all" more responsive
+ * and reduce the chances of multiple simultaneous events occurring.
+ * However, it could change the behavior some.
+ *
+ * TODO: method entry/exit events are probably less common than location
+ * breakpoints. We may be able to speed things up a bit if we don't query
+ * the event list unless we know there's at least one lurking within.
+ */
+static void updateDebugger(const Method* method, const u2* pc, const u4* fp,
+ bool methodEntry, Thread* self)
+{
+ int eventFlags = 0;
+
+ /*
+ * Update xtra.currentPc on every instruction. We need to do this if
+ * there's a chance that we could get suspended. This can happen if
+ * eventFlags != 0 here, or somebody manually requests a suspend
+ * (which gets handled at PERIOD_CHECKS time). One place where this
+ * needs to be correct is in dvmAddSingleStep().
+ */
+ EXPORT_PC();
+
+ if (methodEntry)
+ eventFlags |= DBG_METHOD_ENTRY;
+
+ /*
+ * See if we have a breakpoint here.
+ *
+ * Depending on the "mods" associated with event(s) on this address,
+ * we may or may not actually send a message to the debugger.
+ */
+ if (INST_INST(*pc) == OP_BREAKPOINT) {
+ LOGV("+++ breakpoint hit at %p\n", pc);
+ eventFlags |= DBG_BREAKPOINT;
+ }
+
+ /*
+ * If the debugger is single-stepping one of our threads, check to
+ * see if we're that thread and we've reached a step point.
+ */
+ const StepControl* pCtrl = &gDvm.stepControl;
+ if (pCtrl->active && pCtrl->thread == self) {
+ int frameDepth;
+ bool doStop = false;
+ const char* msg = NULL;
+
+ assert(!dvmIsNativeMethod(method));
+
+ if (pCtrl->depth == SD_INTO) {
+ /*
+ * Step into method calls. We break when the line number
+ * or method pointer changes. If we're in SS_MIN mode, we
+ * always stop.
+ */
+ if (pCtrl->method != method) {
+ doStop = true;
+ msg = "new method";
+ } else if (pCtrl->size == SS_MIN) {
+ doStop = true;
+ msg = "new instruction";
+ } else if (!dvmAddressSetGet(
+ pCtrl->pAddressSet, pc - method->insns)) {
+ doStop = true;
+ msg = "new line";
+ }
+ } else if (pCtrl->depth == SD_OVER) {
+ /*
+ * Step over method calls. We break when the line number is
+ * different and the frame depth is <= the original frame
+ * depth. (We can't just compare on the method, because we
+ * might get unrolled past it by an exception, and it's tricky
+ * to identify recursion.)
+ */
+ frameDepth = dvmComputeVagueFrameDepth(self, fp);
+ if (frameDepth < pCtrl->frameDepth) {
+ /* popped up one or more frames, always trigger */
+ doStop = true;
+ msg = "method pop";
+ } else if (frameDepth == pCtrl->frameDepth) {
+ /* same depth, see if we moved */
+ if (pCtrl->size == SS_MIN) {
+ doStop = true;
+ msg = "new instruction";
+ } else if (!dvmAddressSetGet(pCtrl->pAddressSet,
+ pc - method->insns)) {
+ doStop = true;
+ msg = "new line";
+ }
+ }
+ } else {
+ assert(pCtrl->depth == SD_OUT);
+ /*
+ * Return from the current method. We break when the frame
+ * depth pops up.
+ *
+ * This differs from the "method exit" break in that it stops
+ * with the PC at the next instruction in the returned-to
+ * function, rather than the end of the returning function.
+ */
+ frameDepth = dvmComputeVagueFrameDepth(self, fp);
+ if (frameDepth < pCtrl->frameDepth) {
+ doStop = true;
+ msg = "method pop";
+ }
+ }
+
+ if (doStop) {
+ LOGV("#####S %s\n", msg);
+ eventFlags |= DBG_SINGLE_STEP;
+ }
+ }
+
+ /*
+ * Check to see if this is a "return" instruction. JDWP says we should
+ * send the event *after* the code has been executed, but it also says
+ * the location we provide is the last instruction. Since the "return"
+ * instruction has no interesting side effects, we should be safe.
+ * (We can't just move this down to the returnFromMethod label because
+ * we potentially need to combine it with other events.)
+ *
+ * We're also not supposed to generate a method exit event if the method
+ * terminates "with a thrown exception".
+ */
+ u2 inst = INST_INST(FETCH(0));
+ if (inst == OP_RETURN_VOID || inst == OP_RETURN || inst == OP_RETURN_WIDE ||
+ inst == OP_RETURN_OBJECT)
+ {
+ eventFlags |= DBG_METHOD_EXIT;
+ }
+
+ /*
+ * If there's something interesting going on, see if it matches one
+ * of the debugger filters.
+ */
+ if (eventFlags != 0) {
+ Object* thisPtr = dvmGetThisPtr(method, fp);
+ if (thisPtr != NULL && !dvmIsValidObject(thisPtr)) {
+ /*
+ * TODO: remove this check if we're confident that the "this"
+ * pointer is where it should be -- slows us down, especially
+ * during single-step.
+ */
+ char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+ LOGE("HEY: invalid 'this' ptr %p (%s.%s %s)\n", thisPtr,
+ method->clazz->descriptor, method->name, desc);
+ free(desc);
+ dvmAbort();
+ }
+ dvmDbgPostLocationEvent(method, pc - method->insns, thisPtr,
+ eventFlags);
+ }
+}
+
+/*
+ * Perform some operations at the "top" of the interpreter loop.
+ * This stuff is required to support debugging and profiling.
+ *
+ * Using" __attribute__((noinline))" seems to do more harm than good. This
+ * is best when inlined due to the large number of parameters, most of
+ * which are local vars in the main interp loop.
+ */
+static void checkDebugAndProf(const u2* pc, const u4* fp, Thread* self,
+ const Method* method, bool* pIsMethodEntry)
+{
+ /* check to see if we've run off end of method */
+ assert(pc >= method->insns && pc <
+ method->insns + dvmGetMethodInsnsSize(method));
+
+#if 0
+ /*
+ * When we hit a specific method, enable verbose instruction logging.
+ * Sometimes it's helpful to use the debugger attach as a trigger too.
+ */
+ if (*pIsMethodEntry) {
+ static const char* cd = "Landroid/test/Arithmetic;";
+ static const char* mn = "shiftTest2";
+ static const char* sg = "()V";
+
+ if (/*gDvm.debuggerActive &&*/
+ strcmp(method->clazz->descriptor, cd) == 0 &&
+ strcmp(method->name, mn) == 0 &&
+ strcmp(method->shorty, sg) == 0)
+ {
+ LOGW("Reached %s.%s, enabling verbose mode\n",
+ method->clazz->descriptor, method->name);
+ android_setMinPriority(LOG_TAG"i", ANDROID_LOG_VERBOSE);
+ dumpRegs(method, fp, true);
+ }
+
+ if (!gDvm.debuggerActive)
+ *pIsMethodEntry = false;
+ }
+#endif
+
+ /*
+ * If the debugger is attached, check for events. If the profiler is
+ * enabled, update that too.
+ *
+ * This code is executed for every instruction we interpret, so for
+ * performance we use a couple of #ifdef blocks instead of runtime tests.
+ */
+ bool isEntry = *pIsMethodEntry;
+ if (isEntry) {
+ *pIsMethodEntry = false;
+ TRACE_METHOD_ENTER(self, method);
+ }
+ if (gDvm.debuggerActive) {
+ updateDebugger(method, pc, fp, isEntry, self);
+ }
+ if (gDvm.instructionCountEnableCount != 0) {
+ /*
+ * Count up the #of executed instructions. This isn't synchronized
+ * for thread-safety; if we need that we should make this
+ * thread-local and merge counts into the global area when threads
+ * exit (perhaps suspending all other threads GC-style and pulling
+ * the data out of them).
+ */
+ int inst = *pc & 0xff;
+ gDvm.executedInstrCounts[inst]++;
+ }
+}
+
+/* File: portable/entry.c */
+/*
+ * Main interpreter loop.
+ *
+ * This was written with an ARM implementation in mind.
+ */
+bool INTERP_FUNC_NAME(Thread* self, InterpState* interpState)
+{
+#if defined(EASY_GDB)
+ StackSaveArea* debugSaveArea = SAVEAREA_FROM_FP(self->curFrame);
+#endif
+#if INTERP_TYPE == INTERP_DBG
+ bool debugIsMethodEntry = false;
+ debugIsMethodEntry = interpState->debugIsMethodEntry;
+#endif
+#if defined(WITH_TRACKREF_CHECKS)
+ int debugTrackedRefStart = interpState->debugTrackedRefStart;
+#endif
+ DvmDex* methodClassDex; // curMethod->clazz->pDvmDex
+ JValue retval;
+
+ /* core state */
+ const Method* curMethod; // method we're interpreting
+ const u2* pc; // program counter
+ u4* fp; // frame pointer
+ u2 inst; // current instruction
+ /* instruction decoding */
+ u2 ref; // 16-bit quantity fetched directly
+ u2 vsrc1, vsrc2, vdst; // usually used for register indexes
+ /* method call setup */
+ const Method* methodToCall;
+ bool methodCallRange;
+
+
+#if defined(THREADED_INTERP)
+ /* static computed goto table */
+ DEFINE_GOTO_TABLE(handlerTable);
+#endif
+
+#if defined(WITH_JIT)
+#if 0
+ LOGD("*DebugInterp - entrypoint is %d, tgt is 0x%x, %s\n",
+ interpState->entryPoint,
+ interpState->pc,
+ interpState->method->name);
+#endif
+#if INTERP_TYPE == INTERP_DBG
+ const ClassObject* callsiteClass = NULL;
+
+#if defined(WITH_SELF_VERIFICATION)
+ if (interpState->jitState != kJitSelfVerification) {
+ interpState->self->shadowSpace->jitExitState = kSVSIdle;
+ }
+#endif
+
+ /* Check to see if we've got a trace selection request. */
+ if (
+ /*
+ * Only perform dvmJitCheckTraceRequest if the entry point is
+ * EntryInstr and the jit state is either kJitTSelectRequest or
+ * kJitTSelectRequestHot. If debugger/profiler happens to be attached,
+ * dvmJitCheckTraceRequest will change the jitState to kJitDone but
+ * but stay in the dbg interpreter.
+ */
+ (interpState->entryPoint == kInterpEntryInstr) &&
+ (interpState->jitState == kJitTSelectRequest ||
+ interpState->jitState == kJitTSelectRequestHot) &&
+ dvmJitCheckTraceRequest(self, interpState)) {
+ interpState->nextMode = INTERP_STD;
+ //LOGD("Invalid trace request, exiting\n");
+ return true;
+ }
+#endif /* INTERP_TYPE == INTERP_DBG */
+#endif /* WITH_JIT */
+
+ /* copy state in */
+ curMethod = interpState->method;
+ pc = interpState->pc;
+ fp = interpState->fp;
+ retval = interpState->retval; /* only need for kInterpEntryReturn? */
+
+ methodClassDex = curMethod->clazz->pDvmDex;
+
+ LOGVV("threadid=%d: entry(%s) %s.%s pc=0x%x fp=%p ep=%d\n",
+ self->threadId, (interpState->nextMode == INTERP_STD) ? "STD" : "DBG",
+ curMethod->clazz->descriptor, curMethod->name, pc - curMethod->insns,
+ fp, interpState->entryPoint);
+
+ /*
+ * DEBUG: scramble this to ensure we're not relying on it.
+ */
+ methodToCall = (const Method*) -1;
+
+#if INTERP_TYPE == INTERP_DBG
+ if (debugIsMethodEntry) {
+ ILOGD("|-- Now interpreting %s.%s", curMethod->clazz->descriptor,
+ curMethod->name);
+ DUMP_REGS(curMethod, interpState->fp, false);
+ }
+#endif
+
+ switch (interpState->entryPoint) {
+ case kInterpEntryInstr:
+ /* just fall through to instruction loop or threaded kickstart */
+ break;
+ case kInterpEntryReturn:
+ CHECK_JIT_VOID();
+ goto returnFromMethod;
+ case kInterpEntryThrow:
+ goto exceptionThrown;
+ default:
+ dvmAbort();
+ }
+
+#ifdef THREADED_INTERP
+ FINISH(0); /* fetch and execute first instruction */
+#else
+ while (1) {
+ CHECK_DEBUG_AND_PROF(); /* service debugger and profiling */
+ CHECK_TRACKED_REFS(); /* check local reference tracking */
+
+ /* fetch the next 16 bits from the instruction stream */
+ inst = FETCH(0);
+
+ switch (INST_INST(inst)) {
+#endif
+
+/*--- start of opcodes ---*/
+
+/* File: c/OP_NOP.c */
+HANDLE_OPCODE(OP_NOP)
+ FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE.c */
+HANDLE_OPCODE(OP_MOVE /*vA, vB*/)
+ vdst = INST_A(inst);
+ vsrc1 = INST_B(inst);
+ ILOGV("|move%s v%d,v%d %s(v%d=0x%08x)",
+ (INST_INST(inst) == OP_MOVE) ? "" : "-object", vdst, vsrc1,
+ kSpacing, vdst, GET_REGISTER(vsrc1));
+ SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+ FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE_FROM16.c */
+HANDLE_OPCODE(OP_MOVE_FROM16 /*vAA, vBBBB*/)
+ vdst = INST_AA(inst);
+ vsrc1 = FETCH(1);
+ ILOGV("|move%s/from16 v%d,v%d %s(v%d=0x%08x)",
+ (INST_INST(inst) == OP_MOVE_FROM16) ? "" : "-object", vdst, vsrc1,
+ kSpacing, vdst, GET_REGISTER(vsrc1));
+ SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+ FINISH(2);
+OP_END
+
+/* File: c/OP_MOVE_16.c */
+HANDLE_OPCODE(OP_MOVE_16 /*vAAAA, vBBBB*/)
+ vdst = FETCH(1);
+ vsrc1 = FETCH(2);
+ ILOGV("|move%s/16 v%d,v%d %s(v%d=0x%08x)",
+ (INST_INST(inst) == OP_MOVE_16) ? "" : "-object", vdst, vsrc1,
+ kSpacing, vdst, GET_REGISTER(vsrc1));
+ SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+ FINISH(3);
+OP_END
+
+/* File: c/OP_MOVE_WIDE.c */
+HANDLE_OPCODE(OP_MOVE_WIDE /*vA, vB*/)
+ /* IMPORTANT: must correctly handle overlapping registers, e.g. both
+ * "move-wide v6, v7" and "move-wide v7, v6" */
+ vdst = INST_A(inst);
+ vsrc1 = INST_B(inst);
+ ILOGV("|move-wide v%d,v%d %s(v%d=0x%08llx)", vdst, vsrc1,
+ kSpacing+5, vdst, GET_REGISTER_WIDE(vsrc1));
+ SET_REGISTER_WIDE(vdst, GET_REGISTER_WIDE(vsrc1));
+ FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE_WIDE_FROM16.c */
+HANDLE_OPCODE(OP_MOVE_WIDE_FROM16 /*vAA, vBBBB*/)
+ vdst = INST_AA(inst);
+ vsrc1 = FETCH(1);
+ ILOGV("|move-wide/from16 v%d,v%d (v%d=0x%08llx)", vdst, vsrc1,
+ vdst, GET_REGISTER_WIDE(vsrc1));
+ SET_REGISTER_WIDE(vdst, GET_REGISTER_WIDE(vsrc1));
+ FINISH(2);
+OP_END
+
+/* File: c/OP_MOVE_WIDE_16.c */
+HANDLE_OPCODE(OP_MOVE_WIDE_16 /*vAAAA, vBBBB*/)
+ vdst = FETCH(1);
+ vsrc1 = FETCH(2);
+ ILOGV("|move-wide/16 v%d,v%d %s(v%d=0x%08llx)", vdst, vsrc1,
+ kSpacing+8, vdst, GET_REGISTER_WIDE(vsrc1));
+ SET_REGISTER_WIDE(vdst, GET_REGISTER_WIDE(vsrc1));
+ FINISH(3);
+OP_END
+
+/* File: c/OP_MOVE_OBJECT.c */
+/* File: c/OP_MOVE.c */
+HANDLE_OPCODE(OP_MOVE_OBJECT /*vA, vB*/)
+ vdst = INST_A(inst);
+ vsrc1 = INST_B(inst);
+ ILOGV("|move%s v%d,v%d %s(v%d=0x%08x)",
+ (INST_INST(inst) == OP_MOVE) ? "" : "-object", vdst, vsrc1,
+ kSpacing, vdst, GET_REGISTER(vsrc1));
+ SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+ FINISH(1);
+OP_END
+
+
+/* File: c/OP_MOVE_OBJECT_FROM16.c */
+/* File: c/OP_MOVE_FROM16.c */
+HANDLE_OPCODE(OP_MOVE_OBJECT_FROM16 /*vAA, vBBBB*/)
+ vdst = INST_AA(inst);
+ vsrc1 = FETCH(1);
+ ILOGV("|move%s/from16 v%d,v%d %s(v%d=0x%08x)",
+ (INST_INST(inst) == OP_MOVE_FROM16) ? "" : "-object", vdst, vsrc1,
+ kSpacing, vdst, GET_REGISTER(vsrc1));
+ SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+ FINISH(2);
+OP_END
+
+
+/* File: c/OP_MOVE_OBJECT_16.c */
+/* File: c/OP_MOVE_16.c */
+HANDLE_OPCODE(OP_MOVE_OBJECT_16 /*vAAAA, vBBBB*/)
+ vdst = FETCH(1);
+ vsrc1 = FETCH(2);
+ ILOGV("|move%s/16 v%d,v%d %s(v%d=0x%08x)",
+ (INST_INST(inst) == OP_MOVE_16) ? "" : "-object", vdst, vsrc1,
+ kSpacing, vdst, GET_REGISTER(vsrc1));
+ SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+ FINISH(3);
+OP_END
+
+
+/* File: c/OP_MOVE_RESULT.c */
+HANDLE_OPCODE(OP_MOVE_RESULT /*vAA*/)
+ vdst = INST_AA(inst);
+ ILOGV("|move-result%s v%d %s(v%d=0x%08x)",
+ (INST_INST(inst) == OP_MOVE_RESULT) ? "" : "-object",
+ vdst, kSpacing+4, vdst,retval.i);
+ SET_REGISTER(vdst, retval.i);
+ FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE_RESULT_WIDE.c */
+HANDLE_OPCODE(OP_MOVE_RESULT_WIDE /*vAA*/)
+ vdst = INST_AA(inst);
+ ILOGV("|move-result-wide v%d %s(0x%08llx)", vdst, kSpacing, retval.j);
+ SET_REGISTER_WIDE(vdst, retval.j);
+ FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE_RESULT_OBJECT.c */
+/* File: c/OP_MOVE_RESULT.c */
+HANDLE_OPCODE(OP_MOVE_RESULT_OBJECT /*vAA*/)
+ vdst = INST_AA(inst);
+ ILOGV("|move-result%s v%d %s(v%d=0x%08x)",
+ (INST_INST(inst) == OP_MOVE_RESULT) ? "" : "-object",
+ vdst, kSpacing+4, vdst,retval.i);
+ SET_REGISTER(vdst, retval.i);
+ FINISH(1);
+OP_END
+
+
+/* File: c/OP_MOVE_EXCEPTION.c */
+HANDLE_OPCODE(OP_MOVE_EXCEPTION /*vAA*/)
+ vdst = INST_AA(inst);
+ ILOGV("|move-exception v%d", vdst);
+ assert(self->exception != NULL);
+ SET_REGISTER(vdst, (u4)self->exception);
+ dvmClearException(self);
+ FINISH(1);
+OP_END
+
+/* File: c/OP_RETURN_VOID.c */
+HANDLE_OPCODE(OP_RETURN_VOID /**/)
+ ILOGV("|return-void");
+#ifndef NDEBUG
+ retval.j = 0xababababULL; // placate valgrind
+#endif
+ GOTO_returnFromMethod();
+OP_END
+
+/* File: c/OP_RETURN.c */
+HANDLE_OPCODE(OP_RETURN /*vAA*/)
+ vsrc1 = INST_AA(inst);
+ ILOGV("|return%s v%d",
+ (INST_INST(inst) == OP_RETURN) ? "" : "-object", vsrc1);
+ retval.i = GET_REGISTER(vsrc1);
+ GOTO_returnFromMethod();
+OP_END
+
+/* File: c/OP_RETURN_WIDE.c */
+HANDLE_OPCODE(OP_RETURN_WIDE /*vAA*/)
+ vsrc1 = INST_AA(inst);
+ ILOGV("|return-wide v%d", vsrc1);
+ retval.j = GET_REGISTER_WIDE(vsrc1);
+ GOTO_returnFromMethod();
+OP_END
+
+/* File: c/OP_RETURN_OBJECT.c */
+/* File: c/OP_RETURN.c */
+HANDLE_OPCODE(OP_RETURN_OBJECT /*vAA*/)
+ vsrc1 = INST_AA(inst);
+ ILOGV("|return%s v%d",
+ (INST_INST(inst) == OP_RETURN) ? "" : "-object", vsrc1);
+ retval.i = GET_REGISTER(vsrc1);
+ GOTO_returnFromMethod();
+OP_END
+
+
+/* File: c/OP_CONST_4.c */
+HANDLE_OPCODE(OP_CONST_4 /*vA, #+B*/)
+ {
+ s4 tmp;
+
+ vdst = INST_A(inst);
+ tmp = (s4) (INST_B(inst) << 28) >> 28; // sign extend 4-bit value
+ ILOGV("|const/4 v%d,#0x%02x", vdst, (s4)tmp);
+ SET_REGISTER(vdst, tmp);
+ }
+ FINISH(1);
+OP_END
+
+/* File: c/OP_CONST_16.c */
+HANDLE_OPCODE(OP_CONST_16 /*vAA, #+BBBB*/)
+ vdst = INST_AA(inst);
+ vsrc1 = FETCH(1);
+ ILOGV("|const/16 v%d,#0x%04x", vdst, (s2)vsrc1);
+ SET_REGISTER(vdst, (s2) vsrc1);
+ FINISH(2);
+OP_END
+
+/* File: c/OP_CONST.c */
+HANDLE_OPCODE(OP_CONST /*vAA, #+BBBBBBBB*/)
+ {
+ u4 tmp;
+
+ vdst = INST_AA(inst);
+ tmp = FETCH(1);
+ tmp |= (u4)FETCH(2) << 16;
+ ILOGV("|const v%d,#0x%08x", vdst, tmp);
+ SET_REGISTER(vdst, tmp);
+ }
+ FINISH(3);
+OP_END
+
+/* File: c/OP_CONST_HIGH16.c */
+HANDLE_OPCODE(OP_CONST_HIGH16 /*vAA, #+BBBB0000*/)
+ vdst = INST_AA(inst);
+ vsrc1 = FETCH(1);
+ ILOGV("|const/high16 v%d,#0x%04x0000", vdst, vsrc1);
+ SET_REGISTER(vdst, vsrc1 << 16);
+ FINISH(2);
+OP_END
+
+/* File: c/OP_CONST_WIDE_16.c */
+HANDLE_OPCODE(OP_CONST_WIDE_16 /*vAA, #+BBBB*/)
+ vdst = INST_AA(inst);
+ vsrc1 = FETCH(1);
+ ILOGV("|const-wide/16 v%d,#0x%04x", vdst, (s2)vsrc1);
+ SET_REGISTER_WIDE(vdst, (s2)vsrc1);
+ FINISH(2);
+OP_END
+
+/* File: c/OP_CONST_WIDE_32.c */
+HANDLE_OPCODE(OP_CONST_WIDE_32 /*vAA, #+BBBBBBBB*/)
+ {
+ u4 tmp;
+
+ vdst = INST_AA(inst);
+ tmp = FETCH(1);
+ tmp |= (u4)FETCH(2) << 16;
+ ILOGV("|const-wide/32 v%d,#0x%08x", vdst, tmp);
+ SET_REGISTER_WIDE(vdst, (s4) tmp);
+ }
+ FINISH(3);
+OP_END
+
+/* File: c/OP_CONST_WIDE.c */
+HANDLE_OPCODE(OP_CONST_WIDE /*vAA, #+BBBBBBBBBBBBBBBB*/)
+ {
+ u8 tmp;
+
+ vdst = INST_AA(inst);
+ tmp = FETCH(1);
+ tmp |= (u8)FETCH(2) << 16;
+ tmp |= (u8)FETCH(3) << 32;
+ tmp |= (u8)FETCH(4) << 48;
+ ILOGV("|const-wide v%d,#0x%08llx", vdst, tmp);
+ SET_REGISTER_WIDE(vdst, tmp);
+ }
+ FINISH(5);
+OP_END
+
+/* File: c/OP_CONST_WIDE_HIGH16.c */
+HANDLE_OPCODE(OP_CONST_WIDE_HIGH16 /*vAA, #+BBBB000000000000*/)
+ vdst = INST_AA(inst);
+ vsrc1 = FETCH(1);
+ ILOGV("|const-wide/high16 v%d,#0x%04x000000000000", vdst, vsrc1);
+ SET_REGISTER_WIDE(vdst, ((u8) vsrc1) << 48);
+ FINISH(2);
+OP_END
+
+/* File: c/OP_CONST_STRING.c */
+HANDLE_OPCODE(OP_CONST_STRING /*vAA, string@BBBB*/)
+ {
+ StringObject* strObj;
+
+ vdst = INST_AA(inst);
+ ref = FETCH(1);
+ ILOGV("|const-string v%d string@0x%04x", vdst, ref);
+ strObj = dvmDexGetResolvedString(methodClassDex, ref);
+ if (strObj == NULL) {
+ EXPORT_PC();
+ strObj = dvmResolveString(curMethod->clazz, ref);
+ if (strObj == NULL)
+ GOTO_exceptionThrown();
+ }
+ SET_REGISTER(vdst, (u4) strObj);
+ }
+ FINISH(2);
+OP_END
+
+/* File: c/OP_CONST_STRING_JUMBO.c */
+HANDLE_OPCODE(OP_CONST_STRING_JUMBO /*vAA, string@BBBBBBBB*/)
+ {
+ StringObject* strObj;
+ u4 tmp;
+
+ vdst = INST_AA(inst);
+ tmp = FETCH(1);
+ tmp |= (u4)FETCH(2) << 16;
+ ILOGV("|const-string/jumbo v%d string@0x%08x", vdst, tmp);
+ strObj = dvmDexGetResolvedString(methodClassDex, tmp);
+ if (strObj == NULL) {
+ EXPORT_PC();
+ strObj = dvmResolveString(curMethod->clazz, tmp);
+ if (strObj == NULL)
+ GOTO_exceptionThrown();
+ }
+ SET_REGISTER(vdst, (u4) strObj);
+ }
+ FINISH(3);
+OP_END
+
+/* File: c/OP_CONST_CLASS.c */
+HANDLE_OPCODE(OP_CONST_CLASS /*vAA, class@BBBB*/)
+ {
+ ClassObject* clazz;
+
+ vdst = INST_AA(inst);
+ ref = FETCH(1);
+ ILOGV("|const-class v%d class@0x%04x", vdst, ref);
+ clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+ if (clazz == NULL) {
+ EXPORT_PC();
+ clazz = dvmResolveClass(curMethod->clazz, ref, true);
+ if (clazz == NULL)
+ GOTO_exceptionThrown();
+ }
+ SET_REGISTER(vdst, (u4) clazz);
+ }
+ FINISH(2);
+OP_END
+
+/* File: c/OP_MONITOR_ENTER.c */
+HANDLE_OPCODE(OP_MONITOR_ENTER /*vAA*/)
+ {
+ Object* obj;
+
+ vsrc1 = INST_AA(inst);
+ ILOGV("|monitor-enter v%d %s(0x%08x)",
+ vsrc1, kSpacing+6, GET_REGISTER(vsrc1));
+ obj = (Object*)GET_REGISTER(vsrc1);
+ if (!checkForNullExportPC(obj, fp, pc))
+ GOTO_exceptionThrown();
+ ILOGV("+ locking %p %s\n", obj, obj->clazz->descriptor);
+ EXPORT_PC(); /* need for precise GC, also WITH_MONITOR_TRACKING */
+ dvmLockObject(self, obj);
+#ifdef WITH_DEADLOCK_PREDICTION
+ if (dvmCheckException(self))
+ GOTO_exceptionThrown();
+#endif
+ }
+ FINISH(1);
+OP_END
+
+/* File: c/OP_MONITOR_EXIT.c */
+HANDLE_OPCODE(OP_MONITOR_EXIT /*vAA*/)
+ {
+ Object* obj;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst);
+ ILOGV("|monitor-exit v%d %s(0x%08x)",
+ vsrc1, kSpacing+5, GET_REGISTER(vsrc1));
+ obj = (Object*)GET_REGISTER(vsrc1);
+ if (!checkForNull(obj)) {
+ /*
+ * The exception needs to be processed at the *following*
+ * instruction, not the current instruction (see the Dalvik
+ * spec). Because we're jumping to an exception handler,
+ * we're not actually at risk of skipping an instruction
+ * by doing so.
+ */
+ ADJUST_PC(1); /* monitor-exit width is 1 */
+ GOTO_exceptionThrown();
+ }
+ ILOGV("+ unlocking %p %s\n", obj, obj->clazz->descriptor);
+ if (!dvmUnlockObject(self, obj)) {
+ assert(dvmCheckException(self));
+ ADJUST_PC(1);
+ GOTO_exceptionThrown();
+ }
+ }
+ FINISH(1);
+OP_END
+
+/* File: c/OP_CHECK_CAST.c */
+HANDLE_OPCODE(OP_CHECK_CAST /*vAA, class@BBBB*/)
+ {
+ ClassObject* clazz;
+ Object* obj;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst);
+ ref = FETCH(1); /* class to check against */
+ ILOGV("|check-cast v%d,class@0x%04x", vsrc1, ref);
+
+ obj = (Object*)GET_REGISTER(vsrc1);
+ if (obj != NULL) {
+#if defined(WITH_EXTRA_OBJECT_VALIDATION)
+ if (!checkForNull(obj))
+ GOTO_exceptionThrown();
+#endif
+ clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+ if (clazz == NULL) {
+ clazz = dvmResolveClass(curMethod->clazz, ref, false);
+ if (clazz == NULL)
+ GOTO_exceptionThrown();
+ }
+ if (!dvmInstanceof(obj->clazz, clazz)) {
+ dvmThrowExceptionWithClassMessage(
+ "Ljava/lang/ClassCastException;", obj->clazz->descriptor);
+ GOTO_exceptionThrown();
+ }
+ }
+ }
+ FINISH(2);
+OP_END
+
+/* File: c/OP_INSTANCE_OF.c */
+HANDLE_OPCODE(OP_INSTANCE_OF /*vA, vB, class@CCCC*/)
+ {
+ ClassObject* clazz;
+ Object* obj;
+
+ vdst = INST_A(inst);
+ vsrc1 = INST_B(inst); /* object to check */
+ ref = FETCH(1); /* class to check against */
+ ILOGV("|instance-of v%d,v%d,class@0x%04x", vdst, vsrc1, ref);
+
+ obj = (Object*)GET_REGISTER(vsrc1);
+ if (obj == NULL) {
+ SET_REGISTER(vdst, 0);
+ } else {
+#if defined(WITH_EXTRA_OBJECT_VALIDATION)
+ if (!checkForNullExportPC(obj, fp, pc))
+ GOTO_exceptionThrown();
+#endif
+ clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+ if (clazz == NULL) {
+ EXPORT_PC();
+ clazz = dvmResolveClass(curMethod->clazz, ref, true);
+ if (clazz == NULL)
+ GOTO_exceptionThrown();
+ }
+ SET_REGISTER(vdst, dvmInstanceof(obj->clazz, clazz));
+ }
+ }
+ FINISH(2);
+OP_END
+
+/* File: c/OP_ARRAY_LENGTH.c */
+HANDLE_OPCODE(OP_ARRAY_LENGTH /*vA, vB*/)
+ {
+ ArrayObject* arrayObj;
+
+ vdst = INST_A(inst);
+ vsrc1 = INST_B(inst);
+ arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);
+ ILOGV("|array-length v%d,v%d (%p)", vdst, vsrc1, arrayObj);
+ if (!checkForNullExportPC((Object*) arrayObj, fp, pc))
+ GOTO_exceptionThrown();
+ /* verifier guarantees this is an array reference */
+ SET_REGISTER(vdst, arrayObj->length);
+ }
+ FINISH(1);
+OP_END
+
+/* File: c/OP_NEW_INSTANCE.c */
+HANDLE_OPCODE(OP_NEW_INSTANCE /*vAA, class@BBBB*/)
+ {
+ ClassObject* clazz;
+ Object* newObj;
+
+ EXPORT_PC();
+
+ vdst = INST_AA(inst);
+ ref = FETCH(1);
+ ILOGV("|new-instance v%d,class@0x%04x", vdst, ref);
+ clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+ if (clazz == NULL) {
+ clazz = dvmResolveClass(curMethod->clazz, ref, false);
+ if (clazz == NULL)
+ GOTO_exceptionThrown();
+ }
+
+ if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))
+ GOTO_exceptionThrown();
+
+ /*
+ * The JIT needs dvmDexGetResolvedClass() to return non-null.
+ * Since we use the portable interpreter to build the trace, this extra
+ * check is not needed for mterp.
+ */
+ if (!dvmDexGetResolvedClass(methodClassDex, ref)) {
+ /* Class initialization is still ongoing - abandon the trace */
+ ABORT_JIT_TSELECT();
+ }
+
+ /*
+ * Verifier now tests for interface/abstract class.
+ */
+ //if (dvmIsInterfaceClass(clazz) || dvmIsAbstractClass(clazz)) {
+ // dvmThrowExceptionWithClassMessage("Ljava/lang/InstantiationError;",
+ // clazz->descriptor);
+ // GOTO_exceptionThrown();
+ //}
+ newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+ if (newObj == NULL)
+ GOTO_exceptionThrown();
+ SET_REGISTER(vdst, (u4) newObj);
+ }
+ FINISH(2);
+OP_END
+
+/* File: c/OP_NEW_ARRAY.c */
+HANDLE_OPCODE(OP_NEW_ARRAY /*vA, vB, class@CCCC*/)
+ {
+ ClassObject* arrayClass;
+ ArrayObject* newArray;
+ s4 length;
+
+ EXPORT_PC();
+
+ vdst = INST_A(inst);
+ vsrc1 = INST_B(inst); /* length reg */
+ ref = FETCH(1);
+ ILOGV("|new-array v%d,v%d,class@0x%04x (%d elements)",
+ vdst, vsrc1, ref, (s4) GET_REGISTER(vsrc1));
+ length = (s4) GET_REGISTER(vsrc1);
+ if (length < 0) {
+ dvmThrowException("Ljava/lang/NegativeArraySizeException;", NULL);
+ GOTO_exceptionThrown();
+ }
+ arrayClass = dvmDexGetResolvedClass(methodClassDex, ref);
+ if (arrayClass == NULL) {
+ arrayClass = dvmResolveClass(curMethod->clazz, ref, false);
+ if (arrayClass == NULL)
+ GOTO_exceptionThrown();
+ }
+ /* verifier guarantees this is an array class */
+ assert(dvmIsArrayClass(arrayClass));
+ assert(dvmIsClassInitialized(arrayClass));
+
+ newArray = dvmAllocArrayByClass(arrayClass, length, ALLOC_DONT_TRACK);
+ if (newArray == NULL)
+ GOTO_exceptionThrown();
+ SET_REGISTER(vdst, (u4) newArray);
+ }
+ FINISH(2);
+OP_END
+
+/* File: c/OP_FILLED_NEW_ARRAY.c */
+HANDLE_OPCODE(OP_FILLED_NEW_ARRAY /*vB, {vD, vE, vF, vG, vA}, class@CCCC*/)
+ GOTO_invoke(filledNewArray, false);
+OP_END
+
+/* File: c/OP_FILLED_NEW_ARRAY_RANGE.c */
+HANDLE_OPCODE(OP_FILLED_NEW_ARRAY_RANGE /*{vCCCC..v(CCCC+AA-1)}, class@BBBB*/)
+ GOTO_invoke(filledNewArray, true);
+OP_END
+
+/* File: c/OP_FILL_ARRAY_DATA.c */
+HANDLE_OPCODE(OP_FILL_ARRAY_DATA) /*vAA, +BBBBBBBB*/
+ {
+ const u2* arrayData;
+ s4 offset;
+ ArrayObject* arrayObj;
+
+ EXPORT_PC();
+ vsrc1 = INST_AA(inst);
+ offset = FETCH(1) | (((s4) FETCH(2)) << 16);
+ ILOGV("|fill-array-data v%d +0x%04x", vsrc1, offset);
+ arrayData = pc + offset; // offset in 16-bit units
+#ifndef NDEBUG
+ if (arrayData < curMethod->insns ||
+ arrayData >= curMethod->insns + dvmGetMethodInsnsSize(curMethod))
+ {
+ /* should have been caught in verifier */
+ dvmThrowException("Ljava/lang/InternalError;",
+ "bad fill array data");
+ GOTO_exceptionThrown();
+ }
+#endif
+ arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);
+ if (!dvmInterpHandleFillArrayData(arrayObj, arrayData)) {
+ GOTO_exceptionThrown();
+ }
+ FINISH(3);
+ }
+OP_END
+
+/* File: c/OP_THROW.c */
+HANDLE_OPCODE(OP_THROW /*vAA*/)
+ {
+ Object* obj;
+
+ /*
+ * We don't create an exception here, but the process of searching
+ * for a catch block can do class lookups and throw exceptions.
+ * We need to update the saved PC.
+ */
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst);
+ ILOGV("|throw v%d (%p)", vsrc1, (void*)GET_REGISTER(vsrc1));
+ obj = (Object*) GET_REGISTER(vsrc1);
+ if (!checkForNull(obj)) {
+ /* will throw a null pointer exception */
+ LOGVV("Bad exception\n");
+ } else {
+ /* use the requested exception */
+ dvmSetException(self, obj);
+ }
+ GOTO_exceptionThrown();
+ }
+OP_END
+
+/* File: c/OP_GOTO.c */
+HANDLE_OPCODE(OP_GOTO /*+AA*/)
+ vdst = INST_AA(inst);
+ if ((s1)vdst < 0)
+ ILOGV("|goto -0x%02x", -((s1)vdst));
+ else
+ ILOGV("|goto +0x%02x", ((s1)vdst));
+ ILOGV("> branch taken");
+ if ((s1)vdst < 0)
+ PERIODIC_CHECKS(kInterpEntryInstr, (s1)vdst);
+ FINISH((s1)vdst);
+OP_END
+
+/* File: c/OP_GOTO_16.c */
+HANDLE_OPCODE(OP_GOTO_16 /*+AAAA*/)
+ {
+ s4 offset = (s2) FETCH(1); /* sign-extend next code unit */
+
+ if (offset < 0)
+ ILOGV("|goto/16 -0x%04x", -offset);
+ else
+ ILOGV("|goto/16 +0x%04x", offset);
+ ILOGV("> branch taken");
+ if (offset < 0)
+ PERIODIC_CHECKS(kInterpEntryInstr, offset);
+ FINISH(offset);
+ }
+OP_END
+
+/* File: c/OP_GOTO_32.c */
+HANDLE_OPCODE(OP_GOTO_32 /*+AAAAAAAA*/)
+ {
+ s4 offset = FETCH(1); /* low-order 16 bits */
+ offset |= ((s4) FETCH(2)) << 16; /* high-order 16 bits */
+
+ if (offset < 0)
+ ILOGV("|goto/32 -0x%08x", -offset);
+ else
+ ILOGV("|goto/32 +0x%08x", offset);
+ ILOGV("> branch taken");
+ if (offset <= 0) /* allowed to branch to self */
+ PERIODIC_CHECKS(kInterpEntryInstr, offset);
+ FINISH(offset);
+ }
+OP_END
+
+/* File: c/OP_PACKED_SWITCH.c */
+HANDLE_OPCODE(OP_PACKED_SWITCH /*vAA, +BBBB*/)
+ {
+ const u2* switchData;
+ u4 testVal;
+ s4 offset;
+
+ vsrc1 = INST_AA(inst);
+ offset = FETCH(1) | (((s4) FETCH(2)) << 16);
+ ILOGV("|packed-switch v%d +0x%04x", vsrc1, vsrc2);
+ switchData = pc + offset; // offset in 16-bit units
+#ifndef NDEBUG
+ if (switchData < curMethod->insns ||
+ switchData >= curMethod->insns + dvmGetMethodInsnsSize(curMethod))
+ {
+ /* should have been caught in verifier */
+ EXPORT_PC();
+ dvmThrowException("Ljava/lang/InternalError;", "bad packed switch");
+ GOTO_exceptionThrown();
+ }
+#endif
+ testVal = GET_REGISTER(vsrc1);
+
+ offset = dvmInterpHandlePackedSwitch(switchData, testVal);
+ ILOGV("> branch taken (0x%04x)\n", offset);
+ if (offset <= 0) /* uncommon */
+ PERIODIC_CHECKS(kInterpEntryInstr, offset);
+ FINISH(offset);
+ }
+OP_END
+
+/* File: c/OP_SPARSE_SWITCH.c */
+HANDLE_OPCODE(OP_SPARSE_SWITCH /*vAA, +BBBB*/)
+ {
+ const u2* switchData;
+ u4 testVal;
+ s4 offset;
+
+ vsrc1 = INST_AA(inst);
+ offset = FETCH(1) | (((s4) FETCH(2)) << 16);
+ ILOGV("|sparse-switch v%d +0x%04x", vsrc1, vsrc2);
+ switchData = pc + offset; // offset in 16-bit units
+#ifndef NDEBUG
+ if (switchData < curMethod->insns ||
+ switchData >= curMethod->insns + dvmGetMethodInsnsSize(curMethod))
+ {
+ /* should have been caught in verifier */
+ EXPORT_PC();
+ dvmThrowException("Ljava/lang/InternalError;", "bad sparse switch");
+ GOTO_exceptionThrown();
+ }
+#endif
+ testVal = GET_REGISTER(vsrc1);
+
+ offset = dvmInterpHandleSparseSwitch(switchData, testVal);
+ ILOGV("> branch taken (0x%04x)\n", offset);
+ if (offset <= 0) /* uncommon */
+ PERIODIC_CHECKS(kInterpEntryInstr, offset);
+ FINISH(offset);
+ }
+OP_END
+
+/* File: c/OP_CMPL_FLOAT.c */
+HANDLE_OP_CMPX(OP_CMPL_FLOAT, "l-float", float, _FLOAT, -1)
+OP_END
+
+/* File: c/OP_CMPG_FLOAT.c */
+HANDLE_OP_CMPX(OP_CMPG_FLOAT, "g-float", float, _FLOAT, 1)
+OP_END
+
+/* File: c/OP_CMPL_DOUBLE.c */
+HANDLE_OP_CMPX(OP_CMPL_DOUBLE, "l-double", double, _DOUBLE, -1)
+OP_END
+
+/* File: c/OP_CMPG_DOUBLE.c */
+HANDLE_OP_CMPX(OP_CMPG_DOUBLE, "g-double", double, _DOUBLE, 1)
+OP_END
+
+/* File: c/OP_CMP_LONG.c */
+HANDLE_OP_CMPX(OP_CMP_LONG, "-long", s8, _WIDE, 0)
+OP_END
+
+/* File: c/OP_IF_EQ.c */
+HANDLE_OP_IF_XX(OP_IF_EQ, "eq", ==)
+OP_END
+
+/* File: c/OP_IF_NE.c */
+HANDLE_OP_IF_XX(OP_IF_NE, "ne", !=)
+OP_END
+
+/* File: c/OP_IF_LT.c */
+HANDLE_OP_IF_XX(OP_IF_LT, "lt", <)
+OP_END
+
+/* File: c/OP_IF_GE.c */
+HANDLE_OP_IF_XX(OP_IF_GE, "ge", >=)
+OP_END
+
+/* File: c/OP_IF_GT.c */
+HANDLE_OP_IF_XX(OP_IF_GT, "gt", >)
+OP_END
+
+/* File: c/OP_IF_LE.c */
+HANDLE_OP_IF_XX(OP_IF_LE, "le", <=)
+OP_END
+
+/* File: c/OP_IF_EQZ.c */
+HANDLE_OP_IF_XXZ(OP_IF_EQZ, "eqz", ==)
+OP_END
+
+/* File: c/OP_IF_NEZ.c */
+HANDLE_OP_IF_XXZ(OP_IF_NEZ, "nez", !=)
+OP_END
+
+/* File: c/OP_IF_LTZ.c */
+HANDLE_OP_IF_XXZ(OP_IF_LTZ, "ltz", <)
+OP_END
+
+/* File: c/OP_IF_GEZ.c */
+HANDLE_OP_IF_XXZ(OP_IF_GEZ, "gez", >=)
+OP_END
+
+/* File: c/OP_IF_GTZ.c */
+HANDLE_OP_IF_XXZ(OP_IF_GTZ, "gtz", >)
+OP_END
+
+/* File: c/OP_IF_LEZ.c */
+HANDLE_OP_IF_XXZ(OP_IF_LEZ, "lez", <=)
+OP_END
+
+/* File: c/OP_UNUSED_3E.c */
+HANDLE_OPCODE(OP_UNUSED_3E)
+OP_END
+
+/* File: c/OP_UNUSED_3F.c */
+HANDLE_OPCODE(OP_UNUSED_3F)
+OP_END
+
+/* File: c/OP_UNUSED_40.c */
+HANDLE_OPCODE(OP_UNUSED_40)
+OP_END
+
+/* File: c/OP_UNUSED_41.c */
+HANDLE_OPCODE(OP_UNUSED_41)
+OP_END
+
+/* File: c/OP_UNUSED_42.c */
+HANDLE_OPCODE(OP_UNUSED_42)
+OP_END
+
+/* File: c/OP_UNUSED_43.c */
+HANDLE_OPCODE(OP_UNUSED_43)
+OP_END
+
+/* File: c/OP_AGET.c */
+HANDLE_OP_AGET(OP_AGET, "", u4, )
+OP_END
+
+/* File: c/OP_AGET_WIDE.c */
+HANDLE_OP_AGET(OP_AGET_WIDE, "-wide", s8, _WIDE)
+OP_END
+
+/* File: c/OP_AGET_OBJECT.c */
+HANDLE_OP_AGET(OP_AGET_OBJECT, "-object", u4, )
+OP_END
+
+/* File: c/OP_AGET_BOOLEAN.c */
+HANDLE_OP_AGET(OP_AGET_BOOLEAN, "-boolean", u1, )
+OP_END
+
+/* File: c/OP_AGET_BYTE.c */
+HANDLE_OP_AGET(OP_AGET_BYTE, "-byte", s1, )
+OP_END
+
+/* File: c/OP_AGET_CHAR.c */
+HANDLE_OP_AGET(OP_AGET_CHAR, "-char", u2, )
+OP_END
+
+/* File: c/OP_AGET_SHORT.c */
+HANDLE_OP_AGET(OP_AGET_SHORT, "-short", s2, )
+OP_END
+
+/* File: c/OP_APUT.c */
+HANDLE_OP_APUT(OP_APUT, "", u4, )
+OP_END
+
+/* File: c/OP_APUT_WIDE.c */
+HANDLE_OP_APUT(OP_APUT_WIDE, "-wide", s8, _WIDE)
+OP_END
+
+/* File: c/OP_APUT_OBJECT.c */
+HANDLE_OPCODE(OP_APUT_OBJECT /*vAA, vBB, vCC*/)
+ {
+ ArrayObject* arrayObj;
+ Object* obj;
+ u2 arrayInfo;
+ EXPORT_PC();
+ vdst = INST_AA(inst); /* AA: source value */
+ arrayInfo = FETCH(1);
+ vsrc1 = arrayInfo & 0xff; /* BB: array ptr */
+ vsrc2 = arrayInfo >> 8; /* CC: index */
+ ILOGV("|aput%s v%d,v%d,v%d", "-object", vdst, vsrc1, vsrc2);
+ arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);
+ if (!checkForNull((Object*) arrayObj))
+ GOTO_exceptionThrown();
+ if (GET_REGISTER(vsrc2) >= arrayObj->length) {
+ dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;",
+ NULL);
+ GOTO_exceptionThrown();
+ }
+ obj = (Object*) GET_REGISTER(vdst);
+ if (obj != NULL) {
+ if (!checkForNull(obj))
+ GOTO_exceptionThrown();
+ if (!dvmCanPutArrayElement(obj->clazz, arrayObj->obj.clazz)) {
+ LOGV("Can't put a '%s'(%p) into array type='%s'(%p)\n",
+ obj->clazz->descriptor, obj,
+ arrayObj->obj.clazz->descriptor, arrayObj);
+ //dvmDumpClass(obj->clazz);
+ //dvmDumpClass(arrayObj->obj.clazz);
+ dvmThrowException("Ljava/lang/ArrayStoreException;", NULL);
+ GOTO_exceptionThrown();
+ }
+ }
+ ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));
+ dvmSetObjectArrayElement(arrayObj,
+ GET_REGISTER(vsrc2),
+ (Object *)GET_REGISTER(vdst));
+ }
+ FINISH(2);
+OP_END
+
+/* File: c/OP_APUT_BOOLEAN.c */
+HANDLE_OP_APUT(OP_APUT_BOOLEAN, "-boolean", u1, )
+OP_END
+
+/* File: c/OP_APUT_BYTE.c */
+HANDLE_OP_APUT(OP_APUT_BYTE, "-byte", s1, )
+OP_END
+
+/* File: c/OP_APUT_CHAR.c */
+HANDLE_OP_APUT(OP_APUT_CHAR, "-char", u2, )
+OP_END
+
+/* File: c/OP_APUT_SHORT.c */
+HANDLE_OP_APUT(OP_APUT_SHORT, "-short", s2, )
+OP_END
+
+/* File: c/OP_IGET.c */
+HANDLE_IGET_X(OP_IGET, "", Int, )
+OP_END
+
+/* File: c/OP_IGET_WIDE.c */
+HANDLE_IGET_X(OP_IGET_WIDE, "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IGET_OBJECT.c */
+HANDLE_IGET_X(OP_IGET_OBJECT, "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IGET_BOOLEAN.c */
+HANDLE_IGET_X(OP_IGET_BOOLEAN, "", Int, )
+OP_END
+
+/* File: c/OP_IGET_BYTE.c */
+HANDLE_IGET_X(OP_IGET_BYTE, "", Int, )
+OP_END
+
+/* File: c/OP_IGET_CHAR.c */
+HANDLE_IGET_X(OP_IGET_CHAR, "", Int, )
+OP_END
+
+/* File: c/OP_IGET_SHORT.c */
+HANDLE_IGET_X(OP_IGET_SHORT, "", Int, )
+OP_END
+
+/* File: c/OP_IPUT.c */
+HANDLE_IPUT_X(OP_IPUT, "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_WIDE.c */
+HANDLE_IPUT_X(OP_IPUT_WIDE, "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IPUT_OBJECT.c */
+/*
+ * The VM spec says we should verify that the reference being stored into
+ * the field is assignment compatible. In practice, many popular VMs don't
+ * do this because it slows down a very common operation. It's not so bad
+ * for us, since "dexopt" quickens it whenever possible, but it's still an
+ * issue.
+ *
+ * To make this spec-complaint, we'd need to add a ClassObject pointer to
+ * the Field struct, resolve the field's type descriptor at link or class
+ * init time, and then verify the type here.
+ */
+HANDLE_IPUT_X(OP_IPUT_OBJECT, "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IPUT_BOOLEAN.c */
+HANDLE_IPUT_X(OP_IPUT_BOOLEAN, "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_BYTE.c */
+HANDLE_IPUT_X(OP_IPUT_BYTE, "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_CHAR.c */
+HANDLE_IPUT_X(OP_IPUT_CHAR, "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_SHORT.c */
+HANDLE_IPUT_X(OP_IPUT_SHORT, "", Int, )
+OP_END
+
+/* File: c/OP_SGET.c */
+HANDLE_SGET_X(OP_SGET, "", Int, )
+OP_END
+
+/* File: c/OP_SGET_WIDE.c */
+HANDLE_SGET_X(OP_SGET_WIDE, "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_SGET_OBJECT.c */
+HANDLE_SGET_X(OP_SGET_OBJECT, "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SGET_BOOLEAN.c */
+HANDLE_SGET_X(OP_SGET_BOOLEAN, "", Int, )
+OP_END
+
+/* File: c/OP_SGET_BYTE.c */
+HANDLE_SGET_X(OP_SGET_BYTE, "", Int, )
+OP_END
+
+/* File: c/OP_SGET_CHAR.c */
+HANDLE_SGET_X(OP_SGET_CHAR, "", Int, )
+OP_END
+
+/* File: c/OP_SGET_SHORT.c */
+HANDLE_SGET_X(OP_SGET_SHORT, "", Int, )
+OP_END
+
+/* File: c/OP_SPUT.c */
+HANDLE_SPUT_X(OP_SPUT, "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_WIDE.c */
+HANDLE_SPUT_X(OP_SPUT_WIDE, "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_SPUT_OBJECT.c */
+HANDLE_SPUT_X(OP_SPUT_OBJECT, "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SPUT_BOOLEAN.c */
+HANDLE_SPUT_X(OP_SPUT_BOOLEAN, "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_BYTE.c */
+HANDLE_SPUT_X(OP_SPUT_BYTE, "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_CHAR.c */
+HANDLE_SPUT_X(OP_SPUT_CHAR, "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_SHORT.c */
+HANDLE_SPUT_X(OP_SPUT_SHORT, "", Int, )
+OP_END
+
+/* File: c/OP_INVOKE_VIRTUAL.c */
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+ GOTO_invoke(invokeVirtual, false);
+OP_END
+
+/* File: c/OP_INVOKE_SUPER.c */
+HANDLE_OPCODE(OP_INVOKE_SUPER /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+ GOTO_invoke(invokeSuper, false);
+OP_END
+
+/* File: c/OP_INVOKE_DIRECT.c */
+HANDLE_OPCODE(OP_INVOKE_DIRECT /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+ GOTO_invoke(invokeDirect, false);
+OP_END
+
+/* File: c/OP_INVOKE_STATIC.c */
+HANDLE_OPCODE(OP_INVOKE_STATIC /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+ GOTO_invoke(invokeStatic, false);
+OP_END
+
+/* File: c/OP_INVOKE_INTERFACE.c */
+HANDLE_OPCODE(OP_INVOKE_INTERFACE /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+ GOTO_invoke(invokeInterface, false);
+OP_END
+
+/* File: c/OP_UNUSED_73.c */
+HANDLE_OPCODE(OP_UNUSED_73)
+OP_END
+
+/* File: c/OP_INVOKE_VIRTUAL_RANGE.c */
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+ GOTO_invoke(invokeVirtual, true);
+OP_END
+
+/* File: c/OP_INVOKE_SUPER_RANGE.c */
+HANDLE_OPCODE(OP_INVOKE_SUPER_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+ GOTO_invoke(invokeSuper, true);
+OP_END
+
+/* File: c/OP_INVOKE_DIRECT_RANGE.c */
+HANDLE_OPCODE(OP_INVOKE_DIRECT_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+ GOTO_invoke(invokeDirect, true);
+OP_END
+
+/* File: c/OP_INVOKE_STATIC_RANGE.c */
+HANDLE_OPCODE(OP_INVOKE_STATIC_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+ GOTO_invoke(invokeStatic, true);
+OP_END
+
+/* File: c/OP_INVOKE_INTERFACE_RANGE.c */
+HANDLE_OPCODE(OP_INVOKE_INTERFACE_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+ GOTO_invoke(invokeInterface, true);
+OP_END
+
+/* File: c/OP_UNUSED_79.c */
+HANDLE_OPCODE(OP_UNUSED_79)
+OP_END
+
+/* File: c/OP_UNUSED_7A.c */
+HANDLE_OPCODE(OP_UNUSED_7A)
+OP_END
+
+/* File: c/OP_NEG_INT.c */
+HANDLE_UNOP(OP_NEG_INT, "neg-int", -, , )
+OP_END
+
+/* File: c/OP_NOT_INT.c */
+HANDLE_UNOP(OP_NOT_INT, "not-int", , ^ 0xffffffff, )
+OP_END
+
+/* File: c/OP_NEG_LONG.c */
+HANDLE_UNOP(OP_NEG_LONG, "neg-long", -, , _WIDE)
+OP_END
+
+/* File: c/OP_NOT_LONG.c */
+HANDLE_UNOP(OP_NOT_LONG, "not-long", , ^ 0xffffffffffffffffULL, _WIDE)
+OP_END
+
+/* File: c/OP_NEG_FLOAT.c */
+HANDLE_UNOP(OP_NEG_FLOAT, "neg-float", -, , _FLOAT)
+OP_END
+
+/* File: c/OP_NEG_DOUBLE.c */
+HANDLE_UNOP(OP_NEG_DOUBLE, "neg-double", -, , _DOUBLE)
+OP_END
+
+/* File: c/OP_INT_TO_LONG.c */
+HANDLE_NUMCONV(OP_INT_TO_LONG, "int-to-long", _INT, _WIDE)
+OP_END
+
+/* File: c/OP_INT_TO_FLOAT.c */
+HANDLE_NUMCONV(OP_INT_TO_FLOAT, "int-to-float", _INT, _FLOAT)
+OP_END
+
+/* File: c/OP_INT_TO_DOUBLE.c */
+HANDLE_NUMCONV(OP_INT_TO_DOUBLE, "int-to-double", _INT, _DOUBLE)
+OP_END
+
+/* File: c/OP_LONG_TO_INT.c */
+HANDLE_NUMCONV(OP_LONG_TO_INT, "long-to-int", _WIDE, _INT)
+OP_END
+
+/* File: c/OP_LONG_TO_FLOAT.c */
+HANDLE_NUMCONV(OP_LONG_TO_FLOAT, "long-to-float", _WIDE, _FLOAT)
+OP_END
+
+/* File: c/OP_LONG_TO_DOUBLE.c */
+HANDLE_NUMCONV(OP_LONG_TO_DOUBLE, "long-to-double", _WIDE, _DOUBLE)
+OP_END
+
+/* File: c/OP_FLOAT_TO_INT.c */
+HANDLE_FLOAT_TO_INT(OP_FLOAT_TO_INT, "float-to-int",
+ float, _FLOAT, s4, _INT)
+OP_END
+
+/* File: c/OP_FLOAT_TO_LONG.c */
+HANDLE_FLOAT_TO_INT(OP_FLOAT_TO_LONG, "float-to-long",
+ float, _FLOAT, s8, _WIDE)
+OP_END
+
+/* File: c/OP_FLOAT_TO_DOUBLE.c */
+HANDLE_NUMCONV(OP_FLOAT_TO_DOUBLE, "float-to-double", _FLOAT, _DOUBLE)
+OP_END
+
+/* File: c/OP_DOUBLE_TO_INT.c */
+HANDLE_FLOAT_TO_INT(OP_DOUBLE_TO_INT, "double-to-int",
+ double, _DOUBLE, s4, _INT)
+OP_END
+
+/* File: c/OP_DOUBLE_TO_LONG.c */
+HANDLE_FLOAT_TO_INT(OP_DOUBLE_TO_LONG, "double-to-long",
+ double, _DOUBLE, s8, _WIDE)
+OP_END
+
+/* File: c/OP_DOUBLE_TO_FLOAT.c */
+HANDLE_NUMCONV(OP_DOUBLE_TO_FLOAT, "double-to-float", _DOUBLE, _FLOAT)
+OP_END
+
+/* File: c/OP_INT_TO_BYTE.c */
+HANDLE_INT_TO_SMALL(OP_INT_TO_BYTE, "byte", s1)
+OP_END
+
+/* File: c/OP_INT_TO_CHAR.c */
+HANDLE_INT_TO_SMALL(OP_INT_TO_CHAR, "char", u2)
+OP_END
+
+/* File: c/OP_INT_TO_SHORT.c */
+HANDLE_INT_TO_SMALL(OP_INT_TO_SHORT, "short", s2) /* want sign bit */
+OP_END
+
+/* File: c/OP_ADD_INT.c */
+HANDLE_OP_X_INT(OP_ADD_INT, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_INT.c */
+HANDLE_OP_X_INT(OP_SUB_INT, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_INT.c */
+HANDLE_OP_X_INT(OP_MUL_INT, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT.c */
+HANDLE_OP_X_INT(OP_DIV_INT, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT.c */
+HANDLE_OP_X_INT(OP_REM_INT, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT.c */
+HANDLE_OP_X_INT(OP_AND_INT, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT.c */
+HANDLE_OP_X_INT(OP_OR_INT, "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT.c */
+HANDLE_OP_X_INT(OP_XOR_INT, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_INT.c */
+HANDLE_OP_SHX_INT(OP_SHL_INT, "shl", (s4), <<)
+OP_END
+
+/* File: c/OP_SHR_INT.c */
+HANDLE_OP_SHX_INT(OP_SHR_INT, "shr", (s4), >>)
+OP_END
+
+/* File: c/OP_USHR_INT.c */
+HANDLE_OP_SHX_INT(OP_USHR_INT, "ushr", (u4), >>)
+OP_END
+
+/* File: c/OP_ADD_LONG.c */
+HANDLE_OP_X_LONG(OP_ADD_LONG, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_LONG.c */
+HANDLE_OP_X_LONG(OP_SUB_LONG, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_LONG.c */
+HANDLE_OP_X_LONG(OP_MUL_LONG, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_LONG.c */
+HANDLE_OP_X_LONG(OP_DIV_LONG, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_LONG.c */
+HANDLE_OP_X_LONG(OP_REM_LONG, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_LONG.c */
+HANDLE_OP_X_LONG(OP_AND_LONG, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_LONG.c */
+HANDLE_OP_X_LONG(OP_OR_LONG, "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_LONG.c */
+HANDLE_OP_X_LONG(OP_XOR_LONG, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_LONG.c */
+HANDLE_OP_SHX_LONG(OP_SHL_LONG, "shl", (s8), <<)
+OP_END
+
+/* File: c/OP_SHR_LONG.c */
+HANDLE_OP_SHX_LONG(OP_SHR_LONG, "shr", (s8), >>)
+OP_END
+
+/* File: c/OP_USHR_LONG.c */
+HANDLE_OP_SHX_LONG(OP_USHR_LONG, "ushr", (u8), >>)
+OP_END
+
+/* File: c/OP_ADD_FLOAT.c */
+HANDLE_OP_X_FLOAT(OP_ADD_FLOAT, "add", +)
+OP_END
+
+/* File: c/OP_SUB_FLOAT.c */
+HANDLE_OP_X_FLOAT(OP_SUB_FLOAT, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_FLOAT.c */
+HANDLE_OP_X_FLOAT(OP_MUL_FLOAT, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_FLOAT.c */
+HANDLE_OP_X_FLOAT(OP_DIV_FLOAT, "div", /)
+OP_END
+
+/* File: c/OP_REM_FLOAT.c */
+HANDLE_OPCODE(OP_REM_FLOAT /*vAA, vBB, vCC*/)
+ {
+ u2 srcRegs;
+ vdst = INST_AA(inst);
+ srcRegs = FETCH(1);
+ vsrc1 = srcRegs & 0xff;
+ vsrc2 = srcRegs >> 8;
+ ILOGV("|%s-float v%d,v%d,v%d", "mod", vdst, vsrc1, vsrc2);
+ SET_REGISTER_FLOAT(vdst,
+ fmodf(GET_REGISTER_FLOAT(vsrc1), GET_REGISTER_FLOAT(vsrc2)));
+ }
+ FINISH(2);
+OP_END
+
+/* File: c/OP_ADD_DOUBLE.c */
+HANDLE_OP_X_DOUBLE(OP_ADD_DOUBLE, "add", +)
+OP_END
+
+/* File: c/OP_SUB_DOUBLE.c */
+HANDLE_OP_X_DOUBLE(OP_SUB_DOUBLE, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_DOUBLE.c */
+HANDLE_OP_X_DOUBLE(OP_MUL_DOUBLE, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_DOUBLE.c */
+HANDLE_OP_X_DOUBLE(OP_DIV_DOUBLE, "div", /)
+OP_END
+
+/* File: c/OP_REM_DOUBLE.c */
+HANDLE_OPCODE(OP_REM_DOUBLE /*vAA, vBB, vCC*/)
+ {
+ u2 srcRegs;
+ vdst = INST_AA(inst);
+ srcRegs = FETCH(1);
+ vsrc1 = srcRegs & 0xff;
+ vsrc2 = srcRegs >> 8;
+ ILOGV("|%s-double v%d,v%d,v%d", "mod", vdst, vsrc1, vsrc2);
+ SET_REGISTER_DOUBLE(vdst,
+ fmod(GET_REGISTER_DOUBLE(vsrc1), GET_REGISTER_DOUBLE(vsrc2)));
+ }
+ FINISH(2);
+OP_END
+
+/* File: c/OP_ADD_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_ADD_INT_2ADDR, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_SUB_INT_2ADDR, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_MUL_INT_2ADDR, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_DIV_INT_2ADDR, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_REM_INT_2ADDR, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_AND_INT_2ADDR, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_OR_INT_2ADDR, "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_XOR_INT_2ADDR, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_INT_2ADDR.c */
+HANDLE_OP_SHX_INT_2ADDR(OP_SHL_INT_2ADDR, "shl", (s4), <<)
+OP_END
+
+/* File: c/OP_SHR_INT_2ADDR.c */
+HANDLE_OP_SHX_INT_2ADDR(OP_SHR_INT_2ADDR, "shr", (s4), >>)
+OP_END
+
+/* File: c/OP_USHR_INT_2ADDR.c */
+HANDLE_OP_SHX_INT_2ADDR(OP_USHR_INT_2ADDR, "ushr", (u4), >>)
+OP_END
+
+/* File: c/OP_ADD_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_ADD_LONG_2ADDR, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_SUB_LONG_2ADDR, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_MUL_LONG_2ADDR, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_DIV_LONG_2ADDR, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_REM_LONG_2ADDR, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_AND_LONG_2ADDR, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_OR_LONG_2ADDR, "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_XOR_LONG_2ADDR, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_LONG_2ADDR.c */
+HANDLE_OP_SHX_LONG_2ADDR(OP_SHL_LONG_2ADDR, "shl", (s8), <<)
+OP_END
+
+/* File: c/OP_SHR_LONG_2ADDR.c */
+HANDLE_OP_SHX_LONG_2ADDR(OP_SHR_LONG_2ADDR, "shr", (s8), >>)
+OP_END
+
+/* File: c/OP_USHR_LONG_2ADDR.c */
+HANDLE_OP_SHX_LONG_2ADDR(OP_USHR_LONG_2ADDR, "ushr", (u8), >>)
+OP_END
+
+/* File: c/OP_ADD_FLOAT_2ADDR.c */
+HANDLE_OP_X_FLOAT_2ADDR(OP_ADD_FLOAT_2ADDR, "add", +)
+OP_END
+
+/* File: c/OP_SUB_FLOAT_2ADDR.c */
+HANDLE_OP_X_FLOAT_2ADDR(OP_SUB_FLOAT_2ADDR, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_FLOAT_2ADDR.c */
+HANDLE_OP_X_FLOAT_2ADDR(OP_MUL_FLOAT_2ADDR, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_FLOAT_2ADDR.c */
+HANDLE_OP_X_FLOAT_2ADDR(OP_DIV_FLOAT_2ADDR, "div", /)
+OP_END
+
+/* File: c/OP_REM_FLOAT_2ADDR.c */
+HANDLE_OPCODE(OP_REM_FLOAT_2ADDR /*vA, vB*/)
+ vdst = INST_A(inst);
+ vsrc1 = INST_B(inst);
+ ILOGV("|%s-float-2addr v%d,v%d", "mod", vdst, vsrc1);
+ SET_REGISTER_FLOAT(vdst,
+ fmodf(GET_REGISTER_FLOAT(vdst), GET_REGISTER_FLOAT(vsrc1)));
+ FINISH(1);
+OP_END
+
+/* File: c/OP_ADD_DOUBLE_2ADDR.c */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_ADD_DOUBLE_2ADDR, "add", +)
+OP_END
+
+/* File: c/OP_SUB_DOUBLE_2ADDR.c */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_SUB_DOUBLE_2ADDR, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_DOUBLE_2ADDR.c */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_MUL_DOUBLE_2ADDR, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_DOUBLE_2ADDR.c */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_DIV_DOUBLE_2ADDR, "div", /)
+OP_END
+
+/* File: c/OP_REM_DOUBLE_2ADDR.c */
+HANDLE_OPCODE(OP_REM_DOUBLE_2ADDR /*vA, vB*/)
+ vdst = INST_A(inst);
+ vsrc1 = INST_B(inst);
+ ILOGV("|%s-double-2addr v%d,v%d", "mod", vdst, vsrc1);
+ SET_REGISTER_DOUBLE(vdst,
+ fmod(GET_REGISTER_DOUBLE(vdst), GET_REGISTER_DOUBLE(vsrc1)));
+ FINISH(1);
+OP_END
+
+/* File: c/OP_ADD_INT_LIT16.c */
+HANDLE_OP_X_INT_LIT16(OP_ADD_INT_LIT16, "add", +, 0)
+OP_END
+
+/* File: c/OP_RSUB_INT.c */
+HANDLE_OPCODE(OP_RSUB_INT /*vA, vB, #+CCCC*/)
+ {
+ vdst = INST_A(inst);
+ vsrc1 = INST_B(inst);
+ vsrc2 = FETCH(1);
+ ILOGV("|rsub-int v%d,v%d,#+0x%04x", vdst, vsrc1, vsrc2);
+ SET_REGISTER(vdst, (s2) vsrc2 - (s4) GET_REGISTER(vsrc1));
+ }
+ FINISH(2);
+OP_END
+
+/* File: c/OP_MUL_INT_LIT16.c */
+HANDLE_OP_X_INT_LIT16(OP_MUL_INT_LIT16, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT_LIT16.c */
+HANDLE_OP_X_INT_LIT16(OP_DIV_INT_LIT16, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT_LIT16.c */
+HANDLE_OP_X_INT_LIT16(OP_REM_INT_LIT16, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT_LIT16.c */
+HANDLE_OP_X_INT_LIT16(OP_AND_INT_LIT16, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT_LIT16.c */
+HANDLE_OP_X_INT_LIT16(OP_OR_INT_LIT16, "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT_LIT16.c */
+HANDLE_OP_X_INT_LIT16(OP_XOR_INT_LIT16, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_ADD_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_ADD_INT_LIT8, "add", +, 0)
+OP_END
+
+/* File: c/OP_RSUB_INT_LIT8.c */
+HANDLE_OPCODE(OP_RSUB_INT_LIT8 /*vAA, vBB, #+CC*/)
+ {
+ u2 litInfo;
+ vdst = INST_AA(inst);
+ litInfo = FETCH(1);
+ vsrc1 = litInfo & 0xff;
+ vsrc2 = litInfo >> 8;
+ ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x", "rsub", vdst, vsrc1, vsrc2);
+ SET_REGISTER(vdst, (s1) vsrc2 - (s4) GET_REGISTER(vsrc1));
+ }
+ FINISH(2);
+OP_END
+
+/* File: c/OP_MUL_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_MUL_INT_LIT8, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_DIV_INT_LIT8, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_REM_INT_LIT8, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_AND_INT_LIT8, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_OR_INT_LIT8, "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_XOR_INT_LIT8, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_INT_LIT8.c */
+HANDLE_OP_SHX_INT_LIT8(OP_SHL_INT_LIT8, "shl", (s4), <<)
+OP_END
+
+/* File: c/OP_SHR_INT_LIT8.c */
+HANDLE_OP_SHX_INT_LIT8(OP_SHR_INT_LIT8, "shr", (s4), >>)
+OP_END
+
+/* File: c/OP_USHR_INT_LIT8.c */
+HANDLE_OP_SHX_INT_LIT8(OP_USHR_INT_LIT8, "ushr", (u4), >>)
+OP_END
+
+/* File: c/OP_IGET_VOLATILE.c */
+HANDLE_IGET_X(OP_IGET_VOLATILE, "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_IPUT_VOLATILE.c */
+HANDLE_IPUT_X(OP_IPUT_VOLATILE, "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_SGET_VOLATILE.c */
+HANDLE_SGET_X(OP_SGET_VOLATILE, "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_SPUT_VOLATILE.c */
+HANDLE_SPUT_X(OP_SPUT_VOLATILE, "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_IGET_OBJECT_VOLATILE.c */
+HANDLE_IGET_X(OP_IGET_OBJECT_VOLATILE, "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IGET_WIDE_VOLATILE.c */
+HANDLE_IGET_X(OP_IGET_WIDE_VOLATILE, "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_IPUT_WIDE_VOLATILE.c */
+HANDLE_IPUT_X(OP_IPUT_WIDE_VOLATILE, "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_SGET_WIDE_VOLATILE.c */
+HANDLE_SGET_X(OP_SGET_WIDE_VOLATILE, "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_SPUT_WIDE_VOLATILE.c */
+HANDLE_SPUT_X(OP_SPUT_WIDE_VOLATILE, "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_BREAKPOINT.c */
+HANDLE_OPCODE(OP_BREAKPOINT)
+#if (INTERP_TYPE == INTERP_DBG)
+ {
+ /*
+ * Restart this instruction with the original opcode. We do
+ * this by simply jumping to the handler.
+ *
+ * It's probably not necessary to update "inst", but we do it
+ * for the sake of anything that needs to do disambiguation in a
+ * common handler with INST_INST.
+ *
+ * The breakpoint itself is handled over in updateDebugger(),
+ * because we need to detect other events (method entry, single
+ * step) and report them in the same event packet, and we're not
+ * yet handling those through breakpoint instructions. By the
+ * time we get here, the breakpoint has already been handled and
+ * the thread resumed.
+ */
+ u1 originalOpCode = dvmGetOriginalOpCode(pc);
+ LOGV("+++ break 0x%02x (0x%04x -> 0x%04x)\n", originalOpCode, inst,
+ INST_REPLACE_OP(inst, originalOpCode));
+ inst = INST_REPLACE_OP(inst, originalOpCode);
+ FINISH_BKPT(originalOpCode);
+ }
+#else
+ LOGE("Breakpoint hit in non-debug interpreter\n");
+ dvmAbort();
+#endif
+OP_END
+
+/* File: c/OP_THROW_VERIFICATION_ERROR.c */
+HANDLE_OPCODE(OP_THROW_VERIFICATION_ERROR)
+ EXPORT_PC();
+ vsrc1 = INST_AA(inst);
+ ref = FETCH(1); /* class/field/method ref */
+ dvmThrowVerificationError(curMethod, vsrc1, ref);
+ GOTO_exceptionThrown();
+OP_END
+
+/* File: c/OP_EXECUTE_INLINE.c */
+HANDLE_OPCODE(OP_EXECUTE_INLINE /*vB, {vD, vE, vF, vG}, inline@CCCC*/)
+ {
+ /*
+ * This has the same form as other method calls, but we ignore
+ * the 5th argument (vA). This is chiefly because the first four
+ * arguments to a function on ARM are in registers.
+ *
+ * We only set the arguments that are actually used, leaving
+ * the rest uninitialized. We're assuming that, if the method
+ * needs them, they'll be specified in the call.
+ *
+ * However, this annoys gcc when optimizations are enabled,
+ * causing a "may be used uninitialized" warning. Quieting
+ * the warnings incurs a slight penalty (5%: 373ns vs. 393ns
+ * on empty method). Note that valgrind is perfectly happy
+ * either way as the uninitialiezd values are never actually
+ * used.
+ */
+ u4 arg0, arg1, arg2, arg3;
+ arg0 = arg1 = arg2 = arg3 = 0;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_B(inst); /* #of args */
+ ref = FETCH(1); /* inline call "ref" */
+ vdst = FETCH(2); /* 0-4 register indices */
+ ILOGV("|execute-inline args=%d @%d {regs=0x%04x}",
+ vsrc1, ref, vdst);
+
+ assert((vdst >> 16) == 0); // 16-bit type -or- high 16 bits clear
+ assert(vsrc1 <= 4);
+
+ switch (vsrc1) {
+ case 4:
+ arg3 = GET_REGISTER(vdst >> 12);
+ /* fall through */
+ case 3:
+ arg2 = GET_REGISTER((vdst & 0x0f00) >> 8);
+ /* fall through */
+ case 2:
+ arg1 = GET_REGISTER((vdst & 0x00f0) >> 4);
+ /* fall through */
+ case 1:
+ arg0 = GET_REGISTER(vdst & 0x0f);
+ /* fall through */
+ default: // case 0
+ ;
+ }
+
+#if INTERP_TYPE == INTERP_DBG
+ if (!dvmPerformInlineOp4Dbg(arg0, arg1, arg2, arg3, &retval, ref))
+ GOTO_exceptionThrown();
+#else
+ if (!dvmPerformInlineOp4Std(arg0, arg1, arg2, arg3, &retval, ref))
+ GOTO_exceptionThrown();
+#endif
+ }
+ FINISH(3);
+OP_END
+
+/* File: c/OP_EXECUTE_INLINE_RANGE.c */
+HANDLE_OPCODE(OP_EXECUTE_INLINE_RANGE /*{vCCCC..v(CCCC+AA-1)}, inline@BBBB*/)
+ {
+ u4 arg0, arg1, arg2, arg3;
+ arg0 = arg1 = arg2 = arg3 = 0; /* placate gcc */
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst); /* #of args */
+ ref = FETCH(1); /* inline call "ref" */
+ vdst = FETCH(2); /* range base */
+ ILOGV("|execute-inline-range args=%d @%d {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+
+ assert((vdst >> 16) == 0); // 16-bit type -or- high 16 bits clear
+ assert(vsrc1 <= 4);
+
+ switch (vsrc1) {
+ case 4:
+ arg3 = GET_REGISTER(vdst+3);
+ /* fall through */
+ case 3:
+ arg2 = GET_REGISTER(vdst+2);
+ /* fall through */
+ case 2:
+ arg1 = GET_REGISTER(vdst+1);
+ /* fall through */
+ case 1:
+ arg0 = GET_REGISTER(vdst+0);
+ /* fall through */
+ default: // case 0
+ ;
+ }
+
+#if INTERP_TYPE == INTERP_DBG
+ if (!dvmPerformInlineOp4Dbg(arg0, arg1, arg2, arg3, &retval, ref))
+ GOTO_exceptionThrown();
+#else
+ if (!dvmPerformInlineOp4Std(arg0, arg1, arg2, arg3, &retval, ref))
+ GOTO_exceptionThrown();
+#endif
+ }
+ FINISH(3);
+OP_END
+
+/* File: c/OP_INVOKE_DIRECT_EMPTY.c */
+HANDLE_OPCODE(OP_INVOKE_DIRECT_EMPTY /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+#if INTERP_TYPE != INTERP_DBG
+ //LOGI("Ignoring empty\n");
+ FINISH(3);
+#else
+ if (!gDvm.debuggerActive) {
+ //LOGI("Skipping empty\n");
+ FINISH(3); // don't want it to show up in profiler output
+ } else {
+ //LOGI("Running empty\n");
+ /* fall through to OP_INVOKE_DIRECT */
+ GOTO_invoke(invokeDirect, false);
+ }
+#endif
+OP_END
+
+/* File: c/OP_UNUSED_F1.c */
+HANDLE_OPCODE(OP_UNUSED_F1)
+OP_END
+
+/* File: c/OP_IGET_QUICK.c */
+HANDLE_IGET_X_QUICK(OP_IGET_QUICK, "", Int, )
+OP_END
+
+/* File: c/OP_IGET_WIDE_QUICK.c */
+HANDLE_IGET_X_QUICK(OP_IGET_WIDE_QUICK, "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IGET_OBJECT_QUICK.c */
+HANDLE_IGET_X_QUICK(OP_IGET_OBJECT_QUICK, "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IPUT_QUICK.c */
+HANDLE_IPUT_X_QUICK(OP_IPUT_QUICK, "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_WIDE_QUICK.c */
+HANDLE_IPUT_X_QUICK(OP_IPUT_WIDE_QUICK, "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IPUT_OBJECT_QUICK.c */
+HANDLE_IPUT_X_QUICK(OP_IPUT_OBJECT_QUICK, "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_INVOKE_VIRTUAL_QUICK.c */
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL_QUICK /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+ GOTO_invoke(invokeVirtualQuick, false);
+OP_END
+
+/* File: c/OP_INVOKE_VIRTUAL_QUICK_RANGE.c */
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL_QUICK_RANGE/*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+ GOTO_invoke(invokeVirtualQuick, true);
+OP_END
+
+/* File: c/OP_INVOKE_SUPER_QUICK.c */
+HANDLE_OPCODE(OP_INVOKE_SUPER_QUICK /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+ GOTO_invoke(invokeSuperQuick, false);
+OP_END
+
+/* File: c/OP_INVOKE_SUPER_QUICK_RANGE.c */
+HANDLE_OPCODE(OP_INVOKE_SUPER_QUICK_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+ GOTO_invoke(invokeSuperQuick, true);
+OP_END
+
+/* File: c/OP_IPUT_OBJECT_VOLATILE.c */
+HANDLE_IPUT_X(OP_IPUT_OBJECT_VOLATILE, "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SGET_OBJECT_VOLATILE.c */
+HANDLE_SGET_X(OP_SGET_OBJECT_VOLATILE, "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SPUT_OBJECT_VOLATILE.c */
+HANDLE_SPUT_X(OP_SPUT_OBJECT_VOLATILE, "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_UNUSED_FF.c */
+HANDLE_OPCODE(OP_UNUSED_FF)
+ /*
+ * In portable interp, most unused opcodes will fall through to here.
+ */
+ LOGE("unknown opcode 0x%02x\n", INST_INST(inst));
+ dvmAbort();
+ FINISH(1);
+OP_END
+
+/* File: c/gotoTargets.c */
+/*
+ * C footer. This has some common code shared by the various targets.
+ */
+
+/*
+ * Everything from here on is a "goto target". In the basic interpreter
+ * we jump into these targets and then jump directly to the handler for
+ * next instruction. Here, these are subroutines that return to the caller.
+ */
+
+GOTO_TARGET(filledNewArray, bool methodCallRange)
+ {
+ ClassObject* arrayClass;
+ ArrayObject* newArray;
+ u4* contents;
+ char typeCh;
+ int i;
+ u4 arg5;
+
+ EXPORT_PC();
+
+ ref = FETCH(1); /* class ref */
+ vdst = FETCH(2); /* first 4 regs -or- range base */
+
+ if (methodCallRange) {
+ vsrc1 = INST_AA(inst); /* #of elements */
+ arg5 = -1; /* silence compiler warning */
+ ILOGV("|filled-new-array-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ } else {
+ arg5 = INST_A(inst);
+ vsrc1 = INST_B(inst); /* #of elements */
+ ILOGV("|filled-new-array args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1, ref, vdst, arg5);
+ }
+
+ /*
+ * Resolve the array class.
+ */
+ arrayClass = dvmDexGetResolvedClass(methodClassDex, ref);
+ if (arrayClass == NULL) {
+ arrayClass = dvmResolveClass(curMethod->clazz, ref, false);
+ if (arrayClass == NULL)
+ GOTO_exceptionThrown();
+ }
+ /*
+ if (!dvmIsArrayClass(arrayClass)) {
+ dvmThrowException("Ljava/lang/RuntimeError;",
+ "filled-new-array needs array class");
+ GOTO_exceptionThrown();
+ }
+ */
+ /* verifier guarantees this is an array class */
+ assert(dvmIsArrayClass(arrayClass));
+ assert(dvmIsClassInitialized(arrayClass));
+
+ /*
+ * Create an array of the specified type.
+ */
+ LOGVV("+++ filled-new-array type is '%s'\n", arrayClass->descriptor);
+ typeCh = arrayClass->descriptor[1];
+ if (typeCh == 'D' || typeCh == 'J') {
+ /* category 2 primitives not allowed */
+ dvmThrowException("Ljava/lang/RuntimeError;",
+ "bad filled array req");
+ GOTO_exceptionThrown();
+ } else if (typeCh != 'L' && typeCh != '[' && typeCh != 'I') {
+ /* TODO: requires multiple "fill in" loops with different widths */
+ LOGE("non-int primitives not implemented\n");
+ dvmThrowException("Ljava/lang/InternalError;",
+ "filled-new-array not implemented for anything but 'int'");
+ GOTO_exceptionThrown();
+ }
+
+ newArray = dvmAllocArrayByClass(arrayClass, vsrc1, ALLOC_DONT_TRACK);
+ if (newArray == NULL)
+ GOTO_exceptionThrown();
+
+ /*
+ * Fill in the elements. It's legal for vsrc1 to be zero.
+ */
+ contents = (u4*) newArray->contents;
+ if (methodCallRange) {
+ for (i = 0; i < vsrc1; i++)
+ contents[i] = GET_REGISTER(vdst+i);
+ } else {
+ assert(vsrc1 <= 5);
+ if (vsrc1 == 5) {
+ contents[4] = GET_REGISTER(arg5);
+ vsrc1--;
+ }
+ for (i = 0; i < vsrc1; i++) {
+ contents[i] = GET_REGISTER(vdst & 0x0f);
+ vdst >>= 4;
+ }
+ }
+ if (typeCh == 'L' || typeCh == '[') {
+ dvmWriteBarrierArray(newArray, 0, newArray->length);
+ }
+
+ retval.l = newArray;
+ }
+ FINISH(3);
+GOTO_TARGET_END
+
+
+GOTO_TARGET(invokeVirtual, bool methodCallRange)
+ {
+ Method* baseMethod;
+ Object* thisPtr;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* method ref */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ /*
+ * The object against which we are executing a method is always
+ * in the first argument.
+ */
+ if (methodCallRange) {
+ assert(vsrc1 > 0);
+ ILOGV("|invoke-virtual-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ thisPtr = (Object*) GET_REGISTER(vdst);
+ } else {
+ assert((vsrc1>>4) > 0);
+ ILOGV("|invoke-virtual args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+ thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+ }
+
+ if (!checkForNull(thisPtr))
+ GOTO_exceptionThrown();
+
+ /*
+ * Resolve the method. This is the correct method for the static
+ * type of the object. We also verify access permissions here.
+ */
+ baseMethod = dvmDexGetResolvedMethod(methodClassDex, ref);
+ if (baseMethod == NULL) {
+ baseMethod = dvmResolveMethod(curMethod->clazz, ref,METHOD_VIRTUAL);
+ if (baseMethod == NULL) {
+ ILOGV("+ unknown method or access denied\n");
+ GOTO_exceptionThrown();
+ }
+ }
+
+ /*
+ * Combine the object we found with the vtable offset in the
+ * method.
+ */
+ assert(baseMethod->methodIndex < thisPtr->clazz->vtableCount);
+ methodToCall = thisPtr->clazz->vtable[baseMethod->methodIndex];
+
+#if defined(WITH_JIT) && (INTERP_TYPE == INTERP_DBG)
+ callsiteClass = thisPtr->clazz;
+#endif
+
+#if 0
+ if (dvmIsAbstractMethod(methodToCall)) {
+ /*
+ * This can happen if you create two classes, Base and Sub, where
+ * Sub is a sub-class of Base. Declare a protected abstract
+ * method foo() in Base, and invoke foo() from a method in Base.
+ * Base is an "abstract base class" and is never instantiated
+ * directly. Now, Override foo() in Sub, and use Sub. This
+ * Works fine unless Sub stops providing an implementation of
+ * the method.
+ */
+ dvmThrowException("Ljava/lang/AbstractMethodError;",
+ "abstract method not implemented");
+ GOTO_exceptionThrown();
+ }
+#else
+ assert(!dvmIsAbstractMethod(methodToCall) ||
+ methodToCall->nativeFunc != NULL);
+#endif
+
+ LOGVV("+++ base=%s.%s virtual[%d]=%s.%s\n",
+ baseMethod->clazz->descriptor, baseMethod->name,
+ (u4) baseMethod->methodIndex,
+ methodToCall->clazz->descriptor, methodToCall->name);
+ assert(methodToCall != NULL);
+
+#if 0
+ if (vsrc1 != methodToCall->insSize) {
+ LOGW("WRONG METHOD: base=%s.%s virtual[%d]=%s.%s\n",
+ baseMethod->clazz->descriptor, baseMethod->name,
+ (u4) baseMethod->methodIndex,
+ methodToCall->clazz->descriptor, methodToCall->name);
+ //dvmDumpClass(baseMethod->clazz);
+ //dvmDumpClass(methodToCall->clazz);
+ dvmDumpAllClasses(0);
+ }
+#endif
+
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+ }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeSuper, bool methodCallRange)
+ {
+ Method* baseMethod;
+ u2 thisReg;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* method ref */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ if (methodCallRange) {
+ ILOGV("|invoke-super-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ thisReg = vdst;
+ } else {
+ ILOGV("|invoke-super args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+ thisReg = vdst & 0x0f;
+ }
+ /* impossible in well-formed code, but we must check nevertheless */
+ if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+ GOTO_exceptionThrown();
+
+ /*
+ * Resolve the method. This is the correct method for the static
+ * type of the object. We also verify access permissions here.
+ * The first arg to dvmResolveMethod() is just the referring class
+ * (used for class loaders and such), so we don't want to pass
+ * the superclass into the resolution call.
+ */
+ baseMethod = dvmDexGetResolvedMethod(methodClassDex, ref);
+ if (baseMethod == NULL) {
+ baseMethod = dvmResolveMethod(curMethod->clazz, ref,METHOD_VIRTUAL);
+ if (baseMethod == NULL) {
+ ILOGV("+ unknown method or access denied\n");
+ GOTO_exceptionThrown();
+ }
+ }
+
+ /*
+ * Combine the object we found with the vtable offset in the
+ * method's class.
+ *
+ * We're using the current method's class' superclass, not the
+ * superclass of "this". This is because we might be executing
+ * in a method inherited from a superclass, and we want to run
+ * in that class' superclass.
+ */
+ if (baseMethod->methodIndex >= curMethod->clazz->super->vtableCount) {
+ /*
+ * Method does not exist in the superclass. Could happen if
+ * superclass gets updated.
+ */
+ dvmThrowException("Ljava/lang/NoSuchMethodError;",
+ baseMethod->name);
+ GOTO_exceptionThrown();
+ }
+ methodToCall = curMethod->clazz->super->vtable[baseMethod->methodIndex];
+#if 0
+ if (dvmIsAbstractMethod(methodToCall)) {
+ dvmThrowException("Ljava/lang/AbstractMethodError;",
+ "abstract method not implemented");
+ GOTO_exceptionThrown();
+ }
+#else
+ assert(!dvmIsAbstractMethod(methodToCall) ||
+ methodToCall->nativeFunc != NULL);
+#endif
+ LOGVV("+++ base=%s.%s super-virtual=%s.%s\n",
+ baseMethod->clazz->descriptor, baseMethod->name,
+ methodToCall->clazz->descriptor, methodToCall->name);
+ assert(methodToCall != NULL);
+
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+ }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeInterface, bool methodCallRange)
+ {
+ Object* thisPtr;
+ ClassObject* thisClass;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* method ref */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ /*
+ * The object against which we are executing a method is always
+ * in the first argument.
+ */
+ if (methodCallRange) {
+ assert(vsrc1 > 0);
+ ILOGV("|invoke-interface-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ thisPtr = (Object*) GET_REGISTER(vdst);
+ } else {
+ assert((vsrc1>>4) > 0);
+ ILOGV("|invoke-interface args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+ thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+ }
+ if (!checkForNull(thisPtr))
+ GOTO_exceptionThrown();
+
+ thisClass = thisPtr->clazz;
+
+#if defined(WITH_JIT) && (INTERP_TYPE == INTERP_DBG)
+ callsiteClass = thisClass;
+#endif
+
+ /*
+ * Given a class and a method index, find the Method* with the
+ * actual code we want to execute.
+ */
+ methodToCall = dvmFindInterfaceMethodInCache(thisClass, ref, curMethod,
+ methodClassDex);
+ if (methodToCall == NULL) {
+ assert(dvmCheckException(self));
+ GOTO_exceptionThrown();
+ }
+
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+ }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeDirect, bool methodCallRange)
+ {
+ u2 thisReg;
+
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* method ref */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ EXPORT_PC();
+
+ if (methodCallRange) {
+ ILOGV("|invoke-direct-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ thisReg = vdst;
+ } else {
+ ILOGV("|invoke-direct args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+ thisReg = vdst & 0x0f;
+ }
+ if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+ GOTO_exceptionThrown();
+
+ methodToCall = dvmDexGetResolvedMethod(methodClassDex, ref);
+ if (methodToCall == NULL) {
+ methodToCall = dvmResolveMethod(curMethod->clazz, ref,
+ METHOD_DIRECT);
+ if (methodToCall == NULL) {
+ ILOGV("+ unknown direct method\n"); // should be impossible
+ GOTO_exceptionThrown();
+ }
+ }
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+ }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeStatic, bool methodCallRange)
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* method ref */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ EXPORT_PC();
+
+ if (methodCallRange)
+ ILOGV("|invoke-static-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ else
+ ILOGV("|invoke-static args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+
+ methodToCall = dvmDexGetResolvedMethod(methodClassDex, ref);
+ if (methodToCall == NULL) {
+ methodToCall = dvmResolveMethod(curMethod->clazz, ref, METHOD_STATIC);
+ if (methodToCall == NULL) {
+ ILOGV("+ unknown method\n");
+ GOTO_exceptionThrown();
+ }
+
+ /*
+ * The JIT needs dvmDexGetResolvedMethod() to return non-null.
+ * Since we use the portable interpreter to build the trace, this extra
+ * check is not needed for mterp.
+ */
+ if (dvmDexGetResolvedMethod(methodClassDex, ref) == NULL) {
+ /* Class initialization is still ongoing */
+ ABORT_JIT_TSELECT();
+ }
+ }
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeVirtualQuick, bool methodCallRange)
+ {
+ Object* thisPtr;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* vtable index */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ /*
+ * The object against which we are executing a method is always
+ * in the first argument.
+ */
+ if (methodCallRange) {
+ assert(vsrc1 > 0);
+ ILOGV("|invoke-virtual-quick-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ thisPtr = (Object*) GET_REGISTER(vdst);
+ } else {
+ assert((vsrc1>>4) > 0);
+ ILOGV("|invoke-virtual-quick args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+ thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+ }
+
+ if (!checkForNull(thisPtr))
+ GOTO_exceptionThrown();
+
+#if defined(WITH_JIT) && (INTERP_TYPE == INTERP_DBG)
+ callsiteClass = thisPtr->clazz;
+#endif
+
+ /*
+ * Combine the object we found with the vtable offset in the
+ * method.
+ */
+ assert(ref < thisPtr->clazz->vtableCount);
+ methodToCall = thisPtr->clazz->vtable[ref];
+
+#if 0
+ if (dvmIsAbstractMethod(methodToCall)) {
+ dvmThrowException("Ljava/lang/AbstractMethodError;",
+ "abstract method not implemented");
+ GOTO_exceptionThrown();
+ }
+#else
+ assert(!dvmIsAbstractMethod(methodToCall) ||
+ methodToCall->nativeFunc != NULL);
+#endif
+
+ LOGVV("+++ virtual[%d]=%s.%s\n",
+ ref, methodToCall->clazz->descriptor, methodToCall->name);
+ assert(methodToCall != NULL);
+
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+ }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeSuperQuick, bool methodCallRange)
+ {
+ u2 thisReg;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* vtable index */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ if (methodCallRange) {
+ ILOGV("|invoke-super-quick-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ thisReg = vdst;
+ } else {
+ ILOGV("|invoke-super-quick args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+ thisReg = vdst & 0x0f;
+ }
+ /* impossible in well-formed code, but we must check nevertheless */
+ if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+ GOTO_exceptionThrown();
+
+#if 0 /* impossible in optimized + verified code */
+ if (ref >= curMethod->clazz->super->vtableCount) {
+ dvmThrowException("Ljava/lang/NoSuchMethodError;", NULL);
+ GOTO_exceptionThrown();
+ }
+#else
+ assert(ref < curMethod->clazz->super->vtableCount);
+#endif
+
+ /*
+ * Combine the object we found with the vtable offset in the
+ * method's class.
+ *
+ * We're using the current method's class' superclass, not the
+ * superclass of "this". This is because we might be executing
+ * in a method inherited from a superclass, and we want to run
+ * in the method's class' superclass.
+ */
+ methodToCall = curMethod->clazz->super->vtable[ref];
+
+#if 0
+ if (dvmIsAbstractMethod(methodToCall)) {
+ dvmThrowException("Ljava/lang/AbstractMethodError;",
+ "abstract method not implemented");
+ GOTO_exceptionThrown();
+ }
+#else
+ assert(!dvmIsAbstractMethod(methodToCall) ||
+ methodToCall->nativeFunc != NULL);
+#endif
+ LOGVV("+++ super-virtual[%d]=%s.%s\n",
+ ref, methodToCall->clazz->descriptor, methodToCall->name);
+ assert(methodToCall != NULL);
+
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+ }
+GOTO_TARGET_END
+
+
+ /*
+ * General handling for return-void, return, and return-wide. Put the
+ * return value in "retval" before jumping here.
+ */
+GOTO_TARGET(returnFromMethod)
+ {
+ StackSaveArea* saveArea;
+
+ /*
+ * We must do this BEFORE we pop the previous stack frame off, so
+ * that the GC can see the return value (if any) in the local vars.
+ *
+ * Since this is now an interpreter switch point, we must do it before
+ * we do anything at all.
+ */
+ PERIODIC_CHECKS(kInterpEntryReturn, 0);
+
+ ILOGV("> retval=0x%llx (leaving %s.%s %s)",
+ retval.j, curMethod->clazz->descriptor, curMethod->name,
+ curMethod->shorty);
+ //DUMP_REGS(curMethod, fp);
+
+ saveArea = SAVEAREA_FROM_FP(fp);
+
+#ifdef EASY_GDB
+ debugSaveArea = saveArea;
+#endif
+#if (INTERP_TYPE == INTERP_DBG)
+ TRACE_METHOD_EXIT(self, curMethod);
+#endif
+
+ /* back up to previous frame and see if we hit a break */
+ fp = saveArea->prevFrame;
+ assert(fp != NULL);
+ if (dvmIsBreakFrame(fp)) {
+ /* bail without popping the method frame from stack */
+ LOGVV("+++ returned into break frame\n");
+#if defined(WITH_JIT)
+ /* Let the Jit know the return is terminating normally */
+ CHECK_JIT_VOID();
+#endif
+ GOTO_bail();
+ }
+
+ /* update thread FP, and reset local variables */
+ self->curFrame = fp;
+ curMethod = SAVEAREA_FROM_FP(fp)->method;
+ //methodClass = curMethod->clazz;
+ methodClassDex = curMethod->clazz->pDvmDex;
+ pc = saveArea->savedPc;
+ ILOGD("> (return to %s.%s %s)", curMethod->clazz->descriptor,
+ curMethod->name, curMethod->shorty);
+
+ /* use FINISH on the caller's invoke instruction */
+ //u2 invokeInstr = INST_INST(FETCH(0));
+ if (true /*invokeInstr >= OP_INVOKE_VIRTUAL &&
+ invokeInstr <= OP_INVOKE_INTERFACE*/)
+ {
+ FINISH(3);
+ } else {
+ //LOGE("Unknown invoke instr %02x at %d\n",
+ // invokeInstr, (int) (pc - curMethod->insns));
+ assert(false);
+ }
+ }
+GOTO_TARGET_END
+
+
+ /*
+ * Jump here when the code throws an exception.
+ *
+ * By the time we get here, the Throwable has been created and the stack
+ * trace has been saved off.
+ */
+GOTO_TARGET(exceptionThrown)
+ {
+ Object* exception;
+ int catchRelPc;
+
+ /*
+ * Since this is now an interpreter switch point, we must do it before
+ * we do anything at all.
+ */
+ PERIODIC_CHECKS(kInterpEntryThrow, 0);
+
+#if defined(WITH_JIT)
+ // Something threw during trace selection - abort the current trace
+ ABORT_JIT_TSELECT();
+#endif
+ /*
+ * We save off the exception and clear the exception status. While
+ * processing the exception we might need to load some Throwable
+ * classes, and we don't want class loader exceptions to get
+ * confused with this one.
+ */
+ assert(dvmCheckException(self));
+ exception = dvmGetException(self);
+ dvmAddTrackedAlloc(exception, self);
+ dvmClearException(self);
+
+ LOGV("Handling exception %s at %s:%d\n",
+ exception->clazz->descriptor, curMethod->name,
+ dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+
+#if (INTERP_TYPE == INTERP_DBG)
+ /*
+ * Tell the debugger about it.
+ *
+ * TODO: if the exception was thrown by interpreted code, control
+ * fell through native, and then back to us, we will report the
+ * exception at the point of the throw and again here. We can avoid
+ * this by not reporting exceptions when we jump here directly from
+ * the native call code above, but then we won't report exceptions
+ * that were thrown *from* the JNI code (as opposed to *through* it).
+ *
+ * The correct solution is probably to ignore from-native exceptions
+ * here, and have the JNI exception code do the reporting to the
+ * debugger.
+ */
+ if (gDvm.debuggerActive) {
+ void* catchFrame;
+ catchRelPc = dvmFindCatchBlock(self, pc - curMethod->insns,
+ exception, true, &catchFrame);
+ dvmDbgPostException(fp, pc - curMethod->insns, catchFrame,
+ catchRelPc, exception);
+ }
+#endif
+
+ /*
+ * We need to unroll to the catch block or the nearest "break"
+ * frame.
+ *
+ * A break frame could indicate that we have reached an intermediate
+ * native call, or have gone off the top of the stack and the thread
+ * needs to exit. Either way, we return from here, leaving the
+ * exception raised.
+ *
+ * If we do find a catch block, we want to transfer execution to
+ * that point.
+ *
+ * Note this can cause an exception while resolving classes in
+ * the "catch" blocks.
+ */
+ catchRelPc = dvmFindCatchBlock(self, pc - curMethod->insns,
+ exception, false, (void*)&fp);
+
+ /*
+ * Restore the stack bounds after an overflow. This isn't going to
+ * be correct in all circumstances, e.g. if JNI code devours the
+ * exception this won't happen until some other exception gets
+ * thrown. If the code keeps pushing the stack bounds we'll end
+ * up aborting the VM.
+ *
+ * Note we want to do this *after* the call to dvmFindCatchBlock,
+ * because that may need extra stack space to resolve exception
+ * classes (e.g. through a class loader).
+ *
+ * It's possible for the stack overflow handling to cause an
+ * exception (specifically, class resolution in a "catch" block
+ * during the call above), so we could see the thread's overflow
+ * flag raised but actually be running in a "nested" interpreter
+ * frame. We don't allow doubled-up StackOverflowErrors, so
+ * we can check for this by just looking at the exception type
+ * in the cleanup function. Also, we won't unroll past the SOE
+ * point because the more-recent exception will hit a break frame
+ * as it unrolls to here.
+ */
+ if (self->stackOverflowed)
+ dvmCleanupStackOverflow(self, exception);
+
+ if (catchRelPc < 0) {
+ /* falling through to JNI code or off the bottom of the stack */
+#if DVM_SHOW_EXCEPTION >= 2
+ LOGD("Exception %s from %s:%d not caught locally\n",
+ exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+ dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+#endif
+ dvmSetException(self, exception);
+ dvmReleaseTrackedAlloc(exception, self);
+ GOTO_bail();
+ }
+
+#if DVM_SHOW_EXCEPTION >= 3
+ {
+ const Method* catchMethod = SAVEAREA_FROM_FP(fp)->method;
+ LOGD("Exception %s thrown from %s:%d to %s:%d\n",
+ exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+ dvmLineNumFromPC(curMethod, pc - curMethod->insns),
+ dvmGetMethodSourceFile(catchMethod),
+ dvmLineNumFromPC(catchMethod, catchRelPc));
+ }
+#endif
+
+ /*
+ * Adjust local variables to match self->curFrame and the
+ * updated PC.
+ */
+ //fp = (u4*) self->curFrame;
+ curMethod = SAVEAREA_FROM_FP(fp)->method;
+ //methodClass = curMethod->clazz;
+ methodClassDex = curMethod->clazz->pDvmDex;
+ pc = curMethod->insns + catchRelPc;
+ ILOGV("> pc <-- %s.%s %s", curMethod->clazz->descriptor,
+ curMethod->name, curMethod->shorty);
+ DUMP_REGS(curMethod, fp, false); // show all regs
+
+ /*
+ * Restore the exception if the handler wants it.
+ *
+ * The Dalvik spec mandates that, if an exception handler wants to
+ * do something with the exception, the first instruction executed
+ * must be "move-exception". We can pass the exception along
+ * through the thread struct, and let the move-exception instruction
+ * clear it for us.
+ *
+ * If the handler doesn't call move-exception, we don't want to
+ * finish here with an exception still pending.
+ */
+ if (INST_INST(FETCH(0)) == OP_MOVE_EXCEPTION)
+ dvmSetException(self, exception);
+
+ dvmReleaseTrackedAlloc(exception, self);
+ FINISH(0);
+ }
+GOTO_TARGET_END
+
+
+
+ /*
+ * General handling for invoke-{virtual,super,direct,static,interface},
+ * including "quick" variants.
+ *
+ * Set "methodToCall" to the Method we're calling, and "methodCallRange"
+ * depending on whether this is a "/range" instruction.
+ *
+ * For a range call:
+ * "vsrc1" holds the argument count (8 bits)
+ * "vdst" holds the first argument in the range
+ * For a non-range call:
+ * "vsrc1" holds the argument count (4 bits) and the 5th argument index
+ * "vdst" holds four 4-bit register indices
+ *
+ * The caller must EXPORT_PC before jumping here, because any method
+ * call can throw a stack overflow exception.
+ */
+GOTO_TARGET(invokeMethod, bool methodCallRange, const Method* _methodToCall,
+ u2 count, u2 regs)
+ {
+ STUB_HACK(vsrc1 = count; vdst = regs; methodToCall = _methodToCall;);
+
+ //printf("range=%d call=%p count=%d regs=0x%04x\n",
+ // methodCallRange, methodToCall, count, regs);
+ //printf(" --> %s.%s %s\n", methodToCall->clazz->descriptor,
+ // methodToCall->name, methodToCall->shorty);
+
+ u4* outs;
+ int i;
+
+ /*
+ * Copy args. This may corrupt vsrc1/vdst.
+ */
+ if (methodCallRange) {
+ // could use memcpy or a "Duff's device"; most functions have
+ // so few args it won't matter much
+ assert(vsrc1 <= curMethod->outsSize);
+ assert(vsrc1 == methodToCall->insSize);
+ outs = OUTS_FROM_FP(fp, vsrc1);
+ for (i = 0; i < vsrc1; i++)
+ outs[i] = GET_REGISTER(vdst+i);
+ } else {
+ u4 count = vsrc1 >> 4;
+
+ assert(count <= curMethod->outsSize);
+ assert(count == methodToCall->insSize);
+ assert(count <= 5);
+
+ outs = OUTS_FROM_FP(fp, count);
+#if 0
+ if (count == 5) {
+ outs[4] = GET_REGISTER(vsrc1 & 0x0f);
+ count--;
+ }
+ for (i = 0; i < (int) count; i++) {
+ outs[i] = GET_REGISTER(vdst & 0x0f);
+ vdst >>= 4;
+ }
+#else
+ // This version executes fewer instructions but is larger
+ // overall. Seems to be a teensy bit faster.
+ assert((vdst >> 16) == 0); // 16 bits -or- high 16 bits clear
+ switch (count) {
+ case 5:
+ outs[4] = GET_REGISTER(vsrc1 & 0x0f);
+ case 4:
+ outs[3] = GET_REGISTER(vdst >> 12);
+ case 3:
+ outs[2] = GET_REGISTER((vdst & 0x0f00) >> 8);
+ case 2:
+ outs[1] = GET_REGISTER((vdst & 0x00f0) >> 4);
+ case 1:
+ outs[0] = GET_REGISTER(vdst & 0x0f);
+ default:
+ ;
+ }
+#endif
+ }
+ }
+
+ /*
+ * (This was originally a "goto" target; I've kept it separate from the
+ * stuff above in case we want to refactor things again.)
+ *
+ * At this point, we have the arguments stored in the "outs" area of
+ * the current method's stack frame, and the method to call in
+ * "methodToCall". Push a new stack frame.
+ */
+ {
+ StackSaveArea* newSaveArea;
+ u4* newFp;
+
+ ILOGV("> %s%s.%s %s",
+ dvmIsNativeMethod(methodToCall) ? "(NATIVE) " : "",
+ methodToCall->clazz->descriptor, methodToCall->name,
+ methodToCall->shorty);
+
+ newFp = (u4*) SAVEAREA_FROM_FP(fp) - methodToCall->registersSize;
+ newSaveArea = SAVEAREA_FROM_FP(newFp);
+
+ /* verify that we have enough space */
+ if (true) {
+ u1* bottom;
+ bottom = (u1*) newSaveArea - methodToCall->outsSize * sizeof(u4);
+ if (bottom < self->interpStackEnd) {
+ /* stack overflow */
+ LOGV("Stack overflow on method call (start=%p end=%p newBot=%p(%d) size=%d '%s')\n",
+ self->interpStackStart, self->interpStackEnd, bottom,
+ (u1*) fp - bottom, self->interpStackSize,
+ methodToCall->name);
+ dvmHandleStackOverflow(self, methodToCall);
+ assert(dvmCheckException(self));
+ GOTO_exceptionThrown();
+ }
+ //LOGD("+++ fp=%p newFp=%p newSave=%p bottom=%p\n",
+ // fp, newFp, newSaveArea, bottom);
+ }
+
+#ifdef LOG_INSTR
+ if (methodToCall->registersSize > methodToCall->insSize) {
+ /*
+ * This makes valgrind quiet when we print registers that
+ * haven't been initialized. Turn it off when the debug
+ * messages are disabled -- we want valgrind to report any
+ * used-before-initialized issues.
+ */
+ memset(newFp, 0xcc,
+ (methodToCall->registersSize - methodToCall->insSize) * 4);
+ }
+#endif
+
+#ifdef EASY_GDB
+ newSaveArea->prevSave = SAVEAREA_FROM_FP(fp);
+#endif
+ newSaveArea->prevFrame = fp;
+ newSaveArea->savedPc = pc;
+#if defined(WITH_JIT)
+ newSaveArea->returnAddr = 0;
+#endif
+ newSaveArea->method = methodToCall;
+
+ if (!dvmIsNativeMethod(methodToCall)) {
+ /*
+ * "Call" interpreted code. Reposition the PC, update the
+ * frame pointer and other local state, and continue.
+ */
+ curMethod = methodToCall;
+ methodClassDex = curMethod->clazz->pDvmDex;
+ pc = methodToCall->insns;
+ fp = self->curFrame = newFp;
+#ifdef EASY_GDB
+ debugSaveArea = SAVEAREA_FROM_FP(newFp);
+#endif
+#if INTERP_TYPE == INTERP_DBG
+ debugIsMethodEntry = true; // profiling, debugging
+#endif
+ ILOGD("> pc <-- %s.%s %s", curMethod->clazz->descriptor,
+ curMethod->name, curMethod->shorty);
+ DUMP_REGS(curMethod, fp, true); // show input args
+ FINISH(0); // jump to method start
+ } else {
+ /* set this up for JNI locals, even if not a JNI native */
+#ifdef USE_INDIRECT_REF
+ newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
+#else
+ newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.nextEntry;
+#endif
+
+ self->curFrame = newFp;
+
+ DUMP_REGS(methodToCall, newFp, true); // show input args
+
+#if (INTERP_TYPE == INTERP_DBG)
+ if (gDvm.debuggerActive) {
+ dvmDbgPostLocationEvent(methodToCall, -1,
+ dvmGetThisPtr(curMethod, fp), DBG_METHOD_ENTRY);
+ }
+#endif
+#if (INTERP_TYPE == INTERP_DBG)
+ TRACE_METHOD_ENTER(self, methodToCall);
+#endif
+
+ {
+ ILOGD("> native <-- %s.%s %s", methodToCall->clazz->descriptor,
+ methodToCall->name, methodToCall->shorty);
+ }
+
+#if defined(WITH_JIT)
+ /* Allow the Jit to end any pending trace building */
+ CHECK_JIT_VOID();
+#endif
+
+ /*
+ * Jump through native call bridge. Because we leave no
+ * space for locals on native calls, "newFp" points directly
+ * to the method arguments.
+ */
+ (*methodToCall->nativeFunc)(newFp, &retval, methodToCall, self);
+
+#if (INTERP_TYPE == INTERP_DBG)
+ if (gDvm.debuggerActive) {
+ dvmDbgPostLocationEvent(methodToCall, -1,
+ dvmGetThisPtr(curMethod, fp), DBG_METHOD_EXIT);
+ }
+#endif
+#if (INTERP_TYPE == INTERP_DBG)
+ TRACE_METHOD_EXIT(self, methodToCall);
+#endif
+
+ /* pop frame off */
+ dvmPopJniLocals(self, newSaveArea);
+ self->curFrame = fp;
+
+ /*
+ * If the native code threw an exception, or interpreted code
+ * invoked by the native call threw one and nobody has cleared
+ * it, jump to our local exception handling.
+ */
+ if (dvmCheckException(self)) {
+ LOGV("Exception thrown by/below native code\n");
+ GOTO_exceptionThrown();
+ }
+
+ ILOGD("> retval=0x%llx (leaving native)", retval.j);
+ ILOGD("> (return from native %s.%s to %s.%s %s)",
+ methodToCall->clazz->descriptor, methodToCall->name,
+ curMethod->clazz->descriptor, curMethod->name,
+ curMethod->shorty);
+
+ //u2 invokeInstr = INST_INST(FETCH(0));
+ if (true /*invokeInstr >= OP_INVOKE_VIRTUAL &&
+ invokeInstr <= OP_INVOKE_INTERFACE*/)
+ {
+ FINISH(3);
+ } else {
+ //LOGE("Unknown invoke instr %02x at %d\n",
+ // invokeInstr, (int) (pc - curMethod->insns));
+ assert(false);
+ }
+ }
+ }
+ assert(false); // should not get here
+GOTO_TARGET_END
+
+/* File: portable/enddefs.c */
+/*--- end of opcodes ---*/
+
+#ifndef THREADED_INTERP
+ } // end of "switch"
+ } // end of "while"
+#endif
+
+bail:
+ ILOGD("|-- Leaving interpreter loop"); // note "curMethod" may be NULL
+
+ interpState->retval = retval;
+ return false;
+
+bail_switch:
+ /*
+ * The standard interpreter currently doesn't set or care about the
+ * "debugIsMethodEntry" value, so setting this is only of use if we're
+ * switching between two "debug" interpreters, which we never do.
+ *
+ * TODO: figure out if preserving this makes any sense.
+ */
+#if INTERP_TYPE == INTERP_DBG
+ interpState->debugIsMethodEntry = debugIsMethodEntry;
+#else
+ interpState->debugIsMethodEntry = false;
+#endif
+
+ /* export state changes */
+ interpState->method = curMethod;
+ interpState->pc = pc;
+ interpState->fp = fp;
+ /* debugTrackedRefStart doesn't change */
+ interpState->retval = retval; /* need for _entryPoint=ret */
+ interpState->nextMode =
+ (INTERP_TYPE == INTERP_STD) ? INTERP_DBG : INTERP_STD;
+ LOGVV(" meth='%s.%s' pc=0x%x fp=%p\n",
+ curMethod->clazz->descriptor, curMethod->name,
+ pc - curMethod->insns, fp);
+ return true;
+}
+
diff --git a/vm/mterp/out/InterpC-portstd.c b/vm/mterp/out/InterpC-portstd.c
new file mode 100644
index 0000000..f82c97d
--- /dev/null
+++ b/vm/mterp/out/InterpC-portstd.c
@@ -0,0 +1,4192 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'portstd'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: c/header.c */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* common includes */
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "mterp/Mterp.h"
+#include <math.h> // needed for fmod, fmodf
+#include "mterp/common/FindInterface.h"
+
+/*
+ * Configuration defines. These affect the C implementations, i.e. the
+ * portable interpreter(s) and C stubs.
+ *
+ * Some defines are controlled by the Makefile, e.g.:
+ * WITH_INSTR_CHECKS
+ * WITH_TRACKREF_CHECKS
+ * EASY_GDB
+ * NDEBUG
+ *
+ * If THREADED_INTERP is not defined, we use a classic "while true / switch"
+ * interpreter. If it is defined, then the tail end of each instruction
+ * handler fetches the next instruction and jumps directly to the handler.
+ * This increases the size of the "Std" interpreter by about 10%, but
+ * provides a speedup of about the same magnitude.
+ *
+ * There's a "hybrid" approach that uses a goto table instead of a switch
+ * statement, avoiding the "is the opcode in range" tests required for switch.
+ * The performance is close to the threaded version, and without the 10%
+ * size increase, but the benchmark results are off enough that it's not
+ * worth adding as a third option.
+ */
+#define THREADED_INTERP /* threaded vs. while-loop interpreter */
+
+#ifdef WITH_INSTR_CHECKS /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * ARM EABI requires 64-bit alignment for access to 64-bit data types. We
+ * can't just use pointers to copy 64-bit values out of our interpreted
+ * register set, because gcc will generate ldrd/strd.
+ *
+ * The __UNION version copies data in and out of a union. The __MEMCPY
+ * version uses a memcpy() call to do the transfer; gcc is smart enough to
+ * not actually call memcpy(). The __UNION version is very bad on ARM;
+ * it only uses one more instruction than __MEMCPY, but for some reason
+ * gcc thinks it needs separate storage for every instance of the union.
+ * On top of that, it feels the need to zero them out at the start of the
+ * method. Net result is we zero out ~700 bytes of stack space at the top
+ * of the interpreter using ARM STM instructions.
+ */
+#if defined(__ARM_EABI__)
+//# define NO_UNALIGN_64__UNION
+# define NO_UNALIGN_64__MEMCPY
+#endif
+
+//#define LOG_INSTR /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Keep a tally of accesses to fields. Currently only works if full DEX
+ * optimization is disabled.
+ */
+#ifdef PROFILE_FIELD_ACCESS
+# define UPDATE_FIELD_GET(_field) { (_field)->gets++; }
+# define UPDATE_FIELD_PUT(_field) { (_field)->puts++; }
+#else
+# define UPDATE_FIELD_GET(_field) ((void)0)
+# define UPDATE_FIELD_PUT(_field) ((void)0)
+#endif
+
+/*
+ * Export another copy of the PC on every instruction; this is largely
+ * redundant with EXPORT_PC and the debugger code. This value can be
+ * compared against what we have stored on the stack with EXPORT_PC to
+ * help ensure that we aren't missing any export calls.
+ */
+#if WITH_EXTRA_GC_CHECKS > 1
+# define EXPORT_EXTRA_PC() (self->currentPc2 = pc)
+#else
+# define EXPORT_EXTRA_PC()
+#endif
+
+/*
+ * Adjust the program counter. "_offset" is a signed int, in 16-bit units.
+ *
+ * Assumes the existence of "const u2* pc" and "const u2* curMethod->insns".
+ *
+ * We don't advance the program counter until we finish an instruction or
+ * branch, because we do want to have to unroll the PC if there's an
+ * exception.
+ */
+#ifdef CHECK_BRANCH_OFFSETS
+# define ADJUST_PC(_offset) do { \
+ int myoff = _offset; /* deref only once */ \
+ if (pc + myoff < curMethod->insns || \
+ pc + myoff >= curMethod->insns + dvmGetMethodInsnsSize(curMethod)) \
+ { \
+ char* desc; \
+ desc = dexProtoCopyMethodDescriptor(&curMethod->prototype); \
+ LOGE("Invalid branch %d at 0x%04x in %s.%s %s\n", \
+ myoff, (int) (pc - curMethod->insns), \
+ curMethod->clazz->descriptor, curMethod->name, desc); \
+ free(desc); \
+ dvmAbort(); \
+ } \
+ pc += myoff; \
+ EXPORT_EXTRA_PC(); \
+ } while (false)
+#else
+# define ADJUST_PC(_offset) do { \
+ pc += _offset; \
+ EXPORT_EXTRA_PC(); \
+ } while (false)
+#endif
+
+/*
+ * If enabled, log instructions as we execute them.
+ */
+#ifdef LOG_INSTR
+# define ILOGD(...) ILOG(LOG_DEBUG, __VA_ARGS__)
+# define ILOGV(...) ILOG(LOG_VERBOSE, __VA_ARGS__)
+# define ILOG(_level, ...) do { \
+ char debugStrBuf[128]; \
+ snprintf(debugStrBuf, sizeof(debugStrBuf), __VA_ARGS__); \
+ if (curMethod != NULL) \
+ LOG(_level, LOG_TAG"i", "%-2d|%04x%s\n", \
+ self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+ else \
+ LOG(_level, LOG_TAG"i", "%-2d|####%s\n", \
+ self->threadId, debugStrBuf); \
+ } while(false)
+void dvmDumpRegs(const Method* method, const u4* framePtr, bool inOnly);
+# define DUMP_REGS(_meth, _frame, _inOnly) dvmDumpRegs(_meth, _frame, _inOnly)
+static const char kSpacing[] = " ";
+#else
+# define ILOGD(...) ((void)0)
+# define ILOGV(...) ((void)0)
+# define DUMP_REGS(_meth, _frame, _inOnly) ((void)0)
+#endif
+
+/* get a long from an array of u4 */
+static inline s8 getLongFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { s8 ll; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.parts[0] = ptr[0];
+ conv.parts[1] = ptr[1];
+ return conv.ll;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ s8 val;
+ memcpy(&val, &ptr[idx], 8);
+ return val;
+#else
+ return *((s8*) &ptr[idx]);
+#endif
+}
+
+/* store a long into an array of u4 */
+static inline void putLongToArray(u4* ptr, int idx, s8 val)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { s8 ll; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.ll = val;
+ ptr[0] = conv.parts[0];
+ ptr[1] = conv.parts[1];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ memcpy(&ptr[idx], &val, 8);
+#else
+ *((s8*) &ptr[idx]) = val;
+#endif
+}
+
+/* get a double from an array of u4 */
+static inline double getDoubleFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { double d; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.parts[0] = ptr[0];
+ conv.parts[1] = ptr[1];
+ return conv.d;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ double dval;
+ memcpy(&dval, &ptr[idx], 8);
+ return dval;
+#else
+ return *((double*) &ptr[idx]);
+#endif
+}
+
+/* store a double into an array of u4 */
+static inline void putDoubleToArray(u4* ptr, int idx, double dval)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { double d; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.d = dval;
+ ptr[0] = conv.parts[0];
+ ptr[1] = conv.parts[1];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ memcpy(&ptr[idx], &dval, 8);
+#else
+ *((double*) &ptr[idx]) = dval;
+#endif
+}
+
+/*
+ * If enabled, validate the register number on every access. Otherwise,
+ * just do an array access.
+ *
+ * Assumes the existence of "u4* fp".
+ *
+ * "_idx" may be referenced more than once.
+ */
+#ifdef CHECK_REGISTER_INDICES
+# define GET_REGISTER(_idx) \
+ ( (_idx) < curMethod->registersSize ? \
+ (fp[(_idx)]) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER(_idx, _val) \
+ ( (_idx) < curMethod->registersSize ? \
+ (fp[(_idx)] = (u4)(_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_AS_OBJECT(_idx) ((Object *)GET_REGISTER(_idx))
+# define SET_REGISTER_AS_OBJECT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_INT(_idx) ((s4) GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ getLongFromArray(fp, (_idx)) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER_WIDE(_idx, _val) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ putLongToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_FLOAT(_idx) \
+ ( (_idx) < curMethod->registersSize ? \
+ (*((float*) &fp[(_idx)])) : (assert(!"bad reg"),1969.0f) )
+# define SET_REGISTER_FLOAT(_idx, _val) \
+ ( (_idx) < curMethod->registersSize ? \
+ (*((float*) &fp[(_idx)]) = (_val)) : (assert(!"bad reg"),1969.0f) )
+# define GET_REGISTER_DOUBLE(_idx) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ getDoubleFromArray(fp, (_idx)) : (assert(!"bad reg"),1969.0) )
+# define SET_REGISTER_DOUBLE(_idx, _val) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ putDoubleToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969.0) )
+#else
+# define GET_REGISTER(_idx) (fp[(_idx)])
+# define SET_REGISTER(_idx, _val) (fp[(_idx)] = (_val))
+# define GET_REGISTER_AS_OBJECT(_idx) ((Object*) fp[(_idx)])
+# define SET_REGISTER_AS_OBJECT(_idx, _val) (fp[(_idx)] = (u4)(_val))
+# define GET_REGISTER_INT(_idx) ((s4)GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) getLongFromArray(fp, (_idx))
+# define SET_REGISTER_WIDE(_idx, _val) putLongToArray(fp, (_idx), (_val))
+# define GET_REGISTER_FLOAT(_idx) (*((float*) &fp[(_idx)]))
+# define SET_REGISTER_FLOAT(_idx, _val) (*((float*) &fp[(_idx)]) = (_val))
+# define GET_REGISTER_DOUBLE(_idx) getDoubleFromArray(fp, (_idx))
+# define SET_REGISTER_DOUBLE(_idx, _val) putDoubleToArray(fp, (_idx), (_val))
+#endif
+
+/*
+ * Get 16 bits from the specified offset of the program counter. We always
+ * want to load 16 bits at a time from the instruction stream -- it's more
+ * efficient than 8 and won't have the alignment problems that 32 might.
+ *
+ * Assumes existence of "const u2* pc".
+ */
+#define FETCH(_offset) (pc[(_offset)])
+
+/*
+ * Extract instruction byte from 16-bit fetch (_inst is a u2).
+ */
+#define INST_INST(_inst) ((_inst) & 0xff)
+
+/*
+ * Replace the opcode (used when handling breakpoints). _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
+ * Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
+ */
+#define INST_A(_inst) (((_inst) >> 8) & 0x0f)
+#define INST_B(_inst) ((_inst) >> 12)
+
+/*
+ * Get the 8-bit "vAA" 8-bit register index from the instruction word.
+ * (_inst is u2)
+ */
+#define INST_AA(_inst) ((_inst) >> 8)
+
+/*
+ * The current PC must be available to Throwable constructors, e.g.
+ * those created by dvmThrowException(), so that the exception stack
+ * trace can be generated correctly. If we don't do this, the offset
+ * within the current method won't be shown correctly. See the notes
+ * in Exception.c.
+ *
+ * This is also used to determine the address for precise GC.
+ *
+ * Assumes existence of "u4* fp" and "const u2* pc".
+ */
+#define EXPORT_PC() (SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc)
+
+/*
+ * Determine if we need to switch to a different interpreter. "_current"
+ * is either INTERP_STD or INTERP_DBG. It should be fixed for a given
+ * interpreter generation file, which should remove the outer conditional
+ * from the following.
+ *
+ * If we're building without debug and profiling support, we never switch.
+ */
+#if defined(WITH_JIT)
+# define NEED_INTERP_SWITCH(_current) ( \
+ (_current == INTERP_STD) ? \
+ dvmJitDebuggerOrProfilerActive() : !dvmJitDebuggerOrProfilerActive() )
+#else
+# define NEED_INTERP_SWITCH(_current) ( \
+ (_current == INTERP_STD) ? \
+ dvmDebuggerOrProfilerActive() : !dvmDebuggerOrProfilerActive() )
+#endif
+
+/*
+ * Check to see if "obj" is NULL. If so, throw an exception. Assumes the
+ * pc has already been exported to the stack.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler calls into
+ * something that could throw an exception (so we have already called
+ * EXPORT_PC at the top).
+ */
+static inline bool checkForNull(Object* obj)
+{
+ if (obj == NULL) {
+ dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+ return false;
+ }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+ if (!dvmIsValidObject(obj)) {
+ LOGE("Invalid object %p\n", obj);
+ dvmAbort();
+ }
+#endif
+#ifndef NDEBUG
+ if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+ /* probable heap corruption */
+ LOGE("Invalid object class %p (in %p)\n", obj->clazz, obj);
+ dvmAbort();
+ }
+#endif
+ return true;
+}
+
+/*
+ * Check to see if "obj" is NULL. If so, export the PC into the stack
+ * frame and throw an exception.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler doesn't do
+ * anything else that can throw an exception.
+ */
+static inline bool checkForNullExportPC(Object* obj, u4* fp, const u2* pc)
+{
+ if (obj == NULL) {
+ EXPORT_PC();
+ dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+ return false;
+ }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+ if (!dvmIsValidObject(obj)) {
+ LOGE("Invalid object %p\n", obj);
+ dvmAbort();
+ }
+#endif
+#ifndef NDEBUG
+ if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+ /* probable heap corruption */
+ LOGE("Invalid object class %p (in %p)\n", obj->clazz, obj);
+ dvmAbort();
+ }
+#endif
+ return true;
+}
+
+/* File: portable/portstd.c */
+#define INTERP_FUNC_NAME dvmInterpretStd
+#define INTERP_TYPE INTERP_STD
+
+#define CHECK_DEBUG_AND_PROF() ((void)0)
+
+#define CHECK_JIT_BOOL() (false)
+#define CHECK_JIT_VOID()
+#define ABORT_JIT_TSELECT() ((void)0)
+
+/* File: portable/stubdefs.c */
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...)
+
+#define GOTO_TARGET(_target, ...) _target:
+
+#define GOTO_TARGET_END
+
+/* ugh */
+#define STUB_HACK(x)
+
+/*
+ * Instruction framing. For a switch-oriented implementation this is
+ * case/break, for a threaded implementation it's a goto label and an
+ * instruction fetch/computed goto.
+ *
+ * Assumes the existence of "const u2* pc" and (for threaded operation)
+ * "u2 inst".
+ *
+ * TODO: remove "switch" version.
+ */
+#ifdef THREADED_INTERP
+# define H(_op) &&op_##_op
+# define HANDLE_OPCODE(_op) op_##_op:
+# define FINISH(_offset) { \
+ ADJUST_PC(_offset); \
+ inst = FETCH(0); \
+ CHECK_DEBUG_AND_PROF(); \
+ CHECK_TRACKED_REFS(); \
+ if (CHECK_JIT_BOOL()) GOTO_bail_switch(); \
+ goto *handlerTable[INST_INST(inst)]; \
+ }
+# define FINISH_BKPT(_opcode) { \
+ goto *handlerTable[_opcode]; \
+ }
+#else
+# define HANDLE_OPCODE(_op) case _op:
+# define FINISH(_offset) { ADJUST_PC(_offset); break; }
+# define FINISH_BKPT(opcode) { > not implemented < }
+#endif
+
+#define OP_END
+
+#if defined(WITH_TRACKREF_CHECKS)
+# define CHECK_TRACKED_REFS() \
+ dvmInterpCheckTrackedRefs(self, curMethod, debugTrackedRefStart)
+#else
+# define CHECK_TRACKED_REFS() ((void)0)
+#endif
+
+
+/*
+ * The "goto" targets just turn into goto statements. The "arguments" are
+ * passed through local variables.
+ */
+
+#define GOTO_exceptionThrown() goto exceptionThrown;
+
+#define GOTO_returnFromMethod() goto returnFromMethod;
+
+#define GOTO_invoke(_target, _methodCallRange) \
+ do { \
+ methodCallRange = _methodCallRange; \
+ goto _target; \
+ } while(false)
+
+/* for this, the "args" are already in the locals */
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst) goto invokeMethod;
+
+#define GOTO_bail() goto bail;
+#define GOTO_bail_switch() goto bail_switch;
+
+/*
+ * Periodically check for thread suspension.
+ *
+ * While we're at it, see if a debugger has attached or the profiler has
+ * started. If so, switch to a different "goto" table.
+ */
+#define PERIODIC_CHECKS(_entryPoint, _pcadj) { \
+ if (dvmCheckSuspendQuick(self)) { \
+ EXPORT_PC(); /* need for precise GC */ \
+ dvmCheckSuspendPending(self); \
+ } \
+ if (NEED_INTERP_SWITCH(INTERP_TYPE)) { \
+ ADJUST_PC(_pcadj); \
+ interpState->entryPoint = _entryPoint; \
+ LOGVV("threadid=%d: switch to %s ep=%d adj=%d\n", \
+ self->threadId, \
+ (interpState->nextMode == INTERP_STD) ? "STD" : "DBG", \
+ (_entryPoint), (_pcadj)); \
+ GOTO_bail_switch(); \
+ } \
+ }
+
+/* File: c/opcommon.c */
+/* forward declarations of goto targets */
+GOTO_TARGET_DECL(filledNewArray, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtual, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuper, bool methodCallRange);
+GOTO_TARGET_DECL(invokeInterface, bool methodCallRange);
+GOTO_TARGET_DECL(invokeDirect, bool methodCallRange);
+GOTO_TARGET_DECL(invokeStatic, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtualQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuperQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeMethod, bool methodCallRange, const Method* methodToCall,
+ u2 count, u2 regs);
+GOTO_TARGET_DECL(returnFromMethod);
+GOTO_TARGET_DECL(exceptionThrown);
+
+/*
+ * ===========================================================================
+ *
+ * What follows are opcode definitions shared between multiple opcodes with
+ * minor substitutions handled by the C pre-processor. These should probably
+ * use the mterp substitution mechanism instead, with the code here moved
+ * into common fragment files (like the asm "binop.S"), although it's hard
+ * to give up the C preprocessor in favor of the much simpler text subst.
+ *
+ * ===========================================================================
+ */
+
+#define HANDLE_NUMCONV(_opcode, _opname, _fromtype, _totype) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER##_totype(vdst, \
+ GET_REGISTER##_fromtype(vsrc1)); \
+ FINISH(1);
+
+#define HANDLE_FLOAT_TO_INT(_opcode, _opname, _fromvtype, _fromrtype, \
+ _tovtype, _tortype) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ { \
+ /* spec defines specific handling for +/- inf and NaN values */ \
+ _fromvtype val; \
+ _tovtype intMin, intMax, result; \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1); \
+ val = GET_REGISTER##_fromrtype(vsrc1); \
+ intMin = (_tovtype) 1 << (sizeof(_tovtype) * 8 -1); \
+ intMax = ~intMin; \
+ result = (_tovtype) val; \
+ if (val >= intMax) /* +inf */ \
+ result = intMax; \
+ else if (val <= intMin) /* -inf */ \
+ result = intMin; \
+ else if (val != val) /* NaN */ \
+ result = 0; \
+ else \
+ result = (_tovtype) val; \
+ SET_REGISTER##_tortype(vdst, result); \
+ } \
+ FINISH(1);
+
+#define HANDLE_INT_TO_SMALL(_opcode, _opname, _type) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|int-to-%s v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER(vdst, (_type) GET_REGISTER(vsrc1)); \
+ FINISH(1);
+
+/* NOTE: the comparison result is always a signed 4-byte integer */
+#define HANDLE_OP_CMPX(_opcode, _opname, _varType, _type, _nanVal) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ int result; \
+ u2 regs; \
+ _varType val1, val2; \
+ vdst = INST_AA(inst); \
+ regs = FETCH(1); \
+ vsrc1 = regs & 0xff; \
+ vsrc2 = regs >> 8; \
+ ILOGV("|cmp%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ val1 = GET_REGISTER##_type(vsrc1); \
+ val2 = GET_REGISTER##_type(vsrc2); \
+ if (val1 == val2) \
+ result = 0; \
+ else if (val1 < val2) \
+ result = -1; \
+ else if (val1 > val2) \
+ result = 1; \
+ else \
+ result = (_nanVal); \
+ ILOGV("+ result=%d\n", result); \
+ SET_REGISTER(vdst, result); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_IF_XX(_opcode, _opname, _cmp) \
+ HANDLE_OPCODE(_opcode /*vA, vB, +CCCC*/) \
+ vsrc1 = INST_A(inst); \
+ vsrc2 = INST_B(inst); \
+ if ((s4) GET_REGISTER(vsrc1) _cmp (s4) GET_REGISTER(vsrc2)) { \
+ int branchOffset = (s2)FETCH(1); /* sign-extended */ \
+ ILOGV("|if-%s v%d,v%d,+0x%04x", (_opname), vsrc1, vsrc2, \
+ branchOffset); \
+ ILOGV("> branch taken"); \
+ if (branchOffset < 0) \
+ PERIODIC_CHECKS(kInterpEntryInstr, branchOffset); \
+ FINISH(branchOffset); \
+ } else { \
+ ILOGV("|if-%s v%d,v%d,-", (_opname), vsrc1, vsrc2); \
+ FINISH(2); \
+ }
+
+#define HANDLE_OP_IF_XXZ(_opcode, _opname, _cmp) \
+ HANDLE_OPCODE(_opcode /*vAA, +BBBB*/) \
+ vsrc1 = INST_AA(inst); \
+ if ((s4) GET_REGISTER(vsrc1) _cmp 0) { \
+ int branchOffset = (s2)FETCH(1); /* sign-extended */ \
+ ILOGV("|if-%s v%d,+0x%04x", (_opname), vsrc1, branchOffset); \
+ ILOGV("> branch taken"); \
+ if (branchOffset < 0) \
+ PERIODIC_CHECKS(kInterpEntryInstr, branchOffset); \
+ FINISH(branchOffset); \
+ } else { \
+ ILOGV("|if-%s v%d,-", (_opname), vsrc1); \
+ FINISH(2); \
+ }
+
+#define HANDLE_UNOP(_opcode, _opname, _pfx, _sfx, _type) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER##_type(vdst, _pfx GET_REGISTER##_type(vsrc1) _sfx); \
+ FINISH(1);
+
+#define HANDLE_OP_X_INT(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER(vsrc1); \
+ secondVal = GET_REGISTER(vsrc2); \
+ if (secondVal == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && secondVal == -1) { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ /* non-div/rem case */ \
+ SET_REGISTER(vdst, \
+ (s4) GET_REGISTER(vsrc1) _op (s4) GET_REGISTER(vsrc2)); \
+ } \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_SHX_INT(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER(vdst, \
+ _cast GET_REGISTER(vsrc1) _op (GET_REGISTER(vsrc2) & 0x1f)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT16(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vA, vB, #+CCCC*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ vsrc2 = FETCH(1); \
+ ILOGV("|%s-int/lit16 v%d,v%d,#+0x%04x", \
+ (_opname), vdst, vsrc1, vsrc2); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, result; \
+ firstVal = GET_REGISTER(vsrc1); \
+ if ((s2) vsrc2 == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && ((s2) vsrc2) == -1) { \
+ /* won't generate /lit16 instr for this; check anyway */ \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op (s2) vsrc2; \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ /* non-div/rem case */ \
+ SET_REGISTER(vdst, GET_REGISTER(vsrc1) _op (s2) vsrc2); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT8(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/) \
+ { \
+ u2 litInfo; \
+ vdst = INST_AA(inst); \
+ litInfo = FETCH(1); \
+ vsrc1 = litInfo & 0xff; \
+ vsrc2 = litInfo >> 8; /* constant */ \
+ ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x", \
+ (_opname), vdst, vsrc1, vsrc2); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, result; \
+ firstVal = GET_REGISTER(vsrc1); \
+ if ((s1) vsrc2 == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && ((s1) vsrc2) == -1) { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op ((s1) vsrc2); \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ SET_REGISTER(vdst, \
+ (s4) GET_REGISTER(vsrc1) _op (s1) vsrc2); \
+ } \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_SHX_INT_LIT8(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/) \
+ { \
+ u2 litInfo; \
+ vdst = INST_AA(inst); \
+ litInfo = FETCH(1); \
+ vsrc1 = litInfo & 0xff; \
+ vsrc2 = litInfo >> 8; /* constant */ \
+ ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x", \
+ (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER(vdst, \
+ _cast GET_REGISTER(vsrc1) _op (vsrc2 & 0x1f)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_INT_2ADDR(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER(vdst); \
+ secondVal = GET_REGISTER(vsrc1); \
+ if (secondVal == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && secondVal == -1) { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ SET_REGISTER(vdst, \
+ (s4) GET_REGISTER(vdst) _op (s4) GET_REGISTER(vsrc1)); \
+ } \
+ FINISH(1);
+
+#define HANDLE_OP_SHX_INT_2ADDR(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER(vdst, \
+ _cast GET_REGISTER(vdst) _op (GET_REGISTER(vsrc1) & 0x1f)); \
+ FINISH(1);
+
+#define HANDLE_OP_X_LONG(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ if (_chkdiv != 0) { \
+ s8 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER_WIDE(vsrc1); \
+ secondVal = GET_REGISTER_WIDE(vsrc2); \
+ if (secondVal == 0LL) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u8)firstVal == 0x8000000000000000ULL && \
+ secondVal == -1LL) \
+ { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER_WIDE(vdst, result); \
+ } else { \
+ SET_REGISTER_WIDE(vdst, \
+ (s8) GET_REGISTER_WIDE(vsrc1) _op (s8) GET_REGISTER_WIDE(vsrc2)); \
+ } \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_SHX_LONG(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER_WIDE(vdst, \
+ _cast GET_REGISTER_WIDE(vsrc1) _op (GET_REGISTER(vsrc2) & 0x3f)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_LONG_2ADDR(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ if (_chkdiv != 0) { \
+ s8 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER_WIDE(vdst); \
+ secondVal = GET_REGISTER_WIDE(vsrc1); \
+ if (secondVal == 0LL) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u8)firstVal == 0x8000000000000000ULL && \
+ secondVal == -1LL) \
+ { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER_WIDE(vdst, result); \
+ } else { \
+ SET_REGISTER_WIDE(vdst, \
+ (s8) GET_REGISTER_WIDE(vdst) _op (s8)GET_REGISTER_WIDE(vsrc1));\
+ } \
+ FINISH(1);
+
+#define HANDLE_OP_SHX_LONG_2ADDR(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER_WIDE(vdst, \
+ _cast GET_REGISTER_WIDE(vdst) _op (GET_REGISTER(vsrc1) & 0x3f)); \
+ FINISH(1);
+
+#define HANDLE_OP_X_FLOAT(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-float v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER_FLOAT(vdst, \
+ GET_REGISTER_FLOAT(vsrc1) _op GET_REGISTER_FLOAT(vsrc2)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_DOUBLE(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-double v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER_DOUBLE(vdst, \
+ GET_REGISTER_DOUBLE(vsrc1) _op GET_REGISTER_DOUBLE(vsrc2)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_FLOAT_2ADDR(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-float-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER_FLOAT(vdst, \
+ GET_REGISTER_FLOAT(vdst) _op GET_REGISTER_FLOAT(vsrc1)); \
+ FINISH(1);
+
+#define HANDLE_OP_X_DOUBLE_2ADDR(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-double-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER_DOUBLE(vdst, \
+ GET_REGISTER_DOUBLE(vdst) _op GET_REGISTER_DOUBLE(vsrc1)); \
+ FINISH(1);
+
+#define HANDLE_OP_AGET(_opcode, _opname, _type, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ ArrayObject* arrayObj; \
+ u2 arrayInfo; \
+ EXPORT_PC(); \
+ vdst = INST_AA(inst); \
+ arrayInfo = FETCH(1); \
+ vsrc1 = arrayInfo & 0xff; /* array ptr */ \
+ vsrc2 = arrayInfo >> 8; /* index */ \
+ ILOGV("|aget%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ arrayObj = (ArrayObject*) GET_REGISTER(vsrc1); \
+ if (!checkForNull((Object*) arrayObj)) \
+ GOTO_exceptionThrown(); \
+ if (GET_REGISTER(vsrc2) >= arrayObj->length) { \
+ LOGV("Invalid array access: %p %d (len=%d)\n", \
+ arrayObj, vsrc2, arrayObj->length); \
+ dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+ NULL); \
+ GOTO_exceptionThrown(); \
+ } \
+ SET_REGISTER##_regsize(vdst, \
+ ((_type*) arrayObj->contents)[GET_REGISTER(vsrc2)]); \
+ ILOGV("+ AGET[%d]=0x%x", GET_REGISTER(vsrc2), GET_REGISTER(vdst)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_APUT(_opcode, _opname, _type, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ ArrayObject* arrayObj; \
+ u2 arrayInfo; \
+ EXPORT_PC(); \
+ vdst = INST_AA(inst); /* AA: source value */ \
+ arrayInfo = FETCH(1); \
+ vsrc1 = arrayInfo & 0xff; /* BB: array ptr */ \
+ vsrc2 = arrayInfo >> 8; /* CC: index */ \
+ ILOGV("|aput%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ arrayObj = (ArrayObject*) GET_REGISTER(vsrc1); \
+ if (!checkForNull((Object*) arrayObj)) \
+ GOTO_exceptionThrown(); \
+ if (GET_REGISTER(vsrc2) >= arrayObj->length) { \
+ dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+ NULL); \
+ GOTO_exceptionThrown(); \
+ } \
+ ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+ ((_type*) arrayObj->contents)[GET_REGISTER(vsrc2)] = \
+ GET_REGISTER##_regsize(vdst); \
+ } \
+ FINISH(2);
+
+/*
+ * It's possible to get a bad value out of a field with sub-32-bit stores
+ * because the -quick versions always operate on 32 bits. Consider:
+ * short foo = -1 (sets a 32-bit register to 0xffffffff)
+ * iput-quick foo (writes all 32 bits to the field)
+ * short bar = 1 (sets a 32-bit register to 0x00000001)
+ * iput-short (writes the low 16 bits to the field)
+ * iget-quick foo (reads all 32 bits from the field, yielding 0xffff0001)
+ * This can only happen when optimized and non-optimized code has interleaved
+ * access to the same field. This is unlikely but possible.
+ *
+ * The easiest way to fix this is to always read/write 32 bits at a time. On
+ * a device with a 16-bit data bus this is sub-optimal. (The alternative
+ * approach is to have sub-int versions of iget-quick, but now we're wasting
+ * Dalvik instruction space and making it less likely that handler code will
+ * already be in the CPU i-cache.)
+ */
+#define HANDLE_IGET_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ InstField* ifield; \
+ Object* obj; \
+ EXPORT_PC(); \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|iget%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNull(obj)) \
+ GOTO_exceptionThrown(); \
+ ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref); \
+ if (ifield == NULL) { \
+ ifield = dvmResolveInstField(curMethod->clazz, ref); \
+ if (ifield == NULL) \
+ GOTO_exceptionThrown(); \
+ } \
+ SET_REGISTER##_regsize(vdst, \
+ dvmGetField##_ftype(obj, ifield->byteOffset)); \
+ ILOGV("+ IGET '%s'=0x%08llx", ifield->field.name, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_GET(&ifield->field); \
+ } \
+ FINISH(2);
+
+#define HANDLE_IGET_X_QUICK(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ Object* obj; \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field offset */ \
+ ILOGV("|iget%s-quick v%d,v%d,field@+%u", \
+ (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNullExportPC(obj, fp, pc)) \
+ GOTO_exceptionThrown(); \
+ SET_REGISTER##_regsize(vdst, dvmGetField##_ftype(obj, ref)); \
+ ILOGV("+ IGETQ %d=0x%08llx", ref, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_IPUT_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ InstField* ifield; \
+ Object* obj; \
+ EXPORT_PC(); \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|iput%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNull(obj)) \
+ GOTO_exceptionThrown(); \
+ ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref); \
+ if (ifield == NULL) { \
+ ifield = dvmResolveInstField(curMethod->clazz, ref); \
+ if (ifield == NULL) \
+ GOTO_exceptionThrown(); \
+ } \
+ dvmSetField##_ftype(obj, ifield->byteOffset, \
+ GET_REGISTER##_regsize(vdst)); \
+ ILOGV("+ IPUT '%s'=0x%08llx", ifield->field.name, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_PUT(&ifield->field); \
+ } \
+ FINISH(2);
+
+#define HANDLE_IPUT_X_QUICK(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ Object* obj; \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field offset */ \
+ ILOGV("|iput%s-quick v%d,v%d,field@0x%04x", \
+ (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNullExportPC(obj, fp, pc)) \
+ GOTO_exceptionThrown(); \
+ dvmSetField##_ftype(obj, ref, GET_REGISTER##_regsize(vdst)); \
+ ILOGV("+ IPUTQ %d=0x%08llx", ref, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ } \
+ FINISH(2);
+
+/*
+ * The JIT needs dvmDexGetResolvedField() to return non-null.
+ * Since we use the portable interpreter to build the trace, the extra
+ * checks in HANDLE_SGET_X and HANDLE_SPUT_X are not needed for mterp.
+ */
+#define HANDLE_SGET_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/) \
+ { \
+ StaticField* sfield; \
+ vdst = INST_AA(inst); \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|sget%s v%d,sfield@0x%04x", (_opname), vdst, ref); \
+ sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+ if (sfield == NULL) { \
+ EXPORT_PC(); \
+ sfield = dvmResolveStaticField(curMethod->clazz, ref); \
+ if (sfield == NULL) \
+ GOTO_exceptionThrown(); \
+ if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) { \
+ ABORT_JIT_TSELECT(); \
+ } \
+ } \
+ SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield)); \
+ ILOGV("+ SGET '%s'=0x%08llx", \
+ sfield->field.name, (u8)GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_GET(&sfield->field); \
+ } \
+ FINISH(2);
+
+#define HANDLE_SPUT_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/) \
+ { \
+ StaticField* sfield; \
+ vdst = INST_AA(inst); \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|sput%s v%d,sfield@0x%04x", (_opname), vdst, ref); \
+ sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+ if (sfield == NULL) { \
+ EXPORT_PC(); \
+ sfield = dvmResolveStaticField(curMethod->clazz, ref); \
+ if (sfield == NULL) \
+ GOTO_exceptionThrown(); \
+ if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) { \
+ ABORT_JIT_TSELECT(); \
+ } \
+ } \
+ dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst)); \
+ ILOGV("+ SPUT '%s'=0x%08llx", \
+ sfield->field.name, (u8)GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_PUT(&sfield->field); \
+ } \
+ FINISH(2);
+
+/* File: portable/entry.c */
+/*
+ * Main interpreter loop.
+ *
+ * This was written with an ARM implementation in mind.
+ */
+bool INTERP_FUNC_NAME(Thread* self, InterpState* interpState)
+{
+#if defined(EASY_GDB)
+ StackSaveArea* debugSaveArea = SAVEAREA_FROM_FP(self->curFrame);
+#endif
+#if INTERP_TYPE == INTERP_DBG
+ bool debugIsMethodEntry = false;
+ debugIsMethodEntry = interpState->debugIsMethodEntry;
+#endif
+#if defined(WITH_TRACKREF_CHECKS)
+ int debugTrackedRefStart = interpState->debugTrackedRefStart;
+#endif
+ DvmDex* methodClassDex; // curMethod->clazz->pDvmDex
+ JValue retval;
+
+ /* core state */
+ const Method* curMethod; // method we're interpreting
+ const u2* pc; // program counter
+ u4* fp; // frame pointer
+ u2 inst; // current instruction
+ /* instruction decoding */
+ u2 ref; // 16-bit quantity fetched directly
+ u2 vsrc1, vsrc2, vdst; // usually used for register indexes
+ /* method call setup */
+ const Method* methodToCall;
+ bool methodCallRange;
+
+
+#if defined(THREADED_INTERP)
+ /* static computed goto table */
+ DEFINE_GOTO_TABLE(handlerTable);
+#endif
+
+#if defined(WITH_JIT)
+#if 0
+ LOGD("*DebugInterp - entrypoint is %d, tgt is 0x%x, %s\n",
+ interpState->entryPoint,
+ interpState->pc,
+ interpState->method->name);
+#endif
+#if INTERP_TYPE == INTERP_DBG
+ const ClassObject* callsiteClass = NULL;
+
+#if defined(WITH_SELF_VERIFICATION)
+ if (interpState->jitState != kJitSelfVerification) {
+ interpState->self->shadowSpace->jitExitState = kSVSIdle;
+ }
+#endif
+
+ /* Check to see if we've got a trace selection request. */
+ if (
+ /*
+ * Only perform dvmJitCheckTraceRequest if the entry point is
+ * EntryInstr and the jit state is either kJitTSelectRequest or
+ * kJitTSelectRequestHot. If debugger/profiler happens to be attached,
+ * dvmJitCheckTraceRequest will change the jitState to kJitDone but
+ * but stay in the dbg interpreter.
+ */
+ (interpState->entryPoint == kInterpEntryInstr) &&
+ (interpState->jitState == kJitTSelectRequest ||
+ interpState->jitState == kJitTSelectRequestHot) &&
+ dvmJitCheckTraceRequest(self, interpState)) {
+ interpState->nextMode = INTERP_STD;
+ //LOGD("Invalid trace request, exiting\n");
+ return true;
+ }
+#endif /* INTERP_TYPE == INTERP_DBG */
+#endif /* WITH_JIT */
+
+ /* copy state in */
+ curMethod = interpState->method;
+ pc = interpState->pc;
+ fp = interpState->fp;
+ retval = interpState->retval; /* only need for kInterpEntryReturn? */
+
+ methodClassDex = curMethod->clazz->pDvmDex;
+
+ LOGVV("threadid=%d: entry(%s) %s.%s pc=0x%x fp=%p ep=%d\n",
+ self->threadId, (interpState->nextMode == INTERP_STD) ? "STD" : "DBG",
+ curMethod->clazz->descriptor, curMethod->name, pc - curMethod->insns,
+ fp, interpState->entryPoint);
+
+ /*
+ * DEBUG: scramble this to ensure we're not relying on it.
+ */
+ methodToCall = (const Method*) -1;
+
+#if INTERP_TYPE == INTERP_DBG
+ if (debugIsMethodEntry) {
+ ILOGD("|-- Now interpreting %s.%s", curMethod->clazz->descriptor,
+ curMethod->name);
+ DUMP_REGS(curMethod, interpState->fp, false);
+ }
+#endif
+
+ switch (interpState->entryPoint) {
+ case kInterpEntryInstr:
+ /* just fall through to instruction loop or threaded kickstart */
+ break;
+ case kInterpEntryReturn:
+ CHECK_JIT_VOID();
+ goto returnFromMethod;
+ case kInterpEntryThrow:
+ goto exceptionThrown;
+ default:
+ dvmAbort();
+ }
+
+#ifdef THREADED_INTERP
+ FINISH(0); /* fetch and execute first instruction */
+#else
+ while (1) {
+ CHECK_DEBUG_AND_PROF(); /* service debugger and profiling */
+ CHECK_TRACKED_REFS(); /* check local reference tracking */
+
+ /* fetch the next 16 bits from the instruction stream */
+ inst = FETCH(0);
+
+ switch (INST_INST(inst)) {
+#endif
+
+/*--- start of opcodes ---*/
+
+/* File: c/OP_NOP.c */
+HANDLE_OPCODE(OP_NOP)
+ FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE.c */
+HANDLE_OPCODE(OP_MOVE /*vA, vB*/)
+ vdst = INST_A(inst);
+ vsrc1 = INST_B(inst);
+ ILOGV("|move%s v%d,v%d %s(v%d=0x%08x)",
+ (INST_INST(inst) == OP_MOVE) ? "" : "-object", vdst, vsrc1,
+ kSpacing, vdst, GET_REGISTER(vsrc1));
+ SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+ FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE_FROM16.c */
+HANDLE_OPCODE(OP_MOVE_FROM16 /*vAA, vBBBB*/)
+ vdst = INST_AA(inst);
+ vsrc1 = FETCH(1);
+ ILOGV("|move%s/from16 v%d,v%d %s(v%d=0x%08x)",
+ (INST_INST(inst) == OP_MOVE_FROM16) ? "" : "-object", vdst, vsrc1,
+ kSpacing, vdst, GET_REGISTER(vsrc1));
+ SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+ FINISH(2);
+OP_END
+
+/* File: c/OP_MOVE_16.c */
+HANDLE_OPCODE(OP_MOVE_16 /*vAAAA, vBBBB*/)
+ vdst = FETCH(1);
+ vsrc1 = FETCH(2);
+ ILOGV("|move%s/16 v%d,v%d %s(v%d=0x%08x)",
+ (INST_INST(inst) == OP_MOVE_16) ? "" : "-object", vdst, vsrc1,
+ kSpacing, vdst, GET_REGISTER(vsrc1));
+ SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+ FINISH(3);
+OP_END
+
+/* File: c/OP_MOVE_WIDE.c */
+HANDLE_OPCODE(OP_MOVE_WIDE /*vA, vB*/)
+ /* IMPORTANT: must correctly handle overlapping registers, e.g. both
+ * "move-wide v6, v7" and "move-wide v7, v6" */
+ vdst = INST_A(inst);
+ vsrc1 = INST_B(inst);
+ ILOGV("|move-wide v%d,v%d %s(v%d=0x%08llx)", vdst, vsrc1,
+ kSpacing+5, vdst, GET_REGISTER_WIDE(vsrc1));
+ SET_REGISTER_WIDE(vdst, GET_REGISTER_WIDE(vsrc1));
+ FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE_WIDE_FROM16.c */
+HANDLE_OPCODE(OP_MOVE_WIDE_FROM16 /*vAA, vBBBB*/)
+ vdst = INST_AA(inst);
+ vsrc1 = FETCH(1);
+ ILOGV("|move-wide/from16 v%d,v%d (v%d=0x%08llx)", vdst, vsrc1,
+ vdst, GET_REGISTER_WIDE(vsrc1));
+ SET_REGISTER_WIDE(vdst, GET_REGISTER_WIDE(vsrc1));
+ FINISH(2);
+OP_END
+
+/* File: c/OP_MOVE_WIDE_16.c */
+HANDLE_OPCODE(OP_MOVE_WIDE_16 /*vAAAA, vBBBB*/)
+ vdst = FETCH(1);
+ vsrc1 = FETCH(2);
+ ILOGV("|move-wide/16 v%d,v%d %s(v%d=0x%08llx)", vdst, vsrc1,
+ kSpacing+8, vdst, GET_REGISTER_WIDE(vsrc1));
+ SET_REGISTER_WIDE(vdst, GET_REGISTER_WIDE(vsrc1));
+ FINISH(3);
+OP_END
+
+/* File: c/OP_MOVE_OBJECT.c */
+/* File: c/OP_MOVE.c */
+HANDLE_OPCODE(OP_MOVE_OBJECT /*vA, vB*/)
+ vdst = INST_A(inst);
+ vsrc1 = INST_B(inst);
+ ILOGV("|move%s v%d,v%d %s(v%d=0x%08x)",
+ (INST_INST(inst) == OP_MOVE) ? "" : "-object", vdst, vsrc1,
+ kSpacing, vdst, GET_REGISTER(vsrc1));
+ SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+ FINISH(1);
+OP_END
+
+
+/* File: c/OP_MOVE_OBJECT_FROM16.c */
+/* File: c/OP_MOVE_FROM16.c */
+HANDLE_OPCODE(OP_MOVE_OBJECT_FROM16 /*vAA, vBBBB*/)
+ vdst = INST_AA(inst);
+ vsrc1 = FETCH(1);
+ ILOGV("|move%s/from16 v%d,v%d %s(v%d=0x%08x)",
+ (INST_INST(inst) == OP_MOVE_FROM16) ? "" : "-object", vdst, vsrc1,
+ kSpacing, vdst, GET_REGISTER(vsrc1));
+ SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+ FINISH(2);
+OP_END
+
+
+/* File: c/OP_MOVE_OBJECT_16.c */
+/* File: c/OP_MOVE_16.c */
+HANDLE_OPCODE(OP_MOVE_OBJECT_16 /*vAAAA, vBBBB*/)
+ vdst = FETCH(1);
+ vsrc1 = FETCH(2);
+ ILOGV("|move%s/16 v%d,v%d %s(v%d=0x%08x)",
+ (INST_INST(inst) == OP_MOVE_16) ? "" : "-object", vdst, vsrc1,
+ kSpacing, vdst, GET_REGISTER(vsrc1));
+ SET_REGISTER(vdst, GET_REGISTER(vsrc1));
+ FINISH(3);
+OP_END
+
+
+/* File: c/OP_MOVE_RESULT.c */
+HANDLE_OPCODE(OP_MOVE_RESULT /*vAA*/)
+ vdst = INST_AA(inst);
+ ILOGV("|move-result%s v%d %s(v%d=0x%08x)",
+ (INST_INST(inst) == OP_MOVE_RESULT) ? "" : "-object",
+ vdst, kSpacing+4, vdst,retval.i);
+ SET_REGISTER(vdst, retval.i);
+ FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE_RESULT_WIDE.c */
+HANDLE_OPCODE(OP_MOVE_RESULT_WIDE /*vAA*/)
+ vdst = INST_AA(inst);
+ ILOGV("|move-result-wide v%d %s(0x%08llx)", vdst, kSpacing, retval.j);
+ SET_REGISTER_WIDE(vdst, retval.j);
+ FINISH(1);
+OP_END
+
+/* File: c/OP_MOVE_RESULT_OBJECT.c */
+/* File: c/OP_MOVE_RESULT.c */
+HANDLE_OPCODE(OP_MOVE_RESULT_OBJECT /*vAA*/)
+ vdst = INST_AA(inst);
+ ILOGV("|move-result%s v%d %s(v%d=0x%08x)",
+ (INST_INST(inst) == OP_MOVE_RESULT) ? "" : "-object",
+ vdst, kSpacing+4, vdst,retval.i);
+ SET_REGISTER(vdst, retval.i);
+ FINISH(1);
+OP_END
+
+
+/* File: c/OP_MOVE_EXCEPTION.c */
+HANDLE_OPCODE(OP_MOVE_EXCEPTION /*vAA*/)
+ vdst = INST_AA(inst);
+ ILOGV("|move-exception v%d", vdst);
+ assert(self->exception != NULL);
+ SET_REGISTER(vdst, (u4)self->exception);
+ dvmClearException(self);
+ FINISH(1);
+OP_END
+
+/* File: c/OP_RETURN_VOID.c */
+HANDLE_OPCODE(OP_RETURN_VOID /**/)
+ ILOGV("|return-void");
+#ifndef NDEBUG
+ retval.j = 0xababababULL; // placate valgrind
+#endif
+ GOTO_returnFromMethod();
+OP_END
+
+/* File: c/OP_RETURN.c */
+HANDLE_OPCODE(OP_RETURN /*vAA*/)
+ vsrc1 = INST_AA(inst);
+ ILOGV("|return%s v%d",
+ (INST_INST(inst) == OP_RETURN) ? "" : "-object", vsrc1);
+ retval.i = GET_REGISTER(vsrc1);
+ GOTO_returnFromMethod();
+OP_END
+
+/* File: c/OP_RETURN_WIDE.c */
+HANDLE_OPCODE(OP_RETURN_WIDE /*vAA*/)
+ vsrc1 = INST_AA(inst);
+ ILOGV("|return-wide v%d", vsrc1);
+ retval.j = GET_REGISTER_WIDE(vsrc1);
+ GOTO_returnFromMethod();
+OP_END
+
+/* File: c/OP_RETURN_OBJECT.c */
+/* File: c/OP_RETURN.c */
+HANDLE_OPCODE(OP_RETURN_OBJECT /*vAA*/)
+ vsrc1 = INST_AA(inst);
+ ILOGV("|return%s v%d",
+ (INST_INST(inst) == OP_RETURN) ? "" : "-object", vsrc1);
+ retval.i = GET_REGISTER(vsrc1);
+ GOTO_returnFromMethod();
+OP_END
+
+
+/* File: c/OP_CONST_4.c */
+HANDLE_OPCODE(OP_CONST_4 /*vA, #+B*/)
+ {
+ s4 tmp;
+
+ vdst = INST_A(inst);
+ tmp = (s4) (INST_B(inst) << 28) >> 28; // sign extend 4-bit value
+ ILOGV("|const/4 v%d,#0x%02x", vdst, (s4)tmp);
+ SET_REGISTER(vdst, tmp);
+ }
+ FINISH(1);
+OP_END
+
+/* File: c/OP_CONST_16.c */
+HANDLE_OPCODE(OP_CONST_16 /*vAA, #+BBBB*/)
+ vdst = INST_AA(inst);
+ vsrc1 = FETCH(1);
+ ILOGV("|const/16 v%d,#0x%04x", vdst, (s2)vsrc1);
+ SET_REGISTER(vdst, (s2) vsrc1);
+ FINISH(2);
+OP_END
+
+/* File: c/OP_CONST.c */
+HANDLE_OPCODE(OP_CONST /*vAA, #+BBBBBBBB*/)
+ {
+ u4 tmp;
+
+ vdst = INST_AA(inst);
+ tmp = FETCH(1);
+ tmp |= (u4)FETCH(2) << 16;
+ ILOGV("|const v%d,#0x%08x", vdst, tmp);
+ SET_REGISTER(vdst, tmp);
+ }
+ FINISH(3);
+OP_END
+
+/* File: c/OP_CONST_HIGH16.c */
+HANDLE_OPCODE(OP_CONST_HIGH16 /*vAA, #+BBBB0000*/)
+ vdst = INST_AA(inst);
+ vsrc1 = FETCH(1);
+ ILOGV("|const/high16 v%d,#0x%04x0000", vdst, vsrc1);
+ SET_REGISTER(vdst, vsrc1 << 16);
+ FINISH(2);
+OP_END
+
+/* File: c/OP_CONST_WIDE_16.c */
+HANDLE_OPCODE(OP_CONST_WIDE_16 /*vAA, #+BBBB*/)
+ vdst = INST_AA(inst);
+ vsrc1 = FETCH(1);
+ ILOGV("|const-wide/16 v%d,#0x%04x", vdst, (s2)vsrc1);
+ SET_REGISTER_WIDE(vdst, (s2)vsrc1);
+ FINISH(2);
+OP_END
+
+/* File: c/OP_CONST_WIDE_32.c */
+HANDLE_OPCODE(OP_CONST_WIDE_32 /*vAA, #+BBBBBBBB*/)
+ {
+ u4 tmp;
+
+ vdst = INST_AA(inst);
+ tmp = FETCH(1);
+ tmp |= (u4)FETCH(2) << 16;
+ ILOGV("|const-wide/32 v%d,#0x%08x", vdst, tmp);
+ SET_REGISTER_WIDE(vdst, (s4) tmp);
+ }
+ FINISH(3);
+OP_END
+
+/* File: c/OP_CONST_WIDE.c */
+HANDLE_OPCODE(OP_CONST_WIDE /*vAA, #+BBBBBBBBBBBBBBBB*/)
+ {
+ u8 tmp;
+
+ vdst = INST_AA(inst);
+ tmp = FETCH(1);
+ tmp |= (u8)FETCH(2) << 16;
+ tmp |= (u8)FETCH(3) << 32;
+ tmp |= (u8)FETCH(4) << 48;
+ ILOGV("|const-wide v%d,#0x%08llx", vdst, tmp);
+ SET_REGISTER_WIDE(vdst, tmp);
+ }
+ FINISH(5);
+OP_END
+
+/* File: c/OP_CONST_WIDE_HIGH16.c */
+HANDLE_OPCODE(OP_CONST_WIDE_HIGH16 /*vAA, #+BBBB000000000000*/)
+ vdst = INST_AA(inst);
+ vsrc1 = FETCH(1);
+ ILOGV("|const-wide/high16 v%d,#0x%04x000000000000", vdst, vsrc1);
+ SET_REGISTER_WIDE(vdst, ((u8) vsrc1) << 48);
+ FINISH(2);
+OP_END
+
+/* File: c/OP_CONST_STRING.c */
+HANDLE_OPCODE(OP_CONST_STRING /*vAA, string@BBBB*/)
+ {
+ StringObject* strObj;
+
+ vdst = INST_AA(inst);
+ ref = FETCH(1);
+ ILOGV("|const-string v%d string@0x%04x", vdst, ref);
+ strObj = dvmDexGetResolvedString(methodClassDex, ref);
+ if (strObj == NULL) {
+ EXPORT_PC();
+ strObj = dvmResolveString(curMethod->clazz, ref);
+ if (strObj == NULL)
+ GOTO_exceptionThrown();
+ }
+ SET_REGISTER(vdst, (u4) strObj);
+ }
+ FINISH(2);
+OP_END
+
+/* File: c/OP_CONST_STRING_JUMBO.c */
+HANDLE_OPCODE(OP_CONST_STRING_JUMBO /*vAA, string@BBBBBBBB*/)
+ {
+ StringObject* strObj;
+ u4 tmp;
+
+ vdst = INST_AA(inst);
+ tmp = FETCH(1);
+ tmp |= (u4)FETCH(2) << 16;
+ ILOGV("|const-string/jumbo v%d string@0x%08x", vdst, tmp);
+ strObj = dvmDexGetResolvedString(methodClassDex, tmp);
+ if (strObj == NULL) {
+ EXPORT_PC();
+ strObj = dvmResolveString(curMethod->clazz, tmp);
+ if (strObj == NULL)
+ GOTO_exceptionThrown();
+ }
+ SET_REGISTER(vdst, (u4) strObj);
+ }
+ FINISH(3);
+OP_END
+
+/* File: c/OP_CONST_CLASS.c */
+HANDLE_OPCODE(OP_CONST_CLASS /*vAA, class@BBBB*/)
+ {
+ ClassObject* clazz;
+
+ vdst = INST_AA(inst);
+ ref = FETCH(1);
+ ILOGV("|const-class v%d class@0x%04x", vdst, ref);
+ clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+ if (clazz == NULL) {
+ EXPORT_PC();
+ clazz = dvmResolveClass(curMethod->clazz, ref, true);
+ if (clazz == NULL)
+ GOTO_exceptionThrown();
+ }
+ SET_REGISTER(vdst, (u4) clazz);
+ }
+ FINISH(2);
+OP_END
+
+/* File: c/OP_MONITOR_ENTER.c */
+HANDLE_OPCODE(OP_MONITOR_ENTER /*vAA*/)
+ {
+ Object* obj;
+
+ vsrc1 = INST_AA(inst);
+ ILOGV("|monitor-enter v%d %s(0x%08x)",
+ vsrc1, kSpacing+6, GET_REGISTER(vsrc1));
+ obj = (Object*)GET_REGISTER(vsrc1);
+ if (!checkForNullExportPC(obj, fp, pc))
+ GOTO_exceptionThrown();
+ ILOGV("+ locking %p %s\n", obj, obj->clazz->descriptor);
+ EXPORT_PC(); /* need for precise GC, also WITH_MONITOR_TRACKING */
+ dvmLockObject(self, obj);
+#ifdef WITH_DEADLOCK_PREDICTION
+ if (dvmCheckException(self))
+ GOTO_exceptionThrown();
+#endif
+ }
+ FINISH(1);
+OP_END
+
+/* File: c/OP_MONITOR_EXIT.c */
+HANDLE_OPCODE(OP_MONITOR_EXIT /*vAA*/)
+ {
+ Object* obj;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst);
+ ILOGV("|monitor-exit v%d %s(0x%08x)",
+ vsrc1, kSpacing+5, GET_REGISTER(vsrc1));
+ obj = (Object*)GET_REGISTER(vsrc1);
+ if (!checkForNull(obj)) {
+ /*
+ * The exception needs to be processed at the *following*
+ * instruction, not the current instruction (see the Dalvik
+ * spec). Because we're jumping to an exception handler,
+ * we're not actually at risk of skipping an instruction
+ * by doing so.
+ */
+ ADJUST_PC(1); /* monitor-exit width is 1 */
+ GOTO_exceptionThrown();
+ }
+ ILOGV("+ unlocking %p %s\n", obj, obj->clazz->descriptor);
+ if (!dvmUnlockObject(self, obj)) {
+ assert(dvmCheckException(self));
+ ADJUST_PC(1);
+ GOTO_exceptionThrown();
+ }
+ }
+ FINISH(1);
+OP_END
+
+/* File: c/OP_CHECK_CAST.c */
+HANDLE_OPCODE(OP_CHECK_CAST /*vAA, class@BBBB*/)
+ {
+ ClassObject* clazz;
+ Object* obj;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst);
+ ref = FETCH(1); /* class to check against */
+ ILOGV("|check-cast v%d,class@0x%04x", vsrc1, ref);
+
+ obj = (Object*)GET_REGISTER(vsrc1);
+ if (obj != NULL) {
+#if defined(WITH_EXTRA_OBJECT_VALIDATION)
+ if (!checkForNull(obj))
+ GOTO_exceptionThrown();
+#endif
+ clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+ if (clazz == NULL) {
+ clazz = dvmResolveClass(curMethod->clazz, ref, false);
+ if (clazz == NULL)
+ GOTO_exceptionThrown();
+ }
+ if (!dvmInstanceof(obj->clazz, clazz)) {
+ dvmThrowExceptionWithClassMessage(
+ "Ljava/lang/ClassCastException;", obj->clazz->descriptor);
+ GOTO_exceptionThrown();
+ }
+ }
+ }
+ FINISH(2);
+OP_END
+
+/* File: c/OP_INSTANCE_OF.c */
+HANDLE_OPCODE(OP_INSTANCE_OF /*vA, vB, class@CCCC*/)
+ {
+ ClassObject* clazz;
+ Object* obj;
+
+ vdst = INST_A(inst);
+ vsrc1 = INST_B(inst); /* object to check */
+ ref = FETCH(1); /* class to check against */
+ ILOGV("|instance-of v%d,v%d,class@0x%04x", vdst, vsrc1, ref);
+
+ obj = (Object*)GET_REGISTER(vsrc1);
+ if (obj == NULL) {
+ SET_REGISTER(vdst, 0);
+ } else {
+#if defined(WITH_EXTRA_OBJECT_VALIDATION)
+ if (!checkForNullExportPC(obj, fp, pc))
+ GOTO_exceptionThrown();
+#endif
+ clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+ if (clazz == NULL) {
+ EXPORT_PC();
+ clazz = dvmResolveClass(curMethod->clazz, ref, true);
+ if (clazz == NULL)
+ GOTO_exceptionThrown();
+ }
+ SET_REGISTER(vdst, dvmInstanceof(obj->clazz, clazz));
+ }
+ }
+ FINISH(2);
+OP_END
+
+/* File: c/OP_ARRAY_LENGTH.c */
+HANDLE_OPCODE(OP_ARRAY_LENGTH /*vA, vB*/)
+ {
+ ArrayObject* arrayObj;
+
+ vdst = INST_A(inst);
+ vsrc1 = INST_B(inst);
+ arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);
+ ILOGV("|array-length v%d,v%d (%p)", vdst, vsrc1, arrayObj);
+ if (!checkForNullExportPC((Object*) arrayObj, fp, pc))
+ GOTO_exceptionThrown();
+ /* verifier guarantees this is an array reference */
+ SET_REGISTER(vdst, arrayObj->length);
+ }
+ FINISH(1);
+OP_END
+
+/* File: c/OP_NEW_INSTANCE.c */
+HANDLE_OPCODE(OP_NEW_INSTANCE /*vAA, class@BBBB*/)
+ {
+ ClassObject* clazz;
+ Object* newObj;
+
+ EXPORT_PC();
+
+ vdst = INST_AA(inst);
+ ref = FETCH(1);
+ ILOGV("|new-instance v%d,class@0x%04x", vdst, ref);
+ clazz = dvmDexGetResolvedClass(methodClassDex, ref);
+ if (clazz == NULL) {
+ clazz = dvmResolveClass(curMethod->clazz, ref, false);
+ if (clazz == NULL)
+ GOTO_exceptionThrown();
+ }
+
+ if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))
+ GOTO_exceptionThrown();
+
+ /*
+ * The JIT needs dvmDexGetResolvedClass() to return non-null.
+ * Since we use the portable interpreter to build the trace, this extra
+ * check is not needed for mterp.
+ */
+ if (!dvmDexGetResolvedClass(methodClassDex, ref)) {
+ /* Class initialization is still ongoing - abandon the trace */
+ ABORT_JIT_TSELECT();
+ }
+
+ /*
+ * Verifier now tests for interface/abstract class.
+ */
+ //if (dvmIsInterfaceClass(clazz) || dvmIsAbstractClass(clazz)) {
+ // dvmThrowExceptionWithClassMessage("Ljava/lang/InstantiationError;",
+ // clazz->descriptor);
+ // GOTO_exceptionThrown();
+ //}
+ newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+ if (newObj == NULL)
+ GOTO_exceptionThrown();
+ SET_REGISTER(vdst, (u4) newObj);
+ }
+ FINISH(2);
+OP_END
+
+/* File: c/OP_NEW_ARRAY.c */
+HANDLE_OPCODE(OP_NEW_ARRAY /*vA, vB, class@CCCC*/)
+ {
+ ClassObject* arrayClass;
+ ArrayObject* newArray;
+ s4 length;
+
+ EXPORT_PC();
+
+ vdst = INST_A(inst);
+ vsrc1 = INST_B(inst); /* length reg */
+ ref = FETCH(1);
+ ILOGV("|new-array v%d,v%d,class@0x%04x (%d elements)",
+ vdst, vsrc1, ref, (s4) GET_REGISTER(vsrc1));
+ length = (s4) GET_REGISTER(vsrc1);
+ if (length < 0) {
+ dvmThrowException("Ljava/lang/NegativeArraySizeException;", NULL);
+ GOTO_exceptionThrown();
+ }
+ arrayClass = dvmDexGetResolvedClass(methodClassDex, ref);
+ if (arrayClass == NULL) {
+ arrayClass = dvmResolveClass(curMethod->clazz, ref, false);
+ if (arrayClass == NULL)
+ GOTO_exceptionThrown();
+ }
+ /* verifier guarantees this is an array class */
+ assert(dvmIsArrayClass(arrayClass));
+ assert(dvmIsClassInitialized(arrayClass));
+
+ newArray = dvmAllocArrayByClass(arrayClass, length, ALLOC_DONT_TRACK);
+ if (newArray == NULL)
+ GOTO_exceptionThrown();
+ SET_REGISTER(vdst, (u4) newArray);
+ }
+ FINISH(2);
+OP_END
+
+/* File: c/OP_FILLED_NEW_ARRAY.c */
+HANDLE_OPCODE(OP_FILLED_NEW_ARRAY /*vB, {vD, vE, vF, vG, vA}, class@CCCC*/)
+ GOTO_invoke(filledNewArray, false);
+OP_END
+
+/* File: c/OP_FILLED_NEW_ARRAY_RANGE.c */
+HANDLE_OPCODE(OP_FILLED_NEW_ARRAY_RANGE /*{vCCCC..v(CCCC+AA-1)}, class@BBBB*/)
+ GOTO_invoke(filledNewArray, true);
+OP_END
+
+/* File: c/OP_FILL_ARRAY_DATA.c */
+HANDLE_OPCODE(OP_FILL_ARRAY_DATA) /*vAA, +BBBBBBBB*/
+ {
+ const u2* arrayData;
+ s4 offset;
+ ArrayObject* arrayObj;
+
+ EXPORT_PC();
+ vsrc1 = INST_AA(inst);
+ offset = FETCH(1) | (((s4) FETCH(2)) << 16);
+ ILOGV("|fill-array-data v%d +0x%04x", vsrc1, offset);
+ arrayData = pc + offset; // offset in 16-bit units
+#ifndef NDEBUG
+ if (arrayData < curMethod->insns ||
+ arrayData >= curMethod->insns + dvmGetMethodInsnsSize(curMethod))
+ {
+ /* should have been caught in verifier */
+ dvmThrowException("Ljava/lang/InternalError;",
+ "bad fill array data");
+ GOTO_exceptionThrown();
+ }
+#endif
+ arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);
+ if (!dvmInterpHandleFillArrayData(arrayObj, arrayData)) {
+ GOTO_exceptionThrown();
+ }
+ FINISH(3);
+ }
+OP_END
+
+/* File: c/OP_THROW.c */
+HANDLE_OPCODE(OP_THROW /*vAA*/)
+ {
+ Object* obj;
+
+ /*
+ * We don't create an exception here, but the process of searching
+ * for a catch block can do class lookups and throw exceptions.
+ * We need to update the saved PC.
+ */
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst);
+ ILOGV("|throw v%d (%p)", vsrc1, (void*)GET_REGISTER(vsrc1));
+ obj = (Object*) GET_REGISTER(vsrc1);
+ if (!checkForNull(obj)) {
+ /* will throw a null pointer exception */
+ LOGVV("Bad exception\n");
+ } else {
+ /* use the requested exception */
+ dvmSetException(self, obj);
+ }
+ GOTO_exceptionThrown();
+ }
+OP_END
+
+/* File: c/OP_GOTO.c */
+HANDLE_OPCODE(OP_GOTO /*+AA*/)
+ vdst = INST_AA(inst);
+ if ((s1)vdst < 0)
+ ILOGV("|goto -0x%02x", -((s1)vdst));
+ else
+ ILOGV("|goto +0x%02x", ((s1)vdst));
+ ILOGV("> branch taken");
+ if ((s1)vdst < 0)
+ PERIODIC_CHECKS(kInterpEntryInstr, (s1)vdst);
+ FINISH((s1)vdst);
+OP_END
+
+/* File: c/OP_GOTO_16.c */
+HANDLE_OPCODE(OP_GOTO_16 /*+AAAA*/)
+ {
+ s4 offset = (s2) FETCH(1); /* sign-extend next code unit */
+
+ if (offset < 0)
+ ILOGV("|goto/16 -0x%04x", -offset);
+ else
+ ILOGV("|goto/16 +0x%04x", offset);
+ ILOGV("> branch taken");
+ if (offset < 0)
+ PERIODIC_CHECKS(kInterpEntryInstr, offset);
+ FINISH(offset);
+ }
+OP_END
+
+/* File: c/OP_GOTO_32.c */
+HANDLE_OPCODE(OP_GOTO_32 /*+AAAAAAAA*/)
+ {
+ s4 offset = FETCH(1); /* low-order 16 bits */
+ offset |= ((s4) FETCH(2)) << 16; /* high-order 16 bits */
+
+ if (offset < 0)
+ ILOGV("|goto/32 -0x%08x", -offset);
+ else
+ ILOGV("|goto/32 +0x%08x", offset);
+ ILOGV("> branch taken");
+ if (offset <= 0) /* allowed to branch to self */
+ PERIODIC_CHECKS(kInterpEntryInstr, offset);
+ FINISH(offset);
+ }
+OP_END
+
+/* File: c/OP_PACKED_SWITCH.c */
+HANDLE_OPCODE(OP_PACKED_SWITCH /*vAA, +BBBB*/)
+ {
+ const u2* switchData;
+ u4 testVal;
+ s4 offset;
+
+ vsrc1 = INST_AA(inst);
+ offset = FETCH(1) | (((s4) FETCH(2)) << 16);
+ ILOGV("|packed-switch v%d +0x%04x", vsrc1, vsrc2);
+ switchData = pc + offset; // offset in 16-bit units
+#ifndef NDEBUG
+ if (switchData < curMethod->insns ||
+ switchData >= curMethod->insns + dvmGetMethodInsnsSize(curMethod))
+ {
+ /* should have been caught in verifier */
+ EXPORT_PC();
+ dvmThrowException("Ljava/lang/InternalError;", "bad packed switch");
+ GOTO_exceptionThrown();
+ }
+#endif
+ testVal = GET_REGISTER(vsrc1);
+
+ offset = dvmInterpHandlePackedSwitch(switchData, testVal);
+ ILOGV("> branch taken (0x%04x)\n", offset);
+ if (offset <= 0) /* uncommon */
+ PERIODIC_CHECKS(kInterpEntryInstr, offset);
+ FINISH(offset);
+ }
+OP_END
+
+/* File: c/OP_SPARSE_SWITCH.c */
+HANDLE_OPCODE(OP_SPARSE_SWITCH /*vAA, +BBBB*/)
+ {
+ const u2* switchData;
+ u4 testVal;
+ s4 offset;
+
+ vsrc1 = INST_AA(inst);
+ offset = FETCH(1) | (((s4) FETCH(2)) << 16);
+ ILOGV("|sparse-switch v%d +0x%04x", vsrc1, vsrc2);
+ switchData = pc + offset; // offset in 16-bit units
+#ifndef NDEBUG
+ if (switchData < curMethod->insns ||
+ switchData >= curMethod->insns + dvmGetMethodInsnsSize(curMethod))
+ {
+ /* should have been caught in verifier */
+ EXPORT_PC();
+ dvmThrowException("Ljava/lang/InternalError;", "bad sparse switch");
+ GOTO_exceptionThrown();
+ }
+#endif
+ testVal = GET_REGISTER(vsrc1);
+
+ offset = dvmInterpHandleSparseSwitch(switchData, testVal);
+ ILOGV("> branch taken (0x%04x)\n", offset);
+ if (offset <= 0) /* uncommon */
+ PERIODIC_CHECKS(kInterpEntryInstr, offset);
+ FINISH(offset);
+ }
+OP_END
+
+/* File: c/OP_CMPL_FLOAT.c */
+HANDLE_OP_CMPX(OP_CMPL_FLOAT, "l-float", float, _FLOAT, -1)
+OP_END
+
+/* File: c/OP_CMPG_FLOAT.c */
+HANDLE_OP_CMPX(OP_CMPG_FLOAT, "g-float", float, _FLOAT, 1)
+OP_END
+
+/* File: c/OP_CMPL_DOUBLE.c */
+HANDLE_OP_CMPX(OP_CMPL_DOUBLE, "l-double", double, _DOUBLE, -1)
+OP_END
+
+/* File: c/OP_CMPG_DOUBLE.c */
+HANDLE_OP_CMPX(OP_CMPG_DOUBLE, "g-double", double, _DOUBLE, 1)
+OP_END
+
+/* File: c/OP_CMP_LONG.c */
+HANDLE_OP_CMPX(OP_CMP_LONG, "-long", s8, _WIDE, 0)
+OP_END
+
+/* File: c/OP_IF_EQ.c */
+HANDLE_OP_IF_XX(OP_IF_EQ, "eq", ==)
+OP_END
+
+/* File: c/OP_IF_NE.c */
+HANDLE_OP_IF_XX(OP_IF_NE, "ne", !=)
+OP_END
+
+/* File: c/OP_IF_LT.c */
+HANDLE_OP_IF_XX(OP_IF_LT, "lt", <)
+OP_END
+
+/* File: c/OP_IF_GE.c */
+HANDLE_OP_IF_XX(OP_IF_GE, "ge", >=)
+OP_END
+
+/* File: c/OP_IF_GT.c */
+HANDLE_OP_IF_XX(OP_IF_GT, "gt", >)
+OP_END
+
+/* File: c/OP_IF_LE.c */
+HANDLE_OP_IF_XX(OP_IF_LE, "le", <=)
+OP_END
+
+/* File: c/OP_IF_EQZ.c */
+HANDLE_OP_IF_XXZ(OP_IF_EQZ, "eqz", ==)
+OP_END
+
+/* File: c/OP_IF_NEZ.c */
+HANDLE_OP_IF_XXZ(OP_IF_NEZ, "nez", !=)
+OP_END
+
+/* File: c/OP_IF_LTZ.c */
+HANDLE_OP_IF_XXZ(OP_IF_LTZ, "ltz", <)
+OP_END
+
+/* File: c/OP_IF_GEZ.c */
+HANDLE_OP_IF_XXZ(OP_IF_GEZ, "gez", >=)
+OP_END
+
+/* File: c/OP_IF_GTZ.c */
+HANDLE_OP_IF_XXZ(OP_IF_GTZ, "gtz", >)
+OP_END
+
+/* File: c/OP_IF_LEZ.c */
+HANDLE_OP_IF_XXZ(OP_IF_LEZ, "lez", <=)
+OP_END
+
+/* File: c/OP_UNUSED_3E.c */
+HANDLE_OPCODE(OP_UNUSED_3E)
+OP_END
+
+/* File: c/OP_UNUSED_3F.c */
+HANDLE_OPCODE(OP_UNUSED_3F)
+OP_END
+
+/* File: c/OP_UNUSED_40.c */
+HANDLE_OPCODE(OP_UNUSED_40)
+OP_END
+
+/* File: c/OP_UNUSED_41.c */
+HANDLE_OPCODE(OP_UNUSED_41)
+OP_END
+
+/* File: c/OP_UNUSED_42.c */
+HANDLE_OPCODE(OP_UNUSED_42)
+OP_END
+
+/* File: c/OP_UNUSED_43.c */
+HANDLE_OPCODE(OP_UNUSED_43)
+OP_END
+
+/* File: c/OP_AGET.c */
+HANDLE_OP_AGET(OP_AGET, "", u4, )
+OP_END
+
+/* File: c/OP_AGET_WIDE.c */
+HANDLE_OP_AGET(OP_AGET_WIDE, "-wide", s8, _WIDE)
+OP_END
+
+/* File: c/OP_AGET_OBJECT.c */
+HANDLE_OP_AGET(OP_AGET_OBJECT, "-object", u4, )
+OP_END
+
+/* File: c/OP_AGET_BOOLEAN.c */
+HANDLE_OP_AGET(OP_AGET_BOOLEAN, "-boolean", u1, )
+OP_END
+
+/* File: c/OP_AGET_BYTE.c */
+HANDLE_OP_AGET(OP_AGET_BYTE, "-byte", s1, )
+OP_END
+
+/* File: c/OP_AGET_CHAR.c */
+HANDLE_OP_AGET(OP_AGET_CHAR, "-char", u2, )
+OP_END
+
+/* File: c/OP_AGET_SHORT.c */
+HANDLE_OP_AGET(OP_AGET_SHORT, "-short", s2, )
+OP_END
+
+/* File: c/OP_APUT.c */
+HANDLE_OP_APUT(OP_APUT, "", u4, )
+OP_END
+
+/* File: c/OP_APUT_WIDE.c */
+HANDLE_OP_APUT(OP_APUT_WIDE, "-wide", s8, _WIDE)
+OP_END
+
+/* File: c/OP_APUT_OBJECT.c */
+HANDLE_OPCODE(OP_APUT_OBJECT /*vAA, vBB, vCC*/)
+ {
+ ArrayObject* arrayObj;
+ Object* obj;
+ u2 arrayInfo;
+ EXPORT_PC();
+ vdst = INST_AA(inst); /* AA: source value */
+ arrayInfo = FETCH(1);
+ vsrc1 = arrayInfo & 0xff; /* BB: array ptr */
+ vsrc2 = arrayInfo >> 8; /* CC: index */
+ ILOGV("|aput%s v%d,v%d,v%d", "-object", vdst, vsrc1, vsrc2);
+ arrayObj = (ArrayObject*) GET_REGISTER(vsrc1);
+ if (!checkForNull((Object*) arrayObj))
+ GOTO_exceptionThrown();
+ if (GET_REGISTER(vsrc2) >= arrayObj->length) {
+ dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;",
+ NULL);
+ GOTO_exceptionThrown();
+ }
+ obj = (Object*) GET_REGISTER(vdst);
+ if (obj != NULL) {
+ if (!checkForNull(obj))
+ GOTO_exceptionThrown();
+ if (!dvmCanPutArrayElement(obj->clazz, arrayObj->obj.clazz)) {
+ LOGV("Can't put a '%s'(%p) into array type='%s'(%p)\n",
+ obj->clazz->descriptor, obj,
+ arrayObj->obj.clazz->descriptor, arrayObj);
+ //dvmDumpClass(obj->clazz);
+ //dvmDumpClass(arrayObj->obj.clazz);
+ dvmThrowException("Ljava/lang/ArrayStoreException;", NULL);
+ GOTO_exceptionThrown();
+ }
+ }
+ ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));
+ dvmSetObjectArrayElement(arrayObj,
+ GET_REGISTER(vsrc2),
+ (Object *)GET_REGISTER(vdst));
+ }
+ FINISH(2);
+OP_END
+
+/* File: c/OP_APUT_BOOLEAN.c */
+HANDLE_OP_APUT(OP_APUT_BOOLEAN, "-boolean", u1, )
+OP_END
+
+/* File: c/OP_APUT_BYTE.c */
+HANDLE_OP_APUT(OP_APUT_BYTE, "-byte", s1, )
+OP_END
+
+/* File: c/OP_APUT_CHAR.c */
+HANDLE_OP_APUT(OP_APUT_CHAR, "-char", u2, )
+OP_END
+
+/* File: c/OP_APUT_SHORT.c */
+HANDLE_OP_APUT(OP_APUT_SHORT, "-short", s2, )
+OP_END
+
+/* File: c/OP_IGET.c */
+HANDLE_IGET_X(OP_IGET, "", Int, )
+OP_END
+
+/* File: c/OP_IGET_WIDE.c */
+HANDLE_IGET_X(OP_IGET_WIDE, "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IGET_OBJECT.c */
+HANDLE_IGET_X(OP_IGET_OBJECT, "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IGET_BOOLEAN.c */
+HANDLE_IGET_X(OP_IGET_BOOLEAN, "", Int, )
+OP_END
+
+/* File: c/OP_IGET_BYTE.c */
+HANDLE_IGET_X(OP_IGET_BYTE, "", Int, )
+OP_END
+
+/* File: c/OP_IGET_CHAR.c */
+HANDLE_IGET_X(OP_IGET_CHAR, "", Int, )
+OP_END
+
+/* File: c/OP_IGET_SHORT.c */
+HANDLE_IGET_X(OP_IGET_SHORT, "", Int, )
+OP_END
+
+/* File: c/OP_IPUT.c */
+HANDLE_IPUT_X(OP_IPUT, "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_WIDE.c */
+HANDLE_IPUT_X(OP_IPUT_WIDE, "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IPUT_OBJECT.c */
+/*
+ * The VM spec says we should verify that the reference being stored into
+ * the field is assignment compatible. In practice, many popular VMs don't
+ * do this because it slows down a very common operation. It's not so bad
+ * for us, since "dexopt" quickens it whenever possible, but it's still an
+ * issue.
+ *
+ * To make this spec-complaint, we'd need to add a ClassObject pointer to
+ * the Field struct, resolve the field's type descriptor at link or class
+ * init time, and then verify the type here.
+ */
+HANDLE_IPUT_X(OP_IPUT_OBJECT, "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IPUT_BOOLEAN.c */
+HANDLE_IPUT_X(OP_IPUT_BOOLEAN, "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_BYTE.c */
+HANDLE_IPUT_X(OP_IPUT_BYTE, "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_CHAR.c */
+HANDLE_IPUT_X(OP_IPUT_CHAR, "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_SHORT.c */
+HANDLE_IPUT_X(OP_IPUT_SHORT, "", Int, )
+OP_END
+
+/* File: c/OP_SGET.c */
+HANDLE_SGET_X(OP_SGET, "", Int, )
+OP_END
+
+/* File: c/OP_SGET_WIDE.c */
+HANDLE_SGET_X(OP_SGET_WIDE, "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_SGET_OBJECT.c */
+HANDLE_SGET_X(OP_SGET_OBJECT, "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SGET_BOOLEAN.c */
+HANDLE_SGET_X(OP_SGET_BOOLEAN, "", Int, )
+OP_END
+
+/* File: c/OP_SGET_BYTE.c */
+HANDLE_SGET_X(OP_SGET_BYTE, "", Int, )
+OP_END
+
+/* File: c/OP_SGET_CHAR.c */
+HANDLE_SGET_X(OP_SGET_CHAR, "", Int, )
+OP_END
+
+/* File: c/OP_SGET_SHORT.c */
+HANDLE_SGET_X(OP_SGET_SHORT, "", Int, )
+OP_END
+
+/* File: c/OP_SPUT.c */
+HANDLE_SPUT_X(OP_SPUT, "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_WIDE.c */
+HANDLE_SPUT_X(OP_SPUT_WIDE, "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_SPUT_OBJECT.c */
+HANDLE_SPUT_X(OP_SPUT_OBJECT, "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SPUT_BOOLEAN.c */
+HANDLE_SPUT_X(OP_SPUT_BOOLEAN, "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_BYTE.c */
+HANDLE_SPUT_X(OP_SPUT_BYTE, "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_CHAR.c */
+HANDLE_SPUT_X(OP_SPUT_CHAR, "", Int, )
+OP_END
+
+/* File: c/OP_SPUT_SHORT.c */
+HANDLE_SPUT_X(OP_SPUT_SHORT, "", Int, )
+OP_END
+
+/* File: c/OP_INVOKE_VIRTUAL.c */
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+ GOTO_invoke(invokeVirtual, false);
+OP_END
+
+/* File: c/OP_INVOKE_SUPER.c */
+HANDLE_OPCODE(OP_INVOKE_SUPER /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+ GOTO_invoke(invokeSuper, false);
+OP_END
+
+/* File: c/OP_INVOKE_DIRECT.c */
+HANDLE_OPCODE(OP_INVOKE_DIRECT /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+ GOTO_invoke(invokeDirect, false);
+OP_END
+
+/* File: c/OP_INVOKE_STATIC.c */
+HANDLE_OPCODE(OP_INVOKE_STATIC /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+ GOTO_invoke(invokeStatic, false);
+OP_END
+
+/* File: c/OP_INVOKE_INTERFACE.c */
+HANDLE_OPCODE(OP_INVOKE_INTERFACE /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+ GOTO_invoke(invokeInterface, false);
+OP_END
+
+/* File: c/OP_UNUSED_73.c */
+HANDLE_OPCODE(OP_UNUSED_73)
+OP_END
+
+/* File: c/OP_INVOKE_VIRTUAL_RANGE.c */
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+ GOTO_invoke(invokeVirtual, true);
+OP_END
+
+/* File: c/OP_INVOKE_SUPER_RANGE.c */
+HANDLE_OPCODE(OP_INVOKE_SUPER_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+ GOTO_invoke(invokeSuper, true);
+OP_END
+
+/* File: c/OP_INVOKE_DIRECT_RANGE.c */
+HANDLE_OPCODE(OP_INVOKE_DIRECT_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+ GOTO_invoke(invokeDirect, true);
+OP_END
+
+/* File: c/OP_INVOKE_STATIC_RANGE.c */
+HANDLE_OPCODE(OP_INVOKE_STATIC_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+ GOTO_invoke(invokeStatic, true);
+OP_END
+
+/* File: c/OP_INVOKE_INTERFACE_RANGE.c */
+HANDLE_OPCODE(OP_INVOKE_INTERFACE_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+ GOTO_invoke(invokeInterface, true);
+OP_END
+
+/* File: c/OP_UNUSED_79.c */
+HANDLE_OPCODE(OP_UNUSED_79)
+OP_END
+
+/* File: c/OP_UNUSED_7A.c */
+HANDLE_OPCODE(OP_UNUSED_7A)
+OP_END
+
+/* File: c/OP_NEG_INT.c */
+HANDLE_UNOP(OP_NEG_INT, "neg-int", -, , )
+OP_END
+
+/* File: c/OP_NOT_INT.c */
+HANDLE_UNOP(OP_NOT_INT, "not-int", , ^ 0xffffffff, )
+OP_END
+
+/* File: c/OP_NEG_LONG.c */
+HANDLE_UNOP(OP_NEG_LONG, "neg-long", -, , _WIDE)
+OP_END
+
+/* File: c/OP_NOT_LONG.c */
+HANDLE_UNOP(OP_NOT_LONG, "not-long", , ^ 0xffffffffffffffffULL, _WIDE)
+OP_END
+
+/* File: c/OP_NEG_FLOAT.c */
+HANDLE_UNOP(OP_NEG_FLOAT, "neg-float", -, , _FLOAT)
+OP_END
+
+/* File: c/OP_NEG_DOUBLE.c */
+HANDLE_UNOP(OP_NEG_DOUBLE, "neg-double", -, , _DOUBLE)
+OP_END
+
+/* File: c/OP_INT_TO_LONG.c */
+HANDLE_NUMCONV(OP_INT_TO_LONG, "int-to-long", _INT, _WIDE)
+OP_END
+
+/* File: c/OP_INT_TO_FLOAT.c */
+HANDLE_NUMCONV(OP_INT_TO_FLOAT, "int-to-float", _INT, _FLOAT)
+OP_END
+
+/* File: c/OP_INT_TO_DOUBLE.c */
+HANDLE_NUMCONV(OP_INT_TO_DOUBLE, "int-to-double", _INT, _DOUBLE)
+OP_END
+
+/* File: c/OP_LONG_TO_INT.c */
+HANDLE_NUMCONV(OP_LONG_TO_INT, "long-to-int", _WIDE, _INT)
+OP_END
+
+/* File: c/OP_LONG_TO_FLOAT.c */
+HANDLE_NUMCONV(OP_LONG_TO_FLOAT, "long-to-float", _WIDE, _FLOAT)
+OP_END
+
+/* File: c/OP_LONG_TO_DOUBLE.c */
+HANDLE_NUMCONV(OP_LONG_TO_DOUBLE, "long-to-double", _WIDE, _DOUBLE)
+OP_END
+
+/* File: c/OP_FLOAT_TO_INT.c */
+HANDLE_FLOAT_TO_INT(OP_FLOAT_TO_INT, "float-to-int",
+ float, _FLOAT, s4, _INT)
+OP_END
+
+/* File: c/OP_FLOAT_TO_LONG.c */
+HANDLE_FLOAT_TO_INT(OP_FLOAT_TO_LONG, "float-to-long",
+ float, _FLOAT, s8, _WIDE)
+OP_END
+
+/* File: c/OP_FLOAT_TO_DOUBLE.c */
+HANDLE_NUMCONV(OP_FLOAT_TO_DOUBLE, "float-to-double", _FLOAT, _DOUBLE)
+OP_END
+
+/* File: c/OP_DOUBLE_TO_INT.c */
+HANDLE_FLOAT_TO_INT(OP_DOUBLE_TO_INT, "double-to-int",
+ double, _DOUBLE, s4, _INT)
+OP_END
+
+/* File: c/OP_DOUBLE_TO_LONG.c */
+HANDLE_FLOAT_TO_INT(OP_DOUBLE_TO_LONG, "double-to-long",
+ double, _DOUBLE, s8, _WIDE)
+OP_END
+
+/* File: c/OP_DOUBLE_TO_FLOAT.c */
+HANDLE_NUMCONV(OP_DOUBLE_TO_FLOAT, "double-to-float", _DOUBLE, _FLOAT)
+OP_END
+
+/* File: c/OP_INT_TO_BYTE.c */
+HANDLE_INT_TO_SMALL(OP_INT_TO_BYTE, "byte", s1)
+OP_END
+
+/* File: c/OP_INT_TO_CHAR.c */
+HANDLE_INT_TO_SMALL(OP_INT_TO_CHAR, "char", u2)
+OP_END
+
+/* File: c/OP_INT_TO_SHORT.c */
+HANDLE_INT_TO_SMALL(OP_INT_TO_SHORT, "short", s2) /* want sign bit */
+OP_END
+
+/* File: c/OP_ADD_INT.c */
+HANDLE_OP_X_INT(OP_ADD_INT, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_INT.c */
+HANDLE_OP_X_INT(OP_SUB_INT, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_INT.c */
+HANDLE_OP_X_INT(OP_MUL_INT, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT.c */
+HANDLE_OP_X_INT(OP_DIV_INT, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT.c */
+HANDLE_OP_X_INT(OP_REM_INT, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT.c */
+HANDLE_OP_X_INT(OP_AND_INT, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT.c */
+HANDLE_OP_X_INT(OP_OR_INT, "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT.c */
+HANDLE_OP_X_INT(OP_XOR_INT, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_INT.c */
+HANDLE_OP_SHX_INT(OP_SHL_INT, "shl", (s4), <<)
+OP_END
+
+/* File: c/OP_SHR_INT.c */
+HANDLE_OP_SHX_INT(OP_SHR_INT, "shr", (s4), >>)
+OP_END
+
+/* File: c/OP_USHR_INT.c */
+HANDLE_OP_SHX_INT(OP_USHR_INT, "ushr", (u4), >>)
+OP_END
+
+/* File: c/OP_ADD_LONG.c */
+HANDLE_OP_X_LONG(OP_ADD_LONG, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_LONG.c */
+HANDLE_OP_X_LONG(OP_SUB_LONG, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_LONG.c */
+HANDLE_OP_X_LONG(OP_MUL_LONG, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_LONG.c */
+HANDLE_OP_X_LONG(OP_DIV_LONG, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_LONG.c */
+HANDLE_OP_X_LONG(OP_REM_LONG, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_LONG.c */
+HANDLE_OP_X_LONG(OP_AND_LONG, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_LONG.c */
+HANDLE_OP_X_LONG(OP_OR_LONG, "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_LONG.c */
+HANDLE_OP_X_LONG(OP_XOR_LONG, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_LONG.c */
+HANDLE_OP_SHX_LONG(OP_SHL_LONG, "shl", (s8), <<)
+OP_END
+
+/* File: c/OP_SHR_LONG.c */
+HANDLE_OP_SHX_LONG(OP_SHR_LONG, "shr", (s8), >>)
+OP_END
+
+/* File: c/OP_USHR_LONG.c */
+HANDLE_OP_SHX_LONG(OP_USHR_LONG, "ushr", (u8), >>)
+OP_END
+
+/* File: c/OP_ADD_FLOAT.c */
+HANDLE_OP_X_FLOAT(OP_ADD_FLOAT, "add", +)
+OP_END
+
+/* File: c/OP_SUB_FLOAT.c */
+HANDLE_OP_X_FLOAT(OP_SUB_FLOAT, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_FLOAT.c */
+HANDLE_OP_X_FLOAT(OP_MUL_FLOAT, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_FLOAT.c */
+HANDLE_OP_X_FLOAT(OP_DIV_FLOAT, "div", /)
+OP_END
+
+/* File: c/OP_REM_FLOAT.c */
+HANDLE_OPCODE(OP_REM_FLOAT /*vAA, vBB, vCC*/)
+ {
+ u2 srcRegs;
+ vdst = INST_AA(inst);
+ srcRegs = FETCH(1);
+ vsrc1 = srcRegs & 0xff;
+ vsrc2 = srcRegs >> 8;
+ ILOGV("|%s-float v%d,v%d,v%d", "mod", vdst, vsrc1, vsrc2);
+ SET_REGISTER_FLOAT(vdst,
+ fmodf(GET_REGISTER_FLOAT(vsrc1), GET_REGISTER_FLOAT(vsrc2)));
+ }
+ FINISH(2);
+OP_END
+
+/* File: c/OP_ADD_DOUBLE.c */
+HANDLE_OP_X_DOUBLE(OP_ADD_DOUBLE, "add", +)
+OP_END
+
+/* File: c/OP_SUB_DOUBLE.c */
+HANDLE_OP_X_DOUBLE(OP_SUB_DOUBLE, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_DOUBLE.c */
+HANDLE_OP_X_DOUBLE(OP_MUL_DOUBLE, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_DOUBLE.c */
+HANDLE_OP_X_DOUBLE(OP_DIV_DOUBLE, "div", /)
+OP_END
+
+/* File: c/OP_REM_DOUBLE.c */
+HANDLE_OPCODE(OP_REM_DOUBLE /*vAA, vBB, vCC*/)
+ {
+ u2 srcRegs;
+ vdst = INST_AA(inst);
+ srcRegs = FETCH(1);
+ vsrc1 = srcRegs & 0xff;
+ vsrc2 = srcRegs >> 8;
+ ILOGV("|%s-double v%d,v%d,v%d", "mod", vdst, vsrc1, vsrc2);
+ SET_REGISTER_DOUBLE(vdst,
+ fmod(GET_REGISTER_DOUBLE(vsrc1), GET_REGISTER_DOUBLE(vsrc2)));
+ }
+ FINISH(2);
+OP_END
+
+/* File: c/OP_ADD_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_ADD_INT_2ADDR, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_SUB_INT_2ADDR, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_MUL_INT_2ADDR, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_DIV_INT_2ADDR, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_REM_INT_2ADDR, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_AND_INT_2ADDR, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_OR_INT_2ADDR, "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT_2ADDR.c */
+HANDLE_OP_X_INT_2ADDR(OP_XOR_INT_2ADDR, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_INT_2ADDR.c */
+HANDLE_OP_SHX_INT_2ADDR(OP_SHL_INT_2ADDR, "shl", (s4), <<)
+OP_END
+
+/* File: c/OP_SHR_INT_2ADDR.c */
+HANDLE_OP_SHX_INT_2ADDR(OP_SHR_INT_2ADDR, "shr", (s4), >>)
+OP_END
+
+/* File: c/OP_USHR_INT_2ADDR.c */
+HANDLE_OP_SHX_INT_2ADDR(OP_USHR_INT_2ADDR, "ushr", (u4), >>)
+OP_END
+
+/* File: c/OP_ADD_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_ADD_LONG_2ADDR, "add", +, 0)
+OP_END
+
+/* File: c/OP_SUB_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_SUB_LONG_2ADDR, "sub", -, 0)
+OP_END
+
+/* File: c/OP_MUL_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_MUL_LONG_2ADDR, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_DIV_LONG_2ADDR, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_REM_LONG_2ADDR, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_AND_LONG_2ADDR, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_OR_LONG_2ADDR, "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_LONG_2ADDR.c */
+HANDLE_OP_X_LONG_2ADDR(OP_XOR_LONG_2ADDR, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_LONG_2ADDR.c */
+HANDLE_OP_SHX_LONG_2ADDR(OP_SHL_LONG_2ADDR, "shl", (s8), <<)
+OP_END
+
+/* File: c/OP_SHR_LONG_2ADDR.c */
+HANDLE_OP_SHX_LONG_2ADDR(OP_SHR_LONG_2ADDR, "shr", (s8), >>)
+OP_END
+
+/* File: c/OP_USHR_LONG_2ADDR.c */
+HANDLE_OP_SHX_LONG_2ADDR(OP_USHR_LONG_2ADDR, "ushr", (u8), >>)
+OP_END
+
+/* File: c/OP_ADD_FLOAT_2ADDR.c */
+HANDLE_OP_X_FLOAT_2ADDR(OP_ADD_FLOAT_2ADDR, "add", +)
+OP_END
+
+/* File: c/OP_SUB_FLOAT_2ADDR.c */
+HANDLE_OP_X_FLOAT_2ADDR(OP_SUB_FLOAT_2ADDR, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_FLOAT_2ADDR.c */
+HANDLE_OP_X_FLOAT_2ADDR(OP_MUL_FLOAT_2ADDR, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_FLOAT_2ADDR.c */
+HANDLE_OP_X_FLOAT_2ADDR(OP_DIV_FLOAT_2ADDR, "div", /)
+OP_END
+
+/* File: c/OP_REM_FLOAT_2ADDR.c */
+HANDLE_OPCODE(OP_REM_FLOAT_2ADDR /*vA, vB*/)
+ vdst = INST_A(inst);
+ vsrc1 = INST_B(inst);
+ ILOGV("|%s-float-2addr v%d,v%d", "mod", vdst, vsrc1);
+ SET_REGISTER_FLOAT(vdst,
+ fmodf(GET_REGISTER_FLOAT(vdst), GET_REGISTER_FLOAT(vsrc1)));
+ FINISH(1);
+OP_END
+
+/* File: c/OP_ADD_DOUBLE_2ADDR.c */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_ADD_DOUBLE_2ADDR, "add", +)
+OP_END
+
+/* File: c/OP_SUB_DOUBLE_2ADDR.c */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_SUB_DOUBLE_2ADDR, "sub", -)
+OP_END
+
+/* File: c/OP_MUL_DOUBLE_2ADDR.c */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_MUL_DOUBLE_2ADDR, "mul", *)
+OP_END
+
+/* File: c/OP_DIV_DOUBLE_2ADDR.c */
+HANDLE_OP_X_DOUBLE_2ADDR(OP_DIV_DOUBLE_2ADDR, "div", /)
+OP_END
+
+/* File: c/OP_REM_DOUBLE_2ADDR.c */
+HANDLE_OPCODE(OP_REM_DOUBLE_2ADDR /*vA, vB*/)
+ vdst = INST_A(inst);
+ vsrc1 = INST_B(inst);
+ ILOGV("|%s-double-2addr v%d,v%d", "mod", vdst, vsrc1);
+ SET_REGISTER_DOUBLE(vdst,
+ fmod(GET_REGISTER_DOUBLE(vdst), GET_REGISTER_DOUBLE(vsrc1)));
+ FINISH(1);
+OP_END
+
+/* File: c/OP_ADD_INT_LIT16.c */
+HANDLE_OP_X_INT_LIT16(OP_ADD_INT_LIT16, "add", +, 0)
+OP_END
+
+/* File: c/OP_RSUB_INT.c */
+HANDLE_OPCODE(OP_RSUB_INT /*vA, vB, #+CCCC*/)
+ {
+ vdst = INST_A(inst);
+ vsrc1 = INST_B(inst);
+ vsrc2 = FETCH(1);
+ ILOGV("|rsub-int v%d,v%d,#+0x%04x", vdst, vsrc1, vsrc2);
+ SET_REGISTER(vdst, (s2) vsrc2 - (s4) GET_REGISTER(vsrc1));
+ }
+ FINISH(2);
+OP_END
+
+/* File: c/OP_MUL_INT_LIT16.c */
+HANDLE_OP_X_INT_LIT16(OP_MUL_INT_LIT16, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT_LIT16.c */
+HANDLE_OP_X_INT_LIT16(OP_DIV_INT_LIT16, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT_LIT16.c */
+HANDLE_OP_X_INT_LIT16(OP_REM_INT_LIT16, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT_LIT16.c */
+HANDLE_OP_X_INT_LIT16(OP_AND_INT_LIT16, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT_LIT16.c */
+HANDLE_OP_X_INT_LIT16(OP_OR_INT_LIT16, "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT_LIT16.c */
+HANDLE_OP_X_INT_LIT16(OP_XOR_INT_LIT16, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_ADD_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_ADD_INT_LIT8, "add", +, 0)
+OP_END
+
+/* File: c/OP_RSUB_INT_LIT8.c */
+HANDLE_OPCODE(OP_RSUB_INT_LIT8 /*vAA, vBB, #+CC*/)
+ {
+ u2 litInfo;
+ vdst = INST_AA(inst);
+ litInfo = FETCH(1);
+ vsrc1 = litInfo & 0xff;
+ vsrc2 = litInfo >> 8;
+ ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x", "rsub", vdst, vsrc1, vsrc2);
+ SET_REGISTER(vdst, (s1) vsrc2 - (s4) GET_REGISTER(vsrc1));
+ }
+ FINISH(2);
+OP_END
+
+/* File: c/OP_MUL_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_MUL_INT_LIT8, "mul", *, 0)
+OP_END
+
+/* File: c/OP_DIV_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_DIV_INT_LIT8, "div", /, 1)
+OP_END
+
+/* File: c/OP_REM_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_REM_INT_LIT8, "rem", %, 2)
+OP_END
+
+/* File: c/OP_AND_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_AND_INT_LIT8, "and", &, 0)
+OP_END
+
+/* File: c/OP_OR_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_OR_INT_LIT8, "or", |, 0)
+OP_END
+
+/* File: c/OP_XOR_INT_LIT8.c */
+HANDLE_OP_X_INT_LIT8(OP_XOR_INT_LIT8, "xor", ^, 0)
+OP_END
+
+/* File: c/OP_SHL_INT_LIT8.c */
+HANDLE_OP_SHX_INT_LIT8(OP_SHL_INT_LIT8, "shl", (s4), <<)
+OP_END
+
+/* File: c/OP_SHR_INT_LIT8.c */
+HANDLE_OP_SHX_INT_LIT8(OP_SHR_INT_LIT8, "shr", (s4), >>)
+OP_END
+
+/* File: c/OP_USHR_INT_LIT8.c */
+HANDLE_OP_SHX_INT_LIT8(OP_USHR_INT_LIT8, "ushr", (u4), >>)
+OP_END
+
+/* File: c/OP_IGET_VOLATILE.c */
+HANDLE_IGET_X(OP_IGET_VOLATILE, "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_IPUT_VOLATILE.c */
+HANDLE_IPUT_X(OP_IPUT_VOLATILE, "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_SGET_VOLATILE.c */
+HANDLE_SGET_X(OP_SGET_VOLATILE, "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_SPUT_VOLATILE.c */
+HANDLE_SPUT_X(OP_SPUT_VOLATILE, "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_IGET_OBJECT_VOLATILE.c */
+HANDLE_IGET_X(OP_IGET_OBJECT_VOLATILE, "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IGET_WIDE_VOLATILE.c */
+HANDLE_IGET_X(OP_IGET_WIDE_VOLATILE, "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_IPUT_WIDE_VOLATILE.c */
+HANDLE_IPUT_X(OP_IPUT_WIDE_VOLATILE, "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_SGET_WIDE_VOLATILE.c */
+HANDLE_SGET_X(OP_SGET_WIDE_VOLATILE, "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_SPUT_WIDE_VOLATILE.c */
+HANDLE_SPUT_X(OP_SPUT_WIDE_VOLATILE, "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_BREAKPOINT.c */
+HANDLE_OPCODE(OP_BREAKPOINT)
+#if (INTERP_TYPE == INTERP_DBG)
+ {
+ /*
+ * Restart this instruction with the original opcode. We do
+ * this by simply jumping to the handler.
+ *
+ * It's probably not necessary to update "inst", but we do it
+ * for the sake of anything that needs to do disambiguation in a
+ * common handler with INST_INST.
+ *
+ * The breakpoint itself is handled over in updateDebugger(),
+ * because we need to detect other events (method entry, single
+ * step) and report them in the same event packet, and we're not
+ * yet handling those through breakpoint instructions. By the
+ * time we get here, the breakpoint has already been handled and
+ * the thread resumed.
+ */
+ u1 originalOpCode = dvmGetOriginalOpCode(pc);
+ LOGV("+++ break 0x%02x (0x%04x -> 0x%04x)\n", originalOpCode, inst,
+ INST_REPLACE_OP(inst, originalOpCode));
+ inst = INST_REPLACE_OP(inst, originalOpCode);
+ FINISH_BKPT(originalOpCode);
+ }
+#else
+ LOGE("Breakpoint hit in non-debug interpreter\n");
+ dvmAbort();
+#endif
+OP_END
+
+/* File: c/OP_THROW_VERIFICATION_ERROR.c */
+HANDLE_OPCODE(OP_THROW_VERIFICATION_ERROR)
+ EXPORT_PC();
+ vsrc1 = INST_AA(inst);
+ ref = FETCH(1); /* class/field/method ref */
+ dvmThrowVerificationError(curMethod, vsrc1, ref);
+ GOTO_exceptionThrown();
+OP_END
+
+/* File: c/OP_EXECUTE_INLINE.c */
+HANDLE_OPCODE(OP_EXECUTE_INLINE /*vB, {vD, vE, vF, vG}, inline@CCCC*/)
+ {
+ /*
+ * This has the same form as other method calls, but we ignore
+ * the 5th argument (vA). This is chiefly because the first four
+ * arguments to a function on ARM are in registers.
+ *
+ * We only set the arguments that are actually used, leaving
+ * the rest uninitialized. We're assuming that, if the method
+ * needs them, they'll be specified in the call.
+ *
+ * However, this annoys gcc when optimizations are enabled,
+ * causing a "may be used uninitialized" warning. Quieting
+ * the warnings incurs a slight penalty (5%: 373ns vs. 393ns
+ * on empty method). Note that valgrind is perfectly happy
+ * either way as the uninitialiezd values are never actually
+ * used.
+ */
+ u4 arg0, arg1, arg2, arg3;
+ arg0 = arg1 = arg2 = arg3 = 0;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_B(inst); /* #of args */
+ ref = FETCH(1); /* inline call "ref" */
+ vdst = FETCH(2); /* 0-4 register indices */
+ ILOGV("|execute-inline args=%d @%d {regs=0x%04x}",
+ vsrc1, ref, vdst);
+
+ assert((vdst >> 16) == 0); // 16-bit type -or- high 16 bits clear
+ assert(vsrc1 <= 4);
+
+ switch (vsrc1) {
+ case 4:
+ arg3 = GET_REGISTER(vdst >> 12);
+ /* fall through */
+ case 3:
+ arg2 = GET_REGISTER((vdst & 0x0f00) >> 8);
+ /* fall through */
+ case 2:
+ arg1 = GET_REGISTER((vdst & 0x00f0) >> 4);
+ /* fall through */
+ case 1:
+ arg0 = GET_REGISTER(vdst & 0x0f);
+ /* fall through */
+ default: // case 0
+ ;
+ }
+
+#if INTERP_TYPE == INTERP_DBG
+ if (!dvmPerformInlineOp4Dbg(arg0, arg1, arg2, arg3, &retval, ref))
+ GOTO_exceptionThrown();
+#else
+ if (!dvmPerformInlineOp4Std(arg0, arg1, arg2, arg3, &retval, ref))
+ GOTO_exceptionThrown();
+#endif
+ }
+ FINISH(3);
+OP_END
+
+/* File: c/OP_EXECUTE_INLINE_RANGE.c */
+HANDLE_OPCODE(OP_EXECUTE_INLINE_RANGE /*{vCCCC..v(CCCC+AA-1)}, inline@BBBB*/)
+ {
+ u4 arg0, arg1, arg2, arg3;
+ arg0 = arg1 = arg2 = arg3 = 0; /* placate gcc */
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst); /* #of args */
+ ref = FETCH(1); /* inline call "ref" */
+ vdst = FETCH(2); /* range base */
+ ILOGV("|execute-inline-range args=%d @%d {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+
+ assert((vdst >> 16) == 0); // 16-bit type -or- high 16 bits clear
+ assert(vsrc1 <= 4);
+
+ switch (vsrc1) {
+ case 4:
+ arg3 = GET_REGISTER(vdst+3);
+ /* fall through */
+ case 3:
+ arg2 = GET_REGISTER(vdst+2);
+ /* fall through */
+ case 2:
+ arg1 = GET_REGISTER(vdst+1);
+ /* fall through */
+ case 1:
+ arg0 = GET_REGISTER(vdst+0);
+ /* fall through */
+ default: // case 0
+ ;
+ }
+
+#if INTERP_TYPE == INTERP_DBG
+ if (!dvmPerformInlineOp4Dbg(arg0, arg1, arg2, arg3, &retval, ref))
+ GOTO_exceptionThrown();
+#else
+ if (!dvmPerformInlineOp4Std(arg0, arg1, arg2, arg3, &retval, ref))
+ GOTO_exceptionThrown();
+#endif
+ }
+ FINISH(3);
+OP_END
+
+/* File: c/OP_INVOKE_DIRECT_EMPTY.c */
+HANDLE_OPCODE(OP_INVOKE_DIRECT_EMPTY /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+#if INTERP_TYPE != INTERP_DBG
+ //LOGI("Ignoring empty\n");
+ FINISH(3);
+#else
+ if (!gDvm.debuggerActive) {
+ //LOGI("Skipping empty\n");
+ FINISH(3); // don't want it to show up in profiler output
+ } else {
+ //LOGI("Running empty\n");
+ /* fall through to OP_INVOKE_DIRECT */
+ GOTO_invoke(invokeDirect, false);
+ }
+#endif
+OP_END
+
+/* File: c/OP_UNUSED_F1.c */
+HANDLE_OPCODE(OP_UNUSED_F1)
+OP_END
+
+/* File: c/OP_IGET_QUICK.c */
+HANDLE_IGET_X_QUICK(OP_IGET_QUICK, "", Int, )
+OP_END
+
+/* File: c/OP_IGET_WIDE_QUICK.c */
+HANDLE_IGET_X_QUICK(OP_IGET_WIDE_QUICK, "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IGET_OBJECT_QUICK.c */
+HANDLE_IGET_X_QUICK(OP_IGET_OBJECT_QUICK, "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IPUT_QUICK.c */
+HANDLE_IPUT_X_QUICK(OP_IPUT_QUICK, "", Int, )
+OP_END
+
+/* File: c/OP_IPUT_WIDE_QUICK.c */
+HANDLE_IPUT_X_QUICK(OP_IPUT_WIDE_QUICK, "-wide", Long, _WIDE)
+OP_END
+
+/* File: c/OP_IPUT_OBJECT_QUICK.c */
+HANDLE_IPUT_X_QUICK(OP_IPUT_OBJECT_QUICK, "-object", Object, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_INVOKE_VIRTUAL_QUICK.c */
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL_QUICK /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+ GOTO_invoke(invokeVirtualQuick, false);
+OP_END
+
+/* File: c/OP_INVOKE_VIRTUAL_QUICK_RANGE.c */
+HANDLE_OPCODE(OP_INVOKE_VIRTUAL_QUICK_RANGE/*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+ GOTO_invoke(invokeVirtualQuick, true);
+OP_END
+
+/* File: c/OP_INVOKE_SUPER_QUICK.c */
+HANDLE_OPCODE(OP_INVOKE_SUPER_QUICK /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)
+ GOTO_invoke(invokeSuperQuick, false);
+OP_END
+
+/* File: c/OP_INVOKE_SUPER_QUICK_RANGE.c */
+HANDLE_OPCODE(OP_INVOKE_SUPER_QUICK_RANGE /*{vCCCC..v(CCCC+AA-1)}, meth@BBBB*/)
+ GOTO_invoke(invokeSuperQuick, true);
+OP_END
+
+/* File: c/OP_IPUT_OBJECT_VOLATILE.c */
+HANDLE_IPUT_X(OP_IPUT_OBJECT_VOLATILE, "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SGET_OBJECT_VOLATILE.c */
+HANDLE_SGET_X(OP_SGET_OBJECT_VOLATILE, "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SPUT_OBJECT_VOLATILE.c */
+HANDLE_SPUT_X(OP_SPUT_OBJECT_VOLATILE, "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_UNUSED_FF.c */
+HANDLE_OPCODE(OP_UNUSED_FF)
+ /*
+ * In portable interp, most unused opcodes will fall through to here.
+ */
+ LOGE("unknown opcode 0x%02x\n", INST_INST(inst));
+ dvmAbort();
+ FINISH(1);
+OP_END
+
+/* File: c/gotoTargets.c */
+/*
+ * C footer. This has some common code shared by the various targets.
+ */
+
+/*
+ * Everything from here on is a "goto target". In the basic interpreter
+ * we jump into these targets and then jump directly to the handler for
+ * next instruction. Here, these are subroutines that return to the caller.
+ */
+
+GOTO_TARGET(filledNewArray, bool methodCallRange)
+ {
+ ClassObject* arrayClass;
+ ArrayObject* newArray;
+ u4* contents;
+ char typeCh;
+ int i;
+ u4 arg5;
+
+ EXPORT_PC();
+
+ ref = FETCH(1); /* class ref */
+ vdst = FETCH(2); /* first 4 regs -or- range base */
+
+ if (methodCallRange) {
+ vsrc1 = INST_AA(inst); /* #of elements */
+ arg5 = -1; /* silence compiler warning */
+ ILOGV("|filled-new-array-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ } else {
+ arg5 = INST_A(inst);
+ vsrc1 = INST_B(inst); /* #of elements */
+ ILOGV("|filled-new-array args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1, ref, vdst, arg5);
+ }
+
+ /*
+ * Resolve the array class.
+ */
+ arrayClass = dvmDexGetResolvedClass(methodClassDex, ref);
+ if (arrayClass == NULL) {
+ arrayClass = dvmResolveClass(curMethod->clazz, ref, false);
+ if (arrayClass == NULL)
+ GOTO_exceptionThrown();
+ }
+ /*
+ if (!dvmIsArrayClass(arrayClass)) {
+ dvmThrowException("Ljava/lang/RuntimeError;",
+ "filled-new-array needs array class");
+ GOTO_exceptionThrown();
+ }
+ */
+ /* verifier guarantees this is an array class */
+ assert(dvmIsArrayClass(arrayClass));
+ assert(dvmIsClassInitialized(arrayClass));
+
+ /*
+ * Create an array of the specified type.
+ */
+ LOGVV("+++ filled-new-array type is '%s'\n", arrayClass->descriptor);
+ typeCh = arrayClass->descriptor[1];
+ if (typeCh == 'D' || typeCh == 'J') {
+ /* category 2 primitives not allowed */
+ dvmThrowException("Ljava/lang/RuntimeError;",
+ "bad filled array req");
+ GOTO_exceptionThrown();
+ } else if (typeCh != 'L' && typeCh != '[' && typeCh != 'I') {
+ /* TODO: requires multiple "fill in" loops with different widths */
+ LOGE("non-int primitives not implemented\n");
+ dvmThrowException("Ljava/lang/InternalError;",
+ "filled-new-array not implemented for anything but 'int'");
+ GOTO_exceptionThrown();
+ }
+
+ newArray = dvmAllocArrayByClass(arrayClass, vsrc1, ALLOC_DONT_TRACK);
+ if (newArray == NULL)
+ GOTO_exceptionThrown();
+
+ /*
+ * Fill in the elements. It's legal for vsrc1 to be zero.
+ */
+ contents = (u4*) newArray->contents;
+ if (methodCallRange) {
+ for (i = 0; i < vsrc1; i++)
+ contents[i] = GET_REGISTER(vdst+i);
+ } else {
+ assert(vsrc1 <= 5);
+ if (vsrc1 == 5) {
+ contents[4] = GET_REGISTER(arg5);
+ vsrc1--;
+ }
+ for (i = 0; i < vsrc1; i++) {
+ contents[i] = GET_REGISTER(vdst & 0x0f);
+ vdst >>= 4;
+ }
+ }
+ if (typeCh == 'L' || typeCh == '[') {
+ dvmWriteBarrierArray(newArray, 0, newArray->length);
+ }
+
+ retval.l = newArray;
+ }
+ FINISH(3);
+GOTO_TARGET_END
+
+
+GOTO_TARGET(invokeVirtual, bool methodCallRange)
+ {
+ Method* baseMethod;
+ Object* thisPtr;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* method ref */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ /*
+ * The object against which we are executing a method is always
+ * in the first argument.
+ */
+ if (methodCallRange) {
+ assert(vsrc1 > 0);
+ ILOGV("|invoke-virtual-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ thisPtr = (Object*) GET_REGISTER(vdst);
+ } else {
+ assert((vsrc1>>4) > 0);
+ ILOGV("|invoke-virtual args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+ thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+ }
+
+ if (!checkForNull(thisPtr))
+ GOTO_exceptionThrown();
+
+ /*
+ * Resolve the method. This is the correct method for the static
+ * type of the object. We also verify access permissions here.
+ */
+ baseMethod = dvmDexGetResolvedMethod(methodClassDex, ref);
+ if (baseMethod == NULL) {
+ baseMethod = dvmResolveMethod(curMethod->clazz, ref,METHOD_VIRTUAL);
+ if (baseMethod == NULL) {
+ ILOGV("+ unknown method or access denied\n");
+ GOTO_exceptionThrown();
+ }
+ }
+
+ /*
+ * Combine the object we found with the vtable offset in the
+ * method.
+ */
+ assert(baseMethod->methodIndex < thisPtr->clazz->vtableCount);
+ methodToCall = thisPtr->clazz->vtable[baseMethod->methodIndex];
+
+#if defined(WITH_JIT) && (INTERP_TYPE == INTERP_DBG)
+ callsiteClass = thisPtr->clazz;
+#endif
+
+#if 0
+ if (dvmIsAbstractMethod(methodToCall)) {
+ /*
+ * This can happen if you create two classes, Base and Sub, where
+ * Sub is a sub-class of Base. Declare a protected abstract
+ * method foo() in Base, and invoke foo() from a method in Base.
+ * Base is an "abstract base class" and is never instantiated
+ * directly. Now, Override foo() in Sub, and use Sub. This
+ * Works fine unless Sub stops providing an implementation of
+ * the method.
+ */
+ dvmThrowException("Ljava/lang/AbstractMethodError;",
+ "abstract method not implemented");
+ GOTO_exceptionThrown();
+ }
+#else
+ assert(!dvmIsAbstractMethod(methodToCall) ||
+ methodToCall->nativeFunc != NULL);
+#endif
+
+ LOGVV("+++ base=%s.%s virtual[%d]=%s.%s\n",
+ baseMethod->clazz->descriptor, baseMethod->name,
+ (u4) baseMethod->methodIndex,
+ methodToCall->clazz->descriptor, methodToCall->name);
+ assert(methodToCall != NULL);
+
+#if 0
+ if (vsrc1 != methodToCall->insSize) {
+ LOGW("WRONG METHOD: base=%s.%s virtual[%d]=%s.%s\n",
+ baseMethod->clazz->descriptor, baseMethod->name,
+ (u4) baseMethod->methodIndex,
+ methodToCall->clazz->descriptor, methodToCall->name);
+ //dvmDumpClass(baseMethod->clazz);
+ //dvmDumpClass(methodToCall->clazz);
+ dvmDumpAllClasses(0);
+ }
+#endif
+
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+ }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeSuper, bool methodCallRange)
+ {
+ Method* baseMethod;
+ u2 thisReg;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* method ref */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ if (methodCallRange) {
+ ILOGV("|invoke-super-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ thisReg = vdst;
+ } else {
+ ILOGV("|invoke-super args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+ thisReg = vdst & 0x0f;
+ }
+ /* impossible in well-formed code, but we must check nevertheless */
+ if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+ GOTO_exceptionThrown();
+
+ /*
+ * Resolve the method. This is the correct method for the static
+ * type of the object. We also verify access permissions here.
+ * The first arg to dvmResolveMethod() is just the referring class
+ * (used for class loaders and such), so we don't want to pass
+ * the superclass into the resolution call.
+ */
+ baseMethod = dvmDexGetResolvedMethod(methodClassDex, ref);
+ if (baseMethod == NULL) {
+ baseMethod = dvmResolveMethod(curMethod->clazz, ref,METHOD_VIRTUAL);
+ if (baseMethod == NULL) {
+ ILOGV("+ unknown method or access denied\n");
+ GOTO_exceptionThrown();
+ }
+ }
+
+ /*
+ * Combine the object we found with the vtable offset in the
+ * method's class.
+ *
+ * We're using the current method's class' superclass, not the
+ * superclass of "this". This is because we might be executing
+ * in a method inherited from a superclass, and we want to run
+ * in that class' superclass.
+ */
+ if (baseMethod->methodIndex >= curMethod->clazz->super->vtableCount) {
+ /*
+ * Method does not exist in the superclass. Could happen if
+ * superclass gets updated.
+ */
+ dvmThrowException("Ljava/lang/NoSuchMethodError;",
+ baseMethod->name);
+ GOTO_exceptionThrown();
+ }
+ methodToCall = curMethod->clazz->super->vtable[baseMethod->methodIndex];
+#if 0
+ if (dvmIsAbstractMethod(methodToCall)) {
+ dvmThrowException("Ljava/lang/AbstractMethodError;",
+ "abstract method not implemented");
+ GOTO_exceptionThrown();
+ }
+#else
+ assert(!dvmIsAbstractMethod(methodToCall) ||
+ methodToCall->nativeFunc != NULL);
+#endif
+ LOGVV("+++ base=%s.%s super-virtual=%s.%s\n",
+ baseMethod->clazz->descriptor, baseMethod->name,
+ methodToCall->clazz->descriptor, methodToCall->name);
+ assert(methodToCall != NULL);
+
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+ }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeInterface, bool methodCallRange)
+ {
+ Object* thisPtr;
+ ClassObject* thisClass;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* method ref */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ /*
+ * The object against which we are executing a method is always
+ * in the first argument.
+ */
+ if (methodCallRange) {
+ assert(vsrc1 > 0);
+ ILOGV("|invoke-interface-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ thisPtr = (Object*) GET_REGISTER(vdst);
+ } else {
+ assert((vsrc1>>4) > 0);
+ ILOGV("|invoke-interface args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+ thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+ }
+ if (!checkForNull(thisPtr))
+ GOTO_exceptionThrown();
+
+ thisClass = thisPtr->clazz;
+
+#if defined(WITH_JIT) && (INTERP_TYPE == INTERP_DBG)
+ callsiteClass = thisClass;
+#endif
+
+ /*
+ * Given a class and a method index, find the Method* with the
+ * actual code we want to execute.
+ */
+ methodToCall = dvmFindInterfaceMethodInCache(thisClass, ref, curMethod,
+ methodClassDex);
+ if (methodToCall == NULL) {
+ assert(dvmCheckException(self));
+ GOTO_exceptionThrown();
+ }
+
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+ }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeDirect, bool methodCallRange)
+ {
+ u2 thisReg;
+
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* method ref */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ EXPORT_PC();
+
+ if (methodCallRange) {
+ ILOGV("|invoke-direct-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ thisReg = vdst;
+ } else {
+ ILOGV("|invoke-direct args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+ thisReg = vdst & 0x0f;
+ }
+ if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+ GOTO_exceptionThrown();
+
+ methodToCall = dvmDexGetResolvedMethod(methodClassDex, ref);
+ if (methodToCall == NULL) {
+ methodToCall = dvmResolveMethod(curMethod->clazz, ref,
+ METHOD_DIRECT);
+ if (methodToCall == NULL) {
+ ILOGV("+ unknown direct method\n"); // should be impossible
+ GOTO_exceptionThrown();
+ }
+ }
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+ }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeStatic, bool methodCallRange)
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* method ref */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ EXPORT_PC();
+
+ if (methodCallRange)
+ ILOGV("|invoke-static-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ else
+ ILOGV("|invoke-static args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+
+ methodToCall = dvmDexGetResolvedMethod(methodClassDex, ref);
+ if (methodToCall == NULL) {
+ methodToCall = dvmResolveMethod(curMethod->clazz, ref, METHOD_STATIC);
+ if (methodToCall == NULL) {
+ ILOGV("+ unknown method\n");
+ GOTO_exceptionThrown();
+ }
+
+ /*
+ * The JIT needs dvmDexGetResolvedMethod() to return non-null.
+ * Since we use the portable interpreter to build the trace, this extra
+ * check is not needed for mterp.
+ */
+ if (dvmDexGetResolvedMethod(methodClassDex, ref) == NULL) {
+ /* Class initialization is still ongoing */
+ ABORT_JIT_TSELECT();
+ }
+ }
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeVirtualQuick, bool methodCallRange)
+ {
+ Object* thisPtr;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* vtable index */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ /*
+ * The object against which we are executing a method is always
+ * in the first argument.
+ */
+ if (methodCallRange) {
+ assert(vsrc1 > 0);
+ ILOGV("|invoke-virtual-quick-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ thisPtr = (Object*) GET_REGISTER(vdst);
+ } else {
+ assert((vsrc1>>4) > 0);
+ ILOGV("|invoke-virtual-quick args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+ thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+ }
+
+ if (!checkForNull(thisPtr))
+ GOTO_exceptionThrown();
+
+#if defined(WITH_JIT) && (INTERP_TYPE == INTERP_DBG)
+ callsiteClass = thisPtr->clazz;
+#endif
+
+ /*
+ * Combine the object we found with the vtable offset in the
+ * method.
+ */
+ assert(ref < thisPtr->clazz->vtableCount);
+ methodToCall = thisPtr->clazz->vtable[ref];
+
+#if 0
+ if (dvmIsAbstractMethod(methodToCall)) {
+ dvmThrowException("Ljava/lang/AbstractMethodError;",
+ "abstract method not implemented");
+ GOTO_exceptionThrown();
+ }
+#else
+ assert(!dvmIsAbstractMethod(methodToCall) ||
+ methodToCall->nativeFunc != NULL);
+#endif
+
+ LOGVV("+++ virtual[%d]=%s.%s\n",
+ ref, methodToCall->clazz->descriptor, methodToCall->name);
+ assert(methodToCall != NULL);
+
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+ }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeSuperQuick, bool methodCallRange)
+ {
+ u2 thisReg;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* vtable index */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ if (methodCallRange) {
+ ILOGV("|invoke-super-quick-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ thisReg = vdst;
+ } else {
+ ILOGV("|invoke-super-quick args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+ thisReg = vdst & 0x0f;
+ }
+ /* impossible in well-formed code, but we must check nevertheless */
+ if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+ GOTO_exceptionThrown();
+
+#if 0 /* impossible in optimized + verified code */
+ if (ref >= curMethod->clazz->super->vtableCount) {
+ dvmThrowException("Ljava/lang/NoSuchMethodError;", NULL);
+ GOTO_exceptionThrown();
+ }
+#else
+ assert(ref < curMethod->clazz->super->vtableCount);
+#endif
+
+ /*
+ * Combine the object we found with the vtable offset in the
+ * method's class.
+ *
+ * We're using the current method's class' superclass, not the
+ * superclass of "this". This is because we might be executing
+ * in a method inherited from a superclass, and we want to run
+ * in the method's class' superclass.
+ */
+ methodToCall = curMethod->clazz->super->vtable[ref];
+
+#if 0
+ if (dvmIsAbstractMethod(methodToCall)) {
+ dvmThrowException("Ljava/lang/AbstractMethodError;",
+ "abstract method not implemented");
+ GOTO_exceptionThrown();
+ }
+#else
+ assert(!dvmIsAbstractMethod(methodToCall) ||
+ methodToCall->nativeFunc != NULL);
+#endif
+ LOGVV("+++ super-virtual[%d]=%s.%s\n",
+ ref, methodToCall->clazz->descriptor, methodToCall->name);
+ assert(methodToCall != NULL);
+
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+ }
+GOTO_TARGET_END
+
+
+ /*
+ * General handling for return-void, return, and return-wide. Put the
+ * return value in "retval" before jumping here.
+ */
+GOTO_TARGET(returnFromMethod)
+ {
+ StackSaveArea* saveArea;
+
+ /*
+ * We must do this BEFORE we pop the previous stack frame off, so
+ * that the GC can see the return value (if any) in the local vars.
+ *
+ * Since this is now an interpreter switch point, we must do it before
+ * we do anything at all.
+ */
+ PERIODIC_CHECKS(kInterpEntryReturn, 0);
+
+ ILOGV("> retval=0x%llx (leaving %s.%s %s)",
+ retval.j, curMethod->clazz->descriptor, curMethod->name,
+ curMethod->shorty);
+ //DUMP_REGS(curMethod, fp);
+
+ saveArea = SAVEAREA_FROM_FP(fp);
+
+#ifdef EASY_GDB
+ debugSaveArea = saveArea;
+#endif
+#if (INTERP_TYPE == INTERP_DBG)
+ TRACE_METHOD_EXIT(self, curMethod);
+#endif
+
+ /* back up to previous frame and see if we hit a break */
+ fp = saveArea->prevFrame;
+ assert(fp != NULL);
+ if (dvmIsBreakFrame(fp)) {
+ /* bail without popping the method frame from stack */
+ LOGVV("+++ returned into break frame\n");
+#if defined(WITH_JIT)
+ /* Let the Jit know the return is terminating normally */
+ CHECK_JIT_VOID();
+#endif
+ GOTO_bail();
+ }
+
+ /* update thread FP, and reset local variables */
+ self->curFrame = fp;
+ curMethod = SAVEAREA_FROM_FP(fp)->method;
+ //methodClass = curMethod->clazz;
+ methodClassDex = curMethod->clazz->pDvmDex;
+ pc = saveArea->savedPc;
+ ILOGD("> (return to %s.%s %s)", curMethod->clazz->descriptor,
+ curMethod->name, curMethod->shorty);
+
+ /* use FINISH on the caller's invoke instruction */
+ //u2 invokeInstr = INST_INST(FETCH(0));
+ if (true /*invokeInstr >= OP_INVOKE_VIRTUAL &&
+ invokeInstr <= OP_INVOKE_INTERFACE*/)
+ {
+ FINISH(3);
+ } else {
+ //LOGE("Unknown invoke instr %02x at %d\n",
+ // invokeInstr, (int) (pc - curMethod->insns));
+ assert(false);
+ }
+ }
+GOTO_TARGET_END
+
+
+ /*
+ * Jump here when the code throws an exception.
+ *
+ * By the time we get here, the Throwable has been created and the stack
+ * trace has been saved off.
+ */
+GOTO_TARGET(exceptionThrown)
+ {
+ Object* exception;
+ int catchRelPc;
+
+ /*
+ * Since this is now an interpreter switch point, we must do it before
+ * we do anything at all.
+ */
+ PERIODIC_CHECKS(kInterpEntryThrow, 0);
+
+#if defined(WITH_JIT)
+ // Something threw during trace selection - abort the current trace
+ ABORT_JIT_TSELECT();
+#endif
+ /*
+ * We save off the exception and clear the exception status. While
+ * processing the exception we might need to load some Throwable
+ * classes, and we don't want class loader exceptions to get
+ * confused with this one.
+ */
+ assert(dvmCheckException(self));
+ exception = dvmGetException(self);
+ dvmAddTrackedAlloc(exception, self);
+ dvmClearException(self);
+
+ LOGV("Handling exception %s at %s:%d\n",
+ exception->clazz->descriptor, curMethod->name,
+ dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+
+#if (INTERP_TYPE == INTERP_DBG)
+ /*
+ * Tell the debugger about it.
+ *
+ * TODO: if the exception was thrown by interpreted code, control
+ * fell through native, and then back to us, we will report the
+ * exception at the point of the throw and again here. We can avoid
+ * this by not reporting exceptions when we jump here directly from
+ * the native call code above, but then we won't report exceptions
+ * that were thrown *from* the JNI code (as opposed to *through* it).
+ *
+ * The correct solution is probably to ignore from-native exceptions
+ * here, and have the JNI exception code do the reporting to the
+ * debugger.
+ */
+ if (gDvm.debuggerActive) {
+ void* catchFrame;
+ catchRelPc = dvmFindCatchBlock(self, pc - curMethod->insns,
+ exception, true, &catchFrame);
+ dvmDbgPostException(fp, pc - curMethod->insns, catchFrame,
+ catchRelPc, exception);
+ }
+#endif
+
+ /*
+ * We need to unroll to the catch block or the nearest "break"
+ * frame.
+ *
+ * A break frame could indicate that we have reached an intermediate
+ * native call, or have gone off the top of the stack and the thread
+ * needs to exit. Either way, we return from here, leaving the
+ * exception raised.
+ *
+ * If we do find a catch block, we want to transfer execution to
+ * that point.
+ *
+ * Note this can cause an exception while resolving classes in
+ * the "catch" blocks.
+ */
+ catchRelPc = dvmFindCatchBlock(self, pc - curMethod->insns,
+ exception, false, (void*)&fp);
+
+ /*
+ * Restore the stack bounds after an overflow. This isn't going to
+ * be correct in all circumstances, e.g. if JNI code devours the
+ * exception this won't happen until some other exception gets
+ * thrown. If the code keeps pushing the stack bounds we'll end
+ * up aborting the VM.
+ *
+ * Note we want to do this *after* the call to dvmFindCatchBlock,
+ * because that may need extra stack space to resolve exception
+ * classes (e.g. through a class loader).
+ *
+ * It's possible for the stack overflow handling to cause an
+ * exception (specifically, class resolution in a "catch" block
+ * during the call above), so we could see the thread's overflow
+ * flag raised but actually be running in a "nested" interpreter
+ * frame. We don't allow doubled-up StackOverflowErrors, so
+ * we can check for this by just looking at the exception type
+ * in the cleanup function. Also, we won't unroll past the SOE
+ * point because the more-recent exception will hit a break frame
+ * as it unrolls to here.
+ */
+ if (self->stackOverflowed)
+ dvmCleanupStackOverflow(self, exception);
+
+ if (catchRelPc < 0) {
+ /* falling through to JNI code or off the bottom of the stack */
+#if DVM_SHOW_EXCEPTION >= 2
+ LOGD("Exception %s from %s:%d not caught locally\n",
+ exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+ dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+#endif
+ dvmSetException(self, exception);
+ dvmReleaseTrackedAlloc(exception, self);
+ GOTO_bail();
+ }
+
+#if DVM_SHOW_EXCEPTION >= 3
+ {
+ const Method* catchMethod = SAVEAREA_FROM_FP(fp)->method;
+ LOGD("Exception %s thrown from %s:%d to %s:%d\n",
+ exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+ dvmLineNumFromPC(curMethod, pc - curMethod->insns),
+ dvmGetMethodSourceFile(catchMethod),
+ dvmLineNumFromPC(catchMethod, catchRelPc));
+ }
+#endif
+
+ /*
+ * Adjust local variables to match self->curFrame and the
+ * updated PC.
+ */
+ //fp = (u4*) self->curFrame;
+ curMethod = SAVEAREA_FROM_FP(fp)->method;
+ //methodClass = curMethod->clazz;
+ methodClassDex = curMethod->clazz->pDvmDex;
+ pc = curMethod->insns + catchRelPc;
+ ILOGV("> pc <-- %s.%s %s", curMethod->clazz->descriptor,
+ curMethod->name, curMethod->shorty);
+ DUMP_REGS(curMethod, fp, false); // show all regs
+
+ /*
+ * Restore the exception if the handler wants it.
+ *
+ * The Dalvik spec mandates that, if an exception handler wants to
+ * do something with the exception, the first instruction executed
+ * must be "move-exception". We can pass the exception along
+ * through the thread struct, and let the move-exception instruction
+ * clear it for us.
+ *
+ * If the handler doesn't call move-exception, we don't want to
+ * finish here with an exception still pending.
+ */
+ if (INST_INST(FETCH(0)) == OP_MOVE_EXCEPTION)
+ dvmSetException(self, exception);
+
+ dvmReleaseTrackedAlloc(exception, self);
+ FINISH(0);
+ }
+GOTO_TARGET_END
+
+
+
+ /*
+ * General handling for invoke-{virtual,super,direct,static,interface},
+ * including "quick" variants.
+ *
+ * Set "methodToCall" to the Method we're calling, and "methodCallRange"
+ * depending on whether this is a "/range" instruction.
+ *
+ * For a range call:
+ * "vsrc1" holds the argument count (8 bits)
+ * "vdst" holds the first argument in the range
+ * For a non-range call:
+ * "vsrc1" holds the argument count (4 bits) and the 5th argument index
+ * "vdst" holds four 4-bit register indices
+ *
+ * The caller must EXPORT_PC before jumping here, because any method
+ * call can throw a stack overflow exception.
+ */
+GOTO_TARGET(invokeMethod, bool methodCallRange, const Method* _methodToCall,
+ u2 count, u2 regs)
+ {
+ STUB_HACK(vsrc1 = count; vdst = regs; methodToCall = _methodToCall;);
+
+ //printf("range=%d call=%p count=%d regs=0x%04x\n",
+ // methodCallRange, methodToCall, count, regs);
+ //printf(" --> %s.%s %s\n", methodToCall->clazz->descriptor,
+ // methodToCall->name, methodToCall->shorty);
+
+ u4* outs;
+ int i;
+
+ /*
+ * Copy args. This may corrupt vsrc1/vdst.
+ */
+ if (methodCallRange) {
+ // could use memcpy or a "Duff's device"; most functions have
+ // so few args it won't matter much
+ assert(vsrc1 <= curMethod->outsSize);
+ assert(vsrc1 == methodToCall->insSize);
+ outs = OUTS_FROM_FP(fp, vsrc1);
+ for (i = 0; i < vsrc1; i++)
+ outs[i] = GET_REGISTER(vdst+i);
+ } else {
+ u4 count = vsrc1 >> 4;
+
+ assert(count <= curMethod->outsSize);
+ assert(count == methodToCall->insSize);
+ assert(count <= 5);
+
+ outs = OUTS_FROM_FP(fp, count);
+#if 0
+ if (count == 5) {
+ outs[4] = GET_REGISTER(vsrc1 & 0x0f);
+ count--;
+ }
+ for (i = 0; i < (int) count; i++) {
+ outs[i] = GET_REGISTER(vdst & 0x0f);
+ vdst >>= 4;
+ }
+#else
+ // This version executes fewer instructions but is larger
+ // overall. Seems to be a teensy bit faster.
+ assert((vdst >> 16) == 0); // 16 bits -or- high 16 bits clear
+ switch (count) {
+ case 5:
+ outs[4] = GET_REGISTER(vsrc1 & 0x0f);
+ case 4:
+ outs[3] = GET_REGISTER(vdst >> 12);
+ case 3:
+ outs[2] = GET_REGISTER((vdst & 0x0f00) >> 8);
+ case 2:
+ outs[1] = GET_REGISTER((vdst & 0x00f0) >> 4);
+ case 1:
+ outs[0] = GET_REGISTER(vdst & 0x0f);
+ default:
+ ;
+ }
+#endif
+ }
+ }
+
+ /*
+ * (This was originally a "goto" target; I've kept it separate from the
+ * stuff above in case we want to refactor things again.)
+ *
+ * At this point, we have the arguments stored in the "outs" area of
+ * the current method's stack frame, and the method to call in
+ * "methodToCall". Push a new stack frame.
+ */
+ {
+ StackSaveArea* newSaveArea;
+ u4* newFp;
+
+ ILOGV("> %s%s.%s %s",
+ dvmIsNativeMethod(methodToCall) ? "(NATIVE) " : "",
+ methodToCall->clazz->descriptor, methodToCall->name,
+ methodToCall->shorty);
+
+ newFp = (u4*) SAVEAREA_FROM_FP(fp) - methodToCall->registersSize;
+ newSaveArea = SAVEAREA_FROM_FP(newFp);
+
+ /* verify that we have enough space */
+ if (true) {
+ u1* bottom;
+ bottom = (u1*) newSaveArea - methodToCall->outsSize * sizeof(u4);
+ if (bottom < self->interpStackEnd) {
+ /* stack overflow */
+ LOGV("Stack overflow on method call (start=%p end=%p newBot=%p(%d) size=%d '%s')\n",
+ self->interpStackStart, self->interpStackEnd, bottom,
+ (u1*) fp - bottom, self->interpStackSize,
+ methodToCall->name);
+ dvmHandleStackOverflow(self, methodToCall);
+ assert(dvmCheckException(self));
+ GOTO_exceptionThrown();
+ }
+ //LOGD("+++ fp=%p newFp=%p newSave=%p bottom=%p\n",
+ // fp, newFp, newSaveArea, bottom);
+ }
+
+#ifdef LOG_INSTR
+ if (methodToCall->registersSize > methodToCall->insSize) {
+ /*
+ * This makes valgrind quiet when we print registers that
+ * haven't been initialized. Turn it off when the debug
+ * messages are disabled -- we want valgrind to report any
+ * used-before-initialized issues.
+ */
+ memset(newFp, 0xcc,
+ (methodToCall->registersSize - methodToCall->insSize) * 4);
+ }
+#endif
+
+#ifdef EASY_GDB
+ newSaveArea->prevSave = SAVEAREA_FROM_FP(fp);
+#endif
+ newSaveArea->prevFrame = fp;
+ newSaveArea->savedPc = pc;
+#if defined(WITH_JIT)
+ newSaveArea->returnAddr = 0;
+#endif
+ newSaveArea->method = methodToCall;
+
+ if (!dvmIsNativeMethod(methodToCall)) {
+ /*
+ * "Call" interpreted code. Reposition the PC, update the
+ * frame pointer and other local state, and continue.
+ */
+ curMethod = methodToCall;
+ methodClassDex = curMethod->clazz->pDvmDex;
+ pc = methodToCall->insns;
+ fp = self->curFrame = newFp;
+#ifdef EASY_GDB
+ debugSaveArea = SAVEAREA_FROM_FP(newFp);
+#endif
+#if INTERP_TYPE == INTERP_DBG
+ debugIsMethodEntry = true; // profiling, debugging
+#endif
+ ILOGD("> pc <-- %s.%s %s", curMethod->clazz->descriptor,
+ curMethod->name, curMethod->shorty);
+ DUMP_REGS(curMethod, fp, true); // show input args
+ FINISH(0); // jump to method start
+ } else {
+ /* set this up for JNI locals, even if not a JNI native */
+#ifdef USE_INDIRECT_REF
+ newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
+#else
+ newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.nextEntry;
+#endif
+
+ self->curFrame = newFp;
+
+ DUMP_REGS(methodToCall, newFp, true); // show input args
+
+#if (INTERP_TYPE == INTERP_DBG)
+ if (gDvm.debuggerActive) {
+ dvmDbgPostLocationEvent(methodToCall, -1,
+ dvmGetThisPtr(curMethod, fp), DBG_METHOD_ENTRY);
+ }
+#endif
+#if (INTERP_TYPE == INTERP_DBG)
+ TRACE_METHOD_ENTER(self, methodToCall);
+#endif
+
+ {
+ ILOGD("> native <-- %s.%s %s", methodToCall->clazz->descriptor,
+ methodToCall->name, methodToCall->shorty);
+ }
+
+#if defined(WITH_JIT)
+ /* Allow the Jit to end any pending trace building */
+ CHECK_JIT_VOID();
+#endif
+
+ /*
+ * Jump through native call bridge. Because we leave no
+ * space for locals on native calls, "newFp" points directly
+ * to the method arguments.
+ */
+ (*methodToCall->nativeFunc)(newFp, &retval, methodToCall, self);
+
+#if (INTERP_TYPE == INTERP_DBG)
+ if (gDvm.debuggerActive) {
+ dvmDbgPostLocationEvent(methodToCall, -1,
+ dvmGetThisPtr(curMethod, fp), DBG_METHOD_EXIT);
+ }
+#endif
+#if (INTERP_TYPE == INTERP_DBG)
+ TRACE_METHOD_EXIT(self, methodToCall);
+#endif
+
+ /* pop frame off */
+ dvmPopJniLocals(self, newSaveArea);
+ self->curFrame = fp;
+
+ /*
+ * If the native code threw an exception, or interpreted code
+ * invoked by the native call threw one and nobody has cleared
+ * it, jump to our local exception handling.
+ */
+ if (dvmCheckException(self)) {
+ LOGV("Exception thrown by/below native code\n");
+ GOTO_exceptionThrown();
+ }
+
+ ILOGD("> retval=0x%llx (leaving native)", retval.j);
+ ILOGD("> (return from native %s.%s to %s.%s %s)",
+ methodToCall->clazz->descriptor, methodToCall->name,
+ curMethod->clazz->descriptor, curMethod->name,
+ curMethod->shorty);
+
+ //u2 invokeInstr = INST_INST(FETCH(0));
+ if (true /*invokeInstr >= OP_INVOKE_VIRTUAL &&
+ invokeInstr <= OP_INVOKE_INTERFACE*/)
+ {
+ FINISH(3);
+ } else {
+ //LOGE("Unknown invoke instr %02x at %d\n",
+ // invokeInstr, (int) (pc - curMethod->insns));
+ assert(false);
+ }
+ }
+ }
+ assert(false); // should not get here
+GOTO_TARGET_END
+
+/* File: portable/enddefs.c */
+/*--- end of opcodes ---*/
+
+#ifndef THREADED_INTERP
+ } // end of "switch"
+ } // end of "while"
+#endif
+
+bail:
+ ILOGD("|-- Leaving interpreter loop"); // note "curMethod" may be NULL
+
+ interpState->retval = retval;
+ return false;
+
+bail_switch:
+ /*
+ * The standard interpreter currently doesn't set or care about the
+ * "debugIsMethodEntry" value, so setting this is only of use if we're
+ * switching between two "debug" interpreters, which we never do.
+ *
+ * TODO: figure out if preserving this makes any sense.
+ */
+#if INTERP_TYPE == INTERP_DBG
+ interpState->debugIsMethodEntry = debugIsMethodEntry;
+#else
+ interpState->debugIsMethodEntry = false;
+#endif
+
+ /* export state changes */
+ interpState->method = curMethod;
+ interpState->pc = pc;
+ interpState->fp = fp;
+ /* debugTrackedRefStart doesn't change */
+ interpState->retval = retval; /* need for _entryPoint=ret */
+ interpState->nextMode =
+ (INTERP_TYPE == INTERP_STD) ? INTERP_DBG : INTERP_STD;
+ LOGVV(" meth='%s.%s' pc=0x%x fp=%p\n",
+ curMethod->clazz->descriptor, curMethod->name,
+ pc - curMethod->insns, fp);
+ return true;
+}
+
diff --git a/vm/mterp/out/InterpC-x86-atom.c b/vm/mterp/out/InterpC-x86-atom.c
new file mode 100644
index 0000000..6d088f7
--- /dev/null
+++ b/vm/mterp/out/InterpC-x86-atom.c
@@ -0,0 +1,2326 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'x86-atom'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: c/header.c */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* common includes */
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "mterp/Mterp.h"
+#include <math.h> // needed for fmod, fmodf
+#include "mterp/common/FindInterface.h"
+
+/*
+ * Configuration defines. These affect the C implementations, i.e. the
+ * portable interpreter(s) and C stubs.
+ *
+ * Some defines are controlled by the Makefile, e.g.:
+ * WITH_INSTR_CHECKS
+ * WITH_TRACKREF_CHECKS
+ * EASY_GDB
+ * NDEBUG
+ *
+ * If THREADED_INTERP is not defined, we use a classic "while true / switch"
+ * interpreter. If it is defined, then the tail end of each instruction
+ * handler fetches the next instruction and jumps directly to the handler.
+ * This increases the size of the "Std" interpreter by about 10%, but
+ * provides a speedup of about the same magnitude.
+ *
+ * There's a "hybrid" approach that uses a goto table instead of a switch
+ * statement, avoiding the "is the opcode in range" tests required for switch.
+ * The performance is close to the threaded version, and without the 10%
+ * size increase, but the benchmark results are off enough that it's not
+ * worth adding as a third option.
+ */
+#define THREADED_INTERP /* threaded vs. while-loop interpreter */
+
+#ifdef WITH_INSTR_CHECKS /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * ARM EABI requires 64-bit alignment for access to 64-bit data types. We
+ * can't just use pointers to copy 64-bit values out of our interpreted
+ * register set, because gcc will generate ldrd/strd.
+ *
+ * The __UNION version copies data in and out of a union. The __MEMCPY
+ * version uses a memcpy() call to do the transfer; gcc is smart enough to
+ * not actually call memcpy(). The __UNION version is very bad on ARM;
+ * it only uses one more instruction than __MEMCPY, but for some reason
+ * gcc thinks it needs separate storage for every instance of the union.
+ * On top of that, it feels the need to zero them out at the start of the
+ * method. Net result is we zero out ~700 bytes of stack space at the top
+ * of the interpreter using ARM STM instructions.
+ */
+#if defined(__ARM_EABI__)
+//# define NO_UNALIGN_64__UNION
+# define NO_UNALIGN_64__MEMCPY
+#endif
+
+//#define LOG_INSTR /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Keep a tally of accesses to fields. Currently only works if full DEX
+ * optimization is disabled.
+ */
+#ifdef PROFILE_FIELD_ACCESS
+# define UPDATE_FIELD_GET(_field) { (_field)->gets++; }
+# define UPDATE_FIELD_PUT(_field) { (_field)->puts++; }
+#else
+# define UPDATE_FIELD_GET(_field) ((void)0)
+# define UPDATE_FIELD_PUT(_field) ((void)0)
+#endif
+
+/*
+ * Export another copy of the PC on every instruction; this is largely
+ * redundant with EXPORT_PC and the debugger code. This value can be
+ * compared against what we have stored on the stack with EXPORT_PC to
+ * help ensure that we aren't missing any export calls.
+ */
+#if WITH_EXTRA_GC_CHECKS > 1
+# define EXPORT_EXTRA_PC() (self->currentPc2 = pc)
+#else
+# define EXPORT_EXTRA_PC()
+#endif
+
+/*
+ * Adjust the program counter. "_offset" is a signed int, in 16-bit units.
+ *
+ * Assumes the existence of "const u2* pc" and "const u2* curMethod->insns".
+ *
+ * We don't advance the program counter until we finish an instruction or
+ * branch, because we do want to have to unroll the PC if there's an
+ * exception.
+ */
+#ifdef CHECK_BRANCH_OFFSETS
+# define ADJUST_PC(_offset) do { \
+ int myoff = _offset; /* deref only once */ \
+ if (pc + myoff < curMethod->insns || \
+ pc + myoff >= curMethod->insns + dvmGetMethodInsnsSize(curMethod)) \
+ { \
+ char* desc; \
+ desc = dexProtoCopyMethodDescriptor(&curMethod->prototype); \
+ LOGE("Invalid branch %d at 0x%04x in %s.%s %s\n", \
+ myoff, (int) (pc - curMethod->insns), \
+ curMethod->clazz->descriptor, curMethod->name, desc); \
+ free(desc); \
+ dvmAbort(); \
+ } \
+ pc += myoff; \
+ EXPORT_EXTRA_PC(); \
+ } while (false)
+#else
+# define ADJUST_PC(_offset) do { \
+ pc += _offset; \
+ EXPORT_EXTRA_PC(); \
+ } while (false)
+#endif
+
+/*
+ * If enabled, log instructions as we execute them.
+ */
+#ifdef LOG_INSTR
+# define ILOGD(...) ILOG(LOG_DEBUG, __VA_ARGS__)
+# define ILOGV(...) ILOG(LOG_VERBOSE, __VA_ARGS__)
+# define ILOG(_level, ...) do { \
+ char debugStrBuf[128]; \
+ snprintf(debugStrBuf, sizeof(debugStrBuf), __VA_ARGS__); \
+ if (curMethod != NULL) \
+ LOG(_level, LOG_TAG"i", "%-2d|%04x%s\n", \
+ self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+ else \
+ LOG(_level, LOG_TAG"i", "%-2d|####%s\n", \
+ self->threadId, debugStrBuf); \
+ } while(false)
+void dvmDumpRegs(const Method* method, const u4* framePtr, bool inOnly);
+# define DUMP_REGS(_meth, _frame, _inOnly) dvmDumpRegs(_meth, _frame, _inOnly)
+static const char kSpacing[] = " ";
+#else
+# define ILOGD(...) ((void)0)
+# define ILOGV(...) ((void)0)
+# define DUMP_REGS(_meth, _frame, _inOnly) ((void)0)
+#endif
+
+/* get a long from an array of u4 */
+static inline s8 getLongFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { s8 ll; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.parts[0] = ptr[0];
+ conv.parts[1] = ptr[1];
+ return conv.ll;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ s8 val;
+ memcpy(&val, &ptr[idx], 8);
+ return val;
+#else
+ return *((s8*) &ptr[idx]);
+#endif
+}
+
+/* store a long into an array of u4 */
+static inline void putLongToArray(u4* ptr, int idx, s8 val)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { s8 ll; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.ll = val;
+ ptr[0] = conv.parts[0];
+ ptr[1] = conv.parts[1];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ memcpy(&ptr[idx], &val, 8);
+#else
+ *((s8*) &ptr[idx]) = val;
+#endif
+}
+
+/* get a double from an array of u4 */
+static inline double getDoubleFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { double d; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.parts[0] = ptr[0];
+ conv.parts[1] = ptr[1];
+ return conv.d;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ double dval;
+ memcpy(&dval, &ptr[idx], 8);
+ return dval;
+#else
+ return *((double*) &ptr[idx]);
+#endif
+}
+
+/* store a double into an array of u4 */
+static inline void putDoubleToArray(u4* ptr, int idx, double dval)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { double d; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.d = dval;
+ ptr[0] = conv.parts[0];
+ ptr[1] = conv.parts[1];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ memcpy(&ptr[idx], &dval, 8);
+#else
+ *((double*) &ptr[idx]) = dval;
+#endif
+}
+
+/*
+ * If enabled, validate the register number on every access. Otherwise,
+ * just do an array access.
+ *
+ * Assumes the existence of "u4* fp".
+ *
+ * "_idx" may be referenced more than once.
+ */
+#ifdef CHECK_REGISTER_INDICES
+# define GET_REGISTER(_idx) \
+ ( (_idx) < curMethod->registersSize ? \
+ (fp[(_idx)]) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER(_idx, _val) \
+ ( (_idx) < curMethod->registersSize ? \
+ (fp[(_idx)] = (u4)(_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_AS_OBJECT(_idx) ((Object *)GET_REGISTER(_idx))
+# define SET_REGISTER_AS_OBJECT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_INT(_idx) ((s4) GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ getLongFromArray(fp, (_idx)) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER_WIDE(_idx, _val) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ putLongToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_FLOAT(_idx) \
+ ( (_idx) < curMethod->registersSize ? \
+ (*((float*) &fp[(_idx)])) : (assert(!"bad reg"),1969.0f) )
+# define SET_REGISTER_FLOAT(_idx, _val) \
+ ( (_idx) < curMethod->registersSize ? \
+ (*((float*) &fp[(_idx)]) = (_val)) : (assert(!"bad reg"),1969.0f) )
+# define GET_REGISTER_DOUBLE(_idx) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ getDoubleFromArray(fp, (_idx)) : (assert(!"bad reg"),1969.0) )
+# define SET_REGISTER_DOUBLE(_idx, _val) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ putDoubleToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969.0) )
+#else
+# define GET_REGISTER(_idx) (fp[(_idx)])
+# define SET_REGISTER(_idx, _val) (fp[(_idx)] = (_val))
+# define GET_REGISTER_AS_OBJECT(_idx) ((Object*) fp[(_idx)])
+# define SET_REGISTER_AS_OBJECT(_idx, _val) (fp[(_idx)] = (u4)(_val))
+# define GET_REGISTER_INT(_idx) ((s4)GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) getLongFromArray(fp, (_idx))
+# define SET_REGISTER_WIDE(_idx, _val) putLongToArray(fp, (_idx), (_val))
+# define GET_REGISTER_FLOAT(_idx) (*((float*) &fp[(_idx)]))
+# define SET_REGISTER_FLOAT(_idx, _val) (*((float*) &fp[(_idx)]) = (_val))
+# define GET_REGISTER_DOUBLE(_idx) getDoubleFromArray(fp, (_idx))
+# define SET_REGISTER_DOUBLE(_idx, _val) putDoubleToArray(fp, (_idx), (_val))
+#endif
+
+/*
+ * Get 16 bits from the specified offset of the program counter. We always
+ * want to load 16 bits at a time from the instruction stream -- it's more
+ * efficient than 8 and won't have the alignment problems that 32 might.
+ *
+ * Assumes existence of "const u2* pc".
+ */
+#define FETCH(_offset) (pc[(_offset)])
+
+/*
+ * Extract instruction byte from 16-bit fetch (_inst is a u2).
+ */
+#define INST_INST(_inst) ((_inst) & 0xff)
+
+/*
+ * Replace the opcode (used when handling breakpoints). _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
+ * Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
+ */
+#define INST_A(_inst) (((_inst) >> 8) & 0x0f)
+#define INST_B(_inst) ((_inst) >> 12)
+
+/*
+ * Get the 8-bit "vAA" 8-bit register index from the instruction word.
+ * (_inst is u2)
+ */
+#define INST_AA(_inst) ((_inst) >> 8)
+
+/*
+ * The current PC must be available to Throwable constructors, e.g.
+ * those created by dvmThrowException(), so that the exception stack
+ * trace can be generated correctly. If we don't do this, the offset
+ * within the current method won't be shown correctly. See the notes
+ * in Exception.c.
+ *
+ * This is also used to determine the address for precise GC.
+ *
+ * Assumes existence of "u4* fp" and "const u2* pc".
+ */
+#define EXPORT_PC() (SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc)
+
+/*
+ * Determine if we need to switch to a different interpreter. "_current"
+ * is either INTERP_STD or INTERP_DBG. It should be fixed for a given
+ * interpreter generation file, which should remove the outer conditional
+ * from the following.
+ *
+ * If we're building without debug and profiling support, we never switch.
+ */
+#if defined(WITH_JIT)
+# define NEED_INTERP_SWITCH(_current) ( \
+ (_current == INTERP_STD) ? \
+ dvmJitDebuggerOrProfilerActive() : !dvmJitDebuggerOrProfilerActive() )
+#else
+# define NEED_INTERP_SWITCH(_current) ( \
+ (_current == INTERP_STD) ? \
+ dvmDebuggerOrProfilerActive() : !dvmDebuggerOrProfilerActive() )
+#endif
+
+/*
+ * Check to see if "obj" is NULL. If so, throw an exception. Assumes the
+ * pc has already been exported to the stack.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler calls into
+ * something that could throw an exception (so we have already called
+ * EXPORT_PC at the top).
+ */
+static inline bool checkForNull(Object* obj)
+{
+ if (obj == NULL) {
+ dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+ return false;
+ }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+ if (!dvmIsValidObject(obj)) {
+ LOGE("Invalid object %p\n", obj);
+ dvmAbort();
+ }
+#endif
+#ifndef NDEBUG
+ if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+ /* probable heap corruption */
+ LOGE("Invalid object class %p (in %p)\n", obj->clazz, obj);
+ dvmAbort();
+ }
+#endif
+ return true;
+}
+
+/*
+ * Check to see if "obj" is NULL. If so, export the PC into the stack
+ * frame and throw an exception.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler doesn't do
+ * anything else that can throw an exception.
+ */
+static inline bool checkForNullExportPC(Object* obj, u4* fp, const u2* pc)
+{
+ if (obj == NULL) {
+ EXPORT_PC();
+ dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+ return false;
+ }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+ if (!dvmIsValidObject(obj)) {
+ LOGE("Invalid object %p\n", obj);
+ dvmAbort();
+ }
+#endif
+#ifndef NDEBUG
+ if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+ /* probable heap corruption */
+ LOGE("Invalid object class %p (in %p)\n", obj->clazz, obj);
+ dvmAbort();
+ }
+#endif
+ return true;
+}
+
+/* File: cstubs/stubdefs.c */
+/* this is a standard (no debug support) interpreter */
+#define INTERP_TYPE INTERP_STD
+#define CHECK_DEBUG_AND_PROF() ((void)0)
+# define CHECK_TRACKED_REFS() ((void)0)
+#define CHECK_JIT_BOOL() (false)
+#define CHECK_JIT_VOID()
+#define ABORT_JIT_TSELECT() ((void)0)
+
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...) \
+ void dvmMterp_##_target(MterpGlue* glue, ## __VA_ARGS__);
+
+#define GOTO_TARGET(_target, ...) \
+ void dvmMterp_##_target(MterpGlue* glue, ## __VA_ARGS__) { \
+ u2 ref, vsrc1, vsrc2, vdst; \
+ u2 inst = FETCH(0); \
+ const Method* methodToCall; \
+ StackSaveArea* debugSaveArea;
+
+#define GOTO_TARGET_END }
+
+/*
+ * Redefine what used to be local variable accesses into MterpGlue struct
+ * references. (These are undefined down in "footer.c".)
+ */
+#define retval glue->retval
+#define pc glue->pc
+#define fp glue->fp
+#define curMethod glue->method
+#define methodClassDex glue->methodClassDex
+#define self glue->self
+#define debugTrackedRefStart glue->debugTrackedRefStart
+
+/* ugh */
+#define STUB_HACK(x) x
+
+
+/*
+ * Opcode handler framing macros. Here, each opcode is a separate function
+ * that takes a "glue" argument and returns void. We can't declare
+ * these "static" because they may be called from an assembly stub.
+ */
+#define HANDLE_OPCODE(_op) \
+ void dvmMterp_##_op(MterpGlue* glue) { \
+ u2 ref, vsrc1, vsrc2, vdst; \
+ u2 inst = FETCH(0);
+
+#define OP_END }
+
+/*
+ * Like the "portable" FINISH, but don't reload "inst", and return to caller
+ * when done.
+ */
+#define FINISH(_offset) { \
+ ADJUST_PC(_offset); \
+ CHECK_DEBUG_AND_PROF(); \
+ CHECK_TRACKED_REFS(); \
+ return; \
+ }
+
+
+/*
+ * The "goto label" statements turn into function calls followed by
+ * return statements. Some of the functions take arguments, which in the
+ * portable interpreter are handled by assigning values to globals.
+ */
+
+#define GOTO_exceptionThrown() \
+ do { \
+ dvmMterp_exceptionThrown(glue); \
+ return; \
+ } while(false)
+
+#define GOTO_returnFromMethod() \
+ do { \
+ dvmMterp_returnFromMethod(glue); \
+ return; \
+ } while(false)
+
+#define GOTO_invoke(_target, _methodCallRange) \
+ do { \
+ dvmMterp_##_target(glue, _methodCallRange); \
+ return; \
+ } while(false)
+
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst) \
+ do { \
+ dvmMterp_invokeMethod(glue, _methodCallRange, _methodToCall, \
+ _vsrc1, _vdst); \
+ return; \
+ } while(false)
+
+/*
+ * As a special case, "goto bail" turns into a longjmp. Use "bail_switch"
+ * if we need to switch to the other interpreter upon our return.
+ */
+#define GOTO_bail() \
+ dvmMterpStdBail(glue, false);
+#define GOTO_bail_switch() \
+ dvmMterpStdBail(glue, true);
+
+/*
+ * Periodically check for thread suspension.
+ *
+ * While we're at it, see if a debugger has attached or the profiler has
+ * started. If so, switch to a different "goto" table.
+ */
+#define PERIODIC_CHECKS(_entryPoint, _pcadj) { \
+ if (dvmCheckSuspendQuick(self)) { \
+ EXPORT_PC(); /* need for precise GC */ \
+ dvmCheckSuspendPending(self); \
+ } \
+ if (NEED_INTERP_SWITCH(INTERP_TYPE)) { \
+ ADJUST_PC(_pcadj); \
+ glue->entryPoint = _entryPoint; \
+ LOGVV("threadid=%d: switch to STD ep=%d adj=%d\n", \
+ self->threadId, (_entryPoint), (_pcadj)); \
+ GOTO_bail_switch(); \
+ } \
+ }
+
+/* File: c/opcommon.c */
+/* forward declarations of goto targets */
+GOTO_TARGET_DECL(filledNewArray, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtual, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuper, bool methodCallRange);
+GOTO_TARGET_DECL(invokeInterface, bool methodCallRange);
+GOTO_TARGET_DECL(invokeDirect, bool methodCallRange);
+GOTO_TARGET_DECL(invokeStatic, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtualQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuperQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeMethod, bool methodCallRange, const Method* methodToCall,
+ u2 count, u2 regs);
+GOTO_TARGET_DECL(returnFromMethod);
+GOTO_TARGET_DECL(exceptionThrown);
+
+/*
+ * ===========================================================================
+ *
+ * What follows are opcode definitions shared between multiple opcodes with
+ * minor substitutions handled by the C pre-processor. These should probably
+ * use the mterp substitution mechanism instead, with the code here moved
+ * into common fragment files (like the asm "binop.S"), although it's hard
+ * to give up the C preprocessor in favor of the much simpler text subst.
+ *
+ * ===========================================================================
+ */
+
+#define HANDLE_NUMCONV(_opcode, _opname, _fromtype, _totype) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER##_totype(vdst, \
+ GET_REGISTER##_fromtype(vsrc1)); \
+ FINISH(1);
+
+#define HANDLE_FLOAT_TO_INT(_opcode, _opname, _fromvtype, _fromrtype, \
+ _tovtype, _tortype) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ { \
+ /* spec defines specific handling for +/- inf and NaN values */ \
+ _fromvtype val; \
+ _tovtype intMin, intMax, result; \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1); \
+ val = GET_REGISTER##_fromrtype(vsrc1); \
+ intMin = (_tovtype) 1 << (sizeof(_tovtype) * 8 -1); \
+ intMax = ~intMin; \
+ result = (_tovtype) val; \
+ if (val >= intMax) /* +inf */ \
+ result = intMax; \
+ else if (val <= intMin) /* -inf */ \
+ result = intMin; \
+ else if (val != val) /* NaN */ \
+ result = 0; \
+ else \
+ result = (_tovtype) val; \
+ SET_REGISTER##_tortype(vdst, result); \
+ } \
+ FINISH(1);
+
+#define HANDLE_INT_TO_SMALL(_opcode, _opname, _type) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|int-to-%s v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER(vdst, (_type) GET_REGISTER(vsrc1)); \
+ FINISH(1);
+
+/* NOTE: the comparison result is always a signed 4-byte integer */
+#define HANDLE_OP_CMPX(_opcode, _opname, _varType, _type, _nanVal) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ int result; \
+ u2 regs; \
+ _varType val1, val2; \
+ vdst = INST_AA(inst); \
+ regs = FETCH(1); \
+ vsrc1 = regs & 0xff; \
+ vsrc2 = regs >> 8; \
+ ILOGV("|cmp%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ val1 = GET_REGISTER##_type(vsrc1); \
+ val2 = GET_REGISTER##_type(vsrc2); \
+ if (val1 == val2) \
+ result = 0; \
+ else if (val1 < val2) \
+ result = -1; \
+ else if (val1 > val2) \
+ result = 1; \
+ else \
+ result = (_nanVal); \
+ ILOGV("+ result=%d\n", result); \
+ SET_REGISTER(vdst, result); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_IF_XX(_opcode, _opname, _cmp) \
+ HANDLE_OPCODE(_opcode /*vA, vB, +CCCC*/) \
+ vsrc1 = INST_A(inst); \
+ vsrc2 = INST_B(inst); \
+ if ((s4) GET_REGISTER(vsrc1) _cmp (s4) GET_REGISTER(vsrc2)) { \
+ int branchOffset = (s2)FETCH(1); /* sign-extended */ \
+ ILOGV("|if-%s v%d,v%d,+0x%04x", (_opname), vsrc1, vsrc2, \
+ branchOffset); \
+ ILOGV("> branch taken"); \
+ if (branchOffset < 0) \
+ PERIODIC_CHECKS(kInterpEntryInstr, branchOffset); \
+ FINISH(branchOffset); \
+ } else { \
+ ILOGV("|if-%s v%d,v%d,-", (_opname), vsrc1, vsrc2); \
+ FINISH(2); \
+ }
+
+#define HANDLE_OP_IF_XXZ(_opcode, _opname, _cmp) \
+ HANDLE_OPCODE(_opcode /*vAA, +BBBB*/) \
+ vsrc1 = INST_AA(inst); \
+ if ((s4) GET_REGISTER(vsrc1) _cmp 0) { \
+ int branchOffset = (s2)FETCH(1); /* sign-extended */ \
+ ILOGV("|if-%s v%d,+0x%04x", (_opname), vsrc1, branchOffset); \
+ ILOGV("> branch taken"); \
+ if (branchOffset < 0) \
+ PERIODIC_CHECKS(kInterpEntryInstr, branchOffset); \
+ FINISH(branchOffset); \
+ } else { \
+ ILOGV("|if-%s v%d,-", (_opname), vsrc1); \
+ FINISH(2); \
+ }
+
+#define HANDLE_UNOP(_opcode, _opname, _pfx, _sfx, _type) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER##_type(vdst, _pfx GET_REGISTER##_type(vsrc1) _sfx); \
+ FINISH(1);
+
+#define HANDLE_OP_X_INT(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER(vsrc1); \
+ secondVal = GET_REGISTER(vsrc2); \
+ if (secondVal == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && secondVal == -1) { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ /* non-div/rem case */ \
+ SET_REGISTER(vdst, \
+ (s4) GET_REGISTER(vsrc1) _op (s4) GET_REGISTER(vsrc2)); \
+ } \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_SHX_INT(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER(vdst, \
+ _cast GET_REGISTER(vsrc1) _op (GET_REGISTER(vsrc2) & 0x1f)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT16(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vA, vB, #+CCCC*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ vsrc2 = FETCH(1); \
+ ILOGV("|%s-int/lit16 v%d,v%d,#+0x%04x", \
+ (_opname), vdst, vsrc1, vsrc2); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, result; \
+ firstVal = GET_REGISTER(vsrc1); \
+ if ((s2) vsrc2 == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && ((s2) vsrc2) == -1) { \
+ /* won't generate /lit16 instr for this; check anyway */ \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op (s2) vsrc2; \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ /* non-div/rem case */ \
+ SET_REGISTER(vdst, GET_REGISTER(vsrc1) _op (s2) vsrc2); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT8(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/) \
+ { \
+ u2 litInfo; \
+ vdst = INST_AA(inst); \
+ litInfo = FETCH(1); \
+ vsrc1 = litInfo & 0xff; \
+ vsrc2 = litInfo >> 8; /* constant */ \
+ ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x", \
+ (_opname), vdst, vsrc1, vsrc2); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, result; \
+ firstVal = GET_REGISTER(vsrc1); \
+ if ((s1) vsrc2 == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && ((s1) vsrc2) == -1) { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op ((s1) vsrc2); \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ SET_REGISTER(vdst, \
+ (s4) GET_REGISTER(vsrc1) _op (s1) vsrc2); \
+ } \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_SHX_INT_LIT8(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/) \
+ { \
+ u2 litInfo; \
+ vdst = INST_AA(inst); \
+ litInfo = FETCH(1); \
+ vsrc1 = litInfo & 0xff; \
+ vsrc2 = litInfo >> 8; /* constant */ \
+ ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x", \
+ (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER(vdst, \
+ _cast GET_REGISTER(vsrc1) _op (vsrc2 & 0x1f)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_INT_2ADDR(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER(vdst); \
+ secondVal = GET_REGISTER(vsrc1); \
+ if (secondVal == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && secondVal == -1) { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ SET_REGISTER(vdst, \
+ (s4) GET_REGISTER(vdst) _op (s4) GET_REGISTER(vsrc1)); \
+ } \
+ FINISH(1);
+
+#define HANDLE_OP_SHX_INT_2ADDR(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER(vdst, \
+ _cast GET_REGISTER(vdst) _op (GET_REGISTER(vsrc1) & 0x1f)); \
+ FINISH(1);
+
+#define HANDLE_OP_X_LONG(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ if (_chkdiv != 0) { \
+ s8 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER_WIDE(vsrc1); \
+ secondVal = GET_REGISTER_WIDE(vsrc2); \
+ if (secondVal == 0LL) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u8)firstVal == 0x8000000000000000ULL && \
+ secondVal == -1LL) \
+ { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER_WIDE(vdst, result); \
+ } else { \
+ SET_REGISTER_WIDE(vdst, \
+ (s8) GET_REGISTER_WIDE(vsrc1) _op (s8) GET_REGISTER_WIDE(vsrc2)); \
+ } \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_SHX_LONG(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER_WIDE(vdst, \
+ _cast GET_REGISTER_WIDE(vsrc1) _op (GET_REGISTER(vsrc2) & 0x3f)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_LONG_2ADDR(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ if (_chkdiv != 0) { \
+ s8 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER_WIDE(vdst); \
+ secondVal = GET_REGISTER_WIDE(vsrc1); \
+ if (secondVal == 0LL) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u8)firstVal == 0x8000000000000000ULL && \
+ secondVal == -1LL) \
+ { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER_WIDE(vdst, result); \
+ } else { \
+ SET_REGISTER_WIDE(vdst, \
+ (s8) GET_REGISTER_WIDE(vdst) _op (s8)GET_REGISTER_WIDE(vsrc1));\
+ } \
+ FINISH(1);
+
+#define HANDLE_OP_SHX_LONG_2ADDR(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER_WIDE(vdst, \
+ _cast GET_REGISTER_WIDE(vdst) _op (GET_REGISTER(vsrc1) & 0x3f)); \
+ FINISH(1);
+
+#define HANDLE_OP_X_FLOAT(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-float v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER_FLOAT(vdst, \
+ GET_REGISTER_FLOAT(vsrc1) _op GET_REGISTER_FLOAT(vsrc2)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_DOUBLE(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-double v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER_DOUBLE(vdst, \
+ GET_REGISTER_DOUBLE(vsrc1) _op GET_REGISTER_DOUBLE(vsrc2)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_FLOAT_2ADDR(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-float-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER_FLOAT(vdst, \
+ GET_REGISTER_FLOAT(vdst) _op GET_REGISTER_FLOAT(vsrc1)); \
+ FINISH(1);
+
+#define HANDLE_OP_X_DOUBLE_2ADDR(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-double-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER_DOUBLE(vdst, \
+ GET_REGISTER_DOUBLE(vdst) _op GET_REGISTER_DOUBLE(vsrc1)); \
+ FINISH(1);
+
+#define HANDLE_OP_AGET(_opcode, _opname, _type, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ ArrayObject* arrayObj; \
+ u2 arrayInfo; \
+ EXPORT_PC(); \
+ vdst = INST_AA(inst); \
+ arrayInfo = FETCH(1); \
+ vsrc1 = arrayInfo & 0xff; /* array ptr */ \
+ vsrc2 = arrayInfo >> 8; /* index */ \
+ ILOGV("|aget%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ arrayObj = (ArrayObject*) GET_REGISTER(vsrc1); \
+ if (!checkForNull((Object*) arrayObj)) \
+ GOTO_exceptionThrown(); \
+ if (GET_REGISTER(vsrc2) >= arrayObj->length) { \
+ LOGV("Invalid array access: %p %d (len=%d)\n", \
+ arrayObj, vsrc2, arrayObj->length); \
+ dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+ NULL); \
+ GOTO_exceptionThrown(); \
+ } \
+ SET_REGISTER##_regsize(vdst, \
+ ((_type*) arrayObj->contents)[GET_REGISTER(vsrc2)]); \
+ ILOGV("+ AGET[%d]=0x%x", GET_REGISTER(vsrc2), GET_REGISTER(vdst)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_APUT(_opcode, _opname, _type, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ ArrayObject* arrayObj; \
+ u2 arrayInfo; \
+ EXPORT_PC(); \
+ vdst = INST_AA(inst); /* AA: source value */ \
+ arrayInfo = FETCH(1); \
+ vsrc1 = arrayInfo & 0xff; /* BB: array ptr */ \
+ vsrc2 = arrayInfo >> 8; /* CC: index */ \
+ ILOGV("|aput%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ arrayObj = (ArrayObject*) GET_REGISTER(vsrc1); \
+ if (!checkForNull((Object*) arrayObj)) \
+ GOTO_exceptionThrown(); \
+ if (GET_REGISTER(vsrc2) >= arrayObj->length) { \
+ dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+ NULL); \
+ GOTO_exceptionThrown(); \
+ } \
+ ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+ ((_type*) arrayObj->contents)[GET_REGISTER(vsrc2)] = \
+ GET_REGISTER##_regsize(vdst); \
+ } \
+ FINISH(2);
+
+/*
+ * It's possible to get a bad value out of a field with sub-32-bit stores
+ * because the -quick versions always operate on 32 bits. Consider:
+ * short foo = -1 (sets a 32-bit register to 0xffffffff)
+ * iput-quick foo (writes all 32 bits to the field)
+ * short bar = 1 (sets a 32-bit register to 0x00000001)
+ * iput-short (writes the low 16 bits to the field)
+ * iget-quick foo (reads all 32 bits from the field, yielding 0xffff0001)
+ * This can only happen when optimized and non-optimized code has interleaved
+ * access to the same field. This is unlikely but possible.
+ *
+ * The easiest way to fix this is to always read/write 32 bits at a time. On
+ * a device with a 16-bit data bus this is sub-optimal. (The alternative
+ * approach is to have sub-int versions of iget-quick, but now we're wasting
+ * Dalvik instruction space and making it less likely that handler code will
+ * already be in the CPU i-cache.)
+ */
+#define HANDLE_IGET_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ InstField* ifield; \
+ Object* obj; \
+ EXPORT_PC(); \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|iget%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNull(obj)) \
+ GOTO_exceptionThrown(); \
+ ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref); \
+ if (ifield == NULL) { \
+ ifield = dvmResolveInstField(curMethod->clazz, ref); \
+ if (ifield == NULL) \
+ GOTO_exceptionThrown(); \
+ } \
+ SET_REGISTER##_regsize(vdst, \
+ dvmGetField##_ftype(obj, ifield->byteOffset)); \
+ ILOGV("+ IGET '%s'=0x%08llx", ifield->field.name, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_GET(&ifield->field); \
+ } \
+ FINISH(2);
+
+#define HANDLE_IGET_X_QUICK(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ Object* obj; \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field offset */ \
+ ILOGV("|iget%s-quick v%d,v%d,field@+%u", \
+ (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNullExportPC(obj, fp, pc)) \
+ GOTO_exceptionThrown(); \
+ SET_REGISTER##_regsize(vdst, dvmGetField##_ftype(obj, ref)); \
+ ILOGV("+ IGETQ %d=0x%08llx", ref, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_IPUT_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ InstField* ifield; \
+ Object* obj; \
+ EXPORT_PC(); \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|iput%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNull(obj)) \
+ GOTO_exceptionThrown(); \
+ ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref); \
+ if (ifield == NULL) { \
+ ifield = dvmResolveInstField(curMethod->clazz, ref); \
+ if (ifield == NULL) \
+ GOTO_exceptionThrown(); \
+ } \
+ dvmSetField##_ftype(obj, ifield->byteOffset, \
+ GET_REGISTER##_regsize(vdst)); \
+ ILOGV("+ IPUT '%s'=0x%08llx", ifield->field.name, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_PUT(&ifield->field); \
+ } \
+ FINISH(2);
+
+#define HANDLE_IPUT_X_QUICK(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ Object* obj; \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field offset */ \
+ ILOGV("|iput%s-quick v%d,v%d,field@0x%04x", \
+ (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNullExportPC(obj, fp, pc)) \
+ GOTO_exceptionThrown(); \
+ dvmSetField##_ftype(obj, ref, GET_REGISTER##_regsize(vdst)); \
+ ILOGV("+ IPUTQ %d=0x%08llx", ref, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ } \
+ FINISH(2);
+
+/*
+ * The JIT needs dvmDexGetResolvedField() to return non-null.
+ * Since we use the portable interpreter to build the trace, the extra
+ * checks in HANDLE_SGET_X and HANDLE_SPUT_X are not needed for mterp.
+ */
+#define HANDLE_SGET_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/) \
+ { \
+ StaticField* sfield; \
+ vdst = INST_AA(inst); \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|sget%s v%d,sfield@0x%04x", (_opname), vdst, ref); \
+ sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+ if (sfield == NULL) { \
+ EXPORT_PC(); \
+ sfield = dvmResolveStaticField(curMethod->clazz, ref); \
+ if (sfield == NULL) \
+ GOTO_exceptionThrown(); \
+ if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) { \
+ ABORT_JIT_TSELECT(); \
+ } \
+ } \
+ SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield)); \
+ ILOGV("+ SGET '%s'=0x%08llx", \
+ sfield->field.name, (u8)GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_GET(&sfield->field); \
+ } \
+ FINISH(2);
+
+#define HANDLE_SPUT_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/) \
+ { \
+ StaticField* sfield; \
+ vdst = INST_AA(inst); \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|sput%s v%d,sfield@0x%04x", (_opname), vdst, ref); \
+ sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+ if (sfield == NULL) { \
+ EXPORT_PC(); \
+ sfield = dvmResolveStaticField(curMethod->clazz, ref); \
+ if (sfield == NULL) \
+ GOTO_exceptionThrown(); \
+ if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) { \
+ ABORT_JIT_TSELECT(); \
+ } \
+ } \
+ dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst)); \
+ ILOGV("+ SPUT '%s'=0x%08llx", \
+ sfield->field.name, (u8)GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_PUT(&sfield->field); \
+ } \
+ FINISH(2);
+
+/* File: c/OP_IGET_VOLATILE.c */
+HANDLE_IGET_X(OP_IGET_VOLATILE, "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_IPUT_VOLATILE.c */
+HANDLE_IPUT_X(OP_IPUT_VOLATILE, "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_SGET_VOLATILE.c */
+HANDLE_SGET_X(OP_SGET_VOLATILE, "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_SPUT_VOLATILE.c */
+HANDLE_SPUT_X(OP_SPUT_VOLATILE, "-volatile", IntVolatile, )
+OP_END
+
+/* File: c/OP_IGET_OBJECT_VOLATILE.c */
+HANDLE_IGET_X(OP_IGET_OBJECT_VOLATILE, "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_IGET_WIDE_VOLATILE.c */
+HANDLE_IGET_X(OP_IGET_WIDE_VOLATILE, "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_IPUT_WIDE_VOLATILE.c */
+HANDLE_IPUT_X(OP_IPUT_WIDE_VOLATILE, "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_SGET_WIDE_VOLATILE.c */
+HANDLE_SGET_X(OP_SGET_WIDE_VOLATILE, "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_SPUT_WIDE_VOLATILE.c */
+HANDLE_SPUT_X(OP_SPUT_WIDE_VOLATILE, "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_BREAKPOINT.c */
+HANDLE_OPCODE(OP_BREAKPOINT)
+#if (INTERP_TYPE == INTERP_DBG)
+ {
+ /*
+ * Restart this instruction with the original opcode. We do
+ * this by simply jumping to the handler.
+ *
+ * It's probably not necessary to update "inst", but we do it
+ * for the sake of anything that needs to do disambiguation in a
+ * common handler with INST_INST.
+ *
+ * The breakpoint itself is handled over in updateDebugger(),
+ * because we need to detect other events (method entry, single
+ * step) and report them in the same event packet, and we're not
+ * yet handling those through breakpoint instructions. By the
+ * time we get here, the breakpoint has already been handled and
+ * the thread resumed.
+ */
+ u1 originalOpCode = dvmGetOriginalOpCode(pc);
+ LOGV("+++ break 0x%02x (0x%04x -> 0x%04x)\n", originalOpCode, inst,
+ INST_REPLACE_OP(inst, originalOpCode));
+ inst = INST_REPLACE_OP(inst, originalOpCode);
+ FINISH_BKPT(originalOpCode);
+ }
+#else
+ LOGE("Breakpoint hit in non-debug interpreter\n");
+ dvmAbort();
+#endif
+OP_END
+
+/* File: c/OP_EXECUTE_INLINE_RANGE.c */
+HANDLE_OPCODE(OP_EXECUTE_INLINE_RANGE /*{vCCCC..v(CCCC+AA-1)}, inline@BBBB*/)
+ {
+ u4 arg0, arg1, arg2, arg3;
+ arg0 = arg1 = arg2 = arg3 = 0; /* placate gcc */
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst); /* #of args */
+ ref = FETCH(1); /* inline call "ref" */
+ vdst = FETCH(2); /* range base */
+ ILOGV("|execute-inline-range args=%d @%d {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+
+ assert((vdst >> 16) == 0); // 16-bit type -or- high 16 bits clear
+ assert(vsrc1 <= 4);
+
+ switch (vsrc1) {
+ case 4:
+ arg3 = GET_REGISTER(vdst+3);
+ /* fall through */
+ case 3:
+ arg2 = GET_REGISTER(vdst+2);
+ /* fall through */
+ case 2:
+ arg1 = GET_REGISTER(vdst+1);
+ /* fall through */
+ case 1:
+ arg0 = GET_REGISTER(vdst+0);
+ /* fall through */
+ default: // case 0
+ ;
+ }
+
+#if INTERP_TYPE == INTERP_DBG
+ if (!dvmPerformInlineOp4Dbg(arg0, arg1, arg2, arg3, &retval, ref))
+ GOTO_exceptionThrown();
+#else
+ if (!dvmPerformInlineOp4Std(arg0, arg1, arg2, arg3, &retval, ref))
+ GOTO_exceptionThrown();
+#endif
+ }
+ FINISH(3);
+OP_END
+
+/* File: c/OP_IPUT_OBJECT_VOLATILE.c */
+HANDLE_IPUT_X(OP_IPUT_OBJECT_VOLATILE, "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SGET_OBJECT_VOLATILE.c */
+HANDLE_SGET_X(OP_SGET_OBJECT_VOLATILE, "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/OP_SPUT_OBJECT_VOLATILE.c */
+HANDLE_SPUT_X(OP_SPUT_OBJECT_VOLATILE, "-object-volatile", ObjectVolatile, _AS_OBJECT)
+OP_END
+
+/* File: c/gotoTargets.c */
+/*
+ * C footer. This has some common code shared by the various targets.
+ */
+
+/*
+ * Everything from here on is a "goto target". In the basic interpreter
+ * we jump into these targets and then jump directly to the handler for
+ * next instruction. Here, these are subroutines that return to the caller.
+ */
+
+GOTO_TARGET(filledNewArray, bool methodCallRange)
+ {
+ ClassObject* arrayClass;
+ ArrayObject* newArray;
+ u4* contents;
+ char typeCh;
+ int i;
+ u4 arg5;
+
+ EXPORT_PC();
+
+ ref = FETCH(1); /* class ref */
+ vdst = FETCH(2); /* first 4 regs -or- range base */
+
+ if (methodCallRange) {
+ vsrc1 = INST_AA(inst); /* #of elements */
+ arg5 = -1; /* silence compiler warning */
+ ILOGV("|filled-new-array-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ } else {
+ arg5 = INST_A(inst);
+ vsrc1 = INST_B(inst); /* #of elements */
+ ILOGV("|filled-new-array args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1, ref, vdst, arg5);
+ }
+
+ /*
+ * Resolve the array class.
+ */
+ arrayClass = dvmDexGetResolvedClass(methodClassDex, ref);
+ if (arrayClass == NULL) {
+ arrayClass = dvmResolveClass(curMethod->clazz, ref, false);
+ if (arrayClass == NULL)
+ GOTO_exceptionThrown();
+ }
+ /*
+ if (!dvmIsArrayClass(arrayClass)) {
+ dvmThrowException("Ljava/lang/RuntimeError;",
+ "filled-new-array needs array class");
+ GOTO_exceptionThrown();
+ }
+ */
+ /* verifier guarantees this is an array class */
+ assert(dvmIsArrayClass(arrayClass));
+ assert(dvmIsClassInitialized(arrayClass));
+
+ /*
+ * Create an array of the specified type.
+ */
+ LOGVV("+++ filled-new-array type is '%s'\n", arrayClass->descriptor);
+ typeCh = arrayClass->descriptor[1];
+ if (typeCh == 'D' || typeCh == 'J') {
+ /* category 2 primitives not allowed */
+ dvmThrowException("Ljava/lang/RuntimeError;",
+ "bad filled array req");
+ GOTO_exceptionThrown();
+ } else if (typeCh != 'L' && typeCh != '[' && typeCh != 'I') {
+ /* TODO: requires multiple "fill in" loops with different widths */
+ LOGE("non-int primitives not implemented\n");
+ dvmThrowException("Ljava/lang/InternalError;",
+ "filled-new-array not implemented for anything but 'int'");
+ GOTO_exceptionThrown();
+ }
+
+ newArray = dvmAllocArrayByClass(arrayClass, vsrc1, ALLOC_DONT_TRACK);
+ if (newArray == NULL)
+ GOTO_exceptionThrown();
+
+ /*
+ * Fill in the elements. It's legal for vsrc1 to be zero.
+ */
+ contents = (u4*) newArray->contents;
+ if (methodCallRange) {
+ for (i = 0; i < vsrc1; i++)
+ contents[i] = GET_REGISTER(vdst+i);
+ } else {
+ assert(vsrc1 <= 5);
+ if (vsrc1 == 5) {
+ contents[4] = GET_REGISTER(arg5);
+ vsrc1--;
+ }
+ for (i = 0; i < vsrc1; i++) {
+ contents[i] = GET_REGISTER(vdst & 0x0f);
+ vdst >>= 4;
+ }
+ }
+ if (typeCh == 'L' || typeCh == '[') {
+ dvmWriteBarrierArray(newArray, 0, newArray->length);
+ }
+
+ retval.l = newArray;
+ }
+ FINISH(3);
+GOTO_TARGET_END
+
+
+GOTO_TARGET(invokeVirtual, bool methodCallRange)
+ {
+ Method* baseMethod;
+ Object* thisPtr;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* method ref */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ /*
+ * The object against which we are executing a method is always
+ * in the first argument.
+ */
+ if (methodCallRange) {
+ assert(vsrc1 > 0);
+ ILOGV("|invoke-virtual-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ thisPtr = (Object*) GET_REGISTER(vdst);
+ } else {
+ assert((vsrc1>>4) > 0);
+ ILOGV("|invoke-virtual args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+ thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+ }
+
+ if (!checkForNull(thisPtr))
+ GOTO_exceptionThrown();
+
+ /*
+ * Resolve the method. This is the correct method for the static
+ * type of the object. We also verify access permissions here.
+ */
+ baseMethod = dvmDexGetResolvedMethod(methodClassDex, ref);
+ if (baseMethod == NULL) {
+ baseMethod = dvmResolveMethod(curMethod->clazz, ref,METHOD_VIRTUAL);
+ if (baseMethod == NULL) {
+ ILOGV("+ unknown method or access denied\n");
+ GOTO_exceptionThrown();
+ }
+ }
+
+ /*
+ * Combine the object we found with the vtable offset in the
+ * method.
+ */
+ assert(baseMethod->methodIndex < thisPtr->clazz->vtableCount);
+ methodToCall = thisPtr->clazz->vtable[baseMethod->methodIndex];
+
+#if defined(WITH_JIT) && (INTERP_TYPE == INTERP_DBG)
+ callsiteClass = thisPtr->clazz;
+#endif
+
+#if 0
+ if (dvmIsAbstractMethod(methodToCall)) {
+ /*
+ * This can happen if you create two classes, Base and Sub, where
+ * Sub is a sub-class of Base. Declare a protected abstract
+ * method foo() in Base, and invoke foo() from a method in Base.
+ * Base is an "abstract base class" and is never instantiated
+ * directly. Now, Override foo() in Sub, and use Sub. This
+ * Works fine unless Sub stops providing an implementation of
+ * the method.
+ */
+ dvmThrowException("Ljava/lang/AbstractMethodError;",
+ "abstract method not implemented");
+ GOTO_exceptionThrown();
+ }
+#else
+ assert(!dvmIsAbstractMethod(methodToCall) ||
+ methodToCall->nativeFunc != NULL);
+#endif
+
+ LOGVV("+++ base=%s.%s virtual[%d]=%s.%s\n",
+ baseMethod->clazz->descriptor, baseMethod->name,
+ (u4) baseMethod->methodIndex,
+ methodToCall->clazz->descriptor, methodToCall->name);
+ assert(methodToCall != NULL);
+
+#if 0
+ if (vsrc1 != methodToCall->insSize) {
+ LOGW("WRONG METHOD: base=%s.%s virtual[%d]=%s.%s\n",
+ baseMethod->clazz->descriptor, baseMethod->name,
+ (u4) baseMethod->methodIndex,
+ methodToCall->clazz->descriptor, methodToCall->name);
+ //dvmDumpClass(baseMethod->clazz);
+ //dvmDumpClass(methodToCall->clazz);
+ dvmDumpAllClasses(0);
+ }
+#endif
+
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+ }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeSuper, bool methodCallRange)
+ {
+ Method* baseMethod;
+ u2 thisReg;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* method ref */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ if (methodCallRange) {
+ ILOGV("|invoke-super-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ thisReg = vdst;
+ } else {
+ ILOGV("|invoke-super args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+ thisReg = vdst & 0x0f;
+ }
+ /* impossible in well-formed code, but we must check nevertheless */
+ if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+ GOTO_exceptionThrown();
+
+ /*
+ * Resolve the method. This is the correct method for the static
+ * type of the object. We also verify access permissions here.
+ * The first arg to dvmResolveMethod() is just the referring class
+ * (used for class loaders and such), so we don't want to pass
+ * the superclass into the resolution call.
+ */
+ baseMethod = dvmDexGetResolvedMethod(methodClassDex, ref);
+ if (baseMethod == NULL) {
+ baseMethod = dvmResolveMethod(curMethod->clazz, ref,METHOD_VIRTUAL);
+ if (baseMethod == NULL) {
+ ILOGV("+ unknown method or access denied\n");
+ GOTO_exceptionThrown();
+ }
+ }
+
+ /*
+ * Combine the object we found with the vtable offset in the
+ * method's class.
+ *
+ * We're using the current method's class' superclass, not the
+ * superclass of "this". This is because we might be executing
+ * in a method inherited from a superclass, and we want to run
+ * in that class' superclass.
+ */
+ if (baseMethod->methodIndex >= curMethod->clazz->super->vtableCount) {
+ /*
+ * Method does not exist in the superclass. Could happen if
+ * superclass gets updated.
+ */
+ dvmThrowException("Ljava/lang/NoSuchMethodError;",
+ baseMethod->name);
+ GOTO_exceptionThrown();
+ }
+ methodToCall = curMethod->clazz->super->vtable[baseMethod->methodIndex];
+#if 0
+ if (dvmIsAbstractMethod(methodToCall)) {
+ dvmThrowException("Ljava/lang/AbstractMethodError;",
+ "abstract method not implemented");
+ GOTO_exceptionThrown();
+ }
+#else
+ assert(!dvmIsAbstractMethod(methodToCall) ||
+ methodToCall->nativeFunc != NULL);
+#endif
+ LOGVV("+++ base=%s.%s super-virtual=%s.%s\n",
+ baseMethod->clazz->descriptor, baseMethod->name,
+ methodToCall->clazz->descriptor, methodToCall->name);
+ assert(methodToCall != NULL);
+
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+ }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeInterface, bool methodCallRange)
+ {
+ Object* thisPtr;
+ ClassObject* thisClass;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* method ref */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ /*
+ * The object against which we are executing a method is always
+ * in the first argument.
+ */
+ if (methodCallRange) {
+ assert(vsrc1 > 0);
+ ILOGV("|invoke-interface-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ thisPtr = (Object*) GET_REGISTER(vdst);
+ } else {
+ assert((vsrc1>>4) > 0);
+ ILOGV("|invoke-interface args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+ thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+ }
+ if (!checkForNull(thisPtr))
+ GOTO_exceptionThrown();
+
+ thisClass = thisPtr->clazz;
+
+#if defined(WITH_JIT) && (INTERP_TYPE == INTERP_DBG)
+ callsiteClass = thisClass;
+#endif
+
+ /*
+ * Given a class and a method index, find the Method* with the
+ * actual code we want to execute.
+ */
+ methodToCall = dvmFindInterfaceMethodInCache(thisClass, ref, curMethod,
+ methodClassDex);
+ if (methodToCall == NULL) {
+ assert(dvmCheckException(self));
+ GOTO_exceptionThrown();
+ }
+
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+ }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeDirect, bool methodCallRange)
+ {
+ u2 thisReg;
+
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* method ref */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ EXPORT_PC();
+
+ if (methodCallRange) {
+ ILOGV("|invoke-direct-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ thisReg = vdst;
+ } else {
+ ILOGV("|invoke-direct args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+ thisReg = vdst & 0x0f;
+ }
+ if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+ GOTO_exceptionThrown();
+
+ methodToCall = dvmDexGetResolvedMethod(methodClassDex, ref);
+ if (methodToCall == NULL) {
+ methodToCall = dvmResolveMethod(curMethod->clazz, ref,
+ METHOD_DIRECT);
+ if (methodToCall == NULL) {
+ ILOGV("+ unknown direct method\n"); // should be impossible
+ GOTO_exceptionThrown();
+ }
+ }
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+ }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeStatic, bool methodCallRange)
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* method ref */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ EXPORT_PC();
+
+ if (methodCallRange)
+ ILOGV("|invoke-static-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ else
+ ILOGV("|invoke-static args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+
+ methodToCall = dvmDexGetResolvedMethod(methodClassDex, ref);
+ if (methodToCall == NULL) {
+ methodToCall = dvmResolveMethod(curMethod->clazz, ref, METHOD_STATIC);
+ if (methodToCall == NULL) {
+ ILOGV("+ unknown method\n");
+ GOTO_exceptionThrown();
+ }
+
+ /*
+ * The JIT needs dvmDexGetResolvedMethod() to return non-null.
+ * Since we use the portable interpreter to build the trace, this extra
+ * check is not needed for mterp.
+ */
+ if (dvmDexGetResolvedMethod(methodClassDex, ref) == NULL) {
+ /* Class initialization is still ongoing */
+ ABORT_JIT_TSELECT();
+ }
+ }
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeVirtualQuick, bool methodCallRange)
+ {
+ Object* thisPtr;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* vtable index */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ /*
+ * The object against which we are executing a method is always
+ * in the first argument.
+ */
+ if (methodCallRange) {
+ assert(vsrc1 > 0);
+ ILOGV("|invoke-virtual-quick-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ thisPtr = (Object*) GET_REGISTER(vdst);
+ } else {
+ assert((vsrc1>>4) > 0);
+ ILOGV("|invoke-virtual-quick args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+ thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+ }
+
+ if (!checkForNull(thisPtr))
+ GOTO_exceptionThrown();
+
+#if defined(WITH_JIT) && (INTERP_TYPE == INTERP_DBG)
+ callsiteClass = thisPtr->clazz;
+#endif
+
+ /*
+ * Combine the object we found with the vtable offset in the
+ * method.
+ */
+ assert(ref < thisPtr->clazz->vtableCount);
+ methodToCall = thisPtr->clazz->vtable[ref];
+
+#if 0
+ if (dvmIsAbstractMethod(methodToCall)) {
+ dvmThrowException("Ljava/lang/AbstractMethodError;",
+ "abstract method not implemented");
+ GOTO_exceptionThrown();
+ }
+#else
+ assert(!dvmIsAbstractMethod(methodToCall) ||
+ methodToCall->nativeFunc != NULL);
+#endif
+
+ LOGVV("+++ virtual[%d]=%s.%s\n",
+ ref, methodToCall->clazz->descriptor, methodToCall->name);
+ assert(methodToCall != NULL);
+
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+ }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeSuperQuick, bool methodCallRange)
+ {
+ u2 thisReg;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* vtable index */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ if (methodCallRange) {
+ ILOGV("|invoke-super-quick-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ thisReg = vdst;
+ } else {
+ ILOGV("|invoke-super-quick args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+ thisReg = vdst & 0x0f;
+ }
+ /* impossible in well-formed code, but we must check nevertheless */
+ if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+ GOTO_exceptionThrown();
+
+#if 0 /* impossible in optimized + verified code */
+ if (ref >= curMethod->clazz->super->vtableCount) {
+ dvmThrowException("Ljava/lang/NoSuchMethodError;", NULL);
+ GOTO_exceptionThrown();
+ }
+#else
+ assert(ref < curMethod->clazz->super->vtableCount);
+#endif
+
+ /*
+ * Combine the object we found with the vtable offset in the
+ * method's class.
+ *
+ * We're using the current method's class' superclass, not the
+ * superclass of "this". This is because we might be executing
+ * in a method inherited from a superclass, and we want to run
+ * in the method's class' superclass.
+ */
+ methodToCall = curMethod->clazz->super->vtable[ref];
+
+#if 0
+ if (dvmIsAbstractMethod(methodToCall)) {
+ dvmThrowException("Ljava/lang/AbstractMethodError;",
+ "abstract method not implemented");
+ GOTO_exceptionThrown();
+ }
+#else
+ assert(!dvmIsAbstractMethod(methodToCall) ||
+ methodToCall->nativeFunc != NULL);
+#endif
+ LOGVV("+++ super-virtual[%d]=%s.%s\n",
+ ref, methodToCall->clazz->descriptor, methodToCall->name);
+ assert(methodToCall != NULL);
+
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+ }
+GOTO_TARGET_END
+
+
+ /*
+ * General handling for return-void, return, and return-wide. Put the
+ * return value in "retval" before jumping here.
+ */
+GOTO_TARGET(returnFromMethod)
+ {
+ StackSaveArea* saveArea;
+
+ /*
+ * We must do this BEFORE we pop the previous stack frame off, so
+ * that the GC can see the return value (if any) in the local vars.
+ *
+ * Since this is now an interpreter switch point, we must do it before
+ * we do anything at all.
+ */
+ PERIODIC_CHECKS(kInterpEntryReturn, 0);
+
+ ILOGV("> retval=0x%llx (leaving %s.%s %s)",
+ retval.j, curMethod->clazz->descriptor, curMethod->name,
+ curMethod->shorty);
+ //DUMP_REGS(curMethod, fp);
+
+ saveArea = SAVEAREA_FROM_FP(fp);
+
+#ifdef EASY_GDB
+ debugSaveArea = saveArea;
+#endif
+#if (INTERP_TYPE == INTERP_DBG)
+ TRACE_METHOD_EXIT(self, curMethod);
+#endif
+
+ /* back up to previous frame and see if we hit a break */
+ fp = saveArea->prevFrame;
+ assert(fp != NULL);
+ if (dvmIsBreakFrame(fp)) {
+ /* bail without popping the method frame from stack */
+ LOGVV("+++ returned into break frame\n");
+#if defined(WITH_JIT)
+ /* Let the Jit know the return is terminating normally */
+ CHECK_JIT_VOID();
+#endif
+ GOTO_bail();
+ }
+
+ /* update thread FP, and reset local variables */
+ self->curFrame = fp;
+ curMethod = SAVEAREA_FROM_FP(fp)->method;
+ //methodClass = curMethod->clazz;
+ methodClassDex = curMethod->clazz->pDvmDex;
+ pc = saveArea->savedPc;
+ ILOGD("> (return to %s.%s %s)", curMethod->clazz->descriptor,
+ curMethod->name, curMethod->shorty);
+
+ /* use FINISH on the caller's invoke instruction */
+ //u2 invokeInstr = INST_INST(FETCH(0));
+ if (true /*invokeInstr >= OP_INVOKE_VIRTUAL &&
+ invokeInstr <= OP_INVOKE_INTERFACE*/)
+ {
+ FINISH(3);
+ } else {
+ //LOGE("Unknown invoke instr %02x at %d\n",
+ // invokeInstr, (int) (pc - curMethod->insns));
+ assert(false);
+ }
+ }
+GOTO_TARGET_END
+
+
+ /*
+ * Jump here when the code throws an exception.
+ *
+ * By the time we get here, the Throwable has been created and the stack
+ * trace has been saved off.
+ */
+GOTO_TARGET(exceptionThrown)
+ {
+ Object* exception;
+ int catchRelPc;
+
+ /*
+ * Since this is now an interpreter switch point, we must do it before
+ * we do anything at all.
+ */
+ PERIODIC_CHECKS(kInterpEntryThrow, 0);
+
+#if defined(WITH_JIT)
+ // Something threw during trace selection - abort the current trace
+ ABORT_JIT_TSELECT();
+#endif
+ /*
+ * We save off the exception and clear the exception status. While
+ * processing the exception we might need to load some Throwable
+ * classes, and we don't want class loader exceptions to get
+ * confused with this one.
+ */
+ assert(dvmCheckException(self));
+ exception = dvmGetException(self);
+ dvmAddTrackedAlloc(exception, self);
+ dvmClearException(self);
+
+ LOGV("Handling exception %s at %s:%d\n",
+ exception->clazz->descriptor, curMethod->name,
+ dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+
+#if (INTERP_TYPE == INTERP_DBG)
+ /*
+ * Tell the debugger about it.
+ *
+ * TODO: if the exception was thrown by interpreted code, control
+ * fell through native, and then back to us, we will report the
+ * exception at the point of the throw and again here. We can avoid
+ * this by not reporting exceptions when we jump here directly from
+ * the native call code above, but then we won't report exceptions
+ * that were thrown *from* the JNI code (as opposed to *through* it).
+ *
+ * The correct solution is probably to ignore from-native exceptions
+ * here, and have the JNI exception code do the reporting to the
+ * debugger.
+ */
+ if (gDvm.debuggerActive) {
+ void* catchFrame;
+ catchRelPc = dvmFindCatchBlock(self, pc - curMethod->insns,
+ exception, true, &catchFrame);
+ dvmDbgPostException(fp, pc - curMethod->insns, catchFrame,
+ catchRelPc, exception);
+ }
+#endif
+
+ /*
+ * We need to unroll to the catch block or the nearest "break"
+ * frame.
+ *
+ * A break frame could indicate that we have reached an intermediate
+ * native call, or have gone off the top of the stack and the thread
+ * needs to exit. Either way, we return from here, leaving the
+ * exception raised.
+ *
+ * If we do find a catch block, we want to transfer execution to
+ * that point.
+ *
+ * Note this can cause an exception while resolving classes in
+ * the "catch" blocks.
+ */
+ catchRelPc = dvmFindCatchBlock(self, pc - curMethod->insns,
+ exception, false, (void*)&fp);
+
+ /*
+ * Restore the stack bounds after an overflow. This isn't going to
+ * be correct in all circumstances, e.g. if JNI code devours the
+ * exception this won't happen until some other exception gets
+ * thrown. If the code keeps pushing the stack bounds we'll end
+ * up aborting the VM.
+ *
+ * Note we want to do this *after* the call to dvmFindCatchBlock,
+ * because that may need extra stack space to resolve exception
+ * classes (e.g. through a class loader).
+ *
+ * It's possible for the stack overflow handling to cause an
+ * exception (specifically, class resolution in a "catch" block
+ * during the call above), so we could see the thread's overflow
+ * flag raised but actually be running in a "nested" interpreter
+ * frame. We don't allow doubled-up StackOverflowErrors, so
+ * we can check for this by just looking at the exception type
+ * in the cleanup function. Also, we won't unroll past the SOE
+ * point because the more-recent exception will hit a break frame
+ * as it unrolls to here.
+ */
+ if (self->stackOverflowed)
+ dvmCleanupStackOverflow(self, exception);
+
+ if (catchRelPc < 0) {
+ /* falling through to JNI code or off the bottom of the stack */
+#if DVM_SHOW_EXCEPTION >= 2
+ LOGD("Exception %s from %s:%d not caught locally\n",
+ exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+ dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+#endif
+ dvmSetException(self, exception);
+ dvmReleaseTrackedAlloc(exception, self);
+ GOTO_bail();
+ }
+
+#if DVM_SHOW_EXCEPTION >= 3
+ {
+ const Method* catchMethod = SAVEAREA_FROM_FP(fp)->method;
+ LOGD("Exception %s thrown from %s:%d to %s:%d\n",
+ exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+ dvmLineNumFromPC(curMethod, pc - curMethod->insns),
+ dvmGetMethodSourceFile(catchMethod),
+ dvmLineNumFromPC(catchMethod, catchRelPc));
+ }
+#endif
+
+ /*
+ * Adjust local variables to match self->curFrame and the
+ * updated PC.
+ */
+ //fp = (u4*) self->curFrame;
+ curMethod = SAVEAREA_FROM_FP(fp)->method;
+ //methodClass = curMethod->clazz;
+ methodClassDex = curMethod->clazz->pDvmDex;
+ pc = curMethod->insns + catchRelPc;
+ ILOGV("> pc <-- %s.%s %s", curMethod->clazz->descriptor,
+ curMethod->name, curMethod->shorty);
+ DUMP_REGS(curMethod, fp, false); // show all regs
+
+ /*
+ * Restore the exception if the handler wants it.
+ *
+ * The Dalvik spec mandates that, if an exception handler wants to
+ * do something with the exception, the first instruction executed
+ * must be "move-exception". We can pass the exception along
+ * through the thread struct, and let the move-exception instruction
+ * clear it for us.
+ *
+ * If the handler doesn't call move-exception, we don't want to
+ * finish here with an exception still pending.
+ */
+ if (INST_INST(FETCH(0)) == OP_MOVE_EXCEPTION)
+ dvmSetException(self, exception);
+
+ dvmReleaseTrackedAlloc(exception, self);
+ FINISH(0);
+ }
+GOTO_TARGET_END
+
+
+
+ /*
+ * General handling for invoke-{virtual,super,direct,static,interface},
+ * including "quick" variants.
+ *
+ * Set "methodToCall" to the Method we're calling, and "methodCallRange"
+ * depending on whether this is a "/range" instruction.
+ *
+ * For a range call:
+ * "vsrc1" holds the argument count (8 bits)
+ * "vdst" holds the first argument in the range
+ * For a non-range call:
+ * "vsrc1" holds the argument count (4 bits) and the 5th argument index
+ * "vdst" holds four 4-bit register indices
+ *
+ * The caller must EXPORT_PC before jumping here, because any method
+ * call can throw a stack overflow exception.
+ */
+GOTO_TARGET(invokeMethod, bool methodCallRange, const Method* _methodToCall,
+ u2 count, u2 regs)
+ {
+ STUB_HACK(vsrc1 = count; vdst = regs; methodToCall = _methodToCall;);
+
+ //printf("range=%d call=%p count=%d regs=0x%04x\n",
+ // methodCallRange, methodToCall, count, regs);
+ //printf(" --> %s.%s %s\n", methodToCall->clazz->descriptor,
+ // methodToCall->name, methodToCall->shorty);
+
+ u4* outs;
+ int i;
+
+ /*
+ * Copy args. This may corrupt vsrc1/vdst.
+ */
+ if (methodCallRange) {
+ // could use memcpy or a "Duff's device"; most functions have
+ // so few args it won't matter much
+ assert(vsrc1 <= curMethod->outsSize);
+ assert(vsrc1 == methodToCall->insSize);
+ outs = OUTS_FROM_FP(fp, vsrc1);
+ for (i = 0; i < vsrc1; i++)
+ outs[i] = GET_REGISTER(vdst+i);
+ } else {
+ u4 count = vsrc1 >> 4;
+
+ assert(count <= curMethod->outsSize);
+ assert(count == methodToCall->insSize);
+ assert(count <= 5);
+
+ outs = OUTS_FROM_FP(fp, count);
+#if 0
+ if (count == 5) {
+ outs[4] = GET_REGISTER(vsrc1 & 0x0f);
+ count--;
+ }
+ for (i = 0; i < (int) count; i++) {
+ outs[i] = GET_REGISTER(vdst & 0x0f);
+ vdst >>= 4;
+ }
+#else
+ // This version executes fewer instructions but is larger
+ // overall. Seems to be a teensy bit faster.
+ assert((vdst >> 16) == 0); // 16 bits -or- high 16 bits clear
+ switch (count) {
+ case 5:
+ outs[4] = GET_REGISTER(vsrc1 & 0x0f);
+ case 4:
+ outs[3] = GET_REGISTER(vdst >> 12);
+ case 3:
+ outs[2] = GET_REGISTER((vdst & 0x0f00) >> 8);
+ case 2:
+ outs[1] = GET_REGISTER((vdst & 0x00f0) >> 4);
+ case 1:
+ outs[0] = GET_REGISTER(vdst & 0x0f);
+ default:
+ ;
+ }
+#endif
+ }
+ }
+
+ /*
+ * (This was originally a "goto" target; I've kept it separate from the
+ * stuff above in case we want to refactor things again.)
+ *
+ * At this point, we have the arguments stored in the "outs" area of
+ * the current method's stack frame, and the method to call in
+ * "methodToCall". Push a new stack frame.
+ */
+ {
+ StackSaveArea* newSaveArea;
+ u4* newFp;
+
+ ILOGV("> %s%s.%s %s",
+ dvmIsNativeMethod(methodToCall) ? "(NATIVE) " : "",
+ methodToCall->clazz->descriptor, methodToCall->name,
+ methodToCall->shorty);
+
+ newFp = (u4*) SAVEAREA_FROM_FP(fp) - methodToCall->registersSize;
+ newSaveArea = SAVEAREA_FROM_FP(newFp);
+
+ /* verify that we have enough space */
+ if (true) {
+ u1* bottom;
+ bottom = (u1*) newSaveArea - methodToCall->outsSize * sizeof(u4);
+ if (bottom < self->interpStackEnd) {
+ /* stack overflow */
+ LOGV("Stack overflow on method call (start=%p end=%p newBot=%p(%d) size=%d '%s')\n",
+ self->interpStackStart, self->interpStackEnd, bottom,
+ (u1*) fp - bottom, self->interpStackSize,
+ methodToCall->name);
+ dvmHandleStackOverflow(self, methodToCall);
+ assert(dvmCheckException(self));
+ GOTO_exceptionThrown();
+ }
+ //LOGD("+++ fp=%p newFp=%p newSave=%p bottom=%p\n",
+ // fp, newFp, newSaveArea, bottom);
+ }
+
+#ifdef LOG_INSTR
+ if (methodToCall->registersSize > methodToCall->insSize) {
+ /*
+ * This makes valgrind quiet when we print registers that
+ * haven't been initialized. Turn it off when the debug
+ * messages are disabled -- we want valgrind to report any
+ * used-before-initialized issues.
+ */
+ memset(newFp, 0xcc,
+ (methodToCall->registersSize - methodToCall->insSize) * 4);
+ }
+#endif
+
+#ifdef EASY_GDB
+ newSaveArea->prevSave = SAVEAREA_FROM_FP(fp);
+#endif
+ newSaveArea->prevFrame = fp;
+ newSaveArea->savedPc = pc;
+#if defined(WITH_JIT)
+ newSaveArea->returnAddr = 0;
+#endif
+ newSaveArea->method = methodToCall;
+
+ if (!dvmIsNativeMethod(methodToCall)) {
+ /*
+ * "Call" interpreted code. Reposition the PC, update the
+ * frame pointer and other local state, and continue.
+ */
+ curMethod = methodToCall;
+ methodClassDex = curMethod->clazz->pDvmDex;
+ pc = methodToCall->insns;
+ fp = self->curFrame = newFp;
+#ifdef EASY_GDB
+ debugSaveArea = SAVEAREA_FROM_FP(newFp);
+#endif
+#if INTERP_TYPE == INTERP_DBG
+ debugIsMethodEntry = true; // profiling, debugging
+#endif
+ ILOGD("> pc <-- %s.%s %s", curMethod->clazz->descriptor,
+ curMethod->name, curMethod->shorty);
+ DUMP_REGS(curMethod, fp, true); // show input args
+ FINISH(0); // jump to method start
+ } else {
+ /* set this up for JNI locals, even if not a JNI native */
+#ifdef USE_INDIRECT_REF
+ newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
+#else
+ newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.nextEntry;
+#endif
+
+ self->curFrame = newFp;
+
+ DUMP_REGS(methodToCall, newFp, true); // show input args
+
+#if (INTERP_TYPE == INTERP_DBG)
+ if (gDvm.debuggerActive) {
+ dvmDbgPostLocationEvent(methodToCall, -1,
+ dvmGetThisPtr(curMethod, fp), DBG_METHOD_ENTRY);
+ }
+#endif
+#if (INTERP_TYPE == INTERP_DBG)
+ TRACE_METHOD_ENTER(self, methodToCall);
+#endif
+
+ {
+ ILOGD("> native <-- %s.%s %s", methodToCall->clazz->descriptor,
+ methodToCall->name, methodToCall->shorty);
+ }
+
+#if defined(WITH_JIT)
+ /* Allow the Jit to end any pending trace building */
+ CHECK_JIT_VOID();
+#endif
+
+ /*
+ * Jump through native call bridge. Because we leave no
+ * space for locals on native calls, "newFp" points directly
+ * to the method arguments.
+ */
+ (*methodToCall->nativeFunc)(newFp, &retval, methodToCall, self);
+
+#if (INTERP_TYPE == INTERP_DBG)
+ if (gDvm.debuggerActive) {
+ dvmDbgPostLocationEvent(methodToCall, -1,
+ dvmGetThisPtr(curMethod, fp), DBG_METHOD_EXIT);
+ }
+#endif
+#if (INTERP_TYPE == INTERP_DBG)
+ TRACE_METHOD_EXIT(self, methodToCall);
+#endif
+
+ /* pop frame off */
+ dvmPopJniLocals(self, newSaveArea);
+ self->curFrame = fp;
+
+ /*
+ * If the native code threw an exception, or interpreted code
+ * invoked by the native call threw one and nobody has cleared
+ * it, jump to our local exception handling.
+ */
+ if (dvmCheckException(self)) {
+ LOGV("Exception thrown by/below native code\n");
+ GOTO_exceptionThrown();
+ }
+
+ ILOGD("> retval=0x%llx (leaving native)", retval.j);
+ ILOGD("> (return from native %s.%s to %s.%s %s)",
+ methodToCall->clazz->descriptor, methodToCall->name,
+ curMethod->clazz->descriptor, curMethod->name,
+ curMethod->shorty);
+
+ //u2 invokeInstr = INST_INST(FETCH(0));
+ if (true /*invokeInstr >= OP_INVOKE_VIRTUAL &&
+ invokeInstr <= OP_INVOKE_INTERFACE*/)
+ {
+ FINISH(3);
+ } else {
+ //LOGE("Unknown invoke instr %02x at %d\n",
+ // invokeInstr, (int) (pc - curMethod->insns));
+ assert(false);
+ }
+ }
+ }
+ assert(false); // should not get here
+GOTO_TARGET_END
+
+/* File: cstubs/enddefs.c */
+
+/* undefine "magic" name remapping */
+#undef retval
+#undef pc
+#undef fp
+#undef curMethod
+#undef methodClassDex
+#undef self
+#undef debugTrackedRefStart
+
diff --git a/vm/mterp/out/InterpC-x86.c b/vm/mterp/out/InterpC-x86.c
new file mode 100644
index 0000000..4882184
--- /dev/null
+++ b/vm/mterp/out/InterpC-x86.c
@@ -0,0 +1,2263 @@
+/*
+ * This file was generated automatically by gen-mterp.py for 'x86'.
+ *
+ * --> DO NOT EDIT <--
+ */
+
+/* File: c/header.c */
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* common includes */
+#include "Dalvik.h"
+#include "interp/InterpDefs.h"
+#include "mterp/Mterp.h"
+#include <math.h> // needed for fmod, fmodf
+#include "mterp/common/FindInterface.h"
+
+/*
+ * Configuration defines. These affect the C implementations, i.e. the
+ * portable interpreter(s) and C stubs.
+ *
+ * Some defines are controlled by the Makefile, e.g.:
+ * WITH_INSTR_CHECKS
+ * WITH_TRACKREF_CHECKS
+ * EASY_GDB
+ * NDEBUG
+ *
+ * If THREADED_INTERP is not defined, we use a classic "while true / switch"
+ * interpreter. If it is defined, then the tail end of each instruction
+ * handler fetches the next instruction and jumps directly to the handler.
+ * This increases the size of the "Std" interpreter by about 10%, but
+ * provides a speedup of about the same magnitude.
+ *
+ * There's a "hybrid" approach that uses a goto table instead of a switch
+ * statement, avoiding the "is the opcode in range" tests required for switch.
+ * The performance is close to the threaded version, and without the 10%
+ * size increase, but the benchmark results are off enough that it's not
+ * worth adding as a third option.
+ */
+#define THREADED_INTERP /* threaded vs. while-loop interpreter */
+
+#ifdef WITH_INSTR_CHECKS /* instruction-level paranoia (slow!) */
+# define CHECK_BRANCH_OFFSETS
+# define CHECK_REGISTER_INDICES
+#endif
+
+/*
+ * ARM EABI requires 64-bit alignment for access to 64-bit data types. We
+ * can't just use pointers to copy 64-bit values out of our interpreted
+ * register set, because gcc will generate ldrd/strd.
+ *
+ * The __UNION version copies data in and out of a union. The __MEMCPY
+ * version uses a memcpy() call to do the transfer; gcc is smart enough to
+ * not actually call memcpy(). The __UNION version is very bad on ARM;
+ * it only uses one more instruction than __MEMCPY, but for some reason
+ * gcc thinks it needs separate storage for every instance of the union.
+ * On top of that, it feels the need to zero them out at the start of the
+ * method. Net result is we zero out ~700 bytes of stack space at the top
+ * of the interpreter using ARM STM instructions.
+ */
+#if defined(__ARM_EABI__)
+//# define NO_UNALIGN_64__UNION
+# define NO_UNALIGN_64__MEMCPY
+#endif
+
+//#define LOG_INSTR /* verbose debugging */
+/* set and adjust ANDROID_LOG_TAGS='*:i jdwp:i dalvikvm:i dalvikvmi:i' */
+
+/*
+ * Keep a tally of accesses to fields. Currently only works if full DEX
+ * optimization is disabled.
+ */
+#ifdef PROFILE_FIELD_ACCESS
+# define UPDATE_FIELD_GET(_field) { (_field)->gets++; }
+# define UPDATE_FIELD_PUT(_field) { (_field)->puts++; }
+#else
+# define UPDATE_FIELD_GET(_field) ((void)0)
+# define UPDATE_FIELD_PUT(_field) ((void)0)
+#endif
+
+/*
+ * Export another copy of the PC on every instruction; this is largely
+ * redundant with EXPORT_PC and the debugger code. This value can be
+ * compared against what we have stored on the stack with EXPORT_PC to
+ * help ensure that we aren't missing any export calls.
+ */
+#if WITH_EXTRA_GC_CHECKS > 1
+# define EXPORT_EXTRA_PC() (self->currentPc2 = pc)
+#else
+# define EXPORT_EXTRA_PC()
+#endif
+
+/*
+ * Adjust the program counter. "_offset" is a signed int, in 16-bit units.
+ *
+ * Assumes the existence of "const u2* pc" and "const u2* curMethod->insns".
+ *
+ * We don't advance the program counter until we finish an instruction or
+ * branch, because we do want to have to unroll the PC if there's an
+ * exception.
+ */
+#ifdef CHECK_BRANCH_OFFSETS
+# define ADJUST_PC(_offset) do { \
+ int myoff = _offset; /* deref only once */ \
+ if (pc + myoff < curMethod->insns || \
+ pc + myoff >= curMethod->insns + dvmGetMethodInsnsSize(curMethod)) \
+ { \
+ char* desc; \
+ desc = dexProtoCopyMethodDescriptor(&curMethod->prototype); \
+ LOGE("Invalid branch %d at 0x%04x in %s.%s %s\n", \
+ myoff, (int) (pc - curMethod->insns), \
+ curMethod->clazz->descriptor, curMethod->name, desc); \
+ free(desc); \
+ dvmAbort(); \
+ } \
+ pc += myoff; \
+ EXPORT_EXTRA_PC(); \
+ } while (false)
+#else
+# define ADJUST_PC(_offset) do { \
+ pc += _offset; \
+ EXPORT_EXTRA_PC(); \
+ } while (false)
+#endif
+
+/*
+ * If enabled, log instructions as we execute them.
+ */
+#ifdef LOG_INSTR
+# define ILOGD(...) ILOG(LOG_DEBUG, __VA_ARGS__)
+# define ILOGV(...) ILOG(LOG_VERBOSE, __VA_ARGS__)
+# define ILOG(_level, ...) do { \
+ char debugStrBuf[128]; \
+ snprintf(debugStrBuf, sizeof(debugStrBuf), __VA_ARGS__); \
+ if (curMethod != NULL) \
+ LOG(_level, LOG_TAG"i", "%-2d|%04x%s\n", \
+ self->threadId, (int)(pc - curMethod->insns), debugStrBuf); \
+ else \
+ LOG(_level, LOG_TAG"i", "%-2d|####%s\n", \
+ self->threadId, debugStrBuf); \
+ } while(false)
+void dvmDumpRegs(const Method* method, const u4* framePtr, bool inOnly);
+# define DUMP_REGS(_meth, _frame, _inOnly) dvmDumpRegs(_meth, _frame, _inOnly)
+static const char kSpacing[] = " ";
+#else
+# define ILOGD(...) ((void)0)
+# define ILOGV(...) ((void)0)
+# define DUMP_REGS(_meth, _frame, _inOnly) ((void)0)
+#endif
+
+/* get a long from an array of u4 */
+static inline s8 getLongFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { s8 ll; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.parts[0] = ptr[0];
+ conv.parts[1] = ptr[1];
+ return conv.ll;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ s8 val;
+ memcpy(&val, &ptr[idx], 8);
+ return val;
+#else
+ return *((s8*) &ptr[idx]);
+#endif
+}
+
+/* store a long into an array of u4 */
+static inline void putLongToArray(u4* ptr, int idx, s8 val)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { s8 ll; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.ll = val;
+ ptr[0] = conv.parts[0];
+ ptr[1] = conv.parts[1];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ memcpy(&ptr[idx], &val, 8);
+#else
+ *((s8*) &ptr[idx]) = val;
+#endif
+}
+
+/* get a double from an array of u4 */
+static inline double getDoubleFromArray(const u4* ptr, int idx)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { double d; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.parts[0] = ptr[0];
+ conv.parts[1] = ptr[1];
+ return conv.d;
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ double dval;
+ memcpy(&dval, &ptr[idx], 8);
+ return dval;
+#else
+ return *((double*) &ptr[idx]);
+#endif
+}
+
+/* store a double into an array of u4 */
+static inline void putDoubleToArray(u4* ptr, int idx, double dval)
+{
+#if defined(NO_UNALIGN_64__UNION)
+ union { double d; u4 parts[2]; } conv;
+
+ ptr += idx;
+ conv.d = dval;
+ ptr[0] = conv.parts[0];
+ ptr[1] = conv.parts[1];
+#elif defined(NO_UNALIGN_64__MEMCPY)
+ memcpy(&ptr[idx], &dval, 8);
+#else
+ *((double*) &ptr[idx]) = dval;
+#endif
+}
+
+/*
+ * If enabled, validate the register number on every access. Otherwise,
+ * just do an array access.
+ *
+ * Assumes the existence of "u4* fp".
+ *
+ * "_idx" may be referenced more than once.
+ */
+#ifdef CHECK_REGISTER_INDICES
+# define GET_REGISTER(_idx) \
+ ( (_idx) < curMethod->registersSize ? \
+ (fp[(_idx)]) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER(_idx, _val) \
+ ( (_idx) < curMethod->registersSize ? \
+ (fp[(_idx)] = (u4)(_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_AS_OBJECT(_idx) ((Object *)GET_REGISTER(_idx))
+# define SET_REGISTER_AS_OBJECT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_INT(_idx) ((s4) GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ getLongFromArray(fp, (_idx)) : (assert(!"bad reg"),1969) )
+# define SET_REGISTER_WIDE(_idx, _val) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ putLongToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969) )
+# define GET_REGISTER_FLOAT(_idx) \
+ ( (_idx) < curMethod->registersSize ? \
+ (*((float*) &fp[(_idx)])) : (assert(!"bad reg"),1969.0f) )
+# define SET_REGISTER_FLOAT(_idx, _val) \
+ ( (_idx) < curMethod->registersSize ? \
+ (*((float*) &fp[(_idx)]) = (_val)) : (assert(!"bad reg"),1969.0f) )
+# define GET_REGISTER_DOUBLE(_idx) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ getDoubleFromArray(fp, (_idx)) : (assert(!"bad reg"),1969.0) )
+# define SET_REGISTER_DOUBLE(_idx, _val) \
+ ( (_idx) < curMethod->registersSize-1 ? \
+ putDoubleToArray(fp, (_idx), (_val)) : (assert(!"bad reg"),1969.0) )
+#else
+# define GET_REGISTER(_idx) (fp[(_idx)])
+# define SET_REGISTER(_idx, _val) (fp[(_idx)] = (_val))
+# define GET_REGISTER_AS_OBJECT(_idx) ((Object*) fp[(_idx)])
+# define SET_REGISTER_AS_OBJECT(_idx, _val) (fp[(_idx)] = (u4)(_val))
+# define GET_REGISTER_INT(_idx) ((s4)GET_REGISTER(_idx))
+# define SET_REGISTER_INT(_idx, _val) SET_REGISTER(_idx, (s4)_val)
+# define GET_REGISTER_WIDE(_idx) getLongFromArray(fp, (_idx))
+# define SET_REGISTER_WIDE(_idx, _val) putLongToArray(fp, (_idx), (_val))
+# define GET_REGISTER_FLOAT(_idx) (*((float*) &fp[(_idx)]))
+# define SET_REGISTER_FLOAT(_idx, _val) (*((float*) &fp[(_idx)]) = (_val))
+# define GET_REGISTER_DOUBLE(_idx) getDoubleFromArray(fp, (_idx))
+# define SET_REGISTER_DOUBLE(_idx, _val) putDoubleToArray(fp, (_idx), (_val))
+#endif
+
+/*
+ * Get 16 bits from the specified offset of the program counter. We always
+ * want to load 16 bits at a time from the instruction stream -- it's more
+ * efficient than 8 and won't have the alignment problems that 32 might.
+ *
+ * Assumes existence of "const u2* pc".
+ */
+#define FETCH(_offset) (pc[(_offset)])
+
+/*
+ * Extract instruction byte from 16-bit fetch (_inst is a u2).
+ */
+#define INST_INST(_inst) ((_inst) & 0xff)
+
+/*
+ * Replace the opcode (used when handling breakpoints). _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
+ * Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
+ */
+#define INST_A(_inst) (((_inst) >> 8) & 0x0f)
+#define INST_B(_inst) ((_inst) >> 12)
+
+/*
+ * Get the 8-bit "vAA" 8-bit register index from the instruction word.
+ * (_inst is u2)
+ */
+#define INST_AA(_inst) ((_inst) >> 8)
+
+/*
+ * The current PC must be available to Throwable constructors, e.g.
+ * those created by dvmThrowException(), so that the exception stack
+ * trace can be generated correctly. If we don't do this, the offset
+ * within the current method won't be shown correctly. See the notes
+ * in Exception.c.
+ *
+ * This is also used to determine the address for precise GC.
+ *
+ * Assumes existence of "u4* fp" and "const u2* pc".
+ */
+#define EXPORT_PC() (SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc)
+
+/*
+ * Determine if we need to switch to a different interpreter. "_current"
+ * is either INTERP_STD or INTERP_DBG. It should be fixed for a given
+ * interpreter generation file, which should remove the outer conditional
+ * from the following.
+ *
+ * If we're building without debug and profiling support, we never switch.
+ */
+#if defined(WITH_JIT)
+# define NEED_INTERP_SWITCH(_current) ( \
+ (_current == INTERP_STD) ? \
+ dvmJitDebuggerOrProfilerActive() : !dvmJitDebuggerOrProfilerActive() )
+#else
+# define NEED_INTERP_SWITCH(_current) ( \
+ (_current == INTERP_STD) ? \
+ dvmDebuggerOrProfilerActive() : !dvmDebuggerOrProfilerActive() )
+#endif
+
+/*
+ * Check to see if "obj" is NULL. If so, throw an exception. Assumes the
+ * pc has already been exported to the stack.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler calls into
+ * something that could throw an exception (so we have already called
+ * EXPORT_PC at the top).
+ */
+static inline bool checkForNull(Object* obj)
+{
+ if (obj == NULL) {
+ dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+ return false;
+ }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+ if (!dvmIsValidObject(obj)) {
+ LOGE("Invalid object %p\n", obj);
+ dvmAbort();
+ }
+#endif
+#ifndef NDEBUG
+ if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+ /* probable heap corruption */
+ LOGE("Invalid object class %p (in %p)\n", obj->clazz, obj);
+ dvmAbort();
+ }
+#endif
+ return true;
+}
+
+/*
+ * Check to see if "obj" is NULL. If so, export the PC into the stack
+ * frame and throw an exception.
+ *
+ * Perform additional checks on debug builds.
+ *
+ * Use this to check for NULL when the instruction handler doesn't do
+ * anything else that can throw an exception.
+ */
+static inline bool checkForNullExportPC(Object* obj, u4* fp, const u2* pc)
+{
+ if (obj == NULL) {
+ EXPORT_PC();
+ dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+ return false;
+ }
+#ifdef WITH_EXTRA_OBJECT_VALIDATION
+ if (!dvmIsValidObject(obj)) {
+ LOGE("Invalid object %p\n", obj);
+ dvmAbort();
+ }
+#endif
+#ifndef NDEBUG
+ if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) {
+ /* probable heap corruption */
+ LOGE("Invalid object class %p (in %p)\n", obj->clazz, obj);
+ dvmAbort();
+ }
+#endif
+ return true;
+}
+
+/* File: cstubs/stubdefs.c */
+/* this is a standard (no debug support) interpreter */
+#define INTERP_TYPE INTERP_STD
+#define CHECK_DEBUG_AND_PROF() ((void)0)
+# define CHECK_TRACKED_REFS() ((void)0)
+#define CHECK_JIT_BOOL() (false)
+#define CHECK_JIT_VOID()
+#define ABORT_JIT_TSELECT() ((void)0)
+
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...) \
+ void dvmMterp_##_target(MterpGlue* glue, ## __VA_ARGS__);
+
+#define GOTO_TARGET(_target, ...) \
+ void dvmMterp_##_target(MterpGlue* glue, ## __VA_ARGS__) { \
+ u2 ref, vsrc1, vsrc2, vdst; \
+ u2 inst = FETCH(0); \
+ const Method* methodToCall; \
+ StackSaveArea* debugSaveArea;
+
+#define GOTO_TARGET_END }
+
+/*
+ * Redefine what used to be local variable accesses into MterpGlue struct
+ * references. (These are undefined down in "footer.c".)
+ */
+#define retval glue->retval
+#define pc glue->pc
+#define fp glue->fp
+#define curMethod glue->method
+#define methodClassDex glue->methodClassDex
+#define self glue->self
+#define debugTrackedRefStart glue->debugTrackedRefStart
+
+/* ugh */
+#define STUB_HACK(x) x
+
+
+/*
+ * Opcode handler framing macros. Here, each opcode is a separate function
+ * that takes a "glue" argument and returns void. We can't declare
+ * these "static" because they may be called from an assembly stub.
+ */
+#define HANDLE_OPCODE(_op) \
+ void dvmMterp_##_op(MterpGlue* glue) { \
+ u2 ref, vsrc1, vsrc2, vdst; \
+ u2 inst = FETCH(0);
+
+#define OP_END }
+
+/*
+ * Like the "portable" FINISH, but don't reload "inst", and return to caller
+ * when done.
+ */
+#define FINISH(_offset) { \
+ ADJUST_PC(_offset); \
+ CHECK_DEBUG_AND_PROF(); \
+ CHECK_TRACKED_REFS(); \
+ return; \
+ }
+
+
+/*
+ * The "goto label" statements turn into function calls followed by
+ * return statements. Some of the functions take arguments, which in the
+ * portable interpreter are handled by assigning values to globals.
+ */
+
+#define GOTO_exceptionThrown() \
+ do { \
+ dvmMterp_exceptionThrown(glue); \
+ return; \
+ } while(false)
+
+#define GOTO_returnFromMethod() \
+ do { \
+ dvmMterp_returnFromMethod(glue); \
+ return; \
+ } while(false)
+
+#define GOTO_invoke(_target, _methodCallRange) \
+ do { \
+ dvmMterp_##_target(glue, _methodCallRange); \
+ return; \
+ } while(false)
+
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst) \
+ do { \
+ dvmMterp_invokeMethod(glue, _methodCallRange, _methodToCall, \
+ _vsrc1, _vdst); \
+ return; \
+ } while(false)
+
+/*
+ * As a special case, "goto bail" turns into a longjmp. Use "bail_switch"
+ * if we need to switch to the other interpreter upon our return.
+ */
+#define GOTO_bail() \
+ dvmMterpStdBail(glue, false);
+#define GOTO_bail_switch() \
+ dvmMterpStdBail(glue, true);
+
+/*
+ * Periodically check for thread suspension.
+ *
+ * While we're at it, see if a debugger has attached or the profiler has
+ * started. If so, switch to a different "goto" table.
+ */
+#define PERIODIC_CHECKS(_entryPoint, _pcadj) { \
+ if (dvmCheckSuspendQuick(self)) { \
+ EXPORT_PC(); /* need for precise GC */ \
+ dvmCheckSuspendPending(self); \
+ } \
+ if (NEED_INTERP_SWITCH(INTERP_TYPE)) { \
+ ADJUST_PC(_pcadj); \
+ glue->entryPoint = _entryPoint; \
+ LOGVV("threadid=%d: switch to STD ep=%d adj=%d\n", \
+ self->threadId, (_entryPoint), (_pcadj)); \
+ GOTO_bail_switch(); \
+ } \
+ }
+
+/* File: c/opcommon.c */
+/* forward declarations of goto targets */
+GOTO_TARGET_DECL(filledNewArray, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtual, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuper, bool methodCallRange);
+GOTO_TARGET_DECL(invokeInterface, bool methodCallRange);
+GOTO_TARGET_DECL(invokeDirect, bool methodCallRange);
+GOTO_TARGET_DECL(invokeStatic, bool methodCallRange);
+GOTO_TARGET_DECL(invokeVirtualQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeSuperQuick, bool methodCallRange);
+GOTO_TARGET_DECL(invokeMethod, bool methodCallRange, const Method* methodToCall,
+ u2 count, u2 regs);
+GOTO_TARGET_DECL(returnFromMethod);
+GOTO_TARGET_DECL(exceptionThrown);
+
+/*
+ * ===========================================================================
+ *
+ * What follows are opcode definitions shared between multiple opcodes with
+ * minor substitutions handled by the C pre-processor. These should probably
+ * use the mterp substitution mechanism instead, with the code here moved
+ * into common fragment files (like the asm "binop.S"), although it's hard
+ * to give up the C preprocessor in favor of the much simpler text subst.
+ *
+ * ===========================================================================
+ */
+
+#define HANDLE_NUMCONV(_opcode, _opname, _fromtype, _totype) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER##_totype(vdst, \
+ GET_REGISTER##_fromtype(vsrc1)); \
+ FINISH(1);
+
+#define HANDLE_FLOAT_TO_INT(_opcode, _opname, _fromvtype, _fromrtype, \
+ _tovtype, _tortype) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ { \
+ /* spec defines specific handling for +/- inf and NaN values */ \
+ _fromvtype val; \
+ _tovtype intMin, intMax, result; \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1); \
+ val = GET_REGISTER##_fromrtype(vsrc1); \
+ intMin = (_tovtype) 1 << (sizeof(_tovtype) * 8 -1); \
+ intMax = ~intMin; \
+ result = (_tovtype) val; \
+ if (val >= intMax) /* +inf */ \
+ result = intMax; \
+ else if (val <= intMin) /* -inf */ \
+ result = intMin; \
+ else if (val != val) /* NaN */ \
+ result = 0; \
+ else \
+ result = (_tovtype) val; \
+ SET_REGISTER##_tortype(vdst, result); \
+ } \
+ FINISH(1);
+
+#define HANDLE_INT_TO_SMALL(_opcode, _opname, _type) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|int-to-%s v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER(vdst, (_type) GET_REGISTER(vsrc1)); \
+ FINISH(1);
+
+/* NOTE: the comparison result is always a signed 4-byte integer */
+#define HANDLE_OP_CMPX(_opcode, _opname, _varType, _type, _nanVal) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ int result; \
+ u2 regs; \
+ _varType val1, val2; \
+ vdst = INST_AA(inst); \
+ regs = FETCH(1); \
+ vsrc1 = regs & 0xff; \
+ vsrc2 = regs >> 8; \
+ ILOGV("|cmp%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ val1 = GET_REGISTER##_type(vsrc1); \
+ val2 = GET_REGISTER##_type(vsrc2); \
+ if (val1 == val2) \
+ result = 0; \
+ else if (val1 < val2) \
+ result = -1; \
+ else if (val1 > val2) \
+ result = 1; \
+ else \
+ result = (_nanVal); \
+ ILOGV("+ result=%d\n", result); \
+ SET_REGISTER(vdst, result); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_IF_XX(_opcode, _opname, _cmp) \
+ HANDLE_OPCODE(_opcode /*vA, vB, +CCCC*/) \
+ vsrc1 = INST_A(inst); \
+ vsrc2 = INST_B(inst); \
+ if ((s4) GET_REGISTER(vsrc1) _cmp (s4) GET_REGISTER(vsrc2)) { \
+ int branchOffset = (s2)FETCH(1); /* sign-extended */ \
+ ILOGV("|if-%s v%d,v%d,+0x%04x", (_opname), vsrc1, vsrc2, \
+ branchOffset); \
+ ILOGV("> branch taken"); \
+ if (branchOffset < 0) \
+ PERIODIC_CHECKS(kInterpEntryInstr, branchOffset); \
+ FINISH(branchOffset); \
+ } else { \
+ ILOGV("|if-%s v%d,v%d,-", (_opname), vsrc1, vsrc2); \
+ FINISH(2); \
+ }
+
+#define HANDLE_OP_IF_XXZ(_opcode, _opname, _cmp) \
+ HANDLE_OPCODE(_opcode /*vAA, +BBBB*/) \
+ vsrc1 = INST_AA(inst); \
+ if ((s4) GET_REGISTER(vsrc1) _cmp 0) { \
+ int branchOffset = (s2)FETCH(1); /* sign-extended */ \
+ ILOGV("|if-%s v%d,+0x%04x", (_opname), vsrc1, branchOffset); \
+ ILOGV("> branch taken"); \
+ if (branchOffset < 0) \
+ PERIODIC_CHECKS(kInterpEntryInstr, branchOffset); \
+ FINISH(branchOffset); \
+ } else { \
+ ILOGV("|if-%s v%d,-", (_opname), vsrc1); \
+ FINISH(2); \
+ }
+
+#define HANDLE_UNOP(_opcode, _opname, _pfx, _sfx, _type) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER##_type(vdst, _pfx GET_REGISTER##_type(vsrc1) _sfx); \
+ FINISH(1);
+
+#define HANDLE_OP_X_INT(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER(vsrc1); \
+ secondVal = GET_REGISTER(vsrc2); \
+ if (secondVal == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && secondVal == -1) { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ /* non-div/rem case */ \
+ SET_REGISTER(vdst, \
+ (s4) GET_REGISTER(vsrc1) _op (s4) GET_REGISTER(vsrc2)); \
+ } \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_SHX_INT(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-int v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER(vdst, \
+ _cast GET_REGISTER(vsrc1) _op (GET_REGISTER(vsrc2) & 0x1f)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT16(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vA, vB, #+CCCC*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ vsrc2 = FETCH(1); \
+ ILOGV("|%s-int/lit16 v%d,v%d,#+0x%04x", \
+ (_opname), vdst, vsrc1, vsrc2); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, result; \
+ firstVal = GET_REGISTER(vsrc1); \
+ if ((s2) vsrc2 == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && ((s2) vsrc2) == -1) { \
+ /* won't generate /lit16 instr for this; check anyway */ \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op (s2) vsrc2; \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ /* non-div/rem case */ \
+ SET_REGISTER(vdst, GET_REGISTER(vsrc1) _op (s2) vsrc2); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_INT_LIT8(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/) \
+ { \
+ u2 litInfo; \
+ vdst = INST_AA(inst); \
+ litInfo = FETCH(1); \
+ vsrc1 = litInfo & 0xff; \
+ vsrc2 = litInfo >> 8; /* constant */ \
+ ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x", \
+ (_opname), vdst, vsrc1, vsrc2); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, result; \
+ firstVal = GET_REGISTER(vsrc1); \
+ if ((s1) vsrc2 == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && ((s1) vsrc2) == -1) { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op ((s1) vsrc2); \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ SET_REGISTER(vdst, \
+ (s4) GET_REGISTER(vsrc1) _op (s1) vsrc2); \
+ } \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_SHX_INT_LIT8(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, #+CC*/) \
+ { \
+ u2 litInfo; \
+ vdst = INST_AA(inst); \
+ litInfo = FETCH(1); \
+ vsrc1 = litInfo & 0xff; \
+ vsrc2 = litInfo >> 8; /* constant */ \
+ ILOGV("|%s-int/lit8 v%d,v%d,#+0x%02x", \
+ (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER(vdst, \
+ _cast GET_REGISTER(vsrc1) _op (vsrc2 & 0x1f)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_INT_2ADDR(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ if (_chkdiv != 0) { \
+ s4 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER(vdst); \
+ secondVal = GET_REGISTER(vsrc1); \
+ if (secondVal == 0) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u4)firstVal == 0x80000000 && secondVal == -1) { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER(vdst, result); \
+ } else { \
+ SET_REGISTER(vdst, \
+ (s4) GET_REGISTER(vdst) _op (s4) GET_REGISTER(vsrc1)); \
+ } \
+ FINISH(1);
+
+#define HANDLE_OP_SHX_INT_2ADDR(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-int-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER(vdst, \
+ _cast GET_REGISTER(vdst) _op (GET_REGISTER(vsrc1) & 0x1f)); \
+ FINISH(1);
+
+#define HANDLE_OP_X_LONG(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ if (_chkdiv != 0) { \
+ s8 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER_WIDE(vsrc1); \
+ secondVal = GET_REGISTER_WIDE(vsrc2); \
+ if (secondVal == 0LL) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u8)firstVal == 0x8000000000000000ULL && \
+ secondVal == -1LL) \
+ { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER_WIDE(vdst, result); \
+ } else { \
+ SET_REGISTER_WIDE(vdst, \
+ (s8) GET_REGISTER_WIDE(vsrc1) _op (s8) GET_REGISTER_WIDE(vsrc2)); \
+ } \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_SHX_LONG(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-long v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER_WIDE(vdst, \
+ _cast GET_REGISTER_WIDE(vsrc1) _op (GET_REGISTER(vsrc2) & 0x3f)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_LONG_2ADDR(_opcode, _opname, _op, _chkdiv) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ if (_chkdiv != 0) { \
+ s8 firstVal, secondVal, result; \
+ firstVal = GET_REGISTER_WIDE(vdst); \
+ secondVal = GET_REGISTER_WIDE(vsrc1); \
+ if (secondVal == 0LL) { \
+ EXPORT_PC(); \
+ dvmThrowException("Ljava/lang/ArithmeticException;", \
+ "divide by zero"); \
+ GOTO_exceptionThrown(); \
+ } \
+ if ((u8)firstVal == 0x8000000000000000ULL && \
+ secondVal == -1LL) \
+ { \
+ if (_chkdiv == 1) \
+ result = firstVal; /* division */ \
+ else \
+ result = 0; /* remainder */ \
+ } else { \
+ result = firstVal _op secondVal; \
+ } \
+ SET_REGISTER_WIDE(vdst, result); \
+ } else { \
+ SET_REGISTER_WIDE(vdst, \
+ (s8) GET_REGISTER_WIDE(vdst) _op (s8)GET_REGISTER_WIDE(vsrc1));\
+ } \
+ FINISH(1);
+
+#define HANDLE_OP_SHX_LONG_2ADDR(_opcode, _opname, _cast, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-long-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER_WIDE(vdst, \
+ _cast GET_REGISTER_WIDE(vdst) _op (GET_REGISTER(vsrc1) & 0x3f)); \
+ FINISH(1);
+
+#define HANDLE_OP_X_FLOAT(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-float v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER_FLOAT(vdst, \
+ GET_REGISTER_FLOAT(vsrc1) _op GET_REGISTER_FLOAT(vsrc2)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_DOUBLE(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ u2 srcRegs; \
+ vdst = INST_AA(inst); \
+ srcRegs = FETCH(1); \
+ vsrc1 = srcRegs & 0xff; \
+ vsrc2 = srcRegs >> 8; \
+ ILOGV("|%s-double v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ SET_REGISTER_DOUBLE(vdst, \
+ GET_REGISTER_DOUBLE(vsrc1) _op GET_REGISTER_DOUBLE(vsrc2)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_X_FLOAT_2ADDR(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-float-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER_FLOAT(vdst, \
+ GET_REGISTER_FLOAT(vdst) _op GET_REGISTER_FLOAT(vsrc1)); \
+ FINISH(1);
+
+#define HANDLE_OP_X_DOUBLE_2ADDR(_opcode, _opname, _op) \
+ HANDLE_OPCODE(_opcode /*vA, vB*/) \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); \
+ ILOGV("|%s-double-2addr v%d,v%d", (_opname), vdst, vsrc1); \
+ SET_REGISTER_DOUBLE(vdst, \
+ GET_REGISTER_DOUBLE(vdst) _op GET_REGISTER_DOUBLE(vsrc1)); \
+ FINISH(1);
+
+#define HANDLE_OP_AGET(_opcode, _opname, _type, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ ArrayObject* arrayObj; \
+ u2 arrayInfo; \
+ EXPORT_PC(); \
+ vdst = INST_AA(inst); \
+ arrayInfo = FETCH(1); \
+ vsrc1 = arrayInfo & 0xff; /* array ptr */ \
+ vsrc2 = arrayInfo >> 8; /* index */ \
+ ILOGV("|aget%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ arrayObj = (ArrayObject*) GET_REGISTER(vsrc1); \
+ if (!checkForNull((Object*) arrayObj)) \
+ GOTO_exceptionThrown(); \
+ if (GET_REGISTER(vsrc2) >= arrayObj->length) { \
+ LOGV("Invalid array access: %p %d (len=%d)\n", \
+ arrayObj, vsrc2, arrayObj->length); \
+ dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+ NULL); \
+ GOTO_exceptionThrown(); \
+ } \
+ SET_REGISTER##_regsize(vdst, \
+ ((_type*) arrayObj->contents)[GET_REGISTER(vsrc2)]); \
+ ILOGV("+ AGET[%d]=0x%x", GET_REGISTER(vsrc2), GET_REGISTER(vdst)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_OP_APUT(_opcode, _opname, _type, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, vBB, vCC*/) \
+ { \
+ ArrayObject* arrayObj; \
+ u2 arrayInfo; \
+ EXPORT_PC(); \
+ vdst = INST_AA(inst); /* AA: source value */ \
+ arrayInfo = FETCH(1); \
+ vsrc1 = arrayInfo & 0xff; /* BB: array ptr */ \
+ vsrc2 = arrayInfo >> 8; /* CC: index */ \
+ ILOGV("|aput%s v%d,v%d,v%d", (_opname), vdst, vsrc1, vsrc2); \
+ arrayObj = (ArrayObject*) GET_REGISTER(vsrc1); \
+ if (!checkForNull((Object*) arrayObj)) \
+ GOTO_exceptionThrown(); \
+ if (GET_REGISTER(vsrc2) >= arrayObj->length) { \
+ dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
+ NULL); \
+ GOTO_exceptionThrown(); \
+ } \
+ ILOGV("+ APUT[%d]=0x%08x", GET_REGISTER(vsrc2), GET_REGISTER(vdst));\
+ ((_type*) arrayObj->contents)[GET_REGISTER(vsrc2)] = \
+ GET_REGISTER##_regsize(vdst); \
+ } \
+ FINISH(2);
+
+/*
+ * It's possible to get a bad value out of a field with sub-32-bit stores
+ * because the -quick versions always operate on 32 bits. Consider:
+ * short foo = -1 (sets a 32-bit register to 0xffffffff)
+ * iput-quick foo (writes all 32 bits to the field)
+ * short bar = 1 (sets a 32-bit register to 0x00000001)
+ * iput-short (writes the low 16 bits to the field)
+ * iget-quick foo (reads all 32 bits from the field, yielding 0xffff0001)
+ * This can only happen when optimized and non-optimized code has interleaved
+ * access to the same field. This is unlikely but possible.
+ *
+ * The easiest way to fix this is to always read/write 32 bits at a time. On
+ * a device with a 16-bit data bus this is sub-optimal. (The alternative
+ * approach is to have sub-int versions of iget-quick, but now we're wasting
+ * Dalvik instruction space and making it less likely that handler code will
+ * already be in the CPU i-cache.)
+ */
+#define HANDLE_IGET_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ InstField* ifield; \
+ Object* obj; \
+ EXPORT_PC(); \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|iget%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNull(obj)) \
+ GOTO_exceptionThrown(); \
+ ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref); \
+ if (ifield == NULL) { \
+ ifield = dvmResolveInstField(curMethod->clazz, ref); \
+ if (ifield == NULL) \
+ GOTO_exceptionThrown(); \
+ } \
+ SET_REGISTER##_regsize(vdst, \
+ dvmGetField##_ftype(obj, ifield->byteOffset)); \
+ ILOGV("+ IGET '%s'=0x%08llx", ifield->field.name, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_GET(&ifield->field); \
+ } \
+ FINISH(2);
+
+#define HANDLE_IGET_X_QUICK(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ Object* obj; \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field offset */ \
+ ILOGV("|iget%s-quick v%d,v%d,field@+%u", \
+ (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNullExportPC(obj, fp, pc)) \
+ GOTO_exceptionThrown(); \
+ SET_REGISTER##_regsize(vdst, dvmGetField##_ftype(obj, ref)); \
+ ILOGV("+ IGETQ %d=0x%08llx", ref, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ } \
+ FINISH(2);
+
+#define HANDLE_IPUT_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ InstField* ifield; \
+ Object* obj; \
+ EXPORT_PC(); \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|iput%s v%d,v%d,field@0x%04x", (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNull(obj)) \
+ GOTO_exceptionThrown(); \
+ ifield = (InstField*) dvmDexGetResolvedField(methodClassDex, ref); \
+ if (ifield == NULL) { \
+ ifield = dvmResolveInstField(curMethod->clazz, ref); \
+ if (ifield == NULL) \
+ GOTO_exceptionThrown(); \
+ } \
+ dvmSetField##_ftype(obj, ifield->byteOffset, \
+ GET_REGISTER##_regsize(vdst)); \
+ ILOGV("+ IPUT '%s'=0x%08llx", ifield->field.name, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_PUT(&ifield->field); \
+ } \
+ FINISH(2);
+
+#define HANDLE_IPUT_X_QUICK(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vA, vB, field@CCCC*/) \
+ { \
+ Object* obj; \
+ vdst = INST_A(inst); \
+ vsrc1 = INST_B(inst); /* object ptr */ \
+ ref = FETCH(1); /* field offset */ \
+ ILOGV("|iput%s-quick v%d,v%d,field@0x%04x", \
+ (_opname), vdst, vsrc1, ref); \
+ obj = (Object*) GET_REGISTER(vsrc1); \
+ if (!checkForNullExportPC(obj, fp, pc)) \
+ GOTO_exceptionThrown(); \
+ dvmSetField##_ftype(obj, ref, GET_REGISTER##_regsize(vdst)); \
+ ILOGV("+ IPUTQ %d=0x%08llx", ref, \
+ (u8) GET_REGISTER##_regsize(vdst)); \
+ } \
+ FINISH(2);
+
+/*
+ * The JIT needs dvmDexGetResolvedField() to return non-null.
+ * Since we use the portable interpreter to build the trace, the extra
+ * checks in HANDLE_SGET_X and HANDLE_SPUT_X are not needed for mterp.
+ */
+#define HANDLE_SGET_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/) \
+ { \
+ StaticField* sfield; \
+ vdst = INST_AA(inst); \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|sget%s v%d,sfield@0x%04x", (_opname), vdst, ref); \
+ sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+ if (sfield == NULL) { \
+ EXPORT_PC(); \
+ sfield = dvmResolveStaticField(curMethod->clazz, ref); \
+ if (sfield == NULL) \
+ GOTO_exceptionThrown(); \
+ if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) { \
+ ABORT_JIT_TSELECT(); \
+ } \
+ } \
+ SET_REGISTER##_regsize(vdst, dvmGetStaticField##_ftype(sfield)); \
+ ILOGV("+ SGET '%s'=0x%08llx", \
+ sfield->field.name, (u8)GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_GET(&sfield->field); \
+ } \
+ FINISH(2);
+
+#define HANDLE_SPUT_X(_opcode, _opname, _ftype, _regsize) \
+ HANDLE_OPCODE(_opcode /*vAA, field@BBBB*/) \
+ { \
+ StaticField* sfield; \
+ vdst = INST_AA(inst); \
+ ref = FETCH(1); /* field ref */ \
+ ILOGV("|sput%s v%d,sfield@0x%04x", (_opname), vdst, ref); \
+ sfield = (StaticField*)dvmDexGetResolvedField(methodClassDex, ref); \
+ if (sfield == NULL) { \
+ EXPORT_PC(); \
+ sfield = dvmResolveStaticField(curMethod->clazz, ref); \
+ if (sfield == NULL) \
+ GOTO_exceptionThrown(); \
+ if (dvmDexGetResolvedField(methodClassDex, ref) == NULL) { \
+ ABORT_JIT_TSELECT(); \
+ } \
+ } \
+ dvmSetStaticField##_ftype(sfield, GET_REGISTER##_regsize(vdst)); \
+ ILOGV("+ SPUT '%s'=0x%08llx", \
+ sfield->field.name, (u8)GET_REGISTER##_regsize(vdst)); \
+ UPDATE_FIELD_PUT(&sfield->field); \
+ } \
+ FINISH(2);
+
+/* File: c/OP_IGET_WIDE_VOLATILE.c */
+HANDLE_IGET_X(OP_IGET_WIDE_VOLATILE, "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_IPUT_WIDE_VOLATILE.c */
+HANDLE_IPUT_X(OP_IPUT_WIDE_VOLATILE, "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_SGET_WIDE_VOLATILE.c */
+HANDLE_SGET_X(OP_SGET_WIDE_VOLATILE, "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_SPUT_WIDE_VOLATILE.c */
+HANDLE_SPUT_X(OP_SPUT_WIDE_VOLATILE, "-wide-volatile", LongVolatile, _WIDE)
+OP_END
+
+/* File: c/OP_EXECUTE_INLINE_RANGE.c */
+HANDLE_OPCODE(OP_EXECUTE_INLINE_RANGE /*{vCCCC..v(CCCC+AA-1)}, inline@BBBB*/)
+ {
+ u4 arg0, arg1, arg2, arg3;
+ arg0 = arg1 = arg2 = arg3 = 0; /* placate gcc */
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst); /* #of args */
+ ref = FETCH(1); /* inline call "ref" */
+ vdst = FETCH(2); /* range base */
+ ILOGV("|execute-inline-range args=%d @%d {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+
+ assert((vdst >> 16) == 0); // 16-bit type -or- high 16 bits clear
+ assert(vsrc1 <= 4);
+
+ switch (vsrc1) {
+ case 4:
+ arg3 = GET_REGISTER(vdst+3);
+ /* fall through */
+ case 3:
+ arg2 = GET_REGISTER(vdst+2);
+ /* fall through */
+ case 2:
+ arg1 = GET_REGISTER(vdst+1);
+ /* fall through */
+ case 1:
+ arg0 = GET_REGISTER(vdst+0);
+ /* fall through */
+ default: // case 0
+ ;
+ }
+
+#if INTERP_TYPE == INTERP_DBG
+ if (!dvmPerformInlineOp4Dbg(arg0, arg1, arg2, arg3, &retval, ref))
+ GOTO_exceptionThrown();
+#else
+ if (!dvmPerformInlineOp4Std(arg0, arg1, arg2, arg3, &retval, ref))
+ GOTO_exceptionThrown();
+#endif
+ }
+ FINISH(3);
+OP_END
+
+/* File: c/gotoTargets.c */
+/*
+ * C footer. This has some common code shared by the various targets.
+ */
+
+/*
+ * Everything from here on is a "goto target". In the basic interpreter
+ * we jump into these targets and then jump directly to the handler for
+ * next instruction. Here, these are subroutines that return to the caller.
+ */
+
+GOTO_TARGET(filledNewArray, bool methodCallRange)
+ {
+ ClassObject* arrayClass;
+ ArrayObject* newArray;
+ u4* contents;
+ char typeCh;
+ int i;
+ u4 arg5;
+
+ EXPORT_PC();
+
+ ref = FETCH(1); /* class ref */
+ vdst = FETCH(2); /* first 4 regs -or- range base */
+
+ if (methodCallRange) {
+ vsrc1 = INST_AA(inst); /* #of elements */
+ arg5 = -1; /* silence compiler warning */
+ ILOGV("|filled-new-array-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ } else {
+ arg5 = INST_A(inst);
+ vsrc1 = INST_B(inst); /* #of elements */
+ ILOGV("|filled-new-array args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1, ref, vdst, arg5);
+ }
+
+ /*
+ * Resolve the array class.
+ */
+ arrayClass = dvmDexGetResolvedClass(methodClassDex, ref);
+ if (arrayClass == NULL) {
+ arrayClass = dvmResolveClass(curMethod->clazz, ref, false);
+ if (arrayClass == NULL)
+ GOTO_exceptionThrown();
+ }
+ /*
+ if (!dvmIsArrayClass(arrayClass)) {
+ dvmThrowException("Ljava/lang/RuntimeError;",
+ "filled-new-array needs array class");
+ GOTO_exceptionThrown();
+ }
+ */
+ /* verifier guarantees this is an array class */
+ assert(dvmIsArrayClass(arrayClass));
+ assert(dvmIsClassInitialized(arrayClass));
+
+ /*
+ * Create an array of the specified type.
+ */
+ LOGVV("+++ filled-new-array type is '%s'\n", arrayClass->descriptor);
+ typeCh = arrayClass->descriptor[1];
+ if (typeCh == 'D' || typeCh == 'J') {
+ /* category 2 primitives not allowed */
+ dvmThrowException("Ljava/lang/RuntimeError;",
+ "bad filled array req");
+ GOTO_exceptionThrown();
+ } else if (typeCh != 'L' && typeCh != '[' && typeCh != 'I') {
+ /* TODO: requires multiple "fill in" loops with different widths */
+ LOGE("non-int primitives not implemented\n");
+ dvmThrowException("Ljava/lang/InternalError;",
+ "filled-new-array not implemented for anything but 'int'");
+ GOTO_exceptionThrown();
+ }
+
+ newArray = dvmAllocArrayByClass(arrayClass, vsrc1, ALLOC_DONT_TRACK);
+ if (newArray == NULL)
+ GOTO_exceptionThrown();
+
+ /*
+ * Fill in the elements. It's legal for vsrc1 to be zero.
+ */
+ contents = (u4*) newArray->contents;
+ if (methodCallRange) {
+ for (i = 0; i < vsrc1; i++)
+ contents[i] = GET_REGISTER(vdst+i);
+ } else {
+ assert(vsrc1 <= 5);
+ if (vsrc1 == 5) {
+ contents[4] = GET_REGISTER(arg5);
+ vsrc1--;
+ }
+ for (i = 0; i < vsrc1; i++) {
+ contents[i] = GET_REGISTER(vdst & 0x0f);
+ vdst >>= 4;
+ }
+ }
+ if (typeCh == 'L' || typeCh == '[') {
+ dvmWriteBarrierArray(newArray, 0, newArray->length);
+ }
+
+ retval.l = newArray;
+ }
+ FINISH(3);
+GOTO_TARGET_END
+
+
+GOTO_TARGET(invokeVirtual, bool methodCallRange)
+ {
+ Method* baseMethod;
+ Object* thisPtr;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* method ref */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ /*
+ * The object against which we are executing a method is always
+ * in the first argument.
+ */
+ if (methodCallRange) {
+ assert(vsrc1 > 0);
+ ILOGV("|invoke-virtual-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ thisPtr = (Object*) GET_REGISTER(vdst);
+ } else {
+ assert((vsrc1>>4) > 0);
+ ILOGV("|invoke-virtual args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+ thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+ }
+
+ if (!checkForNull(thisPtr))
+ GOTO_exceptionThrown();
+
+ /*
+ * Resolve the method. This is the correct method for the static
+ * type of the object. We also verify access permissions here.
+ */
+ baseMethod = dvmDexGetResolvedMethod(methodClassDex, ref);
+ if (baseMethod == NULL) {
+ baseMethod = dvmResolveMethod(curMethod->clazz, ref,METHOD_VIRTUAL);
+ if (baseMethod == NULL) {
+ ILOGV("+ unknown method or access denied\n");
+ GOTO_exceptionThrown();
+ }
+ }
+
+ /*
+ * Combine the object we found with the vtable offset in the
+ * method.
+ */
+ assert(baseMethod->methodIndex < thisPtr->clazz->vtableCount);
+ methodToCall = thisPtr->clazz->vtable[baseMethod->methodIndex];
+
+#if defined(WITH_JIT) && (INTERP_TYPE == INTERP_DBG)
+ callsiteClass = thisPtr->clazz;
+#endif
+
+#if 0
+ if (dvmIsAbstractMethod(methodToCall)) {
+ /*
+ * This can happen if you create two classes, Base and Sub, where
+ * Sub is a sub-class of Base. Declare a protected abstract
+ * method foo() in Base, and invoke foo() from a method in Base.
+ * Base is an "abstract base class" and is never instantiated
+ * directly. Now, Override foo() in Sub, and use Sub. This
+ * Works fine unless Sub stops providing an implementation of
+ * the method.
+ */
+ dvmThrowException("Ljava/lang/AbstractMethodError;",
+ "abstract method not implemented");
+ GOTO_exceptionThrown();
+ }
+#else
+ assert(!dvmIsAbstractMethod(methodToCall) ||
+ methodToCall->nativeFunc != NULL);
+#endif
+
+ LOGVV("+++ base=%s.%s virtual[%d]=%s.%s\n",
+ baseMethod->clazz->descriptor, baseMethod->name,
+ (u4) baseMethod->methodIndex,
+ methodToCall->clazz->descriptor, methodToCall->name);
+ assert(methodToCall != NULL);
+
+#if 0
+ if (vsrc1 != methodToCall->insSize) {
+ LOGW("WRONG METHOD: base=%s.%s virtual[%d]=%s.%s\n",
+ baseMethod->clazz->descriptor, baseMethod->name,
+ (u4) baseMethod->methodIndex,
+ methodToCall->clazz->descriptor, methodToCall->name);
+ //dvmDumpClass(baseMethod->clazz);
+ //dvmDumpClass(methodToCall->clazz);
+ dvmDumpAllClasses(0);
+ }
+#endif
+
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+ }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeSuper, bool methodCallRange)
+ {
+ Method* baseMethod;
+ u2 thisReg;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* method ref */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ if (methodCallRange) {
+ ILOGV("|invoke-super-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ thisReg = vdst;
+ } else {
+ ILOGV("|invoke-super args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+ thisReg = vdst & 0x0f;
+ }
+ /* impossible in well-formed code, but we must check nevertheless */
+ if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+ GOTO_exceptionThrown();
+
+ /*
+ * Resolve the method. This is the correct method for the static
+ * type of the object. We also verify access permissions here.
+ * The first arg to dvmResolveMethod() is just the referring class
+ * (used for class loaders and such), so we don't want to pass
+ * the superclass into the resolution call.
+ */
+ baseMethod = dvmDexGetResolvedMethod(methodClassDex, ref);
+ if (baseMethod == NULL) {
+ baseMethod = dvmResolveMethod(curMethod->clazz, ref,METHOD_VIRTUAL);
+ if (baseMethod == NULL) {
+ ILOGV("+ unknown method or access denied\n");
+ GOTO_exceptionThrown();
+ }
+ }
+
+ /*
+ * Combine the object we found with the vtable offset in the
+ * method's class.
+ *
+ * We're using the current method's class' superclass, not the
+ * superclass of "this". This is because we might be executing
+ * in a method inherited from a superclass, and we want to run
+ * in that class' superclass.
+ */
+ if (baseMethod->methodIndex >= curMethod->clazz->super->vtableCount) {
+ /*
+ * Method does not exist in the superclass. Could happen if
+ * superclass gets updated.
+ */
+ dvmThrowException("Ljava/lang/NoSuchMethodError;",
+ baseMethod->name);
+ GOTO_exceptionThrown();
+ }
+ methodToCall = curMethod->clazz->super->vtable[baseMethod->methodIndex];
+#if 0
+ if (dvmIsAbstractMethod(methodToCall)) {
+ dvmThrowException("Ljava/lang/AbstractMethodError;",
+ "abstract method not implemented");
+ GOTO_exceptionThrown();
+ }
+#else
+ assert(!dvmIsAbstractMethod(methodToCall) ||
+ methodToCall->nativeFunc != NULL);
+#endif
+ LOGVV("+++ base=%s.%s super-virtual=%s.%s\n",
+ baseMethod->clazz->descriptor, baseMethod->name,
+ methodToCall->clazz->descriptor, methodToCall->name);
+ assert(methodToCall != NULL);
+
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+ }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeInterface, bool methodCallRange)
+ {
+ Object* thisPtr;
+ ClassObject* thisClass;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* method ref */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ /*
+ * The object against which we are executing a method is always
+ * in the first argument.
+ */
+ if (methodCallRange) {
+ assert(vsrc1 > 0);
+ ILOGV("|invoke-interface-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ thisPtr = (Object*) GET_REGISTER(vdst);
+ } else {
+ assert((vsrc1>>4) > 0);
+ ILOGV("|invoke-interface args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+ thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+ }
+ if (!checkForNull(thisPtr))
+ GOTO_exceptionThrown();
+
+ thisClass = thisPtr->clazz;
+
+#if defined(WITH_JIT) && (INTERP_TYPE == INTERP_DBG)
+ callsiteClass = thisClass;
+#endif
+
+ /*
+ * Given a class and a method index, find the Method* with the
+ * actual code we want to execute.
+ */
+ methodToCall = dvmFindInterfaceMethodInCache(thisClass, ref, curMethod,
+ methodClassDex);
+ if (methodToCall == NULL) {
+ assert(dvmCheckException(self));
+ GOTO_exceptionThrown();
+ }
+
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+ }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeDirect, bool methodCallRange)
+ {
+ u2 thisReg;
+
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* method ref */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ EXPORT_PC();
+
+ if (methodCallRange) {
+ ILOGV("|invoke-direct-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ thisReg = vdst;
+ } else {
+ ILOGV("|invoke-direct args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+ thisReg = vdst & 0x0f;
+ }
+ if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+ GOTO_exceptionThrown();
+
+ methodToCall = dvmDexGetResolvedMethod(methodClassDex, ref);
+ if (methodToCall == NULL) {
+ methodToCall = dvmResolveMethod(curMethod->clazz, ref,
+ METHOD_DIRECT);
+ if (methodToCall == NULL) {
+ ILOGV("+ unknown direct method\n"); // should be impossible
+ GOTO_exceptionThrown();
+ }
+ }
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+ }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeStatic, bool methodCallRange)
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* method ref */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ EXPORT_PC();
+
+ if (methodCallRange)
+ ILOGV("|invoke-static-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ else
+ ILOGV("|invoke-static args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+
+ methodToCall = dvmDexGetResolvedMethod(methodClassDex, ref);
+ if (methodToCall == NULL) {
+ methodToCall = dvmResolveMethod(curMethod->clazz, ref, METHOD_STATIC);
+ if (methodToCall == NULL) {
+ ILOGV("+ unknown method\n");
+ GOTO_exceptionThrown();
+ }
+
+ /*
+ * The JIT needs dvmDexGetResolvedMethod() to return non-null.
+ * Since we use the portable interpreter to build the trace, this extra
+ * check is not needed for mterp.
+ */
+ if (dvmDexGetResolvedMethod(methodClassDex, ref) == NULL) {
+ /* Class initialization is still ongoing */
+ ABORT_JIT_TSELECT();
+ }
+ }
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeVirtualQuick, bool methodCallRange)
+ {
+ Object* thisPtr;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* vtable index */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ /*
+ * The object against which we are executing a method is always
+ * in the first argument.
+ */
+ if (methodCallRange) {
+ assert(vsrc1 > 0);
+ ILOGV("|invoke-virtual-quick-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ thisPtr = (Object*) GET_REGISTER(vdst);
+ } else {
+ assert((vsrc1>>4) > 0);
+ ILOGV("|invoke-virtual-quick args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+ thisPtr = (Object*) GET_REGISTER(vdst & 0x0f);
+ }
+
+ if (!checkForNull(thisPtr))
+ GOTO_exceptionThrown();
+
+#if defined(WITH_JIT) && (INTERP_TYPE == INTERP_DBG)
+ callsiteClass = thisPtr->clazz;
+#endif
+
+ /*
+ * Combine the object we found with the vtable offset in the
+ * method.
+ */
+ assert(ref < thisPtr->clazz->vtableCount);
+ methodToCall = thisPtr->clazz->vtable[ref];
+
+#if 0
+ if (dvmIsAbstractMethod(methodToCall)) {
+ dvmThrowException("Ljava/lang/AbstractMethodError;",
+ "abstract method not implemented");
+ GOTO_exceptionThrown();
+ }
+#else
+ assert(!dvmIsAbstractMethod(methodToCall) ||
+ methodToCall->nativeFunc != NULL);
+#endif
+
+ LOGVV("+++ virtual[%d]=%s.%s\n",
+ ref, methodToCall->clazz->descriptor, methodToCall->name);
+ assert(methodToCall != NULL);
+
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+ }
+GOTO_TARGET_END
+
+GOTO_TARGET(invokeSuperQuick, bool methodCallRange)
+ {
+ u2 thisReg;
+
+ EXPORT_PC();
+
+ vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */
+ ref = FETCH(1); /* vtable index */
+ vdst = FETCH(2); /* 4 regs -or- first reg */
+
+ if (methodCallRange) {
+ ILOGV("|invoke-super-quick-range args=%d @0x%04x {regs=v%d-v%d}",
+ vsrc1, ref, vdst, vdst+vsrc1-1);
+ thisReg = vdst;
+ } else {
+ ILOGV("|invoke-super-quick args=%d @0x%04x {regs=0x%04x %x}",
+ vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);
+ thisReg = vdst & 0x0f;
+ }
+ /* impossible in well-formed code, but we must check nevertheless */
+ if (!checkForNull((Object*) GET_REGISTER(thisReg)))
+ GOTO_exceptionThrown();
+
+#if 0 /* impossible in optimized + verified code */
+ if (ref >= curMethod->clazz->super->vtableCount) {
+ dvmThrowException("Ljava/lang/NoSuchMethodError;", NULL);
+ GOTO_exceptionThrown();
+ }
+#else
+ assert(ref < curMethod->clazz->super->vtableCount);
+#endif
+
+ /*
+ * Combine the object we found with the vtable offset in the
+ * method's class.
+ *
+ * We're using the current method's class' superclass, not the
+ * superclass of "this". This is because we might be executing
+ * in a method inherited from a superclass, and we want to run
+ * in the method's class' superclass.
+ */
+ methodToCall = curMethod->clazz->super->vtable[ref];
+
+#if 0
+ if (dvmIsAbstractMethod(methodToCall)) {
+ dvmThrowException("Ljava/lang/AbstractMethodError;",
+ "abstract method not implemented");
+ GOTO_exceptionThrown();
+ }
+#else
+ assert(!dvmIsAbstractMethod(methodToCall) ||
+ methodToCall->nativeFunc != NULL);
+#endif
+ LOGVV("+++ super-virtual[%d]=%s.%s\n",
+ ref, methodToCall->clazz->descriptor, methodToCall->name);
+ assert(methodToCall != NULL);
+
+ GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst);
+ }
+GOTO_TARGET_END
+
+
+ /*
+ * General handling for return-void, return, and return-wide. Put the
+ * return value in "retval" before jumping here.
+ */
+GOTO_TARGET(returnFromMethod)
+ {
+ StackSaveArea* saveArea;
+
+ /*
+ * We must do this BEFORE we pop the previous stack frame off, so
+ * that the GC can see the return value (if any) in the local vars.
+ *
+ * Since this is now an interpreter switch point, we must do it before
+ * we do anything at all.
+ */
+ PERIODIC_CHECKS(kInterpEntryReturn, 0);
+
+ ILOGV("> retval=0x%llx (leaving %s.%s %s)",
+ retval.j, curMethod->clazz->descriptor, curMethod->name,
+ curMethod->shorty);
+ //DUMP_REGS(curMethod, fp);
+
+ saveArea = SAVEAREA_FROM_FP(fp);
+
+#ifdef EASY_GDB
+ debugSaveArea = saveArea;
+#endif
+#if (INTERP_TYPE == INTERP_DBG)
+ TRACE_METHOD_EXIT(self, curMethod);
+#endif
+
+ /* back up to previous frame and see if we hit a break */
+ fp = saveArea->prevFrame;
+ assert(fp != NULL);
+ if (dvmIsBreakFrame(fp)) {
+ /* bail without popping the method frame from stack */
+ LOGVV("+++ returned into break frame\n");
+#if defined(WITH_JIT)
+ /* Let the Jit know the return is terminating normally */
+ CHECK_JIT_VOID();
+#endif
+ GOTO_bail();
+ }
+
+ /* update thread FP, and reset local variables */
+ self->curFrame = fp;
+ curMethod = SAVEAREA_FROM_FP(fp)->method;
+ //methodClass = curMethod->clazz;
+ methodClassDex = curMethod->clazz->pDvmDex;
+ pc = saveArea->savedPc;
+ ILOGD("> (return to %s.%s %s)", curMethod->clazz->descriptor,
+ curMethod->name, curMethod->shorty);
+
+ /* use FINISH on the caller's invoke instruction */
+ //u2 invokeInstr = INST_INST(FETCH(0));
+ if (true /*invokeInstr >= OP_INVOKE_VIRTUAL &&
+ invokeInstr <= OP_INVOKE_INTERFACE*/)
+ {
+ FINISH(3);
+ } else {
+ //LOGE("Unknown invoke instr %02x at %d\n",
+ // invokeInstr, (int) (pc - curMethod->insns));
+ assert(false);
+ }
+ }
+GOTO_TARGET_END
+
+
+ /*
+ * Jump here when the code throws an exception.
+ *
+ * By the time we get here, the Throwable has been created and the stack
+ * trace has been saved off.
+ */
+GOTO_TARGET(exceptionThrown)
+ {
+ Object* exception;
+ int catchRelPc;
+
+ /*
+ * Since this is now an interpreter switch point, we must do it before
+ * we do anything at all.
+ */
+ PERIODIC_CHECKS(kInterpEntryThrow, 0);
+
+#if defined(WITH_JIT)
+ // Something threw during trace selection - abort the current trace
+ ABORT_JIT_TSELECT();
+#endif
+ /*
+ * We save off the exception and clear the exception status. While
+ * processing the exception we might need to load some Throwable
+ * classes, and we don't want class loader exceptions to get
+ * confused with this one.
+ */
+ assert(dvmCheckException(self));
+ exception = dvmGetException(self);
+ dvmAddTrackedAlloc(exception, self);
+ dvmClearException(self);
+
+ LOGV("Handling exception %s at %s:%d\n",
+ exception->clazz->descriptor, curMethod->name,
+ dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+
+#if (INTERP_TYPE == INTERP_DBG)
+ /*
+ * Tell the debugger about it.
+ *
+ * TODO: if the exception was thrown by interpreted code, control
+ * fell through native, and then back to us, we will report the
+ * exception at the point of the throw and again here. We can avoid
+ * this by not reporting exceptions when we jump here directly from
+ * the native call code above, but then we won't report exceptions
+ * that were thrown *from* the JNI code (as opposed to *through* it).
+ *
+ * The correct solution is probably to ignore from-native exceptions
+ * here, and have the JNI exception code do the reporting to the
+ * debugger.
+ */
+ if (gDvm.debuggerActive) {
+ void* catchFrame;
+ catchRelPc = dvmFindCatchBlock(self, pc - curMethod->insns,
+ exception, true, &catchFrame);
+ dvmDbgPostException(fp, pc - curMethod->insns, catchFrame,
+ catchRelPc, exception);
+ }
+#endif
+
+ /*
+ * We need to unroll to the catch block or the nearest "break"
+ * frame.
+ *
+ * A break frame could indicate that we have reached an intermediate
+ * native call, or have gone off the top of the stack and the thread
+ * needs to exit. Either way, we return from here, leaving the
+ * exception raised.
+ *
+ * If we do find a catch block, we want to transfer execution to
+ * that point.
+ *
+ * Note this can cause an exception while resolving classes in
+ * the "catch" blocks.
+ */
+ catchRelPc = dvmFindCatchBlock(self, pc - curMethod->insns,
+ exception, false, (void*)&fp);
+
+ /*
+ * Restore the stack bounds after an overflow. This isn't going to
+ * be correct in all circumstances, e.g. if JNI code devours the
+ * exception this won't happen until some other exception gets
+ * thrown. If the code keeps pushing the stack bounds we'll end
+ * up aborting the VM.
+ *
+ * Note we want to do this *after* the call to dvmFindCatchBlock,
+ * because that may need extra stack space to resolve exception
+ * classes (e.g. through a class loader).
+ *
+ * It's possible for the stack overflow handling to cause an
+ * exception (specifically, class resolution in a "catch" block
+ * during the call above), so we could see the thread's overflow
+ * flag raised but actually be running in a "nested" interpreter
+ * frame. We don't allow doubled-up StackOverflowErrors, so
+ * we can check for this by just looking at the exception type
+ * in the cleanup function. Also, we won't unroll past the SOE
+ * point because the more-recent exception will hit a break frame
+ * as it unrolls to here.
+ */
+ if (self->stackOverflowed)
+ dvmCleanupStackOverflow(self, exception);
+
+ if (catchRelPc < 0) {
+ /* falling through to JNI code or off the bottom of the stack */
+#if DVM_SHOW_EXCEPTION >= 2
+ LOGD("Exception %s from %s:%d not caught locally\n",
+ exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+ dvmLineNumFromPC(curMethod, pc - curMethod->insns));
+#endif
+ dvmSetException(self, exception);
+ dvmReleaseTrackedAlloc(exception, self);
+ GOTO_bail();
+ }
+
+#if DVM_SHOW_EXCEPTION >= 3
+ {
+ const Method* catchMethod = SAVEAREA_FROM_FP(fp)->method;
+ LOGD("Exception %s thrown from %s:%d to %s:%d\n",
+ exception->clazz->descriptor, dvmGetMethodSourceFile(curMethod),
+ dvmLineNumFromPC(curMethod, pc - curMethod->insns),
+ dvmGetMethodSourceFile(catchMethod),
+ dvmLineNumFromPC(catchMethod, catchRelPc));
+ }
+#endif
+
+ /*
+ * Adjust local variables to match self->curFrame and the
+ * updated PC.
+ */
+ //fp = (u4*) self->curFrame;
+ curMethod = SAVEAREA_FROM_FP(fp)->method;
+ //methodClass = curMethod->clazz;
+ methodClassDex = curMethod->clazz->pDvmDex;
+ pc = curMethod->insns + catchRelPc;
+ ILOGV("> pc <-- %s.%s %s", curMethod->clazz->descriptor,
+ curMethod->name, curMethod->shorty);
+ DUMP_REGS(curMethod, fp, false); // show all regs
+
+ /*
+ * Restore the exception if the handler wants it.
+ *
+ * The Dalvik spec mandates that, if an exception handler wants to
+ * do something with the exception, the first instruction executed
+ * must be "move-exception". We can pass the exception along
+ * through the thread struct, and let the move-exception instruction
+ * clear it for us.
+ *
+ * If the handler doesn't call move-exception, we don't want to
+ * finish here with an exception still pending.
+ */
+ if (INST_INST(FETCH(0)) == OP_MOVE_EXCEPTION)
+ dvmSetException(self, exception);
+
+ dvmReleaseTrackedAlloc(exception, self);
+ FINISH(0);
+ }
+GOTO_TARGET_END
+
+
+
+ /*
+ * General handling for invoke-{virtual,super,direct,static,interface},
+ * including "quick" variants.
+ *
+ * Set "methodToCall" to the Method we're calling, and "methodCallRange"
+ * depending on whether this is a "/range" instruction.
+ *
+ * For a range call:
+ * "vsrc1" holds the argument count (8 bits)
+ * "vdst" holds the first argument in the range
+ * For a non-range call:
+ * "vsrc1" holds the argument count (4 bits) and the 5th argument index
+ * "vdst" holds four 4-bit register indices
+ *
+ * The caller must EXPORT_PC before jumping here, because any method
+ * call can throw a stack overflow exception.
+ */
+GOTO_TARGET(invokeMethod, bool methodCallRange, const Method* _methodToCall,
+ u2 count, u2 regs)
+ {
+ STUB_HACK(vsrc1 = count; vdst = regs; methodToCall = _methodToCall;);
+
+ //printf("range=%d call=%p count=%d regs=0x%04x\n",
+ // methodCallRange, methodToCall, count, regs);
+ //printf(" --> %s.%s %s\n", methodToCall->clazz->descriptor,
+ // methodToCall->name, methodToCall->shorty);
+
+ u4* outs;
+ int i;
+
+ /*
+ * Copy args. This may corrupt vsrc1/vdst.
+ */
+ if (methodCallRange) {
+ // could use memcpy or a "Duff's device"; most functions have
+ // so few args it won't matter much
+ assert(vsrc1 <= curMethod->outsSize);
+ assert(vsrc1 == methodToCall->insSize);
+ outs = OUTS_FROM_FP(fp, vsrc1);
+ for (i = 0; i < vsrc1; i++)
+ outs[i] = GET_REGISTER(vdst+i);
+ } else {
+ u4 count = vsrc1 >> 4;
+
+ assert(count <= curMethod->outsSize);
+ assert(count == methodToCall->insSize);
+ assert(count <= 5);
+
+ outs = OUTS_FROM_FP(fp, count);
+#if 0
+ if (count == 5) {
+ outs[4] = GET_REGISTER(vsrc1 & 0x0f);
+ count--;
+ }
+ for (i = 0; i < (int) count; i++) {
+ outs[i] = GET_REGISTER(vdst & 0x0f);
+ vdst >>= 4;
+ }
+#else
+ // This version executes fewer instructions but is larger
+ // overall. Seems to be a teensy bit faster.
+ assert((vdst >> 16) == 0); // 16 bits -or- high 16 bits clear
+ switch (count) {
+ case 5:
+ outs[4] = GET_REGISTER(vsrc1 & 0x0f);
+ case 4:
+ outs[3] = GET_REGISTER(vdst >> 12);
+ case 3:
+ outs[2] = GET_REGISTER((vdst & 0x0f00) >> 8);
+ case 2:
+ outs[1] = GET_REGISTER((vdst & 0x00f0) >> 4);
+ case 1:
+ outs[0] = GET_REGISTER(vdst & 0x0f);
+ default:
+ ;
+ }
+#endif
+ }
+ }
+
+ /*
+ * (This was originally a "goto" target; I've kept it separate from the
+ * stuff above in case we want to refactor things again.)
+ *
+ * At this point, we have the arguments stored in the "outs" area of
+ * the current method's stack frame, and the method to call in
+ * "methodToCall". Push a new stack frame.
+ */
+ {
+ StackSaveArea* newSaveArea;
+ u4* newFp;
+
+ ILOGV("> %s%s.%s %s",
+ dvmIsNativeMethod(methodToCall) ? "(NATIVE) " : "",
+ methodToCall->clazz->descriptor, methodToCall->name,
+ methodToCall->shorty);
+
+ newFp = (u4*) SAVEAREA_FROM_FP(fp) - methodToCall->registersSize;
+ newSaveArea = SAVEAREA_FROM_FP(newFp);
+
+ /* verify that we have enough space */
+ if (true) {
+ u1* bottom;
+ bottom = (u1*) newSaveArea - methodToCall->outsSize * sizeof(u4);
+ if (bottom < self->interpStackEnd) {
+ /* stack overflow */
+ LOGV("Stack overflow on method call (start=%p end=%p newBot=%p(%d) size=%d '%s')\n",
+ self->interpStackStart, self->interpStackEnd, bottom,
+ (u1*) fp - bottom, self->interpStackSize,
+ methodToCall->name);
+ dvmHandleStackOverflow(self, methodToCall);
+ assert(dvmCheckException(self));
+ GOTO_exceptionThrown();
+ }
+ //LOGD("+++ fp=%p newFp=%p newSave=%p bottom=%p\n",
+ // fp, newFp, newSaveArea, bottom);
+ }
+
+#ifdef LOG_INSTR
+ if (methodToCall->registersSize > methodToCall->insSize) {
+ /*
+ * This makes valgrind quiet when we print registers that
+ * haven't been initialized. Turn it off when the debug
+ * messages are disabled -- we want valgrind to report any
+ * used-before-initialized issues.
+ */
+ memset(newFp, 0xcc,
+ (methodToCall->registersSize - methodToCall->insSize) * 4);
+ }
+#endif
+
+#ifdef EASY_GDB
+ newSaveArea->prevSave = SAVEAREA_FROM_FP(fp);
+#endif
+ newSaveArea->prevFrame = fp;
+ newSaveArea->savedPc = pc;
+#if defined(WITH_JIT)
+ newSaveArea->returnAddr = 0;
+#endif
+ newSaveArea->method = methodToCall;
+
+ if (!dvmIsNativeMethod(methodToCall)) {
+ /*
+ * "Call" interpreted code. Reposition the PC, update the
+ * frame pointer and other local state, and continue.
+ */
+ curMethod = methodToCall;
+ methodClassDex = curMethod->clazz->pDvmDex;
+ pc = methodToCall->insns;
+ fp = self->curFrame = newFp;
+#ifdef EASY_GDB
+ debugSaveArea = SAVEAREA_FROM_FP(newFp);
+#endif
+#if INTERP_TYPE == INTERP_DBG
+ debugIsMethodEntry = true; // profiling, debugging
+#endif
+ ILOGD("> pc <-- %s.%s %s", curMethod->clazz->descriptor,
+ curMethod->name, curMethod->shorty);
+ DUMP_REGS(curMethod, fp, true); // show input args
+ FINISH(0); // jump to method start
+ } else {
+ /* set this up for JNI locals, even if not a JNI native */
+#ifdef USE_INDIRECT_REF
+ newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all;
+#else
+ newSaveArea->xtra.localRefCookie = self->jniLocalRefTable.nextEntry;
+#endif
+
+ self->curFrame = newFp;
+
+ DUMP_REGS(methodToCall, newFp, true); // show input args
+
+#if (INTERP_TYPE == INTERP_DBG)
+ if (gDvm.debuggerActive) {
+ dvmDbgPostLocationEvent(methodToCall, -1,
+ dvmGetThisPtr(curMethod, fp), DBG_METHOD_ENTRY);
+ }
+#endif
+#if (INTERP_TYPE == INTERP_DBG)
+ TRACE_METHOD_ENTER(self, methodToCall);
+#endif
+
+ {
+ ILOGD("> native <-- %s.%s %s", methodToCall->clazz->descriptor,
+ methodToCall->name, methodToCall->shorty);
+ }
+
+#if defined(WITH_JIT)
+ /* Allow the Jit to end any pending trace building */
+ CHECK_JIT_VOID();
+#endif
+
+ /*
+ * Jump through native call bridge. Because we leave no
+ * space for locals on native calls, "newFp" points directly
+ * to the method arguments.
+ */
+ (*methodToCall->nativeFunc)(newFp, &retval, methodToCall, self);
+
+#if (INTERP_TYPE == INTERP_DBG)
+ if (gDvm.debuggerActive) {
+ dvmDbgPostLocationEvent(methodToCall, -1,
+ dvmGetThisPtr(curMethod, fp), DBG_METHOD_EXIT);
+ }
+#endif
+#if (INTERP_TYPE == INTERP_DBG)
+ TRACE_METHOD_EXIT(self, methodToCall);
+#endif
+
+ /* pop frame off */
+ dvmPopJniLocals(self, newSaveArea);
+ self->curFrame = fp;
+
+ /*
+ * If the native code threw an exception, or interpreted code
+ * invoked by the native call threw one and nobody has cleared
+ * it, jump to our local exception handling.
+ */
+ if (dvmCheckException(self)) {
+ LOGV("Exception thrown by/below native code\n");
+ GOTO_exceptionThrown();
+ }
+
+ ILOGD("> retval=0x%llx (leaving native)", retval.j);
+ ILOGD("> (return from native %s.%s to %s.%s %s)",
+ methodToCall->clazz->descriptor, methodToCall->name,
+ curMethod->clazz->descriptor, curMethod->name,
+ curMethod->shorty);
+
+ //u2 invokeInstr = INST_INST(FETCH(0));
+ if (true /*invokeInstr >= OP_INVOKE_VIRTUAL &&
+ invokeInstr <= OP_INVOKE_INTERFACE*/)
+ {
+ FINISH(3);
+ } else {
+ //LOGE("Unknown invoke instr %02x at %d\n",
+ // invokeInstr, (int) (pc - curMethod->insns));
+ assert(false);
+ }
+ }
+ }
+ assert(false); // should not get here
+GOTO_TARGET_END
+
+/* File: cstubs/enddefs.c */
+
+/* undefine "magic" name remapping */
+#undef retval
+#undef pc
+#undef fp
+#undef curMethod
+#undef methodClassDex
+#undef self
+#undef debugTrackedRefStart
+
diff --git a/vm/mterp/portable/debug.c b/vm/mterp/portable/debug.c
new file mode 100644
index 0000000..1d06188
--- /dev/null
+++ b/vm/mterp/portable/debug.c
@@ -0,0 +1,239 @@
+/* code in here is only included in portable-debug interpreter */
+
+/*
+ * Update the debugger on interesting events, such as hitting a breakpoint
+ * or a single-step point. This is called from the top of the interpreter
+ * loop, before the current instruction is processed.
+ *
+ * Set "methodEntry" if we've just entered the method. This detects
+ * method exit by checking to see if the next instruction is "return".
+ *
+ * This can't catch native method entry/exit, so we have to handle that
+ * at the point of invocation. We also need to catch it in dvmCallMethod
+ * if we want to capture native->native calls made through JNI.
+ *
+ * Notes to self:
+ * - Don't want to switch to VMWAIT while posting events to the debugger.
+ * Let the debugger code decide if we need to change state.
+ * - We may want to check for debugger-induced thread suspensions on
+ * every instruction. That would make a "suspend all" more responsive
+ * and reduce the chances of multiple simultaneous events occurring.
+ * However, it could change the behavior some.
+ *
+ * TODO: method entry/exit events are probably less common than location
+ * breakpoints. We may be able to speed things up a bit if we don't query
+ * the event list unless we know there's at least one lurking within.
+ */
+static void updateDebugger(const Method* method, const u2* pc, const u4* fp,
+ bool methodEntry, Thread* self)
+{
+ int eventFlags = 0;
+
+ /*
+ * Update xtra.currentPc on every instruction. We need to do this if
+ * there's a chance that we could get suspended. This can happen if
+ * eventFlags != 0 here, or somebody manually requests a suspend
+ * (which gets handled at PERIOD_CHECKS time). One place where this
+ * needs to be correct is in dvmAddSingleStep().
+ */
+ EXPORT_PC();
+
+ if (methodEntry)
+ eventFlags |= DBG_METHOD_ENTRY;
+
+ /*
+ * See if we have a breakpoint here.
+ *
+ * Depending on the "mods" associated with event(s) on this address,
+ * we may or may not actually send a message to the debugger.
+ */
+ if (INST_INST(*pc) == OP_BREAKPOINT) {
+ LOGV("+++ breakpoint hit at %p\n", pc);
+ eventFlags |= DBG_BREAKPOINT;
+ }
+
+ /*
+ * If the debugger is single-stepping one of our threads, check to
+ * see if we're that thread and we've reached a step point.
+ */
+ const StepControl* pCtrl = &gDvm.stepControl;
+ if (pCtrl->active && pCtrl->thread == self) {
+ int frameDepth;
+ bool doStop = false;
+ const char* msg = NULL;
+
+ assert(!dvmIsNativeMethod(method));
+
+ if (pCtrl->depth == SD_INTO) {
+ /*
+ * Step into method calls. We break when the line number
+ * or method pointer changes. If we're in SS_MIN mode, we
+ * always stop.
+ */
+ if (pCtrl->method != method) {
+ doStop = true;
+ msg = "new method";
+ } else if (pCtrl->size == SS_MIN) {
+ doStop = true;
+ msg = "new instruction";
+ } else if (!dvmAddressSetGet(
+ pCtrl->pAddressSet, pc - method->insns)) {
+ doStop = true;
+ msg = "new line";
+ }
+ } else if (pCtrl->depth == SD_OVER) {
+ /*
+ * Step over method calls. We break when the line number is
+ * different and the frame depth is <= the original frame
+ * depth. (We can't just compare on the method, because we
+ * might get unrolled past it by an exception, and it's tricky
+ * to identify recursion.)
+ */
+ frameDepth = dvmComputeVagueFrameDepth(self, fp);
+ if (frameDepth < pCtrl->frameDepth) {
+ /* popped up one or more frames, always trigger */
+ doStop = true;
+ msg = "method pop";
+ } else if (frameDepth == pCtrl->frameDepth) {
+ /* same depth, see if we moved */
+ if (pCtrl->size == SS_MIN) {
+ doStop = true;
+ msg = "new instruction";
+ } else if (!dvmAddressSetGet(pCtrl->pAddressSet,
+ pc - method->insns)) {
+ doStop = true;
+ msg = "new line";
+ }
+ }
+ } else {
+ assert(pCtrl->depth == SD_OUT);
+ /*
+ * Return from the current method. We break when the frame
+ * depth pops up.
+ *
+ * This differs from the "method exit" break in that it stops
+ * with the PC at the next instruction in the returned-to
+ * function, rather than the end of the returning function.
+ */
+ frameDepth = dvmComputeVagueFrameDepth(self, fp);
+ if (frameDepth < pCtrl->frameDepth) {
+ doStop = true;
+ msg = "method pop";
+ }
+ }
+
+ if (doStop) {
+ LOGV("#####S %s\n", msg);
+ eventFlags |= DBG_SINGLE_STEP;
+ }
+ }
+
+ /*
+ * Check to see if this is a "return" instruction. JDWP says we should
+ * send the event *after* the code has been executed, but it also says
+ * the location we provide is the last instruction. Since the "return"
+ * instruction has no interesting side effects, we should be safe.
+ * (We can't just move this down to the returnFromMethod label because
+ * we potentially need to combine it with other events.)
+ *
+ * We're also not supposed to generate a method exit event if the method
+ * terminates "with a thrown exception".
+ */
+ u2 inst = INST_INST(FETCH(0));
+ if (inst == OP_RETURN_VOID || inst == OP_RETURN || inst == OP_RETURN_WIDE ||
+ inst == OP_RETURN_OBJECT)
+ {
+ eventFlags |= DBG_METHOD_EXIT;
+ }
+
+ /*
+ * If there's something interesting going on, see if it matches one
+ * of the debugger filters.
+ */
+ if (eventFlags != 0) {
+ Object* thisPtr = dvmGetThisPtr(method, fp);
+ if (thisPtr != NULL && !dvmIsValidObject(thisPtr)) {
+ /*
+ * TODO: remove this check if we're confident that the "this"
+ * pointer is where it should be -- slows us down, especially
+ * during single-step.
+ */
+ char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+ LOGE("HEY: invalid 'this' ptr %p (%s.%s %s)\n", thisPtr,
+ method->clazz->descriptor, method->name, desc);
+ free(desc);
+ dvmAbort();
+ }
+ dvmDbgPostLocationEvent(method, pc - method->insns, thisPtr,
+ eventFlags);
+ }
+}
+
+/*
+ * Perform some operations at the "top" of the interpreter loop.
+ * This stuff is required to support debugging and profiling.
+ *
+ * Using" __attribute__((noinline))" seems to do more harm than good. This
+ * is best when inlined due to the large number of parameters, most of
+ * which are local vars in the main interp loop.
+ */
+static void checkDebugAndProf(const u2* pc, const u4* fp, Thread* self,
+ const Method* method, bool* pIsMethodEntry)
+{
+ /* check to see if we've run off end of method */
+ assert(pc >= method->insns && pc <
+ method->insns + dvmGetMethodInsnsSize(method));
+
+#if 0
+ /*
+ * When we hit a specific method, enable verbose instruction logging.
+ * Sometimes it's helpful to use the debugger attach as a trigger too.
+ */
+ if (*pIsMethodEntry) {
+ static const char* cd = "Landroid/test/Arithmetic;";
+ static const char* mn = "shiftTest2";
+ static const char* sg = "()V";
+
+ if (/*gDvm.debuggerActive &&*/
+ strcmp(method->clazz->descriptor, cd) == 0 &&
+ strcmp(method->name, mn) == 0 &&
+ strcmp(method->shorty, sg) == 0)
+ {
+ LOGW("Reached %s.%s, enabling verbose mode\n",
+ method->clazz->descriptor, method->name);
+ android_setMinPriority(LOG_TAG"i", ANDROID_LOG_VERBOSE);
+ dumpRegs(method, fp, true);
+ }
+
+ if (!gDvm.debuggerActive)
+ *pIsMethodEntry = false;
+ }
+#endif
+
+ /*
+ * If the debugger is attached, check for events. If the profiler is
+ * enabled, update that too.
+ *
+ * This code is executed for every instruction we interpret, so for
+ * performance we use a couple of #ifdef blocks instead of runtime tests.
+ */
+ bool isEntry = *pIsMethodEntry;
+ if (isEntry) {
+ *pIsMethodEntry = false;
+ TRACE_METHOD_ENTER(self, method);
+ }
+ if (gDvm.debuggerActive) {
+ updateDebugger(method, pc, fp, isEntry, self);
+ }
+ if (gDvm.instructionCountEnableCount != 0) {
+ /*
+ * Count up the #of executed instructions. This isn't synchronized
+ * for thread-safety; if we need that we should make this
+ * thread-local and merge counts into the global area when threads
+ * exit (perhaps suspending all other threads GC-style and pulling
+ * the data out of them).
+ */
+ int inst = *pc & 0xff;
+ gDvm.executedInstrCounts[inst]++;
+ }
+}
diff --git a/vm/mterp/portable/enddefs.c b/vm/mterp/portable/enddefs.c
new file mode 100644
index 0000000..30deedc
--- /dev/null
+++ b/vm/mterp/portable/enddefs.c
@@ -0,0 +1,40 @@
+/*--- end of opcodes ---*/
+
+#ifndef THREADED_INTERP
+ } // end of "switch"
+ } // end of "while"
+#endif
+
+bail:
+ ILOGD("|-- Leaving interpreter loop"); // note "curMethod" may be NULL
+
+ interpState->retval = retval;
+ return false;
+
+bail_switch:
+ /*
+ * The standard interpreter currently doesn't set or care about the
+ * "debugIsMethodEntry" value, so setting this is only of use if we're
+ * switching between two "debug" interpreters, which we never do.
+ *
+ * TODO: figure out if preserving this makes any sense.
+ */
+#if INTERP_TYPE == INTERP_DBG
+ interpState->debugIsMethodEntry = debugIsMethodEntry;
+#else
+ interpState->debugIsMethodEntry = false;
+#endif
+
+ /* export state changes */
+ interpState->method = curMethod;
+ interpState->pc = pc;
+ interpState->fp = fp;
+ /* debugTrackedRefStart doesn't change */
+ interpState->retval = retval; /* need for _entryPoint=ret */
+ interpState->nextMode =
+ (INTERP_TYPE == INTERP_STD) ? INTERP_DBG : INTERP_STD;
+ LOGVV(" meth='%s.%s' pc=0x%x fp=%p\n",
+ curMethod->clazz->descriptor, curMethod->name,
+ pc - curMethod->insns, fp);
+ return true;
+}
diff --git a/vm/mterp/portable/entry.c b/vm/mterp/portable/entry.c
new file mode 100644
index 0000000..56649e7
--- /dev/null
+++ b/vm/mterp/portable/entry.c
@@ -0,0 +1,127 @@
+/*
+ * Main interpreter loop.
+ *
+ * This was written with an ARM implementation in mind.
+ */
+bool INTERP_FUNC_NAME(Thread* self, InterpState* interpState)
+{
+#if defined(EASY_GDB)
+ StackSaveArea* debugSaveArea = SAVEAREA_FROM_FP(self->curFrame);
+#endif
+#if INTERP_TYPE == INTERP_DBG
+ bool debugIsMethodEntry = false;
+ debugIsMethodEntry = interpState->debugIsMethodEntry;
+#endif
+#if defined(WITH_TRACKREF_CHECKS)
+ int debugTrackedRefStart = interpState->debugTrackedRefStart;
+#endif
+ DvmDex* methodClassDex; // curMethod->clazz->pDvmDex
+ JValue retval;
+
+ /* core state */
+ const Method* curMethod; // method we're interpreting
+ const u2* pc; // program counter
+ u4* fp; // frame pointer
+ u2 inst; // current instruction
+ /* instruction decoding */
+ u2 ref; // 16-bit quantity fetched directly
+ u2 vsrc1, vsrc2, vdst; // usually used for register indexes
+ /* method call setup */
+ const Method* methodToCall;
+ bool methodCallRange;
+
+
+#if defined(THREADED_INTERP)
+ /* static computed goto table */
+ DEFINE_GOTO_TABLE(handlerTable);
+#endif
+
+#if defined(WITH_JIT)
+#if 0
+ LOGD("*DebugInterp - entrypoint is %d, tgt is 0x%x, %s\n",
+ interpState->entryPoint,
+ interpState->pc,
+ interpState->method->name);
+#endif
+#if INTERP_TYPE == INTERP_DBG
+ const ClassObject* callsiteClass = NULL;
+
+#if defined(WITH_SELF_VERIFICATION)
+ if (interpState->jitState != kJitSelfVerification) {
+ interpState->self->shadowSpace->jitExitState = kSVSIdle;
+ }
+#endif
+
+ /* Check to see if we've got a trace selection request. */
+ if (
+ /*
+ * Only perform dvmJitCheckTraceRequest if the entry point is
+ * EntryInstr and the jit state is either kJitTSelectRequest or
+ * kJitTSelectRequestHot. If debugger/profiler happens to be attached,
+ * dvmJitCheckTraceRequest will change the jitState to kJitDone but
+ * but stay in the dbg interpreter.
+ */
+ (interpState->entryPoint == kInterpEntryInstr) &&
+ (interpState->jitState == kJitTSelectRequest ||
+ interpState->jitState == kJitTSelectRequestHot) &&
+ dvmJitCheckTraceRequest(self, interpState)) {
+ interpState->nextMode = INTERP_STD;
+ //LOGD("Invalid trace request, exiting\n");
+ return true;
+ }
+#endif /* INTERP_TYPE == INTERP_DBG */
+#endif /* WITH_JIT */
+
+ /* copy state in */
+ curMethod = interpState->method;
+ pc = interpState->pc;
+ fp = interpState->fp;
+ retval = interpState->retval; /* only need for kInterpEntryReturn? */
+
+ methodClassDex = curMethod->clazz->pDvmDex;
+
+ LOGVV("threadid=%d: entry(%s) %s.%s pc=0x%x fp=%p ep=%d\n",
+ self->threadId, (interpState->nextMode == INTERP_STD) ? "STD" : "DBG",
+ curMethod->clazz->descriptor, curMethod->name, pc - curMethod->insns,
+ fp, interpState->entryPoint);
+
+ /*
+ * DEBUG: scramble this to ensure we're not relying on it.
+ */
+ methodToCall = (const Method*) -1;
+
+#if INTERP_TYPE == INTERP_DBG
+ if (debugIsMethodEntry) {
+ ILOGD("|-- Now interpreting %s.%s", curMethod->clazz->descriptor,
+ curMethod->name);
+ DUMP_REGS(curMethod, interpState->fp, false);
+ }
+#endif
+
+ switch (interpState->entryPoint) {
+ case kInterpEntryInstr:
+ /* just fall through to instruction loop or threaded kickstart */
+ break;
+ case kInterpEntryReturn:
+ CHECK_JIT_VOID();
+ goto returnFromMethod;
+ case kInterpEntryThrow:
+ goto exceptionThrown;
+ default:
+ dvmAbort();
+ }
+
+#ifdef THREADED_INTERP
+ FINISH(0); /* fetch and execute first instruction */
+#else
+ while (1) {
+ CHECK_DEBUG_AND_PROF(); /* service debugger and profiling */
+ CHECK_TRACKED_REFS(); /* check local reference tracking */
+
+ /* fetch the next 16 bits from the instruction stream */
+ inst = FETCH(0);
+
+ switch (INST_INST(inst)) {
+#endif
+
+/*--- start of opcodes ---*/
diff --git a/vm/mterp/portable/portdbg.c b/vm/mterp/portable/portdbg.c
new file mode 100644
index 0000000..65349e9
--- /dev/null
+++ b/vm/mterp/portable/portdbg.c
@@ -0,0 +1,17 @@
+#define INTERP_FUNC_NAME dvmInterpretDbg
+#define INTERP_TYPE INTERP_DBG
+
+#define CHECK_DEBUG_AND_PROF() \
+ checkDebugAndProf(pc, fp, self, curMethod, &debugIsMethodEntry)
+
+#if defined(WITH_JIT)
+#define CHECK_JIT_BOOL() (dvmCheckJit(pc, self, interpState, callsiteClass,\
+ methodToCall))
+#define CHECK_JIT_VOID() (dvmCheckJit(pc, self, interpState, callsiteClass,\
+ methodToCall))
+#define ABORT_JIT_TSELECT() (dvmJitAbortTraceSelect(interpState))
+#else
+#define CHECK_JIT_BOOL() (false)
+#define CHECK_JIT_VOID()
+#define ABORT_JIT_TSELECT(x) ((void)0)
+#endif
diff --git a/vm/mterp/portable/portstd.c b/vm/mterp/portable/portstd.c
new file mode 100644
index 0000000..f37c22b
--- /dev/null
+++ b/vm/mterp/portable/portstd.c
@@ -0,0 +1,8 @@
+#define INTERP_FUNC_NAME dvmInterpretStd
+#define INTERP_TYPE INTERP_STD
+
+#define CHECK_DEBUG_AND_PROF() ((void)0)
+
+#define CHECK_JIT_BOOL() (false)
+#define CHECK_JIT_VOID()
+#define ABORT_JIT_TSELECT() ((void)0)
diff --git a/vm/mterp/portable/stubdefs.c b/vm/mterp/portable/stubdefs.c
new file mode 100644
index 0000000..b46bb3a
--- /dev/null
+++ b/vm/mterp/portable/stubdefs.c
@@ -0,0 +1,96 @@
+/*
+ * In the C mterp stubs, "goto" is a function call followed immediately
+ * by a return.
+ */
+
+#define GOTO_TARGET_DECL(_target, ...)
+
+#define GOTO_TARGET(_target, ...) _target:
+
+#define GOTO_TARGET_END
+
+/* ugh */
+#define STUB_HACK(x)
+
+/*
+ * Instruction framing. For a switch-oriented implementation this is
+ * case/break, for a threaded implementation it's a goto label and an
+ * instruction fetch/computed goto.
+ *
+ * Assumes the existence of "const u2* pc" and (for threaded operation)
+ * "u2 inst".
+ *
+ * TODO: remove "switch" version.
+ */
+#ifdef THREADED_INTERP
+# define H(_op) &&op_##_op
+# define HANDLE_OPCODE(_op) op_##_op:
+# define FINISH(_offset) { \
+ ADJUST_PC(_offset); \
+ inst = FETCH(0); \
+ CHECK_DEBUG_AND_PROF(); \
+ CHECK_TRACKED_REFS(); \
+ if (CHECK_JIT_BOOL()) GOTO_bail_switch(); \
+ goto *handlerTable[INST_INST(inst)]; \
+ }
+# define FINISH_BKPT(_opcode) { \
+ goto *handlerTable[_opcode]; \
+ }
+#else
+# define HANDLE_OPCODE(_op) case _op:
+# define FINISH(_offset) { ADJUST_PC(_offset); break; }
+# define FINISH_BKPT(opcode) { > not implemented < }
+#endif
+
+#define OP_END
+
+#if defined(WITH_TRACKREF_CHECKS)
+# define CHECK_TRACKED_REFS() \
+ dvmInterpCheckTrackedRefs(self, curMethod, debugTrackedRefStart)
+#else
+# define CHECK_TRACKED_REFS() ((void)0)
+#endif
+
+
+/*
+ * The "goto" targets just turn into goto statements. The "arguments" are
+ * passed through local variables.
+ */
+
+#define GOTO_exceptionThrown() goto exceptionThrown;
+
+#define GOTO_returnFromMethod() goto returnFromMethod;
+
+#define GOTO_invoke(_target, _methodCallRange) \
+ do { \
+ methodCallRange = _methodCallRange; \
+ goto _target; \
+ } while(false)
+
+/* for this, the "args" are already in the locals */
+#define GOTO_invokeMethod(_methodCallRange, _methodToCall, _vsrc1, _vdst) goto invokeMethod;
+
+#define GOTO_bail() goto bail;
+#define GOTO_bail_switch() goto bail_switch;
+
+/*
+ * Periodically check for thread suspension.
+ *
+ * While we're at it, see if a debugger has attached or the profiler has
+ * started. If so, switch to a different "goto" table.
+ */
+#define PERIODIC_CHECKS(_entryPoint, _pcadj) { \
+ if (dvmCheckSuspendQuick(self)) { \
+ EXPORT_PC(); /* need for precise GC */ \
+ dvmCheckSuspendPending(self); \
+ } \
+ if (NEED_INTERP_SWITCH(INTERP_TYPE)) { \
+ ADJUST_PC(_pcadj); \
+ interpState->entryPoint = _entryPoint; \
+ LOGVV("threadid=%d: switch to %s ep=%d adj=%d\n", \
+ self->threadId, \
+ (interpState->nextMode == INTERP_STD) ? "STD" : "DBG", \
+ (_entryPoint), (_pcadj)); \
+ GOTO_bail_switch(); \
+ } \
+ }
diff --git a/vm/mterp/rebuild.sh b/vm/mterp/rebuild.sh
new file mode 100755
index 0000000..841546f
--- /dev/null
+++ b/vm/mterp/rebuild.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Rebuild for all known targets. Necessary until the stuff in "out" gets
+# generated as part of the build.
+#
+set -e
+
+for arch in portstd portdbg allstubs armv4t armv5te armv5te-vfp armv7-a armv7-a-neon x86 x86-atom; do TARGET_ARCH_EXT=$arch make -f Makefile-mterp; done
+
+# These aren't actually used, so just go ahead and remove them. The correct
+# approach is to prevent them from being generated in the first place, but
+# this will do for now.
+echo Removing unneeded assembly source for portable interpreter
+rm -f out/InterpAsm-portstd.S out/InterpAsm-portdbg.S
diff --git a/vm/mterp/x86-atom/OP_ADD_DOUBLE.S b/vm/mterp/x86-atom/OP_ADD_DOUBLE.S
new file mode 100644
index 0000000..22f3938
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_ADD_DOUBLE.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_ADD_DOUBLE.S
+ */
+
+%include "x86-atom/binopWide.S" {"instr":"addsd %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_ADD_DOUBLE_2ADDR.S b/vm/mterp/x86-atom/OP_ADD_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..0b2bf4f
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_ADD_DOUBLE_2ADDR.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_ADD_DOUBLE_2ADDR.S
+ */
+
+%include "x86-atom/binopWide2addr.S" {"instr":"addsd %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_ADD_FLOAT.S b/vm/mterp/x86-atom/OP_ADD_FLOAT.S
new file mode 100644
index 0000000..aa3aa22
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_ADD_FLOAT.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_ADD_FLOAT.S
+ */
+
+%include "x86-atom/binopF.S" {"instr":"addss %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_ADD_FLOAT_2ADDR.S b/vm/mterp/x86-atom/OP_ADD_FLOAT_2ADDR.S
new file mode 100644
index 0000000..3d62703
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_ADD_FLOAT_2ADDR.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_ADD_FLOAT_2ADDR.S
+ */
+
+%include "x86-atom/binopF2addr.S" {"instr":"addss %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_ADD_INT.S b/vm/mterp/x86-atom/OP_ADD_INT.S
new file mode 100644
index 0000000..a423e75
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_ADD_INT.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_ADD_INT.S
+ */
+
+%include "x86-atom/binop.S" {"instr":"addl %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_ADD_INT_2ADDR.S b/vm/mterp/x86-atom/OP_ADD_INT_2ADDR.S
new file mode 100644
index 0000000..2a91f41
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_ADD_INT_2ADDR.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_ADD_INT_2ADDR.S
+ */
+
+%include "x86-atom/binop2addr.S" {"instr":"addl %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_ADD_INT_LIT16.S b/vm/mterp/x86-atom/OP_ADD_INT_LIT16.S
new file mode 100644
index 0000000..72479ba
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_ADD_INT_LIT16.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_ADD_INT_LIT16.S
+ */
+
+%include "x86-atom/binopLit16.S" {"instr":"addl %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_ADD_INT_LIT8.S b/vm/mterp/x86-atom/OP_ADD_INT_LIT8.S
new file mode 100644
index 0000000..eabd4b5
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_ADD_INT_LIT8.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_ADD_INT_LIT8.S
+ */
+
+%include "x86-atom/binopLit8.S" {"instr":"addl %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_ADD_LONG.S b/vm/mterp/x86-atom/OP_ADD_LONG.S
new file mode 100644
index 0000000..7e31d35
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_ADD_LONG.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_ADD_LONG.S
+ */
+
+%include "x86-atom/binopWide.S" {"instr":"paddq %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_ADD_LONG_2ADDR.S b/vm/mterp/x86-atom/OP_ADD_LONG_2ADDR.S
new file mode 100644
index 0000000..4c65a45
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_ADD_LONG_2ADDR.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_ADD_LONG_2ADDR.S
+ */
+
+%include "x86-atom/binopWide2addr.S" {"instr":"paddq %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_AGET.S b/vm/mterp/x86-atom/OP_AGET.S
new file mode 100644
index 0000000..73a27ab
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_AGET.S
@@ -0,0 +1,53 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_AGET.S
+ *
+ * Code: Generic 32-bit array "get" operation. Provides a "scale" variable
+ * to specify a scale value which depends on the width of the array
+ * elements. Provides a "mov" variable which determines the type of
+ * mov performed also dependent on the type of the array element.
+ *
+ * For: aget, aget-boolean, aget-byte, aget-char, aget-object, sget, aget-short
+ *
+ * Description: Perform an array get operation at the identified index
+ * of a given array; load the array value into the value
+ * register. vAA <- vBB[vCC].
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+%default { "mov":"l","scale":"4"}
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ GET_VREG %ecx # %ecx<- vBB
+ GET_VREG %edx # %edx<- vCC
+ cmp $$0, %ecx # check for null array object
+ je common_errNullObject # handle null array object
+ cmp offArrayObject_length(%ecx), %edx # compare index to arrayObj->length
+ jnc common_errArrayIndex # handle index >= length, bail
+ lea (%ecx, %edx, $scale), %ecx # %ecx<- &vBB[vCC]
+ # trying: lea (%ecx, %edx, scale), %ecx
+ # to reduce code size
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ mov$mov offArrayObject_contents(%ecx), %edx # %edx<- vBB[vCC]
+ # doing this and the previous instr
+ # with one instr was not faster
+ SET_VREG %edx rINST # vAA<- %edx; value
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_AGET_BOOLEAN.S b/vm/mterp/x86-atom/OP_AGET_BOOLEAN.S
new file mode 100644
index 0000000..1d28745
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_AGET_BOOLEAN.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_AGET_BOOLEAN.S
+ */
+
+%include "x86-atom/OP_AGET.S" { "mov":"zbl", "scale":"1" }
diff --git a/vm/mterp/x86-atom/OP_AGET_BYTE.S b/vm/mterp/x86-atom/OP_AGET_BYTE.S
new file mode 100644
index 0000000..60e4266
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_AGET_BYTE.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_AGET_BYTE.S
+ */
+
+%include "x86-atom/OP_AGET.S" { "mov":"sbl", "scale":"1" }
diff --git a/vm/mterp/x86-atom/OP_AGET_CHAR.S b/vm/mterp/x86-atom/OP_AGET_CHAR.S
new file mode 100644
index 0000000..114d02d
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_AGET_CHAR.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_AGET_CHAR.S
+ */
+
+%include "x86-atom/OP_AGET.S" { "mov":"zwl", "scale":"2" }
diff --git a/vm/mterp/x86-atom/OP_AGET_OBJECT.S b/vm/mterp/x86-atom/OP_AGET_OBJECT.S
new file mode 100644
index 0000000..0ed02c3
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_AGET_OBJECT.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_AGET_OBJECT.S
+ */
+
+%include "x86-atom/OP_AGET.S"
diff --git a/vm/mterp/x86-atom/OP_AGET_SHORT.S b/vm/mterp/x86-atom/OP_AGET_SHORT.S
new file mode 100644
index 0000000..3ddb9b9
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_AGET_SHORT.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_AGET_SHORT.S
+ */
+
+%include "x86-atom/OP_AGET.S" { "mov":"swl", "scale":"2" }
diff --git a/vm/mterp/x86-atom/OP_AGET_WIDE.S b/vm/mterp/x86-atom/OP_AGET_WIDE.S
new file mode 100644
index 0000000..db5a930
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_AGET_WIDE.S
@@ -0,0 +1,43 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_AGET_WIDE.S
+ *
+ * Code: 64-bit array get operation.
+ *
+ * For: aget-wide
+ *
+ * Description: Perform an array get operation at the identified index
+ * of a given array; load the array value into the destination
+ * register. vAA <- vBB[vCC].
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ GET_VREG %ecx # %ecx<- vBB
+ GET_VREG %edx # %edx<- vCC
+ cmp $$0, %ecx # check for null array object
+ je common_errNullObject # handle null array object
+ cmp offArrayObject_length(%ecx), %edx # compare index to arrayObj->length
+ jnc common_errArrayIndex # handle index >= length, bail
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movq offArrayObject_contents(%ecx, %edx, 8), %xmm0 # %xmm0<- vBB[vCC]
+ movq %xmm0, (rFP, rINST, 4) # vAA<- %xmm0; value
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_AND_INT.S b/vm/mterp/x86-atom/OP_AND_INT.S
new file mode 100644
index 0000000..10d223b
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_AND_INT.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_AND_INT.S
+ */
+
+%include "x86-atom/binop.S" {"instr":"andl %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_AND_INT_2ADDR.S b/vm/mterp/x86-atom/OP_AND_INT_2ADDR.S
new file mode 100644
index 0000000..dcbd531
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_AND_INT_2ADDR.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_AND_INT_2ADDR.S
+ */
+
+%include "x86-atom/binop2addr.S" {"instr":"andl %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_AND_INT_LIT16.S b/vm/mterp/x86-atom/OP_AND_INT_LIT16.S
new file mode 100644
index 0000000..7e6493d
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_AND_INT_LIT16.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_AND_INT_LIT16.S
+ */
+
+%include "x86-atom/binopLit16.S" {"instr":"andl %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_AND_INT_LIT8.S b/vm/mterp/x86-atom/OP_AND_INT_LIT8.S
new file mode 100644
index 0000000..511e3ae
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_AND_INT_LIT8.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_AND_INT_LIT8.S
+ */
+
+%include "x86-atom/binopLit8.S" {"instr":"andl %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_AND_LONG.S b/vm/mterp/x86-atom/OP_AND_LONG.S
new file mode 100644
index 0000000..e62e312
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_AND_LONG.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_AND_LONG.S
+ */
+
+%include "x86-atom/binopWide.S" {"instr":"pand %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_AND_LONG_2ADDR.S b/vm/mterp/x86-atom/OP_AND_LONG_2ADDR.S
new file mode 100644
index 0000000..90e77e6
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_AND_LONG_2ADDR.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_AND_LONG_2ADDR.S
+ */
+
+%include "x86-atom/binopWide2addr.S" {"instr":"pand %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_APUT.S b/vm/mterp/x86-atom/OP_APUT.S
new file mode 100644
index 0000000..93b3866
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_APUT.S
@@ -0,0 +1,50 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_APUT.S
+ *
+ * Code: Generic 32-bit array put operation. Provides a "scale" variable
+ * to specify a scale value which depends on the width of the array
+ * elements. Provides a "mov" variable which determines the type of
+ * move performed also dependent on the type of the array element.
+ * Provides a "value" register to specify the source of the move
+ *
+ * For: aput-boolean, aput-byte, aput-char, aput-object, aput-short
+ *
+ * Description: Perform an array put operation from the value register;
+ * store the value register at the identified index of a
+ * given array. vBB[vCC] <- vAA
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+%default { "mov":"l","scale":"4", "value": "rINST"}
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ GET_VREG %ecx # %ecx<- vBB
+ GET_VREG %edx # %edx<- vCC
+ cmp $$0, %ecx # check for null array object
+ je common_errNullObject # handle null array object
+ cmp offArrayObject_length(%ecx), %edx # compare index to arrayObj->length
+ jnc common_errArrayIndex # handle index >= length, bail
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ lea (%ecx, %edx, $scale), %ecx # %ecx<- &vBB[vCC]
+ GET_VREG rINST # rINST<- vAA
+ mov$mov $value, offArrayObject_contents(%ecx) # vBB[vCC]<- rINSTx; value
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_APUT_BOOLEAN.S b/vm/mterp/x86-atom/OP_APUT_BOOLEAN.S
new file mode 100644
index 0000000..d9afd6d
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_APUT_BOOLEAN.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_APUT_BOOLEAN.S
+ */
+
+%include "x86-atom/OP_APUT.S" { "mov":"b", "scale":"1", "value":"rINSTbl" }
diff --git a/vm/mterp/x86-atom/OP_APUT_BYTE.S b/vm/mterp/x86-atom/OP_APUT_BYTE.S
new file mode 100644
index 0000000..29cb708
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_APUT_BYTE.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_APUT_BYTE.S
+ */
+
+%include "x86-atom/OP_APUT.S" { "mov":"b", "scale":"1", "value":"rINSTbl" }
diff --git a/vm/mterp/x86-atom/OP_APUT_CHAR.S b/vm/mterp/x86-atom/OP_APUT_CHAR.S
new file mode 100644
index 0000000..d43e540
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_APUT_CHAR.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_APUT_CHAR.S
+ */
+
+%include "x86-atom/OP_APUT.S" { "mov":"w", "scale":"2", "value":"rINSTw" }
diff --git a/vm/mterp/x86-atom/OP_APUT_OBJECT.S b/vm/mterp/x86-atom/OP_APUT_OBJECT.S
new file mode 100644
index 0000000..0e23d71
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_APUT_OBJECT.S
@@ -0,0 +1,77 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_APUT_OBJECT.S
+ *
+ * Code: 32-bit array put operation. Provides an "scale" variable
+ * specify a scale value which depends on the width of the array
+ * elements. Provides a "mov" variable which determines the type of
+ * mov performed also dependent on the type of the array element.
+ * Provides a "value" register to specify the source of the mov
+ *
+ * For: aput-boolean, aput-byte, aput-char, aput-object, aput-short
+ *
+ * Description: Perform an array put operation from the value register;
+ * store the value register at the identified index of a
+ * given array. vBB[vCC] <- vAA
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %eax # %eax<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ GET_VREG %eax # %eax<- vBB
+ GET_VREG %edx # %edx<- vCC
+ cmp $$0, %eax # check for null array object
+ je common_errNullObject # handle null array object
+ cmp offArrayObject_length(%eax), %edx # compare index to arrayObj->length
+ jnc common_errArrayIndex # handle index >= length, bail
+ GET_VREG rINST # rINST<- vAA
+ lea (%eax, %edx, 4), %edx # %edx<- &vBB[vCC]
+ cmp $$0, rINST # check for null reference
+ je .L${opcode}_skip_check # reference is null so skip type check
+ jmp .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+ movl %edx, sReg0 # save &vBB[vCC]
+ movl %eax, sReg1 # save object head
+ movl offObject_clazz(rINST), %edx # %edx<- obj->clazz
+ movl %edx, -8(%esp) # push parameter obj->clazz
+ movl offObject_clazz(%eax), %eax # %eax<- arrayObj->clazz
+ movl %eax, -4(%esp) # push parameter arrayObj->clazz
+ lea -8(%esp), %esp
+ call dvmCanPutArrayElement # test object type vs. array type
+ # call: ClassObject* elemClass, ClassObject* arrayClass)
+ # return: bool
+ lea 8(%esp), %esp
+ testl %eax, %eax # check for invalid array value
+ je common_errArrayStore # handle invalid array value
+ movl sReg0, %edx # restore &vBB[vCC]
+ movl rINST, offArrayObject_contents(%edx)
+ movl rGLUE, %eax
+ FFETCH_ADV 2, %ecx # %ecx<- next instruction hi; fetch, advance
+ movl offGlue_cardTable(%eax), %eax # get card table base
+ movl sReg1, %edx # restore object head
+ shrl $$GC_CARD_SHIFT, %edx # object head to card number
+ movb %al, (%eax, %edx) # mark card using object head
+ FGETOP_JMP 2, %ecx # jump to next instruction; getop, jmp
+.L${opcode}_skip_check:
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movl rINST, offArrayObject_contents(%edx)
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_APUT_SHORT.S b/vm/mterp/x86-atom/OP_APUT_SHORT.S
new file mode 100644
index 0000000..daef0d8
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_APUT_SHORT.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_APUT_SHORT.S
+ */
+
+%include "x86-atom/OP_APUT.S" { "mov":"w", "scale":"2", "value":"rINSTw" }
diff --git a/vm/mterp/x86-atom/OP_APUT_WIDE.S b/vm/mterp/x86-atom/OP_APUT_WIDE.S
new file mode 100644
index 0000000..b1b9e6a
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_APUT_WIDE.S
@@ -0,0 +1,43 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_APUT_WIDE.S
+ *
+ * Code: 64-bit array put operation.
+ *
+ * For: aput-wide
+ *
+ * Description: Perform an array put operation from the value register;
+ * store the value register at the identified index of a
+ * given array. vBB[vCC] <- vAA.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ GET_VREG %ecx # %ecx<- vBB
+ GET_VREG %edx # %edx<- vCC
+ cmp $$0, %ecx # check for null array object
+ je common_errNullObject # handle null array object
+ cmp offArrayObject_length(%ecx), %edx # compare index to arrayObj->length
+ jnc common_errArrayIndex # handle index >= length, bail
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movq (rFP, rINST, 4), %xmm0 # %xmm0<- vAA
+ movq %xmm0, offArrayObject_contents(%ecx, %edx, 8) # vBB[vCC]<- %xmm0; value
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_ARRAY_LENGTH.S b/vm/mterp/x86-atom/OP_ARRAY_LENGTH.S
new file mode 100644
index 0000000..485f815
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_ARRAY_LENGTH.S
@@ -0,0 +1,40 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_ARRAY_LENGTH.S
+ *
+ * Code: 32-bit array length operation.
+ *
+ * For: array-length
+ *
+ * Description: Store the length of the indicated array in the given
+ * destination register. vB <- offArrayObject_length(vA)
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %eax # %eax<- BA
+ shr $$4, %eax # %eax<- B
+ andl $$15, rINST # rINST<- A
+ GET_VREG %eax # %eax<- vB
+ cmp $$0, %eax # check for null array object
+ je common_errNullObject # handle null array object
+ FFETCH_ADV 1, %edx # %edx<- next instruction hi; fetch, advance
+ movl offArrayObject_length(%eax), %eax # %eax<- array length
+ movl %eax, (rFP, rINST, 4) # vA<- %eax; array length
+ FGETOP_JMP 1, %edx # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_BREAKPOINT.S b/vm/mterp/x86-atom/OP_BREAKPOINT.S
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_BREAKPOINT.S
diff --git a/vm/mterp/x86-atom/OP_CHECK_CAST.S b/vm/mterp/x86-atom/OP_CHECK_CAST.S
new file mode 100644
index 0000000..bbbdb0f
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_CHECK_CAST.S
@@ -0,0 +1,113 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_CHECK_CAST.S
+ *
+ * Code: Checks to see if a cast is allowed. Uses no substitutions.
+ *
+ * For: check-cast
+ *
+ * Description: Throw if the reference in the given register cannot be
+ * cast to the indicated type. The type must be a reference
+ * type (not a primitive type).
+ *
+ * Format: AA|op BBBB (21c)
+ *
+ * Syntax: op vAA, type@BBBB
+ */
+
+ movl rGLUE, %edx # get MterpGlue pointer
+ movl offGlue_methodClassDex(%edx), %eax # %eax<- pDvmDex
+ GET_VREG rINST # rINST<- vAA
+ movl offDvmDex_pResClasses(%eax), %eax # %eax<- pDvmDex->pResClasses
+ cmp $$0, rINST # check for null reference object
+ je .L${opcode}_okay # can always cast null object
+ FETCH 1, %ecx # %ecx<- BBBB
+ movl (%eax, %ecx, 4), %ecx # %ecx<- resolved class
+ cmp $$0, %ecx # check if classes is resolved before?
+ je .L${opcode}_resolve # resolve class
+ jmp .L${opcode}_resolved # continue
+%break
+
+.L${opcode}_resolved:
+ cmp %ecx, offObject_clazz(rINST) # check for same class
+ jne .L${opcode}_fullcheck # not same class; do full check
+
+.L${opcode}_okay:
+ FINISH 2 # jump to next instruction
+
+ /*
+ * Trivial test failed, need to perform full check.
+ * offObject_clazz(rINST) holds obj->clazz
+ * %ecx holds class resolved from BBBB
+ * rINST holds object
+ */
+
+.L${opcode}_fullcheck:
+ movl offObject_clazz(rINST), %eax # %eax<- obj->clazz
+ movl %eax, -12(%esp) # push parameter obj->clazz
+ movl %ecx, -8(%esp) # push parameter # push parameter resolved class
+ lea -12(%esp), %esp
+ call dvmInstanceofNonTrivial # call: (ClassObject* instance, ClassObject* clazz)
+ # return: int
+ lea 12(%esp), %esp
+ cmp $$0, %eax # failed?
+ jne .L${opcode}_okay # success
+
+ /*
+ * A cast has failed. We need to throw a ClassCastException with the
+ * class of the object that failed to be cast.
+ */
+
+ EXPORT_PC # we will throw an exception
+ movl $$.LstrClassCastExceptionPtr, -8(%esp) # push parameter message
+ movl offObject_clazz(rINST), rINST # rINST<- obj->clazz
+ movl offClassObject_descriptor(rINST), rINST # rINST<- obj->clazz->descriptor
+ movl rINST, -4(%esp) # push parameter obj->clazz->descriptor
+ lea -8(%esp), %esp
+ call dvmThrowExceptionWithClassMessage # call: (const char* exceptionDescriptor,
+ # const char* messageDescriptor, Object* cause)
+ # return: void
+ lea 8(%esp), %esp
+ jmp common_exceptionThrown
+
+ /*
+ * Resolution required. This is the least-likely path.
+ *
+ * rINST holds object
+ */
+
+.L${opcode}_resolve:
+ movl offGlue_method(%edx), %eax # %eax<- glue->method
+ FETCH 1, %ecx # %ecx holds BBBB
+ EXPORT_PC # in case we throw an exception
+ movl $$0, -8(%esp) # push parameter false
+ movl offMethod_clazz(%eax), %eax # %eax<- glue->method->clazz
+ movl %ecx, -12(%esp) # push parameter BBBB
+ movl %eax, -16(%esp) # push parameter glue->method>clazz
+ lea -16(%esp), %esp
+ call dvmResolveClass # resolve ClassObject pointer
+ # call: (const ClassObject* referrer, u4 classIdx,
+ # bool fromUnverifiedConstant)
+ # return ClassObject*
+ lea 16(%esp), %esp
+ cmp $$0, %eax # check for null pointer
+ je common_exceptionThrown # handle excpetion
+ movl %eax, %ecx # %ecx<- resolved class
+ jmp .L${opcode}_resolved
+
+.LstrClassCastExceptionPtr:
+.asciz "Ljava/lang/ClassCastException;"
diff --git a/vm/mterp/x86-atom/OP_CMPG_DOUBLE.S b/vm/mterp/x86-atom/OP_CMPG_DOUBLE.S
new file mode 100644
index 0000000..87f8a3b
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_CMPG_DOUBLE.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_CMPG_DOUBLE.S
+ */
+
+%include "x86-atom/OP_CMPL_FLOAT.S" { "nan":"$0x1", "sod":"d"}
diff --git a/vm/mterp/x86-atom/OP_CMPG_FLOAT.S b/vm/mterp/x86-atom/OP_CMPG_FLOAT.S
new file mode 100644
index 0000000..de42969
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_CMPG_FLOAT.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_CMPG_FLOAT.S
+ */
+
+%include "x86-atom/OP_CMPL_FLOAT.S" { "nan":"$0x1" }
diff --git a/vm/mterp/x86-atom/OP_CMPL_DOUBLE.S b/vm/mterp/x86-atom/OP_CMPL_DOUBLE.S
new file mode 100644
index 0000000..2a603a4
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_CMPL_DOUBLE.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_CMPL_DOUBLE.S
+ */
+
+%include "x86-atom/OP_CMPL_FLOAT.S" { "sod":"d" }
diff --git a/vm/mterp/x86-atom/OP_CMPL_FLOAT.S b/vm/mterp/x86-atom/OP_CMPL_FLOAT.S
new file mode 100644
index 0000000..5cb3ec7
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_CMPL_FLOAT.S
@@ -0,0 +1,63 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_CMPL_FLOAT.S
+ *
+ * Code: Provides a "nan" variable to specify the return value for
+ * NaN. Provides a variable "sod" which appends a "s" or a "d"
+ * to the move and comparison instructions, depending on if we
+ * are working with a float or a double. For instructions
+ * cmpx-float and cmpx-double, the x will be eiher a g or a l
+ * to specify positive or negative bias for NaN.
+ *
+ * For: cmpg-double, dmpg-float, cmpl-double, cmpl-float
+ *
+ * Description: Perform the indicated floating point or long comparison,
+ * storing 0 if the two arguments are equal, 1 if the second
+ * argument is larger, or -1 if the first argument is larger.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+%default { "nan":"$0xFFFFFFFF" , "sod":"s" }
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movs$sod (rFP, %ecx, 4), %xmm0 # %xmm0<- vBB
+ comis$sod (rFP, %edx, 4), %xmm0 # do comparison
+ ja .L${opcode}_greater
+ jp .L${opcode}_finalNan
+ jz .L${opcode}_final
+
+.L${opcode}_less:
+ movl $$0xFFFFFFFF, (rFP, rINST, 4) # vAA<- less than
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+%break
+.L${opcode}_greater:
+ movl $$0x1, (rFP, rINST, 4) # vAA<- greater than
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+.L${opcode}_final:
+ movl $$0x0, (rFP, rINST, 4) # vAA<- equal
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+.L${opcode}_finalNan:
+ movl $nan, (rFP, rINST, 4) # vAA<- NaN
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_CMP_LONG.S b/vm/mterp/x86-atom/OP_CMP_LONG.S
new file mode 100644
index 0000000..cf021a3
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_CMP_LONG.S
@@ -0,0 +1,55 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_CMP_LONG.S
+ *
+ * Code: Compare floating point values. Uses no substitutions.
+ *
+ * For: cmp-long
+ *
+ * Description: Perform a long comparison, storing 0 if the two
+ * arguments are equal, 1 if the second argument is larger
+ * or -1 if the first argument is larger.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ movl 4(rFP, %ecx, 4), %eax # %eax<- vBBhi
+ cmp 4(rFP, %edx, 4), %eax # compare vCChi and vBBhi
+ jl .L${opcode}_less
+ jg .L${opcode}_greater
+ movl (rFP, %ecx, 4), %eax # %eax<- vBBlo
+ cmp (rFP, %edx, 4), %eax # compare vCClo and vBBlo
+ ja .L${opcode}_greater
+ jne .L${opcode}_less
+ jmp .L${opcode}_final
+%break
+
+.L${opcode}_final:
+ movl $$0x0, (rFP, rINST, 4) # vAA<- equal
+ FINISH 2 # jump to next instruction
+
+.L${opcode}_less:
+ movl $$0xFFFFFFFF, (rFP, rINST, 4) # vAA<- less than
+ FINISH 2 # jump to next instruction
+
+.L${opcode}_greater:
+ movl $$0x1, (rFP, rINST, 4) # vAA<- greater than
+ FINISH 2 # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_CONST.S b/vm/mterp/x86-atom/OP_CONST.S
new file mode 100644
index 0000000..7ff4c23
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_CONST.S
@@ -0,0 +1,36 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_CONST.S
+ *
+ * Code: Move a literal to a register. Uses no substitutions.
+ *
+ * For: const
+ *
+ * Description: Move the given literal value into the specified register
+ *
+ * Format: AA|op BBBBlo BBBBhi (31i)
+ *
+ * Syntax: op vAA, #+BBBBBBBB
+ */
+
+ FETCH 2, %edx # %edx<- BBBBhi
+ FETCH 1, %ecx # %ecx<- BBBBlo
+ shl $$16, %edx # move BBBB to high bits
+ or %edx, %ecx # %ecx<- #+BBBBBBBB
+ FFETCH_ADV 3, %eax # %eax<- next instruction hi; fetch, advance
+ SET_VREG %ecx, rINST # vAA<- %ecx; literal
+ FGETOP_JMP 3, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_CONST_16.S b/vm/mterp/x86-atom/OP_CONST_16.S
new file mode 100644
index 0000000..f340696
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_CONST_16.S
@@ -0,0 +1,34 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_CONST_16.S
+ *
+ * Code: Moves a literal to a register. Uses no substitutions.
+ *
+ * For: const/16
+ *
+ * Description: Move the given literal value (right-zero-extended to 32
+ * bits) into the specified register
+ *
+ * Format: AA|op BBBB (21s)
+ *
+ * Syntax: op vAA, #+BBBB
+ */
+
+ FETCHs 1, %edx # %edx<- BBBB
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ SET_VREG %edx rINST # vAA<- BBBB; literal
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_CONST_4.S b/vm/mterp/x86-atom/OP_CONST_4.S
new file mode 100644
index 0000000..5396d72
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_CONST_4.S
@@ -0,0 +1,37 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_CONST_4.S
+ *
+ * Code: Moves a literal to a register. Uses no substitutions.
+ *
+ * For: const/4
+ *
+ * Description: Move the given literal value (right-zero-extended to 32
+ * bits) into the specified register.
+ *
+ * Format: B|A|op (11n)
+ *
+ * Syntax: op vA, #+B
+ */
+
+ movl rINST, %edx # %edx<- BA
+ andl $$15, rINST # rINST<- A
+ shl $$24, %edx # %edx<- B000
+ sar $$28, %edx # %edx<- right-zero-extended B
+ FFETCH_ADV 1, %eax # %eax<- next instruction hi; fetch, advance
+ SET_VREG %edx, rINST # vA<- %edx; literal
+ FGETOP_JMP 1, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_CONST_CLASS.S b/vm/mterp/x86-atom/OP_CONST_CLASS.S
new file mode 100644
index 0000000..bc6657c
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_CONST_CLASS.S
@@ -0,0 +1,67 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_CONST_CLASS.S
+ *
+ * Code: Move a class reference to a register. Uses no substitutions.
+ *
+ * For: const/class
+ *
+ * Description: Move a reference to the class specified
+ * by the given index into the specified register.
+ * In the case where the indicated type is primitive,
+ * this will store a reference to the primitive type's
+ * degenerate class.
+ *
+ * Format: AA|op BBBBlo BBBBhi (21c)
+ *
+ * Syntax: op vAA, field@BBBB
+ */
+
+ movl rGLUE, %edx # get MterpGlue pointer
+ FETCH 1, %ecx # %ecx<- BBBB
+ movl offGlue_methodClassDex(%edx), %eax # %eax<- pDvmDex
+ movl offDvmDex_pResClasses(%eax), %eax # %eax<- pDvmDex->pResClasses
+ movl (%eax, %ecx, 4), %eax # %eax<- resolved class
+ cmp $$0, %eax # check if classes is resolved before?
+ je .L${opcode}_resolve # resolve class
+ SET_VREG %eax, rINST # vAA<- resolved class
+ FINISH 2 # jump to next instruction
+%break
+
+ /*
+ * Continuation if the Class has not yet been resolved.
+ * %ecx: BBBB (Class ref)
+ * need: target register
+ */
+
+.L${opcode}_resolve:
+ EXPORT_PC
+ movl offGlue_method(%edx), %edx # %edx<- glue->method
+ movl offMethod_clazz(%edx), %edx # %edx<- glue->method->clazz
+ movl $$1, -4(%esp) # push parameter true
+ movl %ecx, -8(%esp) # push parameter
+ movl %edx, -12(%esp) # push parameter glue->method->clazz
+ lea -12(%esp), %esp
+ call dvmResolveClass # resolve ClassObject pointer
+ # class: (const ClassObject* referrer, u4 classIdx,
+ # bool fromUnverifiedConstant)
+ # return: ClassObject*
+ lea 12(%esp), %esp
+ cmp $$0, %eax # check for null pointer
+ je common_exceptionThrown # handle exception
+ SET_VREG %eax, rINST # vAA<- resolved class
+ FINISH 2 # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_CONST_HIGH16.S b/vm/mterp/x86-atom/OP_CONST_HIGH16.S
new file mode 100644
index 0000000..f47d34b
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_CONST_HIGH16.S
@@ -0,0 +1,35 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_CONST_HIGH16.S
+ *
+ * Code: Move a literal to a register. Uses no substitutions.
+ *
+ * For: const/high16
+ *
+ * Description: Move the given literal value (right-zero-extended to 32
+ * bits) into the specified register
+ *
+ * Format: AA|op BBBB (21h)
+ *
+ * Syntax: op vAA, #+BBBB0000
+ */
+
+ FETCH 1, %ecx # %ecx<- 0000BBBB (zero-extended)
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ shl $$16, %ecx # %ecx<- BBBB0000
+ SET_VREG %ecx, rINST # vAA<- %ecx; BBBB0000
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_CONST_STRING.S b/vm/mterp/x86-atom/OP_CONST_STRING.S
new file mode 100644
index 0000000..c958ed4
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_CONST_STRING.S
@@ -0,0 +1,65 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_CONST_STRING.S
+ *
+ * Code: Move a string reference to a register. Uses no substitutions.
+ *
+ * For: const/string
+ *
+ * Description: Move a referece to the string specified by the given
+ * index into the specified register. vAA <- pResString[BBBB]
+ *
+ * Format: AA|op BBBB (21c)
+ *
+ * Syntax: op vAA, string@BBBB
+ */
+
+ FETCH 1, %ecx # %ecx<- BBBB
+ movl rGLUE, %edx # get MterpGlue pointer
+ movl offGlue_methodClassDex(%edx), %eax # %eax<- glue->methodClassDex
+ movl offDvmDex_pResStrings(%eax), %eax # %eax<- glue->methodClassDex->pResStrings
+ movl (%eax, %ecx, 4), %eax # %eax<- pResStrings[BBBB]
+ cmp $$0, %eax # check if string is resolved
+ je .L${opcode}_resolve # resolve string reference
+ SET_VREG %eax, rINST # vAA<- %eax; pResString[BBBB]
+ FINISH 2 # jump to next instruction
+
+%break
+
+
+ /*
+ * Continuation if the Class has not yet been resolved.
+ * %ecx: BBBB (Class ref)
+ * need: target register
+ */
+
+.L${opcode}_resolve:
+ EXPORT_PC
+ movl offGlue_method(%edx), %edx # %edx<- glue->method
+ movl offMethod_clazz(%edx), %edx # %edx<- glue->method->clazz
+ movl %ecx, -4(%esp) # push parameter class ref
+ movl %edx, -8(%esp) # push parameter glue->method->clazz
+ lea -8(%esp), %esp
+ call dvmResolveString # resolve string reference
+ # call: (const ClassObject* referrer, u4 stringIdx)
+ # return: StringObject*
+ lea 8(%esp), %esp
+ cmp $$0, %eax # check if resolved string failed
+ je common_exceptionThrown # resolve failed; exception thrown
+ SET_VREG %eax, rINST # vAA<- %eax; pResString[BBBB]
+ FFETCH_ADV 2, %edx # %edx<- next instruction hi; fetch, advance
+ FGETOP_JMP 2, %edx # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_CONST_STRING_JUMBO.S b/vm/mterp/x86-atom/OP_CONST_STRING_JUMBO.S
new file mode 100644
index 0000000..09d045d
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_CONST_STRING_JUMBO.S
@@ -0,0 +1,66 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_CONST_STRING_JUMBO.S
+ *
+ * Code: Move a string reference to a register. Uses no substitutions.
+ *
+ * For: const/string-jumbo
+ *
+ * Description: Move a reference to the string specified by the given
+ * index into the specified register. vAA <- pResString[BBBB]
+ *
+ * Format: AA|op BBBBlo BBBBhi (31c)
+ *
+ * Syntax: op vAA, string@BBBBBBBB
+ */
+
+ movl rGLUE, %edx # get MterpGlue pointer
+ movl offGlue_methodClassDex(%edx), %eax # %eax<- glue->methodClassDex
+ movl offDvmDex_pResStrings(%eax), %eax # %eax<- glue->methodClassDex->pResStrings
+ FETCH 1, %ecx # %ecx<- BBBBlo
+ FETCH 2, %edx # %edx<- BBBBhi
+ shl $$16, %edx # %edx<- prepare to create &BBBBBBBB
+ or %edx, %ecx # %ecx<- &BBBBBBBB
+ movl (%eax, %ecx, 4), %eax # %eax<- pResStrings[BBBB]
+ cmp $$0, %eax # check if string is resolved
+ je .L${opcode}_resolve # resolve string reference
+ SET_VREG %eax, rINST # vAA<- %eax; pResString[BBBB]
+ FINISH 3 # jump to next instruction
+%break
+
+
+ /*
+ * Continuation if the Class has not yet been resolved.
+ * %ecx: BBBB (Class ref)
+ * need: target register
+ */
+.L${opcode}_resolve:
+ EXPORT_PC
+ movl rGLUE, %edx # get MterpGlue pointer
+ movl offGlue_method(%edx), %edx # %edx<- glue->method
+ movl offMethod_clazz(%edx), %edx # %edx <- glue->method->clazz
+ movl %ecx, -4(%esp) # push parameter class ref
+ movl %edx, -8(%esp) # push parameter glue->method->clazz
+ lea -8(%esp), %esp
+ call dvmResolveString # resolve string reference
+ # call: (const ClassObject* referrer, u4 stringIdx)
+ # return: StringObject*
+ lea 8(%esp), %esp
+ cmp $$0, %eax # check if resolved string failed
+ je common_exceptionThrown # resolve failed; exception thrown
+ SET_VREG %eax, rINST # vAA<- %eax; pResString[BBBB]
+ FINISH 3 # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_CONST_WIDE.S b/vm/mterp/x86-atom/OP_CONST_WIDE.S
new file mode 100644
index 0000000..1ce7c76
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_CONST_WIDE.S
@@ -0,0 +1,42 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_CONST_WIDE.S
+ *
+ * Code: Move a literal to a register. Uses no substitutions.
+ *
+ * For: const-wide
+ *
+ * Description: Move the given literal value into the specified
+ * register pair
+ *
+ * Format: AA|op BBBBlolo BBBBlohi BBBBhilo BBBBhihi (51l)
+ *
+ * Syntax: op vAA, #+BBBBBBBBBBBBBBBB
+ */
+
+ FETCH 1, %ecx # %ecx<- BBBBlolo
+ FETCH 2, %edx # %edx<- BBBBlohi
+ shl $$16, %edx # %edx<- prepare to create #+BBBBBBBBlo
+ or %edx, %ecx # %ecx<- #+BBBBBBBBlo
+ movl %ecx, (rFP, rINST, 4) # vAA <- #+BBBBBBBBlo
+ FETCH 3, %ecx # %ecx<- BBBBhilo
+ FETCH 4, %edx # %edx<- BBBBhihi
+ FFETCH_ADV 5, %eax # %eax<- next instruction hi; fetch, advance
+ shl $$16, %edx # %edx<- prepare to create #+BBBBBBBBhi
+ or %edx, %ecx # %ecx<- #+BBBBBBBBhi
+ movl %ecx, 4(rFP, rINST, 4) # vAA+1 <- #+BBBBBBBBlo
+ FGETOP_JMP 5, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_CONST_WIDE_16.S b/vm/mterp/x86-atom/OP_CONST_WIDE_16.S
new file mode 100644
index 0000000..c980f10
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_CONST_WIDE_16.S
@@ -0,0 +1,37 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_CONST_WIDE_16.S
+ *
+ * Code: Move a literal to a register. Uses no substitutions.
+ *
+ * For: const-wide/16
+ *
+ * Description: Move the given literal value (sign-extended to 64 bits)
+ * into the specified register-pair
+ *
+ * Format: AA|op BBBB (21s)
+ *
+ * Syntax: op vAA, #+BBBB
+ */
+
+ FETCHs 1, %ecx # %ecx<- ssssBBBB (sign-extended)
+ movl %ecx, %edx # %edx<- ssssBBBB (sign-extended)
+ sar $$31, %ecx # %ecx<- sign bit
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movl %edx, (rFP, rINST, 4) # vAA<- ssssBBBB
+ movl %ecx, 4(rFP, rINST, 4) # vAA+1<- ssssssss
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_CONST_WIDE_32.S b/vm/mterp/x86-atom/OP_CONST_WIDE_32.S
new file mode 100644
index 0000000..92f8450
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_CONST_WIDE_32.S
@@ -0,0 +1,39 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_CONST_WIDE_32.S
+ *
+ * Code: Move a literal to a register. Uses no substitutions.
+ *
+ * For: const-wide/32
+ *
+ * Description: Move the given literal value (sign-extended to 64 bits)
+ * into the specified register-pair
+ *
+ * Format: AA|op BBBBlo BBBBhi (31i)
+ *
+ * Syntax: op vAA, #+BBBBBBBB
+ */
+
+ FETCH 1, %edx # %edx<- BBBBlo
+ FETCHs 2, %ecx # %ecx<- BBBBhi
+ shl $$16, %ecx # prepare to create #+BBBBBBBB
+ or %ecx, %edx # %edx<- %edx<- #+BBBBBBBB
+ sar $$31, %ecx # %ecx<- sign bit
+ FFETCH_ADV 3, %eax # %eax<- next instruction hi; fetch, advance
+ movl %edx, (rFP, rINST, 4) # vAA<- BBBBBBBB
+ movl %ecx, 4(rFP, rINST, 4) # vAA+1<- ssssssss
+ FGETOP_JMP 3, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_CONST_WIDE_HIGH16.S b/vm/mterp/x86-atom/OP_CONST_WIDE_HIGH16.S
new file mode 100644
index 0000000..5b4b4f1
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_CONST_WIDE_HIGH16.S
@@ -0,0 +1,35 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_CONST_WIDE_HIGH16.S
+ *
+ * Code: Move a literal value to a register. Uses no substitutions.
+ *
+ * For: const-wide/high16
+ *
+ * Description: Move the given literal value (right-zero-extended to 64
+ * bits) into the specified register
+ *
+ * Format: AA|op BBBB (21h)
+ *
+ * Syntax: op vAA, #+BBBB000000000000
+ */
+
+ FETCH 1, %ecx # %ecx<- 0000BBBB (zero-extended)
+ shl $$16, %ecx # rINST<- AA
+ movl $$0, (rFP, rINST, 4) # vAAlow<- 00000000
+ movl %ecx, 4(rFP, rINST, 4) # vAAhigh<- %ecx; BBBB0000
+ FINISH 2 # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_DIV_DOUBLE.S b/vm/mterp/x86-atom/OP_DIV_DOUBLE.S
new file mode 100644
index 0000000..418a230
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_DIV_DOUBLE.S
@@ -0,0 +1,37 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_DIV_DOUBLE.S
+ *
+ * Code: Divides doubles. Uses no substitutions.
+ *
+ * For: div-double
+ *
+ * Description: Divide operation on two source registers, storing
+ * the result in a destination register
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ fldl (rFP, %ecx, 4) # floating point stack vBB
+ fdivl (rFP, %edx, 4) # divide double; vBB/vCC
+ fstpl (rFP, rINST, 4) # vAA<- result
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_DIV_DOUBLE_2ADDR.S b/vm/mterp/x86-atom/OP_DIV_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..7003e30
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_DIV_DOUBLE_2ADDR.S
@@ -0,0 +1,37 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_DIV_DOUBLE_2ADDR.S
+ *
+ * Code: Divides doubles. Uses no substitutions.
+ *
+ * For: div-double/2addr
+ *
+ * Description: Divide operation on two source registers, storing
+ * the result in the first source reigster
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ andl $$15, %edx # %edx<- A
+ shr $$4, rINST # rINST<- B
+ fldl (rFP, %edx, 4) # %xmm0<- vA
+ fdivl (rFP, rINST, 4) # divide double; vA/vB
+ fstpl (rFP, %edx, 4) # vAA<- result
+ FINISH 1 # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_DIV_FLOAT.S b/vm/mterp/x86-atom/OP_DIV_FLOAT.S
new file mode 100644
index 0000000..a7aabd7
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_DIV_FLOAT.S
@@ -0,0 +1,37 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_DIV_FLOAT.S
+ *
+ * Code: Divides floats. Uses no substitutions.
+ *
+ * For: div-float
+ *
+ * Description: Divide operation on two source registers, storing
+ * the result in a destiniation register
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %eax # %eax<- BB
+ FETCH_CC 1, %ecx # %ecx<- CC
+ flds (rFP, %eax, 4) # floating point stack vBB
+ fdivs (rFP, %ecx, 4) # divide double; vBB/vCC
+ fstps (rFP, rINST, 4) # vAA<- result
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_DIV_FLOAT_2ADDR.S b/vm/mterp/x86-atom/OP_DIV_FLOAT_2ADDR.S
new file mode 100644
index 0000000..471f30a
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_DIV_FLOAT_2ADDR.S
@@ -0,0 +1,37 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_DIV_FLOAT_2ADDR.S
+ *
+ * Code: Divides floats. Uses no substitutions.
+ *
+ * For: div-float/2addr
+ *
+ * Description: Divide operation on two source registers, storing
+ * the result in the first source reigster
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %ecx # %ecx<- BA
+ andl $$15, %ecx # %ecx<- A
+ shr $$4, rINST # rINST<- B
+ flds (rFP, %ecx, 4) # %xmm0<- vA
+ fdivs (rFP, rINST, 4) # divide double; vA/vB
+ fstps (rFP, %ecx, 4) # vAA<- result
+ FINISH 1 # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_DIV_INT.S b/vm/mterp/x86-atom/OP_DIV_INT.S
new file mode 100644
index 0000000..c7f5b27
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_DIV_INT.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_DIV_INT.S
+ */
+
+%include "x86-atom/binopD.S"
diff --git a/vm/mterp/x86-atom/OP_DIV_INT_2ADDR.S b/vm/mterp/x86-atom/OP_DIV_INT_2ADDR.S
new file mode 100644
index 0000000..762d82f
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_DIV_INT_2ADDR.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_DIV_INT_2ADDR.S
+ */
+
+%include "x86-atom/binopD2addr.S"
diff --git a/vm/mterp/x86-atom/OP_DIV_INT_LIT16.S b/vm/mterp/x86-atom/OP_DIV_INT_LIT16.S
new file mode 100644
index 0000000..9c2ce55
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_DIV_INT_LIT16.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_DIV_INT_LIT16.S
+ */
+
+%include "x86-atom/binopDLit16.S"
diff --git a/vm/mterp/x86-atom/OP_DIV_INT_LIT8.S b/vm/mterp/x86-atom/OP_DIV_INT_LIT8.S
new file mode 100644
index 0000000..ef0ea7b
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_DIV_INT_LIT8.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_DIV_INT_LIT8.S
+ */
+
+%include "x86-atom/binopDLit8.S"
diff --git a/vm/mterp/x86-atom/OP_DIV_LONG.S b/vm/mterp/x86-atom/OP_DIV_LONG.S
new file mode 100644
index 0000000..38ade6a
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_DIV_LONG.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_DIV_LONG.S
+ */
+
+%include "x86-atom/binopDivRemLong.S"
diff --git a/vm/mterp/x86-atom/OP_DIV_LONG_2ADDR.S b/vm/mterp/x86-atom/OP_DIV_LONG_2ADDR.S
new file mode 100644
index 0000000..41e5430
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_DIV_LONG_2ADDR.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_DIV_LONG_2ADDR.S
+ */
+
+%include "x86-atom/binopDivRemLong2Addr.S"
diff --git a/vm/mterp/x86-atom/OP_DOUBLE_TO_FLOAT.S b/vm/mterp/x86-atom/OP_DOUBLE_TO_FLOAT.S
new file mode 100644
index 0000000..516a2e6
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_DOUBLE_TO_FLOAT.S
@@ -0,0 +1,36 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_DOUBLE_TO_FLOAT.S
+ *
+ * Code: Converts a double to a float. Uses no substitutions.
+ *
+ * For: double-to-float
+ *
+ * Description: Convert the source register (a double) to a float
+ * and store the result in the destination register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $$4, rINST # rINST<- B
+ and $$15, %edx # %edx<- A
+ fldl (rFP, rINST, 4) # load &vB
+ fstps (rFP, %edx, 4) # store float
+ FINISH 1 # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_DOUBLE_TO_INT.S b/vm/mterp/x86-atom/OP_DOUBLE_TO_INT.S
new file mode 100644
index 0000000..f377762
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_DOUBLE_TO_INT.S
@@ -0,0 +1,68 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_DOUBLE_TO_INT.S
+ *
+ * Code: Converts a double to an integer. Uses no substitutions.
+ *
+ * For: double-to-int
+ *
+ * Description: Convert the source register (a double) to an integer
+ * and store the result in the destination register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $$4, rINST # rINST<- B
+ and $$15, %edx # %edx<- A
+ fldl (rFP, rINST, 4) # load &vB
+ fildl .LintMax # push max int value
+ fildl .LintMin # push min int value
+ fucomip %st(2), %st(0) # check for negInf
+ jae .L${opcode}_negInf # handle negInf
+ fucomip %st(1), %st(0) # check for posInf or NaN
+ jc .L${opcode}_nanInf # handle posInf or NaN
+ jmp .L${opcode}_break # do conversion
+%break
+
+.L${opcode}_break:
+ fnstcw -2(%esp) # save control word
+ orl $$0xc00, -2(%esp) # reset control
+ fldcw -2(%esp) # load control word
+ xorl $$0xc00, -2(%esp) # reset control
+ fistpl (rFP, %edx, 4) # move converted int
+ fldcw -2(%esp) # load saved control word
+ FINISH 1 # jump to next instruction
+
+.L${opcode}_nanInf:
+ jnp .L${opcode}_posInf
+ fstps (rFP, %edx, 4)
+ movl $$0x00000000, (rFP, %edx, 4) # vA<- NaN
+ FINISH 1 # jump to next instruction
+
+.L${opcode}_posInf:
+ fstps (rFP, %edx, 4)
+ movl $$0x7FFFFFFF, (rFP, %edx, 4) # vA<- posInf
+ FINISH 1 # jump to next instruction
+
+.L${opcode}_negInf:
+ fstps (rFP, %edx, 4)
+ fstps (rFP, %edx, 4)
+ movl $$0x80000000, (rFP, %edx, 4) # vA<- negInf
+ FINISH 1 # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_DOUBLE_TO_LONG.S b/vm/mterp/x86-atom/OP_DOUBLE_TO_LONG.S
new file mode 100644
index 0000000..2ce9bcc
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_DOUBLE_TO_LONG.S
@@ -0,0 +1,71 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_DOUBLE_TO_LONG.S
+ *
+ * Code: Converts a double to a long. Uses no substitutions.
+ *
+ * For: double-to-long
+ *
+ * Description: Convert the double in source register to a long
+ * and store in the destintation register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %ecx<- BA
+ shr $$4, rINST # rINST<- B
+ and $$15, %edx # %ecx<- A
+ fldl (rFP, rINST, 4) # push vB to floating point stack
+ fildll .LvaluePosInfLong # push max int value
+ fildll .LvalueNegInfLong # push min int value
+ fucomip %st(2), %st(0) # check for negInf
+ jae .L${opcode}_negInf # handle negInf
+ fucomip %st(1), %st(0) # check for posInf or NaN
+ jc .L${opcode}_nanInf # handle posInf or NaN
+ jmp .L${opcode}_break # do conversion
+%break
+
+.L${opcode}_break:
+ fnstcw -2(%esp) # save control word
+ orl $$0xc00, -2(%esp) # reset control
+ fldcw -2(%esp) # load control word
+ xorl $$0xc00, -2(%esp) # reset control
+ fistpll (rFP, %edx, 4) # move converted int
+ fldcw -2(%esp) # load saved control word
+ FINISH 1 # jump to next instruction
+
+.L${opcode}_nanInf:
+ jnp .L${opcode}_posInf
+ fstpl (rFP, %edx, 4) # move converted int
+ movq .LvalueNanLong, %xmm0 # %xmm0<- NaN
+ movq %xmm0, (rFP, %edx, 4) # vA<- %xmm0; NaN
+ FINISH 1 # jump to next instruction
+
+.L${opcode}_posInf:
+ fstpl (rFP, %edx, 4) # move converted int
+ movq .LvaluePosInfLong, %xmm0 # %xmm0<- posInf
+ movq %xmm0, (rFP, %edx, 4) # vA<- %xmm0; posInf
+ FINISH 1 # jump to next instruction
+
+.L${opcode}_negInf:
+ fstpl (rFP, %edx, 4) # move converted int
+ movq .LvalueNegInfLong, %xmm0 # %xmm0<- negInf
+ fstpl (rFP, %edx, 4) # move converted int
+ movq %xmm0, (rFP, %edx, 4) # vA<- %xmm0; negInf
+ FINISH 1 # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_EXECUTE_INLINE.S b/vm/mterp/x86-atom/OP_EXECUTE_INLINE.S
new file mode 100644
index 0000000..4f01cef
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_EXECUTE_INLINE.S
@@ -0,0 +1,86 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_EXECUTE_INLINE.S
+ *
+ * Code: Executes a "native inline" instruction. Uses no substitutions.
+ *
+ * For: execute-inline
+ *
+ * Description: Executes a "native inline" instruction. This instruction
+ * is generated by the optimizer.
+ *
+ * Format:
+ *
+ * Syntax: vAA, {vC, vD, vE, vF}, inline@BBBB
+ */
+
+ FETCH 1, %ecx # %ecx<- BBBB
+ movl rGLUE, %eax # %eax<- MterpGlue pointer
+ addl $$offGlue_retval, %eax # %eax<- &glue->retval
+ EXPORT_PC
+ shr $$4, rINST # rINST<- B
+ movl %eax, -8(%esp) # push parameter glue->retval
+ lea -24(%esp), %esp
+ jmp .L${opcode}_continue
+%break
+
+ /*
+ * Extract args, call function.
+ * rINST = #of args (0-4)
+ * %ecx = call index
+ */
+
+.L${opcode}_continue:
+ FETCH 2, %edx # %edx<- FEDC
+ cmp $$1, rINST # determine number of arguments
+ jl 0f # handle zero args
+ je 1f # handle one arg
+ cmp $$3, rINST
+ jl 2f # handle two args
+ je 3f # handle three args
+4:
+ movl %edx, rINST # rINST<- FEDC
+ and $$0xf000, rINST # isolate F
+ shr $$10, rINST
+ movl (rFP, rINST), rINST # rINST<- vF
+ movl rINST, 12(%esp) # push parameter vF
+3:
+ movl %edx, rINST # rINST<- FEDC
+ and $$0x0f00, rINST # isolate E
+ shr $$6, rINST
+ movl (rFP, rINST), rINST # rINST<- vE
+ movl rINST, 8(%esp) # push parameter E
+2:
+ movl %edx, rINST # rINST<- FEDC
+ and $$0x00f0, rINST # isolate D
+ shr $$2, rINST
+ movl (rFP, rINST), rINST # rINST<- vD
+ movl rINST, 4(%esp) # push parameter D
+1:
+ and $$0x000f, %edx # isolate C
+ movl (rFP, %edx, 4), %edx # rINST<- vC
+ movl %edx, (%esp) # push parameter C
+0:
+ shl $$4, %ecx
+ movl $$gDvmInlineOpsTable, %eax # %eax<- address for table of inline operations
+ call *(%eax, %ecx) # call function
+
+ cmp $$0, %eax # check boolean result of inline
+ FFETCH_ADV 3, %eax # %eax<- next instruction hi; fetch, advance
+ lea 24(%esp), %esp # update stack pointer
+ je common_exceptionThrown # handle exception
+ FGETOP_JMP 3, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_EXECUTE_INLINE_RANGE.S b/vm/mterp/x86-atom/OP_EXECUTE_INLINE_RANGE.S
new file mode 100644
index 0000000..cd5a048
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_EXECUTE_INLINE_RANGE.S
@@ -0,0 +1,75 @@
+ /* Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_EXECUTE_INLINE_RANGE.S
+ *
+ * Code: Executes a "native inline" instruction. Uses no substitutions.
+ *
+ * For: execute-inline
+ *
+ * Description: Executes a "native inline" instruction. This instruction
+ * is generated by the optimizer.
+ *
+ * Format:
+ *
+ * Syntax: AA, {vCCCC..v(CCCC+AA-1)}, inline@BBBB
+ */
+
+ FETCH 1, %ecx # %ecx<- BBBB
+ movl rGLUE, %eax # %eax<- MterpGlue pointer
+ addl $$offGlue_retval, %eax # %eax<- &glue->retval
+ EXPORT_PC
+ movl %eax, -8(%esp) # push parameter glue->retval
+ lea -24(%esp), %esp
+ jmp .L${opcode}_continue
+%break
+
+ /*
+ * Extract args, call function.
+ * rINST = #of args (0-4)
+ * %ecx = call index
+ */
+
+.L${opcode}_continue:
+ FETCH 2, %edx # %edx<- FEDC
+ cmp $$1, rINST # determine number of arguments
+ jl 0f # handle zero args
+ je 1f # handle one arg
+ cmp $$3, rINST
+ jl 2f # handle two args
+ je 3f # handle three args
+4:
+ movl 12(rFP, %edx, 4), rINST # rINST<- vF
+ movl rINST, 12(%esp) # push parameter vF
+3:
+ movl 8(rFP, %edx, 4), rINST # rINST<- vE
+ movl rINST, 8(%esp) # push parameter E
+2:
+ movl 4(rFP, %edx, 4), rINST # rINST<- vD
+ movl rINST, 4(%esp) # push parameter D
+1:
+ movl (rFP, %edx, 4), %edx # rINST<- vC
+ movl %edx, (%esp) # push parameter C
+0:
+ shl $$4, %ecx
+ movl $$gDvmInlineOpsTable, %eax # %eax<- address for table of inline operations
+ call *(%eax, %ecx) # call function
+
+ cmp $$0, %eax # check boolean result of inline
+ FFETCH_ADV 3, %eax # %eax<- next instruction hi; fetch, advance
+ lea 24(%esp), %esp # update stack pointer
+ je common_exceptionThrown # handle exception
+ FGETOP_JMP 3, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_FILLED_NEW_ARRAY.S b/vm/mterp/x86-atom/OP_FILLED_NEW_ARRAY.S
new file mode 100644
index 0000000..f804f3e
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_FILLED_NEW_ARRAY.S
@@ -0,0 +1,173 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_FILLED_NEW_ARRAY.S
+ *
+ * Code: Constructs and fills an array with the given data. Provides
+ *
+ * For: float-to-int
+ *
+ * Description: Construct an array of the given type and size,
+ * filling it with the supplied contents. The type
+ * must be an array type. The array's contents
+ * must be single-word. The constructed instance
+ * is stored as a result in the same way that the
+ * method invocation instructions store their results,
+ * so the constructed instance must be moved to a
+ * register with a subsequent move-result-object
+ * instruction.
+ *
+ * Format: B|A|op CCCC G|F|E|D (35c)
+ * AA|op BBBB CCCC (3rc) (range)
+ *
+ * Syntax: [B=5] op {vD, vE, vF, vG, vA}, vtaboff@CCCC
+ * [B=4] op {vD, vE, vF, vG}, vtaboff@CCCC
+ * [B=3] op {vD, vE, vF}, vtaboff@CCCC
+ * [B=2] op {vD, vE}, vtaboff@CCCC
+ * [B=1] op {vD}, vtaboff@CCCC
+ *
+ * op {vCCCC .. vNNNN}, meth@BBBB
+ * op {vCCCC .. vNNNN}, type@BBBB
+ */
+
+%default { "isrange":"0" }
+
+ movl rGLUE, %edx # %edx<- MterpGlue pointer
+ movl offGlue_methodClassDex(%edx), %edx # %edx<- glue->methodClassDex
+ movl offDvmDex_pResClasses(%edx), %edx # %edx<- glue->methodClassDex->pResClasses
+ FETCH 1, %ecx # %ecx<- BBBB
+ EXPORT_PC
+ movl (%edx, %ecx, 4), %eax # %eax<- possibly resolved class
+ cmp $$0, %eax # %eax<- check if already resolved
+ jne .L${opcode}_continue
+ jmp .L${opcode}_break
+%break
+
+.L${opcode}_break:
+ movl $$0, -8(%esp) # push parameter false
+ movl %ecx, -12(%esp) # push parameter BBBB
+ movl rGLUE, %edx # %edx<- MterpGlue pointer
+ movl offGlue_method(%edx), %edx # %edx<- glue->method
+ movl offMethod_clazz(%edx), %edx # %edx<- glue->method->clazz
+ movl %edx, -16(%esp) # push parameter glue->method->clazz
+ lea -16(%esp), %esp
+ call dvmResolveClass # call: (const ClassObject* referrer, u4 classIdx,
+ # bool fromUnverifiedConstant)
+ # return: ClassObject*
+ lea 16(%esp), %esp
+ cmp $$0, %eax # check for null return
+ je common_exceptionThrown # handle exception
+
+ /*
+ * On entry:
+ * %eax holds array class
+ * rINST holds BA or AA
+ */
+
+.L${opcode}_continue:
+ movl offClassObject_descriptor(%eax), %eax # %eax<- arrayClass->descriptor
+ movzbl 1(%eax), %eax # %eax<- descriptor[1]
+ cmpb $$'I', %al # check if array of ints
+ je 1f
+ cmpb $$'L', %al
+ je 1f
+ cmpb $$'[', %al
+ jne .L${opcode}_notimpl # jump to not implemented
+1:
+ movl %eax, sReg0 # save type
+ movl rINST, -12(%esp) # push parameter length
+ movl %eax, -16(%esp) # push parameter descriptor[1]
+ movl $$ALLOC_DONT_TRACK, -8(%esp) # push parameter to allocate flags
+ .if (!$isrange)
+ shrl $$4, -12(%esp) # parameter length is B
+ .endif
+ lea -16(%esp), %esp
+ call dvmAllocPrimitiveArray # call: (char type, size_t length, int allocFlags)
+ # return: ArrayObject*
+ lea 16(%esp), %esp
+ cmp $$0, %eax # check for null return
+ je common_exceptionThrown # handle exception
+
+ FETCH 2, %edx # %edx<- FEDC or CCCC
+ movl rGLUE, %ecx # %ecx<- MterpGlue pointer
+ movl %eax, offGlue_retval(%ecx) # retval<- new array
+ lea offArrayObject_contents(%eax), %eax # %eax<- newArray->contents
+ subl $$1, -12(%esp) # length--; check for negative
+ js 2f # if length was zero, finish
+
+ /*
+ * copy values from registers into the array
+ * %eax=array, %edx=CCCC/FEDC, -12(%esp)=length (from AA or B), rINST=AA/BA
+ */
+
+ .if $isrange
+ lea (rFP, %edx, 4), %ecx # %ecx<- &fpp[CCCC]
+1:
+ movl (%ecx), %edx # %edx<- %ecx++
+ lea 4(%ecx), %ecx # %ecx++
+ movl %edx, (%eax) # *contents<- vX
+ lea 4(%eax), %eax # %eax++; contents++
+ subl $$1, -12(%esp) # length--
+ jns 1b # or continue at 2
+ .else
+ cmp $$4, -12(%esp) # check length
+ jne 1f # has four args
+ and $$15, rINST # rINST<- A
+ GET_VREG rINST # rINST<- vA
+ subl $$1, -12(%esp) # count--
+ movl rINST, 16(%eax) # contents[4]<- vA
+1:
+ movl %edx, %ecx # %ecx<- %edx; ecx for temp
+ andl $$15, %ecx # %ecx<- G/F/E/D
+ GET_VREG %ecx # %ecx<- vG/vF/vE/vD
+ shr $$4, %edx # %edx<- put next reg in low 4
+ subl $$1, -12(%esp) # count--
+ movl %ecx, (%eax) # *contents<- vX
+ lea 4(%eax), %eax # %eax++; contents++
+ jns 1b # or continue at 2
+ .endif
+2:
+ cmpb $$'I', sReg0 # check for int array
+ je 3f
+ movl rGLUE, %ecx # %ecx<- MterpGlue pointer
+ movl offGlue_retval(%ecx), %eax # Object head
+ movl offGlue_cardTable(%ecx), %ecx # card table base
+ shrl $$GC_CARD_SHIFT, %eax # convert to card num
+ movb %cl,(%ecx, %eax) # mark card based on object head
+3:
+ FINISH 3 # jump to next instruction
+
+ /*
+ * Throw an exception to indicate this mode of filled-new-array
+ * has not been implemented.
+ */
+
+.L${opcode}_notimpl:
+ movl $$.LstrInternalError, -8(%esp)
+ movl $$.LstrFilledNewArrayNotImpl, -4(%esp)
+ lea -8(%esp), %esp
+ call dvmThrowException # call: (const char* exceptionDescriptor,
+ # const char* msg)
+ # return: void
+ lea 8(%esp), %esp
+ jmp common_exceptionThrown
+
+.if (!$isrange) # define in one or the other, not both
+.LstrFilledNewArrayNotImpl:
+.asciz "filled-new-array only implemented for 'int'"
+.LstrInternalError:
+.asciz "Ljava/lang/InternalError;"
+.endif
diff --git a/vm/mterp/x86-atom/OP_FILLED_NEW_ARRAY_RANGE.S b/vm/mterp/x86-atom/OP_FILLED_NEW_ARRAY_RANGE.S
new file mode 100644
index 0000000..fd8b0c5
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_FILLED_NEW_ARRAY_RANGE.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_FILLED_NEW_ARRAY_RANGE.S
+ */
+
+%include "x86-atom/OP_FILLED_NEW_ARRAY.S" { "isrange":"1" }
diff --git a/vm/mterp/x86-atom/OP_FILL_ARRAY_DATA.S b/vm/mterp/x86-atom/OP_FILL_ARRAY_DATA.S
new file mode 100644
index 0000000..de808d9
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_FILL_ARRAY_DATA.S
@@ -0,0 +1,46 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_FILL_ARRAY_DATA.S
+ *
+ * Code: Fills an array with given data. Uses no substitutions.
+ *
+ * For: fill-array-data
+ *
+ * Description: Fill the given array with the idicated data. The reference
+ * must be an array of primitives, and the data table must
+ * match it in type and size
+ *
+ * Format: AA|op BBBBlo BBBBhi (31t)
+ *
+ * Syntax: op vAA, +BBBBBBBB
+ */
+
+ FETCH 1, %ecx # %ecx<- BBBBlo
+ FETCH 2, %edx # %edx<- BBBBhi
+ shl $$16, %edx # prepare to create +BBBBBBBB
+ or %ecx, %edx # %edx<- +BBBBBBBB
+ lea (rPC, %edx, 2), %edx # %edx<- PC + +BBBBBBBB; array data location
+ EXPORT_PC
+ push %edx
+ push (rFP, rINST, 4)
+ call dvmInterpHandleFillArrayData # call: (ArrayObject* arrayObject, const u2* arrayData)
+ # return: bool
+ FFETCH_ADV 3, %edx # %edx<- next instruction hi; fetch, advance
+ cmp $$0, %eax
+ lea 8(%esp), %esp
+ je common_exceptionThrown
+ FGETOP_JMP 3, %edx # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_FLOAT_TO_DOUBLE.S b/vm/mterp/x86-atom/OP_FLOAT_TO_DOUBLE.S
new file mode 100644
index 0000000..91866a4
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_FLOAT_TO_DOUBLE.S
@@ -0,0 +1,36 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_FLOAT_TO_DOUBLE.S
+ *
+ * Code: Converts a float to a double. Uses no substitutions.
+ *
+ * For: float-to-double
+ *
+ * Description: Convert the float in source register to a double
+ * and store the result in the destintation register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $$4, rINST # rINST<- B
+ and $$15, %edx # %edx<- A
+ flds (rFP, rINST, 4) # load float
+ fstpl (rFP, %edx, 4) # store double
+ FINISH 1 # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_FLOAT_TO_INT.S b/vm/mterp/x86-atom/OP_FLOAT_TO_INT.S
new file mode 100644
index 0000000..615f187
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_FLOAT_TO_INT.S
@@ -0,0 +1,68 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_FLOAT_TO_INT.S
+ *
+ * Code: Converts a float to a int. Uses no substitutions.
+ *
+ * For: float-to-int
+ *
+ * Description: Convert the float in source register to a int
+ * and store the result in the destintation register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $$4, rINST # rINST<- B
+ and $$15, %edx # %edx<- A
+ flds (rFP, rINST, 4) # push vB to floating point stack
+ fildl .LintMax # push max int value
+ fildl .LintMin # push min int value
+ fucomip %st(2), %st(0) # check for negInf
+ jae .L${opcode}_negInf # handle negInf
+ fucomip %st(1), %st(0) # check for posInf or NaN
+ jc .L${opcode}_nanInf # handle posInf or NaN
+ jmp .L${opcode}_break # do conversion
+%break
+
+.L${opcode}_break:
+ fnstcw -2(%esp) # save control word
+ orl $$0xc00, -2(%esp) # reset control
+ fldcw -2(%esp) # load control word
+ xorl $$0xc00, -2(%esp) # reset control
+ fistpl (rFP, %edx, 4) # move converted int
+ fldcw -2(%esp) # load saved control word
+ FINISH 1 # jump to next instruction
+
+.L${opcode}_nanInf:
+ jnp .L${opcode}_posInf # handle posInf
+ fstps (rFP, %edx, 4) # pop floating point stack
+ movl $$0x00000000, (rFP, %edx, 4) # vA<- NaN
+ FINISH 1 # jump to next instruction
+
+.L${opcode}_posInf:
+ fstps (rFP, %edx, 4) # pop floating point stack
+ movl $$0x7FFFFFFF, (rFP, %edx, 4) # vA<- posInf
+ FINISH 1 # jump to next instruction
+
+.L${opcode}_negInf:
+ fstps (rFP, %edx, 4) # pop floating point stack
+ fstps (rFP, %edx, 4) # pop floating point stack
+ movl $$0x80000000, (rFP, %edx, 4) # vA<- negInf
+ FINISH 1 # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_FLOAT_TO_LONG.S b/vm/mterp/x86-atom/OP_FLOAT_TO_LONG.S
new file mode 100644
index 0000000..9a50b78
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_FLOAT_TO_LONG.S
@@ -0,0 +1,71 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_FLOAT_TO_LONG.S
+ *
+ * Code: Converts a float to a long. Uses no substitutions.
+ *
+ * For: float-to-long
+ *
+ * Description: Convert the float in source register to a long
+ * and store the result in the destintation register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $$4, rINST # rINST<- B
+ and $$15, %edx # %edx<- A
+ flds (rFP, rINST, 4) # push vB to floating point stack
+ fildll .LvaluePosInfLong # push max int value
+ fildll .LvalueNegInfLong # push min int value
+ fucomip %st(2), %st(0) # check for negInf
+ jae .L${opcode}_negInf # handle negInf
+ fucomip %st(1), %st(0) # check for posInf or NaN
+ jc .L${opcode}_nanInf # handle posInf or NaN
+ jmp .L${opcode}_break # do conversion
+%break
+
+.L${opcode}_break:
+ fnstcw -2(%esp) # save control word
+ orl $$0xc00, -2(%esp) # update control
+ fldcw -2(%esp) # load control word
+ xorl $$0xc00, -2(%esp) # reset control
+ fistpll (rFP, %edx, 4) # move converted int
+ fldcw -2(%esp) # load saved control word
+ FINISH 1 # jump to next instruction
+
+.L${opcode}_nanInf:
+ jnp .L${opcode}_posInf
+ fstpl (rFP, %edx, 4) # move converted int
+ movq .LvalueNanLong, %xmm0 # %xmm0<- NaN
+ movq %xmm0, (rFP, %edx, 4) # vA<- %xmm0; NaN
+ FINISH 1 # jump to next instruction
+
+.L${opcode}_posInf:
+ fstpl (rFP, %edx, 4) # move converted int
+ movq .LvaluePosInfLong, %xmm0 # %xmm0<- posInf
+ movq %xmm0, (rFP, %edx, 4) # vA<- %xmm0; posInf
+ FINISH 1 # jump to next instruction
+
+.L${opcode}_negInf:
+ fstpl (rFP, %edx, 4) # move converted int
+ movq .LvalueNegInfLong, %xmm0 # %xmm0<- negInf
+ fstpl (rFP, %edx, 4) # move converted int
+ movq %xmm0, (rFP, %edx, 4) # vA<- %xmm0; negInf
+ FINISH 1 # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_GOTO.S b/vm/mterp/x86-atom/OP_GOTO.S
new file mode 100644
index 0000000..7bd9956
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_GOTO.S
@@ -0,0 +1,36 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_GOTO.S
+ *
+ * Code: Do an unconditional branch. Uses no substitutions.
+ *
+ * For: goto
+ *
+ * Description: Performs an unconditionally jump to the indicated instruction.
+ * The branch uses an 8-bit offset that cannot be zero.
+ *
+ * Format: AA|op (10t)
+ *
+ * Syntax: op +AA
+ */
+
+LOP_GOTO.S:
+
+ movsbl rINSTbl, %edx # %edx<- +AA
+ shl $$1, %edx # %edx is shifted for byte offset
+ js common_periodicChecks_backwardBranch # do check on backwards branch
+ FINISH_RB %edx, %ecx # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_GOTO_16.S b/vm/mterp/x86-atom/OP_GOTO_16.S
new file mode 100644
index 0000000..931d215
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_GOTO_16.S
@@ -0,0 +1,34 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_GOTO_16.S
+ *
+ * Code: Do an unconditional branch. Uses no substitutions.
+ *
+ * For: goto/16
+ *
+ * Description: Performs an unconditionally jump to the indicated instruction.
+ * The branch uses a 16-bit offset that cannot be zero.
+ *
+ * Format: ØØ|op AAAA (20t)
+ *
+ * Syntax: op +AAAA
+ */
+
+ FETCHs 1, %edx # %edx<- ssssAAAA (sign-extended)
+ shl $$1, %edx # %edx is doubled to get the byte offset
+ js common_periodicChecks_backwardBranch # do check on backwards branch
+ FINISH_RB %edx, %ecx # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_GOTO_32.S b/vm/mterp/x86-atom/OP_GOTO_32.S
new file mode 100644
index 0000000..d00c3a4
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_GOTO_32.S
@@ -0,0 +1,37 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_GOTO_32.S
+ *
+ * Code: Do an unconditional branch. Uses no substitutions.
+ *
+ * For: goto/32
+ *
+ * Description: Performs an unconditionally jump to the indicated instruction.
+ * The branch uses a 32-bit offset that can be zero.
+ *
+ * Format: ØØ|op AAAAlo AAAAhi (30t)
+ *
+ * Syntax: op +AAAAAAAA
+ */
+
+ FETCH 1, %edx # %edx<- AAAAlo
+ FETCH 2, %ecx # %ecx<- AAAAhi
+ shl $$16, %ecx # prepare to create +AAAAAAAA
+ or %ecx, %edx # %edx<- +AAAAAAAA
+ shl $$1, %edx # %edx is doubled to get the byte offset
+ jle common_periodicChecks_backwardBranch # do check on backwards branch
+ FINISH_RB %edx, %ecx # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_IF_EQ.S b/vm/mterp/x86-atom/OP_IF_EQ.S
new file mode 100644
index 0000000..61781a0
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IF_EQ.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IF_EQ.S
+ */
+
+%include "x86-atom/bincmp.S" { "revcmp":"ne" }
diff --git a/vm/mterp/x86-atom/OP_IF_EQZ.S b/vm/mterp/x86-atom/OP_IF_EQZ.S
new file mode 100644
index 0000000..2f7c140
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IF_EQZ.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IF_EQZ.S
+ */
+
+%include "x86-atom/zcmp.S" { "revcmp":"ne" }
diff --git a/vm/mterp/x86-atom/OP_IF_GE.S b/vm/mterp/x86-atom/OP_IF_GE.S
new file mode 100644
index 0000000..e90a1e5
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IF_GE.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IF_GE.S
+ */
+
+%include "x86-atom/bincmp.S" { "revcmp":"l" }
diff --git a/vm/mterp/x86-atom/OP_IF_GEZ.S b/vm/mterp/x86-atom/OP_IF_GEZ.S
new file mode 100644
index 0000000..8ee71a8
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IF_GEZ.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IF_GEZ.S
+ */
+
+%include "x86-atom/zcmp.S" { "revcmp":"l" }
diff --git a/vm/mterp/x86-atom/OP_IF_GT.S b/vm/mterp/x86-atom/OP_IF_GT.S
new file mode 100644
index 0000000..7f19db9
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IF_GT.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IF_GT.S
+ */
+
+%include "x86-atom/bincmp.S" { "revcmp":"le" }
diff --git a/vm/mterp/x86-atom/OP_IF_GTZ.S b/vm/mterp/x86-atom/OP_IF_GTZ.S
new file mode 100644
index 0000000..3f8039f
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IF_GTZ.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IF_GTZ.S
+ */
+
+%include "x86-atom/zcmp.S" { "revcmp":"le" }
diff --git a/vm/mterp/x86-atom/OP_IF_LE.S b/vm/mterp/x86-atom/OP_IF_LE.S
new file mode 100644
index 0000000..287bd0d
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IF_LE.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IF_LE.S
+ */
+
+%include "x86-atom/bincmp.S" { "revcmp":"g" }
diff --git a/vm/mterp/x86-atom/OP_IF_LEZ.S b/vm/mterp/x86-atom/OP_IF_LEZ.S
new file mode 100644
index 0000000..b7d31d1
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IF_LEZ.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IF_LEZ.S
+ */
+
+%include "x86-atom/zcmp.S" { "revcmp":"g" }
diff --git a/vm/mterp/x86-atom/OP_IF_LT.S b/vm/mterp/x86-atom/OP_IF_LT.S
new file mode 100644
index 0000000..7e58e18
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IF_LT.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IF_LT.S
+ */
+
+%include "x86-atom/bincmp.S" { "revcmp":"ge" }
diff --git a/vm/mterp/x86-atom/OP_IF_LTZ.S b/vm/mterp/x86-atom/OP_IF_LTZ.S
new file mode 100644
index 0000000..0a3e56b
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IF_LTZ.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IF_LTZ.S
+ */
+
+%include "x86-atom/zcmp.S" { "revcmp":"ge" }
diff --git a/vm/mterp/x86-atom/OP_IF_NE.S b/vm/mterp/x86-atom/OP_IF_NE.S
new file mode 100644
index 0000000..929bd05
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IF_NE.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IF_NE.S
+ */
+
+%include "x86-atom/bincmp.S" { "revcmp":"e" }
diff --git a/vm/mterp/x86-atom/OP_IF_NEZ.S b/vm/mterp/x86-atom/OP_IF_NEZ.S
new file mode 100644
index 0000000..07f2c87
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IF_NEZ.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IF_NEZ.S
+ */
+
+%include "x86-atom/zcmp.S" { "revcmp":"e" }
diff --git a/vm/mterp/x86-atom/OP_IGET.S b/vm/mterp/x86-atom/OP_IGET.S
new file mode 100644
index 0000000..e3a72f7
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IGET.S
@@ -0,0 +1,80 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IGET.S
+ *
+ * Code: Generic 32-bit instance field "get" operation. Provides a
+ * "mov" variable which determines the type of mov performed.
+ * Currently, none of the iget's use this variable - may want
+ * to change this, but seems ok for now.
+ *
+ * For: iget-boolean, iget-byte, iget-char, iget-object, iget
+ * iget-short
+ *
+ * Description: Perform the object instance field "get" operation
+ * with the identified field; load the instance value into
+ * the value register.
+ *
+ *
+ * Format: B|A|op CCCC (22c)
+ *
+ * Syntax: op vA, vB, type@CCCC
+ * op vA, vB, field@CCCC
+ */
+
+%default { "mov":"l" }
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_methodClassDex(%edx), %edx # %edx<- pDvmDex
+ FETCH 1, %ecx # %ecx<- CCCC
+ movl offDvmDex_pResFields(%edx), %edx # %edx<- pDvmDex->pResFields
+ cmp $$0, (%edx, %ecx, 4) # check for null ptr; resolved InstField ptr
+ movl (%edx, %ecx, 4), %eax # %eax<- resolved InstField ptr
+ jne .L${opcode}_finish2
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_method(%edx), %edx # %edx <- current method
+ EXPORT_PC # in case an exception is thrown
+ movl offMethod_clazz(%edx), %edx # %edx<- method->clazz
+ movl %ecx, -4(%esp) # push parameter CCCC; field ref
+ movl %edx, -8(%esp) # push parameter method->clazz
+ lea -8(%esp), %esp
+ jmp .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+ call dvmResolveInstField # call: (const ClassObject* referrer, u4 ifieldIdx)
+ # return: InstField*
+ cmp $$0, %eax # check if resolved
+ lea 8(%esp), %esp
+ je common_exceptionThrown # not resolved; handle exception
+
+ /*
+ * %eax holds resolved field
+ */
+
+.L${opcode}_finish2:
+ movl rINST, %ecx # %ecx<- BA
+ shr $$4, %ecx # %ecx<- B
+ and $$15, rINST # rINST<- A
+
+ GET_VREG %ecx # %ecx<- vB
+ cmp $$0, %ecx # check for null object
+ je common_errNullObject # handle null object
+ movl offInstField_byteOffset(%eax), %edx # %edx<- field offset
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ mov$mov (%ecx, %edx), %edx # %edx<- object field
+ SET_VREG %edx, rINST # vA<- %edx; object field
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_IGET_BOOLEAN.S b/vm/mterp/x86-atom/OP_IGET_BOOLEAN.S
new file mode 100644
index 0000000..12100f9
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IGET_BOOLEAN.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IGET_BOOLEAN.S
+ */
+
+%include "x86-atom/OP_IGET.S"
diff --git a/vm/mterp/x86-atom/OP_IGET_BYTE.S b/vm/mterp/x86-atom/OP_IGET_BYTE.S
new file mode 100644
index 0000000..6d6b870
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IGET_BYTE.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IGET_BYTE.S
+ */
+
+%include "x86-atom/OP_IGET.S"
diff --git a/vm/mterp/x86-atom/OP_IGET_CHAR.S b/vm/mterp/x86-atom/OP_IGET_CHAR.S
new file mode 100644
index 0000000..8f285d7
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IGET_CHAR.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IGET_CHAR.S
+ */
+
+%include "x86-atom/OP_IGET.S"
diff --git a/vm/mterp/x86-atom/OP_IGET_OBJECT.S b/vm/mterp/x86-atom/OP_IGET_OBJECT.S
new file mode 100644
index 0000000..369e1b9
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IGET_OBJECT.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IGET_OBJECT.S
+ */
+
+%include "x86-atom/OP_IGET.S"
diff --git a/vm/mterp/x86-atom/OP_IGET_OBJECT_QUICK.S b/vm/mterp/x86-atom/OP_IGET_OBJECT_QUICK.S
new file mode 100644
index 0000000..36b7f0e
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IGET_OBJECT_QUICK.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IGET_OBJECT_QUICK.S
+ */
+
+%include "x86-atom/OP_IGET_QUICK.S"
diff --git a/vm/mterp/x86-atom/OP_IGET_OBJECT_VOLATILE.S b/vm/mterp/x86-atom/OP_IGET_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..5de2fa3
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IGET_OBJECT_VOLATILE.S
@@ -0,0 +1 @@
+%include "x86/OP_IGET.S"
diff --git a/vm/mterp/x86-atom/OP_IGET_QUICK.S b/vm/mterp/x86-atom/OP_IGET_QUICK.S
new file mode 100644
index 0000000..8ec86ec
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IGET_QUICK.S
@@ -0,0 +1,38 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IGET_QUICK.S
+ *
+ * Code: Optimization for iget
+ *
+ * For: iget-quick
+ *
+ * Format: B|A|op CCCC (22c)
+ *
+ * Syntax: op vA, vB, offset@CCCC
+ */
+
+ movl rINST, %eax # %eax<- BA
+ shr $$4, %eax # %eax<- B
+ and $$15, rINST # rINST<- A
+ GET_VREG %eax # %eax<- vB; object to operate on
+ FETCH 1, %ecx # %ecx<- CCCC; field byte offset
+ cmp $$0, %eax # check if object is null
+ je common_errNullObject # handle null object
+ FFETCH_ADV 2, %edx # %eax<- next instruction hi; fetch, advance
+ movl (%ecx, %eax), %eax # %eax<- object field
+ SET_VREG %eax, rINST # fp[A]<- %eax
+ FGETOP_JMP 2, %edx # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_IGET_SHORT.S b/vm/mterp/x86-atom/OP_IGET_SHORT.S
new file mode 100644
index 0000000..968b815
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IGET_SHORT.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IGET_SHORT.S
+ */
+
+%include "x86-atom/OP_IGET.S"
diff --git a/vm/mterp/x86-atom/OP_IGET_VOLATILE.S b/vm/mterp/x86-atom/OP_IGET_VOLATILE.S
new file mode 100644
index 0000000..5de2fa3
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IGET_VOLATILE.S
@@ -0,0 +1 @@
+%include "x86/OP_IGET.S"
diff --git a/vm/mterp/x86-atom/OP_IGET_WIDE.S b/vm/mterp/x86-atom/OP_IGET_WIDE.S
new file mode 100644
index 0000000..370b0b0
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IGET_WIDE.S
@@ -0,0 +1,74 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IGET_WIDE.S
+ *
+ * Code: 64 bit instance field "get" operation. Uses no substitutions.
+ *
+ * For: iget-wide
+ *
+ * Description: Perform the object instance field "get" operation
+ * with the identified field; load the instance value into
+ * the value register.
+ *
+ * Format: B|A|op CCCC (22c)
+ *
+ * Syntax: op vA, vB, type@CCCC
+ * op vA, vB, field@CCCC
+ */
+
+ movl rGLUE, %eax # %eax<- MterpGlue pointer
+ movl offGlue_methodClassDex(%eax), %ecx # %ecx<- pDvmDex
+ movl offDvmDex_pResFields(%ecx), %ecx # %ecx<- CCCC
+ FETCH 1, %edx # %edx<- pDvmDex->pResFields
+ movl (%ecx, %edx, 4), %ecx # %ecx<- resolved InstField ptr
+ cmp $$0, %ecx # check for null ptr; resolved InstField ptr
+ jne .L${opcode}_finish
+ movl offGlue_method(%eax), %ecx # %ecx <- current method
+ EXPORT_PC # in case an exception is thrown
+ movl offMethod_clazz(%ecx), %ecx # %ecx<- method->clazz
+ movl %ecx, -8(%esp) # push parameter CCCC; field ref
+ movl %edx, -4(%esp) # push parameter method->clazz
+ jmp .L${opcode}_finish2
+%break
+
+.L${opcode}_finish2:
+ lea -8(%esp), %esp
+ call dvmResolveInstField # resolve InstField ptr
+ # call: (const ClassObject* referrer, u4 ifieldIdx)
+ # return: InstField*
+ cmp $$0, %eax # check if resolved
+ lea 8(%esp), %esp
+ movl %eax, %ecx # %ecx<- %eax; %ecx expected to hold field
+ je common_exceptionThrown
+
+ /*
+ * %ecx holds resolved field
+ */
+
+.L${opcode}_finish:
+
+ movl rINST, %edx # %edx<- BA
+ shr $$4, %edx # %edx<- B
+ andl $$15, rINST # rINST<- A
+ GET_VREG %edx # %edx<- vB
+ cmp $$0, %edx # check for null object
+ je common_errNullObject
+ movl offInstField_byteOffset(%ecx), %ecx # %ecx<- field offset
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movq (%ecx, %edx), %xmm0 # %xmm0<- object field
+ movq %xmm0, (rFP, rINST, 4) # vA<- %xmm0; object field
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_IGET_WIDE_QUICK.S b/vm/mterp/x86-atom/OP_IGET_WIDE_QUICK.S
new file mode 100644
index 0000000..08a57f6
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IGET_WIDE_QUICK.S
@@ -0,0 +1,38 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IGET_WIDE_QUICK.S
+ *
+ * Code: Optimization for iget
+ *
+ * For: iget/wide-quick
+ *
+ * Format: B|A|op CCCC (22c)
+ *
+ * Syntax: op vA, vB, offset@CCCC
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $$4, %edx # %edx<- B
+ andl $$15, rINST # rINST<- A
+ GET_VREG %edx # %edx<- vB; object to operate on
+ cmp $$0, %edx # check if object is null
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ je common_errNullObject # handle null object
+ FETCH 1, %ecx # %ecx<- CCCC; field byte offset
+ movq (%ecx, %edx), %xmm0 # %xmm0<- object field
+ movq %xmm0, (rFP, rINST, 4) # fp[A]<- %xmm0
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_INSTANCE_OF.S b/vm/mterp/x86-atom/OP_INSTANCE_OF.S
new file mode 100644
index 0000000..4dde31c
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INSTANCE_OF.S
@@ -0,0 +1,121 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INSTANCE_OF.S
+ *
+ * Code: Checks if object is instance of a class. Uses no substitutions.
+ *
+ * For: instance-of
+ *
+ * Description: Store in the given destination register 1 if the indicated
+ * reference is an instance of the given type, or 0 if not.
+ * The type must be a reference type (not a primitive type).
+ *
+ * Format: B|A|op CCCC (22c)
+ *
+ * Syntax: op vA, vB, type@CCCC
+ * op vA, vB, field@CCCC
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $$4, %edx # %edx<- B
+ GET_VREG %edx # %edx<- vB
+ cmp $$0, %edx # check for null object
+ je .L${opcode}_store # null object
+ jmp .L${opcode}_break
+%break
+
+.L${opcode}_break:
+ movl rGLUE, %ecx # %ecx<- pMterpGlue
+ movl offGlue_methodClassDex(%ecx), %ecx # %ecx<- pDvmDex
+ FETCH 1, %eax # %eax<- CCCC
+ movl offDvmDex_pResClasses(%ecx), %ecx # %ecx<- pDvmDex->pResClasses
+ movl (%ecx, %eax, 4), %ecx # %ecx<- resolved class
+ movl offObject_clazz(%edx), %edx # %edx<- obj->clazz
+ cmp $$0, %ecx # check if already resovled
+ je .L${opcode}_resolve # not resolved before, so resolve now
+
+.L${opcode}_resolved:
+ cmp %ecx, %edx # check if same class
+ je .L${opcode}_trivial # yes, finish
+ jmp .L${opcode}_fullcheck # no, do full check
+
+ /*
+ * The trivial test failed, we need to perform a full check.
+ * %edx holds obj->clazz
+ * %ecx holds class resolved from BBBB
+ */
+
+.L${opcode}_fullcheck:
+ movl %edx, -8(%esp) # push parameter obj->clazz
+ movl %ecx, -4(%esp) # push parameter resolved class
+ lea -8(%esp), %esp
+ call dvmInstanceofNonTrivial # perform full check
+ # call: (ClassObject* instance, ClassObject* clazz)
+ # return: int
+ andl $$15, rINST # rINST<- A
+ FFETCH_ADV 2, %edx # %edx<- next instruction hi; fetch, advance
+ lea 8(%esp), %esp
+ SET_VREG %eax, rINST # vA<- r0
+ FGETOP_JMP 2, %edx # jump to next instruction; getop, jmp
+
+ /*
+ * %edx holds boolean result
+ */
+
+.L${opcode}_store:
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ andl $$15, rINST # rINST<- A
+ SET_VREG %edx, rINST # vA<- r0
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+ /*
+ * Trivial test succeeded, save and bail.
+ */
+
+.L${opcode}_trivial:
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ andl $$15, rINST # rINST<- A
+ SET_VREG $$1, rINST # vA<- r0
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+
+ /*
+ * Resolution required. This is the least-likely path.
+ * %eax holds BBBB
+ */
+
+.L${opcode}_resolve:
+
+ movl rGLUE, %ecx # %ecx<- pMterpGlue
+ EXPORT_PC
+ movl offGlue_method(%ecx), %ecx # %ecx<- glue->method
+ movl offMethod_clazz(%ecx), %ecx # %ecx<- glue->method->clazz
+ movl %ecx, -12(%esp) # push parameter glue->method->clazz
+ movl %eax, -8(%esp) # push parameter CCCC; type index
+ movl $$1, -4(%esp) # push parameter true
+ lea -12(%esp), %esp
+ call dvmResolveClass # call: (const ClassObject* referrer, u4 classIdx,
+ # bool fromUnverifiedConstant)
+ # return: ClassObject*
+ lea 12(%esp), %esp
+ cmp $$0, %eax # check for null
+ je common_exceptionThrown # handle exception
+ movl rINST, %edx # %edx<- BA+
+ shr $$4, %edx # %edx<- B
+ movl %eax, %ecx # need class in %ecx
+ GET_VREG %edx # %edx<- vB
+ movl offObject_clazz(%edx), %edx # %edx<- obj->clazz
+ jmp .L${opcode}_resolved # clazz resolved, continue
diff --git a/vm/mterp/x86-atom/OP_INT_TO_BYTE.S b/vm/mterp/x86-atom/OP_INT_TO_BYTE.S
new file mode 100644
index 0000000..27cafe9
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INT_TO_BYTE.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INT_TO_BYTE.S
+ */
+
+%include "x86-atom/unop.S" { "preinstr":"sal $24, %ecx", "instr":"sar $24, %ecx" }
diff --git a/vm/mterp/x86-atom/OP_INT_TO_CHAR.S b/vm/mterp/x86-atom/OP_INT_TO_CHAR.S
new file mode 100644
index 0000000..a28602d
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INT_TO_CHAR.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INT_TO_CHAR.S
+ */
+
+%include "x86-atom/unop.S" {"preinstr":"sal $16, %ecx", "instr":"shr $16, %ecx" }
diff --git a/vm/mterp/x86-atom/OP_INT_TO_DOUBLE.S b/vm/mterp/x86-atom/OP_INT_TO_DOUBLE.S
new file mode 100644
index 0000000..cd16eea
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INT_TO_DOUBLE.S
@@ -0,0 +1,37 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INT_TO_DOUBLE.S
+ *
+ * Code: Convert an int to a double. Uses no substitutions.
+ *
+ * For: int-to-double
+ *
+ * Description: Converts an int in the source register, to a double, and
+ * stores the result in the destination register. vA<- (double) vB
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %eax # %eax<- BA+
+ shr $$4, %eax # %eax<- B
+ andl $$15, rINST # rINST<- A
+ cvtsi2sd (rFP, %eax, 4), %xmm0 # %xmm0<- vB
+ movq %xmm0, (rFP, rINST, 4) # vA<- %xmm0; (double) vB
+ FFETCH_ADV 1, %edx # %edx<- next instruction hi; fetch, advance
+ FGETOP_JMP 1, %edx # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_INT_TO_FLOAT.S b/vm/mterp/x86-atom/OP_INT_TO_FLOAT.S
new file mode 100644
index 0000000..52ce729
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INT_TO_FLOAT.S
@@ -0,0 +1,36 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INT_TO_FLOAT.S
+ *
+ * Code: Convert an int to a float. Uses no substitutions.
+ *
+ * For: int-to-float
+ *
+ * Description: Convert an int in the source register, to a float, and
+ * stores the result in the destintation register. vA<- (float) vB
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %eax # %eax<- BA+
+ shr $$4, %eax # %eax<- B
+ andl $$15, rINST # rINST<- A
+ cvtsi2ss (rFP,%eax,4), %xmm0 # %xmm0<- vB
+ movss %xmm0, (rFP, rINST, 4) # vA<- %xmm0
+ FINISH 1 # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_INT_TO_LONG.S b/vm/mterp/x86-atom/OP_INT_TO_LONG.S
new file mode 100644
index 0000000..1bc125b
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INT_TO_LONG.S
@@ -0,0 +1,40 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INT_TO_LONG.S
+ *
+ * Code: Convert an int to a long. Uses no substitutions.
+ *
+ * For:
+ *
+ * Description: Convert an int in the source register, to a long, and
+ * stores the result in the destintation register. vA<- (long) vB
+ *
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %eax # %eax<- BA+
+ movl rINST, %ecx # %ecx<- BA+
+ shr $$4, %eax # %eax<- B
+ andl $$15, %ecx # %ecx<- A
+ GET_VREG %eax # %eax<- vB
+ cdq # %edx:%eax<- sign-extend of %eax
+ movl %eax, (rFP, %ecx, 4) # vA<- lo part
+ movl %edx, 4(rFP, %ecx, 4) # vA+1<- hi part
+ FINISH 1 # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_INT_TO_SHORT.S b/vm/mterp/x86-atom/OP_INT_TO_SHORT.S
new file mode 100644
index 0000000..f2b0b87
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INT_TO_SHORT.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INT_TO_SHORT.S
+ */
+
+%include "x86-atom/unop.S" { "preinstr":"sal $16, %ecx", "instr":"sar $16, %ecx" }
diff --git a/vm/mterp/x86-atom/OP_INVOKE_DIRECT.S b/vm/mterp/x86-atom/OP_INVOKE_DIRECT.S
new file mode 100644
index 0000000..78b6c06
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INVOKE_DIRECT.S
@@ -0,0 +1,92 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INVOKE_DIRECT.S
+ *
+ * Code: Call a non-static direct method. Provides an "isrange" variable and
+ * a "routine" variable to specify this is the "range" version of
+ * invoke_direct that allows up to 255 arguments.
+ *
+ * For: invoke-direct, invoke-direct/range
+ *
+ * Description: invoke-direct is used to invoke a non-static direct method;
+ * an instance method that is non-overridable, for example,
+ * either a private instance method or a constructor.
+ *
+ * Format: B|A|op CCCC G|F|E|D (35c)
+ * AA|op BBBB CCCC (3rc)
+ *
+ * Syntax: [B=5] op {vD, vE, vF, vG, vA}, meth@CCCC (35c)
+ * [B=5] op {vD, vE, vF, vG, vA}, type@CCCC (35c)
+ * [B=4] op {vD, vE, vF, vG}, kind@CCCC (35c)
+ * [B=3] op {vD, vE, vF}, kind@CCCC (35c)
+ * [B=2] op {vD, vE}, kind@CCCC (35c)
+ * [B=1] op {vD}, kind@CCCC (35c)
+ * [B=0] op {}, kind@CCCC (35c)
+ *
+ * op {vCCCC .. vNNNN}, meth@BBBB (3rc) (where NNNN = CCCC+AA-1, that
+ * op {vCCCC .. vNNNN}, type@BBBB (3rc) is A determines the count 0..255,
+ * and C determines the first register)
+ */
+
+%default { "isrange":"0", "routine":"NoRange" }
+
+ movl rGLUE, %ecx # %ecx<- pMterpGlue
+ movl offGlue_methodClassDex(%ecx), %ecx # %ecx<- pDvmDex
+ FETCH 1, %eax # %eax<- method index
+ movl offDvmDex_pResMethods(%ecx), %ecx # %ecx<- pDvmDex->pResMethods
+ FETCH 2, %edx # %edx<- GFED or CCCC
+ movl (%ecx, %eax, 4), %ecx # %ecx<- resolved method to call
+ .if (!$isrange)
+ andl $$15, %edx # %edx<- D if not range
+ .endif
+ EXPORT_PC # must export for invoke
+ movl %edx, -4(%esp) # save "this" pointer register
+ cmp $$0, %ecx # check if already resolved
+ GET_VREG %edx # %edx<- "this" pointer
+ je .L${opcode}_resolve # handle resolve
+
+.L${opcode}_finish:
+ cmp $$0, %edx # check for null "this"
+ jne common_invokeMethod${routine} # invoke method common code
+ jmp common_errNullObject
+%break
+
+ /*
+ * %eax = reference (BBBB or CCCC)
+ * -4(%esp) = "this" register
+ */
+
+.L${opcode}_resolve:
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl $$METHOD_DIRECT, -8(%esp) # push parameter method type
+ movl offGlue_method(%edx), %edx # %edx<- glue->method
+ movl %eax, -12(%esp) # push parameter reference
+ lea -16(%esp), %esp
+ movl offMethod_clazz(%edx), %edx # %edx<- glue->method->clazz
+ movl %edx, (%esp) # push parameter clazz
+ call dvmResolveMethod # call: (const ClassObject* referrer,
+ # u4 methodIdx, MethodType methodType)
+ # return: Method*
+ lea 16(%esp), %esp
+ cmp $$0, %eax # check for null method return
+ movl -4(%esp), %edx # get "this" pointer register
+ GET_VREG %edx # get "this" pointer
+ je common_exceptionThrown # null pointer; handle exception
+ cmp $$0, %edx # check for null "this"
+ movl %eax, %ecx # %ecx<- method
+ jne common_invokeMethod${routine} # invoke method common code
+ jmp common_errNullObject # handle null object
diff --git a/vm/mterp/x86-atom/OP_INVOKE_DIRECT_EMPTY.S b/vm/mterp/x86-atom/OP_INVOKE_DIRECT_EMPTY.S
new file mode 100644
index 0000000..85c0418
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INVOKE_DIRECT_EMPTY.S
@@ -0,0 +1,26 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INVOKE_DIRECT_EMPTY.S
+ *
+ * Code: Used as a no-op. Uses no substitutions.
+ *
+ * For: invoke-direct-empty
+ *
+ * Format: B|A|op CCCC G|F|E|D (35c)
+ */
+
+ FINISH 3
diff --git a/vm/mterp/x86-atom/OP_INVOKE_DIRECT_RANGE.S b/vm/mterp/x86-atom/OP_INVOKE_DIRECT_RANGE.S
new file mode 100644
index 0000000..3ad26e1
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INVOKE_DIRECT_RANGE.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INVOKE_DIRECT_RANGE.S
+ */
+
+%include "x86-atom/OP_INVOKE_DIRECT.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/x86-atom/OP_INVOKE_INTERFACE.S b/vm/mterp/x86-atom/OP_INVOKE_INTERFACE.S
new file mode 100644
index 0000000..cbd7b31
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INVOKE_INTERFACE.S
@@ -0,0 +1,76 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INVOKE_INTERFACE.S
+ *
+ * Code: Call at method. Provides an "isrange" variable and
+ * a "routine" variable to specify this is the "range" version of
+ * invoke_interface that allows up to 255 arguments.
+ *
+ * For: invoke-interface, invoke-interface-range
+ *
+ * Description: invoke-interface is used to invoke an interface method; on an
+ * object whose concrete class isn't known, using a method_id that
+ * refers to an interface.
+ *
+ * Format: B|A|op CCCC G|F|E|D (35c)
+ * AA|op BBBB CCCC (3rc)
+ *
+ * Syntax: [B=5] op {vD, vE, vF, vG, vA}, meth@CCCC (35c)
+ * [B=5] op {vD, vE, vF, vG, vA}, type@CCCC (35c)
+ * [B=4] op {vD, vE, vF, vG}, kind@CCCC (35c)
+ * [B=3] op {vD, vE, vF}, kind@CCCC (35c)
+ * [B=2] op {vD, vE}, kind@CCCC (35c)
+ * [B=1] op {vD}, kind@CCCC (35c)
+ * [B=0] op {}, kind@CCCC (35c)
+ *
+ * op {vCCCC .. vNNNN}, meth@BBBB (3rc) (where NNNN = CCCC+AA-1, that
+ * op {vCCCC .. vNNNN}, type@BBBB (3rc) is A determines the count 0..255,
+ * and C determines the first register)
+ */
+
+%default { "isrange":"0", "routine":"NoRange" }
+
+ FETCH 2, %edx # %edx<- GFED or CCCC
+ FETCH 1, %ecx # %ecx<- method index
+ movl %ecx, -12(%esp) # push argument method index
+ .if (!$isrange)
+ and $$15, %edx # %edx<- D if not range
+ .endif
+ EXPORT_PC # must export for invoke
+ GET_VREG %edx # %edx<- first arg "this pointer"
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ movl offGlue_methodClassDex(%eax), %eax # %eax<- glue->pDvmDex
+ movl %eax, -4(%esp) # push parameter class
+ cmp $$0, %edx # check for null object
+ je common_errNullObject # handle null object
+ jmp .L${opcode}_break
+%break
+.L${opcode}_break:
+ movl rGLUE, %ecx # %ecx<- pMterpGlue
+ movl offGlue_method(%ecx), %ecx # %ecx<- glue->method
+ movl %ecx, -8(%esp) # push parameter method
+ movl offObject_clazz(%edx), %edx # %edx<- glue->method->clazz
+ movl %edx, -16(%esp) # push parameter
+ lea -16(%esp), %esp
+ call dvmFindInterfaceMethodInCache # call: (ClassObject* thisClass, u4 methodIdx,
+ # const Method* method, DvmDex* methodClassDex)
+ # return: Method*
+ lea 16(%esp), %esp
+ cmp $$0, %eax # check if find failed
+ je common_exceptionThrown # handle exception
+ movl %eax, %ecx # %ecx<- method
+ jmp common_invokeMethod${routine} # invoke method common code
diff --git a/vm/mterp/x86-atom/OP_INVOKE_INTERFACE_RANGE.S b/vm/mterp/x86-atom/OP_INVOKE_INTERFACE_RANGE.S
new file mode 100644
index 0000000..b323ba0
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INVOKE_INTERFACE_RANGE.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INVOKE_INTERFACE_RANGE.S
+ */
+
+%include "x86-atom/OP_INVOKE_INTERFACE.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/x86-atom/OP_INVOKE_STATIC.S b/vm/mterp/x86-atom/OP_INVOKE_STATIC.S
new file mode 100644
index 0000000..30b6d8c
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INVOKE_STATIC.S
@@ -0,0 +1,70 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INVOKE_STATIC.S
+ *
+ * Code: Call static direct method. Provides an "isrange" variable and
+ * a "routine" variable to specify this is the "range" version of
+ * invoke_static that allows up to 255 arguments.
+ *
+ * For: invoke-static, invoke-static/range
+ *
+ * Description: invoke-static is used to invoke static direct method.
+ *
+ * Format: B|A|op CCCC G|F|E|D (35c)
+ * AA|op BBBB CCCC (3rc)
+ *
+ * Syntax: [B=5] op {vD, vE, vF, vG, vA}, meth@CCCC (35c)
+ * [B=5] op {vD, vE, vF, vG, vA}, type@CCCC (35c)
+ * [B=4] op {vD, vE, vF, vG}, kind@CCCC (35c)
+ * [B=3] op {vD, vE, vF}, kind@CCCC (35c)
+ * [B=2] op {vD, vE}, kind@CCCC (35c)
+ * [B=1] op {vD}, kind@CCCC (35c)
+ * [B=0] op {}, kind@CCCC (35c)
+ *
+ * op {vCCCC .. vNNNN}, meth@BBBB (3rc) (where NNNN = CCCC+AA-1, that
+ * op {vCCCC .. vNNNN}, type@BBBB (3rc) is A determines the count 0..255,
+ * and C determines the first register)
+ */
+
+%default { "routine":"NoRange" }
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_methodClassDex(%edx), %ecx # %edx<- pDvmDex
+ FETCH 1, %eax # %eax<- method index
+ movl offDvmDex_pResMethods(%ecx), %ecx # %edx<- pDvmDex->pResMethods
+ movl (%ecx, %eax, 4), %ecx # %ecx<- resolved method to call
+ cmp $$0, %ecx # check if already resolved
+ EXPORT_PC # must export for invoke
+ jne common_invokeMethod${routine} # invoke method common code
+ jmp .L${opcode}_break
+%break
+
+.L${opcode}_break:
+ movl offGlue_method(%edx), %edx # %edx<- glue->method
+ movl $$METHOD_STATIC, -4(%esp) # resolver method type
+ movl %eax, -8(%esp) # push parameter method index
+ movl offMethod_clazz(%edx), %edx # %edx<- glue->method->clazz
+ movl %edx, -12(%esp) # push parameter method
+ lea -12(%esp), %esp
+ call dvmResolveMethod # call: (const ClassObject* referrer,
+ # u4 methodIdx, MethodType methodType)
+ # return: Method*
+ lea 12(%esp), %esp
+ cmp $$0, %eax # check for null method
+ je common_exceptionThrown
+ movl %eax, %ecx # %ecx<- method
+ jmp common_invokeMethod${routine} # invoke method common code
diff --git a/vm/mterp/x86-atom/OP_INVOKE_STATIC_RANGE.S b/vm/mterp/x86-atom/OP_INVOKE_STATIC_RANGE.S
new file mode 100644
index 0000000..ce39e13
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INVOKE_STATIC_RANGE.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INVOKE_STATIC_RANGE.S
+ */
+
+%include "x86-atom/OP_INVOKE_STATIC.S" { "routine":"Range" }
diff --git a/vm/mterp/x86-atom/OP_INVOKE_SUPER.S b/vm/mterp/x86-atom/OP_INVOKE_SUPER.S
new file mode 100644
index 0000000..539bea1
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INVOKE_SUPER.S
@@ -0,0 +1,105 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INVOKE_SUPER.S
+ *
+ * Code: Call super method.
+ *
+ * For: invoke-super, invoke-super/range
+ *
+ * Description: invoke-super is used to invoke the closest superclass's virtual
+ * method (as opposed to the one with the same method_id in the
+ * calling class).
+ *
+ * Format: B|A|op CCCC G|F|E|D (35c)
+ * AA|op BBBB CCCC (3rc)
+ *
+ * Syntax: [B=5] op {vD, vE, vF, vG, vA}, meth@CCCC (35c)
+ * [B=5] op {vD, vE, vF, vG, vA}, type@CCCC (35c)
+ * [B=4] op {vD, vE, vF, vG}, kind@CCCC (35c)
+ * [B=3] op {vD, vE, vF}, kind@CCCC (35c)
+ * [B=2] op {vD, vE}, kind@CCCC (35c)
+ * [B=1] op {vD}, kind@CCCC (35c)
+ * [B=0] op {}, kind@CCCC (35c)
+ *
+ * op {vCCCC .. vNNNN}, meth@BBBB (3rc) (where NNNN = CCCC+AA-1, that
+ * op {vCCCC .. vNNNN}, type@BBBB (3rc) is A determines the count 0..255,
+ * and C determines the first register)
+ */
+
+%default { "isrange":"0", "routine":"NoRange" }
+
+ movl rGLUE, %ecx # %ecx<- pMterpGlue
+ FETCH 2, %eax # %eax<- GFED or CCCC
+ movl offGlue_methodClassDex(%ecx), %ecx # %ecx<- pDvmDex
+ .if (!$isrange)
+ and $$15, %eax # %eax<- D if not range
+ .endif
+ FETCH 1, %edx # %edx<- method index
+ movl offDvmDex_pResMethods(%ecx), %ecx # %ecx<- pDvmDex->pResMethods
+ cmp $$0, (rFP, %eax, 4) # check for null object
+ movl (%ecx, %edx, 4), %ecx # %ecx<- resolved base method
+ je common_errNullObject # handle null object
+ jmp .L${opcode}_continue2
+%break
+
+.L${opcode}_continue2:
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ movl offGlue_method(%eax), %eax # %eax<- glue->method
+ movl offMethod_clazz(%eax), %eax # %eax<- glue->method->clazz
+ EXPORT_PC # must export for invoke
+ cmp $$0, %ecx # check if already resolved
+ jne .L${opcode}_continue
+ jmp .L${opcode}_resolve # handle resolve
+
+ /*
+ * %ecx = resolved base method
+ * %eax = method->clazz
+ */
+
+.L${opcode}_continue:
+ movl offClassObject_super(%eax), %edx # %edx<- glue->method->clazz->super
+ movzwl offMethod_methodIndex(%ecx), %ecx # %ecx<- baseMethod->methodIndex
+ cmp offClassObject_vtableCount(%edx), %ecx # compare vtableCount with methodIndex
+ EXPORT_PC # must export for invoke
+ jnc .L${opcode}_nsm # handle method not present
+ movl offClassObject_vtable(%edx), %edx # %edx<- glue->method->clazz->super->vtable
+ movl (%edx, %ecx, 4), %ecx # %ecx<- vtable[methodIndex]
+ jmp common_invokeMethod${routine} # invoke method common code
+
+.L${opcode}_resolve:
+ movl %eax, -12(%esp) # push parameter clazz
+ movl %edx, -8(%esp) # push parameter method index
+ movl $$METHOD_VIRTUAL, -4(%esp) # push parameter method type
+ lea -12(%esp), %esp
+ call dvmResolveMethod # call: (const ClassObject* referrer,
+ # u4 methodIdx, MethodType methodType)
+ # return: Method*
+ lea 12(%esp), %esp
+ movl %eax, %ecx # %ecx<- method
+ cmp $$0, %ecx # check for null method return
+ movl -12(%esp), %eax # %eax<- glue->method->clazz
+ jne .L${opcode}_continue
+ jmp common_exceptionThrown # null pointer; handle exception
+
+ /*
+ * Throw a NoSuchMethodError with the method name as the message.
+ * %ecx = resolved base method
+ */
+
+.L${opcode}_nsm:
+ movl offMethod_name(%ecx), %edx # %edx<- method name
+ jmp common_errNoSuchMethod
diff --git a/vm/mterp/x86-atom/OP_INVOKE_SUPER_QUICK.S b/vm/mterp/x86-atom/OP_INVOKE_SUPER_QUICK.S
new file mode 100644
index 0000000..55c7e94
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INVOKE_SUPER_QUICK.S
@@ -0,0 +1,40 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INVOKE_SUPER_QUICK.S
+ *
+ * Code: Optimization for invoke-super and invoke-super/range
+ *
+ * For: invoke-super/quick, invoke-super/quick-range
+ */
+
+%default { "isrange":"0", "routine":"NoRange" }
+
+ FETCH 2, %edx # %edx<- GFED or CCCC
+ movl rGLUE, %ecx # %ecx<- pMterpGlue
+ movl offGlue_method(%ecx), %eax # %eax<- glue->method
+ .if (!$isrange)
+ and $$15, %edx # %edx<- D if not range
+ .endif
+ FETCH 1, %ecx # %ecx<- method index
+ movl offMethod_clazz(%eax), %eax # %eax<- glue->method->clazz
+ movl offClassObject_super(%eax), %eax # %eax<- glue->method->clazz->super
+ EXPORT_PC # must export for invoke
+ movl offClassObject_vtable(%eax), %eax # %edx<- glue->method->clazz->super->vtable
+ cmp $$0, (rFP, %edx, 4) # check for null object
+ movl (%eax, %ecx, 4), %ecx # %ecx<- vtable[methodIndex]
+ je common_errNullObject # handle null object
+ jmp common_invokeMethod${routine} # invoke method common code
diff --git a/vm/mterp/x86-atom/OP_INVOKE_SUPER_QUICK_RANGE.S b/vm/mterp/x86-atom/OP_INVOKE_SUPER_QUICK_RANGE.S
new file mode 100644
index 0000000..9e9f311
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INVOKE_SUPER_QUICK_RANGE.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INVOKE_SUPER_QUICK_RANGE.S
+ */
+
+%include "x86-atom/OP_INVOKE_SUPER_QUICK.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/x86-atom/OP_INVOKE_SUPER_RANGE.S b/vm/mterp/x86-atom/OP_INVOKE_SUPER_RANGE.S
new file mode 100644
index 0000000..6e77c02
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INVOKE_SUPER_RANGE.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INVOKE_SUPER_RANGE.S
+ */
+
+%include "x86-atom/OP_INVOKE_SUPER.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/x86-atom/OP_INVOKE_VIRTUAL.S b/vm/mterp/x86-atom/OP_INVOKE_VIRTUAL.S
new file mode 100644
index 0000000..46c9265
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INVOKE_VIRTUAL.S
@@ -0,0 +1,93 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INVOKE_VIRTUAL.S
+ *
+ * Code: Call a virtual method. Provides an "isrange" variable and
+ * a "routine" variable to specify this is the "range" version of
+ * invoke_direct that allows up to 255 arguments.
+ *
+ * For: invoke-virtual, invoke-virtual/range
+ *
+ * Description: invoke-virtual is used to invoke a normal virtual method;
+ * a method that is not static or final, and is not a constructor.
+ *
+ * Format: B|A|op CCCC G|F|E|D (35c)
+ * AA|op BBBB CCCC (3rc)
+ *
+ * Syntax: [B=5] op {vD, vE, vF, vG, vA}, meth@CCCC (35c)
+ * [B=5] op {vD, vE, vF, vG, vA}, type@CCCC (35c)
+ * [B=4] op {vD, vE, vF, vG}, kind@CCCC (35c)
+ * [B=3] op {vD, vE, vF}, kind@CCCC (35c)
+ * [B=2] op {vD, vE}, kind@CCCC (35c)
+ * [B=1] op {vD}, kind@CCCC (35c)
+ * [B=0] op {}, kind@CCCC (35c)
+ *
+ * op {vCCCC .. vNNNN}, meth@BBBB (3rc) (where NNNN = CCCC+AA-1, that
+ * op {vCCCC .. vNNNN}, type@BBBB (3rc) is A determines the count 0..255,
+ * and C determines the first register)
+ */
+
+%default { "isrange":"0", "routine":"NoRange" }
+
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ EXPORT_PC # must export pc for invoke
+ movl offGlue_methodClassDex(%eax), %eax # %eax<- pDvmDex
+ FETCH 1, %ecx # %ecx<- method index
+ movl offDvmDex_pResMethods(%eax), %eax # %eax<- pDvmDex->pResMethods
+ FETCH 2, %edx # %edx<- GFED or CCCC
+ .if (!$isrange)
+ and $$15, %edx # %edx<- D if not range
+ .endif
+ cmp $$0, (%eax, %ecx, 4) # check if already resolved
+ je .L${opcode}_break
+ movl (%eax, %ecx, 4), %eax # %eax<- resolved base method
+ jmp .L${opcode}_continue
+%break
+
+.L${opcode}_break:
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ movl %edx, -4(%esp) # save "this" pointer register
+ movl offGlue_method(%eax), %eax # %eax<- glue->method
+ movl $$METHOD_VIRTUAL, -8(%esp) # push parameter method type
+ movl %ecx, -12(%esp) # push paramter method index
+ movl offMethod_clazz(%eax), %eax # %eax<- glue->method->clazz
+ lea -16(%esp), %esp
+ movl %eax, (%esp) # push parameter clazz
+ call dvmResolveMethod # call: (const ClassObject* referrer,
+ # u4 methodIdx, MethodType methodType)
+ # return: Method*
+ lea 16(%esp), %esp
+ cmp $$0, %eax # check for null method return
+ movl -4(%esp), %edx # get "this" pointer register
+ jne .L${opcode}_continue
+ jmp common_exceptionThrown # null pointer; handle exception
+
+ /*
+ * At this point:
+ * %eax = resolved base method
+ * %edx = D or CCCC (index of first arg, which is the "this" ptr)
+ */
+
+.L${opcode}_continue:
+ GET_VREG %edx # %edx<- "this" ptr
+ movzwl offMethod_methodIndex(%eax), %eax # %eax<- baseMethod->methodIndex
+ cmp $$0, %edx # %edx<- check for null "this"
+ je common_errNullObject # handle null object
+ movl offObject_clazz(%edx), %edx # %edx<- thisPtr->clazz
+ movl offClassObject_vtable(%edx), %edx # %edx<- thisPtr->clazz->vtable
+ movl (%edx, %eax, 4), %ecx # %ecx<- vtable[methodIndex]
+ jmp common_invokeMethod${routine} # invoke method common code
diff --git a/vm/mterp/x86-atom/OP_INVOKE_VIRTUAL_QUICK.S b/vm/mterp/x86-atom/OP_INVOKE_VIRTUAL_QUICK.S
new file mode 100644
index 0000000..16a4e40
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INVOKE_VIRTUAL_QUICK.S
@@ -0,0 +1,38 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INVOKE_VIRTUAL_QUICK.S
+ *
+ * Code: Optimization for invoke-virtual and invoke-virtual/range
+ *
+ * For: invoke-virtual/quick, invoke-virtual/quick-range
+ */
+
+%default { "isrange":"0", "routine":"NoRange" }
+
+ FETCH 2, %edx # %edx<- GFED or CCCC
+ .if (!$isrange)
+ and $$15, %edx # %edx<- D if not range
+ .endif
+ FETCH 1, %ecx # %ecx<- method index
+ GET_VREG %edx # %edx<- "this" ptr
+ cmp $$0, %edx # %edx<- check for null "this"
+ EXPORT_PC # must export pc for invoke
+ je common_errNullObject
+ movl offObject_clazz(%edx), %edx # %edx<- thisPtr->clazz
+ movl offClassObject_vtable(%edx), %edx # %edx<- thisPtr->clazz->vtable
+ movl (%edx, %ecx, 4), %ecx # %ecx<- vtable[methodIndex]
+ jmp common_invokeMethod${routine} # invoke method common code
diff --git a/vm/mterp/x86-atom/OP_INVOKE_VIRTUAL_QUICK_RANGE.S b/vm/mterp/x86-atom/OP_INVOKE_VIRTUAL_QUICK_RANGE.S
new file mode 100644
index 0000000..888bcc0
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INVOKE_VIRTUAL_QUICK_RANGE.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INVOKE_VIRTUAL_QUICK_RANGE.S
+ */
+
+%include "x86-atom/OP_INVOKE_VIRTUAL_QUICK.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/x86-atom/OP_INVOKE_VIRTUAL_RANGE.S b/vm/mterp/x86-atom/OP_INVOKE_VIRTUAL_RANGE.S
new file mode 100644
index 0000000..d548a22
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_INVOKE_VIRTUAL_RANGE.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_INVOKE_VIRTUAL_RANGE.S
+ */
+
+%include "x86-atom/OP_INVOKE_VIRTUAL.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/x86-atom/OP_IPUT.S b/vm/mterp/x86-atom/OP_IPUT.S
new file mode 100644
index 0000000..4c029be
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IPUT.S
@@ -0,0 +1,76 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IPUT.S
+ *
+ * Code: Generic 32-bit instance field "put" operation. Provides a
+ * "mov" variable which determines the type of mov performed.
+ * Currently, none of the iput's use this variable - may want
+ * to change this, but seems ok for now.
+ *
+ * For: iput-boolean, iput-byte, iput-char, iput-object, iput
+ * iput-short
+ *
+ * Description: Perform the object instance field "get" operation
+ * with the identified field; load the instance value into
+ * the value register.
+ *
+ *
+ * Format: B|A|op CCCC (22c)
+ *
+ * Syntax: op vA, vB, type@CCCC
+ * op vA, vB, field@CCCC
+ */
+
+%default { "mov":"l" }
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_methodClassDex(%edx), %edx # %edx<- pDvmDex
+ FETCH 1, %ecx # %ecx<- CCCC
+ movl offDvmDex_pResFields(%edx), %edx # %edx<- pDvmDex->pResFields
+ cmp $$0, (%edx, %ecx, 4) # check for null ptr; resolved InstField ptr
+ movl (%edx, %ecx, 4), %eax # %eax<- resolved InstField ptr
+ jne .L${opcode}_finish2
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ jmp .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+ movl offGlue_method(%edx), %edx # %edx<- glue->method
+ EXPORT_PC # in case an exception is thrown
+ movl %ecx, -4(%esp) # push parameter CCCC; field ref
+ movl offMethod_clazz(%edx), %edx # %edx<- method->clazz
+ lea -8(%esp), %esp
+ movl %edx, (%esp) # push parameter method->clazz
+ call dvmResolveInstField # call: (const ClassObject* referrer, u4 ifieldIdx)
+ # return: InstField*
+ lea 8(%esp), %esp
+ cmp $$0, %eax # check if resolved
+ jne .L${opcode}_finish2
+ jmp common_exceptionThrown # not resolved; handle exception
+
+.L${opcode}_finish2:
+ movl rINST, %ecx # %ecx<- BA+
+ shr $$4, %ecx # %ecx<- B
+ and $$15, rINST # rINST<- A
+ GET_VREG %ecx # %ecx<- vB
+ cmp $$0, %ecx # check for null object
+ je common_errNullObject # handle null object
+ movl offInstField_byteOffset(%eax), %edx # %edx<- field offset
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG rINST # rINST<- vA
+ mov$mov rINST, (%edx, %ecx) # object field<- vA
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_IPUT_BOOLEAN.S b/vm/mterp/x86-atom/OP_IPUT_BOOLEAN.S
new file mode 100644
index 0000000..46c2932
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IPUT_BOOLEAN.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IPUT_BOOLEAN.S
+ */
+
+%include "x86-atom/OP_IPUT.S"
diff --git a/vm/mterp/x86-atom/OP_IPUT_BYTE.S b/vm/mterp/x86-atom/OP_IPUT_BYTE.S
new file mode 100644
index 0000000..d23f492
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IPUT_BYTE.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IPUT_BYTE.S
+ */
+
+%include "x86-atom/OP_IPUT.S"
diff --git a/vm/mterp/x86-atom/OP_IPUT_CHAR.S b/vm/mterp/x86-atom/OP_IPUT_CHAR.S
new file mode 100644
index 0000000..d645fae
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IPUT_CHAR.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IPUT_CHAR.S
+ */
+
+%include "x86-atom/OP_IPUT.S"
diff --git a/vm/mterp/x86-atom/OP_IPUT_OBJECT.S b/vm/mterp/x86-atom/OP_IPUT_OBJECT.S
new file mode 100644
index 0000000..302cf44
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IPUT_OBJECT.S
@@ -0,0 +1,81 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IPUT.S
+ *
+ * Code: Generic 32-bit instance field "put" operation. Provides a
+ * "mov" variable which determines the type of mov performed.
+ * Currently, none of the iput's use this variable - may want
+ * to change this, but seems ok for now.
+ *
+ * For: iput-boolean, iput-byte, iput-char, iput-object, iput
+ * iput-short
+ *
+ * Description: Perform the object instance field "get" operation
+ * with the identified field; load the instance value into
+ * the value register.
+ *
+ *
+ * Format: B|A|op CCCC (22c)
+ *
+ * Syntax: op vA, vB, type@CCCC
+ * op vA, vB, field@CCCC
+ */
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_methodClassDex(%edx), %edx # %edx<- pDvmDex
+ FETCH 1, %ecx # %ecx<- CCCC
+ movl offDvmDex_pResFields(%edx), %edx # %edx<- pDvmDex->pResFields
+ cmp $$0, (%edx, %ecx, 4) # check for null ptr; resolved InstField ptr
+ movl (%edx, %ecx, 4), %eax # %eax<- resolved InstField ptr
+ jne .L${opcode}_finish2
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ jmp .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+ movl offGlue_method(%edx), %edx # %edx<- glue->method
+ EXPORT_PC # in case an exception is thrown
+ movl %ecx, -4(%esp) # push parameter CCCC; field ref
+ movl offMethod_clazz(%edx), %edx # %edx<- method->clazz
+ lea -8(%esp), %esp
+ movl %edx, (%esp) # push parameter method->clazz
+ call dvmResolveInstField # call: (const ClassObject* referrer, u4 ifieldIdx)
+ # return: InstField*
+ lea 8(%esp), %esp
+ cmp $$0, %eax # check if resolved
+ jne .L${opcode}_finish2
+ jmp common_exceptionThrown # not resolved; handle exception
+
+.L${opcode}_finish2:
+ movl rINST, %ecx # %ecx<- BA+
+ shr $$4, %ecx # %ecx<- B
+ and $$15, rINST # rINST<- A
+ GET_VREG %ecx # %ecx<- vB
+ cmp $$0, %ecx # check for null object
+ je common_errNullObject # handle null object
+ movl offInstField_byteOffset(%eax), %edx # %edx<- field offset
+ GET_VREG rINST # rINST<- vA
+ movl rINST, (%edx, %ecx) # object field<- vA
+ FFETCH_ADV 2, %edx # %edx<- next instruction hi; fetch, advance
+ movl rGLUE, %eax # get glue
+ movl offGlue_cardTable(%eax), %eax # get card table base
+ testl rINST, rINST # test if we stored a null value
+ je 1f # skip card mark if null stored
+ shrl $$GC_CARD_SHIFT, %ecx # set obeject head to card number
+ movb %al, (%eax, %ecx)
+1:
+ FGETOP_JMP 2, %edx # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_IPUT_OBJECT_QUICK.S b/vm/mterp/x86-atom/OP_IPUT_OBJECT_QUICK.S
new file mode 100644
index 0000000..eee88e9
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IPUT_OBJECT_QUICK.S
@@ -0,0 +1,44 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IPUT_QUICK.S
+ * Code: Optimization for iput
+ *
+ * For: iput-quick
+ *
+ * Format: B|A|op CCCC (22c)
+ *
+ * Syntax: op vA, vB, offset@CCCC
+ */
+
+ movl rINST, %eax # %eax<- BA
+ shr $$4, %eax # %eax<- B
+ and $$15, rINST # rINST<- A
+ GET_VREG %eax # %eax<- vB; object to operate on
+ FETCH 1, %ecx # %ecx<- CCCC; field byte offset
+ cmp $$0, %eax # check if object is null
+ je common_errNullObject # handle null object
+ FFETCH_ADV 2, %edx # %edx<- next instruction hi; fetch, advance
+ GET_VREG rINST # rINST<- vA
+ movl rINST, (%eax, %ecx) # object field<- vA
+ testl rINST, rINST # did we write a null object
+ je 1f
+ movl rGLUE, %ecx # get glue
+ movl offGlue_cardTable(%ecx), %ecx # get card table base
+ shrl $$GC_CARD_SHIFT, %eax # get gc card index
+ movb %cl, (%eax, %ecx) # mark gc card in table
+1:
+ FGETOP_JMP 2, %edx # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_IPUT_OBJECT_VOLATILE.S b/vm/mterp/x86-atom/OP_IPUT_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..4b024d0
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IPUT_OBJECT_VOLATILE.S
@@ -0,0 +1 @@
+%include "x86/OP_IPUT_OBJECT.S"
diff --git a/vm/mterp/x86-atom/OP_IPUT_QUICK.S b/vm/mterp/x86-atom/OP_IPUT_QUICK.S
new file mode 100644
index 0000000..572291e
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IPUT_QUICK.S
@@ -0,0 +1,37 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IPUT_QUICK.S
+ * Code: Optimization for iput
+ *
+ * For: iput-quick
+ *
+ * Format: B|A|op CCCC (22c)
+ *
+ * Syntax: op vA, vB, offset@CCCC
+ */
+
+ movl rINST, %eax # %eax<- BA
+ shr $$4, %eax # %eax<- B
+ and $$15, rINST # rINST<- A
+ GET_VREG %eax # %eax<- vB; object to operate on
+ FETCH 1, %ecx # %ecx<- CCCC; field byte offset
+ cmp $$0, %eax # check if object is null
+ je common_errNullObject # handle null object
+ FFETCH_ADV 2, %edx # %edx<- next instruction hi; fetch, advance
+ GET_VREG rINST # rINST<- vA
+ movl rINST, (%eax, %ecx) # object field<- vA
+ FGETOP_JMP 2, %edx # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_IPUT_SHORT.S b/vm/mterp/x86-atom/OP_IPUT_SHORT.S
new file mode 100644
index 0000000..9836283
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IPUT_SHORT.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IPUT_SHORT.S
+ */
+
+%include "x86-atom/OP_IPUT.S"
diff --git a/vm/mterp/x86-atom/OP_IPUT_VOLATILE.S b/vm/mterp/x86-atom/OP_IPUT_VOLATILE.S
new file mode 100644
index 0000000..475a0c5
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IPUT_VOLATILE.S
@@ -0,0 +1 @@
+%include "x86/OP_IPUT.S"
diff --git a/vm/mterp/x86-atom/OP_IPUT_WIDE.S b/vm/mterp/x86-atom/OP_IPUT_WIDE.S
new file mode 100644
index 0000000..1686219
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IPUT_WIDE.S
@@ -0,0 +1,74 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IPUT_WIDE.S
+ *
+ * Code: 64 bit instance field "put" operation. Uses no substitutions.
+ *
+ * For: iget-wide
+ *
+ * Description: Perform the object instance field "put" operation
+ * with the identified field; load the instance value into
+ * the value register.
+ *
+ * Format: B|A|op CCCC (22c)
+ *
+ * Syntax: op vA, vB, type@CCCC
+ * op vA, vB, field@CCCC
+ */
+
+ movl rGLUE, %eax # %eax<- MterpGlue pointer
+ movl offGlue_methodClassDex(%eax), %ecx # %ecx<- pDvmDex
+ movl offDvmDex_pResFields(%ecx), %ecx # %ecx<- CCCC
+ FETCH 1, %edx # %edx<- pDvmDex->pResFields
+ movl (%ecx, %edx, 4), %ecx # %ecx<- resolved InstField ptr
+ cmp $$0, %ecx # check for null ptr; resolved InstField ptr
+ jne .L${opcode}_finish
+ movl offGlue_method(%eax), %ecx # %ecx <- current method
+ EXPORT_PC # in case an exception is thrown
+ movl offMethod_clazz(%ecx), %ecx # %ecx<- method->clazz
+ movl %ecx, -8(%esp) # push parameter CCCC; field ref
+ movl %edx, -4(%esp) # push parameter method->clazz
+ jmp .L${opcode}_finish2
+%break
+
+.L${opcode}_finish2:
+ lea -8(%esp), %esp
+ call dvmResolveInstField # resolve InstField ptr
+ cmp $$0, %eax # check if resolved
+ lea 8(%esp), %esp
+ movl %eax, %ecx # %ecx<- %eax; %ecx expected to hold field
+ jne .L${opcode}_finish
+ jmp common_exceptionThrown
+
+ /*
+ * Currently:
+ * %ecx holds resolved field
+ * %edx does not hold object yet
+ */
+
+.L${opcode}_finish:
+ movl rINST, %edx # %edx<- BA
+ shr $$4, %edx # %edx<- B
+ andl $$15, rINST # rINST<- A
+ GET_VREG %edx # %edx<- vB
+ cmp $$0, %edx # check for null object
+ je common_errNullObject
+ movl offInstField_byteOffset(%ecx), %ecx # %ecx<- field offset
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movq (rFP, rINST, 4), %xmm0 # %xmm0<- vA
+ movq %xmm0, (%ecx, %edx) # object field<- %xmm0; vA
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_IPUT_WIDE_QUICK.S b/vm/mterp/x86-atom/OP_IPUT_WIDE_QUICK.S
new file mode 100644
index 0000000..5880231
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_IPUT_WIDE_QUICK.S
@@ -0,0 +1,38 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_IPUT_WIDE_QUICK.S
+ *
+ * Code: Optimization for iput
+ *
+ * For: iput/wide-quick
+ *
+ * Format: B|A|op CCCC (22c)
+ *
+ * Syntax: op vA, vB, offset@CCCC
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $$4, %edx # %edx<- B
+ andl $$15, rINST # rINST<- A
+ GET_VREG %edx # %edx<- vB; object to operate on
+ cmp $$0, %edx # check if object is null
+ FETCH 1, %ecx # %ecx<- CCCC; field byte offset
+ je common_errNullObject # handle null object
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movq (rFP, rINST, 4), %xmm0 # %xmm0<- fp[A]
+ movq %xmm0, (%edx, %ecx) # object field<- %xmm0; fp[A]
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_LONG_TO_DOUBLE.S b/vm/mterp/x86-atom/OP_LONG_TO_DOUBLE.S
new file mode 100644
index 0000000..5705e25
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_LONG_TO_DOUBLE.S
@@ -0,0 +1,37 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_LONG_TO_DOUBLE.S
+ *
+ * Code: Convert a long to a dobule. Uses no substitutions.
+ *
+ * For: long-to-double
+ *
+ * Description: Converts a long in the source register to a double, and
+ * stores the result in the destination register. vA<- (double) vB
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %ecx # %ecx<- BA+
+ shr $$4, rINST # rINST<- B
+ and $$15, %ecx # %ecx<- A
+ FFETCH_ADV 1, %eax # %eax<- next instruction hi; fetch, advance
+ fildll (rFP, rINST, 4) # FPU<- vB
+ fstpl (rFP, %ecx, 4) # vA<- FPU; (double) vB
+ FGETOP_JMP 1, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_LONG_TO_FLOAT.S b/vm/mterp/x86-atom/OP_LONG_TO_FLOAT.S
new file mode 100644
index 0000000..1bb8779
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_LONG_TO_FLOAT.S
@@ -0,0 +1,37 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_LONG_TO_FLOAT.S
+ *
+ * Code: Convert a long to a float. Uses no substitutions.
+ *
+ * For: int-to-float
+ *
+ * Description: Converts a float in the source register, to a float, and
+ * stores the result in the destination register. vA<- (double) vB
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %ecx # %ecx<- BA+
+ shr $$4, rINST # rINST<- B
+ and $$15, %ecx # %ecx<- A
+ FFETCH_ADV 1, %eax # %eax<- next instruction hi; fetch, advance
+ fildll (rFP, rINST, 4) # FPU<- vB
+ fstps (rFP, %ecx, 4) # vA<- FPU; (float) vB
+ FGETOP_JMP 1, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_LONG_TO_INT.S b/vm/mterp/x86-atom/OP_LONG_TO_INT.S
new file mode 100644
index 0000000..0984bc0
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_LONG_TO_INT.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_LONG_TO_INT.S
+ */
+
+%include "x86-atom/OP_MOVE.S"
diff --git a/vm/mterp/x86-atom/OP_MONITOR_ENTER.S b/vm/mterp/x86-atom/OP_MONITOR_ENTER.S
new file mode 100644
index 0000000..d3fada3
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MONITOR_ENTER.S
@@ -0,0 +1,58 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MONITOR_ENTER.S
+ *
+ * Code: Aquire a monitor
+ *
+ * For: monitor-enter
+ *
+ * Description: Aquire a monitor for the indicated object.
+ *
+ *
+ *
+ * Format: AA|op (11x)
+ *
+ * Syntax: op vAA
+ */
+
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ GET_VREG rINST # rINST<- vAA
+ cmp $$0, rINST # check for null object
+ movl offGlue_self(%eax), %eax # %eax<- glue->self
+#ifdef WITH_MONITOR_TRACKING
+ EXPORT_PC # export PC so we can grab stack trace
+#endif
+ je common_errNullObject # handle null object
+# jmp .L${opcode}_finish
+#%break
+#.L${opcode}_finish:
+ movl rINST, -4(%esp) # push parameter reference
+ movl %eax, -8(%esp) # push parameter
+ lea -8(%esp), %esp
+ call dvmLockObject # call: (struct Thread* self,
+ # struct Object* obj)
+ # return: void
+ FFETCH_ADV 1, %edx # %edx<- next instruction hi; fetch, advance
+ lea 8(%esp), %esp
+#ifdef WITH_DEADLOCK_PREDICTION
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ movl offGlue_self(%eax), %eax # %eax<- glue->self
+ movl offThread_exception(%eax), %eax # %eax<- glue->self->exception
+ cmp $$0, %eax # check for exception
+ jne common_exceptionThrown # handle exception
+#endif
+ FGETOP_JMP 1, %edx # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_MONITOR_EXIT.S b/vm/mterp/x86-atom/OP_MONITOR_EXIT.S
new file mode 100644
index 0000000..37738d5
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MONITOR_EXIT.S
@@ -0,0 +1,46 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MONITOR_EXIT.S
+ *
+ * Code: Release a monitor
+ *
+ * For: monitor-exit
+ *
+ * Description: Release a monitor for the indicated object. If this instruction needs
+ * to throw an execption, it must do so as if the pc has already
+ * advanced pased the instruction.
+ *
+ * Format: AA|op (11x)
+ *
+ * Syntax: op vAA
+ */
+
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ EXPORT_PC # export the pc
+ GET_VREG rINST # rINST<- vAA
+ cmp $$0, rINST # rINST<- check for null object
+ je common_errNullObject # handle null object
+ push rINST # push parameter object
+ push offGlue_self(%eax) # push parameter self
+ call dvmUnlockObject # call: (struct Thread* self,
+ # struct Object* obj)
+ # return: bool
+ FINISH_FETCH_ADVANCE 1, %edx # advance pc before exception
+ cmp $$0, %eax # check for success
+ lea 8(%esp), %esp
+ je common_exceptionThrown # handle exception
+ FINISH_JMP %edx # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_MOVE.S b/vm/mterp/x86-atom/OP_MOVE.S
new file mode 100644
index 0000000..9982ced
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MOVE.S
@@ -0,0 +1,38 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MOVE.S
+ *
+ * Code: Copies contents from one register to another. Uses no
+ * substitutions.
+ *
+ * For: move, move-object, long-to-int
+ *
+ * Description: Copies contents from one non-object register to another.
+ * vA<- vB; fp[A]<- fp[B]
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %ecx # %ecx<- BA
+ shr $$4, rINST # rINST<- B
+ and $$15, %ecx # %ecx<- A
+ FFETCH_ADV 1, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG rINST # rINST<- vB
+ SET_VREG rINST, %ecx # vA<- vB; %edx
+ FGETOP_JMP 1, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_MOVE_16.S b/vm/mterp/x86-atom/OP_MOVE_16.S
new file mode 100644
index 0000000..013a11b
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MOVE_16.S
@@ -0,0 +1,36 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MOVE_16.S
+ *
+ * Code: Copies contents from one register to another
+ *
+ * For: move/16, move-object/16
+ *
+ * Description: Copies contents from one non-object register to another.
+ * fp[A]<- fp[B]
+ *
+ * Format: ØØ|op AAAA BBBB (32x)
+ *
+ * Syntax: op vAAAA, vBBBB
+ */
+
+ FETCH 2, %edx # %edx<- BBBB
+ FETCH 1, %ecx # %ecx<- AAAA
+ FFETCH_ADV 3, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %edx # %edx<- vB
+ SET_VREG %edx, %ecx # vA<- vB; %edx
+ FGETOP_JMP 3, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_MOVE_EXCEPTION.S b/vm/mterp/x86-atom/OP_MOVE_EXCEPTION.S
new file mode 100644
index 0000000..76e700d
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MOVE_EXCEPTION.S
@@ -0,0 +1,38 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MOVE_EXCEPTION.S
+ *
+ * Code: Moves an exception to a register
+ *
+ * For: move-exception
+ *
+ * Description: Save a just-caught exception into the given register. This
+ * instruction is only valid as the first instruction of an
+ * exception handler.
+ *
+ * Format: AA|op (11x)
+ *
+ * Syntax: op vAA
+ */
+
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ movl offGlue_self(%eax), %ecx # %ecx<- glue->self
+ FFETCH_ADV 1, %eax # %eax<- next instruction hi; fetch, advance
+ movl offThread_exception(%ecx), %edx # %edx<- glue->self->exception
+ movl $$0, offThread_exception(%ecx) # clear exception
+ SET_VREG %edx, rINST # vAA<- glue->self->exception
+ FGETOP_JMP 1, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_MOVE_FROM16.S b/vm/mterp/x86-atom/OP_MOVE_FROM16.S
new file mode 100644
index 0000000..55a6e96
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MOVE_FROM16.S
@@ -0,0 +1,35 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MOVE_FROM16.S
+ *
+ * Code: Copies contents from one register to another
+ *
+ * For: move/from16, move-object/from16
+ *
+ * Description: Copies contents from one non-object register to another.
+ * vA<- vB; fp[A]<- fp[B]
+ *
+ * Format: AA|op BBBB (22x)
+ *
+ * Syntax: op vAA, vBBBB
+ */
+
+ FETCH 1, %edx # %edx<- BBBB
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %edx # %edx<- vB
+ SET_VREG %edx, rINST # vA<- vB; %edx
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_MOVE_OBJECT.S b/vm/mterp/x86-atom/OP_MOVE_OBJECT.S
new file mode 100644
index 0000000..1cd22cb
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MOVE_OBJECT.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MOVE_OBJECT.S
+ */
+
+%include "x86-atom/OP_MOVE.S"
diff --git a/vm/mterp/x86-atom/OP_MOVE_OBJECT_16.S b/vm/mterp/x86-atom/OP_MOVE_OBJECT_16.S
new file mode 100644
index 0000000..a61162c
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MOVE_OBJECT_16.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MOVE_OBJECT_16.S
+ */
+
+%include "x86-atom/OP_MOVE_16.S"
diff --git a/vm/mterp/x86-atom/OP_MOVE_OBJECT_FROM16.S b/vm/mterp/x86-atom/OP_MOVE_OBJECT_FROM16.S
new file mode 100644
index 0000000..bfca7da
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MOVE_OBJECT_FROM16.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MOVE_OBJECT_FROM16.S
+ */
+
+%include "x86-atom/OP_MOVE_FROM16.S"
diff --git a/vm/mterp/x86-atom/OP_MOVE_RESULT.S b/vm/mterp/x86-atom/OP_MOVE_RESULT.S
new file mode 100644
index 0000000..1d13bf5
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MOVE_RESULT.S
@@ -0,0 +1,38 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MOVE_RESULT.S
+ *
+ * Code: Copies a return value to a register
+ *
+ * For: move-result, move-result-object
+ *
+ * Description: Move the single-word non-object result of the most
+ * recent method invocation into the indicated register. This
+ * must be done as the instruction immediately after a
+ * method invocation whose (single-word, non-object) result
+ * is not to be ignored; anywhere else is invalid.
+ *
+ * Format: AA|op (11x)
+ *
+ * Syntax: op vAA
+ */
+
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ FFETCH_ADV 1, %ecx # %ecx<- next instruction hi; fetch, advance
+ movl offGlue_retval(%eax), %edx # %edx<- glue->retval
+ SET_VREG %edx, rINST # vA<- glue->retval
+ FGETOP_JMP 1, %ecx # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_MOVE_RESULT_OBJECT.S b/vm/mterp/x86-atom/OP_MOVE_RESULT_OBJECT.S
new file mode 100644
index 0000000..6d1fa75
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MOVE_RESULT_OBJECT.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MOVE_RESULT_OBJECT.S
+ */
+
+%include "x86-atom/OP_MOVE_RESULT.S"
diff --git a/vm/mterp/x86-atom/OP_MOVE_RESULT_WIDE.S b/vm/mterp/x86-atom/OP_MOVE_RESULT_WIDE.S
new file mode 100644
index 0000000..8f15264
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MOVE_RESULT_WIDE.S
@@ -0,0 +1,38 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MOVE_RESULT_WIDE.S
+ *
+ * Code: Copies a return value to a register
+ *
+ * For: move-result-wide
+ *
+ * Description: Move the double-word non-object result of the most
+ * recent method invocation into the indicated register. This
+ * must be done as the instruction immediately after a
+ * method invocation whose (single-word, non-object) result
+ * is not to be ignored; anywhere else is invalid.
+ *
+ * Format: AA|op (11x)
+ *
+ * Syntax: op vAA
+ */
+
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ movq offGlue_retval(%eax), %xmm0 # %xmm0<- glue->retval
+ movq %xmm0, (rFP, rINST, 4) # vA<- glue->retval
+ FFETCH_ADV 1, %edx # %edx<- next instruction hi; fetch, advance
+ FGETOP_JMP 1, %edx # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_MOVE_WIDE.S b/vm/mterp/x86-atom/OP_MOVE_WIDE.S
new file mode 100644
index 0000000..909243b
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MOVE_WIDE.S
@@ -0,0 +1,37 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MOVE_WIDE.S
+ *
+ * Code: Copies contents from one register to another. Uses no
+ * substitutions.
+ *
+ * For: move-wide
+ *
+ * Description: Copies contents from one non-object register to another.
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA+
+ shr $$4, %edx # %edx<- B
+ and $$15, rINST # rINST<- A
+ FFETCH_ADV 1, %eax # %eax<- next instruction hi; fetch, advance
+ movq (rFP, %edx, 4), %xmm0 # %xmm0<- vB
+ movq %xmm0, (rFP, rINST, 4) # vA<- vB
+ FGETOP_JMP 1, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_MOVE_WIDE_16.S b/vm/mterp/x86-atom/OP_MOVE_WIDE_16.S
new file mode 100644
index 0000000..af266fe
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MOVE_WIDE_16.S
@@ -0,0 +1,36 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MOVE_WIDE_16.S
+ *
+ * Code: Copies contents from one register to another. Uses no
+ * substitutions.
+ *
+ * For: move-wide/16
+ *
+ * Description: Copies contents from one non-object register to another.
+ *
+ * Format: ØØ|op AAAA BBBB (32x)
+ *
+ * Syntax: op vAAAA, vBBBB
+ */
+
+ FETCH 2, %edx # %edx<- BBBB
+ FETCH 1, %ecx # %ecx<- AAAA
+ FFETCH_ADV 3, %eax # %eax<- next instruction hi; fetch, advance
+ movq (rFP, %edx, 4), %xmm0 # %xmm0<- vB
+ movq %xmm0, (rFP, %ecx, 4) # vA<- vB; %xmm0
+ FGETOP_JMP 3, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_MOVE_WIDE_FROM16.S b/vm/mterp/x86-atom/OP_MOVE_WIDE_FROM16.S
new file mode 100644
index 0000000..4056b34
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MOVE_WIDE_FROM16.S
@@ -0,0 +1,34 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MOVE_WIDE_FROM16.S
+ *
+ * Code: Copies contents from one register to another
+ *
+ * For: move-wide/from16
+ *
+ * Description: Copies contents from one non-object register to another.
+ *
+ * Format: AA|op BBBB (22x)
+ *
+ * Syntax: op vAA, vBBBB
+ */
+
+ FETCH 1, %edx # %edx<- BBBB
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movq (rFP, %edx, 4), %xmm0 # %xmm0<- vB
+ movq %xmm0, (rFP, rINST, 4) # vA<- vB
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_MUL_DOUBLE.S b/vm/mterp/x86-atom/OP_MUL_DOUBLE.S
new file mode 100644
index 0000000..cecbf05
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MUL_DOUBLE.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MUL_DOUBLE.S
+ */
+
+%include "x86-atom/binopWide.S" {"instr":"mulsd %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_MUL_DOUBLE_2ADDR.S b/vm/mterp/x86-atom/OP_MUL_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..adc61d6
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MUL_DOUBLE_2ADDR.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MUL_DOUBLE_2ADDR.S
+ */
+
+%include "x86-atom/binopWide2addr.S" {"instr":"mulsd %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_MUL_FLOAT.S b/vm/mterp/x86-atom/OP_MUL_FLOAT.S
new file mode 100644
index 0000000..34eba58
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MUL_FLOAT.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MUL_FLOAT.S
+ */
+
+%include "x86-atom/binopF.S" {"instr":"mulss %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_MUL_FLOAT_2ADDR.S b/vm/mterp/x86-atom/OP_MUL_FLOAT_2ADDR.S
new file mode 100644
index 0000000..dbd615d
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MUL_FLOAT_2ADDR.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MUL_FLOAT_2ADDR.S
+ */
+
+%include "x86-atom/binopF2addr.S" {"instr":"mulss %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_MUL_INT.S b/vm/mterp/x86-atom/OP_MUL_INT.S
new file mode 100644
index 0000000..8f5dac5
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MUL_INT.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MUL_INT.S
+ */
+
+%include "x86-atom/binop.S" {"instr":"imul %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_MUL_INT_2ADDR.S b/vm/mterp/x86-atom/OP_MUL_INT_2ADDR.S
new file mode 100644
index 0000000..b544df7
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MUL_INT_2ADDR.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MUL_INT_2ADDR.S
+ */
+
+%include "x86-atom/binop2addr.S" {"instr":"imul %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_MUL_INT_LIT16.S b/vm/mterp/x86-atom/OP_MUL_INT_LIT16.S
new file mode 100644
index 0000000..241531f
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MUL_INT_LIT16.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MUL_INT_LIT16.S
+ */
+
+%include "x86-atom/binopLit16.S" {"instr":"imul %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_MUL_INT_LIT8.S b/vm/mterp/x86-atom/OP_MUL_INT_LIT8.S
new file mode 100644
index 0000000..efcffe1
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MUL_INT_LIT8.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MUL_INT_LIT8.S
+ */
+
+%include "x86-atom/binopLit8.S" {"instr":"imul %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_MUL_LONG.S b/vm/mterp/x86-atom/OP_MUL_LONG.S
new file mode 100644
index 0000000..85cccf2
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MUL_LONG.S
@@ -0,0 +1,71 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MUL_LONG.S
+ *
+ * Code: 64-bit integer multiply
+ *
+ * For: mul-long
+ *
+ * Description: Multiply two source registers and store the
+ * result in a destination register.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ /*
+ * Signed 64-bit integer multiply.
+ *
+ * Consider WXxYZ (r1r0 x r3r2) with a long multiply:
+ * WX
+ * x YZ
+ * --------
+ * ZW ZX
+ * YW YX
+ *
+ * The low word of the result holds ZX, the high word holds
+ * (ZW+YX) + (the high overflow from ZX). YW doesn't matter because
+ * it doesn't fit in the low 64 bits.
+ */
+
+ movl rINST, -4(%esp) # -4(%esp)<- AA+
+ FETCH_BB 1, rINST # rINST<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ jmp .L${opcode}_finish
+%break
+
+ /*
+ * X = (rFP, rINST, 4)
+ * W = 4(rFP, rINST, 4)
+ * Z = (rFP, %edx, 4)
+ * Y = 4(rFP, %edx, 4)
+ */
+
+.L${opcode}_finish:
+ movl 4(rFP, rINST, 4), %ecx # %ecx<- W
+ imull (rFP, %edx, 4), %ecx # %ecx<- WxZ
+ mov 4(rFP, %edx, 4), %eax # %ecx<- Y
+ imull (rFP, rINST, 4), %eax # %eax<- XxY
+ addl %eax, %ecx # %ecx<- (WZ + XY)
+ movl (rFP, %edx, 4), %eax # %eax<- Z
+ mull (rFP, rINST, 4) # %edx:eax<- XZ
+ movzbl -4(%esp), rINST # rINST<- AA
+ addl %edx, %ecx # %ecx<- carry + (WZ + XY)
+ movl %ecx, 4(rFP, rINST, 4) # vAA+1<- results hi
+ movl %eax, (rFP, rINST, 4) # vAA<- results lo
+ FINISH 2 # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_MUL_LONG_2ADDR.S b/vm/mterp/x86-atom/OP_MUL_LONG_2ADDR.S
new file mode 100644
index 0000000..d6b8c16
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_MUL_LONG_2ADDR.S
@@ -0,0 +1,72 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_MUL_LONG_2ADDR.S
+ *
+ * Code: 64-bit integer multiply
+ *
+ * For: mul-long/2addr
+ *
+ * Description: Multiply two sources registers and store the result
+ * in the first source register.
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ /*
+ * Signed 64-bit integer multiply.
+ *
+ * Consider WXxYZ (r1r0 x r3r2) with a long multiply:
+ * WX
+ * x YZ
+ * --------
+ * ZW ZX
+ * YW YX
+ *
+ * The low word of the result holds ZX, the high word holds
+ * (ZW+YX) + (the high overflow from ZX). YW doesn't matter because
+ * it doesn't fit in the low 64 bits.
+ */
+
+ movl rINST, %edx # %edx<- BA+
+ shr $$4, rINST # rINST<- B
+ andl $$15, %edx # %edx<- A
+ movl %edx, sReg0 # sReg0<- A
+ jmp .L${opcode}_finish
+%break
+
+ /*
+ * X = (rFP, rINST, 4)
+ * W = 4(rFP, rINST, 4)
+ * Z = (rFP, %edx, 4)
+ * Y = 4(rFP, %edx, 4)
+ */
+
+.L${opcode}_finish:
+ movl 4(rFP, rINST, 4), %ecx # %ecx<- W
+ imull (rFP, %edx, 4), %ecx # %ecx<- WxZ
+ movl 4(rFP, %edx, 4), %eax # %eax<- Y
+ imull (rFP, rINST, 4), %eax # %eax<- X*Y
+ addl %eax, %ecx # %ecx<- (WZ + XY)
+ movl (rFP, %edx, 4), %eax # %eax<- Z
+ mull (rFP, rINST, 4) # %edx:eax<- XZ
+ addl %edx, %ecx # %ecx<- carry + (WZ + XY)
+ movl sReg0, %edx # %edx<- A
+ movl %ecx, 4(rFP, %edx, 4) # vA+1<- results hi
+ movl %eax, (rFP, %edx, 4) # vA<- results lo
+ FINISH 1 # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_NEG_DOUBLE.S b/vm/mterp/x86-atom/OP_NEG_DOUBLE.S
new file mode 100644
index 0000000..b6fb070
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_NEG_DOUBLE.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_NEG_DOUBLE.S
+ */
+
+%include "x86-atom/unopWide.S" { "preinstr":"movq .LdoubNeg, %xmm1", "instr":"pxor %xmm1, %xmm0" }
diff --git a/vm/mterp/x86-atom/OP_NEG_FLOAT.S b/vm/mterp/x86-atom/OP_NEG_FLOAT.S
new file mode 100644
index 0000000..418fc0a
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_NEG_FLOAT.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_NEG_FLOAT.S
+ */
+
+%include "x86-atom/unop.S" { "instr":"addl $0x80000000, %ecx" }
diff --git a/vm/mterp/x86-atom/OP_NEG_INT.S b/vm/mterp/x86-atom/OP_NEG_INT.S
new file mode 100644
index 0000000..68acb89
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_NEG_INT.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_NEG_INT.S
+ */
+
+%include "x86-atom/unop.S" {"instr":"neg %ecx"}
diff --git a/vm/mterp/x86-atom/OP_NEG_LONG.S b/vm/mterp/x86-atom/OP_NEG_LONG.S
new file mode 100644
index 0000000..3f500bb
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_NEG_LONG.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_NEG_LONG.S
+ */
+
+%include "x86-atom/unopWide.S" {"preinstr":"xorps %xmm1, %xmm1", "instr":"psubq %xmm0, %xmm1", "result":"%xmm1"}
diff --git a/vm/mterp/x86-atom/OP_NEW_ARRAY.S b/vm/mterp/x86-atom/OP_NEW_ARRAY.S
new file mode 100644
index 0000000..a6d5fd3
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_NEW_ARRAY.S
@@ -0,0 +1,92 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_NEW_ARRAY.S
+ *
+ * Code: Create a new array. Uses no substitutions.
+ *
+ * For: new-array
+ *
+ * Description: Construct a new array of the indicated type and size.
+ * The type must be an array type.
+ *
+ * Format: B|A|op CCCC (22c)
+ *
+ * Syntax: op vA, vB, type@CCCC
+ * op vA, vB, field@CCCC
+ */
+
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ movl rINST, %edx # %edx<- BA
+ shr $$4, %edx # %edx<- B
+ movl offGlue_methodClassDex(%eax), %eax # %eax<- glue->pDvmDex
+ FETCH 1, %ecx # %ecx<- CCCC
+ GET_VREG %edx # %edx<- vB
+ movl offDvmDex_pResClasses(%eax), %eax # %eax<- glue->pDvmDex->pResClasses
+ cmp $$0, %edx # check for negative length
+ movl (%eax, %ecx, 4), %eax # %eax<- resolved class
+ js common_errNegativeArraySize # handle negative array length
+ cmp $$0, %eax # check for null
+ EXPORT_PC # required for resolve
+ jne .L${opcode}_finish # already resovled so continue
+ jmp .L${opcode}_resolve # need to resolve
+%break
+
+ /*
+ * Resolve class. (This is an uncommon case.)
+ *
+ * %edx holds array length
+ * %ecx holds class ref CCCC
+ */
+
+.L${opcode}_resolve:
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ movl offGlue_method(%eax), %eax # %eax<- glue->method
+ movl %edx, -4(%esp) # save length
+ movl $$0, -8(%esp) # push parameter false
+ movl %ecx, -12(%esp) # push parameter class ref
+ movl offMethod_clazz(%eax), %eax # %eax<- glue->method->clazz
+ movl %eax, -16(%esp) # push parameter clazz
+ lea -16(%esp), %esp
+ call dvmResolveClass # call: (const ClassObject* referrer,
+ # u4 classIdx, bool fromUnverifiedConstant)
+ # return: ClassObject*
+ cmp $$0, %eax # check for failure
+ lea 16(%esp), %esp
+ je common_exceptionThrown # handle exception
+ movl -4(%esp), %edx # %edx<- length
+
+ /*
+ * Finish allocation.
+ *
+ * %eax holds class
+ * %edx holds array length
+ */
+
+.L${opcode}_finish:
+ movl %eax, -12(%esp) # push parameter class
+ movl %edx, -8(%esp) # push parameter length
+ movl $$ALLOC_DONT_TRACK, -4(%esp)
+ lea -12(%esp), %esp
+ call dvmAllocArrayByClass # call: (ClassObject* arrayClass,
+ # size_t length, int allocFlags)
+ # return: ArrayObject*
+ and $$15, rINST # rINST<- A
+ cmp $$0, %eax # check for allocation failure
+ lea 12(%esp), %esp
+ je common_exceptionThrown # handle exception
+ SET_VREG %eax, rINST # vA<- pArray
+ FINISH 2 # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_NEW_INSTANCE.S b/vm/mterp/x86-atom/OP_NEW_INSTANCE.S
new file mode 100644
index 0000000..d65afb7
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_NEW_INSTANCE.S
@@ -0,0 +1,147 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_NEW_INSTANCE.S
+ *
+ * Code: Create a new instance of a given type. Uses no substitutions.
+ *
+ * For: new-instance
+ *
+ * Description: Construct a new instance of the indicated type,
+ * storing a reference to it in the destination.
+ * The type must refer to a non-array class.
+ *
+ *
+ *
+ * Format: AA|op BBBB (21c)
+ *
+ * Syntax: op vAA, type@BBBB
+ * op vAA, field@BBBB
+ * op vAA, string@BBBB
+ */
+
+ movl rGLUE, %ecx # %ecx<- pMterpGlue
+ movl offGlue_methodClassDex(%ecx), %ecx # %ecx<- glue->pDvmDex
+ FETCH 1, %edx # %edx<- BBBB
+ movl offDvmDex_pResClasses(%ecx), %ecx # %ecx<- glue->pDvmDex->pResClasses
+ movl (%ecx, %edx, 4), %edx # %edx<- vB
+ EXPORT_PC # required for resolve
+ cmp $$0, %edx # check for null
+ je .L${opcode}_resolve # need to resolve
+
+ /*
+ * %edx holds class object
+ */
+
+.L${opcode}_resolved:
+ movzbl offClassObject_status(%edx), %eax # %eax<- class status
+ cmp $$CLASS_INITIALIZED, %eax # check if class is initialized
+ jne .L${opcode}_needinit # initialize class
+
+ /*
+ * %edx holds class object
+ */
+
+.L${opcode}_initialized:
+ testl $$(ACC_INTERFACE|ACC_ABSTRACT), offClassObject_accessFlags(%edx)
+ mov $$ALLOC_DONT_TRACK, %eax # %eax<- flag for alloc call
+ je .L${opcode}_finish # continue
+ jmp .L${opcode}_abstract # handle abstract or interface
+
+ /*
+ * %edx holds class object
+ * %eax holds flags for alloc call
+ */
+
+%break
+.balign 32
+.L${opcode}_finish:
+ movl %edx, -8(%esp) # push parameter object
+ movl %eax, -4(%esp) # push parameter flags
+ lea -8(%esp), %esp
+ call dvmAllocObject # call: (ClassObject* clazz, int flags)
+ # return: Object*
+ cmp $$0, %eax # check for failure
+ lea 8(%esp), %esp
+ je common_exceptionThrown # handle exception
+ SET_VREG %eax, rINST # vAA<- pObject
+ FINISH 2 # jump to next instruction
+
+ /*
+ * Class initialization required.
+ *
+ * %edx holds class object
+ */
+
+.L${opcode}_needinit:
+ movl %edx, -4(%esp) # push parameter object
+ lea -4(%esp), %esp
+ call dvmInitClass # call: (ClassObject* clazz)
+ # return: bool
+ lea 4(%esp), %esp
+ cmp $$0, %eax # check for failure
+ movl -4(%esp), %edx # %edx<- object
+ je common_exceptionThrown # handle exception
+ testl $$(ACC_INTERFACE|ACC_ABSTRACT), offClassObject_accessFlags(%edx)
+ mov $$ALLOC_DONT_TRACK, %eax # %eax<- flag for alloc call
+ je .L${opcode}_finish # continue
+ jmp .L${opcode}_abstract # handle abstract or interface
+
+ /*
+ * Resolution required. This is the least-likely path.
+ *
+ * BBBB in %eax
+ */
+
+.L${opcode}_resolve:
+
+
+ movl rGLUE, %ecx # %ecx<- pMterpGlue
+ FETCH 1, %eax # %eax<- BBBB
+ movl offGlue_method(%ecx), %ecx # %ecx<- glue->method
+ movl offMethod_clazz(%ecx), %ecx # %ecx<- glue->method->clazz
+ movl %ecx, -12(%esp) # push parameter clazz
+ movl $$0, -4(%esp) # push parameter false
+ movl %eax, -8(%esp) # push parameter BBBB
+ lea -12(%esp), %esp
+ call dvmResolveClass # call: (const ClassObject* referrer,
+ # u4 classIdx, bool fromUnverifiedConstant)
+ # return: ClassObject*
+ lea 12(%esp), %esp
+ movl %eax, %edx # %edx<- pObject
+ cmp $$0, %edx # check for failure
+ jne .L${opcode}_resolved # continue
+ jmp common_exceptionThrown # handle exception
+
+ /*
+ * We can't instantiate an abstract class or interface, so throw an
+ * InstantiationError with the class descriptor as the message.
+ *
+ * %edx holds class object
+ */
+
+.L${opcode}_abstract:
+ movl offClassObject_descriptor(%edx), %ecx # %ecx<- descriptor
+ movl %ecx, -4(%esp) # push parameter descriptor
+ movl $$.LstrInstantiationErrorPtr, -8(%esp) # push parameter message
+ lea -8(%esp), %esp
+ call dvmThrowExceptionWithClassMessage # call: (const char* exceptionDescriptor,
+ # const char* messageDescriptor)
+ # return: void
+ jmp common_exceptionThrown # handle exception
+
+.LstrInstantiationErrorPtr:
+.asciz "Ljava/lang/InstantiationError;"
diff --git a/vm/mterp/x86-atom/OP_NOP.S b/vm/mterp/x86-atom/OP_NOP.S
new file mode 100644
index 0000000..9911da3
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_NOP.S
@@ -0,0 +1,41 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_NOP.S
+ *
+ * Code: Use a cycle. Uses no substitutions.
+ *
+ * For: nop
+ *
+ * Description: No operation. Use a cycle
+ *
+ * Format: ØØ|op (10x)
+ *
+ * Syntax: op
+ */
+
+ FINISH 1 # jump to next instruction
+
+#ifdef ASSIST_DEBUGGER
+
+ /*
+ * insert fake function header to help gdb find the stack frame
+ */
+
+ .type dalvik_inst, %function
+dalvik_inst:
+ MTERP_ENTRY
+#endif
diff --git a/vm/mterp/x86-atom/OP_NOT_INT.S b/vm/mterp/x86-atom/OP_NOT_INT.S
new file mode 100644
index 0000000..b82e5b6
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_NOT_INT.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_NOT_INT.S
+ */
+
+%include "x86-atom/unop.S" {"instr":"not %ecx"}
diff --git a/vm/mterp/x86-atom/OP_NOT_LONG.S b/vm/mterp/x86-atom/OP_NOT_LONG.S
new file mode 100644
index 0000000..98ff80b
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_NOT_LONG.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_NOT_LONG.S
+ */
+
+%include "x86-atom/unopWide.S" {"instr":"pandn 0xFFFFFFFF, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_OR_INT.S b/vm/mterp/x86-atom/OP_OR_INT.S
new file mode 100644
index 0000000..0ece38c
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_OR_INT.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_OR_INT.S
+ */
+
+%include "x86-atom/binop.S" {"instr":"or %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_OR_INT_2ADDR.S b/vm/mterp/x86-atom/OP_OR_INT_2ADDR.S
new file mode 100644
index 0000000..693e099
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_OR_INT_2ADDR.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_OR_INT_2ADDR.S
+ */
+
+%include "x86-atom/binop2addr.S" {"instr":"or %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_OR_INT_LIT16.S b/vm/mterp/x86-atom/OP_OR_INT_LIT16.S
new file mode 100644
index 0000000..5c63867
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_OR_INT_LIT16.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_OR_INT_LIT16.S
+ */
+
+%include "x86-atom/binopLit16.S" {"instr":"or %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_OR_INT_LIT8.S b/vm/mterp/x86-atom/OP_OR_INT_LIT8.S
new file mode 100644
index 0000000..aacd6c3
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_OR_INT_LIT8.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_OR_INT_LIT8.S
+ */
+
+%include "x86-atom/binopLit8.S" {"instr":"or %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_OR_LONG.S b/vm/mterp/x86-atom/OP_OR_LONG.S
new file mode 100644
index 0000000..f698e54
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_OR_LONG.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_OR_LONG.S
+ */
+
+%include "x86-atom/binopWide.S" {"instr":"por %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_OR_LONG_2ADDR.S b/vm/mterp/x86-atom/OP_OR_LONG_2ADDR.S
new file mode 100644
index 0000000..12a88ec
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_OR_LONG_2ADDR.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_OR_LONG_2ADDR.S
+ */
+
+%include "x86-atom/binopWide2addr.S" {"instr":"por %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_PACKED_SWITCH.S b/vm/mterp/x86-atom/OP_PACKED_SWITCH.S
new file mode 100644
index 0000000..debac02
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_PACKED_SWITCH.S
@@ -0,0 +1,52 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_PACKED_SWITCH.S
+ *
+ * Code: Jump to a new instruction using a jump table
+ *
+ * For: packed-switch, sparse-switch
+ *
+ * Description: Jump to a new instruction based on the value in the given
+ * register, using a table of offsets corresponding to each
+ * value in a particular integral range, or fall through to
+ * the next instruction if there is no match.
+ *
+ * Format: AA|op BBBBlo BBBBhi (31t)
+ *
+ * Syntax: op vAA, +BBBBBBBB
+ */
+
+%default { "func":"dvmInterpHandlePackedSwitch" }
+
+ FETCH 1, %ecx # %ecx<- BBBBlo
+ FETCH 2, %edx # %edx<- BBBBhi
+ shl $$16, %edx # prepare to create +BBBBBBBB
+ or %edx, %ecx # %ecx<- +BBBBBBBB
+ GET_VREG rINST # rINST<- vAA
+ movl rINST, -4(%esp) # push parameter vAA
+ lea (rPC, %ecx, 2), %ecx # %ecx<- PC + +BBBBBBBB*2
+ movl %ecx, -8(%esp) # push parameter PC + +BBBBBBBB*2
+ lea -8(%esp), %esp
+ call $func # call code-unit branch offset
+ shl $$1, %eax # shift for byte offset
+ movl %eax, %edx # %edx<- offset
+ lea 8(%esp), %esp
+ jle common_periodicChecks_backwardBranch # do backward branch
+ jmp .L${opcode}_finish
+%break
+.L${opcode}_finish:
+ FINISH_RB %edx, %ecx # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_REM_DOUBLE.S b/vm/mterp/x86-atom/OP_REM_DOUBLE.S
new file mode 100644
index 0000000..aa7d332
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_REM_DOUBLE.S
@@ -0,0 +1,51 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_REM_DOUBLE.S
+ *
+ * Code: Computes the remainder of a division. Performs no substitutions.
+ *
+ * For: rem-double
+ *
+ * Description: Calls fmod to compute the remainder of the result of dividing a
+ * source register by a second, and stores the result in a
+ * destination register.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ movl (rFP, %ecx, 4), %eax # %eax<- vBBlo
+ movl %eax, -16(%esp) # push parameter double lo
+ movl 4(rFP, %ecx, 4), %eax # %eax<- vBBhi
+ movl %eax, -12(%esp) # push parameter double hi
+ movl (rFP, %edx, 4), %eax # %eax<- vCClo
+ movl %eax, -8(%esp) # push parameter double lo
+ movl 4(rFP, %edx, 4), %eax # %eax<- vCChi
+ movl %eax, -4(%esp) # push parameter double hi
+ lea -16(%esp), %esp
+ jmp .L${opcode}_break
+%break
+
+.L${opcode}_break:
+ call fmod # call: (long double x, long double y)
+ # return: double
+ lea 16(%esp), %esp
+ fstpl (rFP, rINST, 4) # vAA<- remainder; return of fmod
+ FINISH 2 # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_REM_DOUBLE_2ADDR.S b/vm/mterp/x86-atom/OP_REM_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..434c878
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_REM_DOUBLE_2ADDR.S
@@ -0,0 +1,52 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_REM_DOUBLE_2ADDR.S
+ *
+ * Code: Computes the remainder of a division. Performs no substitutions.
+ *
+ * For: rem-double/2addr
+ *
+ * Description: Calls fmod to compute the remainder of the result of dividing a
+ * source register by a second, and stores the result in the first
+ * source register.
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ and $$15, rINST # rINST<- A
+ shr $$4, %edx # %edx<- B
+ movl (rFP, rINST, 4), %eax # %eax<- vAlo
+ movl %eax, -20(%esp) # push parameter vAAlo
+ movl 4(rFP, rINST, 4), %eax # %eax<- vAhi
+ movl %eax, -16(%esp) # push parameter vAAhi
+ movl (rFP, %edx, 4), %eax # %eax<- vBlo
+ movl %eax, -12(%esp) # push parameter vBBlo
+ movl 4(rFP, %edx, 4), %eax # %eax<- vBhi
+ movl %eax, -8(%esp) # push parameter vBBhi
+ lea -20(%esp), %esp
+ jmp .L${opcode}_break
+%break
+
+.L${opcode}_break:
+ call fmod # call: (long double x, long double y)
+ # return: double
+ lea 20(%esp), %esp
+ fstpl (rFP, rINST, 4) # vAA<- remainder; return of fmod
+ FINISH 1 # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_REM_FLOAT.S b/vm/mterp/x86-atom/OP_REM_FLOAT.S
new file mode 100644
index 0000000..de5e161
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_REM_FLOAT.S
@@ -0,0 +1,43 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_REM_FLOAT.S
+ *
+ * Code: Computes the remainder of a division. Performs no substitutions.
+ *
+ * For: rem-float
+ *
+ * Description: Calls fmod to compute the remainder of the result of dividing a
+ * source register by a second, and stores the result in a
+ * destination register.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ GET_VREG %ecx # %ecx<- vBB
+ GET_VREG %edx # %edx<- vCC
+ movl %ecx, -8(%esp) # push parameter float
+ movl %edx, -4(%esp) # push parameter float
+ lea -8(%esp), %esp
+ call fmodf # call: (float x, float y)
+ # return: float
+ lea 8(%esp), %esp
+ fstps (rFP, rINST, 4) # vAA<- remainder; return of fmod
+ FINISH 2 # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_REM_FLOAT_2ADDR.S b/vm/mterp/x86-atom/OP_REM_FLOAT_2ADDR.S
new file mode 100644
index 0000000..5ff5af5
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_REM_FLOAT_2ADDR.S
@@ -0,0 +1,44 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_REM_FLOAT_2ADDR.S
+ *
+ * Code: Computes the remainder of a division. Performs no substitutions.
+ *
+ * For: rem-float/2addr
+ *
+ * Description: Calls fmod to compute the remainder of the result of dividing a
+ * source register by a second, and stores the result in the first
+ * source register.
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $$4, %edx # %edx<- B
+ andl $$15, rINST # rINST<- A
+ GET_VREG %edx # %edx<- vB
+ movl (rFP, rINST, 4), %ecx # %ecx<- vA
+ movl %ecx, -8(%esp) # push parameter vA
+ movl %edx, -4(%esp) # push parameter vB
+ lea -8(%esp), %esp
+ call fmodf # call: (float x, float y)
+ # return: float
+ lea 8(%esp), %esp
+ fstps (rFP, rINST, 4)
+ FINISH 1 # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_REM_INT.S b/vm/mterp/x86-atom/OP_REM_INT.S
new file mode 100644
index 0000000..5f62d66
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_REM_INT.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_REM_INT.S
+ */
+
+%include "x86-atom/binopD.S" {"div":"0"}
diff --git a/vm/mterp/x86-atom/OP_REM_INT_2ADDR.S b/vm/mterp/x86-atom/OP_REM_INT_2ADDR.S
new file mode 100644
index 0000000..369ea5c
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_REM_INT_2ADDR.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_REM_INT_2ADDR.S
+ */
+
+%include "x86-atom/binopD2addr.S" {"div":"0"}
diff --git a/vm/mterp/x86-atom/OP_REM_INT_LIT16.S b/vm/mterp/x86-atom/OP_REM_INT_LIT16.S
new file mode 100644
index 0000000..0c9afa3
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_REM_INT_LIT16.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_REM_INT_LIT16.S
+ */
+
+%include "x86-atom/binopDLit16.S" {"div":"0"}
diff --git a/vm/mterp/x86-atom/OP_REM_INT_LIT8.S b/vm/mterp/x86-atom/OP_REM_INT_LIT8.S
new file mode 100644
index 0000000..6578c7c
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_REM_INT_LIT8.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_REM_INT_LIT8.S
+ */
+
+%include "x86-atom/binopDLit8.S" {"div":"0"}
diff --git a/vm/mterp/x86-atom/OP_REM_LONG.S b/vm/mterp/x86-atom/OP_REM_LONG.S
new file mode 100644
index 0000000..3e3b200
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_REM_LONG.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_REM_LONG.S
+ */
+
+%include "x86-atom/binopDivRemLong.S" {"func":"__moddi3"}
diff --git a/vm/mterp/x86-atom/OP_REM_LONG_2ADDR.S b/vm/mterp/x86-atom/OP_REM_LONG_2ADDR.S
new file mode 100644
index 0000000..f494caf
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_REM_LONG_2ADDR.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_REM_LONG_2ADDR.S
+ */
+
+%include "x86-atom/binopDivRemLong2Addr.S" {"func":"__moddi3"}
diff --git a/vm/mterp/x86-atom/OP_RETURN.S b/vm/mterp/x86-atom/OP_RETURN.S
new file mode 100644
index 0000000..48d7e34
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_RETURN.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_RETURN.S
+ */
+
+%include "x86-atom/OP_RETURN_COMMON.S"
diff --git a/vm/mterp/x86-atom/OP_RETURN_COMMON.S b/vm/mterp/x86-atom/OP_RETURN_COMMON.S
new file mode 100644
index 0000000..d58a16c
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_RETURN_COMMON.S
@@ -0,0 +1,34 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_RETURN_COMMON.S
+ *
+ * Code: Return a 32-bit value. Uses no substitutions.
+ *
+ * For: return, return-object
+ *
+ * Description: Copies the return value into the "glue"
+ * structure, then jumps to the return handler.
+ *
+ * Format: AA|op (11x)
+ *
+ * Syntax: op vAA
+ */
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ GET_VREG rINST # rINST<- vAA
+ movl rINST, offGlue_retval(%edx) # glue->retval<- vAA
+ jmp common_returnFromMethod # jump to common return code
diff --git a/vm/mterp/x86-atom/OP_RETURN_OBJECT.S b/vm/mterp/x86-atom/OP_RETURN_OBJECT.S
new file mode 100644
index 0000000..3b9c10c
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_RETURN_OBJECT.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_RETURN_OBJECT.S
+ */
+
+%include "x86-atom/OP_RETURN_COMMON.S"
diff --git a/vm/mterp/x86-atom/OP_RETURN_VOID.S b/vm/mterp/x86-atom/OP_RETURN_VOID.S
new file mode 100644
index 0000000..4d8c92b
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_RETURN_VOID.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_RETURN_VOID.S
+ */
+
+ jmp common_returnFromMethod
diff --git a/vm/mterp/x86-atom/OP_RETURN_WIDE.S b/vm/mterp/x86-atom/OP_RETURN_WIDE.S
new file mode 100644
index 0000000..8069e85
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_RETURN_WIDE.S
@@ -0,0 +1,34 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_RETURN_WIDE.S
+ *
+ * Code: Return a 64-bit value. Uses no substitutions.
+ *
+ * For: return-wide
+ *
+ * Description: Copies the return value into the "glue"
+ * structure, then jumps to the return handler.
+ *
+ * Format: AA|op (11x)
+ *
+ * Syntax: op vAA
+ */
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movq (rFP, rINST, 4), %xmm0 # %xmm0<- vAA
+ movq %xmm0, offGlue_retval(%edx)# glue->retval<- vAA
+ jmp common_returnFromMethod # jump to common return code
diff --git a/vm/mterp/x86-atom/OP_RSUB_INT.S b/vm/mterp/x86-atom/OP_RSUB_INT.S
new file mode 100644
index 0000000..87498f9
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_RSUB_INT.S
@@ -0,0 +1,39 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_RSUB_INT.S
+ *
+ * Code: 32-bit reverse-subtraction. Uses no substitutions.
+ *
+ * For: rsub-int
+ *
+ * Description: Perform a reverse subtraction on a register and a
+ * signed extended 16-bit literal value and store the
+ * result in a destination register.
+ *
+ * Format: B|A|op CCCC (22s)
+ *
+ * Syntax: op vA, vB, #+CCCC
+ */
+
+ movl rINST, %ecx # %ecx<- BA
+ shr $$4, %ecx # %ecx<- B
+ andl $$15, rINST # rINST<- A
+ FETCHs 1, %edx # %edx<- +CCCC, sign-extended literal
+ GET_VREG %ecx # %ecx<- vB
+ subl %ecx, %edx # %edx<- +CCCC sub vB
+ SET_VREG %edx, rINST # vA<- %edx; result
+ FINISH 2 # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_RSUB_INT_LIT8.S b/vm/mterp/x86-atom/OP_RSUB_INT_LIT8.S
new file mode 100644
index 0000000..d6114dd
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_RSUB_INT_LIT8.S
@@ -0,0 +1,36 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_RSUB_INT_LIT8.S
+ *
+ * Code: 32-bit reverse-subtraction. Uses no substitutions.
+ *
+ * For: rsub-int/lit8
+ *
+ * Description: Perform a reverse subtraction on a register and a
+ * signed extended 8-bit literal value.
+ *
+ * Format: AA|op CC|BB (22b)
+ *
+ * Syntax: op vAA, vBB, #+CC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CCs 1, %edx # %edx<- +CC, sign-extended literal
+ GET_VREG %ecx # %ecx<- vBB
+ sub %ecx, %edx # %edx<- +CC sub vBB
+ SET_VREG %edx, rINST # vAA<- %edx; result
+ FINISH 2 # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_SGET.S b/vm/mterp/x86-atom/OP_SGET.S
new file mode 100644
index 0000000..914b4dc
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SGET.S
@@ -0,0 +1,60 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SGET.S
+ *
+ * Code: Generic 32-bit static field "get" operation. Uses no substitutions.
+ *
+ * For: sget-boolean, sget-byte, sget-char, sget-object, sget, sget-short
+ *
+ * Description: Perform the identified object static field operation
+ * with the identified static field; load the field value
+ * into the value register.
+ *
+ * Format: AA|op BBBB (21c)
+ *
+ * Syntax: op vAA, string@BBBB
+ */
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_methodClassDex(%edx), %ecx # %ecx<- glue->pDvmDex
+ FETCH 1, %eax # %eax<- BBBB
+ movl offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+ cmp $$0, (%ecx, %eax, 4) # check for null ptr; resolved StaticField ptr
+ movl (%ecx, %eax, 4), %ecx # %ecx<- resolved StaticField ptr
+ je .L${opcode}_resolve
+ jmp .L${opcode}_finish
+%break
+
+.L${opcode}_resolve:
+ movl offGlue_method(%edx), %edx # %edx <- glue->method
+ EXPORT_PC # in case an exception is thrown
+ movl %eax, -4(%esp) # push parameter CCCC; field ref
+ movl offMethod_clazz(%edx), %edx # %edx<- method->clazz
+ movl %edx, -8(%esp) # push parameter method->clazz
+ lea -8(%esp), %esp
+ call dvmResolveStaticField # call: (const ClassObject* referrer, u4 ifieldIdx)
+ # return: StaticField*
+ cmp $$0, %eax # check if initalization failed
+ lea 8(%esp), %esp
+ je common_exceptionThrown # failed; handle exception
+ mov %eax, %ecx # %ecx<- result
+
+.L${opcode}_finish:
+ FFETCH_ADV 2, %edx # %edx<- next instruction hi; fetch, advance
+ movl offStaticField_value(%ecx), %eax # %eax<- field value
+ SET_VREG %eax, rINST # vAA<- field value
+ FGETOP_JMP 2, %edx # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_SGET_BOOLEAN.S b/vm/mterp/x86-atom/OP_SGET_BOOLEAN.S
new file mode 100644
index 0000000..8e383b4
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SGET_BOOLEAN.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SGET_BOOLEAN.S
+ */
+
+%include "x86-atom/OP_SGET.S"
diff --git a/vm/mterp/x86-atom/OP_SGET_BYTE.S b/vm/mterp/x86-atom/OP_SGET_BYTE.S
new file mode 100644
index 0000000..c86fc20
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SGET_BYTE.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SGET_BYTE.S
+ */
+
+%include "x86-atom/OP_SGET.S"
diff --git a/vm/mterp/x86-atom/OP_SGET_CHAR.S b/vm/mterp/x86-atom/OP_SGET_CHAR.S
new file mode 100644
index 0000000..0a3cffd
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SGET_CHAR.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SGET_CHAR.S
+ */
+
+%include "x86-atom/OP_SGET.S"
diff --git a/vm/mterp/x86-atom/OP_SGET_OBJECT.S b/vm/mterp/x86-atom/OP_SGET_OBJECT.S
new file mode 100644
index 0000000..5145f14
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SGET_OBJECT.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SGET_OBJECT.S
+ */
+
+%include "x86-atom/OP_SGET.S"
diff --git a/vm/mterp/x86-atom/OP_SGET_OBJECT_VOLATILE.S b/vm/mterp/x86-atom/OP_SGET_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..5f64fb5
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SGET_OBJECT_VOLATILE.S
@@ -0,0 +1 @@
+%include "x86/OP_SGET.S"
diff --git a/vm/mterp/x86-atom/OP_SGET_SHORT.S b/vm/mterp/x86-atom/OP_SGET_SHORT.S
new file mode 100644
index 0000000..77064b6
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SGET_SHORT.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SGET_SHORT.S
+ */
+
+%include "x86-atom/OP_SGET.S"
diff --git a/vm/mterp/x86-atom/OP_SGET_VOLATILE.S b/vm/mterp/x86-atom/OP_SGET_VOLATILE.S
new file mode 100644
index 0000000..5f64fb5
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SGET_VOLATILE.S
@@ -0,0 +1 @@
+%include "x86/OP_SGET.S"
diff --git a/vm/mterp/x86-atom/OP_SGET_WIDE.S b/vm/mterp/x86-atom/OP_SGET_WIDE.S
new file mode 100644
index 0000000..3ef6916
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SGET_WIDE.S
@@ -0,0 +1,65 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SGET_WIDE.S
+ *
+ * Code: 64-bit static field "get" operation. Uses no substitutions.
+ *
+ * For: sget-wide
+ *
+ * Description: Perform the identified object static field operation
+ * with the identified static field, loading or storing
+ * into the value register.
+ *
+ * Format: AA|op BBBB (21c)
+ *
+ * Syntax: op vAA, string@BBBB
+ */
+
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ movl offGlue_methodClassDex(%eax), %ecx # %ecx<- glue->pDvmDex
+ FETCH 1, %edx # %edx<- BBBB
+ movl offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+ cmp $$0, (%ecx, %edx, 4) # check for null ptr; resolved StaticField ptr
+ movl (%ecx, %edx, 4), %ecx # %ecx<- resolved StaticField ptr
+ je .L${opcode}_resolve
+
+.L${opcode}_finish:
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movq offStaticField_value(%ecx), %xmm0 # %xmm0<- field value
+ movq %xmm0, (rFP, rINST, 4) # vAA<- field value
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+%break
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * %edx: BBBB field ref
+ */
+
+.L${opcode}_resolve:
+ movl offGlue_method(%eax), %eax # %eax <- glue->method
+ EXPORT_PC # in case an exception is thrown
+ movl %edx, -4(%esp) # push parameter CCCC; field ref
+ movl offMethod_clazz(%eax), %eax # %eax<- method->clazz
+ movl %eax, -8(%esp) # push parameter method->clazz
+ lea -8(%esp), %esp
+ call dvmResolveStaticField # call: (const ClassObject* referrer, u4 ifieldIdx)
+ # return: StaticField*
+ lea 8(%esp), %esp
+ cmp $$0, %eax # check if initalization failed
+ movl %eax, %ecx # %ecx<- result
+ jne .L${opcode}_finish # success, continue
+ jmp common_exceptionThrown # failed; handle exception
diff --git a/vm/mterp/x86-atom/OP_SHL_INT.S b/vm/mterp/x86-atom/OP_SHL_INT.S
new file mode 100644
index 0000000..13e4a11
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SHL_INT.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SHL_INT.S
+ */
+
+%include "x86-atom/binopS.S" {"instr":"sal %cl, %edx"}
diff --git a/vm/mterp/x86-atom/OP_SHL_INT_2ADDR.S b/vm/mterp/x86-atom/OP_SHL_INT_2ADDR.S
new file mode 100644
index 0000000..a27e09a
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SHL_INT_2ADDR.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SHL_INT_2ADDR.S
+ */
+
+%include "x86-atom/binopS2addr.S" {"instr":"sal %cl, %edx"}
diff --git a/vm/mterp/x86-atom/OP_SHL_INT_LIT8.S b/vm/mterp/x86-atom/OP_SHL_INT_LIT8.S
new file mode 100644
index 0000000..5141e5c
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SHL_INT_LIT8.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SHL_INT_LIT8.S
+ */
+
+%include "x86-atom/binopLit8S.S" {"instr":"sal %cl, %edx"}
diff --git a/vm/mterp/x86-atom/OP_SHL_LONG.S b/vm/mterp/x86-atom/OP_SHL_LONG.S
new file mode 100644
index 0000000..cef558c
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SHL_LONG.S
@@ -0,0 +1,40 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SHL_LONG.S
+ *
+ * Code: Performs a shift left long. Uses no substitutions.
+ *
+ * For: shl-long
+ *
+ * Description: Perform a binary shift operation using two source registers
+ * where one is the shift amount and the other is the value to shift.
+ * Store the result in a destination register.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_CC 1, %eax # %eax<- CC
+ FETCH_BB 1, %edx # %edx<- BB
+ movq .LshiftMask, %xmm2 # %xmm2<- mask for the shift bits
+ movss (rFP, %eax, 4), %xmm0 # %xmm0<- vCC
+ pand %xmm2, %xmm0 # %xmm0<- masked shift bits
+ movq (rFP, %edx, 4), %xmm1 # %xmm1<- vBB
+ psllq %xmm0, %xmm1 # %xmm1<- shifted vBB
+ movq %xmm1, (rFP, rINST, 4) # vAA<- shifted vBB
+ FINISH 2 # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_SHL_LONG_2ADDR.S b/vm/mterp/x86-atom/OP_SHL_LONG_2ADDR.S
new file mode 100644
index 0000000..28dfaf2
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SHL_LONG_2ADDR.S
@@ -0,0 +1,41 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SHL_LONG_2ADDR.S
+ *
+ * Code: Performs a shift left long. Uses no substitutions.
+ *
+ * For: shl-long/2addr
+ *
+ * Description: Perform a binary shift operation using two source registers
+ * where the fist is the value to shift and the second is the
+ * shift amount. Store the result in the first source register.
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $$4, %edx # %edx<- B
+ andl $$15, rINST # rINST<- A
+ movss (rFP, %edx, 4), %xmm0 # %xmm0<- vB
+ movq (rFP, rINST, 4), %xmm1 # %xmm1<- vA
+ movq .LshiftMask, %xmm2 # %xmm2<- mask for the shift bits
+ pand %xmm2, %xmm0 # %xmm0<- masked shift bits
+ psllq %xmm0, %xmm1 # %xmm1<- shifted vA
+ movq %xmm1, (rFP, rINST, 4) # vA<- shifted vA
+ FINISH 1 # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_SHR_INT.S b/vm/mterp/x86-atom/OP_SHR_INT.S
new file mode 100644
index 0000000..e7fd28b
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SHR_INT.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SHR_INT.S
+ */
+
+%include "x86-atom/binopS.S" {"instr":"sar %cl, %edx"}
diff --git a/vm/mterp/x86-atom/OP_SHR_INT_2ADDR.S b/vm/mterp/x86-atom/OP_SHR_INT_2ADDR.S
new file mode 100644
index 0000000..0d0b461
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SHR_INT_2ADDR.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SHR_INT_2ADDR.S
+ */
+
+%include "x86-atom/binopS2addr.S" {"instr":"sar %cl, %edx"}
diff --git a/vm/mterp/x86-atom/OP_SHR_INT_LIT8.S b/vm/mterp/x86-atom/OP_SHR_INT_LIT8.S
new file mode 100644
index 0000000..3467bf5
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SHR_INT_LIT8.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SHR_INT_LIT8.S
+ */
+
+%include "x86-atom/binopLit8S.S" {"instr":"sar %cl, %edx"}
diff --git a/vm/mterp/x86-atom/OP_SHR_LONG.S b/vm/mterp/x86-atom/OP_SHR_LONG.S
new file mode 100644
index 0000000..be893ef
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SHR_LONG.S
@@ -0,0 +1,53 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SHR_LONG.S
+ *
+ * Code: Performs a shift right long
+ *
+ * For: shl-long
+ *
+ * Description: Perform a binary shift operation using two source registers
+ * where one is the shift amount and the other is the value to shift.
+ * Store the result in a destination register.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %edx # %edx<- BB
+ FETCH_CC 1, %eax # %eax<- CC
+ movq (rFP, %edx, 4), %xmm1 # %xmm1<- vBB
+ movss (rFP, %eax, 4), %xmm0 # %xmm0<- vCC
+ movq .LshiftMask, %xmm2
+ pand %xmm2, %xmm0 # %xmm0<- masked for the shift bits
+ psrlq %xmm0, %xmm1 # %xmm1<- shifted vBB
+ cmpl $$0, 4(rFP, %edx, 4) # check if we need to consider sign
+ jl .L${opcode}_finish # consider sign
+ jmp .L${opcode}_final # sign is fine, finish
+%break
+
+.L${opcode}_finish:
+ movq .Lvalue64, %xmm3 # %xmm3<- 64
+ psubq %xmm0, %xmm3 # %xmm3<- 64 - shift amount
+ movq .L64bits, %xmm4 # %xmm4<- lower 64 bits set
+ psllq %xmm3, %xmm4 # %xmm4<- correct mask for sign bits
+ por %xmm4, %xmm1 # %xmm1<- signed and shifted vBB
+
+.L${opcode}_final:
+ movq %xmm1, (rFP, rINST, 4) # vAA<- shifted vBB
+ FINISH 2 # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_SHR_LONG_2ADDR.S b/vm/mterp/x86-atom/OP_SHR_LONG_2ADDR.S
new file mode 100644
index 0000000..38aefcf
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SHR_LONG_2ADDR.S
@@ -0,0 +1,54 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SHR_LONG_2ADDR.S
+ *
+ * Code: Performs a shift left long
+ *
+ * For: shl-long/2addr
+ *
+ * Description: Perform a binary shift operation using two source registers
+ * where the fist is the value to shift and the second is the
+ * shift amount. Store the result in the first source register.
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $$4, %edx # %edx<- B
+ andl $$15, rINST # rINST<- BA
+ movss (rFP, %edx, 4), %xmm0 # %xmm0<- vB
+ movq (rFP, rINST, 4), %xmm1 # %xmm1<- vA
+ movq .LshiftMask, %xmm2
+ pand %xmm2, %xmm0 # %xmm0<- masked for the shift bits
+ psrlq %xmm0, %xmm1 # %xmm1<- shifted vBB
+ cmpl $$0, 4(rFP, rINST, 4) # check if we need to consider sign
+ jl .L${opcode}_finish # consider sign
+ jmp .L${opcode}_final # sign is fine, finish
+%break
+
+.L${opcode}_finish:
+ movq .Lvalue64, %xmm3 # %xmm3<- 64
+ psubq %xmm0, %xmm3 # %xmm3<- 64 - shift amount
+ movq .L64bits, %xmm4 # %xmm4<- lower 64 bits set
+ psllq %xmm3, %xmm4 # %xmm4<- correct mask for sign bits
+ por %xmm4, %xmm1 # %xmm1<- signed and shifted vBB
+
+.L${opcode}_final:
+ movq %xmm1, (rFP, rINST, 4) # vAA<- shifted vBB
+ FINISH 1 # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_SPARSE_SWITCH.S b/vm/mterp/x86-atom/OP_SPARSE_SWITCH.S
new file mode 100644
index 0000000..8020d1a
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SPARSE_SWITCH.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SPARSE_SWITCH.S
+ */
+
+%include "x86-atom/OP_PACKED_SWITCH.S" { "func":"dvmInterpHandleSparseSwitch" }
diff --git a/vm/mterp/x86-atom/OP_SPUT.S b/vm/mterp/x86-atom/OP_SPUT.S
new file mode 100644
index 0000000..55715a7
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SPUT.S
@@ -0,0 +1,60 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SPUT.S
+ *
+ * Code: Generic 32-bit static field "put" operation. Uses no substitutions.
+ *
+ * For: sput-boolean, sput-byte, sput-char, sput-object, sput, sput-short
+ *
+ * Description: Perform the identified object static field operation
+ * with the identified static field; store the field value
+ * register.
+ *
+ * Format: AA|op BBBB (21c)
+ *
+ * Syntax: op vAA, string@BBBB
+ */
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_methodClassDex(%edx), %ecx # %ecx<- pDvmDex
+ FETCH 1, %eax # %eax<- BBBB
+ movl offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+ cmp $$0, (%ecx, %eax, 4) # check for null ptr; resolved StaticField ptr
+ movl (%ecx, %eax, 4), %ecx # %ecx<- resolved StaticField ptr
+ je .L${opcode}_resolve
+ jmp .L${opcode}_finish
+%break
+
+.L${opcode}_resolve:
+ movl offGlue_method(%edx), %edx # %edx <- glue->method
+ EXPORT_PC # in case an exception is thrown
+ movl %eax, -4(%esp) # push parameter CCCC; field ref
+ movl offMethod_clazz(%edx), %edx # %edx<- method->clazz
+ movl %edx, -8(%esp) # push parameter method->clazz
+ lea -8(%esp), %esp
+ call dvmResolveStaticField # call: (const ClassObject* referrer, u4 ifieldIdx)
+ # return: StaticField*
+ cmp $$0, %eax # check if initalization failed
+ lea 8(%esp), %esp
+ je common_exceptionThrown # failed; handle exception
+ movl %eax, %ecx # %ecx<- result
+
+.L${opcode}_finish:
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG rINST # rINST<- vAA
+ movl rINST, offStaticField_value(%ecx) # field value<- vAA
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_SPUT_BOOLEAN.S b/vm/mterp/x86-atom/OP_SPUT_BOOLEAN.S
new file mode 100644
index 0000000..9bb64f8
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SPUT_BOOLEAN.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SPUT_BOOLEAN.S
+ */
+
+%include "x86-atom/OP_SPUT.S"
diff --git a/vm/mterp/x86-atom/OP_SPUT_BYTE.S b/vm/mterp/x86-atom/OP_SPUT_BYTE.S
new file mode 100644
index 0000000..1d4f016
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SPUT_BYTE.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SPUT_BYTE.S
+ */
+
+%include "x86-atom/OP_SPUT.S"
diff --git a/vm/mterp/x86-atom/OP_SPUT_CHAR.S b/vm/mterp/x86-atom/OP_SPUT_CHAR.S
new file mode 100644
index 0000000..58300ef
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SPUT_CHAR.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SPUT_CHAR.S
+ */
+
+%include "x86-atom/OP_SPUT.S"
diff --git a/vm/mterp/x86-atom/OP_SPUT_OBJECT.S b/vm/mterp/x86-atom/OP_SPUT_OBJECT.S
new file mode 100644
index 0000000..88ebaf7
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SPUT_OBJECT.S
@@ -0,0 +1,70 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SPUT_OBJECT.S
+ *
+ * Code: Generic 32-bit static field "put" operation. Uses no substitutions.
+ *
+ * For: sput-boolean, sput-byte, sput-char, sput-object, sput, sput-short
+ *
+ * Description: Perform the identified object static field operation
+ * with the identified static field; store the field value
+ * register.
+ *
+ * Format: AA|op BBBB (21c)
+ *
+ * Syntax: op vAA, string@BBBB
+ */
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_methodClassDex(%edx), %ecx # %ecx<- pDvmDex
+ FETCH 1, %eax # %eax<- BBBB
+ movl offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+ cmp $$0, (%ecx, %eax, 4) # check for null ptr; resolved StaticField
+ movl (%ecx, %eax, 4), %ecx # %ecx<- resolved StaticField
+ je .L${opcode}_resolve
+ jmp .L${opcode}_finish
+%break
+
+.L${opcode}_resolve:
+ movl offGlue_method(%edx), %edx # %edx <- glue->method
+ EXPORT_PC # in case an exception is thrown
+ movl %eax, -4(%esp) # push parameter CCCC; field ref
+ movl offMethod_clazz(%edx), %edx # %edx<- method->clazz
+ movl %edx, -8(%esp) # push parameter method->clazz
+ lea -8(%esp), %esp
+ call dvmResolveStaticField # call: (const ClassObject* referrer, u4 ifieldIdx)
+ # return: StaticField*
+ cmp $$0, %eax # check if initalization failed
+ lea 8(%esp), %esp
+ je common_exceptionThrown # failed; handle exception
+ movl %eax, %ecx # %ecx<- result
+
+.L${opcode}_finish:
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG rINST # rINST<- vAA
+
+
+ movl rINST, offStaticField_value(%ecx) # field value<- vAA
+ testl rINST, rINST # stored null object ptr?
+ je 1f
+ movl rGLUE, %edx # get glue
+ movl offField_clazz(%ecx), %ecx # ecx<- field->clazz
+ movl offGlue_cardTable(%edx), %edx # get card table base
+ shrl $$GC_CARD_SHIFT, %ecx # head to card number
+ movb %dl, (%edx, %ecx) # mark card
+1:
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/OP_SPUT_OBJECT_VOLATILE.S b/vm/mterp/x86-atom/OP_SPUT_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..b368e31
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SPUT_OBJECT_VOLATILE.S
@@ -0,0 +1 @@
+%include "x86/OP_SPUT_OBJECT.S"
diff --git a/vm/mterp/x86-atom/OP_SPUT_SHORT.S b/vm/mterp/x86-atom/OP_SPUT_SHORT.S
new file mode 100644
index 0000000..1ecc562
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SPUT_SHORT.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SPUT_SHORT.S
+ */
+
+%include "x86-atom/OP_SPUT.S"
diff --git a/vm/mterp/x86-atom/OP_SPUT_VOLATILE.S b/vm/mterp/x86-atom/OP_SPUT_VOLATILE.S
new file mode 100644
index 0000000..7ee4140
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SPUT_VOLATILE.S
@@ -0,0 +1 @@
+%include "x86/OP_SPUT.S"
diff --git a/vm/mterp/x86-atom/OP_SPUT_WIDE.S b/vm/mterp/x86-atom/OP_SPUT_WIDE.S
new file mode 100644
index 0000000..7d661cf
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SPUT_WIDE.S
@@ -0,0 +1,65 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SPUT_WIDE.S
+ *
+ * Code: Generic 32-bit static field "put" operation. Uses no substitutions.
+ *
+ * For: sput-boolean, sput-byte, sput-char, sput-object, sput, sput-short
+ *
+ * Description: Perform the identified object static field operation
+ * with the identified static field; store the field value
+ * register.
+ *
+ * Format: AA|op BBBB (21c)
+ *
+ * Syntax: op vAA, string@BBBB
+ */
+
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ movl offGlue_methodClassDex(%eax), %ecx # %ecx<- glue->pDvmDex
+ FETCH 1, %edx # %edx<- BBBB
+ movl offDvmDex_pResFields(%ecx), %ecx # %ecx<- pResFields
+ cmp $$0, (%ecx, %edx, 4) # check for null ptr; resolved StaticField ptr
+ movl (%ecx, %edx, 4), %ecx # %ecx<- resolved StaticField ptr
+ je .L${opcode}_resolve
+
+.L${opcode}_finish:
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movq (rFP, rINST, 4), %xmm0 # %xmm0<- vAA
+ movq %xmm0, offStaticField_value(%ecx) # field value<- field value
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
+%break
+
+ /*
+ * Continuation if the field has not yet been resolved.
+ * %edx: BBBB field ref
+ */
+
+.L${opcode}_resolve:
+ movl offGlue_method(%eax), %eax # %eax <- glue->method
+ EXPORT_PC # in case an exception is thrown
+ movl %edx, -4(%esp) # push parameter CCCC; field ref
+ movl offMethod_clazz(%eax), %eax # %eax<- method->clazz
+ movl %eax, -8(%esp)
+ lea -8(%esp), %esp
+ call dvmResolveStaticField # call: (const ClassObject* referrer, u4 ifieldIdx)
+ # return: StaticField*
+ lea 8(%esp), %esp
+ cmp $$0, %eax # check if initalization failed
+ movl %eax, %ecx # %ecx<- result
+ jne .L${opcode}_finish # success, continue
+ jmp common_exceptionThrown # failed; handle exception
diff --git a/vm/mterp/x86-atom/OP_SUB_DOUBLE.S b/vm/mterp/x86-atom/OP_SUB_DOUBLE.S
new file mode 100644
index 0000000..5f2dbb6
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SUB_DOUBLE.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SUB_DOUBLE.S
+ */
+
+%include "x86-atom/binopWide.S" {"instr":"subsd %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_SUB_DOUBLE_2ADDR.S b/vm/mterp/x86-atom/OP_SUB_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..cd6f12a
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SUB_DOUBLE_2ADDR.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SUB_DOUBLE_2ADDR.S
+ */
+
+%include "x86-atom/binopWide2addr.S" {"instr":"subsd %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_SUB_FLOAT.S b/vm/mterp/x86-atom/OP_SUB_FLOAT.S
new file mode 100644
index 0000000..eb79d79
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SUB_FLOAT.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SUB_FLOAT.S
+ */
+
+%include "x86-atom/binopF.S" {"instr":"subss %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_SUB_FLOAT_2ADDR.S b/vm/mterp/x86-atom/OP_SUB_FLOAT_2ADDR.S
new file mode 100644
index 0000000..77f23f1
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SUB_FLOAT_2ADDR.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SUB_FLOAT_2ADDR.S
+ */
+
+%include "x86-atom/binopF2addr.S" {"instr":"subss %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_SUB_INT.S b/vm/mterp/x86-atom/OP_SUB_INT.S
new file mode 100644
index 0000000..8d342cd
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SUB_INT.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SUB_INT.S
+ */
+
+%include "x86-atom/binop.S" {"instr":"subl %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_SUB_INT_2ADDR.S b/vm/mterp/x86-atom/OP_SUB_INT_2ADDR.S
new file mode 100644
index 0000000..4d295a4
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SUB_INT_2ADDR.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SUB_INT_2ADDR.S
+ */
+
+%include "x86-atom/binop2addr.S" {"instr":"subl %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_SUB_INT_LIT8.S b/vm/mterp/x86-atom/OP_SUB_INT_LIT8.S
new file mode 100644
index 0000000..8bf0902
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SUB_INT_LIT8.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SUB_INT_LIT8.S
+ */
+
+%include "x86-atom/binopLit8.S" {"instr":"subl %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_SUB_LONG.S b/vm/mterp/x86-atom/OP_SUB_LONG.S
new file mode 100644
index 0000000..84e25d0
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SUB_LONG.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SUB_LONG.S
+ */
+
+%include "x86-atom/binopWide.S" {"instr":"psubq %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_SUB_LONG_2ADDR.S b/vm/mterp/x86-atom/OP_SUB_LONG_2ADDR.S
new file mode 100644
index 0000000..ca6a2ad
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_SUB_LONG_2ADDR.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_SUB_LONG_2ADDR.S
+ */
+
+%include "x86-atom/binopWide2addr.S" {"instr":"psubq %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_THROW.S b/vm/mterp/x86-atom/OP_THROW.S
new file mode 100644
index 0000000..120b1e9
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_THROW.S
@@ -0,0 +1,37 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_THROW.S
+ *
+ * Code: Throw an exception
+ *
+ * For: throw
+ *
+ * Description: Throw an exception object in the current thread.
+ *
+ * Format: AA|op (11x)
+ *
+ * Syntax: op vAA
+ */
+
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ EXPORT_PC # export the pc
+ GET_VREG rINST # rINST<- vAA
+ cmp $$0, rINST # check for null
+ movl offGlue_self(%eax), %ecx # %ecx<- glue->self
+ je common_errNullObject # handle null object
+ movl rINST, offThread_exception(%ecx) # thread->exception<- object
+ jmp common_exceptionThrown # handle exception
diff --git a/vm/mterp/x86-atom/OP_THROW_VERIFICATION_ERROR.S b/vm/mterp/x86-atom/OP_THROW_VERIFICATION_ERROR.S
new file mode 100644
index 0000000..f920b50
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_THROW_VERIFICATION_ERROR.S
@@ -0,0 +1,40 @@
+ /* Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_THROW_VERIFICATION_ERROR.S
+ *
+ * Code:
+ *
+ * For: throw-verification-error
+ *
+ * Description: Throws an exception for an error discovered during verification.
+ * The exception is indicated by AA with details provided by BBBB.
+ *
+ * Format: AA|op BBBB (21c)
+ *
+ * Syntax: op vAA, ref@BBBB
+ */
+
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl offGlue_method(%edx), %ecx # %ecx<- glue->method
+ EXPORT_PC # in case an exception is thrown
+ FETCH 1, %eax # %eax<- BBBB
+ movl %eax, -4(%esp) # push parameter BBBB; ref
+ movl rINST, -8(%esp) # push parameter AA
+ movl %ecx, -12(%esp) # push parameter glue->method
+ lea -12(%esp), %esp
+ call dvmThrowVerificationError # call: (const Method* method, int kind, int ref)
+ jmp common_exceptionThrown # failed; handle exception
diff --git a/vm/mterp/x86-atom/OP_UNUSED_3E.S b/vm/mterp/x86-atom/OP_UNUSED_3E.S
new file mode 100644
index 0000000..d91d469
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_3E.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_UNUSED_3E.S
+ */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_UNUSED_3F.S b/vm/mterp/x86-atom/OP_UNUSED_3F.S
new file mode 100644
index 0000000..84cc69d
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_3F.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_UNUSED_3F.S
+ */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_UNUSED_40.S b/vm/mterp/x86-atom/OP_UNUSED_40.S
new file mode 100644
index 0000000..e0853da
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_40.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_UNUSED_40.S
+ */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_UNUSED_41.S b/vm/mterp/x86-atom/OP_UNUSED_41.S
new file mode 100644
index 0000000..a30fbe3
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_41.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_UNUSED_41.S
+ */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_UNUSED_42.S b/vm/mterp/x86-atom/OP_UNUSED_42.S
new file mode 100644
index 0000000..64c5648
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_42.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_UNUSED_42.S
+ */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_UNUSED_43.S b/vm/mterp/x86-atom/OP_UNUSED_43.S
new file mode 100644
index 0000000..4a6120b
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_43.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_UNUSED_43.S
+ */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_UNUSED_73.S b/vm/mterp/x86-atom/OP_UNUSED_73.S
new file mode 100644
index 0000000..9808865
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_73.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_UNUSED_73.S
+ */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_UNUSED_79.S b/vm/mterp/x86-atom/OP_UNUSED_79.S
new file mode 100644
index 0000000..6ebd8ff
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_79.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_UNUSED_79.S
+ */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_UNUSED_7A.S b/vm/mterp/x86-atom/OP_UNUSED_7A.S
new file mode 100644
index 0000000..79a22d0
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_7A.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_UNUSED_7A.S
+ */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_UNUSED_E3.S b/vm/mterp/x86-atom/OP_UNUSED_E3.S
new file mode 100644
index 0000000..2921274
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_E3.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_UNUSED_E3.S
+ */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_UNUSED_E4.S b/vm/mterp/x86-atom/OP_UNUSED_E4.S
new file mode 100644
index 0000000..69eb419
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_E4.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_UNUSED_E4.S
+ */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_UNUSED_E5.S b/vm/mterp/x86-atom/OP_UNUSED_E5.S
new file mode 100644
index 0000000..2172369
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_E5.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_UNUSED_E5.S
+ */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_UNUSED_E6.S b/vm/mterp/x86-atom/OP_UNUSED_E6.S
new file mode 100644
index 0000000..1464cbd
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_E6.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_UNUSED_E6.S
+ */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_UNUSED_E7.S b/vm/mterp/x86-atom/OP_UNUSED_E7.S
new file mode 100644
index 0000000..67029e4
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_E7.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_UNUSED_E7.S
+ */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_UNUSED_F1.S b/vm/mterp/x86-atom/OP_UNUSED_F1.S
new file mode 100644
index 0000000..b6c264a
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_F1.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_UNUSED_F1.S
+ */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_UNUSED_FC.S b/vm/mterp/x86-atom/OP_UNUSED_FC.S
new file mode 100644
index 0000000..24b104c
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_FC.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_UNUSED_FC.S
+ */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_UNUSED_FD.S b/vm/mterp/x86-atom/OP_UNUSED_FD.S
new file mode 100644
index 0000000..b3cc6fb
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_FD.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_UNUSED_FD.S
+ */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_UNUSED_FE.S b/vm/mterp/x86-atom/OP_UNUSED_FE.S
new file mode 100644
index 0000000..435624f
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_FE.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_UNUSED_FE.S
+ */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_UNUSED_FF.S b/vm/mterp/x86-atom/OP_UNUSED_FF.S
new file mode 100644
index 0000000..e831696
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_UNUSED_FF.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_UNUSED_FF.S
+ */
+
+%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_USHR_INT.S b/vm/mterp/x86-atom/OP_USHR_INT.S
new file mode 100644
index 0000000..a1d91c6
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_USHR_INT.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_USHR_INT.S
+ */
+
+%include "x86-atom/binopS.S" {"instr":"shr %cl, %edx"}
diff --git a/vm/mterp/x86-atom/OP_USHR_INT_2ADDR.S b/vm/mterp/x86-atom/OP_USHR_INT_2ADDR.S
new file mode 100644
index 0000000..9ee9c66
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_USHR_INT_2ADDR.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_USHR_INT_2ADDR.S
+ */
+
+%include "x86-atom/binopS2addr.S" {"instr":"shr %cl, %edx"}
diff --git a/vm/mterp/x86-atom/OP_USHR_INT_LIT8.S b/vm/mterp/x86-atom/OP_USHR_INT_LIT8.S
new file mode 100644
index 0000000..5a54df7
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_USHR_INT_LIT8.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_USHR_INT_LIT8.S
+ */
+
+%include "x86-atom/binopLit8S.S" {"instr":"shr %cl, %edx"}
diff --git a/vm/mterp/x86-atom/OP_USHR_LONG.S b/vm/mterp/x86-atom/OP_USHR_LONG.S
new file mode 100644
index 0000000..1c404f0
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_USHR_LONG.S
@@ -0,0 +1,39 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_USHR_LONG.S
+ *
+ * Code: Performs an unsigned shift right long operation. Uses no substitutions.
+ *
+ * For: ushr-long
+ *
+ * Description: Perform a binary shift operation using two source registers
+ * where one is the shift amount and the other is the value to shift.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_CC 1, %eax # %eax<- CC
+ FETCH_BB 1, %edx # %edx<- BB
+ movsd .LshiftMask, %xmm2 # %xmm2<- mask for the shift bits
+ movss (rFP, %eax, 4), %xmm0 # %xmm0<- vCC
+ pand %xmm2, %xmm0 # %xmm0<- masked shift bits
+ movsd (rFP, %edx, 4), %xmm1 # %xmm1<- vBB
+ psrlq %xmm0, %xmm1 # %xmm1<- shifted vBB
+ movsd %xmm1, (rFP, rINST, 4) # vAA<- shifted vBB
+ FINISH 2 # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_USHR_LONG_2ADDR.S b/vm/mterp/x86-atom/OP_USHR_LONG_2ADDR.S
new file mode 100644
index 0000000..31cb5bc
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_USHR_LONG_2ADDR.S
@@ -0,0 +1,41 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_USHR_LONG_2ADDR.S
+ *
+ * Code: Performs an unsigned shift right long operation. Uses no substiutions.
+ *
+ * For: ushr-long/2addr
+ *
+ * Description: Perform a binary shift operation using two source registers
+ * where the fist is the value to shift and the second is the
+ * shift amount. Store the result in the first source register.
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $$4, %edx # %edx<- B
+ andl $$15, rINST # rINST<- A
+ movq .LshiftMask, %xmm2 # %xmm2<- mask for the shift bits
+ movss (rFP, %edx, 4), %xmm0 # %xmm0<- vB
+ movq (rFP, rINST, 4), %xmm1 # %xmm1<- vA
+ pand %xmm2, %xmm0 # %xmm0<- masked shift bits
+ psrlq %xmm0, %xmm1 # %xmm1<- shifted vA
+ movq %xmm1, (rFP, rINST, 4) # vA<- shifted vA
+ FINISH 1 # jump to next instruction
diff --git a/vm/mterp/x86-atom/OP_XOR_INT.S b/vm/mterp/x86-atom/OP_XOR_INT.S
new file mode 100644
index 0000000..d36b83f
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_XOR_INT.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_XOR_INT.S
+ */
+
+%include "x86-atom/binop.S" {"instr":"xor %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_XOR_INT_2ADDR.S b/vm/mterp/x86-atom/OP_XOR_INT_2ADDR.S
new file mode 100644
index 0000000..0bab865
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_XOR_INT_2ADDR.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_XOR_INT_2ADDR.S
+ */
+
+%include "x86-atom/binop2addr.S" {"instr":"xor %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_XOR_INT_LIT16.S b/vm/mterp/x86-atom/OP_XOR_INT_LIT16.S
new file mode 100644
index 0000000..a26bcc5
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_XOR_INT_LIT16.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_XOR_INT_LIT16.S
+ */
+
+%include "x86-atom/binopLit16.S" {"instr":"xor %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_XOR_INT_LIT8.S b/vm/mterp/x86-atom/OP_XOR_INT_LIT8.S
new file mode 100644
index 0000000..9a3c8e3
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_XOR_INT_LIT8.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_XOR_INT_LIT8.S
+ */
+
+%include "x86-atom/binopLit8.S" {"instr":"xor %edx, %ecx"}
diff --git a/vm/mterp/x86-atom/OP_XOR_LONG.S b/vm/mterp/x86-atom/OP_XOR_LONG.S
new file mode 100644
index 0000000..58a8384
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_XOR_LONG.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_XOR_LONG.S
+ */
+
+%include "x86-atom/binopWide.S" {"instr":"pxor %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/OP_XOR_LONG_2ADDR.S b/vm/mterp/x86-atom/OP_XOR_LONG_2ADDR.S
new file mode 100644
index 0000000..6b42cbd
--- /dev/null
+++ b/vm/mterp/x86-atom/OP_XOR_LONG_2ADDR.S
@@ -0,0 +1,20 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: OP_XOR_LONG_2ADDR.S
+ */
+
+%include "x86-atom/binopWide2addr.S" {"instr":"pxor %xmm1, %xmm0"}
diff --git a/vm/mterp/x86-atom/TODO.txt b/vm/mterp/x86-atom/TODO.txt
new file mode 100644
index 0000000..72d8855
--- /dev/null
+++ b/vm/mterp/x86-atom/TODO.txt
@@ -0,0 +1,4 @@
+Items requiring attention:
+
+(lo) Implement OP_BREAKPOINT
+(lo) Implement OP_*_VOLATILE (12 instructions)
diff --git a/vm/mterp/x86-atom/bincmp.S b/vm/mterp/x86-atom/bincmp.S
new file mode 100644
index 0000000..a8fbed5
--- /dev/null
+++ b/vm/mterp/x86-atom/bincmp.S
@@ -0,0 +1,44 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: bincmp.S
+ *
+ * Code: Generic 32-bit comparison operation. Provides a "revcmp"
+ * variable to specify the reverse comparison to perform.
+ *
+ * For: if-eq, if-ge, if-gt, if-le, if-lt, if-ne
+ *
+ * Description: Branch to the given destination if the comparison
+ * test between the given registers values is true.
+ *
+ * Format: B|A|op CCCC (22t)
+ *
+ * Syntax: op vA, vB, +CCCC
+ */
+
+ movl rINST, %eax # %eax<- BA
+ andl $$15, rINST # rINST<- A
+ shr $$4, %eax # %eax<- B
+ GET_VREG rINST # rINST<- vA
+ movl $$4, %edx # %edx<- 4
+ cmp (rFP, %eax, 4), rINST # compare vA and vB
+ j${revcmp} 1f # goto next instruction if reverse
+ # comparison is true
+ FETCHs 1, %edx # %edx<- +CCCC, Branch offset
+ sal $$1, %edx
+ js common_periodicChecks_backwardBranch
+1:
+ FINISH_RB %edx, %ecx # jump to next instruction
diff --git a/vm/mterp/x86-atom/binop.S b/vm/mterp/x86-atom/binop.S
new file mode 100644
index 0000000..1706b72
--- /dev/null
+++ b/vm/mterp/x86-atom/binop.S
@@ -0,0 +1,39 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binop.S
+ *
+ * Code: Generic 32-bit binary operation. Provides an "instr" line to
+ * specify an instruction that performs "%ecx = %ecx op %edx"
+ *
+ * For: add-int, and-int, mul-int, or-int, sub-int, xor-int
+ *
+ * Description: Perform a binary operation on two source registers
+ * and store the result in a destination register.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %ecx # %ecx<- vBB
+ GET_VREG %edx # %edx<- vCC
+ $instr # %ecx<- vBB op vCC
+ SET_VREG %ecx, rINST # vAA<- %ecx; result
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/binop2addr.S b/vm/mterp/x86-atom/binop2addr.S
new file mode 100644
index 0000000..b26b25a
--- /dev/null
+++ b/vm/mterp/x86-atom/binop2addr.S
@@ -0,0 +1,45 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binop2addr.S
+ *
+ * Code: Generic 32-bit "/2addr" binary operation. Provides an
+ * "instr" line to specify an instruction that performs
+ * "%ecx = %ecx op %edx".
+ *
+ * For: add-int/2addr, and-int/2addr, mul-int/2addr, or-int/2addr,
+ * sub-int/2addr, xor-int/2addr
+ *
+ * Description: Perform a binary operation on two sources registers
+ * and store the result in the first source register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $$4, %edx # %edx<- B
+ andl $$15, rINST # rINST<- A
+ movl rINST, %ecx # %ecx<- A
+ FFETCH_ADV 1, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %edx # %edx<- vB
+ GET_VREG %ecx # %ecx<- vA
+ $instr # %ecx<- vA op vB
+ SET_VREG %ecx, rINST # vAA<- %ecx; result
+ FGETOP_JMP 1, %eax # jump to next instruction; getop, jmp
+
+
diff --git a/vm/mterp/x86-atom/binopD.S b/vm/mterp/x86-atom/binopD.S
new file mode 100644
index 0000000..20a2e9a
--- /dev/null
+++ b/vm/mterp/x86-atom/binopD.S
@@ -0,0 +1,61 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopD.S
+ *
+ * Code: 32-bit integer divide operation. If "div" is set, the code
+ * returns the quotient, else it returns the remainder.
+ * Also, a divide-by-zero check is done.
+ *
+ * For: div-int, rem-int
+ *
+ * Description: Perform a binary operation on two source
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+%default {"div":"1"}
+
+ FETCH_BB 1, %eax # %eax<- BB
+ FETCH_CC 1, %ecx # %ecx<- CC
+ GET_VREG %eax # %eax<- vBB
+ GET_VREG %ecx # %ecx<- vCC
+ cmp $$0, %ecx # check for divide by zero
+ je common_errDivideByZero # handle divide by zero
+ cmpl $$-1, %ecx # handle -1 special case divide error
+ jne .L${opcode}_noerror
+ cmpl $$0x80000000,%eax # handle min int special case divide error
+ je .L${opcode}_break
+.L${opcode}_noerror:
+ cdq # sign-extend %eax to %edx
+ idiv %ecx # divide %edx:%eax by %ecx
+ .if $div
+ SET_VREG %eax rINST # vAA<- %eax (quotient)
+ .else
+ SET_VREG %edx rINST # vAA<- %edx (remainder)
+ .endif
+ jmp .L${opcode}_break2
+%break
+.L${opcode}_break:
+ .if $div
+ movl $$0x80000000, (rFP, rINST, 4) # vAA<- min int
+ .else
+ movl $$0, (rFP, rINST, 4) # vAA<- 0
+ .endif
+.L${opcode}_break2:
+ FINISH 2 # jump to next instruction
diff --git a/vm/mterp/x86-atom/binopD2addr.S b/vm/mterp/x86-atom/binopD2addr.S
new file mode 100644
index 0000000..392b46b
--- /dev/null
+++ b/vm/mterp/x86-atom/binopD2addr.S
@@ -0,0 +1,68 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopD2addr.S
+ *
+ * Code: 32-bit "/2addr" integer divde operation. If "div"
+ * is set, the code returns the quotient, else it returns
+ * the remainder. Also, a divide-by-zero check is done.
+ *
+ * For: div-int/2addr, rem-int/2addr
+ *
+ * Description: Perform a binary operation on two sources registers
+ * and store the result in the first source register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+%default {"div":"1"}
+ movl rINST, %ecx # %ecx<- BA
+ andl $$15, rINST # rINST<- A, to be used as dest
+ movl rINST, %eax # %eax<- A
+ shr $$4, %ecx # %ecx<- B
+ GET_VREG %eax # %eax<- vA
+ GET_VREG %ecx # %edx<- vB
+ cmp $$0, %ecx # check for divide by zero
+ je common_errDivideByZero # handle divide by zero
+ cmpl $$-1, %ecx # handle -1 special case divide error
+ jne .L${opcode}_noerror
+ cmpl $$0x80000000,%eax # handle min int special case divide error
+ je .L${opcode}_break
+.L${opcode}_noerror:
+ cdq # sign-extend %eax to %edx
+ idiv %ecx # divide %edx:%eax by %ecx
+ .if $div
+ SET_VREG %eax rINST # vAA<- %eax (quotient)
+ .else
+ SET_VREG %edx rINST # vAA<- %edx (remainder)
+ .endif
+ jmp .L${opcode}_break2
+ #FFETCH_ADV 1, %edx # %ecx<- next instruction hi; fetch, advance
+ #FGETOP_JMP 1, %edx # jump to next instruction; getop, jmp
+
+%break
+.L${opcode}_break:
+ .if $div
+ movl $$0x80000000, (rFP, rINST, 4) # vAA<- min int
+ .else
+ movl $$0, (rFP, rINST, 4) # vAA<- 0
+ .endif
+.L${opcode}_break2:
+ FFETCH_ADV 1, %edx # %ecx<- next instruction hi; fetch, advance
+ FGETOP_JMP 1, %edx # jump to next instruction; getop, jmp
+
diff --git a/vm/mterp/x86-atom/binopDLit16.S b/vm/mterp/x86-atom/binopDLit16.S
new file mode 100644
index 0000000..3e67d0a
--- /dev/null
+++ b/vm/mterp/x86-atom/binopDLit16.S
@@ -0,0 +1,66 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopDLit16.S
+ *
+ * Code: 32-bit "lit16" divide operation. If "div" is set, the code
+ * returns the quotient, else it returns the remainder.
+ * Also, a divide-by-zero check is done.
+ *
+ * For: div-int/lit16, rem-int/lit16
+ *
+ * Description: Perform a binary operation on a register and a
+ * sign extended 16-bit literal value
+ *
+ * Format: B|A|op CCCC (22s)
+ *
+ * Syntax: op vA, vB, #+CCCC
+ */
+
+%default {"div":"1"}
+
+ movl rINST, %eax # %eax<- BA
+ shr $$4, %eax # %eax<- B
+ FETCHs 1, %ecx # %ecx<- +CCCC, sign-extended literal
+ cmp $$0, %ecx # check for divide by zero
+ GET_VREG %eax # %eax<- vB
+ je common_errDivideByZero # handle divide by zero
+ andl $$15, rINST # rINST<- A
+ cmpl $$-1, %ecx # handle -1 special case divide error
+ jne .L${opcode}_noerror
+ cmpl $$0x80000000,%eax # handle min int special case divide error
+ je .L${opcode}_break
+.L${opcode}_noerror:
+ cdq # sign-extend %eax to %edx
+ idiv %ecx # divide %edx:%eax by %ecx
+ #FFETCH_ADV 2, %ecx # %ecx<- next instruction hi; fetch, advance
+ .if $div
+ SET_VREG %eax rINST # vA<- %eax (quotient)
+ .else
+ SET_VREG %edx rINST # vA<- %edx (remainder)
+ .endif
+ jmp .L${opcode}_break2
+
+%break
+.L${opcode}_break:
+ .if $div
+ movl $$0x80000000, (rFP, rINST, 4) # vAA<- min int
+ .else
+ movl $$0, (rFP, rINST, 4) # vAA<- 0
+ .endif
+.L${opcode}_break2:
+
+ FINISH 2 # jump to next instruction
diff --git a/vm/mterp/x86-atom/binopDLit8.S b/vm/mterp/x86-atom/binopDLit8.S
new file mode 100644
index 0000000..f197714
--- /dev/null
+++ b/vm/mterp/x86-atom/binopDLit8.S
@@ -0,0 +1,65 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopDLit8.S
+ *
+ * Code: 32-bit "lit8" divide operation. If "div" is set, the code
+ * returns the quotient, else it returns the remainder.
+ * Also, a divide-by-zero check is done.
+ *
+ * For: div-int/lit8, rem-int/lit8
+ *
+ * Description: Perform a binary operation on a register and a
+ * signe extended 8-bit literal value
+ *
+ * Format: AA|op CC|BB (22b)
+ *
+ * Syntax: op vAA, vBB, #+CC
+ */
+
+%default {"div":"1"}
+
+ FETCH_BB 1, %eax # %eax<- BB
+ FETCH_CCs 1, %ecx # %ecx<- +CC, sign-extended literal
+ cmp $$0, %ecx # check for divide by zero
+ GET_VREG %eax # %eax<- vBB
+ je common_errDivideByZero # handle divide by zero
+
+ cmpl $$-1, %ecx # handle -1 special case divide error
+ jne .L${opcode}_noerror
+ cmpl $$0x80000000,%eax # handle min int special case divide error
+ je .L${opcode}_break
+.L${opcode}_noerror:
+ cdq # sign-extend %eax to %edx
+ idiv %ecx # divide %edx:%eax by %ecx
+ #FFETCH_ADV 2, %ecx # %ecx<- next instruction hi; fetch, advance
+ .if $div
+ SET_VREG %eax rINST # vAA<- %eax (quotient)
+ .else
+ SET_VREG %edx rINST # vAA<- %edx (remainder)
+ .endif
+ jmp .L${opcode}_break2
+%break
+.L${opcode}_break:
+ .if $div
+ movl $$0x80000000, (rFP, rINST, 4) # vAA<- min int
+ .else
+ movl $$0, (rFP, rINST, 4) # vAA<- 0
+ .endif
+
+.L${opcode}_break2:
+ FINISH 2 # jump to next instruction
+ #FGETOP_JMP 2, %ecx # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/binopDivRemLong.S b/vm/mterp/x86-atom/binopDivRemLong.S
new file mode 100644
index 0000000..93ed4f8
--- /dev/null
+++ b/vm/mterp/x86-atom/binopDivRemLong.S
@@ -0,0 +1,52 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopDivRemLong.S
+ *
+ * Code: 64-bit long divide operation. Variable
+ * "func" defines the function called to do the operation.
+ *
+ * For: div-long, rem-long
+ *
+ * Description: Perform a binary operation on two source registers
+ * and store the result in a destination register.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+%default {"func":"__divdi3"}
+
+ FETCH_CC 1, %edx # %edx<- CC
+ movl (rFP, %edx, 4), %eax # %eax<- vCC
+ movl 4(rFP, %edx, 4), %ecx # %ecx<- vCC+1
+ movl %eax, -8(%esp) # push arg vCC
+ or %ecx, %eax # check for divide by zero
+ je common_errDivideByZero # handle divide by zero
+ FETCH_BB 1, %edx # %edx<- BB
+ movl %ecx, -4(%esp) # push arg vCC+1
+ movq (rFP, %edx, 4), %xmm0 # %xmm0<- vBB,vBB+1
+ jmp .L${opcode}_finish
+%break
+.L${opcode}_finish:
+ movq %xmm0, -16(%esp) # push arg vBB,vBB+1
+ lea -16(%esp), %esp
+ call $func # call func
+ lea 16(%esp), %esp
+ movl %eax, (rFP, rINST, 4) # vAA<- return low
+ movl %edx, 4(rFP, rINST, 4) # vAA+1<- return high
+ FINISH 2 # jump to next instruction
diff --git a/vm/mterp/x86-atom/binopDivRemLong2Addr.S b/vm/mterp/x86-atom/binopDivRemLong2Addr.S
new file mode 100644
index 0000000..aa427de
--- /dev/null
+++ b/vm/mterp/x86-atom/binopDivRemLong2Addr.S
@@ -0,0 +1,54 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopDivRemLong2Addr.S
+ *
+ * Code: 64-bit "/2addr" long divide operation. Variable
+ * "func" defines the function called to do the operation.
+ *
+ * For: div-long/2addr, rem-long/2addr
+ *
+ * Description: Perform a binary operation on two sources registers
+ * and store the result in the first source register.
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+%default {"func":"__divdi3"}
+
+ movl rINST, %edx # %edx<- BA
+ shr $$4, %edx # %edx<- B
+ and $$15, rINST # rINST<- A
+ movl (rFP, %edx, 4), %eax # %eax<- vB
+ movl %eax, -12(%esp) # push arg vB
+ movl 4(rFP, %edx, 4), %ecx # %ecx<- vB+1
+ or %ecx, %eax # check for divide by zero
+ je common_errDivideByZero # handle divide by zero
+ movl %ecx, -8(%esp) # push arg vB+1
+ movq (rFP, rINST, 4), %xmm0 # %xmm0<- vA,vA+1
+ jmp .L${opcode}_break
+%break
+.L${opcode}_break:
+ movq %xmm0, -20(%esp) # push arg vA, vA+1
+ lea -20(%esp), %esp
+ call $func # call func
+ lea 20(%esp), %esp
+ movl %eax, (rFP, rINST, 4) # vA<- return low
+ movl %edx, 4(rFP, rINST, 4) # vA<- return high
+ FFETCH_ADV 1, %ecx # %ecx<- next instruction hi; fetch, advance
+ FGETOP_JMP 1, %ecx # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/binopF.S b/vm/mterp/x86-atom/binopF.S
new file mode 100644
index 0000000..0c07b97
--- /dev/null
+++ b/vm/mterp/x86-atom/binopF.S
@@ -0,0 +1,39 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopF.S
+ *
+ * Code: Generic 32-bit binary operation. Provides an "instr" line to
+ * specify an instruction that performs "%xmm0 = %xmm0 op %xmm1"
+ *
+ * For: add-float, mul-float, sub-float
+ *
+ * Description: Perform a binary operation on two source registers
+ * and store the result in a destination register.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movss (rFP, %ecx, 4), %xmm0 # %xmm0<-vBB
+ movss (rFP, %edx, 4), %xmm1 # %xmm1<- vCC
+ $instr # %xmm0<- vBB op vCC
+ movss %xmm0, (rFP, rINST, 4) # vAA<- %xmm0; result
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/binopF2addr.S b/vm/mterp/x86-atom/binopF2addr.S
new file mode 100644
index 0000000..135ca0c
--- /dev/null
+++ b/vm/mterp/x86-atom/binopF2addr.S
@@ -0,0 +1,41 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopF2addr.S
+ *
+ * Code: Generic 32-bit "/2addr" binary operation. Provides an
+ * "instr" line to specify an instruction that performs
+ * "%xmm0 = %xmm0 op %xmm1".
+ *
+ * For: add-float/2addr, mul-float/2addr, sub-float/2addr
+ *
+ * Description: Perform a binary operation on two sources registers
+ * and store the result in the first source register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %ecx # %ecx<- BA
+ andl $$15, %ecx # %ecx<- A
+ shr $$4, rINST # rINST<- B
+ FFETCH_ADV 1, %edx # %ecx<- next instruction hi; fetch, advance
+ movss (rFP, %ecx, 4), %xmm0 # %xmm0<- vA
+ movss (rFP, rINST, 4), %xmm1 # %xmm1<- vB
+ $instr # %xmm0<- vA op vB
+ movss %xmm0, (rFP, %ecx, 4) # vA<- %xmm0; result
+ FGETOP_JMP 1, %edx # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/binopLit16.S b/vm/mterp/x86-atom/binopLit16.S
new file mode 100644
index 0000000..4972b4d
--- /dev/null
+++ b/vm/mterp/x86-atom/binopLit16.S
@@ -0,0 +1,43 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopLit16.S
+ *
+ * Code: 32-bit "lit16" operation. Provides an "instr" line to
+ * specify an instruction that performs "%ecx = %ecx op %edx"
+ *
+ *
+ * For: add-int/lit16, and-int/lit16, mul-int/lit16, or-int/lit16
+ * xor-int/lit16
+ *
+ * Description: Perform a binary operation on a register and a
+ * sign extended 16-bit literal value and store the
+ * result in a destination register.
+ *
+ * Format: B|A|op CCCC (22s)
+ *
+ * Syntax: op vA, vB, #+CCCC
+ */
+
+ movl rINST, %ecx # %ecx<- BA
+ shr $$4, %ecx # %ecx<- B
+ andl $$15, rINST # rINST<- A
+ FETCHs 1, %edx # %edx<- +CCCC, sign-extended literal
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %ecx # %ecx<- vB
+ $instr # %ecx<- vA op vB
+ SET_VREG %ecx, rINST # vA<- %ecx; result
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/binopLit8.S b/vm/mterp/x86-atom/binopLit8.S
new file mode 100644
index 0000000..239e443
--- /dev/null
+++ b/vm/mterp/x86-atom/binopLit8.S
@@ -0,0 +1,40 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopLit8.S
+ *
+ * Code: 32-bit "lit8" divide operation. Provides an "instr" line
+ * to specify an instruction that performs "%ecx = %ecx op %edx"
+ *
+ *
+ * For: add-int/lit8, and-int/lit8, mul-int/lit8, or-int/lit8
+ * xor-int/lit8
+ *
+ * Description: Perform a binary operation on a register and a
+ * signed extended 8-bit literal value
+ *
+ * Format: AA|op CC|BB (22b)
+ *
+ * Syntax: op vAA, vBB, #+CC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CCs 1, %edx # %edx<- +CC, sign-extended literal
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %ecx # %ecx<- vBB
+ $instr # %ecx<- vBB op +CC
+ SET_VREG %ecx, rINST # vAA<- %ecx; result
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/binopLit8S.S b/vm/mterp/x86-atom/binopLit8S.S
new file mode 100644
index 0000000..c0360aa
--- /dev/null
+++ b/vm/mterp/x86-atom/binopLit8S.S
@@ -0,0 +1,40 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopLit8S.S
+ *
+ * Code: 32-bit "lit8" divide operation. Provides an "instr" line
+ * to specify an instruction that performs "%edx = %edx op %cl"
+ *
+ *
+ * For: shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ *
+ *
+ * Description: Perform a binary operation on a register and a
+ * signed extended 8-bit literal value
+ *
+ * Format: AA|op CC|BB (22b)
+ *
+ * Syntax: op vAA, vBB, #+CC
+ */
+
+ FETCH_BB 1, %edx # %edx<- BB
+ FETCH_CCs 1, %ecx # %ecx<- +CC, sign-extended literal
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %edx # %edx<- vBB
+ $instr # %edx<- vBB op +CC
+ SET_VREG %edx, rINST # vAA<- %edx; result
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/binopS.S b/vm/mterp/x86-atom/binopS.S
new file mode 100644
index 0000000..b09f5c2
--- /dev/null
+++ b/vm/mterp/x86-atom/binopS.S
@@ -0,0 +1,39 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopS.S
+ *
+ * Code: Generic 32-bit binary operation. Provides an "instr" line to
+ * specify an instruction that performs "%edx = %edx op %cl"
+ *
+ * For: shl-int, shr-int, ushr-int
+ *
+ * Description: Perform a binary operation on two source registers
+ * and store the result in a destination register.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %edx # %edx<- BB
+ FETCH_CC 1, %ecx # %ecx<- CC
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %edx # %edx<- vBB
+ GET_VREG %ecx # %ecx<- vCC
+ $instr # %edx<- vBB op +CC
+ SET_VREG %edx, rINST # vAA<- %edx; result
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/binopS2addr.S b/vm/mterp/x86-atom/binopS2addr.S
new file mode 100644
index 0000000..0c3c29a
--- /dev/null
+++ b/vm/mterp/x86-atom/binopS2addr.S
@@ -0,0 +1,42 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopS2addr.S
+ *
+ * Code: Generic 32-bit "/2addr" binary operation. Provides an
+ * "instr" line to specify an instruction that performs
+ * "%edx = %edx op %cl".
+ *
+ * For: shl-int/2addr, shr-int/2addr, ushr-int/2addr
+ *
+ * Description: Perform a binary operation on two sources registers
+ * and store the result in the first source register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %ecx # %ecx<- BA
+ shr $$4, %ecx # %ecx<- B
+ andl $$15, rINST # rINST<- A
+ movl rINST, %edx # %edx<- A
+ FFETCH_ADV 1, %eax # %ecx<- next instruction hi; fetch, advance
+ GET_VREG %ecx # %ecx<- vB
+ GET_VREG %edx # %edx<- vA
+ $instr # %edx<- vA op vB
+ SET_VREG %edx, rINST # vAA<- %edx; result
+ FGETOP_JMP 1, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/binopWide.S b/vm/mterp/x86-atom/binopWide.S
new file mode 100644
index 0000000..3cd3e52
--- /dev/null
+++ b/vm/mterp/x86-atom/binopWide.S
@@ -0,0 +1,40 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopWide.S
+ *
+ * Code: Generic 64-bit binary operation. Provides an "instr" variable to
+ * specify an instruction that performs "%xmm0 = %xmm0 op %xmm1"
+ *
+ * For: add-double, add-long, and-long, mul-double, or-long,
+ * sub-double, sub-long, xor-long
+ *
+ * Description: Perform a binary operation on two source registers
+ * and store the result in a destination register.
+ *
+ * Format: AA|op CC|BB (23x)
+ *
+ * Syntax: op vAA, vBB, vCC
+ */
+
+ FETCH_BB 1, %ecx # %ecx<- BB
+ FETCH_CC 1, %edx # %edx<- CC
+ FFETCH_ADV 2, %eax # %eax<- next instruction hi; fetch, advance
+ movq (rFP, %ecx, 4), %xmm0 # %xmm0<- vBB
+ movq (rFP, %edx, 4), %xmm1 # %xmm1<- vCC
+ $instr # %xmm0<- vBB op vCC
+ movq %xmm0, (rFP, rINST, 4) # vAA<- %ecx
+ FGETOP_JMP 2, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/binopWide2addr.S b/vm/mterp/x86-atom/binopWide2addr.S
new file mode 100644
index 0000000..f9aa29c
--- /dev/null
+++ b/vm/mterp/x86-atom/binopWide2addr.S
@@ -0,0 +1,41 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: binopWide2addr.S
+ *
+ * Code: Generic 64-bit "/2addr" binary operation. Provides an
+ * "instr" line to specify an instruction that performs
+ * "%xmm0= %xmm0 op %xmm1".
+ *
+ * For: add-double/2addr, add-long/2addr, and-long/2addr, mul-long/2addr,
+ * or-long/2addr, sub-double/2addr, sub-long/2addr, xor-long/2addr
+ *
+ * Description: Perform a binary operation on two sources registers
+ * and store the result in the first source register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+ movl rINST, %edx # %edx<- BA
+ shr $$4, rINST # rINST<- B
+ andl $$15, %edx # %edx<- A
+ movq (rFP, rINST, 4), %xmm1 # %xmm1<- vB
+ movq (rFP, %edx, 4), %xmm0 # %xmm0<- vA
+ $instr # %xmm0<- vA op vB
+ movq %xmm0, (rFP, %edx, 4) # vA<- %xmm0; result
+ FINISH 1 # jump to next instruction
diff --git a/vm/mterp/x86-atom/entry.S b/vm/mterp/x86-atom/entry.S
new file mode 100644
index 0000000..9d7f61e
--- /dev/null
+++ b/vm/mterp/x86-atom/entry.S
@@ -0,0 +1,384 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: entry.S
+ */
+
+#define ASSIST_DEBUGGER 1
+ .text
+ .align 2
+ .global dvmMterpStdRun
+ .type dvmMterpStdRun, %function
+
+ /*
+ * Save registers, initialize sp and fp.
+ * On entry:
+ * bool MterpGlue(glue *)
+ */
+
+ .macro MTERP_ENTRY
+ movl 4(%esp), %ecx # get first argument
+ movl %ebp, -4(%esp) # save caller base pointer
+ movl %ebx, -8(%esp) # save %ebx
+ movl %esi, -12(%esp) # save %esi
+ movl %edi, -16(%esp) # save %edi
+ lea -40(%esp), %ebp # set callee base pointer
+ lea -40(%esp), %esp # set callee stack pointer
+ .endm
+
+ /*
+ * Restore registers.
+ * This function returns a boolean "changeInterp" value.
+ * The return value is from dvmMterpStdBail().
+ */
+
+ .macro MTERP_EXIT
+ lea 40(%esp), %esp # correct stack pointer
+ movl -16(%esp), %edi # restore %edi
+ movl -12(%esp), %esi # restore %esi
+ movl -8(%esp), %ebx # restore %ebx
+ movl -4(%esp), %ebp # restore caller base pointer
+ ret # return
+ .endm
+
+ /*
+ * DvmMterpStdRun entry point: save stack pointer, setup memory locations, get
+ * entry point, start executing instructions.
+ */
+
+dvmMterpStdRun:
+ MTERP_ENTRY
+ movl %ecx, rGLUE # save value for pMterpGlue
+ movl offGlue_pc(%ecx), rPC # get program counter
+ cmp $$kInterpEntryInstr, offGlue_entryPoint(%ecx) # check instruction
+ movl offGlue_fp(%ecx), rFP # get frame pointer
+ movl %esp, offGlue_bailPtr(%ecx) # save SP for eventual return
+ FFETCH %edx # %edx<- opcode
+ jne .Lnot_instr # no, handle it
+ FGETOP_JMPa %edx # start executing the instruction at rPC
+
+ /*
+ * Not an instruction. Are we returning from a method?
+ */
+
+.Lnot_instr:
+ cmpl $$kInterpEntryReturn, offGlue_entryPoint(%ecx)
+ je common_returnFromMethod
+
+ /*
+ * No, are we throwing an exception?
+ */
+
+.Lnot_return:
+ cmpl $$kInterpEntryThrow, offGlue_entryPoint(%ecx)
+ je common_exceptionThrown
+
+ /*
+ * No, then we must abort.
+ */
+
+.Lbad_arg:
+ pushl offGlue_entryPoint(%ecx)
+ movl $$.LstrBadEntryPoint, -4(%esp)
+ lea -4(%esp), %esp
+ call printf
+ lea 8(%esp), %esp
+ call dvmAbort # call (void)
+
+ /*
+ * Restore the stack pointer and PC from the save point established on entry and
+ * return to whoever called dvmMterpStdRun.
+ *
+ * On entry:
+ * 4(%esp) MterpGlue* glue
+ * 8(%esp) bool changeInterp
+ */
+
+ .global dvmMterpStdBail
+ .type dvmMterpStdBail, %function
+
+dvmMterpStdBail:
+ movl 4(%esp), %ecx # get first argument
+ movl 8(%esp), %eax # get second argument
+ movl offGlue_bailPtr(%ecx), %esp # sp <- saved SP
+ MTERP_EXIT
+
+ /*
+ * String references.
+ */
+
+.LstrBadEntryPoint:
+ .asciz "Bad entry point %d\n"
+
+
+dvmAsmInstructionJmpTable = .LdvmAsmInstructionJmpTable
+.LdvmAsmInstructionJmpTable:
+.long .L_OP_NOP
+.long .L_OP_MOVE
+.long .L_OP_MOVE_FROM16
+.long .L_OP_MOVE_16
+.long .L_OP_MOVE_WIDE
+.long .L_OP_MOVE_WIDE_FROM16
+.long .L_OP_MOVE_WIDE_16
+.long .L_OP_MOVE_OBJECT
+.long .L_OP_MOVE_OBJECT_FROM16
+.long .L_OP_MOVE_OBJECT_16
+.long .L_OP_MOVE_RESULT
+.long .L_OP_MOVE_RESULT_WIDE
+.long .L_OP_MOVE_RESULT_OBJECT
+.long .L_OP_MOVE_EXCEPTION
+.long .L_OP_RETURN_VOID
+.long .L_OP_RETURN
+.long .L_OP_RETURN_WIDE
+.long .L_OP_RETURN_OBJECT
+.long .L_OP_CONST_4
+.long .L_OP_CONST_16
+.long .L_OP_CONST
+.long .L_OP_CONST_HIGH16
+.long .L_OP_CONST_WIDE_16
+.long .L_OP_CONST_WIDE_32
+.long .L_OP_CONST_WIDE
+.long .L_OP_CONST_WIDE_HIGH16
+.long .L_OP_CONST_STRING
+.long .L_OP_CONST_STRING_JUMBO
+.long .L_OP_CONST_CLASS
+.long .L_OP_MONITOR_ENTER
+.long .L_OP_MONITOR_EXIT
+.long .L_OP_CHECK_CAST
+.long .L_OP_INSTANCE_OF
+.long .L_OP_ARRAY_LENGTH
+.long .L_OP_NEW_INSTANCE
+.long .L_OP_NEW_ARRAY
+.long .L_OP_FILLED_NEW_ARRAY
+.long .L_OP_FILLED_NEW_ARRAY_RANGE
+.long .L_OP_FILL_ARRAY_DATA
+.long .L_OP_THROW
+.long .L_OP_GOTO
+.long .L_OP_GOTO_16
+.long .L_OP_GOTO_32
+.long .L_OP_PACKED_SWITCH
+.long .L_OP_SPARSE_SWITCH
+.long .L_OP_CMPL_FLOAT
+.long .L_OP_CMPG_FLOAT
+.long .L_OP_CMPL_DOUBLE
+.long .L_OP_CMPG_DOUBLE
+.long .L_OP_CMP_LONG
+.long .L_OP_IF_EQ
+.long .L_OP_IF_NE
+.long .L_OP_IF_LT
+.long .L_OP_IF_GE
+.long .L_OP_IF_GT
+.long .L_OP_IF_LE
+.long .L_OP_IF_EQZ
+.long .L_OP_IF_NEZ
+.long .L_OP_IF_LTZ
+.long .L_OP_IF_GEZ
+.long .L_OP_IF_GTZ
+.long .L_OP_IF_LEZ
+.long .L_OP_UNUSED_3E
+.long .L_OP_UNUSED_3F
+.long .L_OP_UNUSED_40
+.long .L_OP_UNUSED_41
+.long .L_OP_UNUSED_42
+.long .L_OP_UNUSED_43
+.long .L_OP_AGET
+.long .L_OP_AGET_WIDE
+.long .L_OP_AGET_OBJECT
+.long .L_OP_AGET_BOOLEAN
+.long .L_OP_AGET_BYTE
+.long .L_OP_AGET_CHAR
+.long .L_OP_AGET_SHORT
+.long .L_OP_APUT
+.long .L_OP_APUT_WIDE
+.long .L_OP_APUT_OBJECT
+.long .L_OP_APUT_BOOLEAN
+.long .L_OP_APUT_BYTE
+.long .L_OP_APUT_CHAR
+.long .L_OP_APUT_SHORT
+.long .L_OP_IGET
+.long .L_OP_IGET_WIDE
+.long .L_OP_IGET_OBJECT
+.long .L_OP_IGET_BOOLEAN
+.long .L_OP_IGET_BYTE
+.long .L_OP_IGET_CHAR
+.long .L_OP_IGET_SHORT
+.long .L_OP_IPUT
+.long .L_OP_IPUT_WIDE
+.long .L_OP_IPUT_OBJECT
+.long .L_OP_IPUT_BOOLEAN
+.long .L_OP_IPUT_BYTE
+.long .L_OP_IPUT_CHAR
+.long .L_OP_IPUT_SHORT
+.long .L_OP_SGET
+.long .L_OP_SGET_WIDE
+.long .L_OP_SGET_OBJECT
+.long .L_OP_SGET_BOOLEAN
+.long .L_OP_SGET_BYTE
+.long .L_OP_SGET_CHAR
+.long .L_OP_SGET_SHORT
+.long .L_OP_SPUT
+.long .L_OP_SPUT_WIDE
+.long .L_OP_SPUT_OBJECT
+.long .L_OP_SPUT_BOOLEAN
+.long .L_OP_SPUT_BYTE
+.long .L_OP_SPUT_CHAR
+.long .L_OP_SPUT_SHORT
+.long .L_OP_INVOKE_VIRTUAL
+.long .L_OP_INVOKE_SUPER
+.long .L_OP_INVOKE_DIRECT
+.long .L_OP_INVOKE_STATIC
+.long .L_OP_INVOKE_INTERFACE
+.long .L_OP_UNUSED_73
+.long .L_OP_INVOKE_VIRTUAL_RANGE
+.long .L_OP_INVOKE_SUPER_RANGE
+.long .L_OP_INVOKE_DIRECT_RANGE
+.long .L_OP_INVOKE_STATIC_RANGE
+.long .L_OP_INVOKE_INTERFACE_RANGE
+.long .L_OP_UNUSED_79
+.long .L_OP_UNUSED_7A
+.long .L_OP_NEG_INT
+.long .L_OP_NOT_INT
+.long .L_OP_NEG_LONG
+.long .L_OP_NOT_LONG
+.long .L_OP_NEG_FLOAT
+.long .L_OP_NEG_DOUBLE
+.long .L_OP_INT_TO_LONG
+.long .L_OP_INT_TO_FLOAT
+.long .L_OP_INT_TO_DOUBLE
+.long .L_OP_LONG_TO_INT
+.long .L_OP_LONG_TO_FLOAT
+.long .L_OP_LONG_TO_DOUBLE
+.long .L_OP_FLOAT_TO_INT
+.long .L_OP_FLOAT_TO_LONG
+.long .L_OP_FLOAT_TO_DOUBLE
+.long .L_OP_DOUBLE_TO_INT
+.long .L_OP_DOUBLE_TO_LONG
+.long .L_OP_DOUBLE_TO_FLOAT
+.long .L_OP_INT_TO_BYTE
+.long .L_OP_INT_TO_CHAR
+.long .L_OP_INT_TO_SHORT
+.long .L_OP_ADD_INT
+.long .L_OP_SUB_INT
+.long .L_OP_MUL_INT
+.long .L_OP_DIV_INT
+.long .L_OP_REM_INT
+.long .L_OP_AND_INT
+.long .L_OP_OR_INT
+.long .L_OP_XOR_INT
+.long .L_OP_SHL_INT
+.long .L_OP_SHR_INT
+.long .L_OP_USHR_INT
+.long .L_OP_ADD_LONG
+.long .L_OP_SUB_LONG
+.long .L_OP_MUL_LONG
+.long .L_OP_DIV_LONG
+.long .L_OP_REM_LONG
+.long .L_OP_AND_LONG
+.long .L_OP_OR_LONG
+.long .L_OP_XOR_LONG
+.long .L_OP_SHL_LONG
+.long .L_OP_SHR_LONG
+.long .L_OP_USHR_LONG
+.long .L_OP_ADD_FLOAT
+.long .L_OP_SUB_FLOAT
+.long .L_OP_MUL_FLOAT
+.long .L_OP_DIV_FLOAT
+.long .L_OP_REM_FLOAT
+.long .L_OP_ADD_DOUBLE
+.long .L_OP_SUB_DOUBLE
+.long .L_OP_MUL_DOUBLE
+.long .L_OP_DIV_DOUBLE
+.long .L_OP_REM_DOUBLE
+.long .L_OP_ADD_INT_2ADDR
+.long .L_OP_SUB_INT_2ADDR
+.long .L_OP_MUL_INT_2ADDR
+.long .L_OP_DIV_INT_2ADDR
+.long .L_OP_REM_INT_2ADDR
+.long .L_OP_AND_INT_2ADDR
+.long .L_OP_OR_INT_2ADDR
+.long .L_OP_XOR_INT_2ADDR
+.long .L_OP_SHL_INT_2ADDR
+.long .L_OP_SHR_INT_2ADDR
+.long .L_OP_USHR_INT_2ADDR
+.long .L_OP_ADD_LONG_2ADDR
+.long .L_OP_SUB_LONG_2ADDR
+.long .L_OP_MUL_LONG_2ADDR
+.long .L_OP_DIV_LONG_2ADDR
+.long .L_OP_REM_LONG_2ADDR
+.long .L_OP_AND_LONG_2ADDR
+.long .L_OP_OR_LONG_2ADDR
+.long .L_OP_XOR_LONG_2ADDR
+.long .L_OP_SHL_LONG_2ADDR
+.long .L_OP_SHR_LONG_2ADDR
+.long .L_OP_USHR_LONG_2ADDR
+.long .L_OP_ADD_FLOAT_2ADDR
+.long .L_OP_SUB_FLOAT_2ADDR
+.long .L_OP_MUL_FLOAT_2ADDR
+.long .L_OP_DIV_FLOAT_2ADDR
+.long .L_OP_REM_FLOAT_2ADDR
+.long .L_OP_ADD_DOUBLE_2ADDR
+.long .L_OP_SUB_DOUBLE_2ADDR
+.long .L_OP_MUL_DOUBLE_2ADDR
+.long .L_OP_DIV_DOUBLE_2ADDR
+.long .L_OP_REM_DOUBLE_2ADDR
+.long .L_OP_ADD_INT_LIT16
+.long .L_OP_RSUB_INT
+.long .L_OP_MUL_INT_LIT16
+.long .L_OP_DIV_INT_LIT16
+.long .L_OP_REM_INT_LIT16
+.long .L_OP_AND_INT_LIT16
+.long .L_OP_OR_INT_LIT16
+.long .L_OP_XOR_INT_LIT16
+.long .L_OP_ADD_INT_LIT8
+.long .L_OP_RSUB_INT_LIT8
+.long .L_OP_MUL_INT_LIT8
+.long .L_OP_DIV_INT_LIT8
+.long .L_OP_REM_INT_LIT8
+.long .L_OP_AND_INT_LIT8
+.long .L_OP_OR_INT_LIT8
+.long .L_OP_XOR_INT_LIT8
+.long .L_OP_SHL_INT_LIT8
+.long .L_OP_SHR_INT_LIT8
+.long .L_OP_USHR_INT_LIT8
+.long .L_OP_IGET_VOLATILE
+.long .L_OP_IPUT_VOLATILE
+.long .L_OP_SGET_VOLATILE
+.long .L_OP_SPUT_VOLATILE
+.long .L_OP_IGET_OBJECT_VOLATILE
+.long .L_OP_IGET_WIDE_VOLATILE
+.long .L_OP_IPUT_WIDE_VOLATILE
+.long .L_OP_SGET_WIDE_VOLATILE
+.long .L_OP_SPUT_WIDE_VOLATILE
+.long .L_OP_BREAKPOINT
+.long .L_OP_THROW_VERIFICATION_ERROR
+.long .L_OP_EXECUTE_INLINE
+.long .L_OP_EXECUTE_INLINE_RANGE
+.long .L_OP_INVOKE_DIRECT_EMPTY
+.long .L_OP_UNUSED_F1
+.long .L_OP_IGET_QUICK
+.long .L_OP_IGET_WIDE_QUICK
+.long .L_OP_IGET_OBJECT_QUICK
+.long .L_OP_IPUT_QUICK
+.long .L_OP_IPUT_WIDE_QUICK
+.long .L_OP_IPUT_OBJECT_QUICK
+.long .L_OP_INVOKE_VIRTUAL_QUICK
+.long .L_OP_INVOKE_VIRTUAL_QUICK_RANGE
+.long .L_OP_INVOKE_SUPER_QUICK
+.long .L_OP_INVOKE_SUPER_QUICK_RANGE
+.long .L_OP_IPUT_OBJECT_VOLATILE
+.long .L_OP_SGET_OBJECT_VOLATILE
+.long .L_OP_SPUT_OBJECT_VOLATILE
+.long .L_OP_UNUSED_FF
diff --git a/vm/mterp/x86-atom/footer.S b/vm/mterp/x86-atom/footer.S
new file mode 100644
index 0000000..cb9970d
--- /dev/null
+++ b/vm/mterp/x86-atom/footer.S
@@ -0,0 +1,662 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: footer.S
+ */
+
+ .text
+ .align 2
+
+ /*
+ * Check to see if the thread needs to be suspended or debugger/profiler
+ * activity has begun.
+ *
+ * On entry:
+ * %ecx is reentry type, e.g. kInterpEntryInstr
+ * %edx is PC adjustment in bytes
+ */
+
+common_periodicChecks:
+ movl %edx, -8(%esp) # save pc adjustments
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl %ebx, -4(%esp) # save %ebx to the stack
+ movl offGlue_pSelfSuspendCount(%edx), %ebx # %ebx<- pSuspendCount (int)
+4:
+ movl offGlue_pDebuggerActive(%edx), %eax # %eax<- pDebuggerActive
+ testl %eax, %eax
+ je 5f
+ movzbl (%eax), %eax # %eax<- get debuggerActive (boolean)
+5:
+ cmp $$0, (%ebx) # check if suspend is pending
+ jne 2f # handle suspend
+ movl offGlue_pActiveProfilers(%edx), %ebx # %ebx<- activeProfilers (int)
+ orl (%ebx), %eax # %eax<- merge activeProfilers and debuggerActive
+ movl -8(%esp), %edx # %edx<- restore %edx
+ jne 3f # debugger or profiler active; switch interp
+ movl -4(%esp), %ebx # %ebx<- restore %ebx
+ ret # return
+2: # check suspended
+ EXPORT_PC
+ movl offGlue_self(%edx), %eax # %eax<- glue->self
+ movl %eax, -12(%esp) # push parameter boolean
+ lea -12(%esp), %esp
+ call dvmCheckSuspendPending # call: (Thread* self)
+ # return: bool
+ movl 4(%esp), %edx # %edx<- restore %edx
+ movl 8(%esp), %ebx # %ebx<- restore %ebx
+ lea 12(%esp), %esp
+ ret
+3: # debugger/profiler enabled, bail out
+ leal (rPC, %edx, 2), rPC # adjust pc to show target
+ movl rGLUE, %ecx # %ecx<- pMterpGlue
+ movb $$kInterpEntryInstr, offGlue_entryPoint(%ecx)
+ movl $$1, %edx # switch interpreter
+ jmp common_gotoBail # bail
+
+ /*
+ * Check to see if the thread needs to be suspended or debugger/profiler
+ * activity has begun. With this variant, the reentry type is hard coded
+ * as kInterpEntryInstr.
+ *
+ * On entry:
+ * %edx is PC adjustment in bytes
+ */
+
+common_periodicChecks_backwardBranch:
+ EXPORT_PC
+ movl rGLUE, %ecx # %ecx<- pMterpGlue
+ movl offGlue_pSelfSuspendCount(%ecx), rINST # %ebx<- pSuspendCount (int)
+4:
+ movl offGlue_pDebuggerActive(%ecx), %eax # %eax<- pDebuggerActive
+ testl %eax, %eax # test for NULL pointer
+ je 5f
+ movzbl (%eax), %eax # %eax<- get debuggerActive count
+5:
+ cmp $$0, (rINST) # check if suspend is pending
+ jne 2f # handle suspend
+ movl offGlue_pActiveProfilers(%ecx), rINST # %edx<- activeProfilers (int)
+ orl (rINST), %eax # %eax<- merge activeProfilers and debuggerActive
+ jne 3f # debugger or profiler active; switch interp
+ FINISH_RB %edx, %ecx # jump to next instruction
+2: # check suspended
+ movl offGlue_self(%ecx), %eax# %eax<- glue->self
+ movl %edx, rINST
+ movl %eax, -12(%esp) # push parameter boolean
+ lea -12(%esp), %esp
+ call dvmCheckSuspendPending # call: (Thread* self)
+ # return: bool
+ movl rINST, %edx # %edx<- restore %edx
+ lea 12(%esp), %esp
+ FINISH_RB %edx, %ecx
+3: # debugger/profiler enabled, bail out
+ leal (rPC, %edx, 2), rPC # adjust pc to show target
+ movb $$kInterpEntryInstr, offGlue_entryPoint(%ecx)
+ movl $$1, %edx # switch interpreter
+ jmp common_gotoBail # bail
+
+ /*
+ * The equivalent of "goto bail", this calls through the "bail handler".
+ * State registers will be saved to the "glue" area before bailing.
+ *
+ * On entry:
+ * %edx is "bool changeInterp", indicating if we want to switch to the
+ * other interpreter or just bail all the way out
+ */
+
+common_gotoBail:
+ SAVE_PC_FP_TO_GLUE %ecx # save program counter and frame pointer
+
+ /*
+ * Inlined dvmMterpStdBail
+ */
+
+ lea 40(%ebp), %esp
+ movl %edx, %eax
+ movl 24(%ebp), %edi
+ movl 28(%ebp), %esi
+ movl 32(%ebp), %ebx
+ movl 36(%ebp), %ebp
+ ret
+
+ /*
+ * Common code for method invocation with range.
+ *
+ * On entry:
+ * %ecx is "Method* methodToCall", the method we're trying to call
+ */
+
+common_invokeMethodRange:
+.LinvokeNewRange:
+
+ /*
+ * prepare to copy args to "outs" area of current frame
+ */
+
+ SAVEAREA_FROM_FP %eax # %eax<- &outs; &StackSaveArea
+ test rINST, rINST # test for no args
+ movl rINST, sReg0 # sReg0<- AA
+ jz .LinvokeArgsDone # no args; jump to args done
+ FETCH 2, %edx # %edx<- CCCC
+
+ /*
+ * %ecx=methodToCall, %edx=CCCC, sReg0=count, %eax=&outs (&stackSaveArea)
+ * (very few methods have > 10 args; could unroll for common cases)
+ */
+
+ movl %ebx, sReg1 # sReg1<- save %ebx
+ lea (rFP, %edx, 4), %edx # %edx<- &vCCCC
+ shll $$2, sReg0 # sReg0<- offset
+ subl sReg0, %eax # %eax<- update &outs
+ shrl $$2, sReg0 # sReg0<- offset
+1:
+ movl (%edx), %ebx # %ebx<- vCCCC
+ lea 4(%edx), %edx # %edx<- &vCCCC++
+ subl $$1, sReg0 # sReg<- sReg--
+ movl %ebx, (%eax) # *outs<- vCCCC
+ lea 4(%eax), %eax # outs++
+ jne 1b # loop if count (sReg0) not zero
+ movl sReg1, %ebx # %ebx<- restore %ebx
+ jmp .LinvokeArgsDone # continue
+
+ /*
+ * %ecx is "Method* methodToCall", the method we're trying to call
+ * prepare to copy args to "outs" area of current frame
+ */
+
+common_invokeMethodNoRange:
+.LinvokeNewNoRange:
+ movl rINST, sReg0 # sReg0<- BA
+ shrl $$4, sReg0 # sReg0<- B
+ je .LinvokeArgsDone # no args; jump to args done
+ SAVEAREA_FROM_FP %eax # %eax<- &outs; &StackSaveArea
+ FETCH 2, %edx # %edx<- GFED
+
+ /*
+ * %ecx=methodToCall, %edx=GFED, sReg0=count, %eax=outs
+ */
+
+.LinvokeNonRange:
+ cmp $$2, sReg0 # compare sReg0 to 2
+ movl %edx, sReg1 # sReg1<- GFED
+ jl 1f # handle 1 arg
+ je 2f # handle 2 args
+ cmp $$4, sReg0 # compare sReg0 to 4
+ jl 3f # handle 3 args
+ je 4f # handle 4 args
+5:
+ andl $$15, rINST # rINST<- A
+ lea -4(%eax), %eax # %eax<- update &outs; &outs--
+ movl (rFP, rINST, 4), %edx # %edx<- vA
+ movl %edx, (%eax) # *outs<- vA
+ movl sReg1, %edx # %edx<- GFED
+4:
+ shr $$12, %edx # %edx<- G
+ lea -4(%eax), %eax # %eax<- update &outs; &outs--
+ movl (rFP, %edx, 4), %edx # %edx<- vG
+ movl %edx, (%eax) # *outs<- vG
+ movl sReg1, %edx # %edx<- GFED
+3:
+ and $$0x0f00, %edx # %edx<- 0F00
+ shr $$6, %edx # %edx<- F at correct offset
+ lea -4(%eax), %eax # %eax<- update &outs; &outs--
+ movl (rFP, %edx), %edx # %edx<- vF
+ movl %edx, (%eax) # *outs<- vF
+ movl sReg1, %edx # %edx<- GFED
+2:
+ and $$0x00f0, %edx # %edx<- 00E0
+ shr $$2, %edx # %edx<- E at correct offset
+ lea -4(%eax), %eax # %eax<- update &outs; &outs--
+ movl (rFP, %edx), %edx # %edx<- vE
+ movl %edx, (%eax) # *outs<- vE
+ movl sReg1, %edx # %edx<- GFED
+1:
+ and $$0x000f, %edx # %edx<- 000D
+ movl (rFP, %edx, 4), %edx # %edx<- vD
+ movl %edx, -4(%eax) # *--outs<- vD
+0:
+
+ /*
+ * %ecx is "Method* methodToCall", the method we're trying to call
+ * find space for the new stack frame, check for overflow
+ */
+
+.LinvokeArgsDone:
+ movzwl offMethod_registersSize(%ecx), %eax # %eax<- methodToCall->regsSize
+ movzwl offMethod_outsSize(%ecx), %edx # %edx<- methodToCall->outsSize
+ movl %ecx, sReg0 # sReg<- methodToCall
+ shl $$2, %eax # %eax<- update offset
+ SAVEAREA_FROM_FP %ecx # %ecx<- &outs; &StackSaveArea
+ subl %eax, %ecx # %ecx<- newFP; (old savearea - regsSize)
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ movl %ecx, sReg1 # sReg1<- &outs
+ subl $$sizeofStackSaveArea, %ecx # %ecx<- newSaveArea (stack save area using newFP)
+ movl offGlue_interpStackEnd(%eax), %eax # %eax<- glue->interpStackEnd
+ movl %eax, sReg2 # sReg2<- glue->interpStackEnd
+ shl $$2, %edx # %edx<- update offset for outsSize
+ movl %ecx, %eax # %eax<- newSaveArea
+ sub %edx, %ecx # %ecx<- bottom; (newSaveArea - outsSize)
+ cmp sReg2, %ecx # compare interpStackEnd and bottom
+ movl sReg0, %ecx # %ecx<- restore methodToCall
+ jl .LstackOverflow # handle frame overflow
+
+ /*
+ * set up newSaveArea
+ */
+
+#ifdef EASY_GDB
+ SAVEAREA_FROM_FP %edx # %edx<- &outs; &StackSaveArea
+ movl %edx, offStackSaveArea_prevSave(%eax) # newSaveArea->prevSave<- &outs
+#endif
+ movl rFP, offStackSaveArea_prevFrame(%eax) # newSaveArea->prevFrame<- rFP
+ movl rPC, offStackSaveArea_savedPc(%eax) # newSaveArea->savedPc<- rPC
+ testl $$ACC_NATIVE, offMethod_accessFlags(%ecx) # check for native call
+ movl %ecx, offStackSaveArea_method(%eax) # newSaveArea->method<- method to call
+ jne .LinvokeNative # handle native call
+
+ /*
+ * Update "glue" values for the new method
+ * %ecx=methodToCall, sReg1=newFp
+ */
+
+ movl offMethod_clazz(%ecx), %edx # %edx<- method->clazz
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ movl %ecx, offGlue_method(%eax) # glue->method<- methodToCall
+ movl offClassObject_pDvmDex(%edx), %edx # %edx<- method->clazz->pDvmDex
+ movl offMethod_insns(%ecx), rPC # rPC<- methodToCall->insns
+ movl %edx, offGlue_methodClassDex(%eax) # glue->methodClassDex<- method->clazz->pDvmDex
+ movl offGlue_self(%eax), %ecx # %ecx<- glue->self
+ movl sReg1, rFP # rFP<- newFP
+ movl rFP, offThread_curFrame(%ecx) # glue->self->curFrame<- newFP
+ FINISH_A # jump to methodToCall->insns
+
+ /*
+ * Prep for the native call
+ * %ecx=methodToCall, sReg1=newFP, %eax=newSaveArea
+ */
+
+.LinvokeNative:
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl %ecx, -20(%esp) # push parameter methodToCall
+ movl offGlue_self(%edx), %edx # %edx<- glue->self
+ movl offThread_jniLocal_topCookie(%edx), %ecx # %ecx<- glue->self->thread->refNext
+ movl %ecx, offStackSaveArea_localRefCookie(%eax) # newSaveArea->localRefCookie<- refNext
+ movl %eax, -4(%esp) # save newSaveArea
+ movl sReg1, %eax # %eax<- newFP
+ movl %eax, offThread_curFrame(%edx) # glue->self->curFrame<- newFP
+ movl %edx, -8(%esp) # save glue->self
+ movl %edx, -16(%esp) # push parameter glue->self
+ movl rGLUE, %edx # %edx<- pMterpGlue
+ movl -20(%esp), %ecx # %ecx<- methodToCall
+ lea offGlue_retval(%edx), %edx # %edx<- &retval
+ movl %edx, -24(%esp) # push parameter pMterpGlue
+ movl %eax, -28(%esp) # push parameter newFP
+ lea -28(%esp), %esp
+
+#ifdef ASSIST_DEBUGGER
+ jmp .Lskip
+ .type dalvik_mterp, %function
+dalvik_mterp:
+ MTERP_ENTRY
+.Lskip:
+#endif
+ call *offMethod_nativeFunc(%ecx) # call methodToCall->nativeFunc
+ lea 28(%esp), %esp
+ movl -4(%esp), %edx # %edx<- newSaveArea
+ movl -8(%esp), %ecx # %ecx<- glue->self
+ movl offStackSaveArea_localRefCookie(%edx), %eax # %eax<- newSaveArea->localRefCookie
+ FFETCH_ADV 3, %edx # %edx<- next instruction hi; fetch, advance
+ cmp $$0, offThread_exception(%ecx) # check for exception
+ movl rFP, offThread_curFrame(%ecx) # glue->self->curFrame<- rFP
+ movl %eax, offThread_jniLocal_topCookie(%ecx) # glue->self<- newSaveArea->localRefCookie
+ jne common_exceptionThrown # handle exception
+ FGETOP_JMP 3, %edx # jump to next instruction; getop, jmp
+
+.LstackOverflow:
+ movl %ecx, -4(%esp) # push method to call
+ movl rGLUE, %ecx # %ecx<- pMterpGlue
+ movl offGlue_self(%ecx), %ecx # %ecx<- glue->self
+ movl %ecx, -8(%esp) # push parameter self
+ lea -8(%esp), %esp
+ call dvmHandleStackOverflow # call: (Thread* self, Method *meth)
+ # return: void
+ lea 8(%esp), %esp
+ jmp common_exceptionThrown # handle exception
+#ifdef ASSIST_DEBUGGER
+#endif
+
+ /*
+ * Common code for handling a return instruction.
+ *
+ * This does not return.
+ */
+
+common_returnFromMethod:
+.LreturnNew:
+
+ /*
+ * Inline common periodic checks
+ */
+
+ movl rGLUE, rINST # %ecx<- pMterpGlue
+ movl offGlue_pSelfSuspendCount(rINST), %edx # %ebx<- pSuspendCount (int)
+ movl offGlue_pDebuggerActive(rINST), %eax # %eax<- pDebuggerActive
+ movl (%eax), %eax # %eax<- get debuggerActive (boolean)
+ and $$7, %eax # %eax<- mask for boolean (just how many bits does it take?)
+ cmp $$0, (%edx) # check if suspend is pending
+ jne 2f # handle suspend
+ movl offGlue_pActiveProfilers(rINST), %edx # %edx<- activeProfilers (int)
+ or (%edx), %eax # %eax<- merge activeProfilers and debuggerActive
+ cmp $$0, %eax # check for debuggerActive
+ jne 3f # debugger or profiler active; switch interp
+ jmp 4f
+2: # check suspended
+ movl offGlue_self(rINST), %eax# %eax<- glue->self
+ movl %eax, -12(%esp) # push parameter boolean
+ lea -12(%esp), %esp
+ call dvmCheckSuspendPending # call: (Thread* self)
+ # return: bool
+ lea 12(%esp), %esp
+ jmp 4f
+3: # debugger/profiler enabled, bail out
+ movl $$kInterpEntryInstr, offGlue_entryPoint(rINST) # glue->entryPoint<- reentry type
+ movl $$1, %edx # switch to interp<- true
+ jmp common_gotoBail # bail
+
+
+ /*
+ * Get save area; rGLUE is %ebx, rFP is %eax
+ */
+4:
+ SAVEAREA_FROM_FP %ecx # %ecx<- saveArea(old)
+ movl offStackSaveArea_prevFrame(%ecx), rFP # rFP<- saveArea->PrevFrame
+ movl (offStackSaveArea_method - sizeofStackSaveArea)(rFP), %edx # %edx<- method we are returning to
+ cmpl $$0, %edx # check for break frame
+ je common_gotoBail # bail if break frame
+ movl offStackSaveArea_savedPc(%ecx), rPC # rPC<- saveAreaOld->savedPc
+ movl offGlue_self(rINST), %ecx # %eax<- glue->self
+ movl %edx, offGlue_method(rINST) # glue->method<- newSave->method
+ movl offMethod_clazz(%edx), %edx # %edx<- method->clazz
+ FFETCH_ADV 3, %eax # %ecx<- next instruction hi; fetch, advance
+ movl rFP, offThread_curFrame(%ecx) # glue->self->curFrame<- rFP
+ movl offClassObject_pDvmDex(%edx), %edx # %edx<- method->clazz->pDvmDex
+ movl %edx, offGlue_methodClassDex(rINST) # glue->pDvmDex<- method->clazz->pDvmDex
+ FGETOP_JMP 3, %eax # jump to next instruction; getop, jmp
+
+ /*
+ * Handle thrown an exception. If the exception processing code
+ * returns to us (instead of falling out of the interpreter),
+ * continue with whatever the next instruction now happens to be.
+ * This does not return.
+ */
+
+common_exceptionThrown:
+.LexceptionNew:
+ movl $$kInterpEntryThrow, %ecx # %ecx<- reentry type
+ movl $$0, %edx # %edx<- pc adjustment
+ call common_periodicChecks
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ movl offGlue_self(%eax), %edx # %edx<- glue->self
+ movl offThread_exception(%edx), %ecx # %ecx<- pMterpGlue->self->exception
+ movl %edx, -4(%esp) # push parameter self
+ movl %ecx, -8(%esp) # push parameter obj
+ lea -8(%esp), %esp
+ call dvmAddTrackedAlloc # don't allow the exception to be GC'd
+ # call: (Object* obj, Thread* self)
+ # return: void
+ movl 4(%esp), %edx # %edx<- glue->self
+ movl $$0, offThread_exception(%edx) # glue->self->exception<- NULL
+
+ /*
+ * set up args and a local for &fp
+ */
+
+ movl rFP, -4(%esp) # move fp to stack
+ lea -4(%esp), %esp # update %esp
+ movl %esp, -4(%esp) # push parameter 4<- &fp
+ movl $$0, -8(%esp) # push parameter 3<- false
+ movl 4(%esp), %edx
+ movl %edx, -12(%esp) # push parameter 2<- glue->self->exception
+ movl rGLUE, %eax # %eax<- pMterpGlue
+ movl offGlue_method(%eax), %edx # %edx<- glue->method
+ movl offMethod_insns(%edx), %edx # %edx<- glue->method->insns
+ movl rPC, %ecx # %ecx<- rPC
+ subl %edx, %ecx # %ecx<- pc - glue->method->insns
+ sar $$1, %ecx # %ecx<- adjust %ecx for offset
+ movl %ecx, -16(%esp) # push parameter 1<- glue->method->insns
+ movl 8(%esp), %edx
+ movl %edx, -20(%esp) # push parameter 0<- glue->self
+ lea -20(%esp), %esp
+
+ /*
+ * call dvmFindCatchBlock, %eax gets catchRelPc (a code-unit offset)
+ */
+
+ call dvmFindCatchBlock # call: (Thread* self, int relPc, Object* exception,
+ # bool doUnroll, void** newFrame)
+ # return: int
+ lea 32(%esp), %esp
+ movl -12(%esp), rFP # rFP<- updated rFP
+ cmp $$0, %eax # check for catchRelPc < 0
+ jl .LnotCaughtLocally # handle not caught locally
+
+ /*
+ * fix stack overflow if necessary
+ */
+
+ movl -4(%esp), %ecx # %ecx<- glue->self
+ cmp $$0, offThread_stackOverflowed(%ecx)
+ je 1f
+ movl %eax, -4(%esp) # save %eax for later
+ movl %ecx, -12(%esp) # push parameter 2 glue->self
+ lea -12(%esp), %esp
+ call dvmCleanupStackOverflow # call: (Thread* self, Object* exception)
+ # return: void
+ lea 12(%esp), %esp
+ movl -4(%esp), %eax # %eax<- restore %eax
+ jmp 2f
+1:
+ movl %ecx, -12(%esp) # push parameter 2 glue->self
+2:
+
+ /*
+ * adjust locals to match self->curFrame and updated PC
+ *
+ */
+
+ SAVEAREA_FROM_FP %edx # %edx<- get newSaveArea
+ movl rGLUE, %ecx # %ecx<- pMterpGlue
+ movl offStackSaveArea_method(%edx), rPC # rPC<- newMethod
+ movl rPC, offGlue_method(%ecx) # glue->method<- newMethod
+ movl offMethod_clazz(rPC), %edx # %edx<- method->clazz
+ movl offMethod_insns(rPC), rPC # rPC<- method->insns
+ movl offClassObject_pDvmDex(%edx), %edx # %edx<- method->clazz->pDvmDex
+ lea (rPC, %eax, 2), rPC # rPC<- method->insns + catchRelPc
+ movl %edx, offGlue_methodClassDex(%ecx) # glue->pDvmDex<- method->clazz->pDvmDex
+ movl -8(%esp), %eax
+ movl %eax, -16(%esp) # push parameter 1 obj
+ lea -16(%esp), %esp
+ call dvmReleaseTrackedAlloc # call: (Object* obj, Thread* self)
+ # return: void
+ lea 16(%esp), %esp
+ FINISH_FETCH %eax
+ cmp $$OP_MOVE_EXCEPTION, %eax # is it a move exception
+ jne 1f
+ movl -12(%esp), %edx # %edx<- glue->self
+ movl -8(%esp), %ecx # %ecx<- exception
+ movl %ecx, offThread_exception(%edx) # restore the exception
+1:
+ FINISH_JMP %eax
+
+ /*
+ * -8(%esp) = exception, -4(%esp) = self
+ */
+
+.LnotCaughtLocally:
+ movl -4(%esp), %edx # %edx<- glue->self
+ movzb offThread_stackOverflowed(%edx), %eax # %eax<- self->stackOverflowed
+ cmp $$0, %eax # check for stack overflow;
+ # maybe should use cmpb
+ je 1f #
+ movl %edx, -12(%esp) # push parameter 1 glue->self
+ lea -12(%esp), %esp
+ call dvmCleanupStackOverflow # call: (Thread* self, Object* exception)
+ # return: void
+ lea 12(%esp), %esp
+
+ /*
+ * Release the exception
+ * -8(%esp) = exception, -4(%esp) = self
+ */
+1:
+ movl -8(%esp), %ecx # %ecx<- exception
+ movl -4(%esp), %edx # %edx<- glue->self
+ movl %ecx, offThread_exception(%edx) # glue->self<- exception
+ lea -8(%esp), %esp
+ call dvmReleaseTrackedAlloc # call: (Object* obj, Thread* self)
+ # return: void
+ lea 8(%esp), %esp
+ movl $$0, %edx # switch to interp<- false
+ jmp common_gotoBail # bail
+
+ /*
+ * After returning from a "glued" function, pull out the updated
+ * values and start executing at the next instruction.
+ */
+
+common_resumeAfterGlueCall:
+ LOAD_PC_FP_FROM_GLUE # pull rPC and rFP out of glue
+ FINISH_A # jump to next instruction
+
+ /*
+ * For debugging, cause an immediate fault.
+ */
+
+common_abort:
+ jmp .LdeadFood
+
+.LdeadFood:
+.int 0xdeadf00d
+
+ /*
+ * Invalid array index.
+ */
+
+common_errArrayIndex:
+ EXPORT_PC
+ movl $$.LstrArrayIndexException, -8(%esp) # push parameter description
+ movl $$0, -4(%esp) # push parameter msg paramter
+ lea -8(%esp), %esp
+ call dvmThrowException # call: (const char* exceptionDescriptor, const char* msg)
+ # return: void
+ lea 8(%esp), %esp
+ jmp common_exceptionThrown # handle exception
+
+ /*
+ * Invalid array value.
+ */
+
+common_errArrayStore:
+ EXPORT_PC
+ movl $$.LstrArrayStoreException, -8(%esp) # push parameter description
+ movl $$0, -4(%esp) # push parameter msg paramter
+ lea -8(%esp), %esp
+ call dvmThrowException # call: (const char* exceptionDescriptor, const char* msg)
+ # return: void
+ lea 8(%esp), %esp
+ jmp common_exceptionThrown # handle exception
+
+ /*
+ * Integer divide or mod by zero.
+ */
+
+common_errDivideByZero:
+ EXPORT_PC
+ movl $$.LstrArithmeticException, -8(%esp) # push parameter description
+ movl $$.LstrDivideByZero, -4(%esp) # push parameter msg paramter
+ lea -8(%esp), %esp
+ call dvmThrowException # call: (const char* exceptionDescriptor, const char* msg)
+ # return: void
+ lea 8(%esp), %esp
+ jmp common_exceptionThrown # handle exception
+
+ /*
+ * Attempt to allocate an array with a negative size.
+ */
+
+common_errNegativeArraySize:
+ EXPORT_PC
+ movl $$.LstrNegativeArraySizeException, -8(%esp) # push parameter description
+ movl $$0, -4(%esp) # push parameter msg paramter
+ lea -8(%esp), %esp
+ call dvmThrowException # call: (const char* exceptionDescriptor, const char* msg)
+ # return: void
+ lea 8(%esp), %esp
+ jmp common_exceptionThrown # handle exception
+
+ /*
+ * Invocation of a non-existent method.
+ */
+
+common_errNoSuchMethod:
+ EXPORT_PC
+ movl $$.LstrNoSuchMethodError, -8(%esp) # push parameter description
+ movl $$0, -4(%esp) # push parameter msg paramter
+ lea -8(%esp), %esp
+ call dvmThrowException # call: (const char* exceptionDescriptor, const char* msg)
+ # return: void
+ lea 8(%esp), %esp
+ jmp common_exceptionThrown # handle exception
+
+ /*
+ * Unexpected null object.
+ */
+
+common_errNullObject:
+ EXPORT_PC
+ movl $$.LstrNullPointerException, -8(%esp) # push parameter description
+ movl $$0, -4(%esp) # push parameter msg paramter
+ lea -8(%esp), %esp
+ call dvmThrowException # call: (const char* exceptionDescriptor, const char* msg)
+ # return: void
+ lea 8(%esp), %esp
+ jmp common_exceptionThrown # handle exception
+
+ /*
+ * String references
+ */
+
+ .align 4
+ .section .rodata
+.LstrArithmeticException:
+ .asciz "Ljava/lang/ArithmeticException;"
+.LstrArrayIndexException:
+ .asciz "Ljava/lang/ArrayIndexOutOfBoundsException;"
+.LstrArrayStoreException:
+ .asciz "Ljava/lang/ArrayStoreException;"
+.LstrClassCastException:
+ .asciz "Ljava/lang/ClassCastException;"
+.LstrDivideByZero:
+ .asciz "divide by zero"
+.LstrInstantiationError:
+ .asciz "Ljava/lang/InstantiationError;"
+.LstrNegativeArraySizeException:
+ .asciz "Ljava/lang/NegativeArraySizeException;"
+.LstrNoSuchMethodError:
+ .asciz "Ljava/lang/NoSuchMethodError;"
+.LstrNullPointerException:
+ .asciz "Ljava/lang/NullPointerException;"
+.LstrExceptionNotCaughtLocally:
+ .asciz "Exception %s from %s:%d not caught locally\n"
diff --git a/vm/mterp/x86-atom/header.S b/vm/mterp/x86-atom/header.S
new file mode 100644
index 0000000..28c19d6
--- /dev/null
+++ b/vm/mterp/x86-atom/header.S
@@ -0,0 +1,433 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: header.S
+ */
+
+ /*
+ * IA32 calling convention and general notes:
+ *
+ * EAX, ECX, EDX - general purpose scratch registers (caller-saved);
+ *
+ * The stack (%esp) - used to pass arguments to functions
+ *
+ * EAX - holds the first 4 bytes of a return
+ * EDX - holds the second 4 bytes of a return
+ *
+ * EBX, ESI, EDI, EBP - are callee saved
+ *
+ * CS, DS, SS - are segment registers
+ * ES, FS, GS - are segment registers. We will try to avoid using these registers
+ *
+ * The stack is "full descending". Only the arguments that do not fit * in the first two arg registers are placed on the stack.
+ * "%esp" points to the first stacked argument (i.e. the 3rd arg).
+ */
+
+ /*
+ * Mterp and IA32 notes
+ *
+ * mem nick purpose
+ * (%ebp) rGLUE InterpState base pointer (A.K.A. MterpGlue Pointer)
+ * %esi rPC interpreted program counter, used for fetching
+ * instructions
+ * %ebx rINST first 16-bit code unit of current instruction
+ * %edi rFP interpreted frame pointer, used for accessing
+ * locals and args
+ */
+
+ /*
+ * Includes
+ */
+
+#include "../common/asm-constants.h"
+
+ /*
+ * Reserved registers
+ */
+
+#define rGLUE (%ebp)
+#define rINST %ebx
+#define rINSTbl %bl
+#define rINSTbh %bh
+#define rINSTw %bx
+#define rPC %esi
+#define rFP %edi
+
+ /*
+ * Temporary register used when finishing an opcode
+ */
+
+#define rFinish %edx
+
+ /*
+ * Stack locations used for temporary data. For convenience.
+ */
+
+#define sReg0 4(%ebp)
+#define sReg1 8(%ebp)
+#define sReg2 12(%ebp)
+#define sReg3 16(%ebp)
+
+ /*
+ * Save the PC and FP to the glue struct
+ */
+
+ .macro SAVE_PC_FP_TO_GLUE _reg
+ movl rGLUE, \_reg
+ movl rPC, offGlue_pc(\_reg)
+ movl rFP, offGlue_fp(\_reg)
+ .endm
+
+ /*
+ * Restore the PC and FP from the glue struct
+ */
+
+ .macro LOAD_PC_FP_FROM_GLUE
+ movl rGLUE, rFP
+ movl offGlue_pc(rFP), rPC
+ movl offGlue_fp(rFP), rFP
+ .endm
+
+ /*
+ * "Export" the PC to the stack frame, f/b/o future exception objects. This must
+ * be done *before* something calls dvmThrowException.
+ *
+ * In C this is "SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc", i.e.
+ * fp - sizeof(StackSaveArea) + offsetof(SaveArea, xtra.currentPc)
+ *
+ * It's okay to do this more than once.
+ */
+
+ .macro EXPORT_PC
+ movl rPC, (-sizeofStackSaveArea + offStackSaveArea_currentPc)(rFP)
+ .endm
+
+ /*
+ * Given a frame pointer, find the stack save area.
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+
+ .macro SAVEAREA_FROM_FP _reg
+ lea -sizeofStackSaveArea(rFP), \_reg
+ .endm
+
+ /*
+ * Get the 32-bit value from a dalvik register.
+ */
+
+ .macro GET_VREG _vreg
+ movl (rFP,\_vreg, 4), \_vreg
+ .endm
+
+ /*
+ * Set the 32-bit value from a dalvik register.
+ */
+
+ .macro SET_VREG _reg _vreg
+ movl \_reg, (rFP,\_vreg, 4)
+ .endm
+
+ /*
+ * Fetch the next instruction from rPC into rINST. Does not advance rPC.
+ */
+
+ .macro FETCH_INST
+ movzwl (rPC), rINST
+ .endm
+
+ /*
+ * Fetch the next instruction from the specified offset. Advances rPC
+ * to point to the next instruction. "_count" is in 16-bit code units.
+ *
+ * This must come AFTER anything that can throw an exception, or the
+ * exception catch may miss. (This also implies that it must come after
+ * EXPORT_PC())
+ */
+
+ .macro FETCH_ADVANCE_INST _count
+ add $$(\_count*2), rPC
+ movzwl (rPC), rINST
+ .endm
+
+ /*
+ * Fetch the next instruction from an offset specified by _reg. Updates
+ * rPC to point to the next instruction. "_reg" must specify the distance
+ * in bytes, *not* 16-bit code units, and may be a signed value.
+ */
+
+ .macro FETCH_ADVANCE_INST_RB _reg
+ addl \_reg, rPC
+ movzwl (rPC), rINST
+ .endm
+
+ /*
+ * Fetch a half-word code unit from an offset past the current PC. The
+ * "_count" value is in 16-bit code units. Does not advance rPC.
+ * For example, given instruction of format: AA|op BBBB, it
+ * fetches BBBB.
+ */
+
+ .macro FETCH _count _reg
+ movzwl (\_count*2)(rPC), \_reg
+ .endm
+
+ /*
+ * Fetch a half-word code unit from an offset past the current PC. The
+ * "_count" value is in 16-bit code units. Does not advance rPC.
+ * This variant treats the value as signed.
+ */
+
+ .macro FETCHs _count _reg
+ movswl (\_count*2)(rPC), \_reg
+ .endm
+
+ /*
+ * Fetch the first byte from an offset past the current PC. The
+ * "_count" value is in 16-bit code units. Does not advance rPC.
+ * For example, given instruction of format: AA|op CC|BB, it
+ * fetches BB.
+ */
+
+ .macro FETCH_BB _count _reg
+ movzbl (\_count*2)(rPC), \_reg
+ .endm
+
+ /*
+ * Fetch the second byte from an offset past the current PC. The
+ * "_count" value is in 16-bit code units. Does not advance rPC.
+ * For example, given instruction of format: AA|op CC|BB, it
+ * fetches CC.
+ */
+
+ .macro FETCH_CC _count _reg
+ movzbl (\_count*2 + 1)(rPC), \_reg
+ .endm
+
+ /*
+ * Fetch the second byte from an offset past the current PC. The
+ * "_count" value is in 16-bit code units. Does not advance rPC.
+ * This variant treats the value as signed.
+ */
+
+ .macro FETCH_CCs _count _reg
+ movsbl (\_count*2 + 1)(rPC), \_reg
+ .endm
+
+
+ /*
+ * Fetch one byte from an offset past the current PC. Pass in the same
+ * "_count" as you would for FETCH, and an additional 0/1 indicating which
+ * byte of the halfword you want (lo/hi).
+ */
+
+ .macro FETCH_B _reg _count _byte
+ movzbl (\_count*2+\_byte)(rPC), \_reg
+ .endm
+
+ /*
+ * Put the instruction's opcode field into the specified register.
+ */
+
+ .macro GET_INST_OPCODE _reg
+ movzbl rINSTbl, \_reg
+ .endm
+
+ /*
+ * Begin executing the opcode in _reg.
+ */
+
+ .macro GOTO_OPCODE _reg
+ shl $$${handler_size_bits}, \_reg
+ addl $$dvmAsmInstructionStart,\_reg
+ jmp *\_reg
+ .endm
+
+
+
+ /*
+ * Macros pair attempts to speed up FETCH_INST, GET_INST_OPCODE and GOTO_OPCODE
+ * by using a jump table. _rFinish should must be the same register for
+ * both macros.
+ */
+
+ .macro FFETCH _rFinish
+ movzbl (rPC), \_rFinish
+ .endm
+
+ .macro FGETOP_JMPa _rFinish
+ movzbl 1(rPC), rINST
+ jmp *dvmAsmInstructionJmpTable(,\_rFinish, 4)
+ .endm
+
+ /*
+ * Macro pair attempts to speed up FETCH_INST, GET_INST_OPCODE and GOTO_OPCODE
+ * by using a jump table. _rFinish and _count should must be the same register for
+ * both macros.
+ */
+
+ .macro FFETCH_ADV _count _rFinish
+ movzbl (\_count*2)(rPC), \_rFinish
+ .endm
+
+ .macro FGETOP_JMP _count _rFinish
+ movzbl (\_count*2 + 1)(rPC), rINST
+ addl $$(\_count*2), rPC
+ jmp *dvmAsmInstructionJmpTable(,\_rFinish, 4)
+ .endm
+
+ /*
+ * Macro pair attempts to speed up FETCH_INST, GET_INST_OPCODE and GOTO_OPCODE
+ * by using a jump table. _rFinish and _reg should must be the same register for
+ * both macros.
+ */
+
+ .macro FFETCH_ADV_RB _reg _rFinish
+ movzbl (\_reg, rPC), \_rFinish
+ .endm
+
+ .macro FGETOP_RB_JMP _reg _rFinish
+ movzbl 1(\_reg, rPC), rINST
+ addl \_reg, rPC
+ jmp *dvmAsmInstructionJmpTable(,\_rFinish, 4)
+ .endm
+
+ /*
+ * Attempts to speed up FETCH_INST, GET_INST_OPCODE using
+ * a jump table. This macro should be called before FINISH_JMP where
+ * rFinish should be the same register containing the opcode value.
+ * This is an attempt to split up FINISH in order to reduce or remove
+ * potential stalls due to the wait for rFINISH.
+ */
+
+ .macro FINISH_FETCH _rFinish
+ movzbl (rPC), \_rFinish
+ movzbl 1(rPC), rINST
+ .endm
+
+
+ /*
+ * Attempts to speed up FETCH_ADVANCE_INST, GET_INST_OPCODE using
+ * a jump table. This macro should be called before FINISH_JMP where
+ * rFinish should be the same register containing the opcode value.
+ * This is an attempt to split up FINISH in order to reduce or remove
+ * potential stalls due to the wait for rFINISH.
+ */
+
+ .macro FINISH_FETCH_ADVANCE _count _rFinish
+ movzbl (\_count*2)(rPC), \_rFinish
+ movzbl (\_count*2 + 1)(rPC), rINST
+ addl $$(\_count*2), rPC
+ .endm
+
+ /*
+ * Attempts to speed up FETCH_ADVANCE_INST_RB, GET_INST_OPCODE using
+ * a jump table. This macro should be called before FINISH_JMP where
+ * rFinish should be the same register containing the opcode value.
+ * This is an attempt to split up FINISH in order to reduce or remove
+ * potential stalls due to the wait for rFINISH.
+ */
+
+ .macro FINISH_FETCH_ADVANCE_RB _reg _rFinish
+ movzbl (\_reg, rPC), \_rFinish
+ movzbl 1(\_reg, rPC), rINST
+ addl \_reg, rPC
+ .endm
+
+ /*
+ * Attempts to speed up GOTO_OPCODE using a jump table. This macro should
+ * be called after a FINISH_FETCH* instruction where rFinish should be the
+ * same register containing the opcode value. This is an attempt to split up
+ * FINISH in order to reduce or remove potential stalls due to the wait for rFINISH.
+ */
+
+ .macro FINISH_JMP _rFinish
+ jmp *dvmAsmInstructionJmpTable(,\_rFinish, 4)
+ .endm
+
+ /*
+ * Attempts to speed up FETCH_INST, GET_INST_OPCODE, GOTO_OPCODE by using
+ * a jump table. Uses a single macro - but it should be faster if we
+ * split up the fetch for rFinish and the jump using rFinish.
+ */
+
+ .macro FINISH_A
+ movzbl (rPC), rFinish
+ movzbl 1(rPC), rINST
+ jmp *dvmAsmInstructionJmpTable(,rFinish, 4)
+ .endm
+
+ /*
+ * Attempts to speed up FETCH_ADVANCE_INST, GET_INST_OPCODE,
+ * GOTO_OPCODE by using a jump table. Uses a single macro -
+ * but it should be faster if we split up the fetch for rFinish
+ * and the jump using rFinish.
+ */
+
+ .macro FINISH _count
+ movzbl (\_count*2)(rPC), rFinish
+ movzbl (\_count*2 + 1)(rPC), rINST
+ addl $$(\_count*2), rPC
+ jmp *dvmAsmInstructionJmpTable(,rFinish, 4)
+ .endm
+
+ /*
+ * Attempts to speed up FETCH_ADVANCE_INST_RB, GET_INST_OPCODE,
+ * GOTO_OPCODE by using a jump table. Uses a single macro -
+ * but it should be faster if we split up the fetch for rFinish
+ * and the jump using rFinish.
+ */
+
+ .macro FINISH_RB _reg _rFinish
+ movzbl (\_reg, rPC), \_rFinish
+ movzbl 1(\_reg, rPC), rINST
+ addl \_reg, rPC
+ jmp *dvmAsmInstructionJmpTable(,\_rFinish, 4)
+ .endm
+
+ /*
+ * Hard coded helper values.
+ */
+
+.balign 16
+
+.LdoubNeg:
+ .quad 0x8000000000000000
+
+.L64bits:
+ .quad 0xFFFFFFFFFFFFFFFF
+
+.LshiftMask2:
+ .quad 0x0000000000000000
+.LshiftMask:
+ .quad 0x000000000000003F
+
+.Lvalue64:
+ .quad 0x0000000000000040
+
+.LvaluePosInfLong:
+ .quad 0x7FFFFFFFFFFFFFFF
+
+.LvalueNegInfLong:
+ .quad 0x8000000000000000
+
+.LvalueNanLong:
+ .quad 0x0000000000000000
+
+.LintMin:
+.long 0x80000000
+
+.LintMax:
+.long 0x7FFFFFFF
diff --git a/vm/mterp/x86-atom/stub.S b/vm/mterp/x86-atom/stub.S
new file mode 100644
index 0000000..54f3f54
--- /dev/null
+++ b/vm/mterp/x86-atom/stub.S
@@ -0,0 +1,25 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: stub.S
+ */
+
+ SAVE_PC_FP_TO_GLUE %edx # save program counter and frame pointer
+ pushl rGLUE # push parameter glue
+ call dvmMterp_${opcode} # call c-based implementation
+ lea 4(%esp), %esp
+ LOAD_PC_FP_FROM_GLUE # restore program counter and frame pointer
+ FINISH_A # jump to next instruction
diff --git a/vm/mterp/x86-atom/unop.S b/vm/mterp/x86-atom/unop.S
new file mode 100644
index 0000000..00f9f8d
--- /dev/null
+++ b/vm/mterp/x86-atom/unop.S
@@ -0,0 +1,43 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: unop.S
+ *
+ * Code: Generic 32-bit unary operation. Provide an "instr" variable and a
+ * preinstr variable that together specify an instruction that
+ * performs, for example, "%ecx = op %edx".
+ *
+ * For: int-to-byte, int-to-char, int-to-short, neg-float, neg-int, not-int
+ *
+ * Description: Perform the identified unary operation on the source
+ * register, storing the result in the destination register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+%default {"preinstr":"", "instr":""}
+
+ movl rINST, %ecx # %ecx<- BA+
+ shr $$4, %ecx # %ecx<- B
+ and $$15, rINST # rINST<- A
+ FFETCH_ADV 1, %eax # %eax<- next instruction hi; fetch, advance
+ GET_VREG %ecx # %ecx<- vB
+ $preinstr # do operation part 1
+ $instr # do operation part 2
+ SET_VREG %ecx, rINST # vA<- result
+ FGETOP_JMP 1, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/unopWide.S b/vm/mterp/x86-atom/unopWide.S
new file mode 100644
index 0000000..3790a2c
--- /dev/null
+++ b/vm/mterp/x86-atom/unopWide.S
@@ -0,0 +1,43 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: unopWide.S
+ *
+ * Code: Generic 64-bit unary operation. Provide an "instr" variable and a
+ * preinstr variable that together specify an instruction that
+ * performs, for example, "%xmm0 = op %xmm1".
+ *
+ * For: neg-double, neg-long, not-long
+ *
+ * Description: Perform the identified unary operation on the source
+ * register, storing the result in the destination register
+ *
+ * Format: B|A|op (12x)
+ *
+ * Syntax: op vA, vB
+ */
+
+%default {"preinstr":"","result":"%xmm0"}
+
+ movl rINST, %ecx # %ecx<- BA+
+ shr $$4, rINST # rINST<- B
+ and $$15, %ecx # %ecx<- A
+ FFETCH_ADV 1, %eax # %eax<- next instruction hi; fetch, advance
+ movq (rFP, rINST, 4), %xmm0 # %xmm0<- vB
+ $preinstr # do operation part 1
+ $instr # do operation part 2
+ movq $result, (rFP, %ecx, 4) # vA<- result
+ FGETOP_JMP 1, %eax # jump to next instruction; getop, jmp
diff --git a/vm/mterp/x86-atom/unused.S b/vm/mterp/x86-atom/unused.S
new file mode 100644
index 0000000..8267709
--- /dev/null
+++ b/vm/mterp/x86-atom/unused.S
@@ -0,0 +1,30 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: unused.S
+ *
+ * Code: Common code for unused bytecodes. Uses no subtitutions.
+ *
+ * For: all unused bytecodes
+ *
+ * Description: aborts if executed.
+ *
+ * Format: ØØ|op (10x)
+ *
+ * Syntax: op
+ */
+
+ call common_abort
diff --git a/vm/mterp/x86-atom/zcmp.S b/vm/mterp/x86-atom/zcmp.S
new file mode 100644
index 0000000..6614dba
--- /dev/null
+++ b/vm/mterp/x86-atom/zcmp.S
@@ -0,0 +1,53 @@
+ /* Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * File: zcmp.S
+ *
+ * Code: Generic 32-bit comparison operation. Provides a "revcmp"
+ * variable to specify the reverse comparison to perform
+ *
+ * For: if-eqz, if-gez, if-gtz, if-lez, if-ltz, if-nez
+ *
+ * Description: Branch to the given destination if the given register's
+ * value compares with 0 as specified.
+ *
+ * Format: AA|op BBBB (21t)
+ *
+ * Syntax: op vAA, +BBBB
+ */
+
+ cmp $$0, (rFP, rINST, 4) # compare vAA with zero
+ j${revcmp} ${opcode}_2f # goto next instruction or branch
+ FETCHs 1, %edx # %edx<- BBBB; branch offset
+ sal $$1, %edx # %edx<- adjust byte offset
+
+ /*
+ * Inline common_backwardBranch
+ */
+
+ js common_periodicChecks_backwardBranch # jump on backwards branch
+1:
+ FINISH_RB %edx, %ecx # jump to next instruction
+
+ /*
+ * FINISH code
+ */
+
+${opcode}_2f:
+ movzbl 4(rPC), %edx # grab the next opcode
+ movzbl 5(rPC), rINST # update the instruction
+ addl $$4, rPC # update the program counter
+ jmp *dvmAsmInstructionJmpTable(, %edx, 4) # jump to next instruction
diff --git a/vm/mterp/x86/OP_ADD_DOUBLE.S b/vm/mterp/x86/OP_ADD_DOUBLE.S
new file mode 100644
index 0000000..9fded29
--- /dev/null
+++ b/vm/mterp/x86/OP_ADD_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop.S" {"instr":"faddl","load":"fldl","store":"fstpl"}
diff --git a/vm/mterp/x86/OP_ADD_DOUBLE_2ADDR.S b/vm/mterp/x86/OP_ADD_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..81e55ea
--- /dev/null
+++ b/vm/mterp/x86/OP_ADD_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop2addr.S" {"instr":"faddl","load":"fldl","store":"fstpl"}
diff --git a/vm/mterp/x86/OP_ADD_FLOAT.S b/vm/mterp/x86/OP_ADD_FLOAT.S
new file mode 100644
index 0000000..61a6f68
--- /dev/null
+++ b/vm/mterp/x86/OP_ADD_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop.S" {"instr":"fadds","load":"flds","store":"fstps"}
diff --git a/vm/mterp/x86/OP_ADD_FLOAT_2ADDR.S b/vm/mterp/x86/OP_ADD_FLOAT_2ADDR.S
new file mode 100644
index 0000000..fd61457
--- /dev/null
+++ b/vm/mterp/x86/OP_ADD_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop2addr.S" {"instr":"fadds","load":"flds","store":"fstps"}
diff --git a/vm/mterp/x86/OP_ADD_INT.S b/vm/mterp/x86/OP_ADD_INT.S
new file mode 100644
index 0000000..b95a2db
--- /dev/null
+++ b/vm/mterp/x86/OP_ADD_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop.S" {"instr":"addl (rFP,%ecx,4),%eax"}
diff --git a/vm/mterp/x86/OP_ADD_INT_2ADDR.S b/vm/mterp/x86/OP_ADD_INT_2ADDR.S
new file mode 100644
index 0000000..5d4681b
--- /dev/null
+++ b/vm/mterp/x86/OP_ADD_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop2addr.S" {"instr":"addl %eax,(rFP,%ecx,4)"}
diff --git a/vm/mterp/x86/OP_ADD_INT_LIT16.S b/vm/mterp/x86/OP_ADD_INT_LIT16.S
new file mode 100644
index 0000000..d96821d
--- /dev/null
+++ b/vm/mterp/x86/OP_ADD_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit16.S" {"instr":"addl %ecx,%eax"}
diff --git a/vm/mterp/x86/OP_ADD_INT_LIT8.S b/vm/mterp/x86/OP_ADD_INT_LIT8.S
new file mode 100644
index 0000000..5d477fb
--- /dev/null
+++ b/vm/mterp/x86/OP_ADD_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit8.S" {"instr":"addl %ecx,%eax"}
diff --git a/vm/mterp/x86/OP_ADD_LONG.S b/vm/mterp/x86/OP_ADD_LONG.S
new file mode 100644
index 0000000..861cf01
--- /dev/null
+++ b/vm/mterp/x86/OP_ADD_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopWide.S" {"instr1":"addl (rFP,%ecx,4),rPC", "instr2":"adcl 4(rFP,%ecx,4),%eax"}
diff --git a/vm/mterp/x86/OP_ADD_LONG_2ADDR.S b/vm/mterp/x86/OP_ADD_LONG_2ADDR.S
new file mode 100644
index 0000000..775802f
--- /dev/null
+++ b/vm/mterp/x86/OP_ADD_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopWide2addr.S" {"instr1":"addl %eax,(rFP,rINST_FULL,4)","instr2":"adcl %ecx,4(rFP,rINST_FULL,4)"}
diff --git a/vm/mterp/x86/OP_AGET.S b/vm/mterp/x86/OP_AGET.S
new file mode 100644
index 0000000..fe87037
--- /dev/null
+++ b/vm/mterp/x86/OP_AGET.S
@@ -0,0 +1,23 @@
+%default { "load":"movl", "shift":"4" }
+%verify "executed"
+ /*
+ * Array get, 32 bits or less. vAA <- vBB[vCC].
+ *
+ * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
+ */
+ /* op vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG(%eax,%eax) # eax<- vBB (array object)
+ GET_VREG(%ecx,%ecx) # ecs<- vCC (requested index)
+ testl %eax,%eax # null array object?
+ je common_errNullObject # bail if so
+ cmpl offArrayObject_length(%eax),%ecx
+ jae common_errArrayIndex # index >= length, bail
+ $load offArrayObject_contents(%eax,%ecx,$shift),%eax
+ movl rINST_FULL,%ecx
+ FETCH_INST_WORD(2)
+ SET_VREG(%eax,%ecx)
+ ADVANCE_PC(2)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_AGET_BOOLEAN.S b/vm/mterp/x86/OP_AGET_BOOLEAN.S
new file mode 100644
index 0000000..27f6de0
--- /dev/null
+++ b/vm/mterp/x86/OP_AGET_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_AGET.S" { "load":"movzbl", "shift":"1" }
diff --git a/vm/mterp/x86/OP_AGET_BYTE.S b/vm/mterp/x86/OP_AGET_BYTE.S
new file mode 100644
index 0000000..49c83e1
--- /dev/null
+++ b/vm/mterp/x86/OP_AGET_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_AGET.S" { "load":"movsbl", "shift":"1" }
diff --git a/vm/mterp/x86/OP_AGET_CHAR.S b/vm/mterp/x86/OP_AGET_CHAR.S
new file mode 100644
index 0000000..b5f6bcf
--- /dev/null
+++ b/vm/mterp/x86/OP_AGET_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_AGET.S" { "load":"movzwl", "shift":"2" }
diff --git a/vm/mterp/x86/OP_AGET_OBJECT.S b/vm/mterp/x86/OP_AGET_OBJECT.S
new file mode 100644
index 0000000..0c43394
--- /dev/null
+++ b/vm/mterp/x86/OP_AGET_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_AGET.S"
diff --git a/vm/mterp/x86/OP_AGET_SHORT.S b/vm/mterp/x86/OP_AGET_SHORT.S
new file mode 100644
index 0000000..aeeffa3
--- /dev/null
+++ b/vm/mterp/x86/OP_AGET_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_AGET.S" { "load":"movswl", "shift":"2" }
diff --git a/vm/mterp/x86/OP_AGET_WIDE.S b/vm/mterp/x86/OP_AGET_WIDE.S
new file mode 100644
index 0000000..c050156
--- /dev/null
+++ b/vm/mterp/x86/OP_AGET_WIDE.S
@@ -0,0 +1,27 @@
+%verify "executed"
+ /*
+ * Array get, 64 bits. vAA <- vBB[vCC].
+ *
+ */
+ /* op vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG(%eax,%eax) # eax<- vBB (array object)
+ GET_VREG(%ecx,%ecx) # ecs<- vCC (requested index)
+ testl %eax,%eax # null array object?
+ je common_errNullObject # bail if so
+ cmpl offArrayObject_length(%eax),%ecx
+ jb .L${opcode}_finish # index < length, OK
+ jmp common_errArrayIndex # index >= length, bail
+%break
+
+.L${opcode}_finish:
+ leal offArrayObject_contents(%eax,%ecx,8),%eax
+ movl (%eax),%ecx
+ movl 4(%eax),%eax
+ SET_VREG_WORD(%ecx,rINST_FULL,0)
+ SET_VREG_WORD(%eax,rINST_FULL,1)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_AND_INT.S b/vm/mterp/x86/OP_AND_INT.S
new file mode 100644
index 0000000..3bb8757
--- /dev/null
+++ b/vm/mterp/x86/OP_AND_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop.S" {"instr":"andl (rFP,%ecx,4),%eax"}
diff --git a/vm/mterp/x86/OP_AND_INT_2ADDR.S b/vm/mterp/x86/OP_AND_INT_2ADDR.S
new file mode 100644
index 0000000..2dee26b
--- /dev/null
+++ b/vm/mterp/x86/OP_AND_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop2addr.S" {"instr":"andl %eax,(rFP,%ecx,4)"}
diff --git a/vm/mterp/x86/OP_AND_INT_LIT16.S b/vm/mterp/x86/OP_AND_INT_LIT16.S
new file mode 100644
index 0000000..e0fe168
--- /dev/null
+++ b/vm/mterp/x86/OP_AND_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit16.S" {"instr":"andl %ecx,%eax"}
diff --git a/vm/mterp/x86/OP_AND_INT_LIT8.S b/vm/mterp/x86/OP_AND_INT_LIT8.S
new file mode 100644
index 0000000..62c68dc
--- /dev/null
+++ b/vm/mterp/x86/OP_AND_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit8.S" {"instr":"andl %ecx,%eax"}
diff --git a/vm/mterp/x86/OP_AND_LONG.S b/vm/mterp/x86/OP_AND_LONG.S
new file mode 100644
index 0000000..0feaead
--- /dev/null
+++ b/vm/mterp/x86/OP_AND_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopWide.S" {"instr1":"andl (rFP,%ecx,4),rPC", "instr2":"andl 4(rFP,%ecx,4),%eax"}
diff --git a/vm/mterp/x86/OP_AND_LONG_2ADDR.S b/vm/mterp/x86/OP_AND_LONG_2ADDR.S
new file mode 100644
index 0000000..47d99d9
--- /dev/null
+++ b/vm/mterp/x86/OP_AND_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopWide2addr.S" {"instr1":"andl %eax,(rFP,rINST_FULL,4)","instr2":"andl %ecx,4(rFP,rINST_FULL,4)"}
diff --git a/vm/mterp/x86/OP_APUT.S b/vm/mterp/x86/OP_APUT.S
new file mode 100644
index 0000000..b9a660f
--- /dev/null
+++ b/vm/mterp/x86/OP_APUT.S
@@ -0,0 +1,23 @@
+%default { "reg":"%ecx", "store":"movl", "shift":"4" }
+%verify "executed"
+ /*
+ * Array put, 32 bits or less. vBB[vCC] <- vAA
+ *
+ * for: aput, aput-object, aput-boolean, aput-byte, aput-char, aput-short
+ */
+ /* op vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG(%eax,%eax) # eax<- vBB (array object)
+ GET_VREG(%ecx,%ecx) # ecs<- vCC (requested index)
+ testl %eax,%eax # null array object?
+ je common_errNullObject # bail if so
+ cmpl offArrayObject_length(%eax),%ecx
+ jae common_errArrayIndex # index >= length, bail
+ leal offArrayObject_contents(%eax,%ecx,$shift),%eax
+ GET_VREG(%ecx,rINST_FULL)
+ FETCH_INST_WORD(2)
+ $store $reg,(%eax)
+ ADVANCE_PC(2)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_APUT_BOOLEAN.S b/vm/mterp/x86/OP_APUT_BOOLEAN.S
new file mode 100644
index 0000000..14f8c7f
--- /dev/null
+++ b/vm/mterp/x86/OP_APUT_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_APUT.S" {"reg":"%cl", "store":"movb", "shift":"1" }
diff --git a/vm/mterp/x86/OP_APUT_BYTE.S b/vm/mterp/x86/OP_APUT_BYTE.S
new file mode 100644
index 0000000..d92225f
--- /dev/null
+++ b/vm/mterp/x86/OP_APUT_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_APUT.S" { "reg":"%cl", "store":"movb", "shift":"1" }
diff --git a/vm/mterp/x86/OP_APUT_CHAR.S b/vm/mterp/x86/OP_APUT_CHAR.S
new file mode 100644
index 0000000..d466007
--- /dev/null
+++ b/vm/mterp/x86/OP_APUT_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_APUT.S" { "reg":"%cx", "store":"movw", "shift":"2" }
diff --git a/vm/mterp/x86/OP_APUT_OBJECT.S b/vm/mterp/x86/OP_APUT_OBJECT.S
new file mode 100644
index 0000000..3cd9e0d
--- /dev/null
+++ b/vm/mterp/x86/OP_APUT_OBJECT.S
@@ -0,0 +1,56 @@
+%verify "executed"
+ /*
+ * Array put, 32 bits or less. vBB[vCC] <- vAA
+ *
+ * for: aput, aput-object, aput-boolean, aput-byte, aput-char, aput-short
+ */
+ /* op vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG(%eax,%eax) # eax<- vBB (array object)
+ GET_VREG(%ecx,%ecx) # ecs<- vCC (requested index)
+ GET_VREG(rINST_FULL,rINST_FULL) # rINST_FULL<- vAA
+ testl %eax,%eax # null array object?
+ je common_errNullObject # bail if so
+ cmpl offArrayObject_length(%eax),%ecx
+ jb .L${opcode}_continue
+ jmp common_errArrayIndex # index >= length, bail
+%break
+
+ /* On entry:
+ * eax<- array object
+ * ecx<- index
+ * rINST_FULL<- vAA
+ */
+.L${opcode}_continue:
+ leal offArrayObject_contents(%eax,%ecx,4),%ecx
+ testl rINST_FULL,rINST_FULL # storing null reference?
+ je .L${opcode}_skip_check
+ SPILL(rPC)
+ SPILL_TMP(%ecx)
+ movl %eax,LOCAL0_OFFSET(%ebp) # save copy of object head
+ movl offObject_clazz(%eax),%eax # eax<- arrayObj->clazz
+ movl offObject_clazz(rINST_FULL),%ecx # ecx<- obj->clazz
+ movl %eax,OUT_ARG1(%esp)
+ movl %ecx,OUT_ARG0(%esp)
+ call dvmCanPutArrayElement # test object type vs. array type
+ UNSPILL(rPC)
+ UNSPILL_TMP(%ecx)
+ testl %eax,%eax
+ GET_GLUE(%eax)
+ je common_errArrayStore
+ movl offGlue_cardTable(%eax),%eax # get card table base
+ movl rINST_FULL,(%ecx)
+ movl LOCAL0_OFFSET(%ebp),%ecx # recover object head
+ FETCH_INST_WORD(2)
+ shrl $$GC_CARD_SHIFT,%ecx # object head to card number
+ movb %al,(%eax,%ecx) # mark card using object head
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+.L${opcode}_skip_check:
+ movl rINST_FULL,(%ecx)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_APUT_SHORT.S b/vm/mterp/x86/OP_APUT_SHORT.S
new file mode 100644
index 0000000..d466007
--- /dev/null
+++ b/vm/mterp/x86/OP_APUT_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_APUT.S" { "reg":"%cx", "store":"movw", "shift":"2" }
diff --git a/vm/mterp/x86/OP_APUT_WIDE.S b/vm/mterp/x86/OP_APUT_WIDE.S
new file mode 100644
index 0000000..658ca7c
--- /dev/null
+++ b/vm/mterp/x86/OP_APUT_WIDE.S
@@ -0,0 +1,27 @@
+%verify "executed"
+ /*
+ * Array put, 64 bits. vBB[vCC]<-vAA.
+ *
+ */
+ /* op vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG(%eax,%eax) # eax<- vBB (array object)
+ GET_VREG(%ecx,%ecx) # ecs<- vCC (requested index)
+ testl %eax,%eax # null array object?
+ je common_errNullObject # bail if so
+ cmpl offArrayObject_length(%eax),%ecx
+ jb .L${opcode}_finish # index < length, OK
+ jmp common_errArrayIndex # index >= length, bail
+%break
+
+.L${opcode}_finish:
+ leal offArrayObject_contents(%eax,%ecx,8),%eax
+ GET_VREG_WORD(%ecx,rINST_FULL,0)
+ GET_VREG_WORD(rINST_FULL,rINST_FULL,1)
+ movl rINST_FULL,4(%eax)
+ FETCH_INST_WORD(2)
+ movl %ecx,(%eax)
+ ADVANCE_PC(2)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_ARRAY_LENGTH.S b/vm/mterp/x86/OP_ARRAY_LENGTH.S
new file mode 100644
index 0000000..8d2a5a2
--- /dev/null
+++ b/vm/mterp/x86/OP_ARRAY_LENGTH.S
@@ -0,0 +1,15 @@
+%verify "executed"
+ /*
+ * Return the length of an array.
+ */
+ movzbl rINST_HI,%eax # eax<- BA
+ sarl $$12,rINST_FULL # rINST_FULL<- B
+ GET_VREG(%ecx,rINST_FULL) # ecx<- vB (object ref)
+ andb $$0xf,%al # eax<- A
+ testl %ecx,%ecx # is null?
+ je common_errNullObject
+ FETCH_INST_WORD(1)
+ movl offArrayObject_length(%ecx),%ecx
+ ADVANCE_PC(1)
+ SET_VREG(%ecx,%eax)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_BREAKPOINT.S b/vm/mterp/x86/OP_BREAKPOINT.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_BREAKPOINT.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_CHECK_CAST.S b/vm/mterp/x86/OP_CHECK_CAST.S
new file mode 100644
index 0000000..b9d651c
--- /dev/null
+++ b/vm/mterp/x86/OP_CHECK_CAST.S
@@ -0,0 +1,82 @@
+%verify "executed"
+%verify "null object"
+%verify "class cast exception thrown, with correct class name"
+%verify "class cast exception not thrown on same class"
+%verify "class cast exception not thrown on subclass"
+%verify "class not resolved"
+%verify "class already resolved"
+ /*
+ * Check to see if a cast from one class to another is allowed.
+ */
+ /* check-cast vAA, class@BBBB */
+ GET_GLUE(%ecx)
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG(rINST_FULL,rINST_FULL) # rINST_FULL<- vAA (object)
+ movzwl 2(rPC),%eax # eax<- BBBB
+ movl offGlue_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+ testl rINST_FULL,rINST_FULL # is oject null?
+ movl offDvmDex_pResClasses(%ecx),%ecx # ecx<- pDvmDex->pResClasses
+ je .L${opcode}_okay # null obj, cast always succeeds
+ movl (%ecx,%eax,4),%eax # eax<- resolved class
+ movl offObject_clazz(rINST_FULL),%ecx # ecx<- obj->clazz
+ testl %eax,%eax # have we resolved this before?
+ je .L${opcode}_resolve # no, go do it now
+.L${opcode}_resolved:
+ cmpl %eax,%ecx # same class (trivial success)?
+ jne .L${opcode}_fullcheck # no, do full check
+.L${opcode}_okay:
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+%break
+
+ /*
+ * Trivial test failed, need to perform full check. This is common.
+ * ecx holds obj->clazz
+ * eax holds class resolved from BBBB
+ * rINST_FULL holds object
+ */
+.L${opcode}_fullcheck:
+ movl %eax,OUT_ARG1(%esp)
+ movl %ecx,OUT_ARG0(%esp)
+ SPILL(rPC)
+ call dvmInstanceofNonTrivial # eax<- boolean result
+ UNSPILL(rPC)
+ testl %eax,%eax # failed?
+ jne .L${opcode}_okay # no, success
+
+ # A cast has failed. We need to throw a ClassCastException with the
+ # class of the object that failed to be cast.
+ EXPORT_PC()
+ movl offObject_clazz(rINST_FULL),%ecx # ecx<- obj->clazz
+ movl $$.LstrClassCastException,%eax
+ movl offClassObject_descriptor(%ecx),%ecx
+ movl %eax,OUT_ARG0(%esp) # arg0<- message
+ movl %ecx,OUT_ARG1(%esp) # arg1<- obj->clazz->descriptor
+ SPILL(rPC)
+ call dvmThrowExceptionWithClassMessage
+ UNSPILL(rPC)
+ jmp common_exceptionThrown
+
+ /*
+ * Resolution required. This is the least-likely path, and we're
+ * going to have to recreate some data.
+ *
+ * rINST_FULL holds object
+ */
+.L${opcode}_resolve:
+ GET_GLUE(%ecx)
+ EXPORT_PC()
+ movzwl 2(rPC),%eax # eax<- BBBB
+ movl offGlue_method(%ecx),%ecx # ecx<- glue->method
+ movl %eax,OUT_ARG1(%esp) # arg1<- BBBB
+ movl offMethod_clazz(%ecx),%ecx # ecx<- metho->clazz
+ movl $$0,OUT_ARG2(%esp) # arg2<- false
+ movl %ecx,OUT_ARG0(%esp) # arg0<- method->clazz
+ SPILL(rPC)
+ call dvmResolveClass # eax<- resolved ClassObject ptr
+ UNSPILL(rPC)
+ testl %eax,%eax # got null?
+ je common_exceptionThrown # yes, handle exception
+ movl offObject_clazz(rINST_FULL),%ecx # ecx<- obj->clazz
+ jmp .L${opcode}_resolved # pick up where we left off
diff --git a/vm/mterp/x86/OP_CMPG_DOUBLE.S b/vm/mterp/x86/OP_CMPG_DOUBLE.S
new file mode 100644
index 0000000..0eb1ac2
--- /dev/null
+++ b/vm/mterp/x86/OP_CMPG_DOUBLE.S
@@ -0,0 +1,36 @@
+%default {"is_double":"1","nanval":"1"}
+%verify "executed"
+%verify "basic lt, gt, eq"
+%verify "left arg NaN"
+%verify "right arg NaN"
+ /* float/double_cmp[gl] vAA, vBB, vCC */
+ movzbl 3(rPC),%eax # eax<- CC
+ movzbl 2(rPC),%ecx # ecx<- BB
+ .if $is_double
+ fldl (rFP,%eax,4)
+ fldl (rFP,%ecx,4)
+ .else
+ flds (rFP,%eax,4)
+ flds (rFP,%ecx,4)
+ .endif
+ movzbl rINST_HI,rINST_FULL
+ xorl %ecx,%ecx
+ fucompp # z if equal, p set if NaN, c set if st0 < st1
+ fnstsw %ax
+ sahf
+ movl rINST_FULL,%eax
+ FETCH_INST_WORD(2)
+ jp .L${opcode}_isNaN
+ je .L${opcode}_finish
+ sbbl %ecx,%ecx
+ jb .L${opcode}_finish
+ incl %ecx
+.L${opcode}_finish:
+ SET_VREG(%ecx,%eax)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+%break
+
+.L${opcode}_isNaN:
+ movl $$$nanval,%ecx
+ jmp .L${opcode}_finish
diff --git a/vm/mterp/x86/OP_CMPG_FLOAT.S b/vm/mterp/x86/OP_CMPG_FLOAT.S
new file mode 100644
index 0000000..bd6674a
--- /dev/null
+++ b/vm/mterp/x86/OP_CMPG_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_CMPG_DOUBLE.S" {"is_double":"0","nanval":"1"}
diff --git a/vm/mterp/x86/OP_CMPL_DOUBLE.S b/vm/mterp/x86/OP_CMPL_DOUBLE.S
new file mode 100644
index 0000000..54ff0d8
--- /dev/null
+++ b/vm/mterp/x86/OP_CMPL_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_CMPG_DOUBLE.S" {"is_double":"1","nanval":"-1"}
diff --git a/vm/mterp/x86/OP_CMPL_FLOAT.S b/vm/mterp/x86/OP_CMPL_FLOAT.S
new file mode 100644
index 0000000..bbb674d
--- /dev/null
+++ b/vm/mterp/x86/OP_CMPL_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_CMPG_DOUBLE.S" {"is_double":"0","nanval":"-1"}
diff --git a/vm/mterp/x86/OP_CMP_LONG.S b/vm/mterp/x86/OP_CMP_LONG.S
new file mode 100644
index 0000000..3b1afbc
--- /dev/null
+++ b/vm/mterp/x86/OP_CMP_LONG.S
@@ -0,0 +1,37 @@
+%verify "executed"
+%verify "basic lt, gt, eq"
+%verify "hi equal, lo <=>"
+%verify "lo equal, hi <=>"
+ /*
+ * Compare two 64-bit values. Puts 0, 1, or -1 into the destination
+ * register based on the results of the comparison.
+ */
+ /* cmp-long vAA, vBB, vCC */
+ movzbl 2(rPC),%ecx # ecx<- BB
+ SPILL(rPC)
+ movzbl 3(rPC),rPC # rPC<- CC
+ GET_VREG_WORD(%eax,%ecx,1) # eax<- v[BB+1]
+ GET_VREG_WORD(%ecx,%ecx,0) # ecx<- v[BB+0]
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ cmpl 4(rFP,rPC,4),%eax
+ jl .L${opcode}_smaller
+ jg .L${opcode}_bigger
+ sub (rFP,rPC,4),%ecx
+ ja .L${opcode}_bigger
+ jb .L${opcode}_smaller
+ UNSPILL(rPC)
+ jmp .L${opcode}_finish
+%break
+
+.L${opcode}_bigger:
+ UNSPILL(rPC)
+ movl $$1,%ecx
+ jmp .L${opcode}_finish
+.L${opcode}_smaller:
+ UNSPILL(rPC)
+ movl $$-1,%ecx
+.L${opcode}_finish:
+ SET_VREG(%ecx,rINST_FULL)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_CONST.S b/vm/mterp/x86/OP_CONST.S
new file mode 100644
index 0000000..80d547a
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST.S
@@ -0,0 +1,8 @@
+%verify "executed"
+ /* const vAA, #+BBBBbbbb */
+ movzbl rINST_HI,%ecx # ecx<- AA
+ movl 2(rPC),%eax # grab all 32 bits at once
+ FETCH_INST_WORD(3)
+ ADVANCE_PC(3)
+ SET_VREG(%eax,%ecx) # vAA<- eax
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_CONST_16.S b/vm/mterp/x86/OP_CONST_16.S
new file mode 100644
index 0000000..d45f1c1
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_16.S
@@ -0,0 +1,8 @@
+%verify "executed"
+ /* const/16 vAA, #+BBBB */
+ movswl 2(rPC),%ecx # ecx<- ssssBBBB
+ movzx rINST_HI,%eax # eax<- AA
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ SET_VREG(%ecx,%eax) # vAA<- ssssBBBB
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_CONST_4.S b/vm/mterp/x86/OP_CONST_4.S
new file mode 100644
index 0000000..499f7c3
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_4.S
@@ -0,0 +1,10 @@
+%verify "executed"
+ /* const/4 vA, #+B */
+ movsx rINST_HI,%eax # eax<-ssssssBx
+ movl $$0xf,%ecx
+ andl %eax,%ecx # ecx<- A
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ sarl $$4,%eax
+ SET_VREG(%eax,%ecx)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_CONST_CLASS.S b/vm/mterp/x86/OP_CONST_CLASS.S
new file mode 100644
index 0000000..fa37917
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_CLASS.S
@@ -0,0 +1,41 @@
+
+%verify "Class already resolved"
+%verify "Class not yet resolved"
+%verify "Class cannot be resolved"
+ /* const/class vAA, Class@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- BBBB
+ movl offGlue_methodClassDex(%ecx),%ecx# ecx<- glue->methodClassDex
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ movl offDvmDex_pResClasses(%ecx),%ecx # ecx<- dvmDex->pResClasses
+ movl (%ecx,%eax,4),%eax # eax<- rResClasses[BBBB]
+ movl rINST_FULL,%ecx
+ FETCH_INST_WORD(2)
+ testl %eax,%eax # resolved yet?
+ je .L${opcode}_resolve
+ SET_VREG(%eax,%ecx) # vAA<- rResClasses[BBBB]
+ ADVANCE_PC(2)
+ GOTO_NEXT
+%break
+
+/* This is the less common path, so we'll redo some work
+ here rather than force spills on the common path */
+.L${opcode}_resolve:
+ GET_GLUE(%eax)
+ movl %ecx,rINST_FULL # rINST_FULL<- AA
+ EXPORT_PC()
+ movl offGlue_method(%eax),%eax # eax<- glue->method
+ movl $$1,OUT_ARG2(%esp) # true
+ movzwl 2(rPC),%ecx # ecx<- BBBB
+ movl offMethod_clazz(%eax),%eax
+ SPILL(rPC)
+ movl %ecx,OUT_ARG1(%esp)
+ movl %eax,OUT_ARG0(%esp)
+ call dvmResolveClass # go resolve
+ UNSPILL(rPC)
+ testl %eax,%eax # failed?
+ je common_exceptionThrown
+ SET_VREG(%eax,rINST_FULL)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_CONST_HIGH16.S b/vm/mterp/x86/OP_CONST_HIGH16.S
new file mode 100644
index 0000000..9e9fa8a
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_HIGH16.S
@@ -0,0 +1,9 @@
+%verify "executed"
+ /* const/high16 vAA, #+BBBB0000 */
+ movzwl 2(rPC),%eax # eax<- 0000BBBB
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ sall $$16,%eax # eax<- BBBB0000
+ SET_VREG(%eax,%ecx) # vAA<- eax
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_CONST_STRING.S b/vm/mterp/x86/OP_CONST_STRING.S
new file mode 100644
index 0000000..96e5dcd
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_STRING.S
@@ -0,0 +1,40 @@
+
+%verify "String already resolved"
+%verify "String not yet resolved"
+%verify "String cannot be resolved"
+ /* const/string vAA, String@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- BBBB
+ movl offGlue_methodClassDex(%ecx),%ecx# ecx<- glue->methodClassDex
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ movl offDvmDex_pResStrings(%ecx),%ecx # ecx<- dvmDex->pResStrings
+ movl (%ecx,%eax,4),%eax # eax<- rResString[BBBB]
+ movl rINST_FULL,%ecx
+ FETCH_INST_WORD(2)
+ testl %eax,%eax # resolved yet?
+ je .L${opcode}_resolve
+ SET_VREG(%eax,%ecx) # vAA<- rResString[BBBB]
+ ADVANCE_PC(2)
+ GOTO_NEXT
+%break
+
+/* This is the less common path, so we'll redo some work
+ here rather than force spills on the common path */
+.L${opcode}_resolve:
+ GET_GLUE(%eax)
+ movl %ecx,rINST_FULL # rINST_FULL<- AA
+ EXPORT_PC()
+ movl offGlue_method(%eax),%eax # eax<- glue->method
+ movzwl 2(rPC),%ecx # ecx<- BBBB
+ movl offMethod_clazz(%eax),%eax
+ SPILL(rPC)
+ movl %ecx,OUT_ARG1(%esp)
+ movl %eax,OUT_ARG0(%esp)
+ call dvmResolveString # go resolve
+ UNSPILL(rPC)
+ testl %eax,%eax # failed?
+ je common_exceptionThrown
+ SET_VREG(%eax,rINST_FULL)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_CONST_STRING_JUMBO.S b/vm/mterp/x86/OP_CONST_STRING_JUMBO.S
new file mode 100644
index 0000000..45316f9
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_STRING_JUMBO.S
@@ -0,0 +1,40 @@
+
+%verify "String already resolved"
+%verify "String not yet resolved"
+%verify "String cannot be resolved"
+ /* const/string vAA, String@BBBBBBBB */
+ GET_GLUE(%ecx)
+ movl 2(rPC),%eax # eax<- BBBBBBBB
+ movl offGlue_methodClassDex(%ecx),%ecx# ecx<- glue->methodClassDex
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ movl offDvmDex_pResStrings(%ecx),%ecx # ecx<- dvmDex->pResStrings
+ movl (%ecx,%eax,4),%eax # eax<- rResString[BBBB]
+ movl rINST_FULL,%ecx
+ FETCH_INST_WORD(3)
+ testl %eax,%eax # resolved yet?
+ je .L${opcode}_resolve
+ SET_VREG(%eax,%ecx) # vAA<- rResString[BBBB]
+ ADVANCE_PC(3)
+ GOTO_NEXT
+%break
+
+/* This is the less common path, so we'll redo some work
+ here rather than force spills on the common path */
+.L${opcode}_resolve:
+ GET_GLUE(%eax)
+ movl %ecx,rINST_FULL # rINST_FULL<- AA
+ EXPORT_PC()
+ movl offGlue_method(%eax),%eax # eax<- glue->method
+ movl 2(rPC),%ecx # ecx<- BBBBBBBB
+ movl offMethod_clazz(%eax),%eax
+ SPILL(rPC)
+ movl %ecx,OUT_ARG1(%esp)
+ movl %eax,OUT_ARG0(%esp)
+ call dvmResolveString # go resolve
+ UNSPILL(rPC)
+ testl %eax,%eax # failed?
+ je common_exceptionThrown
+ SET_VREG(%eax,rINST_FULL)
+ FETCH_INST_WORD(3)
+ ADVANCE_PC(3)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_CONST_WIDE.S b/vm/mterp/x86/OP_CONST_WIDE.S
new file mode 100644
index 0000000..aa582b8
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_WIDE.S
@@ -0,0 +1,11 @@
+%verify "executed"
+ /* const-wide vAA, #+HHHHhhhhBBBBbbbb */
+ movl 2(rPC),%eax # eax<- lsw
+ movzbl rINST_HI,%ecx # ecx <- AA
+ movl 6(rPC),rINST_FULL # rINST_FULL<- msw
+ leal (rFP,%ecx,4),%ecx # dst addr
+ movl rINST_FULL,4(%ecx)
+ FETCH_INST_WORD(5)
+ movl %eax,(%ecx)
+ ADVANCE_PC(5)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_CONST_WIDE_16.S b/vm/mterp/x86/OP_CONST_WIDE_16.S
new file mode 100644
index 0000000..03270dc
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_WIDE_16.S
@@ -0,0 +1,12 @@
+%verify "executed"
+ /* const-wide/16 vAA, #+BBBB */
+ movswl 2(rPC),%eax # eax<- ssssBBBB
+ SPILL(rPC)
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(2)
+ cltd # rPC:eax<- ssssssssssssBBBB
+ SET_VREG_WORD(rPC,%ecx,1) # store msw
+ UNSPILL(rPC)
+ SET_VREG_WORD(%eax,%ecx,0) # store lsw
+ ADVANCE_PC(2)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_CONST_WIDE_32.S b/vm/mterp/x86/OP_CONST_WIDE_32.S
new file mode 100644
index 0000000..19246c7
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_WIDE_32.S
@@ -0,0 +1,12 @@
+%verify "executed"
+ /* const-wide/32 vAA, #+BBBBbbbb */
+ movl 2(rPC),%eax # eax<- BBBBbbbb
+ SPILL(rPC)
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(3)
+ cltd # rPC:eax<- ssssssssssssBBBB
+ SET_VREG_WORD(rPC,%ecx,1) # store msw
+ UNSPILL(rPC)
+ SET_VREG_WORD(%eax,%ecx,0) # store lsw
+ ADVANCE_PC(3)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_CONST_WIDE_HIGH16.S b/vm/mterp/x86/OP_CONST_WIDE_HIGH16.S
new file mode 100644
index 0000000..1d726f4
--- /dev/null
+++ b/vm/mterp/x86/OP_CONST_WIDE_HIGH16.S
@@ -0,0 +1,11 @@
+%verify "executed"
+ /* const-wide/high16 vAA, #+BBBB000000000000 */
+ movzwl 2(rPC),%eax # eax<- 0000BBBB
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ sall $$16,%eax # eax<- BBBB0000
+ SET_VREG_WORD(%eax,%ecx,1) # v[AA+1]<- eax
+ xorl %eax,%eax
+ SET_VREG_WORD(%eax,%ecx,0) # v[AA+0]<- eax
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_DIV_DOUBLE.S b/vm/mterp/x86/OP_DIV_DOUBLE.S
new file mode 100644
index 0000000..8ccff2e
--- /dev/null
+++ b/vm/mterp/x86/OP_DIV_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop.S" {"instr":"fdivl","load":"fldl","store":"fstpl"}
diff --git a/vm/mterp/x86/OP_DIV_DOUBLE_2ADDR.S b/vm/mterp/x86/OP_DIV_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..60a0072
--- /dev/null
+++ b/vm/mterp/x86/OP_DIV_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop2addr.S" {"instr":"fdivl","load":"fldl","store":"fstpl"}
diff --git a/vm/mterp/x86/OP_DIV_FLOAT.S b/vm/mterp/x86/OP_DIV_FLOAT.S
new file mode 100644
index 0000000..740e26e
--- /dev/null
+++ b/vm/mterp/x86/OP_DIV_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop.S" {"instr":"fdivs","load":"flds","store":"fstps"}
diff --git a/vm/mterp/x86/OP_DIV_FLOAT_2ADDR.S b/vm/mterp/x86/OP_DIV_FLOAT_2ADDR.S
new file mode 100644
index 0000000..2ed12b6
--- /dev/null
+++ b/vm/mterp/x86/OP_DIV_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop2addr.S" {"instr":"fdivs","load":"flds","store":"fstps"}
diff --git a/vm/mterp/x86/OP_DIV_INT.S b/vm/mterp/x86/OP_DIV_INT.S
new file mode 100644
index 0000000..8afdcb0
--- /dev/null
+++ b/vm/mterp/x86/OP_DIV_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bindiv.S" {"result":"%eax","special":"$0x80000000"}
diff --git a/vm/mterp/x86/OP_DIV_INT_2ADDR.S b/vm/mterp/x86/OP_DIV_INT_2ADDR.S
new file mode 100644
index 0000000..a1ae0f9
--- /dev/null
+++ b/vm/mterp/x86/OP_DIV_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bindiv2addr.S" {"result":"%eax","special":"$0x80000000"}
diff --git a/vm/mterp/x86/OP_DIV_INT_LIT16.S b/vm/mterp/x86/OP_DIV_INT_LIT16.S
new file mode 100644
index 0000000..4817734
--- /dev/null
+++ b/vm/mterp/x86/OP_DIV_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bindivLit16.S" {"result":"%eax","special":"$0x80000000"}
diff --git a/vm/mterp/x86/OP_DIV_INT_LIT8.S b/vm/mterp/x86/OP_DIV_INT_LIT8.S
new file mode 100644
index 0000000..f59838c
--- /dev/null
+++ b/vm/mterp/x86/OP_DIV_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bindivLit8.S" {"result":"%eax","special":"$0x80000000"}
diff --git a/vm/mterp/x86/OP_DIV_LONG.S b/vm/mterp/x86/OP_DIV_LONG.S
new file mode 100644
index 0000000..f324be3
--- /dev/null
+++ b/vm/mterp/x86/OP_DIV_LONG.S
@@ -0,0 +1,52 @@
+%verify "executed"
+%default {"routine":"__divdi3","special":"$0x80000000"}
+ /* div vAA, vBB, vCC */
+ movzbl 3(rPC),%eax # eax<- CC
+ movzbl 2(rPC),%ecx # ecx<- BB
+ SPILL(rPC)
+ GET_VREG_WORD(rPC,%eax,0)
+ GET_VREG_WORD(%eax,%eax,1)
+ movl rPC,OUT_ARG2(%esp)
+ testl %eax,%eax
+ je .L${opcode}_check_zero
+ cmpl $$-1,%eax
+ je .L${opcode}_check_neg1
+.L${opcode}_notSpecial:
+ GET_VREG_WORD(rPC,%ecx,0)
+ GET_VREG_WORD(%ecx,%ecx,1)
+.L${opcode}_notSpecial1:
+ movl %eax,OUT_ARG3(%esp)
+ movl rPC,OUT_ARG0(%esp)
+ movl %ecx,OUT_ARG1(%esp)
+ jmp .L${opcode}_continue
+%break
+
+.L${opcode}_continue:
+ call $routine
+.L${opcode}_finish:
+ movzbl rINST_HI,%ecx
+ SET_VREG_WORD(rPC,%ecx,1)
+ UNSPILL(rPC)
+ SET_VREG_WORD(%eax,%ecx,0)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+.L${opcode}_check_zero:
+ testl rPC,rPC
+ jne .L${opcode}_notSpecial
+ UNSPILL(rPC)
+ jmp common_errDivideByZero
+.L${opcode}_check_neg1:
+ testl rPC,%eax
+ jne .L${opcode}_notSpecial
+ GET_VREG_WORD(rPC,%ecx,0)
+ GET_VREG_WORD(%ecx,%ecx,1)
+ testl rPC,rPC
+ jne .L${opcode}_notSpecial1
+ cmpl $$0x80000000,%ecx
+ jne .L${opcode}_notSpecial1
+ /* minint / -1, return minint on div, 0 on rem */
+ xorl %eax,%eax
+ movl $special,%edx
+ jmp .L${opcode}_finish
diff --git a/vm/mterp/x86/OP_DIV_LONG_2ADDR.S b/vm/mterp/x86/OP_DIV_LONG_2ADDR.S
new file mode 100644
index 0000000..128ede0
--- /dev/null
+++ b/vm/mterp/x86/OP_DIV_LONG_2ADDR.S
@@ -0,0 +1,54 @@
+%verify "executed"
+%default {"routine":"__divdi3","special":"$0x80000000"}
+ /* div/2addr vA, vB */
+ movzbl rINST_HI,%eax
+ shrl $$4,%eax # eax<- B
+ movzbl rINST_HI,rINST_FULL
+ andb $$0xf,rINST_LO # rINST_FULL<- A
+ SPILL(rPC)
+ GET_VREG_WORD(rPC,%eax,0)
+ GET_VREG_WORD(%eax,%eax,1)
+ movl rPC,OUT_ARG2(%esp)
+ testl %eax,%eax
+ je .L${opcode}_check_zero
+ cmpl $$-1,%eax
+ je .L${opcode}_check_neg1
+.L${opcode}_notSpecial:
+ GET_VREG_WORD(rPC,rINST_FULL,0)
+ GET_VREG_WORD(%ecx,rINST_FULL,1)
+.L${opcode}_notSpecial1:
+ jmp .L${opcode}_continue
+%break
+
+.L${opcode}_continue:
+ movl %eax,OUT_ARG3(%esp)
+ movl rPC,OUT_ARG0(%esp)
+ movl %ecx,OUT_ARG1(%esp)
+ call $routine
+.L${opcode}_finish:
+ movl rINST_FULL,%ecx
+ SET_VREG_WORD(rPC,%ecx,1)
+ UNSPILL(rPC)
+ SET_VREG_WORD(%eax,%ecx,0)
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ GOTO_NEXT
+
+.L${opcode}_check_zero:
+ testl rPC,rPC
+ jne .L${opcode}_notSpecial
+ UNSPILL(rPC)
+ jmp common_errDivideByZero
+.L${opcode}_check_neg1:
+ testl rPC,%eax
+ jne .L${opcode}_notSpecial
+ GET_VREG_WORD(rPC,rINST_FULL,0)
+ GET_VREG_WORD(%ecx,rINST_FULL,1)
+ testl rPC,rPC
+ jne .L${opcode}_notSpecial1
+ cmpl $$0x80000000,%ecx
+ jne .L${opcode}_notSpecial1
+ /* minint / -1, return minint on div, 0 on rem */
+ xorl %eax,%eax
+ movl $special,%edx
+ jmp .L${opcode}_finish
diff --git a/vm/mterp/x86/OP_DOUBLE_TO_FLOAT.S b/vm/mterp/x86/OP_DOUBLE_TO_FLOAT.S
new file mode 100644
index 0000000..12e73a2
--- /dev/null
+++ b/vm/mterp/x86/OP_DOUBLE_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/fpcvt.S" {"load":"fldl","store":"fstps"}
diff --git a/vm/mterp/x86/OP_DOUBLE_TO_INT.S b/vm/mterp/x86/OP_DOUBLE_TO_INT.S
new file mode 100644
index 0000000..7cd993a
--- /dev/null
+++ b/vm/mterp/x86/OP_DOUBLE_TO_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/cvtfp_int.S" {"srcdouble":"1","tgtlong":"0"}
diff --git a/vm/mterp/x86/OP_DOUBLE_TO_LONG.S b/vm/mterp/x86/OP_DOUBLE_TO_LONG.S
new file mode 100644
index 0000000..b80b020
--- /dev/null
+++ b/vm/mterp/x86/OP_DOUBLE_TO_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/cvtfp_int.S" {"srcdouble":"1","tgtlong":"1"}
diff --git a/vm/mterp/x86/OP_EXECUTE_INLINE.S b/vm/mterp/x86/OP_EXECUTE_INLINE.S
new file mode 100644
index 0000000..1eca6e2
--- /dev/null
+++ b/vm/mterp/x86/OP_EXECUTE_INLINE.S
@@ -0,0 +1,66 @@
+%verify "executed"
+%verify "exception handled"
+ /*
+ * Execute a "native inline" instruction.
+ *
+ * We will be calling through a function table:
+ *
+ * (*gDvmInlineOpsTable[opIndex].func)(arg0, arg1, arg2, arg3, pResult)
+ *
+ */
+ /* [opt] execute-inline vAA, {vC, vD, vE, vF}, inline@BBBB */
+ GET_GLUE(%ecx)
+ EXPORT_PC()
+ movzwl 2(rPC),%eax # eax<- BBBB
+ leal offGlue_retval(%ecx),%ecx # ecx<- & glue->retval
+ movl %ecx,OUT_ARG4(%esp)
+ sarl $$12,rINST_FULL # rINST_FULL<- arg count (0-4)
+ SPILL(rPC)
+ call .L${opcode}_continue # make call; will return after
+ UNSPILL(rPC)
+ testl %eax,%eax # successful?
+ FETCH_INST_WORD(3)
+ je common_exceptionThrown # no, handle exception
+ ADVANCE_PC(3)
+ GOTO_NEXT
+%break
+
+.L${opcode}_continue:
+ /*
+ * Extract args, call function.
+ * ecx = #of args (0-4)
+ * eax = call index
+ * @esp = return addr
+ * esp is -4 from normal
+ *
+ * Go ahead and load all 4 args, even if not used.
+ */
+ movzwl 4(rPC),rPC
+
+ movl $$0xf,%ecx
+ andl rPC,%ecx
+ GET_VREG(%ecx,%ecx)
+ sarl $$4,rPC
+ movl %ecx,4+OUT_ARG0(%esp)
+
+ movl $$0xf,%ecx
+ andl rPC,%ecx
+ GET_VREG(%ecx,%ecx)
+ sarl $$4,rPC
+ movl %ecx,4+OUT_ARG1(%esp)
+
+ movl $$0xf,%ecx
+ andl rPC,%ecx
+ GET_VREG(%ecx,%ecx)
+ sarl $$4,rPC
+ movl %ecx,4+OUT_ARG2(%esp)
+
+ movl $$0xf,%ecx
+ andl rPC,%ecx
+ GET_VREG(%ecx,%ecx)
+ sarl $$4,rPC
+ movl %ecx,4+OUT_ARG3(%esp)
+
+ sall $$4,%eax # index *= sizeof(table entry)
+ jmp *gDvmInlineOpsTable(%eax)
+ # will return to caller of .L${opcode}_continue
diff --git a/vm/mterp/x86/OP_EXECUTE_INLINE_RANGE.S b/vm/mterp/x86/OP_EXECUTE_INLINE_RANGE.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_EXECUTE_INLINE_RANGE.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_FILLED_NEW_ARRAY.S b/vm/mterp/x86/OP_FILLED_NEW_ARRAY.S
new file mode 100644
index 0000000..55b1f84
--- /dev/null
+++ b/vm/mterp/x86/OP_FILLED_NEW_ARRAY.S
@@ -0,0 +1,142 @@
+%default { "isrange":"0" }
+%verify "executed"
+%verify "unimplemented array type"
+ /*
+ * Create a new array with elements filled from registers.
+ *
+ * for: filled-new-array, filled-new-array/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */
+ GET_GLUE(%eax)
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA or BA
+ movl offGlue_methodClassDex(%eax),%eax # eax<- pDvmDex
+ movzwl 2(rPC),%ecx # ecx<- BBBB
+ movl offDvmDex_pResClasses(%eax),%eax # eax<- pDvmDex->pResClasses
+ SPILL(rPC)
+ movl (%eax,%ecx,4),%eax # eax<- resolved class
+ EXPORT_PC()
+ testl %eax,%eax # already resolved?
+ jne .L${opcode}_continue # yes, continue
+ # less frequent path, so we'll redo some work
+ GET_GLUE(%eax)
+ movl $$0,OUT_ARG2(%esp) # arg2<- false
+ movl %ecx,OUT_ARG1(%esp) # arg1<- BBBB
+ movl offGlue_method(%eax),%eax # eax<- glue->method
+ jmp .L${opcode}_more
+%break
+
+.L${opcode}_more:
+ movl offMethod_clazz(%eax),%eax # eax<- method->clazz
+ movl %eax,OUT_ARG0(%esp) # arg0<- clazz
+ call dvmResolveClass # eax<- call(clazz,ref,flag)
+ UNSPILL(rPC)
+ testl %eax,%eax # null?
+ je common_exceptionThrown # yes, handle it
+
+ # note: fall through to .L${opcode}_continue
+
+ /*
+ * On entry:
+ * eax holds array class [r0]
+ * rINST_FULL holds AA or BB [r10]
+ * ecx is scratch
+ * rPC is valid, but has been spilled
+ */
+.L${opcode}_continue:
+ movl offClassObject_descriptor(%eax),%ecx # ecx<- arrayClass->descriptor
+ movl $$ALLOC_DONT_TRACK,OUT_ARG2(%esp) # arg2<- flags
+ movzbl 1(%ecx),%ecx # ecx<- descriptor[1]
+ movl %eax,OUT_ARG0(%esp) # arg0<- arrayClass
+ GET_GLUE(%eax)
+ cmpb $$'I',%cl # supported?
+ je 1f
+ cmpb $$'L',%cl
+ je 1f
+ cmpb $$'[',%cl
+ jne .L${opcode}_notimpl # no, not handled yet
+1:
+ movl %ecx,offGlue_retval+4(%eax) # save type
+ .if (!$isrange)
+ SPILL_TMP(rINST_FULL) # save copy, need "B" later
+ sarl $$4,rINST_FULL
+ .endif
+ movl rINST_FULL,OUT_ARG1(%esp) # arg1<- A or AA (length)
+ call dvmAllocArrayByClass # eax<- call(arrayClass, length, flags)
+ UNSPILL(rPC)
+ GET_GLUE(%ecx)
+ testl %eax,%eax # alloc successful?
+ je common_exceptionThrown # no, handle exception
+ movl %eax,offGlue_retval(%ecx) # retval.l<- new array
+ movzwl 4(rPC),%ecx # ecx<- FEDC or CCCC
+ leal offArrayObject_contents(%eax),%eax # eax<- newArray->contents
+
+/* at this point:
+ * eax is pointer to tgt
+ * rINST_FULL is length
+ * ecx is FEDC or CCCC
+ * TMP_SPILL is BA
+ * rPC is valid, but spilled
+ * We now need to copy values from registers into the array
+ */
+
+ .if $isrange
+ # set up src pointer
+ SPILL(rFP) # esi
+ SPILL(rIBASE) # edi
+ movl %eax,%edi # set up dst ptr
+ leal (rFP,%ecx,4),%esi # set up src ptr
+ movl rINST_FULL,%ecx # load count register
+ FETCH_INST_WORD(3)
+ rep
+ movsd
+ GET_GLUE(%ecx)
+ UNSPILL(rIBASE)
+ movl offGlue_retval+4(%ecx),%eax # eax<- type
+ UNSPILL(rFP)
+ .else
+ testl rINST_FULL,rINST_FULL
+ je 4f
+ UNSPILL_TMP(rPC)
+ andl $$0x0f,rPC # rPC<- 0000000A
+ sall $$16,rPC # rPC<- 000A0000
+ orl %ecx,rPC # rpc<- 000AFEDC
+3:
+ movl $$0xf,%ecx
+ andl rPC,%ecx # ecx<- next reg to load
+ GET_VREG(%ecx,%ecx)
+ shrl $$4,rPC
+ leal 4(%eax),%eax
+ movl %ecx,-4(%eax)
+ sub $$1,rINST_FULL
+ jne 3b
+4:
+ GET_GLUE(%ecx)
+ UNSPILL(rPC)
+ movl offGlue_retval+4(%ecx),%eax # eax<- type
+ FETCH_INST_WORD(3)
+ .endif
+
+ cmpb $$'I',%al # Int array?
+ je 5f # skip card mark if so
+ movl offGlue_retval(%ecx),%eax # eax<- object head
+ movl offGlue_cardTable(%ecx),%ecx # card table base
+ shrl $$GC_CARD_SHIFT,%eax # convert to card num
+ movb %cl,(%ecx,%eax) # mark card based on object head
+5:
+ ADVANCE_PC(3)
+ GOTO_NEXT
+
+
+ /*
+ * Throw an exception indicating that we have not implemented this
+ * mode of filled-new-array.
+ */
+.L${opcode}_notimpl:
+ movl $$.LstrInternalError,%eax
+ movl %eax,OUT_ARG0(%esp)
+ movl $$.LstrFilledNewArrayNotImpl,%eax
+ movl %eax,OUT_ARG1(%esp)
+ call dvmThrowException
+ UNSPILL(rPC)
+ jmp common_exceptionThrown
diff --git a/vm/mterp/x86/OP_FILLED_NEW_ARRAY_RANGE.S b/vm/mterp/x86/OP_FILLED_NEW_ARRAY_RANGE.S
new file mode 100644
index 0000000..dc6cc4d
--- /dev/null
+++ b/vm/mterp/x86/OP_FILLED_NEW_ARRAY_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_FILLED_NEW_ARRAY.S" { "isrange":"1" }
diff --git a/vm/mterp/x86/OP_FILL_ARRAY_DATA.S b/vm/mterp/x86/OP_FILL_ARRAY_DATA.S
new file mode 100644
index 0000000..9db60ac
--- /dev/null
+++ b/vm/mterp/x86/OP_FILL_ARRAY_DATA.S
@@ -0,0 +1,17 @@
+%verify "executed"
+ /* fill-array-data vAA, +BBBBBBBB */
+ movl 2(rPC),%ecx # ecx<- BBBBbbbb
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ leal (rPC,%ecx,2),%ecx # ecx<- PC + BBBBbbbb*2
+ GET_VREG(%eax,rINST_FULL)
+ SPILL(rPC)
+ EXPORT_PC()
+ movl %eax,OUT_ARG0(%esp)
+ movl %ecx,OUT_ARG1(%esp)
+ call dvmInterpHandleFillArrayData
+ UNSPILL(rPC)
+ FETCH_INST_WORD(3)
+ testl %eax,%eax # exception thrown?
+ je common_exceptionThrown
+ ADVANCE_PC(3)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_FLOAT_TO_DOUBLE.S b/vm/mterp/x86/OP_FLOAT_TO_DOUBLE.S
new file mode 100644
index 0000000..11b2c23
--- /dev/null
+++ b/vm/mterp/x86/OP_FLOAT_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/fpcvt.S" {"load":"flds","store":"fstpl"}
diff --git a/vm/mterp/x86/OP_FLOAT_TO_INT.S b/vm/mterp/x86/OP_FLOAT_TO_INT.S
new file mode 100644
index 0000000..8d55286
--- /dev/null
+++ b/vm/mterp/x86/OP_FLOAT_TO_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/cvtfp_int.S" {"srcdouble":"0","tgtlong":"0"}
diff --git a/vm/mterp/x86/OP_FLOAT_TO_LONG.S b/vm/mterp/x86/OP_FLOAT_TO_LONG.S
new file mode 100644
index 0000000..2493b13
--- /dev/null
+++ b/vm/mterp/x86/OP_FLOAT_TO_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/cvtfp_int.S" {"srcdouble":"0","tgtlong":"1"}
diff --git a/vm/mterp/x86/OP_GOTO.S b/vm/mterp/x86/OP_GOTO.S
new file mode 100644
index 0000000..5d530ec
--- /dev/null
+++ b/vm/mterp/x86/OP_GOTO.S
@@ -0,0 +1,16 @@
+%verify "executed"
+%verify "forward and backward"
+ /*
+ * Unconditional branch, 8-bit offset.
+ *
+ * The branch distance is a signed code-unit offset, which we need to
+ * double to get a byte offset.
+ */
+ /* goto +AA */
+ movsbl rINST_HI,rINST_FULL # ebx<- ssssssAA
+ testl rINST_FULL,rINST_FULL # test for <0
+ js common_backwardBranch
+ movl rINST_FULL,%eax
+ FETCH_INST_INDEXED(%eax)
+ ADVANCE_PC_INDEXED(%eax)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_GOTO_16.S b/vm/mterp/x86/OP_GOTO_16.S
new file mode 100644
index 0000000..3feb33b
--- /dev/null
+++ b/vm/mterp/x86/OP_GOTO_16.S
@@ -0,0 +1,15 @@
+%verify "executed"
+%verify "forward and backward"
+ /*
+ * Unconditional branch, 16-bit offset.
+ *
+ * The branch distance is a signed code-unit offset
+ */
+ /* goto/16 +AAAA */
+ movswl 2(rPC),rINST_FULL # rINST_FULL<- ssssAAAA
+ testl rINST_FULL,rINST_FULL # test for <0
+ js common_backwardBranch
+ movl rINST_FULL,%eax
+ FETCH_INST_INDEXED(%eax)
+ ADVANCE_PC_INDEXED(%eax)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_GOTO_32.S b/vm/mterp/x86/OP_GOTO_32.S
new file mode 100644
index 0000000..6720bd6
--- /dev/null
+++ b/vm/mterp/x86/OP_GOTO_32.S
@@ -0,0 +1,18 @@
+%verify "executed"
+%verify "forward, backward, self"
+ /*
+ * Unconditional branch, 32-bit offset.
+ *
+ * The branch distance is a signed code-unit offset.
+ *
+ * Unlike most opcodes, this one is allowed to branch to itself, so
+ * our "backward branch" test must be "<=0" instead of "<0".
+ */
+ /* goto/32 AAAAAAAA */
+ movl 2(rPC),rINST_FULL # rINST_FULL<- AAAAAAAA
+ cmpl $$0,rINST_FULL # test for <= 0
+ jle common_backwardBranch
+ movl rINST_FULL,%eax
+ FETCH_INST_INDEXED(%eax)
+ ADVANCE_PC_INDEXED(%eax)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_IF_EQ.S b/vm/mterp/x86/OP_IF_EQ.S
new file mode 100644
index 0000000..a8a38b6
--- /dev/null
+++ b/vm/mterp/x86/OP_IF_EQ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bincmp.S" { "revcmp":"ne" }
diff --git a/vm/mterp/x86/OP_IF_EQZ.S b/vm/mterp/x86/OP_IF_EQZ.S
new file mode 100644
index 0000000..67fd5d7
--- /dev/null
+++ b/vm/mterp/x86/OP_IF_EQZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/zcmp.S" { "revcmp":"ne" }
diff --git a/vm/mterp/x86/OP_IF_GE.S b/vm/mterp/x86/OP_IF_GE.S
new file mode 100644
index 0000000..6930328
--- /dev/null
+++ b/vm/mterp/x86/OP_IF_GE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bincmp.S" { "revcmp":"l" }
diff --git a/vm/mterp/x86/OP_IF_GEZ.S b/vm/mterp/x86/OP_IF_GEZ.S
new file mode 100644
index 0000000..71e9127
--- /dev/null
+++ b/vm/mterp/x86/OP_IF_GEZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/zcmp.S" { "revcmp":"l" }
diff --git a/vm/mterp/x86/OP_IF_GT.S b/vm/mterp/x86/OP_IF_GT.S
new file mode 100644
index 0000000..b987443
--- /dev/null
+++ b/vm/mterp/x86/OP_IF_GT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bincmp.S" { "revcmp":"le" }
diff --git a/vm/mterp/x86/OP_IF_GTZ.S b/vm/mterp/x86/OP_IF_GTZ.S
new file mode 100644
index 0000000..9d7b697
--- /dev/null
+++ b/vm/mterp/x86/OP_IF_GTZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/zcmp.S" { "revcmp":"le" }
diff --git a/vm/mterp/x86/OP_IF_LE.S b/vm/mterp/x86/OP_IF_LE.S
new file mode 100644
index 0000000..b9ac008
--- /dev/null
+++ b/vm/mterp/x86/OP_IF_LE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bincmp.S" { "revcmp":"g" }
diff --git a/vm/mterp/x86/OP_IF_LEZ.S b/vm/mterp/x86/OP_IF_LEZ.S
new file mode 100644
index 0000000..26afc85
--- /dev/null
+++ b/vm/mterp/x86/OP_IF_LEZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/zcmp.S" { "revcmp":"g" }
diff --git a/vm/mterp/x86/OP_IF_LT.S b/vm/mterp/x86/OP_IF_LT.S
new file mode 100644
index 0000000..79aba48
--- /dev/null
+++ b/vm/mterp/x86/OP_IF_LT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bincmp.S" { "revcmp":"ge" }
diff --git a/vm/mterp/x86/OP_IF_LTZ.S b/vm/mterp/x86/OP_IF_LTZ.S
new file mode 100644
index 0000000..01f4daa
--- /dev/null
+++ b/vm/mterp/x86/OP_IF_LTZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/zcmp.S" { "revcmp":"ge" }
diff --git a/vm/mterp/x86/OP_IF_NE.S b/vm/mterp/x86/OP_IF_NE.S
new file mode 100644
index 0000000..552b74c
--- /dev/null
+++ b/vm/mterp/x86/OP_IF_NE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bincmp.S" { "revcmp":"e" }
diff --git a/vm/mterp/x86/OP_IF_NEZ.S b/vm/mterp/x86/OP_IF_NEZ.S
new file mode 100644
index 0000000..b2fdb14
--- /dev/null
+++ b/vm/mterp/x86/OP_IF_NEZ.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/zcmp.S" { "revcmp":"e" }
diff --git a/vm/mterp/x86/OP_IGET.S b/vm/mterp/x86/OP_IGET.S
new file mode 100644
index 0000000..cd8033f
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET.S
@@ -0,0 +1,64 @@
+%default { "load":"movl", "sqnum":"0" }
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+ /*
+ * General 32-bit instance field get.
+ *
+ * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short
+ */
+ /* op vA, vB, field@CCCC */
+ GET_GLUE(%ecx)
+ SPILL(rIBASE) # need another reg
+ movzwl 2(rPC),rIBASE # rIBASE<- 0000CCCC
+ movl offGlue_methodClassDex(%ecx),%eax # eax<- DvmDex
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $$4,%ecx # ecx<- B
+ movl offDvmDex_pResFields(%eax),%eax # eax<- pDvmDex->pResFields
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $$0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG(%ecx,%ecx) # ecx<- fp[B], the object ptr
+ movl (%eax,rIBASE,4),%eax # resolved entry
+ testl %eax,%eax # is resolved entry null?
+ jne .L${opcode}_finish # no, already resolved
+ movl rIBASE,OUT_ARG1(%esp) # needed by dvmResolveInstField
+ GET_GLUE(rIBASE)
+ jmp .L${opcode}_resolve
+%break
+
+
+.L${opcode}_resolve:
+ EXPORT_PC()
+ SPILL(rPC)
+ movl offGlue_method(rIBASE),rPC # rPC<- current method
+ UNSPILL(rIBASE)
+ movl offMethod_clazz(rPC),rPC # rPC<- method->clazz
+ SPILL_TMP(%ecx) # save object pointer across call
+ movl rPC,OUT_ARG0(%esp) # pass in method->clazz
+ call dvmResolveInstField # ... to dvmResolveInstField
+ UNSPILL_TMP(%ecx)
+ UNSPILL(rPC)
+ testl %eax,%eax # ... which returns InstrField ptr
+ jne .L${opcode}_finish
+ jmp common_exceptionThrown
+
+.L${opcode}_finish:
+ /*
+ * Currently:
+ * eax holds resolved field
+ * ecx holds object
+ * rIBASE is scratch, but needs to be unspilled
+ * rINST_FULL holds A
+ */
+ movl offInstField_byteOffset(%eax),%eax # eax<- byte offset of field
+ UNSPILL(rIBASE)
+ testl %ecx,%ecx # object null?
+ je common_errNullObject # object was null
+ $load (%ecx,%eax,1),%ecx # ecx<- obj.field (8/16/32 bits)
+ movl rINST_FULL,%eax # eax<- A
+ FETCH_INST_WORD(2)
+ SET_VREG(%ecx,%eax)
+ ADVANCE_PC(2)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_IGET_BOOLEAN.S b/vm/mterp/x86/OP_IGET_BOOLEAN.S
new file mode 100644
index 0000000..0829e2c
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_IGET.S" { "load":"movzbl", "sqnum":"1" }
diff --git a/vm/mterp/x86/OP_IGET_BYTE.S b/vm/mterp/x86/OP_IGET_BYTE.S
new file mode 100644
index 0000000..2350f8c
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET_BYTE.S
@@ -0,0 +1,3 @@
+%verify "executed"
+%verify "negative value is sign-extended"
+%include "x86/OP_IGET.S" { "load":"movsbl", "sqnum":"2" }
diff --git a/vm/mterp/x86/OP_IGET_CHAR.S b/vm/mterp/x86/OP_IGET_CHAR.S
new file mode 100644
index 0000000..4218a8c
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET_CHAR.S
@@ -0,0 +1,3 @@
+%verify "executed"
+%verify "large values are not sign-extended"
+%include "x86/OP_IGET.S" { "load":"movzwl", "sqnum":"3" }
diff --git a/vm/mterp/x86/OP_IGET_OBJECT.S b/vm/mterp/x86/OP_IGET_OBJECT.S
new file mode 100644
index 0000000..84c0a98
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_IGET.S"
diff --git a/vm/mterp/x86/OP_IGET_OBJECT_QUICK.S b/vm/mterp/x86/OP_IGET_OBJECT_QUICK.S
new file mode 100644
index 0000000..89ebd36
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET_OBJECT_QUICK.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_IGET_QUICK.S"
diff --git a/vm/mterp/x86/OP_IGET_OBJECT_VOLATILE.S b/vm/mterp/x86/OP_IGET_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..4576d39
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET_OBJECT_VOLATILE.S
@@ -0,0 +1,3 @@
+%verify "executed"
+%verify "negative value is sign-extended"
+%include "x86/OP_IGET.S"
diff --git a/vm/mterp/x86/OP_IGET_QUICK.S b/vm/mterp/x86/OP_IGET_QUICK.S
new file mode 100644
index 0000000..88d0725
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET_QUICK.S
@@ -0,0 +1,17 @@
+%verify "executed"
+%verify "null object"
+ /* For: iget-quick, iget-object-quick */
+ /* op vA, vB, offset@CCCC */
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $$4,%ecx # ecx<- B
+ GET_VREG(%ecx,%ecx) # vB (object we're operating on)
+ movzwl 2(rPC),%eax # eax<- field byte offset
+ cmpl $$0,%ecx # is object null?
+ je common_errNullObject
+ movl (%ecx,%eax,1),%eax
+ movzbl rINST_HI,%ecx
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ andb $$0xf,%cl # rINST_FULL<- A
+ SET_VREG (%eax,%ecx) # fp[A]<- result
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_IGET_SHORT.S b/vm/mterp/x86/OP_IGET_SHORT.S
new file mode 100644
index 0000000..71f0d34
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET_SHORT.S
@@ -0,0 +1,3 @@
+%verify "executed"
+%verify "negative value is sign-extended"
+%include "x86/OP_IGET.S" { "load":"movswl", "sqnum":"4" }
diff --git a/vm/mterp/x86/OP_IGET_VOLATILE.S b/vm/mterp/x86/OP_IGET_VOLATILE.S
new file mode 100644
index 0000000..4576d39
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET_VOLATILE.S
@@ -0,0 +1,3 @@
+%verify "executed"
+%verify "negative value is sign-extended"
+%include "x86/OP_IGET.S"
diff --git a/vm/mterp/x86/OP_IGET_WIDE.S b/vm/mterp/x86/OP_IGET_WIDE.S
new file mode 100644
index 0000000..77a5994
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET_WIDE.S
@@ -0,0 +1,64 @@
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+ /*
+ * 64-bit instance field get.
+ *
+ */
+ /* op vA, vB, field@CCCC */
+ GET_GLUE(%ecx)
+ SPILL(rIBASE) # need another reg
+ movzwl 2(rPC),rIBASE # rIBASE<- 0000CCCC
+ movl offGlue_methodClassDex(%ecx),%eax # eax<- DvmDex
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $$4,%ecx # ecx<- B
+ movl offDvmDex_pResFields(%eax),%eax # eax<- pDvmDex->pResFields
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $$0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG(%ecx,%ecx) # ecx<- fp[B], the object ptr
+ movl (%eax,rIBASE,4),%eax # resolved entry
+ testl %eax,%eax # is resolved entry null?
+ jne .L${opcode}_finish # no, already resolved
+ movl rIBASE,OUT_ARG1(%esp) # needed by dvmResolveInstField
+ GET_GLUE(rIBASE)
+ jmp .L${opcode}_resolve
+%break
+
+
+.L${opcode}_resolve:
+ EXPORT_PC()
+ SPILL(rPC)
+ movl offGlue_method(rIBASE),rPC # rPC<- current method
+ UNSPILL(rIBASE)
+ movl offMethod_clazz(rPC),rPC # rPC<- method->clazz
+ SPILL_TMP(%ecx) # save object pointer across call
+ movl rPC,OUT_ARG0(%esp) # pass in method->clazz
+ call dvmResolveInstField # ... to dvmResolveInstField
+ UNSPILL_TMP(%ecx)
+ UNSPILL(rPC)
+ testl %eax,%eax # ... which returns InstrField ptr
+ jne .L${opcode}_finish
+ jmp common_exceptionThrown
+
+.L${opcode}_finish:
+ /*
+ * Currently:
+ * eax holds resolved field
+ * ecx holds object
+ * rIBASE is scratch, but needs to be unspilled
+ * rINST_FULL holds A
+ */
+ movl offInstField_byteOffset(%eax),%eax # eax<- byte offset of field
+ UNSPILL(rIBASE)
+ testl %ecx,%ecx # object null?
+ je common_errNullObject # object was null
+ leal (%ecx,%eax,1),%eax # eax<- address of field
+ movl (%eax),%ecx # ecx<- lsw
+ movl 4(%eax),%eax # eax<- msw
+ SET_VREG_WORD(%ecx,rINST_FULL,0)
+ SET_VREG_WORD(%eax,rINST_FULL,1)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_IGET_WIDE_QUICK.S b/vm/mterp/x86/OP_IGET_WIDE_QUICK.S
new file mode 100644
index 0000000..4747284
--- /dev/null
+++ b/vm/mterp/x86/OP_IGET_WIDE_QUICK.S
@@ -0,0 +1,20 @@
+%verify "executed"
+%verify "null object"
+ /* For: iget-wide-quick */
+ /* op vA, vB, offset@CCCC */
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $$4,%ecx # ecx<- B
+ GET_VREG(%ecx,%ecx) # vB (object we're operating on)
+ movzwl 2(rPC),%eax # eax<- field byte offset
+ cmpl $$0,%ecx # is object null?
+ je common_errNullObject
+ leal (%ecx,%eax,1),%eax # eax<- address of 64-bit source
+ movl (%eax),%ecx # ecx<- lsw
+ movl 4(%eax),%eax # eax<- msw
+ movzbl rINST_HI,rINST_FULL
+ andb $$0xf,rINST_LO # rINST_FULL<- A
+ SET_VREG_WORD(%ecx,rINST_FULL,0) # v[A+0]<- lsw
+ SET_VREG_WORD(%eax,rINST_FULL,1) # v[A+1]<- msw
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_INSTANCE_OF.S b/vm/mterp/x86/OP_INSTANCE_OF.S
new file mode 100644
index 0000000..71b92d3
--- /dev/null
+++ b/vm/mterp/x86/OP_INSTANCE_OF.S
@@ -0,0 +1,102 @@
+%verify "executed"
+%verify "null object"
+%verify "class cast exception thrown, with correct class name"
+%verify "class cast exception not thrown on same class"
+%verify "class cast exception not thrown on subclass"
+%verify "class not resolved"
+%verify "class already resolved"
+ /*
+ * Check to see if an object reference is an instance of a class.
+ *
+ * Most common situation is a non-null object, being compared against
+ * an already-resolved class.
+ */
+ /* instance-of vA, vB, class@CCCC */
+ movzbl rINST_HI,%eax # eax<- BA
+ sarl $$4,%eax # eax<- B
+ GET_VREG(%eax,%eax) # eax<- vB (obj)
+ GET_GLUE(%ecx)
+ testl %eax,%eax # object null?
+ movl offGlue_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+ SPILL(rPC)
+ je .L${opcode}_store # null obj, not instance, store it
+ movzwl 2(rPC),rPC # rPC<- CCCC
+ movl offDvmDex_pResClasses(%ecx),%ecx # ecx<- pDvmDex->pResClasses
+ movl (%ecx,rPC,4),%ecx # ecx<- resolved class
+ movl offObject_clazz(%eax),%eax # eax<- obj->clazz
+ testl %ecx,%ecx # have we resolved this before?
+ je .L${opcode}_resolve # not resolved, do it now
+.L${opcode}_resolved: # eax<- obj->clazz, ecx<- resolved class
+ cmpl %eax,%ecx # same class (trivial success)?
+ je .L${opcode}_trivial # yes, trivial finish
+ jmp .L${opcode}_fullcheck # no, do full check
+%break
+
+ /*
+ * Trivial test failed, need to perform full check. This is common.
+ * eax holds obj->clazz
+ * ecx holds class resolved from BBBB
+ * rINST_HI has BA
+ * rPC already spilled
+ */
+.L${opcode}_fullcheck:
+ movl %eax,OUT_ARG0(%esp)
+ movl %ecx,OUT_ARG1(%esp)
+ call dvmInstanceofNonTrivial # eax<- boolean result
+ # fall through to ${opcode}_store
+
+ /*
+ * eax holds boolean result
+ * rINST_HI holds BA
+ */
+.L${opcode}_store:
+ UNSPILL(rPC)
+ movzbl rINST_HI,%ecx # ecx<- BA
+ FETCH_INST_WORD(2)
+ andb $$0xf,%cl # ecl<- A
+ ADVANCE_PC(2)
+ SET_VREG(%eax,%ecx) # vA<- eax
+ GOTO_NEXT
+
+ /*
+ * Trivial test succeeded, save and bail.
+ * r9 holds A
+ */
+.L${opcode}_trivial:
+ UNSPILL(rPC)
+ movzbl rINST_HI,%ecx # ecx<- BA
+ FETCH_INST_WORD(2)
+ andb $$0xf,%cl # ecl<- A
+ ADVANCE_PC(2)
+ movl $$1,%eax
+ SET_VREG(%eax,%ecx) # vA<- true
+ GOTO_NEXT
+
+ /*
+ * Resolution required. This is the least-likely path.
+ *
+ * rPC holds BBBB
+ * rINST_HI holds BA
+ */
+.L${opcode}_resolve:
+ movl rPC,OUT_ARG1(%esp) # arg1<- BBBB
+ GET_GLUE(%ecx)
+ UNSPILL(rPC)
+ movl offGlue_method(%ecx),%ecx
+ movl $$1,OUT_ARG2(%esp) # arg2<- true
+ movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+ EXPORT_PC()
+ movl %ecx,OUT_ARG0(%esp) # arg0<- method->clazz
+ call dvmResolveClass # eax<- resolved ClassObject ptr
+ UNSPILL(rPC)
+ testl %eax,%eax # success?
+ je common_exceptionThrown # no, handle exception
+/* Now, we need to sync up with fast path. We need eax to
+ * hold the obj->clazz, and ecx to hold the resolved class
+ */
+ movl %eax,%ecx # ecx<- resolved class
+ movzbl rINST_HI,%eax # eax<- BA
+ sarl $$4,%eax # eax<- B
+ GET_VREG(%eax,%eax) # eax<- vB (obj)
+ movl offObject_clazz(%eax),%eax # eax<- obj->clazz
+ jmp .L${opcode}_resolved
diff --git a/vm/mterp/x86/OP_INT_TO_BYTE.S b/vm/mterp/x86/OP_INT_TO_BYTE.S
new file mode 100644
index 0000000..44d032b
--- /dev/null
+++ b/vm/mterp/x86/OP_INT_TO_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/unop.S" {"instr":"movsbl %al,%eax"}
diff --git a/vm/mterp/x86/OP_INT_TO_CHAR.S b/vm/mterp/x86/OP_INT_TO_CHAR.S
new file mode 100644
index 0000000..49c8055
--- /dev/null
+++ b/vm/mterp/x86/OP_INT_TO_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/unop.S" {"instr":"movzwl %ax,%eax"}
diff --git a/vm/mterp/x86/OP_INT_TO_DOUBLE.S b/vm/mterp/x86/OP_INT_TO_DOUBLE.S
new file mode 100644
index 0000000..e9c5640
--- /dev/null
+++ b/vm/mterp/x86/OP_INT_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/fpcvt.S" {"load":"fildl","store":"fstpl"}
diff --git a/vm/mterp/x86/OP_INT_TO_FLOAT.S b/vm/mterp/x86/OP_INT_TO_FLOAT.S
new file mode 100644
index 0000000..06d658d
--- /dev/null
+++ b/vm/mterp/x86/OP_INT_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/fpcvt.S" {"load":"fildl","store":"fstps"}
diff --git a/vm/mterp/x86/OP_INT_TO_LONG.S b/vm/mterp/x86/OP_INT_TO_LONG.S
new file mode 100644
index 0000000..13f7483
--- /dev/null
+++ b/vm/mterp/x86/OP_INT_TO_LONG.S
@@ -0,0 +1,14 @@
+%verify "executed"
+ /* int to long vA, vB */
+ movzbl rINST_HI,%ecx # ecx<- +A
+ sarl $$12,rINST_FULL # rINST_FULL<- B
+ GET_VREG(%eax,rINST_FULL) # eax<- vB
+ SPILL(rPC) # will step on edx later
+ andb $$0xf,%cl # ecx<- A
+ cltd # edx:eax<- sssssssBBBBBBBB
+ SET_VREG_WORD(%edx,%ecx,1) # v[A+1]<- edx/rPC
+ UNSPILL(rPC)
+ SET_VREG_WORD(%eax,%ecx,0) # v[A+0]<- %eax
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_INT_TO_SHORT.S b/vm/mterp/x86/OP_INT_TO_SHORT.S
new file mode 100644
index 0000000..d5dc5d8
--- /dev/null
+++ b/vm/mterp/x86/OP_INT_TO_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/unop.S" {"instr":"movswl %ax,%eax"}
diff --git a/vm/mterp/x86/OP_INVOKE_DIRECT.S b/vm/mterp/x86/OP_INVOKE_DIRECT.S
new file mode 100644
index 0000000..f423dc3
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_DIRECT.S
@@ -0,0 +1,58 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+ /*
+ * Handle a direct method call.
+ *
+ * (We could defer the "is 'this' pointer null" test to the common
+ * method invocation code, and use a flag to indicate that static
+ * calls don't count. If we do this as part of copying the arguments
+ * out we could avoiding loading the first arg twice.)
+ *
+ * for: invoke-direct, invoke-direct/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- BBBB
+ movl offGlue_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+ EXPORT_PC()
+ SPILL(rPC)
+ movl offDvmDex_pResMethods(%ecx),%ecx # ecx<- pDvmDex->pResMethods
+ movzwl 4(rPC),rPC # rPC<- GFED or CCCC
+ movl (%ecx,%eax,4),%eax # eax<- resolved methodToCall
+ .if (!$isrange)
+ andl $$0xf,rPC # rPC<- D (or stays CCCC)
+ .endif
+ testl %eax,%eax # already resolved?
+ GET_VREG(%ecx,rPC) # ecx<- "this" ptr
+ je .L${opcode}_resolve # not resolved, do it now
+.L${opcode}_finish:
+ UNSPILL(rPC)
+ testl %ecx,%ecx # null "this"?
+ jne common_invokeMethod${routine} # no, continue on
+ jmp common_errNullObject
+%break
+
+ /*
+ * On entry:
+ * TMP_SPILL <- "this" register
+ * Things a bit ugly on this path, but it's the less
+ * frequent one. We'll have to do some reloading.
+ */
+.L${opcode}_resolve:
+ SPILL_TMP(%ecx)
+ GET_GLUE(%ecx)
+ UNSPILL(rPC)
+ movl offGlue_method(%ecx),%ecx # ecx<- glue->method
+ movzwl 2(rPC),%eax # reference (BBBB or CCCC)
+ movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+ movl $$METHOD_DIRECT,OUT_ARG2(%esp)
+ movl %eax,OUT_ARG1(%esp)
+ movl %ecx,OUT_ARG0(%esp)
+ call dvmResolveMethod # eax<- call(clazz, ref, flags)
+ UNSPILL_TMP(%ecx)
+ testl %eax,%eax
+ jne .L${opcode}_finish
+ UNSPILL(rPC)
+ jmp common_exceptionThrown
diff --git a/vm/mterp/x86/OP_INVOKE_DIRECT_EMPTY.S b/vm/mterp/x86/OP_INVOKE_DIRECT_EMPTY.S
new file mode 100644
index 0000000..cec8b0a
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_DIRECT_EMPTY.S
@@ -0,0 +1,7 @@
+%verify "executed"
+ /*
+ * invoke-direct-empty is a no-op in a "standard" interpreter.
+ */
+ FETCH_INST_WORD(3)
+ ADVANCE_PC(3)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_INVOKE_DIRECT_RANGE.S b/vm/mterp/x86/OP_INVOKE_DIRECT_RANGE.S
new file mode 100644
index 0000000..140430a
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_DIRECT_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_INVOKE_DIRECT.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/x86/OP_INVOKE_INTERFACE.S b/vm/mterp/x86/OP_INVOKE_INTERFACE.S
new file mode 100644
index 0000000..ff48ab8
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_INTERFACE.S
@@ -0,0 +1,38 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+%verify "null object"
+ /*
+ * Handle an interface method call.
+ *
+ * for: invoke-interface, invoke-interface/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ movzwl 4(rPC),%eax # eax<- FEDC or CCCC
+ GET_GLUE(%ecx)
+ .if (!$isrange)
+ andl $$0xf,%eax # eax<- C (or stays CCCC)
+ .endif
+ GET_VREG(%eax,%eax) # eax<- "this"
+ EXPORT_PC()
+ testl %eax,%eax # null this?
+ je common_errNullObject # yes, fail
+ movl offObject_clazz(%eax),%eax# eax<- thisPtr->clazz
+ movl %eax,OUT_ARG0(%esp) # arg0<- class
+ movl offGlue_methodClassDex(%ecx),%eax # eax<- methodClassDex
+ movl offGlue_method(%ecx),%ecx # ecx<- method
+ movl %eax,OUT_ARG3(%esp) # arg3<- dex
+ movzwl 2(rPC),%eax # eax<- BBBB
+ movl %ecx,OUT_ARG2(%esp) # arg2<- method
+ movl %eax,OUT_ARG1(%esp) # arg1<- BBBB
+ SPILL(rPC)
+ jmp .L${opcode}_continue
+%break
+
+.L${opcode}_continue:
+ call dvmFindInterfaceMethodInCache # eax<- call(class, ref, method, dex)
+ UNSPILL(rPC)
+ testl %eax,%eax
+ je common_exceptionThrown
+ jmp common_invokeMethod${routine}
diff --git a/vm/mterp/x86/OP_INVOKE_INTERFACE_RANGE.S b/vm/mterp/x86/OP_INVOKE_INTERFACE_RANGE.S
new file mode 100644
index 0000000..cbebedb
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_INTERFACE_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_INVOKE_INTERFACE.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/x86/OP_INVOKE_STATIC.S b/vm/mterp/x86/OP_INVOKE_STATIC.S
new file mode 100644
index 0000000..a8a8d77
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_STATIC.S
@@ -0,0 +1,36 @@
+%default { "routine":"NoRange","isrange":"0" }
+%verify "executed"
+%verify "unknown method"
+ /*
+ * Handle a static method call.
+ *
+ * for: invoke-static, invoke-static/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- BBBB
+ movl offGlue_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+ EXPORT_PC()
+ movl offDvmDex_pResMethods(%ecx),%ecx # ecx<- pDvmDex->pResMethods
+ movl (%ecx,%eax,4),%eax # eax<- resolved methodToCall
+ testl %eax,%eax
+ jne common_invokeMethod${routine}
+ GET_GLUE(%ecx)
+ movl offGlue_method(%ecx),%ecx # ecx<- glue->method
+ movzwl 2(rPC),%eax
+ movl offMethod_clazz(%ecx),%ecx# ecx<- method->clazz
+ movl %eax,OUT_ARG1(%esp) # arg1<- BBBB
+ movl %ecx,OUT_ARG0(%esp) # arg0<- clazz
+ jmp .L${opcode}_continue
+%break
+
+.L${opcode}_continue:
+ movl $$METHOD_STATIC,%eax
+ movl %eax,OUT_ARG2(%esp) # arg2<- flags
+ SPILL(rPC)
+ call dvmResolveMethod # call(clazz,ref,flags)
+ UNSPILL(rPC)
+ testl %eax,%eax # got null?
+ jne common_invokeMethod${routine}
+ jmp common_exceptionThrown
diff --git a/vm/mterp/x86/OP_INVOKE_STATIC_RANGE.S b/vm/mterp/x86/OP_INVOKE_STATIC_RANGE.S
new file mode 100644
index 0000000..da6d0bf
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_STATIC_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_INVOKE_STATIC.S" { "routine":"Range","isrange":"1" }
diff --git a/vm/mterp/x86/OP_INVOKE_SUPER.S b/vm/mterp/x86/OP_INVOKE_SUPER.S
new file mode 100644
index 0000000..013fc01
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_SUPER.S
@@ -0,0 +1,72 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+ /*
+ * Handle a "super" method call.
+ *
+ * for: invoke-super, invoke-super/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ GET_GLUE(rINST_FULL)
+ movzwl 2(rPC),%eax # eax<- BBBB
+ movl offGlue_methodClassDex(rINST_FULL),%ecx # ecx<- pDvmDex
+ EXPORT_PC()
+ movl offDvmDex_pResMethods(%ecx),%ecx # ecx<- pDvmDex->pResMethods
+ movl (%ecx,%eax,4),%ecx # ecx<- resolved baseMethod
+ movl offGlue_method(rINST_FULL),%eax # eax<- method
+ movzwl 4(rPC),rINST_FULL # rINST_FULL<- GFED or CCCC
+ .if (!$isrange)
+ andl $$0xf,rINST_FULL # rINST_FULL<- D (or stays CCCC)
+ .endif
+ GET_VREG(rINST_FULL,rINST_FULL) # rINST_FULL<- "this" ptr
+ testl rINST_FULL,rINST_FULL # null "this"?
+ je common_errNullObject # yes, throw
+ movl offMethod_clazz(%eax),%eax # eax<- method->clazz
+ testl %ecx,%ecx # already resolved?
+ jne .L${opcode}_continue # yes - go on
+ jmp .L${opcode}_resolve
+%break
+
+ /*
+ * At this point:
+ * ecx = resolved base method [r0]
+ * eax = method->clazz [r9]
+ */
+.L${opcode}_continue:
+ movl offClassObject_super(%eax),%eax # eax<- method->clazz->super
+ movzwl offMethod_methodIndex(%ecx),%ecx # ecx<- baseMthod->methodIndex
+ cmpl offClassObject_vtableCount(%eax),%ecx # compare(methodIndex,vtableCount)
+ jae .L${opcode}_nsm # method not present in superclass
+ movl offClassObject_vtable(%eax),%eax # eax<- ...clazz->super->vtable
+ movl (%eax,%ecx,4),%eax # eax<- vtable[methodIndex]
+ jmp common_invokeMethod${routine}
+
+
+ /* At this point:
+ * ecx = null (needs to be resolved base method)
+ * eax = method->clazz
+ */
+.L${opcode}_resolve:
+ SPILL_TMP(%eax) # method->clazz
+ movl %eax,OUT_ARG0(%esp) # arg0<- method->clazz
+ movzwl 2(rPC),%ecx # ecx<- BBBB
+ movl $$METHOD_VIRTUAL,OUT_ARG2(%esp) # arg2<- resolver method type
+ movl %ecx,OUT_ARG1(%esp) # arg1<- ref
+ SPILL(rPC)
+ call dvmResolveMethod # eax<- call(clazz, ref, flags)
+ UNSPILL(rPC)
+ testl %eax,%eax # got null?
+ movl %eax,%ecx # ecx<- resolved base method
+ UNSPILL_TMP(%eax) # restore method->clazz
+ jne .L${opcode}_continue # good to go - continue
+ jmp common_exceptionThrown # handle exception
+
+ /*
+ * Throw a NoSuchMethodError with the method name as the message.
+ * ecx = resolved base method
+ */
+.L${opcode}_nsm:
+ movl offMethod_name(%ecx),%eax
+ mov %eax,OUT_ARG1(%esp)
+ jmp common_errNoSuchMethod
diff --git a/vm/mterp/x86/OP_INVOKE_SUPER_QUICK.S b/vm/mterp/x86/OP_INVOKE_SUPER_QUICK.S
new file mode 100644
index 0000000..96c662a
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_SUPER_QUICK.S
@@ -0,0 +1,26 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+ /*
+ * Handle an optimized "super" method call.
+ *
+ * for: [opt] invoke-super-quick, invoke-super-quick/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 4(rPC),%eax # eax<- GFED or CCCC
+ movl offGlue_method(%ecx),%ecx # ecx<- current method
+ .if (!$isrange)
+ andl $$0xf,%eax # eax<- D (or stays CCCC)
+ .endif
+ movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+ GET_VREG(%eax,%eax) # eax<- "this"
+ movl offClassObject_super(%ecx),%ecx # ecx<- method->clazz->super
+ testl %eax,%eax # null "this"?
+ je common_errNullObject # "this" is null, throw exception
+ movzwl 2(rPC),%eax # eax<- BBBB
+ movl offClassObject_vtable(%ecx),%ecx # ecx<- vtable
+ EXPORT_PC()
+ movl (%ecx,%eax,4),%eax # eax<- super->vtable[BBBB]
+ jmp common_invokeMethod${routine}
diff --git a/vm/mterp/x86/OP_INVOKE_SUPER_QUICK_RANGE.S b/vm/mterp/x86/OP_INVOKE_SUPER_QUICK_RANGE.S
new file mode 100644
index 0000000..e0a27a3
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_SUPER_QUICK_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_INVOKE_SUPER_QUICK.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/x86/OP_INVOKE_SUPER_RANGE.S b/vm/mterp/x86/OP_INVOKE_SUPER_RANGE.S
new file mode 100644
index 0000000..ffbcf8c
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_SUPER_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_INVOKE_SUPER.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/x86/OP_INVOKE_VIRTUAL.S b/vm/mterp/x86/OP_INVOKE_VIRTUAL.S
new file mode 100644
index 0000000..85fcf83
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_VIRTUAL.S
@@ -0,0 +1,55 @@
+
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "unknown method"
+%verify "null object"
+ /*
+ * Handle a virtual method call.
+ *
+ * for: invoke-virtual, invoke-virtual/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ GET_GLUE(%eax)
+ movzwl 2(rPC),%ecx # ecx<- BBBB
+ movl offGlue_methodClassDex(%eax),%eax # eax<- pDvmDex
+ EXPORT_PC()
+ movl offDvmDex_pResMethods(%eax),%eax # eax<- pDvmDex->pResMethods
+ movl (%eax,%ecx,4),%eax # eax<- resolved baseMethod
+ testl %eax,%eax # already resolved?
+ jne .L${opcode}_continue # yes, continue
+ GET_GLUE(%eax)
+ movl %ecx,OUT_ARG1(%esp) # arg1<- ref
+ movl offGlue_method(%eax),%eax # eax<- glue->method
+ SPILL(rPC)
+ jmp .L${opcode}_more
+%break
+
+
+.L${opcode}_more:
+ movl offMethod_clazz(%eax),%eax # ecx<- method->clazz
+ movl %eax,OUT_ARG0(%esp) # arg0<- clazz
+ movl $$METHOD_VIRTUAL,OUT_ARG2(%esp) # arg2<- flags
+ call dvmResolveMethod # eax<- call(clazz, ref, flags)
+ UNSPILL(rPC)
+ testl %eax,%eax # got null?
+ jne .L${opcode}_continue # no, continue
+ jmp common_exceptionThrown # yes, handle exception
+
+ /* At this point:
+ * eax = resolved base method
+ * ecx = scratch
+ */
+.L${opcode}_continue:
+ movzwl 4(rPC),%ecx # ecx<- GFED or CCCC
+ .if (!$isrange)
+ andl $$0xf,%ecx # ecx<- D (or stays CCCC)
+ .endif
+ GET_VREG(%ecx,%ecx) # ecx<- "this"
+ movzwl offMethod_methodIndex(%eax),%eax # eax<- baseMethod->methodIndex
+ testl %ecx,%ecx # null this?
+ je common_errNullObject # go if so
+ movl offObject_clazz(%ecx),%ecx # ecx<- thisPtr->clazz
+ movl offClassObject_vtable(%ecx),%ecx # ecx<- thisPtr->clazz->vtable
+ movl (%ecx,%eax,4),%eax # eax<- vtable[methodIndex]
+ jmp common_invokeMethod${routine}
diff --git a/vm/mterp/x86/OP_INVOKE_VIRTUAL_QUICK.S b/vm/mterp/x86/OP_INVOKE_VIRTUAL_QUICK.S
new file mode 100644
index 0000000..1ba93eb
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_VIRTUAL_QUICK.S
@@ -0,0 +1,23 @@
+%default { "isrange":"0", "routine":"NoRange" }
+%verify "executed"
+%verify "null object"
+ /*
+ * Handle an optimized virtual method call.
+ *
+ * for: [opt] invoke-virtual-quick, invoke-virtual-quick/range
+ */
+ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */
+ /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */
+ movzwl 4(rPC),%eax # eax<- FEDC or CCCC
+ movzwl 2(rPC),%ecx # ecx<- BBBB
+ .if (!$isrange)
+ andl $$0xf,%eax # eax<- C (or stays CCCC)
+ .endif
+ GET_VREG(%eax,%eax) # eax<- vC ("this" ptr)
+ testl %eax,%eax # null?
+ je common_errNullObject # yep, throw exception
+ movl offObject_clazz(%eax),%eax # eax<- thisPtr->clazz
+ movl offClassObject_vtable(%eax),%eax # eax<- thisPtr->clazz->vtable
+ EXPORT_PC() # might throw later - get ready
+ movl (%eax,%ecx,4),%eax # eax<- vtable[BBBB]
+ jmp common_invokeMethod${routine}
diff --git a/vm/mterp/x86/OP_INVOKE_VIRTUAL_QUICK_RANGE.S b/vm/mterp/x86/OP_INVOKE_VIRTUAL_QUICK_RANGE.S
new file mode 100644
index 0000000..53d7602
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_VIRTUAL_QUICK_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_INVOKE_VIRTUAL_QUICK.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/x86/OP_INVOKE_VIRTUAL_RANGE.S b/vm/mterp/x86/OP_INVOKE_VIRTUAL_RANGE.S
new file mode 100644
index 0000000..e91a89e
--- /dev/null
+++ b/vm/mterp/x86/OP_INVOKE_VIRTUAL_RANGE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_INVOKE_VIRTUAL.S" { "isrange":"1", "routine":"Range" }
diff --git a/vm/mterp/x86/OP_IPUT.S b/vm/mterp/x86/OP_IPUT.S
new file mode 100644
index 0000000..78d9edb
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT.S
@@ -0,0 +1,64 @@
+
+%default { "store":"movl", "reg":"rINST_FULL", "sqnum":"0" }
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+ /*
+ * General 32-bit instance field put.
+ *
+ * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short
+ */
+ /* op vA, vB, field@CCCC */
+ GET_GLUE(%ecx)
+ SPILL(rIBASE) # need another reg
+ movzwl 2(rPC),rIBASE # rIBASE<- 0000CCCC
+ movl offGlue_methodClassDex(%ecx),%eax # eax<- DvmDex
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $$4,%ecx # ecx<- B
+ movl offDvmDex_pResFields(%eax),%eax # eax<- pDvmDex->pResFields
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $$0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG(%ecx,%ecx) # ecx<- fp[B], the object ptr
+ movl (%eax,rIBASE,4),%eax # resolved entry
+ testl %eax,%eax # is resolved entry null?
+ jne .L${opcode}_finish # no, already resolved
+ movl rIBASE,OUT_ARG1(%esp)
+ GET_GLUE(rIBASE)
+ jmp .L${opcode}_resolve
+%break
+
+
+.L${opcode}_resolve:
+ EXPORT_PC()
+ SPILL(rPC)
+ movl offGlue_method(rIBASE),rPC # rPC<- current method
+ UNSPILL(rIBASE)
+ movl offMethod_clazz(rPC),rPC # rPC<- method->clazz
+ SPILL_TMP(%ecx) # save object pointer across call
+ movl rPC,OUT_ARG0(%esp) # pass in method->clazz
+ call dvmResolveInstField # ... to dvmResolveInstField
+ UNSPILL_TMP(%ecx)
+ UNSPILL(rPC)
+ testl %eax,%eax # ... which returns InstrField ptr
+ jne .L${opcode}_finish
+ jmp common_exceptionThrown
+
+.L${opcode}_finish:
+ /*
+ * Currently:
+ * eax holds resolved field
+ * ecx holds object
+ * rIBASE is scratch, but needs to be unspilled
+ * rINST_FULL holds A
+ */
+ GET_VREG(rINST_FULL,rINST_FULL) # rINST_FULL<- v[A]
+ movl offInstField_byteOffset(%eax),%eax # eax<- byte offset of field
+ UNSPILL(rIBASE)
+ testl %ecx,%ecx # object null?
+ je common_errNullObject # object was null
+ $store $reg,(%ecx,%eax,1) # obj.field <- v[A](8/16/32 bits)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_IPUT_BOOLEAN.S b/vm/mterp/x86/OP_IPUT_BOOLEAN.S
new file mode 100644
index 0000000..5072a68
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_IPUT.S" { "store":"movb","reg":"rINST_LO", "sqnum":"1" }
diff --git a/vm/mterp/x86/OP_IPUT_BYTE.S b/vm/mterp/x86/OP_IPUT_BYTE.S
new file mode 100644
index 0000000..3ad2a4b
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_IPUT.S" { "store":"movb", "reg":"rINST_LO", "sqnum":"2" }
diff --git a/vm/mterp/x86/OP_IPUT_CHAR.S b/vm/mterp/x86/OP_IPUT_CHAR.S
new file mode 100644
index 0000000..c7a7478
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_IPUT.S" { "store":"movw", "reg":"rINST", "sqnum":"3" }
diff --git a/vm/mterp/x86/OP_IPUT_OBJECT.S b/vm/mterp/x86/OP_IPUT_OBJECT.S
new file mode 100644
index 0000000..87ce915
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_OBJECT.S
@@ -0,0 +1,70 @@
+%default { "sqnum":"0" }
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+ /*
+ * Object field put.
+ *
+ * for: iput-object
+ */
+ /* op vA, vB, field@CCCC */
+ GET_GLUE(%ecx)
+ SPILL(rIBASE) # need another reg
+ movzwl 2(rPC),rIBASE # rIBASE<- 0000CCCC
+ movl offGlue_methodClassDex(%ecx),%eax # eax<- DvmDex
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $$4,%ecx # ecx<- B
+ movl offDvmDex_pResFields(%eax),%eax # eax<- pDvmDex->pResFields
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $$0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG(%ecx,%ecx) # ecx<- fp[B], the object ptr
+ movl (%eax,rIBASE,4),%eax # resolved entry
+ testl %eax,%eax # is resolved entry null?
+ jne .L${opcode}_finish # no, already resolved
+ movl rIBASE,OUT_ARG1(%esp)
+ GET_GLUE(rIBASE)
+ jmp .L${opcode}_resolve
+%break
+
+
+.L${opcode}_resolve:
+ EXPORT_PC()
+ SPILL(rPC)
+ movl offGlue_method(rIBASE),rPC # rPC<- current method
+ UNSPILL(rIBASE)
+ movl offMethod_clazz(rPC),rPC # rPC<- method->clazz
+ SPILL_TMP(%ecx) # save object pointer across call
+ movl rPC,OUT_ARG0(%esp) # pass in method->clazz
+ call dvmResolveInstField # ... to dvmResolveInstField
+ UNSPILL_TMP(%ecx)
+ UNSPILL(rPC)
+ testl %eax,%eax # ... which returns InstrField ptr
+ jne .L${opcode}_finish
+ jmp common_exceptionThrown
+
+.L${opcode}_finish:
+ /*
+ * Currently:
+ * eax holds resolved field
+ * ecx holds object
+ * rIBASE is scratch, but needs to be unspilled
+ * rINST_FULL holds A
+ */
+ GET_VREG(rINST_FULL,rINST_FULL) # rINST_FULL<- v[A]
+ movl offInstField_byteOffset(%eax),%eax # eax<- byte offset of field
+ UNSPILL(rIBASE)
+ testl %ecx,%ecx # object null?
+ je common_errNullObject # object was null
+ movl rINST_FULL,(%ecx,%eax) # obj.field <- v[A](8/16/32 bits)
+ GET_GLUE(%eax)
+ testl rINST_FULL,rINST_FULL # stored a NULL?
+ movl offGlue_cardTable(%eax),%eax # get card table base
+ FETCH_INST_WORD(2)
+ je 1f # skip card mark if null store
+ shrl $$GC_CARD_SHIFT,%ecx # object head to card number
+ movb %al,(%eax,%ecx) # mark card using object head
+1:
+ ADVANCE_PC(2)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_IPUT_OBJECT_QUICK.S b/vm/mterp/x86/OP_IPUT_OBJECT_QUICK.S
new file mode 100644
index 0000000..3537628
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_OBJECT_QUICK.S
@@ -0,0 +1,28 @@
+%verify "executed"
+%verify "null object"
+ /* For: iput-object-quick */
+ /* op vA, vB, offset@CCCC */
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $$4,%ecx # ecx<- B
+ GET_VREG(%ecx,%ecx) # vB (object we're operating on)
+ movzbl rINST_HI,rINST_FULL
+ andb $$0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG(rINST_FULL,rINST_FULL) # rINST_FULL<- v[A]
+ movzwl 2(rPC),%eax # eax<- field byte offset
+ testl %ecx,%ecx # is object null?
+ je common_errNullObject
+ movl rINST_FULL,(%ecx,%eax,1)
+ GET_GLUE(%eax)
+ jmp .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+ testl rINST_FULL,rINST_FULL # did we store null?
+ FETCH_INST_WORD(2)
+ movl offGlue_cardTable(%eax),%eax # get card table base
+ je 1f # skip card mark if null store
+ shrl $$GC_CARD_SHIFT,%ecx # object head to card number
+ movb %al,(%eax,%ecx) # mark card based on object head
+1:
+ ADVANCE_PC(2)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_IPUT_OBJECT_VOLATILE.S b/vm/mterp/x86/OP_IPUT_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..3959c06
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_OBJECT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_IPUT_OBJECT.S"
diff --git a/vm/mterp/x86/OP_IPUT_QUICK.S b/vm/mterp/x86/OP_IPUT_QUICK.S
new file mode 100644
index 0000000..e62ec00
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_QUICK.S
@@ -0,0 +1,17 @@
+%verify "executed"
+%verify "null object"
+ /* For: iput-quick */
+ /* op vA, vB, offset@CCCC */
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $$4,%ecx # ecx<- B
+ GET_VREG(%ecx,%ecx) # vB (object we're operating on)
+ movzbl rINST_HI,rINST_FULL
+ andb $$0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG(rINST_FULL,rINST_FULL) # rINST_FULL<- v[A]
+ movzwl 2(rPC),%eax # eax<- field byte offset
+ testl %ecx,%ecx # is object null?
+ je common_errNullObject
+ movl rINST_FULL,(%ecx,%eax,1)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_IPUT_SHORT.S b/vm/mterp/x86/OP_IPUT_SHORT.S
new file mode 100644
index 0000000..4b20b46
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_IPUT.S" { "store":"movw", "reg":"rINST", "sqnum":"4" }
diff --git a/vm/mterp/x86/OP_IPUT_VOLATILE.S b/vm/mterp/x86/OP_IPUT_VOLATILE.S
new file mode 100644
index 0000000..caf007e
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_IPUT.S"
diff --git a/vm/mterp/x86/OP_IPUT_WIDE.S b/vm/mterp/x86/OP_IPUT_WIDE.S
new file mode 100644
index 0000000..53f8212
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_WIDE.S
@@ -0,0 +1,64 @@
+%verify "executed"
+%verify "null object"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+ /*
+ * 64-bit instance field put.
+ *
+ */
+ /* op vA, vB, field@CCCC */
+ GET_GLUE(%ecx)
+ SPILL(rIBASE) # need another reg
+ movzwl 2(rPC),rIBASE # rIBASE<- 0000CCCC
+ movl offGlue_methodClassDex(%ecx),%eax # eax<- DvmDex
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $$4,%ecx # ecx<- B
+ movl offDvmDex_pResFields(%eax),%eax # eax<- pDvmDex->pResFields
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $$0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG(%ecx,%ecx) # ecx<- fp[B], the object ptr
+ movl (%eax,rIBASE,4),%eax # resolved entry
+ testl %eax,%eax # is resolved entry null?
+ jne .L${opcode}_finish # no, already resolved
+ movl rIBASE,OUT_ARG1(%esp)
+ GET_GLUE(rIBASE)
+ jmp .L${opcode}_resolve
+%break
+
+
+.L${opcode}_resolve:
+ EXPORT_PC()
+ SPILL(rPC)
+ movl offGlue_method(rIBASE),rPC # rPC<- current method
+ UNSPILL(rIBASE)
+ movl offMethod_clazz(rPC),rPC # rPC<- method->clazz
+ SPILL_TMP(%ecx) # save object pointer across call
+ movl rPC,OUT_ARG0(%esp) # pass in method->clazz
+ call dvmResolveInstField # ... to dvmResolveInstField
+ UNSPILL_TMP(%ecx)
+ UNSPILL(rPC)
+ testl %eax,%eax # ... which returns InstrField ptr
+ jne .L${opcode}_finish
+ jmp common_exceptionThrown
+
+.L${opcode}_finish:
+ /*
+ * Currently:
+ * eax holds resolved field
+ * ecx holds object
+ * rIBASE is scratch, but needs to be unspilled
+ * rINST_FULL holds A
+ */
+ movl offInstField_byteOffset(%eax),%eax # eax<- byte offset of field
+ UNSPILL(rIBASE)
+ testl %ecx,%ecx # object null?
+ je common_errNullObject # object was null
+ leal (%ecx,%eax,1),%eax # eax<- address of field
+ GET_VREG_WORD(%ecx,rINST_FULL,0) # ecx<- lsw
+ GET_VREG_WORD(rINST_FULL,rINST_FULL,1) # rINST_FULL<- msw
+ movl rINST_FULL,4(%eax)
+ FETCH_INST_WORD(2)
+ movl %ecx,(%eax)
+ ADVANCE_PC(2)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_IPUT_WIDE_QUICK.S b/vm/mterp/x86/OP_IPUT_WIDE_QUICK.S
new file mode 100644
index 0000000..d2a1ae4
--- /dev/null
+++ b/vm/mterp/x86/OP_IPUT_WIDE_QUICK.S
@@ -0,0 +1,20 @@
+%verify "executed"
+%verify "null object"
+ /* For: iput-wide-quick */
+ /* op vA, vB, offset@CCCC */
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $$4,%ecx # ecx<- B
+ GET_VREG(%ecx,%ecx) # vB (object we're operating on)
+ movzwl 2(rPC),%eax # eax<- field byte offset
+ testl %ecx,%ecx # is object null?
+ je common_errNullObject
+ leal (%ecx,%eax,1),%ecx # ecx<- Address of 64-bit target
+ movzbl rINST_HI,rINST_FULL
+ andb $$0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG_WORD(%eax,rINST_FULL,0) # eax<- lsw
+ GET_VREG_WORD(rINST_FULL,rINST_FULL,1) # rINST_FULL<- msw
+ movl %eax,(%ecx)
+ movl rINST_FULL,4(%ecx)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_LONG_TO_DOUBLE.S b/vm/mterp/x86/OP_LONG_TO_DOUBLE.S
new file mode 100644
index 0000000..7235315
--- /dev/null
+++ b/vm/mterp/x86/OP_LONG_TO_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/fpcvt.S" {"load":"fildll","store":"fstpl"}
diff --git a/vm/mterp/x86/OP_LONG_TO_FLOAT.S b/vm/mterp/x86/OP_LONG_TO_FLOAT.S
new file mode 100644
index 0000000..2c4359a
--- /dev/null
+++ b/vm/mterp/x86/OP_LONG_TO_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/fpcvt.S" {"load":"fildll","store":"fstps"}
diff --git a/vm/mterp/x86/OP_LONG_TO_INT.S b/vm/mterp/x86/OP_LONG_TO_INT.S
new file mode 100644
index 0000000..bf5060f
--- /dev/null
+++ b/vm/mterp/x86/OP_LONG_TO_INT.S
@@ -0,0 +1,3 @@
+%verify "executed"
+/* we ignore the high word, making this equivalent to a 32-bit reg move */
+%include "x86/OP_MOVE.S"
diff --git a/vm/mterp/x86/OP_MONITOR_ENTER.S b/vm/mterp/x86/OP_MONITOR_ENTER.S
new file mode 100644
index 0000000..548f71f
--- /dev/null
+++ b/vm/mterp/x86/OP_MONITOR_ENTER.S
@@ -0,0 +1,32 @@
+%verify "executed"
+%verify "exception for null object"
+ /*
+ * Synchronize on an object.
+ */
+ /* monitor-enter vAA */
+ GET_GLUE(%ecx)
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG(%eax,rINST_FULL) # eax<- vAA
+ movl offGlue_self(%ecx),%ecx # ecx<- glue->self
+ FETCH_INST_WORD(1)
+ testl %eax,%eax # null object?
+ EXPORT_PC() # need for precise GC, MONITOR_TRACKING
+ jne .L${opcode}_continue
+ jmp common_errNullObject
+%break
+
+.L${opcode}_continue:
+ SPILL(rPC) # have to - caller save
+ movl %ecx,OUT_ARG0(%esp)
+ movl %eax,OUT_ARG1(%esp)
+ call dvmLockObject # dvmLockObject(self,object)
+ UNSPILL(rPC)
+#ifdef WITH_DEADLOCK_PREDICTION
+ GET_GLUE(%ecx)
+ movl offGlueSelf(%ecx),%ecx # ecx<- glue->self
+ movl offThread_exception(%ecx),%eax
+ testl %eax,%eax
+ jne common_exceptionThrown
+#endif
+ ADVANCE_PC(1)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_MONITOR_EXIT.S b/vm/mterp/x86/OP_MONITOR_EXIT.S
new file mode 100644
index 0000000..788b7a7
--- /dev/null
+++ b/vm/mterp/x86/OP_MONITOR_EXIT.S
@@ -0,0 +1,35 @@
+%verify "executed"
+%verify "exception for null object (impossible in javac)"
+%verify "dvmUnlockObject fails"
+ /*
+ * Unlock an object.
+ *
+ * Exceptions that occur when unlocking a monitor need to appear as
+ * if they happened at the following instruction. See the Dalvik
+ * instruction spec.
+ */
+ /* monitor-exit vAA */
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG(%eax,rINST_FULL)
+ GET_GLUE(%ecx)
+ EXPORT_PC()
+ testl %eax,%eax # null object?
+ je .L${opcode}_errNullObject # go if so
+ movl offGlue_self(%ecx),%ecx # ecx<- glue->self
+ movl %eax,OUT_ARG1(%esp)
+ SPILL(rPC)
+ movl %ecx,OUT_ARG0(%esp)
+ jmp .L${opcode}_continue
+%break
+
+.L${opcode}_continue:
+ call dvmUnlockObject # unlock(self,obj)
+ UNSPILL(rPC)
+ FETCH_INST_WORD(1)
+ testl %eax,%eax # success?
+ ADVANCE_PC(1)
+ je common_exceptionThrown # no, exception pending
+ GOTO_NEXT
+.L${opcode}_errNullObject:
+ ADVANCE_PC(1) # advance before throw
+ jmp common_errNullObject
diff --git a/vm/mterp/x86/OP_MOVE.S b/vm/mterp/x86/OP_MOVE.S
new file mode 100644
index 0000000..f0d070d
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE.S
@@ -0,0 +1,11 @@
+%verify "executed"
+ /* for move, move-object, long-to-int */
+ /* op vA, vB */
+ movzbl rINST_HI,%eax # eax<- BA
+ andb $$0xf,%al # eax<- A
+ shrl $$12,rINST_FULL # rINST_FULL<- B
+ GET_VREG(%ecx,rINST_FULL)
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ SET_VREG(%ecx,%eax) # fp[A]<-fp[B]
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_MOVE_16.S b/vm/mterp/x86/OP_MOVE_16.S
new file mode 100644
index 0000000..9a0e4ee
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_16.S
@@ -0,0 +1,10 @@
+%verify "executed"
+ /* for: move/16, move-object/16 */
+ /* op vAAAA, vBBBB */
+ movzwl 4(rPC),%ecx # ecx<- BBBB
+ movzwl 2(rPC),%eax # eax<- AAAA
+ GET_VREG(%ecx,%ecx)
+ FETCH_INST_WORD(3)
+ ADVANCE_PC(3)
+ SET_VREG(%ecx,%eax)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_MOVE_EXCEPTION.S b/vm/mterp/x86/OP_MOVE_EXCEPTION.S
new file mode 100644
index 0000000..07a32c9
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_EXCEPTION.S
@@ -0,0 +1,11 @@
+%verify "executed"
+ /* move-exception vAA */
+ GET_GLUE(%ecx)
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ movl offGlue_self(%ecx),%ecx # ecx<- glue->self
+ movl offThread_exception(%ecx),%eax # eax<- dvmGetException bypass
+ SET_VREG(%eax,rINST_FULL) # fp[AA]<- exception object
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ movl $$0,offThread_exception(%ecx) # dvmClearException bypass
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_MOVE_FROM16.S b/vm/mterp/x86/OP_MOVE_FROM16.S
new file mode 100644
index 0000000..35d3147
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_FROM16.S
@@ -0,0 +1,10 @@
+%verify "executed"
+ /* for: move/from16, move-object/from16 */
+ /* op vAA, vBBBB */
+ movzx rINST_HI,%eax # eax <= AA
+ movw 2(rPC),rINST # rINST <= BBBB
+ GET_VREG (%ecx,rINST_FULL) # ecx<- fp[BBBB]
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ SET_VREG (%ecx,%eax) # fp[AA]<- ecx]
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_MOVE_OBJECT.S b/vm/mterp/x86/OP_MOVE_OBJECT.S
new file mode 100644
index 0000000..f32d5a6
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_MOVE.S"
diff --git a/vm/mterp/x86/OP_MOVE_OBJECT_16.S b/vm/mterp/x86/OP_MOVE_OBJECT_16.S
new file mode 100644
index 0000000..859e4db
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_OBJECT_16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_MOVE_16.S"
diff --git a/vm/mterp/x86/OP_MOVE_OBJECT_FROM16.S b/vm/mterp/x86/OP_MOVE_OBJECT_FROM16.S
new file mode 100644
index 0000000..fef4401
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_OBJECT_FROM16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_MOVE_FROM16.S"
diff --git a/vm/mterp/x86/OP_MOVE_RESULT.S b/vm/mterp/x86/OP_MOVE_RESULT.S
new file mode 100644
index 0000000..160aec6
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_RESULT.S
@@ -0,0 +1,10 @@
+%verify "executed"
+ /* for: move-result, move-result-object */
+ /* op vAA */
+ GET_GLUE(%eax) # eax<- rGLUE
+ movzx rINST_HI,%ecx # ecx<- AA
+ movl offGlue_retval(%eax),%eax # eax<- glue->retval.l
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ SET_VREG (%eax,%ecx) # fp[AA]<- retval.l
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_MOVE_RESULT_OBJECT.S b/vm/mterp/x86/OP_MOVE_RESULT_OBJECT.S
new file mode 100644
index 0000000..a3f1b1f
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_RESULT_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_MOVE_RESULT.S"
diff --git a/vm/mterp/x86/OP_MOVE_RESULT_WIDE.S b/vm/mterp/x86/OP_MOVE_RESULT_WIDE.S
new file mode 100644
index 0000000..905037f
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_RESULT_WIDE.S
@@ -0,0 +1,11 @@
+%verify "executed"
+ /* move-result-wide vAA */
+ GET_GLUE(%ecx)
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ movl offGlue_retval(%ecx),%eax
+ movl 4+offGlue_retval(%ecx),%ecx
+ SET_VREG_WORD(%eax,rINST_FULL,0) # v[AA+0] <- eax
+ SET_VREG_WORD(%ecx,rINST_FULL,1) # v[AA+1] <- ecx
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_MOVE_WIDE.S b/vm/mterp/x86/OP_MOVE_WIDE.S
new file mode 100644
index 0000000..022cc6e
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_WIDE.S
@@ -0,0 +1,13 @@
+%verify "executed"
+ /* move-wide vA, vB */
+ /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+ movzbl rINST_HI,%ecx # ecx <- BA
+ sarl $$12,rINST_FULL # rinst_FULL<- B
+ GET_VREG_WORD(%eax,rINST_FULL,0) # eax<- v[B+0]
+ GET_VREG_WORD(rINST_FULL,rINST_FULL,1) # rINST_FULL<- v[B+1]
+ andb $$0xf,%cl # ecx <- A
+ SET_VREG_WORD(rINST_FULL,%ecx,1) # v[A+1]<- rINST_FULL
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ SET_VREG_WORD(%eax,%ecx,0) # v[A+0]<- eax
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_MOVE_WIDE_16.S b/vm/mterp/x86/OP_MOVE_WIDE_16.S
new file mode 100644
index 0000000..d7be1d1
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_WIDE_16.S
@@ -0,0 +1,12 @@
+%verify "executed"
+ /* move-wide/16 vAAAA, vBBBB */
+ /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+ movzwl 4(rPC),%ecx # ecx<- BBBB
+ movzwl 2(rPC),%eax # eax<- AAAA
+ GET_VREG_WORD(rINST_FULL,%ecx,0) # rINST_WORD<- v[BBBB+0]
+ GET_VREG_WORD(%ecx,%ecx,1) # ecx<- v[BBBB+1]
+ SET_VREG_WORD(rINST_FULL,%eax,0) # v[AAAA+0]<- rINST_FULL
+ FETCH_INST_WORD(3)
+ ADVANCE_PC(3)
+ SET_VREG_WORD(%ecx,%eax,1) # v[AAAA+1]<- ecx
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_MOVE_WIDE_FROM16.S b/vm/mterp/x86/OP_MOVE_WIDE_FROM16.S
new file mode 100644
index 0000000..cbc13d2
--- /dev/null
+++ b/vm/mterp/x86/OP_MOVE_WIDE_FROM16.S
@@ -0,0 +1,12 @@
+%verify "executed"
+ /* move-wide/from16 vAA, vBBBB */
+ /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */
+ movzwl 2(rPC),%ecx # ecx<- BBBB
+ movzbl rINST_HI,%eax # eax<- AAAA
+ GET_VREG_WORD(rINST_FULL,%ecx,0) # rINST_FULL<- v[BBBB+0]
+ GET_VREG_WORD(%ecx,%ecx,1) # ecx<- v[BBBB+1]
+ SET_VREG_WORD(rINST_FULL,%eax,0) # v[AAAA+0]<- rINST_FULL
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ SET_VREG_WORD(%ecx,%eax,1) # v[AAAA+1]<- eax
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_MUL_DOUBLE.S b/vm/mterp/x86/OP_MUL_DOUBLE.S
new file mode 100644
index 0000000..59a2079
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop.S" {"instr":"fmull","load":"fldl","store":"fstpl"}
diff --git a/vm/mterp/x86/OP_MUL_DOUBLE_2ADDR.S b/vm/mterp/x86/OP_MUL_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..45a2fa3
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop2addr.S" {"instr":"fmull","load":"fldl","store":"fstpl"}
diff --git a/vm/mterp/x86/OP_MUL_FLOAT.S b/vm/mterp/x86/OP_MUL_FLOAT.S
new file mode 100644
index 0000000..5515c4f
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop.S" {"instr":"fmuls","load":"flds","store":"fstps"}
diff --git a/vm/mterp/x86/OP_MUL_FLOAT_2ADDR.S b/vm/mterp/x86/OP_MUL_FLOAT_2ADDR.S
new file mode 100644
index 0000000..c8a3bf7
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop2addr.S" {"instr":"fmuls","load":"flds","store":"fstps"}
diff --git a/vm/mterp/x86/OP_MUL_INT.S b/vm/mterp/x86/OP_MUL_INT.S
new file mode 100644
index 0000000..b859672
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_INT.S
@@ -0,0 +1,16 @@
+%verify "executed"
+ /*
+ * 32-bit binary multiplication.
+ */
+ /* mul vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ SPILL(rPC)
+ GET_VREG(%eax,%eax) # eax<- vBB
+ imull (rFP,%ecx,4),%eax # trashes rPC/edx
+ UNSPILL(rPC)
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ SET_VREG(%eax,%ecx)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_MUL_INT_2ADDR.S b/vm/mterp/x86/OP_MUL_INT_2ADDR.S
new file mode 100644
index 0000000..823ab64
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_INT_2ADDR.S
@@ -0,0 +1,13 @@
+%verify "executed"
+ /* mul vA, vB */
+ movzx rINST_HI,%ecx # ecx<- A+
+ sarl $$12,rINST_FULL # rINST_FULL<- B
+ GET_VREG(%eax,rINST_FULL) # eax<- vB
+ andb $$0xf,%cl # ecx<- A
+ SPILL(rPC)
+ imull (rFP,%ecx,4),%eax
+ UNSPILL(rPC)
+ SET_VREG(%eax,%ecx)
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_MUL_INT_LIT16.S b/vm/mterp/x86/OP_MUL_INT_LIT16.S
new file mode 100644
index 0000000..f562425
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_INT_LIT16.S
@@ -0,0 +1,16 @@
+%verify "executed"
+ /* mul/lit16 vA, vB, #+CCCC */
+ /* Need A in rINST_FULL, ssssCCCC in ecx, vB in eax */
+ movzbl rINST_HI,%eax # eax<- 000000BA
+ sarl $$4,%eax # eax<- B
+ GET_VREG(%eax,%eax) # eax<- vB
+ movswl 2(rPC),%ecx # ecx<- ssssCCCC
+ SPILL(rPC)
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $$0xf,rINST_LO # rINST_FULL<- A
+ imull %ecx,%eax # trashes rPC
+ UNSPILL(rPC)
+ SET_VREG(%eax,rINST_FULL)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_MUL_INT_LIT8.S b/vm/mterp/x86/OP_MUL_INT_LIT8.S
new file mode 100644
index 0000000..2cf11b3
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_INT_LIT8.S
@@ -0,0 +1,13 @@
+%verify "executed"
+ /* mul/lit8 vAA, vBB, #+CC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movsbl 3(rPC),%ecx # ecx<- ssssssCC
+ SPILL(rPC)
+ movzx rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG (%eax,%eax) # eax<- rBB
+ imull %ecx,%eax # trashes rPC
+ UNSPILL(rPC)
+ SET_VREG (%eax,rINST_FULL)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_MUL_LONG.S b/vm/mterp/x86/OP_MUL_LONG.S
new file mode 100644
index 0000000..fd151e0
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_LONG.S
@@ -0,0 +1,43 @@
+%verify "executed"
+ /*
+ * Signed 64-bit integer multiply.
+ *
+ * We could definately use more free registers for
+ * this code. We must spill rPC (edx) because it
+ * is used by imul. We'll also spill rINST (ebx),
+ * giving us eax, ebc, ecx and edx as computational
+ * temps. On top of that, we'll spill rIBASE (edi)
+ * for use as the vB pointer and rFP (esi) for use
+ * as the vC pointer. Yuck.
+ */
+ /* mul-long vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- B
+ movzbl 3(rPC),%ecx # ecx<- C
+ SPILL(rPC)
+ SPILL(rIBASE)
+ SPILL(rFP)
+ SPILL(rINST_FULL)
+ leal (rFP,%eax,4),rIBASE # rIBASE<- &v[B]
+ leal (rFP,%ecx,4),rFP # rFP<- &v[C]
+ movl 4(rIBASE),%ecx # ecx<- Bmsw
+ imull (rFP),%ecx # ecx<- (Bmsw*Clsw)
+ movl 4(rFP),%eax # eax<- Cmsw
+ imull (rIBASE),%eax # eax<- (Cmsw*Blsw)
+ addl %eax,%ecx # ecx<- (Bmsw*Clsw)+(Cmsw*Blsw)
+ movl (rFP),%eax # eax<- Clsw
+ mull (rIBASE) # eax<- (Clsw*Alsw)
+ UNSPILL(rINST_FULL)
+ UNSPILL(rFP)
+ jmp .L${opcode}_continue
+%break
+
+.L${opcode}_continue:
+ leal (%ecx,%edx),%edx # full result now in %edx:%eax
+ movzbl rINST_HI,%ecx # ecx<- A
+ movl %edx,4(rFP,%ecx,4) # v[B+1]<- %edx
+ UNSPILL(rPC) # restore rPC/%edx
+ FETCH_INST_WORD(2)
+ UNSPILL(rIBASE)
+ movl %eax,(rFP,%ecx,4) # v[B]<- %eax
+ ADVANCE_PC(2)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_MUL_LONG_2ADDR.S b/vm/mterp/x86/OP_MUL_LONG_2ADDR.S
new file mode 100644
index 0000000..5651dfe
--- /dev/null
+++ b/vm/mterp/x86/OP_MUL_LONG_2ADDR.S
@@ -0,0 +1,41 @@
+%verify "executed"
+ /*
+ * Signed 64-bit integer multiply, 2-addr version
+ *
+ * We could definately use more free registers for
+ * this code. We must spill rPC (edx) because it
+ * is used by imul. We'll also spill rINST (ebx),
+ * giving us eax, ebc, ecx and edx as computational
+ * temps. On top of that, we'll spill rIBASE (edi)
+ * for use as the vA pointer and rFP (esi) for use
+ * as the vB pointer. Yuck.
+ */
+ /* mul-long/2addr vA, vB */
+ movzbl rINST_HI,%eax # eax<- BA
+ andb $$0xf,%al # eax<- A
+ sarl $$12,rINST_FULL # rINST_FULL<- B
+ SPILL(rPC)
+ SPILL(rIBASE)
+ SPILL(rFP)
+ leal (rFP,%eax,4),rIBASE # rIBASE<- &v[A]
+ leal (rFP,rINST_FULL,4),rFP # rFP<- &v[B]
+ movl 4(rIBASE),%ecx # ecx<- Amsw
+ imull (rFP),%ecx # ecx<- (Amsw*Blsw)
+ movl 4(rFP),%eax # eax<- Bmsw
+ imull (rIBASE),%eax # eax<- (Bmsw*Alsw)
+ addl %eax,%ecx # ecx<- (Amsw*Blsw)+(Bmsw*Alsw)
+ movl (rFP),%eax # eax<- Blsw
+ mull (rIBASE) # eax<- (Blsw*Alsw)
+ jmp .L${opcode}_continue
+%break
+
+.L${opcode}_continue:
+ leal (%ecx,%edx),%edx # full result now in %edx:%eax
+ movl %edx,4(rIBASE) # v[A+1]<- %edx
+ UNSPILL(rPC) # restore rPC/%edx
+ FETCH_INST_WORD(1)
+ movl %eax,(rIBASE) # v[A]<- %eax
+ UNSPILL(rFP)
+ UNSPILL(rIBASE)
+ ADVANCE_PC(1)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_NEG_DOUBLE.S b/vm/mterp/x86/OP_NEG_DOUBLE.S
new file mode 100644
index 0000000..7b24914
--- /dev/null
+++ b/vm/mterp/x86/OP_NEG_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/fpcvt.S" {"instr":"fchs","load":"fldl","store":"fstpl"}
diff --git a/vm/mterp/x86/OP_NEG_FLOAT.S b/vm/mterp/x86/OP_NEG_FLOAT.S
new file mode 100644
index 0000000..e785155
--- /dev/null
+++ b/vm/mterp/x86/OP_NEG_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/fpcvt.S" {"instr":"fchs","load":"flds","store":"fstps"}
diff --git a/vm/mterp/x86/OP_NEG_INT.S b/vm/mterp/x86/OP_NEG_INT.S
new file mode 100644
index 0000000..4dfd6d3
--- /dev/null
+++ b/vm/mterp/x86/OP_NEG_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/unop.S" {"instr":"negl %eax"}
diff --git a/vm/mterp/x86/OP_NEG_LONG.S b/vm/mterp/x86/OP_NEG_LONG.S
new file mode 100644
index 0000000..18bd275
--- /dev/null
+++ b/vm/mterp/x86/OP_NEG_LONG.S
@@ -0,0 +1,16 @@
+%verify "executed"
+ /* unop vA, vB */
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $$4,%ecx # ecx<- B
+ movzbl rINST_HI,rINST_FULL # ecx<- BA
+ andb $$0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG_WORD(%eax,%ecx,0) # eax<- v[B+0]
+ GET_VREG_WORD(%ecx,%ecx,1) # ecx<- v[B+1]
+ negl %eax
+ adcl $$0,%ecx
+ negl %ecx
+ SET_VREG_WORD(%eax,rINST_FULL,0) # v[A+0]<- eax
+ SET_VREG_WORD(%ecx,rINST_FULL,1) # v[A+1]<- ecx
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_NEW_ARRAY.S b/vm/mterp/x86/OP_NEW_ARRAY.S
new file mode 100644
index 0000000..74d72ed
--- /dev/null
+++ b/vm/mterp/x86/OP_NEW_ARRAY.S
@@ -0,0 +1,72 @@
+%verify "executed"
+%verify "negative array length"
+%verify "allocation fails"
+ /*
+ * Allocate an array of objects, specified with the array class
+ * and a count.
+ *
+ * The verifier guarantees that this is an array class, so we don't
+ * check for it here.
+ */
+ /* new-array vA, vB, class@CCCC */
+ GET_GLUE(%ecx)
+ EXPORT_PC()
+ movl offGlue_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+ movzwl 2(rPC),%eax # eax<- CCCC
+ movl offDvmDex_pResClasses(%ecx),%ecx # ecx<- pDvmDex->pResClasses
+ movl (%ecx,%eax,4),%ecx # ecx<- resolved class
+ movzbl rINST_HI,%eax
+ sarl $$4,%eax # eax<- B
+ GET_VREG(%eax,%eax) # eax<- vB (array length)
+ movzbl rINST_HI,rINST_FULL
+ andb $$0xf,rINST_LO # rINST_FULL<- A
+ testl %eax,%eax
+ js common_errNegativeArraySize # bail
+ testl %ecx,%ecx # already resolved?
+ jne .L${opcode}_finish # yes, fast path
+ jmp .L${opcode}_resolve # resolve now
+%break
+
+ /*
+ * Resolve class. (This is an uncommon case.)
+ * ecx holds class (null here)
+ * eax holds array length (vB)
+ */
+.L${opcode}_resolve:
+ GET_GLUE(%ecx)
+ SPILL_TMP(%eax) # save array length
+ movl offGlue_method(%ecx),%ecx # ecx<- glue->method
+ movzwl 2(rPC),%eax # eax<- CCCC
+ movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+ movl %eax,OUT_ARG1(%esp)
+ movl $$0,OUT_ARG2(%esp)
+ movl %ecx,OUT_ARG0(%esp)
+ SPILL(rPC)
+ call dvmResolveClass # eax<- call(clazz,ref,flag)
+ UNSPILL(rPC)
+ movl %eax,%ecx
+ UNSPILL_TMP(%eax)
+ testl %ecx,%ecx # successful resolution?
+ je common_exceptionThrown # no, bail.
+# fall through to ${opcode}_finish
+
+ /*
+ * Finish allocation
+ *
+ * ecx holds class
+ * eax holds array length (vB)
+ */
+.L${opcode}_finish:
+ movl %ecx,OUT_ARG0(%esp)
+ movl %eax,OUT_ARG1(%esp)
+ movl $$ALLOC_DONT_TRACK,OUT_ARG2(%esp)
+ SPILL(rPC)
+ call dvmAllocArrayByClass # eax<- call(clazz,length,flags)
+ UNSPILL(rPC)
+ testl %eax,%eax # failed?
+ je common_exceptionThrown # yup - go handle
+ movl rINST_FULL,%ecx
+ FETCH_INST_WORD(2)
+ SET_VREG(%eax,%ecx)
+ ADVANCE_PC(2)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_NEW_INSTANCE.S b/vm/mterp/x86/OP_NEW_INSTANCE.S
new file mode 100644
index 0000000..e11e518
--- /dev/null
+++ b/vm/mterp/x86/OP_NEW_INSTANCE.S
@@ -0,0 +1,93 @@
+%verify "executed"
+%verify "class not resolved"
+%verify "class cannot be resolved"
+%verify "class not initialized"
+%verify "class fails to initialize"
+%verify "class already resolved/initialized"
+%verify "class is abstract or interface"
+%verify "allocation fails"
+ /*
+ * Create a new instance of a class.
+ */
+ /* new-instance vAA, class@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- BBBB
+ movl offGlue_methodClassDex(%ecx),%ecx # ecx<- pDvmDex
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ movl offDvmDex_pResClasses(%ecx),%ecx # ecx<- pDvmDex->pResClasses
+ EXPORT_PC()
+ movl (%ecx,%eax,4),%ecx # ecx<- resolved class
+ SPILL(rPC)
+ testl %ecx,%ecx # resolved?
+ je .L${opcode}_resolve # no, go do it
+.L${opcode}_resolved: # on entry, ecx<- class
+ cmpb $$CLASS_INITIALIZED,offClassObject_status(%ecx)
+ je .L${opcode}_initialized
+ jmp .L${opcode}_needinit
+%break
+
+.L${opcode}_initialized: # on entry, ecx<- class
+ /* TODO: remove test for interface/abstract, now done in verifier */
+ testl $$(ACC_INTERFACE|ACC_ABSTRACT),offClassObject_accessFlags(%ecx)
+ movl $$ALLOC_DONT_TRACK,OUT_ARG1(%esp)
+ jne .L${opcode}_abstract
+.L${opcode}_finish: # ecx=class
+ movl %ecx,OUT_ARG0(%esp)
+ call dvmAllocObject # eax<- new object
+ UNSPILL(rPC)
+ movl rINST_FULL,%ecx
+ FETCH_INST_WORD(2)
+ testl %eax,%eax # success?
+ je common_exceptionThrown # no, bail out
+ SET_VREG(%eax,%ecx)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+ /*
+ * Class initialization required.
+ *
+ * ecx holds class object
+ */
+.L${opcode}_needinit:
+ SPILL_TMP(%ecx) # save object
+ movl %ecx,OUT_ARG0(%esp)
+ call dvmInitClass # initialize class
+ UNSPILL_TMP(%ecx) # restore object
+ testl %eax,%eax # success?
+ jne .L${opcode}_initialized # success, continue
+ UNSPILL(rPC) # failed, restore PC
+ jmp common_exceptionThrown # go deal with init exception
+
+ /*
+ * Resolution required. This is the least-likely path.
+ *
+ */
+.L${opcode}_resolve:
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax
+ movl offGlue_method(%ecx),%ecx # ecx<- glue->method
+ movl %eax,OUT_ARG1(%esp)
+ movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+ movl $$0,OUT_ARG2(%esp)
+ movl %ecx,OUT_ARG0(%esp)
+ call dvmResolveClass # call(clazz,off,flags)
+ movl %eax,%ecx # ecx<- resolved ClassObject ptr
+ testl %ecx,%ecx # success?
+ jne .L${opcode}_resolved # good to go
+ UNSPILL(rPC)
+ jmp common_exceptionThrown # no, handle exception
+
+ /*
+ * TODO: remove this
+ * We can't instantiate an abstract class or interface, so throw an
+ * InstantiationError with the class descriptor as the message.
+ *
+ * ecx holds class object
+ */
+.L${opcode}_abstract:
+ movl offClassObject_descriptor(%ecx),%eax
+ movl $$.LstrInstantiationError,OUT_ARG0(%esp)
+ movl %eax,OUT_ARG1(%esp)
+ call dvmThrowExceptionWithClassMessage
+ UNSPILL(rPC)
+ jmp common_exceptionThrown
diff --git a/vm/mterp/x86/OP_NOP.S b/vm/mterp/x86/OP_NOP.S
new file mode 100644
index 0000000..17e3589
--- /dev/null
+++ b/vm/mterp/x86/OP_NOP.S
@@ -0,0 +1,4 @@
+%verify "executed"
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_NOT_INT.S b/vm/mterp/x86/OP_NOT_INT.S
new file mode 100644
index 0000000..c910716
--- /dev/null
+++ b/vm/mterp/x86/OP_NOT_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/unop.S" {"instr":"notl %eax"}
diff --git a/vm/mterp/x86/OP_NOT_LONG.S b/vm/mterp/x86/OP_NOT_LONG.S
new file mode 100644
index 0000000..3eca120
--- /dev/null
+++ b/vm/mterp/x86/OP_NOT_LONG.S
@@ -0,0 +1,15 @@
+%verify "executed"
+ /* unop vA, vB */
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $$4,%ecx # ecx<- B
+ movzbl rINST_HI,rINST_FULL # ecx<- BA
+ andb $$0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG_WORD(%eax,%ecx,0) # eax<- v[B+0]
+ GET_VREG_WORD(%ecx,%ecx,1) # ecx<- v[B+1]
+ notl %eax
+ notl %ecx
+ SET_VREG_WORD(%eax,rINST_FULL,0) # v[A+0]<- eax
+ SET_VREG_WORD(%ecx,rINST_FULL,1) # v[A+1]<- ecx
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_OR_INT.S b/vm/mterp/x86/OP_OR_INT.S
new file mode 100644
index 0000000..9453bfd
--- /dev/null
+++ b/vm/mterp/x86/OP_OR_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop.S" {"instr":"orl (rFP,%ecx,4),%eax"}
diff --git a/vm/mterp/x86/OP_OR_INT_2ADDR.S b/vm/mterp/x86/OP_OR_INT_2ADDR.S
new file mode 100644
index 0000000..db69633
--- /dev/null
+++ b/vm/mterp/x86/OP_OR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop2addr.S" {"instr":"orl %eax,(rFP,%ecx,4)"}
diff --git a/vm/mterp/x86/OP_OR_INT_LIT16.S b/vm/mterp/x86/OP_OR_INT_LIT16.S
new file mode 100644
index 0000000..fa70e99
--- /dev/null
+++ b/vm/mterp/x86/OP_OR_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit16.S" {"instr":"orl %ecx,%eax"}
diff --git a/vm/mterp/x86/OP_OR_INT_LIT8.S b/vm/mterp/x86/OP_OR_INT_LIT8.S
new file mode 100644
index 0000000..5761806
--- /dev/null
+++ b/vm/mterp/x86/OP_OR_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit8.S" {"instr":"orl %ecx,%eax"}
diff --git a/vm/mterp/x86/OP_OR_LONG.S b/vm/mterp/x86/OP_OR_LONG.S
new file mode 100644
index 0000000..b14555b
--- /dev/null
+++ b/vm/mterp/x86/OP_OR_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopWide.S" {"instr1":"orl (rFP,%ecx,4),rPC", "instr2":"orl 4(rFP,%ecx,4),%eax"}
diff --git a/vm/mterp/x86/OP_OR_LONG_2ADDR.S b/vm/mterp/x86/OP_OR_LONG_2ADDR.S
new file mode 100644
index 0000000..d1e78b2
--- /dev/null
+++ b/vm/mterp/x86/OP_OR_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopWide2addr.S" {"instr1":"orl %eax,(rFP,rINST_FULL,4)","instr2":"orl %ecx,4(rFP,rINST_FULL,4)"}
diff --git a/vm/mterp/x86/OP_PACKED_SWITCH.S b/vm/mterp/x86/OP_PACKED_SWITCH.S
new file mode 100644
index 0000000..089fc55
--- /dev/null
+++ b/vm/mterp/x86/OP_PACKED_SWITCH.S
@@ -0,0 +1,27 @@
+%default { "func":"dvmInterpHandlePackedSwitch" }
+%verify executed
+ /*
+ * Handle a packed-switch or sparse-switch instruction. In both cases
+ * we decode it and hand it off to a helper function.
+ *
+ * We don't really expect backward branches in a switch statement, but
+ * they're perfectly legal, so we check for them here.
+ *
+ * for: packed-switch, sparse-switch
+ */
+ /* op vAA, +BBBB */
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ movl 2(rPC),%ecx # ecx<- BBBBbbbb
+ GET_VREG(%eax,rINST_FULL) # eax<- vAA
+ leal (rPC,%ecx,2),%ecx # ecx<- PC + BBBBbbbb*2
+ movl %eax,OUT_ARG1(%esp) # ARG1<- vAA
+ movl %ecx,OUT_ARG0(%esp) # ARG0<- switchData
+ SPILL(rPC)
+ call $func
+ UNSPILL(rPC)
+ testl %eax,%eax
+ movl %eax,rINST_FULL # set up word offset
+ jle common_backwardBranch # check on special actions
+ ADVANCE_PC_INDEXED(rINST_FULL)
+ FETCH_INST()
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_REM_DOUBLE.S b/vm/mterp/x86/OP_REM_DOUBLE.S
new file mode 100644
index 0000000..809ac0a
--- /dev/null
+++ b/vm/mterp/x86/OP_REM_DOUBLE.S
@@ -0,0 +1,17 @@
+%verify "executed"
+ /* rem_float vAA, vBB, vCC */
+ movzbl 3(rPC),%ecx # ecx<- BB
+ movzbl 2(rPC),%eax # eax<- CC
+ fldl (rFP,%ecx,4) # vCC to fp stack
+ fldl (rFP,%eax,4) # vCC to fp stack
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(2)
+1:
+ fprem
+ fstsw %ax
+ sahf
+ jp 1b
+ fstp %st(1)
+ ADVANCE_PC(2)
+ fstpl (rFP,%ecx,4) # %st to vAA
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_REM_DOUBLE_2ADDR.S b/vm/mterp/x86/OP_REM_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..b199e6e
--- /dev/null
+++ b/vm/mterp/x86/OP_REM_DOUBLE_2ADDR.S
@@ -0,0 +1,17 @@
+%verify "executed"
+ /* rem_float/2addr vA, vB */
+ movzx rINST_HI,%ecx # ecx<- A+
+ sarl $$12,rINST_FULL # rINST_FULL<- B
+ fldl (rFP,rINST_FULL,4) # vBB to fp stack
+ andb $$0xf,%cl # ecx<- A
+ fldl (rFP,%ecx,4) # vAA to fp stack
+ FETCH_INST_WORD(1)
+1:
+ fprem
+ fstsw %ax
+ sahf
+ jp 1b
+ fstp %st(1)
+ ADVANCE_PC(1)
+ fstpl (rFP,%ecx,4) # %st to vA
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_REM_FLOAT.S b/vm/mterp/x86/OP_REM_FLOAT.S
new file mode 100644
index 0000000..d78bc9a
--- /dev/null
+++ b/vm/mterp/x86/OP_REM_FLOAT.S
@@ -0,0 +1,17 @@
+%verify "executed"
+ /* rem_float vAA, vBB, vCC */
+ movzbl 3(rPC),%ecx # ecx<- BB
+ movzbl 2(rPC),%eax # eax<- CC
+ flds (rFP,%ecx,4) # vCC to fp stack
+ flds (rFP,%eax,4) # vCC to fp stack
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(2)
+1:
+ fprem
+ fstsw %ax
+ sahf
+ jp 1b
+ fstp %st(1)
+ ADVANCE_PC(2)
+ fstps (rFP,%ecx,4) # %st to vAA
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_REM_FLOAT_2ADDR.S b/vm/mterp/x86/OP_REM_FLOAT_2ADDR.S
new file mode 100644
index 0000000..fd1742b
--- /dev/null
+++ b/vm/mterp/x86/OP_REM_FLOAT_2ADDR.S
@@ -0,0 +1,17 @@
+%verify "executed"
+ /* rem_float/2addr vA, vB */
+ movzx rINST_HI,%ecx # ecx<- A+
+ sarl $$12,rINST_FULL # rINST_FULL<- B
+ flds (rFP,rINST_FULL,4) # vBB to fp stack
+ andb $$0xf,%cl # ecx<- A
+ flds (rFP,%ecx,4) # vAA to fp stack
+ FETCH_INST_WORD(1)
+1:
+ fprem
+ fstsw %ax
+ sahf
+ jp 1b
+ fstp %st(1)
+ ADVANCE_PC(1)
+ fstps (rFP,%ecx,4) # %st to vA
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_REM_INT.S b/vm/mterp/x86/OP_REM_INT.S
new file mode 100644
index 0000000..601b383
--- /dev/null
+++ b/vm/mterp/x86/OP_REM_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bindiv.S" {"result":"%edx","special":"$0"}
diff --git a/vm/mterp/x86/OP_REM_INT_2ADDR.S b/vm/mterp/x86/OP_REM_INT_2ADDR.S
new file mode 100644
index 0000000..cfb60bd
--- /dev/null
+++ b/vm/mterp/x86/OP_REM_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bindiv2addr.S" {"result":"%edx","special":"$0"}
diff --git a/vm/mterp/x86/OP_REM_INT_LIT16.S b/vm/mterp/x86/OP_REM_INT_LIT16.S
new file mode 100644
index 0000000..a5fdb1e
--- /dev/null
+++ b/vm/mterp/x86/OP_REM_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bindivLit16.S" {"result":"%edx","special":"$0"}
diff --git a/vm/mterp/x86/OP_REM_INT_LIT8.S b/vm/mterp/x86/OP_REM_INT_LIT8.S
new file mode 100644
index 0000000..9d06fcd
--- /dev/null
+++ b/vm/mterp/x86/OP_REM_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/bindivLit8.S" {"result":"%edx","special":"$0"}
diff --git a/vm/mterp/x86/OP_REM_LONG.S b/vm/mterp/x86/OP_REM_LONG.S
new file mode 100644
index 0000000..ad8091c
--- /dev/null
+++ b/vm/mterp/x86/OP_REM_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_DIV_LONG.S" {"routine":"__moddi3","special":"$0"}
diff --git a/vm/mterp/x86/OP_REM_LONG_2ADDR.S b/vm/mterp/x86/OP_REM_LONG_2ADDR.S
new file mode 100644
index 0000000..f6e6d61
--- /dev/null
+++ b/vm/mterp/x86/OP_REM_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_DIV_LONG_2ADDR.S" {"routine":"__moddi3","special":"$0"}
diff --git a/vm/mterp/x86/OP_RETURN.S b/vm/mterp/x86/OP_RETURN.S
new file mode 100644
index 0000000..5d6a9a0
--- /dev/null
+++ b/vm/mterp/x86/OP_RETURN.S
@@ -0,0 +1,13 @@
+%verify "executed"
+ /*
+ * Return a 32-bit value. Copies the return value into the "glue"
+ * structure, then jumps to the return handler.
+ *
+ * for: return, return-object
+ */
+ /* op vAA */
+ GET_GLUE(%ecx)
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG(%eax,rINST_FULL) # eax<- vAA
+ movl %eax,offGlue_retval(%ecx) # retval.i <- AA
+ jmp common_returnFromMethod
diff --git a/vm/mterp/x86/OP_RETURN_OBJECT.S b/vm/mterp/x86/OP_RETURN_OBJECT.S
new file mode 100644
index 0000000..1a2b83e
--- /dev/null
+++ b/vm/mterp/x86/OP_RETURN_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_RETURN.S"
diff --git a/vm/mterp/x86/OP_RETURN_VOID.S b/vm/mterp/x86/OP_RETURN_VOID.S
new file mode 100644
index 0000000..4d4291f
--- /dev/null
+++ b/vm/mterp/x86/OP_RETURN_VOID.S
@@ -0,0 +1,2 @@
+%verify "executed"
+ jmp common_returnFromMethod
diff --git a/vm/mterp/x86/OP_RETURN_WIDE.S b/vm/mterp/x86/OP_RETURN_WIDE.S
new file mode 100644
index 0000000..a2fd636
--- /dev/null
+++ b/vm/mterp/x86/OP_RETURN_WIDE.S
@@ -0,0 +1,13 @@
+%verify "executed"
+ /*
+ * Return a 64-bit value. Copies the return value into the "glue"
+ * structure, then jumps to the return handler.
+ */
+ /* return-wide vAA */
+ GET_GLUE(%ecx)
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG_WORD(%eax,rINST_FULL,0) # eax<- v[AA+0]
+ GET_VREG_WORD(rINST_FULL,rINST_FULL,1) # rINST_FULL<- v[AA+1]
+ movl %eax,offGlue_retval(%ecx)
+ movl rINST_FULL,4+offGlue_retval(%ecx)
+ jmp common_returnFromMethod
diff --git a/vm/mterp/x86/OP_RSUB_INT.S b/vm/mterp/x86/OP_RSUB_INT.S
new file mode 100644
index 0000000..fa9b410
--- /dev/null
+++ b/vm/mterp/x86/OP_RSUB_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit16.S" {"instr":"subl %eax,%ecx","result":"%ecx"}
diff --git a/vm/mterp/x86/OP_RSUB_INT_LIT8.S b/vm/mterp/x86/OP_RSUB_INT_LIT8.S
new file mode 100644
index 0000000..81f49a7
--- /dev/null
+++ b/vm/mterp/x86/OP_RSUB_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit8.S" {"instr":"subl %eax,%ecx" , "result":"%ecx"}
diff --git a/vm/mterp/x86/OP_SGET.S b/vm/mterp/x86/OP_SGET.S
new file mode 100644
index 0000000..f05eae7
--- /dev/null
+++ b/vm/mterp/x86/OP_SGET.S
@@ -0,0 +1,43 @@
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+ /*
+ * General 32-bit SGET handler.
+ *
+ * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short
+ */
+ /* op vAA, field@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_methodClassDex(%ecx),%ecx # ecx<- DvmDex
+ movl offDvmDex_pResFields(%ecx),%ecx # ecx<- dvmDex->pResFields
+ movl (%ecx,%eax,4),%eax # eax<- resolved StaticField ptr
+ testl %eax,%eax # resolved entry null?
+ je .L${opcode}_resolve # if not, make it so
+.L${opcode}_finish: # field ptr in eax
+ movl offStaticField_value(%eax),%eax
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ SET_VREG(%eax,%ecx)
+ GOTO_NEXT
+%break
+
+ /*
+ * Go resolve the field
+ */
+.L${opcode}_resolve:
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_method(%ecx),%ecx # ecx<- current method
+ EXPORT_PC() # could throw, need to export
+ movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+ SPILL(rPC)
+ movl %eax,OUT_ARG1(%esp)
+ movl %ecx,OUT_ARG0(%esp)
+ call dvmResolveStaticField # eax<- resolved StaticField ptr
+ UNSPILL(rPC)
+ testl %eax,%eax
+ jne .L${opcode}_finish # success, continue
+ jmp common_exceptionThrown # no, handle exception
diff --git a/vm/mterp/x86/OP_SGET_BOOLEAN.S b/vm/mterp/x86/OP_SGET_BOOLEAN.S
new file mode 100644
index 0000000..3b3a492
--- /dev/null
+++ b/vm/mterp/x86/OP_SGET_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SGET.S"
diff --git a/vm/mterp/x86/OP_SGET_BYTE.S b/vm/mterp/x86/OP_SGET_BYTE.S
new file mode 100644
index 0000000..3b3a492
--- /dev/null
+++ b/vm/mterp/x86/OP_SGET_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SGET.S"
diff --git a/vm/mterp/x86/OP_SGET_CHAR.S b/vm/mterp/x86/OP_SGET_CHAR.S
new file mode 100644
index 0000000..3b3a492
--- /dev/null
+++ b/vm/mterp/x86/OP_SGET_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SGET.S"
diff --git a/vm/mterp/x86/OP_SGET_OBJECT.S b/vm/mterp/x86/OP_SGET_OBJECT.S
new file mode 100644
index 0000000..3b3a492
--- /dev/null
+++ b/vm/mterp/x86/OP_SGET_OBJECT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SGET.S"
diff --git a/vm/mterp/x86/OP_SGET_OBJECT_VOLATILE.S b/vm/mterp/x86/OP_SGET_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..3b3a492
--- /dev/null
+++ b/vm/mterp/x86/OP_SGET_OBJECT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SGET.S"
diff --git a/vm/mterp/x86/OP_SGET_SHORT.S b/vm/mterp/x86/OP_SGET_SHORT.S
new file mode 100644
index 0000000..3b3a492
--- /dev/null
+++ b/vm/mterp/x86/OP_SGET_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SGET.S"
diff --git a/vm/mterp/x86/OP_SGET_VOLATILE.S b/vm/mterp/x86/OP_SGET_VOLATILE.S
new file mode 100644
index 0000000..3b3a492
--- /dev/null
+++ b/vm/mterp/x86/OP_SGET_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SGET.S"
diff --git a/vm/mterp/x86/OP_SGET_WIDE.S b/vm/mterp/x86/OP_SGET_WIDE.S
new file mode 100644
index 0000000..5c038a7
--- /dev/null
+++ b/vm/mterp/x86/OP_SGET_WIDE.S
@@ -0,0 +1,44 @@
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+ /*
+ * 64-bit SGET handler.
+ *
+ */
+ /* sget-wide vAA, field@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_methodClassDex(%ecx),%ecx # ecx<- DvmDex
+ movl offDvmDex_pResFields(%ecx),%ecx # ecx<- dvmDex->pResFields
+ movl (%ecx,%eax,4),%eax # eax<- resolved StaticField ptr
+ testl %eax,%eax # resolved entry null?
+ je .L${opcode}_resolve # if not, make it so
+.L${opcode}_finish: # field ptr in eax
+ movl offStaticField_value(%eax),%ecx # ecx<- lsw
+ movl 4+offStaticField_value(%eax),%eax # eax<- msw
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ SET_VREG_WORD(%ecx,rINST_FULL,0)
+ SET_VREG_WORD(%eax,rINST_FULL,1)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+%break
+
+ /*
+ * Go resolve the field
+ */
+.L${opcode}_resolve:
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_method(%ecx),%ecx # ecx<- current method
+ EXPORT_PC() # could throw, need to export
+ movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+ SPILL(rPC)
+ movl %eax,OUT_ARG1(%esp)
+ movl %ecx,OUT_ARG0(%esp)
+ call dvmResolveStaticField # eax<- resolved StaticField ptr
+ UNSPILL(rPC)
+ testl %eax,%eax
+ jne .L${opcode}_finish # success, continue
+ jmp common_exceptionThrown # no, handle exception
diff --git a/vm/mterp/x86/OP_SHL_INT.S b/vm/mterp/x86/OP_SHL_INT.S
new file mode 100644
index 0000000..a72a272
--- /dev/null
+++ b/vm/mterp/x86/OP_SHL_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop1.S" {"instr":"sall %cl,%eax"}
diff --git a/vm/mterp/x86/OP_SHL_INT_2ADDR.S b/vm/mterp/x86/OP_SHL_INT_2ADDR.S
new file mode 100644
index 0000000..c8e1dfe
--- /dev/null
+++ b/vm/mterp/x86/OP_SHL_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/shop2addr.S" {"instr":"sall %cl,%eax"}
diff --git a/vm/mterp/x86/OP_SHL_INT_LIT8.S b/vm/mterp/x86/OP_SHL_INT_LIT8.S
new file mode 100644
index 0000000..8c7f007
--- /dev/null
+++ b/vm/mterp/x86/OP_SHL_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit8.S" {"instr":"sall %cl,%eax"}
diff --git a/vm/mterp/x86/OP_SHL_LONG.S b/vm/mterp/x86/OP_SHL_LONG.S
new file mode 100644
index 0000000..2d06200
--- /dev/null
+++ b/vm/mterp/x86/OP_SHL_LONG.S
@@ -0,0 +1,37 @@
+%verify "executed"
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to mask off the low
+ * 6 bits of the shift distance. x86 shifts automatically mask off
+ * the low 5 bits of %cl, so have to handle the 64 > shiftcount > 31
+ * case specially.
+ */
+ /* shl-long vAA, vBB, vCC */
+ /* ecx gets shift count */
+ /* Need to spill edx */
+ /* rINST gets AA */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ SPILL(rPC) # spill edx
+ GET_VREG_WORD(%edx,%eax,1) # ecx<- v[BB+1]
+ GET_VREG (%ecx,%ecx) # ecx<- vCC
+ GET_VREG_WORD(%eax,%eax,0) # eax<- v[BB+0]
+ shldl %eax,%edx
+ sall %cl,%eax
+ testb $$32,%cl
+ je 2f
+ movl %eax,%edx
+ xorl %eax,%eax
+2:
+ movzbl rINST_HI,%ecx
+ SET_VREG_WORD(%edx,%ecx,1) # v[AA+1]<- %edx
+ UNSPILL(rPC)
+ FETCH_INST_WORD(2)
+ jmp .L${opcode}_finish
+%break
+
+.L${opcode}_finish:
+ SET_VREG_WORD(%eax,%ecx,0) # v[AA+0]<- %eax
+ ADVANCE_PC(2)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_SHL_LONG_2ADDR.S b/vm/mterp/x86/OP_SHL_LONG_2ADDR.S
new file mode 100644
index 0000000..b98ed92
--- /dev/null
+++ b/vm/mterp/x86/OP_SHL_LONG_2ADDR.S
@@ -0,0 +1,35 @@
+%verify "executed"
+ /*
+ * Long integer shift, 2addr version. vA is 64-bit value/result, vB is
+ * 32-bit shift distance.
+ */
+ /* shl-long/2addr vA, vB */
+ /* ecx gets shift count */
+ /* Need to spill edx */
+ /* rINST gets AA */
+ movzbl rINST_HI,%ecx # ecx<- BA
+ movzbl rINST_HI,rINST_FULL # rINST_HI<- BA
+ andb $$0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG_WORD(%eax,rINST_FULL,0) # eax<- v[AA+0]
+ sarl $$4,%ecx # ecx<- B
+ SPILL(rPC)
+ GET_VREG_WORD(%edx,rINST_FULL,1) # edx<- v[AA+1]
+ GET_VREG(%ecx,%ecx) # ecx<- vBB
+ shldl %eax,%edx
+ sall %cl,%eax
+ testb $$32,%cl
+ je 2f
+ movl %eax,%edx
+ xorl %eax,%eax
+2:
+ SET_VREG_WORD(%edx,rINST_FULL,1) # v[AA+1]<- edx
+ UNSPILL(rPC)
+ jmp .L${opcode}_finish
+%break
+
+
+.L${opcode}_finish:
+ SET_VREG_WORD(%eax,rINST_FULL,0) # v[AA+0]<- eax
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_SHR_INT.S b/vm/mterp/x86/OP_SHR_INT.S
new file mode 100644
index 0000000..febc429
--- /dev/null
+++ b/vm/mterp/x86/OP_SHR_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop1.S" {"instr":"sarl %cl,%eax"}
diff --git a/vm/mterp/x86/OP_SHR_INT_2ADDR.S b/vm/mterp/x86/OP_SHR_INT_2ADDR.S
new file mode 100644
index 0000000..89c6625
--- /dev/null
+++ b/vm/mterp/x86/OP_SHR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/shop2addr.S" {"instr":"sarl %cl,%eax"}
diff --git a/vm/mterp/x86/OP_SHR_INT_LIT8.S b/vm/mterp/x86/OP_SHR_INT_LIT8.S
new file mode 100644
index 0000000..77ddd23
--- /dev/null
+++ b/vm/mterp/x86/OP_SHR_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit8.S" {"instr":"sarl %cl,%eax"}
diff --git a/vm/mterp/x86/OP_SHR_LONG.S b/vm/mterp/x86/OP_SHR_LONG.S
new file mode 100644
index 0000000..bccae8e
--- /dev/null
+++ b/vm/mterp/x86/OP_SHR_LONG.S
@@ -0,0 +1,38 @@
+%verify "executed"
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to mask off the low
+ * 6 bits of the shift distance. x86 shifts automatically mask off
+ * the low 5 bits of %cl, so have to handle the 64 > shiftcount > 31
+ * case specially.
+ */
+ /* shr-long vAA, vBB, vCC */
+ /* ecx gets shift count */
+ /* Need to spill edx */
+ /* rINST gets AA */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ SPILL(rPC) # spill edx
+ GET_VREG_WORD(%edx,%eax,1) # edx<- v[BB+1]
+ GET_VREG (%ecx,%ecx) # ecx<- vCC
+ GET_VREG_WORD(%eax,%eax,0) # eax<- v[BB+0]
+ shrdl %edx,%eax
+ sarl %cl,%edx
+ testb $$32,%cl
+ je 2f
+ movl %edx,%eax
+ sarl $$31,%edx
+2:
+ movzbl rINST_HI,%ecx
+ SET_VREG_WORD(%edx,%ecx,1) # v[AA+1]<- edx
+ UNSPILL(rPC)
+ FETCH_INST_WORD(2)
+ jmp .L${opcode}_finish
+%break
+
+
+.L${opcode}_finish:
+ SET_VREG_WORD(%eax,%ecx,0) # v[AA+0]<- eax
+ ADVANCE_PC(2)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_SHR_LONG_2ADDR.S b/vm/mterp/x86/OP_SHR_LONG_2ADDR.S
new file mode 100644
index 0000000..fba1f25
--- /dev/null
+++ b/vm/mterp/x86/OP_SHR_LONG_2ADDR.S
@@ -0,0 +1,35 @@
+%verify "executed"
+ /*
+ * Long integer shift, 2addr version. vA is 64-bit value/result, vB is
+ * 32-bit shift distance.
+ */
+ /* shl-long/2addr vA, vB */
+ /* ecx gets shift count */
+ /* Need to spill edx */
+ /* rINST gets AA */
+ movzbl rINST_HI,%ecx # ecx<- BA
+ movzbl rINST_HI,rINST_FULL # rINST_HI<- BA
+ andb $$0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG_WORD(%eax,rINST_FULL,0) # eax<- v[AA+0]
+ sarl $$4,%ecx # ecx<- B
+ SPILL(rPC)
+ GET_VREG_WORD(%edx,rINST_FULL,1) # edx<- v[AA+1]
+ GET_VREG(%ecx,%ecx) # ecx<- vBB
+ shrdl %edx,%eax
+ sarl %cl,%edx
+ testb $$32,%cl
+ je 2f
+ movl %edx,%eax
+ sarl $$31,%edx
+2:
+ SET_VREG_WORD(%edx,rINST_FULL,1) # v[AA+1]<- edx
+ UNSPILL(rPC)
+ jmp .L${opcode}_finish
+%break
+
+
+.L${opcode}_finish:
+ SET_VREG_WORD(%eax,rINST_FULL,0) # v[AA+0]<- eax
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_SPARSE_SWITCH.S b/vm/mterp/x86/OP_SPARSE_SWITCH.S
new file mode 100644
index 0000000..4cedd40
--- /dev/null
+++ b/vm/mterp/x86/OP_SPARSE_SWITCH.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_PACKED_SWITCH.S" { "func":"dvmInterpHandleSparseSwitch" }
diff --git a/vm/mterp/x86/OP_SPUT.S b/vm/mterp/x86/OP_SPUT.S
new file mode 100644
index 0000000..440dcfb
--- /dev/null
+++ b/vm/mterp/x86/OP_SPUT.S
@@ -0,0 +1,43 @@
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_methodClassDex(%ecx),%ecx # ecx<- DvmDex
+ movl offDvmDex_pResFields(%ecx),%ecx # ecx<- dvmDex->pResFields
+ movl (%ecx,%eax,4),%eax # eax<- resolved StaticField ptr
+ testl %eax,%eax # resolved entry null?
+ je .L${opcode}_resolve # if not, make it so
+.L${opcode}_finish: # field ptr in eax
+ movzbl rINST_HI,%ecx # ecx<- AA
+ GET_VREG(%ecx,%ecx)
+ FETCH_INST_WORD(2)
+ movl %ecx,offStaticField_value(%eax)
+ ADVANCE_PC(2)
+ GOTO_NEXT
+%break
+
+ /*
+ * Go resolve the field
+ */
+.L${opcode}_resolve:
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_method(%ecx),%ecx # ecx<- current method
+ EXPORT_PC() # could throw, need to export
+ movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+ SPILL(rPC)
+ movl %eax,OUT_ARG1(%esp)
+ movl %ecx,OUT_ARG0(%esp)
+ call dvmResolveStaticField # eax<- resolved StaticField ptr
+ UNSPILL(rPC)
+ testl %eax,%eax
+ jne .L${opcode}_finish # success, continue
+ jmp common_exceptionThrown # no, handle exception
diff --git a/vm/mterp/x86/OP_SPUT_BOOLEAN.S b/vm/mterp/x86/OP_SPUT_BOOLEAN.S
new file mode 100644
index 0000000..ec0489f
--- /dev/null
+++ b/vm/mterp/x86/OP_SPUT_BOOLEAN.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SPUT.S"
diff --git a/vm/mterp/x86/OP_SPUT_BYTE.S b/vm/mterp/x86/OP_SPUT_BYTE.S
new file mode 100644
index 0000000..ec0489f
--- /dev/null
+++ b/vm/mterp/x86/OP_SPUT_BYTE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SPUT.S"
diff --git a/vm/mterp/x86/OP_SPUT_CHAR.S b/vm/mterp/x86/OP_SPUT_CHAR.S
new file mode 100644
index 0000000..ec0489f
--- /dev/null
+++ b/vm/mterp/x86/OP_SPUT_CHAR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SPUT.S"
diff --git a/vm/mterp/x86/OP_SPUT_OBJECT.S b/vm/mterp/x86/OP_SPUT_OBJECT.S
new file mode 100644
index 0000000..13fa860
--- /dev/null
+++ b/vm/mterp/x86/OP_SPUT_OBJECT.S
@@ -0,0 +1,50 @@
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+ /*
+ * SPUT object handler.
+ */
+ /* op vAA, field@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_methodClassDex(%ecx),%ecx # ecx<- DvmDex
+ movl offDvmDex_pResFields(%ecx),%ecx # ecx<- dvmDex->pResFields
+ movl (%ecx,%eax,4),%eax # eax<- resolved StaticField
+ testl %eax,%eax # resolved entry null?
+ je .L${opcode}_resolve # if not, make it so
+.L${opcode}_finish: # field ptr in eax
+ movzbl rINST_HI,%ecx # ecx<- AA
+ GET_VREG(%ecx,%ecx)
+ jmp .L${opcode}_continue
+%break
+
+
+.L${opcode}_continue:
+ movl %ecx,offStaticField_value(%eax) # do the store
+ testl %ecx,%ecx # stored null object ptr?
+ FETCH_INST_WORD(2)
+ je 1f # skip card mark if null
+ GET_GLUE(%ecx)
+ movl offField_clazz(%eax),%eax # eax<- field->clazz
+ movl offGlue_cardTable(%ecx),%ecx # get card table base
+ shrl $$GC_CARD_SHIFT,%eax # head to card number
+ movb %cl,(%ecx,%eax) # mark card
+1:
+ ADVANCE_PC(2)
+ GOTO_NEXT
+
+.L${opcode}_resolve:
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_method(%ecx),%ecx # ecx<- current method
+ EXPORT_PC() # could throw, need to export
+ movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+ SPILL(rPC)
+ movl %eax,OUT_ARG1(%esp)
+ movl %ecx,OUT_ARG0(%esp)
+ call dvmResolveStaticField # eax<- resolved StaticField ptr
+ UNSPILL(rPC)
+ testl %eax,%eax
+ jne .L${opcode}_finish # success, continue
+ jmp common_exceptionThrown # no, handle exception
diff --git a/vm/mterp/x86/OP_SPUT_OBJECT_VOLATILE.S b/vm/mterp/x86/OP_SPUT_OBJECT_VOLATILE.S
new file mode 100644
index 0000000..afc6668
--- /dev/null
+++ b/vm/mterp/x86/OP_SPUT_OBJECT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SPUT_OBJECT.S"
diff --git a/vm/mterp/x86/OP_SPUT_SHORT.S b/vm/mterp/x86/OP_SPUT_SHORT.S
new file mode 100644
index 0000000..ec0489f
--- /dev/null
+++ b/vm/mterp/x86/OP_SPUT_SHORT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SPUT.S"
diff --git a/vm/mterp/x86/OP_SPUT_VOLATILE.S b/vm/mterp/x86/OP_SPUT_VOLATILE.S
new file mode 100644
index 0000000..ec0489f
--- /dev/null
+++ b/vm/mterp/x86/OP_SPUT_VOLATILE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/OP_SPUT.S"
diff --git a/vm/mterp/x86/OP_SPUT_WIDE.S b/vm/mterp/x86/OP_SPUT_WIDE.S
new file mode 100644
index 0000000..5a48c2e
--- /dev/null
+++ b/vm/mterp/x86/OP_SPUT_WIDE.S
@@ -0,0 +1,45 @@
+%verify "executed"
+%verify "field already resolved"
+%verify "field not yet resolved"
+%verify "field cannot be resolved"
+ /*
+ * General 32-bit SPUT handler.
+ *
+ * for: sput, sput-object, sput-boolean, sput-byte, sput-char, sput-short
+ */
+ /* op vAA, field@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_methodClassDex(%ecx),%ecx # ecx<- DvmDex
+ movl offDvmDex_pResFields(%ecx),%ecx # ecx<- dvmDex->pResFields
+ movl (%ecx,%eax,4),%eax # eax<- resolved StaticField ptr
+ testl %eax,%eax # resolved entry null?
+ je .L${opcode}_resolve # if not, make it so
+.L${opcode}_finish: # field ptr in eax
+ movzbl rINST_HI,%ecx # ecx<- AA
+ GET_VREG_WORD(rINST_FULL,%ecx,0) # rINST_FULL<- lsw
+ GET_VREG_WORD(%ecx,%ecx,1) # ecx<- msw
+ movl rINST_FULL,offStaticField_value(%eax)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ movl %ecx,4+offStaticField_value(%eax)
+ GOTO_NEXT
+%break
+
+ /*
+ * Go resolve the field
+ */
+.L${opcode}_resolve:
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- field ref BBBB
+ movl offGlue_method(%ecx),%ecx # ecx<- current method
+ EXPORT_PC() # could throw, need to export
+ movl offMethod_clazz(%ecx),%ecx # ecx<- method->clazz
+ SPILL(rPC)
+ movl %eax,OUT_ARG1(%esp)
+ movl %ecx,OUT_ARG0(%esp)
+ call dvmResolveStaticField # eax<- resolved StaticField ptr
+ UNSPILL(rPC)
+ testl %eax,%eax
+ jne .L${opcode}_finish # success, continue
+ jmp common_exceptionThrown # no, handle exception
diff --git a/vm/mterp/x86/OP_SUB_DOUBLE.S b/vm/mterp/x86/OP_SUB_DOUBLE.S
new file mode 100644
index 0000000..224f3a1
--- /dev/null
+++ b/vm/mterp/x86/OP_SUB_DOUBLE.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop.S" {"instr":"fsubl","load":"fldl","store":"fstpl"}
diff --git a/vm/mterp/x86/OP_SUB_DOUBLE_2ADDR.S b/vm/mterp/x86/OP_SUB_DOUBLE_2ADDR.S
new file mode 100644
index 0000000..991c380
--- /dev/null
+++ b/vm/mterp/x86/OP_SUB_DOUBLE_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop2addr.S" {"instr":"fsubl","load":"fldl","store":"fstpl"}
diff --git a/vm/mterp/x86/OP_SUB_FLOAT.S b/vm/mterp/x86/OP_SUB_FLOAT.S
new file mode 100644
index 0000000..99d11c6
--- /dev/null
+++ b/vm/mterp/x86/OP_SUB_FLOAT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop.S" {"instr":"fsubs","load":"flds","store":"fstps"}
diff --git a/vm/mterp/x86/OP_SUB_FLOAT_2ADDR.S b/vm/mterp/x86/OP_SUB_FLOAT_2ADDR.S
new file mode 100644
index 0000000..013334a
--- /dev/null
+++ b/vm/mterp/x86/OP_SUB_FLOAT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binflop2addr.S" {"instr":"fsubs","load":"flds","store":"fstps"}
diff --git a/vm/mterp/x86/OP_SUB_INT.S b/vm/mterp/x86/OP_SUB_INT.S
new file mode 100644
index 0000000..04fcf21
--- /dev/null
+++ b/vm/mterp/x86/OP_SUB_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop.S" {"instr":"subl (rFP,%ecx,4),%eax"}
diff --git a/vm/mterp/x86/OP_SUB_INT_2ADDR.S b/vm/mterp/x86/OP_SUB_INT_2ADDR.S
new file mode 100644
index 0000000..0f63b86
--- /dev/null
+++ b/vm/mterp/x86/OP_SUB_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop2addr.S" {"instr":"subl %eax,(rFP,%ecx,4)"}
diff --git a/vm/mterp/x86/OP_SUB_LONG.S b/vm/mterp/x86/OP_SUB_LONG.S
new file mode 100644
index 0000000..6eda7bb
--- /dev/null
+++ b/vm/mterp/x86/OP_SUB_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopWide.S" {"instr1":"subl (rFP,%ecx,4),rPC", "instr2":"sbbl 4(rFP,%ecx,4),%eax"}
diff --git a/vm/mterp/x86/OP_SUB_LONG_2ADDR.S b/vm/mterp/x86/OP_SUB_LONG_2ADDR.S
new file mode 100644
index 0000000..94bf0d6
--- /dev/null
+++ b/vm/mterp/x86/OP_SUB_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopWide2addr.S" {"instr1":"subl %eax,(rFP,rINST_FULL,4)","instr2":"sbbl %ecx,4(rFP,rINST_FULL,4)"}
diff --git a/vm/mterp/x86/OP_THROW.S b/vm/mterp/x86/OP_THROW.S
new file mode 100644
index 0000000..d7e1574
--- /dev/null
+++ b/vm/mterp/x86/OP_THROW.S
@@ -0,0 +1,15 @@
+%verify "executed"
+%verify "exception for null object"
+ /*
+ * Throw an exception object in the current thread.
+ */
+ /* throw vAA */
+ GET_GLUE(%ecx)
+ EXPORT_PC()
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG(%eax,rINST_FULL) # eax<- exception object
+ movl offGlue_self(%ecx),%ecx # ecx<- glue->self
+ testl %eax,%eax # null object?
+ je common_errNullObject
+ movl %eax,offThread_exception(%ecx) # thread->exception<- obj
+ jmp common_exceptionThrown
diff --git a/vm/mterp/x86/OP_THROW_VERIFICATION_ERROR.S b/vm/mterp/x86/OP_THROW_VERIFICATION_ERROR.S
new file mode 100644
index 0000000..e492e2d
--- /dev/null
+++ b/vm/mterp/x86/OP_THROW_VERIFICATION_ERROR.S
@@ -0,0 +1,19 @@
+%verify executed
+ /*
+ * Handle a throw-verification-error instruction. This throws an
+ * exception for an error discovered during verification. The
+ * exception is indicated by AA, with some detail provided by BBBB.
+ */
+ /* op AA, ref@BBBB */
+ GET_GLUE(%ecx)
+ movzwl 2(rPC),%eax # eax<- BBBB
+ movl offGlue_method(%ecx),%ecx # ecx<- glue->method
+ EXPORT_PC()
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ movl %eax,OUT_ARG2(%esp) # arg2<- BBBB
+ movl rINST_FULL,OUT_ARG1(%esp) # arg1<- AA
+ movl %ecx,OUT_ARG0(%esp) # arg0<- method
+ SPILL(rPC)
+ call dvmThrowVerificationError # call(method, kind, ref)
+ UNSPILL(rPC)
+ jmp common_exceptionThrown # handle exception
diff --git a/vm/mterp/x86/OP_UNUSED_3E.S b/vm/mterp/x86/OP_UNUSED_3E.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_3E.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_3F.S b/vm/mterp/x86/OP_UNUSED_3F.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_3F.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_40.S b/vm/mterp/x86/OP_UNUSED_40.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_40.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_41.S b/vm/mterp/x86/OP_UNUSED_41.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_41.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_42.S b/vm/mterp/x86/OP_UNUSED_42.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_42.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_43.S b/vm/mterp/x86/OP_UNUSED_43.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_43.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_73.S b/vm/mterp/x86/OP_UNUSED_73.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_73.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_79.S b/vm/mterp/x86/OP_UNUSED_79.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_79.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_7A.S b/vm/mterp/x86/OP_UNUSED_7A.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_7A.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_E3.S b/vm/mterp/x86/OP_UNUSED_E3.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_E3.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_E4.S b/vm/mterp/x86/OP_UNUSED_E4.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_E4.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_E5.S b/vm/mterp/x86/OP_UNUSED_E5.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_E5.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_E6.S b/vm/mterp/x86/OP_UNUSED_E6.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_E6.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_E7.S b/vm/mterp/x86/OP_UNUSED_E7.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_E7.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_F1.S b/vm/mterp/x86/OP_UNUSED_F1.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_F1.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_FC.S b/vm/mterp/x86/OP_UNUSED_FC.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_FC.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_FD.S b/vm/mterp/x86/OP_UNUSED_FD.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_FD.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_FE.S b/vm/mterp/x86/OP_UNUSED_FE.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_FE.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_UNUSED_FF.S b/vm/mterp/x86/OP_UNUSED_FF.S
new file mode 100644
index 0000000..31d98c1
--- /dev/null
+++ b/vm/mterp/x86/OP_UNUSED_FF.S
@@ -0,0 +1 @@
+%include "x86/unused.S"
diff --git a/vm/mterp/x86/OP_USHR_INT.S b/vm/mterp/x86/OP_USHR_INT.S
new file mode 100644
index 0000000..04b9210
--- /dev/null
+++ b/vm/mterp/x86/OP_USHR_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop1.S" {"instr":"shrl %cl,%eax"}
diff --git a/vm/mterp/x86/OP_USHR_INT_2ADDR.S b/vm/mterp/x86/OP_USHR_INT_2ADDR.S
new file mode 100644
index 0000000..02a94ff
--- /dev/null
+++ b/vm/mterp/x86/OP_USHR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/shop2addr.S" {"instr":"shrl %cl,%eax"}
diff --git a/vm/mterp/x86/OP_USHR_INT_LIT8.S b/vm/mterp/x86/OP_USHR_INT_LIT8.S
new file mode 100644
index 0000000..24fd087
--- /dev/null
+++ b/vm/mterp/x86/OP_USHR_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit8.S" {"instr":"shrl %cl,%eax"}
diff --git a/vm/mterp/x86/OP_USHR_LONG.S b/vm/mterp/x86/OP_USHR_LONG.S
new file mode 100644
index 0000000..5e4c89b
--- /dev/null
+++ b/vm/mterp/x86/OP_USHR_LONG.S
@@ -0,0 +1,38 @@
+%verify "executed"
+ /*
+ * Long integer shift. This is different from the generic 32/64-bit
+ * binary operations because vAA/vBB are 64-bit but vCC (the shift
+ * distance) is 32-bit. Also, Dalvik requires us to mask off the low
+ * 6 bits of the shift distance. x86 shifts automatically mask off
+ * the low 5 bits of %cl, so have to handle the 64 > shiftcount > 31
+ * case specially.
+ */
+ /* shr-long vAA, vBB, vCC */
+ /* ecx gets shift count */
+ /* Need to spill edx */
+ /* rINST gets AA */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ SPILL(rPC) # spill edx
+ GET_VREG_WORD(%edx,%eax,1) # edx<- v[BB+1]
+ GET_VREG (%ecx,%ecx) # ecx<- vCC
+ GET_VREG_WORD(%eax,%eax,0) # eax<- v[BB+0]
+ shrdl %edx,%eax
+ shrl %cl,%edx
+ testb $$32,%cl
+ je 2f
+ movl %edx,%eax
+ xorl %edx,%edx
+2:
+ movzbl rINST_HI,%ecx
+ SET_VREG_WORD(%edx,%ecx,1) # v[BB+1]<- edx
+ UNSPILL(rPC)
+ jmp .L${opcode}_finish
+%break
+
+
+.L${opcode}_finish:
+ SET_VREG_WORD(%eax,%ecx,0) # v[BB+0]<- eax
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_USHR_LONG_2ADDR.S b/vm/mterp/x86/OP_USHR_LONG_2ADDR.S
new file mode 100644
index 0000000..b2555e9
--- /dev/null
+++ b/vm/mterp/x86/OP_USHR_LONG_2ADDR.S
@@ -0,0 +1,35 @@
+%verify "executed"
+ /*
+ * Long integer shift, 2addr version. vA is 64-bit value/result, vB is
+ * 32-bit shift distance.
+ */
+ /* shl-long/2addr vA, vB */
+ /* ecx gets shift count */
+ /* Need to spill edx */
+ /* rINST gets AA */
+ movzbl rINST_HI,%ecx # ecx<- BA
+ movzbl rINST_HI,rINST_FULL # rINST_HI<- BA
+ andb $$0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG_WORD(%eax,rINST_FULL,0) # eax<- v[AA+0]
+ sarl $$4,%ecx # ecx<- B
+ SPILL(rPC)
+ GET_VREG_WORD(%edx,rINST_FULL,1) # edx<- v[AA+1]
+ GET_VREG(%ecx,%ecx) # ecx<- vBB
+ shrdl %edx,%eax
+ shrl %cl,%edx
+ testb $$32,%cl
+ je 2f
+ movl %edx,%eax
+ xorl %edx,%edx
+2:
+ SET_VREG_WORD(%edx,rINST_FULL,1) # v[AA+1]<- edx
+ UNSPILL(rPC)
+ jmp .L${opcode}_finish
+%break
+
+
+.L${opcode}_finish:
+ SET_VREG_WORD(%eax,rINST_FULL,0) # v[AA+0]<- eax
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/OP_XOR_INT.S b/vm/mterp/x86/OP_XOR_INT.S
new file mode 100644
index 0000000..71e4013
--- /dev/null
+++ b/vm/mterp/x86/OP_XOR_INT.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop.S" {"instr":"xorl (rFP,%ecx,4),%eax"}
diff --git a/vm/mterp/x86/OP_XOR_INT_2ADDR.S b/vm/mterp/x86/OP_XOR_INT_2ADDR.S
new file mode 100644
index 0000000..50880cf
--- /dev/null
+++ b/vm/mterp/x86/OP_XOR_INT_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binop2addr.S" {"instr":"xorl %eax,(rFP,%ecx,4)"}
diff --git a/vm/mterp/x86/OP_XOR_INT_LIT16.S b/vm/mterp/x86/OP_XOR_INT_LIT16.S
new file mode 100644
index 0000000..e62e4df
--- /dev/null
+++ b/vm/mterp/x86/OP_XOR_INT_LIT16.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit16.S" {"instr":"xor %ecx,%eax"}
diff --git a/vm/mterp/x86/OP_XOR_INT_LIT8.S b/vm/mterp/x86/OP_XOR_INT_LIT8.S
new file mode 100644
index 0000000..9ccca3f
--- /dev/null
+++ b/vm/mterp/x86/OP_XOR_INT_LIT8.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopLit8.S" {"instr":"xor %ecx,%eax"}
diff --git a/vm/mterp/x86/OP_XOR_LONG.S b/vm/mterp/x86/OP_XOR_LONG.S
new file mode 100644
index 0000000..ebeb126
--- /dev/null
+++ b/vm/mterp/x86/OP_XOR_LONG.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopWide.S" {"instr1":"xorl (rFP,%ecx,4),rPC", "instr2":"xorl 4(rFP,%ecx,4),%eax"}
diff --git a/vm/mterp/x86/OP_XOR_LONG_2ADDR.S b/vm/mterp/x86/OP_XOR_LONG_2ADDR.S
new file mode 100644
index 0000000..2b127b1
--- /dev/null
+++ b/vm/mterp/x86/OP_XOR_LONG_2ADDR.S
@@ -0,0 +1,2 @@
+%verify "executed"
+%include "x86/binopWide2addr.S" {"instr1":"xorl %eax,(rFP,rINST_FULL,4)","instr2":"xorl %ecx,4(rFP,rINST_FULL,4)"}
diff --git a/vm/mterp/x86/bincmp.S b/vm/mterp/x86/bincmp.S
new file mode 100644
index 0000000..26956b4
--- /dev/null
+++ b/vm/mterp/x86/bincmp.S
@@ -0,0 +1,26 @@
+%verify "branch taken"
+%verify "branch not taken"
+ /*
+ * Generic two-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
+ */
+ /* if-cmp vA, vB, +CCCC */
+ movzx rINST_HI,%ecx # ecx <- A+
+ andb $$0xf,%cl # ecx <- A
+ GET_VREG(%eax,%ecx) # eax <- vA
+ sarl $$12,rINST_FULL # rINST_FULL<- B
+ cmpl (rFP,rINST_FULL,4),%eax # compare (vA, vB)
+ movswl 2(rPC),rINST_FULL # Get signed branch offset
+ movl $$2,%eax # assume not taken
+ j${revcmp} 1f
+ testl rINST_FULL,rINST_FULL
+ js common_backwardBranch
+ movl rINST_FULL,%eax
+1:
+ FETCH_INST_INDEXED(%eax)
+ ADVANCE_PC_INDEXED(%eax)
+ GOTO_NEXT
+ GOTO_NEXT
diff --git a/vm/mterp/x86/bindiv.S b/vm/mterp/x86/bindiv.S
new file mode 100644
index 0000000..6aca1a4
--- /dev/null
+++ b/vm/mterp/x86/bindiv.S
@@ -0,0 +1,32 @@
+
+%default {"result":"","special":""}
+ /*
+ * 32-bit binary div/rem operation. Handles special case of op0=minint and
+ * op1=-1.
+ */
+ /* binop vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ GET_VREG(%eax,%eax) # eax<- vBB
+ GET_VREG(%ecx,%ecx) # eax<- vBB
+ SPILL(rPC)
+ cmpl $$0,%ecx
+ je common_errDivideByZero
+ cmpl $$-1,%ecx
+ jne .L${opcode}_continue_div
+ cmpl $$0x80000000,%eax
+ jne .L${opcode}_continue_div
+ movl $special,$result
+ jmp .L${opcode}_finish_div
+
+%break
+.L${opcode}_continue_div:
+ cltd
+ idivl %ecx
+.L${opcode}_finish_div:
+ movzbl rINST_HI,%ecx # ecl<- AA
+ SET_VREG($result,%ecx)
+ UNSPILL(rPC)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/bindiv2addr.S b/vm/mterp/x86/bindiv2addr.S
new file mode 100644
index 0000000..efa76b5
--- /dev/null
+++ b/vm/mterp/x86/bindiv2addr.S
@@ -0,0 +1,32 @@
+%default {"result":"","special":""}
+ /*
+ * 32-bit binary div/rem operation. Handles special case of op0=minint and
+ * op1=-1.
+ */
+ /* div/rem/2addr vA, vB */
+ movzx rINST_HI,%ecx # eax<- BA
+ sarl $$4,%ecx # ecx<- B
+ GET_VREG(%ecx,%ecx) # eax<- vBB
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $$0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG(%eax,rINST_FULL) # eax<- vBB
+ SPILL(rPC)
+ cmpl $$0,%ecx
+ je common_errDivideByZero
+ cmpl $$-1,%ecx
+ jne .L${opcode}_continue_div2addr
+ cmpl $$0x80000000,%eax
+ jne .L${opcode}_continue_div2addr
+ movl $special,$result
+ jmp .L${opcode}_finish_div2addr
+
+%break
+.L${opcode}_continue_div2addr:
+ cltd
+ idivl %ecx
+.L${opcode}_finish_div2addr:
+ SET_VREG($result,rINST_FULL)
+ UNSPILL(rPC)
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/bindivLit16.S b/vm/mterp/x86/bindivLit16.S
new file mode 100644
index 0000000..3806830
--- /dev/null
+++ b/vm/mterp/x86/bindivLit16.S
@@ -0,0 +1,33 @@
+%default {"result":"","special":""}
+ /*
+ * 32-bit binary div/rem operation. Handles special case of op0=minint and
+ * op1=-1.
+ */
+ /* div/rem/lit16 vA, vB, #+CCCC */
+ /* Need A in rINST_FULL, ssssCCCC in ecx, vB in eax */
+ movzbl rINST_HI,%eax # eax<- 000000BA
+ sarl $$4,%eax # eax<- B
+ GET_VREG(%eax,%eax) # eax<- vB
+ movswl 2(rPC),%ecx # ecx<- ssssCCCC
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $$0xf,rINST_LO # rINST_FULL<- A
+ SPILL(rPC)
+ cmpl $$0,%ecx
+ je common_errDivideByZero
+ cmpl $$-1,%ecx
+ jne .L${opcode}_continue_div
+ cmpl $$0x80000000,%eax
+ jne .L${opcode}_continue_div
+ movl $special,$result
+ jmp .L${opcode}_finish_div
+
+%break
+.L${opcode}_continue_div:
+ cltd
+ idivl %ecx
+.L${opcode}_finish_div:
+ SET_VREG($result,rINST_FULL)
+ UNSPILL(rPC)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/bindivLit8.S b/vm/mterp/x86/bindivLit8.S
new file mode 100644
index 0000000..6d616ba
--- /dev/null
+++ b/vm/mterp/x86/bindivLit8.S
@@ -0,0 +1,30 @@
+%default {"result":"","special":""}
+ /*
+ * 32-bit div/rem "lit8" binary operation. Handles special case of
+ * op0=minint & op1=-1
+ */
+ /* div/rem/lit8 vAA, vBB, #+CC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movsbl 3(rPC),%ecx # ecx<- ssssssCC
+ GET_VREG (%eax,%eax) # eax<- rBB
+ movzx rINST_HI,rINST_FULL # rINST_FULL<- AA
+ SPILL(rPC)
+ cmpl $$0,%ecx
+ je common_errDivideByZero
+ cmpl $$0x80000000,%eax
+ jne .L${opcode}_continue_div
+ cmpl $$-1,%ecx
+ jne .L${opcode}_continue_div
+ movl $special,$result
+ jmp .L${opcode}_finish_div
+
+%break
+.L${opcode}_continue_div:
+ cltd
+ idivl %ecx
+.L${opcode}_finish_div:
+ SET_VREG($result,rINST_FULL)
+ UNSPILL(rPC)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/binflop.S b/vm/mterp/x86/binflop.S
new file mode 100644
index 0000000..233799c
--- /dev/null
+++ b/vm/mterp/x86/binflop.S
@@ -0,0 +1,15 @@
+ /*
+ * Generic 32-bit binary float operation.
+ *
+ * For: add-fp, sub-fp, mul-fp, div-fp
+ */
+ /* binop vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- CC
+ movzbl 3(rPC),%ecx # ecx<- BB
+ $load (rFP,%eax,4) # vCC to fp stack
+ $instr (rFP,%ecx,4) # ex: faddp
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ $store (rFP,%ecx,4) # %st to vAA
+ GOTO_NEXT
diff --git a/vm/mterp/x86/binflop2addr.S b/vm/mterp/x86/binflop2addr.S
new file mode 100644
index 0000000..4b78e11
--- /dev/null
+++ b/vm/mterp/x86/binflop2addr.S
@@ -0,0 +1,16 @@
+ /*
+ * Generic 32-bit binary float operation.
+ *
+ * For: add-fp, sub-fp, mul-fp, div-fp
+ */
+
+ /* binop/2addr vA, vB */
+ movzx rINST_HI,%ecx # ecx<- A+
+ andb $$0xf,%cl # ecx<- A
+ $load (rFP,%ecx,4) # vAA to fp stack
+ sarl $$12,rINST_FULL # rINST_FULL<- B
+ $instr (rFP,rINST_FULL,4) # ex: faddp
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ $store (rFP,%ecx,4) # %st to vA
+ GOTO_NEXT
diff --git a/vm/mterp/x86/binop.S b/vm/mterp/x86/binop.S
new file mode 100644
index 0000000..00d9118
--- /dev/null
+++ b/vm/mterp/x86/binop.S
@@ -0,0 +1,20 @@
+%default {"result":"%eax"}
+ /*
+ * Generic 32-bit binary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = eax op (rFP,%ecx,4)".
+ * This could be an x86 instruction or a function call. (If the result
+ * comes back in a register other than eax, you can override "result".)
+ *
+ * For: add-int, sub-int, and-int, or-int,
+ * xor-int, shl-int, shr-int, ushr-int
+ */
+ /* binop vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ GET_VREG(%eax,%eax) # eax<- vBB
+ $instr # ex: addl (rFP,%ecx,4),%eax
+ movzbl rINST_HI,%ecx # ecx<- AA
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ SET_VREG($result,%ecx)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/binop1.S b/vm/mterp/x86/binop1.S
new file mode 100644
index 0000000..e932103
--- /dev/null
+++ b/vm/mterp/x86/binop1.S
@@ -0,0 +1,16 @@
+%default {"result":"%eax","tmp":"%ecx"}
+ /*
+ * Generic 32-bit binary operation in which both operands loaded to
+ * registers (op0 in eax, op1 in ecx).
+ */
+ /* binop vAA, vBB, vCC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ GET_VREG(%eax,%eax) # eax<- vBB
+ GET_VREG(%ecx,%ecx) # eax<- vBB
+ $instr # ex: addl %ecx,%eax
+ movzbl rINST_HI,$tmp # tmp<- AA
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ SET_VREG($result,$tmp)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/binop2addr.S b/vm/mterp/x86/binop2addr.S
new file mode 100644
index 0000000..0600aa3
--- /dev/null
+++ b/vm/mterp/x86/binop2addr.S
@@ -0,0 +1,24 @@
+%default {"result":"%eax"}
+ /*
+ * Generic 32-bit "/2addr" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = r0 op r1".
+ * This could be an ARM instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * If "chkzero" is set to 1, we perform a divide-by-zero check on
+ * vCC (r1). Useful for integer division and modulus.
+ *
+ * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr,
+ * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr,
+ * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr,
+ * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr
+ */
+ /* binop/2addr vA, vB */
+ movzx rINST_HI,%ecx # ecx<- A+
+ sarl $$12,rINST_FULL # rINST_FULL<- B
+ GET_VREG(%eax,rINST_FULL) # eax<- vB
+ FETCH_INST_WORD(1)
+ andb $$0xf,%cl # ecx<- A
+ $instr # for ex: addl %eax,(rFP,%ecx,4)
+ ADVANCE_PC(1)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/binopLit16.S b/vm/mterp/x86/binopLit16.S
new file mode 100644
index 0000000..762068f
--- /dev/null
+++ b/vm/mterp/x86/binopLit16.S
@@ -0,0 +1,22 @@
+%default {"result":"%eax"}
+ /*
+ * Generic 32-bit "lit16" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = eax op ecx".
+ * This could be an x86 instruction or a function call. (If the result
+ * comes back in a register other than eax, you can override "result".)
+ *
+ * For: add-int/lit16, rsub-int,
+ * and-int/lit16, or-int/lit16, xor-int/lit16
+ */
+ /* binop/lit16 vA, vB, #+CCCC */
+ movzbl rINST_HI,%eax # eax<- 000000BA
+ sarl $$4,%eax # eax<- B
+ GET_VREG(%eax,%eax) # eax<- vB
+ movswl 2(rPC),%ecx # ecx<- ssssCCCC
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $$0xf,rINST_LO # rINST_FULL<- A
+ $instr # for example: addl %ecx, %eax
+ SET_VREG($result,rINST_FULL)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/binopLit8.S b/vm/mterp/x86/binopLit8.S
new file mode 100644
index 0000000..73a146c
--- /dev/null
+++ b/vm/mterp/x86/binopLit8.S
@@ -0,0 +1,21 @@
+%default {"result":"%eax"}
+ /*
+ * Generic 32-bit "lit8" binary operation. Provide an "instr" line
+ * that specifies an instruction that performs "result = eax op ecx".
+ * This could be an x86 instruction or a function call. (If the result
+ * comes back in a register other than r0, you can override "result".)
+ *
+ * For: add-int/lit8, rsub-int/lit8
+ * and-int/lit8, or-int/lit8, xor-int/lit8,
+ * shl-int/lit8, shr-int/lit8, ushr-int/lit8
+ */
+ /* binop/lit8 vAA, vBB, #+CC */
+ movzbl 2(rPC),%eax # eax<- BB
+ movsbl 3(rPC),%ecx # ecx<- ssssssCC
+ movzx rINST_HI,rINST_FULL # rINST_FULL<- AA
+ GET_VREG (%eax,%eax) # eax<- rBB
+ $instr # ex: addl %ecx,%eax
+ SET_VREG ($result,rINST_FULL)
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/binopWide.S b/vm/mterp/x86/binopWide.S
new file mode 100644
index 0000000..592e45f
--- /dev/null
+++ b/vm/mterp/x86/binopWide.S
@@ -0,0 +1,19 @@
+ /*
+ * Generic 64-bit binary operation.
+ */
+ /* binop vAA, vBB, vCC */
+
+ movzbl 2(rPC),%eax # eax<- BB
+ movzbl 3(rPC),%ecx # ecx<- CC
+ SPILL(rPC)
+ GET_VREG_WORD(rPC,%eax,0) # rPC<- v[BB+0]
+ GET_VREG_WORD(%eax,%eax,1) # eax<- v[BB+1]
+ $instr1 # ex: addl (rFP,%ecx,4),rPC
+ $instr2 # ex: adcl 4(rFP,%ecx,4),%eax
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- AA
+ SET_VREG_WORD(rPC,rINST_FULL,0) # v[AA+0] <- rPC
+ UNSPILL(rPC)
+ SET_VREG_WORD(%eax,rINST_FULL,1) # v[AA+1] <- eax
+ FETCH_INST_WORD(2)
+ ADVANCE_PC(2)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/binopWide2addr.S b/vm/mterp/x86/binopWide2addr.S
new file mode 100644
index 0000000..5d378bf
--- /dev/null
+++ b/vm/mterp/x86/binopWide2addr.S
@@ -0,0 +1,15 @@
+ /*
+ * Generic 64-bit binary operation.
+ */
+ /* binop/2addr vA, vB */
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $$4,%ecx # ecx<- B
+ GET_VREG_WORD(%eax,%ecx,0) # eax<- v[B+0]
+ GET_VREG_WORD(%ecx,%ecx,1) # eax<- v[B+1]
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $$0xF,rINST_LO # rINST_FULL<- A
+ $instr1 # example: addl %eax,(rFP,rINST_FULL,4)
+ $instr2 # example: adcl %ecx,4(rFP,rINST_FULL,4)
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/cvtfp_int.S b/vm/mterp/x86/cvtfp_int.S
new file mode 100644
index 0000000..f4f36b8
--- /dev/null
+++ b/vm/mterp/x86/cvtfp_int.S
@@ -0,0 +1,63 @@
+%default {"srcdouble":"1","tgtlong":"1"}
+/* On fp to int conversions, Java requires that
+ * if the result > maxint, it should be clamped to maxint. If it is less
+ * than minint, it should be clamped to minint. If it is a nan, the result
+ * should be zero. Further, the rounding mode is to truncate. This model
+ * differs from what is delivered normally via the x86 fpu, so we have
+ * to play some games.
+ */
+ /* float/double to int/long vA, vB */
+ movzbl rINST_HI,%ecx # ecx<- A+
+ sarl $$12,rINST_FULL # rINST_FULL<- B
+ .if $srcdouble
+ fldl (rFP,rINST_FULL,4) # %st0<- vB
+ .else
+ flds (rFP,rINST_FULL,4) # %st0<- vB
+ .endif
+ ftst
+ fnstcw LOCAL0_OFFSET(%ebp) # remember original rounding mode
+ movzwl LOCAL0_OFFSET(%ebp),%eax
+ movb $$0xc,%ah
+ movw %ax,LOCAL0_OFFSET+2(%ebp)
+ fldcw LOCAL0_OFFSET+2(%ebp) # set "to zero" rounding mode
+ FETCH_INST_WORD(1)
+ andb $$0xf,%cl # ecx<- A
+ .if $tgtlong
+ fistpll (rFP,%ecx,4) # convert and store
+ .else
+ fistpl (rFP,%ecx,4) # convert and store
+ .endif
+ fldcw LOCAL0_OFFSET(%ebp) # restore previous rounding mode
+ jmp .L${opcode}_continue
+%break
+
+
+.L${opcode}_continue:
+ .if $tgtlong
+ movl $$0x80000000,%eax
+ xorl 4(rFP,%ecx,4),%eax
+ orl (rFP,%ecx,4),%eax
+ .else
+ cmpl $$0x80000000,(rFP,%ecx,4)
+ .endif
+ je .L${opcode}_special_case # fix up result
+
+.L${opcode}_finish:
+ ADVANCE_PC(1)
+ GOTO_NEXT
+
+.L${opcode}_special_case:
+ fnstsw %ax
+ sahf
+ jp .L${opcode}_isNaN
+ adcl $$-1,(rFP,%ecx,4)
+ .if $tgtlong
+ adcl $$-1,4(rFP,%ecx,4)
+ .endif
+ jmp .L${opcode}_finish
+.L${opcode}_isNaN:
+ movl $$0,(rFP,%ecx,4)
+ .if $tgtlong
+ movl $$0,4(rFP,%ecx,4)
+ .endif
+ jmp .L${opcode}_finish
diff --git a/vm/mterp/x86/entry.S b/vm/mterp/x86/entry.S
new file mode 100644
index 0000000..ff310fe
--- /dev/null
+++ b/vm/mterp/x86/entry.S
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+ .text
+ .global dvmMterpStdRun
+ .type dvmMterpStdRun, %function
+/*
+ * bool dvmMterpStdRun(MterpGlue* glue)
+ *
+ * Interpreter entry point. Returns changeInterp.
+ *
+ */
+dvmMterpStdRun:
+ push %ebp
+ movl %esp,%ebp
+ push %edi
+ push %esi
+ push %ebx
+
+/* at this point, stack is misaligned by 1 word
+ We're allocating spill space for 6 words, plus
+ outgoing argument (5 words) and local variables
+ (4 words) - 15 words or 60 bytes total. See
+ diagram in header.S
+*/
+ subl $$60,%esp
+
+/* Set up "named" registers */
+ movl IN_ARG0(%ebp),%ecx
+ movl %ecx,rGLUE_SPILL(%ebp)
+ LOAD_PC_FROM_GLUE(%ecx)
+ LOAD_FP_FROM_GLUE(%ecx)
+ movl $$dvmAsmInstructionStart,rIBASE
+
+/* Remember %esp for future "longjmp" */
+ movl %esp,offGlue_bailPtr(%ecx)
+
+/* How to start? */
+ movb offGlue_entryPoint(%ecx),%al
+
+/* Normal start? */
+ cmpb $$kInterpEntryInstr,%al
+ jne .Lnot_instr
+
+ /* Normal case: start executing the instruction at rPC */
+ FETCH_INST()
+ GOTO_NEXT
+
+.Lnot_instr:
+ /* Reset to normal case */
+ movb $$kInterpEntryInstr,offGlue_entryPoint(%ecx)
+ cmpb $$kInterpEntryReturn,%al
+ je common_returnFromMethod
+ cmpb $$kInterpEntryThrow,%al
+ je common_exceptionThrown
+ movzx %al,%eax
+ movl %eax,OUT_ARG1(%esp)
+ movl $$.LstrBadEntryPoint,OUT_ARG0(%esp)
+ call printf
+ call dvmAbort
+ /* Not reached */
+
+
+ .global dvmMterpStdBail
+ .type dvmMterpStdBail, %function
+/*
+ * void dvmMterpStdBail(MterpGlue* glue, bool changeInterp)
+ *
+ * Restore the stack pointer and PC from the save point established on entry.
+ * This is essentially the same as a longjmp, but should be cheaper. The
+ * last instruction causes us to return to whoever called dvmMterpStdRun.
+ *
+ * We're not going to build a standard frame here, so the arg accesses will
+ * look a little strange.
+ *
+ * On entry:
+ * esp+4 (arg0) MterpGlue* glue
+ * esp+8 (arg1) bool changeInterp
+ */
+dvmMterpStdBail:
+ movl 4(%esp),%ecx # grab glue
+ movl 8(%esp),%eax # changeInterp to return reg
+ movl offGlue_bailPtr(%ecx),%esp # Stack back to normal
+ addl $$60,%esp # Strip dvmMterpStdRun's frame
+ pop %ebx
+ pop %esi
+ pop %edi
+ pop %ebp
+ ret # return to dvmMterpStdRun's caller
+
+
+/*
+ * Strings
+ */
+ .section .rodata
+.LstrBadEntryPoint:
+ .asciz "Bad entry point %d\n"
diff --git a/vm/mterp/x86/footer.S b/vm/mterp/x86/footer.S
new file mode 100644
index 0000000..d43a662
--- /dev/null
+++ b/vm/mterp/x86/footer.S
@@ -0,0 +1,535 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Common subroutines and data.
+ */
+
+/*
+ * Common code when a backwards branch is taken
+ *
+ * On entry:
+ * ebx (a.k.a. rINST_FULL) -> PC adjustment in 16-bit words
+ */
+common_backwardBranch:
+ GET_GLUE(%ecx)
+ call common_periodicChecks # Note: expects rPC to be preserved
+ ADVANCE_PC_INDEXED(rINST_FULL)
+ FETCH_INST()
+ GOTO_NEXT
+
+
+
+/*
+ * Common code for method invocation with range.
+ *
+ * On entry:
+ * eax = Method* methodToCall
+ * rINST trashed, must reload
+ */
+
+common_invokeMethodRange:
+.LinvokeNewRange:
+
+ /*
+ * prepare to copy args to "outs" area of current frame
+ */
+
+ movzbl 1(rPC),rINST_FULL # rINST_FULL<- AA
+ movzwl 4(rPC), %ecx # %ecx<- CCCC
+ SPILL(rPC)
+ SAVEAREA_FROM_FP(%edx,rFP) # %edx<- &StackSaveArea
+ test rINST_FULL, rINST_FULL
+ movl rINST_FULL, LOCAL0_OFFSET(%ebp) # LOCAL0_OFFSET(%ebp)<- AA
+ jz .LinvokeArgsDone # no args; jump to args done
+
+
+ /*
+ * %eax=methodToCall, %ecx=CCCC, LOCAL0_OFFSET(%ebp)=count, %edx=&outs (&stackSaveArea)
+ * (very few methods have > 10 args; could unroll for common cases)
+ */
+
+ movl %ebx, LOCAL1_OFFSET(%ebp) # LOCAL1_OFFSET(%ebp)<- save %ebx
+ lea (rFP, %ecx, 4), %ecx # %ecx<- &vCCCC
+ shll $$2, LOCAL0_OFFSET(%ebp) # LOCAL0_OFFSET(%ebp)<- offset
+ subl LOCAL0_OFFSET(%ebp), %edx # %edx<- update &outs
+ shrl $$2, LOCAL0_OFFSET(%ebp) # LOCAL0_OFFSET(%ebp)<- offset
+1:
+ movl (%ecx), %ebx # %ebx<- vCCCC
+ lea 4(%ecx), %ecx # %ecx<- &vCCCC++
+ subl $$1, LOCAL0_OFFSET(%ebp) # LOCAL0_OFFSET<- LOCAL0_OFFSET--
+ movl %ebx, (%edx) # *outs<- vCCCC
+ lea 4(%edx), %edx # outs++
+ jne 1b # loop if count (LOCAL0_OFFSET(%ebp)) not zero
+ movl LOCAL1_OFFSET(%ebp), %ebx # %ebx<- restore %ebx
+ jmp .LinvokeArgsDone # continue
+
+ /*
+ * %eax is "Method* methodToCall", the method we're trying to call
+ * prepare to copy args to "outs" area of current frame
+ */
+
+common_invokeMethodNoRange:
+.LinvokeNewNoRange:
+ movzbl 1(rPC),rINST_FULL # rINST_FULL<- BA
+ SPILL(rPC)
+ movl rINST_FULL, LOCAL0_OFFSET(%ebp) # LOCAL0_OFFSET(%ebp)<- BA
+ shrl $$4, LOCAL0_OFFSET(%ebp) # LOCAL0_OFFSET(%ebp)<- B
+ je .LinvokeArgsDone # no args; jump to args done
+ movzwl 4(rPC), %ecx # %ecx<- GFED
+ SAVEAREA_FROM_FP(%edx,rFP) # %edx<- &StackSaveArea
+
+ /*
+ * %eax=methodToCall, %ecx=GFED, LOCAL0_OFFSET(%ebp)=count, %edx=outs
+ */
+
+.LinvokeNonRange:
+ cmp $$2, LOCAL0_OFFSET(%ebp) # compare LOCAL0_OFFSET(%ebp) to 2
+ movl %ecx, LOCAL1_OFFSET(%ebp) # LOCAL1_OFFSET(%ebp)<- GFED
+ jl 1f # handle 1 arg
+ je 2f # handle 2 args
+ cmp $$4, LOCAL0_OFFSET(%ebp) # compare LOCAL0_OFFSET(%ebp) to 4
+ jl 3f # handle 3 args
+ je 4f # handle 4 args
+5:
+ andl $$15, rINST_FULL # rINST<- A
+ lea -4(%edx), %edx # %edx<- update &outs; &outs--
+ movl (rFP, rINST_FULL, 4), %ecx # %ecx<- vA
+ movl %ecx, (%edx) # *outs<- vA
+ movl LOCAL1_OFFSET(%ebp), %ecx # %ecx<- GFED
+4:
+ shr $$12, %ecx # %ecx<- G
+ lea -4(%edx), %edx # %edx<- update &outs; &outs--
+ movl (rFP, %ecx, 4), %ecx # %ecx<- vG
+ movl %ecx, (%edx) # *outs<- vG
+ movl LOCAL1_OFFSET(%ebp), %ecx # %ecx<- GFED
+3:
+ and $$0x0f00, %ecx # %ecx<- 0F00
+ shr $$8, %ecx # %ecx<- F
+ lea -4(%edx), %edx # %edx<- update &outs; &outs--
+ movl (rFP, %ecx, 4), %ecx # %ecx<- vF
+ movl %ecx, (%edx) # *outs<- vF
+ movl LOCAL1_OFFSET(%ebp), %ecx # %ecx<- GFED
+2:
+ and $$0x00f0, %ecx # %ecx<- 00E0
+ shr $$4, %ecx # %ecx<- E
+ lea -4(%edx), %edx # %edx<- update &outs; &outs--
+ movl (rFP, %ecx, 4), %ecx # %ecx<- vE
+ movl %ecx, (%edx) # *outs<- vE
+ movl LOCAL1_OFFSET(%ebp), %ecx # %ecx<- GFED
+1:
+ and $$0x000f, %ecx # %ecx<- 000D
+ movl (rFP, %ecx, 4), %ecx # %ecx<- vD
+ movl %ecx, -4(%edx) # *--outs<- vD
+0:
+
+ /*
+ * %eax is "Method* methodToCall", the method we're trying to call
+ * find space for the new stack frame, check for overflow
+ */
+
+.LinvokeArgsDone:
+ movzwl offMethod_registersSize(%eax), %edx # %edx<- methodToCall->regsSize
+ movzwl offMethod_outsSize(%eax), %ecx # %ecx<- methodToCall->outsSize
+ movl %eax, LOCAL0_OFFSET(%ebp) # LOCAL0_OFFSET<- methodToCall
+ shl $$2, %edx # %edx<- update offset
+ SAVEAREA_FROM_FP(%eax,rFP) # %eax<- &StackSaveArea
+ subl %edx, %eax # %eax<- newFP; (old savearea - regsSize)
+ GET_GLUE(%edx) # %edx<- pMterpGlue
+ movl %eax, LOCAL1_OFFSET(%ebp) # LOCAL1_OFFSET(%ebp)<- &outs
+ subl $$sizeofStackSaveArea, %eax # %eax<- newSaveArea (stack save area using newFP)
+ movl offGlue_interpStackEnd(%edx), %edx # %edx<- glue->interpStackEnd
+ movl %edx, LOCAL2_OFFSET(%ebp) # LOCAL2_OFFSET<- glue->interpStackEnd
+ shl $$2, %ecx # %ecx<- update offset for outsSize
+ movl %eax, %edx # %edx<- newSaveArea
+ sub %ecx, %eax # %eax<- bottom; (newSaveArea - outsSize)
+ cmp LOCAL2_OFFSET(%ebp), %eax # compare interpStackEnd and bottom
+ movl LOCAL0_OFFSET(%ebp), %eax # %eax<- restore methodToCall
+ jl .LstackOverflow # handle frame overflow
+
+ /*
+ * set up newSaveArea
+ */
+
+#ifdef EASY_GDB
+ SAVEAREA_FROM_FP(%ecx,rFP) # %ecx<- &StackSaveArea
+ movl %ecx, offStackSaveArea_prevSave(%edx) # newSaveArea->prevSave<- &outs
+#endif
+ movl rFP, offStackSaveArea_prevFrame(%edx) # newSaveArea->prevFrame<- rFP
+ movl rPC_SPILL(%ebp), %ecx
+ movl %ecx, offStackSaveArea_savedPc(%edx) # newSaveArea->savedPc<- rPC
+ testl $$ACC_NATIVE, offMethod_accessFlags(%eax) # check for native call
+ movl %eax, offStackSaveArea_method(%edx) # newSaveArea->method<- method to call
+ jne .LinvokeNative # handle native call
+
+ /*
+ * Update "glue" values for the new method
+ * %eax=methodToCall, LOCAL1_OFFSET(%ebp)=newFp
+ */
+
+ movl offMethod_clazz(%eax), %edx # %edx<- method->clazz
+ GET_GLUE(%ecx) # %ecx<- pMterpGlue
+ movl offClassObject_pDvmDex(%edx), %edx # %edx<- method->clazz->pDvmDex
+ movl %eax, offGlue_method(%ecx) # glue->method<- methodToCall
+ movl %edx, offGlue_methodClassDex(%ecx) # glue->methodClassDex<- method->clazz->pDvmDex
+ movl offMethod_insns(%eax), rPC # rPC<- methodToCall->insns
+ movl offGlue_self(%ecx), %eax # %eax<- glue->self
+ movl LOCAL1_OFFSET(%ebp), rFP # rFP<- newFP
+ movl rFP, offThread_curFrame(%eax) # glue->self->curFrame<- newFP
+ FETCH_INST()
+ GOTO_NEXT # jump to methodToCall->insns
+
+ /*
+ * Prep for the native call
+ * %eax=methodToCall, LOCAL1_OFFSET(%ebp)=newFP, %edx=newSaveArea
+ */
+
+.LinvokeNative:
+ GET_GLUE(%ecx) # %ecx<- pMterpGlue
+ movl %eax, OUT_ARG1(%esp) # push parameter methodToCall
+ movl offGlue_self(%ecx), %ecx # %ecx<- glue->self
+ movl offThread_jniLocal_topCookie(%ecx), %eax # %eax<- self->localRef->...
+ movl %eax, offStackSaveArea_localRefCookie(%edx) # newSaveArea->localRefCookie<- top
+ movl %edx, OUT_ARG4(%esp) # save newSaveArea
+ movl LOCAL1_OFFSET(%ebp), %edx # %edx<- newFP
+ movl %edx, offThread_curFrame(%ecx) # glue->self->curFrame<- newFP
+ movl %ecx, OUT_ARG3(%esp) # save glue->self
+ movl %ecx, OUT_ARG2(%esp) # push parameter glue->self
+ GET_GLUE(%ecx) # %ecx<- pMterpGlue
+ movl OUT_ARG1(%esp), %eax # %eax<- methodToCall
+ lea offGlue_retval(%ecx), %ecx # %ecx<- &retval
+ movl %ecx, OUT_ARG0(%esp) # push parameter pMterpGlue
+ push %edx # push parameter newFP
+
+ call *offMethod_nativeFunc(%eax) # call methodToCall->nativeFunc
+ lea 4(%esp), %esp
+ movl OUT_ARG4(%esp), %ecx # %ecx<- newSaveArea
+ movl OUT_ARG3(%esp), %eax # %eax<- glue->self
+ movl offStackSaveArea_localRefCookie(%ecx), %edx # %edx<- old top
+ cmp $$0, offThread_exception(%eax) # check for exception
+ movl rFP, offThread_curFrame(%eax) # glue->self->curFrame<- rFP
+ movl %edx, offThread_jniLocal_topCookie(%eax) # new top <- old top
+ UNSPILL(rPC)
+ jne common_exceptionThrown # handle exception
+ FETCH_INST_WORD(3)
+ ADVANCE_PC(3)
+ GOTO_NEXT # jump to next instruction
+
+.LstackOverflow: # eax=methodToCall
+ movl %eax, OUT_ARG1(%esp) # push parameter methodToCall
+ GET_GLUE(%eax) # %eax<- pMterpGlue
+ movl offGlue_self(%eax), %eax # %eax<- glue->self
+ movl %eax, OUT_ARG0(%esp) # push parameter self
+ call dvmHandleStackOverflow # call: (Thread* self, Method* meth)
+ UNSPILL(rPC) # return: void
+ jmp common_exceptionThrown # handle exception
+
+
+/*
+ * Common invoke code (old-style).
+ * TUNING: Rewrite along lines of new armv5 code?
+ *
+ * On entry:
+ * eax = Method* methodToCall
+ * ecx = bool methodCallRange
+ * rINST trashed, must reload
+ */
+common_invokeOld:
+ movl %ecx,OUT_ARG1(%esp) # arg1<- methodCallRange
+ GET_GLUE(%ecx)
+ movzwl (rPC),rINST_FULL # recover rINST
+ movl %eax,OUT_ARG2(%esp) # arg2<- method
+ movzwl 4(rPC),%eax # eax<- GFED or CCCC
+ SAVE_PC_TO_GLUE(%ecx)
+ SAVE_FP_TO_GLUE(%ecx)
+ movzbl rINST_HI,rINST_FULL
+ movl rINST_FULL,OUT_ARG3(%esp)# arg3<- AA
+ movl %ecx,OUT_ARG0(%esp) # arg0<- GLUE
+ movl %eax,OUT_ARG4(%esp) # arg4<- GFED/CCCC
+ call dvmMterp_invokeMethod
+ jmp common_resumeAfterGlueCall
+
+
+/*
+ * Do we need the thread to be suspended or have debugger/profiling activity?
+ *
+ * On entry:
+ * ebx -> PC adjustment in 16-bit words (must be preserved)
+ * ecx -> GLUE pointer
+ * reentry type, e.g. kInterpEntryInstr stored in rGLUE->entryPoint
+ *
+ * Note: A call will normally kill %eax, rPC/%edx and %ecx. To
+ * streamline the normal case, this routine will preserve rPC and
+ * %ecx in addition to the normal caller save regs. The save/restore
+ * is a bit ugly, but will happen in the relatively uncommon path.
+ * TODO: Basic-block style Jit will need a hook here as well. Fold it into
+ * the suspendCount check so we can get both in 1 shot.
+ */
+common_periodicChecks:
+ movl offGlue_pSelfSuspendCount(%ecx),%eax # eax <- &suspendCount
+ cmpl $$0,(%eax)
+ jne 1f
+
+6:
+ movl offGlue_pDebuggerActive(%ecx),%eax # eax <- &DebuggerActive
+ movl offGlue_pActiveProfilers(%ecx),%ecx # ecx <- &ActiveProfilers
+ testl %eax,%eax # debugger enabled?
+ je 2f
+ movzbl (%eax),%eax # get active count
+2:
+ orl (%ecx),%eax # eax <- debuggerActive | activeProfilers
+ GET_GLUE(%ecx) # restore rGLUE
+ jne 3f # one or both active - switch interp
+
+5:
+ ret
+
+ /* Check for suspend */
+1:
+ /* At this point, the return pointer to the caller of
+ * common_periodicChecks is on the top of stack. We need to preserve
+ * rPC(edx) and GLUE(ecx). We'll spill rPC, and reload GLUE.
+ * The outgoing profile is:
+ * bool dvmCheckSuspendPending(Thread* self)
+ * Because we reached here via a call, go ahead and build a new frame.
+ */
+ EXPORT_PC() # need for precise GC
+ movl offGlue_self(%ecx),%eax # eax<- glue->self
+ SPILL(rPC) # save edx
+ push %ebp
+ movl %esp,%ebp
+ subl $$24,%esp
+ movl %eax,OUT_ARG0(%esp)
+ call dvmCheckSuspendPending
+ addl $$24,%esp
+ pop %ebp
+ UNSPILL(rPC)
+ GET_GLUE(%ecx)
+
+ /*
+ * Need to check to see if debugger or profiler flags got set
+ * while we were suspended.
+ */
+ jmp 6b
+
+ /* Switch interpreters */
+ /* Note: %ebx contains the 16-bit word offset to be applied to rPC to
+ * "complete" the interpretation of backwards branches. In effect, we
+ * are completing the interpretation of the branch instruction here,
+ * and the new interpreter will resume interpretation at the branch
+ * target. However, a switch request recognized during the handling
+ * of a return from method instruction results in an immediate abort,
+ * and the new interpreter will resume by re-interpreting the return
+ * instruction.
+ */
+3:
+ leal (rPC,%ebx,2),rPC # adjust pc to show target
+ GET_GLUE(%ecx) # bail expect GLUE already loaded
+ movl $$1,rINST_FULL # set changeInterp to true
+ jmp common_gotoBail
+
+
+/*
+ * Common code for handling a return instruction
+ */
+common_returnFromMethod:
+ GET_GLUE(%ecx)
+ /* Set entry mode in case we bail */
+ movb $$kInterpEntryReturn,offGlue_entryPoint(%ecx)
+ xorl rINST_FULL,rINST_FULL # zero offset in case we switch interps
+ call common_periodicChecks # Note: expects %ecx to be preserved
+
+ SAVEAREA_FROM_FP(%eax,rFP) # eax<- saveArea (old)
+ movl offStackSaveArea_prevFrame(%eax),rFP # rFP<- prevFrame
+ movl (offStackSaveArea_method-sizeofStackSaveArea)(rFP),rINST_FULL
+ cmpl $$0,rINST_FULL # break?
+ je common_gotoBail # break frame, bail out completely
+
+ movl offStackSaveArea_savedPc(%eax),rPC # pc<- saveArea->savedPC
+ movl offGlue_self(%ecx),%eax # eax<- self
+ movl rINST_FULL,offGlue_method(%ecx) # glue->method = newSave->meethod
+ movl rFP,offThread_curFrame(%eax) # self->curFrame = fp
+ movl offMethod_clazz(rINST_FULL),%eax # eax<- method->clazz
+ FETCH_INST_WORD(3)
+ movl offClassObject_pDvmDex(%eax),%eax # eax<- method->clazz->pDvmDex
+ ADVANCE_PC(3)
+ movl %eax,offGlue_methodClassDex(%ecx)
+ /* not bailing - restore entry mode to default */
+ movb $$kInterpEntryInstr,offGlue_entryPoint(%ecx)
+ GOTO_NEXT
+
+/*
+ * Prepare to strip the current frame and "longjump" back to caller of
+ * dvmMterpStdRun.
+ *
+ * on entry:
+ * rINST_FULL holds changeInterp
+ * ecx holds glue pointer
+ *
+ * expected profile: dvmMterpStdBail(MterpGlue *glue, bool changeInterp)
+ */
+common_gotoBail:
+ SAVE_PC_TO_GLUE(%ecx) # export state to glue
+ SAVE_FP_TO_GLUE(%ecx)
+ movl %ecx,OUT_ARG0(%esp) # glue in arg0
+ movl rINST_FULL,OUT_ARG1(%esp) # changeInterp in arg1
+ call dvmMterpStdBail # bail out....
+
+
+/*
+ * After returning from a "glued" function, pull out the updated values
+ * and start executing at the next instruction.
+ */
+ common_resumeAfterGlueCall:
+ GET_GLUE(%ecx)
+ LOAD_PC_FROM_GLUE(%ecx)
+ LOAD_FP_FROM_GLUE(%ecx)
+ FETCH_INST()
+ GOTO_NEXT
+
+/*
+ * Integer divide or mod by zero
+ */
+common_errDivideByZero:
+ EXPORT_PC()
+ movl $$.LstrArithmeticException,%eax
+ movl %eax,OUT_ARG0(%esp)
+ movl $$.LstrDivideByZero,%eax
+ movl %eax,OUT_ARG1(%esp)
+ SPILL(rPC)
+ call dvmThrowException
+ UNSPILL(rPC)
+ jmp common_exceptionThrown
+
+/*
+ * Attempt to allocate an array with a negative size.
+ */
+common_errNegativeArraySize:
+ EXPORT_PC()
+ movl $$.LstrNegativeArraySizeException,%eax
+ movl %eax,OUT_ARG0(%esp)
+ xorl %eax,%eax
+ movl %eax,OUT_ARG1(%esp)
+ SPILL(rPC)
+ call dvmThrowException
+ UNSPILL(rPC)
+ jmp common_exceptionThrown
+
+/*
+ * Attempt to allocate an array with a negative size.
+ */
+common_errNoSuchMethod:
+
+ EXPORT_PC()
+ movl $$.LstrNoSuchMethodError,%eax
+ movl %eax,OUT_ARG0(%esp)
+ xorl %eax,%eax
+ movl %eax,OUT_ARG1(%esp)
+ SPILL(rPC)
+ call dvmThrowException
+ UNSPILL(rPC)
+ jmp common_exceptionThrown
+
+/*
+ * Hit a null object when we weren't expecting one. Export the PC, throw a
+ * NullPointerException and goto the exception processing code.
+ */
+common_errNullObject:
+ EXPORT_PC()
+ movl $$.LstrNullPointerException,%eax
+ movl %eax,OUT_ARG0(%esp)
+ xorl %eax,%eax
+ movl %eax,OUT_ARG1(%esp)
+ SPILL(rPC)
+ call dvmThrowException
+ UNSPILL(rPC)
+ jmp common_exceptionThrown
+
+/*
+ * Array index exceeds max.
+ */
+common_errArrayIndex:
+ EXPORT_PC()
+ movl $$.LstrArrayIndexException,%eax
+ movl %eax,OUT_ARG0(%esp)
+ xorl %eax,%eax
+ movl %eax,OUT_ARG1(%esp)
+ SPILL(rPC)
+ call dvmThrowException
+ UNSPILL(rPC)
+ jmp common_exceptionThrown
+/*
+ * Invalid array value.
+ */
+common_errArrayStore:
+ EXPORT_PC()
+ movl $$.LstrArrayStoreException,%eax
+ movl %eax,OUT_ARG0(%esp)
+ xorl %eax,%eax
+ movl %eax,OUT_ARG1(%esp)
+ SPILL(rPC)
+ call dvmThrowException
+ UNSPILL(rPC)
+ jmp common_exceptionThrown
+
+/*
+ * Somebody has thrown an exception. Handle it.
+ *
+ * If the exception processing code returns to us (instead of falling
+ * out of the interpreter), continue with whatever the next instruction
+ * now happens to be.
+ *
+ * This does not return.
+ */
+common_exceptionThrown:
+ GET_GLUE(%ecx)
+ SAVE_PC_TO_GLUE(%ecx)
+ SAVE_FP_TO_GLUE(%ecx)
+ movl %ecx,OUT_ARG0(%esp)
+ call dvmMterp_exceptionThrown
+ jmp common_resumeAfterGlueCall
+
+common_abort:
+ movl $$0xdeadf00d,%eax
+ call *%eax
+
+
+/*
+ * Strings
+ */
+
+ .section .rodata
+.LstrNullPointerException:
+ .asciz "Ljava/lang/NullPointerException;"
+.LstrArithmeticException:
+ .asciz "Ljava/lang/ArithmeticException;"
+.LstrDivideByZero:
+ .asciz "divide by zero"
+.LstrArrayIndexException:
+ .asciz "Ljava/lang/ArrayIndexOutOfBoundsException;"
+.LstrArrayStoreException:
+ .asciz "Ljava/lang/ArrayStoreException;"
+.LstrNegativeArraySizeException:
+ .asciz "Ljava/lang/NegativeArraySizeException;"
+.LstrInstantiationError:
+ .asciz "Ljava/lang/InstantiationError;"
+.LstrClassCastException:
+ .asciz "Ljava/lang/ClassCastException;"
+.LstrNoSuchMethodError:
+ .asciz "Ljava/lang/NoSuchMethodError;"
+.LstrInternalError:
+ .asciz "Ljava/lang/InternalError;"
+.LstrFilledNewArrayNotImpl:
+ .asciz "filled-new-array only implemented for 'int'"
diff --git a/vm/mterp/x86/fpcvt.S b/vm/mterp/x86/fpcvt.S
new file mode 100644
index 0000000..4fffbe4
--- /dev/null
+++ b/vm/mterp/x86/fpcvt.S
@@ -0,0 +1,14 @@
+%default {"instr":"","load":"","store":""}
+ /*
+ * Generic 32-bit FP conversion operation.
+ */
+ /* unop vA, vB */
+ movzbl rINST_HI,%ecx # ecx<- A+
+ sarl $$12,rINST_FULL # rINST_FULL<- B
+ $load (rFP,rINST_FULL,4) # %st0<- vB
+ andb $$0xf,%cl # ecx<- A
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ $instr
+ $store (rFP,%ecx,4) # vA<- %st0
+ GOTO_NEXT
diff --git a/vm/mterp/x86/header.S b/vm/mterp/x86/header.S
new file mode 100644
index 0000000..bb043ba
--- /dev/null
+++ b/vm/mterp/x86/header.S
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * 32-bit x86 definitions and declarations.
+ */
+
+/*
+386 ABI general notes:
+
+Caller save set:
+ eax, edx, ecx, st(0)-st(7)
+Callee save set:
+ ebx, esi, edi, ebp
+Return regs:
+ 32-bit in eax
+ 64-bit in edx:eax (low-order 32 in eax)
+ fp on top of fp stack st(0)
+
+Parameters passed on stack, pushed right-to-left. On entry to target, first
+parm is at 4(%esp). Traditional entry code is:
+
+functEntry:
+ push %ebp # save old frame pointer
+ mov %ebp,%esp # establish new frame pointer
+ sub FrameSize,%esp # Allocate storage for spill, locals & outs
+
+Once past the prologue, arguments are referenced at ((argno + 2)*4)(%ebp)
+
+Alignment of stack not strictly required, but should be for performance. We'll
+align frame sizes to 16-byte multiples.
+
+If we're not doing variable stack allocation (alloca), the frame pointer can be
+eliminated and all arg references adjusted to be esp relative.
+
+Mterp notes:
+
+Some key interpreter variables will be assigned to registers. Note that each
+will also have an associated spill location (mostly used useful for those assigned
+to callee save registers).
+
+ nick reg purpose
+ rPC edx interpreted program counter, used for fetching instructions
+ rFP esi interpreted frame pointer, used for accessing locals and args
+ rIBASE edi Base pointer for instruction dispatch computed goto
+ rINST bx first 16-bit code of current instruction
+ rOPCODE bl opcode portion of instruction word
+ rINST_HI bh high byte of instruction word, usually contains src/tgt reg names
+
+Notes:
+ o High order 16 bits of ebx must be zero on entry to handler
+ o rPC, rFP, rIBASE, rINST/rOPCODE valid on handler entry and exit
+ o eax and ecx are scratch, rINST/ebx sometimes scratch
+ o rPC is in the caller save set, and will be killed across external calls. Don't
+ forget to SPILL/UNSPILL it around call points
+
+*/
+
+#define rPC %edx
+#define rFP %esi
+#define rIBASE %edi
+#define rINST_FULL %ebx
+#define rINST %bx
+#define rINST_HI %bh
+#define rINST_LO %bl
+#define rOPCODE %bl
+
+
+/* Frame diagram while executing dvmMterpStdRun, high to low addresses */
+#define IN_ARG0 ( 8)
+#define CALLER_RP ( 4)
+#define PREV_FP ( 0) /* <- dvmMterpStdRun ebp */
+/* Spill offsets relative to %ebp */
+#define EDI_SPILL ( -4)
+#define ESI_SPILL ( -8)
+#define EDX_SPILL (-12) /* <- esp following dmMterpStdRun header */
+#define rPC_SPILL (-16)
+#define rFP_SPILL (-20)
+#define rGLUE_SPILL (-24)
+#define rIBASE_SPILL (-28)
+#define rINST_FULL_SPILL (-32)
+#define TMP_SPILL (-36)
+#define LOCAL0_OFFSET (-40)
+#define LOCAL1_OFFSET (-44)
+#define LOCAL2_OFFSET (-48)
+#define LOCAL3_OFFSET (-52)
+/* Out Arg offsets, relative to %sp */
+#define OUT_ARG4 ( 16)
+#define OUT_ARG3 ( 12)
+#define OUT_ARG2 ( 8)
+#define OUT_ARG1 ( 4)
+#define OUT_ARG0 ( 0) /* <- dvmMterpStdRun esp */
+
+#define SPILL(reg) movl reg##,reg##_SPILL(%ebp)
+#define UNSPILL(reg) movl reg##_SPILL(%ebp),reg
+#define SPILL_TMP(reg) movl reg,TMP_SPILL(%ebp)
+#define UNSPILL_TMP(reg) movl TMP_SPILL(%ebp),reg
+
+
+/* save/restore the PC and/or FP from the glue struct */
+#define LOAD_PC_FROM_GLUE(_glu) movl offGlue_pc(_glu),rPC
+#define SAVE_PC_TO_GLUE(_glu) movl rPC,offGlue_pc(_glu)
+#define LOAD_FP_FROM_GLUE(_glu) movl offGlue_fp(_glu),rFP
+#define SAVE_FP_TO_GLUE(_glu) movl rFP,offGlue_fp(_glu)
+
+#define GET_GLUE(_reg) movl rGLUE_SPILL(%ebp),_reg
+
+/* The interpreter assumes a properly aligned stack on entry, and
+ * will preserve 16-byte alignment.
+ */
+
+/*
+ * "export" the PC to the interpreted stack frame, f/b/o future exception
+ * objects. Must * be done *before* something calls dvmThrowException.
+ *
+ * In C this is "SAVEAREA_FROM_FP(fp)->xtra.currentPc = pc", i.e.
+ * fp - sizeof(StackSaveArea) + offsetof(SaveArea, xtra.currentPc)
+ *
+ * It's okay to do this more than once.
+ */
+#define EXPORT_PC() \
+ movl rPC, (-sizeofStackSaveArea + offStackSaveArea_currentPc)(rFP)
+
+/*
+ * Given a frame pointer, find the stack save area.
+ *
+ * In C this is "((StackSaveArea*)(_fp) -1)".
+ */
+#define SAVEAREA_FROM_FP(_reg, _fpreg) \
+ leal -sizeofStackSaveArea(_fpreg),_reg
+
+/*
+ * Fetch the next instruction from rPC into rINST. Does not advance rPC.
+ */
+#define FETCH_INST() movzwl (rPC),rINST_FULL
+
+/*
+ * Fetch the nth instruction word from rPC into rINST. Does not advance
+ * rPC, and _count is in words
+ */
+#define FETCH_INST_WORD(_count) movzwl _count*2(rPC),rINST_FULL
+
+/*
+ * Fetch instruction word indexed (used for branching).
+ * Index is in instruction word units.
+ */
+#define FETCH_INST_INDEXED(_reg) movzwl (rPC,_reg,2),rINST_FULL
+
+/*
+ * Extract the opcode of the instruction in rINST
+ */
+#define EXTRACT_OPCODE(_reg) movzx rOPCODE,_reg
+
+/*
+ * Advance rPC by instruction count
+ */
+#define ADVANCE_PC(_count) leal 2*_count(rPC),rPC
+
+/*
+ * Advance rPC by branch offset in register
+ */
+#define ADVANCE_PC_INDEXED(_reg) leal (rPC,_reg,2),rPC
+
+/*
+ * Note: assumes opcode previously fetched and in rINST, and
+ * %eax is killable at this point.
+ */
+#if 1
+.macro GOTO_NEXT
+ /* For computed next version */
+ movzx rOPCODE,%eax
+ sall $$$handler_size_bits,%eax
+ addl rIBASE,%eax
+ jmp *%eax
+.endm
+#else
+ /* For jump table version */
+.macro GOTO_NEXT
+ movzx rOPCODE,%eax
+ jmp *(rIBASE,%eax,4)
+.endm
+#endif
+
+/*
+ * Get/set the 32-bit value from a Dalvik register.
+ */
+#define GET_VREG(_reg, _vreg) movl (rFP,_vreg,4),_reg
+#define SET_VREG(_reg, _vreg) movl _reg,(rFP,_vreg,4)
+#define GET_VREG_WORD(_reg, _vreg, _offset) movl 4*(_offset)(rFP,_vreg,4),_reg
+#define SET_VREG_WORD(_reg, _vreg, _offset) movl _reg,4*(_offset)(rFP,_vreg,4)
+
+/*
+ * This is a #include, not a %include, because we want the C pre-processor
+ * to expand the macros into assembler assignment statements.
+ */
+#include "../common/asm-constants.h"
diff --git a/vm/mterp/x86/shop2addr.S b/vm/mterp/x86/shop2addr.S
new file mode 100644
index 0000000..9e8bd56
--- /dev/null
+++ b/vm/mterp/x86/shop2addr.S
@@ -0,0 +1,16 @@
+%default {"result":"%eax"}
+ /*
+ * Generic 32-bit "shift/2addr" operation.
+ */
+ /* shift/2addr vA, vB */
+ movzx rINST_HI,%ecx # eax<- BA
+ sarl $$4,%ecx # ecx<- B
+ GET_VREG(%ecx,%ecx) # eax<- vBB
+ movzbl rINST_HI,rINST_FULL # rINST_FULL<- BA
+ andb $$0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG(%eax,rINST_FULL) # eax<- vAA
+ $instr # ex: sarl %cl,%eax
+ SET_VREG($result,rINST_FULL)
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/stub.S b/vm/mterp/x86/stub.S
new file mode 100644
index 0000000..c75645e
--- /dev/null
+++ b/vm/mterp/x86/stub.S
@@ -0,0 +1,11 @@
+ /* (stub) */
+ GET_GLUE(%ecx)
+ SAVE_PC_TO_GLUE(%ecx) # only need to export these two
+ SAVE_FP_TO_GLUE(%ecx) # only need to export these two
+ movl %ecx,OUT_ARG0(%esp) # glue is first arg to function
+ call dvmMterp_${opcode} # do the real work
+ GET_GLUE(%ecx)
+ LOAD_PC_FROM_GLUE(%ecx) # retrieve updated values
+ LOAD_FP_FROM_GLUE(%ecx) # retrieve updated values
+ FETCH_INST()
+ GOTO_NEXT
diff --git a/vm/mterp/x86/unop.S b/vm/mterp/x86/unop.S
new file mode 100644
index 0000000..7192d9a
--- /dev/null
+++ b/vm/mterp/x86/unop.S
@@ -0,0 +1,17 @@
+%default {"pre0":"","pre1":""}
+ /*
+ * Generic 32-bit unary operation. Provide an "instr" line that
+ * specifies an instruction that performs "result = op eax".
+ */
+ /* unop vA, vB */
+ movzbl rINST_HI,%ecx # ecx<- A+
+ sarl $$12,rINST_FULL # rINST_FULL<- B
+ GET_VREG(%eax,rINST_FULL) # eax<- vB
+ andb $$0xf,%cl # ecx<- A
+ FETCH_INST_WORD(1)
+ ADVANCE_PC(1)
+ $pre0
+ $pre1
+ $instr
+ SET_VREG(%eax,%ecx)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/unopWide.S b/vm/mterp/x86/unopWide.S
new file mode 100644
index 0000000..ad5b361
--- /dev/null
+++ b/vm/mterp/x86/unopWide.S
@@ -0,0 +1,22 @@
+%default {"instr1":"","instr2":"","instr3":""}
+ /*
+ * Generic 64-bit unary operation.
+ * Operand in %ecx:%eax
+ *
+ * For: neg-long, not-long
+ */
+ /* unop vA, vB */
+ movzbl rINST_HI,%ecx # ecx<- BA
+ sarl $$4,%ecx # ecx<- B
+ movzbl rINST_HI,rINST_FULL # ecx<- BA
+ andb $$0xf,rINST_LO # rINST_FULL<- A
+ GET_VREG_WORD(%eax,%ecx,0) # eax<- v[B+0]
+ GET_VREG_WORD(%ecx,%ecx,1) # ecx<- v[B+1]
+ $instr1 # ex: negl %eax
+ $instr2 # ex: adcl $$0,%ecx
+ $instr3 # ex: negl %ecx
+ SET_VREG_WORD(%eax,rINST_FULL,0) # v[A+0] <- eax
+ SET_VREG_WORD(%ecx,rINST_FULL,1) # v[A+1] <- ecx
+ GET_INST_WORD(1)
+ ADVANCE_PC(1)
+ GOTO_NEXT
diff --git a/vm/mterp/x86/unused.S b/vm/mterp/x86/unused.S
new file mode 100644
index 0000000..f0f117c
--- /dev/null
+++ b/vm/mterp/x86/unused.S
@@ -0,0 +1 @@
+ jmp common_abort
diff --git a/vm/mterp/x86/zcmp.S b/vm/mterp/x86/zcmp.S
new file mode 100644
index 0000000..221822d
--- /dev/null
+++ b/vm/mterp/x86/zcmp.S
@@ -0,0 +1,22 @@
+%verify "branch taken"
+%verify "branch not taken"
+ /*
+ * Generic one-operand compare-and-branch operation. Provide a "revcmp"
+ * fragment that specifies the *reverse* comparison to perform, e.g.
+ * for "if-le" you would use "gt".
+ *
+ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
+ */
+ /* if-cmp vAA, +BBBB */
+ movzx rINST_HI,%ecx # ecx <- AA
+ cmpl $$0,(rFP,%ecx,4) # compare (vA, 0)
+ movswl 2(rPC),rINST_FULL # fetch signed displacement
+ movl $$2,%eax # assume branch not taken
+ j${revcmp} 1f
+ testl rINST_FULL,rINST_FULL
+ js common_backwardBranch
+ movl rINST_FULL,%eax
+1:
+ FETCH_INST_INDEXED(%eax)
+ ADVANCE_PC_INDEXED(%eax)
+ GOTO_NEXT
diff --git a/vm/native/InternalNative.c b/vm/native/InternalNative.c
new file mode 100644
index 0000000..06ed665
--- /dev/null
+++ b/vm/native/InternalNative.c
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Internal-native initialization and some common utility functions.
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+/*
+ * Set of classes for which we provide methods.
+ *
+ * The last field, classNameHash, is filled in at startup.
+ */
+static DalvikNativeClass gDvmNativeMethodSet[] = {
+ { "Ljava/lang/Object;", dvm_java_lang_Object, 0 },
+ { "Ljava/lang/Class;", dvm_java_lang_Class, 0 },
+ { "Ljava/lang/Runtime;", dvm_java_lang_Runtime, 0 },
+ { "Ljava/lang/String;", dvm_java_lang_String, 0 },
+ { "Ljava/lang/System;", dvm_java_lang_System, 0 },
+ { "Ljava/lang/SystemProperties;", dvm_java_lang_SystemProperties, 0 },
+ { "Ljava/lang/Throwable;", dvm_java_lang_Throwable, 0 },
+ { "Ljava/lang/VMClassLoader;", dvm_java_lang_VMClassLoader, 0 },
+ { "Ljava/lang/VMThread;", dvm_java_lang_VMThread, 0 },
+ { "Ljava/lang/reflect/AccessibleObject;",
+ dvm_java_lang_reflect_AccessibleObject, 0 },
+ { "Ljava/lang/reflect/Array;", dvm_java_lang_reflect_Array, 0 },
+ { "Ljava/lang/reflect/Constructor;",
+ dvm_java_lang_reflect_Constructor, 0 },
+ { "Ljava/lang/reflect/Field;", dvm_java_lang_reflect_Field, 0 },
+ { "Ljava/lang/reflect/Method;", dvm_java_lang_reflect_Method, 0 },
+ { "Ljava/lang/reflect/Proxy;", dvm_java_lang_reflect_Proxy, 0 },
+ { "Ljava/security/AccessController;",
+ dvm_java_security_AccessController, 0 },
+ { "Ljava/util/concurrent/atomic/AtomicLong;",
+ dvm_java_util_concurrent_atomic_AtomicLong, 0 },
+ { "Ldalvik/system/VMDebug;", dvm_dalvik_system_VMDebug, 0 },
+ { "Ldalvik/system/DexFile;", dvm_dalvik_system_DexFile, 0 },
+ { "Ldalvik/system/VMRuntime;", dvm_dalvik_system_VMRuntime, 0 },
+ { "Ldalvik/system/Zygote;", dvm_dalvik_system_Zygote, 0 },
+ { "Ldalvik/system/VMStack;", dvm_dalvik_system_VMStack, 0 },
+ { "Lorg/apache/harmony/dalvik/ddmc/DdmServer;",
+ dvm_org_apache_harmony_dalvik_ddmc_DdmServer, 0 },
+ { "Lorg/apache/harmony/dalvik/ddmc/DdmVmInternal;",
+ dvm_org_apache_harmony_dalvik_ddmc_DdmVmInternal, 0 },
+ { "Lorg/apache/harmony/dalvik/NativeTestTarget;",
+ dvm_org_apache_harmony_dalvik_NativeTestTarget, 0 },
+ { "Lsun/misc/Unsafe;", dvm_sun_misc_Unsafe, 0 },
+ { NULL, NULL, 0 },
+};
+
+
+/*
+ * Set up hash values on the class names.
+ */
+bool dvmInternalNativeStartup(void)
+{
+ DalvikNativeClass* classPtr = gDvmNativeMethodSet;
+
+ while (classPtr->classDescriptor != NULL) {
+ classPtr->classDescriptorHash =
+ dvmComputeUtf8Hash(classPtr->classDescriptor);
+ classPtr++;
+ }
+
+ gDvm.userDexFiles = dvmHashTableCreate(2, dvmFreeDexOrJar);
+ if (gDvm.userDexFiles == NULL)
+ return false;
+
+ return true;
+}
+
+/*
+ * Clean up.
+ */
+void dvmInternalNativeShutdown(void)
+{
+ dvmHashTableFree(gDvm.userDexFiles);
+}
+
+/*
+ * Search the internal native set for a match.
+ */
+DalvikNativeFunc dvmLookupInternalNativeMethod(const Method* method)
+{
+ const char* classDescriptor = method->clazz->descriptor;
+ const DalvikNativeClass* pClass;
+ u4 hash;
+
+ hash = dvmComputeUtf8Hash(classDescriptor);
+ pClass = gDvmNativeMethodSet;
+ while (true) {
+ if (pClass->classDescriptor == NULL)
+ break;
+ if (pClass->classDescriptorHash == hash &&
+ strcmp(pClass->classDescriptor, classDescriptor) == 0)
+ {
+ const DalvikNativeMethod* pMeth = pClass->methodInfo;
+ while (true) {
+ if (pMeth->name == NULL)
+ break;
+
+ if (dvmCompareNameDescriptorAndMethod(pMeth->name,
+ pMeth->signature, method) == 0)
+ {
+ /* match */
+ //LOGV("+++ match on %s.%s %s at %p\n",
+ // className, methodName, methodSignature, pMeth->fnPtr);
+ return pMeth->fnPtr;
+ }
+
+ pMeth++;
+ }
+ }
+
+ pClass++;
+ }
+
+ return NULL;
+}
+
+
+/*
+ * Magic "internal native" code stub, inserted into abstract method
+ * definitions when a class is first loaded. This throws the expected
+ * exception so we don't have to explicitly check for it in the interpreter.
+ */
+void dvmAbstractMethodStub(const u4* args, JValue* pResult)
+{
+ LOGD("--- called into dvmAbstractMethodStub\n");
+ dvmThrowException("Ljava/lang/AbstractMethodError;",
+ "abstract method not implemented");
+}
+
+
+/*
+ * Verify that "obj" is non-null and is an instance of "clazz".
+ *
+ * Returns "false" and throws an exception if not.
+ */
+bool dvmVerifyObjectInClass(Object* obj, ClassObject* clazz)
+{
+ if (obj == NULL) {
+ dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+ return false;
+ }
+ if (!dvmInstanceof(obj->clazz, clazz)) {
+ dvmThrowException("Ljava/lang/IllegalArgumentException;",
+ "object is not an instance of the class");
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Validate a "binary" class name, e.g. "java.lang.String" or "[I".
+ */
+static bool validateClassName(const char* name)
+{
+ int len = strlen(name);
+ int i = 0;
+
+ /* check for reasonable array types */
+ if (name[0] == '[') {
+ while (name[i] == '[')
+ i++;
+
+ if (name[i] == 'L') {
+ /* array of objects, make sure it ends well */
+ if (name[len-1] != ';')
+ return false;
+ } else if (strchr(PRIM_TYPE_TO_LETTER, name[i]) != NULL) {
+ if (i != len-1)
+ return false;
+ } else {
+ return false;
+ }
+ }
+
+ /* quick check for illegal chars */
+ for ( ; i < len; i++) {
+ if (name[i] == '/')
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Find a class by name, initializing it if requested.
+ */
+ClassObject* dvmFindClassByName(StringObject* nameObj, Object* loader,
+ bool doInit)
+{
+ ClassObject* clazz = NULL;
+ char* name = NULL;
+ char* descriptor = NULL;
+
+ if (nameObj == NULL) {
+ dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+ goto bail;
+ }
+ name = dvmCreateCstrFromString(nameObj);
+
+ /*
+ * We need to validate and convert the name (from x.y.z to x/y/z). This
+ * is especially handy for array types, since we want to avoid
+ * auto-generating bogus array classes.
+ */
+ if (!validateClassName(name)) {
+ LOGW("dvmFindClassByName rejecting '%s'\n", name);
+ dvmThrowException("Ljava/lang/ClassNotFoundException;", name);
+ goto bail;
+ }
+
+ descriptor = dvmDotToDescriptor(name);
+ if (descriptor == NULL) {
+ goto bail;
+ }
+
+ if (doInit)
+ clazz = dvmFindClass(descriptor, loader);
+ else
+ clazz = dvmFindClassNoInit(descriptor, loader);
+
+ if (clazz == NULL) {
+ LOGVV("FAIL: load %s (%d)\n", descriptor, doInit);
+ Thread* self = dvmThreadSelf();
+ Object* oldExcep = dvmGetException(self);
+ dvmAddTrackedAlloc(oldExcep, self); /* don't let this be GCed */
+ dvmClearException(self);
+ dvmThrowChainedException("Ljava/lang/ClassNotFoundException;",
+ name, oldExcep);
+ dvmReleaseTrackedAlloc(oldExcep, self);
+ } else {
+ LOGVV("GOOD: load %s (%d) --> %p ldr=%p\n",
+ descriptor, doInit, clazz, clazz->classLoader);
+ }
+
+bail:
+ free(name);
+ free(descriptor);
+ return clazz;
+}
+
+/*
+ * We insert native method stubs for abstract methods so we don't have to
+ * check the access flags at the time of the method call. This results in
+ * "native abstract" methods, which can't exist. If we see the "abstract"
+ * flag set, clear the "native" flag.
+ *
+ * We also move the DECLARED_SYNCHRONIZED flag into the SYNCHRONIZED
+ * position, because the callers of this function are trying to convey
+ * the "traditional" meaning of the flags to their callers.
+ */
+u4 dvmFixMethodFlags(u4 flags)
+{
+ if ((flags & ACC_ABSTRACT) != 0) {
+ flags &= ~ACC_NATIVE;
+ }
+
+ flags &= ~ACC_SYNCHRONIZED;
+
+ if ((flags & ACC_DECLARED_SYNCHRONIZED) != 0) {
+ flags |= ACC_SYNCHRONIZED;
+ }
+
+ return flags & JAVA_FLAGS_MASK;
+}
+
+
+#define NUM_DOPRIV_FUNCS 4
+
+/*
+ * Determine if "method" is a "privileged" invocation, i.e. is it one
+ * of the variations of AccessController.doPrivileged().
+ *
+ * Because the security stuff pulls in a pile of stuff that we may not
+ * want or need, we don't do the class/method lookups at init time, but
+ * instead on first use.
+ */
+bool dvmIsPrivilegedMethod(const Method* method)
+{
+ int i;
+
+ assert(method != NULL);
+
+ if (!gDvm.javaSecurityAccessControllerReady) {
+ /*
+ * Populate on first use. No concurrency risk since we're just
+ * finding pointers to fixed structures.
+ */
+ static const char* kSignatures[NUM_DOPRIV_FUNCS] = {
+ "(Ljava/security/PrivilegedAction;)Ljava/lang/Object;",
+ "(Ljava/security/PrivilegedExceptionAction;)Ljava/lang/Object;",
+ "(Ljava/security/PrivilegedAction;Ljava/security/AccessControlContext;)Ljava/lang/Object;",
+ "(Ljava/security/PrivilegedExceptionAction;Ljava/security/AccessControlContext;)Ljava/lang/Object;",
+ };
+ ClassObject* clazz;
+
+ clazz = dvmFindClassNoInit("Ljava/security/AccessController;", NULL);
+ if (clazz == NULL) {
+ LOGW("Couldn't find java/security/AccessController\n");
+ return false;
+ }
+
+ assert(NELEM(gDvm.methJavaSecurityAccessController_doPrivileged) ==
+ NELEM(kSignatures));
+
+ /* verify init */
+ for (i = 0; i < NUM_DOPRIV_FUNCS; i++) {
+ gDvm.methJavaSecurityAccessController_doPrivileged[i] =
+ dvmFindDirectMethodByDescriptor(clazz, "doPrivileged", kSignatures[i]);
+ if (gDvm.methJavaSecurityAccessController_doPrivileged[i] == NULL) {
+ LOGW("Warning: couldn't find java/security/AccessController"
+ ".doPrivileged %s\n", kSignatures[i]);
+ return false;
+ }
+ }
+
+ /* all good, raise volatile readiness flag */
+ android_atomic_release_store(true,
+ &gDvm.javaSecurityAccessControllerReady);
+ }
+
+ for (i = 0; i < NUM_DOPRIV_FUNCS; i++) {
+ if (gDvm.methJavaSecurityAccessController_doPrivileged[i] == method) {
+ //LOGI("+++ doPriv match\n");
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/vm/native/InternalNative.h b/vm/native/InternalNative.h
new file mode 100644
index 0000000..7c82dc0
--- /dev/null
+++ b/vm/native/InternalNative.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DALVIK_NATIVE_INTERNALNATIVE
+#define _DALVIK_NATIVE_INTERNALNATIVE
+
+/*
+ * Some setup for internal native functions.
+ */
+bool dvmInternalNativeStartup(void);
+void dvmInternalNativeShutdown(void);
+
+/* search the internal native set for a match */
+DalvikNativeFunc dvmLookupInternalNativeMethod(const Method* method);
+
+/* exception-throwing stub for abstract methods (DalvikNativeFunc) */
+void dvmAbstractMethodStub(const u4* args, JValue* pResult);
+
+#endif /*_DALVIK_NATIVE_INTERNALNATIVE*/
diff --git a/vm/native/InternalNativePriv.h b/vm/native/InternalNativePriv.h
new file mode 100644
index 0000000..6f8d6c9
--- /dev/null
+++ b/vm/native/InternalNativePriv.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Declarations and definitions common to internal native code.
+ */
+#ifndef _DALVIK_NATIVE_INTERNALNATIVEPRIV
+#define _DALVIK_NATIVE_INTERNALNATIVEPRIV
+
+/*
+ * Return macros. Note we use "->i" instead of "->z" for boolean; this
+ * is because the interpreter expects everything to be a 32-bit value.
+ */
+#ifdef NDEBUG
+# define RETURN_VOID() do { (void)(pResult); return; } while(0)
+#else
+# define RETURN_VOID() do { pResult->i = 0xfefeabab; return; }while(0)
+#endif
+#define RETURN_BOOLEAN(_val) do { pResult->i = (_val); return; } while(0)
+#define RETURN_INT(_val) do { pResult->i = (_val); return; } while(0)
+#define RETURN_LONG(_val) do { pResult->j = (_val); return; } while(0)
+#define RETURN_FLOAT(_val) do { pResult->f = (_val); return; } while(0)
+#define RETURN_DOUBLE(_val) do { pResult->d = (_val); return; } while(0)
+#define RETURN_PTR(_val) do { pResult->l = (_val); return; } while(0)
+
+
+/*
+ * Verify that "obj" is non-null and is an instance of "clazz".
+ *
+ * Returns "false" and throws an exception if not.
+ */
+bool dvmVerifyObjectInClass(Object* obj, ClassObject* clazz);
+
+/*
+ * Find a class by name, initializing it if requested.
+ */
+ClassObject* dvmFindClassByName(StringObject* nameObj, Object* loader,
+ bool doInit);
+
+/*
+ * We insert native method stubs for abstract methods so we don't have to
+ * check the access flags at the time of the method call. This results in
+ * "native abstract" methods, which can't exist. If we see the "abstract"
+ * flag set, clear the "native" flag.
+ *
+ * We also move the DECLARED_SYNCHRONIZED flag into the SYNCHRONIZED
+ * position, because the callers of this function are trying to convey
+ * the "traditional" meaning of the flags to their callers.
+ */
+u4 dvmFixMethodFlags(u4 flags);
+
+/*
+ * dvmHashTableFree callback for some DexFile operations.
+ */
+void dvmFreeDexOrJar(void* vptr);
+
+/*
+ * Determine if "method" is a "privileged" invocation, i.e. is it one
+ * of the variations of AccessController.doPrivileged().
+ *
+ * Because the security stuff pulls in a pile of stuff that we may not
+ * want or need, we don't do the class/method lookups at init time, but
+ * instead on first use.
+ */
+bool dvmIsPrivilegedMethod(const Method* method);
+
+
+/*
+ * Tables of methods.
+ */
+extern const DalvikNativeMethod dvm_java_lang_Object[];
+extern const DalvikNativeMethod dvm_java_lang_Class[];
+extern const DalvikNativeMethod dvm_java_lang_Runtime[];
+extern const DalvikNativeMethod dvm_java_lang_String[];
+extern const DalvikNativeMethod dvm_java_lang_System[];
+extern const DalvikNativeMethod dvm_java_lang_SystemProperties[];
+extern const DalvikNativeMethod dvm_java_lang_Throwable[];
+extern const DalvikNativeMethod dvm_java_lang_VMClassLoader[];
+extern const DalvikNativeMethod dvm_java_lang_VMThread[];
+extern const DalvikNativeMethod dvm_java_lang_reflect_AccessibleObject[];
+extern const DalvikNativeMethod dvm_java_lang_reflect_Array[];
+extern const DalvikNativeMethod dvm_java_lang_reflect_Constructor[];
+extern const DalvikNativeMethod dvm_java_lang_reflect_Field[];
+extern const DalvikNativeMethod dvm_java_lang_reflect_Method[];
+extern const DalvikNativeMethod dvm_java_lang_reflect_Proxy[];
+extern const DalvikNativeMethod dvm_java_security_AccessController[];
+extern const DalvikNativeMethod dvm_java_util_concurrent_atomic_AtomicLong[];
+extern const DalvikNativeMethod dvm_dalvik_system_SamplingProfiler[];
+extern const DalvikNativeMethod dvm_dalvik_system_VMDebug[];
+extern const DalvikNativeMethod dvm_dalvik_system_DexFile[];
+extern const DalvikNativeMethod dvm_dalvik_system_VMRuntime[];
+extern const DalvikNativeMethod dvm_dalvik_system_Zygote[];
+extern const DalvikNativeMethod dvm_dalvik_system_VMStack[];
+extern const DalvikNativeMethod dvm_org_apache_harmony_dalvik_ddmc_DdmServer[];
+extern const DalvikNativeMethod dvm_org_apache_harmony_dalvik_ddmc_DdmVmInternal[];
+extern const DalvikNativeMethod dvm_org_apache_harmony_dalvik_NativeTestTarget[];
+extern const DalvikNativeMethod dvm_sun_misc_Unsafe[];
+
+#endif /*_DALVIK_NATIVE_INTERNALNATIVEPRIV*/
diff --git a/vm/native/README.txt b/vm/native/README.txt
new file mode 100644
index 0000000..bc8912f
--- /dev/null
+++ b/vm/native/README.txt
@@ -0,0 +1,23 @@
+Internal native functions.
+
+All of the functions defined here make direct use of VM functions or data
+structures, so they can't be written with JNI and shouldn't really be in
+a separate shared library. Do not add additional functions here unless
+they need to access VM internals directly.
+
+All functions here either complete quickly or are used to enter a wait
+state, so we don't set the thread status to THREAD_NATIVE when executing
+these methods. This means that the GC will wait for these functions
+to finish. DO NOT perform long operations or blocking I/O in here.
+These methods should not be declared "synchronized", because we don't
+check for that flag when issuing the call.
+
+We use "late" binding on these, rather than explicit registration,
+because it's easier to handle the core system classes that way.
+
+The functions here use the DalvikNativeFunc prototype, but we can
+also treat them as DalvikBridgeFunc, which takes two extra arguments.
+The former represents the API that we're most likely to expose should
+JNI performance be deemed insufficient. The Bridge version is used as
+an optimization for a few high-volume Object calls, and should generally
+not be used as we may drop support for it at some point.
diff --git a/vm/native/dalvik_system_DexFile.c b/vm/native/dalvik_system_DexFile.c
new file mode 100644
index 0000000..25c9dfb
--- /dev/null
+++ b/vm/native/dalvik_system_DexFile.c
@@ -0,0 +1,454 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * dalvik.system.DexFile
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * Internal struct for managing DexFile.
+ */
+typedef struct DexOrJar {
+ char* fileName;
+ bool isDex;
+ bool okayToFree;
+ RawDexFile* pRawDexFile;
+ JarFile* pJarFile;
+} DexOrJar;
+
+/*
+ * (This is a dvmHashTableFree callback.)
+ */
+void dvmFreeDexOrJar(void* vptr)
+{
+ DexOrJar* pDexOrJar = (DexOrJar*) vptr;
+
+ LOGV("Freeing DexOrJar '%s'\n", pDexOrJar->fileName);
+
+ if (pDexOrJar->isDex)
+ dvmRawDexFileFree(pDexOrJar->pRawDexFile);
+ else
+ dvmJarFileFree(pDexOrJar->pJarFile);
+ free(pDexOrJar->fileName);
+ free(pDexOrJar);
+}
+
+/*
+ * (This is a dvmHashTableLookup compare func.)
+ *
+ * Args are DexOrJar*.
+ */
+static int hashcmpDexOrJar(const void* tableVal, const void* newVal)
+{
+ return (int) newVal - (int) tableVal;
+}
+
+/*
+ * Verify that the "cookie" is a DEX file we opened.
+ *
+ * Expects that the hash table will be *unlocked* here.
+ *
+ * If the cookie is invalid, we throw an exception and return "false".
+ */
+static bool validateCookie(int cookie)
+{
+ DexOrJar* pDexOrJar = (DexOrJar*) cookie;
+
+ LOGVV("+++ dex verifying cookie %p\n", pDexOrJar);
+
+ if (pDexOrJar == NULL)
+ return false;
+
+ u4 hash = dvmComputeUtf8Hash(pDexOrJar->fileName);
+ dvmHashTableLock(gDvm.userDexFiles);
+ void* result = dvmHashTableLookup(gDvm.userDexFiles, hash, pDexOrJar,
+ hashcmpDexOrJar, false);
+ dvmHashTableUnlock(gDvm.userDexFiles);
+ if (result == NULL) {
+ dvmThrowException("Ljava/lang/RuntimeException;",
+ "invalid DexFile cookie");
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * private static int openDexFile(String sourceName, String outputName,
+ * int flags) throws IOException
+ *
+ * Open a DEX file, returning a pointer to our internal data structure.
+ *
+ * "sourceName" should point to the "source" jar or DEX file.
+ *
+ * If "outputName" is NULL, the DEX code will automatically find the
+ * "optimized" version in the cache directory, creating it if necessary.
+ * If it's non-NULL, the specified file will be used instead.
+ *
+ * TODO: at present we will happily open the same file more than once.
+ * To optimize this away we could search for existing entries in the hash
+ * table and refCount them. Requires atomic ops or adding "synchronized"
+ * to the non-native code that calls here.
+ */
+static void Dalvik_dalvik_system_DexFile_openDexFile(const u4* args,
+ JValue* pResult)
+{
+ StringObject* sourceNameObj = (StringObject*) args[0];
+ StringObject* outputNameObj = (StringObject*) args[1];
+ DexOrJar* pDexOrJar = NULL;
+ JarFile* pJarFile;
+ RawDexFile* pRawDexFile;
+ char* sourceName;
+ char* outputName;
+
+ if (sourceNameObj == NULL) {
+ dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+ RETURN_VOID();
+ }
+
+ sourceName = dvmCreateCstrFromString(sourceNameObj);
+ if (outputNameObj != NULL)
+ outputName = dvmCreateCstrFromString(outputNameObj);
+ else
+ outputName = NULL;
+
+ /*
+ * We have to deal with the possibility that somebody might try to
+ * open one of our bootstrap class DEX files. The set of dependencies
+ * will be different, and hence the results of optimization might be
+ * different, which means we'd actually need to have two versions of
+ * the optimized DEX: one that only knows about part of the boot class
+ * path, and one that knows about everything in it. The latter might
+ * optimize field/method accesses based on a class that appeared later
+ * in the class path.
+ *
+ * We can't let the user-defined class loader open it and start using
+ * the classes, since the optimized form of the code skips some of
+ * the method and field resolution that we would ordinarily do, and
+ * we'd have the wrong semantics.
+ *
+ * We have to reject attempts to manually open a DEX file from the boot
+ * class path. The easiest way to do this is by filename, which works
+ * out because variations in name (e.g. "/system/framework/./ext.jar")
+ * result in us hitting a different dalvik-cache entry. It's also fine
+ * if the caller specifies their own output file.
+ */
+ if (dvmClassPathContains(gDvm.bootClassPath, sourceName)) {
+ LOGW("Refusing to reopen boot DEX '%s'\n", sourceName);
+ dvmThrowException("Ljava/io/IOException;",
+ "Re-opening BOOTCLASSPATH DEX files is not allowed");
+ free(sourceName);
+ RETURN_VOID();
+ }
+
+ /*
+ * Try to open it directly as a DEX. If that fails, try it as a Zip
+ * with a "classes.dex" inside.
+ */
+ if (dvmRawDexFileOpen(sourceName, outputName, &pRawDexFile, false) == 0) {
+ LOGV("Opening DEX file '%s' (DEX)\n", sourceName);
+
+ pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
+ pDexOrJar->isDex = true;
+ pDexOrJar->pRawDexFile = pRawDexFile;
+ } else if (dvmJarFileOpen(sourceName, outputName, &pJarFile, false) == 0) {
+ LOGV("Opening DEX file '%s' (Jar)\n", sourceName);
+
+ pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
+ pDexOrJar->isDex = false;
+ pDexOrJar->pJarFile = pJarFile;
+ } else {
+ LOGV("Unable to open DEX file '%s'\n", sourceName);
+ dvmThrowException("Ljava/io/IOException;", "unable to open DEX file");
+ }
+
+ if (pDexOrJar != NULL) {
+ pDexOrJar->fileName = sourceName;
+
+ /* add to hash table */
+ u4 hash = dvmComputeUtf8Hash(sourceName);
+ void* result;
+ dvmHashTableLock(gDvm.userDexFiles);
+ result = dvmHashTableLookup(gDvm.userDexFiles, hash, pDexOrJar,
+ hashcmpDexOrJar, true);
+ dvmHashTableUnlock(gDvm.userDexFiles);
+ if (result != pDexOrJar) {
+ LOGE("Pointer has already been added?\n");
+ dvmAbort();
+ }
+
+ pDexOrJar->okayToFree = true;
+ } else
+ free(sourceName);
+
+ RETURN_PTR(pDexOrJar);
+}
+
+/*
+ * private static void closeDexFile(int cookie)
+ *
+ * Release resources associated with a user-loaded DEX file.
+ */
+static void Dalvik_dalvik_system_DexFile_closeDexFile(const u4* args,
+ JValue* pResult)
+{
+ int cookie = args[0];
+ DexOrJar* pDexOrJar = (DexOrJar*) cookie;
+
+ if (pDexOrJar == NULL)
+ RETURN_VOID();
+
+ LOGV("Closing DEX file %p (%s)\n", pDexOrJar, pDexOrJar->fileName);
+
+ if (!validateCookie(cookie))
+ RETURN_VOID();
+
+ /*
+ * We can't just free arbitrary DEX files because they have bits and
+ * pieces of loaded classes. The only exception to this rule is if
+ * they were never used to load classes.
+ *
+ * If we can't free them here, dvmInternalNativeShutdown() will free
+ * them when the VM shuts down.
+ */
+ if (pDexOrJar->okayToFree) {
+ u4 hash = dvmComputeUtf8Hash(pDexOrJar->fileName);
+ dvmHashTableLock(gDvm.userDexFiles);
+ if (!dvmHashTableRemove(gDvm.userDexFiles, hash, pDexOrJar)) {
+ LOGW("WARNING: could not remove '%s' from DEX hash table\n",
+ pDexOrJar->fileName);
+ }
+ dvmHashTableUnlock(gDvm.userDexFiles);
+ LOGV("+++ freeing DexFile '%s' resources\n", pDexOrJar->fileName);
+ dvmFreeDexOrJar(pDexOrJar);
+ } else {
+ LOGV("+++ NOT freeing DexFile '%s' resources\n", pDexOrJar->fileName);
+ }
+
+ RETURN_VOID();
+}
+
+/*
+ * private static Class defineClass(String name, ClassLoader loader,
+ * int cookie, ProtectionDomain pd)
+ *
+ * Load a class from a DEX file. This is roughly equivalent to defineClass()
+ * in a regular VM -- it's invoked by the class loader to cause the
+ * creation of a specific class. The difference is that the search for and
+ * reading of the bytes is done within the VM.
+ *
+ * The class name is a "binary name", e.g. "java.lang.String".
+ *
+ * Returns a null pointer with no exception if the class was not found.
+ * Throws an exception on other failures.
+ */
+static void Dalvik_dalvik_system_DexFile_defineClass(const u4* args,
+ JValue* pResult)
+{
+ StringObject* nameObj = (StringObject*) args[0];
+ Object* loader = (Object*) args[1];
+ int cookie = args[2];
+ Object* pd = (Object*) args[3];
+ ClassObject* clazz = NULL;
+ DexOrJar* pDexOrJar = (DexOrJar*) cookie;
+ DvmDex* pDvmDex;
+ char* name;
+ char* descriptor;
+
+ name = dvmCreateCstrFromString(nameObj);
+ descriptor = dvmDotToDescriptor(name);
+ LOGV("--- Explicit class load '%s' 0x%08x\n", descriptor, cookie);
+ free(name);
+
+ if (!validateCookie(cookie))
+ RETURN_VOID();
+
+ if (pDexOrJar->isDex)
+ pDvmDex = dvmGetRawDexFileDex(pDexOrJar->pRawDexFile);
+ else
+ pDvmDex = dvmGetJarFileDex(pDexOrJar->pJarFile);
+
+ /* once we load something, we can't unmap the storage */
+ pDexOrJar->okayToFree = false;
+
+ clazz = dvmDefineClass(pDvmDex, descriptor, loader);
+ Thread* self = dvmThreadSelf();
+ if (dvmCheckException(self)) {
+ /*
+ * If we threw a "class not found" exception, stifle it, since the
+ * contract in the higher method says we simply return null if
+ * the class is not found.
+ */
+ Object* excep = dvmGetException(self);
+ if (strcmp(excep->clazz->descriptor,
+ "Ljava/lang/ClassNotFoundException;") == 0 ||
+ strcmp(excep->clazz->descriptor,
+ "Ljava/lang/NoClassDefFoundError;") == 0)
+ {
+ dvmClearException(self);
+ }
+ clazz = NULL;
+ }
+
+ /*
+ * Set the ProtectionDomain -- do we need this to happen before we
+ * link the class and make it available? If so, we need to pass it
+ * through dvmDefineClass (and figure out some other
+ * stuff, like where it comes from for bootstrap classes).
+ */
+ if (clazz != NULL) {
+ //LOGI("SETTING pd '%s' to %p\n", clazz->descriptor, pd);
+ dvmSetFieldObject((Object*) clazz, gDvm.offJavaLangClass_pd, pd);
+ }
+
+ free(descriptor);
+ RETURN_PTR(clazz);
+}
+
+/*
+ * private static String[] getClassNameList(int cookie)
+ *
+ * Returns a String array that holds the names of all classes in the
+ * specified DEX file.
+ */
+static void Dalvik_dalvik_system_DexFile_getClassNameList(const u4* args,
+ JValue* pResult)
+{
+ int cookie = args[0];
+ DexOrJar* pDexOrJar = (DexOrJar*) cookie;
+ DvmDex* pDvmDex;
+ DexFile* pDexFile;
+ ArrayObject* stringArray;
+ Thread* self = dvmThreadSelf();
+
+ if (!validateCookie(cookie))
+ RETURN_VOID();
+
+ if (pDexOrJar->isDex)
+ pDvmDex = dvmGetRawDexFileDex(pDexOrJar->pRawDexFile);
+ else
+ pDvmDex = dvmGetJarFileDex(pDexOrJar->pJarFile);
+ assert(pDvmDex != NULL);
+ pDexFile = pDvmDex->pDexFile;
+
+ int count = pDexFile->pHeader->classDefsSize;
+ stringArray = dvmAllocObjectArray(gDvm.classJavaLangString, count,
+ ALLOC_DEFAULT);
+ if (stringArray == NULL) {
+ /* probably OOM */
+ LOGD("Failed allocating array of %d strings\n", count);
+ assert(dvmCheckException(self));
+ RETURN_VOID();
+ }
+
+ int i;
+ for (i = 0; i < count; i++) {
+ const DexClassDef* pClassDef = dexGetClassDef(pDexFile, i);
+ const char* descriptor =
+ dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
+
+ char* className = dvmDescriptorToDot(descriptor);
+ StringObject* str = dvmCreateStringFromCstr(className);
+ dvmSetObjectArrayElement(stringArray, i, (Object *)str);
+ dvmReleaseTrackedAlloc((Object *)str, self);
+ free(className);
+ }
+
+ dvmReleaseTrackedAlloc((Object*)stringArray, self);
+ RETURN_PTR(stringArray);
+}
+
+/*
+ * public static boolean isDexOptNeeded(String apkName)
+ * throws FileNotFoundException, IOException
+ *
+ * Returns true if the VM believes that the apk/jar file is out of date
+ * and should be passed through "dexopt" again.
+ *
+ * @param fileName the absolute path to the apk/jar file to examine.
+ * @return true if dexopt should be called on the file, false otherwise.
+ * @throws java.io.FileNotFoundException if fileName is not readable,
+ * not a file, or not present.
+ * @throws java.io.IOException if fileName is not a valid apk/jar file or
+ * if problems occur while parsing it.
+ * @throws java.lang.NullPointerException if fileName is null.
+ * @throws dalvik.system.StaleDexCacheError if the optimized dex file
+ * is stale but exists on a read-only partition.
+ */
+static void Dalvik_dalvik_system_DexFile_isDexOptNeeded(const u4* args,
+ JValue* pResult)
+{
+ StringObject* nameObj = (StringObject*) args[0];
+ char* name;
+ DexCacheStatus status;
+ int result;
+
+ name = dvmCreateCstrFromString(nameObj);
+ if (name == NULL) {
+ dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+ RETURN_VOID();
+ }
+ if (access(name, R_OK) != 0) {
+ dvmThrowException("Ljava/io/FileNotFoundException;", name);
+ free(name);
+ RETURN_VOID();
+ }
+ status = dvmDexCacheStatus(name);
+ LOGV("dvmDexCacheStatus(%s) returned %d\n", name, status);
+
+ result = true;
+ switch (status) {
+ default: //FALLTHROUGH
+ case DEX_CACHE_BAD_ARCHIVE:
+ dvmThrowException("Ljava/io/IOException;", name);
+ result = -1;
+ break;
+ case DEX_CACHE_OK:
+ result = false;
+ break;
+ case DEX_CACHE_STALE:
+ result = true;
+ break;
+ case DEX_CACHE_STALE_ODEX:
+ dvmThrowException("Ldalvik/system/StaleDexCacheError;", name);
+ result = -1;
+ break;
+ }
+ free(name);
+
+ if (result >= 0) {
+ RETURN_BOOLEAN(result);
+ } else {
+ RETURN_VOID();
+ }
+}
+
+const DalvikNativeMethod dvm_dalvik_system_DexFile[] = {
+ { "openDexFile", "(Ljava/lang/String;Ljava/lang/String;I)I",
+ Dalvik_dalvik_system_DexFile_openDexFile },
+ { "closeDexFile", "(I)V",
+ Dalvik_dalvik_system_DexFile_closeDexFile },
+ { "defineClass", "(Ljava/lang/String;Ljava/lang/ClassLoader;ILjava/security/ProtectionDomain;)Ljava/lang/Class;",
+ Dalvik_dalvik_system_DexFile_defineClass },
+ { "getClassNameList", "(I)[Ljava/lang/String;",
+ Dalvik_dalvik_system_DexFile_getClassNameList },
+ { "isDexOptNeeded", "(Ljava/lang/String;)Z",
+ Dalvik_dalvik_system_DexFile_isDexOptNeeded },
+ { NULL, NULL, NULL },
+};
diff --git a/vm/native/dalvik_system_VMDebug.c b/vm/native/dalvik_system_VMDebug.c
new file mode 100644
index 0000000..192a8f2
--- /dev/null
+++ b/vm/native/dalvik_system_VMDebug.c
@@ -0,0 +1,965 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * dalvik.system.VMDebug
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+
+/*
+ * Extracts the fd from a FileDescriptor object.
+ *
+ * If an error is encountered, or the extracted descriptor is numerically
+ * invalid, this returns -1 with an exception raised.
+ */
+static int getFileDescriptor(Object* obj)
+{
+ assert(obj != NULL);
+ assert(strcmp(obj->clazz->descriptor, "Ljava/io/FileDescriptor;") == 0);
+
+ InstField* field = dvmFindInstanceField(obj->clazz, "descriptor", "I");
+ if (field == NULL) {
+ dvmThrowException("Ljava/lang/NoSuchFieldException;",
+ "No FileDescriptor.descriptor field");
+ return -1;
+ }
+
+ int fd = dvmGetFieldInt(obj, field->byteOffset);
+ if (fd < 0) {
+ dvmThrowExceptionFmt("Ljava/lang/RuntimeException;",
+ "Invalid file descriptor");
+ return -1;
+ }
+
+ return fd;
+}
+
+/*
+ * Convert an array of char* into a String[].
+ *
+ * Returns NULL on failure, with an exception raised.
+ */
+static ArrayObject* convertStringArray(char** strings, size_t count)
+{
+ Thread* self = dvmThreadSelf();
+
+ /*
+ * Allocate an array to hold the String objects.
+ */
+ ClassObject* stringArrayClass =
+ dvmFindArrayClass("[Ljava/lang/String;", NULL);
+ if (stringArrayClass == NULL) {
+ /* shouldn't happen */
+ LOGE("Unable to find [Ljava/lang/String;\n");
+ dvmAbort();
+ }
+
+ ArrayObject* stringArray =
+ dvmAllocArrayByClass(stringArrayClass, count, ALLOC_DEFAULT);
+ if (stringArray == NULL) {
+ /* probably OOM */
+ LOGD("Failed allocating array of %d strings\n", count);
+ assert(dvmCheckException(self));
+ return NULL;
+ }
+
+ /*
+ * Create the individual String objects and add them to the array.
+ */
+ size_t i;
+ for (i = 0; i < count; i++) {
+ Object *str =
+ (Object *)dvmCreateStringFromCstr(strings[i]);
+ if (str == NULL) {
+ /* probably OOM; drop out now */
+ assert(dvmCheckException(self));
+ dvmReleaseTrackedAlloc((Object*)stringArray, self);
+ return NULL;
+ }
+ dvmSetObjectArrayElement(stringArray, i, str);
+ /* stored in tracked array, okay to release */
+ dvmReleaseTrackedAlloc(str, self);
+ }
+
+ dvmReleaseTrackedAlloc((Object*)stringArray, self);
+ return stringArray;
+}
+
+/*
+ * static String[] getVmFeatureList()
+ *
+ * Return a set of strings describing available VM features (this is chiefly
+ * of interest to DDMS). Some features may be controlled by compile-time
+ * or command-line flags.
+ */
+static void Dalvik_dalvik_system_VMDebug_getVmFeatureList(const u4* args,
+ JValue* pResult)
+{
+ static const int MAX_FEATURE_COUNT = 10;
+ char* features[MAX_FEATURE_COUNT];
+ int idx = 0;
+
+ /* VM responds to DDMS method profiling requests */
+ features[idx++] = "method-trace-profiling";
+ features[idx++] = "method-trace-profiling-streaming";
+#ifdef WITH_HPROF
+ /* VM responds to DDMS heap dump requests */
+ features[idx++] = "hprof-heap-dump";
+ features[idx++] = "hprof-heap-dump-streaming";
+#endif
+
+ assert(idx <= MAX_FEATURE_COUNT);
+
+ LOGV("+++ sending up %d features\n", idx);
+ ArrayObject* arrayObj = convertStringArray(features, idx);
+ RETURN_PTR(arrayObj); /* will be null on OOM */
+}
+
+
+/* These must match the values in dalvik.system.VMDebug.
+ */
+enum {
+ KIND_ALLOCATED_OBJECTS = 1<<0,
+ KIND_ALLOCATED_BYTES = 1<<1,
+ KIND_FREED_OBJECTS = 1<<2,
+ KIND_FREED_BYTES = 1<<3,
+ KIND_GC_INVOCATIONS = 1<<4,
+ KIND_CLASS_INIT_COUNT = 1<<5,
+ KIND_CLASS_INIT_TIME = 1<<6,
+#if PROFILE_EXTERNAL_ALLOCATIONS
+ KIND_EXT_ALLOCATED_OBJECTS = 1<<12,
+ KIND_EXT_ALLOCATED_BYTES = 1<<13,
+ KIND_EXT_FREED_OBJECTS = 1<<14,
+ KIND_EXT_FREED_BYTES = 1<<15,
+#endif // PROFILE_EXTERNAL_ALLOCATIONS
+
+ KIND_GLOBAL_ALLOCATED_OBJECTS = KIND_ALLOCATED_OBJECTS,
+ KIND_GLOBAL_ALLOCATED_BYTES = KIND_ALLOCATED_BYTES,
+ KIND_GLOBAL_FREED_OBJECTS = KIND_FREED_OBJECTS,
+ KIND_GLOBAL_FREED_BYTES = KIND_FREED_BYTES,
+ KIND_GLOBAL_GC_INVOCATIONS = KIND_GC_INVOCATIONS,
+ KIND_GLOBAL_CLASS_INIT_COUNT = KIND_CLASS_INIT_COUNT,
+ KIND_GLOBAL_CLASS_INIT_TIME = KIND_CLASS_INIT_TIME,
+#if PROFILE_EXTERNAL_ALLOCATIONS
+ KIND_GLOBAL_EXT_ALLOCATED_OBJECTS = KIND_EXT_ALLOCATED_OBJECTS,
+ KIND_GLOBAL_EXT_ALLOCATED_BYTES = KIND_EXT_ALLOCATED_BYTES,
+ KIND_GLOBAL_EXT_FREED_OBJECTS = KIND_EXT_FREED_OBJECTS,
+ KIND_GLOBAL_EXT_FREED_BYTES = KIND_EXT_FREED_BYTES,
+#endif // PROFILE_EXTERNAL_ALLOCATIONS
+
+ KIND_THREAD_ALLOCATED_OBJECTS = KIND_ALLOCATED_OBJECTS << 16,
+ KIND_THREAD_ALLOCATED_BYTES = KIND_ALLOCATED_BYTES << 16,
+ KIND_THREAD_FREED_OBJECTS = KIND_FREED_OBJECTS << 16,
+ KIND_THREAD_FREED_BYTES = KIND_FREED_BYTES << 16,
+#if PROFILE_EXTERNAL_ALLOCATIONS
+ KIND_THREAD_EXT_ALLOCATED_OBJECTS = KIND_EXT_ALLOCATED_OBJECTS << 16,
+ KIND_THREAD_EXT_ALLOCATED_BYTES = KIND_EXT_ALLOCATED_BYTES << 16,
+ KIND_THREAD_EXT_FREED_OBJECTS = KIND_EXT_FREED_OBJECTS << 16,
+ KIND_THREAD_EXT_FREED_BYTES = KIND_EXT_FREED_BYTES << 16,
+#endif // PROFILE_EXTERNAL_ALLOCATIONS
+ KIND_THREAD_GC_INVOCATIONS = KIND_GC_INVOCATIONS << 16,
+
+ // TODO: failedAllocCount, failedAllocSize
+};
+
+#define KIND_ALL_COUNTS 0xffffffff
+
+/*
+ * Zero out the specified fields.
+ */
+static void clearAllocProfStateFields(AllocProfState *allocProf,
+ unsigned int kinds)
+{
+ if (kinds & KIND_ALLOCATED_OBJECTS) {
+ allocProf->allocCount = 0;
+ }
+ if (kinds & KIND_ALLOCATED_BYTES) {
+ allocProf->allocSize = 0;
+ }
+ if (kinds & KIND_FREED_OBJECTS) {
+ allocProf->freeCount = 0;
+ }
+ if (kinds & KIND_FREED_BYTES) {
+ allocProf->freeSize = 0;
+ }
+ if (kinds & KIND_GC_INVOCATIONS) {
+ allocProf->gcCount = 0;
+ }
+ if (kinds & KIND_CLASS_INIT_COUNT) {
+ allocProf->classInitCount = 0;
+ }
+ if (kinds & KIND_CLASS_INIT_TIME) {
+ allocProf->classInitTime = 0;
+ }
+#if PROFILE_EXTERNAL_ALLOCATIONS
+ if (kinds & KIND_EXT_ALLOCATED_OBJECTS) {
+ allocProf->externalAllocCount = 0;
+ }
+ if (kinds & KIND_EXT_ALLOCATED_BYTES) {
+ allocProf->externalAllocSize = 0;
+ }
+ if (kinds & KIND_EXT_FREED_OBJECTS) {
+ allocProf->externalFreeCount = 0;
+ }
+ if (kinds & KIND_EXT_FREED_BYTES) {
+ allocProf->externalFreeSize = 0;
+ }
+#endif // PROFILE_EXTERNAL_ALLOCATIONS
+}
+
+/*
+ * static void startAllocCounting()
+ *
+ * Reset the counters and enable counting.
+ *
+ * TODO: this currently only resets the per-thread counters for the current
+ * thread. If we actually start using the per-thread counters we'll
+ * probably want to fix this.
+ */
+static void Dalvik_dalvik_system_VMDebug_startAllocCounting(const u4* args,
+ JValue* pResult)
+{
+ UNUSED_PARAMETER(args);
+
+ clearAllocProfStateFields(&gDvm.allocProf, KIND_ALL_COUNTS);
+ clearAllocProfStateFields(&dvmThreadSelf()->allocProf, KIND_ALL_COUNTS);
+ dvmStartAllocCounting();
+ RETURN_VOID();
+}
+
+/*
+ * public static void stopAllocCounting()
+ */
+static void Dalvik_dalvik_system_VMDebug_stopAllocCounting(const u4* args,
+ JValue* pResult)
+{
+ UNUSED_PARAMETER(args);
+
+ dvmStopAllocCounting();
+ RETURN_VOID();
+}
+
+/*
+ * private static int getAllocCount(int kind)
+ */
+static void Dalvik_dalvik_system_VMDebug_getAllocCount(const u4* args,
+ JValue* pResult)
+{
+ AllocProfState *allocProf;
+ unsigned int kind = args[0];
+ if (kind < (1<<16)) {
+ allocProf = &gDvm.allocProf;
+ } else {
+ allocProf = &dvmThreadSelf()->allocProf;
+ kind >>= 16;
+ }
+ switch (kind) {
+ case KIND_ALLOCATED_OBJECTS:
+ pResult->i = allocProf->allocCount;
+ break;
+ case KIND_ALLOCATED_BYTES:
+ pResult->i = allocProf->allocSize;
+ break;
+ case KIND_FREED_OBJECTS:
+ pResult->i = allocProf->freeCount;
+ break;
+ case KIND_FREED_BYTES:
+ pResult->i = allocProf->freeSize;
+ break;
+ case KIND_GC_INVOCATIONS:
+ pResult->i = allocProf->gcCount;
+ break;
+ case KIND_CLASS_INIT_COUNT:
+ pResult->i = allocProf->classInitCount;
+ break;
+ case KIND_CLASS_INIT_TIME:
+ /* convert nsec to usec, reduce to 32 bits */
+ pResult->i = (int) (allocProf->classInitTime / 1000);
+ break;
+#if PROFILE_EXTERNAL_ALLOCATIONS
+ case KIND_EXT_ALLOCATED_OBJECTS:
+ pResult->i = allocProf->externalAllocCount;
+ break;
+ case KIND_EXT_ALLOCATED_BYTES:
+ pResult->i = allocProf->externalAllocSize;
+ break;
+ case KIND_EXT_FREED_OBJECTS:
+ pResult->i = allocProf->externalFreeCount;
+ break;
+ case KIND_EXT_FREED_BYTES:
+ pResult->i = allocProf->externalFreeSize;
+ break;
+#endif // PROFILE_EXTERNAL_ALLOCATIONS
+ default:
+ assert(false);
+ pResult->i = -1;
+ }
+}
+
+/*
+ * public static void resetAllocCount(int kinds)
+ */
+static void Dalvik_dalvik_system_VMDebug_resetAllocCount(const u4* args,
+ JValue* pResult)
+{
+ unsigned int kinds = args[0];
+ clearAllocProfStateFields(&gDvm.allocProf, kinds & 0xffff);
+ clearAllocProfStateFields(&dvmThreadSelf()->allocProf, kinds >> 16);
+ RETURN_VOID();
+}
+
+/*
+ * static void startMethodTracingNative(String traceFileName,
+ * FileDescriptor fd, int bufferSize, int flags)
+ *
+ * Start method trace profiling.
+ *
+ * If both "traceFileName" and "fd" are null, the result will be sent
+ * directly to DDMS. (The non-DDMS versions of the calls are expected
+ * to enforce non-NULL filenames.)
+ */
+static void Dalvik_dalvik_system_VMDebug_startMethodTracingNative(const u4* args,
+ JValue* pResult)
+{
+ StringObject* traceFileStr = (StringObject*) args[0];
+ Object* traceFd = (Object*) args[1];
+ int bufferSize = args[2];
+ int flags = args[3];
+
+ if (bufferSize == 0) {
+ // Default to 8MB per the documentation.
+ bufferSize = 8 * 1024 * 1024;
+ }
+
+ if (bufferSize < 1024) {
+ dvmThrowException("Ljava/lang/IllegalArgumentException;", NULL);
+ RETURN_VOID();
+ }
+
+ char* traceFileName = NULL;
+ if (traceFileStr != NULL)
+ traceFileName = dvmCreateCstrFromString(traceFileStr);
+
+ int fd = -1;
+ if (traceFd != NULL) {
+ int origFd = getFileDescriptor(traceFd);
+ if (origFd < 0)
+ RETURN_VOID();
+
+ fd = dup(origFd);
+ if (fd < 0) {
+ dvmThrowExceptionFmt("Ljava/lang/RuntimeException;",
+ "dup(%d) failed: %s", origFd, strerror(errno));
+ RETURN_VOID();
+ }
+ }
+
+ dvmMethodTraceStart(traceFileName != NULL ? traceFileName : "[DDMS]",
+ fd, bufferSize, flags, (traceFileName == NULL && fd == -1));
+ free(traceFileName);
+ RETURN_VOID();
+}
+
+/*
+ * static boolean isMethodTracingActive()
+ *
+ * Determine whether method tracing is currently active.
+ */
+static void Dalvik_dalvik_system_VMDebug_isMethodTracingActive(const u4* args,
+ JValue* pResult)
+{
+ UNUSED_PARAMETER(args);
+
+ RETURN_BOOLEAN(dvmIsMethodTraceActive());
+}
+
+/*
+ * static void stopMethodTracing()
+ *
+ * Stop method tracing.
+ */
+static void Dalvik_dalvik_system_VMDebug_stopMethodTracing(const u4* args,
+ JValue* pResult)
+{
+ UNUSED_PARAMETER(args);
+
+ dvmMethodTraceStop();
+ RETURN_VOID();
+}
+
+/*
+ * static void startEmulatorTracing()
+ *
+ * Start sending method trace info to the emulator.
+ */
+static void Dalvik_dalvik_system_VMDebug_startEmulatorTracing(const u4* args,
+ JValue* pResult)
+{
+ UNUSED_PARAMETER(args);
+
+ dvmEmulatorTraceStart();
+ RETURN_VOID();
+}
+
+/*
+ * static void stopEmulatorTracing()
+ *
+ * Start sending method trace info to the emulator.
+ */
+static void Dalvik_dalvik_system_VMDebug_stopEmulatorTracing(const u4* args,
+ JValue* pResult)
+{
+ UNUSED_PARAMETER(args);
+
+ dvmEmulatorTraceStop();
+ RETURN_VOID();
+}
+
+/*
+ * static int setAllocationLimit(int limit)
+ *
+ * Set the current allocation limit in this thread. Return the previous
+ * value.
+ */
+static void Dalvik_dalvik_system_VMDebug_setAllocationLimit(const u4* args,
+ JValue* pResult)
+{
+#if defined(WITH_ALLOC_LIMITS)
+ gDvm.checkAllocLimits = true;
+
+ Thread* self = dvmThreadSelf();
+ int newLimit = args[0];
+ int oldLimit = self->allocLimit;
+
+ if (newLimit < -1) {
+ LOGE("WARNING: bad limit request (%d)\n", newLimit);
+ newLimit = -1;
+ }
+ self->allocLimit = newLimit;
+ RETURN_INT(oldLimit);
+#else
+ UNUSED_PARAMETER(args);
+ RETURN_INT(-1);
+#endif
+}
+
+/*
+ * static int setGlobalAllocationLimit(int limit)
+ *
+ * Set the allocation limit for this process. Returns the previous value.
+ */
+static void Dalvik_dalvik_system_VMDebug_setGlobalAllocationLimit(const u4* args,
+ JValue* pResult)
+{
+#if defined(WITH_ALLOC_LIMITS)
+ gDvm.checkAllocLimits = true;
+
+ int newLimit = args[0];
+ int oldLimit = gDvm.allocationLimit;
+
+ if (newLimit < -1 || newLimit > 0) {
+ LOGE("WARNING: bad limit request (%d)\n", newLimit);
+ newLimit = -1;
+ }
+ // TODO: should use an atomic swap here
+ gDvm.allocationLimit = newLimit;
+ RETURN_INT(oldLimit);
+#else
+ UNUSED_PARAMETER(args);
+ RETURN_INT(-1);
+#endif
+}
+
+/*
+ * static boolean isDebuggerConnected()
+ *
+ * Returns "true" if a debugger is attached.
+ */
+static void Dalvik_dalvik_system_VMDebug_isDebuggerConnected(const u4* args,
+ JValue* pResult)
+{
+ UNUSED_PARAMETER(args);
+
+ RETURN_BOOLEAN(dvmDbgIsDebuggerConnected());
+}
+
+/*
+ * static boolean isDebuggingEnabled()
+ *
+ * Returns "true" if debugging is enabled.
+ */
+static void Dalvik_dalvik_system_VMDebug_isDebuggingEnabled(const u4* args,
+ JValue* pResult)
+{
+ UNUSED_PARAMETER(args);
+
+ RETURN_BOOLEAN(gDvm.jdwpConfigured);
+}
+
+/*
+ * static long lastDebuggerActivity()
+ *
+ * Returns the time, in msec, since we last had an interaction with the
+ * debugger (send or receive).
+ */
+static void Dalvik_dalvik_system_VMDebug_lastDebuggerActivity(const u4* args,
+ JValue* pResult)
+{
+ UNUSED_PARAMETER(args);
+
+ RETURN_LONG(dvmDbgLastDebuggerActivity());
+}
+
+/*
+ * static void startInstructionCounting()
+ */
+static void Dalvik_dalvik_system_VMDebug_startInstructionCounting(const u4* args,
+ JValue* pResult)
+{
+ dvmStartInstructionCounting();
+ RETURN_VOID();
+}
+
+/*
+ * static void stopInstructionCounting()
+ */
+static void Dalvik_dalvik_system_VMDebug_stopInstructionCounting(const u4* args,
+ JValue* pResult)
+{
+ dvmStopInstructionCounting();
+ RETURN_VOID();
+}
+
+/*
+ * static boolean getInstructionCount(int[] counts)
+ *
+ * Grab a copy of the global instruction count array.
+ *
+ * Since the instruction counts aren't synchronized, we use sched_yield
+ * to improve our chances of finishing without contention. (Only makes
+ * sense on a uniprocessor.)
+ */
+static void Dalvik_dalvik_system_VMDebug_getInstructionCount(const u4* args,
+ JValue* pResult)
+{
+ ArrayObject* countArray = (ArrayObject*) args[0];
+ int* storage;
+
+ storage = (int*) countArray->contents;
+ sched_yield();
+ memcpy(storage, gDvm.executedInstrCounts,
+ kNumDalvikInstructions * sizeof(int));
+ RETURN_VOID();
+}
+
+/*
+ * static boolean resetInstructionCount()
+ *
+ * Reset the instruction count array.
+ */
+static void Dalvik_dalvik_system_VMDebug_resetInstructionCount(const u4* args,
+ JValue* pResult)
+{
+ sched_yield();
+ memset(gDvm.executedInstrCounts, 0, kNumDalvikInstructions * sizeof(int));
+ RETURN_VOID();
+}
+
+/*
+ * static void printLoadedClasses(int flags)
+ *
+ * Dump the list of loaded classes.
+ */
+static void Dalvik_dalvik_system_VMDebug_printLoadedClasses(const u4* args,
+ JValue* pResult)
+{
+ int flags = args[0];
+
+ dvmDumpAllClasses(flags);
+
+ RETURN_VOID();
+}
+
+/*
+ * static int getLoadedClassCount()
+ *
+ * Return the number of loaded classes
+ */
+static void Dalvik_dalvik_system_VMDebug_getLoadedClassCount(const u4* args,
+ JValue* pResult)
+{
+ int count;
+
+ UNUSED_PARAMETER(args);
+
+ count = dvmGetNumLoadedClasses();
+
+ RETURN_INT(count);
+}
+
+/*
+ * Returns the thread-specific CPU-time clock value for the current thread,
+ * or -1 if the feature isn't supported.
+ */
+static void Dalvik_dalvik_system_VMDebug_threadCpuTimeNanos(const u4* args,
+ JValue* pResult)
+{
+ jlong result;
+
+#ifdef HAVE_POSIX_CLOCKS
+ struct timespec now;
+ clock_gettime(CLOCK_THREAD_CPUTIME_ID, &now);
+ result = (jlong) (now.tv_sec*1000000000LL + now.tv_nsec);
+#else
+ result = (jlong) -1;
+#endif
+
+ RETURN_LONG(result);
+}
+
+/*
+ * static void dumpHprofData(String fileName, FileDescriptor fd)
+ *
+ * Cause "hprof" data to be dumped. We can throw an IOException if an
+ * error occurs during file handling.
+ */
+static void Dalvik_dalvik_system_VMDebug_dumpHprofData(const u4* args,
+ JValue* pResult)
+{
+#ifdef WITH_HPROF
+ StringObject* fileNameStr = (StringObject*) args[0];
+ Object* fileDescriptor = (Object*) args[1];
+ char* fileName;
+ int result;
+
+ /*
+ * Only one of these may be NULL.
+ */
+ if (fileNameStr == NULL && fileDescriptor == NULL) {
+ dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+ RETURN_VOID();
+ }
+
+ if (fileNameStr != NULL) {
+ fileName = dvmCreateCstrFromString(fileNameStr);
+ if (fileName == NULL) {
+ /* unexpected -- malloc failure? */
+ dvmThrowException("Ljava/lang/RuntimeException;", "malloc failure?");
+ RETURN_VOID();
+ }
+ } else {
+ fileName = strdup("[fd]");
+ }
+
+ int fd = -1;
+ if (fileDescriptor != NULL) {
+ fd = getFileDescriptor(fileDescriptor);
+ if (fd < 0)
+ RETURN_VOID();
+ }
+
+ result = hprofDumpHeap(fileName, fd, false);
+ free(fileName);
+
+ if (result != 0) {
+ /* ideally we'd throw something more specific based on actual failure */
+ dvmThrowException("Ljava/lang/RuntimeException;",
+ "Failure during heap dump -- check log output for details");
+ RETURN_VOID();
+ }
+#else
+ dvmThrowException("Ljava/lang/UnsupportedOperationException;", NULL);
+#endif
+
+ RETURN_VOID();
+}
+
+/*
+ * static void dumpHprofDataDdms()
+ *
+ * Cause "hprof" data to be computed and sent directly to DDMS.
+ */
+static void Dalvik_dalvik_system_VMDebug_dumpHprofDataDdms(const u4* args,
+ JValue* pResult)
+{
+#ifdef WITH_HPROF
+ int result;
+
+ result = hprofDumpHeap("[DDMS]", -1, true);
+
+ if (result != 0) {
+ /* ideally we'd throw something more specific based on actual failure */
+ dvmThrowException("Ljava/lang/RuntimeException;",
+ "Failure during heap dump -- check log output for details");
+ RETURN_VOID();
+ }
+#else
+ dvmThrowException("Ljava/lang/UnsupportedOperationException;", NULL);
+#endif
+
+ RETURN_VOID();
+}
+
+/*
+ * static boolean cacheRegisterMap(String classAndMethodDescr)
+ *
+ * If the specified class is loaded, and the named method exists, ensure
+ * that the method's register map is ready for use. If the class/method
+ * cannot be found, nothing happens.
+ *
+ * This can improve the zygote's sharing of compressed register maps. Do
+ * this after class preloading.
+ *
+ * Returns true if the register map is cached and ready, either as a result
+ * of this call or earlier activity. Returns false if the class isn't loaded,
+ * if the method couldn't be found, or if the method has no register map.
+ *
+ * (Uncomment logs in dvmGetExpandedRegisterMap0() to gather stats.)
+ */
+static void Dalvik_dalvik_system_VMDebug_cacheRegisterMap(const u4* args,
+ JValue* pResult)
+{
+ StringObject* classAndMethodDescStr = (StringObject*) args[0];
+ ClassObject* clazz;
+ bool result = false;
+
+ if (classAndMethodDescStr == NULL) {
+ dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+ RETURN_VOID();
+ }
+
+ char* classAndMethodDesc = NULL;
+
+ /*
+ * Pick the string apart. We have a local copy, so just modify it
+ * in place.
+ */
+ classAndMethodDesc = dvmCreateCstrFromString(classAndMethodDescStr);
+
+ char* methodName = strchr(classAndMethodDesc, '.');
+ if (methodName == NULL) {
+ dvmThrowException("Ljava/lang/RuntimeException;",
+ "method name not found in string");
+ RETURN_VOID();
+ }
+ *methodName++ = '\0';
+
+ char* methodDescr = strchr(methodName, ':');
+ if (methodDescr == NULL) {
+ dvmThrowException("Ljava/lang/RuntimeException;",
+ "method descriptor not found in string");
+ RETURN_VOID();
+ }
+ *methodDescr++ = '\0';
+
+ //LOGD("GOT: %s %s %s\n", classAndMethodDesc, methodName, methodDescr);
+
+ /*
+ * Find the class, but only if it's already loaded.
+ */
+ clazz = dvmLookupClass(classAndMethodDesc, NULL, false);
+ if (clazz == NULL) {
+ LOGD("Class %s not found in bootstrap loader\n", classAndMethodDesc);
+ goto bail;
+ }
+
+ Method* method;
+
+ /*
+ * Find the method, which could be virtual or direct, defined directly
+ * or inherited.
+ */
+ if (methodName[0] == '<') {
+ /*
+ * Constructor or class initializer. Only need to examine the
+ * "direct" list, and don't need to search up the class hierarchy.
+ */
+ method = dvmFindDirectMethodByDescriptor(clazz, methodName,
+ methodDescr);
+ } else {
+ /*
+ * Try both lists, and scan up the tree.
+ */
+ method = dvmFindVirtualMethodHierByDescriptor(clazz, methodName,
+ methodDescr);
+ if (method == NULL) {
+ method = dvmFindDirectMethodHierByDescriptor(clazz, methodName,
+ methodDescr);
+ }
+ }
+
+ if (method != NULL) {
+ /*
+ * Got it. See if there's a register map here.
+ */
+ const RegisterMap* pMap;
+ pMap = dvmGetExpandedRegisterMap(method);
+ if (pMap == NULL) {
+ LOGV("No map for %s.%s %s\n",
+ classAndMethodDesc, methodName, methodDescr);
+ } else {
+ LOGV("Found map %s.%s %s\n",
+ classAndMethodDesc, methodName, methodDescr);
+ result = true;
+ }
+ } else {
+ LOGV("Unable to find %s.%s %s\n",
+ classAndMethodDesc, methodName, methodDescr);
+ }
+
+bail:
+ free(classAndMethodDesc);
+ RETURN_BOOLEAN(result);
+}
+
+/*
+ * static void dumpReferenceTables()
+ */
+static void Dalvik_dalvik_system_VMDebug_dumpReferenceTables(const u4* args,
+ JValue* pResult)
+{
+ UNUSED_PARAMETER(args);
+ UNUSED_PARAMETER(pResult);
+
+ LOGI("--- reference table dump ---\n");
+ dvmDumpJniReferenceTables();
+ // could dump thread's internalLocalRefTable, probably not useful
+ // ditto for thread's jniMonitorRefTable
+ LOGI("---\n");
+ RETURN_VOID();
+}
+
+/*
+ * static void crash()
+ *
+ * Dump the current thread's interpreted stack and abort the VM. Useful
+ * for seeing both interpreted and native stack traces.
+ *
+ * (Might want to restrict this to debuggable processes as a security
+ * measure, or check SecurityManager.checkExit().)
+ */
+static void Dalvik_dalvik_system_VMDebug_crash(const u4* args,
+ JValue* pResult)
+{
+ UNUSED_PARAMETER(args);
+ UNUSED_PARAMETER(pResult);
+
+ LOGW("Crashing VM on request\n");
+ dvmDumpThread(dvmThreadSelf(), false);
+ dvmAbort();
+}
+
+/*
+ * static void infopoint(int id)
+ *
+ * Provide a hook for gdb to hang to so that the VM can be stopped when
+ * user-tagged source locations are being executed.
+ */
+static void Dalvik_dalvik_system_VMDebug_infopoint(const u4* args,
+ JValue* pResult)
+{
+ gDvm.nativeDebuggerActive = true;
+
+ LOGD("VMDebug infopoint %d hit", args[0]);
+
+ gDvm.nativeDebuggerActive = false;
+ RETURN_VOID();
+}
+
+static void Dalvik_dalvik_system_VMDebug_countInstancesOfClass(const u4* args,
+ JValue* pResult)
+{
+ ClassObject* clazz = (ClassObject*)args[0];
+ bool countAssignable = args[1];
+ if (clazz == NULL) {
+ RETURN_LONG(0);
+ }
+ if (countAssignable) {
+ size_t count = dvmCountAssignableInstancesOfClass(clazz);
+ RETURN_LONG((long long)count);
+ } else {
+ size_t count = dvmCountInstancesOfClass(clazz);
+ RETURN_LONG((long long)count);
+ }
+}
+
+const DalvikNativeMethod dvm_dalvik_system_VMDebug[] = {
+ { "getVmFeatureList", "()[Ljava/lang/String;",
+ Dalvik_dalvik_system_VMDebug_getVmFeatureList },
+ { "getAllocCount", "(I)I",
+ Dalvik_dalvik_system_VMDebug_getAllocCount },
+ { "resetAllocCount", "(I)V",
+ Dalvik_dalvik_system_VMDebug_resetAllocCount },
+ { "startAllocCounting", "()V",
+ Dalvik_dalvik_system_VMDebug_startAllocCounting },
+ { "stopAllocCounting", "()V",
+ Dalvik_dalvik_system_VMDebug_stopAllocCounting },
+ { "startMethodTracingNative", "(Ljava/lang/String;Ljava/io/FileDescriptor;II)V",
+ Dalvik_dalvik_system_VMDebug_startMethodTracingNative },
+ { "isMethodTracingActive", "()Z",
+ Dalvik_dalvik_system_VMDebug_isMethodTracingActive },
+ { "stopMethodTracing", "()V",
+ Dalvik_dalvik_system_VMDebug_stopMethodTracing },
+ { "startEmulatorTracing", "()V",
+ Dalvik_dalvik_system_VMDebug_startEmulatorTracing },
+ { "stopEmulatorTracing", "()V",
+ Dalvik_dalvik_system_VMDebug_stopEmulatorTracing },
+ { "setAllocationLimit", "(I)I",
+ Dalvik_dalvik_system_VMDebug_setAllocationLimit },
+ { "setGlobalAllocationLimit", "(I)I",
+ Dalvik_dalvik_system_VMDebug_setGlobalAllocationLimit },
+ { "startInstructionCounting", "()V",
+ Dalvik_dalvik_system_VMDebug_startInstructionCounting },
+ { "stopInstructionCounting", "()V",
+ Dalvik_dalvik_system_VMDebug_stopInstructionCounting },
+ { "resetInstructionCount", "()V",
+ Dalvik_dalvik_system_VMDebug_resetInstructionCount },
+ { "getInstructionCount", "([I)V",
+ Dalvik_dalvik_system_VMDebug_getInstructionCount },
+ { "isDebuggerConnected", "()Z",
+ Dalvik_dalvik_system_VMDebug_isDebuggerConnected },
+ { "isDebuggingEnabled", "()Z",
+ Dalvik_dalvik_system_VMDebug_isDebuggingEnabled },
+ { "lastDebuggerActivity", "()J",
+ Dalvik_dalvik_system_VMDebug_lastDebuggerActivity },
+ { "printLoadedClasses", "(I)V",
+ Dalvik_dalvik_system_VMDebug_printLoadedClasses },
+ { "getLoadedClassCount", "()I",
+ Dalvik_dalvik_system_VMDebug_getLoadedClassCount },
+ { "threadCpuTimeNanos", "()J",
+ Dalvik_dalvik_system_VMDebug_threadCpuTimeNanos },
+ { "dumpHprofData", "(Ljava/lang/String;Ljava/io/FileDescriptor;)V",
+ Dalvik_dalvik_system_VMDebug_dumpHprofData },
+ { "dumpHprofDataDdms", "()V",
+ Dalvik_dalvik_system_VMDebug_dumpHprofDataDdms },
+ { "cacheRegisterMap", "(Ljava/lang/String;)Z",
+ Dalvik_dalvik_system_VMDebug_cacheRegisterMap },
+ { "dumpReferenceTables", "()V",
+ Dalvik_dalvik_system_VMDebug_dumpReferenceTables },
+ { "crash", "()V",
+ Dalvik_dalvik_system_VMDebug_crash },
+ { "infopoint", "(I)V",
+ Dalvik_dalvik_system_VMDebug_infopoint },
+ { "countInstancesOfClass", "(Ljava/lang/Class;Z)J",
+ Dalvik_dalvik_system_VMDebug_countInstancesOfClass },
+ { NULL, NULL, NULL },
+};
diff --git a/vm/native/dalvik_system_VMRuntime.c b/vm/native/dalvik_system_VMRuntime.c
new file mode 100644
index 0000000..c020f8a
--- /dev/null
+++ b/vm/native/dalvik_system_VMRuntime.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * dalvik.system.VMRuntime
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+#include <limits.h>
+
+
+/*
+ * public native float getTargetHeapUtilization()
+ *
+ * Gets the current ideal heap utilization, represented as a number
+ * between zero and one.
+ */
+static void Dalvik_dalvik_system_VMRuntime_getTargetHeapUtilization(
+ const u4* args, JValue* pResult)
+{
+ UNUSED_PARAMETER(args);
+
+ RETURN_FLOAT(dvmGetTargetHeapUtilization());
+}
+
+/*
+ * native float nativeSetTargetHeapUtilization()
+ *
+ * Sets the current ideal heap utilization, represented as a number
+ * between zero and one. Returns the old utilization.
+ *
+ * Note that this is NOT static.
+ */
+static void Dalvik_dalvik_system_VMRuntime_nativeSetTargetHeapUtilization(
+ const u4* args, JValue* pResult)
+{
+ dvmSetTargetHeapUtilization(dvmU4ToFloat(args[1]));
+
+ RETURN_VOID();
+}
+
+/*
+ * native long nativeMinimumHeapSize(long size, boolean set)
+ *
+ * If set is true, sets the new minimum heap size to size; always
+ * returns the current (or previous) size. If size is negative or
+ * zero, removes the current minimum constraint (if present).
+ */
+static void Dalvik_dalvik_system_VMRuntime_nativeMinimumHeapSize(
+ const u4* args, JValue* pResult)
+{
+ s8 longSize = GET_ARG_LONG(args, 1);
+ size_t size;
+ bool set = (args[3] != 0);
+
+ /* Fit in 32 bits. */
+ if (longSize < 0) {
+ size = 0;
+ } else if (longSize > INT_MAX) {
+ size = INT_MAX;
+ } else {
+ size = (size_t)longSize;
+ }
+
+ size = dvmMinimumHeapSize(size, set);
+
+ RETURN_LONG(size);
+}
+
+/*
+ * public native void gcSoftReferences()
+ *
+ * Does a GC and forces collection of SoftReferences that are
+ * not strongly-reachable.
+ */
+static void Dalvik_dalvik_system_VMRuntime_gcSoftReferences(const u4* args,
+ JValue* pResult)
+{
+ dvmCollectGarbage(true);
+
+ RETURN_VOID();
+}
+
+/*
+ * public native void runFinalizationSync()
+ *
+ * Does not return until any pending finalizers have been called.
+ * This may or may not happen in the context of the calling thread.
+ * No exceptions will escape.
+ *
+ * Used by zygote, which doesn't have a HeapWorker thread.
+ */
+static void Dalvik_dalvik_system_VMRuntime_runFinalizationSync(const u4* args,
+ JValue* pResult)
+{
+ dvmRunFinalizationSync();
+
+ RETURN_VOID();
+}
+
+/*
+ * public native boolean trackExternalAllocation(long size)
+ *
+ * Asks the VM if <size> bytes can be allocated in an external heap.
+ * This information may be used to limit the amount of memory available
+ * to Dalvik threads. Returns false if the VM would rather that the caller
+ * did not allocate that much memory. If the call returns false, the VM
+ * will not update its internal counts.
+ */
+static void Dalvik_dalvik_system_VMRuntime_trackExternalAllocation(
+ const u4* args, JValue* pResult)
+{
+ s8 longSize = GET_ARG_LONG(args, 1);
+
+ /* Fit in 32 bits. */
+ if (longSize < 0) {
+ dvmThrowException("Ljava/lang/IllegalArgumentException;",
+ "size must be positive");
+ RETURN_VOID();
+ } else if (longSize > INT_MAX) {
+ dvmThrowException("Ljava/lang/UnsupportedOperationException;",
+ "size must fit in 32 bits");
+ RETURN_VOID();
+ }
+ RETURN_BOOLEAN(dvmTrackExternalAllocation((size_t)longSize));
+}
+
+/*
+ * public native void trackExternalFree(long size)
+ *
+ * Tells the VM that <size> bytes have been freed in an external
+ * heap. This information may be used to control the amount of memory
+ * available to Dalvik threads.
+ */
+static void Dalvik_dalvik_system_VMRuntime_trackExternalFree(
+ const u4* args, JValue* pResult)
+{
+ s8 longSize = GET_ARG_LONG(args, 1);
+
+ /* Fit in 32 bits. */
+ if (longSize < 0) {
+ dvmThrowException("Ljava/lang/IllegalArgumentException;",
+ "size must be positive");
+ RETURN_VOID();
+ } else if (longSize > INT_MAX) {
+ dvmThrowException("Ljava/lang/UnsupportedOperationException;",
+ "size must fit in 32 bits");
+ RETURN_VOID();
+ }
+ dvmTrackExternalFree((size_t)longSize);
+
+ RETURN_VOID();
+}
+
+/*
+ * public native long getExternalBytesAllocated()
+ *
+ * Returns the number of externally-allocated bytes being tracked by
+ * trackExternalAllocation/Free().
+ */
+static void Dalvik_dalvik_system_VMRuntime_getExternalBytesAllocated(
+ const u4* args, JValue* pResult)
+{
+ RETURN_LONG((s8)dvmGetExternalBytesAllocated());
+}
+
+/*
+ * public native void startJitCompilation()
+ *
+ * Callback function from the framework to indicate that an app has gone
+ * through the startup phase and it is time to enable the JIT compiler.
+ */
+static void Dalvik_dalvik_system_VMRuntime_startJitCompilation(const u4* args,
+ JValue* pResult)
+{
+#if defined(WITH_JIT)
+ if (gDvm.executionMode == kExecutionModeJit &&
+ gDvmJit.disableJit == false) {
+ dvmLockMutex(&gDvmJit.compilerLock);
+ gDvmJit.alreadyEnabledViaFramework = true;
+ pthread_cond_signal(&gDvmJit.compilerQueueActivity);
+ dvmUnlockMutex(&gDvmJit.compilerLock);
+ }
+#endif
+ RETURN_VOID();
+}
+
+/*
+ * public native void disableJitCompilation()
+ *
+ * Callback function from the framework to indicate that a VM instance wants to
+ * permanently disable the JIT compiler. Currently only the system server uses
+ * this interface when it detects system-wide safe mode is enabled.
+ */
+static void Dalvik_dalvik_system_VMRuntime_disableJitCompilation(const u4* args,
+ JValue* pResult)
+{
+#if defined(WITH_JIT)
+ if (gDvm.executionMode == kExecutionModeJit) {
+ gDvmJit.disableJit = true;
+ }
+#endif
+ RETURN_VOID();
+}
+
+const DalvikNativeMethod dvm_dalvik_system_VMRuntime[] = {
+ { "getTargetHeapUtilization", "()F",
+ Dalvik_dalvik_system_VMRuntime_getTargetHeapUtilization },
+ { "nativeSetTargetHeapUtilization", "(F)V",
+ Dalvik_dalvik_system_VMRuntime_nativeSetTargetHeapUtilization },
+ { "nativeMinimumHeapSize", "(JZ)J",
+ Dalvik_dalvik_system_VMRuntime_nativeMinimumHeapSize },
+ { "gcSoftReferences", "()V",
+ Dalvik_dalvik_system_VMRuntime_gcSoftReferences },
+ { "runFinalizationSync", "()V",
+ Dalvik_dalvik_system_VMRuntime_runFinalizationSync },
+ { "trackExternalAllocation", "(J)Z",
+ Dalvik_dalvik_system_VMRuntime_trackExternalAllocation },
+ { "trackExternalFree", "(J)V",
+ Dalvik_dalvik_system_VMRuntime_trackExternalFree },
+ { "getExternalBytesAllocated", "()J",
+ Dalvik_dalvik_system_VMRuntime_getExternalBytesAllocated },
+ { "startJitCompilation", "()V",
+ Dalvik_dalvik_system_VMRuntime_startJitCompilation },
+ { "disableJitCompilation", "()V",
+ Dalvik_dalvik_system_VMRuntime_disableJitCompilation },
+ { NULL, NULL, NULL },
+};
diff --git a/vm/native/dalvik_system_VMStack.c b/vm/native/dalvik_system_VMStack.c
new file mode 100644
index 0000000..8db4a6b
--- /dev/null
+++ b/vm/native/dalvik_system_VMStack.c
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * dalvik.system.VMStack
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * public static ClassLoader getCallingClassLoader()
+ *
+ * Return the defining class loader of the caller's caller.
+ */
+static void Dalvik_dalvik_system_VMStack_getCallingClassLoader(const u4* args,
+ JValue* pResult)
+{
+ ClassObject* clazz = dvmGetCaller2Class(dvmThreadSelf()->curFrame);
+
+ UNUSED_PARAMETER(args);
+
+ if (clazz == NULL)
+ RETURN_PTR(NULL);
+ RETURN_PTR(clazz->classLoader);
+}
+
+/*
+ * public static ClassLoader getCallingClassLoader2()
+ *
+ * Return the defining class loader of the caller's caller's caller.
+ */
+static void Dalvik_dalvik_system_VMStack_getCallingClassLoader2(const u4* args,
+ JValue* pResult)
+{
+ ClassObject* clazz = dvmGetCaller3Class(dvmThreadSelf()->curFrame);
+
+ UNUSED_PARAMETER(args);
+
+ if (clazz == NULL)
+ RETURN_PTR(NULL);
+ RETURN_PTR(clazz->classLoader);
+}
+
+/*
+ * public static Class<?> getStackClass2()
+ *
+ * Returns the class of the caller's caller's caller.
+ */
+static void Dalvik_dalvik_system_VMStack_getStackClass2(const u4* args,
+ JValue* pResult)
+{
+ ClassObject* clazz = dvmGetCaller3Class(dvmThreadSelf()->curFrame);
+
+ UNUSED_PARAMETER(args);
+
+ RETURN_PTR(clazz);
+}
+
+/*
+ * public static Class<?>[] getClasses(int maxDepth, boolean stopAtPrivileged)
+ *
+ * Create an array of classes for the methods on the stack, skipping the
+ * first two and all reflection methods. If "stopAtPrivileged" is set,
+ * stop shortly after we encounter a privileged class.
+ */
+static void Dalvik_dalvik_system_VMStack_getClasses(const u4* args,
+ JValue* pResult)
+{
+ /* note "maxSize" is unsigned, so -1 turns into a very large value */
+ unsigned int maxSize = args[0];
+ bool stopAtPrivileged = args[1];
+ unsigned int size = 0;
+ const unsigned int kSkip = 2;
+ const Method** methods = NULL;
+ int methodCount;
+
+ /*
+ * Get an array with the stack trace in it.
+ */
+ if (!dvmCreateStackTraceArray(dvmThreadSelf()->curFrame, &methods,
+ &methodCount))
+ {
+ LOGE("Failed to create stack trace array\n");
+ dvmThrowException("Ljava/lang/InternalError;", NULL);
+ RETURN_VOID();
+ }
+
+ //int i;
+ //LOGI("dvmCreateStackTraceArray results:\n");
+ //for (i = 0; i < methodCount; i++) {
+ // LOGI(" %2d: %s.%s\n",
+ // i, methods[i]->clazz->descriptor, methods[i]->name);
+ //}
+
+ /*
+ * Run through the array and count up how many elements there are.
+ */
+ unsigned int idx;
+ for (idx = kSkip; (int) idx < methodCount && size < maxSize; idx++) {
+ const Method* meth = methods[idx];
+
+ if (dvmIsReflectionMethod(meth))
+ continue;
+
+ if (stopAtPrivileged && dvmIsPrivilegedMethod(meth)) {
+ /*
+ * We want the last element of the array to be the caller of
+ * the privileged method, so we want to include the privileged
+ * method and the next one.
+ */
+ if (maxSize > size + 2)
+ maxSize = size + 2;
+ }
+
+ size++;
+ }
+
+ /*
+ * Create an array object to hold the classes.
+ * TODO: can use gDvm.classJavaLangClassArray here?
+ */
+ ClassObject* classArrayClass = NULL;
+ ArrayObject* classes = NULL;
+ classArrayClass = dvmFindArrayClass("[Ljava/lang/Class;", NULL);
+ if (classArrayClass == NULL) {
+ LOGW("Unable to find java.lang.Class array class\n");
+ goto bail;
+ }
+ classes = dvmAllocArray(classArrayClass, size, kObjectArrayRefWidth,
+ ALLOC_DEFAULT);
+ if (classes == NULL) {
+ LOGW("Unable to allocate class array (%d elems)\n", size);
+ goto bail;
+ }
+
+ /*
+ * Fill in the array.
+ */
+ unsigned int objCount = 0;
+ for (idx = kSkip; (int) idx < methodCount; idx++) {
+ if (dvmIsReflectionMethod(methods[idx])) {
+ continue;
+ }
+ dvmSetObjectArrayElement(classes, objCount,
+ (Object *)methods[idx]->clazz);
+ objCount++;
+ }
+ assert(objCount == classes->length);
+
+bail:
+ free(methods);
+ dvmReleaseTrackedAlloc((Object*) classes, NULL);
+ RETURN_PTR(classes);
+}
+
+/*
+ * public static StackTraceElement[] getThreadStackTrace(Thread t)
+ *
+ * Retrieve the stack trace of the specified thread and return it as an
+ * array of StackTraceElement. Returns NULL on failure.
+ */
+static void Dalvik_dalvik_system_VMStack_getThreadStackTrace(const u4* args,
+ JValue* pResult)
+{
+ Object* targetThreadObj = (Object*) args[0];
+ Thread* self = dvmThreadSelf();
+ Thread* thread;
+ int* traceBuf;
+
+ assert(targetThreadObj != NULL);
+
+ dvmLockThreadList(self);
+
+ /*
+ * Make sure the thread is still alive and in the list.
+ */
+ for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+ if (thread->threadObj == targetThreadObj)
+ break;
+ }
+ if (thread == NULL) {
+ LOGI("VMStack.getThreadStackTrace: threadObj %p not active\n",
+ targetThreadObj);
+ dvmUnlockThreadList();
+ RETURN_PTR(NULL);
+ }
+
+ /*
+ * Suspend the thread, pull out the stack trace, then resume the thread
+ * and release the thread list lock. If we're being asked to examine
+ * our own stack trace, skip the suspend/resume.
+ */
+ int stackDepth = -1;
+ if (thread != self)
+ dvmSuspendThread(thread);
+ traceBuf = dvmFillInStackTraceRaw(thread, &stackDepth);
+ if (thread != self)
+ dvmResumeThread(thread);
+ dvmUnlockThreadList();
+
+ /*
+ * Convert the raw buffer into an array of StackTraceElement.
+ */
+ ArrayObject* trace = dvmGetStackTraceRaw(traceBuf, stackDepth);
+ free(traceBuf);
+ RETURN_PTR(trace);
+}
+
+const DalvikNativeMethod dvm_dalvik_system_VMStack[] = {
+ { "getCallingClassLoader", "()Ljava/lang/ClassLoader;",
+ Dalvik_dalvik_system_VMStack_getCallingClassLoader },
+ { "getCallingClassLoader2", "()Ljava/lang/ClassLoader;",
+ Dalvik_dalvik_system_VMStack_getCallingClassLoader2 },
+ { "getStackClass2", "()Ljava/lang/Class;",
+ Dalvik_dalvik_system_VMStack_getStackClass2 },
+ { "getClasses", "(IZ)[Ljava/lang/Class;",
+ Dalvik_dalvik_system_VMStack_getClasses },
+ { "getThreadStackTrace", "(Ljava/lang/Thread;)[Ljava/lang/StackTraceElement;",
+ Dalvik_dalvik_system_VMStack_getThreadStackTrace },
+ { NULL, NULL, NULL },
+};
diff --git a/vm/native/dalvik_system_Zygote.c b/vm/native/dalvik_system_Zygote.c
new file mode 100644
index 0000000..bcc2313
--- /dev/null
+++ b/vm/native/dalvik_system_Zygote.c
@@ -0,0 +1,523 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * dalvik.system.Zygote
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <grp.h>
+#include <errno.h>
+
+#if defined(HAVE_PRCTL)
+# include <sys/prctl.h>
+#endif
+
+#define ZYGOTE_LOG_TAG "Zygote"
+
+/* must match values in dalvik.system.Zygote */
+enum {
+ DEBUG_ENABLE_DEBUGGER = 1,
+ DEBUG_ENABLE_CHECKJNI = 1 << 1,
+ DEBUG_ENABLE_ASSERT = 1 << 2,
+ DEBUG_ENABLE_SAFEMODE = 1 << 3,
+};
+
+/*
+ * This signal handler is for zygote mode, since the zygote
+ * must reap its children
+ */
+static void sigchldHandler(int s)
+{
+ pid_t pid;
+ int status;
+
+ while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
+ /* Log process-death status that we care about. In general it is not
+ safe to call LOG(...) from a signal handler because of possible
+ reentrancy. However, we know a priori that the current implementation
+ of LOG() is safe to call from a SIGCHLD handler in the zygote process.
+ If the LOG() implementation changes its locking strategy or its use
+ of syscalls within the lazy-init critical section, its use here may
+ become unsafe. */
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status)) {
+ LOG(LOG_DEBUG, ZYGOTE_LOG_TAG, "Process %d exited cleanly (%d)\n",
+ (int) pid, WEXITSTATUS(status));
+ } else {
+ IF_LOGV(/*should use ZYGOTE_LOG_TAG*/) {
+ LOG(LOG_VERBOSE, ZYGOTE_LOG_TAG,
+ "Process %d exited cleanly (%d)\n",
+ (int) pid, WEXITSTATUS(status));
+ }
+ }
+ } else if (WIFSIGNALED(status)) {
+ if (WTERMSIG(status) != SIGKILL) {
+ LOG(LOG_DEBUG, ZYGOTE_LOG_TAG,
+ "Process %d terminated by signal (%d)\n",
+ (int) pid, WTERMSIG(status));
+ } else {
+ IF_LOGV(/*should use ZYGOTE_LOG_TAG*/) {
+ LOG(LOG_VERBOSE, ZYGOTE_LOG_TAG,
+ "Process %d terminated by signal (%d)\n",
+ (int) pid, WTERMSIG(status));
+ }
+ }
+#ifdef WCOREDUMP
+ if (WCOREDUMP(status)) {
+ LOG(LOG_INFO, ZYGOTE_LOG_TAG, "Process %d dumped core\n",
+ (int) pid);
+ }
+#endif /* ifdef WCOREDUMP */
+ }
+
+ /*
+ * If the just-crashed process is the system_server, bring down zygote
+ * so that it is restarted by init and system server will be restarted
+ * from there.
+ */
+ if (pid == gDvm.systemServerPid) {
+ LOG(LOG_INFO, ZYGOTE_LOG_TAG,
+ "Exit zygote because system server (%d) has terminated\n",
+ (int) pid);
+ kill(getpid(), SIGKILL);
+ }
+ }
+
+ if (pid < 0) {
+ LOG(LOG_WARN, ZYGOTE_LOG_TAG,
+ "Zygote SIGCHLD error in waitpid: %s\n",strerror(errno));
+ }
+}
+
+/*
+ * configure sigchld handler for the zygote process
+ * This is configured very late, because earlier in the dalvik lifecycle
+ * we can fork() and exec() for the verifier/optimizer, and we
+ * want to waitpid() for those rather than have them be harvested immediately.
+ *
+ * This ends up being called repeatedly before each fork(), but there's
+ * no real harm in that.
+ */
+static void setSignalHandler()
+{
+ int err;
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+
+ sa.sa_handler = sigchldHandler;
+
+ err = sigaction (SIGCHLD, &sa, NULL);
+
+ if (err < 0) {
+ LOGW("Error setting SIGCHLD handler: %s", strerror(errno));
+ }
+}
+
+/*
+ * Set the SIGCHLD handler back to default behavior in zygote children
+ */
+static void unsetSignalHandler()
+{
+ int err;
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+
+ sa.sa_handler = SIG_DFL;
+
+ err = sigaction (SIGCHLD, &sa, NULL);
+
+ if (err < 0) {
+ LOGW("Error unsetting SIGCHLD handler: %s", strerror(errno));
+ }
+}
+
+/*
+ * Calls POSIX setgroups() using the int[] object as an argument.
+ * A NULL argument is tolerated.
+ */
+
+static int setgroupsIntarray(ArrayObject* gidArray)
+{
+ gid_t *gids;
+ u4 i;
+ s4 *contents;
+
+ if (gidArray == NULL) {
+ return 0;
+ }
+
+ /* just in case gid_t and u4 are different... */
+ gids = alloca(sizeof(gid_t) * gidArray->length);
+ contents = (s4 *)gidArray->contents;
+
+ for (i = 0 ; i < gidArray->length ; i++) {
+ gids[i] = (gid_t) contents[i];
+ }
+
+ return setgroups((size_t) gidArray->length, gids);
+}
+
+/*
+ * Sets the resource limits via setrlimit(2) for the values in the
+ * two-dimensional array of integers that's passed in. The second dimension
+ * contains a tuple of length 3: (resource, rlim_cur, rlim_max). NULL is
+ * treated as an empty array.
+ *
+ * -1 is returned on error.
+ */
+static int setrlimitsFromArray(ArrayObject* rlimits)
+{
+ u4 i;
+ struct rlimit rlim;
+
+ if (rlimits == NULL) {
+ return 0;
+ }
+
+ memset (&rlim, 0, sizeof(rlim));
+
+ ArrayObject** tuples = (ArrayObject **)(rlimits->contents);
+
+ for (i = 0; i < rlimits->length; i++) {
+ ArrayObject * rlimit_tuple = tuples[i];
+ s4* contents = (s4 *)rlimit_tuple->contents;
+ int err;
+
+ if (rlimit_tuple->length != 3) {
+ LOGE("rlimits array must have a second dimension of size 3");
+ return -1;
+ }
+
+ rlim.rlim_cur = contents[1];
+ rlim.rlim_max = contents[2];
+
+ err = setrlimit(contents[0], &rlim);
+
+ if (err < 0) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/* native public static int fork(); */
+static void Dalvik_dalvik_system_Zygote_fork(const u4* args, JValue* pResult)
+{
+ pid_t pid;
+
+ if (!gDvm.zygote) {
+ dvmThrowException("Ljava/lang/IllegalStateException;",
+ "VM instance not started with -Xzygote");
+
+ RETURN_VOID();
+ }
+
+ if (!dvmGcPreZygoteFork()) {
+ LOGE("pre-fork heap failed\n");
+ dvmAbort();
+ }
+
+ setSignalHandler();
+
+ dvmDumpLoaderStats("zygote");
+ pid = fork();
+
+#ifdef HAVE_ANDROID_OS
+ if (pid == 0) {
+ /* child process */
+ extern int gMallocLeakZygoteChild;
+ gMallocLeakZygoteChild = 1;
+ }
+#endif
+
+ RETURN_INT(pid);
+}
+
+/*
+ * Enable/disable debug features requested by the caller.
+ *
+ * debugger
+ * If set, enable debugging; if not set, disable debugging. This is
+ * easy to handle, because the JDWP thread isn't started until we call
+ * dvmInitAfterZygote().
+ * checkjni
+ * If set, make sure "check JNI" is eabled. This is a little weird,
+ * because we already have the JNIEnv for the main thread set up. However,
+ * since we only have one thread at this point, it's easy to patch up.
+ * assert
+ * If set, make sure assertions are enabled. This gets fairly weird,
+ * because it affects the result of a method called by class initializers,
+ * and hence can't affect pre-loaded/initialized classes.
+ * safemode
+ * If set, operates the VM in the safe mode. The definition of "safe mode" is
+ * implementation dependent and currently only the JIT compiler is disabled.
+ * This is easy to handle because the compiler thread and associated resources
+ * are not requested until we call dvmInitAfterZygote().
+ */
+static void enableDebugFeatures(u4 debugFlags)
+{
+ LOGV("debugFlags is 0x%02x\n", debugFlags);
+
+ gDvm.jdwpAllowed = ((debugFlags & DEBUG_ENABLE_DEBUGGER) != 0);
+
+ if ((debugFlags & DEBUG_ENABLE_CHECKJNI) != 0) {
+ /* turn it on if it's not already enabled */
+ dvmLateEnableCheckedJni();
+ }
+
+ if ((debugFlags & DEBUG_ENABLE_ASSERT) != 0) {
+ /* turn it on if it's not already enabled */
+ dvmLateEnableAssertions();
+ }
+
+ if ((debugFlags & DEBUG_ENABLE_SAFEMODE) != 0) {
+#if defined(WITH_JIT)
+ /* turn off the jit if it is explicitly requested by the app */
+ if (gDvm.executionMode == kExecutionModeJit)
+ gDvm.executionMode = kExecutionModeInterpFast;
+#endif
+ }
+
+#if HAVE_ANDROID_OS
+ if ((debugFlags & DEBUG_ENABLE_DEBUGGER) != 0) {
+ /* To let a non-privileged gdbserver attach to this
+ * process, we must set its dumpable bit flag. However
+ * we are not interested in generating a coredump in
+ * case of a crash, so also set the coredump size to 0
+ * to disable that
+ */
+ if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) {
+ LOGE("could not set dumpable bit flag for pid %d: %s",
+ getpid(), strerror(errno));
+ } else {
+ struct rlimit rl;
+ rl.rlim_cur = 0;
+ rl.rlim_max = RLIM_INFINITY;
+ if (setrlimit(RLIMIT_CORE, &rl) < 0) {
+ LOGE("could not disable core file generation for pid %d: %s",
+ getpid(), strerror(errno));
+ }
+ }
+ }
+#endif
+}
+
+/*
+ * Set Linux capability flags.
+ *
+ * Returns 0 on success, errno on failure.
+ */
+static int setCapabilities(int64_t permitted, int64_t effective)
+{
+#ifdef HAVE_ANDROID_OS
+ struct __user_cap_header_struct capheader;
+ struct __user_cap_data_struct capdata;
+
+ memset(&capheader, 0, sizeof(capheader));
+ memset(&capdata, 0, sizeof(capdata));
+
+ capheader.version = _LINUX_CAPABILITY_VERSION;
+ capheader.pid = 0;
+
+ capdata.effective = effective;
+ capdata.permitted = permitted;
+
+ LOGV("CAPSET perm=%llx eff=%llx\n", permitted, effective);
+ if (capset(&capheader, &capdata) != 0)
+ return errno;
+#endif /*HAVE_ANDROID_OS*/
+
+ return 0;
+}
+
+/*
+ * Utility routine to fork zygote and specialize the child process.
+ */
+static pid_t forkAndSpecializeCommon(const u4* args, bool isSystemServer)
+{
+ pid_t pid;
+
+ uid_t uid = (uid_t) args[0];
+ gid_t gid = (gid_t) args[1];
+ ArrayObject* gids = (ArrayObject *)args[2];
+ u4 debugFlags = args[3];
+ ArrayObject *rlimits = (ArrayObject *)args[4];
+ int64_t permittedCapabilities, effectiveCapabilities;
+
+ if (isSystemServer) {
+ /*
+ * Don't use GET_ARG_LONG here for now. gcc is generating code
+ * that uses register d8 as a temporary, and that's coming out
+ * scrambled in the child process. b/3138621
+ */
+ //permittedCapabilities = GET_ARG_LONG(args, 5);
+ //effectiveCapabilities = GET_ARG_LONG(args, 7);
+ permittedCapabilities = args[5] | (int64_t) args[6] << 32;
+ effectiveCapabilities = args[7] | (int64_t) args[8] << 32;
+ } else {
+ permittedCapabilities = effectiveCapabilities = 0;
+ }
+
+ if (!gDvm.zygote) {
+ dvmThrowException("Ljava/lang/IllegalStateException;",
+ "VM instance not started with -Xzygote");
+
+ return -1;
+ }
+
+ if (!dvmGcPreZygoteFork()) {
+ LOGE("pre-fork heap failed\n");
+ dvmAbort();
+ }
+
+ setSignalHandler();
+
+ dvmDumpLoaderStats("zygote");
+ pid = fork();
+
+ if (pid == 0) {
+ int err;
+ /* The child process */
+
+#ifdef HAVE_ANDROID_OS
+ extern int gMallocLeakZygoteChild;
+ gMallocLeakZygoteChild = 1;
+
+ /* keep caps across UID change, unless we're staying root */
+ if (uid != 0) {
+ err = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
+
+ if (err < 0) {
+ LOGE("cannot PR_SET_KEEPCAPS: %s", strerror(errno));
+ dvmAbort();
+ }
+ }
+
+#endif /* HAVE_ANDROID_OS */
+
+ err = setgroupsIntarray(gids);
+
+ if (err < 0) {
+ LOGE("cannot setgroups(): %s", strerror(errno));
+ dvmAbort();
+ }
+
+ err = setrlimitsFromArray(rlimits);
+
+ if (err < 0) {
+ LOGE("cannot setrlimit(): %s", strerror(errno));
+ dvmAbort();
+ }
+
+ err = setgid(gid);
+ if (err < 0) {
+ LOGE("cannot setgid(%d): %s", gid, strerror(errno));
+ dvmAbort();
+ }
+
+ err = setuid(uid);
+ if (err < 0) {
+ LOGE("cannot setuid(%d): %s", uid, strerror(errno));
+ dvmAbort();
+ }
+
+ err = setCapabilities(permittedCapabilities, effectiveCapabilities);
+ if (err != 0) {
+ LOGE("cannot set capabilities (%llx,%llx): %s\n",
+ permittedCapabilities, effectiveCapabilities, strerror(err));
+ dvmAbort();
+ }
+
+ /*
+ * Our system thread ID has changed. Get the new one.
+ */
+ Thread* thread = dvmThreadSelf();
+ thread->systemTid = dvmGetSysThreadId();
+
+ /* configure additional debug options */
+ enableDebugFeatures(debugFlags);
+
+ unsetSignalHandler();
+ gDvm.zygote = false;
+ if (!dvmInitAfterZygote()) {
+ LOGE("error in post-zygote initialization\n");
+ dvmAbort();
+ }
+ } else if (pid > 0) {
+ /* the parent process */
+ }
+
+ return pid;
+}
+
+/* native public static int forkAndSpecialize(int uid, int gid,
+ * int[] gids, int debugFlags);
+ */
+static void Dalvik_dalvik_system_Zygote_forkAndSpecialize(const u4* args,
+ JValue* pResult)
+{
+ pid_t pid;
+
+ pid = forkAndSpecializeCommon(args, false);
+
+ RETURN_INT(pid);
+}
+
+/* native public static int forkSystemServer(int uid, int gid,
+ * int[] gids, int debugFlags, long permittedCapabilities,
+ * long effectiveCapabilities);
+ */
+static void Dalvik_dalvik_system_Zygote_forkSystemServer(
+ const u4* args, JValue* pResult)
+{
+ pid_t pid;
+ pid = forkAndSpecializeCommon(args, true);
+
+ /* The zygote process checks whether the child process has died or not. */
+ if (pid > 0) {
+ int status;
+
+ LOGI("System server process %d has been created", pid);
+ gDvm.systemServerPid = pid;
+ /* There is a slight window that the system server process has crashed
+ * but it went unnoticed because we haven't published its pid yet. So
+ * we recheck here just to make sure that all is well.
+ */
+ if (waitpid(pid, &status, WNOHANG) == pid) {
+ LOGE("System server process %d has died. Restarting Zygote!", pid);
+ kill(getpid(), SIGKILL);
+ }
+ }
+ RETURN_INT(pid);
+}
+
+const DalvikNativeMethod dvm_dalvik_system_Zygote[] = {
+ { "fork", "()I",
+ Dalvik_dalvik_system_Zygote_fork },
+ { "forkAndSpecialize", "(II[II[[I)I",
+ Dalvik_dalvik_system_Zygote_forkAndSpecialize },
+ { "forkSystemServer", "(II[II[[IJJ)I",
+ Dalvik_dalvik_system_Zygote_forkSystemServer },
+ { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_Class.c b/vm/native/java_lang_Class.c
new file mode 100644
index 0000000..3c772d9
--- /dev/null
+++ b/vm/native/java_lang_Class.c
@@ -0,0 +1,814 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * java.lang.Class
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * native public boolean desiredAssertionStatus()
+ *
+ * Determine the class-init-time assertion status of a class. This is
+ * called from <clinit> in javac-generated classes that use the Java
+ * programming language "assert" keyword.
+ */
+static void Dalvik_java_lang_Class_desiredAssertionStatus(const u4* args,
+ JValue* pResult)
+{
+ ClassObject* thisPtr = (ClassObject*) args[0];
+ char* className = dvmDescriptorToName(thisPtr->descriptor);
+ int i;
+ bool enable = false;
+
+ /*
+ * Run through the list of arguments specified on the command line. The
+ * last matching argument takes precedence.
+ */
+ for (i = 0; i < gDvm.assertionCtrlCount; i++) {
+ const AssertionControl* pCtrl = &gDvm.assertionCtrl[i];
+
+ if (pCtrl->isPackage) {
+ /*
+ * Given "dalvik/system/Debug" or "MyStuff", compute the
+ * length of the package portion of the class name string.
+ *
+ * Unlike most package operations, we allow matching on
+ * "sub-packages", so "dalvik..." will match "dalvik.Foo"
+ * and "dalvik.system.Foo".
+ *
+ * The pkgOrClass string looks like "dalvik/system/", i.e. it still
+ * has the terminating slash, so we can be sure we're comparing
+ * against full package component names.
+ */
+ const char* lastSlash;
+ int pkgLen;
+
+ lastSlash = strrchr(className, '/');
+ if (lastSlash == NULL) {
+ pkgLen = 0;
+ } else {
+ pkgLen = lastSlash - className +1;
+ }
+
+ if (pCtrl->pkgOrClassLen > pkgLen ||
+ memcmp(pCtrl->pkgOrClass, className, pCtrl->pkgOrClassLen) != 0)
+ {
+ LOGV("ASRT: pkg no match: '%s'(%d) vs '%s'\n",
+ className, pkgLen, pCtrl->pkgOrClass);
+ } else {
+ LOGV("ASRT: pkg match: '%s'(%d) vs '%s' --> %d\n",
+ className, pkgLen, pCtrl->pkgOrClass, pCtrl->enable);
+ enable = pCtrl->enable;
+ }
+ } else {
+ /*
+ * "pkgOrClass" holds a fully-qualified class name, converted from
+ * dot-form to slash-form. An empty string means all classes.
+ */
+ if (pCtrl->pkgOrClass == NULL) {
+ /* -esa/-dsa; see if class is a "system" class */
+ if (strncmp(className, "java/", 5) != 0) {
+ LOGV("ASRT: sys no match: '%s'\n", className);
+ } else {
+ LOGV("ASRT: sys match: '%s' --> %d\n",
+ className, pCtrl->enable);
+ enable = pCtrl->enable;
+ }
+ } else if (*pCtrl->pkgOrClass == '\0') {
+ LOGV("ASRT: class all: '%s' --> %d\n",
+ className, pCtrl->enable);
+ enable = pCtrl->enable;
+ } else {
+ if (strcmp(pCtrl->pkgOrClass, className) != 0) {
+ LOGV("ASRT: cls no match: '%s' vs '%s'\n",
+ className, pCtrl->pkgOrClass);
+ } else {
+ LOGV("ASRT: cls match: '%s' vs '%s' --> %d\n",
+ className, pCtrl->pkgOrClass, pCtrl->enable);
+ enable = pCtrl->enable;
+ }
+ }
+ }
+ }
+
+ free(className);
+ RETURN_INT(enable);
+}
+
+/*
+ * static public Class<?> classForName(String name, boolean initialize,
+ * ClassLoader loader)
+ *
+ * Return the Class object associated with the class or interface with
+ * the specified name.
+ *
+ * "name" is in "binary name" format, e.g. "dalvik.system.Debug$1".
+ */
+static void Dalvik_java_lang_Class_classForName(const u4* args, JValue* pResult)
+{
+ StringObject* nameObj = (StringObject*) args[0];
+ bool initialize = (args[1] != 0);
+ Object* loader = (Object*) args[2];
+
+ RETURN_PTR(dvmFindClassByName(nameObj, loader, initialize));
+}
+
+/*
+ * static private ClassLoader getClassLoader(Class clazz)
+ *
+ * Return the class' defining class loader.
+ */
+static void Dalvik_java_lang_Class_getClassLoader(const u4* args,
+ JValue* pResult)
+{
+ ClassObject* clazz = (ClassObject*) args[0];
+
+ RETURN_PTR(clazz->classLoader);
+}
+
+/*
+ * public Class<?> getComponentType()
+ *
+ * If this is an array type, return the class of the elements; otherwise
+ * return NULL.
+ */
+static void Dalvik_java_lang_Class_getComponentType(const u4* args,
+ JValue* pResult)
+{
+ ClassObject* thisPtr = (ClassObject*) args[0];
+
+ if (!dvmIsArrayClass(thisPtr))
+ RETURN_PTR(NULL);
+
+ /*
+ * We can't just return thisPtr->elementClass, because that gives
+ * us the base type (e.g. X[][][] returns X). If this is a multi-
+ * dimensional array, we have to do the lookup by name.
+ */
+ if (thisPtr->descriptor[1] == '[')
+ RETURN_PTR(dvmFindArrayClass(&thisPtr->descriptor[1],
+ thisPtr->classLoader));
+ else
+ RETURN_PTR(thisPtr->elementClass);
+}
+
+/*
+ * private static Class<?>[] getDeclaredClasses(Class<?> clazz,
+ * boolean publicOnly)
+ *
+ * Return an array with the classes that are declared by the specified class.
+ * If "publicOnly" is set, we strip out any classes that don't have "public"
+ * access.
+ */
+static void Dalvik_java_lang_Class_getDeclaredClasses(const u4* args,
+ JValue* pResult)
+{
+ ClassObject* clazz = (ClassObject*) args[0];
+ bool publicOnly = (args[1] != 0);
+ ArrayObject* classes;
+
+ classes = dvmGetDeclaredClasses(clazz);
+ if (classes == NULL) {
+ if (!dvmCheckException(dvmThreadSelf())) {
+ /* empty list, so create a zero-length array */
+ classes = dvmAllocArrayByClass(gDvm.classJavaLangClassArray,
+ 0, ALLOC_DEFAULT);
+ }
+ } else if (publicOnly) {
+ u4 count, newIdx, publicCount = 0;
+ ClassObject** pSource = (ClassObject**) classes->contents;
+ u4 length = classes->length;
+
+ /* count up public classes */
+ for (count = 0; count < length; count++) {
+ if (dvmIsPublicClass(pSource[count]))
+ publicCount++;
+ }
+
+ /* create a new array to hold them */
+ ArrayObject* newClasses;
+ newClasses = dvmAllocArrayByClass(gDvm.classJavaLangClassArray,
+ publicCount, ALLOC_DEFAULT);
+
+ /* copy them over */
+ for (count = newIdx = 0; count < length; count++) {
+ if (dvmIsPublicClass(pSource[count])) {
+ dvmSetObjectArrayElement(newClasses, newIdx,
+ (Object *)pSource[count]);
+ newIdx++;
+ }
+ }
+ assert(newIdx == publicCount);
+ dvmReleaseTrackedAlloc((Object*) classes, NULL);
+ classes = newClasses;
+ }
+
+ dvmReleaseTrackedAlloc((Object*) classes, NULL);
+ RETURN_PTR(classes);
+}
+
+/*
+ * static Constructor[] getDeclaredConstructors(Class clazz, boolean publicOnly)
+ * throws SecurityException
+ */
+static void Dalvik_java_lang_Class_getDeclaredConstructors(const u4* args,
+ JValue* pResult)
+{
+ ClassObject* clazz = (ClassObject*) args[0];
+ bool publicOnly = (args[1] != 0);
+ ArrayObject* constructors;
+
+ constructors = dvmGetDeclaredConstructors(clazz, publicOnly);
+ dvmReleaseTrackedAlloc((Object*) constructors, NULL);
+
+ RETURN_PTR(constructors);
+}
+
+/*
+ * static Field[] getDeclaredFields(Class klass, boolean publicOnly)
+ * throws SecurityException
+ */
+static void Dalvik_java_lang_Class_getDeclaredFields(const u4* args,
+ JValue* pResult)
+{
+ ClassObject* clazz = (ClassObject*) args[0];
+ bool publicOnly = (args[1] != 0);
+ ArrayObject* fields;
+
+ fields = dvmGetDeclaredFields(clazz, publicOnly);
+ dvmReleaseTrackedAlloc((Object*) fields, NULL);
+
+ RETURN_PTR(fields);
+}
+
+/*
+ * static Method[] getDeclaredMethods(Class clazz, boolean publicOnly)
+ * throws SecurityException
+ */
+static void Dalvik_java_lang_Class_getDeclaredMethods(const u4* args,
+ JValue* pResult)
+{
+ ClassObject* clazz = (ClassObject*) args[0];
+ bool publicOnly = (args[1] != 0);
+ ArrayObject* methods;
+
+ methods = dvmGetDeclaredMethods(clazz, publicOnly);
+ dvmReleaseTrackedAlloc((Object*) methods, NULL);
+
+ RETURN_PTR(methods);
+}
+
+/*
+ * Class[] getInterfaces()
+ */
+static void Dalvik_java_lang_Class_getInterfaces(const u4* args,
+ JValue* pResult)
+{
+ ClassObject* clazz = (ClassObject*) args[0];
+ ArrayObject* interfaces;
+
+ interfaces = dvmGetInterfaces(clazz);
+ dvmReleaseTrackedAlloc((Object*) interfaces, NULL);
+
+ RETURN_PTR(interfaces);
+}
+
+/*
+ * private static int getModifiers(Class klass, boolean
+ * ignoreInnerClassesAttrib)
+ *
+ * Return the class' modifier flags. If "ignoreInnerClassesAttrib" is false,
+ * and this is an inner class, we return the access flags from the inner class
+ * attribute.
+ */
+static void Dalvik_java_lang_Class_getModifiers(const u4* args, JValue* pResult)
+{
+ ClassObject* clazz = (ClassObject*) args[0];
+ bool ignoreInner = args[1];
+ u4 accessFlags;
+
+ accessFlags = clazz->accessFlags & JAVA_FLAGS_MASK;
+
+ if (!ignoreInner) {
+ /* see if we have an InnerClass annotation with flags in it */
+ StringObject* className = NULL;
+ int innerFlags;
+
+ if (dvmGetInnerClass(clazz, &className, &innerFlags))
+ accessFlags = innerFlags & JAVA_FLAGS_MASK;
+
+ dvmReleaseTrackedAlloc((Object*) className, NULL);
+ }
+
+ RETURN_INT(accessFlags);
+}
+
+/*
+ * private native String getNameNative()
+ *
+ * Return the class' name.
+ */
+static void Dalvik_java_lang_Class_getNameNative(const u4* args, JValue* pResult)
+{
+ ClassObject* clazz = (ClassObject*) args[0];
+ const char* descriptor = clazz->descriptor;
+ StringObject* nameObj;
+
+ if ((descriptor[0] != 'L') && (descriptor[0] != '[')) {
+ /*
+ * The descriptor indicates that this is the class for
+ * a primitive type; special-case the return value.
+ */
+ const char* name;
+ switch (descriptor[0]) {
+ case 'Z': name = "boolean"; break;
+ case 'B': name = "byte"; break;
+ case 'C': name = "char"; break;
+ case 'S': name = "short"; break;
+ case 'I': name = "int"; break;
+ case 'J': name = "long"; break;
+ case 'F': name = "float"; break;
+ case 'D': name = "double"; break;
+ case 'V': name = "void"; break;
+ default: {
+ LOGE("Unknown primitive type '%c'\n", descriptor[0]);
+ assert(false);
+ RETURN_PTR(NULL);
+ }
+ }
+
+ nameObj = dvmCreateStringFromCstr(name);
+ } else {
+ /*
+ * Convert the UTF-8 name to a java.lang.String. The
+ * name must use '.' to separate package components.
+ *
+ * TODO: this could be more efficient. Consider a custom
+ * conversion function here that walks the string once and
+ * avoids the allocation for the common case (name less than,
+ * say, 128 bytes).
+ */
+ char* dotName = dvmDescriptorToDot(clazz->descriptor);
+ nameObj = dvmCreateStringFromCstr(dotName);
+ free(dotName);
+ }
+
+ dvmReleaseTrackedAlloc((Object*) nameObj, NULL);
+
+#if 0
+ /* doesn't work -- need "java.lang.String" not "java/lang/String" */
+ {
+ /*
+ * Find the string in the DEX file and use the copy in the intern
+ * table if it already exists (else put one there). Only works
+ * for strings in the DEX file, e.g. not arrays.
+ *
+ * We have to do the class lookup by name in the DEX file because
+ * we don't have a DexClassDef pointer in the ClassObject, and it's
+ * not worth adding one there just for this. Should be cheaper
+ * to do this than the string-creation above.
+ */
+ const DexFile* pDexFile = clazz->pDexFile;
+ const DexClassDef* pClassDef;
+ const DexClassId* pClassId;
+
+ pDexFile = clazz->pDexFile;
+ pClassDef = dvmDexFindClass(pDexFile, clazz->descriptor);
+ pClassId = dvmDexGetClassId(pDexFile, pClassDef->classIdx);
+ nameObj = dvmDexGetResolvedString(pDexFile, pClassId->nameIdx);
+ if (nameObj == NULL) {
+ nameObj = dvmResolveString(clazz, pClassId->nameIdx);
+ if (nameObj == NULL)
+ LOGW("WARNING: couldn't find string %u for '%s'\n",
+ pClassId->nameIdx, clazz->name);
+ }
+ }
+#endif
+
+ RETURN_PTR(nameObj);
+}
+
+/*
+ * Return the superclass for instances of this class.
+ *
+ * If the class represents a java/lang/Object, an interface, a primitive
+ * type, or void (which *is* a primitive type??), return NULL.
+ *
+ * For an array, return the java/lang/Object ClassObject.
+ */
+static void Dalvik_java_lang_Class_getSuperclass(const u4* args,
+ JValue* pResult)
+{
+ ClassObject* clazz = (ClassObject*) args[0];
+
+ if (dvmIsPrimitiveClass(clazz) || dvmIsInterfaceClass(clazz))
+ RETURN_PTR(NULL);
+ else
+ RETURN_PTR(clazz->super);
+}
+
+/*
+ * public boolean isAssignableFrom(Class<?> cls)
+ *
+ * Determine if this class is either the same as, or is a superclass or
+ * superinterface of, the class specified in the "cls" parameter.
+ */
+static void Dalvik_java_lang_Class_isAssignableFrom(const u4* args,
+ JValue* pResult)
+{
+ ClassObject* thisPtr = (ClassObject*) args[0];
+ ClassObject* testClass = (ClassObject*) args[1];
+
+ if (testClass == NULL) {
+ dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+ RETURN_INT(false);
+ }
+ RETURN_INT(dvmInstanceof(testClass, thisPtr));
+}
+
+/*
+ * public boolean isInstance(Object o)
+ *
+ * Dynamic equivalent of Java programming language "instanceof".
+ */
+static void Dalvik_java_lang_Class_isInstance(const u4* args,
+ JValue* pResult)
+{
+ ClassObject* thisPtr = (ClassObject*) args[0];
+ Object* testObj = (Object*) args[1];
+
+ if (testObj == NULL)
+ RETURN_INT(false);
+ RETURN_INT(dvmInstanceof(testObj->clazz, thisPtr));
+}
+
+/*
+ * public boolean isInterface()
+ */
+static void Dalvik_java_lang_Class_isInterface(const u4* args,
+ JValue* pResult)
+{
+ ClassObject* thisPtr = (ClassObject*) args[0];
+
+ RETURN_INT(dvmIsInterfaceClass(thisPtr));
+}
+
+/*
+ * public boolean isPrimitive()
+ */
+static void Dalvik_java_lang_Class_isPrimitive(const u4* args,
+ JValue* pResult)
+{
+ ClassObject* thisPtr = (ClassObject*) args[0];
+
+ RETURN_INT(dvmIsPrimitiveClass(thisPtr));
+}
+
+/*
+ * public T newInstance() throws InstantiationException, IllegalAccessException
+ *
+ * Create a new instance of this class.
+ */
+static void Dalvik_java_lang_Class_newInstance(const u4* args, JValue* pResult)
+{
+ Thread* self = dvmThreadSelf();
+ ClassObject* clazz = (ClassObject*) args[0];
+ Method* init;
+ Object* newObj;
+
+ /* can't instantiate these */
+ if (dvmIsPrimitiveClass(clazz) || dvmIsInterfaceClass(clazz)
+ || dvmIsArrayClass(clazz) || dvmIsAbstractClass(clazz))
+ {
+ LOGD("newInstance failed: p%d i%d [%d a%d\n",
+ dvmIsPrimitiveClass(clazz), dvmIsInterfaceClass(clazz),
+ dvmIsArrayClass(clazz), dvmIsAbstractClass(clazz));
+ dvmThrowExceptionWithClassMessage("Ljava/lang/InstantiationException;",
+ clazz->descriptor);
+ RETURN_VOID();
+ }
+
+ /* initialize the class if it hasn't been already */
+ if (!dvmIsClassInitialized(clazz)) {
+ if (!dvmInitClass(clazz)) {
+ LOGW("Class init failed in newInstance call (%s)\n",
+ clazz->descriptor);
+ assert(dvmCheckException(self));
+ RETURN_VOID();
+ }
+ }
+
+ /* find the "nullary" constructor */
+ init = dvmFindDirectMethodByDescriptor(clazz, "<init>", "()V");
+ if (init == NULL) {
+ /* common cause: secret "this" arg on non-static inner class ctor */
+ LOGD("newInstance failed: no <init>()\n");
+ dvmThrowExceptionWithClassMessage("Ljava/lang/InstantiationException;",
+ clazz->descriptor);
+ RETURN_VOID();
+ }
+
+ /*
+ * Verify access from the call site.
+ *
+ * First, make sure the method invoking Class.newInstance() has permission
+ * to access the class.
+ *
+ * Second, make sure it has permission to invoke the constructor. The
+ * constructor must be public or, if the caller is in the same package,
+ * have package scope.
+ */
+ ClassObject* callerClass = dvmGetCaller2Class(self->curFrame);
+
+ if (!dvmCheckClassAccess(callerClass, clazz)) {
+ LOGD("newInstance failed: %s not accessible to %s\n",
+ clazz->descriptor, callerClass->descriptor);
+ dvmThrowException("Ljava/lang/IllegalAccessException;",
+ "access to class not allowed");
+ RETURN_VOID();
+ }
+ if (!dvmCheckMethodAccess(callerClass, init)) {
+ LOGD("newInstance failed: %s.<init>() not accessible to %s\n",
+ clazz->descriptor, callerClass->descriptor);
+ dvmThrowException("Ljava/lang/IllegalAccessException;",
+ "access to constructor not allowed");
+ RETURN_VOID();
+ }
+
+ newObj = dvmAllocObject(clazz, ALLOC_DEFAULT);
+ JValue unused;
+
+ /* invoke constructor; unlike reflection calls, we don't wrap exceptions */
+ dvmCallMethod(self, init, newObj, &unused);
+ dvmReleaseTrackedAlloc(newObj, NULL);
+
+ RETURN_PTR(newObj);
+}
+
+/*
+ * private Object[] getSignatureAnnotation()
+ *
+ * Returns the signature annotation array.
+ */
+static void Dalvik_java_lang_Class_getSignatureAnnotation(const u4* args,
+ JValue* pResult)
+{
+ ClassObject* clazz = (ClassObject*) args[0];
+ ArrayObject* arr = dvmGetClassSignatureAnnotation(clazz);
+
+ dvmReleaseTrackedAlloc((Object*) arr, NULL);
+ RETURN_PTR(arr);
+}
+
+/*
+ * public Class getDeclaringClass()
+ *
+ * Get the class that encloses this class (if any).
+ */
+static void Dalvik_java_lang_Class_getDeclaringClass(const u4* args,
+ JValue* pResult)
+{
+ ClassObject* clazz = (ClassObject*) args[0];
+
+ ClassObject* enclosing = dvmGetDeclaringClass(clazz);
+ dvmReleaseTrackedAlloc((Object*) enclosing, NULL);
+ RETURN_PTR(enclosing);
+}
+
+/*
+ * public Class getEnclosingClass()
+ *
+ * Get the class that encloses this class (if any).
+ */
+static void Dalvik_java_lang_Class_getEnclosingClass(const u4* args,
+ JValue* pResult)
+{
+ ClassObject* clazz = (ClassObject*) args[0];
+
+ ClassObject* enclosing = dvmGetEnclosingClass(clazz);
+ dvmReleaseTrackedAlloc((Object*) enclosing, NULL);
+ RETURN_PTR(enclosing);
+}
+
+/*
+ * public Constructor getEnclosingConstructor()
+ *
+ * Get the constructor that encloses this class (if any).
+ */
+static void Dalvik_java_lang_Class_getEnclosingConstructor(const u4* args,
+ JValue* pResult)
+{
+ ClassObject* clazz = (ClassObject*) args[0];
+
+ Object* enclosing = dvmGetEnclosingMethod(clazz);
+ if (enclosing != NULL) {
+ dvmReleaseTrackedAlloc(enclosing, NULL);
+ if (enclosing->clazz == gDvm.classJavaLangReflectConstructor) {
+ RETURN_PTR(enclosing);
+ }
+ assert(enclosing->clazz == gDvm.classJavaLangReflectMethod);
+ }
+ RETURN_PTR(NULL);
+}
+
+/*
+ * public Method getEnclosingMethod()
+ *
+ * Get the method that encloses this class (if any).
+ */
+static void Dalvik_java_lang_Class_getEnclosingMethod(const u4* args,
+ JValue* pResult)
+{
+ ClassObject* clazz = (ClassObject*) args[0];
+
+ Object* enclosing = dvmGetEnclosingMethod(clazz);
+ if (enclosing != NULL) {
+ dvmReleaseTrackedAlloc(enclosing, NULL);
+ if (enclosing->clazz == gDvm.classJavaLangReflectMethod) {
+ RETURN_PTR(enclosing);
+ }
+ assert(enclosing->clazz == gDvm.classJavaLangReflectConstructor);
+ }
+ RETURN_PTR(NULL);
+}
+
+#if 0
+static void Dalvik_java_lang_Class_getGenericInterfaces(const u4* args,
+ JValue* pResult)
+{
+ dvmThrowException("Ljava/lang/UnsupportedOperationException;",
+ "native method not implemented");
+
+ RETURN_PTR(NULL);
+}
+
+static void Dalvik_java_lang_Class_getGenericSuperclass(const u4* args,
+ JValue* pResult)
+{
+ dvmThrowException("Ljava/lang/UnsupportedOperationException;",
+ "native method not implemented");
+
+ RETURN_PTR(NULL);
+}
+
+static void Dalvik_java_lang_Class_getTypeParameters(const u4* args,
+ JValue* pResult)
+{
+ dvmThrowException("Ljava/lang/UnsupportedOperationException;",
+ "native method not implemented");
+
+ RETURN_PTR(NULL);
+}
+#endif
+
+/*
+ * public boolean isAnonymousClass()
+ *
+ * Returns true if this is an "anonymous" class.
+ */
+static void Dalvik_java_lang_Class_isAnonymousClass(const u4* args,
+ JValue* pResult)
+{
+ ClassObject* clazz = (ClassObject*) args[0];
+ StringObject* className = NULL;
+ int accessFlags;
+
+ /*
+ * If this has an InnerClass annotation, pull it out. Lack of the
+ * annotation, or an annotation with a NULL class name, indicates
+ * that this is an anonymous inner class.
+ */
+ if (!dvmGetInnerClass(clazz, &className, &accessFlags))
+ RETURN_BOOLEAN(false);
+
+ dvmReleaseTrackedAlloc((Object*) className, NULL);
+ RETURN_BOOLEAN(className == NULL);
+}
+
+/*
+ * private Annotation[] getDeclaredAnnotations()
+ *
+ * Return the annotations declared on this class.
+ */
+static void Dalvik_java_lang_Class_getDeclaredAnnotations(const u4* args,
+ JValue* pResult)
+{
+ ClassObject* clazz = (ClassObject*) args[0];
+
+ ArrayObject* annos = dvmGetClassAnnotations(clazz);
+ dvmReleaseTrackedAlloc((Object*) annos, NULL);
+ RETURN_PTR(annos);
+}
+
+/*
+ * public String getInnerClassName()
+ *
+ * Returns the simple name of a member class or local class, or null otherwise.
+ */
+static void Dalvik_java_lang_Class_getInnerClassName(const u4* args,
+ JValue* pResult)
+{
+ ClassObject* clazz = (ClassObject*) args[0];
+ StringObject* nameObj;
+ int flags;
+
+ if (dvmGetInnerClass(clazz, &nameObj, &flags)) {
+ dvmReleaseTrackedAlloc((Object*) nameObj, NULL);
+ RETURN_PTR(nameObj);
+ } else {
+ RETURN_PTR(NULL);
+ }
+}
+
+/*
+ * static native void setAccessibleNoCheck(AccessibleObject ao, boolean flag);
+ */
+static void Dalvik_java_lang_Class_setAccessibleNoCheck(const u4* args,
+ JValue* pResult)
+{
+ Object* target = (Object*) args[0];
+ u4 flag = (u4) args[1];
+
+ dvmSetFieldBoolean(target, gDvm.offJavaLangReflectAccessibleObject_flag,
+ flag);
+}
+
+const DalvikNativeMethod dvm_java_lang_Class[] = {
+ { "desiredAssertionStatus", "()Z",
+ Dalvik_java_lang_Class_desiredAssertionStatus },
+ { "classForName", "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;",
+ Dalvik_java_lang_Class_classForName },
+ { "getClassLoader", "(Ljava/lang/Class;)Ljava/lang/ClassLoader;",
+ Dalvik_java_lang_Class_getClassLoader },
+ { "getComponentType", "()Ljava/lang/Class;",
+ Dalvik_java_lang_Class_getComponentType },
+ { "getSignatureAnnotation", "()[Ljava/lang/Object;",
+ Dalvik_java_lang_Class_getSignatureAnnotation },
+ { "getDeclaredClasses", "(Ljava/lang/Class;Z)[Ljava/lang/Class;",
+ Dalvik_java_lang_Class_getDeclaredClasses },
+ { "getDeclaredConstructors", "(Ljava/lang/Class;Z)[Ljava/lang/reflect/Constructor;",
+ Dalvik_java_lang_Class_getDeclaredConstructors },
+ { "getDeclaredFields", "(Ljava/lang/Class;Z)[Ljava/lang/reflect/Field;",
+ Dalvik_java_lang_Class_getDeclaredFields },
+ { "getDeclaredMethods", "(Ljava/lang/Class;Z)[Ljava/lang/reflect/Method;",
+ Dalvik_java_lang_Class_getDeclaredMethods },
+ { "getInterfaces", "()[Ljava/lang/Class;",
+ Dalvik_java_lang_Class_getInterfaces },
+ { "getModifiers", "(Ljava/lang/Class;Z)I",
+ Dalvik_java_lang_Class_getModifiers },
+ { "getNameNative", "()Ljava/lang/String;",
+ Dalvik_java_lang_Class_getNameNative },
+ { "getSuperclass", "()Ljava/lang/Class;",
+ Dalvik_java_lang_Class_getSuperclass },
+ { "isAssignableFrom", "(Ljava/lang/Class;)Z",
+ Dalvik_java_lang_Class_isAssignableFrom },
+ { "isInstance", "(Ljava/lang/Object;)Z",
+ Dalvik_java_lang_Class_isInstance },
+ { "isInterface", "()Z",
+ Dalvik_java_lang_Class_isInterface },
+ { "isPrimitive", "()Z",
+ Dalvik_java_lang_Class_isPrimitive },
+ { "newInstanceImpl", "()Ljava/lang/Object;",
+ Dalvik_java_lang_Class_newInstance },
+ { "getDeclaringClass", "()Ljava/lang/Class;",
+ Dalvik_java_lang_Class_getDeclaringClass },
+ { "getEnclosingClass", "()Ljava/lang/Class;",
+ Dalvik_java_lang_Class_getEnclosingClass },
+ { "getEnclosingConstructor", "()Ljava/lang/reflect/Constructor;",
+ Dalvik_java_lang_Class_getEnclosingConstructor },
+ { "getEnclosingMethod", "()Ljava/lang/reflect/Method;",
+ Dalvik_java_lang_Class_getEnclosingMethod },
+#if 0
+ { "getGenericInterfaces", "()[Ljava/lang/reflect/Type;",
+ Dalvik_java_lang_Class_getGenericInterfaces },
+ { "getGenericSuperclass", "()Ljava/lang/reflect/Type;",
+ Dalvik_java_lang_Class_getGenericSuperclass },
+ { "getTypeParameters", "()Ljava/lang/reflect/TypeVariable;",
+ Dalvik_java_lang_Class_getTypeParameters },
+#endif
+ { "isAnonymousClass", "()Z",
+ Dalvik_java_lang_Class_isAnonymousClass },
+ { "getDeclaredAnnotations", "()[Ljava/lang/annotation/Annotation;",
+ Dalvik_java_lang_Class_getDeclaredAnnotations },
+ { "getInnerClassName", "()Ljava/lang/String;",
+ Dalvik_java_lang_Class_getInnerClassName },
+ { "setAccessibleNoCheck", "(Ljava/lang/reflect/AccessibleObject;Z)V",
+ Dalvik_java_lang_Class_setAccessibleNoCheck },
+ { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_Object.c b/vm/native/java_lang_Object.c
new file mode 100644
index 0000000..f2adf52
--- /dev/null
+++ b/vm/native/java_lang_Object.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * java.lang.Object
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * private Object internalClone()
+ *
+ * Implements most of Object.clone().
+ */
+static void Dalvik_java_lang_Object_internalClone(const u4* args,
+ JValue* pResult)
+{
+ Object* thisPtr = (Object*) args[0];
+ Object* clone = dvmCloneObject(thisPtr);
+
+ dvmReleaseTrackedAlloc(clone, NULL);
+ RETURN_PTR(clone);
+}
+
+/*
+ * public int hashCode()
+ */
+static void Dalvik_java_lang_Object_hashCode(const u4* args, JValue* pResult)
+{
+ Object* thisPtr = (Object*) args[0];
+ RETURN_INT(dvmIdentityHashCode(thisPtr));
+}
+
+/*
+ * public Class getClass()
+ */
+static void Dalvik_java_lang_Object_getClass(const u4* args, JValue* pResult)
+{
+ Object* thisPtr = (Object*) args[0];
+
+ RETURN_PTR(thisPtr->clazz);
+}
+
+/*
+ * public void notify()
+ *
+ * NOTE: we declare this as a full DalvikBridgeFunc, rather than a
+ * DalvikNativeFunc, because we really want to avoid the "self" lookup.
+ */
+static void Dalvik_java_lang_Object_notify(const u4* args, JValue* pResult,
+ const Method* method, Thread* self)
+{
+ Object* thisPtr = (Object*) args[0];
+
+ dvmObjectNotify(self, thisPtr);
+ RETURN_VOID();
+}
+
+/*
+ * public void notifyAll()
+ */
+static void Dalvik_java_lang_Object_notifyAll(const u4* args, JValue* pResult,
+ const Method* method, Thread* self)
+{
+ Object* thisPtr = (Object*) args[0];
+
+ dvmObjectNotifyAll(self, thisPtr);
+ RETURN_VOID();
+}
+
+/*
+ * public void wait(long ms, int ns) throws InterruptedException
+ */
+static void Dalvik_java_lang_Object_wait(const u4* args, JValue* pResult,
+ const Method* method, Thread* self)
+{
+ Object* thisPtr = (Object*) args[0];
+
+ dvmObjectWait(self, thisPtr, GET_ARG_LONG(args,1), (s4)args[3], true);
+ RETURN_VOID();
+}
+
+const DalvikNativeMethod dvm_java_lang_Object[] = {
+ { "internalClone", "(Ljava/lang/Cloneable;)Ljava/lang/Object;",
+ Dalvik_java_lang_Object_internalClone },
+ { "hashCode", "()I",
+ Dalvik_java_lang_Object_hashCode },
+ { "notify", "()V",
+ (DalvikNativeFunc) Dalvik_java_lang_Object_notify },
+ { "notifyAll", "()V",
+ (DalvikNativeFunc) Dalvik_java_lang_Object_notifyAll },
+ { "wait", "(JI)V",
+ (DalvikNativeFunc) Dalvik_java_lang_Object_wait },
+ { "getClass", "()Ljava/lang/Class;",
+ Dalvik_java_lang_Object_getClass },
+ { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_Runtime.c b/vm/native/java_lang_Runtime.c
new file mode 100644
index 0000000..d28ff84
--- /dev/null
+++ b/vm/native/java_lang_Runtime.c
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * java.lang.Runtime
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+#include <unistd.h>
+#include <limits.h>
+
+/*
+ * public void gc()
+ *
+ * Initiate a gc.
+ */
+static void Dalvik_java_lang_Runtime_gc(const u4* args, JValue* pResult)
+{
+ UNUSED_PARAMETER(args);
+
+ dvmCollectGarbage(false);
+ RETURN_VOID();
+}
+
+/*
+ * private static void nativeExit(int code, boolean isExit)
+ *
+ * Runtime.exit() calls this after doing shutdown processing. Runtime.halt()
+ * uses this as well.
+ */
+static void Dalvik_java_lang_Runtime_nativeExit(const u4* args,
+ JValue* pResult)
+{
+ int status = args[0];
+ bool isExit = (args[1] != 0);
+
+ if (isExit && gDvm.exitHook != NULL) {
+ dvmChangeStatus(NULL, THREAD_NATIVE);
+ (*gDvm.exitHook)(status); // not expected to return
+ dvmChangeStatus(NULL, THREAD_RUNNING);
+ LOGW("JNI exit hook returned\n");
+ }
+ LOGD("Calling exit(%d)\n", status);
+#if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
+ dvmCompilerDumpStats();
+#endif
+ exit(status);
+}
+
+/*
+ * static String nativeLoad(String filename, ClassLoader loader)
+ *
+ * Load the specified full path as a dynamic library filled with
+ * JNI-compatible methods. Returns null on success, or a failure
+ * message on failure.
+ */
+static void Dalvik_java_lang_Runtime_nativeLoad(const u4* args,
+ JValue* pResult)
+{
+ StringObject* fileNameObj = (StringObject*) args[0];
+ Object* classLoader = (Object*) args[1];
+ char* fileName = NULL;
+ StringObject* result = NULL;
+ char* reason = NULL;
+ bool success;
+
+ assert(fileNameObj != NULL);
+ fileName = dvmCreateCstrFromString(fileNameObj);
+
+ success = dvmLoadNativeCode(fileName, classLoader, &reason);
+ if (!success) {
+ const char* msg = (reason != NULL) ? reason : "unknown failure";
+ result = dvmCreateStringFromCstr(msg);
+ dvmReleaseTrackedAlloc((Object*) result, NULL);
+ }
+
+ free(reason);
+ free(fileName);
+ RETURN_PTR(result);
+}
+
+/*
+ * public void runFinalization(boolean forced)
+ *
+ * Requests that the VM runs finalizers for objects on the heap. If the
+ * parameter forced is true, then the VM needs to ensure finalization.
+ * Otherwise this only inspires the VM to make a best-effort attempt to
+ * run finalizers before returning, but it's not guaranteed to actually
+ * do anything.
+ */
+static void Dalvik_java_lang_Runtime_runFinalization(const u4* args,
+ JValue* pResult)
+{
+ bool forced = (args[0] != 0);
+
+ dvmWaitForHeapWorkerIdle();
+ if (forced) {
+ // TODO(Google) Need to explicitly implement this,
+ // although dvmWaitForHeapWorkerIdle()
+ // should usually provide the "forced"
+ // behavior already.
+ }
+
+ RETURN_VOID();
+}
+
+/*
+ * public int availableProcessors()
+ *
+ * Returns the number of online processors, at least one.
+ *
+ */
+static void Dalvik_java_lang_Runtime_availableProcessors(const u4* args,
+ JValue* pResult)
+{
+ long result = 1;
+#ifdef _SC_NPROCESSORS_ONLN
+ result = sysconf(_SC_NPROCESSORS_ONLN);
+ if (result > INT_MAX) {
+ result = INT_MAX;
+ } else if (result < 1 ) {
+ result = 1;
+ }
+#endif
+ RETURN_INT((int)result);
+}
+/*
+ * public void maxMemory()
+ *
+ * Returns GC heap max memory in bytes.
+ */
+static void Dalvik_java_lang_Runtime_maxMemory(const u4* args, JValue* pResult)
+{
+ unsigned int result = gDvm.heapSizeMax;
+ RETURN_LONG(result);
+}
+
+/*
+ * public void totalMemory()
+ *
+ * Returns GC heap total memory in bytes.
+ */
+static void Dalvik_java_lang_Runtime_totalMemory(const u4* args,
+ JValue* pResult)
+{
+ int result = dvmGetHeapDebugInfo(kVirtualHeapSize);
+ RETURN_LONG(result);
+}
+
+/*
+ * public void freeMemory()
+ *
+ * Returns GC heap free memory in bytes.
+ */
+static void Dalvik_java_lang_Runtime_freeMemory(const u4* args,
+ JValue* pResult)
+{
+ int result = dvmGetHeapDebugInfo(kVirtualHeapSize)
+ - dvmGetHeapDebugInfo(kVirtualHeapAllocated);
+ if (result < 0) {
+ result = 0;
+ }
+ RETURN_LONG(result);
+}
+
+const DalvikNativeMethod dvm_java_lang_Runtime[] = {
+ { "freeMemory", "()J",
+ Dalvik_java_lang_Runtime_freeMemory },
+ { "gc", "()V",
+ Dalvik_java_lang_Runtime_gc },
+ { "availableProcessors", "()I",
+ Dalvik_java_lang_Runtime_availableProcessors },
+ { "maxMemory", "()J",
+ Dalvik_java_lang_Runtime_maxMemory },
+ { "nativeExit", "(IZ)V",
+ Dalvik_java_lang_Runtime_nativeExit },
+ { "nativeLoad", "(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/String;",
+ Dalvik_java_lang_Runtime_nativeLoad },
+ { "runFinalization", "(Z)V",
+ Dalvik_java_lang_Runtime_runFinalization },
+ { "totalMemory", "()J",
+ Dalvik_java_lang_Runtime_totalMemory },
+ { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_String.c b/vm/native/java_lang_String.c
new file mode 100644
index 0000000..b3cb7ec
--- /dev/null
+++ b/vm/native/java_lang_String.c
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * java.lang.String
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * public String intern()
+ *
+ * Intern a string in the VM string table.
+ */
+static void Dalvik_java_lang_String_intern(const u4* args, JValue* pResult)
+{
+ StringObject* str = (StringObject*) args[0];
+ StringObject* interned;
+
+ interned = dvmLookupInternedString(str);
+ RETURN_PTR(interned);
+}
+
+const DalvikNativeMethod dvm_java_lang_String[] = {
+ { "intern", "()Ljava/lang/String;",
+ Dalvik_java_lang_String_intern },
+ { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_System.c b/vm/native/java_lang_System.c
new file mode 100644
index 0000000..4af0dfa
--- /dev/null
+++ b/vm/native/java_lang_System.c
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * java.lang.Class
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+/*
+ * Call the appropriate copy function given the circumstances.
+ */
+static void copy(void *dest, const void *src, size_t n, bool sameArray,
+ size_t elemSize)
+{
+ if (sameArray) {
+ /* Might overlap. */
+ if (elemSize == sizeof(Object*)) {
+ /*
+ * In addition to handling overlap properly, bcopy()
+ * guarantees atomic treatment of words. This is needed so
+ * that concurrent threads never see half-formed pointers
+ * or ints. The former is required for proper gc behavior,
+ * and the latter is also required for proper high-level
+ * language support.
+ *
+ * Note: bcopy()'s argument order is different than memcpy().
+ */
+ bcopy(src, dest, n);
+ } else {
+ memmove(dest, src, n);
+ }
+ } else {
+ memcpy(dest, src, n); /* Can't overlap; use faster function. */
+ }
+}
+
+/*
+ * public static void arraycopy(Object src, int srcPos, Object dest,
+ * int destPos, int length)
+ *
+ * The description of this function is long, and describes a multitude
+ * of checks and exceptions.
+ */
+static void Dalvik_java_lang_System_arraycopy(const u4* args, JValue* pResult)
+{
+ ArrayObject* srcArray;
+ ArrayObject* dstArray;
+ ClassObject* srcClass;
+ ClassObject* dstClass;
+ int srcPos, dstPos, length;
+ char srcType, dstType;
+ bool srcPrim, dstPrim;
+ bool sameArray;
+
+ srcArray = (ArrayObject*) args[0];
+ srcPos = args[1];
+ dstArray = (ArrayObject*) args[2];
+ dstPos = args[3];
+ length = args[4];
+
+ sameArray = (srcArray == dstArray);
+
+ /* check for null or bad pointer */
+ if (!dvmValidateObject((Object*)srcArray) ||
+ !dvmValidateObject((Object*)dstArray))
+ {
+ assert(dvmCheckException(dvmThreadSelf()));
+ RETURN_VOID();
+ }
+ /* make sure it's an array */
+ if (!dvmIsArray(srcArray) || !dvmIsArray(dstArray)) {
+ dvmThrowExceptionFmt("Ljava/lang/ArrayStoreException;",
+ "source and destination must be arrays, but were %s and %s",
+ ((Object*)srcArray)->clazz->descriptor,
+ ((Object*)dstArray)->clazz->descriptor);
+ RETURN_VOID();
+ }
+
+ // avoid int overflow
+ if (srcPos < 0 || dstPos < 0 || length < 0 ||
+ srcPos > (int) srcArray->length - length ||
+ dstPos > (int) dstArray->length - length)
+ {
+ dvmThrowExceptionFmt("Ljava/lang/ArrayIndexOutOfBoundsException;",
+ "src.length=%d srcPos=%d dst.length=%d dstPos=%d length=%d",
+ srcArray->length, srcPos, dstArray->length, dstPos, length);
+ RETURN_VOID();
+ }
+
+ srcClass = srcArray->obj.clazz;
+ dstClass = dstArray->obj.clazz;
+ srcType = srcClass->descriptor[1];
+ dstType = dstClass->descriptor[1];
+
+ /*
+ * If one of the arrays holds a primitive type, the other array must
+ * hold the same type.
+ */
+ srcPrim = (srcType != '[' && srcType != 'L');
+ dstPrim = (dstType != '[' && dstType != 'L');
+ if (srcPrim || dstPrim) {
+ int width;
+
+ if (srcPrim != dstPrim || srcType != dstType) {
+ dvmThrowExceptionFmt("Ljava/lang/ArrayStoreException;",
+ "source and destination arrays are incompatible: %s and %s",
+ srcClass->descriptor, dstClass->descriptor);
+ RETURN_VOID();
+ }
+
+ switch (srcClass->descriptor[1]) {
+ case 'B':
+ case 'Z':
+ width = 1;
+ break;
+ case 'C':
+ case 'S':
+ width = 2;
+ break;
+ case 'F':
+ case 'I':
+ width = 4;
+ break;
+ case 'D':
+ case 'J':
+ width = 8;
+ break;
+ default: /* 'V' or something weird */
+ LOGE("Weird array type '%s'\n", srcClass->descriptor);
+ assert(false);
+ width = 0;
+ break;
+ }
+
+ if (false) LOGVV("arraycopy prim dst=%p %d src=%p %d len=%d\n",
+ dstArray->contents, dstPos * width,
+ srcArray->contents, srcPos * width,
+ length * width);
+ copy((u1*)dstArray->contents + dstPos * width,
+ (const u1*)srcArray->contents + srcPos * width,
+ length * width,
+ sameArray, width);
+ } else {
+ /*
+ * Neither class is primitive. See if elements in "src" are instances
+ * of elements in "dst" (e.g. copy String to String or String to
+ * Object).
+ */
+ int width = sizeof(Object*);
+
+ if (srcClass->arrayDim == dstClass->arrayDim &&
+ dvmInstanceof(srcClass, dstClass))
+ {
+ /*
+ * "dst" can hold "src"; copy the whole thing.
+ */
+ if (false) LOGVV("arraycopy ref dst=%p %d src=%p %d len=%d\n",
+ dstArray->contents, dstPos * width,
+ srcArray->contents, srcPos * width,
+ length * width);
+ copy((u1*)dstArray->contents + dstPos * width,
+ (const u1*)srcArray->contents + srcPos * width,
+ length * width,
+ sameArray, width);
+ dvmWriteBarrierArray(dstArray, dstPos, dstPos+length);
+ } else {
+ /*
+ * The arrays are not fundamentally compatible. However, we may
+ * still be able to do this if the destination object is compatible
+ * (e.g. copy Object to String, but the Object being copied is
+ * actually a String). We need to copy elements one by one until
+ * something goes wrong.
+ *
+ * Because of overlapping moves, what we really want to do is
+ * compare the types and count up how many we can move, then call
+ * memmove() to shift the actual data. If we just start from the
+ * front we could do a smear rather than a move.
+ */
+ Object** srcObj;
+ Object** dstObj;
+ int copyCount;
+ ClassObject* clazz = NULL;
+
+ srcObj = ((Object**) srcArray->contents) + srcPos;
+ dstObj = ((Object**) dstArray->contents) + dstPos;
+
+ if (length > 0 && srcObj[0] != NULL)
+ {
+ clazz = srcObj[0]->clazz;
+ if (!dvmCanPutArrayElement(clazz, dstClass))
+ clazz = NULL;
+ }
+
+ for (copyCount = 0; copyCount < length; copyCount++)
+ {
+ if (srcObj[copyCount] != NULL &&
+ srcObj[copyCount]->clazz != clazz &&
+ !dvmCanPutArrayElement(srcObj[copyCount]->clazz, dstClass))
+ {
+ /* can't put this element into the array */
+ break;
+ }
+ }
+
+ if (false) LOGVV("arraycopy iref dst=%p %d src=%p %d count=%d of %d\n",
+ dstArray->contents, dstPos * width,
+ srcArray->contents, srcPos * width,
+ copyCount, length);
+ copy((u1*)dstArray->contents + dstPos * width,
+ (const u1*)srcArray->contents + srcPos * width,
+ copyCount * width,
+ sameArray, width);
+ dvmWriteBarrierArray(dstArray, 0, copyCount);
+ if (copyCount != length) {
+ dvmThrowExceptionFmt("Ljava/lang/ArrayStoreException;",
+ "source[%d] of type %s cannot be stored in destination array of type %s",
+ copyCount, srcObj[copyCount]->clazz->descriptor,
+ dstClass->descriptor);
+ RETURN_VOID();
+ }
+ }
+ }
+
+ RETURN_VOID();
+}
+
+/*
+ * static long currentTimeMillis()
+ *
+ * Current time, in miliseconds. This doesn't need to be internal to the
+ * VM, but we're already handling java.lang.System here.
+ */
+static void Dalvik_java_lang_System_currentTimeMillis(const u4* args,
+ JValue* pResult)
+{
+ struct timeval tv;
+
+ UNUSED_PARAMETER(args);
+
+ gettimeofday(&tv, (struct timezone *) NULL);
+ long long when = tv.tv_sec * 1000LL + tv.tv_usec / 1000;
+
+ RETURN_LONG(when);
+}
+
+/*
+ * static long nanoTime()
+ *
+ * Current monotonically-increasing time, in nanoseconds. This doesn't
+ * need to be internal to the VM, but we're already handling
+ * java.lang.System here.
+ */
+static void Dalvik_java_lang_System_nanoTime(const u4* args, JValue* pResult)
+{
+ UNUSED_PARAMETER(args);
+
+ u8 when = dvmGetRelativeTimeNsec();
+ RETURN_LONG(when);
+}
+
+/*
+ * static int identityHashCode(Object x)
+ *
+ * Returns that hash code that the default hashCode()
+ * method would return for "x", even if "x"s class
+ * overrides hashCode().
+ */
+static void Dalvik_java_lang_System_identityHashCode(const u4* args,
+ JValue* pResult)
+{
+ Object* thisPtr = (Object*) args[0];
+ RETURN_INT(dvmIdentityHashCode(thisPtr));
+}
+
+/*
+ * public static String mapLibraryName(String libname)
+ */
+static void Dalvik_java_lang_System_mapLibraryName(const u4* args,
+ JValue* pResult)
+{
+ StringObject* nameObj = (StringObject*) args[0];
+ StringObject* result = NULL;
+ char* name;
+ char* mappedName;
+
+ if (nameObj == NULL) {
+ dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+ RETURN_VOID();
+ }
+
+ name = dvmCreateCstrFromString(nameObj);
+ mappedName = dvmCreateSystemLibraryName(name);
+ if (mappedName != NULL) {
+ result = dvmCreateStringFromCstr(mappedName);
+ dvmReleaseTrackedAlloc((Object*) result, NULL);
+ }
+
+ free(name);
+ free(mappedName);
+ RETURN_PTR(result);
+}
+
+const DalvikNativeMethod dvm_java_lang_System[] = {
+ { "arraycopy", "(Ljava/lang/Object;ILjava/lang/Object;II)V",
+ Dalvik_java_lang_System_arraycopy },
+ { "currentTimeMillis", "()J",
+ Dalvik_java_lang_System_currentTimeMillis },
+ { "nanoTime", "()J",
+ Dalvik_java_lang_System_nanoTime },
+ { "identityHashCode", "(Ljava/lang/Object;)I",
+ Dalvik_java_lang_System_identityHashCode },
+ { "mapLibraryName", "(Ljava/lang/String;)Ljava/lang/String;",
+ Dalvik_java_lang_System_mapLibraryName },
+ { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_SystemProperties.c b/vm/native/java_lang_SystemProperties.c
new file mode 100644
index 0000000..bbcf25e
--- /dev/null
+++ b/vm/native/java_lang_SystemProperties.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * java.lang.SystemProperties
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * Expected call sequence:
+ * (1) call SystemProperties.preInit() to get VM defaults
+ * (2) set any higher-level defaults
+ * (3) call SystemProperties.postInit() to get command-line overrides
+ * This currently happens the first time somebody tries to access a property.
+ *
+ * SystemProperties is a Dalvik-specific package-scope class.
+ */
+
+/*
+ * void preInit()
+ *
+ * Tells the VM to populate the properties table with VM defaults.
+ */
+static void Dalvik_java_lang_SystemProperties_preInit(const u4* args,
+ JValue* pResult)
+{
+ dvmCreateDefaultProperties((Object*) args[0]);
+ RETURN_VOID();
+}
+
+/*
+ * void postInit()
+ *
+ * Tells the VM to update properties with values from the command line.
+ */
+static void Dalvik_java_lang_SystemProperties_postInit(const u4* args,
+ JValue* pResult)
+{
+ dvmSetCommandLineProperties((Object*) args[0]);
+ RETURN_VOID();
+}
+
+const DalvikNativeMethod dvm_java_lang_SystemProperties[] = {
+ { "preInit", "()V",
+ Dalvik_java_lang_SystemProperties_preInit },
+ { "postInit", "()V",
+ Dalvik_java_lang_SystemProperties_postInit },
+ { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_Throwable.c b/vm/native/java_lang_Throwable.c
new file mode 100644
index 0000000..f96ee6d
--- /dev/null
+++ b/vm/native/java_lang_Throwable.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * java.lang.Throwable
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * private static Object nativeFillInStackTrace()
+ */
+static void Dalvik_java_lang_Throwable_nativeFillInStackTrace(const u4* args,
+ JValue* pResult)
+{
+ Object* stackState = NULL;
+
+ UNUSED_PARAMETER(args);
+
+ stackState = dvmFillInStackTrace(dvmThreadSelf());
+ RETURN_PTR(stackState);
+}
+
+/*
+ * private static StackTraceElement[] nativeGetStackTrace(Object stackState)
+ *
+ * The "stackState" argument must be the value returned by an earlier call to
+ * nativeFillInStackTrace().
+ */
+static void Dalvik_java_lang_Throwable_nativeGetStackTrace(const u4* args,
+ JValue* pResult)
+{
+ Object* stackState = (Object*) args[0];
+ ArrayObject* elements = NULL;
+
+ if (stackState == NULL) {
+ LOGW("getStackTrace() called but no trace available\n");
+ RETURN_PTR(NULL); /* could throw NPE; currently caller will do so */
+ }
+
+ elements = dvmGetStackTrace(stackState);
+ RETURN_PTR(elements);
+}
+
+const DalvikNativeMethod dvm_java_lang_Throwable[] = {
+ { "nativeFillInStackTrace", "()Ljava/lang/Object;",
+ Dalvik_java_lang_Throwable_nativeFillInStackTrace },
+ { "nativeGetStackTrace", "(Ljava/lang/Object;)[Ljava/lang/StackTraceElement;",
+ Dalvik_java_lang_Throwable_nativeGetStackTrace },
+ { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_VMClassLoader.c b/vm/native/java_lang_VMClassLoader.c
new file mode 100644
index 0000000..e8dbc6e
--- /dev/null
+++ b/vm/native/java_lang_VMClassLoader.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * java.lang.VMClassLoader
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * static Class defineClass(ClassLoader cl, String name,
+ * byte[] data, int offset, int len, ProtectionDomain pd)
+ * throws ClassFormatError
+ *
+ * Convert an array of bytes to a Class object.
+ */
+static void Dalvik_java_lang_VMClassLoader_defineClass(const u4* args,
+ JValue* pResult)
+{
+ Object* loader = (Object*) args[0];
+ StringObject* nameObj = (StringObject*) args[1];
+ const u1* data = (const u1*) args[2];
+ int offset = args[3];
+ int len = args[4];
+ Object* pd = (Object*) args[5];
+ char* name = NULL;
+
+ name = dvmCreateCstrFromString(nameObj);
+ LOGE("ERROR: defineClass(%p, %s, %p, %d, %d, %p)\n",
+ loader, name, data, offset, len, pd);
+ dvmThrowException("Ljava/lang/UnsupportedOperationException;",
+ "can't load this type of class file");
+
+ free(name);
+ RETURN_VOID();
+}
+
+/*
+ * static Class defineClass(ClassLoader cl, byte[] data, int offset,
+ * int len, ProtectionDomain pd)
+ * throws ClassFormatError
+ *
+ * Convert an array of bytes to a Class object. Deprecated version of
+ * previous method, lacks name parameter.
+ */
+static void Dalvik_java_lang_VMClassLoader_defineClass2(const u4* args,
+ JValue* pResult)
+{
+ Object* loader = (Object*) args[0];
+ const u1* data = (const u1*) args[1];
+ int offset = args[2];
+ int len = args[3];
+ Object* pd = (Object*) args[4];
+
+ LOGE("ERROR: defineClass(%p, %p, %d, %d, %p)\n",
+ loader, data, offset, len, pd);
+ dvmThrowException("Ljava/lang/UnsupportedOperationException;",
+ "can't load this type of class file");
+
+ RETURN_VOID();
+}
+
+/*
+ * static Class findLoadedClass(ClassLoader cl, String name)
+ */
+static void Dalvik_java_lang_VMClassLoader_findLoadedClass(const u4* args,
+ JValue* pResult)
+{
+ Object* loader = (Object*) args[0];
+ StringObject* nameObj = (StringObject*) args[1];
+ ClassObject* clazz = NULL;
+ char* name = NULL;
+ char* descriptor = NULL;
+
+ if (nameObj == NULL) {
+ dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+ goto bail;
+ }
+
+ /*
+ * Get a UTF-8 copy of the string, and convert dots to slashes.
+ */
+ name = dvmCreateCstrFromString(nameObj);
+ if (name == NULL)
+ goto bail;
+
+ descriptor = dvmDotToDescriptor(name);
+ if (descriptor == NULL)
+ goto bail;
+
+ clazz = dvmLookupClass(descriptor, loader, false);
+ LOGVV("look: %s ldr=%p --> %p\n", descriptor, loader, clazz);
+
+bail:
+ free(name);
+ free(descriptor);
+ RETURN_PTR(clazz);
+}
+
+/*
+ * private static int getBootClassPathSize()
+ *
+ * Get the number of entries in the boot class path.
+ */
+static void Dalvik_java_lang_VMClassLoader_getBootClassPathSize(const u4* args,
+ JValue* pResult)
+{
+ int count = dvmGetBootPathSize();
+ RETURN_INT(count);
+}
+
+/*
+ * private static String getBootClassPathResource(String name, int index)
+ *
+ * Find a resource with a matching name in a boot class path entry.
+ *
+ * This mimics the previous VM interface, since we're sharing class libraries.
+ */
+static void Dalvik_java_lang_VMClassLoader_getBootClassPathResource(
+ const u4* args, JValue* pResult)
+{
+ StringObject* nameObj = (StringObject*) args[0];
+ StringObject* result;
+ int idx = args[1];
+ char* name;
+
+ name = dvmCreateCstrFromString(nameObj);
+ if (name == NULL)
+ RETURN_PTR(NULL);
+
+ result = dvmGetBootPathResource(name, idx);
+ free(name);
+ dvmReleaseTrackedAlloc((Object*)result, NULL);
+ RETURN_PTR(result);
+}
+
+/*
+ * static final Class getPrimitiveClass(char prim_type)
+ */
+static void Dalvik_java_lang_VMClassLoader_getPrimitiveClass(const u4* args,
+ JValue* pResult)
+{
+ int primType = args[0];
+
+ pResult->l = dvmFindPrimitiveClass(primType);
+}
+
+/*
+ * static Class loadClass(String name, boolean resolve)
+ * throws ClassNotFoundException
+ *
+ * Load class using bootstrap class loader.
+ *
+ * Return the Class object associated with the class or interface with
+ * the specified name.
+ *
+ * "name" is in "binary name" format, e.g. "dalvik.system.Debug$1".
+ */
+static void Dalvik_java_lang_VMClassLoader_loadClass(const u4* args,
+ JValue* pResult)
+{
+ StringObject* nameObj = (StringObject*) args[0];
+ bool resolve = (args[1] != 0);
+ ClassObject* clazz;
+
+ clazz = dvmFindClassByName(nameObj, NULL, resolve);
+ assert(clazz == NULL || dvmIsClassLinked(clazz));
+ RETURN_PTR(clazz);
+}
+
+const DalvikNativeMethod dvm_java_lang_VMClassLoader[] = {
+ { "defineClass", "(Ljava/lang/ClassLoader;Ljava/lang/String;[BIILjava/security/ProtectionDomain;)Ljava/lang/Class;",
+ Dalvik_java_lang_VMClassLoader_defineClass },
+ { "defineClass", "(Ljava/lang/ClassLoader;[BIILjava/security/ProtectionDomain;)Ljava/lang/Class;",
+ Dalvik_java_lang_VMClassLoader_defineClass2 },
+ { "findLoadedClass", "(Ljava/lang/ClassLoader;Ljava/lang/String;)Ljava/lang/Class;",
+ Dalvik_java_lang_VMClassLoader_findLoadedClass },
+ { "getBootClassPathSize", "()I",
+ Dalvik_java_lang_VMClassLoader_getBootClassPathSize },
+ { "getBootClassPathResource", "(Ljava/lang/String;I)Ljava/lang/String;",
+ Dalvik_java_lang_VMClassLoader_getBootClassPathResource },
+ { "getPrimitiveClass", "(C)Ljava/lang/Class;",
+ Dalvik_java_lang_VMClassLoader_getPrimitiveClass },
+ { "loadClass", "(Ljava/lang/String;Z)Ljava/lang/Class;",
+ Dalvik_java_lang_VMClassLoader_loadClass },
+ { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_VMThread.c b/vm/native/java_lang_VMThread.c
new file mode 100644
index 0000000..3b7331b
--- /dev/null
+++ b/vm/native/java_lang_VMThread.c
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * java.lang.VMThread
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * static void create(Thread t, long stacksize)
+ *
+ * This is eventually called as a result of Thread.start().
+ *
+ * Throws an exception on failure.
+ */
+static void Dalvik_java_lang_VMThread_create(const u4* args, JValue* pResult)
+{
+ Object* threadObj = (Object*) args[0];
+ s8 stackSize = GET_ARG_LONG(args, 1);
+
+ /* copying collector will pin threadObj for us since it was an argument */
+ dvmCreateInterpThread(threadObj, (int) stackSize);
+ RETURN_VOID();
+}
+
+/*
+ * static Thread currentThread()
+ */
+static void Dalvik_java_lang_VMThread_currentThread(const u4* args,
+ JValue* pResult)
+{
+ UNUSED_PARAMETER(args);
+
+ RETURN_PTR(dvmThreadSelf()->threadObj);
+}
+
+/*
+ * void getStatus()
+ *
+ * Gets the Thread status. Result is in VM terms, has to be mapped to
+ * Thread.State by interpreted code.
+ */
+static void Dalvik_java_lang_VMThread_getStatus(const u4* args, JValue* pResult)
+{
+ Object* thisPtr = (Object*) args[0];
+ Thread* thread;
+ int result;
+
+ dvmLockThreadList(NULL);
+ thread = dvmGetThreadFromThreadObject(thisPtr);
+ if (thread != NULL)
+ result = thread->status;
+ else
+ result = THREAD_ZOMBIE; // assume it used to exist and is now gone
+ dvmUnlockThreadList();
+
+ RETURN_INT(result);
+}
+
+/*
+ * boolean holdsLock(Object object)
+ *
+ * Returns whether the current thread has a monitor lock on the specific
+ * object.
+ */
+static void Dalvik_java_lang_VMThread_holdsLock(const u4* args, JValue* pResult)
+{
+ Object* thisPtr = (Object*) args[0];
+ Object* object = (Object*) args[1];
+ Thread* thread;
+
+ if (object == NULL) {
+ dvmThrowException("Ljava/lang/NullPointerException;", NULL);
+ RETURN_VOID();
+ }
+
+ dvmLockThreadList(NULL);
+ thread = dvmGetThreadFromThreadObject(thisPtr);
+ int result = dvmHoldsLock(thread, object);
+ dvmUnlockThreadList();
+
+ RETURN_BOOLEAN(result);
+}
+
+/*
+ * void interrupt()
+ *
+ * Interrupt a thread that is waiting (or is about to wait) on a monitor.
+ */
+static void Dalvik_java_lang_VMThread_interrupt(const u4* args, JValue* pResult)
+{
+ Object* thisPtr = (Object*) args[0];
+ Thread* thread;
+
+ dvmLockThreadList(NULL);
+ thread = dvmGetThreadFromThreadObject(thisPtr);
+ if (thread != NULL)
+ dvmThreadInterrupt(thread);
+ dvmUnlockThreadList();
+ RETURN_VOID();
+}
+
+/*
+ * static boolean interrupted()
+ *
+ * Determine if the current thread has been interrupted. Clears the flag.
+ */
+static void Dalvik_java_lang_VMThread_interrupted(const u4* args,
+ JValue* pResult)
+{
+ Thread* self = dvmThreadSelf();
+ bool interrupted;
+
+ UNUSED_PARAMETER(args);
+
+ interrupted = self->interrupted;
+ self->interrupted = false;
+ RETURN_BOOLEAN(interrupted);
+}
+
+/*
+ * boolean isInterrupted()
+ *
+ * Determine if the specified thread has been interrupted. Does not clear
+ * the flag.
+ */
+static void Dalvik_java_lang_VMThread_isInterrupted(const u4* args,
+ JValue* pResult)
+{
+ Object* thisPtr = (Object*) args[0];
+ Thread* thread;
+ bool interrupted;
+
+ dvmLockThreadList(NULL);
+ thread = dvmGetThreadFromThreadObject(thisPtr);
+ if (thread != NULL)
+ interrupted = thread->interrupted;
+ else
+ interrupted = false;
+ dvmUnlockThreadList();
+
+ RETURN_BOOLEAN(interrupted);
+}
+
+/*
+ * void nameChanged(String newName)
+ *
+ * The name of the target thread has changed. We may need to alert DDMS.
+ */
+static void Dalvik_java_lang_VMThread_nameChanged(const u4* args,
+ JValue* pResult)
+{
+ Object* thisPtr = (Object*) args[0];
+ StringObject* nameStr = (StringObject*) args[1];
+ Thread* thread;
+ int threadId = -1;
+
+ /* get the thread's ID */
+ dvmLockThreadList(NULL);
+ thread = dvmGetThreadFromThreadObject(thisPtr);
+ if (thread != NULL)
+ threadId = thread->threadId;
+ dvmUnlockThreadList();
+
+ dvmDdmSendThreadNameChange(threadId, nameStr);
+ //char* str = dvmCreateCstrFromString(nameStr);
+ //LOGI("UPDATE: threadid=%d now '%s'\n", threadId, str);
+ //free(str);
+
+ RETURN_VOID();
+}
+
+/*
+ * void setPriority(int newPriority)
+ *
+ * Alter the priority of the specified thread. "newPriority" will range
+ * from Thread.MIN_PRIORITY to Thread.MAX_PRIORITY (1-10), with "normal"
+ * threads at Thread.NORM_PRIORITY (5).
+ */
+static void Dalvik_java_lang_VMThread_setPriority(const u4* args,
+ JValue* pResult)
+{
+ Object* thisPtr = (Object*) args[0];
+ int newPriority = args[1];
+ Thread* thread;
+
+ dvmLockThreadList(NULL);
+ thread = dvmGetThreadFromThreadObject(thisPtr);
+ if (thread != NULL)
+ dvmChangeThreadPriority(thread, newPriority);
+ //dvmDumpAllThreads(false);
+ dvmUnlockThreadList();
+
+ RETURN_VOID();
+}
+
+/*
+ * static void sleep(long msec, int nsec)
+ */
+static void Dalvik_java_lang_VMThread_sleep(const u4* args, JValue* pResult)
+{
+ dvmThreadSleep(GET_ARG_LONG(args,0), args[2]);
+ RETURN_VOID();
+}
+
+/*
+ * public void yield()
+ *
+ * Causes the thread to temporarily pause and allow other threads to execute.
+ *
+ * The exact behavior is poorly defined. Some discussion here:
+ * http://www.cs.umd.edu/~pugh/java/memoryModel/archive/0944.html
+ */
+static void Dalvik_java_lang_VMThread_yield(const u4* args, JValue* pResult)
+{
+ UNUSED_PARAMETER(args);
+
+ sched_yield();
+
+ RETURN_VOID();
+}
+
+const DalvikNativeMethod dvm_java_lang_VMThread[] = {
+ { "create", "(Ljava/lang/Thread;J)V",
+ Dalvik_java_lang_VMThread_create },
+ { "currentThread", "()Ljava/lang/Thread;",
+ Dalvik_java_lang_VMThread_currentThread },
+ { "getStatus", "()I",
+ Dalvik_java_lang_VMThread_getStatus },
+ { "holdsLock", "(Ljava/lang/Object;)Z",
+ Dalvik_java_lang_VMThread_holdsLock },
+ { "interrupt", "()V",
+ Dalvik_java_lang_VMThread_interrupt },
+ { "interrupted", "()Z",
+ Dalvik_java_lang_VMThread_interrupted },
+ { "isInterrupted", "()Z",
+ Dalvik_java_lang_VMThread_isInterrupted },
+ { "nameChanged", "(Ljava/lang/String;)V",
+ Dalvik_java_lang_VMThread_nameChanged },
+ { "setPriority", "(I)V",
+ Dalvik_java_lang_VMThread_setPriority },
+ { "sleep", "(JI)V",
+ Dalvik_java_lang_VMThread_sleep },
+ { "yield", "()V",
+ Dalvik_java_lang_VMThread_yield },
+ { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_reflect_AccessibleObject.c b/vm/native/java_lang_reflect_AccessibleObject.c
new file mode 100644
index 0000000..46a1357
--- /dev/null
+++ b/vm/native/java_lang_reflect_AccessibleObject.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * java.lang.reflect.AccessibleObject
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * private static Object[] getClassSignatureAnnotation(Class clazz)
+ *
+ * Return the Signature annotation for the specified class. Equivalent to
+ * Class.getSignatureAnnotation(), but available to java.lang.reflect.
+ */
+static void Dalvik_java_lang_reflect_AccessibleObject_getClassSignatureAnnotation(
+ const u4* args, JValue* pResult)
+{
+ ClassObject* clazz = (ClassObject*) args[0];
+ ArrayObject* arr = dvmGetClassSignatureAnnotation(clazz);
+
+ dvmReleaseTrackedAlloc((Object*) arr, NULL);
+ RETURN_PTR(arr);
+}
+
+const DalvikNativeMethod dvm_java_lang_reflect_AccessibleObject[] = {
+ { "getClassSignatureAnnotation", "(Ljava/lang/Class;)[Ljava/lang/Object;",
+ Dalvik_java_lang_reflect_AccessibleObject_getClassSignatureAnnotation },
+ { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_reflect_Array.c b/vm/native/java_lang_reflect_Array.c
new file mode 100644
index 0000000..e7713f6
--- /dev/null
+++ b/vm/native/java_lang_reflect_Array.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * java.lang.reflect.Array
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * private static Object createObjectArray(Class<?> componentType,
+ * int length) throws NegativeArraySizeException;
+ *
+ * Create a one-dimensional array of Objects.
+ */
+static void Dalvik_java_lang_reflect_Array_createObjectArray(const u4* args,
+ JValue* pResult)
+{
+ ClassObject* elementClass = (ClassObject*) args[0];
+ int length = args[1];
+ ArrayObject* newArray;
+
+ assert(elementClass != NULL); // tested by caller
+ if (length < 0) {
+ dvmThrowException("Ljava/lang/NegativeArraySizeException;", NULL);
+ RETURN_VOID();
+ }
+
+ newArray = dvmAllocObjectArray(elementClass, length, ALLOC_DEFAULT);
+ if (newArray == NULL) {
+ assert(dvmCheckException(dvmThreadSelf()));
+ RETURN_VOID();
+ }
+ dvmReleaseTrackedAlloc((Object*) newArray, NULL);
+
+ RETURN_PTR(newArray);
+}
+
+/*
+ * private static Object createMultiArray(Class<?> componentType,
+ * int[] dimensions) throws NegativeArraySizeException;
+ *
+ * Create a multi-dimensional array of Objects or primitive types.
+ *
+ * We have to generate the names for X[], X[][], X[][][], and so on. The
+ * easiest way to deal with that is to create the full name once and then
+ * subtract pieces off. Besides, we want to start with the outermost
+ * piece and work our way in.
+ */
+static void Dalvik_java_lang_reflect_Array_createMultiArray(const u4* args,
+ JValue* pResult)
+{
+ static const char kPrimLetter[] = PRIM_TYPE_TO_LETTER;
+ ClassObject* elementClass = (ClassObject*) args[0];
+ ArrayObject* dimArray = (ArrayObject*) args[1];
+ ClassObject* arrayClass;
+ ArrayObject* newArray;
+ char* acDescriptor;
+ int numDim, i;
+ int* dimensions;
+
+ LOGV("createMultiArray: '%s' [%d]\n",
+ elementClass->descriptor, dimArray->length);
+
+ assert(elementClass != NULL); // verified by caller
+
+ /*
+ * Verify dimensions.
+ *
+ * The caller is responsible for verifying that "dimArray" is non-null
+ * and has a length > 0 and <= 255.
+ */
+ assert(dimArray != NULL); // verified by caller
+ numDim = dimArray->length;
+ assert(numDim > 0 && numDim <= 255);
+
+ dimensions = (int*) dimArray->contents;
+ for (i = 0; i < numDim; i++) {
+ if (dimensions[i] < 0) {
+ dvmThrowException("Ljava/lang/NegativeArraySizeException;", NULL);
+ RETURN_VOID();
+ }
+ LOGVV("DIM %d: %d\n", i, dimensions[i]);
+ }
+
+ /*
+ * Generate the full name of the array class.
+ */
+ acDescriptor =
+ (char*) malloc(strlen(elementClass->descriptor) + numDim + 1);
+ memset(acDescriptor, '[', numDim);
+
+ LOGVV("#### element name = '%s'\n", elementClass->descriptor);
+ if (dvmIsPrimitiveClass(elementClass)) {
+ assert(elementClass->primitiveType >= 0);
+ acDescriptor[numDim] = kPrimLetter[elementClass->primitiveType];
+ acDescriptor[numDim+1] = '\0';
+ } else {
+ strcpy(acDescriptor+numDim, elementClass->descriptor);
+ }
+ LOGVV("#### array name = '%s'\n", acDescriptor);
+
+ /*
+ * Find/generate the array class.
+ */
+ arrayClass = dvmFindArrayClass(acDescriptor, elementClass->classLoader);
+ if (arrayClass == NULL) {
+ LOGW("Unable to find or generate array class '%s'\n", acDescriptor);
+ assert(dvmCheckException(dvmThreadSelf()));
+ free(acDescriptor);
+ RETURN_VOID();
+ }
+ free(acDescriptor);
+
+ /* create the array */
+ newArray = dvmAllocMultiArray(arrayClass, numDim-1, dimensions);
+ if (newArray == NULL) {
+ assert(dvmCheckException(dvmThreadSelf()));
+ RETURN_VOID();
+ }
+
+ dvmReleaseTrackedAlloc((Object*) newArray, NULL);
+ RETURN_PTR(newArray);
+}
+
+const DalvikNativeMethod dvm_java_lang_reflect_Array[] = {
+ { "createObjectArray", "(Ljava/lang/Class;I)Ljava/lang/Object;",
+ Dalvik_java_lang_reflect_Array_createObjectArray },
+ { "createMultiArray", "(Ljava/lang/Class;[I)Ljava/lang/Object;",
+ Dalvik_java_lang_reflect_Array_createMultiArray },
+ { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_reflect_Constructor.c b/vm/native/java_lang_reflect_Constructor.c
new file mode 100644
index 0000000..76f69a9
--- /dev/null
+++ b/vm/native/java_lang_reflect_Constructor.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * java.lang.reflect.Constructor
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * public int getConstructorModifiers(Class declaringClass, int slot)
+ */
+static void Dalvik_java_lang_reflect_Constructor_getConstructorModifiers(
+ const u4* args, JValue* pResult)
+{
+ // ignore thisPtr in args[0]
+ ClassObject* declaringClass = (ClassObject*) args[1];
+ int slot = args[2];
+ Method* meth;
+
+ meth = dvmSlotToMethod(declaringClass, slot);
+ RETURN_INT(dvmFixMethodFlags(meth->accessFlags));
+}
+
+/*
+ * public int constructNative(Object[] args, Class declaringClass,
+ * Class[] parameterTypes, int slot, boolean noAccessCheck)
+ *
+ * We get here through Constructor.newInstance(). The Constructor object
+ * would not be available if the constructor weren't public (per the
+ * definition of Class.getConstructor), so we can skip the method access
+ * check. We can also safely assume the constructor isn't associated
+ * with an interface, array, or primitive class.
+ */
+static void Dalvik_java_lang_reflect_Constructor_constructNative(
+ const u4* args, JValue* pResult)
+{
+ // ignore thisPtr in args[0]
+ ArrayObject* argList = (ArrayObject*) args[1];
+ ClassObject* declaringClass = (ClassObject*) args[2];
+ ArrayObject* params = (ArrayObject*) args[3];
+ int slot = args[4];
+ bool noAccessCheck = (args[5] != 0);
+ Object* newObj;
+ Method* meth;
+
+ if (dvmIsAbstractClass(declaringClass)) {
+ dvmThrowExceptionWithClassMessage("Ljava/lang/InstantiationException;",
+ declaringClass->descriptor);
+ RETURN_VOID();
+ }
+
+ /* initialize the class if it hasn't been already */
+ if (!dvmIsClassInitialized(declaringClass)) {
+ if (!dvmInitClass(declaringClass)) {
+ LOGW("Class init failed in Constructor.constructNative (%s)\n",
+ declaringClass->descriptor);
+ assert(dvmCheckException(dvmThreadSelf()));
+ RETURN_VOID();
+ }
+ }
+
+ newObj = dvmAllocObject(declaringClass, ALLOC_DEFAULT);
+ if (newObj == NULL)
+ RETURN_PTR(NULL);
+
+ meth = dvmSlotToMethod(declaringClass, slot);
+ assert(meth != NULL);
+
+ (void) dvmInvokeMethod(newObj, meth, argList, params, NULL, noAccessCheck);
+ dvmReleaseTrackedAlloc(newObj, NULL);
+ RETURN_PTR(newObj);
+}
+
+/*
+ * public Annotation[] getDeclaredAnnotations(Class declaringClass, int slot)
+ *
+ * Return the annotations declared for this constructor.
+ */
+static void Dalvik_java_lang_reflect_Constructor_getDeclaredAnnotations(
+ const u4* args, JValue* pResult)
+{
+ // ignore thisPtr in args[0]
+ ClassObject* declaringClass = (ClassObject*) args[1];
+ int slot = args[2];
+ Method* meth;
+
+ meth = dvmSlotToMethod(declaringClass, slot);
+ assert(meth != NULL);
+
+ ArrayObject* annos = dvmGetMethodAnnotations(meth);
+ dvmReleaseTrackedAlloc((Object*)annos, NULL);
+ RETURN_PTR(annos);
+}
+
+/*
+ * public Annotation[][] getParameterAnnotations(Class declaringClass, int slot)
+ *
+ * Return the annotations declared for this constructor's parameters.
+ */
+static void Dalvik_java_lang_reflect_Constructor_getParameterAnnotations(
+ const u4* args, JValue* pResult)
+{
+ // ignore thisPtr in args[0]
+ ClassObject* declaringClass = (ClassObject*) args[1];
+ int slot = args[2];
+ Method* meth;
+
+ meth = dvmSlotToMethod(declaringClass, slot);
+ assert(meth != NULL);
+
+ ArrayObject* annos = dvmGetParameterAnnotations(meth);
+ dvmReleaseTrackedAlloc((Object*)annos, NULL);
+ RETURN_PTR(annos);
+}
+
+/*
+ * private Object[] getSignatureAnnotation()
+ *
+ * Returns the signature annotation.
+ */
+static void Dalvik_java_lang_reflect_Constructor_getSignatureAnnotation(
+ const u4* args, JValue* pResult)
+{
+ // ignore thisPtr in args[0]
+ ClassObject* declaringClass = (ClassObject*) args[1];
+ int slot = args[2];
+ Method* meth;
+
+ meth = dvmSlotToMethod(declaringClass, slot);
+ assert(meth != NULL);
+
+ ArrayObject* arr = dvmGetMethodSignatureAnnotation(meth);
+ dvmReleaseTrackedAlloc((Object*) arr, NULL);
+ RETURN_PTR(arr);
+}
+
+const DalvikNativeMethod dvm_java_lang_reflect_Constructor[] = {
+ { "constructNative", "([Ljava/lang/Object;Ljava/lang/Class;[Ljava/lang/Class;IZ)Ljava/lang/Object;",
+ Dalvik_java_lang_reflect_Constructor_constructNative },
+ { "getConstructorModifiers", "(Ljava/lang/Class;I)I",
+ Dalvik_java_lang_reflect_Constructor_getConstructorModifiers },
+ { "getDeclaredAnnotations", "(Ljava/lang/Class;I)[Ljava/lang/annotation/Annotation;",
+ Dalvik_java_lang_reflect_Constructor_getDeclaredAnnotations },
+ { "getParameterAnnotations", "(Ljava/lang/Class;I)[[Ljava/lang/annotation/Annotation;",
+ Dalvik_java_lang_reflect_Constructor_getParameterAnnotations },
+ { "getSignatureAnnotation", "(Ljava/lang/Class;I)[Ljava/lang/Object;",
+ Dalvik_java_lang_reflect_Constructor_getSignatureAnnotation },
+ { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_reflect_Field.c b/vm/native/java_lang_reflect_Field.c
new file mode 100644
index 0000000..cb9f2bf
--- /dev/null
+++ b/vm/native/java_lang_reflect_Field.c
@@ -0,0 +1,458 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * java.lang.reflect.Field
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * Get the address of a field from an object. This can be used with "get"
+ * or "set".
+ *
+ * "declaringClass" is the class in which the field was declared. For an
+ * instance field, "obj" is the object that holds the field data; for a
+ * static field its value is ignored.
+ *
+ * "If the underlying field is static, the class that declared the
+ * field is initialized if it has not already been initialized."
+ *
+ * On failure, throws an exception and returns NULL.
+ *
+ * The documentation lists exceptional conditions and the exceptions that
+ * should be thrown, but doesn't say which exception previals when two or
+ * more exceptional conditions exist at the same time. For example,
+ * attempting to set a protected field from an unrelated class causes an
+ * IllegalAccessException, while passing in a data type that doesn't match
+ * the field causes an IllegalArgumentException. If code does both at the
+ * same time, we have to choose one or othe other.
+ *
+ * The expected order is:
+ * (1) Check for illegal access. Throw IllegalAccessException.
+ * (2) Make sure the object actually has the field. Throw
+ * IllegalArgumentException.
+ * (3) Make sure the field matches the expected type, e.g. if we issued
+ * a "getInteger" call make sure the field is an integer or can be
+ * converted to an int with a widening conversion. Throw
+ * IllegalArgumentException.
+ * (4) Make sure "obj" is not null. Throw NullPointerException.
+ *
+ * TODO: we're currently handling #3 after #4, because we don't check the
+ * widening conversion until we're actually extracting the value from the
+ * object (which won't work well if it's a null reference).
+ */
+static JValue* getFieldDataAddr(Object* obj, ClassObject* declaringClass,
+ int slot, bool isSetOperation, bool noAccessCheck)
+{
+ Field* field;
+ JValue* result;
+
+ field = dvmSlotToField(declaringClass, slot);
+ assert(field != NULL);
+
+ /* verify access */
+ if (!noAccessCheck) {
+ if (isSetOperation && dvmIsFinalField(field)) {
+ dvmThrowException("Ljava/lang/IllegalAccessException;",
+ "field is marked 'final'");
+ return NULL;
+ }
+
+ ClassObject* callerClass =
+ dvmGetCaller2Class(dvmThreadSelf()->curFrame);
+
+ /*
+ * We need to check two things:
+ * (1) Would an instance of the calling class have access to the field?
+ * (2) If the field is "protected", is the object an instance of the
+ * calling class, or is the field's declaring class in the same
+ * package as the calling class?
+ *
+ * #1 is basic access control. #2 ensures that, just because
+ * you're a subclass of Foo, you can't mess with protected fields
+ * in arbitrary Foo objects from other packages.
+ */
+ if (!dvmCheckFieldAccess(callerClass, field)) {
+ dvmThrowException("Ljava/lang/IllegalAccessException;",
+ "access to field not allowed");
+ return NULL;
+ }
+ if (dvmIsProtectedField(field)) {
+ bool isInstance, samePackage;
+
+ if (obj != NULL)
+ isInstance = dvmInstanceof(obj->clazz, callerClass);
+ else
+ isInstance = false;
+ samePackage = dvmInSamePackage(declaringClass, callerClass);
+
+ if (!isInstance && !samePackage) {
+ dvmThrowException("Ljava/lang/IllegalAccessException;",
+ "access to protected field not allowed");
+ return NULL;
+ }
+ }
+ }
+
+ if (dvmIsStaticField(field)) {
+ /* init class if necessary, then return ptr to storage in "field" */
+ if (!dvmIsClassInitialized(declaringClass)) {
+ if (!dvmInitClass(declaringClass)) {
+ assert(dvmCheckException(dvmThreadSelf()));
+ return NULL;
+ }
+ }
+
+ result = dvmStaticFieldPtr((StaticField*) field);
+ } else {
+ /*
+ * Verify object is of correct type (i.e. it actually has the
+ * expected field in it), then grab a pointer to obj storage.
+ * The call to dvmVerifyObjectInClass throws an NPE if "obj" is NULL.
+ */
+ if (!dvmVerifyObjectInClass(obj, declaringClass)) {
+ assert(dvmCheckException(dvmThreadSelf()));
+ if (obj != NULL) {
+ LOGD("Wrong type of object for field lookup: %s %s\n",
+ obj->clazz->descriptor, declaringClass->descriptor);
+ }
+ return NULL;
+ }
+ result = dvmFieldPtr(obj, ((InstField*) field)->byteOffset);
+ }
+
+ return result;
+}
+
+/*
+ * public int getFieldModifiers(Class declaringClass, int slot)
+ */
+static void Dalvik_java_lang_reflect_Field_getFieldModifiers(
+ const u4* args, JValue* pResult)
+{
+ // ignore thisPtr in args[0]
+ ClassObject* declaringClass = (ClassObject*) args[1];
+ int slot = args[2];
+ Field* field;
+
+ field = dvmSlotToField(declaringClass, slot);
+ RETURN_INT(field->accessFlags & JAVA_FLAGS_MASK);
+}
+
+/*
+ * private Object getField(Object o, Class declaringClass, Class type,
+ * int slot, boolean noAccessCheck)
+ *
+ * Primitive types need to be boxed.
+ */
+static void Dalvik_java_lang_reflect_Field_getField(const u4* args,
+ JValue* pResult)
+{
+ // ignore thisPtr in args[0]
+ Object* obj = (Object*) args[1];
+ ClassObject* declaringClass = (ClassObject*) args[2];
+ ClassObject* fieldType = (ClassObject*) args[3];
+ int slot = args[4];
+ bool noAccessCheck = (args[5] != 0);
+ JValue value;
+ const JValue* fieldPtr;
+ DataObject* result;
+
+ //dvmDumpClass(obj->clazz, kDumpClassFullDetail);
+
+ /* get a pointer to the field's data; performs access checks */
+ fieldPtr = getFieldDataAddr(obj, declaringClass, slot, false,noAccessCheck);
+ if (fieldPtr == NULL)
+ RETURN_VOID();
+
+ /* copy 4 or 8 bytes out */
+ if (fieldType->primitiveType == PRIM_LONG ||
+ fieldType->primitiveType == PRIM_DOUBLE)
+ {
+ value.j = fieldPtr->j;
+ } else {
+ value.i = fieldPtr->i;
+ }
+
+ result = dvmWrapPrimitive(value, fieldType);
+ dvmReleaseTrackedAlloc((Object*) result, NULL);
+ RETURN_PTR(result);
+}
+
+/*
+ * private void setField(Object o, Class declaringClass, Class type,
+ * int slot, boolean noAccessCheck, Object value)
+ *
+ * When assigning into a primitive field we will automatically extract
+ * the value from box types.
+ */
+static void Dalvik_java_lang_reflect_Field_setField(const u4* args,
+ JValue* pResult)
+{
+ // ignore thisPtr in args[0]
+ Object* obj = (Object*) args[1];
+ ClassObject* declaringClass = (ClassObject*) args[2];
+ ClassObject* fieldType = (ClassObject*) args[3];
+ int slot = args[4];
+ bool noAccessCheck = (args[5] != 0);
+ Object* valueObj = (Object*) args[6];
+ JValue* fieldPtr;
+ JValue value;
+
+ /* unwrap primitive, or verify object type */
+ if (!dvmUnwrapPrimitive(valueObj, fieldType, &value)) {
+ dvmThrowException("Ljava/lang/IllegalArgumentException;",
+ "invalid value for field");
+ RETURN_VOID();
+ }
+
+ /* get a pointer to the field's data; performs access checks */
+ fieldPtr = getFieldDataAddr(obj, declaringClass, slot, true, noAccessCheck);
+ if (fieldPtr == NULL)
+ RETURN_VOID();
+
+ /* store 4 or 8 bytes */
+ if (fieldType->primitiveType == PRIM_LONG ||
+ fieldType->primitiveType == PRIM_DOUBLE)
+ {
+ fieldPtr->j = value.j;
+ } else if (fieldType->primitiveType == PRIM_NOT) {
+ if (slot < 0) {
+ StaticField *sfield;
+ sfield = (StaticField *)dvmSlotToField(declaringClass, slot);
+ assert(fieldPtr == &sfield->value);
+ dvmSetStaticFieldObject(sfield, value.l);
+ } else {
+ int offset = declaringClass->ifields[slot].byteOffset;
+ assert(fieldPtr == (JValue *)BYTE_OFFSET(obj, offset));
+ dvmSetFieldObject(obj, offset, value.l);
+ }
+ } else {
+ fieldPtr->i = value.i;
+ }
+
+ RETURN_VOID();
+}
+
+/*
+ * Convert a reflection primitive type ordinal (inherited from the previous
+ * VM's reflection classes) to our value.
+ */
+static PrimitiveType convPrimType(int typeNum)
+{
+ static const PrimitiveType conv[PRIM_MAX] = {
+ PRIM_NOT, PRIM_BOOLEAN, PRIM_BYTE, PRIM_CHAR, PRIM_SHORT,
+ PRIM_INT, PRIM_FLOAT, PRIM_LONG, PRIM_DOUBLE
+ };
+ if (typeNum <= 0 || typeNum > 8)
+ return PRIM_NOT;
+ return conv[typeNum];
+}
+
+/*
+ * Primitive field getters, e.g.:
+ * private double getIField(Object o, Class declaringClass,
+ * Class type, int slot, boolean noAccessCheck, int type_no)
+ *
+ * The "type_no" is defined by the java.lang.reflect.Field class.
+ */
+static void Dalvik_java_lang_reflect_Field_getPrimitiveField(const u4* args,
+ JValue* pResult)
+{
+ // ignore thisPtr in args[0]
+ Object* obj = (Object*) args[1];
+ ClassObject* declaringClass = (ClassObject*) args[2];
+ ClassObject* fieldType = (ClassObject*) args[3];
+ int slot = args[4];
+ bool noAccessCheck = (args[5] != 0);
+ int typeNum = args[6];
+ PrimitiveType targetType = convPrimType(typeNum);
+ const JValue* fieldPtr;
+ JValue value;
+
+ if (!dvmIsPrimitiveClass(fieldType)) {
+ dvmThrowException("Ljava/lang/IllegalArgumentException;",
+ "not a primitive field");
+ RETURN_VOID();
+ }
+
+ /* get a pointer to the field's data; performs access checks */
+ fieldPtr = getFieldDataAddr(obj, declaringClass, slot, false,noAccessCheck);
+ if (fieldPtr == NULL)
+ RETURN_VOID();
+
+ /* copy 4 or 8 bytes out */
+ if (fieldType->primitiveType == PRIM_LONG ||
+ fieldType->primitiveType == PRIM_DOUBLE)
+ {
+ value.j = fieldPtr->j;
+ } else {
+ value.i = fieldPtr->i;
+ }
+
+ /* retrieve value, performing a widening conversion if necessary */
+ if (dvmConvertPrimitiveValue(fieldType->primitiveType, targetType,
+ &(value.i), &(pResult->i)) < 0)
+ {
+ dvmThrowException("Ljava/lang/IllegalArgumentException;",
+ "invalid primitive conversion");
+ RETURN_VOID();
+ }
+}
+
+/*
+ * Primitive field setters, e.g.:
+ * private void setIField(Object o, Class declaringClass,
+ * Class type, int slot, boolean noAccessCheck, int type_no, int value)
+ *
+ * The "type_no" is defined by the java.lang.reflect.Field class.
+ */
+static void Dalvik_java_lang_reflect_Field_setPrimitiveField(const u4* args,
+ JValue* pResult)
+{
+ // ignore thisPtr in args[0]
+ Object* obj = (Object*) args[1];
+ ClassObject* declaringClass = (ClassObject*) args[2];
+ ClassObject* fieldType = (ClassObject*) args[3];
+ int slot = args[4];
+ bool noAccessCheck = (args[5] != 0);
+ int typeNum = args[6];
+ const s4* valuePtr = (s4*) &args[7];
+ PrimitiveType srcType = convPrimType(typeNum);
+ JValue* fieldPtr;
+ JValue value;
+
+ if (!dvmIsPrimitiveClass(fieldType)) {
+ dvmThrowException("Ljava/lang/IllegalArgumentException;",
+ "not a primitive field");
+ RETURN_VOID();
+ }
+
+ /* convert the 32/64-bit arg to a JValue matching the field type */
+ if (dvmConvertPrimitiveValue(srcType, fieldType->primitiveType,
+ valuePtr, &(value.i)) < 0)
+ {
+ dvmThrowException("Ljava/lang/IllegalArgumentException;",
+ "invalid primitive conversion");
+ RETURN_VOID();
+ }
+
+ /* get a pointer to the field's data; performs access checks */
+ fieldPtr = getFieldDataAddr(obj, declaringClass, slot, true, noAccessCheck);
+ if (fieldPtr == NULL)
+ RETURN_VOID();
+
+ /* store 4 or 8 bytes */
+ if (fieldType->primitiveType == PRIM_LONG ||
+ fieldType->primitiveType == PRIM_DOUBLE)
+ {
+ fieldPtr->j = value.j;
+ } else {
+ fieldPtr->i = value.i;
+ }
+
+ RETURN_VOID();
+}
+
+/*
+ * public Annotation[] getDeclaredAnnotations(Class declaringClass, int slot)
+ *
+ * Return the annotations declared for this field.
+ */
+static void Dalvik_java_lang_reflect_Field_getDeclaredAnnotations(
+ const u4* args, JValue* pResult)
+{
+ // ignore thisPtr in args[0]
+ ClassObject* declaringClass = (ClassObject*) args[1];
+ int slot = args[2];
+ Field* field;
+
+ field = dvmSlotToField(declaringClass, slot);
+ assert(field != NULL);
+
+ ArrayObject* annos = dvmGetFieldAnnotations(field);
+ dvmReleaseTrackedAlloc((Object*) annos, NULL);
+ RETURN_PTR(annos);
+}
+
+/*
+ * private Object[] getSignatureAnnotation()
+ *
+ * Returns the signature annotation.
+ */
+static void Dalvik_java_lang_reflect_Field_getSignatureAnnotation(const u4* args,
+ JValue* pResult)
+{
+ // ignore thisPtr in args[0]
+ ClassObject* declaringClass = (ClassObject*) args[1];
+ int slot = args[2];
+ Field* field;
+
+ field = dvmSlotToField(declaringClass, slot);
+ assert(field != NULL);
+
+ ArrayObject* arr = dvmGetFieldSignatureAnnotation(field);
+ dvmReleaseTrackedAlloc((Object*) arr, NULL);
+ RETURN_PTR(arr);
+}
+
+const DalvikNativeMethod dvm_java_lang_reflect_Field[] = {
+ { "getFieldModifiers", "(Ljava/lang/Class;I)I",
+ Dalvik_java_lang_reflect_Field_getFieldModifiers },
+ { "getField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZ)Ljava/lang/Object;",
+ Dalvik_java_lang_reflect_Field_getField },
+ { "getBField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)B",
+ Dalvik_java_lang_reflect_Field_getPrimitiveField },
+ { "getCField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)C",
+ Dalvik_java_lang_reflect_Field_getPrimitiveField },
+ { "getDField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)D",
+ Dalvik_java_lang_reflect_Field_getPrimitiveField },
+ { "getFField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)F",
+ Dalvik_java_lang_reflect_Field_getPrimitiveField },
+ { "getIField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)I",
+ Dalvik_java_lang_reflect_Field_getPrimitiveField },
+ { "getJField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)J",
+ Dalvik_java_lang_reflect_Field_getPrimitiveField },
+ { "getSField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)S",
+ Dalvik_java_lang_reflect_Field_getPrimitiveField },
+ { "getZField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)Z",
+ Dalvik_java_lang_reflect_Field_getPrimitiveField },
+ { "setField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZLjava/lang/Object;)V",
+ Dalvik_java_lang_reflect_Field_setField },
+ { "setBField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZIB)V",
+ Dalvik_java_lang_reflect_Field_setPrimitiveField },
+ { "setCField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZIC)V",
+ Dalvik_java_lang_reflect_Field_setPrimitiveField },
+ { "setDField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZID)V",
+ Dalvik_java_lang_reflect_Field_setPrimitiveField },
+ { "setFField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZIF)V",
+ Dalvik_java_lang_reflect_Field_setPrimitiveField },
+ { "setIField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZII)V",
+ Dalvik_java_lang_reflect_Field_setPrimitiveField },
+ { "setJField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZIJ)V",
+ Dalvik_java_lang_reflect_Field_setPrimitiveField },
+ { "setSField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZIS)V",
+ Dalvik_java_lang_reflect_Field_setPrimitiveField },
+ { "setZField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZIZ)V",
+ Dalvik_java_lang_reflect_Field_setPrimitiveField },
+ { "getDeclaredAnnotations", "(Ljava/lang/Class;I)[Ljava/lang/annotation/Annotation;",
+ Dalvik_java_lang_reflect_Field_getDeclaredAnnotations },
+ { "getSignatureAnnotation", "(Ljava/lang/Class;I)[Ljava/lang/Object;",
+ Dalvik_java_lang_reflect_Field_getSignatureAnnotation },
+ { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_reflect_Method.c b/vm/native/java_lang_reflect_Method.c
new file mode 100644
index 0000000..f73c8d0
--- /dev/null
+++ b/vm/native/java_lang_reflect_Method.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * java.lang.reflect.Method
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * private int getMethodModifiers(Class decl_class, int slot)
+ *
+ * (Not sure why the access flags weren't stored in the class along with
+ * everything else. Not sure why this isn't static.)
+ */
+static void Dalvik_java_lang_reflect_Method_getMethodModifiers(const u4* args,
+ JValue* pResult)
+{
+ // ignore thisPtr in args[0]
+ ClassObject* declaringClass = (ClassObject*) args[1];
+ int slot = args[2];
+ Method* meth;
+
+ meth = dvmSlotToMethod(declaringClass, slot);
+ RETURN_INT(dvmFixMethodFlags(meth->accessFlags));
+}
+
+/*
+ * private Object invokeNative(Object obj, Object[] args, Class declaringClass,
+ * Class[] parameterTypes, Class returnType, int slot, boolean noAccessCheck)
+ *
+ * Invoke a static or virtual method via reflection.
+ */
+static void Dalvik_java_lang_reflect_Method_invokeNative(const u4* args,
+ JValue* pResult)
+{
+ // ignore thisPtr in args[0]
+ Object* methObj = (Object*) args[1]; // null for static methods
+ ArrayObject* argList = (ArrayObject*) args[2];
+ ClassObject* declaringClass = (ClassObject*) args[3];
+ ArrayObject* params = (ArrayObject*) args[4];
+ ClassObject* returnType = (ClassObject*) args[5];
+ int slot = args[6];
+ bool noAccessCheck = (args[7] != 0);
+ const Method* meth;
+ Object* result;
+
+ /*
+ * "If the underlying method is static, the class that declared the
+ * method is initialized if it has not already been initialized."
+ */
+ meth = dvmSlotToMethod(declaringClass, slot);
+ assert(meth != NULL);
+
+ if (dvmIsStaticMethod(meth)) {
+ if (!dvmIsClassInitialized(declaringClass)) {
+ if (!dvmInitClass(declaringClass))
+ goto init_failed;
+ }
+ } else {
+ /* looks like interfaces need this too? */
+ if (dvmIsInterfaceClass(declaringClass) &&
+ !dvmIsClassInitialized(declaringClass))
+ {
+ if (!dvmInitClass(declaringClass))
+ goto init_failed;
+ }
+
+ /* make sure the object is an instance of the expected class */
+ if (!dvmVerifyObjectInClass(methObj, declaringClass)) {
+ assert(dvmCheckException(dvmThreadSelf()));
+ RETURN_VOID();
+ }
+
+ /* do the virtual table lookup for the method */
+ meth = dvmGetVirtualizedMethod(methObj->clazz, meth);
+ if (meth == NULL) {
+ assert(dvmCheckException(dvmThreadSelf()));
+ RETURN_VOID();
+ }
+ }
+
+ /*
+ * If the method has a return value, "result" will be an object or
+ * a boxed primitive.
+ */
+ result = dvmInvokeMethod(methObj, meth, argList, params, returnType,
+ noAccessCheck);
+
+ RETURN_PTR(result);
+
+init_failed:
+ /*
+ * If initialization failed, an exception will be raised.
+ */
+ LOGD("Method.invoke() on bad class %s failed\n",
+ declaringClass->descriptor);
+ assert(dvmCheckException(dvmThreadSelf()));
+ RETURN_VOID();
+}
+
+/*
+ * public Annotation[] getDeclaredAnnotations(Class declaringClass, int slot)
+ *
+ * Return the annotations declared for this method.
+ */
+static void Dalvik_java_lang_reflect_Method_getDeclaredAnnotations(
+ const u4* args, JValue* pResult)
+{
+ // ignore thisPtr in args[0]
+ ClassObject* declaringClass = (ClassObject*) args[1];
+ int slot = args[2];
+ Method* meth;
+
+ meth = dvmSlotToMethod(declaringClass, slot);
+ assert(meth != NULL);
+
+ ArrayObject* annos = dvmGetMethodAnnotations(meth);
+ dvmReleaseTrackedAlloc((Object*)annos, NULL);
+ RETURN_PTR(annos);
+}
+
+/*
+ * public Annotation[] getParameterAnnotations(Class declaringClass, int slot)
+ *
+ * Return the annotations declared for this method's parameters.
+ */
+static void Dalvik_java_lang_reflect_Method_getParameterAnnotations(
+ const u4* args, JValue* pResult)
+{
+ // ignore thisPtr in args[0]
+ ClassObject* declaringClass = (ClassObject*) args[1];
+ int slot = args[2];
+ Method* meth;
+
+ meth = dvmSlotToMethod(declaringClass, slot);
+ assert(meth != NULL);
+
+ ArrayObject* annos = dvmGetParameterAnnotations(meth);
+ dvmReleaseTrackedAlloc((Object*)annos, NULL);
+ RETURN_PTR(annos);
+}
+
+/*
+ * private Object getDefaultValue(Class declaringClass, int slot)
+ *
+ * Return the default value for the annotation member represented by
+ * this Method instance. Returns NULL if none is defined.
+ */
+static void Dalvik_java_lang_reflect_Method_getDefaultValue(const u4* args,
+ JValue* pResult)
+{
+ // ignore thisPtr in args[0]
+ ClassObject* declaringClass = (ClassObject*) args[1];
+ int slot = args[2];
+ Method* meth;
+
+ /* make sure this is an annotation class member */
+ if (!dvmIsAnnotationClass(declaringClass))
+ RETURN_PTR(NULL);
+
+ meth = dvmSlotToMethod(declaringClass, slot);
+ assert(meth != NULL);
+
+ Object* def = dvmGetAnnotationDefaultValue(meth);
+ dvmReleaseTrackedAlloc(def, NULL);
+ RETURN_PTR(def);
+}
+
+/*
+ * private Object[] getSignatureAnnotation()
+ *
+ * Returns the signature annotation.
+ */
+static void Dalvik_java_lang_reflect_Method_getSignatureAnnotation(
+ const u4* args, JValue* pResult)
+{
+ // ignore thisPtr in args[0]
+ ClassObject* declaringClass = (ClassObject*) args[1];
+ int slot = args[2];
+ Method* meth;
+
+ meth = dvmSlotToMethod(declaringClass, slot);
+ assert(meth != NULL);
+
+ ArrayObject* arr = dvmGetMethodSignatureAnnotation(meth);
+ dvmReleaseTrackedAlloc((Object*) arr, NULL);
+ RETURN_PTR(arr);
+}
+
+const DalvikNativeMethod dvm_java_lang_reflect_Method[] = {
+ { "getMethodModifiers", "(Ljava/lang/Class;I)I",
+ Dalvik_java_lang_reflect_Method_getMethodModifiers },
+ { "invokeNative", "(Ljava/lang/Object;[Ljava/lang/Object;Ljava/lang/Class;[Ljava/lang/Class;Ljava/lang/Class;IZ)Ljava/lang/Object;",
+ Dalvik_java_lang_reflect_Method_invokeNative },
+ { "getDeclaredAnnotations", "(Ljava/lang/Class;I)[Ljava/lang/annotation/Annotation;",
+ Dalvik_java_lang_reflect_Method_getDeclaredAnnotations },
+ { "getParameterAnnotations", "(Ljava/lang/Class;I)[[Ljava/lang/annotation/Annotation;",
+ Dalvik_java_lang_reflect_Method_getParameterAnnotations },
+ { "getDefaultValue", "(Ljava/lang/Class;I)Ljava/lang/Object;",
+ Dalvik_java_lang_reflect_Method_getDefaultValue },
+ { "getSignatureAnnotation", "(Ljava/lang/Class;I)[Ljava/lang/Object;",
+ Dalvik_java_lang_reflect_Method_getSignatureAnnotation },
+ { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_lang_reflect_Proxy.c b/vm/native/java_lang_reflect_Proxy.c
new file mode 100644
index 0000000..da1232c
--- /dev/null
+++ b/vm/native/java_lang_reflect_Proxy.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * java.lang.reflect.Proxy
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * static Class generateProxy(String name, Class[] interfaces,
+ * ClassLoader loader)
+ *
+ * Generate a proxy class with the specified characteristics. Throws an
+ * exception on error.
+ */
+static void Dalvik_java_lang_reflect_Proxy_generateProxy(const u4* args,
+ JValue* pResult)
+{
+ StringObject* str = (StringObject*) args[0];
+ ArrayObject* interfaces = (ArrayObject*) args[1];
+ Object* loader = (Object*) args[2];
+ ClassObject* result;
+
+ result = dvmGenerateProxyClass(str, interfaces, loader);
+ RETURN_PTR(result);
+}
+
+const DalvikNativeMethod dvm_java_lang_reflect_Proxy[] = {
+ { "generateProxy", "(Ljava/lang/String;[Ljava/lang/Class;Ljava/lang/ClassLoader;)Ljava/lang/Class;",
+ Dalvik_java_lang_reflect_Proxy_generateProxy },
+ { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_security_AccessController.c b/vm/native/java_security_AccessController.c
new file mode 100644
index 0000000..378fb94
--- /dev/null
+++ b/vm/native/java_security_AccessController.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * java.security.AccessController
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * private static ProtectionDomain[] getStackDomains()
+ *
+ * Return an array of ProtectionDomain objects from the classes of the
+ * methods on the stack. Ignore reflection frames. Stop at the first
+ * privileged frame we see.
+ */
+static void Dalvik_java_security_AccessController_getStackDomains(
+ const u4* args, JValue* pResult)
+{
+ UNUSED_PARAMETER(args);
+
+ const Method** methods = NULL;
+ int length;
+
+ /*
+ * Get an array with the stack trace in it.
+ */
+ if (!dvmCreateStackTraceArray(dvmThreadSelf()->curFrame, &methods, &length))
+ {
+ LOGE("Failed to create stack trace array\n");
+ dvmThrowException("Ljava/lang/InternalError;", NULL);
+ RETURN_VOID();
+ }
+
+ //int i;
+ //LOGI("dvmCreateStackTraceArray results:\n");
+ //for (i = 0; i < length; i++)
+ // LOGI(" %2d: %s.%s\n", i, methods[i]->clazz->name, methods[i]->name);
+
+ /*
+ * Generate a list of ProtectionDomain objects from the frames that
+ * we're interested in. Skip the first two methods (this method, and
+ * the one that called us), and ignore reflection frames. Stop on the
+ * frame *after* the first privileged frame we see as we walk up.
+ *
+ * We create a new array, probably over-allocated, and fill in the
+ * stuff we want. We could also just run the list twice, but the
+ * costs of the per-frame tests could be more expensive than the
+ * second alloc. (We could also allocate it on the stack using C99
+ * array creation, but it's not guaranteed to fit.)
+ *
+ * The array we return doesn't include null ProtectionDomain objects,
+ * so we skip those here.
+ */
+ Object** subSet = (Object**) malloc((length-2) * sizeof(Object*));
+ if (subSet == NULL) {
+ LOGE("Failed to allocate subSet (length=%d)\n", length);
+ free(methods);
+ dvmThrowException("Ljava/lang/InternalError;", NULL);
+ RETURN_VOID();
+ }
+ int idx, subIdx = 0;
+ for (idx = 2; idx < length; idx++) {
+ const Method* meth = methods[idx];
+ Object* pd;
+
+ if (dvmIsReflectionMethod(meth))
+ continue;
+
+ if (dvmIsPrivilegedMethod(meth)) {
+ /* find nearest non-reflection frame; note we skip priv frame */
+ //LOGI("GSD priv frame at %s.%s\n", meth->clazz->name, meth->name);
+ while (++idx < length && dvmIsReflectionMethod(methods[idx]))
+ ;
+ length = idx; // stomp length to end loop
+ meth = methods[idx];
+ }
+
+ /* get the pd object from the method's class */
+ assert(gDvm.offJavaLangClass_pd != 0);
+ pd = dvmGetFieldObject((Object*) meth->clazz,
+ gDvm.offJavaLangClass_pd);
+ //LOGI("FOUND '%s' pd=%p\n", meth->clazz->name, pd);
+ if (pd != NULL)
+ subSet[subIdx++] = pd;
+ }
+
+ //LOGI("subSet:\n");
+ //for (i = 0; i < subIdx; i++)
+ // LOGI(" %2d: %s\n", i, subSet[i]->clazz->name);
+
+ /*
+ * Create an array object to contain "subSet".
+ */
+ ClassObject* pdArrayClass = NULL;
+ ArrayObject* domains = NULL;
+ pdArrayClass = dvmFindArrayClass("[Ljava/security/ProtectionDomain;", NULL);
+ if (pdArrayClass == NULL) {
+ LOGW("Unable to find ProtectionDomain class for array\n");
+ goto bail;
+ }
+ domains = dvmAllocArray(pdArrayClass, subIdx, kObjectArrayRefWidth,
+ ALLOC_DEFAULT);
+ if (domains == NULL) {
+ LOGW("Unable to allocate pd array (%d elems)\n", subIdx);
+ goto bail;
+ }
+
+ /* copy the ProtectionDomain objects out */
+ memcpy(domains->contents, subSet, subIdx * sizeof(Object *));
+ dvmWriteBarrierArray(domains, 0, subIdx);
+
+bail:
+ free(subSet);
+ free(methods);
+ dvmReleaseTrackedAlloc((Object*) domains, NULL);
+ RETURN_PTR(domains);
+}
+
+const DalvikNativeMethod dvm_java_security_AccessController[] = {
+ { "getStackDomains", "()[Ljava/security/ProtectionDomain;",
+ Dalvik_java_security_AccessController_getStackDomains },
+ { NULL, NULL, NULL },
+};
diff --git a/vm/native/java_util_concurrent_atomic_AtomicLong.c b/vm/native/java_util_concurrent_atomic_AtomicLong.c
new file mode 100644
index 0000000..eb1d0de
--- /dev/null
+++ b/vm/native/java_util_concurrent_atomic_AtomicLong.c
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * java.util.concurrent.atomic.AtomicLong
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * private static native boolean VMSupportsCS8();
+ */
+static void Dalvik_java_util_concurrent_atomic_AtomicLong_VMSupportsCS8(
+ const u4* args, JValue* pResult)
+{
+ UNUSED_PARAMETER(args);
+ RETURN_BOOLEAN(1);
+}
+
+const DalvikNativeMethod dvm_java_util_concurrent_atomic_AtomicLong[] = {
+ { "VMSupportsCS8", "()Z",
+ Dalvik_java_util_concurrent_atomic_AtomicLong_VMSupportsCS8 },
+ { NULL, NULL, NULL },
+};
diff --git a/vm/native/org_apache_harmony_dalvik_NativeTestTarget.c b/vm/native/org_apache_harmony_dalvik_NativeTestTarget.c
new file mode 100644
index 0000000..ccc9467
--- /dev/null
+++ b/vm/native/org_apache_harmony_dalvik_NativeTestTarget.c
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * org.apache.harmony.dalvik.NativeTestTarget
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * public static void emptyInternalStaticMethod()
+ *
+ * For benchmarks, a do-nothing internal method with no arguments.
+ */
+static void Dalvik_org_apache_harmony_dalvik_NativeTestTarget_emptyInternalMethod(
+ const u4* args, JValue* pResult)
+{
+ UNUSED_PARAMETER(args);
+
+ RETURN_VOID();
+}
+
+const DalvikNativeMethod dvm_org_apache_harmony_dalvik_NativeTestTarget[] =
+{
+ { "emptyInternalStaticMethod", "()V",
+ Dalvik_org_apache_harmony_dalvik_NativeTestTarget_emptyInternalMethod },
+ { NULL, NULL, NULL },
+};
diff --git a/vm/native/org_apache_harmony_dalvik_ddmc_DdmServer.c b/vm/native/org_apache_harmony_dalvik_ddmc_DdmServer.c
new file mode 100644
index 0000000..570d469
--- /dev/null
+++ b/vm/native/org_apache_harmony_dalvik_ddmc_DdmServer.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * org.apache.harmony.dalvik.ddmc.DdmServer
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * private static void nativeSendChunk(int type, byte[] data,
+ * int offset, int length)
+ *
+ * Send a DDM chunk to the server.
+ */
+static void Dalvik_org_apache_harmony_dalvik_ddmc_DdmServer_nativeSendChunk(
+ const u4* args, JValue* pResult)
+{
+ int type = args[0];
+ ArrayObject* data = (ArrayObject*) args[1];
+ int offset = args[2];
+ int length = args[3];
+
+ assert(offset+length <= (int)data->length);
+
+ dvmDbgDdmSendChunk(type, length, (const u1*)data->contents + offset);
+ RETURN_VOID();
+}
+
+const DalvikNativeMethod dvm_org_apache_harmony_dalvik_ddmc_DdmServer[] = {
+ { "nativeSendChunk", "(I[BII)V",
+ Dalvik_org_apache_harmony_dalvik_ddmc_DdmServer_nativeSendChunk },
+ { NULL, NULL, NULL },
+};
diff --git a/vm/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.c b/vm/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.c
new file mode 100644
index 0000000..caa280b
--- /dev/null
+++ b/vm/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * org.apache.harmony.dalvik.ddmc.DdmVmInternal
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * public static void threadNotify(boolean enable)
+ *
+ * Enable DDM thread notifications.
+ */
+static void Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_threadNotify(
+ const u4* args, JValue* pResult)
+{
+ bool enable = (args[0] != 0);
+
+ //LOGI("ddmThreadNotification: %d\n", enable);
+ dvmDdmSetThreadNotification(enable);
+ RETURN_VOID();
+}
+
+/*
+ * public static byte[] getThreadStats()
+ *
+ * Get a buffer full of thread info.
+ */
+static void Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_getThreadStats(
+ const u4* args, JValue* pResult)
+{
+ UNUSED_PARAMETER(args);
+
+ ArrayObject* result = dvmDdmGenerateThreadStats();
+ dvmReleaseTrackedAlloc((Object*) result, NULL);
+ RETURN_PTR(result);
+}
+
+/*
+ * public static int heapInfoNotify(int what)
+ *
+ * Enable DDM heap notifications.
+ */
+static void Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_heapInfoNotify(
+ const u4* args, JValue* pResult)
+{
+ int when = args[0];
+ bool ret;
+
+ ret = dvmDdmHandleHpifChunk(when);
+ RETURN_BOOLEAN(ret);
+}
+
+/*
+ * public static boolean heapSegmentNotify(int when, int what, bool native)
+ *
+ * Enable DDM heap notifications.
+ */
+static void
+ Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_heapSegmentNotify(
+ const u4* args, JValue* pResult)
+{
+ int when = args[0]; // 0=never (off), 1=during GC
+ int what = args[1]; // 0=merged objects, 1=distinct objects
+ bool native = (args[2] != 0); // false=virtual heap, true=native heap
+ bool ret;
+
+ ret = dvmDdmHandleHpsgNhsgChunk(when, what, native);
+ RETURN_BOOLEAN(ret);
+}
+
+/*
+ * public static StackTraceElement[] getStackTraceById(int threadId)
+ *
+ * Get a stack trace as an array of StackTraceElement objects. Returns
+ * NULL on failure, e.g. if the threadId couldn't be found.
+ */
+static void
+ Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_getStackTraceById(
+ const u4* args, JValue* pResult)
+{
+ u4 threadId = args[0];
+ ArrayObject* trace;
+
+ trace = dvmDdmGetStackTraceById(threadId);
+ RETURN_PTR(trace);
+}
+
+/*
+ * public static void enableRecentAllocations(boolean enable)
+ *
+ * Enable or disable recent allocation tracking.
+ */
+static void
+ Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_enableRecentAllocations(
+ const u4* args, JValue* pResult)
+{
+ bool enable = (args[0] != 0);
+
+ if (enable)
+ (void) dvmEnableAllocTracker();
+ else
+ (void) dvmDisableAllocTracker();
+ RETURN_VOID();
+}
+
+/*
+ * public static boolean getRecentAllocationStatus()
+ *
+ * Returns "true" if allocation tracking is enabled.
+ */
+static void
+ Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_getRecentAllocationStatus(
+ const u4* args, JValue* pResult)
+{
+ UNUSED_PARAMETER(args);
+ RETURN_BOOLEAN(gDvm.allocRecords != NULL);
+}
+
+/*
+ * public static byte[] getRecentAllocations()
+ *
+ * Fill a buffer with data on recent heap allocations.
+ */
+static void
+ Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_getRecentAllocations(
+ const u4* args, JValue* pResult)
+{
+ ArrayObject* data;
+
+ data = dvmDdmGetRecentAllocations();
+ dvmReleaseTrackedAlloc((Object*) data, NULL);
+ RETURN_PTR(data);
+}
+
+const DalvikNativeMethod dvm_org_apache_harmony_dalvik_ddmc_DdmVmInternal[] = {
+ { "threadNotify", "(Z)V",
+ Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_threadNotify },
+ { "getThreadStats", "()[B",
+ Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_getThreadStats },
+ { "heapInfoNotify", "(I)Z",
+ Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_heapInfoNotify },
+ { "heapSegmentNotify", "(IIZ)Z",
+ Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_heapSegmentNotify },
+ { "getStackTraceById", "(I)[Ljava/lang/StackTraceElement;",
+ Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_getStackTraceById },
+ { "enableRecentAllocations", "(Z)V",
+ Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_enableRecentAllocations },
+ { "getRecentAllocationStatus", "()Z",
+ Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_getRecentAllocationStatus },
+ { "getRecentAllocations", "()[B",
+ Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_getRecentAllocations },
+ { NULL, NULL, NULL },
+};
diff --git a/vm/native/sun_misc_Unsafe.c b/vm/native/sun_misc_Unsafe.c
new file mode 100644
index 0000000..9b9caba
--- /dev/null
+++ b/vm/native/sun_misc_Unsafe.c
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * sun.misc.Unsafe
+ */
+#include "Dalvik.h"
+#include "native/InternalNativePriv.h"
+
+
+/*
+ * private static native long objectFieldOffset0(Field field);
+ */
+static void Dalvik_sun_misc_Unsafe_objectFieldOffset0(const u4* args,
+ JValue* pResult)
+{
+ Object* fieldObject = (Object*) args[0];
+ InstField* field = (InstField*) dvmGetFieldFromReflectObj(fieldObject);
+ s8 result = ((s8) field->byteOffset);
+
+ RETURN_LONG(result);
+}
+
+/*
+ * private static native int arrayBaseOffset0(Class clazz);
+ */
+static void Dalvik_sun_misc_Unsafe_arrayBaseOffset0(const u4* args,
+ JValue* pResult)
+{
+ // The base offset is not type-dependent in this vm.
+ UNUSED_PARAMETER(args);
+ RETURN_INT(offsetof(ArrayObject, contents));
+}
+
+/*
+ * private static native int arrayIndexScale0(Class clazz);
+ */
+static void Dalvik_sun_misc_Unsafe_arrayIndexScale0(const u4* args,
+ JValue* pResult)
+{
+ ClassObject* clazz = (ClassObject*) args[0];
+ RETURN_INT(dvmArrayClassElementWidth(clazz));
+}
+
+/*
+ * public native boolean compareAndSwapInt(Object obj, long offset,
+ * int expectedValue, int newValue);
+ */
+static void Dalvik_sun_misc_Unsafe_compareAndSwapInt(const u4* args,
+ JValue* pResult)
+{
+ // We ignore the this pointer in args[0].
+ Object* obj = (Object*) args[1];
+ s8 offset = GET_ARG_LONG(args, 2);
+ s4 expectedValue = args[4];
+ s4 newValue = args[5];
+ volatile int32_t* address = (volatile int32_t*) (((u1*) obj) + offset);
+
+ // Note: android_atomic_release_cas() returns 0 on success, not failure.
+ int result = android_atomic_release_cas(expectedValue, newValue, address);
+
+ RETURN_BOOLEAN(result == 0);
+}
+
+/*
+ * public native boolean compareAndSwapLong(Object obj, long offset,
+ * long expectedValue, long newValue);
+ */
+static void Dalvik_sun_misc_Unsafe_compareAndSwapLong(const u4* args,
+ JValue* pResult)
+{
+ // We ignore the this pointer in args[0].
+ Object* obj = (Object*) args[1];
+ s8 offset = GET_ARG_LONG(args, 2);
+ s8 expectedValue = GET_ARG_LONG(args, 4);
+ s8 newValue = GET_ARG_LONG(args, 6);
+ volatile int64_t* address = (volatile int64_t*) (((u1*) obj) + offset);
+
+ // Note: android_atomic_cmpxchg() returns 0 on success, not failure.
+ int result =
+ dvmQuasiAtomicCas64(expectedValue, newValue, address);
+
+ RETURN_BOOLEAN(result == 0);
+}
+
+/*
+ * public native boolean compareAndSwapObject(Object obj, long offset,
+ * Object expectedValue, Object newValue);
+ */
+static void Dalvik_sun_misc_Unsafe_compareAndSwapObject(const u4* args,
+ JValue* pResult)
+{
+ // We ignore the this pointer in args[0].
+ Object* obj = (Object*) args[1];
+ s8 offset = GET_ARG_LONG(args, 2);
+ Object* expectedValue = (Object*) args[4];
+ Object* newValue = (Object*) args[5];
+ int32_t* address = (int32_t*) (((u1*) obj) + offset);
+
+ // Note: android_atomic_cmpxchg() returns 0 on success, not failure.
+ int result = android_atomic_release_cas((int32_t) expectedValue,
+ (int32_t) newValue, address);
+ dvmWriteBarrierField(obj, address);
+ RETURN_BOOLEAN(result == 0);
+}
+
+/*
+ * public native int getIntVolatile(Object obj, long offset);
+ */
+static void Dalvik_sun_misc_Unsafe_getIntVolatile(const u4* args,
+ JValue* pResult)
+{
+ // We ignore the this pointer in args[0].
+ Object* obj = (Object*) args[1];
+ s8 offset = GET_ARG_LONG(args, 2);
+ volatile int32_t* address = (volatile int32_t*) (((u1*) obj) + offset);
+
+ int32_t value = android_atomic_acquire_load(address);
+ RETURN_INT(value);
+}
+
+/*
+ * public native void putIntVolatile(Object obj, long offset, int newValue);
+ */
+static void Dalvik_sun_misc_Unsafe_putIntVolatile(const u4* args,
+ JValue* pResult)
+{
+ // We ignore the this pointer in args[0].
+ Object* obj = (Object*) args[1];
+ s8 offset = GET_ARG_LONG(args, 2);
+ s4 value = (s4) args[4];
+ volatile int32_t* address = (volatile int32_t*) (((u1*) obj) + offset);
+
+ android_atomic_release_store(value, address);
+ RETURN_VOID();
+}
+
+/*
+ * public native long getLongVolatile(Object obj, long offset);
+ */
+static void Dalvik_sun_misc_Unsafe_getLongVolatile(const u4* args,
+ JValue* pResult)
+{
+ // We ignore the this pointer in args[0].
+ Object* obj = (Object*) args[1];
+ s8 offset = GET_ARG_LONG(args, 2);
+ volatile int64_t* address = (volatile int64_t*) (((u1*) obj) + offset);
+
+ assert((offset & 7) == 0);
+ RETURN_LONG(dvmQuasiAtomicRead64(address));
+}
+
+/*
+ * public native void putLongVolatile(Object obj, long offset, long newValue);
+ */
+static void Dalvik_sun_misc_Unsafe_putLongVolatile(const u4* args,
+ JValue* pResult)
+{
+ // We ignore the this pointer in args[0].
+ Object* obj = (Object*) args[1];
+ s8 offset = GET_ARG_LONG(args, 2);
+ s8 value = GET_ARG_LONG(args, 4);
+ volatile int64_t* address = (volatile int64_t*) (((u1*) obj) + offset);
+
+ assert((offset & 7) == 0);
+ dvmQuasiAtomicSwap64(value, address);
+ RETURN_VOID();
+}
+
+/*
+ * public native Object getObjectVolatile(Object obj, long offset);
+ */
+static void Dalvik_sun_misc_Unsafe_getObjectVolatile(const u4* args,
+ JValue* pResult)
+{
+ // We ignore the this pointer in args[0].
+ Object* obj = (Object*) args[1];
+ s8 offset = GET_ARG_LONG(args, 2);
+ volatile int32_t* address = (volatile int32_t*) (((u1*) obj) + offset);
+
+ RETURN_PTR((Object*) android_atomic_acquire_load(address));
+}
+
+/*
+ * public native void putObjectVolatile(Object obj, long offset,
+ * Object newValue);
+ */
+static void Dalvik_sun_misc_Unsafe_putObjectVolatile(const u4* args,
+ JValue* pResult)
+{
+ // We ignore the this pointer in args[0].
+ Object* obj = (Object*) args[1];
+ s8 offset = GET_ARG_LONG(args, 2);
+ Object* value = (Object*) args[4];
+ volatile int32_t* address = (volatile int32_t*) (((u1*) obj) + offset);
+
+ android_atomic_release_store((int32_t)value, address);
+ dvmWriteBarrierField(obj, (void *)address);
+ RETURN_VOID();
+}
+
+/*
+ * public native int getInt(Object obj, long offset);
+ */
+static void Dalvik_sun_misc_Unsafe_getInt(const u4* args, JValue* pResult)
+{
+ // We ignore the this pointer in args[0].
+ Object* obj = (Object*) args[1];
+ s8 offset = GET_ARG_LONG(args, 2);
+ s4* address = (s4*) (((u1*) obj) + offset);
+
+ RETURN_INT(*address);
+}
+
+/*
+ * public native void putInt(Object obj, long offset, int newValue);
+ */
+static void Dalvik_sun_misc_Unsafe_putInt(const u4* args, JValue* pResult)
+{
+ // We ignore the this pointer in args[0].
+ Object* obj = (Object*) args[1];
+ s8 offset = GET_ARG_LONG(args, 2);
+ s4 value = (s4) args[4];
+ s4* address = (s4*) (((u1*) obj) + offset);
+
+ *address = value;
+ RETURN_VOID();
+}
+
+/*
+ * public native long getLong(Object obj, long offset);
+ */
+static void Dalvik_sun_misc_Unsafe_getLong(const u4* args, JValue* pResult)
+{
+ // We ignore the this pointer in args[0].
+ Object* obj = (Object*) args[1];
+ s8 offset = GET_ARG_LONG(args, 2);
+ s8* address = (s8*) (((u1*) obj) + offset);
+
+ RETURN_LONG(*address);
+}
+
+/*
+ * public native void putLong(Object obj, long offset, long newValue);
+ */
+static void Dalvik_sun_misc_Unsafe_putLong(const u4* args, JValue* pResult)
+{
+ // We ignore the this pointer in args[0].
+ Object* obj = (Object*) args[1];
+ s8 offset = GET_ARG_LONG(args, 2);
+ s8 value = GET_ARG_LONG(args, 4);
+ s8* address = (s8*) (((u1*) obj) + offset);
+
+ *address = value;
+ RETURN_VOID();
+}
+
+/*
+ * public native Object getObject(Object obj, long offset);
+ */
+static void Dalvik_sun_misc_Unsafe_getObject(const u4* args, JValue* pResult)
+{
+ // We ignore the this pointer in args[0].
+ Object* obj = (Object*) args[1];
+ s8 offset = GET_ARG_LONG(args, 2);
+ Object** address = (Object**) (((u1*) obj) + offset);
+
+ RETURN_PTR(*address);
+}
+
+/*
+ * public native void putObject(Object obj, long offset, Object newValue);
+ */
+static void Dalvik_sun_misc_Unsafe_putObject(const u4* args, JValue* pResult)
+{
+ // We ignore the this pointer in args[0].
+ Object* obj = (Object*) args[1];
+ s8 offset = GET_ARG_LONG(args, 2);
+ Object* value = (Object*) args[4];
+ Object** address = (Object**) (((u1*) obj) + offset);
+
+ *address = value;
+ dvmWriteBarrierField(obj, address);
+ RETURN_VOID();
+}
+
+const DalvikNativeMethod dvm_sun_misc_Unsafe[] = {
+ { "objectFieldOffset0", "(Ljava/lang/reflect/Field;)J",
+ Dalvik_sun_misc_Unsafe_objectFieldOffset0 },
+ { "arrayBaseOffset0", "(Ljava/lang/Class;)I",
+ Dalvik_sun_misc_Unsafe_arrayBaseOffset0 },
+ { "arrayIndexScale0", "(Ljava/lang/Class;)I",
+ Dalvik_sun_misc_Unsafe_arrayIndexScale0 },
+ { "compareAndSwapInt", "(Ljava/lang/Object;JII)Z",
+ Dalvik_sun_misc_Unsafe_compareAndSwapInt },
+ { "compareAndSwapLong", "(Ljava/lang/Object;JJJ)Z",
+ Dalvik_sun_misc_Unsafe_compareAndSwapLong },
+ { "compareAndSwapObject",
+ "(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z",
+ Dalvik_sun_misc_Unsafe_compareAndSwapObject },
+ { "getIntVolatile", "(Ljava/lang/Object;J)I",
+ Dalvik_sun_misc_Unsafe_getIntVolatile },
+ { "putIntVolatile", "(Ljava/lang/Object;JI)V",
+ Dalvik_sun_misc_Unsafe_putIntVolatile },
+ { "getLongVolatile", "(Ljava/lang/Object;J)J",
+ Dalvik_sun_misc_Unsafe_getLongVolatile },
+ { "putLongVolatile", "(Ljava/lang/Object;JJ)V",
+ Dalvik_sun_misc_Unsafe_putLongVolatile },
+ { "getObjectVolatile", "(Ljava/lang/Object;J)Ljava/lang/Object;",
+ Dalvik_sun_misc_Unsafe_getObjectVolatile },
+ { "putObjectVolatile", "(Ljava/lang/Object;JLjava/lang/Object;)V",
+ Dalvik_sun_misc_Unsafe_putObjectVolatile },
+ { "getInt", "(Ljava/lang/Object;J)I",
+ Dalvik_sun_misc_Unsafe_getInt },
+ { "putInt", "(Ljava/lang/Object;JI)V",
+ Dalvik_sun_misc_Unsafe_putInt },
+ { "getLong", "(Ljava/lang/Object;J)J",
+ Dalvik_sun_misc_Unsafe_getLong },
+ { "putLong", "(Ljava/lang/Object;JJ)V",
+ Dalvik_sun_misc_Unsafe_putLong },
+ { "getObject", "(Ljava/lang/Object;J)Ljava/lang/Object;",
+ Dalvik_sun_misc_Unsafe_getObject },
+ { "putObject", "(Ljava/lang/Object;JLjava/lang/Object;)V",
+ Dalvik_sun_misc_Unsafe_putObject },
+ { NULL, NULL, NULL },
+};
diff --git a/vm/oo/AccessCheck.c b/vm/oo/AccessCheck.c
new file mode 100644
index 0000000..95b7f3e
--- /dev/null
+++ b/vm/oo/AccessCheck.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Check access to fields and methods.
+ */
+#include "Dalvik.h"
+
+/*
+ * Return the #of initial characters that match.
+ */
+static int strcmpCount(const char* str1, const char* str2)
+{
+ int count = 0;
+
+ while (true) {
+ char ch = str1[count];
+ if (ch == '\0' || ch != str2[count])
+ return count;
+ count++;
+ }
+}
+
+/*
+ * Returns "true" if the two classes are in the same runtime package.
+ */
+bool dvmInSamePackage(const ClassObject* class1, const ClassObject* class2)
+{
+ /* quick test for intra-class access */
+ if (class1 == class2)
+ return true;
+
+ /* class loaders must match */
+ if (class1->classLoader != class2->classLoader)
+ return false;
+
+ /*
+ * Switch array classes to their element types. Arrays receive the
+ * class loader of the underlying element type. The point of doing
+ * this is to get the un-decorated class name, without all the
+ * "[[L...;" stuff.
+ */
+ if (dvmIsArrayClass(class1))
+ class1 = class1->elementClass;
+ if (dvmIsArrayClass(class2))
+ class2 = class2->elementClass;
+
+ /* check again */
+ if (class1 == class2)
+ return true;
+
+ /*
+ * We have two classes with different names. Compare them and see
+ * if they match up through the final '/'.
+ *
+ * Ljava/lang/Object; + Ljava/lang/Class; --> true
+ * LFoo; + LBar; --> true
+ * Ljava/lang/Object; + Ljava/io/File; --> false
+ * Ljava/lang/Object; + Ljava/lang/reflect/Method; --> false
+ */
+ int commonLen;
+
+ commonLen = strcmpCount(class1->descriptor, class2->descriptor);
+ if (strchr(class1->descriptor + commonLen, '/') != NULL ||
+ strchr(class2->descriptor + commonLen, '/') != NULL)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Validate method/field access.
+ */
+static bool checkAccess(const ClassObject* accessFrom,
+ const ClassObject* accessTo, u4 accessFlags)
+{
+ /* quick accept for public access */
+ if (accessFlags & ACC_PUBLIC)
+ return true;
+
+ /* quick accept for access from same class */
+ if (accessFrom == accessTo)
+ return true;
+
+ /* quick reject for private access from another class */
+ if (accessFlags & ACC_PRIVATE)
+ return false;
+
+ /*
+ * Semi-quick test for protected access from a sub-class, which may or
+ * may not be in the same package.
+ */
+ if (accessFlags & ACC_PROTECTED)
+ if (dvmIsSubClass(accessFrom, accessTo))
+ return true;
+
+ /*
+ * Allow protected and private access from other classes in the same
+ * package.
+ */
+ return dvmInSamePackage(accessFrom, accessTo);
+}
+
+/*
+ * Determine whether the "accessFrom" class is allowed to get at "clazz".
+ *
+ * It's allowed if "clazz" is public or is in the same package. (Only
+ * inner classes can be marked "private" or "protected", so we don't need
+ * to check for it here.)
+ */
+bool dvmCheckClassAccess(const ClassObject* accessFrom,
+ const ClassObject* clazz)
+{
+ if (dvmIsPublicClass(clazz))
+ return true;
+ return dvmInSamePackage(accessFrom, clazz);
+}
+
+/*
+ * Determine whether the "accessFrom" class is allowed to get at "method".
+ */
+bool dvmCheckMethodAccess(const ClassObject* accessFrom, const Method* method)
+{
+ return checkAccess(accessFrom, method->clazz, method->accessFlags);
+}
+
+/*
+ * Determine whether the "accessFrom" class is allowed to get at "field".
+ */
+bool dvmCheckFieldAccess(const ClassObject* accessFrom, const Field* field)
+{
+ //LOGI("CHECK ACCESS from '%s' to field '%s' (in %s) flags=0x%x\n",
+ // accessFrom->descriptor, field->name,
+ // field->clazz->descriptor, field->accessFlags);
+ return checkAccess(accessFrom, field->clazz, field->accessFlags);
+}
diff --git a/vm/oo/AccessCheck.h b/vm/oo/AccessCheck.h
new file mode 100644
index 0000000..105c9e1
--- /dev/null
+++ b/vm/oo/AccessCheck.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Check access to fields and methods.
+ */
+#ifndef _DALVIK_OO_ACCESSCHECK
+#define _DALVIK_OO_ACCESSCHECK
+
+/*
+ * Determine whether the "accessFrom" class is allowed to get at "clazz".
+ */
+bool dvmCheckClassAccess(const ClassObject* accessFrom,
+ const ClassObject* clazz);
+
+/*
+ * Determine whether the "accessFrom" class is allowed to get at "method".
+ */
+bool dvmCheckMethodAccess(const ClassObject* accessFrom, const Method* method);
+
+/*
+ * Determine whether the "accessFrom" class is allowed to get at "field".
+ */
+bool dvmCheckFieldAccess(const ClassObject* accessFrom, const Field* field);
+
+/*
+ * Returns "true" if the two classes are in the same runtime package.
+ */
+bool dvmInSamePackage(const ClassObject* class1, const ClassObject* class2);
+
+#endif /*_DALVIK_OO_ACCESSCHECK*/
diff --git a/vm/oo/Array.c b/vm/oo/Array.c
new file mode 100644
index 0000000..4166a85
--- /dev/null
+++ b/vm/oo/Array.c
@@ -0,0 +1,814 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Array objects.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+#include <stddef.h>
+
+#if WITH_HPROF && WITH_HPROF_STACK
+#include "hprof/Hprof.h"
+#endif
+
+static ClassObject* createArrayClass(const char* descriptor, Object* loader);
+static ClassObject* createPrimitiveClass(int idx);
+
+static const char gPrimLetter[] = PRIM_TYPE_TO_LETTER;
+
+/*
+ * Allocate space for a new array object. This is the lowest-level array
+ * allocation function.
+ *
+ * Pass in the array class and the width of each element.
+ *
+ * On failure, returns NULL with an exception raised.
+ */
+ArrayObject* dvmAllocArray(ClassObject* arrayClass, size_t length,
+ size_t elemWidth, int allocFlags)
+{
+ ArrayObject* newArray;
+ size_t size;
+
+ assert(arrayClass->descriptor[0] == '[');
+
+ if (length > 0x0fffffff) {
+ /* too large and (length * elemWidth) will overflow 32 bits */
+ LOGE("Rejecting allocation of %u-element array\n", length);
+ dvmThrowBadAllocException("array size too large");
+ return NULL;
+ }
+
+ size = offsetof(ArrayObject, contents);
+ size += length * elemWidth;
+
+ /* Note that we assume that the Array class does not
+ * override finalize().
+ */
+ newArray = dvmMalloc(size, allocFlags);
+ if (newArray != NULL) {
+ DVM_OBJECT_INIT(&newArray->obj, arrayClass);
+ newArray->length = length;
+ LOGVV("AllocArray: %s [%d] (%d)\n",
+ arrayClass->descriptor, (int) length, (int) size);
+#if WITH_HPROF && WITH_HPROF_STACK
+ hprofFillInStackTrace(&newArray->obj);
+#endif
+ dvmTrackAllocation(arrayClass, size);
+ }
+ /* the caller must call dvmReleaseTrackedAlloc */
+ return newArray;
+}
+
+/*
+ * Create a new array, given an array class. The class may represent an
+ * array of references or primitives.
+ */
+ArrayObject* dvmAllocArrayByClass(ClassObject* arrayClass,
+ size_t length, int allocFlags)
+{
+ const char* descriptor = arrayClass->descriptor;
+
+ assert(descriptor[0] == '['); /* must be array class */
+ if (descriptor[1] != '[' && descriptor[1] != 'L') {
+ /* primitive array */
+ assert(descriptor[2] == '\0');
+ return dvmAllocPrimitiveArray(descriptor[1], length, allocFlags);
+ } else {
+ return dvmAllocArray(arrayClass, length, kObjectArrayRefWidth,
+ allocFlags);
+ }
+}
+
+/*
+ * Find the array class for "elemClassObj", which could itself be an
+ * array class.
+ */
+ClassObject* dvmFindArrayClassForElement(ClassObject* elemClassObj)
+{
+ ClassObject* arrayClass;
+
+ assert(elemClassObj != NULL);
+
+ /* Simply prepend "[" to the descriptor. */
+ int nameLen = strlen(elemClassObj->descriptor);
+ char className[nameLen + 2];
+
+ className[0] = '[';
+ memcpy(className+1, elemClassObj->descriptor, nameLen+1);
+ arrayClass = dvmFindArrayClass(className, elemClassObj->classLoader);
+
+ return arrayClass;
+}
+
+/*
+ * Create a new array that holds references to members of the specified class.
+ *
+ * "elemClassObj" is the element type, and may itself be an array class. It
+ * may not be a primitive class.
+ *
+ * "allocFlags" determines whether the new object will be added to the
+ * "tracked alloc" table.
+ *
+ * This is less efficient than dvmAllocArray(), but occasionally convenient.
+ */
+ArrayObject* dvmAllocObjectArray(ClassObject* elemClassObj, size_t length,
+ int allocFlags)
+{
+ ClassObject* arrayClass;
+ ArrayObject* newArray = NULL;
+
+ LOGVV("dvmAllocObjectArray: '%s' len=%d\n",
+ elemClassObj->descriptor, (int)length);
+
+ arrayClass = dvmFindArrayClassForElement(elemClassObj);
+ if (arrayClass != NULL) {
+ newArray = dvmAllocArray(arrayClass, length, kObjectArrayRefWidth,
+ allocFlags);
+ }
+
+ /* the caller must call dvmReleaseTrackedAlloc */
+ return newArray;
+}
+
+/*
+ * Create a new array that holds primitive types.
+ *
+ * "type" is the primitive type letter, e.g. 'I' for int or 'J' for long.
+ * If the array class doesn't exist, it will be created.
+ */
+ArrayObject* dvmAllocPrimitiveArray(char type, size_t length, int allocFlags)
+{
+ ArrayObject* newArray;
+ ClassObject** pTypeClass;
+ int width;
+
+ switch (type) {
+ case 'I':
+ pTypeClass = &gDvm.classArrayInt;
+ width = 4;
+ break;
+ case 'C':
+ pTypeClass = &gDvm.classArrayChar;
+ width = 2;
+ break;
+ case 'B':
+ pTypeClass = &gDvm.classArrayByte;
+ width = 1;
+ break;
+ case 'Z':
+ pTypeClass = &gDvm.classArrayBoolean;
+ width = 1; /* special-case this? */
+ break;
+ case 'F':
+ pTypeClass = &gDvm.classArrayFloat;
+ width = 4;
+ break;
+ case 'D':
+ pTypeClass = &gDvm.classArrayDouble;
+ width = 8;
+ break;
+ case 'S':
+ pTypeClass = &gDvm.classArrayShort;
+ width = 2;
+ break;
+ case 'J':
+ pTypeClass = &gDvm.classArrayLong;
+ width = 8;
+ break;
+ default:
+ LOGE("Unknown type '%c'\n", type);
+ assert(false);
+ return NULL;
+ }
+
+ if (*pTypeClass == NULL) {
+ char typeClassName[3] = "[x";
+
+ typeClassName[1] = type;
+
+ *pTypeClass = dvmFindArrayClass(typeClassName, NULL);
+ if (*pTypeClass == NULL) {
+ LOGE("ERROR: failed to generate array class for '%s'\n",
+ typeClassName);
+ return NULL;
+ }
+ }
+
+ newArray = dvmAllocArray(*pTypeClass, length, width, allocFlags);
+
+ /* the caller must dvmReleaseTrackedAlloc if allocFlags==ALLOC_DEFAULT */
+ return newArray;
+}
+
+/*
+ * Recursively create an array with multiple dimensions. Elements may be
+ * Objects or primitive types.
+ *
+ * The dimension we're creating is in dimensions[0], so when we recurse
+ * we advance the pointer.
+ */
+ArrayObject* dvmAllocMultiArray(ClassObject* arrayClass, int curDim,
+ const int* dimensions)
+{
+ ArrayObject* newArray;
+ const char* elemName = arrayClass->descriptor + 1; // Advance past one '['.
+
+ LOGVV("dvmAllocMultiArray: class='%s' curDim=%d *dimensions=%d\n",
+ arrayClass->descriptor, curDim, *dimensions);
+
+ if (curDim == 0) {
+ if (*elemName == 'L' || *elemName == '[') {
+ LOGVV(" end: array class (obj) is '%s'\n",
+ arrayClass->descriptor);
+ newArray = dvmAllocArray(arrayClass, *dimensions,
+ kObjectArrayRefWidth, ALLOC_DEFAULT);
+ } else {
+ LOGVV(" end: array class (prim) is '%s'\n",
+ arrayClass->descriptor);
+ newArray = dvmAllocPrimitiveArray(
+ gPrimLetter[arrayClass->elementClass->primitiveType],
+ *dimensions, ALLOC_DEFAULT);
+ }
+ } else {
+ ClassObject* subArrayClass;
+ int i;
+
+ /* if we have X[][], find X[] */
+ subArrayClass = dvmFindArrayClass(elemName, arrayClass->classLoader);
+ if (subArrayClass == NULL) {
+ /* not enough '['s on the initial class? */
+ assert(dvmCheckException(dvmThreadSelf()));
+ return NULL;
+ }
+ assert(dvmIsArrayClass(subArrayClass));
+
+ /* allocate the array that holds the sub-arrays */
+ newArray = dvmAllocArray(arrayClass, *dimensions, kObjectArrayRefWidth,
+ ALLOC_DEFAULT);
+ if (newArray == NULL) {
+ assert(dvmCheckException(dvmThreadSelf()));
+ return NULL;
+ }
+
+ /*
+ * Create a new sub-array in every element of the array.
+ */
+ for (i = 0; i < *dimensions; i++) {
+ ArrayObject* newSubArray;
+ newSubArray = dvmAllocMultiArray(subArrayClass, curDim-1,
+ dimensions+1);
+ if (newSubArray == NULL) {
+ dvmReleaseTrackedAlloc((Object*) newArray, NULL);
+ assert(dvmCheckException(dvmThreadSelf()));
+ return NULL;
+ }
+ dvmSetObjectArrayElement(newArray, i, (Object *)newSubArray);
+ dvmReleaseTrackedAlloc((Object*) newSubArray, NULL);
+ }
+ }
+
+ /* caller must call dvmReleaseTrackedAlloc */
+ return newArray;
+}
+
+
+/*
+ * Find an array class, by name (e.g. "[I").
+ *
+ * If the array class doesn't exist, we generate it.
+ *
+ * If the element class doesn't exist, we return NULL (no exception raised).
+ */
+ClassObject* dvmFindArrayClass(const char* descriptor, Object* loader)
+{
+ ClassObject* clazz;
+
+ assert(descriptor[0] == '[');
+ //LOGV("dvmFindArrayClass: '%s' %p\n", descriptor, loader);
+
+ clazz = dvmLookupClass(descriptor, loader, false);
+ if (clazz == NULL) {
+ LOGV("Array class '%s' %p not found; creating\n", descriptor, loader);
+ clazz = createArrayClass(descriptor, loader);
+ if (clazz != NULL)
+ dvmAddInitiatingLoader(clazz, loader);
+ }
+
+ return clazz;
+}
+
+/*
+ * Create an array class (i.e. the class object for the array, not the
+ * array itself). "descriptor" looks like "[C" or "[Ljava/lang/String;".
+ *
+ * If "descriptor" refers to an array of primitives, look up the
+ * primitive type's internally-generated class object.
+ *
+ * "loader" is the class loader of the class that's referring to us. It's
+ * used to ensure that we're looking for the element type in the right
+ * context. It does NOT become the class loader for the array class; that
+ * always comes from the base element class.
+ *
+ * Returns NULL with an exception raised on failure.
+ */
+static ClassObject* createArrayClass(const char* descriptor, Object* loader)
+{
+ ClassObject* newClass = NULL;
+ ClassObject* elementClass = NULL;
+ int arrayDim;
+ u4 extraFlags;
+
+ assert(descriptor[0] == '[');
+ assert(gDvm.classJavaLangClass != NULL);
+ assert(gDvm.classJavaLangObject != NULL);
+
+ /*
+ * Identify the underlying element class and the array dimension depth.
+ */
+ extraFlags = CLASS_ISARRAY;
+ if (descriptor[1] == '[') {
+ /* array of arrays; keep descriptor and grab stuff from parent */
+ ClassObject* outer;
+
+ outer = dvmFindClassNoInit(&descriptor[1], loader);
+ if (outer != NULL) {
+ /* want the base class, not "outer", in our elementClass */
+ elementClass = outer->elementClass;
+ arrayDim = outer->arrayDim + 1;
+ extraFlags |= CLASS_ISOBJECTARRAY;
+ } else {
+ assert(elementClass == NULL); /* make sure we fail */
+ }
+ } else {
+ arrayDim = 1;
+ if (descriptor[1] == 'L') {
+ /* array of objects; strip off "[" and look up descriptor. */
+ const char* subDescriptor = &descriptor[1];
+ LOGVV("searching for element class '%s'\n", subDescriptor);
+ elementClass = dvmFindClassNoInit(subDescriptor, loader);
+ extraFlags |= CLASS_ISOBJECTARRAY;
+ } else {
+ /* array of a primitive type */
+ elementClass = dvmFindPrimitiveClass(descriptor[1]);
+ }
+ }
+
+ if (elementClass == NULL) {
+ /* failed */
+ assert(dvmCheckException(dvmThreadSelf()));
+ dvmFreeClassInnards(newClass);
+ dvmReleaseTrackedAlloc((Object*) newClass, NULL);
+ return NULL;
+ }
+
+ /*
+ * See if it's already loaded. Array classes are always associated
+ * with the class loader of their underlying element type -- an array
+ * of Strings goes with the loader for java/lang/String -- so we need
+ * to look for it there. (The caller should have checked for the
+ * existence of the class before calling here, but they did so with
+ * *their* class loader, not the element class' loader.)
+ *
+ * If we find it, the caller adds "loader" to the class' initiating
+ * loader list, which should prevent us from going through this again.
+ *
+ * This call is unnecessary if "loader" and "elementClass->classLoader"
+ * are the same, because our caller (dvmFindArrayClass) just did the
+ * lookup. (Even if we get this wrong we still have correct behavior,
+ * because we effectively do this lookup again when we add the new
+ * class to the hash table -- necessary because of possible races with
+ * other threads.)
+ */
+ if (loader != elementClass->classLoader) {
+ LOGVV("--- checking for '%s' in %p vs. elem %p\n",
+ descriptor, loader, elementClass->classLoader);
+ newClass = dvmLookupClass(descriptor, elementClass->classLoader, false);
+ if (newClass != NULL) {
+ LOGV("--- we already have %s in %p, don't need in %p\n",
+ descriptor, elementClass->classLoader, loader);
+ return newClass;
+ }
+ }
+
+
+ /*
+ * Fill out the fields in the ClassObject.
+ *
+ * It is possible to execute some methods against arrays, because all
+ * arrays are instances of Object, so we need to set up a vtable. We
+ * can just point at the one in Object.
+ *
+ * Array classes are simple enough that we don't need to do a full
+ * link step.
+ */
+ newClass = (ClassObject*) dvmMalloc(sizeof(*newClass), ALLOC_DEFAULT);
+ if (newClass == NULL)
+ return NULL;
+ DVM_OBJECT_INIT(&newClass->obj, gDvm.classJavaLangClass);
+ dvmSetClassSerialNumber(newClass);
+ newClass->descriptorAlloc = strdup(descriptor);
+ newClass->descriptor = newClass->descriptorAlloc;
+ dvmSetFieldObject((Object *)newClass,
+ offsetof(ClassObject, super),
+ (Object *)gDvm.classJavaLangObject);
+ newClass->vtableCount = gDvm.classJavaLangObject->vtableCount;
+ newClass->vtable = gDvm.classJavaLangObject->vtable;
+ newClass->primitiveType = PRIM_NOT;
+ dvmSetFieldObject((Object *)newClass,
+ offsetof(ClassObject, elementClass),
+ (Object *)elementClass);
+ dvmSetFieldObject((Object *)newClass,
+ offsetof(ClassObject, classLoader),
+ (Object *)elementClass->classLoader);
+ newClass->arrayDim = arrayDim;
+ newClass->status = CLASS_INITIALIZED;
+#if WITH_HPROF && WITH_HPROF_STACK
+ hprofFillInStackTrace(newClass);
+#endif
+
+ /* don't need to set newClass->objectSize */
+
+ /*
+ * All arrays have java/lang/Cloneable and java/io/Serializable as
+ * interfaces. We need to set that up here, so that stuff like
+ * "instanceof" works right.
+ *
+ * Note: The GC could run during the call to dvmFindSystemClassNoInit(),
+ * so we need to make sure the class object is GC-valid while we're in
+ * there. Do this by clearing the interface list so the GC will just
+ * think that the entries are null.
+ *
+ * TODO?
+ * We may want to cache these two classes to avoid the lookup, though
+ * it's not vital -- we only do it when creating an array class, not
+ * every time we create an array. Better yet, create a single, global
+ * copy of "interfaces" and "iftable" somewhere near the start and
+ * just point to those (and remember not to free them for arrays).
+ */
+ newClass->interfaceCount = 2;
+ newClass->interfaces = (ClassObject**)dvmLinearAlloc(newClass->classLoader,
+ sizeof(ClassObject*) * 2);
+ memset(newClass->interfaces, 0, sizeof(ClassObject*) * 2);
+ newClass->interfaces[0] =
+ dvmFindSystemClassNoInit("Ljava/lang/Cloneable;");
+ newClass->interfaces[1] =
+ dvmFindSystemClassNoInit("Ljava/io/Serializable;");
+ dvmLinearReadOnly(newClass->classLoader, newClass->interfaces);
+ if (newClass->interfaces[0] == NULL || newClass->interfaces[1] == NULL) {
+ LOGE("Unable to create array class '%s': missing interfaces\n",
+ descriptor);
+ dvmFreeClassInnards(newClass);
+ dvmThrowException("Ljava/lang/InternalError;", "missing array ifaces");
+ dvmReleaseTrackedAlloc((Object*) newClass, NULL);
+ return NULL;
+ }
+ /*
+ * We assume that Cloneable/Serializable don't have superinterfaces --
+ * normally we'd have to crawl up and explicitly list all of the
+ * supers as well. These interfaces don't have any methods, so we
+ * don't have to worry about the ifviPool either.
+ */
+ newClass->iftableCount = 2;
+ newClass->iftable = (InterfaceEntry*) dvmLinearAlloc(newClass->classLoader,
+ sizeof(InterfaceEntry) * 2);
+ memset(newClass->iftable, 0, sizeof(InterfaceEntry) * 2);
+ newClass->iftable[0].clazz = newClass->interfaces[0];
+ newClass->iftable[1].clazz = newClass->interfaces[1];
+ dvmLinearReadOnly(newClass->classLoader, newClass->iftable);
+
+ /*
+ * Inherit access flags from the element. Arrays can't be used as a
+ * superclass or interface, so we want to add "final" and remove
+ * "interface".
+ *
+ * Don't inherit any non-standard flags (e.g., CLASS_FINALIZABLE)
+ * from elementClass. We assume that the array class does not
+ * override finalize().
+ */
+ newClass->accessFlags = ((newClass->elementClass->accessFlags &
+ ~ACC_INTERFACE) | ACC_FINAL) & JAVA_FLAGS_MASK;
+
+ /* Set the flags we determined above.
+ * This must happen after accessFlags is set.
+ */
+ SET_CLASS_FLAG(newClass, extraFlags);
+
+ if (!dvmAddClassToHash(newClass)) {
+ /*
+ * Another thread must have loaded the class after we
+ * started but before we finished. Discard what we've
+ * done and leave some hints for the GC.
+ */
+ LOGI("WOW: somebody generated %s simultaneously\n",
+ newClass->descriptor);
+
+ /* Clean up the class before letting the
+ * GC get its hands on it.
+ */
+ dvmFreeClassInnards(newClass);
+
+ /* Let the GC free the class.
+ */
+ dvmReleaseTrackedAlloc((Object*) newClass, NULL);
+
+ /* Grab the winning class.
+ */
+ newClass = dvmLookupClass(descriptor, elementClass->classLoader, false);
+ assert(newClass != NULL);
+ return newClass;
+ }
+ dvmReleaseTrackedAlloc((Object*) newClass, NULL);
+
+ LOGV("Created array class '%s' %p (access=0x%04x.%04x)\n",
+ descriptor, newClass->classLoader,
+ newClass->accessFlags >> 16,
+ newClass->accessFlags & JAVA_FLAGS_MASK);
+
+ return newClass;
+}
+
+/*
+ * Get a class we generated for the primitive types.
+ *
+ * These correspond to e.g. Integer.TYPE, and are used as the element
+ * class in arrays of primitives.
+ *
+ * "type" should be 'I', 'J', 'Z', etc.
+ *
+ * Returns NULL if the type doesn't correspond to a known primitive type.
+ */
+ClassObject* dvmFindPrimitiveClass(char type)
+{
+ int idx;
+
+ switch (type) {
+ case 'Z':
+ idx = PRIM_BOOLEAN;
+ break;
+ case 'C':
+ idx = PRIM_CHAR;
+ break;
+ case 'F':
+ idx = PRIM_FLOAT;
+ break;
+ case 'D':
+ idx = PRIM_DOUBLE;
+ break;
+ case 'B':
+ idx = PRIM_BYTE;
+ break;
+ case 'S':
+ idx = PRIM_SHORT;
+ break;
+ case 'I':
+ idx = PRIM_INT;
+ break;
+ case 'J':
+ idx = PRIM_LONG;
+ break;
+ case 'V':
+ idx = PRIM_VOID;
+ break;
+ default:
+ LOGW("Unknown primitive type '%c'\n", type);
+ return NULL;
+ }
+
+ /*
+ * Create the primitive class if it hasn't already been, and add it
+ * to the table.
+ */
+ if (gDvm.primitiveClass[idx] == NULL) {
+ ClassObject* primClass = createPrimitiveClass(idx);
+ dvmReleaseTrackedAlloc((Object*) primClass, NULL);
+
+ if (android_atomic_release_cas(0, (int) primClass,
+ (int*) &gDvm.primitiveClass[idx]) != 0)
+ {
+ /*
+ * Looks like somebody beat us to it. Free up the one we
+ * just created and use the other one.
+ */
+ dvmFreeClassInnards(primClass);
+ }
+ }
+
+ return gDvm.primitiveClass[idx];
+}
+
+/*
+ * Synthesize a primitive class.
+ *
+ * Just creates the class and returns it (does not add it to the class list).
+ */
+static ClassObject* createPrimitiveClass(int idx)
+{
+ ClassObject* newClass;
+ static const char* kClassDescriptors[PRIM_MAX] = {
+ "Z", "C", "F", "D", "B", "S", "I", "J", "V"
+ };
+
+ assert(gDvm.classJavaLangClass != NULL);
+ assert(idx >= 0 && idx < PRIM_MAX);
+
+ /*
+ * Fill out a few fields in the ClassObject.
+ *
+ * Note that primitive classes do not sub-class java/lang/Object. This
+ * matters for "instanceof" checks. Also, we assume that the primitive
+ * class does not override finalize().
+ */
+ newClass = (ClassObject*) dvmMalloc(sizeof(*newClass), ALLOC_DEFAULT);
+ if (newClass == NULL)
+ return NULL;
+ DVM_OBJECT_INIT(&newClass->obj, gDvm.classJavaLangClass);
+ dvmSetClassSerialNumber(newClass);
+ newClass->accessFlags = ACC_PUBLIC | ACC_FINAL | ACC_ABSTRACT;
+ newClass->primitiveType = idx;
+ newClass->descriptorAlloc = NULL;
+ newClass->descriptor = kClassDescriptors[idx];
+ //newClass->super = gDvm.classJavaLangObject;
+ newClass->status = CLASS_INITIALIZED;
+#if WITH_HPROF && WITH_HPROF_STACK
+ hprofFillInStackTrace(newClass);
+#endif
+
+ /* don't need to set newClass->objectSize */
+
+ LOGVV("Created primitive class '%s'\n", kClassDescriptors[idx]);
+
+ return newClass;
+}
+
+/*
+ * Copy the entire contents of one array of objects to another. If the copy
+ * is impossible because of a type clash, we fail and return "false".
+ */
+bool dvmCopyObjectArray(ArrayObject* dstArray, const ArrayObject* srcArray,
+ ClassObject* dstElemClass)
+{
+ Object** src = (Object**)srcArray->contents;
+ u4 length, count;
+
+ assert(srcArray->length == dstArray->length);
+ assert(dstArray->obj.clazz->elementClass == dstElemClass ||
+ (dstArray->obj.clazz->elementClass == dstElemClass->elementClass &&
+ dstArray->obj.clazz->arrayDim == dstElemClass->arrayDim+1));
+
+ length = dstArray->length;
+ for (count = 0; count < length; count++) {
+ if (!dvmInstanceof(src[count]->clazz, dstElemClass)) {
+ LOGW("dvmCopyObjectArray: can't store %s in %s\n",
+ src[count]->clazz->descriptor, dstElemClass->descriptor);
+ return false;
+ }
+ dvmSetObjectArrayElement(dstArray, count, src[count]);
+ }
+
+ return true;
+}
+
+/*
+ * Copy the entire contents of an array of boxed primitives into an
+ * array of primitives. The boxed value must fit in the primitive (i.e.
+ * narrowing conversions are not allowed).
+ */
+bool dvmUnboxObjectArray(ArrayObject* dstArray, const ArrayObject* srcArray,
+ ClassObject* dstElemClass)
+{
+ Object** src = (Object**)srcArray->contents;
+ void* dst = (void*)dstArray->contents;
+ u4 count = dstArray->length;
+ PrimitiveType typeIndex = dstElemClass->primitiveType;
+
+ assert(typeIndex != PRIM_NOT);
+ assert(srcArray->length == dstArray->length);
+
+ while (count--) {
+ JValue result;
+
+ /*
+ * This will perform widening conversions as appropriate. It
+ * might make sense to be more restrictive and require that the
+ * primitive type exactly matches the box class, but it's not
+ * necessary for correctness.
+ */
+ if (!dvmUnwrapPrimitive(*src, dstElemClass, &result)) {
+ LOGW("dvmCopyObjectArray: can't store %s in %s\n",
+ (*src)->clazz->descriptor, dstElemClass->descriptor);
+ return false;
+ }
+
+ /* would be faster with 4 loops, but speed not crucial here */
+ switch (typeIndex) {
+ case PRIM_BOOLEAN:
+ case PRIM_BYTE:
+ {
+ u1* tmp = dst;
+ *tmp++ = result.b;
+ dst = tmp;
+ }
+ break;
+ case PRIM_CHAR:
+ case PRIM_SHORT:
+ {
+ u2* tmp = dst;
+ *tmp++ = result.s;
+ dst = tmp;
+ }
+ break;
+ case PRIM_FLOAT:
+ case PRIM_INT:
+ {
+ u4* tmp = dst;
+ *tmp++ = result.i;
+ dst = tmp;
+ }
+ break;
+ case PRIM_DOUBLE:
+ case PRIM_LONG:
+ {
+ u8* tmp = dst;
+ *tmp++ = result.j;
+ dst = tmp;
+ }
+ break;
+ default:
+ /* should not be possible to get here */
+ dvmAbort();
+ }
+
+ src++;
+ }
+
+ return true;
+}
+
+/*
+ * Returns the width, in bytes, required by elements in instances of
+ * the array class.
+ */
+size_t dvmArrayClassElementWidth(const ClassObject* arrayClass)
+{
+ const char *descriptor;
+
+ assert(dvmIsArrayClass(arrayClass));
+
+ if (dvmIsObjectArrayClass(arrayClass)) {
+ return sizeof(Object *);
+ } else {
+ descriptor = arrayClass->descriptor;
+ switch (descriptor[1]) {
+ case 'B': return 1; /* byte */
+ case 'C': return 2; /* char */
+ case 'D': return 8; /* double */
+ case 'F': return 4; /* float */
+ case 'I': return 4; /* int */
+ case 'J': return 8; /* long */
+ case 'S': return 2; /* short */
+ case 'Z': return 1; /* boolean */
+ }
+ }
+ LOGE("class %p has an unhandled descriptor '%s'", arrayClass, descriptor);
+ dvmDumpThread(dvmThreadSelf(), false);
+ dvmAbort();
+ return 0; /* Quiet the compiler. */
+}
+
+size_t dvmArrayObjectSize(const ArrayObject *array)
+{
+ size_t size;
+
+ assert(array != NULL);
+ size = offsetof(ArrayObject, contents);
+ size += array->length * dvmArrayClassElementWidth(array->obj.clazz);
+ return size;
+}
+
+/*
+ * Add all primitive classes to the root set of objects.
+TODO: do these belong to the root class loader?
+ */
+void dvmGcScanPrimitiveClasses()
+{
+ int i;
+
+ for (i = 0; i < PRIM_MAX; i++) {
+ dvmMarkObject((Object *)gDvm.primitiveClass[i]); // may be NULL
+ }
+}
diff --git a/vm/oo/Array.h b/vm/oo/Array.h
new file mode 100644
index 0000000..9cd7996
--- /dev/null
+++ b/vm/oo/Array.h
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Array handling.
+ */
+#ifndef _DALVIK_OO_ARRAY
+#define _DALVIK_OO_ARRAY
+
+/* width of an object reference, for arrays of objects */
+#define kObjectArrayRefWidth sizeof(Object*)
+
+/*
+ * Find a matching array class. If it doesn't exist, create it.
+ *
+ * "descriptor" looks like "[I".
+ *
+ * "loader" should be the defining class loader for the elements held
+ * in the array.
+ */
+ClassObject* dvmFindArrayClass(const char* descriptor, Object* loader);
+
+/*
+ * Find the array class for the specified class. If "elemClassObj" is the
+ * class "Foo", this returns the class object for "[Foo".
+ */
+ClassObject* dvmFindArrayClassForElement(ClassObject* elemClassObj);
+
+/*
+ * Allocate space for a new array object.
+ *
+ * "allocFlags" determines whether the new object will be added to the
+ * "tracked alloc" table.
+ *
+ * Returns NULL with an exception raised if allocation fails.
+ */
+ArrayObject* dvmAllocArray(ClassObject* arrayClass, size_t length,
+ size_t elemWidth, int allocFlags);
+
+/*
+ * Create a new array, given an array class. The class may represent an
+ * array of references or primitives.
+ *
+ * Returns NULL with an exception raised if allocation fails.
+ */
+ArrayObject* dvmAllocArrayByClass(ClassObject* arrayClass,
+ size_t length, int allocFlags);
+
+/*
+ * Create a new array that holds references to members of the specified class.
+ *
+ * "elemClassObj" is the element type, and may itself be an array class. It
+ * may not be a primitive class.
+ *
+ * "allocFlags" determines whether the new object will be added to the
+ * "tracked alloc" table.
+ *
+ * This is less efficient than dvmAllocArray(), but occasionally convenient.
+ *
+ * Returns NULL with an exception raised if allocation fails.
+ */
+ArrayObject* dvmAllocObjectArray(ClassObject* elemClassObj, size_t length,
+ int allocFlags);
+
+/*
+ * Allocate an array whose members are primitives (bools, ints, etc.).
+ *
+ * "type" should be 'I', 'J', 'Z', etc.
+ *
+ * The new object will be added to the "tracked alloc" table.
+ *
+ * Returns NULL with an exception raised if allocation fails.
+ */
+ArrayObject* dvmAllocPrimitiveArray(char type, size_t length, int allocFlags);
+
+/*
+ * Allocate an array with multiple dimensions. Elements may be Objects or
+ * primitive types.
+ *
+ * The base object will be added to the "tracked alloc" table.
+ *
+ * Returns NULL with an exception raised if allocation fails.
+ */
+ArrayObject* dvmAllocMultiArray(ClassObject* arrayClass, int curDim,
+ const int* dimensions);
+
+/*
+ * Find the synthesized object for the primitive class, generating it
+ * if this is the first reference.
+ */
+ClassObject* dvmFindPrimitiveClass(char type);
+
+/*
+ * Verify that the object is actually an array.
+ *
+ * Does not verify that the object is actually a non-NULL object.
+ */
+INLINE bool dvmIsArray(const ArrayObject* arrayObj)
+{
+ return ( ((Object*)arrayObj)->clazz->descriptor[0] == '[' );
+}
+
+/*
+ * Verify that the array is an object array and not a primitive array.
+ *
+ * Does not verify that the object is actually a non-NULL object.
+ */
+INLINE bool dvmIsObjectArrayClass(const ClassObject* clazz)
+{
+ const char* descriptor = clazz->descriptor;
+ return descriptor[0] == '[' && (descriptor[1] == 'L' ||
+ descriptor[1] == '[');
+}
+
+/*
+ * Verify that the array is an object array and not a primitive array.
+ *
+ * Does not verify that the object is actually a non-NULL object.
+ */
+INLINE bool dvmIsObjectArray(const ArrayObject* arrayObj)
+{
+ return dvmIsObjectArrayClass(arrayObj->obj.clazz);
+}
+
+/*
+ * Verify that the class is an array class.
+ *
+ * TODO: there may be some performance advantage to setting a flag in
+ * the accessFlags field instead of chasing into the name string.
+ */
+INLINE bool dvmIsArrayClass(const ClassObject* clazz)
+{
+ return (clazz->descriptor[0] == '[');
+}
+
+/*
+ * Copy the entire contents of one array of objects to another. If the copy
+ * is impossible because of a type clash, we fail and return "false".
+ *
+ * "dstElemClass" is the type of element that "dstArray" holds.
+ */
+bool dvmCopyObjectArray(ArrayObject* dstArray, const ArrayObject* srcArray,
+ ClassObject* dstElemClass);
+
+/*
+ * Copy the entire contents of an array of boxed primitives into an
+ * array of primitives. The boxed value must fit in the primitive (i.e.
+ * narrowing conversions are not allowed).
+ */
+bool dvmUnboxObjectArray(ArrayObject* dstArray, const ArrayObject* srcArray,
+ ClassObject* dstElemClass);
+
+/*
+ * Returns the size of the given array object in bytes.
+ */
+size_t dvmArrayObjectSize(const ArrayObject *array);
+
+/*
+ * Returns the width, in bytes, required by elements in instances of
+ * the array class.
+ */
+size_t dvmArrayClassElementWidth(const ClassObject* clazz);
+
+#endif /*_DALVIK_OO_ARRAY*/
diff --git a/vm/oo/Class.c b/vm/oo/Class.c
new file mode 100644
index 0000000..8f55815
--- /dev/null
+++ b/vm/oo/Class.c
@@ -0,0 +1,4967 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Class loading, including bootstrap class loader, linking, and
+ * initialization.
+ */
+
+#define LOG_CLASS_LOADING 0
+
+#include "Dalvik.h"
+#include "libdex/DexClass.h"
+#include "analysis/Optimize.h"
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <sys/stat.h>
+
+#if LOG_CLASS_LOADING
+#include <unistd.h>
+#include <pthread.h>
+#include <cutils/process_name.h>
+#include <sys/types.h>
+#endif
+
+/*
+Notes on Linking and Verification
+
+The basic way to retrieve a class is to load it, make sure its superclass
+and interfaces are available, prepare its fields, and return it. This gets
+a little more complicated when multiple threads can be trying to retrieve
+the class simultaneously, requiring that we use the class object's monitor
+to keep things orderly.
+
+The linking (preparing, resolving) of a class can cause us to recursively
+load superclasses and interfaces. Barring circular references (e.g. two
+classes that are superclasses of each other), this will complete without
+the loader attempting to access the partially-linked class.
+
+With verification, the situation is different. If we try to verify
+every class as we load it, we quickly run into trouble. Even the lowly
+java.lang.Object requires CloneNotSupportedException; follow the list
+of referenced classes and you can head down quite a trail. The trail
+eventually leads back to Object, which is officially not fully-formed yet.
+
+The VM spec (specifically, v2 5.4.1) notes that classes pulled in during
+verification do not need to be prepared or verified. This means that we
+are allowed to have loaded but unverified classes. It further notes that
+the class must be verified before it is initialized, which allows us to
+defer verification for all classes until class init. You can't execute
+code or access fields in an uninitialized class, so this is safe.
+
+It also allows a more peaceful coexistence between verified and
+unverifiable code. If class A refers to B, and B has a method that
+refers to a bogus class C, should we allow class A to be verified?
+If A only exercises parts of B that don't use class C, then there is
+nothing wrong with running code in A. We can fully verify both A and B,
+and allow execution to continue until B causes initialization of C. The
+VerifyError is thrown close to the point of use.
+
+This gets a little weird with java.lang.Class, which is the only class
+that can be instantiated before it is initialized. We have to force
+initialization right after the class is created, because by definition we
+have instances of it on the heap, and somebody might get a class object and
+start making virtual calls on it. We can end up going recursive during
+verification of java.lang.Class, but we avoid that by checking to see if
+verification is already in progress before we try to initialize it.
+*/
+
+/*
+Notes on class loaders and interaction with optimization / verification
+
+In what follows, "pre-verification" and "optimization" are the steps
+performed by the dexopt command, which attempts to verify and optimize
+classes as part of unpacking jar files and storing the DEX data in the
+dalvik-cache directory. These steps are performed by loading the DEX
+files directly, without any assistance from ClassLoader instances.
+
+When we pre-verify and optimize a class in a DEX file, we make some
+assumptions about where the class loader will go to look for classes.
+If we can't guarantee those assumptions, e.g. because a class ("AppClass")
+references something not defined in the bootstrap jars or the AppClass jar,
+we can't pre-verify or optimize the class.
+
+The VM doesn't define the behavior of user-defined class loaders.
+For example, suppose application class AppClass, loaded by UserLoader,
+has a method that creates a java.lang.String. The first time
+AppClass.stringyMethod tries to do something with java.lang.String, it
+asks UserLoader to find it. UserLoader is expected to defer to its parent
+loader, but isn't required to. UserLoader might provide a replacement
+for String.
+
+We can run into trouble if we pre-verify AppClass with the assumption that
+java.lang.String will come from core.jar, and don't verify this assumption
+at runtime. There are two places that an alternate implementation of
+java.lang.String can come from: the AppClass jar, or from some other jar
+that UserLoader knows about. (Someday UserLoader will be able to generate
+some bytecode and call DefineClass, but not yet.)
+
+To handle the first situation, the pre-verifier will explicitly check for
+conflicts between the class being optimized/verified and the bootstrap
+classes. If an app jar contains a class that has the same package and
+class name as a class in a bootstrap jar, the verification resolver refuses
+to find either, which will block pre-verification and optimization on
+classes that reference ambiguity. The VM will postpone verification of
+the app class until first load.
+
+For the second situation, we need to ensure that all references from a
+pre-verified class are satisified by the class' jar or earlier bootstrap
+jars. In concrete terms: when resolving a reference to NewClass,
+which was caused by a reference in class AppClass, we check to see if
+AppClass was pre-verified. If so, we require that NewClass comes out
+of either the AppClass jar or one of the jars in the bootstrap path.
+(We may not control the class loaders, but we do manage the DEX files.
+We can verify that it's either (loader==null && dexFile==a_boot_dex)
+or (loader==UserLoader && dexFile==AppClass.dexFile). Classes from
+DefineClass can't be pre-verified, so this doesn't apply.)
+
+This should ensure that you can't "fake out" the pre-verifier by creating
+a user-defined class loader that replaces system classes. It should
+also ensure that you can write such a loader and have it work in the
+expected fashion; all you lose is some performance due to "just-in-time
+verification" and the lack of DEX optimizations.
+
+There is a "back door" of sorts in the class resolution check, due to
+the fact that the "class ref" entries are shared between the bytecode
+and meta-data references (e.g. annotations and exception handler lists).
+The class references in annotations have no bearing on class verification,
+so when a class does an annotation query that causes a class reference
+index to be resolved, we don't want to fail just because the calling
+class was pre-verified and the resolved class is in some random DEX file.
+The successful resolution adds the class to the "resolved classes" table,
+so when optimized bytecode references it we don't repeat the resolve-time
+check. We can avoid this by not updating the "resolved classes" table
+when the class reference doesn't come out of something that has been
+checked by the verifier, but that has a nonzero performance impact.
+Since the ultimate goal of this test is to catch an unusual situation
+(user-defined class loaders redefining core classes), the added caution
+may not be worth the performance hit.
+*/
+
+/*
+ * Class serial numbers start at this value. We use a nonzero initial
+ * value so they stand out in binary dumps (e.g. hprof output).
+ */
+#define INITIAL_CLASS_SERIAL_NUMBER 0x50000000
+
+/*
+ * Constant used to size an auxillary class object data structure.
+ * For optimum memory use this should be equal to or slightly larger than
+ * the number of classes loaded when the zygote finishes initializing.
+ */
+#define ZYGOTE_CLASS_CUTOFF 2304
+
+#define CLASS_SFIELD_SLOTS 1
+
+static ClassPathEntry* processClassPath(const char* pathStr, bool isBootstrap);
+static void freeCpeArray(ClassPathEntry* cpe);
+
+static ClassObject* findClassFromLoaderNoInit(
+ const char* descriptor, Object* loader);
+static ClassObject* findClassNoInit(const char* descriptor, Object* loader,\
+ DvmDex* pDvmDex);
+static ClassObject* loadClassFromDex(DvmDex* pDvmDex,
+ const DexClassDef* pClassDef, Object* loader);
+static void loadMethodFromDex(ClassObject* clazz, const DexMethod* pDexMethod,\
+ Method* meth);
+static int computeJniArgInfo(const DexProto* proto);
+static void loadSFieldFromDex(ClassObject* clazz,
+ const DexField* pDexSField, StaticField* sfield);
+static void loadIFieldFromDex(ClassObject* clazz,
+ const DexField* pDexIField, InstField* field);
+static bool precacheReferenceOffsets(ClassObject* clazz);
+static void computeRefOffsets(ClassObject* clazz);
+static void freeMethodInnards(Method* meth);
+static bool createVtable(ClassObject* clazz);
+static bool createIftable(ClassObject* clazz);
+static bool insertMethodStubs(ClassObject* clazz);
+static bool computeFieldOffsets(ClassObject* clazz);
+static void throwEarlierClassFailure(ClassObject* clazz);
+
+#if LOG_CLASS_LOADING
+/*
+ * Logs information about a class loading with given timestamp.
+ *
+ * TODO: In the case where we fail in dvmLinkClass() and log the class as closing (type='<'),
+ * it would probably be better to use a new type code to indicate the failure. This change would
+ * require a matching change in the parser and analysis code in frameworks/base/tools/preload.
+ */
+static void logClassLoadWithTime(char type, ClassObject* clazz, u8 time) {
+ pid_t ppid = getppid();
+ pid_t pid = getpid();
+ unsigned int tid = (unsigned int) pthread_self();
+
+ LOG(LOG_INFO, "PRELOAD", "%c%d:%d:%d:%s:%d:%s:%lld\n", type, ppid, pid, tid,
+ get_process_name(), (int) clazz->classLoader, clazz->descriptor,
+ time);
+}
+
+/*
+ * Logs information about a class loading.
+ */
+static void logClassLoad(char type, ClassObject* clazz) {
+ logClassLoadWithTime(type, clazz, dvmGetThreadCpuTimeNsec());
+}
+#endif
+
+/*
+ * Some LinearAlloc unit tests.
+ */
+static void linearAllocTests()
+{
+ char* fiddle;
+ int try = 1;
+
+ switch (try) {
+ case 0:
+ fiddle = dvmLinearAlloc(NULL, 3200-28);
+ dvmLinearReadOnly(NULL, fiddle);
+ break;
+ case 1:
+ fiddle = dvmLinearAlloc(NULL, 3200-24);
+ dvmLinearReadOnly(NULL, fiddle);
+ break;
+ case 2:
+ fiddle = dvmLinearAlloc(NULL, 3200-20);
+ dvmLinearReadOnly(NULL, fiddle);
+ break;
+ case 3:
+ fiddle = dvmLinearAlloc(NULL, 3200-16);
+ dvmLinearReadOnly(NULL, fiddle);
+ break;
+ case 4:
+ fiddle = dvmLinearAlloc(NULL, 3200-12);
+ dvmLinearReadOnly(NULL, fiddle);
+ break;
+ }
+ fiddle = dvmLinearAlloc(NULL, 896);
+ dvmLinearReadOnly(NULL, fiddle);
+ fiddle = dvmLinearAlloc(NULL, 20); // watch addr of this alloc
+ dvmLinearReadOnly(NULL, fiddle);
+
+ fiddle = dvmLinearAlloc(NULL, 1);
+ fiddle[0] = 'q';
+ dvmLinearReadOnly(NULL, fiddle);
+ fiddle = dvmLinearAlloc(NULL, 4096);
+ fiddle[0] = 'x';
+ fiddle[4095] = 'y';
+ dvmLinearReadOnly(NULL, fiddle);
+ dvmLinearFree(NULL, fiddle);
+ fiddle = dvmLinearAlloc(NULL, 0);
+ dvmLinearReadOnly(NULL, fiddle);
+ fiddle = dvmLinearRealloc(NULL, fiddle, 12);
+ fiddle[11] = 'z';
+ dvmLinearReadOnly(NULL, fiddle);
+ fiddle = dvmLinearRealloc(NULL, fiddle, 5);
+ dvmLinearReadOnly(NULL, fiddle);
+ fiddle = dvmLinearAlloc(NULL, 17001);
+ fiddle[0] = 'x';
+ fiddle[17000] = 'y';
+ dvmLinearReadOnly(NULL, fiddle);
+
+ char* str = dvmLinearStrdup(NULL, "This is a test!");
+ LOGI("GOT: '%s'\n", str);
+
+ /* try to check the bounds; allocator may round allocation size up */
+ fiddle = dvmLinearAlloc(NULL, 12);
+ LOGI("Should be 1: %d\n", dvmLinearAllocContains(fiddle, 12));
+ LOGI("Should be 0: %d\n", dvmLinearAllocContains(fiddle, 13));
+ LOGI("Should be 0: %d\n", dvmLinearAllocContains(fiddle - 128*1024, 1));
+
+ dvmLinearAllocDump(NULL);
+ dvmLinearFree(NULL, str);
+}
+
+static size_t classObjectSize(size_t sfieldCount)
+{
+ size_t size;
+
+ size = offsetof(ClassObject, sfields);
+ size += sizeof(StaticField) * sfieldCount;
+ return size;
+}
+
+size_t dvmClassObjectSize(const ClassObject *clazz)
+{
+ assert(clazz != NULL);
+ return classObjectSize(clazz->sfieldCount);
+}
+
+/*
+ * Initialize the bootstrap class loader.
+ *
+ * Call this after the bootclasspath string has been finalized.
+ */
+bool dvmClassStartup(void)
+{
+ /* make this a requirement -- don't currently support dirs in path */
+ if (strcmp(gDvm.bootClassPathStr, ".") == 0) {
+ LOGE("ERROR: must specify non-'.' bootclasspath\n");
+ return false;
+ }
+
+ gDvm.loadedClasses =
+ dvmHashTableCreate(256, (HashFreeFunc) dvmFreeClassInnards);
+
+ gDvm.pBootLoaderAlloc = dvmLinearAllocCreate(NULL);
+ if (gDvm.pBootLoaderAlloc == NULL)
+ return false;
+
+ if (false) {
+ linearAllocTests();
+ exit(0);
+ }
+
+ /*
+ * Class serial number. We start with a high value to make it distinct
+ * in binary dumps (e.g. hprof).
+ */
+ gDvm.classSerialNumber = INITIAL_CLASS_SERIAL_NUMBER;
+
+ /* Set up the table we'll use for tracking initiating loaders for
+ * early classes.
+ * If it's NULL, we just fall back to the InitiatingLoaderList in the
+ * ClassObject, so it's not fatal to fail this allocation.
+ */
+ gDvm.initiatingLoaderList =
+ calloc(ZYGOTE_CLASS_CUTOFF, sizeof(InitiatingLoaderList));
+
+ gDvm.classJavaLangClass = (ClassObject*) dvmMalloc(
+ classObjectSize(CLASS_SFIELD_SLOTS), ALLOC_DEFAULT);
+ DVM_OBJECT_INIT(&gDvm.classJavaLangClass->obj, gDvm.classJavaLangClass);
+ gDvm.classJavaLangClass->descriptor = "Ljava/lang/Class;";
+ /*
+ * Process the bootstrap class path. This means opening the specified
+ * DEX or Jar files and possibly running them through the optimizer.
+ */
+ assert(gDvm.bootClassPath == NULL);
+ processClassPath(gDvm.bootClassPathStr, true);
+
+ if (gDvm.bootClassPath == NULL)
+ return false;
+
+ return true;
+}
+
+/*
+ * Clean up.
+ */
+void dvmClassShutdown(void)
+{
+ int i;
+
+ /* discard all system-loaded classes */
+ dvmHashTableFree(gDvm.loadedClasses);
+ gDvm.loadedClasses = NULL;
+
+ /* discard primitive classes created for arrays */
+ for (i = 0; i < PRIM_MAX; i++)
+ dvmFreeClassInnards(gDvm.primitiveClass[i]);
+
+ /* this closes DEX files, JAR files, etc. */
+ freeCpeArray(gDvm.bootClassPath);
+ gDvm.bootClassPath = NULL;
+
+ dvmLinearAllocDestroy(NULL);
+
+ free(gDvm.initiatingLoaderList);
+}
+
+
+/*
+ * ===========================================================================
+ * Bootstrap class loader
+ * ===========================================================================
+ */
+
+/*
+ * Dump the contents of a ClassPathEntry array.
+ */
+static void dumpClassPath(const ClassPathEntry* cpe)
+{
+ int idx = 0;
+
+ while (cpe->kind != kCpeLastEntry) {
+ const char* kindStr;
+
+ switch (cpe->kind) {
+ case kCpeDir: kindStr = "dir"; break;
+ case kCpeJar: kindStr = "jar"; break;
+ case kCpeDex: kindStr = "dex"; break;
+ default: kindStr = "???"; break;
+ }
+
+ LOGI(" %2d: type=%s %s %p\n", idx, kindStr, cpe->fileName, cpe->ptr);
+ if (CALC_CACHE_STATS && cpe->kind == kCpeJar) {
+ JarFile* pJarFile = (JarFile*) cpe->ptr;
+ DvmDex* pDvmDex = dvmGetJarFileDex(pJarFile);
+ dvmDumpAtomicCacheStats(pDvmDex->pInterfaceCache);
+ }
+
+ cpe++;
+ idx++;
+ }
+}
+
+/*
+ * Dump the contents of the bootstrap class path.
+ */
+void dvmDumpBootClassPath(void)
+{
+ dumpClassPath(gDvm.bootClassPath);
+}
+
+/*
+ * Returns "true" if the class path contains the specified path.
+ */
+bool dvmClassPathContains(const ClassPathEntry* cpe, const char* path)
+{
+ while (cpe->kind != kCpeLastEntry) {
+ if (strcmp(cpe->fileName, path) == 0)
+ return true;
+
+ cpe++;
+ }
+ return false;
+}
+
+/*
+ * Free an array of ClassPathEntry structs.
+ *
+ * We release the contents of each entry, then free the array itself.
+ */
+static void freeCpeArray(ClassPathEntry* cpe)
+{
+ ClassPathEntry* cpeStart = cpe;
+
+ if (cpe == NULL)
+ return;
+
+ while (cpe->kind != kCpeLastEntry) {
+ switch (cpe->kind) {
+ case kCpeJar:
+ /* free JarFile */
+ dvmJarFileFree((JarFile*) cpe->ptr);
+ break;
+ case kCpeDex:
+ /* free RawDexFile */
+ dvmRawDexFileFree((RawDexFile*) cpe->ptr);
+ break;
+ default:
+ /* e.g. kCpeDir */
+ assert(cpe->ptr == NULL);
+ break;
+ }
+
+ free(cpe->fileName);
+ cpe++;
+ }
+
+ free(cpeStart);
+}
+
+/*
+ * Prepare a ClassPathEntry struct, which at this point only has a valid
+ * filename. We need to figure out what kind of file it is, and for
+ * everything other than directories we need to open it up and see
+ * what's inside.
+ */
+static bool prepareCpe(ClassPathEntry* cpe, bool isBootstrap)
+{
+ JarFile* pJarFile = NULL;
+ RawDexFile* pRawDexFile = NULL;
+ struct stat sb;
+ int cc;
+
+ cc = stat(cpe->fileName, &sb);
+ if (cc < 0) {
+ LOGD("Unable to stat classpath element '%s'\n", cpe->fileName);
+ return false;
+ }
+ if (S_ISDIR(sb.st_mode)) {
+ /*
+ * The directory will usually have .class files in subdirectories,
+ * which may be a few levels down. Doing a recursive scan and
+ * caching the results would help us avoid hitting the filesystem
+ * on misses. Whether or not this is of measureable benefit
+ * depends on a number of factors, but most likely it is not
+ * worth the effort (especially since most of our stuff will be
+ * in DEX or JAR).
+ */
+ cpe->kind = kCpeDir;
+ assert(cpe->ptr == NULL);
+ return true;
+ }
+
+ if (dvmJarFileOpen(cpe->fileName, NULL, &pJarFile, isBootstrap) == 0) {
+ cpe->kind = kCpeJar;
+ cpe->ptr = pJarFile;
+ return true;
+ }
+
+ // TODO: do we still want to support "raw" DEX files in the classpath?
+ if (dvmRawDexFileOpen(cpe->fileName, NULL, &pRawDexFile, isBootstrap) == 0)
+ {
+ cpe->kind = kCpeDex;
+ cpe->ptr = pRawDexFile;
+ return true;
+ }
+
+ LOGD("Unable to process classpath element '%s'\n", cpe->fileName);
+ return false;
+}
+
+/*
+ * Convert a colon-separated list of directories, Zip files, and DEX files
+ * into an array of ClassPathEntry structs.
+ *
+ * During normal startup we fail if there are no entries, because we won't
+ * get very far without the basic language support classes, but if we're
+ * optimizing a DEX file we allow it.
+ *
+ * If entries are added or removed from the bootstrap class path, the
+ * dependencies in the DEX files will break, and everything except the
+ * very first entry will need to be regenerated.
+ */
+static ClassPathEntry* processClassPath(const char* pathStr, bool isBootstrap)
+{
+ ClassPathEntry* cpe = NULL;
+ char* mangle;
+ char* cp;
+ const char* end;
+ int idx, count;
+
+ assert(pathStr != NULL);
+
+ mangle = strdup(pathStr);
+
+ /*
+ * Run through and essentially strtok() the string. Get a count of
+ * the #of elements while we're at it.
+ *
+ * If the path was constructed strangely (e.g. ":foo::bar:") this will
+ * over-allocate, which isn't ideal but is mostly harmless.
+ */
+ count = 1;
+ for (cp = mangle; *cp != '\0'; cp++) {
+ if (*cp == ':') { /* separates two entries */
+ count++;
+ *cp = '\0';
+ }
+ }
+ end = cp;
+
+ /*
+ * Allocate storage. We over-alloc by one so we can set an "end" marker.
+ */
+ cpe = (ClassPathEntry*) calloc(count+1, sizeof(ClassPathEntry));
+
+ /*
+ * Set the global pointer so the DEX file dependency stuff can find it.
+ */
+ gDvm.bootClassPath = cpe;
+
+ /*
+ * Go through a second time, pulling stuff out.
+ */
+ cp = mangle;
+ idx = 0;
+ while (cp < end) {
+ if (*cp == '\0') {
+ /* leading, trailing, or doubled ':'; ignore it */
+ } else {
+ if (isBootstrap &&
+ dvmPathToAbsolutePortion(cp) == NULL) {
+ LOGE("Non-absolute bootclasspath entry '%s'\n", cp);
+ free(cpe);
+ cpe = NULL;
+ goto bail;
+ }
+
+ ClassPathEntry tmp;
+ tmp.kind = kCpeUnknown;
+ tmp.fileName = strdup(cp);
+ tmp.ptr = NULL;
+
+ /*
+ * Drop an end marker here so DEX loader can walk unfinished
+ * list.
+ */
+ cpe[idx].kind = kCpeLastEntry;
+ cpe[idx].fileName = NULL;
+ cpe[idx].ptr = NULL;
+
+ if (!prepareCpe(&tmp, isBootstrap)) {
+ /* drop from list and continue on */
+ free(tmp.fileName);
+ } else {
+ /* copy over, pointers and all */
+ cpe[idx] = tmp;
+ idx++;
+ }
+ }
+
+ cp += strlen(cp) +1;
+ }
+ assert(idx <= count);
+ if (idx == 0 && !gDvm.optimizing) {
+ LOGE("No valid entries found in bootclasspath '%s'\n", pathStr);
+ free(cpe);
+ cpe = NULL;
+ goto bail;
+ }
+
+ LOGVV(" (filled %d of %d slots)\n", idx, count);
+
+ /* put end marker in over-alloc slot */
+ cpe[idx].kind = kCpeLastEntry;
+ cpe[idx].fileName = NULL;
+ cpe[idx].ptr = NULL;
+
+ //dumpClassPath(cpe);
+
+bail:
+ free(mangle);
+ gDvm.bootClassPath = cpe;
+ return cpe;
+}
+
+/*
+ * Search the DEX files we loaded from the bootstrap class path for a DEX
+ * file that has the class with the matching descriptor.
+ *
+ * Returns the matching DEX file and DexClassDef entry if found, otherwise
+ * returns NULL.
+ */
+static DvmDex* searchBootPathForClass(const char* descriptor,
+ const DexClassDef** ppClassDef)
+{
+ const ClassPathEntry* cpe = gDvm.bootClassPath;
+ const DexClassDef* pFoundDef = NULL;
+ DvmDex* pFoundFile = NULL;
+
+ LOGVV("+++ class '%s' not yet loaded, scanning bootclasspath...\n",
+ descriptor);
+
+ while (cpe->kind != kCpeLastEntry) {
+ //LOGV("+++ checking '%s' (%d)\n", cpe->fileName, cpe->kind);
+
+ switch (cpe->kind) {
+ case kCpeDir:
+ LOGW("Directory entries ('%s') not supported in bootclasspath\n",
+ cpe->fileName);
+ break;
+ case kCpeJar:
+ {
+ JarFile* pJarFile = (JarFile*) cpe->ptr;
+ const DexClassDef* pClassDef;
+ DvmDex* pDvmDex;
+
+ pDvmDex = dvmGetJarFileDex(pJarFile);
+ pClassDef = dexFindClass(pDvmDex->pDexFile, descriptor);
+ if (pClassDef != NULL) {
+ /* found */
+ pFoundDef = pClassDef;
+ pFoundFile = pDvmDex;
+ goto found;
+ }
+ }
+ break;
+ case kCpeDex:
+ {
+ RawDexFile* pRawDexFile = (RawDexFile*) cpe->ptr;
+ const DexClassDef* pClassDef;
+ DvmDex* pDvmDex;
+
+ pDvmDex = dvmGetRawDexFileDex(pRawDexFile);
+ pClassDef = dexFindClass(pDvmDex->pDexFile, descriptor);
+ if (pClassDef != NULL) {
+ /* found */
+ pFoundDef = pClassDef;
+ pFoundFile = pDvmDex;
+ goto found;
+ }
+ }
+ break;
+ default:
+ LOGE("Unknown kind %d\n", cpe->kind);
+ assert(false);
+ break;
+ }
+
+ cpe++;
+ }
+
+ /*
+ * Special handling during verification + optimization.
+ *
+ * The DEX optimizer needs to load classes from the DEX file it's working
+ * on. Rather than trying to insert it into the bootstrap class path
+ * or synthesizing a class loader to manage it, we just make it available
+ * here. It logically comes after all existing entries in the bootstrap
+ * class path.
+ */
+ if (gDvm.bootClassPathOptExtra != NULL) {
+ const DexClassDef* pClassDef;
+
+ pClassDef =
+ dexFindClass(gDvm.bootClassPathOptExtra->pDexFile, descriptor);
+ if (pClassDef != NULL) {
+ /* found */
+ pFoundDef = pClassDef;
+ pFoundFile = gDvm.bootClassPathOptExtra;
+ }
+ }
+
+found:
+ *ppClassDef = pFoundDef;
+ return pFoundFile;
+}
+
+/*
+ * Set the "extra" DEX, which becomes a de facto member of the bootstrap
+ * class set.
+ */
+void dvmSetBootPathExtraDex(DvmDex* pDvmDex)
+{
+ gDvm.bootClassPathOptExtra = pDvmDex;
+}
+
+
+/*
+ * Return the #of entries in the bootstrap class path.
+ *
+ * (Used for ClassLoader.getResources().)
+ */
+int dvmGetBootPathSize(void)
+{
+ const ClassPathEntry* cpe = gDvm.bootClassPath;
+
+ while (cpe->kind != kCpeLastEntry)
+ cpe++;
+
+ return cpe - gDvm.bootClassPath;
+}
+
+/*
+ * Find a resource with the specified name in entry N of the boot class path.
+ *
+ * We return a newly-allocated String of one of these forms:
+ * file://path/name
+ * jar:file://path!/name
+ * Where "path" is the bootstrap class path entry and "name" is the string
+ * passed into this method. "path" needs to be an absolute path (starting
+ * with '/'); if it's not we'd need to "absolutify" it as part of forming
+ * the URL string.
+ */
+StringObject* dvmGetBootPathResource(const char* name, int idx)
+{
+ const int kUrlOverhead = 13; // worst case for Jar URL
+ const ClassPathEntry* cpe = gDvm.bootClassPath;
+ StringObject* urlObj = NULL;
+
+ LOGV("+++ searching for resource '%s' in %d(%s)\n",
+ name, idx, cpe[idx].fileName);
+
+ /* we could use direct array index, but I don't entirely trust "idx" */
+ while (idx-- && cpe->kind != kCpeLastEntry)
+ cpe++;
+ if (cpe->kind == kCpeLastEntry) {
+ assert(false);
+ return NULL;
+ }
+
+ char urlBuf[strlen(name) + strlen(cpe->fileName) + kUrlOverhead +1];
+
+ switch (cpe->kind) {
+ case kCpeDir:
+ sprintf(urlBuf, "file://%s/%s", cpe->fileName, name);
+ if (access(urlBuf+7, F_OK) != 0)
+ goto bail;
+ break;
+ case kCpeJar:
+ {
+ JarFile* pJarFile = (JarFile*) cpe->ptr;
+ if (dexZipFindEntry(&pJarFile->archive, name) == NULL)
+ goto bail;
+ sprintf(urlBuf, "jar:file://%s!/%s", cpe->fileName, name);
+ }
+ break;
+ case kCpeDex:
+ LOGV("No resources in DEX files\n");
+ goto bail;
+ default:
+ assert(false);
+ goto bail;
+ }
+
+ LOGV("+++ using URL='%s'\n", urlBuf);
+ urlObj = dvmCreateStringFromCstr(urlBuf);
+
+bail:
+ return urlObj;
+}
+
+
+/*
+ * ===========================================================================
+ * Class list management
+ * ===========================================================================
+ */
+
+/* search for these criteria in the Class hash table */
+typedef struct ClassMatchCriteria {
+ const char* descriptor;
+ Object* loader;
+} ClassMatchCriteria;
+
+#define kInitLoaderInc 4 /* must be power of 2 */
+
+static InitiatingLoaderList *dvmGetInitiatingLoaderList(ClassObject* clazz)
+{
+ assert(clazz->serialNumber >= INITIAL_CLASS_SERIAL_NUMBER);
+ int classIndex = clazz->serialNumber-INITIAL_CLASS_SERIAL_NUMBER;
+ if (gDvm.initiatingLoaderList != NULL &&
+ classIndex < ZYGOTE_CLASS_CUTOFF) {
+ return &(gDvm.initiatingLoaderList[classIndex]);
+ } else {
+ return &(clazz->initiatingLoaderList);
+ }
+}
+
+/*
+ * Determine if "loader" appears in clazz' initiating loader list.
+ *
+ * The class hash table lock must be held when calling here, since
+ * it's also used when updating a class' initiating loader list.
+ *
+ * TODO: switch to some sort of lock-free data structure so we don't have
+ * to grab the lock to do a lookup. Among other things, this would improve
+ * the speed of compareDescriptorClasses().
+ */
+bool dvmLoaderInInitiatingList(const ClassObject* clazz, const Object* loader)
+{
+ /*
+ * The bootstrap class loader can't be just an initiating loader for
+ * anything (it's always the defining loader if the class is visible
+ * to it). We don't put defining loaders in the initiating list.
+ */
+ if (loader == NULL)
+ return false;
+
+ /*
+ * Scan the list for a match. The list is expected to be short.
+ */
+ /* Cast to remove the const from clazz, but use const loaderList */
+ ClassObject* nonConstClazz = (ClassObject*) clazz;
+ const InitiatingLoaderList *loaderList =
+ dvmGetInitiatingLoaderList(nonConstClazz);
+ int i;
+ for (i = loaderList->initiatingLoaderCount-1; i >= 0; --i) {
+ if (loaderList->initiatingLoaders[i] == loader) {
+ //LOGI("+++ found initiating match %p in %s\n",
+ // loader, clazz->descriptor);
+ return true;
+ }
+ }
+ return false;
+}
+
+/*
+ * Add "loader" to clazz's initiating loader set, unless it's the defining
+ * class loader.
+ *
+ * In the common case this will be a short list, so we don't need to do
+ * anything too fancy here.
+ *
+ * This locks gDvm.loadedClasses for synchronization, so don't hold it
+ * when calling here.
+ */
+void dvmAddInitiatingLoader(ClassObject* clazz, Object* loader)
+{
+ if (loader != clazz->classLoader) {
+ assert(loader != NULL);
+
+ LOGVV("Adding %p to '%s' init list\n", loader, clazz->descriptor);
+ dvmHashTableLock(gDvm.loadedClasses);
+
+ /*
+ * Make sure nobody snuck in. The penalty for adding twice is
+ * pretty minor, and probably outweighs the O(n^2) hit for
+ * checking before every add, so we may not want to do this.
+ */
+ //if (dvmLoaderInInitiatingList(clazz, loader)) {
+ // LOGW("WOW: simultaneous add of initiating class loader\n");
+ // goto bail_unlock;
+ //}
+
+ /*
+ * The list never shrinks, so we just keep a count of the
+ * number of elements in it, and reallocate the buffer when
+ * we run off the end.
+ *
+ * The pointer is initially NULL, so we *do* want to call realloc
+ * when count==0.
+ */
+ InitiatingLoaderList *loaderList = dvmGetInitiatingLoaderList(clazz);
+ if ((loaderList->initiatingLoaderCount & (kInitLoaderInc-1)) == 0) {
+ Object** newList;
+
+ newList = (Object**) realloc(loaderList->initiatingLoaders,
+ (loaderList->initiatingLoaderCount + kInitLoaderInc)
+ * sizeof(Object*));
+ if (newList == NULL) {
+ /* this is mainly a cache, so it's not the EotW */
+ assert(false);
+ goto bail_unlock;
+ }
+ loaderList->initiatingLoaders = newList;
+
+ //LOGI("Expanded init list to %d (%s)\n",
+ // loaderList->initiatingLoaderCount+kInitLoaderInc,
+ // clazz->descriptor);
+ }
+ loaderList->initiatingLoaders[loaderList->initiatingLoaderCount++] =
+ loader;
+
+bail_unlock:
+ dvmHashTableUnlock(gDvm.loadedClasses);
+ }
+}
+
+/*
+ * (This is a dvmHashTableLookup callback.)
+ *
+ * Entries in the class hash table are stored as { descriptor, d-loader }
+ * tuples. If the hashed class descriptor matches the requested descriptor,
+ * and the hashed defining class loader matches the requested class
+ * loader, we're good. If only the descriptor matches, we check to see if the
+ * loader is in the hashed class' initiating loader list. If so, we
+ * can return "true" immediately and skip some of the loadClass melodrama.
+ *
+ * The caller must lock the hash table before calling here.
+ *
+ * Returns 0 if a matching entry is found, nonzero otherwise.
+ */
+static int hashcmpClassByCrit(const void* vclazz, const void* vcrit)
+{
+ const ClassObject* clazz = (const ClassObject*) vclazz;
+ const ClassMatchCriteria* pCrit = (const ClassMatchCriteria*) vcrit;
+ bool match;
+
+ match = (strcmp(clazz->descriptor, pCrit->descriptor) == 0 &&
+ (clazz->classLoader == pCrit->loader ||
+ (pCrit->loader != NULL &&
+ dvmLoaderInInitiatingList(clazz, pCrit->loader)) ));
+ //if (match)
+ // LOGI("+++ %s %p matches existing %s %p\n",
+ // pCrit->descriptor, pCrit->loader,
+ // clazz->descriptor, clazz->classLoader);
+ return !match;
+}
+
+/*
+ * Like hashcmpClassByCrit, but passing in a fully-formed ClassObject
+ * instead of a ClassMatchCriteria.
+ */
+static int hashcmpClassByClass(const void* vclazz, const void* vaddclazz)
+{
+ const ClassObject* clazz = (const ClassObject*) vclazz;
+ const ClassObject* addClazz = (const ClassObject*) vaddclazz;
+ bool match;
+
+ match = (strcmp(clazz->descriptor, addClazz->descriptor) == 0 &&
+ (clazz->classLoader == addClazz->classLoader ||
+ (addClazz->classLoader != NULL &&
+ dvmLoaderInInitiatingList(clazz, addClazz->classLoader)) ));
+ return !match;
+}
+
+/*
+ * Search through the hash table to find an entry with a matching descriptor
+ * and an initiating class loader that matches "loader".
+ *
+ * The table entries are hashed on descriptor only, because they're unique
+ * on *defining* class loader, not *initiating* class loader. This isn't
+ * great, because it guarantees we will have to probe when multiple
+ * class loaders are used.
+ *
+ * Note this does NOT try to load a class; it just finds a class that
+ * has already been loaded.
+ *
+ * If "unprepOkay" is set, this will return classes that have been added
+ * to the hash table but are not yet fully loaded and linked. Otherwise,
+ * such classes are ignored. (The only place that should set "unprepOkay"
+ * is findClassNoInit(), which will wait for the prep to finish.)
+ *
+ * Returns NULL if not found.
+ */
+ClassObject* dvmLookupClass(const char* descriptor, Object* loader,
+ bool unprepOkay)
+{
+ ClassMatchCriteria crit;
+ void* found;
+ u4 hash;
+
+ crit.descriptor = descriptor;
+ crit.loader = loader;
+ hash = dvmComputeUtf8Hash(descriptor);
+
+ LOGVV("threadid=%d: dvmLookupClass searching for '%s' %p\n",
+ dvmThreadSelf()->threadId, descriptor, loader);
+
+ dvmHashTableLock(gDvm.loadedClasses);
+ found = dvmHashTableLookup(gDvm.loadedClasses, hash, &crit,
+ hashcmpClassByCrit, false);
+ dvmHashTableUnlock(gDvm.loadedClasses);
+
+ /*
+ * The class has been added to the hash table but isn't ready for use.
+ * We're going to act like we didn't see it, so that the caller will
+ * go through the full "find class" path, which includes locking the
+ * object and waiting until it's ready. We could do that lock/wait
+ * here, but this is an extremely rare case, and it's simpler to have
+ * the wait-for-class code centralized.
+ */
+ if (found != NULL && !unprepOkay && !dvmIsClassLinked(found)) {
+ LOGV("Ignoring not-yet-ready %s, using slow path\n",
+ ((ClassObject*)found)->descriptor);
+ found = NULL;
+ }
+
+ return (ClassObject*) found;
+}
+
+/*
+ * Add a new class to the hash table.
+ *
+ * The class is considered "new" if it doesn't match on both the class
+ * descriptor and the defining class loader.
+ *
+ * TODO: we should probably have separate hash tables for each
+ * ClassLoader. This could speed up dvmLookupClass and
+ * other common operations. It does imply a VM-visible data structure
+ * for each ClassLoader object with loaded classes, which we don't
+ * have yet.
+ */
+bool dvmAddClassToHash(ClassObject* clazz)
+{
+ void* found;
+ u4 hash;
+
+ hash = dvmComputeUtf8Hash(clazz->descriptor);
+
+ dvmHashTableLock(gDvm.loadedClasses);
+ found = dvmHashTableLookup(gDvm.loadedClasses, hash, clazz,
+ hashcmpClassByClass, true);
+ dvmHashTableUnlock(gDvm.loadedClasses);
+
+ LOGV("+++ dvmAddClassToHash '%s' %p (isnew=%d) --> %p\n",
+ clazz->descriptor, clazz->classLoader,
+ (found == (void*) clazz), clazz);
+
+ //dvmCheckClassTablePerf();
+
+ /* can happen if two threads load the same class simultaneously */
+ return (found == (void*) clazz);
+}
+
+#if 0
+/*
+ * Compute hash value for a class.
+ */
+u4 hashcalcClass(const void* item)
+{
+ return dvmComputeUtf8Hash(((const ClassObject*) item)->descriptor);
+}
+
+/*
+ * Check the performance of the "loadedClasses" hash table.
+ */
+void dvmCheckClassTablePerf(void)
+{
+ dvmHashTableLock(gDvm.loadedClasses);
+ dvmHashTableProbeCount(gDvm.loadedClasses, hashcalcClass,
+ hashcmpClassByClass);
+ dvmHashTableUnlock(gDvm.loadedClasses);
+}
+#endif
+
+/*
+ * Remove a class object from the hash table.
+ */
+static void removeClassFromHash(ClassObject* clazz)
+{
+ LOGV("+++ removeClassFromHash '%s'\n", clazz->descriptor);
+
+ u4 hash = dvmComputeUtf8Hash(clazz->descriptor);
+
+ dvmHashTableLock(gDvm.loadedClasses);
+ if (!dvmHashTableRemove(gDvm.loadedClasses, hash, clazz))
+ LOGW("Hash table remove failed on class '%s'\n", clazz->descriptor);
+ dvmHashTableUnlock(gDvm.loadedClasses);
+}
+
+
+/*
+ * ===========================================================================
+ * Class creation
+ * ===========================================================================
+ */
+
+/*
+ * Set clazz->serialNumber to the next available value.
+ *
+ * This usually happens *very* early in class creation, so don't expect
+ * anything else in the class to be ready.
+ */
+void dvmSetClassSerialNumber(ClassObject* clazz)
+{
+ assert(clazz->serialNumber == 0);
+ clazz->serialNumber = android_atomic_inc(&gDvm.classSerialNumber);
+}
+
+
+/*
+ * Find the named class (by descriptor), using the specified
+ * initiating ClassLoader.
+ *
+ * The class will be loaded and initialized if it has not already been.
+ * If necessary, the superclass will be loaded.
+ *
+ * If the class can't be found, returns NULL with an appropriate exception
+ * raised.
+ */
+ClassObject* dvmFindClass(const char* descriptor, Object* loader)
+{
+ ClassObject* clazz;
+
+ clazz = dvmFindClassNoInit(descriptor, loader);
+ if (clazz != NULL && clazz->status < CLASS_INITIALIZED) {
+ /* initialize class */
+ if (!dvmInitClass(clazz)) {
+ /* init failed; leave it in the list, marked as bad */
+ assert(dvmCheckException(dvmThreadSelf()));
+ assert(clazz->status == CLASS_ERROR);
+ return NULL;
+ }
+ }
+
+ return clazz;
+}
+
+/*
+ * Find the named class (by descriptor), using the specified
+ * initiating ClassLoader.
+ *
+ * The class will be loaded if it has not already been, as will its
+ * superclass. It will not be initialized.
+ *
+ * If the class can't be found, returns NULL with an appropriate exception
+ * raised.
+ */
+ClassObject* dvmFindClassNoInit(const char* descriptor,
+ Object* loader)
+{
+ assert(descriptor != NULL);
+ //assert(loader != NULL);
+
+ LOGVV("FindClassNoInit '%s' %p\n", descriptor, loader);
+
+ if (*descriptor == '[') {
+ /*
+ * Array class. Find in table, generate if not found.
+ */
+ return dvmFindArrayClass(descriptor, loader);
+ } else {
+ /*
+ * Regular class. Find in table, load if not found.
+ */
+ if (loader != NULL) {
+ return findClassFromLoaderNoInit(descriptor, loader);
+ } else {
+ return dvmFindSystemClassNoInit(descriptor);
+ }
+ }
+}
+
+/*
+ * Load the named class (by descriptor) from the specified class
+ * loader. This calls out to let the ClassLoader object do its thing.
+ *
+ * Returns with NULL and an exception raised on error.
+ */
+static ClassObject* findClassFromLoaderNoInit(const char* descriptor,
+ Object* loader)
+{
+ //LOGI("##### findClassFromLoaderNoInit (%s,%p)\n",
+ // descriptor, loader);
+
+ Thread* self = dvmThreadSelf();
+ ClassObject* clazz;
+
+ assert(loader != NULL);
+
+ /*
+ * Do we already have it?
+ *
+ * The class loader code does the "is it already loaded" check as
+ * well. However, this call is much faster than calling through
+ * interpreted code. Doing this does mean that in the common case
+ * (365 out of 420 calls booting the sim) we're doing the
+ * lookup-by-descriptor twice. It appears this is still a win, so
+ * I'm keeping it in.
+ */
+ clazz = dvmLookupClass(descriptor, loader, false);
+ if (clazz != NULL) {
+ LOGVV("Already loaded: %s %p\n", descriptor, loader);
+ return clazz;
+ } else {
+ LOGVV("Not already loaded: %s %p\n", descriptor, loader);
+ }
+
+ char* dotName = NULL;
+ StringObject* nameObj = NULL;
+ Object* excep;
+ Method* loadClass;
+
+ /* convert "Landroid/debug/Stuff;" to "android.debug.Stuff" */
+ dotName = dvmDescriptorToDot(descriptor);
+ if (dotName == NULL) {
+ dvmThrowException("Ljava/lang/OutOfMemoryError;", NULL);
+ goto bail;
+ }
+ nameObj = dvmCreateStringFromCstr(dotName);
+ if (nameObj == NULL) {
+ assert(dvmCheckException(self));
+ goto bail;
+ }
+
+ // TODO: cache the vtable offset
+ loadClass = dvmFindVirtualMethodHierByDescriptor(loader->clazz, "loadClass",
+ "(Ljava/lang/String;)Ljava/lang/Class;");
+ if (loadClass == NULL) {
+ LOGW("Couldn't find loadClass in ClassLoader\n");
+ goto bail;
+ }
+
+ dvmMethodTraceClassPrepBegin();
+
+ /*
+ * Invoke loadClass(). This will probably result in a couple of
+ * exceptions being thrown, because the ClassLoader.loadClass()
+ * implementation eventually calls VMClassLoader.loadClass to see if
+ * the bootstrap class loader can find it before doing its own load.
+ */
+ LOGVV("--- Invoking loadClass(%s, %p)\n", dotName, loader);
+ JValue result;
+ dvmCallMethod(self, loadClass, loader, &result, nameObj);
+ clazz = (ClassObject*) result.l;
+
+ dvmMethodTraceClassPrepEnd();
+
+ excep = dvmGetException(self);
+ if (excep != NULL) {
+#if DVM_SHOW_EXCEPTION >= 2
+ LOGD("NOTE: loadClass '%s' %p threw exception %s\n",
+ dotName, loader, excep->clazz->descriptor);
+#endif
+ dvmAddTrackedAlloc(excep, self);
+ dvmClearException(self);
+ dvmThrowChainedExceptionWithClassMessage(
+ "Ljava/lang/NoClassDefFoundError;", descriptor, excep);
+ dvmReleaseTrackedAlloc(excep, self);
+ clazz = NULL;
+ goto bail;
+ } else if (clazz == NULL) {
+ LOGW("ClassLoader returned NULL w/o exception pending\n");
+ dvmThrowException("Ljava/lang/NullPointerException;",
+ "ClassLoader returned null");
+ goto bail;
+ }
+
+ dvmAddInitiatingLoader(clazz, loader);
+
+ LOGVV("--- Successfully loaded %s %p (thisldr=%p clazz=%p)\n",
+ descriptor, clazz->classLoader, loader, clazz);
+
+bail:
+ dvmReleaseTrackedAlloc((Object*)nameObj, NULL);
+ free(dotName);
+ return clazz;
+}
+
+/*
+ * Load the named class (by descriptor) from the specified DEX file.
+ * Used by class loaders to instantiate a class object from a
+ * VM-managed DEX.
+ */
+ClassObject* dvmDefineClass(DvmDex* pDvmDex, const char* descriptor,
+ Object* classLoader)
+{
+ assert(pDvmDex != NULL);
+
+ return findClassNoInit(descriptor, classLoader, pDvmDex);
+}
+
+
+/*
+ * Find the named class (by descriptor), scanning through the
+ * bootclasspath if it hasn't already been loaded.
+ *
+ * "descriptor" looks like "Landroid/debug/Stuff;".
+ *
+ * Uses NULL as the defining class loader.
+ */
+ClassObject* dvmFindSystemClass(const char* descriptor)
+{
+ ClassObject* clazz;
+
+ clazz = dvmFindSystemClassNoInit(descriptor);
+ if (clazz != NULL && clazz->status < CLASS_INITIALIZED) {
+ /* initialize class */
+ if (!dvmInitClass(clazz)) {
+ /* init failed; leave it in the list, marked as bad */
+ assert(dvmCheckException(dvmThreadSelf()));
+ assert(clazz->status == CLASS_ERROR);
+ return NULL;
+ }
+ }
+
+ return clazz;
+}
+
+/*
+ * Find the named class (by descriptor), searching for it in the
+ * bootclasspath.
+ *
+ * On failure, this returns NULL with an exception raised.
+ */
+ClassObject* dvmFindSystemClassNoInit(const char* descriptor)
+{
+ return findClassNoInit(descriptor, NULL, NULL);
+}
+
+/*
+ * Find the named class (by descriptor). If it's not already loaded,
+ * we load it and link it, but don't execute <clinit>. (The VM has
+ * specific limitations on which events can cause initialization.)
+ *
+ * If "pDexFile" is NULL, we will search the bootclasspath for an entry.
+ *
+ * On failure, this returns NULL with an exception raised.
+ *
+ * TODO: we need to return an indication of whether we loaded the class or
+ * used an existing definition. If somebody deliberately tries to load a
+ * class twice in the same class loader, they should get a LinkageError,
+ * but inadvertent simultaneous class references should "just work".
+ */
+static ClassObject* findClassNoInit(const char* descriptor, Object* loader,
+ DvmDex* pDvmDex)
+{
+ Thread* self = dvmThreadSelf();
+ ClassObject* clazz;
+ bool profilerNotified = false;
+
+ if (loader != NULL) {
+ LOGVV("#### findClassNoInit(%s,%p,%p)\n", descriptor, loader,
+ pDvmDex->pDexFile);
+ }
+
+ /*
+ * We don't expect an exception to be raised at this point. The
+ * exception handling code is good about managing this. This *can*
+ * happen if a JNI lookup fails and the JNI code doesn't do any
+ * error checking before doing another class lookup, so we may just
+ * want to clear this and restore it on exit. If we don't, some kinds
+ * of failures can't be detected without rearranging other stuff.
+ *
+ * Most often when we hit this situation it means that something is
+ * broken in the VM or in JNI code, so I'm keeping it in place (and
+ * making it an informative abort rather than an assert).
+ */
+ if (dvmCheckException(self)) {
+ LOGE("Class lookup %s attempted while exception %s pending\n",
+ descriptor, dvmGetException(self)->clazz->descriptor);
+ dvmDumpAllThreads(false);
+ dvmAbort();
+ }
+
+ clazz = dvmLookupClass(descriptor, loader, true);
+ if (clazz == NULL) {
+ const DexClassDef* pClassDef;
+
+ dvmMethodTraceClassPrepBegin();
+ profilerNotified = true;
+
+#if LOG_CLASS_LOADING
+ u8 startTime = dvmGetThreadCpuTimeNsec();
+#endif
+
+ if (pDvmDex == NULL) {
+ assert(loader == NULL); /* shouldn't be here otherwise */
+ pDvmDex = searchBootPathForClass(descriptor, &pClassDef);
+ } else {
+ pClassDef = dexFindClass(pDvmDex->pDexFile, descriptor);
+ }
+
+ if (pDvmDex == NULL || pClassDef == NULL) {
+ if (gDvm.noClassDefFoundErrorObj != NULL) {
+ /* usual case -- use prefabricated object */
+ dvmSetException(self, gDvm.noClassDefFoundErrorObj);
+ } else {
+ /* dexopt case -- can't guarantee prefab (core.jar) */
+ dvmThrowExceptionWithClassMessage(
+ "Ljava/lang/NoClassDefFoundError;", descriptor);
+ }
+ goto bail;
+ }
+
+ /* found a match, try to load it */
+ clazz = loadClassFromDex(pDvmDex, pClassDef, loader);
+ if (dvmCheckException(self)) {
+ /* class was found but had issues */
+ if (clazz != NULL) {
+ dvmFreeClassInnards(clazz);
+ dvmReleaseTrackedAlloc((Object*) clazz, NULL);
+ }
+ goto bail;
+ }
+
+ /*
+ * Lock the class while we link it so other threads must wait for us
+ * to finish. Set the "initThreadId" so we can identify recursive
+ * invocation.
+ */
+ dvmLockObject(self, (Object*) clazz);
+ clazz->initThreadId = self->threadId;
+
+ /*
+ * Add to hash table so lookups succeed.
+ *
+ * [Are circular references possible when linking a class?]
+ */
+ assert(clazz->classLoader == loader);
+ if (!dvmAddClassToHash(clazz)) {
+ /*
+ * Another thread must have loaded the class after we
+ * started but before we finished. Discard what we've
+ * done and leave some hints for the GC.
+ *
+ * (Yes, this happens.)
+ */
+ //LOGW("WOW: somebody loaded %s simultaneously\n", descriptor);
+ clazz->initThreadId = 0;
+ dvmUnlockObject(self, (Object*) clazz);
+
+ /* Let the GC free the class.
+ */
+ dvmFreeClassInnards(clazz);
+ dvmReleaseTrackedAlloc((Object*) clazz, NULL);
+
+ /* Grab the winning class.
+ */
+ clazz = dvmLookupClass(descriptor, loader, true);
+ assert(clazz != NULL);
+ goto got_class;
+ }
+ dvmReleaseTrackedAlloc((Object*) clazz, NULL);
+
+#if LOG_CLASS_LOADING
+ logClassLoadWithTime('>', clazz, startTime);
+#endif
+ /*
+ * Prepare and resolve.
+ */
+ if (!dvmLinkClass(clazz)) {
+ assert(dvmCheckException(self));
+
+ /* Make note of the error and clean up the class.
+ */
+ removeClassFromHash(clazz);
+ clazz->status = CLASS_ERROR;
+ dvmFreeClassInnards(clazz);
+
+ /* Let any waiters know.
+ */
+ clazz->initThreadId = 0;
+ dvmObjectNotifyAll(self, (Object*) clazz);
+ dvmUnlockObject(self, (Object*) clazz);
+
+#if LOG_CLASS_LOADING
+ LOG(LOG_INFO, "DVMLINK FAILED FOR CLASS ", "%s in %s\n",
+ clazz->descriptor, get_process_name());
+
+ /*
+ * TODO: It would probably be better to use a new type code here (instead of '<') to
+ * indicate the failure. This change would require a matching change in the parser
+ * and analysis code in frameworks/base/tools/preload.
+ */
+ logClassLoad('<', clazz);
+#endif
+ clazz = NULL;
+ if (gDvm.optimizing) {
+ /* happens with "external" libs */
+ LOGV("Link of class '%s' failed\n", descriptor);
+ } else {
+ LOGW("Link of class '%s' failed\n", descriptor);
+ }
+ goto bail;
+ }
+ dvmObjectNotifyAll(self, (Object*) clazz);
+ dvmUnlockObject(self, (Object*) clazz);
+
+ /*
+ * Add class stats to global counters.
+ *
+ * TODO: these should probably be atomic ops.
+ */
+ gDvm.numLoadedClasses++;
+ gDvm.numDeclaredMethods +=
+ clazz->virtualMethodCount + clazz->directMethodCount;
+ gDvm.numDeclaredInstFields += clazz->ifieldCount;
+ gDvm.numDeclaredStaticFields += clazz->sfieldCount;
+
+ /*
+ * Cache pointers to basic classes. We want to use these in
+ * various places, and it's easiest to initialize them on first
+ * use rather than trying to force them to initialize (startup
+ * ordering makes it weird).
+ */
+ if (gDvm.classJavaLangObject == NULL &&
+ strcmp(descriptor, "Ljava/lang/Object;") == 0)
+ {
+ /* It should be impossible to get here with anything
+ * but the bootclasspath loader.
+ */
+ assert(loader == NULL);
+ gDvm.classJavaLangObject = clazz;
+ }
+
+#if LOG_CLASS_LOADING
+ logClassLoad('<', clazz);
+#endif
+
+ } else {
+got_class:
+ if (!dvmIsClassLinked(clazz) && clazz->status != CLASS_ERROR) {
+ /*
+ * We can race with other threads for class linking. We should
+ * never get here recursively; doing so indicates that two
+ * classes have circular dependencies.
+ *
+ * One exception: we force discovery of java.lang.Class in
+ * dvmLinkClass(), and Class has Object as its superclass. So
+ * if the first thing we ever load is Object, we will init
+ * Object->Class->Object. The easiest way to avoid this is to
+ * ensure that Object is never the first thing we look up, so
+ * we get Foo->Class->Object instead.
+ */
+ dvmLockObject(self, (Object*) clazz);
+ if (!dvmIsClassLinked(clazz) &&
+ clazz->initThreadId == self->threadId)
+ {
+ LOGW("Recursive link on class %s\n", clazz->descriptor);
+ dvmUnlockObject(self, (Object*) clazz);
+ dvmThrowExceptionWithClassMessage(
+ "Ljava/lang/ClassCircularityError;", clazz->descriptor);
+ clazz = NULL;
+ goto bail;
+ }
+ //LOGI("WAITING for '%s' (owner=%d)\n",
+ // clazz->descriptor, clazz->initThreadId);
+ while (!dvmIsClassLinked(clazz) && clazz->status != CLASS_ERROR) {
+ dvmObjectWait(self, (Object*) clazz, 0, 0, false);
+ }
+ dvmUnlockObject(self, (Object*) clazz);
+ }
+ if (clazz->status == CLASS_ERROR) {
+ /*
+ * Somebody else tried to load this and failed. We need to raise
+ * an exception and report failure.
+ */
+ throwEarlierClassFailure(clazz);
+ clazz = NULL;
+ goto bail;
+ }
+ }
+
+ /* check some invariants */
+ assert(dvmIsClassLinked(clazz));
+ assert(gDvm.classJavaLangClass != NULL);
+ assert(clazz->obj.clazz == gDvm.classJavaLangClass);
+ if (clazz != gDvm.classJavaLangObject) {
+ if (clazz->super == NULL) {
+ LOGE("Non-Object has no superclass (gDvm.classJavaLangObject=%p)\n",
+ gDvm.classJavaLangObject);
+ dvmAbort();
+ }
+ }
+ if (!dvmIsInterfaceClass(clazz)) {
+ //LOGI("class=%s vtableCount=%d, virtualMeth=%d\n",
+ // clazz->descriptor, clazz->vtableCount,
+ // clazz->virtualMethodCount);
+ assert(clazz->vtableCount >= clazz->virtualMethodCount);
+ }
+
+ /*
+ * Normally class objects are initialized before we instantiate them,
+ * but we can't do that with java.lang.Class (chicken, meet egg). We
+ * do it explicitly here.
+ *
+ * The verifier could call here to find Class while verifying Class,
+ * so we need to check for CLASS_VERIFYING as well as !initialized.
+ */
+ if (clazz == gDvm.classJavaLangClass && !dvmIsClassInitialized(clazz) &&
+ !(clazz->status == CLASS_VERIFYING))
+ {
+ LOGV("+++ explicitly initializing %s\n", clazz->descriptor);
+ dvmInitClass(clazz);
+ }
+
+bail:
+ if (profilerNotified)
+ dvmMethodTraceClassPrepEnd();
+ assert(clazz != NULL || dvmCheckException(self));
+ return clazz;
+}
+
+/*
+ * Helper for loadClassFromDex, which takes a DexClassDataHeader and
+ * encoded data pointer in addition to the other arguments.
+ */
+static ClassObject* loadClassFromDex0(DvmDex* pDvmDex,
+ const DexClassDef* pClassDef, const DexClassDataHeader* pHeader,
+ const u1* pEncodedData, Object* classLoader)
+{
+ ClassObject* newClass = NULL;
+ const DexFile* pDexFile;
+ const char* descriptor;
+ int i;
+
+ pDexFile = pDvmDex->pDexFile;
+ descriptor = dexGetClassDescriptor(pDexFile, pClassDef);
+
+ /*
+ * Make sure the aren't any "bonus" flags set, since we use them for
+ * runtime state.
+ */
+ if ((pClassDef->accessFlags & ~EXPECTED_FILE_FLAGS) != 0) {
+ LOGW("Invalid file flags in class %s: %04x\n",
+ descriptor, pClassDef->accessFlags);
+ return NULL;
+ }
+
+ /*
+ * Allocate storage for the class object on the GC heap, so that other
+ * objects can have references to it. We bypass the usual mechanism
+ * (allocObject), because we don't have all the bits and pieces yet.
+ *
+ * Note that we assume that java.lang.Class does not override
+ * finalize().
+ */
+ /* TODO: Can there be fewer special checks in the usual path? */
+ assert(descriptor != NULL);
+ if (classLoader == NULL &&
+ strcmp(descriptor, "Ljava/lang/Class;") == 0) {
+ assert(gDvm.classJavaLangClass != NULL);
+ newClass = gDvm.classJavaLangClass;
+ } else {
+ size_t size = classObjectSize(pHeader->staticFieldsSize);
+ newClass = (ClassObject*) dvmMalloc(size, ALLOC_DEFAULT);
+ }
+ if (newClass == NULL)
+ return NULL;
+
+ DVM_OBJECT_INIT(&newClass->obj, gDvm.classJavaLangClass);
+ dvmSetClassSerialNumber(newClass);
+ newClass->descriptor = descriptor;
+ assert(newClass->descriptorAlloc == NULL);
+ newClass->accessFlags = pClassDef->accessFlags;
+ dvmSetFieldObject((Object *)newClass,
+ offsetof(ClassObject, classLoader),
+ (Object *)classLoader);
+ newClass->pDvmDex = pDvmDex;
+ newClass->primitiveType = PRIM_NOT;
+ newClass->status = CLASS_IDX;
+
+ /*
+ * Stuff the superclass index into the object pointer field. The linker
+ * pulls it out and replaces it with a resolved ClassObject pointer.
+ * I'm doing it this way (rather than having a dedicated superclassIdx
+ * field) to save a few bytes of overhead per class.
+ *
+ * newClass->super is not traversed or freed by dvmFreeClassInnards, so
+ * this is safe.
+ */
+ assert(sizeof(u4) == sizeof(ClassObject*)); /* 32-bit check */
+ newClass->super = (ClassObject*) pClassDef->superclassIdx;
+
+ /*
+ * Stuff class reference indices into the pointer fields.
+ *
+ * The elements of newClass->interfaces are not traversed or freed by
+ * dvmFreeClassInnards, so this is GC-safe.
+ */
+ const DexTypeList* pInterfacesList;
+ pInterfacesList = dexGetInterfacesList(pDexFile, pClassDef);
+ if (pInterfacesList != NULL) {
+ newClass->interfaceCount = pInterfacesList->size;
+ newClass->interfaces = (ClassObject**) dvmLinearAlloc(classLoader,
+ newClass->interfaceCount * sizeof(ClassObject*));
+
+ for (i = 0; i < newClass->interfaceCount; i++) {
+ const DexTypeItem* pType = dexGetTypeItem(pInterfacesList, i);
+ newClass->interfaces[i] = (ClassObject*)(u4) pType->typeIdx;
+ }
+ dvmLinearReadOnly(classLoader, newClass->interfaces);
+ }
+
+ /* load field definitions */
+
+ /*
+ * Over-allocate the class object and append static field info
+ * onto the end. It's fixed-size and known at alloc time. This
+ * seems to increase zygote sharing. Heap compaction will have to
+ * be careful if it ever tries to move ClassObject instances,
+ * because we pass Field pointers around internally. But at least
+ * now these Field pointers are in the object heap.
+ */
+
+ if (pHeader->staticFieldsSize != 0) {
+ /* static fields stay on system heap; field data isn't "write once" */
+ int count = (int) pHeader->staticFieldsSize;
+ u4 lastIndex = 0;
+ DexField field;
+
+ newClass->sfieldCount = count;
+ for (i = 0; i < count; i++) {
+ dexReadClassDataField(&pEncodedData, &field, &lastIndex);
+ loadSFieldFromDex(newClass, &field, &newClass->sfields[i]);
+ }
+ }
+
+ if (pHeader->instanceFieldsSize != 0) {
+ int count = (int) pHeader->instanceFieldsSize;
+ u4 lastIndex = 0;
+ DexField field;
+
+ newClass->ifieldCount = count;
+ newClass->ifields = (InstField*) dvmLinearAlloc(classLoader,
+ count * sizeof(InstField));
+ for (i = 0; i < count; i++) {
+ dexReadClassDataField(&pEncodedData, &field, &lastIndex);
+ loadIFieldFromDex(newClass, &field, &newClass->ifields[i]);
+ }
+ dvmLinearReadOnly(classLoader, newClass->ifields);
+ }
+
+ /*
+ * Load method definitions. We do this in two batches, direct then
+ * virtual.
+ *
+ * If register maps have already been generated for this class, and
+ * precise GC is enabled, we pull out pointers to them. We know that
+ * they were streamed to the DEX file in the same order in which the
+ * methods appear.
+ *
+ * If the class wasn't pre-verified, the maps will be generated when
+ * the class is verified during class initialization.
+ */
+ u4 classDefIdx = dexGetIndexForClassDef(pDexFile, pClassDef);
+ const void* classMapData;
+ u4 numMethods;
+
+ if (gDvm.preciseGc) {
+ classMapData =
+ dvmRegisterMapGetClassData(pDexFile, classDefIdx, &numMethods);
+
+ /* sanity check */
+ if (classMapData != NULL &&
+ pHeader->directMethodsSize + pHeader->virtualMethodsSize != numMethods)
+ {
+ LOGE("ERROR: in %s, direct=%d virtual=%d, maps have %d\n",
+ newClass->descriptor, pHeader->directMethodsSize,
+ pHeader->virtualMethodsSize, numMethods);
+ assert(false);
+ classMapData = NULL; /* abandon */
+ }
+ } else {
+ classMapData = NULL;
+ }
+
+ if (pHeader->directMethodsSize != 0) {
+ int count = (int) pHeader->directMethodsSize;
+ u4 lastIndex = 0;
+ DexMethod method;
+
+ newClass->directMethodCount = count;
+ newClass->directMethods = (Method*) dvmLinearAlloc(classLoader,
+ count * sizeof(Method));
+ for (i = 0; i < count; i++) {
+ dexReadClassDataMethod(&pEncodedData, &method, &lastIndex);
+ loadMethodFromDex(newClass, &method, &newClass->directMethods[i]);
+ if (classMapData != NULL) {
+ const RegisterMap* pMap = dvmRegisterMapGetNext(&classMapData);
+ if (dvmRegisterMapGetFormat(pMap) != kRegMapFormatNone) {
+ newClass->directMethods[i].registerMap = pMap;
+ /* TODO: add rigorous checks */
+ assert((newClass->directMethods[i].registersSize+7) / 8 ==
+ newClass->directMethods[i].registerMap->regWidth);
+ }
+ }
+ }
+ dvmLinearReadOnly(classLoader, newClass->directMethods);
+ }
+
+ if (pHeader->virtualMethodsSize != 0) {
+ int count = (int) pHeader->virtualMethodsSize;
+ u4 lastIndex = 0;
+ DexMethod method;
+
+ newClass->virtualMethodCount = count;
+ newClass->virtualMethods = (Method*) dvmLinearAlloc(classLoader,
+ count * sizeof(Method));
+ for (i = 0; i < count; i++) {
+ dexReadClassDataMethod(&pEncodedData, &method, &lastIndex);
+ loadMethodFromDex(newClass, &method, &newClass->virtualMethods[i]);
+ if (classMapData != NULL) {
+ const RegisterMap* pMap = dvmRegisterMapGetNext(&classMapData);
+ if (dvmRegisterMapGetFormat(pMap) != kRegMapFormatNone) {
+ newClass->virtualMethods[i].registerMap = pMap;
+ /* TODO: add rigorous checks */
+ assert((newClass->virtualMethods[i].registersSize+7) / 8 ==
+ newClass->virtualMethods[i].registerMap->regWidth);
+ }
+ }
+ }
+ dvmLinearReadOnly(classLoader, newClass->virtualMethods);
+ }
+
+ newClass->sourceFile = dexGetSourceFile(pDexFile, pClassDef);
+
+ /* caller must call dvmReleaseTrackedAlloc */
+ return newClass;
+}
+
+/*
+ * Try to load the indicated class from the specified DEX file.
+ *
+ * This is effectively loadClass()+defineClass() for a DexClassDef. The
+ * loading was largely done when we crunched through the DEX.
+ *
+ * Returns NULL on failure. If we locate the class but encounter an error
+ * while processing it, an appropriate exception is thrown.
+ */
+static ClassObject* loadClassFromDex(DvmDex* pDvmDex,
+ const DexClassDef* pClassDef, Object* classLoader)
+{
+ ClassObject* result;
+ DexClassDataHeader header;
+ const u1* pEncodedData;
+ const DexFile* pDexFile;
+
+ assert((pDvmDex != NULL) && (pClassDef != NULL));
+ pDexFile = pDvmDex->pDexFile;
+
+ if (gDvm.verboseClass) {
+ LOGV("CLASS: loading '%s'...\n",
+ dexGetClassDescriptor(pDexFile, pClassDef));
+ }
+
+ pEncodedData = dexGetClassData(pDexFile, pClassDef);
+
+ if (pEncodedData != NULL) {
+ dexReadClassDataHeader(&pEncodedData, &header);
+ } else {
+ // Provide an all-zeroes header for the rest of the loading.
+ memset(&header, 0, sizeof(header));
+ }
+
+ result = loadClassFromDex0(pDvmDex, pClassDef, &header, pEncodedData,
+ classLoader);
+
+ if (gDvm.verboseClass && (result != NULL)) {
+ LOGI("[Loaded %s from DEX %p (cl=%p)]\n",
+ result->descriptor, pDvmDex, classLoader);
+ }
+
+ return result;
+}
+
+/*
+ * Free anything in a ClassObject that was allocated on the system heap.
+ *
+ * The ClassObject itself is allocated on the GC heap, so we leave it for
+ * the garbage collector.
+ *
+ * NOTE: this may be called with a partially-constructed object.
+ * NOTE: there is no particular ordering imposed, so don't go poking at
+ * superclasses.
+ */
+void dvmFreeClassInnards(ClassObject* clazz)
+{
+ void *tp;
+ int i;
+
+ if (clazz == NULL)
+ return;
+
+ assert(clazz->obj.clazz == gDvm.classJavaLangClass);
+
+ /* Guarantee that dvmFreeClassInnards can be called on a given
+ * class multiple times by clearing things out as we free them.
+ * We don't make any attempt at real atomicity here; higher
+ * levels need to make sure that no two threads can free the
+ * same ClassObject at the same time.
+ *
+ * TODO: maybe just make it so the GC will never free the
+ * innards of an already-freed class.
+ *
+ * TODO: this #define isn't MT-safe -- the compiler could rearrange it.
+ */
+#define NULL_AND_FREE(p) \
+ do { \
+ if ((p) != NULL) { \
+ tp = (p); \
+ (p) = NULL; \
+ free(tp); \
+ } \
+ } while (0)
+#define NULL_AND_LINEAR_FREE(p) \
+ do { \
+ if ((p) != NULL) { \
+ tp = (p); \
+ (p) = NULL; \
+ dvmLinearFree(clazz->classLoader, tp); \
+ } \
+ } while (0)
+
+ /* arrays just point at Object's vtable; don't free vtable in this case.
+ */
+ clazz->vtableCount = -1;
+ if (clazz->vtable == gDvm.classJavaLangObject->vtable) {
+ clazz->vtable = NULL;
+ } else {
+ NULL_AND_LINEAR_FREE(clazz->vtable);
+ }
+
+ clazz->descriptor = NULL;
+ NULL_AND_FREE(clazz->descriptorAlloc);
+
+ if (clazz->directMethods != NULL) {
+ Method *directMethods = clazz->directMethods;
+ int directMethodCount = clazz->directMethodCount;
+ clazz->directMethods = NULL;
+ clazz->directMethodCount = -1;
+ dvmLinearReadWrite(clazz->classLoader, directMethods);
+ for (i = 0; i < directMethodCount; i++) {
+ freeMethodInnards(&directMethods[i]);
+ }
+ dvmLinearReadOnly(clazz->classLoader, directMethods);
+ dvmLinearFree(clazz->classLoader, directMethods);
+ }
+ if (clazz->virtualMethods != NULL) {
+ Method *virtualMethods = clazz->virtualMethods;
+ int virtualMethodCount = clazz->virtualMethodCount;
+ clazz->virtualMethodCount = -1;
+ clazz->virtualMethods = NULL;
+ dvmLinearReadWrite(clazz->classLoader, virtualMethods);
+ for (i = 0; i < virtualMethodCount; i++) {
+ freeMethodInnards(&virtualMethods[i]);
+ }
+ dvmLinearReadOnly(clazz->classLoader, virtualMethods);
+ dvmLinearFree(clazz->classLoader, virtualMethods);
+ }
+
+ InitiatingLoaderList *loaderList = dvmGetInitiatingLoaderList(clazz);
+ loaderList->initiatingLoaderCount = -1;
+ NULL_AND_FREE(loaderList->initiatingLoaders);
+
+ clazz->interfaceCount = -1;
+ NULL_AND_LINEAR_FREE(clazz->interfaces);
+
+ clazz->iftableCount = -1;
+ NULL_AND_LINEAR_FREE(clazz->iftable);
+
+ clazz->ifviPoolCount = -1;
+ NULL_AND_LINEAR_FREE(clazz->ifviPool);
+
+ clazz->sfieldCount = -1;
+ /* The sfields are attached to the ClassObject, and will be freed
+ * with it. */
+
+ clazz->ifieldCount = -1;
+ NULL_AND_LINEAR_FREE(clazz->ifields);
+
+#undef NULL_AND_FREE
+#undef NULL_AND_LINEAR_FREE
+}
+
+/*
+ * Free anything in a Method that was allocated on the system heap.
+ *
+ * The containing class is largely torn down by this point.
+ */
+static void freeMethodInnards(Method* meth)
+{
+#if 0
+ free(meth->exceptions);
+ free(meth->lines);
+ free(meth->locals);
+#endif
+
+ /*
+ * Some register maps are allocated on the heap, either because of late
+ * verification or because we're caching an uncompressed form.
+ */
+ const RegisterMap* pMap = meth->registerMap;
+ if (pMap != NULL && dvmRegisterMapGetOnHeap(pMap)) {
+ dvmFreeRegisterMap((RegisterMap*) pMap);
+ meth->registerMap = NULL;
+ }
+
+ /*
+ * We may have copied the instructions.
+ */
+ if (IS_METHOD_FLAG_SET(meth, METHOD_ISWRITABLE)) {
+ DexCode* methodDexCode = (DexCode*) dvmGetMethodCode(meth);
+ dvmLinearFree(meth->clazz->classLoader, methodDexCode);
+ }
+}
+
+/*
+ * Clone a Method, making new copies of anything that will be freed up
+ * by freeMethodInnards(). This is used for "miranda" methods.
+ */
+static void cloneMethod(Method* dst, const Method* src)
+{
+ if (src->registerMap != NULL) {
+ LOGE("GLITCH: only expected abstract methods here\n");
+ LOGE(" cloning %s.%s\n", src->clazz->descriptor, src->name);
+ dvmAbort();
+ }
+ memcpy(dst, src, sizeof(Method));
+}
+
+/*
+ * Pull the interesting pieces out of a DexMethod.
+ *
+ * The DEX file isn't going anywhere, so we don't need to make copies of
+ * the code area.
+ */
+static void loadMethodFromDex(ClassObject* clazz, const DexMethod* pDexMethod,
+ Method* meth)
+{
+ DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+ const DexMethodId* pMethodId;
+ const DexCode* pDexCode;
+
+ pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
+
+ meth->name = dexStringById(pDexFile, pMethodId->nameIdx);
+ dexProtoSetFromMethodId(&meth->prototype, pDexFile, pMethodId);
+ meth->shorty = dexProtoGetShorty(&meth->prototype);
+ meth->accessFlags = pDexMethod->accessFlags;
+ meth->clazz = clazz;
+ meth->jniArgInfo = 0;
+
+ if (dvmCompareNameDescriptorAndMethod("finalize", "()V", meth) == 0) {
+ SET_CLASS_FLAG(clazz, CLASS_ISFINALIZABLE);
+ }
+
+ pDexCode = dexGetCode(pDexFile, pDexMethod);
+ if (pDexCode != NULL) {
+ /* integer constants, copy over for faster access */
+ meth->registersSize = pDexCode->registersSize;
+ meth->insSize = pDexCode->insSize;
+ meth->outsSize = pDexCode->outsSize;
+
+ /* pointer to code area */
+ meth->insns = pDexCode->insns;
+ } else {
+ /*
+ * We don't have a DexCode block, but we still want to know how
+ * much space is needed for the arguments (so we don't have to
+ * compute it later). We also take this opportunity to compute
+ * JNI argument info.
+ *
+ * We do this for abstract methods as well, because we want to
+ * be able to substitute our exception-throwing "stub" in.
+ */
+ int argsSize = dvmComputeMethodArgsSize(meth);
+ if (!dvmIsStaticMethod(meth))
+ argsSize++;
+ meth->registersSize = meth->insSize = argsSize;
+ assert(meth->outsSize == 0);
+ assert(meth->insns == NULL);
+
+ if (dvmIsNativeMethod(meth)) {
+ meth->nativeFunc = dvmResolveNativeMethod;
+ meth->jniArgInfo = computeJniArgInfo(&meth->prototype);
+ }
+ }
+}
+
+#if 0 /* replaced with private/read-write mapping */
+/*
+ * We usually map bytecode directly out of the DEX file, which is mapped
+ * shared read-only. If we want to be able to modify it, we have to make
+ * a new copy.
+ *
+ * Once copied, the code will be in the LinearAlloc region, which may be
+ * marked read-only.
+ *
+ * The bytecode instructions are embedded inside a DexCode structure, so we
+ * need to copy all of that. (The dvmGetMethodCode function backs up the
+ * instruction pointer to find the start of the DexCode.)
+ */
+void dvmMakeCodeReadWrite(Method* meth)
+{
+ DexCode* methodDexCode = (DexCode*) dvmGetMethodCode(meth);
+
+ if (IS_METHOD_FLAG_SET(meth, METHOD_ISWRITABLE)) {
+ dvmLinearReadWrite(meth->clazz->classLoader, methodDexCode);
+ return;
+ }
+
+ assert(!dvmIsNativeMethod(meth) && !dvmIsAbstractMethod(meth));
+
+ size_t dexCodeSize = dexGetDexCodeSize(methodDexCode);
+ LOGD("Making a copy of %s.%s code (%d bytes)\n",
+ meth->clazz->descriptor, meth->name, dexCodeSize);
+
+ DexCode* newCode =
+ (DexCode*) dvmLinearAlloc(meth->clazz->classLoader, dexCodeSize);
+ memcpy(newCode, methodDexCode, dexCodeSize);
+
+ meth->insns = newCode->insns;
+ SET_METHOD_FLAG(meth, METHOD_ISWRITABLE);
+}
+
+/*
+ * Mark the bytecode read-only.
+ *
+ * If the contents of the DexCode haven't actually changed, we could revert
+ * to the original shared page.
+ */
+void dvmMakeCodeReadOnly(Method* meth)
+{
+ DexCode* methodDexCode = (DexCode*) dvmGetMethodCode(meth);
+ LOGV("+++ marking %p read-only\n", methodDexCode);
+ dvmLinearReadOnly(meth->clazz->classLoader, methodDexCode);
+}
+#endif
+
+
+/*
+ * jniArgInfo (32-bit int) layout:
+ * SRRRHHHH HHHHHHHH HHHHHHHH HHHHHHHH
+ *
+ * S - if set, do things the hard way (scan the signature)
+ * R - return-type enumeration
+ * H - target-specific hints
+ *
+ * This info is used at invocation time by dvmPlatformInvoke. In most
+ * cases, the target-specific hints allow dvmPlatformInvoke to avoid
+ * having to fully parse the signature.
+ *
+ * The return-type bits are always set, even if target-specific hint bits
+ * are unavailable.
+ */
+static int computeJniArgInfo(const DexProto* proto)
+{
+ const char* sig = dexProtoGetShorty(proto);
+ int returnType, jniArgInfo;
+ u4 hints;
+
+ /* The first shorty character is the return type. */
+ switch (*(sig++)) {
+ case 'V':
+ returnType = DALVIK_JNI_RETURN_VOID;
+ break;
+ case 'F':
+ returnType = DALVIK_JNI_RETURN_FLOAT;
+ break;
+ case 'D':
+ returnType = DALVIK_JNI_RETURN_DOUBLE;
+ break;
+ case 'J':
+ returnType = DALVIK_JNI_RETURN_S8;
+ break;
+ case 'Z':
+ case 'B':
+ returnType = DALVIK_JNI_RETURN_S1;
+ break;
+ case 'C':
+ returnType = DALVIK_JNI_RETURN_U2;
+ break;
+ case 'S':
+ returnType = DALVIK_JNI_RETURN_S2;
+ break;
+ default:
+ returnType = DALVIK_JNI_RETURN_S4;
+ break;
+ }
+
+ jniArgInfo = returnType << DALVIK_JNI_RETURN_SHIFT;
+
+ hints = dvmPlatformInvokeHints(proto);
+
+ if (hints & DALVIK_JNI_NO_ARG_INFO) {
+ jniArgInfo |= DALVIK_JNI_NO_ARG_INFO;
+ } else {
+ assert((hints & DALVIK_JNI_RETURN_MASK) == 0);
+ jniArgInfo |= hints;
+ }
+
+ return jniArgInfo;
+}
+
+/*
+ * Load information about a static field.
+ *
+ * This also "prepares" static fields by initializing them
+ * to their "standard default values".
+ */
+static void loadSFieldFromDex(ClassObject* clazz,
+ const DexField* pDexSField, StaticField* sfield)
+{
+ DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+ const DexFieldId* pFieldId;
+
+ pFieldId = dexGetFieldId(pDexFile, pDexSField->fieldIdx);
+
+ sfield->field.clazz = clazz;
+ sfield->field.name = dexStringById(pDexFile, pFieldId->nameIdx);
+ sfield->field.signature = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
+ sfield->field.accessFlags = pDexSField->accessFlags;
+
+ /* Static object field values are set to "standard default values"
+ * (null or 0) until the class is initialized. We delay loading
+ * constant values from the class until that time.
+ */
+ //sfield->value.j = 0;
+ assert(sfield->value.j == 0LL); // cleared earlier with calloc
+
+#ifdef PROFILE_FIELD_ACCESS
+ sfield->field.gets = sfield->field.puts = 0;
+#endif
+}
+
+/*
+ * Load information about an instance field.
+ */
+static void loadIFieldFromDex(ClassObject* clazz,
+ const DexField* pDexIField, InstField* ifield)
+{
+ DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+ const DexFieldId* pFieldId;
+
+ pFieldId = dexGetFieldId(pDexFile, pDexIField->fieldIdx);
+
+ ifield->field.clazz = clazz;
+ ifield->field.name = dexStringById(pDexFile, pFieldId->nameIdx);
+ ifield->field.signature = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
+ ifield->field.accessFlags = pDexIField->accessFlags;
+#ifndef NDEBUG
+ assert(ifield->byteOffset == 0); // cleared earlier with calloc
+ ifield->byteOffset = -1; // make it obvious if we fail to set later
+#endif
+
+#ifdef PROFILE_FIELD_ACCESS
+ ifield->field.gets = ifield->field.puts = 0;
+#endif
+}
+
+/*
+ * Cache java.lang.ref.Reference fields and methods.
+ */
+static bool precacheReferenceOffsets(ClassObject* clazz)
+{
+ Method *meth;
+ int i;
+
+ /* We trick the GC object scanner by not counting
+ * java.lang.ref.Reference.referent as an object
+ * field. It will get explicitly scanned as part
+ * of the reference-walking process.
+ *
+ * Find the object field named "referent" and put it
+ * just after the list of object reference fields.
+ */
+ dvmLinearReadWrite(clazz->classLoader, clazz->ifields);
+ for (i = 0; i < clazz->ifieldRefCount; i++) {
+ InstField *pField = &clazz->ifields[i];
+ if (strcmp(pField->field.name, "referent") == 0) {
+ int targetIndex;
+
+ /* Swap this field with the last object field.
+ */
+ targetIndex = clazz->ifieldRefCount - 1;
+ if (i != targetIndex) {
+ InstField *swapField = &clazz->ifields[targetIndex];
+ InstField tmpField;
+ int tmpByteOffset;
+
+ /* It's not currently strictly necessary
+ * for the fields to be in byteOffset order,
+ * but it's more predictable that way.
+ */
+ tmpByteOffset = swapField->byteOffset;
+ swapField->byteOffset = pField->byteOffset;
+ pField->byteOffset = tmpByteOffset;
+
+ tmpField = *swapField;
+ *swapField = *pField;
+ *pField = tmpField;
+ }
+
+ /* One fewer object field (wink wink).
+ */
+ clazz->ifieldRefCount--;
+ i--; /* don't trip "didn't find it" test if field was last */
+ break;
+ }
+ }
+ dvmLinearReadOnly(clazz->classLoader, clazz->ifields);
+ if (i == clazz->ifieldRefCount) {
+ LOGE("Unable to reorder 'referent' in %s\n", clazz->descriptor);
+ return false;
+ }
+
+ /* Cache pretty much everything about Reference so that
+ * we don't need to call interpreted code when clearing/enqueueing
+ * references. This is fragile, so we'll be paranoid.
+ */
+ gDvm.classJavaLangRefReference = clazz;
+
+ gDvm.offJavaLangRefReference_referent =
+ dvmFindFieldOffset(gDvm.classJavaLangRefReference,
+ "referent", "Ljava/lang/Object;");
+ assert(gDvm.offJavaLangRefReference_referent >= 0);
+
+ gDvm.offJavaLangRefReference_queue =
+ dvmFindFieldOffset(gDvm.classJavaLangRefReference,
+ "queue", "Ljava/lang/ref/ReferenceQueue;");
+ assert(gDvm.offJavaLangRefReference_queue >= 0);
+
+ gDvm.offJavaLangRefReference_queueNext =
+ dvmFindFieldOffset(gDvm.classJavaLangRefReference,
+ "queueNext", "Ljava/lang/ref/Reference;");
+ assert(gDvm.offJavaLangRefReference_queueNext >= 0);
+
+ gDvm.offJavaLangRefReference_pendingNext =
+ dvmFindFieldOffset(gDvm.classJavaLangRefReference,
+ "pendingNext", "Ljava/lang/ref/Reference;");
+ assert(gDvm.offJavaLangRefReference_pendingNext >= 0);
+
+ /* enqueueInternal() is private and thus a direct method. */
+ meth = dvmFindDirectMethodByDescriptor(clazz, "enqueueInternal", "()Z");
+ assert(meth != NULL);
+ gDvm.methJavaLangRefReference_enqueueInternal = meth;
+
+ return true;
+}
+
+
+/*
+ * Set the bitmap of reference offsets, refOffsets, from the ifields
+ * list.
+ */
+static void computeRefOffsets(ClassObject* clazz)
+{
+ if (clazz->super != NULL) {
+ clazz->refOffsets = clazz->super->refOffsets;
+ } else {
+ clazz->refOffsets = 0;
+ }
+ /*
+ * If our superclass overflowed, we don't stand a chance.
+ */
+ if (clazz->refOffsets != CLASS_WALK_SUPER) {
+ InstField *f;
+ int i;
+
+ /* All of the fields that contain object references
+ * are guaranteed to be at the beginning of the ifields list.
+ */
+ f = clazz->ifields;
+ const int ifieldRefCount = clazz->ifieldRefCount;
+ for (i = 0; i < ifieldRefCount; i++) {
+ /*
+ * Note that, per the comment on struct InstField,
+ * f->byteOffset is the offset from the beginning of
+ * obj, not the offset into obj->instanceData.
+ */
+ assert(f->byteOffset >= (int) CLASS_SMALLEST_OFFSET);
+ assert((f->byteOffset & (CLASS_OFFSET_ALIGNMENT - 1)) == 0);
+ if (CLASS_CAN_ENCODE_OFFSET(f->byteOffset)) {
+ u4 newBit = CLASS_BIT_FROM_OFFSET(f->byteOffset);
+ assert(newBit != 0);
+ clazz->refOffsets |= newBit;
+ } else {
+ clazz->refOffsets = CLASS_WALK_SUPER;
+ break;
+ }
+ f++;
+ }
+ }
+}
+
+
+/*
+ * Link (prepare and resolve). Verification is deferred until later.
+ *
+ * This converts symbolic references into pointers. It's independent of
+ * the source file format.
+ *
+ * If clazz->status is CLASS_IDX, then clazz->super and interfaces[] are
+ * holding class reference indices rather than pointers. The class
+ * references will be resolved during link. (This is done when
+ * loading from DEX to avoid having to create additional storage to
+ * pass the indices around.)
+ *
+ * Returns "false" with an exception pending on failure.
+ */
+bool dvmLinkClass(ClassObject* clazz)
+{
+ u4 superclassIdx = 0;
+ u4 *interfaceIdxArray = NULL;
+ bool okay = false;
+ int i;
+
+ assert(clazz != NULL);
+ assert(clazz->descriptor != NULL);
+ assert(clazz->status == CLASS_IDX || clazz->status == CLASS_LOADED);
+ if (gDvm.verboseClass)
+ LOGV("CLASS: linking '%s'...\n", clazz->descriptor);
+
+ assert(gDvm.classJavaLangClass != NULL);
+ assert(clazz->obj.clazz == gDvm.classJavaLangClass);
+ if (clazz->classLoader == NULL &&
+ (strcmp(clazz->descriptor, "Ljava/lang/Class;") == 0))
+ {
+ if (gDvm.classJavaLangClass->ifieldCount > CLASS_FIELD_SLOTS) {
+ LOGE("java.lang.Class has %d instance fields (expected at most %d)",
+ gDvm.classJavaLangClass->ifieldCount, CLASS_FIELD_SLOTS);
+ dvmAbort();
+ }
+ if (gDvm.classJavaLangClass->sfieldCount != CLASS_SFIELD_SLOTS) {
+ LOGE("java.lang.Class has %d static fields (expected %d)",
+ gDvm.classJavaLangClass->sfieldCount, CLASS_SFIELD_SLOTS);
+ dvmAbort();
+ }
+ }
+ /* "Resolve" the class.
+ *
+ * At this point, clazz's reference fields may contain Dex file
+ * indices instead of direct object references. Proxy objects are
+ * an exception, and may be the only exception. We need to
+ * translate those indices into real references, and let the GC
+ * look inside this ClassObject.
+ */
+ if (clazz->status == CLASS_IDX) {
+ if (clazz->interfaceCount > 0) {
+ /* Copy u4 DEX idx values out of the ClassObject* array
+ * where we stashed them.
+ */
+ assert(sizeof(*interfaceIdxArray) == sizeof(*clazz->interfaces));
+ size_t len = clazz->interfaceCount * sizeof(*interfaceIdxArray);
+ interfaceIdxArray = malloc(len);
+ if (interfaceIdxArray == NULL) {
+ LOGW("Unable to allocate memory to link %s", clazz->descriptor);
+ goto bail;
+ }
+ memcpy(interfaceIdxArray, clazz->interfaces, len);
+
+ dvmLinearReadWrite(clazz->classLoader, clazz->interfaces);
+ memset(clazz->interfaces, 0, len);
+ dvmLinearReadOnly(clazz->classLoader, clazz->interfaces);
+ }
+
+ assert(sizeof(superclassIdx) == sizeof(clazz->super));
+ superclassIdx = (u4) clazz->super;
+ clazz->super = NULL;
+ /* After this line, clazz will be fair game for the GC. The
+ * superclass and interfaces are all NULL.
+ */
+ clazz->status = CLASS_LOADED;
+
+ if (superclassIdx != kDexNoIndex) {
+ ClassObject* super = dvmResolveClass(clazz, superclassIdx, false);
+ if (super == NULL) {
+ assert(dvmCheckException(dvmThreadSelf()));
+ if (gDvm.optimizing) {
+ /* happens with "external" libs */
+ LOGV("Unable to resolve superclass of %s (%d)\n",
+ clazz->descriptor, superclassIdx);
+ } else {
+ LOGW("Unable to resolve superclass of %s (%d)\n",
+ clazz->descriptor, superclassIdx);
+ }
+ goto bail;
+ }
+ dvmSetFieldObject((Object *)clazz,
+ offsetof(ClassObject, super),
+ (Object *)super);
+ }
+
+ if (clazz->interfaceCount > 0) {
+ /* Resolve the interfaces implemented directly by this class. */
+ assert(interfaceIdxArray != NULL);
+ dvmLinearReadWrite(clazz->classLoader, clazz->interfaces);
+ for (i = 0; i < clazz->interfaceCount; i++) {
+ assert(interfaceIdxArray[i] != kDexNoIndex);
+ clazz->interfaces[i] =
+ dvmResolveClass(clazz, interfaceIdxArray[i], false);
+ if (clazz->interfaces[i] == NULL) {
+ const DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+
+ assert(dvmCheckException(dvmThreadSelf()));
+ dvmLinearReadOnly(clazz->classLoader, clazz->interfaces);
+
+ const char* classDescriptor;
+ classDescriptor =
+ dexStringByTypeIdx(pDexFile, interfaceIdxArray[i]);
+ if (gDvm.optimizing) {
+ /* happens with "external" libs */
+ LOGV("Failed resolving %s interface %d '%s'\n",
+ clazz->descriptor, interfaceIdxArray[i],
+ classDescriptor);
+ } else {
+ LOGI("Failed resolving %s interface %d '%s'\n",
+ clazz->descriptor, interfaceIdxArray[i],
+ classDescriptor);
+ }
+ goto bail;
+ }
+
+ /* are we allowed to implement this interface? */
+ if (!dvmCheckClassAccess(clazz, clazz->interfaces[i])) {
+ dvmLinearReadOnly(clazz->classLoader, clazz->interfaces);
+ LOGW("Interface '%s' is not accessible to '%s'\n",
+ clazz->interfaces[i]->descriptor, clazz->descriptor);
+ dvmThrowException("Ljava/lang/IllegalAccessError;",
+ "interface not accessible");
+ goto bail;
+ }
+ LOGVV("+++ found interface '%s'\n",
+ clazz->interfaces[i]->descriptor);
+ }
+ dvmLinearReadOnly(clazz->classLoader, clazz->interfaces);
+ }
+ }
+ /*
+ * There are now Class references visible to the GC in super and
+ * interfaces.
+ */
+
+ /*
+ * All classes have a direct superclass, except for
+ * java/lang/Object and primitive classes. Primitive classes are
+ * are created CLASS_INITIALIZED, so won't get here.
+ */
+ assert(clazz->primitiveType == PRIM_NOT);
+ if (strcmp(clazz->descriptor, "Ljava/lang/Object;") == 0) {
+ if (clazz->super != NULL) {
+ /* TODO: is this invariant true for all java/lang/Objects,
+ * regardless of the class loader? For now, assume it is.
+ */
+ dvmThrowException("Ljava/lang/ClassFormatError;",
+ "java.lang.Object has a superclass");
+ goto bail;
+ }
+
+ /* Don't finalize objects whose classes use the
+ * default (empty) Object.finalize().
+ */
+ CLEAR_CLASS_FLAG(clazz, CLASS_ISFINALIZABLE);
+ } else {
+ if (clazz->super == NULL) {
+ dvmThrowException("Ljava/lang/LinkageError;",
+ "no superclass defined");
+ goto bail;
+ }
+ /* verify */
+ if (dvmIsFinalClass(clazz->super)) {
+ LOGW("Superclass of '%s' is final '%s'\n",
+ clazz->descriptor, clazz->super->descriptor);
+ dvmThrowException("Ljava/lang/IncompatibleClassChangeError;",
+ "superclass is final");
+ goto bail;
+ } else if (dvmIsInterfaceClass(clazz->super)) {
+ LOGW("Superclass of '%s' is interface '%s'\n",
+ clazz->descriptor, clazz->super->descriptor);
+ dvmThrowException("Ljava/lang/IncompatibleClassChangeError;",
+ "superclass is an interface");
+ goto bail;
+ } else if (!dvmCheckClassAccess(clazz, clazz->super)) {
+ LOGW("Superclass of '%s' (%s) is not accessible\n",
+ clazz->descriptor, clazz->super->descriptor);
+ dvmThrowException("Ljava/lang/IllegalAccessError;",
+ "superclass not accessible");
+ goto bail;
+ }
+
+ /* Inherit finalizability from the superclass. If this
+ * class also overrides finalize(), its CLASS_ISFINALIZABLE
+ * bit will already be set.
+ */
+ if (IS_CLASS_FLAG_SET(clazz->super, CLASS_ISFINALIZABLE)) {
+ SET_CLASS_FLAG(clazz, CLASS_ISFINALIZABLE);
+ }
+
+ /* See if this class descends from java.lang.Reference
+ * and set the class flags appropriately.
+ */
+ if (IS_CLASS_FLAG_SET(clazz->super, CLASS_ISREFERENCE)) {
+ u4 superRefFlags;
+
+ /* We've already determined the reference type of this
+ * inheritance chain. Inherit reference-ness from the superclass.
+ */
+ superRefFlags = GET_CLASS_FLAG_GROUP(clazz->super,
+ CLASS_ISREFERENCE |
+ CLASS_ISWEAKREFERENCE |
+ CLASS_ISPHANTOMREFERENCE);
+ SET_CLASS_FLAG(clazz, superRefFlags);
+ } else if (clazz->classLoader == NULL &&
+ clazz->super->classLoader == NULL &&
+ strcmp(clazz->super->descriptor,
+ "Ljava/lang/ref/Reference;") == 0)
+ {
+ u4 refFlags;
+
+ /* This class extends Reference, which means it should
+ * be one of the magic Soft/Weak/PhantomReference classes.
+ */
+ refFlags = CLASS_ISREFERENCE;
+ if (strcmp(clazz->descriptor,
+ "Ljava/lang/ref/SoftReference;") == 0)
+ {
+ /* Only CLASS_ISREFERENCE is set for soft references.
+ */
+ } else if (strcmp(clazz->descriptor,
+ "Ljava/lang/ref/WeakReference;") == 0)
+ {
+ refFlags |= CLASS_ISWEAKREFERENCE;
+ } else if (strcmp(clazz->descriptor,
+ "Ljava/lang/ref/PhantomReference;") == 0)
+ {
+ refFlags |= CLASS_ISPHANTOMREFERENCE;
+ } else {
+ /* No-one else is allowed to inherit directly
+ * from Reference.
+ */
+//xxx is this the right exception? better than an assertion.
+ dvmThrowException("Ljava/lang/LinkageError;",
+ "illegal inheritance from Reference");
+ goto bail;
+ }
+
+ /* The class should not have any reference bits set yet.
+ */
+ assert(GET_CLASS_FLAG_GROUP(clazz,
+ CLASS_ISREFERENCE |
+ CLASS_ISWEAKREFERENCE |
+ CLASS_ISPHANTOMREFERENCE) == 0);
+
+ SET_CLASS_FLAG(clazz, refFlags);
+ }
+ }
+
+ /*
+ * Populate vtable.
+ */
+ if (dvmIsInterfaceClass(clazz)) {
+ /* no vtable; just set the method indices */
+ int count = clazz->virtualMethodCount;
+
+ if (count != (u2) count) {
+ LOGE("Too many methods (%d) in interface '%s'\n", count,
+ clazz->descriptor);
+ goto bail;
+ }
+
+ dvmLinearReadWrite(clazz->classLoader, clazz->virtualMethods);
+
+ for (i = 0; i < count; i++)
+ clazz->virtualMethods[i].methodIndex = (u2) i;
+
+ dvmLinearReadOnly(clazz->classLoader, clazz->virtualMethods);
+ } else {
+ if (!createVtable(clazz)) {
+ LOGW("failed creating vtable\n");
+ goto bail;
+ }
+ }
+
+ /*
+ * Populate interface method tables. Can alter the vtable.
+ */
+ if (!createIftable(clazz))
+ goto bail;
+
+ /*
+ * Insert special-purpose "stub" method implementations.
+ */
+ if (!insertMethodStubs(clazz))
+ goto bail;
+
+ /*
+ * Compute instance field offsets and, hence, the size of the object.
+ */
+ if (!computeFieldOffsets(clazz))
+ goto bail;
+
+ /*
+ * Cache fields and methods from java/lang/ref/Reference and
+ * java/lang/Class. This has to happen after computeFieldOffsets().
+ */
+ if (clazz->classLoader == NULL) {
+ if (strcmp(clazz->descriptor, "Ljava/lang/ref/Reference;") == 0) {
+ if (!precacheReferenceOffsets(clazz)) {
+ LOGE("failed pre-caching Reference offsets\n");
+ dvmThrowException("Ljava/lang/InternalError;", NULL);
+ goto bail;
+ }
+ } else if (clazz == gDvm.classJavaLangClass) {
+ gDvm.offJavaLangClass_pd = dvmFindFieldOffset(clazz, "pd",
+ "Ljava/security/ProtectionDomain;");
+ if (gDvm.offJavaLangClass_pd <= 0) {
+ LOGE("ERROR: unable to find 'pd' field in Class\n");
+ dvmAbort(); /* we're not going to get much farther */
+ }
+ }
+ }
+
+ /*
+ * Compact the offsets the GC has to examine into a bitmap, if
+ * possible. (This has to happen after Reference.referent is
+ * massaged in precacheReferenceOffsets.)
+ */
+ computeRefOffsets(clazz);
+
+ /*
+ * Done!
+ */
+ if (IS_CLASS_FLAG_SET(clazz, CLASS_ISPREVERIFIED))
+ clazz->status = CLASS_VERIFIED;
+ else
+ clazz->status = CLASS_RESOLVED;
+ okay = true;
+ if (gDvm.verboseClass)
+ LOGV("CLASS: linked '%s'\n", clazz->descriptor);
+
+ /*
+ * We send CLASS_PREPARE events to the debugger from here. The
+ * definition of "preparation" is creating the static fields for a
+ * class and initializing them to the standard default values, but not
+ * executing any code (that comes later, during "initialization").
+ *
+ * We did the static prep in loadSFieldFromDex() while loading the class.
+ *
+ * The class has been prepared and resolved but possibly not yet verified
+ * at this point.
+ */
+ if (gDvm.debuggerActive) {
+ dvmDbgPostClassPrepare(clazz);
+ }
+
+bail:
+ if (!okay) {
+ clazz->status = CLASS_ERROR;
+ if (!dvmCheckException(dvmThreadSelf())) {
+ dvmThrowException("Ljava/lang/VirtualMachineError;", NULL);
+ }
+ }
+ if (interfaceIdxArray != NULL) {
+ free(interfaceIdxArray);
+ }
+ return okay;
+}
+
+/*
+ * Create the virtual method table.
+ *
+ * The top part of the table is a copy of the table from our superclass,
+ * with our local methods overriding theirs. The bottom part of the table
+ * has any new methods we defined.
+ */
+static bool createVtable(ClassObject* clazz)
+{
+ bool result = false;
+ int maxCount;
+ int i;
+
+ if (clazz->super != NULL) {
+ //LOGI("SUPER METHODS %d %s->%s\n", clazz->super->vtableCount,
+ // clazz->descriptor, clazz->super->descriptor);
+ }
+
+ /* the virtual methods we define, plus the superclass vtable size */
+ maxCount = clazz->virtualMethodCount;
+ if (clazz->super != NULL) {
+ maxCount += clazz->super->vtableCount;
+ } else {
+ /* TODO: is this invariant true for all java/lang/Objects,
+ * regardless of the class loader? For now, assume it is.
+ */
+ assert(strcmp(clazz->descriptor, "Ljava/lang/Object;") == 0);
+ }
+ //LOGD("+++ max vmethods for '%s' is %d\n", clazz->descriptor, maxCount);
+
+ /*
+ * Over-allocate the table, then realloc it down if necessary. So
+ * long as we don't allocate anything in between we won't cause
+ * fragmentation, and reducing the size should be unlikely to cause
+ * a buffer copy.
+ */
+ dvmLinearReadWrite(clazz->classLoader, clazz->virtualMethods);
+ clazz->vtable = (Method**) dvmLinearAlloc(clazz->classLoader,
+ sizeof(Method*) * maxCount);
+ if (clazz->vtable == NULL)
+ goto bail;
+
+ if (clazz->super != NULL) {
+ int actualCount;
+
+ memcpy(clazz->vtable, clazz->super->vtable,
+ sizeof(*(clazz->vtable)) * clazz->super->vtableCount);
+ actualCount = clazz->super->vtableCount;
+
+ /*
+ * See if any of our virtual methods override the superclass.
+ */
+ for (i = 0; i < clazz->virtualMethodCount; i++) {
+ Method* localMeth = &clazz->virtualMethods[i];
+ int si;
+
+ for (si = 0; si < clazz->super->vtableCount; si++) {
+ Method* superMeth = clazz->vtable[si];
+
+ if (dvmCompareMethodNamesAndProtos(localMeth, superMeth) == 0)
+ {
+ /* verify */
+ if (dvmIsFinalMethod(superMeth)) {
+ LOGW("Method %s.%s overrides final %s.%s\n",
+ localMeth->clazz->descriptor, localMeth->name,
+ superMeth->clazz->descriptor, superMeth->name);
+ goto bail;
+ }
+ clazz->vtable[si] = localMeth;
+ localMeth->methodIndex = (u2) si;
+ //LOGV("+++ override %s.%s (slot %d)\n",
+ // clazz->descriptor, localMeth->name, si);
+ break;
+ }
+ }
+
+ if (si == clazz->super->vtableCount) {
+ /* not an override, add to end */
+ clazz->vtable[actualCount] = localMeth;
+ localMeth->methodIndex = (u2) actualCount;
+ actualCount++;
+
+ //LOGV("+++ add method %s.%s\n",
+ // clazz->descriptor, localMeth->name);
+ }
+ }
+
+ if (actualCount != (u2) actualCount) {
+ LOGE("Too many methods (%d) in class '%s'\n", actualCount,
+ clazz->descriptor);
+ goto bail;
+ }
+
+ assert(actualCount <= maxCount);
+
+ if (actualCount < maxCount) {
+ assert(clazz->vtable != NULL);
+ dvmLinearReadOnly(clazz->classLoader, clazz->vtable);
+ clazz->vtable = dvmLinearRealloc(clazz->classLoader, clazz->vtable,
+ sizeof(*(clazz->vtable)) * actualCount);
+ if (clazz->vtable == NULL) {
+ LOGE("vtable realloc failed\n");
+ goto bail;
+ } else {
+ LOGVV("+++ reduced vtable from %d to %d\n",
+ maxCount, actualCount);
+ }
+ }
+
+ clazz->vtableCount = actualCount;
+ } else {
+ /* java/lang/Object case */
+ int count = clazz->virtualMethodCount;
+ if (count != (u2) count) {
+ LOGE("Too many methods (%d) in base class '%s'\n", count,
+ clazz->descriptor);
+ goto bail;
+ }
+
+ for (i = 0; i < count; i++) {
+ clazz->vtable[i] = &clazz->virtualMethods[i];
+ clazz->virtualMethods[i].methodIndex = (u2) i;
+ }
+ clazz->vtableCount = clazz->virtualMethodCount;
+ }
+
+ result = true;
+
+bail:
+ dvmLinearReadOnly(clazz->classLoader, clazz->vtable);
+ dvmLinearReadOnly(clazz->classLoader, clazz->virtualMethods);
+ return result;
+}
+
+/*
+ * Create and populate "iftable".
+ *
+ * The set of interfaces we support is the combination of the interfaces
+ * we implement directly and those implemented by our superclass. Each
+ * interface can have one or more "superinterfaces", which we must also
+ * support. For speed we flatten the tree out.
+ *
+ * We might be able to speed this up when there are lots of interfaces
+ * by merge-sorting the class pointers and binary-searching when removing
+ * duplicates. We could also drop the duplicate removal -- it's only
+ * there to reduce the memory footprint.
+ *
+ * Because of "Miranda methods", this may reallocate clazz->virtualMethods.
+ *
+ * Returns "true" on success.
+ */
+static bool createIftable(ClassObject* clazz)
+{
+ bool result = false;
+ bool zapIftable = false;
+ bool zapVtable = false;
+ bool zapIfvipool = false;
+ int ifCount, superIfCount, idx;
+ int i;
+
+ if (clazz->super != NULL)
+ superIfCount = clazz->super->iftableCount;
+ else
+ superIfCount = 0;
+
+ ifCount = superIfCount;
+ ifCount += clazz->interfaceCount;
+ for (i = 0; i < clazz->interfaceCount; i++)
+ ifCount += clazz->interfaces[i]->iftableCount;
+
+ LOGVV("INTF: class '%s' direct w/supra=%d super=%d total=%d\n",
+ clazz->descriptor, ifCount - superIfCount, superIfCount, ifCount);
+
+ if (ifCount == 0) {
+ assert(clazz->iftableCount == 0);
+ assert(clazz->iftable == NULL);
+ result = true;
+ goto bail;
+ }
+
+ /*
+ * Create a table with enough space for all interfaces, and copy the
+ * superclass' table in.
+ */
+ clazz->iftable = (InterfaceEntry*) dvmLinearAlloc(clazz->classLoader,
+ sizeof(InterfaceEntry) * ifCount);
+ zapIftable = true;
+ memset(clazz->iftable, 0x00, sizeof(InterfaceEntry) * ifCount);
+ if (superIfCount != 0) {
+ memcpy(clazz->iftable, clazz->super->iftable,
+ sizeof(InterfaceEntry) * superIfCount);
+ }
+
+ /*
+ * Create a flattened interface hierarchy of our immediate interfaces.
+ */
+ idx = superIfCount;
+
+ for (i = 0; i < clazz->interfaceCount; i++) {
+ ClassObject* interf;
+ int j;
+
+ interf = clazz->interfaces[i];
+ assert(interf != NULL);
+
+ /* make sure this is still an interface class */
+ if (!dvmIsInterfaceClass(interf)) {
+ LOGW("Class '%s' implements non-interface '%s'\n",
+ clazz->descriptor, interf->descriptor);
+ dvmThrowExceptionWithClassMessage(
+ "Ljava/lang/IncompatibleClassChangeError;",
+ clazz->descriptor);
+ goto bail;
+ }
+
+ /* add entry for this interface */
+ clazz->iftable[idx++].clazz = interf;
+
+ /* add entries for the interface's superinterfaces */
+ for (j = 0; j < interf->iftableCount; j++) {
+ clazz->iftable[idx++].clazz = interf->iftable[j].clazz;
+ }
+ }
+
+ assert(idx == ifCount);
+
+ if (false) {
+ /*
+ * Remove anything redundant from our recent additions. Note we have
+ * to traverse the recent adds when looking for duplicates, because
+ * it's possible the recent additions are self-redundant. This
+ * reduces the memory footprint of classes with lots of inherited
+ * interfaces.
+ *
+ * (I don't know if this will cause problems later on when we're trying
+ * to find a static field. It looks like the proper search order is
+ * (1) current class, (2) interfaces implemented by current class,
+ * (3) repeat with superclass. A field implemented by an interface
+ * and by a superclass might come out wrong if the superclass also
+ * implements the interface. The javac compiler will reject the
+ * situation as ambiguous, so the concern is somewhat artificial.)
+ *
+ * UPDATE: this makes ReferenceType.Interfaces difficult to implement,
+ * because it wants to return just the interfaces declared to be
+ * implemented directly by the class. I'm excluding this code for now.
+ */
+ for (i = superIfCount; i < ifCount; i++) {
+ int j;
+
+ for (j = 0; j < ifCount; j++) {
+ if (i == j)
+ continue;
+ if (clazz->iftable[i].clazz == clazz->iftable[j].clazz) {
+ LOGVV("INTF: redundant interface %s in %s\n",
+ clazz->iftable[i].clazz->descriptor,
+ clazz->descriptor);
+
+ if (i != ifCount-1)
+ memmove(&clazz->iftable[i], &clazz->iftable[i+1],
+ (ifCount - i -1) * sizeof(InterfaceEntry));
+ ifCount--;
+ i--; // adjust for i++ above
+ break;
+ }
+ }
+ }
+ LOGVV("INTF: class '%s' nodupes=%d\n", clazz->descriptor, ifCount);
+ } // if (false)
+
+ clazz->iftableCount = ifCount;
+
+ /*
+ * If we're an interface, we don't need the vtable pointers, so
+ * we're done. If this class doesn't implement an interface that our
+ * superclass doesn't have, then we again have nothing to do.
+ */
+ if (dvmIsInterfaceClass(clazz) || superIfCount == ifCount) {
+ //dvmDumpClass(clazz, kDumpClassFullDetail);
+ result = true;
+ goto bail;
+ }
+
+ /*
+ * When we're handling invokeinterface, we probably have an object
+ * whose type is an interface class rather than a concrete class. We
+ * need to convert the method reference into a vtable index. So, for
+ * every entry in "iftable", we create a list of vtable indices.
+ *
+ * Because our vtable encompasses the superclass vtable, we can use
+ * the vtable indices from our superclass for all of the interfaces
+ * that weren't directly implemented by us.
+ *
+ * Each entry in "iftable" has a pointer to the start of its set of
+ * vtable offsets. The iftable entries in the superclass point to
+ * storage allocated in the superclass, and the iftable entries added
+ * for this class point to storage allocated in this class. "iftable"
+ * is flat for fast access in a class and all of its subclasses, but
+ * "ifviPool" is only created for the topmost implementor.
+ */
+ int poolSize = 0;
+ for (i = superIfCount; i < ifCount; i++) {
+ /*
+ * Note it's valid for an interface to have no methods (e.g.
+ * java/io/Serializable).
+ */
+ LOGVV("INTF: pool: %d from %s\n",
+ clazz->iftable[i].clazz->virtualMethodCount,
+ clazz->iftable[i].clazz->descriptor);
+ poolSize += clazz->iftable[i].clazz->virtualMethodCount;
+ }
+
+ if (poolSize == 0) {
+ LOGVV("INTF: didn't find any new interfaces with methods\n");
+ result = true;
+ goto bail;
+ }
+
+ clazz->ifviPoolCount = poolSize;
+ clazz->ifviPool = (int*) dvmLinearAlloc(clazz->classLoader,
+ poolSize * sizeof(int*));
+ zapIfvipool = true;
+
+ /*
+ * Fill in the vtable offsets for the interfaces that weren't part of
+ * our superclass.
+ */
+ int poolOffset = 0;
+ Method** mirandaList = NULL;
+ int mirandaCount = 0, mirandaAlloc = 0;
+
+ for (i = superIfCount; i < ifCount; i++) {
+ ClassObject* interface;
+ int methIdx;
+
+ clazz->iftable[i].methodIndexArray = clazz->ifviPool + poolOffset;
+ interface = clazz->iftable[i].clazz;
+ poolOffset += interface->virtualMethodCount; // end here
+
+ /*
+ * For each method listed in the interface's method list, find the
+ * matching method in our class's method list. We want to favor the
+ * subclass over the superclass, which just requires walking
+ * back from the end of the vtable. (This only matters if the
+ * superclass defines a private method and this class redefines
+ * it -- otherwise it would use the same vtable slot. In Dalvik
+ * those don't end up in the virtual method table, so it shouldn't
+ * matter which direction we go. We walk it backward anyway.)
+ *
+ *
+ * Suppose we have the following arrangement:
+ * public interface MyInterface
+ * public boolean inInterface();
+ * public abstract class MirandaAbstract implements MirandaInterface
+ * //public abstract boolean inInterface(); // not declared!
+ * public boolean inAbstract() { stuff } // in vtable
+ * public class MirandClass extends MirandaAbstract
+ * public boolean inInterface() { stuff }
+ * public boolean inAbstract() { stuff } // in vtable
+ *
+ * The javac compiler happily compiles MirandaAbstract even though
+ * it doesn't declare all methods from its interface. When we try
+ * to set up a vtable for MirandaAbstract, we find that we don't
+ * have an slot for inInterface. To prevent this, we synthesize
+ * abstract method declarations in MirandaAbstract.
+ *
+ * We have to expand vtable and update some things that point at it,
+ * so we accumulate the method list and do it all at once below.
+ */
+ for (methIdx = 0; methIdx < interface->virtualMethodCount; methIdx++) {
+ Method* imeth = &interface->virtualMethods[methIdx];
+ int j;
+
+ IF_LOGVV() {
+ char* desc = dexProtoCopyMethodDescriptor(&imeth->prototype);
+ LOGVV("INTF: matching '%s' '%s'\n", imeth->name, desc);
+ free(desc);
+ }
+
+ for (j = clazz->vtableCount-1; j >= 0; j--) {
+ if (dvmCompareMethodNamesAndProtos(imeth, clazz->vtable[j])
+ == 0)
+ {
+ LOGVV("INTF: matched at %d\n", j);
+ if (!dvmIsPublicMethod(clazz->vtable[j])) {
+ LOGW("Implementation of %s.%s is not public\n",
+ clazz->descriptor, clazz->vtable[j]->name);
+ dvmThrowException("Ljava/lang/IllegalAccessError;",
+ "interface implementation not public");
+ goto bail;
+ }
+ clazz->iftable[i].methodIndexArray[methIdx] = j;
+ break;
+ }
+ }
+ if (j < 0) {
+ IF_LOGV() {
+ char* desc =
+ dexProtoCopyMethodDescriptor(&imeth->prototype);
+ LOGV("No match for '%s' '%s' in '%s' (creating miranda)\n",
+ imeth->name, desc, clazz->descriptor);
+ free(desc);
+ }
+ //dvmThrowException("Ljava/lang/RuntimeException;", "Miranda!");
+ //return false;
+
+ if (mirandaCount == mirandaAlloc) {
+ mirandaAlloc += 8;
+ if (mirandaList == NULL) {
+ mirandaList = dvmLinearAlloc(clazz->classLoader,
+ mirandaAlloc * sizeof(Method*));
+ } else {
+ dvmLinearReadOnly(clazz->classLoader, mirandaList);
+ mirandaList = dvmLinearRealloc(clazz->classLoader,
+ mirandaList, mirandaAlloc * sizeof(Method*));
+ }
+ assert(mirandaList != NULL); // mem failed + we leaked
+ }
+
+ /*
+ * These may be redundant (e.g. method with same name and
+ * signature declared in two interfaces implemented by the
+ * same abstract class). We can squeeze the duplicates
+ * out here.
+ */
+ int mir;
+ for (mir = 0; mir < mirandaCount; mir++) {
+ if (dvmCompareMethodNamesAndProtos(
+ mirandaList[mir], imeth) == 0)
+ {
+ IF_LOGVV() {
+ char* desc = dexProtoCopyMethodDescriptor(
+ &imeth->prototype);
+ LOGVV("MIRANDA dupe: %s and %s %s%s\n",
+ mirandaList[mir]->clazz->descriptor,
+ imeth->clazz->descriptor,
+ imeth->name, desc);
+ free(desc);
+ }
+ break;
+ }
+ }
+
+ /* point the iftable at a phantom slot index */
+ clazz->iftable[i].methodIndexArray[methIdx] =
+ clazz->vtableCount + mir;
+ LOGVV("MIRANDA: %s points at slot %d\n",
+ imeth->name, clazz->vtableCount + mir);
+
+ /* if non-duplicate among Mirandas, add to Miranda list */
+ if (mir == mirandaCount) {
+ //LOGV("MIRANDA: holding '%s' in slot %d\n",
+ // imeth->name, mir);
+ mirandaList[mirandaCount++] = imeth;
+ }
+ }
+ }
+ }
+
+ if (mirandaCount != 0) {
+ static const int kManyMirandas = 150; /* arbitrary */
+ Method* newVirtualMethods;
+ Method* meth;
+ int oldMethodCount, oldVtableCount;
+
+ for (i = 0; i < mirandaCount; i++) {
+ LOGVV("MIRANDA %d: %s.%s\n", i,
+ mirandaList[i]->clazz->descriptor, mirandaList[i]->name);
+ }
+ if (mirandaCount > kManyMirandas) {
+ /*
+ * Some obfuscators like to create an interface with a huge
+ * pile of methods, declare classes as implementing it, and then
+ * only define a couple of methods. This leads to a rather
+ * massive collection of Miranda methods and a lot of wasted
+ * space, sometimes enough to blow out the LinearAlloc cap.
+ */
+ LOGD("Note: class %s has %d unimplemented (abstract) methods\n",
+ clazz->descriptor, mirandaCount);
+ }
+
+ /*
+ * We found methods in one or more interfaces for which we do not
+ * have vtable entries. We have to expand our virtualMethods
+ * table (which might be empty) to hold some new entries.
+ */
+ if (clazz->virtualMethods == NULL) {
+ newVirtualMethods = (Method*) dvmLinearAlloc(clazz->classLoader,
+ sizeof(Method) * (clazz->virtualMethodCount + mirandaCount));
+ } else {
+ //dvmLinearReadOnly(clazz->classLoader, clazz->virtualMethods);
+ newVirtualMethods = (Method*) dvmLinearRealloc(clazz->classLoader,
+ clazz->virtualMethods,
+ sizeof(Method) * (clazz->virtualMethodCount + mirandaCount));
+ }
+ if (newVirtualMethods != clazz->virtualMethods) {
+ /*
+ * Table was moved in memory. We have to run through the
+ * vtable and fix the pointers. The vtable entries might be
+ * pointing at superclasses, so we flip it around: run through
+ * all locally-defined virtual methods, and fix their entries
+ * in the vtable. (This would get really messy if sub-classes
+ * had already been loaded.)
+ *
+ * Reminder: clazz->virtualMethods and clazz->virtualMethodCount
+ * hold the virtual methods declared by this class. The
+ * method's methodIndex is the vtable index, and is the same
+ * for all sub-classes (and all super classes in which it is
+ * defined). We're messing with these because the Miranda
+ * stuff makes it look like the class actually has an abstract
+ * method declaration in it.
+ */
+ LOGVV("MIRANDA fixing vtable pointers\n");
+ dvmLinearReadWrite(clazz->classLoader, clazz->vtable);
+ Method* meth = newVirtualMethods;
+ for (i = 0; i < clazz->virtualMethodCount; i++, meth++)
+ clazz->vtable[meth->methodIndex] = meth;
+ dvmLinearReadOnly(clazz->classLoader, clazz->vtable);
+ }
+
+ oldMethodCount = clazz->virtualMethodCount;
+ clazz->virtualMethods = newVirtualMethods;
+ clazz->virtualMethodCount += mirandaCount;
+
+ dvmLinearReadOnly(clazz->classLoader, clazz->virtualMethods);
+
+ /*
+ * We also have to expand the vtable.
+ */
+ assert(clazz->vtable != NULL);
+ clazz->vtable = (Method**) dvmLinearRealloc(clazz->classLoader,
+ clazz->vtable,
+ sizeof(Method*) * (clazz->vtableCount + mirandaCount));
+ if (clazz->vtable == NULL) {
+ assert(false);
+ goto bail;
+ }
+ zapVtable = true;
+
+ oldVtableCount = clazz->vtableCount;
+ clazz->vtableCount += mirandaCount;
+
+ /*
+ * Now we need to create the fake methods. We clone the abstract
+ * method definition from the interface and then replace a few
+ * things.
+ *
+ * The Method will be an "abstract native", with nativeFunc set to
+ * dvmAbstractMethodStub().
+ */
+ meth = clazz->virtualMethods + oldMethodCount;
+ for (i = 0; i < mirandaCount; i++, meth++) {
+ dvmLinearReadWrite(clazz->classLoader, clazz->virtualMethods);
+ cloneMethod(meth, mirandaList[i]);
+ meth->clazz = clazz;
+ meth->accessFlags |= ACC_MIRANDA;
+ meth->methodIndex = (u2) (oldVtableCount + i);
+ dvmLinearReadOnly(clazz->classLoader, clazz->virtualMethods);
+
+ /* point the new vtable entry at the new method */
+ clazz->vtable[oldVtableCount + i] = meth;
+ }
+
+ dvmLinearReadOnly(clazz->classLoader, mirandaList);
+ dvmLinearFree(clazz->classLoader, mirandaList);
+
+ }
+
+ /*
+ * TODO?
+ * Sort the interfaces by number of declared methods. All we really
+ * want is to get the interfaces with zero methods at the end of the
+ * list, so that when we walk through the list during invoke-interface
+ * we don't examine interfaces that can't possibly be useful.
+ *
+ * The set will usually be small, so a simple insertion sort works.
+ *
+ * We have to be careful not to change the order of two interfaces
+ * that define the same method. (Not a problem if we only move the
+ * zero-method interfaces to the end.)
+ *
+ * PROBLEM:
+ * If we do this, we will no longer be able to identify super vs.
+ * current class interfaces by comparing clazz->super->iftableCount. This
+ * breaks anything that only wants to find interfaces declared directly
+ * by the class (dvmFindStaticFieldHier, ReferenceType.Interfaces,
+ * dvmDbgOutputAllInterfaces, etc). Need to provide a workaround.
+ *
+ * We can sort just the interfaces implemented directly by this class,
+ * but that doesn't seem like it would provide much of an advantage. I'm
+ * not sure this is worthwhile.
+ *
+ * (This has been made largely obsolete by the interface cache mechanism.)
+ */
+
+ //dvmDumpClass(clazz);
+
+ result = true;
+
+bail:
+ if (zapIftable)
+ dvmLinearReadOnly(clazz->classLoader, clazz->iftable);
+ if (zapVtable)
+ dvmLinearReadOnly(clazz->classLoader, clazz->vtable);
+ if (zapIfvipool)
+ dvmLinearReadOnly(clazz->classLoader, clazz->ifviPool);
+ return result;
+}
+
+
+/*
+ * Provide "stub" implementations for methods without them.
+ *
+ * Currently we provide an implementation for all abstract methods that
+ * throws an AbstractMethodError exception. This allows us to avoid an
+ * explicit check for abstract methods in every virtual call.
+ *
+ * NOTE: for Miranda methods, the method declaration is a clone of what
+ * was found in the interface class. That copy may already have had the
+ * function pointer filled in, so don't be surprised if it's not NULL.
+ *
+ * NOTE: this sets the "native" flag, giving us an "abstract native" method,
+ * which is nonsensical. Need to make sure that this doesn't escape the
+ * VM. We can either mask it out in reflection calls, or copy "native"
+ * into the high 16 bits of accessFlags and check that internally.
+ */
+static bool insertMethodStubs(ClassObject* clazz)
+{
+ dvmLinearReadWrite(clazz->classLoader, clazz->virtualMethods);
+
+ Method* meth;
+ int i;
+
+ meth = clazz->virtualMethods;
+ for (i = 0; i < clazz->virtualMethodCount; i++, meth++) {
+ if (dvmIsAbstractMethod(meth)) {
+ assert(meth->insns == NULL);
+ assert(meth->nativeFunc == NULL ||
+ meth->nativeFunc == (DalvikBridgeFunc)dvmAbstractMethodStub);
+
+ meth->accessFlags |= ACC_NATIVE;
+ meth->nativeFunc = (DalvikBridgeFunc) dvmAbstractMethodStub;
+ }
+ }
+
+ dvmLinearReadOnly(clazz->classLoader, clazz->virtualMethods);
+ return true;
+}
+
+
+/*
+ * Swap two instance fields.
+ */
+static inline void swapField(InstField* pOne, InstField* pTwo)
+{
+ InstField swap;
+
+ LOGVV(" --- swap '%s' and '%s'\n", pOne->field.name, pTwo->field.name);
+ swap = *pOne;
+ *pOne = *pTwo;
+ *pTwo = swap;
+}
+
+/*
+ * Assign instance fields to u4 slots.
+ *
+ * The top portion of the instance field area is occupied by the superclass
+ * fields, the bottom by the fields for this class.
+ *
+ * "long" and "double" fields occupy two adjacent slots. On some
+ * architectures, 64-bit quantities must be 64-bit aligned, so we need to
+ * arrange fields (or introduce padding) to ensure this. We assume the
+ * fields of the topmost superclass (i.e. Object) are 64-bit aligned, so
+ * we can just ensure that the offset is "even". To avoid wasting space,
+ * we want to move non-reference 32-bit fields into gaps rather than
+ * creating pad words.
+ *
+ * In the worst case we will waste 4 bytes, but because objects are
+ * allocated on >= 64-bit boundaries, those bytes may well be wasted anyway
+ * (assuming this is the most-derived class).
+ *
+ * Pad words are not represented in the field table, so the field table
+ * itself does not change size.
+ *
+ * The number of field slots determines the size of the object, so we
+ * set that here too.
+ *
+ * This function feels a little more complicated than I'd like, but it
+ * has the property of moving the smallest possible set of fields, which
+ * should reduce the time required to load a class.
+ *
+ * NOTE: reference fields *must* come first, or precacheReferenceOffsets()
+ * will break.
+ */
+static bool computeFieldOffsets(ClassObject* clazz)
+{
+ int fieldOffset;
+ int i, j;
+
+ dvmLinearReadWrite(clazz->classLoader, clazz->ifields);
+
+ if (clazz->super != NULL)
+ fieldOffset = clazz->super->objectSize;
+ else
+ fieldOffset = offsetof(DataObject, instanceData);
+
+ LOGVV("--- computeFieldOffsets '%s'\n", clazz->descriptor);
+
+ //LOGI("OFFSETS fieldCount=%d\n", clazz->ifieldCount);
+ //LOGI("dataobj, instance: %d\n", offsetof(DataObject, instanceData));
+ //LOGI("classobj, access: %d\n", offsetof(ClassObject, accessFlags));
+ //LOGI("super=%p, fieldOffset=%d\n", clazz->super, fieldOffset);
+
+ /*
+ * Start by moving all reference fields to the front.
+ */
+ clazz->ifieldRefCount = 0;
+ j = clazz->ifieldCount - 1;
+ for (i = 0; i < clazz->ifieldCount; i++) {
+ InstField* pField = &clazz->ifields[i];
+ char c = pField->field.signature[0];
+
+ if (c != '[' && c != 'L') {
+ /* This isn't a reference field; see if any reference fields
+ * follow this one. If so, we'll move it to this position.
+ * (quicksort-style partitioning)
+ */
+ while (j > i) {
+ InstField* refField = &clazz->ifields[j--];
+ char rc = refField->field.signature[0];
+
+ if (rc == '[' || rc == 'L') {
+ /* Here's a reference field that follows at least one
+ * non-reference field. Swap it with the current field.
+ * (When this returns, "pField" points to the reference
+ * field, and "refField" points to the non-ref field.)
+ */
+ swapField(pField, refField);
+
+ /* Fix the signature.
+ */
+ c = rc;
+
+ clazz->ifieldRefCount++;
+ break;
+ }
+ }
+ /* We may or may not have swapped a field.
+ */
+ } else {
+ /* This is a reference field.
+ */
+ clazz->ifieldRefCount++;
+ }
+
+ /*
+ * If we've hit the end of the reference fields, break.
+ */
+ if (c != '[' && c != 'L')
+ break;
+
+ pField->byteOffset = fieldOffset;
+ fieldOffset += sizeof(u4);
+ LOGVV(" --- offset1 '%s'=%d\n", pField->field.name,pField->byteOffset);
+ }
+
+ /*
+ * Now we want to pack all of the double-wide fields together. If we're
+ * not aligned, though, we want to shuffle one 32-bit field into place.
+ * If we can't find one, we'll have to pad it.
+ */
+ if (i != clazz->ifieldCount && (fieldOffset & 0x04) != 0) {
+ LOGVV(" +++ not aligned\n");
+
+ InstField* pField = &clazz->ifields[i];
+ char c = pField->field.signature[0];
+
+ if (c != 'J' && c != 'D') {
+ /*
+ * The field that comes next is 32-bit, so just advance past it.
+ */
+ assert(c != '[' && c != 'L');
+ pField->byteOffset = fieldOffset;
+ fieldOffset += sizeof(u4);
+ i++;
+ LOGVV(" --- offset2 '%s'=%d\n",
+ pField->field.name, pField->byteOffset);
+ } else {
+ /*
+ * Next field is 64-bit, so search for a 32-bit field we can
+ * swap into it.
+ */
+ bool found = false;
+ j = clazz->ifieldCount - 1;
+ while (j > i) {
+ InstField* singleField = &clazz->ifields[j--];
+ char rc = singleField->field.signature[0];
+
+ if (rc != 'J' && rc != 'D') {
+ swapField(pField, singleField);
+ //c = rc;
+ LOGVV(" +++ swapped '%s' for alignment\n",
+ pField->field.name);
+ pField->byteOffset = fieldOffset;
+ fieldOffset += sizeof(u4);
+ LOGVV(" --- offset3 '%s'=%d\n",
+ pField->field.name, pField->byteOffset);
+ found = true;
+ i++;
+ break;
+ }
+ }
+ if (!found) {
+ LOGV(" +++ inserting pad field in '%s'\n", clazz->descriptor);
+ fieldOffset += sizeof(u4);
+ }
+ }
+ }
+
+ /*
+ * Alignment is good, shuffle any double-wide fields forward, and
+ * finish assigning field offsets to all fields.
+ */
+ assert(i == clazz->ifieldCount || (fieldOffset & 0x04) == 0);
+ j = clazz->ifieldCount - 1;
+ for ( ; i < clazz->ifieldCount; i++) {
+ InstField* pField = &clazz->ifields[i];
+ char c = pField->field.signature[0];
+
+ if (c != 'D' && c != 'J') {
+ /* This isn't a double-wide field; see if any double fields
+ * follow this one. If so, we'll move it to this position.
+ * (quicksort-style partitioning)
+ */
+ while (j > i) {
+ InstField* doubleField = &clazz->ifields[j--];
+ char rc = doubleField->field.signature[0];
+
+ if (rc == 'D' || rc == 'J') {
+ /* Here's a double-wide field that follows at least one
+ * non-double field. Swap it with the current field.
+ * (When this returns, "pField" points to the reference
+ * field, and "doubleField" points to the non-double field.)
+ */
+ swapField(pField, doubleField);
+ c = rc;
+
+ break;
+ }
+ }
+ /* We may or may not have swapped a field.
+ */
+ } else {
+ /* This is a double-wide field, leave it be.
+ */
+ }
+
+ pField->byteOffset = fieldOffset;
+ LOGVV(" --- offset4 '%s'=%d\n", pField->field.name,pField->byteOffset);
+ fieldOffset += sizeof(u4);
+ if (c == 'J' || c == 'D')
+ fieldOffset += sizeof(u4);
+ }
+
+#ifndef NDEBUG
+ /* Make sure that all reference fields appear before
+ * non-reference fields, and all double-wide fields are aligned.
+ */
+ j = 0; // seen non-ref
+ for (i = 0; i < clazz->ifieldCount; i++) {
+ InstField *pField = &clazz->ifields[i];
+ char c = pField->field.signature[0];
+
+ if (c == 'D' || c == 'J') {
+ assert((pField->byteOffset & 0x07) == 0);
+ }
+
+ if (c != '[' && c != 'L') {
+ if (!j) {
+ assert(i == clazz->ifieldRefCount);
+ j = 1;
+ }
+ } else if (j) {
+ assert(false);
+ }
+ }
+ if (!j) {
+ assert(clazz->ifieldRefCount == clazz->ifieldCount);
+ }
+#endif
+
+ /*
+ * We map a C struct directly on top of java/lang/Class objects. Make
+ * sure we left enough room for the instance fields.
+ */
+ assert(clazz != gDvm.classJavaLangClass || (size_t)fieldOffset <
+ offsetof(ClassObject, instanceData) + sizeof(clazz->instanceData));
+
+ clazz->objectSize = fieldOffset;
+
+ dvmLinearReadOnly(clazz->classLoader, clazz->ifields);
+ return true;
+}
+
+/*
+ * Throw the VM-spec-mandated error when an exception is thrown during
+ * class initialization.
+ *
+ * The safest way to do this is to call the ExceptionInInitializerError
+ * constructor that takes a Throwable.
+ *
+ * [Do we want to wrap it if the original is an Error rather than
+ * an Exception?]
+ */
+static void throwClinitError(void)
+{
+ Thread* self = dvmThreadSelf();
+ Object* exception;
+ Object* eiie;
+
+ exception = dvmGetException(self);
+ dvmAddTrackedAlloc(exception, self);
+ dvmClearException(self);
+
+ if (gDvm.classJavaLangExceptionInInitializerError == NULL) {
+ /*
+ * Always resolves to same thing -- no race condition.
+ */
+ gDvm.classJavaLangExceptionInInitializerError =
+ dvmFindSystemClass(
+ "Ljava/lang/ExceptionInInitializerError;");
+ if (gDvm.classJavaLangExceptionInInitializerError == NULL) {
+ LOGE("Unable to prep java/lang/ExceptionInInitializerError\n");
+ goto fail;
+ }
+
+ gDvm.methJavaLangExceptionInInitializerError_init =
+ dvmFindDirectMethodByDescriptor(gDvm.classJavaLangExceptionInInitializerError,
+ "<init>", "(Ljava/lang/Throwable;)V");
+ if (gDvm.methJavaLangExceptionInInitializerError_init == NULL) {
+ LOGE("Unable to prep java/lang/ExceptionInInitializerError\n");
+ goto fail;
+ }
+ }
+
+ eiie = dvmAllocObject(gDvm.classJavaLangExceptionInInitializerError,
+ ALLOC_DEFAULT);
+ if (eiie == NULL)
+ goto fail;
+
+ /*
+ * Construct the new object, and replace the exception with it.
+ */
+ JValue unused;
+ dvmCallMethod(self, gDvm.methJavaLangExceptionInInitializerError_init,
+ eiie, &unused, exception);
+ dvmSetException(self, eiie);
+ dvmReleaseTrackedAlloc(eiie, NULL);
+ dvmReleaseTrackedAlloc(exception, self);
+ return;
+
+fail: /* restore original exception */
+ dvmSetException(self, exception);
+ dvmReleaseTrackedAlloc(exception, self);
+ return;
+}
+
+/*
+ * The class failed to initialize on a previous attempt, so we want to throw
+ * a NoClassDefFoundError (v2 2.17.5). The exception to this rule is if we
+ * failed in verification, in which case v2 5.4.1 says we need to re-throw
+ * the previous error.
+ */
+static void throwEarlierClassFailure(ClassObject* clazz)
+{
+ LOGI("Rejecting re-init on previously-failed class %s v=%p\n",
+ clazz->descriptor, clazz->verifyErrorClass);
+
+ if (clazz->verifyErrorClass == NULL) {
+ dvmThrowExceptionWithClassMessage("Ljava/lang/NoClassDefFoundError;",
+ clazz->descriptor);
+ } else {
+ dvmThrowExceptionByClassWithClassMessage(clazz->verifyErrorClass,
+ clazz->descriptor);
+ }
+}
+
+/*
+ * Initialize any static fields whose values are stored in
+ * the DEX file. This must be done during class initialization.
+ */
+static void initSFields(ClassObject* clazz)
+{
+ Thread* self = dvmThreadSelf(); /* for dvmReleaseTrackedAlloc() */
+ DexFile* pDexFile;
+ const DexClassDef* pClassDef;
+ const DexEncodedArray* pValueList;
+ EncodedArrayIterator iterator;
+ int i;
+
+ if (clazz->sfieldCount == 0) {
+ return;
+ }
+ if (clazz->pDvmDex == NULL) {
+ /* generated class; any static fields should already be set up */
+ LOGV("Not initializing static fields in %s\n", clazz->descriptor);
+ return;
+ }
+ pDexFile = clazz->pDvmDex->pDexFile;
+
+ pClassDef = dexFindClass(pDexFile, clazz->descriptor);
+ assert(pClassDef != NULL);
+
+ pValueList = dexGetStaticValuesList(pDexFile, pClassDef);
+ if (pValueList == NULL) {
+ return;
+ }
+
+ dvmEncodedArrayIteratorInitialize(&iterator, pValueList, clazz);
+
+ /*
+ * Iterate over the initial values array, setting the corresponding
+ * static field for each array element.
+ */
+
+ for (i = 0; dvmEncodedArrayIteratorHasNext(&iterator); i++) {
+ AnnotationValue value;
+ bool parsed = dvmEncodedArrayIteratorGetNext(&iterator, &value);
+ StaticField* sfield = &clazz->sfields[i];
+ const char* descriptor = sfield->field.signature;
+ bool isObj = false;
+
+ if (! parsed) {
+ /*
+ * TODO: Eventually verification should attempt to ensure
+ * that this can't happen at least due to a data integrity
+ * problem.
+ */
+ LOGE("Static initializer parse failed for %s at index %d",
+ clazz->descriptor, i);
+ dvmAbort();
+ }
+
+ /* Verify that the value we got was of a valid type. */
+
+ switch (descriptor[0]) {
+ case 'Z': parsed = (value.type == kDexAnnotationBoolean); break;
+ case 'B': parsed = (value.type == kDexAnnotationByte); break;
+ case 'C': parsed = (value.type == kDexAnnotationChar); break;
+ case 'S': parsed = (value.type == kDexAnnotationShort); break;
+ case 'I': parsed = (value.type == kDexAnnotationInt); break;
+ case 'J': parsed = (value.type == kDexAnnotationLong); break;
+ case 'F': parsed = (value.type == kDexAnnotationFloat); break;
+ case 'D': parsed = (value.type == kDexAnnotationDouble); break;
+ case '[': parsed = (value.type == kDexAnnotationNull); break;
+ case 'L': {
+ switch (value.type) {
+ case kDexAnnotationNull: {
+ /* No need for further tests. */
+ break;
+ }
+ case kDexAnnotationString: {
+ parsed =
+ (strcmp(descriptor, "Ljava/lang/String;") == 0);
+ isObj = true;
+ break;
+ }
+ case kDexAnnotationType: {
+ parsed =
+ (strcmp(descriptor, "Ljava/lang/Class;") == 0);
+ isObj = true;
+ break;
+ }
+ default: {
+ parsed = false;
+ break;
+ }
+ }
+ break;
+ }
+ default: {
+ parsed = false;
+ break;
+ }
+ }
+
+ if (parsed) {
+ /*
+ * All's well, so store the value.
+ */
+ if (isObj) {
+ dvmSetStaticFieldObject(sfield, value.value.l);
+ dvmReleaseTrackedAlloc(value.value.l, self);
+ } else {
+ /*
+ * Note: This always stores the full width of a
+ * JValue, even though most of the time only the first
+ * word is needed.
+ */
+ sfield->value = value.value;
+ }
+ } else {
+ /*
+ * Something up above had a problem. TODO: See comment
+ * above the switch about verfication.
+ */
+ LOGE("Bogus static initialization: value type %d in field type "
+ "%s for %s at index %d",
+ value.type, descriptor, clazz->descriptor, i);
+ dvmAbort();
+ }
+ }
+}
+
+
+/*
+ * Determine whether "descriptor" yields the same class object in the
+ * context of clazz1 and clazz2.
+ *
+ * The caller must hold gDvm.loadedClasses.
+ *
+ * Returns "true" if they match.
+ */
+static bool compareDescriptorClasses(const char* descriptor,
+ const ClassObject* clazz1, const ClassObject* clazz2)
+{
+ ClassObject* result1;
+ ClassObject* result2;
+
+ /*
+ * Do the first lookup by name.
+ */
+ result1 = dvmFindClassNoInit(descriptor, clazz1->classLoader);
+
+ /*
+ * We can skip a second lookup by name if the second class loader is
+ * in the initiating loader list of the class object we retrieved.
+ * (This means that somebody already did a lookup of this class through
+ * the second loader, and it resolved to the same class.) If it's not
+ * there, we may simply not have had an opportunity to add it yet, so
+ * we do the full lookup.
+ *
+ * The initiating loader test should catch the majority of cases
+ * (in particular, the zillions of references to String/Object).
+ *
+ * Unfortunately we're still stuck grabbing a mutex to do the lookup.
+ *
+ * For this to work, the superclass/interface should be the first
+ * argument, so that way if it's from the bootstrap loader this test
+ * will work. (The bootstrap loader, by definition, never shows up
+ * as the initiating loader of a class defined by some other loader.)
+ */
+ dvmHashTableLock(gDvm.loadedClasses);
+ bool isInit = dvmLoaderInInitiatingList(result1, clazz2->classLoader);
+ dvmHashTableUnlock(gDvm.loadedClasses);
+
+ if (isInit) {
+ //printf("%s(obj=%p) / %s(cl=%p): initiating\n",
+ // result1->descriptor, result1,
+ // clazz2->descriptor, clazz2->classLoader);
+ return true;
+ } else {
+ //printf("%s(obj=%p) / %s(cl=%p): RAW\n",
+ // result1->descriptor, result1,
+ // clazz2->descriptor, clazz2->classLoader);
+ result2 = dvmFindClassNoInit(descriptor, clazz2->classLoader);
+ }
+
+ if (result1 == NULL || result2 == NULL) {
+ dvmClearException(dvmThreadSelf());
+ if (result1 == result2) {
+ /*
+ * Neither class loader could find this class. Apparently it
+ * doesn't exist.
+ *
+ * We can either throw some sort of exception now, or just
+ * assume that it'll fail later when something actually tries
+ * to use the class. For strict handling we should throw now,
+ * because a "tricky" class loader could start returning
+ * something later, and a pair of "tricky" loaders could set
+ * us up for confusion.
+ *
+ * I'm not sure if we're allowed to complain about nonexistent
+ * classes in method signatures during class init, so for now
+ * this will just return "true" and let nature take its course.
+ */
+ return true;
+ } else {
+ /* only one was found, so clearly they're not the same */
+ return false;
+ }
+ }
+
+ return result1 == result2;
+}
+
+/*
+ * For every component in the method descriptor, resolve the class in the
+ * context of the two classes and compare the results.
+ *
+ * For best results, the "superclass" class should be first.
+ *
+ * Returns "true" if the classes match, "false" otherwise.
+ */
+static bool checkMethodDescriptorClasses(const Method* meth,
+ const ClassObject* clazz1, const ClassObject* clazz2)
+{
+ DexParameterIterator iterator;
+ const char* descriptor;
+
+ /* walk through the list of parameters */
+ dexParameterIteratorInit(&iterator, &meth->prototype);
+ while (true) {
+ descriptor = dexParameterIteratorNextDescriptor(&iterator);
+
+ if (descriptor == NULL)
+ break;
+
+ if (descriptor[0] == 'L' || descriptor[0] == '[') {
+ /* non-primitive type */
+ if (!compareDescriptorClasses(descriptor, clazz1, clazz2))
+ return false;
+ }
+ }
+
+ /* check the return type */
+ descriptor = dexProtoGetReturnType(&meth->prototype);
+ if (descriptor[0] == 'L' || descriptor[0] == '[') {
+ if (!compareDescriptorClasses(descriptor, clazz1, clazz2))
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Validate the descriptors in the superclass and interfaces.
+ *
+ * What we need to do is ensure that the classes named in the method
+ * descriptors in our ancestors and ourselves resolve to the same class
+ * objects. We can get conflicts when the classes come from different
+ * class loaders, and the resolver comes up with different results for
+ * the same class name in different contexts.
+ *
+ * An easy way to cause the problem is to declare a base class that uses
+ * class Foo in a method signature (e.g. as the return type). Then,
+ * define a subclass and a different version of Foo, and load them from a
+ * different class loader. If the subclass overrides the method, it will
+ * have a different concept of what Foo is than its parent does, so even
+ * though the method signature strings are identical, they actually mean
+ * different things.
+ *
+ * A call to the method through a base-class reference would be treated
+ * differently than a call to the method through a subclass reference, which
+ * isn't the way polymorphism works, so we have to reject the subclass.
+ * If the subclass doesn't override the base method, then there's no
+ * problem, because calls through base-class references and subclass
+ * references end up in the same place.
+ *
+ * We don't need to check to see if an interface's methods match with its
+ * superinterface's methods, because you can't instantiate an interface
+ * and do something inappropriate with it. If interface I1 extends I2
+ * and is implemented by C, and I1 and I2 are in separate class loaders
+ * and have conflicting views of other classes, we will catch the conflict
+ * when we process C. Anything that implements I1 is doomed to failure,
+ * but we don't need to catch that while processing I1.
+ *
+ * On failure, throws an exception and returns "false".
+ */
+static bool validateSuperDescriptors(const ClassObject* clazz)
+{
+ int i;
+
+ if (dvmIsInterfaceClass(clazz))
+ return true;
+
+ /*
+ * Start with the superclass-declared methods.
+ */
+ if (clazz->super != NULL &&
+ clazz->classLoader != clazz->super->classLoader)
+ {
+ /*
+ * Walk through every overridden method and compare resolved
+ * descriptor components. We pull the Method structs out of
+ * the vtable. It doesn't matter whether we get the struct from
+ * the parent or child, since we just need the UTF-8 descriptor,
+ * which must match.
+ *
+ * We need to do this even for the stuff inherited from Object,
+ * because it's possible that the new class loader has redefined
+ * a basic class like String.
+ *
+ * We don't need to check stuff defined in a superclass because
+ * it was checked when the superclass was loaded.
+ */
+ const Method* meth;
+
+ //printf("Checking %s %p vs %s %p\n",
+ // clazz->descriptor, clazz->classLoader,
+ // clazz->super->descriptor, clazz->super->classLoader);
+ for (i = clazz->super->vtableCount - 1; i >= 0; i--) {
+ meth = clazz->vtable[i];
+ if (meth != clazz->super->vtable[i] &&
+ !checkMethodDescriptorClasses(meth, clazz->super, clazz))
+ {
+ LOGW("Method mismatch: %s in %s (cl=%p) and super %s (cl=%p)\n",
+ meth->name, clazz->descriptor, clazz->classLoader,
+ clazz->super->descriptor, clazz->super->classLoader);
+ dvmThrowException("Ljava/lang/LinkageError;",
+ "Classes resolve differently in superclass");
+ return false;
+ }
+ }
+ }
+
+ /*
+ * Check the methods defined by this class against the interfaces it
+ * implements. If we inherited the implementation from a superclass,
+ * we have to check it against the superclass (which might be in a
+ * different class loader). If the superclass also implements the
+ * interface, we could skip the check since by definition it was
+ * performed when the class was loaded.
+ */
+ for (i = 0; i < clazz->iftableCount; i++) {
+ const InterfaceEntry* iftable = &clazz->iftable[i];
+
+ if (clazz->classLoader != iftable->clazz->classLoader) {
+ const ClassObject* iface = iftable->clazz;
+ int j;
+
+ for (j = 0; j < iface->virtualMethodCount; j++) {
+ const Method* meth;
+ int vtableIndex;
+
+ vtableIndex = iftable->methodIndexArray[j];
+ meth = clazz->vtable[vtableIndex];
+
+ if (!checkMethodDescriptorClasses(meth, iface, meth->clazz)) {
+ LOGW("Method mismatch: %s in %s (cl=%p) and "
+ "iface %s (cl=%p)\n",
+ meth->name, clazz->descriptor, clazz->classLoader,
+ iface->descriptor, iface->classLoader);
+ dvmThrowException("Ljava/lang/LinkageError;",
+ "Classes resolve differently in interface");
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+/*
+ * Returns true if the class is being initialized by us (which means that
+ * calling dvmInitClass will return immediately after fiddling with locks).
+ *
+ * There isn't a race here, because either clazz->initThreadId won't match
+ * us, or it will and it was set in the same thread.
+ */
+bool dvmIsClassInitializing(const ClassObject* clazz)
+{
+ return (clazz->status == CLASS_INITIALIZING &&
+ clazz->initThreadId == dvmThreadSelf()->threadId);
+}
+
+/*
+ * If a class has not been initialized, do so by executing the code in
+ * <clinit>. The sequence is described in the VM spec v2 2.17.5.
+ *
+ * It is possible for multiple threads to arrive here simultaneously, so
+ * we need to lock the class while we check stuff. We know that no
+ * interpreted code has access to the class yet, so we can use the class's
+ * monitor lock.
+ *
+ * We will often be called recursively, e.g. when the <clinit> code resolves
+ * one of its fields, the field resolution will try to initialize the class.
+ * In that case we will return "true" even though the class isn't actually
+ * ready to go. The ambiguity can be resolved with dvmIsClassInitializing().
+ * (TODO: consider having this return an enum to avoid the extra call --
+ * return -1 on failure, 0 on success, 1 on still-initializing. Looks like
+ * dvmIsClassInitializing() is always paired with *Initialized())
+ *
+ * This can get very interesting if a class has a static field initialized
+ * to a new instance of itself. <clinit> will end up calling <init> on
+ * the members it is initializing, which is fine unless it uses the contents
+ * of static fields to initialize instance fields. This will leave the
+ * static-referenced objects in a partially initialized state. This is
+ * reasonably rare and can sometimes be cured with proper field ordering.
+ *
+ * On failure, returns "false" with an exception raised.
+ *
+ * -----
+ *
+ * It is possible to cause a deadlock by having a situation like this:
+ * class A { static { sleep(10000); new B(); } }
+ * class B { static { sleep(10000); new A(); } }
+ * new Thread() { public void run() { new A(); } }.start();
+ * new Thread() { public void run() { new B(); } }.start();
+ * This appears to be expected under the spec.
+ *
+ * The interesting question is what to do if somebody calls Thread.interrupt()
+ * on one of the deadlocked threads. According to the VM spec, they're both
+ * sitting in "wait". Should the interrupt code quietly raise the
+ * "interrupted" flag, or should the "wait" return immediately with an
+ * exception raised?
+ *
+ * This gets a little murky. The VM spec says we call "wait", and the
+ * spec for Thread.interrupt says Object.wait is interruptible. So it
+ * seems that, if we get unlucky and interrupt class initialization, we
+ * are expected to throw (which gets converted to ExceptionInInitializerError
+ * since InterruptedException is checked).
+ *
+ * There are a couple of problems here. First, all threads are expected to
+ * present a consistent view of class initialization, so we can't have it
+ * fail in one thread and succeed in another. Second, once a class fails
+ * to initialize, it must *always* fail. This means that a stray interrupt()
+ * call could render a class unusable for the lifetime of the VM.
+ *
+ * In most cases -- the deadlock example above being a counter-example --
+ * the interrupting thread can't tell whether the target thread handled
+ * the initialization itself or had to wait while another thread did the
+ * work. Refusing to interrupt class initialization is, in most cases,
+ * not something that a program can reliably detect.
+ *
+ * On the assumption that interrupting class initialization is highly
+ * undesirable in most circumstances, and that failing to do so does not
+ * deviate from the spec in a meaningful way, we don't allow class init
+ * to be interrupted by Thread.interrupt().
+ */
+bool dvmInitClass(ClassObject* clazz)
+{
+#if LOG_CLASS_LOADING
+ bool initializedByUs = false;
+#endif
+
+ Thread* self = dvmThreadSelf();
+ const Method* method;
+
+ dvmLockObject(self, (Object*) clazz);
+ assert(dvmIsClassLinked(clazz) || clazz->status == CLASS_ERROR);
+
+ /*
+ * If the class hasn't been verified yet, do so now.
+ */
+ if (clazz->status < CLASS_VERIFIED) {
+ /*
+ * If we're in an "erroneous" state, throw an exception and bail.
+ */
+ if (clazz->status == CLASS_ERROR) {
+ throwEarlierClassFailure(clazz);
+ goto bail_unlock;
+ }
+
+ assert(clazz->status == CLASS_RESOLVED);
+ assert(!IS_CLASS_FLAG_SET(clazz, CLASS_ISPREVERIFIED));
+
+ if (gDvm.classVerifyMode == VERIFY_MODE_NONE ||
+ (gDvm.classVerifyMode == VERIFY_MODE_REMOTE &&
+ clazz->classLoader == NULL))
+ {
+ /* advance to "verified" state */
+ LOGV("+++ not verifying class %s (cl=%p)\n",
+ clazz->descriptor, clazz->classLoader);
+ clazz->status = CLASS_VERIFIED;
+ goto noverify;
+ }
+
+ if (!gDvm.optimizing)
+ LOGV("+++ late verify on %s\n", clazz->descriptor);
+
+ /*
+ * We're not supposed to optimize an unverified class, but during
+ * development this mode was useful. We can't verify an optimized
+ * class because the optimization process discards information.
+ */
+ if (IS_CLASS_FLAG_SET(clazz, CLASS_ISOPTIMIZED)) {
+ LOGW("Class '%s' was optimized without verification; "
+ "not verifying now\n",
+ clazz->descriptor);
+ LOGW(" ('rm /data/dalvik-cache/*' and restart to fix this)");
+ goto verify_failed;
+ }
+
+ clazz->status = CLASS_VERIFYING;
+ if (!dvmVerifyClass(clazz)) {
+verify_failed:
+ dvmThrowExceptionWithClassMessage("Ljava/lang/VerifyError;",
+ clazz->descriptor);
+ dvmSetFieldObject((Object*) clazz,
+ offsetof(ClassObject, verifyErrorClass),
+ (Object*) dvmGetException(self)->clazz);
+ clazz->status = CLASS_ERROR;
+ goto bail_unlock;
+ }
+
+ clazz->status = CLASS_VERIFIED;
+ }
+noverify:
+
+ /*
+ * We need to ensure that certain instructions, notably accesses to
+ * volatile fields, are replaced before any code is executed. This
+ * must happen even if DEX optimizations are disabled.
+ */
+ if (!IS_CLASS_FLAG_SET(clazz, CLASS_ISOPTIMIZED)) {
+ LOGV("+++ late optimize on %s (pv=%d)\n",
+ clazz->descriptor, IS_CLASS_FLAG_SET(clazz, CLASS_ISPREVERIFIED));
+ dvmOptimizeClass(clazz, true);
+ SET_CLASS_FLAG(clazz, CLASS_ISOPTIMIZED);
+ }
+
+ /* update instruction stream now that verification + optimization is done */
+ dvmFlushBreakpoints(clazz);
+
+ if (clazz->status == CLASS_INITIALIZED)
+ goto bail_unlock;
+
+ while (clazz->status == CLASS_INITIALIZING) {
+ /* we caught somebody else in the act; was it us? */
+ if (clazz->initThreadId == self->threadId) {
+ //LOGV("HEY: found a recursive <clinit>\n");
+ goto bail_unlock;
+ }
+
+ if (dvmCheckException(self)) {
+ LOGW("GLITCH: exception pending at start of class init\n");
+ dvmAbort();
+ }
+
+ /*
+ * Wait for the other thread to finish initialization. We pass
+ * "false" for the "interruptShouldThrow" arg so it doesn't throw
+ * an exception on interrupt.
+ */
+ dvmObjectWait(self, (Object*) clazz, 0, 0, false);
+
+ /*
+ * When we wake up, repeat the test for init-in-progress. If there's
+ * an exception pending (only possible if "interruptShouldThrow"
+ * was set), bail out.
+ */
+ if (dvmCheckException(self)) {
+ LOGI("Class init of '%s' failing with wait() exception\n",
+ clazz->descriptor);
+ /*
+ * TODO: this is bogus, because it means the two threads have a
+ * different idea of the class status. We need to flag the
+ * class as bad and ensure that the initializer thread respects
+ * our notice. If we get lucky and wake up after the class has
+ * finished initialization but before being woken, we have to
+ * swallow the exception, perhaps raising thread->interrupted
+ * to preserve semantics.
+ *
+ * Since we're not currently allowing interrupts, this should
+ * never happen and we don't need to fix this.
+ */
+ assert(false);
+ throwClinitError();
+ clazz->status = CLASS_ERROR;
+ goto bail_unlock;
+ }
+ if (clazz->status == CLASS_INITIALIZING) {
+ LOGI("Waiting again for class init\n");
+ continue;
+ }
+ assert(clazz->status == CLASS_INITIALIZED ||
+ clazz->status == CLASS_ERROR);
+ if (clazz->status == CLASS_ERROR) {
+ /*
+ * The caller wants an exception, but it was thrown in a
+ * different thread. Synthesize one here.
+ */
+ dvmThrowException("Ljava/lang/UnsatisfiedLinkError;",
+ "(<clinit> failed, see exception in other thread)");
+ }
+ goto bail_unlock;
+ }
+
+ /* see if we failed previously */
+ if (clazz->status == CLASS_ERROR) {
+ // might be wise to unlock before throwing; depends on which class
+ // it is that we have locked
+ dvmUnlockObject(self, (Object*) clazz);
+ throwEarlierClassFailure(clazz);
+ return false;
+ }
+
+ u8 startWhen = 0;
+ if (gDvm.allocProf.enabled) {
+ startWhen = dvmGetRelativeTimeNsec();
+ }
+
+ /*
+ * We're ready to go, and have exclusive access to the class.
+ *
+ * Before we start initialization, we need to do one extra bit of
+ * validation: make sure that the methods declared here match up
+ * with our superclass and interfaces. We know that the UTF-8
+ * descriptors match, but classes from different class loaders can
+ * have the same name.
+ *
+ * We do this now, rather than at load/link time, for the same reason
+ * that we defer verification.
+ *
+ * It's unfortunate that we need to do this at all, but we risk
+ * mixing reference types with identical names (see Dalvik test 068).
+ */
+ if (!validateSuperDescriptors(clazz)) {
+ assert(dvmCheckException(self));
+ clazz->status = CLASS_ERROR;
+ goto bail_unlock;
+ }
+
+ /*
+ * Let's initialize this thing.
+ *
+ * We unlock the object so that other threads can politely sleep on
+ * our mutex with Object.wait(), instead of hanging or spinning trying
+ * to grab our mutex.
+ */
+ assert(clazz->status < CLASS_INITIALIZING);
+
+#if LOG_CLASS_LOADING
+ // We started initializing.
+ logClassLoad('+', clazz);
+ initializedByUs = true;
+#endif
+
+ clazz->status = CLASS_INITIALIZING;
+ clazz->initThreadId = self->threadId;
+ dvmUnlockObject(self, (Object*) clazz);
+
+ /* init our superclass */
+ if (clazz->super != NULL && clazz->super->status != CLASS_INITIALIZED) {
+ assert(!dvmIsInterfaceClass(clazz));
+ if (!dvmInitClass(clazz->super)) {
+ assert(dvmCheckException(self));
+ clazz->status = CLASS_ERROR;
+ /* wake up anybody who started waiting while we were unlocked */
+ dvmLockObject(self, (Object*) clazz);
+ goto bail_notify;
+ }
+ }
+
+ /* Initialize any static fields whose values are
+ * stored in the Dex file. This should include all of the
+ * simple "final static" fields, which are required to
+ * be initialized first. (vmspec 2 sec 2.17.5 item 8)
+ * More-complicated final static fields should be set
+ * at the beginning of <clinit>; all we can do is trust
+ * that the compiler did the right thing.
+ */
+ initSFields(clazz);
+
+ /* Execute any static initialization code.
+ */
+ method = dvmFindDirectMethodByDescriptor(clazz, "<clinit>", "()V");
+ if (method == NULL) {
+ LOGVV("No <clinit> found for %s\n", clazz->descriptor);
+ } else {
+ LOGVV("Invoking %s.<clinit>\n", clazz->descriptor);
+ JValue unused;
+ dvmCallMethod(self, method, NULL, &unused);
+ }
+
+ if (dvmCheckException(self)) {
+ /*
+ * We've had an exception thrown during static initialization. We
+ * need to throw an ExceptionInInitializerError, but we want to
+ * tuck the original exception into the "cause" field.
+ */
+ LOGW("Exception %s thrown while initializing %s\n",
+ (dvmGetException(self)->clazz)->descriptor, clazz->descriptor);
+ throwClinitError();
+ //LOGW("+++ replaced\n");
+
+ dvmLockObject(self, (Object*) clazz);
+ clazz->status = CLASS_ERROR;
+ } else {
+ /* success! */
+ dvmLockObject(self, (Object*) clazz);
+ clazz->status = CLASS_INITIALIZED;
+ LOGVV("Initialized class: %s\n", clazz->descriptor);
+
+ /*
+ * Update alloc counters. TODO: guard with mutex.
+ */
+ if (gDvm.allocProf.enabled && startWhen != 0) {
+ u8 initDuration = dvmGetRelativeTimeNsec() - startWhen;
+ gDvm.allocProf.classInitTime += initDuration;
+ self->allocProf.classInitTime += initDuration;
+ gDvm.allocProf.classInitCount++;
+ self->allocProf.classInitCount++;
+ }
+ }
+
+bail_notify:
+ /*
+ * Notify anybody waiting on the object.
+ */
+ dvmObjectNotifyAll(self, (Object*) clazz);
+
+bail_unlock:
+
+#if LOG_CLASS_LOADING
+ if (initializedByUs) {
+ // We finished initializing.
+ logClassLoad('-', clazz);
+ }
+#endif
+
+ dvmUnlockObject(self, (Object*) clazz);
+
+ return (clazz->status != CLASS_ERROR);
+}
+
+/*
+ * Replace method->nativeFunc and method->insns with new values. This is
+ * commonly performed after successful resolution of a native method.
+ *
+ * There are three basic states:
+ * (1) (initial) nativeFunc = dvmResolveNativeMethod, insns = NULL
+ * (2) (internal native) nativeFunc = <impl>, insns = NULL
+ * (3) (JNI) nativeFunc = JNI call bridge, insns = <impl>
+ *
+ * nativeFunc must never be NULL for a native method.
+ *
+ * The most common transitions are (1)->(2) and (1)->(3). The former is
+ * atomic, since only one field is updated; the latter is not, but since
+ * dvmResolveNativeMethod ignores the "insns" field we just need to make
+ * sure the update happens in the correct order.
+ *
+ * A transition from (2)->(1) would work fine, but (3)->(1) will not,
+ * because both fields change. If we did this while a thread was executing
+ * in the call bridge, we could null out the "insns" field right before
+ * the bridge tried to call through it. So, once "insns" is set, we do
+ * not allow it to be cleared. A NULL value for the "insns" argument is
+ * treated as "do not change existing value".
+ */
+void dvmSetNativeFunc(Method* method, DalvikBridgeFunc func,
+ const u2* insns)
+{
+ ClassObject* clazz = method->clazz;
+
+ assert(func != NULL);
+
+ /* just open up both; easier that way */
+ dvmLinearReadWrite(clazz->classLoader, clazz->virtualMethods);
+ dvmLinearReadWrite(clazz->classLoader, clazz->directMethods);
+
+ if (insns != NULL) {
+ /* update both, ensuring that "insns" is observed first */
+ method->insns = insns;
+ android_atomic_release_store((int32_t) func,
+ (void*) &method->nativeFunc);
+ } else {
+ /* only update nativeFunc */
+ method->nativeFunc = func;
+ }
+
+ dvmLinearReadOnly(clazz->classLoader, clazz->virtualMethods);
+ dvmLinearReadOnly(clazz->classLoader, clazz->directMethods);
+}
+
+/*
+ * Add a RegisterMap to a Method. This is done when we verify the class
+ * and compute the register maps at class initialization time (i.e. when
+ * we don't have a pre-generated map). This means "pMap" is on the heap
+ * and should be freed when the Method is discarded.
+ */
+void dvmSetRegisterMap(Method* method, const RegisterMap* pMap)
+{
+ ClassObject* clazz = method->clazz;
+
+ if (method->registerMap != NULL) {
+ /* unexpected during class loading, okay on first use (uncompress) */
+ LOGV("NOTE: registerMap already set for %s.%s\n",
+ method->clazz->descriptor, method->name);
+ /* keep going */
+ }
+ assert(!dvmIsNativeMethod(method) && !dvmIsAbstractMethod(method));
+
+ /* might be virtual or direct */
+ dvmLinearReadWrite(clazz->classLoader, clazz->virtualMethods);
+ dvmLinearReadWrite(clazz->classLoader, clazz->directMethods);
+
+ method->registerMap = pMap;
+
+ dvmLinearReadOnly(clazz->classLoader, clazz->virtualMethods);
+ dvmLinearReadOnly(clazz->classLoader, clazz->directMethods);
+}
+
+/*
+ * dvmHashForeach callback. A nonzero return value causes foreach to
+ * bail out.
+ */
+static int findClassCallback(void* vclazz, void* arg)
+{
+ ClassObject* clazz = vclazz;
+ const char* descriptor = (const char*) arg;
+
+ if (strcmp(clazz->descriptor, descriptor) == 0)
+ return (int) clazz;
+ return 0;
+}
+
+/*
+ * Find a loaded class by descriptor. Returns the first one found.
+ * Because there can be more than one if class loaders are involved,
+ * this is not an especially good API. (Currently only used by the
+ * debugger and "checking" JNI.)
+ *
+ * "descriptor" should have the form "Ljava/lang/Class;" or
+ * "[Ljava/lang/Class;", i.e. a descriptor and not an internal-form
+ * class name.
+ */
+ClassObject* dvmFindLoadedClass(const char* descriptor)
+{
+ int result;
+
+ dvmHashTableLock(gDvm.loadedClasses);
+ result = dvmHashForeach(gDvm.loadedClasses, findClassCallback,
+ (void*) descriptor);
+ dvmHashTableUnlock(gDvm.loadedClasses);
+
+ return (ClassObject*) result;
+}
+
+/*
+ * Retrieve the system (a/k/a application) class loader.
+ */
+Object* dvmGetSystemClassLoader(void)
+{
+ ClassObject* clazz;
+ Method* getSysMeth;
+ Object* loader;
+
+ clazz = dvmFindSystemClass("Ljava/lang/ClassLoader;");
+ if (clazz == NULL)
+ return NULL;
+
+ getSysMeth = dvmFindDirectMethodByDescriptor(clazz, "getSystemClassLoader",
+ "()Ljava/lang/ClassLoader;");
+ if (getSysMeth == NULL)
+ return NULL;
+
+ JValue result;
+ dvmCallMethod(dvmThreadSelf(), getSysMeth, NULL, &result);
+ loader = (Object*)result.l;
+ return loader;
+}
+
+
+/*
+ * This is a dvmHashForeach callback.
+ */
+static int dumpClass(void* vclazz, void* varg)
+{
+ const ClassObject* clazz = (const ClassObject*) vclazz;
+ const ClassObject* super;
+ int flags = (int) varg;
+ char* desc;
+ int i;
+
+ if (clazz == NULL) {
+ LOGI("dumpClass: ignoring request to dump null class\n");
+ return 0;
+ }
+
+ if ((flags & kDumpClassFullDetail) == 0) {
+ bool showInit = (flags & kDumpClassInitialized) != 0;
+ bool showLoader = (flags & kDumpClassClassLoader) != 0;
+ const char* initStr;
+
+ initStr = dvmIsClassInitialized(clazz) ? "true" : "false";
+
+ if (showInit && showLoader)
+ LOGI("%s %p %s\n", clazz->descriptor, clazz->classLoader, initStr);
+ else if (showInit)
+ LOGI("%s %s\n", clazz->descriptor, initStr);
+ else if (showLoader)
+ LOGI("%s %p\n", clazz->descriptor, clazz->classLoader);
+ else
+ LOGI("%s\n", clazz->descriptor);
+
+ return 0;
+ }
+
+ /* clazz->super briefly holds the superclass index during class prep */
+ if ((u4)clazz->super > 0x10000 && (u4) clazz->super != (u4)-1)
+ super = clazz->super;
+ else
+ super = NULL;
+
+ LOGI("----- %s '%s' cl=%p ser=0x%08x -----\n",
+ dvmIsInterfaceClass(clazz) ? "interface" : "class",
+ clazz->descriptor, clazz->classLoader, clazz->serialNumber);
+ LOGI(" objectSize=%d (%d from super)\n", (int) clazz->objectSize,
+ super != NULL ? (int) super->objectSize : -1);
+ LOGI(" access=0x%04x.%04x\n", clazz->accessFlags >> 16,
+ clazz->accessFlags & JAVA_FLAGS_MASK);
+ if (super != NULL)
+ LOGI(" super='%s' (cl=%p)\n", super->descriptor, super->classLoader);
+ if (dvmIsArrayClass(clazz)) {
+ LOGI(" dimensions=%d elementClass=%s\n",
+ clazz->arrayDim, clazz->elementClass->descriptor);
+ }
+ if (clazz->iftableCount > 0) {
+ LOGI(" interfaces (%d):\n", clazz->iftableCount);
+ for (i = 0; i < clazz->iftableCount; i++) {
+ InterfaceEntry* ent = &clazz->iftable[i];
+ int j;
+
+ LOGI(" %2d: %s (cl=%p)\n",
+ i, ent->clazz->descriptor, ent->clazz->classLoader);
+
+ /* enable when needed */
+ if (false && ent->methodIndexArray != NULL) {
+ for (j = 0; j < ent->clazz->virtualMethodCount; j++)
+ LOGI(" %2d: %d %s %s\n",
+ j, ent->methodIndexArray[j],
+ ent->clazz->virtualMethods[j].name,
+ clazz->vtable[ent->methodIndexArray[j]]->name);
+ }
+ }
+ }
+ if (!dvmIsInterfaceClass(clazz)) {
+ LOGI(" vtable (%d entries, %d in super):\n", clazz->vtableCount,
+ super != NULL ? super->vtableCount : 0);
+ for (i = 0; i < clazz->vtableCount; i++) {
+ desc = dexProtoCopyMethodDescriptor(&clazz->vtable[i]->prototype);
+ LOGI(" %s%2d: %p %20s %s\n",
+ (i != clazz->vtable[i]->methodIndex) ? "*** " : "",
+ (u4) clazz->vtable[i]->methodIndex, clazz->vtable[i],
+ clazz->vtable[i]->name, desc);
+ free(desc);
+ }
+ LOGI(" direct methods (%d entries):\n", clazz->directMethodCount);
+ for (i = 0; i < clazz->directMethodCount; i++) {
+ desc = dexProtoCopyMethodDescriptor(
+ &clazz->directMethods[i].prototype);
+ LOGI(" %2d: %20s %s\n", i, clazz->directMethods[i].name,
+ desc);
+ free(desc);
+ }
+ } else {
+ LOGI(" interface methods (%d):\n", clazz->virtualMethodCount);
+ for (i = 0; i < clazz->virtualMethodCount; i++) {
+ desc = dexProtoCopyMethodDescriptor(
+ &clazz->virtualMethods[i].prototype);
+ LOGI(" %2d: %2d %20s %s\n", i,
+ (u4) clazz->virtualMethods[i].methodIndex,
+ clazz->virtualMethods[i].name,
+ desc);
+ free(desc);
+ }
+ }
+ if (clazz->sfieldCount > 0) {
+ LOGI(" static fields (%d entries):\n", clazz->sfieldCount);
+ for (i = 0; i < clazz->sfieldCount; i++) {
+ LOGI(" %2d: %20s %s\n", i, clazz->sfields[i].field.name,
+ clazz->sfields[i].field.signature);
+ }
+ }
+ if (clazz->ifieldCount > 0) {
+ LOGI(" instance fields (%d entries):\n", clazz->ifieldCount);
+ for (i = 0; i < clazz->ifieldCount; i++) {
+ LOGI(" %2d: %20s %s\n", i, clazz->ifields[i].field.name,
+ clazz->ifields[i].field.signature);
+ }
+ }
+ return 0;
+}
+
+/*
+ * Dump the contents of a single class.
+ *
+ * Pass kDumpClassFullDetail into "flags" to get lots of detail.
+ */
+void dvmDumpClass(const ClassObject* clazz, int flags)
+{
+ dumpClass((void*) clazz, (void*) flags);
+}
+
+/*
+ * Dump the contents of all classes.
+ */
+void dvmDumpAllClasses(int flags)
+{
+ dvmHashTableLock(gDvm.loadedClasses);
+ dvmHashForeach(gDvm.loadedClasses, dumpClass, (void*) flags);
+ dvmHashTableUnlock(gDvm.loadedClasses);
+}
+
+/*
+ * Get the number of loaded classes
+ */
+int dvmGetNumLoadedClasses()
+{
+ int count;
+ dvmHashTableLock(gDvm.loadedClasses);
+ count = dvmHashTableNumEntries(gDvm.loadedClasses);
+ dvmHashTableUnlock(gDvm.loadedClasses);
+ return count;
+}
+
+/*
+ * Write some statistics to the log file.
+ */
+void dvmDumpLoaderStats(const char* msg)
+{
+ LOGV("VM stats (%s): cls=%d/%d meth=%d ifld=%d sfld=%d linear=%d\n",
+ msg, gDvm.numLoadedClasses, dvmHashTableNumEntries(gDvm.loadedClasses),
+ gDvm.numDeclaredMethods, gDvm.numDeclaredInstFields,
+ gDvm.numDeclaredStaticFields, gDvm.pBootLoaderAlloc->curOffset);
+#ifdef COUNT_PRECISE_METHODS
+ LOGI("GC precise methods: %d\n",
+ dvmPointerSetGetCount(gDvm.preciseMethods));
+#endif
+}
+
+#ifdef PROFILE_FIELD_ACCESS
+/*
+ * Dump the field access counts for all fields in this method.
+ */
+static int dumpAccessCounts(void* vclazz, void* varg)
+{
+ const ClassObject* clazz = (const ClassObject*) vclazz;
+ int i;
+
+ for (i = 0; i < clazz->ifieldCount; i++) {
+ Field* field = &clazz->ifields[i].field;
+
+ if (field->gets != 0)
+ printf("GI %d %s.%s\n", field->gets,
+ field->clazz->descriptor, field->name);
+ if (field->puts != 0)
+ printf("PI %d %s.%s\n", field->puts,
+ field->clazz->descriptor, field->name);
+ }
+ for (i = 0; i < clazz->sfieldCount; i++) {
+ Field* field = &clazz->sfields[i].field;
+
+ if (field->gets != 0)
+ printf("GS %d %s.%s\n", field->gets,
+ field->clazz->descriptor, field->name);
+ if (field->puts != 0)
+ printf("PS %d %s.%s\n", field->puts,
+ field->clazz->descriptor, field->name);
+ }
+
+ return 0;
+}
+
+/*
+ * Dump the field access counts for all loaded classes.
+ */
+void dvmDumpFieldAccessCounts(void)
+{
+ dvmHashTableLock(gDvm.loadedClasses);
+ dvmHashForeach(gDvm.loadedClasses, dumpAccessCounts, NULL);
+ dvmHashTableUnlock(gDvm.loadedClasses);
+}
+#endif
+
+
+/*
+ * Mark all classes associated with the built-in loader.
+ */
+static int markClassObject(void *clazz, void *arg)
+{
+ UNUSED_PARAMETER(arg);
+
+ dvmMarkObjectNonNull((Object *)clazz);
+ return 0;
+}
+
+/*
+ * The garbage collector calls this to mark the class objects for all
+ * loaded classes.
+ */
+void dvmGcScanRootClassLoader()
+{
+ /* dvmClassStartup() may not have been called before the first GC.
+ */
+ if (gDvm.loadedClasses != NULL) {
+ dvmHashTableLock(gDvm.loadedClasses);
+ dvmHashForeach(gDvm.loadedClasses, markClassObject, NULL);
+ dvmHashTableUnlock(gDvm.loadedClasses);
+ }
+}
+
+
+/*
+ * ===========================================================================
+ * Method Prototypes and Descriptors
+ * ===========================================================================
+ */
+
+/*
+ * Compare the two method names and prototypes, a la strcmp(). The
+ * name is considered the "major" order and the prototype the "minor"
+ * order. The prototypes are compared as if by dvmCompareMethodProtos().
+ */
+int dvmCompareMethodNamesAndProtos(const Method* method1,
+ const Method* method2)
+{
+ int result = strcmp(method1->name, method2->name);
+
+ if (result != 0) {
+ return result;
+ }
+
+ return dvmCompareMethodProtos(method1, method2);
+}
+
+/*
+ * Compare the two method names and prototypes, a la strcmp(), ignoring
+ * the return value. The name is considered the "major" order and the
+ * prototype the "minor" order. The prototypes are compared as if by
+ * dvmCompareMethodArgProtos().
+ */
+int dvmCompareMethodNamesAndParameterProtos(const Method* method1,
+ const Method* method2)
+{
+ int result = strcmp(method1->name, method2->name);
+
+ if (result != 0) {
+ return result;
+ }
+
+ return dvmCompareMethodParameterProtos(method1, method2);
+}
+
+/*
+ * Compare a (name, prototype) pair with the (name, prototype) of
+ * a method, a la strcmp(). The name is considered the "major" order and
+ * the prototype the "minor" order. The descriptor and prototype are
+ * compared as if by dvmCompareDescriptorAndMethodProto().
+ */
+int dvmCompareNameProtoAndMethod(const char* name,
+ const DexProto* proto, const Method* method)
+{
+ int result = strcmp(name, method->name);
+
+ if (result != 0) {
+ return result;
+ }
+
+ return dexProtoCompare(proto, &method->prototype);
+}
+
+/*
+ * Compare a (name, method descriptor) pair with the (name, prototype) of
+ * a method, a la strcmp(). The name is considered the "major" order and
+ * the prototype the "minor" order. The descriptor and prototype are
+ * compared as if by dvmCompareDescriptorAndMethodProto().
+ */
+int dvmCompareNameDescriptorAndMethod(const char* name,
+ const char* descriptor, const Method* method)
+{
+ int result = strcmp(name, method->name);
+
+ if (result != 0) {
+ return result;
+ }
+
+ return dvmCompareDescriptorAndMethodProto(descriptor, method);
+}
diff --git a/vm/oo/Class.h b/vm/oo/Class.h
new file mode 100644
index 0000000..e27ef79
--- /dev/null
+++ b/vm/oo/Class.h
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Class loader.
+ */
+#ifndef _DALVIK_OO_CLASS
+#define _DALVIK_OO_CLASS
+
+/*
+ * The classpath and bootclasspath differ in that only the latter is
+ * consulted when looking for classes needed by the VM. When searching
+ * for an arbitrary class definition, we start with the bootclasspath,
+ * look for optional packages (a/k/a standard extensions), and then try
+ * the classpath.
+ *
+ * In Dalvik, a class can be found in one of three ways:
+ * - as a "loose" .class file in a directory
+ * - as a .class file held in a JAR archive
+ * - in a .dex file
+ *
+ * These three may be freely intermixed in a classpath specification.
+ * Ordering is significant. (Currently only ".dex" is supported directly
+ * by the VM.)
+ */
+typedef struct ClassPathEntry {
+ enum {
+ kCpeUnknown = 0,
+ kCpeDir,
+ kCpeJar,
+ kCpeDex,
+ kCpeLastEntry /* used as sentinel at end of array */
+ } kind;
+ char* fileName;
+ void* ptr; /* JarFile* or DexFile* */
+} ClassPathEntry;
+
+bool dvmClassStartup(void);
+void dvmClassShutdown(void);
+bool dvmPrepBootClassPath(bool isNormalStart);
+
+/*
+ * Boot class path accessors, for class loader getResources().
+ */
+int dvmGetBootPathSize(void);
+StringObject* dvmGetBootPathResource(const char* name, int idx);
+void dvmDumpBootClassPath(void);
+
+/*
+ * Determine whether "path" is a member of "cpe".
+ */
+bool dvmClassPathContains(const ClassPathEntry* cpe, const char* path);
+
+/*
+ * Set clazz->serialNumber to the next available value.
+ */
+void dvmSetClassSerialNumber(ClassObject* clazz);
+
+/*
+ * Find the class with the given descriptor. Load it if it hasn't already
+ * been.
+ *
+ * "loader" is the initiating class loader.
+ */
+ClassObject* dvmFindClass(const char* descriptor, Object* loader);
+ClassObject* dvmFindClassNoInit(const char* descriptor, Object* loader);
+
+/*
+ * Like dvmFindClass, but only for system classes.
+ */
+ClassObject* dvmFindSystemClass(const char* descriptor);
+ClassObject* dvmFindSystemClassNoInit(const char* descriptor);
+
+/*
+ * Find a loaded class by descriptor. Returns the first one found.
+ * Because there can be more than one if class loaders are involved,
+ * this is not an especially good API. (Currently only used by the
+ * debugger and "checking" JNI.)
+ *
+ * "descriptor" should have the form "Ljava/lang/Class;" or
+ * "[Ljava/lang/Class;", i.e. a descriptor and not an internal-form
+ * class name.
+ */
+ClassObject* dvmFindLoadedClass(const char* descriptor);
+
+/*
+ * Load the named class (by descriptor) from the specified DEX file.
+ * Used by class loaders to instantiate a class object from a
+ * VM-managed DEX.
+ */
+ClassObject* dvmDefineClass(DvmDex* pDvmDex, const char* descriptor,
+ Object* classLoader);
+
+/*
+ * Link a loaded class. Normally done as part of one of the "find class"
+ * variations, this is only called explicitly for synthetic class
+ * generation (e.g. reflect.Proxy).
+ */
+bool dvmLinkClass(ClassObject* clazz);
+
+/*
+ * Determine if a class has been initialized.
+ */
+INLINE bool dvmIsClassInitialized(const ClassObject* clazz) {
+ return (clazz->status == CLASS_INITIALIZED);
+}
+bool dvmIsClassInitializing(const ClassObject* clazz);
+
+/*
+ * Initialize a class.
+ */
+bool dvmInitClass(ClassObject* clazz);
+
+/*
+ * Retrieve the system class loader.
+ */
+Object* dvmGetSystemClassLoader(void);
+
+/*
+ * Utility functions.
+ */
+ClassObject* dvmLookupClass(const char* descriptor, Object* loader,
+ bool unprepOkay);
+void dvmFreeClassInnards(ClassObject* clazz);
+bool dvmAddClassToHash(ClassObject* clazz);
+void dvmAddInitiatingLoader(ClassObject* clazz, Object* loader);
+bool dvmLoaderInInitiatingList(const ClassObject* clazz, const Object* loader);
+
+/*
+ * Update method's "nativeFunc" and "insns". If "insns" is NULL, the
+ * current method->insns value is not changed.
+ */
+void dvmSetNativeFunc(Method* method, DalvikBridgeFunc func, const u2* insns);
+
+/*
+ * Set the method's "registerMap" field.
+ */
+void dvmSetRegisterMap(Method* method, const RegisterMap* pMap);
+
+/*
+ * Make a method's DexCode (which includes the bytecode) read-write or
+ * read-only. The conversion to read-write may involve making a new copy
+ * of the DexCode, and in normal operation the read-only state is not
+ * actually enforced.
+ */
+void dvmMakeCodeReadWrite(Method* meth);
+void dvmMakeCodeReadOnly(Method* meth);
+
+/*
+ * During DEX optimizing, add an extra DEX to the bootstrap class path.
+ */
+void dvmSetBootPathExtraDex(DvmDex* pDvmDex);
+
+/*
+ * Debugging.
+ */
+void dvmDumpClass(const ClassObject* clazz, int flags);
+void dvmDumpAllClasses(int flags);
+void dvmDumpLoaderStats(const char* msg);
+int dvmGetNumLoadedClasses();
+
+#ifdef PROFILE_FIELD_ACCESS
+void dvmDumpFieldAccessCounts(void);
+#endif
+
+/* flags for dvmDumpClass / dvmDumpAllClasses */
+#define kDumpClassFullDetail 1
+#define kDumpClassClassLoader (1 << 1)
+#define kDumpClassInitialized (1 << 2)
+
+
+/*
+ * Store a copy of the method prototype descriptor string
+ * for the given method into the given DexStringCache, returning the
+ * stored string for convenience.
+ */
+INLINE char* dvmCopyDescriptorStringFromMethod(const Method* method,
+ DexStringCache *pCache)
+{
+ const char* result =
+ dexProtoGetMethodDescriptor(&method->prototype, pCache);
+ return dexStringCacheEnsureCopy(pCache, result);
+}
+
+/*
+ * Compute the number of argument words (u4 units) required by the
+ * given method's prototype. For example, if the method descriptor is
+ * "(IJ)D", this would return 3 (one for the int, two for the long;
+ * return value isn't relevant).
+ */
+INLINE int dvmComputeMethodArgsSize(const Method* method)
+{
+ return dexProtoComputeArgsSize(&method->prototype);
+}
+
+/*
+ * Compare the two method prototypes. The two prototypes are compared
+ * as if by strcmp() on the result of dexProtoGetMethodDescriptor().
+ */
+INLINE int dvmCompareMethodProtos(const Method* method1,
+ const Method* method2)
+{
+ return dexProtoCompare(&method1->prototype, &method2->prototype);
+}
+
+/*
+ * Compare the two method prototypes, considering only the parameters
+ * (i.e. ignoring the return types). The two prototypes are compared
+ * as if by strcmp() on the result of dexProtoGetMethodDescriptor().
+ */
+INLINE int dvmCompareMethodParameterProtos(const Method* method1,
+ const Method* method2)
+{
+ return dexProtoCompareParameters(&method1->prototype, &method2->prototype);
+}
+
+/*
+ * Compare the two method names and prototypes, a la strcmp(). The
+ * name is considered the "major" order and the prototype the "minor"
+ * order. The prototypes are compared as if by dexProtoGetMethodDescriptor().
+ */
+int dvmCompareMethodNamesAndProtos(const Method* method1,
+ const Method* method2);
+
+/*
+ * Compare the two method names and prototypes, a la strcmp(), ignoring
+ * the return type. The name is considered the "major" order and the
+ * prototype the "minor" order. The prototypes are compared as if by
+ * dexProtoGetMethodDescriptor().
+ */
+int dvmCompareMethodNamesAndParameterProtos(const Method* method1,
+ const Method* method2);
+
+/*
+ * Compare a method descriptor string with the prototype of a method,
+ * as if by converting the descriptor to a DexProto and comparing it
+ * with dexProtoCompare().
+ */
+INLINE int dvmCompareDescriptorAndMethodProto(const char* descriptor,
+ const Method* method)
+{
+ // Sense is reversed.
+ return -dexProtoCompareToDescriptor(&method->prototype, descriptor);
+}
+
+/*
+ * Compare a (name, prototype) pair with the (name, prototype) of
+ * a method, a la strcmp(). The name is considered the "major" order and
+ * the prototype the "minor" order. The descriptor and prototype are
+ * compared as if by dvmCompareDescriptorAndMethodProto().
+ */
+int dvmCompareNameProtoAndMethod(const char* name,
+ const DexProto* proto, const Method* method);
+
+/*
+ * Compare a (name, method descriptor) pair with the (name, prototype) of
+ * a method, a la strcmp(). The name is considered the "major" order and
+ * the prototype the "minor" order. The descriptor and prototype are
+ * compared as if by dvmCompareDescriptorAndMethodProto().
+ */
+int dvmCompareNameDescriptorAndMethod(const char* name,
+ const char* descriptor, const Method* method);
+
+/*
+ * Returns the size of the given class object in bytes.
+ */
+size_t dvmClassObjectSize(const ClassObject *clazz);
+
+#endif /*_DALVIK_OO_CLASS*/
diff --git a/vm/oo/Object.c b/vm/oo/Object.c
new file mode 100644
index 0000000..32ed679
--- /dev/null
+++ b/vm/oo/Object.c
@@ -0,0 +1,776 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Operations on an Object.
+ */
+#include "Dalvik.h"
+
+/*
+ * Find a matching field, in the current class only.
+ *
+ * Returns NULL if the field can't be found. (Does not throw an exception.)
+ */
+InstField* dvmFindInstanceField(const ClassObject* clazz,
+ const char* fieldName, const char* signature)
+{
+ InstField* pField;
+ int i;
+
+ assert(clazz != NULL);
+
+ /*
+ * Find a field with a matching name and signature. The Java programming
+ * language does not allow you to have two fields with the same name
+ * and different types, but the Java VM spec does allow it, so we can't
+ * bail out early when the name matches.
+ */
+ pField = clazz->ifields;
+ for (i = 0; i < clazz->ifieldCount; i++, pField++) {
+ if (strcmp(fieldName, pField->field.name) == 0 &&
+ strcmp(signature, pField->field.signature) == 0)
+ {
+ return pField;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Find a matching field, in this class or a superclass.
+ *
+ * Searching through interfaces isn't necessary, because interface fields
+ * are inherently public/static/final.
+ *
+ * Returns NULL if the field can't be found. (Does not throw an exception.)
+ */
+InstField* dvmFindInstanceFieldHier(const ClassObject* clazz,
+ const char* fieldName, const char* signature)
+{
+ InstField* pField;
+
+ /*
+ * Search for a match in the current class.
+ */
+ pField = dvmFindInstanceField(clazz, fieldName, signature);
+ if (pField != NULL)
+ return pField;
+
+ if (clazz->super != NULL)
+ return dvmFindInstanceFieldHier(clazz->super, fieldName, signature);
+ else
+ return NULL;
+}
+
+
+/*
+ * Find a matching field, in this class or an interface.
+ *
+ * Returns NULL if the field can't be found. (Does not throw an exception.)
+ */
+StaticField* dvmFindStaticField(const ClassObject* clazz,
+ const char* fieldName, const char* signature)
+{
+ const StaticField* pField;
+ int i;
+
+ assert(clazz != NULL);
+
+ /*
+ * Find a field with a matching name and signature. As with instance
+ * fields, the VM allows you to have two fields with the same name so
+ * long as they have different types.
+ */
+ pField = &clazz->sfields[0];
+ for (i = 0; i < clazz->sfieldCount; i++, pField++) {
+ if (strcmp(fieldName, pField->field.name) == 0 &&
+ strcmp(signature, pField->field.signature) == 0)
+ {
+ return (StaticField*) pField;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Find a matching field, in this class or a superclass.
+ *
+ * Returns NULL if the field can't be found. (Does not throw an exception.)
+ */
+StaticField* dvmFindStaticFieldHier(const ClassObject* clazz,
+ const char* fieldName, const char* signature)
+{
+ StaticField* pField;
+
+ /*
+ * Search for a match in the current class.
+ */
+ pField = dvmFindStaticField(clazz, fieldName, signature);
+ if (pField != NULL)
+ return pField;
+
+ /*
+ * See if it's in any of our interfaces. We don't check interfaces
+ * inherited from the superclass yet.
+ *
+ * (Note the set may have been stripped down because of redundancy with
+ * the superclass; see notes in createIftable.)
+ */
+ int i = 0;
+ if (clazz->super != NULL) {
+ assert(clazz->iftableCount >= clazz->super->iftableCount);
+ i = clazz->super->iftableCount;
+ }
+ for ( ; i < clazz->iftableCount; i++) {
+ ClassObject* iface = clazz->iftable[i].clazz;
+ pField = dvmFindStaticField(iface, fieldName, signature);
+ if (pField != NULL)
+ return pField;
+ }
+
+ if (clazz->super != NULL)
+ return dvmFindStaticFieldHier(clazz->super, fieldName, signature);
+ else
+ return NULL;
+}
+
+/*
+ * Find a matching field, in this class or a superclass.
+ *
+ * We scan both the static and instance field lists in the class. If it's
+ * not found there, we check the direct interfaces, and then recursively
+ * scan the superclasses. This is the order prescribed in the VM spec
+ * (v2 5.4.3.2).
+ *
+ * In most cases we know that we're looking for either a static or an
+ * instance field and there's no value in searching through both types.
+ * During verification we need to recognize and reject certain unusual
+ * situations, and we won't see them unless we walk the lists this way.
+ */
+Field* dvmFindFieldHier(const ClassObject* clazz, const char* fieldName,
+ const char* signature)
+{
+ Field* pField;
+
+ /*
+ * Search for a match in the current class. Which set we scan first
+ * doesn't really matter.
+ */
+ pField = (Field*) dvmFindStaticField(clazz, fieldName, signature);
+ if (pField != NULL)
+ return pField;
+ pField = (Field*) dvmFindInstanceField(clazz, fieldName, signature);
+ if (pField != NULL)
+ return pField;
+
+ /*
+ * See if it's in any of our interfaces. We don't check interfaces
+ * inherited from the superclass yet.
+ */
+ int i = 0;
+ if (clazz->super != NULL) {
+ assert(clazz->iftableCount >= clazz->super->iftableCount);
+ i = clazz->super->iftableCount;
+ }
+ for ( ; i < clazz->iftableCount; i++) {
+ ClassObject* iface = clazz->iftable[i].clazz;
+ pField = (Field*) dvmFindStaticField(iface, fieldName, signature);
+ if (pField != NULL)
+ return pField;
+ }
+
+ if (clazz->super != NULL)
+ return dvmFindFieldHier(clazz->super, fieldName, signature);
+ else
+ return NULL;
+}
+
+
+/*
+ * Compare the given name, return type, and argument types with the contents
+ * of the given method. This returns 0 if they are equal and non-zero if not.
+ */
+static inline int compareMethodHelper(Method* method, const char* methodName,
+ const char* returnType, size_t argCount, const char** argTypes)
+{
+ DexParameterIterator iterator;
+ const DexProto* proto;
+
+ if (strcmp(methodName, method->name) != 0) {
+ return 1;
+ }
+
+ proto = &method->prototype;
+
+ if (strcmp(returnType, dexProtoGetReturnType(proto)) != 0) {
+ return 1;
+ }
+
+ if (dexProtoGetParameterCount(proto) != argCount) {
+ return 1;
+ }
+
+ dexParameterIteratorInit(&iterator, proto);
+
+ for (/*argCount*/; argCount != 0; argCount--, argTypes++) {
+ const char* argType = *argTypes;
+ const char* paramType = dexParameterIteratorNextDescriptor(&iterator);
+
+ if (paramType == NULL) {
+ /* Param list ended early; no match */
+ break;
+ } else if (strcmp(argType, paramType) != 0) {
+ /* Types aren't the same; no match. */
+ break;
+ }
+ }
+
+ if (argCount == 0) {
+ /* We ran through all the given arguments... */
+ if (dexParameterIteratorNextDescriptor(&iterator) == NULL) {
+ /* ...and through all the method's arguments; success! */
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/*
+ * Get the count of arguments in the given method descriptor string,
+ * and also find a pointer to the return type.
+ */
+static inline size_t countArgsAndFindReturnType(const char* descriptor,
+ const char** pReturnType)
+{
+ size_t count = 0;
+ bool bogus = false;
+ bool done = false;
+
+ assert(*descriptor == '(');
+ descriptor++;
+
+ while (!done) {
+ switch (*descriptor) {
+ case 'B': case 'C': case 'D': case 'F':
+ case 'I': case 'J': case 'S': case 'Z': {
+ count++;
+ break;
+ }
+ case '[': {
+ do {
+ descriptor++;
+ } while (*descriptor == '[');
+ /*
+ * Don't increment count, as it will be taken care of
+ * by the next iteration. Also, decrement descriptor
+ * to compensate for the increment below the switch.
+ */
+ descriptor--;
+ break;
+ }
+ case 'L': {
+ do {
+ descriptor++;
+ } while ((*descriptor != ';') && (*descriptor != '\0'));
+ count++;
+ if (*descriptor == '\0') {
+ /* Bogus descriptor. */
+ done = true;
+ bogus = true;
+ }
+ break;
+ }
+ case ')': {
+ /*
+ * Note: The loop will exit after incrementing descriptor
+ * one more time, so it then points at the return type.
+ */
+ done = true;
+ break;
+ }
+ default: {
+ /* Bogus descriptor. */
+ done = true;
+ bogus = true;
+ break;
+ }
+ }
+
+ descriptor++;
+ }
+
+ if (bogus) {
+ *pReturnType = NULL;
+ return 0;
+ }
+
+ *pReturnType = descriptor;
+ return count;
+}
+
+/*
+ * Copy the argument types into the given array using the given buffer
+ * for the contents.
+ */
+static inline void copyTypes(char* buffer, const char** argTypes,
+ size_t argCount, const char* descriptor)
+{
+ size_t i;
+ char c;
+
+ /* Skip the '('. */
+ descriptor++;
+
+ for (i = 0; i < argCount; i++) {
+ argTypes[i] = buffer;
+
+ /* Copy all the array markers and one extra character. */
+ do {
+ c = *(descriptor++);
+ *(buffer++) = c;
+ } while (c == '[');
+
+ if (c == 'L') {
+ /* Copy the rest of a class name. */
+ do {
+ c = *(descriptor++);
+ *(buffer++) = c;
+ } while (c != ';');
+ }
+
+ *(buffer++) = '\0';
+ }
+}
+
+/*
+ * Look for a match in the given class. Returns the match if found
+ * or NULL if not.
+ */
+static Method* findMethodInListByDescriptor(const ClassObject* clazz,
+ bool findVirtual, bool isHier, const char* name, const char* descriptor)
+{
+ const char* returnType;
+ size_t argCount = countArgsAndFindReturnType(descriptor, &returnType);
+
+ if (returnType == NULL) {
+ LOGW("Bogus method descriptor: %s\n", descriptor);
+ return NULL;
+ }
+
+ /*
+ * Make buffer big enough for all the argument type characters and
+ * one '\0' per argument. The "- 2" is because "returnType -
+ * descriptor" includes two parens.
+ */
+ char buffer[argCount + (returnType - descriptor) - 2];
+ const char* argTypes[argCount];
+
+ copyTypes(buffer, argTypes, argCount, descriptor);
+
+ while (clazz != NULL) {
+ Method* methods;
+ size_t methodCount;
+ size_t i;
+
+ if (findVirtual) {
+ methods = clazz->virtualMethods;
+ methodCount = clazz->virtualMethodCount;
+ } else {
+ methods = clazz->directMethods;
+ methodCount = clazz->directMethodCount;
+ }
+
+ for (i = 0; i < methodCount; i++) {
+ Method* method = &methods[i];
+ if (compareMethodHelper(method, name, returnType, argCount,
+ argTypes) == 0) {
+ return method;
+ }
+ }
+
+ if (! isHier) {
+ break;
+ }
+
+ clazz = clazz->super;
+ }
+
+ return NULL;
+}
+
+/*
+ * Look for a match in the given clazz. Returns the match if found
+ * or NULL if not.
+ *
+ * "wantedType" should be METHOD_VIRTUAL or METHOD_DIRECT to indicate the
+ * list to search through. If the match can come from either list, use
+ * MATCH_UNKNOWN to scan both.
+ */
+static Method* findMethodInListByProto(const ClassObject* clazz,
+ MethodType wantedType, bool isHier, const char* name, const DexProto* proto)
+{
+ while (clazz != NULL) {
+ int i;
+
+ /*
+ * Check the virtual and/or direct method lists.
+ */
+ if (wantedType == METHOD_VIRTUAL || wantedType == METHOD_UNKNOWN) {
+ for (i = 0; i < clazz->virtualMethodCount; i++) {
+ Method* method = &clazz->virtualMethods[i];
+ if (dvmCompareNameProtoAndMethod(name, proto, method) == 0) {
+ return method;
+ }
+ }
+ }
+ if (wantedType == METHOD_DIRECT || wantedType == METHOD_UNKNOWN) {
+ for (i = 0; i < clazz->directMethodCount; i++) {
+ Method* method = &clazz->directMethods[i];
+ if (dvmCompareNameProtoAndMethod(name, proto, method) == 0) {
+ return method;
+ }
+ }
+ }
+
+ if (! isHier) {
+ break;
+ }
+
+ clazz = clazz->super;
+ }
+
+ return NULL;
+}
+
+/*
+ * Find a "virtual" method in a class.
+ *
+ * Does not chase into the superclass.
+ *
+ * Returns NULL if the method can't be found. (Does not throw an exception.)
+ */
+Method* dvmFindVirtualMethodByDescriptor(const ClassObject* clazz,
+ const char* methodName, const char* descriptor)
+{
+ return findMethodInListByDescriptor(clazz, true, false,
+ methodName, descriptor);
+
+ // TODO? - throw IncompatibleClassChangeError if a match is
+ // found in the directMethods list, rather than NotFoundError.
+ // Note we could have been called by dvmFindVirtualMethodHier though.
+}
+
+
+/*
+ * Find a "virtual" method in a class, knowing only the name. This is
+ * only useful in limited circumstances, e.g. when searching for a member
+ * of an annotation class.
+ *
+ * Does not chase into the superclass.
+ *
+ * Returns NULL if the method can't be found. (Does not throw an exception.)
+ */
+Method* dvmFindVirtualMethodByName(const ClassObject* clazz,
+ const char* methodName)
+{
+ Method* methods = clazz->virtualMethods;
+ int methodCount = clazz->virtualMethodCount;
+ int i;
+
+ for (i = 0; i < methodCount; i++) {
+ if (strcmp(methods[i].name, methodName) == 0)
+ return &methods[i];
+ }
+
+ return NULL;
+}
+
+/*
+ * Find a "virtual" method in a class.
+ *
+ * Does not chase into the superclass.
+ *
+ * Returns NULL if the method can't be found. (Does not throw an exception.)
+ */
+Method* dvmFindVirtualMethod(const ClassObject* clazz, const char* methodName,
+ const DexProto* proto)
+{
+ return findMethodInListByProto(clazz, METHOD_VIRTUAL, false, methodName,
+ proto);
+}
+
+/*
+ * Find a "virtual" method in a class. If we don't find it, try the
+ * superclass.
+ *
+ * Returns NULL if the method can't be found. (Does not throw an exception.)
+ */
+Method* dvmFindVirtualMethodHierByDescriptor(const ClassObject* clazz,
+ const char* methodName, const char* descriptor)
+{
+ return findMethodInListByDescriptor(clazz, true, true,
+ methodName, descriptor);
+}
+
+/*
+ * Find a "virtual" method in a class. If we don't find it, try the
+ * superclass.
+ *
+ * Returns NULL if the method can't be found. (Does not throw an exception.)
+ */
+Method* dvmFindVirtualMethodHier(const ClassObject* clazz,
+ const char* methodName, const DexProto* proto)
+{
+ return findMethodInListByProto(clazz, METHOD_VIRTUAL, true, methodName,
+ proto);
+}
+
+/*
+ * Find a "direct" method (static, private, or "<*init>").
+ *
+ * Returns NULL if the method can't be found. (Does not throw an exception.)
+ */
+Method* dvmFindDirectMethodByDescriptor(const ClassObject* clazz,
+ const char* methodName, const char* descriptor)
+{
+ return findMethodInListByDescriptor(clazz, false, false,
+ methodName, descriptor);
+}
+
+/*
+ * Find a "direct" method. If we don't find it, try the superclass. This
+ * is only appropriate for static methods, but will work for all direct
+ * methods.
+ *
+ * Returns NULL if the method can't be found. (Does not throw an exception.)
+ */
+Method* dvmFindDirectMethodHierByDescriptor(const ClassObject* clazz,
+ const char* methodName, const char* descriptor)
+{
+ return findMethodInListByDescriptor(clazz, false, true,
+ methodName, descriptor);
+}
+
+/*
+ * Find a "direct" method (static or "<*init>").
+ *
+ * Returns NULL if the method can't be found. (Does not throw an exception.)
+ */
+Method* dvmFindDirectMethod(const ClassObject* clazz, const char* methodName,
+ const DexProto* proto)
+{
+ return findMethodInListByProto(clazz, METHOD_DIRECT, false, methodName,
+ proto);
+}
+
+/*
+ * Find a "direct" method in a class. If we don't find it, try the
+ * superclass.
+ *
+ * Returns NULL if the method can't be found. (Does not throw an exception.)
+ */
+Method* dvmFindDirectMethodHier(const ClassObject* clazz,
+ const char* methodName, const DexProto* proto)
+{
+ return findMethodInListByProto(clazz, METHOD_DIRECT, true, methodName,
+ proto);
+}
+
+/*
+ * Find a virtual or static method in a class. If we don't find it, try the
+ * superclass. This is compatible with the VM spec (v2 5.4.3.3) method
+ * search order, but it stops short of scanning through interfaces (which
+ * should be done after this function completes).
+ *
+ * In most cases we know that we're looking for either a static or an
+ * instance field and there's no value in searching through both types.
+ * During verification we need to recognize and reject certain unusual
+ * situations, and we won't see them unless we walk the lists this way.
+ *
+ * Returns NULL if the method can't be found. (Does not throw an exception.)
+ */
+Method* dvmFindMethodHier(const ClassObject* clazz, const char* methodName,
+ const DexProto* proto)
+{
+ return findMethodInListByProto(clazz, METHOD_UNKNOWN, true, methodName,
+ proto);
+}
+
+
+/*
+ * We have a method pointer for a method in "clazz", but it might be
+ * pointing to a method in a derived class. We want to find the actual entry
+ * from the class' vtable. If "clazz" is an interface, we have to do a
+ * little more digging.
+ *
+ * (This is used for reflection and JNI "call method" calls.)
+ */
+const Method* dvmGetVirtualizedMethod(const ClassObject* clazz,
+ const Method* meth)
+{
+ Method* actualMeth;
+ int methodIndex;
+
+ assert(!dvmIsStaticMethod(meth));
+
+ if (dvmIsPrivateMethod(meth)) // no vtable entry for these
+ return meth;
+
+ /*
+ * If the method was declared in an interface, we need to scan through
+ * the class' list of interfaces for it, and find the vtable index
+ * from that.
+ *
+ * TODO: use the interface cache.
+ */
+ if (dvmIsInterfaceClass(meth->clazz)) {
+ int i;
+
+ for (i = 0; i < clazz->iftableCount; i++) {
+ if (clazz->iftable[i].clazz == meth->clazz)
+ break;
+ }
+ if (i == clazz->iftableCount) {
+ dvmThrowException("Ljava/lang/IncompatibleClassChangeError;",
+ "invoking method from interface not implemented by class");
+ return NULL;
+ }
+
+ methodIndex = clazz->iftable[i].methodIndexArray[meth->methodIndex];
+ } else {
+ methodIndex = meth->methodIndex;
+ }
+
+ assert(methodIndex >= 0 && methodIndex < clazz->vtableCount);
+ actualMeth = clazz->vtable[methodIndex];
+
+ /*
+ * Make sure there's code to execute.
+ */
+ if (dvmIsAbstractMethod(actualMeth)) {
+ dvmThrowException("Ljava/lang/AbstractMethodError;", NULL);
+ return NULL;
+ }
+ assert(!dvmIsMirandaMethod(actualMeth));
+
+ return actualMeth;
+}
+
+/*
+ * Get the source file for a method.
+ */
+const char* dvmGetMethodSourceFile(const Method* meth)
+{
+ /*
+ * TODO: A method's debug info can override the default source
+ * file for a class, so we should account for that possibility
+ * here.
+ */
+ return meth->clazz->sourceFile;
+}
+
+/*
+ * Dump some information about an object.
+ */
+void dvmDumpObject(const Object* obj)
+{
+ ClassObject* clazz;
+ int i;
+
+ if (obj == NULL || obj->clazz == NULL) {
+ LOGW("Null or malformed object not dumped");
+ return;
+ }
+
+ clazz = obj->clazz;
+ LOGD("----- Object dump: %p (%s, %d bytes) -----",
+ obj, clazz->descriptor, (int) clazz->objectSize);
+ //printHexDump(obj, clazz->objectSize);
+ LOGD(" Fields:");
+ while (clazz != NULL) {
+ LOGD(" -- %s", clazz->descriptor);
+ for (i = 0; i < clazz->ifieldCount; i++) {
+ const InstField* pField = &clazz->ifields[i];
+ char type = pField->field.signature[0];
+
+ if (type == 'F' || type == 'D') {
+ double dval;
+
+ if (type == 'F')
+ dval = dvmGetFieldFloat(obj, pField->byteOffset);
+ else
+ dval = dvmGetFieldDouble(obj, pField->byteOffset);
+
+ LOGD(" %2d: '%s' '%s' af=%04x off=%d %.3f", i,
+ pField->field.name, pField->field.signature,
+ pField->field.accessFlags, pField->byteOffset, dval);
+ } else {
+ u8 lval;
+
+ if (type == 'J')
+ lval = dvmGetFieldLong(obj, pField->byteOffset);
+ else if (type == 'Z')
+ lval = dvmGetFieldBoolean(obj, pField->byteOffset);
+ else
+ lval = dvmGetFieldInt(obj, pField->byteOffset);
+
+ LOGD(" %2d: '%s' '%s' af=%04x off=%d 0x%08llx", i,
+ pField->field.name, pField->field.signature,
+ pField->field.accessFlags, pField->byteOffset, lval);
+ }
+ }
+
+ clazz = clazz->super;
+ }
+ if (obj->clazz == gDvm.classJavaLangClass) {
+ LOGD(" Static fields:");
+ const StaticField* sfields = &((ClassObject *)obj)->sfields[0];
+ for (i = 0; i < ((ClassObject *)obj)->sfieldCount; ++i) {
+ const StaticField* pField = &sfields[i];
+ size_t byteOffset = (size_t)pField - (size_t)sfields;
+ char type = pField->field.signature[0];
+
+ if (type == 'F' || type == 'D') {
+ double dval;
+
+ if (type == 'F')
+ dval = pField->value.f;
+ else
+ dval = pField->value.d;
+
+ LOGD(" %2d: '%s' '%s' af=%04x off=%zd %.3f", i,
+ pField->field.name, pField->field.signature,
+ pField->field.accessFlags, byteOffset, dval);
+ } else {
+ u8 lval;
+
+ if (type == 'J')
+ lval = pField->value.j;
+ else if (type == 'Z')
+ lval = pField->value.z;
+ else
+ lval = pField->value.i;
+
+ LOGD(" %2d: '%s' '%s' af=%04x off=%zd 0x%08llx", i,
+ pField->field.name, pField->field.signature,
+ pField->field.accessFlags, byteOffset, lval);
+ }
+ }
+ }
+}
diff --git a/vm/oo/Object.h b/vm/oo/Object.h
new file mode 100644
index 0000000..903450f
--- /dev/null
+++ b/vm/oo/Object.h
@@ -0,0 +1,794 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Declaration of the fundamental Object type and refinements thereof, plus
+ * some functions for manipulating them.
+ */
+#ifndef _DALVIK_OO_OBJECT
+#define _DALVIK_OO_OBJECT
+
+#include <Atomic.h>
+
+#include <stddef.h>
+
+/* fwd decl */
+struct DataObject;
+struct InitiatingLoaderList;
+struct ClassObject;
+struct StringObject;
+struct ArrayObject;
+struct Method;
+struct ExceptionEntry;
+struct LineNumEntry;
+struct StaticField;
+struct InstField;
+struct Field;
+struct RegisterMap;
+typedef struct DataObject DataObject;
+typedef struct InitiatingLoaderList InitiatingLoaderList;
+typedef struct ClassObject ClassObject;
+typedef struct StringObject StringObject;
+typedef struct ArrayObject ArrayObject;
+typedef struct Method Method;
+typedef struct ExceptionEntry ExceptionEntry;
+typedef struct LineNumEntry LineNumEntry;
+typedef struct StaticField StaticField;
+typedef struct InstField InstField;
+typedef struct Field Field;
+typedef struct RegisterMap RegisterMap;
+
+/*
+ * Native function pointer type.
+ *
+ * "args[0]" holds the "this" pointer for virtual methods.
+ *
+ * The "Bridge" form is a super-set of the "Native" form; in many places
+ * they are used interchangeably. Currently, all functions have all
+ * arguments passed in, but some functions only care about the first two.
+ * Passing extra arguments to a C function is (mostly) harmless.
+ */
+typedef void (*DalvikBridgeFunc)(const u4* args, JValue* pResult,
+ const Method* method, struct Thread* self);
+typedef void (*DalvikNativeFunc)(const u4* args, JValue* pResult);
+
+
+/* vm-internal access flags and related definitions */
+typedef enum AccessFlags {
+ ACC_MIRANDA = 0x8000, // method (internal to VM)
+ JAVA_FLAGS_MASK = 0xffff, // bits set from Java sources (low 16)
+} AccessFlags;
+
+/* Use the top 16 bits of the access flags field for
+ * other class flags. Code should use the *CLASS_FLAG*()
+ * macros to set/get these flags.
+ */
+typedef enum ClassFlags {
+ CLASS_ISFINALIZABLE = (1<<31), // class/ancestor overrides finalize()
+ CLASS_ISARRAY = (1<<30), // class is a "[*"
+ CLASS_ISOBJECTARRAY = (1<<29), // class is a "[L*" or "[[*"
+ CLASS_ISREFERENCE = (1<<28), // class is a soft/weak/phantom ref
+ // only ISREFERENCE is set --> soft
+ CLASS_ISWEAKREFERENCE = (1<<27), // class is a weak reference
+ CLASS_ISPHANTOMREFERENCE = (1<<26), // class is a phantom reference
+
+ CLASS_MULTIPLE_DEFS = (1<<25), // DEX verifier: defs in multiple DEXs
+
+ /* unlike the others, these can be present in the optimized DEX file */
+ CLASS_ISOPTIMIZED = (1<<17), // class may contain opt instrs
+ CLASS_ISPREVERIFIED = (1<<16), // class has been pre-verified
+} ClassFlags;
+
+/* bits we can reasonably expect to see set in a DEX access flags field */
+#define EXPECTED_FILE_FLAGS \
+ (ACC_CLASS_MASK | CLASS_ISPREVERIFIED | CLASS_ISOPTIMIZED)
+
+/*
+ * Get/set class flags.
+ */
+#define SET_CLASS_FLAG(clazz, flag) \
+ do { (clazz)->accessFlags |= (flag); } while (0)
+
+#define CLEAR_CLASS_FLAG(clazz, flag) \
+ do { (clazz)->accessFlags &= ~(flag); } while (0)
+
+#define IS_CLASS_FLAG_SET(clazz, flag) \
+ (((clazz)->accessFlags & (flag)) != 0)
+
+#define GET_CLASS_FLAG_GROUP(clazz, flags) \
+ ((u4)((clazz)->accessFlags & (flags)))
+
+/*
+ * Use the top 16 bits of the access flags field for other method flags.
+ * Code should use the *METHOD_FLAG*() macros to set/get these flags.
+ */
+typedef enum MethodFlags {
+ METHOD_ISWRITABLE = (1<<31), // the method's code is writable
+} MethodFlags;
+
+/*
+ * Get/set method flags.
+ */
+#define SET_METHOD_FLAG(method, flag) \
+ do { (method)->accessFlags |= (flag); } while (0)
+
+#define CLEAR_METHOD_FLAG(method, flag) \
+ do { (method)->accessFlags &= ~(flag); } while (0)
+
+#define IS_METHOD_FLAG_SET(method, flag) \
+ (((method)->accessFlags & (flag)) != 0)
+
+#define GET_METHOD_FLAG_GROUP(method, flags) \
+ ((u4)((method)->accessFlags & (flags)))
+
+/* current state of the class, increasing as we progress */
+typedef enum ClassStatus {
+ CLASS_ERROR = -1,
+
+ CLASS_NOTREADY = 0,
+ CLASS_IDX = 1, /* loaded, DEX idx in super or ifaces */
+ CLASS_LOADED = 2, /* DEX idx values resolved */
+ CLASS_RESOLVED = 3, /* part of linking */
+ CLASS_VERIFYING = 4, /* in the process of being verified */
+ CLASS_VERIFIED = 5, /* logically part of linking; done pre-init */
+ CLASS_INITIALIZING = 6, /* class init in progress */
+ CLASS_INITIALIZED = 7, /* ready to go */
+} ClassStatus;
+
+/*
+ * Primitive type identifiers. We use these values as indexes into an
+ * array of synthesized classes, so these start at zero and count up.
+ * The order is arbitrary (mimics table in doc for newarray opcode),
+ * but can't be changed without shuffling some reflection tables.
+ *
+ * PRIM_VOID can't be used as an array type, but we include it here for
+ * other uses (e.g. Void.TYPE).
+ */
+typedef enum PrimitiveType {
+ PRIM_NOT = -1, /* value is not a primitive type */
+ PRIM_BOOLEAN = 0,
+ PRIM_CHAR = 1,
+ PRIM_FLOAT = 2,
+ PRIM_DOUBLE = 3,
+ PRIM_BYTE = 4,
+ PRIM_SHORT = 5,
+ PRIM_INT = 6,
+ PRIM_LONG = 7,
+ PRIM_VOID = 8,
+
+ PRIM_MAX
+} PrimitiveType;
+#define PRIM_TYPE_TO_LETTER "ZCFDBSIJV" /* must match order in enum */
+
+/*
+ * Definitions for packing refOffsets in ClassObject.
+ */
+/*
+ * A magic value for refOffsets. Ignore the bits and walk the super
+ * chain when this is the value.
+ * [This is an unlikely "natural" value, since it would be 30 non-ref instance
+ * fields followed by 2 ref instance fields.]
+ */
+#define CLASS_WALK_SUPER ((unsigned int)(3))
+#define CLASS_SMALLEST_OFFSET (sizeof(struct Object))
+#define CLASS_BITS_PER_WORD (sizeof(unsigned long int) * 8)
+#define CLASS_OFFSET_ALIGNMENT 4
+#define CLASS_HIGH_BIT ((unsigned int)1 << (CLASS_BITS_PER_WORD - 1))
+/*
+ * Given an offset, return the bit number which would encode that offset.
+ * Local use only.
+ */
+#define _CLASS_BIT_NUMBER_FROM_OFFSET(byteOffset) \
+ (((unsigned int)(byteOffset) - CLASS_SMALLEST_OFFSET) / \
+ CLASS_OFFSET_ALIGNMENT)
+/*
+ * Is the given offset too large to be encoded?
+ */
+#define CLASS_CAN_ENCODE_OFFSET(byteOffset) \
+ (_CLASS_BIT_NUMBER_FROM_OFFSET(byteOffset) < CLASS_BITS_PER_WORD)
+/*
+ * Return a single bit, encoding the offset.
+ * Undefined if the offset is too large, as defined above.
+ */
+#define CLASS_BIT_FROM_OFFSET(byteOffset) \
+ (CLASS_HIGH_BIT >> _CLASS_BIT_NUMBER_FROM_OFFSET(byteOffset))
+/*
+ * Return an offset, given a bit number as returned from CLZ.
+ */
+#define CLASS_OFFSET_FROM_CLZ(rshift) \
+ (((int)(rshift) * CLASS_OFFSET_ALIGNMENT) + CLASS_SMALLEST_OFFSET)
+
+
+/*
+ * Used for iftable in ClassObject.
+ */
+typedef struct InterfaceEntry {
+ /* pointer to interface class */
+ ClassObject* clazz;
+
+ /*
+ * Index into array of vtable offsets. This points into the ifviPool,
+ * which holds the vtables for all interfaces declared by this class.
+ */
+ int* methodIndexArray;
+} InterfaceEntry;
+
+
+
+/*
+ * There are three types of objects:
+ * Class objects - an instance of java.lang.Class
+ * Array objects - an object created with a "new array" instruction
+ * Data objects - an object that is neither of the above
+ *
+ * We also define String objects. At present they're equivalent to
+ * DataObject, but that may change. (Either way, they make some of the
+ * code more obvious.)
+ *
+ * All objects have an Object header followed by type-specific data.
+ */
+typedef struct Object {
+ /* ptr to class object */
+ ClassObject* clazz;
+
+ /*
+ * A word containing either a "thin" lock or a "fat" monitor. See
+ * the comments in Sync.c for a description of its layout.
+ */
+ u4 lock;
+} Object;
+
+/*
+ * Properly initialize an Object.
+ * void DVM_OBJECT_INIT(Object *obj, ClassObject *clazz_)
+ */
+#define DVM_OBJECT_INIT(obj, clazz_) \
+ do { \
+ dvmSetFieldObject((Object *)obj, offsetof(Object, clazz), \
+ (Object *)clazz_); \
+ DVM_LOCK_INIT(&(obj)->lock); \
+ } while (0)
+
+/*
+ * Data objects have an Object header followed by their instance data.
+ */
+struct DataObject {
+ Object obj; /* MUST be first item */
+
+ /* variable #of u4 slots; u8 uses 2 slots */
+ u4 instanceData[1];
+};
+
+/*
+ * Strings are used frequently enough that we may want to give them their
+ * own unique type.
+ *
+ * Using a dedicated type object to access the instance data provides a
+ * performance advantage but makes the java/lang/String.java implementation
+ * fragile.
+ *
+ * Currently this is just equal to DataObject, and we pull the fields out
+ * like we do for any other object.
+ */
+struct StringObject {
+ Object obj; /* MUST be first item */
+
+ /* variable #of u4 slots; u8 uses 2 slots */
+ u4 instanceData[1];
+};
+
+
+/*
+ * Array objects have these additional fields.
+ *
+ * We don't currently store the size of each element. Usually it's implied
+ * by the instruction. If necessary, the width can be derived from
+ * the first char of obj->clazz->descriptor.
+ */
+struct ArrayObject {
+ Object obj; /* MUST be first item */
+
+ /* number of elements; immutable after init */
+ u4 length;
+
+ /*
+ * Array contents; actual size is (length * sizeof(type)). This is
+ * declared as u8 so that the compiler inserts any necessary padding
+ * (e.g. for EABI); the actual allocation may be smaller than 8 bytes.
+ */
+ u8 contents[1];
+};
+
+/*
+ * For classes created early and thus probably in the zygote, the
+ * InitiatingLoaderList is kept in gDvm. Later classes use the structure in
+ * Object Class. This helps keep zygote pages shared.
+ */
+struct InitiatingLoaderList {
+ /* a list of initiating loader Objects; grown and initialized on demand */
+ Object** initiatingLoaders;
+ /* count of loaders in the above list */
+ int initiatingLoaderCount;
+};
+
+/*
+ * Generic field header. We pass this around when we want a generic Field
+ * pointer (e.g. for reflection stuff). Testing the accessFlags for
+ * ACC_STATIC allows a proper up-cast.
+ */
+struct Field {
+ ClassObject* clazz; /* class in which the field is declared */
+ const char* name;
+ const char* signature; /* e.g. "I", "[C", "Landroid/os/Debug;" */
+ u4 accessFlags;
+#ifdef PROFILE_FIELD_ACCESS
+ u4 gets;
+ u4 puts;
+#endif
+};
+
+/*
+ * Static field.
+ */
+struct StaticField {
+ Field field; /* MUST be first item */
+ JValue value; /* initially set from DEX for primitives */
+};
+
+/*
+ * Instance field.
+ */
+struct InstField {
+ Field field; /* MUST be first item */
+
+ /*
+ * This field indicates the byte offset from the beginning of the
+ * (Object *) to the actual instance data; e.g., byteOffset==0 is
+ * the same as the object pointer (bug!), and byteOffset==4 is 4
+ * bytes farther.
+ */
+ int byteOffset;
+};
+
+/*
+ * This defines the amount of space we leave for field slots in the
+ * java.lang.Class definition. If we alter the class to have more than
+ * this many fields, the VM will abort at startup.
+ */
+#define CLASS_FIELD_SLOTS 4
+
+/*
+ * Class objects have many additional fields. This is used for both
+ * classes and interfaces, including synthesized classes (arrays and
+ * primitive types).
+ *
+ * Class objects are unusual in that they have some fields allocated with
+ * the system malloc (or LinearAlloc), rather than on the GC heap. This is
+ * handy during initialization, but does require special handling when
+ * discarding java.lang.Class objects.
+ *
+ * The separation of methods (direct vs. virtual) and fields (class vs.
+ * instance) used in Dalvik works out pretty well. The only time it's
+ * annoying is when enumerating or searching for things with reflection.
+ */
+struct ClassObject {
+ Object obj; /* MUST be first item */
+
+ /* leave space for instance data; we could access fields directly if we
+ freeze the definition of java/lang/Class */
+ u4 instanceData[CLASS_FIELD_SLOTS];
+
+ /* UTF-8 descriptor for the class; from constant pool, or on heap
+ if generated ("[C") */
+ const char* descriptor;
+ char* descriptorAlloc;
+
+ /* access flags; low 16 bits are defined by VM spec */
+ u4 accessFlags;
+
+ /* VM-unique class serial number, nonzero, set very early */
+ u4 serialNumber;
+
+ /* DexFile from which we came; needed to resolve constant pool entries */
+ /* (will be NULL for VM-generated, e.g. arrays and primitive classes) */
+ DvmDex* pDvmDex;
+
+ /* state of class initialization */
+ ClassStatus status;
+
+ /* if class verify fails, we must return same error on subsequent tries */
+ ClassObject* verifyErrorClass;
+
+ /* threadId, used to check for recursive <clinit> invocation */
+ u4 initThreadId;
+
+ /*
+ * Total object size; used when allocating storage on gc heap. (For
+ * interfaces and abstract classes this will be zero.)
+ */
+ size_t objectSize;
+
+ /* arrays only: class object for base element, for instanceof/checkcast
+ (for String[][][], this will be String) */
+ ClassObject* elementClass;
+
+ /* arrays only: number of dimensions, e.g. int[][] is 2 */
+ int arrayDim;
+
+ /* primitive type index, or PRIM_NOT (-1); set for generated prim classes */
+ PrimitiveType primitiveType;
+
+ /* superclass, or NULL if this is java.lang.Object */
+ ClassObject* super;
+
+ /* defining class loader, or NULL for the "bootstrap" system loader */
+ Object* classLoader;
+
+ /* initiating class loader list */
+ /* NOTE: for classes with low serialNumber, these are unused, and the
+ values are kept in a table in gDvm. */
+ InitiatingLoaderList initiatingLoaderList;
+
+ /* array of interfaces this class implements directly */
+ int interfaceCount;
+ ClassObject** interfaces;
+
+ /* static, private, and <init> methods */
+ int directMethodCount;
+ Method* directMethods;
+
+ /* virtual methods defined in this class; invoked through vtable */
+ int virtualMethodCount;
+ Method* virtualMethods;
+
+ /*
+ * Virtual method table (vtable), for use by "invoke-virtual". The
+ * vtable from the superclass is copied in, and virtual methods from
+ * our class either replace those from the super or are appended.
+ */
+ int vtableCount;
+ Method** vtable;
+
+ /*
+ * Interface table (iftable), one entry per interface supported by
+ * this class. That means one entry for each interface we support
+ * directly, indirectly via superclass, or indirectly via
+ * superinterface. This will be null if neither we nor our superclass
+ * implement any interfaces.
+ *
+ * Why we need this: given "class Foo implements Face", declare
+ * "Face faceObj = new Foo()". Invoke faceObj.blah(), where "blah" is
+ * part of the Face interface. We can't easily use a single vtable.
+ *
+ * For every interface a concrete class implements, we create a list of
+ * virtualMethod indices for the methods in the interface.
+ */
+ int iftableCount;
+ InterfaceEntry* iftable;
+
+ /*
+ * The interface vtable indices for iftable get stored here. By placing
+ * them all in a single pool for each class that implements interfaces,
+ * we decrease the number of allocations.
+ */
+ int ifviPoolCount;
+ int* ifviPool;
+
+ /* instance fields
+ *
+ * These describe the layout of the contents of a DataObject-compatible
+ * Object. Note that only the fields directly defined by this class
+ * are listed in ifields; fields defined by a superclass are listed
+ * in the superclass's ClassObject.ifields.
+ *
+ * All instance fields that refer to objects are guaranteed to be
+ * at the beginning of the field list. ifieldRefCount specifies
+ * the number of reference fields.
+ */
+ int ifieldCount;
+ int ifieldRefCount; // number of fields that are object refs
+ InstField* ifields;
+
+ /* bitmap of offsets of ifields */
+ u4 refOffsets;
+
+ /* source file name, if known */
+ const char* sourceFile;
+
+ /* static fields */
+ int sfieldCount;
+ StaticField sfields[]; /* MUST be last item */
+};
+
+/*
+ * A method. We create one of these for every method in every class
+ * we load, so try to keep the size to a minimum.
+ *
+ * Much of this comes from and could be accessed in the data held in shared
+ * memory. We hold it all together here for speed. Everything but the
+ * pointers could be held in a shared table generated by the optimizer;
+ * if we're willing to convert them to offsets and take the performance
+ * hit (e.g. "meth->insns" becomes "baseAddr + meth->insnsOffset") we
+ * could move everything but "nativeFunc".
+ */
+struct Method {
+ /* the class we are a part of */
+ ClassObject* clazz;
+
+ /* access flags; low 16 bits are defined by spec (could be u2?) */
+ u4 accessFlags;
+
+ /*
+ * For concrete virtual methods, this is the offset of the method
+ * in "vtable".
+ *
+ * For abstract methods in an interface class, this is the offset
+ * of the method in "iftable[n]->methodIndexArray".
+ */
+ u2 methodIndex;
+
+ /*
+ * Method bounds; not needed for an abstract method.
+ *
+ * For a native method, we compute the size of the argument list, and
+ * set "insSize" and "registerSize" equal to it.
+ */
+ u2 registersSize; /* ins + locals */
+ u2 outsSize;
+ u2 insSize;
+
+ /* method name, e.g. "<init>" or "eatLunch" */
+ const char* name;
+
+ /*
+ * Method prototype descriptor string (return and argument types).
+ *
+ * TODO: This currently must specify the DexFile as well as the proto_ids
+ * index, because generated Proxy classes don't have a DexFile. We can
+ * remove the DexFile* and reduce the size of this struct if we generate
+ * a DEX for proxies.
+ */
+ DexProto prototype;
+
+ /* short-form method descriptor string */
+ const char* shorty;
+
+ /*
+ * The remaining items are not used for abstract or native methods.
+ * (JNI is currently hijacking "insns" as a function pointer, set
+ * after the first call. For internal-native this stays null.)
+ */
+
+ /* the actual code */
+ const u2* insns; /* instructions, in memory-mapped .dex */
+
+ /* cached JNI argument and return-type hints */
+ int jniArgInfo;
+
+ /*
+ * Native method ptr; could be actual function or a JNI bridge. We
+ * don't currently discriminate between DalvikBridgeFunc and
+ * DalvikNativeFunc; the former takes an argument superset (i.e. two
+ * extra args) which will be ignored. If necessary we can use
+ * insns==NULL to detect JNI bridge vs. internal native.
+ */
+ DalvikBridgeFunc nativeFunc;
+
+ /*
+ * Register map data, if available. This will point into the DEX file
+ * if the data was computed during pre-verification, or into the
+ * linear alloc area if not.
+ */
+ const RegisterMap* registerMap;
+
+ /* set if method was called during method profiling */
+ bool inProfile;
+};
+
+
+/*
+ * Find a method within a class. The superclass is not searched.
+ */
+Method* dvmFindDirectMethodByDescriptor(const ClassObject* clazz,
+ const char* methodName, const char* signature);
+Method* dvmFindVirtualMethodByDescriptor(const ClassObject* clazz,
+ const char* methodName, const char* signature);
+Method* dvmFindVirtualMethodByName(const ClassObject* clazz,
+ const char* methodName);
+Method* dvmFindDirectMethod(const ClassObject* clazz, const char* methodName,
+ const DexProto* proto);
+Method* dvmFindVirtualMethod(const ClassObject* clazz, const char* methodName,
+ const DexProto* proto);
+
+
+/*
+ * Find a method within a class hierarchy.
+ */
+Method* dvmFindDirectMethodHierByDescriptor(const ClassObject* clazz,
+ const char* methodName, const char* descriptor);
+Method* dvmFindVirtualMethodHierByDescriptor(const ClassObject* clazz,
+ const char* methodName, const char* signature);
+Method* dvmFindDirectMethodHier(const ClassObject* clazz,
+ const char* methodName, const DexProto* proto);
+Method* dvmFindVirtualMethodHier(const ClassObject* clazz,
+ const char* methodName, const DexProto* proto);
+Method* dvmFindMethodHier(const ClassObject* clazz, const char* methodName,
+ const DexProto* proto);
+
+/*
+ * Find the implementation of "meth" in "clazz".
+ *
+ * Returns NULL and throws an exception if not found.
+ */
+const Method* dvmGetVirtualizedMethod(const ClassObject* clazz,
+ const Method* meth);
+
+/*
+ * Get the source file associated with a method.
+ */
+const char* dvmGetMethodSourceFile(const Method* meth);
+
+/*
+ * Find a field within a class. The superclass is not searched.
+ */
+InstField* dvmFindInstanceField(const ClassObject* clazz,
+ const char* fieldName, const char* signature);
+StaticField* dvmFindStaticField(const ClassObject* clazz,
+ const char* fieldName, const char* signature);
+
+/*
+ * Find a field in a class/interface hierarchy.
+ */
+InstField* dvmFindInstanceFieldHier(const ClassObject* clazz,
+ const char* fieldName, const char* signature);
+StaticField* dvmFindStaticFieldHier(const ClassObject* clazz,
+ const char* fieldName, const char* signature);
+Field* dvmFindFieldHier(const ClassObject* clazz, const char* fieldName,
+ const char* signature);
+
+/*
+ * Find a field and return the byte offset from the object pointer. Only
+ * searches the specified class, not the superclass.
+ *
+ * Returns -1 on failure.
+ */
+INLINE int dvmFindFieldOffset(const ClassObject* clazz,
+ const char* fieldName, const char* signature)
+{
+ InstField* pField = dvmFindInstanceField(clazz, fieldName, signature);
+ if (pField == NULL)
+ return -1;
+ else
+ return pField->byteOffset;
+}
+
+/*
+ * Helpers.
+ */
+INLINE bool dvmIsPublicMethod(const Method* method) {
+ return (method->accessFlags & ACC_PUBLIC) != 0;
+}
+INLINE bool dvmIsPrivateMethod(const Method* method) {
+ return (method->accessFlags & ACC_PRIVATE) != 0;
+}
+INLINE bool dvmIsStaticMethod(const Method* method) {
+ return (method->accessFlags & ACC_STATIC) != 0;
+}
+INLINE bool dvmIsSynchronizedMethod(const Method* method) {
+ return (method->accessFlags & ACC_SYNCHRONIZED) != 0;
+}
+INLINE bool dvmIsDeclaredSynchronizedMethod(const Method* method) {
+ return (method->accessFlags & ACC_DECLARED_SYNCHRONIZED) != 0;
+}
+INLINE bool dvmIsFinalMethod(const Method* method) {
+ return (method->accessFlags & ACC_FINAL) != 0;
+}
+INLINE bool dvmIsNativeMethod(const Method* method) {
+ return (method->accessFlags & ACC_NATIVE) != 0;
+}
+INLINE bool dvmIsAbstractMethod(const Method* method) {
+ return (method->accessFlags & ACC_ABSTRACT) != 0;
+}
+INLINE bool dvmIsMirandaMethod(const Method* method) {
+ return (method->accessFlags & ACC_MIRANDA) != 0;
+}
+INLINE bool dvmIsConstructorMethod(const Method* method) {
+ return *method->name == '<';
+}
+/* Dalvik puts private, static, and constructors into non-virtual table */
+INLINE bool dvmIsDirectMethod(const Method* method) {
+ return dvmIsPrivateMethod(method) ||
+ dvmIsStaticMethod(method) ||
+ dvmIsConstructorMethod(method);
+}
+/* Get whether the given method has associated bytecode. This is the
+ * case for methods which are neither native nor abstract. */
+INLINE bool dvmIsBytecodeMethod(const Method* method) {
+ return (method->accessFlags & (ACC_NATIVE | ACC_ABSTRACT)) == 0;
+}
+
+INLINE bool dvmIsProtectedField(const Field* field) {
+ return (field->accessFlags & ACC_PROTECTED) != 0;
+}
+INLINE bool dvmIsStaticField(const Field* field) {
+ return (field->accessFlags & ACC_STATIC) != 0;
+}
+INLINE bool dvmIsFinalField(const Field* field) {
+ return (field->accessFlags & ACC_FINAL) != 0;
+}
+INLINE bool dvmIsVolatileField(const Field* field) {
+ return (field->accessFlags & ACC_VOLATILE) != 0;
+}
+
+INLINE bool dvmIsInterfaceClass(const ClassObject* clazz) {
+ return (clazz->accessFlags & ACC_INTERFACE) != 0;
+}
+INLINE bool dvmIsPublicClass(const ClassObject* clazz) {
+ return (clazz->accessFlags & ACC_PUBLIC) != 0;
+}
+INLINE bool dvmIsFinalClass(const ClassObject* clazz) {
+ return (clazz->accessFlags & ACC_FINAL) != 0;
+}
+INLINE bool dvmIsAbstractClass(const ClassObject* clazz) {
+ return (clazz->accessFlags & ACC_ABSTRACT) != 0;
+}
+INLINE bool dvmIsAnnotationClass(const ClassObject* clazz) {
+ return (clazz->accessFlags & ACC_ANNOTATION) != 0;
+}
+INLINE bool dvmIsPrimitiveClass(const ClassObject* clazz) {
+ return clazz->primitiveType != PRIM_NOT;
+}
+
+/* linked, here meaning prepared and resolved */
+INLINE bool dvmIsClassLinked(const ClassObject* clazz) {
+ return clazz->status >= CLASS_RESOLVED;
+}
+/* has class been verified? */
+INLINE bool dvmIsClassVerified(const ClassObject* clazz) {
+ return clazz->status >= CLASS_VERIFIED;
+}
+
+/*
+ * Get the associated code struct for a method. This returns NULL
+ * for non-bytecode methods.
+ */
+INLINE const DexCode* dvmGetMethodCode(const Method* meth) {
+ if (dvmIsBytecodeMethod(meth)) {
+ /*
+ * The insns field for a bytecode method actually points at
+ * &(DexCode.insns), so we can subtract back to get at the
+ * DexCode in front.
+ */
+ return (const DexCode*)
+ (((const u1*) meth->insns) - offsetof(DexCode, insns));
+ } else {
+ return NULL;
+ }
+}
+
+/*
+ * Get the size of the insns associated with a method. This returns 0
+ * for non-bytecode methods.
+ */
+INLINE u4 dvmGetMethodInsnsSize(const Method* meth) {
+ const DexCode* pCode = dvmGetMethodCode(meth);
+ return (pCode == NULL) ? 0 : pCode->insnsSize;
+}
+
+/* debugging */
+void dvmDumpObject(const Object* obj);
+
+#endif /*_DALVIK_OO_OBJECT*/
diff --git a/vm/oo/ObjectInlines.h b/vm/oo/ObjectInlines.h
new file mode 100644
index 0000000..23a72b2
--- /dev/null
+++ b/vm/oo/ObjectInlines.h
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Helper functions to access data fields in Objects.
+ */
+#ifndef _DALVIK_OO_OBJECTINLINES
+#define _DALVIK_OO_OBJECTINLINES
+
+/*
+ * Store a single value in the array, and if the value isn't null,
+ * note in the write barrier.
+ */
+INLINE void dvmSetObjectArrayElement(const ArrayObject* obj, int index,
+ Object* val) {
+ ((Object **)(obj)->contents)[index] = val;
+ if (val != NULL) {
+ dvmWriteBarrierArray(obj, index, index + 1);
+ }
+}
+
+
+/*
+ * Field access functions. Pass in the word offset from Field->byteOffset.
+ *
+ * We guarantee that long/double field data is 64-bit aligned, so it's safe
+ * to access them with ldrd/strd on ARM.
+ *
+ * The VM treats all fields as 32 or 64 bits, so the field set functions
+ * write 32 bits even if the underlying type is smaller.
+ *
+ * Setting Object types to non-null values includes a call to the
+ * write barrier.
+ */
+#define BYTE_OFFSET(_ptr, _offset) ((void*) (((u1*)(_ptr)) + (_offset)))
+
+INLINE JValue* dvmFieldPtr(const Object* obj, int offset) {
+ return ((JValue*)BYTE_OFFSET(obj, offset));
+}
+
+INLINE bool dvmGetFieldBoolean(const Object* obj, int offset) {
+ return ((JValue*)BYTE_OFFSET(obj, offset))->z;
+}
+INLINE s1 dvmGetFieldByte(const Object* obj, int offset) {
+ return ((JValue*)BYTE_OFFSET(obj, offset))->b;
+}
+INLINE s2 dvmGetFieldShort(const Object* obj, int offset) {
+ return ((JValue*)BYTE_OFFSET(obj, offset))->s;
+}
+INLINE u2 dvmGetFieldChar(const Object* obj, int offset) {
+ return ((JValue*)BYTE_OFFSET(obj, offset))->c;
+}
+INLINE s4 dvmGetFieldInt(const Object* obj, int offset) {
+ return ((JValue*)BYTE_OFFSET(obj, offset))->i;
+}
+INLINE s8 dvmGetFieldLong(const Object* obj, int offset) {
+ return ((JValue*)BYTE_OFFSET(obj, offset))->j;
+}
+INLINE float dvmGetFieldFloat(const Object* obj, int offset) {
+ return ((JValue*)BYTE_OFFSET(obj, offset))->f;
+}
+INLINE double dvmGetFieldDouble(const Object* obj, int offset) {
+ return ((JValue*)BYTE_OFFSET(obj, offset))->d;
+}
+INLINE Object* dvmGetFieldObject(const Object* obj, int offset) {
+ return ((JValue*)BYTE_OFFSET(obj, offset))->l;
+}
+INLINE bool dvmGetFieldBooleanVolatile(const Object* obj, int offset) {
+ s4* ptr = &((JValue*)BYTE_OFFSET(obj, offset))->i;
+ return (bool)android_atomic_acquire_load(ptr);
+}
+INLINE s1 dvmGetFieldByteVolatile(const Object* obj, int offset) {
+ s4* ptr = &((JValue*)BYTE_OFFSET(obj, offset))->i;
+ return (s1)android_atomic_acquire_load(ptr);
+}
+INLINE s2 dvmGetFieldShortVolatile(const Object* obj, int offset) {
+ s4* ptr = &((JValue*)BYTE_OFFSET(obj, offset))->i;
+ return (s2)android_atomic_acquire_load(ptr);
+}
+INLINE u2 dvmGetFieldCharVolatile(const Object* obj, int offset) {
+ s4* ptr = &((JValue*)BYTE_OFFSET(obj, offset))->i;
+ return (u2)android_atomic_acquire_load(ptr);
+}
+INLINE s4 dvmGetFieldIntVolatile(const Object* obj, int offset) {
+ s4* ptr = &((JValue*)BYTE_OFFSET(obj, offset))->i;
+ return android_atomic_acquire_load(ptr);
+}
+INLINE float dvmGetFieldFloatVolatile(const Object* obj, int offset) {
+ union { s4 ival; float fval; } alias;
+ s4* ptr = &((JValue*)BYTE_OFFSET(obj, offset))->i;
+ alias.ival = android_atomic_acquire_load(ptr);
+ return alias.fval;
+}
+INLINE s8 dvmGetFieldLongVolatile(const Object* obj, int offset) {
+ const s8* addr = BYTE_OFFSET(obj, offset);
+ s8 val = dvmQuasiAtomicRead64(addr);
+ ANDROID_MEMBAR_FULL();
+ return val;
+}
+INLINE double dvmGetFieldDoubleVolatile(const Object* obj, int offset) {
+ union { s8 lval; double dval; } alias;
+ const s8* addr = BYTE_OFFSET(obj, offset);
+ alias.lval = dvmQuasiAtomicRead64(addr);
+ ANDROID_MEMBAR_FULL();
+ return alias.dval;
+}
+INLINE Object* dvmGetFieldObjectVolatile(const Object* obj, int offset) {
+ void** ptr = &((JValue*)BYTE_OFFSET(obj, offset))->l;
+ return (Object*)android_atomic_acquire_load((int32_t*)ptr);
+}
+
+INLINE void dvmSetFieldBoolean(Object* obj, int offset, bool val) {
+ ((JValue*)BYTE_OFFSET(obj, offset))->i = val;
+}
+INLINE void dvmSetFieldByte(Object* obj, int offset, s1 val) {
+ ((JValue*)BYTE_OFFSET(obj, offset))->i = val;
+}
+INLINE void dvmSetFieldShort(Object* obj, int offset, s2 val) {
+ ((JValue*)BYTE_OFFSET(obj, offset))->i = val;
+}
+INLINE void dvmSetFieldChar(Object* obj, int offset, u2 val) {
+ ((JValue*)BYTE_OFFSET(obj, offset))->i = val;
+}
+INLINE void dvmSetFieldInt(Object* obj, int offset, s4 val) {
+ ((JValue*)BYTE_OFFSET(obj, offset))->i = val;
+}
+INLINE void dvmSetFieldFloat(Object* obj, int offset, float val) {
+ ((JValue*)BYTE_OFFSET(obj, offset))->f = val;
+}
+INLINE void dvmSetFieldLong(Object* obj, int offset, s8 val) {
+ ((JValue*)BYTE_OFFSET(obj, offset))->j = val;
+}
+INLINE void dvmSetFieldDouble(Object* obj, int offset, double val) {
+ ((JValue*)BYTE_OFFSET(obj, offset))->d = val;
+}
+INLINE void dvmSetFieldObject(Object* obj, int offset, Object* val) {
+ JValue* lhs = BYTE_OFFSET(obj, offset);
+ lhs->l = val;
+ if (val != NULL) {
+ dvmWriteBarrierField(obj, &lhs->l);
+ }
+}
+INLINE void dvmSetFieldIntVolatile(Object* obj, int offset, s4 val) {
+ s4* ptr = &((JValue*)BYTE_OFFSET(obj, offset))->i;
+ android_atomic_release_store(val, ptr);
+}
+INLINE void dvmSetFieldBooleanVolatile(Object* obj, int offset, bool val) {
+ dvmSetFieldIntVolatile(obj, offset, val);
+}
+INLINE void dvmSetFieldByteVolatile(Object* obj, int offset, s1 val) {
+ dvmSetFieldIntVolatile(obj, offset, val);
+}
+INLINE void dvmSetFieldShortVolatile(Object* obj, int offset, s2 val) {
+ dvmSetFieldIntVolatile(obj, offset, val);
+}
+INLINE void dvmSetFieldCharVolatile(Object* obj, int offset, u2 val) {
+ dvmSetFieldIntVolatile(obj, offset, val);
+}
+INLINE void dvmSetFieldFloatVolatile(Object* obj, int offset, float val) {
+ union { s4 ival; float fval; } alias;
+ alias.fval = val;
+ dvmSetFieldIntVolatile(obj, offset, alias.ival);
+}
+INLINE void dvmSetFieldLongVolatile(Object* obj, int offset, s8 val) {
+ s8* addr = BYTE_OFFSET(obj, offset);
+ ANDROID_MEMBAR_FULL();
+ dvmQuasiAtomicSwap64(val, addr);
+}
+INLINE void dvmSetFieldDoubleVolatile(Object* obj, int offset, double val) {
+ union { s8 lval; double dval; } alias;
+ alias.dval = val;
+ dvmSetFieldLongVolatile(obj, offset, alias.lval);
+}
+INLINE void dvmSetFieldObjectVolatile(Object* obj, int offset, Object* val) {
+ void** ptr = &((JValue*)BYTE_OFFSET(obj, offset))->l;
+ android_atomic_release_store((int32_t)val, (int32_t*)ptr);
+ if (val != NULL) {
+ dvmWriteBarrierField(obj, ptr);
+ }
+}
+
+/*
+ * Static field access functions.
+ */
+INLINE JValue* dvmStaticFieldPtr(const StaticField* sfield) {
+ return (JValue*)&sfield->value;
+}
+
+INLINE bool dvmGetStaticFieldBoolean(const StaticField* sfield) {
+ return sfield->value.z;
+}
+INLINE s1 dvmGetStaticFieldByte(const StaticField* sfield) {
+ return sfield->value.b;
+}
+INLINE s2 dvmGetStaticFieldShort(const StaticField* sfield) {
+ return sfield->value.s;
+}
+INLINE u2 dvmGetStaticFieldChar(const StaticField* sfield) {
+ return sfield->value.c;
+}
+INLINE s4 dvmGetStaticFieldInt(const StaticField* sfield) {
+ return sfield->value.i;
+}
+INLINE float dvmGetStaticFieldFloat(const StaticField* sfield) {
+ return sfield->value.f;
+}
+INLINE s8 dvmGetStaticFieldLong(const StaticField* sfield) {
+ return sfield->value.j;
+}
+INLINE double dvmGetStaticFieldDouble(const StaticField* sfield) {
+ return sfield->value.d;
+}
+INLINE Object* dvmGetStaticFieldObject(const StaticField* sfield) {
+ return sfield->value.l;
+}
+INLINE bool dvmGetStaticFieldBooleanVolatile(const StaticField* sfield) {
+ const s4* ptr = &(sfield->value.i);
+ return (bool)android_atomic_acquire_load((s4*)ptr);
+}
+INLINE s1 dvmGetStaticFieldByteVolatile(const StaticField* sfield) {
+ const s4* ptr = &(sfield->value.i);
+ return (s1)android_atomic_acquire_load((s4*)ptr);
+}
+INLINE s2 dvmGetStaticFieldShortVolatile(const StaticField* sfield) {
+ const s4* ptr = &(sfield->value.i);
+ return (s2)android_atomic_acquire_load((s4*)ptr);
+}
+INLINE u2 dvmGetStaticFieldCharVolatile(const StaticField* sfield) {
+ const s4* ptr = &(sfield->value.i);
+ return (u2)android_atomic_acquire_load((s4*)ptr);
+}
+INLINE s4 dvmGetStaticFieldIntVolatile(const StaticField* sfield) {
+ const s4* ptr = &(sfield->value.i);
+ return android_atomic_acquire_load((s4*)ptr);
+}
+INLINE float dvmGetStaticFieldFloatVolatile(const StaticField* sfield) {
+ union { s4 ival; float fval; } alias;
+ const s4* ptr = &(sfield->value.i);
+ alias.ival = android_atomic_acquire_load((s4*)ptr);
+ return alias.fval;
+}
+INLINE s8 dvmGetStaticFieldLongVolatile(const StaticField* sfield) {
+ const s8* addr = &sfield->value.j;
+ s8 val = dvmQuasiAtomicRead64(addr);
+ ANDROID_MEMBAR_FULL();
+ return val;
+}
+INLINE double dvmGetStaticFieldDoubleVolatile(const StaticField* sfield) {
+ union { s8 lval; double dval; } alias;
+ const s8* addr = &sfield->value.j;
+ alias.lval = dvmQuasiAtomicRead64(addr);
+ ANDROID_MEMBAR_FULL();
+ return alias.dval;
+}
+INLINE Object* dvmGetStaticFieldObjectVolatile(const StaticField* sfield) {
+ void* const* ptr = &(sfield->value.l);
+ return (Object*)android_atomic_acquire_load((int32_t*)ptr);
+}
+
+INLINE void dvmSetStaticFieldBoolean(StaticField* sfield, bool val) {
+ sfield->value.i = val;
+}
+INLINE void dvmSetStaticFieldByte(StaticField* sfield, s1 val) {
+ sfield->value.i = val;
+}
+INLINE void dvmSetStaticFieldShort(StaticField* sfield, s2 val) {
+ sfield->value.i = val;
+}
+INLINE void dvmSetStaticFieldChar(StaticField* sfield, u2 val) {
+ sfield->value.i = val;
+}
+INLINE void dvmSetStaticFieldInt(StaticField* sfield, s4 val) {
+ sfield->value.i = val;
+}
+INLINE void dvmSetStaticFieldFloat(StaticField* sfield, float val) {
+ sfield->value.f = val;
+}
+INLINE void dvmSetStaticFieldLong(StaticField* sfield, s8 val) {
+ sfield->value.j = val;
+}
+INLINE void dvmSetStaticFieldDouble(StaticField* sfield, double val) {
+ sfield->value.d = val;
+}
+INLINE void dvmSetStaticFieldObject(StaticField* sfield, Object* val) {
+ sfield->value.l = val;
+ if (val != NULL) {
+ dvmWriteBarrierField((Object *)sfield->field.clazz, &sfield->value.l);
+ }
+}
+INLINE void dvmSetStaticFieldIntVolatile(StaticField* sfield, s4 val) {
+ s4* ptr = &sfield->value.i;
+ android_atomic_release_store(val, ptr);
+}
+INLINE void dvmSetStaticFieldBooleanVolatile(StaticField* sfield, bool val) {
+ dvmSetStaticFieldIntVolatile(sfield, val);
+}
+INLINE void dvmSetStaticFieldByteVolatile(StaticField* sfield, s1 val) {
+ dvmSetStaticFieldIntVolatile(sfield, val);
+}
+INLINE void dvmSetStaticFieldShortVolatile(StaticField* sfield, s2 val) {
+ dvmSetStaticFieldIntVolatile(sfield, val);
+}
+INLINE void dvmSetStaticFieldCharVolatile(StaticField* sfield, u2 val) {
+ dvmSetStaticFieldIntVolatile(sfield, val);
+}
+INLINE void dvmSetStaticFieldFloatVolatile(StaticField* sfield, float val) {
+ union { s4 ival; float fval; } alias;
+ alias.fval = val;
+ dvmSetStaticFieldIntVolatile(sfield, alias.ival);
+}
+INLINE void dvmSetStaticFieldLongVolatile(StaticField* sfield, s8 val) {
+ s8* addr = &sfield->value.j;
+ ANDROID_MEMBAR_FULL();
+ dvmQuasiAtomicSwap64(val, addr);
+}
+INLINE void dvmSetStaticFieldDoubleVolatile(StaticField* sfield, double val) {
+ union { s8 lval; double dval; } alias;
+ alias.dval = val;
+ dvmSetStaticFieldLongVolatile(sfield, alias.lval);
+}
+INLINE void dvmSetStaticFieldObjectVolatile(StaticField* sfield, Object* val) {
+ void** ptr = &(sfield->value.l);
+ android_atomic_release_store((int32_t)val, (int32_t*)ptr);
+ if (val != NULL) {
+ dvmWriteBarrierField((Object *)sfield->field.clazz, &sfield->value.l);
+ }
+}
+
+#endif /*_DALVIK_OO_OBJECTINLINES*/
diff --git a/vm/oo/Resolve.c b/vm/oo/Resolve.c
new file mode 100644
index 0000000..c4bda8b
--- /dev/null
+++ b/vm/oo/Resolve.c
@@ -0,0 +1,588 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Resolve classes, methods, fields, and strings.
+ *
+ * According to the VM spec (v2 5.5), classes may be initialized by use
+ * of the "new", "getstatic", "putstatic", or "invokestatic" instructions.
+ * If we are resolving a static method or static field, we make the
+ * initialization check here.
+ *
+ * (NOTE: the verifier has its own resolve functions, which can be invoked
+ * if a class isn't pre-verified. Those functions must not update the
+ * "resolved stuff" tables for static fields and methods, because they do
+ * not perform initialization.)
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+
+
+/*
+ * Find the class corresponding to "classIdx", which maps to a class name
+ * string. It might be in the same DEX file as "referrer", in a different
+ * DEX file, generated by a class loader, or generated by the VM (e.g.
+ * array classes).
+ *
+ * Because the DexTypeId is associated with the referring class' DEX file,
+ * we may have to resolve the same class more than once if it's referred
+ * to from classes in multiple DEX files. This is a necessary property for
+ * DEX files associated with different class loaders.
+ *
+ * We cache a copy of the lookup in the DexFile's "resolved class" table,
+ * so future references to "classIdx" are faster.
+ *
+ * Note that "referrer" may be in the process of being linked.
+ *
+ * Traditional VMs might do access checks here, but in Dalvik the class
+ * "constant pool" is shared between all classes in the DEX file. We rely
+ * on the verifier to do the checks for us.
+ *
+ * Does not initialize the class.
+ *
+ * "fromUnverifiedConstant" should only be set if this call is the direct
+ * result of executing a "const-class" or "instance-of" instruction, which
+ * use class constants not resolved by the bytecode verifier.
+ *
+ * Returns NULL with an exception raised on failure.
+ */
+ClassObject* dvmResolveClass(const ClassObject* referrer, u4 classIdx,
+ bool fromUnverifiedConstant)
+{
+ DvmDex* pDvmDex = referrer->pDvmDex;
+ ClassObject* resClass;
+ const char* className;
+
+ /*
+ * Check the table first -- this gets called from the other "resolve"
+ * methods.
+ */
+ resClass = dvmDexGetResolvedClass(pDvmDex, classIdx);
+ if (resClass != NULL)
+ return resClass;
+
+ LOGVV("--- resolving class %u (referrer=%s cl=%p)\n",
+ classIdx, referrer->descriptor, referrer->classLoader);
+
+ /*
+ * Class hasn't been loaded yet, or is in the process of being loaded
+ * and initialized now. Try to get a copy. If we find one, put the
+ * pointer in the DexTypeId. There isn't a race condition here --
+ * 32-bit writes are guaranteed atomic on all target platforms. Worst
+ * case we have two threads storing the same value.
+ *
+ * If this is an array class, we'll generate it here.
+ */
+ className = dexStringByTypeIdx(pDvmDex->pDexFile, classIdx);
+ if (className[0] != '\0' && className[1] == '\0') {
+ /* primitive type */
+ resClass = dvmFindPrimitiveClass(className[0]);
+ } else {
+ resClass = dvmFindClassNoInit(className, referrer->classLoader);
+ }
+
+ if (resClass != NULL) {
+ /*
+ * If the referrer was pre-verified, the resolved class must come
+ * from the same DEX or from a bootstrap class. The pre-verifier
+ * makes assumptions that could be invalidated by a wacky class
+ * loader. (See the notes at the top of oo/Class.c.)
+ *
+ * The verifier does *not* fail a class for using a const-class
+ * or instance-of instruction referring to an unresolveable class,
+ * because the result of the instruction is simply a Class object
+ * or boolean -- there's no need to resolve the class object during
+ * verification. Instance field and virtual method accesses can
+ * break dangerously if we get the wrong class, but const-class and
+ * instance-of are only interesting at execution time. So, if we
+ * we got here as part of executing one of the "unverified class"
+ * instructions, we skip the additional check.
+ *
+ * Ditto for class references from annotations and exception
+ * handler lists.
+ */
+ if (!fromUnverifiedConstant &&
+ IS_CLASS_FLAG_SET(referrer, CLASS_ISPREVERIFIED))
+ {
+ ClassObject* resClassCheck = resClass;
+ if (dvmIsArrayClass(resClassCheck))
+ resClassCheck = resClassCheck->elementClass;
+
+ if (referrer->pDvmDex != resClassCheck->pDvmDex &&
+ resClassCheck->classLoader != NULL)
+ {
+ LOGW("Class resolved by unexpected DEX:"
+ " %s(%p):%p ref [%s] %s(%p):%p\n",
+ referrer->descriptor, referrer->classLoader,
+ referrer->pDvmDex,
+ resClass->descriptor, resClassCheck->descriptor,
+ resClassCheck->classLoader, resClassCheck->pDvmDex);
+ LOGW("(%s had used a different %s during pre-verification)\n",
+ referrer->descriptor, resClass->descriptor);
+ dvmThrowException("Ljava/lang/IllegalAccessError;",
+ "Class ref in pre-verified class resolved to unexpected "
+ "implementation");
+ return NULL;
+ }
+ }
+
+ LOGVV("##### +ResolveClass(%s): referrer=%s dex=%p ldr=%p ref=%d\n",
+ resClass->descriptor, referrer->descriptor, referrer->pDvmDex,
+ referrer->classLoader, classIdx);
+
+ /*
+ * Add what we found to the list so we can skip the class search
+ * next time through.
+ *
+ * TODO: should we be doing this when fromUnverifiedConstant==true?
+ * (see comments at top of oo/Class.c)
+ */
+ dvmDexSetResolvedClass(pDvmDex, classIdx, resClass);
+ } else {
+ /* not found, exception should be raised */
+ LOGVV("Class not found: %s\n",
+ dexStringByTypeIdx(pDvmDex->pDexFile, classIdx));
+ assert(dvmCheckException(dvmThreadSelf()));
+ }
+
+ return resClass;
+}
+
+
+/*
+ * Find the method corresponding to "methodRef".
+ *
+ * We use "referrer" to find the DexFile with the constant pool that
+ * "methodRef" is an index into. We also use its class loader. The method
+ * being resolved may very well be in a different DEX file.
+ *
+ * If this is a static method, we ensure that the method's class is
+ * initialized.
+ */
+Method* dvmResolveMethod(const ClassObject* referrer, u4 methodIdx,
+ MethodType methodType)
+{
+ DvmDex* pDvmDex = referrer->pDvmDex;
+ ClassObject* resClass;
+ const DexMethodId* pMethodId;
+ Method* resMethod;
+
+ assert(methodType != METHOD_INTERFACE);
+
+ LOGVV("--- resolving method %u (referrer=%s)\n", methodIdx,
+ referrer->descriptor);
+ pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx);
+
+ resClass = dvmResolveClass(referrer, pMethodId->classIdx, false);
+ if (resClass == NULL) {
+ /* can't find the class that the method is a part of */
+ assert(dvmCheckException(dvmThreadSelf()));
+ return NULL;
+ }
+ if (dvmIsInterfaceClass(resClass)) {
+ /* method is part of an interface */
+ dvmThrowExceptionWithClassMessage(
+ "Ljava/lang/IncompatibleClassChangeError;",
+ resClass->descriptor);
+ return NULL;
+ }
+
+ const char* name = dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx);
+ DexProto proto;
+ dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId);
+
+ /*
+ * We need to chase up the class hierarchy to find methods defined
+ * in super-classes. (We only want to check the current class
+ * if we're looking for a constructor; since DIRECT calls are only
+ * for constructors and private methods, we don't want to walk up.)
+ */
+ if (methodType == METHOD_DIRECT) {
+ resMethod = dvmFindDirectMethod(resClass, name, &proto);
+ } else if (methodType == METHOD_STATIC) {
+ resMethod = dvmFindDirectMethodHier(resClass, name, &proto);
+ } else {
+ resMethod = dvmFindVirtualMethodHier(resClass, name, &proto);
+ }
+
+ if (resMethod == NULL) {
+ dvmThrowException("Ljava/lang/NoSuchMethodError;", name);
+ return NULL;
+ }
+
+ LOGVV("--- found method %d (%s.%s)\n",
+ methodIdx, resClass->descriptor, resMethod->name);
+
+ /* see if this is a pure-abstract method */
+ if (dvmIsAbstractMethod(resMethod) && !dvmIsAbstractClass(resClass)) {
+ dvmThrowException("Ljava/lang/AbstractMethodError;", name);
+ return NULL;
+ }
+
+ /*
+ * If we're the first to resolve this class, we need to initialize
+ * it now. Only necessary for METHOD_STATIC.
+ */
+ if (methodType == METHOD_STATIC) {
+ if (!dvmIsClassInitialized(resMethod->clazz) &&
+ !dvmInitClass(resMethod->clazz))
+ {
+ assert(dvmCheckException(dvmThreadSelf()));
+ return NULL;
+ } else {
+ assert(!dvmCheckException(dvmThreadSelf()));
+ }
+ } else {
+ /*
+ * Edge case: if the <clinit> for a class creates an instance
+ * of itself, we will call <init> on a class that is still being
+ * initialized by us.
+ */
+ assert(dvmIsClassInitialized(resMethod->clazz) ||
+ dvmIsClassInitializing(resMethod->clazz));
+ }
+
+ /*
+ * If the class has been initialized, add a pointer to our data structure
+ * so we don't have to jump through the hoops again. If this is a
+ * static method and the defining class is still initializing (i.e. this
+ * thread is executing <clinit>), don't do the store, otherwise other
+ * threads could call the method without waiting for class init to finish.
+ */
+ if (methodType == METHOD_STATIC && !dvmIsClassInitialized(resMethod->clazz))
+ {
+ LOGVV("--- not caching resolved method %s.%s (class init=%d/%d)\n",
+ resMethod->clazz->descriptor, resMethod->name,
+ dvmIsClassInitializing(resMethod->clazz),
+ dvmIsClassInitialized(resMethod->clazz));
+ } else {
+ dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod);
+ }
+
+ return resMethod;
+}
+
+/*
+ * Resolve an interface method reference.
+ *
+ * Returns NULL with an exception raised on failure.
+ */
+Method* dvmResolveInterfaceMethod(const ClassObject* referrer, u4 methodIdx)
+{
+ DvmDex* pDvmDex = referrer->pDvmDex;
+ ClassObject* resClass;
+ const DexMethodId* pMethodId;
+ Method* resMethod;
+ int i;
+
+ LOGVV("--- resolving interface method %d (referrer=%s)\n",
+ methodIdx, referrer->descriptor);
+ pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx);
+
+ resClass = dvmResolveClass(referrer, pMethodId->classIdx, false);
+ if (resClass == NULL) {
+ /* can't find the class that the method is a part of */
+ assert(dvmCheckException(dvmThreadSelf()));
+ return NULL;
+ }
+ if (!dvmIsInterfaceClass(resClass)) {
+ /* whoops */
+ dvmThrowExceptionWithClassMessage(
+ "Ljava/lang/IncompatibleClassChangeError;",
+ resClass->descriptor);
+ return NULL;
+ }
+
+ /*
+ * This is the first time the method has been resolved. Set it in our
+ * resolved-method structure. It always resolves to the same thing,
+ * so looking it up and storing it doesn't create a race condition.
+ *
+ * If we scan into the interface's superclass -- which is always
+ * java/lang/Object -- we will catch things like:
+ * interface I ...
+ * I myobj = (something that implements I)
+ * myobj.hashCode()
+ * However, the Method->methodIndex will be an offset into clazz->vtable,
+ * rather than an offset into clazz->iftable. The invoke-interface
+ * code can test to see if the method returned is abstract or concrete,
+ * and use methodIndex accordingly. I'm not doing this yet because
+ * (a) we waste time in an unusual case, and (b) we're probably going
+ * to fix it in the DEX optimizer.
+ *
+ * We do need to scan the superinterfaces, in case we're invoking a
+ * superinterface method on an interface reference. The class in the
+ * DexTypeId is for the static type of the object, not the class in
+ * which the method is first defined. We have the full, flattened
+ * list in "iftable".
+ */
+ const char* methodName =
+ dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx);
+
+ DexProto proto;
+ dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId);
+
+ LOGVV("+++ looking for '%s' '%s' in resClass='%s'\n",
+ methodName, methodSig, resClass->descriptor);
+ resMethod = dvmFindVirtualMethod(resClass, methodName, &proto);
+ if (resMethod == NULL) {
+ LOGVV("+++ did not resolve immediately\n");
+ for (i = 0; i < resClass->iftableCount; i++) {
+ resMethod = dvmFindVirtualMethod(resClass->iftable[i].clazz,
+ methodName, &proto);
+ if (resMethod != NULL)
+ break;
+ }
+
+ if (resMethod == NULL) {
+ dvmThrowException("Ljava/lang/NoSuchMethodError;", methodName);
+ return NULL;
+ }
+ } else {
+ LOGVV("+++ resolved immediately: %s (%s %d)\n", resMethod->name,
+ resMethod->clazz->descriptor, (u4) resMethod->methodIndex);
+ }
+
+ LOGVV("--- found interface method %d (%s.%s)\n",
+ methodIdx, resClass->descriptor, resMethod->name);
+
+ /* we're expecting this to be abstract */
+ assert(dvmIsAbstractMethod(resMethod));
+
+ /* interface methods are always public; no need to check access */
+
+ /*
+ * The interface class *may* be initialized. According to VM spec
+ * v2 2.17.4, the interfaces a class refers to "need not" be initialized
+ * when the class is initialized.
+ *
+ * It isn't necessary for an interface class to be initialized before
+ * we resolve methods on that interface.
+ *
+ * We choose not to do the initialization now.
+ */
+ //assert(dvmIsClassInitialized(resMethod->clazz));
+
+ /*
+ * Add a pointer to our data structure so we don't have to jump
+ * through the hoops again.
+ *
+ * As noted above, no need to worry about whether the interface that
+ * defines the method has been or is currently executing <clinit>.
+ */
+ dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod);
+
+ return resMethod;
+}
+
+/*
+ * Resolve an instance field reference.
+ *
+ * Returns NULL and throws an exception on error (no such field, illegal
+ * access).
+ */
+InstField* dvmResolveInstField(const ClassObject* referrer, u4 ifieldIdx)
+{
+ DvmDex* pDvmDex = referrer->pDvmDex;
+ ClassObject* resClass;
+ const DexFieldId* pFieldId;
+ InstField* resField;
+
+ LOGVV("--- resolving field %u (referrer=%s cl=%p)\n",
+ ifieldIdx, referrer->descriptor, referrer->classLoader);
+
+ pFieldId = dexGetFieldId(pDvmDex->pDexFile, ifieldIdx);
+
+ /*
+ * Find the field's class.
+ */
+ resClass = dvmResolveClass(referrer, pFieldId->classIdx, false);
+ if (resClass == NULL) {
+ assert(dvmCheckException(dvmThreadSelf()));
+ return NULL;
+ }
+
+ resField = dvmFindInstanceFieldHier(resClass,
+ dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx),
+ dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx));
+ if (resField == NULL) {
+ dvmThrowException("Ljava/lang/NoSuchFieldError;",
+ dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
+ return NULL;
+ }
+
+ /*
+ * Class must be initialized by now (unless verifier is buggy). We
+ * could still be in the process of initializing it if the field
+ * access is from a static initializer.
+ */
+ assert(dvmIsClassInitialized(resField->field.clazz) ||
+ dvmIsClassInitializing(resField->field.clazz));
+
+ /*
+ * The class is initialized (or initializing), the field has been
+ * found. Add a pointer to our data structure so we don't have to
+ * jump through the hoops again.
+ *
+ * Anything that uses the resolved table entry must have an instance
+ * of the class, so any class init activity has already happened (or
+ * been deliberately bypassed when <clinit> created an instance).
+ * So it's always okay to update the table.
+ */
+ dvmDexSetResolvedField(pDvmDex, ifieldIdx, (Field*)resField);
+ LOGVV(" field %u is %s.%s\n",
+ ifieldIdx, resField->field.clazz->descriptor, resField->field.name);
+
+ return resField;
+}
+
+/*
+ * Resolve a static field reference. The DexFile format doesn't distinguish
+ * between static and instance field references, so the "resolved" pointer
+ * in the Dex struct will have the wrong type. We trivially cast it here.
+ *
+ * Causes the field's class to be initialized.
+ */
+StaticField* dvmResolveStaticField(const ClassObject* referrer, u4 sfieldIdx)
+{
+ DvmDex* pDvmDex = referrer->pDvmDex;
+ ClassObject* resClass;
+ const DexFieldId* pFieldId;
+ StaticField* resField;
+
+ pFieldId = dexGetFieldId(pDvmDex->pDexFile, sfieldIdx);
+
+ /*
+ * Find the field's class.
+ */
+ resClass = dvmResolveClass(referrer, pFieldId->classIdx, false);
+ if (resClass == NULL) {
+ assert(dvmCheckException(dvmThreadSelf()));
+ return NULL;
+ }
+
+ resField = dvmFindStaticFieldHier(resClass,
+ dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx),
+ dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx));
+ if (resField == NULL) {
+ dvmThrowException("Ljava/lang/NoSuchFieldError;",
+ dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
+ return NULL;
+ }
+
+ /*
+ * If we're the first to resolve the field in which this class resides,
+ * we need to do it now. Note that, if the field was inherited from
+ * a superclass, it is not necessarily the same as "resClass".
+ */
+ if (!dvmIsClassInitialized(resField->field.clazz) &&
+ !dvmInitClass(resField->field.clazz))
+ {
+ assert(dvmCheckException(dvmThreadSelf()));
+ return NULL;
+ }
+
+ /*
+ * If the class has been initialized, add a pointer to our data structure
+ * so we don't have to jump through the hoops again. If it's still
+ * initializing (i.e. this thread is executing <clinit>), don't do
+ * the store, otherwise other threads could use the field without waiting
+ * for class init to finish.
+ */
+ if (dvmIsClassInitialized(resField->field.clazz)) {
+ dvmDexSetResolvedField(pDvmDex, sfieldIdx, (Field*) resField);
+ } else {
+ LOGVV("--- not caching resolved field %s.%s (class init=%d/%d)\n",
+ resField->field.clazz->descriptor, resField->field.name,
+ dvmIsClassInitializing(resField->field.clazz),
+ dvmIsClassInitialized(resField->field.clazz));
+ }
+
+ return resField;
+}
+
+
+/*
+ * Resolve a string reference.
+ *
+ * Finding the string is easy. We need to return a reference to a
+ * java/lang/String object, not a bunch of characters, which means the
+ * first time we get here we need to create an interned string.
+ */
+StringObject* dvmResolveString(const ClassObject* referrer, u4 stringIdx)
+{
+ DvmDex* pDvmDex = referrer->pDvmDex;
+ StringObject* strObj;
+ StringObject* internStrObj;
+ const char* utf8;
+ u4 utf16Size;
+
+ LOGVV("+++ resolving string, referrer is %s\n", referrer->descriptor);
+
+ /*
+ * Create a UTF-16 version so we can trivially compare it to what's
+ * already interned.
+ */
+ utf8 = dexStringAndSizeById(pDvmDex->pDexFile, stringIdx, &utf16Size);
+ strObj = dvmCreateStringFromCstrAndLength(utf8, utf16Size);
+ if (strObj == NULL) {
+ /* ran out of space in GC heap? */
+ assert(dvmCheckException(dvmThreadSelf()));
+ goto bail;
+ }
+
+ /*
+ * Add it to the intern list. The return value is the one in the
+ * intern list, which (due to race conditions) may or may not be
+ * the one we just created. The intern list is synchronized, so
+ * there will be only one "live" version.
+ *
+ * By requesting an immortal interned string, we guarantee that
+ * the returned object will never be collected by the GC.
+ *
+ * A NULL return here indicates some sort of hashing failure.
+ */
+ internStrObj = dvmLookupImmortalInternedString(strObj);
+ dvmReleaseTrackedAlloc((Object*) strObj, NULL);
+ strObj = internStrObj;
+ if (strObj == NULL) {
+ assert(dvmCheckException(dvmThreadSelf()));
+ goto bail;
+ }
+
+ /* save a reference so we can go straight to the object next time */
+ dvmDexSetResolvedString(pDvmDex, stringIdx, strObj);
+
+bail:
+ return strObj;
+}
+
+/*
+ * For debugging: return a string representing the methodType.
+ */
+const char* dvmMethodTypeStr(MethodType methodType)
+{
+ switch (methodType) {
+ case METHOD_DIRECT: return "direct";
+ case METHOD_STATIC: return "static";
+ case METHOD_VIRTUAL: return "virtual";
+ case METHOD_INTERFACE: return "interface";
+ case METHOD_UNKNOWN: return "UNKNOWN";
+ }
+ assert(false);
+ return "BOGUS";
+}
diff --git a/vm/oo/Resolve.h b/vm/oo/Resolve.h
new file mode 100644
index 0000000..70b2294
--- /dev/null
+++ b/vm/oo/Resolve.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Resolve "constant pool" references into pointers to VM structs.
+ */
+#ifndef _DALVIK_OO_RESOLVE
+#define _DALVIK_OO_RESOLVE
+
+/*
+ * "Direct" and "virtual" methods are stored independently. The type of call
+ * used to invoke the method determines which list we search, and whether
+ * we travel up into superclasses.
+ *
+ * (<clinit>, <init>, and methods declared "private" or "static" are stored
+ * in the "direct" list. All others are stored in the "virtual" list.)
+ */
+typedef enum MethodType {
+ METHOD_UNKNOWN = 0,
+ METHOD_DIRECT, // <init>, private
+ METHOD_STATIC, // static
+ METHOD_VIRTUAL, // virtual, super
+ METHOD_INTERFACE // interface
+} MethodType;
+
+/*
+ * Resolve a class, given the referring class and a constant pool index
+ * for the DexTypeId.
+ *
+ * Does not initialize the class.
+ *
+ * Throws an exception and returns NULL on failure.
+ */
+ClassObject* dvmResolveClass(const ClassObject* referrer, u4 classIdx,
+ bool fromUnverifiedConstant);
+
+/*
+ * Resolve a direct, static, or virtual method.
+ *
+ * Can cause the method's class to be initialized if methodType is
+ * METHOD_STATIC.
+ *
+ * Throws an exception and returns NULL on failure.
+ */
+Method* dvmResolveMethod(const ClassObject* referrer, u4 methodIdx,
+ MethodType methodType);
+
+/*
+ * Resolve an interface method.
+ *
+ * Throws an exception and returns NULL on failure.
+ */
+Method* dvmResolveInterfaceMethod(const ClassObject* referrer, u4 methodIdx);
+
+/*
+ * Resolve an instance field.
+ *
+ * Throws an exception and returns NULL on failure.
+ */
+InstField* dvmResolveInstField(const ClassObject* referrer, u4 ifieldIdx);
+
+/*
+ * Resolve a static field.
+ *
+ * Causes the field's class to be initialized.
+ *
+ * Throws an exception and returns NULL on failure.
+ */
+StaticField* dvmResolveStaticField(const ClassObject* referrer, u4 sfieldIdx);
+
+/*
+ * Resolve a "const-string" reference.
+ *
+ * Throws an exception and returns NULL on failure.
+ */
+StringObject* dvmResolveString(const ClassObject* referrer, u4 stringIdx);
+
+/*
+ * Return debug string constant for enum.
+ */
+const char* dvmMethodTypeStr(MethodType methodType);
+
+#endif /*_DALVIK_OO_RESOLVE*/
diff --git a/vm/oo/TypeCheck.c b/vm/oo/TypeCheck.c
new file mode 100644
index 0000000..fdd38ea
--- /dev/null
+++ b/vm/oo/TypeCheck.c
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * instanceof, checkcast, etc.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+
+/*
+ * I think modern C mandates that the results of a boolean expression are
+ * 0 or 1. If not, or we suddenly turn into C++ and bool != int, use this.
+ */
+#define BOOL_TO_INT(x) (x)
+//#define BOOL_TO_INT(x) ((x) ? 1 : 0)
+
+/*
+ * Number of entries in instanceof cache. MUST be a power of 2.
+ */
+#define INSTANCEOF_CACHE_SIZE 1024
+
+
+/*
+ * Allocate cache.
+ */
+bool dvmInstanceofStartup(void)
+{
+ gDvm.instanceofCache = dvmAllocAtomicCache(INSTANCEOF_CACHE_SIZE);
+ if (gDvm.instanceofCache == NULL)
+ return false;
+ return true;
+}
+
+/*
+ * Discard the cache.
+ */
+void dvmInstanceofShutdown(void)
+{
+ dvmFreeAtomicCache(gDvm.instanceofCache);
+}
+
+
+/*
+ * Determine whether "sub" is an instance of "clazz", where both of these
+ * are array classes.
+ *
+ * Consider an array class, e.g. Y[][], where Y is a subclass of X.
+ * Y[][] instanceof Y[][] --> true (identity)
+ * Y[][] instanceof X[][] --> true (element superclass)
+ * Y[][] instanceof Y --> false
+ * Y[][] instanceof Y[] --> false
+ * Y[][] instanceof Object --> true (everything is an object)
+ * Y[][] instanceof Object[] --> true
+ * Y[][] instanceof Object[][] --> true
+ * Y[][] instanceof Object[][][] --> false (too many []s)
+ * Y[][] instanceof Serializable --> true (all arrays are Serializable)
+ * Y[][] instanceof Serializable[] --> true
+ * Y[][] instanceof Serializable[][] --> false (unless Y is Serializable)
+ *
+ * Don't forget about primitive types.
+ * int[] instanceof Object[] --> false
+ *
+ * "subElemClass" is sub->elementClass.
+ *
+ * "subDim" is usually just sub->dim, but for some kinds of checks we want
+ * to pass in a non-array class and pretend that it's an array.
+ */
+static int isArrayInstanceOfArray(const ClassObject* subElemClass, int subDim,
+ const ClassObject* clazz)
+{
+ //assert(dvmIsArrayClass(sub));
+ assert(dvmIsArrayClass(clazz));
+
+ /* "If T is an array type TC[]... one of the following must be true:
+ * TC and SC are the same primitive type.
+ * TC and SC are reference types and type SC can be cast to TC [...]."
+ *
+ * We need the class objects for the array elements. For speed we
+ * tucked them into the class object.
+ */
+ assert(subDim > 0 && clazz->arrayDim > 0);
+ if (subDim == clazz->arrayDim) {
+ /*
+ * See if "sub" is an instance of "clazz". This handles the
+ * interfaces, java.lang.Object, superclassing, etc.
+ */
+ return dvmInstanceof(subElemClass, clazz->elementClass);
+ } else if (subDim > clazz->arrayDim) {
+ /*
+ * The thing we might be an instance of has fewer dimensions. It
+ * must be an Object or array of Object, or a standard array
+ * interface or array of standard array interfaces (the standard
+ * interfaces being java/lang/Cloneable and java/io/Serializable).
+ */
+ if (dvmIsInterfaceClass(clazz->elementClass)) {
+ /*
+ * See if the class implements its base element. We know the
+ * base element is an interface; if the array class implements
+ * it, we know it's a standard array interface.
+ */
+ return dvmImplements(clazz, clazz->elementClass);
+ } else {
+ /*
+ * See if this is an array of Object, Object[], etc. We know
+ * that the superclass of an array is always Object, so we
+ * just compare the element type to that.
+ */
+ return (clazz->elementClass == clazz->super);
+ }
+ } else {
+ /*
+ * Too many []s.
+ */
+ return false;
+ }
+}
+
+/*
+ * Determine whether "sub" is a sub-class of "clazz", where "sub" is an
+ * array class.
+ *
+ * "clazz" could be an array class, interface, or simple class.
+ */
+static int isArrayInstanceOf(const ClassObject* sub, const ClassObject* clazz)
+{
+ assert(dvmIsArrayClass(sub));
+
+ /* "If T is an interface type, T must be one of the interfaces
+ * implemented by arrays."
+ *
+ * I'm not checking that here, because dvmInstanceof tests for
+ * interfaces first, and the generic dvmImplements stuff should
+ * work correctly.
+ */
+ assert(!dvmIsInterfaceClass(clazz)); /* make sure */
+
+ /* "If T is a class type, then T must be Object."
+ *
+ * The superclass of an array is always java.lang.Object, so just
+ * compare against that.
+ */
+ if (!dvmIsArrayClass(clazz))
+ return BOOL_TO_INT(clazz == sub->super);
+
+ /*
+ * If T is an array type TC[] ...
+ */
+ return isArrayInstanceOfArray(sub->elementClass, sub->arrayDim, clazz);
+}
+
+
+/*
+ * Returns 1 (true) if "clazz" is an implementation of "interface".
+ *
+ * "clazz" could be a class or an interface.
+ */
+int dvmImplements(const ClassObject* clazz, const ClassObject* interface)
+{
+ int i;
+
+ assert(dvmIsInterfaceClass(interface));
+
+ /*
+ * All interfaces implemented directly and by our superclass, and
+ * recursively all super-interfaces of those interfaces, are listed
+ * in "iftable", so we can just do a linear scan through that.
+ */
+ for (i = 0; i < clazz->iftableCount; i++) {
+ if (clazz->iftable[i].clazz == interface)
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Determine whether or not we can put an object into an array, based on
+ * the class hierarchy. The object might itself by an array, which means
+ * we have to pay attention to the array instanceof rules.
+ *
+ * Note that "objectClass" could be an array, but objectClass->elementClass
+ * is always a non-array type.
+ */
+bool dvmCanPutArrayElement(const ClassObject* objectClass,
+ const ClassObject* arrayClass)
+{
+ if (dvmIsArrayClass(objectClass)) {
+ /*
+ * We're stuffing an array into an array. We want to see if the
+ * elements of "arrayClass" are compatible with "objectClass".
+ * We bump up the number of dimensions in "objectClass" so that we
+ * can compare the two directly.
+ */
+ return isArrayInstanceOfArray(objectClass->elementClass,
+ objectClass->arrayDim + 1, arrayClass);
+ } else {
+ /*
+ * We're putting a non-array element into an array. We need to
+ * test to see if the elements are compatible. The easiest way
+ * to do that is to "arrayify" it and use the standard array
+ * compatibility check.
+ */
+ return isArrayInstanceOfArray(objectClass, 1, arrayClass);
+ }
+}
+
+
+/*
+ * Perform the instanceof calculation.
+ */
+static inline int isInstanceof(const ClassObject* instance,
+ const ClassObject* clazz)
+{
+ if (dvmIsInterfaceClass(clazz)) {
+ return dvmImplements(instance, clazz);
+ } else if (dvmIsArrayClass(instance)) {
+ return isArrayInstanceOf(instance, clazz);
+ } else {
+ return dvmIsSubClass(instance, clazz);
+ }
+}
+
+
+/*
+ * Do the instanceof calculation, pulling the result from the cache if
+ * possible.
+ */
+int dvmInstanceofNonTrivial(const ClassObject* instance,
+ const ClassObject* clazz)
+{
+#define ATOMIC_CACHE_CALC isInstanceof(instance, clazz)
+ return ATOMIC_CACHE_LOOKUP(gDvm.instanceofCache,
+ INSTANCEOF_CACHE_SIZE, instance, clazz);
+#undef ATOMIC_CACHE_CALC
+}
diff --git a/vm/oo/TypeCheck.h b/vm/oo/TypeCheck.h
new file mode 100644
index 0000000..1397998
--- /dev/null
+++ b/vm/oo/TypeCheck.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * instanceof, checkcast, etc.
+ */
+#ifndef _DALVIK_OO_TYPECHECK
+#define _DALVIK_OO_TYPECHECK
+
+/* VM startup/shutdown */
+bool dvmInstanceofStartup(void);
+void dvmInstanceofShutdown(void);
+
+
+/* used by dvmInstanceof; don't call */
+int dvmInstanceofNonTrivial(const ClassObject* instance,
+ const ClassObject* clazz);
+
+/*
+ * Determine whether "instance" is an instance of "clazz".
+ *
+ * Returns 0 (false) if not, 1 (true) if so.
+ */
+INLINE int dvmInstanceof(const ClassObject* instance, const ClassObject* clazz)
+{
+ if (instance == clazz) {
+ if (CALC_CACHE_STATS)
+ gDvm.instanceofCache->trivial++;
+ return 1;
+ } else
+ return dvmInstanceofNonTrivial(instance, clazz);
+}
+
+/*
+ * Determine whether a class implements an interface.
+ *
+ * Returns 0 (false) if not, 1 (true) if so.
+ */
+int dvmImplements(const ClassObject* clazz, const ClassObject* interface);
+
+/*
+ * Determine whether "sub" is a sub-class of "clazz".
+ *
+ * Returns 0 (false) if not, 1 (true) if so.
+ */
+INLINE int dvmIsSubClass(const ClassObject* sub, const ClassObject* clazz) {
+ do {
+ /*printf("###### sub='%s' clazz='%s'\n", sub->name, clazz->name);*/
+ if (sub == clazz)
+ return 1;
+ sub = sub->super;
+ } while (sub != NULL);
+
+ return 0;
+}
+
+/*
+ * Determine whether or not we can store an object into an array, based
+ * on the classes of the two.
+ *
+ * Returns 0 (false) if not, 1 (true) if so.
+ */
+bool dvmCanPutArrayElement(const ClassObject* elemClass,
+ const ClassObject* arrayClass);
+
+#endif /*_DALVIK_OO_TYPECHECK*/
diff --git a/vm/reflect/Annotation.c b/vm/reflect/Annotation.c
new file mode 100644
index 0000000..a5007ba
--- /dev/null
+++ b/vm/reflect/Annotation.c
@@ -0,0 +1,2181 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Annotations.
+ *
+ * We're not expecting to make much use of runtime annotations, so speed vs.
+ * space choices are weighted heavily toward small size.
+ *
+ * It would have been nice to treat "system" annotations in the same way
+ * we do "real" annotations, but that doesn't work. The chief difficulty
+ * is that some of them have member types that are not legal in annotations,
+ * such as Method and Annotation. Another source of pain comes from the
+ * AnnotationDefault annotation, which by virtue of being an annotation
+ * could itself have default values, requiring some additional checks to
+ * prevent recursion.
+ *
+ * It's simpler, and more efficient, to handle the system annotations
+ * entirely inside the VM. There are empty classes defined for the system
+ * annotation types, but their only purpose is to allow the system
+ * annotations to share name space with standard annotations.
+ */
+#include "Dalvik.h"
+
+// fwd
+static Object* processEncodedAnnotation(const ClassObject* clazz,\
+ const u1** pPtr);
+static bool skipEncodedAnnotation(const ClassObject* clazz, const u1** pPtr);
+
+/*
+ * System annotation descriptors.
+ */
+static const char* kDescrAnnotationDefault
+ = "Ldalvik/annotation/AnnotationDefault;";
+static const char* kDescrEnclosingClass
+ = "Ldalvik/annotation/EnclosingClass;";
+static const char* kDescrEnclosingMethod
+ = "Ldalvik/annotation/EnclosingMethod;";
+static const char* kDescrInnerClass = "Ldalvik/annotation/InnerClass;";
+static const char* kDescrMemberClasses
+ = "Ldalvik/annotation/MemberClasses;";
+static const char* kDescrSignature = "Ldalvik/annotation/Signature;";
+static const char* kDescrThrows = "Ldalvik/annotation/Throws;";
+
+
+/*
+ * Perform Annotation setup.
+ */
+bool dvmReflectAnnotationStartup(void)
+{
+ Method* meth;
+
+ /*
+ * Find some standard Annotation classes.
+ */
+ gDvm.classJavaLangAnnotationAnnotationArray =
+ dvmFindArrayClass("[Ljava/lang/annotation/Annotation;", NULL);
+ gDvm.classJavaLangAnnotationAnnotationArrayArray =
+ dvmFindArrayClass("[[Ljava/lang/annotation/Annotation;", NULL);
+ if (gDvm.classJavaLangAnnotationAnnotationArray == NULL ||
+ gDvm.classJavaLangAnnotationAnnotationArrayArray == NULL)
+ {
+ LOGE("Could not find Annotation-array classes\n");
+ return false;
+ }
+
+ /*
+ * VM-specific annotation classes.
+ */
+ gDvm.classOrgApacheHarmonyLangAnnotationAnnotationFactory =
+ dvmFindSystemClassNoInit("Lorg/apache/harmony/lang/annotation/AnnotationFactory;");
+ gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMember =
+ dvmFindSystemClassNoInit("Lorg/apache/harmony/lang/annotation/AnnotationMember;");
+ gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMemberArray =
+ dvmFindArrayClass("[Lorg/apache/harmony/lang/annotation/AnnotationMember;", NULL);
+ if (gDvm.classOrgApacheHarmonyLangAnnotationAnnotationFactory == NULL ||
+ gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMember == NULL ||
+ gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMemberArray == NULL)
+ {
+ LOGE("Could not find android.lang annotation classes\n");
+ return false;
+ }
+
+ meth = dvmFindDirectMethodByDescriptor(gDvm.classOrgApacheHarmonyLangAnnotationAnnotationFactory,
+ "createAnnotation",
+ "(Ljava/lang/Class;[Lorg/apache/harmony/lang/annotation/AnnotationMember;)Ljava/lang/annotation/Annotation;");
+ if (meth == NULL) {
+ LOGE("Unable to find createAnnotation() in android AnnotationFactory\n");
+ return false;
+ }
+ gDvm.methOrgApacheHarmonyLangAnnotationAnnotationFactory_createAnnotation = meth;
+
+ meth = dvmFindDirectMethodByDescriptor(gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMember,
+ "<init>",
+ "(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/reflect/Method;)V");
+ if (meth == NULL) {
+ LOGE("Unable to find 4-arg constructor in android AnnotationMember\n");
+ return false;
+ }
+
+ gDvm.methOrgApacheHarmonyLangAnnotationAnnotationMember_init = meth;
+
+ return true;
+}
+
+/*
+ * Read an unsigned LEB128 value from a buffer. Advances "pBuf".
+ */
+static u4 readUleb128(const u1** pBuf)
+{
+ u4 result = 0;
+ int shift = 0;
+ const u1* buf = *pBuf;
+ u1 val;
+
+ do {
+ /*
+ * Worst-case on bad data is we read too much data and return a bogus
+ * result. Safe to assume that we will encounter a byte with its
+ * high bit clear before the end of the mapped file.
+ */
+ assert(shift < 32);
+
+ val = *buf++;
+ result |= (val & 0x7f) << shift;
+ shift += 7;
+ } while ((val & 0x80) != 0);
+
+ *pBuf = buf;
+ return result;
+}
+
+/*
+ * Get the annotations directory item.
+ */
+static const DexAnnotationsDirectoryItem* getAnnoDirectory(DexFile* pDexFile,
+ const ClassObject* clazz)
+{
+ const DexClassDef* pClassDef;
+
+ /*
+ * Find the class def in the DEX file. For better performance we should
+ * stash this in the ClassObject.
+ */
+ pClassDef = dexFindClass(pDexFile, clazz->descriptor);
+ assert(pClassDef != NULL);
+ return dexGetAnnotationsDirectoryItem(pDexFile, pClassDef);
+}
+
+/*
+ * Return a zero-length array of Annotation objects.
+ *
+ * TODO: this currently allocates a new array each time, but I think we
+ * can get away with returning a canonical copy.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+static ArrayObject* emptyAnnoArray(void)
+{
+ return dvmAllocArrayByClass(
+ gDvm.classJavaLangAnnotationAnnotationArray, 0, ALLOC_DEFAULT);
+}
+
+/*
+ * Return an array of empty arrays of Annotation objects.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+static ArrayObject* emptyAnnoArrayArray(int numElements)
+{
+ Thread* self = dvmThreadSelf();
+ ArrayObject* arr;
+ int i;
+
+ arr = dvmAllocArrayByClass(gDvm.classJavaLangAnnotationAnnotationArrayArray,
+ numElements, ALLOC_DEFAULT);
+ if (arr != NULL) {
+ ArrayObject** elems = (ArrayObject**) arr->contents;
+ for (i = 0; i < numElements; i++) {
+ elems[i] = emptyAnnoArray();
+ dvmReleaseTrackedAlloc((Object*)elems[i], self);
+ }
+ }
+
+ return arr;
+}
+
+/*
+ * Read a signed integer. "zwidth" is the zero-based byte count.
+ */
+static s4 readSignedInt(const u1* ptr, int zwidth)
+{
+ s4 val = 0;
+ int i;
+
+ for (i = zwidth; i >= 0; --i)
+ val = ((u4)val >> 8) | (((s4)*ptr++) << 24);
+ val >>= (3 - zwidth) * 8;
+
+ return val;
+}
+
+/*
+ * Read an unsigned integer. "zwidth" is the zero-based byte count,
+ * "fillOnRight" indicates which side we want to zero-fill from.
+ */
+static u4 readUnsignedInt(const u1* ptr, int zwidth, bool fillOnRight)
+{
+ u4 val = 0;
+ int i;
+
+ if (!fillOnRight) {
+ for (i = zwidth; i >= 0; --i)
+ val = (val >> 8) | (((u4)*ptr++) << 24);
+ val >>= (3 - zwidth) * 8;
+ } else {
+ for (i = zwidth; i >= 0; --i)
+ val = (val >> 8) | (((u4)*ptr++) << 24);
+ }
+ return val;
+}
+
+/*
+ * Read a signed long. "zwidth" is the zero-based byte count.
+ */
+static s8 readSignedLong(const u1* ptr, int zwidth)
+{
+ s8 val = 0;
+ int i;
+
+ for (i = zwidth; i >= 0; --i)
+ val = ((u8)val >> 8) | (((s8)*ptr++) << 56);
+ val >>= (7 - zwidth) * 8;
+
+ return val;
+}
+
+/*
+ * Read an unsigned long. "zwidth" is the zero-based byte count,
+ * "fillOnRight" indicates which side we want to zero-fill from.
+ */
+static u8 readUnsignedLong(const u1* ptr, int zwidth, bool fillOnRight)
+{
+ u8 val = 0;
+ int i;
+
+ if (!fillOnRight) {
+ for (i = zwidth; i >= 0; --i)
+ val = (val >> 8) | (((u8)*ptr++) << 56);
+ val >>= (7 - zwidth) * 8;
+ } else {
+ for (i = zwidth; i >= 0; --i)
+ val = (val >> 8) | (((u8)*ptr++) << 56);
+ }
+ return val;
+}
+
+
+/*
+ * ===========================================================================
+ * Element extraction
+ * ===========================================================================
+ */
+
+/*
+ * An annotation in "clazz" refers to a method by index. This just gives
+ * us the name of the class and the name and signature of the method. We
+ * need to find the method's class, and then find the method within that
+ * class. If the method has been resolved before, we can just use the
+ * results of the previous lookup.
+ *
+ * Normally we do this as part of method invocation in the interpreter, which
+ * provides us with a bit of context: is it virtual or direct, do we need
+ * to initialize the class because it's a static method, etc. We don't have
+ * that information here, so we have to do a bit of searching.
+ *
+ * Returns NULL if the method was not found (exception may be pending).
+ */
+static Method* resolveAmbiguousMethod(const ClassObject* referrer, u4 methodIdx)
+{
+ DexFile* pDexFile;
+ ClassObject* resClass;
+ Method* resMethod;
+ const DexMethodId* pMethodId;
+ const char* name;
+
+ /* if we've already resolved this method, return it */
+ resMethod = dvmDexGetResolvedMethod(referrer->pDvmDex, methodIdx);
+ if (resMethod != NULL)
+ return resMethod;
+
+ pDexFile = referrer->pDvmDex->pDexFile;
+ pMethodId = dexGetMethodId(pDexFile, methodIdx);
+ resClass = dvmResolveClass(referrer, pMethodId->classIdx, true);
+ if (resClass == NULL) {
+ /* note exception will be pending */
+ LOGD("resolveAmbiguousMethod: unable to find class %d\n", methodIdx);
+ return NULL;
+ }
+ if (dvmIsInterfaceClass(resClass)) {
+ /* method is part of an interface -- not expecting that */
+ LOGD("resolveAmbiguousMethod: method in interface?\n");
+ return NULL;
+ }
+
+ // TODO - consider a method access flag that indicates direct vs. virtual
+ name = dexStringById(pDexFile, pMethodId->nameIdx);
+
+ DexProto proto;
+ dexProtoSetFromMethodId(&proto, pDexFile, pMethodId);
+
+ if (name[0] == '<') {
+ /*
+ * Constructor or class initializer. Only need to examine the
+ * "direct" list, and don't need to look up the class hierarchy.
+ */
+ resMethod = dvmFindDirectMethod(resClass, name, &proto);
+ } else {
+ /*
+ * Do a hierarchical scan for direct and virtual methods.
+ *
+ * This uses the search order from the VM spec (v2 5.4.3.3), which
+ * seems appropriate here.
+ */
+ resMethod = dvmFindMethodHier(resClass, name, &proto);
+ }
+
+ return resMethod;
+}
+
+/*
+ * constants for processAnnotationValue indicating what style of
+ * result is wanted
+ */
+typedef enum {
+ kAllObjects, /* return everything as an object */
+ kAllRaw, /* return everything as a raw value or index */
+ kPrimitivesOrObjects /* return primitives as-is but the rest as objects */
+} AnnotationResultStyle;
+
+/*
+ * Recursively process an annotation value.
+ *
+ * "clazz" is the class on which the annotations are defined. It may be
+ * NULL when "resultStyle" is "kAllRaw".
+ *
+ * If "resultStyle" is "kAllObjects", the result will always be an Object of an
+ * appropriate type (in pValue->value.l). For primitive types, the usual
+ * wrapper objects will be created.
+ *
+ * If "resultStyle" is "kAllRaw", numeric constants are stored directly into
+ * "pValue", and indexed values like String and Method are returned as
+ * indexes. Complex values like annotations and arrays are not handled.
+ *
+ * If "resultStyle" is "kPrimitivesOrObjects", numeric constants are stored
+ * directly into "pValue", and everything else is constructed as an Object
+ * of appropriate type (in pValue->value.l).
+ *
+ * The caller must call dvmReleaseTrackedAlloc on returned objects, when
+ * using "kAllObjects" or "kPrimitivesOrObjects".
+ *
+ * Returns "true" on success, "false" if the value could not be processed
+ * or an object could not be allocated. On allocation failure an exception
+ * will be raised.
+ */
+static bool processAnnotationValue(const ClassObject* clazz,
+ const u1** pPtr, AnnotationValue* pValue,
+ AnnotationResultStyle resultStyle)
+{
+ Thread* self = dvmThreadSelf();
+ Object* elemObj = NULL;
+ bool setObject = false;
+ const u1* ptr = *pPtr;
+ u1 valueType, valueArg;
+ int width;
+ u4 idx;
+
+ valueType = *ptr++;
+ valueArg = valueType >> kDexAnnotationValueArgShift;
+ width = valueArg + 1; /* assume, correct later */
+
+ LOGV("----- type is 0x%02x %d, ptr=%p [0x%06x]\n",
+ valueType & kDexAnnotationValueTypeMask, valueArg, ptr-1,
+ (ptr-1) - (u1*)clazz->pDvmDex->pDexFile->baseAddr);
+
+ pValue->type = valueType & kDexAnnotationValueTypeMask;
+
+ switch (valueType & kDexAnnotationValueTypeMask) {
+ case kDexAnnotationByte:
+ pValue->value.i = (s1) readSignedInt(ptr, valueArg);
+ if (resultStyle == kAllObjects) {
+ elemObj = (Object*) dvmWrapPrimitive(pValue->value,
+ dvmFindPrimitiveClass('B'));
+ setObject = true;
+ }
+ break;
+ case kDexAnnotationShort:
+ pValue->value.i = (s2) readSignedInt(ptr, valueArg);
+ if (resultStyle == kAllObjects) {
+ elemObj = (Object*) dvmWrapPrimitive(pValue->value,
+ dvmFindPrimitiveClass('S'));
+ setObject = true;
+ }
+ break;
+ case kDexAnnotationChar:
+ pValue->value.i = (u2) readUnsignedInt(ptr, valueArg, false);
+ if (resultStyle == kAllObjects) {
+ elemObj = (Object*) dvmWrapPrimitive(pValue->value,
+ dvmFindPrimitiveClass('C'));
+ setObject = true;
+ }
+ break;
+ case kDexAnnotationInt:
+ pValue->value.i = readSignedInt(ptr, valueArg);
+ if (resultStyle == kAllObjects) {
+ elemObj = (Object*) dvmWrapPrimitive(pValue->value,
+ dvmFindPrimitiveClass('I'));
+ setObject = true;
+ }
+ break;
+ case kDexAnnotationLong:
+ pValue->value.j = readSignedLong(ptr, valueArg);
+ if (resultStyle == kAllObjects) {
+ elemObj = (Object*) dvmWrapPrimitive(pValue->value,
+ dvmFindPrimitiveClass('J'));
+ setObject = true;
+ }
+ break;
+ case kDexAnnotationFloat:
+ pValue->value.i = readUnsignedInt(ptr, valueArg, true);
+ if (resultStyle == kAllObjects) {
+ elemObj = (Object*) dvmWrapPrimitive(pValue->value,
+ dvmFindPrimitiveClass('F'));
+ setObject = true;
+ }
+ break;
+ case kDexAnnotationDouble:
+ pValue->value.j = readUnsignedLong(ptr, valueArg, true);
+ if (resultStyle == kAllObjects) {
+ elemObj = (Object*) dvmWrapPrimitive(pValue->value,
+ dvmFindPrimitiveClass('D'));
+ setObject = true;
+ }
+ break;
+ case kDexAnnotationBoolean:
+ pValue->value.i = (valueArg != 0);
+ if (resultStyle == kAllObjects) {
+ elemObj = (Object*) dvmWrapPrimitive(pValue->value,
+ dvmFindPrimitiveClass('Z'));
+ setObject = true;
+ }
+ width = 0;
+ break;
+
+ case kDexAnnotationString:
+ idx = readUnsignedInt(ptr, valueArg, false);
+ if (resultStyle == kAllRaw) {
+ pValue->value.i = idx;
+ } else {
+ elemObj = (Object*) dvmResolveString(clazz, idx);
+ setObject = true;
+ if (elemObj == NULL)
+ return false;
+ dvmAddTrackedAlloc(elemObj, self); // balance the Release
+ }
+ break;
+ case kDexAnnotationType:
+ idx = readUnsignedInt(ptr, valueArg, false);
+ if (resultStyle == kAllRaw) {
+ pValue->value.i = idx;
+ } else {
+ elemObj = (Object*) dvmResolveClass(clazz, idx, true);
+ setObject = true;
+ if (elemObj == NULL) {
+ /* we're expected to throw a TypeNotPresentException here */
+ DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+ const char* desc = dexStringByTypeIdx(pDexFile, idx);
+ dvmClearException(self);
+ dvmThrowExceptionWithClassMessage(
+ "Ljava/lang/TypeNotPresentException;", desc);
+ return false;
+ } else {
+ dvmAddTrackedAlloc(elemObj, self); // balance the Release
+ }
+ }
+ break;
+ case kDexAnnotationMethod:
+ idx = readUnsignedInt(ptr, valueArg, false);
+ if (resultStyle == kAllRaw) {
+ pValue->value.i = idx;
+ } else {
+ Method* meth = resolveAmbiguousMethod(clazz, idx);
+ if (meth == NULL)
+ return false;
+ elemObj = dvmCreateReflectObjForMethod(clazz, meth);
+ setObject = true;
+ if (elemObj == NULL)
+ return false;
+ }
+ break;
+ case kDexAnnotationField:
+ idx = readUnsignedInt(ptr, valueArg, false);
+ assert(false); // TODO
+ break;
+ case kDexAnnotationEnum:
+ /* enum values are the contents of a static field */
+ idx = readUnsignedInt(ptr, valueArg, false);
+ if (resultStyle == kAllRaw) {
+ pValue->value.i = idx;
+ } else {
+ StaticField* sfield;
+
+ sfield = dvmResolveStaticField(clazz, idx);
+ if (sfield == NULL) {
+ return false;
+ } else {
+ assert(sfield->field.clazz->descriptor[0] == 'L');
+ elemObj = sfield->value.l;
+ setObject = true;
+ dvmAddTrackedAlloc(elemObj, self); // balance the Release
+ }
+ }
+ break;
+ case kDexAnnotationArray:
+ /*
+ * encoded_array format, which is a size followed by a stream
+ * of annotation_value.
+ *
+ * We create an array of Object, populate it, and return it.
+ */
+ if (resultStyle == kAllRaw) {
+ return false;
+ } else {
+ ArrayObject* newArray;
+ u4 size, count;
+
+ size = readUleb128(&ptr);
+ LOGVV("--- annotation array, size is %u at %p\n", size, ptr);
+ newArray = dvmAllocArrayByClass(gDvm.classJavaLangObjectArray,
+ size, ALLOC_DEFAULT);
+ if (newArray == NULL) {
+ LOGE("annotation element array alloc failed (%d)\n", size);
+ return false;
+ }
+
+ AnnotationValue avalue;
+ for (count = 0; count < size; count++) {
+ if (!processAnnotationValue(clazz, &ptr, &avalue,
+ kAllObjects)) {
+ dvmReleaseTrackedAlloc((Object*)newArray, self);
+ return false;
+ }
+ Object* obj = avalue.value.l;
+ dvmSetObjectArrayElement(newArray, count, obj);
+ dvmReleaseTrackedAlloc(obj, self);
+ }
+
+ elemObj = (Object*) newArray;
+ setObject = true;
+ }
+ width = 0;
+ break;
+ case kDexAnnotationAnnotation:
+ /* encoded_annotation format */
+ if (resultStyle == kAllRaw)
+ return false;
+ elemObj = processEncodedAnnotation(clazz, &ptr);
+ setObject = true;
+ if (elemObj == NULL)
+ return false;
+ dvmAddTrackedAlloc(elemObj, self); // balance the Release
+ width = 0;
+ break;
+ case kDexAnnotationNull:
+ if (resultStyle == kAllRaw) {
+ pValue->value.i = 0;
+ } else {
+ assert(elemObj == NULL);
+ setObject = true;
+ }
+ width = 0;
+ break;
+ default:
+ LOGE("Bad annotation element value byte 0x%02x (0x%02x)\n",
+ valueType, valueType & kDexAnnotationValueTypeMask);
+ assert(false);
+ return false;
+ }
+
+ ptr += width;
+
+ *pPtr = ptr;
+ if (setObject)
+ pValue->value.l = elemObj;
+ return true;
+}
+
+
+/*
+ * For most object types, we have nothing to do here, and we just return
+ * "valueObj".
+ *
+ * For an array annotation, the type of the extracted object will always
+ * be java.lang.Object[], but we want it to match the type that the
+ * annotation member is expected to return. In some cases this may
+ * involve un-boxing primitive values.
+ *
+ * We allocate a second array with the correct type, then copy the data
+ * over. This releases the tracked allocation on "valueObj" and returns
+ * a new, tracked object.
+ *
+ * On failure, this releases the tracking on "valueObj" and returns NULL
+ * (allowing the call to say "foo = convertReturnType(foo, ..)").
+ */
+static Object* convertReturnType(Object* valueObj, ClassObject* methodReturn)
+{
+ if (valueObj == NULL ||
+ !dvmIsArray((ArrayObject*)valueObj) || !dvmIsArrayClass(methodReturn))
+ {
+ return valueObj;
+ }
+
+ Thread* self = dvmThreadSelf();
+ ClassObject* srcElemClass;
+ ClassObject* dstElemClass;
+
+ /*
+ * We always extract kDexAnnotationArray into Object[], so we expect to
+ * find that here. This means we can skip the FindClass on
+ * (valueObj->clazz->descriptor+1, valueObj->clazz->classLoader).
+ */
+ if (strcmp(valueObj->clazz->descriptor, "[Ljava/lang/Object;") != 0) {
+ LOGE("Unexpected src type class (%s)\n", valueObj->clazz->descriptor);
+ return NULL;
+ }
+ srcElemClass = gDvm.classJavaLangObject;
+
+ /*
+ * Skip past the '[' to get element class name. Note this is not always
+ * the same as methodReturn->elementClass.
+ */
+ char firstChar = methodReturn->descriptor[1];
+ if (firstChar == 'L' || firstChar == '[') {
+ dstElemClass = dvmFindClass(methodReturn->descriptor+1,
+ methodReturn->classLoader);
+ } else {
+ dstElemClass = dvmFindPrimitiveClass(firstChar);
+ }
+ LOGV("HEY: converting valueObj from [%s to [%s\n",
+ srcElemClass->descriptor, dstElemClass->descriptor);
+
+ ArrayObject* srcArray = (ArrayObject*) valueObj;
+ u4 length = srcArray->length;
+ ArrayObject* newArray;
+
+ newArray = dvmAllocArrayByClass(methodReturn, length, ALLOC_DEFAULT);
+ if (newArray == NULL) {
+ LOGE("Failed creating duplicate annotation class (%s %d)\n",
+ methodReturn->descriptor, length);
+ goto bail;
+ }
+
+ bool success;
+ if (dstElemClass->primitiveType == PRIM_NOT) {
+ success = dvmCopyObjectArray(newArray, srcArray, dstElemClass);
+ } else {
+ success = dvmUnboxObjectArray(newArray, srcArray, dstElemClass);
+ }
+ if (!success) {
+ LOGE("Annotation array copy failed\n");
+ dvmReleaseTrackedAlloc((Object*)newArray, self);
+ newArray = NULL;
+ goto bail;
+ }
+
+bail:
+ /* replace old, return new */
+ dvmReleaseTrackedAlloc(valueObj, self);
+ return (Object*) newArray;
+}
+
+/*
+ * Create a new AnnotationMember.
+ *
+ * "clazz" is the class on which the annotations are defined. "pPtr"
+ * points to a pointer into the annotation data. "annoClass" is the
+ * annotation's class.
+ *
+ * We extract the annotation's value, create a new AnnotationMember object,
+ * and construct it.
+ *
+ * Returns NULL on failure; an exception may or may not be raised.
+ */
+static Object* createAnnotationMember(const ClassObject* clazz,
+ const ClassObject* annoClass, const u1** pPtr)
+{
+ Thread* self = dvmThreadSelf();
+ const DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+ StringObject* nameObj = NULL;
+ Object* valueObj = NULL;
+ Object* newMember = NULL;
+ Object* methodObj = NULL;
+ ClassObject* methodReturn = NULL;
+ u4 elementNameIdx;
+ const char* name;
+ AnnotationValue avalue;
+ JValue result;
+ bool failed = true;
+
+ elementNameIdx = readUleb128(pPtr);
+
+ if (!processAnnotationValue(clazz, pPtr, &avalue, kAllObjects)) {
+ LOGW("Failed processing annotation value\n");
+ goto bail;
+ }
+ valueObj = avalue.value.l;
+
+ /* new member to hold the element */
+ newMember =
+ dvmAllocObject(gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMember,
+ ALLOC_DEFAULT);
+ name = dexStringById(pDexFile, elementNameIdx);
+ nameObj = dvmCreateStringFromCstr(name);
+
+ /* find the method in the annotation class, given only the name */
+ if (name != NULL) {
+ Method* annoMeth = dvmFindVirtualMethodByName(annoClass, name);
+ if (annoMeth == NULL) {
+ LOGW("WARNING: could not find annotation member %s in %s\n",
+ name, annoClass->descriptor);
+ } else {
+ methodObj = dvmCreateReflectMethodObject(annoMeth);
+ methodReturn = dvmGetBoxedReturnType(annoMeth);
+ }
+ }
+ if (newMember == NULL || nameObj == NULL || methodObj == NULL ||
+ methodReturn == NULL)
+ {
+ LOGE("Failed creating annotation element (m=%p n=%p a=%p r=%p)\n",
+ newMember, nameObj, methodObj, methodReturn);
+ goto bail;
+ }
+
+ /* convert the return type, if necessary */
+ valueObj = convertReturnType(valueObj, methodReturn);
+ if (valueObj == NULL)
+ goto bail;
+
+ /* call 4-argument constructor */
+ dvmCallMethod(self, gDvm.methOrgApacheHarmonyLangAnnotationAnnotationMember_init,
+ newMember, &result, nameObj, valueObj, methodReturn, methodObj);
+ if (dvmCheckException(self)) {
+ LOGD("Failed constructing annotation element\n");
+ goto bail;
+ }
+
+ failed = false;
+
+bail:
+ /* release tracked allocations */
+ dvmReleaseTrackedAlloc(newMember, self);
+ dvmReleaseTrackedAlloc((Object*)nameObj, self);
+ dvmReleaseTrackedAlloc(valueObj, self);
+ dvmReleaseTrackedAlloc(methodObj, self);
+ if (failed)
+ return NULL;
+ else
+ return newMember;
+}
+
+/*
+ * Create a new Annotation object from what we find in the annotation item.
+ *
+ * "clazz" is the class on which the annotations are defined. "pPtr"
+ * points to a pointer into the annotation data.
+ *
+ * We use the AnnotationFactory class to create the annotation for us. The
+ * method we call is:
+ *
+ * public static Annotation createAnnotation(
+ * Class<? extends Annotation> annotationType,
+ * AnnotationMember[] elements)
+ *
+ * Returns a new Annotation, which will NOT be in the local ref table and
+ * not referenced elsewhere, so store it away soon. On failure, returns NULL
+ * with an exception raised.
+ */
+static Object* processEncodedAnnotation(const ClassObject* clazz,
+ const u1** pPtr)
+{
+ Thread* self = dvmThreadSelf();
+ Object* newAnno = NULL;
+ ArrayObject* elementArray = NULL;
+ const ClassObject* annoClass;
+ const u1* ptr;
+ u4 typeIdx, size, count;
+
+ ptr = *pPtr;
+ typeIdx = readUleb128(&ptr);
+ size = readUleb128(&ptr);
+
+ LOGVV("----- processEnc ptr=%p type=%d size=%d\n", ptr, typeIdx, size);
+
+ annoClass = dvmDexGetResolvedClass(clazz->pDvmDex, typeIdx);
+ if (annoClass == NULL) {
+ annoClass = dvmResolveClass(clazz, typeIdx, true);
+ if (annoClass == NULL) {
+ LOGE("Unable to resolve %s annotation class %d\n",
+ clazz->descriptor, typeIdx);
+ assert(dvmCheckException(self));
+ return NULL;
+ }
+ }
+
+ LOGV("----- processEnc ptr=%p [0x%06x] typeIdx=%d size=%d class=%s\n",
+ *pPtr, *pPtr - (u1*) clazz->pDvmDex->pDexFile->baseAddr,
+ typeIdx, size, annoClass->descriptor);
+
+ /*
+ * Elements are parsed out and stored in an array. The Harmony
+ * constructor wants an array with just the declared elements --
+ * default values get merged in later.
+ */
+ JValue result;
+
+ if (size > 0) {
+ elementArray = dvmAllocArrayByClass(
+ gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMemberArray,
+ size, ALLOC_DEFAULT);
+ if (elementArray == NULL) {
+ LOGE("failed to allocate annotation member array (%d elements)\n",
+ size);
+ goto bail;
+ }
+ }
+
+ /*
+ * "ptr" points to a byte stream with "size" occurrences of
+ * annotation_element.
+ */
+ for (count = 0; count < size; count++) {
+ Object* newMember = createAnnotationMember(clazz, annoClass, &ptr);
+ if (newMember == NULL)
+ goto bail;
+
+ /* add it to the array */
+ dvmSetObjectArrayElement(elementArray, count, newMember);
+ }
+
+ dvmCallMethod(self,
+ gDvm.methOrgApacheHarmonyLangAnnotationAnnotationFactory_createAnnotation,
+ NULL, &result, annoClass, elementArray);
+ if (dvmCheckException(self)) {
+ LOGD("Failed creating an annotation\n");
+ //dvmLogExceptionStackTrace();
+ goto bail;
+ }
+
+ newAnno = result.l;
+
+bail:
+ dvmReleaseTrackedAlloc((Object*) elementArray, NULL);
+ *pPtr = ptr;
+ if (newAnno == NULL && !dvmCheckException(self)) {
+ /* make sure an exception is raised */
+ dvmThrowException("Ljava/lang/RuntimeException;",
+ "failure in processEncodedAnnotation");
+ }
+ return newAnno;
+}
+
+/*
+ * Run through an annotation set and convert each entry into an Annotation
+ * object.
+ *
+ * Returns an array of Annotation objects, or NULL with an exception raised
+ * on alloc failure.
+ */
+static ArrayObject* processAnnotationSet(const ClassObject* clazz,
+ const DexAnnotationSetItem* pAnnoSet, int visibility)
+{
+ DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+ const DexAnnotationItem* pAnnoItem;
+ ArrayObject* annoArray;
+ int i, count;
+ u4 dstIndex;
+
+ /* we need these later; make sure they're initialized */
+ if (!dvmIsClassInitialized(gDvm.classOrgApacheHarmonyLangAnnotationAnnotationFactory))
+ dvmInitClass(gDvm.classOrgApacheHarmonyLangAnnotationAnnotationFactory);
+ if (!dvmIsClassInitialized(gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMember))
+ dvmInitClass(gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMember);
+
+ /* count up the number of visible elements */
+ for (i = count = 0; i < (int) pAnnoSet->size; i++) {
+ pAnnoItem = dexGetAnnotationItem(pDexFile, pAnnoSet, i);
+ if (pAnnoItem->visibility == visibility)
+ count++;
+ }
+
+ annoArray =
+ dvmAllocArrayByClass(gDvm.classJavaLangAnnotationAnnotationArray,
+ count, ALLOC_DEFAULT);
+ if (annoArray == NULL)
+ return NULL;
+
+ /*
+ * Generate Annotation objects. We must put them into the array
+ * immediately (or add them to the tracked ref table).
+ */
+ dstIndex = 0;
+ for (i = 0; i < (int) pAnnoSet->size; i++) {
+ pAnnoItem = dexGetAnnotationItem(pDexFile, pAnnoSet, i);
+ if (pAnnoItem->visibility != visibility)
+ continue;
+ const u1* ptr = pAnnoItem->annotation;
+ Object *anno = processEncodedAnnotation(clazz, &ptr);
+ if (anno == NULL) {
+ dvmReleaseTrackedAlloc((Object*) annoArray, NULL);
+ return NULL;
+ }
+ dvmSetObjectArrayElement(annoArray, dstIndex, anno);
+ ++dstIndex;
+ }
+
+ return annoArray;
+}
+
+
+/*
+ * ===========================================================================
+ * Skipping and scanning
+ * ===========================================================================
+ */
+
+/*
+ * Skip past an annotation value.
+ *
+ * "clazz" is the class on which the annotations are defined.
+ *
+ * Returns "true" on success, "false" on parsing failure.
+ */
+static bool skipAnnotationValue(const ClassObject* clazz, const u1** pPtr)
+{
+ const u1* ptr = *pPtr;
+ u1 valueType, valueArg;
+ int width;
+
+ valueType = *ptr++;
+ valueArg = valueType >> kDexAnnotationValueArgShift;
+ width = valueArg + 1; /* assume */
+
+ LOGV("----- type is 0x%02x %d, ptr=%p [0x%06x]\n",
+ valueType & kDexAnnotationValueTypeMask, valueArg, ptr-1,
+ (ptr-1) - (u1*)clazz->pDvmDex->pDexFile->baseAddr);
+
+ switch (valueType & kDexAnnotationValueTypeMask) {
+ case kDexAnnotationByte: break;
+ case kDexAnnotationShort: break;
+ case kDexAnnotationChar: break;
+ case kDexAnnotationInt: break;
+ case kDexAnnotationLong: break;
+ case kDexAnnotationFloat: break;
+ case kDexAnnotationDouble: break;
+ case kDexAnnotationString: break;
+ case kDexAnnotationType: break;
+ case kDexAnnotationMethod: break;
+ case kDexAnnotationField: break;
+ case kDexAnnotationEnum: break;
+
+ case kDexAnnotationArray:
+ /* encoded_array format */
+ {
+ u4 size = readUleb128(&ptr);
+ while (size--) {
+ if (!skipAnnotationValue(clazz, &ptr))
+ return false;
+ }
+ }
+ width = 0;
+ break;
+ case kDexAnnotationAnnotation:
+ /* encoded_annotation format */
+ if (!skipEncodedAnnotation(clazz, &ptr))
+ return false;
+ width = 0;
+ break;
+ case kDexAnnotationBoolean:
+ case kDexAnnotationNull:
+ width = 0;
+ break;
+ default:
+ LOGE("Bad annotation element value byte 0x%02x\n", valueType);
+ assert(false);
+ return false;
+ }
+
+ ptr += width;
+
+ *pPtr = ptr;
+ return true;
+}
+
+/*
+ * Skip past an encoded annotation. Mainly useful for annotations embedded
+ * in other annotations.
+ */
+static bool skipEncodedAnnotation(const ClassObject* clazz, const u1** pPtr)
+{
+ const u1* ptr;
+ u4 size;
+
+ ptr = *pPtr;
+ (void) readUleb128(&ptr);
+ size = readUleb128(&ptr);
+
+ /*
+ * "ptr" points to a byte stream with "size" occurrences of
+ * annotation_element.
+ */
+ while (size--) {
+ (void) readUleb128(&ptr);
+
+ if (!skipAnnotationValue(clazz, &ptr))
+ return false;
+ }
+
+ *pPtr = ptr;
+ return true;
+}
+
+
+/*
+ * Compare the name of the class in the DEX file to the supplied descriptor.
+ * Return value is equivalent to strcmp.
+ */
+static int compareClassDescriptor(DexFile* pDexFile, u4 typeIdx,
+ const char* descriptor)
+{
+ const char* str = dexStringByTypeIdx(pDexFile, typeIdx);
+
+ return strcmp(str, descriptor);
+}
+
+/*
+ * Search through the annotation set for an annotation with a matching
+ * descriptor.
+ *
+ * Comparing the string descriptor is slower than comparing an integer class
+ * index. If annotation lists are expected to be long, we could look up
+ * the class' index by name from the DEX file, rather than doing a class
+ * lookup and string compare on each entry. (Note the index will be
+ * different for each DEX file, so we can't cache annotation class indices
+ * globally.)
+ */
+static const DexAnnotationItem* searchAnnotationSet(const ClassObject* clazz,
+ const DexAnnotationSetItem* pAnnoSet, const char* descriptor,
+ int visibility)
+{
+ DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+ const DexAnnotationItem* result = NULL;
+ u4 typeIdx;
+ int i;
+
+ //printf("##### searchAnnotationSet %s %d\n", descriptor, visibility);
+
+ for (i = 0; i < (int) pAnnoSet->size; i++) {
+ const DexAnnotationItem* pAnnoItem;
+
+ pAnnoItem = dexGetAnnotationItem(pDexFile, pAnnoSet, i);
+ if (pAnnoItem->visibility != visibility)
+ continue;
+ const u1* ptr = pAnnoItem->annotation;
+ typeIdx = readUleb128(&ptr);
+
+ if (compareClassDescriptor(pDexFile, typeIdx, descriptor) == 0) {
+ //printf("##### match on %x/%p at %d\n", typeIdx, pDexFile, i);
+ result = pAnnoItem;
+ break;
+ }
+ }
+
+ return result;
+}
+
+/*
+ * Find an annotation value in the annotation_item whose name matches "name".
+ * A pointer to the annotation_value is returned, or NULL if it's not found.
+ */
+static const u1* searchEncodedAnnotation(const ClassObject* clazz,
+ const u1* ptr, const char* name)
+{
+ DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+ u4 typeIdx, size;
+
+ typeIdx = readUleb128(&ptr);
+ size = readUleb128(&ptr);
+ //printf("##### searching ptr=%p type=%u size=%u\n", ptr, typeIdx, size);
+
+ while (size--) {
+ u4 elementNameIdx;
+ const char* elemName;
+
+ elementNameIdx = readUleb128(&ptr);
+ elemName = dexStringById(pDexFile, elementNameIdx);
+ if (strcmp(name, elemName) == 0) {
+ //printf("##### item match on %s\n", name);
+ return ptr; /* points to start of value */
+ }
+
+ skipAnnotationValue(clazz, &ptr);
+ }
+
+ //printf("##### no item match on %s\n", name);
+ return NULL;
+}
+
+#define GAV_FAILED ((Object*) 0x10000001)
+
+/*
+ * Extract an encoded annotation value from the field specified by "annoName".
+ *
+ * "expectedType" is an annotation value type, e.g. kDexAnnotationString.
+ * "debugAnnoName" is only used in debug messages.
+ *
+ * Returns GAV_FAILED on failure. If an allocation failed, an exception
+ * will be raised.
+ */
+static Object* getAnnotationValue(const ClassObject* clazz,
+ const DexAnnotationItem* pAnnoItem, const char* annoName,
+ int expectedType, const char* debugAnnoName)
+{
+ const u1* ptr;
+ AnnotationValue avalue;
+
+ /* find the annotation */
+ ptr = searchEncodedAnnotation(clazz, pAnnoItem->annotation, annoName);
+ if (ptr == NULL) {
+ LOGW("%s annotation lacks '%s' member\n", debugAnnoName, annoName);
+ return GAV_FAILED;
+ }
+
+ if (!processAnnotationValue(clazz, &ptr, &avalue, kAllObjects))
+ return GAV_FAILED;
+
+ /* make sure it has the expected format */
+ if (avalue.type != expectedType) {
+ LOGW("%s %s has wrong type (0x%02x, expected 0x%02x)\n",
+ debugAnnoName, annoName, avalue.type, expectedType);
+ return GAV_FAILED;
+ }
+
+ return avalue.value.l;
+}
+
+
+/*
+ * Find the Signature attribute and extract its value. (Signatures can
+ * be found in annotations on classes, constructors, methods, and fields.)
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ *
+ * Returns NULL if not found. On memory alloc failure, returns NULL with an
+ * exception raised.
+ */
+static ArrayObject* getSignatureValue(const ClassObject* clazz,
+ const DexAnnotationSetItem* pAnnoSet)
+{
+ const DexAnnotationItem* pAnnoItem;
+ Object* obj;
+
+ pAnnoItem = searchAnnotationSet(clazz, pAnnoSet, kDescrSignature,
+ kDexVisibilitySystem);
+ if (pAnnoItem == NULL)
+ return NULL;
+
+ /*
+ * The Signature annotation has one member, "String value".
+ */
+ obj = getAnnotationValue(clazz, pAnnoItem, "value", kDexAnnotationArray,
+ "Signature");
+ if (obj == GAV_FAILED)
+ return NULL;
+ assert(obj->clazz == gDvm.classJavaLangObjectArray);
+
+ return (ArrayObject*)obj;
+}
+
+
+/*
+ * ===========================================================================
+ * Class
+ * ===========================================================================
+ */
+
+/*
+ * Find the DexAnnotationSetItem for this class.
+ */
+static const DexAnnotationSetItem* findAnnotationSetForClass(
+ const ClassObject* clazz)
+{
+ DexFile* pDexFile;
+ const DexAnnotationsDirectoryItem* pAnnoDir;
+
+ if (clazz->pDvmDex == NULL) /* generated class (Proxy, array) */
+ return NULL;
+
+ pDexFile = clazz->pDvmDex->pDexFile;
+ pAnnoDir = getAnnoDirectory(pDexFile, clazz);
+ if (pAnnoDir != NULL)
+ return dexGetClassAnnotationSet(pDexFile, pAnnoDir);
+ else
+ return NULL;
+}
+
+/*
+ * Return an array of Annotation objects for the class. Returns an empty
+ * array if there are no annotations.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ *
+ * On allocation failure, this returns NULL with an exception raised.
+ */
+ArrayObject* dvmGetClassAnnotations(const ClassObject* clazz)
+{
+ ArrayObject* annoArray;
+ const DexAnnotationSetItem* pAnnoSet = NULL;
+
+ pAnnoSet = findAnnotationSetForClass(clazz);
+ if (pAnnoSet == NULL) {
+ /* no annotations for anything in class, or no class annotations */
+ annoArray = emptyAnnoArray();
+ } else {
+ annoArray = processAnnotationSet(clazz, pAnnoSet,
+ kDexVisibilityRuntime);
+ }
+
+ return annoArray;
+}
+
+/*
+ * Retrieve the Signature annotation, if any. Returns NULL if no signature
+ * exists.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+ArrayObject* dvmGetClassSignatureAnnotation(const ClassObject* clazz)
+{
+ ArrayObject* signature = NULL;
+ const DexAnnotationSetItem* pAnnoSet;
+
+ pAnnoSet = findAnnotationSetForClass(clazz);
+ if (pAnnoSet != NULL)
+ signature = getSignatureValue(clazz, pAnnoSet);
+
+ return signature;
+}
+
+/*
+ * Get the EnclosingMethod attribute from an annotation. Returns a Method
+ * object, or NULL.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+Object* dvmGetEnclosingMethod(const ClassObject* clazz)
+{
+ const DexAnnotationItem* pAnnoItem;
+ const DexAnnotationSetItem* pAnnoSet;
+ Object* obj;
+
+ pAnnoSet = findAnnotationSetForClass(clazz);
+ if (pAnnoSet == NULL)
+ return NULL;
+
+ pAnnoItem = searchAnnotationSet(clazz, pAnnoSet, kDescrEnclosingMethod,
+ kDexVisibilitySystem);
+ if (pAnnoItem == NULL)
+ return NULL;
+
+ /*
+ * The EnclosingMethod annotation has one member, "Method value".
+ */
+ obj = getAnnotationValue(clazz, pAnnoItem, "value", kDexAnnotationMethod,
+ "EnclosingMethod");
+ if (obj == GAV_FAILED)
+ return NULL;
+ assert(obj->clazz == gDvm.classJavaLangReflectConstructor ||
+ obj->clazz == gDvm.classJavaLangReflectMethod);
+
+ return obj;
+}
+
+/*
+ * Find a class' enclosing class. We return what we find in the
+ * EnclosingClass attribute.
+ *
+ * Returns a Class object, or NULL.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+ClassObject* dvmGetDeclaringClass(const ClassObject* clazz)
+{
+ const DexAnnotationItem* pAnnoItem;
+ const DexAnnotationSetItem* pAnnoSet;
+ Object* obj;
+
+ pAnnoSet = findAnnotationSetForClass(clazz);
+ if (pAnnoSet == NULL)
+ return NULL;
+
+ pAnnoItem = searchAnnotationSet(clazz, pAnnoSet, kDescrEnclosingClass,
+ kDexVisibilitySystem);
+ if (pAnnoItem == NULL)
+ return NULL;
+
+ /*
+ * The EnclosingClass annotation has one member, "Class value".
+ */
+ obj = getAnnotationValue(clazz, pAnnoItem, "value", kDexAnnotationType,
+ "EnclosingClass");
+ if (obj == GAV_FAILED)
+ return NULL;
+
+ assert(obj->clazz == gDvm.classJavaLangClass);
+ return (ClassObject*)obj;
+}
+
+/*
+ * Find a class' enclosing class. We first search for an EnclosingClass
+ * attribute, and if that's not found we look for an EnclosingMethod.
+ *
+ * Returns a Class object, or NULL.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+ClassObject* dvmGetEnclosingClass(const ClassObject* clazz)
+{
+ const DexAnnotationItem* pAnnoItem;
+ const DexAnnotationSetItem* pAnnoSet;
+ Object* obj;
+
+ pAnnoSet = findAnnotationSetForClass(clazz);
+ if (pAnnoSet == NULL)
+ return NULL;
+
+ pAnnoItem = searchAnnotationSet(clazz, pAnnoSet, kDescrEnclosingClass,
+ kDexVisibilitySystem);
+ if (pAnnoItem != NULL) {
+ /*
+ * The EnclosingClass annotation has one member, "Class value".
+ */
+ obj = getAnnotationValue(clazz, pAnnoItem, "value", kDexAnnotationType,
+ "EnclosingClass");
+ if (obj != GAV_FAILED) {
+ assert(obj->clazz == gDvm.classJavaLangClass);
+ return (ClassObject*)obj;
+ }
+ }
+
+ /*
+ * That didn't work. Look for an EnclosingMethod.
+ *
+ * We could create a java.lang.reflect.Method object and extract the
+ * declaringClass from it, but that's more work than we want to do.
+ * Instead, we find the "value" item and parse the index out ourselves.
+ */
+ pAnnoItem = searchAnnotationSet(clazz, pAnnoSet, kDescrEnclosingMethod,
+ kDexVisibilitySystem);
+ if (pAnnoItem == NULL)
+ return NULL;
+
+ /* find the value member */
+ const u1* ptr;
+ ptr = searchEncodedAnnotation(clazz, pAnnoItem->annotation, "value");
+ if (ptr == NULL) {
+ LOGW("EnclosingMethod annotation lacks 'value' member\n");
+ return NULL;
+ }
+
+ /* parse it, verify the type */
+ AnnotationValue avalue;
+ if (!processAnnotationValue(clazz, &ptr, &avalue, kAllRaw)) {
+ LOGW("EnclosingMethod parse failed\n");
+ return NULL;
+ }
+ if (avalue.type != kDexAnnotationMethod) {
+ LOGW("EnclosingMethod value has wrong type (0x%02x, expected 0x%02x)\n",
+ avalue.type, kDexAnnotationMethod);
+ return NULL;
+ }
+
+ /* pull out the method index and resolve the method */
+ Method* meth = resolveAmbiguousMethod(clazz, avalue.value.i);
+ if (meth == NULL)
+ return NULL;
+
+ ClassObject* methClazz = meth->clazz;
+ dvmAddTrackedAlloc((Object*) methClazz, NULL); // balance the Release
+ return methClazz;
+}
+
+/*
+ * Get the EnclosingClass attribute from an annotation. If found, returns
+ * "true". A String with the original name of the class and the original
+ * access flags are returned through the arguments. (The name will be NULL
+ * for an anonymous inner class.)
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+bool dvmGetInnerClass(const ClassObject* clazz, StringObject** pName,
+ int* pAccessFlags)
+{
+ const DexAnnotationItem* pAnnoItem;
+ const DexAnnotationSetItem* pAnnoSet;
+
+ pAnnoSet = findAnnotationSetForClass(clazz);
+ if (pAnnoSet == NULL)
+ return false;
+
+ pAnnoItem = searchAnnotationSet(clazz, pAnnoSet, kDescrInnerClass,
+ kDexVisibilitySystem);
+ if (pAnnoItem == NULL)
+ return false;
+
+ /*
+ * The InnerClass annotation has two members, "String name" and
+ * "int accessFlags". We don't want to get the access flags as an
+ * Integer, so we process that as a simple value.
+ */
+ const u1* ptr;
+ ptr = searchEncodedAnnotation(clazz, pAnnoItem->annotation, "name");
+ if (ptr == NULL) {
+ LOGW("InnerClass annotation lacks 'name' member\n");
+ return false;
+ }
+
+ /* parse it into an Object */
+ AnnotationValue avalue;
+ if (!processAnnotationValue(clazz, &ptr, &avalue, kAllObjects)) {
+ LOGD("processAnnotationValue failed on InnerClass member 'name'\n");
+ return false;
+ }
+
+ /* make sure it has the expected format */
+ if (avalue.type != kDexAnnotationNull &&
+ avalue.type != kDexAnnotationString)
+ {
+ LOGW("InnerClass name has bad type (0x%02x, expected STRING or NULL)\n",
+ avalue.type);
+ return false;
+ }
+
+ *pName = (StringObject*) avalue.value.l;
+ assert(*pName == NULL || (*pName)->obj.clazz == gDvm.classJavaLangString);
+
+ ptr = searchEncodedAnnotation(clazz, pAnnoItem->annotation, "accessFlags");
+ if (ptr == NULL) {
+ LOGW("InnerClass annotation lacks 'accessFlags' member\n");
+ return false;
+ }
+
+ /* parse it, verify the type */
+ if (!processAnnotationValue(clazz, &ptr, &avalue, kAllRaw)) {
+ LOGW("InnerClass accessFlags parse failed\n");
+ return false;
+ }
+ if (avalue.type != kDexAnnotationInt) {
+ LOGW("InnerClass value has wrong type (0x%02x, expected 0x%02x)\n",
+ avalue.type, kDexAnnotationInt);
+ return false;
+ }
+
+ *pAccessFlags = avalue.value.i;
+
+ return true;
+}
+
+/*
+ * Extract an array of Class objects from the MemberClasses annotation
+ * for this class.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ *
+ * Returns NULL if we don't find any member classes.
+ */
+ArrayObject* dvmGetDeclaredClasses(const ClassObject* clazz)
+{
+ const DexAnnotationSetItem* pAnnoSet;
+ const DexAnnotationItem* pAnnoItem;
+ Object* obj;
+
+ pAnnoSet = findAnnotationSetForClass(clazz);
+ if (pAnnoSet == NULL)
+ return NULL;
+
+ pAnnoItem = searchAnnotationSet(clazz, pAnnoSet, kDescrMemberClasses,
+ kDexVisibilitySystem);
+ if (pAnnoItem == NULL)
+ return NULL;
+
+ /*
+ * The MemberClasses annotation has one member, "Class[] value".
+ */
+ obj = getAnnotationValue(clazz, pAnnoItem, "value",
+ kDexAnnotationArray, "MemberClasses");
+ if (obj == GAV_FAILED)
+ return NULL;
+ assert(dvmIsArray((ArrayObject*)obj));
+ obj = convertReturnType(obj, gDvm.classJavaLangClassArray);
+ return (ArrayObject*)obj;
+}
+
+
+/*
+ * ===========================================================================
+ * Method (and Constructor)
+ * ===========================================================================
+ */
+
+/*
+ * Compare the attributes (class name, method name, method signature) of
+ * the specified method to "method".
+ */
+static int compareMethodStr(DexFile* pDexFile, u4 methodIdx,
+ const Method* method)
+{
+ const DexMethodId* pMethodId = dexGetMethodId(pDexFile, methodIdx);
+ const char* str = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
+ int result = strcmp(str, method->clazz->descriptor);
+
+ if (result == 0) {
+ str = dexStringById(pDexFile, pMethodId->nameIdx);
+ result = strcmp(str, method->name);
+ if (result == 0) {
+ DexProto proto;
+ dexProtoSetFromMethodId(&proto, pDexFile, pMethodId);
+ result = dexProtoCompare(&proto, &method->prototype);
+ }
+ }
+
+ return result;
+}
+
+/*
+ * Given a method, determine the method's index.
+ *
+ * We could simply store this in the Method*, but that would cost 4 bytes
+ * per method. Instead we plow through the DEX data.
+ *
+ * We have two choices: look through the class method data, or look through
+ * the global method_ids table. The former is awkward because the method
+ * could have been defined in a superclass or interface. The latter works
+ * out reasonably well because it's in sorted order, though we're still left
+ * doing a fair number of string comparisons.
+ */
+static u4 getMethodIdx(const Method* method)
+{
+ DexFile* pDexFile = method->clazz->pDvmDex->pDexFile;
+ u4 hi = pDexFile->pHeader->methodIdsSize -1;
+ u4 lo = 0;
+ u4 cur;
+
+ while (hi >= lo) {
+ int cmp;
+ cur = (lo + hi) / 2;
+
+ cmp = compareMethodStr(pDexFile, cur, method);
+ if (cmp < 0) {
+ lo = cur + 1;
+ } else if (cmp > 0) {
+ hi = cur - 1;
+ } else {
+ break;
+ }
+ }
+
+ if (hi < lo) {
+ /* this should be impossible -- the method came out of this DEX */
+ char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+ LOGE("Unable to find method %s.%s %s in DEX file!\n",
+ method->clazz->descriptor, method->name, desc);
+ free(desc);
+ dvmAbort();
+ }
+
+ return cur;
+}
+
+/*
+ * Find the DexAnnotationSetItem for this method.
+ *
+ * Returns NULL if none found.
+ */
+static const DexAnnotationSetItem* findAnnotationSetForMethod(
+ const Method* method)
+{
+ ClassObject* clazz = method->clazz;
+ DexFile* pDexFile;
+ const DexAnnotationsDirectoryItem* pAnnoDir;
+ const DexMethodAnnotationsItem* pMethodList;
+ const DexAnnotationSetItem* pAnnoSet = NULL;
+
+ if (clazz->pDvmDex == NULL) /* generated class (Proxy, array) */
+ return NULL;
+ pDexFile = clazz->pDvmDex->pDexFile;
+
+ pAnnoDir = getAnnoDirectory(pDexFile, clazz);
+ if (pAnnoDir != NULL) {
+ pMethodList = dexGetMethodAnnotations(pDexFile, pAnnoDir);
+ if (pMethodList != NULL) {
+ /*
+ * Run through the list and find a matching method. We compare the
+ * method ref indices in the annotation list with the method's DEX
+ * method_idx value.
+ *
+ * TODO: use a binary search for long lists
+ *
+ * Alternate approach: for each entry in the annotations list,
+ * find the method definition in the DEX file and perform string
+ * comparisons on class name, method name, and signature.
+ */
+ u4 methodIdx = getMethodIdx(method);
+ u4 count = dexGetMethodAnnotationsSize(pDexFile, pAnnoDir);
+ u4 idx;
+
+ for (idx = 0; idx < count; idx++) {
+ if (pMethodList[idx].methodIdx == methodIdx) {
+ /* found! */
+ pAnnoSet = dexGetMethodAnnotationSetItem(pDexFile,
+ &pMethodList[idx]);
+ break;
+ }
+ }
+ }
+ }
+
+ return pAnnoSet;
+}
+
+/*
+ * Return an array of Annotation objects for the method. Returns an empty
+ * array if there are no annotations.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ *
+ * On allocation failure, this returns NULL with an exception raised.
+ */
+ArrayObject* dvmGetMethodAnnotations(const Method* method)
+{
+ ClassObject* clazz = method->clazz;
+ const DexAnnotationSetItem* pAnnoSet;
+ ArrayObject* annoArray = NULL;
+
+ pAnnoSet = findAnnotationSetForMethod(method);
+ if (pAnnoSet == NULL) {
+ /* no matching annotations found */
+ annoArray = emptyAnnoArray();
+ } else {
+ annoArray = processAnnotationSet(clazz, pAnnoSet,kDexVisibilityRuntime);
+ }
+
+ return annoArray;
+}
+
+/*
+ * Retrieve the Signature annotation, if any. Returns NULL if no signature
+ * exists.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+ArrayObject* dvmGetMethodSignatureAnnotation(const Method* method)
+{
+ ClassObject* clazz = method->clazz;
+ const DexAnnotationSetItem* pAnnoSet;
+ ArrayObject* signature = NULL;
+
+ pAnnoSet = findAnnotationSetForMethod(method);
+ if (pAnnoSet != NULL)
+ signature = getSignatureValue(clazz, pAnnoSet);
+
+ return signature;
+}
+
+/*
+ * Extract an array of exception classes from the "system" annotation list
+ * for this method.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ *
+ * Returns NULL if we don't find any exceptions for this method.
+ */
+ArrayObject* dvmGetMethodThrows(const Method* method)
+{
+ ClassObject* clazz = method->clazz;
+ const DexAnnotationSetItem* pAnnoSet;
+ const DexAnnotationItem* pAnnoItem;
+
+ /* find the set for this method */
+ pAnnoSet = findAnnotationSetForMethod(method);
+ if (pAnnoSet == NULL)
+ return NULL; /* nothing for this method */
+
+ /* find the "Throws" annotation, if any */
+ pAnnoItem = searchAnnotationSet(clazz, pAnnoSet, kDescrThrows,
+ kDexVisibilitySystem);
+ if (pAnnoItem == NULL)
+ return NULL; /* no Throws */
+
+ /*
+ * The Throws annotation has one member, "Class[] value".
+ */
+ Object* obj = getAnnotationValue(clazz, pAnnoItem, "value",
+ kDexAnnotationArray, "Throws");
+ if (obj == GAV_FAILED)
+ return NULL;
+ assert(dvmIsArray((ArrayObject*)obj));
+ obj = convertReturnType(obj, gDvm.classJavaLangClassArray);
+ return (ArrayObject*)obj;
+}
+
+/*
+ * Given an Annotation's method, find the default value, if any.
+ *
+ * If this is a CLASS annotation, and we can't find a match for the
+ * default class value, we need to throw a TypeNotPresentException.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+Object* dvmGetAnnotationDefaultValue(const Method* method)
+{
+ const ClassObject* clazz = method->clazz;
+ DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+ const DexAnnotationsDirectoryItem* pAnnoDir;
+ const DexAnnotationSetItem* pAnnoSet = NULL;
+
+ /*
+ * The method's declaring class (the annotation) will have an
+ * AnnotationDefault "system" annotation associated with it if any
+ * of its methods have default values. Start by finding the
+ * DexAnnotationItem associated with the class.
+ */
+ pAnnoDir = getAnnoDirectory(pDexFile, clazz);
+ if (pAnnoDir != NULL)
+ pAnnoSet = dexGetClassAnnotationSet(pDexFile, pAnnoDir);
+ if (pAnnoSet == NULL) {
+ /* no annotations for anything in class, or no class annotations */
+ return NULL;
+ }
+
+ /* find the "AnnotationDefault" annotation, if any */
+ const DexAnnotationItem* pAnnoItem;
+ pAnnoItem = searchAnnotationSet(clazz, pAnnoSet, kDescrAnnotationDefault,
+ kDexVisibilitySystem);
+ if (pAnnoItem == NULL) {
+ /* no default values for any member in this annotation */
+ //printf("##### no default annotations for %s.%s\n",
+ // method->clazz->descriptor, method->name);
+ return NULL;
+ }
+
+ /*
+ * The AnnotationDefault annotation has one member, "Annotation value".
+ * We need to pull that out.
+ */
+ const u1* ptr;
+ ptr = searchEncodedAnnotation(clazz, pAnnoItem->annotation, "value");
+ if (ptr == NULL) {
+ LOGW("AnnotationDefault annotation lacks 'value'\n");
+ return NULL;
+ }
+ if ((*ptr & kDexAnnotationValueTypeMask) != kDexAnnotationAnnotation) {
+ LOGW("AnnotationDefault value has wrong type (0x%02x)\n",
+ *ptr & kDexAnnotationValueTypeMask);
+ return NULL;
+ }
+
+ /*
+ * The value_type byte for VALUE_ANNOTATION is followed by
+ * encoded_annotation data. We want to scan through it to find an
+ * entry whose name matches our method name.
+ */
+ ptr++;
+ ptr = searchEncodedAnnotation(clazz, ptr, method->name);
+ if (ptr == NULL)
+ return NULL; /* no default annotation for this method */
+
+ /* got it, pull it out */
+ AnnotationValue avalue;
+ if (!processAnnotationValue(clazz, &ptr, &avalue, kAllObjects)) {
+ LOGD("processAnnotationValue failed on default for '%s'\n",
+ method->name);
+ return NULL;
+ }
+
+ /* convert the return type, if necessary */
+ ClassObject* methodReturn = dvmGetBoxedReturnType(method);
+ Object* obj = avalue.value.l;
+ obj = convertReturnType(obj, methodReturn);
+
+ return obj;
+}
+
+
+/*
+ * ===========================================================================
+ * Field
+ * ===========================================================================
+ */
+
+/*
+ * Compare the attributes (class name, field name, field signature) of
+ * the specified field to "field".
+ */
+static int compareFieldStr(DexFile* pDexFile, u4 idx, const Field* field)
+{
+ const DexFieldId* pFieldId = dexGetFieldId(pDexFile, idx);
+ const char* str = dexStringByTypeIdx(pDexFile, pFieldId->classIdx);
+ int result = strcmp(str, field->clazz->descriptor);
+
+ if (result == 0) {
+ str = dexStringById(pDexFile, pFieldId->nameIdx);
+ result = strcmp(str, field->name);
+ if (result == 0) {
+ str = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
+ result = strcmp(str, field->signature);
+ }
+ }
+
+ return result;
+}
+
+/*
+ * Given a field, determine the field's index.
+ *
+ * This has the same tradeoffs as getMethodIdx.
+ */
+static u4 getFieldIdx(const Field* field)
+{
+ DexFile* pDexFile = field->clazz->pDvmDex->pDexFile;
+ u4 hi = pDexFile->pHeader->fieldIdsSize -1;
+ u4 lo = 0;
+ u4 cur;
+
+ while (hi >= lo) {
+ int cmp;
+ cur = (lo + hi) / 2;
+
+ cmp = compareFieldStr(pDexFile, cur, field);
+ if (cmp < 0) {
+ lo = cur + 1;
+ } else if (cmp > 0) {
+ hi = cur - 1;
+ } else {
+ break;
+ }
+ }
+
+ if (hi < lo) {
+ /* this should be impossible -- the field came out of this DEX */
+ LOGE("Unable to find field %s.%s %s in DEX file!\n",
+ field->clazz->descriptor, field->name, field->signature);
+ dvmAbort();
+ }
+
+ return cur;
+}
+
+/*
+ * Find the DexAnnotationSetItem for this field.
+ *
+ * Returns NULL if none found.
+ */
+static const DexAnnotationSetItem* findAnnotationSetForField(const Field* field)
+{
+ ClassObject* clazz = field->clazz;
+ DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+ const DexAnnotationsDirectoryItem* pAnnoDir;
+ const DexFieldAnnotationsItem* pFieldList;
+
+ pAnnoDir = getAnnoDirectory(pDexFile, clazz);
+ if (pAnnoDir == NULL)
+ return NULL;
+
+ pFieldList = dexGetFieldAnnotations(pDexFile, pAnnoDir);
+ if (pFieldList == NULL)
+ return NULL;
+
+ /*
+ * Run through the list and find a matching field. We compare the
+ * field ref indices in the annotation list with the field's DEX
+ * field_idx value.
+ *
+ * TODO: use a binary search for long lists
+ *
+ * Alternate approach: for each entry in the annotations list,
+ * find the field definition in the DEX file and perform string
+ * comparisons on class name, field name, and signature.
+ */
+ u4 fieldIdx = getFieldIdx(field);
+ u4 count = dexGetFieldAnnotationsSize(pDexFile, pAnnoDir);
+ u4 idx;
+
+ for (idx = 0; idx < count; idx++) {
+ if (pFieldList[idx].fieldIdx == fieldIdx) {
+ /* found! */
+ return dexGetFieldAnnotationSetItem(pDexFile, &pFieldList[idx]);
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Return an array of Annotation objects for the field. Returns an empty
+ * array if there are no annotations.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ *
+ * On allocation failure, this returns NULL with an exception raised.
+ */
+ArrayObject* dvmGetFieldAnnotations(const Field* field)
+{
+ ClassObject* clazz = field->clazz;
+ ArrayObject* annoArray = NULL;
+ const DexAnnotationSetItem* pAnnoSet = NULL;
+
+ pAnnoSet = findAnnotationSetForField(field);
+ if (pAnnoSet == NULL) {
+ /* no matching annotations found */
+ annoArray = emptyAnnoArray();
+ } else {
+ annoArray = processAnnotationSet(clazz, pAnnoSet,
+ kDexVisibilityRuntime);
+ }
+
+ return annoArray;
+}
+
+/*
+ * Retrieve the Signature annotation, if any. Returns NULL if no signature
+ * exists.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+ArrayObject* dvmGetFieldSignatureAnnotation(const Field* field)
+{
+ ClassObject* clazz = field->clazz;
+ const DexAnnotationSetItem* pAnnoSet;
+ ArrayObject* signature = NULL;
+
+ pAnnoSet = findAnnotationSetForField(field);
+ if (pAnnoSet != NULL)
+ signature = getSignatureValue(clazz, pAnnoSet);
+
+ return signature;
+}
+
+
+/*
+ * ===========================================================================
+ * Parameter
+ * ===========================================================================
+ */
+
+/*
+ * We have an annotation_set_ref_list, which is essentially a list of
+ * entries that we pass to processAnnotationSet().
+ *
+ * The returned object must be released with dvmReleaseTrackedAlloc.
+ */
+static ArrayObject* processAnnotationSetRefList(const ClassObject* clazz,
+ const DexAnnotationSetRefList* pAnnoSetList, u4 count)
+{
+ DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+ ArrayObject* annoArrayArray = NULL;
+ u4 idx;
+
+ /* allocate an array of Annotation arrays to hold results */
+ annoArrayArray = dvmAllocArrayByClass(
+ gDvm.classJavaLangAnnotationAnnotationArrayArray, count, ALLOC_DEFAULT);
+ if (annoArrayArray == NULL) {
+ LOGW("annotation set ref array alloc failed\n");
+ goto bail;
+ }
+
+ for (idx = 0; idx < count; idx++) {
+ Thread* self = dvmThreadSelf();
+ const DexAnnotationSetRefItem* pItem;
+ const DexAnnotationSetItem* pAnnoSet;
+ Object *annoSet;
+
+ pItem = dexGetParameterAnnotationSetRef(pAnnoSetList, idx);
+ pAnnoSet = dexGetSetRefItemItem(pDexFile, pItem);
+ annoSet = (Object *)processAnnotationSet(clazz,
+ pAnnoSet,
+ kDexVisibilityRuntime);
+ if (annoSet == NULL) {
+ LOGW("processAnnotationSet failed\n");
+ annoArrayArray = NULL;
+ goto bail;
+ }
+ dvmSetObjectArrayElement(annoArrayArray, idx, annoSet);
+ dvmReleaseTrackedAlloc((Object*) annoSet, self);
+ }
+
+bail:
+ return annoArrayArray;
+}
+
+/*
+ * Find the DexAnnotationSetItem for this parameter.
+ *
+ * Returns NULL if none found.
+ */
+static const DexParameterAnnotationsItem* findAnnotationsItemForMethod(
+ const Method* method)
+{
+ ClassObject* clazz = method->clazz;
+ DexFile* pDexFile;
+ const DexAnnotationsDirectoryItem* pAnnoDir;
+ const DexParameterAnnotationsItem* pParameterList;
+
+ if (clazz->pDvmDex == NULL) /* generated class (Proxy, array) */
+ return NULL;
+
+ pDexFile = clazz->pDvmDex->pDexFile;
+ pAnnoDir = getAnnoDirectory(pDexFile, clazz);
+ if (pAnnoDir == NULL)
+ return NULL;
+
+ pParameterList = dexGetParameterAnnotations(pDexFile, pAnnoDir);
+ if (pParameterList == NULL)
+ return NULL;
+
+ /*
+ * Run through the list and find a matching method. We compare the
+ * method ref indices in the annotation list with the method's DEX
+ * method_idx value.
+ *
+ * TODO: use a binary search for long lists
+ *
+ * Alternate approach: for each entry in the annotations list,
+ * find the method definition in the DEX file and perform string
+ * comparisons on class name, method name, and signature.
+ */
+ u4 methodIdx = getMethodIdx(method);
+ u4 count = dexGetParameterAnnotationsSize(pDexFile, pAnnoDir);
+ u4 idx;
+
+ for (idx = 0; idx < count; idx++) {
+ if (pParameterList[idx].methodIdx == methodIdx) {
+ /* found! */
+ return &pParameterList[idx];
+ }
+ }
+
+ return NULL;
+}
+
+
+/*
+ * Count up the number of arguments the method takes. The "this" pointer
+ * doesn't count.
+ */
+static int countMethodArguments(const Method* method)
+{
+ /* method->shorty[0] is the return type */
+ return strlen(method->shorty + 1);
+}
+
+/*
+ * Return an array of arrays of Annotation objects. The outer array has
+ * one entry per method parameter, the inner array has the list of annotations
+ * associated with that parameter.
+ *
+ * If the method has no parameters, we return an array of length zero. If
+ * the method has one or more parameters, we return an array whose length
+ * is equal to the number of parameters; if a given parameter does not have
+ * an annotation, the corresponding entry will be null.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+ArrayObject* dvmGetParameterAnnotations(const Method* method)
+{
+ ClassObject* clazz = method->clazz;
+ const DexParameterAnnotationsItem* pItem;
+ ArrayObject* annoArrayArray = NULL;
+
+ pItem = findAnnotationsItemForMethod(method);
+ if (pItem != NULL) {
+ DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+ const DexAnnotationSetRefList* pAnnoSetList;
+ u4 size;
+
+ size = dexGetParameterAnnotationSetRefSize(pDexFile, pItem);
+ pAnnoSetList = dexGetParameterAnnotationSetRefList(pDexFile, pItem);
+ annoArrayArray = processAnnotationSetRefList(clazz, pAnnoSetList, size);
+ } else {
+ /* no matching annotations found */
+ annoArrayArray = emptyAnnoArrayArray(countMethodArguments(method));
+ }
+
+ return annoArrayArray;
+}
+
+
+/*
+ * ===========================================================================
+ * DexEncodedArray interpretation
+ * ===========================================================================
+ */
+
+/**
+ * Initializes an encoded array iterator.
+ *
+ * @param iterator iterator to initialize
+ * @param encodedArray encoded array to iterate over
+ * @param clazz class to use when resolving strings and types
+ */
+void dvmEncodedArrayIteratorInitialize(EncodedArrayIterator* iterator,
+ const DexEncodedArray* encodedArray, const ClassObject* clazz) {
+ iterator->encodedArray = encodedArray;
+ iterator->cursor = encodedArray->array;
+ iterator->size = readUleb128(&iterator->cursor);
+ iterator->elementsLeft = iterator->size;
+ iterator->clazz = clazz;
+}
+
+/**
+ * Returns whether there are more elements to be read.
+ */
+bool dvmEncodedArrayIteratorHasNext(const EncodedArrayIterator* iterator) {
+ return (iterator->elementsLeft != 0);
+}
+
+/**
+ * Returns the next decoded value from the iterator, advancing its
+ * cursor. This returns primitive values in their corresponding union
+ * slots, and returns everything else (including nulls) as object
+ * references in the "l" union slot.
+ *
+ * The caller must call dvmReleaseTrackedAlloc() on any returned reference.
+ *
+ * @param value pointer to store decoded value into
+ * @returns true if a value was decoded and the cursor advanced; false if
+ * the last value had already been decoded or if there was a problem decoding
+ */
+bool dvmEncodedArrayIteratorGetNext(EncodedArrayIterator* iterator,
+ AnnotationValue* value) {
+ bool processed;
+
+ if (iterator->elementsLeft == 0) {
+ return false;
+ }
+
+ processed = processAnnotationValue(iterator->clazz, &iterator->cursor,
+ value, kPrimitivesOrObjects);
+
+ if (! processed) {
+ LOGE("Failed to process array element %d from %p",
+ iterator->size - iterator->elementsLeft,
+ iterator->encodedArray);
+ iterator->elementsLeft = 0;
+ return false;
+ }
+
+ iterator->elementsLeft--;
+ return true;
+}
diff --git a/vm/reflect/Proxy.c b/vm/reflect/Proxy.c
new file mode 100644
index 0000000..eef658d
--- /dev/null
+++ b/vm/reflect/Proxy.c
@@ -0,0 +1,1103 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Implementation of java.lang.reflect.Proxy.
+ *
+ * Traditionally this is implemented entirely in interpreted code,
+ * generating bytecode that defines the proxy class. Dalvik doesn't
+ * currently support this approach, so we generate the class directly. If
+ * we add support for DefineClass with standard classfiles we can
+ * eliminate this.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+
+// fwd
+static bool returnTypesAreCompatible(Method* baseMethod, Method* subMethod);
+static bool gatherMethods(ArrayObject* interfaces, Method*** pMethods,\
+ ArrayObject** pThrows, int* pMethodCount);
+static int copyWithoutDuplicates(Method** allMethods, int allCount,
+ Method** outMethods, ArrayObject* throws);
+static bool createExceptionClassList(const Method* method,
+ PointerSet** pThrows);
+static void updateExceptionClassList(const Method* method, PointerSet* throws);
+static void createConstructor(ClassObject* clazz, Method* meth);
+static void createHandlerMethod(ClassObject* clazz, Method* dstMeth,
+ const Method* srcMeth);
+static void proxyConstructor(const u4* args, JValue* pResult,
+ const Method* method, Thread* self);
+static void proxyInvoker(const u4* args, JValue* pResult,
+ const Method* method, Thread* self);
+static bool mustWrapException(const Method* method, const Object* throwable);
+
+/* private static fields in the Proxy class */
+#define kThrowsField 0
+#define kProxySFieldCount 1
+
+
+/*
+ * Perform Proxy setup.
+ */
+bool dvmReflectProxyStartup()
+{
+ /*
+ * Standard methods we must provide in our proxy.
+ */
+ Method* methE;
+ Method* methH;
+ Method* methT;
+ Method* methF;
+ methE = dvmFindVirtualMethodByDescriptor(gDvm.classJavaLangObject,
+ "equals", "(Ljava/lang/Object;)Z");
+ methH = dvmFindVirtualMethodByDescriptor(gDvm.classJavaLangObject,
+ "hashCode", "()I");
+ methT = dvmFindVirtualMethodByDescriptor(gDvm.classJavaLangObject,
+ "toString", "()Ljava/lang/String;");
+ methF = dvmFindVirtualMethodByDescriptor(gDvm.classJavaLangObject,
+ "finalize", "()V");
+ if (methE == NULL || methH == NULL || methT == NULL || methF == NULL) {
+ LOGE("Could not find equals/hashCode/toString/finalize in Object\n");
+ return false;
+ }
+ gDvm.voffJavaLangObject_equals = methE->methodIndex;
+ gDvm.voffJavaLangObject_hashCode = methH->methodIndex;
+ gDvm.voffJavaLangObject_toString = methT->methodIndex;
+ gDvm.voffJavaLangObject_finalize = methF->methodIndex;
+
+ /*
+ * The prototype signature needs to be cloned from a method in a
+ * "real" DEX file. We declared this otherwise unused method just
+ * for this purpose.
+ */
+ ClassObject* proxyClass;
+ Method* meth;
+ proxyClass = dvmFindSystemClassNoInit("Ljava/lang/reflect/Proxy;");
+ if (proxyClass == NULL) {
+ LOGE("No java.lang.reflect.Proxy\n");
+ return false;
+ }
+ meth = dvmFindDirectMethodByDescriptor(proxyClass, "constructorPrototype",
+ "(Ljava/lang/reflect/InvocationHandler;)V");
+ if (meth == NULL) {
+ LOGE("Could not find java.lang.Proxy.constructorPrototype()\n");
+ return false;
+ }
+ gDvm.methJavaLangReflectProxy_constructorPrototype = meth;
+
+ /*
+ * Get the offset of the "h" field in Proxy.
+ */
+ gDvm.offJavaLangReflectProxy_h = dvmFindFieldOffset(proxyClass, "h",
+ "Ljava/lang/reflect/InvocationHandler;");
+ if (gDvm.offJavaLangReflectProxy_h < 0) {
+ LOGE("Unable to find 'h' field in java.lang.Proxy\n");
+ return false;
+ }
+
+ return true;
+}
+
+
+/*
+ * Generate a proxy class with the specified name, interfaces, and loader.
+ * "interfaces" is an array of class objects.
+ *
+ * The Proxy.getProxyClass() code has done the following:
+ * - Verified that "interfaces" contains only interfaces
+ * - Verified that no interface appears twice
+ * - Prepended the package name to the class name if one or more
+ * interfaces are non-public
+ * - Searched for an existing instance of an appropriate Proxy class
+ *
+ * On failure we leave a partially-created class object sitting around,
+ * but the garbage collector will take care of it.
+ */
+ClassObject* dvmGenerateProxyClass(StringObject* str, ArrayObject* interfaces,
+ Object* loader)
+{
+ int result = -1;
+ char* nameStr = NULL;
+ Method** methods = NULL;
+ ArrayObject* throws = NULL;
+ ClassObject* newClass = NULL;
+ int i;
+
+ nameStr = dvmCreateCstrFromString(str);
+ if (nameStr == NULL) {
+ dvmThrowException("Ljava/lang/IllegalArgumentException;",
+ "missing name");
+ goto bail;
+ }
+
+ LOGV("+++ Generate proxy class '%s' %p from %d interface classes\n",
+ nameStr, loader, interfaces->length);
+
+
+ /*
+ * Characteristics of a Proxy class:
+ * - concrete class, public and final
+ * - superclass is java.lang.reflect.Proxy
+ * - implements all listed interfaces (req'd for instanceof)
+ * - has one method for each method in the interfaces (for duplicates,
+ * the method in the earliest interface wins)
+ * - has one constructor (takes an InvocationHandler arg)
+ * - has overrides for hashCode, equals, and toString (these come first)
+ * - has one field, a reference to the InvocationHandler object, inherited
+ * from Proxy
+ *
+ * TODO: set protection domain so it matches bootstrap classes.
+ *
+ * The idea here is to create a class object and fill in the details
+ * as we would in loadClassFromDex(), and then call dvmLinkClass() to do
+ * all the heavy lifting (notably populating the virtual and interface
+ * method tables).
+ */
+
+ /*
+ * Generate a temporary list of virtual methods.
+ */
+ int methodCount = -1;
+ if (!gatherMethods(interfaces, &methods, &throws, &methodCount))
+ goto bail;
+
+ /*
+ * Allocate storage for the class object and set some basic fields.
+ */
+ newClass = (ClassObject*) dvmMalloc(sizeof(*newClass) +
+ kProxySFieldCount * sizeof(StaticField),
+ ALLOC_DEFAULT);
+ if (newClass == NULL)
+ goto bail;
+ DVM_OBJECT_INIT(&newClass->obj, gDvm.classJavaLangClass);
+ dvmSetClassSerialNumber(newClass);
+ newClass->descriptorAlloc = dvmNameToDescriptor(nameStr);
+ newClass->descriptor = newClass->descriptorAlloc;
+ newClass->accessFlags = ACC_PUBLIC | ACC_FINAL;
+ dvmSetFieldObject((Object *)newClass,
+ offsetof(ClassObject, super),
+ (Object *)gDvm.classJavaLangReflectProxy);
+ newClass->primitiveType = PRIM_NOT;
+ dvmSetFieldObject((Object *)newClass,
+ offsetof(ClassObject, classLoader),
+ (Object *)loader);
+#if WITH_HPROF && WITH_HPROF_STACK
+ hprofFillInStackTrace(newClass);
+#endif
+
+ /*
+ * Add direct method definitions. We have one (the constructor).
+ */
+ newClass->directMethodCount = 1;
+ newClass->directMethods = (Method*) dvmLinearAlloc(newClass->classLoader,
+ 1 * sizeof(Method));
+ createConstructor(newClass, &newClass->directMethods[0]);
+ dvmLinearReadOnly(newClass->classLoader, newClass->directMethods);
+
+ /*
+ * Add virtual method definitions.
+ */
+ newClass->virtualMethodCount = methodCount;
+ newClass->virtualMethods = (Method*) dvmLinearAlloc(newClass->classLoader,
+ newClass->virtualMethodCount * sizeof(Method));
+ for (i = 0; i < newClass->virtualMethodCount; i++) {
+ createHandlerMethod(newClass, &newClass->virtualMethods[i],methods[i]);
+ }
+ dvmLinearReadOnly(newClass->classLoader, newClass->virtualMethods);
+
+ /*
+ * Add interface list.
+ */
+ int interfaceCount = interfaces->length;
+ ClassObject** ifArray = (ClassObject**) interfaces->contents;
+ newClass->interfaceCount = interfaceCount;
+ newClass->interfaces = (ClassObject**)dvmLinearAlloc(newClass->classLoader,
+ sizeof(ClassObject*) * interfaceCount);
+ for (i = 0; i < interfaceCount; i++)
+ newClass->interfaces[i] = ifArray[i];
+ dvmLinearReadOnly(newClass->classLoader, newClass->interfaces);
+
+ /*
+ * Static field list. We have one private field, for our list of
+ * exceptions declared for each method.
+ */
+ assert(kProxySFieldCount == 1);
+ newClass->sfieldCount = kProxySFieldCount;
+ StaticField* sfield = &newClass->sfields[kThrowsField];
+ sfield->field.clazz = newClass;
+ sfield->field.name = "throws";
+ sfield->field.signature = "[[Ljava/lang/Throwable;";
+ sfield->field.accessFlags = ACC_STATIC | ACC_PRIVATE;
+ dvmSetStaticFieldObject(sfield, (Object*)throws);
+
+ /*
+ * Everything is ready. This class didn't come out of a DEX file
+ * so we didn't tuck any indexes into the class object. We can
+ * advance to LOADED state immediately.
+ */
+ newClass->status = CLASS_LOADED;
+ if (!dvmLinkClass(newClass)) {
+ LOGD("Proxy class link failed\n");
+ goto bail;
+ }
+
+ /*
+ * All good. Add it to the hash table. We should NOT see a collision
+ * here; if we do, it means the caller has screwed up and provided us
+ * with a duplicate name.
+ */
+ if (!dvmAddClassToHash(newClass)) {
+ LOGE("ERROR: attempted to generate %s more than once\n",
+ newClass->descriptor);
+ goto bail;
+ }
+
+ result = 0;
+
+bail:
+ free(nameStr);
+ free(methods);
+ if (result != 0) {
+ /* must free innards explicitly if we didn't finish linking */
+ dvmFreeClassInnards(newClass);
+ newClass = NULL;
+ if (!dvmCheckException(dvmThreadSelf())) {
+ /* throw something */
+ dvmThrowException("Ljava/lang/RuntimeException;", NULL);
+ }
+ }
+
+ /* allow the GC to free these when nothing else has a reference */
+ dvmReleaseTrackedAlloc((Object*) throws, NULL);
+ dvmReleaseTrackedAlloc((Object*) newClass, NULL);
+
+ return newClass;
+}
+
+
+/*
+ * Generate a list of methods. The Method pointers returned point to the
+ * abstract method definition from the appropriate interface, or to the
+ * virtual method definition in java.lang.Object.
+ *
+ * We also allocate an array of arrays of throwable classes, one for each
+ * method,so we can do some special handling of checked exceptions. The
+ * caller must call ReleaseTrackedAlloc() on *pThrows.
+ */
+static bool gatherMethods(ArrayObject* interfaces, Method*** pMethods,
+ ArrayObject** pThrows, int* pMethodCount)
+{
+ ClassObject** classes;
+ ArrayObject* throws = NULL;
+ Method** methods = NULL;
+ Method** allMethods = NULL;
+ int numInterfaces, maxCount, actualCount, allCount;
+ bool result = false;
+ int i;
+
+ /*
+ * Get a maximum count so we can allocate storage. We need the
+ * methods declared by each interface and all of its superinterfaces.
+ */
+ maxCount = 3; // 3 methods in java.lang.Object
+ numInterfaces = interfaces->length;
+ classes = (ClassObject**) interfaces->contents;
+
+ for (i = 0; i < numInterfaces; i++, classes++) {
+ ClassObject* clazz = *classes;
+
+ LOGVV("--- %s virtualMethodCount=%d\n",
+ clazz->descriptor, clazz->virtualMethodCount);
+ maxCount += clazz->virtualMethodCount;
+
+ int j;
+ for (j = 0; j < clazz->iftableCount; j++) {
+ ClassObject* iclass = clazz->iftable[j].clazz;
+
+ LOGVV("--- +%s %d\n",
+ iclass->descriptor, iclass->virtualMethodCount);
+ maxCount += iclass->virtualMethodCount;
+ }
+ }
+
+ methods = (Method**) malloc(maxCount * sizeof(*methods));
+ allMethods = (Method**) malloc(maxCount * sizeof(*methods));
+ if (methods == NULL || allMethods == NULL)
+ goto bail;
+
+ /*
+ * First three entries are the java.lang.Object methods.
+ */
+ ClassObject* obj = gDvm.classJavaLangObject;
+ allMethods[0] = obj->vtable[gDvm.voffJavaLangObject_equals];
+ allMethods[1] = obj->vtable[gDvm.voffJavaLangObject_hashCode];
+ allMethods[2] = obj->vtable[gDvm.voffJavaLangObject_toString];
+ allCount = 3;
+
+ /*
+ * Add the methods from each interface, in order.
+ */
+ classes = (ClassObject**) interfaces->contents;
+ for (i = 0; i < numInterfaces; i++, classes++) {
+ ClassObject* clazz = *classes;
+ int j;
+
+ for (j = 0; j < clazz->virtualMethodCount; j++) {
+ allMethods[allCount++] = &clazz->virtualMethods[j];
+ }
+
+ for (j = 0; j < clazz->iftableCount; j++) {
+ ClassObject* iclass = clazz->iftable[j].clazz;
+ int k;
+
+ for (k = 0; k < iclass->virtualMethodCount; k++) {
+ allMethods[allCount++] = &iclass->virtualMethods[k];
+ }
+ }
+ }
+ assert(allCount == maxCount);
+
+ /*
+ * Allocate some storage to hold the lists of throwables. We need
+ * one entry per unique method, but it's convenient to allocate it
+ * ahead of the duplicate processing.
+ */
+ ClassObject* arrArrClass;
+ arrArrClass = dvmFindArrayClass("[[Ljava/lang/Throwable;", NULL);
+ if (arrArrClass == NULL)
+ goto bail;
+ throws = dvmAllocArrayByClass(arrArrClass, allCount, ALLOC_DEFAULT);
+
+ /*
+ * Identify and remove duplicates.
+ */
+ actualCount = copyWithoutDuplicates(allMethods, allCount, methods, throws);
+ if (actualCount < 0)
+ goto bail;
+
+ //LOGI("gathered methods:\n");
+ //for (i = 0; i < actualCount; i++) {
+ // LOGI(" %d: %s.%s\n",
+ // i, methods[i]->clazz->descriptor, methods[i]->name);
+ //}
+
+ *pMethods = methods;
+ *pMethodCount = actualCount;
+ *pThrows = throws;
+ result = true;
+
+bail:
+ free(allMethods);
+ if (!result) {
+ free(methods);
+ dvmReleaseTrackedAlloc((Object*)throws, NULL);
+ }
+ return result;
+}
+
+/*
+ * Identify and remove duplicates, where "duplicate" means it has the
+ * same name and arguments, but not necessarily the same return type.
+ *
+ * If duplicate methods have different return types, we want to use the
+ * first method whose return type is assignable from all other duplicate
+ * methods. That is, if we have:
+ * class base {...}
+ * class sub extends base {...}
+ * class subsub extends sub {...}
+ * Then we want to return the method that returns subsub, since callers
+ * to any form of the method will get a usable object back.
+ *
+ * All other duplicate methods are stripped out.
+ *
+ * This also populates the "throwLists" array with arrays of Class objects,
+ * one entry per method in "outMethods". Methods that don't declare any
+ * throwables (or have no common throwables with duplicate methods) will
+ * have NULL entries.
+ *
+ * Returns the number of methods copied into "methods", or -1 on failure.
+ */
+static int copyWithoutDuplicates(Method** allMethods, int allCount,
+ Method** outMethods, ArrayObject* throwLists)
+{
+ int outCount = 0;
+ int i, j;
+
+ /*
+ * The plan is to run through all methods, checking all other methods
+ * for a duplicate. If we find a match, we see if the other methods'
+ * return type is compatible/assignable with ours. If the current
+ * method is assignable from all others, we copy it to the new list,
+ * and NULL out all other entries. If not, we keep looking for a
+ * better version.
+ *
+ * If there are no duplicates, we copy the method and NULL the entry.
+ *
+ * At the end of processing, if we have any non-NULL entries, then we
+ * have bad duplicates and must exit with an exception.
+ */
+ for (i = 0; i < allCount; i++) {
+ bool best, dupe;
+
+ if (allMethods[i] == NULL)
+ continue;
+
+ /*
+ * Find all duplicates. If any of the return types is not
+ * assignable to our return type, then we're not the best.
+ *
+ * We start from 0, not i, because we need to compare assignability
+ * the other direction even if we've compared these before.
+ */
+ dupe = false;
+ best = true;
+ for (j = 0; j < allCount; j++) {
+ if (i == j)
+ continue;
+ if (allMethods[j] == NULL)
+ continue;
+
+ if (dvmCompareMethodNamesAndParameterProtos(allMethods[i],
+ allMethods[j]) == 0)
+ {
+ /*
+ * Duplicate method, check return type. If it's a primitive
+ * type or void, the types must match exactly, or we throw
+ * an exception now.
+ */
+ LOGV("MATCH on %s.%s and %s.%s\n",
+ allMethods[i]->clazz->descriptor, allMethods[i]->name,
+ allMethods[j]->clazz->descriptor, allMethods[j]->name);
+ dupe = true;
+ if (!returnTypesAreCompatible(allMethods[i], allMethods[j]))
+ best = false;
+ }
+ }
+
+ /*
+ * If this is the best of a set of duplicates, copy it over and
+ * nuke all duplicates.
+ *
+ * While we do this, we create the set of exceptions declared to
+ * be thrown by all occurrences of the method.
+ */
+ if (dupe) {
+ if (best) {
+ LOGV("BEST %d %s.%s -> %d\n", i,
+ allMethods[i]->clazz->descriptor, allMethods[i]->name,
+ outCount);
+
+ /* if we have exceptions, make a local copy */
+ PointerSet* commonThrows = NULL;
+ if (!createExceptionClassList(allMethods[i], &commonThrows))
+ return -1;
+
+ /*
+ * Run through one more time, erasing the duplicates. (This
+ * would go faster if we had marked them somehow.)
+ */
+ for (j = 0; j < allCount; j++) {
+ if (i == j)
+ continue;
+ if (allMethods[j] == NULL)
+ continue;
+ if (dvmCompareMethodNamesAndParameterProtos(allMethods[i],
+ allMethods[j]) == 0)
+ {
+ LOGV("DEL %d %s.%s\n", j,
+ allMethods[j]->clazz->descriptor,
+ allMethods[j]->name);
+
+ /*
+ * Update set to hold the intersection of method[i]'s
+ * and method[j]'s throws.
+ */
+ if (commonThrows != NULL) {
+ updateExceptionClassList(allMethods[j],
+ commonThrows);
+ }
+
+ allMethods[j] = NULL;
+ }
+ }
+
+ /*
+ * If the set of Throwable classes isn't empty, create an
+ * array of Class, copy them into it, and put the result
+ * into the "throwLists" array.
+ */
+ if (commonThrows != NULL &&
+ dvmPointerSetGetCount(commonThrows) > 0)
+ {
+ int commonCount = dvmPointerSetGetCount(commonThrows);
+ ArrayObject* throwArray;
+ Object** contents;
+ int ent;
+
+ throwArray = dvmAllocArrayByClass(
+ gDvm.classJavaLangClassArray, commonCount,
+ ALLOC_DEFAULT);
+ if (throwArray == NULL) {
+ LOGE("common-throw array alloc failed\n");
+ return -1;
+ }
+
+ contents = (Object**) throwArray->contents;
+ for (ent = 0; ent < commonCount; ent++) {
+ contents[ent] = (Object*)
+ dvmPointerSetGetEntry(commonThrows, ent);
+ }
+
+ /* add it to the array of arrays */
+ contents = (Object**) throwLists->contents;
+ contents[outCount] = (Object*) throwArray;
+ dvmReleaseTrackedAlloc((Object*) throwArray, NULL);
+ }
+
+ /* copy the winner and NULL it out */
+ outMethods[outCount++] = allMethods[i];
+ allMethods[i] = NULL;
+
+ dvmPointerSetFree(commonThrows);
+ } else {
+ LOGV("BEST not %d\n", i);
+ }
+ } else {
+ /*
+ * Singleton. Copy the entry and NULL it out.
+ */
+ LOGV("COPY singleton %d %s.%s -> %d\n", i,
+ allMethods[i]->clazz->descriptor, allMethods[i]->name,
+ outCount);
+
+ /* keep track of our throwables */
+ ArrayObject* exceptionArray = dvmGetMethodThrows(allMethods[i]);
+ if (exceptionArray != NULL) {
+ Object** contents;
+
+ contents = (Object**) throwLists->contents;
+ contents[outCount] = (Object*) exceptionArray;
+ dvmReleaseTrackedAlloc((Object*) exceptionArray, NULL);
+ }
+
+ outMethods[outCount++] = allMethods[i];
+ allMethods[i] = NULL;
+ }
+ }
+
+ /*
+ * Check for stragglers. If we find any, throw an exception.
+ */
+ for (i = 0; i < allCount; i++) {
+ if (allMethods[i] != NULL) {
+ LOGV("BAD DUPE: %d %s.%s\n", i,
+ allMethods[i]->clazz->descriptor, allMethods[i]->name);
+ dvmThrowException("Ljava/lang/IllegalArgumentException;",
+ "incompatible return types in proxied interfaces");
+ return -1;
+ }
+ }
+
+ return outCount;
+}
+
+
+/*
+ * Classes can declare to throw multiple exceptions in a hierarchy, e.g.
+ * IOException and FileNotFoundException. Since we're only interested in
+ * knowing the set that can be thrown without requiring an extra wrapper,
+ * we can remove anything that is a subclass of something else in the list.
+ *
+ * The "mix" step we do next reduces things toward the most-derived class,
+ * so it's important that we start with the least-derived classes.
+ */
+static void reduceExceptionClassList(ArrayObject* exceptionArray)
+{
+ const ClassObject** classes = (const ClassObject**)exceptionArray->contents;
+ int len = exceptionArray->length;
+ int i, j;
+
+ /*
+ * Consider all pairs of classes. If one is the subclass of the other,
+ * null out the subclass.
+ */
+ for (i = 0; i < len-1; i++) {
+ if (classes[i] == NULL)
+ continue;
+ for (j = i + 1; j < len; j++) {
+ if (classes[j] == NULL)
+ continue;
+
+ if (dvmInstanceof(classes[i], classes[j])) {
+ classes[i] = NULL;
+ break; /* no more comparisons against classes[i] */
+ } else if (dvmInstanceof(classes[j], classes[i])) {
+ classes[j] = NULL;
+ }
+ }
+ }
+}
+
+/*
+ * Create a local array with a copy of the throwable classes declared by
+ * "method". If no throws are declared, "*pSet" will be NULL.
+ *
+ * Returns "false" on allocation failure.
+ */
+static bool createExceptionClassList(const Method* method, PointerSet** pThrows)
+{
+ ArrayObject* exceptionArray = NULL;
+ bool result = false;
+
+ exceptionArray = dvmGetMethodThrows(method);
+ if (exceptionArray != NULL && exceptionArray->length > 0) {
+ /* reduce list, nulling out redundant entries */
+ reduceExceptionClassList(exceptionArray);
+
+ *pThrows = dvmPointerSetAlloc(exceptionArray->length);
+ if (*pThrows == NULL)
+ goto bail;
+
+ const ClassObject** contents;
+ int i;
+
+ contents = (const ClassObject**) exceptionArray->contents;
+ for (i = 0; i < (int) exceptionArray->length; i++) {
+ if (contents[i] != NULL)
+ dvmPointerSetAddEntry(*pThrows, contents[i]);
+ }
+ } else {
+ *pThrows = NULL;
+ }
+
+ result = true;
+
+bail:
+ dvmReleaseTrackedAlloc((Object*) exceptionArray, NULL);
+ return result;
+}
+
+/*
+ * We need to compute the intersection of the arguments, i.e. remove
+ * anything from "throws" that isn't in the method's list of throws.
+ *
+ * If one class is a subclass of another, we want to keep just the subclass,
+ * moving toward the most-restrictive set.
+ *
+ * We assume these are all classes, and don't try to filter out interfaces.
+ */
+static void updateExceptionClassList(const Method* method, PointerSet* throws)
+{
+ int setSize = dvmPointerSetGetCount(throws);
+ if (setSize == 0)
+ return;
+
+ ArrayObject* exceptionArray = dvmGetMethodThrows(method);
+ if (exceptionArray == NULL) {
+ /* nothing declared, so intersection is empty */
+ dvmPointerSetClear(throws);
+ return;
+ }
+
+ /* reduce list, nulling out redundant entries */
+ reduceExceptionClassList(exceptionArray);
+
+ int mixLen = dvmPointerSetGetCount(throws);
+ const ClassObject* mixSet[mixLen];
+
+ int declLen = exceptionArray->length;
+ const ClassObject** declSet = (const ClassObject**)exceptionArray->contents;
+
+ int i, j;
+
+ /* grab a local copy to work on */
+ for (i = 0; i < mixLen; i++) {
+ mixSet[i] = dvmPointerSetGetEntry(throws, i);
+ }
+
+ for (i = 0; i < mixLen; i++) {
+ for (j = 0; j < declLen; j++) {
+ if (declSet[j] == NULL)
+ continue;
+
+ if (mixSet[i] == declSet[j]) {
+ /* match, keep this one */
+ break;
+ } else if (dvmInstanceof(mixSet[i], declSet[j])) {
+ /* mix is a subclass of a declared throwable, keep it */
+ break;
+ } else if (dvmInstanceof(declSet[j], mixSet[i])) {
+ /* mix is a superclass, replace it */
+ mixSet[i] = declSet[j];
+ break;
+ }
+ }
+
+ if (j == declLen) {
+ /* no match, remove entry by nulling it out */
+ mixSet[i] = NULL;
+ }
+ }
+
+ /* copy results back out; this eliminates duplicates as we go */
+ dvmPointerSetClear(throws);
+ for (i = 0; i < mixLen; i++) {
+ if (mixSet[i] != NULL)
+ dvmPointerSetAddEntry(throws, mixSet[i]);
+ }
+
+ dvmReleaseTrackedAlloc((Object*) exceptionArray, NULL);
+}
+
+
+/*
+ * Check to see if the return types are compatible.
+ *
+ * If the return type is primitive or void, it must match exactly.
+ *
+ * If not, the type in "subMethod" must be assignable to the type in
+ * "baseMethod".
+ */
+static bool returnTypesAreCompatible(Method* subMethod, Method* baseMethod)
+{
+ const char* baseSig = dexProtoGetReturnType(&baseMethod->prototype);
+ const char* subSig = dexProtoGetReturnType(&subMethod->prototype);
+ ClassObject* baseClass;
+ ClassObject* subClass;
+
+ if (baseSig[1] == '\0' || subSig[1] == '\0') {
+ /* at least one is primitive type */
+ return (baseSig[0] == subSig[0] && baseSig[1] == subSig[1]);
+ }
+
+ baseClass = dvmFindClass(baseSig, baseMethod->clazz->classLoader);
+ subClass = dvmFindClass(subSig, subMethod->clazz->classLoader);
+ bool result = dvmInstanceof(subClass, baseClass);
+ return result;
+}
+
+/*
+ * Create a constructor for our Proxy class. The constructor takes one
+ * argument, a java.lang.reflect.InvocationHandler.
+ */
+static void createConstructor(ClassObject* clazz, Method* meth)
+{
+ meth->clazz = clazz;
+ meth->accessFlags = ACC_PUBLIC | ACC_NATIVE;
+ meth->name = "<init>";
+ meth->prototype =
+ gDvm.methJavaLangReflectProxy_constructorPrototype->prototype;
+ meth->shorty =
+ gDvm.methJavaLangReflectProxy_constructorPrototype->shorty;
+ // no pDexCode or pDexMethod
+
+ int argsSize = dvmComputeMethodArgsSize(meth) + 1;
+ meth->registersSize = meth->insSize = argsSize;
+
+ meth->nativeFunc = proxyConstructor;
+}
+
+/*
+ * Create a method in our Proxy class with the name and signature of
+ * the interface method it implements.
+ */
+static void createHandlerMethod(ClassObject* clazz, Method* dstMeth,
+ const Method* srcMeth)
+{
+ dstMeth->clazz = clazz;
+ dstMeth->insns = (u2*) srcMeth;
+ dstMeth->accessFlags = ACC_PUBLIC | ACC_NATIVE;
+ dstMeth->name = srcMeth->name;
+ dstMeth->prototype = srcMeth->prototype;
+ dstMeth->shorty = srcMeth->shorty;
+ // no pDexCode or pDexMethod
+
+ int argsSize = dvmComputeMethodArgsSize(dstMeth) + 1;
+ dstMeth->registersSize = dstMeth->insSize = argsSize;
+
+ dstMeth->nativeFunc = proxyInvoker;
+}
+
+/*
+ * Return a new Object[] array with the contents of "args". We determine
+ * the number and types of values in "args" based on the method signature.
+ * Primitive types are boxed.
+ *
+ * Returns NULL if the method takes no arguments.
+ *
+ * The caller must call dvmReleaseTrackedAlloc() on the return value.
+ *
+ * On failure, returns with an appropriate exception raised.
+ */
+static ArrayObject* boxMethodArgs(const Method* method, const u4* args)
+{
+ const char* desc = &method->shorty[1]; // [0] is the return type.
+ ArrayObject* argArray = NULL;
+ int argCount;
+ Object** argObjects;
+ bool failed = true;
+
+ /* count args */
+ argCount = dexProtoGetParameterCount(&method->prototype);
+
+ /* allocate storage */
+ argArray = dvmAllocArray(gDvm.classJavaLangObjectArray, argCount,
+ kObjectArrayRefWidth, ALLOC_DEFAULT);
+ if (argArray == NULL)
+ goto bail;
+ argObjects = (Object**) argArray->contents;
+
+ /*
+ * Fill in the array.
+ */
+
+ int srcIndex = 0;
+
+ argCount = 0;
+ while (*desc != '\0') {
+ char descChar = *(desc++);
+ JValue value;
+
+ switch (descChar) {
+ case 'Z':
+ case 'C':
+ case 'F':
+ case 'B':
+ case 'S':
+ case 'I':
+ value.i = args[srcIndex++];
+ argObjects[argCount] = (Object*) dvmWrapPrimitive(value,
+ dvmFindPrimitiveClass(descChar));
+ /* argObjects is tracked, don't need to hold this too */
+ dvmReleaseTrackedAlloc(argObjects[argCount], NULL);
+ argCount++;
+ break;
+ case 'D':
+ case 'J':
+ value.j = dvmGetArgLong(args, srcIndex);
+ srcIndex += 2;
+ argObjects[argCount] = (Object*) dvmWrapPrimitive(value,
+ dvmFindPrimitiveClass(descChar));
+ dvmReleaseTrackedAlloc(argObjects[argCount], NULL);
+ argCount++;
+ break;
+ case '[':
+ case 'L':
+ argObjects[argCount++] = (Object*) args[srcIndex++];
+ break;
+ }
+ }
+
+ failed = false;
+
+bail:
+ if (failed) {
+ dvmReleaseTrackedAlloc((Object*)argArray, NULL);
+ argArray = NULL;
+ }
+ return argArray;
+}
+
+/*
+ * This is the constructor for a generated proxy object. All we need to
+ * do is stuff "handler" into "h".
+ */
+static void proxyConstructor(const u4* args, JValue* pResult,
+ const Method* method, Thread* self)
+{
+ Object* obj = (Object*) args[0];
+ Object* handler = (Object*) args[1];
+
+ dvmSetFieldObject(obj, gDvm.offJavaLangReflectProxy_h, handler);
+}
+
+/*
+ * This is the common message body for proxy methods.
+ *
+ * The method we're calling looks like:
+ * public Object invoke(Object proxy, Method method, Object[] args)
+ *
+ * This means we have to create a Method object, box our arguments into
+ * a new Object[] array, make the call, and unbox the return value if
+ * necessary.
+ */
+static void proxyInvoker(const u4* args, JValue* pResult,
+ const Method* method, Thread* self)
+{
+ Object* thisObj = (Object*) args[0];
+ Object* methodObj = NULL;
+ ArrayObject* argArray = NULL;
+ Object* handler;
+ Method* invoke;
+ ClassObject* returnType;
+ JValue invokeResult;
+
+ /*
+ * Retrieve handler object for this proxy instance. The field is
+ * defined in the superclass (Proxy).
+ */
+ handler = dvmGetFieldObject(thisObj, gDvm.offJavaLangReflectProxy_h);
+
+ /*
+ * Find the invoke() method, looking in "this"s class. (Because we
+ * start here we don't have to convert it to a vtable index and then
+ * index into this' vtable.)
+ */
+ invoke = dvmFindVirtualMethodHierByDescriptor(handler->clazz, "invoke",
+ "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;");
+ if (invoke == NULL) {
+ LOGE("Unable to find invoke()\n");
+ dvmAbort();
+ }
+
+ LOGV("invoke: %s.%s, this=%p, handler=%s\n",
+ method->clazz->descriptor, method->name,
+ thisObj, handler->clazz->descriptor);
+
+ /*
+ * Create a java.lang.reflect.Method object for this method.
+ *
+ * We don't want to use "method", because that's the concrete
+ * implementation in the proxy class. We want the abstract Method
+ * from the declaring interface. We have a pointer to it tucked
+ * away in the "insns" field.
+ *
+ * TODO: this could be cached for performance.
+ */
+ methodObj = dvmCreateReflectMethodObject((Method*) method->insns);
+ if (methodObj == NULL) {
+ assert(dvmCheckException(self));
+ goto bail;
+ }
+
+ /*
+ * Determine the return type from the signature.
+ *
+ * TODO: this could be cached for performance.
+ */
+ returnType = dvmGetBoxedReturnType(method);
+ if (returnType == NULL) {
+ char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
+ LOGE("Could not determine return type for '%s'\n", desc);
+ free(desc);
+ assert(dvmCheckException(self));
+ goto bail;
+ }
+ LOGV(" return type will be %s\n", returnType->descriptor);
+
+ /*
+ * Convert "args" array into Object[] array, using the method
+ * signature to determine types. If the method takes no arguments,
+ * we must pass null.
+ */
+ argArray = boxMethodArgs(method, args+1);
+ if (dvmCheckException(self))
+ goto bail;
+
+ /*
+ * Call h.invoke(proxy, method, args).
+ *
+ * We don't need to repackage exceptions, so if one has been thrown
+ * just jump to the end.
+ */
+ dvmCallMethod(self, invoke, handler, &invokeResult,
+ thisObj, methodObj, argArray);
+ if (dvmCheckException(self)) {
+ Object* excep = dvmGetException(self);
+ if (mustWrapException(method, excep)) {
+ /* wrap with UndeclaredThrowableException */
+ dvmWrapException("Ljava/lang/reflect/UndeclaredThrowableException;");
+ }
+ goto bail;
+ }
+
+ /*
+ * Unbox the return value. If it's the wrong type, throw a
+ * ClassCastException. If it's a null pointer and we need a
+ * primitive type, throw a NullPointerException.
+ */
+ if (returnType->primitiveType == PRIM_VOID) {
+ LOGVV("+++ ignoring return to void\n");
+ } else if (invokeResult.l == NULL) {
+ if (dvmIsPrimitiveClass(returnType)) {
+ dvmThrowException("Ljava/lang/NullPointerException;",
+ "null result when primitive expected");
+ goto bail;
+ }
+ pResult->l = NULL;
+ } else {
+ if (!dvmUnwrapPrimitive(invokeResult.l, returnType, pResult)) {
+ dvmThrowExceptionWithClassMessage("Ljava/lang/ClassCastException;",
+ ((Object*)invokeResult.l)->clazz->descriptor);
+ goto bail;
+ }
+ }
+
+bail:
+ dvmReleaseTrackedAlloc(methodObj, self);
+ dvmReleaseTrackedAlloc((Object*)argArray, self);
+}
+
+/*
+ * Determine if it's okay for this method to throw this exception. If
+ * an unchecked exception was thrown we immediately return false. If
+ * checked, we have to ensure that this method and all of its duplicates
+ * have declared that they throw it.
+ */
+static bool mustWrapException(const Method* method, const Object* throwable)
+{
+ const ArrayObject* throws;
+ const ArrayObject* methodThrows;
+ const Object** contents;
+ const ClassObject** classes;
+
+ if (!dvmIsCheckedException(throwable))
+ return false;
+
+ const StaticField* sfield = &method->clazz->sfields[kThrowsField];
+ throws = (ArrayObject*) dvmGetStaticFieldObject(sfield);
+
+ int methodIndex = method - method->clazz->virtualMethods;
+ assert(methodIndex >= 0 && methodIndex < method->clazz->virtualMethodCount);
+
+ contents = (const Object**) throws->contents;
+ methodThrows = (ArrayObject*) contents[methodIndex];
+
+ if (methodThrows == NULL) {
+ /* no throws declared, must wrap all checked exceptions */
+ //printf("+++ methodThrows[%d] is null, wrapping all\n", methodIndex);
+ return true;
+ }
+
+ int throwCount = methodThrows->length;
+ classes = (const ClassObject**) methodThrows->contents;
+ int i;
+
+ //printf("%s.%s list:\n", method->clazz->descriptor, method->name);
+ //for (i = 0; i < throwCount; i++)
+ // printf(" %d: %s\n", i, classes[i]->descriptor);
+
+ for (i = 0; i < throwCount; i++) {
+ if (dvmInstanceof(throwable->clazz, classes[i])) {
+ /* this was declared, okay to throw */
+ return false;
+ }
+ }
+
+ /* no match in declared throws */
+ return true;
+}
diff --git a/vm/reflect/Reflect.c b/vm/reflect/Reflect.c
new file mode 100644
index 0000000..7e93f19
--- /dev/null
+++ b/vm/reflect/Reflect.c
@@ -0,0 +1,1253 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Basic reflection calls and utility functions.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+
+/*
+ * Cache some classes.
+ */
+bool dvmReflectStartup(void)
+{
+ gDvm.classJavaLangReflectAccessibleObject =
+ dvmFindSystemClassNoInit("Ljava/lang/reflect/AccessibleObject;");
+ gDvm.classJavaLangReflectConstructor =
+ dvmFindSystemClassNoInit("Ljava/lang/reflect/Constructor;");
+ gDvm.classJavaLangReflectConstructorArray =
+ dvmFindArrayClass("[Ljava/lang/reflect/Constructor;", NULL);
+ gDvm.classJavaLangReflectField =
+ dvmFindSystemClassNoInit("Ljava/lang/reflect/Field;");
+ gDvm.classJavaLangReflectFieldArray =
+ dvmFindArrayClass("[Ljava/lang/reflect/Field;", NULL);
+ gDvm.classJavaLangReflectMethod =
+ dvmFindSystemClassNoInit("Ljava/lang/reflect/Method;");
+ gDvm.classJavaLangReflectMethodArray =
+ dvmFindArrayClass("[Ljava/lang/reflect/Method;", NULL);
+ gDvm.classJavaLangReflectProxy =
+ dvmFindSystemClassNoInit("Ljava/lang/reflect/Proxy;");
+ if (gDvm.classJavaLangReflectAccessibleObject == NULL ||
+ gDvm.classJavaLangReflectConstructor == NULL ||
+ gDvm.classJavaLangReflectConstructorArray == NULL ||
+ gDvm.classJavaLangReflectField == NULL ||
+ gDvm.classJavaLangReflectFieldArray == NULL ||
+ gDvm.classJavaLangReflectMethod == NULL ||
+ gDvm.classJavaLangReflectMethodArray == NULL ||
+ gDvm.classJavaLangReflectProxy == NULL)
+ {
+ LOGE("Could not find one or more reflection classes\n");
+ return false;
+ }
+
+ gDvm.methJavaLangReflectConstructor_init =
+ dvmFindDirectMethodByDescriptor(gDvm.classJavaLangReflectConstructor, "<init>",
+ "(Ljava/lang/Class;[Ljava/lang/Class;[Ljava/lang/Class;I)V");
+ gDvm.methJavaLangReflectField_init =
+ dvmFindDirectMethodByDescriptor(gDvm.classJavaLangReflectField, "<init>",
+ "(Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/String;I)V");
+ gDvm.methJavaLangReflectMethod_init =
+ dvmFindDirectMethodByDescriptor(gDvm.classJavaLangReflectMethod, "<init>",
+ "(Ljava/lang/Class;[Ljava/lang/Class;[Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/String;I)V");
+ if (gDvm.methJavaLangReflectConstructor_init == NULL ||
+ gDvm.methJavaLangReflectField_init == NULL ||
+ gDvm.methJavaLangReflectMethod_init == NULL)
+ {
+ LOGE("Could not find reflection constructors\n");
+ return false;
+ }
+
+ gDvm.classJavaLangClassArray =
+ dvmFindArrayClass("[Ljava/lang/Class;", NULL);
+ gDvm.classJavaLangObjectArray =
+ dvmFindArrayClass("[Ljava/lang/Object;", NULL);
+ if (gDvm.classJavaLangClassArray == NULL ||
+ gDvm.classJavaLangObjectArray == NULL)
+ {
+ LOGE("Could not find class-array or object-array class\n");
+ return false;
+ }
+
+ gDvm.offJavaLangReflectAccessibleObject_flag =
+ dvmFindFieldOffset(gDvm.classJavaLangReflectAccessibleObject, "flag",
+ "Z");
+
+ gDvm.offJavaLangReflectConstructor_slot =
+ dvmFindFieldOffset(gDvm.classJavaLangReflectConstructor, "slot", "I");
+ gDvm.offJavaLangReflectConstructor_declClass =
+ dvmFindFieldOffset(gDvm.classJavaLangReflectConstructor,
+ "declaringClass", "Ljava/lang/Class;");
+
+ gDvm.offJavaLangReflectField_slot =
+ dvmFindFieldOffset(gDvm.classJavaLangReflectField, "slot", "I");
+ gDvm.offJavaLangReflectField_declClass =
+ dvmFindFieldOffset(gDvm.classJavaLangReflectField,
+ "declaringClass", "Ljava/lang/Class;");
+
+ gDvm.offJavaLangReflectMethod_slot =
+ dvmFindFieldOffset(gDvm.classJavaLangReflectMethod, "slot", "I");
+ gDvm.offJavaLangReflectMethod_declClass =
+ dvmFindFieldOffset(gDvm.classJavaLangReflectMethod,
+ "declaringClass", "Ljava/lang/Class;");
+
+ if (gDvm.offJavaLangReflectAccessibleObject_flag < 0 ||
+ gDvm.offJavaLangReflectConstructor_slot < 0 ||
+ gDvm.offJavaLangReflectConstructor_declClass < 0 ||
+ gDvm.offJavaLangReflectField_slot < 0 ||
+ gDvm.offJavaLangReflectField_declClass < 0 ||
+ gDvm.offJavaLangReflectMethod_slot < 0 ||
+ gDvm.offJavaLangReflectMethod_declClass < 0)
+ {
+ LOGE("Could not find reflection fields\n");
+ return false;
+ }
+
+ if (!dvmReflectProxyStartup())
+ return false;
+ if (!dvmReflectAnnotationStartup())
+ return false;
+
+ return true;
+}
+
+/*
+ * Clean up.
+ */
+void dvmReflectShutdown(void)
+{
+ // nothing to do
+}
+
+/*
+ * For some of the reflection stuff we need to un-box primitives, e.g.
+ * convert a java/lang/Integer to int or even a float. We assume that
+ * the first instance field holds the value.
+ *
+ * To verify this, we either need to ensure that the class has only one
+ * instance field, or we need to look up the field by name and verify
+ * that it comes first. The former is simpler, and should work.
+ */
+bool dvmValidateBoxClasses()
+{
+ static const char* classes[] = {
+ "Ljava/lang/Boolean;",
+ "Ljava/lang/Character;",
+ "Ljava/lang/Float;",
+ "Ljava/lang/Double;",
+ "Ljava/lang/Byte;",
+ "Ljava/lang/Short;",
+ "Ljava/lang/Integer;",
+ "Ljava/lang/Long;",
+ NULL
+ };
+ const char** ccp;
+
+ for (ccp = classes; *ccp != NULL; ccp++) {
+ ClassObject* clazz;
+
+ clazz = dvmFindClassNoInit(*ccp, NULL);
+ if (clazz == NULL) {
+ LOGE("Couldn't find '%s'\n", *ccp);
+ return false;
+ }
+
+ if (clazz->ifieldCount != 1) {
+ LOGE("Found %d instance fields in '%s'\n",
+ clazz->ifieldCount, *ccp);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+/*
+ * Find the named class object. We have to trim "*pSignature" down to just
+ * the first token, do the lookup, and then restore anything important
+ * that we've stomped on.
+ *
+ * "pSig" will be advanced to the start of the next token.
+ */
+static ClassObject* convertSignaturePartToClass(char** pSignature,
+ const ClassObject* defClass)
+{
+ ClassObject* clazz = NULL;
+ char* signature = *pSignature;
+
+ if (*signature == '[') {
+ /* looks like "[[[Landroid/debug/Stuff;"; we want the whole thing */
+ char savedChar;
+
+ while (*++signature == '[')
+ ;
+ if (*signature == 'L') {
+ while (*++signature != ';')
+ ;
+ }
+
+ /* advance past ';', and stomp on whatever comes next */
+ savedChar = *++signature;
+ *signature = '\0';
+ clazz = dvmFindArrayClass(*pSignature, defClass->classLoader);
+ *signature = savedChar;
+ } else if (*signature == 'L') {
+ /* looks like 'Landroid/debug/Stuff;"; we want the whole thing */
+ char savedChar;
+ while (*++signature != ';')
+ ;
+ savedChar = *++signature;
+ *signature = '\0';
+ clazz = dvmFindClassNoInit(*pSignature, defClass->classLoader);
+ *signature = savedChar;
+ } else {
+ clazz = dvmFindPrimitiveClass(*signature++);
+ }
+
+ if (clazz == NULL) {
+ LOGW("Unable to match class for part: '%s'\n", *pSignature);
+ dvmClearException(dvmThreadSelf());
+ dvmThrowException("Ljava/lang/NoSuchMethodException;", NULL);
+ }
+ *pSignature = signature;
+ return clazz;
+}
+
+/*
+ * Convert the method signature to an array of classes.
+ *
+ * The tokenization process may mangle "*pSignature". On return, it will
+ * be pointing at the closing ')'.
+ *
+ * "defClass" is the method's class, which is needed to make class loaders
+ * happy.
+ */
+static ArrayObject* convertSignatureToClassArray(char** pSignature,
+ ClassObject* defClass)
+{
+ ArrayObject* classArray;
+ char* signature = *pSignature;
+ char* cp;
+ int i, count;
+
+ assert(*signature == '(');
+ signature++;
+
+ /* count up the number of parameters */
+ count = 0;
+ cp = signature;
+ while (*cp != ')') {
+ count++;
+
+ if (*cp == '[') {
+ while (*++cp == '[')
+ ;
+ }
+ if (*cp == 'L') {
+ while (*++cp != ';')
+ ;
+ }
+ cp++;
+ }
+ LOGVV("REFLECT found %d parameters in '%s'\n", count, *pSignature);
+
+ /* create an array to hold them */
+ classArray = dvmAllocArray(gDvm.classJavaLangClassArray, count,
+ kObjectArrayRefWidth, ALLOC_DEFAULT);
+ if (classArray == NULL)
+ return NULL;
+
+ /* fill it in */
+ cp = signature;
+ for (i = 0; i < count; i++) {
+ ClassObject* clazz;
+
+ clazz = convertSignaturePartToClass(&cp, defClass);
+ if (clazz == NULL) {
+ assert(dvmCheckException(dvmThreadSelf()));
+ return NULL;
+ }
+ LOGVV("REFLECT %d: '%s'\n", i, clazz->descriptor);
+ dvmSetObjectArrayElement(classArray, i, (Object *)clazz);
+ }
+
+ *pSignature = cp;
+
+ /* caller must call dvmReleaseTrackedAlloc */
+ return classArray;
+}
+
+
+/*
+ * Convert a field pointer to a slot number.
+ *
+ * We use positive values starting from 0 for instance fields, negative
+ * values starting from -1 for static fields.
+ */
+static int fieldToSlot(const Field* field, const ClassObject* clazz)
+{
+ int slot;
+
+ if (dvmIsStaticField(field)) {
+ slot = (StaticField*)field - &clazz->sfields[0];
+ assert(slot >= 0 && slot < clazz->sfieldCount);
+ slot = -(slot+1);
+ } else {
+ slot = (InstField*)field - clazz->ifields;
+ assert(slot >= 0 && slot < clazz->ifieldCount);
+ }
+
+ return slot;
+}
+
+/*
+ * Convert a slot number to a field pointer.
+ */
+Field* dvmSlotToField(ClassObject* clazz, int slot)
+{
+ if (slot < 0) {
+ slot = -(slot+1);
+ assert(slot < clazz->sfieldCount);
+ return (Field*) &clazz->sfields[slot];
+ } else {
+ assert(slot < clazz->ifieldCount);
+ return (Field*) &clazz->ifields[slot];
+ }
+}
+
+/*
+ * Create a new java.lang.reflect.Field object from "field".
+ *
+ * The Field spec doesn't specify the constructor. We're going to use the
+ * one from our existing class libs:
+ *
+ * private Field(Class declaringClass, Class type, String name, int slot)
+ */
+static Object* createFieldObject(Field* field, const ClassObject* clazz)
+{
+ Object* result = NULL;
+ Object* fieldObj = NULL;
+ StringObject* nameObj = NULL;
+ ClassObject* type;
+ char* mangle;
+ char* cp;
+ int slot;
+
+ assert(dvmIsClassInitialized(gDvm.classJavaLangReflectField));
+
+ fieldObj = dvmAllocObject(gDvm.classJavaLangReflectField, ALLOC_DEFAULT);
+ if (fieldObj == NULL)
+ goto bail;
+
+ cp = mangle = strdup(field->signature);
+ type = convertSignaturePartToClass(&cp, clazz);
+ free(mangle);
+ if (type == NULL)
+ goto bail;
+
+ nameObj = dvmCreateStringFromCstr(field->name);
+ if (nameObj == NULL)
+ goto bail;
+
+ slot = fieldToSlot(field, clazz);
+
+ JValue unused;
+ dvmCallMethod(dvmThreadSelf(), gDvm.methJavaLangReflectField_init,
+ fieldObj, &unused, clazz, type, nameObj, slot);
+ if (dvmCheckException(dvmThreadSelf())) {
+ LOGD("Field class init threw exception\n");
+ goto bail;
+ }
+
+ result = fieldObj;
+
+bail:
+ dvmReleaseTrackedAlloc((Object*) nameObj, NULL);
+ if (result == NULL)
+ dvmReleaseTrackedAlloc((Object*) fieldObj, NULL);
+ /* caller must dvmReleaseTrackedAlloc(result) */
+ return result;
+}
+
+/*
+ *
+ * Get an array with all fields declared by a class.
+ *
+ * This includes both static and instance fields.
+ */
+ArrayObject* dvmGetDeclaredFields(ClassObject* clazz, bool publicOnly)
+{
+ ArrayObject* fieldArray = NULL;
+ int i, count;
+
+ if (!dvmIsClassInitialized(gDvm.classJavaLangReflectField))
+ dvmInitClass(gDvm.classJavaLangReflectField);
+
+ /* count #of fields */
+ if (!publicOnly)
+ count = clazz->sfieldCount + clazz->ifieldCount;
+ else {
+ count = 0;
+ for (i = 0; i < clazz->sfieldCount; i++) {
+ if ((clazz->sfields[i].field.accessFlags & ACC_PUBLIC) != 0)
+ count++;
+ }
+ for (i = 0; i < clazz->ifieldCount; i++) {
+ if ((clazz->ifields[i].field.accessFlags & ACC_PUBLIC) != 0)
+ count++;
+ }
+ }
+
+ /* create the Field[] array */
+ fieldArray = dvmAllocArray(gDvm.classJavaLangReflectFieldArray, count,
+ kObjectArrayRefWidth, ALLOC_DEFAULT);
+ if (fieldArray == NULL)
+ return NULL;
+
+ /* populate */
+ size_t fieldCount = 0;
+ for (i = 0; i < clazz->sfieldCount; i++) {
+ if (!publicOnly ||
+ (clazz->sfields[i].field.accessFlags & ACC_PUBLIC) != 0)
+ {
+ Object* field = createFieldObject(&clazz->sfields[i].field, clazz);
+ if (field == NULL) {
+ goto fail;
+ }
+ dvmSetObjectArrayElement(fieldArray, fieldCount, field);
+ dvmReleaseTrackedAlloc(field, NULL);
+ ++fieldCount;
+ }
+ }
+ for (i = 0; i < clazz->ifieldCount; i++) {
+ if (!publicOnly ||
+ (clazz->ifields[i].field.accessFlags & ACC_PUBLIC) != 0)
+ {
+ Object* field = createFieldObject(&clazz->ifields[i].field, clazz);
+ if (field == NULL) {
+ goto fail;
+ }
+ dvmSetObjectArrayElement(fieldArray, fieldCount, field);
+ dvmReleaseTrackedAlloc(field, NULL);
+ ++fieldCount;
+ }
+ }
+
+ assert(fieldCount == fieldArray->length);
+
+ /* caller must call dvmReleaseTrackedAlloc */
+ return fieldArray;
+
+fail:
+ dvmReleaseTrackedAlloc((Object*) fieldArray, NULL);
+ return NULL;
+}
+
+
+/*
+ * Convert a method pointer to a slot number.
+ *
+ * We use positive values starting from 0 for virtual methods, negative
+ * values starting from -1 for static methods.
+ */
+static int methodToSlot(const Method* meth)
+{
+ ClassObject* clazz = meth->clazz;
+ int slot;
+
+ if (dvmIsDirectMethod(meth)) {
+ slot = meth - clazz->directMethods;
+ assert(slot >= 0 && slot < clazz->directMethodCount);
+ slot = -(slot+1);
+ } else {
+ slot = meth - clazz->virtualMethods;
+ assert(slot >= 0 && slot < clazz->virtualMethodCount);
+ }
+
+ return slot;
+}
+
+/*
+ * Convert a slot number to a method pointer.
+ */
+Method* dvmSlotToMethod(ClassObject* clazz, int slot)
+{
+ if (slot < 0) {
+ slot = -(slot+1);
+ assert(slot < clazz->directMethodCount);
+ return &clazz->directMethods[slot];
+ } else {
+ assert(slot < clazz->virtualMethodCount);
+ return &clazz->virtualMethods[slot];
+ }
+}
+
+/*
+ * Create a new java/lang/reflect/Constructor object, using the contents of
+ * "meth" to construct it.
+ *
+ * The spec doesn't specify the constructor. We're going to use the
+ * one from our existing class libs:
+ *
+ * private Constructor (Class declaringClass, Class[] ptypes, Class[] extypes,
+ * int slot)
+ */
+static Object* createConstructorObject(Method* meth)
+{
+ Object* result = NULL;
+ ArrayObject* params = NULL;
+ ArrayObject* exceptions = NULL;
+ Object* consObj;
+ DexStringCache mangle;
+ char* cp;
+ int slot;
+
+ dexStringCacheInit(&mangle);
+
+ /* parent should guarantee init so we don't have to check on every call */
+ assert(dvmIsClassInitialized(gDvm.classJavaLangReflectConstructor));
+
+ consObj = dvmAllocObject(gDvm.classJavaLangReflectConstructor,
+ ALLOC_DEFAULT);
+ if (consObj == NULL)
+ goto bail;
+
+ /*
+ * Convert the signature string into an array of classes representing
+ * the arguments.
+ */
+ cp = dvmCopyDescriptorStringFromMethod(meth, &mangle);
+ params = convertSignatureToClassArray(&cp, meth->clazz);
+ if (params == NULL)
+ goto bail;
+ assert(*cp == ')');
+ assert(*(cp+1) == 'V');
+
+ /*
+ * Create an array with one entry for every exception that the class
+ * is declared to throw.
+ */
+ exceptions = dvmGetMethodThrows(meth);
+ if (dvmCheckException(dvmThreadSelf()))
+ goto bail;
+
+ slot = methodToSlot(meth);
+
+ JValue unused;
+ dvmCallMethod(dvmThreadSelf(), gDvm.methJavaLangReflectConstructor_init,
+ consObj, &unused, meth->clazz, params, exceptions, slot);
+ if (dvmCheckException(dvmThreadSelf())) {
+ LOGD("Constructor class init threw exception\n");
+ goto bail;
+ }
+
+ result = consObj;
+
+bail:
+ dexStringCacheRelease(&mangle);
+ dvmReleaseTrackedAlloc((Object*) params, NULL);
+ dvmReleaseTrackedAlloc((Object*) exceptions, NULL);
+ if (result == NULL) {
+ assert(dvmCheckException(dvmThreadSelf()));
+ dvmReleaseTrackedAlloc(consObj, NULL);
+ }
+ /* caller must dvmReleaseTrackedAlloc(result) */
+ return result;
+}
+
+/*
+ * Get an array with all constructors declared by a class.
+ */
+ArrayObject* dvmGetDeclaredConstructors(ClassObject* clazz, bool publicOnly)
+{
+ ArrayObject* consArray;
+ Method* meth;
+ int i, count;
+
+ if (!dvmIsClassInitialized(gDvm.classJavaLangReflectConstructor))
+ dvmInitClass(gDvm.classJavaLangReflectConstructor);
+
+ /*
+ * Ordinarily we init the class the first time we resolve a method.
+ * We're bypassing the normal resolution mechanism, so we init it here.
+ */
+ if (!dvmIsClassInitialized(clazz))
+ dvmInitClass(clazz);
+
+ /*
+ * Count up the #of relevant methods.
+ */
+ count = 0;
+ meth = clazz->directMethods;
+ for (i = 0; i < clazz->directMethodCount; i++, meth++) {
+ if ((!publicOnly || dvmIsPublicMethod(meth)) &&
+ dvmIsConstructorMethod(meth) && !dvmIsStaticMethod(meth))
+ {
+ count++;
+ }
+ }
+
+ /*
+ * Create an array of Constructor objects.
+ */
+ consArray = dvmAllocArray(gDvm.classJavaLangReflectConstructorArray, count,
+ kObjectArrayRefWidth, ALLOC_DEFAULT);
+ if (consArray == NULL)
+ return NULL;
+
+ /*
+ * Fill out the array.
+ */
+ meth = clazz->directMethods;
+ size_t consObjCount = 0;
+ for (i = 0; i < clazz->directMethodCount; i++, meth++) {
+ if ((!publicOnly || dvmIsPublicMethod(meth)) &&
+ dvmIsConstructorMethod(meth) && !dvmIsStaticMethod(meth))
+ {
+ Object* consObj = createConstructorObject(meth);
+ if (consObj == NULL)
+ goto fail;
+ dvmSetObjectArrayElement(consArray, consObjCount, consObj);
+ ++consObjCount;
+ dvmReleaseTrackedAlloc(consObj, NULL);
+ }
+ }
+
+ assert(consObjCount == consArray->length);
+
+ /* caller must call dvmReleaseTrackedAlloc */
+ return consArray;
+
+fail:
+ dvmReleaseTrackedAlloc((Object*) consArray, NULL);
+ return NULL;
+}
+
+/*
+ * Create a new java/lang/reflect/Method object, using the contents of
+ * "meth" to construct it.
+ *
+ * The spec doesn't specify the constructor. We're going to use the
+ * one from our existing class libs:
+ *
+ * private Method(Class declaring, Class[] paramTypes, Class[] exceptTypes,
+ * Class returnType, String name, int slot)
+ *
+ * The caller must call dvmReleaseTrackedAlloc() on the result.
+ */
+Object* dvmCreateReflectMethodObject(const Method* meth)
+{
+ Object* result = NULL;
+ ArrayObject* params = NULL;
+ ArrayObject* exceptions = NULL;
+ StringObject* nameObj = NULL;
+ Object* methObj;
+ ClassObject* returnType;
+ DexStringCache mangle;
+ char* cp;
+ int slot;
+
+ if (dvmCheckException(dvmThreadSelf())) {
+ LOGW("WARNING: dvmCreateReflectMethodObject called with "
+ "exception pending\n");
+ return NULL;
+ }
+
+ dexStringCacheInit(&mangle);
+
+ /* parent should guarantee init so we don't have to check on every call */
+ assert(dvmIsClassInitialized(gDvm.classJavaLangReflectMethod));
+
+ methObj = dvmAllocObject(gDvm.classJavaLangReflectMethod, ALLOC_DEFAULT);
+ if (methObj == NULL)
+ goto bail;
+
+ /*
+ * Convert the signature string into an array of classes representing
+ * the arguments, and a class for the return type.
+ */
+ cp = dvmCopyDescriptorStringFromMethod(meth, &mangle);
+ params = convertSignatureToClassArray(&cp, meth->clazz);
+ if (params == NULL)
+ goto bail;
+ assert(*cp == ')');
+ cp++;
+ returnType = convertSignaturePartToClass(&cp, meth->clazz);
+ if (returnType == NULL)
+ goto bail;
+
+ /*
+ * Create an array with one entry for every exception that the class
+ * is declared to throw.
+ */
+ exceptions = dvmGetMethodThrows(meth);
+ if (dvmCheckException(dvmThreadSelf()))
+ goto bail;
+
+ /* method name */
+ nameObj = dvmCreateStringFromCstr(meth->name);
+ if (nameObj == NULL)
+ goto bail;
+
+ slot = methodToSlot(meth);
+
+ JValue unused;
+ dvmCallMethod(dvmThreadSelf(), gDvm.methJavaLangReflectMethod_init,
+ methObj, &unused, meth->clazz, params, exceptions, returnType,
+ nameObj, slot);
+ if (dvmCheckException(dvmThreadSelf())) {
+ LOGD("Method class init threw exception\n");
+ goto bail;
+ }
+
+ result = methObj;
+
+bail:
+ dexStringCacheRelease(&mangle);
+ if (result == NULL) {
+ assert(dvmCheckException(dvmThreadSelf()));
+ }
+ dvmReleaseTrackedAlloc((Object*) nameObj, NULL);
+ dvmReleaseTrackedAlloc((Object*) params, NULL);
+ dvmReleaseTrackedAlloc((Object*) exceptions, NULL);
+ if (result == NULL)
+ dvmReleaseTrackedAlloc(methObj, NULL);
+ return result;
+}
+
+/*
+ * Get an array with all methods declared by a class.
+ *
+ * This includes both static and virtual methods, and can include private
+ * members if "publicOnly" is false. It does not include Miranda methods,
+ * since those weren't declared in the class, or constructors.
+ */
+ArrayObject* dvmGetDeclaredMethods(ClassObject* clazz, bool publicOnly)
+{
+ ArrayObject* methodArray;
+ Method* meth;
+ int i, count;
+
+ if (!dvmIsClassInitialized(gDvm.classJavaLangReflectMethod))
+ dvmInitClass(gDvm.classJavaLangReflectMethod);
+
+ /*
+ * Count up the #of relevant methods.
+ *
+ * Ignore virtual Miranda methods and direct class/object constructors.
+ */
+ count = 0;
+ meth = clazz->virtualMethods;
+ for (i = 0; i < clazz->virtualMethodCount; i++, meth++) {
+ if ((!publicOnly || dvmIsPublicMethod(meth)) &&
+ !dvmIsMirandaMethod(meth))
+ {
+ count++;
+ }
+ }
+ meth = clazz->directMethods;
+ for (i = 0; i < clazz->directMethodCount; i++, meth++) {
+ if ((!publicOnly || dvmIsPublicMethod(meth)) &&
+ meth->name[0] != '<')
+ {
+ count++;
+ }
+ }
+
+ /*
+ * Create an array of Method objects.
+ */
+ methodArray = dvmAllocArray(gDvm.classJavaLangReflectMethodArray, count,
+ kObjectArrayRefWidth, ALLOC_DEFAULT);
+ if (methodArray == NULL)
+ return NULL;
+
+
+ /*
+ * Fill out the array.
+ */
+ meth = clazz->virtualMethods;
+ size_t methObjCount = 0;
+ for (i = 0; i < clazz->virtualMethodCount; i++, meth++) {
+ if ((!publicOnly || dvmIsPublicMethod(meth)) &&
+ !dvmIsMirandaMethod(meth))
+ {
+ Object* methObj = dvmCreateReflectMethodObject(meth);
+ if (methObj == NULL)
+ goto fail;
+ dvmSetObjectArrayElement(methodArray, methObjCount, methObj);
+ ++methObjCount;
+ dvmReleaseTrackedAlloc(methObj, NULL);
+ }
+ }
+ meth = clazz->directMethods;
+ for (i = 0; i < clazz->directMethodCount; i++, meth++) {
+ if ((!publicOnly || dvmIsPublicMethod(meth)) &&
+ meth->name[0] != '<')
+ {
+ Object* methObj = dvmCreateReflectMethodObject(meth);
+ if (methObj == NULL)
+ goto fail;
+ dvmSetObjectArrayElement(methodArray, methObjCount, methObj);
+ ++methObjCount;
+ dvmReleaseTrackedAlloc(methObj, NULL);
+ }
+ }
+
+ assert(methObjCount == methodArray->length);
+
+ /* caller must call dvmReleaseTrackedAlloc */
+ return methodArray;
+
+fail:
+ dvmReleaseTrackedAlloc((Object*) methodArray, NULL);
+ return NULL;
+}
+
+/*
+ * Get all interfaces a class implements. If this is unable to allocate
+ * the result array, this raises an OutOfMemoryError and returns NULL.
+ */
+ArrayObject* dvmGetInterfaces(ClassObject* clazz)
+{
+ ArrayObject* interfaceArray;
+
+ if (!dvmIsClassInitialized(gDvm.classJavaLangReflectMethod))
+ dvmInitClass(gDvm.classJavaLangReflectMethod);
+
+ /*
+ * Create an array of Class objects.
+ */
+ int count = clazz->interfaceCount;
+ interfaceArray = dvmAllocArray(gDvm.classJavaLangClassArray, count,
+ kObjectArrayRefWidth, ALLOC_DEFAULT);
+ if (interfaceArray == NULL)
+ return NULL;
+
+ /*
+ * Fill out the array.
+ */
+ memcpy(interfaceArray->contents, clazz->interfaces,
+ count * sizeof(Object *));
+ dvmWriteBarrierArray(interfaceArray, 0, count);
+
+ /* caller must call dvmReleaseTrackedAlloc */
+ return interfaceArray;
+}
+
+/*
+ * Given a boxed primitive type, such as java/lang/Integer, return the
+ * primitive type index.
+ *
+ * Returns PRIM_NOT for void, since we never "box" that.
+ */
+static PrimitiveType getBoxedType(DataObject* arg)
+{
+ static const int kJavaLangLen = 11; // strlen("Ljava/lang/")
+ const char* name;
+
+ if (arg == NULL)
+ return PRIM_NOT;
+
+ name = arg->obj.clazz->descriptor;
+
+ if (strncmp(name, "Ljava/lang/", kJavaLangLen) != 0)
+ return PRIM_NOT;
+
+ if (strcmp(name + kJavaLangLen, "Boolean;") == 0)
+ return PRIM_BOOLEAN;
+ if (strcmp(name + kJavaLangLen, "Character;") == 0)
+ return PRIM_CHAR;
+ if (strcmp(name + kJavaLangLen, "Float;") == 0)
+ return PRIM_FLOAT;
+ if (strcmp(name + kJavaLangLen, "Double;") == 0)
+ return PRIM_DOUBLE;
+ if (strcmp(name + kJavaLangLen, "Byte;") == 0)
+ return PRIM_BYTE;
+ if (strcmp(name + kJavaLangLen, "Short;") == 0)
+ return PRIM_SHORT;
+ if (strcmp(name + kJavaLangLen, "Integer;") == 0)
+ return PRIM_INT;
+ if (strcmp(name + kJavaLangLen, "Long;") == 0)
+ return PRIM_LONG;
+ return PRIM_NOT;
+}
+
+/*
+ * Convert primitive, boxed data from "srcPtr" to "dstPtr".
+ *
+ * Section v2 2.6 lists the various conversions and promotions. We
+ * allow the "widening" and "identity" conversions, but don't allow the
+ * "narrowing" conversions.
+ *
+ * Allowed:
+ * byte to short, int, long, float, double
+ * short to int, long, float double
+ * char to int, long, float, double
+ * int to long, float, double
+ * long to float, double
+ * float to double
+ * Values of types byte, char, and short are "internally" widened to int.
+ *
+ * Returns the width in bytes of the destination primitive, or -1 if the
+ * conversion is not allowed.
+ *
+ * TODO? use JValue rather than u4 pointers
+ */
+int dvmConvertPrimitiveValue(PrimitiveType srcType,
+ PrimitiveType dstType, const s4* srcPtr, s4* dstPtr)
+{
+ enum {
+ OK4, OK8, ItoJ,
+ ItoD, JtoD, FtoD,
+ ItoF, JtoF,
+ bad, kMax
+ };
+ /* [src][dst] */
+ static const int kConvMode[kMax][kMax] = {
+ /*FROM *TO: bool char float double byte short int long */
+ /*bool */ { OK4, bad, bad, bad, bad, bad, bad, bad },
+ /*char */ { bad, OK4, ItoF, ItoD, bad, bad, OK4, ItoJ },
+ /*float*/ { bad, bad, OK4, FtoD, bad, bad, bad, bad },
+ /*doubl*/ { bad, bad, bad, OK8, bad, bad, bad, bad },
+ /*byte */ { bad, bad, ItoF, ItoD, OK4, OK4, OK4, ItoJ },
+ /*short*/ { bad, bad, ItoF, ItoD, bad, OK4, OK4, ItoJ },
+ /*int */ { bad, bad, ItoF, ItoD, bad, bad, OK4, ItoJ },
+ /*long */ { bad, bad, JtoF, JtoD, bad, bad, bad, OK8 },
+ };
+ int result;
+
+ assert(srcType != PRIM_NOT && dstType != PRIM_NOT &&
+ srcType != PRIM_VOID && dstType != PRIM_VOID);
+ result = kConvMode[srcType][dstType];
+
+ //LOGV("+++ convprim: src=%d dst=%d result=%d\n", srcType, dstType, result);
+
+ switch (result) {
+ case OK4:
+ *dstPtr = *srcPtr;
+ return 1;
+ case OK8:
+ *(s8*)dstPtr = *(s8*)srcPtr;
+ return 2;
+ case ItoJ:
+ *(s8*)dstPtr = (s8) (*(s4*) srcPtr);
+ return 2;
+ case ItoD:
+ *(double*)dstPtr = (double) (*(s4*) srcPtr);
+ return 2;
+ case JtoD:
+ *(double*)dstPtr = (double) (*(long long*) srcPtr);
+ return 2;
+ case FtoD:
+ *(double*)dstPtr = (double) (*(float*) srcPtr);
+ return 2;
+ case ItoF:
+ *(float*)dstPtr = (float) (*(int*) srcPtr);
+ return 1;
+ case JtoF:
+ *(float*)dstPtr = (float) (*(long long*) srcPtr);
+ return 1;
+ case bad:
+ LOGV("convert primitive: prim %d to %d not allowed\n",
+ srcType, dstType);
+ return -1;
+ default:
+ assert(false);
+ return -1;
+ }
+}
+
+/*
+ * Convert types and widen primitives. Puts the value of "arg" into
+ * "destPtr".
+ *
+ * Returns the width of the argument in 32-bit words (1 or 2), or -1 on error.
+ */
+int dvmConvertArgument(DataObject* arg, ClassObject* type, s4* destPtr)
+{
+ int retVal;
+
+ if (dvmIsPrimitiveClass(type)) {
+ /* e.g.: "arg" is java/lang/Float instance, "type" is VM float class */
+ PrimitiveType srcType;
+ s4* valuePtr;
+
+ srcType = getBoxedType(arg);
+ if (srcType < 0) { // didn't pass a boxed primitive in
+ LOGVV("conv arg: type '%s' not boxed primitive\n",
+ arg->obj.clazz->descriptor);
+ return -1;
+ }
+
+ /* assumes value is stored in first instance field */
+ valuePtr = (s4*) arg->instanceData;
+
+ retVal = dvmConvertPrimitiveValue(srcType, type->primitiveType,
+ valuePtr, destPtr);
+ } else {
+ /* verify object is compatible */
+ if ((arg == NULL) || dvmInstanceof(arg->obj.clazz, type)) {
+ *destPtr = (s4) arg;
+ retVal = 1;
+ } else {
+ LOGVV("Arg %p (%s) not compatible with %s\n",
+ arg, arg->obj.clazz->descriptor, type->descriptor);
+ retVal = -1;
+ }
+ }
+
+ return retVal;
+}
+
+/*
+ * Create a wrapper object for a primitive data type. If "returnType" is
+ * not primitive, this just casts "value" to an object and returns it.
+ *
+ * We could invoke the "toValue" method on the box types to take
+ * advantage of pre-created values, but running that through the
+ * interpreter is probably less efficient than just allocating storage here.
+ *
+ * The caller must call dvmReleaseTrackedAlloc on the result.
+ */
+DataObject* dvmWrapPrimitive(JValue value, ClassObject* returnType)
+{
+ static const char* boxTypes[] = { // order from enum PrimitiveType
+ "Ljava/lang/Boolean;",
+ "Ljava/lang/Character;",
+ "Ljava/lang/Float;",
+ "Ljava/lang/Double;",
+ "Ljava/lang/Byte;",
+ "Ljava/lang/Short;",
+ "Ljava/lang/Integer;",
+ "Ljava/lang/Long;"
+ };
+ ClassObject* wrapperClass;
+ DataObject* wrapperObj;
+ s4* dataPtr;
+ PrimitiveType typeIndex = returnType->primitiveType;
+ const char* classDescriptor;
+
+ if (typeIndex == PRIM_NOT) {
+ /* add to tracking table so return value is always in table */
+ if (value.l != NULL)
+ dvmAddTrackedAlloc(value.l, NULL);
+ return (DataObject*) value.l;
+ }
+
+ assert(typeIndex >= 0 && typeIndex < PRIM_MAX);
+ if (typeIndex == PRIM_VOID)
+ return NULL;
+
+ classDescriptor = boxTypes[typeIndex];
+
+ wrapperClass = dvmFindSystemClass(classDescriptor);
+ if (wrapperClass == NULL) {
+ LOGW("Unable to find '%s'\n", classDescriptor);
+ assert(dvmCheckException(dvmThreadSelf()));
+ return NULL;
+ }
+
+ wrapperObj = (DataObject*) dvmAllocObject(wrapperClass, ALLOC_DEFAULT);
+ if (wrapperObj == NULL)
+ return NULL;
+ dataPtr = (s4*) wrapperObj->instanceData;
+
+ /* assumes value is stored in first instance field */
+ /* (see dvmValidateBoxClasses) */
+ if (typeIndex == PRIM_LONG || typeIndex == PRIM_DOUBLE)
+ *(s8*)dataPtr = value.j;
+ else
+ *dataPtr = value.i;
+
+ return wrapperObj;
+}
+
+/*
+ * Unwrap a primitive data type, if necessary.
+ *
+ * If "returnType" is not primitive, we just tuck "value" into JValue and
+ * return it after verifying that it's the right type of object.
+ *
+ * Fails if the field is primitive and "value" is either not a boxed
+ * primitive or is of a type that cannot be converted.
+ *
+ * Returns "true" on success, "false" on failure.
+ */
+bool dvmUnwrapPrimitive(Object* value, ClassObject* returnType,
+ JValue* pResult)
+{
+ PrimitiveType typeIndex = returnType->primitiveType;
+ PrimitiveType valueIndex;
+
+ if (typeIndex == PRIM_NOT) {
+ if (value != NULL && !dvmInstanceof(value->clazz, returnType)) {
+ LOGD("wrong object type: %s %s\n",
+ value->clazz->descriptor, returnType->descriptor);
+ return false;
+ }
+ pResult->l = value;
+ return true;
+ } else if (typeIndex == PRIM_VOID) {
+ /* can't put anything into a void */
+ return false;
+ }
+
+ valueIndex = getBoxedType((DataObject*)value);
+ if (valueIndex == PRIM_NOT)
+ return false;
+
+ /* assumes value is stored in first instance field of "value" */
+ /* (see dvmValidateBoxClasses) */
+ if (dvmConvertPrimitiveValue(valueIndex, typeIndex,
+ (s4*) ((DataObject*)value)->instanceData, (s4*)pResult) < 0)
+ {
+ LOGV("Prim conversion failed\n");
+ return false;
+ }
+
+ return true;
+}
+
+
+/*
+ * Find the return type in the signature, and convert it to a class
+ * object. For primitive types we use a boxed class, for reference types
+ * we do a name lookup.
+ *
+ * On failure, we return NULL with an exception raised.
+ */
+ClassObject* dvmGetBoxedReturnType(const Method* meth)
+{
+ const char* sig = dexProtoGetReturnType(&meth->prototype);
+
+ switch (*sig) {
+ case 'Z':
+ case 'C':
+ case 'F':
+ case 'D':
+ case 'B':
+ case 'S':
+ case 'I':
+ case 'J':
+ case 'V':
+ return dvmFindPrimitiveClass(*sig);
+ case '[':
+ case 'L':
+ return dvmFindClass(sig, meth->clazz->classLoader);
+ default: {
+ /* should not have passed verification */
+ char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
+ LOGE("Bad return type in signature '%s'\n", desc);
+ free(desc);
+ dvmThrowException("Ljava/lang/InternalError;", NULL);
+ return NULL;
+ }
+ }
+}
+
+
+/*
+ * JNI reflection support: convert reflection object to Field ptr.
+ */
+Field* dvmGetFieldFromReflectObj(Object* obj)
+{
+ ClassObject* clazz;
+ int slot;
+
+ assert(obj->clazz == gDvm.classJavaLangReflectField);
+ clazz = (ClassObject*)dvmGetFieldObject(obj,
+ gDvm.offJavaLangReflectField_declClass);
+ slot = dvmGetFieldInt(obj, gDvm.offJavaLangReflectField_slot);
+
+ /* must initialize the class before returning a field ID */
+ if (!dvmInitClass(clazz))
+ return NULL;
+
+ return dvmSlotToField(clazz, slot);
+}
+
+/*
+ * JNI reflection support: convert reflection object to Method ptr.
+ */
+Method* dvmGetMethodFromReflectObj(Object* obj)
+{
+ ClassObject* clazz;
+ int slot;
+
+ if (obj->clazz == gDvm.classJavaLangReflectConstructor) {
+ clazz = (ClassObject*)dvmGetFieldObject(obj,
+ gDvm.offJavaLangReflectConstructor_declClass);
+ slot = dvmGetFieldInt(obj, gDvm.offJavaLangReflectConstructor_slot);
+ } else if (obj->clazz == gDvm.classJavaLangReflectMethod) {
+ clazz = (ClassObject*)dvmGetFieldObject(obj,
+ gDvm.offJavaLangReflectMethod_declClass);
+ slot = dvmGetFieldInt(obj, gDvm.offJavaLangReflectMethod_slot);
+ } else {
+ assert(false);
+ return NULL;
+ }
+
+ /* must initialize the class before returning a method ID */
+ if (!dvmInitClass(clazz))
+ return NULL;
+
+ return dvmSlotToMethod(clazz, slot);
+}
+
+/*
+ * JNI reflection support: convert Field to reflection object.
+ *
+ * The return value is a java.lang.reflect.Field.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+Object* dvmCreateReflectObjForField(const ClassObject* clazz, Field* field)
+{
+ if (!dvmIsClassInitialized(gDvm.classJavaLangReflectField))
+ dvmInitClass(gDvm.classJavaLangReflectField);
+
+ /* caller must dvmReleaseTrackedAlloc(result) */
+ return createFieldObject(field, clazz);
+}
+
+/*
+ * JNI reflection support: convert Method to reflection object.
+ *
+ * The returned object will be either a java.lang.reflect.Method or
+ * .Constructor, depending on whether "method" is a constructor.
+ *
+ * This is also used for certain "system" annotations.
+ *
+ * Caller must call dvmReleaseTrackedAlloc().
+ */
+Object* dvmCreateReflectObjForMethod(const ClassObject* clazz, Method* method)
+{
+ UNUSED_PARAMETER(clazz);
+
+ if (strcmp(method->name, "<init>") == 0) {
+ if (!dvmIsClassInitialized(gDvm.classJavaLangReflectConstructor))
+ dvmInitClass(gDvm.classJavaLangReflectConstructor);
+
+ return createConstructorObject(method);
+ } else {
+ if (!dvmIsClassInitialized(gDvm.classJavaLangReflectMethod))
+ dvmInitClass(gDvm.classJavaLangReflectMethod);
+
+ return dvmCreateReflectMethodObject(method);
+ }
+}
diff --git a/vm/reflect/Reflect.h b/vm/reflect/Reflect.h
new file mode 100644
index 0000000..16e7148
--- /dev/null
+++ b/vm/reflect/Reflect.h
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Basic reflection calls and utility functions.
+ */
+#ifndef _DALVIK_REFLECT_REFLECT
+#define _DALVIK_REFLECT_REFLECT
+
+bool dvmReflectStartup(void);
+bool dvmReflectProxyStartup(void);
+bool dvmReflectAnnotationStartup(void);
+void dvmReflectShutdown(void);
+
+/*
+ * During startup, validate the "box" classes, e.g. java/lang/Integer.
+ */
+bool dvmValidateBoxClasses();
+
+/*
+ * Get all fields declared by a class.
+ *
+ * Includes both class and instance fields.
+ */
+ArrayObject* dvmGetDeclaredFields(ClassObject* clazz, bool publicOnly);
+
+/*
+ * Get all constructors declared by a class.
+ */
+ArrayObject* dvmGetDeclaredConstructors(ClassObject* clazz, bool publicOnly);
+
+/*
+ * Get all methods declared by a class.
+ *
+ * This includes both static and virtual methods, and can include private
+ * members if "publicOnly" is false. It does not include Miranda methods,
+ * since those weren't declared in the class, or constructors.
+ */
+ArrayObject* dvmGetDeclaredMethods(ClassObject* clazz, bool publicOnly);
+
+/*
+ * Get all interfaces a class implements. If this is unable to allocate
+ * the result array, this raises an OutOfMemoryError and returns NULL.
+ */
+ArrayObject* dvmGetInterfaces(ClassObject* clazz);
+
+/*
+ * Convert slot numbers back to objects.
+ */
+Field* dvmSlotToField(ClassObject* clazz, int slot);
+Method* dvmSlotToMethod(ClassObject* clazz, int slot);
+
+/*
+ * Convert a primitive value, performing a widening conversion if necessary.
+ */
+int dvmConvertPrimitiveValue(PrimitiveType srcType,
+ PrimitiveType dstType, const s4* srcPtr, s4* dstPtr);
+
+/*
+ * Convert the argument to the specified type.
+ *
+ * Returns the width of the argument (1 for most types, 2 for J/D, -1 on
+ * error).
+ */
+int dvmConvertArgument(DataObject* arg, ClassObject* type, s4* ins);
+
+/*
+ * Create a wrapper object for a primitive data type. If "returnType" is
+ * not primitive, this just returns "value" cast to an object.
+ */
+DataObject* dvmWrapPrimitive(JValue value, ClassObject* returnType);
+
+/*
+ * Unwrap a boxed primitive. If "returnType" is not primitive, this just
+ * returns "value" cast into a JValue.
+ */
+bool dvmUnwrapPrimitive(Object* value, ClassObject* returnType,
+ JValue* pResult);
+
+/*
+ * Return the class object that matches the method's signature. For
+ * primitive types, returns the box class.
+ */
+ClassObject* dvmGetBoxedReturnType(const Method* meth);
+
+/*
+ * JNI reflection support.
+ */
+Field* dvmGetFieldFromReflectObj(Object* obj);
+Method* dvmGetMethodFromReflectObj(Object* obj);
+Object* dvmCreateReflectObjForField(const ClassObject* clazz, Field* field);
+Object* dvmCreateReflectObjForMethod(const ClassObject* clazz, Method* method);
+
+/*
+ * Quick test to determine if the method in question is a reflection call.
+ * Used for some stack parsing. Currently defined as "the method's declaring
+ * class is java.lang.reflect.Method".
+ */
+INLINE bool dvmIsReflectionMethod(const Method* method)
+{
+ return (method->clazz == gDvm.classJavaLangReflectMethod);
+}
+
+/*
+ * Proxy class generation.
+ */
+ClassObject* dvmGenerateProxyClass(StringObject* str, ArrayObject* interfaces,
+ Object* loader);
+
+/*
+ * Create a new java.lang.reflect.Method object based on "meth".
+ */
+Object* dvmCreateReflectMethodObject(const Method* meth);
+
+/*
+ * Return an array of Annotation objects for the specified piece. For method
+ * parameters this is an array of arrays of Annotation objects.
+ *
+ * Method also applies to Constructor.
+ */
+ArrayObject* dvmGetClassAnnotations(const ClassObject* clazz);
+ArrayObject* dvmGetMethodAnnotations(const Method* method);
+ArrayObject* dvmGetFieldAnnotations(const Field* field);
+ArrayObject* dvmGetParameterAnnotations(const Method* method);
+
+/*
+ * Find the default value for an annotation member.
+ */
+Object* dvmGetAnnotationDefaultValue(const Method* method);
+
+/*
+ * Get the list of thrown exceptions for a method. Returns NULL if there
+ * are no exceptions listed.
+ */
+ArrayObject* dvmGetMethodThrows(const Method* method);
+
+/*
+ * Get the Signature annotation.
+ */
+ArrayObject* dvmGetClassSignatureAnnotation(const ClassObject* clazz);
+ArrayObject* dvmGetMethodSignatureAnnotation(const Method* method);
+ArrayObject* dvmGetFieldSignatureAnnotation(const Field* field);
+
+/*
+ * Get the EnclosingMethod attribute from an annotation. Returns a Method
+ * object, or NULL.
+ */
+Object* dvmGetEnclosingMethod(const ClassObject* clazz);
+
+/*
+ * Return clazz's declaring class, or NULL if there isn't one.
+ */
+ClassObject* dvmGetDeclaringClass(const ClassObject* clazz);
+
+/*
+ * Return clazz's enclosing class, or NULL if there isn't one.
+ */
+ClassObject* dvmGetEnclosingClass(const ClassObject* clazz);
+
+/*
+ * Get the EnclosingClass attribute from an annotation. If found, returns
+ * "true". A String with the original name of the class and the original
+ * access flags are returned through the arguments. (The name will be NULL
+ * for an anonymous inner class.)
+ */
+bool dvmGetInnerClass(const ClassObject* clazz, StringObject** pName,
+ int* pAccessFlags);
+
+/*
+ * Get an array of class objects from the MemberClasses annotation. Returns
+ * NULL if none found.
+ */
+ArrayObject* dvmGetDeclaredClasses(const ClassObject* clazz);
+
+/*
+ * Used to pass values out of annotation (and encoded array) processing
+ * functions.
+ */
+typedef struct AnnotationValue {
+ JValue value;
+ u1 type;
+} AnnotationValue;
+
+
+/**
+ * Iterator structure for iterating over DexEncodedArray instances. The
+ * structure should be treated as opaque.
+ */
+typedef struct {
+ const u1* cursor; /* current cursor */
+ u4 elementsLeft; /* number of elements left to read */
+ const DexEncodedArray* encodedArray; /* instance being iterated over */
+ u4 size; /* number of elements in instance */
+ const ClassObject* clazz; /* class to resolve with respect to */
+} EncodedArrayIterator;
+
+/**
+ * Initializes an encoded array iterator.
+ *
+ * @param iterator iterator to initialize
+ * @param encodedArray encoded array to iterate over
+ * @param clazz class to use when resolving strings and types
+ */
+void dvmEncodedArrayIteratorInitialize(EncodedArrayIterator* iterator,
+ const DexEncodedArray* encodedArray, const ClassObject* clazz);
+
+/**
+ * Returns whether there are more elements to be read.
+ */
+bool dvmEncodedArrayIteratorHasNext(const EncodedArrayIterator* iterator);
+
+/**
+ * Returns the next decoded value from the iterator, advancing its
+ * cursor. This returns primitive values in their corresponding union
+ * slots, and returns everything else (including nulls) as object
+ * references in the "l" union slot.
+ *
+ * The caller must call dvmReleaseTrackedAlloc() on any returned reference.
+ *
+ * @param value pointer to store decoded value into
+ * @returns true if a value was decoded and the cursor advanced; false if
+ * the last value had already been decoded or if there was a problem decoding
+ */
+bool dvmEncodedArrayIteratorGetNext(EncodedArrayIterator* iterator,
+ AnnotationValue* value);
+
+#endif /*_DALVIK_REFLECT_REFLECT*/
diff --git a/vm/test/AtomicTest.c b/vm/test/AtomicTest.c
new file mode 100644
index 0000000..62333d0
--- /dev/null
+++ b/vm/test/AtomicTest.c
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This provides a handful of correctness and speed tests on our atomic
+ * operations.
+ *
+ * This doesn't really belong here, but we currently lack a better place
+ * for it, so this will do for now.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <cutils/atomic.h>
+#ifdef __arm__
+# include <machine/cpu-features.h>
+#endif
+
+#define USE_ATOMIC 1
+#define THREAD_COUNT 10
+#define ITERATION_COUNT 500000
+
+#ifdef HAVE_ANDROID_OS
+/*#define TEST_BIONIC 1*/
+#endif
+
+
+#ifdef TEST_BIONIC
+extern int __atomic_cmpxchg(int old, int _new, volatile int *ptr);
+extern int __atomic_swap(int _new, volatile int *ptr);
+extern int __atomic_dec(volatile int *ptr);
+extern int __atomic_inc(volatile int *ptr);
+#endif
+
+static pthread_mutex_t waitLock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t waitCond = PTHREAD_COND_INITIALIZER;
+
+static volatile int threadsStarted = 0;
+
+/* results */
+static int incTest = 0;
+static int decTest = 0;
+static int addTest = 0;
+static int andTest = 0;
+static int orTest = 0;
+static int casTest = 0;
+static int failingCasTest = 0;
+static int swapTest = 0;
+static int64_t wideCasTest = 0x6600000077000000LL;
+
+/*
+ * Get a relative time value.
+ */
+static int64_t getRelativeTimeNsec(void)
+{
+#define HAVE_POSIX_CLOCKS
+#ifdef HAVE_POSIX_CLOCKS
+ struct timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ return (int64_t) now.tv_sec*1000000000LL + now.tv_nsec;
+#else
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ return (int64_t) now.tv_sec*1000000000LL + now.tv_usec * 1000LL;
+#endif
+}
+
+
+/*
+ * Non-atomic implementations, for comparison.
+ *
+ * If these get inlined the compiler may figure out what we're up to and
+ * completely elide the operations.
+ */
+static void incr(void) __attribute__((noinline));
+static void decr(void) __attribute__((noinline));
+static void add(int addVal) __attribute__((noinline));
+static int compareAndSwap(int oldVal, int newVal, int* addr) __attribute__((noinline));
+static int compareAndSwapWide(int64_t oldVal, int64_t newVal, int64_t* addr) __attribute__((noinline));
+
+static void incr(void)
+{
+ incTest++;
+}
+static void decr(void)
+{
+ decTest--;
+}
+static void add(int32_t addVal)
+{
+ addTest += addVal;
+}
+static int compareAndSwap(int32_t oldVal, int32_t newVal, int32_t* addr)
+{
+ if (*addr == oldVal) {
+ *addr = newVal;
+ return 0;
+ }
+ return 1;
+}
+static int compareAndSwapWide(int64_t oldVal, int64_t newVal, int64_t* addr)
+{
+ if (*addr == oldVal) {
+ *addr = newVal;
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Exercise several of the atomic ops.
+ */
+static void doAtomicTest(int num)
+{
+ int addVal = (num & 0x01) + 1;
+
+ int i;
+ for (i = 0; i < ITERATION_COUNT; i++) {
+ if (USE_ATOMIC) {
+ android_atomic_inc(&incTest);
+ android_atomic_dec(&decTest);
+ android_atomic_add(addVal, &addTest);
+
+ int val;
+ do {
+ val = casTest;
+ } while (android_atomic_release_cas(val, val+3, &casTest) != 0);
+ do {
+ val = casTest;
+ } while (android_atomic_acquire_cas(val, val-1, &casTest) != 0);
+
+ int64_t wval;
+ do {
+ wval = dvmQuasiAtomicRead64(&wideCasTest);
+ } while (dvmQuasiAtomicCas64(wval,
+ wval + 0x0000002000000001LL, &wideCasTest) != 0);
+ do {
+ wval = dvmQuasiAtomicRead64(&wideCasTest);
+ } while (dvmQuasiAtomicCas64(wval,
+ wval - 0x0000002000000001LL, &wideCasTest) != 0);
+ } else {
+ incr();
+ decr();
+ add(addVal);
+
+ int val;
+ do {
+ val = casTest;
+ } while (compareAndSwap(val, val+3, &casTest) != 0);
+ do {
+ val = casTest;
+ } while (compareAndSwap(val, val-1, &casTest) != 0);
+
+ int64_t wval;
+ do {
+ wval = wideCasTest;
+ } while (compareAndSwapWide(wval,
+ wval + 0x0000002000000001LL, &wideCasTest) != 0);
+ do {
+ wval = wideCasTest;
+ } while (compareAndSwapWide(wval,
+ wval - 0x0000002000000001LL, &wideCasTest) != 0);
+ }
+ }
+}
+
+/*
+ * Entry point for multi-thread test.
+ */
+static void* atomicTest(void* arg)
+{
+ pthread_mutex_lock(&waitLock);
+ threadsStarted++;
+ pthread_cond_wait(&waitCond, &waitLock);
+ pthread_mutex_unlock(&waitLock);
+
+ doAtomicTest((int) arg);
+
+ return NULL;
+}
+
+/* lifted from a VM test */
+static int64_t testAtomicSpeedSub(int repeatCount)
+{
+ static int value = 7;
+ int* valuePtr = &value;
+ int64_t start, end;
+ int i;
+
+ start = getRelativeTimeNsec();
+
+ for (i = repeatCount / 10; i != 0; i--) {
+ if (USE_ATOMIC) {
+ // succeed 10x
+ (void) android_atomic_release_cas(7, 7, valuePtr);
+ (void) android_atomic_release_cas(7, 7, valuePtr);
+ (void) android_atomic_release_cas(7, 7, valuePtr);
+ (void) android_atomic_release_cas(7, 7, valuePtr);
+ (void) android_atomic_release_cas(7, 7, valuePtr);
+ (void) android_atomic_release_cas(7, 7, valuePtr);
+ (void) android_atomic_release_cas(7, 7, valuePtr);
+ (void) android_atomic_release_cas(7, 7, valuePtr);
+ (void) android_atomic_release_cas(7, 7, valuePtr);
+ (void) android_atomic_release_cas(7, 7, valuePtr);
+ } else {
+ // succeed 10x
+ compareAndSwap(7, 7, valuePtr);
+ compareAndSwap(7, 7, valuePtr);
+ compareAndSwap(7, 7, valuePtr);
+ compareAndSwap(7, 7, valuePtr);
+ compareAndSwap(7, 7, valuePtr);
+ compareAndSwap(7, 7, valuePtr);
+ compareAndSwap(7, 7, valuePtr);
+ compareAndSwap(7, 7, valuePtr);
+ compareAndSwap(7, 7, valuePtr);
+ compareAndSwap(7, 7, valuePtr);
+ }
+ }
+
+ end = getRelativeTimeNsec();
+
+ dvmFprintf(stdout, ".");
+ fflush(stdout);
+ return end - start;
+}
+
+static void testAtomicSpeed(void)
+{
+ static const int kIterations = 10;
+ static const int kRepeatCount = 5 * 1000 * 1000;
+ static const int kDelay = 50 * 1000;
+ int64_t results[kIterations];
+ int i;
+
+ for (i = 0; i < kIterations; i++) {
+ results[i] = testAtomicSpeedSub(kRepeatCount);
+ usleep(kDelay);
+ }
+
+ dvmFprintf(stdout, "\n");
+ dvmFprintf(stdout, "%s speed test results (%d per iteration):\n",
+ USE_ATOMIC ? "Atomic" : "Non-atomic", kRepeatCount);
+ for (i = 0; i < kIterations; i++) {
+ dvmFprintf(stdout,
+ " %2d: %.3fns\n", i, (double) results[i] / kRepeatCount);
+ }
+}
+
+/*
+ * Start tests, show results.
+ */
+bool dvmTestAtomicSpeed(void)
+{
+ pthread_t threads[THREAD_COUNT];
+ void *(*startRoutine)(void*) = atomicTest;
+ int64_t startWhen, endWhen;
+
+#if defined(__ARM_ARCH__)
+ dvmFprintf(stdout, "__ARM_ARCH__ is %d\n", __ARM_ARCH__);
+#endif
+#if defined(ANDROID_SMP)
+ dvmFprintf(stdout, "ANDROID_SMP is %d\n", ANDROID_SMP);
+#endif
+ dvmFprintf(stdout, "Creating threads\n");
+
+ int i;
+ for (i = 0; i < THREAD_COUNT; i++) {
+ void* arg = (void*) i;
+ if (pthread_create(&threads[i], NULL, startRoutine, arg) != 0) {
+ dvmFprintf(stderr, "thread create failed\n");
+ }
+ }
+
+ /* wait for all the threads to reach the starting line */
+ while (1) {
+ pthread_mutex_lock(&waitLock);
+ if (threadsStarted == THREAD_COUNT) {
+ dvmFprintf(stdout, "Starting test\n");
+ startWhen = getRelativeTimeNsec();
+ pthread_cond_broadcast(&waitCond);
+ pthread_mutex_unlock(&waitLock);
+ break;
+ }
+ pthread_mutex_unlock(&waitLock);
+ usleep(100000);
+ }
+
+ for (i = 0; i < THREAD_COUNT; i++) {
+ void* retval;
+ if (pthread_join(threads[i], &retval) != 0) {
+ dvmFprintf(stderr, "thread join (%d) failed\n", i);
+ }
+ }
+
+ endWhen = getRelativeTimeNsec();
+ dvmFprintf(stdout, "All threads stopped, time is %.6fms\n",
+ (endWhen - startWhen) / 1000000.0);
+
+ /*
+ * Show results; expecting:
+ *
+ * incTest = 5000000
+ * decTest = -5000000
+ * addTest = 7500000
+ * casTest = 10000000
+ * wideCasTest = 0x6600000077000000
+ */
+ dvmFprintf(stdout, "incTest = %d\n", incTest);
+ dvmFprintf(stdout, "decTest = %d\n", decTest);
+ dvmFprintf(stdout, "addTest = %d\n", addTest);
+ dvmFprintf(stdout, "casTest = %d\n", casTest);
+ dvmFprintf(stdout, "wideCasTest = 0x%llx\n", wideCasTest);
+
+ /* do again, serially (SMP check) */
+ startWhen = getRelativeTimeNsec();
+ for (i = 0; i < THREAD_COUNT; i++) {
+ doAtomicTest(i);
+ }
+ endWhen = getRelativeTimeNsec();
+ dvmFprintf(stdout, "Same iterations done serially: time is %.6fms\n",
+ (endWhen - startWhen) / 1000000.0);
+
+ /*
+ * Hard to do a meaningful thrash test on these, so just do a simple
+ * function test.
+ */
+ andTest = 0xffd7fa96;
+ orTest = 0x122221ff;
+ swapTest = 0x11111111;
+ android_atomic_and(0xfffdaf96, &andTest);
+ android_atomic_or(0xdeaaeb00, &orTest);
+ int oldSwap = android_atomic_swap(0x22222222, &swapTest);
+ int oldSwap2 = android_atomic_swap(0x33333333, &swapTest);
+ if (android_atomic_release_cas(failingCasTest+1, failingCasTest-1,
+ &failingCasTest) == 0)
+ dvmFprintf(stdout, "failing test did not fail!\n");
+
+ dvmFprintf(stdout, "andTest = 0x%x\n", andTest);
+ dvmFprintf(stdout, "orTest = 0x%x\n", orTest);
+ dvmFprintf(stdout, "swapTest = 0x%x -> 0x%x\n", oldSwap, oldSwap2);
+ dvmFprintf(stdout, "swapTest = 0x%x -> 0x%x\n", oldSwap2, swapTest);
+ dvmFprintf(stdout, "failingCasTest = %d\n", failingCasTest);
+
+#ifdef TEST_BIONIC
+ /*
+ * Quick function test on the bionic ops.
+ */
+ int prev;
+ int tester = 7;
+ prev = __atomic_inc(&tester);
+ __atomic_inc(&tester);
+ __atomic_inc(&tester);
+ dvmFprintf(stdout, "bionic 3 inc: %d -> %d\n", prev, tester);
+ prev = __atomic_dec(&tester);
+ __atomic_dec(&tester);
+ __atomic_dec(&tester);
+ dvmFprintf(stdout, "bionic 3 dec: %d -> %d\n", prev, tester);
+ prev = __atomic_swap(27, &tester);
+ dvmFprintf(stdout, "bionic swap: %d -> %d\n", prev, tester);
+ int swapok = __atomic_cmpxchg(27, 72, &tester);
+ dvmFprintf(stdout, "bionic cmpxchg: %d (%d)\n", tester, swapok);
+#endif
+
+ testAtomicSpeed();
+
+ return 0;
+}
diff --git a/vm/test/Test.h b/vm/test/Test.h
new file mode 100644
index 0000000..f17526f
--- /dev/null
+++ b/vm/test/Test.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Internal unit tests.
+ */
+#ifndef _DALVIK_TEST_TEST
+#define _DALVIK_TEST_TEST
+
+bool dvmTestHash(void);
+bool dvmTestAtomicSpeed(void);
+bool dvmTestIndirectRefTable(void);
+
+#endif /*_DALVIK_TEST_TEST*/
diff --git a/vm/test/TestHash.c b/vm/test/TestHash.c
new file mode 100644
index 0000000..26de141
--- /dev/null
+++ b/vm/test/TestHash.c
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Test the hash table functions.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+
+#ifndef NDEBUG
+
+#define kNumTestEntries 14
+
+/*
+ * Test foreach.
+ */
+static int printFunc(void* data, void* arg)
+{
+ //printf(" '%s'\n", (const char*) data);
+ // (should verify strings)
+
+ int* count = (int*) arg;
+ (*count)++;
+ return 0;
+}
+static void dumpForeach(HashTable* pTab)
+{
+ int count = 0;
+
+ //printf("Print from foreach:\n");
+ dvmHashForeach(pTab, printFunc, &count);
+ if (count != kNumTestEntries) {
+ LOGE("TestHash foreach test failed\n");
+ assert(false);
+ }
+}
+
+/*
+ * Test iterator.
+ */
+static void dumpIterator(HashTable* pTab)
+{
+ int count = 0;
+
+ //printf("Print from iterator:\n");
+ HashIter iter;
+ for (dvmHashIterBegin(pTab, &iter); !dvmHashIterDone(&iter);
+ dvmHashIterNext(&iter))
+ {
+ //const char* str = (const char*) dvmHashIterData(&iter);
+ //printf(" '%s'\n", str);
+ // (should verify strings)
+ count++;
+ }
+ if (count != kNumTestEntries) {
+ LOGE("TestHash iterator test failed\n");
+ assert(false);
+ }
+}
+
+/*
+ * Some quick hash table tests.
+ */
+bool dvmTestHash(void)
+{
+ HashTable* pTab;
+ char tmpStr[64];
+ const char* str;
+ u4 hash;
+ int i;
+
+ LOGV("TestHash BEGIN\n");
+
+ pTab = dvmHashTableCreate(dvmHashSize(12), free);
+ if (pTab == NULL)
+ return false;
+
+ dvmHashTableLock(pTab);
+
+ /* add some entries */
+ for (i = 0; i < kNumTestEntries; i++) {
+ sprintf(tmpStr, "entry %d", i);
+ hash = dvmComputeUtf8Hash(tmpStr);
+ dvmHashTableLookup(pTab, hash, strdup(tmpStr),
+ (HashCompareFunc) strcmp, true);
+ }
+
+ dvmHashTableUnlock(pTab);
+
+ /* make sure we can find all entries */
+ for (i = 0; i < kNumTestEntries; i++) {
+ sprintf(tmpStr, "entry %d", i);
+ hash = dvmComputeUtf8Hash(tmpStr);
+ str = (const char*) dvmHashTableLookup(pTab, hash, tmpStr,
+ (HashCompareFunc) strcmp, false);
+ if (str == NULL) {
+ LOGE("TestHash: failure: could not find '%s'\n", tmpStr);
+ /* return false */
+ }
+ }
+
+ /* make sure it behaves correctly when entry not found and !doAdd */
+ sprintf(tmpStr, "entry %d", 17);
+ hash = dvmComputeUtf8Hash(tmpStr);
+ str = (const char*) dvmHashTableLookup(pTab, hash, tmpStr,
+ (HashCompareFunc) strcmp, false);
+ if (str == NULL) {
+ /* good */
+ } else {
+ LOGE("TestHash found nonexistent string (improper add?)\n");
+ }
+
+ dumpForeach(pTab);
+ dumpIterator(pTab);
+
+ /* make sure they all get freed */
+ dvmHashTableFree(pTab);
+
+
+ /*
+ * Round 2: verify probing & tombstones.
+ */
+ pTab = dvmHashTableCreate(dvmHashSize(2), free);
+ if (pTab == NULL)
+ return false;
+
+ hash = 0;
+
+ /* two entries, same hash, different values */
+ char* str1;
+ str1 = dvmHashTableLookup(pTab, hash, strdup("one"),
+ (HashCompareFunc) strcmp, true);
+ assert(str1 != NULL);
+ str = dvmHashTableLookup(pTab, hash, strdup("two"),
+ (HashCompareFunc) strcmp, true);
+
+ /* remove the first one */
+ if (!dvmHashTableRemove(pTab, hash, str1))
+ LOGE("TestHash failed to delete item\n");
+ else
+ free(str1); // "Remove" doesn't call the free func
+
+ /* make sure iterator doesn't included deleted entries */
+ int count = 0;
+ HashIter iter;
+ for (dvmHashIterBegin(pTab, &iter); !dvmHashIterDone(&iter);
+ dvmHashIterNext(&iter))
+ {
+ count++;
+ }
+ if (count != 1) {
+ LOGE("TestHash wrong number of entries (%d)\n", count);
+ }
+
+ /* see if we can find them */
+ str = dvmHashTableLookup(pTab, hash, "one", (HashCompareFunc) strcmp,false);
+ if (str != NULL)
+ LOGE("TestHash deleted entry has returned!");
+ str = dvmHashTableLookup(pTab, hash, "two", (HashCompareFunc) strcmp,false);
+ if (str == NULL)
+ LOGE("TestHash entry vanished\n");
+
+ /* force a table realloc to exercise tombstone removal */
+ for (i = 0; i < 20; i++) {
+ sprintf(tmpStr, "entry %d", i);
+ str = (const char*) dvmHashTableLookup(pTab, hash, strdup(tmpStr),
+ (HashCompareFunc) strcmp, true);
+ assert(str != NULL);
+ }
+
+ dvmHashTableFree(pTab);
+ LOGV("TestHash END\n");
+
+ return true;
+}
+
+#endif /*NDEBUG*/
diff --git a/vm/test/TestIndirectRefTable.c b/vm/test/TestIndirectRefTable.c
new file mode 100644
index 0000000..25f1dd1
--- /dev/null
+++ b/vm/test/TestIndirectRefTable.c
@@ -0,0 +1,488 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Test the indirect reference table implementation.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+
+#ifndef NDEBUG
+
+#define DBUG_MSG LOGV
+
+/*
+ * Basic add/get/delete tests in an unsegmented table.
+ */
+static bool basicTest(void)
+{
+ static const int kTableMax = 20;
+ IndirectRefTable irt;
+ IndirectRef iref0, iref1, iref2, iref3;
+ IndirectRef manyRefs[kTableMax];
+ ClassObject* clazz = dvmFindClass("Ljava/lang/Object;", NULL);
+ Object* obj0 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+ Object* obj1 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+ Object* obj2 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+ Object* obj3 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+ const u4 cookie = IRT_FIRST_SEGMENT;
+ bool result = false;
+
+ if (!dvmInitIndirectRefTable(&irt, kTableMax/2, kTableMax,
+ kIndirectKindGlobal))
+ {
+ return false;
+ }
+
+ iref0 = (IndirectRef) 0x11110;
+ if (dvmRemoveFromIndirectRefTable(&irt, cookie, iref0)) {
+ LOGE("unexpectedly successful removal\n");
+ goto bail;
+ }
+
+ /*
+ * Add three, check, remove in the order in which they were added.
+ */
+ DBUG_MSG("+++ START fifo\n");
+ iref0 = dvmAddToIndirectRefTable(&irt, cookie, obj0);
+ iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj1);
+ iref2 = dvmAddToIndirectRefTable(&irt, cookie, obj2);
+ if (iref0 == NULL || iref1 == NULL || iref2 == NULL) {
+ LOGE("trivial add1 failed\n");
+ goto bail;
+ }
+
+ if (dvmGetFromIndirectRefTable(&irt, iref0) != obj0 ||
+ dvmGetFromIndirectRefTable(&irt, iref1) != obj1 ||
+ dvmGetFromIndirectRefTable(&irt, iref2) != obj2)
+ {
+ LOGE("objects don't match expected values %p %p %p vs. %p %p %p\n",
+ dvmGetFromIndirectRefTable(&irt, iref0),
+ dvmGetFromIndirectRefTable(&irt, iref1),
+ dvmGetFromIndirectRefTable(&irt, iref2),
+ obj0, obj1, obj2);
+ goto bail;
+ } else {
+ DBUG_MSG("+++ obj1=%p --> iref1=%p\n", obj1, iref1);
+ }
+
+ if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref0) ||
+ !dvmRemoveFromIndirectRefTable(&irt, cookie, iref1) ||
+ !dvmRemoveFromIndirectRefTable(&irt, cookie, iref2))
+ {
+ LOGE("fifo deletion failed\n");
+ goto bail;
+ }
+
+ /* table should be empty now */
+ if (dvmIndirectRefTableEntries(&irt) != 0) {
+ LOGE("fifo del not empty\n");
+ goto bail;
+ }
+
+ /* get invalid entry (off the end of the list) */
+ if (dvmGetFromIndirectRefTable(&irt, iref0) != NULL) {
+ LOGE("stale entry get succeeded unexpectedly\n");
+ goto bail;
+ }
+
+ /*
+ * Add three, remove in the opposite order.
+ */
+ DBUG_MSG("+++ START lifo\n");
+ iref0 = dvmAddToIndirectRefTable(&irt, cookie, obj0);
+ iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj1);
+ iref2 = dvmAddToIndirectRefTable(&irt, cookie, obj2);
+ if (iref0 == NULL || iref1 == NULL || iref2 == NULL) {
+ LOGE("trivial add2 failed\n");
+ goto bail;
+ }
+
+ if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref2) ||
+ !dvmRemoveFromIndirectRefTable(&irt, cookie, iref1) ||
+ !dvmRemoveFromIndirectRefTable(&irt, cookie, iref0))
+ {
+ LOGE("lifo deletion failed\n");
+ goto bail;
+ }
+
+ /* table should be empty now */
+ if (dvmIndirectRefTableEntries(&irt) != 0) {
+ LOGE("lifo del not empty\n");
+ goto bail;
+ }
+
+ /*
+ * Add three, remove middle / middle / bottom / top. (Second attempt
+ * to remove middle should fail.)
+ */
+ DBUG_MSG("+++ START unorder\n");
+ iref0 = dvmAddToIndirectRefTable(&irt, cookie, obj0);
+ iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj1);
+ iref2 = dvmAddToIndirectRefTable(&irt, cookie, obj2);
+ if (iref0 == NULL || iref1 == NULL || iref2 == NULL) {
+ LOGE("trivial add3 failed\n");
+ goto bail;
+ }
+
+ if (dvmIndirectRefTableEntries(&irt) != 3) {
+ LOGE("expected 3 entries, found %d\n",
+ dvmIndirectRefTableEntries(&irt));
+ goto bail;
+ }
+
+ if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref1) ||
+ dvmRemoveFromIndirectRefTable(&irt, cookie, iref1))
+ {
+ LOGE("unorder deletion1 failed\n");
+ goto bail;
+ }
+
+ /* get invalid entry (from hole) */
+ if (dvmGetFromIndirectRefTable(&irt, iref1) != NULL) {
+ LOGE("hole get succeeded unexpectedly\n");
+ goto bail;
+ }
+
+ if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref2) ||
+ !dvmRemoveFromIndirectRefTable(&irt, cookie, iref0))
+ {
+ LOGE("unorder deletion2 failed\n");
+ goto bail;
+ }
+
+ /* table should be empty now */
+ if (dvmIndirectRefTableEntries(&irt) != 0) {
+ LOGE("unorder del not empty\n");
+ goto bail;
+ }
+
+ /*
+ * Add four entries. Remove #1, add new entry, verify that table size
+ * is still 4 (i.e. holes are getting filled). Remove #1 and #3, verify
+ * that we delete one and don't hole-compact the other.
+ */
+ DBUG_MSG("+++ START hole fill\n");
+ iref0 = dvmAddToIndirectRefTable(&irt, cookie, obj0);
+ iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj1);
+ iref2 = dvmAddToIndirectRefTable(&irt, cookie, obj2);
+ iref3 = dvmAddToIndirectRefTable(&irt, cookie, obj3);
+ if (iref0 == NULL || iref1 == NULL || iref2 == NULL || iref3 == NULL) {
+ LOGE("trivial add4 failed\n");
+ goto bail;
+ }
+ if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref1)) {
+ LOGE("remove 1 of 4 failed\n");
+ goto bail;
+ }
+ iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj1);
+ if (dvmIndirectRefTableEntries(&irt) != 4) {
+ LOGE("hole not filled\n");
+ goto bail;
+ }
+ if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref1) ||
+ !dvmRemoveFromIndirectRefTable(&irt, cookie, iref3))
+ {
+ LOGE("remove 1/3 failed\n");
+ goto bail;
+ }
+ if (dvmIndirectRefTableEntries(&irt) != 3) {
+ LOGE("should be 3 after two deletions\n");
+ goto bail;
+ }
+ if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref2) ||
+ !dvmRemoveFromIndirectRefTable(&irt, cookie, iref0))
+ {
+ LOGE("remove 2/0 failed\n");
+ goto bail;
+ }
+ if (dvmIndirectRefTableEntries(&irt) != 0) {
+ LOGE("not empty after split remove\n");
+ goto bail;
+ }
+
+ /*
+ * Add an entry, remove it, add a new entry, and try to use the original
+ * iref. They have the same slot number but are for different objects.
+ * With the extended checks in place, this should fail.
+ */
+ DBUG_MSG("+++ START switched\n");
+ iref0 = dvmAddToIndirectRefTable(&irt, cookie, obj0);
+ dvmRemoveFromIndirectRefTable(&irt, cookie, iref0);
+ iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj1);
+ if (dvmRemoveFromIndirectRefTable(&irt, cookie, iref0)) {
+ LOGE("mismatched del succeeded (%p vs %p)\n", iref0, iref1);
+ goto bail;
+ }
+ if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref1)) {
+ LOGE("switched del failed\n");
+ goto bail;
+ }
+ if (dvmIndirectRefTableEntries(&irt) != 0) {
+ LOGE("switching del not empty\n");
+ goto bail;
+ }
+
+ /*
+ * Same as above, but with the same object. A more rigorous checker
+ * (e.g. with slot serialization) will catch this.
+ */
+ iref0 = dvmAddToIndirectRefTable(&irt, cookie, obj0);
+ dvmRemoveFromIndirectRefTable(&irt, cookie, iref0);
+ iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj0);
+ if (iref0 != iref1) {
+ /* try 0, should not work */
+ if (dvmRemoveFromIndirectRefTable(&irt, cookie, iref0)) {
+ LOGE("temporal del succeeded (%p vs %p)\n", iref0, iref1);
+ goto bail;
+ }
+ }
+ if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref1)) {
+ LOGE("temporal cleanup failed\n");
+ goto bail;
+ }
+ if (dvmIndirectRefTableEntries(&irt) != 0) {
+ LOGE("temporal del not empty\n");
+ goto bail;
+ }
+
+ /*
+ * Test table overflow.
+ */
+ DBUG_MSG("+++ START overflow\n");
+ int i;
+ for (i = 0; i < kTableMax; i++) {
+ manyRefs[i] = dvmAddToIndirectRefTable(&irt, cookie, obj0);
+ if (manyRefs[i] == NULL) {
+ LOGE("Failed adding %d of %d\n", i, kTableMax);
+ goto bail;
+ }
+ }
+ if (dvmAddToIndirectRefTable(&irt, cookie, obj0) != NULL) {
+ LOGE("Table overflow succeeded\n");
+ goto bail;
+ }
+ if (dvmIndirectRefTableEntries(&irt) != (size_t)kTableMax) {
+ LOGE("Expected %d entries, found %d\n",
+ kTableMax, dvmIndirectRefTableEntries(&irt));
+ goto bail;
+ }
+ for (i = 0; i < kTableMax-1; i++) {
+ if (!dvmRemoveFromIndirectRefTable(&irt, cookie, manyRefs[i])) {
+ LOGE("multi-remove failed at %d\n", i);
+ goto bail;
+ }
+ }
+ /* because of removal order, should have 20 entries, 19 of them holes */
+ if (dvmIndirectRefTableEntries(&irt) != (size_t)kTableMax) {
+ LOGE("Expected %d entries (with holes), found %d\n",
+ kTableMax, dvmIndirectRefTableEntries(&irt));
+ goto bail;
+ }
+ if (!dvmRemoveFromIndirectRefTable(&irt, cookie, manyRefs[kTableMax-1])) {
+ LOGE("multi-remove final failed\n");
+ goto bail;
+ }
+ if (dvmIndirectRefTableEntries(&irt) != 0) {
+ LOGE("multi-del not empty\n");
+ goto bail;
+ }
+
+ DBUG_MSG("+++ basic test complete\n");
+ result = true;
+
+bail:
+ dvmClearIndirectRefTable(&irt);
+ return result;
+}
+
+/*
+ * Test operations on a segmented table.
+ */
+static bool segmentTest(void)
+{
+ static const int kTableMax = 20;
+ IndirectRefTable irt;
+ IndirectRef iref0, iref1, iref2, iref3;
+ ClassObject* clazz = dvmFindClass("Ljava/lang/Object;", NULL);
+ Object* obj0 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+ Object* obj1 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+ Object* obj2 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+ Object* obj3 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+ u4 cookie;
+ u4 segmentState[4];
+ bool result = false;
+
+ if (!dvmInitIndirectRefTable(&irt, kTableMax, kTableMax,
+ kIndirectKindLocal))
+ {
+ return false;
+ }
+ cookie = segmentState[0] = IRT_FIRST_SEGMENT;
+ DBUG_MSG("+++ objs %p %p %p %p\n", obj0, obj1, obj2, obj3);
+
+ /*
+ * Push two, create new segment, push two more, try to get all four,
+ * try to delete all 4. All four should be accessible, but only the
+ * last two should be deletable.
+ */
+ DBUG_MSG("+++ START basic segment\n");
+ iref0 = dvmAddToIndirectRefTable(&irt, cookie, obj0);
+ iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj1);
+ cookie = segmentState[1] = dvmPushIndirectRefTableSegment(&irt);
+ DBUG_MSG("+++ pushed, cookie is 0x%08x\n", cookie);
+ iref2 = dvmAddToIndirectRefTable(&irt, cookie, obj2);
+ iref3 = dvmAddToIndirectRefTable(&irt, cookie, obj3);
+
+ if (dvmRemoveFromIndirectRefTable(&irt, cookie, iref0) ||
+ dvmRemoveFromIndirectRefTable(&irt, cookie, iref1))
+ {
+ LOGE("removed values from earlier segment\n");
+ goto bail;
+ }
+ if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref2) ||
+ !dvmRemoveFromIndirectRefTable(&irt, cookie, iref3))
+ {
+ LOGE("unable to remove values from current segment\n");
+ goto bail;
+ }
+ if (dvmIndirectRefTableEntries(&irt) != 2) {
+ LOGE("wrong total entries\n");
+ goto bail;
+ }
+ dvmPopIndirectRefTableSegment(&irt, segmentState[1]);
+ cookie = segmentState[0];
+ if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref0) ||
+ !dvmRemoveFromIndirectRefTable(&irt, cookie, iref1))
+ {
+ LOGE("unable to remove values from first segment\n");
+ goto bail;
+ }
+ if (dvmIndirectRefTableEntries(&irt) != 0) {
+ LOGE("basic push/pop not empty\n");
+ goto bail;
+ }
+
+ /*
+ * Push two, delete first, segment, push two more, pop segment, verify
+ * the last two are no longer present and hole count is right. The
+ * adds after the segment pop should not be filling in the hole.
+ */
+ DBUG_MSG("+++ START segment pop\n");
+ iref0 = dvmAddToIndirectRefTable(&irt, cookie, obj0);
+ iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj1);
+ dvmRemoveFromIndirectRefTable(&irt, cookie, iref0);
+ cookie = segmentState[1] = dvmPushIndirectRefTableSegment(&irt);
+ iref2 = dvmAddToIndirectRefTable(&irt, cookie, obj2);
+ iref3 = dvmAddToIndirectRefTable(&irt, cookie, obj3);
+ dvmPopIndirectRefTableSegment(&irt, segmentState[1]);
+ cookie = segmentState[0];
+ if (dvmIndirectRefTableEntries(&irt) != 2) {
+ LOGE("wrong total entries after pop\n");
+ goto bail;
+ }
+ dvmRemoveFromIndirectRefTable(&irt, cookie, iref1);
+ if (dvmIndirectRefTableEntries(&irt) != 0) {
+ LOGE("not back to zero after pop + del\n");
+ goto bail;
+ }
+
+ /*
+ * Multiple segments, some empty.
+ */
+ DBUG_MSG("+++ START multiseg\n");
+ iref0 = dvmAppendToIndirectRefTable(&irt, cookie, obj0);
+ iref1 = dvmAppendToIndirectRefTable(&irt, cookie, obj1);
+ cookie = segmentState[1] = dvmPushIndirectRefTableSegment(&irt);
+ cookie = segmentState[2] = dvmPushIndirectRefTableSegment(&irt);
+ iref3 = dvmAppendToIndirectRefTable(&irt, cookie, obj3);
+ iref2 = dvmAppendToIndirectRefTable(&irt, cookie, obj2);
+ dvmRemoveFromIndirectRefTable(&irt, cookie, iref3);
+ cookie = segmentState[3] = dvmPushIndirectRefTableSegment(&irt);
+ iref3 = dvmAppendToIndirectRefTable(&irt, cookie, obj3);
+
+ if (dvmGetFromIndirectRefTable(&irt, iref0) != obj0 ||
+ dvmGetFromIndirectRefTable(&irt, iref1) != obj1 ||
+ dvmGetFromIndirectRefTable(&irt, iref2) != obj2 ||
+ dvmGetFromIndirectRefTable(&irt, iref3) != obj3)
+ {
+ LOGE("Unable to retrieve all multiseg objects\n");
+ goto bail;
+ }
+
+ dvmDumpIndirectRefTable(&irt, "test");
+
+ //int i;
+ //for (i = 0; i < sizeof(segmentState) / sizeof(segmentState[0]); i++) {
+ // DBUG_MSG("+++ segment %d = 0x%08x\n", i, segmentState[i]);
+ //}
+
+ dvmRemoveFromIndirectRefTable(&irt, cookie, iref3);
+ if (dvmRemoveFromIndirectRefTable(&irt, cookie, iref2)) {
+ LOGE("multiseg del2 worked\n");
+ goto bail;
+ }
+ dvmPopIndirectRefTableSegment(&irt, segmentState[3]);
+ cookie = segmentState[2];
+ if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref2)) {
+ LOGE("multiseg del2b failed (cookie=0x%08x ref=%p)\n", cookie, iref2);
+ goto bail;
+ }
+ iref2 = dvmAddToIndirectRefTable(&irt, cookie, obj2);
+
+ /* pop two off at once */
+ dvmPopIndirectRefTableSegment(&irt, segmentState[1]);
+ cookie = segmentState[0];
+
+ if (dvmIndirectRefTableEntries(&irt) != 2) {
+ LOGE("Unexpected entry count in multiseg\n");
+ goto bail;
+ }
+ dvmRemoveFromIndirectRefTable(&irt, cookie, iref0);
+ dvmRemoveFromIndirectRefTable(&irt, cookie, iref1);
+ if (dvmIndirectRefTableEntries(&irt) != 0) {
+ LOGE("Unexpected entry count at multiseg end\n");
+ goto bail;
+ }
+
+ DBUG_MSG("+++ segment test complete\n");
+ result = true;
+
+bail:
+ dvmClearIndirectRefTable(&irt);
+ return result;
+}
+
+
+/*
+ * Some quick tests.
+ */
+bool dvmTestIndirectRefTable(void)
+{
+ if (!basicTest()) {
+ LOGE("IRT basic test failed\n");
+ return false;
+ }
+ if (!segmentTest()) {
+ LOGE("IRT segment test failed\n");
+ return false;
+ }
+
+ return true;
+}
+
+#endif /*NDEBUG*/